From 6d37e071a36d13ff1b0e8d3799366c6980fe993a Mon Sep 17 00:00:00 2001 From: twodreams Date: Mon, 16 Mar 2026 16:59:04 -0300 Subject: [PATCH 1/4] feat: Integrate Jolt Physics as native host service in SpacetimeDB Add Jolt Physics 5.0.0 as a native C++ host service accessible from WASM modules via host functions. Includes: - JoltC C API: Vehicle system (WheeledVehicleController) with structs for vehicle params, wheel state, and full vehicle state - Rust physics service: PhysicsState singleton managing multiple physics worlds, body creation/destruction, vehicle support, and 2-layer broadphase - WASM host functions: 18 physics_* functions in spacetime_10.5 namespace bridging WASM memory to native Jolt calls - C# bindings: High-level Physics.cs wrapper with serialization for FFI - Test module: physics-test-cs with tables, reducers, and 30Hz physics tick Co-Authored-By: Claude Opus 4.6 --- .cargo/config.toml | 2 - Cargo.lock | 155 +- Cargo.toml | 10 + .../bindings-csharp/Runtime/Internal/FFI.cs | 131 + crates/bindings-csharp/Runtime/Physics.cs | 422 +++ crates/bindings-csharp/Runtime/bindings.c | 59 + crates/bindings-sys/src/lib.rs | 178 ++ crates/core/Cargo.toml | 5 + crates/core/src/host/mod.rs | 23 + crates/core/src/host/physics.rs | 673 ++++ crates/core/src/host/wasm_common.rs | 22 + .../src/host/wasmtime/wasm_instance_env.rs | 445 +++ .../core/src/host/wasmtime/wasmtime_module.rs | 2 +- crates/joltc-sys-patched/.cargo-ok | 1 + crates/joltc-sys-patched/.cargo_vcs_info.json | 6 + crates/joltc-sys-patched/.gitignore | 1 + crates/joltc-sys-patched/Cargo.toml | 51 + crates/joltc-sys-patched/Cargo.toml.orig | 30 + crates/joltc-sys-patched/JoltC/.editorconfig | 6 + .../JoltC/.github/workflows/ci.yml | 54 + crates/joltc-sys-patched/JoltC/.gitignore | 1 + crates/joltc-sys-patched/JoltC/.gitmodules | 3 + crates/joltc-sys-patched/JoltC/CMakeLists.txt | 76 + .../JoltC/HelloWorld/README.md | 2 + .../JoltC/HelloWorld/main.cpp | 218 ++ crates/joltc-sys-patched/JoltC/JoltC/Enums.h | 301 ++ .../joltc-sys-patched/JoltC/JoltC/Functions.h | 819 +++++ .../joltc-sys-patched/JoltC/JoltC/JoltC.cpp | 1526 +++++++++ crates/joltc-sys-patched/JoltC/JoltC/JoltC.h | 12 + crates/joltc-sys-patched/JoltC/JoltC/Test.cpp | 50 + .../joltc-sys-patched/JoltC/JoltC/Vehicle.cpp | 375 +++ .../joltc-sys-patched/JoltC/JoltC/Vehicle.h | 130 + .../JoltC/JoltPhysics/.clang-format | 1 + .../JoltC/JoltPhysics/.editorconfig | 13 + .../JoltC/JoltPhysics/.gitattributes | 18 + .../JoltC/JoltPhysics/.github/dependabot.yml | 9 + .../JoltPhysics/.github/workflows/build.yml | 322 ++ .../.github/workflows/determinism_check.yml | 119 + .../JoltPhysics/.github/workflows/doxygen.yml | 26 + .../.github/workflows/sonar-cloud.yml | 59 + .../JoltC/JoltPhysics/.gitignore | 8 + .../JoltC/JoltPhysics/Build/.gitignore | 6 + .../JoltPhysics/Build/Android/.gitignore | 5 + .../Android/PerformanceTest/build.gradle | 51 + .../src/main/AndroidManifest.xml | 20 + .../src/main/cpp/CMakeLists.txt | 20 + .../Build/Android/UnitTests/build.gradle | 51 + .../UnitTests/src/main/AndroidManifest.xml | 20 + .../UnitTests/src/main/cpp/CMakeLists.txt | 20 + .../JoltPhysics/Build/Android/build.gradle | 17 + .../Build/Android/gradle.properties | 22 + .../Android/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 59203 bytes .../gradle/wrapper/gradle-wrapper.properties | 5 + .../JoltC/JoltPhysics/Build/Android/gradlew | 185 ++ .../JoltPhysics/Build/Android/gradlew.bat | 89 + .../JoltPhysics/Build/Android/settings.gradle | 10 + .../JoltC/JoltPhysics/Build/CMakeLists.txt | 319 ++ .../JoltC/JoltPhysics/Build/README.md | 237 ++ .../Build/cmake_linux_clang_gcc.sh | 28 + .../JoltPhysics/Build/cmake_linux_mingw.sh | 19 + .../JoltPhysics/Build/cmake_vs2019_cl.bat | 3 + .../JoltPhysics/Build/cmake_vs2019_cl_arm.bat | 3 + .../Build/cmake_vs2019_cl_arm_32bit.bat | 3 + .../JoltPhysics/Build/cmake_vs2019_clang.bat | 10 + .../JoltPhysics/Build/cmake_vs2022_cl.bat | 3 + .../Build/cmake_vs2022_cl_32bit.bat | 3 + .../JoltPhysics/Build/cmake_vs2022_cl_arm.bat | 3 + .../Build/cmake_vs2022_cl_arm_32bit.bat | 3 + ...vs2022_cl_cross_platform_deterministic.bat | 3 + .../Build/cmake_vs2022_cl_double.bat | 3 + .../JoltPhysics/Build/cmake_vs2022_clang.bat | 10 + .../Build/cmake_vs2022_clang_double.bat | 10 + .../JoltPhysics/Build/cmake_vs2022_uwp.bat | 5 + .../Build/cmake_vs2022_uwp_arm.bat | 5 + .../JoltPhysics/Build/cmake_windows_mingw.sh | 19 + .../JoltPhysics/Build/cmake_xcode_ios.sh | 4 + .../JoltPhysics/Build/cmake_xcode_macos.sh | 4 + .../JoltPhysics/Build/iOS/UnitTestsInfo.plist | 34 + .../JoltC/JoltPhysics/ContributorAgreement.md | 123 + .../JoltC/JoltPhysics/Docs/APIChanges.md | 111 + .../JoltC/JoltPhysics/Docs/Architecture.md | 755 +++++ .../JoltPhysics/Docs/Images/ActiveEdge.jpg | Bin 0 -> 15391 bytes .../Images/ActiveVsInactiveContactNormal.jpg | Bin 0 -> 16028 bytes .../JoltPhysics/Docs/Images/ConvexRadius.jpg | Bin 0 -> 11149 bytes .../JoltPhysics/Docs/Images/EllipsoidAABB.png | Bin 0 -> 158978 bytes .../Docs/Images/GhostCollision.jpg | Bin 0 -> 16249 bytes .../JoltPhysics/Docs/Images/LongAndThin.jpg | Bin 0 -> 13674 bytes .../JoltPhysics/Docs/Images/MotionQuality.jpg | Bin 0 -> 33112 bytes .../JoltPhysics/Docs/Images/MotorDamping.jpg | Bin 0 -> 96716 bytes .../Docs/Images/MotorFrequency.jpg | Bin 0 -> 101436 bytes .../Docs/Images/QuadTreeExample.png | Bin 0 -> 65699 bytes .../Docs/Images/ShapeCenterOfMass.jpg | Bin 0 -> 30218 bytes .../Docs/Images/SimulationIsland.jpg | Bin 0 -> 19279 bytes .../Docs/Images/SoftBodySkinnedConstraint.jpg | Bin 0 -> 27634 bytes .../JoltC/JoltPhysics/Docs/Logo.png | Bin 0 -> 1937976 bytes .../JoltC/JoltPhysics/Docs/LogoDark.png | Bin 0 -> 2478107 bytes .../JoltC/JoltPhysics/Docs/LogoSmall.png | Bin 0 -> 14038 bytes .../JoltC/JoltPhysics/Docs/PerformanceTest.md | 36 + .../Docs/PhysicsSystemUpdate.drawio | 537 ++++ .../JoltPhysics/Docs/PhysicsSystemUpdate.svg | 4 + .../JoltPhysics/Docs/ProjectsUsingJolt.md | 16 + .../JoltC/JoltPhysics/Docs/ReleaseNotes.md | 209 ++ .../JoltC/JoltPhysics/Docs/Samples.md | 168 + .../JoltPhysics/Docs/SwingTwistConstraint.png | Bin 0 -> 92708 bytes .../JoltC/JoltPhysics/Doxyfile | 2775 +++++++++++++++++ .../JoltPhysics/HelloWorld/HelloWorld.cmake | 11 + .../JoltPhysics/HelloWorld/HelloWorld.cpp | 364 +++ .../Jolt/AABBTree/AABBTreeBuilder.cpp | 225 ++ .../Jolt/AABBTree/AABBTreeBuilder.h | 110 + .../Jolt/AABBTree/AABBTreeToBuffer.h | 245 ++ .../NodeCodec/NodeCodecQuadTreeHalfFloat.h | 287 ++ .../TriangleCodecIndexed8BitPackSOA4Flags.h | 456 +++ .../JoltPhysics/Jolt/ConfigurationString.h | 71 + .../JoltC/JoltPhysics/Jolt/Core/ARMNeon.h | 88 + .../JoltC/JoltPhysics/Jolt/Core/Atomics.h | 44 + .../JoltC/JoltPhysics/Jolt/Core/ByteBuffer.h | 74 + .../JoltC/JoltPhysics/Jolt/Core/Color.cpp | 38 + .../JoltC/JoltPhysics/Jolt/Core/Color.h | 81 + .../JoltC/JoltPhysics/Jolt/Core/Core.h | 555 ++++ .../JoltPhysics/Jolt/Core/FPControlWord.h | 135 + .../JoltC/JoltPhysics/Jolt/Core/FPException.h | 74 + .../JoltPhysics/Jolt/Core/FPFlushDenormals.h | 41 + .../JoltC/JoltPhysics/Jolt/Core/Factory.cpp | 87 + .../JoltC/JoltPhysics/Jolt/Core/Factory.h | 54 + .../JoltPhysics/Jolt/Core/FixedSizeFreeList.h | 120 + .../Jolt/Core/FixedSizeFreeList.inl | 211 ++ .../JoltC/JoltPhysics/Jolt/Core/HashCombine.h | 97 + .../JoltPhysics/Jolt/Core/InsertionSort.h | 58 + .../JoltPhysics/Jolt/Core/IssueReporting.cpp | 31 + .../JoltPhysics/Jolt/Core/IssueReporting.h | 38 + .../JoltC/JoltPhysics/Jolt/Core/JobSystem.h | 305 ++ .../JoltC/JoltPhysics/Jolt/Core/JobSystem.inl | 56 + .../Jolt/Core/JobSystemSingleThreaded.cpp | 65 + .../Jolt/Core/JobSystemSingleThreaded.h | 62 + .../Jolt/Core/JobSystemThreadPool.cpp | 354 +++ .../Jolt/Core/JobSystemThreadPool.h | 92 + .../Jolt/Core/JobSystemWithBarrier.cpp | 227 ++ .../Jolt/Core/JobSystemWithBarrier.h | 85 + .../JoltPhysics/Jolt/Core/LinearCurve.cpp | 51 + .../JoltC/JoltPhysics/Jolt/Core/LinearCurve.h | 67 + .../JoltPhysics/Jolt/Core/LockFreeHashMap.h | 182 ++ .../JoltPhysics/Jolt/Core/LockFreeHashMap.inl | 351 +++ .../JoltC/JoltPhysics/Jolt/Core/Memory.cpp | 74 + .../JoltC/JoltPhysics/Jolt/Core/Memory.h | 55 + .../JoltC/JoltPhysics/Jolt/Core/Mutex.h | 223 ++ .../JoltC/JoltPhysics/Jolt/Core/MutexArray.h | 98 + .../JoltC/JoltPhysics/Jolt/Core/NonCopyable.h | 18 + .../JoltC/JoltPhysics/Jolt/Core/Profiler.cpp | 346 ++ .../JoltC/JoltPhysics/Jolt/Core/Profiler.h | 284 ++ .../JoltC/JoltPhysics/Jolt/Core/Profiler.inl | 89 + .../JoltC/JoltPhysics/Jolt/Core/QuickSort.h | 137 + .../JoltC/JoltPhysics/Jolt/Core/RTTI.cpp | 143 + .../JoltC/JoltPhysics/Jolt/Core/RTTI.h | 436 +++ .../JoltC/JoltPhysics/Jolt/Core/Reference.h | 226 ++ .../JoltC/JoltPhysics/Jolt/Core/Result.h | 177 ++ .../Jolt/Core/STLAlignedAllocator.h | 66 + .../JoltPhysics/Jolt/Core/STLAllocator.h | 102 + .../JoltPhysics/Jolt/Core/STLTempAllocator.h | 77 + .../JoltC/JoltPhysics/Jolt/Core/Semaphore.cpp | 80 + .../JoltC/JoltPhysics/Jolt/Core/Semaphore.h | 51 + .../JoltC/JoltPhysics/Jolt/Core/StaticArray.h | 323 ++ .../JoltC/JoltPhysics/Jolt/Core/StreamIn.h | 110 + .../JoltC/JoltPhysics/Jolt/Core/StreamOut.h | 86 + .../JoltC/JoltPhysics/Jolt/Core/StreamUtils.h | 167 + .../JoltPhysics/Jolt/Core/StreamWrapper.h | 53 + .../JoltPhysics/Jolt/Core/StringTools.cpp | 101 + .../JoltC/JoltPhysics/Jolt/Core/StringTools.h | 51 + .../JoltPhysics/Jolt/Core/TempAllocator.h | 121 + .../JoltPhysics/Jolt/Core/TickCounter.cpp | 34 + .../JoltC/JoltPhysics/Jolt/Core/TickCounter.h | 49 + .../JoltPhysics/Jolt/Core/UnorderedMap.h | 15 + .../JoltPhysics/Jolt/Core/UnorderedSet.h | 15 + .../JoltC/JoltPhysics/Jolt/Geometry/AABox.h | 304 ++ .../JoltC/JoltPhysics/Jolt/Geometry/AABox4.h | 224 ++ .../JoltPhysics/Jolt/Geometry/ClipPoly.h | 200 ++ .../JoltPhysics/Jolt/Geometry/ClosestPoint.h | 498 +++ .../Jolt/Geometry/ConvexHullBuilder.cpp | 1465 +++++++++ .../Jolt/Geometry/ConvexHullBuilder.h | 276 ++ .../Jolt/Geometry/ConvexHullBuilder2D.cpp | 336 ++ .../Jolt/Geometry/ConvexHullBuilder2D.h | 105 + .../JoltPhysics/Jolt/Geometry/ConvexSupport.h | 188 ++ .../Jolt/Geometry/EPAConvexHullBuilder.h | 844 +++++ .../Jolt/Geometry/EPAPenetrationDepth.h | 546 ++++ .../JoltC/JoltPhysics/Jolt/Geometry/Ellipse.h | 77 + .../Jolt/Geometry/GJKClosestPoint.h | 952 ++++++ .../Jolt/Geometry/IndexedTriangle.h | 115 + .../JoltPhysics/Jolt/Geometry/Indexify.cpp | 218 ++ .../JoltPhysics/Jolt/Geometry/Indexify.h | 19 + .../JoltPhysics/Jolt/Geometry/MortonCode.h | 40 + .../JoltPhysics/Jolt/Geometry/OrientedBox.cpp | 178 ++ .../JoltPhysics/Jolt/Geometry/OrientedBox.h | 39 + .../JoltC/JoltPhysics/Jolt/Geometry/Plane.h | 86 + .../JoltPhysics/Jolt/Geometry/RayAABox.h | 241 ++ .../JoltPhysics/Jolt/Geometry/RayAABox8.h | 76 + .../JoltPhysics/Jolt/Geometry/RayCapsule.h | 37 + .../JoltPhysics/Jolt/Geometry/RayCylinder.h | 101 + .../JoltPhysics/Jolt/Geometry/RaySphere.h | 96 + .../JoltPhysics/Jolt/Geometry/RayTriangle.h | 158 + .../JoltPhysics/Jolt/Geometry/RayTriangle8.h | 91 + .../JoltC/JoltPhysics/Jolt/Geometry/Sphere.h | 72 + .../JoltPhysics/Jolt/Geometry/Triangle.h | 34 + .../JoltC/JoltPhysics/Jolt/Jolt.cmake | 634 ++++ .../JoltC/JoltPhysics/Jolt/Jolt.h | 16 + .../JoltC/JoltPhysics/Jolt/Jolt.natvis | 86 + .../JoltC/JoltPhysics/Jolt/Math/DMat44.h | 158 + .../JoltC/JoltPhysics/Jolt/Math/DMat44.inl | 310 ++ .../JoltC/JoltPhysics/Jolt/Math/DVec3.h | 288 ++ .../JoltC/JoltPhysics/Jolt/Math/DVec3.inl | 921 ++++++ .../JoltC/JoltPhysics/Jolt/Math/Double3.h | 48 + .../JoltC/JoltPhysics/Jolt/Math/DynMatrix.h | 31 + .../Jolt/Math/EigenValueSymmetric.h | 175 ++ .../JoltC/JoltPhysics/Jolt/Math/FindRoot.h | 42 + .../JoltC/JoltPhysics/Jolt/Math/Float2.h | 36 + .../JoltC/JoltPhysics/Jolt/Math/Float3.h | 50 + .../JoltC/JoltPhysics/Jolt/Math/Float4.h | 33 + .../Jolt/Math/GaussianElimination.h | 102 + .../JoltC/JoltPhysics/Jolt/Math/HalfFloat.h | 204 ++ .../JoltC/JoltPhysics/Jolt/Math/Mat44.h | 243 ++ .../JoltC/JoltPhysics/Jolt/Math/Mat44.inl | 952 ++++++ .../JoltC/JoltPhysics/Jolt/Math/Math.h | 203 ++ .../JoltC/JoltPhysics/Jolt/Math/MathTypes.h | 34 + .../JoltC/JoltPhysics/Jolt/Math/Matrix.h | 259 ++ .../JoltC/JoltPhysics/Jolt/Math/Quat.h | 255 ++ .../JoltC/JoltPhysics/Jolt/Math/Quat.inl | 328 ++ .../JoltC/JoltPhysics/Jolt/Math/Real.h | 44 + .../JoltC/JoltPhysics/Jolt/Math/Swizzle.h | 19 + .../JoltPhysics/Jolt/Math/Trigonometry.h | 59 + .../JoltC/JoltPhysics/Jolt/Math/UVec4.h | 220 ++ .../JoltC/JoltPhysics/Jolt/Math/UVec4.inl | 573 ++++ .../JoltC/JoltPhysics/Jolt/Math/UVec8.h | 100 + .../JoltC/JoltPhysics/Jolt/Math/UVec8.inl | 138 + .../JoltC/JoltPhysics/Jolt/Math/Vec3.cpp | 59 + .../JoltC/JoltPhysics/Jolt/Math/Vec3.h | 294 ++ .../JoltC/JoltPhysics/Jolt/Math/Vec3.inl | 845 +++++ .../JoltC/JoltPhysics/Jolt/Math/Vec4.h | 283 ++ .../JoltC/JoltPhysics/Jolt/Math/Vec4.inl | 970 ++++++ .../JoltC/JoltPhysics/Jolt/Math/Vec8.h | 112 + .../JoltC/JoltPhysics/Jolt/Math/Vec8.inl | 148 + .../JoltC/JoltPhysics/Jolt/Math/Vector.h | 216 ++ .../ObjectStream/GetPrimitiveTypeOfType.h | 54 + .../Jolt/ObjectStream/ObjectStream.cpp | 34 + .../Jolt/ObjectStream/ObjectStream.h | 329 ++ .../ObjectStream/ObjectStreamBinaryIn.cpp | 230 ++ .../Jolt/ObjectStream/ObjectStreamBinaryIn.h | 51 + .../ObjectStream/ObjectStreamBinaryOut.cpp | 150 + .../Jolt/ObjectStream/ObjectStreamBinaryOut.h | 51 + .../Jolt/ObjectStream/ObjectStreamIn.cpp | 617 ++++ .../Jolt/ObjectStream/ObjectStreamIn.h | 144 + .../Jolt/ObjectStream/ObjectStreamOut.cpp | 164 + .../Jolt/ObjectStream/ObjectStreamOut.h | 100 + .../Jolt/ObjectStream/ObjectStreamTextIn.cpp | 392 +++ .../Jolt/ObjectStream/ObjectStreamTextIn.h | 49 + .../Jolt/ObjectStream/ObjectStreamTextOut.cpp | 227 ++ .../Jolt/ObjectStream/ObjectStreamTextOut.h | 56 + .../Jolt/ObjectStream/ObjectStreamTypes.h | 24 + .../Jolt/ObjectStream/SerializableAttribute.h | 107 + .../ObjectStream/SerializableAttributeEnum.h | 58 + .../ObjectStream/SerializableAttributeTyped.h | 51 + .../Jolt/ObjectStream/SerializableObject.cpp | 15 + .../Jolt/ObjectStream/SerializableObject.h | 155 + .../Jolt/ObjectStream/TypeDeclarations.cpp | 55 + .../Jolt/ObjectStream/TypeDeclarations.h | 41 + .../Jolt/Physics/Body/AllowedDOFs.h | 68 + .../JoltPhysics/Jolt/Physics/Body/Body.cpp | 413 +++ .../JoltPhysics/Jolt/Physics/Body/Body.h | 388 +++ .../JoltPhysics/Jolt/Physics/Body/Body.inl | 197 ++ .../Jolt/Physics/Body/BodyAccess.cpp | 18 + .../Jolt/Physics/Body/BodyAccess.h | 55 + .../Physics/Body/BodyActivationListener.h | 28 + .../Physics/Body/BodyCreationSettings.cpp | 234 ++ .../Jolt/Physics/Body/BodyCreationSettings.h | 124 + .../Jolt/Physics/Body/BodyFilter.h | 102 + .../JoltPhysics/Jolt/Physics/Body/BodyID.h | 100 + .../Jolt/Physics/Body/BodyInterface.cpp | 1002 ++++++ .../Jolt/Physics/Body/BodyInterface.h | 278 ++ .../JoltPhysics/Jolt/Physics/Body/BodyLock.h | 111 + .../Jolt/Physics/Body/BodyLockInterface.h | 134 + .../Jolt/Physics/Body/BodyLockMulti.h | 104 + .../Jolt/Physics/Body/BodyManager.cpp | 1149 +++++++ .../Jolt/Physics/Body/BodyManager.h | 364 +++ .../JoltPhysics/Jolt/Physics/Body/BodyPair.h | 36 + .../JoltPhysics/Jolt/Physics/Body/BodyType.h | 19 + .../Jolt/Physics/Body/MassProperties.cpp | 185 ++ .../Jolt/Physics/Body/MassProperties.h | 58 + .../Jolt/Physics/Body/MotionProperties.cpp | 90 + .../Jolt/Physics/Body/MotionProperties.h | 278 ++ .../Jolt/Physics/Body/MotionProperties.inl | 168 + .../Jolt/Physics/Body/MotionQuality.h | 31 + .../Jolt/Physics/Body/MotionType.h | 17 + .../Jolt/Physics/Character/Character.cpp | 317 ++ .../Jolt/Physics/Character/Character.h | 139 + .../Jolt/Physics/Character/CharacterBase.cpp | 59 + .../Jolt/Physics/Character/CharacterBase.h | 154 + .../Physics/Character/CharacterVirtual.cpp | 1512 +++++++++ .../Jolt/Physics/Character/CharacterVirtual.h | 502 +++ .../Jolt/Physics/Collision/AABoxCast.h | 20 + .../Jolt/Physics/Collision/ActiveEdgeMode.h | 17 + .../Jolt/Physics/Collision/ActiveEdges.h | 114 + .../Jolt/Physics/Collision/BackFaceMode.h | 16 + .../Collision/BroadPhase/BroadPhase.cpp | 16 + .../Physics/Collision/BroadPhase/BroadPhase.h | 112 + .../BroadPhase/BroadPhaseBruteForce.cpp | 313 ++ .../BroadPhase/BroadPhaseBruteForce.h | 38 + .../Collision/BroadPhase/BroadPhaseLayer.h | 148 + .../BroadPhase/BroadPhaseLayerInterfaceMask.h | 92 + .../BroadPhaseLayerInterfaceTable.h | 64 + .../BroadPhase/BroadPhaseQuadTree.cpp | 609 ++++ .../Collision/BroadPhase/BroadPhaseQuadTree.h | 108 + .../Collision/BroadPhase/BroadPhaseQuery.h | 53 + .../ObjectVsBroadPhaseLayerFilterMask.h | 35 + .../ObjectVsBroadPhaseLayerFilterTable.h | 66 + .../Physics/Collision/BroadPhase/QuadTree.cpp | 1684 ++++++++++ .../Physics/Collision/BroadPhase/QuadTree.h | 388 +++ .../Collision/CastConvexVsTriangles.cpp | 109 + .../Physics/Collision/CastConvexVsTriangles.h | 46 + .../Jolt/Physics/Collision/CastResult.h | 37 + .../Collision/CastSphereVsTriangles.cpp | 223 ++ .../Physics/Collision/CastSphereVsTriangles.h | 49 + .../Jolt/Physics/Collision/CollectFacesMode.h | 16 + .../Collision/CollideConvexVsTriangles.cpp | 150 + .../Collision/CollideConvexVsTriangles.h | 56 + .../Physics/Collision/CollidePointResult.h | 25 + .../Jolt/Physics/Collision/CollideShape.h | 105 + .../CollideSoftBodyVerticesVsTriangles.h | 98 + .../Collision/CollideSphereVsTriangles.cpp | 123 + .../Collision/CollideSphereVsTriangles.h | 50 + .../Physics/Collision/CollisionCollector.h | 102 + .../Collision/CollisionCollectorImpl.h | 134 + .../Physics/Collision/CollisionDispatch.cpp | 107 + .../Physics/Collision/CollisionDispatch.h | 97 + .../Jolt/Physics/Collision/CollisionGroup.cpp | 33 + .../Jolt/Physics/Collision/CollisionGroup.h | 94 + .../Jolt/Physics/Collision/ContactListener.h | 114 + .../Collision/EstimateCollisionResponse.cpp | 213 ++ .../Collision/EstimateCollisionResponse.h | 48 + .../Jolt/Physics/Collision/GroupFilter.cpp | 32 + .../Jolt/Physics/Collision/GroupFilter.h | 41 + .../Physics/Collision/GroupFilterTable.cpp | 38 + .../Jolt/Physics/Collision/GroupFilterTable.h | 130 + .../Collision/InternalEdgeRemovingCollector.h | 233 ++ .../Collision/ManifoldBetweenTwoFaces.cpp | 237 ++ .../Collision/ManifoldBetweenTwoFaces.h | 44 + .../Physics/Collision/NarrowPhaseQuery.cpp | 412 +++ .../Jolt/Physics/Collision/NarrowPhaseQuery.h | 74 + .../Physics/Collision/NarrowPhaseStats.cpp | 62 + .../Jolt/Physics/Collision/NarrowPhaseStats.h | 110 + .../Jolt/Physics/Collision/ObjectLayer.h | 111 + .../Collision/ObjectLayerPairFilterMask.h | 52 + .../Collision/ObjectLayerPairFilterTable.h | 78 + .../Physics/Collision/PhysicsMaterial.cpp | 35 + .../Jolt/Physics/Collision/PhysicsMaterial.h | 52 + .../Collision/PhysicsMaterialSimple.cpp | 38 + .../Physics/Collision/PhysicsMaterialSimple.h | 37 + .../Jolt/Physics/Collision/RayCast.h | 81 + .../Jolt/Physics/Collision/Shape/BoxShape.cpp | 318 ++ .../Jolt/Physics/Collision/Shape/BoxShape.h | 115 + .../Physics/Collision/Shape/CapsuleShape.cpp | 446 +++ .../Physics/Collision/Shape/CapsuleShape.h | 128 + .../Physics/Collision/Shape/CompoundShape.cpp | 398 +++ .../Physics/Collision/Shape/CompoundShape.h | 344 ++ .../Collision/Shape/CompoundShapeVisitors.h | 460 +++ .../Collision/Shape/ConvexHullShape.cpp | 1311 ++++++++ .../Physics/Collision/Shape/ConvexHullShape.h | 202 ++ .../Physics/Collision/Shape/ConvexShape.cpp | 559 ++++ .../Physics/Collision/Shape/ConvexShape.h | 150 + .../Physics/Collision/Shape/CylinderShape.cpp | 417 +++ .../Physics/Collision/Shape/CylinderShape.h | 126 + .../Collision/Shape/DecoratedShape.cpp | 87 + .../Physics/Collision/Shape/DecoratedShape.h | 70 + .../Collision/Shape/GetTrianglesContext.h | 244 ++ .../Collision/Shape/HeightFieldShape.cpp | 2624 ++++++++++++++++ .../Collision/Shape/HeightFieldShape.h | 349 +++ .../Physics/Collision/Shape/MeshShape.cpp | 1244 ++++++++ .../Jolt/Physics/Collision/Shape/MeshShape.h | 207 ++ .../Collision/Shape/MutableCompoundShape.cpp | 560 ++++ .../Collision/Shape/MutableCompoundShape.h | 160 + .../Shape/OffsetCenterOfMassShape.cpp | 217 ++ .../Collision/Shape/OffsetCenterOfMassShape.h | 143 + .../PolyhedronSubmergedVolumeCalculator.h | 319 ++ .../Shape/RotatedTranslatedShape.cpp | 315 ++ .../Collision/Shape/RotatedTranslatedShape.h | 158 + .../Physics/Collision/Shape/ScaleHelpers.h | 68 + .../Physics/Collision/Shape/ScaledShape.cpp | 226 ++ .../Physics/Collision/Shape/ScaledShape.h | 140 + .../Jolt/Physics/Collision/Shape/Shape.cpp | 309 ++ .../Jolt/Physics/Collision/Shape/Shape.h | 447 +++ .../Physics/Collision/Shape/SphereShape.cpp | 352 +++ .../Physics/Collision/Shape/SphereShape.h | 125 + .../Collision/Shape/StaticCompoundShape.cpp | 675 ++++ .../Collision/Shape/StaticCompoundShape.h | 139 + .../Jolt/Physics/Collision/Shape/SubShapeID.h | 138 + .../Physics/Collision/Shape/SubShapeIDPair.h | 80 + .../Collision/Shape/TaperedCapsuleShape.cpp | 458 +++ .../Shape/TaperedCapsuleShape.gliffy | 1 + .../Collision/Shape/TaperedCapsuleShape.h | 125 + .../Physics/Collision/Shape/TriangleShape.cpp | 413 +++ .../Physics/Collision/Shape/TriangleShape.h | 138 + .../Jolt/Physics/Collision/ShapeCast.h | 170 + .../Jolt/Physics/Collision/ShapeFilter.h | 72 + .../Physics/Collision/SortReverseAndStore.h | 48 + .../Physics/Collision/TransformedShape.cpp | 180 ++ .../Jolt/Physics/Collision/TransformedShape.h | 194 ++ .../Constraints/CalculateSolverSteps.h | 66 + .../Physics/Constraints/ConeConstraint.cpp | 246 ++ .../Jolt/Physics/Constraints/ConeConstraint.h | 133 + .../Jolt/Physics/Constraints/Constraint.cpp | 73 + .../Jolt/Physics/Constraints/Constraint.h | 238 ++ .../Physics/Constraints/ConstraintManager.cpp | 289 ++ .../Physics/Constraints/ConstraintManager.h | 99 + .../ConstraintPart/AngleConstraintPart.h | 257 ++ .../ConstraintPart/AxisConstraintPart.h | 682 ++++ .../ConstraintPart/DualAxisConstraintPart.h | 276 ++ .../ConstraintPart/GearConstraintPart.h | 195 ++ .../HingeRotationConstraintPart.h | 222 ++ .../IndependentAxisConstraintPart.h | 246 ++ .../ConstraintPart/PointConstraintPart.h | 239 ++ .../RackAndPinionConstraintPart.h | 196 ++ .../RotationEulerConstraintPart.h | 270 ++ .../RotationQuatConstraintPart.h | 246 ++ .../Constraints/ConstraintPart/SpringPart.h | 169 + .../ConstraintPart/SwingTwistConstraintPart.h | 597 ++++ .../Constraints/ContactConstraintManager.cpp | 1718 ++++++++++ .../Constraints/ContactConstraintManager.h | 513 +++ .../Constraints/DistanceConstraint.cpp | 266 ++ .../Physics/Constraints/DistanceConstraint.h | 120 + .../Physics/Constraints/FixedConstraint.cpp | 215 ++ .../Physics/Constraints/FixedConstraint.h | 96 + .../Physics/Constraints/GearConstraint.cpp | 188 ++ .../Jolt/Physics/Constraints/GearConstraint.h | 116 + .../Physics/Constraints/HingeConstraint.cpp | 424 +++ .../Physics/Constraints/HingeConstraint.h | 182 ++ .../Physics/Constraints/MotorSettings.cpp | 43 + .../Jolt/Physics/Constraints/MotorSettings.h | 66 + .../Physics/Constraints/PathConstraint.cpp | 458 +++ .../Jolt/Physics/Constraints/PathConstraint.h | 186 ++ .../Constraints/PathConstraintPath.cpp | 85 + .../Physics/Constraints/PathConstraintPath.h | 71 + .../Constraints/PathConstraintPathHermite.cpp | 308 ++ .../Constraints/PathConstraintPathHermite.h | 54 + .../Physics/Constraints/PointConstraint.cpp | 157 + .../Physics/Constraints/PointConstraint.h | 94 + .../Physics/Constraints/PulleyConstraint.cpp | 253 ++ .../Physics/Constraints/PulleyConstraint.h | 137 + .../Constraints/RackAndPinionConstraint.cpp | 189 ++ .../Constraints/RackAndPinionConstraint.h | 118 + .../Physics/Constraints/SixDOFConstraint.cpp | 900 ++++++ .../Physics/Constraints/SixDOFConstraint.h | 289 ++ .../Physics/Constraints/SliderConstraint.cpp | 501 +++ .../Physics/Constraints/SliderConstraint.h | 198 ++ .../Physics/Constraints/SpringSettings.cpp | 35 + .../Jolt/Physics/Constraints/SpringSettings.h | 70 + .../Constraints/SwingTwistConstraint.cpp | 524 ++++ .../Constraints/SwingTwistConstraint.h | 197 ++ .../Physics/Constraints/TwoBodyConstraint.cpp | 56 + .../Physics/Constraints/TwoBodyConstraint.h | 65 + .../Jolt/Physics/DeterminismLog.cpp | 17 + .../JoltPhysics/Jolt/Physics/DeterminismLog.h | 159 + .../JoltPhysics/Jolt/Physics/EActivation.h | 16 + .../Jolt/Physics/EPhysicsUpdateError.h | 37 + .../Jolt/Physics/IslandBuilder.cpp | 484 +++ .../JoltPhysics/Jolt/Physics/IslandBuilder.h | 125 + .../Jolt/Physics/LargeIslandSplitter.cpp | 579 ++++ .../Jolt/Physics/LargeIslandSplitter.h | 185 ++ .../JoltPhysics/Jolt/Physics/PhysicsLock.cpp | 17 + .../JoltPhysics/Jolt/Physics/PhysicsLock.h | 169 + .../JoltPhysics/Jolt/Physics/PhysicsScene.cpp | 261 ++ .../JoltPhysics/Jolt/Physics/PhysicsScene.h | 104 + .../Jolt/Physics/PhysicsSettings.h | 119 + .../Jolt/Physics/PhysicsStepListener.h | 27 + .../Jolt/Physics/PhysicsSystem.cpp | 2703 ++++++++++++++++ .../JoltPhysics/Jolt/Physics/PhysicsSystem.h | 320 ++ .../Jolt/Physics/PhysicsUpdateContext.cpp | 23 + .../Jolt/Physics/PhysicsUpdateContext.h | 172 + .../Jolt/Physics/Ragdoll/Ragdoll.cpp | 699 +++++ .../Jolt/Physics/Ragdoll/Ragdoll.h | 240 ++ .../SoftBody/SoftBodyContactListener.h | 55 + .../SoftBody/SoftBodyCreationSettings.cpp | 122 + .../SoftBody/SoftBodyCreationSettings.h | 73 + .../Jolt/Physics/SoftBody/SoftBodyManifold.h | 59 + .../SoftBody/SoftBodyMotionProperties.cpp | 1089 +++++++ .../SoftBody/SoftBodyMotionProperties.h | 254 ++ .../Jolt/Physics/SoftBody/SoftBodyShape.cpp | 338 ++ .../Jolt/Physics/SoftBody/SoftBodyShape.h | 73 + .../SoftBody/SoftBodySharedSettings.cpp | 698 +++++ .../Physics/SoftBody/SoftBodySharedSettings.h | 322 ++ .../Physics/SoftBody/SoftBodyUpdateContext.h | 67 + .../Jolt/Physics/SoftBody/SoftBodyVertex.h | 28 + .../JoltPhysics/Jolt/Physics/StateRecorder.h | 66 + .../Jolt/Physics/StateRecorderImpl.cpp | 90 + .../Jolt/Physics/StateRecorderImpl.h | 47 + .../Physics/Vehicle/MotorcycleController.cpp | 293 ++ .../Physics/Vehicle/MotorcycleController.h | 116 + .../Vehicle/TrackedVehicleController.cpp | 531 ++++ .../Vehicle/TrackedVehicleController.h | 166 + .../Physics/Vehicle/VehicleAntiRollBar.cpp | 33 + .../Jolt/Physics/Vehicle/VehicleAntiRollBar.h | 31 + .../Vehicle/VehicleCollisionTester.cpp | 376 +++ .../Physics/Vehicle/VehicleCollisionTester.h | 146 + .../Physics/Vehicle/VehicleConstraint.cpp | 681 ++++ .../Jolt/Physics/Vehicle/VehicleConstraint.h | 236 ++ .../Physics/Vehicle/VehicleController.cpp | 17 + .../Jolt/Physics/Vehicle/VehicleController.h | 80 + .../Physics/Vehicle/VehicleDifferential.cpp | 81 + .../Physics/Vehicle/VehicleDifferential.h | 39 + .../Jolt/Physics/Vehicle/VehicleEngine.cpp | 122 + .../Jolt/Physics/Vehicle/VehicleEngine.h | 93 + .../Jolt/Physics/Vehicle/VehicleTrack.cpp | 52 + .../Jolt/Physics/Vehicle/VehicleTrack.h | 56 + .../Physics/Vehicle/VehicleTransmission.cpp | 159 + .../Physics/Vehicle/VehicleTransmission.h | 87 + .../Jolt/Physics/Vehicle/Wheel.cpp | 93 + .../JoltPhysics/Jolt/Physics/Vehicle/Wheel.h | 148 + .../Vehicle/WheeledVehicleController.cpp | 845 +++++ .../Vehicle/WheeledVehicleController.h | 199 ++ .../JoltC/JoltPhysics/Jolt/RegisterTypes.cpp | 197 ++ .../JoltC/JoltPhysics/Jolt/RegisterTypes.h | 29 + .../Jolt/Renderer/DebugRenderer.cpp | 1071 +++++++ .../JoltPhysics/Jolt/Renderer/DebugRenderer.h | 347 +++ .../Jolt/Renderer/DebugRendererPlayback.cpp | 168 + .../Jolt/Renderer/DebugRendererPlayback.h | 48 + .../Jolt/Renderer/DebugRendererRecorder.cpp | 158 + .../Jolt/Renderer/DebugRendererRecorder.h | 130 + .../Jolt/Renderer/DebugRendererSimple.cpp | 80 + .../Jolt/Renderer/DebugRendererSimple.h | 88 + .../Jolt/Skeleton/SkeletalAnimation.cpp | 110 + .../Jolt/Skeleton/SkeletalAnimation.h | 77 + .../JoltPhysics/Jolt/Skeleton/Skeleton.cpp | 82 + .../JoltPhysics/Jolt/Skeleton/Skeleton.h | 72 + .../Jolt/Skeleton/SkeletonMapper.cpp | 237 ++ .../Jolt/Skeleton/SkeletonMapper.h | 145 + .../Jolt/Skeleton/SkeletonPose.cpp | 87 + .../JoltPhysics/Jolt/Skeleton/SkeletonPose.h | 82 + .../Jolt/TriangleGrouper/TriangleGrouper.h | 27 + .../TriangleGrouperClosestCentroid.cpp | 95 + .../TriangleGrouperClosestCentroid.h | 21 + .../TriangleGrouper/TriangleGrouperMorton.cpp | 49 + .../TriangleGrouper/TriangleGrouperMorton.h | 20 + .../TriangleSplitter/TriangleSplitter.cpp | 67 + .../Jolt/TriangleSplitter/TriangleSplitter.h | 84 + .../TriangleSplitterBinning.cpp | 112 + .../TriangleSplitterBinning.h | 52 + .../TriangleSplitterFixedLeafSize.cpp | 170 + .../TriangleSplitterFixedLeafSize.h | 55 + .../TriangleSplitterLongestAxis.cpp | 31 + .../TriangleSplitterLongestAxis.h | 28 + .../TriangleSplitter/TriangleSplitterMean.cpp | 40 + .../TriangleSplitter/TriangleSplitterMean.h | 28 + .../TriangleSplitterMorton.cpp | 63 + .../TriangleSplitter/TriangleSplitterMorton.h | 32 + .../JoltPhysics/JoltViewer/JoltViewer.cmake | 20 + .../JoltPhysics/JoltViewer/JoltViewer.cpp | 154 + .../JoltC/JoltPhysics/JoltViewer/JoltViewer.h | 46 + .../JoltC/JoltPhysics/LICENSE | 7 + .../PerformanceTest/ConvexVsMeshScene.h | 122 + .../JoltPhysics/PerformanceTest/Layers.h | 100 + .../PerformanceTest/PerformanceTest.cmake | 16 + .../PerformanceTest/PerformanceTest.cpp | 464 +++ .../PerformanceTest/PerformanceTestScene.h | 25 + .../PerformanceTest/PyramidScene.h | 48 + .../PerformanceTest/RagdollScene.h | 140 + .../JoltC/JoltPhysics/README.md | 159 + .../JoltPhysics/WebIncludes/profile_chart.css | 36 + .../JoltPhysics/WebIncludes/profile_chart.js | 285 ++ .../JoltC/JoltPhysics/run_doxygen.bat | 4 + .../JoltPhysics/sonar-project.properties | 14 + crates/joltc-sys-patched/JoltC/LICENSE-APACHE | 201 ++ crates/joltc-sys-patched/JoltC/LICENSE-MIT | 21 + crates/joltc-sys-patched/JoltC/README.md | 45 + .../joltc-sys-patched/JoltC/test-all-flags.sh | 16 + crates/joltc-sys-patched/README.md | 10 + crates/joltc-sys-patched/build.rs | 166 + crates/joltc-sys-patched/src/generated.rs | 5 + crates/joltc-sys-patched/src/lib.rs | 40 + .../joltc-sys-patched/tests/framework/mod.rs | 206 ++ crates/joltc-sys-patched/tests/smoke_test.rs | 246 ++ modules/physics-test-cs/Lib.cs | 380 +++ .../physics-test-cs/physics-test-cs.csproj | 8 + 577 files changed, 109603 insertions(+), 40 deletions(-) create mode 100644 crates/bindings-csharp/Runtime/Physics.cs create mode 100644 crates/core/src/host/physics.rs create mode 100644 crates/joltc-sys-patched/.cargo-ok create mode 100644 crates/joltc-sys-patched/.cargo_vcs_info.json create mode 100644 crates/joltc-sys-patched/.gitignore create mode 100644 crates/joltc-sys-patched/Cargo.toml create mode 100644 crates/joltc-sys-patched/Cargo.toml.orig create mode 100644 crates/joltc-sys-patched/JoltC/.editorconfig create mode 100644 crates/joltc-sys-patched/JoltC/.github/workflows/ci.yml create mode 100644 crates/joltc-sys-patched/JoltC/.gitignore create mode 100644 crates/joltc-sys-patched/JoltC/.gitmodules create mode 100644 crates/joltc-sys-patched/JoltC/CMakeLists.txt create mode 100644 crates/joltc-sys-patched/JoltC/HelloWorld/README.md create mode 100644 crates/joltc-sys-patched/JoltC/HelloWorld/main.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltC/Enums.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltC/Functions.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltC/JoltC.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltC/JoltC.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltC/Test.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltC/Vehicle.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltC/Vehicle.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/.clang-format create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/.editorconfig create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/.gitattributes create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/.github/dependabot.yml create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/.github/workflows/build.yml create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/.github/workflows/determinism_check.yml create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/.github/workflows/doxygen.yml create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/.github/workflows/sonar-cloud.yml create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/.gitignore create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Build/.gitignore create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/.gitignore create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/PerformanceTest/build.gradle create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/PerformanceTest/src/main/AndroidManifest.xml create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/PerformanceTest/src/main/cpp/CMakeLists.txt create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/UnitTests/build.gradle create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/UnitTests/src/main/AndroidManifest.xml create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/UnitTests/src/main/cpp/CMakeLists.txt create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/build.gradle create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/gradle.properties create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/gradle/wrapper/gradle-wrapper.jar create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/gradle/wrapper/gradle-wrapper.properties create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/gradlew create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/gradlew.bat create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/settings.gradle create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Build/CMakeLists.txt create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Build/README.md create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_linux_clang_gcc.sh create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_linux_mingw.sh create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2019_cl.bat create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2019_cl_arm.bat create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2019_cl_arm_32bit.bat create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2019_clang.bat create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_cl.bat create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_cl_32bit.bat create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_cl_arm.bat create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_cl_arm_32bit.bat create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_cl_cross_platform_deterministic.bat create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_cl_double.bat create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_clang.bat create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_clang_double.bat create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_uwp.bat create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_uwp_arm.bat create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_windows_mingw.sh create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_xcode_ios.sh create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_xcode_macos.sh create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Build/iOS/UnitTestsInfo.plist create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/ContributorAgreement.md create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/APIChanges.md create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Architecture.md create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Images/ActiveEdge.jpg create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Images/ActiveVsInactiveContactNormal.jpg create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Images/ConvexRadius.jpg create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Images/EllipsoidAABB.png create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Images/GhostCollision.jpg create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Images/LongAndThin.jpg create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Images/MotionQuality.jpg create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Images/MotorDamping.jpg create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Images/MotorFrequency.jpg create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Images/QuadTreeExample.png create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Images/ShapeCenterOfMass.jpg create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Images/SimulationIsland.jpg create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Images/SoftBodySkinnedConstraint.jpg create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Logo.png create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/LogoDark.png create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/LogoSmall.png create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/PerformanceTest.md create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/PhysicsSystemUpdate.drawio create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/PhysicsSystemUpdate.svg create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/ProjectsUsingJolt.md create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/ReleaseNotes.md create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Samples.md create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/SwingTwistConstraint.png create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Doxyfile create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/HelloWorld/HelloWorld.cmake create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/HelloWorld/HelloWorld.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/AABBTree/AABBTreeBuilder.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/AABBTree/AABBTreeBuilder.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/AABBTree/AABBTreeToBuffer.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/AABBTree/NodeCodec/NodeCodecQuadTreeHalfFloat.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/AABBTree/TriangleCodec/TriangleCodecIndexed8BitPackSOA4Flags.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ConfigurationString.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/ARMNeon.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Atomics.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/ByteBuffer.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Color.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Color.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Core.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/FPControlWord.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/FPException.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/FPFlushDenormals.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Factory.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Factory.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/FixedSizeFreeList.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/FixedSizeFreeList.inl create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/HashCombine.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/InsertionSort.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/IssueReporting.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/IssueReporting.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/JobSystem.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/JobSystem.inl create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/JobSystemSingleThreaded.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/JobSystemSingleThreaded.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/JobSystemThreadPool.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/JobSystemThreadPool.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/JobSystemWithBarrier.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/JobSystemWithBarrier.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/LinearCurve.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/LinearCurve.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/LockFreeHashMap.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/LockFreeHashMap.inl create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Memory.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Memory.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Mutex.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/MutexArray.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/NonCopyable.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Profiler.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Profiler.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Profiler.inl create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/QuickSort.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/RTTI.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/RTTI.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Reference.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Result.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/STLAlignedAllocator.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/STLAllocator.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/STLTempAllocator.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Semaphore.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Semaphore.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/StaticArray.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/StreamIn.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/StreamOut.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/StreamUtils.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/StreamWrapper.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/StringTools.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/StringTools.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/TempAllocator.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/TickCounter.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/TickCounter.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/UnorderedMap.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/UnorderedSet.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/AABox.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/AABox4.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/ClipPoly.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/ClosestPoint.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/ConvexHullBuilder.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/ConvexHullBuilder.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/ConvexHullBuilder2D.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/ConvexHullBuilder2D.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/ConvexSupport.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/EPAConvexHullBuilder.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/EPAPenetrationDepth.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/Ellipse.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/GJKClosestPoint.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/IndexedTriangle.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/Indexify.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/Indexify.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/MortonCode.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/OrientedBox.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/OrientedBox.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/Plane.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/RayAABox.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/RayAABox8.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/RayCapsule.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/RayCylinder.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/RaySphere.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/RayTriangle.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/RayTriangle8.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/Sphere.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/Triangle.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Jolt.cmake create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Jolt.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Jolt.natvis create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/DMat44.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/DMat44.inl create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/DVec3.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/DVec3.inl create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Double3.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/DynMatrix.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/EigenValueSymmetric.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/FindRoot.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Float2.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Float3.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Float4.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/GaussianElimination.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/HalfFloat.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Mat44.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Mat44.inl create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Math.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/MathTypes.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Matrix.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Quat.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Quat.inl create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Real.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Swizzle.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Trigonometry.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/UVec4.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/UVec4.inl create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/UVec8.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/UVec8.inl create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Vec3.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Vec3.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Vec3.inl create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Vec4.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Vec4.inl create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Vec8.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Vec8.inl create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Vector.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/GetPrimitiveTypeOfType.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStream.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStream.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamBinaryIn.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamBinaryIn.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamBinaryOut.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamBinaryOut.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamIn.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamIn.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamOut.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamOut.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamTextIn.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamTextIn.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamTextOut.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamTextOut.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamTypes.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/SerializableAttribute.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/SerializableAttributeEnum.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/SerializableAttributeTyped.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/SerializableObject.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/SerializableObject.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/TypeDeclarations.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/TypeDeclarations.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/AllowedDOFs.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/Body.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/Body.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/Body.inl create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyAccess.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyAccess.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyActivationListener.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyCreationSettings.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyCreationSettings.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyFilter.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyID.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyInterface.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyInterface.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyLock.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyLockInterface.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyLockMulti.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyManager.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyManager.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyPair.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyType.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/MassProperties.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/MassProperties.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/MotionProperties.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/MotionProperties.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/MotionProperties.inl create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/MotionQuality.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/MotionType.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Character/Character.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Character/Character.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Character/CharacterBase.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Character/CharacterBase.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Character/CharacterVirtual.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Character/CharacterVirtual.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/AABoxCast.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ActiveEdgeMode.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ActiveEdges.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BackFaceMode.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhase.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhase.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhaseBruteForce.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhaseBruteForce.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhaseLayer.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhaseLayerInterfaceMask.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhaseLayerInterfaceTable.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhaseQuadTree.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhaseQuadTree.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhaseQuery.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/ObjectVsBroadPhaseLayerFilterMask.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/ObjectVsBroadPhaseLayerFilterTable.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/QuadTree.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/QuadTree.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CastConvexVsTriangles.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CastConvexVsTriangles.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CastResult.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CastSphereVsTriangles.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CastSphereVsTriangles.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollectFacesMode.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollideConvexVsTriangles.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollideConvexVsTriangles.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollidePointResult.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollideShape.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollideSoftBodyVerticesVsTriangles.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollideSphereVsTriangles.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollideSphereVsTriangles.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollisionCollector.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollisionCollectorImpl.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollisionDispatch.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollisionDispatch.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollisionGroup.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollisionGroup.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ContactListener.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/EstimateCollisionResponse.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/EstimateCollisionResponse.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/GroupFilter.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/GroupFilter.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/GroupFilterTable.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/GroupFilterTable.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/InternalEdgeRemovingCollector.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ManifoldBetweenTwoFaces.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ManifoldBetweenTwoFaces.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/NarrowPhaseQuery.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/NarrowPhaseQuery.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/NarrowPhaseStats.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/NarrowPhaseStats.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ObjectLayer.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ObjectLayerPairFilterMask.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ObjectLayerPairFilterTable.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/PhysicsMaterial.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/PhysicsMaterial.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/PhysicsMaterialSimple.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/PhysicsMaterialSimple.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/RayCast.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/BoxShape.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/BoxShape.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/CapsuleShape.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/CapsuleShape.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/CompoundShape.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/CompoundShape.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/CompoundShapeVisitors.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/ConvexHullShape.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/ConvexHullShape.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/ConvexShape.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/ConvexShape.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/CylinderShape.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/CylinderShape.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/DecoratedShape.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/DecoratedShape.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/GetTrianglesContext.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/HeightFieldShape.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/HeightFieldShape.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/MeshShape.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/MeshShape.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/MutableCompoundShape.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/MutableCompoundShape.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/OffsetCenterOfMassShape.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/OffsetCenterOfMassShape.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/PolyhedronSubmergedVolumeCalculator.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/RotatedTranslatedShape.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/RotatedTranslatedShape.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/ScaleHelpers.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/ScaledShape.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/ScaledShape.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/Shape.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/Shape.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/SphereShape.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/SphereShape.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/StaticCompoundShape.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/StaticCompoundShape.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/SubShapeID.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/SubShapeIDPair.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/TaperedCapsuleShape.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/TaperedCapsuleShape.gliffy create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/TaperedCapsuleShape.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/TriangleShape.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/TriangleShape.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ShapeCast.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ShapeFilter.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/SortReverseAndStore.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/TransformedShape.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/TransformedShape.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/CalculateSolverSteps.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConeConstraint.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConeConstraint.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/Constraint.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/Constraint.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintManager.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintManager.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/AngleConstraintPart.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/AxisConstraintPart.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/DualAxisConstraintPart.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/GearConstraintPart.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/HingeRotationConstraintPart.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/IndependentAxisConstraintPart.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/PointConstraintPart.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/RackAndPinionConstraintPart.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/RotationEulerConstraintPart.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/RotationQuatConstraintPart.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/SpringPart.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/SwingTwistConstraintPart.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ContactConstraintManager.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ContactConstraintManager.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/DistanceConstraint.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/DistanceConstraint.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/FixedConstraint.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/FixedConstraint.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/GearConstraint.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/GearConstraint.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/HingeConstraint.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/HingeConstraint.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/MotorSettings.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/MotorSettings.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PathConstraint.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PathConstraint.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PathConstraintPath.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PathConstraintPath.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PathConstraintPathHermite.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PathConstraintPathHermite.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PointConstraint.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PointConstraint.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PulleyConstraint.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PulleyConstraint.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/RackAndPinionConstraint.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/RackAndPinionConstraint.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/SixDOFConstraint.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/SixDOFConstraint.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/SliderConstraint.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/SliderConstraint.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/SpringSettings.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/SpringSettings.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/SwingTwistConstraint.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/SwingTwistConstraint.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/TwoBodyConstraint.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/TwoBodyConstraint.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/DeterminismLog.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/DeterminismLog.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/EActivation.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/EPhysicsUpdateError.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/IslandBuilder.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/IslandBuilder.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/LargeIslandSplitter.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/LargeIslandSplitter.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsLock.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsLock.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsScene.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsScene.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsSettings.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsStepListener.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsSystem.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsSystem.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsUpdateContext.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsUpdateContext.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Ragdoll/Ragdoll.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Ragdoll/Ragdoll.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyContactListener.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyCreationSettings.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyCreationSettings.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyManifold.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyMotionProperties.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyMotionProperties.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyShape.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyShape.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodySharedSettings.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodySharedSettings.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyUpdateContext.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyVertex.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/StateRecorder.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/StateRecorderImpl.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/StateRecorderImpl.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/MotorcycleController.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/MotorcycleController.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/TrackedVehicleController.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/TrackedVehicleController.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleAntiRollBar.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleAntiRollBar.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleCollisionTester.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleCollisionTester.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleConstraint.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleConstraint.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleController.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleController.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleDifferential.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleDifferential.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleEngine.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleEngine.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleTrack.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleTrack.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleTransmission.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleTransmission.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/Wheel.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/Wheel.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/WheeledVehicleController.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/WheeledVehicleController.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/RegisterTypes.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/RegisterTypes.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Renderer/DebugRenderer.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Renderer/DebugRenderer.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Renderer/DebugRendererPlayback.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Renderer/DebugRendererPlayback.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Renderer/DebugRendererRecorder.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Renderer/DebugRendererRecorder.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Renderer/DebugRendererSimple.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Renderer/DebugRendererSimple.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Skeleton/SkeletalAnimation.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Skeleton/SkeletalAnimation.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Skeleton/Skeleton.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Skeleton/Skeleton.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Skeleton/SkeletonMapper.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Skeleton/SkeletonMapper.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Skeleton/SkeletonPose.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Skeleton/SkeletonPose.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleGrouper/TriangleGrouper.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleGrouper/TriangleGrouperClosestCentroid.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleGrouper/TriangleGrouperClosestCentroid.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleGrouper/TriangleGrouperMorton.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleGrouper/TriangleGrouperMorton.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitter.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitter.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterBinning.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterBinning.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterFixedLeafSize.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterFixedLeafSize.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterLongestAxis.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterLongestAxis.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterMean.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterMean.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterMorton.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterMorton.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/JoltViewer/JoltViewer.cmake create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/JoltViewer/JoltViewer.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/JoltViewer/JoltViewer.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/LICENSE create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/PerformanceTest/ConvexVsMeshScene.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/PerformanceTest/Layers.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/PerformanceTest/PerformanceTest.cmake create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/PerformanceTest/PerformanceTest.cpp create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/PerformanceTest/PerformanceTestScene.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/PerformanceTest/PyramidScene.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/PerformanceTest/RagdollScene.h create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/README.md create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/WebIncludes/profile_chart.css create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/WebIncludes/profile_chart.js create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/run_doxygen.bat create mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/sonar-project.properties create mode 100644 crates/joltc-sys-patched/JoltC/LICENSE-APACHE create mode 100644 crates/joltc-sys-patched/JoltC/LICENSE-MIT create mode 100644 crates/joltc-sys-patched/JoltC/README.md create mode 100644 crates/joltc-sys-patched/JoltC/test-all-flags.sh create mode 100644 crates/joltc-sys-patched/README.md create mode 100644 crates/joltc-sys-patched/build.rs create mode 100644 crates/joltc-sys-patched/src/generated.rs create mode 100644 crates/joltc-sys-patched/src/lib.rs create mode 100644 crates/joltc-sys-patched/tests/framework/mod.rs create mode 100644 crates/joltc-sys-patched/tests/smoke_test.rs create mode 100644 modules/physics-test-cs/Lib.cs create mode 100644 modules/physics-test-cs/physics-test-cs.csproj diff --git a/.cargo/config.toml b/.cargo/config.toml index 949d1ed002f..cb4793f3221 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -14,6 +14,4 @@ smoketests = "smoketest" # I (@bfops) tried a variety of other link options besides switching linkers, but this # seems to be the only thing that worked. linker = "lld-link" -# Without this, the linker complains that libc functions are undefined - -# it probably signals to rustc and lld-link that libucrt should be included. rustflags = ["-Ctarget-feature=+crt-static"] diff --git a/Cargo.lock b/Cargo.lock index 95fd0e6e1c3..adacb3a26ac 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -482,6 +482,29 @@ dependencies = [ "serde", ] +[[package]] +name = "bindgen" +version = "0.69.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" +dependencies = [ + "bitflags 2.10.0", + "cexpr", + "clang-sys", + "itertools 0.12.1", + "lazy_static", + "lazycell", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash 1.1.0", + "shlex", + "syn 2.0.107", + "which 4.4.2", +] + [[package]] name = "bindgen" version = "0.72.1" @@ -497,7 +520,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "rustc-hash", + "rustc-hash 2.1.1", "shlex", "syn 2.0.107", ] @@ -788,9 +811,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.41" +version = "1.2.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac9fe6cdbb24b6ade63616c0a0688e45bb56732262c158df3c0c4bea4ca47cb7" +checksum = "7a0dd1ca384932ff3641c8718a02769f1698e7563dc6974ffd03346116310423" dependencies = [ "find-msvc-tools", "jobserver", @@ -1021,6 +1044,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "cmake" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75443c44cd6b379beb8c5b45d85d0773baf31cce901fe7bb252f4eff3008ef7d" +dependencies = [ + "cc", +] + [[package]] name = "cobs" version = "0.3.0" @@ -1259,7 +1291,7 @@ dependencies = [ "log", "pulley-interpreter", "regalloc2", - "rustc-hash", + "rustc-hash 2.1.1", "serde", "smallvec", "target-lexicon", @@ -2186,9 +2218,9 @@ dependencies = [ [[package]] name = "find-msvc-tools" -version = "0.1.4" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" [[package]] name = "fixedbitset" @@ -2533,6 +2565,18 @@ dependencies = [ "url", ] +[[package]] +name = "glam" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e05e7e6723e3455f4818c7b26e855439f7546cf617ef669d1adedb8669e5cb9" + +[[package]] +name = "glam" +version = "0.29.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8babf46d4c1c9d92deac9f7be466f76dfc4482b6452fc5024b5e8daf6ffeb3ee" + [[package]] name = "glob" version = "0.3.3" @@ -3502,6 +3546,17 @@ dependencies = [ "libc", ] +[[package]] +name = "joltc-sys" +version = "0.3.1+Jolt-5.0.0" +dependencies = [ + "anyhow", + "bindgen 0.69.5", + "cc", + "cmake", + "walkdir", +] + [[package]] name = "js-sys" version = "0.3.81" @@ -3614,6 +3669,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + [[package]] name = "lean_string" version = "0.5.1" @@ -4096,7 +4157,7 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "496f264d3ead4870d6b366deb9d20597592d64aac2a907f3e7d07c2325ba4663" dependencies = [ - "bindgen", + "bindgen 0.72.1", "bitflags 2.10.0", "byteorder", "netlink-packet-core", @@ -4561,7 +4622,7 @@ checksum = "75b1853bc34cadaa90aa09f95713d8b77ec0c0d3e2d90ccf7a74216f40d20850" dependencies = [ "flate2", "postcard", - "rustc-hash", + "rustc-hash 2.1.1", "serde", "serde_json", "thiserror 2.0.17", @@ -4603,7 +4664,7 @@ dependencies = [ "hashbrown 0.16.1", "oxc_data_structures", "oxc_estree", - "rustc-hash", + "rustc-hash 2.1.1", "serde", ] @@ -4659,7 +4720,7 @@ dependencies = [ "oxc_index", "oxc_syntax", "petgraph 0.8.3", - "rustc-hash", + "rustc-hash 2.1.1", ] [[package]] @@ -4680,7 +4741,7 @@ dependencies = [ "oxc_sourcemap", "oxc_span", "oxc_syntax", - "rustc-hash", + "rustc-hash 2.1.1", ] [[package]] @@ -4692,7 +4753,7 @@ dependencies = [ "cow-utils", "oxc-browserslist", "oxc_syntax", - "rustc-hash", + "rustc-hash 2.1.1", "serde", ] @@ -4768,7 +4829,7 @@ dependencies = [ "oxc_ecmascript", "oxc_span", "oxc_syntax", - "rustc-hash", + "rustc-hash 2.1.1", ] [[package]] @@ -4785,7 +4846,7 @@ dependencies = [ "oxc_semantic", "oxc_span", "oxc_syntax", - "rustc-hash", + "rustc-hash 2.1.1", ] [[package]] @@ -4809,7 +4870,7 @@ dependencies = [ "oxc_span", "oxc_syntax", "oxc_traverse", - "rustc-hash", + "rustc-hash 2.1.1", ] [[package]] @@ -4831,7 +4892,7 @@ dependencies = [ "oxc_regular_expression", "oxc_span", "oxc_syntax", - "rustc-hash", + "rustc-hash 2.1.1", "seq-macro", ] @@ -4847,7 +4908,7 @@ dependencies = [ "oxc_diagnostics", "oxc_span", "phf", - "rustc-hash", + "rustc-hash 2.1.1", "unicode-id-start", ] @@ -4866,7 +4927,7 @@ dependencies = [ "papaya", "parking_lot 0.12.5", "pnp", - "rustc-hash", + "rustc-hash 2.1.1", "rustix 1.1.2", "self_cell", "serde", @@ -4897,7 +4958,7 @@ dependencies = [ "oxc_index", "oxc_span", "oxc_syntax", - "rustc-hash", + "rustc-hash 2.1.1", "self_cell", "smallvec", ] @@ -4910,7 +4971,7 @@ checksum = "36801dbbd025f2fa133367494e38eef75a53d334ae6746ba0c889fc4e76fa3a3" dependencies = [ "base64-simd", "json-escape-simd", - "rustc-hash", + "rustc-hash 2.1.1", "serde", "serde_json", ] @@ -4986,7 +5047,7 @@ dependencies = [ "oxc_span", "oxc_syntax", "oxc_traverse", - "rustc-hash", + "rustc-hash 2.1.1", "serde", "serde_json", "sha1", @@ -5011,7 +5072,7 @@ dependencies = [ "oxc_syntax", "oxc_transformer", "oxc_traverse", - "rustc-hash", + "rustc-hash 2.1.1", ] [[package]] @@ -5029,7 +5090,7 @@ dependencies = [ "oxc_semantic", "oxc_span", "oxc_syntax", - "rustc-hash", + "rustc-hash 2.1.1", ] [[package]] @@ -5394,7 +5455,7 @@ dependencies = [ "nodejs-built-in-modules 1.0.0", "pathdiff", "radix_trie 0.3.0", - "rustc-hash", + "rustc-hash 2.1.1", "serde", "serde_json", "thiserror 2.0.17", @@ -6077,7 +6138,7 @@ dependencies = [ "bumpalo", "hashbrown 0.15.5", "log", - "rustc-hash", + "rustc-hash 2.1.1", "smallvec", ] @@ -6379,7 +6440,7 @@ dependencies = [ "rolldown_std_utils", "rolldown_tracing", "rolldown_utils", - "rustc-hash", + "rustc-hash 2.1.1", "serde", "serde_json", "string_wizard", @@ -6447,7 +6508,7 @@ dependencies = [ "rolldown_sourcemap", "rolldown_std_utils", "rolldown_utils", - "rustc-hash", + "rustc-hash 2.1.1", "serde", "serde_json", "simdutf8", @@ -6474,7 +6535,7 @@ dependencies = [ "blake3", "dashmap 6.1.0", "rolldown_devtools_action", - "rustc-hash", + "rustc-hash 2.1.1", "serde", "serde_json", "tracing", @@ -6528,7 +6589,7 @@ dependencies = [ "oxc_resolver", "rolldown-ariadne", "ropey", - "rustc-hash", + "rustc-hash 2.1.1", "sugar_path", ] @@ -6561,7 +6622,7 @@ dependencies = [ "rolldown_resolver", "rolldown_sourcemap", "rolldown_utils", - "rustc-hash", + "rustc-hash 2.1.1", "serde", "serde_json", "string_wizard", @@ -6580,7 +6641,7 @@ dependencies = [ "rolldown_common", "rolldown_plugin", "rolldown_utils", - "rustc-hash", + "rustc-hash 2.1.1", "serde_json", "xxhash-rust", ] @@ -6659,7 +6720,7 @@ dependencies = [ "oxc", "oxc_sourcemap", "rolldown_utils", - "rustc-hash", + "rustc-hash 2.1.1", ] [[package]] @@ -6709,7 +6770,7 @@ dependencies = [ "regress", "rolldown_error", "rolldown_std_utils", - "rustc-hash", + "rustc-hash 2.1.1", "serde_json", "simdutf8", "sugar_path", @@ -6718,6 +6779,17 @@ dependencies = [ "xxhash-rust", ] +[[package]] +name = "rolt" +version = "0.3.1+Jolt-5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd0675375c16f769f5c726fecd4ffea39f1ee4ebb46b9060d7dd6a58db5d3961" +dependencies = [ + "glam 0.27.0", + "joltc-sys", + "paste", +] + [[package]] name = "ron" version = "0.8.1" @@ -6777,6 +6849,12 @@ version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + [[package]] name = "rustc-hash" version = "2.1.1" @@ -7463,7 +7541,7 @@ dependencies = [ "data-encoding", "debugid", "if_chain", - "rustc-hash", + "rustc-hash 2.1.1", "serde", "serde_json", "unicode-id-start", @@ -7868,6 +7946,7 @@ dependencies = [ "fs_extra", "futures", "futures-util", + "glam 0.29.3", "hex", "hostname", "http 1.3.1", @@ -7876,6 +7955,7 @@ dependencies = [ "imara-diff", "indexmap 2.12.0", "itertools 0.12.1", + "joltc-sys", "lazy_static", "log", "memchr", @@ -7895,8 +7975,9 @@ dependencies = [ "rayon-core", "regex", "reqwest 0.12.24", + "rolt", "rustc-demangle", - "rustc-hash", + "rustc-hash 2.1.1", "scopeguard", "semver", "serde", @@ -8809,7 +8890,7 @@ dependencies = [ "oxc_index", "oxc_sourcemap", "regex", - "rustc-hash", + "rustc-hash 2.1.1", "serde", ] @@ -10049,7 +10130,7 @@ version = "145.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61d9a107e16bae35a0be2bb0096ac1d2318aac352c82edd796ab2b9cac66d8f0" dependencies = [ - "bindgen", + "bindgen 0.72.1", "bitflags 2.10.0", "fslock", "gzip-header", diff --git a/Cargo.toml b/Cargo.toml index e4066079213..84b41fa6417 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -350,6 +350,11 @@ tikv-jemalloc-ctl = { version = "0.6.0", features = ["stats"] } jemalloc_pprof = { version = "0.8", features = ["symbolize", "flamegraph"] } zstd-framed = { version = "0.1.1", features = ["tokio"] } +# Physics +rolt = "0.3" +joltc-sys = "0.3" +glam = "0.29" + # Vendor the openssl we rely on, rather than depend on a # potentially very old system version. openssl = { version = "0.10", features = ["vendored"] } @@ -399,3 +404,8 @@ result_large_err = "allow" # This silences a warning in our Nix flake, which otherwise would be upset that our call to `crateNameFromCargoToml` # is running against a file that doesn't have a name in it. crane.name = "spacetimedb" + +[patch.crates-io] +# Patched to use static CRT (/MT) on Windows when crt-static is enabled, +# fixing RuntimeLibrary mismatch with SpacetimeDB's linker config. +joltc-sys = { path = "crates/joltc-sys-patched" } diff --git a/crates/bindings-csharp/Runtime/Internal/FFI.cs b/crates/bindings-csharp/Runtime/Internal/FFI.cs index 9cf994e929b..eb9932e30d7 100644 --- a/crates/bindings-csharp/Runtime/Internal/FFI.cs +++ b/crates/bindings-csharp/Runtime/Internal/FFI.cs @@ -397,4 +397,135 @@ public static partial Errno procedure_http_request( uint body_len, out BytesSourcePair out_ ); + + // ==================== Physics (Jolt) ==================== + + const string StdbNamespace10_5 = +#if EXPERIMENTAL_WASM_AOT + "spacetime_10.5" +#else + "bindings" +#endif + ; + + [LibraryImport(StdbNamespace10_5, EntryPoint = "physics_create_world")] + public static partial CheckedStatus physics_create_world( + ReadOnlySpan gravity, // 3 x f32 = 12 bytes + out uint world_id + ); + + [LibraryImport(StdbNamespace10_5, EntryPoint = "physics_destroy_world")] + public static partial CheckedStatus physics_destroy_world(uint world_id); + + [LibraryImport(StdbNamespace10_5, EntryPoint = "physics_create_body")] + public static partial CheckedStatus physics_create_body( + uint world_id, + ReadOnlySpan body_params, // 54 bytes packed struct + out uint body_id + ); + + [LibraryImport(StdbNamespace10_5, EntryPoint = "physics_destroy_body")] + public static partial CheckedStatus physics_destroy_body(uint world_id, uint body_id); + + [LibraryImport(StdbNamespace10_5, EntryPoint = "physics_step")] + public static partial CheckedStatus physics_step( + uint world_id, + uint delta_time_bits, // f32 as bits + uint num_steps + ); + + [LibraryImport(StdbNamespace10_5, EntryPoint = "physics_get_body_transform")] + public static partial CheckedStatus physics_get_body_transform( + uint world_id, + uint body_id, + Span out_ // 7 x f32 = 28 bytes [pos.xyz, rot.xyzw] + ); + + [LibraryImport(StdbNamespace10_5, EntryPoint = "physics_get_body_velocity")] + public static partial CheckedStatus physics_get_body_velocity( + uint world_id, + uint body_id, + Span out_ // 3 x f32 = 12 bytes + ); + + [LibraryImport(StdbNamespace10_5, EntryPoint = "physics_set_body_position")] + public static partial CheckedStatus physics_set_body_position( + uint world_id, + uint body_id, + ReadOnlySpan position // 3 x f32 = 12 bytes + ); + + [LibraryImport(StdbNamespace10_5, EntryPoint = "physics_set_body_velocity")] + public static partial CheckedStatus physics_set_body_velocity( + uint world_id, + uint body_id, + ReadOnlySpan velocity // 3 x f32 = 12 bytes + ); + + [LibraryImport(StdbNamespace10_5, EntryPoint = "physics_set_body_rotation")] + public static partial CheckedStatus physics_set_body_rotation( + uint world_id, + uint body_id, + ReadOnlySpan rotation // 4 x f32 = 16 bytes + ); + + [LibraryImport(StdbNamespace10_5, EntryPoint = "physics_add_force")] + public static partial CheckedStatus physics_add_force( + uint world_id, + uint body_id, + ReadOnlySpan force // 3 x f32 = 12 bytes + ); + + [LibraryImport(StdbNamespace10_5, EntryPoint = "physics_add_impulse")] + public static partial CheckedStatus physics_add_impulse( + uint world_id, + uint body_id, + ReadOnlySpan impulse // 3 x f32 = 12 bytes + ); + + [LibraryImport(StdbNamespace10_5, EntryPoint = "physics_get_all_body_transforms")] + public static partial CheckedStatus physics_get_all_body_transforms( + uint world_id, + out uint bytes_source_id // BytesSource with packed 44-byte records + ); + + // ── Vehicle Physics ──────────────────────────────────────────── + + [LibraryImport(StdbNamespace10_5, EntryPoint = "physics_create_vehicle")] + public static partial CheckedStatus physics_create_vehicle( + uint world_id, + ReadOnlySpan params_ptr, + uint params_len, + ReadOnlySpan pos_ptr, // 3 x f32 = 12 bytes + ReadOnlySpan rot_ptr, // 4 x f32 = 16 bytes + out uint vehicle_id + ); + + [LibraryImport(StdbNamespace10_5, EntryPoint = "physics_set_vehicle_input")] + public static partial CheckedStatus physics_set_vehicle_input( + uint world_id, + uint vehicle_id, + uint forward_bits, + uint right_bits, + uint brake_bits, + uint handbrake_bits + ); + + [LibraryImport(StdbNamespace10_5, EntryPoint = "physics_get_vehicle_state")] + public static partial CheckedStatus physics_get_vehicle_state( + uint world_id, + uint vehicle_id, + out uint bytes_source_id + ); + + [LibraryImport(StdbNamespace10_5, EntryPoint = "physics_destroy_vehicle")] + public static partial CheckedStatus physics_destroy_vehicle( + uint world_id, + uint vehicle_id + ); + + [LibraryImport(StdbNamespace10_5, EntryPoint = "physics_get_vehicle_params_default")] + public static partial CheckedStatus physics_get_vehicle_params_default( + out uint bytes_source_id + ); } diff --git a/crates/bindings-csharp/Runtime/Physics.cs b/crates/bindings-csharp/Runtime/Physics.cs new file mode 100644 index 00000000000..033eb33ff41 --- /dev/null +++ b/crates/bindings-csharp/Runtime/Physics.cs @@ -0,0 +1,422 @@ +namespace SpacetimeDB; + +using System; +using System.Buffers.Binary; +using System.Runtime.InteropServices; +using SpacetimeDB.Internal; + +/// +/// Motion type for physics bodies. +/// +public enum PhysicsMotionType : byte +{ + Static = 0, + Kinematic = 1, + Dynamic = 2, +} + +/// +/// Shape type for physics collision shapes. +/// +public enum PhysicsShapeType : byte +{ + Sphere = 0, + Box = 1, + Capsule = 2, +} + +/// +/// Transform data for a physics body. +/// +public struct PhysicsTransform +{ + public float PosX, PosY, PosZ; + public float RotX, RotY, RotZ, RotW; +} + +/// +/// Bulk body state returned by GetAllBodyTransforms. +/// +public struct PhysicsBodyState +{ + public uint BodyId; + public float PosX, PosY, PosZ; + public float RotX, RotY, RotZ, RotW; + public float VelX, VelY, VelZ; +} + +/// +/// Jolt Physics integration for SpacetimeDB modules. +/// +/// The physics world lives in host memory (native performance) and is accessible +/// from any module language via host functions. State is volatile — modules must +/// persist authoritative data in tables and reconstruct the world on restart. +/// +/// Usage: +/// +/// // In your Init reducer: +/// var worldId = Physics.CreateWorld(0f, -9.81f, 0f); +/// +/// // Create a ground plane (static box) +/// var ground = Physics.CreateBody(worldId, PhysicsShapeType.Box, +/// halfExtents: (50f, 0.5f, 50f), +/// position: (0f, -0.5f, 0f), +/// motionType: PhysicsMotionType.Static); +/// +/// // Create a dynamic sphere +/// var ball = Physics.CreateBody(worldId, PhysicsShapeType.Sphere, +/// radius: 0.5f, +/// position: (0f, 10f, 0f), +/// motionType: PhysicsMotionType.Dynamic, +/// mass: 1f, restitution: 0.8f); +/// +/// // In your PhysicsTick reducer (30Hz): +/// Physics.Step(worldId, 1f / 30f); +/// var states = Physics.GetAllBodyTransforms(worldId); +/// foreach (var s in states) { /* update tables */ } +/// +/// +public static class Physics +{ + /// + /// Create a new physics world with the given gravity. + /// + public static uint CreateWorld(float gravityX, float gravityY, float gravityZ) + { + Span gravity = stackalloc byte[12]; + BinaryPrimitives.WriteSingleLittleEndian(gravity[0..], gravityX); + BinaryPrimitives.WriteSingleLittleEndian(gravity[4..], gravityY); + BinaryPrimitives.WriteSingleLittleEndian(gravity[8..], gravityZ); + FFI.physics_create_world(gravity, out var worldId); + return worldId; + } + + /// + /// Destroy a physics world and all its bodies. + /// + public static void DestroyWorld(uint worldId) + { + FFI.physics_destroy_world(worldId); + } + + /// + /// Create a sphere body. + /// + public static uint CreateBody( + uint worldId, + PhysicsShapeType shapeType, + float radius = 0.5f, + (float x, float y, float z) halfExtents = default, + float halfHeight = 0f, + (float x, float y, float z) position = default, + (float x, float y, float z, float w) rotation = default, + PhysicsMotionType motionType = PhysicsMotionType.Dynamic, + float mass = 1f, + float restitution = 0.3f, + float friction = 0.5f) + { + if (rotation == default) + rotation = (0f, 0f, 0f, 1f); // identity quaternion + + // Pack the 54-byte params struct + Span data = stackalloc byte[54]; + data[0] = (byte)shapeType; + + // shape_params: depends on type + switch (shapeType) + { + case PhysicsShapeType.Sphere: + BinaryPrimitives.WriteSingleLittleEndian(data[1..], radius); + BinaryPrimitives.WriteSingleLittleEndian(data[5..], 0f); + BinaryPrimitives.WriteSingleLittleEndian(data[9..], 0f); + break; + case PhysicsShapeType.Box: + BinaryPrimitives.WriteSingleLittleEndian(data[1..], halfExtents.x); + BinaryPrimitives.WriteSingleLittleEndian(data[5..], halfExtents.y); + BinaryPrimitives.WriteSingleLittleEndian(data[9..], halfExtents.z); + break; + case PhysicsShapeType.Capsule: + BinaryPrimitives.WriteSingleLittleEndian(data[1..], radius); + BinaryPrimitives.WriteSingleLittleEndian(data[5..], halfHeight); + BinaryPrimitives.WriteSingleLittleEndian(data[9..], 0f); + break; + } + + // position + BinaryPrimitives.WriteSingleLittleEndian(data[13..], position.x); + BinaryPrimitives.WriteSingleLittleEndian(data[17..], position.y); + BinaryPrimitives.WriteSingleLittleEndian(data[21..], position.z); + + // rotation (quaternion xyzw) + BinaryPrimitives.WriteSingleLittleEndian(data[25..], rotation.x); + BinaryPrimitives.WriteSingleLittleEndian(data[29..], rotation.y); + BinaryPrimitives.WriteSingleLittleEndian(data[33..], rotation.z); + BinaryPrimitives.WriteSingleLittleEndian(data[37..], rotation.w); + + // motion_type + data[41] = (byte)motionType; + + // mass, restitution, friction + BinaryPrimitives.WriteSingleLittleEndian(data[42..], mass); + BinaryPrimitives.WriteSingleLittleEndian(data[46..], restitution); + BinaryPrimitives.WriteSingleLittleEndian(data[50..], friction); + + FFI.physics_create_body(worldId, data, out var bodyId); + return bodyId; + } + + /// + /// Destroy a body from a physics world. + /// + public static void DestroyBody(uint worldId, uint bodyId) + { + FFI.physics_destroy_body(worldId, bodyId); + } + + /// + /// Step the physics simulation. + /// + /// World to step. + /// Time step in seconds (e.g., 1/30f for 30Hz). + /// Number of collision integration sub-steps (default 1). + public static void Step(uint worldId, float deltaTime, uint numSteps = 1) + { + FFI.physics_step(worldId, BitConverter.SingleToUInt32Bits(deltaTime), numSteps); + } + + /// + /// Get a body's position and rotation. + /// + public static PhysicsTransform GetBodyTransform(uint worldId, uint bodyId) + { + Span buf = stackalloc byte[28]; + FFI.physics_get_body_transform(worldId, bodyId, buf); + return new PhysicsTransform + { + PosX = BinaryPrimitives.ReadSingleLittleEndian(buf[0..]), + PosY = BinaryPrimitives.ReadSingleLittleEndian(buf[4..]), + PosZ = BinaryPrimitives.ReadSingleLittleEndian(buf[8..]), + RotX = BinaryPrimitives.ReadSingleLittleEndian(buf[12..]), + RotY = BinaryPrimitives.ReadSingleLittleEndian(buf[16..]), + RotZ = BinaryPrimitives.ReadSingleLittleEndian(buf[20..]), + RotW = BinaryPrimitives.ReadSingleLittleEndian(buf[24..]), + }; + } + + /// + /// Get a body's linear velocity. + /// + public static (float x, float y, float z) GetBodyVelocity(uint worldId, uint bodyId) + { + Span buf = stackalloc byte[12]; + FFI.physics_get_body_velocity(worldId, bodyId, buf); + return ( + BinaryPrimitives.ReadSingleLittleEndian(buf[0..]), + BinaryPrimitives.ReadSingleLittleEndian(buf[4..]), + BinaryPrimitives.ReadSingleLittleEndian(buf[8..]) + ); + } + + /// + /// Set a body's position. + /// + public static void SetBodyPosition(uint worldId, uint bodyId, float x, float y, float z) + { + Span buf = stackalloc byte[12]; + BinaryPrimitives.WriteSingleLittleEndian(buf[0..], x); + BinaryPrimitives.WriteSingleLittleEndian(buf[4..], y); + BinaryPrimitives.WriteSingleLittleEndian(buf[8..], z); + FFI.physics_set_body_position(worldId, bodyId, buf); + } + + /// + /// Set a body's linear velocity. + /// + public static void SetBodyVelocity(uint worldId, uint bodyId, float x, float y, float z) + { + Span buf = stackalloc byte[12]; + BinaryPrimitives.WriteSingleLittleEndian(buf[0..], x); + BinaryPrimitives.WriteSingleLittleEndian(buf[4..], y); + BinaryPrimitives.WriteSingleLittleEndian(buf[8..], z); + FFI.physics_set_body_velocity(worldId, bodyId, buf); + } + + /// + /// Set a body's rotation (quaternion). + /// + public static void SetBodyRotation(uint worldId, uint bodyId, float x, float y, float z, float w) + { + Span buf = stackalloc byte[16]; + BinaryPrimitives.WriteSingleLittleEndian(buf[0..], x); + BinaryPrimitives.WriteSingleLittleEndian(buf[4..], y); + BinaryPrimitives.WriteSingleLittleEndian(buf[8..], z); + BinaryPrimitives.WriteSingleLittleEndian(buf[12..], w); + FFI.physics_set_body_rotation(worldId, bodyId, buf); + } + + /// + /// Apply a force to a body (applied until next step). + /// + public static void AddForce(uint worldId, uint bodyId, float fx, float fy, float fz) + { + Span buf = stackalloc byte[12]; + BinaryPrimitives.WriteSingleLittleEndian(buf[0..], fx); + BinaryPrimitives.WriteSingleLittleEndian(buf[4..], fy); + BinaryPrimitives.WriteSingleLittleEndian(buf[8..], fz); + FFI.physics_add_force(worldId, bodyId, buf); + } + + /// + /// Apply an impulse to a body (instantaneous velocity change). + /// + public static void AddImpulse(uint worldId, uint bodyId, float ix, float iy, float iz) + { + Span buf = stackalloc byte[12]; + BinaryPrimitives.WriteSingleLittleEndian(buf[0..], ix); + BinaryPrimitives.WriteSingleLittleEndian(buf[4..], iy); + BinaryPrimitives.WriteSingleLittleEndian(buf[8..], iz); + FFI.physics_add_impulse(worldId, bodyId, buf); + } + + // ── Vehicle Physics ───────────────────────────────────────────── + + /// + /// Get default vehicle parameters from the Jolt engine. + /// Returns the raw bytes of JPC_VehicleParams with sensible sedan defaults. + /// + public static byte[] GetVehicleParamsDefault() + { + FFI.physics_get_vehicle_params_default(out var sourceBits); + var source = new BytesSource(sourceBits); + if (source == BytesSource.INVALID) + return Array.Empty(); + + uint remaining = 0; + FFI.bytes_source_remaining_length(source, ref remaining); + var buffer = new byte[remaining]; + uint readLen = remaining; + FFI.bytes_source_read(source, buffer.AsSpan(), ref readLen); + return buffer; + } + + /// + /// Create a 4-wheel vehicle in a physics world. + /// + public static uint CreateVehicle( + uint worldId, + byte[] vehicleParams, + (float x, float y, float z) position = default, + (float x, float y, float z, float w) rotation = default) + { + if (rotation == default) + rotation = (0f, 0f, 0f, 1f); + + Span pos = stackalloc byte[12]; + BinaryPrimitives.WriteSingleLittleEndian(pos[0..], position.x); + BinaryPrimitives.WriteSingleLittleEndian(pos[4..], position.y); + BinaryPrimitives.WriteSingleLittleEndian(pos[8..], position.z); + + Span rot = stackalloc byte[16]; + BinaryPrimitives.WriteSingleLittleEndian(rot[0..], rotation.x); + BinaryPrimitives.WriteSingleLittleEndian(rot[4..], rotation.y); + BinaryPrimitives.WriteSingleLittleEndian(rot[8..], rotation.z); + BinaryPrimitives.WriteSingleLittleEndian(rot[12..], rotation.w); + + FFI.physics_create_vehicle(worldId, vehicleParams, (uint)vehicleParams.Length, pos, rot, out var vehicleId); + return vehicleId; + } + + /// + /// Set driver input for a vehicle. + /// + public static void SetVehicleInput(uint worldId, uint vehicleId, + float forward, float right, float brake, float handbrake) + { + FFI.physics_set_vehicle_input(worldId, vehicleId, + BitConverter.SingleToUInt32Bits(forward), + BitConverter.SingleToUInt32Bits(right), + BitConverter.SingleToUInt32Bits(brake), + BitConverter.SingleToUInt32Bits(handbrake)); + } + + /// + /// Get vehicle state as raw bytes (JPC_VehicleState struct). + /// Returns null if vehicle/world not found. + /// + public static byte[]? GetVehicleStateRaw(uint worldId, uint vehicleId) + { + FFI.physics_get_vehicle_state(worldId, vehicleId, out var sourceBits); + var source = new BytesSource(sourceBits); + if (source == BytesSource.INVALID) + return null; + + uint remaining = 0; + FFI.bytes_source_remaining_length(source, ref remaining); + var buffer = new byte[remaining]; + uint readLen = remaining; + FFI.bytes_source_read(source, buffer.AsSpan(), ref readLen); + return buffer; + } + + /// + /// Destroy a vehicle from a physics world. + /// + public static void DestroyVehicle(uint worldId, uint vehicleId) + { + FFI.physics_destroy_vehicle(worldId, vehicleId); + } + + /// + /// Get transforms and velocities for ALL bodies in a world (bulk query). + /// Efficient for syncing physics state to database tables. + /// + public static PhysicsBodyState[] GetAllBodyTransforms(uint worldId) + { + FFI.physics_get_all_body_transforms(worldId, out var sourceBits); + var source = new BytesSource(sourceBits); + if (source == BytesSource.INVALID) + return Array.Empty(); + + // Read the bytes source + // Each record is 44 bytes: u32 + 3*f32 + 4*f32 + 3*f32 + const int RecordSize = 44; + var buffer = new byte[4096]; // initial buffer + uint totalRead = 0; + + // Read all bytes from the source + uint remaining = 0; + FFI.bytes_source_remaining_length(source, ref remaining); + if (remaining > 0) + buffer = new byte[remaining]; + + // Read in one call + uint readLen = (uint)buffer.Length; + FFI.bytes_source_read(source, buffer.AsSpan(), ref readLen); + totalRead = readLen; + + int count = (int)(totalRead / RecordSize); + var result = new PhysicsBodyState[count]; + + for (int i = 0; i < count; i++) + { + int offset = i * RecordSize; + result[i] = new PhysicsBodyState + { + BodyId = BinaryPrimitives.ReadUInt32LittleEndian(buffer.AsSpan(offset)), + PosX = BinaryPrimitives.ReadSingleLittleEndian(buffer.AsSpan(offset + 4)), + PosY = BinaryPrimitives.ReadSingleLittleEndian(buffer.AsSpan(offset + 8)), + PosZ = BinaryPrimitives.ReadSingleLittleEndian(buffer.AsSpan(offset + 12)), + RotX = BinaryPrimitives.ReadSingleLittleEndian(buffer.AsSpan(offset + 16)), + RotY = BinaryPrimitives.ReadSingleLittleEndian(buffer.AsSpan(offset + 20)), + RotZ = BinaryPrimitives.ReadSingleLittleEndian(buffer.AsSpan(offset + 24)), + RotW = BinaryPrimitives.ReadSingleLittleEndian(buffer.AsSpan(offset + 28)), + VelX = BinaryPrimitives.ReadSingleLittleEndian(buffer.AsSpan(offset + 32)), + VelY = BinaryPrimitives.ReadSingleLittleEndian(buffer.AsSpan(offset + 36)), + VelZ = BinaryPrimitives.ReadSingleLittleEndian(buffer.AsSpan(offset + 40)), + }; + } + + return result; + } + +} diff --git a/crates/bindings-csharp/Runtime/bindings.c b/crates/bindings-csharp/Runtime/bindings.c index 1b55d095713..c2f92f5154b 100644 --- a/crates/bindings-csharp/Runtime/bindings.c +++ b/crates/bindings-csharp/Runtime/bindings.c @@ -129,6 +129,65 @@ IMPORT(uint16_t, procedure_http_request, (request_ptr, request_len, body_ptr, body_len, out)); #undef SPACETIME_MODULE_VERSION +#define SPACETIME_MODULE_VERSION "spacetime_10.5" +IMPORT(Status, physics_create_world, + (const uint8_t* gravity_ptr, uint32_t* world_id), + (gravity_ptr, world_id)); +IMPORT(Status, physics_destroy_world, + (uint32_t world_id), + (world_id)); +IMPORT(Status, physics_create_body, + (uint32_t world_id, const uint8_t* body_params_ptr, uint32_t* body_id), + (world_id, body_params_ptr, body_id)); +IMPORT(Status, physics_destroy_body, + (uint32_t world_id, uint32_t body_id), + (world_id, body_id)); +IMPORT(Status, physics_step, + (uint32_t world_id, uint32_t delta_time_bits, uint32_t num_steps), + (world_id, delta_time_bits, num_steps)); +IMPORT(Status, physics_get_body_transform, + (uint32_t world_id, uint32_t body_id, uint8_t* out_ptr), + (world_id, body_id, out_ptr)); +IMPORT(Status, physics_get_body_velocity, + (uint32_t world_id, uint32_t body_id, uint8_t* out_ptr), + (world_id, body_id, out_ptr)); +IMPORT(Status, physics_set_body_position, + (uint32_t world_id, uint32_t body_id, const uint8_t* position_ptr), + (world_id, body_id, position_ptr)); +IMPORT(Status, physics_set_body_velocity, + (uint32_t world_id, uint32_t body_id, const uint8_t* velocity_ptr), + (world_id, body_id, velocity_ptr)); +IMPORT(Status, physics_set_body_rotation, + (uint32_t world_id, uint32_t body_id, const uint8_t* rotation_ptr), + (world_id, body_id, rotation_ptr)); +IMPORT(Status, physics_add_force, + (uint32_t world_id, uint32_t body_id, const uint8_t* force_ptr), + (world_id, body_id, force_ptr)); +IMPORT(Status, physics_add_impulse, + (uint32_t world_id, uint32_t body_id, const uint8_t* impulse_ptr), + (world_id, body_id, impulse_ptr)); +IMPORT(Status, physics_get_all_body_transforms, + (uint32_t world_id, uint32_t* out), + (world_id, out)); + +// Vehicle physics +IMPORT(Status, physics_create_vehicle, + (uint32_t world_id, const uint8_t* params_ptr, uint32_t params_len, const uint8_t* pos_ptr, const uint8_t* rot_ptr, uint32_t* out), + (world_id, params_ptr, params_len, pos_ptr, rot_ptr, out)); +IMPORT(Status, physics_set_vehicle_input, + (uint32_t world_id, uint32_t vehicle_id, uint32_t forward_bits, uint32_t right_bits, uint32_t brake_bits, uint32_t handbrake_bits), + (world_id, vehicle_id, forward_bits, right_bits, brake_bits, handbrake_bits)); +IMPORT(Status, physics_get_vehicle_state, + (uint32_t world_id, uint32_t vehicle_id, uint32_t* out), + (world_id, vehicle_id, out)); +IMPORT(Status, physics_destroy_vehicle, + (uint32_t world_id, uint32_t vehicle_id), + (world_id, vehicle_id)); +IMPORT(Status, physics_get_vehicle_params_default, + (uint32_t* out), + (out)); +#undef SPACETIME_MODULE_VERSION + #ifndef EXPERIMENTAL_WASM_AOT static MonoClass* ffi_class; diff --git a/crates/bindings-sys/src/lib.rs b/crates/bindings-sys/src/lib.rs index 95dfbc7e600..d832d87ca4a 100644 --- a/crates/bindings-sys/src/lib.rs +++ b/crates/bindings-sys/src/lib.rs @@ -865,6 +865,61 @@ pub mod raw { ) -> u16; } + #[link(wasm_import_module = "spacetime_10.5")] + unsafe extern "C" { + /// Create a new physics world with the given gravity. + /// `gravity_ptr` points to 3 f32s [x, y, z] in WASM memory. + /// The new world ID is written to `out`. + pub fn physics_create_world(gravity_ptr: *const u8, out: *mut u32) -> u16; + + /// Destroy a physics world. + pub fn physics_destroy_world(world_id: u32) -> u16; + + /// Create a body in a physics world. + /// `params_ptr` points to a packed struct (54 bytes): + /// shape_type: u8, shape_params: [f32;3], position: [f32;3], + /// rotation: [f32;4], motion_type: u8, mass: f32, restitution: f32, friction: f32 + /// Body ID is written to `out`. + pub fn physics_create_body(world_id: u32, params_ptr: *const u8, out: *mut u32) -> u16; + + /// Destroy a body from a physics world. + pub fn physics_destroy_body(world_id: u32, body_id: u32) -> u16; + + /// Step the physics simulation. + /// `delta_time_bits` is the f32 delta time as u32 bits. + /// `num_steps` is the number of sub-steps. + pub fn physics_step(world_id: u32, delta_time_bits: u32, num_steps: u32) -> u16; + + /// Get a body's transform (position + rotation). + /// Writes 7 f32s to `out_ptr`: [px, py, pz, rx, ry, rz, rw] + pub fn physics_get_body_transform(world_id: u32, body_id: u32, out_ptr: *mut u8) -> u16; + + /// Get a body's linear velocity. + /// Writes 3 f32s to `out_ptr`: [vx, vy, vz] + pub fn physics_get_body_velocity(world_id: u32, body_id: u32, out_ptr: *mut u8) -> u16; + + /// Set a body's position. Reads 3 f32s from `pos_ptr`. + pub fn physics_set_body_position(world_id: u32, body_id: u32, pos_ptr: *const u8) -> u16; + + /// Set a body's linear velocity. Reads 3 f32s from `vel_ptr`. + pub fn physics_set_body_velocity(world_id: u32, body_id: u32, vel_ptr: *const u8) -> u16; + + /// Set a body's rotation. Reads 4 f32s [x,y,z,w] from `rot_ptr`. + pub fn physics_set_body_rotation(world_id: u32, body_id: u32, rot_ptr: *const u8) -> u16; + + /// Apply a force to a body. Reads 3 f32s from `force_ptr`. + pub fn physics_add_force(world_id: u32, body_id: u32, force_ptr: *const u8) -> u16; + + /// Apply an impulse to a body. Reads 3 f32s from `impulse_ptr`. + pub fn physics_add_impulse(world_id: u32, body_id: u32, impulse_ptr: *const u8) -> u16; + + /// Get transforms for all bodies in a world (bulk). + /// Returns a BytesSource containing packed records (44 bytes each): + /// [body_id: u32, pos: [f32;3], rot: [f32;4], vel: [f32;3]] + /// The BytesSourceId is written to `out`. + pub fn physics_get_all_body_transforms(world_id: u32, out: *mut u32) -> u16; + } + /// What strategy does the database index use? /// /// See also: @@ -1626,3 +1681,126 @@ pub mod procedure { } } } + +// ==================== Physics (Jolt) safe wrappers ==================== + +/// Physics world ID handle. +pub type PhysicsWorldId = u32; + +/// Physics body ID handle. +pub type PhysicsBodyId = u32; + +/// Motion type for physics bodies. +#[repr(u8)] +pub enum PhysicsMotionType { + Static = 0, + Kinematic = 1, + Dynamic = 2, +} + +/// Shape type for physics collision shapes. +#[repr(u8)] +pub enum PhysicsShapeType { + Sphere = 0, + Box = 1, + Capsule = 2, +} + +/// Parameters for creating a physics body. +#[repr(C, packed)] +pub struct PhysicsBodyParams { + pub shape_type: u8, + pub shape_params: [f32; 3], + pub position: [f32; 3], + pub rotation: [f32; 4], + pub motion_type: u8, + pub mass: f32, + pub restitution: f32, + pub friction: f32, +} + +/// Create a new physics world with the given gravity. +pub fn physics_create_world(gravity: [f32; 3]) -> Result { + unsafe { + call(|out| { + raw::physics_create_world(gravity.as_ptr() as *const u8, out) + }) + } +} + +/// Destroy a physics world. +pub fn physics_destroy_world(world_id: PhysicsWorldId) -> Result<()> { + cvt(unsafe { raw::physics_destroy_world(world_id) }) +} + +/// Create a body in a physics world. +pub fn physics_create_body(world_id: PhysicsWorldId, params: &PhysicsBodyParams) -> Result { + unsafe { + call(|out| { + raw::physics_create_body(world_id, params as *const PhysicsBodyParams as *const u8, out) + }) + } +} + +/// Destroy a body from a physics world. +pub fn physics_destroy_body(world_id: PhysicsWorldId, body_id: PhysicsBodyId) -> Result<()> { + cvt(unsafe { raw::physics_destroy_body(world_id, body_id) }) +} + +/// Step the physics simulation. +pub fn physics_step(world_id: PhysicsWorldId, delta_time: f32, num_steps: u32) -> Result<()> { + cvt(unsafe { raw::physics_step(world_id, delta_time.to_bits(), num_steps) }) +} + +/// Get a body's position and rotation. +/// Returns ([px, py, pz], [rx, ry, rz, rw]). +pub fn physics_get_body_transform(world_id: PhysicsWorldId, body_id: PhysicsBodyId) -> Result<([f32; 3], [f32; 4])> { + let mut buf = [0u8; 28]; // 7 * f32 + cvt(unsafe { raw::physics_get_body_transform(world_id, body_id, buf.as_mut_ptr()) })?; + + let read_f32 = |offset: usize| -> f32 { + f32::from_le_bytes(buf[offset..offset + 4].try_into().unwrap()) + }; + + Ok(( + [read_f32(0), read_f32(4), read_f32(8)], + [read_f32(12), read_f32(16), read_f32(20), read_f32(24)], + )) +} + +/// Get a body's linear velocity. +pub fn physics_get_body_velocity(world_id: PhysicsWorldId, body_id: PhysicsBodyId) -> Result<[f32; 3]> { + let mut buf = [0u8; 12]; // 3 * f32 + cvt(unsafe { raw::physics_get_body_velocity(world_id, body_id, buf.as_mut_ptr()) })?; + + Ok([ + f32::from_le_bytes(buf[0..4].try_into().unwrap()), + f32::from_le_bytes(buf[4..8].try_into().unwrap()), + f32::from_le_bytes(buf[8..12].try_into().unwrap()), + ]) +} + +/// Set a body's position. +pub fn physics_set_body_position(world_id: PhysicsWorldId, body_id: PhysicsBodyId, position: [f32; 3]) -> Result<()> { + cvt(unsafe { raw::physics_set_body_position(world_id, body_id, position.as_ptr() as *const u8) }) +} + +/// Set a body's linear velocity. +pub fn physics_set_body_velocity(world_id: PhysicsWorldId, body_id: PhysicsBodyId, velocity: [f32; 3]) -> Result<()> { + cvt(unsafe { raw::physics_set_body_velocity(world_id, body_id, velocity.as_ptr() as *const u8) }) +} + +/// Set a body's rotation (quaternion [x, y, z, w]). +pub fn physics_set_body_rotation(world_id: PhysicsWorldId, body_id: PhysicsBodyId, rotation: [f32; 4]) -> Result<()> { + cvt(unsafe { raw::physics_set_body_rotation(world_id, body_id, rotation.as_ptr() as *const u8) }) +} + +/// Apply a force to a body. +pub fn physics_add_force(world_id: PhysicsWorldId, body_id: PhysicsBodyId, force: [f32; 3]) -> Result<()> { + cvt(unsafe { raw::physics_add_force(world_id, body_id, force.as_ptr() as *const u8) }) +} + +/// Apply an impulse to a body. +pub fn physics_add_impulse(world_id: PhysicsWorldId, body_id: PhysicsBodyId, impulse: [f32; 3]) -> Result<()> { + cvt(unsafe { raw::physics_add_impulse(world_id, body_id, impulse.as_ptr() as *const u8) }) +} diff --git a/crates/core/Cargo.toml b/crates/core/Cargo.toml index e0934d245b8..0283304a473 100644 --- a/crates/core/Cargo.toml +++ b/crates/core/Cargo.toml @@ -127,6 +127,11 @@ async_cache = "0.3.1" faststr = "0.2.23" core_affinity = "0.8" +# Physics +rolt.workspace = true +joltc-sys.workspace = true +glam.workspace = true + [target.'cfg(not(target_env = "msvc"))'.dependencies] tikv-jemallocator = {workspace = true} tikv-jemalloc-ctl = {workspace = true} diff --git a/crates/core/src/host/mod.rs b/crates/core/src/host/mod.rs index 0daa9c359bc..65611b79ff2 100644 --- a/crates/core/src/host/mod.rs +++ b/crates/core/src/host/mod.rs @@ -21,6 +21,7 @@ pub mod wasmtime; // Visible for integration testing. pub mod instance_env; pub mod v8; // only pub for testing +pub mod physics; mod wasm_common; pub use disk_storage::DiskStorage; @@ -194,4 +195,26 @@ pub enum AbiCall { ProcedureCommitMutTransaction, ProcedureAbortMutTransaction, ProcedureHttpRequest, + + // Physics (Jolt) + PhysicsCreateWorld, + PhysicsDestroyWorld, + PhysicsCreateBody, + PhysicsDestroyBody, + PhysicsStep, + PhysicsGetBodyTransform, + PhysicsGetBodyVelocity, + PhysicsSetBodyPosition, + PhysicsSetBodyVelocity, + PhysicsSetBodyRotation, + PhysicsAddForce, + PhysicsAddImpulse, + PhysicsGetAllBodyTransforms, + + // Physics (Jolt) - Vehicles + PhysicsCreateVehicle, + PhysicsSetVehicleInput, + PhysicsGetVehicleState, + PhysicsDestroyVehicle, + PhysicsGetVehicleParamsDefault, } diff --git a/crates/core/src/host/physics.rs b/crates/core/src/host/physics.rs new file mode 100644 index 00000000000..e351a45a044 --- /dev/null +++ b/crates/core/src/host/physics.rs @@ -0,0 +1,673 @@ +//! Jolt Physics integration for SpacetimeDB. +//! +//! Provides a host-side physics world that WASM modules can interact with +//! via host functions. The physics state lives in host memory (not WASM linear memory), +//! giving native performance while being callable from any module language. +//! +//! # Persistence model +//! +//! The Jolt physics world is **not persisted** — it lives only in RAM. +//! Modules must store authoritative state (positions, velocities, shapes) +//! in SpacetimeDB tables and reconstruct the physics world on restart. +//! Between reducer calls, the world stays alive (WASM instances are pooled). + +use joltc_sys::*; +use parking_lot::Mutex; +use rolt::{ + register_default_allocator, factory_init, register_types, + PhysicsSystem, BodyId, ObjectLayer, BroadPhaseLayer, + BroadPhaseLayerInterface, ObjectVsBroadPhaseLayerFilter, ObjectLayerPairFilter, +}; +use std::collections::HashMap; + +/// Identifies a physics world instance. +pub type PhysicsWorldId = u32; + +/// Identifies a body within a physics world. +pub type PhysicsBodyId = u32; + +/// Identifies a vehicle within a physics world. +pub type PhysicsVehicleId = u32; + +/// Motion type for physics bodies. +#[repr(u8)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum MotionType { + Static = 0, + Kinematic = 1, + Dynamic = 2, +} + +impl MotionType { + pub fn from_u8(v: u8) -> Option { + match v { + 0 => Some(Self::Static), + 1 => Some(Self::Kinematic), + 2 => Some(Self::Dynamic), + _ => None, + } + } + + fn to_jolt(self) -> JPC_MotionType { + match self { + Self::Static => JPC_MOTION_TYPE_STATIC, + Self::Kinematic => JPC_MOTION_TYPE_KINEMATIC, + Self::Dynamic => JPC_MOTION_TYPE_DYNAMIC, + } + } +} + +/// Shape type for creating collision shapes. +#[repr(u8)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ShapeType { + Sphere = 0, + Box = 1, + Capsule = 2, +} + +impl ShapeType { + pub fn from_u8(v: u8) -> Option { + match v { + 0 => Some(Self::Sphere), + 1 => Some(Self::Box), + 2 => Some(Self::Capsule), + _ => None, + } + } +} + +// Simple 2-layer broadphase setup: MOVING and NON_MOVING +const LAYER_NON_MOVING: ObjectLayer = ObjectLayer::new(0); +const LAYER_MOVING: ObjectLayer = ObjectLayer::new(1); +const BP_LAYER_NON_MOVING: BroadPhaseLayer = BroadPhaseLayer::new(0); +const BP_LAYER_MOVING: BroadPhaseLayer = BroadPhaseLayer::new(1); +const NUM_BP_LAYERS: u32 = 2; + +struct SimpleBroadPhaseLayerInterface; +impl BroadPhaseLayerInterface for SimpleBroadPhaseLayerInterface { + fn get_num_broad_phase_layers(&self) -> u32 { + NUM_BP_LAYERS + } + fn get_broad_phase_layer(&self, layer: ObjectLayer) -> BroadPhaseLayer { + if layer == LAYER_NON_MOVING { + BP_LAYER_NON_MOVING + } else { + BP_LAYER_MOVING + } + } +} + +struct SimpleObjectVsBroadPhaseLayerFilter; +impl ObjectVsBroadPhaseLayerFilter for SimpleObjectVsBroadPhaseLayerFilter { + fn should_collide(&self, layer1: ObjectLayer, layer2: BroadPhaseLayer) -> bool { + if layer1 == LAYER_NON_MOVING { + layer2 == BP_LAYER_MOVING + } else { + true + } + } +} + +struct SimpleObjectLayerPairFilter; +impl ObjectLayerPairFilter for SimpleObjectLayerPairFilter { + fn should_collide(&self, layer1: ObjectLayer, layer2: ObjectLayer) -> bool { + // Non-moving objects don't collide with each other + !(layer1 == LAYER_NON_MOVING && layer2 == LAYER_NON_MOVING) + } +} + +/// Helper to create a JPC_Vec3 (which has an unused _w padding field). +fn jpc_vec3(x: f32, y: f32, z: f32) -> JPC_Vec3 { + JPC_Vec3 { x, y, z, _w: 0.0 } +} + +/// Manages all physics worlds for a module instance. +pub struct PhysicsState { + worlds: Mutex>, + next_world_id: Mutex, +} + +/// A single Jolt physics world with its system and resources. +struct PhysicsWorld { + physics_system: PhysicsSystem, + temp_allocator: *mut JPC_TempAllocatorImpl, + job_system: *mut JPC_JobSystemThreadPool, + body_map: HashMap, + next_body_id: PhysicsBodyId, +} + +impl Drop for PhysicsWorld { + fn drop(&mut self) { + unsafe { + if !self.temp_allocator.is_null() { + JPC_TempAllocatorImpl_delete(self.temp_allocator); + } + if !self.job_system.is_null() { + JPC_JobSystemThreadPool_delete(self.job_system); + } + } + } +} + +// Safety: The PhysicsWorld wraps Jolt resources which are thread-safe +// when accessed through the BodyInterface (which Jolt locks internally). +unsafe impl Send for PhysicsWorld {} + +impl PhysicsState { + pub fn new() -> Self { + register_default_allocator(); + factory_init(); + register_types(); + + Self { + worlds: Mutex::new(HashMap::new()), + next_world_id: Mutex::new(1), + } + } + + /// Create a new physics world. Returns the world ID. + /// Note: Custom gravity is not supported by the current joltc_sys bindings. + /// Jolt defaults to (0, -9.81, 0). + pub fn create_world(&self, _gravity: [f32; 3]) -> PhysicsWorldId { + let mut next_id = self.next_world_id.lock(); + let world_id = *next_id; + *next_id += 1; + + let mut physics_system = PhysicsSystem::new(); + + // Initialize with broadphase layer interfaces + physics_system.init( + 1024, // max_bodies + 0, // num_body_mutexes (0 = auto) + 1024, // max_body_pairs + 1024, // max_contact_constraints + SimpleBroadPhaseLayerInterface, + SimpleObjectVsBroadPhaseLayerFilter, + SimpleObjectLayerPairFilter, + ); + + // Create temp allocator and job system for stepping + // JPC_JobSystemThreadPool_new2(maxJobs, maxBarriers) + let temp_allocator = unsafe { JPC_TempAllocatorImpl_new(10 * 1024 * 1024) }; // 10MB + let job_system = unsafe { JPC_JobSystemThreadPool_new2(1024, 8) }; + + let world = PhysicsWorld { + physics_system, + temp_allocator, + job_system, + body_map: HashMap::new(), + next_body_id: 1, + }; + + self.worlds.lock().insert(world_id, world); + world_id + } + + /// Destroy a physics world. + pub fn destroy_world(&self, world_id: PhysicsWorldId) -> bool { + self.worlds.lock().remove(&world_id).is_some() + } + + /// Create a body in the specified world. Returns the body ID. + pub fn create_body( + &self, + world_id: PhysicsWorldId, + shape_type: u8, + shape_params: [f32; 3], + position: [f32; 3], + rotation: [f32; 4], + motion_type: u8, + _mass: f32, + restitution: f32, + friction: f32, + ) -> Option { + let mut worlds = self.worlds.lock(); + let world = worlds.get_mut(&world_id)?; + + let shape_type = ShapeType::from_u8(shape_type)?; + let motion = MotionType::from_u8(motion_type)?; + + let our_body_id = world.next_body_id; + world.next_body_id += 1; + + unsafe { + // Create shape - _default() functions are in-place initializers + let mut out_shape: *mut JPC_Shape = std::ptr::null_mut(); + let success = match shape_type { + ShapeType::Sphere => { + let mut settings: JPC_SphereShapeSettings = std::mem::zeroed(); + JPC_SphereShapeSettings_default(&mut settings); + settings.Radius = shape_params[0]; + JPC_SphereShapeSettings_Create(&settings, &mut out_shape, std::ptr::null_mut()) + } + ShapeType::Box => { + let mut settings: JPC_BoxShapeSettings = std::mem::zeroed(); + JPC_BoxShapeSettings_default(&mut settings); + settings.HalfExtent = jpc_vec3(shape_params[0], shape_params[1], shape_params[2]); + JPC_BoxShapeSettings_Create(&settings, &mut out_shape, std::ptr::null_mut()) + } + ShapeType::Capsule => { + let mut settings: JPC_CapsuleShapeSettings = std::mem::zeroed(); + JPC_CapsuleShapeSettings_default(&mut settings); + settings.Radius = shape_params[0]; + settings.HalfHeightOfCylinder = shape_params[1]; + JPC_CapsuleShapeSettings_Create(&settings, &mut out_shape, std::ptr::null_mut()) + } + }; + + if !success || out_shape.is_null() { + return None; + } + + // Choose object layer based on motion type + let object_layer = if motion == MotionType::Static { + LAYER_NON_MOVING + } else { + LAYER_MOVING + }; + + let activation = if motion == MotionType::Static { + JPC_ACTIVATION_DONT_ACTIVATE + } else { + JPC_ACTIVATION_ACTIVATE + }; + + // Create body creation settings with proper defaults + let mut body_settings: JPC_BodyCreationSettings = std::mem::zeroed(); + JPC_BodyCreationSettings_default(&mut body_settings); + body_settings.Position = jpc_vec3(position[0], position[1], position[2]); + body_settings.Rotation = JPC_Quat { + x: rotation[0], + y: rotation[1], + z: rotation[2], + w: rotation[3], + }; + body_settings.ObjectLayer = object_layer.raw(); + body_settings.MotionType = motion.to_jolt(); + body_settings.Shape = out_shape; + body_settings.Friction = friction; + body_settings.Restitution = restitution; + + // Create body via body interface + let bi = world.physics_system.body_interface(); + let body = bi.create_body(&body_settings); + let jolt_body_id = body.id(); + + // Add to world + bi.add_body(jolt_body_id, activation); + + world.body_map.insert(our_body_id, jolt_body_id); + Some(our_body_id) + } + } + + /// Remove a body from the world. + pub fn destroy_body(&self, world_id: PhysicsWorldId, body_id: PhysicsBodyId) -> bool { + let mut worlds = self.worlds.lock(); + let world = match worlds.get_mut(&world_id) { + Some(w) => w, + None => return false, + }; + + if let Some(jolt_id) = world.body_map.remove(&body_id) { + let bi = world.physics_system.body_interface(); + bi.remove_body(jolt_id); + bi.destroy_body(jolt_id); + true + } else { + false + } + } + + /// Step the physics simulation. + pub fn step(&self, world_id: PhysicsWorldId, delta_time: f32, num_steps: i32) -> bool { + let mut worlds = self.worlds.lock(); + let world = match worlds.get_mut(&world_id) { + Some(w) => w, + None => return false, + }; + + unsafe { + world.physics_system.update( + delta_time, + num_steps.max(1), + world.temp_allocator, + world.job_system, + ); + } + true + } + + /// Get body position and rotation. + pub fn get_body_transform( + &self, + world_id: PhysicsWorldId, + body_id: PhysicsBodyId, + ) -> Option<([f32; 3], [f32; 4])> { + let worlds = self.worlds.lock(); + let world = worlds.get(&world_id)?; + let jolt_id = world.body_map.get(&body_id)?; + + let bi = world.physics_system.body_interface(); + let pos = bi.center_of_mass_position(*jolt_id); + // Get rotation via raw API + let rot = unsafe { + JPC_BodyInterface_GetRotation(bi.as_raw(), jolt_id.raw()) + }; + + Some(( + [pos.x as f32, pos.y as f32, pos.z as f32], + [rot.x, rot.y, rot.z, rot.w], + )) + } + + /// Get body linear velocity. + pub fn get_body_velocity( + &self, + world_id: PhysicsWorldId, + body_id: PhysicsBodyId, + ) -> Option<[f32; 3]> { + let worlds = self.worlds.lock(); + let world = worlds.get(&world_id)?; + let jolt_id = world.body_map.get(&body_id)?; + + let bi = world.physics_system.body_interface(); + let vel = bi.linear_velocity(*jolt_id); + Some([vel.x, vel.y, vel.z]) + } + + /// Set body position. + pub fn set_body_position( + &self, + world_id: PhysicsWorldId, + body_id: PhysicsBodyId, + position: [f32; 3], + ) -> bool { + let mut worlds = self.worlds.lock(); + let world = match worlds.get_mut(&world_id) { + Some(w) => w, + None => return false, + }; + + if let Some(jolt_id) = world.body_map.get(&body_id) { + unsafe { + let bi = world.physics_system.body_interface(); + let pos = jpc_vec3(position[0], position[1], position[2]); + JPC_BodyInterface_SetPosition( + bi.as_raw(), + jolt_id.raw(), + pos, + JPC_ACTIVATION_ACTIVATE, + ); + } + true + } else { + false + } + } + + /// Set body linear velocity. + pub fn set_body_velocity( + &self, + world_id: PhysicsWorldId, + body_id: PhysicsBodyId, + velocity: [f32; 3], + ) -> bool { + let mut worlds = self.worlds.lock(); + let world = match worlds.get_mut(&world_id) { + Some(w) => w, + None => return false, + }; + + if let Some(jolt_id) = world.body_map.get(&body_id) { + unsafe { + let bi = world.physics_system.body_interface(); + let vel = jpc_vec3(velocity[0], velocity[1], velocity[2]); + JPC_BodyInterface_SetLinearVelocity(bi.as_raw(), jolt_id.raw(), vel); + } + true + } else { + false + } + } + + /// Set body rotation. + pub fn set_body_rotation( + &self, + world_id: PhysicsWorldId, + body_id: PhysicsBodyId, + rotation: [f32; 4], + ) -> bool { + let mut worlds = self.worlds.lock(); + let world = match worlds.get_mut(&world_id) { + Some(w) => w, + None => return false, + }; + + if let Some(jolt_id) = world.body_map.get(&body_id) { + unsafe { + let bi = world.physics_system.body_interface(); + let rot = JPC_Quat { + x: rotation[0], + y: rotation[1], + z: rotation[2], + w: rotation[3], + }; + JPC_BodyInterface_SetRotation( + bi.as_raw(), + jolt_id.raw(), + rot, + JPC_ACTIVATION_ACTIVATE, + ); + } + true + } else { + false + } + } + + /// Apply a force to a body. + pub fn add_force( + &self, + world_id: PhysicsWorldId, + body_id: PhysicsBodyId, + force: [f32; 3], + ) -> bool { + let mut worlds = self.worlds.lock(); + let world = match worlds.get_mut(&world_id) { + Some(w) => w, + None => return false, + }; + + if let Some(jolt_id) = world.body_map.get(&body_id) { + unsafe { + let bi = world.physics_system.body_interface(); + let f = jpc_vec3(force[0], force[1], force[2]); + JPC_BodyInterface_AddForce(bi.as_raw(), jolt_id.raw(), f); + } + true + } else { + false + } + } + + /// Apply an impulse to a body. + pub fn add_impulse( + &self, + world_id: PhysicsWorldId, + body_id: PhysicsBodyId, + impulse: [f32; 3], + ) -> bool { + let mut worlds = self.worlds.lock(); + let world = match worlds.get_mut(&world_id) { + Some(w) => w, + None => return false, + }; + + if let Some(jolt_id) = world.body_map.get(&body_id) { + unsafe { + let bi = world.physics_system.body_interface(); + let imp = jpc_vec3(impulse[0], impulse[1], impulse[2]); + JPC_BodyInterface_AddImpulse(bi.as_raw(), jolt_id.raw(), imp); + } + true + } else { + false + } + } + + /// Get transforms for ALL bodies in bulk. + pub fn get_all_body_transforms( + &self, + world_id: PhysicsWorldId, + ) -> Option> { + let worlds = self.worlds.lock(); + let world = worlds.get(&world_id)?; + + let bi = world.physics_system.body_interface(); + let result: Vec<_> = world + .body_map + .iter() + .map(|(&our_id, &jolt_id)| { + let pos = bi.center_of_mass_position(jolt_id); + let rot = unsafe { JPC_BodyInterface_GetRotation(bi.as_raw(), jolt_id.raw()) }; + let vel = bi.linear_velocity(jolt_id); + ( + our_id, + [pos.x as f32, pos.y as f32, pos.z as f32], + [rot.x, rot.y, rot.z, rot.w], + [vel.x, vel.y, vel.z], + ) + }) + .collect(); + + Some(result) + } + + // ──────────────────── Vehicle API ──────────────────── + + /// Create a 4-wheel vehicle in the specified world. + /// `params` is a flat byte slice of JPC_VehicleParams (will be reinterpreted). + pub fn create_vehicle( + &self, + world_id: PhysicsWorldId, + params: &[u8], + position: [f32; 3], + rotation: [f32; 4], + ) -> Option { + let worlds = self.worlds.lock(); + let world = worlds.get(&world_id)?; + + // Safety: params must be exactly sizeof(JPC_VehicleParams) + if params.len() < std::mem::size_of::() { + return None; + } + + unsafe { + let vparams = params.as_ptr() as *const JPC_VehicleParams; + let vid = JPC_PhysicsSystem_CreateVehicle( + world.physics_system.as_raw(), + vparams, + position[0], position[1], position[2], + rotation[0], rotation[1], rotation[2], rotation[3], + ); + if vid == 0 { None } else { Some(vid) } + } + } + + /// Set driver input for a vehicle. + pub fn set_vehicle_input( + &self, + world_id: PhysicsWorldId, + vehicle_id: PhysicsVehicleId, + forward: f32, + right: f32, + brake: f32, + handbrake: f32, + ) -> bool { + let worlds = self.worlds.lock(); + let world = match worlds.get(&world_id) { + Some(w) => w, + None => return false, + }; + + unsafe { + JPC_PhysicsSystem_SetVehicleInput( + world.physics_system.as_raw(), + vehicle_id, + forward, right, brake, handbrake, + ); + } + true + } + + /// Get vehicle state (body transform, wheels, engine, gear). + /// Returns the raw JPC_VehicleState bytes for serialization to WASM. + pub fn get_vehicle_state( + &self, + world_id: PhysicsWorldId, + vehicle_id: PhysicsVehicleId, + ) -> Option> { + let worlds = self.worlds.lock(); + let world = worlds.get(&world_id)?; + + unsafe { + let mut state: JPC_VehicleState = std::mem::zeroed(); + let ok = JPC_PhysicsSystem_GetVehicleState( + world.physics_system.as_raw(), + vehicle_id, + &mut state, + ); + if !ok { + return None; + } + + // Serialize as raw bytes + let bytes = std::slice::from_raw_parts( + &state as *const JPC_VehicleState as *const u8, + std::mem::size_of::(), + ); + Some(bytes.to_vec()) + } + } + + /// Destroy a vehicle and remove it from the physics world. + pub fn destroy_vehicle( + &self, + world_id: PhysicsWorldId, + vehicle_id: PhysicsVehicleId, + ) -> bool { + let worlds = self.worlds.lock(); + let world = match worlds.get(&world_id) { + Some(w) => w, + None => return false, + }; + + unsafe { + JPC_PhysicsSystem_DestroyVehicle( + world.physics_system.as_raw(), + vehicle_id, + ); + } + true + } +} + +impl Default for PhysicsState { + fn default() -> Self { + Self::new() + } +} + +unsafe impl Send for PhysicsState {} +unsafe impl Sync for PhysicsState {} + +/// Global physics state, shared across all module instances. +static PHYSICS: once_cell::sync::Lazy = + once_cell::sync::Lazy::new(PhysicsState::new); + +/// Get the global physics state. +pub fn physics() -> &'static PhysicsState { + &PHYSICS +} diff --git a/crates/core/src/host/wasm_common.rs b/crates/core/src/host/wasm_common.rs index a5c737d54d6..c01ebb1a225 100644 --- a/crates/core/src/host/wasm_common.rs +++ b/crates/core/src/host/wasm_common.rs @@ -431,6 +431,28 @@ macro_rules! abi_funcs { "spacetime_10.4"::datastore_index_scan_point_bsatn, "spacetime_10.4"::datastore_delete_by_index_scan_point_bsatn, + // Physics (Jolt) host functions + "spacetime_10.5"::physics_create_world, + "spacetime_10.5"::physics_destroy_world, + "spacetime_10.5"::physics_create_body, + "spacetime_10.5"::physics_destroy_body, + "spacetime_10.5"::physics_step, + "spacetime_10.5"::physics_get_body_transform, + "spacetime_10.5"::physics_get_body_velocity, + "spacetime_10.5"::physics_set_body_position, + "spacetime_10.5"::physics_set_body_velocity, + "spacetime_10.5"::physics_set_body_rotation, + "spacetime_10.5"::physics_add_force, + "spacetime_10.5"::physics_add_impulse, + "spacetime_10.5"::physics_get_all_body_transforms, + + // Vehicle physics host functions + "spacetime_10.5"::physics_create_vehicle, + "spacetime_10.5"::physics_set_vehicle_input, + "spacetime_10.5"::physics_get_vehicle_state, + "spacetime_10.5"::physics_destroy_vehicle, + "spacetime_10.5"::physics_get_vehicle_params_default, + } $link_async! { diff --git a/crates/core/src/host/wasmtime/wasm_instance_env.rs b/crates/core/src/host/wasmtime/wasm_instance_env.rs index 74a57b35e92..ac69ea6d0d0 100644 --- a/crates/core/src/host/wasmtime/wasm_instance_env.rs +++ b/crates/core/src/host/wasmtime/wasm_instance_env.rs @@ -1943,6 +1943,451 @@ impl WasmInstanceEnv { ) }) } + + // ==================== Physics (Jolt) host functions ==================== + + /// Create a new physics world with the given gravity vector. + /// + /// Writes the new world ID to `out`. + /// Gravity is read as 3 consecutive f32s from WASM memory at `gravity_ptr`. + pub fn physics_create_world( + caller: Caller<'_, Self>, + gravity_ptr: WasmPtr, + out: WasmPtr, + ) -> RtResult { + Self::cvt_ret::(caller, AbiCall::PhysicsCreateWorld, out, |caller| { + let (mem, _env) = Self::mem_env(caller); + let gravity_bytes = mem.deref_slice(gravity_ptr, 12)?; // 3 * f32 + let gx = f32::from_le_bytes(gravity_bytes[0..4].try_into().unwrap()); + let gy = f32::from_le_bytes(gravity_bytes[4..8].try_into().unwrap()); + let gz = f32::from_le_bytes(gravity_bytes[8..12].try_into().unwrap()); + + let world_id = crate::host::physics::physics().create_world([gx, gy, gz]); + Ok(world_id) + }) + } + + /// Destroy a physics world. + /// + /// Returns errno 0 on success. + pub fn physics_destroy_world( + caller: Caller<'_, Self>, + world_id: u32, + ) -> RtResult { + Self::cvt::(caller, AbiCall::PhysicsDestroyWorld, |_caller| { + crate::host::physics::physics().destroy_world(world_id); + Ok(()) + }) + } + + /// Create a body in the specified world. + /// + /// Reads body creation params from WASM memory: + /// - `params_ptr`: packed struct of: + /// - shape_type: u8 + /// - shape_params: [f32; 3] (12 bytes) + /// - position: [f32; 3] (12 bytes) + /// - rotation: [f32; 4] (16 bytes) quaternion xyzw + /// - motion_type: u8 + /// - mass: f32 + /// - restitution: f32 + /// - friction: f32 + /// Total: 1 + 12 + 12 + 16 + 1 + 4 + 4 + 4 = 54 bytes + /// + /// Writes body ID to `out`. + pub fn physics_create_body( + caller: Caller<'_, Self>, + world_id: u32, + params_ptr: WasmPtr, + out: WasmPtr, + ) -> RtResult { + Self::cvt_ret::(caller, AbiCall::PhysicsCreateBody, out, |caller| { + let (mem, _env) = Self::mem_env(caller); + let data = mem.deref_slice(params_ptr, 54)?; + + let shape_type = data[0]; + + let read_f32 = |offset: usize| -> f32 { + f32::from_le_bytes(data[offset..offset + 4].try_into().unwrap()) + }; + + let shape_params = [read_f32(1), read_f32(5), read_f32(9)]; + let position = [read_f32(13), read_f32(17), read_f32(21)]; + let rotation = [read_f32(25), read_f32(29), read_f32(33), read_f32(37)]; + let motion_type = data[41]; + let mass = read_f32(42); + let restitution = read_f32(46); + let friction = read_f32(50); + + let body_id = crate::host::physics::physics() + .create_body( + world_id, + shape_type, + shape_params, + position, + rotation, + motion_type, + mass, + restitution, + friction, + ) + .ok_or_else(|| WasmError::Wasm(anyhow!("Failed to create physics body")))?; + + Ok(body_id) + }) + } + + /// Destroy a body from a physics world. + pub fn physics_destroy_body( + caller: Caller<'_, Self>, + world_id: u32, + body_id: u32, + ) -> RtResult { + Self::cvt::(caller, AbiCall::PhysicsDestroyBody, |_caller| { + crate::host::physics::physics().destroy_body(world_id, body_id); + Ok(()) + }) + } + + /// Step the physics simulation. + /// + /// `delta_time_bits` is the IEEE 754 bit representation of the f32 delta time. + /// `num_steps` is how many sub-steps to run (typically 1). + pub fn physics_step( + caller: Caller<'_, Self>, + world_id: u32, + delta_time_bits: u32, + num_steps: u32, + ) -> RtResult { + Self::cvt::(caller, AbiCall::PhysicsStep, |_caller| { + let dt = f32::from_bits(delta_time_bits); + crate::host::physics::physics().step(world_id, dt, num_steps as i32); + Ok(()) + }) + } + + /// Get a body's transform (position + rotation). + /// + /// Writes 7 f32s to `out_ptr`: [px, py, pz, rx, ry, rz, rw] + pub fn physics_get_body_transform( + caller: Caller<'_, Self>, + world_id: u32, + body_id: u32, + out_ptr: WasmPtr, + ) -> RtResult { + Self::cvt::(caller, AbiCall::PhysicsGetBodyTransform, |caller| { + let (pos, rot) = crate::host::physics::physics() + .get_body_transform(world_id, body_id) + .ok_or_else(|| WasmError::Wasm(anyhow!("Body not found")))?; + + let (mem, _env) = Self::mem_env(caller); + let out = mem.deref_slice_mut(out_ptr, 28)?; // 7 * f32 + out[0..4].copy_from_slice(&pos[0].to_le_bytes()); + out[4..8].copy_from_slice(&pos[1].to_le_bytes()); + out[8..12].copy_from_slice(&pos[2].to_le_bytes()); + out[12..16].copy_from_slice(&rot[0].to_le_bytes()); + out[16..20].copy_from_slice(&rot[1].to_le_bytes()); + out[20..24].copy_from_slice(&rot[2].to_le_bytes()); + out[24..28].copy_from_slice(&rot[3].to_le_bytes()); + Ok(()) + }) + } + + /// Get a body's linear velocity. + /// + /// Writes 3 f32s to `out_ptr`: [vx, vy, vz] + pub fn physics_get_body_velocity( + caller: Caller<'_, Self>, + world_id: u32, + body_id: u32, + out_ptr: WasmPtr, + ) -> RtResult { + Self::cvt::(caller, AbiCall::PhysicsGetBodyVelocity, |caller| { + let vel = crate::host::physics::physics() + .get_body_velocity(world_id, body_id) + .ok_or_else(|| WasmError::Wasm(anyhow!("Body not found")))?; + + let (mem, _env) = Self::mem_env(caller); + let out = mem.deref_slice_mut(out_ptr, 12)?; // 3 * f32 + out[0..4].copy_from_slice(&vel[0].to_le_bytes()); + out[4..8].copy_from_slice(&vel[1].to_le_bytes()); + out[8..12].copy_from_slice(&vel[2].to_le_bytes()); + Ok(()) + }) + } + + /// Set a body's position. + /// + /// Reads 3 f32s from `pos_ptr`: [px, py, pz] + pub fn physics_set_body_position( + caller: Caller<'_, Self>, + world_id: u32, + body_id: u32, + pos_ptr: WasmPtr, + ) -> RtResult { + Self::cvt::(caller, AbiCall::PhysicsSetBodyPosition, |caller| { + let (mem, _env) = Self::mem_env(caller); + let data = mem.deref_slice(pos_ptr, 12)?; + let pos = [ + f32::from_le_bytes(data[0..4].try_into().unwrap()), + f32::from_le_bytes(data[4..8].try_into().unwrap()), + f32::from_le_bytes(data[8..12].try_into().unwrap()), + ]; + crate::host::physics::physics().set_body_position(world_id, body_id, pos); + Ok(()) + }) + } + + /// Set a body's linear velocity. + /// + /// Reads 3 f32s from `vel_ptr`: [vx, vy, vz] + pub fn physics_set_body_velocity( + caller: Caller<'_, Self>, + world_id: u32, + body_id: u32, + vel_ptr: WasmPtr, + ) -> RtResult { + Self::cvt::(caller, AbiCall::PhysicsSetBodyVelocity, |caller| { + let (mem, _env) = Self::mem_env(caller); + let data = mem.deref_slice(vel_ptr, 12)?; + let vel = [ + f32::from_le_bytes(data[0..4].try_into().unwrap()), + f32::from_le_bytes(data[4..8].try_into().unwrap()), + f32::from_le_bytes(data[8..12].try_into().unwrap()), + ]; + crate::host::physics::physics().set_body_velocity(world_id, body_id, vel); + Ok(()) + }) + } + + /// Set a body's rotation. + /// + /// Reads 4 f32s from `rot_ptr`: [rx, ry, rz, rw] + pub fn physics_set_body_rotation( + caller: Caller<'_, Self>, + world_id: u32, + body_id: u32, + rot_ptr: WasmPtr, + ) -> RtResult { + Self::cvt::(caller, AbiCall::PhysicsSetBodyRotation, |caller| { + let (mem, _env) = Self::mem_env(caller); + let data = mem.deref_slice(rot_ptr, 16)?; + let rot = [ + f32::from_le_bytes(data[0..4].try_into().unwrap()), + f32::from_le_bytes(data[4..8].try_into().unwrap()), + f32::from_le_bytes(data[8..12].try_into().unwrap()), + f32::from_le_bytes(data[12..16].try_into().unwrap()), + ]; + crate::host::physics::physics().set_body_rotation(world_id, body_id, rot); + Ok(()) + }) + } + + /// Apply a force to a body. + /// + /// Reads 3 f32s from `force_ptr`: [fx, fy, fz] + pub fn physics_add_force( + caller: Caller<'_, Self>, + world_id: u32, + body_id: u32, + force_ptr: WasmPtr, + ) -> RtResult { + Self::cvt::(caller, AbiCall::PhysicsAddForce, |caller| { + let (mem, _env) = Self::mem_env(caller); + let data = mem.deref_slice(force_ptr, 12)?; + let force = [ + f32::from_le_bytes(data[0..4].try_into().unwrap()), + f32::from_le_bytes(data[4..8].try_into().unwrap()), + f32::from_le_bytes(data[8..12].try_into().unwrap()), + ]; + crate::host::physics::physics().add_force(world_id, body_id, force); + Ok(()) + }) + } + + /// Apply an impulse to a body. + /// + /// Reads 3 f32s from `impulse_ptr`: [ix, iy, iz] + pub fn physics_add_impulse( + caller: Caller<'_, Self>, + world_id: u32, + body_id: u32, + impulse_ptr: WasmPtr, + ) -> RtResult { + Self::cvt::(caller, AbiCall::PhysicsAddImpulse, |caller| { + let (mem, _env) = Self::mem_env(caller); + let data = mem.deref_slice(impulse_ptr, 12)?; + let impulse = [ + f32::from_le_bytes(data[0..4].try_into().unwrap()), + f32::from_le_bytes(data[4..8].try_into().unwrap()), + f32::from_le_bytes(data[8..12].try_into().unwrap()), + ]; + crate::host::physics::physics().add_impulse(world_id, body_id, impulse); + Ok(()) + }) + } + + // ── Vehicle Physics ──────────────────────────────────────────────── + + /// Create a vehicle in a physics world. + /// + /// `params_ptr` points to the raw bytes of JPC_VehicleParams. + /// `params_len` is the byte length. + /// `pos_ptr` points to [f32; 3] (position). + /// `rot_ptr` points to [f32; 4] (rotation quaternion). + /// Writes the vehicle ID to `out`. + pub fn physics_create_vehicle( + caller: Caller<'_, Self>, + world_id: u32, + params_ptr: WasmPtr, + params_len: u32, + pos_ptr: WasmPtr, + rot_ptr: WasmPtr, + out: WasmPtr, + ) -> RtResult { + Self::cvt_ret::(caller, AbiCall::PhysicsCreateVehicle, out, |caller| { + let (mem, _env) = Self::mem_env(caller); + let params = mem.deref_slice(params_ptr, params_len)?.to_vec(); + let pos_data = mem.deref_slice(pos_ptr, 12)?; + let rot_data = mem.deref_slice(rot_ptr, 16)?; + + let read_f32 = |slice: &[u8], off: usize| -> f32 { + f32::from_le_bytes(slice[off..off + 4].try_into().unwrap()) + }; + + let position = [read_f32(&pos_data, 0), read_f32(&pos_data, 4), read_f32(&pos_data, 8)]; + let rotation = [read_f32(&rot_data, 0), read_f32(&rot_data, 4), read_f32(&rot_data, 8), read_f32(&rot_data, 12)]; + + let vid = crate::host::physics::physics() + .create_vehicle(world_id, ¶ms, position, rotation) + .ok_or_else(|| WasmError::Wasm(anyhow!("Failed to create vehicle")))?; + + Ok(vid) + }) + } + + /// Set driver input for a vehicle. + /// + /// `forward`, `right`, `brake`, `handbrake` are IEEE 754 f32 bit patterns. + pub fn physics_set_vehicle_input( + caller: Caller<'_, Self>, + world_id: u32, + vehicle_id: u32, + forward_bits: u32, + right_bits: u32, + brake_bits: u32, + handbrake_bits: u32, + ) -> RtResult { + Self::cvt::(caller, AbiCall::PhysicsSetVehicleInput, |_caller| { + let forward = f32::from_bits(forward_bits); + let right = f32::from_bits(right_bits); + let brake = f32::from_bits(brake_bits); + let handbrake = f32::from_bits(handbrake_bits); + + if !crate::host::physics::physics().set_vehicle_input(world_id, vehicle_id, forward, right, brake, handbrake) { + return Err(WasmError::Wasm(anyhow!("Vehicle or world not found")).into()); + } + Ok(()) + }) + } + + /// Get vehicle state (body transform, wheel states, engine RPM, gear). + /// + /// Returns data as a BytesSource containing the raw JPC_VehicleState bytes. + /// Writes the BytesSourceId to `out`. + pub fn physics_get_vehicle_state( + caller: Caller<'_, Self>, + world_id: u32, + vehicle_id: u32, + out: WasmPtr, + ) -> RtResult { + Self::cvt_ret::(caller, AbiCall::PhysicsGetVehicleState, out, |caller| { + let state_bytes = crate::host::physics::physics() + .get_vehicle_state(world_id, vehicle_id) + .ok_or_else(|| WasmError::Wasm(anyhow!("Vehicle or world not found")))?; + + let env = caller.data_mut(); + let source_id = env.create_bytes_source(state_bytes.into())?; + Ok(source_id.0) + }) + } + + /// Destroy a vehicle from a physics world. + pub fn physics_destroy_vehicle( + caller: Caller<'_, Self>, + world_id: u32, + vehicle_id: u32, + ) -> RtResult { + Self::cvt::(caller, AbiCall::PhysicsDestroyVehicle, |_caller| { + if !crate::host::physics::physics().destroy_vehicle(world_id, vehicle_id) { + return Err(WasmError::Wasm(anyhow!("Vehicle or world not found")).into()); + } + Ok(()) + }) + } + + /// Get default vehicle parameters. + /// + /// Returns a BytesSource containing the raw JPC_VehicleParams bytes with defaults. + /// Writes the BytesSourceId to `out`. + pub fn physics_get_vehicle_params_default( + caller: Caller<'_, Self>, + out: WasmPtr, + ) -> RtResult { + Self::cvt_ret::(caller, AbiCall::PhysicsGetVehicleParamsDefault, out, |caller| { + use std::mem; + let default_bytes = unsafe { + let mut params: joltc_sys::JPC_VehicleParams = mem::zeroed(); + joltc_sys::JPC_VehicleParams_default(&mut params); + let bytes = std::slice::from_raw_parts( + ¶ms as *const joltc_sys::JPC_VehicleParams as *const u8, + mem::size_of::(), + ); + bytes.to_vec() + }; + + let env = caller.data_mut(); + let source_id = env.create_bytes_source(default_bytes.into())?; + Ok(source_id.0) + }) + } + + /// Get transforms for all bodies in a world (bulk query). + /// + /// Returns data as a BytesSource containing packed records: + /// For each body: [body_id: u32, pos: [f32;3], rot: [f32;4], vel: [f32;3]] = 44 bytes per body + /// + /// Writes the BytesSourceId to `out`. + pub fn physics_get_all_body_transforms( + caller: Caller<'_, Self>, + world_id: u32, + out: WasmPtr, + ) -> RtResult { + Self::cvt_ret::(caller, AbiCall::PhysicsGetAllBodyTransforms, out, |caller| { + let transforms = crate::host::physics::physics() + .get_all_body_transforms(world_id) + .ok_or_else(|| WasmError::Wasm(anyhow!("World not found")))?; + + // Pack into bytes: 44 bytes per body + let mut buf = Vec::with_capacity(transforms.len() * 44); + for (body_id, pos, rot, vel) in &transforms { + buf.extend_from_slice(&body_id.to_le_bytes()); + for v in pos { + buf.extend_from_slice(&v.to_le_bytes()); + } + for v in rot { + buf.extend_from_slice(&v.to_le_bytes()); + } + for v in vel { + buf.extend_from_slice(&v.to_le_bytes()); + } + } + + let env = caller.data_mut(); + let source_id = env.create_bytes_source(buf.into())?; + Ok(source_id.0) + }) + } } type Fut<'caller, T> = Box>; diff --git a/crates/core/src/host/wasmtime/wasmtime_module.rs b/crates/core/src/host/wasmtime/wasmtime_module.rs index 48ac0fe80e2..0690120eb16 100644 --- a/crates/core/src/host/wasmtime/wasmtime_module.rs +++ b/crates/core/src/host/wasmtime/wasmtime_module.rs @@ -50,7 +50,7 @@ impl WasmtimeModule { WasmtimeModule { module } } - pub const IMPLEMENTED_ABI: abi::VersionTuple = abi::VersionTuple::new(10, 4); + pub const IMPLEMENTED_ABI: abi::VersionTuple = abi::VersionTuple::new(10, 5); pub(super) fn link_imports(linker: &mut Linker) -> anyhow::Result<()> { const { assert!(WasmtimeModule::IMPLEMENTED_ABI.major == spacetimedb_lib::MODULE_ABI_MAJOR_VERSION) }; diff --git a/crates/joltc-sys-patched/.cargo-ok b/crates/joltc-sys-patched/.cargo-ok new file mode 100644 index 00000000000..5f8b795830a --- /dev/null +++ b/crates/joltc-sys-patched/.cargo-ok @@ -0,0 +1 @@ +{"v":1} \ No newline at end of file diff --git a/crates/joltc-sys-patched/.cargo_vcs_info.json b/crates/joltc-sys-patched/.cargo_vcs_info.json new file mode 100644 index 00000000000..4fde47e80d8 --- /dev/null +++ b/crates/joltc-sys-patched/.cargo_vcs_info.json @@ -0,0 +1,6 @@ +{ + "git": { + "sha1": "dd8c14371c08f3123c49f93b4f37f85896ac6c50" + }, + "path_in_vcs": "crates/joltc-sys" +} \ No newline at end of file diff --git a/crates/joltc-sys-patched/.gitignore b/crates/joltc-sys-patched/.gitignore new file mode 100644 index 00000000000..ea8c4bf7f35 --- /dev/null +++ b/crates/joltc-sys-patched/.gitignore @@ -0,0 +1 @@ +/target diff --git a/crates/joltc-sys-patched/Cargo.toml b/crates/joltc-sys-patched/Cargo.toml new file mode 100644 index 00000000000..dc8d3ede9d4 --- /dev/null +++ b/crates/joltc-sys-patched/Cargo.toml @@ -0,0 +1,51 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2021" +name = "joltc-sys" +version = "0.3.1+Jolt-5.0.0" +exclude = [ + "JoltC/JoltPhysics/Assets", + "JoltC/JoltPhysics/TestFramework", + "JoltC/JoltPhysics/Samples", + "JoltC/JoltPhysics/UnitTests", +] +description = "Unsafe bindings to Jolt Physics using JoltC" +readme = "README.md" +license = "MIT OR Apache-2.0" +repository = "https://github.com/SecondHalfGames/jolt-rust" + +[dependencies] + +[dev-dependencies.rand] +version = "0.8.5" + +[build-dependencies.anyhow] +version = "1.0.69" + +[build-dependencies.bindgen] +version = "0.69.1" + +[build-dependencies.cc] +version = "1.0.94" +features = ["parallel"] + +[build-dependencies.cmake] +version = "0.1.50" + +[build-dependencies.walkdir] +version = "2.5.0" + +[features] +default = [] +double-precision = [] +object-layer-u32 = [] diff --git a/crates/joltc-sys-patched/Cargo.toml.orig b/crates/joltc-sys-patched/Cargo.toml.orig new file mode 100644 index 00000000000..9a595d38375 --- /dev/null +++ b/crates/joltc-sys-patched/Cargo.toml.orig @@ -0,0 +1,30 @@ +[package] +name = "joltc-sys" +description = "Unsafe bindings to Jolt Physics using JoltC" +version = "0.3.1+Jolt-5.0.0" +license = "MIT OR Apache-2.0" +repository = "https://github.com/SecondHalfGames/jolt-rust" +edition = "2021" + +exclude = ["JoltC/JoltPhysics/Assets", "JoltC/JoltPhysics/TestFramework", "JoltC/JoltPhysics/Samples", "JoltC/JoltPhysics/UnitTests"] + +[features] +default = [] + +# Changes all world coordinates to use f64 instead of 32 coordinates. +double-precision = [] + +# Changes ObjectLayer and related types to be u32 instead of u16. +object-layer-u32 = [] + +[dependencies] + +[build-dependencies] +bindgen = "0.69.1" +cmake = "0.1.50" +cc = { version = "1.0.94", features = ["parallel"] } +walkdir = "2.5.0" +anyhow = "1.0.69" + +[dev-dependencies] +rand = "0.8.5" diff --git a/crates/joltc-sys-patched/JoltC/.editorconfig b/crates/joltc-sys-patched/JoltC/.editorconfig new file mode 100644 index 00000000000..9e1c34d0095 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/.editorconfig @@ -0,0 +1,6 @@ +root = true + +[*.{c,cpp,h}] +charset = utf-8 +indent_style = tab +trim_trailing_whitespace = true \ No newline at end of file diff --git a/crates/joltc-sys-patched/JoltC/.github/workflows/ci.yml b/crates/joltc-sys-patched/JoltC/.github/workflows/ci.yml new file mode 100644 index 00000000000..a5180e2e132 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/.github/workflows/ci.yml @@ -0,0 +1,54 @@ +name: Test Suite + +on: + push: + branches: + - main + + pull_request: + branches: + - main + +jobs: + test-suite: + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest] + build_type: [Debug, Release] + build_flags: ['', '-DDOUBLE_PRECISION=ON', '-DOBJECT_LAYER_BITS=32'] + exclude: + - os: ubuntu-latest + build_type: Release + + runs-on: ${{ matrix.os }} + name: Tests - ${{ matrix.os }} ${{ matrix.build_type }} ${{ matrix.build_flags }} + + steps: + - uses: actions/checkout@v4 + with: + submodules: true + + - name: Setup C++ Tooling + uses: aminya/setup-cpp@v1 + with: + compiler: ${{ contains(matrix.os, 'windows') && 'msvc' || 'clang' }} + cmake: true + + - name: Configure CMake + run: cmake -B build ${{ matrix.build_flags }} -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} + + - name: Build + run: cmake --build build --config=${{ matrix.build_type }} + + - name: Run Hello World + shell: bash + run: | + ./build/${{ matrix.build_type }}/HelloWorld + if: ${{ contains(matrix.os, 'windows') }} + + - name: Run Hello World + shell: bash + run: | + ./build/HelloWorld + if: ${{ !contains(matrix.os, 'windows') }} diff --git a/crates/joltc-sys-patched/JoltC/.gitignore b/crates/joltc-sys-patched/JoltC/.gitignore new file mode 100644 index 00000000000..28ed25ce552 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/.gitignore @@ -0,0 +1 @@ +/build* \ No newline at end of file diff --git a/crates/joltc-sys-patched/JoltC/.gitmodules b/crates/joltc-sys-patched/JoltC/.gitmodules new file mode 100644 index 00000000000..1024921b1d7 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/.gitmodules @@ -0,0 +1,3 @@ +[submodule "JoltPhysics"] + path = JoltPhysics + url = ../../jrouwe/JoltPhysics.git diff --git a/crates/joltc-sys-patched/JoltC/CMakeLists.txt b/crates/joltc-sys-patched/JoltC/CMakeLists.txt new file mode 100644 index 00000000000..82a64066ead --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/CMakeLists.txt @@ -0,0 +1,76 @@ +cmake_minimum_required(VERSION 3.16 FATAL_ERROR) + +# When turning this option on, the library will be compiled using doubles for positions. This allows for much bigger worlds. +option(DOUBLE_PRECISION "Use double precision math" OFF) + +# Number of bits to use in ObjectLayer. Can be 16 or 32. +option(OBJECT_LAYER_BITS "Number of bits in ObjectLayer" 16) + +include(CMakeDependentOption) + +# Ability to toggle between the static and DLL versions of the MSVC runtime library +# Windows Store only supports the DLL version +cmake_dependent_option(USE_STATIC_MSVC_RUNTIME_LIBRARY "Use the static MSVC runtime library" ON "MSVC;NOT WINDOWS_STORE" OFF) + +if (USE_STATIC_MSVC_RUNTIME_LIBRARY) + set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") +endif() + +# Only do this when we're at the top level, see: https://gitlab.kitware.com/cmake/cmake/-/issues/24181 +if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) + set(CMAKE_CONFIGURATION_TYPES "Debug;Release") +endif() + +project(JoltC VERSION 0.1 + DESCRIPTION "C Wrapper for Jolt Physics" + LANGUAGES C CXX) + +add_library(joltc STATIC + JoltC/JoltC.h + JoltC/JoltC.cpp + JoltC/Enums.h + JoltC/Functions.h + JoltC/Vehicle.h + JoltC/Vehicle.cpp + JoltC/Test.cpp +) + +target_compile_features(joltc PUBLIC cxx_std_17) +target_include_directories(joltc PUBLIC . JoltPhysics) +target_link_libraries(joltc PUBLIC Jolt) + +install(TARGETS joltc DESTINATION lib) + +if(MSVC) + target_compile_options(joltc PRIVATE /W4) + + # Ignore 'unreferenced function with internal linkage' + target_compile_options(joltc PRIVATE /wd4505) +else() + target_compile_options(joltc PRIVATE -Wall -Wextra -Wpedantic) +endif() + +if (DOUBLE_PRECISION) + target_compile_definitions(joltc PUBLIC JPC_DOUBLE_PRECISION) +endif() + +if (OBJECT_LAYER_BITS) + target_compile_definitions(joltc PUBLIC JPC_OBJECT_LAYER_BITS=${OBJECT_LAYER_BITS}) +endif() + +if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) + add_executable(HelloWorld + HelloWorld/main.cpp + ) + + # cxx_std_20 gives us designated initializers, bringing us closer to Actual C. + target_compile_features(HelloWorld PRIVATE cxx_std_20) + + # Eventually we should switch back to Actual C: + # set_property(TARGET HelloWorld PROPERTY C_STANDARD 23) + + target_include_directories(HelloWorld PUBLIC .) + target_link_libraries(HelloWorld PUBLIC joltc) +endif() + +add_subdirectory(JoltPhysics/Build) \ No newline at end of file diff --git a/crates/joltc-sys-patched/JoltC/HelloWorld/README.md b/crates/joltc-sys-patched/JoltC/HelloWorld/README.md new file mode 100644 index 00000000000..d1b8c9f9c80 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/HelloWorld/README.md @@ -0,0 +1,2 @@ +# HelloWorld in JoltC +A port of the HelloWorld example from Jolt, but using JoltC. \ No newline at end of file diff --git a/crates/joltc-sys-patched/JoltC/HelloWorld/main.cpp b/crates/joltc-sys-patched/JoltC/HelloWorld/main.cpp new file mode 100644 index 00000000000..b0218fced5c --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/HelloWorld/main.cpp @@ -0,0 +1,218 @@ +#include +#include +#include + +#include "JoltC/JoltC.h" + +#ifdef _MSC_VER + #define unreachable() __assume(0) +#else + #define unreachable() __builtin_unreachable() +#endif + +typedef enum Hello_ObjectLayers { + HELLO_OL_NON_MOVING, + HELLO_OL_MOVING, + HELLO_OL_COUNT, +} Hello_ObjectLayers; + +typedef enum Hello_BroadPhaseLayers { + HELLO_BPL_NON_MOVING, + HELLO_BPL_MOVING, + HELLO_BPL_COUNT, +} Hello_BroadPhaseLayers; + +unsigned int Hello_BPL_GetNumBroadPhaseLayers(const void *self) { + return HELLO_BPL_COUNT; +} + +JPC_BroadPhaseLayer Hello_BPL_GetBroadPhaseLayer(const void *self, JPC_ObjectLayer inLayer) { + switch (inLayer) { + case HELLO_OL_NON_MOVING: + return HELLO_BPL_NON_MOVING; + + case HELLO_OL_MOVING: + return HELLO_BPL_MOVING; + + default: + unreachable(); + } +} + +static JPC_BroadPhaseLayerInterfaceFns Hello_BPL = { + .GetNumBroadPhaseLayers = Hello_BPL_GetNumBroadPhaseLayers, + .GetBroadPhaseLayer = Hello_BPL_GetBroadPhaseLayer, +}; + +bool Hello_OVB_ShouldCollide(const void *self, JPC_ObjectLayer inLayer1, JPC_BroadPhaseLayer inLayer2) { + switch (inLayer1) { + case HELLO_OL_NON_MOVING: + return inLayer2 == HELLO_BPL_MOVING; + + case HELLO_OL_MOVING: + return true; + + default: + unreachable(); + } +} + +static JPC_ObjectVsBroadPhaseLayerFilterFns Hello_OVB = { + .ShouldCollide = Hello_OVB_ShouldCollide, +}; + +bool Hello_OVO_ShouldCollide(const void *self, JPC_ObjectLayer inLayer1, JPC_ObjectLayer inLayer2) { + switch (inLayer1) + { + case HELLO_OL_NON_MOVING: + return inLayer2 == HELLO_OL_MOVING; // Non moving only collides with moving + + case HELLO_OL_MOVING: + return true; // Moving collides with everything + + default: + unreachable(); + } +} + +static JPC_ObjectLayerPairFilterFns Hello_OVO = { + .ShouldCollide = Hello_OVO_ShouldCollide, +}; + +void Hello_Debug_DrawLine(const void *self, JPC_RVec3 inFrom, JPC_RVec3 inTo, JPC_Color inColor) { + // printf("Draw line from (%f, %f, %f) to (%f, %f, %f) with color (%d, %d, %d)\n", + // inFrom.x, inFrom.y, inFrom.z, inTo.x, inTo.y, inTo.z, inColor.r, inColor.g, inColor.b); +} + +static JPC_DebugRendererSimpleFns Hello_DebugRenderer = { + .DrawLine = Hello_Debug_DrawLine, +}; + +int main() { + JPC_RegisterDefaultAllocator(); + JPC_FactoryInit(); + JPC_RegisterTypes(); + + JPC_TempAllocatorImpl* temp_allocator = JPC_TempAllocatorImpl_new(10 * 1024 * 1024); + + JPC_JobSystemThreadPool* job_system = JPC_JobSystemThreadPool_new2(JPC_MAX_PHYSICS_JOBS, JPC_MAX_PHYSICS_BARRIERS); + + JPC_BroadPhaseLayerInterface* broad_phase_layer_interface = JPC_BroadPhaseLayerInterface_new(nullptr, Hello_BPL); + JPC_ObjectVsBroadPhaseLayerFilter* object_vs_broad_phase_layer_filter = JPC_ObjectVsBroadPhaseLayerFilter_new(nullptr, Hello_OVB); + JPC_ObjectLayerPairFilter* object_vs_object_layer_filter = JPC_ObjectLayerPairFilter_new(nullptr, Hello_OVO); + + const unsigned int cMaxBodies = 1024; + const unsigned int cNumBodyMutexes = 0; + const unsigned int cMaxBodyPairs = 1024; + const unsigned int cMaxContactConstraints = 1024; + + JPC_PhysicsSystem* physics_system = JPC_PhysicsSystem_new(); + JPC_PhysicsSystem_Init( + physics_system, + cMaxBodies, + cNumBodyMutexes, + cMaxBodyPairs, + cMaxContactConstraints, + broad_phase_layer_interface, + object_vs_broad_phase_layer_filter, + object_vs_object_layer_filter); + + // TODO: register body activation listener + // TODO: register contact listener + + JPC_BodyInterface* body_interface = JPC_PhysicsSystem_GetBodyInterface(physics_system); + + JPC_BoxShapeSettings floor_shape_settings; + JPC_BoxShapeSettings_default(&floor_shape_settings); + floor_shape_settings.HalfExtent = JPC_Vec3{100.0f, 1.0f, 100.0f}; + floor_shape_settings.Density = 500.0; + + JPC_Shape* floor_shape; + JPC_String* err; + if (!JPC_BoxShapeSettings_Create(&floor_shape_settings, &floor_shape, &err)) { + printf("fatal error: %s\n", JPC_String_c_str(err)); + + // the world is ending, but I guess we can still free memory + JPC_String_delete(err); + + exit(1); + } + + JPC_BodyCreationSettings floor_settings; + JPC_BodyCreationSettings_default(&floor_settings); + floor_settings.Position = JPC_RVec3{0.0, -1.0, 0.0}; + floor_settings.MotionType = JPC_MOTION_TYPE_STATIC; + floor_settings.ObjectLayer = HELLO_OL_NON_MOVING; + floor_settings.Shape = floor_shape; + + JPC_Body* floor = JPC_BodyInterface_CreateBody(body_interface, &floor_settings); + JPC_BodyInterface_AddBody(body_interface, JPC_Body_GetID(floor), JPC_ACTIVATION_DONT_ACTIVATE); + + JPC_SphereShapeSettings sphere_shape_settings; + JPC_SphereShapeSettings_default(&sphere_shape_settings); + sphere_shape_settings.Radius = 0.5; + + JPC_Shape* sphere_shape; + if (!JPC_SphereShapeSettings_Create(&sphere_shape_settings, &sphere_shape, &err)) { + printf("fatal error: %s\n", JPC_String_c_str(err)); + + // the world is ending, but I guess we can still free memory + JPC_String_delete(err); + + exit(1); + } + + JPC_BodyCreationSettings sphere_settings; + JPC_BodyCreationSettings_default(&sphere_settings); + sphere_settings.Position = JPC_RVec3{0.0, 2.0, 0.0}; + sphere_settings.MotionType = JPC_MOTION_TYPE_DYNAMIC; + sphere_settings.ObjectLayer = HELLO_OL_MOVING; + sphere_settings.Shape = sphere_shape; + + JPC_Body* sphere = JPC_BodyInterface_CreateBody(body_interface, &sphere_settings); + JPC_BodyID sphere_id = JPC_Body_GetID(sphere); + JPC_BodyInterface_AddBody(body_interface, sphere_id, JPC_ACTIVATION_ACTIVATE); + + JPC_BodyInterface_SetLinearVelocity(body_interface, sphere_id, JPC_Vec3{0.0, -5.0, 0.0}); + + JPC_DebugRendererSimple* debug_renderer = JPC_DebugRendererSimple_new(nullptr, Hello_DebugRenderer); + JPC_BodyManager_DrawSettings draw_settings; + JPC_BodyManager_DrawSettings_default(&draw_settings); + JPC_PhysicsSystem_DrawBodies(physics_system, &draw_settings, debug_renderer, nullptr); + + JPC_PhysicsSystem_OptimizeBroadPhase(physics_system); + + const float cDeltaTime = 1.0f / 60.0f; + const int cCollisionSteps = 1; + + int step = 0; + while (JPC_BodyInterface_IsActive(body_interface, sphere_id)) { + ++step; + + JPC_RVec3 position = JPC_BodyInterface_GetCenterOfMassPosition(body_interface, sphere_id); + JPC_Vec3 velocity = JPC_BodyInterface_GetLinearVelocity(body_interface, sphere_id); + + printf("Step %d: Position = (%f, %f, %f), Velocity = (%f, %f, %f)\n", step, position.x, position.y, position.z, velocity.x, velocity.y, velocity.z); + + JPC_PhysicsSystem_Update(physics_system, cDeltaTime, cCollisionSteps, temp_allocator, job_system); + } + + JPC_BodyInterface_RemoveBody(body_interface, sphere_id); + JPC_BodyInterface_DestroyBody(body_interface, sphere_id); + + JPC_BodyInterface_RemoveBody(body_interface, JPC_Body_GetID(floor)); + JPC_BodyInterface_DestroyBody(body_interface, JPC_Body_GetID(floor)); + + JPC_PhysicsSystem_delete(physics_system); + JPC_BroadPhaseLayerInterface_delete(broad_phase_layer_interface); + JPC_ObjectVsBroadPhaseLayerFilter_delete(object_vs_broad_phase_layer_filter); + JPC_ObjectLayerPairFilter_delete(object_vs_object_layer_filter); + + JPC_JobSystemThreadPool_delete(job_system); + JPC_TempAllocatorImpl_delete(temp_allocator); + + JPC_UnregisterTypes(); + JPC_FactoryDelete(); + + printf("Hello, world!\n"); +} \ No newline at end of file diff --git a/crates/joltc-sys-patched/JoltC/JoltC/Enums.h b/crates/joltc-sys-patched/JoltC/JoltC/Enums.h new file mode 100644 index 00000000000..84f17c47f40 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltC/Enums.h @@ -0,0 +1,301 @@ +#pragma once + +#include + +const int JPC_MAX_PHYSICS_JOBS = 2048; +const int JPC_MAX_PHYSICS_BARRIERS = 8; + +ENSURE_EQUAL(JPC_MAX_PHYSICS_JOBS, JPH::cMaxPhysicsJobs); +ENSURE_EQUAL(JPC_MAX_PHYSICS_BARRIERS, JPH::cMaxPhysicsBarriers); + +typedef enum JPC_ShapeType: uint8_t { + JPC_SHAPE_TYPE_CONVEX, + JPC_SHAPE_TYPE_COMPOUND, + JPC_SHAPE_TYPE_DECORATED, + JPC_SHAPE_TYPE_MESH, + JPC_SHAPE_TYPE_HEIGHT_FIELD, + JPC_SHAPE_TYPE_SOFTBODY, + JPC_SHAPE_TYPE_USER1, + JPC_SHAPE_TYPE_USER2, + JPC_SHAPE_TYPE_USER3, + JPC_SHAPE_TYPE_USER4, +} JPC_ShapeType; + +ENSURE_ENUM_EQ(JPC_SHAPE_TYPE_CONVEX, JPH::EShapeType::Convex) +ENSURE_ENUM_EQ(JPC_SHAPE_TYPE_COMPOUND, JPH::EShapeType::Compound) +ENSURE_ENUM_EQ(JPC_SHAPE_TYPE_DECORATED, JPH::EShapeType::Decorated) +ENSURE_ENUM_EQ(JPC_SHAPE_TYPE_MESH, JPH::EShapeType::Mesh) +ENSURE_ENUM_EQ(JPC_SHAPE_TYPE_HEIGHT_FIELD, JPH::EShapeType::HeightField) +ENSURE_ENUM_EQ(JPC_SHAPE_TYPE_USER1, JPH::EShapeType::User1) +ENSURE_ENUM_EQ(JPC_SHAPE_TYPE_USER2, JPH::EShapeType::User2) +ENSURE_ENUM_EQ(JPC_SHAPE_TYPE_USER3, JPH::EShapeType::User3) +ENSURE_ENUM_EQ(JPC_SHAPE_TYPE_USER4, JPH::EShapeType::User4) + +typedef enum JPC_ShapeSubType: uint8_t { + JPC_SHAPE_SUB_TYPE_SPHERE, + JPC_SHAPE_SUB_TYPE_BOX, + JPC_SHAPE_SUB_TYPE_TRIANGLE, + JPC_SHAPE_SUB_TYPE_CAPSULE, + JPC_SHAPE_SUB_TYPE_TAPEREDCAPSULE, + JPC_SHAPE_SUB_TYPE_CYLINDER, + JPC_SHAPE_SUB_TYPE_CONVEX_HULL, + JPC_SHAPE_SUB_TYPE_STATIC_COMPOUND, + JPC_SHAPE_SUB_TYPE_MUTABLE_COMPOUND, + JPC_SHAPE_SUB_TYPE_ROTATED_TRANSLATED, + JPC_SHAPE_SUB_TYPE_SCALED, + JPC_SHAPE_SUB_TYPE_OFFSET_CENTER_OF_MASS, + JPC_SHAPE_SUB_TYPE_MESH, + JPC_SHAPE_SUB_TYPE_HEIGHT_FIELD, + JPC_SHAPE_SUB_TYPE_SOFT_BODY, + JPC_SHAPE_SUB_TYPE_USER1, + JPC_SHAPE_SUB_TYPE_USER2, + JPC_SHAPE_SUB_TYPE_USER3, + JPC_SHAPE_SUB_TYPE_USER4, + JPC_SHAPE_SUB_TYPE_USER5, + JPC_SHAPE_SUB_TYPE_USER6, + JPC_SHAPE_SUB_TYPE_USER7, + JPC_SHAPE_SUB_TYPE_USER8, + JPC_SHAPE_SUB_TYPE_USER_CONVEX1, + JPC_SHAPE_SUB_TYPE_USER_CONVEX2, + JPC_SHAPE_SUB_TYPE_USER_CONVEX3, + JPC_SHAPE_SUB_TYPE_USER_CONVEX4, + JPC_SHAPE_SUB_TYPE_USER_CONVEX5, + JPC_SHAPE_SUB_TYPE_USER_CONVEX6, + JPC_SHAPE_SUB_TYPE_USER_CONVEX7, + JPC_SHAPE_SUB_TYPE_USER_CONVEX8, +} JPC_ShapeSubType; + +ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_SPHERE, JPH::EShapeSubType::Sphere) +ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_BOX, JPH::EShapeSubType::Box) +ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_TRIANGLE, JPH::EShapeSubType::Triangle) +ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_CAPSULE, JPH::EShapeSubType::Capsule) +ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_TAPEREDCAPSULE, JPH::EShapeSubType::TaperedCapsule) +ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_CYLINDER, JPH::EShapeSubType::Cylinder) +ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_CONVEX_HULL, JPH::EShapeSubType::ConvexHull) +ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_STATIC_COMPOUND, JPH::EShapeSubType::StaticCompound) +ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_MUTABLE_COMPOUND, JPH::EShapeSubType::MutableCompound) +ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_ROTATED_TRANSLATED, JPH::EShapeSubType::RotatedTranslated) +ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_SCALED, JPH::EShapeSubType::Scaled) +ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_OFFSET_CENTER_OF_MASS, JPH::EShapeSubType::OffsetCenterOfMass) +ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_MESH, JPH::EShapeSubType::Mesh) +ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_HEIGHT_FIELD, JPH::EShapeSubType::HeightField) +ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_SOFT_BODY, JPH::EShapeSubType::SoftBody) +ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_USER1, JPH::EShapeSubType::User1) +ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_USER2, JPH::EShapeSubType::User2) +ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_USER3, JPH::EShapeSubType::User3) +ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_USER4, JPH::EShapeSubType::User4) +ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_USER5, JPH::EShapeSubType::User5) +ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_USER6, JPH::EShapeSubType::User6) +ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_USER7, JPH::EShapeSubType::User7) +ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_USER8, JPH::EShapeSubType::User8) +ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_USER_CONVEX1, JPH::EShapeSubType::UserConvex1) +ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_USER_CONVEX2, JPH::EShapeSubType::UserConvex2) +ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_USER_CONVEX3, JPH::EShapeSubType::UserConvex3) +ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_USER_CONVEX4, JPH::EShapeSubType::UserConvex4) +ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_USER_CONVEX5, JPH::EShapeSubType::UserConvex5) +ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_USER_CONVEX6, JPH::EShapeSubType::UserConvex6) +ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_USER_CONVEX7, JPH::EShapeSubType::UserConvex7) +ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_USER_CONVEX8, JPH::EShapeSubType::UserConvex8) + +typedef uint32_t JPC_PhysicsUpdateError; +const JPC_PhysicsUpdateError JPC_PHYSICS_UPDATE_ERROR_NONE = 0; +const JPC_PhysicsUpdateError JPC_PHYSICS_UPDATE_ERROR_MANIFOLD_CACHE_FULL = 1 << 0; +const JPC_PhysicsUpdateError JPC_PHYSICS_UPDATE_ERROR_BODY_PAIR_CACHE_FULL = 1 << 1; +const JPC_PhysicsUpdateError JPC_PHYSICS_UPDATE_ERROR_CONTACT_CONSTRAINTS_FULL = 1 << 2; + +ENSURE_ENUM_EQ(JPC_PHYSICS_UPDATE_ERROR_NONE, JPH::EPhysicsUpdateError::None) +ENSURE_ENUM_EQ(JPC_PHYSICS_UPDATE_ERROR_MANIFOLD_CACHE_FULL, JPH::EPhysicsUpdateError::ManifoldCacheFull) +ENSURE_ENUM_EQ(JPC_PHYSICS_UPDATE_ERROR_BODY_PAIR_CACHE_FULL, JPH::EPhysicsUpdateError::BodyPairCacheFull) +ENSURE_ENUM_EQ(JPC_PHYSICS_UPDATE_ERROR_CONTACT_CONSTRAINTS_FULL, JPH::EPhysicsUpdateError::ContactConstraintsFull) + +typedef enum JPC_ConstraintType: uint32_t { + JPC_CONSTRAINT_TYPE_CONSTRAINT, + JPC_CONSTRAINT_TYPE_TWO_BODY_CONSTRAINT, +} JPC_ConstraintType; + +ENSURE_ENUM_EQ(JPC_CONSTRAINT_TYPE_CONSTRAINT, JPH::EConstraintType::Constraint) +ENSURE_ENUM_EQ(JPC_CONSTRAINT_TYPE_TWO_BODY_CONSTRAINT, JPH::EConstraintType::TwoBodyConstraint) + +typedef enum JPC_ConstraintSubType: uint32_t { + JPC_CONSTRAINT_SUB_TYPE_FIXED, + JPC_CONSTRAINT_SUB_TYPE_POINT, + JPC_CONSTRAINT_SUB_TYPE_HINGE, + JPC_CONSTRAINT_SUB_TYPE_SLIDER, + JPC_CONSTRAINT_SUB_TYPE_DISTANCE, + JPC_CONSTRAINT_SUB_TYPE_CONE, + JPC_CONSTRAINT_SUB_TYPE_SWING_TWIST, + JPC_CONSTRAINT_SUB_TYPE_SIX_DOF, + JPC_CONSTRAINT_SUB_TYPE_PATH, + JPC_CONSTRAINT_SUB_TYPE_VEHICLE, + JPC_CONSTRAINT_SUB_TYPE_RACK_AND_PINION, + JPC_CONSTRAINT_SUB_TYPE_GEAR, + JPC_CONSTRAINT_SUB_TYPE_PULLEY, + JPC_CONSTRAINT_SUB_TYPE_USER1, + JPC_CONSTRAINT_SUB_TYPE_USER2, + JPC_CONSTRAINT_SUB_TYPE_USER3, + JPC_CONSTRAINT_SUB_TYPE_USER4, +} JPC_ConstraintSubType; + +ENSURE_ENUM_EQ(JPC_CONSTRAINT_SUB_TYPE_FIXED, JPH::EConstraintSubType::Fixed) +ENSURE_ENUM_EQ(JPC_CONSTRAINT_SUB_TYPE_POINT, JPH::EConstraintSubType::Point) +ENSURE_ENUM_EQ(JPC_CONSTRAINT_SUB_TYPE_HINGE, JPH::EConstraintSubType::Hinge) +ENSURE_ENUM_EQ(JPC_CONSTRAINT_SUB_TYPE_SLIDER, JPH::EConstraintSubType::Slider) +ENSURE_ENUM_EQ(JPC_CONSTRAINT_SUB_TYPE_DISTANCE, JPH::EConstraintSubType::Distance) +ENSURE_ENUM_EQ(JPC_CONSTRAINT_SUB_TYPE_CONE, JPH::EConstraintSubType::Cone) +ENSURE_ENUM_EQ(JPC_CONSTRAINT_SUB_TYPE_SWING_TWIST, JPH::EConstraintSubType::SwingTwist) +ENSURE_ENUM_EQ(JPC_CONSTRAINT_SUB_TYPE_SIX_DOF, JPH::EConstraintSubType::SixDOF) +ENSURE_ENUM_EQ(JPC_CONSTRAINT_SUB_TYPE_PATH, JPH::EConstraintSubType::Path) +ENSURE_ENUM_EQ(JPC_CONSTRAINT_SUB_TYPE_VEHICLE, JPH::EConstraintSubType::Vehicle) +ENSURE_ENUM_EQ(JPC_CONSTRAINT_SUB_TYPE_RACK_AND_PINION, JPH::EConstraintSubType::RackAndPinion) +ENSURE_ENUM_EQ(JPC_CONSTRAINT_SUB_TYPE_GEAR, JPH::EConstraintSubType::Gear) +ENSURE_ENUM_EQ(JPC_CONSTRAINT_SUB_TYPE_PULLEY, JPH::EConstraintSubType::Pulley) +ENSURE_ENUM_EQ(JPC_CONSTRAINT_SUB_TYPE_USER1, JPH::EConstraintSubType::User1) +ENSURE_ENUM_EQ(JPC_CONSTRAINT_SUB_TYPE_USER2, JPH::EConstraintSubType::User2) +ENSURE_ENUM_EQ(JPC_CONSTRAINT_SUB_TYPE_USER3, JPH::EConstraintSubType::User3) +ENSURE_ENUM_EQ(JPC_CONSTRAINT_SUB_TYPE_USER4, JPH::EConstraintSubType::User4) + +typedef enum JPC_ConstraintSpace: uint32_t { + JPC_CONSTRAINT_SPACE_LOCAL_TO_BODY_COM, + JPC_CONSTRAINT_SPACE_WORLD_SPACE, +} JPC_ConstraintSpace; + +ENSURE_ENUM_EQ(JPC_CONSTRAINT_SPACE_LOCAL_TO_BODY_COM, JPH::EConstraintSpace::LocalToBodyCOM) +ENSURE_ENUM_EQ(JPC_CONSTRAINT_SPACE_WORLD_SPACE, JPH::EConstraintSpace::WorldSpace) + +typedef enum JPC_MotionType: uint8_t { + JPC_MOTION_TYPE_STATIC, + JPC_MOTION_TYPE_KINEMATIC, + JPC_MOTION_TYPE_DYNAMIC, +} JPC_MotionType; + +ENSURE_ENUM_EQ(JPC_MOTION_TYPE_STATIC, JPH::EMotionType::Static) +ENSURE_ENUM_EQ(JPC_MOTION_TYPE_KINEMATIC, JPH::EMotionType::Kinematic) +ENSURE_ENUM_EQ(JPC_MOTION_TYPE_DYNAMIC, JPH::EMotionType::Dynamic) + +typedef enum JPC_MotionQuality: uint8_t { + JPC_MOTION_QUALITY_DISCRETE, + JPC_MOTION_QUALITY_LINEAR_CAST, +} JPC_MotionQuality; + +ENSURE_ENUM_EQ(JPC_MOTION_QUALITY_DISCRETE, JPH::EMotionQuality::Discrete) +ENSURE_ENUM_EQ(JPC_MOTION_QUALITY_LINEAR_CAST, JPH::EMotionQuality::LinearCast) + +typedef enum JPC_OverrideMassProperties: uint8_t { + JPC_OVERRIDE_MASS_PROPS_CALC_MASS_INERTIA, + JPC_OVERRIDE_MASS_PROPS_CALC_INERTIA, + JPC_OVERRIDE_MASS_PROPS_MASS_INERTIA_PROVIDED, +} JPC_OverrideMassProperties; + +ENSURE_ENUM_EQ(JPC_OVERRIDE_MASS_PROPS_CALC_MASS_INERTIA, + JPH::EOverrideMassProperties::CalculateMassAndInertia); +ENSURE_ENUM_EQ(JPC_OVERRIDE_MASS_PROPS_CALC_INERTIA, + JPH::EOverrideMassProperties::CalculateInertia); +ENSURE_ENUM_EQ(JPC_OVERRIDE_MASS_PROPS_MASS_INERTIA_PROVIDED, + JPH::EOverrideMassProperties::MassAndInertiaProvided); + +typedef enum JPC_GroundState: uint32_t { + JPC_CHARACTER_GROUND_STATE_ON_GROUND, + JPC_CHARACTER_GROUND_STATE_ON_STEEP_GROUND, + JPC_CHARACTER_GROUND_STATE_NOT_SUPPORTED, + JPC_CHARACTER_GROUND_STATE_IN_AIR, +} JPC_GroundState; + +// ENSURE_ENUM_EQ(JPC_CHARACTER_GROUND_STATE_ON_GROUND, JPH::EGroundState::OnGround) +// ENSURE_ENUM_EQ(JPC_CHARACTER_GROUND_STATE_ON_STEEP_GROUND, JPH::EGroundState::OnSteepGround) +// ENSURE_ENUM_EQ(JPC_CHARACTER_GROUND_STATE_NOT_SUPPORTED, JPH::EGroundState::NotSupported) +// ENSURE_ENUM_EQ(JPC_CHARACTER_GROUND_STATE_IN_AIR, JPH::EGroundState::InAir) + +typedef enum JPC_Activation: uint32_t { + JPC_ACTIVATION_ACTIVATE = 0, + JPC_ACTIVATION_DONT_ACTIVATE = 1, +} JPC_Activation; + +ENSURE_ENUM_EQ(JPC_ACTIVATION_ACTIVATE, JPH::EActivation::Activate) +ENSURE_ENUM_EQ(JPC_ACTIVATION_DONT_ACTIVATE, JPH::EActivation::DontActivate) + +typedef enum JPC_ValidateResult: uint32_t { + JPC_VALIDATE_RESULT_ACCEPT_ALL_CONTACTS, + JPC_VALIDATE_RESULT_ACCEPT_CONTACT, + JPC_VALIDATE_RESULT_REJECT_CONTACT, + JPC_VALIDATE_RESULT_REJECT_ALL_CONTACTS, +} JPC_ValidateResult; + +ENSURE_ENUM_EQ(JPC_VALIDATE_RESULT_ACCEPT_ALL_CONTACTS, + JPH::ValidateResult::AcceptAllContactsForThisBodyPair); +ENSURE_ENUM_EQ(JPC_VALIDATE_RESULT_ACCEPT_CONTACT, + JPH::ValidateResult::AcceptContact); +ENSURE_ENUM_EQ(JPC_VALIDATE_RESULT_REJECT_CONTACT, + JPH::ValidateResult::RejectContact); +ENSURE_ENUM_EQ(JPC_VALIDATE_RESULT_REJECT_ALL_CONTACTS, + JPH::ValidateResult::RejectAllContactsForThisBodyPair); + +typedef enum JPC_BackFaceMode: uint8_t { + JPC_BACK_FACE_IGNORE, + JPC_BACK_FACE_COLLIDE, +} JPC_BackFaceMode; + +ENSURE_ENUM_EQ(JPC_BACK_FACE_IGNORE, JPH::EBackFaceMode::IgnoreBackFaces) +ENSURE_ENUM_EQ(JPC_BACK_FACE_COLLIDE, JPH::EBackFaceMode::CollideWithBackFaces) + +typedef enum JPC_BodyType: uint8_t { + JPC_BODY_TYPE_RIGID_BODY = 0, + JPC_BODY_TYPE_SOFT_BODY = 1, +} JPC_BodyType; + +ENSURE_ENUM_EQ(JPC_BODY_TYPE_RIGID_BODY, JPH::EBodyType::RigidBody) +ENSURE_ENUM_EQ(JPC_BODY_TYPE_SOFT_BODY, JPH::EBodyType::SoftBody) + +typedef enum JPC_AllowedDOFs: uint8_t { + JPC_ALLOWED_DOFS_NONE = 0b000000, + JPC_ALLOWED_DOFS_ALL = 0b111111, + JPC_ALLOWED_DOFS_TRANSLATIONX = 0b000001, + JPC_ALLOWED_DOFS_TRANSLATIONY = 0b000010, + JPC_ALLOWED_DOFS_TRANSLATIONZ = 0b000100, + JPC_ALLOWED_DOFS_ROTATIONX = 0b001000, + JPC_ALLOWED_DOFS_ROTATIONY = 0b010000, + JPC_ALLOWED_DOFS_ROTATIONZ = 0b100000, + JPC_ALLOWED_DOFS_PLANE2D = JPC_ALLOWED_DOFS_TRANSLATIONX | JPC_ALLOWED_DOFS_TRANSLATIONY | JPC_ALLOWED_DOFS_ROTATIONZ, +} JPC_AllowedDOFs; + +ENSURE_ENUM_EQ(JPC_ALLOWED_DOFS_NONE, JPH::EAllowedDOFs::None) +ENSURE_ENUM_EQ(JPC_ALLOWED_DOFS_ALL, JPH::EAllowedDOFs::All) +ENSURE_ENUM_EQ(JPC_ALLOWED_DOFS_TRANSLATIONX, JPH::EAllowedDOFs::TranslationX) +ENSURE_ENUM_EQ(JPC_ALLOWED_DOFS_TRANSLATIONY, JPH::EAllowedDOFs::TranslationY) +ENSURE_ENUM_EQ(JPC_ALLOWED_DOFS_TRANSLATIONZ, JPH::EAllowedDOFs::TranslationZ) +ENSURE_ENUM_EQ(JPC_ALLOWED_DOFS_ROTATIONX, JPH::EAllowedDOFs::RotationX) +ENSURE_ENUM_EQ(JPC_ALLOWED_DOFS_ROTATIONY, JPH::EAllowedDOFs::RotationY) +ENSURE_ENUM_EQ(JPC_ALLOWED_DOFS_ROTATIONZ, JPH::EAllowedDOFs::RotationZ) +ENSURE_ENUM_EQ(JPC_ALLOWED_DOFS_PLANE2D, JPH::EAllowedDOFs::Plane2D) + +typedef enum JPC_Features: uint32_t { + JPC_FEATURE_DOUBLE_PRECISION = (1 << 0), + JPC_FEATURE_NEON = (1 << 1), + JPC_FEATURE_SSE = (1 << 2), + JPC_FEATURE_SSE4_1 = (1 << 3), + JPC_FEATURE_SSE4_2 = (1 << 4), + JPC_FEATURE_AVX = (1 << 5), + JPC_FEATURE_AVX2 = (1 << 6), + JPC_FEATURE_AVX512 = (1 << 7), + JPC_FEATURE_F16C = (1 << 8), + JPC_FEATURE_LZCNT = (1 << 9), + JPC_FEATURE_TZCNT = (1 << 10), + JPC_FEATURE_FMADD = (1 << 11), + JPC_FEATURE_PLATFORM_DETERMINISTIC = (1 << 12), + JPC_FEATURE_FLOATING_POINT_EXCEPTIONS = (1 << 13), + JPC_FEATURE_DEBUG = (1 << 14), +} JPC_Features; + +typedef int JPC_ShapeColor; +const JPC_ShapeColor JPC_SHAPE_COLOR_INSTANCE_COLOR = 0; +const JPC_ShapeColor JPC_SHAPE_COLOR_SHAPE_TYPE_COLOR = 1; +const JPC_ShapeColor JPC_SHAPE_COLOR_MOTION_TYPE_COLOR = 2; +const JPC_ShapeColor JPC_SHAPE_COLOR_SLEEP_COLOR = 3; +const JPC_ShapeColor JPC_SHAPE_COLOR_ISLAND_COLOR = 4; +const JPC_ShapeColor JPC_SHAPE_COLOR_MATERIAL_COLOR = 5; + +ENSURE_ENUM_EQ(JPC_SHAPE_COLOR_INSTANCE_COLOR, JPH::BodyManager::EShapeColor::InstanceColor) +ENSURE_ENUM_EQ(JPC_SHAPE_COLOR_SHAPE_TYPE_COLOR, JPH::BodyManager::EShapeColor::ShapeTypeColor) +ENSURE_ENUM_EQ(JPC_SHAPE_COLOR_MOTION_TYPE_COLOR, JPH::BodyManager::EShapeColor::MotionTypeColor) +ENSURE_ENUM_EQ(JPC_SHAPE_COLOR_SLEEP_COLOR, JPH::BodyManager::EShapeColor::SleepColor) +ENSURE_ENUM_EQ(JPC_SHAPE_COLOR_ISLAND_COLOR, JPH::BodyManager::EShapeColor::IslandColor) +ENSURE_ENUM_EQ(JPC_SHAPE_COLOR_MATERIAL_COLOR, JPH::BodyManager::EShapeColor::MaterialColor) \ No newline at end of file diff --git a/crates/joltc-sys-patched/JoltC/JoltC/Functions.h b/crates/joltc-sys-patched/JoltC/JoltC/Functions.h new file mode 100644 index 00000000000..f8dcdee8ad5 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltC/Functions.h @@ -0,0 +1,819 @@ +#pragma once + +#include +#include +#include +#include + +#ifdef _MSC_VER + #define JPC_API extern __declspec(dllexport) +#else + #define JPC_API +#endif + +static float JPC_PI = 3.14159265358979323846f; + +// C-compatible typedefs that match Jolt's internal primitive typedefs. +#define uint unsigned int + +#ifdef __cplusplus +extern "C" { +#endif + +JPC_API void JPC_RegisterDefaultAllocator(); +JPC_API void JPC_FactoryInit(); +JPC_API void JPC_FactoryDelete(); +JPC_API void JPC_RegisterTypes(); +JPC_API void JPC_UnregisterTypes(); + +//////////////////////////////////////////////////////////////////////////////// +// Primitive types + +typedef struct JPC_Float3 { + float x; + float y; + float z; +} JPC_Float3; + +ENSURE_SIZE_ALIGN(JPC_Float3, JPH::Float3) + +typedef struct JPC_Vec3 { + alignas(16) float x; + float y; + float z; + float _w; +} JPC_Vec3; + +ENSURE_SIZE_ALIGN(JPC_Vec3, JPH::Vec3) + +typedef struct JPC_Vec4 { + alignas(16) float x; + float y; + float z; + float w; +} JPC_Vec4; + +ENSURE_SIZE_ALIGN(JPC_Vec4, JPH::Vec4) + +typedef struct JPC_DVec3 { + alignas(32) double x; + double y; + double z; + double _w; +} JPC_DVec3; + +ENSURE_SIZE_ALIGN(JPC_DVec3, JPH::DVec3) + +typedef struct JPC_Quat { + alignas(16) float x; + float y; + float z; + float w; +} JPC_Quat; + +ENSURE_SIZE_ALIGN(JPC_Quat, JPH::Quat) + +typedef struct JPC_Mat44 { + alignas(16) JPC_Vec4 matrix[4]; +} JPC_Mat44; + +ENSURE_SIZE_ALIGN(JPC_Mat44, JPH::Mat44) + +typedef struct JPC_DMat44 { + alignas(32) JPC_Vec4 col[3]; + JPC_DVec3 col3; +} JPC_DMat44; + +ENSURE_SIZE_ALIGN(JPC_DMat44, JPH::DMat44) + +typedef struct JPC_Color { + alignas(uint32_t) uint8_t r; + uint8_t g; + uint8_t b; + uint8_t a; +} JPC_Color; + +ENSURE_SIZE_ALIGN(JPC_Color, JPH::Color) + +#ifdef JPC_DOUBLE_PRECISION + typedef JPC_DVec3 JPC_RVec3; + typedef JPC_DMat44 JPC_RMat44; + typedef double Real; +#else + typedef JPC_Vec3 JPC_RVec3; + typedef JPC_Mat44 JPC_RMat44; + typedef float Real; +#endif + +ENSURE_SIZE_ALIGN(JPC_RVec3, JPH::RVec3) + +typedef uint32_t JPC_BodyID; +ENSURE_SIZE_ALIGN(JPC_BodyID, JPH::BodyID) + +typedef uint32_t JPC_SubShapeID; +ENSURE_SIZE_ALIGN(JPC_SubShapeID, JPH::SubShapeID) + +typedef uint8_t JPC_BroadPhaseLayer; +ENSURE_SIZE_ALIGN(JPC_BroadPhaseLayer, JPH::BroadPhaseLayer) + +#ifndef JPC_OBJECT_LAYER_BITS + #define JPC_OBJECT_LAYER_BITS 16 +#endif + +#if JPC_OBJECT_LAYER_BITS == 16 + typedef uint16_t JPC_ObjectLayer; +#elif JPC_OBJECT_LAYER_BITS == 32 + typedef uint32_t JPC_ObjectLayer; +#else + #error "JPC_OBJECT_LAYER_BITS must be 16 or 32" +#endif + +ENSURE_SIZE_ALIGN(JPC_ObjectLayer, JPH::ObjectLayer) + +typedef struct JPC_IndexedTriangleNoMaterial { + uint32_t idx[3]; +} JPC_IndexedTriangleNoMaterial; + +ENSURE_SIZE_ALIGN(JPC_IndexedTriangleNoMaterial, JPH::IndexedTriangleNoMaterial) + +typedef struct JPC_IndexedTriangle { + uint32_t idx[3]; + uint32_t materialIndex; +} JPC_IndexedTriangle; + +ENSURE_SIZE_ALIGN(JPC_IndexedTriangle, JPH::IndexedTriangle) + +typedef struct JPC_RayCast { + JPC_Vec3 Origin; + JPC_Vec3 Direction; +} JPC_RayCast; + +typedef struct JPC_RRayCast { + JPC_RVec3 Origin; + JPC_Vec3 Direction; +} JPC_RRayCast; + +typedef struct JPC_RayCastResult { + JPC_BodyID BodyID; + float Fraction; + JPC_SubShapeID SubShapeID2; +} JPC_RayCastResult; + +typedef struct JPC_Body JPC_Body; + +//////////////////////////////////////////////////////////////////////////////// +// VertexList == Array == std::vector + +typedef struct JPC_VertexList JPC_VertexList; + +JPC_API JPC_VertexList* JPC_VertexList_new(const JPC_Float3* storage, size_t len); +JPC_API void JPC_VertexList_delete(JPC_VertexList* object); + +//////////////////////////////////////////////////////////////////////////////// +// IndexedTriangleList == Array == std::vector + +typedef struct JPC_IndexedTriangleList JPC_IndexedTriangleList; + +JPC_API JPC_IndexedTriangleList* JPC_IndexedTriangleList_new(const JPC_IndexedTriangle* storage, size_t len); +JPC_API void JPC_IndexedTriangleList_delete(JPC_IndexedTriangleList* object); + +//////////////////////////////////////////////////////////////////////////////// +// TempAllocatorImpl + +typedef struct JPC_TempAllocatorImpl JPC_TempAllocatorImpl; + +JPC_API JPC_TempAllocatorImpl* JPC_TempAllocatorImpl_new(uint size); +JPC_API void JPC_TempAllocatorImpl_delete(JPC_TempAllocatorImpl* object); + +//////////////////////////////////////////////////////////////////////////////// +// JobSystemThreadPool + +typedef struct JPC_JobSystemThreadPool JPC_JobSystemThreadPool; + +JPC_API JPC_JobSystemThreadPool* JPC_JobSystemThreadPool_new2( + uint inMaxJobs, + uint inMaxBarriers); +JPC_API JPC_JobSystemThreadPool* JPC_JobSystemThreadPool_new3( + uint inMaxJobs, + uint inMaxBarriers, + int inNumThreads); + +JPC_API void JPC_JobSystemThreadPool_delete(JPC_JobSystemThreadPool* object); + +//////////////////////////////////////////////////////////////////////////////// +// BroadPhaseLayerInterface + +typedef struct JPC_BroadPhaseLayerInterfaceFns { + uint (*GetNumBroadPhaseLayers)(const void *self); + JPC_BroadPhaseLayer (*GetBroadPhaseLayer)(const void *self, JPC_ObjectLayer inLayer); +} JPC_BroadPhaseLayerInterfaceFns; + +typedef struct JPC_BroadPhaseLayerInterface JPC_BroadPhaseLayerInterface; + +JPC_API JPC_BroadPhaseLayerInterface* JPC_BroadPhaseLayerInterface_new( + const void *self, + JPC_BroadPhaseLayerInterfaceFns fns); + +JPC_API void JPC_BroadPhaseLayerInterface_delete(JPC_BroadPhaseLayerInterface* object); + +//////////////////////////////////////////////////////////////////////////////// +// BroadPhaseLayerFilter + +typedef struct JPC_BroadPhaseLayerFilterFns { + bool (*ShouldCollide)(const void *self, JPC_BroadPhaseLayer inLayer); +} JPC_BroadPhaseLayerFilterFns; + +typedef struct JPC_BroadPhaseLayerFilter JPC_BroadPhaseLayerFilter; + +JPC_API JPC_BroadPhaseLayerFilter* JPC_BroadPhaseLayerFilter_new( + const void *self, + JPC_BroadPhaseLayerFilterFns fns); + +JPC_API void JPC_BroadPhaseLayerFilter_delete(JPC_BroadPhaseLayerFilter* object); + +//////////////////////////////////////////////////////////////////////////////// +// ObjectLayerFilter + +typedef struct JPC_ObjectLayerFilterFns { + bool (*ShouldCollide)(const void *self, JPC_ObjectLayer inLayer); +} JPC_ObjectLayerFilterFns; + +typedef struct JPC_ObjectLayerFilter JPC_ObjectLayerFilter; + +JPC_API JPC_ObjectLayerFilter* JPC_ObjectLayerFilter_new( + const void *self, + JPC_ObjectLayerFilterFns fns); + +JPC_API void JPC_ObjectLayerFilter_delete(JPC_ObjectLayerFilter* object); + +//////////////////////////////////////////////////////////////////////////////// +// BodyFilter + +typedef struct JPC_BodyFilterFns { + bool (*ShouldCollide)(const void *self, JPC_BodyID inBodyID); + bool (*ShouldCollideLocked)(const void *self, const JPC_Body *inBodyID); +} JPC_BodyFilterFns; + +typedef struct JPC_BodyFilter JPC_BodyFilter; + +JPC_API JPC_BodyFilter* JPC_BodyFilter_new( + const void *self, + JPC_BodyFilterFns fns); + +JPC_API void JPC_BodyFilter_delete(JPC_BodyFilter* object); + +//////////////////////////////////////////////////////////////////////////////// +// ObjectVsBroadPhaseLayerFilter + +typedef struct JPC_ObjectVsBroadPhaseLayerFilterFns { + bool (*ShouldCollide)(const void *self, JPC_ObjectLayer inLayer1, JPC_BroadPhaseLayer inLayer2); +} JPC_ObjectVsBroadPhaseLayerFilterFns; + +typedef struct JPC_ObjectVsBroadPhaseLayerFilter JPC_ObjectVsBroadPhaseLayerFilter; + +JPC_API JPC_ObjectVsBroadPhaseLayerFilter* JPC_ObjectVsBroadPhaseLayerFilter_new( + const void *self, + JPC_ObjectVsBroadPhaseLayerFilterFns fns); + +JPC_API void JPC_ObjectVsBroadPhaseLayerFilter_delete(JPC_ObjectVsBroadPhaseLayerFilter* object); + +//////////////////////////////////////////////////////////////////////////////// +// ObjectLayerPairFilter + +typedef struct JPC_ObjectLayerPairFilterFns { + bool (*ShouldCollide)(const void *self, JPC_ObjectLayer inLayer1, JPC_ObjectLayer inLayer2); +} JPC_ObjectLayerPairFilterFns; + +typedef struct JPC_ObjectLayerPairFilter JPC_ObjectLayerPairFilter; + +JPC_API JPC_ObjectLayerPairFilter* JPC_ObjectLayerPairFilter_new( + const void *self, + JPC_ObjectLayerPairFilterFns fns); + +JPC_API void JPC_ObjectLayerPairFilter_delete(JPC_ObjectLayerPairFilter* object); + +//////////////////////////////////////////////////////////////////////////////// +// DrawSettings + +typedef struct JPC_BodyManager_DrawSettings { + bool mDrawGetSupportFunction; + bool mDrawSupportDirection; + bool mDrawGetSupportingFace; + bool mDrawShape; + bool mDrawShapeWireframe; + JPC_ShapeColor mDrawShapeColor; + bool mDrawBoundingBox; + bool mDrawCenterOfMassTransform; + bool mDrawWorldTransform; + bool mDrawVelocity; + bool mDrawMassAndInertia; + bool mDrawSleepStats; + bool mDrawSoftBodyVertices; + bool mDrawSoftBodyVertexVelocities; + bool mDrawSoftBodyEdgeConstraints; + bool mDrawSoftBodyBendConstraints; + bool mDrawSoftBodyVolumeConstraints; + bool mDrawSoftBodySkinConstraints; + bool mDrawSoftBodyLRAConstraints; + bool mDrawSoftBodyPredictedBounds; +} JPC_BodyManager_DrawSettings; + +ENSURE_SIZE_ALIGN(JPC_BodyManager_DrawSettings, JPH::BodyManager::DrawSettings) + +JPC_API void JPC_BodyManager_DrawSettings_default(JPC_BodyManager_DrawSettings* object); + +//////////////////////////////////////////////////////////////////////////////// +// DebugRendererSimple + +typedef struct JPC_DebugRendererSimpleFns { + void (*DrawLine)(const void *self, JPC_RVec3 inFrom, JPC_RVec3 inTo, JPC_Color inColor); +} JPC_DebugRendererSimpleFns; + +typedef struct JPC_DebugRendererSimple JPC_DebugRendererSimple; + +JPC_API JPC_DebugRendererSimple* JPC_DebugRendererSimple_new( + const void *self, + JPC_DebugRendererSimpleFns fns); + +JPC_API void JPC_DebugRendererSimple_delete(JPC_DebugRendererSimple* object); + +//////////////////////////////////////////////////////////////////////////////// +// String + +typedef struct JPC_String JPC_String; + +JPC_API void JPC_String_delete(JPC_String* self); +JPC_API const char* JPC_String_c_str(JPC_String* self); + +//////////////////////////////////////////////////////////////////////////////// +// Shape -> RefTarget + +typedef struct JPC_Shape JPC_Shape; + +JPC_API uint32_t JPC_Shape_GetRefCount(const JPC_Shape* self); +JPC_API void JPC_Shape_AddRef(const JPC_Shape* self); +JPC_API void JPC_Shape_Release(const JPC_Shape* self); + +JPC_API JPC_Vec3 JPC_Shape_GetCenterOfMass(const JPC_Shape* self); + +//////////////////////////////////////////////////////////////////////////////// +// TriangleShapeSettings + +typedef struct JPC_TriangleShapeSettings { + // ShapeSettings + uint64_t UserData; + + // ConvexShapeSettings + // TODO: Material + float Density; + + // TriangleShapeSettings + JPC_Vec3 V1; + JPC_Vec3 V2; + JPC_Vec3 V3; + float ConvexRadius; +} JPC_TriangleShapeSettings; + +JPC_API void JPC_TriangleShapeSettings_default(JPC_TriangleShapeSettings* object); +JPC_API bool JPC_TriangleShapeSettings_Create(const JPC_TriangleShapeSettings* self, JPC_Shape** outShape, JPC_String** outError); + +//////////////////////////////////////////////////////////////////////////////// +// BoxShapeSettings -> ConvexShapeSettings -> ShapeSettings + +typedef struct JPC_BoxShapeSettings { + // ShapeSettings + uint64_t UserData; + + // ConvexShapeSettings + // TODO: Material + float Density; + + // BoxShapeSettings + JPC_Vec3 HalfExtent; + float ConvexRadius; +} JPC_BoxShapeSettings; + +JPC_API void JPC_BoxShapeSettings_default(JPC_BoxShapeSettings* object); +JPC_API bool JPC_BoxShapeSettings_Create(const JPC_BoxShapeSettings* self, JPC_Shape** outShape, JPC_String** outError); + +//////////////////////////////////////////////////////////////////////////////// +// SphereShapeSettings -> ConvexShapeSettings -> ShapeSettings + +typedef struct JPC_SphereShapeSettings { + // ShapeSettings + uint64_t UserData; + + // ConvexShapeSettings + // TODO: Material + float Density; + + // SphereShapeSettings + float Radius; +} JPC_SphereShapeSettings; + +JPC_API void JPC_SphereShapeSettings_default(JPC_SphereShapeSettings* object); +JPC_API bool JPC_SphereShapeSettings_Create(const JPC_SphereShapeSettings* self, JPC_Shape** outShape, JPC_String** outError); + +//////////////////////////////////////////////////////////////////////////////// +// CapsuleShapeSettings -> ConvexShapeSettings -> ShapeSettings + +typedef struct JPC_CapsuleShapeSettings { + // ShapeSettings + uint64_t UserData; + + // ConvexShapeSettings + // TODO: Material + float Density; + + // CapsuleShapeSettings + float Radius; + float HalfHeightOfCylinder; +} JPC_CapsuleShapeSettings; + +JPC_API void JPC_CapsuleShapeSettings_default(JPC_CapsuleShapeSettings* object); +JPC_API bool JPC_CapsuleShapeSettings_Create(const JPC_CapsuleShapeSettings* self, JPC_Shape** outShape, JPC_String** outError); + +//////////////////////////////////////////////////////////////////////////////// +// CylinderShapeSettings -> ConvexShapeSettings -> ShapeSettings + +typedef struct JPC_CylinderShapeSettings { + // ShapeSettings + uint64_t UserData; + + // ConvexShapeSettings + // TODO: Material + float Density; + + // CylinderShapeSettings + float HalfHeight; + float Radius; + float ConvexRadius; +} JPC_CylinderShapeSettings; + +JPC_API void JPC_CylinderShapeSettings_default(JPC_CylinderShapeSettings* object); +JPC_API bool JPC_CylinderShapeSettings_Create(const JPC_CylinderShapeSettings* self, JPC_Shape** outShape, JPC_String** outError); + +//////////////////////////////////////////////////////////////////////////////// +// ConvexHullShapeSettings -> ConvexShapeSettings -> ShapeSettings + +typedef struct JPC_ConvexHullShapeSettings { + // ShapeSettings + uint64_t UserData; + + // ConvexShapeSettings + // TODO: Material + float Density; + + // ConvexHullShapeSettings + const JPC_Vec3* Points; + size_t PointsLen; + float MaxConvexRadius; + float MaxErrorConvexRadius; + float HullTolerance; +} JPC_ConvexHullShapeSettings; + +JPC_API void JPC_ConvexHullShapeSettings_default(JPC_ConvexHullShapeSettings* object); +JPC_API bool JPC_ConvexHullShapeSettings_Create(const JPC_ConvexHullShapeSettings* self, JPC_Shape** outShape, JPC_String** outError); + +//////////////////////////////////////////////////////////////////////////////// +// CompoundShape::SubShapeSettings + +typedef struct JPC_SubShapeSettings { + const JPC_Shape* Shape; + JPC_Vec3 Position; + JPC_Quat Rotation; + uint32_t UserData; +} JPC_SubShapeSettings; + +JPC_API void JPC_SubShapeSettings_default(JPC_SubShapeSettings* object); + +//////////////////////////////////////////////////////////////////////////////// +// StaticCompoundShapeSettings -> CompoundShapeSettings -> ShapeSettings + +typedef struct JPC_StaticCompoundShapeSettings { + // ShapeSettings + uint64_t UserData; + + // CompoundShapeSettings + const JPC_SubShapeSettings* SubShapes; + size_t SubShapesLen; + + // StaticCompoundShapeSettings + // (no fields) +} JPC_StaticCompoundShapeSettings; + +JPC_API void JPC_StaticCompoundShapeSettings_default(JPC_StaticCompoundShapeSettings* object); +JPC_API bool JPC_StaticCompoundShapeSettings_Create(const JPC_StaticCompoundShapeSettings* self, JPC_Shape** outShape, JPC_String** outError); + +//////////////////////////////////////////////////////////////////////////////// +// MutableCompoundShape -> CompoundShapeSettings -> ShapeSettings + +typedef struct JPC_MutableCompoundShapeSettings { + // ShapeSettings + uint64_t UserData; + + // CompoundShapeSettings + const JPC_SubShapeSettings* SubShapes; + size_t SubShapesLen; + + // MutableCompoundShape + // (no fields) +} JPC_MutableCompoundShapeSettings; + +JPC_API void JPC_MutableCompoundShapeSettings_default(JPC_MutableCompoundShapeSettings* object); +JPC_API bool JPC_MutableCompoundShapeSettings_Create(const JPC_MutableCompoundShapeSettings* self, JPC_Shape** outShape, JPC_String** outError); + +//////////////////////////////////////////////////////////////////////////////// +// BodyCreationSettings + +typedef struct JPC_BodyCreationSettings { + JPC_RVec3 Position; + JPC_Quat Rotation; + JPC_Vec3 LinearVelocity; + JPC_Vec3 AngularVelocity; + uint64_t UserData; + JPC_ObjectLayer ObjectLayer; + // CollisionGroup CollisionGroup; + JPC_MotionType MotionType; + JPC_AllowedDOFs AllowedDOFs; + bool AllowDynamicOrKinematic; + bool IsSensor; + bool CollideKinematicVsNonDynamic; + bool UseManifoldReduction; + bool ApplyGyroscopicForce; + JPC_MotionQuality MotionQuality; + bool EnhancedInternalEdgeRemoval; + bool AllowSleeping; + float Friction; + float Restitution; + float LinearDamping; + float AngularDamping; + float MaxLinearVelocity; + float MaxAngularVelocity; + float GravityFactor; + uint NumVelocityStepsOverride; + uint NumPositionStepsOverride; + JPC_OverrideMassProperties OverrideMassProperties; + float InertiaMultiplier; + + // MassProperties MassPropertiesOverride; + + const JPC_Shape* Shape; +} JPC_BodyCreationSettings; + +JPC_API void JPC_BodyCreationSettings_default(JPC_BodyCreationSettings* settings); + +typedef struct JPC_BodyCreationSettings JPC_BodyCreationSettings; + +JPC_API JPC_BodyCreationSettings* JPC_BodyCreationSettings_new(); + +//////////////////////////////////////////////////////////////////////////////// +// Body + +JPC_API JPC_BodyID JPC_Body_GetID(const JPC_Body* self); +JPC_API JPC_BodyType JPC_Body_GetBodyType(const JPC_Body* self); +JPC_API bool JPC_Body_IsRigidBody(const JPC_Body* self); +JPC_API bool JPC_Body_IsSoftBody(const JPC_Body* self); +JPC_API bool JPC_Body_IsActive(const JPC_Body* self); +JPC_API bool JPC_Body_IsStatic(const JPC_Body* self); +JPC_API bool JPC_Body_IsKinematic(const JPC_Body* self); +JPC_API bool JPC_Body_IsDynamic(const JPC_Body* self); +JPC_API bool JPC_Body_CanBeKinematicOrDynamic(const JPC_Body* self); +JPC_API void JPC_Body_SetIsSensor(JPC_Body* self, bool inIsSensor); +JPC_API bool JPC_Body_IsSensor(const JPC_Body* self); +JPC_API void JPC_Body_SetCollideKinematicVsNonDynamic(JPC_Body* self, bool inCollide); +JPC_API bool JPC_Body_GetCollideKinematicVsNonDynamic(const JPC_Body* self); +JPC_API void JPC_Body_SetUseManifoldReduction(JPC_Body* self, bool inUseReduction); +JPC_API bool JPC_Body_GetUseManifoldReduction(const JPC_Body* self); +JPC_API bool JPC_Body_GetUseManifoldReductionWithBody(const JPC_Body* self, const JPC_Body* inBody2); +JPC_API void JPC_Body_SetApplyGyroscopicForce(JPC_Body* self, bool inApply); +JPC_API bool JPC_Body_GetApplyGyroscopicForce(const JPC_Body* self); +JPC_API void JPC_Body_SetEnhancedInternalEdgeRemoval(JPC_Body* self, bool inApply); +JPC_API bool JPC_Body_GetEnhancedInternalEdgeRemoval(const JPC_Body* self); +JPC_API bool JPC_Body_GetEnhancedInternalEdgeRemovalWithBody(const JPC_Body* self, const JPC_Body* inBody2); +JPC_API JPC_MotionType JPC_Body_GetMotionType(const JPC_Body* self); +JPC_API void JPC_Body_SetMotionType(JPC_Body* self, JPC_MotionType inMotionType); +JPC_API JPC_BroadPhaseLayer JPC_Body_GetBroadPhaseLayer(const JPC_Body* self); +JPC_API JPC_ObjectLayer JPC_Body_GetObjectLayer(const JPC_Body* self); + +// JPC_API const CollisionGroup & JPC_Body_GetCollisionGroup(const JPC_Body* self); +// JPC_API CollisionGroup & JPC_Body_GetCollisionGroup(JPC_Body* self); +// JPC_API void JPC_Body_SetCollisionGroup(JPC_Body* self, const CollisionGroup &inGroup); + +JPC_API bool JPC_Body_GetAllowSleeping(const JPC_Body* self); +JPC_API void JPC_Body_SetAllowSleeping(JPC_Body* self, bool inAllow); +JPC_API void JPC_Body_ResetSleepTimer(JPC_Body* self); +JPC_API float JPC_Body_GetFriction(const JPC_Body* self); +JPC_API void JPC_Body_SetFriction(JPC_Body* self, float inFriction); +JPC_API float JPC_Body_GetRestitution(const JPC_Body* self); +JPC_API void JPC_Body_SetRestitution(JPC_Body* self, float inRestitution); +JPC_API JPC_Vec3 JPC_Body_GetLinearVelocity(const JPC_Body* self); +JPC_API void JPC_Body_SetLinearVelocity(JPC_Body* self, JPC_Vec3 inLinearVelocity); +JPC_API void JPC_Body_SetLinearVelocityClamped(JPC_Body* self, JPC_Vec3 inLinearVelocity); +JPC_API JPC_Vec3 JPC_Body_GetAngularVelocity(const JPC_Body* self); +JPC_API void JPC_Body_SetAngularVelocity(JPC_Body* self, JPC_Vec3 inAngularVelocity); +JPC_API void JPC_Body_SetAngularVelocityClamped(JPC_Body* self, JPC_Vec3 inAngularVelocity); +JPC_API JPC_Vec3 JPC_Body_GetPointVelocityCOM(const JPC_Body* self, JPC_Vec3 inPointRelativeToCOM); +JPC_API JPC_Vec3 JPC_Body_GetPointVelocity(const JPC_Body* self, JPC_RVec3 inPoint); +JPC_API void JPC_Body_AddForce(JPC_Body* self, JPC_Vec3 inForce); + +// JPC_API void JPC_Body_AddForce(JPC_Body* self, JPC_Vec3 inForce, JPC_RVec3 inPosition); + +JPC_API void JPC_Body_AddTorque(JPC_Body* self, JPC_Vec3 inTorque); +JPC_API JPC_Vec3 JPC_Body_GetAccumulatedForce(const JPC_Body* self); +JPC_API JPC_Vec3 JPC_Body_GetAccumulatedTorque(const JPC_Body* self); +JPC_API void JPC_Body_ResetForce(JPC_Body* self); +JPC_API void JPC_Body_ResetTorque(JPC_Body* self); +JPC_API void JPC_Body_ResetMotion(JPC_Body* self); +JPC_API void JPC_Body_GetInverseInertia(const JPC_Body* self, JPC_Mat44* outMatrix); +JPC_API void JPC_Body_AddImpulse(JPC_Body* self, JPC_Vec3 inImpulse); +JPC_API void JPC_Body_AddImpulse2(JPC_Body* self, JPC_Vec3 inImpulse, JPC_RVec3 inPosition); +JPC_API void JPC_Body_AddAngularImpulse(JPC_Body* self, JPC_Vec3 inAngularImpulse); +JPC_API void JPC_Body_MoveKinematic(JPC_Body* self, JPC_RVec3 inTargetPosition, JPC_Quat inTargetRotation, float inDeltaTime); +JPC_API bool JPC_Body_ApplyBuoyancyImpulse(JPC_Body* self, JPC_RVec3 inSurfacePosition, JPC_Vec3 inSurfaceNormal, float inBuoyancy, float inLinearDrag, float inAngularDrag, JPC_Vec3 inFluidVelocity, JPC_Vec3 inGravity, float inDeltaTime); +JPC_API bool JPC_Body_IsInBroadPhase(const JPC_Body* self); +JPC_API bool JPC_Body_IsCollisionCacheInvalid(const JPC_Body* self); +JPC_API const JPC_Shape* JPC_Body_GetShape(const JPC_Body* self); +JPC_API JPC_RVec3 JPC_Body_GetPosition(const JPC_Body* self); +JPC_API JPC_Quat JPC_Body_GetRotation(const JPC_Body* self); + +// JPC_API RMat44 JPC_Body_GetWorldTransform(const JPC_Body* self); + +JPC_API JPC_RVec3 JPC_Body_GetCenterOfMassPosition(const JPC_Body* self); + +// JPC_API RMat44 JPC_Body_GetCenterOfMassTransform(const JPC_Body* self); +// JPC_API RMat44 JPC_Body_GetInverseCenterOfMassTransform(const JPC_Body* self); +// JPC_API const AABox & JPC_Body_GetWorldSpaceBounds(const JPC_Body* self); +// JPC_API const MotionProperties *JPC_Body_GetMotionProperties(const JPC_Body* self) +// JPC_API MotionProperties * JPC_Body_GetMotionProperties(JPC_Body* self); +// JPC_API const MotionProperties *JPC_Body_GetMotionPropertiesUnchecked(const JPC_Body* self) +// JPC_API MotionProperties * JPC_Body_GetMotionPropertiesUnchecked(JPC_Body* self); + +JPC_API uint64_t JPC_Body_GetUserData(const JPC_Body* self); +JPC_API void JPC_Body_SetUserData(JPC_Body* self, uint64_t inUserData); + +// JPC_API JPC_Vec3 JPC_Body_GetWorldSpaceSurfaceNormal(const JPC_Body* self, const SubShapeID &inSubShapeID, JPC_RVec3 inPosition); +// JPC_API TransformedShape JPC_Body_GetTransformedShape(const JPC_Body* self); +// JPC_API BodyCreationSettings JPC_Body_GetBodyCreationSettings(const JPC_Body* self); +// JPC_API SoftBodyCreationSettings JPC_Body_GetSoftBodyCreationSettings(const JPC_Body* self); + +//////////////////////////////////////////////////////////////////////////////// +// BodyInterface + +typedef struct JPC_BodyInterface JPC_BodyInterface; + +JPC_API JPC_Body* JPC_BodyInterface_CreateBody(JPC_BodyInterface* self, const JPC_BodyCreationSettings* inSettings); +JPC_API JPC_Body* JPC_BodyInterface_CreateBodyWithID(JPC_BodyInterface *self, JPC_BodyID inBodyID, const JPC_BodyCreationSettings* inSettings); +JPC_API JPC_Body* JPC_BodyInterface_CreateBodyWithoutID(const JPC_BodyInterface *self, const JPC_BodyCreationSettings* inSettings); + +// JPC_API JPC_Body* JPC_BodyInterface_CreateSoftBody(JPC_BodyInterface *self, const SoftBodyCreationSettings &inSettings); +// JPC_API JPC_Body* JPC_BodyInterface_CreateSoftBodyWithID(JPC_BodyInterface *self, JPC_BodyID inBodyID, const SoftBodyCreationSettings* inSettings); +// JPC_API JPC_Body* JPC_BodyInterface_CreateSoftBodyWithoutID(const JPC_BodyInterface *self, const SoftBodyCreationSettings* inSettings); + +JPC_API void JPC_BodyInterface_DestroyBodyWithoutID(const JPC_BodyInterface *self, JPC_Body *inBody); +JPC_API bool JPC_BodyInterface_AssignBodyID(JPC_BodyInterface *self, JPC_Body *ioBody); + +// JPC_API bool JPC_BodyInterface_AssignBodyID(JPC_BodyInterface *self, JPC_Body *ioBody, JPC_BodyID inBodyID); + +JPC_API JPC_Body* JPC_BodyInterface_UnassignBodyID(JPC_BodyInterface *self, JPC_BodyID inBodyID); +JPC_API void JPC_BodyInterface_UnassignBodyIDs(JPC_BodyInterface *self, const JPC_BodyID *inBodyIDs, int inNumber, JPC_Body **outBodies); +JPC_API void JPC_BodyInterface_DestroyBody(JPC_BodyInterface *self, JPC_BodyID inBodyID); +JPC_API void JPC_BodyInterface_DestroyBodies(JPC_BodyInterface *self, const JPC_BodyID *inBodyIDs, int inNumber); +JPC_API void JPC_BodyInterface_AddBody(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_Activation inActivationMode); +JPC_API void JPC_BodyInterface_RemoveBody(JPC_BodyInterface *self, JPC_BodyID inBodyID); +JPC_API bool JPC_BodyInterface_IsAdded(const JPC_BodyInterface *self, JPC_BodyID inBodyID); +JPC_API JPC_BodyID JPC_BodyInterface_CreateAndAddBody(JPC_BodyInterface *self, const JPC_BodyCreationSettings* inSettings, JPC_Activation inActivationMode); + +// JPC_API JPC_BodyID JPC_BodyInterface_CreateAndAddSoftBody(JPC_BodyInterface *self, const SoftBodyCreationSettings &inSettings, JPC_Activation inActivationMode); + +JPC_API void* JPC_BodyInterface_AddBodiesPrepare(JPC_BodyInterface *self, JPC_BodyID *ioBodies, int inNumber); +JPC_API void JPC_BodyInterface_AddBodiesFinalize(JPC_BodyInterface *self, JPC_BodyID *ioBodies, int inNumber, void* inAddState, JPC_Activation inActivationMode); +JPC_API void JPC_BodyInterface_AddBodiesAbort(JPC_BodyInterface *self, JPC_BodyID *ioBodies, int inNumber, void* inAddState); +JPC_API void JPC_BodyInterface_RemoveBodies(JPC_BodyInterface *self, JPC_BodyID *ioBodies, int inNumber); +JPC_API void JPC_BodyInterface_ActivateBody(JPC_BodyInterface *self, JPC_BodyID inBodyID); +JPC_API void JPC_BodyInterface_ActivateBodies(JPC_BodyInterface *self, JPC_BodyID *inBodyIDs, int inNumber); + +// JPC_API void JPC_BodyInterface_ActivateBodiesInAABox(JPC_BodyInterface *self, const AABox &inBox, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter); + +JPC_API void JPC_BodyInterface_DeactivateBody(JPC_BodyInterface *self, JPC_BodyID inBodyID); +JPC_API void JPC_BodyInterface_DeactivateBodies(JPC_BodyInterface *self, JPC_BodyID *inBodyIDs, int inNumber); +JPC_API bool JPC_BodyInterface_IsActive(const JPC_BodyInterface *self, JPC_BodyID inBodyID); + +// TwoBodyConstraint * JPC_BodyInterface_CreateConstraint(JPC_BodyInterface *self, const TwoBodyConstraintSettings *inSettings, JPC_BodyID inBodyID1, JPC_BodyID inBodyID2); +// JPC_API void JPC_BodyInterface_ActivateConstraint(JPC_BodyInterface *self, const TwoBodyConstraint *inConstraint); +// RefConst JPC_BodyInterface_GetShape(const JPC_BodyInterface *self, JPC_BodyID inBodyID); + +JPC_API void JPC_BodyInterface_SetShape(const JPC_BodyInterface *self, JPC_BodyID inBodyID, const JPC_Shape *inShape, bool inUpdateMassProperties, JPC_Activation inActivationMode); +JPC_API void JPC_BodyInterface_NotifyShapeChanged(const JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_Vec3 inPreviousCenterOfMass, bool inUpdateMassProperties, JPC_Activation inActivationMode); +JPC_API void JPC_BodyInterface_SetObjectLayer(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_ObjectLayer inLayer); +JPC_API JPC_ObjectLayer JPC_BodyInterface_GetObjectLayer(const JPC_BodyInterface *self, JPC_BodyID inBodyID); +JPC_API void JPC_BodyInterface_SetPositionAndRotation(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_RVec3 inPosition, JPC_Quat inRotation, JPC_Activation inActivationMode); +JPC_API void JPC_BodyInterface_SetPositionAndRotationWhenChanged(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_RVec3 inPosition, JPC_Quat inRotation, JPC_Activation inActivationMode); +JPC_API void JPC_BodyInterface_GetPositionAndRotation(const JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_RVec3 *outPosition, JPC_Quat *outRotation); +JPC_API void JPC_BodyInterface_SetPosition(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_RVec3 inPosition, JPC_Activation inActivationMode); +JPC_API JPC_RVec3 JPC_BodyInterface_GetPosition(const JPC_BodyInterface *self, JPC_BodyID inBodyID); +JPC_API JPC_RVec3 JPC_BodyInterface_GetCenterOfMassPosition(const JPC_BodyInterface *self, JPC_BodyID inBodyID); +JPC_API void JPC_BodyInterface_SetRotation(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_Quat inRotation, JPC_Activation inActivationMode); +JPC_API JPC_Quat JPC_BodyInterface_GetRotation(const JPC_BodyInterface *self, JPC_BodyID inBodyID); + +// RMat44 JPC_BodyInterface_GetWorldTransform(const JPC_BodyInterface *self, JPC_BodyID inBodyID); +// RMat44 JPC_BodyInterface_GetCenterOfMassTransform(const JPC_BodyInterface *self, JPC_BodyID inBodyID); + +JPC_API void JPC_BodyInterface_MoveKinematic(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_RVec3 inTargetPosition, JPC_Quat inTargetRotation, float inDeltaTime); +JPC_API void JPC_BodyInterface_SetLinearAndAngularVelocity(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_Vec3 inLinearVelocity, JPC_Vec3 inAngularVelocity); +JPC_API void JPC_BodyInterface_GetLinearAndAngularVelocity(const JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_Vec3 *outLinearVelocity, JPC_Vec3 *outAngularVelocity); +JPC_API void JPC_BodyInterface_SetLinearVelocity(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_Vec3 inLinearVelocity); +JPC_API JPC_Vec3 JPC_BodyInterface_GetLinearVelocity(const JPC_BodyInterface *self, JPC_BodyID inBodyID); +JPC_API void JPC_BodyInterface_AddLinearVelocity(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_Vec3 inLinearVelocity); +JPC_API void JPC_BodyInterface_AddLinearAndAngularVelocity(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_Vec3 inLinearVelocity, JPC_Vec3 inAngularVelocity); +JPC_API void JPC_BodyInterface_SetAngularVelocity(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_Vec3 inAngularVelocity); +JPC_API JPC_Vec3 JPC_BodyInterface_GetAngularVelocity(const JPC_BodyInterface *self, JPC_BodyID inBodyID); +JPC_API JPC_Vec3 JPC_BodyInterface_GetPointVelocity(const JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_RVec3 inPoint); +JPC_API void JPC_BodyInterface_SetPositionRotationAndVelocity(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_RVec3 inPosition, JPC_Quat inRotation, JPC_Vec3 inLinearVelocity, JPC_Vec3 inAngularVelocity); +JPC_API void JPC_BodyInterface_AddForce(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_Vec3 inForce); + +// JPC_API void JPC_BodyInterface_AddForce(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_Vec3 inForce, JPC_RVec3 inPoint); + +JPC_API void JPC_BodyInterface_AddTorque(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_Vec3 inTorque); +JPC_API void JPC_BodyInterface_AddForceAndTorque(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_Vec3 inForce, JPC_Vec3 inTorque); +JPC_API void JPC_BodyInterface_AddImpulse(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_Vec3 inImpulse); +JPC_API void JPC_BodyInterface_AddImpulse3(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_Vec3 inImpulse, JPC_RVec3 inPoint); +JPC_API void JPC_BodyInterface_AddAngularImpulse(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_Vec3 inAngularImpulse); +JPC_API JPC_BodyType JPC_BodyInterface_GetBodyType(const JPC_BodyInterface *self, JPC_BodyID inBodyID); +JPC_API void JPC_BodyInterface_SetMotionType(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_MotionType inMotionType, JPC_Activation inActivationMode); +JPC_API JPC_MotionType JPC_BodyInterface_GetMotionType(const JPC_BodyInterface *self, JPC_BodyID inBodyID); +JPC_API void JPC_BodyInterface_SetMotionQuality(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_MotionQuality inMotionQuality); +JPC_API JPC_MotionQuality JPC_BodyInterface_GetMotionQuality(const JPC_BodyInterface *self, JPC_BodyID inBodyID); +JPC_API void JPC_BodyInterface_GetInverseInertia(const JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_Mat44 *outMatrix); +JPC_API void JPC_BodyInterface_SetRestitution(JPC_BodyInterface *self, JPC_BodyID inBodyID, float inRestitution); +JPC_API float JPC_BodyInterface_GetRestitution(const JPC_BodyInterface *self, JPC_BodyID inBodyID); +JPC_API void JPC_BodyInterface_SetFriction(JPC_BodyInterface *self, JPC_BodyID inBodyID, float inFriction); +JPC_API float JPC_BodyInterface_GetFriction(const JPC_BodyInterface *self, JPC_BodyID inBodyID); +JPC_API void JPC_BodyInterface_SetGravityFactor(JPC_BodyInterface *self, JPC_BodyID inBodyID, float inGravityFactor); +JPC_API float JPC_BodyInterface_GetGravityFactor(const JPC_BodyInterface *self, JPC_BodyID inBodyID); +JPC_API void JPC_BodyInterface_SetUseManifoldReduction(JPC_BodyInterface *self, JPC_BodyID inBodyID, bool inUseReduction); +JPC_API bool JPC_BodyInterface_GetUseManifoldReduction(const JPC_BodyInterface *self, JPC_BodyID inBodyID); + +// TransformedShape JPC_BodyInterface_GetTransformedShape(const JPC_BodyInterface *self, JPC_BodyID inBodyID); + +JPC_API uint64_t JPC_BodyInterface_GetUserData(const JPC_BodyInterface *self, JPC_BodyID inBodyID); +JPC_API void JPC_BodyInterface_SetUserData(const JPC_BodyInterface *self, JPC_BodyID inBodyID, uint64_t inUserData); + +// const PhysicsMaterial* JPC_BodyInterface_GetMaterial(const JPC_BodyInterface *self, JPC_BodyID inBodyID, const SubShapeID &inSubShapeID); + +JPC_API void JPC_BodyInterface_InvalidateContactCache(JPC_BodyInterface *self, JPC_BodyID inBodyID); + +//////////////////////////////////////////////////////////////////////////////// +// NarrowPhaseQuery + +typedef struct JPC_NarrowPhaseQuery JPC_NarrowPhaseQuery; + +typedef struct JPC_NarrowPhaseQuery_CastRayArgs { + JPC_RRayCast Ray; + JPC_RayCastResult Result; + const JPC_BroadPhaseLayerFilter *BroadPhaseLayerFilter; + const JPC_ObjectLayerFilter *ObjectLayerFilter; + const JPC_BodyFilter *BodyFilter; +} JPC_NarrowPhaseQuery_CastRayArgs; + +JPC_API bool JPC_NarrowPhaseQuery_CastRay(const JPC_NarrowPhaseQuery* self, JPC_NarrowPhaseQuery_CastRayArgs* args); + +//////////////////////////////////////////////////////////////////////////////// +// PhysicsSystem + +typedef struct JPC_PhysicsSystem JPC_PhysicsSystem; + +JPC_API JPC_PhysicsSystem* JPC_PhysicsSystem_new(); +JPC_API void JPC_PhysicsSystem_delete(JPC_PhysicsSystem* object); +JPC_API void JPC_PhysicsSystem_Init( + JPC_PhysicsSystem* self, + uint inMaxBodies, + uint inNumBodyMutexes, + uint inMaxBodyPairs, + uint inMaxContactConstraints, + JPC_BroadPhaseLayerInterface* inBroadPhaseLayerInterface, + JPC_ObjectVsBroadPhaseLayerFilter* inObjectVsBroadPhaseLayerFilter, + JPC_ObjectLayerPairFilter* inObjectLayerPairFilter); + +JPC_API void JPC_PhysicsSystem_OptimizeBroadPhase(JPC_PhysicsSystem* self); + +JPC_API JPC_PhysicsUpdateError JPC_PhysicsSystem_Update( + JPC_PhysicsSystem* self, + float inDeltaTime, + int inCollisionSteps, + JPC_TempAllocatorImpl *inTempAllocator, // FIXME: un-specialize + JPC_JobSystemThreadPool *inJobSystem); // FIXME: un-specialize + +JPC_API JPC_BodyInterface* JPC_PhysicsSystem_GetBodyInterface(JPC_PhysicsSystem* self); + +JPC_API const JPC_NarrowPhaseQuery* JPC_PhysicsSystem_GetNarrowPhaseQuery(const JPC_PhysicsSystem* self); + +JPC_API void JPC_PhysicsSystem_DrawBodies( + JPC_PhysicsSystem* self, + JPC_BodyManager_DrawSettings* inSettings, + JPC_DebugRendererSimple* inRenderer, // FIXME: un-specialize + const void* inBodyFilter); // FIXME: BodyDrawFilter + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/crates/joltc-sys-patched/JoltC/JoltC/JoltC.cpp b/crates/joltc-sys-patched/JoltC/JoltC/JoltC.cpp new file mode 100644 index 00000000000..925747ea9a7 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltC/JoltC.cpp @@ -0,0 +1,1526 @@ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#define OPAQUE_WRAPPER(c_type, cpp_type) \ + static c_type* to_jpc(cpp_type *in) { return reinterpret_cast(in); } \ + static const c_type* to_jpc(const cpp_type *in) { return reinterpret_cast(in); } \ + static cpp_type* to_jph(c_type *in) { return reinterpret_cast(in); } \ + static const cpp_type* to_jph(const c_type *in) { return reinterpret_cast(in); } \ + static cpp_type** to_jph(c_type **in) { return reinterpret_cast(in); } + +#define DESTRUCTOR(c_type) \ + JPC_API void c_type##_delete(c_type* object) { \ + delete to_jph(object); \ + } + +#define ENUM_CONVERSION(c_type, cpp_type) \ + static c_type to_jpc(cpp_type in) { return static_cast(in); } \ + static cpp_type to_jph(c_type in) { return static_cast(in); } + +#define LAYOUT_COMPATIBLE(c_type, cpp_type) \ + static c_type to_jpc(cpp_type in) { \ + c_type out; \ + memcpy(&out, &in, sizeof(c_type)); \ + return out; \ + } \ + static cpp_type to_jph(c_type in) { \ + cpp_type out; \ + memcpy(&out, &in, sizeof(cpp_type)); \ + return out; \ + } \ + static c_type* to_jpc(cpp_type* in) { \ + return reinterpret_cast(in); \ + } \ + static cpp_type* to_jph(c_type* in) { \ + return reinterpret_cast(in); \ + } \ + static const c_type* to_jpc(const cpp_type* in) { \ + return reinterpret_cast(in); \ + } \ + static const cpp_type* to_jph(const c_type* in) { \ + return reinterpret_cast(in); \ + } \ + static_assert(sizeof(c_type) == sizeof(cpp_type), "size of " #c_type " did not match size of " #cpp_type); \ + static_assert(alignof(c_type) == alignof(cpp_type), "align of " #c_type " did not match align of " #cpp_type); \ + static_assert(!std::is_polymorphic_v, #cpp_type " is polymorphic and cannot be made layout compatible"); + +template +constexpr auto to_integral(E e) -> typename std::underlying_type::type +{ + return static_cast::type>(e); +} + +ENUM_CONVERSION(JPC_MotionType, JPH::EMotionType) +ENUM_CONVERSION(JPC_AllowedDOFs, JPH::EAllowedDOFs) +ENUM_CONVERSION(JPC_Activation, JPH::EActivation) +ENUM_CONVERSION(JPC_BodyType, JPH::EBodyType) +ENUM_CONVERSION(JPC_MotionQuality, JPH::EMotionQuality) +ENUM_CONVERSION(JPC_OverrideMassProperties, JPH::EOverrideMassProperties) + +OPAQUE_WRAPPER(JPC_PhysicsSystem, JPH::PhysicsSystem) +DESTRUCTOR(JPC_PhysicsSystem) + +OPAQUE_WRAPPER(JPC_BodyInterface, JPH::BodyInterface) +OPAQUE_WRAPPER(JPC_NarrowPhaseQuery, JPH::NarrowPhaseQuery) + +OPAQUE_WRAPPER(JPC_TempAllocatorImpl, JPH::TempAllocatorImpl) +DESTRUCTOR(JPC_TempAllocatorImpl) + +OPAQUE_WRAPPER(JPC_JobSystemThreadPool, JPH::JobSystemThreadPool) +DESTRUCTOR(JPC_JobSystemThreadPool) + +OPAQUE_WRAPPER(JPC_Shape, JPH::Shape) +OPAQUE_WRAPPER(JPC_Body, JPH::Body) + +OPAQUE_WRAPPER(JPC_VertexList, JPH::VertexList) +DESTRUCTOR(JPC_VertexList) + +OPAQUE_WRAPPER(JPC_IndexedTriangleList, JPH::IndexedTriangleList) +DESTRUCTOR(JPC_IndexedTriangleList) + +OPAQUE_WRAPPER(JPC_String, JPH::String) +DESTRUCTOR(JPC_String) + +LAYOUT_COMPATIBLE(JPC_BodyManager_DrawSettings, JPH::BodyManager::DrawSettings) + +LAYOUT_COMPATIBLE(JPC_BodyID, JPH::BodyID) + +static auto to_jpc(JPH::BroadPhaseLayer in) { return in.GetValue(); } +static auto to_jph(JPC_BroadPhaseLayer in) { return JPH::BroadPhaseLayer(in); } + +static JPC_Vec3 to_jpc(JPH::Vec3 in) { + return JPC_Vec3{in.GetX(), in.GetY(), in.GetZ(), in.GetZ()}; +} +static JPH::Vec3 to_jph(JPC_Vec3 in) { + return JPH::Vec3(in.x, in.y, in.z); +} + +static JPH::Array to_jph(const JPC_Vec3* src, size_t n) { + JPH::Array vec; + vec.resize(n); + + if (src != nullptr) { + memcpy(vec.data(), src, n * sizeof(*src)); + } + + return vec; +} + +static JPC_DVec3 to_jpc(JPH::DVec3 in) { + return JPC_DVec3{in.GetX(), in.GetY(), in.GetZ(), in.GetZ()}; +} +static JPH::DVec3 to_jph(JPC_DVec3 in) { + return JPH::DVec3(in.x, in.y, in.z); +} + +static JPC_Quat to_jpc(JPH::Quat in) { + return JPC_Quat{in.GetX(), in.GetY(), in.GetZ(), in.GetW()}; +} +static JPH::Quat to_jph(JPC_Quat in) { + return JPH::Quat(in.x, in.y, in.z, in.w); +} + +static JPC_Mat44 to_jpc(JPH::Mat44 in) { + JPC_Mat44 out; + in.StoreFloat4x4(reinterpret_cast(&out)); + return out; +} +static JPH::Mat44 to_jph(JPC_Mat44 in) { + return JPH::Mat44::sLoadFloat4x4Aligned(reinterpret_cast(&in)); +} + +static JPC_Color to_jpc(JPH::Color in) { + return JPC_Color{in.r, in.g, in.b, in.a}; +} +static JPH::Color to_jph(JPC_Color in) { + return JPH::Color(in.r, in.g, in.b, in.a); +} + +static JPH::RayCast to_jph(JPC_RayCast in) { + return JPH::RayCast(to_jph(in.Origin), to_jph(in.Direction)); +} + +static JPH::RRayCast to_jph(JPC_RRayCast in) { + return JPH::RRayCast(to_jph(in.Origin), to_jph(in.Direction)); +} + +static JPC_SubShapeID to_jpc(JPH::SubShapeID in) { + return in.GetValue(); +} + +static JPC_RayCastResult to_jpc(JPH::RayCastResult in) { + JPC_RayCastResult out{0}; + out.BodyID = to_jpc(in.mBodyID); + out.Fraction = in.mFraction; + out.SubShapeID2 = to_jpc(in.mSubShapeID2); + + return out; +} + +JPC_API void JPC_RegisterDefaultAllocator() { + JPH::RegisterDefaultAllocator(); +} + +JPC_API void JPC_FactoryInit() { + JPH::Factory::sInstance = new JPH::Factory(); +} + +JPC_API void JPC_FactoryDelete() { + delete JPH::Factory::sInstance; + JPH::Factory::sInstance = nullptr; +} + +JPC_API void JPC_RegisterTypes() { + JPH::RegisterTypes(); +} + +JPC_API void JPC_UnregisterTypes() { + JPH::UnregisterTypes(); +} + +//////////////////////////////////////////////////////////////////////////////// +// VertexList == Array == std::vector + +JPC_API JPC_VertexList* JPC_VertexList_new(const JPC_Float3* storage, size_t len) { + const JPH::Float3* new_storage = (const JPH::Float3*)storage; + return to_jpc(new JPH::VertexList(new_storage, new_storage + len)); +} + +//////////////////////////////////////////////////////////////////////////////// +// IndexedTriangleList == Array == std::vector + +JPC_API JPC_IndexedTriangleList* JPC_IndexedTriangleList_new(const JPC_IndexedTriangle* storage, size_t len) { + const JPH::IndexedTriangle* new_storage = (const JPH::IndexedTriangle*)storage; + return to_jpc(new JPH::IndexedTriangleList(new_storage, new_storage + len)); +} + +//////////////////////////////////////////////////////////////////////////////// +// TempAllocatorImpl + +JPC_API JPC_TempAllocatorImpl* JPC_TempAllocatorImpl_new(uint size) { + return to_jpc(new JPH::TempAllocatorImpl(size)); +} + +//////////////////////////////////////////////////////////////////////////////// +// JobSystemThreadPool + +JPC_API JPC_JobSystemThreadPool* JPC_JobSystemThreadPool_new2( + uint inMaxJobs, + uint inMaxBarriers) +{ + return to_jpc(new JPH::JobSystemThreadPool(inMaxJobs, inMaxBarriers)); +} + +JPC_API JPC_JobSystemThreadPool* JPC_JobSystemThreadPool_new3( + uint inMaxJobs, + uint inMaxBarriers, + int inNumThreads) +{ + return to_jpc(new JPH::JobSystemThreadPool(inMaxJobs, inMaxBarriers, inNumThreads)); +} + +//////////////////////////////////////////////////////////////////////////////// +// BroadPhaseLayerInterface + +class JPC_BroadPhaseLayerInterfaceBridge final : public JPH::BroadPhaseLayerInterface { +public: + explicit JPC_BroadPhaseLayerInterfaceBridge(const void *self, JPC_BroadPhaseLayerInterfaceFns fns) : self(self), fns(fns) {} + + virtual uint GetNumBroadPhaseLayers() const override { + return fns.GetNumBroadPhaseLayers(self); + } + + virtual JPH::BroadPhaseLayer GetBroadPhaseLayer(JPH::ObjectLayer inLayer) const override { + return to_jph(fns.GetBroadPhaseLayer(self, inLayer)); + } + +#if defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED) + virtual const char * GetBroadPhaseLayerName([[maybe_unused]] JPH::BroadPhaseLayer inLayer) const override { + return "FIXME"; + } +#endif + +private: + const void* self; + JPC_BroadPhaseLayerInterfaceFns fns; +}; + +OPAQUE_WRAPPER(JPC_BroadPhaseLayerInterface, JPC_BroadPhaseLayerInterfaceBridge) +DESTRUCTOR(JPC_BroadPhaseLayerInterface) + +JPC_API JPC_BroadPhaseLayerInterface* JPC_BroadPhaseLayerInterface_new( + const void *self, + JPC_BroadPhaseLayerInterfaceFns fns) +{ + return to_jpc(new JPC_BroadPhaseLayerInterfaceBridge(self, fns)); +} + +//////////////////////////////////////////////////////////////////////////////// +// ObjectVsBroadPhaseLayerFilter + +class JPC_ObjectVsBroadPhaseLayerFilterBridge final : public JPH::ObjectVsBroadPhaseLayerFilter { +public: + explicit JPC_ObjectVsBroadPhaseLayerFilterBridge(const void *self, JPC_ObjectVsBroadPhaseLayerFilterFns fns) : self(self), fns(fns) {} + + virtual bool ShouldCollide(JPH::ObjectLayer inLayer1, JPH::BroadPhaseLayer inLayer2) const override { + return fns.ShouldCollide(self, inLayer1, to_jpc(inLayer2)); + } + +private: + const void* self; + JPC_ObjectVsBroadPhaseLayerFilterFns fns; +}; + +OPAQUE_WRAPPER(JPC_ObjectVsBroadPhaseLayerFilter, JPC_ObjectVsBroadPhaseLayerFilterBridge) +DESTRUCTOR(JPC_ObjectVsBroadPhaseLayerFilter) + +JPC_API JPC_ObjectVsBroadPhaseLayerFilter* JPC_ObjectVsBroadPhaseLayerFilter_new( + const void *self, + JPC_ObjectVsBroadPhaseLayerFilterFns fns) +{ + return to_jpc(new JPC_ObjectVsBroadPhaseLayerFilterBridge(self, fns)); +} + +//////////////////////////////////////////////////////////////////////////////// +// BroadPhaseLayerFilter + +class JPC_BroadPhaseLayerFilterBridge final : public JPH::BroadPhaseLayerFilter { +public: + explicit JPC_BroadPhaseLayerFilterBridge(const void *self, JPC_BroadPhaseLayerFilterFns fns) : self(self), fns(fns) {} + + virtual bool ShouldCollide(JPH::BroadPhaseLayer inLayer) const override { + return fns.ShouldCollide(self, to_jpc(inLayer)); + } + +private: + const void* self; + JPC_BroadPhaseLayerFilterFns fns; +}; + +OPAQUE_WRAPPER(JPC_BroadPhaseLayerFilter, JPC_BroadPhaseLayerFilterBridge) +DESTRUCTOR(JPC_BroadPhaseLayerFilter) + +JPC_API JPC_BroadPhaseLayerFilter* JPC_BroadPhaseLayerFilter_new( + const void *self, + JPC_BroadPhaseLayerFilterFns fns) +{ + return to_jpc(new JPC_BroadPhaseLayerFilterBridge(self, fns)); +} + +//////////////////////////////////////////////////////////////////////////////// +// ObjectLayerFilter + +class JPC_ObjectLayerFilterBridge final : public JPH::ObjectLayerFilter { +public: + explicit JPC_ObjectLayerFilterBridge(const void *self, JPC_ObjectLayerFilterFns fns) : self(self), fns(fns) {} + + virtual bool ShouldCollide(JPH::ObjectLayer inLayer) const override { + return fns.ShouldCollide(self, inLayer); + } + +private: + const void* self; + JPC_ObjectLayerFilterFns fns; +}; + +OPAQUE_WRAPPER(JPC_ObjectLayerFilter, JPC_ObjectLayerFilterBridge) +DESTRUCTOR(JPC_ObjectLayerFilter) + +JPC_API JPC_ObjectLayerFilter* JPC_ObjectLayerFilter_new( + const void *self, + JPC_ObjectLayerFilterFns fns) +{ + return to_jpc(new JPC_ObjectLayerFilterBridge(self, fns)); +} + +//////////////////////////////////////////////////////////////////////////////// +// BodyFilter + +class JPC_BodyFilterBridge final : public JPH::BodyFilter { +public: + explicit JPC_BodyFilterBridge(const void *self, JPC_BodyFilterFns fns) : self(self), fns(fns) {} + + virtual bool ShouldCollide(const JPH::BodyID &inBodyID) const override { + return fns.ShouldCollide(self, to_jpc(inBodyID)); + } + + virtual bool ShouldCollideLocked(const JPH::Body &inBody) const override { + return fns.ShouldCollideLocked(self, to_jpc(&inBody)); + } + +private: + const void* self; + JPC_BodyFilterFns fns; +}; + +OPAQUE_WRAPPER(JPC_BodyFilter, JPC_BodyFilterBridge) +DESTRUCTOR(JPC_BodyFilter) + +JPC_API JPC_BodyFilter* JPC_BodyFilter_new( + const void *self, + JPC_BodyFilterFns fns) +{ + return to_jpc(new JPC_BodyFilterBridge(self, fns)); +} + +//////////////////////////////////////////////////////////////////////////////// +// JPC_ObjectLayerPairFilter + +class JPC_ObjectLayerPairFilterBridge final : public JPH::ObjectLayerPairFilter { +public: + explicit JPC_ObjectLayerPairFilterBridge(const void *self, JPC_ObjectLayerPairFilterFns fns) : self(self), fns(fns) {} + + virtual bool ShouldCollide(JPH::ObjectLayer inLayer1, JPH::ObjectLayer inLayer2) const override { + return fns.ShouldCollide(self, inLayer1, inLayer2); + } + +private: + const void* self; + JPC_ObjectLayerPairFilterFns fns; +}; + +OPAQUE_WRAPPER(JPC_ObjectLayerPairFilter, JPC_ObjectLayerPairFilterBridge) +DESTRUCTOR(JPC_ObjectLayerPairFilter) + +JPC_API JPC_ObjectLayerPairFilter* JPC_ObjectLayerPairFilter_new( + const void *self, + JPC_ObjectLayerPairFilterFns fns) +{ + return to_jpc(new JPC_ObjectLayerPairFilterBridge(self, fns)); +} + +//////////////////////////////////////////////////////////////////////////////// +// BodyManager::DrawSettings + +JPC_API void JPC_BodyManager_DrawSettings_default(JPC_BodyManager_DrawSettings* object) { + *object = to_jpc(JPH::BodyManager::DrawSettings()); +} + +//////////////////////////////////////////////////////////////////////////////// +// DebugRendererSimple + +class JPC_DebugRendererSimpleBridge final : public JPH::DebugRendererSimple { +public: + explicit JPC_DebugRendererSimpleBridge(const void *self, JPC_DebugRendererSimpleFns fns) : self(self), fns(fns) {} + + virtual void DrawLine(JPH::RVec3Arg inFrom, JPH::RVec3Arg inTo, JPH::ColorArg inColor) override { + fns.DrawLine(self, to_jpc(inFrom), to_jpc(inTo), to_jpc(inColor)); + } + + virtual void DrawText3D( + [[maybe_unused]] JPH::RVec3Arg inPosition, + [[maybe_unused]] const std::string_view &inString, + [[maybe_unused]] JPH::ColorArg inColor = JPH::Color::sWhite, + [[maybe_unused]] float inHeight = 0.5f) override + { + // TODO + } + +private: + const void* self; + JPC_DebugRendererSimpleFns fns; +}; + +OPAQUE_WRAPPER(JPC_DebugRendererSimple, JPC_DebugRendererSimpleBridge) +DESTRUCTOR(JPC_DebugRendererSimple) + +JPC_API JPC_DebugRendererSimple* JPC_DebugRendererSimple_new( + const void *self, + JPC_DebugRendererSimpleFns fns) +{ + return to_jpc(new JPC_DebugRendererSimpleBridge(self, fns)); +} + +//////////////////////////////////////////////////////////////////////////////// +// String + +JPC_API const char* JPC_String_c_str(JPC_String* self) { + return to_jph(self)->c_str(); +} + +//////////////////////////////////////////////////////////////////////////////// +// Shape + +JPC_API uint32_t JPC_Shape_GetRefCount(const JPC_Shape* self) { + return to_jph(self)->GetRefCount(); +} + +JPC_API void JPC_Shape_AddRef(const JPC_Shape* self) { + to_jph(self)->AddRef(); +} + +JPC_API void JPC_Shape_Release(const JPC_Shape* self) { + to_jph(self)->Release(); +} + +JPC_API JPC_Vec3 JPC_Shape_GetCenterOfMass(const JPC_Shape* self) { + return to_jpc(to_jph(self)->GetCenterOfMass()); +} + +//////////////////////////////////////////////////////////////////////////////// +// ShapeSettings + +// Unpack a ShapeResult into a bool and two pointers to be friendlier to C. +static bool HandleShapeResult(JPH::ShapeSettings::ShapeResult res, JPC_Shape** outShape, JPC_String** outError) { + if (res.HasError()) { + if (outError != nullptr) { + JPH::String* created = new JPH::String(std::move(res.GetError())); + *outError = to_jpc(created); + } + + return false; + } else { + JPH::Ref shape = res.Get(); + shape->AddRef(); + *outShape = to_jpc((JPH::Shape*)shape); + + return true; + } +} + +//////////////////////////////////////////////////////////////////////////////// +// TriangleShapeSettings + +static void to_jph(const JPC_TriangleShapeSettings* input, JPH::TriangleShapeSettings* output) { + output->mUserData = input->UserData; + + // TODO: Material + output->mDensity = input->Density; + + output->mV1 = to_jph(input->V1); + output->mV2 = to_jph(input->V2); + output->mV3 = to_jph(input->V3); + output->mConvexRadius = input->ConvexRadius; +} + +JPC_API void JPC_TriangleShapeSettings_default(JPC_TriangleShapeSettings* object) { + object->UserData = 0; + + // TODO: Material + object->Density = 1000.0; + + object->V1 = {0}; + object->V2 = {0}; + object->V3 = {0}; + object->ConvexRadius = 0.0; +} + +JPC_API bool JPC_TriangleShapeSettings_Create(const JPC_TriangleShapeSettings* self, JPC_Shape** outShape, JPC_String** outError) { + JPH::TriangleShapeSettings settings; + to_jph(self, &settings); + + return HandleShapeResult(settings.Create(), outShape, outError); +} + +//////////////////////////////////////////////////////////////////////////////// +// BoxShapeSettings + +static void to_jph(const JPC_BoxShapeSettings* input, JPH::BoxShapeSettings* output) { + output->mUserData = input->UserData; + + // TODO: Material + output->mDensity = input->Density; + + output->mHalfExtent = to_jph(input->HalfExtent); + output->mConvexRadius = input->ConvexRadius; +} + +JPC_API void JPC_BoxShapeSettings_default(JPC_BoxShapeSettings* object) { + object->UserData = 0; + + // TODO: Material + object->Density = 1000.0; + + object->HalfExtent = JPC_Vec3{0}; + object->ConvexRadius = 0.0; +} + +JPC_API bool JPC_BoxShapeSettings_Create(const JPC_BoxShapeSettings* self, JPC_Shape** outShape, JPC_String** outError) { + JPH::BoxShapeSettings settings; + to_jph(self, &settings); + + return HandleShapeResult(settings.Create(), outShape, outError); +} + +//////////////////////////////////////////////////////////////////////////////// +// SphereShapeSettings + +static void to_jph(const JPC_SphereShapeSettings* input, JPH::SphereShapeSettings* output) { + output->mUserData = input->UserData; + + // TODO: Material + output->mDensity = input->Density; + + output->mRadius = input->Radius; +} + +JPC_API void JPC_SphereShapeSettings_default(JPC_SphereShapeSettings* object) { + object->UserData = 0; + + // TODO: Material + object->Density = 1000.0; + + object->Radius = 0.0; +} + +JPC_API bool JPC_SphereShapeSettings_Create(const JPC_SphereShapeSettings* self, JPC_Shape** outShape, JPC_String** outError) { + JPH::SphereShapeSettings settings; + to_jph(self, &settings); + + return HandleShapeResult(settings.Create(), outShape, outError); +} + +//////////////////////////////////////////////////////////////////////////////// +// CapsuleShapeSettings + +static void to_jph(const JPC_CapsuleShapeSettings* input, JPH::CapsuleShapeSettings* output) { + output->mUserData = input->UserData; + + // TODO: Material + output->mDensity = input->Density; + + output->mRadius = input->Radius; + output->mHalfHeightOfCylinder = input->HalfHeightOfCylinder; +} + +JPC_API void JPC_CapsuleShapeSettings_default(JPC_CapsuleShapeSettings* object) { + object->UserData = 0; + + // TODO: Material + object->Density = 1000.0; + + object->Radius = 0.0; + object->HalfHeightOfCylinder = 0.0; +} + +JPC_API bool JPC_CapsuleShapeSettings_Create(const JPC_CapsuleShapeSettings* self, JPC_Shape** outShape, JPC_String** outError) { + JPH::CapsuleShapeSettings settings; + to_jph(self, &settings); + + return HandleShapeResult(settings.Create(), outShape, outError); +} + +//////////////////////////////////////////////////////////////////////////////// +// CylinderShapeSettings + +static void to_jph(const JPC_CylinderShapeSettings* input, JPH::CylinderShapeSettings* output) { + output->mUserData = input->UserData; + + // TODO: Material + output->mDensity = input->Density; + + output->mHalfHeight = input->HalfHeight; + output->mRadius = input->Radius; + output->mConvexRadius = input->ConvexRadius; +} + +JPC_API void JPC_CylinderShapeSettings_default(JPC_CylinderShapeSettings* object) { + object->UserData = 0; + + // TODO: Material + object->Density = 1000.0; + + object->HalfHeight = 0.0; + object->Radius = 0.0; + object->ConvexRadius = 0.0; +} + +JPC_API bool JPC_CylinderShapeSettings_Create(const JPC_CylinderShapeSettings* self, JPC_Shape** outShape, JPC_String** outError) { + JPH::CylinderShapeSettings settings; + to_jph(self, &settings); + + return HandleShapeResult(settings.Create(), outShape, outError); +} + +//////////////////////////////////////////////////////////////////////////////// +// ConvexHullShapeSettings + +static void to_jph(const JPC_ConvexHullShapeSettings* input, JPH::ConvexHullShapeSettings* output) { + output->mUserData = input->UserData; + + // TODO: Material + output->mDensity = input->Density; + + output->mPoints = to_jph(input->Points, input->PointsLen); + output->mMaxConvexRadius = input->MaxConvexRadius; + output->mMaxErrorConvexRadius = input->MaxErrorConvexRadius; + output->mHullTolerance = input->HullTolerance; +} + +JPC_API void JPC_ConvexHullShapeSettings_default(JPC_ConvexHullShapeSettings* object) { + object->UserData = 0; + + // TODO: Material + object->Density = 1000.0; + + object->Points = nullptr; + object->PointsLen = 0; + object->MaxConvexRadius = 0.0; + object->MaxErrorConvexRadius = 0.05f; + object->HullTolerance = 1.0e-3f; +} + +JPC_API bool JPC_ConvexHullShapeSettings_Create(const JPC_ConvexHullShapeSettings* self, JPC_Shape** outShape, JPC_String** outError) { + JPH::ConvexHullShapeSettings settings; + to_jph(self, &settings); + + return HandleShapeResult(settings.Create(), outShape, outError); +} + +//////////////////////////////////////////////////////////////////////////////// +// CompoundShape::SubShapeSettings + +static JPH::CompoundShapeSettings::SubShapeSettings to_jph(const JPC_SubShapeSettings* input) { + const JPH::Shape* shape = to_jph(input->Shape); + + JPH::CompoundShapeSettings::SubShapeSettings output; + output.mShape = nullptr; + output.mShapePtr = shape; + output.mPosition = to_jph(input->Position); + output.mRotation = to_jph(input->Rotation); + output.mUserData = input->UserData; + return output; +} + +static JPH::Array to_jph(const JPC_SubShapeSettings* src, size_t n) { + JPH::Array vec; + vec.reserve(n); + + for (size_t i = 0; i < n; i++) { + vec.push_back(to_jph(&src[i])); + } + + return vec; +} + +JPC_API void JPC_SubShapeSettings_default(JPC_SubShapeSettings* object) { + object->Shape = nullptr; + object->Position = JPC_Vec3{0}; + object->Rotation = JPC_Quat{0, 0, 0, 1}; + object->UserData = 0; +} + +//////////////////////////////////////////////////////////////////////////////// +// StaticCompoundShapeSettings -> CompoundShapeSettings -> ShapeSettings + +static void to_jph(const JPC_StaticCompoundShapeSettings* input, JPH::StaticCompoundShapeSettings* output) { + output->mUserData = input->UserData; + + output->mSubShapes = to_jph(input->SubShapes, input->SubShapesLen); +} + +JPC_API void JPC_StaticCompoundShapeSettings_default(JPC_StaticCompoundShapeSettings* object) { + object->UserData = 0; + + object->SubShapes = nullptr; + object->SubShapesLen = 0; +} + +JPC_API bool JPC_StaticCompoundShapeSettings_Create(const JPC_StaticCompoundShapeSettings* self, JPC_Shape** outShape, JPC_String** outError) { + JPH::StaticCompoundShapeSettings settings; + to_jph(self, &settings); + + return HandleShapeResult(settings.Create(), outShape, outError); +} + +//////////////////////////////////////////////////////////////////////////////// +// MutableCompoundShapeSettings -> CompoundShapeSettings -> ShapeSettings + +static void to_jph(const JPC_MutableCompoundShapeSettings* input, JPH::MutableCompoundShapeSettings* output) { + output->mUserData = input->UserData; + + output->mSubShapes = to_jph(input->SubShapes, input->SubShapesLen); +} + +JPC_API void JPC_MutableCompoundShapeSettings_default(JPC_MutableCompoundShapeSettings* object) { + object->UserData = 0; + + object->SubShapes = nullptr; + object->SubShapesLen = 0; +} + +JPC_API bool JPC_MutableCompoundShapeSettings_Create(const JPC_MutableCompoundShapeSettings* self, JPC_Shape** outShape, JPC_String** outError) { + JPH::MutableCompoundShapeSettings settings; + to_jph(self, &settings); + + return HandleShapeResult(settings.Create(), outShape, outError); +} + +//////////////////////////////////////////////////////////////////////////////// +// BodyCreationSettings + +static JPH::BodyCreationSettings to_jph(const JPC_BodyCreationSettings* settings) { + JPH::BodyCreationSettings output{}; + + output.mPosition = to_jph(settings->Position); + output.mRotation = to_jph(settings->Rotation); + output.mLinearVelocity = to_jph(settings->LinearVelocity); + output.mAngularVelocity = to_jph(settings->AngularVelocity); + output.mUserData = settings->UserData; + output.mObjectLayer = settings->ObjectLayer; + // CollisionGroup + output.mMotionType = to_jph(settings->MotionType); + output.mAllowedDOFs = to_jph(settings->AllowedDOFs); + output.mAllowDynamicOrKinematic = settings->AllowDynamicOrKinematic; + output.mIsSensor = settings->IsSensor; + output.mCollideKinematicVsNonDynamic = settings->CollideKinematicVsNonDynamic; + output.mUseManifoldReduction = settings->UseManifoldReduction; + output.mApplyGyroscopicForce = settings->ApplyGyroscopicForce; + output.mMotionQuality = to_jph(settings->MotionQuality); + output.mEnhancedInternalEdgeRemoval = settings->EnhancedInternalEdgeRemoval; + output.mAllowSleeping = settings->AllowSleeping; + output.mFriction = settings->Friction; + output.mRestitution = settings->Restitution; + output.mLinearDamping = settings->LinearDamping; + output.mAngularDamping = settings->AngularDamping; + output.mMaxLinearVelocity = settings->MaxLinearVelocity; + output.mMaxAngularVelocity = settings->MaxAngularVelocity; + output.mGravityFactor = settings->GravityFactor; + output.mNumVelocityStepsOverride = settings->NumVelocityStepsOverride; + output.mNumPositionStepsOverride = settings->NumPositionStepsOverride; + output.mOverrideMassProperties = to_jph(settings->OverrideMassProperties); + output.mInertiaMultiplier = settings->InertiaMultiplier; + // output.mMassPropertiesOverride = settings->MassPropertiesOverride; + output.SetShape(to_jph(settings->Shape)); + + return output; +} + +JPC_API void JPC_BodyCreationSettings_default(JPC_BodyCreationSettings* settings) { + settings->Position = JPC_RVec3{0, 0, 0}; + settings->Rotation = JPC_Quat{0, 0, 0, 1}; + settings->LinearVelocity = JPC_Vec3{0, 0, 0}; + settings->AngularVelocity = JPC_Vec3{0, 0, 0}; + settings->UserData = 0; + settings->ObjectLayer = 0; + // CollisionGroup + settings->MotionType = JPC_MOTION_TYPE_DYNAMIC; + settings->AllowedDOFs = JPC_ALLOWED_DOFS_ALL; + settings->AllowDynamicOrKinematic = false; + settings->IsSensor = false; + settings->CollideKinematicVsNonDynamic = false; + settings->UseManifoldReduction = true; + settings->ApplyGyroscopicForce = false; + settings->MotionQuality = JPC_MOTION_QUALITY_DISCRETE; + settings->EnhancedInternalEdgeRemoval = false; + settings->AllowSleeping = true; + settings->Friction = 0.2f; + settings->Restitution = 0.0f; + settings->LinearDamping = 0.05f; + settings->AngularDamping = 0.05f; + settings->MaxLinearVelocity = 500.0f; + settings->MaxAngularVelocity = 0.25f * JPC_PI * 60.0f; + settings->GravityFactor = 1.0f; + settings->NumVelocityStepsOverride = 0; + settings->NumPositionStepsOverride = 0; + settings->OverrideMassProperties = JPC_OVERRIDE_MASS_PROPS_CALC_MASS_INERTIA; + settings->InertiaMultiplier = 1.0f; + // MassPropertiesOverride +} + +//////////////////////////////////////////////////////////////////////////////// +// Body + +JPC_API JPC_BodyID JPC_Body_GetID(const JPC_Body* self) { + return to_jpc(to_jph(self)->GetID()); +} + +JPC_API JPC_BodyType JPC_Body_GetBodyType(const JPC_Body* self) { + return to_jpc(to_jph(self)->GetBodyType()); +} + +JPC_API bool JPC_Body_IsRigidBody(const JPC_Body* self) { + return to_jph(self)->IsRigidBody(); +} + +JPC_API bool JPC_Body_IsSoftBody(const JPC_Body* self) { + return to_jph(self)->IsSoftBody(); +} + +JPC_API bool JPC_Body_IsActive(const JPC_Body* self) { + return to_jph(self)->IsActive(); +} + +JPC_API bool JPC_Body_IsStatic(const JPC_Body* self) { + return to_jph(self)->IsStatic(); +} + +JPC_API bool JPC_Body_IsKinematic(const JPC_Body* self) { + return to_jph(self)->IsKinematic(); +} + +JPC_API bool JPC_Body_IsDynamic(const JPC_Body* self) { + return to_jph(self)->IsDynamic(); +} + +JPC_API bool JPC_Body_CanBeKinematicOrDynamic(const JPC_Body* self) { + return to_jph(self)->CanBeKinematicOrDynamic(); +} + +JPC_API void JPC_Body_SetIsSensor(JPC_Body* self, bool inIsSensor) { + to_jph(self)->SetIsSensor(inIsSensor); +} + +JPC_API bool JPC_Body_IsSensor(const JPC_Body* self) { + return to_jph(self)->IsSensor(); +} + +JPC_API void JPC_Body_SetCollideKinematicVsNonDynamic(JPC_Body* self, bool inCollide) { + to_jph(self)->SetCollideKinematicVsNonDynamic(inCollide); +} + +JPC_API bool JPC_Body_GetCollideKinematicVsNonDynamic(const JPC_Body* self) { + return to_jph(self)->GetCollideKinematicVsNonDynamic(); +} + +JPC_API void JPC_Body_SetUseManifoldReduction(JPC_Body* self, bool inUseReduction) { + to_jph(self)->SetUseManifoldReduction(inUseReduction); +} + +JPC_API bool JPC_Body_GetUseManifoldReduction(const JPC_Body* self) { + return to_jph(self)->GetUseManifoldReduction(); +} + +JPC_API bool JPC_Body_GetUseManifoldReductionWithBody(const JPC_Body* self, const JPC_Body* inBody2) { + return to_jph(self)->GetUseManifoldReductionWithBody(*to_jph(inBody2)); +} + +JPC_API void JPC_Body_SetApplyGyroscopicForce(JPC_Body* self, bool inApply) { + to_jph(self)->SetApplyGyroscopicForce(inApply); +} + +JPC_API bool JPC_Body_GetApplyGyroscopicForce(const JPC_Body* self) { + return to_jph(self)->GetApplyGyroscopicForce(); +} + +JPC_API void JPC_Body_SetEnhancedInternalEdgeRemoval(JPC_Body* self, bool inApply) { + to_jph(self)->SetEnhancedInternalEdgeRemoval(inApply); +} + +JPC_API bool JPC_Body_GetEnhancedInternalEdgeRemoval(const JPC_Body* self) { + return to_jph(self)->GetEnhancedInternalEdgeRemoval(); +} + +JPC_API bool JPC_Body_GetEnhancedInternalEdgeRemovalWithBody(const JPC_Body* self, const JPC_Body* inBody2) { + return to_jph(self)->GetEnhancedInternalEdgeRemovalWithBody(*to_jph(inBody2)); +} + +JPC_API JPC_MotionType JPC_Body_GetMotionType(const JPC_Body* self) { + return to_jpc(to_jph(self)->GetMotionType()); +} + +JPC_API void JPC_Body_SetMotionType(JPC_Body* self, JPC_MotionType inMotionType) { + to_jph(self)->SetMotionType(to_jph(inMotionType)); +} + +JPC_API JPC_BroadPhaseLayer JPC_Body_GetBroadPhaseLayer(const JPC_Body* self) { + return to_jpc(to_jph(self)->GetBroadPhaseLayer()); +} + +JPC_API JPC_ObjectLayer JPC_Body_GetObjectLayer(const JPC_Body* self) { + return to_jph(self)->GetObjectLayer(); +} + +// JPC_API const CollisionGroup & JPC_Body_GetCollisionGroup(const JPC_Body* self); +// JPC_API CollisionGroup & JPC_Body_GetCollisionGroup(JPC_Body* self); +// JPC_API void JPC_Body_SetCollisionGroup(JPC_Body* self, const CollisionGroup &inGroup); + +JPC_API bool JPC_Body_GetAllowSleeping(const JPC_Body* self) { + return to_jph(self)->GetAllowSleeping(); +} + +JPC_API void JPC_Body_SetAllowSleeping(JPC_Body* self, bool inAllow) { + to_jph(self)->SetAllowSleeping(inAllow); +} + +JPC_API void JPC_Body_ResetSleepTimer(JPC_Body* self) { + to_jph(self)->ResetSleepTimer(); +} + +JPC_API float JPC_Body_GetFriction(const JPC_Body* self) { + return to_jph(self)->GetFriction(); +} + +JPC_API void JPC_Body_SetFriction(JPC_Body* self, float inFriction) { + to_jph(self)->SetFriction(inFriction); +} + +JPC_API float JPC_Body_GetRestitution(const JPC_Body* self) { + return to_jph(self)->GetRestitution(); +} + +JPC_API void JPC_Body_SetRestitution(JPC_Body* self, float inRestitution) { + to_jph(self)->SetRestitution(inRestitution); +} + +JPC_API JPC_Vec3 JPC_Body_GetLinearVelocity(const JPC_Body* self) { + return to_jpc(to_jph(self)->GetLinearVelocity()); +} + +JPC_API void JPC_Body_SetLinearVelocity(JPC_Body* self, JPC_Vec3 inLinearVelocity) { + to_jph(self)->SetLinearVelocity(to_jph(inLinearVelocity)); +} + +JPC_API void JPC_Body_SetLinearVelocityClamped(JPC_Body* self, JPC_Vec3 inLinearVelocity) { + to_jph(self)->SetLinearVelocityClamped(to_jph(inLinearVelocity)); +} + +JPC_API JPC_Vec3 JPC_Body_GetAngularVelocity(const JPC_Body* self) { + return to_jpc(to_jph(self)->GetAngularVelocity()); +} + +JPC_API void JPC_Body_SetAngularVelocity(JPC_Body* self, JPC_Vec3 inAngularVelocity) { + to_jph(self)->SetAngularVelocity(to_jph(inAngularVelocity)); +} + +JPC_API void JPC_Body_SetAngularVelocityClamped(JPC_Body* self, JPC_Vec3 inAngularVelocity) { + to_jph(self)->SetAngularVelocityClamped(to_jph(inAngularVelocity)); +} + +JPC_API JPC_Vec3 JPC_Body_GetPointVelocityCOM(const JPC_Body* self, JPC_Vec3 inPointRelativeToCOM) { + return to_jpc(to_jph(self)->GetPointVelocityCOM(to_jph(inPointRelativeToCOM))); +} + +JPC_API JPC_Vec3 JPC_Body_GetPointVelocity(const JPC_Body* self, JPC_RVec3 inPoint) { + return to_jpc(to_jph(self)->GetPointVelocity(to_jph(inPoint))); +} + +// JPC_API void JPC_Body_AddForce(JPC_Body* self, JPC_Vec3 inForce); +// JPC_API void JPC_Body_AddForce(JPC_Body* self, JPC_Vec3 inForce, JPC_RVec3 inPosition); + +JPC_API void JPC_Body_AddTorque(JPC_Body* self, JPC_Vec3 inTorque) { + to_jph(self)->AddTorque(to_jph(inTorque)); +} + +JPC_API JPC_Vec3 JPC_Body_GetAccumulatedForce(const JPC_Body* self) { + return to_jpc(to_jph(self)->GetAccumulatedForce()); +} + +JPC_API JPC_Vec3 JPC_Body_GetAccumulatedTorque(const JPC_Body* self) { + return to_jpc(to_jph(self)->GetAccumulatedTorque()); +} + +JPC_API void JPC_Body_ResetForce(JPC_Body* self) { + to_jph(self)->ResetForce(); +} + +JPC_API void JPC_Body_ResetTorque(JPC_Body* self) { + to_jph(self)->ResetTorque(); +} + +JPC_API void JPC_Body_ResetMotion(JPC_Body* self) { + to_jph(self)->ResetMotion(); +} + +JPC_API void JPC_Body_GetInverseInertia(const JPC_Body* self, JPC_Mat44* outMatrix) { + to_jph(self)->GetInverseInertia().StoreFloat4x4(reinterpret_cast(outMatrix)); +} + +JPC_API void JPC_Body_AddImpulse(JPC_Body* self, JPC_Vec3 inImpulse) { + to_jph(self)->AddImpulse(to_jph(inImpulse)); +} + +JPC_API void JPC_Body_AddImpulse2(JPC_Body* self, JPC_Vec3 inImpulse, JPC_RVec3 inPosition) { + to_jph(self)->AddImpulse(to_jph(inImpulse), to_jph(inPosition)); +} + +JPC_API void JPC_Body_AddAngularImpulse(JPC_Body* self, JPC_Vec3 inAngularImpulse) { + to_jph(self)->AddAngularImpulse(to_jph(inAngularImpulse)); +} + +JPC_API void JPC_Body_MoveKinematic(JPC_Body* self, JPC_RVec3 inTargetPosition, JPC_Quat inTargetRotation, float inDeltaTime) { + to_jph(self)->MoveKinematic(to_jph(inTargetPosition), to_jph(inTargetRotation), inDeltaTime); +} + +JPC_API bool JPC_Body_ApplyBuoyancyImpulse(JPC_Body* self, JPC_RVec3 inSurfacePosition, JPC_Vec3 inSurfaceNormal, float inBuoyancy, float inLinearDrag, float inAngularDrag, JPC_Vec3 inFluidVelocity, JPC_Vec3 inGravity, float inDeltaTime) { + return to_jph(self)->ApplyBuoyancyImpulse(to_jph(inSurfacePosition), to_jph(inSurfaceNormal), inBuoyancy, inLinearDrag, inAngularDrag, to_jph(inFluidVelocity), to_jph(inGravity), inDeltaTime); +} + +JPC_API bool JPC_Body_IsInBroadPhase(const JPC_Body* self) { + return to_jph(self)->IsInBroadPhase(); +} + +JPC_API bool JPC_Body_IsCollisionCacheInvalid(const JPC_Body* self) { + return to_jph(self)->IsCollisionCacheInvalid(); +} + +JPC_API const JPC_Shape* JPC_Body_GetShape(const JPC_Body* self) { + return to_jpc(to_jph(self)->GetShape()); +} + +JPC_API JPC_RVec3 JPC_Body_GetPosition(const JPC_Body* self) { + return to_jpc(to_jph(self)->GetPosition()); +} + +JPC_API JPC_Quat JPC_Body_GetRotation(const JPC_Body* self) { + return to_jpc(to_jph(self)->GetRotation()); +} + +// JPC_API RMat44 JPC_Body_GetWorldTransform(const JPC_Body* self); + +JPC_API JPC_RVec3 JPC_Body_GetCenterOfMassPosition(const JPC_Body* self) { + return to_jpc(to_jph(self)->GetCenterOfMassPosition()); +} + +// JPC_API RMat44 JPC_Body_GetCenterOfMassTransform(const JPC_Body* self); +// JPC_API RMat44 JPC_Body_GetInverseCenterOfMassTransform(const JPC_Body* self); +// JPC_API const AABox & JPC_Body_GetWorldSpaceBounds(const JPC_Body* self); +// JPC_API const MotionProperties *JPC_Body_GetMotionProperties(const JPC_Body* self) +// JPC_API MotionProperties * JPC_Body_GetMotionProperties(JPC_Body* self); +// JPC_API const MotionProperties *JPC_Body_GetMotionPropertiesUnchecked(const JPC_Body* self) +// JPC_API MotionProperties * JPC_Body_GetMotionPropertiesUnchecked(JPC_Body* self); + +JPC_API uint64_t JPC_Body_GetUserData(const JPC_Body* self) { + return to_jph(self)->GetUserData(); +} + +JPC_API void JPC_Body_SetUserData(JPC_Body* self, uint64_t inUserData) { + to_jph(self)->SetUserData(inUserData); +} + +// JPC_API JPC_Vec3 JPC_Body_GetWorldSpaceSurfaceNormal(const JPC_Body* self, const SubShapeID &inSubShapeID, JPC_RVec3 inPosition); +// JPC_API TransformedShape JPC_Body_GetTransformedShape(const JPC_Body* self); +// JPC_API BodyCreationSettings JPC_Body_GetBodyCreationSettings(const JPC_Body* self); +// JPC_API SoftBodyCreationSettings JPC_Body_GetSoftBodyCreationSettings(const JPC_Body* self); + +//////////////////////////////////////////////////////////////////////////////// +// BodyInterface + +JPC_API JPC_Body* JPC_BodyInterface_CreateBody(JPC_BodyInterface* self, const JPC_BodyCreationSettings* inSettings) { + return to_jpc(to_jph(self)->CreateBody(to_jph(inSettings))); +} + +// JPC_API JPC_Body* JPC_BodyInterface_CreateSoftBody(JPC_BodyInterface *self, const SoftBodyCreationSettings &inSettings); + +JPC_API JPC_Body* JPC_BodyInterface_CreateBodyWithID(JPC_BodyInterface *self, JPC_BodyID inBodyID, const JPC_BodyCreationSettings* inSettings) { + return to_jpc(to_jph(self)->CreateBodyWithID(to_jph(inBodyID), to_jph(inSettings))); +} + +// JPC_API JPC_Body* JPC_BodyInterface_CreateSoftBodyWithID(JPC_BodyInterface *self, JPC_BodyID inBodyID, const SoftBodyCreationSettings* inSettings); + +JPC_API JPC_Body* JPC_BodyInterface_CreateBodyWithoutID(const JPC_BodyInterface *self, const JPC_BodyCreationSettings* inSettings) { + return to_jpc(to_jph(self)->CreateBodyWithoutID(to_jph(inSettings))); +} + +// JPC_API JPC_Body* JPC_BodyInterface_CreateSoftBodyWithoutID(const JPC_BodyInterface *self, const SoftBodyCreationSettings* inSettings); + +JPC_API void JPC_BodyInterface_DestroyBodyWithoutID(const JPC_BodyInterface *self, JPC_Body *inBody) { + to_jph(self)->DestroyBodyWithoutID(to_jph(inBody)); +} + +JPC_API bool JPC_BodyInterface_AssignBodyID(JPC_BodyInterface *self, JPC_Body *ioBody) { + return to_jph(self)->AssignBodyID(to_jph(ioBody)); +} + +// JPC_API bool JPC_BodyInterface_AssignBodyID(JPC_BodyInterface *self, JPC_Body *ioBody, JPC_BodyID inBodyID); + +JPC_API JPC_Body* JPC_BodyInterface_UnassignBodyID(JPC_BodyInterface *self, JPC_BodyID inBodyID) { + return to_jpc(to_jph(self)->UnassignBodyID(to_jph(inBodyID))); +} + +// JPC_API void JPC_BodyInterface_UnassignBodyIDs(JPC_BodyInterface *self, const JPC_BodyID *inBodyIDs, int inNumber, JPC_Body **outBodies) { +// return to_jph(self)->UnassignBodyIDs(to_jph(inBodyIDs), inNumber, to_jph(outBodies)); +// } + +JPC_API void JPC_BodyInterface_DestroyBody(JPC_BodyInterface *self, JPC_BodyID inBodyID) { + to_jph(self)->DestroyBody(to_jph(inBodyID)); +} + +// JPC_API void JPC_BodyInterface_DestroyBodies(JPC_BodyInterface *self, const JPC_BodyID *inBodyIDs, int inNumber) { +// return to_jph(self)->DestroyBodies(to_jph(inBodyIDs), int inNumber); +// } + +JPC_API void JPC_BodyInterface_AddBody(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_Activation inActivationMode) { + to_jph(self)->AddBody(to_jph(inBodyID), to_jph(inActivationMode)); +} + +JPC_API void JPC_BodyInterface_RemoveBody(JPC_BodyInterface *self, JPC_BodyID inBodyID) { + to_jph(self)->RemoveBody(to_jph(inBodyID)); +} + +JPC_API bool JPC_BodyInterface_IsAdded(const JPC_BodyInterface *self, JPC_BodyID inBodyID) { + return to_jph(self)->IsAdded(to_jph(inBodyID)); +} + +JPC_API JPC_BodyID JPC_BodyInterface_CreateAndAddBody(JPC_BodyInterface *self, const JPC_BodyCreationSettings* inSettings, JPC_Activation inActivationMode) { + return to_jpc(to_jph(self)->CreateAndAddBody(to_jph(inSettings), to_jph(inActivationMode))); +} + +// JPC_API JPC_BodyID JPC_BodyInterface_CreateAndAddSoftBody(JPC_BodyInterface *self, const SoftBodyCreationSettings &inSettings, JPC_Activation inActivationMode); + +JPC_API void* JPC_BodyInterface_AddBodiesPrepare(JPC_BodyInterface *self, JPC_BodyID *ioBodies, int inNumber) { + return to_jph(self)->AddBodiesPrepare(to_jph(ioBodies), inNumber); +} + +JPC_API void JPC_BodyInterface_AddBodiesFinalize(JPC_BodyInterface *self, JPC_BodyID *ioBodies, int inNumber, void* inAddState, JPC_Activation inActivationMode) { + to_jph(self)->AddBodiesFinalize(to_jph(ioBodies), inNumber, inAddState, to_jph(inActivationMode)); +} + +JPC_API void JPC_BodyInterface_AddBodiesAbort(JPC_BodyInterface *self, JPC_BodyID *ioBodies, int inNumber, void* inAddState) { + to_jph(self)->AddBodiesAbort(to_jph(ioBodies), inNumber, inAddState); +} + +JPC_API void JPC_BodyInterface_RemoveBodies(JPC_BodyInterface *self, JPC_BodyID *ioBodies, int inNumber) { + to_jph(self)->RemoveBodies(to_jph(ioBodies), inNumber); +} + +JPC_API void JPC_BodyInterface_ActivateBody(JPC_BodyInterface *self, JPC_BodyID inBodyID) { + to_jph(self)->ActivateBody(to_jph(inBodyID)); +} + +JPC_API void JPC_BodyInterface_ActivateBodies(JPC_BodyInterface *self, JPC_BodyID *inBodyIDs, int inNumber) { + to_jph(self)->ActivateBodies(to_jph(inBodyIDs), inNumber); +} + +// JPC_API void JPC_BodyInterface_ActivateBodiesInAABox(JPC_BodyInterface *self, const AABox &inBox, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter); + +JPC_API void JPC_BodyInterface_DeactivateBody(JPC_BodyInterface *self, JPC_BodyID inBodyID) { + to_jph(self)->DeactivateBody(to_jph(inBodyID)); +} + +JPC_API void JPC_BodyInterface_DeactivateBodies(JPC_BodyInterface *self, JPC_BodyID *inBodyIDs, int inNumber) { + to_jph(self)->DeactivateBodies(to_jph(inBodyIDs), inNumber); +} + +JPC_API bool JPC_BodyInterface_IsActive(const JPC_BodyInterface *self, JPC_BodyID inBodyID) { + return to_jph(self)->IsActive(to_jph(inBodyID)); +} + +// TwoBodyConstraint * JPC_BodyInterface_CreateConstraint(JPC_BodyInterface *self, const TwoBodyConstraintSettings *inSettings, JPC_BodyID inBodyID1, JPC_BodyID inBodyID2); +// JPC_API void JPC_BodyInterface_ActivateConstraint(JPC_BodyInterface *self, const TwoBodyConstraint *inConstraint); +// RefConst JPC_BodyInterface_GetShape(const JPC_BodyInterface *self, JPC_BodyID inBodyID); + +JPC_API void JPC_BodyInterface_SetShape(const JPC_BodyInterface *self, JPC_BodyID inBodyID, const JPC_Shape *inShape, bool inUpdateMassProperties, JPC_Activation inActivationMode) { + to_jph(self)->SetShape(to_jph(inBodyID), to_jph(inShape), inUpdateMassProperties, to_jph(inActivationMode)); +} + +JPC_API void JPC_BodyInterface_NotifyShapeChanged(const JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_Vec3 inPreviousCenterOfMass, bool inUpdateMassProperties, JPC_Activation inActivationMode) { + to_jph(self)->NotifyShapeChanged(to_jph(inBodyID), to_jph(inPreviousCenterOfMass), inUpdateMassProperties, to_jph(inActivationMode)); +} + +JPC_API void JPC_BodyInterface_SetObjectLayer(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_ObjectLayer inLayer) { + to_jph(self)->SetObjectLayer(to_jph(inBodyID), inLayer); +} + +JPC_API JPC_ObjectLayer JPC_BodyInterface_GetObjectLayer(const JPC_BodyInterface *self, JPC_BodyID inBodyID) { + return to_jph(self)->GetObjectLayer(to_jph(inBodyID)); +} + +JPC_API void JPC_BodyInterface_SetPositionAndRotation(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_RVec3 inPosition, JPC_Quat inRotation, JPC_Activation inActivationMode) { + to_jph(self)->SetPositionAndRotation(to_jph(inBodyID), to_jph(inPosition), to_jph(inRotation), to_jph(inActivationMode)); +} + +JPC_API void JPC_BodyInterface_SetPositionAndRotationWhenChanged(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_RVec3 inPosition, JPC_Quat inRotation, JPC_Activation inActivationMode) { + to_jph(self)->SetPositionAndRotationWhenChanged(to_jph(inBodyID), to_jph(inPosition), to_jph(inRotation), to_jph(inActivationMode)); +} + +JPC_API void JPC_BodyInterface_GetPositionAndRotation(const JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_RVec3 *outPosition, JPC_Quat *outRotation) { + JPH::RVec3 outPos{}; + JPH::Quat outRot{}; + + to_jph(self)->GetPositionAndRotation(to_jph(inBodyID), outPos, outRot); + + *outPosition = to_jpc(outPos); + *outRotation = to_jpc(outRot); +} + +JPC_API void JPC_BodyInterface_SetPosition(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_RVec3 inPosition, JPC_Activation inActivationMode) { + to_jph(self)->SetPosition(to_jph(inBodyID), to_jph(inPosition), to_jph(inActivationMode)); +} + +JPC_API JPC_RVec3 JPC_BodyInterface_GetPosition(const JPC_BodyInterface *self, JPC_BodyID inBodyID) { + return to_jpc(to_jph(self)->GetPosition(to_jph(inBodyID))); +} + +JPC_API JPC_RVec3 JPC_BodyInterface_GetCenterOfMassPosition(const JPC_BodyInterface *self, JPC_BodyID inBodyID) { + return to_jpc(to_jph(self)->GetCenterOfMassPosition(to_jph(inBodyID))); +} + +JPC_API void JPC_BodyInterface_SetRotation(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_Quat inRotation, JPC_Activation inActivationMode) { + to_jph(self)->SetRotation(to_jph(inBodyID), to_jph(inRotation), to_jph(inActivationMode)); +} + +JPC_API JPC_Quat JPC_BodyInterface_GetRotation(const JPC_BodyInterface *self, JPC_BodyID inBodyID) { + return to_jpc(to_jph(self)->GetRotation(to_jph(inBodyID))); +} + +// RMat44 JPC_BodyInterface_GetWorldTransform(const JPC_BodyInterface *self, JPC_BodyID inBodyID); +// RMat44 JPC_BodyInterface_GetCenterOfMassTransform(const JPC_BodyInterface *self, JPC_BodyID inBodyID); + +JPC_API void JPC_BodyInterface_MoveKinematic(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_RVec3 inTargetPosition, JPC_Quat inTargetRotation, float inDeltaTime) { + to_jph(self)->MoveKinematic(to_jph(inBodyID), to_jph(inTargetPosition), to_jph(inTargetRotation), inDeltaTime); +} + +JPC_API void JPC_BodyInterface_SetLinearAndAngularVelocity(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_Vec3 inLinearVelocity, JPC_Vec3 inAngularVelocity) { + to_jph(self)->SetLinearAndAngularVelocity(to_jph(inBodyID), to_jph(inLinearVelocity), to_jph(inAngularVelocity)); +} + +JPC_API void JPC_BodyInterface_GetLinearAndAngularVelocity(const JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_Vec3 *outLinearVelocity, JPC_Vec3 *outAngularVelocity) { + JPH::Vec3 outLinVel; + JPH::Vec3 outAngVel; + + to_jph(self)->GetLinearAndAngularVelocity(to_jph(inBodyID), outLinVel, outAngVel); + + *outLinearVelocity = to_jpc(outLinVel); + *outAngularVelocity = to_jpc(outAngVel); +} + +JPC_API void JPC_BodyInterface_SetLinearVelocity(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_Vec3 inLinearVelocity) { + to_jph(self)->SetLinearVelocity(to_jph(inBodyID), to_jph(inLinearVelocity)); +} + +JPC_API JPC_Vec3 JPC_BodyInterface_GetLinearVelocity(const JPC_BodyInterface *self, JPC_BodyID inBodyID) { + return to_jpc(to_jph(self)->GetLinearVelocity(to_jph(inBodyID))); +} + +JPC_API void JPC_BodyInterface_AddLinearVelocity(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_Vec3 inLinearVelocity) { + to_jph(self)->AddLinearVelocity(to_jph(inBodyID), to_jph(inLinearVelocity)); +} + +JPC_API void JPC_BodyInterface_AddLinearAndAngularVelocity(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_Vec3 inLinearVelocity, JPC_Vec3 inAngularVelocity) { + to_jph(self)->AddLinearAndAngularVelocity(to_jph(inBodyID), to_jph(inLinearVelocity), to_jph(inAngularVelocity)); +} + +JPC_API void JPC_BodyInterface_SetAngularVelocity(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_Vec3 inAngularVelocity) { + to_jph(self)->SetAngularVelocity(to_jph(inBodyID), to_jph(inAngularVelocity)); +} + +JPC_API JPC_Vec3 JPC_BodyInterface_GetAngularVelocity(const JPC_BodyInterface *self, JPC_BodyID inBodyID) { + return to_jpc(to_jph(self)->GetAngularVelocity(to_jph(inBodyID))); +} + +JPC_API JPC_Vec3 JPC_BodyInterface_GetPointVelocity(const JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_RVec3 inPoint) { + return to_jpc(to_jph(self)->GetPointVelocity(to_jph(inBodyID), to_jph(inPoint))); +} + +JPC_API void JPC_BodyInterface_SetPositionRotationAndVelocity(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_RVec3 inPosition, JPC_Quat inRotation, JPC_Vec3 inLinearVelocity, JPC_Vec3 inAngularVelocity) { + to_jph(self)->SetPositionRotationAndVelocity(to_jph(inBodyID), to_jph(inPosition), to_jph(inRotation), to_jph(inLinearVelocity), to_jph(inAngularVelocity)); +} + +JPC_API void JPC_BodyInterface_AddForce(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_Vec3 inForce) { + to_jph(self)->AddForce(to_jph(inBodyID), to_jph(inForce)); +} + +// JPC_API void JPC_BodyInterface_AddForce(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_Vec3 inForce, JPC_RVec3 inPoint); + +JPC_API void JPC_BodyInterface_AddTorque(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_Vec3 inTorque) { + to_jph(self)->AddTorque(to_jph(inBodyID), to_jph(inTorque)); +} + +JPC_API void JPC_BodyInterface_AddForceAndTorque(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_Vec3 inForce, JPC_Vec3 inTorque) { + to_jph(self)->AddForceAndTorque(to_jph(inBodyID), to_jph(inForce), to_jph(inTorque)); +} + +JPC_API void JPC_BodyInterface_AddImpulse(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_Vec3 inImpulse) { + to_jph(self)->AddImpulse(to_jph(inBodyID), to_jph(inImpulse)); +} + +JPC_API void JPC_BodyInterface_AddImpulse3(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_Vec3 inImpulse, JPC_RVec3 inPoint) { + to_jph(self)->AddImpulse(to_jph(inBodyID), to_jph(inImpulse), to_jph(inPoint)); +} + +JPC_API void JPC_BodyInterface_AddAngularImpulse(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_Vec3 inAngularImpulse) { + to_jph(self)->AddAngularImpulse(to_jph(inBodyID), to_jph(inAngularImpulse)); +} + +JPC_API JPC_BodyType JPC_BodyInterface_GetBodyType(const JPC_BodyInterface *self, JPC_BodyID inBodyID) { + return to_jpc(to_jph(self)->GetBodyType(to_jph(inBodyID))); +} + +JPC_API void JPC_BodyInterface_SetMotionType(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_MotionType inMotionType, JPC_Activation inActivationMode) { + to_jph(self)->SetMotionType(to_jph(inBodyID), to_jph(inMotionType), to_jph(inActivationMode)); +} + +JPC_API JPC_MotionType JPC_BodyInterface_GetMotionType(const JPC_BodyInterface *self, JPC_BodyID inBodyID) { + return to_jpc(to_jph(self)->GetMotionType(to_jph(inBodyID))); +} + +JPC_API void JPC_BodyInterface_SetMotionQuality(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_MotionQuality inMotionQuality) { + to_jph(self)->SetMotionQuality(to_jph(inBodyID), to_jph(inMotionQuality)); +} + +JPC_API JPC_MotionQuality JPC_BodyInterface_GetMotionQuality(const JPC_BodyInterface *self, JPC_BodyID inBodyID) { + return to_jpc(to_jph(self)->GetMotionQuality(to_jph(inBodyID))); +} + +JPC_API void JPC_BodyInterface_GetInverseInertia(const JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_Mat44 *outMatrix) { + to_jph(self)->GetInverseInertia(to_jph(inBodyID)).StoreFloat4x4(reinterpret_cast(outMatrix)); +} + +JPC_API void JPC_BodyInterface_SetRestitution(JPC_BodyInterface *self, JPC_BodyID inBodyID, float inRestitution) { + to_jph(self)->SetRestitution(to_jph(inBodyID), inRestitution); +} + +JPC_API float JPC_BodyInterface_GetRestitution(const JPC_BodyInterface *self, JPC_BodyID inBodyID) { + return to_jph(self)->GetRestitution(to_jph(inBodyID)); +} + +JPC_API void JPC_BodyInterface_SetFriction(JPC_BodyInterface *self, JPC_BodyID inBodyID, float inFriction) { + to_jph(self)->SetFriction(to_jph(inBodyID), inFriction); +} + +JPC_API float JPC_BodyInterface_GetFriction(const JPC_BodyInterface *self, JPC_BodyID inBodyID) { + return to_jph(self)->GetFriction(to_jph(inBodyID)); +} + +JPC_API void JPC_BodyInterface_SetGravityFactor(JPC_BodyInterface *self, JPC_BodyID inBodyID, float inGravityFactor) { + to_jph(self)->SetGravityFactor(to_jph(inBodyID), inGravityFactor); +} + +JPC_API float JPC_BodyInterface_GetGravityFactor(const JPC_BodyInterface *self, JPC_BodyID inBodyID) { + return to_jph(self)->GetGravityFactor(to_jph(inBodyID)); +} + +JPC_API void JPC_BodyInterface_SetUseManifoldReduction(JPC_BodyInterface *self, JPC_BodyID inBodyID, bool inUseReduction) { + to_jph(self)->SetUseManifoldReduction(to_jph(inBodyID), inUseReduction); +} + +JPC_API bool JPC_BodyInterface_GetUseManifoldReduction(const JPC_BodyInterface *self, JPC_BodyID inBodyID) { + return to_jph(self)->GetUseManifoldReduction(to_jph(inBodyID)); +} + +// TransformedShape JPC_BodyInterface_GetTransformedShape(const JPC_BodyInterface *self, JPC_BodyID inBodyID); + +JPC_API uint64_t JPC_BodyInterface_GetUserData(const JPC_BodyInterface *self, JPC_BodyID inBodyID) { + return to_jph(self)->GetUserData(to_jph(inBodyID)); +} + +JPC_API void JPC_BodyInterface_SetUserData(const JPC_BodyInterface *self, JPC_BodyID inBodyID, uint64_t inUserData) { + to_jph(self)->SetUserData(to_jph(inBodyID), inUserData); +} + +// const PhysicsMaterial* JPC_BodyInterface_GetMaterial(const JPC_BodyInterface *self, JPC_BodyID inBodyID, const SubShapeID &inSubShapeID); + +JPC_API void JPC_BodyInterface_InvalidateContactCache(JPC_BodyInterface *self, JPC_BodyID inBodyID) { + to_jph(self)->InvalidateContactCache(to_jph(inBodyID)); +} + +//////////////////////////////////////////////////////////////////////////////// +// NarrowPhaseQuery + +JPC_API bool JPC_NarrowPhaseQuery_CastRay(const JPC_NarrowPhaseQuery* self, JPC_NarrowPhaseQuery_CastRayArgs* args) { + JPH::RayCastResult result; + + JPH::BroadPhaseLayerFilter defaultBplFilter{}; + const JPH::BroadPhaseLayerFilter* bplFilter = &defaultBplFilter; + if (args->BroadPhaseLayerFilter != nullptr) { + bplFilter = to_jph(args->BroadPhaseLayerFilter); + } + + JPH::ObjectLayerFilter defaultOlFilter{}; + const JPH::ObjectLayerFilter* olFilter = &defaultOlFilter; + if (args->ObjectLayerFilter != nullptr) { + olFilter = to_jph(args->ObjectLayerFilter); + } + + JPH::BodyFilter defaultBodyFilter{}; + const JPH::BodyFilter* bodyFilter = &defaultBodyFilter; + if (args->BodyFilter != nullptr) { + bodyFilter = to_jph(args->BodyFilter); + } + + bool hit = to_jph(self)->CastRay( + to_jph(args->Ray), + result, + *bplFilter, + *olFilter, + *bodyFilter + ); + + if (hit) { + args->Result = to_jpc(result); + } + + return hit; +} + +//////////////////////////////////////////////////////////////////////////////// +// PhysicsSystem + +JPC_API JPC_PhysicsSystem* JPC_PhysicsSystem_new() { + return to_jpc(new JPH::PhysicsSystem()); +} + +JPC_API void JPC_PhysicsSystem_Init( + JPC_PhysicsSystem* self, + uint inMaxBodies, + uint inNumBodyMutexes, + uint inMaxBodyPairs, + uint inMaxContactConstraints, + JPC_BroadPhaseLayerInterface* inBroadPhaseLayerInterface, + JPC_ObjectVsBroadPhaseLayerFilter* inObjectVsBroadPhaseLayerFilter, + JPC_ObjectLayerPairFilter* inObjectLayerPairFilter) +{ + JPC_BroadPhaseLayerInterfaceBridge* impl_inBroadPhaseLayerInterface = to_jph(inBroadPhaseLayerInterface); + JPC_ObjectVsBroadPhaseLayerFilterBridge* impl_inObjectVsBroadPhaseLayerFilter = to_jph(inObjectVsBroadPhaseLayerFilter); + JPC_ObjectLayerPairFilterBridge* impl_inObjectLayerPairFilter = to_jph(inObjectLayerPairFilter); + + to_jph(self)->Init( + inMaxBodies, + inNumBodyMutexes, + inMaxBodyPairs, + inMaxContactConstraints, + *impl_inBroadPhaseLayerInterface, + *impl_inObjectVsBroadPhaseLayerFilter, + *impl_inObjectLayerPairFilter); +} + +JPC_API void JPC_PhysicsSystem_OptimizeBroadPhase(JPC_PhysicsSystem* self) { + to_jph(self)->OptimizeBroadPhase(); +} + +JPC_API JPC_BodyInterface* JPC_PhysicsSystem_GetBodyInterface(JPC_PhysicsSystem* self) { + return to_jpc(&to_jph(self)->GetBodyInterface()); +} + +JPC_API const JPC_NarrowPhaseQuery* JPC_PhysicsSystem_GetNarrowPhaseQuery(const JPC_PhysicsSystem* self) { + return to_jpc(&to_jph(self)->GetNarrowPhaseQuery()); +} + +JPC_API JPC_PhysicsUpdateError JPC_PhysicsSystem_Update( + JPC_PhysicsSystem* self, + float inDeltaTime, + int inCollisionSteps, + JPC_TempAllocatorImpl *inTempAllocator, + JPC_JobSystemThreadPool *inJobSystem) +{ + auto res = to_jph(self)->Update( + inDeltaTime, + inCollisionSteps, + to_jph(inTempAllocator), + to_jph(inJobSystem)); + + return to_integral(res); +} + +JPC_API void JPC_PhysicsSystem_DrawBodies( + JPC_PhysicsSystem* self, + JPC_BodyManager_DrawSettings* inSettings, + JPC_DebugRendererSimple* inRenderer, + [[maybe_unused]] const void* inBodyFilter) +{ + to_jph(self)->DrawBodies(to_jph(*inSettings), to_jph(inRenderer), nullptr); +} \ No newline at end of file diff --git a/crates/joltc-sys-patched/JoltC/JoltC/JoltC.h b/crates/joltc-sys-patched/JoltC/JoltC/JoltC.h new file mode 100644 index 00000000000..d7515a6950f --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltC/JoltC.h @@ -0,0 +1,12 @@ +#pragma once + +#ifndef ENSURE_TESTS + #define ENSURE_EQUAL(a, b) + #define ENSURE_ENUM_EQ(a, b) + #define ENSURE_SIZE_ALIGN(a, b) + #define ENSURE_FIELD(a, b, c, d) +#endif + +#include "JoltC/Enums.h" +#include "JoltC/Functions.h" +#include "JoltC/Vehicle.h" \ No newline at end of file diff --git a/crates/joltc-sys-patched/JoltC/JoltC/Test.cpp b/crates/joltc-sys-patched/JoltC/JoltC/Test.cpp new file mode 100644 index 00000000000..070daae1ef0 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltC/Test.cpp @@ -0,0 +1,50 @@ +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +template +constexpr auto to_integral(E e) -> typename std::underlying_type::type +{ + return static_cast::type>(e); +} + +#define ENSURE_TESTS + +#define ENSURE_EQUAL(c_const, cpp_const) \ + static_assert(c_const == cpp_const, #c_const " did not match " #cpp_const); \ + static_assert(std::is_same_v, "type of " #c_const " did not match type of " #cpp_const); + +#define ENSURE_ENUM_EQ(c_const, cpp_enum) \ + static_assert(c_const == to_integral(cpp_enum), #c_const " did not match " #cpp_enum); \ + static_assert(sizeof(c_const) == sizeof(cpp_enum), #c_const " did not have same size as " #cpp_enum); + +// Ensures that a C struct we define is compatible in layout with its +// corresponding C++ type, and that the C++ type is safe to represent in C. +#define ENSURE_SIZE_ALIGN(c_type, cpp_type) \ + static_assert(sizeof(c_type) == sizeof(cpp_type), "size of " #c_type " did not match size of " #cpp_type); \ + static_assert(alignof(c_type) == alignof(cpp_type), "align of " #c_type " did not match align of " #cpp_type); \ + static_assert(!std::is_polymorphic_v, #cpp_type " is polymorphic and cannot be mirrored"); + +#define unsafe_offsetof(st, m) ((size_t) ( (char *)&((st *)(0))->m - (char *)0 )) +#define unsafe_fieldtype(st, m) decltype((st *)(0)->m) + +#define ENSURE_FIELD(type0, field0, type1, field1) \ + static_assert(unsafe_offsetof(type0, field0) == unsafe_offsetof(type1, field1), \ + #type0 "." #field0 " did not have same offset as " #type1 "." #field1); \ + ENSURE_SIZE_ALIGN(unsafe_fieldtype(type0, field0), unsafe_fieldtype(type1, field1)); + +#include "JoltC/JoltC.h" \ No newline at end of file diff --git a/crates/joltc-sys-patched/JoltC/JoltC/Vehicle.cpp b/crates/joltc-sys-patched/JoltC/JoltC/Vehicle.cpp new file mode 100644 index 00000000000..e67ec9d5119 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltC/Vehicle.cpp @@ -0,0 +1,375 @@ +// Vehicle.cpp — High-level vehicle API wrapping Jolt's WheeledVehicleController. +// +// Internally manages VehicleConstraint lifetimes and maps vehicle IDs to them. +// All vehicle creation complexity (wheels, engine, transmission, differential, +// collision tester, step listener) is handled here behind a flat C API. + +// Jolt C++ headers first (same pattern as JoltC.cpp) +#include +#include +#include +#include +#include +#include +#include +#include + +// JoltC header after Jolt — defines ENSURE_SIZE_ALIGN as empty +#include + +#include +#include +#include + +using namespace JPH; + +// Object layers — must match what physics.rs uses +static constexpr ObjectLayer LAYER_NON_MOVING = 0; +static constexpr ObjectLayer LAYER_MOVING = 1; + +//////////////////////////////////////////////////////////////////////////////// +// Internal vehicle tracking + +struct VehicleData { + VehicleConstraint* constraint; + BodyID body_id; + Ref collision_tester; +}; + +static std::mutex g_vehicle_mutex; +static std::unordered_map g_vehicles; +static uint32_t g_next_vehicle_id = 1; + +// Convert opaque JPC_PhysicsSystem* to JPH::PhysicsSystem* +static PhysicsSystem* to_system(JPC_PhysicsSystem* s) { + return reinterpret_cast(s); +} + +//////////////////////////////////////////////////////////////////////////////// +// Default parameters + +JPC_API void JPC_VehicleParams_default(JPC_VehicleParams* p) { + // Chassis: sedan-like box + p->chassis_half_x = 0.9f; + p->chassis_half_y = 0.3f; + p->chassis_half_z = 2.0f; + p->chassis_mass = 1500.0f; + + // Front wheels + p->front_wheel_x = 0.85f; + p->front_wheel_y = -0.3f; + p->front_wheel_z = 1.3f; + + // Rear wheels + p->rear_wheel_x = 0.85f; + p->rear_wheel_y = -0.3f; + p->rear_wheel_z = -1.3f; + + // Wheel geometry + p->wheel_radius = 0.3f; + p->wheel_width = 0.1f; + + // Suspension + p->suspension_min_length = 0.3f; + p->suspension_max_length = 0.5f; + p->suspension_frequency = 1.5f; + p->suspension_damping = 0.5f; + + // Steering + p->max_steer_angle_deg = 30.0f; + + // Brakes + p->front_brake_torque = 1500.0f; + p->rear_brake_torque = 1000.0f; + p->rear_handbrake_torque = 4000.0f; + + // Engine + p->engine_max_torque = 500.0f; + p->engine_min_rpm = 1000.0f; + p->engine_max_rpm = 6000.0f; + + // Transmission (5-speed) + p->num_gears = 5; + p->gear_ratios[0] = 2.66f; + p->gear_ratios[1] = 1.78f; + p->gear_ratios[2] = 1.30f; + p->gear_ratios[3] = 1.00f; + p->gear_ratios[4] = 0.74f; + p->gear_ratios[5] = 0.0f; + p->reverse_gear_ratio = -2.90f; + p->shift_up_rpm = 4000.0f; + p->shift_down_rpm = 2000.0f; + + // Differential + p->differential_ratio = 3.42f; + p->drive_type = 0; // AWD +} + +//////////////////////////////////////////////////////////////////////////////// +// Create vehicle + +JPC_API uint32_t JPC_PhysicsSystem_CreateVehicle( + JPC_PhysicsSystem* system, + const JPC_VehicleParams* p, + float pos_x, float pos_y, float pos_z, + float rot_x, float rot_y, float rot_z, float rot_w) +{ + PhysicsSystem* ps = to_system(system); + BodyInterface& bi = ps->GetBodyInterface(); + + // --- 1. Create chassis body --- + BoxShapeSettings chassis_shape(Vec3(p->chassis_half_x, p->chassis_half_y, p->chassis_half_z)); + + // Offset center of mass downward for stability + OffsetCenterOfMassShapeSettings com_shape( + Vec3(0, -p->chassis_half_y, 0), + chassis_shape.Create().Get() + ); + + BodyCreationSettings body_settings( + com_shape.Create().Get(), + RVec3(pos_x, pos_y, pos_z), + Quat(rot_x, rot_y, rot_z, rot_w), + EMotionType::Dynamic, + LAYER_MOVING + ); + body_settings.mOverrideMassProperties = EOverrideMassProperties::CalculateInertia; + body_settings.mMassPropertiesOverride.mMass = p->chassis_mass; + + Body* body = bi.CreateBody(body_settings); + if (!body) + return 0; + + bi.AddBody(body->GetID(), EActivation::Activate); + + // --- 2. Configure vehicle constraint --- + VehicleConstraintSettings vehicle_settings; + vehicle_settings.mUp = Vec3::sAxisY(); + vehicle_settings.mForward = Vec3::sAxisZ(); + + // Max pitch/roll angle to prevent flipping + vehicle_settings.mMaxPitchRollAngle = DegreesToRadians(60.0f); + + // --- 3. Create 4 wheels (FL=0, FR=1, RL=2, RR=3) --- + float steer_rad = DegreesToRadians(p->max_steer_angle_deg); + + auto make_wheel = [&](float x, float y, float z, bool is_front) -> Ref { + WheelSettingsWV* w = new WheelSettingsWV; + w->mPosition = Vec3(x, y, z); + w->mSuspensionDirection = Vec3(0, -1, 0); + w->mSteeringAxis = Vec3(0, 1, 0); + w->mWheelUp = Vec3(0, 1, 0); + w->mWheelForward = Vec3(0, 0, 1); + w->mRadius = p->wheel_radius; + w->mWidth = p->wheel_width; + w->mSuspensionMinLength = p->suspension_min_length; + w->mSuspensionMaxLength = p->suspension_max_length; + w->mSuspensionSpring.mFrequency = p->suspension_frequency; + w->mSuspensionSpring.mDamping = p->suspension_damping; + w->mMaxSteerAngle = is_front ? steer_rad : 0.0f; + w->mMaxBrakeTorque = is_front ? p->front_brake_torque : p->rear_brake_torque; + w->mMaxHandBrakeTorque = is_front ? 0.0f : p->rear_handbrake_torque; + return w; + }; + + // FL, FR, RL, RR + vehicle_settings.mWheels.resize(4); + vehicle_settings.mWheels[0] = make_wheel(-p->front_wheel_x, p->front_wheel_y, p->front_wheel_z, true); + vehicle_settings.mWheels[1] = make_wheel( p->front_wheel_x, p->front_wheel_y, p->front_wheel_z, true); + vehicle_settings.mWheels[2] = make_wheel(-p->rear_wheel_x, p->rear_wheel_y, p->rear_wheel_z, false); + vehicle_settings.mWheels[3] = make_wheel( p->rear_wheel_x, p->rear_wheel_y, p->rear_wheel_z, false); + + // --- 4. Controller (engine + transmission + differentials) --- + WheeledVehicleControllerSettings* controller = new WheeledVehicleControllerSettings; + + // Engine + controller->mEngine.mMaxTorque = p->engine_max_torque; + controller->mEngine.mMinRPM = p->engine_min_rpm; + controller->mEngine.mMaxRPM = p->engine_max_rpm; + + // Transmission (auto) + controller->mTransmission.mShiftUpRPM = p->shift_up_rpm; + controller->mTransmission.mShiftDownRPM = p->shift_down_rpm; + controller->mTransmission.mGearRatios.clear(); + for (int i = 0; i < p->num_gears && i < 6; i++) { + if (p->gear_ratios[i] != 0.0f) + controller->mTransmission.mGearRatios.push_back(p->gear_ratios[i]); + } + controller->mTransmission.mReverseGearRatios.clear(); + controller->mTransmission.mReverseGearRatios.push_back(p->reverse_gear_ratio); + + // Differentials based on drive type + controller->mDifferentials.clear(); + if (p->drive_type == 0 || p->drive_type == 1) { + // FWD or AWD: front differential + VehicleDifferentialSettings front_diff; + front_diff.mLeftWheel = 0; + front_diff.mRightWheel = 1; + front_diff.mDifferentialRatio = p->differential_ratio; + front_diff.mEngineTorqueRatio = (p->drive_type == 0) ? 0.5f : 1.0f; + controller->mDifferentials.push_back(front_diff); + } + if (p->drive_type == 0 || p->drive_type == 2) { + // RWD or AWD: rear differential + VehicleDifferentialSettings rear_diff; + rear_diff.mLeftWheel = 2; + rear_diff.mRightWheel = 3; + rear_diff.mDifferentialRatio = p->differential_ratio; + rear_diff.mEngineTorqueRatio = (p->drive_type == 0) ? 0.5f : 1.0f; + controller->mDifferentials.push_back(rear_diff); + } + + vehicle_settings.mController = controller; + + // --- 5. Create constraint and register --- + VehicleConstraint* constraint = new VehicleConstraint(*body, vehicle_settings); + + // Collision tester: use MOVING layer so the broadphase filter allows hitting NON_MOVING ground + // (our filter says MOVING collides with everything, NON_MOVING only collides with MOVING) + Ref tester = new VehicleCollisionTesterRay(LAYER_MOVING); + constraint->SetVehicleCollisionTester(tester); + + // Add constraint to physics system + ps->AddConstraint(constraint); + + // Add as step listener (VehicleConstraint implements PhysicsStepListener) + ps->AddStepListener(constraint); + + // --- 6. Register internally --- + std::lock_guard lock(g_vehicle_mutex); + uint32_t vid = g_next_vehicle_id++; + + g_vehicles[vid] = VehicleData{ + constraint, + body->GetID(), + tester, + }; + + return vid; +} + +//////////////////////////////////////////////////////////////////////////////// +// Set driver input + +JPC_API void JPC_PhysicsSystem_SetVehicleInput( + JPC_PhysicsSystem* system, + uint32_t vehicle_id, + float forward, float right, float brake, float handbrake) +{ + PhysicsSystem* ps = to_system(system); + std::lock_guard lock(g_vehicle_mutex); + auto it = g_vehicles.find(vehicle_id); + if (it == g_vehicles.end()) + return; + + VehicleData& vd = it->second; + + WheeledVehicleController* controller = + static_cast(vd.constraint->GetController()); + controller->SetDriverInput(forward, right, brake, handbrake); + + // Activate the body so Jolt processes the input (sleeping bodies don't move) + if (forward != 0.0f || right != 0.0f || brake != 0.0f || handbrake != 0.0f) { + ps->GetBodyInterface().ActivateBody(vd.body_id); + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Get vehicle state + +JPC_API bool JPC_PhysicsSystem_GetVehicleState( + JPC_PhysicsSystem* system, + uint32_t vehicle_id, + JPC_VehicleState* out) +{ + PhysicsSystem* ps = to_system(system); + std::lock_guard lock(g_vehicle_mutex); + + auto it = g_vehicles.find(vehicle_id); + if (it == g_vehicles.end()) + return false; + + VehicleData& vd = it->second; + BodyInterface& bi = ps->GetBodyInterface(); + + // Body transform + RVec3 pos = bi.GetCenterOfMassPosition(vd.body_id); + Quat rot = bi.GetRotation(vd.body_id); + Vec3 vel = bi.GetLinearVelocity(vd.body_id); + + out->pos_x = (float)pos.GetX(); + out->pos_y = (float)pos.GetY(); + out->pos_z = (float)pos.GetZ(); + out->rot_x = rot.GetX(); + out->rot_y = rot.GetY(); + out->rot_z = rot.GetZ(); + out->rot_w = rot.GetW(); + out->vel_x = vel.GetX(); + out->vel_y = vel.GetY(); + out->vel_z = vel.GetZ(); + + // Speed in km/h (forward component of velocity) + Vec3 forward = rot * Vec3::sAxisZ(); + float forward_speed = vel.Dot(forward); + out->speed_kmh = forward_speed * 3.6f; + + // Engine/transmission + WheeledVehicleController* ctrl = + static_cast(vd.constraint->GetController()); + out->engine_rpm = ctrl->GetEngine().GetCurrentRPM(); + out->current_gear = ctrl->GetTransmission().GetCurrentGear(); + + // Wheel states + Vec3 wheel_right(1, 0, 0); + Vec3 wheel_up(0, 1, 0); + + for (int i = 0; i < 4; i++) { + Wheel* wheel = vd.constraint->GetWheel(i); + RMat44 wt = vd.constraint->GetWheelWorldTransform(i, wheel_right, wheel_up); + + RVec3 wpos = wt.GetTranslation(); + Quat wrot = wt.GetRotation().GetQuaternion(); + + out->wheels[i].pos_x = (float)wpos.GetX(); + out->wheels[i].pos_y = (float)wpos.GetY(); + out->wheels[i].pos_z = (float)wpos.GetZ(); + out->wheels[i].rot_x = wrot.GetX(); + out->wheels[i].rot_y = wrot.GetY(); + out->wheels[i].rot_z = wrot.GetZ(); + out->wheels[i].rot_w = wrot.GetW(); + out->wheels[i].steer_angle = wheel->GetSteerAngle(); + out->wheels[i].rotation_angle = wheel->GetRotationAngle(); + out->wheels[i].angular_velocity = wheel->GetAngularVelocity(); + out->wheels[i].suspension_length = wheel->GetSuspensionLength(); + out->wheels[i].has_contact = wheel->HasContact(); + } + + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// Destroy vehicle + +JPC_API void JPC_PhysicsSystem_DestroyVehicle( + JPC_PhysicsSystem* system, + uint32_t vehicle_id) +{ + PhysicsSystem* ps = to_system(system); + std::lock_guard lock(g_vehicle_mutex); + + auto it = g_vehicles.find(vehicle_id); + if (it == g_vehicles.end()) + return; + + VehicleData& vd = it->second; + + // Remove step listener first, then constraint, then body + ps->RemoveStepListener(vd.constraint); + ps->RemoveConstraint(vd.constraint); + + BodyInterface& bi = ps->GetBodyInterface(); + bi.RemoveBody(vd.body_id); + bi.DestroyBody(vd.body_id); + + g_vehicles.erase(it); +} diff --git a/crates/joltc-sys-patched/JoltC/JoltC/Vehicle.h b/crates/joltc-sys-patched/JoltC/JoltC/Vehicle.h new file mode 100644 index 00000000000..763236f6f9f --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltC/Vehicle.h @@ -0,0 +1,130 @@ +#pragma once + +#include "Functions.h" + +#ifdef __cplusplus +extern "C" { +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Vehicle Parameters — flat struct for creating a 4-wheel vehicle. +// All positions are in local space relative to the vehicle body center. + +typedef struct JPC_VehicleParams { + // Chassis shape (box half-extents in meters) + float chassis_half_x; + float chassis_half_y; + float chassis_half_z; + float chassis_mass; + + // Front wheel attachment positions (mirrored ±x) + float front_wheel_x; // Positive = right side offset + float front_wheel_y; // Height (usually negative, below chassis center) + float front_wheel_z; // Forward offset from center + + // Rear wheel attachment positions (mirrored ±x) + float rear_wheel_x; + float rear_wheel_y; + float rear_wheel_z; + + // Wheel geometry + float wheel_radius; + float wheel_width; + + // Suspension + float suspension_min_length; // Fully compressed (m) + float suspension_max_length; // Fully extended (m) + float suspension_frequency; // Spring frequency (Hz) + float suspension_damping; // Damping ratio (0-1) + + // Steering (front wheels only) + float max_steer_angle_deg; + + // Brakes + float front_brake_torque; + float rear_brake_torque; + float rear_handbrake_torque; + + // Engine + float engine_max_torque; + float engine_min_rpm; + float engine_max_rpm; + + // Transmission (auto mode) + int num_gears; // 1-6 forward gears + float gear_ratios[6]; // Gear ratios (1st through 6th, 0 = unused) + float reverse_gear_ratio; + float shift_up_rpm; + float shift_down_rpm; + + // Differential + float differential_ratio; + int drive_type; // 0 = AWD, 1 = FWD, 2 = RWD +} JPC_VehicleParams; + +// Wheel state for replication +typedef struct JPC_WheelState { + float pos_x, pos_y, pos_z; + float rot_x, rot_y, rot_z, rot_w; + float steer_angle; + float rotation_angle; + float angular_velocity; + float suspension_length; + bool has_contact; +} JPC_WheelState; + +// Complete vehicle state for replication +typedef struct JPC_VehicleState { + // Body transform + float pos_x, pos_y, pos_z; + float rot_x, rot_y, rot_z, rot_w; + float vel_x, vel_y, vel_z; + // Vehicle info + float speed_kmh; + float engine_rpm; + int current_gear; + // Wheel states (FL, FR, RL, RR) + JPC_WheelState wheels[4]; +} JPC_VehicleState; + +//////////////////////////////////////////////////////////////////////////////// +// Vehicle API — high-level functions + +// Fill a VehicleParams struct with sensible defaults for a sedan-like vehicle. +JPC_API void JPC_VehicleParams_default(JPC_VehicleParams* params); + +// Create a 4-wheel vehicle in the physics system. +// Returns a vehicle ID (>0) on success, 0 on failure. +// The vehicle body, constraint, collision tester, and step listener are all +// created and managed internally. +JPC_API uint32_t JPC_PhysicsSystem_CreateVehicle( + JPC_PhysicsSystem* system, + const JPC_VehicleParams* params, + float pos_x, float pos_y, float pos_z, + float rot_x, float rot_y, float rot_z, float rot_w); + +// Set driver input for a vehicle. +// forward: -1 to 1 (backward to forward, auto transmission handles reverse) +// right: -1 to 1 (left to right steering) +// brake: 0 to 1 +// handbrake: 0 to 1 +JPC_API void JPC_PhysicsSystem_SetVehicleInput( + JPC_PhysicsSystem* system, + uint32_t vehicle_id, + float forward, float right, float brake, float handbrake); + +// Get the full vehicle state (body + wheels + engine). +// Returns true on success. +JPC_API bool JPC_PhysicsSystem_GetVehicleState( + JPC_PhysicsSystem* system, + uint32_t vehicle_id, + JPC_VehicleState* out_state); + +// Destroy a vehicle and remove it from the physics system. +JPC_API void JPC_PhysicsSystem_DestroyVehicle( + JPC_PhysicsSystem* system, + uint32_t vehicle_id); + +#ifdef __cplusplus +} +#endif diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/.clang-format b/crates/joltc-sys-patched/JoltC/JoltPhysics/.clang-format new file mode 100644 index 00000000000..e3845288a2a --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/.clang-format @@ -0,0 +1 @@ +DisableFormat: true diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/.editorconfig b/crates/joltc-sys-patched/JoltC/JoltPhysics/.editorconfig new file mode 100644 index 00000000000..7ea7624a6af --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/.editorconfig @@ -0,0 +1,13 @@ +root = true + +[*.{cpp,h,inl,cmake,sh,bat,hlsl}] +indent_style = tab +tab_width = 4 +trim_trailing_whitespace = true +insert_final_newline = true + +[CMakeLists.txt] +indent_style = tab +tab_width = 4 +trim_trailing_whitespace = true +insert_final_newline = true diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/.gitattributes b/crates/joltc-sys-patched/JoltC/JoltPhysics/.gitattributes new file mode 100644 index 00000000000..c5482ed7cdb --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/.gitattributes @@ -0,0 +1,18 @@ +# Convert LF to CRLF on windows on checkout and convert back before submitting to the repository +* text=auto + +# Explicitly declare text files to always be normalized and converted to native line endings on checkout +*.cpp text +*.inl text +*.h text +*.tof text +*.bat text + +# Force shell files to use LF only +*.sh text eol=lf +gradlew text eol=lf + +# Declare binary file types +*.tga binary +*.bof binary +*.bin binary \ No newline at end of file diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/.github/dependabot.yml b/crates/joltc-sys-patched/JoltC/JoltPhysics/.github/dependabot.yml new file mode 100644 index 00000000000..2c431b0bf04 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/.github/dependabot.yml @@ -0,0 +1,9 @@ +# Please see the documentation for all configuration options: +# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/.github/workflows/build.yml b/crates/joltc-sys-patched/JoltC/JoltPhysics/.github/workflows/build.yml new file mode 100644 index 00000000000..c7f4433b3ad --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/.github/workflows/build.yml @@ -0,0 +1,322 @@ +name: Build + +on: + push: + branches: [ master, 4.x ] + paths-ignore: + - 'Docs/**' + - '**.md' + pull_request: + branches: [ master, 4.x ] + paths-ignore: + - 'Docs/**' + - '**.md' + +jobs: + linux-clang: + runs-on: ubuntu-latest + name: Linux Clang + strategy: + fail-fast: false + matrix: + build_type: [Debug, Release, Distribution, ReleaseASAN, ReleaseUBSAN] + clang_version: [clang++-13, clang++-14] + double_precision: [No, Yes] + + steps: + - name: Checkout Code + uses: actions/checkout@v4 + - name: Configure CMake + run: cmake -B ${{github.workspace}}/Build/Linux_${{matrix.build_type}}_${{matrix.clang_version}} -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DCMAKE_CXX_COMPILER=${{matrix.clang_version}} -DDOUBLE_PRECISION=${{matrix.double_precision}} Build + - name: Build + run: cmake --build ${{github.workspace}}/Build/Linux_${{matrix.build_type}}_${{matrix.clang_version}} -j 2 + - name: Test + working-directory: ${{github.workspace}}/Build/Linux_${{matrix.build_type}}_${{matrix.clang_version}} + run: ctest --output-on-failure --verbose + + linux-clang-so: + runs-on: ubuntu-latest + name: Linux Clang Shared Library + strategy: + fail-fast: false + matrix: + build_type: [Debug, Release, Distribution] + clang_version: [clang++-14] + + steps: + - name: Checkout Code + uses: actions/checkout@v4 + - name: Configure CMake + run: cmake -B ${{github.workspace}}/Build/Linux_${{matrix.build_type}}_${{matrix.clang_version}} -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DCMAKE_CXX_COMPILER=${{matrix.clang_version}} -DBUILD_SHARED_LIBS=Yes Build + - name: Build + run: cmake --build ${{github.workspace}}/Build/Linux_${{matrix.build_type}}_${{matrix.clang_version}} -j 2 + - name: Test + working-directory: ${{github.workspace}}/Build/Linux_${{matrix.build_type}}_${{matrix.clang_version}} + run: ctest --output-on-failure --verbose + + linux-clang-32-bit: + runs-on: ubuntu-latest + name: Linux Clang 32-bit + strategy: + fail-fast: false + matrix: + build_type: [Debug, Release, Distribution] + clang_version: [clang++-14] + + steps: + - name: Checkout Code + uses: actions/checkout@v4 + - name: Update APT + run: sudo apt update + - name: Install G++-Multilib + run: sudo apt -y install g++-multilib + - name: Configure CMake + run: cmake -B ${{github.workspace}}/Build/Linux_${{matrix.build_type}}_${{matrix.clang_version}} -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DCMAKE_CXX_COMPILER=${{matrix.clang_version}} -DCMAKE_CXX_FLAGS=-m32 -DUSE_SSE4_1=OFF -DUSE_SSE4_2=OFF -DUSE_AVX=OFF -DUSE_AVX2=OFF -DUSE_AVX512=OFF -DUSE_LZCNT=OFF -DUSE_TZCNT=OFF -DUSE_F16C=OFF -DUSE_FMADD=OFF Build + - name: Build + run: cmake --build ${{github.workspace}}/Build/Linux_${{matrix.build_type}}_${{matrix.clang_version}} -j 2 + - name: Test + working-directory: ${{github.workspace}}/Build/Linux_${{matrix.build_type}}_${{matrix.clang_version}} + run: ctest --output-on-failure --verbose + + linux-gcc: + runs-on: ubuntu-latest + name: Linux GCC + strategy: + fail-fast: false + matrix: + build_type: [Debug, Release, Distribution] + clang_version: [g++] + + steps: + - name: Checkout Code + uses: actions/checkout@v4 + - name: Configure CMake + run: cmake -B ${{github.workspace}}/Build/Linux_${{matrix.build_type}}_${{matrix.clang_version}} -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DCMAKE_CXX_COMPILER=${{matrix.clang_version}} Build + - name: Build + run: cmake --build ${{github.workspace}}/Build/Linux_${{matrix.build_type}}_${{matrix.clang_version}} -j 2 + - name: Test + working-directory: ${{github.workspace}}/Build/Linux_${{matrix.build_type}}_${{matrix.clang_version}} + run: ctest --output-on-failure --verbose + + linux-gcc-so: + runs-on: ubuntu-latest + name: Linux GCC Shared Library + strategy: + fail-fast: false + matrix: + build_type: [Debug, Release, Distribution] + clang_version: [g++] + + steps: + - name: Checkout Code + uses: actions/checkout@v4 + - name: Configure CMake + run: cmake -B ${{github.workspace}}/Build/Linux_${{matrix.build_type}}_${{matrix.clang_version}} -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DCMAKE_CXX_COMPILER=${{matrix.clang_version}} -DBUILD_SHARED_LIBS=Yes Build + - name: Build + run: cmake --build ${{github.workspace}}/Build/Linux_${{matrix.build_type}}_${{matrix.clang_version}} -j 2 + - name: Test + working-directory: ${{github.workspace}}/Build/Linux_${{matrix.build_type}}_${{matrix.clang_version}} + run: ctest --output-on-failure --verbose + + msys2_mingw_gcc: + runs-on: windows-latest + defaults: + run: + shell: msys2 {0} + name: MSYS2 MinGW GCC + strategy: + fail-fast: false + matrix: + build_type: [Debug, Release] + + steps: + - name: Checkout Code + uses: actions/checkout@v4 + - name: Setup MSYS2 + uses: msys2/setup-msys2@v2 + with: + msystem: mingw64 + install: mingw-w64-x86_64-toolchain mingw-w64-x86_64-cmake + update: true + - name: Configure CMake + run: cmake -B Build/MSYS2_MinGW_GCC -G "MinGW Makefiles" -DCMAKE_BUILD_TYPE=${{matrix.build_type}} Build + - name: Build + run: cmake --build Build/MSYS2_MinGW_GCC -j 2 + - name: Test + working-directory: Build/MSYS2_MinGW_GCC + run: ctest --output-on-failure --verbose + + msvc_cl: + runs-on: windows-latest + name: Visual Studio CL + strategy: + fail-fast: false + matrix: + build_type: [Debug, Release, Distribution] + double_precision: [No, Yes] + + steps: + - name: Checkout Code + uses: actions/checkout@v4 + - name: Add msbuild to PATH + uses: microsoft/setup-msbuild@v2 + - name: Configure CMake + run: cmake -B ${{github.workspace}}/Build/VS2022_CL -G "Visual Studio 17 2022" -A x64 Build -DDOUBLE_PRECISION=${{matrix.double_precision}} + - name: Build + run: msbuild Build\VS2022_CL\JoltPhysics.sln /property:Configuration=${{matrix.build_type}} -m + - name: Test + working-directory: ${{github.workspace}}/Build/VS2022_CL/${{matrix.build_type}} + run: ./UnitTests.exe + + msvc_cl_dll: + runs-on: windows-latest + name: Visual Studio CL Shared Library + strategy: + fail-fast: false + matrix: + build_type: [Debug, Release, Distribution] + + steps: + - name: Checkout Code + uses: actions/checkout@v4 + - name: Add msbuild to PATH + uses: microsoft/setup-msbuild@v2 + - name: Configure CMake + run: cmake -B ${{github.workspace}}/Build/VS2022_CL -G "Visual Studio 17 2022" -A x64 Build -DBUILD_SHARED_LIBS=Yes + - name: Build + run: msbuild Build\VS2022_CL\JoltPhysics.sln /property:Configuration=${{matrix.build_type}} -m + - name: Test + working-directory: ${{github.workspace}}/Build/VS2022_CL/${{matrix.build_type}} + run: ./UnitTests.exe + + msvc_cl_32_bit: + runs-on: windows-latest + name: Visual Studio CL 32-bit + strategy: + fail-fast: false + matrix: + build_type: [Debug, Release] + + steps: + - name: Checkout Code + uses: actions/checkout@v4 + - name: Add msbuild to PATH + uses: microsoft/setup-msbuild@v2 + - name: Configure CMake + run: cmake -B ${{github.workspace}}/Build/VS2022_CL_32_BIT -G "Visual Studio 17 2022" -A Win32 -DUSE_SSE4_1=OFF -DUSE_SSE4_2=OFF -DUSE_AVX=OFF -DUSE_AVX2=OFF -DUSE_AVX512=OFF -DUSE_LZCNT=OFF -DUSE_TZCNT=OFF -DUSE_F16C=OFF -DUSE_FMADD=OFF Build + - name: Build + run: msbuild Build\VS2022_CL_32_BIT\JoltPhysics.sln /property:Configuration=${{matrix.build_type}} -m + - name: Test + working-directory: ${{github.workspace}}/Build/VS2022_CL_32_BIT/${{matrix.build_type}} + run: ./UnitTests.exe + + msvc_cl_arm: + runs-on: windows-latest + name: Visual Studio CL ARM + strategy: + fail-fast: false + matrix: + build_type: [Debug, Release] + + steps: + - name: Checkout Code + uses: actions/checkout@v4 + - name: Add msbuild to PATH + uses: microsoft/setup-msbuild@v2 + - name: Configure CMake + run: cmake -B ${{github.workspace}}/Build/VS2022_CL_ARM -G "Visual Studio 17 2022" -A ARM64 Build + - name: Build + run: msbuild Build\VS2022_CL_ARM\JoltPhysics.sln /property:Configuration=${{matrix.build_type}} -m + + msvc_cl_arm_32_bit: + runs-on: windows-latest + name: Visual Studio CL ARM 32-bit + strategy: + fail-fast: false + matrix: + build_type: [Debug, Release] + + steps: + - name: Checkout Code + uses: actions/checkout@v4 + - name: Add msbuild to PATH + uses: microsoft/setup-msbuild@v2 + - name: Configure CMake + run: cmake -B ${{github.workspace}}/Build/VS2022_CL_ARM_32_BIT -G "Visual Studio 17 2022" -A ARM Build + - name: Build + run: msbuild Build\VS2022_CL_ARM_32_BIT\JoltPhysics.sln /property:Configuration=${{matrix.build_type}} -m + + msvc_clang: + runs-on: windows-latest + name: Visual Studio Clang + strategy: + fail-fast: false + matrix: + build_type: [Debug, Release, Distribution] + double_precision: [No, Yes] + + steps: + - name: Checkout Code + uses: actions/checkout@v4 + - name: Add msbuild to PATH + uses: microsoft/setup-msbuild@v2 + - name: Configure CMake + run: cmake -B ${{github.workspace}}/Build/VS2022_Clang -G "Visual Studio 17 2022" -A x64 -T ClangCL Build -DDOUBLE_PRECISION=${{matrix.double_precision}} + - name: Build + run: msbuild Build\VS2022_Clang\JoltPhysics.sln /property:Configuration=${{matrix.build_type}} -m + - name: Test + working-directory: ${{github.workspace}}/Build/VS2022_Clang/${{matrix.build_type}} + run: ./UnitTests.exe + + macos: + runs-on: macos-latest + name: macOS + strategy: + fail-fast: false + matrix: + build_type: [Debug, Release, Distribution] + clang_version: [clang++] + + steps: + - name: Checkout Code + uses: actions/checkout@v4 + - name: Configure CMake + # github macos-latest runs on a 2013 Ivy Bridge CPU so doesn't have AVX2, LZCNT, TZCNT or FMADD + run: cmake -B ${{github.workspace}}/Build/MacOS_${{matrix.build_type}}_${{matrix.clang_version}} -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DCMAKE_CXX_COMPILER=${{matrix.clang_version}} -DUSE_AVX2=OFF -DUSE_AVX512=OFF -DUSE_LZCNT=OFF -DUSE_TZCNT=OFF -DUSE_FMADD=OFF Build + - name: Build + run: cmake --build ${{github.workspace}}/Build/MacOS_${{matrix.build_type}}_${{matrix.clang_version}} -j 2 + - name: Test + working-directory: ${{github.workspace}}/Build/MacOS_${{matrix.build_type}}_${{matrix.clang_version}} + run: ctest --output-on-failure --verbose + + android: + runs-on: ubuntu-latest + name: Android + strategy: + fail-fast: false + + steps: + - name: Checkout Code + uses: actions/checkout@v4 + - name: Setup Java + uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: '17' + - name: Gradle Build + working-directory: ${{github.workspace}}/Build/Android + run: ./gradlew build --no-daemon + + ios: + runs-on: macos-latest + name: iOS + strategy: + fail-fast: false + + steps: + - name: Checkout Code + uses: actions/checkout@v4 + - name: Configure CMake + run: cmake -B ${{github.workspace}}/Build/XCode_iOS -DTARGET_HELLO_WORLD=OFF -DTARGET_PERFORMANCE_TEST=OFF -DCMAKE_SYSTEM_NAME=iOS -GXcode Build + - name: Build + run: cmake --build ${{github.workspace}}/Build/XCode_iOS -- -sdk iphonesimulator -arch x86_64 diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/.github/workflows/determinism_check.yml b/crates/joltc-sys-patched/JoltC/JoltPhysics/.github/workflows/determinism_check.yml new file mode 100644 index 00000000000..764497b1935 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/.github/workflows/determinism_check.yml @@ -0,0 +1,119 @@ +name: Determinism Check + +env: + CONVEX_VS_MESH_HASH: '0x10139effe747511' + RAGDOLL_HASH: '0x777396947c3fff6a' + +on: + push: + branches: [ master, 4.x ] + paths-ignore: + - 'Docs/**' + - '**.md' + pull_request: + branches: [ master, 4.x ] + paths-ignore: + - 'Docs/**' + - '**.md' + +jobs: + linux: + runs-on: ubuntu-latest + name: Linux Determinism Check + + steps: + - name: Checkout Code + uses: actions/checkout@v4 + - name: Configure CMake + run: cmake -B ${{github.workspace}}/Build/Linux_Distribution -DCMAKE_BUILD_TYPE=Distribution -DCMAKE_CXX_COMPILER=clang++ Build -DCROSS_PLATFORM_DETERMINISTIC=ON -DTARGET_VIEWER=OFF -DTARGET_SAMPLES=OFF -DTARGET_HELLO_WORLD=OFF -DTARGET_UNIT_TESTS=OFF + - name: Build + run: cmake --build ${{github.workspace}}/Build/Linux_Distribution + - name: Test ConvexVsMesh + working-directory: ${{github.workspace}}/Build/Linux_Distribution + run: ./PerformanceTest -q=LinearCast -t=2 -s=ConvexVsMesh -validate_hash=${CONVEX_VS_MESH_HASH} + - name: Test Ragdoll + working-directory: ${{github.workspace}}/Build/Linux_Distribution + run: ./PerformanceTest -q=LinearCast -t=2 -s=Ragdoll -validate_hash=${RAGDOLL_HASH} + + msvc_cl: + runs-on: windows-latest + name: Visual Studio CL Determinism Check + + steps: + - name: Checkout Code + uses: actions/checkout@v4 + - name: Add msbuild to PATH + uses: microsoft/setup-msbuild@v2 + - name: Configure CMake + run: cmake -B ${{github.workspace}}/Build/VS2022_CL -G "Visual Studio 17 2022" -A x64 Build -DCROSS_PLATFORM_DETERMINISTIC=ON -DTARGET_VIEWER=OFF -DTARGET_SAMPLES=OFF -DTARGET_HELLO_WORLD=OFF -DTARGET_UNIT_TESTS=OFF + - name: Build + run: msbuild Build\VS2022_CL\JoltPhysics.sln /property:Configuration=Distribution + - name: Test ConvexVsMesh + working-directory: ${{github.workspace}}/Build/VS2022_CL/Distribution + run: ./PerformanceTest -q=LinearCast -t=2 -s=ConvexVsMesh "-validate_hash=$env:CONVEX_VS_MESH_HASH" + - name: Test Ragdoll + working-directory: ${{github.workspace}}/Build/VS2022_CL/Distribution + run: ./PerformanceTest -q=LinearCast -t=2 -s=Ragdoll "-validate_hash=$env:RAGDOLL_HASH" + + msvc_cl_32: + runs-on: windows-latest + name: Visual Studio CL 32-bit Determinism Check + + steps: + - name: Checkout Code + uses: actions/checkout@v4 + - name: Add msbuild to PATH + uses: microsoft/setup-msbuild@v2 + - name: Configure CMake + working-directory: ${{github.workspace}}/Build + run: ./cmake_vs2022_cl_32bit.bat -DCROSS_PLATFORM_DETERMINISTIC=ON -DTARGET_VIEWER=OFF -DTARGET_SAMPLES=OFF -DTARGET_HELLO_WORLD=OFF -DTARGET_UNIT_TESTS=OFF + - name: Build + run: msbuild Build\VS2022_CL_32BIT\JoltPhysics.sln /property:Configuration=Distribution + - name: Test ConvexVsMesh + working-directory: ${{github.workspace}}/Build/VS2022_CL_32BIT/Distribution + run: ./PerformanceTest -q=LinearCast -t=2 -s=ConvexVsMesh "-validate_hash=$env:CONVEX_VS_MESH_HASH" + - name: Test Ragdoll + working-directory: ${{github.workspace}}/Build/VS2022_CL_32BIT/Distribution + run: ./PerformanceTest -q=LinearCast -t=2 -s=Ragdoll "-validate_hash=$env:RAGDOLL_HASH" + + macos: + runs-on: macos-latest + name: macOS Determinism Check + + steps: + - name: Checkout Code + uses: actions/checkout@v4 + - name: Configure CMake + run: cmake -B ${{github.workspace}}/Build/Linux_Distribution -DCMAKE_BUILD_TYPE=Distribution Build -DCROSS_PLATFORM_DETERMINISTIC=ON -DTARGET_VIEWER=OFF -DTARGET_SAMPLES=OFF -DTARGET_HELLO_WORLD=OFF -DTARGET_UNIT_TESTS=OFF -DUSE_AVX2=OFF -DUSE_AVX512=OFF -DUSE_LZCNT=OFF -DUSE_TZCNT=OFF + - name: Build + run: cmake --build ${{github.workspace}}/Build/Linux_Distribution + - name: Test ConvexVsMesh + working-directory: ${{github.workspace}}/Build/Linux_Distribution + run: ./PerformanceTest -q=LinearCast -t=2 -s=ConvexVsMesh -validate_hash=${CONVEX_VS_MESH_HASH} + - name: Test Ragdoll + working-directory: ${{github.workspace}}/Build/Linux_Distribution + run: ./PerformanceTest -q=LinearCast -t=2 -s=Ragdoll -validate_hash=${RAGDOLL_HASH} + + arm: + runs-on: ubuntu-latest + name: ARM Determinism Check + steps: + - name: Checkout Code + uses: actions/checkout@v4 + - name: Update index + run: sudo apt-get update + - name: Install Cross Compiler + run: sudo apt-get install gcc-11-aarch64-linux-gnu gcc-11-multilib g++-11-multilib libstdc++-11-dev-arm64-cross qemu-user -y + - name: Configure CMake + run: cmake -B ${{github.workspace}}/Build/Linux_Distribution -DCMAKE_BUILD_TYPE=Distribution -DCMAKE_CXX_COMPILER=clang++ Build -DCROSS_PLATFORM_DETERMINISTIC=ON -DCROSS_COMPILE_ARM=ON -DTARGET_VIEWER=OFF -DTARGET_SAMPLES=OFF -DTARGET_HELLO_WORLD=OFF -DTARGET_UNIT_TESTS=ON + - name: Build + run: cmake --build ${{github.workspace}}/Build/Linux_Distribution + - name: Test + working-directory: ${{github.workspace}}/Build/Linux_Distribution + run: qemu-aarch64 -L /usr/aarch64-linux-gnu/ ./UnitTests + - name: Test ConvexVsMesh + working-directory: ${{github.workspace}}/Build/Linux_Distribution + run: qemu-aarch64 -L /usr/aarch64-linux-gnu/ ./PerformanceTest -q=LinearCast -t=2 -s=ConvexVsMesh -validate_hash=${CONVEX_VS_MESH_HASH} + - name: Test Ragdoll + working-directory: ${{github.workspace}}/Build/Linux_Distribution + run: qemu-aarch64 -L /usr/aarch64-linux-gnu/ ./PerformanceTest -q=LinearCast -t=2 -s=Ragdoll -validate_hash=${RAGDOLL_HASH} diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/.github/workflows/doxygen.yml b/crates/joltc-sys-patched/JoltC/JoltPhysics/.github/workflows/doxygen.yml new file mode 100644 index 00000000000..c1a0f88e7e0 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/.github/workflows/doxygen.yml @@ -0,0 +1,26 @@ +name: Doxygen Action + +on: + push: + branches: [ master ] + +# Builds and deploys doxygen documentation +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Doxygen Action + uses: mattnotmitt/doxygen-action@v1.9.8 + with: + doxyfile-path: "./Doxyfile" + working-directory: "." + + - name: Deploy + uses: peaceiris/actions-gh-pages@v3 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: ./Build/Doxygen + force_orphan: true diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/.github/workflows/sonar-cloud.yml b/crates/joltc-sys-patched/JoltC/JoltPhysics/.github/workflows/sonar-cloud.yml new file mode 100644 index 00000000000..0ba40c345fd --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/.github/workflows/sonar-cloud.yml @@ -0,0 +1,59 @@ +name: Sonar Cloud + +on: + push: + branches: [ master, 4.x ] + paths-ignore: + - 'Docs/**' + - '**.md' + pull_request: + branches: [ master, 4.x ] + paths-ignore: + - 'Docs/**' + - '**.md' + +jobs: + check-secret: + runs-on: ubuntu-latest + outputs: + sonar-token: ${{ steps.sonar-token.outputs.defined }} + steps: + - id: sonar-token + if: ${{ env.SONAR_TOKEN != '' }} + run: echo "defined=true" >> $GITHUB_OUTPUT + env: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + + build: + name: Build + needs: [check-secret] + if: needs.check-secret.outputs.sonar-token == 'true' + runs-on: ubuntu-latest + env: + BUILD_WRAPPER_OUT_DIR: build_wrapper_output_directory # Directory where build-wrapper output will be placed + CLANG_VERSION: 14 + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis + - name: Install sonar-scanner and build-wrapper + uses: SonarSource/sonarcloud-github-c-cpp@v2 + - name: Configure CMake + run: | + cd ${{github.workspace}}/Build/ + ./cmake_linux_clang_gcc.sh ReleaseCoverage clang++-${{ env.CLANG_VERSION }} + - name: Run build-wrapper + run: | + build-wrapper-linux-x86-64 --out-dir ${{ env.BUILD_WRAPPER_OUT_DIR }} cmake --build ${{github.workspace}}/Build/Linux_ReleaseCoverage + - name: Run unit tests and create coverage report + run: | + cd ${{github.workspace}}/Build/Linux_ReleaseCoverage/ + cmake --build . --target test + llvm-profdata-${{ env.CLANG_VERSION }} merge -sparse default.profraw -o default.profdata + llvm-cov-${{ env.CLANG_VERSION }} show -format=text UnitTests -instr-profile=default.profdata > coverage.txt + - name: Run sonar-scanner + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + run: | + sonar-scanner --define sonar.cfamily.build-wrapper-output="${{ env.BUILD_WRAPPER_OUT_DIR }}" --define sonar.cfamily.llvm-cov.reportPath="${{github.workspace}}/Build/Linux_ReleaseCoverage/coverage.txt" diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/.gitignore b/crates/joltc-sys-patched/JoltC/JoltPhysics/.gitignore new file mode 100644 index 00000000000..132f40ca483 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/.gitignore @@ -0,0 +1,8 @@ +.vs +.vscode +.DS_Store +/profile_chart_*.html +/stats*.html +/snapshot.bin +/*.jor +/detlog.txt diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/.gitignore b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/.gitignore new file mode 100644 index 00000000000..a11c7ca5a38 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/.gitignore @@ -0,0 +1,6 @@ +/Linux* +/VS20* +/XCode* +/MinGW* +/Doxygen +/CoverageReport diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/.gitignore b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/.gitignore new file mode 100644 index 00000000000..be3ca89c736 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/.gitignore @@ -0,0 +1,5 @@ +.idea +.gradle +.cxx +build +local.properties diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/PerformanceTest/build.gradle b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/PerformanceTest/build.gradle new file mode 100644 index 00000000000..dc9eb4f92f2 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/PerformanceTest/build.gradle @@ -0,0 +1,51 @@ +plugins { + id 'com.android.application' +} + +android { + compileSdk 33 + ndkVersion "26.1.10909125" + + defaultConfig { + applicationId "com.joltphysics.performancetest" + minSdk 21 + targetSdk 33 + versionCode 1 + versionName "1.0" + ndk.abiFilters 'arm64-v8a', 'armeabi-v7a', 'x86_64', 'x86' + + externalNativeBuild { + cmake { + cppFlags '-std=c++17 -Wall -Werror -ffp-model=precise -ffp-contract=off -DJPH_PROFILE_ENABLED -DJPH_DEBUG_RENDERER' + arguments '-DANDROID_TOOLCHAIN=clang', '-DANDROID_STL=c++_static', '-DCROSS_PLATFORM_DETERMINISTIC=ON' + } + } + signingConfig signingConfigs.debug + } + + buildTypes { + release { + minifyEnabled false + } + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + externalNativeBuild { + cmake { + path file('src/main/cpp/CMakeLists.txt') + version '3.22.1' + } + } + + buildFeatures { + viewBinding true + } + namespace 'com.joltphysics.performancetest' +} + +dependencies { +} \ No newline at end of file diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/PerformanceTest/src/main/AndroidManifest.xml b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/PerformanceTest/src/main/AndroidManifest.xml new file mode 100644 index 00000000000..fc5682a7476 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/PerformanceTest/src/main/AndroidManifest.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/PerformanceTest/src/main/cpp/CMakeLists.txt b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/PerformanceTest/src/main/cpp/CMakeLists.txt new file mode 100644 index 00000000000..ab8db81ba63 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/PerformanceTest/src/main/cpp/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 3.10.2) + +project("JoltPhysicsPerformanceTest") + +# Make sure we include the app glue sources +set(APP_GLUE_DIR ${ANDROID_NDK}/sources/android/native_app_glue) +include_directories(${APP_GLUE_DIR}) + +# Set repository root +set(PHYSICS_REPO_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../") + +# Make targets +include(${PHYSICS_REPO_ROOT}/Jolt/Jolt.cmake) +include(${PHYSICS_REPO_ROOT}/PerformanceTest/PerformanceTest.cmake) + +# Link shared native library +set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -u ANativeActivity_onCreate") +add_library(PerformanceTest SHARED ${PERFORMANCE_TEST_SRC_FILES} ${APP_GLUE_DIR}/android_native_app_glue.c) +target_include_directories(PerformanceTest PUBLIC Jolt ${JOLT_PHYSICS_ROOT} ${PERFORMANCE_TEST_ROOT}) +target_link_libraries(PerformanceTest Jolt android log) diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/UnitTests/build.gradle b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/UnitTests/build.gradle new file mode 100644 index 00000000000..8f0b3f7e69d --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/UnitTests/build.gradle @@ -0,0 +1,51 @@ +plugins { + id 'com.android.application' +} + +android { + compileSdk 33 + ndkVersion "26.1.10909125" + + defaultConfig { + applicationId "com.joltphysics.unittests" + minSdk 21 + targetSdk 33 + versionCode 1 + versionName "1.0" + ndk.abiFilters 'arm64-v8a', 'armeabi-v7a', 'x86_64', 'x86' + + externalNativeBuild { + cmake { + cppFlags '-std=c++17 -Wall -Werror -ffp-contract=off -DJPH_PROFILE_ENABLED -DJPH_DEBUG_RENDERER' + arguments '-DANDROID_TOOLCHAIN=clang', '-DANDROID_STL=c++_static' + } + } + signingConfig signingConfigs.debug + } + + buildTypes { + release { + minifyEnabled false + } + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + externalNativeBuild { + cmake { + path file('src/main/cpp/CMakeLists.txt') + version '3.22.1' + } + } + + buildFeatures { + viewBinding true + } + namespace 'com.joltphysics.unittests' +} + +dependencies { +} \ No newline at end of file diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/UnitTests/src/main/AndroidManifest.xml b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/UnitTests/src/main/AndroidManifest.xml new file mode 100644 index 00000000000..e31e98364ae --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/UnitTests/src/main/AndroidManifest.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/UnitTests/src/main/cpp/CMakeLists.txt b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/UnitTests/src/main/cpp/CMakeLists.txt new file mode 100644 index 00000000000..de46f4c1e9e --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/UnitTests/src/main/cpp/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 3.10.2) + +project("JoltPhysicsUnitTests") + +# Make sure we include the app glue sources +set(APP_GLUE_DIR ${ANDROID_NDK}/sources/android/native_app_glue) +include_directories(${APP_GLUE_DIR}) + +# Set repository root +set(PHYSICS_REPO_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../") + +# Make targets +include(${PHYSICS_REPO_ROOT}/Jolt/Jolt.cmake) +include(${PHYSICS_REPO_ROOT}/UnitTests/UnitTests.cmake) + +# Link shared native library +set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -u ANativeActivity_onCreate") +add_library(UnitTests SHARED ${UNIT_TESTS_SRC_FILES} ${APP_GLUE_DIR}/android_native_app_glue.c) +target_include_directories(UnitTests PUBLIC Jolt ${JOLT_PHYSICS_ROOT} ${UNIT_TESTS_ROOT}) +target_link_libraries(UnitTests Jolt android log) diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/build.gradle b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/build.gradle new file mode 100644 index 00000000000..5b7ac75ff5a --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/build.gradle @@ -0,0 +1,17 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +buildscript { + repositories { + google() + mavenCentral() + } + dependencies { + classpath 'com.android.tools.build:gradle:8.1.2' + + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files + } +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/gradle.properties b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/gradle.properties new file mode 100644 index 00000000000..193a5b26e77 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/gradle.properties @@ -0,0 +1,22 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app"s APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Automatically convert third-party libraries to use AndroidX +android.enableJetifier=true +android.defaults.buildfeatures.buildconfig=true +android.nonTransitiveRClass=false +android.nonFinalResIds=false \ No newline at end of file diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/gradle/wrapper/gradle-wrapper.jar b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..e708b1c023ec8b20f512888fe07c5bd3ff77bb8f GIT binary patch literal 59203 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

K7UpUK$>l@ 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 E2O(~oYXATM literal 0 HcmV?d00001 diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/gradle/wrapper/gradle-wrapper.properties b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000000..3ffebc84b17 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip +distributionPath=wrapper/dists +zipStorePath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/gradlew b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/gradlew new file mode 100644 index 00000000000..4f906e0c811 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/gradlew.bat b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/gradlew.bat new file mode 100644 index 00000000000..107acd32c4e --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/settings.gradle b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/settings.gradle new file mode 100644 index 00000000000..35a66bb3769 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/settings.gradle @@ -0,0 +1,10 @@ +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + } +} +rootProject.name = "Jolt Physics" +include ':UnitTests' +include ':PerformanceTest' diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/CMakeLists.txt b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/CMakeLists.txt new file mode 100644 index 00000000000..830453f8093 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/CMakeLists.txt @@ -0,0 +1,319 @@ +cmake_minimum_required(VERSION 3.16 FATAL_ERROR) + +project(JoltPhysics CXX) + +# When turning this option on, the library will be compiled using doubles for positions. This allows for much bigger worlds. +option(DOUBLE_PRECISION "Use double precision math" OFF) + +# When turning this option on, the library will be compiled with debug symbols +option(GENERATE_DEBUG_SYMBOLS "Generate debug symbols" ON) + +# When turning this option on, the library will override the default CMAKE_CXX_FLAGS_DEBUG/RELEASE values, otherwise they will use the platform defaults +option(OVERRIDE_CXX_FLAGS "Override CMAKE_CXX_FLAGS_DEBUG/RELEASE" ON) + +# When turning this option on, the library will be compiled in such a way to attempt to keep the simulation deterministic across platforms +option(CROSS_PLATFORM_DETERMINISTIC "Cross platform deterministic" OFF) + +# When turning this option on, the library will be compiled for ARM (aarch64-linux-gnu), requires compiling with clang +option(CROSS_COMPILE_ARM "Cross compile to aarch64-linux-gnu" OFF) + +# When turning this option on, Jolt will be compiled as a shared library and public symbols will be exported. +option(BUILD_SHARED_LIBS "Compile Jolt as a shared library" OFF) + +# When turning this option on, the library will be compiled with interprocedural optimizations enabled, also known as link-time optimizations or link-time code generation. +# Note that if you turn this on you need to use SET_INTERPROCEDURAL_OPTIMIZATION() or set(CMAKE_INTERPROCEDURAL_OPTIMIZATION ON) to enable LTO specifically for your own project as well. +# If you don't do this you may get an error: /usr/bin/ld: libJolt.a: error adding symbols: file format not recognized +option(INTERPROCEDURAL_OPTIMIZATION "Enable interprocedural optimizations" ON) + +# When turning this on, in Debug and Release mode, the library will emit extra code to ensure that the 4th component of a 3-vector is kept the same as the 3rd component +# and will enable floating point exceptions during simulation to detect divisions by zero. +# Note that this currently only works using MSVC. Clang turns Float2 into a SIMD vector sometimes causing floating point exceptions (the option is ignored). +option(FLOATING_POINT_EXCEPTIONS_ENABLED "Enable floating point exceptions" ON) + +# Number of bits to use in ObjectLayer. Can be 16 or 32. +option(OBJECT_LAYER_BITS "Number of bits in ObjectLayer" 16) + +# Select X86 processor features to use (if everything is off it will be SSE2 compatible) +option(USE_SSE4_1 "Enable SSE4.1" ON) +option(USE_SSE4_2 "Enable SSE4.2" ON) +option(USE_AVX "Enable AVX" ON) +option(USE_AVX2 "Enable AVX2" ON) +option(USE_AVX512 "Enable AVX512" OFF) +option(USE_LZCNT "Enable LZCNT" ON) +option(USE_TZCNT "Enable TZCNT" ON) +option(USE_F16C "Enable F16C" ON) +option(USE_FMADD "Enable FMADD" ON) + +# Enable all warnings +option(ENABLE_ALL_WARNINGS "Enable all warnings and warnings as errors" ON) + +# Setting to periodically trace broadphase stats to help determine if the broadphase layer configuration is optimal +option(TRACK_BROADPHASE_STATS "Track Broadphase Stats" OFF) + +# Setting to periodically trace narrowphase stats to help determine which collision queries could be optimized +option(TRACK_NARROWPHASE_STATS "Track Narrowphase Stats" OFF) + +# Enable the debug renderer in the Debug and Release builds. Note that DEBUG_RENDERER_IN_DISTRIBUTION will override this setting. +option(DEBUG_RENDERER_IN_DEBUG_AND_RELEASE "Enable debug renderer in Debug and Release builds" ON) + +# Setting to enable the debug renderer in all builds. +# Note that enabling this reduces the performance of the library even if you're not drawing anything. +option(DEBUG_RENDERER_IN_DISTRIBUTION "Enable debug renderer in all builds" OFF) + +# Enable the profiler in Debug and Release builds. Note that PROFILER_IN_DISTRIBUTION will override this setting. +option(PROFILER_IN_DEBUG_AND_RELEASE "Enable the profiler in Debug and Release builds" ON) + +# Enable the profiler in all builds. +# Note that enabling this reduces the performance of the library. +option(PROFILER_IN_DISTRIBUTION "Enable the profiler in all builds" OFF) + +# Setting this option will force the library to use malloc/free instead of allowing the user to override the memory allocator +option(DISABLE_CUSTOM_ALLOCATOR "Disable support for a custom memory allocator" OFF) + +include(CMakeDependentOption) + +# Ability to toggle between the static and DLL versions of the MSVC runtime library +# Windows Store only supports the DLL version +cmake_dependent_option(USE_STATIC_MSVC_RUNTIME_LIBRARY "Use the static MSVC runtime library" ON "MSVC;NOT WINDOWS_STORE" OFF) + +# Determine which configurations exist +if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) # Only do this when we're at the top level, see: https://gitlab.kitware.com/cmake/cmake/-/issues/24181 + if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") + set(CMAKE_CONFIGURATION_TYPES "Debug;Release;Distribution") + elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang") + set(CMAKE_CONFIGURATION_TYPES "Debug;Release;ReleaseASAN;ReleaseUBSAN;ReleaseCoverage;Distribution") + endif() +endif() + +if (MSVC) + # Fill in the path to the asan libraries + set(CLANG_LIB_PATH "\"$(VSInstallDir)\\VC\\Tools\\Llvm\\x64\\lib\\clang\\${CMAKE_CXX_COMPILER_VERSION}\\lib\\windows\"") + + # 64 bit architecture + set(CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE "x64") + + # Set runtime library + if (USE_STATIC_MSVC_RUNTIME_LIBRARY) + set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") + endif() + + # Set general compiler flags + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zc:__cplusplus /Gm- /MP /nologo /diagnostics:classic /FC /fp:except- /Zc:inline") + + # Enable warnings + if (ENABLE_ALL_WARNINGS) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Wall /WX") + endif() + + # Optionally generate debug symbols + if (GENERATE_DEBUG_SYMBOLS) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zi") + endif() + + # Remove any existing compiler flag that enables RTTI + string(REPLACE "/GR" "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS}) + + # Set compiler flag for disabling RTTI + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /GR-") + + if ("${CMAKE_VS_PLATFORM_NAME}" STREQUAL "ARM") + # On ARM the exception handling flag is missing which causes warnings + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /EHsc") + endif() + + # Set compiler flags for various configurations + set(CMAKE_CXX_FLAGS_DEBUG "/GS /Od /Ob0 /RTC1") + set(CMAKE_CXX_FLAGS_RELEASE "/GS- /Gy /O2 /Oi /Ot") + set(CMAKE_CXX_FLAGS_DISTRIBUTION "/GS- /Gy /O2 /Oi /Ot") + set(CMAKE_CXX_FLAGS_RELEASEASAN "-fsanitize=address /Od") + set(CMAKE_CXX_FLAGS_RELEASEUBSAN "-fsanitize=undefined,implicit-conversion,float-divide-by-zero,local-bounds -fno-sanitize-recover=all") + set(CMAKE_CXX_FLAGS_RELEASECOVERAGE "-fprofile-instr-generate -fcoverage-mapping") + + # Set linker flags + set(CMAKE_EXE_LINKER_FLAGS "/SUBSYSTEM:WINDOWS /ignore:4221") + if (GENERATE_DEBUG_SYMBOLS) + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /DEBUG") + endif() + if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") + if (CROSS_PLATFORM_DETERMINISTIC) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fp:precise") + else() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fp:fast") # Clang doesn't use fast math because it cannot be turned off inside a single compilation unit + endif() + elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /showFilenames") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Qunused-arguments") # Clang emits warnings about unused arguments such as /MP and /GL + set(CMAKE_EXE_LINKER_FLAGS_RELEASEASAN "/SUBSYSTEM:CONSOLE /LIBPATH:${CLANG_LIB_PATH} clang_rt.asan-x86_64.lib -wholearchive:clang_rt.asan-x86_64.lib clang_rt.asan_cxx-x86_64.lib -wholearchive:clang_rt.asan_cxx-x86_64.lib") + set(CMAKE_EXE_LINKER_FLAGS_RELEASEUBSAN "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /LIBPATH:${CLANG_LIB_PATH}") + set(CMAKE_EXE_LINKER_FLAGS_RELEASECOVERAGE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /LIBPATH:${CLANG_LIB_PATH}") + endif() +else() + # Enable warnings + if (ENABLE_ALL_WARNINGS) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Werror") + endif() + + # Optionally generate debug symbols + if (GENERATE_DEBUG_SYMBOLS) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g") + endif() + + if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") + # Also disable -Wstringop-overflow or it will generate false positives that can't be disabled from code when link-time optimizations are enabled + # Also turn off automatic fused multiply add contractions, there doesn't seem to be a way to do this selectively through the macro JPH_PRECISE_MATH_OFF + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-stringop-overflow -ffp-contract=off") + else() + # Do not use -ffast-math since it cannot be turned off in a single compilation unit under clang, see Core.h + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ffp-model=precise") + + # On clang 14 and later we can turn off float contraction through a pragma, older versions and deterministic versions need it off always, see Core.h + if (CMAKE_CXX_COMPILER_VERSION LESS 14 OR CROSS_PLATFORM_DETERMINISTIC) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ffp-contract=off") + endif() + endif() + + # See https://github.com/jrouwe/JoltPhysics/issues/922. When compiling with DOUBLE_PRECISION=YES and CMAKE_OSX_DEPLOYMENT_TARGET=10.12 clang triggers a warning that we silence here. + if ("${CMAKE_SYSTEM_NAME}" MATCHES "Darwin" AND "${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -faligned-allocation") + endif() + + # Cross compiler flags + if (CROSS_COMPILE_ARM) + set(CMAKE_CXX_FLAGS "--target=aarch64-linux-gnu ${CMAKE_CXX_FLAGS}") + endif() + + # Set compiler flags for various configurations + if (OVERRIDE_CXX_FLAGS) + set(CMAKE_CXX_FLAGS_DEBUG "") + set(CMAKE_CXX_FLAGS_RELEASE "-O3") + endif() + set(CMAKE_CXX_FLAGS_DISTRIBUTION "${CMAKE_CXX_FLAGS_RELEASE}") + set(CMAKE_CXX_FLAGS_RELEASEASAN "-fsanitize=address") + set(CMAKE_CXX_FLAGS_RELEASEUBSAN "-fsanitize=undefined,implicit-conversion,float-divide-by-zero,local-bounds -fno-sanitize-recover=all") + set(CMAKE_CXX_FLAGS_RELEASECOVERAGE "-O0 -DJPH_NO_FORCE_INLINE -fprofile-instr-generate -fcoverage-mapping") + + # Set linker flags + if (NOT ("${CMAKE_SYSTEM_NAME}" STREQUAL "Windows")) + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -pthread") + endif() +endif() + +# Set linker flags +set(CMAKE_EXE_LINKER_FLAGS_DISTRIBUTION "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") + +# Enable link time optimization in Release and Distribution mode if requested and available +function(SET_INTERPROCEDURAL_OPTIMIZATION) + set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE OFF PARENT_SCOPE) + set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_DISTRIBUTION OFF PARENT_SCOPE) + + # On ARM, whole program optimization triggers an internal compiler error during code gen, so we don't turn it on + if (INTERPROCEDURAL_OPTIMIZATION AND NOT ("${CMAKE_VS_PLATFORM_NAME}" STREQUAL "ARM64") AND NOT ("${CMAKE_VS_PLATFORM_NAME}" STREQUAL "ARM")) + include(CheckIPOSupported) + check_ipo_supported(RESULT IS_IPO_SUPPORTED OUTPUT IPO_CHECK_OUTPUT) + + if (IS_IPO_SUPPORTED) + set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE ON PARENT_SCOPE) + set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_DISTRIBUTION ON PARENT_SCOPE) + else() + message(WARNING "Interprocedural optimizations are not supported: ${IPO_CHECK_OUTPUT}") + endif() + endif() +endfunction() +SET_INTERPROCEDURAL_OPTIMIZATION() + +# Set repository root +set(PHYSICS_REPO_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/../) + +# Make Jolt Library +include(${PHYSICS_REPO_ROOT}/Jolt/Jolt.cmake) +if (XCODE) + # Ensure that we enable SSE4.2 for the x86_64 build, XCode builds multiple architectures + set_property(TARGET Jolt PROPERTY XCODE_ATTRIBUTE_OTHER_CPLUSPLUSFLAGS[arch=x86_64] "$(inherited) -msse4.2 -mpopcnt") +endif() + +# Install Jolt library and includes +install(TARGETS Jolt DESTINATION lib) +foreach(SRC_FILE ${JOLT_PHYSICS_SRC_FILES}) + string(REPLACE ${PHYSICS_REPO_ROOT} "" RELATIVE_SRC_FILE ${SRC_FILE}) + get_filename_component(DESTINATION_PATH ${RELATIVE_SRC_FILE} DIRECTORY) + if (NOT RELATIVE_SRC_FILE MATCHES "\.cpp") + install(FILES ${SRC_FILE} DESTINATION include/${DESTINATION_PATH}) + endif() +endforeach() + +# Check if we're the root CMakeLists.txt, if not we are included by another CMake file and we should disable everything except for the main library +if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) + # Ability to turn ON/OFF individual applications + option(TARGET_UNIT_TESTS "Build Unit Tests" ON) + option(TARGET_HELLO_WORLD "Build Hello World" ON) + option(TARGET_PERFORMANCE_TEST "Build Performance Test" ON) + option(TARGET_SAMPLES "Build Samples" ON) + option(TARGET_VIEWER "Build JoltViewer" ON) + + if (TARGET_UNIT_TESTS) + # Create UnitTests executable + include(${PHYSICS_REPO_ROOT}/UnitTests/UnitTests.cmake) + add_executable(UnitTests ${UNIT_TESTS_SRC_FILES}) + target_include_directories(UnitTests PUBLIC ${UNIT_TESTS_ROOT}) + target_link_libraries(UnitTests LINK_PUBLIC Jolt) + target_precompile_headers(UnitTests PRIVATE ${JOLT_PHYSICS_ROOT}/Jolt.h) + if (MSVC) + target_link_options(UnitTests PUBLIC "/SUBSYSTEM:CONSOLE") + endif() + if (IOS) + # Set the bundle information + set_property(TARGET UnitTests PROPERTY MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/iOS/UnitTestsInfo.plist") + set_property(TARGET UnitTests PROPERTY XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "com.joltphysics.unittests") + endif() + if (XCODE) + # Ensure that we enable SSE4.2 for the x86_64 build, XCode builds multiple architectures + set_property(TARGET UnitTests PROPERTY XCODE_ATTRIBUTE_OTHER_CPLUSPLUSFLAGS[arch=x86_64] "$(inherited) -msse4.2 -mpopcnt") + endif() + + # Register unit tests as a test so that it can be run with: + # ctest --output-on-failure + enable_testing() + add_test(UnitTests UnitTests) + endif() + + if (NOT "${CMAKE_SYSTEM_NAME}" STREQUAL "WindowsStore") + if (TARGET_HELLO_WORLD) + # Example 'Hello World' application + include(${PHYSICS_REPO_ROOT}/HelloWorld/HelloWorld.cmake) + add_executable(HelloWorld ${HELLO_WORLD_SRC_FILES}) + target_include_directories(HelloWorld PUBLIC ${HELLO_WORLD_ROOT}) + target_link_libraries(HelloWorld LINK_PUBLIC Jolt) + if (MSVC) + target_link_options(HelloWorld PUBLIC "/SUBSYSTEM:CONSOLE") + endif() + endif() + + if (TARGET_PERFORMANCE_TEST) + # Performance Test application + include(${PHYSICS_REPO_ROOT}/PerformanceTest/PerformanceTest.cmake) + add_executable(PerformanceTest ${PERFORMANCE_TEST_SRC_FILES}) + target_include_directories(PerformanceTest PUBLIC ${PERFORMANCE_TEST_ROOT}) + target_link_libraries(PerformanceTest LINK_PUBLIC Jolt) + if (MSVC) + target_link_options(PerformanceTest PUBLIC "/SUBSYSTEM:CONSOLE") + endif() + set_property(TARGET PerformanceTest PROPERTY VS_DEBUGGER_WORKING_DIRECTORY "${PHYSICS_REPO_ROOT}") + + # Copy the assets folder + add_custom_command(TARGET PerformanceTest PRE_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${PHYSICS_REPO_ROOT}/Assets/ $/Assets/) + endif() + endif() + + if ("${CMAKE_SYSTEM_NAME}" STREQUAL "Windows" AND NOT ("${CMAKE_VS_PLATFORM_NAME}" STREQUAL "ARM")) # ARM 32-bit is missing dinput8.lib + # Windows only targets + if (TARGET_SAMPLES OR TARGET_VIEWER) + include(${PHYSICS_REPO_ROOT}/TestFramework/TestFramework.cmake) + endif() + if (TARGET_SAMPLES) + include(${PHYSICS_REPO_ROOT}/Samples/Samples.cmake) + endif() + if (TARGET_VIEWER) + include(${PHYSICS_REPO_ROOT}/JoltViewer/JoltViewer.cmake) + endif() + endif() +endif() diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/README.md b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/README.md new file mode 100644 index 00000000000..637477e5b55 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/README.md @@ -0,0 +1,237 @@ +# Building and Using Jolt Physics + +## Build Types + +Each platform supports multiple build targets + +- Debug - Debug version of the library, turns on asserts +- Release - Release version of the library, no asserts but includes profiling support and can draw the world and simulation properties +- ReleaseASAN - As Release but turns on Address Sanitizer (clang only) to find bugs +- ReleaseUBSAN - As Release but turns on Undefined Behavior Sanitizer (clang only) to find bugs +- ReleaseCoverage - As Release but turns on Coverage reporting (clang only) to find which areas of the code are not executed +- Distribution - Shippable version of the library, turns off all debugging support + +## Includes + +The Jolt headers don't include Jolt/Jolt.h. Always include Jolt/Jolt.h before including any other Jolt header. +You can use Jolt/Jolt.h in your precompiled header to speed up compilation. + +## Defines + +There are a number of user configurable defines that turn on/off certain features: +

+ General Options (click to see more) +
    +
  • JPH_PROFILE_ENABLED - Turns on the internal profiler.
  • +
  • JPH_EXTERNAL_PROFILE - Turns on the internal profiler but forwards the information to a user defined external system (see Profiler.h).
  • +
  • JPH_DEBUG_RENDERER - Adds support to draw lines and triangles, used to be able to debug draw the state of the world.
  • +
  • JPH_DISABLE_TEMP_ALLOCATOR - Disables the temporary memory allocator, used mainly to allow ASAN to do its job.
  • +
  • JPH_DISABLE_CUSTOM_ALLOCATOR - Disables the ability to override the memory allocator.
  • +
  • JPH_FLOATING_POINT_EXCEPTIONS_ENABLED - Turns on division by zero and invalid floating point exception support in order to detect bugs (Windows only).
  • +
  • JPH_CROSS_PLATFORM_DETERMINISTIC - Turns on behavior to attempt cross platform determinism. If this is set, JPH_USE_FMADD is ignored.
  • +
  • JPH_DOUBLE_PRECISION - Compiles the library so that all positions are stored in doubles instead of floats. This makes larger worlds possible.
  • +
+
+ +
+ CPU Instruction Sets (click to see more) +
    +
  • JPH_USE_SSE4_1 - Enable SSE4.1 CPU instructions (default: on, x86/x64 only)
  • +
  • JPH_USE_SSE4_2 - Enable SSE4.2 CPU instructions (default: on, x86/x64 only)
  • +
  • JPH_USE_F16C - Enable half float CPU instructions (default: on, x86/x64 only)
  • +
  • JPH_USE_LZCNT - Enable the lzcnt CPU instruction (default: on, x86/x64 only)
  • +
  • JPH_USE_TZCNT - Enable the tzcnt CPU instruction (default: on, x86/x64 only)
  • +
  • JPH_USE_AVX - Enable AVX CPU instructions (default: on, x86/x64 only)
  • +
  • JPH_USE_AVX2 - Enable AVX2 CPU instructions (default: on, x86/x64 only)
  • +
  • JPH_USE_AVX512 - Enable AVX512F+AVX512VL CPU instructions (default: off, x86/x64 only)
  • +
  • JPH_USE_FMADD - Enable fused multiply add CPU instructions (default: on, x86/x64 only)
  • +
+
+ +## Logging & Asserting + +To override the default trace and assert mechanism install your own custom handlers in Trace and AssertFailed (see IssueReporting.h). + +## Custom Memory Allocator + +To implement your custom memory allocator override Allocate, Free, AlignedAllocate and AlignedFree (see Memory.h). + +## Building + +
+ Windows 10+ +
  • +
    + MSVC CL (default compiler) +
      +
    • Download Visual Studio 2022 (Community or other edition)
    • +
    • Download CMake 3.15+ (https://cmake.org/download/)
    • +
    • Run cmake_vs2022_cl.bat
    • +
    • Open the resulting project file VS2022_CL\JoltPhysics.sln
    • +
    • Compile and run either 'Samples' or 'UnitTests'
    • +
    +
    +
    + MSVC CL - 32 bit +
      +
    • Download Visual Studio 2022 (Community or other edition)
    • +
    • Download CMake 3.15+ (https://cmake.org/download/)
    • +
    • Run cmake_vs2022_cl_32bit.bat
    • +
    • Open the resulting project file VS2022_CL_32BIT\JoltPhysics.sln
    • +
    • Compile and run either 'Samples' or 'UnitTests'
    • +
    +
    +
    + MSVC Clang compiler +
      +
    • Download Visual Studio 2022 (Community or other edition)
    • +
    • Make sure to install "C++ Clang Compiler for Windows 11.0.0+" and "C++ Clang-cl for v142+ build tools (x64/x86)" using the Visual Studio Installer
    • +
    • Download CMake 3.15+ (https://cmake.org/download/)
    • +
    • Run cmake_vs2022_clang.bat
    • +
    • Open the resulting project file VS2022_Clang\JoltPhysics.sln
    • +
    • Compile and run either 'Samples' or 'UnitTests'
    • +
    +
    +
    + MSVC Universal Windows Platform +
      +
    • Download Visual Studio 2022+ (Community or other edition)
    • +
    • Make sure to install "Universal Windows Platform development" using the Visual Studio Installer
    • +
    • Download CMake 3.15+ (https://cmake.org/download/)
    • +
    • Run cmake_vs2022_uwp.bat
    • +
    • Open the resulting project file VS2022_UWP\JoltPhysics.sln
    • +
    • Compile and run 'UnitTests'
    • +
    +
    +
    + MinGW +
      +
    • Follow download instructions for MSYS2 (https://www.msys2.org/)
    • +
    • From the MSYS2 MSYS app run: pacman -S --needed mingw-w64-x86_64-toolchain mingw-w64-x86_64-cmake
    • +
    • From the MSYS2 MINGW x64 app, in the Build folder run: ./cmake_windows_mingw.sh
    • +
    • Run: cmake --build MinGW_Debug
    • +
    • Run: MinGW_Debug/UnitTests.exe
    • +
    +
    +
+
+ +
+ Linux +
  • +
    + Debian flavor, x64 or ARM64 +
      +
    • Install clang (apt-get install clang)
    • +
    • Install cmake (apt-get install cmake)
    • +
    • Run: ./cmake_linux_clang_gcc.sh
    • +
    • Go to the Linux_Debug folder
    • +
    • Run: make -j$(nproc) && ./UnitTests
    • +
    +
    +
    + Debian flavor, MinGW Cross Compile +
      +
    • This setup can be used to run samples on Linux using wine and vkd3d. Tested on Ubuntu 22.04
    • +
    • Graphics card must support Vulkan and related drivers must be installed
    • +
    • Install mingw-w64 (apt-get install mingw-w64)
    • +
    • Run: update-alternatives --config x86_64-w64-mingw32-g++ (Select /usr/bin/x86_64-w64-mingw32-g++-posix)
    • +
    • Install cmake (apt-get install cmake)
    • +
    • Install wine64 (apt-get install wine64)
    • +
    • Run: export WINEPATH="/usr/x86_64-w64-mingw32/lib;/usr/lib/gcc/x86_64-w64-mingw32/10-posix" (change it based on your environment)
    • +
    • Run: ./cmake_linux_mingw.sh Release (Debug doesn't work)
    • +
    • Go to the MinGW_Release folder
    • +
    • Run: make -j$(nproc) && wine UnitTests.exe
    • +
    • Run: wine Samples.exe
    • +
    +
    +
+
+ +
+ Android +
    +
  • Install Android Studio 2020.3.1+ (https://developer.android.com/studio/)
  • +
  • Open the 'Android' folder in Android Studio and wait until gradle finishes
  • +
  • Select 'Run' / 'Run...' and 'UnitTests'
  • +
  • If the screen turns green after a while the unit tests succeeded, when red they failed (see the android log for details)
  • +
+
+ +
+ macOS +
    +
  • Install XCode
  • +
  • Download CMake 3.23+ (https://cmake.org/download/)
  • +
  • Run: ./cmake_xcode_macos.sh
  • +
  • This will open XCode with a newly generated project
  • +
  • Build and run the project
  • +
  • Note that you can also follow the steps in the 'Linux' section if you wish to build without XCode.
  • +
+
+ +
+ iOS +
    +
  • Install XCode
  • +
  • Download CMake 3.23+ (https://cmake.org/download/)
  • +
  • Run: ./cmake_xcode.ios.sh
  • +
  • This will open XCode with a newly generated project
  • +
  • Build and run the project (note that this will only work in the simulator as the code signing information is not set up)
  • +
+
+ +## Other Build Tools + +* A vcpkg package is available [here](https://github.com/microsoft/vcpkg/tree/master/ports/joltphysics). +* A xmake package is available [here](https://github.com/xmake-io/xmake-repo/tree/dev/packages/j/joltphysics). +* Jolt has been verified to build with [ninja](https://ninja-build.org/) through CMake. + +## Errors + +### Link Error: File Format Not Recognized + +If you receive the following error when linking: + +``` +/usr/bin/ld: libJolt.a: error adding symbols: file format not recognized +``` + +Then you have not enabled interprocedural optimizations (link time optimizations) for your own application. See the INTERPROCEDURAL_OPTIMIZATION option in CMakeLists.txt. + +### Link Error: Unresolved External Symbol + +If you receive a link error that looks like: + +``` +error LNK2001: unresolved external symbol "public: virtual void __cdecl JPH::ConvexShape::GetSubmergedVolume(...) const" +``` + +you have a mismatch in defines between your own code and the Jolt library. In this case the mismatch is in the define `JPH_DEBUG_RENDERER` which is most likely defined in `Jolt.lib` and not in your own project. In `Debug` and `Release` builds, Jolt by default has `JPH_DEBUG_RENDERER` defined, in `Distribution` it is not defined. The cmake options `DEBUG_RENDERER_IN_DEBUG_AND_RELEASE` and `DEBUG_RENDERER_IN_DISTRIBUTION` override this behavior. + +The `RegisterTypes` function (which you have to call to initialize the library) checks the other important defines and will trace and abort if there are more mismatches. + +### DirectX Error + +The samples use DirectX for the graphics implementation, when attempting to run the samples you may get a DirectX error pop-up which may say "The GPU device instance has been suspended", in your debugger you may see the message "Using the Redistributable D3D12 SDKLayers dll also requires that the latest SDKLayers for Windows 10 is installed.". + +Fix this by enabling "Graphics Tools" which is an optional Windows settings. To enable it you have to press the windows key, search for "Manage Optional Features", and then click "Add a Feature", and install "Graphics Tools". + +### Illegal Instruction Error + +If your CPU doesn't support all of the instructions you'll get an `Illegal instruction` exception. + +On Linux to see what instructions your CPU supports run `lscpu` and then look at the flags section, on Windows you can use a program like [`coreinfo`](https://learn.microsoft.com/en-us/sysinternals/downloads/coreinfo). Once you know what instructions your cpu supports you can configure the project through cmake and for example disable all special instructions: + +``` +./cmake_linux_clang_gcc.sh Release clang++ -DUSE_SSE4_1=OFF -DUSE_SSE4_2=OFF -DUSE_AVX=OFF -DUSE_AVX2=OFF -DUSE_AVX512=OFF -DUSE_LZCNT=OFF -DUSE_TZCNT=OFF -DUSE_F16C=OFF -DUSE_FMADD=OFF +``` + +Note that this example is for Linux but the cmake settings work on Windows too. + +## Doxygen on Windows + +Documentation can be generated through doxygen: + +- Install Doxygen (https://www.doxygen.nl/download.html) +- Run: run_doxygen.bat diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_linux_clang_gcc.sh b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_linux_clang_gcc.sh new file mode 100644 index 00000000000..6d563c4c8da --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_linux_clang_gcc.sh @@ -0,0 +1,28 @@ +#!/bin/sh + +if [ -z $1 ] +then + BUILD_TYPE=Debug +else + BUILD_TYPE=$1 + shift +fi + +if [ -z $1 ] +then + COMPILER=clang++ +else + COMPILER=$1 + shift +fi + +BUILD_DIR=Linux_$BUILD_TYPE + +echo Usage: ./cmake_linux_clang_gcc.sh [Configuration] [Compiler] +echo "Possible configurations: Debug (default), Release, Distribution, ReleaseUBSAN, ReleaseASAN, ReleaseCoverage" +echo "Possible compilers: clang++, clang++-XX, g++, g++-XX where XX is the version" +echo Generating Makefile for build type \"$BUILD_TYPE\" and compiler \"$COMPILER\" in folder \"$BUILD_DIR\" + +cmake -S . -B $BUILD_DIR -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_COMPILER=$COMPILER "${@}" + +echo Compile by running \"make -j 8 \&\& ./UnitTests\" in folder \"$BUILD_DIR\" diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_linux_mingw.sh b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_linux_mingw.sh new file mode 100644 index 00000000000..700265ae478 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_linux_mingw.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +if [ -z $1 ] +then + BUILD_TYPE=Release +else + BUILD_TYPE=$1 + shift +fi + +BUILD_DIR=MinGW_$BUILD_TYPE + +echo Usage: ./cmake_linux_mingw.sh [Configuration] +echo "Possible configurations: Debug, Release (default), Distribution" +echo Generating Makefile for build type \"$BUILD_TYPE\" in folder \"$BUILD_DIR\" + +cmake -S . -B $BUILD_DIR -DCMAKE_TOOLCHAIN_FILE=mingw-w64-x86_64.cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE "${@}" + +echo Compile by running \"cmake --build $BUILD_DIR -j 8\" diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2019_cl.bat b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2019_cl.bat new file mode 100644 index 00000000000..3eb4886e6e9 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2019_cl.bat @@ -0,0 +1,3 @@ +@echo off +cmake -S . -B VS2019_CL -G "Visual Studio 16 2019" -A x64 %* +echo Open VS2019_CL\JoltPhysics.sln to build the project. diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2019_cl_arm.bat b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2019_cl_arm.bat new file mode 100644 index 00000000000..8c733e056ba --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2019_cl_arm.bat @@ -0,0 +1,3 @@ +@echo off +cmake -S . -B VS2019_CL_ARM -G "Visual Studio 16 2019" -A ARM64 %* +echo Open VS2019_CL_ARM\JoltPhysics.sln to build the project. diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2019_cl_arm_32bit.bat b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2019_cl_arm_32bit.bat new file mode 100644 index 00000000000..7e78624a2ec --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2019_cl_arm_32bit.bat @@ -0,0 +1,3 @@ +@echo off +cmake -S . -B VS2019_CL_ARM_32BIT -G "Visual Studio 16 2019" -A ARM %* +echo Open VS2019_CL_ARM_32BIT\JoltPhysics.sln to build the project. diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2019_clang.bat b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2019_clang.bat new file mode 100644 index 00000000000..e25a36cafb2 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2019_clang.bat @@ -0,0 +1,10 @@ +@echo off +cmake -S . -B VS2019_Clang -G "Visual Studio 16 2019" -A x64 -T ClangCL %* +echo: +echo !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +echo Make sure to install: +echo - C++ Clang Compiler for Windows 11.0.0+ +echo - C++ Clang-cl for v142+ build tools (x64/x86) +echo Using the Visual Studio Installer +echo !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +echo Open VS2019_Clang/JoltPhysics.sln to build the project. diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_cl.bat b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_cl.bat new file mode 100644 index 00000000000..4cf9990ee1c --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_cl.bat @@ -0,0 +1,3 @@ +@echo off +cmake -S . -B VS2022_CL -G "Visual Studio 17 2022" -A x64 %* +echo Open VS2022_CL\JoltPhysics.sln to build the project. diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_cl_32bit.bat b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_cl_32bit.bat new file mode 100644 index 00000000000..299fd331d04 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_cl_32bit.bat @@ -0,0 +1,3 @@ +@echo off +cmake -S . -B VS2022_CL_32BIT -G "Visual Studio 17 2022" -A Win32 -DUSE_SSE4_1=OFF -DUSE_SSE4_2=OFF -DUSE_AVX=OFF -DUSE_AVX2=OFF -DUSE_AVX512=OFF -DUSE_LZCNT=OFF -DUSE_TZCNT=OFF -DUSE_F16C=OFF -DUSE_FMADD=OFF %* +echo Open VS2022_CL_32BIT\JoltPhysics.sln to build the project. diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_cl_arm.bat b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_cl_arm.bat new file mode 100644 index 00000000000..146f01f914a --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_cl_arm.bat @@ -0,0 +1,3 @@ +@echo off +cmake -S . -B VS2022_CL_ARM -G "Visual Studio 17 2022" -A ARM64 %* +echo Open VS2022_CL_ARM\JoltPhysics.sln to build the project. diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_cl_arm_32bit.bat b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_cl_arm_32bit.bat new file mode 100644 index 00000000000..ad65e5c4295 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_cl_arm_32bit.bat @@ -0,0 +1,3 @@ +@echo off +cmake -S . -B VS2022_CL_ARM_32BIT -G "Visual Studio 17 2022" -A ARM %* +echo Open VS2022_CL_ARM_32BIT\JoltPhysics.sln to build the project. diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_cl_cross_platform_deterministic.bat b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_cl_cross_platform_deterministic.bat new file mode 100644 index 00000000000..c054c8e8143 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_cl_cross_platform_deterministic.bat @@ -0,0 +1,3 @@ +@echo off +cmake -S . -B VS2022_CL_CPD -G "Visual Studio 17 2022" -A x64 -DCROSS_PLATFORM_DETERMINISTIC=ON %* +echo Open VS2022_CL_CPD\JoltPhysics.sln to build the project. diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_cl_double.bat b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_cl_double.bat new file mode 100644 index 00000000000..52ac1245cfd --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_cl_double.bat @@ -0,0 +1,3 @@ +@echo off +cmake -S . -B VS2022_CL_Double -G "Visual Studio 17 2022" -A x64 -DDOUBLE_PRECISION=ON %* +echo Open VS2022_CL_Double\JoltPhysics.sln to build the project. diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_clang.bat b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_clang.bat new file mode 100644 index 00000000000..1f49861304e --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_clang.bat @@ -0,0 +1,10 @@ +@echo off +cmake -S . -B VS2022_Clang -G "Visual Studio 17 2022" -A x64 -T ClangCL %* +echo: +echo !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +echo Make sure to install: +echo - C++ Clang Compiler for Windows 12.0.0+ +echo - C++ Clang-cl for v143+ build tools (x64/x86) +echo Using the Visual Studio Installer +echo !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +echo Open VS2022_Clang/JoltPhysics.sln to build the project. diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_clang_double.bat b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_clang_double.bat new file mode 100644 index 00000000000..908a893bdba --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_clang_double.bat @@ -0,0 +1,10 @@ +@echo off +cmake -S . -B VS2022_Clang_Double -G "Visual Studio 17 2022" -A x64 -T ClangCL -DDOUBLE_PRECISION=YES %* +echo: +echo !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +echo Make sure to install: +echo - C++ Clang Compiler for Windows 12.0.0+ +echo - C++ Clang-cl for v143+ build tools (x64/x86) +echo Using the Visual Studio Installer +echo !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +echo Open VS2022_Clang_Double/JoltPhysics.sln to build the project. diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_uwp.bat b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_uwp.bat new file mode 100644 index 00000000000..43167bbd3e7 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_uwp.bat @@ -0,0 +1,5 @@ +@echo off +cmake -S . -B VS2022_UWP -G "Visual Studio 17 2022" -DCMAKE_SYSTEM_NAME=WindowsStore -DCMAKE_SYSTEM_VERSION=10.0 %* +echo If cmake failed then be sure to check the "Universal Windows Platform development" checkbox in the Visual Studio Installer +echo Open VS2022_UWP\JoltPhysics.sln to build the project. +echo Note that none of the sample applications are available for the Universal Windows Platform (use cmake_vs2022_cl.bat instead). diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_uwp_arm.bat b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_uwp_arm.bat new file mode 100644 index 00000000000..31651ae33b6 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_uwp_arm.bat @@ -0,0 +1,5 @@ +@echo off +cmake -S . -B VS2022_UWP_ARM -G "Visual Studio 17 2022" -A ARM64 -DCMAKE_SYSTEM_NAME=WindowsStore -DCMAKE_SYSTEM_VERSION=10.0 %* +echo If cmake failed then be sure to check the "Universal Windows Platform development" checkbox in the Visual Studio Installer +echo Open VS2022_UWP_ARM\JoltPhysics.sln to build the project. +echo Note that none of the sample applications are available for the Universal Windows Platform (use cmake_vs2022_cl_arm.bat instead). diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_windows_mingw.sh b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_windows_mingw.sh new file mode 100644 index 00000000000..39d6d28432f --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_windows_mingw.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +if [ -z $1 ] +then + BUILD_TYPE=Debug +else + BUILD_TYPE=$1 + shift +fi + +BUILD_DIR=MinGW_$BUILD_TYPE + +echo Usage: ./cmake_windows_mingw.sh [Configuration] +echo "Possible configurations: Debug (default), Release, Distribution" +echo Generating Makefile for build type \"$BUILD_TYPE\" in folder \"$BUILD_DIR\" + +cmake -S . -B $BUILD_DIR -G "MinGW Makefiles" -DCMAKE_BUILD_TYPE=$BUILD_TYPE "${@}" + +echo Compile by running \"cmake --build $BUILD_DIR -j 8\" diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_xcode_ios.sh b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_xcode_ios.sh new file mode 100644 index 00000000000..65e9aac1004 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_xcode_ios.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +cmake -S . -B XCode_iOS -DTARGET_HELLO_WORLD=OFF -DTARGET_PERFORMANCE_TEST=OFF -DCMAKE_SYSTEM_NAME=iOS -GXcode +open XCode_iOS/JoltPhysics.xcodeproj diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_xcode_macos.sh b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_xcode_macos.sh new file mode 100644 index 00000000000..6dd1488a80d --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_xcode_macos.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +cmake -S . -B XCode_MacOS -GXcode -D"CMAKE_OSX_ARCHITECTURES=x86_64;arm64" +open XCode_MacOS/JoltPhysics.xcodeproj diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/iOS/UnitTestsInfo.plist b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/iOS/UnitTestsInfo.plist new file mode 100644 index 00000000000..84c416f439e --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/iOS/UnitTestsInfo.plist @@ -0,0 +1,34 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleGetInfoString + + CFBundleIconFile + + CFBundleIdentifier + com.joltphysics.unittests + CFBundleInfoDictionaryVersion + 6.0 + CFBundleLongVersionString + 1.0 + CFBundleName + UnitTests + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + CSResourcesFileMapped + + NSHumanReadableCopyright + + + diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/ContributorAgreement.md b/crates/joltc-sys-patched/JoltC/JoltPhysics/ContributorAgreement.md new file mode 100644 index 00000000000..37065f490b2 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/ContributorAgreement.md @@ -0,0 +1,123 @@ +## Fiduciary License Agreement 2.0 + +based on the + +## Individual Contributor Exclusive License Agreement + +## (including the Traditional Patent License OPTION) + +Thank you for your interest in contributing to Jolt Physics ("We" or "Us"). + +The purpose of this contributor agreement ("Agreement") is to clarify and document the rights granted by contributors to Us. + +### 0\. Preamble + +Software is deeply embedded in all aspects of our lives and it is important that it empower, rather than restrict us. Free Software gives everybody the rights to use, understand, adapt and share software. These rights help support other fundamental freedoms like freedom of speech, press and privacy. + +Development of Free Software can follow many patterns. In some cases whole development is handled by a sole programmer or a small group of people. But usually, the creation and maintenance of software is a complex process that requires the contribution of many individuals. This also affects who owns the rights to the software. In the latter case, rights in software are owned jointly by a great number of individuals. + +To tackle this issue some projects require a full copyright assignment to be signed by all contributors. The problem with such assignments is that they often lack checks and balances that would protect the contributors from potential abuse of power from the new copyright holder. + +FSFE’s Fiduciary License Agreement (FLA) was created by the Free Software Foundation Europe e.V. with just that in mind – to concentrate all deciding power within one entity and prevent fragmentation of rights on one hand, while on the other preventing that single entity from abusing its power. The main aim is to ensure that the software covered under the FLA will forever remain Free Software. + +This process only serves for the transfer of economic rights. So-called moral rights (e.g. authors right to be identified as author) remain with the original author(s) and are inalienable. + +### How to use this FLA + +If You are an employee and have created the Contribution as part of your employment, You need to have Your employer approve this Agreement or sign the Entity version of this document. If You do not own the Copyright in the entire work of authorship, any other author of the Contribution should also sign this – in any event, please contact Us at jorrit@jrouwe.nl + +### 1\. Definitions + +**"You"** means the individual Copyright owner who Submits a Contribution to Us. + +**"Legal Entity"** means an entity that is not a natural person. + +**"Affiliate"** means any other Legal Entity that controls, is controlled by, or under common control with that Legal Entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such Legal Entity, whether by contract or otherwise, (ii) ownership of fifty percent (50%) or more of the outstanding shares or securities that vote to elect the management or other persons who direct such Legal Entity or (iii) beneficial ownership of such entity. + +**"Contribution"** means any original work of authorship, including any original modifications or additions to an existing work of authorship, Submitted by You to Us, in which You own the Copyright. + +**"Copyright"** means all rights protecting works of authorship, including copyright, moral and neighboring rights, as appropriate, for the full term of their existence. + +**"Material"** means the software or documentation made available by Us to third parties. When this Agreement covers more than one software project, the Material means the software or documentation to which the Contribution was Submitted. After You Submit the Contribution, it may be included in the Material. + +**"Submit"** means any act by which a Contribution is transferred to Us by You by means of tangible or intangible media, including but not limited to electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, Us, but excluding any transfer that is conspicuously marked or otherwise designated in writing by You as "Not a Contribution." + +**"Documentation"** means any non-software portion of a Contribution. + +### 2\. License grant + +#### 2.1 Copyright license to Us + +Subject to the terms and conditions of this Agreement, You hereby grant to Us a worldwide, royalty-free, exclusive, perpetual and irrevocable (except as stated in Section 8.2) license, with the right to transfer an unlimited number of non-exclusive licenses or to grant sublicenses to third parties, under the Copyright covering the Contribution to use the Contribution by all means, including, but not limited to: + +* publish the Contribution, +* modify the Contribution, +* prepare derivative works based upon or containing the Contribution and/or to combine the Contribution with other Materials, +* reproduce the Contribution in original or modified form, +* distribute, to make the Contribution available to the public, display and publicly perform the Contribution in original or modified form. + +#### 2.2 Moral rights + +Moral Rights remain unaffected to the extent they are recognized and not waivable by applicable law. Notwithstanding, You may add your name to the attribution mechanism customary used in the Materials you Contribute to, such as the header of the source code files of Your Contribution, and We will respect this attribution when using Your Contribution. + +#### 2.3 Copyright license back to You + +Upon such grant of rights to Us, We immediately grant to You a worldwide, royalty-free, non-exclusive, perpetual and irrevocable license, with the right to transfer an unlimited number of non-exclusive licenses or to grant sublicenses to third parties, under the Copyright covering the Contribution to use the Contribution by all means, including, but not limited to: + +* publish the Contribution, +* modify the Contribution, +* prepare derivative works based upon or containing the Contribution and/or to combine the Contribution with other Materials, +* reproduce the Contribution in original or modified form, +* distribute, to make the Contribution available to the public, display and publicly perform the Contribution in original or modified form. + +This license back is limited to the Contribution and does not provide any rights to the Material. + +### 3\. Patents + +#### 3.1 Patent license + +Subject to the terms and conditions of this Agreement You hereby grant to Us and to recipients of Materials distributed by Us a worldwide, royalty-free, non-exclusive, perpetual and irrevocable (except as stated in Section 3.2) patent license, with the right to transfer an unlimited number of non-exclusive licenses or to grant sublicenses to third parties, to make, have made, use, sell, offer for sale, import and otherwise transfer the Contribution and the Contribution in combination with any Material (and portions of such combination). This license applies to all patents owned or controlled by You, whether already acquired or hereafter acquired, that would be infringed by making, having made, using, selling, offering for sale, importing or otherwise transferring of Your Contribution(s) alone or by combination of Your Contribution(s) with any Material. + +#### 3.2 Revocation of patent license + +You reserve the right to revoke the patent license stated in section 3.1 if We make any infringement claim that is targeted at your Contribution and not asserted for a Defensive Purpose. An assertion of claims of the Patents shall be considered for a "Defensive Purpose" if the claims are asserted against an entity that has filed, maintained, threatened, or voluntarily participated in a patent infringement lawsuit against Us or any of Our licensees. + +### 4\. License obligations by Us + +We agree to (sub)license the Contribution or any Materials containing, based on or derived from your Contribution under the terms of any licenses the Free Software Foundation classifies as Free Software License and which are approved by the Open Source Initiative as Open Source licenses. + +More specifically and in strict accordance with the above paragraph, we agree to (sub)license the Contribution or any Materials containing, based on or derived from the Contribution only under the terms of the following license(s) MIT (including any right to adopt any future version of a license if permitted). + +We agree to license patents owned or controlled by You only to the extent necessary to (sub)license Your Contribution(s) and the combination of Your Contribution(s) with the Material under the terms of any licenses the Free Software Foundation classifies as Free Software licenses and which are approved by the Open Source Initiative as Open Source licenses.. + +### 5. Disclaimer + +THE CONTRIBUTION IS PROVIDED "AS IS". MORE PARTICULARLY, ALL EXPRESS OR IMPLIED WARRANTIES INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTY OF SATISFACTORY QUALITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE EXPRESSLY DISCLAIMED BY YOU TO US AND BY US TO YOU. TO THE EXTENT THAT ANY SUCH WARRANTIES CANNOT BE DISCLAIMED, SUCH WARRANTY IS LIMITED IN DURATION AND EXTENT TO THE MINIMUM PERIOD AND EXTENT PERMITTED BY LAW. + +### 6. Consequential damage waiver + +TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT WILL YOU OR WE BE LIABLE FOR ANY LOSS OF PROFITS, LOSS OF ANTICIPATED SAVINGS, LOSS OF DATA, INDIRECT, SPECIAL, INCIDENTAL, CONSEQUENTIAL AND EXEMPLARY DAMAGES ARISING OUT OF THIS AGREEMENT REGARDLESS OF THE LEGAL OR EQUITABLE THEORY (CONTRACT, TORT OR OTHERWISE) UPON WHICH THE CLAIM IS BASED. + +### 7. Approximation of disclaimer and damage waiver + +IF THE DISCLAIMER AND DAMAGE WAIVER MENTIONED IN SECTION 5. AND SECTION 6. CANNOT BE GIVEN LEGAL EFFECT UNDER APPLICABLE LOCAL LAW, REVIEWING COURTS SHALL APPLY LOCAL LAW THAT MOST CLOSELY APPROXIMATES AN ABSOLUTE WAIVER OF ALL CIVIL OR CONTRACTUAL LIABILITY IN CONNECTION WITH THE CONTRIBUTION. + +### 8. Term + +8.1 This Agreement shall come into effect upon Your acceptance of the terms and conditions. + +8.2 This Agreement shall apply for the term of the copyright and patents licensed here. However, You shall have the right to terminate the Agreement if We do not fulfill the obligations as set forth in Section 4. Such termination must be made in writing. + +8.3 In the event of a termination of this Agreement Sections 5., 6., 7., 8., and 9. shall survive such termination and shall remain in full force thereafter. For the avoidance of doubt, Free and Open Source Software (sub)licenses that have already been granted for Contributions at the date of the termination shall remain in full force after the termination of this Agreement. + +### 9. Miscellaneous + +9.1 This Agreement and all disputes, claims, actions, suits or other proceedings arising out of this agreement or relating in any way to it shall be governed by the laws of Netherlands excluding its private international law provisions. + +9.2 This Agreement sets out the entire agreement between You and Us for Your Contributions to Us and overrides all other agreements or understandings. + +9.3 In case of Your death, this agreement shall continue with Your heirs. In case of more than one heir, all heirs must exercise their rights through a commonly authorized person. + +9.4 If any provision of this Agreement is found void and unenforceable, such provision will be replaced to the extent possible with a provision that comes closest to the meaning of the original provision and that is enforceable. The terms and conditions set forth in this Agreement shall apply notwithstanding any failure of essential purpose of this Agreement or any limited remedy to the maximum extent possible under law. + +9.5 You agree to notify Us of any facts or circumstances of which you become aware that would make this Agreement inaccurate in any respect. diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/APIChanges.md b/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/APIChanges.md new file mode 100644 index 00000000000..874050962c7 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/APIChanges.md @@ -0,0 +1,111 @@ +# Breaking API Changes + +This document lists all breaking API changes by date and by release tag. Note that not all API changes are listed here, trivial changes (that cause a compile error and require an obvious fix) are not listed. + +Changes that make some state saved through SaveBinaryState from a prior version of the library unreadable by the new version is marked as *SBS*. See [Saving Shapes](https://jrouwe.github.io/JoltPhysics/#saving-shapes) for further information. + +## Changes between v4.0.2 and v5.0.0 + +* 20240327 - *SBS* - SoftBodySharedSettings::CreateEdges was renamed to CreateConstraints and can now also create shear and bend constraints. This also breaks the serialization format for SoftBodySharedSettings. (8e4bf3fa03f59cff6af7394d69cdf62abaf7a1d2) +* 20240310 - *SBS* - Soft body skinned constraints now use a sphere as backstop instead of an infinite plane. This also breaks the serialization format for SoftBodySharedSettings. (17db6d3f245d2198319c3787f62498fe5935b7c8) +* 20240225 - *SBS* - Changes were made to SoftBodySharedSettings that break the binary serialization format of that class. (277b818ffefed4f15477ff1e6d0cc07065899903) +* 20240223 - Added ConvexShape::ESupportMode::Default. If you have custom convex shapes you need to handle this in ConvexShape::GetSupportFunction. (0f67cc2915c5e34a4a38480580dad73888a1952e) +* 20240216 - Restriction angular motion using EAllowedDOFs now works in world space rather than in local space. This change was made to be more in line with other physics engines and to fix some issues with constraints. If you need the old behavior then copy [this](https://github.com/jrouwe/JoltPhysics/blob/9631e217e54b8492ac36471f2aa966df40d6c2ad/Jolt/Physics/Body/MotionProperties.cpp#L33-L118) code into your own code base and call MotionProperties::SetInverseInertia(diagonal, rotation) where diagonal is called mInvInertiaDiagonal and rotation is called mInertiaRotation in the code snippet. (191536d51d71ee29147205aa09d1acab52789e5f) +* 20240210 - Fixed spelling error EPathRotationConstraintType::ConstaintToPath to EPathRotationConstraintType::ConstrainToPath (6c095bbf7906b01f427b52d43212f5ebf760fc81) +* 20240210 - Added extra parameter fraction hint to PathConstraintPath::GetClosestPoint. This can be used to speed up the search along the curve and to disambiguate fractions in case a path reaches the same point multiple times (i.e. a figure-8) (b91e729e6e2c34df16cc03f5ac3b3f6d3fa8b762) +* 20240203 - Longitudinal friction impulse for wheeled/tracked vehicles could become much higher than the calculated max because each iteration it was clamped to the max friction impulse which meant the total friction impulse could be PhysicsSettings::mNumVelocitySteps times too high. In case this breaks your vehicle, the new max tire impulse callback can be used to restore the old behavior, see [the vehicle constraint test](https://github.com/jrouwe/JoltPhysics/blob/a456b244aa2ad2ce0a8124d27823377ed0b1c4b4/Samples/Tests/Vehicle/VehicleConstraintTest.cpp#L156-L164). (a456b244aa2ad2ce0a8124d27823377ed0b1c4b4) +* 20240120 - *SBS* - Implemented enhanced internal edge removal algorithm. This breaks the binary serialization format for BodyCreationSettings. (94c1ad811b95c72f4d3bb6841c73c1c3461caa91) +* 20240113 - VehicleConstraint::CombineFunction now calculates both longitudinal and lateral friction in 1 call so there can be dependencies between the two. (d6ed5b3e7b22904af555088b6ae4770f8fb0e00f) +* 20240105 - CharacterVirtual will now receive an OnContactAdded callback when it collides with a sensor (but will have no further interaction). You may need to update the logic in your CharacterContactListener to ignore those contacts. (fb778c568d3ba14556559324671ffec172957f5c) +* 20240101 - Renamed SensorDetectsStatic to CollideKinematicVsNonDynamic and made it work for non-sensors. This means that kinematic bodies can now get collision callbacks when they collide with other static / kinematic objects. It can also affect the order in which bodies are passed in the ContactListener::OnContactValidate callback. (2d607c4161a65201d66558a2cc76d1265aea527e) +* 20231220 - *SBS* - Added ability to enable gyroscopic forces on BodyCreationSettings. This breaks the binary serialization format for this class. (9d7748eaa91341adc17554f32bf991bfed04e47e) +* 20231219 - *SBS* - Added a 'swing type' attribute to SixDOFConstraint and SwingTwistConstraint. This breaks the binary serialization format. (41016256e2cf1262ec05cff3cfa7645668ee0bf0) +* 20231208 - Changed the meaning of Constraint::mNumVelocity/PositionStepsOverride. Before the number of steps would be the maximum of all constraints and the default value, now an overridden value of 0 means that the constraint uses the default value, otherwise it will use the value as specified. This means that if all constraints in an island have a lower value than the default, we will now use the lower value instead of the default. (0771808a03b850d16f1c64156f0aee827ca3706b) +* 20231208 - *SBS* - Bodies can now also override the default number of solver iterations. This breaks the binary serialization format. (0771808a03b850d16f1c64156f0aee827ca3706b) +* 20231203 - VehicleConstraint::CombineFunction got two additional parameters to identify which wheel is requesting friction. (8d80155f93d0d0c3ffe3dd46550650b9c830d304) + +## Changes between v4.0.0 and v4.0.2 + +* No breaking changes. + +## Changes between v3.0.1 and v4.0.0 + +* 20231003 - *SBS* - Bug fix in serialization of SoftBodySharedSettings breaks binary serialization format. (ccb250747eee4dedebfa02d950775478fb52f786) +* 20230914 - Removed GetProcessorTicksPerSecond as it was not correctly implemented for all platforms. (d44f4bad0872075d5cef2779742c89203d4f4488) +* 20230819 - *SBS* - RagdollSettings got the ability to have constraints that do not follow the skeleton. This changes the binary serialization format for this class. (08fc49d2d7abfa1a69e21971785d37724c748bb6) +* 20230807 - Renamed ContactSettings::mRelativeSurfaceVelocity to mRelativeLinearSurfaceVelocity. (76b809ddb1abf96641acc587fffa70101323d323) +* 20230807 - *SBS* - PhysicsScene is now able to load/save soft bodies. This changes the binary serialization format. (779ba3673beebdc4021842516f4ff6aa7c1e09b4) +* 20230805 - Body::SaveState and MotionProperties::SaveState now only save the state that can be changed by the simulation. Configuration properties like friction, restitution etc. must be saved by the user if desired. (7ff50429abd53f1914fd25a9e80ff47f22bc9f0e) +* 20230801 - *SBS* - Constraint priority was added to all constraints which changes the binary serialization format. (e341bb3e959460fbe196032095c1ab0346d7e746) +* 20230704 - *SBS* - A new flag was added to BodyCreationSettings that changes the binary serialization format. (2dd3a033a41e422eb470484029324cc9bbaf0825) +* 20230629 - Fix for engine RPM being much higher than wheel RPM when measured at clutch. Before we were ignoring bake and wheel torques in engine RPM calculation. Now they're much closer but this unfortunately means that the simulation of the vehicle has changed and mainly the engine torque and clutch strength need to be re-tweaked. (b40090766c545a68dccfac76cde8c6345ca626a6) +* 20230623 - The parameter inIntegrationSubSteps was removed from PhysicsSystem::Update because more and more features didn't support it. If you were using it multiply inCollisionSteps with the value of inIntegrationSubSteps to get roughly the same behavior. (8fcc7a78ec051b215bf13b037b9f975baa803b6f) +* 20230618 - *SBS* - A new flag was added to BodyCreationSettings that changes the binary serialization format. (107b70c7585909f0757a62c318261a18d670ff97) +* 20230610 - A bug was fixed that causes the vehicle suspension to be weaker when driving over low mass objects. This also changes suspension behavior a bit when driving over static objects. (44b82e395697ea553574df3cd806ffe264bfa5c4) +* 20230609 - *SBS* - The MotorcycleController lean controller is now a full PID controller. This changes binary serialization format. (70e7bb3e5808dabc17ee38fb823fbfa7e9140a91) +* 20230609 - *SBS* - VehicleConstraint uses the new SpringSettings class as a member which contains the mFrequency and mDamping members. This requires minor code changes. (0da97d8f3345f14c5b4b0ee3571c05832c556f98) +* 20230609 - *SBS* - DistanceConstraintSettings, SliderConstraintSettings and MotorSettings now use the new SpringSettings class as a member which contains the mFrequency and mDamping members. This requires minor code changes. (3cabc057c1267fde288c1ab2a23076702c71eb79) +* 20230520 - A bug was fixed in CharacterVirtual that makes mPenetrationRecoverySpeed behave according to the documentation (1 = fully resolve collision in 1 update). With the bug the recovery was too little. If you want the penetration recovery to work as before with the bug multiply it by 1 / delta_time. (8dd93317d66a9a72d3afeff4ecb17c257a7e9d91) +* 20230420 - To support compiling Jolt as a shared library, the RTTI macros were changed to be able to specify if a symbol should be exported or not. If you're using Jolt's RTTI system in your own project you need to change e.g. JPH_DECLARE_RTTI_VIRTUAL(XXX) to JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, XXX). (d2f1d97004d036c6f759203c42e264e401472037) + +## Changes between v2.0.1 and v3.0.0 + +* 20230331 - *SBS* - Vehicle wheels now support specifying the steering axis and wheel forward and up axis separately. This breaks the serialization format and requires setting extra properties on the wheels. (4269d8bbc77b889552a842c2e8476ba7ffc6b9a1) +* 20230328 - Vehicle now supports suspension under an angle. The behavior of the suspension, even if it is under 90 degrees with the vehicle body, changed so this may require tweaking the spring constants. (172a99c718bded5faa169ac440517286684fa2f0) +* 20230316 - The signature of ShapeFilter changed and the ShouldCollide function is no longer called for triangles inside a mesh/heightfield shape (you can use CollisionCollector::AddHit to filter per triangle). The previous implementation didn't pass in enough context for the application to fully determine which sub shapes were colliding. See [#473](https://github.com/jrouwe/JoltPhysics/discussions/473) for more information. (bc4fa997f15f2953dc87ee5c1ba51ecf2077c287) +* 20230313 - VehicleCollisionTester::Collide parameter outSuspensionLength was returning suspension length + wheel radius, now it returns the suspension length. If you have your own implementation of VehicleCollisionTester you need to update your code. (fcd9cb0f1677709e30951f2748aefd5f72ffdae1) +* 20230212 - Sensors are now able to detect other Sensors, make sure you put sensors in an ObjectLayer that doesn't collide with other sensors if you want to preserve the old behavior. (a76f5891ee429ae4fcde659c19f1eb769f9d8a21) +* 20230205 - *SBS* - Added 'IsSensor' and 'UseManifoldReduction' to BodyCreationSettings::SaveBinaryState. (8f6f210f53fc71e43760e20aeb2eae28ea168f4b) +* 20221231 - ObjectLayerPairFilter and ObjectVsBroadPhaseLayerFilter are now objects instead of function pointers. (4315ad53e354f094f753664fcf7a52870f6915e4) +* 20221208 - ContactListener::OnContactValidate is reporting collisions relative to inBaseOffset. Add this to the contact point if you want world space positions. (428611482825e369e60e0a5daf17c69a4d0f2a6f) +* 20221204 - Changes related to double precision support for positions (a2c1c22059fa031faf0208258e654bcff79a63e4) + * In many places in the public API Vec3 has been replaced by RVec3 (a Vec3 of Real values which can either be double or float depending on if JPH_DOUBLE_PRECISION is defined). In the same way RMat44 replaces Mat44. When compiling in single precision mode (the default) you should not notice a change. + * Shape::GetSubmergedVolume now takes a plane that's relative to inCenterOfMassTransform instead of one in world space + * Many of the NarrowPhaseQuery and TransformedShape collision queries now have a 'base offset' that you need to specify. Go to [Big Worlds](https://jrouwe.github.io/JoltPhysics/#big-worlds) for more info. + * The NarrowPhaseQuery/TransformedShape CastRay / CastShape functions now take a RRayCast / RShapeCast struct as input. When compiling in single precision mode this is the same as a RayCast or ShapeCast so only the type name needs to be updated. + * If you implement your own TempAllocator and want to compile in double precision, make sure you align to JPH_RVECTOR_ALIGNMENT bytes (instead of 16) + * The SkeletonPose got a 'root offset' member, this means that the ragdoll will now make the joint transform of the first body zero and put that offset in the 'root offset'. + * ContactManifold now stores the contacts relative to mBaseOffset, the arrays containing the contact points have been renamed from mWorldSpaceContactPointsOn1/2 to mRelativeContactPointsOn1/2 to reflect this. + * The DebugRenderer::DrawLine function now takes RVec3Arg parameters instead of Float3 parameters. + * The format of a recording recorded with DebugRendererRecorder has changed, this invalidates any prior recordings. +* 20221128 - MotionProperties::SetMotionQuality has been removed because changing it for an active body could cause crashes. Use BodyInterface::SetMotionQuality instead. (64802d163a7336e60916365ad9bce764cec4ca70) + +## Changes between v1.1.0 and v2.0.0 + +* 20221027 - *SBS* (vehicles only) - Rewrote engine model for wheeled vehicle. Before engine inertia was only used when the clutch was pressed, now it is always used, so you may want to use a lower value. The way torque is distributed over the wheels has also changed and may require tweaking the vehicle parameters. (5ac751cee9afcc097fd4f884308f5e4dc9fdaeaf) +* 20220903 - *SBS* - Added overrides for number of position/velocity solver iterations. Only affects serialization. (38ec33942ead4968a83409bd13d868f60e6397c4) +* 20220826 - *SBS* - Removed FixedConstraintSettings and SliderConstraintSettings SetPoint functions. If you were calling this function replace it by setting mAutoDetectPoint = true. (d16a0b05bfeed42b1618e3774a9c953e6922d22b) +* 20220614 - It is now possible to override the memory allocator, register the default using RegisterDefaultAllocator(). This means that the public API now takes STL containers that use a custom memory allocator so use Array instead of vector, UnorderedMap instead of unordered_map etc. If you're using placement new, add ```::``` in front of new. Define JPH_DISABLE_CUSTOM_ALLOCATOR to disable this new behavior (b68097f582148d6f66c18a6ff95c5ca9b40b48cc) +* 20220606 - *SBS* - The slider constraint now has frequency and damping for its limits (09d6d9d51c46fbd159bf98abfd43cc639f6c0403) +* 20220606 - *SBS* - The rack and pinion and gear constraints were added (09d6d9d51c46fbd159bf98abfd43cc639f6c0403) +* 20220517 - Note: Superseded by d16a0b05bfeed42b1618e3774a9c953e6922d22b. When constructing a FixedConstraint you now need to call FixedConstraintSettings::SetPoint to configure the point where the bodies attach (4f7c925c31f39eda1d8d68e4e72456b5def93d9b) +* 20220516 - Constraint::GetType was renamed to GetSubType, a new GetType function was introduced (3e2151a009e8f11ca724754b2bd25e14d2654fb6) +* 20220516 - *SBS* - Added possibility to save the current state of the physics world as a scene (3e2151a009e8f11ca724754b2bd25e14d2654fb6) +* 20220510 - Factory::sInstance must now be allocated by the application prior to calling RegisterTypes() and has changed to a pointer (3ca62973dae7cda7a9ceece698438a45b9ad1433) +* 20220503 - Unused function SerializableObject::OnLoaded was removed (388d47254a236c053a472e54c10b264765badc09) +* 20220502 - ContactConstraintManager::CombineFunction has additional parameters: the SubShapeIDs from both bodies (6b873563739dfd3d77263c2c50af2f3f418ec15b) +* 20220415 - Removed Body::GetDebugName / SetDebugName, keep this info in a lookaside table if you need it (6db4d3beac6760e55f65102db00f93dfbc56ac26) +* 20220406 - Renamed CollisionDispatch::sCastShapeVsShape to sCastShapeVsShapeLocalSpace (6ba21f50dcf17bd506080ec30759724a7f3097d8) +* 20220327 - Changed the default include path, ```#include ``` must be replaced by ```#include ``` (06e9d17d385814cd24d3b77d689c0a29d854e194) +* 20220318 - Added support for SSE2. If you want to use later versions of SSE make sure you have JPH_USE_SSE4_1 and JPH_USE_SSE4_2 defined (28f363856a007d03f657e46e8f6d90ccd7c6487a) +* 20220303 - Note: Partially superseded by d16a0b05bfeed42b1618e3774a9c953e6922d22b. When constructing a SliderConstraint you now need to call SliderConstraintSettings::SetPoint to configure the point where the bodies attach. Also replace mSliderAxis = x with SetSliderAxis(x) (5a327ec182d0436d435c62d0bccb4e76c6324659) +* 20220228 - PointConstraint::mCommonPoint is now mPoint1 / mPoint2. Replace mCommonPoint = x with mPoint1 = mPoint2 = x (066dfb8940ba3e7dbf8ed47e9a1eeb194730e04b) +* 20220226 - ObjectToBroadPhaseLayer and BroadPhaseLayerToString changed to BroadPhaseLayerInterface, this makes mapping a broadphase layer to an object layer more flexible (36dd3f8c8c31ef1aeb7585b2b615c23bc8b76f13) +* 20220222 - Shape and body user data changed from void * / uint32 to uint64 (14e062ac96abd571c6eff5e40b1df4d8b2333f55) + +## Changes between v1.0.0 and v1.1.0 + +* No breaking changes. + +## Changes between v0.0.0 and v1.0.0 + +* 20220107 - PhysicsSettings::mBodyPairCacheCosMaxDeltaRotation was renamed to mBodyPairCacheCosMaxDeltaRotationDiv2 +* 20211219 - *SBS* - Now storing 3 components for a Vec3 instead of 4 in SaveBinaryState (23c1b9d9029d74076c0549c8779b3b5ac2179ea3) +* 20211212 - Removed StatCollector (92a117e0f05a08de154e86d3cd0b354783aa5593) +* 20210711 - HeightFieldShapeSettings::mBlockSize is subdivided one more time at run-time, so this is effectively 2x the block size (2aa3b443bf71785616f3140c32e6a04c49516535) +* 20211106 - Mutex class now has its own implementation on Platform Blue, users must implement the JPH_PLATFORM_BLUE_MUTEX_* functions (a61dc67503a87ef0e190f7fb31d495ac51aa43de) +* 20211019 - ShapeCast::mShape no longer keeps a reference, the caller is responsible for keeping the reference now (e2bbdda9110b083b49ba323f8fd0d88c19847c2e) +* 20211004 - Removed RTTI from Shape class, use Shape::GetType / GetSubType now (6d5cafd53501c2c1e313f1b1f29d5161db074fd5) +* 20210930 - Changed RestoreMaterialState and RestoreSubShapeState to use pointers instead of vectors to allow loading shapes with fewer memory allocations (b8953791f35a91fcd12568c7dc4cc2f68f40fb3f) +* 20210918 - PhysicsSystem::Init takes an extra parameter to specify the amount of mutexes to use (ef371411af878023f062b9930db09f17411f01ba) +* 20210827 - BroadPhaseLayerPairFilter was changed to ObjectVsBroadPhaseLayerFilter to avoid testing too many layers during collision queries (33883574bbc6fe208a4b62054d00b582872da6f4) diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Architecture.md b/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Architecture.md new file mode 100644 index 00000000000..a3524bd98a6 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Architecture.md @@ -0,0 +1,755 @@ +[TOC] + +# Architecture of Jolt Physics {#architecture-jolt-physics} + +For demos and videos go to the [Samples](Samples.md) section. + +# Bodies {#bodies} + +We use a pretty traditional physics engine setup, so \ref Body "bodies" in our simulation are objects which have attached collision \ref Shape "shapes" + +## Types {#body-types} + +Bodies can either be: +- [static](@ref EMotionType) (not moving or simulating) +- [dynamic](@ref EMotionType) (moved by forces) or +- [kinematic](@ref EMotionType) (moved by velocities only). + +Moving bodies have a [MotionProperties](@ref MotionProperties) object that contains information about the movement of the object. Static bodies do not have this to save space (but they can be configured to have it if a static body needs to become dynamic during its lifetime by setting [BodyCreationSettings::mAllowDynamicOrKinematic](@ref BodyCreationSettings::mAllowDynamicOrKinematic)). + +## Creating Bodies {#creating-bodies} + +Bodies are inserted into the [PhysicsSystem](@ref PhysicsSystem) and interacted with through the [BodyInterface](@ref BodyInterface). + +## Multithreaded Access + +Jolt is designed to be accessed from multiple threads so the body interface comes in two flavors: A locking and a non-locking variant. The locking variant uses a mutex array (a fixed size array of mutexes, bodies are associated with a mutex through hashing and multiple bodies use the same mutex, see [MutexArray](@ref MutexArray)) to prevent concurrent access to the same body. The non-locking variant doesn't use mutexes, so requires the user to be careful. + +In general, body ID's ([BodyID](@ref BodyID)) are used to refer to bodies. You can access a body through the following construct: + + JPH::BodyLockInterface lock_interface = physics_system.GetBodyLockInterface(); // Or GetBodyLockInterfaceNoLock + JPH::BodyID body_id = ...; // Obtain ID to body + + // Scoped lock + { + JPH::BodyLockRead lock(lock_interface, body_id); + if (lock.Succeeded()) // body_id may no longer be valid + { + const JPH::Body &body = lock.GetBody(); + + // Do something with body + ... + } + } + +When another thread has removed the body between the time the body ID was obtained and the lock, the lock will fail. While the lock is taken, other threads cannot modify the body, so it is safe to work with it. Each body ID contains a sequence number, so body ID's will only be reused after many add/remove cycles. To write to a body use [BodyLockWrite](@ref BodyLockWrite). + +You cannot use BodyLockRead to lock multiple bodies (if two threads lock the same bodies in opposite order you'll get a deadlock). Use [BodyLockMultiRead](@ref BodyLockMultiRead) or [BodyLockMultiWrite](@ref BodyLockMultiWrite) to lock them in a consistent order. + +Note that a lot of convenience functions are exposed through the BodyInterface, but not all functionality is available, so you may need to lock the body to get the pointer and then call the function directly on the body. + +## Single Threaded Access {#single-threaded-access} + +If you're only accessing the physics system from a single thread, you can use Body pointers instead of BodyID's. In this case you can also use the non-locking variant of the body interface. + +Note that there are still some restrictions: + +* You cannot read from / write to bodies or constraints while PhysicsSystem::Update is running. As soon as the Update starts, all body / constraint mutexes are locked. +* Collision callbacks (see ContactListener) are called from within the PhysicsSystem::Update call from multiple threads. You can only read the body data during a callback. +* Activation callbacks (see BodyActivationListener) are called in the same way. Again you should only read the body during the callback and not make any modifications. +* Step callbacks (see PhysicsStepListener) are also called from PhysicsSystem::Update from multiple threads. You're responsible for making sure that there are no race conditions. In a step listener you can read/write bodies or constraints but you cannot add/remove them. + +If you are accessing the physics system from multiple threads, you should probably use BodyID's and the locking variant of the body interface. It is however still possible to use Body pointers if you're really careful. E.g. if there is a clear owner of a Body and you ensure that this owner does not read/write state during PhysicsSystem::Update or while other threads are reading the Body there will not be any race conditions. + +## Shapes {#shapes} + +Each body has a shape attached that determines the collision volume. The following shapes are available (in order of computational complexity): + +* [SphereShape](@ref SphereShape) - A sphere centered around zero. +* [BoxShape](@ref BoxShape) - A box centered around zero. +* [CapsuleShape](@ref CapsuleShape) - A capsule centered around zero. +* [TaperedCapsuleShape](@ref TaperedCapsuleShape) - A capsule with different radii at the bottom and top. +* [CylinderShape](@ref CylinderShape) - A cylinder shape. Note that cylinders are the least stable of all shapes, so use another shape if possible. +* [ConvexHullShape](@ref ConvexHullShape) - A convex hull defined by a set of points. +* [StaticCompoundShape](@ref StaticCompoundShape) - A shape containing other shapes. This shape is constructed once and cannot be changed afterwards. Child shapes are organized in a tree to speed up collision detection. +* [MutableCompoundShape](@ref MutableCompoundShape) - A shape containing other shapes. This shape can be constructed/changed at runtime and trades construction time for runtime performance. Child shapes are organized in a list to make modification easy. +* [MeshShape](@ref MeshShape) - A shape consisting of triangles. They are mostly used for static geometry. +* [HeightFieldShape](@ref HeightFieldShape) - A shape consisting of NxN points that define the height at each point, very suitable for representing hilly terrain. Any body that uses this shape needs to be static. + +Next to this there are a number of decorator shapes that change the behavior of their children: + +* [ScaledShape](@ref ScaledShape) - This shape can scale a child shape. Note that if a shape is rotated first and then scaled, you can introduce shearing which is not supported by the library. +* [RotatedTranslatedShape](@ref RotatedTranslatedShape) - This shape can rotate and translate a child shape, it can e.g. be used to offset a sphere from the origin. +* [OffsetCenterOfMassShape](@ref OffsetCenterOfMassShape) - This shape does not change its child shape but it does shift the calculated center of mass for that shape. It allows you to e.g. shift the center of mass of a vehicle down to improve its handling. + +### Dynamic Mesh Shapes {#dynamic-mesh-shapes} + +Meshes are usually static, but they can be made kinematic or dynamic provided that they don't collide with other mesh- or heightfield shapes (an assert will trigger when this happens and the collision will be ignored). + +Mesh shapes also cannot calculate their mass and inertia, so when you want a dynamic mesh, you need to provide these yourself by setting BodyCreationSettings::mOverrideMassProperties = EOverrideMassProperties::MassAndInertiaProvided and supplying the mass and inertia in BodyCreationSettings::mMassPropertiesOverride. + +An example can be found [here](https://github.com/jrouwe/JoltPhysics/blob/master/Samples/Tests/General/DynamicMeshTest.cpp). + +Note that you should try to avoid dynamic mesh shapes as they are fairly expensive to simulate. Also, mesh shapes don't have a clear inside/outside so a mesh is only considered to be colliding when one of its triangles intersect with the other object. This can result in objects getting stuck inside the mesh without knowing which way is out. + +### Creating Shapes {#creating-shapes} + +Simple shapes like spheres and boxes can be constructed immediately by simply new-ing them. Other shapes need to be converted into an optimized format in order to be usable in the physics simulation. The uncooked data is usually stored in a [ShapeSettings](@ref ShapeSettings) object and then converted to cooked format by a [Create](@ref ShapeSettings::Create) function that returns a [Result](@ref Result) object that indicates success or failure and provides the cooked object. + +Creating a convex hull for example looks like: + + // Shapes are refcounted and can be shared between bodies + JPH::Ref shape; + + // The ShapeSettings object is only required for building the shape, all information is copied into the Shape class + { + // Create an array of vertices + JPH::Array vertices = { ... }; + + // Create the settings object for a convex hull + JPH::ConvexHullShapeSettings settings(vertices, JPH::cDefaultConvexRadius); + + // Create shape + JPH::Shape::ShapeResult result = settings.Create(); + if (result.IsValid()) + shape = result.Get(); + else + ... // Error handling + } + +Note that after you call Create, the shape is cached and ShapeSettings keeps a reference to your shape (see @ref memory-management). If you call Create again, the same shape will be returned regardless of what changed to the settings object (unless you call [ClearCachedResult](@ref ShapeSettings::ClearCachedResult) to clear the cache). + +### Saving Shapes {#saving-shapes} + +There are two ways of serializing data: + +* The uncooked data can be serialized using the [ObjectStream](@ref ObjectStream) system (either in [binary](@ref ObjectStreamBinaryOut) or in [text](@ref ObjectStreamTextOut) format), data stored in this way is likely to be compatible with future versions of the library (although there is no 100% guarantee of this). +* The cooked data can be serialized using the [SaveBinaryState](@ref Shape::SaveBinaryState) interface that various objects provide. Data stored in this way is optimized for simulation performance and loading speed but is very likely to change between versions of the library, so this should never be your primary data format. + +An example of saving a shape in binary format: + + // Create a sphere of radius 1 + JPH::Ref sphere = new JPH::SphereShape(1.0f); + + // For this example we'll be saving the shape in a STL string stream, but if you implement StreamOut you don't have to use STL. + stringstream data; + JPH::StreamOutWrapper stream_out(data); + + // Save the shape (note this function handles CompoundShape too). + // The maps are there to avoid saving the same shape twice (it will assign an ID to each shape the first time it encounters them). + // If you don't want certain shapes to be saved, add them to the map and give them an ID. + // You can save many shapes to the same stream by repeatedly calling SaveWithChildren on different shapes. + JPH::Shape::ShapeToIDMap shape_to_id; + JPH::Shape::MaterialToIDMap material_to_id; + sphere->SaveWithChildren(stream_out, shape_to_id, material_to_id); + + // Wrap the STL stream in a StreamIn + JPH::StreamInWrapper stream_in(data); + + // Load the shape + // If you have assigned custom ID's on save, you need to ensure that the shapes exist in this map on restore too. + JPH::Shape::IDToShapeMap id_to_shape; + JPH::Shape::IDToMaterialMap id_to_material; + JPH::Shape::ShapeResult result = JPH::Shape::sRestoreWithChildren(stream_in, id_to_shape, id_to_material); + + JPH::Ref restored_shape; + if (result.IsValid()) + restored_shape = result.Get(); + else + ... // Error handling + +As the library does not offer an exporter from content creation packages and since most games will have their own content pipeline, we encourage you to store data in your own format, cook data while cooking the game data and store the result using the SaveBinaryState interface (and provide a way to force a re-cook when the library is updated). + +### Convex Radius {#convex-radius} + +In order to speed up the collision detection system, all convex shapes use a convex radius. The provided shape will first be shrunken by the convex radius and then inflated again by the same amount, resulting in a rounded off shape: + +![In this example a box (green) was created with a fairly large convex radius. The shape is shrunken first (dashed green line) and then inflated again equally on all sides. The resulting shape as seen by the collision detection system is shown in blue. A larger convex radius results in better performance but a less accurate simulation. A convex radius of 0 is allowed.](Images/ConvexRadius.jpg) + +### Center of Mass {#center-of-mass} + +__Beware: When a shape is created, it will automatically recenter itself around its center of mass.__ The center of mass can be obtained by calling [Shape::GetCenterOfMass](@ref Shape::GetCenterOfMass) and most functions operate in this Center of Mass (COM) space. Some functions work in the original space the shape was created in, they usually have World Space (WS) or Shape Space (SS) in their name (or documentation). + +![Shape Center of Mass](Images/ShapeCenterOfMass.jpg) + +As an example, say we create a box and then translate it: + + // Create box of 2x2x2 m (you specify half the side) + JPH::BoxShapeSettings box(JPH::Vec3(1, 1, 1)); + JPH::Ref box_shape = box.Create().Get(); + + // Offset it by 10 m + JPH::RotatedTranslatedShapeSettings translated_box(JPH::Vec3(10, 0, 0), JPH::Quat::sIdentity(), box_shape); + JPH::Ref translated_box_shape = translated_box.Create().Get(); + + // Cast a ray against the offset box (WRONG!) + JPH::RayCast ray; + ray.mOrigin = JPH::Vec3(10, 2, 0); + ray.mDirection = JPH::Vec3(0, -2, 0); + + // Cast ray + JPH::RayCastResult hit; + bool had_hit = translated_box_shape->CastRay(ray, JPH::SubShapeIDCreator(), hit); + JPH_ASSERT(!had_hit); // There's no hit because we did not correct for COM! + + // Convert the ray to center of mass space for the shape (CORRECT!) + ray.mOrigin -= translated_box_shape->GetCenterOfMass(); + + // Cast ray + had_hit = translated_box_shape->CastRay(ray, JPH::SubShapeIDCreator(), hit); + JPH_ASSERT(had_hit); // Ray was in COM space, now there's a hit! + +In the same way calling: + + translated_box_shape->GetLocalBounds(); + +will return a box of size 2x2x2 centered around the origin, so in order to get it back to the space in which it was originally created you need to offset the bounding box: + + JPH::AABox shape_bounds = translated_box_shape->GetLocalBounds(); + shape_bounds.Translate(translated_box_shape->GetCenterOfMass()); + JPH_ASSERT(shape_bounds == JPH::AABox(JPH::Vec3(9, -1, -1), JPH::Vec3(11, 1, 1))); // Now we have the box relative to how we created it + +Note that when you work with interface of [BroadPhaseQuery](@ref BroadPhaseQuery), [NarrowPhaseQuery](@ref NarrowPhaseQuery) or [TransformedShape](@ref TransformedShape) this transformation is done for you. + +### Creating Custom Shapes {#creating-custom-shapes} + +If the defined Shape classes are not sufficient, or if your application can make a more efficient implementation because it has specific domain knowledge, it is possible to create a custom collision shape: + +* Derive a new class from Shape (e.g. MyShape). If your shape is convex you can consider deriving from ConvexShape, if it contains multiple sub shapes you can derive from CompoundShape or if it wraps a single other shape it can be derived from DecoratedShape. +* Create a settings class that configures your shape (e.g. MyShapeSettings) and inherit it from the corresponding settings class (e.g. ShapeSettings, CompoundShapeSettings or DecoratedShapeSettings). +* Override the ```MyShapeSettings::Create``` function to construct an instance of MyShape. +* If you want to serialize the settings class, register it with the factory: ```Factory::sInstance->Register(RTTI_OF(MyShapeSettings))``` +* If you inherited from Shape you need to select a shape type, use e.g. ```EShapeType::User1``` +* In all cases you will need to specify a sub shape type, use e.g. ```EShapeSubType::User1``` +* If you inherited from ConvexShape you can also specify a convex sub shape type, e.g. ```EShapeSubType::UserConvex1```, in which case you don't need to implement or register the collision detection functions mentioned below. +* Implement the virtual functions that your selected base class exposes. Some functions could be implemented as a dummy if you don't care about the functionality, e.g. if you don't care about buoyancy then GetSubmergedVolume does not need to be implemented. +* Create a ```MyShape::sRegister()``` function to register all collision functions, make sure you call this function after calling ```RegisterTypes()```, see [MeshShape::sRegister](@ref MeshShape::sRegister) for an example. +* Now write collision detection functions to test collision with all other shape types that this shape could collide with and register them with [CollisionDispatch::sRegisterCollideShape](@ref CollisionDispatch::sRegisterCollideShape) and [CollisionDispatch::sRegisterCastShape](@ref CollisionDispatch::sRegisterCastShape). This can be a lot of work, but there are some helper functions that you can use to reduce the work: + * If you have implemented a collision test for type A vs B then you can register [CollisionDispatch::sReversedCastShape](@ref CollisionDispatch::sReversedCastShape) and [CollisionDispatch::sReversedCollideShape](@ref CollisionDispatch::sReversedCollideShape) for B vs A. + * If your shape is triangle based, you can forward the testing of a shape vs a single triangle to the [CollideConvexVsTriangles](@ref CollideConvexVsTriangles) and [CastConvexVsTriangles](@ref CastConvexVsTriangles) classes. + * If your shape contains sub shapes and you have determined that the shape intersects with one of the sub shapes you can forward the sub shape to the collision dispatch again through [CollisionDispatch::sCollideShapeVsShape](@ref CollisionDispatch::sCollideShapeVsShape) and [CollisionDispatch::sCastShapeVsShapeLocalSpace](@ref CollisionDispatch::sCastShapeVsShapeLocalSpace). + +## Sensors {#sensors} + +Sensors are normal rigid bodies that report contacts with other Dynamic or Kinematic bodies through the [ContactListener](@ref ContactListener) interface. Any detected penetrations will however not be resolved. Sensors can be used to implement triggers that detect when an object enters their area. + +The cheapest sensor has a Static motion type. This type of sensor will only detect active bodies entering their area. As soon as a body goes to sleep, the contact will be lost. Note that you can still move a Static sensor around using [BodyInterface::SetPosition](@ref BodyInterface::SetPosition). + +When you make a sensor Kinematic or Dynamic and activate it, it will also detect collisions with sleeping bodies, albeit with a higher run-time cost. + +To create a sensor, either set [BodyCreationSettings::mIsSensor](@ref BodyCreationSettings::mIsSensor) to true when constructing a body or set it after construction through [Body::SetIsSensor](@ref Body::SetIsSensor). A sensor can only use the discrete motion quality type at this moment. + +To make sensors detect collisions with static objects, set the [BodyCreationSettings::mCollideKinematicVsNonDynamic](@ref BodyCreationSettings::mCollideKinematicVsNonDynamic) to true or call [Body::SetCollideKinematicVsNonDynamic](@ref Body::SetCollideKinematicVsNonDynamic). Note that it can place a large burden on the collision detection system if you have a large sensor intersect with e.g. a large mesh terrain or a height field as you will get many contact callbacks and these contacts will take up a lot of space in the contact cache. Ensure that your sensor is in an object layer that collides with as few static bodies as possible. + +## Sleeping {#sleeping-bodies} + +During the simulation step, bodies are divided in 'islands'. Each island consists of a set of dynamic bodies that are either in contact with each other, or that are connected through a constraint: + +![Simulation islands are enclosed by a red box. Note that the floor is static so not part of an island.](Images/SimulationIsland.jpg) + +At the end of each step, all the bodies in an island are checked to see if they have come to rest, if this is the case then the entire island is put to sleep. When a body is sleeping, it can still detect collisions with other objects that are not sleeping, but it will not move or otherwise participate in the simulation to conserve CPU cycles. Sleeping bodies wake up automatically when they're in contact with non-sleeping objects or they can be explicitly woken through an API call like BodyInterface::ActivateBody. Unlike some other physics engines, removing a Body from the world doesn't wake up any surrounding bodies. If you want this you can call BodyInterface::ActivateBodiesInAABox with the bounding box of the removed body (or the combined bounding box if you're removing multiple bodies). Also, things like setting the velocity through Body::SetLinearVelocity will not wake up the Body, use BodyInterface::SetLinearVelocity instead. You can configure the definition of a body 'at rest' through PhysicsSettings::mTimeBeforeSleep and PhysicsSettings::mPointVelocitySleepThreshold. + +## Soft Bodies {#soft-bodies} + +Soft bodies (also known as deformable bodies) can be used to create e.g. a soft ball or a piece of cloth. They are created in a very similar way to normal rigid bodies: + +* First allocate a new SoftBodySharedSettings object on the heap. This object will contain the initial positions of all particles and the constraints between the particles. This object can be shared between multiple soft bodies and should remain constant during its lifetime. +* Then create a SoftBodyCreationSettings object (e.g. on the stack) and fill in the desired properties of the soft body. +* Finally construct the body and add it to the world through BodyInterface::CreateAndAddSoftBody. + +Soft bodies use the Body class just like rigid bodies but can be identified by checking Body::IsSoftBody. To get to the soft body state, cast the result of Body::GetMotionProperties to SoftBodyMotionProperties and use its API. + +Soft bodies try to implement as much as possible of the normal Body interface, but this interface provides a simplified version of reality, e.g. Body::GetLinearVelocity will return the average particle speed and Body::GetPosition returns the average particle position. During simulation, a soft body will never update its rotation. Internally it stores particle velocities in local space, so if you rotate a soft body e.g. by calling BodyInterface::SetRotation, the body will rotate but its velocity will as well. + +### Soft Body Contact Listeners {#soft-body-contact-listener} + +Soft Bodies provide contacts with other bodies through the SoftBodyContactListener class. This contact listener works a little bit different from the normal contact listener as you will not receive a contact callback per colliding vertex. + +After the broad phase has detected an overlap and the normal layer / collision group filters have had a chance to reject the collision, you will receive a SoftBodyContactListener::OnSoftBodyContactValidate callback. This callback allows you to specify how the vertices of the soft body should interact with the other body. You can override the mass for both bodies and you can turn the contact into a sensor contact. + +The simulation will then proceed to do all collision detection and response and after that is finished, you will receive a SoftBodyContactListener::OnSoftBodyContactAdded callback that allows you to inspect all collisions that happened during the simulation step. In order to do this a SoftBodyManifold is provided which allows you to loop over the vertices and ask each vertex what it collided with. + +Note that at the time of the callback, multiple threads are operating at the same time. The soft body is stable and can be safely read. The other body that is collided with is not stable however, so you cannot safely read its position/orientation and velocity as it may be modified by another soft body collision at the same time. + +### Skinning Soft Bodies {#skinning-soft-bodies} + +Using the [skinning](@ref SoftBodySharedSettings::Skinned) constraints, a soft body can be (partially) skinned to joints. This can be used e.g. to partially drive cloth with a character animation. The [vertices](@ref SoftBodySharedSettings::Vertex::mPosition) of the soft body need to be placed in the neutral pose of the character and the joints for this pose need to be calculated in model space (relative to these vertices). The inverted matrices of this neutral pose need to be stored as the [inverse bind matrices](@ref SoftBodySharedSettings::InvBind) and the skinning constraints can then be [weighted](@ref SoftBodySharedSettings::SkinWeight) to these joints. SoftBodySharedSettings::CalculateSkinnedConstraintNormals must be called to gather information needed to calculate the face normals at run-time. + +At run-time, you need to provide the animated joints every simulation step through the SoftBodyMotionProperties::SkinVertices call. During simulation, each skinned vertex will calculate its position and this position will be used to limit the movement of its simulated counterpart. + +![A Skinned Constraint](Images/SoftBodySkinnedConstraint.jpg) + +The adjacent faces of the soft body will be used to calculate the normal of each vertex (shown in red), the vertex is then free to move inside the sphere formed by the skinned vertex position with radius [MaxDistance](@ref SoftBodySharedSettings::Skinned::mMaxDistance) (green sphere). To prevent the vertex from intersecting with the character, it is possible to specify a [BackStopDistance](@ref SoftBodySharedSettings::Skinned::mBackStopDistance) and [BackStopRadius](@ref SoftBodySharedSettings::Skinned::mBackStopRadius), together these form the red sphere. The vertex is not allowed to move inside this sphere. + +### Soft Body Work In Progress {#soft-body-wip} + +Soft bodies are currently in development, please note the following: + +* Soft bodies can only collide with rigid bodies, collisions between soft bodies are not implemented yet. +* AddForce/AddTorque/SetLinearVelocity/SetLinearVelocityClamped/SetAngularVelocity/SetAngularVelocityClamped/AddImpulse/AddAngularImpulse have no effect on soft bodies as the velocity is stored per particle rather than per body. +* Buoyancy calculations have not been implemented yet. +* Constraints cannot operate on soft bodies, set the inverse mass of a particle to zero and move it by setting a velocity to constrain a soft body to something else. +* When calculating friction / restitution an empty SubShapeID will be passed to the ContactConstraintManager::CombineFunction because this is called once per body pair rather than once per sub shape as is common for rigid bodies. + +# Constraints {#constraints} + +Bodies can be connected to each other using constraints ([Constraint](@ref Constraint)). + +The following constraints are available: + +* [FixedConstraint](@ref FixedConstraintSettings) - Will attach a body to another without any degrees of freedom. +* [DistanceConstraint](@ref DistanceConstraintSettings) - Will attach two bodies with a stick (removing 1 degree of freedom). +* [PointConstraint](@ref PointConstraintSettings) - Will attach two bodies in a single point (removing 3 degrees of freedom) +* [HingeConstraint](@ref HingeConstraintSettings) - Will attach two bodies through a hinge. +* [ConeConstraint](@ref ConeConstraintSettings) - Attaches two bodies in a point and will limit the rotation within a cone. +* [SliderConstraint](@ref SliderConstraintSettings) - Attaches two bodies and allows only movement in a single translation axis (also known as prismatic constraint). +* [SwingTwistConstraint](@ref SwingTwistConstraintSettings) - Attaches two bodies using a point constraint and a swing-twist constraint which approximates the shoulder joint of a human. +* [SixDOFConstraint](@ref SixDOFConstraintSettings) - The most configurable joint allows specifying per translation axis and rotation axis what the limits are. +* [PathConstraint](@ref PathConstraintSettings) - This constraint allows attaching two bodies connected through a Hermite spline path. +* [GearConstraint](@ref GearConstraintSettings) - This constraint connects to two hinge joints and constrains them to connect two gears. +* [RackAndPinionConstraint](@ref RackAndPinionConstraintSettings) - This constraint connects a hinge and a slider constraint to connect a rack and pinion. +* [PulleyConstraint](@ref PulleyConstraintSettings) - This constraint connects two bodies through two fixed points creating something that behaves like two bodies connected through a rope. +* [VehicleConstraint](@ref VehicleConstraintSettings) - This constraint adds virtual wheels or tracks to a body and allows it to behave as a vehicle. + +If you want to constrain a dynamic object to the unmovable 'world' you can use [Body::sFixedToWorld](@ref Body::sFixedToWorld) instead of creating a static body. + +Bodies do not keep track of the constraints that are connected to them. This means that you're responsible for removing any constraints attached to a body before removing the body from the PhysicsSystem. + +Adding and removing constraints can be done from multiple threads, but the constraints themselves do not have any protection against concurrent access. We assume that constraints are owned by some object (e.g. a Ragdoll) and that object ensures that it only modifies its own constraints and contains its own synchronization logic. Constraints can be freely modified except during the physics simulation step. + +Contact constraints (when bodies collide) are not handled through the [Constraint](@ref Constraint) class but through the [ContactConstraintManager](@ref ContactConstraintManager) which is considered an internal class. + +## Constraint Motors {#constraint-motors} + +Most of the constraints support motors (see [MotorSettings](@ref MotorSettings)) which allow you to apply forces/torques on two constrained bodies to drive them to a relative position/orientation. There are two types of motors: +* Linear motors: These motors drive the relative position between two bodies. A linear motor would, for example, slide a body along a straight line when you use a slider constraint. +* Angular motors: These motors drive the relative rotation between two bodies. An example is a hinge constraint. The motor drives the rotation along the hinge axis. + +Motors can have three states (see [EMotorState](@ref EMotorState) or e.g. SliderConstraint::SetMotorState): +* Off: The motor is not doing any work. +* Velocity: This type of motor drives the relative velocity between bodies. For a slider constraint, you would push the bodies towards/away from each other with constant velocity. For a hinge constraint, you would rotate the bodies relative to each other with constant velocity. Set the target velocity through e.g. SliderConstraint::SetTargetVelocity / HingeConstraint::SetTargetAngularVelocity. +* Position: This type of motor drives the relative position between bodies. For a slider constraint, you can specify the relative distance you want to achieve between the bodies. For a hinge constraint you can specify the relative angle you want to achieve between the bodies. Set the target position through e.g. SliderConstraint::SetTargetPosition / HingeConstraint::SetTargetAngle. + +Motors apply a force (when driving position) or torque (when driving angle) every simulation step to achieve the desired velocity or position. You can control the maximum force/torque that the motor can apply through MotorSettings::mMinForceLimit, MotorSettings::mMaxForceLimit, MotorSettings::mMinTorqueLimit and MotorSettings::mMaxTorqueLimit. Note that if a motor is driving to a position, the torque limits are not used. If a constraint is driving to an angle, the force limits are not used. + +Usually the limits are symmetric, so you would set -mMinForceLimit = mMaxForceLimit. This way the motor can push at an equal rate as it can pull. If you would set the range to e.g. [0, FLT_MAX] then the motor would only be able to push in the positive direction. The units for the force limits are Newtons and the values can get pretty big. If your motor doesn't seem to do anything, chances are that you have set the value too low. Since Force = Mass * Acceleration you can calculate the approximate force that a motor would need to supply in order to be effective. Usually the range is set to [-FLT_MAX, FLT_MAX] which lets the motor achieve its target as fast as possible. + +For an angular motor, the units are Newton Meters. The formula is Torque = Inertia * Angular Acceleration. Inertia of a solid sphere is 2/5 * Mass * Radius^2. You can use this to get a sense of the amount of torque needed to get the angular acceleration you want. Again, you'd usually set the range to [-FLT_MAX, FLT_MAX] to not limit the motor. + +When settings the force or torque limits to [-FLT_MAX, FLT_MAX] a velocity motor will accelerate the bodies to the desired relative velocity in a single time step (if no other forces act on those bodies). + +Position motors have two additional parameters: Frequency (MotorSettings::mSpringSettings.mFrequency, Hz) and damping (MotorSettings::mSpringSettings.mDamping, no units). They are implemented as described in [Soft Constraints: Reinventing The Spring - Erin Catto - GDC 2011](https://box2d.org/files/ErinCatto_SoftConstraints_GDC2011.pdf). + +You can see a position motor as a spring between the target position and the rigid body. The force applied to reach the target is linear with the distance between current position and target position. When there is no damping, the position motor will cause the rigid body to oscillate around its target. + +![A rigid body on a slider constraint. The body starts at 1 and is driven to 0 with a position motor. Two different motor frequencies are shown. The higher the frequency, the faster the motor will reach its target, but without damping it will overshoot and oscillate forever.](Images/MotorFrequency.jpg) + +Valid frequencies are in the range (0, 0.5 * simulation frequency]. A frequency of 0 results in no force being applied, a frequency larger than half of the physics simulation frequency will result in instability. For a 60 Hz physics simulation, 20 is a good value for a stiff spring (without damping it will reach its target in 1/(4 * 20) = 0.0125 s), 2 is good for a soft spring (will reach its target in 1/(4 * 2) = 0.125 s). + +In order to prevent the motor from overshooting its target, we use damping. + +![A rigid body on a slider constraint. The body starts at 1 and is driven to 0 with a position motor. The frequency of the motor is 2 Hz and the lines correspond to different damping values.](Images/MotorDamping.jpg) + +Sensible values for damping are [0, 1] but higher values are also possible. When the damping is below 1, the body will still oscillate around its target, but that oscillation will die out. When the damping is 1 (called critical damping) there is no oscillation at all but it will take longer for the motor to reach its target. When damping is bigger than 1, the system is over dampened. There will not be any oscillation, but it will take even longer for the motor to reach its target. + +Because Jolt Physics uses a Symplectic Euler integrator, there will still be a small amount of damping when damping is 0, so you cannot get infinite oscillation (allowing this would make it very likely for the system to become unstable). + +## Breakable Constraints {#breakable-constraints} + +Constraints can be turned on / off by calling Constraint::SetEnabled. After every simulation step, check the total 'lambda' applied on each constraint and disable the constraint if the value goes over a certain threshold. Use e.g. SliderConstraint::GetTotalLambdaPosition / HingeConstraint::GetTotalLambdaRotation. You can see 'lambda' as the linear/angular impulse applied at the constraint in the last physics step to keep the constraint together. + +# Collision Detection {#collision-detection} + +## Broad Phase {#broad-phase} + +When bodies are added to the PhysicsSystem, they are inserted in the broad phase ([BroadPhaseQuadTree](@ref BroadPhaseQuadTree)). This provides quick coarse collision detection based on the axis aligned bounding box (AABB) of a body. + +![To quickly test if two objects overlap you can check if their axis aligned bounding boxes overlap. If they do, a check between the actual shapes is needed to be sure.](Images/EllipsoidAABB.png) + +Our broad phase is a quad tree, which means each node has 4 children. In the following image you see a random collection of spheres and triangles and a possible way to split the tree. + +![QuadTree Example](Images/QuadTreeExample.png) + + At the highest level we split all objects in 4 mostly disjoint sets. Note that nodes are allowed to overlap, but for efficiency reasons we want the amount of overlap to be minimal. The example split here is indicated by a red, blue, green and yellow box and you can see them appear in the tree on the right. Three out of four nodes: blue, yellow and red, have 4 or less shapes in them, so the tree can directly point at the shapes rather than at a next node. One node: green, has more than 4 shapes in it so needs a further split. The three shapes can be added directly to the node and we need to create a new node, dotted green, to hold the last two shapes. The reason why we pick 4 children is that modern CPUs support doing 4 math operations in a single instruction, so when we walk the tree from top to bottom during a collision query, we can handle 4 children at the same time and quickly get to a minimal set of colliding objects. + +Since we want to access bodies concurrently the broad phase has special behavior. When a body moves, all nodes in the AABB tree from root to the node where the body resides will be expanded using a lock-free approach. This way multiple threads can move bodies at the same time without requiring a lock on the broad phase. Nodes that have been expanded are marked and during the next physics step a new tight-fitting tree will be built in the background while the physics step is running. This new tree will replace the old tree before the end of the simulation step. This is possible since no bodies can be added/removed during the physics step. For more information about this see the [GDC 2022 talk](https://jrouwe.nl/architectingjolt/ArchitectingJoltPhysics_Rouwe_Jorrit_Notes.pdf). + +The broad phase is divided in layers (BroadPhaseLayer), each broad phase layer has an AABB quad tree associated with it. A standard setup would be to have at least 2 broad phase layers: One for all static bodies (which is infrequently updated but is expensive to update since it usually contains most bodies) and one for all dynamic bodies (which is updated every simulation step but cheaper to update since it contains fewer objects). In general you should only have a few broad phase layers as there is overhead in querying and maintaining many different broad phase trees. + +When doing a query against the broad phase ([BroadPhaseQuery](@ref BroadPhaseQuery)), you generally will get a body ID for intersecting objects. If a collision query takes a long time to process the resulting bodies (e.g. across multiple simulation steps), you can safely keep using the body ID's as specified in the @ref bodies section. + +## Narrow Phase {#narrow-phase} + +A narrow phase query ([NarrowPhaseQuery](@ref NarrowPhaseQuery)) will first query the broad phase for intersecting bodies and will under the protection of a body lock construct a transformed shape ([TransformedShape](@ref TransformedShape)) object. This object contains the transform, a reference counted shape and a body ID. Since the shape will not be deleted until you destroy the TransformedShape object, it is a consistent snapshot of the collision information of the body. This ensures that the body is only locked for a short time frame and makes it possible to do the bulk of the collision detection work outside the protection of a lock. + +For very long running jobs (e.g. navigation mesh creation) it is possible to query all transformed shapes in an area and then do the processing work using a long running thread without requiring additional locks (see [NarrowPhaseQuery::CollectTransformedShapes](@ref NarrowPhaseQuery::CollectTransformedShapes)). + +The narrow phase queries are all handled through the [GJK](@ref GJKClosestPoint) and [EPA](@ref EPAPenetrationDepth) algorithms. + +## Collision Filtering {#collision-filtering} + +Each Body is in an [ObjectLayer](@ref ObjectLayer). If two object layers don't collide, the bodies inside those layers cannot collide. You can define object layers in any way you like, it could be a simple number from 0 to N or it could be a bitmask. Jolt supports 16 or 32 bit ObjectLayers through the JPH_OBJECT_LAYER_BITS define and you're free to define as many as you like as they don't incur any overhead in the system. + +When constructing the PhysicsSystem you need to provide a number of filtering interfaces: +* BroadPhaseLayerInterface: This class defines a mapping from ObjectLayer to BroadPhaseLayer through the BroadPhaseLayerInterface::GetBroadPhaseLayer function. Each Body can only be in 1 BroadPhaseLayer so an ObjectLayer maps to 1 BroadphaseLayer. In general there will be multiple ObjectLayers mapping to the same BroadPhaseLayer (because each broad phase layer comes at a cost). If there are multiple object layers in a single broad phase layer, they are stored in the same tree. When a query visits the tree it will visit all objects whose AABB overlaps with the query and only when the overlap is detected, the actual object layer will be checked. This means that you should carefully design which object layers end up in which broad phase layer, balancing the requirement of having few broad phase layers with the number of needless objects that are visited because multiple object layers share the same broad phase layer. You can define JPH_TRACK_BROADPHASE_STATS to let Jolt print out some statistics about the query patterns your application is using. In general it is wise to start with only 2 broad phase layers as listed in the \ref broad-phase section. +* ObjectVsBroadPhaseLayerFilter: This class defines a ObjectVsBroadPhaseLayerFilter::ShouldCollide function that checks if an ObjectLayer collides with objects that reside in a particular BroadPhaseLayer. ObjectLayers can collide with as many BroadPhaseLayers as needed, so it is possible for a collision query to visit multiple broad phase trees. +* ObjectLayerPairFilter: This class defines a ObjectLayerPairFilter::ShouldCollide function that checks if an ObjectLayer collides with another ObjectLayer. + +As an example we will use a simple enum as ObjectLayer: +* NON_MOVING - Layer for all static objects. +* MOVING - Layer for all regular dynamic bodies. +* DEBRIS - Layer for all debris dynamic bodies, we want to test these only against the static geometry because we want to save some simulation cost. +* BULLET - Layer for high detail collision bodies that we attach to regular dynamic bodies. These are not used for simulation but we want extra precision when we shoot with bullets. +* WEAPON - This is a query layer so we don't create any bodies with this layer but we use it when doing ray cast querying for our weapon system. + +We define the following object layers to collide: +* MOVING vs NON_MOVING, MOVING vs MOVING - These are for our regular dynamic objects that need to collide with the static world and with each other. +* DEBRIS vs NON_MOVING - As said, we only want debris to collide with the static world and not with anything else. +* WEAPON vs BULLET, WEAPON vs NON_MOVING - We want our weapon ray cast to hit the high detail BULLET collision instead of the normal MOVING collision and we want bullets to be blocked by the static world (obviously the static world could also have a high detail version, but not in this example). + +This means that we need to implement a ObjectLayerPairFilter::ShouldCollide that returns true for the permutations listed above. Note that if ShouldCollide(A, B) returns true, ShouldCollide(B, A) should return true too. + +We define the following broad phase layers: +* BP_NON_MOVING - For everything static (contains object layer: NON_MOVING). +* BP_MOVING - The default layer for dynamic objects (contains object layers: MOVING, BULLET). +* BP_DEBRIS - An extra layer that contains only debris (contains object layers: DEBRIS). + +This means we now implement a BroadPhaseLayerInterface::GetBroadPhaseLayer that maps: NON_MOVING -> BP_NON_MOVING, MOVING -> BP_MOVING, BULLET -> BP_MOVING and DEBRIS -> BP_DEBRIS. We can map WEAPON to anything as we won't create any objects with this layer. + +We also need to implement a ObjectVsBroadPhaseLayerFilter::ShouldCollide that determines which object layer should collide with what broad phase layers, these can be deduced from the two lists above: +* NON_MOVING: BP_MOVING, BP_DEBRIS +* MOVING: BP_NON_MOVING, BP_MOVING +* DEBRIS: BP_NON_MOVING +* BULLET: None (these are not simulated so need no collision with other objects) +* WEAPON: BP_NON_MOVING, BP_MOVING + +So you can see now that when we simulate DEBRIS we only need to visit a single broad phase tree to check for collision, we did this because in our example we know that there are going to be 1000s of debris objects so it is important that their queries are as fast as possible. We could have moved the BULLET layer to its own broad phase layer too because now BP_MOVING contains a lot of bodies that WEAPON is not interested in, but in this example we didn't because we know that there are not enough of these objects for this to be a performance problem. + +For convenience two filtering implementations are provided: +* ObjectLayerPairFilterTable, ObjectVsBroadPhaseLayerFilterTable and BroadPhaseLayerInterfaceTable: These three implement collision layers as a simple table. You construct ObjectLayerPairFilterTable with a fixed number of object layers and then call ObjectLayerPairFilterTable::EnableCollision or ObjectLayerPairFilterTable::DisableCollision to selectively enable or disable collisions between layers. BroadPhaseLayerInterfaceTable is constructed with a number of broad phase layers. You can then map each object layer to a broad phase layer through BroadPhaseLayerInterfaceTable::MapObjectToBroadPhaseLayer. +* ObjectLayerPairFilterMask, ObjectVsBroadPhaseLayerFilterMask and BroadPhaseLayerInterfaceMask: These split an ObjectLayer in an equal amount of bits for group and mask. Two objects collide if (object1.group & object2.mask) != 0 && (object2.group & object1.mask) != 0. This behavior is similar to e.g. Bullet. In order to map groups to broad phase layers, you call BroadPhaseLayerInterfaceMask::ConfigureLayer for each broad phase layer. You determine which groups can be put in that layer and which group must be excluded from that layer. E.g. a broad phase layer could include everything that has the STATIC group but should exclude everything that has the SENSOR group, so that if an object has both STATIC and SENSOR bits set, this broad phase layer will not be used. The broad phase layers are checked one by one and the first one that meets the condition is the one that the body will be put in. If you use this implementation, consider setting the cmake option OBJECT_LAYER_BITS to 32 to get a 32-bit ObjectLayer instead of a 16-bit one. + +Now that we know about the basics, we list the order in which the collision detection pipeline goes through the various collision filters: + +* Broadphase layer: At this stage, the object layer is tested against the broad phase trees that are relevant by checking the [ObjectVsBroadPhaseLayerFilter](@ref ObjectVsBroadPhaseLayerFilter). +* Object layer: Once the broad phase layer test succeeds, we will test object layers vs object layers through [ObjectLayerPairFilter](@ref ObjectLayerPairFilter) (used for simulation) and [ObjectLayerFilter](@ref ObjectLayerFilter) (used for collision queries). The default implementation of ObjectLayerFilter is DefaultObjectLayerFilter and uses ObjectLayerPairFilter so the behavior is consistent between simulation and collision queries. +* Group filter: Most expensive filtering (bounding boxes already overlap), used only during simulation. Allows you fine tune collision e.g. by discarding collisions between bodies connected by a constraint. See [GroupFilter](@ref GroupFilter) and implementation for ragdolls [GroupFilterTable](@ref GroupFilterTable). +* Body filter: This filter is used instead of the group filter if you do collision queries like CastRay. See [BodyFilter](@ref BodyFilter). +* Shape filter: This filter is used only during collision queries and can be used to filter out individual (sub)shapes. See [ShapeFilter](@ref ShapeFilter). +* Contact listener: During simulation, after all collision detection work has been performed you can still choose to discard a contact point. This is a very expensive way of rejecting collisions as most of the work is already done. See [ContactListener](@ref ContactListener). + +To avoid work, try to filter out collisions as early as possible. + +## Continuous Collision Detection {#continuous-collision-detection} + +Each body has a motion quality setting ([EMotionQuality](@ref EMotionQuality)). By default the motion quality is [Discrete](@ref Discrete). This means that at the beginning of each simulation step we will perform collision detection and if no collision is found, the body is free to move according to its velocity. This usually works fine for big or slow moving objects. Fast and small objects can easily 'tunnel' through thin objects because they can completely move through them in a single time step. For these objects there is the motion quality [LinearCast](@ref LinearCast). Objects that have this motion quality setting will do the same collision detection at the beginning of the simulation step, but once their new position is known, they will do an additional CastShape to check for any collisions that may have been missed. If this is the case, the object is placed back to where the collision occurred and will remain there until the next time step. This is called 'time stealing' and has the disadvantage that an object may appear to move much slower for a single time step and then speed up again. The alternative, back stepping the entire simulation, is computationally heavy so was not implemented. + +![With the Discrete motion quality the blue object tunnels through the green object in a single time step. With motion quality LinearCast it doesn't.](Images/MotionQuality.jpg) + +Fast rotating long objects are also to be avoided, as the LinearCast motion quality will fully rotate the object at the beginning of the time step and from that orientation perform the CastShape, there is a chance that the object misses a collision because it rotated through it. + +![Even with the LinearCast motion quality the blue object rotates through the green object in a single time step.](Images/LongAndThin.jpg) + +## Ghost Collisions {#ghost-collisions} + +A ghost collision can occur when a body slides over another body and hits an internal edge of that body. The most common case is where a body hits an edge of a triangle in a mesh shape but it can also happen on 2 box shapes as shown below. + +![A blue box sliding over 2 green boxes. Because the blue box can sink into the green box a little bit, it can hit the edge between the two boxes. This will cause the box to stop or jump up.](Images/GhostCollision.jpg) + +There are a couple of ways to avoid ghost collisions in Jolt. MeshShape and HeightFieldShape keep track of active edges during construction. + +![An inactive edge (concave) and an active edge (convex, angle > threshold angle).](Images/ActiveEdge.jpg) + +Whenever a body hits an inactive edge, the contact normal is the face normal. When it hits an active edge, it can be somewhere in between the connecting face normals so the movement of the body is impeded in the scenario below. + +![Contact normal (red) of hitting an active vs an inactive edge.](Images/ActiveVsInactiveContactNormal.jpg) + +By tweaking MeshShapeSettings::mActiveEdgeCosThresholdAngle or HeightFieldShapeSettings::mActiveEdgeCosThresholdAngle you can determine the angle at which an edge is considered an active edge. By default this is 5 degrees, making this bigger reduces the amount of ghost collisions but can create simulation artifacts if you hit the edge straight on. + +To further reduce ghost collisions, you can turn on BodyCreationSettings::mEnhancedInternalEdgeRemoval. When enabling this setting, additional checks will be made at run-time to detect if an edge is active or inactive based on all of the contact points between the two bodies. Beware that this algorithm only considers 2 bodies at a time, so if the two green boxes above belong to two different bodies, the ghost collision can still occur. Use a StaticCompoundShape to combine the boxes in a single body to allow the system to eliminate ghost collisions between the blue and the two green boxes. You can also use this functionality for your custom collision tests by making use of InternalEdgeRemovingCollector. + +# Character Controllers {#character-controllers} + +The [Character](@ref Character) and [CharacterVirtual](@ref CharacterVirtual) classes can be used to create a character controller. These are usually used to represent the player as a simple capsule or tall box and perform collision detection while the character navigates through the world. + +The Character class is the simplest controller and is essentially a rigid body that has been configured to only allow translation (and no rotation so it stays upright). It is simulated together with the other rigid bodies so it properly reacts to them. Because it is simulated, it is usually not the best solution for a player as the player usually requires a lot of behavior that is non-physical. This character controller is cheap so it is recommended for e.g. simple AI characters. After every PhysicsSystem::Update call you must call Character::PostSimulation to update the ground contacts. + +The CharacterVirtual class is much more advanced. It is implemented using collision detection functionality only (through NarrowPhaseQuery) and is simulated when CharacterVirtual::Update is called. Since the character is not 'added' to the world, it is not visible to rigid bodies and it only interacts with them during the CharacterVirtual::Update function by applying impulses. This does mean there can be some update order artifacts, like the character slightly hovering above an elevator going down, because the characters moves at a different time than the other rigid bodies. Separating it has the benefit that the update can happen at the appropriate moment in the game code. Multiple CharacterVirtuals can update concurrently, so it is not an issue if the game code is parallelized. + +CharacterVirtual has the following extra functionality: +* Sliding along walls +* Interaction with elevators and moving platforms +* Enhanced steep slope detection (standing in a funnel whose sides are too steep to stand on will not be considered as too steep) +* Stair stepping through the CharacterVirtual::ExtendedUpdate call +* Sticking to the ground when walking down a slope through the CharacterVirtual::ExtendedUpdate call +* Support for specifying a local coordinate system that allows e.g. [walking around in a flying space ship](https://github.com/jrouwe/JoltPhysics/blob/master/Samples/Tests/Character/CharacterSpaceShipTest.cpp) that is equipped with 'inertial dampers' (a sci-fi concept often used in games). + +If you want CharacterVirtual to have presence in the world, it is recommended to pair it with a slightly smaller [Kinematic](@ref EMotionType) body (or Character). After each update, move this body using BodyInterface::MoveKinematic to the new location. This ensures that standard collision tests like ray casts are able to find the character in the world and that fast moving objects with motion quality [LinearCast](@ref EMotionQuality) will not pass through the character in 1 update. As an alternative to a Kinematic body, you can also use a regular Dynamic body with a [gravity factor](@ref BodyCreationSettings::mGravityFactor) of 0. Ensure that the character only collides with dynamic objects in this case. The advantage of this approach is that the paired body doesn't have infinite mass so is less strong. + +Characters are usually driven in a kinematic way (i.e. by calling Character::SetLinearVelocity or CharacterVirtual::SetLinearVelocity before their update). + +To get started take a look at the [Character](https://github.com/jrouwe/JoltPhysics/blob/master/Samples/Tests/Character/CharacterTest.cpp) and [CharacterVirtual](https://github.com/jrouwe/JoltPhysics/blob/master/Samples/Tests/Character/CharacterVirtualTest.cpp) examples. + +# The Simulation Step {#the-simulation-step} + +The simulation step [PhysicsSystem::Update](@ref PhysicsSystem::Update) uses jobs ([JobSystem](@ref JobSystem)) to perform the needed work. This allows spreading the workload across multiple CPU's. We use a Sequential Impulse solver with warm starting as described in [Modeling and Solving Constraints - Erin Catto](https://box2d.org/files/ErinCatto_ModelingAndSolvingConstraints_GDC2009.pdf) + +Each physics step can be divided into multiple collision steps. So if you run the simulation at 60 Hz with 2 collision steps we run: + +* Collision (1/120s) +* Integration (1/120s) +* Collision (1/120s) +* Integration (1/120s) + +In general, the system is stable when running at 60 Hz with 1 collision step. + +# Conventions and Limits {#conventions-and-limits} + +Jolt Physics uses a right handed coordinate system with Y-up. It is easy to use another axis as up axis by changing the gravity vector using [PhysicsSystem::SetGravity](@ref PhysicsSystem::SetGravity). Some shapes like the [HeightFieldShape](@ref HeightFieldShapeSettings) will need an additional [RotatedTranslatedShape](@ref RotatedTranslatedShapeSettings) to rotate it to the new up axis and vehicles ([VehicleConstraint](@ref VehicleConstraintSettings)) and characters ([CharacterBaseSettings](@ref CharacterBaseSettings)) will need their new up-axis specified too. + +We use column-major vectors and matrices, this means that to transform a point you need to multiply it on the right hand side: TransformedPoint = Matrix * Point. + +Note that the physics simulation works best if you use SI units (meters, radians, seconds, kg). In order for the simulation to be accurate, dynamic objects should be in the order [0.1, 10] meters long, have speeds in the order of [0, 500] m/s and have gravity in the order of [0, 10] m/s^2. Static object should be in the order [0.1, 2000] meter long. If you are using different units, consider scaling the objects before passing them on to the physics simulation. + +# Big Worlds {#big-worlds} + +By default the library compiles using floats. This means that the simulation gets less accurate the further you go from the origin. If all simulation takes place within roughly 5 km from the origin, floating point precision is accurate enough. + +If you have a bigger world, you may want to compile the library using the JPH_DOUBLE_PRECISION define. When you do this, all positions will be stored as doubles, which will make the simulation accurate even at thousands of kilometers away from the origin. + +Calculations with doubles are much slower than calculations with floats. A naive implementation that changes all calculations to doubles has been measured to run more than 2x slower than the same calculations using floats. Because of this, Jolt Physics will only use doubles where necessary and drop down to floats as soon as possible. In order to do this, many of the collision query functions will need a 'base offset'. All collision results will be returned as floats relative to this base offset. By choosing the base offset wisely (i.e. close to where collision results are expected) the results will be accurate. Make sure your base offset is not kilometers away from the collision result. + +Keep in mind that: + +* There are a lot of 'epsilons' in the code that have been tuned for objects of sizes/speeds as described in the @ref conventions-and-limits section. Try to keep the individual objects to the specified scale even if they're really far from the origin. +* When the collision results of a single query are kilometers apart, precision will suffer as they will be far away from the 'base offset'. +* The effectiveness of the broad phase (which works in floats) will become less at large distances from the origin, e.g. at 10000 km from the origin, the resolution of the broad phase is reduced to 1 m which means that everything that's closer than 1 m will be considered colliding. This will not impact the quality of the simulation but it will result in extra collision tests in the narrow phase so will hurt performance. + +Because of the minimal use of doubles, the simulation runs 5-10% slower in double precision mode compared to float precision mode. + +# Deterministic Simulation {#deterministic-simulation} + +The physics simulation is deterministic provided that: + +* The APIs that modify the simulation are called in exactly the same order. For example, bodies and constraints need to be added/removed/modified in exactly the same order so that the state at the beginning of a simulation step is exactly the same for both simulations. +* The same binary code is used to run the simulation. For example, when you run the simulation on Windows it doesn't matter if you have an AMD or Intel processor. + +If you want cross platform determinism then please turn on the CROSS_PLATFORM_DETERMINISTIC option in CMake. This will make the library approximately 8% slower but the simulation will be deterministic regardless of: + +* Compiler used to compile the library (tested MSVC2022 vs clang) +* Configuration (Debug, Release or Distribution) +* OS (tested Windows, macOS, Linux) +* Architecture (x86 or ARM). + +Some caveats: + +* The same source code must be used to compile the library on all platforms. +* The source code must be compiled with the same defines, e.g. you can't have one platform using JPH_DOUBLE_PRECISION and another not. + +It is quite difficult to verify cross platform determinism, so this feature is less tested than other features. With every build, the following architectures are verified to produce the same results: + +* Windows MSVC x86 64-bit with AVX2 +* Windows MSVC x86 32-bit with SSE2 +* macOS clang x86 64-bit with AVX +* Linux clang x86 64-bit with AVX2 +* Linux clang ARM 64-bit with NEON + +The most important things to look out for in your own application: + +* Compile your application mode in Precise mode (clang: -ffp-model=precise, MSVC: /fp:precise) +* Turn off floating point contract operations (clang: -ffp-contract=off) +* Do not use the standard trigonometry functions (sin, cos etc.) as they have different implementations on different platforms, use Jolt's functions (Sin, Cos etc.) +* Do not use std::sort as it has a different implementation on different platforms, use Jolt's QuickSort function + +When running the Samples Application you can press ESC, Physics Settings and check the 'Check Determinism' checkbox. Before every simulation step we will record the state using the [StateRecorder](@ref StateRecorder) interface, rewind the simulation and do the step again to validate that the simulation runs deterministically. Some of the tests (e.g. the MultiThreaded) test will explicitly disable the check because they randomly add/remove bodies from different threads. This violates the rule that the API calls must be done in the same order so will not result in a deterministic simulation. + +# Rolling Back a Simulation {#rolling-back-a-simulation} + +When synchronizing two simulations via a network, it is possible that a change that needed to be applied at frame N is received at frame N + M. This will require rolling back the simulation to the state of frame N and repeating the simulation with the new inputs. This can be implemented by saving the physics state using [SaveState](@ref PhysicsSystem::SaveState) at every frame. To roll back, call [RestoreState](@ref PhysicsSystem::RestoreState) with the state at frame N. SaveState only records the state that the physics engine modifies during its update step (positions, velocities etc.), so if you change anything else you need to restore this yourself. E.g. if you did a [SetFriction](@ref Body::SetFriction) on frame N + 2 then, when rewinding, you need to restore the friction to what is was on frame N and update it again on frame N + 2 when you replay. If you start adding/removing objects (e.g. bodies or constraints) during these frames, the RestoreState function will not work. If you added a body on frame N + 1, you'll need to remove it when rewinding and then add it back on frame N + 1 again (with the proper initial position/velocity etc. because it won't be contained in the snapshot at frame N). + +If you wish to share saved state between server and client, you need to ensure that all APIs that modify the state of the world are called in the exact same order. So if the client creates physics objects for player 1 then 2 and the server creates the objects for 2 then 1 you already have a problem (the body IDs will be different, which will render the save state snapshots incompatible). When rolling back a simulation, you'll also need to ensure that the BodyIDs are kept the same, so you need to remove/add the body from/to the physics system instead of destroy/re-create them or you need to create bodies with the same ID on both sides using [BodyInterface::CreateBodyWithID](@ref BodyInterface::CreateBodyWithID). + +# Working With Multiple Physics Systems {#working-with-multiple-physics-systems} + +You can create, simulate and interact with multiple PhysicsSystems at the same time provided that you do not share any objects (bodies, constraints) between the systems. +When a Body is created it receives a BodyID that is unique for the PhysicsSystem that it was created for, so it cannot be shared. The only object that can be shared between PhysicsSystems is a Shape. +If you want to move a body from one PhysicsSystem to another, use Body::GetBodyCreationSettings to get the settings needed to create the body in the other PhysicsSystem. + +PhysicsSystems are not completely independent: + +* There is only 1 RTTI factory (Factory::sInstance). +* There is only 1 default material (PhysicsMaterial::sDefault). +* There is only 1 debug renderer (DebugRenderer::sInstance) although many functions take a custom DebugRenderer for drawing. +* Custom shapes and CollisionDispatch functions are shared. +* The custom memory allocation functions (e.g. Allocate), Trace and AssertFailed functions are shared. + +These functions / systems need to be registered in advance. + +# Debug Rendering {#debug-rendering} + +When the define JPH_DEBUG_RENDERER is defined (which by default is defined in Debug and Release but not Distribution), Jolt is able to render its internal state. To integrate this into your own application you must inherit from the DebugRenderer class and implement the pure virtual functions DebugRenderer::DrawLine, DebugRenderer::DrawTriangle, DebugRenderer::CreateTriangleBatch, DebugRenderer::DrawGeometry and DebugRenderer::DrawText3D. The CreateTriangleBatch is used to prepare a batch of triangles to be drawn by a single DrawGeometry call, which means that Jolt can render a complex scene much more efficiently than when each triangle in that scene would have been drawn through DrawTriangle. At run-time create an instance of your DebugRenderer which will internally assign itself to DebugRenderer::sInstance. Finally call for example PhysicsSystem::DrawBodies or PhysicsSystem::DrawConstraints to draw the state of the simulation. For an example implementation see [the DebugRenderer from the Samples application](https://github.com/jrouwe/JoltPhysics/blob/master/TestFramework/Renderer/DebugRendererImp.h) or to get started quickly take a look at DebugRendererSimple. + +# Memory Management {#memory-management} + +Jolt uses reference counting for a number of its classes (everything that inherits from RefTarget). The most important classes are: + +* ShapeSettings +* Shape +* ConstraintSettings +* Constraint +* PhysicsMaterial +* GroupFilter +* PhysicsScene +* SoftBodySharedSettings +* VehicleCollisionTester +* VehicleController +* WheelSettings +* CharacterBaseSettings +* CharacterBase +* RagdollSettings +* Ragdoll +* Skeleton +* SkeletalAnimation +* SkeletonMapper + +Reference counting objects start with a reference count of 0. If you want to keep ownership of the object, you need to call [object->AddRef()](@ref RefTarget::AddRef), this will increment the reference count. If you want to release ownership you call [object->ReleaseRef()](@ref RefTarget::Release), this will decrement the reference count and if the reference count reaches 0 the object will be destroyed. If, after newing, you pass a reference counted object on to another object (e.g. a ShapeSettings to a CompoundShapeSettings or a Shape to a Body) then that other object will take a reference, in that case it is not needed take a reference yourself beforehand so you can skip the calls to ```AddRef/Release```. Note that it is also possible to do ```auto x = new XXX``` followed by ```delete x``` for a reference counted object if no one ever took a reference. The safest way of working with reference counting objects is to use the Ref or RefConst classes, these automatically manage the reference count for you when assigning a new value or on destruction: + +``` +// Calls 'AddRef' to keep a reference the shape +JPH::Ref shape = new JPH::SphereShape(1.0f); + +// Calls 'Release' to release and delete the shape (note that this also happens if JPH::Ref goes out of scope) +shape = nullptr; +``` + +The Body class is a special case, it is destroyed through BodyInterface::DestroyBody (which internally destroys the Body). + +Jolt also supports routing all of its internal allocations through a custom allocation function. See: [Allocate](@ref Allocate), [Free](@ref Free), [AlignedAllocate](@ref AlignedAllocate) and [AlignedFree](@ref AlignedFree). + +# The Simulation Step in Detail {#the-simulation-step-in-detail} + +The job graph looks like this: + +![Job Graph Physics Step](PhysicsSystemUpdate.svg) + +Note that each job indicates if it reads/writes positions/velocities and if it deactivates/activates bodies. We do not allow jobs to read/write the same data concurrently. The arrows indicate the order in which jobs are executed. Yellow blocks mean that there are multiple jobs of this type. Dotted arrows have special meaning and are explained below. + +## Broad Phase Update Prepare {#broad-phase-update-prepare} + +This job will refit the AABBs of the broad phase. It does this by building a new tree while keeping the old one available as described in the @ref broad-phase section. + +## Broad Phase Update Finalize {#broad-phase-update-finalize} + +This job will simply swap the new tree with the old tree. The old tree will be discarded at the beginning of the next PhysicsSystem::Update call so that any broad phase query can continue to run. + +## Step Listeners {#step-listeners-update} + +You can register one or more step listeners (See [PhysicsSystem::AddStepListener](@ref PhysicsSystem::AddStepListener)). This job will call [PhysicsStepListener::OnStep](@ref PhysicsStepListener::OnStep) for every listener. This can be used to do work that needs to be done at the beginning of each step, e.g. set velocities on ragdoll bodies. + +## Apply Gravity {#apply-gravity-update} + +A number of these jobs run in parallel. Each job takes a batch of active bodies and applies gravity and damping (updating linear and angular velocity). + +## Determine Active Constraints {#determine-active-constraints} + +This job will go through all non-contact constraints and determine which constraints are active based on if the bodies that the constraint connects to are active. + +## Build Islands from Constraints {#build-islands-from-constraints} + +This job will go through all non-contact constraints and assign the involved bodies and constraint to the same island. Since we allow concurrent insertion/removal of bodies we do not want to keep island data across multiple simulation steps, so we recreate the islands from scratch every simulation step. The operation is lock-free and O(N) where N is the number of constraints. + +If a constraint connects an active and a non-active body, the non-active body is woken up. One find collisions job will not start until this job has finished in order to pick up any collision testing for newly activated bodies. + +## Find Collisions {#find-collisions} + +This job will do broad and narrow phase checks. Initially a number of jobs are started based on the amount of active bodies. The job will do the following: + +- Take a batch of active bodies and collide them against the broadphase. +- When a collision pair is found it is inserted in a lock free queue to be processed later. +- If the queue is full, it will be processed immediately (more Find Collisions jobs are spawned if not all CPU cores are occupied yet as the queue starts to fill up). +- If there are no more active bodies to process, the job will start to perform narrow phase collision detection and set up contact constraints if any collisions are found. +- As soon as a narrow phase pair is processed it will recheck if there are new active bodies to be processed (active bodies can be generated by an active body colliding with an inactive body) and if so process them. +- When there are no more active bodies to test and no more collision pairs to be processed the job terminates. + +Note that this job cannot start until apply gravity is done because the velocity needs to be known for elastic collisions to be calculated properly. + +The contact points between the two bodies will be determined by the [GJK](@ref GJKClosestPoint) and [EPA](@ref EPAPenetrationDepth) algorithms. For each contact point we will calculate the face that belongs to that contact point. The faces of both bodies are clipped against each other ([ManifoldBetweenTwoFaces](@ref ManifoldBetweenTwoFaces)) so that we have a polygon (or point / line) that represents the contact between the two bodies (contact manifold). + +Multiple contact manifolds with similar normals are merged together (PhysicsSystem::ProcessBodyPair::ReductionCollideShapeCollector). After this the contact constraints are created in the [ContactConstraintManager](@ref ContactConstraintManager) and their Jacobians / effective masses calculated. + +Contacting bodies are also linked together to form islands. This is the same operation as described in the @ref build-islands-from-constraints section. + +The narrow phase makes use of a lock free contact cache. We have 2 caches, one that is used for reading (which contains the contacts from the previous step) and one for writing new contact pairs. When a contact point is preserved from the last simulation step, it will be copied from the read cache to the write cache. + +## Setup Velocity Constraints {#setup-velocity-constraints} + +This job will go through all non-contact constraints and prepare them for execution. This involves calculating Jacobians and effective masses for each constraint part. + +## Finalize Islands {#finalize-islands} + +This job will finalize the building of the simulation islands. Each island contains bodies that interact with each other through a contact point or through a constraint. These islands will be simulated separately in different jobs later. The finalization of the islands is an O(N) operation where N is the amount of active bodies (see [IslandBuilder::Finalize](@ref IslandBuilder::Finalize)). + +## Set Body Island Idx {#set-body-island-idx} + +This job does some housekeeping work that can be executed concurrent to the solver: + +* It will assign the island ID to all bodies (which is mainly used for debugging purposes) + +## Solve Velocity Constraints {#solve-velocity-constraints} + +A number of these jobs will run in parallel. Each job takes the next unprocessed island and will run the iterative constraint solver for that island. It will first apply the impulses applied from the previous simulation step (which are stored in the contact cache) to warm start the solver. It will then repeatedly iterate over all contact and non-contact constraints until either the applied impulses are too small or a max iteration count is reached ([PhysicsSettings::mNumVelocitySteps](@ref PhysicsSettings::mNumVelocitySteps)). The result will be that the new velocities are known for all active bodies. The applied impulses are stored in the contact cache for the next step. + +When an island consists of more than LargeIslandSplitter::cLargeIslandTreshold contacts plus constraints it is considered a large island. In order to not do all work on a single thread, this island will be split up by the LargeIslandSplitter. This follows an algorithm described in High-Performance Physical Simulations on Next-Generation Architecture with Many Cores by Chen et al. This is basically a greedy algorithm that tries to group contacts and constraints into groups where no contact or constraint affects the same body. Within a group, the order of execution does not matter since every memory location is only read/written once, so we can parallelize the update. At the end of each group, we need to synchronize the CPU cores before starting on the next group. When the number of groups becomes too large, a final group is created that contains all other contacts and constraints and these are solved on a single thread. The groups are processed PhysicsSettings::mNumVelocitySteps times so the end result is almost the same as an island that was not split up (only the evaluation order changes in a consistent way). + +## Pre Integrate {#pre-integrate} + +This job prepares the CCD buffers. + +## Integrate & Clamp Velocities {#integrate-and-clamp-velocities} + +This job will integrate the velocity and update the position. It will clamp the velocity to the max velocity. + +Depending on the motion quality ([EMotionQuality](@ref EMotionQuality)) of the body, it will schedule a body for continuous collision detection (CCD) if its movement is bigger than some threshold based on the [inner radius](@ref Shape::GetInnerRadius)) of the shape. + +## Post Integrate {#post-integrate} + +Find CCD Contact jobs are created on the fly depending on how many CCD bodies were found. If there are no CCD bodies it will immediately start Resolve CCD Contacts. + +## Find CCD Contacts {#find-ccd-contacts} + +A number of jobs will run in parallel and pick up bodies that have been scheduled for CCD and will do a linear cast to detect the first collision. It always allows movement of the object by a fraction if its inner radius in order to prevent it from getting fully stuck. + +## Resolve CCD Contacts {#resolve-ccd-contacts} + +This job will take the collision results from the previous job and update position and velocity of the involved bodies. If an object hits another object, its time will be 'stolen' (it will move less far than it should according to its velocity). + +## Finalize Contact Cache, Contact Removed Callbacks {#finalize-contact-cache} + +This job will: + +* Swap the read/write contact cache and prepare the contact cache for the next step. +* It will detect all contacts that existed previous step and do not exist anymore to fire callbacks for them through the [ContactListener](@ref ContactListener) interface. + +## Solve Position Constraints, Update Bodies Broad Phase {#solve-position-constraints} + +A number of these jobs will run in parallel. Each job takes the next unprocessed island and run the position based constraint solver. This fixes numerical drift that may have caused constrained bodies to separate (remember that the constraints are solved in the velocity domain, so errors get introduced when doing a linear integration step). It will run until either the applied position corrections are too small or until the max amount of iterations is reached ([PhysicsSettings::mNumPositionSteps](@ref PhysicsSettings::mNumPositionSteps)). Here there is also support for large islands, the island splits that were calculated in the Solve Velocity Constraints job are reused to solve partial islands in the same way as before. + +It will also notify the broad phase of the new body positions / AABBs. + +When objects move too little the body will be put to sleep. This is detected by taking the biggest two axis of the local space bounding box of the shape together with the center of mass of the shape (all points in world space) and keep track of 3 bounding spheres for those points over time. If the bounding spheres become too big, the bounding spheres are reset and the timer restarted. When the timer reaches a certain time, the object has is considered non-moving and is put to sleep. + +## Soft Body Prepare {#soft-body-prepare} + +If there are any active soft bodies, this job will create the Soft Body Collide, Simulate and Finalize Jobs. It will also create a list of sorted SoftBodyUpdateContext objects that forms the context for those jobs. + +## Soft Body Collide {#soft-body-collide} + +These jobs will do broadphase checks for all of the soft bodies. A thread picks up a single soft body and uses the bounding box of the soft body to find intersecting rigid bodies. Once found, information will be collected about that rigid body so that Simulate can run in parallel. + +## Soft Body Simulate {#soft-body-simulate} + +These jobs will do the actual simulation of the soft bodies. They first collide batches of soft body vertices with the rigid bodies found during the Collide job (multiple threads can work on a single soft body) and then perform the simulation using XPBD (also partially distributing a single soft body on multiple threads). + +## Soft Body Finalize {#soft-body-finalize} + +This job writes back all the rigid body velocity changes and updates the positions and velocities of the soft bodies. It can activate/deactivate bodies as needed. diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Images/ActiveEdge.jpg b/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Images/ActiveEdge.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8ec8073147fc56a2f10d7cd0386064b619eadafa GIT binary patch literal 15391 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 literal 0 HcmV?d00001 diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Images/ActiveVsInactiveContactNormal.jpg b/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Images/ActiveVsInactiveContactNormal.jpg new file mode 100644 index 0000000000000000000000000000000000000000..434bc142078019d69ae2418e148d106d95d11c8e GIT binary patch literal 16028 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 w=y4i8sM>x~{BVyp!5mEP9 zqP2sixRJ(|G8Ww4UG<&4ZsD~w3Cle@sAsP7xlXlibdD2o$C+^qPeP+UcZeI82U zI!vih=ly^wfAIDA_`TzDs=I8312{rIMXf<1U}DS|EyVE3MGB_LGhochn@ieW5)KETxi&(^UzEjy= zC^cPnxF8IwRQ*uyHJ~kYk~3r)x-FQWaf6x1k{V{%Q6dOtpq&t-p+wg(^bI@&Wldly7n2dudQ!JW&Zec`2+GNlo5vR3NY7%Np}os(n?!G)t9)36;QyLzs@rtrRo}_M0&b4vPiGDoalE3W zWaNTpGC`~^u`ywq3wdUe3oBCXmuELGD;4sh;Xz8SOZQUXP+y1$*R@kVNCO85!7K7P zyd-0hlcL=0xU08RyVqAhk_{}H_}_ARKCO7bMMF;bO-!LizQ25do(1|UdPIuY6Wp_8-u@vlA2{*Q1fau`u-o^LY$eeSto8f>d6UN>C zYNMPka$19TN4ndH(pZ&^y4~6R6%}#Pi$dy+QaqgAbQ6fjR4^aPdeU|2%|sb3hlCj6 zY(nzqmA;w|+SxG1-)GgLEG~35_5$IK``t_AuTM3qrW@#L_4r{AG(U)Jz3()HH2OMa zPYp4k`2O`e$HzBF->2x zb3ClfADO-1*|H&}e%E;Ih_RAwZ8h#W+8N3p{5k1j4apeo+Pe4Ia{pn^(wuht9Qw;A z_vFt+9%wHr|9UsDcrKx*_QTt41=aQwvBCr$|KfoX8qR6P`AB0+(B%lF;KVA!Gij3X z-bs~C-sV?6XWwXLo`+C_Om+6SGU?F!xQ)LlJ zapad#ffCo4+gCO%cYPXaOG&6 zDG5iVe>*RY}DHPbewJnc?FCC1| ztKH2Sf&zH=6M+jW;pKL^Zhl*dxsJE)7^;8OG=IvoC|a$0v~BlOeMomV>l>0+3oVW^ zX{Z&aSi-a@ypdk=rKQE>)#L=e^23^oM+)UsZ9`>-pnjnbAx~=MS)X+tg8C-`rBM$j zR|Lx?$2vFk&bLik&lWet2Ztm{ge+hh&lmRLz#(Yoyxq_c*=)Zas-PnmK@p=e6$90lKh4c zs?cj~Q>E)UaA$Ma%8LkeY~N4TYwqMZ*8`5_4T|14E{wmLgBLc@|_woX4h%=(JmUF}L%v!v~$L|mZkDHMC`!#pda-E4P8yC~YE zLc#Fb{(>CO8+_$}#-T=WdI9J9wHQ{ph?9&1!2BDoXn>J$~v-M(+(}c=Cy_ zJ!e||cm!3FZ4vx!p*>YzBReM+(O`_+1-=_4-)^ktDm%T;P);cdV_C1BXL%SiO zpn@z2&1(Wt2iZX@O_;Qf_9T_|2YTkVQ?oqf!i1N^INmGVE0#E?V!Ts=b9!F{lu}hU zE`+}h1rQYHdVGm%Wbi(R*#raCLLy$w<4}NW=E>FUbNgZnSN#Kt!B;x$Hf|lN+VyFs2`0?Y*>!(w2XP#u;I(AVMzl+SzKRx z^x(|G!vmpOW%YZ*2b&(0Z0?s0Z8iStU0&JoC%HN6N!fMt{hY1@u$My!tu z&-K`NlwBGhF&;Sd;!aoQI*gP=@o>pj_q}tMdKb(lMP?s&2`ipS{edKIis~tlJcS98 zcWSzQx8s~h53)e*FM&`BF8zqqrj=9g4Cw94l<^~uW$9@Uo~a}`6oq`1U%!g39DkJ) zyq%eGuy+Mt7#Nh%_5pcjRB~gVGU!d3%Fp93cYU$!{p=v2WHYo113|^u3T&1(37yLS zhzySSQgYcr!?JiF*QvUoGJLhi?Bl_Z#vF)~Qh$K^hx|C^21xEL{p1QCoZrb%-7wIdNksO&Mo|`=r7Z!oATT*U{ zc)V4-bo8PA(JSTQ!Mka~^$(WD%84u7C_jS%FAg!CxL4^0UfxF(_Uk@+@T`$|MY`nmJRqG0ER%hfLApD9R! zPy||(W^tOL>)z>7BPap(P zsq;rm6$VNI0tjZal4qQ_?^|Btag7L6OG6o5G=-n)QSq_op7Qirxc+G0x+P%s<{vV5 z9Ste+R8R6stlnsimbF2@{o!f*jOFWIRMJwyH}93CjS=OPTG{6Zr4!S9T+Cl9<=f_9 z58Ke#z@$Q*NoEO1Eo^H`-`yUTUYu%y&%GY%LJ+-ae47{!hB89Di_`na}Rj(H% ze@}sDZ_1M7$0~0wNO_*`vA3^6E$5{>6rEP*HIjWSD}aJy;4ZY^{N$i9rhKI?k`J_csLMEeafh_}0xWr1UXc~@#B zw8>P77cqpzfrnT65)<2XUAHQD*Y%9dh~*-@U6D@%8o7 zHTz#vJid^?P6MeRFL{)RiykFIGdZd0Z@HStW#N}`!F3yosx3~N_nxPMZ zc9ulmq{6Vw5SdE5gNam2y3(nKN@wI+zC-~W-VcFX3^zlbtlj9Zx}S`B#Ly?YtHK9i z(dh;^uCoDDOEk8n@{=MbXA2U-P@_m)aL{c|k?PSyWaLEd>9sA0THf23#uaY^l6z}2 zn)O7Ub3K`Mo((ie3ao@(8*%=!tl7H1&@|)j(!>1vN50*G+A z%F;($BlZC=oh!-{&l`(mOUNKD8B9bGHIc&$L}d5|X30~5m=U^OW9^MDw{<&K&{gp| zp~hL@^=8Kd?!P5W|1Gz``X~t<&VtgyZtNY%74kECvTQMxq_&C7*N-zQ^;x&P_*%)Z z!DB8#F-zIl0jfHW-WeD~hHW`m)W2Ar-@546emm!5A?AlWUk`x#BrwvBUPG7&V*oD459c08Z+v zL1DO&JUYXfL|V8Y35# z^Qw?D*Lhb?1=TJG-5jZvV%#LpG^L+YnK@>czr#~!HbjAQ|GQaI7U}h_lliY<&KFB+ z5)Ip6U7%C$KIyF8cA#DL^U7>$)W$_HCH}wXUvt3saLPB03HRu^e5eU8z9zsd_7Q@- zf@Bv=Au9IvBe&7S&eQzg#VVhR9T@-YZaa0gbCHlE%|CG!a#i_VY7y1)2NS0>J;FTq z9iy#Z3g#M>s{Fe-PXFxrTk*MtMLH2@#a*sFHJIO?uXc{6O?2RGL6V=%ZQUus4}Fr0 znH0S-QJFz5Q$Jo;ujiLP67|U^tIxgjP-*t6+@+Y@?`8jGqx)wg{`20ypY8VN9R4|n K|1UAX9{yijqgQDF literal 0 HcmV?d00001 diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Images/ConvexRadius.jpg b/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Images/ConvexRadius.jpg new file mode 100644 index 0000000000000000000000000000000000000000..01d3c253d999e715daedf580f7c0637967b09dee GIT binary patch literal 11149 zcmc(F2|QG9-}jLuOQ9@LOxbHJMM9P-Lh@&lEZHVhVnQf8Q`xddmJkyXl7_5VX0m6A z$}+MWOR~;b#*EpX(|teB^WOjed*9FJect!;eBLv2&CKV_Ip?~*zkRt_BdlqF|B9ip zA;8AQ1_XdFfHel_1MGYD{C#(yM3|y7rAIt?KOago|5P*ntJ;Gv&?KwwaKL}XO-)0o(l=c#Gw z880%k@(W%Uz9}j$dHbQVs=B7O?qhvRYg>Cq=jX2O?*oHF!}yUOqceorx%q{ki@%mg z8=G6(a0!68_UzruzL)biF19`X;9wWn%W>k&zC#zyIi0)@ zOR7BHFQ}KCSJBKRrFxAh_CqHVKs5*zuUCHgVC==sS;3MzvmM0fP-J;Q18e zePiPtw}?;1Mu&B+$lq{Xs=03@bA(eTl92h07ECcCX5kb)mj$do`14IHU^@=V0?;_B z0`7C?@(5~y4xvim4FZbjXOkEvII0c{7+qumQOPWz#GeJo;b;z!WnpU;07GruKs^f$ z=A%!AvjC~lMa|(o9}(kT0Cx$+l#Ip ztj_}SINKmQgAZ2~WKS-yxUzspJ@lAe=20pOP+bv&&aP7e$k8l7kMAjLy%=0{bkbea z)^SQWb2kw^NJ4BbpeVe!&$_rMCaRaVFP1`MO6cUVfUn^MKHAcG*v#xGgt1@uI19iQ z6f6Z`sGBhQKExaZ(TajCgoYc0fH?RhzOsPDizH6Qwz%sO3m{tbb1lN0X{aaF%U(6( zg*jM4QE%pE^*&CEaC~g0llK|xw39MSQPi_xEk3#1%FQewnVcX*+WAL7AWu50V7vSw z7g<24k>$3wHJyrPvMXQDjVLp2+QoNWul3Jfbo z`=v#3&b@hKWB-TcgeVJ`KEwi!|2yHA>!Azca^x+dXZ7*K{bht_Ea1S<^2Po)j>n

ok1`_ODn#6BnjW=I=73Qy)u$Q`{iC8K{k5 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 literal 0 HcmV?d00001 diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Images/EllipsoidAABB.png b/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Images/EllipsoidAABB.png new file mode 100644 index 0000000000000000000000000000000000000000..f4ad423f1ac3e08d868e9ddd0f263edc1fddd183 GIT binary patch literal 158978 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 z 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 literal 0 HcmV?d00001 diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Images/MotorDamping.jpg b/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Images/MotorDamping.jpg new file mode 100644 index 0000000000000000000000000000000000000000..9e3c1e5ed40faee1c18137c479e354a2bd3db961 GIT binary patch literal 96716 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

P^@SPnPU! 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

+AMgOAbzx-VeOHHV0jS9`&q|@`IELK80uPG8X z*D&H>U!unxe5Yt~*f}mkgiDME|Gn-1 zeE`UpLX~G zggILVIs3@+&%X7qL#Le4$zAX#cHG?M$b+4k(r%7D`RxEOezbkUYm;R*dHK*Z*%g4h z)E%s*WCe6tWr+@`F2iWruUovMyskrs`QvT>D*){8Gl|oFeqtNYF`@g25+QTE1ZyuK z_nmV0=gud;6aHMFp+J9uLYEJc6sM8Ws{pGsoM`P(+7M66?p}xy_;2AzfPxSySS}t% z7A5sK*Cp@iU3sL1XdeAhPN&uo>22rgHXtlOAy9til&{Tkjy@l*(pBX>jZUa3Z_A>_ z8#3$`pU1ipW8JoAuKpX^__u$CM#Ht|js8RxyI~B@zP)_19dV^P6FEx0+X<+Kw++CN zp3jyzpE>2#yTXs8A>SVo9Yw$Vm%Z4@QqzsGDqpb>FbF(%l|SZ)Qm>^DJGT9AHnI+jFh8yDws%9{EyU zKA<~vJQf^|!BX6O)}BsASN2O@+!8vP*;lo+^ewyk)H@<^5D=to#UK_y%ns>yMqc8# z^IW*N!w3zT#^XP$gJr*#g>&^W^S031U-n9Mq$o5$>Y!)a39r%U3Vi zrRV&3pJUJF4(hZYHT0-73DC~#GA0|9g0VxtU`BH+JB@+n$~npMSAPs{+RCi%TEvme z3Uf;QrHq%6_jN+BVPc1YNLlr!DpBi{ug#)_=Aiijv1(^Gk_H#PJjz^?wl2K%_1H|p z2|IL|Vo+~o(CuaPboEBi(^av<<(1Gnpn(dMJgjib>gqB{;A1O`nJhv$Z8mw>2Sw!@ z^MoINfL;Dl0N&mYEke+A-Ro;=!h2=kPn?eAv%FdAqGSq@KN{DN`05h6=0itDO(Cmo_wpRjzXV8$8;4Gt^#r2Xb)<- zme!y#h0lO8*f>(6CZRWMF--p-890^}ic>kXc#J6HkwuI9cs6ZbwX3{WRc<51* z8rzyP*Kr!y7{v@1(*Yc*(wi={NgCx&K2gVq19_Cw$A9vI{;NVr!RttqQLsV(YIqcc zOT|)ub(!%0(e~X@O{U+vSW!R`q$wpTA_8J86s5&R7m$v$h;$+#N-v2G=|rSSmkv^* zRH;#_fK-9dd#?#41XAX{>d%>R&ROT)yVjh4Sj<|kSs%&!?RW2IKg9*uDmE6mmYWnr zQUpBE6o!iTm805qgSlGdSj>bIi6 zwu^gBq=80{AxbX8330XVjf3!OIMj%3uHjc&tS7p;qF{?_oS?koS2oZdD; zp&8xtCAL0I(2h5hlky(v0CxI+biw+wA^D$@V80jRJ&8SfEBxmUvB=*Wy0jDQoEjHt z9PM*l&XnXfzPJns$~k=DDFFgKGAxHCY)?SmjM^B+*RaEg15G;U+krcZp9kub0xCr^ zNI}X87NypV+c+d-;xqW?B2|4;RjQmfnX9=G?!gS zF5>#b=xCM+X%|_ov$O@~hmw3#W9CKAh8kJ3s7B(|dk${$Kt;U2ImW7OI-=Bjr3!|a ziAYF)>w0Uot@5f6tke01lHRTv)vii3F)GUOm?=qPEFt>qhyYH8dP97XnqXVv@n*dy z`hsH8T^+vS68@+}-ksF>OKO*eAEV65P-85QDP1DSzFjVmL+f%7vZT+(7JAn&Rf6#GZngzqT%OrNf{}~ja4S6kpo5u0V zMFugUd7ODU4*$&*0wtEd+-yCD-tyZK?}Hc*#uA@AZuR79Jf>OOeGm5o6(W$uAP{%z3vo|j+8QB?*c8##$aQ> zb(6L#KIL^OKDY~)Sn_*XZrFOLFtB~3ze=#K>2O~hNBL8ZK4y;L`|x(;t`uZ~^%Efs zFpczKky1g%UYm^vfboqXA^{Z&)h_!Q3WmK=YGXmsA*A3%0Dy^(51dOjZU=rYP@ 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

f+CS3i{$QfT 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

Q9t)f-@PSL7)=@sPfGB^f5{BcXd3ggxMfG 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 literal 0 HcmV?d00001 diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Images/QuadTreeExample.png b/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Images/QuadTreeExample.png new file mode 100644 index 0000000000000000000000000000000000000000..9e49489cf12e75aab5f26d435a18394305554f54 GIT binary patch literal 65699 zcmYhib9CHav_3qs8a1|Un~mAnXwuj=8>g{ttFf)dw#|vn-}HO$UGF<<%^Li%KWFc= z@$7w`6aHO65)mF39smF!N=u0;0{{@`003A%EI8<&8`rf{&|4QDT<913%~6RFwnwBz=rJ-hW$l>QVM}sVf}g88SUeB>9avM^4W-Z50Jrnfm@j)5y{-3V^E{8^nu3UiEtBoWa0MOE;C^Se) zmNwk%R6FqElZ|1_@(TdaKpP#}3O~5!nJ$MF-qS_A*3!~+Zz+HT0Cd-Se`$VxjPI?j zq+5D!cfc9%rA7_efBDA8?;OL;_m!83Zy~yTj0Y!xjSGJRp91kbEUZ{^y20EtVn{08 z!wASa?=^TPYn!(@5RJ38;fmyv<6b}9e301!r m{j#+=b)m|jXwy3D6qB^s-LvZK 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%0A*lMC;xAs1sFr`kN;FdsZoGV z_8FtDxua9m69rdf7Z_L)X>QD~j0O6C)&hl+ae+mmRI0GbV+}NYt3!4j zpKX^?hTs?){F!7XmkzKrKur^1I}UVdbo#r}k1mz$V+U@kNK(Bv6~yR0H8qoW)&1j8 zPp=0xZ4n>p=A4#TEQ*wN69ZhT^h0~@lrNc;nV_lw0PuszS^(Ug4-vhs^SvbX*u6Sb z*kQOxX}>n^qm82rWjYf%8UPZzyxBpbw79bAvH&XN8Ynr@mZ$vy*;4A?j2ALI>~j*~ z7uvzl>mnKSH;(CS@KD18`@|bj{8srb9qL9@h88(P{Xr^pRtv z*ECb^@@vikFs*KnZ^OO(uxEy3AdM-Dsy$ z*n)kB)XU#(v-fbk7m0A-<B%r={rT6^geLyY_hR>PxU}g&06)Btqwt2H? zkF!ErCAq_C$DJuShz7!zx@Hx#Lnq~Le+*Jwmvpkb|T?lz5w1O)eqHgMtBcmEy3J}wl`-nG~d(w1=R8@42T>8w;TfJp%Vjp)Y zW$XMTtiIy{(q$~;9FwherH}mLS#Xka4k~Yo@MnnAsY8EOFobZDuxyvQeK7J zq}QG^q;{eS7q9HwVDrnssZTc7v%AAZ<~xYQL`$k&seST0mgvlWU}aKV&V28ja2~E$gn~HeE)LHu$Hc=t07)(IV&vf4 ziKPq)i?VKUpFE#>TOQIO|qQEzL@EUR>Tc#QCkMZRxD{#r2-$OVRjj;JL$rF z2OG`pQEvx%+awFHGw=Ika@>``u*(J*NPef^=_QvU->Uc#L=4+uLO#4MD>B+^s#aMn zomE9(j;lUdc6qkgt-H%z)lqi*{rz;r&}YB9hjE!jj)xFQ)Nl4~S~3r&zyGE~(?#3X zyOWL^tF5`j<^=B8s2%UV8$Pj=WxXr)yGyNEo$3d9`2v6O-9xki@#9P-goZKG~R{pEA9F41!?MM%ZsQt(1F4UiFO=gjpuyegj~Lvw6&5+5ZI;G zWTcg*$LJ_YYMV-$>cdWnFDssOPJBooe?id=UThT~UC9I9&Sq1PV)L>YEf13;kSaA@ zx)Pf$79Zg2`|m`d!EuDdLo?CBcyN%LsPw}Cm28()w?14N=Q?HaG(3DTgOEii{kG1W0=}uZNllH-XF_?x~VCR5#nmO z=*)8EL*=@eBUw`9kZos@YMZf*+k(Gf^|P-;=_BzM`K?Ia0 z-(h!SMRzxUoA1zR!}xf@h71Ud1F@-@Nh5-5^ypoc=RKL!dUJ99Es0t|`E`&Mv!29T zQ*zT-pI*?guHVx##-nPs2>5tO>?@XUW1nxvymzV>K{H%L$JT0n7z`p2CiZju#!P+V z;%*u}VkW7I<~u!NYwhxQ^ecTjoKUFB~33+t};)kLR(hxmT>}ba{@H zfLI_a{pMtyv7KUsIr|nd(_okoJlE0(zjqX23CNGMYVG^AuJZ*M`E}Miyw!4h5!ko1HB>i8D;Ca&LF!>3^X|B7^EEBnOF*hH}^=_km9gnfe&JVq5p<>)#4$*w=$ zpVPJTrVj=9CXfUP13ss}meTJ~=WsKIY^zo$O|fM3C&|fX+k8e)+edo^_6BC+l2h79 zAA{oDypPi}?&WEg4F_$C&d~g(+tnFPw(`$)zeZ=7qxLw|UrNAvPTL4kp+-FK{t*zJ zg&5C8Wsq}Wygp#+`dovzotVDlUAAHNq7!#b@GZ-aw}Rh&5AE0A*;kME5f2vTj36ie zSl;U_t6c8-WfWa)7H28;%wibR{JeGR}+sIJQ+;Wy-EtbuG zb4EDi@3z}0$u*@|Ea;v-ZjAP@B`6xsMG(2|yoqA+ zt5nmEV27S9FiQ6-S@FbEXw8&MXI})YNJ&>yh};Eu9k@y6Q>Rzk-jV!6S(QD0hQA}uAA8jCtZMn?%7UwTc7{a? z?v918VwPtl*!QKUzLIp!FS_^(s$a~o%>-t)-S8aenpd(GWTxNDf(JhtbI17lhV?rS z7qV+xCMkNK*U#;eCztj%TZ67g4x@!a$cmd zU~iqu`FABbny*pMt}J$vM1grLb5B{* zl-S6WyJ^yd^hQJt^m`%8ZTzD{x~}Lx2v5xbt}9z`({MsjZhVX1=4SiO+Oan=Deyfy z_Wy<*XB}H4k&1nmD}m}vXEKv<{wY7$?mLAQ-a6_3=bS?9IJu%}!QH;C=%N&Q>ikfR zFD33-4@0X0kx}bR#Leqb*|s9amYHWAgQX&=rL@~A%%Kq%-#kHL-9|G(hRge^hO2iS z8DdFn$e|;|bF8$bRq^iq#&b>pQ%f6O+S;^q`<`X3?!V~iduSsxBE>pF(fSn*ymWEE z2@xGFV)5toNdC(b+u1#!M3j^LGLFQ_nI#csQ(xvq&GPy?BRxaDmSzU7@lbjodZav) z{RaE2g83(&*ZsL_QCCE7)5r~@+^roJBiHX8M-*XUw>?z#*LtCyCKWnN!(T&fN69M|q7J-P|J)q4q!X5Vi1@^_{eHfuOvA-r} zEjInCSr~>yk}La$hO&s-XfDZ^;!k}MecD!eshdGT^jDvVqw<@YQi%$?EjTnaG*mRI zy|J?STCV=;i#(nNU4``f2cd z-FO59ZLsUb&HfnUc0VC?*BN7f*i60S>hQ3FRfDX`+}+(y7OO#nNxPAdP2|$j5Fg6P!#5^wJaA8!uh_l-@j0?ErN!&tpPxlVvjz!F zbaZq=LWbqDoZX5W0Nv^-Sbrd~bj(W{os~u5}cN;fv_41UvF3enZE z2LmY}Oau&RT}zvf_@j|?jn%OGkBnNs3m1(>t*R%4zWvGXL_;j?BHO4A!p{~?sK1N<<7pzq`rXR*LAD2$MjaF52HC~B3EWCQ|KX1S-8<@9Z}sC7f)(y zYm1E6$z-+7cQ-&f*Hn61)yT+if4T^lPDw*cYo!9~tCgCDhU??)8MRC_9vmJAj8Q-PtWIbds=4L92*;3TwJ!tn2?qR z*_))fL=b|VZw-ZFk%^;&7t7x6W@L)^laHJ(HG=c<@@i|zYCLzNviW#XzTjJK5fqSo z-U+7#`#BUSXGtrUKe*7wq4^r{XN;OvW?QaQnxl9RM>Z4aAz%7UaAae*uh0dpoTqSF zj|+&$Opq+KLU{BRyd7JLOnBU1dfY^$34`AAi4ak zdqK21?S8(Zq6nM|ohEFLj~8QbIDE4&3bGzdi1_m5&S-`{gqQ%&VuAzd)vMOaN%=TB zWh`v$)f(G4u!aywLF8hXQ&m+p2pLnn9N0+4)|f~%CK{AyS64@??N2`_C7Rq0+{c7& zvf~eZ2&(Mnt7eV;$%K4?Wsio2=CSNXsFy7A^msuKx&q~kArq*RPhznNF>H3%EScE_ zJF$Rub&0(9iqs4K!j_nb51W5($@sD4lBrV6fUJg-1B9bw<7jj8#Fd{mV?;iW%>{jC z+TG;$JYDlx^$qRABH=##HxM_f>XuXgRT%u=#GvfP-JK7yW)zW$;Rlfmc^?9k-5ZWi zNl6)mf-Nl{0axHj$F~tCPl9zdyvV@M-!E26I?@O-cXctkMhWtf!Px7>189q{Z%)*J ztVPDkbR5(>Md3Yx!82jMV)4cA#`VKIx)=&Y{h|RJ+}zi>F~X@L{(j3UMue|-K|bx* z3tvEBIvN_oMi)z9lpk(R{7*Ykz%DN7Q|UsG>zq(>Vs^70V8UYK;&w*TLi1Tcpu$D+ znDc`nfP*>)Mkpkf(fjnc`2>n_WS1`Nd(Rbq0^S~eUf{>e%{Rx(2Rl1EK4ylXYUq2v z%iW2i#d>QC3sMok#}&wlRC%mkpZTkJ_K$ z5&7REBQ4|(M6>R8Cvqo@QCjIQ|Bf5KW=8g=9<3Zze%FeADI<9Wx}2S_Fnw7J5d;>B z8zDwbwu%4AJT8j1#`d0@CQI_>1cR!9*4)_a_5Lkn-BSwzp|C`(*>1P^FY<5hp|hcf zJeQAv7<=?29Q5i{Xy4(8LB-vgo^mr{d^PybT&4L0Y@;^ zdhm6TVr(zT==q13%!t%M<^g%`{bM5!c-} z;iD3vSeV$@C(F$|gRmIEb5vsdH*dZUqF|FpLY-`EK;HK?wo~I~*-IxrWw_pHj%{~V z1xgv?NV_i>_?$Psfi(=d(~ip9;o{hBPw)pyi4aK{8IO|{GGDAQ!HeON^0w5Zr2Z|J z4EeCnc+u8#_RCH9%OzCbkfhW3-H3$<^De}KkaIl#ot(r7i`I02ssu_31&e}t31Q^Iqi74TYhocja{x+ z`+CdMdNs%D>++o9)gh_yQ`8IFJ6q(SzC0H}y`th`xx2baqH?S=?V%L4arXbW;gFGDbqBBM2CC)Bnc2 zaVe&NY@n?L|AM8MCEjRibzt`&fveHq+ zy<0BNR>=%{amZDxg4T3%e(r+B{}ySY+2dq-S|dJERZX&!OHhy$pCh-t?rhfO+LlPb z{kH+jjk72=t#W4M>kM`GlNH~Q)Q=@vrQ$hv%O0yjF8@%&TduAD2EY{>8ym3&Prq_z z7_$lpK<>`BfSp3B$9;+8*m8!<;L7beTP(2aW3VXO%5u4%l9FO$s`Aw;ob&?@T9Zc` zS+MxoUO^U%e%&vIrqyF+f^&ohoPh-!W6E?h!wKG&5Y^lI0y(12;x4BUCspP+8=-E%3DvTf8tv! z#c(J(QNnv;xuPbgQR{jk7zIb3Wj~q&T&(}&S-;!C-<%em`d%_;zpkr|_Y9Kr+Q7;| z8Qpg0+Vydg7)=3e($08}#6$T409L=_(xo4b+aPNE6s0HK@{Hd;X}K5$HsNdvoT(Aw$ow5b}rh-lW|4m!1vYJt;EAI~7$`cEI6e2zWTm zRZs#0vqI<8UH_E8O1M?)`)&L2a+9(K*JF;4}#gdR2r1(RZ(qX*3_<{n5sDQSFEt!8wJW z(kv@D3NW1oZdU#M&CE6eU!m_Co*FaN8H?5%jk&ZMWyQx&=gAY|S*s>Wr92$YSG&bo zwwi<^(lo_{hl{I2Kkd8TEPLDl#@Zo+^1}yrH6|bF)swxhvZ!g8(fZ%`! zowecOka9bmt9&^lT4%o)vd!jsYQpuTl)=xwNsQe}uTjb8wy)7jfs~#Vq*v;W1x?yr ztY>`<)U(wsNh+l{uWFx8b1XkuOD-?>O?`eIWC#E{m}7qv5)yX7?}WTBfe8tNi|+2( z(B|aiTUyOAxT??>LLkd6J|`GtfwcXZd9_95))*D$Z1U zA>qMX7u{NTL5Mw`2!J7L{BJf4yVlVmJ!;0>^}3-of34dQ@d(IQ%^NVQckTKElr1p!#A`RlhUad{F`vwC z&SKQ!`FQKRg9Ne=hL?+yo#;@UnVx?p^lfUgX2Bn7y`Ve5UH;$k0&9>`Eln z9$>g8f|}?&#rEdCEQ-OSwXNzZKY;xmcHxYht=BKl+FJ8vwhRCf>{8J|Dk8AUDZp&a zx(~|4<=nJ+o^7zUKUN9qKb&T3qhW>5as|8{UAX{&$L~c$6iqZ@H+qATnQ&p0%YZOR zDr;F67mnf*UV-81R6g)F95%d8N*iZ8Q>?iga3N=ujd{68a5n_V-^xRsl=r1_#e!mM zVSfinF;87BHD%>Sx3V(B;C6$#reeawynxVw2dFR!OVGs6Qdm6T{Z$*BBRGKXB;I|` zydZq!vk;}+=*dYM$ogI!?J?DLt15k>(J1H`K(`@SWT}aXA>^~%dHub;BfzeJKi*OJ zoOZTj8T($%^nAw^f!~_%{LBC;U=VYm3VB}7TdTUu;3}kWyxLlL_N5s6=`k0PhlQtr zDU`bU1|WeP*JhJWL@tUQ+XVn5LAXBmN0WtOQy8^+Uv&kex8H4(B*+Cz&jiPwQ3F_E zT3tD3OALGq0N}3f?u=I5^c1)GD$8fUvJL@`PB!quU)NCvt71!h`ym-%m0(j~aksU# z0dN<n~II-3cq{)=Soa>Euq8@@08pWj6E?y6^ST4Q+^fSPk5J4K%RXsD8UWsPl2#FF1}gGKPdG75aebeR4E`1$eSq@4ml!onzcxT>RmI~g9IvCAW4 zVnm!bBqb%0!W(T6DJUrLbX0v2Mk?yq{iX|5L94lM1buFu;(I4_@a(fZH_)t;ur>Te z6GBb9L#|F%De&O{=E0>^CevqBmV2_ndPesEOz(>%3X)z1;K{SUyTXhw zo>)@BM;xI@m>orG-KwZyV@Wz7l}tzZ!#}19ZvFG;&r1}nmb>jVWf2&c-u6#w8Q@$i zz{fZ5WtFMd+1a^!A%>j^*`se%5L3H;RGi=n?Utr^Ta1u>8S@!&ml;6hFiT5I;2Exh zV@ArNH{v;8QhxUF`>I#@FG>|VHi4x6ERlB4!#%%Pp&~+XIQpkF>cM)Qk{0Tg#m#28kGr(3 z^Zu-PZjvAW^{5d$^EFV+d_0Fe^lZ6nJMwO)M1+LAqNVx7Bp;P)xNewYOk;$2`$=E9 z-Be-14Z!EKCHcma#~LX2(2kM#I$q+TQH8x+zl{<0saJvz-_aevDDD^7F3`k;JmQxe zj^H)2mCWcF8b;w*EK6I`S+~bG-I0iu_$n$!Ra=tfjd=qUxi~HmvcXGLeT6{t$@(VE z`wA&aIw)U^Q%o1h(#G1bOSWW&Uq5@|0D3p;d*uy4G^K)E(EdF=BLl;ySjnMm6%c@| zg`lsT`gFPcBYLTlhQ62!rQ#@d>|B&Tz4@FYHFbhHv6Qigkyne%@|W^@c!l+TX`C)3Ga@nP z7)4)4=N$m{Hy%hFTLSXMlqFBfjv;n||0hwmd9NSQq0@{;Ib&?8<>4{5yVoqBYfPSg z4IMSm-qF?R@qQ!ycc;q74tUmx)dUIVUS988#3|gD-Q=W89~p*|Qjk0&TR!WbIv7%6 zi||V_Y>ORXe(h_ttw0zFZQHQXgB)C6wZx`R@d<2!Bv;C~XGnPiUWH1Z4BY#4w~3HE z%88lJr>sS^92*{a{xFQAglbdWey zPSWNosuwZJ^$*2C=;a7RNLu%rC*q9WVWF*XR-(;uys+W7`R*vSSe=+&uIE$!WG&j}w)(9AQSh>t z_bFijflx6p9)WnnDu6Qx29Cp8Y2fL$ErC}W?+7)qfI3|7cGan!(nqM6xc9}4wz3G( z(MPmc#GG^t$n`sDiM{{6XS8KsjN~4)`c=5{dcr*8<;zwnqyPrQ>&=3c?_c{x88s_2 ziL*>)|Cx;AV2k2%fqJq^;D^GfXHnLaiXSY^s~ghOklvSyaheYd5sM`n(6=8^K<`wd z%ge@-<$0^K?&t;Ir_#5gp`K-P_r%1cL8{#vEEAHF{DCtJU`2fX{giQlT}XAq;>?JD zT6C$5+Fg2Sbw*X7MM@diyV}^r5JQxSAA|z0J+)qcX%+s~!JO?D5M|D!AYeeXT{}#R z{dMQ-o7K!%HRZPY{mi*|1vV(I_yJ$}9A+KMh*FBIUWt#&jmPfV| zsgBqGVDIpV=kH5Twb$P%n%dGAoWx{4ZcsZq&&?W))RdK`M7UhZprEvn?OC=X(F5;T z%|HfR3IgVIwsHB|&FEhhHC<0Ca6wWM#l@z}Vm5nVM^|^VLkpYv>{+S8dK3mW0(BD5^_pcz+6-(KnBPP!Wb^nK?#}QEy6bqh_X>!7k`o$;Hwx|0 z-3{p`N(1m3(Cz^H+wHX}btz=b;Y!t=c3~4qzi{Dw?CZSGXOEcG=tJi~!I9@{jrJKG zlUO=uW`_`!Q8%-}#3x>)7_1l>;;Tg8XhAgJY9~H&YCBE>xns{)2bpM?qZ9^TG!U-0Z=N(j?+1#-#{7G#F+DlqvTy%J_upMkKt7}X&jgM=`)2!^ z1fn*%FXM;&?OI2;%cDZ0wvoNGUDV#Cg4_px?ntJ&{=4Jrm(M_`*PJ8`x!%}FcB`dH z)^K2+(1ojCQZ45~oc<^*-?lAmhl2DPmS0rVn3ST1(yE!uuC4b8NCp#pclh5JXiouj z1YPnMF+9c07FWp+gjI9j7=5a^q=s64*)@7igs?Jx+)^ll37%c}HN4AEP3XaRiH!{T z?{ChCdZgBA8ELw+*=N*iXCKr!PI8r=UntI=Ji=CKw9Y^8P(X|+Cay%N|M$7>D3oob z8Oj2hmdzjDoGsp7p4LlORE$@AQ%tgU5NL^}uhQl)x%%H=Nd1^xi0`!$_Hizsq$BmH z#Lfh0Bb{~{h_HejUw~XTCi#D#U?~>nsNN{0YmLr5WU(?_!B}bhLXKABc5vJNu_Bu* zNzo~X?!Uo$W*k?;KP_10$bnKVLf8+d6R5dX|KQdf?5#US=i9I*hz||w)>(=L3x_KH z0kU2Hdwjz4M0%hI-${#`LdOh8#iH8oJA&}NVhf?w{Xzy$Qi-C7>nqfqUAHhQECWv zfHoh%TVFDt+5dtTyZR3jJ36+zD@a~97?CWo4}Y%Pt64Zdhdv%gUj9QT96Kr=;3+B^ zy?LeVm2=XSQyh~SZ9uZMJaT8SHeiDN{HO-v$2!H*G|ug3HB-4_Bu5b(lf@A>wEh;!)G-TXE%CWo0s;aHIuJkLkFV!81ZT&b;O;2VKDn7{ z=~&oR>#aWxEnoZ7hga#@X_cVmx#x;Gj%&~V=Jy}S5UaXH)eM{*68OWzHBJ{lwh3+& z#6rh;_n&+d2v`pR`&PslHtz)ZhI$(F&c;;i&AaO8!zLYiKGuXG;>Dy8+b`u)MayP& z@2-$b+oo+;epKPWf-|Nke~7xN6dGHE@vDLG!jY)nX7Lr#AdIMzY-EL}4TZdUp27y4 z|6C@AzrtTw5p%pDzxpu=4b@za{rx2G;)3%7MR5M1uf=bIv71%m&y^f^WB4-^6{wkIN_R zv>Xyu@tIoTxorzY%SO@C3%`H$+Ht3HA)6=kipE-rcbsG0>=EZ36Yt{gA8j4&kY%gg zR_h?8^*Rq{FS@qjJJjWwJ<;MzdPuc$eG|psS^aRmLoTa}y%QF*2*v2Qx}<#+pC9r zktKmP{~!F3{adwSfijU6icxM!QB@p3j~?LauwSVacr~b$s3^hJ@S}dT( z9wmPoIwagtglu2Xg|^O>UwnP@#Fo$%)9=i+_`LoAJ{cEq3xrX+c!yy!)K)F*CU>DR ztWWTn_BL;?y`KI8>~^r^&F#wD7nM{2Lz%M31&(0S=lmsr2c>W_`NFsmFMJceO~|T5 zK_SMJ#cZ~|1PRJATeq{S2@2*q`U9Vr)^+MvEpz)>V{h{(!mKQvsO_`zXgIO_*%e(> zrCR=r7n_>U_g1&Xo5A|$ZiHnbQrQU{?l)c_gNYQPwtbiRi6PC0!I1&tgWk8D5oQUk zDQ`QvI|5395BNItY;zeRdu3if2^$M#bohQMR_STGm{CgeWO@ORTozMf?QGKuMt#n2 z)m;-3SUjn7Bq>$cyD_}?@@8L0M$CC4s5>PDFnO|X%T+KX0$(O^pI)tbavDaW@TBu6 zvur8nMZP>?uY}`PSiUWM}t}^cCa>N(`N4IvKM!NXK(|sa@E6&M_kbW$JsEwiIKDN&w zWb>+7DQE9xkE$?K6c`LDd#mr1D9oIx??2%jFkhNWBVfM-`h)<}rTe^ud7hhwJpTX` z3(u!6EJ|{&@14aT7bD&Lc|=Rnlj&q~#>G=F2^3pZeWE{g5d7pSV%MG)>ZBp=HFeIA zK?QJiAiudSDfKTwM=r+Jygu>NgAw&E^$IjBJ&YQ*D`_-7Gjjb;YTQwcRVyHt2)d2I zWYZXp|I7dc#*PoX9Kl8gz@d7xoh=_j7R)a5c05_L z2g_F&ZjBXD&-pN`j-PdbDnmgTKVUm7PAEjtJSFqaDev+2KK zE!xMv-Gvzi`!}GuMXv*uJQ7>OnJ$;pbbf9-A5|JzrB+ZG))E%|HDx%FM-3t zL6xc&x-HZuoTx7Ml^uJ8NvhEE4p>#FCVF4sIelPCx1<@dn$&T`i=kPo@B{>t(fG6~t>Rw_iwaprmi;-Cg-?^z>IU0-NqG@gE2obv7F2GZN!Caf<3!-SdX4b<|)o4mR7@rca<;15BgvuO7MP-J)B|Ikpk>US}hduU{ffwxi z2Wch3PIxKdo~)4yo&ToTqj}tqOz~Ne=ty}8q$_^+Z4{q(({_7Ba=YB3wZW;KWmee` zQla;b!#)`Dyy17(mU5yfng#~xX=xGXu5NB_8X6jQc1&_JcGn+gQ|&Od%M8B9#5}Es zQW*Ke-17hA6c+9R?8Rt*tu@%)Z%+*mpPW4N$B#88q$orn?GWmYaVA|YR#GB6(j1?X z<;C|jofk{S4b`K%O8B&e*cR5k$$L#5UHi1ajF2#^Ve=t>oYnZ&(sA7pWyq*$b#)5c z?ns4lB{lGhzDu%AA?KiSvSz?yn0Z8LOi^(}xmCrr>QAzEKC4gLtbV!By?YRrIpSJ~ zQu}w!(QbaA$I+kPS_dnwkWfsLZPLq|8&)PJNo?V}e{qCHo?>}wd_($Z#oXDvZ8j6o zBxAo6dY3ss&+rmkumi(jr*_sTO~hZA6M^_;+gs5C=z-XpZ=LPrnukQmA5WQME4!UG28Nk zLdz%(P5m)b0h9%+5)Hiu0-d=?Pk*Rm044-QYCl&dCKFH>l-94L0*Zm(y49}yWSoYJ z`^(K9YHE{r7dxzmO*JlCavB`=3pEjhtWX>cVXt$G9gFNr;>)~ocX|w0COrFUwcZ+- zN|%;~k$P;po9#vC$1K?O3M3*ctJUGK+8L*>X^ZbmdOL@Cm0v-=YMF5zRHIMDqzhCyEo-Y2w(^>9ae}V}y=xAY zGs|fNg{cIgY30U0RH)wDbF+{YUSFGM0%vxf*p!sJVa~D0ckCO_AzgsvH83!6a&nq2 zGZeYs%>^VTz7tTX#d=3;T6;2}p0P%e21B#!-Xx$SxxKwjPJX8mNWer%`640vd|ah} z>;1v|fxl6hWXM2Qr_xW>MRh-{UdS++aB6GYQ@Ylc6&L59%Uw6dr!(%+>~Pcy%#pSk zkb@G#_zYOmy1P#KmzPK|ga$rW;^!xg5;y-=m&A^0oV4RaCAF@x$Upr+RwtlXE~v24 zknad#OLpZCVrgbOGP6?i7L(JPp^Q387f*cubHLL_IIWvjM{-${u-`dDYp4|Cc6Xkl z#j^B4op*OlNwKGR$Nc+vMt-;WJPeq}g;vN->&?m#o}t_E5?}nbvFCa)K!b$!USE8C zx)>9&9!*bSGZhyV{R&P!KR*YEe0p~Fx0)}<-!EY1=)Fi~5)q*gvZ|hkJ>1BE3BJQ0 z#DSG>P;I4XWJI2dph@!h_~?DP>+I{1+7wM8vI3Bf!RUl-=Yxz2Z$t8a{ras}yAQ}9 zewUTO0p8)#8@A}!9>qXuqhV>em9LlvkVl_X<0YxQfbbEpU$%V`)up6*Loi8LjN2j; z#TXKzN%^Y4JEOx5(O7^RWW)h)86mnweDF-oSFkd7aQ<7inU?fB?WuE;(D<%s6$-;u z)_^;lF@%R7k>M{c_iNLHv6)V0mE930;t zC?v`WwDky2t%^&+E?=p997>on*5$R>UI?wT8fM-gRS)*=rNg?w#)|#(!Jt2fc(%L= z8PG$$eThbn;Dk;axA*5Kb!BDc#KeSxDp{e^pC6QxMNWfM{NgT z3xAT6bHUpF_fJwzBp#;e0#%o^kMIH@K>*YH)B+g-fv zzm`8h5K{qW?yQgj3IwVrk5}8<0nHRZ_n1n1xB}2`{5z1?WHl4a+1o7d$~Ww^IsZxD zNcQg;dZ?DY{yBV%wWZec-%YdeTqaLH6LYlEE&U%@d!xzB1_)t=VzcZ!e$X9V3k)Xbs{4{r1s|@plFWc>BJ2o(| zR645I#CI)*(p5b5tQ)vF>KsW@WAGbRh@_WeA~-PusydMW&EfnMK)Ta6X^D!8Iy+a6 z^pWEAY|fXv>F_uSP=FhUPs%_%xm+y(PXs849=owc45}>92B!es(C^}67o`gCt9?Kq zmJ|O3P&&hDJSTv9r;uu$)J^{?u&tV6l+f7c4A=qB!ksi8$K$C2Y$;WL_}vb${6;f{ zA_&$Jw2j2d!xBx0li5de-6kNqjV@aNpT%O(z~*~@scr1*cC=6n&^Hq3--R*zBLy0e zgMm(x+dg;KZjC_~H3P#=-9PYjHGcY29vxB$Bo6Cw>;+k}`qVhz-v49e3jF<5=Ok=* zRIHO-t?^rLU3qKtZV=o*p;S*hB_*rw;5N}wYsAO@OrG1fTHkj_?epaUyyAucC?-gwNo)6~_~TU%O? z5MQWkpvVo#<>3g44$SGg=ofOZIjxDNMD8gvBn<(CkKbj-d}0jru~@T2#f+T8BFtPc z@y8D~^FEBrY_W%05--@*>;%RgR$(+CwiI^Tp9aKTIRH&NiZ;$62*`Rqe*BokYAhWD z@JiKIBjSxB?CcYKFT5Y9r6Wbnv6c3=|KxUb$<1SltmBql^9?G*CDvAl_weVWh@A>!;*F+fa`3{F}6aE$P6-CzjAPLP6+8mHsKzwwo~9kvty10T7cCO#+f}p$~YD^`#!dcNa6n9 zA^IYgqfWzinyg5)-^J_;MrGx86N9mv+CDlA%&4$yHHJGxaI#zmx}#B985lKbiP*yp$Uin zE!7XT5^!4U0Gx#FVx2vRCD+AT`Q6Su10U%FecG3gEh$eSuRmA}uRG2$L0lrD@6tK_yX1L~O}N?hPfOsj;Ly-T>xl`mtQX_RW%KIE%=wp(|0Jxh z;Wd7vi|Cp6CR?|`p_w9jb+ILNy>9#X1VrU(D6e}FWg?n!FM9rlrVZb}vE|82=L;N{ z)>pJ3lIrd~4j_|Km%{gwr7wW5)Vgd>&aUWjZ3D$0e=BDgjtpVM#C#7w!Fh*A88doCxK(|Q+RGXQO5Ie;5DHra;ft4z3vGlGL4 z_xmL!+U4+zVgQ`8>Q?p4clO;Gt@vDwc;jX>goYX*5&-7}LoVqm)VdKXW~Lzlx4qri zD6lpB@h=Kv=Dq7;1?+B!-cnbzMp@EAxxM4f&u=k-%`p1W%PB0IRwdluuL;kN3RcAf zd(b>UgmL*D6BfE|&MtrRY9U%ab$0ugd8~^1LKZXgH}c=*2R|8kDu~-zW((T6ft zX6C35vhHL|1XD6Rpg${PZxn$lO2fRN3uyenX1KjZ)98TBgW|#n-I&}K3EM}LYySPT z{cMBhHc1oz;K_RAi+jHAz+ zw&QEa|4dyYe6XC_E#^E|<0iX3cvuz5s&nlkX?HjDI}gGeQi^UD=E&Ffw4e))0cr{_ z2zb}JI+aI2ZMvYSS1~3)*O|3#WK7~q02=$856~I=-C2r>i76^70tXofhvR48F~YMd z_;6V5Hb!w`H*D1GZVyP10VQbFv&6CR7k7^UNf)5k#lQk28P97YBlk_PIQ!>m(B-9K z8g~xiv(e*&|6d3{N&qQPY{yFxeDB)AN~95i|*;V{bxj1})h0cj!*|&Q5@y znk}%+hS?+xJr|3~@B5k!RkSQ;D`C4x6t#LF@fjt&rHtgAI-E-(n~%DdOQQaH1;(83 z5XRrEtgHZfF91y<4xR-B1OO@&aaDm2ACNGil|@BGva+(ZFl#e2vwWqDaF<58yl1uk z;LXMMh&n?8s9b>V9k<;aAd}?;FjFO<{2}E7hCS zwfl)_=`6tbn>ZDrYW#tN28eC1R@$7MeW$nm^FzB4LFys4xddJKs3BS^rAEDZJ%;3*?yD#RmGF5;2SAQUhdY{^0NdIQiG0htD&1}rCwNxBl}7Z))UBAFQ(M<*w% zxiZ)}Dq%opXVP6jZ=9a}`}ZM$7;O*(M4bSk3l!9DIh6SI>sLUMCP3Ffu2%shP-IIL zLkwrSy2N)9ia#q8=%O?8c~1bR1$07|Z^6Mi^+Cm?ot;k~?RQ=IcaV0Mb8dpwgj1`l z8I?kToU9t?XZ4hoVm=@DlfmWHJ0wO6Bb=mC(oG~IJ&k0LkJDtso*>qgr4!_DEG|AP z#@XUDPX`<~r(iFcawXAhKixnA6fOrIvrGp%whR;sYB~r};r9Rex8g$ys2DhR0_{%R z@O^u&e$ffdz|vHMGA)40RaijhqCT_AX`@CodIknP0Ji~xYXLyZ0^q@!OdaH-1Bc}o zA<#m7ic0TAG(E}IdT{Ha()gLE2l06L^%nCJ9}R~fzYWl>ZODc3GXq!sih2!oDzx@~ z!>N4^kQ%IA+nty{5xTx(vYU!7H!`@Ki(iG|+-9Z^#RBzqLxz6AI6@fEru~xMzgQ#$-1ADz#h&kN8$B*OIlg@hRp?R`3Bav6d9yVF?yizkjGm<%4t-PXot(emhk3%w-GSWobvr5F}BHpEJ zIDNy`oo$|7H2OAUl|lfeBrypuvB505pFJ(%mz`@EtHJgFEc?<>FK6E4a&k7JKFSZW zWq#tAUU9qhtG0JYX(3iFuUD&9lz{RgI@U{Spf#7x)TV4=$1~^j!MAS~KIreaz9+My z3tOrYAt+x0KPogy`YRB;^{5Upj zUHiR0SLi=}^~&B4VKoD_bSvl_iyb7=CE{jDr#LMTW=LeuYciui|Er-sJ`k|eq} z5qT@1(TBSmEVKhs;!WHi$>u74ZH>D>V?5fRE7;ZE0izGrO zlXrDmLfLG?B@Q}DknKre_=0S%_|0Q>;UM}2VF8aG7O8S3{Hp1NdOZ`LilNxIC8Dd!$N(m?P@x zYxPg@3Z0skeTNaLDKq&vR_|PS-v+RA_CKvj&=G9cfi0_8@OCl0?SDA3MVJ8j!kRe> znXY`+3)%P75_Mm_R!zat>|HwV%zM z;y#k2L+*9MQn{bW2u2VHte{uto3x&@$-?5w3AOQjVEr{rQ=Mz$puk}FNChu}4)(N^VL2t^^Lta{ zJP~zNgRx-7#w>UP#F3ng@J*mgoq2}s$Q5$`#LA$}IQi&Es&n~?yz&a}T8WAD$MK@? zI{%NUw+za1{ocPp5JgZzItA&JZV-^}mb^FJ-K~T)NK1E@bc1wvcX#(q|4+X2%sjuD z{mSi(=yhG^xz@3cA>bt4RT2>=z+-8<@{5{y3o~7h|FNb?$N?CSa$)YH( zsafiGW$Hj4^z=w3M9qS%71U>;eb>{XutI#LC<2huS)SsW*YCfU`B%;|wV{!-r=hWM zST99?Ys1|V)US_}avDv?fWGgt!e=dwx@V|S&gpSywvHE`Rv zO^U5Zn=hvyb@NmSWY(Z*5{#y_6!7&hJb3!{K`SU;`F8qGLbE3^STwex!#Hs1h{hV*?Oek`4`LX#ZGb}7 zehxt15!-MC%?wE6pd%)8)pq@hhc^+y2pe}%=xE*9L_bjzmBqzir)InTJtR~k7@l_| zhKlF&D4ein{{Ah&y-{9p>Av9Lms6N`K{p2xG6&eF${PLDNjXC_(ZGBrqG(Y>^VQS$ zgFw1x!r=7nfRpS}va_`^Zvd^y00Q(NLk&9^PFYdwQjCvVzUW#d72`HNH=)tGydrteWJ9`F6(4kZta%ep)!p_B zREN$?y0Nmlq)@lA!B+I0^HU3hUQTXE`5DC*n32^AGdzM>+HH0;Z3(P$m2q`4W znDm<=e_pM=lGlSgj-1u1=6wha*DbHV)}2}HJ&P)BQ^?vT4>uVfDFgF*WBA7W~v)%W4)$)V7Ue)YsjQ%ysju|6M$G_@61X8ytp z=_>S^wg@3)f(erlbYFK{u48ZR^bec62~~ToDMYAfL))j?B`iPqdXRGX9)yga>o{o3 zLSOz&n3ubVa2V3M-(M9I494^Nr}aL0#2&W=%ScL!PDz(>(_lT{!1t_?i0cEo)upv z>HjbF>tW)O>zE=n20yjN*u%;-jx<&-AY6{IQFV62Bbp?>Q<6a%z>BXN zYirw+s9zloi7F1v5Y}g-QDM-`NQ2d}W26thI|LR+Ou;3-1{+%4+>GEQi~P@#ETQQ+c0KvvM8ISKEs=9T96MJpJ#<6K8BRWH<(P zRZpv);xIg&oT!c)tBuI@)6PinCnNX-XM56Qd)@` z1H5-}(74y0>p$^7*{qNK+*rn|A?4X9u8~|}c84XHF*VnNYqXo?I6Ak=8bYWD%`O4o z^MCowx1&FLR*0^htwalT^i5LANVSUSL*5?`BLBR00?1@Y{FRQKb*(9XXF1OEzt>3< z6?Rep_xZKw^^)n=A|Z(9HkP`t4YGIobH4L2L?K2cyI_({Kxhm@J4+@AWxvg6iM8`a zIpPnhC&@6l4`-Fyi2enM{3U8SD3`dQAMUHmEV9bzHz3RgzA>rx$E*b1(b6jJiSsaH zuu_Nao}pca{Z-<&-K|F4VOoj==sHT_8z!pgc4gr-(87VviPFC^Xk)+DamV*C97r5& z9}K#jpyl>sEqWy|Sy(rdf=WHYkdl~wMAnocq%4yJpHEeTUz4ptaM~!x+y>8 zkI^cTKvEk($T6prw-~+%BqAO)@}04E-+7Jd{4Y<~P4@Hsr(WOnM-}FZQZ1TH-uIDu zY}URzqYUPFia~cSctB-2Ecg$_rrF&M`SJ9OO~s#0#Zab9dfLBb;qtEVKS%h84&&gj z_taE{&<&f6*4o-6if|L+t7*?Z?&btsgnw*br91|F;y}^O5KqYWyOvvS;v5_9WWn@b zXt@CX!1t}LSebe1QMWL5-cLUNtg8`$-zypXfx;%_$=v&5GUWvoRj=)u9#7Gl=_iS0 zUG~vsQ$`s6+SO}ua8eIljgp3ltb3|=#|#R+e5Nq;XY@safPs=LQNmwQX(AoOAA)P8 zu%v%4jn6WpI7~xNXKCRl)QA?wK+jM|%Yyth`ck&e{_am#gQAqHt!t$nFWACOYqTW- z-}!|+U0YkS59N1%0_=@&Cxr7?VOPrsli|bw3p;DI}@HWPsJ@z>^jXmPJP+n3{j; zr>TywEHn^eVmbUZiZ>-f!e_%iV^H(LLr3*7adR*zPxLwyv*80LeRG$;V=9vszubvh zvRbIZ`~I`*e+J_L1NRbLiU8kjw2QaoKL63ao_l5ep&g;bXQqRxq?EUg zD2*{nI556JdzRJ>$8#_>a^ZhqorCai-xf&sySlo* ze)9&>;*pSmW>N#rSx#iwy1KtV^7o$aheLvgNBUYU!$Gvgx$%Xl0lts0jh{(^+4+{(X{zT1pohLCB7l!^#!^96&gRDV>ionc;_1a3* z)<9D^ucCIIEhEEGGS86VK2{OU_sZW^k0L&WULV`4jq1OEC2)RdzC#JU{qMmU_S?uq zaxuaam0x;+1$&a;ThW6rU_*CAST^%{VUzzW=d$K?c-?-V^cu!Cm{Y!I|{9LSo|>ftJW@*bn6O;E<3MJ~K%vDG$# zhIx;@j?ZtnNZEGCa~RwAYujZpp98_oUqa+vF2KAd<`%JvG6W1DH!>~$Dwh_OTk-rE zClr-EWF#dk`*cv&3KGh^W|R4ZJWlC$7(J9SiL7AW0RRS>i2QfEpJPrGxaMQzbK?h& zj9uNpd=U&x_5dIzJ1-B(UkD_}KD=r8Py_RF7Dp=X5|f3+om)*@HNC#2hRfqUGtGvU zCWnUN@3;_f>^d;*>v+7BGWBq--ouTfdlqL5|GNy*733dn`s?PH)=9V$sB51T0)Ogj zSc+K4tFb57w!NM$YcTu^5Iwb!Biqn!#3alQr0_#3+S;DGqZuz_I{>wDUbO8oFKe#x zymw+`EZa$Sw*p!$;7mMUFIdM)Zh=Z11Ux|!?{>eaY&Z?L0MHEYfWr8ql!EMGv&Zc@ z5Ldy7^$O(LUKk=kz?uz6~Y=3{Jd)x;p4&ffAKZ1TM>VY2$Gsz%qcmn*1#YRL&Hu z`-;+QRPFwJLlhVsEGZ}Fy%EMSnh%C|qTjxybD#PleoQ_(Gm$L+{s5loF>v<)DK6gt zpiEwdx>v9gk_$eyE`u)f-R4@rV`m#xm-lCnslnTMM;Vofckdd z=vX~qnNEOWi3%*0iJ}E}K<7{Myq*KB3HPggHp_Wd$Kd%vbkyfn~2N_|H-E>1Th3?{LBZ2A1)ghLZp3iSTK}&?vc9y&wr*c+r9#K9IYp$ur^AXp4^(mUgBix#ZI%2uWE&(3>+P=zz4EDoU#BA0FB2D2f*Ag+v@EtEj_a z*n2bwv2s4mj@)1G#KWiG8tA?5KZEn3$j%|C){qAiqTUIFX7*klX&Q%>kOya~U28IQ z8;SAl>##G{1_cXF57b3Pb@c=m|C^H)?-$ocU{(`DtRx8Zs>CdT4x8J0xoNE@R!J9NchNq8#wn!x7>%jb1aQVR|V%F4M=q5*>nz?<;^X@7h$ z5~;J@0I!1r2_BE#W-o~LdR`21fSm8d!PT|;4^1E>a07T%8vfaxuKM?$s;NP@FU1 z$RHyp9|Cz%_nTwzYM(UU5EY`Ns z?5`u7uZv!+64;D77R=iA+e%e1+6@k_N7Xa(o-nq<7cWkW^&m=ldOZ;NH6bY~DoXk2 z+i<#&x^8UW{cte_K}Lbb4xlUJTx>L*$OrjUZ5L*bYikLOc34v-t>&sxY#o%s2kK`v zHOx^R?5!VRjn9g(iT7UsawnvelWyzI;i4IP=tyrb&I>A(hvIz>CqU@XzFGVw_sm~C0b)1LwnkUYcBVhIF z6u+CK4p(L8yvigY3;d+umje=59aNTRw_M|K-!a4!N7=+K9Swv+=yp*E!P=fCGoB_Z@XI*sZ!w8*&2@=i)`(rTudV%8JFo22Vo~;ke(#tE zBWD&g;v#pUsHS>7f%*$yle3`o`R_w+>yLiNs`iQzANk^ z;g-6Zi_*R}ye90|J!38$-Z5po56R4Ic2CdCGGvduW8jA~^f0W?I90qAM4V|%I{8%P z65k&lgKyulwd9*UCYtmledcs2Iw}pdP|%T)6dh8J{Y18$M|^jXMm?8Pshl}7r@5RxlYaxEW?J*RC(^rgY z8_^K&L27CqlNn#escc*zK{Mz7lsouVY=h&DSHHf|x(DkelB|aR( z^Z`RLS{Z-Gw7_mBHI$Dw-&_+X4-@U_nua5KIkD(u7oAO><3i^Y%1N{z#a_GkbKN&( zs-0nriBWND9$DMG5qX~3mDP=jDROvH;8n_C*EgKkm(*zmc;PXMj+h3<|ErqvT;?%r z$`sW+Uf#^9uPH3KzWOkKC;x|Ie<;!8A{l!+zxgp?@2fkH6HWcfAe`^UM+H=);6)VK zsObtHse=|gTui4cn-Has<@>ka*A)h98I@k~!bn7c+8e;qUdRsM07;9F-=RBMtiO1f z2J3YOehJX5t%HmhRRS2y!GxTE$kk79#nWKa)(`qmnQ4Pd5STE6iAGX*Zop`F{o;05 z;VPZW*wB!W%Wf0Ej^=ADlPdpuKR-R+uRM=>IykT<>$j-FX|5l6&)aWl)?kE}uwrHO zxS0>Nie#b0dFf^y_R&d2U&^i3731AYh|AaUDxb6Ah2#qQ#^$cmi|CI$v^L-Wh{!Z~ zhT(5?Z_c?P8kd0CYrdhNA;q!?{O`sZ+cRe#DgC=3hKL9?>A3qzbM}o6L*G?P<8%F@ zPu0I1NYu{#`Q4o7b(DqO5r=&xOMkTsexq`=z0#Sl;V-Qw%Nks#Sm|XY?)PuLHECwc zbOekSqO4gG1j;EtH03~HtLxEE%;;1!Lx?$bS_lH%o03YogRO%;b1v76$9d*39#W-P zRLlp+#p0izW`w9*qd5Py?|%8g+jivwZL!CSN>0+*`}+3btv8 z^xB}S42m?B`>v^oZDHj0`|VA*)}IV+9sA)vm&Wa6NXJg-2HSGU7pzrbY?|6t%R{X# zQIBCseY9n#BjUV= zCgB&Qrw|`x&a2~3*~3rn2bPx=>;$Y**R&}eFY&lZT;c=m7F2n4t>;3Eo=?*n+T{bS z|Et1R2~HDM1+J!q8>LP@kV@83Iwvr?1it;5^pQgk16)y}fQg`p!M&xO#UQwYv;%1sQv%~jDUp{Hs+Eb*&! zg}1kvV%>vHtp|&tRUH(*{-2KQVemrdsZNOO3Va&TY+w}&I>ma&uhG~_YFJYLjScOS zlRS8?W=wfuVq+8Yy4V1I2&j>W0VoDk@!R630QN=kp$A;T8dWA^e|0;KIgPgCJ`i&I zedJws*~^JP{yYufvyk*}0XcKTp#CBonV3I_x_^%N0LI zf3sRQGW+054oz}EmnTKCjEq7gsTdZtyT?(&7e&?SY z{%;6{D;7i5ewb4u(Gs5D*h8E~e)c;)%5#tbUx(T7oZm>o3}ISOJQ->li_ATINYA=G zmZIK+4B0oK{9>u!i%+I8b7mJG*P{=MQQg*w)h?WgY?ioaPq_ltTPvq%9Fi;lFjkMT z?a&?Jtz!jb{hTv8L%ZzWIV3r+t|dL(@!)VCX7?Bl%BYZqtq^|*Nh+cG%8K*MS@7>Y zM;}z$As-{nUhMcJbVPlu4?m5i-P$yjBCOycw8%4iV~ zt3lPu`2SYfAc58_<=fa8>#cb)O|;6)+x;Z>5lFF={UXOtse+=FoZpx^q5atF!Xy>n ztR+xGaFT}T-#%YHdoB9?r>^*yxBININ?1ec%D=Fctm!uce^%M!XjXM5eZC2<9*x2% zt@tc+->*y;LLLV+7G3w@C$jkaxA0Bp=gG?%H&?r}@r|!-VChI1k|kB<$YgbRH+PMWi%E!n5MMnj<#i3IC7nRryvK-Yi+!}j{ZO|vlJ=4;;RpUKpeX>tWNUFT^300+ z31826A#fLkg@rlV+ZTyBIp-G^x`DP3SRQTNVe%~d>;3mnhq#@O4^K{j69#KB`1Io9 zV6tF>zAWAm^_@RJF##)l8kfBhsP8e}b%ACQ6v35dQ-#1M0}?K)!O|3sTi+j?+6&fL z3ldj6{5>1)WhG9n*wWh6O+&^^sEX+7V?2*bcT?Be2vkh1jwr71ij_^CfT7;Oj5_b-p@>D+>_mj>Rx;;XG_yFl9W8Zx@wluD~$%Njj_+wa8)D8lOxxFLpmIM}nC z7NN0*xKY{pao^h!St;xOR!fW_Hy`!>5FNGNM@MJzCpUA_&_Eph3$^w18d%ubfW6yzyQ4m?OHV>h zuBVm9N{WEbZBl`N-vbIPos%HY0a!!VHjhP0Bu`Cd$9}7hDGvy?RLs8pSjPV3HIH{pJh?AAxr*vdN9#Bjjff&$$6qWQXuW0ggMpS7$Zjvu z63~j3Qp}v5p1z0|#y3DuaC32CHFW{xF!5He$5RCUpMbQ*8GPky{}2J*4B#Q`;6ou;>BgEH`>%!y06Ef`fxU5c7l2U>p4YB?VwppZF-p z%LE+Ub#$3-8qo&)QZ1Hf*qJt{PI~qRseoovc1j2{gNNiBwo#7Qx0b?Mh2Y@rSqMWcV>o; zB09Fy34>YcT?IM0Qdl%S9rS_>YHz=JM3I_ri!?0NZ-9}s1f<>LdNR!`6;=zAb#^QG zGiG&MsvRi150VIMMdeH=bVB9uBB# zP457vK>_Gmv9Z-4tMYPva2rB!5wfVv0M~oT%muu|&(d^UTwFmxL7b#70?`Z9OU)A5 zsM7TXZ7J}~yIi^gg(j_0$b(Z4e9kr2E8%jp3Y|NENY)9gjrg_Ri%Uy{U8+NUYphat z_g=G~{|gPI+$xm$YA$bHwIABP8Ivr9OCDb_`KIJsLZjxJ1R(~DU>A>6PfHAshT-Pl z>oE`o9rO<3+q=*9B0lU;+;=cCFBZX_!l}ER?T3WIvr{Yjlr#9qsQ9b(FJ)@Rp&9u} zxiA5!C+!yO_#eO?6D>}FQwG|J{a%!3f=Dr-CUQDopR5RwkhD!qROT2dbmr9p^=>g3 z2ef4XeztvdG(>1Zh81E6u*%`KY*KM_ZCowO8)jC(Jb!V1?sU9Z57=oSO~DxqSkm3C zy@6<-0R~g^iuFPkGK*KyLOq?vp(%~m%uRoyC#yT$<)cr%*&%4)t5EyO={r$kss4>l+f@J4dGu9f-)hV!P5 zcJXW=`mpX6N(e9aqfm&!Lj>@aVTGCw+SXJGlv6=45`-ngb0i3XKb#eOOaMiG zRSzaUpd#P+1;0ECQg=YhCG6^+3IyLGVr`_NqM{9)DR3;SVS9LjdIJH4Xc2f_7QYtW zjH}W27NDjT>!20=nI3ZX_?wCL>njsomU1jsET$2*Ve&BdZxyFDaSR3Yq()L@ni9n0 zYk>_y(Sb#@fxqge@etf*5AoVDEkxE|^m8j(%ANu=CJNZC6s~x1GhlN=hn`&eQpMO{ z&x(akLn}8mZ=v9cgiQEX$OioUM`kzj-=5YUT z8>HGMa%B!?${<2QJ_pi8nW{x;z}x}kzZQ^b0Q^G12}kV>iXlKp$G7!(7&@)wq@_Io z;ycKw9e}J9L05J**+ZobEjd4bi=*Qp4;RIg^~dPtxs4;+$ZO1+eX#>Up{brjqjSZ>VgU zf@u+>QQGjJlpHNjO355GYX8#GBN;Q7)xjw=Jiv6Ll?EMtybw92f1@&?$N-&7?RA)- zWogwY1vP|Zt%JBG%s15qh5#59n~mGTJ5MK`^SE8r!bI)HR^bevgpgOKO$uE}Dq9fo#vwEjAFDAH3$ z!rDj9I?dv3;9@&$4=rxLtaQD%D~vericw~WA7~QMLyS22V^h1G$F3fMAc#)>+TYV7 zkKcafz(eN%g&3v^emCgNYT?Qg|3hzuy>RA77mp07tDcYWy38gl@5Y}}IdYw67CyN8 zqpma+tD1v!#qcquY%&zgo9fMmTy8+k_a2`eIY0y(65MN4N&o%tf1oFRua#C^T1rU# z3CfF8VO*W}x0&_6i12<8rnY~}#pyGs=g-f%E5sC)kr3u`;^pp_eQoiU#uhwxHL z?RiaWkAhX7wv>vhfJ$PB$D93LJd%4!d@DG|j>GlGcbZ(6Z2dp(Sz&RuLZWwTJ8vG? z8&T;i4;OM+W_z5k0NQT1?Seor`Gpe9hn;v!6&3#sh`9vC7dZJ zukv=>Ryb;U2ulY*RYZ5H|Cy1I5xkWL(;lK90SFV%Iv%-~W_Zn?E~yIXb-?DDc$m;c^sTl1U(70-&O5;lau@DNOp^*%HxU zqX91;AE5~LnsT7r!~^w~zw=r0IFHFIwXP$wdBq$>)xww^=7_vaoqjcKnp&brNuQ-9 zk1l2=k(ZA!srnx+;ZF#%ik z!~f|#Y=l2sGUDQ4N}Xx&_bhyDQuqGE;<%;c%*R@QQnt-^tXS} zy!v67iZ_?x<5=JDamkai$90Ms_LT`OKrz|CRZl~=vSmQsz6P!O>SfP)wqWJipY7(F<+ zh99V(b%UfBNa7K685vb;fVzgse44Il3FAU`IZ`r5QbJ-59IjxJkeZONN3}lj&ynA^58-NYwj7jIK|EHzNT-aB&HCB%nTMGc2b{Dg)}t2 zvqTqZ8bScD6T|^i@)kg8ta3RtR7<%|7m2CiNHayyMWmW8=2mQ`_JiLUKE;oq;P6z1 zcsg+ygQ|^}Qws|d6BN@{U<(I$1J?@Qg4w+b$lh?afcvm&{P6g9bTkVn%?UJpXTIN!X?RX}E$n+83|0Gz4dfO>i0%JY8a8Oy(vNcfdnTbRY{*5zBo@`pN> z&+`-L(dhA|l-5K53n4Si@+%2KV=3)Rbu1ikn@xa}Q~guV*vd zX_9PZGxjvOrVF&&Cc#r=)rXc*=3Mb4gzuq zxDt=%9t(?RDqkMhrg;4KkK6OEl(yk%O!}fBmpweIl#M`h^v(g{ad7d?DqLP%xPzP& z*sint=gXvmh1syb%Mdq_99?-g1+g;rsF-hiyEyvuhsF0=@4OHaWTwXtE98hF#JCt8 zDuNrfTC;B6yKzJ|x1;X1xT zi&k1zpPSF6YqnXR1Q%OA;>4-G^(bxDSQ0c=AY{WUiMaxCa>0ErZr-M!jv)tojK^AJD~9C#3MAdMv9Y z5SCiYT>_!jQk`dRQ1*d)7O1Nj0=$WL$SLp6OcSdyNRE$L_?4U|KY7dXT9`TrS@y5Yy80@i0$A1hOVQi^$I^&Q?iTV(zvb#g+uJ_ z)mH7F&K1-D>{F4!i3^eK_ycorIy=2ed ziJ_`1MoqzJoECz&u=ewe?_d$u8`IBaXZ7!&=xBS;dVs0?Be-t0swt9}z!(D*1mQtJ zXQ!vDg_a+}PdI(o!!|cJp->mGF_F4n(4Z737Xm5>xUn4f#&a}ErWG8drRS>6)x5pE z#d5~D{&&j~5}jH`{9uB<&dKLJ32EIn!oLQx*6)~sEf=4!1iFmE}7TE%mVWvHFPC&H0mUo19)!4S-Ep=kP0n=>9bujy)>*Vz{;Igc?Y> z6Cf1$X7xIh;-H)b7vC9jckNQ2Hb~psAxsmA25;(pu6^5DSsL2+pGlHN*-%*_#zbOX zfPVyGR15~T(2^}t-@*@6Z%xim+uPggn+>3A0qArHAqxvgoB@h^9k{GPYlj}x$u^^6 zNe8lgAQCSwC3O$>HV~-KP1Oy9f4VW&l=ygOaC!pFcA0u*x5D9a^F5e4|3V=SU+@a^ z`u_x&W$0>k$|C|7O-`%`r0jlIXleybfl%U|_ZzV}6$4{3f#rh^cLagKBVW-RcIu%j z2^#su(K4`9paym1?_gO{E5!NsbHTq6-{aHC+2@1ARgD>(Y?agp$`f^xy%_{yk1}*J z%R(=-(Nx?LD)KCsnaIuNoq-xm_1}{nVszN@9{h4P8fQqM-@eV>jckW)K|R<>hA1`Jz$9ht{gzdZt%FR$by2x zI~WC-97c{wec`O`Q3G;Uws6xtXBroyy!e8*y^M_W*m|h!Y{MRKwZL zkT-F>PNe+IL9{uhm_Xg`nnm?nNiW+Kr5EcsKwd))1pFo%$+HOvyj(nZ_>+ulVwsFWQ8CxvtgPB z=Ae;$66f$y{N&@jbL{e}ycly#g)XZt0&F93e?#te`(}aX92bw{u|8=^bBo4cK7v~) zuom1YU0q#0puhvw&r~&6x%+Xw@o2grXBWboH;wb>$ujS24PN#z0SePR8vyW&jj_#H z)Bsx?ykz&BW`S!)G8U@gQ909CgJt}oz0u(h|}ki6Og1>nqs zrJf>pV=GIk@8bnhfTT><&4GY`VihwpcTf+dHVG)gnfK{cZs*I49uE!>`7CAhq^@h@ zl?B3)?=H7>aaN70{au zPiNunSpeqsClUMCFp2L_-ndy+$_kqvmLA#mL`;c#)U7MEuh8$IzB6f6s$Dyc2ip%4 z6YpNCIB@!ERU3KTfTS<#P9ZpC>K|HX_OFGDriTA8ZM>Y;w_EXm;`2E}zL(HOWhOiV z0tR2;>eDjT8hU@4#XwH}x;D}#21q1+S%C{u(BwwP11x-e&699fWA}Am^3RQT(xJ^> zk+QL+?{=V`t`M=F%T2_59zQMAucoU|ax$->WEKcU^SxhKBhah-OXqUc zGy`XLr^>u;KJAsn({w^zg;D*6B(b-4?$SFGJ}x)W#@+W8y{>LcbxA@rEE6mqT3&pU z?wL76^&h9*$-ko-O8?g)JrRT}b-ELbtAz3cZ*#%kMp}QVxJAQufL6ew`c1c5t zQ>`+tr)W))i5}GKEm>K+>vXDaT_dzNk_P6${t@&FxC(;Qz33Bv8iKG)#v`AEgF z)4nkHXuCNBRY+PBO+QrYiiU-o^*&FUl?scskr=}XKJcx65_NT=e2}*yf zH;Dl3VCnSZnv;#^YQH-zRa<)$0wfr@b0<@=&zqeyPcewR~tXnGwl&fTtBO0+_&^OJVo@KX)*$5 z%V|oVH?kxiGF|z4MiZNot59`Mj44~9QRjkqMw?!AnNgbjp;&<&-cTh;xf5Ky)J1+z zE5bACZ*+E0_r`}lYZF-6!{Z%0P^l73c@M!tjUyL)@^p)-qQK)gkY zR5N6fM(b>~%3t~l?aJkT8XotiFWhuo7#vZvqcxYR=xLN}+-h?(&16(+IEA%-le*ks(lglm$Aw_7ew@uFO!s$1 zReO4N8WETCx3V*gInD_ME)>uW!<6`Z?)(^-IA(iQ%~BLv1ecLzT4pE^&TINO&+rNS7XC{r*b${yM{L zgzk1Nz~uTcNw`VrJsy(M;()u;b2|0nQ_0)cnWh^?{PXvXlaqX+DYtQB*`{cII(b1z z4Iv|~70d5UUTHu43J>?dL-znuBwm!B`R(k?vV4!+r@$pNn(PulcS6t`~Q@6wrpL}iz`~08N z`-ZM|%NaQP1E&xrl2lR5)Xc95s~;}Z h-v+_m=E|r|RaeZs*?Rf|O(r6nAiF3e? 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 X_nqAq_YIJFf`XD1l@oyo{qX;PjVpQ$ literal 0 HcmV?d00001 diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Images/ShapeCenterOfMass.jpg b/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Images/ShapeCenterOfMass.jpg new file mode 100644 index 0000000000000000000000000000000000000000..7126964e9892e190c04a170d7028936cbd07faf6 GIT binary patch literal 30218 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 literal 0 HcmV?d00001 diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Logo.png b/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Logo.png new file mode 100644 index 0000000000000000000000000000000000000000..e1b80cc97d99062c9d8f1557a8594a1083f17fd5 GIT binary patch literal 1937976 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

    !*e_H=^oJliEo9#t8n|C_8(^qwcpt${{)WPD~@ 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�_*xfDmS~IKP%v#g-7m)fR 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
    g{ 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

    -b>p9TC`sIFHCkJ76-GU++4qqNU3G|Rgr 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

    yFrEqhx+Z7hbpL1^#Y8m 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�dL7k8gzhOj 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

    >R^t2MoO^voNVX=9t3KM$mY(Fmk$}k$Z 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

  1. oZjgZXUa;4}_|BA-%D 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

    #;dGQM+VpN1b8Q!keMrg-$$~-f(=IjLF?Y}*! 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 r8Y^tK9IQoh{`AyedC6!EZtL#i zP!u`)AX;7s^Qf6*5aHX+qMKUB>%-ryTEf}5kRgejWaNR_EY%TkTp$NXc#KeYQsoDC zXt|M0)C~+5PJ{Fz(5>S&zIDm@Ce!87j^3lrn&e*guUFCO!;wJNA|GjY7cJK(z=90^ zj>n4N`e^%uiD*?KB#%($l*Ye$U43aQ-Ze8pUU%N;Po6uPI$4#w8ZNm0)A65o{PPD# z?s{ADZIs}^(WytDJn+Du{j*&=+h6uNJ8;3ZuU>!CPfxd8Rg-)0fF5$@&#UV+U6gyv z{uULanZxD>v5-_44R&oLZO(yw8Dv5WA~ush#QcSeSDd@)l9#;Y%2)m5#w%~0o}Rb( z_&%6>A!`nEfD2#!^Vi;Zi-E{>WPEu){M)Z${Fq^yr@u{s-d)uG7W5HI_Yo0K* z&o2tB`l@l%%7`@QOU*JYSx~cB1jLY$)`0ou%cuU{N7{VX@`vccqWRSvsyKu%I;TD~ z_Q>9it~FuDu1NR_5TPTcvKP=zCdO;5Scj25Kx|r$fIRr42e^$D) z3ikjIeE@~i1ZE&3+2s-8zynX!CnTfT)y+mjbUv-F7z{n4do8*f8OlHpgRrVhGnght zVIk*V)21k;}Jw5&-C)qp%E>-HyMWpd?`GUEe-|53y4=#|trbZQJ(N zbIup7A34n;0z0bohCJ-m7Z=T@9wR1EW*kP=+E$UYjGWV+uUxfm>TBDJkW+s6n-w`a zl1fQxQgcn~lH< zt%|2Tw5O|GWC%-h85d`62I#t+Z+oQfPsnIme${A8>mluHSsW?rha{~naP_*n z0ZU^OPpT53F#MNwB~WfyQGD{s{2GEvu8TDJ?k6focgP5T?eP-p@x{Wu3<31ttU;h^dYUXE`-8sg3deUE85bj1TD{r=v6H&7Z$``Ra9-UVp_a-g(K( zf9jT-Ui{?V!~36l?8LG5skS-H;fE5NHf_4;XaAeSi#O!LbhYN9bK&#>(D1cnLH!IZ zFXtKn4bfoWGB_LSh-E0FTg*0Q8U*OR`??w%{WIX>+e{@vhE`%9y}w*QE_{9&$z`h~ zZ)}Xy1ep=oW_V}NAZ%bK7QfV&FDfRR>=?;|NCJ;$c_9g244Kn8;w7_r;!GPqc<0n* zZQQ2vJ71sr$!l7W<9URAc6fYSmU3Dap+vJ|K017?ezLcCei`J8uB=0D&?hOJJ02@7 zL_jhwFu{a~Hp%Ag=676O`N>#Z<=S>21bplLb!3@Zy<~K5P_Kz^S+(MuJIfvQ^wo;?J3cZ_iLX3X ztAWDE5lmYgIB7{vj5ddqd|4YG7rNwGE`#kaJle&KBx^A!5-sKBRTtG{bl3Ju>ex{g z~t3xV#rAVE(FJ}gVO{vE5tK2 z7;6(QI>J9y?F$QDhE1E<@aKTeUMp{|^b~KH~l?@T|_NoQ2Q!Ts*yS2A$L6 zR33-?e5vs{plfv^W^D-bVu)S~p?l=YF0*Onr0{7#Y}ovA_wBub?&GfprglPXNOac` zut_7rU%RYkS%gU|Rqwy;=~=mZi3PKlT}D!ClR6A1o-iY94lu`z=owAOZch}#iRH=w znit7RpD%;5)ABi6jAyzeykNwy zzcM*C>*V?0KVLH9l%1sEJ$So`KTWv%iK(mB);mF8+E#fj0f(3c_T^^+!1>VKMStPp z`snyq+e`lImQQV3G3p}LO04y>1d&bo=wthe36?adI7h$y!ukqE`hlybV6RGb&`EGiT8yRWUE3KG+St6O99 zJC2H{Jx@+=B+*9*PkHH@3gA~hIrX*2>W<=l?XOdT6WdM8#j7g`W~%zIoqJFgo}4;z zq8_ncva0>P_VMYF#FW#U$IDVqwlp&v-3X9R07`vw=M&{i|8P`of27iDwIneKW2TF8 z^mB;uLHva50x=-)@(U^-wX6AzXiojH&gL;@BgTTZh$^jSoa=-a<17dO@S}*2sFQ8H z?BaC#{cjY9sY_NIy5mp&<&#gGMGIILUGuV4*Zugp%cc`~kk$z}$ZoH#+o$&X$|2He z(6!QhP*TYu=te>q1k&w840l5-yS0vqT>`t0)K3c+UwG3sZ+P#OuX)#+O;;S-aqphp zyG9aon8ObfHof>q{=#4So4b!J%=Me$E8~2|$v%AbpO)j}C?P{}#?lbM=O3E7VPi23 zo-STCcOR}d)6u{1($R#HkydH=HPll_8_7*u840;~eBacS>&DaN!O`#c6Bvj&MgUP$ z`QV#Y4003!il&1r8%J%8V{OeVGbw9*4yBJz_u)NLFIra*Q5dWVqWuAo-@B^>@<5{a zn`=3#z7E-5GQww2v5-0by)h zQAVn}+BB`thXi4MS?0?j!~ElWD|^{{qztD2l`<9<2xrSd`RqgW4)`zKQdz5Aoo|bG zuE_u+;oG>tH@`p7QdufoFIh14rpwEP;GGPyoUNx%#cZv6^1$e6C`DB~@1N6bPLI-7(YS5W^~F zwPJ{UM=HsOp~utU48?+H6i#i#&fM%-h2F;R)A+w9_#s-rdR7IAUfR z7>0+=bQ{U=l|z9}L#Gj{5_WVuzavv}s(N4%h}m8X6YGO9uGVdTJ_e6YY0~Pi?`pE? zWYv<&t`Z3?AXd9ijXw0idk-~JGTOuwNsysRM@C-SF99!GJ@vWqM+XuZB6O_Tn3_Lb zk1k`GZf1H&B>3y{$?f)K?xX^N&dKq5lBu>{##f&j=|Tn5%o2-byA+o#stA*SN@$d$ zjHi8`L&=fs0iBf{i2zJNv%V=N$tMnsUgx>tyhD$F z_3K|8*^w4ke&1HIv{W!cr7p81&r6!7E$Bz~mY6MFnqNur;^KO6PYJMd@6K{Th6TYh z!WzrFnj#Ke=SG+**?;f#BVq9aU4C)y+gW*=Xb`AEvUafLffPO~BE&r@>1%)qNmLSc4q>_zD=0eau~l=Pz5m;rwe~b@^-FyXoqe zpE!Q((4L*gj~$u&tA%s;A;F?Wi~j2W>Ho23<+W(TsVmgbw`y@sjxce7XSlP0s=6@8 z&*U;UWwgxr>5|NKM@wnTc)N6A)w)hIK8L^KK#iGYt<5RaX1L*#Cxgs;E<}KN8Xu(G}bH`6z>#LF`70j9KhO>na#wymkCiwwXw+ z#+r^HPZq0-+N;D$*6gO!G?2O};Gy<-1_wXtzt;Y!%fVyyqEq_(qE%JPY1&0MsA0DK zs*Z)%UNZW282LPoGNCZ*Kezhlx94YSGdJC{qaJ0UGk>XKA$@L_)9SjGS)G4Qy{i1x z$7^k6*~0pU049U^Bf@A9-oklB+gQ%u4YVEV4xM(-%u9eG(ZdhMONj)Z~P z)p4YDFKORCgR}x7pf(0m(#?&)P#v?wZVpG<5+FK#DohB(zaQf2he~1V14;`$Q)u;e*9+j3QRU# zEcR_Zwjr~l(;+4Tvzz%~$dAGIk3T83e9_Y%O*B(>-ON4F8paS)nLcF(J!U19nlHye zhHrMn*|@U4c~6VXHS3BWo8%eU-1x~SVjL=fwt5whkSZ#~`a>ijDOCajHo41BUQ>hy za$)vORD_j@3PJ1gHB;AaXpY*lh$?)Wbc&9|0w-}2u(1;(KwjnwWxQq5ix;%NhuxAn zy5<8Z4&f*Bc6!TWfVBnfGu||d+1{m>8OL%Qslu-ubew1klCZ=P9J?Dg79T2!AHT$| zy|%yik(yS2=DKP{@?b_loK(KCqtxGbOZ$t5iz;%C?55U6D<{SW?kq)wOW^IBs})(X zqQWNoTiUb4$VmEW3AkF(RzqsH(K6s%L}`~$xnL?D3D8moGAFx9H(z=2)IYzyjB|>k zg!mG$4nZ*DHlrMk&${xSbE&-o#>IPXs3zZXVN-duQpwy5(EX)bs)|fu@TqS$F|~yq z%-jfxL4Huob{rYOp~XuP4e1Br;oqK5iH(05q2a`+cY|nP%{6lUc_qoau=P(7Ew@&S zj=CzGySCSTMeZ)5B1i&>8E|ar8JsfJme10Dd&?E|+2Dk&baQoYd&b@+m0Wjg zN=#U_=-4NI@7E9Rexi2H4xE4GjjON!@p<#>Bkqu9LT^Iihnz6kIvsQZLU)-Cbl=r) zysTL=I?xl60cO5!rz+(T0_z5cfkDb((bARcF1hZi*Sx#^)xxV!o;-PE|L$W)56^uh zYYsoyIREcNZ;LR@X(FuwdtbN?i=XhIAvLu|1`W(FL{RK+TNg(Xu$xN{xPv zgXJo78D7->ek%b+R1wJ4mr+8wVR^lqi~#&@P{u+<+D65|^3m~3l&JyJE`upW%h8_k z->oAc5sE?bVVd%GA08RZYg38Ge34lUEsRVbglo)c6LF1iXifMu;Nh38&&`Qp7!jsEvr=rwkL&lZCXU##&w+} zZ87T=7gm>o-3Y5PX=XXqE<5;-?45d3`=ix$jHc>2E`I*RC6bE1bu~H*ULB(Cvd9B>>qz!Ua2j@K=j&9 zIK(0P?{*2lWYg0!-8Ftp#m-PS%+Q!!7ovjMIcJUuJHg>&qu*6@lHh}?DUxk zpB`O!R;@l74$BVl&s2fVL}Ux?k{){LG%wKkpsN4Tx}$B5ITH*mKz5nRsGT_mv-|A= z6GVsIA!qfH`fx*xbbg4MHJcZq#*T70e`G#Oe})5*vAb<={Ss;zo^m+Pb%bLA?lN~j zQEyY{p1#M20f$@Kr(N2;V}5(^PaAaM|2cZMjhY0YZnoI z1mLY%d})$Ov%6ggt?PJk%c>USscIQ9>&H}b0w<3&PE|JPh*FCj)=Zi?1j^gDjTDCo zjmXJ!q>9)TYwJCw1n`#2YZ}gNYdR29n6nZnViJjg|e0j}FnO{DkQ7lS|l!QoNTN`Ad+gCx@YIqm&b1ix<>A4Z^@y#umO6> zf?Da;5SBGxKR6F<`wmw#g{SK@Fd&bPBNmpnKZuE=33`w5pxV;nL{v*#6LRKo#08bD z?OjM5n?8(KUaZqU)lpB5vd^?vsR~i^6N*D zsG^dossd;WRh)8RyaIDErrJz5;nXbCes*WV1&XG&Rf|i}TvOIsmc>X+g&>bm6_pH$ zY?_bM^2@9yE6HfZ&EprM(Xmh;Qy-6k?1vvC)J;`{^)?_jt*kdvFKFM3v`gNW14kwC zsH&;3y}q4pk86?yw0go%Rki1EeRcqWFZm&s#4L_TN>I@e00PoKWdgI2kq+e>WW?-pq~r^55?Gb( zD5zq}N&eY~ir#f-wC>Q;^~g{ZNsJ`^IE9h5R_3Zfgr;ay#f8-Fn)S6CE$Cut3%YyE zF5rki!1>(6Wg&FRp^A_qjAM%0GQQCWe<+6TyTQb65Zzui^wlw|;&An9xRf)~0#0vW zuRXoT;8gqx3@x0Y%5%feIHwo3Z>pRj`rp}~!&!y1bOGx$SH<@P6Yt!hv^C3;qGvbfeA-;V>A4jVW`c} znK{NT3}re!p%_s4%44H13Y%*aFif~pYEC9&lS-yH=&>aRyONL0WY8@PPX;o!KHSd) z6*&j3xY*mCM&_I4Hi*;9+xu1>F^m7eI6sSPTN!M5;U~9_rrw3~M>B5ba-66qs^k^{ zPV7X!I$^wWWH*Ytn%`8(52RC=F%mn6k3ap_B>Ssd5HV?Agwy%yK)Z3xQ8rqVmBhSy zNi7Ls@>OnQjOi+4-M*tB&$Ju5OD&)r%V$yQ4uNe1~Lp`(B9 zlFDERkcT9)0*VC@+Rj^E@B1*NmZbJphqPK^IQ$~ACi3SGPW_WFRW0VQy)lkRHXQrp z9k?$%GHP9QNL$tcGYr_Y<0;m%AV@~Lh_OIL6%M=Ad#tx}M%w!KK2mLG zDjg04B21lqWzGk&4dE67lVt>ZBtP?DoxlmTb|0$UZ#`KLhky6Z`kBD{+7Bnmzv=RX z@NGuKe95XRb7yk?Id%2zJWvn4om3Rk5AKW95>w)&Czah=dQSjdg|I4O+Upnz30?i% zP8&2Y<+nwiz{*a$OuoEr^rc~R9n*FQ5bn)5>h>w3Fh z2?Ii4H(L;aA?(KZ5JL_DH}iXsFMi3D7aY6rbyvLNCoX#N88l) zn-wLFSg>1Rw9gLcX9Omw#iuI(G(}~SRWdr9O&ldJ+|@y8(;|U{#EzJ4l~ALezX{lPXGOQH%ocjW{!DcKgg{oWFw<{CU=BH5t6NY; zac0aDUQUHGkvYAo)GE(eGFq>nz)bZsaXQ4@>ebnavvdLL*`8kCX`DWuS+B0=)`Ywm z#h_86TbR}UBQt)cJF8ens%8~9jn)tur1l=Ei#g-|nJ5m&wN6CWmfG-@6E=r5$@EEN zl+is+82J6~VLi0F60%4}i?wdbxX&8&NkxehW^u-U!WnIzh?p%PpJ0^3;XWYWyDKu; zOs<)6`$qw~`j(cZbw}Z6ev%|@+gmdDZnm%8P_r9?Meud}3-BT}ZzxhfBV%jQG zX^TWQ9&O@Be`(u@nIJ0-ZH|v?%Jt^pAv6Y|?I=@Y5S=yO;#}*KVs`w)>UGD&^H)n^q*}u6}OoafHcm z#2rGpqjd`z0+m=#Reqc-)-DUoCKBgB3ABzt&?<7erI^D7vX)+R!6qbBk_i2YnY)Pp z;|ui!SPc4~fU5kSA~!06VS)ELzU?%|q|5Juc_p0tA2L_1E6eCx$BEQ~yQWqxtq*pb zXy1%TTM&%Aw|&{2yTivzjMnEKs>3%uU4KuVop_Jrq6PJ4oEV(&2ak?!(v~i$^Qzz2 z==Pi#bh+Rt{YKasf@Tm9eA(=hM}Ss9^-o*ECa+f2P&Yjjt(a29zTHEI58wA^zqxJO zS+sz)Xz7aA{={E=)fLNyr%|xyaJ}!QMIC7-=wxk#)MhumkwXA?Zm-?&BaB$TeAG)b zp%!{qQ;ArvsaX*fl2p3iFd?o%85vt>rEPlNyoF0vtloI}#V>y26|eY-^RIo)lI5!o zAJ}vB(7ux=jyD@~n8Wuarl+Uh__KfeZ~oo?_Q>>7*O4JHbEV6~#f2~c=CK5tfJ@uu zgU)z`lPjfPEUIh>Vx|B>#P+r?SR3$}k}PG8z~|0DXP2;AP7Q{t(3vI(AQ(rRJ3i1p z{B*~*x*I2!L6|#=!p>kz%=q8hR)q;m75f>lOk^M5Qsr;mQ5LwX8M(biT`vfaKgzCT zCpKfB&04!@j~4Lbo%*@U>fNh6V6%aH-F0o+ihWyhfcR2Uwt(Hf^Gybhr6NEV` z-RFQ=O${SYIqT{bynvCs;Mjer600tXEyNSvSHTo1RIZROmRduA!FBKUorYa$Y5T4p(x)eqJfv?vqLL}1(5E-pw zmKTana`OrS96CBm?R#!0;c9H#XgSXjs&W*RI8tX85cpz4laz)onqTQ=p{+yhJ5oK# zel@doa+z%hI_j^vxV{s?TKHrfDZL7t*H&_wn-d_PbD+00uZc-&9f*qazEOnwGZAVU z^}G&q?8MY<57)x9bTFBdTK!3}T(@LYx_ry{oh-ZQr7hz>sacZk{DJDm7O-tqJ3#mC zEMHf(MMLor5g-&2ZZB$6&qix{klocsJ5wvwB}9KL_BVDZv7zxVIF<9+||_=5UEXNKhrK4#n8vl$y*vqrX>``VR4 z0P&Y@ochEAH8Ya3YjfeaUJQL1-_Wl2TyCqE6yH#pt10q0WVY-Y(KR?XdApoOCUb;I z!GLUNCC~WAm7~E^`Kxa)8T=&+r~bperJms$ZfbUd8L)w-qr=;3$`ZqthTJHIXPktv(N>MK{x2zgqV zBf75_(er9KMY?8t3TY?V_F%DD$9JxW#fFs98{T6=Im5C?kY7}EImb)a~&_iT>O4Xk6 zpDZMU~>DB^ZN-8%{ulu|+#D$qm}T4gqDJom-c59Ws2hA>#{Yq zswF40$x9@TWU8+9K-7t8QRGwFP46k0at@%m*4sN%7j{nx4TJkm^eDp+pSCy%XZQNL9Wzst5#EDw@ zVjMHygR}kdy;DDRO{M$Nrkq!-arBkP>Vx`xkr(Zjtg^M#O{3~F9|!9NF>Rq6?`rj1 zwWx{ZasV`@3{@Aw+GQ1(1_D!AiMW4PeP4_a3 ztaTvntCDL??zKRdV!9XM{-G}jaz~gJK3Z2GaL^nv#U@iWoXDptkvzHo*cbo(`wyH= ze}!<_s`V>h{vW;dMJq&j@6O_+AqhLXfHUORs&RU(G2eTAU8fO?K;*kX=xcHfOh#^!FLu;4jbNc4XYuM3PlB}s+ws7hd7uB#9NhNEUAX0(}S7(~z zsOd?eGA?XT-tl-z;;VgoTN#8M2a03nRaKJhi|>^PwUMgO-jC*PYkQTL%-v747+Sib zjmEC?Η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 Gy3H!PW1)`<mhaj#v5F7RB!F3|(q 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 EWrMYtPr-)n)^3yTyU>)ynCL zVe5i!&m&DhxNSE=Eh{d9T*8bY^&*WkUyMN!^z9rx0%YTR#Wu63kiQ@XE7uc zKi^&iQ_8MR^6vGmf40WWj+$xp#ekA#uvu92}_xJ-xW>#K(+Xt>(Hh=g#M|<~8O;to-Q=P=Kryt!~ zjRIHe{H`3luH!XIJhH2LxS;8KTHhUd?9?Ee(<}UfgA&c~eB>M80fH}LX=_j}wQozM zlel6rX?lxRShK9Yh_rgrPnb@-gYF@7X(LFkc!H=APqfo36rMwj_7RuL0-QZKcuuw3 z98qd608B5OSEB(WTf6Z^NsUzP@BG4|nsnhzSBxi8Y-%JR5OwF9(xfP?2d5Aa!4n^p zMo9vKl7TDd5%C=U-`rG>x2#_=P@dagFS`-RF^!?4(#a`L?s4F5?2~NhS0QUgRr0&an)E*;Z5s$I@-#l24o_)NXJ>@<0w3o}+2><-V z?(_Sr;#8%w>LR4+MJT+j`?n2!;YT!skW$1q6p&~e_E#*f6QbxrS+~44)j7UIshN(3 zHj@9rll5p7$N^cZ$%-PO1F}Qfj>ax;kC?@xJ_!LEFR!XG_xn$lehB6i;UW9XE5oCR z;sa3J0;2TG@2V^j+7t;0DGnQ@uUb~`ZqtLgirQmSoGj%{!w>e>9}*y#xvB;80An~Q zQFQoZwIKZoyE`b_=;^%<1+G$h*F!JYBP|JyMsXoAym)!4d=Y5K> z#cn!$aH$JKZ79hC6hJ4U(Pr=DCmJG!9)^g7Em^&h+kl}R2L~dCC->HbMMIhZF=-$ zdZSzj;#K$ULQc-6=K;n`@V65#9bldRGzZLmL(Yba;WtEH$lO~9bB|CL@4(dQ)f99S zQ>RyRTb~==gkVH6_ki;rBTi9X*Gd>vB)CQ1Sfp3b#MWQX3#4dIdkB_GnU>BAQ&CE` z*8+@}0-bp*0+LOs6m*N7*hR*&`G|ffc)ApdM$d{TcDWRKPZ8r-aI)P>M|wr1uAE*e zrJ5`X98Z*6S7oghwTiOR0^NsrXhAG%`X3t=C~Wy#v?5@!6{BGQm?L6gw0cSLN2e5p z6iT_#{wADk*H4=(t5qIavfS7nklAo~y%AQAjLmEN+jiQL6C#@P*>zb!aMAoh51lUi z6GaeKEVDk*p1QYmlUg}Rt@En1eZgws{Hi1B-@mQ8A|*_?Ac)v3&}}UF#*l2U6eVhe zqVyMTDYf=yc zsN8tSDjR+xuGLH1&w93KeC@gVW;Qr}x;{|F3ZOr> zFsJ|LOe#gxa*_$iH?|M49ro(B-0jghco?MF=%RB*)o7+f)&)_74K8)j0|zYX?e@JY zb>QRzySC>a_%EkV|Aar~K8ADW&h7f^2Ul&pVcwND9Xz=}^TqS@y+l&&Ko5fS;E4fG z3ntVFmk8Gx;>yy~8mI1{u{b(h+AqLRzTy{1ht!(umKU>|G~(F0Y&-#oY3O682BH*% z=jf?ALj++ap4M?`M|g;@94(`|GzzKjzBK4bTZi8j>p&W3T=oh1z*b5>-A-M5DGl(b zqWp9BS7#8hZEY~8938~2IXZ`KMB+@xb2JmRaT_raMoA~coGU&9{Ha2!gNXTom+G7% zdX$>-FwkJE5W~~w1^~c7Kff!QQfbb7iSstL^l(@VA$~6JD z7>#wXc3D-*D2Hgs%0tcYC)>}PL=SVq@{6~Wd#OckYKTBt1H5l@jdF&ROc3aTz+&;V z$FfRl^3+m-59hzs3KKtDPwg!VDMLgtAFGb4zKHR#9!_aWctvMs&ts>n*2FouR!?d7diE z2H*e>=JbtdU=#&XrN?WYr85jsE6oSd;9q>aT0eGW5mzo9o&*nPIzMrtpdkUrrYJnF zoKP$Mjjs;=Q;0t;UPfE2AjD(0i_?}G-PxX^I&`v{PiM~G13*lGhybf8cqyOsNxiX` zb_Bp#3`LsLE0pL&$pe_3q2nNIJqzHSVoc6UivXu(r$EJFS(9Tv<7*1QuieLb0w`m1 zfO0)B5PDM4NCR~Wn3C0{hrhdw=s9%_O!3c#FeIIuv%}mvU)SOrg=ib=%tp0GJ}}$p zk8*nTme=AEyv=aw0PAgqx8d{({ABORH(~wa$B{AT7@v&bhJgBAE4cl&bL|9c@vC5@H;VS5xi<`jikv>9~$v zA&3Uq&X7{+SU-x{w68fLNI6Qffvmbh%!Ok86ld&?a?XX`RmEJ8q+w7Bq%_pNy=^=+ z2W|O4b(nL+&^cpEQ8c(Lu&`M-OS2lN^;}!mMnh-jlU16ce)^--s)AbHl7aw&jN;6S zoX#-BRys_1GFet){=dGjRDpUyrYWJVMA{#OA9}eE+eMT-|3#0j1)k`%I=3XPinI$G zJV3l`wS_(<{K-jKn6vs%(4N{?4y%+_vsnK?dzX!s&$ib_$Y4{10fLf;DdrK($-VH%&iX*lgF9yK zZeItBhN^WtLW+}!kU$T@`>&}(lf?^a&y3gw>&BTd%c8GpKM#k_hYCoPv^_J1a=v*h zE%C{BmlR;*QX28jo3DA{dtceLV|z)yW8n13K5L8Vs?K~)(+gKe+RAGik1U+e(0Xig)uo*XMhun5}a{XUuSHe_UNvGCkm;;qZB0?Py)%WYoGtyn`anX zXUHu;3dECort+0K+}BA^^eeLcUIS_;fYESY8{b4gR?oa`Z9VI~cKI-@MMJF6DH}kM zec`~cX}|yKIv@Y^?R7(iGquK#lXX1N^TBKDE>71?HNW{nm10IYc%mA`umevA*8l0m zfEkgMd)py(Zc=)f+rEGJ#Iyc}qM=MWh;ri+vzHv>+z#es;pz{5N1*vsu) z^!Aq|&OFmbS8a!f-5b}`IKW|dv@2sKA0VW#X&Oiz%gg>Q{s_C_izl*}m6`}}W{lzt z9$*@RvtnkcnWBxNiZWKfGsx4Nb%?AzJw}@X+}he29X!o>IUtA1~Z=CF>;zy8d~Xz)Ah>Uvc)r} z&y=6@=hXojMBiTCj`PGp#UV7X5+EL6I&^GY#l}yOXA#J{;BgOUhG+py7gE%luPWGj zm#mJB8Q@&BpvD=FMU@m!6w1a>)vonwL}4*j#0>!9R{2Zio9$ zxd-RR0T*A3OYqKwO9xo5K_=8o@TS4sW-Sshd5?#l*BxFjGWcVjF+Mikfzgckv*(zx z>}=o0lqt@xDs<@-L$Xt6OoroylTSa6MeGVXK0e&cdAsvd)EH`I%Iz%%9d(jZ+A z#QG!98|c*8_OoscI{)H@wLXqi1mPfQ53H@*8 z3?NNtXIta*TVi?yST|aK{TV= zm8Cj;a}r43kX3r-T~I6#%`HLGgf zpV6s8G&wd(WRTh7iAAarAUjoV-@dM%UkG5prV2)t+MKtS8RKyAy8&A*Aa>(?!?|Q( z?F?fAMeyB6;a7^T7NSi=jbyKAdzIX1nj=N5M5F@MZgW7~16@lKoHZJbC%uH(ztriiIjt@oa}35P+iA)&D1B zT`G$qoe8hg3MpZNbcvJ}>1`w$f9c!Qc4iOk;5#ib*R%Eq8j3O#Rk%hRe zI%n+4LQr%PIEp3#Nzm4>s8--M9!2TxF@$2}fT$b0Bk-{6zr;3bjhwP@ zMspj|#*IvQCr{T2mr_78f286GQToijnU@a_Zp4Qur%HPD%9>M@mfa@HR(RAjbajWQ zGI15;=fC^j;N-1Wl}KrBe;zQPk&4f>edf-gO>>q+oJEL1SX0iQU!$4v?c1%Z;HM|H zQgLlWVUF7Xih&EB5g5NXL@lFV{_FN!VgH*!o7!S7cDeMq`|FL)QZ;$(2m%a@HC43< zCM(=qx@sc`_HMCQd&8)%N;34Su7s zQnJ)a|Eis4mcwl}9C~y^tF(k@qOHnS_s$i3f4?z0{0pU^WM|Tf(?}GGLg1W7S!UTmdGf4v7nIsonUAf>Ojqc{fBUgo znk(u&p+llG^Y^fDY(y8p^Oa|7^&<=a`~$^f1rwJj$=o~!L=^#btkVvF0@z|SQe!j(@ zVSw0RNb%T{T+_bwz=SM4bcPvN?;=_z%Gy%i*nTPwGyML^2ioHsT&*D|&sH7bIq3QG zXD2K8_Ba@Nq)xZfird$fZxIKAklhPnoqbIHByPvmm1JoSaV?lv#~e0A{6nwBXe&*9 z5t-Y(vi{V(VoC8=c-x83&1;&sZPDMgJ-N5)qEyP_jZFy=scnhf$gU%`n~k8o8G81p zj7rOH+bpFOLg$j&p=8Mpr~FrMpR`^3^RnYR?lHc^l6Dt{h&xttGt>&1$&tyI32%@77D6Pk>+P$-YZ z_`^vT6XN#x?s`}QKb;(~v#naU>LeNT=JfWz{=th6{^4^k?704x+kffZtL+>T6VCG-u6F7IR9qZTT&W$lK!`cL ziWHyncQoLx^XyNX+(g+Tf-4;aa$+(^zGFp0#T^xF~myT``rE2byE}`J7)|A zPXOuN=9yROL_WXp@a_$Dtk5e0k-BW64xV_;5PIDUt#g8OW0ddaW!MHCNmE8p1f=-s zR7XWWb8X$KvT!00oTaMWcbGZvA}C6nKhVyvT`^50`u9(c6!W_csU0QYiM{pe7K+>O z_~i3p8I-PFHGGj>DX*?=ai)Z-+~XToR4-z;2Hv)5CyrT`xH0oj^O>q;!`R4qLz!VS z{u_}rRyp}<4TtI{X~P*mi`nhKZX0+F)MwAl{I~6hFo$IfZQEaUhf8H4nPmW+6lQk$~OdJ(qW7O;T=H%Tx=4 z+!}EyASN9@cB=a49qS86WIgBIu}%;LJyi(4`$)Auz{5IS&Gitrb6 zi$!p0ioyo?vuN^n)(&BQ@nDU#V|s9LQ04Hrf{X!WrBz=$7oJ@-o1!lNfA;+8m86q&%jv2~O)^!TxTdC3H8VAy z@}!bfGMTa6on+G8NiXu&cH3@i-#3bzxQGix5FkM8Aol$N^Lx(!`wm}y_u>+yD47)T zulK(9yw7>IbKdiw^PKa&OXxY>Av&Dk?3hWP^`n6uL*+9a=m>q*T*Ns9Az7+1pWBAi z(oFQ#E10P;R&rbA5OZ694e=TpdUdLPWF@@>xU_)v8sLr9UgutJ`vJ@HT2;lwfb@!x z9%NQ=S$+HlR=S~?{Z1(9$7gCAu3k-8eC}X-+sJH1`0n1(ug%FC3eIGPAzKdPbA>Xk zASgr6I~!s+Q{K=KhjBxjbzb|hc zsX~#se@DH=oWkX@Qv42@0btRQ7kgxcco zv`;+ooS@Ai<@`y|j9+Xj&_v&Vd}?WXI~YTmw?H}E7CTHWBq%3{2c3TDpKTAXIXrTz zJTbvQB+iF2F^p1WMio7ksjV{fx=JM=Ev_Il>(c&w^L>!l*uFp+s2in z6)_e`9XVaMZvEqLS31>*$9&RZ@E#SuZ1k}qmAuz~?&AE!SZ#I$0`#+e|#y-dmJhQgY*-EK@0I^r&Luo*R}uvHi%lhd$T3H-Y9t~SsKbd|&y~RVLKOSjS@x-25 zJ>1sb(+q2t7cn~N2ReZ&hqfE9s3)wc%9Wn7e7Lf-Ihf#Bc0sqS5)$naI+g<5WNF?r8V%QI5xn{;e-H zwsx9qm}Nx+*|-7dfasi(LgWv&^yE&;*PpK{QX<`yiq+<|d|8x}ik`bSjI^oVXu}|_ z66!3pR}-ow9g)MxX2^C(=luEg(5Nn5*5ZYuTi6t;ZtrR}Pn9BYLlr5n-u{ zWW`n$^JIMoV?nFHTh_FmxkW@;MG6&j0g6MMNlPfKp^9Dfok!|2#7sFoG_yrdR3Sz% zdqkX_9%6ixPVP&b?Kd99$6Q`DLHqCQu0dQ@4(s(;SC@t~br(4PM6EPOEks->k|+Lb zaLvjQJp)aD2}a*w>B7-XrVf`cF3$MV6VRQ>Spea|uJ%f)Aw!Tv&+5o)(T79|;gMbS z9;;4&zFmVOi|tJdy;eI<4#9r7OcP|@)R{ZFNRW4m&t!wHe+X)a#hl55l5-IZa|JCE zeolzzF#DPGnHUnCri;W-TM`qiS1_x&9|qI_{KpA8R_$e=z%cuq~% zx%MS17C~Mf9&}lZxE1mO-$VC$1^|5g#hCPa;3~phKKg9D?FA3bWD} zO59MiTAmn#pe#Woen{P>LQ?sp>>VnIGsr#szutarmbkZlH5@ z4e{UM^7gzWVrquPRGC(o7=BEm9W2`-PFDgBs#@7pz3toL4zZ%wkN=cjL+`rMY%gWrZuoRW{X>ct%K z5&dMfx8rB4fMCL=6f~Hb%MB+diGOHQRkB?3u}orBzD9EOI$KVxQc$P=gIlaJM@S+S(< zH{k|4wsN4g5cNQnH1{Z^3XemEAp4pCw}~rREupGrsy=||e^e1vSgi;?dS@jlA)C^f zJwCd-4y!HYsl)L^#fD+~;hH?lr7Ak(gHo509aTnbX}uB2A_X=RcrqvsF`#TcIPYC| z-SYG||LoxYz2(sB59iOHKYHMWZI66z#}i+B-@R{NzWS;cPtH>Wh0Tn@bh;NRkWkWG zkLc>eUSZSJ)ke-#mM2mbYSJN~1n5NSDmyS*y@FF`r{1}7^lB=Ty@z)en@vv$5;W7c zd4IK#+_FKfD=vCAtf-5ZEp*I~RhTG|rZy)%r_Uwu=TDEkjfgEir%)-2!^l3ff0W*F zX!NOv=t*0Oph>KHI5}O*K#>i-yJS!5=;_fLtacr#L@t4_rB=I3%4M$qvK~P_qH{4y=*>E?elFLYE`Q~8GNw3m5wT#674+Fo@j3UAy4R6X}S?riuj&u z>osKfQ&IE>(ZJRCW6#O(NatMCG;)63o2g=H4g)%)Khu^BbPFI0XvW}bb!CN(14SG^ z+MLTjXOqtToLN?@Gk@}Ay$f9Rri;K#AvXJ^3+o3JM0(b=51xMP?n*PwLlkOtK@Ksi z#?eLH?laQc!CN)O0He;A$G39k}8K)?5Ua-a0(r@%{^@9(alS`GdQ2y0z#rBQ%y*Wzi z^@VNSB!bZ+;j*fFJ2CZdpPBl1-%&iDYB&6VY;! zsam^i>hZl*3a+wC^RA=yqmBeYCypL{@76;lZV0Htj(dCOj@rn+@4DJ6ms~~=F>nus zOQa-}ls5Eg4{g;MfHUQO=fxjWc&`cu5b0 zAqwqt7=#E~k%}7T+S|m0of$GT+j8Q(Iwe9zC?VeV2W69aIH)^pddnEK*#FO7K; ziEOf#1Yw>4qF6S;4R9doL_d%;Ri@B5qXe6TX`6uf!JRLih|nL{`BLyin_Tgn$$-7c zSV$VjhRB=~bM!-LYwJ5}+R^|B%Mx*>#=;gz$H~xb5s?`BR{Iz0Rw=nO$0ETKZH*?N z#AZ~s7q7?-;xh^j$+DMQ(c5^FWfNyJWTJ?H2^3jy*ZT6I1ly~l;*2bbFi4Q)m_(LT zXdqH(b0mlMw%={P=R4!aPmiKib2+lu;t~kiS!XLswjCNRMUAGcZ3ZQ6rlhcpim^W3?BC-+r!3$lLa{+jQdo-T9PQaWtTxLL}t-cg6L z%n}jtr%=+L-%_8<&u8E`z%&oPyRY~$C>K>J_?_#D;bZNN394Q=Qa_~s{$mwYhX+wh zOQ|6mgrq0Wjwt3lIPbZ--UN)9p?~>k?Mc0}H4OZmNh{4t6N`m;Q%~-#2}~7je5C^s zS+QFSXYDqq)Oo54=hZ7(d)QbPi$ms2jnZOc5i_r7a7fB5?gQ7A+7#0{GUW(!80>Fd zQ|5B>2thMCM4It`qTR8T!-)Jwd*mlxajDaTd>uUPXSDElV(RGBisL6wZ+rN&=g;S@ z{jW!yKX>l%o~;jm<iWc_gt|2uX7qpmj_T(fV#|r6$`l${biOcm z)2<+G076Gew}XKj&MetX#dGSJivv*wG;3W-*C`Su{c<7Y(VEdVY=JKbl z{^*TA_iPQ&*bFf`oQ*3>i9@xAGqs14MW=wD?5eICuc*7BQ*RhFU};+xHSnkM7tAmB zC?vajStU{~WzR){^Bo)OLBfCiWTlDN7^Nz1C{BDp(QFV^wZ85rudks$R%}X?TB;sw z+)>}(O3I>AQ@*0jIZ=#vDrfJp5=YUK>%iBvJFS*3DhAiDG=_IA{GM&27QG?xlE)cm9AdM*xKZ`w%4BL zJi(I!;&i4oT4zl`Zd+T6bhQxHz5|ON>{Cm>`N39=Z7h>(W7n)WXZ+tjQ(dtWdfloz zQ|HfDJvd+0S+$k8boHQjy6W<(C>ox2MW3wG$IA1Erhes)kx>fKne%6_>gwLE_TEEL z1f7&T-WKGmd*4`BD3Jt6C3~WNT#5Bf?USIiY*KIhE%ksL{-Mt z&ZSwwP;QI^56GDcn@w5J<2EF%dR;FI7mSpk7&qU3)u{42Ge~P5h;`xy;Sf%)#(J7o z_E5ziGwTO}9J;9El}(IyWj@%_-s%{&`JIxg;i3{s`Pm%H7X6fT9tkZZiEJIxc~*eK zPKU^FmJ?1rneg*RfLBqFOL*Pl(gM~G7bXdM*w+bu z^p~#gPhgSnh_T*#!&xzR?hrVK18OH;d;u?p8H0WwGKzQpi?l)nFwf2Z+;~-SxQD!* z6zIwDBNRT^em|E+5^8ug`X)qzLz`B(&cdYO{!Bg0xfwqjZG>96d6Fb_B z@#0tf*qBfpi6DAt5GN%ji&vx^L_AxZiMf6Vq1b0mr7}VMO+8dCNKyeB@``e zapRMZjTDL<1=bO#DmT9%)?r)si7ZYia~nr5+DKq}`dr%m!F*1XYthGxVQZ3SoDvg*+An~=Znx}wBY zGcg_#ku?%;v3P0^^U3zzG`^|BdpFh$kOTHB78gDIw0&+%wfW3Ff)wA(Eo^oAGRGJ% zv7ylOfB9@BfI0L?Ntle8hww11>zVx(ZU4^?PW}CNm+T|En%k|yO;GUzCmH(Frz)L? zZZ2C?ltrepSJWGSpWHiIy~5djFab05<(UZ{>p2S#@2ZC~G#=s{v#DX8C}~bE+`XZ0 zhoM9%oV7F4^*9f=>7)dktZ%d_5Vgn7)KA}PKrXs6Rw9d-tu2NA*KezOmOw{w2CFHa zteYl2)!u)E)5T)X|5$VddT?a`2yuiCX7ms_T|98TLa`B&XPeCK@HD&W`F(Wrp5CkcKRWVNS)??6fUM#xA0)YeGFl3)1ljW-YN87uT!09VbTGy0)buz80|LBS86*)kyjasfo zoCA1J9N)FRZWNLxXkYiR%p1+mA=;>o5*y8w@X-E4H`S%by{M9f(>#rF39G9veXwKm5w;!U=PjGauPmWgbqm6R8=(>6dYGrL& zT_?_ATs0gdmu%HrQ=%J`tKFh}@wwus3Pt5B6cdzFm6l0dZgptwXC4aAoi8H(`SWY> zs|>YeP~z`upQnJhElWp~MVzCjs_X=7C_Gs;FRe?*DVI6Swik}ng%h1xiv*v6h!dpX zl%xg%veIe~8)vF|TYLM$bM>Q3#XK#yx5ILv$)a4hvalD zSg{8Q*S=Cz-L?ipcEP;L-Po4uC{b|XW$J`u(QFSa=IrC|L_8G63QCAcgg>T1i0Fbn ze&!`ko3wUlzx!DAoN6)=EXa*`jE0}NR13rhS)G{%O*fp8ieT5E*>s@f%FXHMh!oD@ zYLt3|AzLt{BrLZDlt4d>8~sp(qo?hM7X!?5@FzE`R~N$2feWGY3^`v`Y6#48705 ziHN6kiHC9+CD9O7wtjs`toQKS-dVm64L5TiWcnBeAVrE6Zv5(uWGoy{Iw z>d?L>f#dm7*A`A}c`89b!#502U%p#bKjd~3;yhWl=n+jGZ1Gtbs7e9@VvI^eWWoH= zls>aTQRcBTb=SQFy9rn&l;WO^qaZKtr5Ug&H^Ff{?C)fW{_qpW|B3d2Z3@v5S&_?b z7=Q-&tuM7iE3z!gGhw)_eI4?5?ysyEUAMe$(n;xfQp*rcO8S--A2Sq+#4YXP?$Rl{ zg9P*{&?%tg|M}&z!j>Ra!d7ez%6|CN)R(qS9XwSp_M*S$-tEo^g;F$9n7Ndh25n08 zM7#4yC@;&QT%4vZ#LY3{cAPPD;6~L<^9O}>F|H|1in+PH8#sbQC;<$aC@R;3?X!U8 z@Vw$*)83Qv@UD@SyEhc^*YB?98Q;0FephGZw?9(M+YZ%=p7T=cP*e3gwmGl~jF}#_ z@4UKZR$C9&3%sJrKF>=kkt|imCElVhTU7H6zMX50E$7rMLZPZ!+B`YuRPNWGFIgs} zdHypuPMsewb5to>%@9dil+0n%%d9ltDcDS>rLS963vd#2#)0^Z z!m2~ev`ILj`7IrIiw8Y3x@-}*rRi!Ll%y=&b9r91X64QU$DjYs7hlgeVU2M9{PBak zANk6kAKd!zipv(=e)F{p7cb3;>w6`=uPp~8ZBr-7Qv94j?NHm!#2Zv*Rcy;5=n~Mo zYUDYg*^|xb0QD@5*mEPx1XWBp1<emkyRMsvZL+w=c0p1Egl|y{1;i5jAO7F)73t zWh**y=6aD8`k|AxDiL$i#w-b&YOgDz`H+RGZ)~6X$s6h$d9qv-Wb5^ROGnORm5LR6 zBInN62tsYTCd^g9)@@cV*Y3Woo~=E6&8~t_g`qmVwlG8qW(rlKT>Lompd6il@yyiv z^qzKRB@|AaB{#VSe(lN>h>&MKiYdpfnSyH(AlI(qb2d1~__pbVp38f>im5 zp(UQ#*PLnZ=|?g8>+}>#Rz?Ak`0BQ)x3p(7bI3;8Ql7SR_`;DggvfOD=k0EB1Fm9z z=GkKS^440{&TvrfWm#=}PiS0K0}_G+)+x8gX=J&n4IGr)Qx5rQ3OG}zpbajd&&gm% zun$IIBa6+J?~PFIq~MWNb=Z97ZT<8z{(*;Pdg}hfmMCw>Z9b(SPCYh)l!TshJ$4F&;!w<8)uY~P*NM3aS|M(JeR|vQ&r)Q?5?`1YqR}e zNknkv3}$_2$Kgt=?d^x^T6=WQ)LUA>hAj~YRBg02lfwW7P?8n9p`@f?W|VkNUl&&# zc2fy?>k~wejwLHEeNBg~L~<2GEEk~A93J~&%q%Oz3Y6*A@;hgz4=Jg=xEeZ0P?f7y zB0gLUCfFR>x@L*gRUlR$=FY^dS;C3_(t4Ofd{)e4&f7eO2+ypV5ihbf@Lb3@ME%L! zN-iY$1L6nQ@Pp&^;KQ37mlm)t;U&!M0l`)JvVCiEFYIcKWNQ>u*-jZvNb5_s8(`PjSW|+y`fk8wPpA+=NC7XIkFoDkJiXGb_X8W)ut6NFT zD(lpA&9ooeT~RVRiVY&0Ky&S~3a!?RYcg5(I8(rcdfChhHt(-nAz~3Qn@Q#YLkfk$ zaUO(&^XIiSi*dYrWp~+VQ4B-XcmZ5_`CPlG>p$o zRn=Gfy2jD2o}NUk$(diHSp z6yE;hQ(xU$Ch`;#wYRns(MCC!{`k9nYe!2F*}W=(V|h4YL4(Yxxh%Gr5*PbuLr z`ro|0?xM;EycEj$#UPErB$)<#QM=ix`jNG6+0-5FVMf4HXQbV%fx;iPBoWQh%;5>j zVtW-!DDh2NBDwUO9DkJwn_SgS1GRV%pWIuE1qxm#ef89Rx74ZOKysrb={QrB+~%du z<9+Ss3obd#>5~pa)&d+xgTk6hou8U_)%CYN_Ki;+d+~MmIl@^nefG@$9h;x}=3luj_x$YpW3%eKpE`=`q3ecji3r=9S;vV zlQW*&l%>Q|=i`^tr@HXFYBsDG<&w=C-_c!wC^xJg{ZN$q1j8eW<1;v$@4RYs?Lf}8 zxFVd8nt^w<2Nk8O+i$g0ztF6%5G5xC=e}c=ICi@DYjhrmHxQjlq<(Nl#HK6?5)%$N zAj@9U!U;ouY%AO6IK%eP&MG5d0K-Q;TY93FhD9|)U@+S1=;TUAp?wa+(24N1=UWRL z^Mt`6Fx?Rb`yp`%E%ST(tD>R}TlA}kUODu~jjpYG6^2>UJ<*wG&7Fy4xg1ixfwnzV zG9iAUg`o*ohoEo>SBFR@4_M5^Op6m>NEn>YBhJA&x02T!!wMg!FX7FJOAA=9J>C%4 z)l4T*GBVA?Yg?QNZ34#&b+Rvulw#HjpBMmU^2{oj!#rId;2DmT7;K4kU_!(2u4YKC z&5+aYb?=B^U%i4*NDT)_X>m{VgxKM*o0tGfqL5)0=02R~aHil8T|AtfUqXzVSj}WL z^q515^C5-~*(b|BRceUsPS)t&6w2DuzC^RW1&Ezrf=&q{$Jst@LI$yE;JdamecM9K(Dsf z!fxncC}$99!xM@u?nx7g1JXA1yfLJvK)Ju5JwTB-UU+)^Y^8}{mW3w@qiSn=hdKt6 z4g0Euwxl9q7IfvZdcsj!>9!m!f9RoB%x#}G|gi-v&Q6NpoRU?5&P;O?_3ROjDdtQZ;+RXi& zo$s3oH*MkPvsn!d9B^_SBF5;^whj_0&R?^#TJ_hr)mvS#oj+d=e0A4~wTq5D@y*Yk zcl-vy^qEt;w>b8NEgkr6iotKD3G+)^7Ou1n1(bz%=QdbV`z)2Ni1JfugAi#a+WNp z7dg}5P?imda3-r)5U=8x*;ky`0#O)}8!Op^1aamB3=n4=s@`1h*hsmYu>n0g;rJ$P z@4V2Df^GBu>ZotHq6PvQa*ouGaZPS`LgXL)2e`4U-nf@smkdg4Iv9sp;y^sYKU;z{4NzI^z9e6jpwF$IsG5mb#gx3}i8Vq4MT91H~PiLk;k zsw6Oc;QIRPol0|BKlyld1yAoE4U{*ou1+tU_GReBYKMloV`u7iITTGA@#vY6hkv=b z_CgN)T;hx_t1PxBvW*2Uz>l^c0HT)v68~PKXF| zcqEjrYG(X;MB_#Aj0f(BIAv*s`gSf!l>Twgyt!34Js)bX_D_dz??Tp>#!%3udYUs1 zogD>t;wsVjm7+>Cj&o?s$kmmzh#}3-p{5wMaw8iDa?_TIMFHMMgTpL(hHmfFB>m{_ zS~bhnXZzxo(oB#=_Tt-#Go#!<0zD1PFqiwb5J{mUN`N#~ks68^H#j6!(==_wJ0JhO665HE(OV`Y0=>V)!{E}`@REV=f5Sc1+kkq{^7sxl-`2xbc; z5R>TW;t-JuyAYnfKRsO!EA>ExU88A!mCa#yBNT|v5ET>8FpkMuXN-mdIH5-v5G5vH zc?2NDsHG6tjgmx5wwMdTSs_uA4W~p-kaTL*UYbu)8vp^A_V1%^F;rpKljtM_|KsN?m1Q;-U^7wvX2iyRzyyQp9CG| z&Jlj*(_K|X?M5wG4(-YAFhw8V-%)7u6Q)supSYn8$zoR9rXtaR^TW+gWidB<@3loy zYMhj-_8yR71I0wQ;i-L_(qO~$Q#Vfi;S(d%K!ip7_qSI9Qx0(eA|9U)ovJyTw5&4P zCU2b~b58onQkVjo=w?03$%=a^DdwNMsV#=0?W z|B2BGRMrjvGm+GWG`8dIE*R-lppA9rTv-fZu2QtS6=;yQLENUl(rvEDReKv}6hUh# zD@~AyG?=JM%MDnU%ObYgk5{WSvWNhMT@H2*49hb zFxd9BkJj5X$TT)0XU|pdclnaKFR9w+(%RE6D~;Cnu8s8!BWKbG2_9~5XO{qbJQ}d; zD+M8*OXeL{*V247;IjzSngO+gt{2pwIAgvSFX?! z9Dg06Of)@fmemql6>4KG;(xY{=~W5izOE{HN9C%kPQ+AhpDZ^p;I|RvFrJUKYd@yT zht|KvPyMO^kwtV}`ltytW|Z&jszdzOud3k&@`N3=TMm{XHCzs~B}??bep?ksSyE#k zy{jhA8a7~1?R0+r*?RQSA!HEbHh(my1@lHH*9PTh8%juJ&_|)aoNpScEY56u&!a4= z6p$4>4wVI^m9k#vlow0U6N}CxqMPmnUeCQw0M3t||KzGv=ZvK#7N0y^46}m(F3Nlt|+9TSlv9 zl-NfE94c=p#6!>CMu~1DoE>suNJMeUU`Xs~x%1ldu-*RvgRJ-rF$#>@>eXz=U-~h7 zSp2S8BHIK~b|BKz7sAAk^s_Q|-+JQJA+o*43tlVbuLxPadTnuO_390ZH}wLRd39!+ zuUqI8(lxxI-r-`(^y<2sSJY3vEb|<>J!`1shu2HeZ2TT7pJ^zR?t?@hz>Kd6wqzM~ zVD7P}XJtr+2o6WYuED7w`SCRxIosekpg8JFBb3%S~L}4gV$d*=b zn_6U(Nyw?`dQ1j`bmAegMb?#Y2C?FhGxiR%1Z+>O>eV(*oSj6R$Y=~xA_ULhjdxR5rI*$@2|A~D8k;2l`eq@$gvzMk>5G)IuFggLZ ziNt*^{>ESfZX7=|b++9F1Bub`P)kr+;sZB~o;%=t6{>l1=3M&@KwHoH`tvp0WFkty zB&345_ROHYn#^~v8O0CmKCrWDCclu&!kI|jiFo@}qe9tNp;}c-+c4XH_x}2Inp#du ztCqUkrA_{o8ga$qsXu$V&VrykRiun-n-$_YiB(Z;-AcJsKISL))w`u6=z&xe;6%*R zi#X{_o1us1tVU5KT?Nq?HntVgWpzVJn4L;)s9xfup=4eKdH8FcZeCSImU=fI7)AVm zIf4|D1@*=gUd1|ei}*}#Y2!xbA$1d6`yymSY$oK2fDI3R6l|=}Mh_+GJVh=;?KZ1K zqQ&2$Qg!>8_M#s;Ie-E@lu!e;Z!`9Q5{F$wG{58OY91_d!x==BhP$s@F*R?&!(aW> zdDEab7|x$Qd3?{ak8J+hA3IpL@rG0LSG;&~^qInx?&EpSbgQ3Ebg1d7QbhHmO3h}S zi0uc@>_2+;##PfV9y|B+?vwksefRn2cN{;sXT{k)hhE&b@YMdtH$VU67yiYg-}>^- zr@ndqg>OIm-G^?Qe)#u4|L8MMK5^}tC%^XaGtWM|`OwbqOw(38(Sw{_d$z1w!Z zu>FM{FMMb7&I5aQA9-=lxnuj5p4tE6ffx6^_~NPKhjtw}a_ace9s7^(J#=#V?5T5S z&s@K9`rNtc@9m$ya`}R1UmTSsA6tan1f=CV#aWqB76nO50F6AoaRd>y+x4R1 zrKzNOq;CXteT&0dWp0o7nH(~tz1dV9HhOfhXQ2e!DWlw>=9)*JC~-&w--t^CiP)_Md2{^`idsiptq*|sj(#V){QHOLqy;nP{kAfB(itagdTCJ8kol66LayI zI42=l_UM#zgDeWDLkSyVcoadd1o18vL=O{6`mR==>Yr)Im571=&UM)ABNbI)qg>6X zUdTH-AMHZ86x~3SAaeAAClWCQ5zgNT|Yx!w($FY0JnsgxE6TyUtK}+-r zVzP7qLLyPlOy$wgi9HyCWJNHDco%tc-_%>jFMyvw=Namcc*jK6!UeUs7M@`immG@G zhxO2l1JaOKS+v?m>xhfp#dCR-CgMO9q+H!r)IwAZbA!m9Ys=z8`h+tPhiLeRe_6q9 z_uRf01Y$JePLvZT@u8b)HEnadDI;ej%t`k|wAl;6YQomeIEs^=RTYRWK+pSza~KWg z24=Prak~HpLHWKyx$$s;LUYWGtc<0?Tz9MCZ$${3-1sRsoSi@EPKPJ<*2EPhhGIe~ ze)}Vp_{{fenNn^ZOGH zq5yt8kL;{TGVsS?Dm*hlhxil7_H@)nwkt2GMh|3B61T$-T@`FSSn+*nYgLhklsbWE zbH+a0d2|9IDldv_&{44>#jymG>0ud zqu;r{UOaTwyj}mpfAa4<`&4_k&>IsA7A;wO!<|3#i+^X$y?^JjOCG(drU9j)?{8g7Nn7?4blBLV8dhf4awPD?wrvh>jTes8b0vuPw$E!uYRBQx4=0h0GsJ_LCQ`aejBninxx`&Q zDLDF#tLx#ZJ?(+Y_#FMNbmo?{0I%zW#92(|q07v?*!uA5}T9K>P-tFLGe@eLiuAS>M|hK-k3wFmJtiIm+Q zXFcfM>VctK*3@eU|A%i){g)q_+SLS!=(Y4e{M2Ti+E*SfUsN5DPB`NFc`YP`jfwK` zmgd;lD1-q}LhlrC$me(hpLw<(-i)8q=WC2hZrHdQSq%T?sd`yq*9l3Cz8=%kKXpSb zCPKuc=hij#xTrKGkVHx}aoBm-CH~cI_1;EAh(*L9WQkQicw>FN@u%CH^`aqeyXf;X z0ZP|Affxb|@tG$$Q)<|DXzFJ+jeO{OPEC*6HYBTK!ip+21b3d~=CD^$AnUmor=Dr2 zGKU>8nuIG38=!RhtQbm7u=P@dVY;BbO?A?3-MV5kWA{P&4N4rf(0Bo0UP2v(%nRyXn%^Px7FRbh~< z&M*}0=|6q4o;eyuDm^QL>ULH8*{bIzD@Z5By-o>R5N0I`CagqH-&3`vC5E{|D^g|= zM^25xtX@C=@GtAe1Z+>YM*^=M-%S>&pS!6Z&BOV7->ykoL}Yakq^d`U(kR#>4zY!y zhj(!%j)lJjH`nL$@~yX8L}))*L+6Aj45Bc(B2?$}uiaYOAoIq!nlKvu%yj+c{E=Hn z&**h=h!5S+a4+lUZmxASdahVH_3`$WD4gq8)GS3SqC|*q9yKg-lyk!sEg{|&?uUYB z)A-Z?DRk*1LP=H`YV$pwbmcV1TZerQZ`)AsWKoAsWf)+BC-d9*mi^vib^9KMSP$`r zRrOFq7yRb)#S^?`ZKW?;SSEzsCT#eX?%zH$ng`O8nQ&&;QTVxM>rOY**P%0zG#%`oWWR2VW4jBqrJ|umWcC zPwjhYMIIK+pZeKNBL|{6dWK5wSYJ;LqHI`Ew{9UOOAmki%;-9W7?PFCio@p8E{!l_ zb74w^MQX~1Azr_#7E}13R)>o$@ePKu-`GC&zH94{GpPzZ645pR#HbBtN3`{thbVz)W;*(3 zstv&zj{S~DSrSv=I0+uN>eS#V|hz+dhb4nM((@gT(R6?BXCE0#=c9Xz!J=JqUp`-OVw zEERI-Ad%<9)JX6j3l&pR8_or`PiW*VO72 z!MqUD~lqlo50D*uz}F6G_>yq z=1tpQey;9LGOaSlmP?5ep~rdqhj!KzAFJD&P&h`GZ{{`*>BWpFohLZ#*@}DUHmdGg z-*Vd$k{I#XgXJx{(;X4wd6p_p;sj|ECwmGjF>Xcg-dIuz{r~8@_4=?fG=4{{J?o}J zI3=yY=i@mo9EjO)c(5BkWh?HR)z)yPv<540x-H2{_HYj>2aO%Xov?plPZQ&aX| z@+VVnhFEv`~fx-XrKz{bjU%6WEae@D@Esp?!;Q-2TYt z5AS`xNN-r2KR11N_twY1_4&si{MwTbe(9+%{i}yQ`!7HLFaO&oKmX4j`{E~`_}1s2 zdgyBpeCumR_P=o8z=0D-4xBo6Xmr}t&Fk!!!t>|PojW&uZu;!>*)wO)oIZQz)Y;P~ zPn|q*=H&4s`?l|T=7FtGd~4gopMCnvfAGC8{?n&E_fH=D%YVAsMX&?2FYpoc*)}jU>%1ZBGj^Rl9Xfd(f}3p@Xi*wU8SF(KX=Y+BW7T zP`8vIx-=yq9ky;rYm{aFi5?1znxZD>lEId%Ak8>Z5+Hp;yFo_c*y$pMQ{u^e3Q-H!Ea;6$8_WGxhQ>aDk)nkTyl*o=YiGaXtwiMtMJs!jvMM-~uGBCS92J zu%>-87Z$hAj8cL@f;r-@J8aS@L==H17yxIo>TukdbG6q$AHZIkGuUofGdfe8Nl%5? zJxS?ol-$yW9#Sa|wzbQuEBM%5wd!>H`l3sx=l_lN@eOAds?;t0i*GHv9q1WHgilOt z(hr|4w^0;92fMi4ncLaaF#4{eBZUfEKA)&{NrP#MY>%EnIeojsoKuHoV*K{Ws;hP` z3_3}o_Y)4PmA}{f@x46V@6lFmXTp52k7EkyVK^q>N0jWT*E-jb! zs>W6Wn)=YCNa2xX$Z!Rj3q&gW$M=>jPKSV>AZKEnOAf=wbQ29j~x)`LG6AJU1v)2$8@9--v+N`JP5?=qfw1D-az$?8xet4JD zgxcZi)x{W{^)cqTrOvgv)`jo`mbA+n&L@5w*>kKSn3*6Fqh0p+nfmhmL|;F*S3|d@ zGlXY=oG6h1H@O`>jV{g*-^>IhfI$(AxX`8g>eUcK{fc9t9a11(#gm?u#B_V)tw#nS zCf)f%3{QZju*Q|(PgH;zAt zpWHIn))&kKKG|LeOG#b^fuV&Cjy(v2IUg6@Q~VZWb~R3 z)}u-ylMM_eD$!|kNQ&opvT`Ir5dF3_^;+?W*d#*dlJtjesx`zc1L<&XZ9Zg?G?xsh zXO$yuW1`PG9O6+M_T@>d1Pu16XP{ZNXO4uy#G@VvZR>PA?9j*+jY3|@`c#|Z#=41t z$suf5USByp*S=b~?|6BIQ&mTb!zv^NFUrY!ZxkOGl{R&u(4p&M;evKH+iSV_3E$Z@ z_4nRgf;5oDoKr@dVv@p1Jd?(I+eA?oA_wTX=jx(-;@&D34LnJ1#Oe9__m)o6{^|Az z2@0L_wVXCRCZZG=s_gBXarWHcgRbxTRdppkv%hjv&-80rIv*|(SGN+}pi`G+PDO(P z0{3W1S->6}&G~5E?dz(0L0q@$vSa6$KYRaQoSSa1?|LKS-1ONa`?l}d^4N|oPwwBb z`Q))9r%oQ9KGWW0^Wz02J#+HtiDO5PA3JjN@WIWGefxoLe0kd=UwHPLfBN;m_!oN~ z{G%N&ys&56(|26I`p}t$YgaDO478IV?bE66*R^yOgVIQFYHYbWP_j?s#A6R>em&Pl z1EN+pdT-s`)vEENF%KIy&Rl&F57Jyw9vHux5OVd!L)WqkDhhLbMT0cEIQgdc>*)i> zYxOGPDII$HPtFjx|M>Xwp~t19)W=TObph!*5beLTWpuHS1>HzKsG9x(ohp0USQIue ziXP_67gyJsSA!{|K0t?j%bJlJlZeb$vWtf(7`nS(2qmQ=4p9u~dz zy#}TO?Pma4xmQc6orUT;+6YC8pZNdbPt~t7oQ$n7E9QUHdP4lhqpL5kG+X%T(M?r_ zw{b+J*>)bSboKe;yX}}Dg4rg4rw-e|v~*$JC`~isL%T}wo{jYh3rgwdm6J|C@CsYb z@2RtO_}syI{?uV*wWrY`{-mDi$1h9e96VJ&C9<-*eY8YaLV5IKV>g;fsdmxnwJXcp zs{Ct4>rixp3UR*Zlx2^O0Z8dQ5h)4L9-Q~%XKU@oVW(H|udH9Hv;l&7*Bg)x97g=e z>H2LchaAYc3s|p$k5>Sp7S390OhIhD2G%UEWw@}}qik#iq!6>QBlmN4x9y_}IB6^< z61Ssy7{G@uiJlJd6GR-=A*3K-c3zy6E{GqT5#v^T=$s&t8t$rw7|D)3zYyZmte9Ky zA~92cy1_u!Fc)(ip2PGU4!=5FNZV^}=!b+?>f{@xTtD`>w1D-az$RM}?ml`!J)w(N1R^kVxhI&zlObKY4;Vy386t-> zwK*KFUKx^mUF{x!`$iPo1&128jXy%%AvbQAktQJ=*$P>x^UuCOSpmW*Ohz?N6kJB$?@*sENcwL}h;pU4|SGRlS<69D?s#$-> z)gup6Ui|cMNp9A3)E;f+Qj?0pkK9spJlStboRK7H|T7tPEAAl0=t(K+Q z8YOK`86~lWN32QK%upp)-L_h#it|*(pG>?ZaOOrW>Ct&<6FC%V6lrzNO42ms+%e~T z(SsO7GljrI{0`G%BQAmer*5dFx*;gU*6%FCO#Gc2Yu5DO3-v$;mvmMizNM@yQFO*1 zTb}nJYcrW#x2(-kcTBCm=GJ3d@87lc>5-XBc!T2n`P0XboH(+7-|ijHJ^Ga`-~PAX z{=%Q0edhulaN2y?Pr-INfKo)IF>qxO_wOB$U9ecJjJwS*y!$b@}{9tqt7W=3Iy`u)4qe^& z^_d)?HoaE96^kBM?Yp+NMqT3Z!JgZ?9fGQei#oJ}O;&BVPRV}qu~A24K>X^ql0__+ z#=laK5>b#c(CGcRq{8L?`rR!T+M)yN99B&IcKyWC-r28D%c2-?Al~xo_u37-`17NS zvg_WJon2!}iL^Q=!gE9+8S6~E^Xgi4iV|80A{sRow;u+abh1@xZ3NNnYa}>%wQ@;2 z1>?mp8=g>zko|+ls~-bOIHRXR1lp8{=MJf|L7lbck5b5@Ct1}dU#yqPlalp7yTx0% zPMs|ZhZUJr_&M<1y(24Xe(Rypz#0?isV@BGWb3X`L_}}9YLu1>PT7tlQ;XZU6E|$} z4MlDq*9tS_7UR?*ZXHHyt)0^5v}+HiV}uPugj96+0>F;hv47 zbv;T_Q8eRmjUxhznvCGGMWb9$sA>}9f^>wz=1gr|S;0?!+=bLhP?1tw)I*Hj_z>Bk z+mZme67gi1n2?%4=M011jKv=YW;)<3TnKYJoU98m%nI>aCQ3tyjg(nOM@s@ki65JU}r%2@87Y6Jk$b-ZXx&sLTxp_^t<0qWuEhZkB1^NQQ7?vsW2m z4pB@aC`8O-6gFd|kyRFFs=TXTT9Qb}(qk9SP$iMvuWcKJh7enEOyd6W!e98|)-^2> zT{bFmTuKBMg^3`EK(A+h%1uI~>wIN7fQFjDWJcGbe~0$f6x+}8rVyb=kmdu&r;eVP z`i0wCdsx~gY~wEylHGHx;&KV%%n98HR>8-=vGdSg?tvE5cRajn^nd_{6vUoI%=A}r z+R_pchnZ<8DYi^JlCFZQingev{)vZ~+U4yt{}KYt%NJLXY396ylr#;XshED4-rz*3 zDO0N%vNS6u`mJl5t8G&Ep-of&{5!Q=Rh82|u7}gvR0C!sRS)fi+5!}8|MZc1vqolF zD1bRdV*-U0bHayrR%`t2kJJP>v$D)yXe;+Nm-1BU=bo)uUv5LVYkld#kTplSQC>}` z&F(VIc2d5yb?QI7r_LkLg8~n>`vhsGfvf~so4U?FGk(GX8|KhC@SbbS3g-a>b0$9z?5GA1PB?V3E!vK6WXO5PObOZC zlXCKGEoa9)^z8NLDb&H!3pZ_C^WYc%^vCjMr8g(e!@22`M-FV;w(V|(YjD8Wz?`X8;4`0w67qBsO};?noE z4q>qo|HaXRf$1Ws+IV?I0wA3}JU!vLHj?E!?W=6LG^8<1pQ~^D#SkE3&XTv@Z?u#% zzJ*8DAAh$!7rCrNc!*QfL{F+h2eP!?s}laIf$=i^qkC#Kjg$^YRZxr0`BYu?y;JqO z0=isTIO();6`PCqufL@p1Vae}{TZg&b%@Rxy)Nb&6I+F&#Dw6@Ddq-4=%3m=b?>z$ zD@)h$i*GF@Pwy`W{9bh`cI7!4fsF*7JmNk{jD;D`66%TK23!o0tw}@N* z##`!C7F)XqwhGk-1)XXM=Df6_M8dXzJo4(L*{YgvL#?Wi<~b!*y33~io3Bm%NAIdb zHB18$MuC*HV#&AplPyzdn zz4QYx+1O3E{^;(yfevS?G@2ni5Ixe_z`j$G$R4`X1TZL(U?8`HsmzN<*Ey?l7u~k; zveAXCcVOs~g;@~|uDiUf(@C6L;RI%`UQw7u*kHoLzIqk(+#%bn#5pjNZB}iU@MgoM z1+2eH%(}My17ePg`A2Y_M}G{>G&lF^75-UQueKekXNX{?^D|9k48BOrRM;WT5CcJv zStpq1knP}J7wr5KKF{hfo*sNmK$cL6+Gvip2@@2Co_*J!QMR*n9_Yc!keQcSw6p|ejN>)JtGF2C-VCAhAo**d$zQ=cY#Qv9jf1CfJKD3LykFw^1p?fDBUGU@R!`UR3=(v zj1yfh&Z^b+1FSD%>Ey}b_KSU#a4(hMFol(PYJVkYvzMEbwYj~@>Nh`FR~Zt)dGji`f2I|UTFw-` zc^Lh5QdUp>`O{PX$@{Ayc=F?I*kT%685lQ6dxhP1yx!JPHQbh6P@DqV%+>_Ena46` zHX=|~Obv4Tp{d_^Px+=cI3VoXyuY5o%!3;=<0r-LWs6#+wcp$MQ(3Fq%?_@SHj9gU z5}e80II}*7E*9FfK!QxAlonF})vq^@qWcO8f; zF7;3zxAh$Z+=BTd&kvufZkD&I+HmPg{BXN-3LE}WqIEJ7*RHC0_}_R-UDseju3WdR zEz^2P9Sb^hy2O!1!|TTCB3#{B5knWGsa{f*+aW&;gCgu)b7RiuNGrMo8bi*GHv0UjE3TrAoHZ8&3y6b9Pl^7cSmSB*<) zGuac7i`n6aZYmK}re&QyKXtm@V8z?`hRvbeD6!X&B(}76E< zMmI+T!9Z5P`jOM4l_s|EXV^M$d?bG5vic!PPs#)e4g9I{w%|b(*;68G@q)@y4=M6U zXA7!T1@Tl7*=V+bv<)2M(Hg@*=N~>%jV;Wv!g^VBq06xXuIbEZF0NCPCRGo(ZvDHmj)kTvO^ z)zd@D5MAf2x!mXz!#R`$vU+Zw#GK$yS``IzsJckdFf=i@!dclaQnq6z+pO9y;mw9i z3s|oK=I&qSz-zvG1#@`vBfHLH$o>`8%!N3!N-}}^9rF8Msh%j%F#IYIKZpB?VRdNe zLYP@thoQ)tb;Lq9{P%RS4EYIhg2*Nu0rYH9*x80468|gS8We;`FIVU3#I9-%&4>wN zpr4m+MPxwMAy>nO-pdvAbgTgWlvf0t03yiGvwTAoCFGX)F0Mkvi!f0ra2Vwd1!{sb zZKGV8SMNuVkPG6KxXMY);U|k(G+&QOwHTC!ltt(h_wYm{d*_RYv<(BDkYG_3h>1fH z?8eY2WNe{u{3n=wR4JXT`7K(7uqA;(I{NXW>~E7aL|Nb5UJFC$xqpfS_gq~!%~4pZ z60M9pEGuo=Ku=tX<}%c(3#u~5;d?@4+U$29E8_q3KslTry3tXJudQyxJzx|~@uL># zpkJo=ev(rCI#WuA0h{jg!Cu ziI3h@;#=B}1Y%r74s$Oj$P`oo5!s@rX)0IESE+i5IImW(Kxjy(2_d2cp0}>8M_q7o zW?u^&?`ii_07Hr?jgzr7hi3oKnU*ywng%(3c* zy7htSC94-*an0hzi$31ojILQ$t8VMUdN!@Du7cLN*=m7l{Q7e(v$er$V?EEuB6d&g8D)!0rJ*gBNxK{nbu9k< z(pzh#1|?69pExtJrC&viczOn)L0T7w2e`58NLhCkA3a^2NiJuYK3_k~>^L&|O+|SVS6%P)NCWq3+2VYeRtlQ8v=V*d-kPK4n&ry<+bg$RnpX~E zt1C3if30Jeuw@i@cxM~C+gOlMY2ETs(MY9clFNGZA39mjq|vcU37*g zHNywqu3J`P%9>@Pr5P4E%pzub;#GcZNTZUV1P|q0!WNNI-u#J)<%>p(J{gY?F_6V+ zZ!D8Q%;HFL#WVF{&87h{hRD_s)?yE1kVABvBFL2>IBJb2YkWb$ESCm!WBm*5{=_IT zXlCI?E*OUH-capF0TmXA1967%Q?AI6A^>7GKnXEL=(4(W{96x}x%`}Lq)TqtY}n$G zbc#Sp?1pm)^hlj*g9wFPoQdRiWc4cOS@zQLGdC8A*=`#D{27FKLR-%G1USskx>#mI z_=l!bKncWNQOWA)%(Ei0v*ymE4^4E;diO)#BNpaa^g!^@`~`Iv~>xu zH(Xl4dJS;t`neD;vSu#S2Tjn2hZ7GB!i8+jd|lK`42R)_0&|!SW>O{&hme)rNM%Bp z`E4JZ?30@ypaTP!jIR?Ij%q_^q*DY)T+(y9NaqlG3Fakm~J$ay%)BxH&otZ6PdrD1UtKC>>`w z#wg9@{E4Ywd`rDRL1E2l`ERCYuSAeVEBtjHmySNT89inf+>q7hBoc9W!-_H|3o(nH zh}W&EcVpqCA@6*F%y}@E?D)_M@k0=TY@7tE0kmK5%{l+| zlcR!T_}~qt&9nhhU6@TF5Wls(Js~xJG;t+fQ|LCKO6}ELZhY^+&upsKlxQ<%t#_<1 z3T>E4DWH?Wlb>x%gurgff+&|d4At<)2@N}fz{I8&?sLgrU6JB|T$`!y{^K<7hg zXbY}%bVSV(-9_}__Iw{AAWFNM00o1VmIk?YSuF-(K-Uf>Ts!ERD2hHuGnPn8)-6z* zmE6(g)2`f4ZytB5xp3Mgy7ySUtLv}dR!K%@6-%wQw`XAOwVE{@#zoq)a zs8xX(GBEQQF-|H7I${oKAgBRwQsd$Ig9cU+VEaL0>!GSoMB$_(K%x8hOW zjy%kc9v>iXbZ3GbmSf+)@&yx|(8cYQG`aQ@Y(sPhv}X*bMkWX{>iS6-^n)Otbblrso9X98 zTD%Ys`p{N}qF0JO;)--za54+J0~fLtPx@dP%7rlWS>nv}=xt4qy{JN#7{%dCon1T_ zG7N#(V}c>WOy;hkXU$B^X2$Or=s;NEg+esg|zQmtDT=6bJA_*uZ zIFR^~av~hI2_nxxh_0~AL5jw#`GEA{Q=`{0XzJKlq3X6K z*jqj}bTKZ5&I5f0!ZfHlhumH>{(d6&SfK|))=kjk44`13T;RB%8Iu)N_Bd}}S2Zso z9;%boBKugxsWzxi>@F=j=hYQWKXJZ^yrsdMHVk<$l`NB}a`i;r z=Aa0fGGQ>sICF05sl8Lbd`F3x{Fp2Dd93RFhd)&({qiNFJ3&xtveb$|1BX-RyN=c! zUI*K(M#HF6*5ZB)u%^A~Hfl{{PEAk!+8xcwwwy{Ex9cg+LEx^8W#7 z243adNkrUn=EfZRoxJ}gbGdd6) z{QQ>>?%viUF5$-+XU?47yY0zGzxwCTKELa2ciwjO+SRMutDPXtQY+HN;1_PI`+%f1 zFpbcr1-h&x9%wB;53)(1bfit{TfVuy=31Jc_OI8yaXceUC!mqnn*O*AYl^irFn(I_ z`9t-Mf}%HzAGzhFyIS-uC33QOa%F`lO5|3@HC1l~%7tKag+8*Y3Stzf-l>&k-|<>b z;sAzr*_C^Ge(AN#YS3}DzVEuJW4X7hSwxYhUz#T>EkDH}X1(TLJld|QrVcYH#0*lY z#$}7DN>k)gJHDYcB6eIgQbaa4bSV4&6ZKL&;|TiSynPe~Z#B-gzCsOCZ10?MKv|>-)^}st zYa;P%Ias3-Xc)w);!+Lw<5Om%Bms#El;*YyWF5sJwsp(vLCXwD)ibAUw7b8FIE*Ja ziBM%+Fd}m`?*K~ur%B*>?{%Z9&fDm*MG56w50!7WjB$LEt0v{*q1@r#W0loc!Pt$s zG_WPjkVbW1Ti*m(QNlW(B{Jwp7y)rI#Jy^|?WZ6*qb+J}62QamQqDN2c86!rjpCvJ zCAmiP?d^HJY#bxb&}ECNASf%Bjvx4KBFVJ}hr{&nFp%b0D&NUf51kGva^0}s^oNo} z(kd`OOjdF`51<1*Dt1T*S=}OZ<-O8^PM=73bjm~)=BSz#v2MSleW!Z@7m|8eT%_Pc zL58bW7YUsw%wcZUx_Uk1hwbefu0mcv`0-r8x|o}8-avS*8ut&bhXxm0A2zzeT!BOV zGbyhK{gUn-{SU6nSQvE5g)nO_;zjz<*^&@ONHE&VBHahb37xW_6LToG=z>F4!y&&T zKgta;aX1kxL|cFUKPDiCl1?HuuHx4g(nX}xqm#CZ1y9zRnpjO5JepkrbVh@4&|Z;arB{^s1F1bno5^;=KL^g8D|4Nb&g?^sxj=|4loS`5@X&+5emb72 z2-0-ub6ZMEgo@6+R4i4AIAt9;UaBahgl3!{yQ>()vVZVIRXU3{vhY&O+U6L-T!NKo z(}1uYJXK+FI33#eT$;%e`Q&4DF9V!7TQ?=GT2klNB;hUVYE96rfFNf~W~#8Q$dqtC zI@20URr5_22Dt=dNL5Q<0L<$A|M-Dcgn88%Bu-7&su^G=m6afSaA>AM#Fih)|NQB? zbD?7Wwl4GG_DHCJ;h`cNNH^K9rxCr6}cuq?M=g&b%R~Xw49(speisUeR^}9 z$DiHQtk5~HP71dBw~uO%+CZYp>)f@MOfw|yY{R*_ng9U*^hrcPR5Bq!8=u)?$dvEN zz4gYlyjzW3`%KPFZ1|kIGKu|_J8CLLGYz%1&jsioYO2;=op%3@QA;KZ*#lqMiyqd_qA4mM|s0sb$ep!#JpvzuUP%?m;Y?~Y+FmdgdcaD zK6&i9i!TecirkmjV(YCmaFeW^twNo&zWQMB01%ui0&@Lo! z=PJ->?H%U!A}yb)=uDRmxu(z^?m9a4GdI>fl4+%`O_!d|)*P&^M}u#>U~$F}RSxVa zapC+|665Ioi%YG#PJFzdHn z?6HNKn6|{*3(NoG_ml%n7|0NT8?MB&?FSwjOylD-4Z8RAJhdv(E^R!Ye0-Ee;YaVR zy$+%HTkrRoXKU2vRhX3`l}5xKC4zk0a&YP|o+}9GL)bF_`hgD`A;OOV%qsl`pBt~ zMTZhExq9X3F+ivmK0cv%AR})y04dDxzpgkDZCDW@@71CWWfND3^}^LxjOORi<9^Ggq&|_Cv)FqJLKRW)d&PM5KNM<^2(ucv<|FJMhNE zkLLo`kJIYa>kTfpnb+IJP+k$b_^f(#I>XUo?mlQ{)*Q?ovv!~c-F`UJ=&WvpXk^Mj zp2L}JaQIaH|z=AM$e(|4l=b6=_sKO}N>xbzcWP~z|20+@jWt`iy@F(1XiL4M| z=s3S|5N*F8X%$ygRK20YU2IE;aWFAyz27N zcbD*IQ_uD^L?lv`)fPt}p4e9{wSd($Mg${v=V!#p_G$#_`^NX)tIzl&*Iy4(%fyMP z58hZIH9h&ZI-W=|7q#}ZMT&I^$kc+LfO6v%#sA2zsz$MLRbcz%kQPFrEIizxEe@zZ%G1*RNk3o3z$a;80yB2d=eu3uFZ z&PqkAZIVDr4gB7>%Mfj;RsI}1UFNumfg4wtZMJ6{?PC-VaTZ6aLLV=yDPuX=-tqCh z{WS-W8^HwS=h^8bD2c)Z&FTKhHhX6mm~L0GM-0^2C+p61$~co8|Dy`i|jW zJYA;Kb(|}SdRqDVE9wrfzjbG=7Cp1Sv=LGEGwmr`T~j^yc188K3+In6Nha#;f%n$) z+MLX_W(fG(caQE&!Sk~>)hBoJJIy^;mk+?J^9H6?e*AT%r|)fb61(>Q=z*!9ySch_ zowivw&QG*Xk6QjWR&~W~{8A?h@?cxg0Tkj{=>S&J{2-OEx-D8Qm{+}MTtZhH zqyieS!DpT=qC?xBW3_rEQDb7`#FI2PfQNz{YTvr9jE2ER`L~?(c1EHWe|IU$L2+vI zY4lWrgxZrE3R}G_rK&M&W@6F6D29SRR&aI@jFtGH_<2bjpp#P1$#R&jLoTER!kz0Y zZtU75_>)7NN7@IP<6&GKGR#EShPj5P?s=yxyVI0!QX^&qEv**~LZ4}_Q^L@5XL343 z)SQwY0$pfvmezv*$)P zuH%X6o&eF_j`m9|Tgl2G7t_)SCX&^qdcr=7N|6dhV%x!5l}JR@nLE0YXbuIxea{kU zMInR;3`MF7LKWfgsA7T9_gq^M=a;rt_~MLXRtmESiTm1P1#fL%be3QxM){vPPt>O1 zbao$5L?wM8+p;jl!? z5>M=7)=mJ5aj>8?N$rTShUJ|RYLQPlN{3;Ij_O*Jyus@7-X5(;j%?lkk9<% zX$q?9I6P{fMzGEq2?XslWg3QMatwXw|u7(gIPS+FM(h0u*#DDos6GJ$=^h zwRD+8>ZZZ1g<;Mm&q~^%Ed46G4%0Xx4-P-DqnLFm6z1~Db@cSUQnmeX>2cA0bz9vy zcE|eq9HC1XgN@O9uc=i7sv^$BuA?=QKC!2k)R!zM{_|}S!Ikokt4qH@6Z5y)qkj@a z7)mj93rdH$Zh0+laqQIDsq5O8RdbCq&FH}sQ$KM-Isf1bwemH8Ui}1e^O{;6yK!|r z_@+}`xM1pEe5ZOR+6d~(Ahk*`bw#h-kKS2(r-1*pD$$34)-#)Ce{EY?uMWJeiOX_m zldj5d{WFUO$`WbM*-@xODT_YvkH1^>5KcwFsZRdjiK)N$?q=FRzoO+jFY{^yQ8U#H zT4x&(Ss3`QmXpwNb&75X`2PYE{Z zEicy4lloVOvM7xBrg5K7Hrhr-7>5>!3L>3P@#;&-QWYoB?U{yDf`~#;1n5*nNf<~$ zOv^&g$QXsuhAthoYB);llStLcl>ijt(vR*bsr}b&4?}TqIArS#_5!D*U8+e0ow+{|*}-!dGDrE45_F>- zp``xbSeRLsOL&vx(gM~S9=QYdH9>Ag(YIdKK4$T%kzVFyyPJvIaAsl-J$?0Ri2i#{ zFq9f9ncJ%ip-WLU97j8Tw|!-4R>b-oc8%*9lK`^`x#()5;6{CvjC?lH^YyDn42I<6 zrzeNl9(e%ek%I2O1azL9i@4DZWf3GqQDbKYJmL~kh{FL}LYDpK2ao#=kz67{WSzTp z%SP|n4TNRr23m=0+6n?&ss+SSTvBRf#9xuqASeL^j1>&hsux3vjDNisL_$PNRC;U| z1|Y(2O$i!3s#n=Q9!eSmzIh>GM--TY2%vjnoWJOk?m|x3VUCtR`f~lT+r95^P_8zE;po zJ0qLDs`%*}OD%_4$1g@_!c_1bSJf+T?_6JxW8}rX^rZGazoph?=r`5L8=Zh5S=44? zAZ?>c)gemPY^|M2=2ALErZWjVzj8-)S-|;kZEqFWlw>kYt$8oazj{aY$uvt-{)zUA z770=~O-Rh*9MVi}-b+<(w;RgL89sPpS;u2$`;l8Kjc&GsQ@TT)Gm63xJ*jifpd&IQ z9g00Tr|3z`Wu1F8_q)tc7zLMx(}B}jOO@hBq=B}qXEBO7H=f8%+jDAY!;`Iu<30Us z)dLhSmXsP^aQRiM7argI@K?^A%bh5f@TS1I>9a39`}p1$p1X3>J1<*#<%=ig)krs< zglm(ZZR_&d%$D)mYoNXL){E{iKVqdZ>*7#IX=Y)|6_H$!>(a=DEFl-zO%nwP(B{*s zUG)#PNB`2)JC6kSfF5JhfBwOG{T3b~t_$WlfbF53qsQ^|DjYDhv6X534Xq*si&}X=tlm4yEbgp8n@Un`&}S$<8CSg0S`A z)T;K|Qgt}3;H9~Bw$hwQ4*>ScVtc97xw)*Z&a-7qH?sV#J1ZA+^<8be17%P5UX{># zc!xwVNb_*vf?5Q3*0^_h3oTMiZb(ygMSI1S%9qXu_pn>7rVj0NU795X=Y1<)V;XvT^?C>FR0GBQ7^-3~9Zjsw!G@ zOAK}P=L-*2oedAfy_}kW@)QA*i&9nBOrYSTA>tB<&$V1xk%A<`mOY9@c;fT5 zD@$|LXB#No_D2(k z7m4#NCIbqy#zD`_K_U>-B!CEpJ`DLo&+=18jojmY)_&>2@4J_{10_TQ=g(}aM6_`! zV`m1`0DXAa(_KRk9VV*FA#IA&iD8&|__-}rKNDG;0hBB@p`4norWGms?f>I*8g`wSw zEup6bThr$%l+-w-vq}{=Fyt*B5uxfGSC78^guxa%(`GU|kB%lVSuDG4Z50zIaVG)< zbE1EhK+kV|Y3kp9XZ=2@WdKM)x4tr3(SI|_!!K_wE5HAExvjjT`Dc5+BVZBdbB8Jj z`_((Dq5{R2#i(3F${kYTFq&1JIZw3x)D1;+I_lhQo_c$GB4*doc2%}n4I)2Nd-mbH zroHRkmgb>~Fqgs;^$4D`ziP?U-B;D&sH&T`+)G8n-+tRmZ@j`_S_svgHddwm${qD^ zC0jhn)np!M_WQYKORcIqex_E?stHZiU4FVIF{=qA&hmNeXO+6uQhY%9#rE0$`SaU^ z=jN&NZB`sN96G@46#T&x)fCM$!E`9IpakpqdE(!%++L6P(VXrJu`Z?7HlEyD^hb6V z|7~lgzVuuzKQZBO`h3-`lI9#`CJE9hsoG;G7t5hmrG2`q611Osh`7CdSzTSBsb}WX z-V~PfiznwTS$EssC%^pSp6yNdCA?X1{`|S!+nzr7!shqi`_5~wT6^=FdD@kxsIh59 z5*oQSrccmL9BN&Aj!wN}W4*mjXXZ7qok~L%j!;_ z#qE2MXS=BP`?b#wyr?@o$=BV1I>Y8=Lm~+USv}&v|rM>GNKPAz|)}||pLK}-b5gv3n z1ll0EwvufH33_fkwB`P$Dp9Ze!TJ=2s13r_DM^ICYM>20(Tsv_bQAMnOEJTrG=@N- z48Ac$zsl+Qv319Sp4A=0lQh8?jh>-Y=NTfu;?D%ODCuz6D-eAm%VEBO(-B*pZI0>B zgdxcDl{4u>43$q{4nbKnD`5h&=FTnh`oYEc`9>+rB~0Mb0@hqGZ2TCs?74fr9|2xl z2dL`WW+w8(!rbQgV~S-e{<4l4{K9kebOj}0o+)AgxNOl&Lu^EPTJCU!NP4(>75>iS zFu&JDwhIoCVHiWk`{j$L{_z7fnhu9~=%Y1GxiJ(ZT!{XmNOX41Ad)cgHUkfaSqTpE zBO%BNqJeUoaXMdj7Cl_Ms&0qIOlS88&sCS#>x~koK$cLuhLkI@LIh2)g=nn2aHJOU zdTr2#xNLF#04^>J;#*Y79l6?;d!jQwoSLrZ8qjIO20}(@du)-CHuK@9D7ixa_A4%; z&{>!+Eup;kY;0B0Rw&4#gSeSUp4y;IK@tJieNW9XvJ_T~wR-gM1$wgZ^OgvODD{?H z{)`n+XLxrCvtv_~q;7>mEP z8B+ojhHAaIH2|h%@xcU!w-T7u>efSLLWSfRz&Pyel!vX1#54a~+Rl3%pqWlEC5z8C zAa1VmFCVR!I+w*Z3yt!xZ*OzgcEi+fez1JtfPYL-{>|-m)#JoK6%+I%nRs(VzkJpJZwH?%q*xnErU1RI^KYWO<%TK6=Q4+!TZeN>&G?752lo)U-Li z*;o5)HTnLVS6p?~RbTn+pHH7@+?Vj?!TIwCcYSaB_nx@F}-c6n`S#wX6!+6v-L<1e4yu&VAN)VdH!p?rFCwX`%vlpJc4D7hST zo>^&1S9#;=)>V#Q6sXZ^_Sonr0>owg}pI5b1(ABlW9yl&!@J%jl!KM{oC&%NYz`-8On! z++m34&R1_tvw|olf8t>_C9($zT`v6&cO0qx-@Lu(wd8cm)Ia)msiK5w-Nkh)ORauJ zVBpk{ieNqG-oD$28H}n|wWrVImU|oPo?M0`sImb@1tfdmWZlRXe-wnP`GvCT|3s~` z|C5JDKA@yujgp_csXWmzGQz6u1@m5Neq7popjs588llZVbJvEE&w#DplN$<1Ywh#n z`FLN6<}6 z5EOpDY7E_&6%!0E%YO-%aPhdbfHfC>Jg&6A@=|mMen_3yD^-hGe3`_{!pJdwe)LuQ z33R(V+smiAwxL?-iD43k43NX@F-$}fGfVfCt@WE;ash*Bst^z+I6Fcg=EhLQ#ZHm7 zbss`8>d(4*1&N!ZK~@lksInbs?=Ju`$GYV&>5QuEGkL}!Ub$>xF@(-NabYS@ScXA} zCaX`pufL-97;KQ@e11z?+Gs=`Cc46(GzO|LT(h#?OXBc%+k0dUi0pat=TFzK!L)s%eej40M6oPn8w)~fbg&r|#A3jOM~TE~$EvU2U~)g@?fQAaJ)8t#rG zbuU%y1|Ql~7Y@ODuPJTP@y!)_!>XxW$4Wzb6`sq5gROu2*xmJg5*>?+E*Fkc>l1PT zW~&Rc-S(JG5ennC`K(-8zZmH!Id3yI+0#Q?D#10&F=}0sS!kBV!y+~to^a~VRd1Zs z&(L7YM>9~2n`(pX&hb}w)rnI>RxVjO%eS`d6+e}N6p;&gb)UYIurbFsuF7SC zKN5ONdmC?6BV-hFsCgVZRZ?U_q@Q3Zxw3vspo-V!Lz9)@TS}c1M+$>O+mX}l2Gs+# z6ssQSM5NGpQ90GF+Da=TV529As?WAEh1sT|8V-VJ4!S}c_r9~c)~zIHNCKog1G_Ae z3Nd;R*;O;iiioVfCm3dFrAuLi5i~i9lkSE)KF1x{0a{(&#-)LT;}^Aeb-+T^Vza zmBZ-l5t4m)Ky)jO?ZKUOUPOvG?c)iMl5nV2K~~AO<%7ISn;umZLszt)nmL?^({$qU z_ThF){`kA~alk0-XXkUzR>T>damij`F}(e%76P`PWaDa&1@nWm=}H{4XA4^|yBXRY z>r0O;%I$yW{;6A=lcM2&_noB;k$wtonJ1t~n6A+CdxP)0$EAeP(&h|!{^4KNG_1_E z!p0E&sjl*~1?~~VCbmOVAxeQB2T1E_@3kjDqbdtq)8|L?@XXJvImyijyxMA)jdkKInuklIC8Og6xy02B z-z0!?dT99W-deq)A7?fW#4$=}z$3A4#exM_zy08MKXYL3?xx@p-c&e$WZ(9Ko4;|( zJAUf2m22O9Z8f3~wxI7dd}ZCVdSsW*Aj>YgBRW~pt4O4KahMAkgG=le z-cmP4IixcT8BL@a`s%iJVa}^3ktHCiFmMNjw%`0<*-bB}wihp~B9t?&lT;~c>G0`# zjXLLvAmUe7WS=|OJa1!}EJMj1>#Da`@Pns{A=Tb}c8;Fd+v>UffWe9^31hb+({F~q zV1AiE{H-sQT5ezlg_T7=dA;SkuAcf|9x9`9c}Nd8Y=(e$wKsEJx2ioa*@kcWH#I#p z>-so=&N_aR_HS(;6%zwq{h_tJy(xfcOETOVd&msBL$pPc0f-S3O0zU7z(q)@Tg~LTX6hLJ+&~(I#8b!;5I2` zj$P%->7!Y&mlYfsDhX$dQYD?NB!+Cjfqv7IvCk0M`G@F&6S)h=&-8U-c{`|8qdR)Id#nGPb68p>V_E`}e^gjX<=GJ!c5URL%JE@5t5 zTEM!5SB6=89OJ2x7z|M&ry#N5oYx{A_ABO^_6?GyiX39Q z(xE_Z-3OFmi5!0R*-;QCt?aV08Wr?{bZ;S6;*W%2OTzU2ioxgngl_v=#1`f*ZqJNoDKJ@*(D@YxHa zKXGqS2>QoF8EWfg1nCh>o1O_n^V6{`=sa>Gn+UR$Ow~))&A2ER(2tng=Lo}wSRS@0 z+*E-)iY(I)d+y1K6$vpqqzKUxz=+9~BL=gbJspN`11L4Q)ck_g-7MD9OU3#z2CTyk$KJRZZ1X`|Cj) z*s#1Nu4Gx)!tcAetY`9^m_CzUvO2w6+FN4;&E-QYK7TTK&e<;q+Wa%#N+S}d@KfKb zkTgHV6SrqkmUjD|>+0|)uCMn!f4Y6qPljA|rFX4E0s8Tp>X@UZ2`T2rmBp-~=Gx&N zwGe+y&+>MUokvD~QkVwD8KsaWm7+NX(!h-0nz)a^sqM z(URWy`YUSr_{&=ddd9t6>XuuELY)Z;)0AvjQPw5GtOAWq@42>kDB%NfoMiRJ>{5$T z`!*{5+MUJuk?}2K=s0g)Q|ZeV)gnv!NxjGZ)u%DdW|5b=NR*&5?gbx?^mz!AY8V%0zk80bWi zAj=;Pa69GzU7E!>PMk}tmK2?al}l&90o3^(szwB)9K}xm|Dwlx%@;v`6L>%oF0$Tpj8k>VaW;=vBus4bEUNK@Zr< zYd2m6*zL9YvUpiPXJQV9i!^!(mvA9mTEKe4VCLG;>j&MhnI5K-y2!3-qH_e4SqGtx zmt~m1%X-oox*q#%6PhRN4zr~D$U1>Ou=N;XLU3aBYUoBsh=~Ix#DffY*dRee4r5xP z#~efPK=69`_(k|kgfoX>o{)-K^Zc6nwpx0Z8FJ%K7Ic0TNLCU|sEQ1tE0>ObLyO=Q ztrT6)x7s8GCnQ#rmE6t~J<@kyRrT*MoG5+O#)gw!ekOthJdp*7Siy#whQ!C9n9*P( zLdgq<>pfgCgxC{Qm7ekA0OnWdVp$?@+fb_Zw9kR#WF^5dw%9_9Ia$OJMYr~9m{AeG z^J}gm_>Kw8PML7_OV3sG{L#B=6+^@ z_9Oy-Ohj>(suq;NcOt7S@A)x<~X)?R>3UUbY@=)kPhJ z60E2Sx!10$AM@k6+E7wi|KL4kPDSCCK$KfcKkdEarnOZkZ7E8Y`RB>$nmiN4rr;7x zs8uGa2(3b9SH00dEl9v$yR{5yuG8)J>TF!1C$~Y_QXEsJ zr}tIvhc?yX6gnyO#%;=(c6h_8dU;Uhw?ESs{+wU>QRtL5<<1nSJAg;7skMZzfPQxE zRT>KA|JhGW{f`e6C&ZQtQe@(aPdVb5>3Yb~oR;6*V2IC($;ng*g@FFyTc#d7anq*D zUVQYS`_EtcBH^17hxYB<{^%DMuDSWPn>Ss#e7?4^d~sdH5}KSg^Om(!_irDy{4}up z+VgHGTK=CuT?<3o504&*)50|1G%(Hh=$ZQ2BUgkbO2eU3)oE*cS|&DG^QY=+3IpZ# z)iQ4$UGLyf!v5B_hTBz10@Vtj4IT}?k-y> zc#saZUM25=dVEj45sVKudhE}hD^BL{IK2LfdPW;Eh*al4>euRL4;Ssic{T3fR8sxn z*S1wZhQ*01>o3;lYol4gX$DHvCxtq5Lk}X^pS`JG@b}!oDv^rCuNmmL(qUq*UbLP1 z>sHn9N4cuj>5-*BOHRz8t5s60Tz&cI#ph+Btt(+br)2$#sjqCSG@>j;!H z!p!;^Qc{5vJadZ#!)|5nvyj{gLtUh21#_#)XgX9F%po$NJb}fm{Lz`vY9=z~VHm~} z5UGSI-Il~Kl5i+mg;4s~i6?Abup@_YMsD6M1qnfhkUNZNl8CFO86x$pUWvJHUNg#- z6+IG6ByFEyG444Onwx>(*>Z3c%t;5~Oqhie;+5+OoA?idfYKyEx+{#8Y#0Q=E0;`t z@ws}ZEQl{L(;(}HD@JOiQ#winwGKnP;<8$x;e%kSJjqH&dsd4E@n}969}!{h#&&BP zRhTthh6vU<9pBVus<3QP1q5Yx-qe-t+s`6SoNrw__1^31Rs|GAUcbCPeusx1pn(Ze zs!L)^TU1J|rp>U(r4*-P>sBEO$g9WpjNU0TeSQ=NGl8ET6E^y7HH&EJt5?*_jjSzt zFz-B4vys>}?^2Ub?yK6sbNP~5y`naz*R~HMnvs5IcQpyKFM<03O2j~Iq2vqCRbp`q zD9yEQTwT+@IKWnwp)Fd;dT3|eA7_ds%M^~opMSQj>rQcMBD;1 z>259JwAC&|mA~g`Jw)K7ti=pR%W^Hy|!uz`0S*AeoF<- zhfY>YAXT2UYG*4A<~Q0~Y(OqU_Bq60;v_9PWCG-J&z@!`Zn?6u;&b;faLAB4`I(!F z&efC!GswazT|TsFnZod5;+n;)P-;OrZx`a0$P8{C9<)P1>m|nPV`Q58mEM7RjK4q}$Xf-fR}-r18!6HA`&O-dfk1W~`0XJGR?liJYv?oS+RQ{I<`2uO|PY zV57mcnPprs_jZV=sq4OSxpBr-miA8rKY#F8?dWMWX@)o-Z{xhq!By#yp5!8yAfMa9v8-J2=<$E!${OrU#bxDf z=%mzPq1}FquG=|vw#E)wI;Ayjl~5(qpriOVPt|WH*z^NGaeX~$s5{NWdd8iP-c_&v zyXx|?^#@PXDo;wMFT#)>9z}hAYe##9dFzBRb5uvig#H$r+DIjDx?D8O$jHqP<*)u}Eir zDwE};AW8tc3~>VkijARoo{b1Zi9DUKw&XAyV)UrqGz^F6NuX41w4WhwYhN#6u82)@ z7`l@Y2^28?NZ9W?Uhi_cVRah<#^EA{ju5*uL&zE`AM$inhi8mgxU^DI61L;H3ul6YFMt>)qej{UD(1o7`)|p(}HZPhh5=3u%To!|D}WsQ8!gL&Bv6tREd-CyjfKUgot$U%h&*EY4Ii zx8`)&FyxV*km`pL83`tI_QTE)Ot8UFbJ97y7*4PsrsMn&L#dva2_ul)FbqXhh~co; zi8#YlTw)j{NJOOXlbg-qkSC~nui7Y!sDAPy%oAtZV_D-DY6cU69VkLcV9UnCUeB_^ zAK89_L@**qVBJg@2tr-~E{!v6^^%}=2TH`FXT;npS=#toPe`q;5c$CI3cJm>iD1Z< z`{Cc&ex{~G4S)98dVnjlf&`Qas*aqlry&w31LoY#PEf9>`uZ#C7OL%sN2U{`nyuZs zrkL|-IrL}t*Kg^h6jK??;d!-E^VMxdG`j$HuWVs)^nZGDt43IX6`^oJ`@&K!Azw zv}qd?=tQs)q2DYEaDL|5qR4gb;(&A_bx3`F@wu9=v}qVePuE+tssXfV58t4c5!+Y!@z9`dPoAw-hX+wTtX)=1Y45qV_%SetvjoQt z{`1dPDY{WrlrY^%A5Grc-B@asa=@kzbNUoV4cA<&tv*CQg%Yi5t8T#|LnzcPUb_5! z?|kc5zWBM5N5%`!m+&UT@ngrHeDEuWU)=i-e(fiB9ACQYXtj{^9`;%5k;v*5IJD^* z4xXs7?4GM@xBnY zdJ!QF8a8B+i>wa$?c=u^f#D`Pf9|GQy^^5n?N?P_R9$zwNKKXh##>5fF54`VG3RoK zo@n4n(z!_She6k2kmbPJuBx{L>TGopE_v7Z`>w0YP;pZH>R@%bZ84hujqNU~FFjYe zI-E~FRz=fkJGA}dZ%^IR#&HhQ|A$Z1tM2tYj*ThI!Hy%f7{m>@>hij-^H4uS?`ls~ ze`j}H`#Kq#-_?doX+_Rxa$GOHq-+^te&ao@hFd>?SuVTk;g@f(XZn(5vX3rJ4>mqv z&^<;(0W(sl6N&5Fl9o|}9#yB#F|#hMIBEcAT($rD^VJojP^A|ogP9dN4QIXnGd68L zP|X7{s88Z)BpviH3ZX|9Bp%x{wfA`I4_g3Gn%k0@06;hqsaQ!3B<<7~xls6~2z)~i zvyv`fRO@$YAe5NHT*}8AAZn}VaY@3KdtoAo!PS(j29LwA<)x?+t}LwRtS?fnR!&neOv5leM7Mq+2J{89 ztzN;*)hn2(CzOe@&x(uTxr9r2SzKDcdecG2kw=hzc$ny(W_DE>kX|)*A02({cJ5Aa z=714QNcF?HwZYsBNc&`d=RUJ|e z`~mzDl+-C}8Zg)sEKgci=n@W-71@5u;HK-Y-Z{N?=cAuJeQNCMCA`TnefG?O=f3me znI+fX{gZ2!)%Bo}=i1R=(lWK5Anh8)$)($mk}xxSt_!}j*8>Mc2O21AA$o-?<0 z`RLkoQ9CqL;ddy_3QlafgBU1$M_YU`1Q;)XKMv_kx3Hys`q1HZ?Pp?S@Uv z(U5B&sFFZenblXc+pOp@cs;bUbUHgk{Z zK%7)4{5QAp%do`?B8wTX9hNim>OQOm<25^uDSl2$5;WVt=h|9R`101eHH;FV{EJ)a zC!(A_JoaVo%6e&(Lx{r9K3hMlU^4>hC$p4;;l$bcz$LnL#4+5ks)l%DcU5ddIXPA1 z5eD1Rh4r8%{*1!uJfj3fF`*Q;;g18h-N#BBJ;Wbu_nt!a-i z#Hof=ROq1fZffsjREj)kpQ-?=Zn&cK#Cd`{j*L!C9gagdYF%rmr>nsusu`4MkkE<@ zd^wa`g&OCEBAtCSvl6%SneQNpiNi<@6%I9bpoBP)MZ6F0Q59tM3Wmf$8}7yLP-C+$ zc=a!FPhd#g*Y0ZUke+EYK%C%wrMQshS<#s5tV?bBB9Wf@ zW${XTu8vs;j}F@MOW;ggq@fjyUq=6|4h)sgG&DEmLOO@IP%gSH+J?WeFo48#TLp_zSwriga7i@TeJ-`*7b}F{mW3+j zObnvI*25`jQVC5`e1ebcs-LV;fJpS`Fspuorx;2!7PafzjY!3FsIm}a7lfYw?mKJ6 z2hat7Sxrk3iqd{vOvp%86xuLEBnyl2fzDJ{NsS{y8>B2L8ajn=^6ZE+I`Jfat>u#D zVb;z_DcxJvjl8lCQ6lpzp(*L6t;) zqb~Pa>`u_RQ%mM!tCy8>MbLzP`S!BKIPsg>GkxZMpUrhKub#n7$WDr8MD9^2p*SDA zyY48g>DGc;qrCd^(VKwra5b}z<7exoD%>!u(Eudm;*rQq505F?JKFskb$GHScup}a zm|sK;Hbr>;PFMd`zW3BSe)6xq?dJ8`m@6*rN?IG!Y#gTf>1tfjPwgwIw8kv-V2;RJ z*4FKfxd1V!8sb`}ecyii#=2)V4H#T;uBk-}>RzER5bT#BS)AIfKIB{5i=qSJVNCpk z_m)e#g}jR@MAv3y>9I%TSgt!x=E{Zt{DY+vClhDSjfg9jjQTEH94qT>wW~U%cKP42 zzFvgabL)a__XyL#PuJZ??;Jh;oU4*TrokbXvQc%<)%9x-ajGyVh<&;dYKc2^;ggc4 zBx8qOG1srk@-<@a7!b=w(?M|oDUJBE&5t^U$mD+{>&on7Uj z3S@5Kyn3NK2O{;v-e$LTefEl==X44vL04T>Cnsv$xpPA`34)>DwPEzyZ{5_5S5%Qj zAf&$jeAPVEDQkC2;fb~sWcAWf0_B07#SrE{dZM~G%z&Y)u1USCy?p&Z&SMsU#=||w z3>m;5Wr;0WDGpg`V_j{tD1nD&3^uv(M7n1Mh{mJK7uQg7<+56x3|s6%YOfL}YDv@p zEhbDB9^+9r0db={e`otjaM~>uf3%jHts`lB`jciN-r7@W z-$7wYy7@r6O{|pyI#2gJ=sMwZ&%U%W6^IS4xU6nhm*}M)is%rG`$P149%AM~Rg^fn zsji{UAyUSUunjqTd0!Ju_;4Ywf}SOTv)9jGEnY(h<}Usvyq@u6wSaZ070$#Da&dOS zSvAa|d2WkyEpq6Zw|F%JT{;II#n^IO;*H*iWsKb}tSs!+sOAto2yhCf>`Pd+v}iFNZbXAP^{HCq4^T3P5cFJIiOx5rPEBDMPakC$50JePy{ zn`()|EVWQ2i)sGz(=>oWQyqG8E|`na-?_hTB3QC;IpUFbKO%(Qhtm0y#h7pt(Wf2!TGRL}J^_keH8m>K(_lXZS2^3%7h zfUOv%;mccVUW%fs18ion+<0VHU4{gu1L;(|#L2Vu%fU^nr|!MB-uj$rJY2u39;oD@ zd0p1Ih}d((^d6hpf@)N7N*qEww^#@PXTHe_syT0@3fAZ|+rz-mrW@6sFYi@n# zFaN@QANlwH&+q^Ef4=EMfA_8r|J_gghyTIK_f}%nhD|qYTzmb^Z@uQmx7>aEtW$^vw66WfOC+t=UYpFJ?fm(=t>ZWDo%+WQR5Om0b}R9__t$;9 zX_+oSJ?+**qw6cGKHk22c>DVLc%KVL+Qn6!RU2#WzN*+$0X~= ziDsQMdM90LHs#>rpiT)H2-U%u$A<&-J?;75*v;$Y?-=!-PF_Vj8{+6R{f-P zCJ-t6oNFL^&B}ThKdV=~!i=H0Tpk+uVi)x14jl49XG{6puBzLSugw%{0(fK*XNwzYA+U5&d;QtqejK^<1SroRs>S{4D%zaDlP&z!( zBbU(#Dvm?8&|{)x4^%UL-*t6@>g5UT9bWkPL*(eGy0NILwEcpGxobXqe(F8rHjkoL zmn)InY{>#=vtFLih+(kbb8PBxtNa`WVt}OAuc&cf+soZyDhHUz`Re>Z(Z^S)eG(`f z08R9Y_MU{`{8YU&G!=rg7gky&96mK#MVhcOJe+x7RUTZZwmKg2lE?)Mef26Lac{x| z%%tQ{@so&b-v?@`moDg80p_%dU?@8ibLV#CNz&kD`MXa0Y;z#La=o^gDAe}D_00Hd zW#u*Z_O-=Dp3pZZE5*G9DD-yW6{s%Z=6b3aZ57U>7T5blL=D5{bEi zQP_LDK3azdr}+E1wlc+GhCocC66!Ryb+MSR=WPlOipHfX;KrI;hF%KWGba1m{Wx4n zkvYI&9`Zb|+(Nh46de{%TUZHmJdftsl1K$+u2S=yJ)?CNY_jgVspz$O)m&P>q<%k7 zZ9vzLA(cZ!-8xj{7mn2GA!p*`&u%IWd_Zxi;Hu*6x%#4(a;1PdHF3uD?BsMU921v& zOMB~Wv;qBp^R2~p;}x|xg=0N>;&~Lx$~ko?p%ktHY!dbxSJrJ%EEaQnkBkauH=khp z#JwdR5p+sUv|Aeh1%pVvT5_9t^5h zkN&&=)2III4=;U-a1I>VzjNpAeLD|NZ(O%({<_;QUvtg=`5WGL(}vSaue#>WpZMvY z|FyS$=wmD1{?Ye-jHjtHUlU*D2-pKYwd|1&;;_>1U3JdARFTf`_5=M|#7G z`qT-dloO$6`=MId!3i`gEdIOOJE~axuf968ZbaPj%gx z<2m<$^IY0^dG&G}@F$msyl_yt0{n_>PqRMcnFCWFxUM>@-~Py`gQ}WrRcEg_C|un= zcxamP`V3IGjR(0%lvkyg1|AsNZh4 z;pC9ijqOu2>R%}&mM$!QE~&t;ZmTpNbRu1COr)gtsm8ag9nq7OQqu{Mk`^ewaJX@_ zj@LHvf-0ahhfyv~ZLN!4R;yPaAs`Fr*2pUNtVVkNBZt4hMd4-@1mBbdC;mNEetIy$qjy{2Ssz z8Vt&LlJ0i1dPQO0yU|4w6i$d^gR3rY z2eYAGUVdWq5(MaiK(umskSAJi+ zxd5{Yp;qPNL@$S%DrtgLNw97n1r963pQ?vGZeLsVrXU={&xHNgpRYuJ*-iADhNXIc z_x|cg`BsPR+d46df|KCswjiZM+=!rgo#N9R4Vw?t^Ek=+jeD!k`S9_(>Q~}-ZLC+% zWI{-lT!q@WvfhQPDyl^7{v(M#X{p-`=T-U-9~h(^7-=@)FinrDc-VX z-YskAA3DA8%(AOryzG{re)n58+<3><%iev>yMO8DfB9n@-t+6%-2Q=o=R-fa{H-5e zx9YMpQ;RQOx$?xZBj?Xej|{vHuyEe_E8q3Ej!j*zI9d_7$`nxhF*<0p2F)k-RAF5g zY1QC*aTtWyuQ=&^TCUpU`1U9PK(eacw%bc-kjRBr9n_UIEnmE#Cj9B8sRcY=daj-n zb|vb5f2TbU{=ZLMV9=Y{M%PjU-#|&1E-D6B z6Y)R#Zb_+x54WEa5cA?cM51ww{%l;;tEV_P9qQunZD|AU!@H_0ldxg_=^LxCn01M| zAZ&D|9x&Xns>)734XiW$m(SE#nN>E>^&v7ra8p~ax2<1ZYu{qmaIzAG*p!I5+t$=! z21hlo@2zKz+ixdwF+(?&z%W|?Rm9cMBMkj;oG;k!*YbB_M( z=?W6mC%WO4ClNtZAxBSFkS0orC*4rkQYc8Z+s79D(&fpRfnazb{a5}1=ap>vq-JVRvlDj3QpF~K}>*b%`l zGSoBS&jh;E1W2Wpf;lvF443LU=$TpMIdJiu(3!IcICn`e9(u3 z=1`z!bnSQ7URMqUpbG1na)?>YUR)Ihs>H-Sd_)mwR;WEV z9h5LFy?^`&RrZj$Sr!IF8%jKW~Qfr<$PnqAN?rr zzkXEz&Ilqntim>n`4T0tQX*O6+*tP(DL6JKi!8c%0-pH5?Vo8kVa0)%%ZIv5D)t?( z8R_49cePLabsLd@?_kv-J*IF~5A6im(_l}~oaE-#Bv4eK`D*31dp+3waGMP(vIa?T z|M7YNM|I(0>t{FBJ!0kF__Z=|{{7b#QG!}Q`GYUigELRH_uz0ow&7t)PcjXV0Aa^gsXa|NNi*w`WhcQ*{X!f$1|RcRu&j<_ACb+(Vx|yXUEs z=NH~^<-*&qS)tkwo}8ziRjgC&FwNk~W%CZ7U9$d)%hq49?ELDR|G_Wav*fyW-~Rqz z*zn$8|LMQ+x39YC?hk(OLkkuzShi^1(G#bp&y2r_@mGY?r%wDAfA<&Oddm&Bv{GnJ z8qXhpw|;EM?T5N7ji<_$+;Ka9`&D&6vZjl!CI8}Ess?fe(W8Y|Khv5$=6rfWLYu_k zs=+MFg^!Yc_^$PJ$)~B)0P53~s85rdi^A1+<+6Gi69!65w(+Aoq(5z7wo0L(LWb+C1zfA&;;z?YutURpQK)njKy zi&;dfi*BPB;NfA$6n2eD{KxRft}4C+L&Wif-(K&^0i}4$+FBeeZBtWMudGtnIU4+g z7Ke44|Lb=~o=0I7unoQjXgu_bF`n9A3^}c-TSfsVIts*%zIw$Li;?==v!iP18XP{n zb2QimnAP(?dAJq6f0mwLv1K z$M>{{$6Mt1VI7E1ki1HaP$_R!$e{r7Kn}lz=rF2? z*>64gQZE0cvkhYxNC*hIU!6Y?ZQLL(@(^^|OWP8HZ9U{(q&C1%0f;e*z@2Z=XaOO!F4DY>m?C|Q*U1{IiQC|&DJCRl8eX(Lt`|Gb5 zeWD21qFi8&)0E&#TTB0@D=SdOuwg|l7Nb+xYu*${ERId68p>DO!!j6TF-Kw87^Rly zjG9*vk%fq2lcpJe7-$$VyLQGakDq+Jc#7WYKXrZ0k6l%6g^RhHy1r0Prnw9=fZ zOit_}4&yPyWV)W0X|r4vqEHg{S&g#Q{JG8X_>kN>JXy2ER2p}F=l*)#Z;<&($)2nU zPhM&4@P;euCMj!_p^_G`;H6-LPua_3wE9``)|m zoxgO&y?j#vm zF0r;G3euXNLp)mM*S1vyt84RQJvLSRZ9yNj!@8_mp7Zr>#Zwp8$r>E?~cP|U0({$phTqj zM$nL&k{WsQn*=&-XV2B11InJ$ls7rZRh+yHEw-FB%@Vj;=kc0a)+9&-FnoDyeF(D3 z(hQw%cNI|;NaTVA zji%}#zwM!m3S>pIJqkUs6)9Vtg~m23y3Qbi5gC5Ishbd^=Gg*=JXAo$yEcp%Ivi3W zH)%RmNN0#Dh?5g=HV>VuAI4-&qdwBj$MP#DGt%4)ZyF=LxeIbKXiJ^bs@Hn zn3!OjyExA51O;YwbpjW|6I>+Qj|>;@`6c`aaA^VS5?(P5pQ-`)hl*Ldi65lnnCOTn z&<=L~MYc{$M#e;2!>U{@++^#U`S;xzlMFN^&=%j=>a3B6B{F&9z zkjR<%GxVw_UN}-iz9?rRD|WL#*feD8gRv}!(ADPU0+B-Gu;`?-p=S%wF*xkv9SMWJ zySLuJ3K54yX)p}7q9O53swmiXs17bPcZ#}5^*4cZJpZHS}jfBb8(kXpo+cp4p# zeTqh^Zu@F4a{9TO%EW>NWul7TV)U#;-4%$V?8 zb9o7xa3b2wD*smdN`2MjfqG(&xB}wN9S8B*As+}*NY>t*L2jhvW)Z4R>-cwgJbbJD zcv;F}JU8v+k1REy;51-}DmppvTVEPg0-nrcYw@coBy#oTBgdpa`FP!;$V0Q!G@H!a za7Kz|X90-lRic3{S!bq4_gGaC+NzTxuzT|C)c@{nW&L;VuUS3Hbh~NG_3|IStLZmu zT~n%Ts!oN_P-m(osxsS&tJ*M`KTzk?fyUz`qCm=mlXX%!bJp0hmy7w{YiplM$oay< zpYQEIKmF+c`j7r!zj@@)c=~<`uLkF*&z?SZXy5*APwaT=frmc*zdrDpe|7q~FMs2) zZMR;vY{#y>JNFzoa(?+kyB9f?PQepGe;^j<}K zOS1&IU|2EK{hcrkgF-zzEV5X9sTA9Ykfbj!r6JG`u1FUcmxC3 z&`n%X@4cqhFH{tUP3;9nG`RZCogWeXArcI}x~+1Vj)&5fXq_H@{Y&$oP1Ob85k6=wbZig*4xkj=rX-c4XQn~il{YT&d34iUQ&?5qY~8l5ZdFE)w^cI+ z-}cp1nuGc6?5G6iv`$0=HU(wKe#enop3Vboq2$eFe5RQyBE{1j;9=f@2y*Wk z-(FO1jGsDGqB<81yh4$s|Br|GCAxBZkWgJ*l0_8_A8Plob3<0P6d~rIA7&%6Zh47l zBx<1-ZENpCa2R?} zcY%#4osJ46j?faXtUB#H9tS zOPC9@c6nkKbHxi`D01|4jd}f9u#3dY_M3Cy(5V{sX8JsLzteLi@&>DZcxt)|Dq0qBHlztQv+GnhtaJT`Gxw2uaV>EOHuV*;fGFqN11($X+qyEsuhX z{+vSMq$tTgf_-xs3gwVECWncT0zCa(hk@x#Cm`;1{t!WYGmO$i-f>16C?qI-;%wbK zFIqxzh~dHZmLe4CD35z`@h~J*RXIO>fVBX1Y*Zam zvhnf~L3S9syN}j#m4IF_3Zkx>rL=0S;J8Vl5ZQZt^d#UPK2i5iNkEDtowBe?CqZ)q zQt;Vl>#7vca~_@NJ@7?o4pwfh)UMWQz@ygYd2K<2=MnAJt zo`_$%;#axz>PP#kt`^R^eCqWpEtt-24uDgxMzw5FO%=#8of(KdKdW*?@Z)DR6s?*=tLg5)(mjUTS@(owbzCSwN}#LE74!GWM#r3Hm7~m}W^tZzIGn zZ(9*$mGezGf1>B_yuBXJAugRj|JEz3-=l$t6joWrJ|8(%={ilVZt?&6-KFR4SCzPg z`pH8~t$Ie~x^sQKqL@E4pra(qhCh?QIM6G{rgAW#JiN1Byuu%+KqW+{O(JY{cyj8% zv2$mi`qTgFzx+=R?%Q2jF5xx8`E%z^9Xq(~d*9vq$mhQPXaDqx&;0+s^e6wz?q?p@ z`{b7&cxuPiqf4({^y0abhfW_qa{1!vNA@nQQ&~%{^E8s0Y`-`U9@@L)J-4h}cl}%c zy^sTDs!o(F5nE&z?W`qj*o*$&)AFecwlq&tH|6qeIXjH6D#tIt{ND zM_SWLEB)s7Vz}b6TJh0P5!2+VZI74BU9hfGqx^;yt!199PKh4fhye~W;+A&Tl}4UR zn<~m5-!r=2r4N(3Y}E7cBrPAEcva?!S}w^of``L8fpiG6z;yuxIYt9QTS2<)y{tW2fs!3m3aw`y~sjPu_GzUAao6 z2-OF+&M-RTxj0Skqr2<2uhbRmx}8)b*}r_IJi)2#WDQSe&Xttjg9h84WA%s}C7>%a zj;P!GC|JaH=MubVH9Y8g!r!-O zv&o8JD0$FNfFyv4;K*)kR4x#uJAJk&6S?XkO(KWbBy3lY?}4(VDw0kDFxxcUyu&mO zYXFHe>$^0A+j(e!?pvoz^hHT@iEv6EYL_I$=uq1PqA=n~bWYVGz(6rr$BL}v_5^72 zbNqzg74}3I?3g1fNQ=FAd~I8GdUGH}o4~N_Gx3TX=y-Kfmv9NM0+$xBE@3Y8&S==v z&9Qm~!~SdWf>~CtCOXZThZ#9uDWYfT_kU$?hzUa%LSMZKe=qE=qji{VCN4y@%`9L) z9HxgnGl3I&&4l?SJuSk%kcJ^WL0hLJAy9bFwIgQ|XKWfjc83_v5*hCuik|ao67lQS zF>S;;rWtArCAQCR87=yl68!3&qw+qzznOrxg2Qcv zGD|T70Y=MidtL74{gq3q@wPf?%TI`TEk#U!kw>lKg&WsE0DY(G?s`)P6*KGAL}F)KD@ zO@o0DF{DM&8FT;5*GdUzu~__KxSvTk9MA zG?+!upYc-lPu{=vp-(^c z{F{IF)qnF(zxUARU)=M;k>|d>`H6@2Jp0J>-ltwTv-*4c&s{eE)QcME^yzPHJNxbJ z(?_P4zULixF2DIFHooFbcyL?(C@pcCcUyWZ?ZCoKeDU%bG<+{^;Q~cw;ZVD*JyLqn;1Kk zHdogVpbKfT#3Pr6&plgDa#2oglwdyJntCWW+3nXgmk@UxnR@OM<|M zPd-*`tCx-5HxyeGO6!`8Stts{AKi}h`W00(#A#OWcea%&dh8LMN$JrN%*oQJ(op#0 z%4O&`KUhS9oLRY~EmG{Lo6-Kw({&rj+U3<(=u-Ft%tf;+{pW8R`7D=gUhpJK;72*% zsKN{k8MtNTeC1{BDClHWBQ~{U|H9EKY#)dN^&_0vb*!Y)q2a`kzW2L(>*2DDGAO`0 zo!{JEoVK(XrjxsFc@3|j1PoDYxcRwOjiSD9v=u1`=q>Y2&1jB25&~j)P3APTOCm=xz1%I~3_LLMo zkb9kQVvEA;fn)bKPxg*~DU`*HL~GJ&QX+9k%!Lh)tAPF-hS@%!2n;dii0o+JCPM5K z`y1Lfd^)Eqi;3iRViFw}I_!yfnANL5)-ZRN&IyJ3lID_;Y#{Bjjci0vQVA32t5;E; zeLqYZB{RO)#GL!Gm(UJV6@_0y58|37>G0j}9~-iN(s`d~UReJ}t9 zo#>6jB9dT(KvJS4s*n`r$ckiJq--a?*Ecr_6Q}s*#!`}-Cy#G(pDWt2qgcMSWlFLo z*|H_ENF>z=5&#Pd0SGgLHUmt-^j_}oUH|VaY<_3vU{Ki4|Jk!=?RULx)%~t@_TFFj zYAVLErs4cn;pMrsb@GQJVVVz<&EVS{GaFdnTX@+M(5pCYjj0;DG~u3gvIh6>v(sCl zk~K{M&qbe#C>e9(tl~p+V?WdD)|H4vyqmu=1)X13zjp>*n?$U4igOnnBAoOLy?uCR z1)ag^um1_l@N~7o?8Xp^GgTF7wU2|k487}jj>7E+Mho2#dze)(%C%@lJS4{D+t3`% z39tfbniGWK8D;@Nk!a6#q$X49=ydLBbmpXwpQ_tZeayRMg+E6e2;KRM+snl0@t?LU z!JI5*5%nhEj(FUtqis^S{OyNFf3^ku$&wIO>Z_5B)cU0pyAIXg{Ka`3a7LG)C!WV8 zM{cVzanYiQ*|TePR6e&qYb%|g{HC>KC4cG_?nP29e|mItyb=zqHS zD@tx#U96*1OV52fMn20*r@gB!0{MV~EoG6$Q-x|fj9P!eoNA6d`-3Pexipl~R-a5H ztuh&f?4&ppjVEl=?5vWnURiH$R>9Ng?csue2_(oG;Qsjpz^adpRxAT{)wB`l>;eh{Ks}zlTGcZMm0vzS#Ih_GfB&J z%$Zdy-d=U3r>zo+a8DJ{gD4@_0kNel+@Rm;g;6>K1xbxtEN15v zbH~Anhj*1u6^KZ%JZukH&@bHuC7iRuk3SMyzxt=Y`ak@~TmRybk@*>XQ*ip!$rDEo z9zT5G*r9z#4?e&1slV8E|0lovC%^aj=YRJv{_J<|`Q*QP@K1jCfj|2A!+-Q^PyE@h zKk%7<^~j(7?!?wlec@BT_0;{p_t+o)ldt~4uWbK|uk72uf7ZmTZ}d0I&YV7Z-PKpF zxaKWic%~i=)A#fu|4AE(AG>Gb`z~pdd>alrl{q%Eu^Gnbur^MQ%>Xa7krGv)m*R}t z^y(k={m4{Q2V3wLk9mCruRw z{05z|`Tgw~u2ZLLy3WuBHy~^`tQq|R3?DLe+=$f~tFT5jL?I$=s{y=3xH1`5J z(}vlcf9WMA z3g-rqQ)imFqqRvCS%TX4_a7@0RZ&Okh3MR0svzwKS4C)dG|r?>K$=r>P5XeyWf+Jb zJz0C5)n09{OF8wFk=&f!-~a5X zkt&xAx%)q`v)E7`ZFg0i7*ZU-wryX%cd5gm?>3MIIq zEDXhyFu1&QMLm9a@c3xUOb^e`t;k9-AKY0xJ}BWQm~#?&9?%g=5&|9xL15l(2gEL} z-WW<%gg;r~{PdRELrgdwe$Z&cZ>mD~VTce_SLj`NrQ4s!0#k>qm4Ec2fU3!S_ zOptV^46}k15QgV5B`)iCODBJxdn(5K>=^1iE7ZK%=|wT7Vd&mi)-<-xA7jM_ojE@t zdj8lt`Rv}yb7{Dp_v-FmO)cFFzHyk@!1~_8xBt-M;klgb^szZ2=@8E)8;l9&GJh!= z0#R1(8Bcuuwp0+o{xEYW?&3qZbpf`_vtz&yLua7hJDq!CDq=keQHwYLIw#`Qt6oS8 zl9IsS923*4b&m6bj3gj}od-m;!^^~BD5Aq&EpGo9hA}}`C>G2IvmyoIVN~#uRfjA@ zx#3AV3d8R*F&T6v5|-BePo^P8BtdN#cO=5d=LVPt1qr`UUM_9)xO7NVAln-H-tBF> zWO8c^;1Z0U1Zw$Hr07rx^`=WF=C!>WJdu?Z=Zm>LRpbz^Vc64tyE4-`f^`iaGk6 zdGn!HW9sDOnGuf_!k<4?iyS(uA=>^I{@Lx<2IyC0J#fSF`o_7eR8bYOIb-=jn?yO; zMu%KPlzI_na_5U7M{k;Wb1K0~I6)KKb!EBXGEGKd8JTK&Tx;XDTnwbEjIA@**X!HsMJ_FFWhvm3 zdn=CTSl++LqBlA%EJf9rU%zqmfCC~C3MDk+b8UTMg?!b@(F1@ybO!Em7!S}1981+P zN95V95B=l+{%<_-wfoLwDLI307o0h;ch|w)Pw(9R#IsLrd1l*VJD+&ykq7R7?(r{e zdFa0FPyEH91N)90+AA8NZ`TDJ1`rN^KkId-M({xmQNuQU{ z-%PM(4;@l})7h-RjRKc|qFbixez3jMiog1B8`=^Xn9f!*Cr;J91CfaCdg=1opwFcd zhbKMUSr0dez(_ZPnXHUx=jg9bHDv?$XvGP68<7u%$$R2WYbXBn$?~77jPwVes~x2; z@2H|-zIk00Ik`j>_V}q9i8xujVa-TSo`#l-Si_u!wzEUZ@^#md+D)4?tKyvXOH13P zwp2F3k}?6-RL~|pAa3U2Y6dSua#i?&WA#**3H-{HMZ|pcWX=3^=1t7S^Xow*3R5i- zieLJhhKt%=>CxTg{3Xk)T4b5%zVFh}_8g$Ft7U0;aA)mBVq3nT>RduCsSv90Jhi`e zTNJP^CueDC_4vg5+lRNL)9w;+z@>AF zzw>a-d)0sPGmGuvuMgZbDs|D@gL3MpUTe(@k8S>RdAz+Oo<2;R!lXg1#<$G>$r3<1{^+K>3q;i)8l;OCDm;R#)LD(4V6p@VqC%lLxzZ$>aSZp+HQl3m(??LoXMDS z%_NG=RjqP8i}`8la|+~X5AMM`c2(i-ZbB4& zG8*SoA`7#;VV6tilV_^;#%>mG)atZCUgu9fUTd&bi|SAATSa4wYBjR`U@aR`ldKEJ z6MstsY{j1Xi#34l)F6BzIRL86Z>ifa#Q=W zIe+#Xtw>a-s=8`>E(B7~7@SMK)v9%@^s@&`o69is1d$aKhvkI0^FVt+FHUMZ%5Q4F zOm87k?+3NznJg5BjE?%weOny?DQmMT(X8rtT;+qq!s(ISwMw)c<@UdRXyUKEws`Vr zTNF}KE2Xxzlm=qG+?d>DwuG~Wu^6WL(iP=oq##nx^honvqrC5>RNjBzGF30>1_J5zNCZIplGx7#^trS|Jd%)gej}W@8qLQ zfFcdg9jXb0IA$Z4hUDt2)Y6l9;Lc4IMVP)a#3Zg+RUd)MK2eRb7Jjg0WEsvzePLD& zid)*uCT;4NJ-Z%pGT3WGw5gS;q z=Se^W-3?3We(RyC{M4{m&g8=uNpZfF-*&Im+bf%k~54r7H;W3&u=Khm!L!D;V;hD>t!1E+IGjE;v>g5yJ`QfO4M5iW^C!J{>UuJ)MLzyL95rr6ZtR0UzE~+X^sblX59n zc*^Iji#o%XcGMnIM7UIuS)RL@H1Vj_5$i2TjT?ed+61QbB%n{ND%7e)qwj0dMvrBT z#h4fof7T4dFPPVMQrdRX73~XU4r>@``csfNN%`iBYjvd3=FYCay`xxVMcW5%s_hSH zL`Ac@pg&d+PLzrI$){%2;nF#|&Nr=_IM4!ahJ>njT~WEx&OAAAyxxwaP`#-uB56?i)tfG) z9uWP@+DWcBl`Lk1LI=Z18-|qPC#;B$j8>ssO5MI?Q#~lB4y#bD2_D&9&wp7E#_eSb ztKJwA{lD>&8ZTAMR^ISaPGRa$7Cfx0P2`R;L#)tH&j>YE@Ui^R@cnDnOat-ndi=AS((fr$K^~ilhI~ zmikHVCCf)|R(7O0*n0crMX!Q44XVO9SrVWh=6rd3iS@eG6Gsjm{L-)f-|qSJZ=F7M zs`6$qgLC89u_K#qx$_OTzUEVpSMRl0(mz2T)1A`gB(in&{u^F+=>YgMWhq*&>4Z%i`$>0+I+&3 z{O$*}(Z;U-7&4;eI!6kmQ$I>k#5TH!=%#XKZE_3V-|I3MZJu zT#mKxHs;Ra5|MsQEOG1lqOdIOzH9<#H}_d~nTQmi;3TC))e|OP?sJJPk6?odic4;w zUu)a+F7IyhEpeA0|7!h3kS)jJc_phVw(ToDZkpz5K+M4bDCLv)%9K~zQ57Ybjq8rQ zXyStQN!C`2qo#}oh2m<}b_$Wiyn-9Jft%Z zHK&09(J`dJ*kwmta_-eNL~?^+?%3`XHX&1u{c{(v1)Y~h?P$=#yS+kss>jf@>9cHSq-98yN$8(8)_T`F<%iM+apwdA z28}awIRmy;i|UiK#G^zMizskUS)j_*_LY*LEgGOa_0YU0@fi$V!YK7v*|tY5dfc7j$c9DRZvgSh$6pQz=3UYr&$luRhz1wx+ZS{lHE2VHBK9i=pq|UOZNta&KK%6rnAk zC`%S;zz0TA3qgB$PV07YtRM1r=2UztTPdv+{z`z7-;|I0II0c@|8T(p|E=l+T@A z{%a;RrTJ7fe)YygHFW!`jvC;Pns+G|y)GXs4g1@#7OIf!onbJaT-cWSYSkhz+m33c zmi?`Fx#R<$6pC_Hry%7X`L|L{?5y4n7GU^A>xeL z+_1U5tViee12sByb$#A=T-cufO*f7S8l;^m$>kG!>v_iYOKU73{=hY(>68Y;%y?rPjN-)b_6{I!WT2!qB_?Gjy9ItSS*(UJP= zS3V9Ede~$s&P5CB0sJ>!TE*{b<8A+ZX9S}yvPEgPo1{}f3}JRz%&nDOh6wHy;uei{ z&C0q28r->p@Q`)Sf+&ZsFa?C}Mn|VZ{ERa&WOjCW@#G5%oo&d|5$3K3I)kE(F>8Xc zwG7!_1pR*+;Dm6-lYZGTb_qkDox80erc%bxmBho0ouTI)-La0Tl=DXyd?iYz!8b_l z48G$qvw`(pg7Y8GFJt7t?8)LK3A$D1sTPRfG~&aoVfw67TPSC}2b>G%!#QTl<2CVWnM&j`M|koeI)s?5BFK%|T3WmwX6g7G zSHlLK2;qeAIA_;v=n|gc0a2B7&e%k3=tNM$U^TvN?}!0Cv1|v5a4HnhlCY0av04%! zt2>ZOSpu@q@kk^~q>_jhof|!9c7Su1Nr*yBxe2dbjycSYtkuIK!9(eoW4(y#t?fEV zJFi+?Ep^q(+O0TvtX?Wa#K0f%eJ>#WDPs?mA%CO=%;7AbTLBrx95=YsH=gJ@bbR8E zwwAUJ+%&2joDokT?ouV(xTd}=E8>aKDtKGh5uqf`E0i86ebq(vSL@Y_Y9l5;L6x93 z6`M^W3xI>i>r+a^AKFR#T0QWATBus;IUm z#LT+01H;$())ui*mCM()pLT+x=cU3Xm};c6tHP|(vL5DY)yJ%f?fXaZw?(Puwj{=8 z;iTc_&lw3ugO%{rtI95&c$Ul`6)?+kP!Oe%g(z~Bj#|f?F0BnjwPew&5tWyFV9ks< zy@Fb23r+}ylV96am#(>OU7A0|6Jd*?yi53z%@hCPD<#6BO5m+(?d04RUf>S3?jxJ{K9fdPjryU^>9PiC`=;CVYpPez zZu1FbOaUJEGOKsPL>F~=OfQ&xJkS|ib<{S$NlT&O_&(|;IDOdKbzzl3Xf76<(t_5vtkCUbm4%2`2%GMmM_+aLzZEJg! z$s}yLu3J(oQM?6ZAqY`Yb8?%CGX)`nxkWuxsL*EG*|R48kNYOpE-tm)R*L(!k9JQY zf(@M#4XVPcHq3|Zq2?-Ps^v)4=ce{)f>PUhl&t)*C!C0j+EZX0P&xDG*7HIC^h;%z zIrA!HLQec3QQ9Ws5a2Be({huQZj6&)9DeP_x}WBeF~xKq$sVbycBw?f1H@+!)r7C2 z6b&a4XR6o=8xtCZlbGE^mM*Bz;rBNUV>)b|(utbB%niQbAdxf^Y;{UD^`gX)_sThd z68@Zr=#*hrPe-t{O>Uoc$V#>BI9PvZ=?G5*Kh{2H)}RIfdciczFG~Nr zv^&+%Fjr90BbBov%sGPLWra#je5^z+E2?V&O3>3`6XFc{rQ`X|_QepFf`crKsp<^Q zks6*O*d1`$!;y$|xZJqBg4!N-B*L51KKvPxuj6k|=p<;1Diq3{1)!sD9Hm&Fn`QS7D5E72P$8dQq_U9^|58$$wL>8MKRNwc$DPD+!l zFEKT*4jwDE)EGBdM2EUOQp*)~`J;?c%MeA!Pu1s#p4wj@He!OHHOA%^KvMl%;Lyr4%Fqo zWA($AZ@+v*@0Gx@fWlaO{UyauCp|@P?vEov->_1i;a17C=j2E?! z%BS`a752xkpLl53#8dlfg=3}1TrIjTocPeqRs1~nM5M|%In^i;H!N&nbD+paztTEN*bA!d%fEo?M z$q@n0dVBs@a`#pB@TO_ONV$JU?M+}#KQ|M`x6A2W-2;}4XSKR@B`P>Pt$UMX7!-?stG1A{Wp5BmGbsnxl7Q^l<}u-uAlCW&zQ@f_}O-vR99Sr6d#@PxCgPW zx)AI3)Hc8Fy0UI?vFl)k<~;F=p93f~MB$S8C9W*^wXf#DBaN***)y^Hf_f%V0$Y$T z_b0a}>5d(56+YA{Grm+LtYb^#_j2GE-^G~O!1|8Ii_VfA zFKKFc8G6otI(t#JsjALj`7{`6=n92*{qA@^CbD}4L*|~AaP%9@@EpX7b8g`<%uPHr zhM|a1!%h8MuF86P|HMCUubT~lIiUcYOBQ5_y}}vy+^E9b9dmZzgkFRm;y@^}lAA;N z;PKHT8)1lsRf}IJ&cnNEJ)dB)V!J#@GVoR+0SX57;BF`W!{37j^sps?r}JZoD(0M{ zGv_1{{5jMBwPLtX@S_NDlN*Kk(<`-{jCJ&``AmD@9MGdgad?Z9Dirk2jDk*;$bb@@ z&fR}@0YfhcL6J{nG@gb)@6b;2i(=AFb^(;%~mRevlDozVi~eRE<^(@!5IXzFJF& z@0AfaMy;&;eIAsNP7ZK2(K&HvvAfHwE~=pX*7fBHikj!AGnZ2NJUMlyo`I6PX#T{t zD~tFiPuAmFV!pE2_K#R6NGfhr#kS>W`ci7CYI5?qDwlBI_To=ERiuVe*&mUQLPqw4=Cv!k5mFdh% zDo_ueHLLV*TviX4k*%_~(Q#z&&VTw#zwouM-e1Ds7|fbA`+}v*FTMVCKk%OSoLX}6 zf>|f8f8)D<@^Ag(rSJON7hL}4@4M@V4xV1L@7XQK54UfC&EPu(r%oP!!}q;;?d3P# zx~_Vx9%VSBYrW%&_8jQbquncTw=z!kSe>wM$m+NHr-Xi(p6`MbHkdQJ=zAtVr=@!v zYxP1_dkZh4=7zP!+%G|&$4RXL7*1+``JT#pboah;&jAX>`LuW%QQD2~_VvZa6E5)|(B&pg3A$WF9dhB^iML%| z>653+jrgB%Xx^Oq*+*IUnQ$*S(%!~)nKOk%ZfwuT(I1Ej^XQOVrRdC|00)9-UcRu- zj$FpE@OK^u6II@(N}4~NEry(zE~uv<(epufhyxLchtfDxS8|V?sx7Mi<<_n$xscr} zh^>zBcVg5IUFr#tPQ`YHF+4ft_s53{yU0*h5&Grn_P%Y5I>+-d=;6Uo$q=d4F|2Gm z_(Sxfy(qL-{5enlso-+|$jSLAIjgO637)GhW-x=ZVrB#DJ08OU{|X2HE1B2emB@~n zo~r5<7HulJ!l{WdoV8kBQe9zQ*1iD&k#bJrMGFo?k$#ho;8Zt6D#DSmJHe1`jDIYv z-!I45re5XkVz=fJkL{jgdt6Wb{TgiShya;bnA5T!RPt8YR< z;vo!xQy51ZC6E9pXCl0f^4Jy4?`S`Wfa@1U3n-;-Nw4|GJH(v-C9oDM_Ia6J=6DFqPntw-4UBsf-d7Ddy7{f?x^}OL==n$r^wN$VJ4Q z%X^<0xdalJ%YjxLA{>VBqhOG5RMu5hOMF0dCSpDFftxDVvH_8yU;IpImIgd1w8R8R zBnqePgY7Lyxv4#MWr4A3QN5C>qO7m>A1{8#Eo Yae`7C`G8MZckvTC~SF~k`|s5 zq!}l>*#6+Ldfp)qwH0QSIG-)>u<_aXj#g30Vhb>5?c@^kM>bc9T&66k3Op`3{KMCc z-f;kD2cxN5Y>74Vag%x(g#Pvw_RB6DEnoGt@Hj}tB2QfIKUQay_=7p6&@|iHFL0%n zss-C7?2`EtCr=fzv!#asE3{d{xvak3h!);B(`tO6Fa>0b(T8`{=Y+UQ56TywKJ!oi zx4(PuCw}c4^s|Gr=geKU=F&I5`-4CFk-z))5B=@8zw5o1-2CHFTjW$$d=+p>03 zo5%FeeeEw#5}9xu7XNy6`nI!vYn+W&osGC#2Eh#T)IjJczx{C8;=JMFNEonXXz=aBO^d)1{@M zo8TeM%B#}M>au92))_rKkQz`GE?G}w(S`@=meHC(ZYA1Z7w){g7W4Ete&d1qwTgO0 zbTS%w=atp&nWNyc74=6c&A~({&bjlJmBF9xZf|eiWgd>_dEvS{fap*UX}&ZQaS3cG z8V9Q2d+H{en@F-yy3x|s!ixEsDb6_3y|DoiUTI$rBaZxU zE#khJ1iG_ET)SjM>4$RBBLRQ7WzW;U?Cz2N@Z?_@6z=RYM)#fu6aI#EHu`C!bA7xikI+;hoJqw|$i!c?49 z)wymt!O#I|w+$`ZUn(3*omE5U88Y{^Zjkhl(wUt*Jw%5@cX+5)V(Z@ec#a<4G2R&G9W0A zT~bLT{bx^&A|dIn9|U>g&XtQREd+^^VFMcaS=3AU(+BFaJ{V{dVv6U4L6m&;xmuYd zh;g$)Ppp=|h$fKTtuwUk5~mw;EyxT ziH0Q{K#w>z1p$Ij?i=wlgp(eUTH!72_sDVL=c>fb>#8eexs^Zgjy!Km3x#^_Q1OJH zlBne#Z&Ac3iH}`EwwWCCSp4Kg4?bsi4}@;3EcV(K5AiB#v&cydil2@b4R|EvGI!CU ziO+2-)3pp}YZ#FG%dNj%w6N`}ENN10FX^@o^{QPxj4-*1<7Z0Y60+>1nU#vm+Yc0` ztpOW7vRKC-Kd)-*#t+<7=Lm+O9Bz+YxWvXH@RRjI+h&q$QAas#&ZL5>y`%Lx5{qs8 zAKqLVs(RZe&+|81JcybVoDHb_3O=1K`Kc0k4wjDTA#%|b&{Emjn*)g14ohu>nZtKX zKC6|II`ju?26bYotXZ`S9~g>PG+6EY&ckI0bM)YGSxQ>l#Rul-l+b&T$S#=E9!R<8 z-~G-n|D6-Z+TY(_Rm_<)XVaC}zVRpi)*F82m+tzZ4?TauRclu)yl=dM8lmtMGf(;MEm?D}_eX5Ws-PtSZy{yPD)CQiThuAf-2aFIs*i5o_P z3(`fQ`v0DK>1l7C&)u87nmpG@d*$q^n2p zi*2X)f#-_3#zOmr#0wXW>~c)!ch}HaY{)Iilg;7m?^pn z+x~7*Gg#Xz+1zR)bck}7U*1tBa5_7bs3xwfitcuUZmy_rEVTPzcuXlht!A9}w0h25 zP3QWhbtiJkJ!cct9c|WPn$HUOuJ-OWPW4=qWXp0~v$BFBRYVV|9S279ZElx&GE0I3 zF|8g@=p4b_tvzZ4&RjZix*h?>PkEji1DBd9X-Jh)D@OXhON&C*!E^OcdRm~x;=lP? zJxHZ`KfR?cp<1(5J8jr#P6vz6h@4Duq?2$ccxF_&0Qo=$zo#eag+WrPB|M0c%^@pN zL6r0ai`xA)tBc~2X(4^yjO(1A{EK=B4&*8k{OOaUC%(H&(mkO{Q=+b+INNZWDw0d- zCG+d)`%h1Pjss*VOGlUOK*7*eIT%&;*{#RI@R_MD80MTilCCgdawqYuP*-wU3A&Qu zua&lvw`*Pg;%(H1>ex|aGtwYFy_YC<%>?WLxgjx$k=6nOd5t+XSLOdXU$DO zFg-|h!KvDYI;GM3bH8qQ{VqOa3q)P+j-8$aPJ!l_)Ku}Y%b+tuj}OK;SF|uLj>Ss< zj(bNg^ZRK1hK;DAL`B2!<@D)MS}x9~_Kng*!63vg*mtkOCK1gd#ZNw7A(3d~iGB5o zQ$E%klAD!RJOHY&AsN zCz6%taZ*JIUL6!xb+rXX#1moXXO%3Oq=}TFg~cx+;upLsASu>!l!tq2TjzHku0k#8i-Hi{u?S-}%zi$}Q(b3r7Ym_Yf+ z=7~RjqSm5RJ$;~_7r?BB@mOPU(uFh+RUQ4<0G(`*g5=o~V6<99XK1C46kBpN@#`-s zhS0Tgx)x4KLX3WASvRh!OXAjb^__dws8CgbZR>5{ z@i|LZUHt$2n?JVj;#;?Fef;2_XGViz2H&nYdHmSA&F|m1eoZxETdW%UkL;>Wh@$&t z#AWcJI3r@ifUalijp-7waarxrOJfM<+9jhed4o#>Her}F4cD%!%_$?oCFLkOm?Qnl zP~CZWG-N)tr5@g)R#t`{>)By(ymn(vMEj1F!p}eT!bqtdgu|n8&DEc}dE&Pos^QNK z`hV>DdPs^+2cFCTxdr_8!!=XMeRTJz)Q;4TTS@*Zqa5`7@N4SNPM@EA|Ced}%KM(H z>+XYxT9izT8aE`5 z42zk5mCkYCSpBgF<=Cl-TiOeKh|1_pIT1rKWl?XGP@bBkhi~W-MDg#O{;8dJdXCMpYPUSftqi4mr=ws-+S5wh7Oyha*qE4O*GrLzacoi|Tfi;6y z8815b!TC>=9WTn(cdy29RzXBzUwy%}-Pgx>a&Hc%@z9;P-^IgoF3vDSRSpQHM;O7v zxi47k3F2<~Ln-H6o(lqzA;Y=&hqeR|iM>MU{Nd?ENNAD>CiIAeKZ&GeojhHiX$QfF z=ria>F^C8w-b1crB{vajG;B|~9zNTf~# z+@eE?JKFjLaPmC1b{wqt@(BK?zgJ{ZEeIc z;M-5!Q0o(3{qjAvuat77Ct!wW+TR#sm)`hKoC8&j?V$;)#Jk&H<0+JjUqQ-5ThGvg zVqvhY{R9|dG#@`zUpyg24`NJj+dJ_q_tkFK_5-!3i>nT~iX+sPrrbJ{OVt*wX+E>H zD3&p-yZpJ^>p`tl0*85}N|!GvXTH*Y0Sa5T)$puofjEif&j zf5kgK@Hc+^KmMQQT>C@UZCu7^>WV)fx~UpH)wptTb&?DO&7_hQM}xweC9`I&yym07 z@S$gpEj_$*%Yps-XST7vZE@G7akk+y$9P58$XBj>per-(- z=As+c6!CQ%tI_q$@1J~%Q^#JK*W%g$N$1yj&@WzA>vIDngH(3{47aZz$ub>eWa(@l z-dr<)@%6_~)POb55SPnhW`LTA+N3A3x4jj(;(`*v!!DwG$I+AZN0A%X)TMz&?a4F6 z%t>s$2&_D{zn*xC>BH?=cupFICD`U132Hz8bbVoc&*bxH*RQEbX48uHoOLU;+W7bb zrQZz3J%Xkequv<#*gf?TdPKv-*~p@Y$mgG`=f{a-u1RC^F98M=6E5GjslIETLgnm0 z=Qg&CM;7}&msW!@a!xlEd#_aY*DnM6| zw_iSbaaC^GnqW=NtqE2wDrOW643*nLpfk0*6owr1bC^Q8F5~tnYcf{JTFuDKWm_r?OBQ_HFKeUpQsgp;AwvXnuA)um9zI_8nsmtQUIp2`3c}gXG{97I zQ-RL&6<`0o`B)!=ic9ae)I14v$NVtLTn*1s_bB)XnXi5C;qs-+((0>mThf& zg=3r~Vg-mOYJc11WkOcgU@0u|Ep0(4ad-QzKnX`Qh`c|yt&}*Vs~}>~SygYTF6VBA zhpu^|6g-h&E})IhKX`27)(sOYFR1U){PQo@a_hvYiS^6smmg`MhgV8x)xohnM!3ny6w< z!_iiA_~a7=^yEP&^;27=?UOO8E|pdtpmh%&!}^>E3O3zaSZ_f{k8WdmA6Ln$Nj=ad zy95o8^1ppGCItbdu$qE1XX;U`MCWp~AQ2|Y+g89T?Bset>B=jEbkD^5+s_dK3ZAsZ z)_t{V)mU4%Y`y3A{@KY_<7K_svu7_|b@30p|3Cb@fA4?%zPo?o=)9HBADvZ&ZKfB^ zt5;oC7$B3=MP4n58$9HE^~Scq4t?$N`Kzvd^L2N=|H5U9pWO23iK7Rbof&-FVfC6d z?|#n@?mISH+vRr;+*vgA)$LAX3~Fu*@AN~R^GiGGq20a5Y6Hm1AA`vR=iE89^^^S+ zB8c_Tz}BDV&aNIJQDeDn&SuMlETY-o0FfYzjVfK}6K#gX`o!M41ee)4+PJ(X6N49p zw;(jaV^lu8s~#ibJk5HnIq;c-#i?`V@ktYfN#nBiLw*cBvb#3v4BPB{fn|36HE32T zxvKSN;(00xF@a4=!SoMYQ|g5pN@sB3#O)WA#S! z{JAyI%&T}ZxGesQd2;V2KLmkZ_1xxpI>BWoSW>0G)wc72_EWq|=8q~?RpX)X5a=uXXyNNWSC>t5IAMALJxY%^6>{I{?aYA2~wQxPJu^n;nnf> zEkz17t^%%azvUNT%LB!f16lkoL4rSrPmU3^Q=3PHj+Md?R~VTc=@8M@9Y%fJnEvaX;s#KeGKrzo`xkG0Ym{Ohcng zuVE@W|CrRdl$g-!GZ8O_sC`MEOoNwKv>AMtVP*sCI~CuCyI0`O4P$f4t2M<$&-rT` za*jz2r@!3o#u$c3)-(*mX*7wkiFJ6HdtNR(v)kLqnRZW2){tj~$Bdd`l>=90y@L~ZCH z#*;uWT7U6w@3pO4S`=|S&0P;Fw=4i(*KzA7_4%CkdO}?;Mfp1f&jMfRx#0lcex(E*^fmgEdNI*H$)(_6gE{h2Z zup8Et$S;1Tyh>qXh!o~DO56|~T8BtGqBGcWuzrOwPc+D~_Opn}QuYtGG|slW!AVu| zxot&p$q;WD{f!4FZdzNrFjivh(tz`l|QZ#0dw`r>MERM&RG$Z-Hpbwtw`<5 z*7bRs$+w{`=GQH$FRE~Z!Uc2cgFfm|NtxK(-ribte8(UE+kbZIL|dW00xVdx{55a+ z@xSrWU;2f=_A`eUY}m55{_M_53I)IWNR^016|2b3pL)EetZKf-=799!YM?fJB#zHn zdd1CeUVh!(r;Z)k_uLbwj!zEb8GPH|z|qsMx%2(=7B1G!ZeCY^;m~k3THCrIuzCo zizi^7xxCF+m@$wgs9K0PGo*4}=LXjX@%Dpt6JObed8`X9E)-g{Db&Qp?Ypn4$u_xh zz`TdSVEouURf-39j-EVKSAc;PvMgegCKy#*Fo(H!MEYHBQ; zh=887a}ef^&%0T)r_mw)>mF6b)^{hSpK)GwoONn`B|f-!x`(~G82U4u{$7TYLsinldn8Py z3{CVKUPr&1CQVkln|OBeiKMB5LxjIGK3bNb2mxavxq`x7hwBBp-uGOJgE5QU1$OnBi5BGpLn)Em zULh)0ZYtb45uUUtxeJDeh~-eNQU+BKB^KT;g$2LgN+CCNYXI?RIO$BB3OB5sShKjE zzM&IEF2_XnWK(eI*Q_f3^0SqlKlg91zsb`^v+5$4fK{$)dHWT$TSqW86QhadFJ4x^ z3$7{h@kqX>MP>Csn%!lPij7AR90zU2C}(*FLqssy)o~aBRcU+8!clos;zm z4H)#Cix!TWj4fGs1mD-JtTeTOECtU3ve3@zffV1Ec++L|$>DON1)w*qDSxVT?U@L& ze9%dL=rtwkl11L)*Iu>#bfmVx-kFWU_S#M!n<9%}op7}@w|;KffFZ5VgiE=6Cit1H z^=t*=+&R_9)-Nq_OB(nG_Y@t^SD&j(IC8QUY&_u)Th;n)dHLXT^_To=(6-@N&a?%& zwX5ny=MSJzZkeQ9(ah&RXdm&Zg-zSbvoslLmS+qVK+X_CrV3z2)t9Uv|@*4<0`D z{4wL4(e#QLEw zVOqecw~t0^8|=n{QGnm%iCLFOuSh3xo7mFcF05g-u--_OWm2k%^hnK+Wb^W@0YbBL zJv%x133<%em@s?jZxr%GmI*T+65#`$&?OY(u|4&th(+_uVVuMPL;0|%#@&xzSLch` zQ$k<{$o@4fTTpK+FP;1|RSIa9NEtIf$4~SVr$*0e!O;_SCxSk&nr(Ft@z9vna10vDu4g6k#z_DD#)yQCGf|uukA4%0)f`J3RcRwtRnLgE-jciO>{)d){kMug&*0LNb2ugPGlhWu zvG&1x^r%X~lZ6w#Yj6x*4Wza2O>0XQ131(9o~z5@Ty{O%+KYaDtEC9HLTUwDBMS`m9 zR@b(jv&DewqKbYEI=6jtLOl@Vi2&MS06z_g&IgW0x|Foc-LdE}Jdz%jrSx;CE(s^M>+=pmM34 zQq`UX`^WdzC0TA!g8u#+YrQdVPSI0oZ@i=u7&ytC_0l_9W1(0~E^nrfotk)kW23|p zu_}L7U2>jIKqt=U^cg%DMs3s{3WNK$*Yx=2OPkxR57TU=D$Na@>M!3`O2C;OkX1{& zqty`ayKYoD>3(rT75(x(bqP3QR3fh((a$ASQN@9_(Wdz}Pksj5p&qPn`;%7D%*vy? zOOFbt5V3orwr^cuf@(g`ebC@+WqV-n?l1n{uO2({GJk?_)#^1r`wM^jlK1`6imTqZ z{=y5aBIsAiY^e;7GnA3d7Ew;FRUSoXJm;pZ-1w@R-&e7t^?QM|`Zp=HzV zSG0$iinypou8c1}T?`t%_aB`WGYz@~NDv?GTeVK^(%nI#7}|Jw_tk9&zwMH^#7sY( zCT(Q5h}4p|V&mZqm`>zvMrNxOfgT0~zQ=v5j>7z_H3;4M;^ zL+=`RrD!1rvQpSk;(#+s{t!DA;q041E+gKp1jBSNl>)I9@g&q<=;6-~3nm?6(tSq> zREW#agJC-84tFIADmTHD6)DQ5@VkgQPhw2O5pxuZp7VPm+IHx?#L&Z3es@Cx zCAp%Uw}m2(av$7TU%Fj6x#GegNYcJ9NFAv^l2dE-0^fht=;@wi z3rFvZ;b9BJ$|;MLSiZaT?;ouewGMe`S1owBE!tkbp#DC;cz(tARkZdX1280_5}cJDnf2h zyE8{i)s1T=ZfPHNE)yHeG2+%OZ`(AACDmA#YGkbtb{s5=ial`*T*dmg!ID)is9@K4q&dg;N%&ZjJjuj0ABM#U=n&+Se`B}ev z)tsIK`}xtsr?E(9AK-UiRWohvF17ie ztij8)w8IGEm{q7FdukYrrsb1w)|2AcruMe`JFckT{iOLLo2zuD;(bT!$uEoNjmv82 zDabFk-@%Z}nKxcCdj65QIB&8gf^ANFOfsG@N@pb|?{0=XW(jp%3Emi?70 z%eO-(YSQ32wZ{IL_8Ql^CGD=!ezNf64-_+RXf{zqi8J4{S8&1HiOZJP`(j5=)LkQW z2>J5^K~pjP|L&2Ahj*4I)e!AI=-|IwgS-4Mex~Y#2~}~h{q0BkkGB<%7I$M+A<{Mb zwc10>78ppdBAr7Tn^xO!MTw;Ah4F>8ujdlo zL7~vaeNb9Q5&+PU=Va9eVs^wbkxHPKSCjLzIXNbXD4jG;xM5BGiA6g5Yrws zPselz!R~F4Xw&7GCx98nS$tyM5sTr;IiX8u3sEsM+;Z8`Db77HG%C?~hRi*!sylX1 zeh&Y9(Am1qsTkY6I!{dFfUIHZrdg?Vo?)q9b#&!3n8EpAW&>*mFCXWc%Z9sGuR6{; z!MSzi8JRH*Dbk{!+-`?B*Q7Z{pZ=@);<6LRCfZ+kuO9mwjrK(Kq*P4x4SlOD=1m{(Dq zm`OT<;bCYP!BBNe`{YD)=A7KJ*kaQJYTbIz1^Sr_CFiO2NfyP*Cq+h27#Fp|%J8U3 z6y`8*SXOl#KN0g+@w6R^Ln(9#M|8R*EJ7ophjrS#Y2Izm48+&l*>wumEgkS zdDqqT-W&>8!|XmXdaKSl1cdvn5X7+WSUm`E>GF}?6s>lz+U~|b|8ntIP+1SGT2#Lu zgor1uN;~d(y8d3xfz;uu#YM49MJW%jsye5{<-P6mH(8FRp?P)ubUj?~nXM&Tbuw#0 zv@cCc= z#Q*%q^^uoUh}?7%B}Jhmt&GSql(aaa)=`tkPS$H0+(-jeJwV&R)K#pY_8)C;47Jai zTy|mmqHuHd@bQ}Qe!u+?WGXWjl~Gk>5Ygsx-~I!C^jp7j;J|@mIzP;sHS6L_FTdj_ z|C=}d^#9|^E3dlis@d5}i-*?IdOlm02O3Ml^3`;tL>BzIM2cK`ChGStpWI)93hS(Q z$>m=8cJb(Suv9pt>HzaYdzZ|=BF(!n&MPKDw39<43!2TfTf^Y&CG zx?#@Mk8G~Xr}h`!yi}i%KU%}k8XLF`^vIRgt1+8i9^O?Z^zD`H;W9&*Le87^@4BK& zic)Al2f-!CQ&9>9>Gp=R8#hqpQlV&IUGXIpM>?ZSE|HW=8SCbR3Qljku1>~~$$nLU*na&mM{X6vdKX-Qhg+PgPn2)!+#=?13Cx71dE|m)> zA{Y31VY??HmfbdoNRu5@hk9qqv0dBj!?A%T`P~ z@uR_d5DZ<1Yha(@m!1S8ER1f9!>O-ityJhIXe; zuc4gvFAV4Md5qY1ue$P~m1p)>+A-C1hpe%C-np1VPnI+fBRG{gOS?`_!nxl*!R@n` zaf}mW_4~Rs$a1hNj7Yj8Y-3Mxixk5@#+-BygT^2(ym;Bj3Wl*(pmW30iYh`+7G^+M zwy-{TCE-v^%_>1pm{Vg4mC#MP*8pb{Y@uTlS$5wd7$u>*#CG6#eg3>JhpIU3H8=eK z77^BU$8%LQ(57grH&x<=*fv& z*HyX5VgtSbLCQIx_|in=TDnj!EsIw>4%VxD|WCMv8q>*+t@j%Pjw*4aMP7aWdEux9q8Xe+(b{Lamuq z^?R-^CoRPh)#N*_sG3Ac2fME|96gH0Kv{2{88ZLyIFAUCEqdGV+Iw`?)t9cl;SF~$+<4ouU5_8yxAXL=nGa)q zYjEQ&cl;+G{?O{B=Dcd4bYQLKoW57xyHPCcv)iuEm^btUQ%60}G^^fy+jY2F->5eY zrk^+)q0w+}nv2A;i#wTt+SQm^@#; z50V>(F%9vm1HG%--S!*}=gvHY&M>6M$u1k@vJ-ojMj2u#BJD6(+~S9b+hh7+D&<`0 zZ095LqCCmP)|+X1{_F;x3qy0~U!obzU@B%du)f>yQfGu0-Mt!9IE{Yv^EaJa!Zg}) z&&!{8f+6$t#q9HJ$GMof(eIb%iLPp>^Q=S|`YF+GkfA62Cr{L$n0iTNb(fOv8X$sQ zkMqz(M=0IOkO46fM$hfh#?aPKBoO^1SOLs`{KUjHt12CBoi35HNc0}7i}q3WF;MlE z%PP^OdOBc8^pE0@n+A2}q4UM%B|$TyL&*5W?G?R2{0uW5U?`U$S{0_} zBef`BeYXC*eBJ8NQi196q-_egJbJPs>?lM})R+pSaP5-XNx7~4Ef-t9aw^Lr=kgY5 zSwuatzrGaCjS6wvuMkGORJF)HKw-`$PSz6-C6U@VcB_3{x}_yvM%x2xq^eF@k!emDAKFIDaMAgrc+B#VRb8oK&lzof=dZ`KlNAL|)a-Q;VPOz7%! z=9CFU#gP!x|M~+>ZCe|oP`LMKJv(-HtIQ9!-}^(y6OaX(6(m-6?mPO)-~30%4(@IF zFM_2Pt$p){{`0@{U;o|N7hQ4DqS@5OAIJ(Hg%5G~WP3Nnf>X!9!`%L()wpSBg5aF? zB94bw8p5O$e*UNUi0*?r$(~jgx%jVMRS&-$JT?EKjn}^QuJLK)pIO zgKsWg^QQMLdd<7`9-Wo(rTb}CJ>jEwRs!*{-PL@sWd7H^04Sl=^@o*57N2bupXP^!quUr;& zs`Mynozp~3PZSzinDgi^X5PlSv)tc&uzrsA;E5W)RSGV(GuaxFxf{0e4oNU-a#Nys z&Y4HrkK7M;lpMeow!5#Y;+Qi{n0dD@dQuf|`-X{M`)WNK<+g~2!rUgvDhnm1&39he zKFBz${-$#IvVJ@Q5;3Re*wRA^a6_%GgCdKe{J z$C28>zxtwjkm}}dZSY=PO=W5P_&Sl`SOdZQ@Q6Rmu%^8hQe$XBEIWz6(kg@ zSW)wFfQSSpg4HgK2QfxvNjpFV(i~4lz41uJ&-yBd=gqAL4^tInBc&<#v`7YA()O!g znt0vDdZvkND6x|B9nAsh>L-)?p2}O3?SP*rbh70#{2iw!zpsQD@~b%5T;>2X>*155 zuVFycCaZsW4MOQz66s?>h`}(8O--f-Iyf2sLmXxefj%@BPa@b6o@90AqjCc|cl8^OoW;YfKzM z8PcW2QnlJ2fDiNx1Bed%iDt%>_sgOG(G!<=*l*7gVxzD{TM&vs0dddaQCx*z>wMzz zQDlQhLafuzC=StEFMBiqeu&*&9PRKiZa$OzMQCNuf+43x{d0H(@7hNg|I4q;8#su$YxgR*%t9=#izQ=uhsiFIx>E_u^%>e~H)0CN<^ybzT}^J! z&7V{C5I2aVo;6LDE{H_3+)BCN;hm!`u=o>tR@5{ri!%{rbWZ6|-g!k!H&->ThRf1R zR@psTUs0LY_K&iArGS=*oTP+)l(09e7HZCpl>69R%$m)SztD4wSCp!5JYsxFWVZev32w`yGuX<}E z1q1qZ7oGTbzwv(_*|)1m=fCH+dKZoFMs4`KeS-cl1F#fx2x14TUlL~IV1GK zdZO@n`xYK=jfJ?%5FBn#L!=TA)iZHf&T)*m3Y0}0MJwRP_R!AiX&CZ0rHcB<=K4)B zf^Cy-)^(RGd+lpCAG`Ffg^QQ(dv@#b!~4s|48B?T>7V)8Wt;Ah&|aD^<48hVJLjD> z>*0J4%e<}YYU1=m=O4SiJ?-1>FQ?AblRgreCv^{P{aX)D{P1-XU)wzzv$~ufE?1YR z(c1PJ^m7OUqW*(H0;J6YEIM`_t|z%Rx0{ImbKqEYAsU?ZXR`Ddo)U;gA|;FFm)n`s zQo3^Q%Gx}7P5WFu^BK{ZxD0tC@q&3}xT~sd)|KXMr1T~;^7cZ?xMES#EXV!6M5J@YG%e&iMfKd+ThKNY~)^%0Y z>KPG5qwHxvSNirVieII&k;@b}5nCfaD%MRX=rtQWM(BVqi|_%VE(%e7n5u3dI2w1;>mbs+=9N2x879@L2+L z&yq;)u0s_kTz28;-T!n~nzy!_PQ1nT$;a!j#OObDbM*v}1<}*B5fAZ>*5UC;R~=4n zv_;A#|>|S+b_bO5l_w;HNK17svyWq0>6B9XyK1@_?w}N-JRFp8=sO-cf+;Q}) zF?3s<4aO2~I&R#6trw^B0E_6uTo@{Z?&P`9a}BO@$tH9hT6rZnt4n9K8<$=QUZMC5 zzGpGBf%V;tZ~u8=`rRwYEIG~m9{O zh~|Eda-b?9CL$~8P~9b_sj3LI{cR1;ruICS1S0-r3#+?g<|jQBh)-^rAyONM1?4oT z4c>x9&eHas6j3RLM5_d;g_wglMw@8Z${}SWqT9>D6K%J(hXs;`Gwt<qk%w)FRDoKgEy6RmqE%7 zD4K&FphPv+BD3u-C6_JCtPsC{d#%c#d}yl?#Jt;>h$;?CsO`6In)r#gJ~+Bz_6 zFj*&07te#w)%U-@`dn2M=w^s`xSiD(6Zo@^y}G@AsAwn(j(KydAKDPX@^wXeW^-HO zbK9!ag#Vy@G>XEY7YFBSR+UAy2a#naHbC>a_^fpAHdbY^K^RBDP0s2dGYlVC|ms&t49zEFbTOmieQvBHURkzX(oczI= z%Vi5reeQSv`JU${%X~J>U%2@D-uLr=`@jABKmG38zWm%A3av5JQfeccW#xeCT2wBf zBnuEBwsHX9luK>%5GS4-3S}i(o75X>MRfm;O5=I`C8M&W2^=XS5J^dA&z^JfhP5|u ze#-@yzj4FT(@$^t+R2mcQ%N)U#^ajr|M2?Du9modO}*J?m}-%a-dTNFKY8Ev)zxecGM5`gsppPK14C)I%gh8R&ll&m|BnyCEY+z29!K($Vz4dO7x06!B?Yo z@*d&n--#my>z0n5wb7AS!Qd``-}dT(W(o5ZD-yDP@iX;nfU@#5rMW0K2%3#YeSX=3 z`p`Hx4jgaa1e|<;3xiRQ)B1l;^B@1(rL~o&q^3B9C7z*8g!1POm!9Pd>M`n{yQ8*a ztlsw>t4DQM$5sVz6GaLO_$AziIEg6ac=k{g>ehAjoaXMs_5D5|B)6(=Q58yBjhRV4 zbaNZg7uN1ot+d-~yqg;X%^a=>9;}K|K)DQbks7TR){PR< zlxvnuJhiVxTw>-%Kt%C5z+pJr1}>tmqEPs`U4P72_yQXZx2~_}^28^Q&VZR|f}9y9 zVm(mRF5M+t{T1fUE}e)tX`qU4?x94gQJSqSZOV!XYVj*z5D%ljl(fsBC&Dl5(iL^f zj%Jst2tAjWxwL9=?Fs(bQI;#k&@d8*X8FqN}Yv`%%Wv-xmV=-0iA!kFNTD(MX8Y3{5z ze{WwN+~3Wj-<#04$NtFi-G`YCtQovGUdAlbMPA7aMK-szX*7)KcWK=Ad)N@Nrv8m( z8Vog$S&$TGKX-aZr?};(24E-!B0{0$kTbyn zLvg9o&su@d)s8tYJ;py(JR-5x^&dQ5-wLVR<_sjt5d|ie>bRqQshaq(OSnG?g`+uf zC-Fna>!x-8_IjQIJt0(#!tgd9g%Ul)l+GWrBph-@sMw@CiryX+qQ^>`$tcVjKXHe( z7K`x~9isM=?S({a7cMM|iJanOV#L^r&$WZolvuZ;rwCQjJ+%C8>nNZ>A#$+oM;I?r3liu|({R~}3d`b~Ts2mb zZIP2trC?6zT+w`c9ElX)6hzQW3a3kK@dPl_QNFc>S+5H9jMH*1-$aiODEX4t(uMV~ zSgmB*rb9)^?XTt7_Ov(qvf-kexn!NsRk5v#Aj}qoY@KW$)pBsa>H-fU4S5`cY_^0z z<=3j9tu2Q|aLIG^%wqK8MESEP%O5`9vcBGPMEU*4M!yId4fxZY9@|~b$AKsI)?<3+ z3Wn6Mr3DS>ebr3~XL4EVx`}#jf^zHX6i{xlwJyzbtLP2w#arM>YQTYqT`My*sv4#sITt&$(#TnswK| z<+k_!^|$}E|M??7`=Q&fxac2#aTc{Ks^%z9wWq2TwluePk>*UQMO7fCbm*iqG|IQD zih_Ubl3LHJBFkNV>O$(gI)7VFrXD`nezy*&C4$Z3{NC;LE-HnJsdxS4EptvSz4Y$8 z-?jX*TXt{Xx_9Sv8*?*wRWSd8B|rF={^ql@HfZZdcD4Str8cjPjyO=?jBa z)~4F7cM=H=`q4Wpp~W?#^UlK)KYLqsn{@b$i1b@$pq3O)Rxo_Ly)lPJdT*PAC4T*Z z_F%}FqF=JS?TfXcN5g_S^_zh82aKb|Pgb8!ciq}7sN95Y#oT6QYSmH-3-ObFMh~4RHu_U)I`QDg5G%&A;0i;V$VY|U zy0_*uw-^C~JAy10>rT}6_?*KWRZKev)Z)KrVNoboDD_a&(d2{GftF$s%CS}h90OAE zgyximlhuXV-xkPK<4(poq4ccKA?`tPgPtasV#Z4JI3f}Ug1B^6 zL@**^5|x7z!7iSc6%+xb>o@M)0Xv4J2;x8|4*eNoj2N~RlOLEL!3J4LvJWL{*}|9=?dKoQE}l)7_|3 zLcFMjwj-Xl+Lz83uLNp9b1fcPVL2ezfrx>4F0KD%=@rt5DHMMGV9RBkiS;UYTL5f7 zFwzWo!M%U_2AC-x6sLgCwKe7`qiiS7SJO~ z)|#v=nLly!x{{?oi=X?p*IO82OI&;Zj!`0Nna*JuLxWY8v;`q0`4x6-=sbVJCGEAW zwn2mb$;XS09`St|>)Au~`F2^DX;v-oymGXkReM0$L1@4Gg)o z@Y~d0^@`8sXR8^Pcw9<2m*A{Yh4|skwV7p&ibB7dd3aaRKX_xwO62#&h}7FC3#zth zc3D+@w0_n-dhlcg#X87BL8oNLf%@#aLJ{8C9(S6WM} zrEK&NN6!m`#WVTscFy#8>%Hy8pL%{RihZZwrwLSnnLBhVGB}%Dn51wQn5P$lNgc5N! z)tjxW@DKzpNz~@i;yI6e?l+&=`n6fJXV05|!P@m3R$ueRANk3@@`FG3U;Neo)BDa6hBTn2#((H$}pGU61WzO+IV%y$& zZfMu!Z`D1yueyqfx4xg%HV*X9yweA|VU{h8aUt5Z^0ZR>~Wt$VAs z%X)J_g`k_gS@{QFsJY_l{iSo=(usfdVDY#exbyCBzrgdvzWR=&T!OM_Ue%s|G=Tu? z=FCiMkUFVE+a;7nF`Y&bQovaJ%+`8}D&O|XPvW|XQ;5Mq)*(SdO|2V7w^#M{=`B^^ zF0Wfte}<`f;Q5+1B_4RLD5?T*GAGMfK|cR<&3klW`=?);_|fYsQL>YFWVWy^TUdK5 z*i1_Q@C%iuM^)r?x!nF7neCx#AQDJun&funX8V=xIbxnus8E46`lBtQ=ewzjl8Cca z{l8J@k#JNcG-k*dlKaHo(Kc3Eq324to3CFw@ug>5KWr_vWI^4?6dCwKL)F!!l@t@A zBy{NQo0k@)dlslMX@Y&@NqOt@odZ>%bbUi@hy%x}S0HwJufw6N3cRWPe0GS=h)g63 zO&l0{7>J7;NKLJlq2^}>KTuBL+9h?*%2RApUG0=1om36RK`lXcuV75WG$5Ye!5qRU zJAV!5!cfDQwy9+tqGt`=m|kQCGdNGoY+!vW@e0SVTln1e`Q(?1^PfJ>%859fXW`p? zKH^({(0B5tP&ilVavK{WoSnG~P8AO@j8)MXx`v*ZicasU&MM+k43~If2=u818M09_ zRMq)=;#@@t(T%^0Ck{w-8;bK#IaSe?C_&axc^YLH2E~UYa7t%wFFf*p1iMbamXHPh zXzL3f^-KU1ZHbLSL;bd`_D5Y(DPVw8 z&<6RjDlVaju0GL1!_sh3ZHs*D3lo3kj!~%tSsbPr1oxLL8(G01A@};#wHB(Ue%g~z zm$&6ye2#l*EQp_o1P`64-M3o0w>1McRt}#S@esd%d%Xvz2pGh&7CZvt*KaHa3!cCB z+WOX5^v72wsKs3A_Sbl4B1~8m3G(vXP->4wz#vh7^lp{>)#vIXT=+Gd2F)M0yUz@< zo7EbryW0jCqO7m&Dh(2pAmZT>J;R zz>`8-b5hP0HhS1f0S_IoTq4no!ceSZ$nF(c>uu$l)3yt##fj&{>CuMN!@Fv6g$+Hb zoUKe$&RZ_4=h3R{vtHO+1PuJaAd7OT&up2vwf#033grQRO1A9^DcZeXyyx_S6|Z~G z&%FO5AAQGP{yUew{pS~7`R2d&&P`w4vvAAadgO|GsUd_t8Cho{lq+BjuC}d^3e;C? zYpQKz;N+F&!>nKZt+y>-yy^9a*1cucvAuhCJaPJ@R++)8k0qDh^0$BC=MJ8l-$!U# zL05#d7y83HtM{gjG$=Mi?U=SR`~y69T{-#%l5pO)ef0bk{kQ$r+(U&eI~TudZ>PlbZP6!W(E{Rj>s2Vc?nwWqcef-FsB_jUYC^DP&zts>GJj* zZmXzAc9nh=a?ASq*;5p1d8Mmo#-N#Rh#>t}Ut2FnuUu4CzWi*}Hnq!J)qQ4Lsd6W} zt9|Mo*iwP_7)0&nf|H>;+NkfKSuRjc2@r`|;06)5q=$CaUfZ_zV}&U6yOn(C=E_Q= z>liA~f|+#*+EUo`2We{E?6`qbi71iCb4h$~Ni*QbbJ>c~B{v*zy}Z(J>{NYW&{Q9{ zZ=L*vKWFp@eGCZhIXv>5(I5$$(WU9c_AB?*Q-Sd?<|0M8d!MegVe&@rkT5SPV1H&R zXD)rZ#SacVZZXH(_c7(V)Fq-JNL{hQ;->awqRXD!L6zW{pWU%-t-R>b>~bT*(Su7bj*I8m5t z>A4+<*f5+mt2>|Ft6Wl;mss!LG5W2co)z@O5b+#_4fHZF^`V=yI)5(2Pvhk!`EWkW zV{W{XGM)>=N*=@1ECF72Eobl@jhPLs?_j*V-77f%>Eo=Nh-38OF6#cYb@8LU}A{8_&vrphM}rFmBva8 zg?R9It&`8Cq?aNIp+*$;$N;*mC8#LS>YZ09cx0iwAs$`Vz8Dp$o=9$0(gWv%ttp3$D{k`_EA z6vip|u>9D4s9F?-IT5@=@LIX8TyZ9esmdvm9 z|J_%O0%F=0&99v%=29UJ3PsPW22T@MDT%qMRb3V~RFzllZ{~72Ml%gcT9{R;^cfUI zV%UXvrvZzG(6*-k4R~Y@=q57Z|6LNPQsvRh+ zk*sG_?|~L%r@gYVLW*6?tje;EQUe?ywYdFG7FCd~w7ru(%2@?E>#h>1-V>*b!d5x9 zXtW|Fe#6@GDz28Q_GPj1Q2i{bvbf|-DAtX%{bBnfd{m)OWXd@!%z^N@ymdnqq~C zAAa}SH*MN<=*%Lr5yy~yC_H7QeTbdq&5Z)s6wN45Z=vwek>-%FAV>dTdw8mz>}XGJ zs`=UzYy(`DdXuJ#Idot#<)xMqxe|274P}wOeM5~$wPbG06cG*d>rTrT)E9qn?mIU7 zvP&{(HBV1{N_`RGZUXPg)Oz$>G)G8m;r<1mUVSmGyLS;mXn{^Ru=JNuG*MYA9w$) zhpSP{hm`brk8%l|N)ack2`;X`B>k(0$}yMtS6oovrMh%QZHTZ^Iwx=37`T@?gby5u zT0FAilJm+%)zIQ7%Kz1a6T8~aIN~9aIgN;Pl)U|l7cRMnli;13s%LWb=*c3|6R@J` zbvB83OB>W*Tqa`%s%~uWdxCU%-Zt{$e^p4+#wenWtqwcF6GWA>_SKZRq&7iavVfCA zpgSOr;o(195(!OXLg$PdF4c*~TF}0V>yqHd?x|_EJCg)GM^2XMp#Nn$T?K=NlZXQm z4Ae}&OsTOgT~N(5WSfd}<&M$2xtzsI9b=sFGv_g!l@m^#u6{U`e;VkY=^??Gxhz=5 z5Xwuka-R4`=p4f|CZ=Yc&+gS(8)ydKR+!nq`d&vyX@39iCE;Ad`$fn6xfq5Drz)R* zHkm&6tPp}Eh#3RQm?yah!!#x$bym(XDVL#4^*NVg#IAOze~77irrw-V*l;=v0z{lW z9&$SeSwpG$b4Hh4s^{)LT({@kuya7pthj3mg4eYdHi6*3f3$1~T)L8+hlzDd%gOFi zrvoV~1eDyu4^Mt!^b_rAk)f(Te`@0R$q_{vIg>~w4eg-HCC{ri+OAf9BM(vTFa*Kr zvSM*7qQ>dcQuc|x6~+-4U0LT?&tQ%l4n_sm61A3Y5wTP<#9oI@h*QQxZXeJGV=u6^B}=s%~3U z;fxy=Bo;Z=FbRUgub;TYqd$cIlI2CDCuNcKTzl87nyGz>CUtB504O=;w0Bh;S-Myn z{n+)TEj(E!Ii%GLhAe?NphW14=huf+#HM-JFl->o|H`wY6(-K|wrz`O7qu5#n=fvA zI@!f&QEUqMsmCi#x$ym__8Mk*R1OUofEyUBW38G#wWat`hy&#~qmx|;x!^Kiy&>p~ z$MUa~xBee-iT7MHT8(o!8t~*59;L>hcU3dB1$kLHGBWqhD{AkIhXw_y+cuQ{Rby>2 zFOBYeX?v)IOVLC5>{+$lx$juDo$9K}pH)w?SOe$Mk#_&mj;d13(ncAc)k|s~r#h<9x{KrSMk5)M2% z59X@m3r4SwX4fDhKY7!rq+xjS@S@kMi=-Q9i$(Kl!$@Q43G%X)py9CTlMCilm(b-<@Vs?XHLe!7 z@~?+Do~|CHO*0*tHZ$VVuYeLf+0}Bc>(+U;wAtRBggGLZEs!o+IC>-7Ha68!2=E~EHaH_)zIT|J+EcyoK{`1c;I6+SmOMngE6 zQ$^A>u{3iS^0_6lbYiH_$+y{C{UB21k`f{)5-Ei=XMU{}lW8V!%1RS_W^3I&O>Vgr z;_@65KkFW*?NXExjIOd5XU!HL_~*X9u=_qa!2P~(?}c+|kSsmgq zeg)xS?1lowFJ4|fs?u%uEn7&D4gUCvy6MEjet`QdEABfNF02x17f4pRUvup^x=v82W$kI`Ko;L}DUq~80y-N=cjnNYhmUu4giSGr;!`m+4M`t6HF~fhJ`d3; z{hVGsL?~mNz|RB6Uwqr;nf))UdDmGNxwsP^v6o4_e7vG^+xjS!O=$qUKCSrPGcBzwWp`nK!uNOZAN66 zJH`enZ0I<}4P)_8M<~N{63{96(ZGHgB%K@Zx+O2%o0+R%bW+t>k=))Q$|bJo%)Mwy zSe;s#i*@jiqRB}&HcoDQrg0h0&m15uaexwM=@e7o z`{njRR$aEA_bx+i!{y66Mj<9Pg(`YB9olC;XyCsUf`quX76}{LQm}p0HfAN*Pr9*K(-KPoQ%oM>P$%)wNk=`+p(5R|Md6jG9SRAR@(Wt4WrpCoSd-& zLAQE;&FXSbS!m-MtXo=p3h%wPh*imZsw|ilpBqO{jygG2j?{k~<}C(CHF{fqOML#R zkq`86+j~Q zb>e9IQTiFYGR&Pf??-<6Z{Bv(YqU%5BlVbJYez#M<60a2!DADD{q?nXv;TN?KQpby z(nk8EGbtU|Edys-(1ZiVHAOm}X&;^b%mdCFmKK||zWw?3+Ufn!v7$X=E0+mbW+xhWc=uJceQf4G-WJ0@aYOy> zCnA*m@e?JIy)fFyFkxarbeTVLC|%x&+;m~hC$}5v8)|g?dixNV!yNLORaK&;lh0_7 zx_?J=xIJOY0oABx>h2t zcfgG+stMF^c8j9iK{ICUa;!xQ)qxilU5!1xf8wECCGMuSw%sytVqiC3!5?m^cwh*c z9UOy$$0pu%SxI$D+)L>k_imrKd__@$J%=l;LVDGufTzyXog#V)t#uS9Y8_z?^2#1c zeE_Z%_Vm~P^xNfa6Vupq&NH*o6D7>CK1A1?1h($5G`LK!VEXdO0N9z@&Y8<-fH36f z2(c&nrE?12nTKC%5d>zVO>UQRj%kaCNcAj8BIzN9JVDY^IU$RV#0lp@U&oE9ogQO; zIxGIL_NKv0GW1;r<8;|eUpUX;Rm98&)^`=YnX^eZG}Wkkc7NRF)pF^4l*HDS_E+~Y z{;|umdgaFOqTXT&KCc)WH8_`ntq=g)2LCtg@;HOIxv>cKJ2P-253 z{HYurkvR*McU)N&g1GZg`-^TiciIk1cCP~B32;?{Qc%S`Jh37z3qxW}m(o=s?af3m zef8R&J*%2tO%ilTL#=$;+bCq;b9E)uOrA+B2PQv?T-Df~m=NWJh+iq3fh{Jul!h7zN(|kiJY=y2m~#FKfA5oL$UBfwAxn*7T8*3_2OD3 zl7gGo);c8C+2D`jv*pZ?8ZJX^L5@LjQg6zhCsRJMx!jPYbU$!)J!RaXqHSyGjgUX-d!r{W%?G$@5>2T@wVtu`0VE5WMUw!TgJ z?knrdFMNw*vY57_uJ&qONS!#-h98Btd)f>h1RI&@AgySKGqR@50622S|1)IqrD0opg%c;i`*+l+oZaS@YL{8HnHX_`R8hu?L&fq3&s#6ASwez8 zq%eq!BoqM!r2qV>s*^Zu(x&Qv{OrU>URM+jGaW%zobk|HmDc!Wag3CSbZz@GHx%>a z?p5HD2xh%VtxBkTkZ@ek{$ApSqI;MCQ3&dvL?WBbdMFc_PTTaJ!s3VQUJ=YCbRS|R z6(Z4h++>AEI=e{Vo_^#+5kb+IixQlUt5#MzBK?Sxz-27%I5_&fS6P$`p|w}W8ic2- zl?LaY=vm3-JZHubX83PDHF|2>5l_@}5T1O;vm^SGm2)op>n5EciC#MBL}$=&@eO5N z>x~^@a1KMHQk-OsX&ys(&L7#m>W-ZYFG+1@PQp3I^oS2H%62ZiXc1nN;aiTG-K%do zW;U?CtMKBJ&Xi4!SB|L)a{~+gn>hz{i{0qCCc%ne&URCo##^fTjq^O`R&4nDUHN>Z za(g~cbTI&%rKU2;tBz1v4I zB4>oK6+X62A3((L#J&nf`Humpr2!AhQ~S`pGKwHfv(xoELOnwxa$p{_olgo0bF^Boh9DGlfuP zZ{KC#wy(~#rPS+|)GhflTPNOl>8KRpzpHJ=J@8yT$%UV|m5fH;+#UhoCo9~xZUmae zSmkQ_TKp~R>y@rl6m6Jy=I6KDYUjCw)wOP1Q?~x!e_E@iJV;fG!}ddm^jpvDX%C%! z^v+tp(NFE`Hn!rlHgXABT&V}{sYY~dDu};uVbRqa2H=B56rB*4vI0DCthQqUD@GA~ zTY8KhHay*)Kff$Z`*`g3b zHo34pwZG~Oq(9i+w?Zd)%eq=HqddBM^e(VExv>2VB4@a*-fmu3udApiD{vxD?yKja z^gt8^p|F;_ZJw!fE36uRu6@i7_{^)$!!fHza~;pwLactWryg?2h{GeROG)P(uo(LM zIn~58=(MVcl57Vk3kGx|bu6BCq}wv@KUf~(Xx;J zz5ngU-f>%cl*ZCVpLndAEQ4LcX&slkYlg;^D@Wb_t}BZ zQGH;jwH=Bfpc86V9g^DW32ppZ^y&Z{2&dUEM(Nj!(!GIlw$d?BT+)_aA$|OGNg1IS z_%H2%Yy_9{7uAm^a7o(GVbN$pT)C(Q_|EpsW{u5@YeZW}#~fLS{DvUq(r=jjA=lVK z;fyq9iS%B?dNTKDc~W0`+FMTUI$YmfTCuP;om2MDKUKTl(gZ)ZZQ|Otfs)H8ab)*O z191_kL-cl$&XCB$Vt(>|Y1Nz9or5d&-{SjdRZ zV3ezv&S5i&X-f1sLltJq`BP7-?wRPW;fA_UV+9C+g;B?1)Y_WH@k zN4J>VubKYTYddrr=1%@HY;@+HdsVw(#UBVKwlpiYl-Eowfc;40#g~{zklcffd2DQz%Y!rq9ed{Xdf(|pM z3XxzjbO}hKxMWM^OMn<)&ey8~-4!r|N9*Tp#I5Zw5Qw`D)tOo&C_SowR4a&}2qX^h zr`P1ZV-;pdk`GeZhKWcViF z60J2clwt$`M&g$N`zuV`Jv9g@2T)7(!@5jMVe5$H`Ii$n6 zDloTCw!&~$o-yF*E{TE00)ipJ=>|N8ZslY5R8Vz3;2u4-;B3F62k}9ayu1Cdm-N)y zh?HEhmoj#@yH`R_kuf){B|w-0;c<3^(<--i1X^U4C&HpCoWU@T%$fD1CFG78bTaZw zI)PCF&FBmyy0+*UhEUS9VMyr&&7%1{1`p`brqmLEt|Ns%mq9eYu6>0RimlZlE8!Oc zrhx<^!X`SymY0%JrJrx>E=XAEkRV%^E!5V*{`{$;Q-1&U3MlF9g_1~x#1@6>3OFxe zan;pg%hyIkZ`{KW=2)lamh~gGvM5w7+#rZ;RHzo1#tJ=(ATnONxHMBJYTR+CmSiHu zT1>WWnh$TDxN&XyM&~uFO8KSB%VLTNG~dDQWc$)thl*;8L%D}eO#Ju_6|mO#o7>}K zIJ1(`>psz*7eIHZEJWtcDe+U{lsClF9ck7=nWsymfX-DV#w$~6g?k-tTI(0uh`i2I*WnDcSifAAh zq!hAs&C1#~`tpw2dnNd5?aNT;OzXE0<;E#~vK*K_ySf%0dP;fQzKJtoMls1VVFuvp z%GQ^vk6l!+#jRdcMBciS0^g!1kAX-;kK@*LWp`2A#??`!5zElgdRwhtmYkUQ#m|)5 z8a~Yd(S`c?z>Vz{^)_!DJ5_?*W(9>yI}VP9m9v?|S&_L!3a2R!Cqwj9ZMTiN>sHsR z=yI{5t5x->T)Q)rj@FmRu3c3_5Cb!_7L4w6NQ1mvfv4v{Bl10CHiDnGx z26%uf>B}#w@75EcM{!j6+9h@Q?yKvJfhQ?c7@|Rza;2_-a)=v{UPsDV;{peyS>)Ii zEmTmq&Mxy8GDq*C1e3)aI$|iLeDZGzKp`L&^W=W|>2}ZM9ubHeiS%~lLy&Dw15$v& z?IWH?k8@7|`iWDOHk*BFOFe2H%@Ct>hSQ~V^Z>Rfan=OgAI0Q@g!%*wnrZui(Sz_n zLn_eO?K@v=fPASl3iHhbiHMA)K@FJULtH*NONYTZyH_AOO7Vy2oGDAvD<nf^Q33D$|&It;WR&dDW*dO1Z>lE6eIAjUa z|MxG}FOCm^Q(3xHG~{pzOoZs1_z8L-m3kP8%j)qKA9_}-NJLd4FhNFQ#~=&|j-{2C z6-VeL)jW{~$|Wr9t-ujOX9YjDNF@|=8H7jlyu5w!RA4GRWsB5Ee~av0CLq47eW^zT zNt+-H&=Y)9B`sG3nM;RDvh>7%m(tjD{$x4J0?z;O6ZPI!EXKnWy!hKnUb!rm4xgyE zPORH4>agJvnrr>F`O9{*RogB{s~i02^|h(Tv`ZQ)_-=0l{rg92vBW$F=(mi)kQIdm z>bCaf=D64Xl?FV!4%d}*ikz}oT%|Hxvme#wLIOQruIw2nG#wuo8^0E7S zkCq0+Vr#0T1D9{UtlpF)Ww}T#3MY*q#f&E`DjJ91bZJpy#X?#|sV>$PcJ$}>Rp#|qVNMZHtav4?ooEYZ6{M{SG|C@st%nYN;)bfF z3{2uYeB0&i!M*m3((Bq-SU9h0BOOm!E|^>UL^M~CC#z0^iQjy1R4$heHl^b{wuoy2 zzDa+ueV|$zQRQS;h!mn8_4%htJW{H(KCRL=q4032m8Sk0|0jB8>)4;&Rtov4-T&RA zC3wm5dT~{5cDq<1$a;o}%h-yE9I8U!;0*nkF@(-8inBBkhSD6DUs%d7U0xsO!a%LX z|AF?6ylYlg(P&_3?)-&UUw7j>?t1rgr&k`_{mik0d(WIc)k-jf=`d&Ryv^_V>F@t* zzxcLmmuguRuKsI-wY9irN}Gc7U02j1Eqz{_rj69UhUh;|r$y39bb~E>+k-r9TDyNo zz1F7%iR1}KhnBs0ZS`s$X5+Ha`=pLM&GBe^dXZhjS%Mz^$Th?=(j9W8fzR~NdE!)U z==Z-!g*ke%*+CB?iyvq|5kSgdbe5HIje#ENyV}c%;YYzx-L+lT&w8}E&d9xNdF>i} z@#*%~-sGdHRq)o&If)H%-_aUDy<*|L*VfC(W(K)<3=OqyYWh_B#Y2LeK_rs8b%vPW zgXQH{o~;LmNl|DLz@YvO7BxWO&cn67{Y1NUKDodC0P~T}<(qlww)Wts8c?oOEQKsG zmmRUP)&3^2|Jdlka7uXMz(YAh7{2^$-7g3pJ6V4_SvC1dGHe#WI!2twjL1srUz*(% zO2W<8or1Tj0^$bd2s)z>MC80iG~dPqA_l}T2le&zc5l_?9y6P@id{0lHtVB-HnJ=- zolAPsxR6WYnOK74X8X*fM-@mHJrjG|{v*qi=#PkWfC4)9Q~Sz)xqv}L{b(CdT?A+Y zm#Md8IddS8j&Gq~-u{NMeey>_&cQGZtV4)QjOF&va-qu| zB1C0?o@((-&z9Y*>HoSv6|baFXT__lZ3Zt7GaFdn41A;e`ZrGh zG@8Hp1GrlpGi`3_zj=374Eg&+CIQ2&F||XCW##^NKFB6cKuPonOmicNGgIxN=LR{| z%2Z}>I}g3*MuW@KZKB_G=mk}>vNP2$EAsn#LqpRTdOCqim$in^-!l_(q352`89=&g zU?}m;srI78kPX6VWtg(((z#!TM_QrsZkII8IfJ;L{xI`ILP!mwx40Kh6_V$G?r5L7 zNZBI=Ifs*<{bvocAHgM%NToUABt3~}*5%qcZ zWSYMK;Wo%f%**^Zhpz4_lP5E(;wjT=U!cF4GUK(5j`{ z6EWpU>t+(1(LJm=pV&WIz9Nde%Sz*sn=Kv{MH^K>^8-6adK@YzB2VnAO0)E}F6B?E z?xuC+Fwe1B5{R^1;%1IEYMrlZ?{4UOmeVTv_a7S-gcPKtz4znIhBq`O61Jv zt5?>7oG7+XFyFSm_HO3Rp7{CKHfJU~e_`^07kxvbej#B)iTL-sk5mtl#n$ZhY+5RW zW94U?B_!_LRLp73Sd4NGN3%;uMjsx%=+WI(V~%x8CtiO^y`YMY6OU>Eg7h$tFZ%f9Dtf*VBvEsA#pVCM}AzXTICp{Xs~t_~rIHgPe(PMC9r!AHB1tFLR3- z{;~E7q08E#ZUb4r!egddy`+|KdF&L0hjMA#f4rWr)Lrv2#7{n6yImZ+b$#u$a^`ow zHhKz`Ah>+P#`59y8_OIYmM^GTy(YuUEtIB8W4xOmcH*Ze; zqN|cBj>zhVUsID3!MLg|%xt?S|B9pRBeb19$Q`8Yw&@51D}0v7m$OJgRa(2zq(3~d zw`we|vUP`PWbzIxfeqq6DMSq7E^~;RvJz3^oGqs?$1&;nKt$6g3tZ~b%+a7wF8|%5 z)!QWie|{p6X*^otvNo&BjVk6qLYp3FoumtzoYyQKvGpSCK3umCpd{}JVo1SLOOXvT z49lJ~=o=|%kg%ClR~1>KCYK7By6T9wVS0ETL(jUrHD&CM@yEKrFGvwOk~@2LwNXdk zy$bN>7?XwRFZWM={r!B<9X@M%NEza+A_2;(=olRou9jIe^g{JuKFiv--;fB;S z2t)rfi1bjTr=4?55B$R-xD+iV4og4)7My~leB~h+Q|oe6kjcO0iU{)~I7Mu#oFg@C zBg8|;N8%1kID*}77?O_Ka0dBedWf+LE))JnNun80#+<|EoZTz@I7MT9`!bOqBB+YQ zg3jzl+!3iX0b)!f9sLj<*m0oFLi36v#k+&3%h)9nbNJ~=<-2r_b?I0Xak@-8bPM?P zOKVrCR_|>Ki7JRlnCaOoR9#rrLPM4c(JUNyCL%<}nL`$+&udoJ-=D>WM8w~8s!SSA zo~|ZS_UgUuO^x!o?Pf}|!abgFNpaYsA5mK9`I9$|tWST11IKDn!4`U^ zH*3i5gH_fg?Y+D(<9Tpr@l*x22dthsQ15i4!qXWF?ej^}q$Ctjnl|(ZrcP9aT&{k& zy3iEMg)>&g zJKLL*KX^^KCl`MIv1&(Zxk_^!#-DAJ(vX^L+cfntj`nH{I=ie3$CZU34VSHGUtMWx z<1?c`PnBixL5+;qMZOedQE%Vxwah7vx z0J8=#~BM_^s;qy;^%H}ov`)xDtr4~RLp>XZ~LW4 z9;P@FSGBz}=}VSX==>*7lx*BC51XDqf-F^E+A;Cqb7kxBiHS#cRaR=FpIHRTwU^SN zm?tpkkG%{B z&03F|w(W9sYNI)S7Q&x#XEu0O+rTAmeuiuiKM9?Oe7JK{y)Y=3 z=O(T^Lc#~7F4-0?O0cDLhCnKzx?=O66wbTa2NNlA5heUre1nvfTgluxqXNn$c;lLy zaEuOPIhEtCLBo|RN577jNtmAunYB__=`)j)>YBy%G5G6O*ISC}Uv8mIELAy^e+D3{ z4$Yi<+Fw@s-xw%Ac)b0}?Uhv_ilB0Y2cO+m+Uk0uK6(B}TPA+$=5mJY%0-okA#>EJ z)3q@~&jUMa8_r#oEnqZl;gVepzx8k}uz9845EW3mTC=%SwcWx7m(0$aQ!O5GvN#QD zBRW2yJl6Pg_l!6mJ};PCy$I0(D&IXf`|@h|eyb{}hvB8w8P_AH!$ zE;nzaGWWWTWlqp#UX88z;NZ{Vw(at7zgFKFOm6VGCu-ZBPQ>shmnWn`hZvQ9?%;@$ zGw2Mmo^GFcq5(a6l8%NPDPu=?q86k(GpM3`xqpc6Ptx%!=`l_SWq9t1;iV(4=5h#^ zG!Hj=x|3a`W9)Ja`reR%_}JIqr^m2>Lp@1PZyKgDoK@td7HWv$>h=7krs9>zz9b9_ zJGJ05_)f&k2G+L>-`%@cFLi=&?>rxzOKpg9&w67!7b+S!8}slJjKNg930V*wSy{nZ zwRPa{xAVxl$%WEY^|Uy$YYSy6A~I6d!YQ#)hymiUlV7)c1!6$~Uw~oZ+|g_Qk#ZIi zb&ENV)vB;b@ll4v*Q~C~E-2B9rk;Djp=X7Y@&r#J={&?P_q%-(o91gt+riE!4v!F5fa}CKLxx7){;Ax5S+iI8nl>H5rwF)Q{j* zsCwjNJ)jg>{G=S>>`zYqkP|1#CR)Yg921m`Ote=yTtdR%t6H3jhsAK#AL>QY?HgWu z4i}MZwjLx%p`ScmZ^QtFs}@cCe)~=ex|KN%4s6sSYT{I2N*xbT{$=~+J8mNu{YWhZ z^M`s>XQgp`@&zeuhfdVr^HavSO8F(riaBc)^eo}5Mih#Od_vits?mL zi=Qc?`sr&VbmFq`NKorQSMA|(DN8)MvLz}v-z&+os%X| zltpb3Ty^>BE%me-=jq3W-x3C3WFcm%%%UA-@3A?TtX{Hh{>^{)umAY|%injyb+38u>8*$N?>VDsXD~4_ zZ^5EB{J>A&_8Z(h)x~Y5HptfMk5 z!Lf9H6UZ;OhjuefVRK}!${j&Mho=pAxcBJ9T>rI4Xm__~%9tjIIbB-msA3L7yj76& zH#|X1$LQ3YPw~y=k6(BC-l}^RD=S9j zN*hUtNiclirlK?ST${CMW*vyTi(Rv-)b47Jn{g84y78rcc*|${%T>9pO_@%Gqx|{< z)!fx~lM@tvM|FnQyFPEWSy=T+TXLmw@@MPbnhjF4WW|BBh1(W3RfxmCdCAxNk(JaSQTcImvmeY`@E-Jm-W^2-&~ z6WXp~Dk`d=bA}kUKtZCP1LnH7Us1%c4bMX#LV@mK&w>NT>n5UFa+&?vF$^ivxuii= zL}%2#BoG8rN@pNQbeBShP+};QA$=;)$2=M1?^#0)J>3&-16l++&&$ABO$1|?FKR_s zv>fF?wGrUKJl+p@wt$Fuhv2=VP}kA2NrS^Rfk_ zpOWb~tJ+TNfD0FXnRYiQ}j0(Tv18Lv-dISac0JhKQ9N2S#)QZAU%6g0b zExlvts*9>mVqlS=#Oc!$ANxY_5KNWE!v~(LZdtyyoh&6qSEUkiIm0M%t0VM!ZLPiU zfFhSjAglP>LWncc{)-_RsO6p_fV#y4wXe~ZUMY@)+SrxWx8HX8Xw75wYh{JanF&kN zY*N!q6*?+pA0*`}3!YZU)m&BoG5~);iqC z@=V44l{-fDfJjMI)K`ip`tcybb!&9x#o<3| zpX+i75=;d7+d${PVNDHl*~*I^uOJND+Q-_lUDdGf^ZwuJc}#rpN@ z|F3`N7hnIukG|#VCGWVho>kFgH>|B{?;Ti8ez?6=CrhWzIj_pfm45HFwGW%oLF6Nw zYn#Iae(|!()pp_B*Sep?8!xG^-PK^fs2FG@rQyAiebd~+oJ&3g=qN4?wEK5dmLnq$D3nDTwM_6zG1c4MZF4hL@EH4mp>`@K zYX58Bv5AxIE9RJEk zut^hfP8x7_hEA!dX7brCkZ`cBOY3{VP%z-?x+SB(1SHp4DN<7C2{R{*i9?s_S*pE$ z%eVCP5? zvDGoW9LqW@h74oOz4Ff!?u;*qKRD5r2`>*X!`zGF8=-Rs-wv4B!20&WH-Dr(d+;pp zc)vnqo8y&WdLINs7qpkif~j-5yX;wK>Tq^?Rx&{n{j%>~ozD#ehCJQ$n9h!AeCtU3 zF{F%%XrFcEJS7B8jg)SJA2( z?pc_{QvJaSmkGo zgjI^kO(GsTs{zYY3&KC#T4`*~S%oU^y=~WqMa(WON_QU~)ejSZpBr-DaYdDvEhe0| z9~eD^-9AJyu49qO-wKYE-7q+)7QTP!5@0#4)lq<1jNh$9 zzVGD1m2(y>Id<^*(1B9-*2`-#h zO^+j^)nSIzb#*ploc?r-w_RRanVQ=GXSaT&qP=DO#iw5|w59a{dYFhs0Kq^$zxw$j z0Nb{A^bWESm)D;+EGvHu4V@o9EA(t!Rxh4sV;Wm}5rwzQ-BW!}XCw9HXNyPg!R9gr zF>k9McR(WZ=hQudLb;BV&ZVL~wx?_v8f1B>uU@mNiiyXWY@EmJzv!2qJEv|dT>bsW zN*tT4=wTW-LtM-z!Iq<*L~B!u!n%xOL}1B+(JzYDC~Nl=?Zv3J(6}H?8$oA1q1M#YV)@&20 zAnj9+7lrcF=gRi;4w+0GnPkY`x1-#4>Fg5l1PWyWj92_jLT>--Baou)(bjvzpfcZb zS+T{RFi7MaU~?16CKUIagP4w$__^uA7xrQhWvR)#+78{22)bx$HNi94(7S%Qd1i+Q zPr9w-CJjm&J<;6t!!rkJjCaj&L;Ja2m5)NIGaESW{zJ~_bQwyg$6Jjah9Nr7xiF@x zqu1E^W2j{4b}x}RMD0lrD`Y73g2eELA`ywt|i{9^msuKG)I zw8^DGqu@-~nz%C|)UlJbOOoK2=bg(x%#_n+Ed5g1CK$onmF5|_7_o|3Ezn3JxB(1!ZkI3gIQ zPw%hSPh?fywv}5}8WP7()wYrJoc1?=K17uTQU*A0xwm>z1)wx+*;~wfV@tItZx)$q zTg_YDMf~HFzap1rx@1{BHLzi6`}1u35xUefq?pl}U({N;s1{P#95mm*y*{?Zb5)u) z#pzO`-@bn0AAF(Q`;7-`fn*JEI3JAr(IfXTd7KelBNZ9{_TTbU*bQ8 zs=BuD8=WUBx%3>VwW85&6?)tH@&rXCsCFjsuU|U)OgMgOiOA*isWau_b*t;yE81k~ z3}oYNee0(+8HTSuH~L!z!6PTj&$n)>N4KIs)DuX5FBYI~_{`Hyvinr~kFp-j1Y5u=JjR@2vk!DRidsg*iZpbRB_H2$SqD_K0C0DI%_tu5AKwYKp z&6@b^)}nKXph~-TWla}+qxqu6vmZRT`YmsL+lJTv=;pV-ckznVS6p@Nf;HF7Ilh1H z+iUw)-HDc`WH zW+AhRZlf2OrVbr1yUPCd%S$tFm4aisBEh34OHX|Pz0K#;8oY%z3hFtUHkKWVPu7eI zRX=SqG{mUF{I<($>)gzMQZsOSh7_GG9hH-OR;av>3+)-t6CeE7~IDmo<*7=THo;clJ>uWD^ zKGK{~a5`yIpHUJiWkIAz!l*Y9r2~Mnu#!I>l%cl$BoTxS1B!E?hp44o(vM#)0p@6{ zifus!V9;-GTwA#!Sh-=#TnbSRp_|X0K`XoMCYK4GQ@E^sSlyvGrlXicXGJ0nEm3>x zE#ZKge_A@1&aqWLE7YWV7Ap{nvp#ci`}hF;A+oDu?r+p<9b&mOGD z9|Q{h4}hUdgQa5v4j3wooj|xxAcze=DQ?#bCe3u5afpvmM3Z6b zwko1(Z->F3K3T!Hbet4_K!w7sa)Mjem!LpXw5@u%lm&)}lQSrSatsNwb&M)VtHw`n zDRGUJKc2@u^pCZl6om4sF<-s1*2BW$1k-U?A`ZW1bwx<|J1uj9b%t;{1D} z#41!<2>pw%R2+=R*5(ZpPaUW<@ho`$^+S`dNVHm#;NDg3rA5vYrw1FNTwq=7{@V{1 zJ!OA#e}&r8%+c)FeYmzr@Ylj`ayf+I{Wnz0N~e}s`Q`RrQUoo7>2#oy$DMjIYAcI~Qa7g9=un9|9Lw^Z?jDwG^s+vWwjE(+KH9ShgmJ57s*?a~IB; zw_w(pQ{U-ZTJshzzWj=7KJelH{LY{G?{0tF`;O0Dw)f~PZB!kgZIP!+)pHU?tAxum zXnHk#pzWxFiVf#At198pNl2MIH(y*G>FIs7g{1P${AtHshpL$fzI9VQJ#^O<)x`L7 z%lP^B{eBeYe)UVWzf)bVjqy{bN6OQhkZ$0g*?=u>I8&l$I_v6k^@Ib*i=Pw*2|nb0 zjY4gFW)b(l{7P+GF!9yrYVLxQr)#hSe~ij>GaB4A%#b>giI<+hos>BYW`jlZ>nHPW zTvOdF_{+D}AH(1e+Lyxq;TOw|KY60YHS1TdEKi27$MQs@@EMdSR?r;=i|Hcu2~~<# zX(&dSf|SDMDh;x3SW{|mT3h!`ZX?Qq3?d97Qjq|n%T%LD%))-{k`Y6Q>z346ZKEun zSE`iGFu!KysLpkRr`r$Cs)zipTx>DKNp+rIfK9bm?`k*qYL`|^C}fosJz!25QOIhM zc$iB%<$?qsIHOUhN=4xevwMfL+p)8QX;z|>Z)7`AZ>XrSV5tOh)#k*wJ@4= z7*aE3Yx1k3pMPp()PXIDq~W2RwX5P%aZtJ*31>H^%bOC~Zd^Oc!W_-4KsZry28nu# zt^K8htEmK?GyGi~TRM!|Ub3M0^PYGVIy-U+q2w~o4=Me!XT`)2ojIJ&TiRQzkXzLd z`X@vrNILq1VQxTY&TEB{KRphg7=841dJJ7XtJ~*^VWq(| zs$Smi)eOFuF|&d7J&jj56;6Yfr+G|jY~r2jc=Yz0Kqr@>a zcZgwb(nE@pI&zPkGa2$=m_N6o1P5U5{7E|!K#ZZY0VS5Hx!(-wkV~5IMtXyoCnzQ!lrl~FoYQd>-V=`S>mMJWI3$l z>CBoW+USv+SXLs7XH_Iiah5G8{xBmFU(jA}OrcZ`=0vn%aE_m|+FxiD8D|tq;FqNi z!`%1I6j{Prde!10(jZVSJ#FQ%tu1@9NOK0VRphpEOB)He1%-%wT@7{UhVn(uY(%l-HA zs;$X6@RrLa{@o*0K(+ehzA}W)D1}5oVpV%A0&_Y8%{rS^eY%aM;Foul6i$aa!IoW^ zvfGO6oZ6OHr0Y{^Dfrm;$@?~dzYz0vf2&q*DC-$BL(P=H1mcbr6?W-~O1dq&liu6q~~(Hi`SU7Y_!P7~(ux&ge+d5L>$s z*NDEOH5OPx=e?)5Y${nA(vCE1J_7-TDy^n0H?J$@%$=T?{gXG(*>+(5u377jEV|@} z?|#$L>)-R%w}1a7um7CAy?V{Td8-$#Saa;a?lWgjPrl^(9e_D==dZr_ ziub(l{a3&DKfCEi{`QZ(_gxc;cf3%*zK(8l8+t#NV zb57H{37RL^Cn!g6M0`@FKJ%pf3X~VOA z+@Ou_Vs4zVm@%TR@L)(EHlyx3Tzgg6IIyq1_Tt`zdG73rj+ABI?9roVU)nL!S$*8ifSV zs~zs!Ue6Yd#M{PY`mNaGvi4hqnZ#r%ivlJWTnZSJj!|sL)zey^(f4xehtYTR+tfrA zwge1`2XuqqcwplCHARV^k&=kijcd!>j1-9elv-NHkd}pKRQ$G?r4)#Kh#pqa!$uV# zMj@9%38%AT@%;M8n%gufxt%#|QxntRKD$?NR{viH z#(X}1RlTZTMLf0t)qme;2H!%=Y+!vCLe!Gq8v72yS!b*BpTEX-uV8xF;mu4sx5HdI zk2%xXhQzZv+%Z*TIFC;w2*cb+g%WKJv1vA{IuOSwK|V&--5C0lbfT*uS?9V(;uIpw za&(c7G1&@Vj*V*dlPw|-BHkg*jU+lKaR~lcNq8i;{A^#9PVC|`yDH&F5g^8^Zo++9 zoeT>IJvUOY{XoTth`oA~#z0&M6#nGK+a9qw+z~le!E}3&M_eoLjWcoH!rS`t#Hp$f zmkE3c3fZj|ut~=mXAGngIy;V?8vWr;`quTunc_(BM6ePz5Dk+>kMhvzG!^{m6C>qJ z%SvqO^5W&CJS8g1(NZWu1y!HgG79lUX(-axC&GG#6(K#*N?j3o(}o{|bZ-J_QacXR z7r42E)1rd&tPl({udJzv#5V^b3IWV2GxY%UTfs(FRC@U2h*>V3WuSp<`P}vgf3f{! zVTcSpvA13feD+ZL)K^=kRea(`3GswaW zGyg5UfZh1HYEi9o@|$AOfC$!pR;P@nARnIG6C_3U?zUZsrylWWo+lUO-B(xbxlDBh zH1iXF_mL_Ko#{E~AHB1zq{q^ejlcBsK-wDb&)dgzlAFR7&(Zq8)~dx5^V@pu$ULP1mm2@_apYr84VzwAT8n3siM# zK;rhcp;@zQYZ3LMZcQwI@q%=xRjRo z_m5VgEZDAY(+hEP;G^w_i8;{0D^6~1L-?InjG_zTM3fz*Wl?+bbUn_4{-tL}zt!6x zW7*W^0mvOHvn$S;H)hq8r3h+i<)Ye7dSv&+@skt(=8<|x>YA0cITeR5YPI*st{2{_ zLwD2vSbj{f{T$(mHaw-{($y;`{?GfWu9PB1f9GrU95i#h1#E`Z>GsYk9!_FoNDGcy zyt(ZxQpzi7ZV*4z-q4p;UVTjB=5;lneEz9oCP+W10q&LaY2SADL3H`{D~bZuh;7{OWkTaJg2s+b{TnE8aW*qCxpezw#!n`LPim1%l(UI`4tBsY8u}eUS|Bn2|%Mjsr zjvJBfmx0ST9Q56*&NF5)3de}hiD%926oYr`-nw555s|(ac|q0S(a4v9JjO-CP>Bgj zON}ABS7-Hn4Cll1s^Dc?`9`=mvwQVjhM5hl??QaX?OwsFGFv5hIv+6CXK=U7G3#e_ zW=JGEymt;UyfF+#&Z=|lE;99|a#jsf_o}7}{{Pwg^Ps=3^FHu=32+k#0wh7KTtRUY zSCKSj*^*^#E!lD6BwnOsl61E2SY2H;RZ}ySY1d5E{4+J~WU8jSYN{)8dPyqX{IUV;?0Si(~m7w10b+0VJ> zp69&h-XDOJq=8hqWV3Vp4N|xkg0>)MNS@LJnRD19_L5#-P&Q>8auy~T1OHn8hKtLH z8m%zJctQthHbPG50gaigATv33nTb?jXklCBq74gfMxA-ykdxp*KC`c0jsuxB1{9(z zCMlCple}%UhIRGELJl*cNV!~CQ-9~I4-YpNcCyW?uRec99I>W2U*B0RivSl4!}cBO z3F~>CtGBvLMzOFqWayRAK&nDXxMTo3%=MdlD^Qj&IxBFd%cZld^mDQ|Tss$|R%2gZ7a;lxPQ4Ywsq=l3wurc-h+e;J)m2$N24vYYj>I?dV^uO}? zN+a>&a*6Rb@2lNdF8Pw=B%e%XUMcDb{NZCuKY#7aFBxn$?@)xR0^1+=rM)~lx7HAe zI()P;D7<`(x;UQY7**h+b+u0ial)7?8XcS0&gelI)?F*^Fo04T%eKb+(ye7`)^Q}; z^{VfgOV1ss&v-FFZFQ1UW`CwW^R${;!H^bF;Iw?qwU;*XHY=rQ2$Vdy1kdfS-AUa1 zn>SW+g_0N7Z7K{c*Y@{TL?DYPSSo?+%`Djv0XcR~{n}kK`xzj54?o|r?IkvCfftz| zW5c+;yC0j;D>y(p&%L`UFJ-FBPycBJQ%@txf%6X@t&JhU& z5Kf?yblrWC->(Fcc^(0j=#d#rURzpq_2$(t9zNr`D=yl0@n!FM=jE$5TzPQQkN(A9 z_?sX5Km6@AKk~Of_*eeU`~S+{e$UVRjh9|qyWzsCH(hq)wzpq*`Bm3m`u3~0U3%rV zOD;eC{L9vFzI5Hji_Sj(g0<&v*}Qr4y7lKRoqpEoXRbZ%jJKV==8RKTpLU9Xp0aw? z>eZ|I-_j|oU`{Eh^P02QuQ~IKqpzu3ed_AfYLPmhdg|)4He7JtdFS2o6F>bEKmM-E zfBygZcmLksyXHUpdw=FsK zgP|^=brc*waY=>6$ocZ&*^)$Uo9d24)XtZxY|Q)zKT_Qz(KBw40Q5Q%atEmu@N_MA zxAdDRP+#hKs$N#rnvBWXR+fq`>5N+E)SxgVykk?HV>vQ#PVXvGPFw4-%4t3}q#IP< z?zieZ6wc~jS-oL%)#{3~F0GZfl|4iO4d-K2k3aR^#@g${DXJJ><6^oc-Mk(t-GiJr!8( z;;!_hOAJ++>ip|_O7FI_%T1OP%fVS!M0t6Cxzc6YF8EO56(W8?7GsZrS-njYgS0OY zF~Nt_7RcBcn;kJbEaRZ%p4}A+y4au=nR8eiz?m*jqH8x5%aK>B&+qPytC66#4DNA^ zCA0*Fgsjm-4xkJD6flCvzF7-B;}Q_AoXeC`ASZZ7R2obso~M&G3UuZt#xz z4`8Fj2x8Ts0Ye(3+$lL2h|(eRleaDX;k!cF< zj6JqRo%0tSuenZ?x)OjXG!?b+iOQ&lm-dH&H$E&1w?hE6>)zdd&n;dmsgG& zu4nev{QK3(_FZ&SX?5GZPdw%GwaH{ ztCarPj>NXi&iSjc5SaJpcj%c%j+Fatr&it-Ga{ip&C=uxRh8XLM5SpoZVEzCE?hqA z22{w<9^F%1!;Huz@PGQ+ERj|wp$zC4216J5^_}T%@gYGWwIBq8HqxPgO^nrj0S{^;~*9WuaMvbVgMw7 zAIohZ?6DjqN&|v-#1RmyNCV6c=yKu@;x(>3vqBL0SY*7;kimc*`)V2G1D92b8{?mR zv4#=meRzI%4|;_W#sMQrcn867Di9WzS8S*aH3MSIID^=L-hh@l9XzDmQrZxKYKK*i z_Wll_b81Re_dc=b_IvA#`oofIHZ481=hctC{m8G~aro7vhu58UiZuN*vZVE@6F_dmGfrQP4$f6;lTopSMeuiCQahRe5{vT8Ls zu`@t5&|k`o`Mo^)Ni2+ZTs2UfSG~??@Sd?UpgOl>h^!3ZeA_*q00&r zA%ZWZ1aa!8udMX~Dgc7C8i=zjq%o$(_`7%27L;M}c0U3(Hi6LV8i~g5#m1XOM5sF!s0&`cZa{%To&%Aa-gR zH?mqrj#kDFrZR_x5e`x~9?d|eoEgZp3Ouu~7UqsgUuE{|QvIh&cEYE2=Sxd>Kl}Q| zjB;R*rMys>!ts!2pE3JH!+_(-Ii`daAoMG9ouJJ079lcQ0Jy*#GUj^@Q%~KM?#=Hq zoM5O=!VeFe+=2ByhSlHoQQ*4?<4-6ngMle)^UDh>!->$mRj`6d%d3KU@z1`IzJhnl zF*OP6!Fude8i4phnbMrac@)N3tr2Ye0r47ye&{k0 zcNsesqR6rFtfEI8N;30@RsdOlb?onn4Ddw91yDwmBbFdDi3~C}-@Ch3A}MmW}FODhZ* z&Km3MJL?smNRxrY>q_ilXBt(a4vpGsxb_{A zqBW;hfjW+Stv3-!Q4mW`Tq;BwKvUbtPMN*-MGh8KJR*>(pZ?SO+RM5#E9t$nzlwqe zm@$9t=?YPZ|H75C2)q2>=Pv=Vtvv0m2l6`7sB?ay$!^)U^zZMVZ4+}Tk}?8ju(rNe z^|s0wrBa*MR@w%n?w6jk^ws&t&arU3H1=1YDVy!5=qg%hCn^NdeoMQ9z1rx4+T`*J z*A^rCCtsW;gx7B_PhmjLrbRcZY@cGP1SSa z!B)o&mv3ErUY!q|v31jyZ5y{KfmA zOjFBFEz2-vVKL?_xYU0{hIaveWps_34(#dklmjSd{Hja1bk`*w&-S~#eD_4!Mmf4nrN zN{{kP5emZVN8dJ-cVRS($ggLJ2`$;jq!n-#&3Q}z>z$R963&<)oyp-EMx&q0x1G9l z)3$0paMk&>Pd3J6RLm>|hGdG|o4a4GO|tG0m*rjGDQc1N8qy(Xy1jqcL7u8?K&c>H zfC`msPAlX;yQ3ahhE`1wBP)y@bG*nDoDty4NOJJ-tjbOFZ|QGyL^X+4Iph#^Zp*e?t^5MMHXxzEEzb7-DuKmF9&FpGtpCwhAk zb)tkN^y8ol*$~K)Y-Q3LJ#dY;fEmOlfN0Fe_PZ;(+_uxctG`dt6Kym<0t-ZE) z!xB9q+5w~jG6cBBgdx}%<;)}EApJVw$$O9tunb=Js_#q8M&J?2@%utlt6wF0(IfbDS4SPcA$mIZ+ z7xsRo0a0X{6+i&<*+=RR_&_xB1=f7qA~Hjkf^*8l0AQA^JT4WcQz-Itz~>B1qeB3a zz@WsH0y2fE%pkcI33y3h!_-+-dk@w28I$<%4hbkFPMMcP2v9|Arh|b9cT9~(@c!+E zQ8wco?5s5(mA8DcU)Bf<75c!vyX%r);d5L?t~SgceqzG4n?A$=fOyig4#I zJYLxaA0=i{2U5GezaqpE0>A!jWhrLN6p5JH%R3QLKqKSbv9D%77)_>0OQ`WWWC*5c zM~+sRVF2ZY>*|##^veJIj!FpCVq|tS?t7)|Ptcy)ThRuUj`j*BWX;jr%?#Z$aG#U;4lf7 zgQ%P%pTPK=KvhEC-{OF#F2K#^Rnl`@k&vbk2&WSnsmHYJq z4I^0+pdC%M`-Vy*vBac{*4J`RZf5|G<_{h%T?hy{cTJ5j?p8o=G4>(_5LFWR@}7%p ztrk)foVvOMQmvWXjPU1J(AxPy`4m7Z}h>mf)pVYRT-lN

    RGXVK+7Kt%Sv~2D;Vh+cr*s-2sqI>5QL+ca@VsP$*03aat|1!*If zjK@n3705)=@S^Vtf~LH2OOIgv7Yp=SL;)`r!_=3aTzdbdrPOl)#&OrPB|rB|`a@mz ziYfyX+!r`OVF?!_73gC83^O-xIcS$-;OCuoNM0@>_~qq541=8FGrL<( zyZ{Te`Fr!=ir57qD+@dk7!nfd|s5Id?AzVKLm8YW=kHT`=_$Vi4ud8!wbF{f0nSQZ|rH)J!h zxO~IxGq-^$%YvL3A9ff8=ghqTq*@rqGy7)0{a0i?M-P`dYlMVL?GT-d}lv-IE$yfxC%r8Ds)|QH% z1*Ubvg;GjTn_`x#k7drCPV(tatusYai0^motSru~G*#PsgGtnxc~cECd99^^PQ7$X zGiQE{HMt89~xq0>U9@zZpMYq?+6T&H`xxWCb6V=-*VVU~@oL|v_bolJ zyZWT`KGG-^9ECEtJ99PP3rp|YULS*p&#kJ9e)Xx6mWFc}i&8*&(faZQ7c*!fkZPF3 zf!k`w**Bh@DH5^7CzlQcy8X6wb!(Fhqlqbv3#3WcwUo3o!3U{wHgZbNDcZ4b z>A(Kb8t_9y0yUzI04AaS{9`o~+_$S93cRC~CH`?lQ)+aKw4+(mtDJnlb7#G2%c<3; z)S^!u+|pl-iJfklLq}>MH9MYyCoO|mXrjnv%7hU9^xjzz6hvEKYiYihV;}*W(_>@2 z0@&N%qPp2F*=t6KU+aO_jleKN`W5FFJ7S}Xp^v}}3uyk*RdrYLks=X8=Kf`jT8437 zId`<$%jaX)o&&ubC3iqgND$9k9~H3 z6%xa8Uq{NWqHoTIN4J18&|Z+di=6=e?q};we-cEgYWo2}NOhaOM_}^xD`hlng8?q1 zG(Ivc$L3@GDg(?Qe(9B^r(Udb6b<~a0f_JjW`GO1)NK?7 zOLXP(=y84+@p!Ou?FqtE!nZH&ErhqkyOZ!k4ks6|PQqIcGAq;89aXA z{(+aM4EpKn)qo)xMELa5;Xw|@%pg@`^=d%0IN!IsrjLPOzu>azwfzfl+Q{+)>V!KNMj!DXY|WBQJ2LpR_R40 zt*s{nC5Ub0!$cP{CSjzGD1{)&DYElwEl)Z(8iE}1Wppn|x;i13;F%6)N#)$$ma<2J z+W8d>v4ogan~cgl2_al9r{G;?y~$-Xmg=|H+lx1BE^}3VvVRVr((-HmtNF@OCbtG5 zyI(GX+ny@d(#pv^(db|Z?%myQDs?XHl0awqd0K=Ga@r$O?tV2g+j-2G;hq1{7nXi< zd##~Vo6i4+&nb!s0TiKkxP+u4rjn+7^~UO}_wTN}7bODGhEL-v%M4{u|IPbm+p;kJ z;iI*OD8dEJeM(eRND(jTZ4u%?)F=A$^PlQS60XrX#ZCp6kblyzgYl?(-Td7vhgx1R zLoG|N2$}**;!gLP%BE!3=dGz>$Ef~v?=g|voN;PpFE?+i%jjZ^av~#c4yutov8TQd zxcTf_y<%;=2G#EU&LdvOu9qsv>71-e(n}^cdGUPr>bRVi)d$MumPRi;dNSd4BxgdP z#0{P$>6X=k?WunXabGlG{>F{9=-`qN_db6SQ{*yX(eB&ME=4zQoo&FZn`PcDF(MP> ze|vAS1WCTKs%tjQ)|cJCUwgWweYC%`W>}`>vU7_|aNHxfzVYl#^_AzB;|kabaU|5) zprkhIK2!jVfbT%8cy#Zsx@~nNNZlJH30jclZo@-Iie|^YlEVwJRZ9p50htP4c)Yrw zf-1(Rz_vtHL65zq*Whx)mL=WC(rOA0#Nv-;&FR&hNDzTv?A~aUfG?W%4w>7{_^gAu z4NK#;UhnHb5r`%9FCVTm8*ovbkwkDTAiqwiOJsCPry^5q-Cs43zWlAtn1np=+{|m2SM<6J5COIk!u_?UcR%Uq zVMs$@apUB-UzLBU??l}-<1Ml@=q#j{*D}+`uJ0hcP>(!=CygXvK>$#uSnL+?rAjpup6P<^I7%zC=oo9+hKM5z{6R1>8IrshQimCIprPO zYK{jBMjp=wpuB(?yNyQG>^%Ql&FjK~K$m$2PXR_Izovf_CiDw5qdf2-%MnnOuaf}C zP^K2~nB2fmg^b5AFa^)dZve`gg7j=@1@N>pT}CtejC!Z{(QZnYkJo}E)gV=x8w;y4 zmtqXe|cBQ3~wfIAwzSxQVXxwR&dXu<`LSd?H)=H#E5xP!CLD9YD* zYHp@Z6iQ_NB~UI+*`tfJUHj|pu>5d1z~Z2y{>s{2^rCeo}t6_=TXCJ8-22pNWyR^M0@yh5OR5KtuKG+j~W2!0wWmbTBlyXG()7BTRuQK@6 z-UlSkPwuVd1|W_9FMp)u5JJ>fJ9$>vjLAdPF0naeF} z>yOxMLqHC^;IJQWHF{?qdM|aLZ0>H4(&09T*WBpS;ei6bp6{jDh&3{#ip>A>dukYQ zNzRjd>Vw~sI+vyElwwiHEx>HqV>BK9k&6fdQI>C}etFXY2!$aK*gKdkHVCpnDJwNh|_n5d}j6tM#xHa`-{x;H&xCb=~W*1JDw?P%hXe9nM1EA?fvrX z+B52{YPVIE_O9DpV_gCG-ZGo%Pq#_{3dBXEMDxtg%Hl3&plfA(WU3qpA+7(sT!RN z*wJ6ot0W3_?!Uy)5?!FijSe74+o>WPak?U89vl^ucHCL5LIjeN9Z8<=pAhx_q>(3`GDI;(me~yiIkV`NG)|=5i#mbUF@QFYOA}x53GEdj`bSQXd zv!h)v&4_^lnv@4`%J3kVfd^AEFfOx@p*)Vf{|E+6#18EPGhpCBxCZY6z%MjM;E6r6 z3F>lKkakS515i#4TMoCAy8y={J09<#KM~&ugtshXg;kv-s+Wkwudx8Je4-(U9Mta!ZLlHr7Gv? zb96ZzPaSYM#f-3I^~yXWvyg@7w6fXlbnn4)f7GJ{P<|GnEjT zAm>y$hig3SVax^~Z{JZtSCu0mi#7|)R8N95l@J=2!{t2nLtw6EHW@rhwy6yl+e{hB z%=|SU^NNFb)tVwln#-KS2(XxOI#c?E$Lk?FCWX(=Cufm^(F_}bZTk&{Pd`#_4q}~I{s~Ve44G^w`3LKJI;Z!n z03Vx12hUV370Avd43^hkf4o>kc!g_rx=&aRDbQN&vKEEtg#0vOgBCA4EH?JpT8 z8v&}W+f-$~u-~O-MFzANUPuu8;l%+sqjw39tTrQbs;TGemhf4yz#D_%4;`&&3oK+J zCIObKvrvH?HfTi6^u;I2k+1avheEpOA3oZV>=*jvX-0`yf*=;^HhCMGsP}I#Y0i5O z)y@~=Kyr0eG~Ty+_C_9q@jmrp&0=BniZY{r8ec{lk2rF3XN=Uva&lZMRMyu^V9Ai{ zc9NkA5p$Y(i%zjSr2)R3$} z|H73e{O)IKw+ni5oXNqZKj<*Ro;Lc(<>fCSD1`31&80}PjRhr}G3G5HD)f!$O$*+< zwN|WC7GhDa+@8Mm$MCpxqza(;w>YAVi3fIe+8_Fq#{b95+ZWjzBu1 zv_7rfm&`*GPr<{_*X{{ov-I<>3(G;gH}~HIVx9t*Vpz4j5Pm`T7Kh8+J_#~A6P(%^+V9*^BxoV#5g~u{M6JQSb4xvv%F~Pk{qVh^ z_s{fQ|bpIK{ivT^F)!u zjX+%_$0 zsW7EEW_i$z%K_8!6mH?s$~^xBZBXbB}oV(}(igu`Qmbg66_dQtzrn`?eaM{nFxlTqhSeWNyB z-FiXw($%Xg5&GPtm7K^TihgP7y_d|QN)&e_du8XmQOc^mJ)NbIS>gKRZL^#K{;Kn9 z|BO!gzK(ZuaQ){j)2hQL_ICsED-d-6j z46<^n2Z!hO&+ZO*Le}=MMu_`SH$x*b5BDMw%UP>Le%cy~qt*g?UxqlxFI6U#?yt)? z)XtZ$JXMj*Ppu^a4j#GnFjNfZAGBfyAw#(PmupyJbA8XQ*Fj{nSrK@s4Fg0WBgZB7 ztnm@zP>Atr4R=9;E6<;OVjMVAL13}>3ZqLPGOQZ5A=8LV+WAr;37oz8*eges@V9H^ zM5aMlM(wUA)+HKkk^ADKm)F{Q)+I^4yyNfIL-xz>oApv<&X&>+uY-iA=2-zzY`_Jn z3+c%)XasQ6S*1GckP0KD7p$8#AF`-0swf!4$zhKnh_fP30$8fYAXe zEC**V0CJ7xE}hG^UI}uU=wpRQ4XZF>gt+(eMOf*Cw z0$mE}tNrzK_uXe6nJwGpc=E+s|4JG!1x+PERl_SVYnSMd@YThl{Kq3TY9% z?&xd#?NJ8M1*yF8tlG$VTfZ_HYeiqdIl4S>4<0Q!Si~?NdEB_B99OGD{@*`c131tM z1Tq69dY2W?_JUszKEHI!)-o)PvkC$C-C3_T4a$TRJ}bP0xGZBjV?4F@QaQMpL2TBw zdJ3cv3G!hq!pLQZvNM{WxTJbr-j3G_#&bYUo;!G}QCj1G&Hed&ks$$;G2NYh_UbZN z$8e{RrcD0LTW3~z$}^TRf_yNl? zATmvrwO}sEuNB+5uB;mdj>$fdX}IITF-@AnF{{$(Q$QBV1$8la1^H7r5wfYZ%js9H zU4WGxUk=A4esil=;Ek?corLdyIJtmz5*FbN4KXJO^S=>(Z{T?2=C|YN%46}g>X>B5 zW3SA=pO>i(nOTKNd@#zU2rFoZR~sQ`o!);AdRu+rd&qP?9&f~sTDnYATrQ|H4;%Z0 zQ|n0|487NH?uunT=CL|3YBSwY7|-TWE5QgvK7vSdkONLzUGIP>$J0P)@_xaf4}>z8 zqmVOQFu7#UV98X^r757?a&3RXYRlTvOE@qqmF;UX_~+XUa?V&?lTN%20W{Z-D_Ok? zjWgdf-9-F*57#gE)mlSO0W&p>8Rmi{$dDz*s?rTexNv2*NW^vqN#D1tJ3 z+iZ5ex__2Cz~zcY|7AOtXQ>D#aY=1fE1YQ|4JCSoqMUDnD8mkxVY0Zy*q%h-IOMo%9?m4sw+7=5y&IEL78?BR$+(1tUD?1VpW~2)z1QtsRK!FJ4!-m9Y-; zBmKRwknKA(`xAPMiK+0@MSPURrj`n|dY94C5TSr9TUQiTg|2UZTn_9YkxaB1!2Y;H z+QB2ePP(SnMr)<7mtgiD?BDFmDon4eQL3QdlI+vU62+v!*}1e58;F*sNo} zZ+i{$+s~<5fo1sv8sB1uvfdv)Ygt> z!~A_Wl4B2nj9j&*p^nW}vxeclFYciHv7GNJEno zd&v0@57ehEk(H6J)!H}v^RY`%G07re_x~6APh22pT2g+zTZH`riwwp=Aa?NnOY1L5 z6|P1`va(0v(O0WA4UeLSHxLIo0(tkdwQh?>Hv?3dIOQ`>Y0YsakUBj?l|xMU7+q>P z1%n1AWK#%xadpIM8qk-(s1kuNK?V<|WpKRmfFNVAvO>=6;rqZ0h*fc63R9W^kchK- zmCI3x*$cuKAT+NFgqXJJCsX1q;;#(Ly*M5abt0CWIIJw~4e2sAz9IUBnCE5o?>?MJ z1D%BL37lNO`tu4#%2A>T>x{7Vb#>aQ80}@@t~PIt~>s4xq|yc^$Jm% z;zQ=FO~H6p!K6HqNyM2p%$p!HbeKGp8x2 zl4+wvHjK|i(A**hKlaU94VgL;`Cz&n1|lcwfF#88K9Ya;p;_i5nK7k9V=89OF<8Qt zdEt-{?}lwI$DbB4n&6Uo&8FF9xFXPGR00sji`IXuO2HA0S-k>d76l&zO2~X~5y+Wd z9%KqQ!yl|K(caweYnZ+#Tp%{+u3z6-e?Om!<-*1_{Z)}e^+{SYZ!EkZ{F;qbHc=>h z`u!18c?}_k0~pNf`*&Arp-dpl$xJ>I*ff8o!ps%BJ4+od)+!@r%xqHIe=RrLbw;W0 znZ@$XEVg#v_Z}!(SS2ww1(CxjBTF8r_SK#u;6?0AZ0w;TnTbMzwqsv?aV8&cx^YVp z7uP(t2m-(VaJ>umj!l&*v1Ay@Qx}NY!wl}z#S&F${`iSXYYCL_ znmid97c%n1Qv}LcJ#qPymoELry=4Xo3@hQiTMd+!UT(XxS852PT|x7KEJT*B`DQu| zEe*bYOT9EV9IuYlEHDvRmQQwX$s1jAP#GB`UD6fzoh3Q>fJN=9=wC?G(8MwoOFqw( zl?4D#mU85icIVFObqNa$$>^!W!c#5-^e*#y6l95QPqA~2C>SjMDd-r=ckU8F`0y1KCJbp!v%hKD6P&x*g%8Bj8=ajS0 zAE?zUNMw8FgCt-fcD|D^))|%c$&>+N(Lm#zxAPNcraFh9glsDUn1oK3c(7NZBM`n~ z+fDCM>uVQP4?I^AU}|}yM=*w+d1`flS6}O8f~I%CM`Ql15?|=b$8mHy9XmM*vHK4% z{oDJBMr`>dlH4Iv({qFtW9;ufJo_LWcW~uwSa##YW>wTVKk;HUw{f^)(l=dt0(E+z z#>(K}5QL)XPgqi)cO_er1#K6*>(!KL52zkYW@nPJt+J>F&y z5zD_exe=MHv(zZm!7FVPTcogVK~J|eUeNP95Js{%pZKZ#9hzU{>>{k6tCxFo&||2 zmdN2tTFL=1!kbnJxFP8B<|R}Ayn9#KMo1R*kWmzv3W*sCv?Ts0flDpoQdfLfB74vNcyJ=Zrv%HNC}4%4P36a`{PxzHfFWmU z&jLhU&MD*pKV=yN16+bBUWz{cY6Rd}p4F>BD05^^JdhygW6nrIoG^wAWF7+|A?i{) zB?I9#1CxV2y|0qGsp??>7&%5@K~A3c(emh?66n!xwyFS5pMRr=RS`0sRRp4U$n;>A zB$&A10%pSp`WL3MRuYKD>o(VG^O-&lc4aeHGJ{E3Ci7z#m1LI;OIV7ya~QU{%o%aM zYa2?KI0By7QzUe_7L)=C+mCmf`i6HKlaG{W2~j%By9Y0#7B77?nvTijka6C}<>*kSr2$M5FLMnU8>^MI_roeS5#$hxo=VOFQ?^ zq79ziU#Uy{lA3BDu47ut+ObU4X5-%TfZLYl4R=WGA?adTNt8b$O?o!-JgsJ zBQ12PN=RX(6J=_@nX3z_Rff@fvwY)H&Iu2wP{kg4KmBmoNz|tPH7)|*bMfr=<`Iab z)~x1efZ~5;9?rG2}n9ccy5=n#z6=sB-ArfA5xBja3E+ z$luDWld^(A>1O+AER4t}`er(9S}lMn54T zVa!LSB9OsqsiQ^Dynb^nDGHSC;TD6W0xw46)ie8Mjzl1u+&C^vTBmUNElb@`*#Vr1 zGUmTyWBrLsgW}5BAf~p$-ai(R&O@>?JQ`)kvoFx+2vM+Q7fG#u3EgOMl!fp?(HT?w=E zrFyT>fVt)D+HrPd{_O&X8~hxDK)(r22x0;=m^%Zv$-YcGO~6y99bw+YjLEClByoP_0Yaslfk zya8BwOnDQ-4VG^WSjS)T2j3z)v2krmbK>+7o+EJL!{`E~%RCfKxHJ0+GBne33WI)t z=bs736x4=t%mA{2Gav+0n99uT6F85DD_F2RFn#ncn4JP77?ywq7eZt18)Sr}fGd|E zmjUJV{Y9$mLU%J@e%7U*A)C4seN! zOtq0_8v4qswH^eY?CXRSycs@nLWIF zQ$q5A%Zi>Z7&=(mb%If?=)u&g(A(FjI5}Aap$@ETbsxU0 zwiJ=@!OJRPM)~8to(mE{re@g)RI44F{n?yqLC(rWt(tX+5J!=)D1#?nEEV!UdOMr@e_c3jO<$>KxSDfGN zdtEU~5jXccp%@84rs}l*behWqlLWQm{M!%CBJ7fF{Qc+^OTT`15&ZbY6;B3;IU($f zJpgg!!#lI;l_S08GGD@jul~@6Vy72U6$A^30vpe&Cc~mFzVLVz44zW-GfpjgLZhlS zU3TZh4q_@#f9w9bR8F9PJnCyZ>OJN9?Xb?n!&p|FQcF$-;WVg&BU&@H@da^|!h})j zx=m%UTJWP6)_c=v#2ouQyQ)q3t}H*_cc`8M%K8#|2O}TsugjC4yW{N}$|J#{!r+h} zc@Q5x;fCIuse*$XGz9T;cGBb*1ea)FVk^(g-0eoi<|;X%1C-mjb7L z;*#Do*zYpCyU`Gy16f(yO)R8@-~wor9khJu$!ah7PxhD8Amg1XN0x`2KmDiGuiO{c z^c!k!t7;B~Z`P`rF#*J763VdtlLlsHi|5yO)=fbmU$V@HjOxGgj%u~UtyZ#Ls7DfZ zW*hMi9jRvJksE=J=l&Uf& zFyrPN3TObBNZW|=)C@% zmf9>x<-703jf?va4+scH9F{aaP)HcXD%wdyz>`#sgBR??<#0IwG2Yrn+JE)Y!(Tj9 z0@1rW;kmXyi>|)ZTuP$ zz#e>rWVJc~qbE4KQ`Q{oH;7b$T$1LWJ<$px&F z@CIOEOnDO{)Cw#Ezp~g9*SNAAQ>E--8GwE~#s-|Ay80`6PvQq)3~#0afgvrnO)VVs z1M+-5Cgp%B-pn#T^))3>&Lj^$-i093m%x;M0T6qBn=hjG7&F(5G=M9Q_6Ur8m?_c$ zg2~4hLKgNwYC)=Mn%FPI1?lWim9m1_&w}wJU<_q2qX1$_6*>V(IdA$X#}^XJCP8Wg zF)YGsM%a@G7zJ4(#v@0YZmtofLrSoxziMH+9A4+ut7fy4srw`lYj*qa3$-=ljQ%o3 zSV9(pU+h^v{OCx?U{-Q*mIX%isx3<;l71fz@cZBM1(FmxZ=+)OsxI{&*1D*tD zm_tXGe)@`~|LJSJS)=nS*KSnUC?-=hvetUy-SDtEBN~l*Y}w7>16_o zkqd2MVLH@ujOL#GN~R7I;Vu{-$K42m&y2FXmx&#^B|w}2ibL$D0y{2=z4$mArDX-{ z&nzKo!EfGIc{PDz`0@T`6Cv-uuwM1rxVF4tb5s+hD#53MKzK-cFF3mr9`^+T2LfaW zZhxj^%I$%)IZGrm_L*NxRV$t`JN76CgP3aybSbdpqII>n`PHXp9=T_EK}%L!2+Sgd z0wj;_+*t%NpWFHaWK^X20QR9mVr&$0iJf6v&MqW@&+IEXtTGBE;a4UHP3#N?O&0RF z=AU!85U`|<-URohC#qaD2)b9N@N9qf9^MejS~UE1`Rz5i>ud4;F+z58(m$(yWQ^ZkH^P=^wo$3yie9JF^vw$3)4fox)&We(m{*S&Hg^+fZe^lgIy|$M zRftt-H8jdE zondq^?GA9cbQT|sMlqLR3Enxv0lk7u^}o8idYaLOz|l$2M1F<3pIpLg{8U!U{ules zLYCkRNO-}hfuEv?B%?M?h#G zr;tnM2n@m0{UAd#Fpfw=7EA~T(GV5YhJnAJ@5%vKhW(BqL1+8oliy-DyI9-r#Rmr2 zg5JQW8puKB45`VgpU+hTk0g)U1sJPWVAM_(y)G;m_9kFK!U?Wk8Fuu~W5SzTy#lZ6 z$o^Lr{{TPW+mFLZ_+fyP3s@&%29g%f=>;%wuP~0RP<6Zk=y)ts`sFlg$CG1#yn4C8 zQ#hWkz{2VkK)+!2i4UXZMhh_IT`nh=Igi>D7Ko>mz?@?|q`|=B+TQquK$&>eo`?x$ z3WMyJN&yU-JilpFmaInFU{MQ3Ip@d;(#%OAy0UteDxe&iFlr8I*g!6Lp`1%+#yBSq zq0ekmc{*!J6&RwN2Qq(zl%M1~FDA`3vz+KvPTpAazxj$!rn3hQ*Cjydn9XJT^0d`U zPrq0mN$r9m$K|izJ>wb(d~;|raFvPAkdiv{sPVYGgp@6hkK>w`IpN_jKEp>1DDmXAW8!A&fcq)g^w>kS!a@I z7YF^y_(=;$uTZOs`|P~6(@q!`3PplUK4X*-w`Y|D)Grvc7bX1M`uv$HWKZ<#A?{gt z9p>ZLR~$&V;P*d9arJ^_}20AgU1cX8sWw9~+P8Q1q2ZLSYpB0#XBptARmlFL zqOs-Tb7sHHXvEOj^2q|CgiGWT{QvhK)zbk~)AuUU&tFr+CfA5cqh!yWD(b4E9;jV) zv;)%kMFn;=2CLfP)qkr1FfSgeQ0tAl`UB6^$~&d$q6xc80!4=L>v|4w@9vrVjF{wposA_5$Ot6gT?v%`yDu(XeSXa`Jg0v3w&H?BABZ%Jsz{){MD-qF zLJ}yKF@q!82z#mSo}iyND?honS~ivkAmhbMS4SDfjTv`_89A#mzGVlHGI9opo^pH_ zZO(FBl{Nz8D60yK+8C2vYzo0I2UREm)d+G1hYhU2xuN%#SPlM7hi*YFnJ zd()2s9utna81$yKV|;wiY?V1P@xljm;snoU_!&N=L1SrBd# z0H(yDM3(fdE_>!wlkfR#X3RDM!I)bpm}HocWlrYU_0o)NI{7G>FVuWdS~xDL2ukgPo|_MWE#hL39nuAtV|}&OKdo-~N7+8Eq{q%-5>MK8x`3T0t#=pLt~VJ4z8kAQp!zmk&K( zbGw)n7i1l@NF>Mvslr7NRD9R$zb}Z2cLtk3+>6hjN^)NM=2*b51f@MGGW>2(H^)OAjvD@JqcCL{zOjwlpj- z*KX= zs-;UJl*nJazIu_`(a`Y@8TZjV@#43tWUX33ts`$ODZFz_zZ&y&$@~jf)^^AckeOr* z^wKM}I1Zzh zQ_{A7eNXkM5VI4z%cu8ti_eD?1xmG1?#G1sr>?dFvAY-U*iq6z*10Gm@Du&%F>P__IdwO|XugkKUr1i;-oNz4Cu*_#oHJ`H zqTvOBkp9B8MSP%tk}t-W8ND`Iw=gs2E2M}KTwy?)uXHm8jXTs7=(eZp*mCyFcz%(e zPfEGbo_w*Ejrg)^RoSEU-Iy6p4<0E>D#)khCoe6eu}FufE)8T{62pT>i&5JN$ltuN z&KMb-HqD^=;mb-+!iZU|o`W|u1gfgY%*TgvWT|}DWRTRsq$!_!xi;P0xufKeNu9_G zJJ&LUy?#sWG&BIle)RBUKE1ErbI)^-wBNXAwt1OM2O7~<1=8U~?COp6F5bqo>JpMb zz~)F6mU4E!wHf}$E-C_}Sv^g93ouXxLPM`J_R%dJFfL!|ZLNeT>p%JFK($nZkU7Z7 z_rd_}JPr~l0Ob%ns}`2Qm~-L#=VD$uG<&xlhDj8~L2$-lCb$#U&DufboQ6nhwtk^lMHl?OCaG znM?zHauO_Z?<2kTwEdi=Pe0P{F7@)n#x=F95UAGaH4c)s?@ilgFNrWos$XxGb;}Ob zi7aX*1|PL>59feN4O4x`|lhSuUP0ZPq^4W3nrElm7H|Jg?>VpP0;`)t`uD)OZ# zYqN>`hb|hkRAH7M3#a3Zfd>1?(n)~|O3M<7cYnNw87?sjCPoCRghB^C zeg2D|#%K0OKji0|`ZYIXpiJ#lhiYYvq6ZWt#?SAsPEICX7{+K*VNCN;!P&6K<`4G= zailA^KU4ddV3JeeRdyz)TBrA|NRof$^L0*hkJ_XC*B8@$sUsDid8BkvA#wV~vn9=C zB*-eWy5!Ch0lhAZCh7LC_WNJqWh4Tl>ouEdK~V6qJHQB+!v4cEGsx5{^jM&+W$cgi zpYB8X{DJy=G7I$zT9}bW5e;64OhBBuBj!JIWpxtev<_paC{a<}XFv|6vC8dybHCAv zSpN3>%QF_|#41YzwD1U$QiizipR{NEhO^2?7iur>o=koMrB)BoE);#e z*OmCi^3Wi_;&Az+7uKhap6g{(py?lep?;1~ReUEZts}6E`R8{meXs{r{%YK-`y2dc zo!;v&y?puAr)GD(i80qf?I^Vsan=9~dy5H4aNoz-H= z>J^M~Fb3zTWPo5`6jF8;RdX{1kAu+U9D*pF@`50zpmt1&vuHA9nbODpW5Q4h2K&Ga zh=YLz!=_csq3!w>7%q+n(+EtoL7Em4Uo)xROcnKm3>^H zHdPDHrv+GGKc+Oum_4n1^2M5$o7o(bB~Xm>nUm0Y2BXZe5C@c*_ekDSHEM<0gfCi- z$ua=hAkKtousC}@j#-o=ylF?ZLt2%;Sue+B^(vSgQ&44c6J3G;zQz7RFERArB!khE zv?EqFdyA3ELjrq3=|CCtoRb5P?{fUPo_PwfP1T0}k)7cYKogcI1Kr(fbPZBUnL|sZ1Mu-5GSW7SavCtx`lr|u%WFtSS)yrX0`Y%0My0Aag>;Pp+ zIHfi&DIms`ANN$2Ieh-{dTA^2$s`|`MziGJ-DLo2@S-Z`mm1-Ml&_j_$D=sNHo%f! z?O%ta_x5Kh{xBs?cHNn<<_MPp3~3Qag@T+)#Mt@YexXuml3{-3^Hr9~vxIq%>?z8! z^OX9tKRJK(9hHB6=8>7e3SDQy3ggVv2c9nr6{0aT5}TJ^DPP#nW=VClL=LOK1!q@! z&mFd5{A9n1LQcJtc%TxhvbklFK%g=_$A9;sIuqzpw$BT9rK}Y@N(0Wvudi@{YMOpa zh|*8oItv;(dCM457MBilNxx0`TzB&L1sOXdBn4HD-+#Ch>^24$vIqLBKqabaqLJlk zK84HMM~AliC-X0_OWqD2t=|Is+c(uB0!G@aZ-297#$QOTVWjBx9fdjntU4q8;AKny z>zy-$LlX%=@W1+0*`WJLXeFp3Ln+JgqI6z+dMy-|q64*-gwahzf=h;}Rd#3~{fc}o zk_C%D?WT9en`IG5W-GV_9_t^n)JBjIt}lg)3g@)lv;6{W;-i*HX+by0rynUY$oN9C z0z6`6ekC&CwfPSh27;7D5LLIvQvEU`h?WaFm8Bw$o8hmz6ai%W8&r+!YkhEX@lj@d?$H)u)P}Q#e z#m-2F7`ju$P}%UJKqi_*iOozk{+xbl70^(z>8v8a_)}L@kJSNOiYJT$30n5IA1wOV z%zfrzR2HZ$Ys)0WWha{6-laCe>2(%iw-x%!`*kzKTrf~;+0#~+G@)dhiIb^_+5}T$ z;eGjVtsuKZmQF=-96WFSR=6@yfM=)3gA!XFbcgIa2!ZPa#O-!V|Imlpe8Gp1yjre6 z-Shn`C>YOMQ=6*vim%T75~Z#E_4g9kdc{G+jJIY|5BK^|xmnUf9FOmreW)Q?@Uou`p?6k=ESC|h zY-ID`hy(`+4KH#T`T>!XGXghA)bTWw`2s1>NfIRxmIYBkE-z0Z$f?F)Xw2onfI&9S zqiQ)!s}{(nGT#J5H5fu*MkrQ*&=2+nSU@h1vYh^lPt;EGzzWh-#f*he zOp#MOSRgigKCA!4m*-;eAuyfF#A~oj&w()JJlqKknm{h`A|984F$a?9=9j7hgGC|d z=MVIctk0`v1m7&it?Oz*BuFYu4V@iaaE6=_x15@_^dga}I4*4%e;_hrQ-qIU=2e-e z=8^=H)O?v@z|aC}vN+#@gfa8KOqYiZ;l+Z0*dZLK1+fjFgi&VjRAWTtJXB}Wuk64@ zwYe#zoTOfG>l8v>blR$7q0}X$Ba_zvEkiXh0cAP!QV2fQI?G|IY94Ct5EVtrvbdBL z2#_pXg@;a1B31pi)^J8b7w1gmKjz~W^>-?eqW(`Pc zwC|O2oDepn_YXaM=>rRpMStx2(#{c3?R!dn^%=ph_Kz_+xDYL>6n5+@;#J*;WnUk-tne5su{l_4 zXCk92DiX$80+_EwlAu=kmi{3;8Bz2yPbX(*n%J4fN@#Vkj>AX#{WZX5jFFS$$-T9Y z>F2H~vQ*)9mSac}$3jCH)2Tb{^de@PJSrN2YDtO)IGD+V1+H126|CjocSaQ)UNmGi8-jrj5 zLrh@)`t|k0mxM=Mi1XSAe?>23)kX(uxkN$7NQw(06B%Mb+jQ1!ZxAvt?A-hpcg`$#CM4}>l+L1_GSv?B@4B$$12<9aFzwJO zymGWeG5g6E%k0lSQVZzeP3tJIoDc<+H*Q(_$A47&k6>aFpckd~N*oj}?@em3yo0RY ztz*W+wOo=X8tti407qY~sJ_r&ql-Oh6kUkR5B8?Tjc3(U53e#VR)MgmEtGNTw921( zqy~qAQ)*EUzW%DwoeeP+@R07WKw)}th!^1R-dtI_yW?w5SCi>x;zKDddU)<=CVl0p z8a#ufch#zL5U8b#*Q?tAJun-=G%*zwr4H}W-q>~NxwSIr_93uVlV7Mu|4&}J^lNvO z%cXQpN&BP6m)?DGsix@Qk(#9>sv=np+E2ac-TO)nQ_uJJAJme3+UioI3eCT{w+2$c zc)}rZF(!o!!T{r;qxB8JtQV^qwF&`cHsA%2sjA=Mo$sPj3rQ)b6w8gCk|*{mux`jPr^y~et?q;SU(70GCCfs z-#(a?dd!~)yzT({hF~gcL9A)n^ttl7dP6W>xp-Ze3S1CA9s?^V9VSg}JEpZ+y;?v5 z8L#;Ph^HWD+NaN%ud3;zI#c0Mo5JjJ@(@bFbV|W7K8_f?dAx~H!2BtbtpUMA zc#c@>7&Ehgw0t^=F&#I386Ri>Yfk%CZGdp|b-gjGSAqB#MPT{-#yLWUs1Z+)?l350 z5(h&-X6CYk9w5k!Ikn*mjY7HEuaB7=i(y(2sErDt4zIE^gW;V;fM3DHi>LS2H-?Y} z!agohf)dzsHR%vwfCe(0kv6!GwV4W$IYRbwgj6I1LALks*0}2Ty zM)O^{mfYB-IJ;z$jPA@+XZflgD_zGjbD?t;cw(6}Qa-msCijGZ550XR3L`SctT}z@ z|NG@)gaLo>ShdkjTPtCcA+rQ;#d*G32VAB{MF?pXx?b5)mEa?A>9}iWEw9XkXowf; zJI8Dj{D+=j+HS0JY{&sL#8Cd|u6N?^PITKpe>w8{op!9h6=_P8%_ z3Gc7>_7-;HC92x7lg6(;RrnlOV2Db@%EFE?6{$Vizd5kJ6UZOAaJFLdmHuu$BT1Hc z&s2BMfnk+|e9PHY<&Hy{wzy%->;p{70Fsu}=7q#))D5zGOZOuR5-<{(<-IXR195gcLrae)j4PY_r78>}t7hch!a^ww=GG-WqdjyL@6#z2k=XnSHhTkXGX| zGGZmPJS7&ufHV`|S@ng->XykukG43cZ#o#YS&nB$xYTBhQeei9URWb=#8NHJvH9^m zrS4zfQ)N90i~ASMM0MX2E}a?6uiaIzhEepc3u`q5BQx}nJNu1wrd)k~jdBUa`ZH=7 z@#byCs38c{bN>4;m6mn+jmvFIKe@eb9~h$wFWheK+)ZbdST{SRM_w&a>GSVy7gS+^ zp@pfJ_BxAO9sx}HVQ5sjp~$(c-#JY*NTWg=^tYWa)uRDE_uXSJmJo$%@qQds=$gwV znQE&|GmgzSYz9&YSq)J0*XNaCN?N<1|2+JBHSu-5m6EB{p^Qlg5WAxVTsH&p#XXW; zv_=@g2NBB*7|l)8v-?Y_LO%Ye7B8_dS|3o2ix~#MJkay@D+!SX^Becp^0y}2t_SwB zHChNk8AWcopT4p#Iptg~bWd{z!mF$mP&%+^6r$=Ajc3*!M%J9>yZ}-ODJP=RL8=o2$y60g)+!dFp>m<6o2Xn+1PWxK&l`W20PH1gHe@Q z=V5tZYQuo6UIhj|o(|9}QZ)!lQU9tYh`I$>pjTK27gGpVfP82FNKjOmx*`6;<24V?ULKdRM5tyZ!--CJ`tfz70|H|iQ`J1`LEB(uZ@X;7KsvMAv$1Z3u9H@nZ zFFje4dvZ+T-Ekc@uK|Y_Z7`uJC|O0wSvqrRfz_@@e4RhInFw*RHS_8&Pu?TAu*sSKK}ND zB^;2$s${%aZr)lhR8=p;#3R1nx~+bde9h@IA=E`+aw=w^j9;oCpL@A3Km7J;T)hR0 z6su??uKhCC=Cg~ki2D+;GC3&uE;}{nduDIp!Pc{Ds{#pG!(xx%ZKNky01=Y;cf^0@i2%toQVSMM7aswnITKZP^BQkm!4O{Df1BAyL;(|&2{5oi3BYA z$u0dcsR#jC(1}Jgmv5*Mo&@E1ktqR|2m$2i2{17urluB>gV^~CNZ<~#Fw)}SY%QVo z<&Zp9uRuhB*a?D(U%4DGRS&Qn;9i6hHF zqX$B8Oi{5R3|+xJ&?#2i4-oC-_dU{)`}e=iub()wcr%$dmC4 zoLs>AVS(cf>&Ju@M$eTY-w-)LIG#No)e_^k5T=TjkIpNEWdzOn1OsSB$oS>Nxr86n zhy@5sj^n|hqcz73!IUiR1+$>nC4a~4B%t6}9*60}+Y@?R4qoS~>fGSZ5I4b81#n64 zcoL7=?Y&>ACXVOT>Q$oQID4{irZCJ_7&9}MvZ)lDNl=?A5SCmbFzZ#w{47Y2iD}9T z_v|XYFCVU{Qf7F8hzesY1c`~+@Q~?j!hLD~u32<3l?u~Mmu!&qp3%RyV{V=Ac_%Yx zd1jmrGr7v^db$}Q0m9$evq^@Tj3TH6?DAfB>)+T;5M~K#PxxG#+$gYFjs##p&0q7; ztHiCF02|4~l7^n-vaNr0D-!l^Hfb`J4BZUIK*zDBQYj=^uTe${<_$ zE;zgXBF&h~&#PrM=~H|J|MKqH$C!XH{=&6&A3fC{p0B9(uhCt%Y1Um?#S->7Kpb(- zhlgHWsuy-r`@TyGgR8>QTfJy6uGgwuh4w;KLB9~ECtTdWMFlTEeBim77LzaX_wSx9 z&Q=q58Ut4M7tkMizG5h)an|CLs;#escfv_KFPt?2u^_xaYrbRS(tqmr^|UVo1Ya$U z&T{6sCfyPQ1;{ipMXHE}L>9+|1@IS{Bh7lSKe?j3|CL%SBOfu>FuGf5RFLFz@L%0s zsXl-yW4ha8j5IWHgnU(mxQ5p>ED?b+HK}iVfu0sLxBxQlBZwE${`X!|>vsgg@7mva z=6uEE!Tu%wSe1y?9WlShv6HEG+u3zPRnLFAmV{J=<($6z*psG5PfONZT#j3ksb$ZB z>TEK@=MQ{qwFQy{xjIjoN)#a)Ix18tKn6%=Ipn)8ocYL3y-`w48hVuEL@D=w?wYa( zCi|XZ1_-ZE>^)dAbwb0}KlqViL7c^xgfTzGKr-HR3}v-fZCv_`SIwdn$Ki?kr+-q6 zFh6!t?G@5k@ynUnSqVxQ5l5OT1wAU#n3V24yZ{Oepyjkxvp3otTA#na{ot%8Ji4a> zP9R2SF>&L&kM4f92q;C9b~*fNy)|~_hFT927NVZpUo#fc)B-NVUnVgR{N$yz0_)P~ zy}nynH!!riBb31}>Cf)*Z`R`1qdhbc8*poXu{Tu02ts-_MYZOVy2+;WAfpg1ZW9C_*j~#G70EUaK?SyNWLyf>vboE;v4Das(Y+5~jQ`?5J7R?((dZ$N zw3pc(1t&)c*rENE~$3um;xbq69k~P zFg>#Ijy<@vGKfr&T7V!05G_0+VIT)(ZYyOh*(0f(BLE-0fYMN=*3df00^<^WBJ8PT zfC>y<10sAu2w09WyUU76v@a(Zkg7y@z@Q(g4GdA){J>Pikmaja znX8=$Of5XY$J_CM1kcd{C&GxuEF3JT9#$ z6S9Dh*t~z2Dn~#eWYe+`W#$ynkunE$tXehO-)=%IJrIygl7`Y^pSOYupRzf6W^&_5 zT9rwp0?5-PLh@%-?2o@Vo8W?+lbIm9r6Th3m(>)E1=gwRJw z{We)Ia=8RGpPnx=R*Uqy0}Kg5S5=)l)|Xj(n#)4O)JW_zb0Xn`mlcv3Uw*RwzRD`9 z;pJ)d6W~2XK1(jqpY2F81DPbjd|jgG@`)Ggl90#u)OR}gh4K@(Rx*&)D^-8t%9(KV zQaEJ-Ir5>-Bxym6EOPY8_gq}%n$vg8aXAxp)%op42@_2%)hy}B9<}`Lyq5i zVSU2|aqNHa`7$iL431R+N*x+H`vG_Dug!N~-&tG-kCanU#2to_hMn0rZ(I7E2bcc( z4P`${gHnB8{6MWjg6lRftvzGbWGs&mA>wrfs-c=yO56#d9N^_wD!GjdxCA7O&UnAp z3pAQCt4;_+IB~ANtxFuNx5hqnS*3q57@*Hfg#c24(mnHAtn$fjcM)T11}HQRNV$4y zB@z@NP(}csu4`t0)3(_iw4;|n>4OiuP8exrY8&uar@=W+-pgSR zKVRzwao-t40ykmM>jsgmDV4Pyh*Ee5E|LWn^0E0!B}`1JsP>X5v2kkC+WHt7Vr0%_ z?tf*r{G<_?TF{$-5TKW)ex`rHQ=^}AX3?{XD8jKA4gUQD)f~z?!=a~l~c~=ttnoQEoB8& z@l-PW;}_MU$^OG7M;|`=YIPf29(=kzX$}s$Um(#Ca?h?Z+vRJomDvo_$7KF)*c-Rh z6U4A4$wz#7$N7$D>XN}b_tndEKiX^e#Nz$f_0?_u+3mAPvJb)ExUm`wFqNGe^yUlx z%rK%L!vFf7Dqzv&&=ZI6a0y8=OhDnX-ax8=>nl&qwxE`reylKz8EHEZGf+~h zmdh9ue`Ly6pI<*X3pj%1S3Y0c6%|f7rNkERc}utNs9`WFT$-H3THd)N8IYz6u`Nx8 zK&^3%F6D~M{OOs3@j*bi$ts{g9XpBTwLw1d%y;q7XPrGxIY$)LqLls&L+>T)tE;5L zXl2O1+~2y24X?af+u}-EZzMDt#|33FS;E>nYl42{(HO}oBU39B?gh6zXK&atTi?-D z&`|ME|6Gi~vH@ec^T;gbW_d8ml#+uOG9F=92ETsy(obAEyL49jukNmm?NPyih|K|+ z1MKd(6JLM<--sGWZQ3Yh@MuQhp56cfCpiK^T97*6nK%}S27$tpdu#1}2pok0Zap{A z6t8^b)p}$b5J0?RfOE<@JBFg;K`4h|tj!;o2ZUe&;_NX2eUv&Il$NvS@|a?SS&m;0 z3#(UP1ujFAdvE~;{qY!=TeuvvuzGb&r@j-gT+4Sy?@9P`2PYS>PQn|2F^;??1Io%m zro-h5lnbNklw}336<`RQvY+7CJ!lq)SKw7ecpi!=r{G9!U=)B!M(C%jSD8tKF_#OF z+Q9S)Z5Fll?D{g-R0S||x@O~_;To`jG07WG&`6g3jxF(7X(A^!4}n-P4Ld_HHTw_x zPpQESjVYLgq`=6jFHr$VOF(EDye=~lbeN~gLlm%Fv7szcCdy?1flJ7UQZ@n2Y>(2= zrwXVY$QKeuv4`K?*$Ii0y7M)sbIOINrMbBE;qR~lm#N`A>n6wKyPCNU#K z7;6*CH|vd<+k5fL7XvS*0`3H%g+FM_`fsP9HT)(+iHi^v= z10UhZ=d}iv|EQvcr%Cu}(Nc2kEGVdzhr_4pgO@G+H@{b0@-Y||BH$OwSgRm_bd?RA z*uXPh=upCDa|(4*>*_g^JL_xx;vOyVpZU|;F7d!~m9!8dYg*g8_BQn1H@P3L3E?I) z0)P0}(&n>f_VAP$9854N{hi6LD~jjCz1>Xfp#R8Rjw7EyDlf9Wb4t|;ns$e070}SK z&1(AYmIjLY0`UvA%%KIdxR4v4*Ul_)7PPFz5Gy`FL%_IXL!yVwWsJ$PHP!d-sy@Ie zTvQ{*7~-5oOn*D4$1!z4k*1BFL{e&d>2IwuFXq+OZ)ckS#XaJ~;$zV!LWX5Wc} z1cZxB+K4;p3{+gVsa{is1fj$tK4p+{f6W~uv2LZnoja-ngZSkM<*`Kqm$M2F+^4!C zv*D?}dgDaqWMD+Vg;o8>q>Lfx(-uZN;bqd5=hqW3VOL9|Zb{&IYw8CgYbfiVl^`n8 z90tr!ysI|7=qLYse>{slx)%A&s2ivI5=cYC1G*9)p%Wt6tTIMoi+8@9bm6*Ldm%P> zqH#n5%2b6#S)3Eqie4*Xt!FVKIila{knmXClDqa-?M++zq1;=aC}rohn`*4^Y<0VR zrhi@&WF*u>qLey7i=ZJsV|6XBr~)LU@Ub z2zX60vTy9F#o6=Qn833y&4@wFi>Mv@W(EVuSOOE*Kv>3Et>ILaat;tm9V7VV0FN=h zEjtK;R z;uA-%0Eh?u>oO8P9-;$sIpzV*vBBBwDg|?^)GomId(<#Hh3VWVNHbT{l<90Eu>1-7 z#QaZcgHj=kQ{I%H?vIx*cS?c*7Lb8~%!Cp!k9NI^+f)ABqxFaAgvBMvvB7L<$V}WKO?4)wPrNwWxa1PON#U@{p%6y3 z5}W;pXR}7rP#BTdj%N9J>9QF6o4C;n50pVB&_hcuLpG{HNf)Ha@mZ%={;6vH8I=+s zpgIR33}D+ApPV_!j2wjQX)R{SC?yZb7*P++URg>DmmaW00gwao z4PBT=_smvph?4LSiv_R3%SUPfF?mjsMnY(((HZf3FD_A>BF6#wss7wAO0j|=M452q zL?GHXubq9J${`LiZItGMU;c(;(uv>6b20-q1>7O@}@G|;^qyTi=BDy3$Xu{`brmey0oUO_1`~G zJD5J)uP#d6@gfFMov1R>eUbDsj*BPxv}Y~mj{fln<4OIvz68D(Wg_G!E}8A`qs~x) z-QD`s-V&7+oKHWz^r6dZse)_h7^Xn=uijQ0oscDZ2~;(WCg>G9Et>-2s<=s!u>AhR zg(+!EwNFh_ov4!YiCb$=1YP3!wo_|sCwoZ7kcC|WbqU&G2oVjG0usG%JFVV{qgMx0 zEw&FlHw#y~TwGwulf9SIO$0+i1*(Jp?u%#lpxPcdXQGh3tG_z0=_x(8mmvV*EUeFv zb(bUL;NdbGUXUXkejH&j{{P~2vyeKnD#58|`V-^SNVDb1o+&?nP5lhQsjKSddiijD zSL?prMM*3((yxBu+L?vqv;1pM*CH(Q48N=T*HpFmzE`SQALFFjdgZW)k;wj;0A>yvl( zN?)|lzMvrHZ{*g-9d37K8c~@(=@tu_OgF`D{NB9FG{3 z6j55sMy;19azvilRDb4?>QSU=DZNfN&@rOOQ`<0}_SK-s6c7-I0Q;|ee)gWJ2*VJf z3f`p=9){)Fg9fse^r8N!8LI$kXaE+#q;&hV2*6UonQvJw`90jZ-4= zB~4ohGJ@Nmsqd)^CU|U5jlFLFzj0$xBGZ{Stu11BVyTWj26O5Vu!j&&aD!fE3(>CX z-re;^Ay7_`fsyT@{`t{t-Vc68(1S2(nvd{m$JVvIN8nrA1;9r%IbiTQYY`m@CPJ`3 z-(O%1jWY?V6hOFAwQc@8hH*zBWoImINyaQdY8@cLfga^~8X*H2FdD30Rb$24Girp- zXc>TvQjmKPjP{+%(Mf_q4+cJ%oTr8jShx&?668EC0}FBpAFUIUat2I&t6*xtfT3ui zd^|vt?s0;k8;{pE-zc1jJtyHLyoGRb0qX}GzRw4g;|+P=rrA^Wl%r@t-L&eMV74@> zj!APo|VUFc{4z%2G4xw{KYWNY`^nrIz`&2% zcWkUQ;PMT1DZIaS*DQZ<=^z<|QMpouHjgx7oioJ`r9xG8ZIH^yGU;F6i%>9e&HPxP z#w;0xv6fEGoQb+KDd2mfHOR=iB(HuY;#E$O33JXt>w`2oA=GWai;8`n$r3kZ|7z= zZLLLt&1Y9W;J#1=YWZnHIhdpoFUfdiFZq@VOoH=~x0i77thI@rzs6?7rsgkn(hks-mA9-vvr_H|l<9?+5SI)9*x804KWCoIRs@_8C(gv~e!12nIi5=fi-}Mu zSHQcWV*~XJKt>2MLYT*fEU(Dl=uP5E1?_0a!5#-Cs1iS-luKIYV{$)=unWj-{Q>Ep zyQXM9^^JNJD=X9>{f$^Lq~dL-RSv^uzy=AtYtsO!u*?i zOBXMMMHTIu4`Tuq_@jPnRc!>idF7A@l*#$PW%c`d2^8cN8!E>`KZ{|nCsOHbpo6Cl zvCF^wj-|hLefh2|?Yv}t5tHmJZ{!izbRjaSE+I>`1T5PR3%?R8iX?;z4?b7H&pP$_ zYij!vQ2;@L{7bi1Ljb(b?5i9j>nsR>R2jr_7CJNa^N-hxzzthUAPI!$W+Yi#D{JW2 z=I`0T?CR~A=?0Q>gCVYjj=k_l7Lx2MZA_ow@F>9HiAw4+q5b|<7(KzgJtAxlet{6w+4cl1N_kn#Q8&hv@o zodUf%lZ+PxUvj#EaM38R2*5kH)~3Q(1&}EdOM;jGqP?BFJ zeY5tr%E?P{b7?i|B8P;h_m%|5s->kbKUI;5Rb*y7F^1?NHNrexgNY!h(!>Owd1}2F zm?#m2SMBDrN;n~Z|Cag+SYG-gpCh!hO1Us?Nfar+b4&dM!N#?RD^*jZAvAhXwM} zIsff_y$i2jFGY-O*Gu(Nh8)!WUOZI3WI>s5dapRY%+8ohNLkzVzz~9qG0rd!LsR{= zX8s$6ckP_*@-(ZZwzN4Fu$FrqW;J5=}V#phJt#)SZ5$Q;azRUmE#PhgG&FkCL5 z-CqI~LdNUBkWc}fFMd^dJ`;$w0~rEig2&MGua#=yL1+TX0_AeLXDUc%hZZzQMvDg) zXG0TdDF>G0r!F>&qGXp&WC^6o7It08K@*fvL)-u(Cw8mvq8_ zl5LgL_2ig9^GIntd{on$GdnXOj9z{bNU0fKAt@@|FP?5slxbdX8Hr{*69rX@cd z0bhZV)1EnVBxx9#^-Z7lf~ebfRMKvywLKq*={w@|Q)d~@Rp-|dRq)AtFuEj-%%8cc z5*x{LmN+reuAuKxjYb(+S=Ej8dkOA%@0Y#A`$ zXhyy>;V^$P|I#_ee3x1Q8Aza9g5U3-HDs!D+K~ihIdFbnGq3oOQ(0f%;Ed7FwN92N zSQM5as z+g~w*lyGXwdv=vBX2fMcj>9EN(s<_%!idF9y>nmp{{(zn?ZZN#E%Y%ockY<|W}JJF zNiag4o+)27T)-Hk9)7<2;p*CKBFBb|QjwvQSSoT!xZ9^{`&R`7LDI6jKMu`8u%&N9 zTsLi5`mOux_J6s*vd=}gOEbQS$mjx9duvm0ymsxs^uGJDwwmrg|&lZdU3+7ie~ zaDd1Mk6j^V#MInG06r~+28OyKn{NtsjxI<97@ZSRK+zQ&>Lx1Ty|XFmvH%^8B#?i{ zj#>mqLmE6JQ^|>HprDrU&pul03XV8JsoRq%!Qm{j*cJlpo6f3cf<&XA5T*a8Pa?oB zMb9Em+V|hwTX**r=hqP)j`6mCtBt>IglL@X}>km<>LW2^dLBTqcVB@nZ0XIG`UYt9`X!Lc|py3cX8Jq?OBk zS1Ob6;PW-;spTesBpF0(ZV(V+a)Kuli*hsqRs6+kP68RqxHAA(SRA8_(gBnM!2>ju zjzuVaLz}8{M^vNFX*7C9`vr*D*jW$g`OpfYeTtbv@Pj6jr)Mzm15AYSz5GDOiFjL8Fi?fglE`!mgAyZ(|;Bsv1?e|$|Lu`r(nM_aN^N&|v zF{Hr&LR{uAPOOTG)Mf=C!z7DS`*QyjH<__5zBmN@$1WzrlDcTM23KQpSX4Q{jzeQ@3n+ouKttTE4jL5+pNE(A!2h%$ zMS4Hu`!1;+aP-PElk;%LkwSLpFq?1!AwhMa1C@R8Ld5baaaJNg^@QFQk-eWRM=vrT z?-w~)$c*bZmpxbX9>z4^V|`z^te`a{6x;`pE{QsLqzVL5M@1s@%kL{o$VW!b+xqiQ z3G7#%EOj61S2(ZVQpZlWCmL_DN|$=IQyraBXdgQtOaZiyK9hx)gS%{=uW=saVmjO8GsP zl&BAGpS9o=EUW!P51;X|Vf zQ$Ztj2r3SJFKq6;W(ooV!yd!0cw%HN7KQES)JnI&(thox8oNj!O4X*bY6%9ug5Qhv zL>c-DRtM=!hSsjyE~3vx3i#eK_Ac|0a4nW`~ROW*6nb(?=@D@^0Dhn z1%mhQiYs@Oq5{6dEl$bdg0P#jz{|m z<#_vsA_!o)aqG9<>D<>p`<=hRLyft1JJ z7f&1zV=-cQ5*?HAx&9TACQ$IJtoJeGT8Yckc;~^>4`-x3aELHKkd23a$VrXyL-@mFF%MClZ7yO&)av#`}i;=ORlAJc!;WAV0A@<0tGs@JGKyT)eVIMO2nE7iVJB5q=kSGx_21X&D z+kj*SP|*F%lcL?^`^Ns!aT3f;lMg7X!b=(#!uXo7VQvJhj+J z3%t~8c0|DHLv1nt7~I;^ z>t!qSQXm;lve_KMyK8@a&iRiXuhot0cQcZt?ZnF&nKK&zY09+76$xadgTWNZGn3D= z>X+YF7%Ua}a|@p?T0!e9*X32`*SB?QJ-1gnpctlr%o+06yXO;9N?TX7m)ZSNUTh#= zRmG}zhyD8pmi{mADU3_a8aHP$dy5T#ad~s^W)cC3Xk1G!hY@J*m-@5cM_*k!WmTn% zIs+|~u0Lbxg7xJB;9#~|z)Py}!rZ*I21&P!n2^tFs#QI@r)uxqQHv}@ZC+dNYTF@B$$MFrf=)yWN1Wy#ZfdFRhm6=u%wDo_GmH&r^86X<+RuVGt~aA97rvKid9vwnS2IXBd4 ztLsgx1dtHKl7E@xbL|~*@Bo3E_Y=340Ynk|7$Zc8bR^>p7w|?e2?*T&fOjMS0YBU?Kjzlnb7)ot6N2JBSR>@PO+I>gCI1!7 z2I|<8l~%_8ldsmVZGsG9$OPe;eKR*6wC-*n?uH9Je^2FXE zaQ|in+#zTss&8#Rx>HoS>)(4xT{7&0{Q<1%jlG>Yz7Q^GvB9C%xo+F}#0E>m=IjVd z-3|wr)}B_4LRZLc+*+3v!yX)z(eUD34{l;2z;|{v0O5Y!Jm7y0!xD9g_nyaWnx(vA%WNvQ0vlBgVKEC zBvYxTk(TN<4Oqe$miQGdkq{);0&yp`Kw&_Hq@dPWS5Zid1fS>)uHb`lhiVrEw;{6u zQw?z!^*Z=q88GCGswvG>OJD^FDGY)I{1o=j|2#ZccJ>GFmji-?G37xs%2TeaUV$Lr z=<3x1EI0cE;mvVP;Z3VP9*mCqMj?MNI|)D7aB>0b`x?G)Z@?7>skeLxd#j|qg@%pi z@$tYEA+=MQ>FO0&V2^oOvK&>Tpw{Cs5bc>FgdpdsgA2|szs#IwfZ9yXfO+S_W?;2g zqH(|({d-s-!kveOgDh2H91=|9WXN>A`daPKkpvE!0mHjQGF8s-4nRUy1WX_A-Br`# z&{9rTf2$EQWNM=7H&AL*6MCQzf)KZ01HecM}j6WjRsS(c(sT?rFCLh2w-xlYvsfFCX1?^FV1^; zlT|$VB*E<*8!K#G2$F9k@yzmpeU3)|A+)QYJ@2#RiiVDFG>Y^-_X* z;FU57!N+@J&PecLUW`fd`DlKeu9w4@L9B*LZjVI|f!x>2VQA#8oFZA=iv3{yIu&B} zC~ys#TD%H46>fWa>7sM0W6%Y#OK!?*Q;lYbfmHPuFA1_?b6R*Ti^6a1mN+e(OK)vK^;%O!j<+?Ae;-ocx}CW z=Mp6{iU?ue&tFp;vPk>=hifr{G@Y4wY#S=Dh^j&wPJE=n+s>{eoCKi%px0RBM=Z?# z{;1g?NCA8NBKgSCx+&1Bg@@G8T2R5r6TcX5OncA8GlC#>ytJ(G;<7U~_Ke)lW|NNcx4fTy@RRi79{W&Wj?(qbQYApZwqqC+jQ@ddv>pgT# z(nV=Oriy&$U%jn9AwLUp-;V(P%znO$5`1n?<=?ocyiPj;^fNi<*20A0h5hMTfEJVj zo;YCP7ioy0nDL>@iXDD+zbj2nny(^|@+lS4YQ7$LLF^<`e8e1qf#U5OX7;F}NHgKB z$hYr|&mRl`WNcueK||-Smy|E4M%2D)*#qoz{KUSr+`ZUtCO@kkODc5 zb!S#*aOv@|0WZ8fn=pYP#M$wcr}~fK z4NuNL10P3*J5vb8nVd1XG1rM$Qw^36_8fyaupl;+Y8kA|&i7vOEy);%kYGkZ9{c#7 z+1qNPY6`i;YdTJjsT&m$6^$TMP3QT1@I z=|pnU336OAl}x~Z{k@j~ym+V*64QFMkr3a_s(0<5;b{rnu(`e-9Z?R%YzzIR=hog3 zg~}7!W@OH5POtS8H1fmKR#(>gkG@c%W;Fd*Xa#n1hfKB!MFREl>#KchjiYUrOB4Qq z%j$&@n&_eemyVYN+tfc9MMe0`S$3D0h<};gD~#pQfoi^_YGF};mrE-I$+ohpef&#T zcP5-=yK`%!Q{D)s0?o7g>zg>Z04*S7ug*IvGImN@2qBO<|KM+brktWT-GlwoR2?`x76;9sxw*6j$AJN-$ zsMh%YbiS)%2J;}7vNiXI6xe!72Rk2)%TzrW37Ai_wC zkh-Bb2)TMyeXbj52`W5go43`Fhzv3-aj_Wz`UI-v9H_OqZR=`; zH!}a^rB%fYzWl+Xvok=iy$bZMCTE0YoS8r1y3d?zCb~o#Lib;m$1P!C(>(ACRmnf7`=ZK#PONMb{ z3u26q^@c$PfTvz8j9QWjjMp$F|C`myo&v@d~y)G}MG^XDJ$8*09T z$(g7S(1^Dp-1}tFS6f_AQ=OZ()$JB(9#2L&DqNy>c2DVSq*c6nR1{=AX9z^&>|nVw zUh+$C_oyI&%haj@p@G2;+{G#904TGP7&5j6PY%6-NCt>UIeWp}GI#DQ?WuAjXfmBG z+M|moc#kBP$mq&EXNUsIJcSHCPK6*I1s?0~pQ-{}i>U!LMtCzTQ0zUhLrmy@~7F)pX@MjaV{umX5b?T>eW2sx&-1>|&o090je;n8j?V@meuoPMlc zsT#AcF-sUg{zCKRa)?S5B)y{?7_~8S$Qd^09LNL<#@UpJNkibEALY92ufQz8kl-A3 zb=j#SV)&e6p{fyZ8f#iSpKh9Uf&866#0k;Xvn#@HJFQ;qinIt&AVC@NyDzFay)qVn zYdXVoDe-Ww7Lob4kwlp>vy=9Sg*5Y40Dts&|E$&J#lpyZtA1y1zDToDcR_zRj5LRN zJoORfQvPtohDyuHlvH28r4q#??c8K10)YPF^<{$tve=bhs812m0+Y9#m=qQ4`Sc@2 zliYytXeTZ2_{baoZ$DY-L1IgYBo-x6%y0vez=gv{i%WcjE7HziQ^YW|5KE&udI+2m zhZ3wlrS=!Nq*Tfz+1TBA*MEAbUT4D#zP__EPonS&RI)7ggZTIIGg23}ZpN=C1q_)lzfxu3 zGOuWfN$EOROJM(x4=laZ-<4BKpfkNL0cqOn6a9OLY=B2Fs+4((-oduJo~@1w5Tgw8 z`pxy;9Fr0*yev0}ApqyRt%BeNPn_#V@s^hU+3o$}+bNZ4zwmOA>2mpzIQ@-YyolGI zQ5$4pHe#KGI~qupm>{Mom#IRwdeyAaYx$!Gf4zk??i*(i#6sEohkJoFKEgW;R(I^E z_Il{~8fTCl(M?+~JM~pfAO+2OnOb6>-dDy`^!`iRfW8?8Cz7+$L~`y&03UbU9od;e zc!CT>=BrN?^3~UtZrNI7`wq>PfN69l8S&drt)&NpKY9d+$Rk$dnE0(Z< zY68_VTfb7)!HS?)TP{7P0^<4NGDDUsT*#CmpWR<_AlIEyJ$Bo=s?zP_NOjtF(Altn z%u$OkSzqg(PrhKzg4_=Xh5z|$wHWPY&o)wIsnQkc)p<%q3oP@Br0my=5(jz#f@19F zCOmf#gWtNpc9n)Q?;Ihh_13wCC|^Fjv~f-M=N@o?kOGtrW_UE8)9Z#oY6a~jM8nRK z-`yq$nM1gz&&K&)Eyi$DIb?+A}lMz8ArPBF_>fQTA$1_4Z_HSmKmWsu83JRrKz2+`g{ zHQTO9{QM*Fk(22o;lr>Lat?$tmno|?^;G!GBPEs)b238Ih5&@dc`AGmIBSUf24ZMT z_)I8)M1q|#z~t=S-Lub8k%lXvkh!nI!6P+oHAy8ltrLxrkdQh6B!~gO^H8-BMrU$P zUsd?uey}H(o#dE0C;c$hmdpLct7f8N12Sn3fh0(K*m>wkW&ZU;+P!*3tjVb9<}-WC zRFg|cRb-ZVS<|_^mIE^9F0u2^3_B7!>eMPrx1?wA`S7+XNr?PVu4_&VPxvcG3Q5%c z&y{WF`Y{!T(t}59$I8d9FYWMOdUCcQD~SZm?K?^ZU9#2)m!+`t$1bkULj~`g5yO9| z_b+ftt_1^sd&eqeIqLRvO0wKI!b@o zt{g?I#aN-6t3V@PRhtlrCP#!+Tlf#7U?Nt9KBDC9^gh^|t&kzE$oC3}3vx&(mfrTI zD+)LQN0E+B-V2nmL>zwaPb(>6-ao&iQZ>%1LN8*XiiXm^bz^O7y6x#2*ilOV$RRSW zp|4b`4ajAjVwG;Gm)3XpdtF9jG3k@Ng{SQ76$i%r{57*ZS-HVuo>FoxU>wk6CAX>= zfVWztW8!l4>JnZudrtwR@ILf>>D|4*vgi=AIh{l~yvh$eSFW*ycD9i$F_Zcdd)3Bz z4HM;0T~R+rXaxB2i^~|oJDf9XKmS+-O$1PGURw#jdz>+!d!$++&1X!wa{itd_{ine zg=hq%i4{3+4P{Wi_p3?97FD@T2uVx`%!<|Fqb1?u_4VN)?jz8RneB$UfBw-cEd`&p za7M;!1zr8G-%x`g;grHx$C&pt5SCL{&32Q0u=nY}Af}@1Iar^o|9dx=Q_P@Ft>9N= zx+kMiCfT@A%Ru{!k>v@L$V3qu1HMfAzx(2>?;*%$A2Fh(T$|rA8Kz|B@9b3`hfAR9 zmaWD6!oljyS+jfCEzain3&^}ip+`k^R!GCGA}o)(Q(2 z9iB!9va8PT_b&Tu*1vUsujZUmo6+*aqYVD~&N6uBRJ$3&vYpj`|DkrWNyHV0Z>O5bZx}`r4uMu$>Y5A+5eT4<@ z{=;>n8H!(cxqLJ}kL^=^~jL8MI8*874Z2Oi~i@XDSs6z0{ws% z0g+j?s`?u|F2Pi;8?&2Z>_5s*;f-g_UJuWbU%>6Gzj>B{>fUYYV|(fkLV)&QRAFal z#k?2(08s!jjp1c{T54Dh+&FqBAz9T`U@C%l^w6XLgHky!rz>;kCoY+-mw}vB4dP$| zVR57|FcqsFZQO&wJ2Q_x#@lMX9@R%x3Ikl$PQ8~(~0c3JVaLY3+~(rWugJ@NP=~rr`|< znOgPTfp16S3XIFEzWc77gzqSvT);XB-w}92!-TQh)B5`dj%oZrKGBOx<8q9N%L{{M z0mfs`gL68hDo>xvU@XUkY@GANT_6Svms1#AV9FSoCM9MbnIr@QrqUpR{Lqv!Gf02{ zKGISiDi(NinLXqo9{6yL`EDdY0_6ocBXkNtSwZ$<#`*cjYvabChp&0`)idDUAqBzA&Y9FEVzDQ+@KIWS((k!`#j5A`CYSOM(rA$`#wtkj zbqU_RyK5FG!^v;$MJzyuo{-01tX*AS>0MngKm>O799phK`)r2U|B)F9jG5!g3^~b7 z^Gp(BY^xA$o4t`lGEr(JQBS^DIg-?ZJ=#@4 zULpfv2oXSb?rX{s+sZ|I+nNAoX(Q2lt>2tV-Y%mf2TQDVY*}01jo!GXI$^b7r%Y;# zv4gXrZ;lR^&=NnH%>sb z9r^FMxZ11Q^gzA7$Kb3(Q6!ya$xs?2eunW(xoeH>J%Y#z8S8 zGl4zp^j=Nw51*0Dmn9^Pk7n$oe&5SpE z>DUZFqXYlj`|7dCX7GCt7klhi80AmgT8r$lKR-A~Y-*{H&EB=M*aJ6gseAV7jk6q* zG3aq#)L+f#!n-e8`rY{t*zu9egfRZcFRF#P42DQUOv}$))!R}!{z8hLJI#9%2JbFp^)!Rzuy8fZYS!c+w*1nKW>4*FETabH@@5BOfwAA~z&s^4; z&-GWdqu2Sd{;n3@TE6dx%Vp=*4irha8w3q?ZhVcn^QDRp$~cJfbC1-ckTLFPH_KJ$ zmq6D3^7|?*VpW~0--a^`Zl7;9M8jqUabr1C^AU7Pf8(CntLxu*wi=XnWn}2Ze9wV0 zmHo^hbMK*wYSGNs*Ag!B6*yaw`DVQ~D4vNGfFv{TEkd59Z}->;*7Rr5xq0K3+4i*h zQ$+su)?eMCi)4c;;mYBHalP-A-u<*xzFgAZJfa1`lY0xP3523XlKz$9bPtTq<32)s z&+f7xGDkrVr0dSCyEq@!x@EEwR8PR3E#zha@L9bA>=9CuAz8bsO%gZkak*5SQjar+ zk)R4D$H895Ib(HA-oi!8|M(|$2i1aR+Xe~v<&uhO$~k3RtG>~riZYBiq{_hthGq3i zLqr-08_%l0;=IsZG+e_lWYwTA)D8a1(VBZiqX6;+&2qpVKo)7~Bvg|Y;qZ4qTYrgx zyr4JSu`|+`r{{o>4QXIu^$MsBfpb2%Ig~DEA23Dc-3Y`%Du9%;dNqaYuZ(c#sr*qG z5SnowmWR@LOahMwhdbcM1kT1^u%LQ+8SQTl-UM&x$CpXI3-G4Bd!uknMOyH?X23~! zYvJSq)=BtYf+=I3&94hJ#rZbIQ!};`e4+AnM|`u=DIgBZcn}_seMp|37apIZ92l1& z%cXfw3ZwQ!o>JU5u?`?!FlGv)b}ClEF&fLDACsI@oAYu@U_g-5$ppLyf6l-h$T4^o zMtR5#GAx6@Bn%_(YHHP+YceAoH0H;eEK|??c~C90k%4$LQ~-tHTB<<(wYhgU$o;ZQ zh$yPDPvaoMbPZopAYsfDk=@uUICuA#CSoT*W}@tjQlj=K^#zx@~Fnx&YZDi zKa>L*dp_~5(gM#&@-?4kB8|b|=53Ygs3j`q8A-U1pusp89H@Zw{jUE@%TMclPgzq- z&;NMu@L~*;5*)<;{{H$-(`O#31rAZIV0L@m(#vz~yzQxqr_Ox!#=58CGB;1}(e*z* zxU_bXlv4EI^CcwxD$*#5%P@jKtTF%N{eyRL^Y7nMRoKzfg_7irYih|)CVcsZs+vIu zUtj&TyUJg_yT{WX_Vf-`hLONoXPG%UyLvYY364lsm2FUr6weq*5eceXlA|`w=Zq^r zLVfzDYtwve(>K`O9wgskKw;!SO=hr!k4yKXFMd@d&%_ zch1{t1@nRDi?KHJbszrRBTHZHmn%Q@jT#06x!-~FA3R!LG$RcplE3$G=~BKVdycwI z*|VcxT?9r0j)>%ayKAK>_5jU<-K7Do3OzWZRO^mbAokzgU$>GLzx3Q%oBJ<+WJY%3 zx_S_30+ulQ;(kBf^O4?Pzq=R-1VkxhI5L>HM37Ne!PE{$l|27HJv8gvF3D8DC=SoQ zRL=!f>Cd0Krv}om&%fW~37C~c(p&OYE3z8I`ndoy`lX7)>GZct*xS=#I7C@J(icdQj8Y)=pJf=Evv${(E)&UwSm|B$czqcJYvUH^P zNrkxo6&c8U23cCf*(FFB4X&uZcX$19C<1dPEgD_2S@b{_Q^JyPeGJ?E+UM`Ht!+I{)@+Hx2!ye>SwceamjzzH%g zhws|z)%P6UbcjyE_Z&_xV4Z~TC1j9XcmOPid}iX?f$8ei%Ic0;HCU#r^#hsn3Xhpk zP8mb2kmvLihFGwC|1+4D1Hnug7eG~BXB#l3T+TS2%?mInN3F?CW>kTNAfV42EAy~{ z40~z=;}1TN=%;c_d^3eg6_^rW8kCTkv6;Ur1d@>=W56t9nNN>Ecq7n3k?EA#9(<~t zA+PTrO$T`~4KOAXS)vd~032qJ@DUQjOaYNmt*i<}dupR15E^*%u*yJsHHyJ0M^4@> zfHzAvij;Jbb!os_B#D0Z>IX)Is|k8pjgYtlBg-|pl!p_jian}uu^|%9KBF)I z@oe3sGbqGme=nnC1&xH*6Lqvy@_De%8bxpz`Ox#VK>v};i#SmgqTMf7`K;5bOq7H? zD&zJ3VX=w=pazNq}+f2|66r7Fk zv$p635y+eoKe@Nk(cieY#!tG)XB8GzSD#;@U=N5>kboRY-QcPaXQ>Oa0@ypMAQkxj zdk@zoyjli);?~luJ5{(c1nI-`l&>WB!dceHaD{+7fNe>bb-!yimIMTe15t$k)D^WA zOQJ~oU%I;91{J-X`>GEhKn#z4?HQG*f2un?`DqA1DKgzsf4jJUZ;_p}hhS&{DFZ>! zB?w7_mXAH~KKoMT;kVD5n5l^mn)hC^^gDf91>i~h`p#PKFot~gk-8D`Npf6S*c$yo z;}Q$=uKDu{+TxK*QkQbkMr0y4&=o$P% zHw`a}w+CUDw5w7$b#)oz_Wy-zOND61UsZq*p4OY@uhb8E{>!^(FQW|!%*e_tv2F)m z!(VlN-Sr8nzH!5rHhykT4#tSK9)%HQ&hSF?c(2FtsIc7`08uVUaFcR#=Sxff_TIMn zK>c>d#p`QZZX}nYeifZw*8b{kC4md^2my9lc#VwMbOV@wc6&WE)zb2x9;yQUPw%U> z(Ke)L6z+enOhtp8Us}g7%bkjYn|l6!y)p^ZA%sVy~Chmx~yK>aB4R|ht(17x5KAkAvn)0A;#n66%puZDfEz;N+xerLjg@w7oD5n53B?vtKa%DaXus|~)`01aR78+tG%9f?5 zbC^X92n|SWT63&XI>S%r1TdK}In30}5wi_y)W)xHIeTnN0EYY1bHpl}%g&mz$zJRq zqB2)DCuR)Hkf}_2%60UNO>J~JEAtN5Fj672M*XC6*H%4PK%F~B4c{6sNwM3wj-Ix_pCHyX7Hk*`3e7DO4nxcKhu z00^XrgvfU2QjnQsuZlDhCY10@BJjp7#ZvZsv*sBDf+QY5o+H}wt1WA1x?(&=*UDn< zoZp)_7k}xMqT%KzZY}yaD~#F{*P)}8HGSx^%I==pTe)0G>#zAHL~NGfheg^+xY2_F zXdV)--kzqG_F~ir+6Dyg+)_$imc|!m!C?tUh$6FUCrykjmS-~sfYCo$1sItYN8o_R zj*G#g3H)=8%~}7Fcso=5k#>P1LI6eg>?$Ezt+ws-x}Jkm$kJ&6OwpgxyDjFgHW1DW zqUr}FOKV*crGqgWiz5St8MUuUs$Lt)HE#74x}5O)yg7#jb|JL+by zw;-RNsYoqGff8E*0 zE4AodI_}+F>5AB5r>V4nqUQE{SqBs_Z>f zp2i-kK~`$``G;o2p^tlPcU>2l`OOT-pbIje zvm`+ToQt^aR|Q_{uctX!9*i^jD8+{t%9N(llBqVUS3(gQNM#*Hl`}w(i%eQz5D4$N zXIAjf@1GpuQ7jz1d3d=b1OdkJ@pzaV@Mo-E`brNY4!%3D?Qipk42-|Cni#w=#*?;( zOcEFnEkpjly1Pwv|IUX60zY+6KO-9Bl98akJJuMGGsxf422z8faRgF!L;_KR5i;e- zpH?kjy#f`|iKqct?V7TWLSR%a;5`-(9Inrk4NJf%Oa%tqT*0V1rbknNJZ9B$%<&eR zrhhVjL-4&_y#gz*UL7xDQ_Ke1+0_s=MOTC2%O*`IEG6TzJ=v5 zu=48FfT8P{O3iV1&p%nU07Lt?cSLm71rm*#NH2ru}ZhbjmruY)*{I6LDq|6^(-0A$b2e>4Y#MV0AnCagoanY|1) zV$()wGSvw#LicqU-*Y#sPC=7OoL33zwlT^UJeFWLK>&C#sM-jCPWgR1i(bRvZfXYuxwdd#B#q6 zbta4e&v3-l8OTB<@XCbCx0j%OS3@-ZfSvnll@Kq<38U^uAhF6UR3#O!W||B&FZ5ZNMm8vyx+ZhUH4<1t zyE6m$Sa$Yju*k%SCIX**sbbo(~HE zGA-iS;r^}s|Ec1t<-dOYmXTRfq4DqCTr0Cz_D?$CfB&WR0V8%Yz({MD6(6AI=N6!a zMnd#z(mb0Ue!etnsV_fORXY6JH_SF$UA(@|B)jF$JgpXmbx3k3x^H*gU$GxSqH)fd zW!pWw>MctK$7>SeX${)F2p*Ykhnu&~{h$sq{uiH9AGw3klxXr9CT;XeZ%YIW zh!^@>UH0cJ%ixwKdK>}DW3L6Q+iBq7zNv6h@uSIz#Qh?wOt$kbAlE);=k9SW+@ z{Kp4Mh=J(QJ^h~V+p2RqSLd1E_2D}6)Y`NdugS;GhMRgJ&vR2806c!1rAt_8K7#^DlFF+SV6*xzyka6 zw5$x%n7la&$Af$k_)Wlx+mNkf(}ldZ)`gSs=NC>cVEuUlGv*WAlezF{`C)*S2enbP zeD&%L!Gf0TyAGH?Rn9!wYBXTwN2A(GavENDXjU77Q3xof(W}rx#-wUNW-g}~PjM4I zv$P-w<8r{eFwaOKGDq!zAgAXsbpRX>ee#?@YD17qkjqTXJ;}nTYN(5F@y|?7dBi+3 z)Fd!44h~8}QX6%({l1q5loiZ}T2);}jzeuQ1DQ#GVBj;MlzF-g$ogUrfTZ2*KLx~f z*%R5!xm`w4Ad04E<{?ua4M8}K`7I=u^3`+uYcFxfk#FZmnu7}NWwI`#b7ltZGs_d| zUXW6LdSCHgx2e>j1kw1y!4jLSAnsHEJ3V;l`TDGqLfS|nEL8mBb)`Mu`XwBTB(}D1 zbSB1NNZ!e!;f1A)?7xZ=f;ev>Nei8|b-X~2F}EILl>fs6)u4$IN{7^L-5I5tNl6#U ze0F~k%PSZ7o@#-sJ0$NEO@cMVvhd)-7~#^ z@^o$1K(B9;K#MH>m7`UlAQ8U3*QW>sY{2#L-u;wxuEv3GuXrKbeDJxl2S()cq7$2A z0Qjx@YvBMUVRuG}h7DN&0xlVlk3GeAHy31VaCzuR1(0eZ^PM}YDu-mgHj?yLP7uHE zlA6U<1NEv9brKI{2Lqf5=fx-cN6n~KhWx$WG$)LFlsSL(saY!nT<|w;tu;I9Gyw@% z+|+XWy6!+xq>P2B^x^^-UFNl>tmXjhS=q!w`yW15tHTHgM>F(Zw|VJ*`dYOw$Yv8V z(Gy6WGtdeZ&!_99(W}m%H9-#10E0|h>(2`DnhIRV$fUO@`*mYy87Uh=*rx1ICh&J2EFV4THk?&!ZDr&$wJ3AthHBRRhbx}Z8!qg5aT4-C{}f;u zHcyOeL9Leyfe>+l>iEkT8pYXIarOB%@|VCgYe%CR>tHRogqTb&=%o+;|JnQVV9$>0 zPV9U@VJ&P0R22&QMgSxLf(sx}3pa_RsaCspEo^tV9rm~_(T<6ji3zuC{W0^`M9A(4 z$3#qoV<>gE-R`#BR#Q?-y{N@qTtyNj2!O<1#9BZV3RM7VLG38a=bZ2Fm%RMFR}Tdc zlql()xOFS<$+KtPym|8Fy}##c*AnAV(nShl+R;-X2)FgO%vHf;DBy*=gKAnZo_l7w z1N3t;-FIAm_tmvVdA#o$O`OJJo-q&$Ayg2e(XZN2WsL5Zy19BFt+lO|MnzORUvOrP zFtv5)U@f3u(=T``7iHhGBAi78Y@i%5G)Bv%jQN>@{U9gAb!t@Pp#1k21 zmM{Q2${24%`td7^>n)pStDoYDfIzVKaCsq~*IZaHSrZsVF;6r$^(`-<&5l3O{y=|# z9~r!{E_WZGE{uT}+ZR?LOYogxsz=SAxmpJj0m!3=G5JP2;EU&=G6LnC$w7lRb~?KY zsw4GifB>9vTK$E?<=Ln8GU$BLf_KOXrOBl3p*A#Se-F#Q`Hi|;<0c4kb=|g*{V{$j zDrXQRa7L4uj9Rx3_~PTUKVrRfq&i^~jk!%2$JyaV&R^FeJZsj3v zxLnb^)gkoLGZ@ptSB2#o2j=yyUcDyp+y_$w7K~YgzqGKnfc57IPX3MJzdU#Nbgkj! z;Z>gjCn8ut=J_`ukL2Zm#zQ^pFH?w?v3dnuLY~aCa7q?AgC&K`f+&Ik_%qYT-l+$=!j)WIP{3Y|Z4!i>q~kdr_KNC1#Aet=6J!D#2R(=qWb}XS%`?kc;w*IaV?DiGmDR2?`%v{RGq9NCU%jGosJ5Q{r*E$`EJ~T7 zaQ>NXTj#WFU?eT_rH}V^s!DhJ1v+Oo|JFT)lx3CJ4z*cY895IclSt!&ykg^gWfMfj z2p~ohoYPFexx&?=L|ewVr~evH6dTB?UeP;!MqVms<35(NPAkbIlA!R*{UdJVGrQoF zr4RJ?xn;I|7oTdle%ayf?KSws8L7UcddqoB@42dG@bQRnq9{d$3nhCL^12q2s6fCx z9r(q=wGbfkm-X*s&cf1ZIT~lY&fEz;{<`<8#piD`ky*Um2d>;um)SK5lhrrIzHQ4a z2#}=RaYg^AS=)vNz8sP~_s{bq*tEVps>Km~xL+t!0F3{bfLu7U;fDSK_w^eqA=h3e z@o5@W3gVO89?f07pd-}q%;rAfv-r$cl-R&cDzvgH(!>y6MZTjH8}=XW&!QQf&Z-Yu z5%t)MwXy&-hpu@3S+#YpL|tDS@#Kdr0r%`EQHPF|is~MH*OTuo2p%ZaCEA9hcF1US zi2!J+80jv%_?+5=jItg_ow z!6nd1+#?LjdPi;(1kd+!%;A^olU^uU2qVq}`VRq0?i*|&>BUP3+i<}!8_gx>)ynl( zdIkTducvU-Fz)M>ENwUI3_ZqXDUAxc;>(A6L5h%^Arl{<_1}3}jfeay`{%o>6nKG> zOc(_>m#1^S8i^owvVY%^+AxK<7OHb7H!}7hS8Qg?auJHDX58(t78&R<;AGU)u z9Re8Bj_l7O5i-bBGcN7Yb@=1fd^xX7vh$Mtdo!Mf|{Xb&KU59Q0_WW&}n74E$h8VM-H#88o?E5vGci zr(WdjVH#Lb!ivaM^d_p&@|qxaPOV*#oXaTzSfDu>Sdb7{zIv70c{wacp1chwlKEO; zMHklKFFC9&V6DNA7M%Q_1p=8aoIGS4bW8`t<%8q&akU&m?|h<%)^dT%SFb>525|~G zUKOTfqc9AcN?5r3)4!rjDHq^GRSWoBa(8_1JaG2ug-osRF3c(zS+lgB zU&g8_NSyxYkxDy(+T{db@BD`*i3$*lap(#xwCsbsW_ISUh^ow_ujr40DLaB3mJ@D{ zThrEV{lqrsP=)~ga8RaXiu~~I*_1SHhBD47haHQ;XZkgn2nRtr!btL|{Vqe0zxs)p z*jjy<{JGe47QiTEau3XUNr4b^8z3 zPjT@AG2y?|yG1s(iYVe88&dmt|76 zgiFr+(K|{q368k0Iy8W&eBr2a;7*y4ZQoaMV3^~AX5>qG-HFpnM=BeXOkzw(ommy{ z0Q$1Cm)G#ujUd;JL~$u<`RhfFltNYw&rXHyIyfr{eo6)ud* zCFHW%C*E0tQ7WY%N>RFP@9aw}%K!4)vn3l1fnA|iXIg?YU=QS$bL(J<;aAnUXO;nf z^3>85{U#S6nLx;yJDr+h)Y}u8*rw@cJE0EM8$fii>aV|f_S<1-L`hq_2g9ye4;`Jc zko@=G(e@~h?Un-DJxw&6-<5T`?wL1TTx(lu*#ntBZ@?RuZoQz2Q~M%{|KD zO^3PT@@j&6o~`mP&f5a$%tg~TAbS1H5E1y?WAz>}xf<8-WRBOI$mh{Ll!T?G&ZuP6ar%-E`14nCLYFr?P z8PfjvRgNE8`s{bhPGyj$M~0DPz^Z@tt)>6=J+yj2WW6=_XBvuOWAe1nnbO2||CO+SMUkugZ%+ ze;ezDtHb9Xn=PXc$V&am07WNqe|1=e>vc$55#A^{Yw#Bd))uhV;D-fcL16(_9}?@u zI{*Ox^hrcPR3cx~>eWetoY|y2FjP-%SZ>b(CVw^@kn(aaAa(RO-#__r-&chpXGMX5 zY0zHvX|X`LaOq(-B&0AsFCYWca$rTwbjGtFAuujekdfxg1)wH*`7G26HHnm6M_^%+ zDP2(o+5WTrDV%9-&N2`aO>-c4urL`iHw6+E_^<@VDjQ6YQW3Lo^T>!r|CvYY&&%dy zIw#?oj80i{hSbfNVeS~)$WfO25t11vf!HJ2EK{PDIjb&eIv&FiOG+PW)uAM*e;g*$lu@pT%#__FX=b+oeLMv)E$8Ju-7>4JxOFAqwxkJL*LSm*X$b z;r*psXATmgR$IGNaFi{*FUI8i>3y>xOJf+OHE-Nl60&5#5>*a`9Br>bLaQ%5xjK*M9Mq`YH`AS|O`Z zh_xg7SPmkS7VOHu_P)vw_a3Um4CW*KX587Q)kl#Izg&r88PJ>HRMAeXn}t|`;^4t! z^}4I3Tz`6Xq$l^x))7RQ96f>Xt2^(7fiD9(bmPa35cpHmuL3V4OLJBdrCB6C)zLX*+U{VY^THl%-ORmGkU zTvwY42{A-K@>-i#=jp`}E{jsju1gx}B44;%N^40LjVOx!?$E#X=CaUb7GhGXOOr{d z@#NeyYU`g%aR4aYq{slum`8QauJs3$Q5~`>w+iu5787JH9a$QnS`ZW{5?gvdI{z_8 zUL>k+XSV}DOqyE9uiR02`vcDvxTSC4hXMhCA(+85 zDt~KxA=4!yjePhZ6S|zsgr?ZG1YL#*(|OR1u3JxcEe0#kfMjnIqDobGihmP zcm`Z~euv^`_gA7H0M7;89v};Q+9=?% z8mNB)FOnUEhsFUPfy&_xm&+9)Zw-@Rwx7?gi@fJZ=rLZop$4}pyC48Vv<^J*Q9;g> z(tOYD%qa#>cYXz?OEXih0SnRwOx@98@SX;nagOh?2jmxWy*DJ1GfmH^( zRiwQpSdcS)>STgD;3PE;npdUIyh@@Kve)6!V3`)K?F=oj;*)*~qiTU*T#o1W^7AN+ zOOP4QRHMRz0m_+f5^iqia%jo(bQKlwJh)JgXAHJsw6}N78_U9eOy5IY!+|ayyypqG%Dc{?CZV{w=pW7dQVX9Uj z8Ag0yxF9|xIE#NiWA9SHRYf>sq@ComO|62u5qy+@C@#GJnsR|{K&OHsB=(RGuyFaI z-L=oIg3wv|&X;O$5k;TcTJq29ybeA$?E2FxAA^TMBaHCG5{|5dyXfD*Lh#JKO20|2 zMWy~Q{I9*Qx)Vs>CMTAF7^WQ&Nbu4V_^5y^1PWh%a_PT*S0O)eUHPu9-+N7o3PF4X zw9^}mT6Q{T(e0`WW*-lV4cmIFnoEZW!w`O9Z=3#?o8Ok;YwDc8;|S zV$&APBPwLh9<|qJXg#HR0%A=}HF^XhqYQJ`Gqoo8@a_`A_;^v>=1?uei4RP)gpsKK z$Gb}x`Hl~FQ(iP*rpy8Xv5`aT&ixf$wOJB%z%z!5tQ)TyfzJgz(`3G#oz?A#MT;0rr~`2%_UloML=Wkf zdE&(?z<@$_uXA98{Ea(jgIiqoy!ibG>IGQBFKB6GKC`#(sc>PZ-6O{Eg=^oD`of^r z(P4B31c?|>&NBi=oHVQd7@0+}Xm=pyFB=|zp`H9#T1SfGQFyEHBg+LQf+1X4sMavu{;T z%a;%UuEFkHnEC3M<`{oC!Omc!p6Gd+h8~8+65xysV<3xnWzZP z0(eZA(7G?&t?qGcq@zxKAE0mNMi{^sG#S}U6}ye1dc zRq?k>bw~PBhr9=*a7K@Qn(~DsmPcEw5GMlztUA2VUW|Mq@dzV`_B> za+ZDN4V@{Z>SW9dgmOx+Jf2Asp8OQf!c$7wWJN|Az+xH%;6pZu$7NJZ@dG(vV4RQi z-lf#Kyrh$VFfcJ`T!srw=QJtELZ3fS7KRL%Xe!iPaE{42e`iw;{;bnxfBwq4&$(yJ zUKLSAJAs*Kx~!>i&!izEis~_Gh#6$!0?IJTp&tMrGI(O+;BeCcTslZbhBudEW|k$t zAW5jKtpDJNGGQ^l4Esy{L1-v5co=ZoWwSp%o1_1ShwDuryo6L{)l?mzMa(JUOJv`KP+-}iPJtN}?LaKYm|TQ3sQNxD5N!E2Rjarx#w?Zb$P~VW&B6_MN2@!H=eFH;9OpHK@C~7 zaIk||G1u!ZJHNF4=#5 z@2Vq;N%ucj+mUnwWnCzg948_1&$}?+?fcQCsNTG}a$1=%2`DkLuF8jMpvWqi4nqrd z%tPY{4Pr=g6v(G8>7qipcuIY+%uToJKt(c;#ZbYycSq5@bxW~FwX-tYh@yS}kuo43 zvZ2L63$hzF%~)gtxsk~%GdfBLuz&f9Y7{pJ3}UU5hNDuM98RIsQjGb9TPhkPz-;bs zw3VDgwWT5}U*FnZce>jX7f^-RK>B<4*KbGuOz(?%zTb{iMaX&c7oy1zMq6;!D3qlf zpjO7a`#>3=9YZMG2&u(lWPaP0QUuTVx{>b&^|hw3|7aOp4b)w(9BkFK z7}V?Xkcr!`^pA2XtG%O#wOZ5Z^{I5K=X(wJj~|O+F{i5KG z<~y=9AA%Sls_1|ImfEo^K5Rfx0qmYY5z7&rwr;jaVdyK~9*WVhaO}qs=MP;st0Lj< z9kXAf12pPdLO~Rp|H(ItchmYB`O!nNMgL@8%$DjFJLP&!D9QueEjK(@i-%`bSo z@9I+bb2pR~LM^yldS2aP`SSqKQbOS_!0V_6?Qw(6&VUM!9xMlsmWA#zp#Y=- z9a!x(y=DhkLPhNz?x6-!r@E$}&ST8JkVa_1~*@hM*M-=pd zRK*einX&@L=yf-V&sh8#5NXKVu$eN&m;uohNdK?3#p5+jjz0~KGZ^#(1Ke5eBbb3y z5Q<^*6o#{d809E~q2h`DZppMN@Z^iLHL?Xc14h-J|8*nz05yg0QxL^=ANo0dBL^T^LV}*8!`H%O33~ zFLubuL+FPAQ)5;!J9q@k!?K)f0kdKxO??J(`RY}GoE0DH%K;^zDCbC480=#rGL*(h z=lb!!dD%oQGoe8im|`;5T!007VA5&!20y9@S?j`pcl!6~M~hy9_eifPL`b+Y(L@uQr$HEEVGmJy0+~>TR`)&zWIHltRX;U%IvI zA%Vd(zWb_5R%g(wSO4PfN^;PzKcikci`nO$SsB;5erEyxzMZo#qyT4z31FtDF00XB zSo-;!OQV7&X7<;&)f$lOn%RyA`$eZL#GG~d(pxrFsMS8+Kf1>ZT|HY!)C!bj4*@ME z_m}hzruSSu6P|<+4RB@!_F+|k%UIG40DFG*6P2!GS5>^HRDtEQs6Jg^gJOTRT`$Ba zlYj+=e3uebRtZ#ILL?Jo7C6u(yr1e%Q^Qx1)@uQ3qlMVN{^q5BeSb6R52sX2Fe-!+ z4Ixqp6ZByxQOfYiemSTuXlG#v^d*wrmBfPY?5x$;Y7yf=F9V^xw%6CF_?`QTrouR1 zyi;8vy}a6&0c0wtnX&?FGYt_V%a<1WcW*5`q_!^JUVTst zDN%Ieh$n$0fOr)44_#NQKgek3V0I?z8`)Na>}^}>6LPZXuRc|Y;?*09IBlovR!ZO7 zpa=)f@V8xBot6zo376#^z5PO8g{uRy^QF2$6w<70RvS62N*oG$IRiUmm;`y|X*2uD zcMGFe;9vsc#*``>bsySQal`xEOYO+|TCTh7SimxD#Q9v3&_9hyx9USPv3J*t%8vNFHq$Ca<9ROAr?x>q3&2OADn+iVJJla zUxqzej6{hiGF_d7a-r`I7hD@>l`9LIY< zFN`M+r7%(I?ASG5lnk*0L^vc!l$eK%I(JznFnWkv2`CT7!H%~g*1X#HQ2&i2lmlXb zgV+R0`N|85Yf3h_oE>P`5cp@`s;$I<&CIYEm){Je5{t~2WXdEF05XG1xEdR6)kJ9 z27e)8Z2@Zy-gwAJdZKrX7al4L@cOD2fQmfb!HJ$dZ!E;NRa}E#UNtadZ7$~_XIzda zB^ZU}u)qiwc$b5!=>f1LdP5q>L>!FTR7JQWwL^kJREOA{*fZE13&v5?!HJ4&5+3TZ z%_IF=Y zf(J|`*6e?8zoDQCI}B6L?TPQ0)3Xy<&1hZslEjghE2Kny=F!eemwPE8me>EK^}eoasR%JqfeI2O~^4gE1!mfYCeParE9X!(MyjLRGp z%fEZ5{;*tLbizs92m1@eAGx(msuig0ap?R_f&1_YGEtB0u8a;>sp|~$qV*m2e)kO? zJ*6<@P@(C6>)w(8a@IpgRvudjvE9(gn}S46ZC(d{`=u3gKn|H{8hU|Ct-t{J=Q}Sg z)v{>GCt#9zp4vYlS3$V2-l}7HQqvE;sd6etN{s8X<-HUY$A9IHQgPr&MFW)ky$s~c zDrZ2NY&knAlFlImQUVz%Vp`b79%leU_~#y-{qaAY$2s<={LwpV`H9$Mj~p!3z0!nC zNV)G)S?2$VUgb&fv&_M^k$Ag_Zs?sjDpW3QAM%2jo+HgUKwG`dul9A!un5{D?&76}fBOiEQb zr*W~sJLoFam(?D$%P*)Lo>g?g8(y%8Gsmlk&sW|lrAYZ>{fml1iYzv0ff(-+zzmOQ zVNwzFAc}=(+3(lFo2jcV^{nd+Yh~Zyu~MP#Xy;N^!t`XWv}z znn1K?!$`F6*k9X!s4%Vq0^ymFT3SR*Jga5<7ZMU2^u#`}t5&bBxM1mb@0-1<>5;)x zln97&M$e?U%r(78d|iNgU)~u z{UJ7=)*tht4#aE7Kum?<5y#^W$en^X^x2X0%)X_cyRmBHBMkF&fZSrY^`99a#~Gjm zh*_0wTrp1}2Vb12fL8!m&g@?_h7qx+`n|T0IcK`7TwFb{I|dk0xfaauF@M#I%>x)5 z2WbgQ$_{a$2w-uU5Jf`B@P^3` z5S%y4hUIcrM6SUatSw-z!Rv>k%RXv2g%hnDoy0eK9|WX2|@zyfaolGy1aBY#11nPWWELwRjq z4&oqlxlo=WMadj1iX^MfFj2a9zH7h%E;+A~Js30Tw{DsJ!Od*vjMHjwKV!z6Opc8> zpKps6XEdDyV_j_*reov8Xl&cI)7Z9~G`4M{vF)DNYHT!VY}=gkelznU_S);Yajk}{ z>v^2K=goJl%xN9vMUn z)nJ7oGu*!K4L$(Xmvwf~jpHR~$yo$>{-r}~+qul+Y6#NNA@yH71oEf$ul|}Jg&Naf zC1r{HPgFY=kisvZ*PrKdP! zDivG^OW13aR-o(B5j@d{aQsVHBWHHQROobW1K(PXcGLx>`LaD9@U&TE4k=Sy;Z|8w z0D2+O`79EQ>xk?V8Z+L(6{6w(om;V>?jSy)6pPSx=+C_;-~exDbtkD-pc#dQNvV0; zwh&*7pVIhe7x^Q?<+WV?<85)dG@+;3@M@s6J;_8?yaby(j`*CB_XZQ)>^McK z$i%_s+yYT4F*P#;zfcI z^PVdF<|)~w`rl9$NNr2hkn0=^RL8)zmvvFg0B89yV04h+CuQ;RiJ;Bu0QX&o=g)c) z&|4*kPk#CH`b|(fQ#6JR612)|#+NgIBg8k3p?oA5sE8TUFdhpkhZq)L%i|@#XEWDP z%J=m3dZ|%H4?M)nSMkEdDzLr}O50wM2H657$%=a|f)e;LM0AX!{BMVK)r%<4TC^N9 zYZFKS$Wot|en6WukWr(X{6~?ov0Hfp10Nsn7qft&(U0B8@M$hq2#uSA6upV6yXl&Z zB=5E=qw}~3q`Spg0F8mF_bP}c_qAPTi`di9UwGQZE~k>^C$|v=WorbJs4VWJZ?oIcx^BI=#jMuaTB5GcA5Rv)mf&rqX5f-Q5m_0#L2i$K{!|Len^6BR@SAE}?UGE2xsIcbKz*uR5QvP_e zB$34000AX*{oou`x`Do1bVZ3vb8&Ecl{-XH`_S8#0&-X(U(b!Fr6hFPWOC!9;gp3Q ziIsH8<9S3fs@A860I z6;0UCXkrrapb^!K0uesO`l1+u^bMRZ*RghBIc)l(kbKNcu%wr_XVCuN6x7hH_Xn_a z#h2ekg|BH4!Kgih+o&^YVVW!2r7IGa4Hb8h^dH>v{(+bh^YX^yL8Ed`UqNAUVjgk_ zRO6%G9*}skG*5ci_67G(?D&{wvst;MqWvZ%J)&I|3H}B9a&+02M-b`Ub=;`mgPD;Kl$~?n6Xf%2YLd&ZA^19k{edK<3 zB0PhmY{~dGIZ0OD0{>gXlkgaHY#M-p{zw3ZCo7#WV~8Vpvdz3Jv;AFtA}DPbTCW?kus|g*2f1HAjSAO6cZ+V>m0_)DC2BSC$M!!?zqNd#?0uD zy?;@yr11)#1?qZ(A`2d5_t=4E_3A`TH|MY3jZw~}8iWzbY*E@)xN|84bZ{gWHye}{ z=4O5><1>`hn4~nm@`=J{t%(}PZV0J==}B!ELn{<>mhznCUOcq|M9Oncxr#VGFMVwu zA+Px#wekq;%+PHDwdLICWack7Rd7G8SY?8=Pu1b~0$v|2bGRw%`1!smfvH4iPsk+B zbV>-vuB0lcdfF;=E55jJGkh#+xMe{ikH^eZ`cHVmN*>G0h3re@kwa{!X}!ixh(AQN zo*%-o!Y_65fyY;(6v5%RSj_Jt+Wo8)GqU8Sl)jcLb;iS&>Hj&>_Oh-khmx%D%+`~g z=J21@k2k%Z5@{-{% zNVaOr-{m-^FHW>h#6)ZXToY5y*Vc@@UVPiHXO4B-1iXe9z=NFRbj?95#D^n0=9~@u zJp9N?QVFa_ka}LE=F^^!t)GbC2{MXSLlC>3PEFqjBdTrPjbbJ94WXUvlz(o7mG1J- zFnwqVYwDt_!W!z&bgK`d!6idU{{YXkjbB%L{hIio_a9Gq;&18o`I=|`(w2qf*lehT znYnwTGA;b1>i-OZJ!PV*Ex@ULFAf9T^2$%&-F@a0qpFaJMkEN5E(lLlZZ=`scuWh2 z!FU$dkOG>o8*!?xPAZ7I>Z;aTE;$v;q7z?}RzDBX>N%HZwp8V{M)tpPYxD)_=F%mg z_k4x?qfa(1y7N4*S}J8eMnyya^!?PG&9C#IB4&MGc(X-|`>+u$y8oLIuMUphM^DVt za*v_4Y@}R;NLT~Si4Fi1rB6w&a^q8&f$h)`l0emH_L;Vjta+(i$}6QOT*AeZh$xBq z`yX9T0JFoj8JGgTf}CMRy*c52#viL(?vOO`HDlC4D;h{-(@4EJjSD7jLA1LrhqOb~w!0qb`^HnJc#tD=NZ0XwGZ}gu z3r`CZnzsy5Es$6YW-mBYpXpe)KqpW%XCT@g!o>)*Jklpao9SvUBwYoJ(+wi+ry9K6 z-jrz!rv3znDW(S4WT9L9U`1Y2A0W}L<6|(hMW2AAH7~P{_8Na@$M!5o`L2UPP)jRh zT5#Gqvl1M}d*8r*C#k_of{sFXk<+};ZY;9t!ZC;vDhLfo0I)Eh8keNT7Ltj5zO<(M zzjn|ejhIj?SgV4u^##hO4q1jfq}%uxCl01|``6Y`j~^O}li+edV7bHoGjEtrv;G5J zS(BKW;A;a@2f{sfavc{x?L93KU(*g8Bx~#zBNQu)VctdG+0_x)Eb%#rEDuD+d3@tf zp7#XHq8m%r)zJaUAMSSlHNX9Ml#dSd)w~hIdU<-lhc&1QE^Nf_<|eV;{dzT!|60hP z=awpXj1rX3cvKo5Hh!ch(WJleY}CU`20+XF6w$ouc__k_vIN7sEj?oFws1+?BPrER zb%u#-mP(X=YyjMTtf;r>yg_qm;!8nW$v-bpVl&2-410)D5hDkF?Jvu91Ck(hjAq&! z9vRvs>T3YyJst;_%jCy0p&#lXt5-dyH%G$h0NF)Zx4l8;krc2Ob?Vcy-D zx8Ad1&%#8{fi0W8NSVFpg{1v>`@rdc z(8Rbky-AaV$jDd8B7?fT{|p9l!?@QLvhZqhve8(m1o2iQ>gZMM)nI;?2TH!Y#@GU? z*SusTpOamzvJYpicW3U)v7qDd`ed>dX>z>JhS__S=bvL784WxhlaaHukJnz`?VG7r zIEuh^vO8kr%}}L<{UsgG=eNA=%ELuQ(AWrfX*ClBjOa;zNSTVuvb|NhG_N=UYla?=8$j>Ql~_wnSyDXGk-+Dre`WnZtym;H^m z{MB78sjELu=iBMU+>cdq=8Wt;hQ;Q^_WRNoVaU*_u))xEy=R4Us#Bc zjR>X7c-!-ZbO-Mgzn^h4My&H)5J<#SodT`#IEuM&EqSaSY!~@Zc_GEx1CbPxXqn3i zKBcC?G7viy5=ZjcwL$@?ybCN$5wo#ncr1(|f)Tce7tt&L66g4u5#6&O@i=9Y!_asAuoEbB|Uwt@n8Gc{*3%5E0fs-j^ zz$6yQ?{;b8EP^4Jc^AmEkT9b0#+nhCzj{?64hm(Kc#IV=gLB3?Z1gfkLK$|Vo6$VI zuj&x`D%eP2&@#u2;EJHz)JdYfv>yL?p+VzyHSKX<*4&GbCW_f-Mi-$`bqQ|}pD2IB zWtbd=!jJQEDtiD2vUG`?4IuOuYt3F;V!L(q`fI5DHT1$epF0-8i%ZtHQ@7A30gB^1 z%26=YUCZ<^MV;C8NaW$V#D5ZaNs9E&plBd~b8Cq3&Lv$a=J35d= zC_qj#ejtY2?&tTksMec@(*O-{&(;M`G4!mspmY?MqpBs2*Is-g|FV*$)3T0a8eiJg z`Tn!OBJ+f`i9}OxZ~Xy2XBP5LlZ^4OYFb+hn=Z%aaK1O7;<%lFscNqcBJO&At=cCD zGRM#R0nmJB_MrxE`HyXLv}EPw_+}LqPNXbls0E5zOl|`4!F{ZUoU(8;CAWT5`IkoR z2CU^d3&`U2V#deiK$u?B1*eppA0Q#@!Y*YoT>Ew`1O&cLcKCb966D~8#CRM(K*{tE zK}4$3yWt?8CZcKg{PuNn`|8kV%DrK0yPcmnmEQqnTCz7}Mx)^;PzkHMofFQP#u02# zd35XtKI7$MaIb{lG#RDd*WRk`wtAcGj`HJBwtx)lCmZAnwY!b9zJYq*`jWq53*$5TG2Kd%x`l;^Shdau%v_O z27tF%N6ZGR8``zmsd!)8f6{wy&HZi=R{!*>{j31XQ=Jo0yJGNAsunTOoS?C={kkR- zSV|03g(f1MAmoPFWn&ecX-HV_aDB8nKzq?IaP=ir2w>MwHL;osPXz_YvDxab9L?Gn zZ<>VC(%`~It)w=!C8VfkA+aJg3W5b8DgwbK_L!F)@k#nTyQ1!l#{m&#d`G@>`$_)T z`u`mFFN9Su;NWw{WV+hV9Es}j@7!T#sZu!*T zCBuMA)x-c$uzM5tSss||eYmR@!h)4O&E@CK$nCj<4q|xoJ;0YV=#PSpAo-9D@eWz)BI++pU|GQMRyq4OJ*h#l z4eRhgfO6gnBp|sP1A5CZdP-9J$!Xkk9-t3Q+nT*lvUuN zw&h45&BmCog>5^Yi!&}*LU@k+wBBpRQ@RF87g(N$dO?)N)5ZCV-#5pqDoDU|;LUQ0 zGyVZ+l_wI75kL;#H2WaGu@G5W2_DAqsUrAi3bVxYXKO#j$A!3p43@2(N$MUR!fckO zNJjKFzY1&+%7{_!C~2-x;3?J_D)H-pI)>hB8~NK=90fYdv?lnbx}DQ`3ICWjr=NVh zJPQT=h@vO$Whax7_tVl6-+0=fYiOhNDD2(9+Pbvb*VmiU^f*mc34@k8mu-8jpl6B} zNMk>f7=c=Fl^Vyzm;`%akbvP$S)BmyU1#PVBhAu9RNW{5K{Zy}wnM8EH+El|yW|Vs zUR!P_L=Ushs!q1NWHne%j(*fY7*jmmp>2)NzM_mvW&dNUmLy1zfq`#~;8F~ngudC&Wa@57Sy!%+39Ba$J zZ*&^Kk=}WF z>jp@@E;9m=3aLrz4*h%XlP5WWrPch&xJ~=FcXDlPZlHiM!#IN|e9qOT5Pb!ixl({b zRmV`_S_oHXJ0GylNbK0peHAcdv8MLC)}mV$^qv35G2G1L^JnkuG5fD)jD*D^m{@re z*jHaeTytR$P@|(;Id=OlVdc!2nV30*FFmy4l$(eKClG^+N=RurkBv3p5gXp0CHk=obG91^b6+L-0S&6Nhwvo{blmk>|xl zWiQ=Q+OW6zRaSQ@+@JdK(>Joc|b0juHzCy^SV)+gUgKjgDQ1 zcZZfD>nMaOTp(cU=K68!^IGq^QA}W=u|*4)i3}x&7bD&-HEn49Z-W?T!nBS~SAcAR z)D4V~D*wm(c*+AL1pbma&!Nt6N=G_OT-$|4%oYBKiDli+1t6DGR+ zjLam0vDoeg$l8*g`iMc9^Mbv5ZzC9YP=(1tqSVSg{~JTR)?$ zyZVOk4ObU=F?RJ~26ov3h}1<=Qk8 zV3?OQ(VCZxI^3{}CN&AEo&o_wmNw{eG6$fK+QXp-*p0wc-(mdNpCJ>ES2kLevxg&6 zpa4Bsvc6u(f${Smp(K0by~*8q2FsEWR82DCE$}t#re>K z5oLoR0pIiLK;#HZBDos59?0I#_D7a=Pu3JfL&DcwMtPpWOiqTG|6T zm~5)(l2iKm=Cx+DTYrO6Z`XQHi$WRu6|nKkuQJAmDov|gQNKq`R9#JQ^xWs6^F5P| z6j!66j7$SQMszuLNaW`a#JK*!FEIs>8_6bkmhiK;!+x zi^{L^8x2f@M1+^8RxN&NM+-8!u?FYs-2rn@a#8ae{gpn=5+NlAo`UJ5J-1@^{rnul zS~v=4x{;63+}9jARzAZJP2?k^o+a|HCHgMgv6m^w$xHIYG!(D%G@Qxw%_jJYS9eY1 zCMupW+^e=CY#i-j!jVYG9TB}w@V%9POqFY{3H1)u4KZVCK6WON6|TvM(>3Z11sDU4 z)572D-)HL(41fj+p|K^wBH&hiHA{vx?u8QvTRwUh@5A4)4bS=YpBbdUG;DNCUdp#> zC^wXT^R_ri=gFD z0iASiEk5aaIk+&Od%NYKs6%A)+K`G>{L>QyE8KT#K^>bK%6{};naGT&*HwJ9 z`;W;)+Giy!gH-1TC|$bw@>FJB(yP^|aC0F-)QlEQ<8O9*&|@s24ZL!>oHR9>%9^D$ zM*+u*SUGW4GcBeMr(5={-D@3hjHLP_$=@Y!HwT-U;fo66lm28_(r!VVN~n47#Mm`Ua$XTKHe0QUa>ioYx^8ii>=CWlczHA+HOBp zPn{-P_9KBF%=F}B$eXz`-0Ch&{iBcB^V!#M=WKj^Y_lBIBAxroDB*JPS3(Ptk z9885=Bvik313_8;i%?dINl@aATV!ROR@lA96I;3yV^=yF&);6GW*GVb>qyRL7li-m zf2Y^Ke%yl0eYaQQ`L^pSQ{lJcr*2%&Tg&%An0$&iPYK5Ir{Dj29H9j!w|_M$-AssT zJl>(=Cv=>HG`&}I7tuKg7e+P({L3c`8F>|zFQ62l9xrKG zn6_w)#yPy|tTPPj2yY*vcY9W7L>T|0hHyf5M8ZRdNVyAW85v{W)erW(V0%h(;;lyi z0!_;(`_k17)+Rdjom0v}ZOWLkTtf@<%&E_riywW6=K6`6X?xv~pbA@(k@JukVmh~D zmGCfmg$U53ssV!r#oiL0k;aU)^r1+Oy7YW!&uP8z&wQRcyrOv-yM&{?i!fD28C;M6 zF^Mu2%g;W&bk}%B@M`rIcsNn`N}UB&sNi28PZ^qZXHOQFmz-)2KhHfaR^H+lFVlp* zszSHPbi}@+Jvu&64+0(c2M>KfJo|b<39Oe(c-=%chZD1YbfKkccHOSg zdVsCc#}sj+J{coA>`fV#M7XvL$24AQ>c(xtlk~bf;qj#)PUZn`JGWZppq@P46e3AV zNk5O;kpf9d^u^fQTUL?v0F?49pc`J7C!ltMehNqONn+dJMRVR72P>A&qt2EHX{YI=51piJ|hsrQF@ zS7#ajCGEw4@P4v9J}cLa-|cHM_CLLHY$Q^#M*N4Yf!d?eUB6&_?BS%qDT91aG?;@i zi+tP{4;NfBtsa->W7$ci282D0TL6$O>4M!)TyYaRki}^Hrr8d@tK%r7wJ zM$c0}PGZw;-z29rxx8fa+#6-m;cq4M*MZ$;jvjn|a^+9_qM!1>C6%d_56P>uzV?tM z4@%?}$?~gsY}EAn42_%bn#<~CERFJN^!s9fbL=ent!{Pp$B!(>Idr|-%)Q*I1<<&Q zAp%K)!D>tp>A)+_7ozKslL1a%p0RM-J+pjD!49zn;#6k^iZMMr-b7%7Rn|&hI0q7U zV8cmR8Yf1L%@ulbsgm52&r_N}Z{$2-%k7>?R^?q4N`G z*r;l$8mqV)p=&v?zCYprW@o-3y%ZmhiBoc=zz(9q{g<|BIP!AS7JAR(-bVw16}sjL z`NJ&Q??Hv^(Iwqr5&#j?W19wk+j_U>gdtmYWZYk2hujc-deSjRREGLSZ~}G^>H6rD zjE1u63dclgf00rj5zt?X#yD9OfQ5r2#zExMn%TM#g(;}c4cXPQA0{zRd$e@MhGnHf zh80RKmARa9uQf<40y2qA>)30fqazi^8|#khRnQEw>cBG!s0zBFQa16&C^E-;U4N^t z-c4(-c@=yD{1)E#)T}F{)ROt{UgG%W8M^kNEX}HAw5H&@ zluuay?C}=YFsa0>c;kZq&%wP-vEk@eA#5f!m_JrP;Zk1(2w4UQE3U+lInJz?1sVmsuQI-gKbuP#UYtz= z7pOtVEkdO#%~mf?-Md;)JIBZ8HgWcOA%Y@^bDq-_nS@S_7_Yz z--<3zH~k@4c1hfLysClWgs^-vcQ8Q!);oLf!n;-vkN1ah336+ojgR z4=>yR>%H`7$=Bd8;M$tgx{-r~-@8lIG*KvAvQJiR8Hypb9A1cxE8u0Ulv&Ww>%@=0 zI-kUN^CDx(c_-j=`n6l$V(0{{cd^${I=-PH=!RhUA zblo0TP*KUirGf}c=IzAof{N`_Z)z1PeTi=D5C%Jp)N~U_dS%5Z!Nw_i8y2N5<$SJ{_*d$^t&Y-AyK(2Q zB3)`uup-y$=#j>~2fa7}KXZ>UlPbN3>&Dj-ZezMJc$|0P*Ht>u;>qWJshq(?hng2ehM_7PUGZtY2SqS8}_xk`lVCed2E}Z5liY1@^;UI|63D@?n$DM|U zFxu1H9Q()lumj(Bmj2ISEZ2Y}Fdcdl0N&ES@}~T;!SP5E9uX@gKDE^iWLeT?Ui`Ss zBa?7|*DM_nt5@mBE?7Quoru`@O2xGvgBs1K~BKzHm<- z4oG@0Ao>e6&Tm-N7-_e4p1(~YnuQ<+N4EeV(CH%ANdh2FtTh=Oa8(93 zvW|dW?7IDi*MBOk`Gx}U7$$7b93jw4W@}AgxR0a?oipq!iOXS(*|$i`vfo9OiI6|= z?0-2n7$s2)M=f5%-;SpR>u|4YAmsJDBWw8oE6u`Yqo+%D_zGG<(7Yg+jiw6byDKQX zOr%oiYN(=4g-s1f&1Q-97)Gb|?*<-hUf^f*4QayW$$+vgsTP`0pV^3g4T8f?6mB^# zMx;%fDpnl?@le@#DLF(Etx{6i;w?ce=bXNg1;=#~=%ox{PSt-nHJS_wb^Ytu=%=UySCH$d z%9&ejdg5yP3r)35L_Z4Km{iVW1cK;?5HY`Vw9bd=O2zrW1WhC+68 z&@u>nSxm)+BXE(YRD}M`0zcmKe{3!pKB;v*c+o9JoBZ0_M3HofI?7ejVw)s_8q$mq zE{(ea`yA4Eh_cS0taV|58bfwAZhv0aArR4VZqTIvVeKBxGUAtU)!t!v*M7e~zDk#Q z!hToU4MqGB@H9r}TOB~$@ZM3Ael6o~>)6fH@&%gOxbZw-gZigp3bh4-Mm?B{%&-;n z#Ma=kq5#AtLttU@(d)qXtLe@HEh+V3ivfBFM$wls96M2cEJ-#Fam9~N)1&TyFG4p> zU|Zi@l|`QZYyroJ8izA(9E4d{_r-iEvyjfJF-6--to@06MEsI;+l*jy&x{7d5w|Yh zYH$4OY#rX=CSlmn!}X~+b827MV5qtuO>^z=w*uPg(Y>Bhff!B(V zd*cG`{$pF)FPnT$Pd%`{C2Xo>KcB)X96?G3U)x>Lvkfs5R>~M&J)RTSxZ)^#55Hi+ z!VsMIS}iVjUGfg|6H`{ixa(&xYh~fUp*wZV0&Wpo7ukr}oQ>y`8Wyu_@7|u+yFWC8 zMkp%g$TZ;qJ~V&|u-)K)abyg9*_n`!fV@tg!;QAe;B+QwEzNpNSE3nAOTZ~nSAf+V zh+BNh2wH)+Y9lN?8Vf$oV5=NgI`p0luhq2D2&&AK5Egej8nlM~-U<6RdYBSrny7;p z|Lg!)Y0P?OV^*3pTm=P<;Aaxj!7|Udr_n8+Gbu1ppjFVJaG1d5Wl-=KEgT}AwglC$ zAVw_7bbl0fAvhvrg__wNG$ zW|eSYYRkGig4K~Kx5FvFz>kC2gJ@AjU@(}VVUWx<>jPm^wDB&GhjkTeNl}Kt0zK_J z^2Za9X_2~uCr;Hj)?Wrzz;M}MZ;V~tW4GSnH@yG}V$;T7NxITR=;=5xa1)UVw!n=( zeV?7(*SUN2?jv2MDPMHr3^i{sUN;vo0o=C+r!YV8ba}n+7}^I3uhgbqwIJB`{srjC zgskoNKzFMoH4PF7We5H$4A5H(!?BLnNkd#}EH z!8a`*$T;ImdZaas1~vfYG~%J*=ArBatRWHUg@XF)wrze18^-mTvXZF9RZ?bEFhUB9@NBsnX znXopC?5>FigcC=`Wq**6;(2%bpn@w+f}g!Q$#?XiT}TgI@31&&{G{iL1nNvobQ&AY zblP+`KojySChrGn5B=Fs!+YRvWw&Ja0X4G#63k4KSs9QSNwQiNv>##H(W>P)ss3-P z_)wXAdlxvq)4bhq6ctMMPhH>;QIr>L%6!x&^2t(`%L8P1G7<;BwTzVM7K^ORc{e%G zZVwfk>lymh;%?=YCN+~x8RI0GHL}0b-BcbnwrrneD=iT0USJzgAo)^k+#e%FdANSwdMu6Gdi7Aae>adY2O+UvbH zDLon(D9~2D42@2yPxIhV^gQsBbYs ztkdpr(Hk>as!?+Tt-oFOy_*_{BE)C;hH=PzFT{eX^JJ%fE0agMU@AebH`K0n6}FO|gePcKK!9jB_1+fV(vVVQ_E6f0jfl{T~Zg z{aJzt?b!^@L-s(9g|DsKCqGmD53hw`!chMc25!#3PVRW_2hfy^is9SgaZ6bX;_7`B z2+U3QO02Y4ABx;mcuhS$wM=POt13$Ftn0o%E(9PJ4>GV<$2^}>f6CG$vSDc%X~#&c z7^wq&w>IS^hd$TdPVJ$v`%YnC?UqdnFa!q(6PJK%Rnh<6-E;$adXp>R#op;XICM5u z&v)js)DpV26Buu#$HWfNszp$|!HSjgPGncuf16(Y(*YNq{^wno&uXqYa~||dgmo;T zqYNQDH`e!!5-3zh-fy=KH>7GVs4QOeer{%gq|m{KZ$&8Ntly-FCjmjh^0^JM-+&wh z0Rb#C$s2$q3GvXM00PeJUA_Zx$WJ2U zB8cEIW1`IY^9y3pUuVHz`{d}aTo$Hs5D9+Fj-^(ydTp)?WA>{Q ztjq=Nv~Cu!t^$>Tc^v$q1{dJQ?AEweq_#nI`%?qF-2&PeIfHldRNs zV1_?| zonJe$Sjh?s)NWj_k`Qt^z>uqHUjKsLZ6~-09A%C32gZT&Vt+N`mhAL0f&8(7%7xcE z+)K-~`Q1I{n&MAIR=XnctUEg!Ky}i^M)nCQ+W@LgB5y=^vrrHFkfL}K2^&|jS@{m3 zcHk!pvnz3TF^Kdfjs@BBcxG&3&T@h7FLla(9lCG%;<%+2m-am-6Wrvbvbw5MZcyEA zZ}PlE{cperVwl9sdeHbLR&@1~DJIQBc0zozEkiB)R)TC4bgFPC3%$}v@xFMgahlOO55gJ*vHqh-dO<#jx4iI5f z=Zp=&lg*u|yy;qo&D;D(EXVJ2(%P%WSltZz!_-z8(_XYUqcAy~Xl0R9&eF8bXl?Px zFtg`8IAt8$4O@MJ9MNuc@3ju^6J`|C6<(F_Ol+8p^!4!a`_m;4J3o*w&iHlPkpvCC z8q$3ie}m#>Lk4-xrc^zg`4_%8qeB|$_Av&~L_KFKV< zb?Za&kqWTMi2q62JHRmwj9J}(pZ1&DMCDA@2_rhTQMzCpq%-xCzLxAqlLxCaX2HiD zmaeB!5cYLZE4T@rS6IS$)tnM^dr$Lj$7Q8>1XXU_t*~~8C@o3WCQ84Dp{b^_l@SVQ zwl>d^c-FIWFqNMt7pkluIDxnGaQ2JDX&&LssxLL z8Os^N`B^>b0WvV|j3;xNanOfHBx z5{x}m^yIHc$)^B{F$+^B9}Sji!|Hz7XR}FF&LF)eB!;Qs~{Q?obpNT^U3<^`|* znO`gt+a=8q5y#a%9w&2t{cVn$x8D>LIVrB$zd3*CrP78>*ZFVN3cELa!Y`)Xr>1;N!W3|naNv#W zc({M&>45|yY=%oJwG3Gt*)wNcd*3v&fBKhB$!3YwT)(`?6P(-;P_SSd`k5$pC62l1Yo^av^HN;Lum%$NksF4NWZh6S zOJs3ac1A>GC`+35<|dH?H6y z;cKyzz~?K5@kFTL8Bh`7%|A^YH~5Nssl&jeUdj}k|?ssdpvKI{kaIvce$Sbckv!ErgNPRqAYG|v``^d|QGIgPUYF0+8YDv3Yu zIhzkBJmMSpPZ8Pf0JH+lt+)p0)c_xaj$XlK(ZpAOKRuna%#xRT0DSm_k2?b>jnOH2 zNc@i~sA6Ke+QHi>pkphisL1Emr#{b*lh#oHq)m>uZeY$ge@G+DR1INc>Re@dwmgMh zoTJwDuh*HiazNcD*Qnvy8;Id6fts=Sd_8l-_-{A#py<)a9%tse)tv{s3TVyg(D6Bo z&<%$s9>e5C2d~>fH@{i*s_Q8D4Is+EiWQ@1kP}8_-9#evwkJyvnLb31k=G}yWVo&R zi#ob97vZxsCc2?y(0(r*&~4s?JZR;8>_P&W2vfZl{a4;{pM@<|+d2aL=js7>K2zMv zA-ivmkZ+f^A$9vq4M_9_8O_vDpiq)USBa+tZ7GN#44AiiJ-l zYUrvdJDM4SWQ;~*5kZ%$I0X9lPjCdh9imun#cD@4Y8|_N8$=n)_L2SWF>D&iiHkW&&vL-U+n%E3X!$1?tBs7-h8Nm~k zzpBCaV2n*13*Ra;nLQw{BWwW`Z7`CNZ37Dtp5=OtbuGzjQ!-b6P2N61Ww*X|!Y@Rx zz7}#Mv|5ZqYv+A_&d?+WwPndRV`cGj9MLxX4unaR{i%o~tNhH3s;SlGEgr|nf_D59 zkK{4PpcM1)HBK^7gK_AOQRS4T6euY{gH^%*+tI3)W%JV|3XO+f=*hZPWgb^14TLaH zjSr$=d}I(Ag()`CiI`+Ii*&f2#=1LPQpp!)>D1dHG(oipWXiA+LTy82D?FXEk%Q4R z*Eckr=Cm!g$b^OQcfv#XaU$pu1Tq1Ie1x};CmsS3x6uP2ogWPu`}#6B6K-z@=!|pp z;eu}$7ne*DhgbFU1m5TxNF>aPt0{;cRCdeOUi#^v3D~laXH$Y2z})GOABwOl7)fReLQ3@+hAIT+ROAfhe;9W-lGz0XJ{ou5ZXKig+QR4nRt<2*_;%Mdc{?{ei>a##A&sA-9 zi3dYBP41GXB7l#7#$`j_ZG%{{7dIX-&`W=Hyn?TEai|elrYe+9|V)7ngCC zY!wvq#`}$-#qctdndWjIPTa6hbNg5o;Vl z^xq6m@Fsd==h&fEj1W~fppzeEl4>yG_~VMV7iye!&p{xUz;D45end6$AoEK z9dpxy%`JjW63#v&9jmIkw5L6<=by>??U(OZ-{$j9jm$&>Rk`Rbi*(&E6adLvza+G|p!Sr!j{F9?O@y z0IyZPCMvXfhyJFGYPDS3G=FhVwSip|;k&Cn;AP+U_!YhL;bl$hcAQpHwub99zgI|2 zqHJQ~Ve+V268Gy+5lB-!0}#{IO~;EQOgvs9bA!KZ>-s#u+0$}m_Ew8~zv?Q1_XbL) z;yxDskl=Q_#Q#$7Z`)@Jj?{3%rg5iQy#5_F*9?_y&Q@hYlq6}Z-q^C`VFhF`=$LTi(VUG2RFmA|*Njyxlf9E;=#OWU)5 zb7GI=`*{y5X2g`!fO=`A^xTsjp%b3E;Sb0U?SCw{FcYq9!gBAfW-)eiHYM@R)unSo zz_)MjV(e^j`HK+^`=FoI&iJ+3P1c1b&*oT8kqp<5kl1|%b~ zRjAe}nVg5>z*)q#YS1-ujg}1l*||O6b$h_9m@zL;$xx{T!xzVKD7J3}pwUVSN=#ahKScm!b1Y_laN-o~dYvFsp@T2hiGmu=-5K;gpbN8MGPM^puE@ z#ej)^fOV?R!pJHv+{mrb_W0G8%0Q#{!?p{j-)=$FR4P@#xMEwMb2B9MfD(AWGk)d~ zwb3099*Coz^lTnFRNn`2W5;G3*y-t}eQdC)&O0sxcxM}EuKrk0ZSwfu9{&Zp?JH#e7etwFC}37Mj7_6 zV)i7({{c}!uD%q^oTkK?L`Hti8s}?F3$_giGBha@Oy+EM1Hq@pKD%Dg_*`DeINNZCZ0ikFVmdpah#91KBC`1)HF z(G|y2n}`ijp&7zsbIp3^KN}P?00J}16u!ExCSHuJZ;^ETIT&LE96^qf)_<2y#~E{6 zXH_Pt#WMdEFCt`RG!vEos^1(djG8D>3eu$d0!$wR&=_C;@fp9#XjHbfnbfuMD)~23b zmVytlsv9@fs#8pYq!)fme>X~jFE?(g^f;YR?e*Qhx*5kyUF$+d?4LYUa&#W}YPrdEQ+sExExUiS23MlmcPbbuxy7y*p>Z&#ewp;5YUQW)R42q@h)69j7~^r^ z86=Fgve_Xelcv6~K-mJPE2esERJ4u-x9`8-pRU z6^kgXpzz457s`)2X>XN&p%!tg9CBUQQft}CDYa@xAc$vVq`F1 z5`@Sh{|k@L%4e*bg(3i%W8dMLNQBsvI)C?yC7N=?JYsVhbtyX^I8s&0413!pv)X`y zL$}F_Kvu7GGsbi0hK+?0v>_9eck`(i2-4Y|8)-DUL)V>B_j_sqWd3LYs*QT$1RmTq z`y=0g6{p{yd9?QPM)>e9=V8gf7(l-ARAG1#fcMB=ePsmALP3`R3GzUKg-bBS7$E^0 z78Y3$nCE2AcrF8;>}~`S+-T?adf$*X6}wz%V2U5BS70V#GPPcS6A7Hct7-(VD&!4^ z6<4oT^z>wWJW1Gpr0&o)_zMqf3s`ILrw1oFJe~X~b~3A1Qy4?!YZ@z;bDcadIC)(Q zWDA$%OlLDIiVbo({zNdPoXU@o1vz6f2eI@Z(2@YcSSmLDOJW@f-_fZZkU zq+93}_VwR&C1cSJLTs{Vwh9@pq!0?9eQdUF0%Sy_NmSl*b=%WZX9es;MVAZ2fBbk& z3eEl#Rg8e5O!pBmJ_`NFd*2`5K9DJ_|u zX@sVZgZ0T|*viA5$qaf<>mKRi-<@czbNJG$a{FS9c{p(;z zgXb4wl(L;WuBd42Kf3h$_m}YX{UR!{`jSF?sYdVhg_&zDzihi`ws|m@2((%}V&q5W zJGm-!eQ*T(u9xaB=l|!oYbO*l=&HE&T^9S#JEMM7?u$<>{a=5g_8UI_VtKK9o>T+! zKX_gJu^nR?=>F&Gg_kS}69Qjft*ZY^au%2rKHM%uPVma7_pf9Emp5*#$^Um?hc%i7Q*( zQvD!c*3`9re63Yo+Gny+A|AtryQ0dgOtkCyRd9 z<<%>`wyjM4)YfwR_6{S^rUv;}Ua1kR9z$mq&5`3JiseXSAv0)XxuIZdFBBqU1|h*$ zr<(ux0L#$}EFpM-g5HjR%=*4N=%?E!9w|BcfGYRPZ+@eE#LL3RUhGwy`8{~S*|oHd z7*{p@yqjraO^@aSZ>n8dcz^23>Jm3}gwc?E>3QWlJ0IRv;~NYiWqh}q$QQU zD|&KoJ>^*f!wV0Zv6l+H+acH5uguJ=eeSPXPKSuk@1nK<*Qd7`bL1j zg1TI~cZdQDRbXI3sY@?>M@jUS1K+_^moi?L4xtE)+9}xx=9Jw)v9K>}0R|<|+lLA; zty%@9>H=wq1z1i7Rz&`gU`0i5p!QW(uRQPSFNJIH7XsE6u-4#50{(PH&oP?6CS8cP z^*-3ux#B`7r!csFKp*i=*;f>`97$=s9@dKLC*!CYGUgpvFa zlT0@qK@PnjG+53*>w7Xo7D~1JFc-|EIRubA8e(&a3`wulvtTB97h%r*=JKE)&HW$rlr_SE=guzugZ8wc0-+0Qt15-3+19GGX*~3EJNGceE zAP&M`w7ypHBx#U)`c;%{e~2YOr~%Au#i8w2tG{J;fYwBrfD_1Ty;15;j&R*{abfbq z`ALfKk|{S+V$z{w<&-SCetdntu;RdVPA^X|m0t24=;6`8pqZ@(cDA5^vfh80_r@f3 z^{uF8Wa_Vfp%$CuCu}H1y=zQWWi@RK;i_CZ5+c{JD)JG$tM{IWLz&cTLvc!(U%zlm z#fPV%xo>BEUOm>jy#KkS{fC!c?p`fu+Uqy(EG1^)S8b>dMG4xy&z3aAG=BWPDlq0> z_2w)XT1fky`)0Rit!$op>a0y! z;tUBWO_@4DM$e1tSM#+kwJGp34TRE=NGx~K%G4o4gDVDP;~PpNKr&&Vwr-zOW~*#m zW6wQ1WeERL`d_?l`a$_5N#T+nus7bm@6BVwqdab_TKewo9u8nF>_F zuoJLc0x$RbC*Ld=TrySvp#@`lU@!viCVFQ6gCR7!aRieK2;75z^DQ%Wbx*ulXHO+$ zTBlY*dljkj{~iZu=OFo#I`$Y64ft6$O1PYnMbstdRRlDO+@4eLx&bp%0EWRgZJcd$ zBh5hzi2dmmM3JdLHC~1}i-Q8y3TlB`Murk_oP%$_bm@2RD`_q%Wdr%fb0ehtor5&u zQftIB!gDQNJlBs`$dA6O?AKC)9An7Q%AbC8#t7-w`;9xhW3*l{EJ=K+_kwokke))L4&M64%PALn;5Tja*3S4%){7R*W=xHyp z{rVn7?srJq5gX^(4{wG5ACFkYed-jB_HPbAs$yygJiNeZTDdnKYHR9l*jVnruWiMvAA`wA-*-Ty(v8&d^UQV z(~B&s0wjy}6W#G%I{LjCBY|!@<;?vQ9MLi$bDp%?0-%gjDT9awM|z)W6^?xmi6&*| z6oPRmWIma4YNxP(Tquvr6?p?EVxMlg8gL>xrbenpi-S-m>?h)aT&S9sGoJ(};yrmd zkpVvnu)1w)t5<)KU~K_w4SpoxPiHhdkrt2Yh1Hn_*fsytP^K~~LI|d-S0{Rw4E&V9 zvufc9_o@#G1k)$nil8fxo~1)IUq2=1rhB(iF;`&J&awnYps^C$`o*7fE$8Sr3^b@A^eZ z+(eTvJt;Wf`E<>b$Um)rXb&>+87_p~ELeffgv78rJ1l4fBy)NNms2SA&cVpUC<5S% zPt>p7xg?FV72Do<0Yd^%0EzjB=3j7+NAk&9iaJy*@4l)Cl;u|xQ!R}*GJNp5s*N1E z6N2;3s828P8ivx;@?CIfl*8bSkT@YF)$lhIn{rYC&ZLDD=fG7ewi;lS%1BkZk4kw7LDU)uB|c&7^NB4 z%VG8w0gbpq%*O|wt5t@C@pta4-|~}tZ)n%V#{}^sw=VsEzEbN2xZJ;Q*}U{uZYcR! zqVBVg&fV$t5uo9OCBekSo%df;XF{?f!zwhSL9)bfjh=VP%!Ab1Khi&s@Y%=85e4`i z`^y)=_fwsFS6V)VM2o_^ude+y@4CFqkg~dQvNH19y<6wO=PE(U%f;xkD8^>?B*RqD zKrrANPcQxbchr(u0)?Hy5(be(;C488tlopGDE0eL;(ytBwXCDPAkgS?vitB zl{VfHg++mUg|}|0uQ*q%o~{pO;riOP`YzJ8y|n?Mwj{SYTpZ_!%cCz>fdsv!!qR)k zjA=$TLXcfN|L8J}gkR9TF~{ZS)$LsAZs+!HjnlFzqZcV4(SSIM02Jnwa;-wsfKuKi zfU1n5w{EEdO9T_w5OUWuvn47R0T&hl*Blac@&PYYjb{f5qV#`$du9)~i5@aPW-2VyX`0f~eMT+f-?+0D z7s4wJMAf;&0^o7oyQ9|Ke(Ba)OS+E9}PCT-1U>j}VP0Tn=Pu zodE61@hA{e(WI(;=`bqjNHN=4t;<9sVu2~jO&6DWddRN%pJ7yy5Hd~k){FZE@CJB2 zI=T<%z=8m%jTr#~)fx|j$qGdPKEUfC#5rP>-`;Oz=dC~o$%+5E`LAt+ z%!EW0`*Z7=N5Sh1&Qo9sc8wKj=8YkU%K=dupjK711YFizkS#x#DGUOj>=;$~nw86_ z&`S+#S!fG>LBjNMAVZe3BhF@1f67y`z$m;9&;wVIGySaCv^M1oV#~P(Osg0?4ezvX zJTuwv_B!BoGyW;S56QM^>#xC&2&^q&t-+src&#JLa{5f)SAkJG9%kTG^`5A9;1!0D z<$s@9T{uASsx&8pOyj1Hw<$=^UhK67`6`%tIxQ^c#S)p$_dZ+OBJi49I0tVoIKB%$ z06r?Fia_K956Ce^Hn-cDFVxJ5Qy8iTy@I4MzdjC=DbApAjJaT@Zb709O=d7(ezF!U z6f%p=++qr$Cj`w@4#*kA!W_EL<1f^EZ2;o}y+fj6j+o0ZYAo7iCP1R~SkE0Z6^v7W zSjbG@xoi$cvN9Fs@!%^S9IzTTN0gQlTs_)uf`y+G!yl5nQ3XxMg-R8d-2%mg| zg~)(}yx#>Z40R^%2_xde$7-vKG7M12K|5XqX4q!`1mbciz)*oEWrvBK@@2%-)}LPM2n=|6o|{QcK`Cv4 z@^GmgQ7%jPq1h+E0WC1(lM{Mpj>zH=Xh*dQWGYYrgE$F4%y{c3k!K_Vz^UJVpjOi! z>Fq6|9XD|i&b(@r-jAkSjIA_h&FU<-{P6Wl_dQpPzwwPCR@ih-;Q@85ios-RM14S5 zYRAe&^(Xq;E_Z0Qh2$H(Rp$>Ltc8i>z#IDpIdHUO<|}E)NRUA4iX2z zFa36}rvS}OxPXUUhuM5iG5*8%lqFf!lIJ64RT>e<`Z&F>Z(gQe*uS`!=HMWW#vd%2 zEN!i^WT7GhG$kP@0mE=50*+Z1%Gz#so4lk$7o>X%0sMD%F8%GBN|Ba*xnIzG^QKZK z1K-l`P!~h5HdZ_KEyj}gt3PWLa$GtmAhCz3AVJ%)uL{`9n66Tu3Y5~H)?X4tpr6BQ z5hi_lYrTCeztsc(-rEX)%f(eyZ?5$q$&#|JEhI{<4#p`r=|}JAol{wd%?i=_qQ8D) z1=(<_4?MfSr2YMO)P9q*PMZa)29sXgRLJd2f&z_E>bR!g-JpW3 zchOEPrOv0GS_3AUqU>)%v7I!gx?~|a`LLAB?fa@NSPsb^E%h_k7X6cZDu@4x-WZht z5*`C;i0BqKu#24U(U~ry-gQMS%aUw-yRU!cQveA^066=2{|SVA2ePC6%ZXLhD^`>- z2|hl?Syki&BTxi}9tJ?F1@H!hn>UvVo<=O1kTE;s9(=W0ucML10QVXUvUK3c4jvB%^79n|dT-ARN8jJ+~<~J(NBI%N05sn7vMeuw_ zA7ps-kjr{kkxOz!6^+7xl+A@M?jOJnyK^A5{Jo)HHV0pLqVxuzv&(?q62{=k8HM0u zMl1>B^2NTv2NHy(_Kvl)s!}^>ro;oLlna;2T8i&u^=kMCTuu$hR;ECZqbh@Qc!%R) z5Jc(f-exWWU>Tn17#MO!R6$c?AuRA3KP0*nBQ+UE|`Zhso1CApAVnBr}4 ziE*$8e)^hnDh8l*goT}P#96I82Ly>%N*S*!8R#`K5aFB8sb8{-`>d*v&T|8KMOXl1 zFvLOv?`na{ksK8+-@JKg!~6>@gsYOhR1$b=sZ)?TvW-|qN?%=O2@Iu-uG*-ZRkpMK z^diFolX*WzTs6^up^`ndVUZpa6&qL{YcZC}{%ftA96`MZBXe8dIc@i9WP}(89_VB; za$ty(n0>NeH%Mh>h=eOQ>b#Hk)Sb;KXvGBIbYOz3{L4&w_ z>1CP#fg9QdZocQLT7)7Q&86p+Nrr;s^HGA#yyTb#lp`-M{qOe_lKosm@WsbxjrAM- zi*Hnrqd(+cLxv__`9;rh!B=o!i?jTv?w8Jj^n9%*0?Dk%+4_9_?p1Cm^3OiKekaZ) zA2V#mLRBu=Mp_KklvE4@$W-h2t+u?{u6r_IjLUEJdt1z=6faZjb6{CLxxD_mz%cYi zIbzdl5AW)hI#lau=}y1@K!x=27iRB;Nv@vWTa+;paEfG)jO-bKjJkvfeBWUZDTRg^C1uYepSgyRUKVr?!cn7iy@GE!J?aw@EJbIX`FQ^73R!gDeg&XJ1{q-p9 zU)@$TbuT0fo%fTB4{0wAD(s#tVToT~e4>_+gboWDRn-vPd%2&*zT8bzqt_6%m#vps z!LS@22*tWnYLS<&_(%>Km#Sbqe+{8&O+YoZN7{R-J#=(!YPGOu{9?o?NV=-6KHlrRUZqkMxauchqJp8aK|zUT656)0a?|cO9rR zPFp&oe=?6EGz?%{qJ$U}%lxg+c&B9(YC_cB+#g5h+WkA1ZrNPBKa}CovkImA;-cPK z6@gR$G~9RZu)&>}z^X!~z>>tAWMpXIH})^OVo`<0!1Zi@2Cjyq7D^_Kam_i}uh}r` z;SL6T_OW^zZaSxWd*ma_tJBKC7^9p?py+!V+sA9QW*5{NuTs#^+Aj2f@no_{Z9tW= zHyeSKNnnY>IKJM$kHN=S?uY@~`x%`Bs11Gr^4rbuVm>R(Cn`Wc2|z|Kr-+(@D%Ls= z`9sJQqdc4f!RRV9geCwf0{Ph7z)wvY5KE$WDj6&Y%%u)5CWA6Ea}4<_F4L$rVH?}b z2Mn$uYQO@1P%Z$xp>M^iDJ++NGVogISAjQt_3A_cSrOhSM?9Jhs%!8U9@ZAH*5D0> z*EC?g4tO2Hf7m9SBn&eao{m!(_#fQnu#fXln$H%H?*R{!)hnPJ0#ox$H8P>k>>*Vv z!gQ7svvbgBg0e6N8Zf38kv#m(84QD+&B#|26)#ce!`U_8aJ)4oE(|S5F{Wl6IQhtgLIAoaU{qr zDMn^!05fnQ)*Lc~0kI^!W5#K24NT(>^^ew}iPGbpw7`_K%Zh90&3sy|$!k)G+b*lF z0$Djp8;so?I1Mq}k#NS;{YQ#`B0xfs^&j?~V*{kb;BUO8s+5y~lslK#V&Ypi*P#lY zzzD&fqzDA*KX^8mkoEP&mY=+`$}w_Ofv8CIq;uz;QFCaRPi>vyvn59X{g!j;bvbDs zufMsybWtrhTKstL1&CpI|NHl~CA~b6O(nNqRy{=ae(I@9&-6QYU0<$!q?f3$*M6cl z>DRwdue$unQ^kt_nas+hS&2Gc+`47fjuo`d2^mQj^CYW~g#6ow%FQI?Va)1Ax!jpH zsJQh#RUNov=!^v!`3z>*x&7;e3Xs|Qfn8WWdPh;Bmx+D6Kj@>CcOO{V*nhxB6CW8y z)mNUZ2z;!6RI#*oZ#;agGvrPPzq+kfh(NfU-*HK~MpS;SQdhiZM`6HUy|H@HW#?74 z`NW>WkboxJb`&=B))KnP{kiW6ktRr$gSA--!R4Uz*zww_lvh-^mhYPLj^xef*76x& z2>H&lwJ@hDtx#5-Qr$qJ_0R9F4P|Vg@x51{0tXM4$P*GrWu@Nwr$WdQkK-&We$nY9c;hF=cJGYiv#k*x4b{j#dEb{{}B zjc1%zL1yYZF0EHS$&rwAjYoK}*bLfp9E?5?r;RUg30iM|e66GR>_w{>yvoo@F*I z41kY7F;~u8!)0f8T8#CsT(1)WDq=|7Y(g z?JzVNY$4eU49>Uo2B^>E*3RwLwb|)LEc2?#dJB(X*(D(`EU8x-yVij>0m&s@(Wp#X z2%JIAkW5W&RDjqpC_!!gn@?#VMZk?ew&cV-USq7C)IB)Bhdnd_!5L-1wu;!qq)`Ba zF$G6im3ytB7FBySc5ub)`*zNp8dbm$2V73=Q4qi&#aS7Uzy+9L7#M{C2#^6)shvs& z<8mO+?(a?vm~u^Z4Hy#4VXnBK3R9MGIV~&ze27m(KyR8A46)1MMED;PtYXQDs#alH z5fFQ7jPH=F0+DP~$;j}7z}o87UlLebz*>Vh7?zK2uXV_qf~WQC<{j{wBzOR?_>fzG zIQ5!F`62dIg@co^0j#KKb?t$aPh>x&syuPcmIj=NUOKeCfA)Jkqyft1yw2uEF(y;M zK*SFD3Yqn8-#1%g3uUp-9Z??YAZu>d?B;f3wgkrP2ZSYL$NFWcNXT3;Rd4Ez^qKR) zj5!v-7442%1L_HD9=E|qK2yy^Lmx1SH+wcqPAz!lmDyU@;2IErBM{8ACu{2WSzHQE zv{%)9QHdpy1_(6wjRZ7ej)2B!$BQMFhs8M(FxHaC^_8^-BbYHJd2I3um>7`qJv-`E zrXi%-uBW_e0hup$iXuxocDz>igf|ip%TNB%&sL5!*yUG`_4igBkTAp9VDyT(aPAqE z{NvK-nc1V^EC_o!eqky7*qyUnI&avi`l+kyPvU$h>fUFoMJ`(3A(~q*2-l21xwlNb zF1aJGld8$kh8kvyNhJ8nt?%BTd&dVytfMG)t{s0zu_GE#Bkt8E?PKccI`6nqE z_dnXdT9}*8%cBgdOPs=x+uGOmx&nngQ zN=nIuzx@2lP6@2vQs@mYxY&t3G8uF2hSH+9pf~G%ywjolt|AB-ztrCUTy4eyglh|2 zn(uGCrP5<^^s(~jtlIIx$WLvpmv`QEc`Y9hQY_b(YCu%hhVmN9MiY0WACBrHVhy{&CX#wW}_u4~LOJK53iR*G7^*d8*cr(93CSC9X67s7QpBC2wRtw5#HS_ngzq(<3kU zDoGD(%ocX$DS(8oHUY`ipDtoz1)O9wX&u6!KTu=}$Y|jaPoI3T`Zk%Y3Z4zLOF{&M zWTII6&3;AHt)y8Axq0&}M~Mn@YM)E5UNM*uWYw4EHysSJeCa9n1M1Ms;?rUk4C(b6 zRIhMVC+S|qfHK?N(Vg@07mJZVwb58){fV)wFRTF}Tay5pSn|4e!W&9J0!!4sZA*<* zx^x9+elwnRdUag{Y!Cr`4>DgVLXg!fpo!J0%5oCe&|8qesi#ysiY?>0f8_;rCyFQS z=yAm!@YahvJjYPT;HoU zr*`}kd@5*Hn}~Jm6?>LZ{NKn?XcO9trD2?8TfhTV*kM{215Gy#ulX;^AUsaFJ#}5` zLO}j${p4kQ{@{TU-ZT1LSCpr?!ak}T&-9n$a|sj(TOWuK~81@;`CO*mgGtYXRXs^yNHB%I9Z6^P{AhHJ0}Cj)B>SZnY@ zhu1OW4LEt;lX-@n3=lHpOl7Y4xSC?Xpjp5NrpP>fSAo@QS5&l$cJo)WjpeX>S963o zXIe9bAf2b5Ey#I&f52&W=oL#*}5@=TS_pvgHAMb=&vNYz8kF1&~6{=6T~X z1vVrlj{R{h^K6hk1!`eZa3~L5S#rb-m|cA!XOwx&GXKb+@ycz03i zHXrWQ6SZW{^3vXolGPKJfMHlbf(djR^Z2LsR(5yBX_XjDO%XfDNgTMTkJYE^I`2}& zfcVagOU_;T){gok^X-?Foe6&2ui!sj8#m;fh?7V|YT4&rDi)&P$6C^^m+B6cSF?@r zYI&6I$Kd+tPcNcDC5-Sgb`rAoN4{qI50BK^E*n@fTZQSRgkO7KVgBx|v$c#0wN`t+ z)=l6Y#8Pp|c}o}fJH_73X&9%ZZ(sl6D{kA5c^x(0gC+$$L&c_TY?(Q8=Fn{Z& z+Qkz{NY!dnje?i~H*K!1H*#s_iN@c#xth3Kn14wfNG!FB?pIHN-j4>*6R2fryrb7` zzs}iXbz`byRazu2gK@7p|LhGlK0NSTWvx%`nZ0laj~JQgeQjHH{`5UTh$SOF^rmpv z_PWyzuq;HG(A64B=fI)oCSYWYA^)LWwGPE9m$&s^9?>W}dq~X3!rUy6zgUF;lh~jO z-P8N^3VL4~JQZ$pLa2iwUxPYVfO;4aF7dhRnHnoda1%i~u()I%Av(ad8|qSHDSYvX zrC;v1-3@*9K;?_AdP#Bd4fv*2@1; zf8FUKdZP_wBAijN?#JFItE_2$>dJ~I3CE5XJp+ojKOKb$6Kiywr6d(7(4fBtS!;8S} zmzN4;X?;kCOQIMiXfBVuTz~oa%y*a0J+s#M)J81O&y_#>Xx(Bk%m5hw&u^DBjwAwx zdY};bssP4-0h9c@d+jM^0K~E3q33G-D^-awz^f{jpbS@Vq0eP7!x*3{EF$d4v@oBU z2RVm5WK;a=FjWc`E=hBwyRIm0fgYSlsfW&K^W`U83z1naX8{o8cNM3wI@jx4y;=a{ zL++y!!Rlte4lvZGeb-HbM*hj$kf-nK zhVa(Q0`pgvz>qm@O=b8MSFgZw8-iJm2a)f{>nE8ctw;$ng8?CkN#lGORB14EY_hCoxU!9|Pd?4hHj;&YGH!`lQdfCjN+%&UL|tX?hY zH~{o9FPF}yfT5TFL<$HbXKdp zbAMGKK;|-=R*Iz)jA1`wG)a&Y;;FNYDDz;*F?)0T({Gl8F2Qk-0RLa!UJXq+d!$nE*;HbC%k60!T5GR0YHE}UMUo9@Dy~DT)!CK~MlK?r&f+LKKC;V@=M$*{e{I)Ii zOL?lmy8gNo8WM<7OTH>*FfZM}*(Ikab?dPlOG4`W>t860#?|HCd1-Bp`G@bRDvaOS zK3iVQ9+`l4H&XFh&%q+kRLfs_BQ~v^6*v7s83BRf|Is_jNMs_w1(Yn`aCWUGTzNq) z(|o=EVzKk3%HqU<*oSx5;Q!sea~Xf&Xazw8jvSx$e1^qB5m3H!TgglS3DHB!^4@;s zP(aMt(@(9Z3{>AWzuY$4^^=}M4j)~zApvnFL^+7|Mc~0?;$g7*|Ncp>P#0tOa8=v)SLbB#Xl{`#y%}aF5Jfa122Z|NVS#kF zKA(l!d2RKd9=P)C2F^Jc3CY8O^eF_i1zm%2;DgDfbJ{VNCAMco zF2g&>rZ6stI#0~APOn=ywmIjMrm+*vh|<);0Wew49+$?Yw7Bwema!cvj-+;hCNMl* zfEYQ21+s`u5LSfXPb7Z|WAzFw=US0{+nyQ+0xJ@{s?yg4D|+EZ_)~`0w|eC!ePg4s z27i8GZ2@Zy3VxJFgp*mlS{RW{H&+~vmk-=0^FVriu)5@F)hd*$8!Qo>){gQ5a(cM{ zk+!<20|rf4G8;P)@0h^^OiCP?L!{v8l}SuQWmi{3IipD-^2eNDIx%vDAms?3+B4Wg znPU(*Uv}P%S8qA@%-TSaNos1tm7iq^R~X>|vLRgAnHMnO8kFB#y_!#4!1ZLpBsSltal>mnNywn-F|rYt8<@^hEvD6hU$fpb#@M*+p#5 zEe?@WpJJIuH8LR)<1*zXe9WF90U_B7>K!%InNdE24^kM)Y zljV{e$q&3av3yBw4w50|T9vx@x5=V~*q^z!(s(o^(Dr`Q+oD7EsK2k z`Xa;Y63bgQ*M6l}dToMUuKn+K*PARDX@BYHOgolxxxcC|BNYc434yQQ{!gybAMmD2 zY6@xMCPCOSf#sYZJEfa!E)Qajx1>>jE1)>=nJvIr;Uet+U-sG2rs^>LbcT5%?b-sU}m9=}9{oFU`B0 zM;)B9_FxIj&XnO7BoOxGuLQ%E>B3YO7rjeAM)@z+GE&&M$ZojOAe0uBm zUiK;5dNt(N-dAF4QQ(wXt5RksUI#NohVjV2o)-MKhD-gl}jwh1X(_Xc}+N~yl zH}n_Hga}eC`h4YDrxG7kWn2GDJ3y~XVBB?}zF6dvcc9GcO%SpC(sO4YkN@gZ)qD5$ z7wbRwSof>`EyeU+9yC1u!XFllY!JEXsT=<63W) z?Gl7pp>K+UQ_AdYlKP$d>icdkKX7dsPXb;nqya3!qz@RWlv9<6edMjh8~FgSGLO*E z^*?^G7{@}%FWyosBZelf)vA-&3i8wC<4;_<^zEIs9pD|8)JNsmcDVOnD3F}^qdaoF zxYB#GZ1M2!+AGKX5bFcKe8 zXJ>y7tZ%D5hs$i%5>7Ii8tbn9I1~a->4dd{-#1r5-xugT3o0h_=T2mB#Hn$E&9NwzI~L{2Eqk~3bk6(C`Ym;18M6xo9F%%kfl)uL=@s6 z%_Y{K@qIRH?qPEvEDk{!j8l`Qn5+NS2O6(ViOT<+|N+=o=2ulhe zNh3@oWJ;H5M3APZWA!I62*8lz95XWO3Vjr%Hs@fR2Qru(W`|M&>|kmGkTe3Ap;{s5 z5Cos9kWG;!n|@98#_>UrBgTNR4=*ySmZCeaJEf8z7?VY_)kp{#jEU)(cbc9ei0aRD z+3XDv3lkNlt!9mq5cCd6cTETaL8cZEPPK!ylrv|gMHypUz!Ga+qLhM~zRNHx2)$DC z+<{W`?;oD!KfiTv?PrmAfBZW|AQcxMfPMebDkNQr&ASfNUZ=eI0#AZ7sMovZf7M5P zpm){H7(3yC59Ree$xfguuUKDSHj&|k9k4+%h5*?m=az&3HzNV?t8JWVB*(dYm)9Mi z{q4ONd&NiL*_Ub&>Wh!p1{_@)S46R2t+O0BrUI$m<1bd{ha_R$sg;6?M!E!mX8>Xv zanT+{W%K&VvVJ?;A3C*`r2XN;Mf^*>QR0bCBXOZj{G&9|KDD)WQpm4{WJ-};etw6# zvq#CT*k*HqV9LzP@opU&D`D$=+u5Zm5CWp$$;^(PG|>JdRTVSRf1Y^pd)+df3IRvN zKYyl<3Nchg2(KyH*LzWQo`{W1o$A&tGs<(%n5_|fxIc=I1@Q6ob+z6afYTX7Mg(r! zTt5!T+SB?+HWMqiRZIeBpI)NM<>%{7MoL>`5!^L@VHUjg;##ZX3$oJNdrR-xF>5cL zYOgqm=CSU9yyL7CQEaIwce-`pFYGl1#^6J7C3L{9p17R7LVXufI*m^5DZ2Dr4~BfC4=^A^b_l-TAHufwIDoTVao8h_!I z!Xscz4C5?Ee*b|gGyAdL^n@~?iVe(TgEJsgkjwZNYHgyvBsK3ai5Ql2Hj#g-Ki7o} zOK!k~NQ>iw)IDzO5BYC>zJ!p;hTH-KqAAO}?-7Vj;bV8!N*S6B`fBf;L;zH2pnJBL zC_|so>74#uQ9~cS3Jww&a87~iYhBN<{NS!ymOb1*=>W)y1R51YGD;l+ zXg(u=BA^zg1Jf+M*r8pFr7XYlg6fkP(W4>lPoAm|RDJrdQPqrSj85j2S8L*{)niCQRK_It} zdt8gq>;6g=mK^A#2n_+kYF_+nvFI%Q?e>Gr}OV=;pxt?;cwExf@EX zK)q#iAtPm~R#KB`s7h)BRORAZds;Tb^W0P8t3Zp!Q_yTes z5P>O7xkh;bpUbOyVRVWIgECbvrVy_&*Q%P71mgmTa?mmFdTWUX1osi!7|uS zBxe=*CM7TTUZDXeZ_J>aYR`EU38Pl}Vaf#-G%n|bSxytot^<`PX95wViAxG&R^hxb z*U6Oevfl7A=$!}6SD&hxD`Xz_qiSFRnIsWz<~p0rAFP)b_V;T$0R>#f1Ll(owV3{)k4Vo*+|6$FO{yahbBSKQbdEQ|AynGjh!2BnhF+B?6Iu<&}zCv^b+D zN6K_31Nv2k%!Y@kbP#2Xq=ER0=8I3%Yc)|FrOIwAv;S>-W=#wrvHbcMDt9WI`y~;8 z3nVPg;y??e+UQO0A=^13f$7(dkSQY|+}V+@u@H+FONF8nc!zS*iJ!i1>4{FgNC-_F z#I7Ju{`St9E_&r%2tL!_Oi?iZM!9`oZS;^z{nowpDHcLdCf}1MP)(F%GAnWD)hd|Q zqm;Ih1GE$#q@S-CdDk zp6IC}NAd=v@cwISQK`IZ8`R3%-@K__Zc=bbyU1YX6fzjca1+Kk_FO;TF~@*3jm-0! zb}BR=*%X-=0UC13vZY!m`9{BM45Jo}i!AYE^(t>v(H<7WrK=?v1WxT&mIT(NDjLOP zOYZ~$F@QAeyAM>}n~+KahCrf}<>xnVu3G7F64z{u`ks*MdzVcw80lshg|Y1JgzzW& z)jtPO?vZt;)ZZS`^vE9GU2XIq-&j96SKpWE?G=f<LvB@VBWMpTZKKq47 zz*7d0ux;5UOpHXN$`gHZ>Aar^#i6jcFLF(c~GyNflq9t(rM z2>P~inUOZb7hTRNM4B#i=x9s3eChwVwOmdowJPB zuP|2TPy5_xEnT=sU8P-W0(Tr0OR3fGcHx( z&83LNn8#nNo76mkK$i}+B!E!vp0fmW1NSvk-SQWlS@hzG086?PvChL71j-SZJAJ^= z65^<+d!!o|96UN(wecWJ?Two%AYjWpAQ2LX5N8HBPvyHcvO|guFZa4CU}|DM%K4L6 zfk7FVmO*#}W99BdFlsei z!u~p7K?U=s)`C@RSlzr6tzLoE8DA4D#PhYnLc+2JYw%iOZ2{{qJG_Cz*&7Jck?~|k zNd$kIj_iDC_V@e+cvTw~d|7yMkMfFg#^X&Pj0@5h9tI~8VivOS(Z?y}p<^{shFP#} zLD%&1#knyHPv5|la{4WKIbQ5lYbt44dD^Mpqns8@^2VjGC!G$MIxM_ls%olH;tCd! zF*TTR<}VntH^;^E_Xq|kkI9NcF2$2C;zs6}_k?VgI>_A7Ufi# zcS;^esMUc68K#aG$pMkS|G5gIS-x|eayCs2GSVEe` zW@pXGWP@#n%WZo~6jK3=)TJiY^S6OWK%W#MGS$Yf@A=!wm~!v{8bRg03Jk*~OSz(d zDpB;F>}G{`cBoCeV>Fk=7$3p6ws%zL-T~7!S<0^dDa@z$&I|wuVtKZHD9G^t!y}c4 zKYy@-0N5`l-*dPEP7xI}5>7ID22@hf*=EV<4I{(?mkwgZYT(1A;2auwIU%uVGSRQ9 zUM&(sK05B`1*TLX<~zNBoN9=sIs3HAAz4BrfQg;NN>DL#g!W_o_LF|%hEhQAjowv6 z4zb`PZ!HM|=%7?EUp!Q6F0_~Zhw29tj~=hAk~DYgOGiqPvf60G_`&Onck}wng=#ax zd}ZOCmsW;mAlmwT?PEfwQ7%2N(jM~Jlbs9Se7ep=As{m?PD1tzL{wsqEf@yy^x6%z z=%XAKPL0o3g@BgwZr)xWxUNUVUJ6v}3@OjguA;y3mbxP#vzH2}O_bE;T}2=!bRnyr zaCs$QMCotO?w={DrI~(x?0@{(ViE8}9>c&ZBef`TvGBNIr$0572&mG6829wejs+P!3C_R% zh4#CBXPZL``sALXgmgzB6Xd+#t@gJ07soX{MT9G#dTQNG=&3LOX7eEJoN|_^r_s}1 z5c9p>zw3x0hI#D88fO&5BoAeA{cGLivyZM1cF zF_QDU_xEeU-4omn(eBXOuk0Vbq++Zo6Y|imy3Cjmr622G+|!N%_{si?pUcOMTK(F! z5W+i#Nq7_LyqTVxE=IXg*WXf-v3zG|ePz!e14-1TbJ~%E)yjn2(7&6B zQaSSL7Xx0X9%~GL$EEeMo)IWno4;O)VZLetrdY4r1>=jt@sebF03gF+osU*Aa;KK-X6=SJ> z2)X+}{oH{jaML9+;;ds6?lS5?TySYz{oVVjz#dgPl-QxYN zO0vTF^KS*xu&uU#G|ZbPb6fOss%V1OHLkEly)-m zS21I4^=b`%Kv-MAT7w@&m=1Z$3*WTbccfnHI7xWbaeO(!tFkPA_N)$rc!6cPgjK{2 zyh6&(uWH5ufpS)_PQ*A?krrxKyo?Zs$7&`nnY9G(Y$5{2bi|};RAm;MOH<5zRiF;Q?Nlq27AQ_XIjG2p^Zvy;L*! ztYd!Z$(qBdGL5dS6Z1tqz|Fy)2`Ee^yU{+XIU6*LaRL=x`NQ)Wpk(QhR z(m73#s$BT~dGMw^# zo?VPNNm0(v6iH6jpI)!MphUx84u0jSszUR7_s=W@Ss;^35RO>KV3MA|2J#*M_Mu)| z>`ff}CF-d?MR{xQ&w?SxnH(}%g}^!T86c?xE)f$VLE6*_EpZ%oW*Eu&3KbSrm1gwH z5j;l-#3<9gy*FwR1(Q$!WNM=~ViOai)`t0R3wQ;11&26jT-VAG#Z3~fxv-M7l6D)%NKdjmsJOAn-ckG2>;bj%(l-EC2hO&=^49x z-dPY_?m1kwB*ZXC^vYRWxU^rLBNl-(k2aoNn-Q{W?6ylw=?xbx{mXkR+kf+>%FR+G zt*x*2r2i*6eXm87gK)hLD9{C;<)kbSI{+hL^ooRqC7LgtKg$r;Y^XN}0etrOVE=IM zs#D;_SDz~7@CS}o(wipEVoFhJ=%^l|5zq2|s0vZLAaMF}BbJel&QVOS{+Yk=$8=bO0061VF?nkD2W64rF*G5UX{~}S|+p}NOj$D{i_HD4HyDl zA{Jqy+(GoBW?_anal=K9GNnusZLVQN+1iTUrK&^~S!BB1h#kD((~s5`B-+hk(D3ep zvu1BcBCB73H9qv(UoC^W*UqX%b_Ss2I17pW`|qd$&@%-AjFF_a%67!Wgs5t%wgH#+ z(nY_%t(I?KRK;eD!~*oX<^Cg8)`At!9^^fpnTqC|)2qWX@7VErz#*W8zY5OUB8H{; z4);&2lCW|8()All7mZYG?=RC2)d*aewvj-xA^q|GHdYbz-bQAJi~;%ss1DXXGUMMr zJfnd@7RuZL8JFDYFfNZDFBSjvoAtLVW~+iEDvb*hlN7e~%do7?;$UK~-{x0Pkdf@1 zcq*XT+l%#SS)ipf1Y)Y=mj->de}XUo<4Cv^07{o6xC~dh+4nvnf}}4K!Opa0pW^yU=)VU zIWtUH2870W2%Nsfydq4^TL5@BT869@VL2}h2vYg{Grza;qNNsKMZF6$4P0)kXZO$k zgf?K1ttce0O4SsI(!?vmLNfKL5UZls&7O(+$-_w+d@`^Q#*>8C$F&B3E?{i|YYl#M zVGKPB1MbPMUV(+dYx!`td~|!&@s5z`<6*hXUJ%R7buKeVm&3v7#AsbVO@kk> zVkm~p0m`{d!NGv+*qTOlxE9A9X7G^aAD8!EiQ&z+$TzpXVPk2@^x82sHDsCjARr+$ zrn3afnksaiaDtrGVnLAk9MxfQU?&h%p|kNlhii+0s%Q_$D8dy`{*5~;u_sVD6Z9eK z_Wn_=GORa244TU5da;0H462fj)@4JKMjFWMkQ|sPjXZ>8qE1^^S+_#6k%IY)JkbuB zGaI(;t?$MD{4Hg9rJa}8MERlJ#e{%`bcJ3a`AN>CDKHy2vl%f;DiktH&Y!zcy5uHI z+7U7x2C?qPWuj*3Viyh^t(UowxqNzGebbjpj1C|X!k*lT3S!9-+x+^wP;7m^8Xd3H z$fb#_GGgr27QOcLot>48DNy>8#X(oO0hKp(;<1L#jTVwVw-P$Q% zr;Y%x|KqQw`S6=6k%q)BZG(?HoW1|K%3!m%ap(Tor;*4}8=JAXWZ0+r1L45sEK1#- zzVUP|?&S4A7!5}StB}!n!I||1Ygzq8{fgYa!xfyDj?^YZ0?XJ_>KjQLyKmsSqkpar z(Dlv!>K2%7?dh;rUpiQ8WZYNai1Tfi)n~#HXxYy`T5rg{bxVC8M^)U37Zf4WSY>=0 zoZ9uL7qPUQx<7cJRF5P`382$c}w|(zL6`4Fc zv&s_*F13!1Erx(2l+vVjQ?EWVEFSSXI|Rjl&^W`017tkPC%Rb05(F~Xr}x&5A))|e zed~Ju+EKj58!w1r7_$jX^SNsj1SGl;S4ihW8Dta{Z+|t+*-de@@5eOpx3-s4@9mf) zPHVE0aJAd_FMaK)rB_a=k3bm@PwhVo(XxGCjT?yFM7d{yE}bPjya-egMF`AY+l%Im zQ%fn;kX$f$rg7cfWAmVK23Okg+t1F%84*xPLdpzq=|aums+*&5iFOfb=+}z{eS~A-mKlSC=@Lu?4h44^7phTus3C#1wd-m=_=qZ?l-bx->$h9Z zse7|t_io#YtYa0Kk&j-%Ik8nHTny91F7w8{12}6Aw(0Z8Ea^X z&_IRn+0K+0s7;k~Y{+Hs&Wu-h{Ds-BqDTXf({r&jr9ueUpfLWX=n*s3H6Xf1)pIY+ zhQtNPD%gs=Fm6?k$f;qfM%mc`69R5dVF($Q3G6B`B~E!k2;WE5fM6V7c)UiN0YQ%P zSiPFUa%odov1+Vx21wxk>T;%#`OJx6m8#dYdIhGUPG77O>XfM;Cq=L(h=8@Tge0920`TLhXWK3vxUu^C-&m_+(%~{)!M9!lzGSg(zCE zVO%PQegQe9Uk-T)4c_dr4=iV#V$6WApKn>8LNrb-AE4^s(V7+y7>~Nl*i7lPg}L1@ zdpr(f;eyzO%W#=>1Ub@%0i>ywuE$68AwWNs^LzK#vp>QeLyL2n(HUtlE(621$aH>U z&ujt#=_rEEB~+Q^%VW)eL1rgm1m^C_05OPpcr$AULiGHBDoCpT&4VRG?Qj!sUa|uf z#!j}!G7}Z#xX7jQPhDB72_zGOK6Yketsv7&yKLKAn^2ORWah2brAV6Do5XKgzjX7( zvuS;0CY@ZVy>>%gG8Gq%M1AJbdI=?4sL-H{3YTc41nW;PqyvU8Z@Z)j2+0|ZgKA03 z=X#NdSonaHL!--!f=o|vj#DH6F(Cndh(Zt0?>k(*`ft3Y)m>PBJJpCU_nUgiu$Y=t zk%p-Bro&qrFR!04`|saZEnewae~3pKOn&X&>bH9Y$GPI)RwWHa0Hg%UjFe7GSs7nO zHyJH*s_26J++(%%2G`csf~vfmFGi4%pIU<%M;cxBOT~lF)tUnDvK z{oLC}@0fK+n0#tVfjaW}NLS1T{36+K@pCuy2iN=dtVs4U)BYpXlzR`=)`b_TrjvKp64W-k*vpLa$z z776JOkZdD11T_>~Uh)4gZ=b~quOOU#S}|@uXVxg8r1Po0wfpJqm(=RjCFj9xj6blzQF)za-NSD?j1w8TjQD!zFtaanuy`yd`T=(|x zz_JHT^_;%t0sBe*2e%g^V?O&>Z6{&dA3s)`BH1JOgvZ(nW5+Pwk>D&)kG)vi=pq~o z%d0o`gA7a7#dzTiW2mn#)Rz&0i$EBm$m0VZ2p7OkCU?vSbc4d3Qa3Nd+|IiX6l3Y? zA6ivQyS`oBwY9Un#T0vmcRmeMhIgjRp^eZd_;EHCf)E>I#HtMLE{7PIIbn2xw6!V+ z8{&d?R12d@Z%o;d5yoXK_uz|`neh4OF)R0DR~cxmGH*U1i9iry5ui61_s%CCsUG6e zZ0f0AR2_g3M99FKRMhHK_a#V!lFqC!w8!9>>WCQzlWDCsVne!X2m;FC3bAfB<(gIv za2c)zFrr>NS+N@CEkJAtOtl22<-l?w12bK{I!RbAZ3+h4>F&1WZfb>v)_GMpdCCP? zG01NqtSEdm%^Ix18whI)SZnZ?6c)zWv3?QoV4f1JI4H09B!CHWMV1r6AfC$fRGSJJ zVy8&-z|%Sp>`av$LryMJ7;vIVz?3VKr(9-_ePB#hmP>#ft5?cXE%)rGDc!i7vMVgu zGo6bBGPxWy!+`MS*^g_?DHr6A^WchkIk@R;_6Awg@ySx!utZgME8vw9jL1X|7?}V4 zp8D=}w4}-zBTDv-m&iiw9AApyh0ZOziW%mY7x#ox>g$Zgnd%!h%|12(@S5*_=(S@0xADO03MnOTzrC$uUyl{^u%XN?MYcdd|{OxgR?n zSYA3(ED8=PA_{xnsz6^~;hj&i)k+C2JFjMPXRe#6gAcgfg!k+HNga7b{Ed(6PF?!9 z56)~RMAe@>RRzEdT8IKH1ib$FTgn*th;mkzTBOyKLHxRV`_c#J@97X29|O3O!IHqR z^=H)gvjrCM=Jh29Fa32S zNRkg8o4t!cjv!zR-sNxHSv^Dn@msglctyU;t=+YTzr?q|s<3IiAZ1pMu0eE$0=Y0N2fxigWbt~X~ z|24JN^9#3>sU*0C@oI<|`SxCZC2DLwa_JV{%bs4g15V_)+|cWQQ2~<{bl~F1uYaNB zXHAN5;PM~*SS9?3|M1~@jgf`Kxh(DTvXqj{vrj9r+`qIxW>1mqI`$xo%gXvco52PB z)?Su-3@Noq@Rd5W>__^I#Sgt{>7RVF+($;j8GW-;lsL%$^(*q75kuytR=}8$iitB` zs^hX+EK>)KJTJd8lOrGzuAt|O+pqNZztR(M{qFrsfB)?*r0?;2cT{k)&_g?!SDs(r zw|eaPTJ^yGe7_c#9VO&2fRNiRUHazsQV|SM^{uG(g4u+??~`d?RO>9Hz%#FQweNZ5IHN}TCT1X(!8RG}_@Cm@D54bfx+CRIgpTiJ-`=v!- z^w9qihyY?YQDmmuYo`02D|IZ{*jui)@2@usSwbKq9mp=~-@C+u47g`?W{w*xj65}# zX3?r!C-)03LIf~w!efWgj=7xyn(Ej+PCIOe(aU{ zxZdvm3l02%qt$zvhl}3m($bEXrKM=f`ARdcqExfXhJ%khx@}M;oML`@VX2HHHn!R7Iw9tPRbmHEfh) z`5S9<1WqRbt3$|gV4O}b08GBWJ7gAe{x`P)!-Xl*gaD*!+D5^sDWIH*Y0mkK3y^H6 z3S|I+%h(y4b-Zi@fRFa%B$+6O#*tcL*_NJ}S^fb2vMQlKKoNqpmjao16{ID*`!~^0 zvY|rSt2RmP5EGL2ijk7K%g?WzVg2c~|4aUi2Bj)-koCn=l39CyuUBeu=?>6KoN95svb6Ls?wMKT66JfYuGK5VcRe#RJ8o7J^b(j@vFhpz%79|&enNgW zu(+f>WVGD9qiE2x8++1P^x4Fh=Ywba?l9Zd1ur*5rGx_EJyYNhxGf-Uh%BRbOdNQ`}DGwwC_G&o5#S8~z^pZvtU7-4Q=lOz$F)qve-H!~A z@WY3a@DE%!OXRNWwFAb)7np<{*A?eiV_mbM)_KwA^)X!$flvbmeC*Etoc~kBUd_~N zE(FFx9{G==bd2|2U1Af2SjgI#M6yBSpM0|xRhddcb&R<^faHXTM)@;FHmIdYnJ6~@ z{LOX#?4xDmXCJH49!tKJ9(6~Kmpf?EcB~>W-Nxm8J7*GgGq+aXZBX_ zioh6u+4=QxAINuIUhI%@CqFzFAcGcWG2hbdhM5DtSNv_&Z>$BJ)9So}`S*q>?$S=ko>;C#%Y8`D$ zD}C-j_p5`o7Fl-o($%Hs^#;toCG{mN3P3zLj?CqcT~U;@AWPFAa~P$sH(YcXFyYM8 zW=qvDw_IEWdV%iEaoVXMQ*G{r5V$OS-*;M;g=d^L+a_rwaXD+&j$z$Ft`c46b*=Z7(qp*k#XnRUXjm2Re&Yw{Pn@XwyK(G{?sOi7M@nWosYmd zA~xp`J3Gi`9(7`ys#;Q<*AkvWs*s^)PYS_g;)Er;fwI;K;4_R6LIttA57c|0hE2pU zfvk@(^~lS$7B;lRhEV2%0}+)!nSl5RVkBLW8OqR%;0zFoTn?Ima_mgdf>|H{w8W%Q z4t`4cCr{Px-PHko%+47kj0=#eL8Fjur>jGV2Ml)S5yqLoG|;@6f?R+Ymf8>umKEVd za(>j{4PU(ie=%0C%!ki-EuX$wgOh=^1*{(kj9g|vYw)^Z4Bvq_WQ@)O{tbc1@$eWk z12BZ=x%C6;TK;5u-7=SR!FV>OFlxtKA+X?G01{a!PecGY)i`w|-$q%OJq&^=m$GBZ z3l=WHz(^!a?!yIQq{&&PM^nHOvqKyS&gg@=|GD}}`#@%#AqXIovDY7g_tj)t)xeMP zn3H|!iTb-b$Q&`EGJ!V5j2T0(LMH1WV0@;zK$Qc;qtH8)<4)Lv42?+-jHlz~-mZmA zN>g) zwtCo8D<>)5{u_0arYx@rfdjyklk6zGXrczP9Ph;!WN?v_zgLq6DODqc5l@+k0jEp^HSbsW)_FJ}8ZdRY;X*|7p>HRw^ z_tu4|tE`~cXaEaIAW+pEd4aLtRAQbp^Pb#ODVcmGrmB?yb6@7Em0hCzjXO&qBsm14 zOgp-)H=E9^r2(la;rTi~X;L5)L3#U!-c+s~?tNb56OwMgytrH@b>EQBE0Z51RY{XR zaBb}oLrLj(I~z|Zp55QX-5cN0ukxj7h$Vr||LQwMh9+;B$xFePo-8v6LG$1Icoq2d z$nK@fJ6B;IMen+z@P?F^`u7k4PsQZH*TzLEL^y7ebpk47i5+Z4Ox+7@JD*1 zTw3b3`PbYMXR6W2>y93;6#A0>T}v}^Tp$si-dREeMXL_$Tc-4q=3ckifzUV)Kzy54TDW}j#o9OYlULOMYn-9+tkX*o%Io@5>-3TaqfywL zvf8h0E6I8wOWY|&Jk6oj)9QU!*OttUXIE$5vA^yVotD>kJyS!(p<_$md3Nb<-CUCY z|Lpx~v|iVFA9!A3CO`rp0RkWi&LAmqkVI0HWZ9BzNueEQsCbZ6PO7VG=tRnw?yO#` zvr1q3t5WIiRcrNHy-Ko^RLY%nEX%QDCypb@8cfY1MN%BZ2_gXy5I{`AJP-YQ_Wyl1 zkKTK60dUad`>cb5eV+Zyd!K#wv+q9Vg}i2YnX1eemOs8@>Tlg%KFWmkOadX}dl9q& zjSE`pmu@I8j2PmkfQ&uWA=@h6FW*$J^k(%+@VWej@7M0ygt7;I=YinWDQMu!i&7_SO+vo@OwKvn5O#%!8mZM>e+oAeN`*VUz{VMOR zqY-dIZfI+-LKjh=exfn9x0M&puZ_vX2AF{d3j(0P(0$Ls#pmukmxf1SK}7uC*&9nUhrHFpP0*X0}_ zAHb+`rab{kz?BiC`vF|y&liL6g7%Z6F(w5Ps0gMUY(9n&^l<}H6}!J6y)gz?j`NsB;jacU#z&U0EHt^cH(*$|}L~kIBS#`=0%HYqo?&OZ}@B(c1c$y9< zyZm7}LSoM>t5?XXxyMb=&l50|rrceCWMQ&~(8Qggkl;tq*oULbE{Wx1k5d9y(XAjy}Cg7agI~d6-jQF zPHU+=hb{y{cHYO&tAmRYGGiDoDz?1#OdYbMFd;-aN6LXtqmT!4W&_Tdq6O6E={{ZN zxghkB2D+BcqL9n0+ozQKB}RHPBk2>wK<5qQGAudF?SQnzBvMW;XCR~lQHXULl%PwS z5F5yk>oFtpLz7EK{8AuaZ6rHJ$i@iNstQ4-zlToL#5#;%SO91g(4RS5S$B}p-YNUU zF4<$Y_Vmd;6@Y(`RX`fTNUNj3l^b)?&pu9DW0I$B~_qPKo|sZcK4+R{nUw6xF}S-k+*S|s1~P)KCrOGz8&hwE3ANk!9k zk(4{_V>b_Os*K`_B~^}g!vFnKwYLam7e#wSC4+_Jl*EHy(RlDgW#b$qL_)Z$XbB%f2`5*7AvUE`yCZ>vB&7ay&*8v1?Z7E;b+~iP{ z2smH4q~eB#B9{)&0qGZL|Lhb0o>aCLiy(PwzlY^pINY@{sHQW#j+h>0;q26$Da>3oiqn*jel}j$Cf;5DuB{0fS57 z!*>@!VyGeh@`cK-mD5GxNqAsGtzN-UckPPNG6xBO3PNaSWVV!kduw5gBgNW>xD42WsQY$L=p;so=H)6)u#tNUEju ztJ~YG%a_zXyT@Oy0z04GJ<9F%Y9`&fdgK=jXRB%2@>7~1czRv(ZtMP%z<_K}ijly% zAAnx94{xi*kq_KltEFiRqW;gnPy}r~2h78Wo`vib1hvoaDec7O<5s}=zq_|2FdHRlJQcl)?|5|0Dp)>ndFo8n z>^L-P{cGDxl-i5-(LREY8{T!J9ALsq6X;{5Kd%QTGwU_YxWP~&kaGzyGQb31ml1dn|OBP{VYB93dkR5-5P+-C2j)DrcuO8tBj2hTkdOCVUS8 z=q&=~wGP%VC!h-nAPXie4yv~wEKHW?Jm&iA$qI2FxKuS=3w)4n(pvv&-|w4%P7_cT z=fFhO5a2~ymqU|FuzQ>uyn6@}nCS%Pf{E3uR{=v^v&7^L z1y464$?OorvU>Oi!5ioPg|A-C!5ai~3s`gTef}2vR<{U zwv(XL>-yif0UJnj?(*SNb<3&}l__tR@9dxjUab@n@T;wuHFW#iGRi-CswiD5%M;{y2-qpD4X;C$bKdqs3=dc$?@F=<`JbL0{fRX( zkx~4`^Yu!ap71wsE4Y}WsX>kVYS4{oAHx~;d*+w|u zrP(qxmLLY`m7m*FOKkazw9uhZ`^jWMxx^ZQLEy7Z% zcU@QaFEXGx0)~WDCrgzXmD|g3+X(gQ&ZR@=&o92Q@Zq+fj>g!d z03$MFvKB$UuWz8%vQeaUmMo}^VCfQ&j)QHt1>qoGJF6uv3y!~d{nVd)ua<#0B^wtX z7GB=QK((8aWkt|nnn*qNo2^_zFr zMxKw|KU#PqoJMEOar#Vg(ZY=M5Uqer2mVfr13XF{_4d_e8~VG}Og*!^-j&uy84}^e zkW!-DL2;jUjLgV6cN~HDwOdjUvS~}%29 zw^h{Eg~;cZP*k^HGGfdU9xbby1b#7+Z6R22N%e>H6Offz_#$X4MAs~@QHWsmmAwj+6Oh$dysH>ON@GrFw$#-Jf*%eac7e|_~%z#A%j4t}yQ zw}3SVf9>F0BO1IBwm;hfS!ExxoNE-$Q(V=F)Aa|Lb3u5sDQqYzg`s5S;pMp?&*{E; z1tx@;Nen+h6)>;G*yl8TPSAP#>J@;T?zO3Z2UR)x zs118~omDxbPZv^7Zg6GG0P)Ig7?5`l-_*VYT!y`cFSL(>g)vT@JU!yg#+fATAKXx1 zM$d{BAV*HSe#I;AQ`9dT?i*!FSuw{&Z!{`1RjzpL@=6*LT)i=^z{q^%giL236@2qm zt)<$^0^5jHIM_Dy!Pi27P`5YTm@No03cL=@^vQ>&{>i(GAgxdV%y)m<&kJ6@sHCxh zRhP`GDrGY9O6DOMs2pCDs;G!z&Nr?cnRN0@ZKF_20`CMWdx+4W*RrGCE^VungcFNP zwOLvC=GM`YP<7tj-=?^ZA22bv+7apl#ImV59Yn6Xv`-l&haRHRLl2Ga7&T?=4 z!Oa^p0_ktOgNZN7BIDLBGmg~ii4I0IBi$+b7=sa%ver@AgYm$zdS#NE{C)Qgb=JgJ zwwLnwHR{hA^u*|WA4cLjtv9DXw->~cHY;$aV%yf2RZ&L*Q88QMdfoEs8@H~mJ0g7m z{m0*1loYY>FP^VU7mO#9F=!}djF^A$mRj*a33JtDqsDrASJD5*o6Gp5C>Rlx@V0EE z`)PvoI+&e@>Kj{I4~*i2gSE^MhkV&*$HCF#^7;0mEA|@@gec7oVs^AR%Euqp>J^uh zbVU)3fBH<_Q;)Sb-!jOXoFf79+O6suV z6r4Z>_Gl5iZOMPr>UwuiMU;yII zCX`(g&Im_x5n2`(o z;Na-5IIkVfrD6`|;MKw00@fV-l)}WI{NsTO(HNTPPmw&lftkvA<@9~a7xKWHpqaQl zFMHPDhCo7pkb~h|B+yl)5c(l#onxm)8<#+zyMVAH4-a?*c1rN27b~?u*7f$Pt|*up z@yce6*`S_F=d4}<6VtpN)p>zSbEb~$RSKCr5gzuwQWKi|cp~7-zOl8#aG}f`OJUif zQ6?6Gt_7r=Jh?j}O(hmFaCT&3=ZMU3nJ|T=>&iKhLguVmA=!d58huPFgm`EtEdZG? znrfRCN}(f1twP8E7J&Zw_NEmxa6w{}o6!154BxjkqnTP zsaMq6Yh*gd5i-MGbJmvm)c9O2bsOnRAJPcX|7&(YrIha9u4p7IEuBjK6AGCB~98Wq2&;D>yK@(gciVKqz;}j za+_L)Yv~-BSQu8dF?IY?ZZh*_3#;d3%}724D;BGCJpCOKe?-IP;y*1e!&ytBhAkGnX_gddNX+WzYtM<>TIpQBpT#U@!amx za@(rPY9L8S3n?JOwd+VN`<^~iW|M%1Yv4n7mQpknF|w*Qw6E+h9ko6rc3KEj%OjLB zRhMZp`ME4~gS_|JTGueM&)%r)l>=zVaR~26?=M}6E%zU|W$MVOS_?=o4JE@L-d3wf zgeVgt9sP%IZ$`FZtNylaEX{WyS!m#EoQy>s@isR$C}Xyj9tTNL_!1~ zyAj~g?Ndiilxs<4Ytz%ZS3Viz=1qv$ATkQOj})fDF&HkG%_J;@qhY|8H%-0!x~V^Z zu0|g8KR8fg`71d`EY9x4m<_@OU=RzkI|3P`<1gDTIk0~D=&sQvoW;ZlaNWwH;du3; z_60W*bg+EmNh@byhs@i2*^8t=zQ%G%6mQse$5kbS4eVLcUM^Emr@gp_1?W*P?kjrF z&HNB;T89A$b{wj+W7Lb5)Xp^rnQoIiuNtky8Sw<1kTeAO3NW z-#ppB-*#nsNa?rmX*=x5y1+0(!jyUAeO_rb0nc7DEFemWawx+CLX0zkNCRa?8}k8V z@L_adbk^A}{n5X40tVM#FOH6uxq&lLOfpz?`C8ee1;jSz2yqs=yVsT%Ozrp%A&w2e zS+Dc>2%I@vV@_v5%uNtcZbs3#zI}ThWEf{~q;)Qka;#06RYY}Da|E(1Je z0$Jo5VusK$YAM<>{)SPPQ{^_!%m`*v2EFNGhtTDK8Eu$=Ax+AjcRSi2afdLxoGx=Z zW(XHz#w;+)>eXwM{L=t4eqLq{epX;^0c#F^O5t@M8DGU{D+S6EaXQQ*YWNI0FU>eX8JIsMcqqBq zJ?;C6I!l1FQm^S7k3nWykq|DEi4>g84%PAkK~}FOJPKvl)pkn)F3C^El1Xh>chC6U z5LR`TF2}=f^ZuHh0~nKFRof4aE(60B$&?)gi5)$R8L)F?hBzuRbyki&=KYyH2cr=B zormkirL6}lgM~y1iN;(!VL=9>8a~2PWy**}Au*J24(Vy>>A~kV);dc*1;r6uK&Lu< zV)R~yW+fRQer|VtKS=iV=#C1Dlr>2OQAbbJ#1RdaFSNZ3HM?#5O3J%7Bai(Ia7O8z z{R<*MP7jOn5ac!>xs9rXOMx8pJY|mtRZAw&Ev4V`3bopoK!w!GNL^-AfUrRNFI=pR zUcwj8AB9dC8GKf+&a_t%L&=_SRi@gSMi3CgRJj?MGvd!YRr#K*gAfU%1C263aQULC zeaGu(3YA%iUV&H!$a5qZd~~Oy=X)9vJEvj^q-6EGJL)bYS@tC_-97#hG_{DqTi4Z~ z$Nqo&c&$EAfw(%uw!+imQhyT-1%u4HudhD9&2Mk3{zDX?j$UU#GD`H{OMcrvdBbK% zG-umd3Dt22lC`qRvb^Q?zuG>iblr;j{PvMkWi1*kQeV0>dK!+uk%Q~89i>8W-_* zcX!JBZmhcl8JpFTjJ>uUHCVo+GVr+U#&pXtm_WzJ?w|UfK34@ovLdN_IKwb$g)VeU z1=eMDSWuz68ek1sS-nzb2H>6BkfLl4p{|FECsORV3+IpCbtR3`rHksb)_FTmpJY6U&B6|eWy1rbs;^RUStQ` zhw$m5h~=MY(I%XcfB8ZQH;gcnYF<=O+By^H}?2QU^wi z*P%6;SD`yr8(TEJ?EM=rl_+HON@H8qBCWbXYjLJ#SXPLIB)n^V^?&Z5C> zSZ=k|87`>~N%*4qbx-LhzjAZi?A(^;5VSTafoKc@birgSWg7w-`*VRh+95ZOKbj5% zGk``X1Uy9(BQIT84~y$p*88>wF5{JEqJyaF3GHti+T~B4uD5X)w&$;InLnA7z;+fB zWHC63G=YBNQo!NeM~np3Q8iX$B$g%YkElA1O|4y4O+XssoncT$O9~F4P17_ksSuES zSWxHJ833`h&O2V-3Z&t1F;Bszf(U%+#j+~3Aqzn+Q*dAerw7X)EKC(}PIH5&cTK%> zeaYckdSd{l+-wGb5C{6|6(HYpBoPhNoBQ$vW&&YA3iE+4dojtU358f;VE2?Qky1!-vWQM!)3_2&A{YRx#pTp2!U#Wa$xu* zgZ#n^m8IOWYBW0!8By0PuX3`KsO}O#CsQjp5+|5k#$}f#)?eFF_DESX*@a9M+cN2= z(Nw!EY^j2LTTa+_K*SzL|Ld#6w0nQcdCcYsiBg~p=|}|BMpuwYNFv0O{=o6tL6cwZ z3r&4-CVz*Q60|KO0A3FEuf9{$Q+WX|LX1rLyDt}wJH6Vp?T1p}jwDc5iFu!TXq23Q z=mI1Vh(IAr1}L2w2`}U2k#wsf*s;mUSeIpS4VmQc75Q;$*MhaqW*-N_D$T_P}9o~StZCQ0cpQ*U4{Cqn=9c5^dg9}JW9aP*VcelRptvf zjh6qHU#PdS?zn0cPk6*J+u>Ht`&nwSQ+?!A3HeN0jS_)7*NlE?@qt^)7)le6lF(KV zvh(HlADjBkJ4P)fP{`hNRlUwxLAat)g_Eb-dx(20fI4UG=4*Wqy&+T&mRH9lH0;U# zJ|pUxvt@i4)4JTh|6Y}!-CgMue)J5JBMA#*9Qt{FIYcR~>J2^1O$jtg6aAv@5?VE9W5!DD_ z2>csu4;p~v^}!Rh=B}BZ+*!m_@RwD>h2<}vD^VL-v~vT-$Ak-#B^8a<>(uJoH?@}v zVKgqM$We9_OY7S}a})cYpDTkuc7KU_W_OKjB;Vi0K$yfC=-3^DE5APg2+UIBW9=9H z-r0UA^Ed7o{h~!;l1`{l2DBh>Us+elE~k>vRxup#4m5akJ7BqEO(Chy-*q&fW@T5k zSJpRM9F!qr*a+W7o?3rt3q8sigoROeLMhaQz(f?ddu_RymDvC;yeJ9LZ+Erdg50#P z9wji<9IekdqjV{ZSve|bKYOZ{invp_wp8tA$rd>*gsXy4_KzrDvq!CHyKw}{rJ=y( z?W^naSe`QZP$S8@>*v}a|J!%gDi76E=y(d`&;^hY9y=Y6Y_E1v2wb{gROQSVQ35W* z^@#_k-ah`FVtrm;^#oxs!_e^h%C=YQfBAZQWjptBYiQ;H1<XXcz z^LsM=5@>zkGQv9^1XDq49#OQTeY>#Zb55K|)`%!?Yd>Ez6lKWDBuy>$4v{bcFf2^f zuvV)@LXaoV)SmQ^1)14h9;v|mq!R;_hSnDc4L2RSJ&PAqpEN2C*}L(4>A>-Dg+L)J zov{M~Fdg3^LLwn#ky($4fo-$EkU)9pejq~90Xi76h8~>@W;bA#s@WkvUKIOHvQ zH@q?~6QPtrm#H8Hy$EnozKh{N>neLE1_)f0? z6e_cA<#0J9gqiMu5L5~=PC^upw-1_RGL0)!&#(h$T+UROAf9O1;A1;R`2hG2PuHK& z5nwb;R0vBJyvo8c<9%cUUo|VV;3wmlL zOSq)g5}4#09MPuAC}SJI8x`wU6pgZ4G982?i|^dIer3(GC3uwwj{ks9rb8{>E&E3A zssNYWcm$Fb@a*o|76B@%DdKN!ErKXwKRIRU(WyWF-qZ(g9*Ip$WsA+zyGKa$C>?;a zfJs2wrb_;QroCXL?BFZ|D){Xu|D;Bgvj3y)lSyFv!AjMXiIRm1s;mzUU5q?-x^|1O z3Zozh1v#jhvG-`L8b}C_oTzmHNLn6fAGbzRZPZq82_avVOZI?nJbRGQ#by{iHm<>N zIc4Ya?vmPml8^4`7+dxik}(mYu$Sbv`Gq6$c6w<{Snh3Ki0{&#+fzR7IZ`>B4ikVk zX-GA}=(uh}C0g%Z- zJ(Jw7NIE(DbBW&B@ps=;E%LoxRZCi4Iov&7I5Ym# zyQ^)Pwc^lNDu9<%@X_Dz_Q}q#Ys-CYedbIVyzkYKGrE~1gM9EGKxs(zf zybjn+-hiPwwfS935@|AdDm6CS1vZg)hn zq{T0(hRb52GL2cTz32r7m-T#7dbETHHZ~--HE{7Gkqbm-qV9aET{r?k2-BGDbRo zr~MdZ>~SVJ4_TC?RfD#$d!TR=jhV(1J8S)2Rre_6YRYSCRW!fHM{n;rdhD&Miw41^ z?b&3`u&eEx8yVJmnsVNu#zH1!R)Lkb?yosUjSp?5l_hU#BlES(M}HoOkQBnJw;w%O zrqXNN_~Hw%Y-n1vpw@*0dY85{B4LRH5Fr5tVX2PYY7d{N?y-4a{S}R!gsV!B)ne>r z?tmHzTQnvB<-h;lD9DbERPJU3Gqj4FV1iTzVPOf4`6<27XYh|qkq4n48l$Sa1~UEY z5Z+!3+`ql7mZ%{;I5YkWD?EaEZGn6LvC+r8KxB4M4rP!NwSit0k8%WhVaAVG4WUcU zx&P%G3?1w+ef4S<+g?YAmK-x`JQut!mRyv^Ir!@da|>8=@G9YFWpIg|eKeeSE?zA3 zF+JdUW7^ao!@cTUgYS&^3C!~Lxyo}%7$TKtq5aAA&kpRf-cm*H=rq>aIl8{vOl)xo+Q)jgRfr5#$d3L-DFEBxjWCD|@ zfE!m@?OG{VQ(wZ)KC(K-*#f082EZ$Kr$Q++iC#41{EZFu z`Wv!*n8Y~WUfju=k;JKX#I)?r6c%OZNhY80IF;QxjE6Mwy=!d=Of6#&^OQg|FdRoF zR+Wynmlk9HryeS##0J28cH`9DYf6{qke0|*^DU}`kOrJ92yLf899)sKvu);bP7p#IEk)cCT2;Zb z1h2S>GaKSps;V`caCkJbf+t_D)eU*wRm-Nfw$-Z;94q-**-1a4 zG<&Hyi#S_RM82S{I3U1<06Z}Mu|<~>PQ7l!s_N(LVF0A`d)MLW66qvvMj17{Vzz zo;_{f3cbpDg|aGK*dav;J!Dlif!zi4X;uUx|MB;ZY#0e?qX)fD@0$8=-&VxNF0Q!^ z-?DaU)BYkb+GOKKsc4IyST^pdhpbCMV*|-}(WK8vxs3^wk^o6jEljk2^4oXT>zset zUQ)iPeeD1A*;?$Acx%cpv@NU5!<)r-N=c4tEL7|BF7>zn`77n4TCGzyUe5IcJx`o?-5+wr?W5b#hKyBjZg2i(Qpy;9x!J=7SOuw!E5b<# zFIY6cBKd*#2u6SssLf@{IU)_D;32a2NcDj&`$zI4wo_uKqMw6Ia|B>gtDvtc;Eh!< z{0%`EgA9EKm|i=CA<$N~-?|B=lyQLQ(28=SLfxMV>g<7t=XhU&I;WG{fB zFJQ{=&^@Bv*iBMDiV(^ z`)V48AXS-uAqHXTKw8Y`Q?!tUK9tUCWjOU-&v;fB#!lb}CV_Q~ChL1gnNP?f(?Otl zb}*?5pvM^cR7KsI%W7_FZq$*T3{dJI(_GoaI%3f{%OW%ia4Bg~-rp`&G0%J&S8A_X zHnn7aiG>k21oG9?29o(maY4e1IFw*_Tl;{IQiX^DCh=yg>sQnlk=gK1-d#vWqRE$o znDJMe>JQgs!aE?P){pKeBTdcaz&EuQXXIDe*hR~-MJ1MsWCe~J+Us4JyJw45zR5+Y zRzO;&Uef+x-kN~GgbwUJGI~d*+pi#=aUuBc?JA5M<4vn8z4`PLwJd^?0SYmG?>Hv` zB!Be&iUXQId3r?59`f0f&MD`tr16saaX0157gYx~cSjQmLP4zD=BAY;xw=#PQN`?k z(IR-AQId1;#MIX|*Bc5!5@IT^Oo&?kVkZsnzkjNNz@G9nOI>Z@!$GWed7yoI?C^=o zehFv5=wmuE0y&>V{EIdq`4G=d8EBKi5>#uR)&?b22?(zAJ(e2=zxd*)KahzLd$xB)I|mabXO{3ZD)JT{JWuhm zkdF$HBc%6VTX&PT%dJrcj~4-zf9d*aVr`-J&)aK#k8Lk$3T~)BdTP|QP%1DrTUV5b ziUA6%msT%z`Aav{4;p&L5iV~}tAVF>R)@KzeRU3CAz@YfZnq%_BmAEBeXGxItUqaB zB%!)=8%5u9L){BZ`plEnuQ-_I#{b}pQ@?d*U1}*}-?Fx(8LY4i1%e3AS+C0{u@<*q z?1H@cih9GewMGGqJFCoVn>DFcW{f!e+O1Q6_Dn?v2sPW@vA!Mzn2(;S&x8SzLr<52 z8$qn<&ok|>8p<$wxNgP^Y5C#><*!9?kuHTeLbeHT6g1%|x1yVW&>F)?8AmUB>a34_7crsD(4C$G`jl z;!8SVoEkyne9O8~0~w!8R|;2@f7+Ixck^s`g9wjWGD(?4ks zqJ>8E^bm6PYz?s0!`rjW{o73fDZ^KbH%9oB6?pW%8|&s;vYCT0}qe_`1N4 zP0<|8!E~5gz?y@fQkZ3A>_hx4kF@i`xp-$0s4863X=dk|5%N2FU`9(`oojiG@xmEH zX|CK^GKUyqKrhcCd;*V>s99zt@Wa|(z-a1x!}wQhf_LtJB|`v|Q`LnGFVTe9JnzWK zQ8`taJeljn7fs;o$X`Ljn0Z_9`QS^h0x@8D`${!BgoL#`?|WI`Nc z9FS7tU$}1SuU?#b)0+D8eg`x;QVxAE5$K#bbnw9h`qxYtgMbZB@2ZRlff;}DC}p#s zE*~Xn&Mbt#uPtyyf&@m^nheUN_$+DPFOTGCcg7f{*b&3zP^$_CBh^^6X7XO;L_@(j zd6aBKs(km_%9mnHzWW1xS)FJp;4{xq1HxLz)n<(`4OQ zLgF$VURp~?S<=`k5-Uh7v02V`W&V<{CtOl{8D!?F@Uk<>9zX!FXRiULD(!4LHL6zT zEbQa=E_=}89lXwY;CRtE3fW%Si=6ze3RVy+J+7`3)kP_0usKx)_iP{3AD&|qAph_6i^niT$kK9$4XU^7Q9Q%K8pcW`O zNZ_yER`13FcAh#@3pZL^472=1VC)yG>)V=9-FoiXb_1#f^-=9s-g&rQy_C!c&m`9J zlt(wTNGfM2CRvrC?Iv}&HBd7Ck?plKrYt^e#-i$2`;Zo~9xiN1 z!~O4%R>Za*tbLJ$lc^&`+O8u-Oac{mtf?0UDNWpXlqGToV9~?>_U&aAE(E`IXDuj* zfBDkJvHDTS@c{KFPrtGfC-_Eqej(U-s6KB;TD)__FWT9kJ*qI=d7$kc1-iHR>tp$B znSs}>O(qL_fjE|gzHDe!RD9yWYLxq0Y^#A90Z<$5+VLAN6%F>nj6cw+ak1CANd|c7 z`lTCMWZFXknI^cIv+XrE#0Z|;Suu<~`GDPZD=Kt^8xoa?#eU-z^{0;f0qn@BdZUm@ zEF=~n_`$*IS_FE&5MCRsmP|dkslF0*sJ)>alPL8lyf+_nJXY(H1kzELM{9i3{gAm7 zfpCQpCNwFig^XSg`VbSP$_?ng)-#o1(4^dXfpa8yIs~cq+V=M?nA)JAXj1&D1#4I$FD{OA`#hY))pgAe^sEC{mG1iB3!QVwxacv_1MY>8tu9?DN4XZUrOzal4; zHT7?bCVb3gFj^o5^inSxqc;$;G}BOZ%8t@97M{5>kQ>DE2oISv%G=v}1m@Wb+n-g# z_{7V#9DoIZ^U_7tVbn$lNW_A1&eXhjyfnekDGBrPCAG5Vk`1_IS$Ts)*&N;N6Wj1A z>~9~Rj{OH(dW^_)R%CDXptm{XKZ!`M3IR=LKUx-A!9IWd!l>K)%C~i z_(d86ID&=2pbOlzuUIM(Z9l0lk-MVh*f5e+wahM=ZO;@?Mcsn=HQi@zh%t%}$q5(Y z`B|6{oVG}TKvkL&5C{*+hVP8OoJ}l{>wa}h5ht*C;loQ2O6R0o5<*Hrmc~AG=jiPx zK^9EO4_yw5uDQHgN)=w0PwlL`9vR^TN{Oj(6hYf0QXy*10od@q8%MPXV{G6fGB$j5 zbETc40h3OsAS}*0C)>!Zm7QZXFp?INswd=C!M3Fcu`~JK-&>oBRxPddsZe&q@_lX~ zdXUV%byXR#uf>1+!DBUM)sX#yrGsGubCOtYh%LE;O zz>l_FVDKo@s~ueC`-Uqk72k7Y>i>98?FV5hInTFsXDo!!#ae?8?bU4>0Y_dwRCzMt zCANK|{efEw5703CJKM|V)ou9(#K<#eram}c2qT#>4A#W?%2i*YB8Tyd#;@}vdbM!b zgeZ@K-@X$sv0t{R7_sygLBocm&4jo#z#6Mlg%lE8Yy2O#;Poq}-gnceQS>ghi9ot5 zvB3lM$`7>PzI5+gw{j%G&BX;}y#-v_ep&TTzE@=8OaiCe9ek|M#9B6U1gOVdvog&X#!P7Ghw_h3qa%BpIBwN=#@*WArNe8 ze|XUUpWjm>7>yiskgv0?Y=iLLBW0Cn<6;Rp?s2yRz3%kSKHt_HTO2%qDx}x8*TdWT zat#`7VVWG;vt?CTEjI-LG5qc$<4tyLl{w8?*0zo(KKl7LUK)Af%#6TSHrEdwrtJhj zi-tTR;r{jYO)iwmIu)fv8SaSs;tS<_8u2&osGe|BTgO+>L+HigS-@+$Fp>}HxPJV_ zOay8PS3Z5FwlFiy;85G~+TeN9%34P|ajJcouLTW0W^2$ygD9;ayvl(6u@(dqGNx)= z4aZ=O!YS~rt>q30=yMAL(a8M{VkZeyhteg(oVl4rI%OJ}O#(^~?as$e)fs_?WCVT|bF&)l@59kIn3Iwyr44ju>7SQo71YVtrS8Ww|El$nBUpJUr zz?y@f8948_Hame@Px={YW<=&o0q0d37!JFc4a^Rw&yH3=I$$S6_H`~8TB4l!M_^*m zH$56|P6or;=}(A`QRuUr>0}^tvJl8)CuknuQJ*+LFnso-$t(wEcpegSP82b@?uYIKL4gkRn zX=0%>jN}2prz*>x%Dvqafv^Man4RODKX3Gw(}dYl&}D7)3Pj_z%NxG^P1{5YnDlSl zTZy{ays{(mSyhF$OaQ4Ci-bpVLQd%#r>sd=K#9$snzqo8u={0 zpO2s`^IGP4e7vuUNqM)0@k9mCOGI7Xf6de{-~7rKzDQWQur@Eew7)I=GB5!FBz>~fl5%@4@as!c%ss_B!1Ft3H-fRS8Dpf_NjQomqsDs#F-i@d8FgX zoat3g((Z7%Y*B6b(Jpkk)RP|?Zy=IDE?-iw6Ke7Q-{a*Pg6g;J{Y_2+QE0^Y#v@*nbLEnDgO8okz1^FKPZm9sbX22Rb^-!!!slCu z92C_WX`7w0-}GTaLAsDL`4|!Cb$vDbZ0m121_HI|rM&*eOI2I_s@*Knh~=_H#m)=2 zq$;|wBO`%d70*UV4x~Zj_SLmw$UI@8L(2kHFYYZ1s|BZODK;N&CyjQsgv2CehZaek zl|dGKVe&UKNR%|X1IYnjXdlIk+3^TXqOBD`gIb3~kTX?)Iyw<7Ha2ixj-vvW2gYs2U~F(c^$!DvD`V990X zc42q!wb7VMlgLEC872V%%Gk|ZT@I0*GBSa}unJ@@7=Yxm`6vh@NYavXSOO6RqcBwJ zi2KetV6ao9-9wz2?ufFzOc81QmC00PrtVzZRhpcTrEF>|QGsEw1LL*JM=YlAJqL(K z`bK8?YNIsMZWv^cRLE(QZK`4+`mb!RR}2tHKc3xFWW>re$dsbwsJwAJr)LbtVPZp? zLm3TrlIsnfmW-9^mcGUewf>cP<2P9ZO=MyxWF$l^h=p4Z6hV&o&J2Y|+HbA{mqD6L zlYk|*NfM$6v2r2|;|7ETC^5=5Imo#V!@G(_}rtQ6=NbyDf1I7Xz|l z5;MO3Qe~Bh@ge|u^W`&76$^HhT4Y`OH8G)}S^B9D<^L8u2z(+W;ceED|4P25sk@;wQ1rq2aUwUzLcPn4n-r6I| zrF8Ch-cqXd{GErZK;ZQ&D>2R|mZQSCBGYUJ$Y{J~{IiU?gA!7P0mPqrs8(vS)^KxM zYj}8D#Yt4jL6$v703NcsecPyol6)9SPn{Vlz4r1^kR|_bSyu!u6J$Xn4NW$hDP*Zg z89^z^-E?f~8(Zq-+y^&L{nj0$&H$K-xFRziH<*n@L6ZFB&e4ik@L4j`3G)`Jj)B*4 zQ(K}{kTz=qG<9RPwGwgWrdl~CUq~SBEo_ZI#?z2GK)6O= zl@8`E({LZTt77%+?t1A`T`f`^tJe$#=YMS-U{%wI2G@t$uMm9V!IJNYwdq>8R82-s_YYJD}{ zJ-Dg72ylv6W~e2N%iq~vk|V9~Kdc{2SUkTlU*9tIKipfb<1j$w-8$f$X8P2_#X^V! zjWdXF?OVt;d;ylcqVWgSfnl~DtXjRAQtT!0p?aJ0Y};!V3@(l3oWEoc=*A(OBN^PV zBXc~xyEau_)m|DU77I&|IfJ@MH;lGH1+r;N=xE2a?ZAlg6}@ehFmgbU%Hg7d8NAqa zc(go`zv`*FY*B5jHX7Gm&|-p7fn@B?0Q-s4wHg%yfF|)llONKBAwL9}rZQCz2_~2G zS|OMEHXxr381sFG)atJKb_KAtCKUprpHPuM=EX-4Q=O58H=qlVklG=*oWKkLJ;`6* zRQ7b}^r_9ILRPN;OjopU@SBez4n6>{V@%A1Lp&4`WXd}Z*56|;!0$u(at?S3&3plR| zld~a>@{BC!wYD=3$vRga$&O(9>Qyd7GXWu%>Gj!0hkj{-GGSP9k|3QY=0iPW$-JaP zC&Lnariq~A70mTBr2`>Q&SfCwE^Sx^7A>eLg<5mXFSZ}LLeS*|mM^Kf(ZTjZ?|@!# z;CRiCGcC?}=ogG~tOb&axB%&hNBJ7-&@hL~E(&`J!6yMAYTxnEQwI_1lV_5NGxZ|s$}wVfk0lI-AAa^sYjFPIARiU0ur^hrcPRIXeiajU#y*(eA} z!+^GsIg>!tEvt$bms%3&ieb{fWY=gCG*!G483hO8aIs$jrP>N?%Ta}Bw2S9hEgv(3 zRX|!A0xG_;xmLCGE{>DNYtqX1d=&g_WqL{!v>tO8yBdT(S@_VOi?S_m z(M5~o^Sw6~F&am$8MS8Ra^xce^De2cIG3H*O#Q1zYE1-j`I5ah$LbEv4O@)q+pWRg zw0g8VN%YuEnyUFyvKD_HCGC&2?9(ZYmyBLx;o1YY)JNi;*;Stj|7Q;tF&24XmNw{O zQY>*MQ1|#>zFx0H2?(gR(Er_5G~2D<}Pm z4H(@}M^DuYfY@V(yj{X3fxqYK!aTFP&eyLfUw9F9Jknii$+QISZgHaqA&a>(U{y z*Ht3(#eHSuP0fCau)~W+HcVBa-LSO4Fd!WRjAV{ki2Blt^)bYX@RhY#oUkDHV0Mp= zA0INSorlYK-RyH4%T(aR04yNtv}Rm@|IAZ`;WaKa8&(w=c4hs4#rW&j2nce=X$6++ z7zh|f6d^}W6jG=4aHQ_|>00=^w+(9^npx9PF0rj61z>JnUGae=P27Z$G#(L>#`Vy5 z$y8Nh0wX4W@m$@r3XmFF-1wy`ZB)Lru~+Z`Q5B+Aj$bfpDScaeqmuy%ASC#`ho^q= zhAL2Pgk(>M3Bl{A0RFIk0u`@b^y&5lL7@NWzzQJ56tNiwC6hoD#X(4>;)QgpdORT( zlUIHNq&<%ivmu4sTI+O(9Oj|Cbydx963jT#^*YCeqHMQ(2s*foVSoXi6w4MBWdi6C zkR_YAK$_9At58Teh2f%A?#k45NL6f0p`QaOP?107hBCyUlTC+-Dwi`7L)NC+vB0SA z#5o6gSs|-eU}E*EPuK!oPUH(_`aodl=5!cV4Og!&7HBaJWMlr14Q6-kwZnzT?@xg@ z6y{d1ekNdU0c#FkD|mzt2b9+`xLoY`_(l(8uU-Cycr-!daoFb>feSQaf^0(UuETY# zoJP~1yPbGKPQUDi4JD^AWKrAa4S~ZaM|pKXRoAOLqy)Jf8s>>bU@n8#*%*Y!aOLS!bNEHd6|i)$I#SflNcuM2;z!bIsS2JGHqBy)K8syG$v1 zcPA3e8n3zh6{`@)?mRMGb}k@Qh-v_j-t0M64ee2ut~(<(v!n=8>XNgBp;L0d2ThdB z(Q!GL-9>eRT!KEiCm9zvgLJTmQUz6rk!dVQ^L3WUiy-qw7*#ox6AF%yh8?|t+;{EN zqdypx({Lv449m=3A@2bxkl=jyL={TcEtSBBi;>IQ2bD<11yJ%@Y81<<1Ln<1nSvq5 zbewI%MVfRayo3{oCT|0sJX4ulAzK9AvA$RmH+e-Qt4+YktS=3lyyKEzb*!qd z7tx~IuyEvTTdFAU+TX)jC?69mWC+MKjaHRtpa;tLHO%*R)xIabEAteYJm+k?1rTV9 z`7NU+jlqBfx@a$TO&VQzlfgPazo+m(V3S2xpjs*(jZ7i|mOu$Pd~7@0_Nv5MXMp|a zsj0WEt6q^@?z=n661DGKU(uGIBbJI&0Z*^X$R~4s+Yr)#LKmILm(usr*@7)UEyl0 zHe8SveU8^{zYjcP7^YSkCOt>QurFFr7z7w&9z_7=)S0Sci833ux7GR6XA6@S+`Ob1>hn|0b8z{C>+Im8W8B8Wj zTCHHb57CoxLtCswrWRSe07copRU~daa_B|h+FDEVs+dhUO644+#ZLW)kA@BgXbTv4 z=AlOxvoY!wWE!S^@%q~L1f!XboUHyFdmMp<^J-hxy34Bd)f$ejTT!NBcZMWWccthL zo~WU?7U1^QP;l?nH3;NQM!qD-qVXqB*GHpR#b&%vn)M&QqpZENeID`9iTWAD+_~?% zzDC_>QMGx0v%ftujCC*VuibErui7?H!0Qsuc@I&+tx9R!CkHP);UK-&$e?SXzjk>I zS%hG@vi(M9Ep+BrdK*0nB0yU8u{Ne53&60!8K6lS4RzHDZCLxo8%iB^qx#=_b0cU! zm6(wnGM_4QvvWb;2v@a8`!U~qVJ93oM_1|1o3q!}F$!-Za75Z;J4SEzEnZN& zw61S|=0S!%LYx6JJRFsSyy=Qs{JN=0bIH7rbx%QvX8|%#oc)MoB${%7uzaP?aGgXBuKj4bn&Xb(XQ=<^tq`3q`iFXHj*XJhBijqFq1Rnr$UfVK$wxi5ADa3X-zj#Ja+G)p%xoULgUkdcl2GBP$eup}n3*>7$w_S(MJRwW#!`!Lw!rUsIO!Qe78{=m&uW&XFn?FRug-grfY z2n}NAE(d!GX=RM1cf6xYTB6`GG^+6ao9|Bj#+yrQXhI+bl?~=2J8xN6`H$SSZiI0@ z4B}cqL*R{@WCBU11X9po9K*Yb3i#|9j3~Up$65`+RGsG&4~`rJkjO$n5&D-8*G_?m zfEV*`Z!0cV$q@E7+caa+azRrFC4tWP2m~a9ILNR-LXiUYYR#6K5*Q4D9q(Wji8%v} zC08^8R6N^n5&S>CT8!wkElWYxDj}p%n)Ube0h#zR5 z&xij=`)m{8Y4opc8ULndv*f0gRnU>HUs1~?X*CD?E3jjmFJ0JZ+Rxs(i6Emw(kw zHZJ{D+y03i@poEl4wtjBtNp+&wbJz9=DIa{t>c~R>*Yr@*`UM#4m$tAR ziP({owZ2R#E&>@(&fROvDtA@}3P?f(wp6z@1u=0JlSD?DeCiZpTR~fI(x4&Gso<|$ zSug2mIE*BJbyMZx@4miPNpvR>&XQSxOsWgn{$>CaVuLDp@H}#-@B)p_BPObDT{YS_^zE&6 z<4||+)%BtG^rEkBt~(3680O1p@D-=lMk01~TLaqYIo0kXO@H`giNaF7v3*78y*JjE zEAMGB(I|pbG0e{+w7z$yeHZ`4=~|-QdY~Q_C)zf;w_V+InJ*ozvT(V-5^#9q{}pYN zi;%>Pims)QSJvj!4edEnv1%)iqUWZ&_VPlqgq_i%tiUR_Mt)JmG+~JB{ep1fP z!5a;83s`gTMuE9b=AGw-^A2hkG62mY_G$&QTwyXwz|k5hgaTSvD%f)UYd?rbi}#qyi*ckiD!}Tvd#G zY)2&z#Hum@m0-ZHTs%q%qMbCe)d)OzyprLVp(?dKo3SyJ>VNoc9#~)%R;Y?@rwA-$)^VC6bP=9Dra;IS+oy|_6Q!66FAk(Vw!Ie77)++yIV}S2 zZ13QZoz}nSIYn0He0)>SH@+&NZdmowGM@$k-1Vz@iw>T5^Z@xSFQv1?HwYsFX z>}g9Xc&qcDttA*3#I%33y{pKoFTGgb20C)G-dAO_%+}Df^7S{-&HT} zftL@Js1v70s~!j###CiVzK-G9O&Udz27&Z>;0zP1G!PXe8|cIqhL9Uq)`D^Z=xhXV zgU@fD?Z@?d57*+_S2kBy1iBOU$9If^xwri;!IL|u?paqMVnz&8<$%$qvB4!FZ@an} zAKhN#Gh#NRU0A3fGJB6y^WD0t_8IA8OBdE#qDU!4Ak*f)KKXxpPdQkUTd!7IZP#uD z0ny*?UOQR_`rYsEk485y^(U67KL25k{lRpFUHL zFdz+q_u3Wp6i8zMGUHCaM6CSM{@PHcfYL!72>^yM6^+KlUowH)Etl7=tY!N5+*p^< zTkNePVG*w1zH`)JVB(A3xM^SQf8+uraKGpzix<@26(s*ccMC=#YaxcaB8wAyeUJA~=NXNPW&Za@6H z>2x?39t3|bf^%_Q((aAaPN$iWX7Cpo6Q0Xjdkx`S@+V*x`!57E%|wHO*CKEZ=3oNm z7O>{vje?6^d3lZSMvsgc&7JeSyr{u_f^s_VjF+Yq`Bue+K$kgP)P=NzhYiZ@ZHIIB zwBx74(1l^4<1>pIk^$xCcGolxq^i%}KY4!xx34Nn<&nm=dL?aknV`{? zoLmaT=Xf2lYB{3ph*-5z!4j9s&LG))s@Pv*TULXe{J-2d+M(uv5v~I#s&#TH(_Yog z;1>*myz6bG3(0?ev%U30GECybK{S;fC+sa5Sa*4KV3Y_%RRHr*UU9}%8FE{!p~V?j z$!X2~^sd@cBmy67s<9x}a}rxhC4v9)kt(MhnTql+o-YZX-&hMhS$O@0>*}4Og0`Xo zLlhV2BCwFHrS4i&RlwQhJJ*j^SA+cSj?t4le#MfSowsVJGS!>*JW=}VUI>Jf79yg!cQ=3ilac&yZ9K0)yZS4kzgfw@oeQf(H znPkp@v}(ai>TXiNB?!NMTP*~zYVWbyca|3UjXPR{wwDcEa!?g|!d(`ALtTF2!7AK; zZ3SWP(R#a1$^uBj*_Ls!O2X+fT0S}6#F}O`wkVfJM{B^`(0;^CNO?hyh~2un)-mJM+GV5d zu3XE;1fvQI1PUcNxxv>jugz{8#4`VqGB|wzjX2SJy|wY!c~{2|}g zHfAP)cGhjnHBdLEvEo?m=F6XFe$cj} z?;Ag=x+U00#BF&|8n5cN?yNtG(0E<+0OcfJ@QKOev zBd{EAk45*$!Q=I;VVp^b_PuS0 z4spDTJptobpI6heY5W^3#vBkzmz_S~QqPPd%Dpy)U>47>`(?|%x>+X3I;hI(6*zIG zS`+2_TNmO*Jetnft%_f9b6Ax_l;UzAHh`S_) zc42M-YYryiHI4#@PQ0dX6aGZtye=ci^PrQRmwtj^h?$)igxG`cyp#bG$37>R5zggc z$2%)u-=6m!)2ABK*;Vxi`4Fa)1=D8$k8B?m;EBy7vdfRge2$?b6+p_pFszD}KEnv8 zBC2zNkOiZh!zIeT#Q?yFXUI%L*IiasJr3)}e{NMq6Z@&oWj|L!GIlMz5ty^7rwJwD zndwR};2kd4URINy7}m`IS-skNpb|xsd&0lExq8Ue?WI3J&f_l^2GB^N zD>=t%slt3cWK;xs)zVR_hKwA3LCVqt>_G+~v-N{I$QXlOxwKzWuY|;-)JBdYprfl1Rnnoj2#nijn z2eZTIOi^b62=Y278jKp08GrG7{aL>3Z)Ti2JxW<)4_Gv>zEi{6h~=R3nq}<+&&^q4 zFIznIp*yQvCKIkEXwd+;Drha$Ph`$c)xH%Tk%fjj5)>H22IW6_x*6G4uZSYy)R~d{ zA8X&eR#3}+g_KdAI6d{&wZ+In{~$5SU@0OQJ4V83k5w3j+U)=y-d0<;s=eA;5^3Zp zICQ++DZDEoT4(RnKYDxN)yf~pe{_5GUcHns1l&|r?b}ujbY|sMm)84G+==7WA&*sN zt}6CW`uU9&fseEo;^4{Q1;D}pwI8^pev09jZyLRwmw@MmUo4+`sK~;5|SMZ+9!T{bh**kh_WlAFo|eiwpny(K0d%A48HgyVd3JoJ!0!fsi^3zY&>YK3$y;^6+|IXbNAGK~7 z0!1cr$UoCYNP&8GcP*1K=CK_m>WP;}{g5I(ndPJjc-BO)eE9BK5OD|>Q6Idy){l0# z#Z5wvo-8s|MBUUH)SXLI8by3aWn>S54doJuEl3TZTvCaQ%vUV^Qsv{q-d>Hy|7S*;l6CbA7QR zyJO8LjM#smvE0<{A#&-0lJ>;T7MAhXuUPB$Jb7m7OE1(-0dx9H*+VH&Op4jgWl8&J zpjz6CrVYr#vT{iYd3I0xd{ras64N2-rwF9r!?KqIf|TJ zing#|&+B+F={X2us`A&i)XrQE>Ki{eRAVM6#+cl?DS^irjlEaEhYMag^eIGUFaUj2=Q6+@fY+$Fbm3_Ail^&W zlzFvBcOt1$I9xvauvewT6WoSBOxI~ht9a7u< z>coIRgHnftH!vgv6PGhWw0FuGU6>BYdTRzcS1-(BSRlyhRl_OV5QfNJo^iKShY5Fj zIbYd()i9%ZKW%Vr9LJ9D*Q&cbVz0LT%J)iw-U`wT-|KV^?MaE-G+I z%pwF*b}buACF3HV4LNPbl^^?^r^(%v>`0q4wz| zwV{tA?94ZG@<(72_;~D8^*5A)Nf$tm7qOCD=Ewb&0DhpL$Nme)h@X1=xk#z_)cW_av3QcSvM2ar+Boj!JSRQFWEt_B5 zJGJ)m(N@7!VRR7a%!LfL7)#)IbqpX@tIF&g62bF(%b3tG2C%2o!^A;A7svt%IYoA~ zzMdw_&D%^c_qR=L zU;PhHm)Nu&e3Sx%32`^i2xv$kUfr383fLai5ch=HHWmSHma{sXk-Q8=; zFjgHuT?X)-OpKp=X!H&jkD|z7%+%I^wIm!pHOlDFJF3GsU*^rLyEQrdrR_^+h!One zomI{U)M_kB$7`*h_GQ5Bt7~(aJKaE`ipI=yloKV*hY=-9D(sE@%C=397GC_?t<}l| z9ywVbfM>S53=2^x(c~Us52VC?W&2rG^xxZAi&JC@RJ_(p$=8z+1JMw%3{ZKUjWmn| zLZWo3N*tbixmLSef;d})n_H}HPJCupRb@|BA;&>b3$H*3$o>9%->IRJsk)F+cU2pc z(W}z2546FD5SI?EMu-qatg4vg(m5}-2jh6IjY7E8Mpy7!jf&tYa%A}is4WR?Y!8cw zC8{3o&+abOQGwSZWB1`Eq>a+50=RN{bBmb*-wKMcs6r3mP>wQ;%RAdP?feu* zgy>71280DZ-();=V$?HXI%I0foq)$#Heo?_Mb07#hIr@hmohRReWxYxU|iT0RGJ@Y4@-3s`ed z@G3?+3B&yMN0^^D^uKa+FD4#NxtuL# zfeD$pRND`LD-gzdQ>H;`qq_H~&~z5%%#RfuA$FO91H>e%a~Vv^=G?(M$jm&jGLtcJ z=9I(Wz`M-UKfH)L{q}>E1K+i_kv2tUgpwZ%`n8gUS-`z2??dr=bRVL8ocea-< z6LdNDNAGX3ZF``S$0B}qPbINs(%$M+?&zbZrfyi#L^WMZ+H+(SK;Wz#d${0|BDw4S zwqyfhGx-3oU{;G?QQ2(X_lY{l9I8nBeET&(j8V!dVhPO3$URrry?OV#_6g@BQ*T*Y zFVfQPVTAsMm8FiT7=z0tY5(cj(Rz)XKc*5zvUcYzGd_HG#Ws-#kca`Z_1NFMW9l0( zm9FxA{M)10_e!war=47~imuyNSB{#H@)zZ8t1BC1LvgiO)%Kaja{P3$U(vo)m4Fof zSRsGy)>_=jodpv--uk>$8^I`zg$jTCy;6!G3#J^2{JLo03_9B!3C39jb{(#>kVbfh zo#=`pgDq}IwGXuYl3&?ei-S53?G@E)sx$Bf5RRB7WmVg#l~!ZFfNy9~rAyWBBkfMi z+U3!DaL@&FS$kFO{`GY~s(N}?1yEnT_v*To#%Dn-@{L#SwRwXl4iyFmcUFnj4ccxL z2K?^B)kgpI-F5Tc*A^xm+0sQ6fh^%Q;xt6>61;W>Fjp?EL|p+=ZL;^Z?Hhl9x;QH) zeECfK*x8nSwGj{IgSSi_J6SYWEv;4QkH5E+vQxwoAE5g&28ee0hD$Wg2snHCOs(0S zY2%n$(ui$VjDY>Y^UF9uPTAR7M43jqG$@*S^|*z|5sy?Mb13Tu*wE)SSiBh`)IPqW z)?f5Vx){9mz|`M=OX1Py8-GPS9>gAPKcq;SM${Xf6STh8w-$oAUD7x2(eJ9fyHI_x0ZvRp={a~BSjJA{QvA)66Ax%%I6HOi0-7jUPDPqxF) zB4ru;kAsv zuP;m=%_lIq%=u!Uc0V?l<*C`LW^rLi(4k+R%j+S5%+sYp|8PK{^JWS)0jef?0{Y>z zbEr4_cVLD{karj=TODDHoCbNyNM z+VwjgYnD~eU_RSEY(${ByYr&?Q}4a0@<8c<$t%eK zrI4!KeyHAxB0-rH?951aRoHQ;81KENE#hdbmZU|ga{`q$7ju0QU?e|`k$M4_=r3=7 z=aiw{y{=Y6u3S>`dnzSipeQRQWFjCF!By?WG3+RjIZL+b)kbVnMFn~($Nz9&(F>GX zLK1%jRc@IW`6ykky}Y7PZ;KqQ)GiAIK(YZt4MuK$^!`eHv9O9XHh{|EudFnCUL(V( zb=W}l#yu6-$^(zp8mQVV1h658^s;tmG+%On&|{C|Z0PiOyD@m#ASM;tYim!$@7&$) zj(bgOA-`H)g(qjO)Qea<|&KG$ulD} zkR^)-32fv;X8HcMRrRXZ`)_VZ_|X~-Zd_SQ4^lg0*R_{d^rW5cJ4pie58qvuiwTV% zxvSo8iw%$@QzWY7(BUDASug=rKrAGoD9XYD>*U(jREx3-Q}$_NKAOe)Fk>=OG;D{Dy8l%U;s*SkcF;ZR?!O{oH|pZmRd*5Q_!m;ZS($mkCi}Tzp?=?7lvOd*RTG~D@_G`W(ZetV{sx>Q_{K{*gkU8GD zzU;3Ev~HkN<*QGsZeLwXGrw|EjTjgsgpV4C0elh981GWwQ{YqpJ%B+V=COR~f-)?B z4btuDWeK&|xs1i-?>`hnI zO(!hMH?`#G>@EN#xW6J0B?%OTWOMW@S7*9YAL2ZqOv;Z;(T`7YS&g^z$ZrN9=#TH=s z;tTb-5tei(S`6133wSO#PM#hKWOF=?sW~D9L{Vyqdf$yDDyvs8fWddHsd8vMaU#cM zTyx}7*t3@^mySR$bRdD)fLa#zAOwQpFZV;BPlA}?!7BLiqJPX`x|V>ucw$eJ2TK2j zR|E!z5}w~P8YC|ehH?Tkikp9*A()w{Mm(z z8taGfQ#-Ox7#0U}o~X*Ds!SJVfgYs^vWe)q%q$H|;9Yji#8d-J*kEpF_SrELTEMw_ z-H=Qy!$nZbQGMf~umTvnYg%z)0w|AnPfq z?JS{*84Awbn9PYmkPLu9k60QkdFU*Xhmg5MU~-I}wPwJGuWy+!d_L|ImJhVw%P+1r zO$WJ*dCq_Qz0qIS5y$sTl<9r*n)>9n**4@iUz+;!=W6a4vA_&WRWwFa&Jm6*c`PJR z4)jTun4t`l^bE0s>fkH2XvWBd&z^1gHern!#5zK39#6QbvopY(+!f@dEiThoIaotR zmoz3H)iPrgv1fLT0urSOKEq@JNfOjzL{DR!b?IQ!Y&ZS5?&aD|ds(g42z zOhJ1?Cjp`tnzbvYHN&38Z|M=98WY5 zz$8iq%|bHSpsXPXL#}`efQ-OqHHh$EW!1SEwuluDh(FB)bL=Y^X&O1Z;~7 z1Rgt8XD%*ZTsb5~X~Z~%CN5mRVrtd0LSm%uS8pAeU5)$0+J0F3p4x)TZ{Jm`0NW4M zy#`n-u!QHMbtD8sPJ%%A`sKAHB*+9A4I_a;VEH(2QHAD;_Co;}moBPLRw2l$9BWzb ztZIMzx?J@ojo8+_NK*)7Tu4&_eUByFO={a{^HR@nm z;X`+pt_AZd<0T5g!uiEWLRW|2?zOdml3Y4?l9QIB)aB~srnanEUbG!9&ee|1DZYca zkg)G>pPWxTnGtso$N<+at3{&P&9|=hU?Pwqr=K&%nGh1NXktyNN?B}4ZvDM{FAG}% zy|NglGw^X)`;u|uqZWG{$G&Rms3DxIiCe$L?m(}-4ESkcnEl@Tzbk0I_^B*^U)<6}|kkElZ0K*cLAiSY;PE;OKz@AivPF~lzIp{Zen>-6-ggd6uulgB!*~Ms z%Z%?uc28%4*>weIOk135^=b|-5X>iMY^gc;8G^Y5tU36Zf&AGgFnz2)@5AP`!;e8l zhbWpwovImYhei$wrsF#%XW{i|%+oQ@3xVnG=kK%~OoaiOP)-no2?@EJKxSem|H<-A z06{mbv*hUb%*aE~&*tu#G-E*tOgK^n6lR=QbOMkB+Yv>*5J>Wnd0s51C@Qj?7fN$z z6OP!>XFQQ3W-!X^C*-m1wU7k5ipsdge=!bZk{6|ri{_71E1PiV@6w|LplKwzru!|? zJ=134E-9rtVjaj#e<6v2zk6K?Pe@gPUO+>vxw~ZSZ?wGw@XqK{2!<3nk3t~T2(S+cM!kF}g)@B?jU z6G}9C(S41$Olo53RV%M>-=RmA)^uj)ZL7-QlHWddv$4_O{rG#UKhO~^OBYoiTh*L0 z8xuDyK-C9ssZ5m(DEA#Nb>G@rI`*saa@Pl(6T{ZuqPYb2;u(UeN*+}o@TQI zOX4QlC9C}%7>in$sw8Ga5t~j66sTTzd2O^I)8+5nT?TADFw&By0fbW#5GYj@u@>;J zzchM3E*o~(S+IrkQSJyVH>`{XM zt4-D1i310<>7PGWzDuDQmh}t3K?Lw@MuuLQG%dmg(xiL|BcwBR3`3^hW&zQm3O8Q8f!3VF6ctCWpWf)8)<<3 zDeOKn3QJ@vJh^k~p7x7}9C2n<(J!seMuOm=XSuenM|r!8btfI>EKTjBY)|hh`2xi- z=YRUyI+xh?R-LkQ8Wj68yK5!obbG)N#ZG!@(Vxlm&_T={gA5k1aNcNPu{T7f2hMp) z^2_wB=wMyQpBvWsAx$O@juYiFR!PqwE}~oE;ti z_{+7Nnh3*e-e1OtH)kwVQ5_-3%rh^ zezGv($lU7H&jHLWV9mi#3!Hc4J#XzB4YQ~)&&%@7dEvazgYyzh!12?yKGNX=<3me- zx<_L_hA=cHg^p2pz9nzdVS+N{J`L)S+D-$InG}M)_qz)jl1-l^DVT`At^Jr$AdFeP z0?LjdAQ%9A@)zJbzOimB+Kmz^0a2AqM$#S?A4++01_3Wg`OT2$AZ? z2AHT55J^HN=&8y9bLqnRI}-d&SB%yllLoLMQ%_(%oj>OybHEcAQPKve!Iv0gZC>B6fC}90qJZR9hbbY^dLbk=);LY!GA?9qB9;h>0{r$)4g&G-}**`|wabXwOsH43;pgvW9($Bjv z!Hd%3_E}#TD%hXcqLjT!y@eTLf}$G<8R+tEG!gi z7+T_vOs^8omUy%3o$IIm@`dtinp^5My~`GtF6AKK(l!L)B#>A+zp;X0pMIhk;~>hM(W|O{JN|an*8R1% ztDtc~G}e1t`^#+XF6nZPvsn|A#A>@utzFbAZ*AWs&do?jJW6YVa%#NZ?M&1s^8)hH zGW*%xg?z`gmCVZnKfkf8Cd5Gk-i7mOe-=?#{{2(c8=G4dPagffH&>M#hY&VX%_JU) zl7Q$)Rc?D5dAedePCm1%ti?`?GbG`6t|@gIw}UFuVlBM5(N&8?ZAqvv`APLRtg4}m zG(D4Qj5Lx)a^B2^0aQC`Xlh>yOWB#UufJ4@y)rK_Ub$q%o;YCV1v88QH?FKIT42Ik zFCluYwB=VURHrZ1K-40ihA>utd+TV~1!Z+YQ94$ZJhZL24jiw>r3Jh5mtU-0NUx4M za_+gh@R0gEz3{c6ueFy@SC{jkiB)Yh05L{cK^5u4_ukInoyL4fxP|mB_?rQM*g9XOz;F1au36R*`n>Xk%?ZKLeRrK1DRv^;C2=M9yLZL7O zdiNd~ndgYWc$C#EWth~)y9&$EYBlF*GysBp#>geIxD&?QH43VngHKh?9eP>8p_7o~ zOiN%mYfVE0+=&LvNXrrU?vBX?yU?UF_ao2?0W$lNK`hTXV4UnzH82!dmt)H5pN<;R zq;Y-8GQz9XYeWI8+4_#M6}qGbBs=13x%ef4TtSnvizkkXx+(nHEIU zs}=}6&@=T4T$HE_^yRg<{Ho!{uW=6EXqa2TnuDJf$oMk*D06{h(Calm&M0R_E=tJWy{V@3x88h{Oa?L>- zvGZ^W6^wJBTY^3l+;x|Ykj^MGPbDx@XApX|QV8XZd+PHvC_yYV({*+vpM(g_R3Ygau=WT}EJRPzVc9F8AAZ59K60phv?D_10!yr5R)QGe-wu zK?(18t|jCqj)a5;OEMEkWR>u=cqZmCBOTJD?Zth?B59OwgA&z}8Ez(4B2!<{T3Tdr zBtFuG=C|9s6@aev^TZP%h=Ty5%edn%BiT%G?>I`%H5C!x=2gL`-;r*H@Qr{ z)w(1N96B-DW2lPbv`7LYnO_P|yj)56(e{lP0bzE+<&eW@acv>-Uf(`>rj?mW!Y3ZA zPYbbR@%-YYOD$=*B#rf&&o~7tjco(PJJy#0;!IlJ17d&yjTgwGJ^3fG2}l}+I+p|j zwxzIYX?-)B7fIdYSFLNPZ9S7TwWBRdt>3<*eb4;PKtn{{_vEQ6!f9uA z?m-8Wko|+VmRM%5%=>z6Aq)!wH<6?+x-_VS5FX9()0LAy&=!dZ6fQslIlN8`=U>ux z9kw2lk3;1nP{LK}HcY*ApakOjy@xCDCRSv=_(F*l1d<(06(T}5?yb!xix$*nb+ZP1 ze{6f@HjrS=Wwml7QNMfbh{kQ=Zu-aXsF;Y)=Qmb@DxM4=Q*-DN3}%uuDc4+HD`;{^ z7?SH=Xz!*0mLq@{F~Uipoe)8OYF8;*)!u{4uWcSVIbsxHGIt%WjP5OKO9DLJ)Cxj* z*BZrH1b*i&O;Kw;QPo%3oM_y+ru4#Nq{umpsJ8rImLjHbePN!=YU?XTtCShDW&_Ol7&_fiSW?VLUzjUrj)HfSg_ zCiF1ZEU!1b#DT#Wt6AG#8$l!a-#tDGg4?S0$gHUSYuMj+V-Z)N+L8xR1}Gz*LMbzLjK?*dC39<=kLmPS_KPVZFIwBAYyj1^V_m9#-c#^xdnDKrlKb6=n6Qz(z z6=LDHw$?jhswyvPpOrCH)=9LBXj8`$TrrQQi5^b8fj@hu@*>12Np_|;d7w)Z`ViAGez96eRi7R)bEn&a+u zwIhsbylAo^j?FMGtK14FPS++l5{RljfGyv4{?MJZm5-fQUsf803-a4rO9g**NS7qj zD^P+)6vo3ROO9+aW7oT{YZ}Mv??9~^fdsLFN55!(ebk(%%U4t*2>01>wD^(XfrRFoL;p&(ztrKAUFJc7LO7of8eMtgz!y^=dwnjG(~H*f#Gh zGbpv+(VNeXc1xD_)`1=D&uu9JX-8#*k8Pj&hi@-3ofDaYC?TefZb5}w~v0u=mPcRyv5(if>!`B6>;c6r~LN# zRqjAo`ne-}InZl|WPypx4tc*aTh#!@jPS$dGtdRb!E;FiswRvKWG(gN1^j5B&Ia`V zJ^}tQz$_k}7p6!1Rlw^a^t<8nu!5- zqCERp+zE2-a|qDIc1#DD>BzwLgFo`;pg@;1;X((Kw#e*aXMBoC3C#RV)G{pwvd2s& zLf>N&m=NL;47H>HpNZEHdQ}QJ_c>X|guryagG)&1O1CQV&3ZHGS=pwN0Mfyo_!6?A z8Ir8bp@%a)M8$m>qhcY{H!~h{1c0)?X}9%5OuNBWq9cfg}tlorggHBmg%{=D~@E zEM?n)%28c@zkRO?8NXzbBv3?@j40re7WnWI0xAM6V{w`ClKG9eP4Oi~p%1Zu3y$#K zJN|xc>@>y9>qW`F_P3AMl)&Z)VQ$)ryi=MfmlUCXohDJ#Hym5wVsy0VXSs^Y% z7+zsnzNA9U_{fh{v@15RvoA_Q5Q~xWtXkXWH6jwb3=D<=H)z$I6tP z%dl%JPnI{vPFy;_OR@+rOWLlkkKJEcncjt9{v{><$(Ku--UTy?-j$UW%Dw_}=?$xj zmw6!G={EUb*Z~;RZ&XL=>;EYD;K)on#(F6 z+(fKP^F>2zdNM!pV2R4hCKaghLMJGbE~$k@kxYXwo?mI($ukutwyCWqSX_%WcdZ#M zhmrY6TSwppW7v$0yp+iiH|_hUPq%Gcv?8nhjwJ#)lu57xP-KV(AHTbby|&z zW2Y)ypKAnq~; zrNsPl@D$mGwxUNG{ED{NiiMqwCWnL7Y1^#|oG}`8pWR)C-PaWH&N;s+S?;&?RjzMf zx21VyT>_0m6$!L3c-f-5)Ia~zvs3@4x0eAZ1=bmjkwopT!`07Kfs~Ssnp7Gyc81E89yRv7Tra=J6ffSIl?MFqhWK)k$Wd>-Zsyneg zj!_EB>9*XHNdkIRS@m!LGE>NT2rrR}R^`G{3# z+|(Z8T?k-vE&~d2&>^tB=`p4W0+3vG#tWZqV!7-`7|D5V_h=4>3coKBLX0nW8CUOqCJkTS96bndqk+b&gB~`_nz^K@Omn z@RYOg7rhRdrvwG4w2mySoGPMmc2w5SUv!OA^-8Yzq zLIU>O)xNgtK#wtE#Sm{i)iupbr|RXym6&0C;^j*J0Cqj(k?qA! zj+7&F=(6FGT5Cg(H*YN=rcrW8U}oEbW4^fUV14KWS1if$io_;nf3Ce&6J8k7ZfvuB zg$UukLqkNuC^F;pY=Nw1{Fi@c1m?0QPmg4(3M0$2k{Czexd7u5m#i4fYquN2kl|%2 zE(FYY;Fd~>i7wo``OKKtchmE<>;>TR6w-An1vbCo5-( zCsp;eskQbc3lf1H#dlmJPKc=@Nd~!WQ=Sa3qkO?xw%mR4%(* zwQOW9ViS_Lt*c$4fAB`!^ZN$)@(Y$=99()}Sa=$1i>_<-4j@KJJ zbN7u*r7p%R7*N<22z}`tk4P{zb~P@xDKPutZ51@U`34r%%Bl+55K&ys_?Tfx1n?+B zpAV?>7r-EPoEd-89>y5r0mlG686|<(Q{}P)Bt{S+&cVb%CP9KLUS-;PoBWL4JVzwZnvvi3ixLetvYubFrTbf3DT5 z^8$fgsX}H~LoKsEkj`aKggArgPxo`dkZfqx1bbGmI=*j}a!FUzU32*e34#QncTRx> zbG6|FC6hLIv${@f9*HKHuGHBByU+Z#x1ZLEYT%qgFFPh?LY)98M@xVf5Q5DAR7r0< z`#V<|*)(QC7R%#i%!sKG#Z7bEhzjIO5H2w$2q+bJRF|y^I6L+=EzuQ$5hWcmdnN(b zlRNA0%xDsmBm-fG%$y!FXVXv;UTm|;-UK1bd|DnG!=z!f!(eenAe}(3N|L|xP>})o zv*a+2Cz+2Nlv0S%8!Ih1u?m-LD#@oK0wGho{a_6gi31DOs&ZEAlKn0TC$QH7^pJn} zWTnBWiUif{$!;>W@5bv06Y>rpTo#xhu-hORCH3_BAfTFS(>{ zgvVbVy&trAe!X*)s5+J`Yz{WPw2*+s4eJt84oP5Q`@0X1bU9qYCQxNn+w_DCFD`D9 z!;IQZ(9%WHe2RwDIr+Mq*Sd! z8s+mFt6TocP4#IoEO`U5!rR`j{NzK0L?5o-f2=b3t1qi9B-vS0evP|2nJxwJdYPoBvC-s z$L^nc&keO{hzkhlDhchTUb>*}xWE6_vghE5dg1Q+6~!W$r7mCAZ}$jrcXNc<2sB(8 zD1A)RfBK2Sl>Nu*6W70bYb}*3zx(>?9>hL6UcJKNzMzqv3Ro0SI;&mWz6QvC;QaKi zTE}6kI56g0TMOwxPeRl&Rp+lUur*UzwV~)qW710pnip-CoMHT3>qmX^t?k$SKD)8X zG)A2&yx_%s6&Y;}h}9xdT1K`ntW%AV5!9(l&(Go!)hH>g7&i87azsyK-p8zaq3t$; z=N&+t?O-FxtwSn*p{>S#sr`WUQ|h7GbFK$(M%Bt$gIb4TOC##ytpZF zq(bz=dVerWezEG(_W4CLE_G%a-_{n<9@*a7x^;$qEe^V?2ie!R6f*C0!>Dyn{n8D! z)pWynb00V3I|6;*|V?I5|Tmyd*~6T>`{V- zC1LNNHb}Ky$nZRbjxiA3*?utqCSN!u2XG|nCIALrZ6GvJ*Xf5YbYf=rRk=XuhpSgz z!gLr4OnEx7<^z!VlL>+e!#dItt0p7_rb`~euyz)*bHRl0iI*f)olE=dR?QAGGMXtR z_!G6SMbRA0!RrNc3s^r7@Uysjb+K?EBPN3Bdt5IHX4Eo)dDR2=jEqB#k`~F*5Oaag ziIAPkc=*{L#$a|uy*7oEBXc^}P*lq1+ICW8{#)@zA!?l1pcl43;w> zmMk$CF{CP)2}*6vN#{21sb&Re$WPu93zJ?Z(Z#rWyxEX^Re}#_k!{M>bO{0@ zL2W{fiwd2Aa7J?3=$BmglMmHiAzJ8i`A1LHy4(Nu$mre4&{I@$+KLaPWLTn@@h5G8 zAin(K_4PK~<%_BtsNyAdEC7q_^yBZX4lhbu_SI5OK1(FEb9ON4ckZtD%MkDaK&E2@ zHV~M0Ah|)#8=Rcd=zdd(jEI;E4QNudc;4lp4C+XssW{SWZi9 z%kle<6)zx!7bI-nSEgbWc?M_M;?JKgvcGw2-2#b{u}*MU%lHNJDrnl*02TT9AQ~Z2 zO@cCO0fMh@F5AkU_THinDfpC{HvHaqMjn0i{^DA?u-4iLNgqq2XwZ7gugO1ftVWNt z9kKwM*_L}jCppx90dT*RySa&M%l=WLESg`NlH|j0Z;N6qeEIOym)cMDkxnMb3Ru#4 z(m(Y;M&|85a>&$Iu}kS=!?(9i{q5W9EwEf7AelQnHBM$HW2(yWCYe~BA(9ug&+X@C zQFUeOb!ik4kg-$!mCd!6C$}xD)RwhBti;l@VEdt3m;>~lI$ImQ6cBG}uck6p1JO$$ zBu0sPy_dbDC?Um`C}e6ur{rd>p`N-P5`sVh11D!&<|Iwj*R!`vp&ULMSrU zF|S@aYhN+PcyGG_QD$)m&~^X%TH@5%e*4b4#qt_doW)i4w~x)EXM;;o3j3E|e5Gds z7!jsDg@kDH52rqK*VG?>uU<(eP)H9QFPgY;rfqZzu!K4xUEls<2e0iAAV_(* zdKEd236sQXcsU*7T>xcvT6H1LLw4l}!$2;FnCYN)!mvO$dv+M|PM0%rIb6L8oJ;<6 z{ES9kI1HB#hwxftf*eCHf&be_&G zKA9*7BEdO^<)E4t^GoTFiE;vIhMHBXKEcA(r~7FpwQ`;=ybDZK6d{PZ$O%4EWV)ml z*$xnh@Rtr$ODV%M9tMkpVG)8o4GAT+VF)8$P+*qlbFRv7-w=6h+4C>wjL_D7MSh$|sQy=94(ovV#Q6kWPw1xXcjv7j5yvVM;eCh_7 z-uQz%T*mU|HT7xpd=UntGh~)*+}b6u9X%%HU0cy`?swwRQ)MtRHos|gy?vH-4MW(#K1{H{fSM5LhN~PkuB2mcP-FL4U`A8*RRVb-rRV^#DI}09?CE_nupLweK z0|KohEHF&f^S}J!)Vr>$^##UfyUL#SejM+pyL4e?VhXrWaN(-U>YG_`^r9D>v;s$@xbIwFa=yK_{stgK@i$#`*Wsdc>Efzo%EebZi`B($N};IQtpktp7Ef;-o=*A-hEIZ!&M z;mA?u!d+{pzPGDd|M=-zp2Nsm5)%0hjdIJrl8oNNXu+vl8H04h?9lUW!MqxpNN^{r z1-lQAmZCafxovgrsX>5?kLu?7#I~aiG zZCBT3$y^RW6@bt48g#!v{Ow#bA_I93^t=!*X9qbcbbJT$IR((f<-m*W{t~4)-q}H3 zG?|by9fkxX+-T?GA1 zJy);5-0Icq3v&xtb8sFQ4nS|{Xc)MVF>yM)Ixg_48He!dB+SlbVx5JgKyu3&S^C=6 z^f?C@V$e)H$fwhHrCDP-7j#h>ktejc><7qXMR~dbA)XN?=sRVf`sC7tQk4nckW!iX zA&8uma|w#2g=->cow64u2wZl_fi4HgUQC!$hD;1IS>JoK_U!bkK+!Y_DFbDN$}W!9 z+#QR{APYYZNLYeT?g7-A_BlYfWC&?SGrw!w&((MKOr7zX3Wo(oo~+}U7MrifY#Ef9 z94=LBmK7#S5%Vas`A~wlw5SibdOeQQPDFDBQD`W z{~LFdJ>b`FEwKRQcemGFgA%>tn=jSIE}@<`uXetOl9)uhL$6a8?H|3rgw!HJ8v(?J z7Coer-u4RTjVnv+TdyAdc0u-QIJiIv-aFU4;y#VoQ`X{f^Wklk@!}OpW<0fPqzf4O z1Q1ug(U!QWW!ppBUfDYX*uWA3Y2>)T8*HMdy2-TGs*6SB-`MRzgLS* zxR@lEh>x7Cst?{=?k9AgexepGRk49&@j=WOwe@F)MOBbImyhF>mpGDO-lE@bEA}+; zp0-jTn1pOURD0qyF{@CzVgKT}sdrpgTGF@c7XaGMCV&R=(d|{hj=o~lR$k6DUC-~S zG?jLYZsdHW?!P=={U1$;L8Rr}flLu|v-9GSACwm{fRWlrcxiNIIl!wx(c!j0gUsP} zuAfS5tG%bsG{?ssHIc#cj%zE=+}q0e)kRu#%h<*RDo`$(SGNiI$TqcA+U*CYzSH)N zl6kx>bh>>a8N>kP3}O!8l0cL-oVImFkmV9iQRuw(vI?5<)8PEx8>@;6coIa+NQ(~T z=6!YdrRu)c3W+6peFL6wx?HMq24zG0gj1>#Msj%PGV6@R*q(-TxkSlkodnH0)>l8j zVP&np-La zau7J^eQAGbjK4vGxZv)1Zcp>BxsQ>A$99xc8B5trAWHPh78N}*m_Rf}ETAIuF^puw zkB;^PiB&H1(RY{**a_!O_DPYicH4m}|4*N*<>16~Ra<{g%LcONwA=6QxV6Wj{fJ5$ z%i-4U>tz92aw$IggMi1oto?ybyyH-Pp28&yMY7`+QtqM@@*xWaN+I1leXeMRkl$Uc z+P3mob`B?R?H=jStLPnu3xo`NFL#(&J|kp;U^;}r$UOb)BGbL-kY&ECUPa5fbX|xI z6VNR&Voba**`bS__&7{f_g+Vc4X?tBz=d#KtmId1%pCkRgt-N*IXDmem`9TfS2z5N z(5p6bM#i6pG1Oyqx*0PX*4MVqYx4y<)i+_CD-Q6Pjm=m)9oaEv29;P27^f4P)0pni zZ-z0{l5u;-w8KnyA=c(+q*LyI z81%15L?eiheq<6EnjAY07O@%O{^RYHllJb3JV!w;A-{4q$4!(_hBpwUW`{Ax95*|d za=_4~mbK0%Mm6cYqNeGp5Rk(js+TSp$v=3!)v-wuL42PJ4CEC7Q4xfT^+@ zq2=~uKD)c}G**%J>$eq#4drQDP2d-ySGJ5)R=@Q?)e5O*&5G&=21!1a+pl_MyTLu{ zDii+v##$YdSFgLQK6teAa2d$}>RvuvctXmrafg4QeKqXN*;->F){}zjBvJ5>ZZE$S z?rJHwTjPVbO#QpZr@sDDNk}t&^#1B>SGLbERj$~gO1Q>C&r|9W8L+y&bQTO^E-#ut z_20a0l>bLyV)C*3E6Y`;9Wk*apsBA`H5Z)uu0Jd2Yzb+ZePw%#ZCynY7f=Q7rq)d5 z-QHTgie$v9(oE4qz{+nKDHMPCLKV=*WCGzdl^)BIg&BAMz*FM2SNv()gL$?5K+*>Wk z4CWyqGhU|`b;(!^QzB1tfFn0uQKJnq1O%e=m@rCcCYOM^sbImp>Zm57svL}muf6BYXy%2*WeSNvC_AzjE)YVT-BLqH z@B=0`6q#2{mwdGC>7KrNHH034a~XEw2AjX>)u6MSYxN3rn&Ijdm|b0;w@-(2#Wprv zpcfb0K(7vd3@YZ}=LY5$u;$=r6($CEa8V=dEX1#641IN@{ES=|f|-EGaWj85e(~C^MW&|u|dzns!Cg=R=NiBTdZ4BmQ z%TYVTye`}{7q|?j@eLImyTiKvl~Yne?TydPkDR$Qh1DUDD>HRCr%h6 z5SVp^xI-=-SC41d5(!*c#XxU%D46n}9~`XRPZIS5$48W53`>36xP1@`Z_bpO>pav< z3Z75H&ewfw-&P?5XxaQ)fyju^J5d9(B zC`g!pNhLxLwA`j*)pmzPl>9-vhn~!X$482o3L~++uPwnO$ngT6gk(YS!lj%_=7!)y zcUHQtip;E70R+rYMh4*3R=PFLF=PwUEml?f(f0U!cSniJQV#7TBzwfj&M&ldP63cm zZno@EZD=G~QKzg*WB?)eUR`Sk=;0k`k|C&KbCQ{u1gb&D3to|+!^m|JK#Idh?y9q- zF*J!oprVwVJTC_RuvRO~?14L%sbzdidXw&kn0EaNB?9rxH*563+g6Q&nK+B^eLOu@ z+fs0S^!|G5M=fd9ZQ8mqel=5~(;#5FcoHweUjHjsT@Lb*ROgP%{YujhbV@{90G~!GT&Jz_==uD{ z8m2z;#MH;%S9rRdPqj7ZXLr|`cx!J~N z2WgIq!}w0&e#fM-3=tq9axbXT{hU2m^G&fB5I}GT29pku34UOxXae$#8$#F83(iJw z!`%c7?NJ^EBoGUSkkzXR#tE+12(v84y=p~6BVS)@Uj-OW&+XzXkknuf{yM=#@UM+wq_*$;tMsYdM$9_HoTT+LwKh@CL_l=e+`=vy51C=hhL8GTrlR8 zJsKhCv!Wq%ITKZgCxTf9H%!+u0l`m92qaPYX~H1$d~(p39YCG9lJk0>tF>eLMwQzFVt5tAnKUtkqy1+g>lSNkR3=e7+iuyiRzlt*;odIH;hPVL%f5fm_C`-_I)%2S_h^qX&n-tB9hg{R03DEZ^{2*`&~R31PSS;AC9)IQNcU( zi0@icizY#8zHCN2bGF`TLXe0tBdXCb{?Y~2%7XBCqoINU$f8B>b=J85YcrJuT=9b0 z{K~S6Tjtl>M}i>J-<1FK>58hXQZ0>1dqjf)RFrpPW4`o4{fQp2Ms&MngHH&Y>rTI> zKIuiZL+9LoyzTQ!hi@$_)PbFcrrvjB`K24*b4@Kkf8gc{AbSE-yx4ZqDKI0D?)R0= zl?k)(!*^FOA>VUD3IF`Y8Vxq>D|*gmNs;^?wEa_58~6V8qf_s{se;B-H!pVO#S6+i zl4(bIynVTh+4{49^Mb(CLZYvLWR0nPeQa+Vqtb{D_?)ZIZeeaBaBM$R@;OCL2>zd6 ztDiXFLWtuLjYhvOy*PSYy5$pOHmgNUuT~B*#p3@->0IZ99;FL`iy}T3 zymko}1;J`YCWr8*FsJmF0Sa5k= zo&gik|BUcjB!~B0kIBGvP1*>|4oS$Y6eaquMZsYr z?i?C3(VDVfTlt$y0;9dA!cB`=#kQF%GTL{HKmL06x~f%<>YSrJypat5 zvHOd%3seBq)ne~)f2jkw^6HT%axrn5X5;6i5hFBwJ_jVD_K zk~gXqG`bi-X5NKC0|)`ijEQ&bOoUO=2+5te&o&0U|K=JlR$*@m4Ebo@x^5I+R|XikuqWH(9rjycol$hJZ0y1ca$YOQpHZ?E&FRhRxJq*Q34AtsSRIxo$h4V`OnW)aHz|@pj=Bl zdgFLt0eZ##>svJ1n%ee*wOGbfhj~0-9^6!`YzP<=3qe{IHhTcmy_u0le4wp~IGCym zkoIf0)|qxKl7UX}Ig&LI(nK5NE80KO6UO6PS>7x(P;+6y{E;!tz^>Ml;`UXez&^Nn zw9Sg;IuaEWtzI_u$o3Y5OKP#(Xh|$e!K4&D(4hbHJta2M_+?aarx>a9#=muY z5tloSUhS7&X!-N_i*Jt@B3k6tq6x6}o~sLEh<@``wO&BNym@7b;4fY<_2o^~j#2uB z7wgVStdK!6G69TO;5Jmq&_{~~3W0Ea=&t%%UZBcz&~y2^73I`BTU#f*3KXHw-&7+1 zuQt^mafTf`sZ1ZD@6OLnc;0KvD(rNkka82GDat#ux>1$d5IXS`P z3_|1F+biYH8%TLXJX3F(pa;1$>FINz;vN4I%@7IVh4Sh4t@L2b7l#nau4u+h+QeqM z3~~mYrJw0?5MX+3(tOSG(ax+8NHygi{co_{RP>3AvqvDLGEhh=9v4EUuw~!KSx}7Q z8NaIh%@dgJ?;B5@Ve)<=mJQ=y;Ep zq+~34YG?gb8OVQN(&A=>H@8=ll(YLFDuOAcEtf`r`b;ImYB9=Mvy37j2y!5!D=!cs zqdJRmkTPnN#7b!Aw=btaiV4a9by;EIFR`3@;FfBv^0aO0!Gb;;Ud-dslje}@si@_r zrOo}el_RS#RAg&FmNV{^QMVKYoIP912V|1({GmIW%~wwS+sEtsEYI$)GIr9|TvpqB zimQFL$(b4cX??s*RTekP+~TN@n$rT5UPSz#!KL@ZYU%nkG9Xr z*G9I+QpUI5;w}S*4nopVH6=6j+yDH0^$IF3TU?I=VEC!eSAlK8Lir@-5|&w-c|*Q1WLm_vujl5 zl;`R1yro*OBJe^r3gDoD7mfHJBU5?rk$Oo}SuagXv2gd{vX%;}*$K%1*4A3xd*6+t zkLaW4?4c8*-75(@IeHzdh{8@j3F0Yw<^HkyJ=5>*sM`%PkIwA}YiCw}HPSR-#dwqe zF@RWw^gSLOZa*N4CVz(`ROQugl2s9fk~+M|AT^fzF&~mA3O)mfGAOd<6fOkhgY?+1ZK-iA zTrLrWmqzkwFH6QhtKyPOcoGmtkpqn?yf8Uj#+?yYGxdg*)ntNCLYdcuKmvC(5LNW4 z0?N2@#)8;GJb%nEyypp94Ro}YI75jhpvtAAL&~)hy`;7fPE5Tzbgu4TF1!3(KJjt| zFrZ2+q*mGOlWrd`h7exB?EBjOyDrl?HU~;(J6>TK5O;j94X7Qm1ST%QkbOG+j1avJ z1-G6%I^}cms>)r)(-~>(L{1P~l=cbvulwp1xF~z( zU=Gd;a|>91P2nd#l$_UqpU%N9RvkOXFYFGV0q1(&Osrmk;bbK&o%dX;SN*wrE-f9l zwXNVCrgNFJnV-4Lxr2m%0wjc)&rP5Dbf-v&@QAv!t+@1oDgTC z*`Z4q5(gB9^PUu7hV&q_UYGf8?_m1$bO@;(su&v6k)Y!{5*O96N0|)>l!dnobl$rE z6&6WU{W#kA&yV365=WYO`49ThFJU;BoLto@bAG~EW%SYhSq#sQTmhzQ~_9Y-Hn0@-?_)ui9NR!}Rj{7v`x z`j+~|F=>*qG_TjKSys8n7hb6SR{6LrWHm$!mx4<&Nx(jeN&AkE%Mr9Uyby>LQG43g zul_~*=n`IO;d6UlAu!ziYWpR`_ulYIeCW;Vy*&M7TOcOnO{=RG7vk^ksO7n6Cv)B9 zRjZ&j!6BNda^1grvA$vs1n}(HDj>tC+sA&=&@Wz4U&5j&K9;lXrvt%zZyaqMg{kN@ z$Mv*7dAi=v6ils_t}E{iqj&=4H?OWYTeaZB+eSNC>PBlcI=oh9uwc6B?r;0la^tA& zzCgZZUA@yGRx!_^3j5z~-|7GS#_|Ott{AKqF^uMZXj^S5a?`1T6aejuE9N<<5L;ek zCzs1VduC)+7D>bS?X883YGyqC^3;7-myyW0_NyD}6Y{D==AVDHlz#lZRfY2MMWd~| z_gr1I3E=AXx|x2C@}>l&z2{i5T{;R8X^i=k@702lGDxnQDkdRTX2aju&~BVd>U~cS z5lVr!R;AUzA3jm*Sy}i<3|G-|MLibZbzSQK?K@&DbieD&*?3q>UB^ay$CyN;xCtQQ z0A%&FHnxb88%n!l=NjAQ&8wv%Y$H>Tj<&P8WW%VvHngy}b}82yW7ajkwxxb5lndws z2yn$aXP2LPsLBy&V0>hI4SK;aCU)|Rma?J!S>oua5?d~}0iQackkJ63iM68L7l3vi zE@?EfR#?`rs1+i?DN5&dYXFqG91MG~xP|Feb>raM`N8xub@3>8|EV%25XDG0brwMY z$&e`n_8&N2x9zjL>)VTo3>t-X?NJDr7kb)b8+9J>_q4}~`Ck7^SOS6|E*mxxXfIEq zG?{L&r)~R+@u3IGxPU35IbJY9I%oMWEFBLRqm@GgLQ_rFWOjfweu^FlWzHQC^q53i z%3b4hhyh^6+93=HQtk$y3wo`BDNtVBP>zGsd0`ycS3V=?OJSej6}=gyfll1p==Fj~ zp04-xg*RIM9L&M%4s#1wbMOin>ofG5gqaWhq~N@dtH6-#0$E-!IM+_sp$!xEOju}| zHSuQL5YFpMCpa(VaP_Lg@S(1uQ+A#)q`i>UD=w?gP87P> zObUl{0!R=t4w?>zawwr+rqD4#f)t#4G@SMNP5>rkc3_NY%8(c(IVS7DbVf*^tuI^j zBgD!>%3ek<)6UJ8xo2;8Owv@~GVoI&xQk=K!8`%&{2ykV(uiOI|JUPJ= z`_;|0Yblfaz@yviB_`>FIl752Vo8)T$&nCtTA~!b$bMKUxAY*Ba%TYf^sdnhag_r! zISyr79cJkMr#*FZC1XPjmOE=V6N^`bt9?6?)42!gwM;)XyIUi`@0*sM5KEJ1yoRrZ!Ko}{d zj?EvvziLbACABD%g4sFSviiB<%F49M!e+oVmybj_wEh+CqxFYQmiFp1EgMw$tvkxF z*pN8Cw7*_C6s{XrR*RsCF%PtTTMn5#bqZ_SR+l9UYt<~r-g;U8VTE0HTJVa0Z7*|Cgv5NwxbYG8rQOmiZoS!_f3^T zr!OHOe^L81Z*ju|YJ>!K&YIW)8!KUK{NBW*U`-nph4V#$KqF?8?M_4T@X*KGW?sg-V9 zEtp>$XlqG;J5TPcZG#$!aP~VR_&>b0s!pA**Y{}u$X(?-MUW!Ks`Bw@EhPPazEw1z zZl4K@*IB+%22{Iq=~Ao8`Gai>k5*7cCUudPFaiyCQ@eK@Fj(BSZ(3aepO5%M;*hND5HnLt^d}+f#daFo*;v*2djV#K8w+`ROkl7BlyfywHO318cap+ zys`Zm2BcTea^E$z$yaS`M#F{w_cv-mt(^K{ed^Azk3K4ZnSjgRxTDsm-nOo4d$W>> zhRqeU7Jpo-(6HHMAd(XzMsC^PY;G%SzkE|gH3o-(aP)*LXh%X^=7_pD?h>G(>$(-A zk;GJ_Hnff(IzmCFn1ipkA240KplGtF7lFs!c+3{I{4aSo$Hg`xlwY|Lmcs=j79;iJ^L*hUvyXqOC z8!!aO^wJ3!68Ex#ZXZwv3Bru9dF<3s)b@k5fS60?Vbu`aZaoY5LVKWevTo{yd{OsY z@`o^c&^la*C~$#fb1)aEpMIEIz?y?spnr4fC%AeAE^yQs4(qRapvfThDu${HXADiq zI{oz3tMiJw5M6ncPJn661mnbIm)!9%UCxj=ltYZN8GPPu10fiix_n7ZJ%`X=?CNJQ zWEpDdNYH6|E$GuhkV2;kq@TdYqfQX$jDcYl2xC77l*u&f?UEG;&p8AsBq;)NQ8CE; zxYtHz#410(r+(9rkW4*;4~uet&lG{FFv(`6svo71&+e4)~2K#1H)mBfr3ipW6#Pso4OewQtVB}e+m-6Ls{lfSpa zb6=ULTDWPgxo^zk%o0=Op5BK5l;rlQ8rmPAMJt_oyci+J zzV`A_sNu0=2^U>}Nks8ZRT6vQE!8bOL$Hf+w* z^R}|Pbw|AK#**Vw*8OxFE2LqP;diXB-F)R#+kh7K<%?^1yzAC1q8=hxyOs}7$mISMoq`}oV%Oc(_g#4rTwSJYZLF(R)vEZk?zquZ;Q z@Tz8paA{dw02!;0>C$`J$RM$&m)FjO#~s927B8rbv3657Ava%9J)b)9%qBjd3h4Q( zmR7fXc-!dX=n05>|DS$#^u8qXAk%h$&9uV<=WPe8#XopUjkM%Y#7=nUKYX&}$0TPR zU}IaiqS~1~$P$xUH)%I}bkQzoD=#gXUvG<{C>;!lOy}Rcqb#r1XahwS+E6l&7G(wy z=rZ;=AWL2Q^@iLI3bl3a?9|oEO9-(H1Gwm7$-CNCP`rd-KX$4vg%O|!?EL$0DZOfu z<wfBopxZ{1mCZW0w2UfkDOTn}#x83C54LdlH(?_=duHf_OR6dWqJOajco6SeBS zaNa9o=@oxBezCNNS9t4oZp+`KeQisT!B8Xuy78A^EMr)bm)dH;detGd81SFpPsVqEcW#^AviO za}>n@@Y4ReCEW;ad_xmt4&dDB1F_$^y82*8SXe?FWIyK+3eX80fjB~Dzf{%F(KWOo zE}LQvSvo-{1IpZYr#lbv6EM9h+EeIShM+cKAHoE27n?#}I~aP}LECj{GdeQd8V`n5 z9S|HG|J*Z}(QI(u)hjR|f2g{{j5bV%*O%;EFr$V4fA;<~*s|-o4?JIv0CGYma!$-3 z24W-u3Zw>#B1IaKJd`b40;{5{qpDn8rlKSIQ$@SV9sQlI?&#`@j_N2<>MED*a@i6s z*^({Eq&SEWMUe!Efh0i8i6Il2$cZ`6iR|Cn|9dx%?tSm!0pI|U?;j^m?z8rq_S*ZL zv)6v-oI4MU^IRq7!JF0j!@)byrA16&aRKWm1@Fvj#M{Bbn=?<_Ltmh(;bT*C2~OJ@ z9?oM()%<-lPgTdnt(3x9@8Vz%nq^9Nbd$vi<Ks2?TcC9#E` zzx3jaMOzALbI6d|%&>8WA(|ClJy9c_!aQLYQj30|y_DCj zq!v=hG%n(RK{C0eeK_Oyo~VbI@^ncA`~J#xqaQ$wU{u))g&d>ow!eh4Yf#yiE^Vs} z%}+dcZmpT0@~V}!j1{Rw2{A}ip)9sq3F>d$J5mycG?0F{eEfrtL4~Njsd3@8N8~WzFA?py#3^SUW(Ka*jP+YeP>_!KvjIRr-8Q$*hpzYYMFLuuPg%F z){d5M9NGjhq5B`dUV`u4R);k2a@pBXuc{eKOIy3pfm%(AHf(r)>!CWVjJFMP`xWDd zt&Adv#W^lugM5|$A3x9^)wueE!kXr9X`&^}^vg0gtwy>_~8kr1wWBa*Lq?y8DPW?rC3q;|#S#goSeA z8pZ#s*VkyI);asz->xzF=?5zg$Ig~o1@RPtoF-ACfjm!l+k+|$dydWg@iV11YqAyp zwnWaHT6-S4A-=c0FdK!v;4V;obg?{1c1pY$+PT9eXO@n0ZDCr0X|??M*!b2!oS#t` zqlk3}9Dl@06&|C`nf3Q>uiNg^?Z+QZx8EtlBPJq@EseT;OBICLnPL-Bz3*Z{!kdpjP zYyAK2{`$KMgpzGEwwrZ4y^u2WLJ$YyA#=(?lqRTJ*}m0?c%*%g55&Rg05MlhXqT+0 zC4$&n+Mzr=jL{*cDIL){to_`xBNH$!75;2$pFU7)rojZWb9wkygc~2(YSEN4g3~Dz zRh@Z)J`D_S$n)0lmeo$TGzfjJ+P8o=tLF_@ui*RT+ai9_u(*KrlY$?oYs8zq950-C z9?hzxKyQfoZDr0n!PAY-Ba3HRa8?Yz`;LDoGE|bM55N>>A!@@j!8t9+M4zh#u{c3@ z2ybXtrew|v(&j9;b^o)crytrJ4yRnhsxH_?(r|Q>8vE^Qix`9_TR;3t`vd!d_N#&+ z#+FH^seD@3tz_=lZDpTY8+v6Ije+2%RaMYZRRl8ujci1^9wLykz3rKUl}^AIJ^-Kb@&nA=xTQs%8aCE}@! zjVfgnaG1R+{Asl^NDuIE#uhOUZBCJ4&$AJBQknR@%re zC3N1hsZPcx57c7WYwZ`=w1O0W?%G;=oke-*WveYK6GZviuKJ+36z<bUj{7q`<=40!?bk-g=Kjz~ic zY2sl}&}(V3LlMAM(Q8XHFCMNHz>(2Y)%N#nt#Yj?yi#l;oR-GDbit#0>g_Q65C&E8 zN~aLVjExBebY=VWtn!^(D-C|-SZRCiP^~Qg>a8>X;h9pX>ct}?J*9B`J{LBv&_+;; zuJ*|3x<0&mqSRso8SF(T{|-PoAqUvNNH~ zVmSG)HVx;Vu;?jcW8>fqP0Rsw&YYRL08rn z%|CF}=;dPORMD%};Nb&vL@2>oL#|yh_P2&_T3ruG{nCwfIa<23th*Qzv0+=fq{hFt zP?)w5RLH4KIi5fheqsE!V6Jlc27grL&a2b8?X3UbzBTi)8)|v3R@mDZi%xrSwx{`n z*VPk7^#!Ze1@drgU{FOTK|QF+LVDM>df)Rtd3D+;*R58Sc9<@Su>Rj#JR`QXi ztJjrdIeoC{4z~84s5l4?*|bp=o%Y~Rxn93*PfR5OoCgef?K(q_DqDJ)9$+O4?!W}8 zh@~{c@^M#AG-ee>%Us_#HiLFkw=FgsE)pjsG z-MNfCpd@Vx>ae}|96_Gs#ZTq53mm#phUiB5!g#jURq-}#7!Yk_Q~RY0_L ze?kF9Gg@Ya&g~)-n4pMZJak5EY+s=349Cyb?`F<}9*Riy)vNAgM^>+f^c4DGJ|+k# z*oJNdh-@tc)7Gc45T0q|?Zt(F{-fiC$Y3sWp-`n;xAi1Z`>u+z4A!(b$|(fHOuOV%amIW|a+vf0 zzZ@eG$gk-+tks#9M_C+n9y*DngX}S=LR>+ATjqm^aPO*?z<~4v|EP^ke-~sl(WvIM z0dLE~oC2S-NwZBBzxaF=(jp8*K0jV1p+r)YSfNx{+Vch9Jy7YTs$aXky1P3NDViNV zb+EEW&Ln9}kR@i%94aXyQKbR>-8$CA2ROsAJGYf@GF!QKy*tzV(LkG4RJdBwOAo=K zk26NK6^gc40P%-oA8FrK=LrVp^uAUkfWL8X^^W|Xl;)?GV~Z6`Tw+DcBbz0U*LphF zUhMjh-d(jw)wK3(l)zRuwRhh>a8*4pLgBR=>ct{$spgMCLg=ZEGvB*Bs_>x)g{2Hc=`)Etm$6afPmI z?@~a7+pN52>!`ojO;`x3igI{lPd#UJv8{eqW7|jN_^GgBSuH3iCvNz)C7~91hxyEZ z+`yw#0zNnvsO#HHTgBgO;d!NfWuJ$L;zl;w6l^aYsmG=WCT%07U4QGATJ_+${mJv? ze7#DxWaf7tFNRB(HwRW#(Aed}P3^;Q3iQMcCa{Tswm8gcl*SU5dmHN7)&0!1f=fUN z&bCxif(G`|_7%C*I)8ykEz&ieE~heZnw6Q3KUZH~FaJ`mE5E$61t;h174@K*jusZ; z^*-R~N?BF)?zSQ3aYBMbO7l!D#_{L2&GnHc=5$Ja1uy5m6HQq|>Msqai%46BN^R|| zkCU!K-`G96K)HSN^!;i;t$Eq(usuCjcHm4+GBb zSO<|fPn(|2&pPKrqGkoL)iL~yyi}z&4G)y_8-<}@bIOFF^Q4W?^$!b7D0fPTz1)H5 z0uqR)PV673g8oOcSz)w0DS`>j(}n_cCN5i1=g0(4x-U>Qtzk$Rwscs?zH_G2Cm#L^ zhEhYIxO2IR&Wbl&z3Quh)DBm#=7;O*gyy+cucjUDn2QecR4rl=KTs?#U@hW@jqlg> z?rrLMv*K^aKhM>_qcbdoc^cCqU61`lZKsE4I3?w38^A9!>v2CWmt=gsyNn zml`5OJiXit{cz{lrJ;wtHiy&Dc2zaqZz5^JkaJir&hg|pbbIA&(CK|v)a~|t3gNUV z^f_OMy~>m;b5>Jxz>v82T#R%3y+Pa)z_(!HH;&O9MTK=D2Y_3h~6xOi43= zHet250aXrxI79R+Kt9Mda{GIu323Z<$))UztO>o^wQWj-nKSIthi;Ds(|QD# zwm%Ugp1m-74vQ64ak%uiuVtaHTt0d_$zf`RBL0NGa*6RXP-?RTkhLmq$lNH2Dz1zg z-ZCrvHVK|%8CO<1;;n=#wkp(Z%p|2p!BELVORXK4wZ7I~i4xePf50g5_g_`}=&{!* z$b%BGqK~QxO-jL5ezw=*XfwYweV00FpqK+Ter8lqh`$=xR(Z5nfo!$6t@zl}20V|6 zM2Q=$$15lYIN4UdHnyxDPM)h(PQ)xUly5C@B^Yz(E>!**9~A!R>Ebk0K)QI(j+P-O z6Kio>t+Xrz#a1^+K;Xl5!9LR$|66x#5Uk88oU=8*A zZmf5g%7GTf|K;I&Kq+U3OCKew(gYM%Ad3hFVH%YVeNM)5w%(n@j7?iA4p-KDxbW)n z`Vg3g5X`!jDpI}UHm<77_P_aT)lz8tjeCoM+d^T4L@n_OW>bFC#`2SsS-t`M=)d09 zTkqUfYd!HKBb-7gP8!=tDb2Xp)Cwv5+fFwod~bAKMZ7xS_R^v(DsYhS7zI<%y{^avc0m_LA=s%oe1CDQ&o@dsdE+s z=P~rFpDKe>w4*1V0*oqMeCk{gMNB-|b+OX3fEgtrvOj*dmhEauu?@(?=aS`he%V~i z!yG-T>e9WV7|y*`vM7eZ_3JAX;iR9rFgxr`cKExG*N5Ti8rv?sxg_!m1G}(hWi904 z)C7AQJ-J!n4<&)ZuMj~ ztz2v&<|Yhv>}fA-c61Z7!Z4T>T`IP^1}B0m-d|dnYxlvSm7#1WPNP>%6edJ+IK6t+ zkzO9c;asa%ZwNm3)xi1vpTHZgi*+nq=OU(&@wSK`CKea47O{x$4<;^NW_5GjgW(OY zbPKsf;mLeuE=0*t&-vGC?$|Mxl@NPXXS1Ky`Gz1OgUzA|bR``Qhm)RRm3c{~&Or+J z)9`+nLiYK*S0|=mi;3Z+2Rd6vm{U-T-v%tEDz!twUI@R#)J9gJGdPUrp+jCpNfH%K z^Ve>7<+94{!zmOXDFT$PL44>mIWCdYqdBg!M8!T+YORObT$u7o=VktDTFq_iH#mWr(Z*W2qFpwNrK+L^72~na%k!g3epn%5H=$AX+;jN zT|X*^?ea@2>B2x^tY^A!OY{(TAFG^DqeLl$+kna5R2H7 z1;q-YR;LVtX1mv6OZe$2A6h1c0cT>8f+~Auh=%x&XiF}t_Vz8K&#Ng*Ig%FAuQo|? zj5;i|CArh*YbhiQs@~v#fmJ54_pO; z*IwT2w%0Z>Z(cR}7E*~U8F?$=zyHok2&HqMf3}%ERc`ave(&+((S*Nvxn5?Z2gUs_NE1c(NKrFyrc{uBw3i?i2M=m{FlB zCrcP^-CXN9)j`Hadn-^(fPRIay}E^QNm=9o9&I<69sZv`P|YD2RX}sa%6|A(ZEBfEiTwFSfNIwd`t3s@ANmA)%XGPwv<<63iH|7i8@cTPE_r zSH2}*QEF!dC+!&m4abavO|2$4wGp*F;mwP6dO=yA8sQf%)}=4cq!d~Vc|{x>4OtM+ zSx?W}m7_tfMU)ifl3GjEJ2!7EyVtF+)hql$?eJn#8H+Bh_-Qsu^Li;dP8~#>Dn68} z?NX@~7*OaLoltF%7|ei&9$O-eXJ`AXK!!j(v`5eB3^;Afty)=yNA}h_l$e8?#foKh zPtl=iczhrp{Th-^1VpEvGJ3huPGoAi8Y0e&F=6>!dpHQva43b%?R(nOL#_{!bKSYSkHaOlZ|{ynktZLsv=@f6=S$lIFILln zeJ5&VzxZ3gflmBDup?Gf#UjKyq(%AEmvWTQrnXPm44yPUBH4G(v+sui9-9b@o_!Zl zh#uwmkS!PC_~sNd!1@#Y!K>{hwv?J39Y_;1HdzV8fY) zh}dv#=!L_hZ>WbSQrY(c!TfI3uJ+OQU>?&jf9(V&INy})G={QoN6%jgyvIO&NTFuqm^B#%4Efsaz7j*wo(PXc0x9V=~+~V+Q;{g ze%%S1!>$Ta?!-(kAqu4AQE~kDrk7<_JXXBqJgT+M`I-nq%K=NI*d3< z*vJy;Gh+$Z?OWQLHEnSRDtm12Ljg`uMI3r7G|-f~^tb#fu4ZP82o9M}Z$`vnbei8w zT;mKu+o^Mvv0S^MvM)J9+J#^8qnV_X0q1KM>j&VZzHv3?Sm8r5rLaLhs)7(f#SQVD`l_J`(<=HEO-)&!Tk7?Te_x5&S_z?kdeOr~&a3&twU9I@P z{ld(@c&yeJGzcZn1aVRu`|1_SU0Z8UR&Ffuriu@*94kFM{KXr}ii=5Nh|`{u6w=ZC zwJfrnm!3({)rt%@dhnNT?SUOZc&(K&VYq5td7IzqV~%xfnxBu~ zh4QZ-Z)?_uUt1f(Af9aMlQE-Hm?W#LRzwPj$ijuc|5{i1lVod$v(IJ`)FV+p%u+&`?Eee6l!C zMfjS_Yr*m0$)aciK~ijm6OnHYUpic^pr0*05_YV$LjRCfB+Vdx&K#y;AvNSiy3LSBDBO!LfAP9I+Y*lE#6p6=4 zfmus8udb`aiuOYpq_EYKT&G44J<-6Z(T>yER3UAeAK6o9kb)#XkLghhZU=EmD_B7! zOT(96m^pv33g{TVvAdqRyIznlt$rzQ+vvUUTJ2fK77DxZv;1$%`qlP!tpKvdKpMX9 zcpcVDwN0C`m-wXl6avRqF0V)SB0?>=5M8IE+f^MM5B^>oX8^@%Vs8vTGx+64qX;Tc z<6FR*m30P15Z&myVZ-Qi{$QU!!#JU`VhC*A)z;m5OZ6$x5TbpckTT*#t>9?5Q-dcx zdeHf)O(;Uq928q9^*5ODeLwWDb*Rn#7gCk|1ZPa6*o+xB=oQOGOSi#LwmaVo9n=z! zxd}*cW?j7kN}_5)TgT8)=bV+U@`;;-iOV1aR7#3F*l1*~@#7FVy}9d+@V$C>$WKR=+G(@b?Es7TM(j+R3<(1qbpIEPz8O)rfSGzIvcOdbOEt@5>D;S zl0=B`omXoTPXlyf&<_>5^Ar9YKl}Qp8PFx|DfE3u_cqXk;b9!;O-^ALs9t5}>gFCn z`xH_e3WNtSi$S1&GiSw+`8QzDq9dp4&(}dHP(BP&U-NIWLPtFAnaanO!+I3Iy={RG zq4xq}CZ>s=$$A!>&}~I*Nk}LTD-CEfZ|0V_NJcPDH=dy739xn&yUQ3J-1_&lvGoXDA<;? zkKia^WR&3vo>=_gcrrN@Bv$kvu!r z#z{6xNhNM+FL8992o(A+-&9K&Eb4kZ3OA0Q-N3AHZwuh+6~)X$+IXVtA8kJo;84)2 zqJ3=T@qIO(>o@7Rw4~l5)Gm=scbt2z5`KNb$_wo+zrT2S=I`HAt&3NnDm@4?F{5b1 zsL>0s@1@M`K?-l3*A<2+);L=9GG8dKccsB9FT+U^Ez%Ku8_nz@V9QMhZmg4q%frI{lsLb z%(aCcjpBLr?5R2hFdHP*((!{uLImj8K5=h}&`HA`TVC%MwaSQi=ob)!fkJdZafmJa zH0YEp<;Z6BTB>R|jh|^ss5MG0JO#25;wa!uIW-X`07yzcUOh?%-CJ}q%t@y?cof!MT7woM z0%3-Dk}{$|#Uf5sC<;75%%Prj<;lZALs<;iSXAo{uJ;M13N$Lt05*YgVyf+KSh_)dmy)c1@ceZJis&;lv zU|Mh!>k(KP~kjqN6xHx;h3ih^RHeld zZ*@bqP7iT-kDnjmhgn5TJ%`gZ1#>KSz0BB#m=HI$a%eN>GacvNE$w%0u#x@qm+DXS z9TSa;s-Z#*rs?(*RzevPCB2yfo<3jGh@s{nGjc>#uyJ*{H-swFf_&pV&Fo4Ph7^0V zD_a^Hx*2dBKv4+dVd46R$kN>y8BFaTs?s!YwF$zQhH zQxfr$;E8kfhz*_mzk5fu`jXagCNNYE*}_6lnJgq(HP}>1Bwjf>T0hdRn)f#om}y{T z>*`AECGDp#)Dj#0Q6)#BLE1*iS6-~Z_U~sAOm4tWWmJ3fetV8#h^+()>Z?6%TqTl_ ze&wFjzdCxIN5=m6zFJcd#%L>F z+aK^vF(pe}q8yzpokXhd+5!qjLH?KDn)%P}F6;GHS9>i^TX=#2C;j;f6^aSJ#`uyAp}>gs3BwqEZwD(2I=L7Rdv2= z@68*lIaKlgYr85S;;_3t9D!NW_^e$?*N4uqCI5Ff5w1J8)>(k^iS}EHQE1C*jzOrs z`LGo0+G{Te@VRG;S*x#Xk8FJP<*H3&zP_uviDnEL4CudcZ|xm&Ni>KUgv!78uKI${ z$@TzIwIz=QwbRF*Ih;~`o?uq%(59z;`KIE*oX4^>liHJK>!DFZqxZ!ljlXSFeQD?D z$uEc8NHpHJy{gKeto5{~IpW3*8|wWj4Uc2h_1~-WLjI4l`s>QN`r%Y-dt^^NBSsax zcYF03{;X!w!1VtuVQkulAV@@FaRT zH?OWv+;w#H{v$dbRaDvj&3Ef-bl6U`*Y1K06-_|D@LKI{>F$*2(Q0gnSG3+fXCniGB5elEc`2`A9tnr-9B#ms10e5+WFAp!2}Vx|0UizK9nM5(+(0Q7LJPu?yr405dpleI4^C~7(u^7)?Tj+Gp z%J;L-G0^M+0uG6z)67H7-!gvZ5x($z4WVGgvYC6fmsgl=K??Q;l3P_4o?Kcx!w`wK zs|-564nd)pA)j#=B2U2exRFhix>WxSzRrNT^K`b3q4l|d zAv44IG1Na%=vF#?0(lf}uG%3M!UMCmYnr&g)hlPy@Kl-r&wDM*71B4mdIfJ@;)jhN z=tF0V_~Br20c#O|l`ye-wUB$*4;pXbCuxUxQ%&eU%hC~Xd$*!nx6G&NO&OYtbAq13 zH`JWzP89|Ziv^huP7twWirmpn*iV1rO55|JNTEQc6j{9*%61?||8Vk=vd!dv$Q+`r ztKwcX^eI=|NG)WJ2#DCSs3ey&TmAA2RldIcY8AGww$IhNp6(B}%>OEL8&AHWb4G(L zg-+kHW;83!6y({1qbFG$QDW=U^vt}YL^xe_&U!OtI9*>qzVyH z&X!;6Porcq2U}#6FJ7z%yW|r1rHeQXstqlFleyXB)Tsab?y@53EhpPU0n%yto#{sp5lc~~;8)yEzKdYQ7S!+P*!QIt6EG7X{AeHpeo~_W8K{lvh zzkO>xm*?#M zPv2AYtXQ2B;SE*RztVF4Xn3WpS5(jHBelt0ue!XJHu|arsKTtAc#=xGh}obu6b*or z=W6Wrg#d+wG|uZ9-7OBu;nfOe6v}u%a{bJc`zrw`i+LEWU6*iPeo3Y7dybb9u}us~ zaS5~qe+8%Y;{z`gb11P(gY(FqtV2A1+LkS7NZ;`}wb3a`2KhI3S9$B2Vi1|0b^OV* zqZetNgF6qGp`31RTd$rdA22fUPamlkDbq|tf|dc3y(*WBKYMY+;5hyZk+Q-!+qd>b ziT;Q8jDqQ0D7GE7oaEw@)iG7twPa~^1aXH6{)eus!i$GTk$mn@4S1Kn%g5uEtP@{f zyAb8!o%oo<*6E*BzJzhCB zI;nMp(L$91D~8S8+slDy7I{V!f3?aNUaOM=Gs@xd@T-B*7Ix!O)wVwyp#S@)E0{F~ z+T|gty?#bx+dd90Rv7NK6*pVMpiDXN9Py?;zSa+9L9}H zm({!53dCP%YuTDW58yXGg@t}O-ru-obV;YgdFp=m;*nZ$!($};om<;i)lSu#rXd<7 z_LKm+@%GT}5*Mqu$|z^z2~KJ;=OaW4-`G`8S?*X@bashoJ6oGti12^m`O3-TDo~YP z;Hu`+36ul6R=oeJVy4-7ar|suLt-6bLa3d~8jHBD1&z15(pai%TDxE5wgt-kAs7uE zDAcOWcu2oTwhP{|wPKZ#!?C=*m~MhFr|{nGwVkjIZI%t_brv!z}zU5hdLXMCc1%owG9pC_-1G^i+E_crZumZaZYoZLzO&8CpYC z3dH+ZQWZ~}^=U}w?3nfhCRVRz<$<~Avl55E@P<6|z@#X1u#Ply!k=l%1l*Tr{nP$% zeQFvLwQr{(f90_7)vGhJ{m|qh-U(P-z*@v#VZ0MAxNm3m3f^WDB5wbNdZrmB?uhf> zAm_otO{Xw4lsVG`)K2K^hh#G+>+`CVC+2fQ@y-d|bf*kCXC<0zQXWB*tt>?;+Z6h2 z1J6(jMS>(3g3#?vywD{=7so%AWG;e7AnM$)A#w7(?_`~2(3PglL7L$!|6(k{DBnCz-@~V@p43t)YD=9g$kYK!%LJGLu$Im7MLiE%o88-l(K! znC(=N8Y z*&aKh3SEJf%0I`yxM$Ba6KdtUGP)W|8){u(>5}>c{xhw+sS6L0IL2+|4_sYM$Qx?a zwDB4=_nta1T90s8p0p+>2zgxxJ%T9=3Aa)P7PtbDFT;Li5q5)B7VRu)&;dwXI_F7eAZ6&)Pn z%(>5s=;@;bnXC9Wtg7$Xvz1Z7C3P941f`34Z&3s?ENO_h)$gY(mEFF!76N|rp_)mH z&&S$RNVKV~mF^Z{TRtuStyim--B>}Q@2) zKXFd}&0A_^0hmUtShbaikGHD?A|A3IytX1^oIJX>oaedyO&jZt;NxfOw*qm-1SP9h zRwM;5-JKIt0mpau*AuvWsKe$49)h&}{B;#K8i2#bp|)qO>Z;32Hqom;NVIYq#Vb%? zk?a?D*2lsVob)NE`pn~_hsF4L?fNqJ?7`}Y=;0oO86~GfvesN_9rDxu^o6oz$Z<8N zRh|hN4V&?0oGJ`@%JtJYi3kRRtwvz`wMU2^QW1}=L++7%Z~M?b*;;LHtpl8tJ5*r+ z|I$rOr1c%~Y=#rH8uF1F%1O0}hCL;W2IWa>FIjXB^nyKC74U>VYaEa}0&VtdE}6Ny z9a548;x{1E-tLb9>^#!x@kCXgyvBCr<+a*=vaO+zbrOXk7Z7k3D{u_a70ex~w?g+7 zL%W&AoSSt<8iqJYr#(aj9&StFX;&B^RWBW`*xs_S{%oW93`I6dx;9&up1TztH1f6u z**^4XP<#89(cikVh4a%7*5!qMZ3*3(o*`ia`W50pcLUNcr%4uzZ4*l#nA2XItP2hd zWkufoF^dJELpKzEatNIv7#=F;(D^bev^sGPrXK+dx=xJ}=bU9QVS0$PF58gp^x%WV zy$Qj@VTS=QL7!N?f-p>r&#JIv*43+dFuid0lYqJUWqvHKUcFF!+bLp*V9*UUINX6$tOf1CE8}bZmO#)2prY8rPSWK&O7|M{p7iKlxIn@rQI55qR zB8M@v=~bDZfy4YYZ>TNh2_?Yjd)g|`M3uuXh^MzvA)Mx6W|l$s!9JU?n858=H=1#t zxlj{y-kMgXY2in#%v#i}eTRD97CYx~?JJd61k)t|Y?;+Jh=MWp1Xw z=#2JUx{F}w5#Vqc9lvx5(biWM$R_#uAO51!OKO!d5V2RrR!vw^UopS4{lZ-u@@u!( z=yfRnjKxcq*J6`HweoO#$|<3rIo1g#jH>9=a1O*JTNWMA@7P*II@NAkHCpab%M-E; z#q-Rpl=HxpDquYh6KBqzPNs>gLLtsBXRO9aG3Li^a>mOr#r zr1~z{vQiE_dC#^I!Ea9lg;W`8OAqj=2g?Vo<|nt+#zWP+k5=9#bi{wVJ&KYb{L@G4 zEf~G<#hsNlV@~tcax!gH{>H9RqY_eVH*KsSA3s~~(>YX2s`g829qLQZmwPtmxmMDD7HEyzE`qg;8K|7mac5u z*Hl9;T&zV6=^3Mp@m8VY+@7zY%Hd6$YTY>u_iV4+EH8Xv(+YHYc#By`^$sH0_in4M z{K`wkz$@M+h8%EcpE59-s`_&IWtELzv8payAGyB#r&hQW)pLCzfkU_M~FMGzxaF=c)N4_J23PsP*~&tiqUcs@P{mg`Xybj z7K~n(px^#>v7yk0hZ5H!C+1a`kKWO~wmps-Z;1nDoUWNppA0bAcYMSU|9g`o0w>Pa zLb+O$RB`N@%PZ!H@k$jsV8~u`QiVt4zp=agjH^M+B`h7W+tmgJ_R%xNY{Q@`VF^$! zTV98xq7%Ytv_xTN?m8h=klHQ_DHxp5Gpur`oWm689P(rj?()jhRrV=#I-V?P&9(eA zL>%blG(o|rGyW&U4ZqjgJHi3BjjL)ujQLz3)wiM1zOM1&_!kd5I?n_qH2ethPFTJA z;fLa#5cx%XAF;TAwTK^2%zvr+zFazKcvE^h=Uh??(PqLrMqeb8P17 z^`#`}nui*cMXArdhQOKTBmoE_-m4&j(ze4*Nf>%z$O#VFG8>k+CPDKo2J%rd^z@Omdt>*if`RO|y4^*bk#2sRe&)+)_*7-R%eRSj6Bkg($=jJ!z2y%pv`8EKfZscb?TN zD;1}K9)}Xz9cwGOmF*{4Wt}Z80-2I2HR6Y^tKso;*S1ErDhjcwveAFw#E6K&Ca@LN zmUYJ&8`wvRX;9FRGM5i*wdJsH?5c+Xcz$X7F~sE9v_%ylD&(O&A_S2br6;OvagVF^ zTDSXXt*`w*zfxbWk>n>+4V&K47qxtoUoLBL1`3%zb-t|BvqG&=Y)MKn>_1r#BBcvz zAl*3g|N8Tp|LUhoT#x@x->51MLl!&!(TA!#e&(8rX9k|y|p5=_w3P?r($e zbbagU-J7bGGonGH_mK!FsoK{POk1a*R?j|luyhi{&#sZ6DiKzAcu%dEgN8e0>Ltqd9L>_D z)plw*bH07_9bG_Z_#3y(ynM8_8{4;sE?lhp%SWyswTs&L&r|%)yANDd3QwP#dFg1; zji{Uj6`a#`1xS;5OM|x9Q?WeSA?-rX z6U5PjL0=g8342VZrGd-Hq4vWF5S}1D08S&2paF*HirTH)h(u>7$`Nr!U(?p?Iwyp4 zR-lIy&YZ75iRnsyPKYQ^98N%}xi@XdA^obRMS@OOi1_E99|fd4*3l_LdM_(XtR(>v zSNrfpJmq;}G8bm$&%`DW1U%izY5)7-MAbCs0uhTpIOKu89|V4&ZZF~|0gDS*i}*3Y ztT%vJFYY)qKbRG7>PE6~i^H4cT>9s^p?0q(7|b(*(C2qvRezj2haD68CzK2i&DHwM zsY5v$vQO2J68>%_cy7E75~Ace-!~mHrz(4x(9qF2$z~co0fODr$vp+WT6!|Aq4Ld2fGsn)>t4zc-Fp*!my#72+j})lP zwJB^8T|9DBsO+c>3`Cm@2WN(#s{UdWV)Ep2udN4Nv1;bC&s2zH)F2lXOh~^2It{6f zeo+E$Bqh6K`6zmye4yH%bjTrxV^PDi1c1mc4R}_!k1Np-C+V-SvmB=@MslD)8N&SPLs!vv{jb?P7$Qgd2eSZg1g-9E66e3UMic#k^{H z?QJN6$vsxEm8;{g4$)RIY_GqO7mB}gYgKW~-uCrfRjz^8REZmYoXVtV)2OtIbwfzI z@M!GKo65?;JSVuM9%VAxIZ(c}btv@u7EfEvk$m3TekA|{aeJz^w*0VvON$k5sBIb> za|;ws&}*6?utadusjByKS!|0_Pamj9ejw4H&=pJ&=*MXTJ@Vz9qcsJp!u*w&Y9S_X zl|nVYog)OL;u~7i+%G8ST?KQznxLGQ*Ib=Q0sVJuojH2CUew~DhNo$-T`Yz?tOOdO zkZ_T|etlht@?gt~WhG*xoSb-`g5fBn+sc&!btU0=^wiMiHMSQsxkm3!%h_V^ytr1qbj`NOB$ z$<@wx0msJqT}MkW7c2UWL+2Ec2VSUG5nbQ72l|4-Y|3Qs+*XT9nnUMkFf=`6<3>Hm z(-v!}t+upI7ZVO>rbclabDU4R)?HFhS`m+u5L0$rcPR__pBN@Glt-I=YX9Qp(QCY+ z#5&|mOo0!2kn)GyPoRMEH+EI?snUI9jel_?qAl7;bM;FL0=qlb)swTFjP<*=&HRsF zuQfG#Sk(3Z;ND_Wb@k;VpH;;W;8CD<^Qv0?bf!>+V$%y@wqYCoT`(;|?|5)`Ej7x?oF8vla3Pp0O7r04xw=X(Vi7+CEG}Rz;>QHDUdI=@QAWqUXHI4^ zG%*Q)?nd?#UUgLZ(QdaxhLq`aC;ro&qnsqQCbfO$1aq|-hF>}xIz!w>>b3S6g8ZT% zLwFR-%xWsuDA50wmC11;sVWv@+9q>L6*wGzbz*4x)Ogi{pberSwO?)D0m&f?mEe#*b@=8;cX2vUrmG+D|Z9q?m5K8g4 z-<6|Z{$NiQ8&BlehD7BpS5&r%=hn?t#epPmOzdjk*`Wm82E9eKGSV^-3D`gUZ0!-r zhEozQnTa;~vjMe${^*|SS(#pV(wm%@VaCaqj*Q-%k^tw2?UkQ7ICE1=(hQpykJKX= zL}-w*+T)3So>Uw*RLYUWOzU_UXY85C(uUx~G;vbOl#>l{!>Xb?pdkgV{=~hlneEvn z>FBp!t?Zf=ASiU=%nAxCMvJ^L-A5rJi;e=2>3hm^IzKaB`r&YOYkL(mI*C*05VT9xvZeK&U=G#t^S}RUtvue` zUX&wFvjLeZ3hBA;#&TG){p5jigSkAUrW^)M5H6QK{lZp)AZ{)-I>Lnu^NDkni(g^D|eMV?c8`aARF4stJbl>6sNvM_&qa=t{WzSjlFf+L+)2 z)O&*C@z0ZAY~S9lPHC&6U)weFU)-0MOtO?P)B~dLSajMFe9yKbe(Z*Nq6eGaEDu}TIT@XCm z^F+xAQLC}E)ktX0RBgK;pr|eJc3s`EwwC5|x*3c2 zKLA9b{bPGavzHuZ*@!BWI(DN!6tXERm_LM)SV0^P!vvzlcHrb_bqqRZ*Aq(iaib%q zvxlKqDFhDn zn{MO6@pfdV5mj$W=l5mx>igS@Mf|v8aRF-)e>LzUxuTgCydk<{)9%gX@C1FjX*ryW z4zoVi8~$u5Piue)qQgGh$YGz~nDgcS+A*ySX0W= z8hVus3NUA%O>Njf!QtA$_c*eJEyxE%au`9i&>1r81(YZRQ%1Brvv730G^VPim~Ev4 z=j)X5-%h*T6e0zbynN*KIeKKX#kXi+F;bbWszOZYWETqb;89jcic)qr)asJ7Cl08h z%|5D*o*6y!6`i|}*2_wH7cIMOG00w0o#xn==dB-YfdU?acWfCs1I(esoB~;U zCNT4P+uC|2LaMKp&U_6A13y3BRs)NF{M~VTY9HD?^D8&a{F6sUPV$*58ln(U?z-oA z@zeS2!OGax;>>Gn-`!uGr8!s;6F2>(ceDuQOFL^+}X zy!UitTSK9$x~xSY4N0gEwQn|Evb^lxv!mY1d|$rt)Vv~9qqJ$rV|TVhTdg=~?|tp{ zL;R$~JZp|wIFQNHsR|^MI_u!crlEc8yvD$e`g+vKbM^Y0wm8d-z-a!@?r!02`KLp~ zL+!P!_g__>)L?FxS)K?TAJXIt7u%vnTaJ14L=n?W;vgX3+BU^DO9XKy@~IMOfdR)teg42f4(AoEEU(?r|()tanNzxaHO4YdR}Tv~s4K#UZk z_9`1%4uM6=AwP`3ijDx@{_)eb)Iu%sG>VmKU^Arr;*okfb?77z=MtigHZjTLb#Yh^ zx`>^*Q0q96+PJEo-ol(QTo;peS<8wcH_&VRG^6>#6hF_l;dg!e8oDZ`vDso8(mFS5 z1)+YhT|z4k?aw4mXZvGkYWy4d8^@uh>YDX6DsVErx2>;5l_~|>XCANcayv7Dvfl4R zr>iXrwApI~KR2}{B*5b~8YNr>bY**1(vPV!Y>YafaQoV-LU$T|@Y*6CJ6pFHCL$gv zWaByhMEjOauU9QwS~sZ^XKS=Og!q|STfH(r_NN$@)W_5e9;dCVLE32R_i@C8v6vgO zdIhm=UomNd!C%rp4k-vG;sj@pJmgt^L|vbU)1#z&6?6@q0#UVQHX`8TWc?Z@!#h`ZDEj9?Kz z-dJ3~TEt%s{2;E&Zw=-^LuVH5*j$%&nS*J^G%$aCSLM&sP&KWsAF47xfLYB=OARU0 zN`{!V38AQxIdqW;&OBG#rx(+DI;GRi4*LuSvDc=M+I5$XLN||N9pUMmrdB4zfhp#2 zwlvhH+#8kISZaqevrudkKI4%ObSn|5s5GmYU7pRU7e zTN*R;M^%8|3_Ek*fRbL0PKTkyNwePWZKo#-y}Esv)>P8O)M4BJ9%9n`Y@SVwor*8O+X zAa0Ag)f|Y3GNY=T zi3+w)K2Y4~i38yLbbAGolRz^;U^mgoJ6U2?n#YejDI3YX$qL3#)@4{%g^eEcwDidX zmGGx^kjG)jVlH#zmJ-au1W1n`c(D>EQmSlvzZ_^^;8ytg_OY$9a%q(@XekOMYWJNe z<-l&#R;TP9Dd#8t`aDXD#=AF{hzL7te)YCGzZgwav#z9U@E|HtMfN@0M(bbHMz$%g zHKcC0J@pT26e9D}G)zux-g zm1Fh1hQ4F#$eG%U3HqNrTY``6t@9Lro*FU1 zwc_O2VgO0wx=ZR>+TByjAC-wP&DX9k>lmIsP=kaaZmR+&j5IBUP(asz;Hr8l%sDG= zOPADhnnJ|%;Zv3KOE{IqoM5`rq0Onsx~{QTr8$~>Py1;?rzhay8&CKEPaUkcz#eM;HC4_CM92K!eyH?l`)Ar)nOcOC4`X8=P zO>Lb{D>D3IG;RayI#d}w!J%`-(wWaaR~Jm)ZeCrhIWFv4m&n*(-rg28ypUHmSr>GdV^*b9?HlR>=;gzFB=T#yA7`RQ(o!hGI4qZ;4I#3ka3o$*hHgzI9+FMh*=}Ev6n)&)FyPwIW(}jXKzTOn8Vm7IHh4= zF{cKEA~@HquX5sVi&RISMxw`0pfkbmF&PRD_rn52FzDh{cD+4Se+vw;uU>Wi7`mPW zp_>baEMKD|l3l`!7&wDfHfy;7#PxCDxru*vE5c9O|&0_vo{Xp@f zvKuU}Uj5`?aRF-)i+FQ*%U9>Q1ZQ2n3T9os3Z}L7J60wQL&_UMj$wK<tdm0$W(j5EfSEg;a@#L0G z^{@s8N;0MX_fORu9BMm-VBl~V3Wt&e4ACa*!1=4U)o<8|LyV>>1&Dac^mlO%r)g!e zy@iqHD}3SkQmYD^gzB;t)oMJ>O0pPI3$^Ye34d2xm20Ur28rUKmaOWaU%Ih!ttSuE z62{kejVvZHv%hD1iP!AErP(&Mlvlf2UTx2=Luze#+Cs*1<#y*sx5WPN(3YTMH{X?r|T#$(LuQ=DWSf~@kNKURApv7#UXfAVal+2Bw@%MdGa zeu5d*VY3mj38Vc3FO(0egedeR)jY>38dtU-)8xR`HI>irJY2P`U%XhH>Xx*{7aH(X zN3_TgG0vIBlSh{DMDt%8zaxe%7RjO*$&c+V%^@1z^)q?xGeFdio)2p820EladVcMW znScFwMTT9}FSg$<{4d{E%bEvHloiZk_@(Eo@X;Gay+i!VFVu@dv4zc^plhRj4(;^< zDQ?@ZTUo0)=!Atr^Z*0XTiOS+5)EV*y{N6WwC7gp++nhH&25{j;VI)urwbI~SKChq z;*S*)HO$7#d58y3jqf$BrN4eh3I3xGRn?~+tamW;8f+)&$-{2B*WUDA87D zO*>!GOd_{ztlt5+;?jBvRAkWq`ps3vnSCef8%#H}gMiUo0PlCFWSC7AVQ*3(@ObXrGHOvsxI~C+5bu%J$PkSx5daK=`_O-_kIRKbzxbI z?RS2!9u;)C`=y&ooU7m6Ke`mt`O=a4PCRjCwTVN9Q}m=QKYjJ+ZxS{PK^?Z!r+HdU z$cwaLpv?x@6znOCW^u;z$ex)?msJGB+=;0WW>qdwt_JBKltig3jAP(14lA&(pmm*y zCqGAsQt#up(Tm~duA4b{q85M8oG&FzJbkeCrKDZ+6;dYa@{TQDp>$yAg*xNLA1JUH z+pjO-wWkQ<6{LguA;i6G9T`!bVz!fK%sY`(wxJX_OoUU$uQ^8h{u8eYPKca3Uk&W5 zDhXXjXO5~ERmc-V&>74moyZJj_IT>t=*hlmi!f~65TQ@k=5Q{lA#r*kOznxYvo9NU zOsi#U+Vh31Ud`p?Lh#)9@#+Z}+jK5a^INw51A@zM9u=GiZzpI&99et_>n8(?3s{Tz zQR4e`!TLeqZThxw@yZ$0il#O|Y&<_+)t zecCz=(@HaGdho-nbDa*gnOyer5TVC%Aa}b_md4fZ;mWj_j z1eo#k*&Y!aL{$!zDG`A<;4rm7b1w&S651y8WKYSE2*gDonr&*607UJCW<(68?D@NQ z+z!#){X^GPAtHz{90LNF?!%DTZJ*;}j;i?9HS;Hl6AGR%=QFWDk8pO89Hva1?D4bp zjpvjR)pB5C%iB?sP|>;Wk|NSml<~7{_|4a)uL|;BMCr8bHcry{JKw1{3gJ`Vs9b{1 z_^g)`WP+L;=SOJSzMRBVOs>M8EJGmTxp8B)an*{7?SEr*LJlhJK8M( z^WVL#+D-#b=04thOGhwNiFa$-`u6>DIM;sN|M#D4L#r)Mq^GiM1ayAp@rq#$v$m9@ zaOsL#3$7k)9jdIo4jFPg_{jAY_Mf|^){oTU5ynK|51(pJ+nugl^zuvUOgz+_+WW4o=hv>^P>Z~q+QW|l z2F)RYpSIrnD8xz8S*qH=Q_D#gw*zeqtJT0P_hZf&0Bp(x?HN+Ib8FoWPPMm9AKP1< zP8{79PefZVDQ86(U2_y5cIgpA1^g_Au3#??YPCWewGNN?T)bEX+rg8ydd^{R#dAH6 zKWuS;pLqlfCsmiUk9?hJFR-J4s)}>##GnRttJU5J0#)3DE~ppOI>ZcJoRze98l|%} zcK?ad3YxNgFO=u){a*~jzd`l_q>#tGfs>?$4B#R^T-=F=;h{ViL$F8eEs7f*sze)H zjUyYQ*`_f;2=rmqten$Cl-SM8^9?bf!N{2v z;hB#4v`aq(yji<%xOxQ(S2(}*#nr2y5G*cWE#gOsT!Vg8m*DyFHeFhH(>lX+VJ^JQ zh`r$-Ll&-_CvMeJxCyKqPh7gInG!+1o{)WzMUC*==9-;Em04_`e1vTx8V=1kJH=rp zRef4uLMCm%V4INbNUcqrQs{Fy=u&2`7&3bf!mJ4;XbH~h>9fGP`H`iYm(ZgepX!bm9i`pbJ%IC+QX-E9fbt8tf2*}1t@Wj4a zR){BQFD3N{?Ix8(|h z-?gnMyiyx0I9IjBN3s;&f9LDU<>aIUL0LMgoM@<3{_qXsNp|}|cSfa2neXQJ8RIZ8 zp{z<~s8$>6nX(M{wg}RBc=gRar7iJ66rw)r97^z>I#9VNA1Kt(nh+}}kMEoLe|AiDD8bL_u(!_1MkQ03}EmNUeO zpeRfJ4a=!|H_=ZS-`a0mDf|P;gljQVvve+G$7| zl=9Q{D3i4OxkEGew%*ZMFCMOEl+*%d9xC(UXRoerd&6(sTZ?Y1+E==n(|m5wkB$F{ zFlcy2NcJKl8rWF5?_DF!h=$+cQ?*8U_Cj3=AKE?h_up5m1K8q&=qXq5jllNg*Pi3G zIFd6W0ZbKW?~yYVD4QtJ&l%#`N?}{&qz#%xv0+w4Vc60Z+o86A=()uMxfIQe%?jpi5y&n-ieB}Dk-4miehVI@`fr@)-Gf_UWY_ci?VEKEny#M ze`7##kuflKtgSj zV^xSV8~`Q~Z4|N&$%gKPIdpUZdFo@dhQ*gF& zDP?0XGp_K9t+;`H5rI0t#zD@0LNlNM;<*de-NRZ{oj>Az@PNZ!{!4$|Y3O>Omt7vO z8UH1Wc<8+lo09!L;zwmS_|aNl#5)9w3s^sf_$&APd%>sf=QpGbH>ZJ&0>iYmU{yr~NDFv#jv z1WDP~Gj}b=Lqythtp2Y1=-#T$lzR9STW_x|hW4GPT&t$_E&aQDd*#x~WF3C^`jIVz zIp!QnzycbWMyD2Ya;i~I(!QRPCL_jfR<8hOHgq~;NSRT4z)UUuw3*6_LuRx+;R&0< zryrczwx&M*LP@ejvZ?*tbM?IyLt^E!S~Sf0I0{q?I1_tMj2;z<8^l2&*8OK6dOApl zikMF99#-%-)EBJFtF~x`_=Wb4TV%22New`uXLWmJ@=!~mqvsEvocVig6(CCR63W2GbhO>C>pk>57ZMV@<7TFx2zhSEHd6L zt1Ek_aM#g#_miu@6SbP@oJGNmfuU39>UYgD*jPz_QF!KjeS}`w@G$ak*;J14L>1O( zoX+uvWfkQ(MlA^KE6D zKxIKq!<9FVziF)_1S>IKR6M4k?^Wwi8FIVT0v zB1|W`ea5GP2GXfamNV2Ud}(LN{?2zs&k#|Fzce)8KHANVy>Vx1XzSb&-i4R}4gDk^kDuIfHh@slv;t8evT{FM+P*oA95Mmwd-I|RJ1%mMw z&;Rs|`iVei$4#4xsMemx&I>u|q9=l3uosgQLQmYB%N>W> z%G&X>qnSpj8h>r76dH}=FG;y2IjL#j9?v-ww$$b}2WlM#X##Z0&_lR*v91Gjy6hOY zT{Z>GkT3=cy$sPaT^qrPzZaUrpu0>t<*_k(uKgZglxPu3M?6PQEw-*%AzP|K34h9v zeK0I{;EZ@jU%i6R1wYXXb4>Jyl@Q}$5YzoIg?<=x_d5FYasty|#u&o6JgsVYI8ikh zrrEmMY5j{>#3J4r78kI74Dn<6{N&rY=r81+F%QpMyX8dW4P_UidGGPpS3M}qy?-u5 zVJ3wgLo3SND}{;KhVt-m8l8XGv|f-2CZ)9IiDL}{XF=%#Czw6i0I5~VLbUpZtb*>@;sOF6YYunyA~VSu4U1(VmzDQ%e5 zUOo6zq(k68m%1wG%u!{}PnmxTUUM!BGQh0%_aw5pn1|C;y za+q0%nm?Q`_JlOnXCo< zPUnf0?|+PCemn#Zf=OvqshS2CEm-DCBan*#hD%S_z)6eEYKEH5;gnwL;D;WB+GRM|)=+OqJ(K zNJ|epyge4l*vE5!$7*)5*92m)0FfdPha6AG`0ep!U@IrjG)^WR`OAn1!ZXF$y>zIbL=;ZLei-o>~8 z{mLq0W8i~5bjWVla-YSZ9m?)vT(3x?bYWEfit*6IR* z81krdOUfyKl}k>h$g7}0M~9nwNfdoyebXVY>(ZsXGHSaFsi&1s;8e!0SIR?zV9@QjtFlo;Z?kG*-|)#u?KM!}_R90$z}sV}k3BeSvs} zn1E+Kwef(z$8Grw31kqY0dC*nwkJ|j$|n>D-GD{q^(|DFL(+cOmf-vIs-O4#F+NzY zw~E!u6h>cPFbnh#*I@fyDfFN&vZ_-$kWC=2{ieIg+;m8M)?M_4^n$jEkkJjz(A!=_ zW`vv;NKv*bz*pg$tJ&r z%!WLGSI{`yY~^R4jZhk5MGRBxhd&`ClJBrpPmsm`a@prt8_+PHEmwbD2G|1VtBR0K z7N7aqk^S8Tt4w~Gh<{d=6Dileim%r=#n8*lPCJ=#)<%Jw|=C-d3%sVTVXc7w{(OP8Jya8DIPyL!$1q9dKntz<ga+E>Nr4OS*$L+F(5r8?`Y zRTJMPFsfNx8p_sR@W~A#;UrN~53G>u*4Lzne`Bll|Y{lABa-9w8}-HMa8Q)?>u{ z;{6z8Luyz&5j0D|qTe6=oxUBAMiAx%Xr@?kwt!RM3IPF0xbgKp!@TB!Vv89={_gqxRrG^9S$*qlUmJy2L{WTL={FO12tyiU&5vr%R~t~id4dfzR0!Wl{?mCwFB9*gMWQOlSiLWf3-@+7uqSyg1&?9;iuYcQKolC!bU?<;bJWr39i_DYjnx z%3rUF`se#5`)^ebVI{T-T4_ve;B(p<-m?O@?(hgFX&(QI-VF?debgNOO+i59#~gz!8aA& zRo~Ouz00Xhy&PA|SfxiQg+Sl$bx%69z8T2r0r;|3o99FE4~#SIKUdnr=dE{yRO$~$9@M0Om zVon8LE?Bs9O-$Hy5f8Ns@IGLY3vorW3ffg%exunLcG_L>y0m3P&sIw>F$~ddBteHa zCRj%Zx{tzTmtMydEQ_nskjUmeh&i*_tKVB-GF=6%rx4cVdd;9;4p&I5DP*C*0UfSs zKlxew!WapS-|#GM``+eWza9ySL4m6vmt|KO5E_CKo zVF!pvKdEsrdobql%_FO-WvJNany!w|U2}(+==YggrP;S^d*ojH*~s`wQJ-xw=gmbO zRJjVuEd2}+ALs0BHr|g{n+OwqX?S~D+d6>-W?8BNZ3C3pT<@wY17|mnS>WxL#md`t z3u&?>(U~89^Cbn(7pJ<80_cUpg0M@b6o(fDbah^~fYXJ5n8#BpT}--w^QrsQ*_YPx zWd!3kHYzjw^q|Jq7>lwf2N!j4)5<{NDJMdRI-L2H_&Eyqf}JEQ5c5c(zn|_yaukJ& z>9eAD#p|&WL9>JL*i+xjt?@0&r`-HsgqGiZZVz+)WJF4z@1rVQkI-NF1$(WJb$1(y)BCxl>SiY`4~E;?pMrqb8+gjW&|Oh zU6H!^SCHBLdZtO6tXq@1y?9Zj413fqwlX+#q@)FbJ4MRG7rKKwM&|{0iJ9_0fCvMp z`(IY>lu}Tw3v(QgVhS7IWIoA%|2xml4{#&CRq_v?WRgv-4%ZP=ApO1NHc2Xg z$uUibiSis-U@T^R9$u$qB9nFf2g$(~^H;ywGj!(uyw7ZmtiJI>bLu-Z?)LBU!ifV; z^nsvY*})^kWF^zSr?&elhZ7E~%=OZBGfE?%(PM&s&$q^taizwH&kuV)Q0Um4wm<0P z*pFP+&;(d(u=4?WWd=3v5Wc=Yw?|7ky_CA#T8`U6%ep+ze!X2K>x>hW_V=h)`$xso z{RKiqW6vILoJkc_BI}>4hcCqdv*1<9xzQBTkQu788UR=nzalvBT;Ej_fKZLc&)e&A zf9uGfTe*hzdteXiZ7KM%z8)Tfg}{(j#UIqgD9f4%h@Ws~x=I7-u{9F%6K8KTfQR|6 zkj~htMPtLUue{*tCc$9&zdyTny1&XEXugfQzw#GD(AhDu8GiOKV~=0!d>h$X&fyxW_si^8nk0#80=aDP8#(@)eFb04VYcTjstsVHOoC$H#O4%lT!7qTHU)%@|hQ6-h= zoQVJpF7Ob~8BVYWUghhNa<#}DiNGt`TAs65Z%rbrbD5zVC%&P08Rvz1i1tP-vY|M6 z-==4&Q~auV^weo1h5iSrf6NRY40&~CTl|EV%1}js`fZI*y8qGZ4z8sP zM{N2RTFr@vT6a#ImmG3ewr$VevCAmseX2|i&jDM9VLWCBJg--B5s}*BiLL2`nUHp? z#@H#DD~!GGxIX2mCTX-;D$Qt&Kv~3Aa=m)LX(qX#2{OV8{kc_6YADVxY7`ldVJAwc zhJkNYtZD%-@uRFbdzSZ`E4cyoQwic##TSndBh9@#(UfR|76EN0?3LsEecrr3pZDfW ze*$zaf-j&3MQh#`H@o&285j$Li75*4uQ1Ft;637ATlwM3gL29>>tui#lz(a5gPVGh z-7Z)-K{Duy#`$EkN`!+!Dc$n~;Tcq$ihNi>Fvg-Wpmvv#`&Hz@{sE>J(y5oPCJfKRR0~a4glbaHjyMY;^@~^ zrAg}465krX?B+7xMG1bOSF`{6h(uY2>@GUXb{&`kg{3{W4<#JlRAQd#U zC4V_w-eN9QZK5%08}bntfrH_v=XdG zBx8L9AwOoN=z#74Ym=d>A%hf4fp_MLzKpU;_k;OZEC44lA8p4~d?ie(H8 zOGaMf@;cIAFRJAjKs#z%E{gv-w7To7kbNADaB851JRNij&dQE-Q3>8^?l&NtE_EGb zEY)}-frJ)2H?R*8avlA}exww!?kQhfNkqk)I3~3A@pvJI?>*LuLon~tjPr>2*j@5{76=lWn8GtGnLP8yCc-$%m*`aabL^LW1=tNHQ z;vY3!Umz8i_kAmW$-HE1l0)&^CRFs0)G4y^;Dj&l@F>M46CT*{;w7C88u&yR31cl% zGW9v(Z854q6Aw;FK|bD&$Fc*M~nWHoRn$|Wx=RtIL~|KTKY8XdrMK?w7gU1k7YL0 z)c125J^xsc+sS2zRG*(M_8FNXu58z3Il17}WU;hBFLbOAl z&Qy5Zc_Bo#e~&#&P`AZ-s#(o{vwRf)jk$Wzvon5XIjF#e(-Z8b0NYm!XhraRGbD;& zSiJlpna%4TWW)LwMpFMd^Ia~z)30!7bu+-bHS0w`pSK!pT>*4-yqyrIeP#gSC{`E9 z-!-rP#G8P6{;AhQhM!}T9Dao1SxzLDT!;g$VV;LN2&K)3+`k-?99lBA=_*RTzwkm> zz2+Le%qBB#&3~=d11T6sXx;WYI-(p|?lk?b`Qen&!2SXMn3}bOxo+0lL(~1wRU3qC z*XkB+6Ptzq9IUhl1Dh&f!iG+s?J(Y;D~gf@7TE;3s0=9Sr_)}wh*-hE=vJ8>SPuHr zMZT8mZtJB51_S74U(<<_z(rom`1=+w?|*Q^?i6&X*O8J^d>S$usUKv`Z=bjLfF9%pBRp8_g8N4BFtOe`(v&jhCs}^E&5N|dXz+hE?=J8Bb3UBF z5Vp??>6MvTi8MI6mBYijKIS+ZC#y0%;nUYGd90XHJjJY9oFNkD#k{JY?Xa6vAH`iz z@iF#Z9&ENq_W}||(!CcizQOI#uX~W#OcK{g+J5I3Hwu~hB5gQ7l&7wFhK{1qXLoFo zhf^4Hx`T)~{`clc?6Nv1+LqR}Hj)~w8^LQq_(az^B7#$72Ikgv;^BJyQ1N=EeM4avH7md*eR}oy zVjub5`l3%-ak@ zXlss?>G*$8l05Y1VI7NM#q{U**?VOSt60LmJw;SOZM15B!%ATaN8E zFhFBg6)d)l@pzriyl11D`X2dd-<{?qs{+L#iiO4OEC!shFvN%_?fS{?F8E23z6LD6 zQdw=jm`15?S0o|0)fYBmt4Y13i%B+70uJ0(kxHR&UT~{Cc1!SkKa4g=Dj-o4Wm6f* z4SHk`8kbfCXk{Rm6aGP3l}lk~z#6XVIQ#izwr0F~OrT0T6yAH?leJLuyZKW?W>|H@kcB$i7ZL{w{r3hvE$Pmkx5M~DonrqkYnaqqMTaee`wrUCU@bav zc~!UI=8TB*AGJ@%Xe{|So7L)}@EodV7~h+4`g+mF`;lbTcRk#hdk+Ep&xf>3wR*p{j6yW^I5;L@kN zpRNu$w?8f~$!!l!_AfXF{0G zwdHYee|G;k9f4D3hwOI|Mzhu6?b|4#-UzRypGGNWWM{o!#DFN@1I2!*YtsKxsUe82 z{s%Kb3@8u=dj9fS<@_;!ECDc9+hn-!@hTK?{9Z2em?9gXbn{{<6NKP18gj>AF^>7X ztS(-#*pVkg+xEvul~&@hQ+Fa;RM*;`;8J9m84Lc*Ew!bSn|eJ(+R^a2^YQ7ZXYQ6L z=vuOs#jh%ZTu3GI*WrNxkq*Ckf}Vhs(wDgMr;vBvSQiH|Ek*#{mVQWz31 zKi#FdwzxahTo&PT3-eh}W!42=g^>RDd7BfPl>M5u64B|;r&d@7v(OQrR;_sMCkT-8 z2{JFKVc_TTe1*pKUY1812Bnz;TYS8ZF7b?JGHvKG>DB5f)$pP2Ob`eJ7D=p#X4B?< z{E?Bn=c_YuN6EHnB5b62lh5)pt5Wpsi?KoHAJF4!JCv+m`DG=Bi>k9=6nhU>LtgmA zcHUr48nE^j<9}?AsX1TB7>T04_jia89`}!ioS2F~hT&`~X<1ELFNCrAi2L)(zSh6XpV451~r#U7zaHykMTYxEMj{ppGfk zL$>}NEF4zOb6^AZLT1yVrE*i3JX}y{tSs^r-}uWSAB(+Q$N)o2S56E3w-9APYy{5F^0<(h?>55O5|j*0XjuyV&3&rhZRIR^%m$0S><9FCt7Ak z?_me_S+L2ul5RC;{*`MjY8e>8I@qf<@};Pc2TV^?@JI z;nj$qqL6(tU&!l6;~3Tf!xwRe-LU%S4Zx)L304QYLE>ph$yVEKQ8*HKVE3_~peC== zX21JcaS+c<-*5;O4)4y0zNGFYNs;8!Vd1dh{(t>a?PEyO<5qssPQ>Mae57>~0@Sws z6X_Y`vQVg>MtQX{^Fgw#H?Wcm0ZyBFS@aC93au($Gzc=;F1ISzg6qW!PP(qy{==_u zU*QR;3d>b;oCw7tp?o|QbHF=@g&%eN#segcRG|rsOYVCsq5MB8)A8^xUP8Fy65g<1 z&?nM2k2QZt!XkglpRCtz*OujnqeOXrnEXTOzX0_?42Oj?K?5}4c0X+!)HFc1Gw9(W zH#7J`YY^Mk0?N4VStTMmx5$FWjXxTE9#t6pFFYwCe6$q6mFKvFXG%Iob%~#cWojC8 z%LxkO0sav!qMg9UlW;Bnec)3~GxP*BJdUxqpBZs>M3~;xt>xqd&R87MxALCK3^2M-vT%?Fv`GvyTAs(!zhbb?Y&Z*OjM%unkMw^D?Fa3AL?YhJFNk3~$GwEdk?e7qQLy*!Y3d2s80M(^ z`sOxU>a|Yal=4^;$3xvJ%?wogktf7`h1w7p(eKEo?GxegvO>9P7%Z*`n4EC(tagrA zJ8=LqE|Zsz4I&~;_jBUq;esdH|J#t;uLES}At=ms&BK4yKci90m(K zpuYUvhWmAn*5k=v!=aeqN38b-KTZiFqfUc?)&H%YgVcQ-(`TqS34>bmVnCFaM=n-f zG&;>}vi(NhO=m;GUI)F>ET1v`1uAvtRL&ww`A0#6#YJ4j(_@{Ucl~Lq7vAV&6jDrz zPvh>Ce|zmo1?OuMtEoQnUZ)1|Sbl5}6!^02Kq;1MrgE|B&5wUHUs`pSbho~Sy~iGy z3k^nL^t&Qxo++3#ZFVG zi1NfyzxuaeYVmB1DffVfvn+B-1o7A zkMw-J?f9RQ1p>Q7FSs?iwH!*S9Ca55eM`qHV+WvQIh!8aojq;ZHo48A?|6`Pdw6e-}Qn5XGV|8LdZ4Y_C$Kb$Y zXDR!5$`#4Dg}X!YxA)x|zhl=O@U|5`Y*j5XF)Zc025G$`Q8nre>dm4G*Liq}0 z`%_;N|7P&j{(BmVM=mV&2$+6})tfY9_sM1oZUfx}dcgHmgs@UvE?1T$x~CX6r-1pT zd_BMmM2AO2s5@A86hKLbhb@WWf9w+f<)9rJF3p3&Cc{`OPaN{*B7$^3?>q5lVqZM3 z!dZhI{%icY11G2Ct5q4T;lV$v8ljN~89uaI+{rlF-I#^&<}l1e+OfT99Vh{^ra<@z zf2F?Q>M9jToVi2OoqIP@Pf)G+XrRt4a2BAWWc9Z<>naQWs$D)IuDNR9}kSE+uWQ zOEyt}KEsqHV9YYoK@2`dN0r()7jV*#Q<>8w^MllY-^f>WR)xZKLZg-_{!`O&=PD0} z^6R{Ah}59NDJhwN z8nRvZ{fZnwHgw!x(65!~;+t8PqYWuv)v-kGqOb&s+fHO5t;8Qk#LGr%Pe@r zH$fSB%cd(Xa>^d6&(8Crdy0ia*U%vTEpZBnG(Bym1>)y5iHG)}2+~v+)&i@3a!5_@ zxSk||M+^tj7iWF0C!teyF$O@11}0sq&J6?}GfI5mv|TK5fh7nvDe6ehrTo#)fr~6Y za-L)JKTOaaSo6?~CE{>K?T(ApvqIm+P;#`Cck;l0$oukd75_nmXYav-W6@$rb#!H{_bEfEXPd0XVO|V3nq@!bwd&RyR-|_$|j}#(JDD- zemhm*(9s6Kt>zPy!5 z2f}*IVYud2+HF2ZSE!hK1ACwRSBX~SChU(Su67;S*b495R|}Nli%GGN>Dqpz<-K&j z$NP<(5VVggB3w=U2T!cClNQ|U!NY{SZT$KE2f1B%a2?Zi5X*T5=X^xe*m!JY*_ zzselBte4@bd7roH2%DfYC@u|I5%|Ii57a4MCSmd4+ES3L)-?;h5Nx}Yo)Ej^3Wf$9 zMHdQb)wrO!@~h>s;cx^ziO=ZPR;7bB8>$7?7L^GG?p}2ln|Jj+=GNxg$_Z>swe~ZlG+ELX(;F(0e!V+@Y($m{UoY>B$YP|q zC0H@Ig|sJ+3H}mY2fey^8n341xkq0j!P#*FMp3^wXgsCZ2$L8LykArp@I23dzhx@C z27V&*KUh6Ku@;_=Jzbort~)IfizLYNny6*?KBN=LUi|Gt%I%jh8Fv+DHkggU<9Pb{ z+^NhEuOOt^?;vnz>h5wb>AvhajWi>CNzA||p} zP1a0$-u7_;*=iJw7THzbxRIuf&)jF!7mv8@avqANV==MJz=5DZ%k8=NBtvY^|XRa-1^~o6x_4S~)x>E};p$ zV(Socce1}&N~hOO8LTgJSZjqH5>)f8)s2|(Ww z3pbWKsb!K<^@@m)VB`t^l}gj^;k#%MrW?OtX(UX}36GbnmT{PYyo4>)=FaUQ;+*qH zw_>zC?PzQLOO8Fm)OR@1mgWWa5i(`{zb%dhj!P_G#}nJd6w%z2)dc8QHo^7gqs`#J zY5+XwJ$IVj&5k*93jWI)HiU2YpD;`qcX)1sfF~QVXTD&xMD6C%znBPBdOp%kW=_}_|eR;jx6a$>gVaMv${WS33F6ukV z!W&!EOOmFy{g2kFj{dPgnycvG=*~)ldj|=9%Pm9aFFFUA?g^SS>q-o@h0gYNtcV1^ ztNVWEs)L|vST;$HDzwHWG_~|I(woV`F}}dU8tLZ+9)2+G z+Vfpy6aL;bz~Yr2u|3K_i;fO$lpcMprDDT+ zKLg&bd=V912+FD!JX9ZRf?AOCH5#v&)C~SDVSch8t-O7`;vXaQ zr*w*Z&R<4X(nx|ztCJV6q+=3beR@6F3ZBoktsS`mRvJHCHaQJ^3!=G>Vhr5uy2s@K z0xQ|}yp{Pdw2ZhZq@JqJkHEG z9?E6scBE#?xJRPl`=cSiSr=}68U4SV>2C=FsHbd3Rkcew-Fj2K^oljCy3KO@*95$% zJG)CJHO%-Cvtfa;4*t(+bZ7){z7zH5^14ZYgh>LEI z7F3dD@eclBZ;&*Z2uvqo_`JY!!nDJ6Am?M`p-unKZ)b{4?~N6db>(XBcP{+Y8Y z#MK`27SKtgSm>QY`+Q<zRXf5+^O%(9mnu5+QlZab z96&O~682*8QaMJZ9(6DXvm|^^dGaX_DMB8w#9OI~oFJ?}%W2e!+KpcJo}w0+csL1TIW35(Xk%bej5PkmnBa)av`l*0~8!Grl_JPJT@~ip#oEO6skWWl3;oO)(iXp z(S3X*J5qm7_lmDqgegDVz67sEjD$)}6g)=XC0|s$-hAh329mtakSU%Xjh2^~gO$^h zxpjgzaLJr}eyYWJ4f&dzYX{tB62?;?~*dqGyze|(}5b^y- zeHmHcJW9o8MImuoxl+FK7D~JpW9E-5^^1%&WVJO$hDzEYM#g$RoKI;l1y8c6o%@mI zDa*+gGLON|E>FpXdt)fBWyEGtmY9}OxGPZe{KmTPnL=Ycd z6twghbW{(19q6Qgp3`0GQ)x4lR{|K`PLszob$c877N#lb){@z${kOTg_|s$Wj%Tkp zAOp)DU*ygTm;|_$J53eWtv1-PbJt2zfB_T9G=p#Q24`h;y9EC5!&q#VN~~TY<24oCsjI=ZhY-c9&B}xVdWNH4UjK# zLgnZML>N`eBopEVg_c?q+u^g2>Bb?w9F4X+k?23_D?lQ(!bm(;U|91oEb-w?dYNAu zr#~!0Szqoht!|_13)3n3(iS`k+_yM!efqdYC+bdV7q;Qc=S z=Xx;F{G0P`d7*x$b}mJX*mf)~hL0WHA);GL=Uh6JU+oih*yi5+;Bcz6t@(0eTjcO$ znv%6&Kv6N?wqaJO)A!mAem_AhkaF?MzrVT&bgCei$qVz18m$oi!q6HHJ1tC(EaSqt zA^EUev@aef;1n4%A9LwHrRe5}Zvm~WsqVFSTKT?>?$Eo?ML7Nt;<$c$)qCHMrSWW( zUn%hfP%8z1KOzMDxHs7mE!prLfIJ3SSE2}U^R85k?R>Xo&86#S<2U#8xsOSX{NvC@ zni4Mbs5x48mHE^L_pjJi3~SF_UvkEwyL{5p9`_%upPj0|ug7-AH~$?@eovg%@W+^>5Gp7$V0)-fovn*ptXjwbrE5dOX2xcz|+Ou1b4ZbUlI;;PgcHi7hgCtc!of5hH1YV`ZgdN z=H>=chgnQce7I5GR7sIoRk`1ZS{rf#m^Ju?ajD)_!ToC(ROF6ub{7|$&O>n~kEuGX zWLr>DmUhDYt||H!k4w7($4Y|fic7@pG|T0YyHj`j(4WLr`;H1IE*6JrI0{IG-i~$d zQUVI9XZV$?qx@0VMxG*3$VcwKeyzaQ4)VV;dJQveanACdVTc7NEjzwaa`4fvG<4GEt{(r1H>o(bs;hdJTkb8a^4dgt;n>2xA79ObE-%Bn z4O@+l4UD4P_G%S<1k}f{HRf4b9)eiARm#j%!`W(kl5aL_ycqbIRxZ2|e4KIipEw)_S{u85AF)^ebJK*`JV? zq}oqr$jr1`zPZ$toZE+{SWQ{9{h@qFn+6~?g;A&Il~Kj=jW0D~rM^eJy=M+rqHr8D*Hr~BAd|)TbvVV585ViV#kn>;u<7e06goLkm4FRiP7!3c^MZco z;#u|8T~@L1Ty(#+{?yD*mNCc21{*18{=Q5^!BB9JE|ZF{0Qd9ylmp@|^UXZ0bB8Hd(Xi2CE%HR1NPA>Dg3=P z>}&7;-|?&Nf@mC|gjEq%;)^3R=?o|)?;*$HSiaY}Rtyn?Nm&+KJw82z>TEd_c-ifI z1CyfYkCVQ)!CvQ_35Lf{j!U{$xb#F+)UX$n)ZXPQS99Za7z1O^%@E)=9oNCLJmg1V zT9ENYomVN#$#BA^MyP&{gv3&z5o(63V9O71;H`9p%)Hyf4i+CbIliWh^!3V<&GQ?S zcWImlfbgQs-k+Q{R30c(TV1f6U?JLt3cE8Z{mJS^Z%>m`Fj*G2goTjd*jZ#>JDh&t7r=xi{z~1gG+{+`tHo_ zf7nnLf8l}1zmzK?`-fb$tFem%-+H>HKwe#ijBUmB*+$`3uOaZzhw~iWJ#r&zYdChc zQ-)ivQSOf@ggX24-sdhbt#e_fM+{jh*j!cCysPr}`|b(e-kIh>NaS)~o`L;P?_D*v zg3>|Mx)pMw*zRW$>PlV#DIW_)*;B2ad>QQuj9>MvUY0U|yKQVk8T;VVZV!DPJf7a( zMVgbL>#jvc;Ze>z;cFRI#-6%{Tke7L09zZ-a~&7R!{NNUNSd4zU#)n6ySVf6Vn(EC z6DFN1GpTE7xF4R%oS*Ee7j$I0bV z&S(~Q4v|3wI$8L6t*JURFQaIz=9(v4EXZbG*BP8j^l}w|j1}X4%d!N>#JYP6^mh@_ zmJw*OC&G|3Xw=Yy(a!j*kksmNJ-|BC=pA|85N*J44&|N;_-Uz$Q90G>0m4cE@bJra zUS`fX1qhMoW;)k;2kt)MD){(MV`eileO34;WY;5J+|_W`nfbose=Jb{)luyNG&O0o zvG`f9f_L9OB@K(E)=1~X`X|Vb|C^pBX?yV^&#^$$92Xl5@MYsG9aCe+g2T#CtT^21 zUPb$jGP?TeeQ^mg?MwUZ=k@bB`7J3fEep)!U1-&p!B3XfNw%O z>US|n2Y5(0XKqgaTCvH}QOmkEolPUHAp8Lz2f_z+aj0z#JnjV-o))ar)0Uiv<|_&> zXg*&G$;N>PH!rhfdgzT4G7m_Gwswgrof9$ujlDa+GgkEk1KM&U=f%TCeSjFo2!OKH zZBk&msV3Zh)GGmM>3eUdXY`)&tt#WCv2zN#a8Rdub`I1vLF zdD0RS)J@`1TBwWqm8SW@RN^$Gll+!k{vVdqYzM;bo*TH0oF;_5N+-jXp#FO}9}p%T z>-&QrRu)rtDn4R+KaUvLY8e4~Av{I&!k7WKCC7>gk-ViV02A1@3iJUs-VuL%=4MF2 zfZ=D)c4jt@%$Ds`oz>j@ua8Ly*b;^nq7wLeXSz{y1Pn(DRX~^%=nav14CVwz+A`<= z9+}|)_OPS>?Wx}lE-e|ub91A(A>(-jlq{DyfdSnpu9;tdO3+_=+k|ZyCRil5h+)|J z)A13Vp$rk2gsLH+qv@qFkx1Mp-xARAjo|OO)*hbiNwgq~MLFG6R5#7%;=eJcj^Cty zNPwQ;jHHqZ!tH?)kw=ky0~>B-Q(^j*`qFJCdAPu+!M=uFMw;(*FU| zE{6@f_}?ySZ{0kDB8XA;`oz$!F)I{GL&tv^Z6;K*W75Ln0Fip-w-g$uk_;4Ig!+bmcUtI2hlyd zw&$Y6N6;FIDII4W9Cgw&_~BmGd@aiCXCFQBa|Z#k1@C8Y>?)?@0W#ylJ)IKImvCHIy7C?f%AYGeI? zTx7D^)@E^4k^}ruF48G~jHmguv*%DR-Lgz*{ohpceO$xlToYsLPvW;nCAxdNeMF;{ z2R|5IW5&&5NA1n;oPCCl*akLDVNqH=ELN5%5-@@PoiA%HI?n7yKh-=oL>%k0-oU`4 zmxbKTVOU`7%oYoP$Z<)P42oIv2&z?+{61YnoJNy)LO;x-)LG4kE$St8g!480_sTbW zY2)i2G??9MfVl6^zzW)40H7%~R2M3x=gW(lP_M%BZ|`&sS;Uz!k_NcG_y?34$B=v9 zyh#s#`Z^IYiljKJx7vFbcvB?_D}D83U?E-}Qw5ga(dN^9~sXFT`^kX6AW z@4k$-^#g{p?g}d$-5`9YGl8oOgMPRq=9$%QRc3O;q^y3M>@q;79l`TQXZ!yE6+!C0 zwk1Cv;%5#;Rds}M)gruoZGAnBA+^{}U#Q3=?|x+V!OkPp{HPoihat`LI1CuzI9UoYr6BAVfTmNakRv zvH|PSljb0zZf-6|)+A>7vffVrMjO$3B26(c37QrqXOdNjg<$8e~E?Yk8O)Z7E zHxyT(GxQFJd7U$z&SHN$JDk(2S8l%bO}fkKNnoOmxItzK!NQ<9jYlaLy_?tEwShZXf1upcVm z>{XI~lROB02t1u_LK~oLT0JUE3x?m8KR0!XeO#TE8Ukm4O}o4J5JPP_45R_*rnW;A za|=t@Ix`4Y_k=bW)&?bhX!Mj5p6C(z&WZTQp3zGIko*BULk=NTUwN_inRbzoQt?<79$QgW-P%C{+I6?t!M~1 z_ppuj?TcW)en&aa3Lo&Efj{i7Aqqi3p?1NQf}70=b}Y162s3WZy`=GlX_PNA9K z*j1nNOWPCeJ;zH=z0lXj2?}qCJ1mA4fDSAS=KS)Oc?Eq=53@d!nxVH8{tAHUJ`d$Gmr6%4rc8 z^Or2IKVN~`zV9L0eYD<8B+G7=hx+Q3Q$A1TsuFq3@h}6sD1)_wn6_8TLvWZ)-@SIR zu0Lr@svsiSlzVNE7C8s=EHj*G&uVc7r+t*95PB#9g@#sEsU{FkbOjhnfp$$$&R(e7 zuCUN~+4x5W`&1;_hWoUuqnG=b>@6CyMK;JZ4EpL7grY-39P$TxIn%spgleDC$Y zHAZ%*VOk*!i8M^`O!Lp9!G0duX~BiSmFKMq!t}s+=U~>pSi~aUCKea47O{xJJL>Z4 zazC8?gIQhvf;THB zv_Yp#SFs`;y}mvEa7W7`z_c5uO&-XuDc5jLMx-AO+a3|A+M|0b_!D7K?o-=nh?4BX z%m=|!uywYS%@>sukW6irBhqz-6BPEfX}zt}^J6^@uUTJdq&z2Y%G6xE@eIv@!&D_1 zko;u!8DJYS_x46JwK5Q4R4{jZ_4vqhA{B)8FivI8gT~sMjb-(UDr`LzQb&_5NfD_B zTkqD)p`^CMg~ z;LM1jE#>g1M*{<$HZ4+?_DIPfal?~ZRTxr8G`{_6rN=0EWV~zJV`6GqL4=jd>KUB$ zFM6^*o+O(xGL!d;O!%Y9cH~Swuz<%#P+MexkrJG)P@Llr5yW(vl#=GdXNupUr18ea z|E*W+OGEgp`RyF}nX9YdP?IG~lmG?^kBq%MX#+qU)YAXV!OF}r^loYIyD1Y_ML$ncrLkE8aA+?l zInlll6b(4(SH;h#4%El6vqDN6`u>w8D~4i=sMR<%AMrcwqun_)u2@kfLbKX?uPXhL zz=V2BdvXV!*jL{t5*!^Q+8NDv;m4!Nx1O46jQ5dJa-W|+t$v~(HPLxYM_mj!)T+b z21)bQ*_W;asc*)Q?KdB6ac)7@cD6+3YcJR9e)~?=>q3I_YqyUSzIvjJ;x~R`B^EDT zQ4fEq`p|WCm}a`u<-2tseOJ9<_Z#U6M?h8Vl@67|j7;>2Cfx{%vHb7V4dGz1Dbu@VR zlh<0A)(||=`Al09B9%BlWhjo9obYH1pIn zwd?DF)9Tjdze`h|o{kdCz*S|nbY>KRs_=llAcSI1m3=5(q;v9+Dg{AP6)FA<-=l=* zS_Ja(0qFFOge%{vR2W%oqupUB3?aVUepfdU?k2L&A_b`&R{2m}oQFY}fa}b}*HrPu zNmGdMQ<%lOcm<&k_d)k3oSlCfLn-Kj6Uc0KRy`WkrSg|i2*vpheQ7f2%qgTifeB)I z7BZV!2xe9L_A!so3qf!DTo{g-A3lDBrWf&JjKu}4Mf{kf-}x4ZH@*|R5yKE~in(J# zWZ@d-;Y?Nc=jfR^*Ni5Vyy2a%uU=6;J!S0s;RGdr!tI#$q)W+dq-WCz!>|C-j2-Jn zkC%5hpfkj7ritBza%bp;u9o$v>Qx=x1ci_WhAk5#q06K^+RXmL(~V|MY%D{lS_Z_+ESG&`{V;TgjbG^{v_VTLs#yeWFD`aeHgy*eB~arB^`yU zF0VW=Ge|rkhB>V;t=!p0LfS65&3XrlVCGlP%SxM|qsTY&-A`opnSJ-*D9#>&no}8FOxkHhZDS?44 zuinsJtiuD?YFVLWupy$5(ylMY=GD;+cSFyKe?=--k$K_1^<{s_x~lcnnBO(o8mdeL(Hv?huevWwH+26(>Eo|gLj z^VK`w*j-Ooty)<_LKQa-pPKnw?`~bwmby-#ua&{~Zm(}_?K?U1`DbT7e0_NW+Qk8Z z)hNLPgV2>Qo#IrYO#`V#0Xna5U*FR&5apC$0PY!DN6yq4K~SUkPfxV1Xy0W+T)nbB z+V%3$(TRYGAPw|DR5=CqpC~1WM1JpyQRp=Di_g{N`iA!UEee-d!CAkhcxf#u8aaq+ zzx+Zqfs=qUja4w5SixhT1!!eef4Z}pA9^;P6BmIx<=ah!tZ*qX`b=9YQUIKBgSJ@A zhyqv0R7Hag&z0@jF;a;3CUyA5;r&;YpB2d_i`gw8mpE{ixk4%%ORfwNADMGsXv02- zcx-NMBH&`%dBVU%Tmq-6H~`HJXC)GvP)@m+G^uPMHwthvtp>bXN{L&s-+ZT{Lu%mUbWx9fnzD0bNJi zSD~2*XUg5EJJG)JAJrhlH$;Ev3>LV0H7mLs^J9Lt1^PS}-cbB)>USPZEEg^W3yjPn z7V*}wxPY~YA9D;ZpbNR4e!uPn(rI(hchu!u^ZN~Zo+qAQ7^u0LFda;XW?i9oTjA-P z(^3-%gDvys{-uct^a;?gsxPHYfQHQML2h$<`>xHjcrbJzA_01ghM^xq_z$)ZJ%kvB z%sv%{p>Sxbn`gnIzl%Wj!LSUmNR`Cqz=_wbm%@RPm@}D1>GZGd8abnk(xVX#kdEtq zDyJ%WMc0Nwl}UUkHk9OSKqQ(=LleaO*V<}@V9I$UdneD0oo{fzXY!t3pS@|Ib zs!-&F(OJ!Ji%s(I_iV2M=A>|J(UYu3Ic`K8L-OQPS- zPtf8XB>ta#pw@keoWD@dLGXtX&f8~`v$6Xxzg24x33V*0q82KE?Jc$sT~{MYnU!*& zy$8oIi~sPm#m~uT20RLfvMw`j9fhwRuQeu|YMl!}WW~~YgRMT#(&)8of zv2WwVd9=S>Ko~OK6cDq}05K6gGzj&^#-=voffOeDqO?)x1jMhi9^F%GBJbT+3~|1G zxpGN;<3ECU?%q)=m|tkWWr^ovTcWMDw1wKM*HvQaY^tTbrc+zuYNE}V$M=`GhJ(`| z^U|fYMx(Kq-7?yloq7`8O{?k&m-n=fxYHTC!jfOu)FR@>s`jcS&L|#s zSwF~CjOGZyk zvZ6L_C>MXz?1E5e%cUqx>IfK8I^toZp#P09oLbbYCUi1NFxyaVFYo@#AGkmt8VZIAgIMY1 zIu}-rey;#MwL^<3WUuz>%U`dW28Ic5T`_jF?LMDAJZU zY|*S3u3kBj``DWwKk_Co;>R0{3s{Tz3BnKOdYU_g6!)EmG=Jg-HtYQiMP4!2!)p;q z)wH(h$=MJSQy={O@K}2&Oxaw`T&_=Kv+st6))xpSGCu~%%v^{weX_-ep_Ohl`{Wxb zXL9-HFTL(FOcc8R_O{_OXq^)}8<^C-FzA8^!stns!6H;PvefqH5wa!Vz4?&C80zK} zOyBc>(t(qsuZKW_5Iu>E94w0|C_LP_>h%d+H$k>2h5(?NOYK)Y!Q}EN)AC5qI zC1#XjbMA&+9p+wxUNB6QmM^S5MUANRd5zVe|+_s%qh*Jx!+S_n)i} zx91D!M#8qW^$C1RpbRakbr4q@RSwA-(nbh#Wc8hqbN`*C_WZ>f;nbodat7i&RD{1* z3$h9csXD4S{7m}|z8g1ImtD4U)RywIeXmMvrS#Zn)}f!v^N($Kl#QU9y?o0$?t0)Q^P<5|1rb?js15| zl`J~7FCMA40-bTp;X&`wR^aE?c9jW-F{E|0UAS0pEM9EC8cDGCKDM-x^89u|JT$2K zSJ<>VCJ4TCc;?sJdx(XmjRp+)ju?L88EM*X{=j(*LKH)wjr22zg4toL8zc^C-q3C< z6@eD*kLDYE;}u>!#&As7*0;~e|9X2r8XF!A5oE>24U~Lr&W0W*!$u*gIF^%0**GMn zToG<=yREzI6RFeZFW+Gu>N{%h+E$^)!#8&y(XQt!FRMrP0Np6=&2fp( zwQpAv#LxqUFFarVq!ppybhUBO&iB+^z7Qi7KeHV3h4#Qy9@vROg6y&tJ+?R_p5X~w zYI|Wwr=gc&di5&EXBKhBR>x301r(<9UA+95%+Onfp~#u`RyYia8kn|s3MgyZ(0Ule zDmS~K2FU8w{u8yZnD_FAwUEE_g-CW*i_9%wGI}pElzG;PhO!gR0nYKRU86L_fHO$&ai9pJrkQaGiReO-l)L9tiIYWL(4v^+c)zC9hR_JW@*fR6B50S{V zQu=Y45%5-RZR2I5&$O?ShaSyfpLT`kj9e%8(SgN=x4UDboi1twrRSwe-It7y; zokjfK%$iGT`Gc&e;>V-E=vR*CpL@3UJs(Rtr~F`h-;P@NusuW0JR<@bPe>6>Q>jHV<3m$l(Qoeuw=*)j~ zYvXJpx`~HrFA*8b@iX zWi%U~Bf&2mRE*@co z+cww5U)MkUN}c{Re0$&M%Wlrl!>30tm*vSX4XL2DQ~R3BXZ{~wnfbf76(=`#wo?_4 z7_xY$9&AkmCy3L*p{D5+{Au{`2LylanxbgSnw2wu^h`bN_4xi7$@tydt1p!C&~J|i z?C}duR(R1hXVuCYz^eEoCS1)0gjXyo>z96MTDh!VjjS-XbMS-Lj+w`uf+r&?5vFA8 znp$HO!#F@2ehg8o5clXI8#<(5&xaS=?`F_%3~X9mU-ZkEoDk6NGZQ} zdyQmg(91{alVrqsWwhh}{PQE{Bei4v%auVC#tl;TKvfnMb8ClLalyr0m&x$%Wff?f!*A5Oq2Fjv(O3*ng`!`@stCR#BMrf0&l zioE@XECdJJk0wpv4V$otMZ9G!E?_NU5#Jw7-^Kgk{8#XWU*fTuHRcZ>F*hMN*WGJ^ zI8FKA9-H}v>qq6FtD48XY5Fv?!{PKLm{sTQ@$b=?#`URc$Iu^%oZMg zccZf@$L&yZsPMw~YDFWTD%U9-GmocDRk9kKx}L%iNkulC{m-zPkBw1PF=0=GTq}j- za%61|F~B9uM?`ztHjlpsCHDw_*r*Mg%tRXeZ=R@JD9=fShv&kT(~!8VsfSl>l9_bG zv*TxKVpKWVV*+@|vezSU;AB1Of=E@@ph_}{k~Jnk$!Mg<}T%*TV;VLW`__~-d3ysxd1Nl`9aUg=!?ks2*sQm<+Th=9VV%)Acs zq|aqDKO=!}OE${lvusIQ@@qe1$17E?@uK5=hledygBHlC2Z|IYFa+tD-Sm|A;KfIc3WFu9kO=Mk7Nq#-D39g0Q*XhYAE zjeMSx^!sxLSQBTFOP_Dg98sb+BK8LPy7t=+d_$3cR#&#uE&j8@f0RAP>mmv0@%+e{ znMYo!%bLRsR8^E|@U0xo1axz+S3T7BkxF|ZgzTvfKW z8I!v5+-vpr98M7DYu48Vxw@mh_vaLLf^(JiIBBEd>o1pPk<0~(byqx4wZ2`H_@Mdb z0M(oA1lOT1V(;5gcYLaF1{l&t==%9nBkHnpfJ=Eb74(`~8#YjIJ#Lp(d%9ncn}ieM;F zQfzHDXjt~DC|Ay2%sX9hQZ_!*iI6W_0S1Uv#yJeDQijZbpi_qEp4dm*G%j0FchXs_ z7LG37EyDad7jAK=a|rag8Z|4X^~|NY!~NZuoFCJF$6dHf-*-%}UcryhpGExGV{rj% z5sP?R==Y(yuGCTp_vv}?rY`Y)!Z9J0xyU@KChnIK3^&M5F)WrXtRtw53f$`jNPFkyLt60o#YKuRpL0 zY?N~kH1p-dGatU8+`xIuruvMO@~_=qJSe=a#f#QVhkTPuQ<&xx2X;f@6;6d@%O}s( zvJOuOirZ)fs+}^mbkY_e(k@lJQYQN^9xFOMC_LeZ0udBSIPmd1>JZPreX^A2nV|U7 zbzZW3=G~h|O7eymdOaf4mZh$cb2e>-@p24KFzj(+%PWnaYA*c}7ZdyIFW<|Ld|7{8H&h`Oy{-ChvY!apPFAa$A z+1^%vE}i*zPgZ`55}y+ghuo-Tpz%VjvRa3FT)}wz&~^3H)vcRHUqi!_z=|6N{scI= z{k|P_B{|R>(EJBpEGGr|-+s4V9=v;7iMx(nY!6|5tX;!x&y6p&jKyMI%Y=5K)Uh|9t604^K4QwY3(Y zb4jd@Zk_;z21XuOTYp)t4C${o{D@!>#=O5c!BgZ(`P5;ej zXFhmcT_uF#{a23`cZNz_pw^DBhjB?2q#w~Qw6}e$cLq}SSt(FZ6*{;?+w_9I&SPAF zfk@VaYysz^hg$K`WMz8Ns8BE{7>#l1?1ef9^$WUDWSo$V!cnx_igTEw88He|Msz8- zt$on5G!Lr4z8BJ*eh4TEHDFqy(^H@%g^sXg3x?1BsO@$;hKIczJ>95@$M7&19_~Fp zzVpt3A@f{H-VWwcg8ruT&mW&zF}*l7Ki+JUzh5DG3oy33=^vpRi}U=!L*fwA^!(}4UK!n};WR=*|o&OP18T6 zBpdnC&e4NZ*zEDwqdM(nrAp*Z*O$?-0S$Pxh@KPe+b;OKdvx~Jl>=?@YuC{VJ0;jO zQz2ow|ITVM%KG+6DdjkWL8^@B>4TN;{p-g^J~O>_O}(9T&HB-*mVD_8&sS3SiF-?2 zF!6lkhI#-;ZV%M*_SE@$AOiE>X>TUxVW?EmY~yDSg?`+&rnG(P!SXPz>yG8O5rGsW zcT!80!Y3c7$G$#%Lv{WA?c>(BY^qhB_q9(@*CI$;&0&tV=ML3-dCHpb(C$)BTPU$e z31Q&JTUV| zPnRHvGT89{+%@$fc4Z=I7ZDM|0Hjc&mWLStppcUtXTFApQs+hMrn1{s#B7^a*Q>H| z(#b9+S$E4d*1!Mir)pT{T}7co>~2@P-Zkn1hYI3De+o>8?e;CT1Qzk?-~1V)eVAcMvUBYtMQ2Ab9h0$CR{eM%3vT|L+pcHsQOd+IcC3ebOVTPOh$i!6tcF z>?bKg(u}Ge6d}E+Laiq~D54@JLLtTILCvaGXgX%54OwlftGXO*(G(46L3m^#VG>A# z%w&3R^jrIXzMTi(ckj)c34jDy_a7%u?z8rCd+mMBx7WVMsJ*U@c??;z5cxH$>UUFw zwU?AC=NdtDuHMu7-C>!|#dDe%yE0_4IES3PZDZ-_lwzL#;!~@P!V_i4Xvyu#dyf{S z-^OJF`_I2HoR4HZ+KV#G#vT z7=PkYY-J11w(n*-qyggR4AhDMolGafUA)8CSUY{qYZ!)eb4f(cjJ6zdwJ!^ropKW8 zj_JZ;nlq1i;;FM0+j)9I;kP5xiNMJV6jP)#yaKgNj8FbEG?vzef$Tr5f zHj~q= zqMqU;TbxN>t0dIM!E59=WwRrF@kVw^olM zMkF19=Tq%oCmQ~3dsz}TJm23{Nvs03XU^B!)`K@z23~wE*NrxwQvx`t%31~yhjQuA zzy3;9DX79s@bJmWrWQ}yh=}2T`$+L{)uv#GYMPDb;)avdt=HCrhp!%<{K2A%|J5E-f**+S0{rWm2WgoYdIqXW9vdjL*TEMe7$TSD2jh_L*KwksRc!qr&fqYTuC0-QdKmhf7Pl2?j=6N4VW$M>`p7Rj@#P%y`8m03&bNx3ja0hs_MS>>Nj+o zChR^^W8)fkMFLf%sa0ehJgsssVdD64^K42Avf^E+{^7P zNDjlJmOmK`e12wcEgNmPa^P2MBNfi9>%|ZZ^maxlXUh*k`Vn0W|M!oU+X+i&1}3O- zRzA7s!t-t@%K4jFJb&qj)%Mh8-@VG(T=x*zCE~DmkO0Q0a=JrpUVqN3VCUgMs$=^P z>xEgaenMe9Lkz{1M(N5#EF+NHGvW|tDB0-`YbS!k5bY1092Tm^4B#+s=LfU{Pd4IX zI-qD>(A$M*{6IuPOa$y>6n(}$myt10#tTC;=xRIWDGozX6(_sJUbPs;+OjeTGbU2* zV=Tz(RevuB^xBBqRESR=c1d{kP)!cs5>-Eac&mcEf*&zf7O+40h2z?k?YS{Or|Grxm?4Tu{>-%mvwg3{d3W-Pg(h3J6zok6 z>s{S*?wRJA4fNG3FhiQxvrL4OROa7fL{%?CXU>$aANImbi)K7Gr8bohrOMNZT{h^e zSDAcc=t{y6p!A6&j2+O58HD-sFANjqu25mz3Xllh);!ImVe8J z;q51C>v^~7U$)n^r*#x6o4TQ%=@{bQ=KT;63@Ei=(Qff@828jBEgUqobp+xe_JBA{ zvXN8}9lv3mPH_QImS_==p5AJA9vjN+#)4`IE2tlk!{QNMa`z2KbxvdWmC zLEqi5e(;bFG)K1gBPfvS3m<7FH`K-%+xbc5x}RzXPYhvDjt^<|+4E(zw6z4-bYGGny`;w>2Mm=h>+3E&GM{Ll*h>txj`HpXRk>}4 zh&|^i1Xk46c$+@^W=~4!H0S5;sD-oKV8RVbw(XnzuReI8;UzM?-A0S*j-<9=R8hLG zh|-}fqwoh?4-O4DLEes8ogDtZ`vz-k?PGSMRB%5NxN<)*<(Rw^XIC>#&WD;P<{%O%@aJjN0VDX|KZ! zHvVV=4a}*cEuEFSZx~v;x@yy|7)=9JiIT|3aK5;`3W)Z^^EM?$@gzPswpA()^MaIBuryf<5BihS&{URJVQHv?G8CUkM18dt26f7P; zQzjG+o}B#KACw7!0!m`;f%b~4I&5o~JP#@|ZNz?mSKaEB*RCg|s#tks>*TLruG^X- z7`3k*p4_#mv>iHGUH08KJ^`0t@`s+fEvuXz?o27g_zFEADvUQ@qq~% z>~m-X%w?|K8>KI{wQj=Dp=@|_2E(=Mh9x$023?s|W;y{e3r0>-?xo2Nv)-xzDEG8y zC(pOfdP45UL{D!r5l(QZeEjs}wIdbq&{=Wfyx9KEheA;Aq#+QClyvJ5wl1E&^B*3E z86nDu*$`d8yU5k;b>j{E28N1jPTZIg zzic;VU|hXhl48!P(? zFwZFI9`;#9RE5Z>Sqp>`VxZhzQqW$4q4b;Vx*kyV(9M;=NGWf=w&vdv!4Qkj?;Gx` z$`cCs5xY)%BJ$xo+Y*&>|2r@wWy-37GG^H?MDV$NLw97xFYWx*duqU#@5X`Lu_Awv zSO4AnYrQP8fkIzT$;@hKY+Ln+hA+HO@BK+tQW{{|p*Txx6=Y~eS87X7%j<{-Y3%lP zbBa_DZn~ycrU)`r%fv0uKh(0jw3nYrUnTtZHaVfRnOzJwwhty&YTY!0n8a!y-j)qOnKwy)kYqd@SN?y40cR=)aj8BKa`0Bn{( zo~c$uL!g$3SUt9*M9|B_w)m14U2%z%4z(gnX_RY-!s=CZ%TijZto!nq4-KSb-S4}( z!X>sSq7BmB|KraO9S!m^M)$WBE=uA*5yap9iz;BCDgjKKt6$o^OR6wae&w1PQOd^o zgEv+oNu~mrpr=uQVsl7gF-%vKP;&O!YTzBOcM>Vb`8*65!L_TZA*%4WzNMv-aN?)h zo6dJ1o!qf;FvL%sP9nX4zxua@@%r_{sIXz69IB=5VYiA*JKd73Qc^>%F+(i!3Q<)h z|LqnV3Tq5EFvqt~K33=E-+k8*ReSCMoeIS554wsTud_+;iPu*J&s3F;wxUJ zO_h(8J1?Uuu`%=~qxe zTha;?pj^=OZ#ALCKb#zkb)opH7fZh~&8%!`%f#uOr~*~-Q^A1gkuNLlt}%z{1w3kF z#q~#{5*9p+I$IqIDLojR%NX)-Uxv-;bHlsX!VvK|Z$QQ$s!%Yi!nU)$zBA{bDo_hV zjVi2D?pjJkJy2oenwli;%ziD3i56-~PxEpr;i!8uAkl4pY^Ua(-X@ZuYiA z)CTEU0YO=i0a6vt8TuIGD2Ya-V1{iTS-pbJndV;64V0z7rSdK<8W&zFG(SFgq&A@EwsLZ70h5|0c!;-SQv|4c^9KE_)~Ei zm+$3^;LMg)16a<_m3jHxugn`TGj|CP=Vt(yf$rhh)%dSaGqI#hcnE8 zVp^GpHO#rkZvhA}h)9>}ldu^*aNmk2!&PgCyVh-ZBqUHNN}H_`q&c+&A@gI1c$$Dngm~PO2Y%(H%DYTA*$NMS z^Lxp;u}zh&YbfO{QPGg}EQ&3?5VlGW+U&fTn|S=G!gM8K_mNT(&Gt#2llY;?Sg>J` zvng;jU$s}o^HfF9UUR4=Udq?knVVwWt5vZ!4` z$1%1j`3LVFdhpr3llR?HB8N{+?r9HCRaDp49WF%l!aseuH0z6WO?-&LM_Q*)@*B-u z5`GM?9x6i$woqzVUQvGD+?MSm!`t=^{aeEfQyYYI5=@R7sW4E^ZDkC^xuLy?y)P7O z6qaKxo_A~<+RIx|(9tv##9%OB{;L-!-)zf;6!s9Q{rn4c8xj5bqFig=XT%^H$_lZl zko%#SaIvDthR5N3ZDrixr<2IRla1J()9h#HUDwrMAi_`nfi29R zc(@jBu-WLrBRE<5QN~P9V&X6bLT=0Et>Ac)SErV)w5V)bZ~+I_UXnTQTl{ajn*tzj@dBQ)9Izq<{7 zp11(Mer&LqwY<&I*`7z+bEE{ZWkDwvZ6CU|RHZ|C$ZfUMo<3Uz;%OI#G&3LTh_~Hs z0YMCBEecP=!;O+F)mU2s!~QH>N)v4xL%m zz;->!ptb+?duyFE464W~fU-i~as`y&2iMgg`l$jZuUi`9% z%%&^rY7=1go8_9Bz*0ygsse|$m=JKFByWX>Xtuq2cyI~g0OXGTl+y}TK$XjWGy?`d zrnxhks_J@}1dgk?ajH?zaUn=IE zUl2>Py%`6ViMQ6oGF!cZ6~M{@)(ZZ1VSIhO)NAFB<)>oa=sYE>S8!1u$YyHWF|X~~ zb;IA2$9OZX8=IIf^x72cXC^Bl&I<;;c4m6lMeH}VL|6WhPxb`nSbgCvoaWzs$_Qhl zxkpTTIfkio7HBwZ9|y)#pq!2J%xjsz*{ed2lx^&G5amHzYArO;xAQS=fS^ zLO<+4neidf@GrR&bSoy5A8U^tX^7#(R$3%+O6ww?7w!fz}8pTNkNlr{e8)k?$My_5{H&9R&hU`hH zvM1<}sv??yPyec)Jz|ovZU`qqQU^}d`V`MUytVEckm_JFM6X;^X-N+e@f+K#vR*w@ z`_|}s)a$?dnPT9o_9|Spw(gdSx!-7SeF~z7R57<^qL6aPC7eto!mMlZ*Ip@e-)Rp| zmD=V^+GyLhzf`4-1ktZqSDQoSY}LlL2y(~9;&F~tB-?w<6Og}txt{V<1+;y8=VZ^( zT30&W*6cVV31{f;wsb;|xZSy_K1andvC3mCpds2Wi~c)zl@vCH1dy#f;Fk7^@J-j& z+wSr3L94@q!Nwn+^uS{Hk53ncs=9%0Ynh-VL9@@=1kD7~*xI)NLpE%nKsi0s8jVlv znf$BA2e<7h2T|gT<`idJ{!>+hygjBG6EsN6X+Re}sn@kv*28qF9%;9sRd+Ot_-UXD zxD9;xz^!!)$?rT`vSs~>dIb1m_t)CoZ$31POiI4j-p&8eO_Po5%C|Viv|9OI_X<&{ zbwQm_ZQ&0w!!L2jh~kejXB2qO!$dfoK#HHL(95e9Ivf6jSxbonIIn0$T>> z@iX$MmXI6%!!BP?)o9O&g+Cq%DIRjgsJ3t6S%aRbGVuv$wZ)wMDnjY#T0v z#2sp5jg~7aXz`Qs)8|SLkir%R>~U(iGVyz_uN5m7E<}2;5r=Z@s>$YShEpJ@m^Tl# zH!*~~ww!F-b`2pSlJS$m^X>06E(Lc_*Hwek*4%RvDN}O8`s(q#E0Ao$$Vy@bfBt;k z6iEq6{7l(iwBvkC*i&dPfPc3w_^RTe0?-ytn;#B_(lpGQ+m%gi_9FAzp(>O!?Q~A7 znGi$Ggsaz9Emba*Y;KwnL(k<&Qmc_8I^$_%GdMwdZs1>-U} z1eDODAxeZLaTxQ%%psgUJKP@)sUTzt3zs=2x$l#*Ddq1!j!J zoB^_#+oh1X+B|4jtWjMM-u3qxHbiIk(0S<3E6>IVy>Dz~uG!TpI81WV zQS+O&93T$~4>$yeDcBoS@?>d-?ES9xB3?R!BrpMsl?k_%DLj3$3(;&J-{OXHk~I8J zzh18vHfEAPC#OoDqn3!hl=xg*C~-(CI?=gWw%ScA8UNv@At!a+<hmsM1T{>el2VB^1fyhOAY zC3nVQONPwEnRAtBIlXD71P|nSu~OTbVA}oIjw-N-!qE43PVTzC{5g5H=sFHvpjeSZ zX5B|Cs@%F>B4>{w4NNU;3kO6*gXWcsV zK)YQ?TkwDGg<1=!oc6vN15mkc>i|SSyS_?^2R#R-YbaWw$>&_-rv3Zmp2`32k1KGN3Q$0sU?JI{W zw!d`O1?4xaFIDj;cB4NUv{6TA4UzJlM=CftRk4By+8z<~Pqde~gIdksvc3)}(S**U zrQd9yZe$l5PwvsnC+dC>wWRblDdGD4Z&jRo4?cWT5%XzI4mh(UmQhPpf`b_+wK(%2 z8z}$5wX1e6rmpMu`o7v?Y?#NE9Of`Ps=!2TLc-&+RKi%uzyv z^7ZY7QyN?4wwR)D+r|=6NH1hi)!5U^?fqQCEoN=GCu~N`x289@Qb_;D+cyoB5y|3I zm3Ap`o2%H=LZpNdF^4Hstd5s08i2w-eXj0Ol+WiU`1t7(q&)iLfV~s;d|UGZ`*a6~ zNxgn_vbWu&NsyJu+Ke%wL(!agWlxY%I_>fJq3H}Zh0rl5tJ;2`_PO%pzS{5ZthHdj zK0+51f9p*fJg}v_1;2p5c;}!kwe-Za-`UOW(~6XH8^lT*Sh;FVh4JdO!w)M4$Pg0b zAbQk(_2n|nTzbJ~KtB2U=*mA{b&K1F22)&;wQd_vPTp8vk8MG+-uj_+-)f zjE?!K=?$b2RIuUX-j0JM<&d@n2TwMfej;_2a;JCqy3s@_2v5iGi!hyIj1dPq_>9d3 zzdx)8Jvc!Zp)Gd1xz5(_VCqI^tegAw!&G(I(8tW+&=$@Z4V3wl+8K1pjOmWW=<`@m zRi1-$$bt9}be@dfF|vBKgx$re7UTah!hl@C3VtH6vVgUMcQh{J`o6^K)mx6G-CO?e zC}1A{ST2Z}sSGS=VlgG1xictaf_tfu9!mzIaBR->E2-vkK)knT#<6)UR@LeAg&zEHhD5hm*&oi=PW}Cy_M>CvIwe3C#b4qM|E;xhejGnw;6_nB8*Y2%-Ff$%f#+H|( ztL;|Cz>tZlH0+_9ixRdx*As@AL*%f1Qbg#}vMA4H%A3EM5^LR1O_yu>~y$eg|n$8g%V$-NNAmC(K zB)+9Rf#r}7+zzu&sg7$)A<7{J!kY%75Il5ha2wLb3^NQFGeXCT@n7%a$^Azysv<@M zHt|<@_~v@Th?8U;(s}lL<#HGjBB6Gu)ygi6iM)b=U8dWD-50jk+L`7t-BztMY+mc!is%1hNL3W?q`d+SNnD9jxY>0+`sUQ@&xQEf=jlU4)%R}YnmpS``x zRom{ydSFX2qlgk`9_LJZ3^^v0#Xs$J$T}0QFlj}G0V^zmvK9ePjGWSWL#?CTo);mt zx9qE0v$`(4sz~8?u|jw0Q&xx`nt6E5x_05czOFW3-%%loa+(jFtfh?)-dcyEN1MVc zhiZA+z}wQ6IVd{UzOE1{<`AjXLGQV--i}0{v7MW?K#TB1KZ*;QP!oUh^}1v01Gm;k z{c;Jd@J{a`PH{nZDXE3+Y&1sY|3{?Rj&b*qLZbBrffudS;V zfBe)&A&{+0N?T0i+w=SD7Z5Qm`s&M*4eJIaAu0=+0)DMcuwvOJSUJg1vh5l}hi=t}?oJY|!ruXtN#@>im4%XCY!PdcI@f>4bb}? zRF+_-a5+D*9hny3H;2w2tm4wllxS(h+(pAUgC+RKRlRJs(aQ>%S&q>~X4K9zW3c7f z2Sa5%Hwv>DN}^mhg=om8o9W*2VT7Emb_%6`Uu|0XmbYI1Iu- zp(aQ*|K!;r)ro#lk}l#w!9eiN>n8ux_a^`Fy@NPNdU2Z9Mi0$0F@d6o1G2$y@0`4M zOD)g@+_1$5I{6m;4*z0XJrk!OAw-YDTzTS8LKIi+Up`Q`2t`9WNG*_xKh&1y7I|q> z?mr3zKW%v<2~aK_lC?&|zLrzn-kg!Us->+XN@Q8D9~;&?9BM2!(DqL}T#GV@oFo{X zfZ>VVbt{=7b0U)RqpA~3XP42 zG~*$dVd+Hv)qCpATz=(m+gR%haA3N&82;D-r&Yw|)zqe&1i`4|{GUBn3rSg_Q)Ofl zdth_%Fh_$(rX$p5FPH>}ZfbAbV@oT<q)V2eu-e!kfUDa_L&+YbfSWflVup@%UdHm6K9#F@3L2U&-q%mBstc=xE6bGZm| z^3n!fHrM%%uD>&k{SQJper9;-VfS`1%xefbons1I%~Oyw%>8giPgeGqz?gq5I4<{= zW}g|_Vh%5X1^Jh5cy3%;%+PWinCBT&W~?tqAuN+ExYR)P5B{uR1wR(7EMTqRA~?}r zG;m4y(X3v_K%r(g6ZN2<_gW@F6%94@UVAT=}7%IXy?I0IV( z-`_R4<=QID2#y_Fuysq+nhJMH3UN3SO6bHHLFmL3>@z!@=gjoIuU>^Gtr#;eC=2LW zw^E2yIKw=4)ixj1-kYjy5Qgw~MAqDQtS}b)A`8gPD7Ez`a^;%gAxA#o&seod`|Yc1TELU= z-+#m8SMF(xGA-F==r6WS{+ss{nzDR6bZiQ2C6fP_zpUt;Ydov3XzPh>l`3rX zEAMZHgoQST=@+qL3tcT$3TMuh234iMt$A^q-GADi{&Q8-U)V@pyRMX^2^os^zx`+} zzy9hy#Q;QXaz)Djf8VXgyJGHv>FrK~{^-v#d-Zzldrs7h41XFg|>ai1}phq9=pJVxC!9C=FAUvm;R4U z2a<#zC)hjRcOEH!D5;KXC&(SwO@9Be$;a;>&Lc#_CgZK^zjAf0?5N^~>&dU(Gx>ji zz4S<~cuR^lO*RzC8Z)lFv=PUz2_H5BTSri9gya8CdsSjiLliNup2~@~wazJL{de1o z?X}|IiOHk47xTK+H8?eP&yiXubxHioZN)#?*Z;5_1`JxwF}<#h&Y)wb-|(r{h?j_K^7ICubU|KDlxHnDW28}@NTLut zae7b^DRD!`P~NUDeh%lsa13PQGoFZ`@Qt3nkOcM)Daql`#`b#=sOo;E$}kpK4B1#u z=osocA+-_2gMm@tHhy~)3i0GC5ZGvlNS-uQuqj}JeBThyU%%W|wA(fI=;=DVzpblk znp(sdHDV2hjw~K^m#ST@%(v>4k@G14Havb_-_?F|BbeG0a*x%(IlV0JIa*^ZbmCls zE;tYL)QU-3I!2`W>Q%IL%%~kRr0TW9H6Hx(cD59@@2}U=`RVECsua50%g2lxsxAX>liD#Z)r1Se(zw)n0#>ksOTx+m)(S3y zOI^Ks2ja4SRLr90KU(*W!e#%eEhadQ)qLC>j%)La9<~`ySUT)ky@D|h5wnd5+QUrk z_%J*(l%Uh?Qy9-+#v&bv;Yn?pwjd{T4?9l|Pn@Y0!%m->HjVj}wKP2Dl-uP?)5&_z zA38JS&a4o@gbAYI02AvqF?4O;X5V_P1@UG(+MV&DlT|N|Z!{J^gwF=m(tXpgT5|5BE<0z|I+ks3P^L z_D-m-P^OWkjrD8m9wHgL6z(5CSS{kAD%s%GYX&_a#Z9{ig6z~)*J1pOi8P;80-V>bt3wR-a^rL7i=kR{MXeFsxPGX@`Tbp$o6-4~&)2dD=i`hjZs)R}R9RKR zBGGz$*W^HZkqRZ}S~kv~v|aXS|HSUAC6 zqg@_)_*CISjkkX=`y}fu;IaJGS zIwJ_bcvoeTz4;%ywbmSgttgQ$UVG)BEy$uOhPzwX!5-V57NWD~YaJw!*JO6dme0y3 zYIvwZ|3LdVBOc8x7^v+XaESc>#KUzDkv2w_k__)e@}V1RK^Z4i#v!7U<(a+3tZ7BCK{%!5_{DW_o&Huwk%5!?a*?Ro+yO&LsQ|Zp@Y6Z$=@buX-@$~Bzv9yI$>2IeY{wMa-KziTJwa!Z?Kh?T;vWUT^ z?e-eNik4~}{Na<6fAV0hAP9)-cGk6@dA;s>d*rrS`1t6(gU>|xRzYrG14!@WAza&6 zUoKgkw{NQU(x#2}st7*6Z~8ZqD~gAEPUkpTQQcoJ8V_Kq9l3(^k?_hPyz8bs_Q3dDg|BV4DhH*-=&$s*o_X9@MjM$AiEA<;OvEN z+JvY7#k%WEZC0Se`OHsdpsK4~5WOme-tew@j2zB9su_%}%+zM} zDp-uCV`*=f!0Gk}kGB=Z#+y%b7Hr_k>eUL~R;(;wt>C90m-ov7Z^!D@W&e`RBTuzl zhPHVhI%P~>j3-c@7a3!Iq5xlScZiHZZRhNJL=&JK_K8LIV>fyM&`o#xYhn&My!Gt{sBH9yt=kI1pBKS$ zEGa=4q$qi7egrdU$04=lkV`$P%I5SPAp-i*Q&Xw-XYiESvSo^&)hkf@n-5nmikL>> zq@iS|f+=Lr=fot3J)g0G+ALM5O?nfBGQD?gt~||Qn!uCP5~+B$+oqxu?6D;mQvUjm zGE|A*^!@Igt#8uHp72oh+3Ct73jHb5&)lK5d=ur8%&d#Wx;&Pp?FTB6yZ?sjqcv?Y zqVnD=stL5wAiH~V&kzTCVlM49-W0m$sg3EtPdkj=sY5)Qr_azCc=G4NCoBDSmOZ_vUM&6KjkPGL zMZ}{M=QnngLP}!&i9L0P7dkh1D9*QSs&}jJI5hdK=}W!=9S>WArpf6ekqwEt z`}EjL2iucet;u|dS|V`lOsx_EJ!)OY;&7CF?uA;-N&mW%?>bU1{HoVHwOVXcz3=8~ zDJeP~%H@=6K(-P1EBDlB#OZ>NHj+iB^8>dQ8_K~GllANBwxEnXo*0doGY+W@@!z-i z&^yFNAq{(u)?ILSLmERd1n6*$)thw`&Mwh8ukUpINQX`}q+Ys&OhtB0pAZM)lj zot)9Gc&On<^1kDf58*j6S#w1-_EcMp5H#n-rgj;}?=Y>dl`q0g&-Lr8U&Ne6R?>Ds zHmY41aeaGdHPE@Qzdmw%QGgU87!vo~P%+%PVOV^8vE5jznHh3m|MB~4k>VqFR1@yF zZn*F7^x3-F^V8<1pDYoAcwBZgLAzj8du^6e8#7>b$j{sdXCsJMVQl8I$Dr-&Ru=<> zH89#spmH+W5>JKi?ka`K5DFeZRHha&CJgNJ?VaeV5(ih3|MX0C0fa6^*9&1L3B)?*vpTd3^2+^?80sB zrIYC445F>OngWKnm-0&o>dk8$FkHiys^GQ5b+yblTa1MKaMP2am`#uOPvT_a6uWn{ za(^*=z)^6nI*88o{J)@R|`hqt%8AAYUOrZxHM%>otuh{q;Vh&Rnm+hb3TS@5* zBGuVU5EpFGcqST{3szd#5_Ra#^a<`5bk4VWY$f}C*bAhxcbE-QYY!>pagx+Rq*9gM z?1IpB#`EylCp(qy{=v5@+O|Yh?vzX*Zb%C^URlJXKpU^Bl#eVP6dLStjz^KhXegJO z6*&^NR}NKTDJhMs-N~$NojqU63W+eUGFHvYMV@u?(pO1fjFOTl?madabg@7N=&$Jg6Y4y$56g^#YT@(Mg+iQiCKdhkZ3;RPS>t$_OA)*so9IFUSUqC)k z8Uti?L&JHcHbgPW!iq#x!hg-Wy6KFPbXi7h^k*dtXOtVyAHTnD{^A&7mR+2@njko& z66Zwbp7x9(f8M>N#3`?~w>7Ld6SlQFt3CZk;V<1)3wa56818Lx;|vkq@!g%JA$^LU z8@yGk$@WCje0I}t8a{G+?TNEqfocQZUadV<1P`95MHW1O!eX|>WcShf_<+L?Z>>Ag za?4K{)g!H63|)KfW&2?!{s z1cZ*=*ABPm6Q^G({pOAmsg7v(Lm4Md^Zji_?D>7QEGtW|&!=vh{M)v)9VH2%f6^8+MCfo za!2in(5Arp>9eJTh;qgfT)C$Dj_i%?MTA@;Vy_GYW!L%<@xb(|m`w!ukXGc%kVm`^ zpDO1QMpYS3DN{mWM3i}=9HZEH6?#nXJ9go&E|k}g)&LVIhIk$xV9p^uQ65h~IXw2t zY<=+7;S>ueijkA^#Xinq{~f7KiE?WZEE9Z5Iq87Y%62Fdiu9g^r*_-kNeIxLr~DsD7`yi=I@so zpOEm5^&~P?HEqK2Gqv`Y!_1l%M3|wk(YP)_*T$mqcrq|XcQsg$Cs4ImZRp+e8FadR zZziCR4+F#_?aPn-a6z5Hg0&~xJ5@pt!-A_$9$1_>DlJ#^!P-XnFKW0(sx&QvTZEdTWyNYv+;S6o?eqUwdV( zjLZ(pRl${A&5BIq~`Zw-l!YY|oK8zj8kme`u?Y zXaR?3_tr^@B8C1>&(vj;iLbv>e#VpVUpKw~R5|{@)34W4lPuy4-Tr_2UfpKp+%)(d zR%5T8Jb2UO(@$1T?vPH6&50175w%4!uV*zRjv-Q*m~;DGTk2OMRx*rJP-S?_Jn=)F zblhuq2tp0%8)|rGIm`*0<)n=Hk59jHc-5MEl@(|p!T3Nwk(;iq7501Edh2KZX7YF6 zRs2zl4bNShYE>&|6IV4V*ClL>g2-d$>Xoa92KMIbyLGE;C8dKxKBN=zxB`%^exKgM zMJ@K+H$+t)_d`^r02vQjN(9|LRn$_*mdm@V7K%9RnZ6AQ!W<>`@g!BDr)kQN;Z)&j zSK95<&z1hcw_2>G=1#Y-yQ*?%%RQ|z1Q?OF)S&hYBGA{rO4&#J9k2$E)qB z{4SXN;Wyqz1ypefFvMG%Q!kY6IYdK8KU7E;hmtl@>+0cGJ5|cYRT#uJz;n2*?<_rm zOp`Syky$3hb_tyq1=%bf&0yxe4ITY3Re^%mb&UDPY{AT7Fm_-;hG2=Rj(Lk6V<%zE z*~=MA3t|b)KYF}#JXyg{8CDjsR`8R7%Uiu#CNAj;JM+`>_F>*DKTB7w9bUJ&Ao}mt zg0dkl@!O^EYEOP`9E? z|NPizmNQB^LL4(Fn9-UGn&}#!V<}Z8)1A5VA3r@jW*9JPBTk9hI2kGFN9LWTo631h zOD6Gohbx|MzFITvU_7sd*c}5U>qH9Uzv4oyrz%pZwRf1v*uUJ~8LVu}>Q(e)LhSIa z>no&jJIO%OGbv3>cU54YcX(sQAlZ;|e0OKs8`oBuEI~cXKTh z)JQu~YY%J zWPAV3Wr8#K(_h!GAI?W~3}^LnTS;MBA$g<2Bc~=GZqL*N-1wR4&1(t{SqCDuF45%gL=32U6(I?W=3A&AY#HpP{yD| zy+dOt@2e6n?l~s4eqF8CJhr0*@vy7PHHV=-2-7ZmhW`7nOg?sR>4X}4(>Lo={?veR0g|{A0kD61YNYn;xmr3QXoI+BdHidq8usyYn!Palbi}OZiMiEtc;4yCS`TM)-4@f9ZXMKvn z`Rmueq<&aFD1fW_zxhE6NPDLni^M7C10w(Lp1wRwFXSC+X%0&EX_Rs{ztp|f*7540 zA+|H<^U1_2QeAo1VCJxPbrld3ieA;(x|CWW?MSWtYlntEnSj%0tUqOfxxKN|oq*%X zUf6P&a|&Xy^8^Auro49e!kY)QRISE#DH>>V5-pAS)hmMi8PfPRj|GV{$fypCjjP}I z;;qFrckDd;hIw7!w@i?iz;YZ5#+<#pg!u9`U$ki}Siw6MD+^dFSi!=OVB}AT?;QB0 zS#b5r4AR`lPtnZuX#C@pf*FJPobt@?Ru0FMd32H7MlpjKg&pYTl$j-u8Rq%t8G3WZ zRSLZ-^j>Q}bL(}`WuecyAaoPfULI@C?5Zp8$cb3^PiDz&*~oe1CT^Jp7NRkt&mBAtjqcCCcHG)7OyoX!I$rLv$J>c(ESk z_GwEOp%A6F3o&JdP}JJ=M`sAs8wbA=k3-Ef~rCRwGfKtZ)>-cDCmOR^CkKGi)|H2Zb(4|^EV$V{hw_w zGlKHAJw!$`iX@YVk|kH~U%0azgY-+v@PS)v$Zc*8u*G6Upaq{578C8Sy;3W2RDJN) zy0GXnMsW&4Ewi?Jc6jJezYyH8uH4{*!*9P<_kKRVuMRN?ZyKnjI+T*E4+2|>$(3t{ z)Blg2p8WisRVatAsP!OMtpn}#Mu;+MbW$I`ueJJkt%gvpXO&$XHp;Zy!XL0mL|gDP zr_3}chse)ITlZ~mw>bUYmuji;fz6XYd%oUEu2D=(+PmJL*;^s94R>d>+piS%9G(2~ zU1dlsP;zY~NdL|w^`h&lI^OP8Y&=@JYE>;uv5vo1Y1;x;#*EPYuRmC_{5*SZcw7Iq zS5BVZTkSpH?v4>m3iaT@la+184HO%@%DM@W&mw8G9BH9T6&`MAHHNHXGUPf_)?I1a z+V0+?)piWPpA<+rYu(%zqYN1j9Wi7a)=F7h&rQtH74RF_kXAdyY!l}+ zjW%$I*_IPiK=6EfXPp;%mSCCyoKfqrR@|m9M59*gwwjg|VUFd7L=2?Tmh2&IaTx#M+V;A}v_&mMwpKUV z%lMrXfMQ#HMFkLp5hc8$9koCf9aQC&=Mk}SqqE)9qUTrjKpPlFn&|Nd9giwyKWYg@ z6r#i>wPS7e&+VK1%H0=Qmx3+#)(KRmi|945-{@^PyX=A(d#)fFaVdTtId#Eew$M`s zeJa{7AE*`)jM0dv01AEOE{BkBFS>d-3}q}U@^K?D%(_=&J^9feW87HGS!PO^pK0t^ zL#8fcwadYnS=iH+mw~b1%<2^^=+fBL`PD1faq!aG@?*g}-@_HGU@@#LV69*U3**hK zUR~ga5mkcrxiAS6Qz)P^B@hEi7OlgP|0^B2@UNgao% zyC;K90fk7QoS9h`&N_oa_EBZa4P`+ubc8497RUP#<6+m?p|($CyY)n=SB1Yft5kw5h70Jp5U&PkZ`&Et36Kq{49!gN8H>#vht{atM5Y=wVTYFCgi-;%RDCNt|>6XHed?xq1ZX9Mbrf zkk^{a6~+HjyRkxFlQQPhC`u6b9xDeh|J`@hqh7m@)Qg-x^@GVT-Z}a8dn@t2j@$Ru z2khQ^IdP_z#*`zDq0-LD@U2gWy6LlRJubEOppfp)A+P*GedLb1 zC+qI(s!`!#YxDYA*}1-5rm&$fO^`(m;Q)O8g?h(4g&0r_kDITZoI6)f_9X)EyQ%iM zJ<2d37-v4y785aj`_*B1Jo9>Krsw5@?FCPxGjYSZq1CQkWNSU*`tszaYsy^0f*+65 zgwFRd&ShH6Dyr>p2)Ut=5o^toErOvWBRY0zcQi*4&Ho{*(YN6?nl zMRtrPZ8JLDlwl&|j?T9+C<|J)0;NE$;xTla^! zfoFHZCfAdCHgLRpK+|e3NZi!3us7v9f@*g0~kx z`U~rV7BAI}@GmX3MC~Qn>R3#w8dCV;uXQ-@zxk`Fj|s|rRsLo>gBc|=BCzqwL18ejc3xx#{cfo-9NK8hG%;nq~MRA9U>MD!S<#wdXgU4hv)FA zdT~rlJIr3~E$yAztV@;omZV74;9h%wO*g9W@1B}`^uFrWy2+wFtHMcvWz_IXUKb7J zURD#Do?AE6YpoUHm@TbVz?>%FiIwlXHh2|(il^mrAGl@sOyMgB>mAYwAVbiDkOWoI zzUy!u@?T2)@Xf`HhfYwS`Q2NF2q*ZmN4C(j90rKQnXtL4uxkVSmElhp2tL&w+zW~_ zT|EhuZ8XrF_S(#?X^1!<5;3ior6b};>9XA1Wz&9@t#Uov<2ckGk`4tsM}gzuNM{>Xf`d>d>i@%4=w}8Uqm-tkj)Op`Z{y zqN)$xTG>9L=0H}jKrlbuZXe_MmtLBzYcHNlM}Opw$$#-cnIMurWhLE|2Qza66t8r_ zXI~iBM!RVmpnTq67rIB<^M;2{)>55w6TKGQrnhmq@)NAJo_2PKTOwAyHeCsYgjzqw zr36hwM8jZ+s|4-o1hT_@H`8TAsx}9%j?@6G&WM@!}ZlK+UT%e zW7keewJH3^r%V5Px3rFHFCeE`6^6%Nt-{gM!~54!n7*eiu2J>$>y!82S|da+@Ih5a z{L~P`l9JfUzD}#@ovMY_Fo&nkUbxLu7`JXHCEBIlP|TkO^b1;jw;Qom@}sDNc3RY;j3}i8#gt#3kdl z+S_igQCo+m!gITsX}L1*jO(fk|}<6&J1M{9)& zX@%MzYMA$Syq|#+1igaLRCVO;xf%4>j)5{yTpB&#GiB&)Q5c(hZJ~z&XE38`nGn)i zqMgG`K$fF9Jc~(Po_i};!A~4k7O+C0)?srMeORrKRR;=YKht{w?WC7)zsT zSkCGdENF4YlO?=bCMfj756e`Azc1o+XY6}956Xo&4^tmlunH)-YRwx~$HLi_!#8%+ zgCQMJ7>v~t876H_cC2BH+%46a#~2?fXB#6rb$tpp&V;9C_ESW8^xL{2%-?))z4JEV56*gsJ9{(}kNLN|?Cz%HiGfNgI-cU{jUXEoIuL3Po7~ z%-V~yjt-JrZef2?$Crv#oLF^%B!|Y6}7dL(}?)d;r31Sx*rVx`tiD< zgF>~K?UP`n38D0YeHs{}V9##eeud$cL#67gFW2$}LsDWXTpq7L_rH`I61R*2g2^rU zw!N*YlrujZ?skc}-}_P#Qx0&-yClhRBi()cO#MM#ddwvXQyY=^oaWn3@~m^ zS#PUXoJ5Z&3UtPStX@Hak1bZB^U%r3sWWw$0J25lt|N7Ojy^(6Uu2~~Zoh7IoenG# zG!8%6Ubx0+{7k2lq9h*5*$jyU!C$#Uwx?-HJGx`}f79(1*cMCwgXqf<;(Em5`b z-h6E>W1#~p=^&kANIUz8U=C5}sQMqiUW>f()*gfAfO3ti_A>acP1y6)n7C~oF)L&_ zk9KT3j|}lYbFQul$4^%;Y;89`Mvo1MNNdGhfj=1%lygioZU5wJKHxCD%$PxVw~LCKJZtcl$qY7 zS#LvcKHDC;sW<^MRelcQ5;g%;0AoO5^EEZ1_!IuDAr^BRx1gEn3x^^Q-wZP%CDoXD z$hUsEP8CWDnrS2)I8keV@7Xel&=Z7*hlUgU;giGQ#9+@ucB9iNlLxD9ywax%oUx}c zJp0=N*R@TX8U7Gs)L|m&5YeFy>VFBNP&rk~=|MjVC<>%mJNU|coB)|q=%&{wY(R2VEqUmDKY#hhHi z)@5L3{9G#1DJxjPJ0B|xSS$EjjkorfV_wzL%1so0G|)_wC;R36w)L1umB()+odHU= zG3GftWlZm7wd09ku%Nl6fq(5AUrHK`5BuuX()xvnOD0}<)l`odO73-!`1f`V_Yusi zn$h166~@y+$RvA4gKYNd>Dx4dFoe_e@zmMMPdY;Hivr5#xLLi5>4*m?NlOsNrc*$X z&nE|vt7N*IDtlo$dA72h{4u{z?vvq2A;Q+Ha+qni0-ZRMx;RWNNHUWXI!uT%BphZo zsbHfg>9JbA-ErOIrS_JPP$Dj#QEvO2cF&r8n9ebqWR)uIiZgr@a0MbgH;4v@2_UJY zrZga?v2qf!Uw&!wj~^V^Y`cyOX(aT9Fo!>f_Nh%3kW~wVR9oh_@tPL@mP~$cXQlLj z69|e}a%m!I3lW^+14mEQ0u3UbQ1JZDbcroK*l$^1eQkSucZuJ8Z4DuVJeuuM(aBzW zWu=!Kt5&r8UleG-ESD36?!O4sU?A2O(ZVK0uW>%Tapqrqd-9v@>8C6!U`X`hmHl6C zE7N3|P7n}nu9lS3qb;hmHw{16KKbuZKdKEsiZXQH1WlQtTJ8E_y-*3m&DRu>l}hGM z)aq53Df!wfwW_67dTGpLO$U#VVu-_#aK#G$d8koqRu?~@6EPI;pYqf|YMyHXC>%Ul zH^NY8bL!=!)2@LqnQNLjA7I z?VEZ!_o})zFPVRa1SpG-7-sPzYKyrA)5d2}x~08y&6O%Gjh~b#q#E7#PU(G+YbH3Hw*%v7O3 zBEaXg2oG@xbND+P=2MBO!b9BPf3z$?T&lw}ZCs}KTN{}btl%=RvVgUMztwmrT!Q@)EI5Z) z5SRC}5fS_480tI=-V!zA0Op;C2a2lsU*a)yN0fB_%7R9MsxrsAq{b$Ni}OqTK@wtm zUb9S9PM(=lR<9szp`@JsE$wcy+;BdlZANok!UHq$K_4?mPgbuWwP7%UJKA33dexfY zFV!7m4T1UJ-R;A0-E<0|F!m=zbLDTew~#nAb@_ z_SW?&7KZe!F2--eW@ao0Qx(T>M*~Q@YTs;Si;0-C-!^^0&8oanxTQ?VaLPpc198{BueMM&at`>rE2^5lhq^0qVS~{g7XU`Rd;CEiDXLiC6@vpry z#x+SpvW=}eNW-)E9!oM$9Gp!N^sNkYh{!tCK_Vnk1U-A7N6N$&$vMX zB8ODS%|QypwKrXSp*bxR{=x2gvp5atAG^0WZK@(tt!VeHa673{mM1vYgF}m9^Jp7F zm^lXcySe1CHrm)#kjEMi0b;sa6>-d@91<)It;?J>F@4VslP|n5yoOoTuiaBkput`Q z-hWHcAy$N|*xKONr^-~FpDvN46&1|mb)j$#!PA$43N36#;^lXScIeoS~r{6F@x#6nX(*wNC0Fx{| zxo23jVEW~Q?d-e0viK4ck-a0IWaGO^(a@xWfxB|=1^G&+ndUzNKh-CAn0;Aof^2WA2`rfuGrz#EXrYU#! zeYmZWQM;j~`f6>xzZ4wSNS?M@IZ6y0l$Q<;%al16K%BLRd-jYH`G5X(E!4(I3bT?6 zszmjDw+xzTz(a{L>^M}vIQShVVydDLp4(S#SGKVTeWXF?9n4i5TNUsP+m~J(7F+3D zzpn1miW`6uyFe%Ll$|eJa?RgyuvV{tNOuXR%`f<)w-+V1m1Tp@run(VIsC%*Vd3od zjkR{nXkV-mjk~X_h5I~AmwV_G4xgOdvZ2~u@oYD0MRN`B7Nv;C7B&}?+zi;Ox+mH) zzbIquX=d*4&K7XY4}rrN0v;MOr_NsBNrC>j+UYn`-gUUD`uw48-&^Q|Jb;)%LRbR<{R1-oAjWU$^zC3-l2F~e>2_;-g3ui zTfV|KX?i*0OY_XsUOjy)JfyIk_=KUejVClg{z7Q7m8sZrbY|uqw(G90yQT6K@L*=z z6gFz3Kj`xn`j`+cP!U;Lzw@^3rL7DR&d$blB(X%02n9$%=DF zL)g-BX>S))NH&!>M?|?zv+TdIqb9z$Z>njZ8P%iH8x1Hi?XC=e{cz(+SNQ2CE9H}A zVSD93B~;4M8ON~M0CWC&44r{9lFScpt>vfCS-HN&A%e-!Y-i5Z3w@5a$basI%A@*w zcIdfnW8LedfD;hoUX}&5u5%^`3K*mipKdPW$4t4?aSZYUqp z*FzJgJt96*(Cr86okud&yEYB}$6~+@lt`_*qHbr%v!#${pr{pa&M=RsuOO!(3Otc_ zIrz}6rLB~=1;lh>nik*eqd{}h#smSRTeY-wRo%qIoLc!OJ!;d%Wb;6is{QSLrC-0N z7S|}*)n4(Q1Q;EILZm*wy%t0SDpfZ050U?Npji6@C;h zaW4(OZ*vhr(1?8HrCO`vClhLoSagNB5nE!B%wJfpTs?jLS-TUex6Ym*eg$3jtyhPK z5D{H|zwpB3w;me0PhbIOMiC146!@H~JXuVC7SW6!5d##VsKh?E1+~={S$$G{z?moh zIPhOQP-8gVEp7(qzT@?FuxdqPrdd@AT5Sj@IfbqN`F zP-1a1OI}{A&x7t|5BH0eW+rpTO|NFo5hOY%oeTl8Fyc$cYoZ+#s_G`jv!8`_Cpf5>MIzf zu5f46$jkg4YT$P%Lj$tAUspMcz@cjz#TpUGcUu;nHPpbcaDR;CAMPgj)6UH@r@+^L za5ry+LRn`@?N#pon(&FQA2)5zSw`=RQcl&t-1!HlJleGNf(r3h^p+2U86gOtjpn*J zjyO*gslJ?|wi#jg6bAbxyNa80ajm@f;pKyr(DZZdv5A+M^j;>H&hube!P|35_LElD zpvBaUhgQ)!-}+_aj`erG@qn(#t6_ya_W$DQWRZJsmaIVtfl3>+8?K>Otr{%^J5$8{ z+*~!gmjx}P@d%$m0@Z3V-T}e&Kf{bWeGvRt_`@LHbmBFSk1U@RNgHMoZT?NpJc&SR zz^PW#RjXF6HLB@!(j?Ohco{vGDS|uSN2xL*EWZ%aF_tHjS&U*ktV#E#35Z^^4PLw+Rfe#lNJt52o(x3W@SO`-jB`62VZ%p&-}IPhkL z+kJ{xg)yPvxvqW9df6toGrlXoD-Ow-nHE=|r=tONuVTx!ma@cU1sJWy_zISo!cSeO zB-g?Sy4(z`G;F}b3JX($3GHM(5|UE1*?@(JB!IKFi(uEJ1Y7^e`$Oow`oW4d6U9n9 z_7^`B-}OlBA_Lg+>m!MhUz<(1V3|h>{g~CquDi$5qm02=krJsV~3ECKodW~ML zCt)M$R|8^Mmf~f<`1ly;UF0HXb0*y3Qunq$j5p_Ybg3=tiezqN-DE=1Dbb{|~!-PeyL&v8umNbhK@@_DnKm_20EHP~%J)wO9PvsBM6fxrSQdfKi}RFnzZ zJaPcpFJyvwNQdS&GN?>j8)(p2zNXW`x;4uR!5T91jo!G)DH(r%X}rh=t`3Fur3Gcp zmnf&j41+_W0D~r!W3uMH@^hIEoJi4=RRTfTnQ(vML`pV~VTa%h3eD#3Yt+tyD_|8=hsim=qbdfO zo|M~Rd^$ZqQZ|vIv#!4(0E;E>wh8!ILSkme7Ogp8^e4#UH^j7HB{yE90&}u5L5y+9 z9{JDLOkmDA$d`S+(ZF}_yhu=vy1dr$h9qD;i}j`2>hPK{M%Pfa!oJ4S)f?nxic#d) zZuu+t(>~|sirE)71kJhx!*_DW>S<`v|AFIh4Z}*Qb+_)AFvVs-b z3$%aFgF#f z5^^cY@%;3q$Dcf2kDgk<$uWo9jgS;z(6ex*Nv~YOofOT0$mBl3biLvo#aZI+fa3ab z%X{-WB3~s*QTnp<^2SW~;fJ;9pK%_TzGqLs`UCx;!CKf6>sC zj`HH_`N7Bj_?xdQ4$MHgThQG3Oqj3_xbXqFMyYE$nMJQmJMg&2S~%($HhJ1paht$` z3{HP1Tb^g;xAO%ZdV#+fZe8eSVx1=U0&l#EyVG30RK0FL6nIj&NcBu-Y==E2nVw|lth|&Ix-om}=L%41albsI)TvV8HR5Y->zLPtZhBBE4uhsRN#HXgs2#}3Co*H(X`so^ zmrICF%)mV4y<8Dt;klm-qcnYpd%9h=T{X|!L0<;4P_98BCxw1JNp#pYZXQcu?@f~P zC%IcQW>D+IQI_ZVDokqcAsTBLxx~4#BKM(IE!Y9?D1DLPVAY=v6_&vd1L4EH;c5Sg zTw)++-zWd3xWWBWK%oqSn-Qxbc;Cigu&Y0Rpf1RdZtEERb-9(ODOT5pxg2;g(5y9! z|D#c&^nf*I(~*Ebk)54}W)aVzt^_|9{4%+3CN+@^L@9#CA*6-1m9erTlDu_FHu zE{2g%Rd%a(=ag* z;|y+?)!#F@Y#Iczee9`swlC#y+=dt5Q6@R-%F zlr@|v8i%*lJmxXKIC+YODg>d}K@?cH=M+K)c7@f<1*7-{fR7BP`QMdsMlEE=;8HGr#nupbctHCo}$5 z(9=j)k-O>8`0Akrplp~Q5F6#oah6wERNMqOwL9g2^H^#O^(1lpKheFcX6K5Z`J*PK zt!%8ng5S0=K=T#jj(C^oOq}H_+y?cie=I{k!8&uDQ-!>v%d4&o=+m+)0SLBhXAqEw81o0^gd5flLOY?~wQIc*n`fgBC=AWR~cR4q!7 zI^5MGw8w_Sb&#l+@gSdaPHY#bC+{{ZuL+B7!mbEudMV+q|MWlSo6b9?=LfkWm2x+Y z3TueC)-So1dBim*Q>KRQRyDPWRXcCo_U)jj=xPz}#SeXhEHb>UVx)$L zTV%`Y=uvtL$%%!y)(aga*1w;~tf|(6PFsL|;}$2}RTI+sdF3>^T+0@YcoW%PdEf>D z3;tmv19GpFX{nwL75s1uHeU~q+6_+B=P(9MFdLVedewbLTS#ikKcNUUP5r9^5UMsV z&yIP)I7{fc%R<*=0D+2hs208b=adyiF1QKNC4)P2O&+}-LJTAf?^?A zM@&O1aMItRj@$JVUWZW|2XX(Lr3m^f7;Dr-j#6AD*29NcV6Fx+;9n@pBI!<0E3>ce zF;93^dgWYgLzj;ciNV~kT7m_!?anz(%REca2WYN{{>owF9)5#>N=~CH?g8w>v?Aus z8&1^t^)FkK?%nW$F7oq@uO6rw{{r->P*RR<8}Bo5A)Ix$rGY_A;adt?Q;PU5__j^< znGm*NAm)@LN$s9rs7gCwHvx)zcBkChN_h}Fbfs$@B&gBYs#Wm4Z91h==JX?npm_Y% zW>Ri2y_b99e)lq|7+m@HLyym(8}DwCwhZiET9A%=ah<_zzhl8fd2%;AA1%XB@QiazHmbb660{3yhj;DHggO2rObNC2L3NVi;kjtg z`iJ0{Au>pLz97LrK=z0t1h?>`AtL!pF%((yy<2prd&1y$XU0GiZy@q7HWZiasrg1>Ntw**s^&R2v{yiVF{ogFwKxwyGE z>?(0pkh_HqiQt?l>Ac^X5_TFFr1{K~n`srBZ_mRiKUSdZNnhwAyqej&1WkJSsV9i_ z37Feq#cc~7ZBh6(1L4WBTk$+_BjnSwoMM8cDV7#xoymVRL0|0&wDt01H3QCju9VJE zwc*InUixG}o%|L@qE!!H5l7gdOp7}^5B}gE*!4uBSo}wCGQL#u*DyWeYUW2|*W_oo z+~&^}%v8!6J8kb@*)5zB*-oA8#29U44$%wMi6oNXUb8#Gim9{Kt_ZUb)`OJ#n>sjnYIUkCJ5Ceka zx^&lfPw!hq2}xmq9QJ#KQIUmtS3^w9HY=7Fht2_g!rh+`%4Oa1LiZ$QLxdSf^@p<# z=stlJH8QBX)gN}%({j5{q{p>7*(Qy=3iN6hh}jBZu$v#ran&>mC?|jaKIU;Tjv(}} z(=OenLsNC|!fpBXYI90SBrrJcilZeN0>g(O2I~$-F;t{T)<;W7>V2as9?27P$as5F zyRK^TDaNzYo;S0Qq|PyExLexw=T8mK%0)`wfCWk{wrq_=wOr)ox2`ynPBrH|B`PB2 zKOzBvKb`vlrH=EiV|5DFCDCbsCbmVqbk;8vT+OY~FS>rTDY3KrGh7ag} zhBesCUFAuHKlm;thO+Oem?1#R7f)o>3{CczJUvtL$y*2qbhe>P#?6zt93T(UkD1I* z&qoOG02ol@5YyQgG=q`F)Z}mkYU`3FX`+hXqJnKmDseVF$IM|g4wsIn_I7u3B803X z?HZ;b&P+TN^6cWGDo2=`*lp`63~bgYy%Tin(uL_khxsrVi@iv?=bqA4rRChbFx*q+ zp-!9(5XFl|RBI!JLl$swvN_4e*!ULZ&X25(@_x@-SH1W1)!`Ab7YsE7Y7XIv0$t$5 zCr`f|OX+_jw{f2=LMCt*5cuRyh#Y-K)`gFo@@?VBetjXDnUntN-6g8?WZ>_i2%Z`f@p0I& zF+&2B*Hg5(BlP---I9`h+(6cHUjDC)wu#@lC&~;f%j4-c=Z+DaX32+}Z`4u(8TLz8 z_}(D7*JAU}&om(a8{`7>=ua629*?NNp>I9!Q`LTGObBqQ{{v+^jK4Fxs9X`tEgWae z9rEJiNg0*=PM(|6uFQk@l-?5>dys(?`@X5UuCH43pOhZDkX}}VSGdX3`E|{-Nv%h` z2mt}2N~kokv29Ms3_~B;X&d7j_ArVVIpza^C75_&EBs|gTl%XVC&o@zm9cOJYWR; zg;R3}EvIlp3$pAfm6f!@JAi8r+v1;$D6m~kWvtR|W+;X|KpBNF;bmhEKa(;5On1|r zDIe!bn&`6btU0oO#1;zMMdf@Tg(Y&CVDJfxj02=3MdII&FB6PR8)^ZYH^nOUi4b#^&jiQS#;h0O0x*;fetz>FbcpC{J$jlXpQXxPz(=izle zj>_LemokGoi&YGn=aguv5Ux9yqz=1pj(2g+NWQSB798aITVCF6YJwzO&du3A*xPVy zn97?4zQ*PQVVTTu+vWLlT-vt;KP0}eOTR$6t+qNYm;2+fra(#KIUa!}+%^gQj1^ro`++PnYs^ zH*4J^i-tMRA$O>T%Q96421dP*a(pQ~@uL@mxnf>`=~64Qta{A(bvZNHopr-lKMY{F zKpC5x{=9s(C&(}-hURT;zN-N6 ziPUXcPw|&Lsqa(@H;5ee&W%Finxr`*o-fy%9w|kL1SM+)HSj_;s4gi=xk&YH@8fc- zmGAd{oh}-ddk}^F+Y}z2EnGN%g`p|Uj(Rg~$0gzK>i>QcD&52hU&kb4j{3It6_<`{ zv$K1$`yNRn2*N#Kt*01pzwb)p!|D8aCCOn(Y4LH<`OJ`H7A6;mnU^Yi)fu!qn73Q< zckrD=npmiT3z?UTpMr`(NgoGIrNQ>wKVCt~TIWYan1@)hnx0152%!ETOA9qFX%+Db zicL)@w)Yci0&cKO(BICdL0!PnS#e~TfaPXOS?C{n;vmQx9pIJ5njb4*6e>izP*nqE zb3!py@a)^*dWo(lKLBsZRaFF4`~$Om)RQzF6LFYfKXBF9^M60;6M5wF%vGbSxl-?g z@YOQQ2o)S|f&N@Ofvxf`9q|7o5T0n0JH_ks8Tgv*iS@rB$PKzW*O=5SHeK zW4-k?O6)uOWBXd_BMdL&onyuIbF-02y`2bPmUm9&IIdwqj$bqyUA(d ztgN&>x%P*eQBb1AvFh#b?X@jro-Tyh+8_Zsu?*tS8A$gr48xK!cbNG}B91 zdhQ0d@K&x@$CU8Sft9O$te<*ZjMm{ztKUvHbY1SR6O(@+xCd+CTZxS*K$==4*8d3& ztLNG;9}XRzzqDwmwVw?C5`YS6In^dSmLRRkBgX>^hsj3^#NR=g`h@uB+1M(JRPULd z|5g#`W-kp+opoz*+~R)){$Xl+5o?OqO^3h8&ECk8`VeohSFyOTZ7H~RJ(n21rSP*3 z&P~VaVL<3)M)Z}jB2v4#GPx*g->J2p_w5u~WdKDm^g+~!$g~HG79m+pY4eI~q@d)k zsn;hJdBim?KK^+pqB`X*+oQP`_du;u1I^vA6JsNHOk{pN3isERubpGO4;88yYB;~v z^(FF-Xyp2+<62)#b$$Me)yf{(?=}6F`0{RDLzr>vgGs3t`a|}DMfj4vNns>>W2m4- z)lzfMpzN#gZ#s>~Er0$+-@PWfM;tUO#kLtA3XO}mdC1<|#T=yLw?k4z`$dLHTQ33+ zi+^2?!sU3I?*4DmJNxOKUl(HiCIjYfRfh3ZT$>xk#YWKzus{gK9~OXOvsTSYFtHmE z)ym{UJ$F|f`<_VcgRD1!i|g_g)%vdRyfgJygur9XngK{P=YG1p_@m)J_oprn#ejpE zlqDliD%QHozl!tKYeK*fK&YTmQqPs{`eGQ-OwM|wYzK5E{}bb{_v5w(UkXUE=_Yf9 z_2i9jWDH19`wJOf7+srggR+^Y@E)ErT|ij3s))`K(mzT$z1+XZzQCf}laaPU%8V5bN^4$>*#l&|IofmjC< z`R18D2Ba=)KRe0jaaxs5f7>jqV)YU=V;(^2DFJ&#kA5c6>21W~xH>^PG`S|M#jHgo zm+{A%>-zn@`NKcc2s}J2W%}OTei<&IEEn>s*Ye}5q3F|GUraIZc?adOi^JRJ9-~-o zqPzLR8RH!$?ul*vu3TOr1n}`1qe)M~lwEUFyG1#~V-mN_;spja@npKgy{<$BuB3sE z#0s~=6g3-{-AI6J)4WzmGvx^}XVz1foIwT1ZbPLj6(zn=1?RA-4!u=`FP+{}l`3WP z&H$T>luo7`xKR2i)R;x(5>ES8`RBC82bM5L<&Q%JG@L3_53|-kFv=*|i_UE%(?m6c zFm?Q@y9VfEWo=O3FNb0U9^#vsgbgp;;Ej(UUPAfg#-d+v(cEusVJZ*jTXJ%GjK{T?x$*Wk~P z^=|_UA}?R9=!&8B5pSOTIjATKAo=hcpjm37g0C)9y`e8yZ>>+nWtxYg+mCPir z>265KZ_ju}Jv096!Mq)qbon?H=ccww92Ts2IS$>E&uSzZe?JCFaL6z5))T^2z+KPy zlsA{s*J7BB-t+Pq+k8i(n4|A}KD9zRX$~KEXMvMF{^&T_-w}P5div+tVQPh~V44AS zUS2x5HvR=>ocUCXKE*G({5kr1o4 zaW({)99aWil3jP&E5NF4%{)Oo)L!U{Mrn!|)=|AjeO|Zvp~WQBYI-QdFQyV(G;<_Q zp|QI4s=+)K;~4hkp{m+aKK+|z9jg&*ugveftmWIPl^oW8x`uH!*gXrt%@uIMzuHQX zB<|z}xRanMO`)AI6Zb`dC`~KH9668~0GPdXul7tKV9w|p>%}|I`N(FK+V@){BH%s> ztbWNeQ4xcup8$7MU1)uYDgd01Xn^GIbBmt6KQpM$8f)Pz7~1W#cmB<{j=+?Vy5htG z2SX6&u@?vOcSh~g44vom<&d#{Y+F|TbVZ66roEw`{x?(|N4;n(ahD{bZBwnm{wN>U z@}-vHu@#~AfwiDkzs{xr_V7h?v?+a#Fa*dAjb7J^H`8VUEVobo-&t}fOdp412V^e( zDnHx6>sFIIYc$I-p%*n4J}%5oTtO5@I+^x;$qFxZS93p@XLyVx$-yKClbL`Ul)z+KpMfI<~OydXd*051hUB^|75fUyZIAM;z`8JzVet z?OTZROy72M+BZwbn{J4`E^ojE`d8)HbkDlI?0n74xx$8bnDed*QK%n%O&*4!1L>2; z93N+Y=GsuOJbyPru^Wp&JPKt@6%Oh9>!7C=Pc*tw#|8I3;V;hHiMAXDMfl%RMc8|t zN*VeRG$df=RQC2%I!Y?zOM9hVovy$9uNwC2pifWUa)@Dd#T%TySV=~H`{3m}rMrS) zgU*0MGztfS@F%xQ1ed%!+t5GwGlmG_Iq9B&q#_HBuNIbuwe$$}E5p`OCF}zytMYl% z6gCEl^f)euE3?ui)_#rtjj>KP_WEj4e3GH2Iq1GVeR)!_U&Now;pa|}_$2?LPF)y( z|1!6ub~UZ&QT084Fl9_;IfI$@b!3RX=f*k4&!UOX$CQj$8qVzyb@@d6_YUTIpo@?y zGy@6TRk|(|M)kXdL%{pk9?*s2?dzY!WA!!wy&+`1aQ=$X+nRs=&5V6cQPPdHfF$Pa zp{-prkxv}`&Fu;NJNKQNi}G=R8YIkBuW~11)j%*CIe&+X7zXCJzZzVK8TjQ}p0i}o z!alkIseFKeek!r#NlsLAI>>NH^-ADY(zuYyM@2yQCv9zs5S0&_U-(gi2`?9!Bz+;Q zXf9}NRsPR#GYfj4O~#2_&3dVbU=86K?@^Om12<&8zno4_uv&hNS}zMC=pe(JJd83B zpEv}`)>o{3fc5li;Gx$S6-(qGNEdWrF_zQM$?C-$*sqjB=2$d3%h@l7?5X4=WPrML z3TZXFyDmnSFT8?1xs8_B(7f2mIl$?+>4h}KWhS0ji0L<#`OMzg3himOS3$ ze0j9RU?;{7`^rkgDr52g|G&Qprbv*V<$(xa$uEx)ycDOuMfl5OHlf(^;j|xe>wy+hmG%ZBkgRX>L|-`C|eKuzLs!B zTrXACvOHq56{_jqfQ?c>r=4&4+~nda-93PeMLVj*`&y6Xd(`bbEuDQa$bn%Q=npEP z*h98-#SbEC#}j<@LHYy(6R#iZ^V$mCZDyB;Q1};ksJc3nmGasGg5WR8=>@XbH!g;n zL9=&$SM!m|r`wXaQ|BO@Pr+cx`aqq})Bx7JR12L8T4Kudv;~u~Vmi%><{hD$c{El# z?PQ+W8>3FU^DLPpX@}8|TOBPLnhI}U_1b>nxQ68>{Dd{^UU}ITuJCyz?Jb`V!(r|Q zI6BY;Q9Uyiozj;H9D7FO=GHn(4+Kv7Q!Jd{=QMIIF@gPr2%xoZbN{+k@yDx3n2#)~ zQJaJs{x?h}2dJBQndg?(GA#-hXyPB!Zv_nBC-1!r?s4^$+PE1NB!4x7qJ->u>j}QM zCq5KA{7w!twHp7UX<3xave_Ta{cfb0p-j5hn<mka|AsqRYh6BVIYD%sinL&(5qhe;OV0AWSX$ z0{hTo6nWzJxIN+s2J@OM#uNYQKSI^OVi<^-gVNxQ9!Wm`LjU4Y68^ympbvgMABhD2 zV4VA*9z^c@Jwjz$Eu!Iz7U8_DQr=}DJ zLDYLj4Ll&tRLX)xr-5-*n)THM!+`e1(8ot#FG7_s4Qyf&1J(@$vZ@n4q>?{DuJTQ= zE=EodacJQu8d_o`XhXN$C4{5RAjyMB9_*=qv8snzH##nHh*3p{gVTR4(@eq z^z=y7Ubmx~@N3_`slxuG#80t`3svAc&&N~B0p*#B0Fy$CeIazETY49OYR$CIjq{8P zx1^?4QrUakCYP~CVqq##V+5&wN`K#-4E@AJ#C%N4<;rTp|<+qHvaALd0dVUXuR<<8Xws8efXpt&+p>%g=Cne;oglTOQ?_@Y< z%x&--oM1ki77(Vi*fRpcc}2S`2AwmArb)924?lpN9j&Xvb@kSU!H$@gldFHP3JE^j zCe>5c3KnQLTrAd@d6~+^DGYcM3)RpbnUPl`wFuG z?||2Rsh*MNyG@DB;bau9p$KQv1&Hn1TgUzII+jSTe<&28?%Vur`JZ4p*Ci_-l#H^x z2`qv?DL3&hdHZ$WG{342r8yi)vs?P97>@S`SNYmqd_M`4uZ3BSf?M$Zt56;F$Ngp4 zg^H~ij$b|7b9@v%2;gKdoR^O(%YNJYceuA#X*fdzqIlawv)EsF+$;1=j>w1@TB>_T z#~(GpYZ9$7-x=6$`aYbKCU_XOP<&*;c;s~n69nC+V=-Z#sk1jQmJf`(>+9I6IyDl- z1abcFyH-mEO*_7Uv&k-wf)t$Z!EeptE(FDTufkyI8Xf)@+(Op-%jZPd`qjE zfuCLOAw#>Y#3;_eA}QHx7#Dq$xjtK-g$DG`v1iWt+ut&YN%5FZ#Z0%+?$Y`e&XZsw zD^zsCb(N+1Y+F1e{wz41z(w>4*7x7hIK(kkGbsVxTQzGs@Pb~~4Isuc#Ae@8%%JJ@ z%Abu4tL1^~X*xVKYR7Jv7;{+!A+ z!X7Ys>a(r_>;U+u$y@3NUOtja9l!&OXgQ)lt4f$KH0uoPXs|iEXs`LXE*SX4C1+P!8Qa3@NK9}8!RA~*$9l=#w$@eF0 zrTQ^QI2zPEJfPpU_z&Hz0v~hr%X{iq)SPo#l|RRat9iSB%JBUG>uC=P|$)+s2_5545T*&(R;qTB|&teYj; zVb&jW@~p{vy~;*^yuJA~{G$ZU{MHxUn#!#irqr@8b?FXt^LQy;tzTc!ba<(jI(>ei zy1&QBwUyUEJf`K2#%Q@ilu{DHlcu5+NT)A}dx^vs(^nEyB<3I|>Hb0G z+liL;pMuQ#X(A2e#YU^czt2S<$6s&RZD)peoZNTMBLEhYE)SeB^RQ3FK})YZp{L8P z;O(p&Zc-RRj#(D8NPZ~Q{d;uEGtcpNOK zwuAsY-9LSDSTATXF_6O6Q$UPI4qW`(DN{NH4_q>(OGVhzX!{dwbSN06t0&zuk0tjn z@Mro8*X-vj;&&Dbxh9988BPv#GipuKoOgXv52+Xn1Q6^BZBCeWZ0f!yji*&(i2X6P z6al`QGG$6Ij-RF@81MP;zm0#c((_Nl@YL_Vy%cFmenPKn;%&K5kwP?!QwZJ5{+8dv zZ<6~|4k0J&BVc%VrED`R1}Tl^vXx;%gx3$pf{2Y$cSdw9n)iUG3~{R;4F{n4G7*~w zDb4W#6@t1{_@Xr|tY@s#CUixg`sc*~A?NRwRjpls>ZQ-u%7A|&YQ9O%;82BnAQ27j zlkmuFX}+0~h2B?r`+-Djr3$RQbF=avz6y6t^`rwDBE*AVsNmI((@q(kTI#ScM3)cw zt1ybbshRqcF$@dX+!Jh8OBWjt<=?JX%d*vfg864|%DIVs`kT)vxDAPnw+kbE>yt1x zb!+lgJX#$mmWVv^$Dv>Z!NwGD`>>H#)kVf*u<0nYmW^HBs z#FEpN66@n^ENo#l3c5iDDw=fBY%kO7dwE2Lw>@}|;jUJH%g65ztwD@rd%&D}VYh1V zG*omwCnSnzo(wj5bz57iz6?xt^W~4!3pk5>g?Gq$=!Xozm_jm`_Wi}%7sC0lGT|`+ zjA``b#PsT1@P|fzuI6=%q2g}>BZ|b3I{nI}ey~_`#zIbH9|iKHPp_69bp**RpUNOb z)9BFDpH*13i=1u3AEnf9mE5f-kapd{I$v}aec5#8c4X+-Tw{z;33o3WYH`fC*wiHP zTtNF?kfTpF8SO*Ebqmj`kJbT!6j#EE~NGx93@Q7+L##U9`FA7iP}OZ-rLizwckiHzI$ZNQ0E&B%+y7(5<$6 zdc9#rdE-N>mFV1qOtx4kKnp%@QX^=Xu}lv0khbi3siIgAOKcqFZ+Axn{+(N43Yc-k zGK*99wb3s01e+x*p1}@_1zZ+-enk-?@Ya_NYy&#n3t~EC-2Nk}h-Mov=iZ0!QLm2N zv@;6&d2?c0m556RtEOmQ47GCs_3>P$m&B@u%g{1cU?6mfN-GP_aQOgy3OgE*E1E{@ zEBH&OI9tc0zV;2qn%XwTx_FHl;A+m}QKjzZ!_#_CEn8x!j^KZLrliC)yoGYgZtL2B> z@$3hSU(GFsqP-BQmlX&MFg&_G!d8{6^?6>~IFG_WedMmi$yz~W9JgVi{h@I(^w7!j zbuI{xNT;z~zW2Z-7T{{~!wz^rs|G(YyBBp8HV?M+V<(LtvaY;%>S5jKIP(8TfE%tn zEJ%A`YCptS;tt%x?+*@kI;n|8a`(5f=KXx+wm{iG;FpG>)SpW9v(`ei(2DEg)#PXi z`Cz4O`D;Iv>sn%Qxik!>^dOZ02eT&xcdfYijUVx7^FU9=Zw;8o<0k^X2Ht%*op66j zbC(Gm`UYG(1hqI3dnLgE+)gGJu#lshVu;>dRNvm62OWS05^Wx%aj6l_sPzcn0aGKH zL0mDHR#Wq6pW;J_#QRZ)i_qw|!KCz9??DDXBSsgLG$dFK>Kust%`OV>wBRlqQHGOn zv!2xCR79NX>s!NVJHo@mgoG}mbN*Z(oH6F`3d*o&Gc_^m>Hehd(bnAK`#4>gC=cjK z$Y$<27$lxWTtUWF(rhkL@P?o`)F@C)ByNPi3v>F{`ipjT$idg8%<*w+w+WJ%+hO+U zDH+%Og$NT5n%I*B1o` zX$L-!@1BXOgfeb=6e;EErrN;2%T$uq7^7w=UY;E`>S#f?33r9=@gpW(IM0)-dLFRd zUuOyHL4HhLw|1p$gEn8Hd_D`6ukxpWz@uxg$13btbz9>g1klE^(eq(r(Ogv3`qmyM z{N&Pn7KXBsnftavRfgu-n)X7UqO%S^=~>X}~SFTFGP)Ez5^iwx$dF2wXE zM!K{o>-nAk)$kXha2OAc1TrAB6K=zB5dg$>O(|N%2ABm^YtL)Ul&^ca)?&v>X(>&< zux#7#FDEwi!Msz)bty#?3re7f$7hV1xrm|Jh1q9Fo=Ai{%QH^?6WPK|iG7%}XU zkh8u$cC(nzae6CU!1RN3f+Lu`tCufi&+XMK-ukV>3bTXujn)uC$?rSEDD;)>u^t$q3zIO*-$oA|4kC?{d4mS zNA{f5CJ-p${8!|ZnNB8`@mQAOeC(0kpCa?=^{BiJb2{SbLX|D+j`SY%(g)G)W*Z!s z8om9XUAL)Me_0!+C9b(zC%I8B8K6Rk({ZyR*LA4iX77Te%*`9l)-S!{fTb!SdQmAp z900*VyEHdo+DnN8w3WWTTfM>5XiojUBHGbB$bVYU+ zTTXGTe`^zx3*pAe+d5hN^BRQjtrhNfG%)%uf&oscB6%mIYvBS7vJe9SNE<7stk-nw zpNaPA)X#Nz)oVYc!<6xQClOQeiv3wA&(cE?ifo$-@iD(>H*y_nf)K9*uZ_7j=Nus; z?a|Qwqvm(K1}C_0^A&PDMTN-}-QJ%*@jDZDwQ(FxD&7pTAV5kGMJJ@GE^|oiOosDf zT1ZOH$Ph(H-ucnTs+*ae6o+Ugr_3fwf6BJ5_FzA%{AEi0EZWc%U7{dc`3vn{IRwb| z(y!ud>ZU{`HN*sOsP> zT;T4COJqIL@!cwe(`3w)lwS0!P&eRxWIzt7EUU~v3csK{|>Dq=1P$A{pndGXyyZ#NPfp_k|h zr#w8*snhn}PKBR0WRl|7=6Uk`~A_n84P&giPJ!G?M2#k>- z{?qC!<~Afvq$Utm^9uC=XvV-!)$PYBJ(6anojJ%F)Y4z!$(q3a;@;wdaK`)J!N8$c zio-nh?sM1J?^#h$Y7D}Bw>NNx^D0W@D?JX1sm)0$RPO`SikG#lmS-wYRDmGe(9&Zk zr340V8T|A%o5pXeBxuGJ%BAXU$eEN5X1AVlSOuu34%!EKAIF9e93o@!A58k`3N|q< zao1#f!!3B-Fw3x^dB@ficBH=l2!;BXx|DP=6)}GbseHH1Tm+*|NhhU6t;TsX@|G&s zQ^9{gNHh}$kGCazw$vn;xGBnLidpGm?SW|-(!U)fT7}QjGJW`{na8(dTggyAy*Uz$ zJt2VS*lFn%44(=7r7)`GZPD(eOgr^&ezUm3q491dueWpz(D!of-RwjW+o zt)si!I1W2dgfTt0nM?lXD5LRTS;i&nspTuAIMzbxA8L(xTJmGjaZ_keS}umi5u|yH-xz zMw5dB|H3lkZo(#w-~oIlgYsFGkYxDuR2Bg3nq`S`&D|oV_zcS2tY2gOyT?-Vrjq{M zi%ChFMw5^h_Qs*NpJ!JxDli49=!L@WjzVhG(5;?4->=@^H7D%4gC}$yl$9Vi1LM|=P&EpRWpJss0?KPN%LSnLK6dsGUI zo9QNrY(|?&7E^Q2oX%W^ENdG6E%A0fk!FJHKAm{mX&woymIB)=uUk@+nEaH$n`^DM z6T4mfXdH{Fd=D`1Ak;ANMg9>P+0u`#h!S&!RiU3RVCmw&n}&TLOP{9xU7`N~L$ zT5H=rDUVe67{0$0+J0Arws@$_*f-wQi40IX{o*oBL;m;K+!vw9B#{NLY#^)vb55+G zue@Q;@5j)m*M|{qs-h-R8>60ve8^WQi%n}K~I{127nLI(CG{D@k!c#?cc=* z?yjjMZ4Y0sCtnOqD;d0ISS0ld@Pg;#N14ptVfQ##%}5}&^0&sQ`T{9!M~%>RX{@mq zrj;%h6YL)NVmwB_B3KU8$XoN0VaqGZ!_#^HZc^;9x3z{UcIIlU3V7wTp^|!71`YU5 z+lLtzn+}P^PeVDZ@9lhz?M$(YUMp?>a4ua0GOf(V8A>Z1Hx@7FN*Vd9fV*K~dNf}V zwOzp8MZ|h#lhA@Rc(uCNi1+L;_&?x5fbf4F(N*^&eO;(jAm@HywNj&J3}Wim6}Bk0 z*#u8(r_n^626qJ==Fnr;w)p66bFse+G#J&a*TuV6#a)gg4OcE@U$6sAG-Nq?6-@TQ z$x%6QJSr2NDZ0Y6c=gyl&BYki{i=@(qG0ls(@04&YmvyxvSfH^{1vh$h0A%zrYS4m z_sjbk6~H6rt!i5yz3mIbcAKwqOE(`M zk+)b6pE|7?*MY+dQTG@x@tgY`!YAw61=qD_#zEHk%gc>M)#Es?96%z z1QBv>cDs0iWv~;%a&!Nr#oM?q;}X*4q<(clqH6~CtQehIxVy~5>?tkaZ~eC+>%hht znomizG*UkqM@$H<6Zf$7o50wrD#bCs*(U+gCzfzsAvh%E*DE<@6S*v5dS*qf6m9+>AqR~SKj-(jPHG5BMg%#Y$Nh(^sDA)=P%^v zt4?-NlwVo%)UD5a{=r;4p))kv2j4&R8h*7B#u$Th2!dfG!>-sde^$;EksctdA!O<9 zc{;G4=xee#+Jv76TL&I4=yr{gto253qpTu2&}~eIdyHCn z#}y{YDv6Buog)3z#z+$B+`NjjTU>ZbMCE=B<@1m7W3f|I@s+-q>L4Y(6m6CN13E#) zzFeXiiqwoOM5;54vGa|u51;2*Oxp~y)}u=Ak877+y&8kmg4Qo%`C|$Gn7F9IFldo0 zZI)b@p3?i(n14^j5?D^0-$E?md9dKvc&qTXHf;reo3XNhwSu=8c_8R-8+IJ5yU;r> z`hzilO1j^6Hv)gBazivQJAh zw+4qMuSq{pI=wTiif_@AeG=C;4=*;6k@;u2v$uL&=qVO#x#mJfl-iPNNof!z-GMFZ zD@BwaCQC_em(>MBTHDG|YyKTh+h4Y)-eS7ia-trxlR0PtL@><(Z1Nu|ag|RDe}|Xk z>pq!kUYZM-6NLCIIp@FAHWWrB26+RQEp7k?f;q(BBY@tup&vb6ANx<07A0Yea^gZE z4-fO-v}FP)!Pc$DC*~Lh#4ITqa=-O(6=?YHzcQq(CigT54lC@cS~Fd+rdA)Rq5Uo$U}#Cl>6RA;P~lc zcy{mPm+vmq@$l)_>s@1boJ7C<=;Yr$RZA!Dzqwv{t|5Mj?6cvCTF&SuXAqHo)vd<1 z;9!dT>4B^C-EAEoI#D$Admk_y z8-LE&mL3Tlx&Yp>>4NnTAvb3AU1uZ&`k$nNO$zZY@t9eKw{XDD*G}0 z^`rHhK3=_ou@ap=J`51k^&MH_aTf47OPNa`aaarsRxPb?9s+pvnm4RBjh~2|*LM0` zUGWw}RW+D2^=jWT9xD-ntJc;ZC*D!G)F7{51@8o`EMTqREyg=!^~$_0O9nrh zyJyF%S8qASlZ^Ran#JY>=5v>Xu4hKu5_67J<&SUq2u|l*Oyv1}mB|Dr&(_>=G0qu` z-B8x2j0Wf^Gm|-#KGPb@205IOf=*AtzSpv*9JOKSRVmLCJ2DhI%yPcIqrUalc}y#b zOD|z87?}00YWIU+>ki}3i)r)1W)KhsTRiErRfjzkh#F#Awtea?h4iO&pb%#C?B8=^ z&2)qCtef7?MVslkyaGtc8BUxYcmE1NBSxJE?Criop_BLynXtT;HD3`quOrA4H=F5%Y(6 z+ZJfD*gbtpLh#TOrL+ zl|B9-Wo+5Uf4;%i4a1|69FAr`SaC1>?2131m3zcA2PFSA4-T}4u#~aUlN*az`N9jW z@1_f9pKNa>Q%G>mw7dCelfZoWrMfkSs_5^w$kOZw1wUpwK@RrmcC!phh!5RV*N~&_ zF*o`J^Se81y#4m0gB}VqsM08VR>&$tq~bhnhK(kxatU%hk287PDSBK1zPD?jrwJNp z|8sXte!tz9MD_=}%Z-2b%hl@t^Dou8eDFl^P@+*8KVZ+OhNmHXfF4pNj-M_HbGL6C z?ziG9=3i|$lqrM8e(2UJ8@-RV<;B7OHn8KNL*=`!D7I$=kdnX;nT28AsqIA@4s=+k*3u-^Hd(F@9U zuo7p&mVM_x+up|yeg#sMA)V7$;gWvOmiYZ!Y<{qW z{!}e?*Uy6Jt5=XmHsVsB(LyL=SC#w3H03KscPaq~VC=)ZY^KFQWJ#rJtO{n>QV1uJ zE;R$3WB&THVW9no`Um+D9QuG7Tu=G|34RxYQ532x^pM zk7$nA*(!@@xureko(z{@OD}Vnn*>Qka9oI25mZJg-Jw zRRyZuXMrJWC<&~pgasw;A%60)da<+&la&|SZ3y!AGv~^gFK(|?HZ4u|n+DPuSCuKe zZ*%=U`SUN-6Kz>UNEk_d`pNcCUh8OSJSVTesy^p`%e4a!J$Y?ERiA5Xc2KKVTL)FF z1ejwnrbD;amQ+F7#T=e+9y~euw?7yTv-YHrG!iF@0&s>b@KAg4i;no<@5&Kvr0mW3 z_q97vbP6`XId*#T%AtWLU67GUvqH2<^7R_1brKytJ^7_OOL?qd8%$@hM$p=FTRbOj zOF-0CN4K>w{2ywoNMv=8LvhRC5%9haRjoKt7QeZpV%uM|9P8j5X9dz;+G6F8pBcWb zN&F+X5BEi)8z(|#h(cgR5ij7AlUHd zsUQ3(d_(;APfgA_Rj;eFfWxW`A#Tuy{|CEkEsR1+?B8?ag++XgQb=30%U%cl+b1SJ zdt2rCl&hl6iTt%!s*hMX3Vwy(NJ}w z-345xo2w~6+L%I?-O-g<45BB9iR^QzDqEny^VAYl=v5F(ht}m|AZ!_vILoWXbNcM$ zySs{x=v>~_K6=<)3SvEbNNqG!tEV5YOs&G$G;|7vtimsW1i|*=4{ND9hcloMLbs3U zSV_5~n_dt&AA5Cp`&hqs8)`7L3s+aAkaBl#jGT9oQq_&7+?nTn%i#``XQ09s7 zO1qjZq4V;vcl0u^WvZQLo1rAsZ_>#(BfB7$SiKrgnBOYAg?+Sw6|5{^tzZQ|*;vj` zLU`<#p4nU;2wtje{E1Zdra|V}Qi4wDI=kBkPE1}qbfF2I0hGswKx$^)E~=DK zZn?JfB!LmDOfi+EB0JuwKm0RucSIs_KC4%;V()M)b@R3L4w_IP`+zy9iT-HOsxv6W zV(yOY9)^eVbg{!;ut7A$A0Sn-?M4-xy%lQ5jT+CxVyh!;p@hf2mVR>A!*olPm~5QB zyuABh#vcbj%r{5wE%8QYOlZPnnmTD^jIZ>dAcgOr9**57w?WuXxV6jmI{V?jZ? z>_b=Hx^ePKdo0m03LifCSX*h@U$=GqtMAu9-F~2|{L45bZZ8Py)>H;cedVqcNzmcUV^a24gng7lp7SE|(fcmkz{tvf{hm4*!xJP>2_R3>(yU%l2n4B( z=GE?yO4|L;Q4zC;Vjp{sRLWw_TZ^+gF-xUwyeb*6y|g32hkyNYtv!LVV194c z#Fqo;ow6CbDa6(XNOfMpdp>4XQMD};eV~bHtVF$p1&}1=seoK zXNq4rJd8S_w1u)&tI9)Fwi9P2@4vO$vZ1X++b|S!i=Ogb*SFy{J*QX^q8Hl;RErH! zA4L$D(9Dbt*xwe&)M^f&frdx&V!<2g;pN=Y$U_X*wkN^CzH4L7IT(7-d5keiGL{@> z^(xM@l|2owAFb{F&2<5}rhSw%Yxims9I`ZUtX81fs#&TawxY*A1DJ^|?X_H~>O2b3 z=I8RuFAWh02A&@d4cEGGvWU_fOUZY4P1o;k7{;LjI{wH)w4p?a+UNHbGYy?t*`#I1 z!GZs!0~c*Z1i8&T&POmj5Mn>n<8<~z2m^#|eE902I;m6cz(B(oU8LjWnSs(* zui(z>>RzNVr%|!BeG+6we2l~j#ysN&_JTPHbMy2uLbRXvc`;;lHSbt!??uxJaB#J8Xp>nfGffIJ^zxCD!#V{gPn|9CcndUeUJhwk zhJlsuwI`T%9I7R=oX>jfLH!iK?~)Vpsimq>As=9doE-|(}t5NAL+ zO2FX!^b-{tsw#%-tM>I{m2Tzl_I5pD%6Ndec#i&q-L*=Q~5JZFYKHB0EEBl&Z5&H0_P2I1 zD%+I3*P7#r>U825a4L{ij;ky>R0exno?*m5r`peKE29L<`SvcSG$h@soV%_l**0An zLBD#<5SG4OzrH>Zc=S}gPzFyo`q8#5f|{>plV@w7s>-I;tbmXO2ybc9&?pRruY%sCSTMQ8LpNKitZ>BaETg zV*SMf)es`CDxeTxpr0qYCN629vl|@h_uDqsCoF;b3=$xv)}EfIYQ~GgdVGeVBO1Mr zYJ#?^I#Gvj*xE8#*eLNkSl=9&8mi{EMFdwoLk02nO{G?3Fq7&o12yurQUK}(r?oI5B%j}X7%c= zQoA&m=!SZs+g*&_G2>)ky@GkQu&d2xLb30Ym~pvd=CDi6PXS^6ZmILr`F&?o7*Adg zovgobb`4?M)82d95sTsJoWa=nF&oU#=bIK{GM0YWdB%smv16TT`{vLV`gotMBSaJ5 zOz~kXn{sDMIq3Aq+8^pW@&GS74Hr#!EWw}zj3{DK$x}b9Wq4uGhEA>q+*e7ox%g?Me$b3+~ zv14-W+A=}I`);Z4qanU_xYACP_<(}J2!Cd8x$0!xu&y*@^@`584Ikb*vvqh=c-kct=A6D#BBu(?0&ngsTl))gEaG3@0tAHK0o>ReM8uV&Yn@Klt0^s ztL^Zqx_yi)8Ms<{AZr}O+@4d*YC@K`%5J-(O8LUlNuzvkXWjVr#GYyCWi zw{5D(V0h_ZUBP-QKoHKgMVTCCg`E;ji*M+hQC47Gd+k}TWuPnC8{FH+?9lh0sIw+- z)58$YH(gZ=Oc>zk>3XquZY5K7;!F|KeVUxsU4PY}0Z-Z}ypOc=;&U(5)wXou-f}O2O%-5EkoO+1F(YWs({t1Nk8J5RhU~E^xYUbq z`U?e*GFdv&9e!q8u|ep$E6|B^TT9^or|%68kVWTD)~)V+{n0YCF+~!Oo!}Ewo6%UC-TZo-M2r-)= z=LK63Hbg_t)^$dZW?;(&3ekW$*Pg7jY2E#eIeH?JF4jh66yo-&Rn~*Q{pfH=>gu(n zq@?yw)~~B4n!_w&TdpZ0XR=5C`i|lZzr%Z*KhYC~uHkuX)8-J!9&E2*i=Koi`%ofo z>(L9bBjtYU;tWv8QUxw4y$q^mR6$PjRE1N)>?r5BO;u`B?yFajC`IAfb9McXNN-4Z zQs|Tp7m6{WKWt-8<=ilps{C96{*CPuD;;C089mxK?|?%WkUkY$aPu@Qm{|r)Qx5@U9k^Oq=xr z^Wd<7S!`B^)52zi_-9xmGhdk*WohnExG$;Dg z7s`m^QE}|_}8Bqfb9sDGdh6Goo7AOES8EMfkz)x~~L7 zvW~a`_{moDrw~owc>y+2p*CO#Vi@6@n6!(`T#uo_@WO*t7zA z(hkCv3W*8-euy+C6n>801acnBywwtncic z+_7<>7~6Z=Z7(21bOLRva4KZ=O1Y1~Ec^*9ian$`AGy7ju0$;2_V{(5COosZEV3eK zPMJP1?z(P>EiiHB+~n!@X+lKe-)V1sdT4rI;mZf>vw)}0P9D6uo^{psEa$O;`TM(S zt(IWiWprFhfAWIo_zE){vmqRv~RYOPe-2>Zs<>qEjOj4`LuSW}s(FMV zJWF%_$Z%1g-$GoTq2;e$Ewe3`-QFK%tXA;12P+F$D|lz&r|B|xNo{$XF7aLGWvpIZ z6wC2>oF5Rl>1&X@PS({Fa#2z?to3I zocm>xEhs=G^X)z$$a`4K%k$V2HdTN#)}Py3nbpnL4wUGLNH2GD(Vwt9bkmS~<5YzR z3Tbaz_rd8x8wx0#J6}n1rwDAyhuh5#;zrx6hw6bQ30N&Iv@|SE_@~cR ziX|bBMfl@qhgUwwjX!(79#uPVqCSL&LO<~x2PRazZ< zIJXY9_<#O|%KZ$3zIYK&P)OwoFPZIEUM}(I%tDNTs_OWe@)PCTuT6gQq58I6#x^*q z|IvdN&IQ0v|6jjSxwNb=ZI4)sHJ$`e&xbM2>Z>B9W= z9aT0YT*LPs8{T<@f|F5E(u9BhSaB*;G^UG#PQBb6qowx3l1q~?VeDbH$AFnXhR2p`Y6a(gH&+#rE$ho4Wjtrj)%)^{0ni0m zeGng284Rb+4*n4N(AEoIr{#H%Ror%%VE`h4hys*!C%NR%DL@;9$d@-O7q8 zn%7T1`JYA^ihxZS10ckId;Tzy^7H$K)04u^5QLbvI4O*!w1q6-tdZY78e6ZxYb*Fb z1R}*~qMCB}gBU_gb2=r~l@0Z$UcYehd8B=l0cRKl3NeCU{Kqa}KzF!pfBoU4|BM2W zjqOQHorRcRc0NQ8O5cL~{R!LFjlZZ$E+IL!F$ksn+Dv?PcqKGrqk|?RqYP2*_~m|q3rj%=0K*!lJ-{))~q)O zkJ?RFm&LqF338uKkSqcZj7_9b_OvOaoC3(bPo8dMtaZk)q_&q+Mk;xji;K6q;_<-|}p6N=o!)gKg6j(f2J zhUJ=7!$*7w#vC3eh0HMT(T{t{bMP?AB6_SdBwx}6s+7O-(&PiTmWispYDjhD{A9Hh ze*NA$cTmWNC`%+8yV0p&Q;4D7q75yrqMX{lda=r!ymdoK8J3dpEY;=XTr%ERlzb}C zbE+*cVNM2!Gy72Jk1c!0_K6a6tl*Ro0=5M3_rFz|l|k*^qvh4;#2XJ7oal)s=nfl-EjV$uUN`>o!Q#{kZ1wQk z=tMIHKC^A|@plc;fWO>U3yy+z-{ztiCuh#pi3WJ$`H3@i(;01dUpK5-7%(Q{ObA8x zJmkzL9B*Xw54+l&Z|kiu9w?ERkQ;Le zu@+wMuZ0OlY5wPr)xB@Z@uA|@{MmJ+7FNVlj-8fjNSSU!J^Y_->oN-Q1YFi~&T56d z-r3N;JjmO}UM+JrR^l`IFWpsd*`tJ%L!c*NQHT{H29?8@pf(z!3Y<8rSH0m9;evpH z9t^n+CwJ>Oq!0sV9-X%0(5V{Uu4xg6uq9A|!&oFMw%^`aWyJh>05V{@(OytXXI6M2 z%v=TLAexO;Y|2Dxl(umqhCS5b7a~J~W}tB1LsjU9Pgbjo*v1K+V4omFNskrS+hYCe zuMDf?58qsaB(mMNa0Zl&4}-2VA}ROPtDOEJs&y)*&^aUSYAi&Lw5v^rDu@q3nET<` zHdY-z^?D6F2*Vg@MLH$d!?6a)5`X;Z`e!IXFU(-xXkRy<*@N-pT9k1Gtkhy(Thx2c zjYYv(^y!KfD5R`PTe9!9OCVz-oV_qc_v+0fs@^JXGk8nXE=S2?0ePF+_0C6DuX2ar z3RV!TEMTo*1@8zfeO-PFf8Z|(3)=0(tX^Hzw|Rz3YD<{yFDey=B_5W<(4Cq0YQcuQ za(`biX5Lw5>gegpjKv(1?atVrJ~zA# znz=nkE>zJhVawspcB@n+x6?%#K#?Y8-66B>2qx$0huNnPHadG66aATOs7k(P3L>pN zcDkB|A#-1~TdoMgKr;_eSZvU?yFMp)Ol!PK;LuP3bkwp^^J;}C9WZ(@4rruJ}F`*NioN$3(Z%n1uwmx5l!;b^cwd%p5>xVya~ z>Y2T@`txV)#aJNS<(O75nkS*Y^IAp5NKO;V|JHoYWZ}b}$Upn#V%8A*9f#^Q@Hpvt zU`s{JIqR_Yuh#v5X;JoxAt(Rzxyi5GU0p^=7If6opq)v@l`-cNezak*eeIQ!0%6pT zU)bLKoG!R%td4eALEc$@W=k!{v;vaYGoi_J_EQxRCznKw4~%BOr~+w(SGd1jOlSz3Jb12xb&Gu&Wnuk^1)$a6V zXND{JhvBhjH|bCp zQ83%<4xLt&1}9N@|w=?BbZx?g8IbO^yTZxzT%{POe@P&AyqxKivua(5l>Y~9s4_m zB*_X-?5@?NM{gfEBdBoTWI0I#4?l88iL*NmJbjE+8nZe`=+|T z34h`saZTsK{WsK-V%ierJS>LBk#&dokAaB~-CD~l@^H-2V@Q+AA=xOA&zHgz!<(?v zs2Kg#doM&LX@45X3Vz|Gga*k!g+NL{%&URZjH{TNMf_2#Huo!$&3OB4TMY~pB%(9} zsfxoW2o5j!KV?I)?L1OzC`q3l*is9itpCTSCqI8@SB- zr9^i0AD$Z8#dBBJNxMx+6*nk!#u3DzEW9^gTYb7}`d;VId7JnMg^?B~S*ItrurZ1l zJ*3b{MevK;>$TZwFC|0aczrDLWB1iEO^B5HFVUbUtJp3D(sH#-xG25<=F;pOK|;h9 zmxx4)OHb^X{NkN;5jb_WnBz~JNm-{KK2@#5AK7(lNZ0Aw4#>HH%?P>inxVb+sok7cGPQ>gZ92l3 zSHOdT)D2e+_YL=)a~<8Wu?u;|vlJeZh3d~+4b z-Fcx@?TNaKzyFp}&ONxg-DjFb%yhbdr{Sf%^^4w&tPLlflv5bQx9o{1qh#YW{u}432vPL_E$7WD%-8d{%`E~f&+uxwXcGa5U zRa|}b3eW-12XAf1ZRsr^6;(x*LXha?ZZcKh+gX3}Q0V$ofL>S{87kpSlY@wV`NhGd zB~O`!>UAW)I18##QnY?Mmz7 z$;{UqJ7;*t=sf7rJZ4V$?)E3;nJ{$0&JSa49d~T1sjUd8k~W-cpHY+2*vBYhSCWFN zuDP#XVMehjLuyl&(xevbg|R!(iM?q=l3&?)g??*GAyQk!wBm<#Qwh*!45VwgEi8`1VkSU>009CZ2+or@NunrPcBJ+onO>f{-RZQ|FS7C`E6esb z=ok3_$?9Htnbne11Fo;2@~gdnc|`a zpOgun4_l$Ax_|IbQ*OKX$>BItATkVq1;a2rf-!gnvUb+xnKF$e_)l*Pr%Yv$(E@Fb zFUN^?bVLdbAOOOsz(>XBeV}13Uex=a{~-iW$1Kxq!ywCv^Gw-QM3xxtFLO)%x*h=^ zJCAJ$4vj-3aBg1KJiGw59f_$FrEeT+%wkYeuO5hvLJzKs6eebg_JjThE+1YW0dSs9 zOoko2Y%OoLM$7;>5QwHDj$W)>05{+u0N)i5s^F$4M}oRoJAFQSgn&5+#^4b^{^Ysn zV(cJ^ybT3%?cn7Dk)=l{x_@0%pT?&McL0IscSjaz+-Ly;gh3HZB7`9Lr^>np^t;D; ztCq&*O5a9A3%iylAYAt(NaQ1!74d;H1|&75}hhmB9#wAT_*#3Yl zox*8I?T(jK%lp>G?jhpAyCLBPvj7eOz(L(os|*bsdoCV9KUwxgqNpT|K&fkm7$gRY z!g9Y6&YTY;hfjrfdx3*W5f}nz&o_%2MglWl7uCNwAmC0qBV=y>=Hccm2XJ zI5DA-BqT${9UK5t-HCI}su?o4c63?U`h-eF2u`1Gywo&1^2lh=uCX1+raS~~XbcwV z1z@VS48KKCWGT@CXnu2D3P~3?_E%v*J~yYL#GdgPS|qIzDoYx#b-Of@8tHCRS;Ow>$iw}XK^akSt7?qZ->ak2B^<{GYE0WA!ybqfP!W~&+)5?c)_xNJ$B zDf5({4Ox-d>~Q(=an!4b`fhW0{6hj0{EYfGgKLC=0koLIBm4+3TD|&t!)O6(gb}U? zbN;k^#F&3DLw`8E^0PiTXnX!owLokWAg?MymBDcEWkjmqvdaj++uRK zy{`YIr-3-%e>Sjdr^SKJY!WF0aa;L1IQL$8w@m=v#Mh>rV1VXcxrn)8%B+(0Uu9Da zLXJ>4Wk5Y!fXJK6yxINkj<_NAE;gqq+A&o(XE*>|#9(p=2qtngY);#B%ArjJYVUw7 zNDD~j#6!!fb0}k)VWa-)f%w}d9SZQmNN_?$gJ-A!Nf!dJ;GQWX`QFa>2%7ZqGtuuD zQTLBuYStapYNpD!#;jG##Q8uq*RpYtgacqQ8r_?~v=csu6=90*lU%@wpHpQ&d1hUl z_+e175ZD}n;S{s*W8-AmO27?01AE8uW)qEGh=P8;R%UUggg4EtS=Zm?hx~F zKLAHD8;#hYpDfdLvY2}PVDE+go0W+F51tMJr}W6Sqw%J5#~TNxpwW7$yx|Km@atvU zkq;C*2q~iOFcUNan&?8fZiOxZ;EojfWO>0qDx9Z;ZJ*L*=cYp#ny@$B6qYF>#nh^0 zy+40Du0LNaJILU})GT;@R|&PO+`&=BFpL3?S;m;_Vu;a1h~bNt2b#q@Mf*;~0?pKP zJSvL-{MQaPOIMWIl^m+C@1IasHkzXUnkNL%{ZDVlhX^nQ(1Jr#&}cr^O5@Y=js)x< zJQ*+ELQG+-*%7ohiVdfKouneDY%PBqz$0e80wDQ+ztQ{Wj|2xk@>ZE{r5)&Nn5ggz z;>*qspNg%o)P;*4f#FUs2vdqRx76tu`$!@NMcKY|7BQbQT4lNH~)RM_D zhU9qV2T0ztHd5T(ynJSxHbB_;rQ@6}t!*|_@PEA0`?dQTS|*a}YFRA-hw9hCh*W6- z`C1)lHfr7P!CH%KZHkLOohzRn6c*XhHCm=Xau{s`?x{}C_IRfJRDKDLmhLr_;OSFcj5 z_UPXd$##9U0(12W$fPvy$&zTP)JVQWE9Ic6B=QDMjsm7S(o20uSO?Q8618N~5Yn}9 zI%OW1-N*mTwP;l=GsCeZp*ft8Fr1Kqu1y)$iY2iF$ngdz!QCfdrpGv`wV>Bjr!=#=rqVDK2pL z`{jjD)`c|pcjVCG+=Rot1r-P!q0Rx2f#dtp?=RbAplM+(*tP&ZzQ`IRO-E`GDb2;X z4@YK9kk-cQnN}~4FX9mooQ}?huu%;JbY(RA;0B|^%@Y56;2iS*6Jkd%e zBsdTc6arlugdp*?qP{9;{{#w}}Ko()^juA|qzYz8HlZ9ld z7krw_ah2{+f%DS7aAf2RA>)Dm$>H8Z<;Ciha?0WtT?2-hzyf{&Hlguiz+t(pC20-C z@9m6uq$$EIDscEu;m>c5?LSZ69(+JYJh3sBtD<%v#+!H6u57e0Vte8)=rVF-8VSJi z+ed!fxISpZI0oOXhR1CAS%wa{`OG_!7H10Ei&4B4&gb{#n!S3u-=<_N2rDYetINU{hlaabVUq#EQ1s<1w_TzKn4Mr#)H>r+tQW# zA|w#jK5VW+*_)iyr~4l$zz#${lo#^l(scTKJPb=#4(X5fMsI$0Q}c3nIHtmwCE#0x zyHp2crOiszt5?L&88g0i7a8VCU}AdIH5mC!?wJg3OVA$IB-&Y!G0C7PTaS zrPO7AXPZx|WSUSVnS>!~vn`bjqs0fw8Mq!)uU=`kYrJc-MqS!kpjnU|pg^31b74+X zmx0=5w-FL8F}If2m`$;}dUbgi659wPd@?Xvz#8Fa0NhVpyKC{B7y8R^?F$;N5%Nyu zN|3i!iMpB#`tjpV4r^Wc`K(?66l-^HGyh!Q&^1z~zNZ`rAJ7wOk;ct*-$JckyKX^8 zT>)o?Fq5BRa-@e?7A=`B(XFXj)4CL*mXsbCK$lZTyYRlc|EwPDHxvc*~lgZbt11ev2qz|S4~R`UnDgB~ldF6PelwpH-}%N=EI$*mzW zMDBzWIJ*O6jS!9j_&R58!iNAB5x{tP4VH@F&BNh>sR;8%rkaQ$&p`mlBZ(LUhbk25 z&U5#Kz@u)zT&^ zXb6g!!#h{k?!ZI_)caOzWKS*LTR$%)JUH~oBtvEt5 z>8KHkNCHN}Uu}y}jo%o2&y)SGDG5E9jGfJP$4*pI16wj(-R68So@==8{w#9sW=uO=-dYRti2pB{UBFc(+u! z5Ph?}Kb}51H`VOU!wz1Uw>x$J$34y8Dr`@ey*LNaP%P0hDqvqzKmpfFf}& zUlJkV&JAVA02=MYWDIq5ajIqHIgtFXA8j_}SrNxSroht0u>p{ALbN!-CZ)fyr8y<3 z>95@vk~H1ag;>SAb4{Fp&u)()y?S}m=Dt+k1_xZ1fX(8|2jOFi6jRFJh1i7cOc5#7 zCk%pNtlD3K%l;Uq`m1mD9mL)fG1@sJ=spxF6*}Ue`Yyo9RDgE)N~xEw0S1xz`inte z5SmghTm&GWo(0+wX%i#X&a4EA3|eRp_;egMZ@80_$gH|3o}Z4+%DRA*=c-i$e*T$6 zq$D6JdWF>A${MsME)p3ws4dh;8B%GQQ4Wn(M=NTZ^FbO0a7e?Yj zEx7Ez#m+P4`B!bBjUZ-I$zOJH8J34*KGD_!^ZZ-#2rabEz7eiBj25s)xZc6Vy8EMh z6>-&nUBIJ{*Y zRhh0N>I|*gNKJROEUoVZymf1>`yhj)#6W0=wB;RehnF|m5Y9IsJZed3{N|~F9Y5Tb zP+0r!w;u@2av*6fGNe?@X^ef13edHZfSKw&WnL=$;m^`~k|P+m*;9yZDle|qI9Z0e zPN8pH+Dzk_3z6m6cOv!%B7;0(Am;8Nff%re6gcgnBEDxG7e`Nr1q3FiV;%yNmS-}S zig2bc#2g7>4Z@)ohJlQBjMma5VCov-hsuY;(gIA35DBxm&y*LUYXqo8t?lu{r(&{4 z4F9;d+1r90N)20ifGLbK{d49w5-OS$=>j867ioX1Gd>v? zZE0j^Pwx2t?)}D&il{)N{+Ugs3TH!%2FEyj>j;n}OUk9pAW;eV@-XZp8)7MES6OBI z{;rTm5n9FDZ;rP?V{>vU)=&40$9}+gs_XtH44iU6Gx&2;%(vc(r?HNe&u$!;=zZbN zhL0HSqh+$hiEgB4V)5eE713xLRyA)*!q+X!Lq!2%CJbUD{vYlMg4rg&K~#Yv8V7ZN zg(v&(jXpRLMRWm7YJ@f4`9=I(ShgJt&i8i4`_ z*q^#B0zj7&*5MI#DNeCKiUm?kZMr$$Pe&1Aa5w;gVGu2{(D|Gr@>bIW6)w@7!tn8B z3+sp?95MNa)((W00p1y+oW(G;i9EQsJQfFxgi>rG!0EGUS*&C+m!^H-D}ZH%qs7)X zeFyz?@s3%9^XCkXQ5k$RY`@nKqbS^!=A{bO#hqU%g8(ovQZ8r!zWoJ$awxPs^$-vc zn2of*M2Lz|2G{UmA_g=CY?DT}qzPya1LIhsto~2edeKHUX0&eob2ObWy9fmY-%r*J*O*qrOnoQ7zQ|wO+lN zt^I%2F@`CI4@Pfaszt~c1{ zFH`p?s^+wc5-t90Vl8x?a!a&z`+z&A;WY(W+KnH`A+?mWdp}yEYr&9eElqitZ3W3K zpf~F>>moolH?IGFx1^@91$H)DbgaLr^7-B_gfFcxt{>S_u_7j&QzChHp917+T>UkK-Q zC}MVpGyJ|CnbJr?0OPR~AnatDW|Z%N{O3HBXo3|h>(HjSgaozN5<5Fz!w(*;D1BH6rvN1Wy7-qO^CRsf&^AFbqsR;jG* zrWSpKj2xgrB(-M$S_B6%IBAd=Cd=0_Y&7I0?q1XUaHOQbBknlwPE5v#qI0cd0s#&# zBl+g?gr+E!z>&ga^Pn+_K`53WYvdfYZZt^QC-qo)!#!C-vKAmElS%yU!)kpJhawFE z+fdL%j=i)A__;hV5$Y+adTo&3Q{_`j3u))r>3B<8q9rCgB>~I~uLX+eUsAiW3k`Nx zuZ$Lgn^;bk7Sx&z6mifF`52>|8pG8y9vpFs%~m^LBtxv#=!(r#@$xXx`0%Ft$Z$Qx zFv8CvMhjRYjBrgbpVcdWez?8xO-xq>7_5Iz2L8e&r!JDNSFb=<(z{_$>x%kAE$KJq z#e9BoQy_^O`fu|ES}+}AsulyLwfCAa*c40t5dRdYmnW%I_|bH z!l?N5{&pvH6$Z#_Bn@&NNyAJ&x1tAP7b07mtXk=UU|{OP@StHj2_XPL_SE@i5@;!WbbSaK4%V1R z6Bx{w_C-iIH3lSU?NjHPEi;4-7#_sD^LX#E+rk<^*kuUh;e7G#2xi%$h=-OrbSj<_ zLS667`kV$ss}3KhfR?m^*wN;He5-eFe`OOHI7iCv3Hz7$ef$$eim;{^e3#MIfT zP+7bvmb#w2J!F_OJ5g8o?ZdraxwqfBWf9H5DDvfc2*}3IC_I*yF&I^d#5Q5f zG*J~9#WEEcReH2Tp=)7MI1uhUvMFFP<|D#lfdizZSvd%tKHuzElmlTZAQ*cnBI#cu zjR(Fmc%*_k_yGa{86aoHlExSHC*_b2AarM^fNe+PQjmQ_kuOu{d{-+aL2XJGnH6Sh z=F+meSxyplmVqju<%<*s5~QwP)kVT=i=Q+^_ihJ3n6)1VqJ9Zi3*9if90Ai~HVi4c zLd+J@72>MfoX=OAMi}8b!Ds<%gb_YA`15lWSFgIrU%27Fp!5HI9U@lm5sO_s{TH&< z$n5YPYQ)pMEfX>a776R3dh1rt`%BTXFvOt9N8Yf*Eh+L{3mgL@H*4yHX`;SYXsH-& zD}HGS)9wu){Cc*-Y(bgf&-3G`ZX9%-dSw=y++Q+n&ebcR3!?%WQ4AO)Oy)e$Sr^Q5 zSj1$B2^(#@bW!NSBvq`YnJ=MYgmIf&UVpB4AOW8snZr1JwGrTRWbKe)$^np}Wup0S zh(`?EK%_g4m}Qj-KZNmrzW=^<08^VZ5nXA$Fon7*hsrxX5Z14oVtiRLwZIxNSoD|g zM0>*La1G)+?}sl-Ip9QnZ;j_f7&%(d3pQ1uBKES2cMwKsJ76jfrgmlK`4{iRN)4sJ z7R$#^bP9HUIJ2(-mCxQD0~GYA%g*3fEZxv z7+dhl$mbG4>3hfGYA`VwQ#fSMvVvSgn2kfz-4T4X7NZQoppHr$WnUHX7sC;cOPpjC zHYH0)ZknT%OBU(a>8K;Mwmq?iq)oc5EIHxSz!1ppLvcr0Swj*IpyFBK=0$`*Y)x;CvUx9 zG}j9GFsTTa0thRpL1W`cJBX1Qm>$V;Y+l`*aUxZcv$|YSH4C)-!C3RmDcOsqqiYLM z@$z${UpSVk3sV^amLrpEDZ)be6r%xY#{A38`=zO~R9Jm#XH?K0oOg#$Mh5}-#$#Yc zl5mwUvA#SGtbnP%voKqVAhWvY3bf$+@>8ZIKbvmWD+6mmX18>pa0>%fvUx9FY}Sb9 zgRU5y*y!Fls?V2v=sCl>XO;+(&)DO|qP967^0Q4S8mI+O^q$o?Gt~OtclaN;(sicENrQFT zMKYDo3C3&ssVS?|2J+!E7(hu1fzIA|;8 zU3iDX5mUteij0cTu3iC4nKrm^0}yjw0g%zGQ>iQp%Vj}C+D9iKn6)xHiqi6k;)ClV z7&t&j@PR=R29r-{<@Kl+<}jpLBtZ&(ushzl1MPCp$^HXe2tWYbEn5=vD|PYfI>=}H zt4{kTdav$pCYHEUQR3zDSearmzq$W(EeCP>6c1Vm5HlWEmxuT$!i(zAa1i5a$ryk` z54y9=Dxkiyd~hIM_Ec8lesZXH^h|_Dt?8q$J`plVdIiumc;!I6YAq^N{!;QAWy=x* z-`*BAVhDvwuXz_3*|F0z!TSy`KsYM%qbArVDot1RIWgM(8o2Zk;b z#=BXxz^U`S|Mkbc&u?j}iMrO(1sB!5C*s_^*o&9wGe}Zhv7JQ48MLw)LU4eqSBg0R zYEUoLkwMq3I_Ib3;aQ*sWjNkG9FM=zS&RGQ47X%zt@??9E5w}9&WS6xaOJqVF%0jZ1)=p~lMzM` zqXn!HM)<^HLBAgU8=(Xt5)Z?=BXzNRF0m+#6e?A@WC0a8-_bK9fuq(R>NrGP*M zzv;S8ErQmy9FQ_9D#HxNo79v^T_y}nz~o>j=O?CcNSz?TvD7C=Ab^`d2+j6?yQ;%M(g#PiE{$NR_-R&n1%#7U_jm_6h1p)`gKvqyjz>=#q!pO`|+mLUc_0s=$JHjzV-lGq^(beZ72Uv?OM>FyZb zFbx$gh_5~|Q)Mh;kSa$}S$kUKiceX1QjGXjJaj6uxZ4V6bF8E&g9YLz20#VLzPvyD zu3s50;1d8^z6Zw*1qj4~PBDv&RUoAb+Jt$b?4bFVkHwZW=8e$6AOX#5Wzix+m`v1B zHvs0|F0bf{PgHdmQs#yZ|l8vI6f5@ z6TfBQ0s;Cfwp^8^Qu@Nt8OSris%SV!+|n_LFJ7T^%8WYe+9_^K)ohw``kbfB>;{Dj#4E76B*O| z9FW<1^(upGeKKg^>XqtO5k_ds&xtu*bLr^H=t8~~+*ejqySVDL`x%0l%`3!JY`xLy z)d&MITEH4%gr5Oi-Cxro)>=i-v)y%F23n>DnoVX#B2m*dYCdV+;SE7IjDdkMH*oKL zqU*iOiH~_zcZAN_63BONE`fGtGydH6=3$UptRq~Tic3czuq1pzP%jZ;fW&rU^_((mqQrWsl@T4;lWz^qwP)u|M7d$94Q zW=8kJ-Lbs#L0P90NapMjI#h0rg%a#!HNWvl)@ECsS zd8Id%LYVnK9Hz}1R>ddwHm-_FA^(|8;ppitQ454QGC~!RL5m8iZV`T!#GP#$y-%}j zi7GI^y;vH72(1M*g+?CXSVh_#Op09kS+OKOV(65{O1aWxJ2VYd7P+z)2HfSINuas`Ji9$sTcTO|L+iD&1?+*cxe0d^A0;ofMDVy0A+^`OtfqVCiX5c z#p}9CHJ{%d>n5Mx7&6Es3?Bh)S{EPq%0Dm>V;h6Ej6KLXxPE0UV==DaGraSGI{_0hwPFX# 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

    rQ?-xuvTNH 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

    wC0Q>kFZqv4R{77= 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�lbI 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ͻ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%0DlO_eq-vj6!0u&1+a0MKY!H;kN@r6N@Q(D`W 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 lS5{ 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∨a!X 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Ⓢ2|EV7>)$$N0By~!g=aSX?59_l1L#mayC_*57jv=1rClCs6E6@Xf0E6tY$KZP3i>L86d zjc^>(@6r{;5T}l2T)F`taY@4OC~g#b9`UV?M8OQ z)Hf%v7Bg-35lUG|KO6IgK%I+NH0u|oGc@Klbo5xVgS4*6xZfH@?E|xL;QUDGQwD`& z6T-sQZ$y%-C3UdX;o!DFQ3@Hzs-HFRzsoT>3)n6%SXL5j)7NkmS?UEjv2yF&{{{`s zG{Piz`Ee%awLcSpz@5p2%b+U(vQ}4h5-mjY1(T!JB~P?fP7l-HxcT$A{RtsuZ*jU# z5b)%O-|67^fTKl`FshEBuI)iIQ)5BJB&5I7DNKlR8V%2L_5V60fq}N#vf57Ls-e;M z-zBrbBGzg>zja)^cl4Llmc7kS;+TW8a5%`bLsp(6LTa!08nYsS34z8=IbeG3x9|t? z(e^JU;wv3Ci#H-Dtb>#R6B3c7#Dvx3sX#+us?q)_4Fad1;@NH#8XX|)S&3QzHR)Qx zH`ve49{}`A156RzroF!FBOf_r{h-2r^npB$px=ZtIC7~0CMI7L#yxcClt$=C?WR=U>R7>@*?|8b~52sel58m3fxR+4M+>pmO^)75a4E&VHB;0_k-IY z;$`Cl$Gf2OdNB>DJrdNaO34WiOia7vNQe^)9;P|u%24KhXM59F1jds&cvet9q9uR~ zdJZEm8nNWiYrf+HuAZ+@eE6}WKm;~2xF3pih$NqI+a_0p#UJ9h1M+>v(Tvv-in6XG zX<8Az@5GIU@HJ@NEH01j?3!>@531m!1j4{B*E;Oesi#PD^YxlsI;D*(UD{NO%$E4T zlOW?E1XJQ&tK6&XJ(u}^m8CBle&k3Ozt9-$2u08Q0~3`pUL?Eb7AnW%pET{R#mi#S z6CMB8Vf+RuSJ!Olv6TvaJFd2$N{-VXO}6}UbcZH?+maM*g|DY-=<6O4@R4-yZ=Tu$ zTGC?C|23sJy@hx(RGqaUGgUJYBhT7AClqO25{Q*0i(LchTl?`ryu+F z?=>ldF=X&jdKx+*j&PcGOu$6N`pOF7*mFByYgUSgbBKAG(6hoV1A)DdKc0fOcaenV zFni9=bec0&g;G0`G){W^U#+{bM*-AAvSkcKvN{?_`~NzBNvBQVmRaq5oK$zR}GyrJI}#i$R3>4P2k7Mt{F@A|b= zc2^g=x9!%Fi-jBkiQKrTG`LSYO`f>O*I@lF%t&;LDm|l&We73G;GD#33AM6I2 zqRr_>-blxJ5_e1VBn48HGAhAYDhc&+^o=Yz32baj3j@Yag({4>{cq5};?QHn*!A|2 z!MlgZphA~bKc?_}#LIA`0@M#Yq(YUIkAGt(5VKo39HCE^I<2v zG;3{5B^97OfP;_c9VwQ*B`GZ=X@qJEF9Pl@YZBIGNS#ZBy$$J9D=wv^7!pO0 z7*&F(lB}lWInA#JQMnrExs z+o2E{<)o8Z=c#-5)3qg-Yxw4ucVwivp(#by_O`M2x7AhBr96NW0X{;S<(w*8k#E8mMD4=i5i`OVZw|l7tth? zvH#s+%)geye{WVneXoYA*P8yel23i^^NV%Ef_1{0TfUFMJwqz0Xp?8i8gQ&EAO~mo zBNeijr<8Hju94b|XB^DY(Jvy!A!wO>*D>2JyEbWl%@^=nUy|n(2oZ`&n^Iq5mJ5Wi zScTN6mWMJTUq@KyserYEZvjfL(o{GwljPBPvL~H%4$2od`1nepWf3_RaY(R4=iN_Z z=~ggSZ_1Zmd+fkFquVfVRL>FUJlx#vrmPC$Ps1eh@^K&5or1aZ_@|n%T_T%YD9FF+~g@qH}DC3m^{fB_AZaygjCXwPN z0H7~sk}ItNksly#tg_Rx2t0>sbrHQq$Yc2LRHgy5)uVxAoDN_cz_yjsF}L&m9_*!d z^S-+ALiQ9`M|A54kYG$4u(lO?4`A8(MES4MOvgG&pNk1s9SH5XI0FJmA1#Csl?c!}Qk?lGVx|~3!QW85 zwr7%Exo66)$skDXH8@<5TA_>;pL!X*1(kJmCyl(AD;yQa#9bgN;1vS5q+*bsVfQ31 z<{&G44oGr|2qN=UtW8}pGAOj(w7_|6^t9O01-^*jJzKT%LRI!?g6YX8e@Hc9s-iXp zP9?@awz@^rtHf=?*U#33lym^FF^QfXFo;kbGqUC=%e#(hN#vus0*EuaX*eUreQ(JzPf*)GCG5*bcdWVD1VX9y+^;6w*S1Qf6o6sEc6z-eTo(ydkz0EG?&i6yk zs1+7fU3i=PQG1F;&2MCpWBBBzoHKM}v+>N7Hn$y}z2TR6nuT)n))Z z&EcIFInzB%$d2{QaGMcCId{g^(L+0wHC4tc=r^;Y$Qy5PJRnj4iK;qoAVBs_%rC~1 zHOqVviD=Neb%^ix7|^m83<8oOU%4Ty7*cr_UoAJHh@xL zR^Q99rBGj8{={_@g@+v79B5T0DZmv-qj~m)5@`ZS} z2n~xaaA?{e(O%4(rcg<~sQro0NefpQ z$8Vgv^PS2`qPBniC-)qRM_@S}W#{;F+qvoD7!N_&$)@O2pHxwYE+X{7F*dJWq`*S?X-k?=(Mv5U_gOjGUzizgJfo{!|9sw0OAPDsKJN|ZL4MHA?Vc~B{-u4GBQOlkXQDU zOe9}hg@TT??@>(O;!4k7uP&_Eg8xngw+V_UN;dS+H`N}NS~e}k_*;v`XPxMWy< zxE}z;B%umj)+Gtyh6N_*bPk)+*Y5sAj*qZ3kND?Xli#(T*t<|F$YtyxO6|J>Is|P* zDMiKU#zG=TRYk#soUK!md56OMH6U+j>9)7nDDdDTQDe-Yj*cl;FONKtyC5q`EWZdO z!IOZ-RFZ{2PySnxpo6EY8nNf9M~L9xt|cbQN170h7+HWxOuL1aXlXuIik`@lNa286 zRc5fQ?R50j4dTHkez+N`Ika&r6x~tGL%V{b_;{W#sz=`+y>S8ZSBV%CGa%^*I8-g! zL7n&L9~T^DMB1apq^%9IY`lvtXZZZg-6Z>tJ5!cdM-V4BMXUt(kpQmg?zldhq#dC{ zI;7VYE0b*LKE*XOqFn;F>Q0ns)givXu|$1lw40Kg$HWXQ08VlYFmEeWl|7nZ$(g`& z%_Bf_noiiA$+FTEvkfXZjZ5qmmCHH|4r(Q`6uZO)Q>9V1KCKl~iSm44?1)2t*x}ND z2^GO1#2#YL@4$+UwLKNEkRCOtMP6>X|CKBb1gUG!_6a#3HBYvGj!u_k zW&$QyX3|5&{LRGemV}qb`|6@{ow^D|+ zTNN?YXJ|Zy;@>vJLM>TQE-)a)KXK4sb5;J9`qfJ^1XVsz4qF3jvSh(38OPRqS|BbEAc)tJKFTp!P79;yP1wO4{)ru-lXglx8R6KsF1L+P!Vddx#WwGYjH8@a zQXNWuZQFgVvNa#VROnVV0I=1FK*ssj_EjRCg$zj*0W59Y`apWCLOx>t-FLwu)nRn_ zI{>MgoDN4%AJ>EF;ZHS-dm4XAoHW-IwxGXWJXbGdcAl?eG6m*6ZUAnJ9jK9KJ-u@> zQ3DK>e=KW$xS?ycPsIIK+asn;A`;oF3z#SVf}-R8v>@NE3xEdfHsQ)@Loz6Q!0&vP z+w|PqW^ll9W7mC)4dAt)_=#mTwD|P8^DjWiMxhdf4V$mnY6%IizKXUwZf^Cr%S_*t z^8kJ4_jrucHNa~5n?doofBPk3@dK!Qq4mLz;ue~=#FgaF7`cPn9NK3?f`d2)I14I{ zfK*F<&r{4TemqWFPy#ZMc^Ai_OUmQ^(1n92@Zjburw_jkMpRAgs=pAkf(bL4U{eS7!25VtG0Z4hE8;Gd!V$z4g7~o!EY-sxpA|)v=&@l9G``K zjk}tUM88@-ZGdSJL3_0kFQ_>=s=8=*ykX!P>>l#y*_y}HzLf{0q;%o*+FuQV$KC|~ zB@-W~m_xWKBp;O&CgRGfUBTZZ$RIeN&6rg?_S*-Dy+PU-QExhdyF{^_g(I{iLY*MJ zfkH>%_c&xUe;yEX=}iY)kOnQANmZM;?6SR@Gm;3$L{&5%CRP(75+(;;&bGEV$n2yY-qq>*1^HFz3teg!z1jYtzG4Pp4qi5RN!3pXvFRwfz+ZVrY$Y$r5NF1!IqY#vsnNS2=f#$!^|q|2NF}PihZ_ETevNb#_yg zx}@u}W}NuQq*&vBvX#5^6xi@Lcdx~w(UN^j`yIuLq0^Cj7*W%u zC9r`E43oW$A;t(~uxel*0KV3tX&1Z$o((N*K@G)O937zrp@&Bc#WB|BQ-#kFHh^_R zbm?dcGCI@&ugFodh!!OkyMekzY!Ct{>Ki^fdZXs< zDf1&Z@9{SEJdm$2W0eD&$0dcux;#mO*XXfI-&-)OU0$tYx%iU;6yXf|4Vph-F+hB5Nc-syWmUuf&@sRMvCL>>DY!67yLFq$aU5+H%MgnT0reNs^1}Ps{H^Dq zL=Kwp%Igw&=D8dB^R$1P3xTnY(=Zcs028P)4-26={FxVHw7t_1HaDeSx1|I;|*7#``&>-=xVhH^5?9A@3i`= z?6+j)o_~N9^A}1rx;ttM`XOondOJ!y>bpAxSv9F5vC9#u;d=(lE!A8tH&(=m=s#xK zwBODwvr}EsvzVZ~acDbZau0TF!s&m}q|{K}QS{J~r)%3pDaLEw2Sp-TwtV*Rh5U9=T|t@^KWhH#Ou;1-mVsk zp3w87a!T+2Rtj6N4AbVAhwl%2WSo-Wuc*`vTT!C8M-UV3u?YC4W?ZhHrAyxtFrQ#O zcs=z8hKEPG2-9SmYaZ?BDOB|8z^Vb(SSggE_TRLm6e(LZUFa)&L8g z_&B~&0Dl=W;)U-I%mlq(l)Q_t0k`Tsg%Ur6!-S+6qsQd}<@0wx)JUM=8Xcj{K89x9 ze8rAuL_yvGi8JS@| z4b*-u(GGOBC2a}78KKtZ{&A%-oY%(0i&a?GsLpZXe&5e- zzCKEQMh!LQ4X1LNv&l6~qBu!Hs> z&0Omg9czWa%Hm8WdLnwgIQth1V-uq2*o&M6$2Wu|*XXbxIpac|Q%4ncYZ*G@DJvWZ z!O`G$kl1xvRUA}5?04G zGPCz~itK5g=m^&vEF&)ti6Rvbnzj1zk-bIL3YN54_%(T6CSfvXw8$a>MrzfkB>Pk- zPuDGv#J;bz_zLUh&v8HJhiwVGv48rl4Ahn@FKIFkWiUJDC}9 zG&MuuL%AQ}y~H`MH?c7`wKy8-TP)J0CWQOYLitoerTSb3l86{iYd!B7q&L;?h+HN+ z2ZC~rsa_i@NVtQhGRvJJ$kLvX*$x=!zv*=_L*e(!A$Q)ky}#m8jhxa^+pnaALpmM+ zwQW%3TRbOb)-V{^U$;k}XaAZsTK~2gTZ%^#ti#|^Gf)rD+1ecr>~;0U(ULFFt8_6g zJ!XPb(XaD9ZP&d2VDR#x_j$3@OAV^L9Alq_fI zq;K5xn!jd$1OnGz!SY{6fV>;1;jXfO|Ai4#Gj{@WYYoI0?*TQWv+;&(#q~dNcH4Rk zaGGb0u01lqe2n)G>yEJNVu&yH~+IX?)A6xng+mQdU9g-RD zf%FU)RoL>}Uz&`_^?sv&<#YJdl|(O#ZlL)V9<#G6WC56@Ts)1LnPmiWQ8UJoNpp{Z zg%3D_1w0yf1X;nIH)OL?8!{o-&5~O`|1|D8r_TfVehkVn#Bs3dPo$o&)rPM*-OX=l zr_i*ES03AcW&HlA)&LJ@k|A#kJb&Z?8f;Wz#E1-#!n_%B!~lH*!1!~>3z?_T1_&_M zP$pnPV&-=JiQWe_)hAp+=-s$JcW2&eoAn%B_@WuAf<%Hk5MaWAyz>)mS)?4R%DtuA z_!pE?7f9Yhfo{>k7R2Cwplej=4`W8|rt>10+vZvFF;BMIZ-+oVs1eNTg~X8K%R)r8!Zc^?j4k1OvSvTj)B&C~Nq z(dcK@Py3sp{$aMs7*ik1{1~b%G27i)_?FKof4t4dAC3KS+u1h^6cu~y^7aWyCHaoz zyfPZN*lY2i-!1d_mpCO8npy#du%^(@;i0Z7QC1zhWc0{CI~v#B??r@m%&gy)c+XyO zSDzMKEH+i~vfkDNkQ|0dCHjXO&qG)gS^VxQ`z&TgIInm7d|%FbE@-L%mD!9UDO$SY zg7>&YEiU$^l@PsnL$o?T2ui*5N&75QILgIWRC2-MEGnTu^4*+vH{+&w=j|`n9qPV~{V}o^#7zJ; zDAq=cdy^>1hLa{?eLShLcEV6|<#YPv-9(@Qk{coZ3c9ZZ4WR$dzLW8KzgGPE0AP25 zQgi{O;67ae$RL1^n0)Li{v*N12}s=h z3j^w^BPK!xm^@xyfafT0`X*lAPn?n-D-SyppeCiQ zFK0|2JhARcvEVpG8KMWU3)X!9ncc5*93(A-Zb8pN#!Tu71P8$$`a8Y}+$$W}WeP2Q z12+aG!473$C-fyjhYzr;y9Scan%&n2U#fYExFOJ_0RNjjA6%3~mY*4Jm2&+rh9zWJ zfSjL*yB+<&@bj*y*xF}9&fC|<2M*T)*z@tV2540*%N!-ACyqa`)8lq}9QAu<$f0R#38e6Yd&+Ol8{GM3uRBq7^ys8YY zn>sE%?lnGhJB!OED-tN4Wdc^q(oPQ+Y@R{ z#^+)vznY90nQH)nC!+o>oUT>Oh`rvhytc_2w#=wVkg!Mhl$1}ja89a;In-gyXFrN{ zj35}zdPiK7CWR+5zij<>tXz(rZ?t8>j=+%zOY-MP$c=TRSg5t@h*oonn9)S?^hE9c z3=q}PAn#{)dbz`9tfow9gY}REDVV6l#tNMzH$!w`>? z_+#BgA3bSCti{M1Jh#q0aaWsN=}Qe34V^KJ<8=PoX54@O{RRoIM3#+UXp)kf4Z#;Eka}%> z#e~tG+xeNi0?nGgI~6V|3#2Fp3xBQPT9Bk+74AocwkxO|kf_GXI2vBW{EVlRNI;=L4zA-?Zu)B;Fa}p+En4gN(v>-X#9T{yU!T zF2Wx7`>{4##TFD?XY>U6v=AvQjgS+58N7k*`!AE@`4o`SmuNZ!D+LfbIX!*+_TF3& zU&K)ufRZlQkgIP%%nGb0dY5*uhpN5&eviWI#T#3Rs8C_c2}yZ!!F))zb zd%ku~YVsj}MNz0#yLuLMR3aq-CT{I)0yL(|C40Ndz#9K77f4C0K|9s}CtC(73S}H6 z3slgc6&?Ou3=UK!8s?fj$FMx-l~lsC@tPV`cLOv7d0Y}c3}~>URfFbR)Y@{;Ul&b2 z>xPPLgz!It9x;ItVES)sD&kUXsFX<0*j0Yi_;jWdy}8#_AX?r><{M&%;KH z`agC`SxZYExIt2A1Y@4(RR$)c!L9_ab`{^;|M@RVdYg`UK;)oQ;99TnN*ST06ZS!Z zdhp^57$uyQ?R)O@sOW+#`#A-w^R7D>TkSRaY&|&mcLPBiT3asO)*CT8Ztz!!FPyAB zmCm4Tt){{(IjHM>HcY^1Eu{N?0D*77AUd$O$^OcNh0ldhU2fZA-&RM@u#H{g|D^kk zcUVFc6a!?}Lf)2@u#jEfs+u!H;KVJN8K!fcyNRIo6 zaRh1!#@2S&*XB98#vkn)Oo??fx5H>IgzIr2@)AGbZElSnAUK!z-alTc`N3+ubK60uYxVimK`MYW7T)zNmVDraD=*N$2_2?{~1^z$5^COI&vK+I$+v?zzo? z8NP(&^j2&5C|UjEv;U6m3yvHy_Wi}&&-Wu(;6{}dsq5yT`i&p56?j}h-v+b81B~+j zD~2AhzaU!11X_VNqv&z{DKhUSCpR@9C}mdDWA~IOgv=`V?VEM;m1{y(>%$1N9Q)Dq znSSY->(zaehT}4zI-tYLzyoapB=g2)$KE8I3y!yYN8Q(7ha8Kb3edji`jzAb0F_%6 zY2IChaH2RFd+qX*Nk>7)v-+U=DG=v%ny!)axqyqXj+sm(A; z7S-P|u;Sr&%-(Ga02oW{g)6hu79DII^IaJ@2`IoQmv&ZC&W!|ghk!n%lHby3nsp5@ zBoNk8f6okp|JDO!dZQ3W6zYcs(ls-6!vDDmT&pns3FZV>0}Qv<x`(pqkg&~~NJsbN`_S{gYR;st zdY4Vhtvi0%arJc|zzayowyPEx2SwOH^{B`V@mlLUR7qNZ_6&}?gippjTA{Au8sEE{ z)S!-5tc61Z5w&ok=uccUgt@%VqDgw#8bwc9T}f)^kp$}w+WluH4=eiysydns>3kem z+_>*-fz;uc89riDNHX{%f}-IFb*YKJ)Vx4Ro|?`vjc0Ts#o7*i>!cFdI!NA1DxtW{ z!iW|NjC5PKuUvGRB~jL`JUCuUSd%4nq>+y`&Vp*cEVAZ_M~=+ovo?nyZ!VltgrVeQ(QB|xV@}+fkp+6=B>7Tif*B7UH5na8-JnV_7gyR?qPo4Ym%w6V|pBQ<%sZZi|wvEQ~~9qAOt503#a zU#CSg2W$PtGT_wLX_jSp>jq`Qz2p(}4T~0wgu=FPe(g#!YE!ju`c)ksl_sMhoDgbWo z0kZE8JpV^F*XeB6S3K`UXk{9JCTjqD1`?m|;gp}x(q0b;#;WG{`md8IP!RRd%($%to|BBbz$3CHqMG8e|F`&d=|};H2+&61lk2SMRkv`$_FTE-K#y^=*$+5g z2`KsbogU9{zvKDtjq@RY1OgT_tt@_<>BCzQSr<1d)O3tz>{3w0GJkz&`H{f^s0P#L z`IM`-qOFYf+kBh)#c{1PooB06P#J$X8t{eMLmLWEgpuMG_}|Wf#Xz+WC_VxK*d?Hr zTQLxbLNW*Lj}z(Inb{1ImlW_$2!B~Ncf^*5aIvN8&N;7NG7tba zbwMNk-ybH}Yuj9{>}y$eD=qvW83>51$`0-Si(_U)i|t25@K^#%ZRpz&CM2g+R)W|A z)LiQ0bWMq-=WV|5B4(??K-@|Nn{21$%T9U0o+(kbDg2WDxrb1axfiSD+JHY_DK`=W z=~T^)H4ds7a8Wztc3rz7QQCDWao~Sf83qD9tMfKr&we|78ML=Du8-1OFq<;?88|SS zLbm*bwyn#a-Aczr2bP~RlqlO%ZEtsxZDijb<=a$zr%AljE)#ne^ACvZ7+dyxzFHDn zN(MBCI_uSrpa%q3RCUJTY_Ts<-g{WE?J>3PBXnObR0bAUHdi02f-&zp?o}2%G;XUn zW|SQaC@Mah_ZFz1DS4^9>%6Vq=bh(C6x78l2BO`~bDKmWv}oezhiDvl3V>fVBQH<>gj{qVh+c zE5E@uFW^X@h99(#1iQ7{Q!<`Dr0avcMU7D&IilgYgp1Z)Sf+bSr1+fxFkozfvPEbq zDNB7##BbW6l$s)=RtI-6(#`%$Bw5`!0$KRm&yMjX9t8|`=YWu`v|lKyN*k_;ulLPq zKNZA}9HT8j=Ri&N>v7)I)%D(HTf38iV9xd?0+cfaYCX3!K$4C$XgC3|CIIx9obAGb za4{DY#kdX#fD^bW;P~FGvh?UH&o%S1B(7;%^WQ^!ZeG%l7aPIMscAxe2w+2d-vmUcNf5T^S;}Vk-;Si_!C_KwdpgQv<36R+w7`zWBs0k z!!ud|Ba%J{v>>tMxB(Ml*Nv(FJW#pAL*~W(-a}(Bt^&pEb)WME$ys*NYfKZY5!@re z=pQon7+9}Q*RWf0HVASrP6{#S;o?^7`}9#k8wbqMQCWWNfFKQk=K^m%l*L7G|4wVx~l!cQ67(0;3*y{!RPlolpDL~C=fXY*L$$MIJMyDZR1JXBS z0JXLLmFEWq3hXAUW_rjt3hvZPu#J0p2X7BBqg|I37*Yt+6acOaFMHMknL-+3X(dEM z3B4jKN_?Vdno2xUkd+z(tZT|yyOP~)+;(#5KjVab85-In6V!3kdn=rT<#N`jatzD>BW(f( zdBsp!@q+lvJlGHG5+1eEhC1;SG10-yksNqWB7U;?e+QAfTBARzOvK7ZSX!tdnDC9s zmx>ZzFu5i}-XPKjtD}Zm>>`5_T^L|<6Q^Ufu{V_bc-#oW;ezL2+f?#qH3v_pr5sBS z^(sX3u7870!v_6;h-{go$>VpMRbgNi!P6Il3f>`#TDUf7fx+>|p-_rBE zRjA?rGWTKpd`Ua%{`K%L(&tF;d)NJ~$<=rR2jk!0bA2x-BmFKej#lKXnDFwBsL1lc zhX;h%K`DK0QlG`UsL6xTt{yFaJ>3wY3w32$Z+q6>wy6E8#AI}B{gySS%CQF-$w?x^ zEy5yAcyZGSfc!u5ADt7gy%Xz_oD;r}_m`Az&xfHYUGK|=8i9|OPZj;1=YIW-)=i%0 z!-lz|p5&kB4JIN39G=)Ia|GR)16JBU5mu)h6MTj{izTZ#_UV3y2!_ zYlYiKMy!j83FZ*)wqonLP0YD3KE5XyMs6*y;*R2KWg&QKwVR_eA7IZ5JF;DKz1v3B=;q<^p+mi|F5W`%VS~?mBv*bTDg8 z_Wvd1#f_U*y#bH;_NFobu^jNghCcCsyaAa6LjE_CiOB>iTpO0Tr*soI_Ie_?Y4~n7 zicpS?wc4v4^RK&-a`*L%3d@=e*O*b|_13Q)04wmp_zBwuiR=C++Xqb&B>7CShXvM% znkM~10MZk*$Up9RIP7GZgjExNQ&2RRXg0j?V(7hD7O}cTnQG2m69l74wVP6`T@ltNJv2;8WCuD;(5#_NTH!ial)uk ziC^S~rQWSTAw!9spCD zy6^rs3#TJQA&4seZ!pE5mY8cTFSCXYiG!?6suR`$vSJP{Rb$N=%_c!gG%PE&*UAYe zkC@5@+z=t)_}JDmAK3+h{C@OUXAXS>Y_-5y1jJ0yhoteL5VS%GZ<^zOj)!?s(HNQjJ6b~YL~kS`-IBMIlIhaR|9vG}XFFHviG}GKZ-jhJNi{f_RQQR8ungnX}0x% zT74O(w9k1wd=+oS$lk-rvbnIGFClizo9G*1L3p&n>cFFpAadfiC(wyN32Xr6HAHp@ z5H1}MyyKVx*uGIbFagzAp$x4uXE$LWFwc;D782t>Blwy+u13w&0lKM?Gb!f=Ac=g= z0FuT)yoEk=lVk3wyz^UKV@TW*3oOxy&6YK=w1-Rx2(og^_BZHn4kUbO0ze+GR%}p^gl9?c#yEDK9JN*=kx6BNkh;6x2djy9T)^O%a!87GUTo8 z$BTBg5_z?pyPS5{v%`kb(Q0_RG#mg4{hBa%V@nyhu-01JdINHN$WRGEbj{1^_a@S# zsMs>TL!XXkQ{d3)lxsz+8>rg2diXen)Qls$IF{v3(Vw6=+3e+KmVd|gUbmqkTjv?L zV&aN5e<}JB{NVLX17xLI!q;?rS7q(;ORIvCbM`e}y`sb|U-6cy`Z#O(UeWMEo(S31 zbkEQuAdbiXkxz`x9}*^b(){&ji@^2WDZdtz=+GaY60e$kICmvBwM2G8b&e2!`{|!= z@HyrvY~SCO^E*Y_GjFC{-qG)=FD3^2cV^^KfW>jZj#UM2b2CkT3EQ3rD7^ofF{;@3a`@Gl1rsYxat@C~(SnB!cISg5IPBY1^R=H`oH4t96-f3z^&CD%9f4U6A zSL!`hR;(zf6NL60N5lI!ux5xoMFx)3@<(EB%Sa3ohP@?0O;sLU^d6HV3QgN$-yVjc z+E74Yh!(gC4#7^+&*P8NBB*$?(WR)8ziT`hm3X%YjB?e$tl@lurlMb3{X;AovZ;eryuq9E;+Z_AZ>~su0%rT2BH!9$~EFXsybr<=ipZGds(UZTzSFnc04Wl<$$3|P z8||_5xv_88e|y|+N@y4$h-)kB>_p*fNt`;UyRb*@%dlkWa_*mHpl6Q2y5s6KXTZu% zzpx+jiMn66=Zp^fdp1kA^LJ2?i6ZN@i@pX{@ zxsn-EZ9ftAW>3w@Fn|s^h57-lV(FJ`CT|7UdiU4apm1C}ONl!Exmc}KsR(GZt62VEyoG^sHr=-bnf>}(!Ys!h%Hu1 zYL0jWg;C${S;SVs2Z?OJ$N78iDc2p_51uvzq)1Q4)=9xKr<7x=XXp?_JzuC^XH3-q zDbt@ZrYbW(r@q8qn)mwB2^zq64}YuubbK1UE~J}&Op$ZRv~GKwn;Gr?$ayA6Pm7RM zyXbcJ;ELgR(rAuq?1ljk3bF6guK(rdss#CfX4!2u&*K|5kCkSIUf-M*9G)AgVztw-8 z{AxM3sB4GInZy5!22#dvAXHDYRw3g@jT|-&?^4~N*{scxe&ME%+NA(hO#;EBebE?J zu^*2JTRg-767PSrI+%?1P`Y9<*}kVz4qq|MN=9Q}K~^e-c9;uVw6K)|8?YZYXwp&;e27s zzJGSdeTg#O!4i1&*}3rLI%6kK`9po3(l#+>;Ew;PynEd=CEc%_Q8gJ2xohp3%4F)3 zEa4G%{d!Bqwv(fJ*J8Km7>k0?U2C=_@(XtoeU!0y56_lX?HYATj-7uOm|Vjs_`c8Sye18EG>7h3alkhXL-OCKUj?+Q!GUzuTN$Z2BS*94@BvOJm|w#` z>|hCtInubKyz&U8w|T>{b{$#@`GX3w+SvmX7!0i zb5r%&4BZfYJFh34Q`3viDGbZ% z(ENqF3cl@aifGnhUVp~Rz{|}AVzWT_ih?7qtzRy1f2_I$891B&yIr@`CI15fLb-0Q zn)Ny^d1KL%ty5UZi}1~MMAl~to|0Z(|D_pS$@BnY2n^=~y9e=j8l@L$Ec3LxH8q0A zdkAu24Vy(uRY*RQuS=!v&W^WB!)<<6?Xund`1J01{CE0Aa1#$G1Uf%uE`m{B+KVnVk~%^ z%4|y>RC`t4@?Yuw9q3p6+Ab=Aw{~Y@E~@Uf2Bs36yKPH`1P5%@S;jfO6pNv9Gd5;G ze1)}miQD$6yjK^~&#UTraQ4NfZV&ep{<*JjO8T+HHF-Ed$tsVLJ- z>=BOIl55aDmRM90MdomIt$Oj2x4A}^f(V9d-7K81U4*$$h!a&>$|6ob*`a8je2!tC z_El%R`P@WsUVT zcS6T-S^r877!8T7P<1UuF+C0rT#Z`9VacGgsyYe+(!=wYp<@k`tJX$?}qY$-fe+SQmYF$R{^YIsy~4+Q&GcCD zJv5z}XWI!pseVYR{MuyU(n~oG=iri>l?@R@)+IBEZ3Bu0XE(3Tg;iCvMP)@d zj}<2kiZ?fLB|trLE(WKO%By1O`@8?9!PwwH#kh=4223>e{N=vvn~a}W0Px2E!&b#W zzjK*K8aS%swT=1=P#SQ19!}wEvC}r4o=Ob;R|1-6@JgJ^G;KU#RKIsWdgpcq9|n?W zs^F zpW8?gg&jjMmb%NkWYO<>OZl?s{IH?G0<);92FaBqQdI46r{|94xT2r+;ST)g;Mkcc zQ$=?}3AF=Z?PijoWO@{OIHVdSFQh76>lL}p9a>}ZBP&--Jrwyt8HI>@!5P*!MUS*j3pQ|U8ap<5!%LJ6-M>C%2()h0)G=lYwJ=7fN} z@gJNMu`0;(_MrXwuY4{o3nxcp=46&ljoOI8iu}q|!x&ewPGy{6r48~{uG5yxyx|~? zt2^}KETtQ2ie>5_Zm^0H307QZ;leiGJ#dXL?#`sTntC30HcAe7Z#H;zyV;$5JLh70 z&oM2X!On*H3`c6+?LYgB!jH(O{)XPgZpL+X6b1G`lu!ZdpoGtY&`BIxw z!}l6(FF|sR>)Y3*p|tbb@q-H8wgdZ>B1f$#RSPVEF{g^wl8blFS!<>Q%kUnmK)a@X zT{}5rC%H6|eHJT-LmQxtlk}eQJ&6!ATh`xQ0>mr^tfR*IPRY@{os2z#BLmb|BkCOR zAz*Npn^c&HaUH$K`NK;fGc3&ARcAHjGcR!z2VFFyMHNtkg2y*?uj>dk<~8N(GaT5% zwBe33Q-)o3ee9+HI!I(w*Ur4^1}DD$GQxPoa$9Z?RMI8MFR*z*Q>v{*flt2;Sp}ou zmid16IzWhb>*Cmxc_)Qq>Ru%L)zQ0ku=8Y2B zf9xPDJm`oT7zFzkig#@eDuB4l_@+XYt!97fnA9T&L}noO4+o~?P?xUYRIYN*yx_iq z&P?wE_t%|CK*_k^b&0p{@+{-8-Co@4D9}I000ProfZtshFeykqz^vIt@ar~-ZQc8E z$_m+=h@Az-a}udJ$)4vA{|Rav*Lp2-8PeB&PebcFz5Ca^$&yFfrrKd7jxMQfi8SsL zcshdXH8BqD@K6tKJ(0z1V0J{;JUfmc-$}5azNdPMV5Y6ahtAaa!1DBg~CL)2%SAF!n8aJI3lkuT?CY)*$^( zBi$V=HQrzJ)5P{d@!cHFP1S{3TenI^Uaoik%mETH3QseM_ItJAXm$?i(wp#ou`#$} z4^u>KDr{s%~a#`1)t{$z?Y1JGADF)MQH{qah3N!w_{0 z$lOxbri{dn608Cn9#h4~Iue#K(U(*HIT>1Iim%vE86B*bh@Rt2-x+JuCXHSSBSw?` z0D|h%n2~;}6fLYs*HDuO2t%?hCl4~x14ZDP%pDiQS~$r_c;X4BJ(%kqG)$*>m|M^F zDy&|wl&jTZ!&1qqfyLmiAnrLOPT5Ww;_10I>3c-$x9NF!!cvmW_tYf7`Bo*+bF-_j z?|m5AGU;u-y(qTeZx5We{8Cua#Xwjy& zSh}c&HGR-BrF|O0^q|ZgB@)MD8A_u?$E}b$TzL^Qj$}f8AL=xC9L`T4z%;^%^5lvc z?=5#w+)J|m5va)?G1O^XNrMr(%Ot~7_f5s|2X>O)$`$fm*o<@fK2*3K<+vCC-4yUG z)nORkmMuXDwXUQn_&i~J@8(lHp{~{)Gk5$G8?~Vkg$|Unxqh8k2981z?6}>e&UI;+ zGFUz@)=j-rW|A@$b7(Q}edeIuB}ASC4ehKv1n2`@1_sl)nG>N}xY*PadpUa9i(k(v zM^N?-u+?*iaL=?YiS|5s|Dr~IUPBCZ2Jh2Za_!UF%F_Q^+ircA)WS$KQQUDO1guj)_uRGh^snU%?dBb*1?kcBw0P|j{h!CXgG~RraD}Y zIRgRB3r;43`ZnA@-l7{3#Oy2>rK^j-&W))kYdRC4=QGc}Cv;VHn;%lo<(ULvccxiA zVGI?)?k(y$1%m|_7U$nFhGto7WABI!cv7Hv+T5s9*ix(R3bLFen7F|)@es`J5jI6j zLIo{M?M8wTRHe=1aGF4hPL}a`p$@96>GBW-8JIVJx*rFEFm4Dj1`ao3cRNFkm&rR= z{D3z$7h-FpeE0*_DU(a)~Tj~7O7U8^S4!K@-!QH1oTDxVSm&8f8syeW~6=n zq)dC%yk}4A*C~H!oojx{G7CpVM;G^I)~80wTJ5x`)pa(8&F%X4K&|X*H>(s!hn1K) zK$`ACsX0_)1!E_Mmps3FDsf`!;6k5HL`RSQTm>O?fK7`F^~)?o!7uN#*kL5(QXJPn z-vncdn2L6UwHp4pjc1|j`=g3u#0a)Y?}1T_*or?T9(uN-dDkbUUJC#Frf!YRX$-p9 zmRR()V)7|4!2FR%G((3kng~6Fs-6>i-nLOz_sSEYqN!Rxq$%Hu{P%1Rw?msC#~K?Y z8zVheF`^%mwnHaO#q`psGE7xVReUJ zipt8j(%Dysopi)}&HAq&^axi>bq3kT6#E|Vx)VE3frPMYnMALW>!JlUzx}#Pjh^_3 ze~Sd3F7JyKVGw=J-*MK(*iyP*b2QYGvp#40+c!isaxzZaL86qGG}kO+XDsTHY`H>~ z5d=K0-KcYowo+(voR(4n6MatcB1wFc$s5Xy689~{*eDNOEk1O*u+h>_ zVa?-Oquj5jXkp=cKi7^+qv>P0Tbcz9M<>>Swvu;H;-|&4g{(Gr^|?$il&A15D>-7O zl+_TFfL^mA63<&gFW-TdC*s6)kISupv(Vj2VRPQ`tUYlAk_iNgqG zE;PgeMs2qv*-JFeeu;tV;ov9OV=3Qnq=uu#4^rEW#_FkMDbrBRxTzboVWcj^kjj2r zkB)*Sow}guDKqC)4%a{aBehjcQF7P`U|u1ok59tHLA;Fepe`RR5m6Zk&yztH=^9Pz z9z1@KQTTW%O3Cp!u>Tqkru@2Jz4P1fILx>(?_P0xTq)aJ9PMrn-)y$9NLR{|5>Gj} z2`~+J=a@6|BqSs101nYoX8Q*P*7T3mz8>h?V!;yeJ&oe(bBAEP8hWbf#N@H41;)?( z;xZ`}rM#P8gJGqkCbQ=&1s4}=A;L%pPMNP>&l9QGW)+kCap&hh8f_etA&Sv&(E=}H z=?D&>H}SA&b*nnjFampjun*>9!~2`|dKzH09}W&0Vx|8c!}pXnTd0u>XX}L{$7XY% zx8ODty}r+<+Z+tpmeZyjIy5$S8OuOy!47xt{ccB#(h>B7&S^}Bl8NyfRg`9)2ulu2 z5;pd)aa=EZy-71=H-(w!Dh(p^hKa0*Np5fQ`W~U9m(wofpy75pH)OLyo3oqn#|BQJ zXV8>(rkN%0VFe0dW8lch4eqG5dR~RUGuGX9%=PqA8sMM&6=D6?E|X!y3F5WiXTVE0-VZ%{{BJ?#K`sZjArmw?qEf#aZ?nf7+^O^Z1e}S zlMoKt-td&$*0VSQom|b*r>vH0Lu&DK6bgDoiho+oFUVvKVZIDLG_0q z3ew~h`H36j= z>0e1@YnY!B<5@6d;(qc_?^DI12!}T3ZG=<1b$UyRke*j?f;5l(TbtcxZnYVkjpy@w{%)$7X1=}eJ?C}KT?t6(TZD3}6Pzo|BOxKg z4fvk9d>)1NT&>6{6O$s2QK)73C^h^djTzKiUEo5|p8*q3M#8ONtzzC?{m>HdvPqHB zrTiA@7*XN!1WgvN&q4_AdueMXRC) zUn`omp3&SR4rgH1LQbj5dC$Xaf5o~;emu5?ArB;ehr=9xGth)xcglI!(|amerp0<> zsE8`~cxY8g;+%C&T&jh*DLxwmHgWxMUV?^}jJ=H!^&Akqz|j64d&rg7thLQa0F~5M zog7rE$zRQ$rmS}TAWJX7Doe#9b1AC`hz|)>tN29^jB8EMzzX)a?( z5{tksEqnBCN*P>y)I)y_n=*ZGU|=|{3{SXi+%^ey21S-tj>Qj0!NhPh&b|JkyPWs7 zK)E0&m?Ub9svNUp@>kBBL_?=D}YU|gZ{tT%HUIrs07zS7b)Zl&TYWL$=1|DPzm0tgP zCnv*lC-}JkXYfAxrRDo4F^r#;S#EE@*7*RGO|G`O5JVL z%vFWE>OA1V*{qE57xWJ~y`S?U)JEmY$OgWICc@D#3yioDJog8)kpb2LMUf=p#R){? zX#`L0gqttp_45b3wGQ{MGQzHNkzX_=KM3(Zlk&9sBha1=lACXy$G(k{)iRBU9l#7| zhqOQEvY_#gxO*rKraHcR(gLcL4I{xZ$c&fV+37kUl)uxXW-CHi`Wk-F=4$l*8w7S{ ziSutf73t2>V&9lsesq1i%rT(`^8*IbXd|VM1xK$Lxd*;@#(JA5E;G!z=;;iUxeSD+ zF*SZFHRdp)Lx(7PmnoNcfvm)V?=tJ~G+C*wq8x^qYowVe#9_pd^&M%|w{&P=3ct*J zjc5O+ku&Zyc~g5kiDY6A_h4;@4iV%b$~ghdXA=4p>aw#3v7 zHkp)@`R7zf(|gA_Bg&p}NhKA*baXw!s)XLZ)<3((f7+5|E(h6jSQ*8YvnKC_(X70^ z+qTC>?S)qsRx`cNuo*qiF$KKD8~W|TYvsR8$<6t99&e#@83pWbOzmFvdRv!tyL(u; z%-}=`=^jBhmHnD&$=tD=C|F3bWskRE|57b}(TF<=lT;IKX?r|GrZ?HUnC!v)dM3bQ z*~N;92#n`1dWqYe-*Qj4*yPNt_L(vp$ns)8o&{yW3X!xz3DADK+Adkv*fcKxIITCW ziDh88qtk2_`OZbh#P1$009(qA%sJk|jYX&yG_&{Z?SOSkJk_w;8YbG=DnFHN7@oB| zj$oLVCpD7>!5~vkeqLN(v5Ve?tYoQ|laC4g1Xd@<-CQ)y+#nmHni~!Cd=)bvSz(c5_hj z@}XLp6@stOyB!~thN-5}e*m?Q%h5s_x z5A2s&!C1s$xAtQ;2$G9u2K-=MBUd zgpL#6ds=S=CG#ZQFhM9^p{8!~6TJCjRg~Cdu4ZLo0@ z@D2=8jbXU1BvC+vu0ThQ)7b{?-GM0+O>JC9#o%-KpisvKmR3({^f(!+sira>x1V+x zkhd%WW_Rn_FpgCh=!9@G9s|io+tBx712w_1jY>Aii?v)Z)3lq$-dk)9+sGT?x_k zCkhW7WDqbQFPq_Bj*2PG0rARl^h*buXfBNAYVF~2@50}Z+h=S@Ha8ajdtLMY|GJ_H zy~VX%5G-aZ#M*SO2=>sJOf9X`pjG2??|oiF|GykB?(oFb);OL-|(kNmO^>7vFS`Z z26_0VBgK8H0>1T6`?IYpbkxfp8h**dK8(73?rFN#+;#iQFDi325Az}hg3Ly5cm#`c zw>k4@NS|pB%$^GipfpXb}xGVgCQ>;>}VPb9#b2 z{e#n-j(4A&-Omhbn(m&dvP&V}@wW-09l)@P_!JFZ$Sho&cCwx_37ruA&(GlYxJwP6 z5Y(ydO(|ddyi?)@ZxU7#NmwbYo=bP+i|`!#4UQ9a9X4{cN49#@d=Xl$R1)V-5xiiUP)@2=or#~*}UxUmS2{=%HJ7KgM%LJ=GLTZ% z2~T%C*0oIWePSE~dxKx(PrS$vX-&@r(^E=ql*yaam11ou2%GX|uvEPQB_o~W93)+ z^&1anN955SMXSSGyX|&J$;0SDF;XPw(pvC~5>}-1l22 zeM%-v53H$2nVTB^A<4|8k%=IMBdh0(Nn1Bwdw+%Xy0Xc1rCyl1iMdDrw4kojzVne$ zZIc6%_F>(kivY~2NpvoDs{W=nT2Kbm&rAf?^YnwBrUB5_c~!Tj7TRWAh=3+2*>%+Z z6)K*W=!E#S`anaAU*i|dGCfiS(tjc&DoxELIOmew4V}Hur2H$c3!!vy1}WSFCV1-X zXqBEStM}5h%uNW=SKrK9B}l10iEX#XW^O^6H(D@OV*m?^J>=sx@NseW`c(!s#% z>O64U|7GO2(Z>P&?sGnq-{F^j;9g96-q+?i)5qmWoEBDD}vk zK*tK)D$zJfB3!WgtdzW)W6$#C$U-wWJcZ7#7=c2fao^LGKU>P!WkQAxIAY6!CQ9k8 zitNb^zd_MoUY+lc_s{S?9!*4_u25h8F4cytu7q3STTWUpu!|%;l9>= z%h86t5NlK`t7g{JMyS`Y!`(G)a^ubPtG}O|jrc7y5j!|mg0aEja0u&GvvM1qV!<{+ zpSoSb!$aL|Yu$S56B&BQ8lSJ1c3Z6D-Gdt{iJ2j5NBpyFo`j3v<~?FP$K9bOu;yGg z+%C(T4G+1!|6VJ&ih=v&=8-K@=;6!oZRLyqW__2a`-Zjen<#kE-|a#0RXWcdfejC| zu3=9)HsYoa@58!oKE+QCh<$`KoLHABSCgVuqZJ5pEekl&kbyzv{mJD_p~oaqK= zl+r+xGuQAh#FvLx^Pt&qfAULyj=0hDZ$yPc73Mbm3Mg!F;QER8L}i?#FpOq+Xf7^U zZSc+70RhdH_h!P(3oSyNzp!#0)v^JfmsYuL9E5(VoxmvLf(jf0k}XJEeXxB#*G@@r z$6(P7h0p^^mY%V{-!w+CJe%m8Rp)Zy6tqCgBfeDr&_7T!Qi&6H3N*~OKu0*_4&_Hz zCOug3qVdDyteQ~>Z;j#?VBh?K<_`86RPQ|0QI<(Q{TcP>Y7v=D2%Ip--&~DMeUyR>D9gw=@Ygz;yxX zAZM@@cD;|Vwa_~vd0Q_if(B>L8au9N_a%*gkt`EpJCb!G7LAy3wz?4$(M*0e-h+jC zTZ|hs3#?Y7bDgY0d#;LQF1Pfo(jDf#u;0&Zbq3ZleMe@K!fTc@9FB>5 zDb~lMAq`N@$%Z`Y$vrw1$Iu>gC?julO%8(G((6_=*&&o_M1Z0BhNNVWFy!4hTV34% zZUwAKRc z4nxAg%hXZ?@(0k%zel*G%F0Smu~pP}h|(jB*8qvc#J=^jJ82Q)Wtc4YC8uW>;#(8< z)_8vT(XeUKy^ z;Yw6TigWRGKdn0ujv@E0IuJ3Yrfys@C%3#`o~Jr$#c*&W^b}WvIVb+0eB!JcvF18{7 z=VM)v>K>rnr!XdfB}?|Fs8Cr4srwjVN^#(UTL61<%F`^*KI=1ceznF3{e5wr>whMP z=}WYuX+t3^!#LTap``60>yp7y4t=Q7kM5R@Wkj*)Z;+e!_!;qTm3*4i2!RUaY?yBQf z_S2syGJL3evwm)727{ew^;-?jA%P)_xgK3ilfOok901i%CMVUZp`kOH{Wbpy>7g?| z&p~x|h~QJU{GBCJG)ra@jGB=uKuR#h(@`oi+EzKa=SYf@ZkId6oo!w&aL zd-G*^zTZ1Mliy8$&e!Xgo$qHolfcu6UVVUV)7y~r7)5|u<(gjr%UDpdMoacoCFgHs zR~H-hF^;U=6|2uw$-4qcwNiPg594G-9oj>UHVaR>!}DizsL-tKZjNpb>wrCM_P0mt zEr+=6K951Z@=}T!H6`m;^FSF6lVb&0u?njj0Rfc?_?c(L0J80EwFb)=ijy%XWBIGP zM5(W-!mYzTOX7n-|I7~mB|2&xGkNn@W`ODp< zhA~N&X&Qu6KO-*ET{5}8g`}5RZ^MN<4DdVjhgQ^hvN zrsu=j+M&`{xg6Ab1`QlY2 zupE6kx)xqB02^|nhbSs9awUE>JGMny$MMH;K!|UlYnC?PR6^mYeiC5*?TH9SyXk?i zhMW^?7zm39;_HIGgc)>_0FgFDi8a}tCk#%uYSh#fNoRCE$h# zG5!qUA0-ArS&St%1@~PO#@ywj~v+)2->VyMrVtMA1b)j3*q>zkuWiuR? zLm$Lbf}uK4dd!?|9WUYCKQI>N&uC$EiMtP`rr>Om1A>lFrcjLNkFmKJ`IOPYzH2ne z{P_c1Rzv?$nOF=FfrrIqS4MRC9}awwy5sODs~}z#)&bx~YP#)9tPDkc`412GbrY$tR`Vaa8EDWoR9D}%+t1W;gIRk*$h6bsB$Myd^v#)d_(xis{+>J#02e$7 z5GUZ6C=%6jiZrX8=3=`dVgd_dyBl;L7y+f%Os(y1I?Y}HE*#}w<&@h^2ZjP>ViaL! zgfijUecB4Ru$34lU%uSLrhF1S_dg(XlOr;FEiJWODCHcDWW+)acJGs?*99=4RfraJv)`>q1rk=ajPZO9M7tt(3bcQW z8MDsCd+>pmTFDzC^RML2k=x~wg2W&wW4}g%cQB*FbLBrnC?+nxGVxdLm=wDv?@fq% z<<$M+QTaK8o&lIB%+P|LOgzXT@(=oXr@bMcDo3}b5t?LC)3l0+75HrH?u`W=#{ez> zcmDLpfk&pa!yc;Ku26Ty>In;L$u2(jX#m0ic<=gyh$miP{{YC3orl+nT4$l|&a{Ly zwLt2R>3hSn(s>fQ;5vH7uSHW{))l$#oWZ8q-+{w0;<{dtf17yyC&5b$D^h-n1;5W% zgwJMOb?+xdiba-nF)<^6Xr5%6R(f_kNO+oPu(D$2<3ax;{Vs5iyJW?jMN&S0&CpQ` zkVsZ~eIH05OGXQNpJ>!y%)y6!*qXNMib)&{z=sw#HRJUZu*8?jU} zS4k7sL^adU*dZzGih0D{fZ9^E4qh7Mg9}E~xYRB)wo2Z9`t1GUDx2=xTM@UI9c5f< z9&RT1Eeo_Ji?|MFdP*DmmZ@T1h=i1*rtxXw7g&Oa$qP)17Ko_Vl@;yIX;pX9>~@>x zP`0R>owlD^eE~OHrE;4YFVORC`97Af=*qiv=fWj<|J;^Rc!TA( zbWNmZpGwi)G;dTcya!9(HRCGuvey5~B(f`#k8kS73}l#uNG4C0YYyx`Y7dZeJtg{hq@ z#3OTe?59aqozj$_Km=0ugvNXAjZ7(bh?B013(rjJx5j|3-Z+Z%ybn|QZd%VJ%`}qy z=dFt0TB?A}Vwv@GR&bM!$$}|3>&+($y#6Iz+Brf5py>ir=P$xprF#%?ZE%QBvv+XCwV{B(ED#5q6Gf{?IMP!Pu5OXoD zJRrfaN7um2zY(mapF%J;Jo)}(=L`-+2*8q>w;}U*K1Nz#8cMX?NyUOtT4u?~4z~6F zy-WiyGYO*p@521&_G?&K<_!D!6wy_k+FL8aXO+$a#!2Xk1c-46Axd7u-!?5kJijJn z3uIQFA0Ihx;z-sdnf1JWOLfaFNN!%9lt0+Tn6m-G094{#454^}w7NG9D-n)e^t*>6 zXh8=v&E`Zg`&r0etWjSpNV`xpW6^IDdrYN!H<_rn70mJy1vDL|>^ha^CN@DoGfwIf`5j#eH# z2f!V{Bmf%OX%>G5CkIaOOnL)xfvsESKU+R0!8v~F{*0@lCBJ*q6Lky<%Z^mg5nH(Z zc6(*d5vM@1CV4A4^cvwf*`iDMD8>0j@mio0*f{44t2{Ugi!{vb(_d8#c!lqlax)eBZDdT0!T;Zf{?p*Zk*C%)$jFP~-o3 z)dGy(!6xXAbclMD@BPtKYDhcMz&hW-$j>gB^T+QJo}v@3+J%OHY(o&aBYEu}MRCIl zE1kaFkSmZ`p~0tn2r$0L8P<_f^c<0f;rz9xqaMhs5Kzl`~NbMO8leVW9*t z1l6*|p@#7O9-M3k=NEJMYyFM=>nfzEFz2z6GX;yRgy``VD1Z+CS*#YS9*HK;S>D~i z+n+(HXI3QT$b3`F+nqjV?lh_n&{i?2PwOh4g#|1PA-6>F1ew&pW%9oaw4np zaTWE`730TlF)V;X4;SbDfp=NN()T)?^`&jgPjH{Q^)vs;Isui!{vW#<9CodtW`GUk zO5{2*qHoi=QfZ$KgAl5QE46bB?}d>`3%gRiNXfSm?e-jUf!iKM zIN-lc2O=Quih}T__rYk|=K^V&6oqif4VCAWw*V}DpfC?Wc;OLu#e+M|AXvD+yT5hH zu7n&e>izq`8AX?!B_~bJt-tg-!R9_1(AxRoGCr&wB9(r--Why;&_1h;PdGbon^$CG z#+-HOlsa(*WxBKqW8>~)?g*mkYQ)Onrj2jIfBcNjxz)#0Q8v;h#edLg(^m9+{sUUB zv+Div{#yU+pPH6_D@LG|=IZP@9CcQZ`IxlNky>&yv%kg8 z#!->|e$h`mlCA{;r5_dL;J7pSU~K1ivO*nUh)#6pwLOz1Xg!Q~GCsW){|sm9KeCR# z6aB^)s%wn)_4hn|b=UEIsCAqmt!bB8I#i~d!eUXdJ>HSfRiLmxHq`e%U6D0@M9R-pnDA`Bt*8G!YT5EI{{atU%6Cx~XoW(UcS+P_4{9g{UR&7{6${Vs* z2$Ay@Chvk;1sip9ZgfVX{y2#*_nfiL;hd5p z;thBl9lyPm_4D{vj*?9Ph)<|~Tdh=I%$#fxQR`d5wND?@;}6MtSD%%S>3s!0$Hs?g z>SnmIEV=|8{QM8&59tF`!sA*i%zzfs|1$p1&%EeE9!OeBm)Wced=t}#L}2FY`HZ%p zhRmtn@f8X}t*{z|9F8PL|Ji4E&N2@p38tQQr&Fz#*G5;d9_7{X&!6b!a(iX1of*-tl$>lshqJ;w`0(YYEvzQkxnQhXB7AH09j71? zB%EfVV>IY22fJ1qL@JzTz$Nt?nLVNtXJu*sS{qE zUTc7lUP;{xPQe2^Xbgv!0B*lLLvHLM4{S-47>F1^zR*PXyPO+j`cF|%2(`rSd{gAj z{kfCK$hHX!XYpEJNNsbY)k0r}j?go-VL+V&&jTw^b*qL?797E5R{`(#S1-Wi>>`zP zPuCAZmG*pOM;cFz>Sw1uje-U^^>oa~h+zLm2U-xnHN?>jZ4PF|?x&J+WgULQ8zs zv&Nmndg95!$bPj=0xzWD1w<$I4h4wr6Z(0>bc|bO5kmN%`@)A|9@)Mtfb zg`%nIfn}lf+=b_(eQvG#GesOn0q#?n>EXCnv_&_ib3x4_>I$8DCx#kK=vhRg&z=yBeD}u713gbB zT?-TSl$mmWP||s9=`?aaUkiSTqT{_EIzu9n#702~gzDzb^W%@fH;H5681XG{Mf zEDQD2?~@ym=bmWnwIS+ak*!2DD_9YLqnuhH=t@L(Ca{)!%$fc&}YEP0BxP zU#7f2aox~}6JhMACrehh3>?|hF60&6Vi929CO!XkxA1Z-S}xXROg%X=urQdJ-KB3O zX)E3vbN;Y^xhz8~O3*c@j9Dq1YpmdBiVCn|Ju7Og>}lDsPjrVq;|;G3M}vEun~F^p zVNx+|RWjviR^gN*jFBmKl8N)|bac=u*IB>J>-K2&J$XmxGJN*i{GHGDbt%O6b~)$S z^PW1z^NfI6ppQ%WB>J#rjkH>%EMHdtmaCui_laT5TK;KR&4;?@Y!M?pp#^^Y zJevMyC$dPYFSE@S9yj4$M6i!OrZ;nW-f8H zlPz42qNnvi?c-xem%wrV{lD{gKNzC8Imm<}HQ?-GZo>}s9f7^qaAsdMK)pMk1F@)3 za@Wdl1sXur0XETBe1{$W$y%+}Z4;<1)>`&yZ`U1oKOD$_v|J~k9KpFLU%~I2WbeBc z51!QyPBNgx4BCY|f#ex4b80mpYz?jc^LtKrZpz8O>Xn~ZUjmCIkBJpMkW9t|D;zD7 z)Hy1yIX8mrAl~`)ULv(l0&f&s9BDYZVK`g*9g>63>Acv!9KX->8~DWK4rv&45fPvsUhv~XEv5#9yRzwS2};n!lK?jW?Hi)MYL zYAANz)`?!_pV!3z(O1bbZpu3ds-XPyz^?@BVF9rSw&j-*j`0=9uyAgx%4R8LdB5Ai z7#9_zkwKz81)Fo`JD8?QIIbki*?drz{dZlagrn+aEt`OczoXyL4wTEl($g;l*G0@P zFE-1^#hKm#Awhmc@q?Ls2u=t4HdODrF>^<6BWGYkzJ0UcAKSXW1UJvaH}I0zZ~A|t zZ`ry3a}hnI&sz^rVV9TQkn7926nyNZC&P7nSR(>1F~-eZP0Ci`ov}6cM9fJod{Ex1 zXAPqsCjQ{9oM{N{Bf-u(M{*nUQ}iVxU}*Gxc-dKGfZ6AP>63kiI(EV_svM7_44gfl zD_G$7cQ+)#JBKBSVCkO2*|bf4z?Cw3ikMTPMkydiL~I6^0H$?QYq9}nAqIQnTJSgJ zh%@!TGRoPLSjvs?gC~sDQ46W5_I!}?0r)NQ<*3irreglgL?i!f67vuOU{ca7k6-D8 zqivq}F=!X6dwnIX7zZe&lGVBJ&YT@$TMY|#rG7nGXVE|egw*nmAfNn?5?wtdy*&>; zZesgc0VAXMb5`$vY-kp*HPZTZh_Lo@ov%}J#`7oq?qlkT8nLV^Bpt2C4u{hzP1PvN z`3dEEKfUbeP?v#M)4a44AlMg02qfnoGc@&__mr0)Ath{@YDR9W{h>*c*ch&5(!A<| z4u-e!@aTO%yuI|)_ulUx4gvTQL>kA`wuw5_Fxc}za4w9Ttk}7>Z#^Vi!8VmB0X77F#THn^e(i78 z(Ih^i7tGVK+oO!z5WF=~)I9oK4^ly{72>I_q$SZn$m%w+X<$_5t+-oCPs27}dgy&r zAhz?SX$RURSiD|vVt&I@-eRKgaIH;huxAPaA(iRUgPlbv<&Mw!;`IK{U0`B0bA{ZK z`x9#fDB!EWM(c4s$p#cbRIDB`s4Ic78xvW{4Tm2vaIX6f?E}Ak3zHsNKKoyZ_3ufb zd9n5`m21%QWM&$9-pU1XQn&wZtXuH%fgd&@#I>O}#7U-NFH=b*vmA!U3T9WJ#=Y1BUzm#eI7&hr z0rXK3ET02o>HE%{={k7{)}8m~RM+xIJ)_snuQwqFfj26xyszx~_m{7Zn~Quyp}T1> z$IY!JD^lFSeFh7*49Wmh+kFQh6!|@eIThKKJzu%Z>+@=&U>fTsRSNQQa*X5Exs!df z-8SHkivyXzpt5cS13U2F_PV1SJbYIFE8PrsUjdK5uIs_*Q%=`LhSf#{72K}-BIBXG zKNo?LkYF}j9lL`8YeuwzX(j$7&}1z08w172$==0&NT8Lvtb2{752V^b&2S=mhU@fj z1XYf%DO+QJv5fHh4+?tci5!dwMT-v2U*^}&ULu-y%P;I|GUikLJ!?wuBhu<;xzV;B z=~jkGGjbfhRTnar2im&~z6A*LLlbR+FH(q7gw0)GiBWm~VQ2v0(p^w=e6b2btM7do z99fu-`G+zaZsh_WxJx*w^hw8WJT}2}<$sZnXy~({wL*+nG^Z4#?d9?-FSIcD&S$G{j;Ft5JuA7q=|?z_o_Q7Rvwu9#<%7b@U2++VO#f+Kwt-4o{IYg z*S$eu>;g~kTA_tN#^dw@VZlJVC^zO7d8?3KQ*UWs57oo)C{_?v23B89cbc!I_D}2F>=EFplf3z))+iKw?4lI0?VJ zfcV#3>37QNP_}V!8Xbdx`Pu!1Cy{zaCXqJLpv*9>$4o@5Arb#?ZD?@%5Hd+ zsts2oO3rizl@wQ+^bMfG;6~(Ll$KTEGI$c}9%LY+hEYoeQ$vh0sL1X)MGp9O5XX5* zABg$5xi~a&VW8s3ejFSF_W9K99kLG2#uF}I8obu&LVb>+jzi}%h|3^;6lg4pOjZ%( zha%Rv=s=fPz6$&y`+Kt2CM763h_s$wDSHV-(nZS`bC4RER+6@NL9}_E8AgMc05k=6wx;}{ zBdX;_JIO8-#~o@)`OijXyDWx73-?kKkNO_m8XM#qm;LAnXd8g0Y0+xOr&^|;$`H?l|H=nw&f-{ITOM^fRsPV{=xU*+B?hj^Ms@F42@-l7 zuxI*}7;w9o1u++(z(55_kBR1xN7BYVD;`D-xzDiYK0|zJIJDKM{*aHT*);W&0ov|n zksTVQlC~RWjl~hRLfcDzqKyw2$u@U|0(ZB$DBCXpg5%ud5Doqrxs_C((n!UJ7t3QJ zO(wK4lcak~UT%N;RB-*JxrI=G7?vPR@x;LxzCHR|RdV&t+rZX5V)6*6I)&)>ynWm) z+RXIScF7`kPZP|%ZoBFp+cE&mw1c4Nh^fX_ivk=j^)U6QYjU^nMR1MH3U*$BAfsc) z28+l+E1(DDS4Dv;u>VNu%1(6n!L@gW+#PIZ{_@)XqX0|?`YDt2}9+8)R7X!R*=E(p!wxy+uTrX@ej*L?PlBLsm zcjc$~s8+6ekA?HkFatm1su!+uO<)j6IieTBhK}$GZGK8y_3ykQ%3ZM!1N zeMvqI@=F8fl0U;IhoWuo+(nU&Euq|v#MF9 zJ8n8Uv^jZ7a?QPJFGVbTOm1@73C3E5dlZ-#J63B6ef9HXq}3 z#E;b!qq8@m6kiDw!`ID&VLf; zqGi(O6jfZh;icyaBX(+Onmd`wOs!64$gVE6+8JTY+U4L6uP``sw6naf)(M|(8H zwR_>pRbFY?AdsSM1t=f40B?7XKfJMj8Ek`Obgy2Qtj;I4<3D+Sg};qrA7nWSd|{)A z&Slo`|0!S!onZBQ@a}GB#>@>L1gQ!?1*NBY1VAd$c>m-6aJCU1Bm;lOe7S?}VCo*u zI#=KYTL|2n7#H6Sj{llkq55hlxgq`qAXQUxbs@5}65pDa8xQmHuLPbD-yuAZ@ z=i(%{7E;@IQ&ca>|gm%b0_w|NPFI7%+=U*Pl$mi9wCE(7NfC`|OMtb_%( zj1=8N7hp`paCWfMP*WfvKX^ymaD#O2nSHZ1soJ%3Jz}F_M!03!JhY7AM`^{U-ZWU? zHIB6<(SvNn1%hDH_eRBr50#SNg6h=;E4ROx2~crQ2fW2*od^`xP+DsjLxqWPl@ALW z5l9%dR`hy5lYw=Qe`*RijdDUUy_ZuH+AUB^+7N~}>G6h92!%?{0iqrc>{VUa4yBS0 z2vM39M5|;451xjVL|l{*dg+&M`Mo~9a8awk>eoQgwP@9bp72?$H2eYce=M}`p3tIR z8e-IOjg`V=`=0V{RBG8l4$mH4X4@BeI5J`tq-wqz4R zIk~?n+-AkHrUPH9bf;chsRBl<-fpJDgKX%EW~)Q+k@Jp<3pAy-WjWrfO`aa2z^(_v z1SQrq(WBV7L;FGJKDCqr5Eq1M^QiWSwbHPh7qzR`VljR;bYS*S3gYSEXP%5itI&r9 zL)Ban`-efxN{RJFQ}+DOO&qi%(*t;wo8qSAl%`g4g$PX6Nn;QgC$EJ}5@y>?|9pb( z2imZ|*VqUAkAe63#Tr#HibyE;s>Gkj$A~Bu=wbyR6yt8%i~>SC1i6Icl*P{GMzzrd zo{JDhI^bG+Opt;IlIG~Lh@W{=gSg!B8zwaGb+5#DxD6A6KTUckRGa?|hEEtWj_J8# z+5=@2h+(3Q#@`Uk+@HT?`pGGFM7D|H-Rj~*;(-(uU-lIVkEH$W`L$_GcDv zP_jbQzt@eG_3%111URtMc)++eZq+uq*1!X_!lkhj6xb*^Up~GG;o&+MWt>&Rn;DPP|nuwt17J*iR7NBbV5eF7f@G<2>>!vxeJ3?*Bd>Jv9>~?+rSyJ-N!3$3a z!qh^x@;UO~SyBajKG76P8jl254G&+s5fwugUGhIrxPYNdyi{s^zPIy4luWe~LN8T4 z?I@LUOXzYr?QH_wGjk-FvM5)usovEKjk)Jq$u#Y)FPE+|WIw?xP?c4{(}J;Mka4m3 zw6GgNaE{~`w0Fr+7rHaRYS}Q2Nq=0tWA&KEd0h=U%kA+hSF1?0J@`k3vNM94$KwFw zzif!uGT>D@c>}dUh1%bGU7q}}_RkiwEsLZ6?v$}+Z#%Hq)Ivq6Vx^l0&^A&xGdmoL zppjzbYLX_q8CsO>Pv&}L#|xE8bwHST0S~K8PzCmI*cy9}8%{ zNWnJdq;$5>`sR2}%TDYFFFbC)LD%=8#Pp41y*!EO>geQM^jFHvUaU0>{c$b|hBQwd z+)t`$A!VqyN%GdQJ7k|AV`JYfNjJCsW#K+)M-aVlmTJj^F(q+K$(YB3b0#3P>3h1x z^*Q(3=<@(?_oWzq&ad&{A~}rrF4?6y_XG^DNWBku#4KKSfXv`Ko;i5UQ<~f4L2$;O z0?7ka!WG|hbN#XSDZz&a~r`{>N_3sdW}bczhrF&_2{8TjpH^o z)ycOZL-K|fU!~5S4bv2yD018=iSgr-0sL~`m4EYAZlS{d=(oC_vIi+vBov~WyRp&9 zk14LaiCLwlrJdxh>Z;GXNn4@9E(4Ik;t?o*>h=`}#V7p*^_$D+F`^O3|b14Gs5O$1_0%GBsElF$M4#EJ50~MIPn??LBm^_L`P}Vv`68 zV3M90ZUDp*kybbjkSWu!U4fhf`6)vsqPhblA%f((K(E_WW95}x zUeX2tIr??~_Z<3O!h%3COO`~C#e*7$YUOQzoYa$CSMk3d|5{bc?JvsIBoEjUhbRdnV@nV6NIh1b)V5$!DF=Yip}`INYe+xN=v7HxN`ZS@8&gpF$* ziMf{?o$Gn1s-mO%_j-lU3u4g~Q*!{XB8#@bM;+8-7O}0ixwO7Tq`7OTT{(5+R6?!* zDaG6!k{T;q{jzh;xJk4Pcz%Tyt^Q(lrE2}g7(aJY795C*4w?Y`7Ghd@h0MlGlNOPe zdEA$BAI3tPED#u#*4U-h1B?fz7aXb|afHN5%`;-ZqwyYmmhz)D={2d`{&u z)TlN~)bjc>4pjD>ja-_e-F^+qV$llUoSKeF^ zOLh@V$?EMrI!R_=(F{&?6aqc83%te#96zv_c|Q1L{}Xhm*0t?32KoVZUJmWvH$ka} zSiw5Jdn>_o5oFdpa}C2Lz|jJJYC`m$?9sdvbSpD+0fZzH#p+ey$$R>zz!_q^2cm($ z=1N8S3K^T}f4x>neC+*~pL2^g(nFZ^$Mmvl>mbayvOH&W8w-Q68G-6`EjeDl2D zFaN2B2VOI?_j#^$sbtO7aX@1UHCEwuj4rK(R#r+|beE!*m;{?ZN66sq_bDpJw!EaI z1m%0%6sz~~H_dEoVX+o?de0URbva@1&i4Fwp<+al77jR3X*5v!8mljWVcATTDW~$c zF;3`R^7}!YO3r7{{59irx5Zp=L64`AUs*qL$?>uSOtYr@hYUA5 z?)i7q$Z-KDMT08zpU?8uOUytwZ|wSXhA(O_lNvdH?MFAOAP(j*{K;NJT7y!YT*1S2 z0UI2gCGk2Jd0eWt+#20IFhcEh!^x^8nH;s~8eYLl9#>R$laR3Mp04+$*R&zerzv^_5jVh?gv+u2^YiJlbuYpziOO z%sxa3yQpv+8AHExrLBI)787UgRiKfWJO7VuGoIogS~dB)S_Hem)Z?XrTImhe0!*pk5V+;SEtU$@EzDkZ1q4Oi;l60_8!_D*t=%+Ct&;?Ulw zTNVfL7@aJb+H(kolW7gL<44z81VnMAPqCN=Z$7HTr)IJ*8{4>wBn;*5k{3qu!J(OF z4WD}^teqbTX!cSneaier;Ol=>m8$19`xz$iiXUNQ_}S)WF600jsY}tZ@7*`}$heMc zX3bd!pSw~8dRV{Ull=oXYmGX>jtIz8)V_*FnzK{ zQ_G~uc{mI#pno1*zw2T*t1c%HRcm6)Jv$FA(9>Bxu#sSQQ6NfH{;e}Xwo@d~gjv&E z%#m`T1XtIx0i}(2DvSB+R~}u+$?xv_6&z>1XN085#mMS=ORrmtuS1T>YoXr-Tum!I zzaxCYF}3Y4*B52S%`=_5!dyN}Cq8@o%W;ENEh%Yx^8zfnz+~FTw_D|o+$gJ!)8^)x z69f)rq;ySLr2HBe1&ooqms>?$t?gb@zd=cJ(x#E((;27t=7)flFyr7ULkjUpZ{C!x z(=MYrY^Vy@gtRsq2 zeYxmT{Y-iq?KnSbe@9vVd zPg-PwU-~`_bdP{O`hZue(GeqPh4Wn=`pB<$6}{_MSqSRiCY}%xo=PzY#=R^##xh6+ z=-;>ax2KPt{2Igg3TXEjDr-mzZTsH$`TL^_?_90#Rm~14!bPbII^MjI5zE)dQ+bab zbzJ)&s4eNNkp(}kAO-L*CQ9k`mX$N5MNL!Bo+NR8o(k*;&@A>w)bcQrxsNW@Z+0t6ULI(GSJ!S0D4&HK!(VNchzF!8knVrq_UbA(VC zb$DexdM4E^8!3 zGme_xT>WTyt$D&YRB+9E?T%a9;!ZKu(rE(j_~N7=hc@8+L~8+v48Hphuld`36D-YH zO5#~tdvgwcm)uc9To`j`3}7FDj0?#RKjIv_VoGFE1fqb=T|sSx!pJW`@fNvs}n zAl*4kjeB4a!ZS>^Z{Q87t_CIZ&&bvCQu6B7o5}% zdVW;=L3~X8iXjMr7vK9h4f5ge^i_)9HpwV!W2dESZ!Lmwrt*YFFLu>loA0J41xOu@NfN9ly-pcGNAF4l?R zX41z`{oASTnVviA+V&?w0aK+bI~yqFZ_Y?KSkhyCxT)e^+>9k{^2E`85-$>k_im>= z9o!^^u3d38v2e_Iz`5{GKVrPRS4lo>+5&S8$%}hApTj3tbSniw$KQA}NxkmvZ_`B` zu?lOBBU3+@K(27I6z8M9(ui&!F&4F@6n0{H$G_rs6rtZ)hW#>a<U<`^NvGv;`MNuE>)2R`G4hVT-DI2M_Pc_%}GG7Td8O^3Qe@?0PO6ux%Azq1&v086z4+yK8s5 z+Ep*H6Pj8AIjsw`6C-K6eG|(yz{KA`MXl-I+Rl@aH8(Ar zYN(!dYy1JW;NyDu@GK9`j1q7!iT=;0wYLGK^o5npaxf)pa**H1hGeXKMz zwmeGUrZ?lonkZ%bMPpZX?0G-)&+Tp>L2XU4oIp)Hl7F-RE=kU3g|}PX(dX27!BHR# zP5)-CG5lw)OmFU{cAmoNUnO=~#RJ&{Wz5)k-TU%o6VJW_X7!z+Y7qCXbBFyNjGWDk zhGSk`Gkk2BApuqqP&PMg2dt_PW^y6vL+i$v?`&U~kE19knUcaSoHRK#+O+3zoQk>- zQ(^$ZR5!Cn>4O1`-S85EaroQ4eW#9RN>Tbc9L$j&8~v*D7|LNxWS?`iRJW=S7fUr!LK_;aV5uIDqkT_2Sv1CblqW703SKpV#B}{ z>S^f2uFO@35DE~*q-DxjDmovg; zaLeS%=?YiFT*O^n>`o@@_Ahc3W^e!bScA@2Ne@Ea80zZmp(9|RfK&&y1&P*ckWKEs z{!M*T7rw25k+#V<0BGf^8Rljqf@(w5g)!U40S0dHW`O4;N&(Z^bJyh1g=1T zpPixtSGixWD`y;pubjemfBVgsf;0u<&egxP2|Nsb$iNF7Lt`*jZolk@C4S9lU-<1i zcR#N;FXEHHN{rKTAtg9=8@t!jOATDu3&SPigP{&m ztia-ElBH8>rhfeuBH|iLv8e4GAZlG3n_XyNV`YN4g@U@8CG+twN+P=Uy)$UMzwI{#L8ys&)tY zb6Be4WmL_m`i$wrYmj>pp5V@ac0$#0OZ=qfv%?guEV&IGtPM7Z?g3QnEGUoQDOCdi ziy;+!Y7QGQ37+≻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

    _Y|Q)%Z!^2 zd6l>37`}u9(Q1K7otoB3F&lg|Rz7}T=xp@m6ck)?!LXrUzIJc?N9Bfl7|a`(xtK8e zk?G1A0OiBJ=scm`0q;}LUmUFal)5yHm^VmcT-*?wY}@%1Vsn{34!cB0C-aEUx}I~( zWx;Rg`eFdyvo%8Rre9y^D*p6w1%b4t!-U9fRaW*wn@QmvE9@7s>KHuxL(BnDrk^>r z%kwEX<2RiOn83-sS6STxdNudUN}5Sm`0VAi0?d1OFWW467P%

    oc#<>0u@kXCR|* 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 a|_RAC={=i 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�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

    NS!>qlC@R0iv2~D>TJKZO` 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

    &3Wx?*$d-?sL{cs36 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�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Ξ2_#&f4SwVgz~@#ll_m8PN#+yV-J>uPno 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

    xxf&j;%Ll=QRM4ke?006NC#p4=mIL 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

    s!_6bN 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

    0T>rUXa@x<6}ADGzPrtk)PCr{-hy|t zqSYj4j1P|Qd#*tPy_zMOw!Cs|5}>NN0lkLrYfI>|Tb|tL8|j?)+J*6ng&G1aQk^TV zeh`glV6}~PnQbk{o;++=15~#5IsMe2aH*rE&o5KxL$p+4-PDBowSqdhzZ6m4Oxj@K zAsMKN2||t8HS|qfp66t|_P|LDlX}2$nH60AQmIrY$G~Iw74iDv0{>`}Ri{eLzjGEw zlHE6+`52$Pgw^?=k<4^rFc^H`A!G)e7~3QY#$qd?7|Ik-X2URzO6T=A`w=2lH^wefMwa83z(sCr2=2jLY?dLZLv)Gc(oUsk{xv*Q@apb7)J` zy+0)pXR{mo!v5P0u59wkg?HMMtfOhH(Y>V~J9z>ayZ#>`JAE9IUwsRn32c2Nl7GkJ z(g`ZhmqUVwm^uc{6IFHcLEPp7S6`;c-{phDWUAW&T_XDii5|hxoUS#lxKiU$dL5vN z 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%0A2#FOK<-^G^=g4 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

  2. 31|k$h`^xv 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⁢Cmn%s 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π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 z zpkW=a;LD=k_`hL2HhGZ6Xt9TJJ7>eTJXT%^R#&bwvrLa5WIaR z9!J^5%edpVYYKvPCj%X9b(h&#L$QicHq#V@%CTbzu#-O)iS-26Ja9}?=c_GIj@AlK z7lAW&KNiL^V{j~!3zFz=c(0}eW4j$dHY4cqnIS7XRp0j{xfWD9%g$9xwQW7oCl^r$Evd0AbsV@Nu$2;j!3O2Z3)u!VBSf;v=J1MwRk8jM>Ru$(TpvpcQO{dMC?4+JkA zHwVdr=V?vdaQLIfOnbfV^oTBvYsqBQ2~h&I0U{TqiV3Ie9u?CIOiIxH7eQ3kRLtOql@`JL}T}dny8$T zPQd9KfuKkFsUV731!A_cG*vUvPjgd^kxQil0>N!0BMLP@N5cCO0qY`EEuI?a=9(}1 z(K}>B$K~<$P+hAb&fl3=4fF7e=GV&wN2R6p8QRZ$dwM$cIh+`R#;Qc)SeRmj%(-aw z;;K^3X;lHp9nB@~GE&4soRHLMguQ5uT0|$JE?jk&2%D%_ws~QE6eB~d_?3Seg*g5J zy-F2Qwj92sGrcEFD3Vg|Pn*;&unW~?56X%k;t-DA=BY9+mZPa;6?vbd!H#%lb!&

    ol1AXEM&T}0faz%a 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

    t})PQh@IVcdGJ`0gOUPG=KK9 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

      p~195D}+ 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

      !23RsA|vi%e(i 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

      PM5F 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 z zo^?ceMwwAc<`z1^6?>vzfI1MU5F> zstWv=*>dAC;@fi7s^sBdTN1t^oudttGPGIdS+bg?BbP>(c%w8F)|S!fmY(CpwMfY~ zm&cezj9V^@0mVWV%VmNzticvv4bJZ{9%8U5_!IOJZti$&OOS;HHAF54(Nh-jAD0 z20AoK_Hw{~<|&P6irH4qQp15Mi#eJVt|oj=M)(^81Y8RoyCoE4u`sG%repc+y&`Xe zqxj8+*KN2)sMSLnlFER78I1NhdUJHsAnvk7eXBQf1^48`L+Cy7Ff(3E!{2ftXpq%M z1A*JB0#yGx%ct%gYK*NOI`An)a9xI4$Qrq+xc0Ltr-1BZ2UFx zq8?G6ZbbcW;ohzRm$`$rs5bhx>XY->+VlTL-lRb0G;}TSY3uATGhgNtJ61>9cYmcA z(4QQq-o|+AZn+sn*-lQHVm*cBZS@$EgZ~$5XR$z~bHf%fBFD0lyGN;;nO9#Nq~3Nv zT-3&J^NtPxEzOImE|3fC9m3UrU!2R_LNxoTi(rWs5&tx9;NBB=EXz`dm;FAZP7!&o z6#uCnM@bBC8{2L0(xQ=9m>Q^;7RR;%j!R)48T%yTf0DVRQ8rdn15){`5=K<&o}jF< zk_hG@&-Hh98Y=VxoL$YWs{>!8Hf1WS_)L*)j*y)rY?!1_P5c7!O9Uwl4WPI&LZ&Sy zKG4<6qMoPqjFg|pg)2tCvFfDkim;u-t!3%DP$m}znV2E&8S2seaOE4A^^swE&~&rU zQMdO%!Yn`?6a%ZOifF5hj6uq^h|OZGMY0 zWtJ8fUrH~(z93wX5CX>v|s0sD=klz0Qpv_doo9W$CrTUWA*? z2LUJ(nQWGDi$lYTNjAx)hOMU$rd)?=X;9vU<{o)IBH#K&2v4XT97ngpmxq(_veMSn z?J!2*owDzi2KHx!;+@ARMsAA4jyFP(V+%c+&a4P3m^k~PgeVfSF^mM4>9_Ec<<2}n z94*ywjI44f%m)BW9qZj*X!k?1?l&j{YV4(P?+RA+sn`Sad~K9~BjMvRGZY)oZh+I z@};5n;rWPrJx^PI5R99rx?$7bCsck=)Lh`nmtH@QakfS)%s~FnXZIo4bJ^Ms!vewsdT^^L=G>F~+E;X}T!6DWb_X2F^ z5R>GIiJErEjsxe2kG~3=Af0D$|M_}2NxRT9ALs}7C!IDurw7HW$39A$G}vChM9fW0 zOgNZeSOJTJDIBnvfO7-*zI5#5Vvf>=LzZGUM96ivW#b+|J0T^o@~-%DS)U0t9@?vX z;Dd-nl@lI@DzRe-6Nd)8E3@EmtTcv*Gd0@5UK{|f?m543WWSNX)Y`9`^l>x?;ERdb z$ynj~HxJH}l4zZeA^T8$9u56{$%(LPN=~J|NC~)Duu8YniM+%Nyp!VD@}Zglt#mY@ z1q!n-kpikdv%}V4#b1Q++ou0`IfK|L0GV{jpBS+@<@b6-6;B}k@=pQ#niskhI^^*H zcnQ=lKQkXs%|n&WnSpMFcaBul3Ui$6E8EQmITOVID@ex zV!L}3Zp^)j#DITmDj)xOJ&D*Aw?{E`RW2f`0he1Tp52@ zc0+VC{GW6voxAugvlKU%gsz~^(f8j*;^fJ}T}L4-s2jY@P9O_ z1G~WWaa#3QZxdsffo^k8E7OZVwk)oM$Ygx7D6!jy-O*9EfVd2uZjKcX^Q;V#X_d3) zLb;hKCN&aI1|Vf(#mo1`_1*s^j(*~OPrB@}T=>uP6d4~Bg1q)!VZ0QF7mA7E14s?9 zjR+SjRM=pVu06tVeXY|An*z{ZQ9h)+Q;a-JyA$;Op&CsgW*WH8BcO)1U<|N8$I0?; zeG`)Q^Gn>iCRmb+CW9L-$3n}Td;2jax{y6AF6^4gsli7B7ynq1@sHrLn`YShnn4+u z$W2fi+u>4Kjb*S@n@EKmZi)SL2OGgVWGb};J@CX6r%;~J#{~Jw{C6{Qw!~f?pD&Y8 zJQReKh8bUrt{98Rzvh=EEDsAXM+yuv?h_*_b;2DDdHsTDgSoDKhjLQpGAnAYRCz&1 zJjSlcb9Q)-5xfa8fSUv>po=7+By621sijr8*n)wG3s^|jh;Sbl$r_Iki!r_&%C!h= zJ?-~1<;^?ZnKph`hc+z>V5Ddpu}FwLXxOSBL$tOhEqgrPDLWe{xBM1-79mSLy4o`9 ztR?$(++~a0o#xn7dHX5#0I+K09-jZtw2@04Tb-E}f#rUF%c57(d0rFr$8Tli;sZKj zD*X-bYx9F{r%a4TLT&PpjX4eYH!V7>fR+bcIL6kmhUxX2S#d7Fa@TN))4LL(dBE?i zY`KC+EA@-y?#psc0>f9gBhVIN4A%yBEVRBTy*iHUyk9HlJ|Vg98ZVtrc&&ajQ#MtW zv}}N!Vhc ztb&>%7xJb@jaC4quV^=*b^aZjn1LIIDy*!2c+nxJq;3r*?J%MsfD{E;Mv;F=84$cR zY*I;Es^l6Vl``#XZxyUp71WYxN&nzdC$>4{oSei zE!#p1tB?7U!9-FnLLWSsvdgXL!;`Kvs5Vm#48sQ^LGt9AFoZ4nri}mHOq(noi{lC= z-kF#u5*m)o2x)Fs!h+zpgd2w0XkLdZGcR0KWfUUjO~@1=%l9aC!pSP0m}{qV>*`lH#TiQX}VcbP}SvulpDLz@go zPia6ccBAfrWMin%ckxCsxcvxQNd@N@)YlQfbhwkSzV#V}knUFU3NwV1VX43*x(-|n z_&Y7MpNusRcue6mGNJ@wmj9|C(*0ftfLrcAs%-=%B>WIygCi?P0DyGRc|K-2yP8AC z7U3;88?eejr~xM&;J=1K4UZ!*&o?L5p7RCn$Sn28a%-|F+e3e6!CUQU8OkgzMtZ}S zFYUuC^63@#_d%f%CF6h`Q6izz#NeTp{Rw+!tm>F@5sZ)^KG(!jK^z9F>R@LdZR{yN zr73X|2@NsS%Ox=gXe1d*YcE#p_}R_7$^`Z9=dH&08tJ_Q16;gt zteKT3Rm*)UTRV=ak)8LqzQeEG8DKX8TD7a}yZ7xrE@Z$sw8=#;(~lP?9?4nLNaXpV zc&M!sKt`$vO_^{u0aBGA^IL$f3lGaVR$*rN#leH+exWe1< z+ehI8l?>ZLmQpmqP;>NT5s*Y4#w?|&X%@)Tvoj2=O?ISj2+832RM?J}zpgyou2I&! zkF;hZAa%fXN>;~1tb{{3tw^sLyW8h(UPRc?^2Gg|^*U#Wy3pW}Hv|)_e2|6V-hNy> zmps|TP6G!z9%PA|=5OiTYhi#IG|j$(IOqA3;#vaC_*!r+Udlqvd+6Bj{fWYaGUdHZvD&xzOQc=9NQFKV~s(*9C?Y?Tm5u0m4P5vJ{QD_Ox!mW*1Kn zA>P&vL2XT36tQ(!_|n*-GlS@NXiqISqz%PJ!D@;eH7sxd!$$+Fqhfdf=|GT~!=f~2 zIyD+G#71B`(O2O{Bf(V^jUq*=zPnbvn|LqKxN`9&wLmnJMfdXXw~>vPuc_Bz!W}Az ztS;a!<8V49diYnN%JUW@J4*9H*Gq@&x`*G62io7RXi z;A#w%nEXFO<0o@$_gv${?lB@`9AlD)#d)*lmY0}(FNr-w^Jzu=yIF*qg-jQ-wUCOlWEXZK#=QfzoqHOk75pdWo}1wEMT< z0X}d=6+F{(@`(@7XUvv~%QV_lFCZAin1I16Tr<%VICfFhi09&0Bq^GD>CXM;U$OFE62jIL+G{VAeakz^xZ)ne5vY%J)>+&zuj zJ`IuxT#TE#HXgdD`mkiMDsNYxDa1sm#F*R4{({l$3HHhuO&I<(1oTA^EU6Ot>z;UnyLz@imk(E zTkZzS)GZ&L``HLHwn`7Ut3hDFmEbwzC~%v=jK;ODvXUBMIwlSi0k_(}89GJRm_6={ zyFCDSfhLDy zggW?67mM(8#B{?q0q4GaDJuR?HK##oC zcL%qzdnH9`0G(zrgnOsOK*s=5W|0=!-dG*ZrJO^T4Nwc0B<625P5hL+)29&U^WE3k7P{uS~9GVCK0GBCnP3digI(c}pS1#z&*?+a_QZ)l5i=qYo! z!SL}9I{&JH?VOVo@2uY*P=%5ebFp;+?CTtcn2sQDh;|XQ(7z4?Hj}VaW~5m9##;62 zk?|XaQhIO2_AqWt zh#lH~fQ%*Q}fz196~s%=x_mR@yPdA z@0QVW@ZhrXwAj7Z>*;u$a2Ji41mP(hG}+sSph<@Fel_1OcG5VSPFUygE7X&iEpRRnxQ zXuhh6-3H1wa!gxXJRA``^XzEi-AsVIi;dZ}_BQ8V@CnYKtKyQSfo9($po9g);OL&d zw)4k!=2kM!&5LGsCcJ*92_tZnN-B;s-D@31nYM{FSE~G4^jdw=l1%ywK*;U6j~^l2 z80fpkeq)wS-AYKn)aZDEKlBwUCI>vb7vE#jXQ*8gANj?3PaM2qc??Yf8I$omvFx1w z{yb752kuP-j|G?82nCOLGHMbjkIeZAj#NZ9rCX%A!Zhx82Id+4eBC_~et3=r&k`4= zMTWY%po7biatf4#i-T0c-j_`58-xKyeB@Cm>Srpcj0fTgfjdN>l_(c|U-$ZUql1R5 zGPvTKhOo~}gfZXahiolyJ};x!ZU9VmcKe@Ku}5ocO^$a(`0XV&@1ol>WV)8Prwd5N zx$9ySc@_G59yjJW)UY>sNk$luC6+8)|1Gda+-fAAS$jPTBL>TBaL}91!0EVkL#lRO z)>;-*?l+)9v2R;sz$Tw%?MUJc)JVQ><`qFa#)bMqFW>ztva?J27F%!0&7dH~KgK$2 zLfz`Dz(B@CGq}TGo~Ln<-wQ{YM?_PKT_W$u4wBum_8v%$u^XEHYPB>VqK@tR4cRpG zqWG|!k1m{xjRC_&0;Q4`hOb)F-nz0!lZ=A~diIYODi$-hn&4tFmtF}QW=x_ud8H(c zKD(g1m7RyF7fcB7UrU9;ts5I&%Pqx`kHsOng~cx|`ye1o=I%pazORIH>V*mFV+@&p zV_;C2i@%$IESuC!BB8|aj=MrNYiL_5`ZXuJ3V{U9jI6sXnac=YU3RmO5=$@<&cv9A z)ES(8GR?j1;-BI%ixMkEcyMxpP(5_E3qhg-MT~D=Bu(Jv9Zd%U_bQ84m(~ArNQnv$ zTF&0J6GluZyaqrw3#&#zxtNsqCQM!vT3>7d$zvQ5Yg5ei<+g|hwVR@7c?ShH?m~4d*R%yw z_*?0&k6&tU-0NZ`)))Oe3fH7kOZ~g1Ve%dDU&vebfwhU*zW+8BR{->Nn24=M4n18! zG?O!u zYXdJ%$G#*jcbUr;+?vaGjHS(G4;VKTON97)MHM7K_ki;QzH^QcOqqFO#rqu_2|W)_ z^yy#^XlnwLK38^Qgi}PCq7NHA$Ufy@Mm9gr1~j1kw~kNk`^W-@?tm~CJPtb7QsX&9 z_r6n3Js~|QPx%(B_Gg1Zg&^bh$M=kIP+2HbF!27y)f5!W2T*86AQ_@B{@DPTwgskU z#zswsu&6?lVeJ!q4o+{bL=O{rGGgrfWvnpo?FIMBcw1HG4{_lTLOZ0a0%O%+G<}^g zc`0X0z@@<)58tXHEbCRHGozLu+$qgogkZg_iMSvt_X>k})EyDUgxD;mA&d~q2eYU&LbmFmxWS{~6CP5mP;|*mMQj`?Bd3nGR|Zk%_GK|hE#T@J z2(d9abNv2JZeCdpELZK;zn?4BC%u06UbXz|?3%+!??<{0Z>miDM*tW>L7^nLrj0<_ z96qj5%!oW{5Y+*(){kYE`{53_ANsm_(TXY+TK^VLh9-qZ1sblB?bUEemfR2d>IX9a zlc8MHd0S}DRdBQ+^qP0?a0LAx?8DG_Fbs;R?~!l}2Iq11xjySNf0X&WTT2bf&;->T zE<1}KBC(+mAyWkh6)DF98E?lfi}(=Gf#;|8l0OnTL}C@jC64!2pj#?zw#{!4usW$p zFreTp0bJ9pu?6*gbR_eGoc9#Nn~Q*vr|{h#Y%%aJe0MqnevZ$OgXN#UvevxxEhzm1 z8s@jAK_Fd5UqDX#xA)p@jw75FgVU{TTLz4rQ%ZkNJ=RO{Ze!=IZ(7?18Fxh52YDvC z(}h#njBEjhsO&U7NO7!sh?;{Q6{=?tz_Vi%W>mhcFZw>n03OKjFVe{3WK9}_#Hzj& z)_|ckS|*GxDS2;ghjd8!`y=`U9vN%1bsAcQx`Ma2u@8T_hc${V*psfHh_(_G`c9eW8fQspq7LdGCpx@~1ZckG4 z95Yc8mC8$Q4K{#@CF3S?$#s;Q`a&o6t0z&-{17spF8Zj& ze74IYzD+z0!VarnF_!Z_w#W&;@vF$tDcJ(5{Q4F(E6eR*Z8 znTI&z3Kj09iFB%pIvUtBv|r17G*yF;g#poAS%!+SRmrBu!}!8myij^s8wDl< zqpV?g)l854F@~qMCR~qUq9O1fJ6Tl&(cJavV|9S9f1-0WUOAff9FC?i?GplwDigE7 zxRr4x>JW&AH9qjT=57M`8L#sbC1^)r9#)SThai89TYs=%m_rgXC0q4#OMd+KUeFWr^0c!jgtVTnz2lMJ zEv%e7@Th;2$Iw28KGxS6C2QMyIA_A@U@YJhhr+K>st@x1^jeS{YaD~dmcOBgB{En3 zr>q3snNd)V(eMZ7baR5fG!iCS;9JV=K{@j;YD!Q!_Bej?$x%Y__-Ki$Tsg2qiOE~m zqm+}grT=Xh2pRup9cDx&2F6ZLbwHU@{0nd9jdA=e-W%#gvAOKujgrPoRZXi%V10l+@HFk-cJh-)-bWgJPi-wQt-fCOG zJ&eEZ&MBrqGP_dT;1s0jcfRN2=ODZO-n3Od9po>=5H5W-7R9z}#CsqAOpok#M0UDw zuu_KSx+*uIU8Qb23j*qOy%&mxsP8qaw9u_v1ZKU|ZJOLD$S|UU$F|1440-|J3%GWe zr@ObnA~~Ww0D0_a$7l3|CC@OOB%bEP-3TA&UvJSSJ|czcWi;t%638~YJ90wcVUaAK z7P^Gal38U5?eEBdiV^02S!+?nV&1TmZu^+v39dsR3Q0iNJyxIxTMr?@Z*T#ScOe`) z!>=!jpur9^V>7dYw^U}zAyNBC7EoWK+#h8-!C)j0?;x{xHS_fRpmx5$Z(I6-X^%M$ z&yW`HVknHQrP!c;M(|9j3l5|%iix?NrNImz#e;@<$oKs{*gE#hJO) zkh$ty-bX-%36MPFBjF}6HB>0z7vX1HlzsV~Le|gef*m7aI%by%Ctk`v0$Eu2juX}$ z*f7QPB~;3*ADEK<5ZLZ?fK5foV^fKFos)Y_{b?(B9+(P$@3$1{cdl|yBPS>(2O$4g z@j5~HtiHCq?0*|646uNPJ}tNA8%c0QAOAKN18+ZwLVy+)Nff#ffqE!pY}CI@{5RW; zQw}W&nf&3g;)F$mkL>cl!Bc5COjoKlb_!$+^{pk%`$$D7qdM$tt#Z>tuYx>`eSaNZ zI=$KAF-izY4Ig{#hv?L1L@Iu+9lkqBf6pz3_yd z70V650d}qfxtg&I!zgG{(Xhg^UH6sJ_wCms&AT^6kR^ zqSFJF3tnL%gvbzvV?E^+-Nse0;T1S!>~gjcGWtK=^R3 z&U^Ei0SR5IUJOY1kRck&iB(rJSA0BJxqzDPCwF=Re-L18FStLiLwiGjBrtV{ZA4h_ zz%+9S8cFsZqTM%sn0X@)m?=^yRhEQ?{;6c_lc-S6^ILPjAw{EGQ-7VPSzuTd8=kmO z{i}vUZUayOn%UkDa*#%>52uIE8i~&SG^c`BMIIoI(uF%WRb++TYn(j~N?Iedeh-E1 zMcwVfk1LPij7FS?qB4td%|=O#o=iSHteB??B>4!B2C4nA7T)UfByc8{LxR;;34kN3 z6jY+B%~;=I^}<7gg{9s1m-MR_Qhhee`8;5fVpFrrICzQEoa~{qg=>g23Rjnq#1eBN z<-Y?hGkWK0T!Ge-XbPUF!)W}~lS2Rb3Z&JK7DX2&4bM6+xs1tq)HLaZdr-&+at*#G zOj0X6F_d@N1zTC!IjuNQU(;uJVCT_in-@YXHsup3;tBhA-jljYWYni&28d(0#N zSj*FNC)0#fsHw4!&(alIW!5l=@4t0T@&1!AyPwbCCI22HW#kBoJ3nOE*KXt~0Kdac z8tx1UH@86PvX5~ms|X{aX7}p5np=!m=zZFRf2UCih6E{@)zu`~QQ( zMg6aqnzz4XI5wPpz*|$>t-ePGAGCdvX}IBZi|D2p_lDZ%{gKJd>-H~aXV}}RO_7o% z51W|=?nC7iFWxLU!@Fm_SrC7Y?n<&srMZF|pK|_fd?92#fT~j%DgvU!wx@ zbRvuZNLRd+MA+RGb+wB-G_ZML7ie{4B5fmAO`MFW(Q8xaptB7%&{{K>vO9F^3g)2< ze4edFof!n7v%$b*<>WhNML~M;rJw&YB3;PA1<&r*G8juiTdEdzWfzBzwY!F24G2m=U|z>~g3?q&4b9FV8#v=e^0X_F93CIMVU5 zY1GKl&e3i}lg7ktIz1oCJCcr*-T-%+2bZGztzCS)xBY4$P67!S*wH$tPZ9wqyz`Ul z?kyN>4N2Y&IK;r-hWXbXK^rK>ruXo~*%~3)6rzZI_dTL(kFTOz_@fxV9tfGW42;q5 zxchTsaRv!+5F=!Jo+Zbw>WCnU095ZDUP5g~Z4NMc7e!Z;`+OXH><16+4dCGbGh6{R zSgMj&6YJOTv~8>DMQKRNxg)`_;$MtN1tS2Jt*hWH9>>5Q6oYT`AzN;!#M9ZlGtfRB zcK1;R*(6w;yqun#4He)~lC5}*x`VuXF) zzTW@7ntqOVE%$>z+=wUl=f#r*FfwRRE3?k!$5>>=E|ru_G0R8}^fI;K99>Q_(u{^! z%Jn6S#ONZR+yj~_`!HJO_)-&t4HdVn7*kEILs|e?Kc@o7=4$~5^$^Tn1Wv+*+P$pJ z2k;^Jf1IsPQWtOVVi}jU);`&*s-Gf@=mZ}ILA&c9>}CRobNztY@xsS^~x^7yn?V=tIlC(V$s|HIU`2+g?1 zHv(QI+OWbes>7+MikKb%!V0kL{>#{dPCreD0Sj>-C3y6yai|gx^z{tA%kh||c9mb& z7g}IhlvQI&<5Ys8G78tIuTK0lnKNlfj>Wc>HtDekF?nq`JpYU48b3AvcN_(IpU_0T z6)IvRRV89@vc8eJ-7V!e4Jq~sHZfn8EF?_|b;8UNnbZe*Y&jLH18!?xKXDwf;S;&S zk}oKFDO?(je`#wWgC6Q+WCrGuOX97?x}BQ;+t4WrN;4vqB6BN#Rr5`8l)OE`la^w!%aRrAA82gAVtNWm^n2Nw-p~1 za#TrCA0ouNtNlK*M&7A33UFp01P#C%U+jlX{KcF2LB zxMB_&ACaI%#3<257yRzx1N~6C_Vws0B0*k$s~BGE%t(86o3X8SaTrvLVALKbPfoI) zPz-gN=&=(L;a;ikddFD>ool@RP2QmehYaW zD0@24Lp>*uluikjAI0dK9G_`^8ht{VPv?Fp@crGp*%OTHZf2rYKSFD>ImrUfIqbQZEX z19|nJ0bqZ8KwFOFQz7YvD$DQR;IqtbuB5?I6tO^sPm&s@NtP+0N8xyBQw5JqRy(+Z z?`eITx_!{hg@hz=2#zGStZa)maqg&Ve(U&$N|W`cLT}UpL%Jd8Uz8eEd|w<|&Y(P; zCaC&2xx3q>UEOB>W$s&nX^TC-z8dllc#rW>3vij&=h^m8Tn-j&OO##mU|gFs34)_d zf2qWeb`=#Z>*#B;U1xUHL%w|X0d?w4_DP+erog^A6vtfSV^{oN&_TC^tbZC@EW1l@ z#zwbvUT0WxNRfkCh{tO9JG+^Ty>Zgoe!;y{lWnE-b8E0FIn}oxCxwlWPVe39}$J*?^6W9qI~cppU@}CSG#eQ1RvTe9nM9>+%cy z4rDiQBrzU;^uD)yor)$jeuWbw&C6fEUJ+#v(24*YFaFgF&94hw{wEiGA)zshjLKXV zm3lVD%!J>$j)()VmpG@nW<05ER(sf|CP9)hAVj7G7E2_al=_5zqcYZfJMjp84@5B_ z#=z8BZojVkwIdFm0R7a=&=4LRi@xu8NbxUJnb#9#w`%(0O)G{ zmbj~Cd*w4f+b!Ka=9pn-ew4Zx-w_B_y3ywQepXQX3QB7e?n;X04lcJ7Mk z3#*~=r`0bO@W4Dza7Cz`I~Ui=Xx|Mju9ySFSM**GV?P>6-byi_JN?iYN}?b34H7mZ zozf{^J!JgkDd>HxrT4FW8&~>(pCHL1i|!5eY1h__f&*phDsa2a_^Ou&qjPwuWsAgK z_V4$fHZh9mrm{|n!DR+azgZ`DO|1P-d=3`?qnM&lxBR5%xkg}x4ey417Px*lUFfEV zO)k9?2aupfhR7DM%*Wsu#%H?XYAI;?lqfK_!O9{GVFdszDHd#S@#4CFH2~vJ+jZZN7cbrLS>Ip6`ME_?@nu1?+Pp3Pmr~d z72{um8{aY`NJ}0NDz9oHY5&{!l|@S82!wdpXQpRnpP770iNm5E;)%;k9NtoCsuAH_wC!`KtE!dvWqjQ@zG;mFR(&!j3mFUw|4;7T_iz(?!? zsLwXg=?M^2BYe&Hy}>nb?q%5*f4>QUWA``zq1jrYUzD(HBY~7byh%#%J7rV!ZTnDl zK3M_jc$;oPJ*M6xgJr4$kXw+*c(d>3%JcbSD0`f8l0qo`irzQY(Upip(9v|eggAO6 zx)*cve=HiKbPXvviXkBInTDd<_oUR5>)oejxA*kLXhG*`K0TMB&>$+AjC|G-`)jL| z6*m!V#z=2QMb6vnb#jK(FZOkr`f3FRwp#=SorY}8%EDZR)@8Mw2&t5cwg#|KC7+fI zd;GGYE4LI*^?o{AiZ4vWi-w6f3AyG&Cj3W^P5tnV>fq#)YZSvTUxX)WZ<#V zQs^Q~vH38q`vNvs`kp-O(!^<%CK3O)# z{J>9ijg^DiEOphb(hWj<1c=NZ?IEuGiINbe4yS`+1y~#tGgF-$`!*R3RC|zZOn3B4 z(fR$%cF~UhZVl4TZN+eMF??-9to>gmT}>eZ(`*4t7#DfK{BQED0+${Bhf9onu`^q^ z1F_w`pGW9~;aMSY0XY*7aar+bfl}qTc{zKIUD1!o=qi8^xVPF6OvyG=*!QSbtjRkch(A=SD+w!8}V^55TV)f2I(H$d(vbzq{ z1b}vONR>;1e%M3DfQL6zW#9LK5Xf`?ZWhDI93%vc6b))si^>-=Kg@?YZS&-isu%y9 zAHeFG5{;BY7NTYF-=uqUv+sZw4ow7XE_|j1fEvd_Ix&4x<5LZ$f*7#^fXXB0KSZNz zRfHj~I9{Vk2BZ(`1CLnv7@=J(LYeA;@OzfpWZ!PW-(a_;dgLDrQYczIybr(8feI+> zIC>`YCK{91Z8ESgl!M!eEeb9u2~h#o_koe(qq-4JAl6mj=D~OZ2s$Ta6rhm(2CWTC z1<#y9Ng)ul6rtc*7Wc9RcPe%eD-;8_JWZ#o%)C11`mYe_PRQP~jB0^WoC%Y-Dj0>V zxC{7f`fmSh_P3+3qjIHpo%I4&<8Gj#$h^-=s^+`B>Ft0q(Rm+6ksf?_98fYf6N&}~ zJ*-nth1S=i&4x9AWW9v`styGeZgvx;rDmn}S-TC-rY3m%q8~a-;?MB6+;1-ZsYa2m z;17=h;em>txcAre^hZF7*9w72p{ghUujfoY4ow7Adz&~8zhlf z2nhKaTlx<Ot>%P23rjY#L(Q&8E-054&Sm1m!N`oe<7ab{TB5ej+_P5ME{tTLjs%j6(WY zA0d2PJRV|IpgzG<1@Ss1#pNIpz@xAXEcKL1nVMrmV&!iYZ+~GI820X`HijL86#CmF zQ4Y1i*~?B?=;mqeT@=Py^1Z{dF}-LZH#fzo5g)-7r#a8(&-?;-QbDH-aX85!pbrYC zZXVjBCEShD9-#ls!aK_j}P&k;)3md?|om(FX4d{q}-fK!xUC)$j_TppHO0mrnXdjb20ljK4sFsle8AWpvG`ktz;Qb5(r@Wi?I9*e*+#-VsSDH z6a9P}4d`chu^X3zrE&ZNiO`Q%IrfxCR)_`|&Uc)cI0$+-+??E~*E6JW@W>+x>jY+w}ybuzK_IyNVD~=xnYmB>*Ft|UhyF_ZB5C2_`>KJ2o zu)BoBgZ|Y=1s2?Ga=`riKH4Eg1jR}ymY6S#He^g_Luv*;*!sbq*re01cFag~?{~gB z6@(_+i<4o`pkU1Q4#uAd4_K(W#oqge>x2A6g2XivF9X=S@;R6Wvbi81U^_^8WEV#f4FU{L7!4I7?z21QVTN-fn%qV(ssx0`P? zW!!HGOWB@fD%#9^OlIl_0j7wOm<{L>7b_vxcge}bX{^NAbSrS6d4)(`hpaaxr?)o> zjTzDfXJv$etf3}CU~rq6imo6>FpLEF%-a1=e$~q+g_a~Th}?kB8Ae2BeO$hoXeGxq z8M$pSjhP@pB7E8Luq$xQDk=z@6|^@FzP*?VLw@7OcEs^;JxoQr(gK*K81lS|V0l68(?KUF1U z8`G$GF)=6U4`L8S#3?;MivfT)Ir90H^}zHAK-4bShyt7DBwQuQMfVRKP~5W|bOJ~9 zF&m9bc zsIK&5dvd1_1e^46Mta8h7oCuD__Who$Wd4kF0N*74K&|;YyeU&HG-5KOWO&2`@I79 zf*}TLK9xfACR=0!xA1P8T$+CGQm)P%uq9VzP>8wB;7_*RUAsdeAU0Z&q7OIS6DCQ* z*aBT{$H@Pn1NcC}3aEinuWr`@4!>f8#2b#P0DOlGqcdESvfM8vin22XfDOm*sDK6= z8p5;at89Y}2}d!I0Cs1^+%tw!^WgKn^LW+2OY8nfV*|O3@aL1QN7P)$5_HqYMK(S) zFC0^mq_gi)xAIh?-LA8D5_-fZ#}WSH>Id&E`aBN;<%RSJhATUK3Z4I34jagbL% z%KCpvXbf=$3tZ!E05zW|mTrvFM6!o?IEG}+R`M+eSIcNb_@2a@B9c!P&`2T0P(veS z)ZT4*>@6DJS8g(k-1F&XZiMS4y-%-e~~Ay;Mk@SDUG-nTE6TQZYCx>Gzjwr z%z$-aUd=dfz!R9N5g;y);aE+JI#D%c0_wl+;LWbl(vZV-XIoDVTt_4$3wPn;#sH7M47(rfcv}mU7{~sJ+)$(i zRykW$rQShCY8%q*ql(a#<|8wOznvMb0muzgB*%w@Y^o)yCA{*7Bf!sB5mJQ-@8G{C zEwRC@<=fIi85x!|G->Gh-CK+$|AW9cZ3ayS>I7j6sgRv&j(}Ly$c@T@_m4`*2uow= zI?2Nue@gI-Kmwu+YSI?=-K>h=KIDyz$BsU21-S2XCbhJ6rQc7=V@#Wq^pgfRmgBl1 zl$1^e;KfVuDwh@Gb=noK)l_^ONnc_aWr^oGd5{aigdf4b}|t$Q{j*e zi#VmKNx^(?wM=&27XY<~VWjwka73AxSynv<&e{*Rb;iJ{zPIrGzbpmT|7x0jSNnKV zix*8PWMkFT7iwb)^6vrj&Ej{CVrsVn5lXiwMQ%}3epk1VSshLBOneW5)B)Wg%PGQ7 zzwOC5kJb4Vn=j8yGpQUx4=z1lRuMBPoUcm~0<0~vybOvt-pa~zecTk?ez&rgg|U@= zwbU>3&>|O24_>9l2#_q}_O&$&;*qk#md+y1{n!6{%+_qnmWURapL#WFIQ5)UtS;=6 zM96kDa+ZJ#MU&Y|XalG~OYNsNd59W5?O`ZmsL-XuON&!MiH|1dvq=AkuQFL3Y8?Z6 zNEJ5TVoVW99PP8 z@}U_~q|+wo)(XRh@=lXkwUR_|wQYDn|v0HWFIK&UNi(*A0#Ylij@MfC*ls_prJWma62)|utRbJMAoak*`Yto!-lhZubu&=1T;PmI~wd)6o_ zW>l@HctmzT;6s5hJvhC0*@pI_%=d1`W`qSr0E-DF9 zEl4s_j3?pj1(AC)-~~(Bn=K`itVV39pQXle0>TJ5oki>wanUjmF~6gOh2*y|$@v^w zG}sT&ELyVEpMCu5JUaHf2MC?BjAi)g3fva%4E?mrHI{^*7t&4Mp0n;=oF=L_C5*a6 zo<;HhD^CTLeW2}bSqsoi#pVGSp=vPKATjU8Bv`KvD&hQqGg#VTnvJUu{2>tA38Q&S zghn78Ihb!R0z)CBC&;tR&>3Q54`j>}nsuajMUF`P z15N2mE9|^3_&k2BEHF(#RwJ`eeZRRByxYN9$w=%%XPI@P>}reFNWe!0Y`-FluH1l( zcd)xj-6ogJJc_awfCC3&aj}a1_rQw_|M4km1%V^!kAj1_wxfm$*tGz?b-C;hc4PL# zL*y9pIVavt@plmJfRL%VF3MkqkzvQC~r)NOF|v8U|WAuojv;hQkz3kg^CSf4gH1A;N&2dlxWMC1L}Ds41r>Vaqq zsJzVefkY@4sE?oiA!FFD3GcY}0VJW%>AVYF>i=cDq3=oiRv?4cxnO#9O@&kLT3hHp zAk&BlCsa^i07O?W%MT%vVayn@F3Q|Dv^-SFp6DeAZN_+;NtHUJ=zX|Mlgb`OmDc9s zpLHDxPY2GMDOklQr;FK%QQ(+F?u$cD>mm98L2o-8hFB}kfi0Fz%st>*ZI=ORDy}7w z3huwIj^s&%FFq-{k7Vb7ht&5wYp6TQ)E3qT(qby#{!gqr4CCc_xpbyNcM!}6bh3c& zXCSWhIzu|8R_Macy$1Q`3qlsqKJN)fO2BP~szJxWgFX>H$^`uferjbOLJtj|(kRh`&ftd<{c|KlrpM@}mzbodJxDhjw!Fbgqk+qayZ^kc#nHl-nB!S^_{hP^vtOpNlPgX7sCn*pC}ssz)L zfGKsx`y;QbXiPxwNz4pZ6fBR>%EB}p7s{bdVzn@KC zhV39r6c_L}%;Rz~8D&suF{uph$JjqP*|F|@`3h&G z5lt&AN4up%9WX1e4!Nj&!thr1WhO6UwiDSeWF?i!&39Q;1P^gc555HG#ce&9Pkn{p?Vtm79Za zC_-0}!HH%x`w6o7sFOZ8TFvtFQ|L7*5E*s9Nm?#|Bk@T;JII4Ue2f~ROh$L}#IfHd zvX|C!T}mEpZDLYV$|CdVnr6F}Y&n8NFe4vB2CX~)5_aUKCw%E2vt)Y(7K406h$AL5 z$ewOK#c>JHuLV%c&3<=bB%P}>nDC@?`kxD#&IQ^~f z6G+X58Xb4)x^s2O&j?cRJM4JZRBU*&A`fIG2Tpy#e%*yLsls_X`QX2A@sISEpFCQO z8+Nu@U%4Bo(t=aj>`T`J0hWMT)@k9{ksi1s6y43(5=hjq*Ajf_sC-2W20LW*S;>({ z5S39KJRak^L=Nrw_Q{T|u}yo=w>MMCq8n|p*d}`&SSh=^1=s|IftWMiafDVX+46^g zhCIXGMTiUGdxY3OV}AsZgaflSe5Ap}%bg{i?BR*ZigiiQ2N8!BU<(R^Yj9er`a!k3 zl6;Y?Ck{UlG83yHdO=sU!wHZvW3x-m^5&;V;0!}O9uSpq!NO= zY@Xw>bPn_lE9<1@CP3i%FeiBoWtlRN_J<}eft%+)P0i0Y_tN<^3`zttcW5!@=u}f3I+oBY> z`Vvk@RNr(0WDs@-iQ5Yvh-6i)KzO9&{9)*gcf*$%0Ok;Zu{TqJ#*A@~=&b88O*fut z^N{S~ecx_674sp}EbS&B;v9GV(+H6HGGiYt8*0`PI$WEi6k_DJx7#mi!?s&7)lJCt zU6k<>2Lj(oAU;%#ssArZYv}i@q-rr0p_A6`2#3<>t2&2uyx>pjzSH3NW>jpWrtW%Z zGlQv;svfv(+REGe98BK++u&m5Z=>jceGy>o0|&0yk;|8ve-j~_1$v(hTcDv#fO=qE zF``B|1DjzEYGHbSya3F_vpyO#UXLaF$FY!F_MX=0$Um>--J}M8BK_qfTdF?-}34xMFz)^OSXI7y;mmoZq z2hP=5Khj@_ozZId|G8Sjhm%5MB62i@7P(eFfK%>Fa`i(^N3xw24Uhn4{wa!&C!y>6 z!AV!GpG;D75m;c+`9)mYNN%j1-9h5GW}C>^jees8>t*@=Qqy!i^vYwfR?M=u$)S7E6@1MNwO&T9%lm zq+LGr48ulTdkL3m*xj&u{lRJNOw(#ebQur>!4m9Na1fy2)AZdMx!OxJ0@q*Ag<;NM zSUR!!H>Ru7c9zVmAc_M}?oQh4 zMn^(6tHPfT3zQg_CBn5hN9q2i;ybiehJF+y`j`-_%1Q};YJnN^eRdUgSnw&vZ9Bx@ z$}aR9>=Pvg`d}%jJa!eha*(CY(CXmkJDxrM8T(+6<7Fc$qLh#ZNwExDe$PP{`Xb@u zC%>YOu@&0+kmBP;X6LF38zYg1*nVpq7#hT{`cy?igMJs2UW8)-Nk; zgkJAxY@}+9OLA*h)|44H~)BUV}HokYKb>49BYxl8E@Z!WLx`8qHM;1kD6BF z*pbaF(i73*`Qpvf6#P1yY6+Dngj<(+I!9af89B#MSN9}w{r7g`EX}7#y~ZcBZW8`? zFN)Y`2V+Yttdj$f|-#)ys5Aa0W;EYIE}v4_^@F z;*GmS#$md`(d+7%avkL{`l7LOT?gI+n`a0u#rS%5&=1|cpRslXO(@S+nHE?$EXe9h$VNR$MwzXGpp!*7C3 z{CW8OYR?%L3r#~x;&4vW@20Ka?`YvZnPMlDKkcX{wo%LZuYS2elGvF{z`F?eQ-N2d z;*O2`ZK|K-Mq!T2KT9!ZwS2MA~t+Z2RaqjQ7e;23kj0vF1F#Xkt^-GNS)ti@q0)`h408g-iFWvKmS z;W#roLh`NCCC3t>4LDK_ z1urP|gm*L`W)&83t*LGc{WVrDArOQdg?{dg4e27K9(T+S(nmrmAY(<{inrk)?t`M( z&K(FgWZuF^V}L0DUp^Tis@|SF^+x1st=J~g5(@vv2eX4ik6Vo?dnaefHp2fxW%RE#oj$jS#-T9)2H`%(QRBgxoYXHpI6R>0-n4 z+S4kIUf7pD%KtL~(rv4xPrqV*>RvLTL+vWRUquqkEZB|%&c8cDU(z{r7+ZoNnY>-w z(Oey>!3>`-!Ho*F;^hbTdqdpItS z%dyYU4TgXIoK#9uK+6_iVNMM(i1XaJb+;|ZarU&V8rtpuc}>Fm&Yx6V`nA!iVH?~y ztan6Sc$2W`FA34Z-m9lWLV=EZ?Beghqyl_acAImXwh8hOli(&HN|ZCyrJm$d89k%$ zgp3lrjci}0w<0Acp?4QqiD6K#0l0Jw6x9hM!)MOgjs8W-ep{&1lx)TQyz`+*mJ6hk z{zsL-tX(DK^0z~;d%4)4rc5c3nRN>1kME|-1b-*_pDlC0=Gyb^`hUj0q}#dPAgv>i zzOcR|_s8Dn_*DMgDtv;{TH`oB7auuA?qy#4E@Z!02gp`{w?xtKpKxSy#AwRbgXvq2>JSElt~^nz4?}OWDuh9o%IgLvEYX*QG?@Ph2|-; z2ON|udy(8r0wac7BVOX<{ir&2FB6?9vH~ifQuY=BMeN8jUh5p+3FYgAyTC_E*fh@c zFs>v9t@O8IW35nGXy}^mEGK!<-5G8G>b~cBM>PJqF1U<}#n8kP6Dx2`cF!=?ODm;% z{2hGH_a7OCDGGJrS$mxAIDUqz80DGd5=Vlq@_|hE2V_>@UqJI-UeE_4vX%Nqx$$4W zKGEEFf7=We-G7UU;+^PQ_^03!Kn-29wW zvL=EwR|Jt7%IBo7581bCSt#A^zjgIQXhRKDL*zbX`8of%_9BmmXs?GgMl9>{dkFvOupEE3CMVTZ;<-Cdo%Es(J2dxNWd!VI!nj`cB z*u8i%iQ(#jPN>~LS_91A{0{;M0>o)+n3SJ#Ob*X$@gx)-KBbj(?~q0x%hSK#i|fv! zNEw_`G++Z#H$KUHyhSxt;D}%x^-dk7j(>&(zCCn1(k)qi8h&UBd6pH(_jyQ< zpo*{Q;|L3BWW16EH$FLZ=<7^2b8X6`AQwOAVthx@Lsfe?v$|)>d1zH9tywmFNqGcW z!$7P4$BT=9dh!Fnl>pOUC<>VcG%%*8e)C|(8il{GTGfKfY_z-k2h^v zHSu3Ui}BcMkZAa)qu$6si~QUav#Jb%l`1WIcCKgT@|k~|J_MQC>o2}Kb9MnB5iNOX zQ?3ER%{heUj=pEBkx!(Yztb`mKA4uM+QbzHawWTk6__m(b_^1e6E{_AeMx95>(@n- zpvC)WV>^fS0@6zqzYxng_}F#9y1|TKhr$|=m|Bt!pkxSo`%8t7?jKt)_WFB^T4RRT z+n|NeIbY*GN10;*l6j_gU=;CZc(!>LzqiUa0j^^()*+?K@NA6N0e{qAM(-s)m{)j1 z$M+W=xy2O$4ah3c;W$}I;rf57>=Q3vv@5xt!wUbqLt2;%8t}-ci3?krn!5M5HxWAF zj1nu4u_LCPL31mN#aK!en953Q^FI2_J!sfn{m#2{@grSzv4Z3!${=9V&qj2O2`sG1 zW{)5l&wQrU+60B7_^0<~4zT&=xUX+a0_^}Mb87NZyhG6si8=}PWNQx$y{$LfjZ~_F zTIj+M^nZN(%b!4W{{y&~hU`Ge*#_K27J|;!i}ufcP}WEUu+Z)lqSw1jmmipPy+-K< zNKGZT?Q0<$fBwUMcqM^9)IGEsVd2y*3d^&=R&|DU`2qMY%c4m^SqDRN0Yw7r!4uWN zFO8YyG&VuX7nykAcfZp7L-3cd-7os%o@*i0B=4;yp9s$O&WB8AuHKtFA8myzl`9vA ze4kM3U;Bqe0Uol{1A*YDp>gL+;MiUOy%{Cqavzvj2|ov2E;=yR2gB`)#i~Q4^q+%3 ziM1i0#8PBs0z0Z2*P0utx*}TO-dcMffM+F3!xMH;?7355#wjve@>NX{7seTYB-h@<*PYBYWdn>AOk{q zEeoraHo($sDpCpK6+&9vM8<;Z!<25@Hw%thn%Bz@fDQn%kZPcUs$l{s5Bivj692Hl zIzdi>kIJ3@Uuy8M_eBsMw_I1+Ed#62xP)_fCkMEC2Q)Ft`+4L41r{A=I)`WzOX-&y zUL|{Mqbf8y<6hJB4mL1>oLe}a=c0V&!aOq;T$Z&>3Jd`RsSH?_x)Qn@|@Mr@*oiTZ#d5?X{MC_b}SoNcDJ(fwWT$IK9$Oq`S+xCU8D{ z$+D(~J8U6CrLmmmqEfE@U(zTz^@`9l2(q=0#%isN#d?mU^r`i>-IxF%J+`#`!yi0X zJ6F{AMbRC50Y#Aq57FUN;Wy1g5ey>o#_5#0i7iH!3E;Y)ql(}#q-2JQp(tHB(jm3R z#=_4^-cty3xWgK$<{Z-qjl9#J;H=dhFWIU#HdoiB*0pwHM-Dpfh!MPT#N}%$V(;Eym0eYO^tu!HKyicc zFOh!i*v|VHj7EsTqKM0R{Lkj4DLQ|~Sa7k@-8$@F$fyS0K^A(l%rVnT2>ywVj}=Mm z3%D@!ZliYFeRQ?-%>0&yID~j3DXYOuyQvgU^;zAVK?{y{za^r6&zBgbMkmO9`~8V? z_Yg7bKST&q(+{(?%l-xI=7Gm?g*YcQBrk|orH-cZ$nKIdba-nhRi%_k7|`A0hrC(0 z&nL`CU(^2zAPW4hI$Z5s)&1Mn6tq1u!EiBs;a^UVeZf`-RMXdm|4%h#xjtE7H5f~l zhXJR?Q_AMk)y0{dTWQ3n3gL!5$NrPah=%bm5aTOCmq;(hAc^e%R2v zvAQ*l$zBm1{0i>!x;aObr_ig(-(1VOQ`-o100lF%>$j99CMMeHM0*#Y{l;!<$DoQe z!N|C0=RKd$IJ>qkJRDP!QWVwsw!U6eW6DW!6=-p}R?u!|BciUvV{&4FD#zB>{0ddN z^V6FvGRqbhrMeVlo1vY33Q}Es{JID8=A7&-Yd_VwUpzVmZW*ffsP-z%ksdo=F9&7L z&U`={i006#22m%|pLkZJ!c*Q~yQP`3vWw?tpz*eOFyu)*Xw`h|f{9-QN7;f5U0TVb2K+IxN8;I{4PIU7xp zYDncnhz*gF$`%L!&~&V?b=LTseQlEZVAag(jgKhTNLtFlxw9!e^=SO8#40F2nrVwS z>m#DY!pAar(Esx#WE20e=G#R(oKr6${zZ7IMBBp;!Dao1r|qe~ss|TAmMljOLAM5= z`jF?Ft>6$JyGhM43>_>eEpx=$$kS-Sq9U}wR3oYRayE|!S`dzq*+Iyv>)&1>;?GLl z8}J&g4G@(9x`BANnTIXYA0iVyJqdlEAmDf$sl<^E+bDS3 zqLxY%ib%-89s9(SKj}NJra*>de8Bood=Gw6!_AIFi*&a-sX-~qg<5r2eP{(a+EH%x z*Yg)Bp+!r@A&ndjn-&1M+~;8VoR`}POqhF)--v8~!uBjTf}ZvmL~HWIW%xMM$dL*U z@X`TWizM{)Q_YItf0^x$5PphUF!&iV;(+W2Iwyk4_MyC_==8(CfY4fh9>0iQjdR=w z=}K*;7#IRA(A@DXoZLaE8qVIZbUC;L5z^sa`8WnX#{n#Rz-7O}srb#XJXJe}OJJzq zf#WNhxWA=WblQxV{(~`njdKT<#Z`7SHlwR(B6DqwJzn2k^e~(#*RqS-if{@jRUCq* zGd!{vf?hDpN5hhi)v7|hy2J}v2k00(D}TXXFMI$Y&xKY3xOGspACHHtClo^(K)GN) z$BH>o$E$)u>@pFl!88fq&CQ~0MGp|KT;O1ncu^MnV3}{e2>>L^@j*R$9SYNxh>fu_ z@PE`md@Hz0EcL<`Ji}Ps{0|^dseoix*v`jsiPWFoce*{(h7{?)S)a&5#MBn?M}cn= z929O?%Pn;R{M2O@D5yb`9yt!8YP|qFyNu^O)s$|-VPJOf{U$hhY6DsTKzV$Rg~0i4 z!^HP#gXyG*9Yj;%;o4)^XWcd|6_3AN8;%cv-bycjxKEYq;V{Avi;N@q`NXIRxAq>7 z03w_4HN0YZXmZ73nsJ~;d-#Xo9vK4grAjCO6AbFZ2*7VAgrumuV<(3G${BJ5kFiG+ zJ^eC{=Ge-546=bdV4~sZpFAnup`EK*7!*!!D-5D!uUDz1@RI(bMaX3l=N>magzsduhI;hV)d$iQbI5vuI8k~00?4#eNF)FGhM;^3r-&c0}516o> z10IEqaqSc~(k~tQOu);6j79|VwF{za--(Zj{zGrS?&b-Iof#Nkh>gSWPVI6JA;t?Q z_J>K%kU*xeStTguAN3~59$xsx{2l;QVdWo*Jfj?9kHeAd1PcB3W^XXdM5+q|?&PciF%FLl;Rm!<3jMgIZ zG(wh#=))vs7R(|BDylyFQm}2Do_~`T8Wpz7QWMLYi~@K8tVHLXiKBp6guoAfphKRZ z^)HE0J3__x5_!o>5h?p;!#WP-V6gh~I^vL0FxciI14<_+^2O=2$nmYg2EC1JSRiQ^ z=X~rGSR&iz(jWfa9ElLXdP<>8FrtqMC5yZs^no&A8NRgHMqjEGzlm=?q=**e#VTa` zf4KUlu*~~t?QGk&ZQIslOtxmSCfk^7+ml_Bjmfsn@Bd!=+b8?1&g$uE{nomP;yWqn zWJt?I`MS9xl?29i^D{YKwUr0kZe}(ml^@jUC5hVg<@52=!H9{(ziaX#a2&eTy|w<( z{s8;&){rT)QakgYOXYtWo&YmNXoH{f5`S{5URTg^b*TzQ8>oWu#XwJ4{JQPEMLTMG z9Z>d!!TWCp*DRTC(W{|3$ENCt3`lq$_`Km}&K)~obR{_{@(34UI>S_Gq4?}-7owJ} z+G?O}fW~1cFCv+v^}PL|OHr$lioO~nse3X?zFb78_WgGVh{&>u(7wyNf#)8p!c9_x z7V;kDBqluiArBMv8K#Df+BxMk?lYG)4VW<-&ms^dv&*;UhZ~b%QrXPcjwB&T#DD;& zL4sM{zL>yt>wX1ev!6c5DpZKW#mTuo)+|?+zX`@0tbW4iEE*?InaY1;joTgQ0Zj2I z_#iO~Y=&!4+X|I( z0G`14F}O0eup=2w*>&4n*VL-?)tm-ocISN@Cw1b-c!sT!c<+}0S~$S5--CiAmILHk zi%ah~&ZhEsKL54?Hxx0#@`kfRVT6s{A(R{osvF|dxCL*&=fe+*wQ++gGgjN-@*=k5 zk%iZ?gz}*R?76c3XAxHxTEwK}(7iy2Z`nqw*67GdgdYvcAiD0Ed-J@3Sgejy9~_H# z_?Aqi4vlD1&G0+=xiX%ONTtI)t74eCwelY5a0Z>y1 zbU=y3q=@C8 zExDhh@gin8@NET_ZXpq7=#70`+CaH2CH95!_C{KcEJN9^fwPJF?V^XlClRCpP?z1D z!Lj`Pf(spVv~;y5AV{v|YncAO_ziw|_CT6qH0*g?Q-vE^z&U3ZXbenHocKX1b8l!p zdmv1;3aDS*CF1yH?rDN#8V6GHybp*b3V<3Y#qJRnE?VsM*cJohbek9lKP$uis9%V%ae{)MJfUua>tcNx30jM0ERIY)$1`*ee{(|A|9TZ z{a7G{(0BZY`7^n#>Egpbob(v7mf)V)lvs>0nzF;8)Ge{*27HoJiZX2Iq z&ifm)>1(eQa-@ylLuSTeSiMjpndgikgi?T3U$-eqy}B~ML5)hcFGn0w{#AwZK>$#>ykRV1CrfW)E-KjAvuYr**SO7 z&O|h9k?HPytxvRz2!vm)hTvZ`VJP-Zl%T(?pW??-VchtKD&~o{oS?x_&~B`>=W%QL zt6CPoemV;BLEt=pZci?U3rJ|7^c%6PyI;!cOBuFNFQIw_#=N&k1ADs%);Q}Np(jud zP`qf#NEAo}>f;}Lq@N_0$_K~X4Cif2JUpu#2s zGMc3k>SJ%F&J>8#5hiCxUkPO;Fv%#oX2Jz4qvN8<%B}QPxMSbhh3ZA|7xy282E`2^ zeo&DID9(V(*uD&(R=ghzhAgQl8FwNtCtZ?1x>mo>2woGNwNE4xFCS<%4Hm zSNl}2!vF$o=pRS)6|6J@819oHKH(JmsF_jKT|Q7UqyDm>yYukaM;1%VUg35yRNB2M zy}x{NxMmZx%C40!gYJ%FdPBMfeck75Etk+MVnUpn7zDZh0F4q(UFz*sKrY+T2;^EH zS7^NO-=Q2$B}e?Taj+hP{M)rzeDlK1&6>{k$%o2k8w2oL=@(-lLv8ZbmN^q%C(|7G z+DQB*Z6k>?Ev8aC`C*aXgo%d2n9cuR2h0JPb{^G9gHOae`Z1gA zE`HBnbXbiAgDp?M#b};kL|U3h zBP>6zMS5x_%Lk5$Mpsaxp%{0Hledm~B9tcIzOBnS*HHYz(jvme#_mzrrllJN4EVv~ zL-FCdAhq&jVoehM?+nqXZ*x>9424OQqQ)SV|x^zQ>XQlh?o*)Od&K7ME^G{7xp+h zH6it`yx1Ej1gH?NZ|S`H2|D3)_s!B_(s%$idZ=yUF*ybEMEiI#1t=9)QTb^EPLfX* z8veWy?YzBY3ruZetJ|xuXr(GJg_yLB8(I=lLD+(TDL_pY#ig5odj^yhL{VZEru>+= zDX+7Q=jFfHp|jcndIK%`uk&knWh=`xS3Epy5G;7sU6_DQ@^o0!I2;rg8X6aAsp0^!96Mq<_t}>W2J{>gBtHUf8kWMN*3>m+Hohz0> zJH!vH7HaZOj|Se>|K7epq?Wx8cqI}Yw$PC$POBf|M<4?&)32592>>5D?PiKj9VFs* zp_5|;s9m`8W({;0o)VDQ%&WWyL>8|pe~8$5ZfYaru+T(f({V016&!wkfxsmEsJe>` z_(R$K((G8Mpt0coNn1%yh#k-$(`*1FTSX{NWt=-kDo%;(h8nwnFF;S>0*rzuK+Hh< zd$d}4&T)G}$A#fc5IKke$z-4YOccDhLqcv1|J3_vs@9D63onY$ru*d4X)g-!x|0fD zQ85uJS#Ut^+Ca7%&pX*oU&*-`>zgK7+ut}h6ofYDcoPmW;<8L#`I?hpVIiNWLdYZ%kcue-RCD|gx z0RL+eod6&>S?ibid^l0r#{q0)bIm{NG)Xru(gEcO2tcZGbl=7l1@Sr8swiQ?Yi=kQVF?4dPl5oP!n3{`kH&@AHnYK9Eh5 z8V<--^{Ub#)`J=3#rV+Bm_)G2`<*f1LWUAVpV8X8(k zA5#On8XJxF-U$$tJJ)S7$GAARA%c(*5JBt-r3+IEL1WFOuSolE00$^InP*_)T5^Ug zE6iHB^_?8)fy5KvM6H8Es6!=%!~~Y;f*4lgxPXZKp*YIju42w$r1$`45HS$(Mq?;- zrH(L|L@Q(v7J5lAA|wJ{O^9M3rEfc_J=&ij?>h?v2iM+_OFWgLt4&ksoP?!}vzihD zjfkyF9;9bF7>e02|6k2#eluxZI+@B|TiIB_Mhv~e@53_7(b<(S*n_xEv8ezc0wf4S zMO|wQ_#R__1aQ-VTU#sXXTw9+F{o2pionDaG9@wMcCd?dD&*Wa`+GV=AIuA>lcLjz zCmsEaT!EQL4d%r`GL;4s_O_fVaMQy&+{VeSG7>W*K<}Ptp{NVS9yJe96;>zm91w6<$vdr zSry;>pGksi{HytY(?RQ5I1{6)e4n3m_a_1%D@_wx);Iw5D&Mt0hpkc6UxTUa$+XVg9toRxY znmld`JvKGQi4Z`~S2)QfYV75ZV48TNuFh4k&it*4gXTug@zzCY3D6HF7yQ@ngF}9< zv0ha)n+U}ud%_l!aBqehDq>4Bmg$T=gAaQ`OgH4AxW z-RSVH7}hv~HA^T_~d~MfwnVLS*Hfvw%ZsPMuC%y`Ev! z$JFowkbvyS0F91KxLFvj6nXR%6fshMf21<%AR(r<9!RRKn_dAU*uV`^OCDm+N~=!i zF9K^)M?iL-df1Nu*L`u>-VUh#^P1nYkpaxU$UHf&i-cFVP*deKgcsm~V#2_4D!hAO z&Hi zwY_>RA?(6G%!?Q`RNBZ()eN(O^z+Gp6%(QBPiNKKDcqyLUzA<7JHTlN^9QIlFrDw! zx%&IJfxQ;*YJdjlRt(0rQz?l}Fjg?ds)6G^?tbR@yWdGh7iqYrsnUlEgnUFh0O=b55Dbv@2G3JhKtG=mfTMy%0DNZ1OQQS_HXoWM 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%0A1L)ZH%*eB1VY3un_yoPl@TF4S89Gf7biv=u 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 z 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 z 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 literal 0 HcmV?d00001 diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/LogoSmall.png b/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/LogoSmall.png new file mode 100644 index 0000000000000000000000000000000000000000..a3a2317c78293b59471bac6b289df83f29614b29 GIT binary patch literal 14038 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 IM6N<$g4PFbdjJ3c literal 0 HcmV?d00001 diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/PerformanceTest.md b/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/PerformanceTest.md new file mode 100644 index 00000000000..3997c31e1a4 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/PerformanceTest.md @@ -0,0 +1,36 @@ +# Performance Test + +The performance test application contains a couple of simple scenes to test performance of Jolt Physics. It will output the results to the TTY in CSV format. + +## Commandline options + +- -s=[scene]: This allows you to select a scene, [scene] can be; + - Ragdoll: A scene with 16 piles of 10 ragdolls (3680 bodies) with motors active dropping on a level section. + - RagdollSinglePile: A single pile of 160 ragdolls (3680 bodies) with motors active dropping on a level section. + - ConvexVsMesh: A simpler scene of 484 convex shapes (sphere, box, convex hull, capsule) falling on a 2000 triangle mesh. + - Pyramid: A pyramid of 1240 boxes stacked on top of each other to profile large island splitting. +- -i=[iterations]: Number of physics steps before the test finishes. +- -q=[quality]: This limits the motion quality types that the test will run on. By default it will test both. [quality] can be: + - Discrete: Discrete collision detection + - LinearCast: Linear cast continous collision detection +- -t=[num]: This sets the amount of threads the test will run on. By default it will test 1 .. number of virtual processors. Can be 'max' to run on as many thread as the CPU has. +- -no_sleep: Disable sleeping. +- -p: Outputs a profile snapshot every 100 iterations +- -r: Outputs a performance_test_[tag].jor file that contains a recording to be played back with JoltViewer +- -f: Outputs the time taken per frame to per_frame_[tag].csv +- -h: Displays a help text +- -rs: Record the simulation state in state_[tag].bin. +- -vs: Validate the recorded simulation state from state_[tag].bin. This will after every simulation step check that the state is the same as the recorded state and trigger a breakpoint if this is not the case. This is used to validate cross platform determinism. +- -repeat=[num]: Repeats all tests num times. +- -validate_hash=[hash]: Will validate that the hash of the simulation matches the supplied hash. Program terminates with return code 1 if it doesn't. Can be used to automatically validate determinism. + +## Output + +- Motion Quality: Shows the motion quality for the test. +- Thread Count: The amount of threads used for the test. +- Steps / Second: Average amount of physics steps / second over the entire duration of the test. +- Hash: A hash of all positions and rotations of the bodies at the end of the test. Can be used to verify that the test was deterministic. + +## Results + +If you're interested in how Jolt scales with multiple CPUs and compares to other physics engines, take a look at [this document](https://jrouwe.nl/jolt/JoltPhysicsMulticoreScaling.pdf). diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/PhysicsSystemUpdate.drawio b/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/PhysicsSystemUpdate.drawio new file mode 100644 index 00000000000..4d5779bc5df --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/PhysicsSystemUpdate.drawio @@ -0,0 +1,537 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/PhysicsSystemUpdate.svg b/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/PhysicsSystemUpdate.svg new file mode 100644 index 00000000000..475dc579126 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/PhysicsSystemUpdate.svg @@ -0,0 +1,4 @@ + + + +
        Apply Gravity
        (in batches)
        Apply Gravity...
        Setup Velocity Constraints
        Setup Velocity C...
        Pre Integrate
        Pre Integrate
        Finalize Islands
        Finalize Islands
        Solve Position Constraints,
        Update Bodies Broadphase
        (per island)
        Solve Position C...
        Set Body Island Idx
        Set Body Island Idx
        P
        P
        V
        V
        P
        P
        V
        V
        Read position
        Read position
        Read/write position
        Read/write position
        Read velocity
        Read velocity
        Read/write velocity
        Read/write velocity
        Broad Phase Update Prepare
        Broad Phase Upda...
        P
        P
        P
        P
        Solve Velocity Constraints
        (per island)
        Solve Velocity C...
        V
        V
        P
        P
        V
        V
        P
        P
        V
        V
        P
        P
        P
        P
        Broad Phase Update Finalize
        Broad Phase Upda...
        Start Next Step
        Start Next Step
        Repeat CollisionStep Times
        Repeat CollisionStep Times
        A
        A
        A
        A
        Reads active bodies
        Reads active bodies
        Deactivates bodies
        Deactivates bodies
        Build Islands from Constraints
        Build Islands fr...
        P
        P
        A
        A
        A
        A
        Find Collisions
        (per batch of active bodies and per pair)
        Find Collisions...
        P
        P
        V
        V
        A
        A
        Multiple concurrent jobs
        Multiple concurrent jobs
        Determine Active Constraints
        (in batches)
        Determine Active...
        A
        A
        Activates bodies
        Activates bodies
        A
        A
        Can spawn
        more jobs
        Can spawn...
        Find CCD Contacts
        (per body)
        Find CCD Contact...
        Resolve CCD Contacts
        Resolve CCD Cont...
        V
        V
        P
        P
        A
        A
        P
        P
        Finalize Contact Cache,
        Contact Removed Callbacks
        Finalize Contact Cache,...
        V
        V
        Not
        Last
        Step
        Not...
        Last Step
        Last Step
        Step Listeners
        (in batches)
        Step Listeners...
        P
        P
        V
        V
        A
        A
        Integrate & Clamp Velocities (in batches)
        Integrate & Clam...
        Post Integrate
        Post Integrate
        If no CCD bodies
        If no CCD bodies
        Starts the final job
        Starts the...
        Soft Body Prepare
        Soft Body Prepare
        Soft Body Collide
        Soft Body Collide
        Soft Body Simulate
        Soft Body Simula...
        Soft Body Finalize
        Soft Body Finali...
        P
        P
        P
        P
        V
        V
        P
        P
        V
        V
        V
        V
        A
        A
        A
        A
        Text is not SVG - cannot display
        \ No newline at end of file diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/ProjectsUsingJolt.md b/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/ProjectsUsingJolt.md new file mode 100644 index 00000000000..b0d8b9407f1 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/ProjectsUsingJolt.md @@ -0,0 +1,16 @@ +# Projects Using Jolt + +* [Dagor Engine](https://github.com/GaijinEntertainment/DagorEngine) - An open source engine used by [War Thunder](https://warthunder.com/). See [here](https://github.com/GaijinEntertainment/DagorEngine/tree/main/prog/engine/phys/physJolt). +* [ezEngine](https://github.com/ezEngine/ezEngine) - An open source C++ game engine. +* [Godot Jolt](https://github.com/godot-jolt/godot-jolt) - Godot extension that integrates the Jolt physics engine +* [Horizon Forbidden West](https://www.playstation.com/en-us/games/horizon-forbidden-west/) - An open world action RPG adventure. +* [HypeHype](https://www.hypehype.com/) - A mobile app to create, remix and play games. See [this](https://twitter.com/SebAaltonen/status/1726871354228482237) X post. +* [Light Tracer Render](https://lighttracer.org/) - A rendering tool, uses Jolt for object placement. See [this](https://lighttracer.org/blog/light-tracer-render-2-4-0/) announcement. +* [The Mirror](https://themirror.space/) - A game development platform designed to empower developers and artists with real-time, limitless creativity. See [this](https://twitter.com/themirrorgdp/status/1718019599361323023?s=20) X post. +* [NeoAxis Engine](https://www.neoaxis.com/) - A 3D game engine. See [this](https://www.neoaxis.com/news/neoaxis_engine_2023_1_released) announcement. +* [Sceneri](https://www.sceneri.com/) - A mobile app for creating and sharing 3D games and experiences. See [this](https://www.sceneri.com/blog/2023-07-27-jolt-physics-bringing-sceneris-worlds-to-life) blog post. +* [Substrata](https://substrata.info/) - A metaverse platform. +* [VPhysics Jolt](https://github.com/Joshua-Ashton/VPhysics-Jolt), a replacement for Ipion Virtual Physics in the Source Engine. Can be used in e.g. [Garry's Mod](https://store.steampowered.com/app/4000/Garrys_Mod/). +* [X4 Foundations](https://store.steampowered.com/app/392160/X4_Foundations/) - A space simulation game. See [this](https://forum.egosoft.com/viewtopic.php?t=451046) announcement. + +If your project is using Jolt, open a discussion or send a PR to add it to the list. diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/ReleaseNotes.md b/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/ReleaseNotes.md new file mode 100644 index 00000000000..62d9b799169 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/ReleaseNotes.md @@ -0,0 +1,209 @@ +# Release Notes + +For breaking API changes see [this document](https://github.com/jrouwe/JoltPhysics/blob/master/Docs/APIChanges.md). + +## v5.0.0 + +### New Functionality + +#### Soft Body + +* Added soft body skinning constraints. This can be used to limit the movement of soft body vertices based on a skinned mesh. See [documentation](https://jrouwe.github.io/JoltPhysics/index.html#skinning-soft-bodies) for more info or watch this [movie](https://www.youtube.com/watch?v=NXw8yMczHJg). +* Added ability to turn on/off skinning constraints and to update the max distance for all constraints with a distance multiplier. +* Added dihedral bend constraints for soft bodies. See [movie](https://www.youtube.com/watch?v=A1iswelnGH4). +* Added long range attachment constraints (also called tethers) for soft bodies. +* Added SoftBodyContactListener which allows you to get callbacks for collisions between soft bodies and rigid bodies. See [movie](https://www.youtube.com/watch?v=DmS_8d2bdOw). +* Added support for a vertex radius for soft bodies. This keeps the vertices a fixed distance away from the surface which can be used to avoid z-fighting while rendering the soft body. +* Added SoftBodySharedSettings::CreateConstraints function that can automatically generate constraints based on the faces of the soft body. +* Added ability to update a soft body outside of the physics simulation step using SoftBodyMotionProperties::CustomUpdate. This is e.g. useful if the soft body is teleported and needs to 'settle'. + +#### Vehicles + +* Added support for less than 1 collision test per simulation step for vehicle wheels. This behavior can be configured differently when the vehicle is active / inactive. This can be used for LODding vehicles. +* Added wheel index to VehicleConstraint::CombineFunction friction callback and calculating longitudinal and lateral friction in the same call so you can have more differentiation between wheels. +* Added ability to override the max tire impulse calculations for wheeled vehicles. See WheeledVehicleController::SetTireMaxImpulseCallback. +* Added ability to disable the lean steering limit for the motorcycle, turning this off makes the motorcycle more unstable, but gives you more control over the final steering angle. + +#### Character + +* CharacterVirtual will now receive an OnContactAdded callback when it collides with a sensor (but will have no further interaction). +* Added user data to CharacterVirtual. + +#### Constraints + +* Swing limits do not need to be symmetrical anymore for SixDOFConstraints. This requires using the new pyramid shaped swing limits (ESwingType::Pyramid). SwingTwistConstraints still requires symmetrical limits but can use the pyramid swing limits too. These are cheaper to evaluate but are less smooth. +* Twist limits no longer need to be centered around zero for SixDOFConstraints and SwingTwistConstraints, any value between -PI and PI is supported now. +* Changed the meaning of Constraint::mNumVelocity/PositionStepsOverride. Before the number of steps would be the maximum of all constraints and the default value, now an overridden value of 0 means that the constraint uses the default value, otherwise it will use the value as specified. This means that if all constraints in an island have a lower value than the default, we will now use the lower value instead of the default. This allows simulating an island at a lower precision than the default. +* Bodies can now also override the default number of solver iterations. This value is used when the body collides with another body and a contact constraint is created (for constraints, the constraint override is always used). +* Added fraction hint to PathConstraintPath::GetClosestPoint. This can be used to speed up the search along the curve and to disambiguate fractions in case a path reaches the same point multiple times (i.e. a figure-8). +* Added Constraint::ResetWarmStart and Ragdoll::ResetWarmStart. Used to notify the system that the configuration of the bodies and/or constraint has changed enough so that the warm start impulses should not be applied the next frame. You can use this function for example when repositioning a ragdoll through Ragdoll::SetPose in such a way that the orientation of the bodies completely changes so that the previous frame impulses are no longer a good approximation of what the impulses will be in the next frame. +* Multithreading the SetupVelocityConstraints job. This was causing a bottleneck in the case that there are a lot of constraints but very few possible collisions. + +#### Collision Detection + +* Created an object layer filter implementation that is similar to Bullet's group & mask filtering, see ObjectLayerPairFilterMask. +* Created implementations of BroadPhaseLayerInterface, ObjectVsBroadPhaseLayerFilter and ObjectLayerPairFilter that use a bit table internally. These make it easier to define ObjectLayers and with which object layers they collide. +* Renamed SensorDetectsStatic to CollideKinematicVsNonDynamic and made it work for non-sensors. This means that kinematic bodies can now get collision callbacks when they collide with other static / kinematic objects. +* Added function to query the bounding box of all bodies in the physics system, see PhysicsSystem::GetBounds. + +#### Simulation + +* Implemented enhanced internal edge removal algorithm. This should help reduce ghost collisions. See BodyCreationSettings::mEnhancedInternalEdgeRemoval and [movie](https://www.youtube.com/watch?v=Wh5MIiiPVDE). +* Added BodyInterface::SetUseManifoldReduction which will clear the contact cache and ensure that you get consistent contact callbacks in case the body that you're changing already has contacts. + +#### Various + +* Ability to enable gyroscopic forces on bodies to create the [Dzhanibekov effect](https://en.wikipedia.org/wiki/Tennis_racket_theorem). +* Supporting SIMD for WASM build. Use -msimd128 -msse4.2 options with emscripten to enable this. +* Allowing WASM build to use a custom memory allocator. +* Added DebugRendererSimple which can be used to simplify the creation of your own DebugRenderer implementation. It only requires a DrawLine, DrawTriangle and DrawText3D function to be implemented (which can be left empty). +* Added ability to update the height field materials after creation. + +### Removed functionality +* Ability to restrict rotational degrees of freedom in local space, instead this is now done in world space. + +### Bug fixes + +* Fixed a bug in cast sphere vs triangle that could return a false positive hit against a degenerate triangle. +* Fixed bug in soft body vs tapered capsule. The calculations were slightly off causing a normal on the top or bottom sphere to be returned while the tapered part was actually closest. +* Fixed bug where colliding a cyclinder against a large triangle could return an incorrect contact point. +* Fixed bug where soft bodies would collide with sensors as if they were normal bodies. +* Sensors will no longer use speculative contacts, so will no longer report contacts before an actual contact is detected. +* Hinge limit constraint forces were clamped wrongly when the hinge was exactly at the minimum limit, making it harder to push the hinge towards the maximum limit. +* Fixed bug when a body with limited DOFs collides with static. If the resulting contact had an infinite effective mass, we would divide by zero and crash. +* Fixed unit tests failing when compiling for 32-bit Linux. The compiler defaults to using x87 instructions in this case which does not work well with the collision detection pipeline. Now defaulting to the SSE instructions. +* Fixed assert and improved interaction between a fast moving rigid body of quality LinearCast and a soft body. +* When creating a MeshShape with triangles that have near identical positions it was possible that the degenerate check decided that a triangle was not degenerate while the triangle in fact would be degenerate after vertex quantization. The simulation would crash when colliding with this triangle. +* A scaled compound shape with a center of mass of non zero would not apply the correct transform to its sub shapes when colliding with a soft body +* A soft body without any edges would hang the solver +* Fixed GCC 11.4 warning in JobSystemThreadPool.cpp: output may be truncated copying 15 bytes from a string of length 63 +* Longitudinal friction impulse for wheeled/tracked vehicles could become much higher than the calculated max because each iteration it was clamped to the max friction impulse which meant the total friction impulse could be PhysicsSettings::mNumVelocitySteps times too high. +* Properly initializing current engine RPM to min RPM for wheeled/tracked vehicles. When min RPM was lower than the default min RPM the engine would not start at min RPM. +* Fixed a possible division by zero in Body::GetBodyCreationSettings when the inverse inertia diagonal had 0's. +* When specifying a -1 for min/max distance of a distance constraint and the calculated distance is incompatible with the other limit, we'll clamp it to that value now instead of ending up with min > max. +* Fixed bug that contact cache was partially uninitialized when colliding two objects with inv mass override of 0. When the contact listener would report a non zero inv mass override the next simulation step this would mean that the simulation would read garbage and potentially crash due to NaNs. + +## v4.0.2 + +### New functionality +* Support for compiling with ninja on Windows. + +### Bug fixes +* Fixed bug in Indexify function that caused it to be really slow when passing 10K identical vertices. Also fixed a problem that could have led to some vertices not being welded. +* Fixed bug in SixDOFConstraint::RestoreState that would cause motors to not properly turn on. +* Fixed a determinism issue in CharacterVirtual. The order of the contacts returned by GetActiveContacts() was not deterministic. +* Fixed issue in sample application that mouse is very sensitive when viewing with Parsec. + +## v4.0.1 + +### New functionality +* Ability to stop overriding CMAKE_CXX_FLAGS_DEBUG/CMAKE_CXX_FLAGS_RELEASE which is important for Android as it uses a lot of extra flags. Set the OVERRIDE_CXX_FLAGS=NO cmake flag to enable this. +* Reduced size of a contact constraint which saves a bit of memory during simulation. +* Can now build a linux shared library using GCC. + +### Bug fixes +* Fixed mass scaling (as provided by the ContactListener) not applied correctly to CCD objects & during solve position constraints. This led to kinematic objects being pushed by dynamic objects. +* Workaround for MSVC 17.8, limits.h doesn't include corecrt.h and triggers an error that \_\_STDC_WANT_SECURE_LIB\_\_ is not defined. +* Fixed bug in MustIncludeC logic in GetClosestPointOnTriangle. +* Removed the need for specifying -Wno-comment when compiling with GCC. + +## v4.0.0 + +### New functionality +* Added support for soft bodies (feature still in development, see [announcement](https://x.com/jrouwe/status/1687051655898955776?s=20)). +* Support for limiting the degrees of freedom of a body to support 2D simulations (see [announcement](https://x.com/jrouwe/status/1676311800797622279?s=20)). +* Support for setting surface velocity of a body (see [announcement](https://x.com/jrouwe/status/1662727355553443844?s=20)). +* Added ability to update a height field after creation (see [announcement](https://x.com/jrouwe/status/1713670512801390829?s=20)). +* Support for non-power of 2 height fields. +* Expose a function to compare the JOLT_VERSION_ID with the version the library was compiled with to detect mismatches between library and client code. +* Added ability to specify extra ragdoll constraints that are not parent/child related. +* Ability to selectively save the state of a physics system to support replicating state over the network. +* Added constraint priority to control the order of evaluation of constraints (and thereby the most influential constraints). +* Sensors can now detect static objects. +* Ability to override mass and inertia of bodies from the ContactListener. +* Ability to specify stiffness/damping for springs instead of frequency/damping. +* Added option to disable the lean spring controller for motorcycles. +* Added vehicle callbacks at the beginning of the step listener and after wheel checks. +* Ability to override the position where the suspension and tire forces are applied. +* Support for building Jolt as a shared library on Windows. +* Optimized Indexify function from O(N^2) to O(N log(N)). + +### Removed functionality +* Removed support for integration sub steps for PhysicsSystem::Update. + +### New supported platforms +* 32-bit versions of Android on ARM and x86. + +### Bug fixes +* Motor frequency/stiffness of 0 should turn the motor off. +* RotatedTranslatedShape::GetPosition returned the wrong value. +* If a body is removed between the broad phase detecting an overlap and the narrow phase locking the body, callbacks could be called on a body that has already been removed. +* Fixed flipped normals in EPA penetration depth algorithm which could cause the normal to point in the wrong direction for collision tests. +* Respecting the IsSensor flag for CCD bodies. +* Fixed double locking issue that could cause a deadlock while updating the AABB of a body during simulation. +* Fixed a crash when fetching a body using an invalid BodyID. +* Windows 32 vs 64-bit versions produce the same deterministic results now. +* Heightfield vs convex was not filled in in collision dispatch table. This caused sensors to assert and not detect collisions with heightfields. +* The friction applied during continuous collision detection could be sqrt(2) times too large. +* The friction was clamped independently on both tangential axis which meant that the total friction could be larger than the amount of friction that should have been allowed. It also meant that an object would slow down quicker on one axis than on another causing a curved trajectory. +* When an object wasn't moving fast enough to trigger restitution for a speculative contact, the contact was enforced at the current position rather than at the distance of the speculative contact. +* Fixed CharacterVirtual jittering issue when moving down on elevator. +* CharacterVirtual was speeding up beyond the requested speed when sliding along a wall. +* CharacterVirtual reported to be on ground for one more frame after jumping against a wall. +* Added missing delta time term in CharacterVirtual::DetermineConstraints. +* CastShape had incorrect early out condition which could cause it to miss the deepest penetration. +* Pitch/roll limit constraint for vehicles didn't work when local vehicle up did not match world up. +* Wheel contact point did not return deepest point in certain cases. +* Fix for engine RPM being much higher than wheel RPM when measured at clutch. Before we were ignoring bake and wheel torques in engine RPM calculation. +* Don't allow the vehicle to sleep when the transmission is switching. +* Fixed bug that caused suspension to be weaker when driving a vehicle over dynamic bodies. + +## v3.0.0 + +* Support for double precision simulation for large worlds (see [announcement](https://twitter.com/jrouwe/status/1599366630273712128)) +* Performance optimization that allows solving large islands on multiple threads (see [announcement](https://twitter.com/jrouwe/status/1633229953775828994)) +* Vehicles now support suspensions that are at an angle with the vehicle body (instead of 90 degrees) +* Supporting cylinder based wheels for vehicles +* Experimental motor cycle physics (see [announcement](https://twitter.com/jrouwe/status/1642479907383959553)) +* CharacterVirtual can now move relative to a moving object (e.g. a space ship) +* Added 2D physics example +* Added functionality to estimate the collision impulse in the contact added callback +* Added a JobSystemWithBarrier class that makes it easier to integrate with your own job system +* Support for 32-bit object layers to allow easier integration with existing collision filtering systems + +## v2.0.1 + +* Adds ARM 32-bit support to support vcpkg-tool + +## v2.0.0 + +### Major new functionality +* Simulation is now deterministic between Windows, Linux and macOS. +* Support for custom memory allocators. +* A new character class that lives outside the main simulation update and is mainly used for player movement (CharacterVirtual). +* Implemented skeleton mapper that can convert an animated skeleton to a ragdoll skeleton and back. +* Rack and pinion, gear and pulley constraints have been added. +* Ability for sensors to detect collisions with sleeping bodies. +* Improved engine model for wheeled vehicles. +* Most constraints can now also be configured in local space. + +### New supported compilers +* MinGW +* GCC + +### New supported platforms +* All intel platforms supporting SSE2 and higher (was SSE4.2) +* 32-bit applications (was 64 bit only) +* Windows on ARM +* Windows UWP +* macOS +* iOS +* WebAssembly + +## v1.1.0 + +* Optimizations. + +## v1.0.0 + +* Initial stable release. diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Samples.md b/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Samples.md new file mode 100644 index 00000000000..ce0ab17741b --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Samples.md @@ -0,0 +1,168 @@ +# Jolt Physics Samples + +This document describes the demos in the Samples application (currently compiles only under Windows). When you run the samples application the application will initially start paused, press P to unpause it. The menu is accessible through pressing ESC, it has the following options: + +* Select Test - This allows you to select between the different types of physics tests +* Test Settings - Some tests will allow extra configuration, if not this setting will be greyed out +* Restart Test (R) - When selecting this, the test will go back to its initial state +* Run All Tests - This will run every tests for 10 seconds before proceeding to the next. This is a good way of visually inspecting the simulation before commiting a code change. +* Next Test (N) - When running all tests, this option can be used to quickly skip to the next test. +* Physics Settings - This menu contains all physics configuration. +* Drawing Options - This menu shows all the options for drawing the internal state of the physics simulation. +* Mouse Probe - This allows you to switch between various collision detection modes to test the different collision detection algorithms +* Shoot Object - A sample application is not complete without being able to shoot some balls at the simulation (B key). This menu allows additional settings. +* Help - A quick help text. + +## General Controls + +* Use the Mouse and WSAD keys to move around, hold Shift to speed up and Ctrl to slow down +* Hold the Space key to pick up an object in the center of the screen and move it around with the mouse and WSAD. +* P - Pause / unpause simulation. +* O - Single step the simulation. +* , - Step back (only when Physics Settings / Record State for Playback is on). +* . - Step forward (only when Physics Settings / Record State for Playback is on). +* Shift + , - Play reverse (only when Physics Settings / Record State for Playback is on). +* Shift + . - Replay forward (only when Physics Settings / Record State for Playback is on). +* T - Dump frame timing information to profile_*.html (when JPH_PROFILE_ENABLED defined). + +## The Tests + +Note that you can watch all movies below in [a single YouTube playlist](https://www.youtube.com/watch?v=pwyCW0yNKMA&list=PLYXVwtOr1CBxbA50jVg2dKUQvHW_5OOom). + +### Vehicles + +This categories shows vehicles created through the VehicleConstraint. These vehicles use ray- or shape casts to detect collision with the ground and simulate a vehicle with an engine, gearbox, differentials and suspension. + +|[![Vehicle Demo](https://img.youtube.com/vi/A_gvLH4KKDA/hqdefault.jpg)](https://www.youtube.com/watch?v=A_gvLH4KKDA)| +|:-| +|*A wheeled vehicle.*| + +|[![Tank Demo](https://img.youtube.com/vi/QwlPOKbxsqU/hqdefault.jpg)](https://www.youtube.com/watch?v=QwlPOKbxsqU)| +|:-| +|*Demonstrates a tracked vehicle with a turret constrained to the main body with hinge constraints.*| + +|[![Motorcycle Demo](https://img.youtube.com/vi/umI8FF0gVxs/hqdefault.jpg)](https://www.youtube.com/watch?v=umI8FF0gVxs)| +|:-| +|*Demonstrates a motor cycle.*| + +### Rig (Ragdolls) + +This category demonstrates how ragdolls can be made and controlled using keyframing or motors. + +|[![Kinematic Ragdoll](https://img.youtube.com/vi/gvq6qdU3ZTs/hqdefault.jpg)](https://www.youtube.com/watch?v=gvq6qdU3ZTs)| +|:-| +|*A ragdoll set to kinematic mode (infinite mass, simulated using velocities only) interacting with dynamic objects.*| + +|[![Ragdoll Driven to Animated Pose](https://img.youtube.com/vi/lYHhe6HLbs4/hqdefault.jpg)](https://www.youtube.com/watch?v=lYHhe6HLbs4)| +|:-| +|*Demonstrating a humanoid ragdoll driven by motors which are trying to match a sprint animation in local space (green sticks).*| + +|[![Skeleton Mapper](https://img.youtube.com/vi/hrnmgNN-m-U/hqdefault.jpg)](https://www.youtube.com/watch?v=hrnmgNN-m-U)| +|:-| +|*An animation is played back on a high detail skeleton ('Animation') and then mapped onto a low detail ragdoll skeleton ('Reversed Mapped'). This animation is used to drive the motors of the ragdoll. The resulting pose is mapped back to the high detail skeleton ('Mapped'). Note that the skeletons are drawn offset to make them clearer..*| + +|[![160 Ragdolls in a Pile](https://img.youtube.com/vi/pwyCW0yNKMA/hqdefault.jpg)](https://www.youtube.com/watch?v=pwyCW0yNKMA)| +|:-| +|*160 Ragdolls being dropped on a scene from Horizon Zero Dawn.*| + +|[![160 Ragdolls in a Pile (Sleeping Visualization)](https://img.youtube.com/vi/7ZMm7yObpqs/hqdefault.jpg)](https://www.youtube.com/watch?v=7ZMm7yObpqs)| +|:-| +|*160 Ragdolls dropping on a pile, simulated using the Jolt Physics engine. Yellow means the ragdoll is simulated, red means the simulation is sleeping.*| + +|[![160 Ragdolls Driven to Pose](https://img.youtube.com/vi/jhpsIqbsU4I/hqdefault.jpg)](https://www.youtube.com/watch?v=jhpsIqbsU4I)| +|:-| +|*A pile of ragdolls that are driven to a specific animated death pose. This gives the ragdolls 'stiffness'.*| + +### Soft Body + +|[![Soft Body Demo](https://img.youtube.com/vi/vJX_3FNISkw/hqdefault.jpg)](https://www.youtube.com/watch?v=vJX_3FNISkw)| +|:-| +|*Demonstrates Soft Body physics as simulated by Jolt Physics. Soft body physics can be used for things like cloth and soft balls.*| + +|[![Soft Body Contact Listener Demo](https://img.youtube.com/vi/DmS_8d2bdOw/hqdefault.jpg)](https://www.youtube.com/watch?v=DmS_8d2bdOw)| +|:-| +|*Demonstrates the use of soft body contact listeners. You can use these to affect the collision response between a soft body and a rigid body by e.g. artificially making the mass of one of the two higher so that the other is less affected by the collision. Finally you can also turn a contact into a sensor contact which means you get the contact points but there will not be any collision response..*| + +|[![Soft Body Bend Constraints Demo](https://img.youtube.com/vi/A1iswelnGH4/hqdefault.jpg)](https://www.youtube.com/watch?v=A1iswelnGH4)| +|:-| +|*This video shows the effect of bend constraints on a wrinkled cloth. The left most patch has no constraints to preserve the wrinkles, the middle uses distance constrains ('sticks') to preserve the wrinkles and the last one uses dihedral angle constraints to preserve the angle between two triangles on their shared edge.*| + +|[![Soft Body Skin Constraints Demo](https://img.youtube.com/vi/NXw8yMczHJg/hqdefault.jpg)](https://www.youtube.com/watch?v=NXw8yMczHJg)| +|:-| +|*This demo shows a soft body that is connected to a skinned mesh via distance constraints. Each simulated vertex can deviate from its skinned position by a fixed length. The green lines indicate the animated joints of the skinned mesh.*| + +### Character + +This category shows how you can simulate a (humanoid) character using a capsule. + +|[![Character Demo](https://img.youtube.com/vi/YjaJT9of7UE/hqdefault.jpg)](https://www.youtube.com/watch?v=YjaJT9of7UE)| +|:-| +|*A demonstration of a game Character. Demonstrates moving, sliding against the environment, crouching and jumping.*| + +### Water + +This category shows how you can implement a water simulation in your game. + +|[![Water Simulation](https://img.youtube.com/vi/CEr_LtQLGeg/hqdefault.jpg)](https://www.youtube.com/watch?v=CEr_LtQLGeg)| +|:-| +|*Water buoyancy and friction simulation. Demonstrates how various shapes and compound shapes behave in the water. The right most object has a lowered center of mass.*| + +### Constraints + +This category shows the various constraints that are supported. Constraints connect two or more bodies together and limit the relative movement. + +|[![Path Constraint](https://img.youtube.com/vi/6xMKNMjD5pE/hqdefault.jpg)](https://www.youtube.com/watch?v=6xMKNMjD5pE)| +|:-| +|*Showing the path constraint in action.*| + +|[![Swing Twist Constraint](https://img.youtube.com/vi/8aQ9x8SQSuM/hqdefault.jpg)](https://www.youtube.com/watch?v=8aQ9x8SQSuM)| +|:-| +|*Demonstrates a chain of swing-twist constraints (usable for humanoid shoulders). The green cones show the swing limit and the pink pie shows the twist limit.*| + +|[![Gear constraint](https://img.youtube.com/vi/3w5SgElroBw/hqdefault.jpg)](https://www.youtube.com/watch?v=3w5SgElroBw)| +|:-| +|*Demonstrates a gear constraint. Note that the gears can be placed at any relative angle of each other, so you could e.g. create a bevel or worm gear.*| + +|[![Rack and pinion constraint](https://img.youtube.com/vi/e588KG-ZSxc/hqdefault.jpg)](https://www.youtube.com/watch?v=e588KG-ZSxc)| +|:-| +|*Demonstrates a rack and pinion constraint.*| + +|[![Pulley constraint](https://img.youtube.com/vi/9P8OaahtU-4/hqdefault.jpg)](https://www.youtube.com/watch?v=9P8OaahtU-4)| +|:-| +|*Shows two boxes connected through a pulley constraint. In this case the constraint is configured as a block and tackle with and advantage of 2: the right block moves 2x as slow as the left block.*| + +### General + +This category contains general simulation tests. It demonstrates things like friction, restitution, damping, modifying gravity and continous collision detection. Some highlights: + +|[![Stable Box Stacking](https://img.youtube.com/vi/fTtjBLYBxco/hqdefault.jpg)](https://www.youtube.com/watch?v=fTtjBLYBxco)| +|:-| +|*A YouTube video showing stability of a pile of boxes.*| + +|[![Active Edge Detection](https://img.youtube.com/vi/EanFxlkZgcA/hqdefault.jpg)](https://www.youtube.com/watch?v=EanFxlkZgcA)| +|:-| +|*Demonstrates objects sliding along a polygon mesh. Internal mesh edges are ignored and do not cause objects to bounce off.*| + +|[![Funnel Test](https://img.youtube.com/vi/Y-UgylH992A/hqdefault.jpg)](https://www.youtube.com/watch?v=Y-UgylH992A)| +|:-| +|*1000 random shapes in a funnel.*| + +|[![Multithreaded Island Simulation](https://img.youtube.com/vi/_Lv5xlWtCpM/hqdefault.jpg)](https://www.youtube.com/watch?v=_Lv5xlWtCpM)| +|:-| +|*We will automatically split up the simulation in islands of non-interacting bodies and distribute the work across multiple threads. Each island has its own color.*| + +|[![Single vs Double Precision](https://img.youtube.com/vi/KGnlYSW3550/hqdefault.jpg)](https://www.youtube.com/watch?v=KGnlYSW3550)| +|:-| +|*Shows the difference between compiling Jolt Physics in single precision and double precision (define JPH_DOUBLE_PRECISION).*| + +|[![Conveyor belt](https://img.youtube.com/vi/p_H6egZzbZE/hqdefault.jpg)](https://www.youtube.com/watch?v=p_H6egZzbZE)| +|:-| +|*A demo of setting the surface velocity of a body to create a conveyor belt. The boxes have decreasing friction from front to back (last one has zero friction so slowly slides down the ramp).*| + +### Shapes & Scaled Shapes + +These categories show off all of the supported shapes and how they can be scaled at run-time. + +|[![Shape Scaling](https://img.youtube.com/vi/u9cPBGUFurc/hqdefault.jpg)](https://www.youtube.com/watch?v=u9cPBGUFurc)| +|:-| +|*A height field shape using various scales in Jolt Physics: Uniform, Non uniform, Mirrored, Inside out*| diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/SwingTwistConstraint.png b/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/SwingTwistConstraint.png new file mode 100644 index 0000000000000000000000000000000000000000..10c16bc2064e15dd39a52a34fb2e32f3a4fff98c GIT binary patch literal 92708 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�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 zQENode, +# Edge and Graph Attributes specification You need to make sure dot is able +# to find the font, which can be done by putting it in a standard location or by +# setting the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the +# directory containing the font. Default graphviz fontsize is 14. +# The default value is: fontname=Helvetica,fontsize=10. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_COMMON_ATTR = "fontname=Helvetica,fontsize=10" + +# DOT_EDGE_ATTR is concatenated with DOT_COMMON_ATTR. For elegant style you can +# add 'arrowhead=open, arrowtail=open, arrowsize=0.5'. Complete documentation about +# arrows shapes. +# The default value is: labelfontname=Helvetica,labelfontsize=10. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_EDGE_ATTR = "labelfontname=Helvetica,labelfontsize=10" + +# DOT_NODE_ATTR is concatenated with DOT_COMMON_ATTR. For view without boxes +# around nodes set 'shape=plain' or 'shape=plaintext' Shapes specification +# The default value is: shape=box,height=0.2,width=0.4. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_NODE_ATTR = "shape=box,height=0.2,width=0.4" + +# You can set the path where dot can find font specified with fontname in +# DOT_COMMON_ATTR and others dot attributes. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_FONTPATH = + +# If the CLASS_GRAPH tag is set to YES (or GRAPH) then doxygen will generate a +# graph for each documented class showing the direct and indirect inheritance +# relations. In case HAVE_DOT is set as well dot will be used to draw the graph, +# otherwise the built-in generator will be used. If the CLASS_GRAPH tag is set +# to TEXT the direct and indirect inheritance relations will be shown as texts / +# links. +# Possible values are: NO, YES, TEXT and GRAPH. +# The default value is: YES. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a +# graph for each documented class showing the direct and indirect implementation +# dependencies (inheritance, containment, and class references variables) of the +# class with other documented classes. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for +# groups, showing the direct groups dependencies. See also the chapter Grouping +# in the manual. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +UML_LOOK = NO + +# If the UML_LOOK tag is enabled, the fields and methods are shown inside the +# class node. If there are many fields or methods and many nodes the graph may +# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the +# number of items for each type to make the size more manageable. Set this to 0 +# for no limit. Note that the threshold may be exceeded by 50% before the limit +# is enforced. So when you set the threshold to 10, up to 15 fields may appear, +# but if the number exceeds 15, the total amount of fields shown is limited to +# 10. +# Minimum value: 0, maximum value: 100, default value: 10. +# This tag requires that the tag UML_LOOK is set to YES. + +UML_LIMIT_NUM_FIELDS = 10 + +# If the DOT_UML_DETAILS tag is set to NO, doxygen will show attributes and +# methods without types and arguments in the UML graphs. If the DOT_UML_DETAILS +# tag is set to YES, doxygen will add type and arguments for attributes and +# methods in the UML graphs. If the DOT_UML_DETAILS tag is set to NONE, doxygen +# will not generate fields with class member information in the UML graphs. The +# class diagrams will look similar to the default class diagrams but using UML +# notation for the relationships. +# Possible values are: NO, YES and NONE. +# The default value is: NO. +# This tag requires that the tag UML_LOOK is set to YES. + +DOT_UML_DETAILS = NO + +# The DOT_WRAP_THRESHOLD tag can be used to set the maximum number of characters +# to display on a single line. If the actual line length exceeds this threshold +# significantly it will wrapped across multiple lines. Some heuristics are apply +# to avoid ugly line breaks. +# Minimum value: 0, maximum value: 1000, default value: 17. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_WRAP_THRESHOLD = 17 + +# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and +# collaboration graphs will show the relations between templates and their +# instances. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +TEMPLATE_RELATIONS = NO + +# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to +# YES then doxygen will generate a graph for each documented file showing the +# direct and indirect include dependencies of the file with other documented +# files. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +INCLUDE_GRAPH = YES + +# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are +# set to YES then doxygen will generate a graph for each documented file showing +# the direct and indirect include dependencies of the file with other documented +# files. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH tag is set to YES then doxygen will generate a call +# dependency graph for every global function or class method. +# +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable call graphs for selected +# functions only using the \callgraph command. Disabling a call graph can be +# accomplished by means of the command \hidecallgraph. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller +# dependency graph for every global function or class method. +# +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable caller graphs for selected +# functions only using the \callergraph command. Disabling a caller graph can be +# accomplished by means of the command \hidecallergraph. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical +# hierarchy of all classes instead of a textual one. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the +# dependencies a directory has on other directories in a graphical way. The +# dependency relations are determined by the #include relations between the +# files in the directories. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +DIRECTORY_GRAPH = YES + +# The DIR_GRAPH_MAX_DEPTH tag can be used to limit the maximum number of levels +# of child directories generated in directory dependency graphs by dot. +# Minimum value: 1, maximum value: 25, default value: 1. +# This tag requires that the tag DIRECTORY_GRAPH is set to YES. + +DIR_GRAPH_MAX_DEPTH = 1 + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. For an explanation of the image formats see the section +# output formats in the documentation of the dot tool (Graphviz (see: +# http://www.graphviz.org/)). +# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order +# to make the SVG files visible in IE 9+ (other browsers do not have this +# requirement). +# Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo, +# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and +# png:gdiplus:gdiplus. +# The default value is: png. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_IMAGE_FORMAT = png + +# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to +# enable generation of interactive SVG images that allow zooming and panning. +# +# Note that this requires a modern browser other than Internet Explorer. Tested +# and working are Firefox, Chrome, Safari, and Opera. +# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make +# the SVG files visible. Older versions of IE do not have SVG support. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +INTERACTIVE_SVG = NO + +# The DOT_PATH tag can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the \dotfile +# command). +# This tag requires that the tag HAVE_DOT is set to YES. + +DOTFILE_DIRS = + +# The MSCFILE_DIRS tag can be used to specify one or more directories that +# contain msc files that are included in the documentation (see the \mscfile +# command). + +MSCFILE_DIRS = + +# The DIAFILE_DIRS tag can be used to specify one or more directories that +# contain dia files that are included in the documentation (see the \diafile +# command). + +DIAFILE_DIRS = + +# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the +# path where java can find the plantuml.jar file or to the filename of jar file +# to be used. If left blank, it is assumed PlantUML is not used or called during +# a preprocessing step. Doxygen will generate a warning when it encounters a +# \startuml command in this case and will not generate output for the diagram. + +PLANTUML_JAR_PATH = + +# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a +# configuration file for plantuml. + +PLANTUML_CFG_FILE = + +# When using plantuml, the specified paths are searched for files specified by +# the !include statement in a plantuml block. + +PLANTUML_INCLUDE_PATH = + +# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes +# that will be shown in the graph. If the number of nodes in a graph becomes +# larger than this value, doxygen will truncate the graph, which is visualized +# by representing a node as a red box. Note that doxygen if the number of direct +# children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that +# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. +# Minimum value: 0, maximum value: 10000, default value: 50. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs +# generated by dot. A depth value of 3 means that only nodes reachable from the +# root by following a path via at most 3 edges will be shown. Nodes that lay +# further from the root node will be omitted. Note that setting this option to 1 +# or 2 may greatly reduce the computation time needed for large code bases. Also +# note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. +# Minimum value: 0, maximum value: 1000, default value: 0. +# This tag requires that the tag HAVE_DOT is set to YES. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) support +# this, this feature is disabled by default. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page +# explaining the meaning of the various boxes and arrows in the dot generated +# graphs. +# Note: This tag requires that UML_LOOK isn't set, i.e. the doxygen internal +# graphical representation for inheritance and collaboration diagrams is used. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate +# files that are used to generate the various graphs. +# +# Note: This setting is not only used for dot files but also for msc temporary +# files. +# The default value is: YES. + +DOT_CLEANUP = YES diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/HelloWorld/HelloWorld.cmake b/crates/joltc-sys-patched/JoltC/JoltPhysics/HelloWorld/HelloWorld.cmake new file mode 100644 index 00000000000..0580dbbe25f --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/HelloWorld/HelloWorld.cmake @@ -0,0 +1,11 @@ +# Root +set(HELLO_WORLD_ROOT ${PHYSICS_REPO_ROOT}/HelloWorld) + +# Source files +set(HELLO_WORLD_SRC_FILES + ${HELLO_WORLD_ROOT}/HelloWorld.cpp + ${HELLO_WORLD_ROOT}/HelloWorld.cmake +) + +# Group source files +source_group(TREE ${HELLO_WORLD_ROOT} FILES ${HELLO_WORLD_SRC_FILES}) diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/HelloWorld/HelloWorld.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/HelloWorld/HelloWorld.cpp new file mode 100644 index 00000000000..72b6b552913 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/HelloWorld/HelloWorld.cpp @@ -0,0 +1,364 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +// The Jolt headers don't include Jolt.h. Always include Jolt.h before including any other Jolt header. +// You can use Jolt.h in your precompiled header to speed up compilation. +#include + +// Jolt includes +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// STL includes +#include +#include +#include + +// Disable common warnings triggered by Jolt, you can use JPH_SUPPRESS_WARNING_PUSH / JPH_SUPPRESS_WARNING_POP to store and restore the warning state +JPH_SUPPRESS_WARNINGS + +// All Jolt symbols are in the JPH namespace +using namespace JPH; + +// If you want your code to compile using single or double precision write 0.0_r to get a Real value that compiles to double or float depending if JPH_DOUBLE_PRECISION is set or not. +using namespace JPH::literals; + +// We're also using STL classes in this example +using namespace std; + +// Callback for traces, connect this to your own trace function if you have one +static void TraceImpl(const char *inFMT, ...) +{ + // Format the message + va_list list; + va_start(list, inFMT); + char buffer[1024]; + vsnprintf(buffer, sizeof(buffer), inFMT, list); + va_end(list); + + // Print to the TTY + cout << buffer << endl; +} + +#ifdef JPH_ENABLE_ASSERTS + +// Callback for asserts, connect this to your own assert handler if you have one +static bool AssertFailedImpl(const char *inExpression, const char *inMessage, const char *inFile, uint inLine) +{ + // Print to the TTY + cout << inFile << ":" << inLine << ": (" << inExpression << ") " << (inMessage != nullptr? inMessage : "") << endl; + + // Breakpoint + return true; +}; + +#endif // JPH_ENABLE_ASSERTS + +// Layer that objects can be in, determines which other objects it can collide with +// Typically you at least want to have 1 layer for moving bodies and 1 layer for static bodies, but you can have more +// layers if you want. E.g. you could have a layer for high detail collision (which is not used by the physics simulation +// but only if you do collision testing). +namespace Layers +{ + static constexpr ObjectLayer NON_MOVING = 0; + static constexpr ObjectLayer MOVING = 1; + static constexpr ObjectLayer NUM_LAYERS = 2; +}; + +/// Class that determines if two object layers can collide +class ObjectLayerPairFilterImpl : public ObjectLayerPairFilter +{ +public: + virtual bool ShouldCollide(ObjectLayer inObject1, ObjectLayer inObject2) const override + { + switch (inObject1) + { + case Layers::NON_MOVING: + return inObject2 == Layers::MOVING; // Non moving only collides with moving + case Layers::MOVING: + return true; // Moving collides with everything + default: + JPH_ASSERT(false); + return false; + } + } +}; + +// Each broadphase layer results in a separate bounding volume tree in the broad phase. You at least want to have +// a layer for non-moving and moving objects to avoid having to update a tree full of static objects every frame. +// You can have a 1-on-1 mapping between object layers and broadphase layers (like in this case) but if you have +// many object layers you'll be creating many broad phase trees, which is not efficient. If you want to fine tune +// your broadphase layers define JPH_TRACK_BROADPHASE_STATS and look at the stats reported on the TTY. +namespace BroadPhaseLayers +{ + static constexpr BroadPhaseLayer NON_MOVING(0); + static constexpr BroadPhaseLayer MOVING(1); + static constexpr uint NUM_LAYERS(2); +}; + +// BroadPhaseLayerInterface implementation +// This defines a mapping between object and broadphase layers. +class BPLayerInterfaceImpl final : public BroadPhaseLayerInterface +{ +public: + BPLayerInterfaceImpl() + { + // Create a mapping table from object to broad phase layer + mObjectToBroadPhase[Layers::NON_MOVING] = BroadPhaseLayers::NON_MOVING; + mObjectToBroadPhase[Layers::MOVING] = BroadPhaseLayers::MOVING; + } + + virtual uint GetNumBroadPhaseLayers() const override + { + return BroadPhaseLayers::NUM_LAYERS; + } + + virtual BroadPhaseLayer GetBroadPhaseLayer(ObjectLayer inLayer) const override + { + JPH_ASSERT(inLayer < Layers::NUM_LAYERS); + return mObjectToBroadPhase[inLayer]; + } + +#if defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED) + virtual const char * GetBroadPhaseLayerName(BroadPhaseLayer inLayer) const override + { + switch ((BroadPhaseLayer::Type)inLayer) + { + case (BroadPhaseLayer::Type)BroadPhaseLayers::NON_MOVING: return "NON_MOVING"; + case (BroadPhaseLayer::Type)BroadPhaseLayers::MOVING: return "MOVING"; + default: JPH_ASSERT(false); return "INVALID"; + } + } +#endif // JPH_EXTERNAL_PROFILE || JPH_PROFILE_ENABLED + +private: + BroadPhaseLayer mObjectToBroadPhase[Layers::NUM_LAYERS]; +}; + +/// Class that determines if an object layer can collide with a broadphase layer +class ObjectVsBroadPhaseLayerFilterImpl : public ObjectVsBroadPhaseLayerFilter +{ +public: + virtual bool ShouldCollide(ObjectLayer inLayer1, BroadPhaseLayer inLayer2) const override + { + switch (inLayer1) + { + case Layers::NON_MOVING: + return inLayer2 == BroadPhaseLayers::MOVING; + case Layers::MOVING: + return true; + default: + JPH_ASSERT(false); + return false; + } + } +}; + +// An example contact listener +class MyContactListener : public ContactListener +{ +public: + // See: ContactListener + virtual ValidateResult OnContactValidate(const Body &inBody1, const Body &inBody2, RVec3Arg inBaseOffset, const CollideShapeResult &inCollisionResult) override + { + cout << "Contact validate callback" << endl; + + // Allows you to ignore a contact before it is created (using layers to not make objects collide is cheaper!) + return ValidateResult::AcceptAllContactsForThisBodyPair; + } + + virtual void OnContactAdded(const Body &inBody1, const Body &inBody2, const ContactManifold &inManifold, ContactSettings &ioSettings) override + { + cout << "A contact was added" << endl; + } + + virtual void OnContactPersisted(const Body &inBody1, const Body &inBody2, const ContactManifold &inManifold, ContactSettings &ioSettings) override + { + cout << "A contact was persisted" << endl; + } + + virtual void OnContactRemoved(const SubShapeIDPair &inSubShapePair) override + { + cout << "A contact was removed" << endl; + } +}; + +// An example activation listener +class MyBodyActivationListener : public BodyActivationListener +{ +public: + virtual void OnBodyActivated(const BodyID &inBodyID, uint64 inBodyUserData) override + { + cout << "A body got activated" << endl; + } + + virtual void OnBodyDeactivated(const BodyID &inBodyID, uint64 inBodyUserData) override + { + cout << "A body went to sleep" << endl; + } +}; + +// Program entry point +int main(int argc, char** argv) +{ + // Register allocation hook. In this example we'll just let Jolt use malloc / free but you can override these if you want (see Memory.h). + // This needs to be done before any other Jolt function is called. + RegisterDefaultAllocator(); + + // Install trace and assert callbacks + Trace = TraceImpl; + JPH_IF_ENABLE_ASSERTS(AssertFailed = AssertFailedImpl;) + + // Create a factory, this class is responsible for creating instances of classes based on their name or hash and is mainly used for deserialization of saved data. + // It is not directly used in this example but still required. + Factory::sInstance = new Factory(); + + // Register all physics types with the factory and install their collision handlers with the CollisionDispatch class. + // If you have your own custom shape types you probably need to register their handlers with the CollisionDispatch before calling this function. + // If you implement your own default material (PhysicsMaterial::sDefault) make sure to initialize it before this function or else this function will create one for you. + RegisterTypes(); + + // We need a temp allocator for temporary allocations during the physics update. We're + // pre-allocating 10 MB to avoid having to do allocations during the physics update. + // B.t.w. 10 MB is way too much for this example but it is a typical value you can use. + // If you don't want to pre-allocate you can also use TempAllocatorMalloc to fall back to + // malloc / free. + TempAllocatorImpl temp_allocator(10 * 1024 * 1024); + + // We need a job system that will execute physics jobs on multiple threads. Typically + // you would implement the JobSystem interface yourself and let Jolt Physics run on top + // of your own job scheduler. JobSystemThreadPool is an example implementation. + JobSystemThreadPool job_system(cMaxPhysicsJobs, cMaxPhysicsBarriers, thread::hardware_concurrency() - 1); + + // This is the max amount of rigid bodies that you can add to the physics system. If you try to add more you'll get an error. + // Note: This value is low because this is a simple test. For a real project use something in the order of 65536. + const uint cMaxBodies = 1024; + + // This determines how many mutexes to allocate to protect rigid bodies from concurrent access. Set it to 0 for the default settings. + const uint cNumBodyMutexes = 0; + + // This is the max amount of body pairs that can be queued at any time (the broad phase will detect overlapping + // body pairs based on their bounding boxes and will insert them into a queue for the narrowphase). If you make this buffer + // too small the queue will fill up and the broad phase jobs will start to do narrow phase work. This is slightly less efficient. + // Note: This value is low because this is a simple test. For a real project use something in the order of 65536. + const uint cMaxBodyPairs = 1024; + + // This is the maximum size of the contact constraint buffer. If more contacts (collisions between bodies) are detected than this + // number then these contacts will be ignored and bodies will start interpenetrating / fall through the world. + // Note: This value is low because this is a simple test. For a real project use something in the order of 10240. + const uint cMaxContactConstraints = 1024; + + // Create mapping table from object layer to broadphase layer + // Note: As this is an interface, PhysicsSystem will take a reference to this so this instance needs to stay alive! + BPLayerInterfaceImpl broad_phase_layer_interface; + + // Create class that filters object vs broadphase layers + // Note: As this is an interface, PhysicsSystem will take a reference to this so this instance needs to stay alive! + ObjectVsBroadPhaseLayerFilterImpl object_vs_broadphase_layer_filter; + + // Create class that filters object vs object layers + // Note: As this is an interface, PhysicsSystem will take a reference to this so this instance needs to stay alive! + ObjectLayerPairFilterImpl object_vs_object_layer_filter; + + // Now we can create the actual physics system. + PhysicsSystem physics_system; + physics_system.Init(cMaxBodies, cNumBodyMutexes, cMaxBodyPairs, cMaxContactConstraints, broad_phase_layer_interface, object_vs_broadphase_layer_filter, object_vs_object_layer_filter); + + // A body activation listener gets notified when bodies activate and go to sleep + // Note that this is called from a job so whatever you do here needs to be thread safe. + // Registering one is entirely optional. + MyBodyActivationListener body_activation_listener; + physics_system.SetBodyActivationListener(&body_activation_listener); + + // A contact listener gets notified when bodies (are about to) collide, and when they separate again. + // Note that this is called from a job so whatever you do here needs to be thread safe. + // Registering one is entirely optional. + MyContactListener contact_listener; + physics_system.SetContactListener(&contact_listener); + + // The main way to interact with the bodies in the physics system is through the body interface. There is a locking and a non-locking + // variant of this. We're going to use the locking version (even though we're not planning to access bodies from multiple threads) + BodyInterface &body_interface = physics_system.GetBodyInterface(); + + // Next we can create a rigid body to serve as the floor, we make a large box + // Create the settings for the collision volume (the shape). + // Note that for simple shapes (like boxes) you can also directly construct a BoxShape. + BoxShapeSettings floor_shape_settings(Vec3(100.0f, 1.0f, 100.0f)); + + // Create the shape + ShapeSettings::ShapeResult floor_shape_result = floor_shape_settings.Create(); + ShapeRefC floor_shape = floor_shape_result.Get(); // We don't expect an error here, but you can check floor_shape_result for HasError() / GetError() + + // Create the settings for the body itself. Note that here you can also set other properties like the restitution / friction. + BodyCreationSettings floor_settings(floor_shape, RVec3(0.0_r, -1.0_r, 0.0_r), Quat::sIdentity(), EMotionType::Static, Layers::NON_MOVING); + + // Create the actual rigid body + Body *floor = body_interface.CreateBody(floor_settings); // Note that if we run out of bodies this can return nullptr + + // Add it to the world + body_interface.AddBody(floor->GetID(), EActivation::DontActivate); + + // Now create a dynamic body to bounce on the floor + // Note that this uses the shorthand version of creating and adding a body to the world + BodyCreationSettings sphere_settings(new SphereShape(0.5f), RVec3(0.0_r, 2.0_r, 0.0_r), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING); + BodyID sphere_id = body_interface.CreateAndAddBody(sphere_settings, EActivation::Activate); + + // Now you can interact with the dynamic body, in this case we're going to give it a velocity. + // (note that if we had used CreateBody then we could have set the velocity straight on the body before adding it to the physics system) + body_interface.SetLinearVelocity(sphere_id, Vec3(0.0f, -5.0f, 0.0f)); + + // We simulate the physics world in discrete time steps. 60 Hz is a good rate to update the physics system. + const float cDeltaTime = 1.0f / 60.0f; + + // Optional step: Before starting the physics simulation you can optimize the broad phase. This improves collision detection performance (it's pointless here because we only have 2 bodies). + // You should definitely not call this every frame or when e.g. streaming in a new level section as it is an expensive operation. + // Instead insert all new objects in batches instead of 1 at a time to keep the broad phase efficient. + physics_system.OptimizeBroadPhase(); + + // Now we're ready to simulate the body, keep simulating until it goes to sleep + uint step = 0; + while (body_interface.IsActive(sphere_id)) + { + // Next step + ++step; + + // Output current position and velocity of the sphere + RVec3 position = body_interface.GetCenterOfMassPosition(sphere_id); + Vec3 velocity = body_interface.GetLinearVelocity(sphere_id); + cout << "Step " << step << ": Position = (" << position.GetX() << ", " << position.GetY() << ", " << position.GetZ() << "), Velocity = (" << velocity.GetX() << ", " << velocity.GetY() << ", " << velocity.GetZ() << ")" << endl; + + // If you take larger steps than 1 / 60th of a second you need to do multiple collision steps in order to keep the simulation stable. Do 1 collision step per 1 / 60th of a second (round up). + const int cCollisionSteps = 1; + + // Step the world + physics_system.Update(cDeltaTime, cCollisionSteps, &temp_allocator, &job_system); + } + + // Remove the sphere from the physics system. Note that the sphere itself keeps all of its state and can be re-added at any time. + body_interface.RemoveBody(sphere_id); + + // Destroy the sphere. After this the sphere ID is no longer valid. + body_interface.DestroyBody(sphere_id); + + // Remove and destroy the floor + body_interface.RemoveBody(floor->GetID()); + body_interface.DestroyBody(floor->GetID()); + + // Unregisters all types with the factory and cleans up the default material + UnregisterTypes(); + + // Destroy the factory + delete Factory::sInstance; + Factory::sInstance = nullptr; + + return 0; +} diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/AABBTree/AABBTreeBuilder.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/AABBTree/AABBTreeBuilder.cpp new file mode 100644 index 00000000000..7e4f0622aa2 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/AABBTree/AABBTreeBuilder.cpp @@ -0,0 +1,225 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include + +JPH_NAMESPACE_BEGIN + +AABBTreeBuilder::Node::Node() +{ + mChild[0] = nullptr; + mChild[1] = nullptr; +} + +AABBTreeBuilder::Node::~Node() +{ + delete mChild[0]; + delete mChild[1]; +} + +uint AABBTreeBuilder::Node::GetMinDepth() const +{ + if (HasChildren()) + { + uint left = mChild[0]->GetMinDepth(); + uint right = mChild[1]->GetMinDepth(); + return min(left, right) + 1; + } + else + return 1; +} + +uint AABBTreeBuilder::Node::GetMaxDepth() const +{ + if (HasChildren()) + { + uint left = mChild[0]->GetMaxDepth(); + uint right = mChild[1]->GetMaxDepth(); + return max(left, right) + 1; + } + else + return 1; +} + +uint AABBTreeBuilder::Node::GetNodeCount() const +{ + if (HasChildren()) + return mChild[0]->GetNodeCount() + mChild[1]->GetNodeCount() + 1; + else + return 1; +} + +uint AABBTreeBuilder::Node::GetLeafNodeCount() const +{ + if (HasChildren()) + return mChild[0]->GetLeafNodeCount() + mChild[1]->GetLeafNodeCount(); + else + return 1; +} + +uint AABBTreeBuilder::Node::GetTriangleCountInTree() const +{ + if (HasChildren()) + return mChild[0]->GetTriangleCountInTree() + mChild[1]->GetTriangleCountInTree(); + else + return GetTriangleCount(); +} + +void AABBTreeBuilder::Node::GetTriangleCountPerNode(float &outAverage, uint &outMin, uint &outMax) const +{ + outMin = INT_MAX; + outMax = 0; + outAverage = 0; + uint avg_divisor = 0; + GetTriangleCountPerNodeInternal(outAverage, avg_divisor, outMin, outMax); + if (avg_divisor > 0) + outAverage /= avg_divisor; +} + +float AABBTreeBuilder::Node::CalculateSAHCost(float inCostTraversal, float inCostLeaf) const +{ + float surface_area = mBounds.GetSurfaceArea(); + return surface_area > 0.0f? CalculateSAHCostInternal(inCostTraversal / surface_area, inCostLeaf / surface_area) : 0.0f; +} + +void AABBTreeBuilder::Node::GetNChildren(uint inN, Array &outChildren) const +{ + JPH_ASSERT(outChildren.empty()); + + // Check if there is anything to expand + if (!HasChildren()) + return; + + // Start with the children of this node + outChildren.push_back(mChild[0]); + outChildren.push_back(mChild[1]); + + size_t next = 0; + bool all_triangles = true; + while (outChildren.size() < inN) + { + // If we have looped over all nodes, start over with the first node again + if (next >= outChildren.size()) + { + // If there only triangle nodes left, we have to terminate + if (all_triangles) + return; + next = 0; + all_triangles = true; + } + + // Try to expand this node into its two children + const Node *to_expand = outChildren[next]; + if (to_expand->HasChildren()) + { + outChildren.erase(outChildren.begin() + next); + outChildren.push_back(to_expand->mChild[0]); + outChildren.push_back(to_expand->mChild[1]); + all_triangles = false; + } + else + { + ++next; + } + } +} + +float AABBTreeBuilder::Node::CalculateSAHCostInternal(float inCostTraversalDivSurfaceArea, float inCostLeafDivSurfaceArea) const +{ + if (HasChildren()) + return inCostTraversalDivSurfaceArea * mBounds.GetSurfaceArea() + + mChild[0]->CalculateSAHCostInternal(inCostTraversalDivSurfaceArea, inCostLeafDivSurfaceArea) + + mChild[1]->CalculateSAHCostInternal(inCostTraversalDivSurfaceArea, inCostLeafDivSurfaceArea); + else + return inCostLeafDivSurfaceArea * mBounds.GetSurfaceArea() * GetTriangleCount(); +} + +void AABBTreeBuilder::Node::GetTriangleCountPerNodeInternal(float &outAverage, uint &outAverageDivisor, uint &outMin, uint &outMax) const +{ + if (HasChildren()) + { + mChild[0]->GetTriangleCountPerNodeInternal(outAverage, outAverageDivisor, outMin, outMax); + mChild[1]->GetTriangleCountPerNodeInternal(outAverage, outAverageDivisor, outMin, outMax); + } + else + { + outAverage += GetTriangleCount(); + outAverageDivisor++; + outMin = min(outMin, GetTriangleCount()); + outMax = max(outMax, GetTriangleCount()); + } +} + +AABBTreeBuilder::AABBTreeBuilder(TriangleSplitter &inSplitter, uint inMaxTrianglesPerLeaf) : + mTriangleSplitter(inSplitter), + mMaxTrianglesPerLeaf(inMaxTrianglesPerLeaf) +{ +} + +AABBTreeBuilder::Node *AABBTreeBuilder::Build(AABBTreeBuilderStats &outStats) +{ + TriangleSplitter::Range initial = mTriangleSplitter.GetInitialRange(); + Node *root = BuildInternal(initial); + + float avg_triangles_per_leaf; + uint min_triangles_per_leaf, max_triangles_per_leaf; + root->GetTriangleCountPerNode(avg_triangles_per_leaf, min_triangles_per_leaf, max_triangles_per_leaf); + + mTriangleSplitter.GetStats(outStats.mSplitterStats); + + outStats.mSAHCost = root->CalculateSAHCost(1.0f, 1.0f); + outStats.mMinDepth = root->GetMinDepth(); + outStats.mMaxDepth = root->GetMaxDepth(); + outStats.mNodeCount = root->GetNodeCount(); + outStats.mLeafNodeCount = root->GetLeafNodeCount(); + outStats.mMaxTrianglesPerLeaf = mMaxTrianglesPerLeaf; + outStats.mTreeMinTrianglesPerLeaf = min_triangles_per_leaf; + outStats.mTreeMaxTrianglesPerLeaf = max_triangles_per_leaf; + outStats.mTreeAvgTrianglesPerLeaf = avg_triangles_per_leaf; + + return root; +} + +AABBTreeBuilder::Node *AABBTreeBuilder::BuildInternal(const TriangleSplitter::Range &inTriangles) +{ + // Check if there are too many triangles left + if (inTriangles.Count() > mMaxTrianglesPerLeaf) + { + // Split triangles in two batches + TriangleSplitter::Range left, right; + if (!mTriangleSplitter.Split(inTriangles, left, right)) + { + JPH_IF_DEBUG(Trace("AABBTreeBuilder: Doing random split for %d triangles (max per node: %u)!", (int)inTriangles.Count(), mMaxTrianglesPerLeaf);) + int half = inTriangles.Count() / 2; + JPH_ASSERT(half > 0); + left = TriangleSplitter::Range(inTriangles.mBegin, inTriangles.mBegin + half); + right = TriangleSplitter::Range(inTriangles.mBegin + half, inTriangles.mEnd); + } + + // Recursively build + Node *node = new Node(); + node->mChild[0] = BuildInternal(left); + node->mChild[1] = BuildInternal(right); + node->mBounds = node->mChild[0]->mBounds; + node->mBounds.Encapsulate(node->mChild[1]->mBounds); + return node; + } + + // Create leaf node + Node *node = new Node(); + node->mTriangles.reserve(inTriangles.Count()); + for (uint i = inTriangles.mBegin; i < inTriangles.mEnd; ++i) + { + const IndexedTriangle &t = mTriangleSplitter.GetTriangle(i); + const VertexList &v = mTriangleSplitter.GetVertices(); + node->mTriangles.push_back(t); + node->mBounds.Encapsulate(v, t); + } + + return node; +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/AABBTree/AABBTreeBuilder.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/AABBTree/AABBTreeBuilder.h new file mode 100644 index 00000000000..dacae5e07ca --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/AABBTree/AABBTreeBuilder.h @@ -0,0 +1,110 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +struct AABBTreeBuilderStats +{ + ///@name Splitter stats + TriangleSplitter::Stats mSplitterStats; ///< Stats returned by the triangle splitter algorithm + + ///@name Tree structure + float mSAHCost = 0.0f; ///< Surface Area Heuristic cost of this tree + int mMinDepth = 0; ///< Minimal depth of tree (number of nodes) + int mMaxDepth = 0; ///< Maximum depth of tree (number of nodes) + int mNodeCount = 0; ///< Number of nodes in the tree + int mLeafNodeCount = 0; ///< Number of leaf nodes (that contain triangles) + + ///@name Configured stats + int mMaxTrianglesPerLeaf = 0; ///< Configured max triangles per leaf + + ///@name Actual stats + int mTreeMinTrianglesPerLeaf = 0; ///< Minimal amount of triangles in a leaf + int mTreeMaxTrianglesPerLeaf = 0; ///< Maximal amount of triangles in a leaf + float mTreeAvgTrianglesPerLeaf = 0.0f; ///< Average amount of triangles in leaf nodes +}; + +/// Helper class to build an AABB tree +class JPH_EXPORT AABBTreeBuilder +{ +public: + /// A node in the tree, contains the AABox for the tree and any child nodes or triangles + class Node : public NonCopyable + { + public: + JPH_OVERRIDE_NEW_DELETE + + /// Constructor + Node(); + ~Node(); + + /// Get number of triangles in this node + inline uint GetTriangleCount() const { return uint(mTriangles.size()); } + + /// Check if this node has any children + inline bool HasChildren() const { return mChild[0] != nullptr || mChild[1] != nullptr; } + + /// Min depth of tree + uint GetMinDepth() const; + + /// Max depth of tree + uint GetMaxDepth() const; + + /// Number of nodes in tree + uint GetNodeCount() const; + + /// Number of leaf nodes in tree + uint GetLeafNodeCount() const; + + /// Get triangle count in tree + uint GetTriangleCountInTree() const; + + /// Calculate min and max triangles per node + void GetTriangleCountPerNode(float &outAverage, uint &outMin, uint &outMax) const; + + /// Calculate the total cost of the tree using the surface area heuristic + float CalculateSAHCost(float inCostTraversal, float inCostLeaf) const; + + /// Recursively get children (breadth first) to get in total inN children (or less if there are no more) + void GetNChildren(uint inN, Array &outChildren) const; + + /// Bounding box + AABox mBounds; + + /// Triangles (if no child nodes) + IndexedTriangleList mTriangles; + + /// Child nodes (if no triangles) + Node * mChild[2]; + + private: + friend class AABBTreeBuilder; + + /// Recursive helper function to calculate cost of the tree + float CalculateSAHCostInternal(float inCostTraversalDivSurfaceArea, float inCostLeafDivSurfaceArea) const; + + /// Recursive helper function to calculate min and max triangles per node + void GetTriangleCountPerNodeInternal(float &outAverage, uint &outAverageDivisor, uint &outMin, uint &outMax) const; + }; + + /// Constructor + AABBTreeBuilder(TriangleSplitter &inSplitter, uint inMaxTrianglesPerLeaf = 16); + + /// Recursively build tree, returns the root node of the tree + Node * Build(AABBTreeBuilderStats &outStats); + +private: + Node * BuildInternal(const TriangleSplitter::Range &inTriangles); + + TriangleSplitter & mTriangleSplitter; + const uint mMaxTrianglesPerLeaf; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/AABBTree/AABBTreeToBuffer.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/AABBTree/AABBTreeToBuffer.h new file mode 100644 index 00000000000..1fa9bb7aec0 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/AABBTree/AABBTreeToBuffer.h @@ -0,0 +1,245 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include + +JPH_SUPPRESS_WARNINGS_STD_BEGIN +#include +JPH_SUPPRESS_WARNINGS_STD_END + +JPH_NAMESPACE_BEGIN + +template using Deque = std::deque>; + +/// Conversion algorithm that converts an AABB tree to an optimized binary buffer +template +class AABBTreeToBuffer +{ +public: + /// Header for the tree + using NodeHeader = typename NodeCodec::Header; + + /// Size in bytes of the header of the tree + static const int HeaderSize = NodeCodec::HeaderSize; + + /// Maximum number of children per node in the tree + static const int NumChildrenPerNode = NodeCodec::NumChildrenPerNode; + + /// Header for the triangles + using TriangleHeader = typename TriangleCodec::TriangleHeader; + + /// Size in bytes of the header for the triangles + static const int TriangleHeaderSize = TriangleCodec::TriangleHeaderSize; + + /// Convert AABB tree. Returns false if failed. + bool Convert(const VertexList &inVertices, const AABBTreeBuilder::Node *inRoot, const char *&outError) + { + const typename NodeCodec::EncodingContext node_ctx; + typename TriangleCodec::EncodingContext tri_ctx(inVertices); + + // Estimate the amount of memory required + uint tri_count = inRoot->GetTriangleCountInTree(); + uint node_count = inRoot->GetNodeCount(); + uint nodes_size = node_ctx.GetPessimisticMemoryEstimate(node_count); + uint total_size = HeaderSize + TriangleHeaderSize + nodes_size + tri_ctx.GetPessimisticMemoryEstimate(tri_count); + mTree.reserve(total_size); + + // Reset counters + mNodesSize = 0; + + // Add headers + NodeHeader *header = HeaderSize > 0? mTree.Allocate() : nullptr; + TriangleHeader *triangle_header = TriangleHeaderSize > 0? mTree.Allocate() : nullptr; + + struct NodeData + { + const AABBTreeBuilder::Node * mNode = nullptr; // Node that this entry belongs to + Vec3 mNodeBoundsMin; // Quantized node bounds + Vec3 mNodeBoundsMax; + uint mNodeStart = uint(-1); // Start of node in mTree + uint mTriangleStart = uint(-1); // Start of the triangle data in mTree + uint mNumChildren = 0; // Number of children + uint mChildNodeStart[NumChildrenPerNode]; // Start of the children of the node in mTree + uint mChildTrianglesStart[NumChildrenPerNode]; // Start of the triangle data in mTree + uint * mParentChildNodeStart = nullptr; // Where to store mNodeStart (to patch mChildNodeStart of my parent) + uint * mParentTrianglesStart = nullptr; // Where to store mTriangleStart (to patch mChildTrianglesStart of my parent) + }; + + Deque to_process; + Deque to_process_triangles; + Array node_list; + + node_list.reserve(node_count); // Needed to ensure that array is not reallocated, so we can keep pointers in the array + + NodeData root; + root.mNode = inRoot; + root.mNodeBoundsMin = inRoot->mBounds.mMin; + root.mNodeBoundsMax = inRoot->mBounds.mMax; + node_list.push_back(root); + to_process.push_back(&node_list.back()); + + // Child nodes out of loop so we don't constantly realloc it + Array child_nodes; + child_nodes.reserve(NumChildrenPerNode); + + for (;;) + { + while (!to_process.empty()) + { + // Get the next node to process + NodeData *node_data = to_process.back(); + to_process.pop_back(); + + // Due to quantization box could have become bigger, not smaller + JPH_ASSERT(AABox(node_data->mNodeBoundsMin, node_data->mNodeBoundsMax).Contains(node_data->mNode->mBounds), "AABBTreeToBuffer: Bounding box became smaller!"); + + // Collect the first NumChildrenPerNode sub-nodes in the tree + child_nodes.clear(); // Won't free the memory + node_data->mNode->GetNChildren(NumChildrenPerNode, child_nodes); + node_data->mNumChildren = (uint)child_nodes.size(); + + // Fill in default child bounds + Vec3 child_bounds_min[NumChildrenPerNode], child_bounds_max[NumChildrenPerNode]; + for (size_t i = 0; i < NumChildrenPerNode; ++i) + if (i < child_nodes.size()) + { + child_bounds_min[i] = child_nodes[i]->mBounds.mMin; + child_bounds_max[i] = child_nodes[i]->mBounds.mMax; + } + else + { + child_bounds_min[i] = Vec3::sZero(); + child_bounds_max[i] = Vec3::sZero(); + } + + // Start a new node + uint old_size = (uint)mTree.size(); + node_data->mNodeStart = node_ctx.NodeAllocate(node_data->mNode, node_data->mNodeBoundsMin, node_data->mNodeBoundsMax, child_nodes, child_bounds_min, child_bounds_max, mTree, outError); + if (node_data->mNodeStart == uint(-1)) + return false; + mNodesSize += (uint)mTree.size() - old_size; + + if (node_data->mNode->HasChildren()) + { + // Insert in reverse order so we process left child first when taking nodes from the back + for (int idx = int(child_nodes.size()) - 1; idx >= 0; --idx) + { + // Due to quantization box could have become bigger, not smaller + JPH_ASSERT(AABox(child_bounds_min[idx], child_bounds_max[idx]).Contains(child_nodes[idx]->mBounds), "AABBTreeToBuffer: Bounding box became smaller!"); + + // Add child to list of nodes to be processed + NodeData child; + child.mNode = child_nodes[idx]; + child.mNodeBoundsMin = child_bounds_min[idx]; + child.mNodeBoundsMax = child_bounds_max[idx]; + child.mParentChildNodeStart = &node_data->mChildNodeStart[idx]; + child.mParentTrianglesStart = &node_data->mChildTrianglesStart[idx]; + NodeData *old = &node_list[0]; + node_list.push_back(child); + if (old != &node_list[0]) + { + outError = "Internal Error: Array reallocated, memory corruption!"; + return false; + } + + // Store triangles in separate list so we process them last + if (node_list.back().mNode->HasChildren()) + to_process.push_back(&node_list.back()); + else + to_process_triangles.push_back(&node_list.back()); + } + } + else + { + // Add triangles + node_data->mTriangleStart = tri_ctx.Pack(node_data->mNode->mTriangles, mTree, outError); + if (node_data->mTriangleStart == uint(-1)) + return false; + } + + // Patch offset into parent + if (node_data->mParentChildNodeStart != nullptr) + { + *node_data->mParentChildNodeStart = node_data->mNodeStart; + *node_data->mParentTrianglesStart = node_data->mTriangleStart; + } + } + + // If we've got triangles to process, loop again with just the triangles + if (to_process_triangles.empty()) + break; + else + to_process.swap(to_process_triangles); + } + + // Finalize all nodes + for (NodeData &n : node_list) + if (!node_ctx.NodeFinalize(n.mNode, n.mNodeStart, n.mNumChildren, n.mChildNodeStart, n.mChildTrianglesStart, mTree, outError)) + return false; + + // Finalize the triangles + tri_ctx.Finalize(inVertices, triangle_header, mTree); + + // Validate that we reserved enough memory + if (nodes_size < mNodesSize) + { + outError = "Internal Error: Not enough memory reserved for nodes!"; + return false; + } + if (total_size < (uint)mTree.size()) + { + outError = "Internal Error: Not enough memory reserved for triangles!"; + return false; + } + + // Finalize the nodes + if (!node_ctx.Finalize(header, inRoot, node_list[0].mNodeStart, node_list[0].mTriangleStart, outError)) + return false; + + // Shrink the tree, this will invalidate the header and triangle_header variables + mTree.shrink_to_fit(); + + return true; + } + + /// Get resulting data + inline const ByteBuffer & GetBuffer() const + { + return mTree; + } + + /// Get resulting data + inline ByteBuffer & GetBuffer() + { + return mTree; + } + + /// Get header for tree + inline const NodeHeader * GetNodeHeader() const + { + return mTree.Get(0); + } + + /// Get header for triangles + inline const TriangleHeader * GetTriangleHeader() const + { + return mTree.Get(HeaderSize); + } + + /// Get root of resulting tree + inline const void * GetRoot() const + { + return mTree.Get(HeaderSize + TriangleHeaderSize); + } + +private: + ByteBuffer mTree; ///< Resulting tree structure + uint mNodesSize; ///< Size in bytes of the nodes in the buffer +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/AABBTree/NodeCodec/NodeCodecQuadTreeHalfFloat.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/AABBTree/NodeCodec/NodeCodecQuadTreeHalfFloat.h new file mode 100644 index 00000000000..e5376ea82dd --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/AABBTree/NodeCodec/NodeCodecQuadTreeHalfFloat.h @@ -0,0 +1,287 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +template +class NodeCodecQuadTreeHalfFloat +{ +public: + /// Number of child nodes of this node + static constexpr int NumChildrenPerNode = 4; + + /// Header for the tree + struct Header + { + Float3 mRootBoundsMin; + Float3 mRootBoundsMax; + uint32 mRootProperties; + }; + + /// Size of the header (an empty struct is always > 0 bytes so this needs a separate variable) + static constexpr int HeaderSize = sizeof(Header); + + /// Stack size to use during DecodingContext::sWalkTree + static constexpr int StackSize = 128; + + /// Node properties + enum : uint32 + { + TRIANGLE_COUNT_BITS = 4, + TRIANGLE_COUNT_SHIFT = 28, + TRIANGLE_COUNT_MASK = (1 << TRIANGLE_COUNT_BITS) - 1, + OFFSET_BITS = 28, + OFFSET_MASK = (1 << OFFSET_BITS) - 1, + OFFSET_NON_SIGNIFICANT_BITS = 2, + OFFSET_NON_SIGNIFICANT_MASK = (1 << OFFSET_NON_SIGNIFICANT_BITS) - 1, + }; + + /// Node structure + struct Node + { + HalfFloat mBoundsMinX[4]; ///< 4 child bounding boxes + HalfFloat mBoundsMinY[4]; + HalfFloat mBoundsMinZ[4]; + HalfFloat mBoundsMaxX[4]; + HalfFloat mBoundsMaxY[4]; + HalfFloat mBoundsMaxZ[4]; + uint32 mNodeProperties[4]; ///< 4 child node properties + }; + + static_assert(sizeof(Node) == 64, "Node should be 64 bytes"); + + /// This class encodes and compresses quad tree nodes + class EncodingContext + { + public: + /// Get an upper bound on the amount of bytes needed for a node tree with inNodeCount nodes + uint GetPessimisticMemoryEstimate(uint inNodeCount) const + { + return inNodeCount * (sizeof(Node) + Alignment - 1); + } + + /// Allocate a new node for inNode. + /// Algorithm can modify the order of ioChildren to indicate in which order children should be compressed + /// Algorithm can enlarge the bounding boxes of the children during compression and returns these in outChildBoundsMin, outChildBoundsMax + /// inNodeBoundsMin, inNodeBoundsMax is the bounding box if inNode possibly widened by compressing the parent node + /// Returns uint(-1) on error and reports the error in outError + uint NodeAllocate(const AABBTreeBuilder::Node *inNode, Vec3Arg inNodeBoundsMin, Vec3Arg inNodeBoundsMax, Array &ioChildren, Vec3 outChildBoundsMin[NumChildrenPerNode], Vec3 outChildBoundsMax[NumChildrenPerNode], ByteBuffer &ioBuffer, const char *&outError) const + { + // We don't emit nodes for leafs + if (!inNode->HasChildren()) + return (uint)ioBuffer.size(); + + // Align the buffer + ioBuffer.Align(Alignment); + uint node_start = (uint)ioBuffer.size(); + + // Fill in bounds + Node *node = ioBuffer.Allocate(); + + for (size_t i = 0; i < 4; ++i) + { + if (i < ioChildren.size()) + { + const AABBTreeBuilder::Node *this_node = ioChildren[i]; + + // Copy bounding box + node->mBoundsMinX[i] = HalfFloatConversion::FromFloat(this_node->mBounds.mMin.GetX()); + node->mBoundsMinY[i] = HalfFloatConversion::FromFloat(this_node->mBounds.mMin.GetY()); + node->mBoundsMinZ[i] = HalfFloatConversion::FromFloat(this_node->mBounds.mMin.GetZ()); + node->mBoundsMaxX[i] = HalfFloatConversion::FromFloat(this_node->mBounds.mMax.GetX()); + node->mBoundsMaxY[i] = HalfFloatConversion::FromFloat(this_node->mBounds.mMax.GetY()); + node->mBoundsMaxZ[i] = HalfFloatConversion::FromFloat(this_node->mBounds.mMax.GetZ()); + + // Store triangle count + node->mNodeProperties[i] = this_node->GetTriangleCount() << TRIANGLE_COUNT_SHIFT; + if (this_node->GetTriangleCount() >= TRIANGLE_COUNT_MASK) + { + outError = "NodeCodecQuadTreeHalfFloat: Too many triangles"; + return uint(-1); + } + } + else + { + // Make this an invalid triangle node + node->mNodeProperties[i] = uint32(TRIANGLE_COUNT_MASK) << TRIANGLE_COUNT_SHIFT; + + // Make bounding box invalid + node->mBoundsMinX[i] = HALF_FLT_MAX; + node->mBoundsMinY[i] = HALF_FLT_MAX; + node->mBoundsMinZ[i] = HALF_FLT_MAX; + node->mBoundsMaxX[i] = HALF_FLT_MAX; + node->mBoundsMaxY[i] = HALF_FLT_MAX; + node->mBoundsMaxZ[i] = HALF_FLT_MAX; + } + } + + // Since we don't keep track of the bounding box while descending the tree, we keep the root bounds at all levels for triangle compression + for (int i = 0; i < NumChildrenPerNode; ++i) + { + outChildBoundsMin[i] = inNodeBoundsMin; + outChildBoundsMax[i] = inNodeBoundsMax; + } + + return node_start; + } + + /// Once all nodes have been added, this call finalizes all nodes by patching in the offsets of the child nodes (that were added after the node itself was added) + bool NodeFinalize(const AABBTreeBuilder::Node *inNode, uint inNodeStart, uint inNumChildren, const uint *inChildrenNodeStart, const uint *inChildrenTrianglesStart, ByteBuffer &ioBuffer, const char *&outError) const + { + if (!inNode->HasChildren()) + return true; + + Node *node = ioBuffer.Get(inNodeStart); + for (uint i = 0; i < inNumChildren; ++i) + { + // If there are triangles, use the triangle offset otherwise use the node offset + uint offset = node->mNodeProperties[i] != 0? inChildrenTrianglesStart[i] : inChildrenNodeStart[i]; + if (offset & OFFSET_NON_SIGNIFICANT_MASK) + { + outError = "NodeCodecQuadTreeHalfFloat: Internal Error: Offset has non-significant bits set"; + return false; + } + offset >>= OFFSET_NON_SIGNIFICANT_BITS; + if (offset & ~OFFSET_MASK) + { + outError = "NodeCodecQuadTreeHalfFloat: Offset too large. Too much data."; + return false; + } + + // Store offset of next node / triangles + node->mNodeProperties[i] |= offset; + } + + return true; + } + + /// Once all nodes have been finalized, this will finalize the header of the nodes + bool Finalize(Header *outHeader, const AABBTreeBuilder::Node *inRoot, uint inRootNodeStart, uint inRootTrianglesStart, const char *&outError) const + { + uint offset = inRoot->HasChildren()? inRootNodeStart : inRootTrianglesStart; + if (offset & OFFSET_NON_SIGNIFICANT_MASK) + { + outError = "NodeCodecQuadTreeHalfFloat: Internal Error: Offset has non-significant bits set"; + return false; + } + offset >>= OFFSET_NON_SIGNIFICANT_BITS; + if (offset & ~OFFSET_MASK) + { + outError = "NodeCodecQuadTreeHalfFloat: Offset too large. Too much data."; + return false; + } + + inRoot->mBounds.mMin.StoreFloat3(&outHeader->mRootBoundsMin); + inRoot->mBounds.mMax.StoreFloat3(&outHeader->mRootBoundsMax); + outHeader->mRootProperties = offset + (inRoot->GetTriangleCount() << TRIANGLE_COUNT_SHIFT); + if (inRoot->GetTriangleCount() >= TRIANGLE_COUNT_MASK) + { + outError = "NodeCodecQuadTreeHalfFloat: Too many triangles"; + return false; + } + + return true; + } + }; + + /// This class decodes and decompresses quad tree nodes + class DecodingContext + { + public: + /// Get the amount of bits needed to store an ID to a triangle block + inline static uint sTriangleBlockIDBits(const ByteBuffer &inTree) + { + return 32 - CountLeadingZeros((uint32)inTree.size()) - OFFSET_NON_SIGNIFICANT_BITS; + } + + /// Convert a triangle block ID to the start of the triangle buffer + inline static const void * sGetTriangleBlockStart(const uint8 *inBufferStart, uint inTriangleBlockID) + { + return inBufferStart + (inTriangleBlockID << OFFSET_NON_SIGNIFICANT_BITS); + } + + /// Constructor + JPH_INLINE explicit DecodingContext(const Header *inHeader) + { + // Start with the root node on the stack + mNodeStack[0] = inHeader->mRootProperties; + } + + /// Walk the node tree calling the Visitor::VisitNodes for each node encountered and Visitor::VisitTriangles for each triangle encountered + template + JPH_INLINE void WalkTree(const uint8 *inBufferStart, const TriangleContext &inTriangleContext, Visitor &ioVisitor) + { + do + { + // Test if node contains triangles + uint32 node_properties = mNodeStack[mTop]; + uint32 tri_count = node_properties >> TRIANGLE_COUNT_SHIFT; + if (tri_count == 0) + { + const Node *node = reinterpret_cast(inBufferStart + (node_properties << OFFSET_NON_SIGNIFICANT_BITS)); + + // Unpack bounds + UVec4 bounds_minxy = UVec4::sLoadInt4(reinterpret_cast(&node->mBoundsMinX[0])); + Vec4 bounds_minx = HalfFloatConversion::ToFloat(bounds_minxy); + Vec4 bounds_miny = HalfFloatConversion::ToFloat(bounds_minxy.Swizzle()); + + UVec4 bounds_minzmaxx = UVec4::sLoadInt4(reinterpret_cast(&node->mBoundsMinZ[0])); + Vec4 bounds_minz = HalfFloatConversion::ToFloat(bounds_minzmaxx); + Vec4 bounds_maxx = HalfFloatConversion::ToFloat(bounds_minzmaxx.Swizzle()); + + UVec4 bounds_maxyz = UVec4::sLoadInt4(reinterpret_cast(&node->mBoundsMaxY[0])); + Vec4 bounds_maxy = HalfFloatConversion::ToFloat(bounds_maxyz); + Vec4 bounds_maxz = HalfFloatConversion::ToFloat(bounds_maxyz.Swizzle()); + + // Load properties for 4 children + UVec4 properties = UVec4::sLoadInt4(&node->mNodeProperties[0]); + + // Check which sub nodes to visit + int num_results = ioVisitor.VisitNodes(bounds_minx, bounds_miny, bounds_minz, bounds_maxx, bounds_maxy, bounds_maxz, properties, mTop); + + // Push them onto the stack + JPH_ASSERT(mTop + 4 < StackSize); + properties.StoreInt4(&mNodeStack[mTop]); + mTop += num_results; + } + else if (tri_count != TRIANGLE_COUNT_MASK) // TRIANGLE_COUNT_MASK indicates a padding node, normally we shouldn't visit these nodes but when querying with a big enough box you could touch HALF_FLT_MAX (about 65K) + { + // Node contains triangles, do individual tests + uint32 triangle_block_id = node_properties & OFFSET_MASK; + const void *triangles = sGetTriangleBlockStart(inBufferStart, triangle_block_id); + + ioVisitor.VisitTriangles(inTriangleContext, triangles, tri_count, triangle_block_id); + } + + // Check if we're done + if (ioVisitor.ShouldAbort()) + break; + + // Fetch next node until we find one that the visitor wants to see + do + --mTop; + while (mTop >= 0 && !ioVisitor.ShouldVisitNode(mTop)); + } + while (mTop >= 0); + } + + /// This can be used to have the visitor early out (ioVisitor.ShouldAbort() returns true) and later continue again (call WalkTree() again) + bool IsDoneWalking() const + { + return mTop < 0; + } + + private: + uint32 mNodeStack[StackSize]; + int mTop = 0; + }; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/AABBTree/TriangleCodec/TriangleCodecIndexed8BitPackSOA4Flags.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/AABBTree/TriangleCodec/TriangleCodecIndexed8BitPackSOA4Flags.h new file mode 100644 index 00000000000..7d2972187ea --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/AABBTree/TriangleCodec/TriangleCodecIndexed8BitPackSOA4Flags.h @@ -0,0 +1,456 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +/// Store vertices in 64 bits and indices in 8 bits + 8 bit of flags per triangle like this: +/// +/// TriangleBlockHeader, +/// TriangleBlock (4 triangles and their flags in 16 bytes), +/// TriangleBlock... +/// +/// Vertices are stored: +/// +/// VertexData (1 vertex in 64 bits), +/// VertexData... +/// +/// They're compressed relative to the bounding box as provided by the node codec. +class TriangleCodecIndexed8BitPackSOA4Flags +{ +public: + class TriangleHeader + { + public: + Float3 mOffset; ///< Offset of all vertices + Float3 mScale; ///< Scale of all vertices, vertex_position = mOffset + mScale * compressed_vertex_position + }; + + /// Size of the header (an empty struct is always > 0 bytes so this needs a separate variable) + static constexpr int TriangleHeaderSize = sizeof(TriangleHeader); + + /// If this codec could return a different offset than the current buffer size when calling Pack() + static constexpr bool ChangesOffsetOnPack = false; + + /// Amount of bits per component + enum EComponentData : uint32 + { + COMPONENT_BITS = 21, + COMPONENT_MASK = (1 << COMPONENT_BITS) - 1, + }; + + /// Packed X and Y coordinate + enum EVertexXY : uint32 + { + COMPONENT_X = 0, + COMPONENT_Y1 = COMPONENT_BITS, + COMPONENT_Y1_BITS = 32 - COMPONENT_BITS, + }; + + /// Packed Z and Y coordinate + enum EVertexZY : uint32 + { + COMPONENT_Z = 0, + COMPONENT_Y2 = COMPONENT_BITS, + COMPONENT_Y2_BITS = 31 - COMPONENT_BITS, + }; + + /// A single packed vertex + struct VertexData + { + uint32 mVertexXY; + uint32 mVertexZY; + }; + + static_assert(sizeof(VertexData) == 8, "Compiler added padding"); + + /// A block of 4 triangles + struct TriangleBlock + { + uint8 mIndices[3][4]; ///< 8 bit indices to triangle vertices for 4 triangles in the form mIndices[vertex][triangle] where vertex in [0, 2] and triangle in [0, 3] + uint8 mFlags[4]; ///< Triangle flags (could contain material and active edges) + }; + + static_assert(sizeof(TriangleBlock) == 16, "Compiler added padding"); + + /// A triangle header, will be followed by one or more TriangleBlocks + struct TriangleBlockHeader + { + const VertexData * GetVertexData() const { return reinterpret_cast(reinterpret_cast(this) + mOffsetToVertices); } + const TriangleBlock * GetTriangleBlock() const { return reinterpret_cast(reinterpret_cast(this) + sizeof(TriangleBlockHeader)); } + + uint32 mOffsetToVertices; ///< Offset from current block to start of vertices in bytes + }; + + static_assert(sizeof(TriangleBlockHeader) == 4, "Compiler added padding"); + + /// This class is used to validate that the triangle data will not be degenerate after compression + class ValidationContext + { + public: + /// Constructor + ValidationContext(const IndexedTriangleList &inTriangles, const VertexList &inVertices) : + mVertices(inVertices) + { + // Only used the referenced triangles, just like EncodingContext::Finalize does + for (const IndexedTriangle &i : inTriangles) + for (uint32 idx : i.mIdx) + mBounds.Encapsulate(Vec3(inVertices[idx])); + } + + /// Test if a triangle will be degenerate after quantization + bool IsDegenerate(const IndexedTriangle &inTriangle) const + { + // Quantize the triangle in the same way as EncodingContext::Finalize does + UVec4 quantized_vertex[3]; + Vec3 compress_scale = Vec3::sReplicate(COMPONENT_MASK) / Vec3::sMax(mBounds.GetSize(), Vec3::sReplicate(1.0e-20f)); + for (int i = 0; i < 3; ++i) + quantized_vertex[i] = ((Vec3(mVertices[inTriangle.mIdx[i]]) - mBounds.mMin) * compress_scale + Vec3::sReplicate(0.5f)).ToInt(); + return quantized_vertex[0] == quantized_vertex[1] || quantized_vertex[1] == quantized_vertex[2] || quantized_vertex[0] == quantized_vertex[2]; + } + + private: + const VertexList & mVertices; + AABox mBounds; + }; + + /// This class is used to encode and compress triangle data into a byte buffer + class EncodingContext + { + public: + /// Construct the encoding context + explicit EncodingContext(const VertexList &inVertices) : + mVertexMap(inVertices.size(), 0xffffffff) // Fill vertex map with 'not found' + { + // Reserve for worst case to avoid allocating in the inner loop + mVertices.reserve(inVertices.size()); + } + + /// Get an upper bound on the amount of bytes needed to store inTriangleCount triangles + uint GetPessimisticMemoryEstimate(uint inTriangleCount) const + { + // Worst case each triangle is alone in a block, none of the vertices are shared and we need to add 3 bytes to align the vertices + return inTriangleCount * (sizeof(TriangleBlockHeader) + sizeof(TriangleBlock) + 3 * sizeof(VertexData)) + 3; + } + + /// Pack the triangles in inContainer to ioBuffer. This stores the mMaterialIndex of a triangle in the 8 bit flags. + /// Returns uint(-1) on error. + uint Pack(const IndexedTriangleList &inTriangles, ByteBuffer &ioBuffer, const char *&outError) + { + // Determine position of triangles start + uint offset = (uint)ioBuffer.size(); + + // Update stats + uint tri_count = (uint)inTriangles.size(); + mNumTriangles += tri_count; + + // Allocate triangle block header + TriangleBlockHeader *header = ioBuffer.Allocate(); + + // Compute first vertex that this batch will use (ensuring there's enough room if none of the vertices are shared) + uint start_vertex = Clamp((int)mVertices.size() - 256 + (int)tri_count * 3, 0, (int)mVertices.size()); + + // Store the start vertex offset, this will later be patched to give the delta offset relative to the triangle block + mOffsetsToPatch.push_back(uint((uint8 *)&header->mOffsetToVertices - &ioBuffer[0])); + header->mOffsetToVertices = start_vertex * sizeof(VertexData); + + // Pack vertices + uint padded_triangle_count = AlignUp(tri_count, 4); + for (uint t = 0; t < padded_triangle_count; t += 4) + { + TriangleBlock *block = ioBuffer.Allocate(); + for (uint vertex_nr = 0; vertex_nr < 3; ++vertex_nr) + for (uint block_tri_idx = 0; block_tri_idx < 4; ++block_tri_idx) + { + // Fetch vertex index. Create degenerate triangles for padding triangles. + bool triangle_available = t + block_tri_idx < tri_count; + uint32 src_vertex_index = triangle_available? inTriangles[t + block_tri_idx].mIdx[vertex_nr] : inTriangles[tri_count - 1].mIdx[0]; + + // Check if we've seen this vertex before and if it is in the range that we can encode + uint32 &vertex_index = mVertexMap[src_vertex_index]; + if (vertex_index == 0xffffffff || vertex_index < start_vertex) + { + // Add vertex + vertex_index = (uint32)mVertices.size(); + mVertices.push_back(src_vertex_index); + } + + // Store vertex index + uint32 vertex_offset = vertex_index - start_vertex; + if (vertex_offset > 0xff) + { + outError = "TriangleCodecIndexed8BitPackSOA4Flags: Offset doesn't fit in 8 bit"; + return uint(-1); + } + block->mIndices[vertex_nr][block_tri_idx] = (uint8)vertex_offset; + + // Store flags + uint32 flags = triangle_available? inTriangles[t + block_tri_idx].mMaterialIndex : 0; + if (flags > 0xff) + { + outError = "TriangleCodecIndexed8BitPackSOA4Flags: Material index doesn't fit in 8 bit"; + return uint(-1); + } + block->mFlags[block_tri_idx] = (uint8)flags; + } + } + + return offset; + } + + /// After all triangles have been packed, this finalizes the header and triangle buffer + void Finalize(const VertexList &inVertices, TriangleHeader *ioHeader, ByteBuffer &ioBuffer) const + { + // Check if anything to do + if (mVertices.empty()) + return; + + // Align buffer to 4 bytes + uint vertices_idx = (uint)ioBuffer.Align(4); + + // Patch the offsets + for (uint o : mOffsetsToPatch) + *ioBuffer.Get(o) += vertices_idx - o; + + // Calculate bounding box + AABox bounds; + for (uint32 v : mVertices) + bounds.Encapsulate(Vec3(inVertices[v])); + + // Compress vertices + VertexData *vertices = ioBuffer.Allocate(mVertices.size()); + Vec3 compress_scale = Vec3::sReplicate(COMPONENT_MASK) / Vec3::sMax(bounds.GetSize(), Vec3::sReplicate(1.0e-20f)); + for (uint32 v : mVertices) + { + UVec4 c = ((Vec3(inVertices[v]) - bounds.mMin) * compress_scale + Vec3::sReplicate(0.5f)).ToInt(); + JPH_ASSERT(c.GetX() <= COMPONENT_MASK); + JPH_ASSERT(c.GetY() <= COMPONENT_MASK); + JPH_ASSERT(c.GetZ() <= COMPONENT_MASK); + vertices->mVertexXY = c.GetX() + (c.GetY() << COMPONENT_Y1); + vertices->mVertexZY = c.GetZ() + ((c.GetY() >> COMPONENT_Y1_BITS) << COMPONENT_Y2); + ++vertices; + } + + // Store decompression information + bounds.mMin.StoreFloat3(&ioHeader->mOffset); + (bounds.GetSize() / Vec3::sReplicate(COMPONENT_MASK)).StoreFloat3(&ioHeader->mScale); + } + + private: + using VertexMap = Array; + + uint mNumTriangles = 0; + Array mVertices; ///< Output vertices as an index into the original vertex list (inVertices), sorted according to occurrence + VertexMap mVertexMap; ///< Maps from the original mesh vertex index (inVertices) to the index in our output vertices (mVertices) + Array mOffsetsToPatch; ///< Offsets to the vertex buffer that need to be patched in once all nodes have been packed + }; + + /// This class is used to decode and decompress triangle data packed by the EncodingContext + class DecodingContext + { + private: + /// Private helper functions to unpack the 1 vertex of 4 triangles (outX contains the x coordinate of triangle 0 .. 3 etc.) + JPH_INLINE void Unpack(const VertexData *inVertices, UVec4Arg inIndex, Vec4 &outX, Vec4 &outY, Vec4 &outZ) const + { + // Get compressed data + UVec4 c1 = UVec4::sGatherInt4<8>(&inVertices->mVertexXY, inIndex); + UVec4 c2 = UVec4::sGatherInt4<8>(&inVertices->mVertexZY, inIndex); + + // Unpack the x y and z component + UVec4 xc = UVec4::sAnd(c1, UVec4::sReplicate(COMPONENT_MASK)); + UVec4 yc = UVec4::sOr(c1.LogicalShiftRight(), c2.LogicalShiftRight().LogicalShiftLeft()); + UVec4 zc = UVec4::sAnd(c2, UVec4::sReplicate(COMPONENT_MASK)); + + // Convert to float + outX = Vec4::sFusedMultiplyAdd(xc.ToFloat(), mScaleX, mOffsetX); + outY = Vec4::sFusedMultiplyAdd(yc.ToFloat(), mScaleY, mOffsetY); + outZ = Vec4::sFusedMultiplyAdd(zc.ToFloat(), mScaleZ, mOffsetZ); + } + + public: + JPH_INLINE explicit DecodingContext(const TriangleHeader *inHeader) : + mOffsetX(Vec4::sReplicate(inHeader->mOffset.x)), + mOffsetY(Vec4::sReplicate(inHeader->mOffset.y)), + mOffsetZ(Vec4::sReplicate(inHeader->mOffset.z)), + mScaleX(Vec4::sReplicate(inHeader->mScale.x)), + mScaleY(Vec4::sReplicate(inHeader->mScale.y)), + mScaleZ(Vec4::sReplicate(inHeader->mScale.z)) + { + } + + /// Unpacks triangles in the format t1v1,t1v2,t1v3, t2v1,t2v2,t2v3, ... + JPH_INLINE void Unpack(const void *inTriangleStart, uint32 inNumTriangles, Vec3 *outTriangles) const + { + JPH_ASSERT(inNumTriangles > 0); + const TriangleBlockHeader *header = reinterpret_cast(inTriangleStart); + const VertexData *vertices = header->GetVertexData(); + const TriangleBlock *t = header->GetTriangleBlock(); + const TriangleBlock *end = t + ((inNumTriangles + 3) >> 2); + + int triangles_left = inNumTriangles; + + do + { + // Get the indices for the three vertices (reads 4 bytes extra, but these are the flags so that's ok) + UVec4 indices = UVec4::sLoadInt4(reinterpret_cast(&t->mIndices[0])); + UVec4 iv1 = indices.Expand4Byte0(); + UVec4 iv2 = indices.Expand4Byte4(); + UVec4 iv3 = indices.Expand4Byte8(); + + // Decompress the triangle data + Vec4 v1x, v1y, v1z, v2x, v2y, v2z, v3x, v3y, v3z; + Unpack(vertices, iv1, v1x, v1y, v1z); + Unpack(vertices, iv2, v2x, v2y, v2z); + Unpack(vertices, iv3, v3x, v3y, v3z); + + // Transpose it so we get normal vectors + Mat44 v1 = Mat44(v1x, v1y, v1z, Vec4::sZero()).Transposed(); + Mat44 v2 = Mat44(v2x, v2y, v2z, Vec4::sZero()).Transposed(); + Mat44 v3 = Mat44(v3x, v3y, v3z, Vec4::sZero()).Transposed(); + + // Store triangle data + for (int i = 0; i < 4 && triangles_left > 0; ++i, --triangles_left) + { + *outTriangles++ = v1.GetColumn3(i); + *outTriangles++ = v2.GetColumn3(i); + *outTriangles++ = v3.GetColumn3(i); + } + + ++t; + } + while (t < end); + } + + /// Tests a ray against the packed triangles + JPH_INLINE float TestRay(Vec3Arg inRayOrigin, Vec3Arg inRayDirection, const void *inTriangleStart, uint32 inNumTriangles, float inClosest, uint32 &outClosestTriangleIndex) const + { + JPH_ASSERT(inNumTriangles > 0); + const TriangleBlockHeader *header = reinterpret_cast(inTriangleStart); + const VertexData *vertices = header->GetVertexData(); + const TriangleBlock *t = header->GetTriangleBlock(); + const TriangleBlock *end = t + ((inNumTriangles + 3) >> 2); + + Vec4 closest = Vec4::sReplicate(inClosest); + UVec4 closest_triangle_idx = UVec4::sZero(); + + UVec4 start_triangle_idx = UVec4::sZero(); + do + { + // Get the indices for the three vertices (reads 4 bytes extra, but these are the flags so that's ok) + UVec4 indices = UVec4::sLoadInt4(reinterpret_cast(&t->mIndices[0])); + UVec4 iv1 = indices.Expand4Byte0(); + UVec4 iv2 = indices.Expand4Byte4(); + UVec4 iv3 = indices.Expand4Byte8(); + + // Decompress the triangle data + Vec4 v1x, v1y, v1z, v2x, v2y, v2z, v3x, v3y, v3z; + Unpack(vertices, iv1, v1x, v1y, v1z); + Unpack(vertices, iv2, v2x, v2y, v2z); + Unpack(vertices, iv3, v3x, v3y, v3z); + + // Perform ray vs triangle test + Vec4 distance = RayTriangle4(inRayOrigin, inRayDirection, v1x, v1y, v1z, v2x, v2y, v2z, v3x, v3y, v3z); + + // Update closest with the smaller values + UVec4 smaller = Vec4::sLess(distance, closest); + closest = Vec4::sSelect(closest, distance, smaller); + + // Update triangle index with the smallest values + UVec4 triangle_idx = start_triangle_idx + UVec4(0, 1, 2, 3); + closest_triangle_idx = UVec4::sSelect(closest_triangle_idx, triangle_idx, smaller); + + // Next block + ++t; + start_triangle_idx += UVec4::sReplicate(4); + } + while (t < end); + + // Get the smallest component + Vec4::sSort4(closest, closest_triangle_idx); + outClosestTriangleIndex = closest_triangle_idx.GetX(); + return closest.GetX(); + } + + /// Decode a single triangle + inline void GetTriangle(const void *inTriangleStart, uint32 inTriangleIdx, Vec3 &outV1, Vec3 &outV2, Vec3 &outV3) const + { + const TriangleBlockHeader *header = reinterpret_cast(inTriangleStart); + const VertexData *vertices = header->GetVertexData(); + const TriangleBlock *block = header->GetTriangleBlock() + (inTriangleIdx >> 2); + uint32 block_triangle_idx = inTriangleIdx & 0b11; + + // Get the 3 vertices + const VertexData &v1 = vertices[block->mIndices[0][block_triangle_idx]]; + const VertexData &v2 = vertices[block->mIndices[1][block_triangle_idx]]; + const VertexData &v3 = vertices[block->mIndices[2][block_triangle_idx]]; + + // Pack the vertices + UVec4 c1(v1.mVertexXY, v2.mVertexXY, v3.mVertexXY, 0); + UVec4 c2(v1.mVertexZY, v2.mVertexZY, v3.mVertexZY, 0); + + // Unpack the x y and z component + UVec4 xc = UVec4::sAnd(c1, UVec4::sReplicate(COMPONENT_MASK)); + UVec4 yc = UVec4::sOr(c1.LogicalShiftRight(), c2.LogicalShiftRight().LogicalShiftLeft()); + UVec4 zc = UVec4::sAnd(c2, UVec4::sReplicate(COMPONENT_MASK)); + + // Convert to float + Vec4 vx = Vec4::sFusedMultiplyAdd(xc.ToFloat(), mScaleX, mOffsetX); + Vec4 vy = Vec4::sFusedMultiplyAdd(yc.ToFloat(), mScaleY, mOffsetY); + Vec4 vz = Vec4::sFusedMultiplyAdd(zc.ToFloat(), mScaleZ, mOffsetZ); + + // Transpose it so we get normal vectors + Mat44 trans = Mat44(vx, vy, vz, Vec4::sZero()).Transposed(); + outV1 = trans.GetAxisX(); + outV2 = trans.GetAxisY(); + outV3 = trans.GetAxisZ(); + } + + /// Get flags for entire triangle block + JPH_INLINE static void sGetFlags(const void *inTriangleStart, uint32 inNumTriangles, uint8 *outTriangleFlags) + { + JPH_ASSERT(inNumTriangles > 0); + const TriangleBlockHeader *header = reinterpret_cast(inTriangleStart); + const TriangleBlock *t = header->GetTriangleBlock(); + const TriangleBlock *end = t + ((inNumTriangles + 3) >> 2); + + int triangles_left = inNumTriangles; + do + { + for (int i = 0; i < 4 && triangles_left > 0; ++i, --triangles_left) + *outTriangleFlags++ = t->mFlags[i]; + + ++t; + } + while (t < end); + } + + /// Get flags for a particular triangle + JPH_INLINE static uint8 sGetFlags(const void *inTriangleStart, int inTriangleIndex) + { + const TriangleBlockHeader *header = reinterpret_cast(inTriangleStart); + const TriangleBlock *first_block = header->GetTriangleBlock(); + return first_block[inTriangleIndex >> 2].mFlags[inTriangleIndex & 0b11]; + } + + /// Unpacks triangles and flags, convenience function + JPH_INLINE void Unpack(const void *inTriangleStart, uint32 inNumTriangles, Vec3 *outTriangles, uint8 *outTriangleFlags) const + { + Unpack(inTriangleStart, inNumTriangles, outTriangles); + sGetFlags(inTriangleStart, inNumTriangles, outTriangleFlags); + } + + private: + Vec4 mOffsetX; + Vec4 mOffsetY; + Vec4 mOffsetZ; + Vec4 mScaleX; + Vec4 mScaleY; + Vec4 mScaleZ; + }; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ConfigurationString.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ConfigurationString.h new file mode 100644 index 00000000000..b6556ce6b5f --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ConfigurationString.h @@ -0,0 +1,71 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2023 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +JPH_NAMESPACE_BEGIN + +/// Construct a string that lists the most important configuration settings +inline const char *GetConfigurationString() +{ + return JPH_IF_SINGLE_PRECISION_ELSE("Single", "Double") " precision " +#if defined(JPH_CPU_X86) + "x86 " +#elif defined(JPH_CPU_ARM) + "ARM " +#elif defined(JPH_PLATFORM_WASM) + "WASM " +#endif +#if JPH_CPU_ADDRESS_BITS == 64 + "64-bit " +#elif JPH_CPU_ADDRESS_BITS == 32 + "32-bit " +#endif + "with instructions: " +#ifdef JPH_USE_NEON + "NEON " +#endif +#ifdef JPH_USE_SSE + "SSE2 " +#endif +#ifdef JPH_USE_SSE4_1 + "SSE4.1 " +#endif +#ifdef JPH_USE_SSE4_2 + "SSE4.2 " +#endif +#ifdef JPH_USE_AVX + "AVX " +#endif +#ifdef JPH_USE_AVX2 + "AVX2 " +#endif +#ifdef JPH_USE_AVX512 + "AVX512 " +#endif +#ifdef JPH_USE_F16C + "F16C " +#endif +#ifdef JPH_USE_LZCNT + "LZCNT " +#endif +#ifdef JPH_USE_TZCNT + "TZCNT " +#endif +#ifdef JPH_USE_FMADD + "FMADD " +#endif +#ifdef JPH_CROSS_PLATFORM_DETERMINISTIC + "(Cross Platform Deterministic) " +#endif +#ifdef JPH_FLOATING_POINT_EXCEPTIONS_ENABLED + "(FP Exceptions) " +#endif +#ifdef _DEBUG + "(Debug) " +#endif + ; +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/ARMNeon.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/ARMNeon.h new file mode 100644 index 00000000000..ee4d5527852 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/ARMNeon.h @@ -0,0 +1,88 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2022 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#ifdef JPH_USE_NEON + +#ifdef JPH_COMPILER_MSVC + JPH_NAMESPACE_BEGIN + + // Constructing NEON values + #define JPH_NEON_INT32x4(v1, v2, v3, v4) { int64_t(v1) + (int64_t(v2) << 32), int64_t(v3) + (int64_t(v4) << 32) } + #define JPH_NEON_UINT32x4(v1, v2, v3, v4) { uint64_t(v1) + (uint64_t(v2) << 32), uint64_t(v3) + (uint64_t(v4) << 32) } + #define JPH_NEON_INT8x16(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) { int64_t(v1) + (int64_t(v2) << 8) + (int64_t(v3) << 16) + (int64_t(v4) << 24) + (int64_t(v5) << 32) + (int64_t(v6) << 40) + (int64_t(v7) << 48) + (int64_t(v8) << 56), int64_t(v9) + (int64_t(v10) << 8) + (int64_t(v11) << 16) + (int64_t(v12) << 24) + (int64_t(v13) << 32) + (int64_t(v14) << 40) + (int64_t(v15) << 48) + (int64_t(v16) << 56) } + + // Generic shuffle vector template + template + JPH_INLINE float32x4_t NeonShuffleFloat32x4(float32x4_t inV1, float32x4_t inV2) + { + float32x4_t ret; + ret = vmovq_n_f32(vgetq_lane_f32(I1 >= 4? inV2 : inV1, I1 & 0b11)); + ret = vsetq_lane_f32(vgetq_lane_f32(I2 >= 4? inV2 : inV1, I2 & 0b11), ret, 1); + ret = vsetq_lane_f32(vgetq_lane_f32(I3 >= 4? inV2 : inV1, I3 & 0b11), ret, 2); + ret = vsetq_lane_f32(vgetq_lane_f32(I4 >= 4? inV2 : inV1, I4 & 0b11), ret, 3); + return ret; + } + + // Specializations + template <> + JPH_INLINE float32x4_t NeonShuffleFloat32x4<0, 1, 2, 2>(float32x4_t inV1, float32x4_t inV2) + { + return vcombine_f32(vget_low_f32(inV1), vdup_lane_s32(vget_high_f32(inV1), 0)); + } + + template <> + JPH_INLINE float32x4_t NeonShuffleFloat32x4<0, 1, 3, 3>(float32x4_t inV1, float32x4_t inV2) + { + return vcombine_f32(vget_low_f32(inV1), vdup_lane_s32(vget_high_f32(inV1), 1)); + } + + template <> + JPH_INLINE float32x4_t NeonShuffleFloat32x4<0, 1, 2, 3>(float32x4_t inV1, float32x4_t inV2) + { + return inV1; + } + + template <> + JPH_INLINE float32x4_t NeonShuffleFloat32x4<1, 0, 3, 2>(float32x4_t inV1, float32x4_t inV2) + { + return vcombine_f32(vrev64_f32(vget_low_f32(inV1)), vrev64_f32(vget_high_f32(inV1))); + } + + template <> + JPH_INLINE float32x4_t NeonShuffleFloat32x4<2, 2, 1, 0>(float32x4_t inV1, float32x4_t inV2) + { + return vcombine_f32(vdup_lane_s32(vget_high_f32(inV1), 0), vrev64_f32(vget_low_f32(inV1))); + } + + template <> + JPH_INLINE float32x4_t NeonShuffleFloat32x4<2, 3, 0, 1>(float32x4_t inV1, float32x4_t inV2) + { + return vcombine_f32(vget_high_f32(inV1), vget_low_f32(inV1)); + } + + // Used extensively by cross product + template <> + JPH_INLINE float32x4_t NeonShuffleFloat32x4<1, 2, 0, 0>(float32x4_t inV1, float32x4_t inV2) + { + static int8x16_t table = JPH_NEON_INT8x16(0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03); + return vreinterpretq_f32_u8(vqtbl1q_u8(vreinterpretq_u8_f32(inV1), table)); + } + + // Shuffle a vector + #define JPH_NEON_SHUFFLE_F32x4(vec1, vec2, index1, index2, index3, index4) NeonShuffleFloat32x4(vec1, vec2) + + JPH_NAMESPACE_END +#else + // Constructing NEON values + #define JPH_NEON_INT32x4(v1, v2, v3, v4) { v1, v2, v3, v4 } + #define JPH_NEON_UINT32x4(v1, v2, v3, v4) { v1, v2, v3, v4 } + #define JPH_NEON_INT8x16(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) { v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16 } + + // Shuffle a vector + #define JPH_NEON_SHUFFLE_F32x4(vec1, vec2, index1, index2, index3, index4) __builtin_shufflevector(vec1, vec2, index1, index2, index3, index4) +#endif + +#endif // JPH_USE_NEON diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Atomics.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Atomics.h new file mode 100644 index 00000000000..a53faa5c877 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Atomics.h @@ -0,0 +1,44 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +JPH_SUPPRESS_WARNINGS_STD_BEGIN +#include +JPH_SUPPRESS_WARNINGS_STD_END + +JPH_NAMESPACE_BEGIN + +// Things we're using from STL +using std::atomic; +using std::memory_order; +using std::memory_order_relaxed; +using std::memory_order_acquire; +using std::memory_order_release; +using std::memory_order_acq_rel; +using std::memory_order_seq_cst; + +/// Atomically compute the min(ioAtomic, inValue) and store it in ioAtomic, returns true if value was updated +template +bool AtomicMin(atomic &ioAtomic, const T inValue, const memory_order inMemoryOrder = memory_order_seq_cst) +{ + T cur_value = ioAtomic.load(memory_order_relaxed); + while (cur_value > inValue) + if (ioAtomic.compare_exchange_weak(cur_value, inValue, inMemoryOrder)) + return true; + return false; +} + +/// Atomically compute the max(ioAtomic, inValue) and store it in ioAtomic, returns true if value was updated +template +bool AtomicMax(atomic &ioAtomic, const T inValue, const memory_order inMemoryOrder = memory_order_seq_cst) +{ + T cur_value = ioAtomic.load(memory_order_relaxed); + while (cur_value < inValue) + if (ioAtomic.compare_exchange_weak(cur_value, inValue, inMemoryOrder)) + return true; + return false; +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/ByteBuffer.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/ByteBuffer.h new file mode 100644 index 00000000000..32e871a7eda --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/ByteBuffer.h @@ -0,0 +1,74 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +/// Underlying data type for ByteBuffer +using ByteBufferVector = std::vector>; + +/// Simple byte buffer, aligned to a cache line +class ByteBuffer : public ByteBufferVector +{ +public: + /// Align the size to a multiple of inSize, returns the length after alignment + size_t Align(size_t inSize) + { + // Assert power of 2 + JPH_ASSERT(IsPowerOf2(inSize)); + + // Calculate new size and resize buffer + size_t s = AlignUp(size(), inSize); + resize(s); + + return s; + } + + /// Allocate block of data of inSize elements and return the pointer + template + Type * Allocate(size_t inSize = 1) + { + // Reserve space + size_t s = size(); + resize(s + inSize * sizeof(Type)); + + // Get data pointer + Type *data = reinterpret_cast(&at(s)); + + // Construct elements + for (Type *d = data, *d_end = data + inSize; d < d_end; ++d) + ::new (d) Type; + + // Return pointer + return data; + } + + /// Append inData to the buffer + template + void AppendVector(const Array &inData) + { + size_t size = inData.size() * sizeof(Type); + uint8 *data = Allocate(size); + memcpy(data, &inData[0], size); + } + + /// Get object at inPosition (an offset in bytes) + template + const Type * Get(size_t inPosition) const + { + return reinterpret_cast(&at(inPosition)); + } + + /// Get object at inPosition (an offset in bytes) + template + Type * Get(size_t inPosition) + { + return reinterpret_cast(&at(inPosition)); + } +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Color.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Color.cpp new file mode 100644 index 00000000000..93d3cabc75b --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Color.cpp @@ -0,0 +1,38 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include + +JPH_NAMESPACE_BEGIN + +// Predefined colors +const Color Color::sBlack(0, 0, 0); +const Color Color::sDarkRed(128, 0, 0); +const Color Color::sRed(255, 0, 0); +const Color Color::sDarkGreen(0, 128, 0); +const Color Color::sGreen(0, 255, 0); +const Color Color::sDarkBlue(0, 0, 128); +const Color Color::sBlue(0, 0, 255); +const Color Color::sYellow(255, 255, 0); +const Color Color::sPurple(255, 0, 255); +const Color Color::sCyan(0, 255, 255); +const Color Color::sOrange(255, 128, 0); +const Color Color::sDarkOrange(128, 64, 0); +const Color Color::sGrey(128, 128, 128); +const Color Color::sLightGrey(192, 192, 192); +const Color Color::sWhite(255, 255, 255); + +// Generated by: http://phrogz.net/css/distinct-colors.html (this algo: https://en.wikipedia.org/wiki/Color_difference#CMC_l:c_.281984.29) +static constexpr Color sColors[] = { Color(255, 0, 0), Color(204, 143, 102), Color(226, 242, 0), Color(41, 166, 124), Color(0, 170, 255), Color(69, 38, 153), Color(153, 38, 130), Color(229, 57, 80), Color(204, 0, 0), Color(255, 170, 0), Color(85, 128, 0), Color(64, 255, 217), Color(0, 75, 140), Color(161, 115, 230), Color(242, 61, 157), Color(178, 101, 89), Color(140, 94, 0), Color(181, 217, 108), Color(64, 242, 255), Color(77, 117, 153), Color(157, 61, 242), Color(140, 0, 56), Color(127, 57, 32), Color(204, 173, 51), Color(64, 255, 64), Color(38, 145, 153), Color(0, 102, 255), Color(242, 0, 226), Color(153, 77, 107), Color(229, 92, 0), Color(140, 126, 70), Color(0, 179, 71), Color(0, 194, 242), Color(27, 0, 204), Color(230, 115, 222), Color(127, 0, 17) }; + +Color Color::sGetDistinctColor(int inIndex) +{ + JPH_ASSERT(inIndex >= 0); + + return sColors[inIndex % (sizeof(sColors) / sizeof(uint32))]; +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Color.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Color.h new file mode 100644 index 00000000000..b979fb4f59e --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Color.h @@ -0,0 +1,81 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +JPH_NAMESPACE_BEGIN + +class Color; + +/// Type to use for passing arguments to a function +using ColorArg = Color; + +/// Class that holds an RGBA color with 8-bits per component +class [[nodiscard]] JPH_EXPORT_GCC_BUG_WORKAROUND Color +{ +public: + /// Constructors + Color() = default; ///< Intentionally not initialized for performance reasons + Color(const Color &inRHS) = default; + Color & operator = (const Color &inRHS) = default; + explicit constexpr Color(uint32 inColor) : mU32(inColor) { } + constexpr Color(uint8 inRed, uint8 inGreen, uint8 inBlue, uint8 inAlpha = 255) : r(inRed), g(inGreen), b(inBlue), a(inAlpha) { } + constexpr Color(ColorArg inRHS, uint8 inAlpha) : r(inRHS.r), g(inRHS.g), b(inRHS.b), a(inAlpha) { } + + /// Comparison + inline bool operator == (ColorArg inRHS) const { return mU32 == inRHS.mU32; } + inline bool operator != (ColorArg inRHS) const { return mU32 != inRHS.mU32; } + + /// Convert to uint32 + uint32 GetUInt32() const { return mU32; } + + /// Element access, 0 = red, 1 = green, 2 = blue, 3 = alpha + inline uint8 operator () (uint inIdx) const { JPH_ASSERT(inIdx < 4); return (&r)[inIdx]; } + inline uint8 & operator () (uint inIdx) { JPH_ASSERT(inIdx < 4); return (&r)[inIdx]; } + + /// Multiply two colors + inline Color operator * (const Color &inRHS) const { return Color(uint8((uint32(r) * inRHS.r) >> 8), uint8((uint32(g) * inRHS.g) >> 8), uint8((uint32(b) * inRHS.b) >> 8), uint8((uint32(a) * inRHS.a) >> 8)); } + + /// Convert to Vec4 with range [0, 1] + inline Vec4 ToVec4() const { return Vec4(r, g, b, a) / 255.0f; } + + /// Get grayscale intensity of color + inline uint8 GetIntensity() const { return uint8((uint32(r) * 54 + g * 183 + b * 19) >> 8); } + + /// Get a visually distinct color + static Color sGetDistinctColor(int inIndex); + + /// Predefined colors + static const Color sBlack; + static const Color sDarkRed; + static const Color sRed; + static const Color sDarkGreen; + static const Color sGreen; + static const Color sDarkBlue; + static const Color sBlue; + static const Color sYellow; + static const Color sPurple; + static const Color sCyan; + static const Color sOrange; + static const Color sDarkOrange; + static const Color sGrey; + static const Color sLightGrey; + static const Color sWhite; + + union + { + uint32 mU32; ///< Combined value for red, green, blue and alpha + struct + { + uint8 r; ///< Red channel + uint8 g; ///< Green channel + uint8 b; ///< Blue channel + uint8 a; ///< Alpha channel + }; + }; +}; + +static_assert(is_trivial(), "Is supposed to be a trivial type!"); + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Core.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Core.h new file mode 100644 index 00000000000..bd26ef11e4c --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Core.h @@ -0,0 +1,555 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +// Jolt library version +#define JPH_VERSION_MAJOR 5 +#define JPH_VERSION_MINOR 0 +#define JPH_VERSION_PATCH 0 + +// Determine which features the library was compiled with +#ifdef JPH_DOUBLE_PRECISION + #define JPH_VERSION_FEATURE_BIT_1 1 +#else + #define JPH_VERSION_FEATURE_BIT_1 0 +#endif +#ifdef JPH_CROSS_PLATFORM_DETERMINISTIC + #define JPH_VERSION_FEATURE_BIT_2 1 +#else + #define JPH_VERSION_FEATURE_BIT_2 0 +#endif +#ifdef JPH_FLOATING_POINT_EXCEPTIONS_ENABLED + #define JPH_VERSION_FEATURE_BIT_3 1 +#else + #define JPH_VERSION_FEATURE_BIT_3 0 +#endif +#ifdef JPH_PROFILE_ENABLED + #define JPH_VERSION_FEATURE_BIT_4 1 +#else + #define JPH_VERSION_FEATURE_BIT_4 0 +#endif +#ifdef JPH_EXTERNAL_PROFILE + #define JPH_VERSION_FEATURE_BIT_5 1 +#else + #define JPH_VERSION_FEATURE_BIT_5 0 +#endif +#ifdef JPH_DEBUG_RENDERER + #define JPH_VERSION_FEATURE_BIT_6 1 +#else + #define JPH_VERSION_FEATURE_BIT_6 0 +#endif +#ifdef JPH_DISABLE_TEMP_ALLOCATOR + #define JPH_VERSION_FEATURE_BIT_7 1 +#else + #define JPH_VERSION_FEATURE_BIT_7 0 +#endif +#ifdef JPH_DISABLE_CUSTOM_ALLOCATOR + #define JPH_VERSION_FEATURE_BIT_8 1 +#else + #define JPH_VERSION_FEATURE_BIT_8 0 +#endif +#if defined(JPH_OBJECT_LAYER_BITS) && JPH_OBJECT_LAYER_BITS == 32 + #define JPH_VERSION_FEATURE_BIT_9 1 +#else + #define JPH_VERSION_FEATURE_BIT_9 0 +#endif +#ifdef JPH_ENABLE_ASSERTS + #define JPH_VERSION_FEATURE_BIT_10 1 +#else + #define JPH_VERSION_FEATURE_BIT_10 0 +#endif +#define JPH_VERSION_FEATURES (uint64(JPH_VERSION_FEATURE_BIT_1) | (JPH_VERSION_FEATURE_BIT_2 << 1) | (JPH_VERSION_FEATURE_BIT_3 << 2) | (JPH_VERSION_FEATURE_BIT_4 << 3) | (JPH_VERSION_FEATURE_BIT_5 << 4) | (JPH_VERSION_FEATURE_BIT_6 << 5) | (JPH_VERSION_FEATURE_BIT_7 << 6) | (JPH_VERSION_FEATURE_BIT_8 << 7) | (JPH_VERSION_FEATURE_BIT_9 << 8) | (JPH_VERSION_FEATURE_BIT_10 << 9)) + +// Combine the version and features in a single ID +#define JPH_VERSION_ID ((JPH_VERSION_FEATURES << 24) | (JPH_VERSION_MAJOR << 16) | (JPH_VERSION_MINOR << 8) | JPH_VERSION_PATCH) + +// Determine platform +#if defined(JPH_PLATFORM_BLUE) + // Correct define already defined, this overrides everything else +#elif defined(_WIN32) || defined(_WIN64) + #include + #if WINAPI_FAMILY == WINAPI_FAMILY_APP + #define JPH_PLATFORM_WINDOWS_UWP // Building for Universal Windows Platform + #endif + #define JPH_PLATFORM_WINDOWS +#elif defined(__ANDROID__) // Android is linux too, so that's why we check it first + #define JPH_PLATFORM_ANDROID +#elif defined(__linux__) + #define JPH_PLATFORM_LINUX +#elif defined(__FreeBSD__) + #define JPH_PLATFORM_FREEBSD +#elif defined(__APPLE__) + #include + #if defined(TARGET_OS_IPHONE) && !TARGET_OS_IPHONE + #define JPH_PLATFORM_MACOS + #else + #define JPH_PLATFORM_IOS + #endif +#elif defined(__EMSCRIPTEN__) + #define JPH_PLATFORM_WASM +#endif + +// Platform helper macros +#ifdef JPH_PLATFORM_ANDROID + #define JPH_IF_NOT_ANDROID(x) +#else + #define JPH_IF_NOT_ANDROID(x) x +#endif + +// Determine compiler +#if defined(__clang__) + #define JPH_COMPILER_CLANG +#elif defined(__GNUC__) + #define JPH_COMPILER_GCC +#elif defined(_MSC_VER) + #define JPH_COMPILER_MSVC +#endif + +#if defined(__MINGW64__) || defined (__MINGW32__) + #define JPH_COMPILER_MINGW +#endif + +// Detect CPU architecture +#if defined(__x86_64__) || defined(_M_X64) || defined(__i386__) || defined(_M_IX86) + // X86 CPU architecture + #define JPH_CPU_X86 + #if defined(__x86_64__) || defined(_M_X64) + #define JPH_CPU_ADDRESS_BITS 64 + #else + #define JPH_CPU_ADDRESS_BITS 32 + #endif + #define JPH_USE_SSE + #define JPH_VECTOR_ALIGNMENT 16 + #define JPH_DVECTOR_ALIGNMENT 32 + + // Detect enabled instruction sets + #if defined(__AVX512F__) && defined(__AVX512VL__) && defined(__AVX512DQ__) && !defined(JPH_USE_AVX512) + #define JPH_USE_AVX512 + #endif + #if (defined(__AVX2__) || defined(JPH_USE_AVX512)) && !defined(JPH_USE_AVX2) + #define JPH_USE_AVX2 + #endif + #if (defined(__AVX__) || defined(JPH_USE_AVX2)) && !defined(JPH_USE_AVX) + #define JPH_USE_AVX + #endif + #if (defined(__SSE4_2__) || defined(JPH_USE_AVX)) && !defined(JPH_USE_SSE4_2) + #define JPH_USE_SSE4_2 + #endif + #if (defined(__SSE4_1__) || defined(JPH_USE_SSE4_2)) && !defined(JPH_USE_SSE4_1) + #define JPH_USE_SSE4_1 + #endif + #if (defined(__F16C__) || defined(JPH_USE_AVX2)) && !defined(JPH_USE_F16C) + #define JPH_USE_F16C + #endif + #if (defined(__LZCNT__) || defined(JPH_USE_AVX2)) && !defined(JPH_USE_LZCNT) + #define JPH_USE_LZCNT + #endif + #if (defined(__BMI__) || defined(JPH_USE_AVX2)) && !defined(JPH_USE_TZCNT) + #define JPH_USE_TZCNT + #endif + #ifndef JPH_CROSS_PLATFORM_DETERMINISTIC // FMA is not compatible with cross platform determinism + #if defined(JPH_COMPILER_CLANG) || defined(JPH_COMPILER_GCC) + #if defined(__FMA__) && !defined(JPH_USE_FMADD) + #define JPH_USE_FMADD + #endif + #elif defined(JPH_COMPILER_MSVC) + #if defined(__AVX2__) && !defined(JPH_USE_FMADD) // AVX2 also enables fused multiply add + #define JPH_USE_FMADD + #endif + #else + #error Undefined compiler + #endif + #endif +#elif defined(__aarch64__) || defined(_M_ARM64) || defined(__arm__) || defined(_M_ARM) + // ARM CPU architecture + #define JPH_CPU_ARM + #if defined(__aarch64__) || defined(_M_ARM64) + #define JPH_CPU_ADDRESS_BITS 64 + #define JPH_USE_NEON + #define JPH_VECTOR_ALIGNMENT 16 + #define JPH_DVECTOR_ALIGNMENT 32 + #else + #define JPH_CPU_ADDRESS_BITS 32 + #define JPH_VECTOR_ALIGNMENT 8 // 32-bit ARM does not support aligning on the stack on 16 byte boundaries + #define JPH_DVECTOR_ALIGNMENT 8 + #endif +#elif defined(JPH_PLATFORM_WASM) + // WebAssembly CPU architecture + #define JPH_CPU_WASM + #define JPH_CPU_ADDRESS_BITS 32 + #define JPH_VECTOR_ALIGNMENT 16 + #define JPH_DVECTOR_ALIGNMENT 32 + #ifdef __wasm_simd128__ + #define JPH_USE_SSE + #define JPH_USE_SSE4_1 + #define JPH_USE_SSE4_2 + #endif +#elif defined(__e2k__) + // Elbrus e2k architecture + #define JPH_CPU_E2K + #define JPH_CPU_ADDRESS_BITS 64 + #define JPH_USE_SSE + #define JPH_VECTOR_ALIGNMENT 16 + #define JPH_DVECTOR_ALIGNMENT 32 +#else + #error Unsupported CPU architecture +#endif + +// If this define is set, Jolt is compiled as a shared library +#ifdef JPH_SHARED_LIBRARY + #ifdef JPH_BUILD_SHARED_LIBRARY + // While building the shared library, we must export these symbols + #ifdef JPH_PLATFORM_WINDOWS + #define JPH_EXPORT __declspec(dllexport) + #else + #define JPH_EXPORT __attribute__ ((visibility ("default"))) + #if defined(JPH_COMPILER_GCC) + // Prevents an issue with GCC attribute parsing (see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=69585) + #define JPH_EXPORT_GCC_BUG_WORKAROUND [[gnu::visibility("default")]] + #endif + #endif + #else + // When linking against Jolt, we must import these symbols + #ifdef JPH_PLATFORM_WINDOWS + #define JPH_EXPORT __declspec(dllimport) + #else + #define JPH_EXPORT __attribute__ ((visibility ("default"))) + #if defined(JPH_COMPILER_GCC) + // Prevents an issue with GCC attribute parsing (see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=69585) + #define JPH_EXPORT_GCC_BUG_WORKAROUND [[gnu::visibility("default")]] + #endif + #endif + #endif +#else + // If the define is not set, we use static linking and symbols don't need to be imported or exported + #define JPH_EXPORT +#endif + +#ifndef JPH_EXPORT_GCC_BUG_WORKAROUND + #define JPH_EXPORT_GCC_BUG_WORKAROUND JPH_EXPORT +#endif + +// Macro used by the RTTI macros to not export a function +#define JPH_NO_EXPORT + +// Pragmas to store / restore the warning state and to disable individual warnings +#ifdef JPH_COMPILER_CLANG +#define JPH_PRAGMA(x) _Pragma(#x) +#define JPH_SUPPRESS_WARNING_PUSH JPH_PRAGMA(clang diagnostic push) +#define JPH_SUPPRESS_WARNING_POP JPH_PRAGMA(clang diagnostic pop) +#define JPH_CLANG_SUPPRESS_WARNING(w) JPH_PRAGMA(clang diagnostic ignored w) +#if __clang_major__ >= 13 + #define JPH_CLANG_13_PLUS_SUPPRESS_WARNING(w) JPH_CLANG_SUPPRESS_WARNING(w) +#else + #define JPH_CLANG_13_PLUS_SUPPRESS_WARNING(w) +#endif +#if __clang_major__ >= 16 + #define JPH_CLANG_16_PLUS_SUPPRESS_WARNING(w) JPH_CLANG_SUPPRESS_WARNING(w) +#else + #define JPH_CLANG_16_PLUS_SUPPRESS_WARNING(w) +#endif +#else +#define JPH_CLANG_SUPPRESS_WARNING(w) +#define JPH_CLANG_13_PLUS_SUPPRESS_WARNING(w) +#define JPH_CLANG_16_PLUS_SUPPRESS_WARNING(w) +#endif +#ifdef JPH_COMPILER_GCC +#define JPH_PRAGMA(x) _Pragma(#x) +#define JPH_SUPPRESS_WARNING_PUSH JPH_PRAGMA(GCC diagnostic push) +#define JPH_SUPPRESS_WARNING_POP JPH_PRAGMA(GCC diagnostic pop) +#define JPH_GCC_SUPPRESS_WARNING(w) JPH_PRAGMA(GCC diagnostic ignored w) +#else +#define JPH_GCC_SUPPRESS_WARNING(w) +#endif +#ifdef JPH_COMPILER_MSVC +#define JPH_PRAGMA(x) __pragma(x) +#define JPH_SUPPRESS_WARNING_PUSH JPH_PRAGMA(warning (push)) +#define JPH_SUPPRESS_WARNING_POP JPH_PRAGMA(warning (pop)) +#define JPH_MSVC_SUPPRESS_WARNING(w) JPH_PRAGMA(warning (disable : w)) +#if _MSC_VER >= 1920 && _MSC_VER < 1930 + #define JPH_MSVC2019_SUPPRESS_WARNING(w) JPH_MSVC_SUPPRESS_WARNING(w) +#else + #define JPH_MSVC2019_SUPPRESS_WARNING(w) +#endif +#else +#define JPH_MSVC_SUPPRESS_WARNING(w) +#define JPH_MSVC2019_SUPPRESS_WARNING(w) +#endif + +// Disable common warnings triggered by Jolt when compiling with -Wall +#define JPH_SUPPRESS_WARNINGS \ + JPH_CLANG_SUPPRESS_WARNING("-Wc++98-compat") \ + JPH_CLANG_SUPPRESS_WARNING("-Wc++98-compat-pedantic") \ + JPH_CLANG_SUPPRESS_WARNING("-Wfloat-equal") \ + JPH_CLANG_SUPPRESS_WARNING("-Wsign-conversion") \ + JPH_CLANG_SUPPRESS_WARNING("-Wold-style-cast") \ + JPH_CLANG_SUPPRESS_WARNING("-Wgnu-anonymous-struct") \ + JPH_CLANG_SUPPRESS_WARNING("-Wnested-anon-types") \ + JPH_CLANG_SUPPRESS_WARNING("-Wglobal-constructors") \ + JPH_CLANG_SUPPRESS_WARNING("-Wexit-time-destructors") \ + JPH_CLANG_SUPPRESS_WARNING("-Wnonportable-system-include-path") \ + JPH_CLANG_SUPPRESS_WARNING("-Wlanguage-extension-token") \ + JPH_CLANG_SUPPRESS_WARNING("-Wunused-parameter") \ + JPH_CLANG_SUPPRESS_WARNING("-Wformat-nonliteral") \ + JPH_CLANG_SUPPRESS_WARNING("-Wcovered-switch-default") \ + JPH_CLANG_SUPPRESS_WARNING("-Wcast-align") \ + JPH_CLANG_SUPPRESS_WARNING("-Winvalid-offsetof") \ + JPH_CLANG_SUPPRESS_WARNING("-Wgnu-zero-variadic-macro-arguments") \ + JPH_CLANG_SUPPRESS_WARNING("-Wdocumentation-unknown-command") \ + JPH_CLANG_SUPPRESS_WARNING("-Wctad-maybe-unsupported") \ + JPH_CLANG_13_PLUS_SUPPRESS_WARNING("-Wdeprecated-copy") \ + JPH_CLANG_13_PLUS_SUPPRESS_WARNING("-Wdeprecated-copy-with-dtor") \ + JPH_CLANG_16_PLUS_SUPPRESS_WARNING("-Wunsafe-buffer-usage") \ + JPH_IF_NOT_ANDROID(JPH_CLANG_SUPPRESS_WARNING("-Wimplicit-int-float-conversion")) \ + \ + JPH_GCC_SUPPRESS_WARNING("-Wcomment") \ + JPH_GCC_SUPPRESS_WARNING("-Winvalid-offsetof") \ + JPH_GCC_SUPPRESS_WARNING("-Wclass-memaccess") \ + JPH_GCC_SUPPRESS_WARNING("-Wpedantic") \ + \ + JPH_MSVC_SUPPRESS_WARNING(4619) /* #pragma warning: there is no warning number 'XXXX' */ \ + JPH_MSVC_SUPPRESS_WARNING(4514) /* 'X' : unreferenced inline function has been removed */ \ + JPH_MSVC_SUPPRESS_WARNING(4710) /* 'X' : function not inlined */ \ + JPH_MSVC_SUPPRESS_WARNING(4711) /* function 'X' selected for automatic inline expansion */ \ + JPH_MSVC_SUPPRESS_WARNING(4820) /* 'X': 'Y' bytes padding added after data member 'Z' */ \ + JPH_MSVC_SUPPRESS_WARNING(4100) /* 'X' : unreferenced formal parameter */ \ + JPH_MSVC_SUPPRESS_WARNING(4626) /* 'X' : assignment operator was implicitly defined as deleted because a base class assignment operator is inaccessible or deleted */ \ + JPH_MSVC_SUPPRESS_WARNING(5027) /* 'X' : move assignment operator was implicitly defined as deleted because a base class move assignment operator is inaccessible or deleted */ \ + JPH_MSVC_SUPPRESS_WARNING(4365) /* 'argument' : conversion from 'X' to 'Y', signed / unsigned mismatch */ \ + JPH_MSVC_SUPPRESS_WARNING(4324) /* 'X' : structure was padded due to alignment specifier */ \ + JPH_MSVC_SUPPRESS_WARNING(4625) /* 'X' : copy constructor was implicitly defined as deleted because a base class copy constructor is inaccessible or deleted */ \ + JPH_MSVC_SUPPRESS_WARNING(5026) /* 'X': move constructor was implicitly defined as deleted because a base class move constructor is inaccessible or deleted */ \ + JPH_MSVC_SUPPRESS_WARNING(4623) /* 'X' : default constructor was implicitly defined as deleted */ \ + JPH_MSVC_SUPPRESS_WARNING(4201) /* nonstandard extension used: nameless struct/union */ \ + JPH_MSVC_SUPPRESS_WARNING(4371) /* 'X': layout of class may have changed from a previous version of the compiler due to better packing of member 'Y' */ \ + JPH_MSVC_SUPPRESS_WARNING(5045) /* Compiler will insert Spectre mitigation for memory load if /Qspectre switch specified */ \ + JPH_MSVC_SUPPRESS_WARNING(4583) /* 'X': destructor is not implicitly called */ \ + JPH_MSVC_SUPPRESS_WARNING(4582) /* 'X': constructor is not implicitly called */ \ + JPH_MSVC_SUPPRESS_WARNING(5219) /* implicit conversion from 'X' to 'Y', possible loss of data */ \ + JPH_MSVC_SUPPRESS_WARNING(4826) /* Conversion from 'X *' to 'JPH::uint64' is sign-extended. This may cause unexpected runtime behavior. (32-bit) */ \ + JPH_MSVC_SUPPRESS_WARNING(5264) /* 'X': 'const' variable is not used */ \ + JPH_MSVC_SUPPRESS_WARNING(4251) /* class 'X' needs to have DLL-interface to be used by clients of class 'Y' */ \ + JPH_MSVC_SUPPRESS_WARNING(4738) /* storing 32-bit float result in memory, possible loss of performance */ \ + JPH_MSVC2019_SUPPRESS_WARNING(5246) /* the initialization of a subobject should be wrapped in braces */ + +// OS-specific includes +#if defined(JPH_PLATFORM_WINDOWS) + #define JPH_BREAKPOINT __debugbreak() +#elif defined(JPH_PLATFORM_BLUE) + // Configuration for a popular game console. + // This file is not distributed because it would violate an NDA. + // Creating one should only be a couple of minutes of work if you have the documentation for the platform + // (you only need to define JPH_BREAKPOINT, JPH_PLATFORM_BLUE_GET_TICKS, JPH_PLATFORM_BLUE_MUTEX*, JPH_PLATFORM_BLUE_RWLOCK* and include the right header). + #include +#elif defined(JPH_PLATFORM_LINUX) || defined(JPH_PLATFORM_ANDROID) || defined(JPH_PLATFORM_MACOS) || defined(JPH_PLATFORM_IOS) || defined(JPH_PLATFORM_FREEBSD) + #if defined(JPH_CPU_X86) + #define JPH_BREAKPOINT __asm volatile ("int $0x3") + #elif defined(JPH_CPU_ARM) + #define JPH_BREAKPOINT __builtin_trap() + #elif defined(JPH_CPU_E2K) + #define JPH_BREAKPOINT __builtin_trap() + #endif +#elif defined(JPH_PLATFORM_WASM) + #define JPH_BREAKPOINT do { } while (false) // Not supported +#else + #error Unknown platform +#endif + +// Begin the JPH namespace +#define JPH_NAMESPACE_BEGIN \ + JPH_SUPPRESS_WARNING_PUSH \ + JPH_SUPPRESS_WARNINGS \ + namespace JPH { + +// End the JPH namespace +#define JPH_NAMESPACE_END \ + } \ + JPH_SUPPRESS_WARNING_POP + +// Suppress warnings generated by the standard template library +#define JPH_SUPPRESS_WARNINGS_STD_BEGIN \ + JPH_SUPPRESS_WARNING_PUSH \ + JPH_MSVC_SUPPRESS_WARNING(4365) \ + JPH_MSVC_SUPPRESS_WARNING(4619) \ + JPH_MSVC_SUPPRESS_WARNING(4710) \ + JPH_MSVC_SUPPRESS_WARNING(4711) \ + JPH_MSVC_SUPPRESS_WARNING(4820) \ + JPH_MSVC_SUPPRESS_WARNING(4514) \ + JPH_MSVC_SUPPRESS_WARNING(5262) \ + JPH_MSVC_SUPPRESS_WARNING(5264) \ + JPH_MSVC_SUPPRESS_WARNING(4738) + +#define JPH_SUPPRESS_WARNINGS_STD_END \ + JPH_SUPPRESS_WARNING_POP + +// Standard C++ includes +#include +#include +#include +JPH_SUPPRESS_WARNINGS_STD_BEGIN +#include +#include +#include +#include +#include +#include +#include +JPH_SUPPRESS_WARNINGS_STD_END +#if defined(JPH_USE_SSE) + #include +#elif defined(JPH_USE_NEON) + #ifdef JPH_COMPILER_MSVC + #include + #include + #else + #include + #endif +#endif + +JPH_NAMESPACE_BEGIN + +// Commonly used STL types +using std::pair; +using std::min; +using std::max; +using std::abs; +using std::sqrt; +using std::ceil; +using std::floor; +using std::trunc; +using std::round; +using std::fmod; +using std::swap; +using std::size; +using std::string; +using std::string_view; +using std::function; +using std::numeric_limits; +using std::isfinite; +using std::isnan; +using std::is_trivial; +using std::is_trivially_constructible; +using std::is_trivially_destructible; +using std::ostream; +using std::istream; + +// Standard types +using uint = unsigned int; +using uint8 = std::uint8_t; +using uint16 = std::uint16_t; +using uint32 = std::uint32_t; +using uint64 = std::uint64_t; + +// Assert sizes of types +static_assert(sizeof(uint) >= 4, "Invalid size of uint"); +static_assert(sizeof(uint8) == 1, "Invalid size of uint8"); +static_assert(sizeof(uint16) == 2, "Invalid size of uint16"); +static_assert(sizeof(uint32) == 4, "Invalid size of uint32"); +static_assert(sizeof(uint64) == 8, "Invalid size of uint64"); +static_assert(sizeof(void *) == (JPH_CPU_ADDRESS_BITS == 64? 8 : 4), "Invalid size of pointer" ); + +// Define inline macro +#if defined(JPH_NO_FORCE_INLINE) + #define JPH_INLINE inline +#elif defined(JPH_COMPILER_CLANG) || defined(JPH_COMPILER_GCC) + #define JPH_INLINE __inline__ __attribute__((always_inline)) +#elif defined(JPH_COMPILER_MSVC) + #define JPH_INLINE __forceinline +#else + #error Undefined +#endif + +// Cache line size (used for aligning to cache line) +#ifndef JPH_CACHE_LINE_SIZE + #define JPH_CACHE_LINE_SIZE 64 +#endif + +// Define macro to get current function name +#if defined(JPH_COMPILER_CLANG) || defined(JPH_COMPILER_GCC) + #define JPH_FUNCTION_NAME __PRETTY_FUNCTION__ +#elif defined(JPH_COMPILER_MSVC) + #define JPH_FUNCTION_NAME __FUNCTION__ +#else + #error Undefined +#endif + +// Stack allocation +#define JPH_STACK_ALLOC(n) alloca(n) + +// Shorthand for #ifdef _DEBUG / #endif +#ifdef _DEBUG + #define JPH_IF_DEBUG(...) __VA_ARGS__ + #define JPH_IF_NOT_DEBUG(...) +#else + #define JPH_IF_DEBUG(...) + #define JPH_IF_NOT_DEBUG(...) __VA_ARGS__ +#endif + +// Shorthand for #ifdef JPH_FLOATING_POINT_EXCEPTIONS_ENABLED / #endif +#ifdef JPH_FLOATING_POINT_EXCEPTIONS_ENABLED + #define JPH_IF_FLOATING_POINT_EXCEPTIONS_ENABLED(...) __VA_ARGS__ +#else + #define JPH_IF_FLOATING_POINT_EXCEPTIONS_ENABLED(...) +#endif + +// Helper macros to detect if we're running in single or double precision mode +#ifdef JPH_DOUBLE_PRECISION + #define JPH_IF_SINGLE_PRECISION(...) + #define JPH_IF_SINGLE_PRECISION_ELSE(s, d) d + #define JPH_IF_DOUBLE_PRECISION(...) __VA_ARGS__ +#else + #define JPH_IF_SINGLE_PRECISION(...) __VA_ARGS__ + #define JPH_IF_SINGLE_PRECISION_ELSE(s, d) s + #define JPH_IF_DOUBLE_PRECISION(...) +#endif + +// Helper macro to detect if the debug renderer is active +#ifdef JPH_DEBUG_RENDERER + #define JPH_IF_DEBUG_RENDERER(...) __VA_ARGS__ + #define JPH_IF_NOT_DEBUG_RENDERER(...) +#else + #define JPH_IF_DEBUG_RENDERER(...) + #define JPH_IF_NOT_DEBUG_RENDERER(...) __VA_ARGS__ +#endif + +// Macro to indicate that a parameter / variable is unused +#define JPH_UNUSED(x) (void)x + +// Macro to enable floating point precise mode and to disable fused multiply add instructions +#if defined(JPH_COMPILER_GCC) || defined(JPH_CROSS_PLATFORM_DETERMINISTIC) + // We compile without -ffast-math and -ffp-contract=fast, so we don't need to disable anything + #define JPH_PRECISE_MATH_ON + #define JPH_PRECISE_MATH_OFF +#elif defined(JPH_COMPILER_CLANG) + // We compile without -ffast-math because pragma float_control(precise, on) doesn't seem to actually negate all of the -ffast-math effects and causes the unit tests to fail (even if the pragma is added to all files) + // On clang 14 and later we can turn off float contraction through a pragma (before it was buggy), so if FMA is on we can disable it through this macro + #if (defined(JPH_CPU_ARM) && !defined(JPH_PLATFORM_ANDROID) && __clang_major__ >= 16) || (defined(JPH_CPU_X86) && __clang_major__ >= 14) + #define JPH_PRECISE_MATH_ON \ + _Pragma("float_control(precise, on, push)") \ + _Pragma("clang fp contract(off)") + #define JPH_PRECISE_MATH_OFF \ + _Pragma("float_control(pop)") + #elif __clang_major__ >= 14 && (defined(JPH_USE_FMADD) || defined(FP_FAST_FMA)) + #define JPH_PRECISE_MATH_ON \ + _Pragma("clang fp contract(off)") + #define JPH_PRECISE_MATH_OFF \ + _Pragma("clang fp contract(on)") + #else + #define JPH_PRECISE_MATH_ON + #define JPH_PRECISE_MATH_OFF + #endif +#elif defined(JPH_COMPILER_MSVC) + // Unfortunately there is no way to push the state of fp_contract, so we have to assume it was turned on before JPH_PRECISE_MATH_ON + #define JPH_PRECISE_MATH_ON \ + __pragma(float_control(precise, on, push)) \ + __pragma(fp_contract(off)) + #define JPH_PRECISE_MATH_OFF \ + __pragma(fp_contract(on)) \ + __pragma(float_control(pop)) +#else + #error Undefined +#endif + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/FPControlWord.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/FPControlWord.h new file mode 100644 index 00000000000..a046bffe9f3 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/FPControlWord.h @@ -0,0 +1,135 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +#if defined(JPH_CPU_WASM) + +// Not supported + +#elif defined(JPH_USE_SSE) + +/// Helper class that needs to be put on the stack to update the state of the floating point control word. +/// This state is kept per thread. +template +class FPControlWord : public NonCopyable +{ +public: + FPControlWord() + { + mPrevState = _mm_getcsr(); + _mm_setcsr((mPrevState & ~Mask) | Value); + } + + ~FPControlWord() + { + _mm_setcsr((_mm_getcsr() & ~Mask) | (mPrevState & Mask)); + } + +private: + uint mPrevState; +}; + +#elif defined(JPH_CPU_ARM) && defined(JPH_COMPILER_MSVC) + +/// Helper class that needs to be put on the stack to update the state of the floating point control word. +/// This state is kept per thread. +template +class FPControlWord : public NonCopyable +{ +public: + FPControlWord() + { + // Read state before change + _controlfp_s(&mPrevState, 0, 0); + + // Update the state + unsigned int dummy; + _controlfp_s(&dummy, Value, Mask); + } + + ~FPControlWord() + { + // Restore state + unsigned int dummy; + _controlfp_s(&dummy, mPrevState, Mask); + } + +private: + unsigned int mPrevState; +}; + +#elif defined(JPH_CPU_ARM) && defined(JPH_USE_NEON) + +/// Helper class that needs to be put on the stack to update the state of the floating point control word. +/// This state is kept per thread. +template +class FPControlWord : public NonCopyable +{ +public: + FPControlWord() + { + uint64 val; + asm volatile("mrs %0, fpcr" : "=r" (val)); + mPrevState = val; + val &= ~Mask; + val |= Value; + asm volatile("msr fpcr, %0" : /* no output */ : "r" (val)); + } + + ~FPControlWord() + { + uint64 val; + asm volatile("mrs %0, fpcr" : "=r" (val)); + val &= ~Mask; + val |= mPrevState & Mask; + asm volatile("msr fpcr, %0" : /* no output */ : "r" (val)); + } + +private: + uint64 mPrevState; +}; + +#elif defined(JPH_CPU_ARM) + +/// Helper class that needs to be put on the stack to update the state of the floating point control word. +/// This state is kept per thread. +template +class FPControlWord : public NonCopyable +{ +public: + FPControlWord() + { + uint32 val; + asm volatile("vmrs %0, fpscr" : "=r" (val)); + mPrevState = val; + val &= ~Mask; + val |= Value; + asm volatile("vmsr fpscr, %0" : /* no output */ : "r" (val)); + } + + ~FPControlWord() + { + uint32 val; + asm volatile("vmrs %0, fpscr" : "=r" (val)); + val &= ~Mask; + val |= mPrevState & Mask; + asm volatile("vmsr fpscr, %0" : /* no output */ : "r" (val)); + } + +private: + uint32 mPrevState; +}; + +#else + +#error Unsupported CPU architecture + +#endif + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/FPException.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/FPException.h new file mode 100644 index 00000000000..3083f05c050 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/FPException.h @@ -0,0 +1,74 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +#ifdef JPH_FLOATING_POINT_EXCEPTIONS_ENABLED + +#if defined(JPH_CPU_WASM) + +// Not supported +class FPExceptionsEnable { }; +class FPExceptionDisableInvalid { }; +class FPExceptionDisableDivByZero { }; + +#elif defined(JPH_USE_SSE) + +/// Enable floating point divide by zero exception and exceptions on invalid numbers +class FPExceptionsEnable : public FPControlWord<0, _MM_MASK_DIV_ZERO | _MM_MASK_INVALID> { }; + +/// Disable invalid floating point value exceptions +class FPExceptionDisableInvalid : public FPControlWord<_MM_MASK_INVALID, _MM_MASK_INVALID> { }; + +/// Disable division by zero floating point exceptions +class FPExceptionDisableDivByZero : public FPControlWord<_MM_MASK_DIV_ZERO, _MM_MASK_DIV_ZERO> { }; + +#elif defined(JPH_CPU_ARM) && defined(JPH_COMPILER_MSVC) + +/// Enable floating point divide by zero exception and exceptions on invalid numbers +class FPExceptionsEnable : public FPControlWord<0, _EM_INVALID | _EM_ZERODIVIDE> { }; + +/// Disable invalid floating point value exceptions +class FPExceptionDisableInvalid : public FPControlWord<_EM_INVALID, _EM_INVALID> { }; + +/// Disable division by zero floating point exceptions +class FPExceptionDisableDivByZero : public FPControlWord<_EM_ZERODIVIDE, _EM_ZERODIVIDE> { }; + +#elif defined(JPH_CPU_ARM) + +/// Invalid operation exception bit +static constexpr uint64 FP_IOE = 1 << 8; + +/// Enable divide by zero exception bit +static constexpr uint64 FP_DZE = 1 << 9; + +/// Enable floating point divide by zero exception and exceptions on invalid numbers +class FPExceptionsEnable : public FPControlWord { }; + +/// Disable invalid floating point value exceptions +class FPExceptionDisableInvalid : public FPControlWord<0, FP_IOE> { }; + +/// Disable division by zero floating point exceptions +class FPExceptionDisableDivByZero : public FPControlWord<0, FP_DZE> { }; + +#else + +#error Unsupported CPU architecture + +#endif + +#else + +/// Dummy implementations +class FPExceptionsEnable { }; +class FPExceptionDisableInvalid { }; +class FPExceptionDisableDivByZero { }; + +#endif + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/FPFlushDenormals.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/FPFlushDenormals.h new file mode 100644 index 00000000000..672a19dbecf --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/FPFlushDenormals.h @@ -0,0 +1,41 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +#if defined(JPH_CPU_WASM) + +// Not supported +class FPFlushDenormals { }; + +#elif defined(JPH_USE_SSE) + +/// Helper class that needs to be put on the stack to enable flushing denormals to zero +/// This can make floating point operations much faster when working with very small numbers +class FPFlushDenormals : public FPControlWord<_MM_FLUSH_ZERO_ON, _MM_FLUSH_ZERO_MASK> { }; + +#elif defined(JPH_CPU_ARM) && defined(JPH_COMPILER_MSVC) + +class FPFlushDenormals : public FPControlWord<_DN_FLUSH, _MCW_DN> { }; + +#elif defined(JPH_CPU_ARM) + +/// Flush denormals to zero bit +static constexpr uint64 FP_FZ = 1 << 24; + +/// Helper class that needs to be put on the stack to enable flushing denormals to zero +/// This can make floating point operations much faster when working with very small numbers +class FPFlushDenormals : public FPControlWord { }; + +#else + +#error Unsupported CPU architecture + +#endif + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Factory.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Factory.cpp new file mode 100644 index 00000000000..079ffb1600e --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Factory.cpp @@ -0,0 +1,87 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include + +JPH_NAMESPACE_BEGIN + +Factory *Factory::sInstance = nullptr; + +void *Factory::CreateObject(const char *inName) +{ + const RTTI *ci = Find(inName); + return ci != nullptr? ci->CreateObject() : nullptr; +} + +const RTTI *Factory::Find(const char *inName) +{ + ClassNameMap::iterator c = mClassNameMap.find(inName); + return c != mClassNameMap.end()? c->second : nullptr; +} + +const RTTI *Factory::Find(uint32 inHash) +{ + ClassHashMap::iterator c = mClassHashMap.find(inHash); + return c != mClassHashMap.end()? c->second : nullptr; +} + +bool Factory::Register(const RTTI *inRTTI) +{ + // Check if we already know the type + if (Find(inRTTI->GetName()) != nullptr) + return true; + + // Insert this class by name + mClassNameMap.try_emplace(inRTTI->GetName(), inRTTI); + + // Insert this class by hash + if (!mClassHashMap.try_emplace(inRTTI->GetHash(), inRTTI).second) + { + JPH_ASSERT(false, "Hash collision registering type!"); + return false; + } + + // Register base classes + for (int i = 0; i < inRTTI->GetBaseClassCount(); ++i) + if (!Register(inRTTI->GetBaseClass(i))) + return false; + + // Register attribute classes + for (int i = 0; i < inRTTI->GetAttributeCount(); ++i) + { + const RTTI *rtti = inRTTI->GetAttribute(i).GetMemberPrimitiveType(); + if (rtti != nullptr && !Register(rtti)) + return false; + } + + return true; +} + +bool Factory::Register(const RTTI **inRTTIs, uint inNumber) +{ + for (const RTTI **rtti = inRTTIs; rtti < inRTTIs + inNumber; ++rtti) + if (!Register(*rtti)) + return false; + + return true; +} + +void Factory::Clear() +{ + mClassNameMap.clear(); + mClassHashMap.clear(); +} + +Array Factory::GetAllClasses() const +{ + Array all_classes; + all_classes.reserve(mClassNameMap.size()); + for (const ClassNameMap::value_type &c : mClassNameMap) + all_classes.push_back(c.second); + return all_classes; +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Factory.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Factory.h new file mode 100644 index 00000000000..557f38173f6 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Factory.h @@ -0,0 +1,54 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +JPH_NAMESPACE_BEGIN + +/// This class is responsible for creating instances of classes based on their name or hash and is mainly used for deserialization of saved data. +class JPH_EXPORT Factory +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Create an object + void * CreateObject(const char *inName); + + /// Find type info for a specific class by name + const RTTI * Find(const char *inName); + + /// Find type info for a specific class by hash + const RTTI * Find(uint32 inHash); + + /// Register an object with the factory. Returns false on failure. + bool Register(const RTTI *inRTTI); + + /// Register a list of objects with the factory. Returns false on failure. + bool Register(const RTTI **inRTTIs, uint inNumber); + + /// Unregisters all types + void Clear(); + + /// Get all registered classes + Array GetAllClasses() const; + + /// Singleton factory instance + static Factory * sInstance; + +private: + using ClassNameMap = UnorderedMap; + + using ClassHashMap = UnorderedMap; + + /// Map of class names to type info + ClassNameMap mClassNameMap; + + // Map of class hash to type info + ClassHashMap mClassHashMap; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/FixedSizeFreeList.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/FixedSizeFreeList.h new file mode 100644 index 00000000000..6f3839b597b --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/FixedSizeFreeList.h @@ -0,0 +1,120 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +/// Class that allows lock free creation / destruction of objects (unless a new page of objects needs to be allocated) +/// It contains a fixed pool of objects and also allows batching up a lot of objects to be destroyed +/// and doing the actual free in a single atomic operation +template +class FixedSizeFreeList : public NonCopyable +{ +private: + /// Storage type for an Object + struct ObjectStorage + { + /// The object we're storing + Object mObject; + + /// When the object is freed (or in the process of being freed as a batch) this will contain the next free object + /// When an object is in use it will contain the object's index in the free list + atomic mNextFreeObject; + }; + + static_assert(alignof(ObjectStorage) == alignof(Object), "Object not properly aligned"); + + /// Access the object storage given the object index + const ObjectStorage & GetStorage(uint32 inObjectIndex) const { return mPages[inObjectIndex >> mPageShift][inObjectIndex & mObjectMask]; } + ObjectStorage & GetStorage(uint32 inObjectIndex) { return mPages[inObjectIndex >> mPageShift][inObjectIndex & mObjectMask]; } + + /// Number of objects that we currently have in the free list / new pages +#ifdef JPH_ENABLE_ASSERTS + atomic mNumFreeObjects; +#endif // JPH_ENABLE_ASSERTS + + /// Simple counter that makes the first free object pointer update with every CAS so that we don't suffer from the ABA problem + atomic mAllocationTag; + + /// Index of first free object, the first 32 bits of an object are used to point to the next free object + atomic mFirstFreeObjectAndTag; + + /// Size (in objects) of a single page + uint32 mPageSize; + + /// Number of bits to shift an object index to the right to get the page number + uint32 mPageShift; + + /// Mask to and an object index with to get the page number + uint32 mObjectMask; + + /// Total number of pages that are usable + uint32 mNumPages; + + /// Total number of objects that have been allocated + uint32 mNumObjectsAllocated; + + /// The first free object to use when the free list is empty (may need to allocate a new page) + atomic mFirstFreeObjectInNewPage; + + /// Array of pages of objects + ObjectStorage ** mPages = nullptr; + + /// Mutex that is used to allocate a new page if the storage runs out + Mutex mPageMutex; + +public: + /// Invalid index + static const uint32 cInvalidObjectIndex = 0xffffffff; + + /// Size of an object + bookkeeping for the freelist + static const int ObjectStorageSize = sizeof(ObjectStorage); + + /// Destructor + inline ~FixedSizeFreeList(); + + /// Initialize the free list, up to inMaxObjects can be allocated + inline void Init(uint inMaxObjects, uint inPageSize); + + /// Lockless construct a new object, inParameters are passed on to the constructor + template + inline uint32 ConstructObject(Parameters &&... inParameters); + + /// Lockless destruct an object and return it to the free pool + inline void DestructObject(uint32 inObjectIndex); + + /// Lockless destruct an object and return it to the free pool + inline void DestructObject(Object *inObject); + + /// A batch of objects that can be destructed + struct Batch + { + uint32 mFirstObjectIndex = cInvalidObjectIndex; + uint32 mLastObjectIndex = cInvalidObjectIndex; + uint32 mNumObjects = 0; + }; + + /// Add a object to an existing batch to be destructed. + /// Adding objects to a batch does not destroy or modify the objects, this will merely link them + /// so that the entire batch can be returned to the free list in a single atomic operation + inline void AddObjectToBatch(Batch &ioBatch, uint32 inObjectIndex); + + /// Lockless destruct batch of objects + inline void DestructObjectBatch(Batch &ioBatch); + + /// Access an object by index. + inline Object & Get(uint32 inObjectIndex) { return GetStorage(inObjectIndex).mObject; } + + /// Access an object by index. + inline const Object & Get(uint32 inObjectIndex) const { return GetStorage(inObjectIndex).mObject; } +}; + +JPH_NAMESPACE_END + +#include "FixedSizeFreeList.inl" diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/FixedSizeFreeList.inl b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/FixedSizeFreeList.inl new file mode 100644 index 00000000000..dbaae4377b7 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/FixedSizeFreeList.inl @@ -0,0 +1,211 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +JPH_NAMESPACE_BEGIN + +template +FixedSizeFreeList::~FixedSizeFreeList() +{ + // Check if we got our Init call + if (mPages != nullptr) + { + // Ensure everything is freed before the freelist is destructed + JPH_ASSERT(mNumFreeObjects.load(memory_order_relaxed) == mNumPages * mPageSize); + + // Free memory for pages + uint32 num_pages = mNumObjectsAllocated / mPageSize; + for (uint32 page = 0; page < num_pages; ++page) + AlignedFree(mPages[page]); + Free(mPages); + } +} + +template +void FixedSizeFreeList::Init(uint inMaxObjects, uint inPageSize) +{ + // Check sanity + JPH_ASSERT(inPageSize > 0 && IsPowerOf2(inPageSize)); + JPH_ASSERT(mPages == nullptr); + + // Store configuration parameters + mNumPages = (inMaxObjects + inPageSize - 1) / inPageSize; + mPageSize = inPageSize; + mPageShift = CountTrailingZeros(inPageSize); + mObjectMask = inPageSize - 1; + JPH_IF_ENABLE_ASSERTS(mNumFreeObjects = mNumPages * inPageSize;) + + // Allocate page table + mPages = reinterpret_cast(Allocate(mNumPages * sizeof(ObjectStorage *))); + + // We didn't yet use any objects of any page + mNumObjectsAllocated = 0; + mFirstFreeObjectInNewPage = 0; + + // Start with 1 as the first tag + mAllocationTag = 1; + + // Set first free object (with tag 0) + mFirstFreeObjectAndTag = cInvalidObjectIndex; +} + +template +template +uint32 FixedSizeFreeList::ConstructObject(Parameters &&... inParameters) +{ + for (;;) + { + // Get first object from the linked list + uint64 first_free_object_and_tag = mFirstFreeObjectAndTag.load(memory_order_acquire); + uint32 first_free = uint32(first_free_object_and_tag); + if (first_free == cInvalidObjectIndex) + { + // The free list is empty, we take an object from the page that has never been used before + first_free = mFirstFreeObjectInNewPage.fetch_add(1, memory_order_relaxed); + if (first_free >= mNumObjectsAllocated) + { + // Allocate new page + lock_guard lock(mPageMutex); + while (first_free >= mNumObjectsAllocated) + { + uint32 next_page = mNumObjectsAllocated / mPageSize; + if (next_page == mNumPages) + return cInvalidObjectIndex; // Out of space! + mPages[next_page] = reinterpret_cast(AlignedAllocate(mPageSize * sizeof(ObjectStorage), max(alignof(ObjectStorage), JPH_CACHE_LINE_SIZE))); + mNumObjectsAllocated += mPageSize; + } + } + + // Allocation successful + JPH_IF_ENABLE_ASSERTS(mNumFreeObjects.fetch_sub(1, memory_order_relaxed);) + ObjectStorage &storage = GetStorage(first_free); + ::new (&storage.mObject) Object(std::forward(inParameters)...); + storage.mNextFreeObject.store(first_free, memory_order_release); + return first_free; + } + else + { + // Load next pointer + uint32 new_first_free = GetStorage(first_free).mNextFreeObject.load(memory_order_acquire); + + // Construct a new first free object tag + uint64 new_first_free_object_and_tag = uint64(new_first_free) + (uint64(mAllocationTag.fetch_add(1, memory_order_relaxed)) << 32); + + // Compare and swap + if (mFirstFreeObjectAndTag.compare_exchange_weak(first_free_object_and_tag, new_first_free_object_and_tag, memory_order_release)) + { + // Allocation successful + JPH_IF_ENABLE_ASSERTS(mNumFreeObjects.fetch_sub(1, memory_order_relaxed);) + ObjectStorage &storage = GetStorage(first_free); + ::new (&storage.mObject) Object(std::forward(inParameters)...); + storage.mNextFreeObject.store(first_free, memory_order_release); + return first_free; + } + } + } +} + +template +void FixedSizeFreeList::AddObjectToBatch(Batch &ioBatch, uint32 inObjectIndex) +{ + JPH_ASSERT(GetStorage(inObjectIndex).mNextFreeObject.load(memory_order_relaxed) == inObjectIndex, "Trying to add a object to the batch that is already in a free list"); + JPH_ASSERT(ioBatch.mNumObjects != uint32(-1), "Trying to reuse a batch that has already been freed"); + + // Link object in batch to free + if (ioBatch.mFirstObjectIndex == cInvalidObjectIndex) + ioBatch.mFirstObjectIndex = inObjectIndex; + else + GetStorage(ioBatch.mLastObjectIndex).mNextFreeObject.store(inObjectIndex, memory_order_release); + ioBatch.mLastObjectIndex = inObjectIndex; + ioBatch.mNumObjects++; +} + +template +void FixedSizeFreeList::DestructObjectBatch(Batch &ioBatch) +{ + if (ioBatch.mFirstObjectIndex != cInvalidObjectIndex) + { + // Call destructors + if constexpr (!is_trivially_destructible()) + { + uint32 object_idx = ioBatch.mFirstObjectIndex; + do + { + ObjectStorage &storage = GetStorage(object_idx); + storage.mObject.~Object(); + object_idx = storage.mNextFreeObject.load(memory_order_relaxed); + } + while (object_idx != cInvalidObjectIndex); + } + + // Add to objects free list + ObjectStorage &storage = GetStorage(ioBatch.mLastObjectIndex); + for (;;) + { + // Get first object from the list + uint64 first_free_object_and_tag = mFirstFreeObjectAndTag.load(memory_order_acquire); + uint32 first_free = uint32(first_free_object_and_tag); + + // Make it the next pointer of the last object in the batch that is to be freed + storage.mNextFreeObject.store(first_free, memory_order_release); + + // Construct a new first free object tag + uint64 new_first_free_object_and_tag = uint64(ioBatch.mFirstObjectIndex) + (uint64(mAllocationTag.fetch_add(1, memory_order_relaxed)) << 32); + + // Compare and swap + if (mFirstFreeObjectAndTag.compare_exchange_weak(first_free_object_and_tag, new_first_free_object_and_tag, memory_order_release)) + { + // Free successful + JPH_IF_ENABLE_ASSERTS(mNumFreeObjects.fetch_add(ioBatch.mNumObjects, memory_order_relaxed);) + + // Mark the batch as freed +#ifdef JPH_ENABLE_ASSERTS + ioBatch.mNumObjects = uint32(-1); +#endif + return; + } + } + } +} + +template +void FixedSizeFreeList::DestructObject(uint32 inObjectIndex) +{ + JPH_ASSERT(inObjectIndex != cInvalidObjectIndex); + + // Call destructor + ObjectStorage &storage = GetStorage(inObjectIndex); + storage.mObject.~Object(); + + // Add to object free list + for (;;) + { + // Get first object from the list + uint64 first_free_object_and_tag = mFirstFreeObjectAndTag.load(memory_order_acquire); + uint32 first_free = uint32(first_free_object_and_tag); + + // Make it the next pointer of the last object in the batch that is to be freed + storage.mNextFreeObject.store(first_free, memory_order_release); + + // Construct a new first free object tag + uint64 new_first_free_object_and_tag = uint64(inObjectIndex) + (uint64(mAllocationTag.fetch_add(1, memory_order_relaxed)) << 32); + + // Compare and swap + if (mFirstFreeObjectAndTag.compare_exchange_weak(first_free_object_and_tag, new_first_free_object_and_tag, memory_order_release)) + { + // Free successful + JPH_IF_ENABLE_ASSERTS(mNumFreeObjects.fetch_add(1, memory_order_relaxed);) + return; + } + } +} + +template +inline void FixedSizeFreeList::DestructObject(Object *inObject) +{ + uint32 index = reinterpret_cast(inObject)->mNextFreeObject.load(memory_order_relaxed); + JPH_ASSERT(index < mNumObjectsAllocated); + DestructObject(index); +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/HashCombine.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/HashCombine.h new file mode 100644 index 00000000000..372070be377 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/HashCombine.h @@ -0,0 +1,97 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +JPH_NAMESPACE_BEGIN + +/// Implements the FNV-1a hash algorithm +/// @see https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function +/// @param inData Data block of bytes +/// @param inSize Number of bytes +/// @param inSeed Seed of the hash (can be used to pass in the hash of a previous operation, otherwise leave default) +/// @return Hash +inline uint64 HashBytes(const void *inData, uint inSize, uint64 inSeed = 0xcbf29ce484222325UL) +{ + uint64 hash = inSeed; + for (const uint8 *data = reinterpret_cast(inData); data < reinterpret_cast(inData) + inSize; ++data) + { + hash = hash ^ uint64(*data); + hash = hash * 0x100000001b3UL; + } + return hash; +} + +/// A 64 bit hash function by Thomas Wang, Jan 1997 +/// See: http://web.archive.org/web/20071223173210/http://www.concentric.net/~Ttwang/tech/inthash.htm +/// @param inValue Value to hash +/// @return Hash +inline uint64 Hash64(uint64 inValue) +{ + uint64 hash = inValue; + hash = (~hash) + (hash << 21); // hash = (hash << 21) - hash - 1; + hash = hash ^ (hash >> 24); + hash = (hash + (hash << 3)) + (hash << 8); // hash * 265 + hash = hash ^ (hash >> 14); + hash = (hash + (hash << 2)) + (hash << 4); // hash * 21 + hash = hash ^ (hash >> 28); + hash = hash + (hash << 31); + return hash; +} + +/// @brief Helper function that hashes a single value into ioSeed +/// Taken from: https://stackoverflow.com/questions/2590677/how-do-i-combine-hash-values-in-c0x +template +inline void HashCombineHelper(size_t &ioSeed, const T &inValue) +{ + std::hash hasher; + ioSeed ^= hasher(inValue) + 0x9e3779b9 + (ioSeed << 6) + (ioSeed >> 2); +} + +/// Hash combiner to use a custom struct in an unordered map or set +/// +/// Usage: +/// +/// struct SomeHashKey +/// { +/// std::string key1; +/// std::string key2; +/// bool key3; +/// }; +/// +/// JPH_MAKE_HASHABLE(SomeHashKey, t.key1, t.key2, t.key3) +template +inline void HashCombine(std::size_t &ioSeed, Values... inValues) +{ + // Hash all values together using a fold expression + (HashCombineHelper(ioSeed, inValues), ...); +} + +JPH_NAMESPACE_END + +JPH_SUPPRESS_WARNING_PUSH +JPH_CLANG_SUPPRESS_WARNING("-Wc++98-compat-pedantic") + +#define JPH_MAKE_HASH_STRUCT(type, name, ...) \ + struct [[nodiscard]] name \ + { \ + std::size_t operator()(const type &t) const \ + { \ + std::size_t ret = 0; \ + ::JPH::HashCombine(ret, __VA_ARGS__); \ + return ret; \ + } \ + }; + +#define JPH_MAKE_HASHABLE(type, ...) \ + JPH_SUPPRESS_WARNING_PUSH \ + JPH_SUPPRESS_WARNINGS \ + namespace std \ + { \ + template<> \ + JPH_MAKE_HASH_STRUCT(type, hash, __VA_ARGS__) \ + } \ + JPH_SUPPRESS_WARNING_POP + +JPH_SUPPRESS_WARNING_POP diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/InsertionSort.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/InsertionSort.h new file mode 100644 index 00000000000..8cd8798be20 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/InsertionSort.h @@ -0,0 +1,58 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2022 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +JPH_NAMESPACE_BEGIN + +/// Implementation of the insertion sort algorithm. +template +inline void InsertionSort(Iterator inBegin, Iterator inEnd, Compare inCompare) +{ + // Empty arrays don't need to be sorted + if (inBegin != inEnd) + { + // Start at the second element + for (Iterator i = inBegin + 1; i != inEnd; ++i) + { + // Move this element to a temporary value + auto x = std::move(*i); + + // Check if the element goes before inBegin (we can't decrement the iterator before inBegin so this needs to be a separate branch) + if (inCompare(x, *inBegin)) + { + // Move all elements to the right to make space for x + Iterator prev; + for (Iterator j = i; j != inBegin; j = prev) + { + prev = j - 1; + *j = *prev; + } + + // Move x to the first place + *inBegin = std::move(x); + } + else + { + // Move elements to the right as long as they are bigger than x + Iterator j = i; + for (Iterator prev = j - 1; inCompare(x, *prev); j = prev, --prev) + *j = std::move(*prev); + + // Move x into place + *j = std::move(x); + } + } + } +} + +/// Implementation of insertion sort algorithm without comparator. +template +inline void InsertionSort(Iterator inBegin, Iterator inEnd) +{ + std::less<> compare; + InsertionSort(inBegin, inEnd, compare); +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/IssueReporting.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/IssueReporting.cpp new file mode 100644 index 00000000000..e4efe126b4a --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/IssueReporting.cpp @@ -0,0 +1,31 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +JPH_SUPPRESS_WARNINGS_STD_BEGIN +#include +JPH_SUPPRESS_WARNINGS_STD_END + +JPH_NAMESPACE_BEGIN + +static void DummyTrace([[maybe_unused]] const char *inFMT, ...) +{ + JPH_ASSERT(false); +}; + +TraceFunction Trace = DummyTrace; + +#ifdef JPH_ENABLE_ASSERTS + +static bool DummyAssertFailed(const char *inExpression, const char *inMessage, const char *inFile, uint inLine) +{ + return true; // Trigger breakpoint +}; + +AssertFailedFunction AssertFailed = DummyAssertFailed; + +#endif // JPH_ENABLE_ASSERTS + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/IssueReporting.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/IssueReporting.h new file mode 100644 index 00000000000..8a5dc2fa667 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/IssueReporting.h @@ -0,0 +1,38 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +JPH_NAMESPACE_BEGIN + +/// Trace function, needs to be overridden by application. This should output a line of text to the log / TTY. +using TraceFunction = void (*)(const char *inFMT, ...); +JPH_EXPORT extern TraceFunction Trace; + +// Always turn on asserts in Debug mode +#if defined(_DEBUG) && !defined(JPH_ENABLE_ASSERTS) + #define JPH_ENABLE_ASSERTS +#endif + +#ifdef JPH_ENABLE_ASSERTS + /// Function called when an assertion fails. This function should return true if a breakpoint needs to be triggered + using AssertFailedFunction = bool(*)(const char *inExpression, const char *inMessage, const char *inFile, uint inLine); + JPH_EXPORT extern AssertFailedFunction AssertFailed; + + // Helper functions to pass message on to failed function + struct AssertLastParam { }; + inline bool AssertFailedParamHelper(const char *inExpression, const char *inFile, uint inLine, AssertLastParam) { return AssertFailed(inExpression, nullptr, inFile, inLine); } + inline bool AssertFailedParamHelper(const char *inExpression, const char *inFile, uint inLine, const char *inMessage, AssertLastParam) { return AssertFailed(inExpression, inMessage, inFile, inLine); } + + /// Main assert macro, usage: JPH_ASSERT(condition, message) or JPH_ASSERT(condition) + #define JPH_ASSERT(inExpression, ...) do { if (!(inExpression) && AssertFailedParamHelper(#inExpression, __FILE__, JPH::uint(__LINE__), ##__VA_ARGS__, JPH::AssertLastParam())) JPH_BREAKPOINT; } while (false) + + #define JPH_IF_ENABLE_ASSERTS(...) __VA_ARGS__ +#else + #define JPH_ASSERT(...) ((void)0) + + #define JPH_IF_ENABLE_ASSERTS(...) +#endif // JPH_ENABLE_ASSERTS + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/JobSystem.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/JobSystem.h new file mode 100644 index 00000000000..f12f53809a2 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/JobSystem.h @@ -0,0 +1,305 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +/// A class that allows units of work (Jobs) to be scheduled across multiple threads. +/// It allows dependencies between the jobs so that the jobs form a graph. +/// +/// The pattern for using this class is: +/// +/// // Create job system +/// JobSystem *job_system = new JobSystemThreadPool(...); +/// +/// // Create some jobs +/// JobHandle second_job = job_system->CreateJob("SecondJob", Color::sRed, []() { ... }, 1); // Create a job with 1 dependency +/// JobHandle first_job = job_system->CreateJob("FirstJob", Color::sGreen, [second_job]() { ....; second_job.RemoveDependency(); }, 0); // Job can start immediately, will start second job when it's done +/// JobHandle third_job = job_system->CreateJob("ThirdJob", Color::sBlue, []() { ... }, 0); // This job can run immediately as well and can run in parallel to job 1 and 2 +/// +/// // Add the jobs to the barrier so that we can execute them while we're waiting +/// Barrier *barrier = job_system->CreateBarrier(); +/// barrier->AddJob(first_job); +/// barrier->AddJob(second_job); +/// barrier->AddJob(third_job); +/// job_system->WaitForJobs(barrier); +/// +/// // Clean up +/// job_system->DestroyBarrier(barrier); +/// delete job_system; +/// +/// Jobs are guaranteed to be started in the order that their dependency counter becomes zero (in case they're scheduled on a background thread) +/// or in the order they're added to the barrier (when dependency count is zero and when executing on the thread that calls WaitForJobs). +/// +/// If you want to implement your own job system, inherit from JobSystem and implement: +/// +/// * JobSystem::GetMaxConcurrency - This should return the maximum number of jobs that can run in parallel. +/// * JobSystem::CreateJob - This should create a Job object and return it to the caller. +/// * JobSystem::FreeJob - This should free the memory associated with the job object. It is called by the Job destructor when it is Release()-ed for the last time. +/// * JobSystem::QueueJob/QueueJobs - These should store the job pointer in an internal queue to run immediately (dependencies are tracked internally, this function is called when the job can run). +/// The Job objects are reference counted and are guaranteed to stay alive during the QueueJob(s) call. If you store the job in your own data structure you need to call AddRef() to take a reference. +/// After the job has been executed you need to call Release() to release the reference. Make sure you no longer dereference the job pointer after calling Release(). +/// +/// JobSystem::Barrier is used to track the completion of a set of jobs. Jobs will be created by other jobs and added to the barrier while it is being waited on. This means that you cannot +/// create a dependency graph beforehand as the graph changes while jobs are running. Implement the following functions: +/// +/// * Barrier::AddJob/AddJobs - Add a job to the barrier, any call to WaitForJobs will now also wait for this job to complete. +/// If you store the job in a data structure in the Barrier you need to call AddRef() on the job to keep it alive and Release() after you're done with it. +/// * Barrier::OnJobFinished - This function is called when a job has finished executing, you can use this to track completion and remove the job from the list of jobs to wait on. +/// +/// The functions on JobSystem that need to be implemented to support barriers are: +/// +/// * JobSystem::CreateBarrier - Create a new barrier. +/// * JobSystem::DestroyBarrier - Destroy a barrier. +/// * JobSystem::WaitForJobs - This is the main function that is used to wait for all jobs that have been added to a Barrier. WaitForJobs can execute jobs that have +/// been added to the barrier while waiting. It is not wise to execute other jobs that touch physics structures as this can cause race conditions and deadlocks. Please keep in mind that the barrier is +/// only intended to wait on the completion of the Jolt jobs added to it, if you scheduled any jobs in your engine's job system to execute the Jolt jobs as part of QueueJob/QueueJobs, you might still need +/// to wait for these in this function after the barrier is finished waiting. +/// +/// An example implementation is JobSystemThreadPool. If you don't want to write the Barrier class you can also inherit from JobSystemWithBarrier. +class JPH_EXPORT JobSystem : public NonCopyable +{ +protected: + class Job; + +public: + JPH_OVERRIDE_NEW_DELETE + + /// A job handle contains a reference to a job. The job will be deleted as soon as there are no JobHandles. + /// referring to the job and when it is not in the job queue / being processed. + class JobHandle : private Ref + { + public: + /// Constructor + inline JobHandle() = default; + inline JobHandle(const JobHandle &inHandle) = default; + inline JobHandle(JobHandle &&inHandle) noexcept : Ref(std::move(inHandle)) { } + + /// Constructor, only to be used by JobSystem + inline explicit JobHandle(Job *inJob) : Ref(inJob) { } + + /// Assignment + inline JobHandle & operator = (const JobHandle &inHandle) = default; + inline JobHandle & operator = (JobHandle &&inHandle) noexcept = default; + + /// Check if this handle contains a job + inline bool IsValid() const { return GetPtr() != nullptr; } + + /// Check if this job has finished executing + inline bool IsDone() const { return GetPtr() != nullptr && GetPtr()->IsDone(); } + + /// Add to the dependency counter. + inline void AddDependency(int inCount = 1) const { GetPtr()->AddDependency(inCount); } + + /// Remove from the dependency counter. Job will start whenever the dependency counter reaches zero + /// and if it does it is no longer valid to call the AddDependency/RemoveDependency functions. + inline void RemoveDependency(int inCount = 1) const { GetPtr()->RemoveDependencyAndQueue(inCount); } + + /// Remove a dependency from a batch of jobs at once, this can be more efficient than removing them one by one as it requires less locking + static inline void sRemoveDependencies(const JobHandle *inHandles, uint inNumHandles, int inCount = 1); + + /// Helper function to remove dependencies on a static array of job handles + template + static inline void sRemoveDependencies(StaticArray &inHandles, int inCount = 1) + { + sRemoveDependencies(inHandles.data(), inHandles.size(), inCount); + } + + /// Inherit the GetPtr function, only to be used by the JobSystem + using Ref::GetPtr; + }; + + /// A job barrier keeps track of a number of jobs and allows waiting until they are all completed. + class Barrier : public NonCopyable + { + public: + JPH_OVERRIDE_NEW_DELETE + + /// Add a job to this barrier + /// Note that jobs can keep being added to the barrier while waiting for the barrier + virtual void AddJob(const JobHandle &inJob) = 0; + + /// Add multiple jobs to this barrier + /// Note that jobs can keep being added to the barrier while waiting for the barrier + virtual void AddJobs(const JobHandle *inHandles, uint inNumHandles) = 0; + + protected: + /// Job needs to be able to call OnJobFinished + friend class Job; + + /// Destructor, you should call JobSystem::DestroyBarrier instead of destructing this object directly + virtual ~Barrier() = default; + + /// Called by a Job to mark that it is finished + virtual void OnJobFinished(Job *inJob) = 0; + }; + + /// Main function of the job + using JobFunction = function; + + /// Destructor + virtual ~JobSystem() = default; + + /// Get maximum number of concurrently executing jobs + virtual int GetMaxConcurrency() const = 0; + + /// Create a new job, the job is started immediately if inNumDependencies == 0 otherwise it starts when + /// RemoveDependency causes the dependency counter to reach 0. + virtual JobHandle CreateJob(const char *inName, ColorArg inColor, const JobFunction &inJobFunction, uint32 inNumDependencies = 0) = 0; + + /// Create a new barrier, used to wait on jobs + virtual Barrier * CreateBarrier() = 0; + + /// Destroy a barrier when it is no longer used. The barrier should be empty at this point. + virtual void DestroyBarrier(Barrier *inBarrier) = 0; + + /// Wait for a set of jobs to be finished, note that only 1 thread can be waiting on a barrier at a time + virtual void WaitForJobs(Barrier *inBarrier) = 0; + +protected: + /// A class that contains information for a single unit of work + class Job + { + public: + JPH_OVERRIDE_NEW_DELETE + + /// Constructor + Job([[maybe_unused]] const char *inJobName, [[maybe_unused]] ColorArg inColor, JobSystem *inJobSystem, const JobFunction &inJobFunction, uint32 inNumDependencies) : + #if defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED) + mJobName(inJobName), + mColor(inColor), + #endif // defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED) + mJobSystem(inJobSystem), + mJobFunction(inJobFunction), + mNumDependencies(inNumDependencies) + { + } + + /// Get the jobs system to which this job belongs + inline JobSystem * GetJobSystem() { return mJobSystem; } + + /// Add or release a reference to this object + inline void AddRef() + { + // Adding a reference can use relaxed memory ordering + mReferenceCount.fetch_add(1, memory_order_relaxed); + } + inline void Release() + { + // Releasing a reference must use release semantics... + if (mReferenceCount.fetch_sub(1, memory_order_release) == 1) + { + // ... so that we can use acquire to ensure that we see any updates from other threads that released a ref before freeing the job + atomic_thread_fence(memory_order_acquire); + mJobSystem->FreeJob(this); + } + } + + /// Add to the dependency counter. + inline void AddDependency(int inCount); + + /// Remove from the dependency counter. Returns true whenever the dependency counter reaches zero + /// and if it does it is no longer valid to call the AddDependency/RemoveDependency functions. + inline bool RemoveDependency(int inCount); + + /// Remove from the dependency counter. Job will be queued whenever the dependency counter reaches zero + /// and if it does it is no longer valid to call the AddDependency/RemoveDependency functions. + inline void RemoveDependencyAndQueue(int inCount); + + /// Set the job barrier that this job belongs to and returns false if this was not possible because the job already finished + inline bool SetBarrier(Barrier *inBarrier) + { + intptr_t barrier = 0; + if (mBarrier.compare_exchange_strong(barrier, reinterpret_cast(inBarrier), memory_order_relaxed)) + return true; + JPH_ASSERT(barrier == cBarrierDoneState, "A job can only belong to 1 barrier"); + return false; + } + + /// Run the job function, returns the number of dependencies that this job still has or cExecutingState or cDoneState + inline uint32 Execute() + { + // Transition job to executing state + uint32 state = 0; // We can only start running with a dependency counter of 0 + if (!mNumDependencies.compare_exchange_strong(state, cExecutingState, memory_order_acquire)) + return state; // state is updated by compare_exchange_strong to the current value + + // Run the job function + { + JPH_PROFILE(mJobName, mColor.GetUInt32()); + mJobFunction(); + } + + // Fetch the barrier pointer and exchange it for the done state, so we're sure that no barrier gets set after we want to call the callback + intptr_t barrier = mBarrier.load(memory_order_relaxed); + for (;;) + { + if (mBarrier.compare_exchange_weak(barrier, cBarrierDoneState, memory_order_relaxed)) + break; + } + JPH_ASSERT(barrier != cBarrierDoneState); + + // Mark job as done + state = cExecutingState; + mNumDependencies.compare_exchange_strong(state, cDoneState, memory_order_relaxed); + JPH_ASSERT(state == cExecutingState); + + // Notify the barrier after we've changed the job to the done state so that any thread reading the state after receiving the callback will see that the job has finished + if (barrier != 0) + reinterpret_cast(barrier)->OnJobFinished(this); + + return cDoneState; + } + + /// Test if the job can be executed + inline bool CanBeExecuted() const { return mNumDependencies.load(memory_order_relaxed) == 0; } + + /// Test if the job finished executing + inline bool IsDone() const { return mNumDependencies.load(memory_order_relaxed) == cDoneState; } + + #if defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED) + /// Get the name of the job + const char * GetName() const { return mJobName; } + #endif // defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED) + + static constexpr uint32 cExecutingState = 0xe0e0e0e0; ///< Value of mNumDependencies when job is executing + static constexpr uint32 cDoneState = 0xd0d0d0d0; ///< Value of mNumDependencies when job is done executing + + static constexpr intptr_t cBarrierDoneState = ~intptr_t(0); ///< Value to use when the barrier has been triggered + +private: + #if defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED) + const char * mJobName; ///< Name of the job + Color mColor; ///< Color of the job in the profiler + #endif // defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED) + JobSystem * mJobSystem; ///< The job system we belong to + atomic mBarrier = 0; ///< Barrier that this job is associated with (is a Barrier pointer) + JobFunction mJobFunction; ///< Main job function + atomic mReferenceCount = 0; ///< Amount of JobHandles pointing to this job + atomic mNumDependencies; ///< Amount of jobs that need to complete before this job can run + }; + + /// Adds a job to the job queue + virtual void QueueJob(Job *inJob) = 0; + + /// Adds a number of jobs at once to the job queue + virtual void QueueJobs(Job **inJobs, uint inNumJobs) = 0; + + /// Frees a job + virtual void FreeJob(Job *inJob) = 0; +}; + +using JobHandle = JobSystem::JobHandle; + +JPH_NAMESPACE_END + +#include "JobSystem.inl" diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/JobSystem.inl b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/JobSystem.inl new file mode 100644 index 00000000000..bee4f13b505 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/JobSystem.inl @@ -0,0 +1,56 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +JPH_NAMESPACE_BEGIN + +void JobSystem::Job::AddDependency(int inCount) +{ + JPH_IF_ENABLE_ASSERTS(uint32 old_value =) mNumDependencies.fetch_add(inCount, memory_order_relaxed); + JPH_ASSERT(old_value > 0 && old_value != cExecutingState && old_value != cDoneState, "Job is queued, running or done, it is not allowed to add a dependency to a running job"); +} + +bool JobSystem::Job::RemoveDependency(int inCount) +{ + uint32 old_value = mNumDependencies.fetch_sub(inCount, memory_order_release); + JPH_ASSERT(old_value != cExecutingState && old_value != cDoneState, "Job is running or done, it is not allowed to add a dependency to a running job"); + uint32 new_value = old_value - inCount; + JPH_ASSERT(old_value > new_value, "Test wrap around, this is a logic error"); + return new_value == 0; +} + +void JobSystem::Job::RemoveDependencyAndQueue(int inCount) +{ + if (RemoveDependency(inCount)) + mJobSystem->QueueJob(this); +} + +void JobSystem::JobHandle::sRemoveDependencies(const JobHandle *inHandles, uint inNumHandles, int inCount) +{ + JPH_PROFILE_FUNCTION(); + + JPH_ASSERT(inNumHandles > 0); + + // Get the job system, all jobs should be part of the same job system + JobSystem *job_system = inHandles->GetPtr()->GetJobSystem(); + + // Allocate a buffer to store the jobs that need to be queued + Job **jobs_to_queue = (Job **)JPH_STACK_ALLOC(inNumHandles * sizeof(Job *)); + Job **next_job = jobs_to_queue; + + // Remove the dependencies on all jobs + for (const JobHandle *handle = inHandles, *handle_end = inHandles + inNumHandles; handle < handle_end; ++handle) + { + Job *job = handle->GetPtr(); + JPH_ASSERT(job->GetJobSystem() == job_system); // All jobs should belong to the same job system + if (job->RemoveDependency(inCount)) + *(next_job++) = job; + } + + // If any jobs need to be scheduled, schedule them as a batch + uint num_jobs_to_queue = uint(next_job - jobs_to_queue); + if (num_jobs_to_queue != 0) + job_system->QueueJobs(jobs_to_queue, num_jobs_to_queue); +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/JobSystemSingleThreaded.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/JobSystemSingleThreaded.cpp new file mode 100644 index 00000000000..5f09d1a04ce --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/JobSystemSingleThreaded.cpp @@ -0,0 +1,65 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2023 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include + +JPH_NAMESPACE_BEGIN + +void JobSystemSingleThreaded::Init(uint inMaxJobs) +{ + mJobs.Init(inMaxJobs, inMaxJobs); +} + +JobHandle JobSystemSingleThreaded::CreateJob(const char *inJobName, ColorArg inColor, const JobFunction &inJobFunction, uint32 inNumDependencies) +{ + // Construct an object + uint32 index = mJobs.ConstructObject(inJobName, inColor, this, inJobFunction, inNumDependencies); + JPH_ASSERT(index != AvailableJobs::cInvalidObjectIndex); + Job *job = &mJobs.Get(index); + + // Construct handle to keep a reference, the job is queued below and will immediately complete + JobHandle handle(job); + + // If there are no dependencies, queue the job now + if (inNumDependencies == 0) + QueueJob(job); + + // Return the handle + return handle; +} + +void JobSystemSingleThreaded::FreeJob(Job *inJob) +{ + mJobs.DestructObject(inJob); +} + +void JobSystemSingleThreaded::QueueJob(Job *inJob) +{ + inJob->Execute(); +} + +void JobSystemSingleThreaded::QueueJobs(Job **inJobs, uint inNumJobs) +{ + for (uint i = 0; i < inNumJobs; ++i) + QueueJob(inJobs[i]); +} + +JobSystem::Barrier *JobSystemSingleThreaded::CreateBarrier() +{ + return &mDummyBarrier; +} + +void JobSystemSingleThreaded::DestroyBarrier(Barrier *inBarrier) +{ + // There's nothing to do here, the barrier is just a dummy +} + +void JobSystemSingleThreaded::WaitForJobs(Barrier *inBarrier) +{ + // There's nothing to do here, the barrier is just a dummy, we just execute the jobs immediately +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/JobSystemSingleThreaded.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/JobSystemSingleThreaded.h new file mode 100644 index 00000000000..4db599b3135 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/JobSystemSingleThreaded.h @@ -0,0 +1,62 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2023 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +JPH_NAMESPACE_BEGIN + +/// Implementation of a JobSystem without threads, runs jobs as soon as they are added +class JPH_EXPORT JobSystemSingleThreaded final : public JobSystem +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Constructor + JobSystemSingleThreaded() = default; + explicit JobSystemSingleThreaded(uint inMaxJobs) { Init(inMaxJobs); } + + /// Initialize the job system + /// @param inMaxJobs Max number of jobs that can be allocated at any time + void Init(uint inMaxJobs); + + // See JobSystem + virtual int GetMaxConcurrency() const override { return 1; } + virtual JobHandle CreateJob(const char *inName, ColorArg inColor, const JobFunction &inJobFunction, uint32 inNumDependencies = 0) override; + virtual Barrier * CreateBarrier() override; + virtual void DestroyBarrier(Barrier *inBarrier) override; + virtual void WaitForJobs(Barrier *inBarrier) override; + +protected: + // Dummy implementation of Barrier, all jobs are executed immediately + class BarrierImpl : public Barrier + { + public: + JPH_OVERRIDE_NEW_DELETE + + // See Barrier + virtual void AddJob(const JobHandle &inJob) override { /* We don't need to track jobs */ } + virtual void AddJobs(const JobHandle *inHandles, uint inNumHandles) override { /* We don't need to track jobs */ } + + protected: + /// Called by a Job to mark that it is finished + virtual void OnJobFinished(Job *inJob) override { /* We don't need to track jobs */ } + }; + + // See JobSystem + virtual void QueueJob(Job *inJob) override; + virtual void QueueJobs(Job **inJobs, uint inNumJobs) override; + virtual void FreeJob(Job *inJob) override; + + /// Shared barrier since the barrier implementation does nothing + BarrierImpl mDummyBarrier; + + /// Array of jobs (fixed size) + using AvailableJobs = FixedSizeFreeList; + AvailableJobs mJobs; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/JobSystemThreadPool.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/JobSystemThreadPool.cpp new file mode 100644 index 00000000000..64beb5b7f24 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/JobSystemThreadPool.cpp @@ -0,0 +1,354 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include + +#ifdef JPH_PLATFORM_WINDOWS + JPH_SUPPRESS_WARNING_PUSH + JPH_MSVC_SUPPRESS_WARNING(5039) // winbase.h(13179): warning C5039: 'TpSetCallbackCleanupGroup': pointer or reference to potentially throwing function passed to 'extern "C"' function under -EHc. Undefined behavior may occur if this function throws an exception. + #define WIN32_LEAN_AND_MEAN +#ifndef JPH_COMPILER_MINGW + #include +#else + #include +#endif + + JPH_SUPPRESS_WARNING_POP +#endif +#ifdef JPH_PLATFORM_LINUX + #include +#endif + +JPH_NAMESPACE_BEGIN + +void JobSystemThreadPool::Init(uint inMaxJobs, uint inMaxBarriers, int inNumThreads) +{ + JobSystemWithBarrier::Init(inMaxBarriers); + + // Init freelist of jobs + mJobs.Init(inMaxJobs, inMaxJobs); + + // Init queue + for (atomic &j : mQueue) + j = nullptr; + + // Start the worker threads + StartThreads(inNumThreads); +} + +JobSystemThreadPool::JobSystemThreadPool(uint inMaxJobs, uint inMaxBarriers, int inNumThreads) +{ + Init(inMaxJobs, inMaxBarriers, inNumThreads); +} + +void JobSystemThreadPool::StartThreads(int inNumThreads) +{ + // Auto detect number of threads + if (inNumThreads < 0) + inNumThreads = thread::hardware_concurrency() - 1; + + // If no threads are requested we're done + if (inNumThreads == 0) + return; + + // Don't quit the threads + mQuit = false; + + // Allocate heads + mHeads = reinterpret_cast *>(Allocate(sizeof(atomic) * inNumThreads)); + for (int i = 0; i < inNumThreads; ++i) + mHeads[i] = 0; + + // Start running threads + JPH_ASSERT(mThreads.empty()); + mThreads.reserve(inNumThreads); + for (int i = 0; i < inNumThreads; ++i) + mThreads.emplace_back([this, i] { ThreadMain(i); }); +} + +JobSystemThreadPool::~JobSystemThreadPool() +{ + // Stop all worker threads + StopThreads(); +} + +void JobSystemThreadPool::StopThreads() +{ + if (mThreads.empty()) + return; + + // Signal threads that we want to stop and wake them up + mQuit = true; + mSemaphore.Release((uint)mThreads.size()); + + // Wait for all threads to finish + for (thread &t : mThreads) + if (t.joinable()) + t.join(); + + // Delete all threads + mThreads.clear(); + + // Ensure that there are no lingering jobs in the queue + for (uint head = 0; head != mTail; ++head) + { + // Fetch job + Job *job_ptr = mQueue[head & (cQueueLength - 1)].exchange(nullptr); + if (job_ptr != nullptr) + { + // And execute it + job_ptr->Execute(); + job_ptr->Release(); + } + } + + // Destroy heads and reset tail + Free(mHeads); + mHeads = nullptr; + mTail = 0; +} + +JobHandle JobSystemThreadPool::CreateJob(const char *inJobName, ColorArg inColor, const JobFunction &inJobFunction, uint32 inNumDependencies) +{ + JPH_PROFILE_FUNCTION(); + + // Loop until we can get a job from the free list + uint32 index; + for (;;) + { + index = mJobs.ConstructObject(inJobName, inColor, this, inJobFunction, inNumDependencies); + if (index != AvailableJobs::cInvalidObjectIndex) + break; + JPH_ASSERT(false, "No jobs available!"); + std::this_thread::sleep_for(std::chrono::microseconds(100)); + } + Job *job = &mJobs.Get(index); + + // Construct handle to keep a reference, the job is queued below and may immediately complete + JobHandle handle(job); + + // If there are no dependencies, queue the job now + if (inNumDependencies == 0) + QueueJob(job); + + // Return the handle + return handle; +} + +void JobSystemThreadPool::FreeJob(Job *inJob) +{ + mJobs.DestructObject(inJob); +} + +uint JobSystemThreadPool::GetHead() const +{ + // Find the minimal value across all threads + uint head = mTail; + for (size_t i = 0; i < mThreads.size(); ++i) + head = min(head, mHeads[i].load()); + return head; +} + +void JobSystemThreadPool::QueueJobInternal(Job *inJob) +{ + // Add reference to job because we're adding the job to the queue + inJob->AddRef(); + + // Need to read head first because otherwise the tail can already have passed the head + // We read the head outside of the loop since it involves iterating over all threads and we only need to update + // it if there's not enough space in the queue. + uint head = GetHead(); + + for (;;) + { + // Check if there's space in the queue + uint old_value = mTail; + if (old_value - head >= cQueueLength) + { + // We calculated the head outside of the loop, update head (and we also need to update tail to prevent it from passing head) + head = GetHead(); + old_value = mTail; + + // Second check if there's space in the queue + if (old_value - head >= cQueueLength) + { + // Wake up all threads in order to ensure that they can clear any nullptrs they may not have processed yet + mSemaphore.Release((uint)mThreads.size()); + + // Sleep a little (we have to wait for other threads to update their head pointer in order for us to be able to continue) + std::this_thread::sleep_for(std::chrono::microseconds(100)); + continue; + } + } + + // Write the job pointer if the slot is empty + Job *expected_job = nullptr; + bool success = mQueue[old_value & (cQueueLength - 1)].compare_exchange_strong(expected_job, inJob); + + // Regardless of who wrote the slot, we will update the tail (if the successful thread got scheduled out + // after writing the pointer we still want to be able to continue) + mTail.compare_exchange_strong(old_value, old_value + 1); + + // If we successfully added our job we're done + if (success) + break; + } +} + +void JobSystemThreadPool::QueueJob(Job *inJob) +{ + JPH_PROFILE_FUNCTION(); + + // If we have no worker threads, we can't queue the job either. We assume in this case that the job will be added to a barrier and that the barrier will execute the job when it's Wait() function is called. + if (mThreads.empty()) + return; + + // Queue the job + QueueJobInternal(inJob); + + // Wake up thread + mSemaphore.Release(); +} + +void JobSystemThreadPool::QueueJobs(Job **inJobs, uint inNumJobs) +{ + JPH_PROFILE_FUNCTION(); + + JPH_ASSERT(inNumJobs > 0); + + // If we have no worker threads, we can't queue the job either. We assume in this case that the job will be added to a barrier and that the barrier will execute the job when it's Wait() function is called. + if (mThreads.empty()) + return; + + // Queue all jobs + for (Job **job = inJobs, **job_end = inJobs + inNumJobs; job < job_end; ++job) + QueueJobInternal(*job); + + // Wake up threads + mSemaphore.Release(min(inNumJobs, (uint)mThreads.size())); +} + +#if defined(JPH_PLATFORM_WINDOWS) + +#if !defined(JPH_COMPILER_MINGW) // MinGW doesn't support __try/__except) + // Sets the current thread name in MSVC debugger + static void RaiseThreadNameException(const char *inName) + { + #pragma pack(push, 8) + + struct THREADNAME_INFO + { + DWORD dwType; // Must be 0x1000. + LPCSTR szName; // Pointer to name (in user addr space). + DWORD dwThreadID; // Thread ID (-1=caller thread). + DWORD dwFlags; // Reserved for future use, must be zero. + }; + + #pragma pack(pop) + + THREADNAME_INFO info; + info.dwType = 0x1000; + info.szName = inName; + info.dwThreadID = (DWORD)-1; + info.dwFlags = 0; + + __try + { + RaiseException(0x406D1388, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR *)&info); + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + } + } +#endif // !JPH_COMPILER_MINGW + + static void SetThreadName(const char* inName) + { + JPH_SUPPRESS_WARNING_PUSH + + // Suppress casting warning, it's fine here as GetProcAddress doesn't really return a FARPROC + JPH_CLANG_SUPPRESS_WARNING("-Wcast-function-type") // error : cast from 'FARPROC' (aka 'long long (*)()') to 'SetThreadDescriptionFunc' (aka 'long (*)(void *, const wchar_t *)') converts to incompatible function type + JPH_CLANG_SUPPRESS_WARNING("-Wcast-function-type-strict") // error : cast from 'FARPROC' (aka 'long long (*)()') to 'SetThreadDescriptionFunc' (aka 'long (*)(void *, const wchar_t *)') converts to incompatible function type + JPH_MSVC_SUPPRESS_WARNING(4191) // reinterpret_cast' : unsafe conversion from 'FARPROC' to 'SetThreadDescriptionFunc'. Calling this function through the result pointer may cause your program to fail + + using SetThreadDescriptionFunc = HRESULT(WINAPI*)(HANDLE hThread, PCWSTR lpThreadDescription); + static SetThreadDescriptionFunc SetThreadDescription = reinterpret_cast(GetProcAddress(GetModuleHandleW(L"Kernel32.dll"), "SetThreadDescription")); + + JPH_SUPPRESS_WARNING_POP + + if (SetThreadDescription) + { + wchar_t name_buffer[64] = { 0 }; + if (MultiByteToWideChar(CP_UTF8, 0, inName, -1, name_buffer, sizeof(name_buffer) / sizeof(wchar_t) - 1) == 0) + return; + + SetThreadDescription(GetCurrentThread(), name_buffer); + } +#if !defined(JPH_COMPILER_MINGW) + else if (IsDebuggerPresent()) + RaiseThreadNameException(inName); +#endif // !JPH_COMPILER_MINGW + } +#elif defined(JPH_PLATFORM_LINUX) + static void SetThreadName(const char *inName) + { + JPH_ASSERT(strlen(inName) < 16); // String will be truncated if it is longer + prctl(PR_SET_NAME, inName, 0, 0, 0); + } +#endif // JPH_PLATFORM_LINUX + +void JobSystemThreadPool::ThreadMain(int inThreadIndex) +{ + // Name the thread + char name[64]; + snprintf(name, sizeof(name), "Worker %d", int(inThreadIndex + 1)); + +#if defined(JPH_PLATFORM_WINDOWS) || defined(JPH_PLATFORM_LINUX) + SetThreadName(name); +#endif // JPH_PLATFORM_WINDOWS && !JPH_COMPILER_MINGW + + // Enable floating point exceptions + FPExceptionsEnable enable_exceptions; + JPH_UNUSED(enable_exceptions); + + JPH_PROFILE_THREAD_START(name); + + atomic &head = mHeads[inThreadIndex]; + + while (!mQuit) + { + // Wait for jobs + mSemaphore.Acquire(); + + { + JPH_PROFILE("Executing Jobs"); + + // Loop over the queue + while (head != mTail) + { + // Exchange any job pointer we find with a nullptr + atomic &job = mQueue[head & (cQueueLength - 1)]; + if (job.load() != nullptr) + { + Job *job_ptr = job.exchange(nullptr); + if (job_ptr != nullptr) + { + // And execute it + job_ptr->Execute(); + job_ptr->Release(); + } + } + head++; + } + } + } + + JPH_PROFILE_THREAD_END(); +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/JobSystemThreadPool.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/JobSystemThreadPool.h new file mode 100644 index 00000000000..05e5ae9ba51 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/JobSystemThreadPool.h @@ -0,0 +1,92 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include + +JPH_SUPPRESS_WARNINGS_STD_BEGIN +#include +JPH_SUPPRESS_WARNINGS_STD_END + +JPH_NAMESPACE_BEGIN + +// Things we're using from STL +using std::thread; + +/// Implementation of a JobSystem using a thread pool +/// +/// Note that this is considered an example implementation. It is expected that when you integrate +/// the physics engine into your own project that you'll provide your own implementation of the +/// JobSystem built on top of whatever job system your project uses. +class JPH_EXPORT JobSystemThreadPool final : public JobSystemWithBarrier +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Creates a thread pool. + /// @see JobSystemThreadPool::Init + JobSystemThreadPool(uint inMaxJobs, uint inMaxBarriers, int inNumThreads = -1); + JobSystemThreadPool() = default; + virtual ~JobSystemThreadPool() override; + + /// Initialize the thread pool + /// @param inMaxJobs Max number of jobs that can be allocated at any time + /// @param inMaxBarriers Max number of barriers that can be allocated at any time + /// @param inNumThreads Number of threads to start (the number of concurrent jobs is 1 more because the main thread will also run jobs while waiting for a barrier to complete). Use -1 to auto detect the amount of CPU's. + void Init(uint inMaxJobs, uint inMaxBarriers, int inNumThreads = -1); + + // See JobSystem + virtual int GetMaxConcurrency() const override { return int(mThreads.size()) + 1; } + virtual JobHandle CreateJob(const char *inName, ColorArg inColor, const JobFunction &inJobFunction, uint32 inNumDependencies = 0) override; + + /// Change the max concurrency after initialization + void SetNumThreads(int inNumThreads) { StopThreads(); StartThreads(inNumThreads); } + +protected: + // See JobSystem + virtual void QueueJob(Job *inJob) override; + virtual void QueueJobs(Job **inJobs, uint inNumJobs) override; + virtual void FreeJob(Job *inJob) override; + +private: + /// Start/stop the worker threads + void StartThreads(int inNumThreads); + void StopThreads(); + + /// Entry point for a thread + void ThreadMain(int inThreadIndex); + + /// Get the head of the thread that has processed the least amount of jobs + inline uint GetHead() const; + + /// Internal helper function to queue a job + inline void QueueJobInternal(Job *inJob); + + /// Array of jobs (fixed size) + using AvailableJobs = FixedSizeFreeList; + AvailableJobs mJobs; + + /// Threads running jobs + Array mThreads; + + // The job queue + static constexpr uint32 cQueueLength = 1024; + static_assert(IsPowerOf2(cQueueLength)); // We do bit operations and require queue length to be a power of 2 + atomic mQueue[cQueueLength]; + + // Head and tail of the queue, do this value modulo cQueueLength - 1 to get the element in the mQueue array + atomic * mHeads = nullptr; ///< Per executing thread the head of the current queue + alignas(JPH_CACHE_LINE_SIZE) atomic mTail = 0; ///< Tail (write end) of the queue + + // Semaphore used to signal worker threads that there is new work + Semaphore mSemaphore; + + /// Boolean to indicate that we want to stop the job system + atomic mQuit = false; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/JobSystemWithBarrier.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/JobSystemWithBarrier.cpp new file mode 100644 index 00000000000..eaaabdb0997 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/JobSystemWithBarrier.cpp @@ -0,0 +1,227 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2023 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include + +JPH_SUPPRESS_WARNINGS_STD_BEGIN +#include +JPH_SUPPRESS_WARNINGS_STD_END + +JPH_NAMESPACE_BEGIN + +JobSystemWithBarrier::BarrierImpl::BarrierImpl() +{ + for (atomic &j : mJobs) + j = nullptr; +} + +JobSystemWithBarrier::BarrierImpl::~BarrierImpl() +{ + JPH_ASSERT(IsEmpty()); +} + +void JobSystemWithBarrier::BarrierImpl::AddJob(const JobHandle &inJob) +{ + JPH_PROFILE_FUNCTION(); + + bool release_semaphore = false; + + // Set the barrier on the job, this returns true if the barrier was successfully set (otherwise the job is already done and we don't need to add it to our list) + Job *job = inJob.GetPtr(); + if (job->SetBarrier(this)) + { + // If the job can be executed we want to release the semaphore an extra time to allow the waiting thread to start executing it + mNumToAcquire++; + if (job->CanBeExecuted()) + { + release_semaphore = true; + mNumToAcquire++; + } + + // Add the job to our job list + job->AddRef(); + uint write_index = mJobWriteIndex++; + while (write_index - mJobReadIndex >= cMaxJobs) + { + JPH_ASSERT(false, "Barrier full, stalling!"); + std::this_thread::sleep_for(std::chrono::microseconds(100)); + } + mJobs[write_index & (cMaxJobs - 1)] = job; + } + + // Notify waiting thread that a new executable job is available + if (release_semaphore) + mSemaphore.Release(); +} + +void JobSystemWithBarrier::BarrierImpl::AddJobs(const JobHandle *inHandles, uint inNumHandles) +{ + JPH_PROFILE_FUNCTION(); + + bool release_semaphore = false; + + for (const JobHandle *handle = inHandles, *handles_end = inHandles + inNumHandles; handle < handles_end; ++handle) + { + // Set the barrier on the job, this returns true if the barrier was successfully set (otherwise the job is already done and we don't need to add it to our list) + Job *job = handle->GetPtr(); + if (job->SetBarrier(this)) + { + // If the job can be executed we want to release the semaphore an extra time to allow the waiting thread to start executing it + mNumToAcquire++; + if (!release_semaphore && job->CanBeExecuted()) + { + release_semaphore = true; + mNumToAcquire++; + } + + // Add the job to our job list + job->AddRef(); + uint write_index = mJobWriteIndex++; + while (write_index - mJobReadIndex >= cMaxJobs) + { + JPH_ASSERT(false, "Barrier full, stalling!"); + std::this_thread::sleep_for(std::chrono::microseconds(100)); + } + mJobs[write_index & (cMaxJobs - 1)] = job; + } + } + + // Notify waiting thread that a new executable job is available + if (release_semaphore) + mSemaphore.Release(); +} + +void JobSystemWithBarrier::BarrierImpl::OnJobFinished(Job *inJob) +{ + JPH_PROFILE_FUNCTION(); + + mSemaphore.Release(); +} + +void JobSystemWithBarrier::BarrierImpl::Wait() +{ + while (mNumToAcquire > 0) + { + { + JPH_PROFILE("Execute Jobs"); + + // Go through all jobs + bool has_executed; + do + { + has_executed = false; + + // Loop through the jobs and erase jobs from the beginning of the list that are done + while (mJobReadIndex < mJobWriteIndex) + { + atomic &job = mJobs[mJobReadIndex & (cMaxJobs - 1)]; + Job *job_ptr = job.load(); + if (job_ptr == nullptr || !job_ptr->IsDone()) + break; + + // Job is finished, release it + job_ptr->Release(); + job = nullptr; + ++mJobReadIndex; + } + + // Loop through the jobs and execute the first executable job + for (uint index = mJobReadIndex; index < mJobWriteIndex; ++index) + { + const atomic &job = mJobs[index & (cMaxJobs - 1)]; + Job *job_ptr = job.load(); + if (job_ptr != nullptr && job_ptr->CanBeExecuted()) + { + // This will only execute the job if it has not already executed + job_ptr->Execute(); + has_executed = true; + break; + } + } + + } while (has_executed); + } + + // Wait for another thread to wake us when either there is more work to do or when all jobs have completed + int num_to_acquire = max(1, mSemaphore.GetValue()); // When there have been multiple releases, we acquire them all at the same time to avoid needlessly spinning on executing jobs + mSemaphore.Acquire(num_to_acquire); + mNumToAcquire -= num_to_acquire; + } + + // All jobs should be done now, release them + while (mJobReadIndex < mJobWriteIndex) + { + atomic &job = mJobs[mJobReadIndex & (cMaxJobs - 1)]; + Job *job_ptr = job.load(); + JPH_ASSERT(job_ptr != nullptr && job_ptr->IsDone()); + job_ptr->Release(); + job = nullptr; + ++mJobReadIndex; + } +} + +void JobSystemWithBarrier::Init(uint inMaxBarriers) +{ + JPH_ASSERT(mBarriers == nullptr); // Already initialized? + + // Init freelist of barriers + mMaxBarriers = inMaxBarriers; + mBarriers = new BarrierImpl [inMaxBarriers]; +} + +JobSystemWithBarrier::JobSystemWithBarrier(uint inMaxBarriers) +{ + Init(inMaxBarriers); +} + +JobSystemWithBarrier::~JobSystemWithBarrier() +{ + // Ensure that none of the barriers are used +#ifdef JPH_ENABLE_ASSERTS + for (const BarrierImpl *b = mBarriers, *b_end = mBarriers + mMaxBarriers; b < b_end; ++b) + JPH_ASSERT(!b->mInUse); +#endif // JPH_ENABLE_ASSERTS + delete [] mBarriers; +} + +JobSystem::Barrier *JobSystemWithBarrier::CreateBarrier() +{ + JPH_PROFILE_FUNCTION(); + + // Find the first unused barrier + for (uint32 index = 0; index < mMaxBarriers; ++index) + { + bool expected = false; + if (mBarriers[index].mInUse.compare_exchange_strong(expected, true)) + return &mBarriers[index]; + } + + return nullptr; +} + +void JobSystemWithBarrier::DestroyBarrier(Barrier *inBarrier) +{ + JPH_PROFILE_FUNCTION(); + + // Check that no jobs are in the barrier + JPH_ASSERT(static_cast(inBarrier)->IsEmpty()); + + // Flag the barrier as unused + bool expected = true; + static_cast(inBarrier)->mInUse.compare_exchange_strong(expected, false); + JPH_ASSERT(expected); +} + +void JobSystemWithBarrier::WaitForJobs(Barrier *inBarrier) +{ + JPH_PROFILE_FUNCTION(); + + // Let our barrier implementation wait for the jobs + static_cast(inBarrier)->Wait(); +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/JobSystemWithBarrier.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/JobSystemWithBarrier.h new file mode 100644 index 00000000000..0428824a4e5 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/JobSystemWithBarrier.h @@ -0,0 +1,85 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2023 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +JPH_NAMESPACE_BEGIN + +/// Implementation of the Barrier class for a JobSystem +/// +/// This class can be used to make it easier to create a new JobSystem implementation that integrates with your own job system. +/// It will implement all functionality relating to barriers, so the only functions that are left to be implemented are: +/// +/// * JobSystem::GetMaxConcurrency +/// * JobSystem::CreateJob +/// * JobSystem::FreeJob +/// * JobSystem::QueueJob/QueueJobs +/// +/// See instructions in JobSystem for more information on how to implement these. +class JPH_EXPORT JobSystemWithBarrier : public JobSystem +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Constructs barriers + /// @see JobSystemWithBarrier::Init + explicit JobSystemWithBarrier(uint inMaxBarriers); + JobSystemWithBarrier() = default; + virtual ~JobSystemWithBarrier() override; + + /// Initialize the barriers + /// @param inMaxBarriers Max number of barriers that can be allocated at any time + void Init(uint inMaxBarriers); + + // See JobSystem + virtual Barrier * CreateBarrier() override; + virtual void DestroyBarrier(Barrier *inBarrier) override; + virtual void WaitForJobs(Barrier *inBarrier) override; + +private: + class BarrierImpl : public Barrier + { + public: + JPH_OVERRIDE_NEW_DELETE + + /// Constructor + BarrierImpl(); + virtual ~BarrierImpl() override; + + // See Barrier + virtual void AddJob(const JobHandle &inJob) override; + virtual void AddJobs(const JobHandle *inHandles, uint inNumHandles) override; + + /// Check if there are any jobs in the job barrier + inline bool IsEmpty() const { return mJobReadIndex == mJobWriteIndex; } + + /// Wait for all jobs in this job barrier, while waiting, execute jobs that are part of this barrier on the current thread + void Wait(); + + /// Flag to indicate if a barrier has been handed out + atomic mInUse { false }; + + protected: + /// Called by a Job to mark that it is finished + virtual void OnJobFinished(Job *inJob) override; + + /// Jobs queue for the barrier + static constexpr uint cMaxJobs = 2048; + static_assert(IsPowerOf2(cMaxJobs)); // We do bit operations and require max jobs to be a power of 2 + atomic mJobs[cMaxJobs]; ///< List of jobs that are part of this barrier, nullptrs for empty slots + alignas(JPH_CACHE_LINE_SIZE) atomic mJobReadIndex { 0 }; ///< First job that could be valid (modulo cMaxJobs), can be nullptr if other thread is still working on adding the job + alignas(JPH_CACHE_LINE_SIZE) atomic mJobWriteIndex { 0 }; ///< First job that can be written (modulo cMaxJobs) + atomic mNumToAcquire { 0 }; ///< Number of times the semaphore has been released, the barrier should acquire the semaphore this many times (written at the same time as mJobWriteIndex so ok to put in same cache line) + Semaphore mSemaphore; ///< Semaphore used by finishing jobs to signal the barrier that they're done + }; + + /// Array of barriers (we keep them constructed all the time since constructing a semaphore/mutex is not cheap) + uint mMaxBarriers = 0; ///< Max amount of barriers + BarrierImpl * mBarriers = nullptr; ///< List of the actual barriers +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/LinearCurve.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/LinearCurve.cpp new file mode 100644 index 00000000000..a5e21d96c65 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/LinearCurve.cpp @@ -0,0 +1,51 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(LinearCurve::Point) +{ + JPH_ADD_ATTRIBUTE(Point, mX) + JPH_ADD_ATTRIBUTE(Point, mY) +} + +JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(LinearCurve) +{ + JPH_ADD_ATTRIBUTE(LinearCurve, mPoints) +} + +float LinearCurve::GetValue(float inX) const +{ + if (mPoints.empty()) + return 0.0f; + + Points::const_iterator i2 = lower_bound(mPoints.begin(), mPoints.end(), inX, [](const Point &inPoint, float inValue) { return inPoint.mX < inValue; }); + + if (i2 == mPoints.begin()) + return mPoints.front().mY; + else if (i2 == mPoints.end()) + return mPoints.back().mY; + + Points::const_iterator i1 = i2 - 1; + return i1->mY + (inX - i1->mX) * (i2->mY - i1->mY) / (i2->mX - i1->mX); +} + +void LinearCurve::SaveBinaryState(StreamOut &inStream) const +{ + inStream.Write(mPoints); +} + +void LinearCurve::RestoreBinaryState(StreamIn &inStream) +{ + inStream.Read(mPoints); +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/LinearCurve.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/LinearCurve.h new file mode 100644 index 00000000000..ed77341a71c --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/LinearCurve.h @@ -0,0 +1,67 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +JPH_NAMESPACE_BEGIN + +class StreamOut; +class StreamIn; + +// A set of points (x, y) that form a linear curve +class JPH_EXPORT LinearCurve +{ +public: + JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, LinearCurve) + + /// A point on the curve + class Point + { + public: + JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, Point) + + float mX = 0.0f; + float mY = 0.0f; + }; + + /// Remove all points + void Clear() { mPoints.clear(); } + + /// Reserve memory for inNumPoints points + void Reserve(uint inNumPoints) { mPoints.reserve(inNumPoints); } + + /// Add a point to the curve. Points must be inserted in ascending X or Sort() needs to be called when all points have been added. + /// @param inX X value + /// @param inY Y value + void AddPoint(float inX, float inY) { mPoints.push_back({ inX, inY }); } + + /// Sort the points on X ascending + void Sort() { QuickSort(mPoints.begin(), mPoints.end(), [](const Point &inLHS, const Point &inRHS) { return inLHS.mX < inRHS.mX; }); } + + /// Get the lowest X value + float GetMinX() const { return mPoints.empty()? 0.0f : mPoints.front().mX; } + + /// Get the highest X value + float GetMaxX() const { return mPoints.empty()? 0.0f : mPoints.back().mX; } + + /// Sample value on the curve + /// @param inX X value to sample at + /// @return Interpolated Y value + float GetValue(float inX) const; + + /// Saves the state of this object in binary form to inStream. + void SaveBinaryState(StreamOut &inStream) const; + + /// Restore the state of this object from inStream. + void RestoreBinaryState(StreamIn &inStream); + + /// The points on the curve, should be sorted ascending by x + using Points = Array; + Points mPoints; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/LockFreeHashMap.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/LockFreeHashMap.h new file mode 100644 index 00000000000..9debf9f9c80 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/LockFreeHashMap.h @@ -0,0 +1,182 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +JPH_NAMESPACE_BEGIN + +/// Allocator for a lock free hash map +class LFHMAllocator : public NonCopyable +{ +public: + /// Destructor + inline ~LFHMAllocator(); + + /// Initialize the allocator + /// @param inObjectStoreSizeBytes Number of bytes to reserve for all key value pairs + inline void Init(uint inObjectStoreSizeBytes); + + /// Clear all allocations + inline void Clear(); + + /// Allocate a new block of data + /// @param inBlockSize Size of block to allocate (will potentially return a smaller block if memory is full). + /// @param ioBegin Should be the start of the first free byte in current memory block on input, will contain the start of the first free byte in allocated block on return. + /// @param ioEnd Should be the byte beyond the current memory block on input, will contain the byte beyond the allocated block on return. + inline void Allocate(uint32 inBlockSize, uint32 &ioBegin, uint32 &ioEnd); + + /// Convert a pointer to an offset + template + inline uint32 ToOffset(const T *inData) const; + + /// Convert an offset to a pointer + template + inline T * FromOffset(uint32 inOffset) const; + +private: + uint8 * mObjectStore = nullptr; ///< This contains a contiguous list of objects (possibly of varying size) + uint32 mObjectStoreSizeBytes = 0; ///< The size of mObjectStore in bytes + atomic mWriteOffset { 0 }; ///< Next offset to write to in mObjectStore +}; + +/// Allocator context object for a lock free hash map that allocates a larger memory block at once and hands it out in smaller portions. +/// This avoids contention on the atomic LFHMAllocator::mWriteOffset. +class LFHMAllocatorContext : public NonCopyable +{ +public: + /// Construct a new allocator context + inline LFHMAllocatorContext(LFHMAllocator &inAllocator, uint32 inBlockSize); + + /// @brief Allocate data block + /// @param inSize Size of block to allocate. + /// @param inAlignment Alignment of block to allocate. + /// @param outWriteOffset Offset in buffer where block is located + /// @return True if allocation succeeded + inline bool Allocate(uint32 inSize, uint32 inAlignment, uint32 &outWriteOffset); + +private: + LFHMAllocator & mAllocator; + uint32 mBlockSize; + uint32 mBegin = 0; + uint32 mEnd = 0; +}; + +/// Very simple lock free hash map that only allows insertion, retrieval and provides a fixed amount of buckets and fixed storage. +/// Note: This class currently assumes key and value are simple types that need no calls to the destructor. +template +class LockFreeHashMap : public NonCopyable +{ +public: + using MapType = LockFreeHashMap; + + /// Destructor + explicit LockFreeHashMap(LFHMAllocator &inAllocator) : mAllocator(inAllocator) { } + ~LockFreeHashMap(); + + /// Initialization + /// @param inMaxBuckets Max amount of buckets to use in the hashmap. Must be power of 2. + void Init(uint32 inMaxBuckets); + + /// Remove all elements. + /// Note that this cannot happen simultaneously with adding new elements. + void Clear(); + + /// Get the current amount of buckets that the map is using + uint32 GetNumBuckets() const { return mNumBuckets; } + + /// Get the maximum amount of buckets that this map supports + uint32 GetMaxBuckets() const { return mMaxBuckets; } + + /// Update the number of buckets. This must be done after clearing the map and cannot be done concurrently with any other operations on the map. + /// Note that the number of buckets can never become bigger than the specified max buckets during initialization and that it must be a power of 2. + void SetNumBuckets(uint32 inNumBuckets); + + /// A key / value pair that is inserted in the map + class KeyValue + { + public: + const Key & GetKey() const { return mKey; } + Value & GetValue() { return mValue; } + const Value & GetValue() const { return mValue; } + + private: + template friend class LockFreeHashMap; + + Key mKey; ///< Key for this entry + uint32 mNextOffset; ///< Offset in mObjectStore of next KeyValue entry with same hash + Value mValue; ///< Value for this entry + optionally extra bytes + }; + + /// Insert a new element, returns null if map full. + /// Multiple threads can be inserting in the map at the same time. + template + inline KeyValue * Create(LFHMAllocatorContext &ioContext, const Key &inKey, uint64 inKeyHash, int inExtraBytes, Params &&... inConstructorParams); + + /// Find an element, returns null if not found + inline const KeyValue * Find(const Key &inKey, uint64 inKeyHash) const; + + /// Value of an invalid handle + const static uint32 cInvalidHandle = uint32(-1); + + /// Get convert key value pair to uint32 handle + inline uint32 ToHandle(const KeyValue *inKeyValue) const; + + /// Convert uint32 handle back to key and value + inline const KeyValue * FromHandle(uint32 inHandle) const; + +#ifdef JPH_ENABLE_ASSERTS + /// Get the number of key value pairs that this map currently contains. + /// Available only when asserts are enabled because adding elements creates contention on this atomic and negatively affects performance. + inline uint32 GetNumKeyValues() const { return mNumKeyValues; } +#endif // JPH_ENABLE_ASSERTS + + /// Get all key/value pairs + inline void GetAllKeyValues(Array &outAll) const; + + /// Non-const iterator + struct Iterator + { + /// Comparison + bool operator == (const Iterator &inRHS) const { return mMap == inRHS.mMap && mBucket == inRHS.mBucket && mOffset == inRHS.mOffset; } + bool operator != (const Iterator &inRHS) const { return !(*this == inRHS); } + + /// Convert to key value pair + KeyValue & operator * (); + + /// Next item + Iterator & operator ++ (); + + MapType * mMap; + uint32 mBucket; + uint32 mOffset; + }; + + /// Iterate over the map, note that it is not safe to do this in parallel to Clear(). + /// It is safe to do this while adding elements to the map, but newly added elements may or may not be returned by the iterator. + Iterator begin(); + Iterator end(); + +#ifdef _DEBUG + /// Output stats about this map to the log + void TraceStats() const; +#endif + +private: + LFHMAllocator & mAllocator; ///< Allocator used to allocate key value pairs + +#ifdef JPH_ENABLE_ASSERTS + atomic mNumKeyValues = 0; ///< Number of key value pairs in the store +#endif // JPH_ENABLE_ASSERTS + + atomic * mBuckets = nullptr; ///< This contains the offset in mObjectStore of the first object with a particular hash + uint32 mNumBuckets = 0; ///< Current number of buckets + uint32 mMaxBuckets = 0; ///< Maximum number of buckets +}; + +JPH_NAMESPACE_END + +#include "LockFreeHashMap.inl" diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/LockFreeHashMap.inl b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/LockFreeHashMap.inl new file mode 100644 index 00000000000..593b5bd331c --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/LockFreeHashMap.inl @@ -0,0 +1,351 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +JPH_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////////// +// LFHMAllocator +/////////////////////////////////////////////////////////////////////////////////// + +inline LFHMAllocator::~LFHMAllocator() +{ + AlignedFree(mObjectStore); +} + +inline void LFHMAllocator::Init(uint inObjectStoreSizeBytes) +{ + JPH_ASSERT(mObjectStore == nullptr); + + mObjectStoreSizeBytes = inObjectStoreSizeBytes; + mObjectStore = reinterpret_cast(JPH::AlignedAllocate(inObjectStoreSizeBytes, 16)); +} + +inline void LFHMAllocator::Clear() +{ + mWriteOffset = 0; +} + +inline void LFHMAllocator::Allocate(uint32 inBlockSize, uint32 &ioBegin, uint32 &ioEnd) +{ + // If we're already beyond the end of our buffer then don't do an atomic add. + // It's possible that many keys are inserted after the allocator is full, making it possible + // for mWriteOffset (uint32) to wrap around to zero. When this happens, there will be a memory corruption. + // This way, we will be able to progress the write offset beyond the size of the buffer + // worst case by max * inBlockSize. + if (mWriteOffset.load(memory_order_relaxed) >= mObjectStoreSizeBytes) + return; + + // Atomically fetch a block from the pool + uint32 begin = mWriteOffset.fetch_add(inBlockSize, memory_order_relaxed); + uint32 end = min(begin + inBlockSize, mObjectStoreSizeBytes); + + if (ioEnd == begin) + { + // Block is allocated straight after our previous block + begin = ioBegin; + } + else + { + // Block is a new block + begin = min(begin, mObjectStoreSizeBytes); + } + + // Store the begin and end of the resulting block + ioBegin = begin; + ioEnd = end; +} + +template +inline uint32 LFHMAllocator::ToOffset(const T *inData) const +{ + const uint8 *data = reinterpret_cast(inData); + JPH_ASSERT(data >= mObjectStore && data < mObjectStore + mObjectStoreSizeBytes); + return uint32(data - mObjectStore); +} + +template +inline T *LFHMAllocator::FromOffset(uint32 inOffset) const +{ + JPH_ASSERT(inOffset < mObjectStoreSizeBytes); + return reinterpret_cast(mObjectStore + inOffset); +} + +/////////////////////////////////////////////////////////////////////////////////// +// LFHMAllocatorContext +/////////////////////////////////////////////////////////////////////////////////// + +inline LFHMAllocatorContext::LFHMAllocatorContext(LFHMAllocator &inAllocator, uint32 inBlockSize) : + mAllocator(inAllocator), + mBlockSize(inBlockSize) +{ +} + +inline bool LFHMAllocatorContext::Allocate(uint32 inSize, uint32 inAlignment, uint32 &outWriteOffset) +{ + // Calculate needed bytes for alignment + JPH_ASSERT(IsPowerOf2(inAlignment)); + uint32 alignment_mask = inAlignment - 1; + uint32 alignment = (inAlignment - (mBegin & alignment_mask)) & alignment_mask; + + // Check if we have space + if (mEnd - mBegin < inSize + alignment) + { + // Allocate a new block + mAllocator.Allocate(mBlockSize, mBegin, mEnd); + + // Update alignment + alignment = (inAlignment - (mBegin & alignment_mask)) & alignment_mask; + + // Check if we have space again + if (mEnd - mBegin < inSize + alignment) + return false; + } + + // Make the allocation + mBegin += alignment; + outWriteOffset = mBegin; + mBegin += inSize; + return true; +} + +/////////////////////////////////////////////////////////////////////////////////// +// LockFreeHashMap +/////////////////////////////////////////////////////////////////////////////////// + +template +void LockFreeHashMap::Init(uint32 inMaxBuckets) +{ + JPH_ASSERT(inMaxBuckets >= 4 && IsPowerOf2(inMaxBuckets)); + JPH_ASSERT(mBuckets == nullptr); + + mNumBuckets = inMaxBuckets; + mMaxBuckets = inMaxBuckets; + + mBuckets = reinterpret_cast *>(AlignedAllocate(inMaxBuckets * sizeof(atomic), 16)); + + Clear(); +} + +template +LockFreeHashMap::~LockFreeHashMap() +{ + AlignedFree(mBuckets); +} + +template +void LockFreeHashMap::Clear() +{ +#ifdef JPH_ENABLE_ASSERTS + // Reset number of key value pairs + mNumKeyValues = 0; +#endif // JPH_ENABLE_ASSERTS + + // Reset buckets 4 at a time + static_assert(sizeof(atomic) == sizeof(uint32)); + UVec4 invalid_handle = UVec4::sReplicate(cInvalidHandle); + uint32 *start = reinterpret_cast(mBuckets); + const uint32 *end = start + mNumBuckets; + JPH_ASSERT(IsAligned(start, 16)); + while (start < end) + { + invalid_handle.StoreInt4Aligned(start); + start += 4; + } +} + +template +void LockFreeHashMap::SetNumBuckets(uint32 inNumBuckets) +{ + JPH_ASSERT(mNumKeyValues == 0); + JPH_ASSERT(inNumBuckets <= mMaxBuckets); + JPH_ASSERT(inNumBuckets >= 4 && IsPowerOf2(inNumBuckets)); + + mNumBuckets = inNumBuckets; +} + +template +template +inline typename LockFreeHashMap::KeyValue *LockFreeHashMap::Create(LFHMAllocatorContext &ioContext, const Key &inKey, uint64 inKeyHash, int inExtraBytes, Params &&... inConstructorParams) +{ + // This is not a multi map, test the key hasn't been inserted yet + JPH_ASSERT(Find(inKey, inKeyHash) == nullptr); + + // Calculate total size + uint size = sizeof(KeyValue) + inExtraBytes; + + // Get the write offset for this key value pair + uint32 write_offset; + if (!ioContext.Allocate(size, alignof(KeyValue), write_offset)) + return nullptr; + +#ifdef JPH_ENABLE_ASSERTS + // Increment amount of entries in map + mNumKeyValues.fetch_add(1, memory_order_relaxed); +#endif // JPH_ENABLE_ASSERTS + + // Construct the key/value pair + KeyValue *kv = mAllocator.template FromOffset(write_offset); + JPH_ASSERT(intptr_t(kv) % alignof(KeyValue) == 0); +#ifdef _DEBUG + memset(kv, 0xcd, size); +#endif + kv->mKey = inKey; + new (&kv->mValue) Value(std::forward(inConstructorParams)...); + + // Get the offset to the first object from the bucket with corresponding hash + atomic &offset = mBuckets[inKeyHash & (mNumBuckets - 1)]; + + // Add this entry as the first element in the linked list + uint32 old_offset = offset.load(memory_order_relaxed); + for (;;) + { + kv->mNextOffset = old_offset; + if (offset.compare_exchange_weak(old_offset, write_offset, memory_order_release)) + break; + } + + return kv; +} + +template +inline const typename LockFreeHashMap::KeyValue *LockFreeHashMap::Find(const Key &inKey, uint64 inKeyHash) const +{ + // Get the offset to the keyvalue object from the bucket with corresponding hash + uint32 offset = mBuckets[inKeyHash & (mNumBuckets - 1)].load(memory_order_acquire); + while (offset != cInvalidHandle) + { + // Loop through linked list of values until the right one is found + const KeyValue *kv = mAllocator.template FromOffset(offset); + if (kv->mKey == inKey) + return kv; + offset = kv->mNextOffset; + } + + // Not found + return nullptr; +} + +template +inline uint32 LockFreeHashMap::ToHandle(const KeyValue *inKeyValue) const +{ + return mAllocator.ToOffset(inKeyValue); +} + +template +inline const typename LockFreeHashMap::KeyValue *LockFreeHashMap::FromHandle(uint32 inHandle) const +{ + return mAllocator.template FromOffset(inHandle); +} + +template +inline void LockFreeHashMap::GetAllKeyValues(Array &outAll) const +{ + for (const atomic *bucket = mBuckets; bucket < mBuckets + mNumBuckets; ++bucket) + { + uint32 offset = *bucket; + while (offset != cInvalidHandle) + { + const KeyValue *kv = mAllocator.template FromOffset(offset); + outAll.push_back(kv); + offset = kv->mNextOffset; + } + } +} + +template +typename LockFreeHashMap::Iterator LockFreeHashMap::begin() +{ + // Start with the first bucket + Iterator it { this, 0, mBuckets[0] }; + + // If it doesn't contain a valid entry, use the ++ operator to find the first valid entry + if (it.mOffset == cInvalidHandle) + ++it; + + return it; +} + +template +typename LockFreeHashMap::Iterator LockFreeHashMap::end() +{ + return { this, mNumBuckets, cInvalidHandle }; +} + +template +typename LockFreeHashMap::KeyValue &LockFreeHashMap::Iterator::operator* () +{ + JPH_ASSERT(mOffset != cInvalidHandle); + + return *mMap->mAllocator.template FromOffset(mOffset); +} + +template +typename LockFreeHashMap::Iterator &LockFreeHashMap::Iterator::operator++ () +{ + JPH_ASSERT(mBucket < mMap->mNumBuckets); + + // Find the next key value in this bucket + if (mOffset != cInvalidHandle) + { + const KeyValue *kv = mMap->mAllocator.template FromOffset(mOffset); + mOffset = kv->mNextOffset; + if (mOffset != cInvalidHandle) + return *this; + } + + // Loop over next buckets + for (;;) + { + // Next bucket + ++mBucket; + if (mBucket >= mMap->mNumBuckets) + return *this; + + // Fetch the first entry in the bucket + mOffset = mMap->mBuckets[mBucket]; + if (mOffset != cInvalidHandle) + return *this; + } +} + +#ifdef _DEBUG + +template +void LockFreeHashMap::TraceStats() const +{ + const int cMaxPerBucket = 256; + + int max_objects_per_bucket = 0; + int num_objects = 0; + int histogram[cMaxPerBucket]; + for (int i = 0; i < cMaxPerBucket; ++i) + histogram[i] = 0; + + for (atomic *bucket = mBuckets, *bucket_end = mBuckets + mNumBuckets; bucket < bucket_end; ++bucket) + { + int objects_in_bucket = 0; + uint32 offset = *bucket; + while (offset != cInvalidHandle) + { + const KeyValue *kv = mAllocator.template FromOffset(offset); + offset = kv->mNextOffset; + ++objects_in_bucket; + ++num_objects; + } + max_objects_per_bucket = max(objects_in_bucket, max_objects_per_bucket); + histogram[min(objects_in_bucket, cMaxPerBucket - 1)]++; + } + + Trace("max_objects_per_bucket = %d, num_buckets = %u, num_objects = %d", max_objects_per_bucket, mNumBuckets, num_objects); + + for (int i = 0; i < cMaxPerBucket; ++i) + if (histogram[i] != 0) + Trace("%d: %d", i, histogram[i]); +} + +#endif + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Memory.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Memory.cpp new file mode 100644 index 00000000000..b259313e367 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Memory.cpp @@ -0,0 +1,74 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +JPH_SUPPRESS_WARNINGS_STD_BEGIN +#include +JPH_SUPPRESS_WARNINGS_STD_END +#include + +JPH_NAMESPACE_BEGIN + +#ifdef JPH_DISABLE_CUSTOM_ALLOCATOR + #define JPH_ALLOC_FN(x) x + #define JPH_ALLOC_SCOPE +#else + #define JPH_ALLOC_FN(x) x##Impl + #define JPH_ALLOC_SCOPE static +#endif + +JPH_ALLOC_SCOPE void *JPH_ALLOC_FN(Allocate)(size_t inSize) +{ + return malloc(inSize); +} + +JPH_ALLOC_SCOPE void JPH_ALLOC_FN(Free)(void *inBlock) +{ + free(inBlock); +} + +JPH_ALLOC_SCOPE void *JPH_ALLOC_FN(AlignedAllocate)(size_t inSize, size_t inAlignment) +{ +#if defined(JPH_PLATFORM_WINDOWS) + // Microsoft doesn't implement posix_memalign + return _aligned_malloc(inSize, inAlignment); +#else + void *block = nullptr; + JPH_SUPPRESS_WARNING_PUSH + JPH_GCC_SUPPRESS_WARNING("-Wunused-result") + JPH_CLANG_SUPPRESS_WARNING("-Wunused-result") + posix_memalign(&block, inAlignment, inSize); + JPH_SUPPRESS_WARNING_POP + return block; +#endif +} + +JPH_ALLOC_SCOPE void JPH_ALLOC_FN(AlignedFree)(void *inBlock) +{ +#if defined(JPH_PLATFORM_WINDOWS) + _aligned_free(inBlock); +#else + free(inBlock); +#endif +} + +#ifndef JPH_DISABLE_CUSTOM_ALLOCATOR + +AllocateFunction Allocate = nullptr; +FreeFunction Free = nullptr; +AlignedAllocateFunction AlignedAllocate = nullptr; +AlignedFreeFunction AlignedFree = nullptr; + +void RegisterDefaultAllocator() +{ + Allocate = AllocateImpl; + Free = FreeImpl; + AlignedAllocate = AlignedAllocateImpl; + AlignedFree = AlignedFreeImpl; +} + +#endif // JPH_DISABLE_CUSTOM_ALLOCATOR + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Memory.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Memory.h new file mode 100644 index 00000000000..72bccaf836a --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Memory.h @@ -0,0 +1,55 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +JPH_NAMESPACE_BEGIN + +#ifndef JPH_DISABLE_CUSTOM_ALLOCATOR + +// Normal memory allocation, must be at least 8 byte aligned on 32 bit platform and 16 byte aligned on 64 bit platform +using AllocateFunction = void *(*)(size_t inSize); +using FreeFunction = void (*)(void *inBlock); + +// Aligned memory allocation +using AlignedAllocateFunction = void *(*)(size_t inSize, size_t inAlignment); +using AlignedFreeFunction = void (*)(void *inBlock); + +// User defined allocation / free functions +JPH_EXPORT extern AllocateFunction Allocate; +JPH_EXPORT extern FreeFunction Free; +JPH_EXPORT extern AlignedAllocateFunction AlignedAllocate; +JPH_EXPORT extern AlignedFreeFunction AlignedFree; + +/// Register platform default allocation / free functions +JPH_EXPORT void RegisterDefaultAllocator(); + +/// Macro to override the new and delete functions +#define JPH_OVERRIDE_NEW_DELETE \ + JPH_INLINE void *operator new (size_t inCount) { return JPH::Allocate(inCount); } \ + JPH_INLINE void operator delete (void *inPointer) noexcept { JPH::Free(inPointer); } \ + JPH_INLINE void *operator new[] (size_t inCount) { return JPH::Allocate(inCount); } \ + JPH_INLINE void operator delete[] (void *inPointer) noexcept { JPH::Free(inPointer); } \ + JPH_INLINE void *operator new (size_t inCount, std::align_val_t inAlignment) { return JPH::AlignedAllocate(inCount, static_cast(inAlignment)); } \ + JPH_INLINE void operator delete (void *inPointer, [[maybe_unused]] std::align_val_t inAlignment) noexcept { JPH::AlignedFree(inPointer); } \ + JPH_INLINE void *operator new[] (size_t inCount, std::align_val_t inAlignment) { return JPH::AlignedAllocate(inCount, static_cast(inAlignment)); } \ + JPH_INLINE void operator delete[] (void *inPointer, [[maybe_unused]] std::align_val_t inAlignment) noexcept { JPH::AlignedFree(inPointer); } + +#else + +// Directly define the allocation functions +JPH_EXPORT void *Allocate(size_t inSize); +JPH_EXPORT void Free(void *inBlock); +JPH_EXPORT void *AlignedAllocate(size_t inSize, size_t inAlignment); +JPH_EXPORT void AlignedFree(void *inBlock); + +// Don't implement allocator registering +inline void RegisterDefaultAllocator() { } + +// Don't override new/delete +#define JPH_OVERRIDE_NEW_DELETE + +#endif // !JPH_DISABLE_CUSTOM_ALLOCATOR + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Mutex.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Mutex.h new file mode 100644 index 00000000000..6969aa45d0a --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Mutex.h @@ -0,0 +1,223 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +JPH_SUPPRESS_WARNINGS_STD_BEGIN +#include +#include +#include +JPH_SUPPRESS_WARNINGS_STD_END + +JPH_NAMESPACE_BEGIN + +// Things we're using from STL +using std::mutex; +using std::shared_mutex; +using std::thread; +using std::lock_guard; +using std::shared_lock; +using std::unique_lock; + +#ifdef JPH_PLATFORM_BLUE + +// On Platform Blue the mutex class is not very fast so we implement it using the official APIs +class MutexBase : public NonCopyable +{ +public: + MutexBase() + { + JPH_PLATFORM_BLUE_MUTEX_INIT(mMutex); + } + + ~MutexBase() + { + JPH_PLATFORM_BLUE_MUTEX_DESTROY(mMutex); + } + + inline bool try_lock() + { + return JPH_PLATFORM_BLUE_MUTEX_TRYLOCK(mMutex); + } + + inline void lock() + { + JPH_PLATFORM_BLUE_MUTEX_LOCK(mMutex); + } + + inline void unlock() + { + JPH_PLATFORM_BLUE_MUTEX_UNLOCK(mMutex); + } + +private: + JPH_PLATFORM_BLUE_MUTEX mMutex; +}; + +// On Platform Blue the shared_mutex class is not very fast so we implement it using the official APIs +class SharedMutexBase : public NonCopyable +{ +public: + SharedMutexBase() + { + JPH_PLATFORM_BLUE_RWLOCK_INIT(mRWLock); + } + + ~SharedMutexBase() + { + JPH_PLATFORM_BLUE_RWLOCK_DESTROY(mRWLock); + } + + inline bool try_lock() + { + return JPH_PLATFORM_BLUE_RWLOCK_TRYWLOCK(mRWLock); + } + + inline bool try_lock_shared() + { + return JPH_PLATFORM_BLUE_RWLOCK_TRYRLOCK(mRWLock); + } + + inline void lock() + { + JPH_PLATFORM_BLUE_RWLOCK_WLOCK(mRWLock); + } + + inline void unlock() + { + JPH_PLATFORM_BLUE_RWLOCK_WUNLOCK(mRWLock); + } + + inline void lock_shared() + { + JPH_PLATFORM_BLUE_RWLOCK_RLOCK(mRWLock); + } + + inline void unlock_shared() + { + JPH_PLATFORM_BLUE_RWLOCK_RUNLOCK(mRWLock); + } + +private: + JPH_PLATFORM_BLUE_RWLOCK mRWLock; +}; + +#else + +// On other platforms just use the STL implementation +using MutexBase = mutex; +using SharedMutexBase = shared_mutex; + +#endif // JPH_PLATFORM_BLUE + +#if defined(JPH_ENABLE_ASSERTS) || defined(JPH_PROFILE_ENABLED) || defined(JPH_EXTERNAL_PROFILE) + +/// Very simple wrapper around MutexBase which tracks lock contention in the profiler +/// and asserts that locks/unlocks take place on the same thread +class Mutex : public MutexBase +{ +public: + inline bool try_lock() + { + JPH_ASSERT(mLockedThreadID != std::this_thread::get_id()); + if (MutexBase::try_lock()) + { + JPH_IF_ENABLE_ASSERTS(mLockedThreadID = std::this_thread::get_id();) + return true; + } + return false; + } + + inline void lock() + { + if (!try_lock()) + { + JPH_PROFILE("Lock", 0xff00ffff); + MutexBase::lock(); + JPH_IF_ENABLE_ASSERTS(mLockedThreadID = std::this_thread::get_id();) + } + } + + inline void unlock() + { + JPH_ASSERT(mLockedThreadID == std::this_thread::get_id()); + JPH_IF_ENABLE_ASSERTS(mLockedThreadID = thread::id();) + MutexBase::unlock(); + } + +#ifdef JPH_ENABLE_ASSERTS + inline bool is_locked() + { + return mLockedThreadID != thread::id(); + } +#endif // JPH_ENABLE_ASSERTS + +private: + JPH_IF_ENABLE_ASSERTS(thread::id mLockedThreadID;) +}; + +/// Very simple wrapper around SharedMutexBase which tracks lock contention in the profiler +/// and asserts that locks/unlocks take place on the same thread +class SharedMutex : public SharedMutexBase +{ +public: + inline bool try_lock() + { + JPH_ASSERT(mLockedThreadID != std::this_thread::get_id()); + if (SharedMutexBase::try_lock()) + { + JPH_IF_ENABLE_ASSERTS(mLockedThreadID = std::this_thread::get_id();) + return true; + } + return false; + } + + inline void lock() + { + if (!try_lock()) + { + JPH_PROFILE("WLock", 0xff00ffff); + SharedMutexBase::lock(); + JPH_IF_ENABLE_ASSERTS(mLockedThreadID = std::this_thread::get_id();) + } + } + + inline void unlock() + { + JPH_ASSERT(mLockedThreadID == std::this_thread::get_id()); + JPH_IF_ENABLE_ASSERTS(mLockedThreadID = thread::id();) + SharedMutexBase::unlock(); + } + +#ifdef JPH_ENABLE_ASSERTS + inline bool is_locked() + { + return mLockedThreadID != thread::id(); + } +#endif // JPH_ENABLE_ASSERTS + + inline void lock_shared() + { + if (!try_lock_shared()) + { + JPH_PROFILE("RLock", 0xff00ffff); + SharedMutexBase::lock_shared(); + } + } + +private: + JPH_IF_ENABLE_ASSERTS(thread::id mLockedThreadID;) +}; + +#else + +using Mutex = MutexBase; +using SharedMutex = SharedMutexBase; + +#endif + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/MutexArray.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/MutexArray.h new file mode 100644 index 00000000000..f8fdd827913 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/MutexArray.h @@ -0,0 +1,98 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +/// A mutex array protects a number of resources with a limited amount of mutexes. +/// It uses hashing to find the mutex of a particular object. +/// The idea is that if the amount of threads is much smaller than the amount of mutexes +/// that there is a relatively small chance that two different objects map to the same mutex. +template +class MutexArray : public NonCopyable +{ +public: + /// Constructor, constructs an empty mutex array that you need to initialize with Init() + MutexArray() = default; + + /// Constructor, constructs an array with inNumMutexes entries + explicit MutexArray(uint inNumMutexes) { Init(inNumMutexes); } + + /// Destructor + ~MutexArray() { delete [] mMutexStorage; } + + /// Initialization + /// @param inNumMutexes The amount of mutexes to allocate + void Init(uint inNumMutexes) + { + JPH_ASSERT(mMutexStorage == nullptr); + JPH_ASSERT(inNumMutexes > 0 && IsPowerOf2(inNumMutexes)); + + mMutexStorage = new MutexStorage[inNumMutexes]; + mNumMutexes = inNumMutexes; + } + + /// Get the number of mutexes that were allocated + inline uint GetNumMutexes() const + { + return mNumMutexes; + } + + /// Convert an object index to a mutex index + inline uint32 GetMutexIndex(uint32 inObjectIndex) const + { + std::hash hasher; + return hasher(inObjectIndex) & (mNumMutexes - 1); + } + + /// Get the mutex belonging to a certain object by index + inline MutexType & GetMutexByObjectIndex(uint32 inObjectIndex) + { + return mMutexStorage[GetMutexIndex(inObjectIndex)].mMutex; + } + + /// Get a mutex by index in the array + inline MutexType & GetMutexByIndex(uint32 inMutexIndex) + { + return mMutexStorage[inMutexIndex].mMutex; + } + + /// Lock all mutexes + void LockAll() + { + JPH_PROFILE_FUNCTION(); + + MutexStorage *end = mMutexStorage + mNumMutexes; + for (MutexStorage *m = mMutexStorage; m < end; ++m) + m->mMutex.lock(); + } + + /// Unlock all mutexes + void UnlockAll() + { + JPH_PROFILE_FUNCTION(); + + MutexStorage *end = mMutexStorage + mNumMutexes; + for (MutexStorage *m = mMutexStorage; m < end; ++m) + m->mMutex.unlock(); + } + +private: + /// Align the mutex to a cache line to ensure there is no false sharing (this is platform dependent, we do this to be safe) + struct alignas(JPH_CACHE_LINE_SIZE) MutexStorage + { + JPH_OVERRIDE_NEW_DELETE + + MutexType mMutex; + }; + + MutexStorage * mMutexStorage = nullptr; + uint mNumMutexes = 0; +}; + +JPH_NAMESPACE_END + diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/NonCopyable.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/NonCopyable.h new file mode 100644 index 00000000000..18b431e925a --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/NonCopyable.h @@ -0,0 +1,18 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +JPH_NAMESPACE_BEGIN + +/// Class that makes another class non-copyable. Usage: Inherit from NonCopyable. +class JPH_EXPORT NonCopyable +{ +public: + NonCopyable() = default; + NonCopyable(const NonCopyable &) = delete; + void operator = (const NonCopyable &) = delete; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Profiler.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Profiler.cpp new file mode 100644 index 00000000000..5678ba77171 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Profiler.cpp @@ -0,0 +1,346 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#include + +JPH_SUPPRESS_WARNINGS_STD_BEGIN +#include +JPH_SUPPRESS_WARNINGS_STD_END + +#ifdef JPH_PROFILE_ENABLED + +JPH_NAMESPACE_BEGIN + +////////////////////////////////////////////////////////////////////////////////////////// +// Profiler +////////////////////////////////////////////////////////////////////////////////////////// + +Profiler *Profiler::sInstance = nullptr; + +#ifdef JPH_SHARED_LIBRARY + static thread_local ProfileThread *sInstance = nullptr; + + ProfileThread *ProfileThread::sGetInstance() + { + return sInstance; + } + + void ProfileThread::sSetInstance(ProfileThread *inInstance) + { + sInstance = inInstance; + } +#else + thread_local ProfileThread *ProfileThread::sInstance = nullptr; +#endif + +bool ProfileMeasurement::sOutOfSamplesReported = false; + +void Profiler::UpdateReferenceTime() +{ + mReferenceTick = GetProcessorTickCount(); + mReferenceTime = std::chrono::high_resolution_clock::now(); +} + +uint64 Profiler::GetProcessorTicksPerSecond() const +{ + uint64 ticks = GetProcessorTickCount(); + std::chrono::high_resolution_clock::time_point time = std::chrono::high_resolution_clock::now(); + + return (ticks - mReferenceTick) * 1000000000ULL / std::chrono::duration_cast(time - mReferenceTime).count(); +} + +void Profiler::NextFrame() +{ + std::lock_guard lock(mLock); + + if (mDump) + { + DumpInternal(); + mDump = false; + } + + for (ProfileThread *t : mThreads) + t->mCurrentSample = 0; + + UpdateReferenceTime(); +} + +void Profiler::Dump(const string_view &inTag) +{ + mDump = true; + mDumpTag = inTag; +} + +void Profiler::AddThread(ProfileThread *inThread) +{ + std::lock_guard lock(mLock); + + mThreads.push_back(inThread); +} + +void Profiler::RemoveThread(ProfileThread *inThread) +{ + std::lock_guard lock(mLock); + + Array::iterator i = find(mThreads.begin(), mThreads.end(), inThread); + JPH_ASSERT(i != mThreads.end()); + mThreads.erase(i); +} + +void Profiler::sAggregate(int inDepth, uint32 inColor, ProfileSample *&ioSample, const ProfileSample *inEnd, Aggregators &ioAggregators, KeyToAggregator &ioKeyToAggregator) +{ + // Store depth + ioSample->mDepth = uint8(min(255, inDepth)); + + // Update color + if (ioSample->mColor == 0) + ioSample->mColor = inColor; + else + inColor = ioSample->mColor; + + // Start accumulating totals + uint64 cycles_this_with_children = ioSample->mEndCycle - ioSample->mStartCycle; + + // Loop over following samples until we find a sample that starts on or after our end + ProfileSample *sample; + for (sample = ioSample + 1; sample < inEnd && sample->mStartCycle < ioSample->mEndCycle; ++sample) + { + JPH_ASSERT(sample[-1].mStartCycle <= sample->mStartCycle); + JPH_ASSERT(sample->mStartCycle >= ioSample->mStartCycle); + JPH_ASSERT(sample->mEndCycle <= ioSample->mEndCycle); + + // Recurse and skip over the children of this child + sAggregate(inDepth + 1, inColor, sample, inEnd, ioAggregators, ioKeyToAggregator); + } + + // Find the aggregator for this name / filename pair + Aggregator *aggregator; + KeyToAggregator::iterator aggregator_idx = ioKeyToAggregator.find(ioSample->mName); + if (aggregator_idx == ioKeyToAggregator.end()) + { + // Not found, add to map and insert in array + ioKeyToAggregator.try_emplace(ioSample->mName, ioAggregators.size()); + ioAggregators.emplace_back(ioSample->mName); + aggregator = &ioAggregators.back(); + } + else + { + // Found + aggregator = &ioAggregators[aggregator_idx->second]; + } + + // Add the measurement to the aggregator + aggregator->AccumulateMeasurement(cycles_this_with_children); + + // Update ioSample to the last child of ioSample + JPH_ASSERT(sample[-1].mStartCycle <= ioSample->mEndCycle); + JPH_ASSERT(sample >= inEnd || sample->mStartCycle >= ioSample->mEndCycle); + ioSample = sample - 1; +} + +void Profiler::DumpInternal() +{ + // Freeze data from threads + // Note that this is not completely thread safe: As a profile sample is added mCurrentSample is incremented + // but the data is not written until the sample finishes. So if we dump the profile information while + // some other thread is running, we may get some garbage information from the previous frame + Threads threads; + for (ProfileThread *t : mThreads) + threads.push_back({ t->mThreadName, t->mSamples, t->mSamples + t->mCurrentSample }); + + // Shift all samples so that the first sample is at zero + uint64 min_cycle = 0xffffffffffffffffUL; + for (const ThreadSamples &t : threads) + if (t.mSamplesBegin < t.mSamplesEnd) + min_cycle = min(min_cycle, t.mSamplesBegin[0].mStartCycle); + for (const ThreadSamples &t : threads) + for (ProfileSample *s = t.mSamplesBegin, *end = t.mSamplesEnd; s < end; ++s) + { + s->mStartCycle -= min_cycle; + s->mEndCycle -= min_cycle; + } + + // Determine tag of this profile + String tag; + if (mDumpTag.empty()) + { + // Next sequence number + static int number = 0; + ++number; + tag = ConvertToString(number); + } + else + { + // Take provided tag + tag = mDumpTag; + mDumpTag.clear(); + } + + // Aggregate data across threads + Aggregators aggregators; + KeyToAggregator key_to_aggregators; + for (const ThreadSamples &t : threads) + for (ProfileSample *s = t.mSamplesBegin, *end = t.mSamplesEnd; s < end; ++s) + sAggregate(0, Color::sGetDistinctColor(0).GetUInt32(), s, end, aggregators, key_to_aggregators); + + // Dump as chart + DumpChart(tag.c_str(), threads, key_to_aggregators, aggregators); +} + +static String sHTMLEncode(const char *inString) +{ + String str(inString); + StringReplace(str, "<", "<"); + StringReplace(str, ">", ">"); + return str; +} + +void Profiler::DumpChart(const char *inTag, const Threads &inThreads, const KeyToAggregator &inKeyToAggregators, const Aggregators &inAggregators) +{ + // Open file + std::ofstream f; + f.open(StringFormat("profile_chart_%s.html", inTag).c_str(), std::ofstream::out | std::ofstream::trunc); + if (!f.is_open()) + return; + + // Write header + f << R"( + + + Profile Chart + + + + + + + +
        + +)"; +} + +JPH_NAMESPACE_END + +#endif // JPH_PROFILE_ENABLED diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Profiler.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Profiler.h new file mode 100644 index 00000000000..bbe64182155 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Profiler.h @@ -0,0 +1,284 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +JPH_SUPPRESS_WARNINGS_STD_BEGIN +#include +#include +JPH_SUPPRESS_WARNINGS_STD_END + +#include +#include +#include + +#if defined(JPH_EXTERNAL_PROFILE) + +JPH_NAMESPACE_BEGIN + +/// Create this class on the stack to start sampling timing information of a particular scope. +/// +/// Left unimplemented intentionally. Needs to be implemented by the user of the library. +/// On construction a measurement should start, on destruction it should be stopped. +class alignas(16) ExternalProfileMeasurement : public NonCopyable +{ +public: + /// Constructor + ExternalProfileMeasurement(const char *inName, uint32 inColor = 0); + ~ExternalProfileMeasurement(); + +private: + uint8 mUserData[64]; +}; + +JPH_NAMESPACE_END + +////////////////////////////////////////////////////////////////////////////////////////// +// Macros to do the actual profiling +////////////////////////////////////////////////////////////////////////////////////////// + +JPH_SUPPRESS_WARNING_PUSH +JPH_CLANG_SUPPRESS_WARNING("-Wc++98-compat-pedantic") + +// Dummy implementations +#define JPH_PROFILE_THREAD_START(name) +#define JPH_PROFILE_THREAD_END() +#define JPH_PROFILE_NEXTFRAME() +#define JPH_PROFILE_DUMP(...) + +// Scope profiling measurement +#define JPH_PROFILE_TAG2(line) profile##line +#define JPH_PROFILE_TAG(line) JPH_PROFILE_TAG2(line) + +/// Macro to collect profiling information. +/// +/// Usage: +/// +/// { +/// JPH_PROFILE("Operation"); +/// do operation; +/// } +/// +#define JPH_PROFILE(...) ExternalProfileMeasurement JPH_PROFILE_TAG(__LINE__)(__VA_ARGS__) + +// Scope profiling for function +#define JPH_PROFILE_FUNCTION() JPH_PROFILE(JPH_FUNCTION_NAME) + +JPH_SUPPRESS_WARNING_POP + +#elif defined(JPH_PROFILE_ENABLED) + +JPH_NAMESPACE_BEGIN + +class ProfileSample; +class ProfileThread; + +/// Singleton class for managing profiling information +class JPH_EXPORT Profiler : public NonCopyable +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Constructor + Profiler() { UpdateReferenceTime(); } + + /// Increments the frame counter to provide statistics per frame + void NextFrame(); + + /// Dump profiling statistics at the start of the next frame + /// @param inTag If not empty, this overrides the auto incrementing number in the filename of the dump file + void Dump(const string_view &inTag = string_view()); + + /// Add a thread to be instrumented + void AddThread(ProfileThread *inThread); + + /// Remove a thread from being instrumented + void RemoveThread(ProfileThread *inThread); + + /// Singleton instance + static Profiler * sInstance; + +private: + /// Helper class to freeze ProfileSamples per thread while processing them + struct ThreadSamples + { + String mThreadName; + ProfileSample * mSamplesBegin; + ProfileSample * mSamplesEnd; + }; + + /// Helper class to aggregate ProfileSamples + class Aggregator + { + public: + /// Constructor + Aggregator(const char *inName) : mName(inName) { } + + /// Accumulate results for a measurement + void AccumulateMeasurement(uint64 inCyclesInCallWithChildren) + { + mCallCounter++; + mTotalCyclesInCallWithChildren += inCyclesInCallWithChildren; + mMinCyclesInCallWithChildren = min(inCyclesInCallWithChildren, mMinCyclesInCallWithChildren); + mMaxCyclesInCallWithChildren = max(inCyclesInCallWithChildren, mMaxCyclesInCallWithChildren); + } + + /// Sort descending by total cycles + bool operator < (const Aggregator &inRHS) const + { + return mTotalCyclesInCallWithChildren > inRHS.mTotalCyclesInCallWithChildren; + } + + /// Identification + const char * mName; ///< User defined name of this item + + /// Statistics + uint32 mCallCounter = 0; ///< Number of times AccumulateMeasurement was called + uint64 mTotalCyclesInCallWithChildren = 0; ///< Total amount of cycles spent in this scope + uint64 mMinCyclesInCallWithChildren = 0xffffffffffffffffUL; ///< Minimum amount of cycles spent per call + uint64 mMaxCyclesInCallWithChildren = 0; ///< Maximum amount of cycles spent per call + }; + + using Threads = Array; + using Aggregators = Array; + using KeyToAggregator = UnorderedMap; + + /// Helper function to aggregate profile sample data + static void sAggregate(int inDepth, uint32 inColor, ProfileSample *&ioSample, const ProfileSample *inEnd, Aggregators &ioAggregators, KeyToAggregator &ioKeyToAggregator); + + /// We measure the amount of ticks per second, this function resets the reference time point + void UpdateReferenceTime(); + + /// Get the amount of ticks per second, note that this number will never be fully accurate as the amount of ticks per second may vary with CPU load, so this number is only to be used to give an indication of time for profiling purposes + uint64 GetProcessorTicksPerSecond() const; + + /// Dump profiling statistics + void DumpInternal(); + void DumpChart(const char *inTag, const Threads &inThreads, const KeyToAggregator &inKeyToAggregators, const Aggregators &inAggregators); + + std::mutex mLock; ///< Lock that protects mThreads + uint64 mReferenceTick; ///< Tick count at the start of the frame + std::chrono::high_resolution_clock::time_point mReferenceTime; ///< Time at the start of the frame + Array mThreads; ///< List of all active threads + bool mDump = false; ///< When true, the samples are dumped next frame + String mDumpTag; ///< When not empty, this overrides the auto incrementing number of the dump filename +}; + +// Class that contains the information of a single scoped measurement +class alignas(16) JPH_EXPORT_GCC_BUG_WORKAROUND ProfileSample : public NonCopyable +{ +public: + JPH_OVERRIDE_NEW_DELETE + + const char * mName; ///< User defined name of this item + uint32 mColor; ///< Color to use for this sample + uint8 mDepth; ///< Calculated depth + uint8 mUnused[3]; + uint64 mStartCycle; ///< Cycle counter at start of measurement + uint64 mEndCycle; ///< Cycle counter at end of measurement +}; + +/// Collects all samples of a single thread +class ProfileThread : public NonCopyable +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Constructor + inline ProfileThread(const string_view &inThreadName); + inline ~ProfileThread(); + + static const uint cMaxSamples = 65536; + + String mThreadName; ///< Name of the thread that we're collecting information for + ProfileSample mSamples[cMaxSamples]; ///< Buffer of samples + uint mCurrentSample = 0; ///< Next position to write a sample to + +#ifdef JPH_SHARED_LIBRARY + JPH_EXPORT static void sSetInstance(ProfileThread *inInstance); + JPH_EXPORT static ProfileThread *sGetInstance(); +#else + static inline void sSetInstance(ProfileThread *inInstance) { sInstance = inInstance; } + static inline ProfileThread *sGetInstance() { return sInstance; } + +private: + static thread_local ProfileThread *sInstance; +#endif +}; + +/// Create this class on the stack to start sampling timing information of a particular scope +class JPH_EXPORT ProfileMeasurement : public NonCopyable +{ +public: + /// Constructor + inline ProfileMeasurement(const char *inName, uint32 inColor = 0); + inline ~ProfileMeasurement(); + +private: + ProfileSample * mSample; + ProfileSample mTemp; + + static bool sOutOfSamplesReported; +}; + +JPH_NAMESPACE_END + +#include "Profiler.inl" + +////////////////////////////////////////////////////////////////////////////////////////// +// Macros to do the actual profiling +////////////////////////////////////////////////////////////////////////////////////////// + +JPH_SUPPRESS_WARNING_PUSH +JPH_CLANG_SUPPRESS_WARNING("-Wc++98-compat-pedantic") + +/// Start instrumenting program +#define JPH_PROFILE_START(name) do { Profiler::sInstance = new Profiler; JPH_PROFILE_THREAD_START(name); } while (false) + +/// End instrumenting program +#define JPH_PROFILE_END() do { JPH_PROFILE_THREAD_END(); delete Profiler::sInstance; Profiler::sInstance = nullptr; } while (false) + +/// Start instrumenting a thread +#define JPH_PROFILE_THREAD_START(name) do { if (Profiler::sInstance) ProfileThread::sSetInstance(new ProfileThread(name)); } while (false) + +/// End instrumenting a thread +#define JPH_PROFILE_THREAD_END() do { delete ProfileThread::sGetInstance(); ProfileThread::sSetInstance(nullptr); } while (false) + +/// Scope profiling measurement +#define JPH_PROFILE_TAG2(line) profile##line +#define JPH_PROFILE_TAG(line) JPH_PROFILE_TAG2(line) +#define JPH_PROFILE(...) ProfileMeasurement JPH_PROFILE_TAG(__LINE__)(__VA_ARGS__) + +/// Scope profiling for function +#define JPH_PROFILE_FUNCTION() JPH_PROFILE(JPH_FUNCTION_NAME) + +/// Update frame counter +#define JPH_PROFILE_NEXTFRAME() Profiler::sInstance->NextFrame() + +/// Dump profiling info +#define JPH_PROFILE_DUMP(...) Profiler::sInstance->Dump(__VA_ARGS__) + +JPH_SUPPRESS_WARNING_POP + +#else + +////////////////////////////////////////////////////////////////////////////////////////// +// Dummy profiling instructions +////////////////////////////////////////////////////////////////////////////////////////// + +JPH_SUPPRESS_WARNING_PUSH +JPH_CLANG_SUPPRESS_WARNING("-Wc++98-compat-pedantic") + +#define JPH_PROFILE_START(name) +#define JPH_PROFILE_END() +#define JPH_PROFILE_THREAD_START(name) +#define JPH_PROFILE_THREAD_END() +#define JPH_PROFILE(...) +#define JPH_PROFILE_FUNCTION() +#define JPH_PROFILE_NEXTFRAME() +#define JPH_PROFILE_DUMP(...) + +JPH_SUPPRESS_WARNING_POP + +#endif diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Profiler.inl b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Profiler.inl new file mode 100644 index 00000000000..cc429ba51ab --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Profiler.inl @@ -0,0 +1,89 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +JPH_NAMESPACE_BEGIN + +////////////////////////////////////////////////////////////////////////////////////////// +// ProfileThread +////////////////////////////////////////////////////////////////////////////////////////// + +ProfileThread::ProfileThread(const string_view &inThreadName) : + mThreadName(inThreadName) +{ + Profiler::sInstance->AddThread(this); +} + +ProfileThread::~ProfileThread() +{ + Profiler::sInstance->RemoveThread(this); +} + +////////////////////////////////////////////////////////////////////////////////////////// +// ProfileMeasurement +////////////////////////////////////////////////////////////////////////////////////////// + +ProfileMeasurement::ProfileMeasurement(const char *inName, uint32 inColor) +{ + ProfileThread *current_thread = ProfileThread::sGetInstance(); + if (current_thread == nullptr) + { + // Thread not instrumented + mSample = nullptr; + } + else if (current_thread->mCurrentSample < ProfileThread::cMaxSamples) + { + // Get pointer to write data to + mSample = ¤t_thread->mSamples[current_thread->mCurrentSample++]; + + // Start constructing sample (will end up on stack) + mTemp.mName = inName; + mTemp.mColor = inColor; + + // Collect start sample last + mTemp.mStartCycle = GetProcessorTickCount(); + } + else + { + // Out of samples + if (!sOutOfSamplesReported) + { + Trace("ProfileMeasurement: Too many samples, some data will be lost!"); + sOutOfSamplesReported = true; + } + mSample = nullptr; + } +} + +ProfileMeasurement::~ProfileMeasurement() +{ + if (mSample != nullptr) + { + // Finalize sample + mTemp.mEndCycle = GetProcessorTickCount(); + + // Write it to the memory buffer bypassing the cache + static_assert(sizeof(ProfileSample) == 32, "Assume 32 bytes"); + static_assert(alignof(ProfileSample) == 16, "Assume 16 byte alignment"); + #if defined(JPH_USE_SSE) + const __m128i *src = reinterpret_cast(&mTemp); + __m128i *dst = reinterpret_cast<__m128i *>(mSample); + __m128i val = _mm_loadu_si128(src); + _mm_stream_si128(dst, val); + val = _mm_loadu_si128(src + 1); + _mm_stream_si128(dst + 1, val); + #elif defined(JPH_USE_NEON) + const int *src = reinterpret_cast(&mTemp); + int *dst = reinterpret_cast(mSample); + int32x4_t val = vld1q_s32(src); + vst1q_s32(dst, val); + val = vld1q_s32(src + 4); + vst1q_s32(dst + 4, val); + #else + memcpy(mSample, &mTemp, sizeof(ProfileSample)); + #endif + mSample = nullptr; + } +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/QuickSort.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/QuickSort.h new file mode 100644 index 00000000000..0b4a3ff9a9e --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/QuickSort.h @@ -0,0 +1,137 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2022 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +/// Helper function for QuickSort, will move the pivot element to inMiddle. +template +inline void QuickSortMedianOfThree(Iterator inFirst, Iterator inMiddle, Iterator inLast, Compare inCompare) +{ + // This should be guaranteed because we switch over to insertion sort when there's 32 or less elements + JPH_ASSERT(inFirst != inMiddle && inMiddle != inLast); + + if (inCompare(*inMiddle, *inFirst)) + swap(*inFirst, *inMiddle); + + if (inCompare(*inLast, *inFirst)) + swap(*inFirst, *inLast); + + if (inCompare(*inLast, *inMiddle)) + swap(*inMiddle, *inLast); +} + +/// Helper function for QuickSort using the Ninther method, will move the pivot element to inMiddle. +template +inline void QuickSortNinther(Iterator inFirst, Iterator inMiddle, Iterator inLast, Compare inCompare) +{ + // Divide the range in 8 equal parts (this means there are 9 points) + auto diff = (inLast - inFirst) >> 3; + auto two_diff = diff << 1; + + // Median of first 3 points + Iterator mid1 = inFirst + diff; + QuickSortMedianOfThree(inFirst, mid1, inFirst + two_diff, inCompare); + + // Median of second 3 points + QuickSortMedianOfThree(inMiddle - diff, inMiddle, inMiddle + diff, inCompare); + + // Median of third 3 points + Iterator mid3 = inLast - diff; + QuickSortMedianOfThree(inLast - two_diff, mid3, inLast, inCompare); + + // Determine the median of the 3 medians + QuickSortMedianOfThree(mid1, inMiddle, mid3, inCompare); +} + +/// Implementation of the quick sort algorithm. The STL version implementation is not consistent across platforms. +template +inline void QuickSort(Iterator inBegin, Iterator inEnd, Compare inCompare) +{ + // Implementation based on https://en.wikipedia.org/wiki/Quicksort using Hoare's partition scheme + + // Loop so that we only need to do 1 recursive call instead of 2. + for (;;) + { + // If there's less than 2 elements we're done + auto num_elements = inEnd - inBegin; + if (num_elements < 2) + return; + + // Fall back to insertion sort if there are too few elements + if (num_elements <= 32) + { + InsertionSort(inBegin, inEnd, inCompare); + return; + } + + // Determine pivot + Iterator pivot_iterator = inBegin + ((num_elements - 1) >> 1); + QuickSortNinther(inBegin, pivot_iterator, inEnd - 1, inCompare); + auto pivot = *pivot_iterator; + + // Left and right iterators + Iterator i = inBegin; + Iterator j = inEnd; + + for (;;) + { + // Find the first element that is bigger than the pivot + while (inCompare(*i, pivot)) + i++; + + // Find the last element that is smaller than the pivot + do + --j; + while (inCompare(pivot, *j)); + + // If the two iterators crossed, we're done + if (i >= j) + break; + + // Swap the elements + swap(*i, *j); + + // Note that the first while loop in this function should + // have been do i++ while (...) but since we cannot decrement + // the iterator from inBegin we left that out, so we need to do + // it here. + ++i; + } + + // Include the middle element on the left side + j++; + + // Check which partition is smaller + if (j - inBegin < inEnd - j) + { + // Left side is smaller, recurse to left first + QuickSort(inBegin, j, inCompare); + + // Loop again with the right side to avoid a call + inBegin = j; + } + else + { + // Right side is smaller, recurse to right first + QuickSort(j, inEnd, inCompare); + + // Loop again with the left side to avoid a call + inEnd = j; + } + } +} + +/// Implementation of quick sort algorithm without comparator. +template +inline void QuickSort(Iterator inBegin, Iterator inEnd) +{ + std::less<> compare; + QuickSort(inBegin, inEnd, compare); +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/RTTI.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/RTTI.cpp new file mode 100644 index 00000000000..81915feaf19 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/RTTI.cpp @@ -0,0 +1,143 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include + +JPH_NAMESPACE_BEGIN + +////////////////////////////////////////////////////////////////////////////////////////// +// RTTI +////////////////////////////////////////////////////////////////////////////////////////// + +RTTI::RTTI(const char *inName, int inSize, pCreateObjectFunction inCreateObject, pDestructObjectFunction inDestructObject) : + mName(inName), + mSize(inSize), + mCreate(inCreateObject), + mDestruct(inDestructObject) +{ + JPH_ASSERT(inDestructObject != nullptr, "Object cannot be destructed"); +} + +RTTI::RTTI(const char *inName, int inSize, pCreateObjectFunction inCreateObject, pDestructObjectFunction inDestructObject, pCreateRTTIFunction inCreateRTTI) : + mName(inName), + mSize(inSize), + mCreate(inCreateObject), + mDestruct(inDestructObject) +{ + JPH_ASSERT(inDestructObject != nullptr, "Object cannot be destructed"); + + inCreateRTTI(*this); +} + +int RTTI::GetBaseClassCount() const +{ + return (int)mBaseClasses.size(); +} + +const RTTI *RTTI::GetBaseClass(int inIdx) const +{ + return mBaseClasses[inIdx].mRTTI; +} + +uint32 RTTI::GetHash() const +{ + // Perform diffusion step to get from 64 to 32 bits (see https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function) + uint64 hash = HashString(mName); + return (uint32)(hash ^ (hash >> 32)); +} + +void *RTTI::CreateObject() const +{ + return IsAbstract()? nullptr : mCreate(); +} + +void RTTI::DestructObject(void *inObject) const +{ + mDestruct(inObject); +} + +void RTTI::AddBaseClass(const RTTI *inRTTI, int inOffset) +{ + JPH_ASSERT(inOffset >= 0 && inOffset < mSize, "Base class not contained in derived class"); + + // Add base class + BaseClass base; + base.mRTTI = inRTTI; + base.mOffset = inOffset; + mBaseClasses.push_back(base); + + // Add attributes of base class + for (const SerializableAttribute &a : inRTTI->mAttributes) + mAttributes.push_back(SerializableAttribute(a, inOffset)); +} + +bool RTTI::operator == (const RTTI &inRHS) const +{ + // Compare addresses + if (this == &inRHS) + return true; + + // Check that the names differ (if that is the case we probably have two instances + // of the same attribute info across the program, probably the second is in a DLL) + JPH_ASSERT(strcmp(mName, inRHS.mName) != 0); + return false; +} + +bool RTTI::IsKindOf(const RTTI *inRTTI) const +{ + // Check if this is the same type + if (this == inRTTI) + return true; + + // Check all base classes + for (const BaseClass &b : mBaseClasses) + if (b.mRTTI->IsKindOf(inRTTI)) + return true; + + return false; +} + +const void *RTTI::CastTo(const void *inObject, const RTTI *inRTTI) const +{ + JPH_ASSERT(inObject != nullptr); + + // Check if this is the same type + if (this == inRTTI) + return inObject; + + // Check all base classes + for (const BaseClass &b : mBaseClasses) + { + // Cast the pointer to the base class + const void *casted = (const void *)(((const uint8 *)inObject) + b.mOffset); + + // Test base class + const void *rv = b.mRTTI->CastTo(casted, inRTTI); + if (rv != nullptr) + return rv; + } + + // Not possible to cast + return nullptr; +} + +void RTTI::AddAttribute(const SerializableAttribute &inAttribute) +{ + mAttributes.push_back(inAttribute); +} + +int RTTI::GetAttributeCount() const +{ + return (int)mAttributes.size(); +} + +const SerializableAttribute &RTTI::GetAttribute(int inIdx) const +{ + return mAttributes[inIdx]; +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/RTTI.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/RTTI.h new file mode 100644 index 00000000000..d9816938fca --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/RTTI.h @@ -0,0 +1,436 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +////////////////////////////////////////////////////////////////////////////////////////// +// RTTI +////////////////////////////////////////////////////////////////////////////////////////// + +/// Light weight runtime type information system. This way we don't need to turn +/// on the default RTTI system of the compiler (introducing a possible overhead for every +/// class) +/// +/// Notes: +/// - An extra virtual member function is added. This adds 8 bytes to the size of +/// an instance of the class (unless you are already using virtual functions). +/// +/// To use RTTI on a specific class use: +/// +/// Header file: +/// +/// class Foo +/// { +/// JPH_DECLARE_RTTI_VIRTUAL_BASE(Foo) +/// } +/// +/// class Bar : public Foo +/// { +/// JPH_DECLARE_RTTI_VIRTUAL(Bar) +/// }; +/// +/// Implementation file: +/// +/// JPH_IMPLEMENT_RTTI_VIRTUAL_BASE(Foo) +/// { +/// } +/// +/// JPH_IMPLEMENT_RTTI_VIRTUAL(Bar) +/// { +/// JPH_ADD_BASE_CLASS(Bar, Foo) // Multiple inheritance is allowed, just do JPH_ADD_BASE_CLASS for every base class +/// } +/// +/// For abstract classes use: +/// +/// Header file: +/// +/// class Foo +/// { +/// JPH_DECLARE_RTTI_ABSTRACT_BASE(Foo) +/// +/// public: +/// virtual void AbstractFunction() = 0; +/// } +/// +/// class Bar : public Foo +/// { +/// JPH_DECLARE_RTTI_VIRTUAL(Bar) +/// +/// public: +/// virtual void AbstractFunction() { } // Function is now implemented so this class is no longer abstract +/// }; +/// +/// Implementation file: +/// +/// JPH_IMPLEMENT_RTTI_ABSTRACT_BASE(Foo) +/// { +/// } +/// +/// JPH_IMPLEMENT_RTTI_VIRTUAL(Bar) +/// { +/// JPH_ADD_BASE_CLASS(Bar, Foo) +/// } +/// +/// Example of usage in a program: +/// +/// Foo *foo_ptr = new Foo; +/// Foo *bar_ptr = new Bar; +/// +/// IsType(foo_ptr, RTTI(Bar)) returns false +/// IsType(bar_ptr, RTTI(Bar)) returns true +/// +/// IsKindOf(foo_ptr, RTTI(Bar)) returns false +/// IsKindOf(bar_ptr, RTTI(Foo)) returns true +/// IsKindOf(bar_ptr, RTTI(Bar)) returns true +/// +/// StaticCast(foo_ptr) asserts and returns foo_ptr casted to pBar +/// StaticCast(bar_ptr) returns bar_ptr casted to pBar +/// +/// DynamicCast(foo_ptr) returns nullptr +/// DynamicCast(bar_ptr) returns bar_ptr casted to pBar +/// +/// Other feature of DynamicCast: +/// +/// class A { int data[5]; }; +/// class B { int data[7]; }; +/// class C : public A, public B { int data[9]; }; +/// +/// C *c = new C; +/// A *a = c; +/// +/// Note that: +/// +/// B *b = (B *)a; +/// +/// generates an invalid pointer, +/// +/// B *b = StaticCast(a); +/// +/// doesn't compile, and +/// +/// B *b = DynamicCast(a); +/// +/// does the correct cast +class JPH_EXPORT RTTI +{ +public: + /// Function to create an object + using pCreateObjectFunction = void *(*)(); + + /// Function to destroy an object + using pDestructObjectFunction = void (*)(void *inObject); + + /// Function to initialize the runtime type info structure + using pCreateRTTIFunction = void (*)(RTTI &inRTTI); + + /// Constructor + RTTI(const char *inName, int inSize, pCreateObjectFunction inCreateObject, pDestructObjectFunction inDestructObject); + RTTI(const char *inName, int inSize, pCreateObjectFunction inCreateObject, pDestructObjectFunction inDestructObject, pCreateRTTIFunction inCreateRTTI); + + // Properties + inline const char * GetName() const { return mName; } + void SetName(const char *inName) { mName = inName; } + inline int GetSize() const { return mSize; } + bool IsAbstract() const { return mCreate == nullptr || mDestruct == nullptr; } + int GetBaseClassCount() const; + const RTTI * GetBaseClass(int inIdx) const; + uint32 GetHash() const; + + /// Create an object of this type (returns nullptr if the object is abstract) + void * CreateObject() const; + + /// Destruct object of this type (does nothing if the object is abstract) + void DestructObject(void *inObject) const; + + /// Add base class + void AddBaseClass(const RTTI *inRTTI, int inOffset); + + /// Equality operators + bool operator == (const RTTI &inRHS) const; + bool operator != (const RTTI &inRHS) const { return !(*this == inRHS); } + + /// Test if this class is derived from class of type inRTTI + bool IsKindOf(const RTTI *inRTTI) const; + + /// Cast inObject of this type to object of type inRTTI, returns nullptr if the cast is unsuccessful + const void * CastTo(const void *inObject, const RTTI *inRTTI) const; + + /// Attribute access + void AddAttribute(const SerializableAttribute &inAttribute); + int GetAttributeCount() const; + const SerializableAttribute & GetAttribute(int inIdx) const; + +protected: + /// Base class information + struct BaseClass + { + const RTTI * mRTTI; + int mOffset; + }; + + const char * mName; ///< Class name + int mSize; ///< Class size + StaticArray mBaseClasses; ///< Names of base classes + pCreateObjectFunction mCreate; ///< Pointer to a function that will create a new instance of this class + pDestructObjectFunction mDestruct; ///< Pointer to a function that will destruct an object of this class + StaticArray mAttributes; ///< All attributes of this class +}; + +////////////////////////////////////////////////////////////////////////////////////////// +// Add run time type info to types that don't have virtual functions +////////////////////////////////////////////////////////////////////////////////////////// + +// JPH_DECLARE_RTTI_NON_VIRTUAL +#define JPH_DECLARE_RTTI_NON_VIRTUAL(linkage, class_name) \ +public: \ + JPH_OVERRIDE_NEW_DELETE \ + friend linkage RTTI * GetRTTIOfType(class_name *); \ + friend inline const RTTI * GetRTTI([[maybe_unused]] const class_name *inObject) { return GetRTTIOfType(static_cast(nullptr)); }\ + static void sCreateRTTI(RTTI &inRTTI); \ + +// JPH_IMPLEMENT_RTTI_NON_VIRTUAL +#define JPH_IMPLEMENT_RTTI_NON_VIRTUAL(class_name) \ + RTTI * GetRTTIOfType(class_name *) \ + { \ + static RTTI rtti(#class_name, sizeof(class_name), []() -> void * { return new class_name; }, [](void *inObject) { delete (class_name *)inObject; }, &class_name::sCreateRTTI); \ + return &rtti; \ + } \ + void class_name::sCreateRTTI(RTTI &inRTTI) \ + +////////////////////////////////////////////////////////////////////////////////////////// +// Same as above, but when you cannot insert the declaration in the class +// itself, for example for templates and third party classes +////////////////////////////////////////////////////////////////////////////////////////// + +// JPH_DECLARE_RTTI_OUTSIDE_CLASS +#define JPH_DECLARE_RTTI_OUTSIDE_CLASS(linkage, class_name) \ + linkage RTTI * GetRTTIOfType(class_name *); \ + inline const RTTI * GetRTTI(const class_name *inObject) { return GetRTTIOfType((class_name *)nullptr); }\ + void CreateRTTI##class_name(RTTI &inRTTI); \ + +// JPH_IMPLEMENT_RTTI_OUTSIDE_CLASS +#define JPH_IMPLEMENT_RTTI_OUTSIDE_CLASS(class_name) \ + RTTI * GetRTTIOfType(class_name *) \ + { \ + static RTTI rtti((const char *)#class_name, sizeof(class_name), []() -> void * { return new class_name; }, [](void *inObject) { delete (class_name *)inObject; }, &CreateRTTI##class_name); \ + return &rtti; \ + } \ + void CreateRTTI##class_name(RTTI &inRTTI) + +////////////////////////////////////////////////////////////////////////////////////////// +// Same as above, but for classes that have virtual functions +////////////////////////////////////////////////////////////////////////////////////////// + +#define JPH_DECLARE_RTTI_HELPER(linkage, class_name, modifier) \ +public: \ + JPH_OVERRIDE_NEW_DELETE \ + friend linkage RTTI * GetRTTIOfType(class_name *); \ + friend inline const RTTI * GetRTTI(const class_name *inObject) { return inObject->GetRTTI(); } \ + virtual const RTTI * GetRTTI() const modifier; \ + virtual const void * CastTo(const RTTI *inRTTI) const modifier; \ + static void sCreateRTTI(RTTI &inRTTI); \ + +// JPH_DECLARE_RTTI_VIRTUAL - for derived classes with RTTI +#define JPH_DECLARE_RTTI_VIRTUAL(linkage, class_name) \ + JPH_DECLARE_RTTI_HELPER(linkage, class_name, override) + +// JPH_IMPLEMENT_RTTI_VIRTUAL +#define JPH_IMPLEMENT_RTTI_VIRTUAL(class_name) \ + RTTI * GetRTTIOfType(class_name *) \ + { \ + static RTTI rtti(#class_name, sizeof(class_name), []() -> void * { return new class_name; }, [](void *inObject) { delete (class_name *)inObject; }, &class_name::sCreateRTTI); \ + return &rtti; \ + } \ + const RTTI * class_name::GetRTTI() const \ + { \ + return JPH_RTTI(class_name); \ + } \ + const void * class_name::CastTo(const RTTI *inRTTI) const \ + { \ + return JPH_RTTI(class_name)->CastTo((const void *)this, inRTTI); \ + } \ + void class_name::sCreateRTTI(RTTI &inRTTI) \ + +// JPH_DECLARE_RTTI_VIRTUAL_BASE - for concrete base class that has RTTI +#define JPH_DECLARE_RTTI_VIRTUAL_BASE(linkage, class_name) \ + JPH_DECLARE_RTTI_HELPER(linkage, class_name, ) + +// JPH_IMPLEMENT_RTTI_VIRTUAL_BASE +#define JPH_IMPLEMENT_RTTI_VIRTUAL_BASE(class_name) \ + JPH_IMPLEMENT_RTTI_VIRTUAL(class_name) + +// JPH_DECLARE_RTTI_ABSTRACT - for derived abstract class that have RTTI +#define JPH_DECLARE_RTTI_ABSTRACT(linkage, class_name) \ + JPH_DECLARE_RTTI_HELPER(linkage, class_name, override) + +// JPH_IMPLEMENT_RTTI_ABSTRACT +#define JPH_IMPLEMENT_RTTI_ABSTRACT(class_name) \ + RTTI * GetRTTIOfType(class_name *) \ + { \ + static RTTI rtti(#class_name, sizeof(class_name), nullptr, [](void *inObject) { delete (class_name *)inObject; }, &class_name::sCreateRTTI); \ + return &rtti; \ + } \ + const RTTI * class_name::GetRTTI() const \ + { \ + return JPH_RTTI(class_name); \ + } \ + const void * class_name::CastTo(const RTTI *inRTTI) const \ + { \ + return JPH_RTTI(class_name)->CastTo((const void *)this, inRTTI); \ + } \ + void class_name::sCreateRTTI(RTTI &inRTTI) \ + +// JPH_DECLARE_RTTI_ABSTRACT_BASE - for abstract base class that has RTTI +#define JPH_DECLARE_RTTI_ABSTRACT_BASE(linkage, class_name) \ + JPH_DECLARE_RTTI_HELPER(linkage, class_name, ) + +// JPH_IMPLEMENT_RTTI_ABSTRACT_BASE +#define JPH_IMPLEMENT_RTTI_ABSTRACT_BASE(class_name) \ + JPH_IMPLEMENT_RTTI_ABSTRACT(class_name) + +////////////////////////////////////////////////////////////////////////////////////////// +// Declare an RTTI class for registering with the factory +////////////////////////////////////////////////////////////////////////////////////////// + +#define JPH_DECLARE_RTTI_FOR_FACTORY(linkage, class_name) \ + linkage RTTI * GetRTTIOfType(class class_name *); + +#define JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(linkage, name_space, class_name) \ + namespace name_space { \ + class class_name; \ + linkage RTTI * GetRTTIOfType(class class_name *); \ + } + +////////////////////////////////////////////////////////////////////////////////////////// +// Find the RTTI of a class +////////////////////////////////////////////////////////////////////////////////////////// + +#define JPH_RTTI(class_name) GetRTTIOfType(static_cast(nullptr)) + +////////////////////////////////////////////////////////////////////////////////////////// +// Macro to rename a class, useful for embedded classes: +// +// class A { class B { }; } +// +// Now use JPH_RENAME_CLASS(B, A::B) to avoid conflicts with other classes named B +////////////////////////////////////////////////////////////////////////////////////////// + +// JPH_RENAME_CLASS +#define JPH_RENAME_CLASS(class_name, new_name) \ + inRTTI.SetName(#new_name); + +////////////////////////////////////////////////////////////////////////////////////////// +// Macro to add base classes +////////////////////////////////////////////////////////////////////////////////////////// + +/// Define very dirty macro to get the offset of a baseclass into a class +#define JPH_BASE_CLASS_OFFSET(inClass, inBaseClass) ((int(uint64((inBaseClass *)((inClass *)0x10000))))-0x10000) + +// JPH_ADD_BASE_CLASS +#define JPH_ADD_BASE_CLASS(class_name, base_class_name) \ + inRTTI.AddBaseClass(JPH_RTTI(base_class_name), JPH_BASE_CLASS_OFFSET(class_name, base_class_name)); + +////////////////////////////////////////////////////////////////////////////////////////// +// Macros and templates to identify a class +////////////////////////////////////////////////////////////////////////////////////////// + +/// Check if inObject is of DstType +template +inline bool IsType(const Type *inObject, const RTTI *inRTTI) +{ + return inObject == nullptr || *inObject->GetRTTI() == *inRTTI; +} + +template +inline bool IsType(const RefConst &inObject, const RTTI *inRTTI) +{ + return inObject == nullptr || *inObject->GetRTTI() == *inRTTI; +} + +template +inline bool IsType(const Ref &inObject, const RTTI *inRTTI) +{ + return inObject == nullptr || *inObject->GetRTTI() == *inRTTI; +} + +/// Check if inObject is or is derived from DstType +template +inline bool IsKindOf(const Type *inObject, const RTTI *inRTTI) +{ + return inObject == nullptr || inObject->GetRTTI()->IsKindOf(inRTTI); +} + +template +inline bool IsKindOf(const RefConst &inObject, const RTTI *inRTTI) +{ + return inObject == nullptr || inObject->GetRTTI()->IsKindOf(inRTTI); +} + +template +inline bool IsKindOf(const Ref &inObject, const RTTI *inRTTI) +{ + return inObject == nullptr || inObject->GetRTTI()->IsKindOf(inRTTI); +} + +/// Cast inObject to DstType, asserts on failure +template +inline const DstType *StaticCast(const SrcType *inObject) +{ + JPH_ASSERT(IsKindOf(inObject, JPH_RTTI(DstType)), "Invalid cast"); + return static_cast(inObject); +} + +template +inline DstType *StaticCast(SrcType *inObject) +{ + JPH_ASSERT(IsKindOf(inObject, JPH_RTTI(DstType)), "Invalid cast"); + return static_cast(inObject); +} + +template +inline RefConst StaticCast(RefConst &inObject) +{ + JPH_ASSERT(IsKindOf(inObject, JPH_RTTI(DstType)), "Invalid cast"); + return static_cast(inObject.GetPtr()); +} + +template +inline Ref StaticCast(Ref &inObject) +{ + JPH_ASSERT(IsKindOf(inObject, JPH_RTTI(DstType)), "Invalid cast"); + return static_cast(inObject.GetPtr()); +} + +/// Cast inObject to DstType, returns nullptr on failure +template +inline const DstType *DynamicCast(const SrcType *inObject) +{ + return inObject != nullptr? reinterpret_cast(inObject->CastTo(JPH_RTTI(DstType))) : nullptr; +} + +template +inline DstType *DynamicCast(SrcType *inObject) +{ + return inObject != nullptr? const_cast(reinterpret_cast(inObject->CastTo(JPH_RTTI(DstType)))) : nullptr; +} + +template +inline RefConst DynamicCast(RefConst &inObject) +{ + return inObject != nullptr? reinterpret_cast(inObject->CastTo(JPH_RTTI(DstType))) : nullptr; +} + +template +inline Ref DynamicCast(Ref &inObject) +{ + return inObject != nullptr? const_cast(reinterpret_cast(inObject->CastTo(JPH_RTTI(DstType)))) : nullptr; +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Reference.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Reference.h new file mode 100644 index 00000000000..d6fdf1da512 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Reference.h @@ -0,0 +1,226 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +// Forward declares +template class Ref; +template class RefConst; + +/// Simple class to facilitate reference counting / releasing +/// Derive your class from RefTarget and you can reference it by using Ref or RefConst +/// +/// Reference counting classes keep an integer which indicates how many references +/// to the object are active. Reference counting objects are derived from RefTarget +/// and staT & their life with a reference count of zero. They can then be assigned +/// to equivalents of pointers (Ref) which will increase the reference count immediately. +/// If the destructor of Ref is called or another object is assigned to the reference +/// counting pointer it will decrease the reference count of the object again. If this +/// reference count becomes zero, the object is destroyed. +/// +/// This provides a very powerful mechanism to prevent memory leaks, but also gives +/// some responsibility to the programmer. The most notable point is that you cannot +/// have one object reference another and have the other reference the first one +/// back, because this way the reference count of both objects will never become +/// lower than 1, resulting in a memory leak. By carefully designing your classes +/// (and particularly identifying who owns who in the class hierarchy) you can avoid +/// these problems. +template +class RefTarget +{ +public: + /// Constructor + inline RefTarget() = default; + inline RefTarget(const RefTarget &) { /* Do not copy refcount */ } + inline ~RefTarget() { JPH_IF_ENABLE_ASSERTS(uint32 value = mRefCount.load(memory_order_relaxed);) JPH_ASSERT(value == 0 || value == cEmbedded); } ///< assert no one is referencing us + + /// Mark this class as embedded, this means the type can be used in a compound or constructed on the stack. + /// The Release function will never destruct the object, it is assumed the destructor will be called by whoever allocated + /// the object and at that point in time it is checked that no references are left to the structure. + inline void SetEmbedded() const { JPH_IF_ENABLE_ASSERTS(uint32 old = ) mRefCount.fetch_add(cEmbedded, memory_order_relaxed); JPH_ASSERT(old < cEmbedded); } + + /// Assignment operator + inline RefTarget & operator = (const RefTarget &) { /* Don't copy refcount */ return *this; } + + /// Get current refcount of this object + uint32 GetRefCount() const { return mRefCount.load(memory_order_relaxed); } + + /// Add or release a reference to this object + inline void AddRef() const + { + // Adding a reference can use relaxed memory ordering + mRefCount.fetch_add(1, memory_order_relaxed); + } + + inline void Release() const + { + // Releasing a reference must use release semantics... + if (mRefCount.fetch_sub(1, memory_order_release) == 1) + { + // ... so that we can use acquire to ensure that we see any updates from other threads that released a ref before deleting the object + atomic_thread_fence(memory_order_acquire); + delete static_cast(this); + } + } + + /// INTERNAL HELPER FUNCTION USED BY SERIALIZATION + static int sInternalGetRefCountOffset() { return offsetof(T, mRefCount); } + +protected: + static constexpr uint32 cEmbedded = 0x0ebedded; ///< A large value that gets added to the refcount to mark the object as embedded + + mutable atomic mRefCount = 0; ///< Current reference count +}; + +/// Pure virtual version of RefTarget +class JPH_EXPORT RefTargetVirtual +{ +public: + /// Virtual destructor + virtual ~RefTargetVirtual() = default; + + /// Virtual add reference + virtual void AddRef() = 0; + + /// Virtual release reference + virtual void Release() = 0; +}; + +/// Class for automatic referencing, this is the equivalent of a pointer to type T +/// if you assign a value to this class it will increment the reference count by one +/// of this object, and if you assign something else it will decrease the reference +/// count of the first object again. If it reaches a reference count of zero it will +/// be deleted +template +class Ref +{ +public: + /// Constructor + inline Ref() : mPtr(nullptr) { } + inline Ref(T *inRHS) : mPtr(inRHS) { AddRef(); } + inline Ref(const Ref &inRHS) : mPtr(inRHS.mPtr) { AddRef(); } + inline Ref(Ref &&inRHS) noexcept : mPtr(inRHS.mPtr) { inRHS.mPtr = nullptr; } + inline ~Ref() { Release(); } + + /// Assignment operators + inline Ref & operator = (T *inRHS) { if (mPtr != inRHS) { Release(); mPtr = inRHS; AddRef(); } return *this; } + inline Ref & operator = (const Ref &inRHS) { if (mPtr != inRHS.mPtr) { Release(); mPtr = inRHS.mPtr; AddRef(); } return *this; } + inline Ref & operator = (Ref &&inRHS) noexcept { if (mPtr != inRHS.mPtr) { Release(); mPtr = inRHS.mPtr; inRHS.mPtr = nullptr; } return *this; } + + /// Casting operators + inline operator T *() const { return mPtr; } + + /// Access like a normal pointer + inline T * operator -> () const { return mPtr; } + inline T & operator * () const { return *mPtr; } + + /// Comparison + inline bool operator == (const T * inRHS) const { return mPtr == inRHS; } + inline bool operator == (const Ref &inRHS) const { return mPtr == inRHS.mPtr; } + inline bool operator != (const T * inRHS) const { return mPtr != inRHS; } + inline bool operator != (const Ref &inRHS) const { return mPtr != inRHS.mPtr; } + + /// Get pointer + inline T * GetPtr() const { return mPtr; } + + /// INTERNAL HELPER FUNCTION USED BY SERIALIZATION + void ** InternalGetPointer() { return reinterpret_cast(&mPtr); } + +private: + template friend class RefConst; + + /// Use "variable = nullptr;" to release an object, do not call these functions + inline void AddRef() { if (mPtr != nullptr) mPtr->AddRef(); } + inline void Release() { if (mPtr != nullptr) mPtr->Release(); } + + T * mPtr; ///< Pointer to object that we are reference counting +}; + +/// Class for automatic referencing, this is the equivalent of a CONST pointer to type T +/// if you assign a value to this class it will increment the reference count by one +/// of this object, and if you assign something else it will decrease the reference +/// count of the first object again. If it reaches a reference count of zero it will +/// be deleted +template +class RefConst +{ +public: + /// Constructor + inline RefConst() : mPtr(nullptr) { } + inline RefConst(const T * inRHS) : mPtr(inRHS) { AddRef(); } + inline RefConst(const RefConst &inRHS) : mPtr(inRHS.mPtr) { AddRef(); } + inline RefConst(RefConst &&inRHS) noexcept : mPtr(inRHS.mPtr) { inRHS.mPtr = nullptr; } + inline RefConst(const Ref &inRHS) : mPtr(inRHS.mPtr) { AddRef(); } + inline RefConst(Ref &&inRHS) noexcept : mPtr(inRHS.mPtr) { inRHS.mPtr = nullptr; } + inline ~RefConst() { Release(); } + + /// Assignment operators + inline RefConst & operator = (const T * inRHS) { if (mPtr != inRHS) { Release(); mPtr = inRHS; AddRef(); } return *this; } + inline RefConst & operator = (const RefConst &inRHS) { if (mPtr != inRHS.mPtr) { Release(); mPtr = inRHS.mPtr; AddRef(); } return *this; } + inline RefConst & operator = (RefConst &&inRHS) noexcept { if (mPtr != inRHS.mPtr) { Release(); mPtr = inRHS.mPtr; inRHS.mPtr = nullptr; } return *this; } + inline RefConst & operator = (const Ref &inRHS) { if (mPtr != inRHS.mPtr) { Release(); mPtr = inRHS.mPtr; AddRef(); } return *this; } + inline RefConst & operator = (Ref &&inRHS) noexcept { if (mPtr != inRHS.mPtr) { Release(); mPtr = inRHS.mPtr; inRHS.mPtr = nullptr; } return *this; } + + /// Casting operators + inline operator const T * () const { return mPtr; } + + /// Access like a normal pointer + inline const T * operator -> () const { return mPtr; } + inline const T & operator * () const { return *mPtr; } + + /// Comparison + inline bool operator == (const T * inRHS) const { return mPtr == inRHS; } + inline bool operator == (const RefConst &inRHS) const { return mPtr == inRHS.mPtr; } + inline bool operator == (const Ref &inRHS) const { return mPtr == inRHS.mPtr; } + inline bool operator != (const T * inRHS) const { return mPtr != inRHS; } + inline bool operator != (const RefConst &inRHS) const { return mPtr != inRHS.mPtr; } + inline bool operator != (const Ref &inRHS) const { return mPtr != inRHS.mPtr; } + + /// Get pointer + inline const T * GetPtr() const { return mPtr; } + + /// INTERNAL HELPER FUNCTION USED BY SERIALIZATION + void ** InternalGetPointer() { return const_cast(reinterpret_cast(&mPtr)); } + +private: + /// Use "variable = nullptr;" to release an object, do not call these functions + inline void AddRef() { if (mPtr != nullptr) mPtr->AddRef(); } + inline void Release() { if (mPtr != nullptr) mPtr->Release(); } + + const T * mPtr; ///< Pointer to object that we are reference counting +}; + +JPH_NAMESPACE_END + +JPH_SUPPRESS_WARNING_PUSH +JPH_CLANG_SUPPRESS_WARNING("-Wc++98-compat") + +namespace std +{ + /// Declare std::hash for Ref + template + struct hash> + { + size_t operator () (const JPH::Ref &inRHS) const + { + return hash { }(inRHS.GetPtr()); + } + }; + + /// Declare std::hash for RefConst + template + struct hash> + { + size_t operator () (const JPH::RefConst &inRHS) const + { + return hash { }(inRHS.GetPtr()); + } + }; +} + +JPH_SUPPRESS_WARNING_POP diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Result.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Result.h new file mode 100644 index 00000000000..da3b98cb39e --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Result.h @@ -0,0 +1,177 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +JPH_NAMESPACE_BEGIN + +// GCC doesn't properly detect that mState is used to ensure that mResult is initialized +JPH_GCC_SUPPRESS_WARNING("-Wmaybe-uninitialized") + +/// Helper class that either contains a valid result or an error +template +class Result +{ +public: + /// Default constructor + Result() { } + + /// Copy constructor + Result(const Result &inRHS) : + mState(inRHS.mState) + { + switch (inRHS.mState) + { + case EState::Valid: + ::new (&mResult) Type (inRHS.mResult); + break; + + case EState::Error: + ::new (&mError) String(inRHS.mError); + break; + + case EState::Invalid: + break; + } + } + + /// Move constructor + Result(Result &&inRHS) noexcept : + mState(inRHS.mState) + { + switch (inRHS.mState) + { + case EState::Valid: + ::new (&mResult) Type (std::move(inRHS.mResult)); + break; + + case EState::Error: + ::new (&mError) String(std::move(inRHS.mError)); + break; + + case EState::Invalid: + break; + } + + // Don't reset the state of inRHS, the destructors still need to be called after a move operation + } + + /// Destructor + ~Result() { Clear(); } + + /// Copy assignment + Result & operator = (const Result &inRHS) + { + Clear(); + + mState = inRHS.mState; + + switch (inRHS.mState) + { + case EState::Valid: + ::new (&mResult) Type (inRHS.mResult); + break; + + case EState::Error: + ::new (&mError) String(inRHS.mError); + break; + + case EState::Invalid: + break; + } + + return *this; + } + + /// Move assignment + Result & operator = (Result &&inRHS) noexcept + { + Clear(); + + mState = inRHS.mState; + + switch (inRHS.mState) + { + case EState::Valid: + ::new (&mResult) Type (std::move(inRHS.mResult)); + break; + + case EState::Error: + ::new (&mError) String(std::move(inRHS.mError)); + break; + + case EState::Invalid: + break; + } + + // Don't reset the state of inRHS, the destructors still need to be called after a move operation + + return *this; + } + + /// Clear result or error + void Clear() + { + switch (mState) + { + case EState::Valid: + mResult.~Type(); + break; + + case EState::Error: + mError.~String(); + break; + + case EState::Invalid: + break; + } + + mState = EState::Invalid; + } + + /// Checks if the result is still uninitialized + bool IsEmpty() const { return mState == EState::Invalid; } + + /// Checks if the result is valid + bool IsValid() const { return mState == EState::Valid; } + + /// Get the result value + const Type & Get() const { JPH_ASSERT(IsValid()); return mResult; } + + /// Set the result value + void Set(const Type &inResult) { Clear(); ::new (&mResult) Type(inResult); mState = EState::Valid; } + + /// Set the result value (move value) + void Set(Type &&inResult) { Clear(); ::new (&mResult) Type(std::move(inResult)); mState = EState::Valid; } + + /// Check if we had an error + bool HasError() const { return mState == EState::Error; } + + /// Get the error value + const String & GetError() const { JPH_ASSERT(HasError()); return mError; } + + /// Set an error value + void SetError(const char *inError) { Clear(); ::new (&mError) String(inError); mState = EState::Error; } + void SetError(const string_view &inError) { Clear(); ::new (&mError) String(inError); mState = EState::Error; } + void SetError(String &&inError) { Clear(); ::new (&mError) String(std::move(inError)); mState = EState::Error; } + +private: + union + { + Type mResult; ///< The actual result object + String mError; ///< The error description if the result failed + }; + + /// State of the result + enum class EState : uint8 + { + Invalid, + Valid, + Error + }; + + EState mState = EState::Invalid; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/STLAlignedAllocator.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/STLAlignedAllocator.h new file mode 100644 index 00000000000..e982ea0238d --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/STLAlignedAllocator.h @@ -0,0 +1,66 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +JPH_NAMESPACE_BEGIN + +/// STL allocator that takes care that memory is aligned to N bytes +template +class STLAlignedAllocator +{ +public: + using value_type = T; + + /// Pointer to type + using pointer = T *; + using const_pointer = const T *; + + /// Reference to type. + /// Can be removed in C++20. + using reference = T &; + using const_reference = const T &; + + using size_type = size_t; + using difference_type = ptrdiff_t; + + /// Constructor + inline STLAlignedAllocator() = default; + + /// Constructor from other allocator + template + inline explicit STLAlignedAllocator(const STLAlignedAllocator &) { } + + /// Allocate memory + inline pointer allocate(size_type inN) + { + return (pointer)AlignedAllocate(inN * sizeof(value_type), N); + } + + /// Free memory + inline void deallocate(pointer inPointer, size_type) + { + AlignedFree(inPointer); + } + + /// Allocators are stateless so assumed to be equal + inline bool operator == (const STLAlignedAllocator &) const + { + return true; + } + + inline bool operator != (const STLAlignedAllocator &) const + { + return false; + } + + /// Converting to allocator for other type + template + struct rebind + { + using other = STLAlignedAllocator; + }; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/STLAllocator.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/STLAllocator.h new file mode 100644 index 00000000000..96417b0cbd1 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/STLAllocator.h @@ -0,0 +1,102 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +JPH_NAMESPACE_BEGIN + +#ifndef JPH_DISABLE_CUSTOM_ALLOCATOR + +/// STL allocator that forwards to our allocation functions +template +class STLAllocator +{ +public: + using value_type = T; + + /// Pointer to type + using pointer = T *; + using const_pointer = const T *; + + /// Reference to type. + /// Can be removed in C++20. + using reference = T &; + using const_reference = const T &; + + using size_type = size_t; + using difference_type = ptrdiff_t; + + /// Constructor + inline STLAllocator() = default; + + /// Constructor from other allocator + template + inline STLAllocator(const STLAllocator &) { } + + /// Allocate memory + inline pointer allocate(size_type inN) + { + if constexpr (alignof(T) > (JPH_CPU_ADDRESS_BITS == 32? 8 : 16)) + return pointer(AlignedAllocate(inN * sizeof(value_type), alignof(T))); + else + return pointer(Allocate(inN * sizeof(value_type))); + } + + /// Free memory + inline void deallocate(pointer inPointer, size_type) + { + if constexpr (alignof(T) > (JPH_CPU_ADDRESS_BITS == 32? 8 : 16)) + AlignedFree(inPointer); + else + Free(inPointer); + } + + /// Allocators are stateless so assumed to be equal + inline bool operator == (const STLAllocator &) const + { + return true; + } + + inline bool operator != (const STLAllocator &) const + { + return false; + } + + /// Converting to allocator for other type + template + struct rebind + { + using other = STLAllocator; + }; +}; + +#else + +template using STLAllocator = std::allocator; + +#endif // !JPH_DISABLE_CUSTOM_ALLOCATOR + +// Declare STL containers that use our allocator +template using Array = std::vector>; +using String = std::basic_string, STLAllocator>; +using IStringStream = std::basic_istringstream, STLAllocator>; + +JPH_NAMESPACE_END + +#if (!defined(JPH_PLATFORM_WINDOWS) || defined(JPH_COMPILER_MINGW)) && !defined(JPH_DISABLE_CUSTOM_ALLOCATOR) + +namespace std +{ + /// Declare std::hash for String, for some reason on Linux based platforms template deduction takes the wrong variant + template <> + struct hash + { + inline size_t operator () (const JPH::String &inRHS) const + { + return hash { } (inRHS); + } + }; +} + +#endif // (!JPH_PLATFORM_WINDOWS || JPH_COMPILER_MINGW) && !JPH_DISABLE_CUSTOM_ALLOCATOR diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/STLTempAllocator.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/STLTempAllocator.h new file mode 100644 index 00000000000..44ed7d6fedf --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/STLTempAllocator.h @@ -0,0 +1,77 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +/// STL allocator that wraps around TempAllocator +template +class STLTempAllocator +{ +public: + using value_type = T; + + /// Pointer to type + using pointer = T *; + using const_pointer = const T *; + + /// Reference to type. + /// Can be removed in C++20. + using reference = T &; + using const_reference = const T &; + + using size_type = size_t; + using difference_type = ptrdiff_t; + + /// Constructor + inline STLTempAllocator(TempAllocator &inAllocator) : mAllocator(inAllocator) { } + + /// Constructor from other allocator + template + inline explicit STLTempAllocator(const STLTempAllocator &inRHS) : mAllocator(inRHS.GetAllocator()) { } + + /// Allocate memory + inline pointer allocate(size_type inN) + { + return pointer(mAllocator.Allocate(uint(inN * sizeof(value_type)))); + } + + /// Free memory + inline void deallocate(pointer inPointer, size_type inN) + { + mAllocator.Free(inPointer, uint(inN * sizeof(value_type))); + } + + /// Allocators are stateless so assumed to be equal + inline bool operator == (const STLTempAllocator &) const + { + return true; + } + + inline bool operator != (const STLTempAllocator &) const + { + return false; + } + + /// Converting to allocator for other type + template + struct rebind + { + using other = STLTempAllocator; + }; + + /// Get our temp allocator + TempAllocator & GetAllocator() const + { + return mAllocator; + } + +private: + TempAllocator & mAllocator; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Semaphore.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Semaphore.cpp new file mode 100644 index 00000000000..294aa755f81 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Semaphore.cpp @@ -0,0 +1,80 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2023 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include + +#ifdef JPH_PLATFORM_WINDOWS + JPH_SUPPRESS_WARNING_PUSH + JPH_MSVC_SUPPRESS_WARNING(5039) // winbase.h(13179): warning C5039: 'TpSetCallbackCleanupGroup': pointer or reference to potentially throwing function passed to 'extern "C"' function under -EHc. Undefined behavior may occur if this function throws an exception. + #define WIN32_LEAN_AND_MEAN +#ifndef JPH_COMPILER_MINGW + #include +#else + #include +#endif + + JPH_SUPPRESS_WARNING_POP +#endif + +JPH_NAMESPACE_BEGIN + +Semaphore::Semaphore() +{ +#ifdef JPH_PLATFORM_WINDOWS + mSemaphore = CreateSemaphore(nullptr, 0, INT_MAX, nullptr); +#endif +} + +Semaphore::~Semaphore() +{ +#ifdef JPH_PLATFORM_WINDOWS + CloseHandle(mSemaphore); +#endif +} + +void Semaphore::Release(uint inNumber) +{ + JPH_ASSERT(inNumber > 0); + +#ifdef JPH_PLATFORM_WINDOWS + int old_value = mCount.fetch_add(inNumber); + if (old_value < 0) + { + int new_value = old_value + (int)inNumber; + int num_to_release = min(new_value, 0) - old_value; + ::ReleaseSemaphore(mSemaphore, num_to_release, nullptr); + } +#else + std::lock_guard lock(mLock); + mCount += (int)inNumber; + if (inNumber > 1) + mWaitVariable.notify_all(); + else + mWaitVariable.notify_one(); +#endif +} + +void Semaphore::Acquire(uint inNumber) +{ + JPH_ASSERT(inNumber > 0); + +#ifdef JPH_PLATFORM_WINDOWS + int old_value = mCount.fetch_sub(inNumber); + int new_value = old_value - (int)inNumber; + if (new_value < 0) + { + int num_to_acquire = min(old_value, 0) - new_value; + for (int i = 0; i < num_to_acquire; ++i) + WaitForSingleObject(mSemaphore, INFINITE); + } +#else + std::unique_lock lock(mLock); + mCount -= (int)inNumber; + mWaitVariable.wait(lock, [this]() { return mCount >= 0; }); +#endif +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Semaphore.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Semaphore.h new file mode 100644 index 00000000000..498b1b8d40b --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Semaphore.h @@ -0,0 +1,51 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2023 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +JPH_SUPPRESS_WARNINGS_STD_BEGIN +#include +#include +#include +JPH_SUPPRESS_WARNINGS_STD_END + +JPH_NAMESPACE_BEGIN + +// Things we're using from STL +using std::atomic; +using std::mutex; +using std::condition_variable; + +/// Implements a semaphore +/// When we switch to C++20 we can use counting_semaphore to unify this +class JPH_EXPORT Semaphore +{ +public: + /// Constructor + Semaphore(); + ~Semaphore(); + + /// Release the semaphore, signaling the thread waiting on the barrier that there may be work + void Release(uint inNumber = 1); + + /// Acquire the semaphore inNumber times + void Acquire(uint inNumber = 1); + + /// Get the current value of the semaphore + inline int GetValue() const { return mCount; } + +private: +#ifdef JPH_PLATFORM_WINDOWS + // On windows we use a semaphore object since it is more efficient than a lock and a condition variable + alignas(JPH_CACHE_LINE_SIZE) atomic mCount { 0 }; ///< We increment mCount for every release, to acquire we decrement the count. If the count is negative we know that we are waiting on the actual semaphore. + void * mSemaphore; ///< The semaphore is an expensive construct so we only acquire/release it if we know that we need to wait/have waiting threads +#else + // Other platforms: Emulate a semaphore using a mutex, condition variable and count + mutex mLock; + condition_variable mWaitVariable; + int mCount = 0; +#endif +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/StaticArray.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/StaticArray.h new file mode 100644 index 00000000000..70e8cb9a8e8 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/StaticArray.h @@ -0,0 +1,323 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +JPH_NAMESPACE_BEGIN + +/// Simple variable length array backed by a fixed size buffer +template +class [[nodiscard]] StaticArray +{ +public: + using value_type = T; + + using size_type = uint; + + static constexpr uint Capacity = N; + + /// Default constructor + StaticArray() = default; + + /// Constructor from initializer list + explicit StaticArray(std::initializer_list inList) + { + JPH_ASSERT(inList.size() <= N); + for (typename std::initializer_list::iterator i = inList.begin(); i != inList.end(); ++i) + ::new (reinterpret_cast(&mElements[mSize++])) T(*i); + } + + /// Copy constructor + StaticArray(const StaticArray &inRHS) + { + while (mSize < inRHS.mSize) + { + ::new (&mElements[mSize]) T(inRHS[mSize]); + ++mSize; + } + } + + /// Destruct all elements + ~StaticArray() + { + if constexpr (!is_trivially_destructible()) + for (T *e = reinterpret_cast(mElements), *end = e + mSize; e < end; ++e) + e->~T(); + } + + /// Destruct all elements and set length to zero + void clear() + { + if constexpr (!is_trivially_destructible()) + for (T *e = reinterpret_cast(mElements), *end = e + mSize; e < end; ++e) + e->~T(); + mSize = 0; + } + + /// Add element to the back of the array + void push_back(const T &inElement) + { + JPH_ASSERT(mSize < N); + ::new (&mElements[mSize++]) T(inElement); + } + + /// Construct element at the back of the array + template + void emplace_back(A &&... inElement) + { + JPH_ASSERT(mSize < N); + ::new (&mElements[mSize++]) T(std::forward(inElement)...); + } + + /// Remove element from the back of the array + void pop_back() + { + JPH_ASSERT(mSize > 0); + reinterpret_cast(mElements[--mSize]).~T(); + } + + /// Returns true if there are no elements in the array + bool empty() const + { + return mSize == 0; + } + + /// Returns amount of elements in the array + size_type size() const + { + return mSize; + } + + /// Returns maximum amount of elements the array can hold + size_type capacity() const + { + return N; + } + + /// Resize array to new length + void resize(size_type inNewSize) + { + JPH_ASSERT(inNewSize <= N); + if constexpr (!is_trivially_constructible()) + for (T *element = reinterpret_cast(mElements) + mSize, *element_end = reinterpret_cast(mElements) + inNewSize; element < element_end; ++element) + ::new (element) T; + if constexpr (!is_trivially_destructible()) + for (T *element = reinterpret_cast(mElements) + inNewSize, *element_end = reinterpret_cast(mElements) + mSize; element < element_end; ++element) + element->~T(); + mSize = inNewSize; + } + + using const_iterator = const T *; + + /// Iterators + const_iterator begin() const + { + return reinterpret_cast(mElements); + } + + const_iterator end() const + { + return reinterpret_cast(mElements + mSize); + } + + using iterator = T *; + + iterator begin() + { + return reinterpret_cast(mElements); + } + + iterator end() + { + return reinterpret_cast(mElements + mSize); + } + + const T * data() const + { + return reinterpret_cast(mElements); + } + + T * data() + { + return reinterpret_cast(mElements); + } + + /// Access element + T & operator [] (size_type inIdx) + { + JPH_ASSERT(inIdx < mSize); + return reinterpret_cast(mElements[inIdx]); + } + + const T & operator [] (size_type inIdx) const + { + JPH_ASSERT(inIdx < mSize); + return reinterpret_cast(mElements[inIdx]); + } + + /// Access element + T & at(size_type inIdx) + { + JPH_ASSERT(inIdx < mSize); + return reinterpret_cast(mElements[inIdx]); + } + + const T & at(size_type inIdx) const + { + JPH_ASSERT(inIdx < mSize); + return reinterpret_cast(mElements[inIdx]); + } + + /// First element in the array + const T & front() const + { + JPH_ASSERT(mSize > 0); + return reinterpret_cast(mElements[0]); + } + + T & front() + { + JPH_ASSERT(mSize > 0); + return reinterpret_cast(mElements[0]); + } + + /// Last element in the array + const T & back() const + { + JPH_ASSERT(mSize > 0); + return reinterpret_cast(mElements[mSize - 1]); + } + + T & back() + { + JPH_ASSERT(mSize > 0); + return reinterpret_cast(mElements[mSize - 1]); + } + + /// Remove one element from the array + void erase(const_iterator inIter) + { + size_type p = size_type(inIter - begin()); + JPH_ASSERT(p < mSize); + reinterpret_cast(mElements[p]).~T(); + if (p + 1 < mSize) + memmove(mElements + p, mElements + p + 1, (mSize - p - 1) * sizeof(T)); + --mSize; + } + + /// Remove multiple element from the array + void erase(const_iterator inBegin, const_iterator inEnd) + { + size_type p = size_type(inBegin - begin()); + size_type n = size_type(inEnd - inBegin); + JPH_ASSERT(inEnd <= end()); + for (size_type i = 0; i < n; ++i) + reinterpret_cast(mElements[p + i]).~T(); + if (p + n < mSize) + memmove(mElements + p, mElements + p + n, (mSize - p - n) * sizeof(T)); + mSize -= n; + } + + /// Assignment operator + StaticArray & operator = (const StaticArray &inRHS) + { + size_type rhs_size = inRHS.size(); + + if (static_cast(this) != static_cast(&inRHS)) + { + clear(); + + while (mSize < rhs_size) + { + ::new (&mElements[mSize]) T(inRHS[mSize]); + ++mSize; + } + } + + return *this; + } + + /// Assignment operator with static array of different max length + template + StaticArray & operator = (const StaticArray &inRHS) + { + size_type rhs_size = inRHS.size(); + JPH_ASSERT(rhs_size <= N); + + if (static_cast(this) != static_cast(&inRHS)) + { + clear(); + + while (mSize < rhs_size) + { + ::new (&mElements[mSize]) T(inRHS[mSize]); + ++mSize; + } + } + + return *this; + } + + /// Comparing arrays + bool operator == (const StaticArray &inRHS) const + { + if (mSize != inRHS.mSize) + return false; + for (size_type i = 0; i < mSize; ++i) + if (!(reinterpret_cast(mElements[i]) == reinterpret_cast(inRHS.mElements[i]))) + return false; + return true; + } + + bool operator != (const StaticArray &inRHS) const + { + if (mSize != inRHS.mSize) + return true; + for (size_type i = 0; i < mSize; ++i) + if (reinterpret_cast(mElements[i]) != reinterpret_cast(inRHS.mElements[i])) + return true; + return false; + } + +protected: + struct alignas(T) Storage + { + uint8 mData[sizeof(T)]; + }; + + static_assert(sizeof(T) == sizeof(Storage), "Mismatch in size"); + static_assert(alignof(T) == alignof(Storage), "Mismatch in alignment"); + + size_type mSize = 0; + Storage mElements[N]; +}; + +JPH_NAMESPACE_END + +JPH_SUPPRESS_WARNING_PUSH +JPH_CLANG_SUPPRESS_WARNING("-Wc++98-compat") + +namespace std +{ + /// Declare std::hash for StaticArray + template + struct hash> + { + size_t operator () (const JPH::StaticArray &inRHS) const + { + std::size_t ret = 0; + + // Hash length first + JPH::HashCombine(ret, inRHS.size()); + + // Then hash elements + for (const T &t : inRHS) + JPH::HashCombine(ret, t); + + return ret; + } + }; +} + +JPH_SUPPRESS_WARNING_POP diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/StreamIn.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/StreamIn.h new file mode 100644 index 00000000000..0820d0fbf4b --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/StreamIn.h @@ -0,0 +1,110 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +/// Simple binary input stream +class JPH_EXPORT StreamIn : public NonCopyable +{ +public: + /// Virtual destructor + virtual ~StreamIn() = default; + + /// Read a string of bytes from the binary stream + virtual void ReadBytes(void *outData, size_t inNumBytes) = 0; + + /// Returns true when an attempt has been made to read past the end of the file + virtual bool IsEOF() const = 0; + + /// Returns true if there was an IO failure + virtual bool IsFailed() const = 0; + + /// Read a primitive (e.g. float, int, etc.) from the binary stream + template + void Read(T &outT) + { + ReadBytes(&outT, sizeof(outT)); + } + + /// Read a vector of primitives from the binary stream + template + void Read(std::vector &outT) + { + typename Array::size_type len = outT.size(); // Initialize to previous array size, this is used for validation in the StateRecorder class + Read(len); + if (!IsEOF() && !IsFailed()) + { + outT.resize(len); + for (typename Array::size_type i = 0; i < len; ++i) + Read(outT[i]); + } + else + outT.clear(); + } + + /// Read a string from the binary stream (reads the number of characters and then the characters) + template + void Read(std::basic_string &outString) + { + typename std::basic_string::size_type len = 0; + Read(len); + if (!IsEOF() && !IsFailed()) + { + outString.resize(len); + ReadBytes(outString.data(), len * sizeof(Type)); + } + else + outString.clear(); + } + + /// Read a vector of primitives from the binary stream using a custom function to read the elements + template + void Read(std::vector &outT, const F &inReadElement) + { + typename Array::size_type len = outT.size(); // Initialize to previous array size, this is used for validation in the StateRecorder class + Read(len); + if (!IsEOF() && !IsFailed()) + { + outT.resize(len); + for (typename Array::size_type i = 0; i < len; ++i) + inReadElement(*this, outT[i]); + } + else + outT.clear(); + } + + /// Read a Vec3 (don't read W) + void Read(Vec3 &outVec) + { + ReadBytes(&outVec, 3 * sizeof(float)); + outVec = Vec3::sFixW(outVec.mValue); + } + + /// Read a DVec3 (don't read W) + void Read(DVec3 &outVec) + { + ReadBytes(&outVec, 3 * sizeof(double)); + outVec = DVec3::sFixW(outVec.mValue); + } + + /// Read a DMat44 (don't read W component of translation) + void Read(DMat44 &outVec) + { + Vec4 x, y, z; + Read(x); + Read(y); + Read(z); + + DVec3 t; + Read(t); + + outVec = DMat44(x, y, z, t); + } +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/StreamOut.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/StreamOut.h new file mode 100644 index 00000000000..ac817ff0828 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/StreamOut.h @@ -0,0 +1,86 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +/// Simple binary output stream +class JPH_EXPORT StreamOut : public NonCopyable +{ +public: + /// Virtual destructor + virtual ~StreamOut() = default; + + /// Write a string of bytes to the binary stream + virtual void WriteBytes(const void *inData, size_t inNumBytes) = 0; + + /// Returns true if there was an IO failure + virtual bool IsFailed() const = 0; + + /// Write a primitive (e.g. float, int, etc.) to the binary stream + template + void Write(const T &inT) + { + WriteBytes(&inT, sizeof(inT)); + } + + /// Write a vector of primitives to the binary stream + template + void Write(const std::vector &inT) + { + typename Array::size_type len = inT.size(); + Write(len); + if (!IsFailed()) + for (typename Array::size_type i = 0; i < len; ++i) + Write(inT[i]); + } + + /// Write a string to the binary stream (writes the number of characters and then the characters) + template + void Write(const std::basic_string &inString) + { + typename std::basic_string::size_type len = inString.size(); + Write(len); + if (!IsFailed()) + WriteBytes(inString.data(), len * sizeof(Type)); + } + + /// Write a vector of primitives to the binary stream using a custom write function + template + void Write(const std::vector &inT, const F &inWriteElement) + { + typename Array::size_type len = inT.size(); + Write(len); + if (!IsFailed()) + for (typename Array::size_type i = 0; i < len; ++i) + inWriteElement(inT[i], *this); + } + + /// Write a Vec3 (don't write W) + void Write(const Vec3 &inVec) + { + WriteBytes(&inVec, 3 * sizeof(float)); + } + + /// Write a DVec3 (don't write W) + void Write(const DVec3 &inVec) + { + WriteBytes(&inVec, 3 * sizeof(double)); + } + + /// Write a DMat44 (don't write W component of translation) + void Write(const DMat44 &inVec) + { + Write(inVec.GetColumn4(0)); + Write(inVec.GetColumn4(1)); + Write(inVec.GetColumn4(2)); + + Write(inVec.GetTranslation()); + } +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/StreamUtils.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/StreamUtils.h new file mode 100644 index 00000000000..e374c521062 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/StreamUtils.h @@ -0,0 +1,167 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +namespace StreamUtils { + +template +using ObjectToIDMap = UnorderedMap; + +template +using IDToObjectMap = Array>; + +// Restore a single object by reading the hash of the type, constructing it and then calling the restore function +template +Result> RestoreObject(StreamIn &inStream, void (Type::*inRestoreBinaryStateFunction)(StreamIn &)) +{ + Result> result; + + // Read the hash of the type + uint32 hash; + inStream.Read(hash); + if (inStream.IsEOF() || inStream.IsFailed()) + { + result.SetError("Failed to read type hash"); + return result; + } + + // Get the RTTI for the type + const RTTI *rtti = Factory::sInstance->Find(hash); + if (rtti == nullptr) + { + result.SetError("Failed to create instance of type"); + return result; + } + + // Construct and read the data of the type + Ref object = reinterpret_cast(rtti->CreateObject()); + (object->*inRestoreBinaryStateFunction)(inStream); + if (inStream.IsEOF() || inStream.IsFailed()) + { + result.SetError("Failed to restore object"); + return result; + } + + result.Set(object); + return result; +} + +/// Save an object reference to a stream. Uses a map to map objects to IDs which is also used to prevent writing duplicates. +template +void SaveObjectReference(StreamOut &inStream, const Type *inObject, ObjectToIDMap *ioObjectToIDMap) +{ + if (ioObjectToIDMap == nullptr || inObject == nullptr) + { + // Write null ID + inStream.Write(~uint32(0)); + } + else + { + typename ObjectToIDMap::const_iterator id = ioObjectToIDMap->find(inObject); + if (id != ioObjectToIDMap->end()) + { + // Existing object, write ID + inStream.Write(id->second); + } + else + { + // New object, write the ID + uint32 new_id = uint32(ioObjectToIDMap->size()); + (*ioObjectToIDMap)[inObject] = new_id; + inStream.Write(new_id); + + // Write the object + inObject->SaveBinaryState(inStream); + } + } +} + +/// Restore an object reference from stream. +template +Result> RestoreObjectReference(StreamIn &inStream, IDToObjectMap &ioIDToObjectMap) +{ + Result> result; + + // Read id + uint32 id = ~uint32(0); + inStream.Read(id); + + // Check null + if (id == ~uint32(0)) + { + result.Set(nullptr); + return result; + } + + // Check if it already exists + if (id >= ioIDToObjectMap.size()) + { + // New object, restore it + result = Type::sRestoreFromBinaryState(inStream); + if (result.HasError()) + return result; + JPH_ASSERT(id == ioIDToObjectMap.size()); + ioIDToObjectMap.push_back(result.Get()); + } + else + { + // Existing object filter + result.Set(ioIDToObjectMap[id].GetPtr()); + } + + return result; +} + +// Save an array of objects to a stream. +template +void SaveObjectArray(StreamOut &inStream, const ArrayType &inArray, ObjectToIDMap *ioObjectToIDMap) +{ + inStream.Write(size_t(inArray.size())); + for (const ValueType *value: inArray) + SaveObjectReference(inStream, value, ioObjectToIDMap); +} + +// Restore an array of objects from a stream. +template +Result RestoreObjectArray(StreamIn &inStream, IDToObjectMap &ioIDToObjectMap) +{ + Result result; + + size_t len; + inStream.Read(len); + if (inStream.IsEOF() || inStream.IsFailed()) + { + result.SetError("Failed to read stream"); + return result; + } + + ArrayType values; + values.reserve(len); + for (size_t i = 0; i < len; ++i) + { + Result value = RestoreObjectReference(inStream, ioIDToObjectMap); + if (value.HasError()) + { + result.SetError(value.GetError()); + return result; + } + values.push_back(std::move(value.Get())); + } + + result.Set(values); + return result; +} + +} // StreamUtils + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/StreamWrapper.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/StreamWrapper.h new file mode 100644 index 00000000000..66a36b0571e --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/StreamWrapper.h @@ -0,0 +1,53 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +JPH_SUPPRESS_WARNINGS_STD_BEGIN +#include +JPH_SUPPRESS_WARNINGS_STD_END + +JPH_NAMESPACE_BEGIN + +/// Wrapper around std::ostream +class StreamOutWrapper : public StreamOut +{ +public: + /// Constructor + StreamOutWrapper(ostream &ioWrapped) : mWrapped(ioWrapped) { } + + /// Write a string of bytes to the binary stream + virtual void WriteBytes(const void *inData, size_t inNumBytes) override { mWrapped.write((const char *)inData, inNumBytes); } + + /// Returns true if there was an IO failure + virtual bool IsFailed() const override { return mWrapped.fail(); } + +private: + ostream & mWrapped; +}; + +/// Wrapper around std::istream +class StreamInWrapper : public StreamIn +{ +public: + /// Constructor + StreamInWrapper(istream &ioWrapped) : mWrapped(ioWrapped) { } + + /// Write a string of bytes to the binary stream + virtual void ReadBytes(void *outData, size_t inNumBytes) override { mWrapped.read((char *)outData, inNumBytes); } + + /// Returns true when an attempt has been made to read past the end of the file + virtual bool IsEOF() const override { return mWrapped.eof(); } + + /// Returns true if there was an IO failure + virtual bool IsFailed() const override { return mWrapped.fail(); } + +private: + istream & mWrapped; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/StringTools.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/StringTools.cpp new file mode 100644 index 00000000000..6eae982cc6b --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/StringTools.cpp @@ -0,0 +1,101 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include + +JPH_SUPPRESS_WARNINGS_STD_BEGIN +#include +JPH_SUPPRESS_WARNINGS_STD_END + +JPH_NAMESPACE_BEGIN + +String StringFormat(const char *inFMT, ...) +{ + char buffer[1024]; + + // Format the string + va_list list; + va_start(list, inFMT); + vsnprintf(buffer, sizeof(buffer), inFMT, list); + va_end(list); + + return String(buffer); +} + +void StringReplace(String &ioString, const string_view &inSearch, const string_view &inReplace) +{ + size_t index = 0; + for (;;) + { + index = ioString.find(inSearch, index); + if (index == String::npos) + break; + + ioString.replace(index, inSearch.size(), inReplace); + + index += inReplace.size(); + } +} + +void StringToVector(const string_view &inString, Array &outVector, const string_view &inDelimiter, bool inClearVector) +{ + JPH_ASSERT(inDelimiter.size() > 0); + + // Ensure vector empty + if (inClearVector) + outVector.clear(); + + // No string? no elements + if (inString.empty()) + return; + + // Start with initial string + String s(inString); + + // Add to vector while we have a delimiter + size_t i; + while (!s.empty() && (i = s.find(inDelimiter)) != String::npos) + { + outVector.push_back(s.substr(0, i)); + s.erase(0, i + inDelimiter.length()); + } + + // Add final element + outVector.push_back(s); +} + +void VectorToString(const Array &inVector, String &outString, const string_view &inDelimiter) +{ + // Ensure string empty + outString.clear(); + + for (const String &s : inVector) + { + // Add delimiter if not first element + if (!outString.empty()) + outString.append(inDelimiter); + + // Add element + outString.append(s); + } +} + +String ToLower(const string_view &inString) +{ + String out; + out.reserve(inString.length()); + for (char c : inString) + out.push_back((char)tolower(c)); + return out; +} + +const char *NibbleToBinary(uint32 inNibble) +{ + static const char *nibbles[] = { "0000", "0001", "0010", "0011", "0100", "0101", "0110", "0111", "1000", "1001", "1010", "1011", "1100", "1101", "1110", "1111" }; + return nibbles[inNibble & 0xf]; +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/StringTools.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/StringTools.h new file mode 100644 index 00000000000..d3def9356b9 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/StringTools.h @@ -0,0 +1,51 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +JPH_NAMESPACE_BEGIN + +/// Create a formatted text string for debugging purposes. +/// Note that this function has an internal buffer of 1024 characters, so long strings will be trimmed. +JPH_EXPORT String StringFormat(const char *inFMT, ...); + +/// Convert type to string +template +String ConvertToString(const T &inValue) +{ + using OStringStream = std::basic_ostringstream, STLAllocator>; + OStringStream oss; + oss << inValue; + return oss.str(); +} + +/// Calculate the FNV-1a hash of inString. +/// @see https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function +constexpr uint64 HashString(const char *inString) +{ + uint64 hash = 14695981039346656037UL; + for (const char *c = inString; *c != 0; ++c) + { + hash ^= *c; + hash = hash * 1099511628211UL; + } + return hash; +} + +/// Replace substring with other string +JPH_EXPORT void StringReplace(String &ioString, const string_view &inSearch, const string_view &inReplace); + +/// Convert a delimited string to an array of strings +JPH_EXPORT void StringToVector(const string_view &inString, Array &outVector, const string_view &inDelimiter = ",", bool inClearVector = true); + +/// Convert an array strings to a delimited string +JPH_EXPORT void VectorToString(const Array &inVector, String &outString, const string_view &inDelimiter = ","); + +/// Convert a string to lower case +JPH_EXPORT String ToLower(const string_view &inString); + +/// Converts the lower 4 bits of inNibble to a string that represents the number in binary format +JPH_EXPORT const char *NibbleToBinary(uint32 inNibble); + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/TempAllocator.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/TempAllocator.h new file mode 100644 index 00000000000..5d228752b86 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/TempAllocator.h @@ -0,0 +1,121 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +/// Allocator for temporary allocations. +/// This allocator works as a stack: The blocks must always be freed in the reverse order as they are allocated. +/// Note that allocations and frees can take place from different threads, but the order is guaranteed though +/// job dependencies, so it is not needed to use any form of locking. +class JPH_EXPORT TempAllocator : public NonCopyable +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Destructor + virtual ~TempAllocator() = default; + + /// Allocates inSize bytes of memory, returned memory address must be JPH_RVECTOR_ALIGNMENT byte aligned + virtual void * Allocate(uint inSize) = 0; + + /// Frees inSize bytes of memory located at inAddress + virtual void Free(void *inAddress, uint inSize) = 0; +}; + +/// Default implementation of the temp allocator that allocates a large block through malloc upfront +class JPH_EXPORT TempAllocatorImpl final : public TempAllocator +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Constructs the allocator with a maximum allocatable size of inSize + explicit TempAllocatorImpl(uint inSize) : + mBase(static_cast(AlignedAllocate(inSize, JPH_RVECTOR_ALIGNMENT))), + mSize(inSize) + { + } + + /// Destructor, frees the block + virtual ~TempAllocatorImpl() override + { + JPH_ASSERT(mTop == 0); + AlignedFree(mBase); + } + + // See: TempAllocator + virtual void * Allocate(uint inSize) override + { + if (inSize == 0) + { + return nullptr; + } + else + { + uint new_top = mTop + AlignUp(inSize, JPH_RVECTOR_ALIGNMENT); + if (new_top > mSize) + { + Trace("TempAllocator: Out of memory"); + std::abort(); + } + void *address = mBase + mTop; + mTop = new_top; + return address; + } + } + + // See: TempAllocator + virtual void Free(void *inAddress, uint inSize) override + { + if (inAddress == nullptr) + { + JPH_ASSERT(inSize == 0); + } + else + { + mTop -= AlignUp(inSize, JPH_RVECTOR_ALIGNMENT); + if (mBase + mTop != inAddress) + { + Trace("TempAllocator: Freeing in the wrong order"); + std::abort(); + } + } + } + + // Check if no allocations have been made + bool IsEmpty() const + { + return mTop == 0; + } + +private: + uint8 * mBase; ///< Base address of the memory block + uint mSize; ///< Size of the memory block + uint mTop = 0; ///< Current top of the stack +}; + +/// Implementation of the TempAllocator that just falls back to malloc/free +/// Note: This can be quite slow when running in the debugger as large memory blocks need to be initialized with 0xcd +class JPH_EXPORT TempAllocatorMalloc final : public TempAllocator +{ +public: + JPH_OVERRIDE_NEW_DELETE + + // See: TempAllocator + virtual void * Allocate(uint inSize) override + { + return AlignedAllocate(inSize, JPH_RVECTOR_ALIGNMENT); + } + + // See: TempAllocator + virtual void Free(void *inAddress, [[maybe_unused]] uint inSize) override + { + AlignedFree(inAddress); + } +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/TickCounter.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/TickCounter.cpp new file mode 100644 index 00000000000..ae0a0d00842 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/TickCounter.cpp @@ -0,0 +1,34 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include + +#if defined(JPH_PLATFORM_WINDOWS) + JPH_SUPPRESS_WARNING_PUSH + JPH_MSVC_SUPPRESS_WARNING(5039) // winbase.h(13179): warning C5039: 'TpSetCallbackCleanupGroup': pointer or reference to potentially throwing function passed to 'extern "C"' function under -EHc. Undefined behavior may occur if this function throws an exception. + #define WIN32_LEAN_AND_MEAN +#ifndef JPH_COMPILER_MINGW + #include +#else + #include +#endif + JPH_SUPPRESS_WARNING_POP +#endif + +JPH_NAMESPACE_BEGIN + +#if defined(JPH_PLATFORM_WINDOWS_UWP) || (defined(JPH_PLATFORM_WINDOWS) && defined(JPH_CPU_ARM)) + +uint64 GetProcessorTickCount() +{ + LARGE_INTEGER count; + QueryPerformanceCounter(&count); + return uint64(count.QuadPart); +} + +#endif // JPH_PLATFORM_WINDOWS_UWP || (JPH_PLATFORM_WINDOWS && JPH_CPU_ARM) + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/TickCounter.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/TickCounter.h new file mode 100644 index 00000000000..2b5410e3d9f --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/TickCounter.h @@ -0,0 +1,49 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +// Include for __rdtsc +#if defined(JPH_PLATFORM_WINDOWS) + #include +#elif defined(JPH_CPU_X86) && defined(JPH_COMPILER_GCC) + #include +#elif defined(JPH_CPU_E2K) + #include +#endif + +JPH_NAMESPACE_BEGIN + +#if defined(JPH_PLATFORM_WINDOWS_UWP) || (defined(JPH_PLATFORM_WINDOWS) && defined(JPH_CPU_ARM)) + +/// Functionality to get the processors cycle counter +uint64 GetProcessorTickCount(); // Not inline to avoid having to include Windows.h + +#else + +/// Functionality to get the processors cycle counter +JPH_INLINE uint64 GetProcessorTickCount() +{ +#if defined(JPH_PLATFORM_BLUE) + return JPH_PLATFORM_BLUE_GET_TICKS(); +#elif defined(JPH_CPU_X86) + return __rdtsc(); +#elif defined(JPH_CPU_E2K) + return __rdtsc(); +#elif defined(JPH_CPU_ARM) && defined(JPH_USE_NEON) + uint64 val; + asm volatile("mrs %0, cntvct_el0" : "=r" (val)); + return val; +#elif defined(JPH_CPU_ARM) + return 0; // Not supported +#elif defined(JPH_CPU_WASM) + return 0; // Not supported +#else + #error Undefined +#endif +} + +#endif // JPH_PLATFORM_WINDOWS_UWP || (JPH_PLATFORM_WINDOWS && JPH_CPU_ARM) + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/UnorderedMap.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/UnorderedMap.h new file mode 100644 index 00000000000..f4ae2ce9642 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/UnorderedMap.h @@ -0,0 +1,15 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +JPH_SUPPRESS_WARNINGS_STD_BEGIN +#include +JPH_SUPPRESS_WARNINGS_STD_END + +JPH_NAMESPACE_BEGIN + +template , class KeyEqual = std::equal_to> using UnorderedMap = std::unordered_map>>; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/UnorderedSet.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/UnorderedSet.h new file mode 100644 index 00000000000..bcb31a005e2 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/UnorderedSet.h @@ -0,0 +1,15 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +JPH_SUPPRESS_WARNINGS_STD_BEGIN +#include +JPH_SUPPRESS_WARNINGS_STD_END + +JPH_NAMESPACE_BEGIN + +template , class KeyEqual = std::equal_to> using UnorderedSet = std::unordered_set>; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/AABox.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/AABox.h new file mode 100644 index 00000000000..f4f02668dc0 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/AABox.h @@ -0,0 +1,304 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +/// Axis aligned box +class [[nodiscard]] AABox +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Constructor + AABox() : mMin(Vec3::sReplicate(FLT_MAX)), mMax(Vec3::sReplicate(-FLT_MAX)) { } + AABox(Vec3Arg inMin, Vec3Arg inMax) : mMin(inMin), mMax(inMax) { } + AABox(DVec3Arg inMin, DVec3Arg inMax) : mMin(inMin.ToVec3RoundDown()), mMax(inMax.ToVec3RoundUp()) { } + AABox(Vec3Arg inCenter, float inRadius) : mMin(inCenter - Vec3::sReplicate(inRadius)), mMax(inCenter + Vec3::sReplicate(inRadius)) { } + + /// Create box from 2 points + static AABox sFromTwoPoints(Vec3Arg inP1, Vec3Arg inP2) { return AABox(Vec3::sMin(inP1, inP2), Vec3::sMax(inP1, inP2)); } + + /// Get bounding box of size 2 * FLT_MAX + static AABox sBiggest() + { + return AABox(Vec3::sReplicate(-FLT_MAX), Vec3::sReplicate(FLT_MAX)); + } + + /// Comparison operators + bool operator == (const AABox &inRHS) const { return mMin == inRHS.mMin && mMax == inRHS.mMax; } + bool operator != (const AABox &inRHS) const { return mMin != inRHS.mMin || mMax != inRHS.mMax; } + + /// Reset the bounding box to an empty bounding box + void SetEmpty() + { + mMin = Vec3::sReplicate(FLT_MAX); + mMax = Vec3::sReplicate(-FLT_MAX); + } + + /// Check if the bounding box is valid (max >= min) + bool IsValid() const + { + return mMin.GetX() <= mMax.GetX() && mMin.GetY() <= mMax.GetY() && mMin.GetZ() <= mMax.GetZ(); + } + + /// Encapsulate point in bounding box + void Encapsulate(Vec3Arg inPos) + { + mMin = Vec3::sMin(mMin, inPos); + mMax = Vec3::sMax(mMax, inPos); + } + + /// Encapsulate bounding box in bounding box + void Encapsulate(const AABox &inRHS) + { + mMin = Vec3::sMin(mMin, inRHS.mMin); + mMax = Vec3::sMax(mMax, inRHS.mMax); + } + + /// Encapsulate triangle in bounding box + void Encapsulate(const Triangle &inRHS) + { + Vec3 v = Vec3::sLoadFloat3Unsafe(inRHS.mV[0]); + Encapsulate(v); + v = Vec3::sLoadFloat3Unsafe(inRHS.mV[1]); + Encapsulate(v); + v = Vec3::sLoadFloat3Unsafe(inRHS.mV[2]); + Encapsulate(v); + } + + /// Encapsulate triangle in bounding box + void Encapsulate(const VertexList &inVertices, const IndexedTriangle &inTriangle) + { + for (uint32 idx : inTriangle.mIdx) + Encapsulate(Vec3(inVertices[idx])); + } + + /// Intersect this bounding box with inOther, returns the intersection + AABox Intersect(const AABox &inOther) const + { + return AABox(Vec3::sMax(mMin, inOther.mMin), Vec3::sMin(mMax, inOther.mMax)); + } + + /// Make sure that each edge of the bounding box has a minimal length + void EnsureMinimalEdgeLength(float inMinEdgeLength) + { + Vec3 min_length = Vec3::sReplicate(inMinEdgeLength); + mMax = Vec3::sSelect(mMax, mMin + min_length, Vec3::sLess(mMax - mMin, min_length)); + } + + /// Widen the box on both sides by inVector + void ExpandBy(Vec3Arg inVector) + { + mMin -= inVector; + mMax += inVector; + } + + /// Get center of bounding box + Vec3 GetCenter() const + { + return 0.5f * (mMin + mMax); + } + + /// Get extent of bounding box (half of the size) + Vec3 GetExtent() const + { + return 0.5f * (mMax - mMin); + } + + /// Get size of bounding box + Vec3 GetSize() const + { + return mMax - mMin; + } + + /// Get surface area of bounding box + float GetSurfaceArea() const + { + Vec3 extent = mMax - mMin; + return 2.0f * (extent.GetX() * extent.GetY() + extent.GetX() * extent.GetZ() + extent.GetY() * extent.GetZ()); + } + + /// Get volume of bounding box + float GetVolume() const + { + Vec3 extent = mMax - mMin; + return extent.GetX() * extent.GetY() * extent.GetZ(); + } + + /// Check if this box contains another box + bool Contains(const AABox &inOther) const + { + return UVec4::sAnd(Vec3::sLessOrEqual(mMin, inOther.mMin), Vec3::sGreaterOrEqual(mMax, inOther.mMax)).TestAllXYZTrue(); + } + + /// Check if this box contains a point + bool Contains(Vec3Arg inOther) const + { + return UVec4::sAnd(Vec3::sLessOrEqual(mMin, inOther), Vec3::sGreaterOrEqual(mMax, inOther)).TestAllXYZTrue(); + } + + /// Check if this box contains a point + bool Contains(DVec3Arg inOther) const + { + return Contains(Vec3(inOther)); + } + + /// Check if this box overlaps with another box + bool Overlaps(const AABox &inOther) const + { + return !UVec4::sOr(Vec3::sGreater(mMin, inOther.mMax), Vec3::sLess(mMax, inOther.mMin)).TestAnyXYZTrue(); + } + + /// Check if this box overlaps with a plane + bool Overlaps(const Plane &inPlane) const + { + Vec3 normal = inPlane.GetNormal(); + float dist_normal = inPlane.SignedDistance(GetSupport(normal)); + float dist_min_normal = inPlane.SignedDistance(GetSupport(-normal)); + return dist_normal * dist_min_normal <= 0.0f; // If both support points are on the same side of the plane we don't overlap + } + + /// Translate bounding box + void Translate(Vec3Arg inTranslation) + { + mMin += inTranslation; + mMax += inTranslation; + } + + /// Translate bounding box + void Translate(DVec3Arg inTranslation) + { + mMin = (DVec3(mMin) + inTranslation).ToVec3RoundDown(); + mMax = (DVec3(mMax) + inTranslation).ToVec3RoundUp(); + } + + /// Transform bounding box + AABox Transformed(Mat44Arg inMatrix) const + { + // Start with the translation of the matrix + Vec3 new_min, new_max; + new_min = new_max = inMatrix.GetTranslation(); + + // Now find the extreme points by considering the product of the min and max with each column of inMatrix + for (int c = 0; c < 3; ++c) + { + Vec3 col = inMatrix.GetColumn3(c); + + Vec3 a = col * mMin[c]; + Vec3 b = col * mMax[c]; + + new_min += Vec3::sMin(a, b); + new_max += Vec3::sMax(a, b); + } + + // Return the new bounding box + return AABox(new_min, new_max); + } + + /// Transform bounding box + AABox Transformed(DMat44Arg inMatrix) const + { + AABox transformed = Transformed(inMatrix.GetRotation()); + transformed.Translate(inMatrix.GetTranslation()); + return transformed; + } + + /// Scale this bounding box, can handle non-uniform and negative scaling + AABox Scaled(Vec3Arg inScale) const + { + return AABox::sFromTwoPoints(mMin * inScale, mMax * inScale); + } + + /// Calculate the support vector for this convex shape. + Vec3 GetSupport(Vec3Arg inDirection) const + { + return Vec3::sSelect(mMax, mMin, Vec3::sLess(inDirection, Vec3::sZero())); + } + + /// Get the vertices of the face that faces inDirection the most + template + void GetSupportingFace(Vec3Arg inDirection, VERTEX_ARRAY &outVertices) const + { + outVertices.resize(4); + + int axis = inDirection.Abs().GetHighestComponentIndex(); + if (inDirection[axis] < 0.0f) + { + switch (axis) + { + case 0: + outVertices[0] = Vec3(mMax.GetX(), mMin.GetY(), mMin.GetZ()); + outVertices[1] = Vec3(mMax.GetX(), mMax.GetY(), mMin.GetZ()); + outVertices[2] = Vec3(mMax.GetX(), mMax.GetY(), mMax.GetZ()); + outVertices[3] = Vec3(mMax.GetX(), mMin.GetY(), mMax.GetZ()); + break; + + case 1: + outVertices[0] = Vec3(mMin.GetX(), mMax.GetY(), mMin.GetZ()); + outVertices[1] = Vec3(mMin.GetX(), mMax.GetY(), mMax.GetZ()); + outVertices[2] = Vec3(mMax.GetX(), mMax.GetY(), mMax.GetZ()); + outVertices[3] = Vec3(mMax.GetX(), mMax.GetY(), mMin.GetZ()); + break; + + case 2: + outVertices[0] = Vec3(mMin.GetX(), mMin.GetY(), mMax.GetZ()); + outVertices[1] = Vec3(mMax.GetX(), mMin.GetY(), mMax.GetZ()); + outVertices[2] = Vec3(mMax.GetX(), mMax.GetY(), mMax.GetZ()); + outVertices[3] = Vec3(mMin.GetX(), mMax.GetY(), mMax.GetZ()); + break; + } + } + else + { + switch (axis) + { + case 0: + outVertices[0] = Vec3(mMin.GetX(), mMin.GetY(), mMin.GetZ()); + outVertices[1] = Vec3(mMin.GetX(), mMin.GetY(), mMax.GetZ()); + outVertices[2] = Vec3(mMin.GetX(), mMax.GetY(), mMax.GetZ()); + outVertices[3] = Vec3(mMin.GetX(), mMax.GetY(), mMin.GetZ()); + break; + + case 1: + outVertices[0] = Vec3(mMin.GetX(), mMin.GetY(), mMin.GetZ()); + outVertices[1] = Vec3(mMax.GetX(), mMin.GetY(), mMin.GetZ()); + outVertices[2] = Vec3(mMax.GetX(), mMin.GetY(), mMax.GetZ()); + outVertices[3] = Vec3(mMin.GetX(), mMin.GetY(), mMax.GetZ()); + break; + + case 2: + outVertices[0] = Vec3(mMin.GetX(), mMin.GetY(), mMin.GetZ()); + outVertices[1] = Vec3(mMin.GetX(), mMax.GetY(), mMin.GetZ()); + outVertices[2] = Vec3(mMax.GetX(), mMax.GetY(), mMin.GetZ()); + outVertices[3] = Vec3(mMax.GetX(), mMin.GetY(), mMin.GetZ()); + break; + } + } + } + + /// Get the closest point on or in this box to inPoint + Vec3 GetClosestPoint(Vec3Arg inPoint) const + { + return Vec3::sMin(Vec3::sMax(inPoint, mMin), mMax); + } + + /// Get the squared distance between inPoint and this box (will be 0 if in Point is inside the box) + inline float GetSqDistanceTo(Vec3Arg inPoint) const + { + return (GetClosestPoint(inPoint) - inPoint).LengthSq(); + } + + /// Bounding box min and max + Vec3 mMin; + Vec3 mMax; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/AABox4.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/AABox4.h new file mode 100644 index 00000000000..4465d4dabe5 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/AABox4.h @@ -0,0 +1,224 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +/// Helper functions that process 4 axis aligned boxes at the same time using SIMD +/// Test if 4 bounding boxes overlap with 1 bounding box, splat 1 box +JPH_INLINE UVec4 AABox4VsBox(const AABox &inBox1, Vec4Arg inBox2MinX, Vec4Arg inBox2MinY, Vec4Arg inBox2MinZ, Vec4Arg inBox2MaxX, Vec4Arg inBox2MaxY, Vec4Arg inBox2MaxZ) +{ + // Splat values of box 1 + Vec4 box1_minx = inBox1.mMin.SplatX(); + Vec4 box1_miny = inBox1.mMin.SplatY(); + Vec4 box1_minz = inBox1.mMin.SplatZ(); + Vec4 box1_maxx = inBox1.mMax.SplatX(); + Vec4 box1_maxy = inBox1.mMax.SplatY(); + Vec4 box1_maxz = inBox1.mMax.SplatZ(); + + // Test separation over each axis + UVec4 nooverlapx = UVec4::sOr(Vec4::sGreater(box1_minx, inBox2MaxX), Vec4::sGreater(inBox2MinX, box1_maxx)); + UVec4 nooverlapy = UVec4::sOr(Vec4::sGreater(box1_miny, inBox2MaxY), Vec4::sGreater(inBox2MinY, box1_maxy)); + UVec4 nooverlapz = UVec4::sOr(Vec4::sGreater(box1_minz, inBox2MaxZ), Vec4::sGreater(inBox2MinZ, box1_maxz)); + + // Return overlap + return UVec4::sNot(UVec4::sOr(UVec4::sOr(nooverlapx, nooverlapy), nooverlapz)); +} + +/// Scale 4 axis aligned boxes +JPH_INLINE void AABox4Scale(Vec3Arg inScale, Vec4Arg inBoxMinX, Vec4Arg inBoxMinY, Vec4Arg inBoxMinZ, Vec4Arg inBoxMaxX, Vec4Arg inBoxMaxY, Vec4Arg inBoxMaxZ, Vec4 &outBoundsMinX, Vec4 &outBoundsMinY, Vec4 &outBoundsMinZ, Vec4 &outBoundsMaxX, Vec4 &outBoundsMaxY, Vec4 &outBoundsMaxZ) +{ + Vec4 scale_x = inScale.SplatX(); + Vec4 scaled_min_x = scale_x * inBoxMinX; + Vec4 scaled_max_x = scale_x * inBoxMaxX; + outBoundsMinX = Vec4::sMin(scaled_min_x, scaled_max_x); // Negative scale can flip min and max + outBoundsMaxX = Vec4::sMax(scaled_min_x, scaled_max_x); + + Vec4 scale_y = inScale.SplatY(); + Vec4 scaled_min_y = scale_y * inBoxMinY; + Vec4 scaled_max_y = scale_y * inBoxMaxY; + outBoundsMinY = Vec4::sMin(scaled_min_y, scaled_max_y); + outBoundsMaxY = Vec4::sMax(scaled_min_y, scaled_max_y); + + Vec4 scale_z = inScale.SplatZ(); + Vec4 scaled_min_z = scale_z * inBoxMinZ; + Vec4 scaled_max_z = scale_z * inBoxMaxZ; + outBoundsMinZ = Vec4::sMin(scaled_min_z, scaled_max_z); + outBoundsMaxZ = Vec4::sMax(scaled_min_z, scaled_max_z); +} + +/// Enlarge 4 bounding boxes with extent (add to both sides) +JPH_INLINE void AABox4EnlargeWithExtent(Vec3Arg inExtent, Vec4 &ioBoundsMinX, Vec4 &ioBoundsMinY, Vec4 &ioBoundsMinZ, Vec4 &ioBoundsMaxX, Vec4 &ioBoundsMaxY, Vec4 &ioBoundsMaxZ) +{ + Vec4 extent_x = inExtent.SplatX(); + ioBoundsMinX -= extent_x; + ioBoundsMaxX += extent_x; + + Vec4 extent_y = inExtent.SplatY(); + ioBoundsMinY -= extent_y; + ioBoundsMaxY += extent_y; + + Vec4 extent_z = inExtent.SplatZ(); + ioBoundsMinZ -= extent_z; + ioBoundsMaxZ += extent_z; +} + +/// Test if 4 bounding boxes overlap with a point +JPH_INLINE UVec4 AABox4VsPoint(Vec3Arg inPoint, Vec4Arg inBoxMinX, Vec4Arg inBoxMinY, Vec4Arg inBoxMinZ, Vec4Arg inBoxMaxX, Vec4Arg inBoxMaxY, Vec4Arg inBoxMaxZ) +{ + // Splat point to 4 component vectors + Vec4 point_x = Vec4(inPoint).SplatX(); + Vec4 point_y = Vec4(inPoint).SplatY(); + Vec4 point_z = Vec4(inPoint).SplatZ(); + + // Test if point overlaps with box + UVec4 overlapx = UVec4::sAnd(Vec4::sGreaterOrEqual(point_x, inBoxMinX), Vec4::sLessOrEqual(point_x, inBoxMaxX)); + UVec4 overlapy = UVec4::sAnd(Vec4::sGreaterOrEqual(point_y, inBoxMinY), Vec4::sLessOrEqual(point_y, inBoxMaxY)); + UVec4 overlapz = UVec4::sAnd(Vec4::sGreaterOrEqual(point_z, inBoxMinZ), Vec4::sLessOrEqual(point_z, inBoxMaxZ)); + + // Test if all are overlapping + return UVec4::sAnd(UVec4::sAnd(overlapx, overlapy), overlapz); +} + +/// Test if 4 bounding boxes overlap with an oriented box +JPH_INLINE UVec4 AABox4VsBox(Mat44Arg inOrientation, Vec3Arg inHalfExtents, Vec4Arg inBoxMinX, Vec4Arg inBoxMinY, Vec4Arg inBoxMinZ, Vec4Arg inBoxMaxX, Vec4Arg inBoxMaxY, Vec4Arg inBoxMaxZ, float inEpsilon = 1.0e-6f) +{ + // Taken from: Real Time Collision Detection - Christer Ericson + // Chapter 4.4.1, page 103-105. + // Note that the code is swapped around: A is the aabox and B is the oriented box (this saves us from having to invert the orientation of the oriented box) + + // Compute translation vector t (the translation of B in the space of A) + Vec4 t[3] { + inOrientation.GetTranslation().SplatX() - 0.5f * (inBoxMinX + inBoxMaxX), + inOrientation.GetTranslation().SplatY() - 0.5f * (inBoxMinY + inBoxMaxY), + inOrientation.GetTranslation().SplatZ() - 0.5f * (inBoxMinZ + inBoxMaxZ) }; + + // Compute common subexpressions. Add in an epsilon term to + // counteract arithmetic errors when two edges are parallel and + // their cross product is (near) null (see text for details) + Vec3 epsilon = Vec3::sReplicate(inEpsilon); + Vec3 abs_r[3] { inOrientation.GetAxisX().Abs() + epsilon, inOrientation.GetAxisY().Abs() + epsilon, inOrientation.GetAxisZ().Abs() + epsilon }; + + // Half extents for a + Vec4 a_half_extents[3] { + 0.5f * (inBoxMaxX - inBoxMinX), + 0.5f * (inBoxMaxY - inBoxMinY), + 0.5f * (inBoxMaxZ - inBoxMinZ) }; + + // Half extents of b + Vec4 b_half_extents_x = inHalfExtents.SplatX(); + Vec4 b_half_extents_y = inHalfExtents.SplatY(); + Vec4 b_half_extents_z = inHalfExtents.SplatZ(); + + // Each component corresponds to 1 overlapping OBB vs ABB + UVec4 overlaps = UVec4(0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff); + + // Test axes L = A0, L = A1, L = A2 + Vec4 ra, rb; + for (int i = 0; i < 3; i++) + { + ra = a_half_extents[i]; + rb = b_half_extents_x * abs_r[0][i] + b_half_extents_y * abs_r[1][i] + b_half_extents_z * abs_r[2][i]; + overlaps = UVec4::sAnd(overlaps, Vec4::sLessOrEqual(t[i].Abs(), ra + rb)); + } + + // Test axes L = B0, L = B1, L = B2 + for (int i = 0; i < 3; i++) + { + ra = a_half_extents[0] * abs_r[i][0] + a_half_extents[1] * abs_r[i][1] + a_half_extents[2] * abs_r[i][2]; + rb = Vec4::sReplicate(inHalfExtents[i]); + overlaps = UVec4::sAnd(overlaps, Vec4::sLessOrEqual((t[0] * inOrientation(0, i) + t[1] * inOrientation(1, i) + t[2] * inOrientation(2, i)).Abs(), ra + rb)); + } + + // Test axis L = A0 x B0 + ra = a_half_extents[1] * abs_r[0][2] + a_half_extents[2] * abs_r[0][1]; + rb = b_half_extents_y * abs_r[2][0] + b_half_extents_z * abs_r[1][0]; + overlaps = UVec4::sAnd(overlaps, Vec4::sLessOrEqual((t[2] * inOrientation(1, 0) - t[1] * inOrientation(2, 0)).Abs(), ra + rb)); + + // Test axis L = A0 x B1 + ra = a_half_extents[1] * abs_r[1][2] + a_half_extents[2] * abs_r[1][1]; + rb = b_half_extents_x * abs_r[2][0] + b_half_extents_z * abs_r[0][0]; + overlaps = UVec4::sAnd(overlaps, Vec4::sLessOrEqual((t[2] * inOrientation(1, 1) - t[1] * inOrientation(2, 1)).Abs(), ra + rb)); + + // Test axis L = A0 x B2 + ra = a_half_extents[1] * abs_r[2][2] + a_half_extents[2] * abs_r[2][1]; + rb = b_half_extents_x * abs_r[1][0] + b_half_extents_y * abs_r[0][0]; + overlaps = UVec4::sAnd(overlaps, Vec4::sLessOrEqual((t[2] * inOrientation(1, 2) - t[1] * inOrientation(2, 2)).Abs(), ra + rb)); + + // Test axis L = A1 x B0 + ra = a_half_extents[0] * abs_r[0][2] + a_half_extents[2] * abs_r[0][0]; + rb = b_half_extents_y * abs_r[2][1] + b_half_extents_z * abs_r[1][1]; + overlaps = UVec4::sAnd(overlaps, Vec4::sLessOrEqual((t[0] * inOrientation(2, 0) - t[2] * inOrientation(0, 0)).Abs(), ra + rb)); + + // Test axis L = A1 x B1 + ra = a_half_extents[0] * abs_r[1][2] + a_half_extents[2] * abs_r[1][0]; + rb = b_half_extents_x * abs_r[2][1] + b_half_extents_z * abs_r[0][1]; + overlaps = UVec4::sAnd(overlaps, Vec4::sLessOrEqual((t[0] * inOrientation(2, 1) - t[2] * inOrientation(0, 1)).Abs(), ra + rb)); + + // Test axis L = A1 x B2 + ra = a_half_extents[0] * abs_r[2][2] + a_half_extents[2] * abs_r[2][0]; + rb = b_half_extents_x * abs_r[1][1] + b_half_extents_y * abs_r[0][1]; + overlaps = UVec4::sAnd(overlaps, Vec4::sLessOrEqual((t[0] * inOrientation(2, 2) - t[2] * inOrientation(0, 2)).Abs(), ra + rb)); + + // Test axis L = A2 x B0 + ra = a_half_extents[0] * abs_r[0][1] + a_half_extents[1] * abs_r[0][0]; + rb = b_half_extents_y * abs_r[2][2] + b_half_extents_z * abs_r[1][2]; + overlaps = UVec4::sAnd(overlaps, Vec4::sLessOrEqual((t[1] * inOrientation(0, 0) - t[0] * inOrientation(1, 0)).Abs(), ra + rb)); + + // Test axis L = A2 x B1 + ra = a_half_extents[0] * abs_r[1][1] + a_half_extents[1] * abs_r[1][0]; + rb = b_half_extents_x * abs_r[2][2] + b_half_extents_z * abs_r[0][2]; + overlaps = UVec4::sAnd(overlaps, Vec4::sLessOrEqual((t[1] * inOrientation(0, 1) - t[0] * inOrientation(1, 1)).Abs(), ra + rb)); + + // Test axis L = A2 x B2 + ra = a_half_extents[0] * abs_r[2][1] + a_half_extents[1] * abs_r[2][0]; + rb = b_half_extents_x * abs_r[1][2] + b_half_extents_y * abs_r[0][2]; + overlaps = UVec4::sAnd(overlaps, Vec4::sLessOrEqual((t[1] * inOrientation(0, 2) - t[0] * inOrientation(1, 2)).Abs(), ra + rb)); + + // Return if the OBB vs AABBs are intersecting + return overlaps; +} + +/// Convenience function that tests 4 AABoxes vs OrientedBox +JPH_INLINE UVec4 AABox4VsBox(const OrientedBox &inBox, Vec4Arg inBoxMinX, Vec4Arg inBoxMinY, Vec4Arg inBoxMinZ, Vec4Arg inBoxMaxX, Vec4Arg inBoxMaxY, Vec4Arg inBoxMaxZ, float inEpsilon = 1.0e-6f) +{ + return AABox4VsBox(inBox.mOrientation, inBox.mHalfExtents, inBoxMinX, inBoxMinY, inBoxMinZ, inBoxMaxX, inBoxMaxY, inBoxMaxZ, inEpsilon); +} + +/// Get the squared distance between 4 AABoxes and a point +JPH_INLINE Vec4 AABox4DistanceSqToPoint(Vec4Arg inPointX, Vec4Arg inPointY, Vec4Arg inPointZ, Vec4Arg inBoxMinX, Vec4Arg inBoxMinY, Vec4Arg inBoxMinZ, Vec4Arg inBoxMaxX, Vec4Arg inBoxMaxY, Vec4Arg inBoxMaxZ) +{ + // Get closest point on box + Vec4 closest_x = Vec4::sMin(Vec4::sMax(inPointX, inBoxMinX), inBoxMaxX); + Vec4 closest_y = Vec4::sMin(Vec4::sMax(inPointY, inBoxMinY), inBoxMaxY); + Vec4 closest_z = Vec4::sMin(Vec4::sMax(inPointZ, inBoxMinZ), inBoxMaxZ); + + // Return the squared distance between the box and point + return Square(closest_x - inPointX) + Square(closest_y - inPointY) + Square(closest_z - inPointZ); +} + +/// Get the squared distance between 4 AABoxes and a point +JPH_INLINE Vec4 AABox4DistanceSqToPoint(Vec3 inPoint, Vec4Arg inBoxMinX, Vec4Arg inBoxMinY, Vec4Arg inBoxMinZ, Vec4Arg inBoxMaxX, Vec4Arg inBoxMaxY, Vec4Arg inBoxMaxZ) +{ + return AABox4DistanceSqToPoint(inPoint.SplatX(), inPoint.SplatY(), inPoint.SplatZ(), inBoxMinX, inBoxMinY, inBoxMinZ, inBoxMaxX, inBoxMaxY, inBoxMaxZ); +} + +/// Test 4 AABoxes vs a sphere +JPH_INLINE UVec4 AABox4VsSphere(Vec4Arg inCenterX, Vec4Arg inCenterY, Vec4Arg inCenterZ, Vec4Arg inRadiusSq, Vec4Arg inBoxMinX, Vec4Arg inBoxMinY, Vec4Arg inBoxMinZ, Vec4Arg inBoxMaxX, Vec4Arg inBoxMaxY, Vec4Arg inBoxMaxZ) +{ + // Test the distance from the center of the sphere to the box is smaller than the radius + Vec4 distance_sq = AABox4DistanceSqToPoint(inCenterX, inCenterY, inCenterZ, inBoxMinX, inBoxMinY, inBoxMinZ, inBoxMaxX, inBoxMaxY, inBoxMaxZ); + return Vec4::sLessOrEqual(distance_sq, inRadiusSq); +} + +/// Test 4 AABoxes vs a sphere +JPH_INLINE UVec4 AABox4VsSphere(Vec3Arg inCenter, float inRadiusSq, Vec4Arg inBoxMinX, Vec4Arg inBoxMinY, Vec4Arg inBoxMinZ, Vec4Arg inBoxMaxX, Vec4Arg inBoxMaxY, Vec4Arg inBoxMaxZ) +{ + return AABox4VsSphere(inCenter.SplatX(), inCenter.SplatY(), inCenter.SplatZ(), Vec4::sReplicate(inRadiusSq), inBoxMinX, inBoxMinY, inBoxMinZ, inBoxMaxX, inBoxMaxY, inBoxMaxZ); +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/ClipPoly.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/ClipPoly.h new file mode 100644 index 00000000000..e0ef4f22b3b --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/ClipPoly.h @@ -0,0 +1,200 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +/// Clip inPolygonToClip against the positive halfspace of plane defined by inPlaneOrigin and inPlaneNormal. +/// inPlaneNormal does not need to be normalized. +template +void ClipPolyVsPlane(const VERTEX_ARRAY &inPolygonToClip, Vec3Arg inPlaneOrigin, Vec3Arg inPlaneNormal, VERTEX_ARRAY &outClippedPolygon) +{ + JPH_ASSERT(inPolygonToClip.size() >= 2); + JPH_ASSERT(outClippedPolygon.empty()); + + // Determine state of last point + Vec3 e1 = inPolygonToClip[inPolygonToClip.size() - 1]; + float prev_num = (inPlaneOrigin - e1).Dot(inPlaneNormal); + bool prev_inside = prev_num < 0.0f; + + // Loop through all vertices + for (typename VERTEX_ARRAY::size_type j = 0; j < inPolygonToClip.size(); ++j) + { + // Check if second point is inside + Vec3Arg e2 = inPolygonToClip[j]; + float num = (inPlaneOrigin - e2).Dot(inPlaneNormal); + bool cur_inside = num < 0.0f; + + // In -> Out or Out -> In: Add point on clipping plane + if (cur_inside != prev_inside) + { + // Solve: (X - inPlaneOrigin) . inPlaneNormal = 0 and X = e1 + t * (e2 - e1) for X + Vec3 e12 = e2 - e1; + float denom = e12.Dot(inPlaneNormal); + if (denom != 0.0f) + outClippedPolygon.push_back(e1 + (prev_num / denom) * e12); + else + cur_inside = prev_inside; // Edge is parallel to plane, treat point as if it were on the same side as the last point + } + + // Point inside, add it + if (cur_inside) + outClippedPolygon.push_back(e2); + + // Update previous state + prev_num = num; + prev_inside = cur_inside; + e1 = e2; + } +} + +/// Clip polygon versus polygon. +/// Both polygons are assumed to be in counter clockwise order. +/// @param inClippingPolygonNormal is used to create planes of all edges in inClippingPolygon against which inPolygonToClip is clipped, inClippingPolygonNormal does not need to be normalized +/// @param inClippingPolygon is the polygon which inClippedPolygon is clipped against +/// @param inPolygonToClip is the polygon that is clipped +/// @param outClippedPolygon will contain clipped polygon when function returns +template +void ClipPolyVsPoly(const VERTEX_ARRAY &inPolygonToClip, const VERTEX_ARRAY &inClippingPolygon, Vec3Arg inClippingPolygonNormal, VERTEX_ARRAY &outClippedPolygon) +{ + JPH_ASSERT(inPolygonToClip.size() >= 2); + JPH_ASSERT(inClippingPolygon.size() >= 3); + + VERTEX_ARRAY tmp_vertices[2]; + int tmp_vertices_idx = 0; + + for (typename VERTEX_ARRAY::size_type i = 0; i < inClippingPolygon.size(); ++i) + { + // Get edge to clip against + Vec3 clip_e1 = inClippingPolygon[i]; + Vec3 clip_e2 = inClippingPolygon[(i + 1) % inClippingPolygon.size()]; + Vec3 clip_normal = inClippingPolygonNormal.Cross(clip_e2 - clip_e1); // Pointing inward to the clipping polygon + + // Get source and target polygon + const VERTEX_ARRAY &src_polygon = (i == 0)? inPolygonToClip : tmp_vertices[tmp_vertices_idx]; + tmp_vertices_idx ^= 1; + VERTEX_ARRAY &tgt_polygon = (i == inClippingPolygon.size() - 1)? outClippedPolygon : tmp_vertices[tmp_vertices_idx]; + tgt_polygon.clear(); + + // Clip against the edge + ClipPolyVsPlane(src_polygon, clip_e1, clip_normal, tgt_polygon); + + // Break out if no polygon left + if (tgt_polygon.size() < 3) + { + outClippedPolygon.clear(); + break; + } + } +} + +/// Clip inPolygonToClip against an edge, the edge is projected on inPolygonToClip using inClippingEdgeNormal. +/// The positive half space (the side on the edge in the direction of inClippingEdgeNormal) is cut away. +template +void ClipPolyVsEdge(const VERTEX_ARRAY &inPolygonToClip, Vec3Arg inEdgeVertex1, Vec3Arg inEdgeVertex2, Vec3Arg inClippingEdgeNormal, VERTEX_ARRAY &outClippedPolygon) +{ + JPH_ASSERT(inPolygonToClip.size() >= 3); + JPH_ASSERT(outClippedPolygon.empty()); + + // Get normal that is perpendicular to the edge and the clipping edge normal + Vec3 edge = inEdgeVertex2 - inEdgeVertex1; + Vec3 edge_normal = inClippingEdgeNormal.Cross(edge); + + // Project vertices of edge on inPolygonToClip + Vec3 polygon_normal = (inPolygonToClip[2] - inPolygonToClip[0]).Cross(inPolygonToClip[1] - inPolygonToClip[0]); + float polygon_normal_len_sq = polygon_normal.LengthSq(); + Vec3 v1 = inEdgeVertex1 + polygon_normal.Dot(inPolygonToClip[0] - inEdgeVertex1) * polygon_normal / polygon_normal_len_sq; + Vec3 v2 = inEdgeVertex2 + polygon_normal.Dot(inPolygonToClip[0] - inEdgeVertex2) * polygon_normal / polygon_normal_len_sq; + Vec3 v12 = v2 - v1; + float v12_len_sq = v12.LengthSq(); + + // Determine state of last point + Vec3 e1 = inPolygonToClip[inPolygonToClip.size() - 1]; + float prev_num = (inEdgeVertex1 - e1).Dot(edge_normal); + bool prev_inside = prev_num < 0.0f; + + // Loop through all vertices + for (typename VERTEX_ARRAY::size_type j = 0; j < inPolygonToClip.size(); ++j) + { + // Check if second point is inside + Vec3 e2 = inPolygonToClip[j]; + float num = (inEdgeVertex1 - e2).Dot(edge_normal); + bool cur_inside = num < 0.0f; + + // In -> Out or Out -> In: Add point on clipping plane + if (cur_inside != prev_inside) + { + // Solve: (X - inPlaneOrigin) . inPlaneNormal = 0 and X = e1 + t * (e2 - e1) for X + Vec3 e12 = e2 - e1; + float denom = e12.Dot(edge_normal); + Vec3 clipped_point = e1 + (prev_num / denom) * e12; + + // Project point on line segment v1, v2 so see if it falls outside if the edge + float projection = (clipped_point - v1).Dot(v12); + if (projection < 0.0f) + outClippedPolygon.push_back(v1); + else if (projection > v12_len_sq) + outClippedPolygon.push_back(v2); + else + outClippedPolygon.push_back(clipped_point); + } + + // Update previous state + prev_num = num; + prev_inside = cur_inside; + e1 = e2; + } +} + +/// Clip polygon vs axis aligned box, inPolygonToClip is assume to be in counter clockwise order. +/// Output will be stored in outClippedPolygon. Everything inside inAABox will be kept. +template +void ClipPolyVsAABox(const VERTEX_ARRAY &inPolygonToClip, const AABox &inAABox, VERTEX_ARRAY &outClippedPolygon) +{ + JPH_ASSERT(inPolygonToClip.size() >= 2); + + VERTEX_ARRAY tmp_vertices[2]; + int tmp_vertices_idx = 0; + + for (int coord = 0; coord < 3; ++coord) + for (int side = 0; side < 2; ++side) + { + // Get plane to clip against + Vec3 origin = Vec3::sZero(), normal = Vec3::sZero(); + if (side == 0) + { + normal.SetComponent(coord, 1.0f); + origin.SetComponent(coord, inAABox.mMin[coord]); + } + else + { + normal.SetComponent(coord, -1.0f); + origin.SetComponent(coord, inAABox.mMax[coord]); + } + + // Get source and target polygon + const VERTEX_ARRAY &src_polygon = tmp_vertices_idx == 0? inPolygonToClip : tmp_vertices[tmp_vertices_idx & 1]; + tmp_vertices_idx++; + VERTEX_ARRAY &tgt_polygon = tmp_vertices_idx == 6? outClippedPolygon : tmp_vertices[tmp_vertices_idx & 1]; + tgt_polygon.clear(); + + // Clip against the edge + ClipPolyVsPlane(src_polygon, origin, normal, tgt_polygon); + + // Break out if no polygon left + if (tgt_polygon.size() < 3) + { + outClippedPolygon.clear(); + return; + } + + // Flip normal + normal = -normal; + } +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/ClosestPoint.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/ClosestPoint.h new file mode 100644 index 00000000000..a437763f574 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/ClosestPoint.h @@ -0,0 +1,498 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +JPH_NAMESPACE_BEGIN + +// Turn off fused multiply add instruction because it makes the equations of the form a * b - c * d inaccurate below +JPH_PRECISE_MATH_ON + +/// Helper utils to find the closest point to a line segment, triangle or tetrahedron +namespace ClosestPoint +{ + /// Compute barycentric coordinates of closest point to origin for infinite line defined by (inA, inB) + /// Point can then be computed as inA * outU + inB * outV + /// Returns false if the points inA, inB do not form a line (are at the same point) + inline bool GetBaryCentricCoordinates(Vec3Arg inA, Vec3Arg inB, float &outU, float &outV) + { + Vec3 ab = inB - inA; + float denominator = ab.LengthSq(); + if (denominator < Square(FLT_EPSILON)) + { + // Degenerate line segment, fallback to points + if (inA.LengthSq() < inB.LengthSq()) + { + // A closest + outU = 1.0f; + outV = 0.0f; + } + else + { + // B closest + outU = 0.0f; + outV = 1.0f; + } + return false; + } + else + { + outV = -inA.Dot(ab) / denominator; + outU = 1.0f - outV; + } + return true; + } + + /// Compute barycentric coordinates of closest point to origin for plane defined by (inA, inB, inC) + /// Point can then be computed as inA * outU + inB * outV + inC * outW + /// Returns false if the points inA, inB, inC do not form a plane (are on the same line or at the same point) + inline bool GetBaryCentricCoordinates(Vec3Arg inA, Vec3Arg inB, Vec3Arg inC, float &outU, float &outV, float &outW) + { + // Taken from: Real-Time Collision Detection - Christer Ericson (Section: Barycentric Coordinates) + // With p = 0 + // Adjusted to always include the shortest edge of the triangle in the calculation to improve numerical accuracy + + // First calculate the three edges + Vec3 v0 = inB - inA; + Vec3 v1 = inC - inA; + Vec3 v2 = inC - inB; + + // Make sure that the shortest edge is included in the calculation to keep the products a * b - c * d as small as possible to preserve accuracy + float d00 = v0.LengthSq(); + float d11 = v1.LengthSq(); + float d22 = v2.LengthSq(); + if (d00 <= d22) + { + // Use v0 and v1 to calculate barycentric coordinates + float d01 = v0.Dot(v1); + + // Denominator must be positive: + // |v0|^2 * |v1|^2 - (v0 . v1)^2 = |v0|^2 * |v1|^2 * (1 - cos(angle)^2) >= 0 + float denominator = d00 * d11 - d01 * d01; + if (denominator < 1.0e-12f) + { + // Degenerate triangle, return coordinates along longest edge + if (d00 > d11) + { + GetBaryCentricCoordinates(inA, inB, outU, outV); + outW = 0.0f; + } + else + { + GetBaryCentricCoordinates(inA, inC, outU, outW); + outV = 0.0f; + } + return false; + } + else + { + float a0 = inA.Dot(v0); + float a1 = inA.Dot(v1); + outV = (d01 * a1 - d11 * a0) / denominator; + outW = (d01 * a0 - d00 * a1) / denominator; + outU = 1.0f - outV - outW; + } + } + else + { + // Use v1 and v2 to calculate barycentric coordinates + float d12 = v1.Dot(v2); + + float denominator = d11 * d22 - d12 * d12; + if (denominator < 1.0e-12f) + { + // Degenerate triangle, return coordinates along longest edge + if (d11 > d22) + { + GetBaryCentricCoordinates(inA, inC, outU, outW); + outV = 0.0f; + } + else + { + GetBaryCentricCoordinates(inB, inC, outV, outW); + outU = 0.0f; + } + return false; + } + else + { + float c1 = inC.Dot(v1); + float c2 = inC.Dot(v2); + outU = (d22 * c1 - d12 * c2) / denominator; + outV = (d11 * c2 - d12 * c1) / denominator; + outW = 1.0f - outU - outV; + } + } + return true; + } + + /// Get the closest point to the origin of line (inA, inB) + /// outSet describes which features are closest: 1 = a, 2 = b, 3 = line segment ab + inline Vec3 GetClosestPointOnLine(Vec3Arg inA, Vec3Arg inB, uint32 &outSet) + { + float u, v; + GetBaryCentricCoordinates(inA, inB, u, v); + if (v <= 0.0f) + { + // inA is closest point + outSet = 0b0001; + return inA; + } + else if (u <= 0.0f) + { + // inB is closest point + outSet = 0b0010; + return inB; + } + else + { + // Closest point lies on line inA inB + outSet = 0b0011; + return u * inA + v * inB; + } + } + + /// Get the closest point to the origin of triangle (inA, inB, inC) + /// outSet describes which features are closest: 1 = a, 2 = b, 4 = c, 5 = line segment ac, 7 = triangle interior etc. + /// If MustIncludeC is true, the function assumes that C is part of the closest feature (vertex, edge, face) and does less work, if the assumption is not true then a closest point to the other features is returned. + template + inline Vec3 GetClosestPointOnTriangle(Vec3Arg inA, Vec3Arg inB, Vec3Arg inC, uint32 &outSet) + { + // Taken from: Real-Time Collision Detection - Christer Ericson (Section: Closest Point on Triangle to Point) + // With p = 0 + + // The most accurate normal is calculated by using the two shortest edges + // See: https://box2d.org/posts/2014/01/troublesome-triangle/ + // The difference in normals is most pronounced when one edge is much smaller than the others (in which case the other 2 must have roughly the same length). + // Therefore we can suffice by just picking the shortest from 2 edges and use that with the 3rd edge to calculate the normal. + // We first check which of the edges is shorter and if bc is shorter than ac then we swap a with c to a is always on the shortest edge + UVec4 swap_ac; + { + Vec3 ac = inC - inA; + Vec3 bc = inC - inB; + swap_ac = Vec4::sLess(bc.DotV4(bc), ac.DotV4(ac)); + } + Vec3 a = Vec3::sSelect(inA, inC, swap_ac); + Vec3 c = Vec3::sSelect(inC, inA, swap_ac); + + // Calculate normal + Vec3 ab = inB - a; + Vec3 ac = c - a; + Vec3 n = ab.Cross(ac); + float n_len_sq = n.LengthSq(); + + // Check degenerate + if (n_len_sq < 1.0e-10f) // Square(FLT_EPSILON) was too small and caused numerical problems, see test case TestCollideParallelTriangleVsCapsule + { + // Degenerate, fallback to vertices and edges + + // Start with vertex C being the closest + uint32 closest_set = 0b0100; + Vec3 closest_point = inC; + float best_dist_sq = inC.LengthSq(); + + // If the closest point must include C then A or B cannot be closest + // Note that we test vertices first because we want to prefer a closest vertex over a closest edge (this results in an outSet with fewer bits set) + if constexpr (!MustIncludeC) + { + // Try vertex A + float a_len_sq = inA.LengthSq(); + if (a_len_sq < best_dist_sq) + { + closest_set = 0b0001; + closest_point = inA; + best_dist_sq = a_len_sq; + } + + // Try vertex B + float b_len_sq = inB.LengthSq(); + if (b_len_sq < best_dist_sq) + { + closest_set = 0b0010; + closest_point = inB; + best_dist_sq = b_len_sq; + } + } + + // Edge AC + float ac_len_sq = ac.LengthSq(); + if (ac_len_sq > Square(FLT_EPSILON)) + { + float v = Clamp(-a.Dot(ac) / ac_len_sq, 0.0f, 1.0f); + Vec3 q = a + v * ac; + float dist_sq = q.LengthSq(); + if (dist_sq < best_dist_sq) + { + closest_set = 0b0101; + closest_point = q; + best_dist_sq = dist_sq; + } + } + + // Edge BC + Vec3 bc = inC - inB; + float bc_len_sq = bc.LengthSq(); + if (bc_len_sq > Square(FLT_EPSILON)) + { + float v = Clamp(-inB.Dot(bc) / bc_len_sq, 0.0f, 1.0f); + Vec3 q = inB + v * bc; + float dist_sq = q.LengthSq(); + if (dist_sq < best_dist_sq) + { + closest_set = 0b0110; + closest_point = q; + best_dist_sq = dist_sq; + } + } + + // If the closest point must include C then AB cannot be closest + if constexpr (!MustIncludeC) + { + // Edge AB + ab = inB - inA; + float ab_len_sq = ab.LengthSq(); + if (ab_len_sq > Square(FLT_EPSILON)) + { + float v = Clamp(-inA.Dot(ab) / ab_len_sq, 0.0f, 1.0f); + Vec3 q = inA + v * ab; + float dist_sq = q.LengthSq(); + if (dist_sq < best_dist_sq) + { + closest_set = 0b0011; + closest_point = q; + best_dist_sq = dist_sq; + } + } + } + + outSet = closest_set; + return closest_point; + } + + // Check if P in vertex region outside A + Vec3 ap = -a; + float d1 = ab.Dot(ap); + float d2 = ac.Dot(ap); + if (d1 <= 0.0f && d2 <= 0.0f) + { + outSet = swap_ac.GetX()? 0b0100 : 0b0001; + return a; // barycentric coordinates (1,0,0) + } + + // Check if P in vertex region outside B + Vec3 bp = -inB; + float d3 = ab.Dot(bp); + float d4 = ac.Dot(bp); + if (d3 >= 0.0f && d4 <= d3) + { + outSet = 0b0010; + return inB; // barycentric coordinates (0,1,0) + } + + // Check if P in edge region of AB, if so return projection of P onto AB + if (d1 * d4 <= d3 * d2 && d1 >= 0.0f && d3 <= 0.0f) + { + float v = d1 / (d1 - d3); + outSet = swap_ac.GetX()? 0b0110 : 0b0011; + return a + v * ab; // barycentric coordinates (1-v,v,0) + } + + // Check if P in vertex region outside C + Vec3 cp = -c; + float d5 = ab.Dot(cp); + float d6 = ac.Dot(cp); + if (d6 >= 0.0f && d5 <= d6) + { + outSet = swap_ac.GetX()? 0b0001 : 0b0100; + return c; // barycentric coordinates (0,0,1) + } + + // Check if P in edge region of AC, if so return projection of P onto AC + if (d5 * d2 <= d1 * d6 && d2 >= 0.0f && d6 <= 0.0f) + { + float w = d2 / (d2 - d6); + outSet = 0b0101; + return a + w * ac; // barycentric coordinates (1-w,0,w) + } + + // Check if P in edge region of BC, if so return projection of P onto BC + float d4_d3 = d4 - d3; + float d5_d6 = d5 - d6; + if (d3 * d6 <= d5 * d4 && d4_d3 >= 0.0f && d5_d6 >= 0.0f) + { + float w = d4_d3 / (d4_d3 + d5_d6); + outSet = swap_ac.GetX()? 0b0011 : 0b0110; + return inB + w * (c - inB); // barycentric coordinates (0,1-w,w) + } + + // P inside face region. + // Here we deviate from Christer Ericson's article to improve accuracy. + // Determine distance between triangle and origin: distance = (centroid - origin) . normal / |normal| + // Closest point to origin is then: distance . normal / |normal| + // Note that this way of calculating the closest point is much more accurate than first calculating barycentric coordinates + // and then calculating the closest point based on those coordinates. + outSet = 0b0111; + return n * (a + inB + c).Dot(n) / (3.0f * n_len_sq); + } + + /// Check if the origin is outside the plane of triangle (inA, inB, inC). inD specifies the front side of the plane. + inline bool OriginOutsideOfPlane(Vec3Arg inA, Vec3Arg inB, Vec3Arg inC, Vec3Arg inD) + { + // Taken from: Real-Time Collision Detection - Christer Ericson (Section: Closest Point on Tetrahedron to Point) + // With p = 0 + + // Test if point p and d lie on opposite sides of plane through abc + Vec3 n = (inB - inA).Cross(inC - inA); + float signp = inA.Dot(n); // [AP AB AC] + float signd = (inD - inA).Dot(n); // [AD AB AC] + + // Points on opposite sides if expression signs are the same + // Note that we left out the minus sign in signp so we need to check > 0 instead of < 0 as in Christer's book + // We compare against a small negative value to allow for a little bit of slop in the calculations + return signp * signd > -FLT_EPSILON; + } + + /// Returns for each of the planes of the tetrahedron if the origin is inside it + /// Roughly equivalent to: + /// [OriginOutsideOfPlane(inA, inB, inC, inD), + /// OriginOutsideOfPlane(inA, inC, inD, inB), + /// OriginOutsideOfPlane(inA, inD, inB, inC), + /// OriginOutsideOfPlane(inB, inD, inC, inA)] + inline UVec4 OriginOutsideOfTetrahedronPlanes(Vec3Arg inA, Vec3Arg inB, Vec3Arg inC, Vec3Arg inD) + { + Vec3 ab = inB - inA; + Vec3 ac = inC - inA; + Vec3 ad = inD - inA; + Vec3 bd = inD - inB; + Vec3 bc = inC - inB; + + Vec3 ab_cross_ac = ab.Cross(ac); + Vec3 ac_cross_ad = ac.Cross(ad); + Vec3 ad_cross_ab = ad.Cross(ab); + Vec3 bd_cross_bc = bd.Cross(bc); + + // For each plane get the side on which the origin is + float signp0 = inA.Dot(ab_cross_ac); // ABC + float signp1 = inA.Dot(ac_cross_ad); // ACD + float signp2 = inA.Dot(ad_cross_ab); // ADB + float signp3 = inB.Dot(bd_cross_bc); // BDC + Vec4 signp(signp0, signp1, signp2, signp3); + + // For each plane get the side that is outside (determined by the 4th point) + float signd0 = ad.Dot(ab_cross_ac); // D + float signd1 = ab.Dot(ac_cross_ad); // B + float signd2 = ac.Dot(ad_cross_ab); // C + float signd3 = -ab.Dot(bd_cross_bc); // A + Vec4 signd(signd0, signd1, signd2, signd3); + + // The winding of all triangles has been chosen so that signd should have the + // same sign for all components. If this is not the case the tetrahedron + // is degenerate and we return that the origin is in front of all sides + int sign_bits = signd.GetSignBits(); + switch (sign_bits) + { + case 0: + // All positive + return Vec4::sGreaterOrEqual(signp, Vec4::sReplicate(-FLT_EPSILON)); + + case 0xf: + // All negative + return Vec4::sLessOrEqual(signp, Vec4::sReplicate(FLT_EPSILON)); + + default: + // Mixed signs, degenerate tetrahedron + return UVec4::sReplicate(0xffffffff); + } + } + + /// Get the closest point between tetrahedron (inA, inB, inC, inD) to the origin + /// outSet specifies which feature was closest, 1 = a, 2 = b, 4 = c, 8 = d. Edges have 2 bits set, triangles 3 and if the point is in the interior 4 bits are set. + /// If MustIncludeD is true, the function assumes that D is part of the closest feature (vertex, edge, face, tetrahedron) and does less work, if the assumption is not true then a closest point to the other features is returned. + template + inline Vec3 GetClosestPointOnTetrahedron(Vec3Arg inA, Vec3Arg inB, Vec3Arg inC, Vec3Arg inD, uint32 &outSet) + { + // Taken from: Real-Time Collision Detection - Christer Ericson (Section: Closest Point on Tetrahedron to Point) + // With p = 0 + + // Start out assuming point inside all halfspaces, so closest to itself + uint32 closest_set = 0b1111; + Vec3 closest_point = Vec3::sZero(); + float best_dist_sq = FLT_MAX; + + // Determine for each of the faces of the tetrahedron if the origin is in front of the plane + UVec4 origin_out_of_planes = OriginOutsideOfTetrahedronPlanes(inA, inB, inC, inD); + + // If point outside face abc then compute closest point on abc + if (origin_out_of_planes.GetX()) // OriginOutsideOfPlane(inA, inB, inC, inD) + { + if constexpr (MustIncludeD) + { + // If the closest point must include D then ABC cannot be closest but the closest point + // cannot be an interior point either so we return A as closest point + closest_set = 0b0001; + closest_point = inA; + } + else + { + // Test the face normally + closest_point = GetClosestPointOnTriangle(inA, inB, inC, closest_set); + } + best_dist_sq = closest_point.LengthSq(); + } + + // Repeat test for face acd + if (origin_out_of_planes.GetY()) // OriginOutsideOfPlane(inA, inC, inD, inB) + { + uint32 set; + Vec3 q = GetClosestPointOnTriangle(inA, inC, inD, set); + float dist_sq = q.LengthSq(); + if (dist_sq < best_dist_sq) + { + best_dist_sq = dist_sq; + closest_point = q; + closest_set = (set & 0b0001) + ((set & 0b0110) << 1); + } + } + + // Repeat test for face adb + if (origin_out_of_planes.GetZ()) // OriginOutsideOfPlane(inA, inD, inB, inC) + { + // Keep original vertex order, it doesn't matter if the triangle is facing inward or outward + // and it improves consistency for GJK which will always add a new vertex D and keep the closest + // feature from the previous iteration in ABC + uint32 set; + Vec3 q = GetClosestPointOnTriangle(inA, inB, inD, set); + float dist_sq = q.LengthSq(); + if (dist_sq < best_dist_sq) + { + best_dist_sq = dist_sq; + closest_point = q; + closest_set = (set & 0b0011) + ((set & 0b0100) << 1); + } + } + + // Repeat test for face bdc + if (origin_out_of_planes.GetW()) // OriginOutsideOfPlane(inB, inD, inC, inA) + { + // Keep original vertex order, it doesn't matter if the triangle is facing inward or outward + // and it improves consistency for GJK which will always add a new vertex D and keep the closest + // feature from the previous iteration in ABC + uint32 set; + Vec3 q = GetClosestPointOnTriangle(inB, inC, inD, set); + float dist_sq = q.LengthSq(); + if (dist_sq < best_dist_sq) + { + closest_point = q; + closest_set = set << 1; + } + } + + outSet = closest_set; + return closest_point; + } +}; + +JPH_PRECISE_MATH_OFF + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/ConvexHullBuilder.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/ConvexHullBuilder.cpp new file mode 100644 index 00000000000..16ccf89c899 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/ConvexHullBuilder.cpp @@ -0,0 +1,1465 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#include +#include + +JPH_SUPPRESS_WARNINGS_STD_BEGIN +#include +JPH_SUPPRESS_WARNINGS_STD_END + +#ifdef JPH_CONVEX_BUILDER_DEBUG + #include +#endif + +JPH_NAMESPACE_BEGIN + +ConvexHullBuilder::Face::~Face() +{ + // Free all edges + Edge *e = mFirstEdge; + if (e != nullptr) + { + do + { + Edge *next = e->mNextEdge; + delete e; + e = next; + } while (e != mFirstEdge); + } +} + +void ConvexHullBuilder::Face::CalculateNormalAndCentroid(const Vec3 *inPositions) +{ + // Get point that we use to construct a triangle fan + Edge *e = mFirstEdge; + Vec3 y0 = inPositions[e->mStartIdx]; + + // Get the 2nd point + e = e->mNextEdge; + Vec3 y1 = inPositions[e->mStartIdx]; + + // Start accumulating the centroid + mCentroid = y0 + y1; + int n = 2; + + // Start accumulating the normal + mNormal = Vec3::sZero(); + + // Loop over remaining edges accumulating normals in a triangle fan fashion + for (e = e->mNextEdge; e != mFirstEdge; e = e->mNextEdge) + { + // Get the 3rd point + Vec3 y2 = inPositions[e->mStartIdx]; + + // Calculate edges (counter clockwise) + Vec3 e0 = y1 - y0; + Vec3 e1 = y2 - y1; + Vec3 e2 = y0 - y2; + + // The best normal is calculated by using the two shortest edges + // See: https://box2d.org/posts/2014/01/troublesome-triangle/ + // The difference in normals is most pronounced when one edge is much smaller than the others (in which case the others must have roughly the same length). + // Therefore we can suffice by just picking the shortest from 2 edges and use that with the 3rd edge to calculate the normal. + // We first check which of the edges is shorter: e1 or e2 + UVec4 e1_shorter_than_e2 = Vec4::sLess(e1.DotV4(e1), e2.DotV4(e2)); + + // We calculate both normals and then select the one that had the shortest edge for our normal (this avoids branching) + Vec3 normal_e01 = e0.Cross(e1); + Vec3 normal_e02 = e2.Cross(e0); + mNormal += Vec3::sSelect(normal_e02, normal_e01, e1_shorter_than_e2); + + // Accumulate centroid + mCentroid += y2; + n++; + + // Update y1 for next triangle + y1 = y2; + } + + // Finalize centroid + mCentroid /= float(n); +} + +void ConvexHullBuilder::Face::Initialize(int inIdx0, int inIdx1, int inIdx2, const Vec3 *inPositions) +{ + JPH_ASSERT(mFirstEdge == nullptr); + JPH_ASSERT(inIdx0 != inIdx1 && inIdx0 != inIdx2 && inIdx1 != inIdx2); + + // Create 3 edges + Edge *e0 = new Edge(this, inIdx0); + Edge *e1 = new Edge(this, inIdx1); + Edge *e2 = new Edge(this, inIdx2); + + // Link edges + e0->mNextEdge = e1; + e1->mNextEdge = e2; + e2->mNextEdge = e0; + mFirstEdge = e0; + + CalculateNormalAndCentroid(inPositions); +} + +ConvexHullBuilder::ConvexHullBuilder(const Positions &inPositions) : + mPositions(inPositions) +{ +#ifdef JPH_CONVEX_BUILDER_DEBUG + mIteration = 0; + + // Center the drawing of the first hull around the origin and calculate the delta offset between states + mOffset = RVec3::sZero(); + if (mPositions.empty()) + { + // No hull will be generated + mDelta = Vec3::sZero(); + } + else + { + Vec3 maxv = Vec3::sReplicate(-FLT_MAX), minv = Vec3::sReplicate(FLT_MAX); + for (Vec3 v : mPositions) + { + minv = Vec3::sMin(minv, v); + maxv = Vec3::sMax(maxv, v); + mOffset -= v; + } + mOffset /= Real(mPositions.size()); + mDelta = Vec3((maxv - minv).GetX() + 0.5f, 0, 0); + mOffset += mDelta; // Don't start at origin, we're already drawing the final hull there + } +#endif +} + +void ConvexHullBuilder::FreeFaces() +{ + for (Face *f : mFaces) + delete f; + mFaces.clear(); +} + +void ConvexHullBuilder::GetFaceForPoint(Vec3Arg inPoint, const Faces &inFaces, Face *&outFace, float &outDistSq) const +{ + outFace = nullptr; + outDistSq = 0.0f; + + for (Face *f : inFaces) + if (!f->mRemoved) + { + // Determine distance to face + float dot = f->mNormal.Dot(inPoint - f->mCentroid); + if (dot > 0.0f) + { + float dist_sq = dot * dot / f->mNormal.LengthSq(); + if (dist_sq > outDistSq) + { + outFace = f; + outDistSq = dist_sq; + } + } + } +} + +float ConvexHullBuilder::GetDistanceToEdgeSq(Vec3Arg inPoint, const Face *inFace) const +{ + bool all_inside = true; + float edge_dist_sq = FLT_MAX; + + // Test if it is inside the edges of the polygon + Edge *edge = inFace->mFirstEdge; + Vec3 p1 = mPositions[edge->GetPreviousEdge()->mStartIdx]; + do + { + Vec3 p2 = mPositions[edge->mStartIdx]; + if ((p2 - p1).Cross(inPoint - p1).Dot(inFace->mNormal) < 0.0f) + { + // It is outside + all_inside = false; + + // Measure distance to this edge + uint32 s; + edge_dist_sq = min(edge_dist_sq, ClosestPoint::GetClosestPointOnLine(p1 - inPoint, p2 - inPoint, s).LengthSq()); + } + p1 = p2; + edge = edge->mNextEdge; + } while (edge != inFace->mFirstEdge); + + return all_inside? 0.0f : edge_dist_sq; +} + +bool ConvexHullBuilder::AssignPointToFace(int inPositionIdx, const Faces &inFaces, float inToleranceSq) +{ + Vec3 point = mPositions[inPositionIdx]; + + // Find the face for which the point is furthest away + Face *best_face; + float best_dist_sq; + GetFaceForPoint(point, inFaces, best_face, best_dist_sq); + + if (best_face != nullptr) + { + // Check if this point is within the tolerance margin to the plane + if (best_dist_sq <= inToleranceSq) + { + // Check distance to edges + float dist_to_edge_sq = GetDistanceToEdgeSq(point, best_face); + if (dist_to_edge_sq > inToleranceSq) + { + // Point is outside of the face and too far away to discard + mCoplanarList.push_back({ inPositionIdx, dist_to_edge_sq }); + } + } + else + { + // This point is in front of the face, add it to the conflict list + if (best_dist_sq > best_face->mFurthestPointDistanceSq) + { + // This point is further away than any others, update the distance and add point as last point + best_face->mFurthestPointDistanceSq = best_dist_sq; + best_face->mConflictList.push_back(inPositionIdx); + } + else + { + // Not the furthest point, add it as the before last point + best_face->mConflictList.push_back(best_face->mConflictList.back()); + best_face->mConflictList[best_face->mConflictList.size() - 2] = inPositionIdx; + } + + return true; + } + } + + return false; +} + +float ConvexHullBuilder::DetermineCoplanarDistance() const +{ + // Formula as per: Implementing Quickhull - Dirk Gregorius. + Vec3 vmax = Vec3::sZero(); + for (Vec3 v : mPositions) + vmax = Vec3::sMax(vmax, v.Abs()); + return 3.0f * FLT_EPSILON * (vmax.GetX() + vmax.GetY() + vmax.GetZ()); +} + +int ConvexHullBuilder::GetNumVerticesUsed() const +{ + UnorderedSet used_verts; + for (Face *f : mFaces) + { + Edge *e = f->mFirstEdge; + do + { + used_verts.insert(e->mStartIdx); + e = e->mNextEdge; + } while (e != f->mFirstEdge); + } + return (int)used_verts.size(); +} + +bool ConvexHullBuilder::ContainsFace(const Array &inIndices) const +{ + for (Face *f : mFaces) + { + Edge *e = f->mFirstEdge; + Array::const_iterator index = find(inIndices.begin(), inIndices.end(), e->mStartIdx); + if (index != inIndices.end()) + { + size_t matches = 0; + + do + { + // Check if index matches + if (*index != e->mStartIdx) + break; + + // Increment number of matches + matches++; + + // Next index in list of inIndices + index++; + if (index == inIndices.end()) + index = inIndices.begin(); + + // Next edge + e = e->mNextEdge; + } while (e != f->mFirstEdge); + + if (matches == inIndices.size()) + return true; + } + } + + return false; +} + +ConvexHullBuilder::EResult ConvexHullBuilder::Initialize(int inMaxVertices, float inTolerance, const char *&outError) +{ + // Free the faces possibly left over from an earlier hull + FreeFaces(); + + // Test that we have at least 3 points + if (mPositions.size() < 3) + { + outError = "Need at least 3 points to make a hull"; + return EResult::TooFewPoints; + } + + // Determine a suitable tolerance for detecting that points are coplanar + float coplanar_tolerance_sq = Square(DetermineCoplanarDistance()); + + // Increase desired tolerance if accuracy doesn't allow it + float tolerance_sq = max(coplanar_tolerance_sq, Square(inTolerance)); + + // Find point furthest from the origin + int idx1 = -1; + float max_dist_sq = -1.0f; + for (int i = 0; i < (int)mPositions.size(); ++i) + { + float dist_sq = mPositions[i].LengthSq(); + if (dist_sq > max_dist_sq) + { + max_dist_sq = dist_sq; + idx1 = i; + } + } + JPH_ASSERT(idx1 >= 0); + + // Find point that is furthest away from this point + int idx2 = -1; + max_dist_sq = -1.0f; + for (int i = 0; i < (int)mPositions.size(); ++i) + if (i != idx1) + { + float dist_sq = (mPositions[i] - mPositions[idx1]).LengthSq(); + if (dist_sq > max_dist_sq) + { + max_dist_sq = dist_sq; + idx2 = i; + } + } + JPH_ASSERT(idx2 >= 0); + + // Find point that forms the biggest triangle + int idx3 = -1; + float best_triangle_area_sq = -1.0f; + for (int i = 0; i < (int)mPositions.size(); ++i) + if (i != idx1 && i != idx2) + { + float triangle_area_sq = (mPositions[idx1] - mPositions[i]).Cross(mPositions[idx2] - mPositions[i]).LengthSq(); + if (triangle_area_sq > best_triangle_area_sq) + { + best_triangle_area_sq = triangle_area_sq; + idx3 = i; + } + } + JPH_ASSERT(idx3 >= 0); + if (best_triangle_area_sq < cMinTriangleAreaSq) + { + outError = "Could not find a suitable initial triangle because its area was too small"; + return EResult::Degenerate; + } + + // Check if we have only 3 vertices + if (mPositions.size() == 3) + { + // Create two triangles (back to back) + Face *t1 = CreateTriangle(idx1, idx2, idx3); + Face *t2 = CreateTriangle(idx1, idx3, idx2); + + // Link faces edges + sLinkFace(t1->mFirstEdge, t2->mFirstEdge->mNextEdge->mNextEdge); + sLinkFace(t1->mFirstEdge->mNextEdge, t2->mFirstEdge->mNextEdge); + sLinkFace(t1->mFirstEdge->mNextEdge->mNextEdge, t2->mFirstEdge); + +#ifdef JPH_CONVEX_BUILDER_DEBUG + // Draw current state + DrawState(); +#endif + + return EResult::Success; + } + + // Find point that forms the biggest tetrahedron + Vec3 initial_plane_normal = (mPositions[idx2] - mPositions[idx1]).Cross(mPositions[idx3] - mPositions[idx1]).Normalized(); + Vec3 initial_plane_centroid = (mPositions[idx1] + mPositions[idx2] + mPositions[idx3]) / 3.0f; + int idx4 = -1; + float max_dist = 0.0f; + for (int i = 0; i < (int)mPositions.size(); ++i) + if (i != idx1 && i != idx2 && i != idx3) + { + float dist = (mPositions[i] - initial_plane_centroid).Dot(initial_plane_normal); + if (abs(dist) > abs(max_dist)) + { + max_dist = dist; + idx4 = i; + } + } + + // Check if the hull is coplanar + if (Square(max_dist) <= 25.0f * coplanar_tolerance_sq) + { + // First project all points in 2D space + Vec3 base1 = initial_plane_normal.GetNormalizedPerpendicular(); + Vec3 base2 = initial_plane_normal.Cross(base1); + Array positions_2d; + positions_2d.reserve(mPositions.size()); + for (Vec3 v : mPositions) + positions_2d.emplace_back(base1.Dot(v), base2.Dot(v), 0.0f); + + // Build hull + Array edges_2d; + ConvexHullBuilder2D builder_2d(positions_2d); + ConvexHullBuilder2D::EResult result = builder_2d.Initialize(idx1, idx2, idx3, inMaxVertices, inTolerance, edges_2d); + + // Create faces (back to back) + Face *f1 = CreateFace(); + Face *f2 = CreateFace(); + + // Create edges for face 1 + Array edges_f1; + edges_f1.reserve(edges_2d.size()); + for (int start_idx : edges_2d) + { + Edge *edge = new Edge(f1, start_idx); + if (edges_f1.empty()) + f1->mFirstEdge = edge; + else + edges_f1.back()->mNextEdge = edge; + edges_f1.push_back(edge); + } + edges_f1.back()->mNextEdge = f1->mFirstEdge; + + // Create edges for face 2 + Array edges_f2; + edges_f2.reserve(edges_2d.size()); + for (int i = (int)edges_2d.size() - 1; i >= 0; --i) + { + Edge *edge = new Edge(f2, edges_2d[i]); + if (edges_f2.empty()) + f2->mFirstEdge = edge; + else + edges_f2.back()->mNextEdge = edge; + edges_f2.push_back(edge); + } + edges_f2.back()->mNextEdge = f2->mFirstEdge; + + // Link edges + for (size_t i = 0; i < edges_2d.size(); ++i) + sLinkFace(edges_f1[i], edges_f2[(2 * edges_2d.size() - 2 - i) % edges_2d.size()]); + + // Calculate the plane for both faces + f1->CalculateNormalAndCentroid(mPositions.data()); + f2->mNormal = -f1->mNormal; + f2->mCentroid = f1->mCentroid; + +#ifdef JPH_CONVEX_BUILDER_DEBUG + // Draw current state + DrawState(); +#endif + + return result == ConvexHullBuilder2D::EResult::MaxVerticesReached? EResult::MaxVerticesReached : EResult::Success; + } + + // Ensure the planes are facing outwards + if (max_dist < 0.0f) + swap(idx2, idx3); + + // Create tetrahedron + Face *t1 = CreateTriangle(idx1, idx2, idx4); + Face *t2 = CreateTriangle(idx2, idx3, idx4); + Face *t3 = CreateTriangle(idx3, idx1, idx4); + Face *t4 = CreateTriangle(idx1, idx3, idx2); + + // Link face edges + sLinkFace(t1->mFirstEdge, t4->mFirstEdge->mNextEdge->mNextEdge); + sLinkFace(t1->mFirstEdge->mNextEdge, t2->mFirstEdge->mNextEdge->mNextEdge); + sLinkFace(t1->mFirstEdge->mNextEdge->mNextEdge, t3->mFirstEdge->mNextEdge); + sLinkFace(t2->mFirstEdge, t4->mFirstEdge->mNextEdge); + sLinkFace(t2->mFirstEdge->mNextEdge, t3->mFirstEdge->mNextEdge->mNextEdge); + sLinkFace(t3->mFirstEdge, t4->mFirstEdge); + + // Build the initial conflict lists + Faces faces { t1, t2, t3, t4 }; + for (int idx = 0; idx < (int)mPositions.size(); ++idx) + if (idx != idx1 && idx != idx2 && idx != idx3 && idx != idx4) + AssignPointToFace(idx, faces, tolerance_sq); + +#ifdef JPH_CONVEX_BUILDER_DEBUG + // Draw current state including conflict list + DrawState(true); + + // Increment iteration counter + ++mIteration; +#endif + + // Overestimate of the actual amount of vertices we use, for limiting the amount of vertices in the hull + int num_vertices_used = 4; + + // Loop through the remainder of the points and add them + for (;;) + { + // Find the face with the furthest point on it + Face *face_with_furthest_point = nullptr; + float furthest_dist_sq = 0.0f; + for (Face *f : mFaces) + if (f->mFurthestPointDistanceSq > furthest_dist_sq) + { + furthest_dist_sq = f->mFurthestPointDistanceSq; + face_with_furthest_point = f; + } + + int furthest_point_idx; + if (face_with_furthest_point != nullptr) + { + // Take the furthest point + furthest_point_idx = face_with_furthest_point->mConflictList.back(); + face_with_furthest_point->mConflictList.pop_back(); + } + else if (!mCoplanarList.empty()) + { + // Try to assign points to faces (this also recalculates the distance to the hull for the coplanar vertices) + CoplanarList coplanar; + mCoplanarList.swap(coplanar); + bool added = false; + for (const Coplanar &c : coplanar) + added |= AssignPointToFace(c.mPositionIdx, mFaces, tolerance_sq); + + // If we were able to assign a point, loop again to pick it up + if (added) + continue; + + // If the coplanar list is empty, there are no points left and we're done + if (mCoplanarList.empty()) + break; + + do + { + // Find the vertex that is furthest from the hull + CoplanarList::size_type best_idx = 0; + float best_dist_sq = mCoplanarList.front().mDistanceSq; + for (CoplanarList::size_type idx = 1; idx < mCoplanarList.size(); ++idx) + { + const Coplanar &c = mCoplanarList[idx]; + if (c.mDistanceSq > best_dist_sq) + { + best_idx = idx; + best_dist_sq = c.mDistanceSq; + } + } + + // Swap it to the end + swap(mCoplanarList[best_idx], mCoplanarList.back()); + + // Remove it + furthest_point_idx = mCoplanarList.back().mPositionIdx; + mCoplanarList.pop_back(); + + // Find the face for which the point is furthest away + GetFaceForPoint(mPositions[furthest_point_idx], mFaces, face_with_furthest_point, best_dist_sq); + } while (!mCoplanarList.empty() && face_with_furthest_point == nullptr); + + if (face_with_furthest_point == nullptr) + break; + } + else + { + // If there are no more vertices, we're done + break; + } + + // Check if we have a limit on the max vertices that we should produce + if (num_vertices_used >= inMaxVertices) + { + // Count the actual amount of used vertices (we did not take the removal of any vertices into account) + num_vertices_used = GetNumVerticesUsed(); + + // Check if there are too many + if (num_vertices_used >= inMaxVertices) + return EResult::MaxVerticesReached; + } + + // We're about to add another vertex + ++num_vertices_used; + + // Add the point to the hull + Faces new_faces; + AddPoint(face_with_furthest_point, furthest_point_idx, coplanar_tolerance_sq, new_faces); + + // Redistribute points on conflict lists belonging to removed faces + for (const Face *face : mFaces) + if (face->mRemoved) + for (int idx : face->mConflictList) + AssignPointToFace(idx, new_faces, tolerance_sq); + + // Permanently delete faces that we removed in AddPoint() + GarbageCollectFaces(); + +#ifdef JPH_CONVEX_BUILDER_DEBUG + // Draw state at the end of this step including conflict list + DrawState(true); + + // Increment iteration counter + ++mIteration; +#endif + } + + // Check if we are left with a hull. It is possible that hull building fails if the points are nearly coplanar. + if (mFaces.size() < 2) + { + outError = "Too few faces in hull"; + return EResult::TooFewFaces; + } + + return EResult::Success; +} + +void ConvexHullBuilder::AddPoint(Face *inFacingFace, int inIdx, float inCoplanarToleranceSq, Faces &outNewFaces) +{ + // Get position + Vec3 pos = mPositions[inIdx]; + +#ifdef JPH_CONVEX_BUILDER_DEBUG + // Draw point to be added + DebugRenderer::sInstance->DrawMarker(cDrawScale * (mOffset + pos), Color::sYellow, 0.1f); + DebugRenderer::sInstance->DrawText3D(cDrawScale * (mOffset + pos), ConvertToString(inIdx), Color::sWhite); +#endif + +#ifdef JPH_ENABLE_ASSERTS + // Check if structure is intact + ValidateFaces(); +#endif + + // Find edge of convex hull of faces that are not facing the new vertex + FullEdges edges; + FindEdge(inFacingFace, pos, edges); + JPH_ASSERT(edges.size() >= 3); + + // Create new faces + outNewFaces.reserve(edges.size()); + for (const FullEdge &e : edges) + { + JPH_ASSERT(e.mStartIdx != e.mEndIdx); + Face *f = CreateTriangle(e.mStartIdx, e.mEndIdx, inIdx); + outNewFaces.push_back(f); + } + + // Link edges + for (Faces::size_type i = 0; i < outNewFaces.size(); ++i) + { + sLinkFace(outNewFaces[i]->mFirstEdge, edges[i].mNeighbourEdge); + sLinkFace(outNewFaces[i]->mFirstEdge->mNextEdge, outNewFaces[(i + 1) % outNewFaces.size()]->mFirstEdge->mNextEdge->mNextEdge); + } + + // Loop on faces that were modified until nothing needs to be checked anymore + Faces affected_faces = outNewFaces; + while (!affected_faces.empty()) + { + // Take the next face + Face *face = affected_faces.back(); + affected_faces.pop_back(); + + if (!face->mRemoved) + { + // Merge with neighbour if this is a degenerate face + MergeDegenerateFace(face, affected_faces); + + // Merge with coplanar neighbours (or when the neighbour forms a concave edge) + if (!face->mRemoved) + MergeCoplanarOrConcaveFaces(face, inCoplanarToleranceSq, affected_faces); + } + } + +#ifdef JPH_ENABLE_ASSERTS + // Check if structure is intact + ValidateFaces(); +#endif +} + +void ConvexHullBuilder::GarbageCollectFaces() +{ + for (int i = (int)mFaces.size() - 1; i >= 0; --i) + { + Face *f = mFaces[i]; + if (f->mRemoved) + { + FreeFace(f); + mFaces.erase(mFaces.begin() + i); + } + } +} + +ConvexHullBuilder::Face *ConvexHullBuilder::CreateFace() +{ + // Call provider to create face + Face *f = new Face(); + +#ifdef JPH_CONVEX_BUILDER_DEBUG + // Remember iteration counter + f->mIteration = mIteration; +#endif + + // Add to list + mFaces.push_back(f); + return f; +} + +ConvexHullBuilder::Face *ConvexHullBuilder::CreateTriangle(int inIdx1, int inIdx2, int inIdx3) +{ + Face *f = CreateFace(); + f->Initialize(inIdx1, inIdx2, inIdx3, mPositions.data()); + return f; +} + +void ConvexHullBuilder::FreeFace(Face *inFace) +{ + JPH_ASSERT(inFace->mRemoved); + +#ifdef JPH_ENABLE_ASSERTS + // Make sure that this face is not connected + Edge *e = inFace->mFirstEdge; + if (e != nullptr) + do + { + JPH_ASSERT(e->mNeighbourEdge == nullptr); + e = e->mNextEdge; + } while (e != inFace->mFirstEdge); +#endif + + // Free the face + delete inFace; +} + +void ConvexHullBuilder::sLinkFace(Edge *inEdge1, Edge *inEdge2) +{ + // Check not connected yet + JPH_ASSERT(inEdge1->mNeighbourEdge == nullptr); + JPH_ASSERT(inEdge2->mNeighbourEdge == nullptr); + JPH_ASSERT(inEdge1->mFace != inEdge2->mFace); + + // Check vertices match + JPH_ASSERT(inEdge1->mStartIdx == inEdge2->mNextEdge->mStartIdx); + JPH_ASSERT(inEdge2->mStartIdx == inEdge1->mNextEdge->mStartIdx); + + // Link up + inEdge1->mNeighbourEdge = inEdge2; + inEdge2->mNeighbourEdge = inEdge1; +} + +void ConvexHullBuilder::sUnlinkFace(Face *inFace) +{ + // Unlink from neighbours + Edge *e = inFace->mFirstEdge; + do + { + if (e->mNeighbourEdge != nullptr) + { + // Validate that neighbour points to us + JPH_ASSERT(e->mNeighbourEdge->mNeighbourEdge == e); + + // Unlink + e->mNeighbourEdge->mNeighbourEdge = nullptr; + e->mNeighbourEdge = nullptr; + } + e = e->mNextEdge; + } while (e != inFace->mFirstEdge); +} + +void ConvexHullBuilder::FindEdge(Face *inFacingFace, Vec3Arg inVertex, FullEdges &outEdges) const +{ + // Assert that we were given an empty array + JPH_ASSERT(outEdges.empty()); + + // Should start with a facing face + JPH_ASSERT(inFacingFace->IsFacing(inVertex)); + + // Flag as removed + inFacingFace->mRemoved = true; + + // Instead of recursing, we build our own stack with the information we need + struct StackEntry + { + Edge * mFirstEdge; + Edge * mCurrentEdge; + }; + constexpr int cMaxEdgeLength = 128; + StackEntry stack[cMaxEdgeLength]; + int cur_stack_pos = 0; + + static_assert(alignof(Edge) >= 2, "Need lowest bit to indicate to tell if we completed the loop"); + + // Start with the face / edge provided + stack[0].mFirstEdge = inFacingFace->mFirstEdge; + stack[0].mCurrentEdge = reinterpret_cast(reinterpret_cast(inFacingFace->mFirstEdge) | 1); // Set lowest bit of pointer to make it different from the first edge + + for (;;) + { + StackEntry &cur_entry = stack[cur_stack_pos]; + + // Next edge + Edge *raw_e = cur_entry.mCurrentEdge; + Edge *e = reinterpret_cast(reinterpret_cast(raw_e) & ~uintptr_t(1)); // Remove the lowest bit which was used to indicate that this is the first edge we're testing + cur_entry.mCurrentEdge = e->mNextEdge; + + // If we're back at the first edge we've completed the face and we're done + if (raw_e == cur_entry.mFirstEdge) + { + // This face needs to be removed, unlink it now, caller will free + sUnlinkFace(e->mFace); + + // Pop from stack + if (--cur_stack_pos < 0) + break; + } + else + { + // Visit neighbour face + Edge *ne = e->mNeighbourEdge; + if (ne != nullptr) + { + Face *n = ne->mFace; + if (!n->mRemoved) + { + // Check if vertex is on the front side of this face + if (n->IsFacing(inVertex)) + { + // Vertex on front, this face needs to be removed + n->mRemoved = true; + + // Add element to the stack of elements to visit + cur_stack_pos++; + JPH_ASSERT(cur_stack_pos < cMaxEdgeLength); + StackEntry &new_entry = stack[cur_stack_pos]; + new_entry.mFirstEdge = ne; + new_entry.mCurrentEdge = ne->mNextEdge; // We don't need to test this edge again since we came from it + } + else + { + // Vertex behind, keep edge + FullEdge full; + full.mNeighbourEdge = ne; + full.mStartIdx = e->mStartIdx; + full.mEndIdx = ne->mStartIdx; + outEdges.push_back(full); + } + } + } + } + } + + // Assert that we have a fully connected loop +#ifdef JPH_ENABLE_ASSERTS + for (int i = 0; i < (int)outEdges.size(); ++i) + JPH_ASSERT(outEdges[i].mEndIdx == outEdges[(i + 1) % outEdges.size()].mStartIdx); +#endif + +#ifdef JPH_CONVEX_BUILDER_DEBUG + // Draw edge of facing faces + for (int i = 0; i < (int)outEdges.size(); ++i) + DebugRenderer::sInstance->DrawArrow(cDrawScale * (mOffset + mPositions[outEdges[i].mStartIdx]), cDrawScale * (mOffset + mPositions[outEdges[i].mEndIdx]), Color::sWhite, 0.01f); + DrawState(); +#endif +} + +void ConvexHullBuilder::MergeFaces(Edge *inEdge) +{ + // Get the face + Face *face = inEdge->mFace; + + // Find the previous and next edge + Edge *next_edge = inEdge->mNextEdge; + Edge *prev_edge = inEdge->GetPreviousEdge(); + + // Get the other face + Edge *other_edge = inEdge->mNeighbourEdge; + Face *other_face = other_edge->mFace; + + // Check if attempting to merge with self + JPH_ASSERT(face != other_face); + +#ifdef JPH_CONVEX_BUILDER_DEBUG + DrawWireFace(face, Color::sGreen); + DrawWireFace(other_face, Color::sRed); + DrawState(); +#endif + + // Loop over the edges of the other face and make them belong to inFace + Edge *edge = other_edge->mNextEdge; + prev_edge->mNextEdge = edge; + for (;;) + { + edge->mFace = face; + if (edge->mNextEdge == other_edge) + { + // Terminate when we are back at other_edge + edge->mNextEdge = next_edge; + break; + } + edge = edge->mNextEdge; + } + + // If the first edge happens to be inEdge we need to fix it because this edge is no longer part of the face. + // Note that we replace it with the first edge of the merged face so that if the MergeFace function is called + // from a loop that loops around the face that it will still terminate after visiting all edges once. + if (face->mFirstEdge == inEdge) + face->mFirstEdge = prev_edge->mNextEdge; + + // Free the edges + delete inEdge; + delete other_edge; + + // Mark the other face as removed + other_face->mFirstEdge = nullptr; + other_face->mRemoved = true; + + // Recalculate plane + face->CalculateNormalAndCentroid(mPositions.data()); + + // Merge conflict lists + if (face->mFurthestPointDistanceSq > other_face->mFurthestPointDistanceSq) + { + // This face has a point that's further away, make sure it remains the last one as we add the other points to this faces list + face->mConflictList.insert(face->mConflictList.end() - 1, other_face->mConflictList.begin(), other_face->mConflictList.end()); + } + else + { + // The other face has a point that's furthest away, add that list at the end. + face->mConflictList.insert(face->mConflictList.end(), other_face->mConflictList.begin(), other_face->mConflictList.end()); + face->mFurthestPointDistanceSq = other_face->mFurthestPointDistanceSq; + } + other_face->mConflictList.clear(); + +#ifdef JPH_CONVEX_BUILDER_DEBUG + DrawWireFace(face, Color::sWhite); + DrawState(); +#endif +} + +void ConvexHullBuilder::MergeDegenerateFace(Face *inFace, Faces &ioAffectedFaces) +{ + // Check area of face + if (inFace->mNormal.LengthSq() < cMinTriangleAreaSq) + { + // Find longest edge, since this face is a sliver this should keep the face convex + float max_length_sq = 0.0f; + Edge *longest_edge = nullptr; + Edge *e = inFace->mFirstEdge; + Vec3 p1 = mPositions[e->mStartIdx]; + do + { + Edge *next = e->mNextEdge; + Vec3 p2 = mPositions[next->mStartIdx]; + float length_sq = (p2 - p1).LengthSq(); + if (length_sq >= max_length_sq) + { + max_length_sq = length_sq; + longest_edge = e; + } + p1 = p2; + e = next; + } while (e != inFace->mFirstEdge); + + // Merge with face on longest edge + MergeFaces(longest_edge); + + // Remove any invalid edges + RemoveInvalidEdges(inFace, ioAffectedFaces); + } +} + +void ConvexHullBuilder::MergeCoplanarOrConcaveFaces(Face *inFace, float inCoplanarToleranceSq, Faces &ioAffectedFaces) +{ + bool merged = false; + + Edge *edge = inFace->mFirstEdge; + do + { + // Store next edge since this edge can be removed + Edge *next_edge = edge->mNextEdge; + + // Test if centroid of one face is above plane of the other face by inCoplanarToleranceSq. + // If so we need to merge other face into inFace. + const Face *other_face = edge->mNeighbourEdge->mFace; + Vec3 delta_centroid = other_face->mCentroid - inFace->mCentroid; + float dist_other_face_centroid = inFace->mNormal.Dot(delta_centroid); + float signed_dist_other_face_centroid_sq = abs(dist_other_face_centroid) * dist_other_face_centroid; + float dist_face_centroid = -other_face->mNormal.Dot(delta_centroid); + float signed_dist_face_centroid_sq = abs(dist_face_centroid) * dist_face_centroid; + float face_normal_len_sq = inFace->mNormal.LengthSq(); + float other_face_normal_len_sq = other_face->mNormal.LengthSq(); + if ((signed_dist_other_face_centroid_sq > -inCoplanarToleranceSq * face_normal_len_sq + || signed_dist_face_centroid_sq > -inCoplanarToleranceSq * other_face_normal_len_sq) + && inFace->mNormal.Dot(other_face->mNormal) > 0.0f) // Never merge faces that are back to back + { + MergeFaces(edge); + merged = true; + } + + edge = next_edge; + } while (edge != inFace->mFirstEdge); + + if (merged) + RemoveInvalidEdges(inFace, ioAffectedFaces); +} + +void ConvexHullBuilder::sMarkAffected(Face *inFace, Faces &ioAffectedFaces) +{ + if (find(ioAffectedFaces.begin(), ioAffectedFaces.end(), inFace) == ioAffectedFaces.end()) + ioAffectedFaces.push_back(inFace); +} + +void ConvexHullBuilder::RemoveInvalidEdges(Face *inFace, Faces &ioAffectedFaces) +{ + // This marks that the plane needs to be recalculated (we delay this until the end of the + // function since we don't use the plane and we want to avoid calculating it multiple times) + bool recalculate_plane = false; + + // We keep going through this loop until no more edges were removed + bool removed; + do + { + removed = false; + + // Loop over all edges in this face + Edge *edge = inFace->mFirstEdge; + Face *neighbour_face = edge->mNeighbourEdge->mFace; + do + { + Edge *next_edge = edge->mNextEdge; + Face *next_neighbour_face = next_edge->mNeighbourEdge->mFace; + + if (neighbour_face == inFace) + { + // We only remove 1 edge at a time, check if this edge's next edge is our neighbour. + // If this check fails, we will continue to scan along the edge until we find an edge where this is the case. + if (edge->mNeighbourEdge == next_edge) + { + // This edge leads back to the starting point, this means the edge is interior and needs to be removed +#ifdef JPH_CONVEX_BUILDER_DEBUG + DrawWireFace(inFace, Color::sBlue); + DrawState(); +#endif + + // Remove edge + Edge *prev_edge = edge->GetPreviousEdge(); + prev_edge->mNextEdge = next_edge->mNextEdge; + if (inFace->mFirstEdge == edge || inFace->mFirstEdge == next_edge) + inFace->mFirstEdge = prev_edge; + delete edge; + delete next_edge; + +#ifdef JPH_CONVEX_BUILDER_DEBUG + DrawWireFace(inFace, Color::sGreen); + DrawState(); +#endif + + // Check if inFace now has only 2 edges left + if (RemoveTwoEdgeFace(inFace, ioAffectedFaces)) + return; // Bail if face no longer exists + + // Restart the loop + recalculate_plane = true; + removed = true; + break; + } + } + else if (neighbour_face == next_neighbour_face) + { + // There are two edges that connect to the same face, we will remove the second one +#ifdef JPH_CONVEX_BUILDER_DEBUG + DrawWireFace(inFace, Color::sYellow); + DrawWireFace(neighbour_face, Color::sRed); + DrawState(); +#endif + + // First merge the neighbours edges + Edge *neighbour_edge = next_edge->mNeighbourEdge; + Edge *next_neighbour_edge = neighbour_edge->mNextEdge; + if (neighbour_face->mFirstEdge == next_neighbour_edge) + neighbour_face->mFirstEdge = neighbour_edge; + neighbour_edge->mNextEdge = next_neighbour_edge->mNextEdge; + neighbour_edge->mNeighbourEdge = edge; + delete next_neighbour_edge; + + // Then merge my own edges + if (inFace->mFirstEdge == next_edge) + inFace->mFirstEdge = edge; + edge->mNextEdge = next_edge->mNextEdge; + edge->mNeighbourEdge = neighbour_edge; + delete next_edge; + +#ifdef JPH_CONVEX_BUILDER_DEBUG + DrawWireFace(inFace, Color::sYellow); + DrawWireFace(neighbour_face, Color::sGreen); + DrawState(); +#endif + + // Check if neighbour has only 2 edges left + if (!RemoveTwoEdgeFace(neighbour_face, ioAffectedFaces)) + { + // No, we need to recalculate its plane + neighbour_face->CalculateNormalAndCentroid(mPositions.data()); + + // Mark neighbour face as affected + sMarkAffected(neighbour_face, ioAffectedFaces); + } + + // Check if inFace now has only 2 edges left + if (RemoveTwoEdgeFace(inFace, ioAffectedFaces)) + return; // Bail if face no longer exists + + // Restart loop + recalculate_plane = true; + removed = true; + break; + } + + // This edge is ok, go to the next edge + edge = next_edge; + neighbour_face = next_neighbour_face; + + } while (edge != inFace->mFirstEdge); + } while (removed); + + // Recalculate plane? + if (recalculate_plane) + inFace->CalculateNormalAndCentroid(mPositions.data()); +} + +bool ConvexHullBuilder::RemoveTwoEdgeFace(Face *inFace, Faces &ioAffectedFaces) const +{ + // Check if this face contains only 2 edges + Edge *edge = inFace->mFirstEdge; + Edge *next_edge = edge->mNextEdge; + JPH_ASSERT(edge != next_edge); // 1 edge faces should not exist + if (next_edge->mNextEdge == edge) + { +#ifdef JPH_CONVEX_BUILDER_DEBUG + DrawWireFace(inFace, Color::sRed); + DrawState(); +#endif + + // Schedule both neighbours for re-checking + Edge *neighbour_edge = edge->mNeighbourEdge; + Face *neighbour_face = neighbour_edge->mFace; + Edge *next_neighbour_edge = next_edge->mNeighbourEdge; + Face *next_neighbour_face = next_neighbour_edge->mFace; + sMarkAffected(neighbour_face, ioAffectedFaces); + sMarkAffected(next_neighbour_face, ioAffectedFaces); + + // Link my neighbours to each other + neighbour_edge->mNeighbourEdge = next_neighbour_edge; + next_neighbour_edge->mNeighbourEdge = neighbour_edge; + + // Unlink my edges + edge->mNeighbourEdge = nullptr; + next_edge->mNeighbourEdge = nullptr; + + // Mark this face as removed + inFace->mRemoved = true; + + return true; + } + + return false; +} + +#ifdef JPH_ENABLE_ASSERTS + +void ConvexHullBuilder::DumpFace(const Face *inFace) const +{ + Trace("f:0x%p", inFace); + + const Edge *e = inFace->mFirstEdge; + do + { + Trace("\te:0x%p { i:%d e:0x%p f:0x%p }", e, e->mStartIdx, e->mNeighbourEdge, e->mNeighbourEdge->mFace); + e = e->mNextEdge; + } while (e != inFace->mFirstEdge); +} + +void ConvexHullBuilder::DumpFaces() const +{ + Trace("Dump Faces:"); + + for (const Face *f : mFaces) + if (!f->mRemoved) + DumpFace(f); +} + +void ConvexHullBuilder::ValidateFace(const Face *inFace) const +{ + if (inFace->mRemoved) + { + const Edge *e = inFace->mFirstEdge; + if (e != nullptr) + do + { + JPH_ASSERT(e->mNeighbourEdge == nullptr); + e = e->mNextEdge; + } while (e != inFace->mFirstEdge); + } + else + { + int edge_count = 0; + + const Edge *e = inFace->mFirstEdge; + do + { + // Count edge + ++edge_count; + + // Validate that adjacent faces are all different + if (mFaces.size() > 2) + for (const Edge *other_edge = e->mNextEdge; other_edge != inFace->mFirstEdge; other_edge = other_edge->mNextEdge) + JPH_ASSERT(e->mNeighbourEdge->mFace != other_edge->mNeighbourEdge->mFace); + + // Assert that the face is correct + JPH_ASSERT(e->mFace == inFace); + + // Assert that we have a neighbour + const Edge *nb_edge = e->mNeighbourEdge; + JPH_ASSERT(nb_edge != nullptr); + if (nb_edge != nullptr) + { + // Assert that our neighbours edge points to us + JPH_ASSERT(nb_edge->mNeighbourEdge == e); + + // Assert that it belongs to a different face + JPH_ASSERT(nb_edge->mFace != inFace); + + // Assert that the next edge of the neighbour points to the same vertex as this edge's vertex + JPH_ASSERT(nb_edge->mNextEdge->mStartIdx == e->mStartIdx); + + // Assert that my next edge points to the same vertex as my neighbours vertex + JPH_ASSERT(e->mNextEdge->mStartIdx == nb_edge->mStartIdx); + } + e = e->mNextEdge; + } while (e != inFace->mFirstEdge); + + // Assert that we have 3 or more edges + JPH_ASSERT(edge_count >= 3); + } +} + +void ConvexHullBuilder::ValidateFaces() const +{ + for (const Face *f : mFaces) + ValidateFace(f); +} + +#endif // JPH_ENABLE_ASSERTS + +void ConvexHullBuilder::GetCenterOfMassAndVolume(Vec3 &outCenterOfMass, float &outVolume) const +{ + // Fourth point is the average of all face centroids + Vec3 v4 = Vec3::sZero(); + for (const Face *f : mFaces) + v4 += f->mCentroid; + v4 /= float(mFaces.size()); + + // Calculate mass and center of mass of this convex hull by summing all tetrahedrons + outVolume = 0.0f; + outCenterOfMass = Vec3::sZero(); + for (const Face *f : mFaces) + { + // Get the first vertex that we'll use to create a triangle fan + Edge *e = f->mFirstEdge; + Vec3 v1 = mPositions[e->mStartIdx]; + + // Get the second vertex + e = e->mNextEdge; + Vec3 v2 = mPositions[e->mStartIdx]; + + for (e = e->mNextEdge; e != f->mFirstEdge; e = e->mNextEdge) + { + // Fetch the last point of the triangle + Vec3 v3 = mPositions[e->mStartIdx]; + + // Calculate center of mass and mass of this tetrahedron, + // see: https://en.wikipedia.org/wiki/Tetrahedron#Volume + float volume_tetrahedron = (v1 - v4).Dot((v2 - v4).Cross(v3 - v4)); // Needs to be divided by 6, postpone this until the end of the loop + Vec3 center_of_mass_tetrahedron = v1 + v2 + v3 + v4; // Needs to be divided by 4, postpone this until the end of the loop + + // Accumulate results + outVolume += volume_tetrahedron; + outCenterOfMass += volume_tetrahedron * center_of_mass_tetrahedron; + + // Update v2 for next triangle + v2 = v3; + } + } + + // Calculate center of mass, fall back to average point in case there is no volume (everything is on a plane in this case) + if (outVolume > FLT_EPSILON) + outCenterOfMass /= 4.0f * outVolume; + else + outCenterOfMass = v4; + + outVolume /= 6.0f; +} + +void ConvexHullBuilder::DetermineMaxError(Face *&outFaceWithMaxError, float &outMaxError, int &outMaxErrorPositionIdx, float &outCoplanarDistance) const +{ + outCoplanarDistance = DetermineCoplanarDistance(); + + // This measures the distance from a polygon to the furthest point outside of the hull + float max_error = 0.0f; + Face *max_error_face = nullptr; + int max_error_point = -1; + + for (int i = 0; i < (int)mPositions.size(); ++i) + { + Vec3 v = mPositions[i]; + + // This measures the closest edge from all faces to point v + // Note that we take the min of all faces since there may be multiple near coplanar faces so if we were to test this per face + // we may find that a point is outside of a polygon and mark it as an error, while it is actually inside a nearly coplanar + // polygon. + float min_edge_dist_sq = FLT_MAX; + Face *min_edge_dist_face = nullptr; + + for (Face *f : mFaces) + { + // Check if point is on or in front of plane + float normal_len = f->mNormal.Length(); + JPH_ASSERT(normal_len > 0.0f); + float plane_dist = f->mNormal.Dot(v - f->mCentroid) / normal_len; + if (plane_dist > -outCoplanarDistance) + { + // Check distance to the edges of this face + float edge_dist_sq = GetDistanceToEdgeSq(v, f); + if (edge_dist_sq < min_edge_dist_sq) + { + min_edge_dist_sq = edge_dist_sq; + min_edge_dist_face = f; + } + + // If the point is inside the polygon and the point is in front of the plane, measure the distance + if (edge_dist_sq == 0.0f && plane_dist > max_error) + { + max_error = plane_dist; + max_error_face = f; + max_error_point = i; + } + } + } + + // If the minimum distance to an edge is further than our current max error, we use that as max error + float min_edge_dist = sqrt(min_edge_dist_sq); + if (min_edge_dist_face != nullptr && min_edge_dist > max_error) + { + max_error = min_edge_dist; + max_error_face = min_edge_dist_face; + max_error_point = i; + } + } + + outFaceWithMaxError = max_error_face; + outMaxError = max_error; + outMaxErrorPositionIdx = max_error_point; +} + +#ifdef JPH_CONVEX_BUILDER_DEBUG + +void ConvexHullBuilder::DrawState(bool inDrawConflictList) const +{ + // Draw origin + DebugRenderer::sInstance->DrawMarker(cDrawScale * mOffset, Color::sRed, 0.2f); + + int face_idx = 0; + + // Draw faces + for (const Face *f : mFaces) + if (!f->mRemoved) + { + Color iteration_color = Color::sGetDistinctColor(f->mIteration); + Color face_color = Color::sGetDistinctColor(face_idx++); + + // First point + const Edge *e = f->mFirstEdge; + RVec3 p1 = cDrawScale * (mOffset + mPositions[e->mStartIdx]); + + // Second point + e = e->mNextEdge; + RVec3 p2 = cDrawScale * (mOffset + mPositions[e->mStartIdx]); + + // First line + DebugRenderer::sInstance->DrawLine(p1, p2, Color::sGrey); + + do + { + // Third point + e = e->mNextEdge; + RVec3 p3 = cDrawScale * (mOffset + mPositions[e->mStartIdx]); + + DebugRenderer::sInstance->DrawTriangle(p1, p2, p3, iteration_color); + + DebugRenderer::sInstance->DrawLine(p2, p3, Color::sGrey); + + p2 = p3; + } + while (e != f->mFirstEdge); + + // Draw normal + RVec3 centroid = cDrawScale * (mOffset + f->mCentroid); + DebugRenderer::sInstance->DrawArrow(centroid, centroid + f->mNormal.NormalizedOr(Vec3::sZero()), face_color, 0.01f); + + // Draw conflict list + if (inDrawConflictList) + for (int idx : f->mConflictList) + DebugRenderer::sInstance->DrawMarker(cDrawScale * (mOffset + mPositions[idx]), face_color, 0.05f); + } + + // Offset to the right + mOffset += mDelta; +} + +void ConvexHullBuilder::DrawWireFace(const Face *inFace, ColorArg inColor) const +{ + const Edge *e = inFace->mFirstEdge; + RVec3 prev = cDrawScale * (mOffset + mPositions[e->mStartIdx]); + do + { + const Edge *next = e->mNextEdge; + RVec3 cur = cDrawScale * (mOffset + mPositions[next->mStartIdx]); + DebugRenderer::sInstance->DrawArrow(prev, cur, inColor, 0.01f); + DebugRenderer::sInstance->DrawText3D(prev, ConvertToString(e->mStartIdx), inColor); + e = next; + prev = cur; + } while (e != inFace->mFirstEdge); +} + +void ConvexHullBuilder::DrawEdge(const Edge *inEdge, ColorArg inColor) const +{ + RVec3 p1 = cDrawScale * (mOffset + mPositions[inEdge->mStartIdx]); + RVec3 p2 = cDrawScale * (mOffset + mPositions[inEdge->mNextEdge->mStartIdx]); + DebugRenderer::sInstance->DrawArrow(p1, p2, inColor, 0.01f); +} + +#endif // JPH_CONVEX_BUILDER_DEBUG + +#ifdef JPH_CONVEX_BUILDER_DUMP_SHAPE + +void ConvexHullBuilder::DumpShape() const +{ + static atomic sShapeNo = 1; + int shape_no = sShapeNo++; + + std::ofstream f; + f.open(StringFormat("dumped_shape%d.cpp", shape_no).c_str(), std::ofstream::out | std::ofstream::trunc); + if (!f.is_open()) + return; + + f << "{\n"; + for (Vec3 v : mPositions) + f << StringFormat("\tVec3(%.9gf, %.9gf, %.9gf),\n", (double)v.GetX(), (double)v.GetY(), (double)v.GetZ()); + f << "},\n"; +} + +#endif // JPH_CONVEX_BUILDER_DUMP_SHAPE + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/ConvexHullBuilder.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/ConvexHullBuilder.h new file mode 100644 index 00000000000..7a6aea8caa5 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/ConvexHullBuilder.h @@ -0,0 +1,276 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +//#define JPH_CONVEX_BUILDER_DEBUG +//#define JPH_CONVEX_BUILDER_DUMP_SHAPE + +#ifdef JPH_CONVEX_BUILDER_DEBUG + #include +#endif + +#include +#include + +JPH_NAMESPACE_BEGIN + +/// A convex hull builder that tries to create hulls as accurately as possible. Used for offline processing. +class JPH_EXPORT ConvexHullBuilder : public NonCopyable +{ +public: + // Forward declare + class Face; + + /// Class that holds the information of an edge + class Edge : public NonCopyable + { + public: + JPH_OVERRIDE_NEW_DELETE + + /// Constructor + Edge(Face *inFace, int inStartIdx) : mFace(inFace), mStartIdx(inStartIdx) { } + + /// Get the previous edge + inline Edge * GetPreviousEdge() + { + Edge *prev_edge = this; + while (prev_edge->mNextEdge != this) + prev_edge = prev_edge->mNextEdge; + return prev_edge; + } + + Face * mFace; ///< Face that this edge belongs to + Edge * mNextEdge = nullptr; ///< Next edge of this face + Edge * mNeighbourEdge = nullptr; ///< Edge that this edge is connected to + int mStartIdx; ///< Vertex index in mPositions that indicates the start vertex of this edge + }; + + using ConflictList = Array; + + /// Class that holds the information of one face + class Face : public NonCopyable + { + public: + JPH_OVERRIDE_NEW_DELETE + + /// Destructor + ~Face(); + + /// Initialize a face with three indices + void Initialize(int inIdx0, int inIdx1, int inIdx2, const Vec3 *inPositions); + + /// Calculates the centroid and normal for this face + void CalculateNormalAndCentroid(const Vec3 *inPositions); + + /// Check if face inFace is facing inPosition + inline bool IsFacing(Vec3Arg inPosition) const + { + JPH_ASSERT(!mRemoved); + return mNormal.Dot(inPosition - mCentroid) > 0.0f; + } + + Vec3 mNormal; ///< Normal of this face, length is 2 times area of face + Vec3 mCentroid; ///< Center of the face + ConflictList mConflictList; ///< Positions associated with this edge (that are closest to this edge). The last position in the list is the point that is furthest away from the face. + Edge * mFirstEdge = nullptr; ///< First edge of this face + float mFurthestPointDistanceSq = 0.0f; ///< Squared distance of furthest point from the conflict list to the face + bool mRemoved = false; ///< Flag that indicates that face has been removed (face will be freed later) +#ifdef JPH_CONVEX_BUILDER_DEBUG + int mIteration; ///< Iteration that this face was created +#endif + }; + + // Typedefs + using Positions = Array; + using Faces = Array; + + /// Constructor + explicit ConvexHullBuilder(const Positions &inPositions); + + /// Destructor + ~ConvexHullBuilder() { FreeFaces(); } + + /// Result enum that indicates how the hull got created + enum class EResult + { + Success, ///< Hull building finished successfully + MaxVerticesReached, ///< Hull building finished successfully, but the desired accuracy was not reached because the max vertices limit was reached + TooFewPoints, ///< Too few points to create a hull + TooFewFaces, ///< Too few faces in the created hull (signifies precision errors during building) + Degenerate, ///< Degenerate hull detected + }; + + /// Takes all positions as provided by the constructor and use them to build a hull + /// Any points that are closer to the hull than inTolerance will be discarded + /// @param inMaxVertices Max vertices to allow in the hull. Specify INT_MAX if there is no limit. + /// @param inTolerance Max distance that a point is allowed to be outside of the hull + /// @param outError Error message when building fails + /// @return Status code that reports if the hull was created or not + EResult Initialize(int inMaxVertices, float inTolerance, const char *&outError); + + /// Returns the amount of vertices that are currently used by the hull + int GetNumVerticesUsed() const; + + /// Returns true if the hull contains a polygon with inIndices (counter clockwise indices in mPositions) + bool ContainsFace(const Array &inIndices) const; + + /// Calculate the center of mass and the volume of the current convex hull + void GetCenterOfMassAndVolume(Vec3 &outCenterOfMass, float &outVolume) const; + + /// Determines the point that is furthest outside of the hull and reports how far it is outside of the hull (which indicates a failure during hull building) + /// @param outFaceWithMaxError The face that caused the error + /// @param outMaxError The maximum distance of a point to the hull + /// @param outMaxErrorPositionIdx The index of the point that had this distance + /// @param outCoplanarDistance Points that are less than this distance from the hull are considered on the hull. This should be used as a lowerbound for the allowed error. + void DetermineMaxError(Face *&outFaceWithMaxError, float &outMaxError, int &outMaxErrorPositionIdx, float &outCoplanarDistance) const; + + /// Access to the created faces. Memory is owned by the convex hull builder. + const Faces & GetFaces() const { return mFaces; } + +private: + /// Minimal square area of a triangle (used for merging and checking if a triangle is degenerate) + static constexpr float cMinTriangleAreaSq = 1.0e-12f; + +#ifdef JPH_CONVEX_BUILDER_DEBUG + /// Factor to scale convex hull when debug drawing the construction process + static constexpr Real cDrawScale = 10; +#endif + + /// Class that holds an edge including start and end index + class FullEdge + { + public: + Edge * mNeighbourEdge; ///< Edge that this edge is connected to + int mStartIdx; ///< Vertex index in mPositions that indicates the start vertex of this edge + int mEndIdx; ///< Vertex index in mPosition that indicates the end vertex of this edge + }; + + // Private typedefs + using FullEdges = Array; + + // Determine a suitable tolerance for detecting that points are coplanar + float DetermineCoplanarDistance() const; + + /// Find the face for which inPoint is furthest to the front + /// @param inPoint Point to test + /// @param inFaces List of faces to test + /// @param outFace Returns the best face + /// @param outDistSq Returns the squared distance how much inPoint is in front of the plane of the face + void GetFaceForPoint(Vec3Arg inPoint, const Faces &inFaces, Face *&outFace, float &outDistSq) const; + + /// @brief Calculates the distance between inPoint and inFace + /// @param inFace Face to test + /// @param inPoint Point to test + /// @return If the projection of the point on the plane is interior to the face 0, otherwise the squared distance to the closest edge + float GetDistanceToEdgeSq(Vec3Arg inPoint, const Face *inFace) const; + + /// Assigns a position to one of the supplied faces based on which face is closest. + /// @param inPositionIdx Index of the position to add + /// @param inFaces List of faces to consider + /// @param inToleranceSq Tolerance of the hull, if the point is closer to the face than this, we ignore it + /// @return True if point was assigned, false if it was discarded or added to the coplanar list + bool AssignPointToFace(int inPositionIdx, const Faces &inFaces, float inToleranceSq); + + /// Add a new point to the convex hull + void AddPoint(Face *inFacingFace, int inIdx, float inToleranceSq, Faces &outNewFaces); + + /// Remove all faces that have been marked 'removed' from mFaces list + void GarbageCollectFaces(); + + /// Create a new face + Face * CreateFace(); + + /// Create a new triangle + Face * CreateTriangle(int inIdx1, int inIdx2, int inIdx3); + + /// Delete a face (checking that it is not connected to any other faces) + void FreeFace(Face *inFace); + + /// Release all faces and edges + void FreeFaces(); + + /// Link face edge to other face edge + static void sLinkFace(Edge *inEdge1, Edge *inEdge2); + + /// Unlink this face from all of its neighbours + static void sUnlinkFace(Face *inFace); + + /// Given one face that faces inVertex, find the edges of the faces that are not facing inVertex. + /// Will flag all those faces for removal. + void FindEdge(Face *inFacingFace, Vec3Arg inVertex, FullEdges &outEdges) const; + + /// Merges the two faces that share inEdge into the face inEdge->mFace + void MergeFaces(Edge *inEdge); + + /// Merges inFace with a neighbour if it is degenerate (a sliver) + void MergeDegenerateFace(Face *inFace, Faces &ioAffectedFaces); + + /// Merges any coplanar as well as neighbours that form a non-convex edge into inFace. + /// Faces are considered coplanar if the distance^2 of the other face's centroid is smaller than inToleranceSq. + void MergeCoplanarOrConcaveFaces(Face *inFace, float inToleranceSq, Faces &ioAffectedFaces); + + /// Mark face as affected if it is not already in the list + static void sMarkAffected(Face *inFace, Faces &ioAffectedFaces); + + /// Removes all invalid edges. + /// 1. Merges inFace with faces that share two edges with it since this means inFace or the other face cannot be convex or the edge is colinear. + /// 2. Removes edges that are interior to inFace (that have inFace on both sides) + /// Any faces that need to be checked for validity will be added to ioAffectedFaces. + void RemoveInvalidEdges(Face *inFace, Faces &ioAffectedFaces); + + /// Removes inFace if it consists of only 2 edges, linking its neighbouring faces together + /// Any faces that need to be checked for validity will be added to ioAffectedFaces. + /// @return True if face was removed. + bool RemoveTwoEdgeFace(Face *inFace, Faces &ioAffectedFaces) const; + +#ifdef JPH_ENABLE_ASSERTS + /// Dumps the text representation of a face to the TTY + void DumpFace(const Face *inFace) const; + + /// Dumps the text representation of all faces to the TTY + void DumpFaces() const; + + /// Check consistency of 1 face + void ValidateFace(const Face *inFace) const; + + /// Check consistency of all faces + void ValidateFaces() const; +#endif + +#ifdef JPH_CONVEX_BUILDER_DEBUG + /// Draw state of algorithm + void DrawState(bool inDrawConflictList = false) const; + + /// Draw a face for debugging purposes + void DrawWireFace(const Face *inFace, ColorArg inColor) const; + + /// Draw an edge for debugging purposes + void DrawEdge(const Edge *inEdge, ColorArg inColor) const; +#endif + +#ifdef JPH_CONVEX_BUILDER_DUMP_SHAPE + void DumpShape() const; +#endif + + const Positions & mPositions; ///< List of positions (some of them are part of the hull) + Faces mFaces; ///< List of faces that are part of the hull (if !mRemoved) + + struct Coplanar + { + int mPositionIdx; ///< Index in mPositions + float mDistanceSq; ///< Distance to the edge of closest face (should be > 0) + }; + using CoplanarList = Array; + + CoplanarList mCoplanarList; ///< List of positions that are coplanar to a face but outside of the face, these are added to the hull at the end + +#ifdef JPH_CONVEX_BUILDER_DEBUG + int mIteration; ///< Number of iterations we've had so far (for debug purposes) + mutable RVec3 mOffset; ///< Offset to use for state drawing + Vec3 mDelta; ///< Delta offset between next states +#endif +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/ConvexHullBuilder2D.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/ConvexHullBuilder2D.cpp new file mode 100644 index 00000000000..968e6b46bb5 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/ConvexHullBuilder2D.cpp @@ -0,0 +1,336 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include + +#ifdef JPH_CONVEX_BUILDER_2D_DEBUG + #include +#endif + +JPH_NAMESPACE_BEGIN + +void ConvexHullBuilder2D::Edge::CalculateNormalAndCenter(const Vec3 *inPositions) +{ + Vec3 p1 = inPositions[mStartIdx]; + Vec3 p2 = inPositions[mNextEdge->mStartIdx]; + + // Center of edge + mCenter = 0.5f * (p1 + p2); + + // Create outward pointing normal. + // We have two choices for the normal (which satisfies normal . edge = 0): + // normal1 = (-edge.y, edge.x, 0) + // normal2 = (edge.y, -edge.x, 0) + // We want (normal x edge).z > 0 so that the normal points out of the polygon. Only normal2 satisfies this condition. + Vec3 edge = p2 - p1; + mNormal = Vec3(edge.GetY(), -edge.GetX(), 0); +} + +ConvexHullBuilder2D::ConvexHullBuilder2D(const Positions &inPositions) : + mPositions(inPositions) +{ +#ifdef JPH_CONVEX_BUILDER_2D_DEBUG + // Center the drawing of the first hull around the origin and calculate the delta offset between states + mOffset = RVec3::sZero(); + if (mPositions.empty()) + { + // No hull will be generated + mDelta = Vec3::sZero(); + } + else + { + Vec3 maxv = Vec3::sReplicate(-FLT_MAX), minv = Vec3::sReplicate(FLT_MAX); + for (Vec3 v : mPositions) + { + minv = Vec3::sMin(minv, v); + maxv = Vec3::sMax(maxv, v); + mOffset -= v; + } + mOffset /= Real(mPositions.size()); + mDelta = Vec3((maxv - minv).GetX() + 0.5f, 0, 0); + mOffset += mDelta; // Don't start at origin, we're already drawing the final hull there + } +#endif +} + +ConvexHullBuilder2D::~ConvexHullBuilder2D() +{ + FreeEdges(); +} + +void ConvexHullBuilder2D::FreeEdges() +{ + if (mFirstEdge == nullptr) + return; + + Edge *edge = mFirstEdge; + do + { + Edge *next = edge->mNextEdge; + delete edge; + edge = next; + } while (edge != mFirstEdge); + + mFirstEdge = nullptr; + mNumEdges = 0; +} + +#ifdef JPH_ENABLE_ASSERTS + +void ConvexHullBuilder2D::ValidateEdges() const +{ + if (mFirstEdge == nullptr) + { + JPH_ASSERT(mNumEdges == 0); + return; + } + + int count = 0; + + Edge *edge = mFirstEdge; + do + { + // Validate connectivity + JPH_ASSERT(edge->mNextEdge->mPrevEdge == edge); + JPH_ASSERT(edge->mPrevEdge->mNextEdge == edge); + + ++count; + edge = edge->mNextEdge; + } while (edge != mFirstEdge); + + // Validate that count matches + JPH_ASSERT(count == mNumEdges); +} + +#endif // JPH_ENABLE_ASSERTS + +void ConvexHullBuilder2D::AssignPointToEdge(int inPositionIdx, const Array &inEdges) const +{ + Vec3 point = mPositions[inPositionIdx]; + + Edge *best_edge = nullptr; + float best_dist_sq = 0.0f; + + // Test against all edges + for (Edge *edge : inEdges) + { + // Determine distance to edge + float dot = edge->mNormal.Dot(point - edge->mCenter); + if (dot > 0.0f) + { + float dist_sq = dot * dot / edge->mNormal.LengthSq(); + if (dist_sq > best_dist_sq) + { + best_edge = edge; + best_dist_sq = dist_sq; + } + } + } + + // If this point is in front of the edge, add it to the conflict list + if (best_edge != nullptr) + { + if (best_dist_sq > best_edge->mFurthestPointDistanceSq) + { + // This point is further away than any others, update the distance and add point as last point + best_edge->mFurthestPointDistanceSq = best_dist_sq; + best_edge->mConflictList.push_back(inPositionIdx); + } + else + { + // Not the furthest point, add it as the before last point + best_edge->mConflictList.push_back(best_edge->mConflictList.back()); + best_edge->mConflictList[best_edge->mConflictList.size() - 2] = inPositionIdx; + } + } +} + +ConvexHullBuilder2D::EResult ConvexHullBuilder2D::Initialize(int inIdx1, int inIdx2, int inIdx3, int inMaxVertices, float inTolerance, Edges &outEdges) +{ + // Clear any leftovers + FreeEdges(); + outEdges.clear(); + + // Reset flag + EResult result = EResult::Success; + + // Determine a suitable tolerance for detecting that points are colinear + // Formula as per: Implementing Quickhull - Dirk Gregorius. + Vec3 vmax = Vec3::sZero(); + for (Vec3 v : mPositions) + vmax = Vec3::sMax(vmax, v.Abs()); + float colinear_tolerance_sq = Square(2.0f * FLT_EPSILON * (vmax.GetX() + vmax.GetY())); + + // Increase desired tolerance if accuracy doesn't allow it + float tolerance_sq = max(colinear_tolerance_sq, Square(inTolerance)); + + // Start with the initial indices in counter clockwise order + float z = (mPositions[inIdx2] - mPositions[inIdx1]).Cross(mPositions[inIdx3] - mPositions[inIdx1]).GetZ(); + if (z < 0.0f) + swap(inIdx1, inIdx2); + + // Create and link edges + Edge *e1 = new Edge(inIdx1); + Edge *e2 = new Edge(inIdx2); + Edge *e3 = new Edge(inIdx3); + e1->mNextEdge = e2; + e1->mPrevEdge = e3; + e2->mNextEdge = e3; + e2->mPrevEdge = e1; + e3->mNextEdge = e1; + e3->mPrevEdge = e2; + mFirstEdge = e1; + mNumEdges = 3; + + // Build the initial conflict lists + Array edges { e1, e2, e3 }; + for (Edge *edge : edges) + edge->CalculateNormalAndCenter(mPositions.data()); + for (int idx = 0; idx < (int)mPositions.size(); ++idx) + if (idx != inIdx1 && idx != inIdx2 && idx != inIdx3) + AssignPointToEdge(idx, edges); + + JPH_IF_ENABLE_ASSERTS(ValidateEdges();) +#ifdef JPH_CONVEX_BUILDER_2D_DEBUG + DrawState(); +#endif + + // Add the remaining points to the hull + for (;;) + { + // Check if we've reached the max amount of vertices that are allowed + if (mNumEdges >= inMaxVertices) + { + result = EResult::MaxVerticesReached; + break; + } + + // Find the edge with the furthest point on it + Edge *edge_with_furthest_point = nullptr; + float furthest_dist_sq = 0.0f; + Edge *edge = mFirstEdge; + do + { + if (edge->mFurthestPointDistanceSq > furthest_dist_sq) + { + furthest_dist_sq = edge->mFurthestPointDistanceSq; + edge_with_furthest_point = edge; + } + edge = edge->mNextEdge; + } while (edge != mFirstEdge); + + // If there is none closer than our tolerance value, we're done + if (edge_with_furthest_point == nullptr || furthest_dist_sq < tolerance_sq) + break; + + // Take the furthest point + int furthest_point_idx = edge_with_furthest_point->mConflictList.back(); + edge_with_furthest_point->mConflictList.pop_back(); + Vec3 furthest_point = mPositions[furthest_point_idx]; + + // Find the horizon of edges that need to be removed + Edge *first_edge = edge_with_furthest_point; + do + { + Edge *prev = first_edge->mPrevEdge; + if (!prev->IsFacing(furthest_point)) + break; + first_edge = prev; + } while (first_edge != edge_with_furthest_point); + + Edge *last_edge = edge_with_furthest_point; + do + { + Edge *next = last_edge->mNextEdge; + if (!next->IsFacing(furthest_point)) + break; + last_edge = next; + } while (last_edge != edge_with_furthest_point); + + // Create new edges + e1 = new Edge(first_edge->mStartIdx); + e2 = new Edge(furthest_point_idx); + e1->mNextEdge = e2; + e1->mPrevEdge = first_edge->mPrevEdge; + e2->mPrevEdge = e1; + e2->mNextEdge = last_edge->mNextEdge; + e1->mPrevEdge->mNextEdge = e1; + e2->mNextEdge->mPrevEdge = e2; + mFirstEdge = e1; // We could delete mFirstEdge so just update it to the newly created edge + mNumEdges += 2; + + // Calculate normals + Array new_edges { e1, e2 }; + for (Edge *new_edge : new_edges) + new_edge->CalculateNormalAndCenter(mPositions.data()); + + // Delete the old edges + for (;;) + { + Edge *next = first_edge->mNextEdge; + + // Redistribute points in conflict list + for (int idx : first_edge->mConflictList) + AssignPointToEdge(idx, new_edges); + + // Delete the old edge + delete first_edge; + --mNumEdges; + + if (first_edge == last_edge) + break; + first_edge = next; + } + + JPH_IF_ENABLE_ASSERTS(ValidateEdges();) + #ifdef JPH_CONVEX_BUILDER_2D_DEBUG + DrawState(); + #endif + } + + // Convert the edge list to a list of indices + outEdges.reserve(mNumEdges); + Edge *edge = mFirstEdge; + do + { + outEdges.push_back(edge->mStartIdx); + edge = edge->mNextEdge; + } while (edge != mFirstEdge); + + return result; +} + +#ifdef JPH_CONVEX_BUILDER_2D_DEBUG + +void ConvexHullBuilder2D::DrawState() +{ + int color_idx = 0; + + const Edge *edge = mFirstEdge; + do + { + const Edge *next = edge->mNextEdge; + + // Get unique color per edge + Color color = Color::sGetDistinctColor(color_idx++); + + // Draw edge and normal + DebugRenderer::sInstance->DrawArrow(cDrawScale * (mOffset + mPositions[edge->mStartIdx]), cDrawScale * (mOffset + mPositions[next->mStartIdx]), color, 0.1f); + DebugRenderer::sInstance->DrawArrow(cDrawScale * (mOffset + edge->mCenter), cDrawScale * (mOffset + edge->mCenter) + edge->mNormal.NormalizedOr(Vec3::sZero()), Color::sGreen, 0.1f); + + // Draw points that belong to this edge in the same color + for (int idx : edge->mConflictList) + DebugRenderer::sInstance->DrawMarker(cDrawScale * (mOffset + mPositions[idx]), color, 0.05f); + + edge = next; + } while (edge != mFirstEdge); + + mOffset += mDelta; +} + +#endif + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/ConvexHullBuilder2D.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/ConvexHullBuilder2D.h new file mode 100644 index 00000000000..ff06a3403e0 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/ConvexHullBuilder2D.h @@ -0,0 +1,105 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +//#define JPH_CONVEX_BUILDER_2D_DEBUG + +JPH_NAMESPACE_BEGIN + +/// A convex hull builder that tries to create 2D hulls as accurately as possible. Used for offline processing. +class JPH_EXPORT ConvexHullBuilder2D : public NonCopyable +{ +public: + using Positions = Array; + using Edges = Array; + + /// Constructor + /// @param inPositions Positions used to make the hull. Uses X and Y component of Vec3 only! + explicit ConvexHullBuilder2D(const Positions &inPositions); + + /// Destructor + ~ConvexHullBuilder2D(); + + /// Result enum that indicates how the hull got created + enum class EResult + { + Success, ///< Hull building finished successfully + MaxVerticesReached, ///< Hull building finished successfully, but the desired accuracy was not reached because the max vertices limit was reached + }; + + /// Takes all positions as provided by the constructor and use them to build a hull + /// Any points that are closer to the hull than inTolerance will be discarded + /// @param inIdx1 , inIdx2 , inIdx3 The indices to use as initial hull (in any order) + /// @param inMaxVertices Max vertices to allow in the hull. Specify INT_MAX if there is no limit. + /// @param inTolerance Max distance that a point is allowed to be outside of the hull + /// @param outEdges On success this will contain the list of indices that form the hull (counter clockwise) + /// @return Status code that reports if the hull was created or not + EResult Initialize(int inIdx1, int inIdx2, int inIdx3, int inMaxVertices, float inTolerance, Edges &outEdges); + +private: +#ifdef JPH_CONVEX_BUILDER_2D_DEBUG + /// Factor to scale convex hull when debug drawing the construction process + static constexpr Real cDrawScale = 10; +#endif + + class Edge; + + /// Frees all edges + void FreeEdges(); + + /// Assigns a position to one of the supplied edges based on which edge is closest. + /// @param inPositionIdx Index of the position to add + /// @param inEdges List of edges to consider + void AssignPointToEdge(int inPositionIdx, const Array &inEdges) const; + +#ifdef JPH_CONVEX_BUILDER_2D_DEBUG + /// Draw state of algorithm + void DrawState(); +#endif + +#ifdef JPH_ENABLE_ASSERTS + /// Validate that the edge structure is intact + void ValidateEdges() const; +#endif + + using ConflictList = Array; + + /// Linked list of edges + class Edge + { + public: + JPH_OVERRIDE_NEW_DELETE + + /// Constructor + explicit Edge(int inStartIdx) : mStartIdx(inStartIdx) { } + + /// Calculate the center of the edge and the edge normal + void CalculateNormalAndCenter(const Vec3 *inPositions); + + /// Check if this edge is facing inPosition + inline bool IsFacing(Vec3Arg inPosition) const { return mNormal.Dot(inPosition - mCenter) > 0.0f; } + + Vec3 mNormal; ///< Normal of the edge (not normalized) + Vec3 mCenter; ///< Center of the edge + ConflictList mConflictList; ///< Positions associated with this edge (that are closest to this edge). Last entry is the one furthest away from the edge, remainder is unsorted. + Edge * mPrevEdge = nullptr; ///< Previous edge in circular list + Edge * mNextEdge = nullptr; ///< Next edge in circular list + int mStartIdx; ///< Position index of start of this edge + float mFurthestPointDistanceSq = 0.0f; ///< Squared distance of furthest point from the conflict list to the edge + }; + + const Positions & mPositions; ///< List of positions (some of them are part of the hull) + Edge * mFirstEdge = nullptr; ///< First edge of the hull + int mNumEdges = 0; ///< Number of edges in hull + +#ifdef JPH_CONVEX_BUILDER_2D_DEBUG + RVec3 mOffset; ///< Offset to use for state drawing + Vec3 mDelta; ///< Delta offset between next states +#endif +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/ConvexSupport.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/ConvexSupport.h new file mode 100644 index 00000000000..3ba2c935db2 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/ConvexSupport.h @@ -0,0 +1,188 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +/// Helper functions to get the support point for a convex object +/// Structure that transforms a convex object (supports only uniform scaling) +template +struct TransformedConvexObject +{ + /// Create transformed convex object. + TransformedConvexObject(Mat44Arg inTransform, const ConvexObject &inObject) : + mTransform(inTransform), + mObject(inObject) + { + } + + /// Calculate the support vector for this convex shape. + Vec3 GetSupport(Vec3Arg inDirection) const + { + return mTransform * mObject.GetSupport(mTransform.Multiply3x3Transposed(inDirection)); + } + + /// Get the vertices of the face that faces inDirection the most + template + void GetSupportingFace(Vec3Arg inDirection, VERTEX_ARRAY &outVertices) const + { + mObject.GetSupportingFace(mTransform.Multiply3x3Transposed(inDirection), outVertices); + + for (Vec3 &v : outVertices) + v = mTransform * v; + } + + Mat44 mTransform; + const ConvexObject & mObject; +}; + +/// Structure that adds a convex radius +template +struct AddConvexRadius +{ + AddConvexRadius(const ConvexObject &inObject, float inRadius) : + mObject(inObject), + mRadius(inRadius) + { + } + + /// Calculate the support vector for this convex shape. + Vec3 GetSupport(Vec3Arg inDirection) const + { + float length = inDirection.Length(); + return length > 0.0f ? mObject.GetSupport(inDirection) + (mRadius / length) * inDirection : mObject.GetSupport(inDirection); + } + + const ConvexObject & mObject; + float mRadius; +}; + +/// Structure that performs a Minkowski difference A - B +template +struct MinkowskiDifference +{ + MinkowskiDifference(const ConvexObjectA &inObjectA, const ConvexObjectB &inObjectB) : + mObjectA(inObjectA), + mObjectB(inObjectB) + { + } + + /// Calculate the support vector for this convex shape. + Vec3 GetSupport(Vec3Arg inDirection) const + { + return mObjectA.GetSupport(inDirection) - mObjectB.GetSupport(-inDirection); + } + + const ConvexObjectA & mObjectA; + const ConvexObjectB & mObjectB; +}; + +/// Class that wraps a point so that it can be used with convex collision detection +struct PointConvexSupport +{ + /// Calculate the support vector for this convex shape. + Vec3 GetSupport([[maybe_unused]] Vec3Arg inDirection) const + { + return mPoint; + } + + Vec3 mPoint; +}; + +/// Class that wraps a triangle so that it can used with convex collision detection +struct TriangleConvexSupport +{ + /// Constructor + TriangleConvexSupport(Vec3Arg inV1, Vec3Arg inV2, Vec3Arg inV3) : + mV1(inV1), + mV2(inV2), + mV3(inV3) + { + } + + /// Calculate the support vector for this convex shape. + Vec3 GetSupport(Vec3Arg inDirection) const + { + // Project vertices on inDirection + float d1 = mV1.Dot(inDirection); + float d2 = mV2.Dot(inDirection); + float d3 = mV3.Dot(inDirection); + + // Return vertex with biggest projection + if (d1 > d2) + { + if (d1 > d3) + return mV1; + else + return mV3; + } + else + { + if (d2 > d3) + return mV2; + else + return mV3; + } + } + + /// Get the vertices of the face that faces inDirection the most + template + void GetSupportingFace([[maybe_unused]] Vec3Arg inDirection, VERTEX_ARRAY &outVertices) const + { + outVertices.push_back(mV1); + outVertices.push_back(mV2); + outVertices.push_back(mV3); + } + + /// The three vertices of the triangle + Vec3 mV1; + Vec3 mV2; + Vec3 mV3; +}; + +/// Class that wraps a polygon so that it can used with convex collision detection +template +struct PolygonConvexSupport +{ + /// Constructor + explicit PolygonConvexSupport(const VERTEX_ARRAY &inVertices) : + mVertices(inVertices) + { + } + + /// Calculate the support vector for this convex shape. + Vec3 GetSupport(Vec3Arg inDirection) const + { + Vec3 support_point = mVertices[0]; + float best_dot = mVertices[0].Dot(inDirection); + + for (typename VERTEX_ARRAY::const_iterator v = mVertices.begin() + 1; v < mVertices.end(); ++v) + { + float dot = v->Dot(inDirection); + if (dot > best_dot) + { + best_dot = dot; + support_point = *v; + } + } + + return support_point; + } + + /// Get the vertices of the face that faces inDirection the most + template + void GetSupportingFace([[maybe_unused]] Vec3Arg inDirection, VERTEX_ARRAY_ARG &outVertices) const + { + for (Vec3 v : mVertices) + outVertices.push_back(v); + } + + /// The vertices of the polygon + const VERTEX_ARRAY & mVertices; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/EPAConvexHullBuilder.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/EPAConvexHullBuilder.h new file mode 100644 index 00000000000..9f1c5a8e48c --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/EPAConvexHullBuilder.h @@ -0,0 +1,844 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +// Define to validate the integrity of the hull structure +//#define JPH_EPA_CONVEX_BUILDER_VALIDATE + +// Define to draw the building of the hull for debugging purposes +//#define JPH_EPA_CONVEX_BUILDER_DRAW + +#include + +#ifdef JPH_EPA_CONVEX_BUILDER_DRAW + #include + #include +#endif + +JPH_NAMESPACE_BEGIN + +/// A convex hull builder specifically made for the EPA penetration depth calculation. It trades accuracy for speed and will simply abort of the hull forms defects due to numerical precision problems. +class EPAConvexHullBuilder : public NonCopyable +{ +private: +#ifdef JPH_EPA_CONVEX_BUILDER_DRAW + /// Factor to scale convex hull when debug drawing the construction process + static constexpr Real cDrawScale = 10; +#endif + +public: + // Due to the Euler characteristic (https://en.wikipedia.org/wiki/Euler_characteristic) we know that Vertices - Edges + Faces = 2 + // In our case we only have triangles and they are always fully connected, so each edge is shared exactly between 2 faces: Edges = Faces * 3 / 2 + // Substituting: Vertices = Faces / 2 + 2 which is approximately Faces / 2. + static constexpr int cMaxTriangles = 256; ///< Max triangles in hull + static constexpr int cMaxPoints = cMaxTriangles / 2; ///< Max number of points in hull + + // Constants + static constexpr int cMaxEdgeLength = 128; ///< Max number of edges in FindEdge + static constexpr float cMinTriangleArea = 1.0e-10f; ///< Minimum area of a triangle before, if smaller than this it will not be added to the priority queue + static constexpr float cBarycentricEpsilon = 1.0e-3f; ///< Epsilon value used to determine if a point is in the interior of a triangle + + // Forward declare + class Triangle; + + /// Class that holds the information of an edge + class Edge + { + public: + /// Information about neighbouring triangle + Triangle * mNeighbourTriangle; ///< Triangle that neighbours this triangle + int mNeighbourEdge; ///< Index in mEdge that specifies edge that this Edge is connected to + + int mStartIdx; ///< Vertex index in mPositions that indicates the start vertex of this edge + }; + + using Edges = StaticArray; + using NewTriangles = StaticArray; + + /// Class that holds the information of one triangle + class Triangle : public NonCopyable + { + public: + /// Constructor + inline Triangle(int inIdx0, int inIdx1, int inIdx2, const Vec3 *inPositions); + + /// Check if triangle is facing inPosition + inline bool IsFacing(Vec3Arg inPosition) const + { + JPH_ASSERT(!mRemoved); + return mNormal.Dot(inPosition - mCentroid) > 0.0f; + } + + /// Check if triangle is facing the origin + inline bool IsFacingOrigin() const + { + JPH_ASSERT(!mRemoved); + return mNormal.Dot(mCentroid) < 0.0f; + } + + /// Get the next edge of edge inIndex + inline const Edge & GetNextEdge(int inIndex) const + { + return mEdge[(inIndex + 1) % 3]; + } + + Edge mEdge[3]; ///< 3 edges of this triangle + Vec3 mNormal; ///< Normal of this triangle, length is 2 times area of triangle + Vec3 mCentroid; ///< Center of the triangle + float mClosestLenSq = FLT_MAX; ///< Closest distance^2 from origin to triangle + float mLambda[2]; ///< Barycentric coordinates of closest point to origin on triangle + bool mLambdaRelativeTo0; ///< How to calculate the closest point, true: y0 + l0 * (y1 - y0) + l1 * (y2 - y0), false: y1 + l0 * (y0 - y1) + l1 * (y2 - y1) + bool mClosestPointInterior = false; ///< Flag that indicates that the closest point from this triangle to the origin is an interior point + bool mRemoved = false; ///< Flag that indicates that triangle has been removed + bool mInQueue = false; ///< Flag that indicates that this triangle was placed in the sorted heap (stays true after it is popped because the triangle is freed by the main EPA algorithm loop) +#ifdef JPH_EPA_CONVEX_BUILDER_DRAW + int mIteration; ///< Iteration that this triangle was created +#endif + }; + + /// Factory that creates triangles in a fixed size buffer + class TriangleFactory : public NonCopyable + { + private: + /// Struct that stores both a triangle or a next pointer in case the triangle is unused + union alignas(Triangle) Block + { + uint8 mTriangle[sizeof(Triangle)]; + Block * mNextFree; + }; + + /// Storage for triangle data + Block mTriangles[cMaxTriangles]; ///< Storage for triangles + Block * mNextFree = nullptr; ///< List of free triangles + int mHighWatermark = 0; ///< High water mark for used triangles (if mNextFree == nullptr we can take one from here) + + public: + /// Return all triangles to the free pool + void Clear() + { + mNextFree = nullptr; + mHighWatermark = 0; + } + + /// Allocate a new triangle with 3 indexes + Triangle * CreateTriangle(int inIdx0, int inIdx1, int inIdx2, const Vec3 *inPositions) + { + Triangle *t; + if (mNextFree != nullptr) + { + // Entry available from the free list + t = reinterpret_cast(&mNextFree->mTriangle); + mNextFree = mNextFree->mNextFree; + } + else + { + // Allocate from never used before triangle store + if (mHighWatermark >= cMaxTriangles) + return nullptr; // Buffer full + t = reinterpret_cast(&mTriangles[mHighWatermark].mTriangle); + ++mHighWatermark; + } + + // Call constructor + new (t) Triangle(inIdx0, inIdx1, inIdx2, inPositions); + + return t; + } + + /// Free a triangle + void FreeTriangle(Triangle *inT) + { + // Destruct triangle + inT->~Triangle(); +#ifdef _DEBUG + memset(inT, 0xcd, sizeof(Triangle)); +#endif + + // Add triangle to the free list + Block *tu = reinterpret_cast(inT); + tu->mNextFree = mNextFree; + mNextFree = tu; + } + }; + + // Typedefs + using PointsBase = StaticArray; + using Triangles = StaticArray; + + /// Specialized points list that allows direct access to the size + class Points : public PointsBase + { + public: + size_type & GetSizeRef() + { + return mSize; + } + }; + + /// Specialized triangles list that keeps them sorted on closest distance to origin + class TriangleQueue : public Triangles + { + public: + /// Function to sort triangles on closest distance to origin + static bool sTriangleSorter(const Triangle *inT1, const Triangle *inT2) + { + return inT1->mClosestLenSq > inT2->mClosestLenSq; + } + + /// Add triangle to the list + void push_back(Triangle *inT) + { + // Add to base + Triangles::push_back(inT); + + // Mark in queue + inT->mInQueue = true; + + // Resort heap + std::push_heap(begin(), end(), sTriangleSorter); + } + + /// Peek the next closest triangle without removing it + Triangle * PeekClosest() + { + return front(); + } + + /// Get next closest triangle + Triangle * PopClosest() + { + // Move closest to end + std::pop_heap(begin(), end(), sTriangleSorter); + + // Remove last triangle + Triangle *t = back(); + pop_back(); + return t; + } + }; + + /// Constructor + explicit EPAConvexHullBuilder(const Points &inPositions) : + mPositions(inPositions) + { +#ifdef JPH_EPA_CONVEX_BUILDER_DRAW + mIteration = 0; + mOffset = RVec3::sZero(); +#endif + } + + /// Initialize the hull with 3 points + void Initialize(int inIdx1, int inIdx2, int inIdx3) + { + // Release triangles + mFactory.Clear(); + + // Create triangles (back to back) + Triangle *t1 = CreateTriangle(inIdx1, inIdx2, inIdx3); + Triangle *t2 = CreateTriangle(inIdx1, inIdx3, inIdx2); + + // Link triangles edges + sLinkTriangle(t1, 0, t2, 2); + sLinkTriangle(t1, 1, t2, 1); + sLinkTriangle(t1, 2, t2, 0); + + // Always add both triangles to the priority queue + mTriangleQueue.push_back(t1); + mTriangleQueue.push_back(t2); + +#ifdef JPH_EPA_CONVEX_BUILDER_DRAW + // Draw current state + DrawState(); + + // Increment iteration counter + ++mIteration; +#endif + } + + /// Check if there's another triangle to process from the queue + bool HasNextTriangle() const + { + return !mTriangleQueue.empty(); + } + + /// Access to the next closest triangle to the origin (won't remove it from the queue). + Triangle * PeekClosestTriangleInQueue() + { + return mTriangleQueue.PeekClosest(); + } + + /// Access to the next closest triangle to the origin and remove it from the queue. + Triangle * PopClosestTriangleFromQueue() + { + return mTriangleQueue.PopClosest(); + } + + /// Find the triangle on which inPosition is the furthest to the front + /// Note this function works as long as all points added have been added with AddPoint(..., FLT_MAX). + Triangle * FindFacingTriangle(Vec3Arg inPosition, float &outBestDistSq) + { + Triangle *best = nullptr; + float best_dist_sq = 0.0f; + + for (Triangle *t : mTriangleQueue) + if (!t->mRemoved) + { + float dot = t->mNormal.Dot(inPosition - t->mCentroid); + if (dot > 0.0f) + { + float dist_sq = dot * dot / t->mNormal.LengthSq(); + if (dist_sq > best_dist_sq) + { + best = t; + best_dist_sq = dist_sq; + } + } + } + + outBestDistSq = best_dist_sq; + return best; + } + + /// Add a new point to the convex hull + bool AddPoint(Triangle *inFacingTriangle, int inIdx, float inClosestDistSq, NewTriangles &outTriangles) + { + // Get position + Vec3 pos = mPositions[inIdx]; + +#ifdef JPH_EPA_CONVEX_BUILDER_DRAW + // Draw new support point + DrawMarker(pos, Color::sYellow, 1.0f); +#endif + +#ifdef JPH_EPA_CONVEX_BUILDER_VALIDATE + // Check if structure is intact + ValidateTriangles(); +#endif + + // Find edge of convex hull of triangles that are not facing the new vertex w + Edges edges; + if (!FindEdge(inFacingTriangle, pos, edges)) + return false; + + // Create new triangles + int num_edges = edges.size(); + for (int i = 0; i < num_edges; ++i) + { + // Create new triangle + Triangle *nt = CreateTriangle(edges[i].mStartIdx, edges[(i + 1) % num_edges].mStartIdx, inIdx); + if (nt == nullptr) + return false; + outTriangles.push_back(nt); + + // Check if we need to put this triangle in the priority queue + if ((nt->mClosestPointInterior && nt->mClosestLenSq < inClosestDistSq) // For the main algorithm + || nt->mClosestLenSq < 0.0f) // For when the origin is not inside the hull yet + mTriangleQueue.push_back(nt); + } + + // Link edges + for (int i = 0; i < num_edges; ++i) + { + sLinkTriangle(outTriangles[i], 0, edges[i].mNeighbourTriangle, edges[i].mNeighbourEdge); + sLinkTriangle(outTriangles[i], 1, outTriangles[(i + 1) % num_edges], 2); + } + +#ifdef JPH_EPA_CONVEX_BUILDER_VALIDATE + // Check if structure is intact + ValidateTriangles(); +#endif + +#ifdef JPH_EPA_CONVEX_BUILDER_DRAW + // Draw state of the hull + DrawState(); + + // Increment iteration counter + ++mIteration; +#endif + + return true; + } + + /// Free a triangle + void FreeTriangle(Triangle *inT) + { +#ifdef JPH_ENABLE_ASSERTS + // Make sure that this triangle is not connected + JPH_ASSERT(inT->mRemoved); + for (const Edge &e : inT->mEdge) + JPH_ASSERT(e.mNeighbourTriangle == nullptr); +#endif + +#if defined(JPH_EPA_CONVEX_BUILDER_VALIDATE) || defined(JPH_EPA_CONVEX_BUILDER_DRAW) + // Remove from list of all triangles + Triangles::iterator i = std::find(mTriangles.begin(), mTriangles.end(), inT); + JPH_ASSERT(i != mTriangles.end()); + mTriangles.erase(i); +#endif + + mFactory.FreeTriangle(inT); + } + +private: + /// Create a new triangle + Triangle * CreateTriangle(int inIdx1, int inIdx2, int inIdx3) + { + // Call provider to create triangle + Triangle *t = mFactory.CreateTriangle(inIdx1, inIdx2, inIdx3, mPositions.data()); + if (t == nullptr) + return nullptr; + +#ifdef JPH_EPA_CONVEX_BUILDER_DRAW + // Remember iteration counter + t->mIteration = mIteration; +#endif + +#if defined(JPH_EPA_CONVEX_BUILDER_VALIDATE) || defined(JPH_EPA_CONVEX_BUILDER_DRAW) + // Add to list of triangles for debugging purposes + mTriangles.push_back(t); +#endif + + return t; + } + + /// Link triangle edge to other triangle edge + static void sLinkTriangle(Triangle *inT1, int inEdge1, Triangle *inT2, int inEdge2) + { + JPH_ASSERT(inEdge1 >= 0 && inEdge1 < 3); + JPH_ASSERT(inEdge2 >= 0 && inEdge2 < 3); + Edge &e1 = inT1->mEdge[inEdge1]; + Edge &e2 = inT2->mEdge[inEdge2]; + + // Check not connected yet + JPH_ASSERT(e1.mNeighbourTriangle == nullptr); + JPH_ASSERT(e2.mNeighbourTriangle == nullptr); + + // Check vertices match + JPH_ASSERT(e1.mStartIdx == inT2->GetNextEdge(inEdge2).mStartIdx); + JPH_ASSERT(e2.mStartIdx == inT1->GetNextEdge(inEdge1).mStartIdx); + + // Link up + e1.mNeighbourTriangle = inT2; + e1.mNeighbourEdge = inEdge2; + e2.mNeighbourTriangle = inT1; + e2.mNeighbourEdge = inEdge1; + } + + /// Unlink this triangle + void UnlinkTriangle(Triangle *inT) + { + // Unlink from neighbours + for (int i = 0; i < 3; ++i) + { + Edge &edge = inT->mEdge[i]; + if (edge.mNeighbourTriangle != nullptr) + { + Edge &neighbour_edge = edge.mNeighbourTriangle->mEdge[edge.mNeighbourEdge]; + + // Validate that neighbour points to us + JPH_ASSERT(neighbour_edge.mNeighbourTriangle == inT); + JPH_ASSERT(neighbour_edge.mNeighbourEdge == i); + + // Unlink + neighbour_edge.mNeighbourTriangle = nullptr; + edge.mNeighbourTriangle = nullptr; + } + } + + // If this triangle is not in the priority queue, we can delete it now + if (!inT->mInQueue) + FreeTriangle(inT); + } + + /// Given one triangle that faces inVertex, find the edges of the triangles that are not facing inVertex. + /// Will flag all those triangles for removal. + bool FindEdge(Triangle *inFacingTriangle, Vec3Arg inVertex, Edges &outEdges) + { + // Assert that we were given an empty array + JPH_ASSERT(outEdges.empty()); + + // Should start with a facing triangle + JPH_ASSERT(inFacingTriangle->IsFacing(inVertex)); + + // Flag as removed + inFacingTriangle->mRemoved = true; + + // Instead of recursing, we build our own stack with the information we need + struct StackEntry + { + Triangle * mTriangle; + int mEdge; + int mIter; + }; + StackEntry stack[cMaxEdgeLength]; + int cur_stack_pos = 0; + + // Start with the triangle / edge provided + stack[0].mTriangle = inFacingTriangle; + stack[0].mEdge = 0; + stack[0].mIter = -1; // Start with edge 0 (is incremented below before use) + + // Next index that we expect to find, if we don't then there are 'islands' + int next_expected_start_idx = -1; + + for (;;) + { + StackEntry &cur_entry = stack[cur_stack_pos]; + + // Next iteration + if (++cur_entry.mIter >= 3) + { + // This triangle needs to be removed, unlink it now + UnlinkTriangle(cur_entry.mTriangle); + + // Pop from stack + if (--cur_stack_pos < 0) + break; + } + else + { + // Visit neighbour + Edge &e = cur_entry.mTriangle->mEdge[(cur_entry.mEdge + cur_entry.mIter) % 3]; + Triangle *n = e.mNeighbourTriangle; + if (n != nullptr && !n->mRemoved) + { + // Check if vertex is on the front side of this triangle + if (n->IsFacing(inVertex)) + { + // Vertex on front, this triangle needs to be removed + n->mRemoved = true; + + // Add element to the stack of elements to visit + cur_stack_pos++; + JPH_ASSERT(cur_stack_pos < cMaxEdgeLength); + StackEntry &new_entry = stack[cur_stack_pos]; + new_entry.mTriangle = n; + new_entry.mEdge = e.mNeighbourEdge; + new_entry.mIter = 0; // Is incremented before use, we don't need to test this edge again since we came from it + } + else + { + // Detect if edge doesn't connect to previous edge, if this happens we have found and 'island' which means + // the newly added point is so close to the triangles of the hull that we classified some (nearly) coplanar + // triangles as before and some behind the point. At this point we just abort adding the point because + // we've reached numerical precision. + // Note that we do not need to test if the first and last edge connect, since when there are islands + // there should be at least 2 disconnects. + if (e.mStartIdx != next_expected_start_idx && next_expected_start_idx != -1) + return false; + + // Next expected index is the start index of our neighbour's edge + next_expected_start_idx = n->mEdge[e.mNeighbourEdge].mStartIdx; + + // Vertex behind, keep edge + outEdges.push_back(e); + } + } + } + } + + // Assert that we have a fully connected loop + JPH_ASSERT(outEdges.empty() || outEdges[0].mStartIdx == next_expected_start_idx); + +#ifdef JPH_EPA_CONVEX_BUILDER_DRAW + // Draw edge of facing triangles + for (int i = 0; i < (int)outEdges.size(); ++i) + { + RVec3 edge_start = cDrawScale * (mOffset + mPositions[outEdges[i].mStartIdx]); + DebugRenderer::sInstance->DrawArrow(edge_start, cDrawScale * (mOffset + mPositions[outEdges[(i + 1) % outEdges.size()].mStartIdx]), Color::sYellow, 0.01f); + DebugRenderer::sInstance->DrawText3D(edge_start, ConvertToString(outEdges[i].mStartIdx), Color::sWhite); + } + + // Draw the state with the facing triangles removed + DrawState(); +#endif + + // When we start with two triangles facing away from each other and adding a point that is on the plane, + // sometimes we consider the point in front of both causing both triangles to be removed resulting in an empty edge list. + // In this case we fail to add the point which will result in no collision reported (the shapes are contacting in 1 point so there's 0 penetration) + return outEdges.size() >= 3; + } + +#ifdef JPH_EPA_CONVEX_BUILDER_VALIDATE + /// Check consistency of 1 triangle + void ValidateTriangle(const Triangle *inT) const + { + if (inT->mRemoved) + { + // Validate that removed triangles are not connected to anything + for (const Edge &my_edge : inT->mEdge) + JPH_ASSERT(my_edge.mNeighbourTriangle == nullptr); + } + else + { + for (int i = 0; i < 3; ++i) + { + const Edge &my_edge = inT->mEdge[i]; + + // Assert that we have a neighbour + const Triangle *nb = my_edge.mNeighbourTriangle; + JPH_ASSERT(nb != nullptr); + + if (nb != nullptr) + { + // Assert that our neighbours edge points to us + const Edge &nb_edge = nb->mEdge[my_edge.mNeighbourEdge]; + JPH_ASSERT(nb_edge.mNeighbourTriangle == inT); + JPH_ASSERT(nb_edge.mNeighbourEdge == i); + + // Assert that the next edge of the neighbour points to the same vertex as this edge's vertex + const Edge &nb_next_edge = nb->GetNextEdge(my_edge.mNeighbourEdge); + JPH_ASSERT(nb_next_edge.mStartIdx == my_edge.mStartIdx); + + // Assert that my next edge points to the same vertex as my neighbours vertex + const Edge &my_next_edge = inT->GetNextEdge(i); + JPH_ASSERT(my_next_edge.mStartIdx == nb_edge.mStartIdx); + } + } + } + } + + /// Check consistency of all triangles + void ValidateTriangles() const + { + for (const Triangle *t : mTriangles) + ValidateTriangle(t); + } +#endif + +#ifdef JPH_EPA_CONVEX_BUILDER_DRAW +public: + /// Draw state of algorithm + void DrawState() + { + // Draw origin + DebugRenderer::sInstance->DrawCoordinateSystem(RMat44::sTranslation(cDrawScale * mOffset), 1.0f); + + // Draw triangles + for (const Triangle *t : mTriangles) + if (!t->mRemoved) + { + // Calculate the triangle vertices + RVec3 p1 = cDrawScale * (mOffset + mPositions[t->mEdge[0].mStartIdx]); + RVec3 p2 = cDrawScale * (mOffset + mPositions[t->mEdge[1].mStartIdx]); + RVec3 p3 = cDrawScale * (mOffset + mPositions[t->mEdge[2].mStartIdx]); + + // Draw triangle + DebugRenderer::sInstance->DrawTriangle(p1, p2, p3, Color::sGetDistinctColor(t->mIteration)); + DebugRenderer::sInstance->DrawWireTriangle(p1, p2, p3, Color::sGrey); + + // Draw normal + RVec3 centroid = cDrawScale * (mOffset + t->mCentroid); + float len = t->mNormal.Length(); + if (len > 0.0f) + DebugRenderer::sInstance->DrawArrow(centroid, centroid + t->mNormal / len, Color::sDarkGreen, 0.01f); + } + + // Determine max position + float min_x = FLT_MAX; + float max_x = -FLT_MAX; + for (Vec3 p : mPositions) + { + min_x = min(min_x, p.GetX()); + max_x = max(max_x, p.GetX()); + } + + // Offset to the right + mOffset += Vec3(max_x - min_x + 0.5f, 0.0f, 0.0f); + } + + /// Draw a label to indicate the next stage in the algorithm + void DrawLabel(const string_view &inText) + { + DebugRenderer::sInstance->DrawText3D(cDrawScale * mOffset, inText, Color::sWhite, 0.1f * cDrawScale); + + mOffset += Vec3(5.0f, 0.0f, 0.0f); + } + + /// Draw geometry for debugging purposes + void DrawGeometry(const DebugRenderer::GeometryRef &inGeometry, ColorArg inColor) + { + RMat44 origin = RMat44::sScale(Vec3::sReplicate(cDrawScale)) * RMat44::sTranslation(mOffset); + DebugRenderer::sInstance->DrawGeometry(origin, inGeometry->mBounds.Transformed(origin), inGeometry->mBounds.GetExtent().LengthSq(), inColor, inGeometry); + + mOffset += Vec3(inGeometry->mBounds.GetSize().GetX(), 0, 0); + } + + /// Draw a triangle for debugging purposes + void DrawWireTriangle(const Triangle &inTriangle, ColorArg inColor) + { + RVec3 prev = cDrawScale * (mOffset + mPositions[inTriangle.mEdge[2].mStartIdx]); + for (const Edge &edge : inTriangle.mEdge) + { + RVec3 cur = cDrawScale * (mOffset + mPositions[edge.mStartIdx]); + DebugRenderer::sInstance->DrawArrow(prev, cur, inColor, 0.01f); + prev = cur; + } + } + + /// Draw a marker for debugging purposes + void DrawMarker(Vec3Arg inPosition, ColorArg inColor, float inSize) + { + DebugRenderer::sInstance->DrawMarker(cDrawScale * (mOffset + inPosition), inColor, inSize); + } + + /// Draw an arrow for debugging purposes + void DrawArrow(Vec3Arg inFrom, Vec3Arg inTo, ColorArg inColor, float inArrowSize) + { + DebugRenderer::sInstance->DrawArrow(cDrawScale * (mOffset + inFrom), cDrawScale * (mOffset + inTo), inColor, inArrowSize); + } +#endif + +private: + TriangleFactory mFactory; ///< Factory to create new triangles and remove old ones + const Points & mPositions; ///< List of positions (some of them are part of the hull) + TriangleQueue mTriangleQueue; ///< List of triangles that are part of the hull that still need to be checked (if !mRemoved) + +#if defined(JPH_EPA_CONVEX_BUILDER_VALIDATE) || defined(JPH_EPA_CONVEX_BUILDER_DRAW) + Triangles mTriangles; ///< The list of all triangles in this hull (for debug purposes) +#endif + +#ifdef JPH_EPA_CONVEX_BUILDER_DRAW + int mIteration; ///< Number of iterations we've had so far (for debug purposes) + RVec3 mOffset; ///< Offset to use for state drawing +#endif +}; + +// The determinant that is calculated in the Triangle constructor is really sensitive +// to numerical round off, disable the fmadd instructions to maintain precision. +JPH_PRECISE_MATH_ON + +EPAConvexHullBuilder::Triangle::Triangle(int inIdx0, int inIdx1, int inIdx2, const Vec3 *inPositions) +{ + // Fill in indexes + JPH_ASSERT(inIdx0 != inIdx1 && inIdx0 != inIdx2 && inIdx1 != inIdx2); + mEdge[0].mStartIdx = inIdx0; + mEdge[1].mStartIdx = inIdx1; + mEdge[2].mStartIdx = inIdx2; + + // Clear links + mEdge[0].mNeighbourTriangle = nullptr; + mEdge[1].mNeighbourTriangle = nullptr; + mEdge[2].mNeighbourTriangle = nullptr; + + // Get vertex positions + Vec3 y0 = inPositions[inIdx0]; + Vec3 y1 = inPositions[inIdx1]; + Vec3 y2 = inPositions[inIdx2]; + + // Calculate centroid + mCentroid = (y0 + y1 + y2) / 3.0f; + + // Calculate edges + Vec3 y10 = y1 - y0; + Vec3 y20 = y2 - y0; + Vec3 y21 = y2 - y1; + + // The most accurate normal is calculated by using the two shortest edges + // See: https://box2d.org/posts/2014/01/troublesome-triangle/ + // The difference in normals is most pronounced when one edge is much smaller than the others (in which case the other 2 must have roughly the same length). + // Therefore we can suffice by just picking the shortest from 2 edges and use that with the 3rd edge to calculate the normal. + // We first check which of the edges is shorter. + float y20_dot_y20 = y20.Dot(y20); + float y21_dot_y21 = y21.Dot(y21); + if (y20_dot_y20 < y21_dot_y21) + { + // We select the edges y10 and y20 + mNormal = y10.Cross(y20); + + // Check if triangle is degenerate + float normal_len_sq = mNormal.LengthSq(); + if (normal_len_sq > cMinTriangleArea) + { + // Determine distance between triangle and origin: distance = (centroid - origin) . normal / |normal| + // Note that this way of calculating the closest point is much more accurate than first calculating barycentric coordinates and then calculating the closest + // point based on those coordinates. Note that we preserve the sign of the distance to check on which side the origin is. + float c_dot_n = mCentroid.Dot(mNormal); + mClosestLenSq = abs(c_dot_n) * c_dot_n / normal_len_sq; + + // Calculate closest point to origin using barycentric coordinates: + // + // v = y0 + l0 * (y1 - y0) + l1 * (y2 - y0) + // v . (y1 - y0) = 0 + // v . (y2 - y0) = 0 + // + // Written in matrix form: + // + // | y10.y10 y20.y10 | | l0 | = | -y0.y10 | + // | y10.y20 y20.y20 | | l1 | | -y0.y20 | + // + // (y10 = y1 - y0 etc.) + // + // Cramers rule to invert matrix: + float y10_dot_y10 = y10.LengthSq(); + float y10_dot_y20 = y10.Dot(y20); + float determinant = y10_dot_y10 * y20_dot_y20 - y10_dot_y20 * y10_dot_y20; + if (determinant > 0.0f) // If determinant == 0 then the system is linearly dependent and the triangle is degenerate, since y10.10 * y20.y20 > y10.y20^2 it should also be > 0 + { + float y0_dot_y10 = y0.Dot(y10); + float y0_dot_y20 = y0.Dot(y20); + float l0 = (y10_dot_y20 * y0_dot_y20 - y20_dot_y20 * y0_dot_y10) / determinant; + float l1 = (y10_dot_y20 * y0_dot_y10 - y10_dot_y10 * y0_dot_y20) / determinant; + mLambda[0] = l0; + mLambda[1] = l1; + mLambdaRelativeTo0 = true; + + // Check if closest point is interior to the triangle. For a convex hull which contains the origin each face must contain the origin, but because + // our faces are triangles, we can have multiple coplanar triangles and only 1 will have the origin as an interior point. We want to use this triangle + // to calculate the contact points because it gives the most accurate results, so we will only add these triangles to the priority queue. + if (l0 > -cBarycentricEpsilon && l1 > -cBarycentricEpsilon && l0 + l1 < 1.0f + cBarycentricEpsilon) + mClosestPointInterior = true; + } + } + } + else + { + // We select the edges y10 and y21 + mNormal = y10.Cross(y21); + + // Check if triangle is degenerate + float normal_len_sq = mNormal.LengthSq(); + if (normal_len_sq > cMinTriangleArea) + { + // Again calculate distance between triangle and origin + float c_dot_n = mCentroid.Dot(mNormal); + mClosestLenSq = abs(c_dot_n) * c_dot_n / normal_len_sq; + + // Calculate closest point to origin using barycentric coordinates but this time using y1 as the reference vertex + // + // v = y1 + l0 * (y0 - y1) + l1 * (y2 - y1) + // v . (y0 - y1) = 0 + // v . (y2 - y1) = 0 + // + // Written in matrix form: + // + // | y10.y10 -y21.y10 | | l0 | = | y1.y10 | + // | -y10.y21 y21.y21 | | l1 | | -y1.y21 | + // + // Cramers rule to invert matrix: + float y10_dot_y10 = y10.LengthSq(); + float y10_dot_y21 = y10.Dot(y21); + float determinant = y10_dot_y10 * y21_dot_y21 - y10_dot_y21 * y10_dot_y21; + if (determinant > 0.0f) + { + float y1_dot_y10 = y1.Dot(y10); + float y1_dot_y21 = y1.Dot(y21); + float l0 = (y21_dot_y21 * y1_dot_y10 - y10_dot_y21 * y1_dot_y21) / determinant; + float l1 = (y10_dot_y21 * y1_dot_y10 - y10_dot_y10 * y1_dot_y21) / determinant; + mLambda[0] = l0; + mLambda[1] = l1; + mLambdaRelativeTo0 = false; + + // Again check if the closest point is inside the triangle + if (l0 > -cBarycentricEpsilon && l1 > -cBarycentricEpsilon && l0 + l1 < 1.0f + cBarycentricEpsilon) + mClosestPointInterior = true; + } + } + } +} + +JPH_PRECISE_MATH_OFF + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/EPAPenetrationDepth.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/EPAPenetrationDepth.h new file mode 100644 index 00000000000..ba7b18f7e72 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/EPAPenetrationDepth.h @@ -0,0 +1,546 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include +#include + +//#define JPH_EPA_PENETRATION_DEPTH_DEBUG + +JPH_NAMESPACE_BEGIN + +/// Implementation of Expanding Polytope Algorithm as described in: +/// +/// Proximity Queries and Penetration Depth Computation on 3D Game Objects - Gino van den Bergen +/// +/// The implementation of this algorithm does not completely follow the article, instead of splitting +/// triangles at each edge as in fig. 7 in the article, we build a convex hull (removing any triangles that +/// are facing the new point, thereby avoiding the problem of getting really oblong triangles as mentioned in +/// the article). +/// +/// The algorithm roughly works like: +/// +/// - Start with a simplex of the Minkowski sum (difference) of two objects that was calculated by GJK +/// - This simplex should contain the origin (or else GJK would have reported: no collision) +/// - In cases where the simplex consists of 1 - 3 points, find some extra support points (of the Minkowski sum) to get to at least 4 points +/// - Convert this into a convex hull with non-zero volume (which includes the origin) +/// - A: Calculate the closest point to the origin for all triangles of the hull and take the closest one +/// - Calculate a new support point (of the Minkowski sum) in this direction and add this point to the convex hull +/// - This will remove all faces that are facing the new point and will create new triangles to fill up the hole +/// - Loop to A until no closer point found +/// - The closest point indicates the position / direction of least penetration +class EPAPenetrationDepth +{ +private: + // Typedefs + static constexpr int cMaxPoints = EPAConvexHullBuilder::cMaxPoints; + static constexpr int cMaxPointsToIncludeOriginInHull = 32; + static_assert(cMaxPointsToIncludeOriginInHull < cMaxPoints); + + using Triangle = EPAConvexHullBuilder::Triangle; + using Points = EPAConvexHullBuilder::Points; + + /// The GJK algorithm, used to start the EPA algorithm + GJKClosestPoint mGJK; + + /// A list of support points for the EPA algorithm + class SupportPoints + { + public: + /// List of support points + Points mY; + Vec3 mP[cMaxPoints]; + Vec3 mQ[cMaxPoints]; + + /// Calculate and add new support point to the list of points + template + Vec3 Add(const A &inA, const B &inB, Vec3Arg inDirection, int &outIndex) + { + // Get support point of the minkowski sum A - B + Vec3 p = inA.GetSupport(inDirection); + Vec3 q = inB.GetSupport(-inDirection); + Vec3 w = p - q; + + // Store new point + outIndex = mY.size(); + mY.push_back(w); + mP[outIndex] = p; + mQ[outIndex] = q; + + return w; + } + }; + +public: + /// Return code for GetPenetrationDepthStepGJK + enum class EStatus + { + NotColliding, ///< Returned if the objects don't collide, in this case outPointA/outPointB are invalid + Colliding, ///< Returned if the objects penetrate + Indeterminate ///< Returned if the objects penetrate further than the convex radius. In this case you need to call GetPenetrationDepthStepEPA to get the actual penetration depth. + }; + + /// Calculates penetration depth between two objects, first step of two (the GJK step) + /// + /// @param inAExcludingConvexRadius Object A without convex radius. + /// @param inBExcludingConvexRadius Object B without convex radius. + /// @param inConvexRadiusA Convex radius for A. + /// @param inConvexRadiusB Convex radius for B. + /// @param ioV Pass in previously returned value or (1, 0, 0). On return this value is changed to direction to move B out of collision along the shortest path (magnitude is meaningless). + /// @param inTolerance Minimal distance before A and B are considered colliding. + /// @param outPointA Position on A that has the least amount of penetration. + /// @param outPointB Position on B that has the least amount of penetration. + /// Use |outPointB - outPointA| to get the distance of penetration. + template + EStatus GetPenetrationDepthStepGJK(const AE &inAExcludingConvexRadius, float inConvexRadiusA, const BE &inBExcludingConvexRadius, float inConvexRadiusB, float inTolerance, Vec3 &ioV, Vec3 &outPointA, Vec3 &outPointB) + { + JPH_PROFILE_FUNCTION(); + + // Don't supply a zero ioV, we only want to get points on the hull of the Minkowsky sum and not internal points + JPH_ASSERT(!ioV.IsNearZero()); + + // Get closest points + float combined_radius = inConvexRadiusA + inConvexRadiusB; + float combined_radius_sq = combined_radius * combined_radius; + float closest_points_dist_sq = mGJK.GetClosestPoints(inAExcludingConvexRadius, inBExcludingConvexRadius, inTolerance, combined_radius_sq, ioV, outPointA, outPointB); + if (closest_points_dist_sq > combined_radius_sq) + { + // No collision + return EStatus::NotColliding; + } + if (closest_points_dist_sq > 0.0f) + { + // Collision within convex radius, adjust points for convex radius + float v_len = sqrt(closest_points_dist_sq); // GetClosestPoints function returns |ioV|^2 when return value < FLT_MAX + outPointA += ioV * (inConvexRadiusA / v_len); + outPointB -= ioV * (inConvexRadiusB / v_len); + return EStatus::Colliding; + } + + return EStatus::Indeterminate; + } + + /// Calculates penetration depth between two objects, second step (the EPA step) + /// + /// @param inAIncludingConvexRadius Object A with convex radius + /// @param inBIncludingConvexRadius Object B with convex radius + /// @param inTolerance A factor that determines the accuracy of the result. If the change of the squared distance is less than inTolerance * current_penetration_depth^2 the algorithm will terminate. Should be bigger or equal to FLT_EPSILON. + /// @param outV Direction to move B out of collision along the shortest path (magnitude is meaningless) + /// @param outPointA Position on A that has the least amount of penetration + /// @param outPointB Position on B that has the least amount of penetration + /// Use |outPointB - outPointA| to get the distance of penetration + /// + /// @return False if the objects don't collide, in this case outPointA/outPointB are invalid. + /// True if the objects penetrate + template + bool GetPenetrationDepthStepEPA(const AI &inAIncludingConvexRadius, const BI &inBIncludingConvexRadius, float inTolerance, Vec3 &outV, Vec3 &outPointA, Vec3 &outPointB) + { + JPH_PROFILE_FUNCTION(); + + // Check that the tolerance makes sense (smaller value than this will just result in needless iterations) + JPH_ASSERT(inTolerance >= FLT_EPSILON); + + // Fetch the simplex from GJK algorithm + SupportPoints support_points; + mGJK.GetClosestPointsSimplex(support_points.mY.data(), support_points.mP, support_points.mQ, support_points.mY.GetSizeRef()); + + // Fill up the amount of support points to 4 + switch (support_points.mY.size()) + { + case 1: + { + // 1 vertex, which must be at the origin, which is useless for our purpose + JPH_ASSERT(support_points.mY[0].IsNearZero(1.0e-8f)); + support_points.mY.pop_back(); + + // Add support points in 4 directions to form a tetrahedron around the origin + int p1, p2, p3, p4; + (void)support_points.Add(inAIncludingConvexRadius, inBIncludingConvexRadius, Vec3(0, 1, 0), p1); + (void)support_points.Add(inAIncludingConvexRadius, inBIncludingConvexRadius, Vec3(-1, -1, -1), p2); + (void)support_points.Add(inAIncludingConvexRadius, inBIncludingConvexRadius, Vec3(1, -1, -1), p3); + (void)support_points.Add(inAIncludingConvexRadius, inBIncludingConvexRadius, Vec3(0, -1, 1), p4); + JPH_ASSERT(p1 == 0); + JPH_ASSERT(p2 == 1); + JPH_ASSERT(p3 == 2); + JPH_ASSERT(p4 == 3); + break; + } + + case 2: + { + // Two vertices, create 3 extra by taking perpendicular axis and rotating it around in 120 degree increments + Vec3 axis = (support_points.mY[1] - support_points.mY[0]).Normalized(); + Mat44 rotation = Mat44::sRotation(axis, DegreesToRadians(120.0f)); + Vec3 dir1 = axis.GetNormalizedPerpendicular(); + Vec3 dir2 = rotation * dir1; + Vec3 dir3 = rotation * dir2; + int p1, p2, p3; + (void)support_points.Add(inAIncludingConvexRadius, inBIncludingConvexRadius, dir1, p1); + (void)support_points.Add(inAIncludingConvexRadius, inBIncludingConvexRadius, dir2, p2); + (void)support_points.Add(inAIncludingConvexRadius, inBIncludingConvexRadius, dir3, p3); + JPH_ASSERT(p1 == 2); + JPH_ASSERT(p2 == 3); + JPH_ASSERT(p3 == 4); + break; + } + + case 3: + case 4: + // We already have enough points + break; + } + + // Create hull out of the initial points + JPH_ASSERT(support_points.mY.size() >= 3); + EPAConvexHullBuilder hull(support_points.mY); +#ifdef JPH_EPA_CONVEX_BUILDER_DRAW + hull.DrawLabel("Build initial hull"); +#endif +#ifdef JPH_EPA_PENETRATION_DEPTH_DEBUG + Trace("Init: num_points = %u", (uint)support_points.mY.size()); +#endif + hull.Initialize(0, 1, 2); + for (typename Points::size_type i = 3; i < support_points.mY.size(); ++i) + { + float dist_sq; + Triangle *t = hull.FindFacingTriangle(support_points.mY[i], dist_sq); + if (t != nullptr) + { + EPAConvexHullBuilder::NewTriangles new_triangles; + if (!hull.AddPoint(t, i, FLT_MAX, new_triangles)) + { + // We can't recover from a failure to add a point to the hull because the old triangles have been unlinked already. + // Assume no collision. This can happen if the shapes touch in 1 point (or plane) in which case the hull is degenerate. + return false; + } + } + } + +#ifdef JPH_EPA_CONVEX_BUILDER_DRAW + hull.DrawLabel("Complete hull"); + + // Generate the hull of the Minkowski difference for visualization + MinkowskiDifference diff(inAIncludingConvexRadius, inBIncludingConvexRadius); + DebugRenderer::GeometryRef geometry = DebugRenderer::sInstance->CreateTriangleGeometryForConvex([&diff](Vec3Arg inDirection) { return diff.GetSupport(inDirection); }); + hull.DrawGeometry(geometry, Color::sYellow); + + hull.DrawLabel("Ensure origin in hull"); +#endif + + // Loop until we are sure that the origin is inside the hull + for (;;) + { + // Get the next closest triangle + Triangle *t = hull.PeekClosestTriangleInQueue(); + + // Don't process removed triangles, just free them (because they're in a heap we don't remove them earlier since we would have to rebuild the sorted heap) + if (t->mRemoved) + { + hull.PopClosestTriangleFromQueue(); + + // If we run out of triangles, we couldn't include the origin in the hull so there must be very little penetration and we report no collision. + if (!hull.HasNextTriangle()) + return false; + + hull.FreeTriangle(t); + continue; + } + + // If the closest to the triangle is zero or positive, the origin is in the hull and we can proceed to the main algorithm + if (t->mClosestLenSq >= 0.0f) + break; + +#ifdef JPH_EPA_CONVEX_BUILDER_DRAW + hull.DrawLabel("Next iteration"); +#endif +#ifdef JPH_EPA_PENETRATION_DEPTH_DEBUG + Trace("EncapsulateOrigin: verts = (%d, %d, %d), closest_dist_sq = %g, centroid = (%g, %g, %g), normal = (%g, %g, %g)", + t->mEdge[0].mStartIdx, t->mEdge[1].mStartIdx, t->mEdge[2].mStartIdx, + t->mClosestLenSq, + t->mCentroid.GetX(), t->mCentroid.GetY(), t->mCentroid.GetZ(), + t->mNormal.GetX(), t->mNormal.GetY(), t->mNormal.GetZ()); +#endif + + // Remove the triangle from the queue before we start adding new ones (which may result in a new closest triangle at the front of the queue) + hull.PopClosestTriangleFromQueue(); + + // Add a support point to get the origin inside the hull + int new_index; + Vec3 w = support_points.Add(inAIncludingConvexRadius, inBIncludingConvexRadius, t->mNormal, new_index); + +#ifdef JPH_EPA_CONVEX_BUILDER_DRAW + // Draw the point that we're adding + hull.DrawMarker(w, Color::sRed, 1.0f); + hull.DrawWireTriangle(*t, Color::sRed); + hull.DrawState(); +#endif + + // Add the point to the hull, if we fail we terminate and report no collision + EPAConvexHullBuilder::NewTriangles new_triangles; + if (!t->IsFacing(w) || !hull.AddPoint(t, new_index, FLT_MAX, new_triangles)) + return false; + + // The triangle is facing the support point "w" and can now be safely removed + JPH_ASSERT(t->mRemoved); + hull.FreeTriangle(t); + + // If we run out of triangles or points, we couldn't include the origin in the hull so there must be very little penetration and we report no collision. + if (!hull.HasNextTriangle() || support_points.mY.size() >= cMaxPointsToIncludeOriginInHull) + return false; + } + +#ifdef JPH_EPA_CONVEX_BUILDER_DRAW + hull.DrawLabel("Main algorithm"); +#endif + + // Current closest distance to origin + float closest_dist_sq = FLT_MAX; + + // Remember last good triangle + Triangle *last = nullptr; + + // If we want to flip the penetration depth + bool flip_v_sign = false; + + // Loop until closest point found + do + { + // Get closest triangle to the origin + Triangle *t = hull.PopClosestTriangleFromQueue(); + + // Don't process removed triangles, just free them (because they're in a heap we don't remove them earlier since we would have to rebuild the sorted heap) + if (t->mRemoved) + { + hull.FreeTriangle(t); + continue; + } + +#ifdef JPH_EPA_CONVEX_BUILDER_DRAW + hull.DrawLabel("Next iteration"); +#endif +#ifdef JPH_EPA_PENETRATION_DEPTH_DEBUG + Trace("FindClosest: verts = (%d, %d, %d), closest_len_sq = %g, centroid = (%g, %g, %g), normal = (%g, %g, %g)", + t->mEdge[0].mStartIdx, t->mEdge[1].mStartIdx, t->mEdge[2].mStartIdx, + t->mClosestLenSq, + t->mCentroid.GetX(), t->mCentroid.GetY(), t->mCentroid.GetZ(), + t->mNormal.GetX(), t->mNormal.GetY(), t->mNormal.GetZ()); +#endif + // Check if next triangle is further away than closest point, we've found the closest point + if (t->mClosestLenSq >= closest_dist_sq) + break; + + // Replace last good with this triangle + if (last != nullptr) + hull.FreeTriangle(last); + last = t; + + // Add support point in direction of normal of the plane + // Note that the article uses the closest point between the origin and plane, but this always has the exact same direction as the normal (if the origin is behind the plane) + // and this way we do less calculations and lose less precision + int new_index; + Vec3 w = support_points.Add(inAIncludingConvexRadius, inBIncludingConvexRadius, t->mNormal, new_index); + + // Project w onto the triangle normal + float dot = t->mNormal.Dot(w); + + // Check if we just found a separating axis. This can happen if the shape shrunk by convex radius and then expanded by + // convex radius is bigger then the original shape due to inaccuracies in the shrinking process. + if (dot < 0.0f) + return false; + + // Get the distance squared (along normal) to the support point + float dist_sq = Square(dot) / t->mNormal.LengthSq(); + +#ifdef JPH_EPA_PENETRATION_DEPTH_DEBUG + Trace("FindClosest: w = (%g, %g, %g), dot = %g, dist_sq = %g", + w.GetX(), w.GetY(), w.GetZ(), + dot, dist_sq); +#endif +#ifdef JPH_EPA_CONVEX_BUILDER_DRAW + // Draw the point that we're adding + hull.DrawMarker(w, Color::sPurple, 1.0f); + hull.DrawWireTriangle(*t, Color::sPurple); + hull.DrawState(); +#endif + + // If the error became small enough, we've converged + if (dist_sq - t->mClosestLenSq < t->mClosestLenSq * inTolerance) + { +#ifdef JPH_EPA_PENETRATION_DEPTH_DEBUG + Trace("Converged"); +#endif // JPH_EPA_PENETRATION_DEPTH_DEBUG + break; + } + + // Keep track of the minimum distance + closest_dist_sq = min(closest_dist_sq, dist_sq); + + // If the triangle thinks this point is not front facing, we've reached numerical precision and we're done + if (!t->IsFacing(w)) + { +#ifdef JPH_EPA_PENETRATION_DEPTH_DEBUG + Trace("Not facing triangle"); +#endif // JPH_EPA_PENETRATION_DEPTH_DEBUG + break; + } + + // Add point to hull + EPAConvexHullBuilder::NewTriangles new_triangles; + if (!hull.AddPoint(t, new_index, closest_dist_sq, new_triangles)) + { +#ifdef JPH_EPA_PENETRATION_DEPTH_DEBUG + Trace("Could not add point"); +#endif // JPH_EPA_PENETRATION_DEPTH_DEBUG + break; + } + + // If the hull is starting to form defects then we're reaching numerical precision and we have to stop + bool has_defect = false; + for (const Triangle *nt : new_triangles) + if (nt->IsFacingOrigin()) + { + has_defect = true; + break; + } + if (has_defect) + { +#ifdef JPH_EPA_PENETRATION_DEPTH_DEBUG + Trace("Has defect"); +#endif // JPH_EPA_PENETRATION_DEPTH_DEBUG + // When the hull has defects it is possible that the origin has been classified on the wrong side of the triangle + // so we do an additional check to see if the penetration in the -triangle normal direction is smaller than + // the penetration in the triangle normal direction. If so we must flip the sign of the penetration depth. + Vec3 w2 = inAIncludingConvexRadius.GetSupport(-t->mNormal) - inBIncludingConvexRadius.GetSupport(t->mNormal); + float dot2 = -t->mNormal.Dot(w2); + if (dot2 < dot) + flip_v_sign = true; + break; + } + } + while (hull.HasNextTriangle() && support_points.mY.size() < cMaxPoints); + + // Determine closest points, if last == null it means the hull was a plane so there's no penetration + if (last == nullptr) + return false; + +#ifdef JPH_EPA_CONVEX_BUILDER_DRAW + hull.DrawLabel("Closest found"); + hull.DrawWireTriangle(*last, Color::sWhite); + hull.DrawArrow(last->mCentroid, last->mCentroid + last->mNormal.NormalizedOr(Vec3::sZero()), Color::sWhite, 0.1f); + hull.DrawState(); +#endif + + // Calculate penetration by getting the vector from the origin to the closest point on the triangle: + // distance = (centroid - origin) . normal / |normal|, closest = origin + distance * normal / |normal| + outV = (last->mCentroid.Dot(last->mNormal) / last->mNormal.LengthSq()) * last->mNormal; + + // If penetration is near zero, treat this as a non collision since we cannot find a good normal + if (outV.IsNearZero()) + return false; + + // Check if we have to flip the sign of the penetration depth + if (flip_v_sign) + outV = -outV; + + // Use the barycentric coordinates for the closest point to the origin to find the contact points on A and B + Vec3 p0 = support_points.mP[last->mEdge[0].mStartIdx]; + Vec3 p1 = support_points.mP[last->mEdge[1].mStartIdx]; + Vec3 p2 = support_points.mP[last->mEdge[2].mStartIdx]; + + Vec3 q0 = support_points.mQ[last->mEdge[0].mStartIdx]; + Vec3 q1 = support_points.mQ[last->mEdge[1].mStartIdx]; + Vec3 q2 = support_points.mQ[last->mEdge[2].mStartIdx]; + + if (last->mLambdaRelativeTo0) + { + // y0 was the reference vertex + outPointA = p0 + last->mLambda[0] * (p1 - p0) + last->mLambda[1] * (p2 - p0); + outPointB = q0 + last->mLambda[0] * (q1 - q0) + last->mLambda[1] * (q2 - q0); + } + else + { + // y1 was the reference vertex + outPointA = p1 + last->mLambda[0] * (p0 - p1) + last->mLambda[1] * (p2 - p1); + outPointB = q1 + last->mLambda[0] * (q0 - q1) + last->mLambda[1] * (q2 - q1); + } + + return true; + } + + /// This function combines the GJK and EPA steps and is provided as a convenience function. + /// Note: less performant since you're providing all support functions in one go + /// Note 2: You need to initialize ioV, see documentation at GetPenetrationDepthStepGJK! + template + bool GetPenetrationDepth(const AE &inAExcludingConvexRadius, const AI &inAIncludingConvexRadius, float inConvexRadiusA, const BE &inBExcludingConvexRadius, const BI &inBIncludingConvexRadius, float inConvexRadiusB, float inCollisionToleranceSq, float inPenetrationTolerance, Vec3 &ioV, Vec3 &outPointA, Vec3 &outPointB) + { + // Check result of collision detection + switch (GetPenetrationDepthStepGJK(inAExcludingConvexRadius, inConvexRadiusA, inBExcludingConvexRadius, inConvexRadiusB, inCollisionToleranceSq, ioV, outPointA, outPointB)) + { + case EPAPenetrationDepth::EStatus::Colliding: + return true; + + case EPAPenetrationDepth::EStatus::NotColliding: + return false; + + case EPAPenetrationDepth::EStatus::Indeterminate: + return GetPenetrationDepthStepEPA(inAIncludingConvexRadius, inBIncludingConvexRadius, inPenetrationTolerance, ioV, outPointA, outPointB); + } + + JPH_ASSERT(false); + return false; + } + + /// Test if a cast shape inA moving from inStart to lambda * inStart.GetTranslation() + inDirection where lambda e [0, ioLambda> intersects inB + /// + /// @param inStart Start position and orientation of the convex object + /// @param inDirection Direction of the sweep (ioLambda * inDirection determines length) + /// @param inCollisionTolerance The minimal distance between A and B before they are considered colliding + /// @param inPenetrationTolerance A factor that determines the accuracy of the result. If the change of the squared distance is less than inTolerance * current_penetration_depth^2 the algorithm will terminate. Should be bigger or equal to FLT_EPSILON. + /// @param inA The convex object A, must support the GetSupport(Vec3) function. + /// @param inB The convex object B, must support the GetSupport(Vec3) function. + /// @param inConvexRadiusA The convex radius of A, this will be added on all sides to pad A. + /// @param inConvexRadiusB The convex radius of B, this will be added on all sides to pad B. + /// @param inReturnDeepestPoint If the shapes are initially intersecting this determines if the EPA algorithm will run to find the deepest point + /// @param ioLambda The max fraction along the sweep, on output updated with the actual collision fraction. + /// @param outPointA is the contact point on A + /// @param outPointB is the contact point on B + /// @param outContactNormal is either the contact normal when the objects are touching or the penetration axis when the objects are penetrating at the start of the sweep (pointing from A to B, length will not be 1) + /// + /// @return true if the a hit was found, in which case ioLambda, outPointA, outPointB and outSurfaceNormal are updated. + template + bool CastShape(Mat44Arg inStart, Vec3Arg inDirection, float inCollisionTolerance, float inPenetrationTolerance, const A &inA, const B &inB, float inConvexRadiusA, float inConvexRadiusB, bool inReturnDeepestPoint, float &ioLambda, Vec3 &outPointA, Vec3 &outPointB, Vec3 &outContactNormal) + { + // First determine if there's a collision at all + if (!mGJK.CastShape(inStart, inDirection, inCollisionTolerance, inA, inB, inConvexRadiusA, inConvexRadiusB, ioLambda, outPointA, outPointB, outContactNormal)) + return false; + + // When our contact normal is too small, we don't have an accurate result + bool contact_normal_invalid = outContactNormal.IsNearZero(Square(inCollisionTolerance)); + + if (inReturnDeepestPoint + && ioLambda == 0.0f // Only when lambda = 0 we can have the bodies overlap + && (inConvexRadiusA + inConvexRadiusB == 0.0f // When no convex radius was provided we can never trust contact points at lambda = 0 + || contact_normal_invalid)) + { + // If we're initially intersecting, we need to run the EPA algorithm in order to find the deepest contact point + AddConvexRadius add_convex_a(inA, inConvexRadiusA); + AddConvexRadius add_convex_b(inB, inConvexRadiusB); + TransformedConvexObject> transformed_a(inStart, add_convex_a); + if (!GetPenetrationDepthStepEPA(transformed_a, add_convex_b, inPenetrationTolerance, outContactNormal, outPointA, outPointB)) + return false; + } + else if (contact_normal_invalid) + { + // If we weren't able to calculate a contact normal, use the cast direction instead + outContactNormal = inDirection; + } + + return true; + } +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/Ellipse.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/Ellipse.h new file mode 100644 index 00000000000..bfa508faae8 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/Ellipse.h @@ -0,0 +1,77 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +/// Ellipse centered around the origin +/// @see https://en.wikipedia.org/wiki/Ellipse +class Ellipse +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Construct ellipse with radius A along the X-axis and B along the Y-axis + Ellipse(float inA, float inB) : mA(inA), mB(inB) { JPH_ASSERT(inA > 0.0f); JPH_ASSERT(inB > 0.0f); } + + /// Check if inPoint is inside the ellipse + bool IsInside(const Float2 &inPoint) const + { + return Square(inPoint.x / mA) + Square(inPoint.y / mB) <= 1.0f; + } + + /// Get the closest point on the ellipse to inPoint + /// Assumes inPoint is outside the ellipse + /// @see Rotation Joint Limits in Quaternion Space by Gino van den Bergen, section 10.1 in Game Engine Gems 3. + Float2 GetClosestPoint(const Float2 &inPoint) const + { + float a_sq = Square(mA); + float b_sq = Square(mB); + + // Equation of ellipse: f(x, y) = (x/a)^2 + (y/b)^2 - 1 = 0 [1] + // Normal on surface: (df/dx, df/dy) = (2 x / a^2, 2 y / b^2) + // Closest point (x', y') on ellipse to point (x, y): (x', y') + t (x / a^2, y / b^2) = (x, y) + // <=> (x', y') = (a^2 x / (t + a^2), b^2 y / (t + b^2)) + // Requiring point to be on ellipse (substituting into [1]): g(t) = (a x / (t + a^2))^2 + (b y / (t + b^2))^2 - 1 = 0 + + // Newton raphson iteration, starting at t = 0 + float t = 0.0f; + for (;;) + { + // Calculate g(t) + float t_plus_a_sq = t + a_sq; + float t_plus_b_sq = t + b_sq; + float gt = Square(mA * inPoint.x / t_plus_a_sq) + Square(mB * inPoint.y / t_plus_b_sq) - 1.0f; + + // Check if g(t) it is close enough to zero + if (abs(gt) < 1.0e-6f) + return Float2(a_sq * inPoint.x / t_plus_a_sq, b_sq * inPoint.y / t_plus_b_sq); + + // Get derivative dg/dt = g'(t) = -2 (b^2 y^2 / (t + b^2)^3 + a^2 x^2 / (t + a^2)^3) + float gt_accent = -2.0f * + (a_sq * Square(inPoint.x) / Cubed(t_plus_a_sq) + + b_sq * Square(inPoint.y) / Cubed(t_plus_b_sq)); + + // Calculate t for next iteration: tn+1 = tn - g(t) / g'(t) + float tn = t - gt / gt_accent; + t = tn; + } + } + + /// Get normal at point inPoint (non-normalized vector) + Float2 GetNormal(const Float2 &inPoint) const + { + // Calculated by [d/dx f(x, y), d/dy f(x, y)], where f(x, y) is the ellipse equation from above + return Float2(inPoint.x / Square(mA), inPoint.y / Square(mB)); + } + +private: + float mA; ///< Radius along X-axis + float mB; ///< Radius along Y-axis +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/GJKClosestPoint.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/GJKClosestPoint.h new file mode 100644 index 00000000000..2be8fec9f75 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/GJKClosestPoint.h @@ -0,0 +1,952 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include +#include + +//#define JPH_GJK_DEBUG +#ifdef JPH_GJK_DEBUG + #include + #include +#endif + +JPH_NAMESPACE_BEGIN + +/// Convex vs convex collision detection +/// Based on: A Fast and Robust GJK Implementation for Collision Detection of Convex Objects - Gino van den Bergen +class GJKClosestPoint : public NonCopyable +{ +private: + /// Get new closest point to origin given simplex mY of mNumPoints points + /// + /// @param inPrevVLenSq Length of |outV|^2 from the previous iteration, used as a maximum value when selecting a new closest point. + /// @param outV Closest point + /// @param outVLenSq |outV|^2 + /// @param outSet Set of points that form the new simplex closest to the origin (bit 1 = mY[0], bit 2 = mY[1], ...) + /// + /// If LastPointPartOfClosestFeature is true then the last point added will be assumed to be part of the closest feature and the function will do less work. + /// + /// @return True if new closest point was found. + /// False if the function failed, in this case the output variables are not modified + template + bool GetClosest(float inPrevVLenSq, Vec3 &outV, float &outVLenSq, uint32 &outSet) const + { +#ifdef JPH_GJK_DEBUG + for (int i = 0; i < mNumPoints; ++i) + Trace("y[%d] = [%s], |y[%d]| = %g", i, ConvertToString(mY[i]).c_str(), i, (double)mY[i].Length()); +#endif + + uint32 set; + Vec3 v; + + switch (mNumPoints) + { + case 1: + // Single point + set = 0b0001; + v = mY[0]; + break; + + case 2: + // Line segment + v = ClosestPoint::GetClosestPointOnLine(mY[0], mY[1], set); + break; + + case 3: + // Triangle + v = ClosestPoint::GetClosestPointOnTriangle(mY[0], mY[1], mY[2], set); + break; + + case 4: + // Tetrahedron + v = ClosestPoint::GetClosestPointOnTetrahedron(mY[0], mY[1], mY[2], mY[3], set); + break; + + default: + JPH_ASSERT(false); + return false; + } + +#ifdef JPH_GJK_DEBUG + Trace("GetClosest: set = 0b%s, v = [%s], |v| = %g", NibbleToBinary(set), ConvertToString(v).c_str(), (double)v.Length()); +#endif + + float v_len_sq = v.LengthSq(); + if (v_len_sq < inPrevVLenSq) // Note, comparison order important: If v_len_sq is NaN then this expression will be false so we will return false + { + // Return closest point + outV = v; + outVLenSq = v_len_sq; + outSet = set; + return true; + } + + // No better match found +#ifdef JPH_GJK_DEBUG + Trace("New closer point is further away, failed to converge"); +#endif + return false; + } + + // Get max(|Y_0|^2 .. |Y_n|^2) + float GetMaxYLengthSq() const + { + float y_len_sq = mY[0].LengthSq(); + for (int i = 1; i < mNumPoints; ++i) + y_len_sq = max(y_len_sq, mY[i].LengthSq()); + return y_len_sq; + } + + // Remove points that are not in the set, only updates mY + void UpdatePointSetY(uint32 inSet) + { + int num_points = 0; + for (int i = 0; i < mNumPoints; ++i) + if ((inSet & (1 << i)) != 0) + { + mY[num_points] = mY[i]; + ++num_points; + } + mNumPoints = num_points; + } + + // GCC 11.3 thinks the assignments to mP, mQ and mY below may use uninitialized variables + JPH_SUPPRESS_WARNING_PUSH + JPH_GCC_SUPPRESS_WARNING("-Wmaybe-uninitialized") + + // Remove points that are not in the set, only updates mP + void UpdatePointSetP(uint32 inSet) + { + int num_points = 0; + for (int i = 0; i < mNumPoints; ++i) + if ((inSet & (1 << i)) != 0) + { + mP[num_points] = mP[i]; + ++num_points; + } + mNumPoints = num_points; + } + + // Remove points that are not in the set, only updates mP and mQ + void UpdatePointSetPQ(uint32 inSet) + { + int num_points = 0; + for (int i = 0; i < mNumPoints; ++i) + if ((inSet & (1 << i)) != 0) + { + mP[num_points] = mP[i]; + mQ[num_points] = mQ[i]; + ++num_points; + } + mNumPoints = num_points; + } + + // Remove points that are not in the set, updates mY, mP and mQ + void UpdatePointSetYPQ(uint32 inSet) + { + int num_points = 0; + for (int i = 0; i < mNumPoints; ++i) + if ((inSet & (1 << i)) != 0) + { + mY[num_points] = mY[i]; + mP[num_points] = mP[i]; + mQ[num_points] = mQ[i]; + ++num_points; + } + mNumPoints = num_points; + } + + JPH_SUPPRESS_WARNING_POP + + // Calculate closest points on A and B + void CalculatePointAAndB(Vec3 &outPointA, Vec3 &outPointB) const + { + switch (mNumPoints) + { + case 1: + outPointA = mP[0]; + outPointB = mQ[0]; + break; + + case 2: + { + float u, v; + ClosestPoint::GetBaryCentricCoordinates(mY[0], mY[1], u, v); + outPointA = u * mP[0] + v * mP[1]; + outPointB = u * mQ[0] + v * mQ[1]; + } + break; + + case 3: + { + float u, v, w; + ClosestPoint::GetBaryCentricCoordinates(mY[0], mY[1], mY[2], u, v, w); + outPointA = u * mP[0] + v * mP[1] + w * mP[2]; + outPointB = u * mQ[0] + v * mQ[1] + w * mQ[2]; + } + break; + + case 4: + #ifdef _DEBUG + memset(&outPointA, 0xcd, sizeof(outPointA)); + memset(&outPointB, 0xcd, sizeof(outPointB)); + #endif + break; + } + } + +public: + /// Test if inA and inB intersect + /// + /// @param inA The convex object A, must support the GetSupport(Vec3) function. + /// @param inB The convex object B, must support the GetSupport(Vec3) function. + /// @param inTolerance Minimal distance between objects when the objects are considered to be colliding + /// @param ioV is used as initial separating axis (provide a zero vector if you don't know yet) + /// + /// @return True if they intersect (in which case ioV = (0, 0, 0)). + /// False if they don't intersect in which case ioV is a separating axis in the direction from A to B (magnitude is meaningless) + template + bool Intersects(const A &inA, const B &inB, float inTolerance, Vec3 &ioV) + { + float tolerance_sq = Square(inTolerance); + + // Reset state + mNumPoints = 0; + +#ifdef JPH_GJK_DEBUG + for (int i = 0; i < 4; ++i) + mY[i] = Vec3::sZero(); +#endif + + // Previous length^2 of v + float prev_v_len_sq = FLT_MAX; + + for (;;) + { +#ifdef JPH_GJK_DEBUG + Trace("v = [%s], num_points = %d", ConvertToString(ioV).c_str(), mNumPoints); +#endif + + // Get support points for shape A and B in the direction of v + Vec3 p = inA.GetSupport(ioV); + Vec3 q = inB.GetSupport(-ioV); + + // Get support point of the minkowski sum A - B of v + Vec3 w = p - q; + + // If the support point sA-B(v) is in the opposite direction as v, then we have found a separating axis and there is no intersection + if (ioV.Dot(w) < 0.0f) + { + // Separating axis found +#ifdef JPH_GJK_DEBUG + Trace("Separating axis"); +#endif + return false; + } + + // Store the point for later use + mY[mNumPoints] = w; + ++mNumPoints; + +#ifdef JPH_GJK_DEBUG + Trace("w = [%s]", ConvertToString(w).c_str()); +#endif + + // Determine the new closest point + float v_len_sq; // Length^2 of v + uint32 set; // Set of points that form the new simplex + if (!GetClosest(prev_v_len_sq, ioV, v_len_sq, set)) + return false; + + // If there are 4 points, the origin is inside the tetrahedron and we're done + if (set == 0xf) + { +#ifdef JPH_GJK_DEBUG + Trace("Full simplex"); +#endif + ioV = Vec3::sZero(); + return true; + } + + // If v is very close to zero, we consider this a collision + if (v_len_sq <= tolerance_sq) + { +#ifdef JPH_GJK_DEBUG + Trace("Distance zero"); +#endif + ioV = Vec3::sZero(); + return true; + } + + // If v is very small compared to the length of y, we also consider this a collision + if (v_len_sq <= FLT_EPSILON * GetMaxYLengthSq()) + { +#ifdef JPH_GJK_DEBUG + Trace("Machine precision reached"); +#endif + ioV = Vec3::sZero(); + return true; + } + + // The next separation axis to test is the negative of the closest point of the Minkowski sum to the origin + // Note: This must be done before terminating as converged since the separating axis is -v + ioV = -ioV; + + // If the squared length of v is not changing enough, we've converged and there is no collision + JPH_ASSERT(prev_v_len_sq >= v_len_sq); + if (prev_v_len_sq - v_len_sq <= FLT_EPSILON * prev_v_len_sq) + { + // v is a separating axis +#ifdef JPH_GJK_DEBUG + Trace("Converged"); +#endif + return false; + } + prev_v_len_sq = v_len_sq; + + // Update the points of the simplex + UpdatePointSetY(set); + } + } + + /// Get closest points between inA and inB + /// + /// @param inA The convex object A, must support the GetSupport(Vec3) function. + /// @param inB The convex object B, must support the GetSupport(Vec3) function. + /// @param inTolerance The minimal distance between A and B before the objects are considered colliding and processing is terminated. + /// @param inMaxDistSq The maximum squared distance between A and B before the objects are considered infinitely far away and processing is terminated. + /// @param ioV Initial guess for the separating axis. Start with any non-zero vector if you don't know. + /// If return value is 0, ioV = (0, 0, 0). + /// If the return value is bigger than 0 but smaller than FLT_MAX, ioV will be the separating axis in the direction from A to B and its length the squared distance between A and B. + /// If the return value is FLT_MAX, ioV will be the separating axis in the direction from A to B and the magnitude of the vector is meaningless. + /// @param outPointA , outPointB + /// If the return value is 0 the points are invalid. + /// If the return value is bigger than 0 but smaller than FLT_MAX these will contain the closest point on A and B. + /// If the return value is FLT_MAX the points are invalid. + /// + /// @return The squared distance between A and B or FLT_MAX when they are further away than inMaxDistSq. + template + float GetClosestPoints(const A &inA, const B &inB, float inTolerance, float inMaxDistSq, Vec3 &ioV, Vec3 &outPointA, Vec3 &outPointB) + { + float tolerance_sq = Square(inTolerance); + + // Reset state + mNumPoints = 0; + +#ifdef JPH_GJK_DEBUG + // Generate the hull of the Minkowski difference for visualization + MinkowskiDifference diff(inA, inB); + mGeometry = DebugRenderer::sInstance->CreateTriangleGeometryForConvex([&diff](Vec3Arg inDirection) { return diff.GetSupport(inDirection); }); + + for (int i = 0; i < 4; ++i) + { + mY[i] = Vec3::sZero(); + mP[i] = Vec3::sZero(); + mQ[i] = Vec3::sZero(); + } +#endif + + // Length^2 of v + float v_len_sq = ioV.LengthSq(); + + // Previous length^2 of v + float prev_v_len_sq = FLT_MAX; + + for (;;) + { +#ifdef JPH_GJK_DEBUG + Trace("v = [%s], num_points = %d", ConvertToString(ioV).c_str(), mNumPoints); +#endif + + // Get support points for shape A and B in the direction of v + Vec3 p = inA.GetSupport(ioV); + Vec3 q = inB.GetSupport(-ioV); + + // Get support point of the minkowski sum A - B of v + Vec3 w = p - q; + + float dot = ioV.Dot(w); + +#ifdef JPH_GJK_DEBUG + // Draw -ioV to show the closest point to the origin from the previous simplex + DebugRenderer::sInstance->DrawArrow(mOffset, mOffset - ioV, Color::sOrange, 0.05f); + + // Draw ioV to show where we're probing next + DebugRenderer::sInstance->DrawArrow(mOffset, mOffset + ioV, Color::sCyan, 0.05f); + + // Draw w, the support point + DebugRenderer::sInstance->DrawArrow(mOffset, mOffset + w, Color::sGreen, 0.05f); + DebugRenderer::sInstance->DrawMarker(mOffset + w, Color::sGreen, 1.0f); + + // Draw the simplex and the Minkowski difference around it + DrawState(); +#endif + + // Test if we have a separation of more than inMaxDistSq, in which case we terminate early + if (dot < 0.0f && dot * dot > v_len_sq * inMaxDistSq) + { +#ifdef JPH_GJK_DEBUG + Trace("Distance bigger than max"); +#endif +#ifdef _DEBUG + memset(&outPointA, 0xcd, sizeof(outPointA)); + memset(&outPointB, 0xcd, sizeof(outPointB)); +#endif + return FLT_MAX; + } + + // Store the point for later use + mY[mNumPoints] = w; + mP[mNumPoints] = p; + mQ[mNumPoints] = q; + ++mNumPoints; + +#ifdef JPH_GJK_DEBUG + Trace("w = [%s]", ConvertToString(w).c_str()); +#endif + + uint32 set; + if (!GetClosest(prev_v_len_sq, ioV, v_len_sq, set)) + { + --mNumPoints; // Undo add last point + break; + } + + // If there are 4 points, the origin is inside the tetrahedron and we're done + if (set == 0xf) + { +#ifdef JPH_GJK_DEBUG + Trace("Full simplex"); +#endif + ioV = Vec3::sZero(); + v_len_sq = 0.0f; + break; + } + + // Update the points of the simplex + UpdatePointSetYPQ(set); + + // If v is very close to zero, we consider this a collision + if (v_len_sq <= tolerance_sq) + { +#ifdef JPH_GJK_DEBUG + Trace("Distance zero"); +#endif + ioV = Vec3::sZero(); + v_len_sq = 0.0f; + break; + } + + // If v is very small compared to the length of y, we also consider this a collision +#ifdef JPH_GJK_DEBUG + Trace("Check v small compared to y: %g <= %g", (double)v_len_sq, (double)(FLT_EPSILON * GetMaxYLengthSq())); +#endif + if (v_len_sq <= FLT_EPSILON * GetMaxYLengthSq()) + { +#ifdef JPH_GJK_DEBUG + Trace("Machine precision reached"); +#endif + ioV = Vec3::sZero(); + v_len_sq = 0.0f; + break; + } + + // The next separation axis to test is the negative of the closest point of the Minkowski sum to the origin + // Note: This must be done before terminating as converged since the separating axis is -v + ioV = -ioV; + + // If the squared length of v is not changing enough, we've converged and there is no collision +#ifdef JPH_GJK_DEBUG + Trace("Check v not changing enough: %g <= %g", (double)(prev_v_len_sq - v_len_sq), (double)(FLT_EPSILON * prev_v_len_sq)); +#endif + JPH_ASSERT(prev_v_len_sq >= v_len_sq); + if (prev_v_len_sq - v_len_sq <= FLT_EPSILON * prev_v_len_sq) + { + // v is a separating axis +#ifdef JPH_GJK_DEBUG + Trace("Converged"); +#endif + break; + } + prev_v_len_sq = v_len_sq; + } + + // Get the closest points + CalculatePointAAndB(outPointA, outPointB); + +#ifdef JPH_GJK_DEBUG + Trace("Return: v = [%s], |v| = %g", ConvertToString(ioV).c_str(), (double)ioV.Length()); + + // Draw -ioV to show the closest point to the origin from the previous simplex + DebugRenderer::sInstance->DrawArrow(mOffset, mOffset - ioV, Color::sOrange, 0.05f); + + // Draw the closest points + DebugRenderer::sInstance->DrawMarker(mOffset + outPointA, Color::sGreen, 1.0f); + DebugRenderer::sInstance->DrawMarker(mOffset + outPointB, Color::sPurple, 1.0f); + + // Draw the simplex and the Minkowski difference around it + DrawState(); +#endif + + JPH_ASSERT(ioV.LengthSq() == v_len_sq); + return v_len_sq; + } + + /// Get the resulting simplex after the GetClosestPoints algorithm finishes. + /// If it returned a squared distance of 0, the origin will be contained in the simplex. + void GetClosestPointsSimplex(Vec3 *outY, Vec3 *outP, Vec3 *outQ, uint &outNumPoints) const + { + uint size = sizeof(Vec3) * mNumPoints; + memcpy(outY, mY, size); + memcpy(outP, mP, size); + memcpy(outQ, mQ, size); + outNumPoints = mNumPoints; + } + + /// Test if a ray inRayOrigin + lambda * inRayDirection for lambda e [0, ioLambda> intersects inA + /// + /// Code based upon: Ray Casting against General Convex Objects with Application to Continuous Collision Detection - Gino van den Bergen + /// + /// @param inRayOrigin Origin of the ray + /// @param inRayDirection Direction of the ray (ioLambda * inDirection determines length) + /// @param inTolerance The minimal distance between the ray and A before it is considered colliding + /// @param inA A convex object that has the GetSupport(Vec3) function + /// @param ioLambda The max fraction along the ray, on output updated with the actual collision fraction. + /// + /// @return true if a hit was found, ioLambda is the solution for lambda. + template + bool CastRay(Vec3Arg inRayOrigin, Vec3Arg inRayDirection, float inTolerance, const A &inA, float &ioLambda) + { + float tolerance_sq = Square(inTolerance); + + // Reset state + mNumPoints = 0; + + float lambda = 0.0f; + Vec3 x = inRayOrigin; + Vec3 v = x - inA.GetSupport(Vec3::sZero()); + float v_len_sq = FLT_MAX; + bool allow_restart = false; + + for (;;) + { +#ifdef JPH_GJK_DEBUG + Trace("v = [%s], num_points = %d", ConvertToString(v).c_str(), mNumPoints); +#endif + + // Get new support point + Vec3 p = inA.GetSupport(v); + Vec3 w = x - p; + +#ifdef JPH_GJK_DEBUG + Trace("w = [%s]", ConvertToString(w).c_str()); +#endif + + float v_dot_w = v.Dot(w); +#ifdef JPH_GJK_DEBUG + Trace("v . w = %g", (double)v_dot_w); +#endif + if (v_dot_w > 0.0f) + { + // If ray and normal are in the same direction, we've passed A and there's no collision + float v_dot_r = v.Dot(inRayDirection); +#ifdef JPH_GJK_DEBUG + Trace("v . r = %g", (double)v_dot_r); +#endif + if (v_dot_r >= 0.0f) + return false; + + // Update the lower bound for lambda + float delta = v_dot_w / v_dot_r; + float old_lambda = lambda; + lambda -= delta; +#ifdef JPH_GJK_DEBUG + Trace("lambda = %g, delta = %g", (double)lambda, (double)delta); +#endif + + // If lambda didn't change, we cannot converge any further and we assume a hit + if (old_lambda == lambda) + break; + + // If lambda is bigger or equal than max, we don't have a hit + if (lambda >= ioLambda) + return false; + + // Update x to new closest point on the ray + x = inRayOrigin + lambda * inRayDirection; + + // We've shifted x, so reset v_len_sq so that it is not used as early out for GetClosest + v_len_sq = FLT_MAX; + + // We allow rebuilding the simplex once after x changes because the simplex was built + // for another x and numerical round off builds up as you keep adding points to an + // existing simplex + allow_restart = true; + } + + // Add p to set P: P = P U {p} + mP[mNumPoints] = p; + ++mNumPoints; + + // Calculate Y = {x} - P + for (int i = 0; i < mNumPoints; ++i) + mY[i] = x - mP[i]; + + // Determine the new closest point from Y to origin + uint32 set; // Set of points that form the new simplex + if (!GetClosest(v_len_sq, v, v_len_sq, set)) + { +#ifdef JPH_GJK_DEBUG + Trace("Failed to converge"); +#endif + + // Only allow 1 restart, if we still can't get a closest point + // we're so close that we return this as a hit + if (!allow_restart) + break; + + // If we fail to converge, we start again with the last point as simplex +#ifdef JPH_GJK_DEBUG + Trace("Restarting"); +#endif + allow_restart = false; + mP[0] = p; + mNumPoints = 1; + v = x - p; + v_len_sq = FLT_MAX; + continue; + } + else if (set == 0xf) + { +#ifdef JPH_GJK_DEBUG + Trace("Full simplex"); +#endif + + // We're inside the tetrahedron, we have a hit (verify that length of v is 0) + JPH_ASSERT(v_len_sq == 0.0f); + break; + } + + // Update the points P to form the new simplex + // Note: We're not updating Y as Y will shift with x so we have to calculate it every iteration + UpdatePointSetP(set); + + // Check if x is close enough to inA + if (v_len_sq <= tolerance_sq) + { +#ifdef JPH_GJK_DEBUG + Trace("Converged"); +#endif + break; + } + } + + // Store hit fraction + ioLambda = lambda; + return true; + } + + /// Test if a cast shape inA moving from inStart to lambda * inStart.GetTranslation() + inDirection where lambda e [0, ioLambda> intersects inB + /// + /// @param inStart Start position and orientation of the convex object + /// @param inDirection Direction of the sweep (ioLambda * inDirection determines length) + /// @param inTolerance The minimal distance between A and B before they are considered colliding + /// @param inA The convex object A, must support the GetSupport(Vec3) function. + /// @param inB The convex object B, must support the GetSupport(Vec3) function. + /// @param ioLambda The max fraction along the sweep, on output updated with the actual collision fraction. + /// + /// @return true if a hit was found, ioLambda is the solution for lambda. + template + bool CastShape(Mat44Arg inStart, Vec3Arg inDirection, float inTolerance, const A &inA, const B &inB, float &ioLambda) + { + // Transform the shape to be cast to the starting position + TransformedConvexObject transformed_a(inStart, inA); + + // Calculate the minkowski difference inB - inA + // inA is moving, so we need to add the back side of inB to the front side of inA + MinkowskiDifference difference(inB, transformed_a); + + // Do a raycast against the Minkowski difference + return CastRay(Vec3::sZero(), inDirection, inTolerance, difference, ioLambda); + } + + /// Test if a cast shape inA moving from inStart to lambda * inStart.GetTranslation() + inDirection where lambda e [0, ioLambda> intersects inB + /// + /// @param inStart Start position and orientation of the convex object + /// @param inDirection Direction of the sweep (ioLambda * inDirection determines length) + /// @param inTolerance The minimal distance between A and B before they are considered colliding + /// @param inA The convex object A, must support the GetSupport(Vec3) function. + /// @param inB The convex object B, must support the GetSupport(Vec3) function. + /// @param inConvexRadiusA The convex radius of A, this will be added on all sides to pad A. + /// @param inConvexRadiusB The convex radius of B, this will be added on all sides to pad B. + /// @param ioLambda The max fraction along the sweep, on output updated with the actual collision fraction. + /// @param outPointA is the contact point on A (if outSeparatingAxis is near zero, this may not be not the deepest point) + /// @param outPointB is the contact point on B (if outSeparatingAxis is near zero, this may not be not the deepest point) + /// @param outSeparatingAxis On return this will contain a vector that points from A to B along the smallest distance of separation. + /// The length of this vector indicates the separation of A and B without their convex radius. + /// If it is near zero, the direction may not be accurate as the bodies may overlap when lambda = 0. + /// + /// @return true if a hit was found, ioLambda is the solution for lambda and outPoint and outSeparatingAxis are valid. + template + bool CastShape(Mat44Arg inStart, Vec3Arg inDirection, float inTolerance, const A &inA, const B &inB, float inConvexRadiusA, float inConvexRadiusB, float &ioLambda, Vec3 &outPointA, Vec3 &outPointB, Vec3 &outSeparatingAxis) + { + float tolerance_sq = Square(inTolerance); + + // Calculate how close A and B (without their convex radius) need to be to each other in order for us to consider this a collision + float sum_convex_radius = inConvexRadiusA + inConvexRadiusB; + + // Transform the shape to be cast to the starting position + TransformedConvexObject transformed_a(inStart, inA); + + // Reset state + mNumPoints = 0; + + float lambda = 0.0f; + Vec3 x = Vec3::sZero(); // Since A is already transformed we can start the cast from zero + Vec3 v = -inB.GetSupport(Vec3::sZero()) + transformed_a.GetSupport(Vec3::sZero()); // See CastRay: v = x - inA.GetSupport(Vec3::sZero()) where inA is the Minkowski difference inB - transformed_a (see CastShape above) and x is zero + float v_len_sq = FLT_MAX; + bool allow_restart = false; + + // Keeps track of separating axis of the previous iteration. + // Initialized at zero as we don't know if our first v is actually a separating axis. + Vec3 prev_v = Vec3::sZero(); + + for (;;) + { +#ifdef JPH_GJK_DEBUG + Trace("v = [%s], num_points = %d", ConvertToString(v).c_str(), mNumPoints); +#endif + + // Calculate the minkowski difference inB - inA + // inA is moving, so we need to add the back side of inB to the front side of inA + // Keep the support points on A and B separate so that in the end we can calculate a contact point + Vec3 p = transformed_a.GetSupport(-v); + Vec3 q = inB.GetSupport(v); + Vec3 w = x - (q - p); + +#ifdef JPH_GJK_DEBUG + Trace("w = [%s]", ConvertToString(w).c_str()); +#endif + + // Difference from article to this code: + // We did not include the convex radius in p and q in order to be able to calculate a good separating axis at the end of the algorithm. + // However when moving forward along inDirection we do need to take this into account so that we keep A and B separated by the sum of their convex radii. + // From p we have to subtract: inConvexRadiusA * v / |v| + // To q we have to add: inConvexRadiusB * v / |v| + // This means that to w we have to add: -(inConvexRadiusA + inConvexRadiusB) * v / |v| + // So to v . w we have to add: v . (-(inConvexRadiusA + inConvexRadiusB) * v / |v|) = -(inConvexRadiusA + inConvexRadiusB) * |v| + float v_dot_w = v.Dot(w) - sum_convex_radius * v.Length(); +#ifdef JPH_GJK_DEBUG + Trace("v . w = %g", (double)v_dot_w); +#endif + if (v_dot_w > 0.0f) + { + // If ray and normal are in the same direction, we've passed A and there's no collision + float v_dot_r = v.Dot(inDirection); +#ifdef JPH_GJK_DEBUG + Trace("v . r = %g", (double)v_dot_r); +#endif + if (v_dot_r >= 0.0f) + return false; + + // Update the lower bound for lambda + float delta = v_dot_w / v_dot_r; + float old_lambda = lambda; + lambda -= delta; +#ifdef JPH_GJK_DEBUG + Trace("lambda = %g, delta = %g", (double)lambda, (double)delta); +#endif + + // If lambda didn't change, we cannot converge any further and we assume a hit + if (old_lambda == lambda) + break; + + // If lambda is bigger or equal than max, we don't have a hit + if (lambda >= ioLambda) + return false; + + // Update x to new closest point on the ray + x = lambda * inDirection; + + // We've shifted x, so reset v_len_sq so that it is not used as early out when GetClosest returns false + v_len_sq = FLT_MAX; + + // Now that we've moved, we know that A and B are not intersecting at lambda = 0, so we can update our tolerance to stop iterating + // as soon as A and B are inConvexRadiusA + inConvexRadiusB apart + tolerance_sq = Square(inTolerance + sum_convex_radius); + + // We allow rebuilding the simplex once after x changes because the simplex was built + // for another x and numerical round off builds up as you keep adding points to an + // existing simplex + allow_restart = true; + } + + // Add p to set P, q to set Q: P = P U {p}, Q = Q U {q} + mP[mNumPoints] = p; + mQ[mNumPoints] = q; + ++mNumPoints; + + // Calculate Y = {x} - (Q - P) + for (int i = 0; i < mNumPoints; ++i) + mY[i] = x - (mQ[i] - mP[i]); + + // Determine the new closest point from Y to origin + uint32 set; // Set of points that form the new simplex + if (!GetClosest(v_len_sq, v, v_len_sq, set)) + { +#ifdef JPH_GJK_DEBUG + Trace("Failed to converge"); +#endif + + // Only allow 1 restart, if we still can't get a closest point + // we're so close that we return this as a hit + if (!allow_restart) + break; + + // If we fail to converge, we start again with the last point as simplex +#ifdef JPH_GJK_DEBUG + Trace("Restarting"); +#endif + allow_restart = false; + mP[0] = p; + mQ[0] = q; + mNumPoints = 1; + v = x - q; + v_len_sq = FLT_MAX; + continue; + } + else if (set == 0xf) + { +#ifdef JPH_GJK_DEBUG + Trace("Full simplex"); +#endif + + // We're inside the tetrahedron, we have a hit (verify that length of v is 0) + JPH_ASSERT(v_len_sq == 0.0f); + break; + } + + // Update the points P and Q to form the new simplex + // Note: We're not updating Y as Y will shift with x so we have to calculate it every iteration + UpdatePointSetPQ(set); + + // Check if A and B are touching according to our tolerance + if (v_len_sq <= tolerance_sq) + { +#ifdef JPH_GJK_DEBUG + Trace("Converged"); +#endif + break; + } + + // Store our v to return as separating axis + prev_v = v; + } + + // Calculate Y = {x} - (Q - P) again so we can calculate the contact points + for (int i = 0; i < mNumPoints; ++i) + mY[i] = x - (mQ[i] - mP[i]); + + // Calculate the offset we need to apply to A and B to correct for the convex radius + Vec3 normalized_v = v.NormalizedOr(Vec3::sZero()); + Vec3 convex_radius_a = inConvexRadiusA * normalized_v; + Vec3 convex_radius_b = inConvexRadiusB * normalized_v; + + // Get the contact point + // Note that A and B will coincide when lambda > 0. In this case we calculate only B as it is more accurate as it contains less terms. + switch (mNumPoints) + { + case 1: + outPointB = mQ[0] + convex_radius_b; + outPointA = lambda > 0.0f? outPointB : mP[0] - convex_radius_a; + break; + + case 2: + { + float bu, bv; + ClosestPoint::GetBaryCentricCoordinates(mY[0], mY[1], bu, bv); + outPointB = bu * mQ[0] + bv * mQ[1] + convex_radius_b; + outPointA = lambda > 0.0f? outPointB : bu * mP[0] + bv * mP[1] - convex_radius_a; + } + break; + + case 3: + case 4: // A full simplex, we can't properly determine a contact point! As contact point we take the closest point of the previous iteration. + { + float bu, bv, bw; + ClosestPoint::GetBaryCentricCoordinates(mY[0], mY[1], mY[2], bu, bv, bw); + outPointB = bu * mQ[0] + bv * mQ[1] + bw * mQ[2] + convex_radius_b; + outPointA = lambda > 0.0f? outPointB : bu * mP[0] + bv * mP[1] + bw * mP[2] - convex_radius_a; + } + break; + } + + // Store separating axis, in case we have a convex radius we can just return v, + // otherwise v will be very small and we resort to returning previous v as an approximation. + outSeparatingAxis = sum_convex_radius > 0.0f? -v : -prev_v; + + // Store hit fraction + ioLambda = lambda; + return true; + } + +private: +#ifdef JPH_GJK_DEBUG + /// Draw state of algorithm + void DrawState() + { + RMat44 origin = RMat44::sTranslation(mOffset); + + // Draw origin + DebugRenderer::sInstance->DrawCoordinateSystem(origin, 1.0f); + + // Draw the hull + DebugRenderer::sInstance->DrawGeometry(origin, mGeometry->mBounds.Transformed(origin), mGeometry->mBounds.GetExtent().LengthSq(), Color::sYellow, mGeometry); + + // Draw Y + for (int i = 0; i < mNumPoints; ++i) + { + // Draw support point + RVec3 y_i = origin * mY[i]; + DebugRenderer::sInstance->DrawMarker(y_i, Color::sRed, 1.0f); + for (int j = i + 1; j < mNumPoints; ++j) + { + // Draw edge + RVec3 y_j = origin * mY[j]; + DebugRenderer::sInstance->DrawLine(y_i, y_j, Color::sRed); + for (int k = j + 1; k < mNumPoints; ++k) + { + // Make sure triangle faces the origin + RVec3 y_k = origin * mY[k]; + RVec3 center = (y_i + y_j + y_k) / Real(3); + RVec3 normal = (y_j - y_i).Cross(y_k - y_i); + if (normal.Dot(center) < Real(0)) + DebugRenderer::sInstance->DrawTriangle(y_i, y_j, y_k, Color::sLightGrey); + else + DebugRenderer::sInstance->DrawTriangle(y_i, y_k, y_j, Color::sLightGrey); + } + } + } + + // Offset to the right + mOffset += Vec3(mGeometry->mBounds.GetSize().GetX() + 2.0f, 0, 0); + } +#endif // JPH_GJK_DEBUG + + Vec3 mY[4]; ///< Support points on A - B + Vec3 mP[4]; ///< Support point on A + Vec3 mQ[4]; ///< Support point on B + int mNumPoints = 0; ///< Number of points in mY, mP and mQ that are valid + +#ifdef JPH_GJK_DEBUG + DebugRenderer::GeometryRef mGeometry; ///< A visualization of the minkowski difference for state drawing + RVec3 mOffset = RVec3::sZero(); ///< Offset to use for state drawing +#endif +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/IndexedTriangle.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/IndexedTriangle.h new file mode 100644 index 00000000000..0275fccade0 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/IndexedTriangle.h @@ -0,0 +1,115 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +/// Triangle with 32-bit indices +class IndexedTriangleNoMaterial +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Constructor + IndexedTriangleNoMaterial() = default; + constexpr IndexedTriangleNoMaterial(uint32 inI1, uint32 inI2, uint32 inI3) : mIdx { inI1, inI2, inI3 } { } + + /// Check if two triangles are identical + bool operator == (const IndexedTriangleNoMaterial &inRHS) const + { + return mIdx[0] == inRHS.mIdx[0] && mIdx[1] == inRHS.mIdx[1] && mIdx[2] == inRHS.mIdx[2]; + } + + /// Check if two triangles are equivalent (using the same vertices) + bool IsEquivalent(const IndexedTriangleNoMaterial &inRHS) const + { + return (mIdx[0] == inRHS.mIdx[0] && mIdx[1] == inRHS.mIdx[1] && mIdx[2] == inRHS.mIdx[2]) + || (mIdx[0] == inRHS.mIdx[1] && mIdx[1] == inRHS.mIdx[2] && mIdx[2] == inRHS.mIdx[0]) + || (mIdx[0] == inRHS.mIdx[2] && mIdx[1] == inRHS.mIdx[0] && mIdx[2] == inRHS.mIdx[1]); + } + + /// Check if two triangles are opposite (using the same vertices but in opposing order) + bool IsOpposite(const IndexedTriangleNoMaterial &inRHS) const + { + return (mIdx[0] == inRHS.mIdx[0] && mIdx[1] == inRHS.mIdx[2] && mIdx[2] == inRHS.mIdx[1]) + || (mIdx[0] == inRHS.mIdx[1] && mIdx[1] == inRHS.mIdx[0] && mIdx[2] == inRHS.mIdx[2]) + || (mIdx[0] == inRHS.mIdx[2] && mIdx[1] == inRHS.mIdx[1] && mIdx[2] == inRHS.mIdx[0]); + } + + /// Check if triangle is degenerate + bool IsDegenerate(const VertexList &inVertices) const + { + Vec3 v0(inVertices[mIdx[0]]); + Vec3 v1(inVertices[mIdx[1]]); + Vec3 v2(inVertices[mIdx[2]]); + + return (v1 - v0).Cross(v2 - v0).IsNearZero(); + } + + /// Rotate the vertices so that the second vertex becomes first etc. This does not change the represented triangle. + void Rotate() + { + uint32 tmp = mIdx[0]; + mIdx[0] = mIdx[1]; + mIdx[1] = mIdx[2]; + mIdx[2] = tmp; + } + + /// Get center of triangle + Vec3 GetCentroid(const VertexList &inVertices) const + { + return (Vec3(inVertices[mIdx[0]]) + Vec3(inVertices[mIdx[1]]) + Vec3(inVertices[mIdx[2]])) / 3.0f; + } + + uint32 mIdx[3]; +}; + +/// Triangle with 32-bit indices and material index +class IndexedTriangle : public IndexedTriangleNoMaterial +{ +public: + using IndexedTriangleNoMaterial::IndexedTriangleNoMaterial; + + /// Constructor + constexpr IndexedTriangle(uint32 inI1, uint32 inI2, uint32 inI3, uint32 inMaterialIndex) : IndexedTriangleNoMaterial(inI1, inI2, inI3), mMaterialIndex(inMaterialIndex) { } + + /// Check if two triangles are identical + bool operator == (const IndexedTriangle &inRHS) const + { + return mMaterialIndex == inRHS.mMaterialIndex && IndexedTriangleNoMaterial::operator==(inRHS); + } + + /// Rotate the vertices so that the lowest vertex becomes the first. This does not change the represented triangle. + IndexedTriangle GetLowestIndexFirst() const + { + if (mIdx[0] < mIdx[1]) + { + if (mIdx[0] < mIdx[2]) + return IndexedTriangle(mIdx[0], mIdx[1], mIdx[2], mMaterialIndex); // 0 is smallest + else + return IndexedTriangle(mIdx[2], mIdx[0], mIdx[1], mMaterialIndex); // 2 is smallest + } + else + { + if (mIdx[1] < mIdx[2]) + return IndexedTriangle(mIdx[1], mIdx[2], mIdx[0], mMaterialIndex); // 1 is smallest + else + return IndexedTriangle(mIdx[2], mIdx[0], mIdx[1], mMaterialIndex); // 2 is smallest + } + } + + uint32 mMaterialIndex = 0; +}; + +using IndexedTriangleNoMaterialList = Array; +using IndexedTriangleList = Array; + +JPH_NAMESPACE_END + +// Create a std::hash for IndexedTriangleNoMaterial and IndexedTriangle +JPH_MAKE_HASHABLE(JPH::IndexedTriangleNoMaterial, t.mIdx[0], t.mIdx[1], t.mIdx[2]) +JPH_MAKE_HASHABLE(JPH::IndexedTriangle, t.mIdx[0], t.mIdx[1], t.mIdx[2], t.mMaterialIndex) diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/Indexify.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/Indexify.cpp new file mode 100644 index 00000000000..ac0ff90a580 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/Indexify.cpp @@ -0,0 +1,218 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include + +JPH_NAMESPACE_BEGIN + +static JPH_INLINE const Float3 &sIndexifyGetFloat3(const TriangleList &inTriangles, uint32 inVertexIndex) +{ + return inTriangles[inVertexIndex / 3].mV[inVertexIndex % 3]; +} + +static JPH_INLINE Vec3 sIndexifyGetVec3(const TriangleList &inTriangles, uint32 inVertexIndex) +{ + return Vec3::sLoadFloat3Unsafe(sIndexifyGetFloat3(inTriangles, inVertexIndex)); +} + +static void sIndexifyVerticesBruteForce(const TriangleList &inTriangles, const uint32 *inVertexIndices, const uint32 *inVertexIndicesEnd, Array &ioWeldedVertices, float inVertexWeldDistance) +{ + float weld_dist_sq = Square(inVertexWeldDistance); + + // Compare every vertex + for (const uint32 *v1_idx = inVertexIndices; v1_idx < inVertexIndicesEnd; ++v1_idx) + { + Vec3 v1 = sIndexifyGetVec3(inTriangles, *v1_idx); + + // with every other vertex... + for (const uint32 *v2_idx = v1_idx + 1; v2_idx < inVertexIndicesEnd; ++v2_idx) + { + Vec3 v2 = sIndexifyGetVec3(inTriangles, *v2_idx); + + // If they're weldable + if ((v2 - v1).LengthSq() <= weld_dist_sq) + { + // Find the lowest indices both indices link to + uint32 idx1 = *v1_idx; + for (;;) + { + uint32 new_idx1 = ioWeldedVertices[idx1]; + if (new_idx1 >= idx1) + break; + idx1 = new_idx1; + } + uint32 idx2 = *v2_idx; + for (;;) + { + uint32 new_idx2 = ioWeldedVertices[idx2]; + if (new_idx2 >= idx2) + break; + idx2 = new_idx2; + } + + // Order the vertices + uint32 lowest = min(idx1, idx2); + uint32 highest = max(idx1, idx2); + + // Link highest to lowest + ioWeldedVertices[highest] = lowest; + + // Also update the vertices we started from to avoid creating long chains + ioWeldedVertices[*v1_idx] = lowest; + ioWeldedVertices[*v2_idx] = lowest; + break; + } + } + } +} + +static void sIndexifyVerticesRecursively(const TriangleList &inTriangles, uint32 *ioVertexIndices, uint inNumVertices, uint32 *ioScratch, Array &ioWeldedVertices, float inVertexWeldDistance, uint inMaxRecursion) +{ + // Check if we have few enough vertices to do a brute force search + // Or if we've recursed too deep (this means we chipped off a few vertices each iteration because all points are very close) + if (inNumVertices <= 8 || inMaxRecursion == 0) + { + sIndexifyVerticesBruteForce(inTriangles, ioVertexIndices, ioVertexIndices + inNumVertices, ioWeldedVertices, inVertexWeldDistance); + return; + } + + // Calculate bounds + AABox bounds; + for (const uint32 *v = ioVertexIndices, *v_end = ioVertexIndices + inNumVertices; v < v_end; ++v) + bounds.Encapsulate(sIndexifyGetVec3(inTriangles, *v)); + + // Determine split plane + int split_axis = bounds.GetExtent().GetHighestComponentIndex(); + float split_value = bounds.GetCenter()[split_axis]; + + // Partition vertices + uint32 *v_read = ioVertexIndices, *v_write = ioVertexIndices, *v_end = ioVertexIndices + inNumVertices; + uint32 *scratch = ioScratch; + while (v_read < v_end) + { + // Calculate distance to plane + float distance_to_split_plane = sIndexifyGetFloat3(inTriangles, *v_read)[split_axis] - split_value; + if (distance_to_split_plane < -inVertexWeldDistance) + { + // Vertex is on the right side + *v_write = *v_read; + ++v_read; + ++v_write; + } + else if (distance_to_split_plane > inVertexWeldDistance) + { + // Vertex is on the wrong side, swap with the last vertex + --v_end; + swap(*v_read, *v_end); + } + else + { + // Vertex is too close to the split plane, it goes on both sides + *scratch++ = *v_read++; + } + } + + // Check if we made any progress + uint num_vertices_on_both_sides = (uint)(scratch - ioScratch); + if (num_vertices_on_both_sides == inNumVertices) + { + sIndexifyVerticesBruteForce(inTriangles, ioVertexIndices, ioVertexIndices + inNumVertices, ioWeldedVertices, inVertexWeldDistance); + return; + } + + // Calculate how we classified the vertices + uint num_vertices_left = (uint)(v_write - ioVertexIndices); + uint num_vertices_right = (uint)(ioVertexIndices + inNumVertices - v_end); + JPH_ASSERT(num_vertices_left + num_vertices_right + num_vertices_on_both_sides == inNumVertices); + memcpy(v_write, ioScratch, num_vertices_on_both_sides * sizeof(uint32)); + + // Recurse + uint max_recursion = inMaxRecursion - 1; + sIndexifyVerticesRecursively(inTriangles, ioVertexIndices, num_vertices_left + num_vertices_on_both_sides, ioScratch, ioWeldedVertices, inVertexWeldDistance, max_recursion); + sIndexifyVerticesRecursively(inTriangles, ioVertexIndices + num_vertices_left, num_vertices_right + num_vertices_on_both_sides, ioScratch, ioWeldedVertices, inVertexWeldDistance, max_recursion); +} + +void Indexify(const TriangleList &inTriangles, VertexList &outVertices, IndexedTriangleList &outTriangles, float inVertexWeldDistance) +{ + uint num_triangles = (uint)inTriangles.size(); + uint num_vertices = num_triangles * 3; + + // Create a list of all vertex indices + Array vertex_indices; + vertex_indices.resize(num_vertices); + for (uint i = 0; i < num_vertices; ++i) + vertex_indices[i] = i; + + // Link each vertex to itself + Array welded_vertices; + welded_vertices.resize(num_vertices); + for (uint i = 0; i < num_vertices; ++i) + welded_vertices[i] = i; + + // A scope to free memory used by the scratch array + { + // Some scratch memory, used for the vertices that fall in both partitions + Array scratch; + scratch.resize(num_vertices); + + // Recursively split the vertices + sIndexifyVerticesRecursively(inTriangles, vertex_indices.data(), num_vertices, scratch.data(), welded_vertices, inVertexWeldDistance, 32); + } + + // Do a pass to complete the welding, linking each vertex to the vertex it is welded to + // (and since we're going from 0 to N we can be sure that the vertex we're linking to is already linked to the lowest vertex) + uint num_resulting_vertices = 0; + for (uint i = 0; i < num_vertices; ++i) + { + JPH_ASSERT(welded_vertices[welded_vertices[i]] <= welded_vertices[i]); + welded_vertices[i] = welded_vertices[welded_vertices[i]]; + if (welded_vertices[i] == i) + ++num_resulting_vertices; + } + + // Collect the vertices + outVertices.clear(); + outVertices.reserve(num_resulting_vertices); + for (uint i = 0; i < num_vertices; ++i) + if (welded_vertices[i] == i) + { + // New vertex + welded_vertices[i] = (uint32)outVertices.size(); + outVertices.push_back(sIndexifyGetFloat3(inTriangles, i)); + } + else + { + // Reused vertex, remap index + welded_vertices[i] = welded_vertices[welded_vertices[i]]; + } + + // Create indexed triangles + outTriangles.clear(); + outTriangles.reserve(num_triangles); + for (uint t = 0; t < num_triangles; ++t) + { + IndexedTriangle it; + it.mMaterialIndex = inTriangles[t].mMaterialIndex; + for (int v = 0; v < 3; ++v) + it.mIdx[v] = welded_vertices[t * 3 + v]; + if (!it.IsDegenerate(outVertices)) + outTriangles.push_back(it); + } +} + +void Deindexify(const VertexList &inVertices, const IndexedTriangleList &inTriangles, TriangleList &outTriangles) +{ + outTriangles.resize(inTriangles.size()); + for (size_t t = 0; t < inTriangles.size(); ++t) + { + outTriangles[t].mMaterialIndex = inTriangles[t].mMaterialIndex; + for (int v = 0; v < 3; ++v) + outTriangles[t].mV[v] = inVertices[inTriangles[t].mIdx[v]]; + } +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/Indexify.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/Indexify.h new file mode 100644 index 00000000000..01fb805305d --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/Indexify.h @@ -0,0 +1,19 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +JPH_NAMESPACE_BEGIN + +/// Take a list of triangles and get the unique set of vertices and use them to create indexed triangles. +/// Vertices that are less than inVertexWeldDistance apart will be combined to a single vertex. +JPH_EXPORT void Indexify(const TriangleList &inTriangles, VertexList &outVertices, IndexedTriangleList &outTriangles, float inVertexWeldDistance = 1.0e-4f); + +/// Take a list of indexed triangles and unpack them +JPH_EXPORT void Deindexify(const VertexList &inVertices, const IndexedTriangleList &inTriangles, TriangleList &outTriangles); + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/MortonCode.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/MortonCode.h new file mode 100644 index 00000000000..e750d7e6d1b --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/MortonCode.h @@ -0,0 +1,40 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +class MortonCode +{ +public: + /// First converts a floating point value in the range [0, 1] to a 10 bit fixed point integer. + /// Then expands a 10-bit integer into 30 bits by inserting 2 zeros after each bit. + static uint32 sExpandBits(float inV) + { + JPH_ASSERT(inV >= 0.0f && inV <= 1.0f); + uint32 v = uint32(inV * 1023.0f + 0.5f); + JPH_ASSERT(v < 1024); + v = (v * 0x00010001u) & 0xFF0000FFu; + v = (v * 0x00000101u) & 0x0F00F00Fu; + v = (v * 0x00000011u) & 0xC30C30C3u; + v = (v * 0x00000005u) & 0x49249249u; + return v; + } + + /// Calculate the morton code for inVector, given that all vectors lie in inVectorBounds + static uint32 sGetMortonCode(Vec3Arg inVector, const AABox &inVectorBounds) + { + // Convert to 10 bit fixed point + Vec3 scaled = (inVector - inVectorBounds.mMin) / inVectorBounds.GetSize(); + uint x = sExpandBits(scaled.GetX()); + uint y = sExpandBits(scaled.GetY()); + uint z = sExpandBits(scaled.GetZ()); + return (x << 2) + (y << 1) + z; + } +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/OrientedBox.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/OrientedBox.cpp new file mode 100644 index 00000000000..cbd5ab17f18 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/OrientedBox.cpp @@ -0,0 +1,178 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include + +JPH_NAMESPACE_BEGIN + +bool OrientedBox::Overlaps(const AABox &inBox, float inEpsilon) const +{ + // Taken from: Real Time Collision Detection - Christer Ericson + // Chapter 4.4.1, page 103-105. + // Note that the code is swapped around: A is the aabox and B is the oriented box (this saves us from having to invert the orientation of the oriented box) + + // Convert AABox to center / extent representation + Vec3 a_center = inBox.GetCenter(); + Vec3 a_half_extents = inBox.GetExtent(); + + // Compute rotation matrix expressing b in a's coordinate frame + Mat44 rot(mOrientation.GetColumn4(0), mOrientation.GetColumn4(1), mOrientation.GetColumn4(2), mOrientation.GetColumn4(3) - Vec4(a_center, 0)); + + // Compute common subexpressions. Add in an epsilon term to + // counteract arithmetic errors when two edges are parallel and + // their cross product is (near) null (see text for details) + Vec3 epsilon = Vec3::sReplicate(inEpsilon); + Vec3 abs_r[3] { rot.GetAxisX().Abs() + epsilon, rot.GetAxisY().Abs() + epsilon, rot.GetAxisZ().Abs() + epsilon }; + + // Test axes L = A0, L = A1, L = A2 + float ra, rb; + for (int i = 0; i < 3; i++) + { + ra = a_half_extents[i]; + rb = mHalfExtents[0] * abs_r[0][i] + mHalfExtents[1] * abs_r[1][i] + mHalfExtents[2] * abs_r[2][i]; + if (abs(rot(i, 3)) > ra + rb) return false; + } + + // Test axes L = B0, L = B1, L = B2 + for (int i = 0; i < 3; i++) + { + ra = a_half_extents.Dot(abs_r[i]); + rb = mHalfExtents[i]; + if (abs(rot.GetTranslation().Dot(rot.GetColumn3(i))) > ra + rb) return false; + } + + // Test axis L = A0 x B0 + ra = a_half_extents[1] * abs_r[0][2] + a_half_extents[2] * abs_r[0][1]; + rb = mHalfExtents[1] * abs_r[2][0] + mHalfExtents[2] * abs_r[1][0]; + if (abs(rot(2, 3) * rot(1, 0) - rot(1, 3) * rot(2, 0)) > ra + rb) return false; + + // Test axis L = A0 x B1 + ra = a_half_extents[1] * abs_r[1][2] + a_half_extents[2] * abs_r[1][1]; + rb = mHalfExtents[0] * abs_r[2][0] + mHalfExtents[2] * abs_r[0][0]; + if (abs(rot(2, 3) * rot(1, 1) - rot(1, 3) * rot(2, 1)) > ra + rb) return false; + + // Test axis L = A0 x B2 + ra = a_half_extents[1] * abs_r[2][2] + a_half_extents[2] * abs_r[2][1]; + rb = mHalfExtents[0] * abs_r[1][0] + mHalfExtents[1] * abs_r[0][0]; + if (abs(rot(2, 3) * rot(1, 2) - rot(1, 3) * rot(2, 2)) > ra + rb) return false; + + // Test axis L = A1 x B0 + ra = a_half_extents[0] * abs_r[0][2] + a_half_extents[2] * abs_r[0][0]; + rb = mHalfExtents[1] * abs_r[2][1] + mHalfExtents[2] * abs_r[1][1]; + if (abs(rot(0, 3) * rot(2, 0) - rot(2, 3) * rot(0, 0)) > ra + rb) return false; + + // Test axis L = A1 x B1 + ra = a_half_extents[0] * abs_r[1][2] + a_half_extents[2] * abs_r[1][0]; + rb = mHalfExtents[0] * abs_r[2][1] + mHalfExtents[2] * abs_r[0][1]; + if (abs(rot(0, 3) * rot(2, 1) - rot(2, 3) * rot(0, 1)) > ra + rb) return false; + + // Test axis L = A1 x B2 + ra = a_half_extents[0] * abs_r[2][2] + a_half_extents[2] * abs_r[2][0]; + rb = mHalfExtents[0] * abs_r[1][1] + mHalfExtents[1] * abs_r[0][1]; + if (abs(rot(0, 3) * rot(2, 2) - rot(2, 3) * rot(0, 2)) > ra + rb) return false; + + // Test axis L = A2 x B0 + ra = a_half_extents[0] * abs_r[0][1] + a_half_extents[1] * abs_r[0][0]; + rb = mHalfExtents[1] * abs_r[2][2] + mHalfExtents[2] * abs_r[1][2]; + if (abs(rot(1, 3) * rot(0, 0) - rot(0, 3) * rot(1, 0)) > ra + rb) return false; + + // Test axis L = A2 x B1 + ra = a_half_extents[0] * abs_r[1][1] + a_half_extents[1] * abs_r[1][0]; + rb = mHalfExtents[0] * abs_r[2][2] + mHalfExtents[2] * abs_r[0][2]; + if (abs(rot(1, 3) * rot(0, 1) - rot(0, 3) * rot(1, 1)) > ra + rb) return false; + + // Test axis L = A2 x B2 + ra = a_half_extents[0] * abs_r[2][1] + a_half_extents[1] * abs_r[2][0]; + rb = mHalfExtents[0] * abs_r[1][2] + mHalfExtents[1] * abs_r[0][2]; + if (abs(rot(1, 3) * rot(0, 2) - rot(0, 3) * rot(1, 2)) > ra + rb) return false; + + // Since no separating axis is found, the OBB and AAB must be intersecting + return true; +} + +bool OrientedBox::Overlaps(const OrientedBox &inBox, float inEpsilon) const +{ + // Taken from: Real Time Collision Detection - Christer Ericson + // Chapter 4.4.1, page 103-105. + // Note that A is this, B is inBox + + // Compute rotation matrix expressing b in a's coordinate frame + Mat44 rot = mOrientation.InversedRotationTranslation() * inBox.mOrientation; + + // Compute common subexpressions. Add in an epsilon term to + // counteract arithmetic errors when two edges are parallel and + // their cross product is (near) null (see text for details) + Vec3 epsilon = Vec3::sReplicate(inEpsilon); + Vec3 abs_r[3] { rot.GetAxisX().Abs() + epsilon, rot.GetAxisY().Abs() + epsilon, rot.GetAxisZ().Abs() + epsilon }; + + // Test axes L = A0, L = A1, L = A2 + float ra, rb; + for (int i = 0; i < 3; i++) + { + ra = mHalfExtents[i]; + rb = inBox.mHalfExtents[0] * abs_r[0][i] + inBox.mHalfExtents[1] * abs_r[1][i] + inBox.mHalfExtents[2] * abs_r[2][i]; + if (abs(rot(i, 3)) > ra + rb) return false; + } + + // Test axes L = B0, L = B1, L = B2 + for (int i = 0; i < 3; i++) + { + ra = mHalfExtents.Dot(abs_r[i]); + rb = inBox.mHalfExtents[i]; + if (abs(rot.GetTranslation().Dot(rot.GetColumn3(i))) > ra + rb) return false; + } + + // Test axis L = A0 x B0 + ra = mHalfExtents[1] * abs_r[0][2] + mHalfExtents[2] * abs_r[0][1]; + rb = inBox.mHalfExtents[1] * abs_r[2][0] + inBox.mHalfExtents[2] * abs_r[1][0]; + if (abs(rot(2, 3) * rot(1, 0) - rot(1, 3) * rot(2, 0)) > ra + rb) return false; + + // Test axis L = A0 x B1 + ra = mHalfExtents[1] * abs_r[1][2] + mHalfExtents[2] * abs_r[1][1]; + rb = inBox.mHalfExtents[0] * abs_r[2][0] + inBox.mHalfExtents[2] * abs_r[0][0]; + if (abs(rot(2, 3) * rot(1, 1) - rot(1, 3) * rot(2, 1)) > ra + rb) return false; + + // Test axis L = A0 x B2 + ra = mHalfExtents[1] * abs_r[2][2] + mHalfExtents[2] * abs_r[2][1]; + rb = inBox.mHalfExtents[0] * abs_r[1][0] + inBox.mHalfExtents[1] * abs_r[0][0]; + if (abs(rot(2, 3) * rot(1, 2) - rot(1, 3) * rot(2, 2)) > ra + rb) return false; + + // Test axis L = A1 x B0 + ra = mHalfExtents[0] * abs_r[0][2] + mHalfExtents[2] * abs_r[0][0]; + rb = inBox.mHalfExtents[1] * abs_r[2][1] + inBox.mHalfExtents[2] * abs_r[1][1]; + if (abs(rot(0, 3) * rot(2, 0) - rot(2, 3) * rot(0, 0)) > ra + rb) return false; + + // Test axis L = A1 x B1 + ra = mHalfExtents[0] * abs_r[1][2] + mHalfExtents[2] * abs_r[1][0]; + rb = inBox.mHalfExtents[0] * abs_r[2][1] + inBox.mHalfExtents[2] * abs_r[0][1]; + if (abs(rot(0, 3) * rot(2, 1) - rot(2, 3) * rot(0, 1)) > ra + rb) return false; + + // Test axis L = A1 x B2 + ra = mHalfExtents[0] * abs_r[2][2] + mHalfExtents[2] * abs_r[2][0]; + rb = inBox.mHalfExtents[0] * abs_r[1][1] + inBox.mHalfExtents[1] * abs_r[0][1]; + if (abs(rot(0, 3) * rot(2, 2) - rot(2, 3) * rot(0, 2)) > ra + rb) return false; + + // Test axis L = A2 x B0 + ra = mHalfExtents[0] * abs_r[0][1] + mHalfExtents[1] * abs_r[0][0]; + rb = inBox.mHalfExtents[1] * abs_r[2][2] + inBox.mHalfExtents[2] * abs_r[1][2]; + if (abs(rot(1, 3) * rot(0, 0) - rot(0, 3) * rot(1, 0)) > ra + rb) return false; + + // Test axis L = A2 x B1 + ra = mHalfExtents[0] * abs_r[1][1] + mHalfExtents[1] * abs_r[1][0]; + rb = inBox.mHalfExtents[0] * abs_r[2][2] + inBox.mHalfExtents[2] * abs_r[0][2]; + if (abs(rot(1, 3) * rot(0, 1) - rot(0, 3) * rot(1, 1)) > ra + rb) return false; + + // Test axis L = A2 x B2 + ra = mHalfExtents[0] * abs_r[2][1] + mHalfExtents[1] * abs_r[2][0]; + rb = inBox.mHalfExtents[0] * abs_r[1][2] + inBox.mHalfExtents[1] * abs_r[0][2]; + if (abs(rot(1, 3) * rot(0, 2) - rot(0, 3) * rot(1, 2)) > ra + rb) return false; + + // Since no separating axis is found, the OBBs must be intersecting + return true; +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/OrientedBox.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/OrientedBox.h new file mode 100644 index 00000000000..c5c2a0e16c8 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/OrientedBox.h @@ -0,0 +1,39 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +class AABox; + +/// Oriented box +class [[nodiscard]] JPH_EXPORT_GCC_BUG_WORKAROUND OrientedBox +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Constructor + OrientedBox() = default; + OrientedBox(Mat44Arg inOrientation, Vec3Arg inHalfExtents) : mOrientation(inOrientation), mHalfExtents(inHalfExtents) { } + + /// Construct from axis aligned box and transform. Only works for rotation/translation matrix (no scaling / shearing). + OrientedBox(Mat44Arg inOrientation, const AABox &inBox) : OrientedBox(inOrientation.PreTranslated(inBox.GetCenter()), inBox.GetExtent()) { } + + /// Test if oriented box overlaps with axis aligned box each other + bool Overlaps(const AABox &inBox, float inEpsilon = 1.0e-6f) const; + + /// Test if two oriented boxes overlap each other + bool Overlaps(const OrientedBox &inBox, float inEpsilon = 1.0e-6f) const; + + Mat44 mOrientation; ///< Transform that positions and rotates the local space axis aligned box into world space + Vec3 mHalfExtents; ///< Half extents (half the size of the edge) of the local space axis aligned box +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/Plane.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/Plane.h new file mode 100644 index 00000000000..c969dbd6f08 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/Plane.h @@ -0,0 +1,86 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +JPH_NAMESPACE_BEGIN + +/// An infinite plane described by the formula X . Normal + Constant = 0. +class [[nodiscard]] Plane +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Constructor + Plane() = default; + explicit Plane(Vec4Arg inNormalAndConstant) : mNormalAndConstant(inNormalAndConstant) { } + Plane(Vec3Arg inNormal, float inConstant) : mNormalAndConstant(inNormal, inConstant) { } + + /// Create from point and normal + static Plane sFromPointAndNormal(Vec3Arg inPoint, Vec3Arg inNormal) { return Plane(Vec4(inNormal, -inNormal.Dot(inPoint))); } + + /// Create from point and normal, double precision version that more accurately calculates the plane constant + static Plane sFromPointAndNormal(DVec3Arg inPoint, Vec3Arg inNormal) { return Plane(Vec4(inNormal, -float(DVec3(inNormal).Dot(inPoint)))); } + + /// Create from 3 counter clockwise points + static Plane sFromPointsCCW(Vec3Arg inV1, Vec3Arg inV2, Vec3Arg inV3) { return sFromPointAndNormal(inV1, (inV2 - inV1).Cross(inV3 - inV1).Normalized()); } + + // Properties + Vec3 GetNormal() const { return Vec3(mNormalAndConstant); } + void SetNormal(Vec3Arg inNormal) { mNormalAndConstant = Vec4(inNormal, mNormalAndConstant.GetW()); } + float GetConstant() const { return mNormalAndConstant.GetW(); } + void SetConstant(float inConstant) { mNormalAndConstant.SetW(inConstant); } + + /// Offset the plane (positive value means move it in the direction of the plane normal) + Plane Offset(float inDistance) const { return Plane(mNormalAndConstant - Vec4(Vec3::sZero(), inDistance)); } + + /// Transform the plane by a matrix + inline Plane GetTransformed(Mat44Arg inTransform) const + { + Vec3 transformed_normal = inTransform.Multiply3x3(GetNormal()); + return Plane(transformed_normal, GetConstant() - inTransform.GetTranslation().Dot(transformed_normal)); + } + + /// Distance point to plane + float SignedDistance(Vec3Arg inPoint) const { return inPoint.Dot(GetNormal()) + GetConstant(); } + + /// Returns intersection point between 3 planes + static bool sIntersectPlanes(const Plane &inP1, const Plane &inP2, const Plane &inP3, Vec3 &outPoint) + { + // We solve the equation: + // |ax, ay, az, aw| | x | | 0 | + // |bx, by, bz, bw| * | y | = | 0 | + // |cx, cy, cz, cw| | z | | 0 | + // | 0, 0, 0, 1| | 1 | | 1 | + // Where normal of plane 1 = (ax, ay, az), plane constant of 1 = aw, normal of plane 2 = (bx, by, bz) etc. + // This involves inverting the matrix and multiplying it with [0, 0, 0, 1] + + // Fetch the normals and plane constants for the three planes + Vec4 a = inP1.mNormalAndConstant; + Vec4 b = inP2.mNormalAndConstant; + Vec4 c = inP3.mNormalAndConstant; + + // Result is a vector that we have to divide by: + float denominator = Vec3(a).Dot(Vec3(b).Cross(Vec3(c))); + if (denominator == 0.0f) + return false; + + // The numerator is: + // [aw*(bz*cy-by*cz)+ay*(bw*cz-bz*cw)+az*(by*cw-bw*cy)] + // [aw*(bx*cz-bz*cx)+ax*(bz*cw-bw*cz)+az*(bw*cx-bx*cw)] + // [aw*(by*cx-bx*cy)+ax*(bw*cy-by*cw)+ay*(bx*cw-bw*cx)] + Vec4 numerator = + a.SplatW() * (b.Swizzle() * c.Swizzle() - b.Swizzle() * c.Swizzle()) + + a.Swizzle() * (b.Swizzle() * c.Swizzle() - b.Swizzle() * c.Swizzle()) + + a.Swizzle() * (b.Swizzle() * c.Swizzle() - b.Swizzle() * c.Swizzle()); + + outPoint = Vec3(numerator) / denominator; + return true; + } + +private: + Vec4 mNormalAndConstant; ///< XYZ = normal, W = constant, plane: x . normal + constant = 0 +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/RayAABox.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/RayAABox.h new file mode 100644 index 00000000000..4506fadbb75 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/RayAABox.h @@ -0,0 +1,241 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +JPH_NAMESPACE_BEGIN + +/// Helper structure holding the reciprocal of a ray for Ray vs AABox testing +class RayInvDirection +{ +public: + /// Constructors + inline RayInvDirection() = default; + inline explicit RayInvDirection(Vec3Arg inDirection) { Set(inDirection); } + + /// Set reciprocal from ray direction + inline void Set(Vec3Arg inDirection) + { + // if (abs(inDirection) <= Epsilon) the ray is nearly parallel to the slab. + mIsParallel = Vec3::sLessOrEqual(inDirection.Abs(), Vec3::sReplicate(1.0e-20f)); + + // Calculate 1 / direction while avoiding division by zero + mInvDirection = Vec3::sSelect(inDirection, Vec3::sReplicate(1.0f), mIsParallel).Reciprocal(); + } + + Vec3 mInvDirection; ///< 1 / ray direction + UVec4 mIsParallel; ///< for each component if it is parallel to the coordinate axis +}; + +/// Intersect AABB with ray, returns minimal distance along ray or FLT_MAX if no hit +/// Note: Can return negative value if ray starts in box +JPH_INLINE float RayAABox(Vec3Arg inOrigin, const RayInvDirection &inInvDirection, Vec3Arg inBoundsMin, Vec3Arg inBoundsMax) +{ + // Constants + Vec3 flt_min = Vec3::sReplicate(-FLT_MAX); + Vec3 flt_max = Vec3::sReplicate(FLT_MAX); + + // Test against all three axii simultaneously. + Vec3 t1 = (inBoundsMin - inOrigin) * inInvDirection.mInvDirection; + Vec3 t2 = (inBoundsMax - inOrigin) * inInvDirection.mInvDirection; + + // Compute the max of min(t1,t2) and the min of max(t1,t2) ensuring we don't + // use the results from any directions parallel to the slab. + Vec3 t_min = Vec3::sSelect(Vec3::sMin(t1, t2), flt_min, inInvDirection.mIsParallel); + Vec3 t_max = Vec3::sSelect(Vec3::sMax(t1, t2), flt_max, inInvDirection.mIsParallel); + + // t_min.xyz = maximum(t_min.x, t_min.y, t_min.z); + t_min = Vec3::sMax(t_min, t_min.Swizzle()); + t_min = Vec3::sMax(t_min, t_min.Swizzle()); + + // t_max.xyz = minimum(t_max.x, t_max.y, t_max.z); + t_max = Vec3::sMin(t_max, t_max.Swizzle()); + t_max = Vec3::sMin(t_max, t_max.Swizzle()); + + // if (t_min > t_max) return FLT_MAX; + UVec4 no_intersection = Vec3::sGreater(t_min, t_max); + + // if (t_max < 0.0f) return FLT_MAX; + no_intersection = UVec4::sOr(no_intersection, Vec3::sLess(t_max, Vec3::sZero())); + + // if (inInvDirection.mIsParallel && !(Min <= inOrigin && inOrigin <= Max)) return FLT_MAX; else return t_min; + UVec4 no_parallel_overlap = UVec4::sOr(Vec3::sLess(inOrigin, inBoundsMin), Vec3::sGreater(inOrigin, inBoundsMax)); + no_intersection = UVec4::sOr(no_intersection, UVec4::sAnd(inInvDirection.mIsParallel, no_parallel_overlap)); + no_intersection = UVec4::sOr(no_intersection, no_intersection.SplatY()); + no_intersection = UVec4::sOr(no_intersection, no_intersection.SplatZ()); + return Vec3::sSelect(t_min, flt_max, no_intersection).GetX(); +} + +/// Intersect 4 AABBs with ray, returns minimal distance along ray or FLT_MAX if no hit +/// Note: Can return negative value if ray starts in box +JPH_INLINE Vec4 RayAABox4(Vec3Arg inOrigin, const RayInvDirection &inInvDirection, Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ) +{ + // Constants + Vec4 flt_min = Vec4::sReplicate(-FLT_MAX); + Vec4 flt_max = Vec4::sReplicate(FLT_MAX); + + // Origin + Vec4 originx = inOrigin.SplatX(); + Vec4 originy = inOrigin.SplatY(); + Vec4 originz = inOrigin.SplatZ(); + + // Parallel + UVec4 parallelx = inInvDirection.mIsParallel.SplatX(); + UVec4 parallely = inInvDirection.mIsParallel.SplatY(); + UVec4 parallelz = inInvDirection.mIsParallel.SplatZ(); + + // Inverse direction + Vec4 invdirx = inInvDirection.mInvDirection.SplatX(); + Vec4 invdiry = inInvDirection.mInvDirection.SplatY(); + Vec4 invdirz = inInvDirection.mInvDirection.SplatZ(); + + // Test against all three axii simultaneously. + Vec4 t1x = (inBoundsMinX - originx) * invdirx; + Vec4 t1y = (inBoundsMinY - originy) * invdiry; + Vec4 t1z = (inBoundsMinZ - originz) * invdirz; + Vec4 t2x = (inBoundsMaxX - originx) * invdirx; + Vec4 t2y = (inBoundsMaxY - originy) * invdiry; + Vec4 t2z = (inBoundsMaxZ - originz) * invdirz; + + // Compute the max of min(t1,t2) and the min of max(t1,t2) ensuring we don't + // use the results from any directions parallel to the slab. + Vec4 t_minx = Vec4::sSelect(Vec4::sMin(t1x, t2x), flt_min, parallelx); + Vec4 t_miny = Vec4::sSelect(Vec4::sMin(t1y, t2y), flt_min, parallely); + Vec4 t_minz = Vec4::sSelect(Vec4::sMin(t1z, t2z), flt_min, parallelz); + Vec4 t_maxx = Vec4::sSelect(Vec4::sMax(t1x, t2x), flt_max, parallelx); + Vec4 t_maxy = Vec4::sSelect(Vec4::sMax(t1y, t2y), flt_max, parallely); + Vec4 t_maxz = Vec4::sSelect(Vec4::sMax(t1z, t2z), flt_max, parallelz); + + // t_min.xyz = maximum(t_min.x, t_min.y, t_min.z); + Vec4 t_min = Vec4::sMax(Vec4::sMax(t_minx, t_miny), t_minz); + + // t_max.xyz = minimum(t_max.x, t_max.y, t_max.z); + Vec4 t_max = Vec4::sMin(Vec4::sMin(t_maxx, t_maxy), t_maxz); + + // if (t_min > t_max) return FLT_MAX; + UVec4 no_intersection = Vec4::sGreater(t_min, t_max); + + // if (t_max < 0.0f) return FLT_MAX; + no_intersection = UVec4::sOr(no_intersection, Vec4::sLess(t_max, Vec4::sZero())); + + // if bounds are invalid return FLOAT_MAX; + UVec4 bounds_invalid = UVec4::sOr(UVec4::sOr(Vec4::sGreater(inBoundsMinX, inBoundsMaxX), Vec4::sGreater(inBoundsMinY, inBoundsMaxY)), Vec4::sGreater(inBoundsMinZ, inBoundsMaxZ)); + no_intersection = UVec4::sOr(no_intersection, bounds_invalid); + + // if (inInvDirection.mIsParallel && !(Min <= inOrigin && inOrigin <= Max)) return FLT_MAX; else return t_min; + UVec4 no_parallel_overlapx = UVec4::sAnd(parallelx, UVec4::sOr(Vec4::sLess(originx, inBoundsMinX), Vec4::sGreater(originx, inBoundsMaxX))); + UVec4 no_parallel_overlapy = UVec4::sAnd(parallely, UVec4::sOr(Vec4::sLess(originy, inBoundsMinY), Vec4::sGreater(originy, inBoundsMaxY))); + UVec4 no_parallel_overlapz = UVec4::sAnd(parallelz, UVec4::sOr(Vec4::sLess(originz, inBoundsMinZ), Vec4::sGreater(originz, inBoundsMaxZ))); + no_intersection = UVec4::sOr(no_intersection, UVec4::sOr(UVec4::sOr(no_parallel_overlapx, no_parallel_overlapy), no_parallel_overlapz)); + return Vec4::sSelect(t_min, flt_max, no_intersection); +} + +/// Intersect AABB with ray, returns minimal and maximal distance along ray or FLT_MAX, -FLT_MAX if no hit +/// Note: Can return negative value for outMin if ray starts in box +JPH_INLINE void RayAABox(Vec3Arg inOrigin, const RayInvDirection &inInvDirection, Vec3Arg inBoundsMin, Vec3Arg inBoundsMax, float &outMin, float &outMax) +{ + // Constants + Vec3 flt_min = Vec3::sReplicate(-FLT_MAX); + Vec3 flt_max = Vec3::sReplicate(FLT_MAX); + + // Test against all three axii simultaneously. + Vec3 t1 = (inBoundsMin - inOrigin) * inInvDirection.mInvDirection; + Vec3 t2 = (inBoundsMax - inOrigin) * inInvDirection.mInvDirection; + + // Compute the max of min(t1,t2) and the min of max(t1,t2) ensuring we don't + // use the results from any directions parallel to the slab. + Vec3 t_min = Vec3::sSelect(Vec3::sMin(t1, t2), flt_min, inInvDirection.mIsParallel); + Vec3 t_max = Vec3::sSelect(Vec3::sMax(t1, t2), flt_max, inInvDirection.mIsParallel); + + // t_min.xyz = maximum(t_min.x, t_min.y, t_min.z); + t_min = Vec3::sMax(t_min, t_min.Swizzle()); + t_min = Vec3::sMax(t_min, t_min.Swizzle()); + + // t_max.xyz = minimum(t_max.x, t_max.y, t_max.z); + t_max = Vec3::sMin(t_max, t_max.Swizzle()); + t_max = Vec3::sMin(t_max, t_max.Swizzle()); + + // if (t_min > t_max) return FLT_MAX; + UVec4 no_intersection = Vec3::sGreater(t_min, t_max); + + // if (t_max < 0.0f) return FLT_MAX; + no_intersection = UVec4::sOr(no_intersection, Vec3::sLess(t_max, Vec3::sZero())); + + // if (inInvDirection.mIsParallel && !(Min <= inOrigin && inOrigin <= Max)) return FLT_MAX; else return t_min; + UVec4 no_parallel_overlap = UVec4::sOr(Vec3::sLess(inOrigin, inBoundsMin), Vec3::sGreater(inOrigin, inBoundsMax)); + no_intersection = UVec4::sOr(no_intersection, UVec4::sAnd(inInvDirection.mIsParallel, no_parallel_overlap)); + no_intersection = UVec4::sOr(no_intersection, no_intersection.SplatY()); + no_intersection = UVec4::sOr(no_intersection, no_intersection.SplatZ()); + outMin = Vec3::sSelect(t_min, flt_max, no_intersection).GetX(); + outMax = Vec3::sSelect(t_max, flt_min, no_intersection).GetX(); +} + +/// Intersect AABB with ray, returns true if there is a hit closer than inClosest +JPH_INLINE bool RayAABoxHits(Vec3Arg inOrigin, const RayInvDirection &inInvDirection, Vec3Arg inBoundsMin, Vec3Arg inBoundsMax, float inClosest) +{ + // Constants + Vec3 flt_min = Vec3::sReplicate(-FLT_MAX); + Vec3 flt_max = Vec3::sReplicate(FLT_MAX); + + // Test against all three axii simultaneously. + Vec3 t1 = (inBoundsMin - inOrigin) * inInvDirection.mInvDirection; + Vec3 t2 = (inBoundsMax - inOrigin) * inInvDirection.mInvDirection; + + // Compute the max of min(t1,t2) and the min of max(t1,t2) ensuring we don't + // use the results from any directions parallel to the slab. + Vec3 t_min = Vec3::sSelect(Vec3::sMin(t1, t2), flt_min, inInvDirection.mIsParallel); + Vec3 t_max = Vec3::sSelect(Vec3::sMax(t1, t2), flt_max, inInvDirection.mIsParallel); + + // t_min.xyz = maximum(t_min.x, t_min.y, t_min.z); + t_min = Vec3::sMax(t_min, t_min.Swizzle()); + t_min = Vec3::sMax(t_min, t_min.Swizzle()); + + // t_max.xyz = minimum(t_max.x, t_max.y, t_max.z); + t_max = Vec3::sMin(t_max, t_max.Swizzle()); + t_max = Vec3::sMin(t_max, t_max.Swizzle()); + + // if (t_min > t_max) return false; + UVec4 no_intersection = Vec3::sGreater(t_min, t_max); + + // if (t_max < 0.0f) return false; + no_intersection = UVec4::sOr(no_intersection, Vec3::sLess(t_max, Vec3::sZero())); + + // if (t_min > inClosest) return false; + no_intersection = UVec4::sOr(no_intersection, Vec3::sGreater(t_min, Vec3::sReplicate(inClosest))); + + // if (inInvDirection.mIsParallel && !(Min <= inOrigin && inOrigin <= Max)) return false; else return true; + UVec4 no_parallel_overlap = UVec4::sOr(Vec3::sLess(inOrigin, inBoundsMin), Vec3::sGreater(inOrigin, inBoundsMax)); + no_intersection = UVec4::sOr(no_intersection, UVec4::sAnd(inInvDirection.mIsParallel, no_parallel_overlap)); + + return !no_intersection.TestAnyXYZTrue(); +} + +/// Intersect AABB with ray without hit fraction, based on separating axis test +/// @see http://www.codercorner.com/RayAABB.cpp +JPH_INLINE bool RayAABoxHits(Vec3Arg inOrigin, Vec3Arg inDirection, Vec3Arg inBoundsMin, Vec3Arg inBoundsMax) +{ + Vec3 extents = inBoundsMax - inBoundsMin; + + Vec3 diff = 2.0f * inOrigin - inBoundsMin - inBoundsMax; + Vec3 abs_diff = diff.Abs(); + + UVec4 no_intersection = UVec4::sAnd(Vec3::sGreater(abs_diff, extents), Vec3::sGreaterOrEqual(diff * inDirection, Vec3::sZero())); + + Vec3 abs_dir = inDirection.Abs(); + Vec3 abs_dir_yzz = abs_dir.Swizzle(); + Vec3 abs_dir_xyx = abs_dir.Swizzle(); + + Vec3 extents_yzz = extents.Swizzle(); + Vec3 extents_xyx = extents.Swizzle(); + + Vec3 diff_yzx = diff.Swizzle(); + + Vec3 dir_yzx = inDirection.Swizzle(); + + no_intersection = UVec4::sOr(no_intersection, Vec3::sGreater((inDirection * diff_yzx - dir_yzx * diff).Abs(), extents_xyx * abs_dir_yzz + extents_yzz * abs_dir_xyx)); + + return !no_intersection.TestAnyXYZTrue(); +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/RayAABox8.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/RayAABox8.h new file mode 100644 index 00000000000..260e3636cd3 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/RayAABox8.h @@ -0,0 +1,76 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +JPH_NAMESPACE_BEGIN + +/// Intersect 8 AABBs with ray, returns minimal distance along ray or FLT_MAX if no hit +/// Note: Can return negative value if ray starts in box +JPH_INLINE Vec8 RayAABox8(Vec3Arg inOrigin, const RayInvDirection &inInvDirection, Vec8Arg inBoundsMinX, Vec8Arg inBoundsMinY, Vec8Arg inBoundsMinZ, Vec8Arg inBoundsMaxX, Vec8Arg inBoundsMaxY, Vec8Arg inBoundsMaxZ) +{ + // Constants + Vec8 flt_min = Vec8::sReplicate(-FLT_MAX); + Vec8 flt_max = Vec8::sReplicate(FLT_MAX); + + // Origin + Vec8 originx = Vec8::sSplatX(Vec4(inOrigin)); + Vec8 originy = Vec8::sSplatY(Vec4(inOrigin)); + Vec8 originz = Vec8::sSplatZ(Vec4(inOrigin)); + + // Parallel + UVec8 parallelx = UVec8::sSplatX(inInvDirection.mIsParallel); + UVec8 parallely = UVec8::sSplatY(inInvDirection.mIsParallel); + UVec8 parallelz = UVec8::sSplatZ(inInvDirection.mIsParallel); + + // Inverse direction + Vec8 invdirx = Vec8::sSplatX(Vec4(inInvDirection.mInvDirection)); + Vec8 invdiry = Vec8::sSplatY(Vec4(inInvDirection.mInvDirection)); + Vec8 invdirz = Vec8::sSplatZ(Vec4(inInvDirection.mInvDirection)); + + // Test against all three axii simultaneously. + Vec8 t1x = (inBoundsMinX - originx) * invdirx; + Vec8 t1y = (inBoundsMinY - originy) * invdiry; + Vec8 t1z = (inBoundsMinZ - originz) * invdirz; + Vec8 t2x = (inBoundsMaxX - originx) * invdirx; + Vec8 t2y = (inBoundsMaxY - originy) * invdiry; + Vec8 t2z = (inBoundsMaxZ - originz) * invdirz; + + // Compute the max of min(t1,t2) and the min of max(t1,t2) ensuring we don't + // use the results from any directions parallel to the slab. + Vec8 t_minx = Vec8::sSelect(Vec8::sMin(t1x, t2x), flt_min, parallelx); + Vec8 t_miny = Vec8::sSelect(Vec8::sMin(t1y, t2y), flt_min, parallely); + Vec8 t_minz = Vec8::sSelect(Vec8::sMin(t1z, t2z), flt_min, parallelz); + Vec8 t_maxx = Vec8::sSelect(Vec8::sMax(t1x, t2x), flt_max, parallelx); + Vec8 t_maxy = Vec8::sSelect(Vec8::sMax(t1y, t2y), flt_max, parallely); + Vec8 t_maxz = Vec8::sSelect(Vec8::sMax(t1z, t2z), flt_max, parallelz); + + // t_min.xyz = maximum(t_min.x, t_min.y, t_min.z); + Vec8 t_min = Vec8::sMax(Vec8::sMax(t_minx, t_miny), t_minz); + + // t_max.xyz = minimum(t_max.x, t_max.y, t_max.z); + Vec8 t_max = Vec8::sMin(Vec8::sMin(t_maxx, t_maxy), t_maxz); + + // if (t_min > t_max) return FLT_MAX; + UVec8 no_intersection = Vec8::sGreater(t_min, t_max); + + // if (t_max < 0.0f) return FLT_MAX; + no_intersection = UVec8::sOr(no_intersection, Vec8::sLess(t_max, Vec8::sZero())); + + // if bounds are invalid return FLOAT_MAX; + UVec8 bounds_invalid = UVec8::sOr(UVec8::sOr(Vec8::sGreater(inBoundsMinX, inBoundsMaxX), Vec8::sGreater(inBoundsMinY, inBoundsMaxY)), Vec8::sGreater(inBoundsMinZ, inBoundsMaxZ)); + no_intersection = UVec8::sOr(no_intersection, bounds_invalid); + + // if (inInvDirection.mIsParallel && !(Min <= inOrigin && inOrigin <= Max)) return FLT_MAX; else return t_min; + UVec8 no_parallel_overlapx = UVec8::sAnd(parallelx, UVec8::sOr(Vec8::sLess(originx, inBoundsMinX), Vec8::sGreater(originx, inBoundsMaxX))); + UVec8 no_parallel_overlapy = UVec8::sAnd(parallely, UVec8::sOr(Vec8::sLess(originy, inBoundsMinY), Vec8::sGreater(originy, inBoundsMaxY))); + UVec8 no_parallel_overlapz = UVec8::sAnd(parallelz, UVec8::sOr(Vec8::sLess(originz, inBoundsMinZ), Vec8::sGreater(originz, inBoundsMaxZ))); + no_intersection = UVec8::sOr(no_intersection, UVec8::sOr(UVec8::sOr(no_parallel_overlapx, no_parallel_overlapy), no_parallel_overlapz)); + return Vec8::sSelect(t_min, flt_max, no_intersection); +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/RayCapsule.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/RayCapsule.h new file mode 100644 index 00000000000..4862931b685 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/RayCapsule.h @@ -0,0 +1,37 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +JPH_NAMESPACE_BEGIN + +/// Tests a ray starting at inRayOrigin and extending infinitely in inRayDirection +/// against a capsule centered around the origin with its axis along the Y axis and half height specified. +/// @return FLT_MAX if there is no intersection, otherwise the fraction along the ray. +/// @param inRayDirection Ray direction. Does not need to be normalized. +/// @param inRayOrigin Origin of the ray. If the ray starts inside the capsule, the returned fraction will be 0. +/// @param inCapsuleHalfHeight Distance from the origin to the center of the top sphere (or that of the bottom) +/// @param inCapsuleRadius Radius of the top/bottom sphere +JPH_INLINE float RayCapsule(Vec3Arg inRayOrigin, Vec3Arg inRayDirection, float inCapsuleHalfHeight, float inCapsuleRadius) +{ + // Test infinite cylinder + float cylinder = RayCylinder(inRayOrigin, inRayDirection, inCapsuleRadius); + if (cylinder == FLT_MAX) + return FLT_MAX; + + // If this hit is in the finite cylinder we have our fraction + if (abs(inRayOrigin.GetY() + cylinder * inRayDirection.GetY()) <= inCapsuleHalfHeight) + return cylinder; + + // Test upper and lower sphere + Vec3 sphere_center(0, inCapsuleHalfHeight, 0); + float upper = RaySphere(inRayOrigin, inRayDirection, sphere_center, inCapsuleRadius); + float lower = RaySphere(inRayOrigin, inRayDirection, -sphere_center, inCapsuleRadius); + return min(upper, lower); +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/RayCylinder.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/RayCylinder.h new file mode 100644 index 00000000000..cabed0680a2 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/RayCylinder.h @@ -0,0 +1,101 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +/// Tests a ray starting at inRayOrigin and extending infinitely in inRayDirection +/// against an infinite cylinder centered along the Y axis +/// @return FLT_MAX if there is no intersection, otherwise the fraction along the ray. +/// @param inRayDirection Direction of the ray. Does not need to be normalized. +/// @param inRayOrigin Origin of the ray. If the ray starts inside the cylinder, the returned fraction will be 0. +/// @param inCylinderRadius Radius of the infinite cylinder +JPH_INLINE float RayCylinder(Vec3Arg inRayOrigin, Vec3Arg inRayDirection, float inCylinderRadius) +{ + // Remove Y component of ray to see of ray intersects with infinite cylinder + UVec4 mask_y = UVec4(0, 0xffffffff, 0, 0); + Vec3 origin_xz = Vec3::sSelect(inRayOrigin, Vec3::sZero(), mask_y); + float origin_xz_len_sq = origin_xz.LengthSq(); + float r_sq = Square(inCylinderRadius); + if (origin_xz_len_sq > r_sq) + { + // Ray starts outside of the infinite cylinder + // Solve: |RayOrigin_xz + fraction * RayDirection_xz|^2 = r^2 to find fraction + Vec3 direction_xz = Vec3::sSelect(inRayDirection, Vec3::sZero(), mask_y); + float a = direction_xz.LengthSq(); + float b = 2.0f * origin_xz.Dot(direction_xz); + float c = origin_xz_len_sq - r_sq; + float fraction1, fraction2; + if (FindRoot(a, b, c, fraction1, fraction2) == 0) + return FLT_MAX; // No intersection with infinite cylinder + + // Get fraction corresponding to the ray entering the circle + float fraction = min(fraction1, fraction2); + if (fraction >= 0.0f) + return fraction; + } + else + { + // Ray starts inside the infinite cylinder + return 0.0f; + } + + // No collision + return FLT_MAX; +} + +/// Test a ray against a cylinder centered around the origin with its axis along the Y axis and half height specified. +/// @return FLT_MAX if there is no intersection, otherwise the fraction along the ray. +/// @param inRayDirection Ray direction. Does not need to be normalized. +/// @param inRayOrigin Origin of the ray. If the ray starts inside the cylinder, the returned fraction will be 0. +/// @param inCylinderRadius Radius of the cylinder +/// @param inCylinderHalfHeight Distance from the origin to the top (or bottom) of the cylinder +JPH_INLINE float RayCylinder(Vec3Arg inRayOrigin, Vec3Arg inRayDirection, float inCylinderHalfHeight, float inCylinderRadius) +{ + // Test infinite cylinder + float fraction = RayCylinder(inRayOrigin, inRayDirection, inCylinderRadius); + if (fraction == FLT_MAX) + return FLT_MAX; + + // If this hit is in the finite cylinder we have our fraction + if (abs(inRayOrigin.GetY() + fraction * inRayDirection.GetY()) <= inCylinderHalfHeight) + return fraction; + + // Check if ray could hit the top or bottom plane of the cylinder + float direction_y = inRayDirection.GetY(); + if (direction_y != 0.0f) + { + // Solving line equation: x = ray_origin + fraction * ray_direction + // and plane equation: plane_normal . x + plane_constant = 0 + // fraction = (-plane_constant - plane_normal . ray_origin) / (plane_normal . ray_direction) + // when the ray_direction.y < 0: + // plane_constant = -cylinder_half_height, plane_normal = (0, 1, 0) + // else + // plane_constant = -cylinder_half_height, plane_normal = (0, -1, 0) + float origin_y = inRayOrigin.GetY(); + float plane_fraction; + if (direction_y < 0.0f) + plane_fraction = (inCylinderHalfHeight - origin_y) / direction_y; + else + plane_fraction = -(inCylinderHalfHeight + origin_y) / direction_y; + + // Check if the hit is in front of the ray + if (plane_fraction >= 0.0f) + { + // Test if this hit is inside the cylinder + Vec3 point = inRayOrigin + plane_fraction * inRayDirection; + float dist_sq = Square(point.GetX()) + Square(point.GetZ()); + if (dist_sq <= Square(inCylinderRadius)) + return plane_fraction; + } + } + + // No collision + return FLT_MAX; +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/RaySphere.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/RaySphere.h new file mode 100644 index 00000000000..d5cfb1905a7 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/RaySphere.h @@ -0,0 +1,96 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +/// Tests a ray starting at inRayOrigin and extending infinitely in inRayDirection against a sphere, +/// @return FLT_MAX if there is no intersection, otherwise the fraction along the ray. +/// @param inRayOrigin Ray origin. If the ray starts inside the sphere, the returned fraction will be 0. +/// @param inRayDirection Ray direction. Does not need to be normalized. +/// @param inSphereCenter Position of the center of the sphere +/// @param inSphereRadius Radius of the sphere +JPH_INLINE float RaySphere(Vec3Arg inRayOrigin, Vec3Arg inRayDirection, Vec3Arg inSphereCenter, float inSphereRadius) +{ + // Solve: |RayOrigin + fraction * RayDirection - SphereCenter|^2 = SphereRadius^2 for fraction + Vec3 center_origin = inRayOrigin - inSphereCenter; + float a = inRayDirection.LengthSq(); + float b = 2.0f * inRayDirection.Dot(center_origin); + float c = center_origin.LengthSq() - inSphereRadius * inSphereRadius; + float fraction1, fraction2; + if (FindRoot(a, b, c, fraction1, fraction2) == 0) + return c <= 0.0f? 0.0f : FLT_MAX; // Return if origin is inside the sphere + + // Sort so that the smallest is first + if (fraction1 > fraction2) + swap(fraction1, fraction2); + + // Test solution with lowest fraction, this will be the ray entering the sphere + if (fraction1 >= 0.0f) + return fraction1; // Sphere is before the ray start + + // Test solution with highest fraction, this will be the ray leaving the sphere + if (fraction2 >= 0.0f) + return 0.0f; // We start inside the sphere + + // No solution + return FLT_MAX; +} + +/// Tests a ray starting at inRayOrigin and extending infinitely in inRayDirection against a sphere. +/// Outputs entry and exit points (outMinFraction and outMaxFraction) along the ray (which could be negative if the hit point is before the start of the ray). +/// @param inRayOrigin Ray origin. If the ray starts inside the sphere, the returned fraction will be 0. +/// @param inRayDirection Ray direction. Does not need to be normalized. +/// @param inSphereCenter Position of the center of the sphere. +/// @param inSphereRadius Radius of the sphere. +/// @param outMinFraction Returned lowest intersection fraction +/// @param outMaxFraction Returned highest intersection fraction +/// @return The amount of intersections with the sphere. +/// If 1 intersection is returned outMinFraction will be equal to outMaxFraction +JPH_INLINE int RaySphere(Vec3Arg inRayOrigin, Vec3Arg inRayDirection, Vec3Arg inSphereCenter, float inSphereRadius, float &outMinFraction, float &outMaxFraction) +{ + // Solve: |RayOrigin + fraction * RayDirection - SphereCenter|^2 = SphereRadius^2 for fraction + Vec3 center_origin = inRayOrigin - inSphereCenter; + float a = inRayDirection.LengthSq(); + float b = 2.0f * inRayDirection.Dot(center_origin); + float c = center_origin.LengthSq() - inSphereRadius * inSphereRadius; + float fraction1, fraction2; + switch (FindRoot(a, b, c, fraction1, fraction2)) + { + case 0: + if (c <= 0.0f) + { + // Origin inside sphere + outMinFraction = outMaxFraction = 0.0f; + return 1; + } + else + { + // Origin outside of the sphere + return 0; + } + break; + + case 1: + // Ray is touching the sphere + outMinFraction = outMaxFraction = fraction1; + return 1; + + default: + // Ray enters and exits the sphere + + // Sort so that the smallest is first + if (fraction1 > fraction2) + swap(fraction1, fraction2); + + outMinFraction = fraction1; + outMaxFraction = fraction2; + return 2; + } +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/RayTriangle.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/RayTriangle.h new file mode 100644 index 00000000000..dabd0275cc0 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/RayTriangle.h @@ -0,0 +1,158 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +JPH_NAMESPACE_BEGIN + +/// Intersect ray with triangle, returns closest point or FLT_MAX if no hit (branch less version) +/// Adapted from: http://en.wikipedia.org/wiki/M%C3%B6ller%E2%80%93Trumbore_intersection_algorithm +JPH_INLINE float RayTriangle(Vec3Arg inOrigin, Vec3Arg inDirection, Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2) +{ + // Epsilon + Vec3 epsilon = Vec3::sReplicate(1.0e-12f); + + // Zero & one + Vec3 zero = Vec3::sZero(); + Vec3 one = Vec3::sReplicate(1.0f); + + // Find vectors for two edges sharing inV0 + Vec3 e1 = inV1 - inV0; + Vec3 e2 = inV2 - inV0; + + // Begin calculating determinant - also used to calculate u parameter + Vec3 p = inDirection.Cross(e2); + + // if determinant is near zero, ray lies in plane of triangle + Vec3 det = Vec3::sReplicate(e1.Dot(p)); + + // Check if determinant is near zero + UVec4 det_near_zero = Vec3::sLess(det.Abs(), epsilon); + + // When the determinant is near zero, set it to one to avoid dividing by zero + det = Vec3::sSelect(det, Vec3::sReplicate(1.0f), det_near_zero); + + // Calculate distance from inV0 to ray origin + Vec3 s = inOrigin - inV0; + + // Calculate u parameter + Vec3 u = Vec3::sReplicate(s.Dot(p)) / det; + + // Prepare to test v parameter + Vec3 q = s.Cross(e1); + + // Calculate v parameter + Vec3 v = Vec3::sReplicate(inDirection.Dot(q)) / det; + + // Get intersection point + Vec3 t = Vec3::sReplicate(e2.Dot(q)) / det; + + // Check if there is an intersection + UVec4 no_intersection = + UVec4::sOr + ( + UVec4::sOr + ( + UVec4::sOr + ( + det_near_zero, + Vec3::sLess(u, zero) + ), + UVec4::sOr + ( + Vec3::sLess(v, zero), + Vec3::sGreater(u + v, one) + ) + ), + Vec3::sLess(t, zero) + ); + + // Select intersection point or FLT_MAX based on if there is an intersection or not + return Vec3::sSelect(t, Vec3::sReplicate(FLT_MAX), no_intersection).GetX(); +} + +/// Intersect ray with 4 triangles in SOA format, returns 4 vector of closest points or FLT_MAX if no hit (uses bit tricks to do less divisions) +JPH_INLINE Vec4 RayTriangle4(Vec3Arg inOrigin, Vec3Arg inDirection, Vec4Arg inV0X, Vec4Arg inV0Y, Vec4Arg inV0Z, Vec4Arg inV1X, Vec4Arg inV1Y, Vec4Arg inV1Z, Vec4Arg inV2X, Vec4Arg inV2Y, Vec4Arg inV2Z) +{ + // Epsilon + Vec4 epsilon = Vec4::sReplicate(1.0e-12f); + + // Zero + Vec4 zero = Vec4::sZero(); + + // Find vectors for two edges sharing inV0 + Vec4 e1x = inV1X - inV0X; + Vec4 e1y = inV1Y - inV0Y; + Vec4 e1z = inV1Z - inV0Z; + Vec4 e2x = inV2X - inV0X; + Vec4 e2y = inV2Y - inV0Y; + Vec4 e2z = inV2Z - inV0Z; + + // Get direction vector components + Vec4 dx = inDirection.SplatX(); + Vec4 dy = inDirection.SplatY(); + Vec4 dz = inDirection.SplatZ(); + + // Begin calculating determinant - also used to calculate u parameter + Vec4 px = dy * e2z - dz * e2y; + Vec4 py = dz * e2x - dx * e2z; + Vec4 pz = dx * e2y - dy * e2x; + + // if determinant is near zero, ray lies in plane of triangle + Vec4 det = e1x * px + e1y * py + e1z * pz; + + // Get sign bit for determinant and make positive + Vec4 det_sign = Vec4::sAnd(det, UVec4::sReplicate(0x80000000).ReinterpretAsFloat()); + det = Vec4::sXor(det, det_sign); + + // Check which determinants are near zero + UVec4 det_near_zero = Vec4::sLess(det, epsilon); + + // Set components of the determinant to 1 that are near zero to avoid dividing by zero + det = Vec4::sSelect(det, Vec4::sReplicate(1.0f), det_near_zero); + + // Calculate distance from inV0 to ray origin + Vec4 sx = inOrigin.SplatX() - inV0X; + Vec4 sy = inOrigin.SplatY() - inV0Y; + Vec4 sz = inOrigin.SplatZ() - inV0Z; + + // Calculate u parameter and flip sign if determinant was negative + Vec4 u = Vec4::sXor(sx * px + sy * py + sz * pz, det_sign); + + // Prepare to test v parameter + Vec4 qx = sy * e1z - sz * e1y; + Vec4 qy = sz * e1x - sx * e1z; + Vec4 qz = sx * e1y - sy * e1x; + + // Calculate v parameter and flip sign if determinant was negative + Vec4 v = Vec4::sXor(dx * qx + dy * qy + dz * qz, det_sign); + + // Get intersection point and flip sign if determinant was negative + Vec4 t = Vec4::sXor(e2x * qx + e2y * qy + e2z * qz, det_sign); + + // Check if there is an intersection + UVec4 no_intersection = + UVec4::sOr + ( + UVec4::sOr + ( + UVec4::sOr + ( + det_near_zero, + Vec4::sLess(u, zero) + ), + UVec4::sOr + ( + Vec4::sLess(v, zero), + Vec4::sGreater(u + v, det) + ) + ), + Vec4::sLess(t, zero) + ); + + // Select intersection point or FLT_MAX based on if there is an intersection or not + return Vec4::sSelect(t / det, Vec4::sReplicate(FLT_MAX), no_intersection); +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/RayTriangle8.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/RayTriangle8.h new file mode 100644 index 00000000000..b97bca3f18d --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/RayTriangle8.h @@ -0,0 +1,91 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +/// Intersect ray with 8 triangles in SOA format, returns 8 vector of closest points or FLT_MAX if no hit +JPH_INLINE Vec8 RayTriangle8(Vec3Arg inOrigin, Vec3Arg inDirection, Vec8Arg inV0X, Vec8Arg inV0Y, Vec8Arg inV0Z, Vec8Arg inV1X, Vec8Arg inV1Y, Vec8Arg inV1Z, Vec8Arg inV2X, Vec8Arg inV2Y, Vec8Arg inV2Z) +{ + // Epsilon + Vec8 epsilon = Vec8::sReplicate(1.0e-12f); + + // Zero & one + Vec8 zero = Vec8::sZero(); + Vec8 one = Vec8::sReplicate(1.0f); + + // Find vectors for two edges sharing inV0 + Vec8 e1x = inV1X - inV0X; + Vec8 e1y = inV1Y - inV0Y; + Vec8 e1z = inV1Z - inV0Z; + Vec8 e2x = inV2X - inV0X; + Vec8 e2y = inV2Y - inV0Y; + Vec8 e2z = inV2Z - inV0Z; + + // Get direction vector components + Vec8 dx = Vec8::sSplatX(Vec4(inDirection)); + Vec8 dy = Vec8::sSplatY(Vec4(inDirection)); + Vec8 dz = Vec8::sSplatZ(Vec4(inDirection)); + + // Begin calculating determinant - also used to calculate u parameter + Vec8 px = dy * e2z - dz * e2y; + Vec8 py = dz * e2x - dx * e2z; + Vec8 pz = dx * e2y - dy * e2x; + + // if determinant is near zero, ray lies in plane of triangle + Vec8 det = e1x * px + e1y * py + e1z * pz; + + // Check which determinants are near zero + UVec8 det_near_zero = Vec8::sLess(det.Abs(), epsilon); + + // Set components of the determinant to 1 that are near zero to avoid dividing by zero + det = Vec8::sSelect(det, Vec8::sReplicate(1.0f), det_near_zero); + + // Calculate distance from inV0 to ray origin + Vec8 sx = Vec8::sSplatX(Vec4(inOrigin)) - inV0X; + Vec8 sy = Vec8::sSplatY(Vec4(inOrigin)) - inV0Y; + Vec8 sz = Vec8::sSplatZ(Vec4(inOrigin)) - inV0Z; + + // Calculate u parameter and flip sign if determinant was negative + Vec8 u = (sx * px + sy * py + sz * pz) / det; + + // Prepare to test v parameter + Vec8 qx = sy * e1z - sz * e1y; + Vec8 qy = sz * e1x - sx * e1z; + Vec8 qz = sx * e1y - sy * e1x; + + // Calculate v parameter and flip sign if determinant was negative + Vec8 v = (dx * qx + dy * qy + dz * qz) / det; + + // Get intersection point and flip sign if determinant was negative + Vec8 t = (e2x * qx + e2y * qy + e2z * qz) / det; + + // Check if there is an intersection + UVec8 no_intersection = + UVec8::sOr + ( + UVec8::sOr + ( + UVec8::sOr + ( + det_near_zero, + Vec8::sLess(u, zero) + ), + UVec8::sOr + ( + Vec8::sLess(v, zero), + Vec8::sGreater(u + v, one) + ) + ), + Vec8::sLess(t, zero) + ); + + // Select intersection point or FLT_MAX based on if there is an intersection or not + return Vec8::sSelect(t, Vec8::sReplicate(FLT_MAX), no_intersection); +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/Sphere.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/Sphere.h new file mode 100644 index 00000000000..c9aa0b3e290 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/Sphere.h @@ -0,0 +1,72 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +class [[nodiscard]] Sphere +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Constructor + inline Sphere() = default; + inline Sphere(const Float3 &inCenter, float inRadius) : mCenter(inCenter), mRadius(inRadius) { } + inline Sphere(Vec3Arg inCenter, float inRadius) : mRadius(inRadius) { inCenter.StoreFloat3(&mCenter); } + + /// Calculate the support vector for this convex shape. + inline Vec3 GetSupport(Vec3Arg inDirection) const + { + float length = inDirection.Length(); + return length > 0.0f ? Vec3::sLoadFloat3Unsafe(mCenter) + (mRadius/ length) * inDirection : Vec3::sLoadFloat3Unsafe(mCenter); + } + + // Properties + inline Vec3 GetCenter() const { return Vec3::sLoadFloat3Unsafe(mCenter); } + inline float GetRadius() const { return mRadius; } + + /// Test if two spheres overlap + inline bool Overlaps(const Sphere &inB) const + { + return (Vec3::sLoadFloat3Unsafe(mCenter) - Vec3::sLoadFloat3Unsafe(inB.mCenter)).LengthSq() <= Square(mRadius + inB.mRadius); + } + + /// Check if this sphere overlaps with a box + inline bool Overlaps(const AABox &inOther) const + { + return inOther.GetSqDistanceTo(GetCenter()) <= Square(mRadius); + } + + /// Create the minimal sphere that encapsulates this sphere and inPoint + inline void EncapsulatePoint(Vec3Arg inPoint) + { + // Calculate distance between point and center + Vec3 center = GetCenter(); + Vec3 d_vec = inPoint - center; + float d_sq = d_vec.LengthSq(); + if (d_sq > Square(mRadius)) + { + // It is further away than radius, we need to widen the sphere + // The diameter of the new sphere is radius + d, so the new radius is half of that + float d = sqrt(d_sq); + float radius = 0.5f * (mRadius + d); + + // The center needs to shift by new radius - old radius in the direction of d + center += (radius - mRadius) / d * d_vec; + + // Store new sphere + center.StoreFloat3(&mCenter); + mRadius = radius; + } + } + +private: + Float3 mCenter; + float mRadius; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/Triangle.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/Triangle.h new file mode 100644 index 00000000000..a88d08c1ff5 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/Triangle.h @@ -0,0 +1,34 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +JPH_NAMESPACE_BEGIN + +/// A simple triangle and its material +class Triangle +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Constructor + Triangle() = default; + Triangle(const Float3 &inV1, const Float3 &inV2, const Float3 &inV3) : mV { inV1, inV2, inV3 } { } + Triangle(const Float3 &inV1, const Float3 &inV2, const Float3 &inV3, uint32 inMaterialIndex) : Triangle(inV1, inV2, inV3) { mMaterialIndex = inMaterialIndex; } + Triangle(Vec3Arg inV1, Vec3Arg inV2, Vec3Arg inV3) { inV1.StoreFloat3(&mV[0]); inV2.StoreFloat3(&mV[1]); inV3.StoreFloat3(&mV[2]); } + + /// Get center of triangle + Vec3 GetCentroid() const + { + return (Vec3::sLoadFloat3Unsafe(mV[0]) + Vec3::sLoadFloat3Unsafe(mV[1]) + Vec3::sLoadFloat3Unsafe(mV[2])) * (1.0f / 3.0f); + } + + /// Vertices + Float3 mV[3]; + uint32 mMaterialIndex = 0; ///< Follows mV[3] so that we can read mV as 4 vectors +}; + +using TriangleList = Array; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Jolt.cmake b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Jolt.cmake new file mode 100644 index 00000000000..2b2828428ca --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Jolt.cmake @@ -0,0 +1,634 @@ +# Requires C++ 17 +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +# Root +set(JOLT_PHYSICS_ROOT ${PHYSICS_REPO_ROOT}/Jolt) + +# Source files +set(JOLT_PHYSICS_SRC_FILES + ${JOLT_PHYSICS_ROOT}/AABBTree/AABBTreeBuilder.cpp + ${JOLT_PHYSICS_ROOT}/AABBTree/AABBTreeBuilder.h + ${JOLT_PHYSICS_ROOT}/AABBTree/AABBTreeToBuffer.h + ${JOLT_PHYSICS_ROOT}/AABBTree/NodeCodec/NodeCodecQuadTreeHalfFloat.h + ${JOLT_PHYSICS_ROOT}/AABBTree/TriangleCodec/TriangleCodecIndexed8BitPackSOA4Flags.h + ${JOLT_PHYSICS_ROOT}/Core/ARMNeon.h + ${JOLT_PHYSICS_ROOT}/Core/Atomics.h + ${JOLT_PHYSICS_ROOT}/Core/ByteBuffer.h + ${JOLT_PHYSICS_ROOT}/Core/Color.cpp + ${JOLT_PHYSICS_ROOT}/Core/Color.h + ${JOLT_PHYSICS_ROOT}/Core/Core.h + ${JOLT_PHYSICS_ROOT}/Core/Factory.cpp + ${JOLT_PHYSICS_ROOT}/Core/Factory.h + ${JOLT_PHYSICS_ROOT}/Core/FixedSizeFreeList.h + ${JOLT_PHYSICS_ROOT}/Core/FixedSizeFreeList.inl + ${JOLT_PHYSICS_ROOT}/Core/FPControlWord.h + ${JOLT_PHYSICS_ROOT}/Core/FPException.h + ${JOLT_PHYSICS_ROOT}/Core/FPFlushDenormals.h + ${JOLT_PHYSICS_ROOT}/Core/HashCombine.h + ${JOLT_PHYSICS_ROOT}/Core/InsertionSort.h + ${JOLT_PHYSICS_ROOT}/Core/IssueReporting.cpp + ${JOLT_PHYSICS_ROOT}/Core/IssueReporting.h + ${JOLT_PHYSICS_ROOT}/Core/JobSystem.h + ${JOLT_PHYSICS_ROOT}/Core/JobSystem.inl + ${JOLT_PHYSICS_ROOT}/Core/JobSystemSingleThreaded.cpp + ${JOLT_PHYSICS_ROOT}/Core/JobSystemSingleThreaded.h + ${JOLT_PHYSICS_ROOT}/Core/JobSystemThreadPool.cpp + ${JOLT_PHYSICS_ROOT}/Core/JobSystemThreadPool.h + ${JOLT_PHYSICS_ROOT}/Core/JobSystemWithBarrier.cpp + ${JOLT_PHYSICS_ROOT}/Core/JobSystemWithBarrier.h + ${JOLT_PHYSICS_ROOT}/Core/LinearCurve.cpp + ${JOLT_PHYSICS_ROOT}/Core/LinearCurve.h + ${JOLT_PHYSICS_ROOT}/Core/LockFreeHashMap.h + ${JOLT_PHYSICS_ROOT}/Core/LockFreeHashMap.inl + ${JOLT_PHYSICS_ROOT}/Core/Memory.cpp + ${JOLT_PHYSICS_ROOT}/Core/Memory.h + ${JOLT_PHYSICS_ROOT}/Core/Mutex.h + ${JOLT_PHYSICS_ROOT}/Core/MutexArray.h + ${JOLT_PHYSICS_ROOT}/Core/NonCopyable.h + ${JOLT_PHYSICS_ROOT}/Core/Profiler.cpp + ${JOLT_PHYSICS_ROOT}/Core/Profiler.h + ${JOLT_PHYSICS_ROOT}/Core/Profiler.inl + ${JOLT_PHYSICS_ROOT}/Core/QuickSort.h + ${JOLT_PHYSICS_ROOT}/Core/Reference.h + ${JOLT_PHYSICS_ROOT}/Core/Result.h + ${JOLT_PHYSICS_ROOT}/Core/RTTI.cpp + ${JOLT_PHYSICS_ROOT}/Core/RTTI.h + ${JOLT_PHYSICS_ROOT}/Core/Semaphore.cpp + ${JOLT_PHYSICS_ROOT}/Core/Semaphore.h + ${JOLT_PHYSICS_ROOT}/Core/StaticArray.h + ${JOLT_PHYSICS_ROOT}/Core/StreamIn.h + ${JOLT_PHYSICS_ROOT}/Core/StreamOut.h + ${JOLT_PHYSICS_ROOT}/Core/StreamUtils.h + ${JOLT_PHYSICS_ROOT}/Core/StreamWrapper.h + ${JOLT_PHYSICS_ROOT}/Core/StringTools.cpp + ${JOLT_PHYSICS_ROOT}/Core/StringTools.h + ${JOLT_PHYSICS_ROOT}/Core/STLAlignedAllocator.h + ${JOLT_PHYSICS_ROOT}/Core/STLAllocator.h + ${JOLT_PHYSICS_ROOT}/Core/STLTempAllocator.h + ${JOLT_PHYSICS_ROOT}/Core/TempAllocator.h + ${JOLT_PHYSICS_ROOT}/Core/TickCounter.cpp + ${JOLT_PHYSICS_ROOT}/Core/TickCounter.h + ${JOLT_PHYSICS_ROOT}/Core/UnorderedMap.h + ${JOLT_PHYSICS_ROOT}/Core/UnorderedSet.h + ${JOLT_PHYSICS_ROOT}/Geometry/AABox.h + ${JOLT_PHYSICS_ROOT}/Geometry/AABox4.h + ${JOLT_PHYSICS_ROOT}/Geometry/ClipPoly.h + ${JOLT_PHYSICS_ROOT}/Geometry/ClosestPoint.h + ${JOLT_PHYSICS_ROOT}/Geometry/ConvexHullBuilder.cpp + ${JOLT_PHYSICS_ROOT}/Geometry/ConvexHullBuilder.h + ${JOLT_PHYSICS_ROOT}/Geometry/ConvexHullBuilder2D.cpp + ${JOLT_PHYSICS_ROOT}/Geometry/ConvexHullBuilder2D.h + ${JOLT_PHYSICS_ROOT}/Geometry/ConvexSupport.h + ${JOLT_PHYSICS_ROOT}/Geometry/Ellipse.h + ${JOLT_PHYSICS_ROOT}/Geometry/EPAConvexHullBuilder.h + ${JOLT_PHYSICS_ROOT}/Geometry/EPAPenetrationDepth.h + ${JOLT_PHYSICS_ROOT}/Geometry/GJKClosestPoint.h + ${JOLT_PHYSICS_ROOT}/Geometry/IndexedTriangle.h + ${JOLT_PHYSICS_ROOT}/Geometry/Indexify.cpp + ${JOLT_PHYSICS_ROOT}/Geometry/Indexify.h + ${JOLT_PHYSICS_ROOT}/Geometry/MortonCode.h + ${JOLT_PHYSICS_ROOT}/Geometry/OrientedBox.cpp + ${JOLT_PHYSICS_ROOT}/Geometry/OrientedBox.h + ${JOLT_PHYSICS_ROOT}/Geometry/Plane.h + ${JOLT_PHYSICS_ROOT}/Geometry/RayAABox.h + ${JOLT_PHYSICS_ROOT}/Geometry/RayAABox8.h + ${JOLT_PHYSICS_ROOT}/Geometry/RayCapsule.h + ${JOLT_PHYSICS_ROOT}/Geometry/RayCylinder.h + ${JOLT_PHYSICS_ROOT}/Geometry/RaySphere.h + ${JOLT_PHYSICS_ROOT}/Geometry/RayTriangle.h + ${JOLT_PHYSICS_ROOT}/Geometry/RayTriangle8.h + ${JOLT_PHYSICS_ROOT}/Geometry/Sphere.h + ${JOLT_PHYSICS_ROOT}/Geometry/Triangle.h + ${JOLT_PHYSICS_ROOT}/Jolt.cmake + ${JOLT_PHYSICS_ROOT}/Jolt.h + ${JOLT_PHYSICS_ROOT}/Math/DMat44.h + ${JOLT_PHYSICS_ROOT}/Math/DMat44.inl + ${JOLT_PHYSICS_ROOT}/Math/Double3.h + ${JOLT_PHYSICS_ROOT}/Math/DVec3.h + ${JOLT_PHYSICS_ROOT}/Math/DVec3.inl + ${JOLT_PHYSICS_ROOT}/Math/DynMatrix.h + ${JOLT_PHYSICS_ROOT}/Math/EigenValueSymmetric.h + ${JOLT_PHYSICS_ROOT}/Math/FindRoot.h + ${JOLT_PHYSICS_ROOT}/Math/Float2.h + ${JOLT_PHYSICS_ROOT}/Math/Float3.h + ${JOLT_PHYSICS_ROOT}/Math/Float4.h + ${JOLT_PHYSICS_ROOT}/Math/GaussianElimination.h + ${JOLT_PHYSICS_ROOT}/Math/HalfFloat.h + ${JOLT_PHYSICS_ROOT}/Math/Mat44.h + ${JOLT_PHYSICS_ROOT}/Math/Mat44.inl + ${JOLT_PHYSICS_ROOT}/Math/Math.h + ${JOLT_PHYSICS_ROOT}/Math/MathTypes.h + ${JOLT_PHYSICS_ROOT}/Math/Matrix.h + ${JOLT_PHYSICS_ROOT}/Math/Quat.h + ${JOLT_PHYSICS_ROOT}/Math/Quat.inl + ${JOLT_PHYSICS_ROOT}/Math/Real.h + ${JOLT_PHYSICS_ROOT}/Math/Swizzle.h + ${JOLT_PHYSICS_ROOT}/Math/Trigonometry.h + ${JOLT_PHYSICS_ROOT}/Math/UVec4.h + ${JOLT_PHYSICS_ROOT}/Math/UVec4.inl + ${JOLT_PHYSICS_ROOT}/Math/UVec8.h + ${JOLT_PHYSICS_ROOT}/Math/UVec8.inl + ${JOLT_PHYSICS_ROOT}/Math/Vec3.cpp + ${JOLT_PHYSICS_ROOT}/Math/Vec3.h + ${JOLT_PHYSICS_ROOT}/Math/Vec3.inl + ${JOLT_PHYSICS_ROOT}/Math/Vec4.h + ${JOLT_PHYSICS_ROOT}/Math/Vec4.inl + ${JOLT_PHYSICS_ROOT}/Math/Vec8.h + ${JOLT_PHYSICS_ROOT}/Math/Vec8.inl + ${JOLT_PHYSICS_ROOT}/Math/Vector.h + ${JOLT_PHYSICS_ROOT}/ObjectStream/GetPrimitiveTypeOfType.h + ${JOLT_PHYSICS_ROOT}/ObjectStream/ObjectStream.cpp + ${JOLT_PHYSICS_ROOT}/ObjectStream/ObjectStream.h + ${JOLT_PHYSICS_ROOT}/ObjectStream/ObjectStreamBinaryIn.cpp + ${JOLT_PHYSICS_ROOT}/ObjectStream/ObjectStreamBinaryIn.h + ${JOLT_PHYSICS_ROOT}/ObjectStream/ObjectStreamBinaryOut.cpp + ${JOLT_PHYSICS_ROOT}/ObjectStream/ObjectStreamBinaryOut.h + ${JOLT_PHYSICS_ROOT}/ObjectStream/ObjectStreamIn.cpp + ${JOLT_PHYSICS_ROOT}/ObjectStream/ObjectStreamIn.h + ${JOLT_PHYSICS_ROOT}/ObjectStream/ObjectStreamOut.cpp + ${JOLT_PHYSICS_ROOT}/ObjectStream/ObjectStreamOut.h + ${JOLT_PHYSICS_ROOT}/ObjectStream/ObjectStreamTextIn.cpp + ${JOLT_PHYSICS_ROOT}/ObjectStream/ObjectStreamTextIn.h + ${JOLT_PHYSICS_ROOT}/ObjectStream/ObjectStreamTextOut.cpp + ${JOLT_PHYSICS_ROOT}/ObjectStream/ObjectStreamTextOut.h + ${JOLT_PHYSICS_ROOT}/ObjectStream/ObjectStreamTypes.h + ${JOLT_PHYSICS_ROOT}/ObjectStream/SerializableAttribute.h + ${JOLT_PHYSICS_ROOT}/ObjectStream/SerializableAttributeEnum.h + ${JOLT_PHYSICS_ROOT}/ObjectStream/SerializableAttributeTyped.h + ${JOLT_PHYSICS_ROOT}/ObjectStream/SerializableObject.cpp + ${JOLT_PHYSICS_ROOT}/ObjectStream/SerializableObject.h + ${JOLT_PHYSICS_ROOT}/ObjectStream/TypeDeclarations.cpp + ${JOLT_PHYSICS_ROOT}/ObjectStream/TypeDeclarations.h + ${JOLT_PHYSICS_ROOT}/Physics/Body/AllowedDOFs.h + ${JOLT_PHYSICS_ROOT}/Physics/Body/Body.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Body/Body.h + ${JOLT_PHYSICS_ROOT}/Physics/Body/Body.inl + ${JOLT_PHYSICS_ROOT}/Physics/Body/BodyAccess.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Body/BodyAccess.h + ${JOLT_PHYSICS_ROOT}/Physics/Body/BodyActivationListener.h + ${JOLT_PHYSICS_ROOT}/Physics/Body/BodyCreationSettings.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Body/BodyCreationSettings.h + ${JOLT_PHYSICS_ROOT}/Physics/Body/BodyFilter.h + ${JOLT_PHYSICS_ROOT}/Physics/Body/BodyID.h + ${JOLT_PHYSICS_ROOT}/Physics/Body/BodyInterface.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Body/BodyInterface.h + ${JOLT_PHYSICS_ROOT}/Physics/Body/BodyLock.h + ${JOLT_PHYSICS_ROOT}/Physics/Body/BodyLockInterface.h + ${JOLT_PHYSICS_ROOT}/Physics/Body/BodyLockMulti.h + ${JOLT_PHYSICS_ROOT}/Physics/Body/BodyManager.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Body/BodyManager.h + ${JOLT_PHYSICS_ROOT}/Physics/Body/BodyPair.h + ${JOLT_PHYSICS_ROOT}/Physics/Body/BodyType.h + ${JOLT_PHYSICS_ROOT}/Physics/Body/MassProperties.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Body/MassProperties.h + ${JOLT_PHYSICS_ROOT}/Physics/Body/MotionProperties.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Body/MotionProperties.h + ${JOLT_PHYSICS_ROOT}/Physics/Body/MotionProperties.inl + ${JOLT_PHYSICS_ROOT}/Physics/Body/MotionQuality.h + ${JOLT_PHYSICS_ROOT}/Physics/Body/MotionType.h + ${JOLT_PHYSICS_ROOT}/Physics/Character/Character.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Character/Character.h + ${JOLT_PHYSICS_ROOT}/Physics/Character/CharacterBase.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Character/CharacterBase.h + ${JOLT_PHYSICS_ROOT}/Physics/Character/CharacterVirtual.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Character/CharacterVirtual.h + ${JOLT_PHYSICS_ROOT}/Physics/Collision/AABoxCast.h + ${JOLT_PHYSICS_ROOT}/Physics/Collision/ActiveEdgeMode.h + ${JOLT_PHYSICS_ROOT}/Physics/Collision/ActiveEdges.h + ${JOLT_PHYSICS_ROOT}/Physics/Collision/BackFaceMode.h + ${JOLT_PHYSICS_ROOT}/Physics/Collision/BroadPhase/BroadPhase.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Collision/BroadPhase/BroadPhase.h + ${JOLT_PHYSICS_ROOT}/Physics/Collision/BroadPhase/BroadPhaseBruteForce.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Collision/BroadPhase/BroadPhaseBruteForce.h + ${JOLT_PHYSICS_ROOT}/Physics/Collision/BroadPhase/BroadPhaseLayer.h + ${JOLT_PHYSICS_ROOT}/Physics/Collision/BroadPhase/BroadPhaseLayerInterfaceMask.h + ${JOLT_PHYSICS_ROOT}/Physics/Collision/BroadPhase/BroadPhaseLayerInterfaceTable.h + ${JOLT_PHYSICS_ROOT}/Physics/Collision/BroadPhase/BroadPhaseQuadTree.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Collision/BroadPhase/BroadPhaseQuadTree.h + ${JOLT_PHYSICS_ROOT}/Physics/Collision/BroadPhase/BroadPhaseQuery.h + ${JOLT_PHYSICS_ROOT}/Physics/Collision/BroadPhase/ObjectVsBroadPhaseLayerFilterMask.h + ${JOLT_PHYSICS_ROOT}/Physics/Collision/BroadPhase/ObjectVsBroadPhaseLayerFilterTable.h + ${JOLT_PHYSICS_ROOT}/Physics/Collision/BroadPhase/QuadTree.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Collision/BroadPhase/QuadTree.h + ${JOLT_PHYSICS_ROOT}/Physics/Collision/CastConvexVsTriangles.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Collision/CastConvexVsTriangles.h + ${JOLT_PHYSICS_ROOT}/Physics/Collision/CastSphereVsTriangles.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Collision/CastSphereVsTriangles.h + ${JOLT_PHYSICS_ROOT}/Physics/Collision/CastResult.h + ${JOLT_PHYSICS_ROOT}/Physics/Collision/CollectFacesMode.h + ${JOLT_PHYSICS_ROOT}/Physics/Collision/CollideConvexVsTriangles.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Collision/CollideConvexVsTriangles.h + ${JOLT_PHYSICS_ROOT}/Physics/Collision/CollidePointResult.h + ${JOLT_PHYSICS_ROOT}/Physics/Collision/CollideShape.h + ${JOLT_PHYSICS_ROOT}/Physics/Collision/CollideSoftBodyVerticesVsTriangles.h + ${JOLT_PHYSICS_ROOT}/Physics/Collision/CollideSphereVsTriangles.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Collision/CollideSphereVsTriangles.h + ${JOLT_PHYSICS_ROOT}/Physics/Collision/CollisionCollector.h + ${JOLT_PHYSICS_ROOT}/Physics/Collision/CollisionCollectorImpl.h + ${JOLT_PHYSICS_ROOT}/Physics/Collision/CollisionDispatch.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Collision/CollisionDispatch.h + ${JOLT_PHYSICS_ROOT}/Physics/Collision/CollisionGroup.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Collision/CollisionGroup.h + ${JOLT_PHYSICS_ROOT}/Physics/Collision/ContactListener.h + ${JOLT_PHYSICS_ROOT}/Physics/Collision/EstimateCollisionResponse.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Collision/EstimateCollisionResponse.h + ${JOLT_PHYSICS_ROOT}/Physics/Collision/GroupFilter.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Collision/GroupFilter.h + ${JOLT_PHYSICS_ROOT}/Physics/Collision/GroupFilterTable.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Collision/GroupFilterTable.h + ${JOLT_PHYSICS_ROOT}/Physics/Collision/InternalEdgeRemovingCollector.h + ${JOLT_PHYSICS_ROOT}/Physics/Collision/ManifoldBetweenTwoFaces.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Collision/ManifoldBetweenTwoFaces.h + ${JOLT_PHYSICS_ROOT}/Physics/Collision/NarrowPhaseQuery.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Collision/NarrowPhaseQuery.h + ${JOLT_PHYSICS_ROOT}/Physics/Collision/NarrowPhaseStats.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Collision/NarrowPhaseStats.h + ${JOLT_PHYSICS_ROOT}/Physics/Collision/ObjectLayer.h + ${JOLT_PHYSICS_ROOT}/Physics/Collision/ObjectLayerPairFilterMask.h + ${JOLT_PHYSICS_ROOT}/Physics/Collision/ObjectLayerPairFilterTable.h + ${JOLT_PHYSICS_ROOT}/Physics/Collision/PhysicsMaterial.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Collision/PhysicsMaterial.h + ${JOLT_PHYSICS_ROOT}/Physics/Collision/PhysicsMaterialSimple.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Collision/PhysicsMaterialSimple.h + ${JOLT_PHYSICS_ROOT}/Physics/Collision/RayCast.h + ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/BoxShape.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/BoxShape.h + ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/CapsuleShape.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/CapsuleShape.h + ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/CompoundShape.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/CompoundShape.h + ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/CompoundShapeVisitors.h + ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/ConvexHullShape.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/ConvexHullShape.h + ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/ConvexShape.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/ConvexShape.h + ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/CylinderShape.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/CylinderShape.h + ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/DecoratedShape.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/DecoratedShape.h + ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/GetTrianglesContext.h + ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/HeightFieldShape.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/HeightFieldShape.h + ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/MeshShape.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/MeshShape.h + ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/MutableCompoundShape.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/MutableCompoundShape.h + ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/OffsetCenterOfMassShape.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/OffsetCenterOfMassShape.h + ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/PolyhedronSubmergedVolumeCalculator.h + ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/RotatedTranslatedShape.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/RotatedTranslatedShape.h + ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/ScaledShape.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/ScaledShape.h + ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/ScaleHelpers.h + ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/Shape.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/Shape.h + ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/SphereShape.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/SphereShape.h + ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/StaticCompoundShape.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/StaticCompoundShape.h + ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/SubShapeID.h + ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/SubShapeIDPair.h + ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/TaperedCapsuleShape.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/TaperedCapsuleShape.gliffy + ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/TaperedCapsuleShape.h + ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/TriangleShape.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/TriangleShape.h + ${JOLT_PHYSICS_ROOT}/Physics/Collision/ShapeCast.h + ${JOLT_PHYSICS_ROOT}/Physics/Collision/ShapeFilter.h + ${JOLT_PHYSICS_ROOT}/Physics/Collision/SortReverseAndStore.h + ${JOLT_PHYSICS_ROOT}/Physics/Collision/TransformedShape.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Collision/TransformedShape.h + ${JOLT_PHYSICS_ROOT}/Physics/Constraints/CalculateSolverSteps.h + ${JOLT_PHYSICS_ROOT}/Physics/Constraints/ConeConstraint.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Constraints/ConeConstraint.h + ${JOLT_PHYSICS_ROOT}/Physics/Constraints/Constraint.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Constraints/Constraint.h + ${JOLT_PHYSICS_ROOT}/Physics/Constraints/ConstraintManager.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Constraints/ConstraintManager.h + ${JOLT_PHYSICS_ROOT}/Physics/Constraints/ConstraintPart/AngleConstraintPart.h + ${JOLT_PHYSICS_ROOT}/Physics/Constraints/ConstraintPart/AxisConstraintPart.h + ${JOLT_PHYSICS_ROOT}/Physics/Constraints/ConstraintPart/DualAxisConstraintPart.h + ${JOLT_PHYSICS_ROOT}/Physics/Constraints/ConstraintPart/GearConstraintPart.h + ${JOLT_PHYSICS_ROOT}/Physics/Constraints/ConstraintPart/HingeRotationConstraintPart.h + ${JOLT_PHYSICS_ROOT}/Physics/Constraints/ConstraintPart/IndependentAxisConstraintPart.h + ${JOLT_PHYSICS_ROOT}/Physics/Constraints/ConstraintPart/PointConstraintPart.h + ${JOLT_PHYSICS_ROOT}/Physics/Constraints/ConstraintPart/RackAndPinionConstraintPart.h + ${JOLT_PHYSICS_ROOT}/Physics/Constraints/ConstraintPart/RotationEulerConstraintPart.h + ${JOLT_PHYSICS_ROOT}/Physics/Constraints/ConstraintPart/RotationQuatConstraintPart.h + ${JOLT_PHYSICS_ROOT}/Physics/Constraints/ConstraintPart/SpringPart.h + ${JOLT_PHYSICS_ROOT}/Physics/Constraints/ConstraintPart/SwingTwistConstraintPart.h + ${JOLT_PHYSICS_ROOT}/Physics/Constraints/ContactConstraintManager.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Constraints/ContactConstraintManager.h + ${JOLT_PHYSICS_ROOT}/Physics/Constraints/DistanceConstraint.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Constraints/DistanceConstraint.h + ${JOLT_PHYSICS_ROOT}/Physics/Constraints/FixedConstraint.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Constraints/FixedConstraint.h + ${JOLT_PHYSICS_ROOT}/Physics/Constraints/GearConstraint.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Constraints/GearConstraint.h + ${JOLT_PHYSICS_ROOT}/Physics/Constraints/HingeConstraint.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Constraints/HingeConstraint.h + ${JOLT_PHYSICS_ROOT}/Physics/Constraints/MotorSettings.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Constraints/MotorSettings.h + ${JOLT_PHYSICS_ROOT}/Physics/Constraints/PathConstraint.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Constraints/PathConstraint.h + ${JOLT_PHYSICS_ROOT}/Physics/Constraints/PathConstraintPath.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Constraints/PathConstraintPath.h + ${JOLT_PHYSICS_ROOT}/Physics/Constraints/PathConstraintPathHermite.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Constraints/PathConstraintPathHermite.h + ${JOLT_PHYSICS_ROOT}/Physics/Constraints/PointConstraint.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Constraints/PointConstraint.h + ${JOLT_PHYSICS_ROOT}/Physics/Constraints/PulleyConstraint.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Constraints/PulleyConstraint.h + ${JOLT_PHYSICS_ROOT}/Physics/Constraints/RackAndPinionConstraint.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Constraints/RackAndPinionConstraint.h + ${JOLT_PHYSICS_ROOT}/Physics/Constraints/SixDOFConstraint.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Constraints/SixDOFConstraint.h + ${JOLT_PHYSICS_ROOT}/Physics/Constraints/SliderConstraint.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Constraints/SliderConstraint.h + ${JOLT_PHYSICS_ROOT}/Physics/Constraints/SpringSettings.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Constraints/SpringSettings.h + ${JOLT_PHYSICS_ROOT}/Physics/Constraints/SwingTwistConstraint.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Constraints/SwingTwistConstraint.h + ${JOLT_PHYSICS_ROOT}/Physics/Constraints/TwoBodyConstraint.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Constraints/TwoBodyConstraint.h + ${JOLT_PHYSICS_ROOT}/Physics/DeterminismLog.cpp + ${JOLT_PHYSICS_ROOT}/Physics/DeterminismLog.h + ${JOLT_PHYSICS_ROOT}/Physics/EActivation.h + ${JOLT_PHYSICS_ROOT}/Physics/EPhysicsUpdateError.h + ${JOLT_PHYSICS_ROOT}/Physics/IslandBuilder.cpp + ${JOLT_PHYSICS_ROOT}/Physics/IslandBuilder.h + ${JOLT_PHYSICS_ROOT}/Physics/LargeIslandSplitter.cpp + ${JOLT_PHYSICS_ROOT}/Physics/LargeIslandSplitter.h + ${JOLT_PHYSICS_ROOT}/Physics/PhysicsLock.cpp + ${JOLT_PHYSICS_ROOT}/Physics/PhysicsLock.h + ${JOLT_PHYSICS_ROOT}/Physics/PhysicsScene.cpp + ${JOLT_PHYSICS_ROOT}/Physics/PhysicsScene.h + ${JOLT_PHYSICS_ROOT}/Physics/PhysicsSettings.h + ${JOLT_PHYSICS_ROOT}/Physics/PhysicsStepListener.h + ${JOLT_PHYSICS_ROOT}/Physics/PhysicsSystem.cpp + ${JOLT_PHYSICS_ROOT}/Physics/PhysicsSystem.h + ${JOLT_PHYSICS_ROOT}/Physics/PhysicsUpdateContext.cpp + ${JOLT_PHYSICS_ROOT}/Physics/PhysicsUpdateContext.h + ${JOLT_PHYSICS_ROOT}/Physics/Ragdoll/Ragdoll.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Ragdoll/Ragdoll.h + ${JOLT_PHYSICS_ROOT}/Physics/SoftBody/SoftBodyContactListener.h + ${JOLT_PHYSICS_ROOT}/Physics/SoftBody/SoftBodyCreationSettings.cpp + ${JOLT_PHYSICS_ROOT}/Physics/SoftBody/SoftBodyCreationSettings.h + ${JOLT_PHYSICS_ROOT}/Physics/SoftBody/SoftBodyManifold.h + ${JOLT_PHYSICS_ROOT}/Physics/SoftBody/SoftBodyMotionProperties.h + ${JOLT_PHYSICS_ROOT}/Physics/SoftBody/SoftBodyMotionProperties.cpp + ${JOLT_PHYSICS_ROOT}/Physics/SoftBody/SoftBodyShape.cpp + ${JOLT_PHYSICS_ROOT}/Physics/SoftBody/SoftBodyShape.h + ${JOLT_PHYSICS_ROOT}/Physics/SoftBody/SoftBodySharedSettings.cpp + ${JOLT_PHYSICS_ROOT}/Physics/SoftBody/SoftBodySharedSettings.h + ${JOLT_PHYSICS_ROOT}/Physics/SoftBody/SoftBodyVertex.h + ${JOLT_PHYSICS_ROOT}/Physics/StateRecorder.h + ${JOLT_PHYSICS_ROOT}/Physics/StateRecorderImpl.cpp + ${JOLT_PHYSICS_ROOT}/Physics/StateRecorderImpl.h + ${JOLT_PHYSICS_ROOT}/Physics/Vehicle/MotorcycleController.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Vehicle/MotorcycleController.h + ${JOLT_PHYSICS_ROOT}/Physics/Vehicle/TrackedVehicleController.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Vehicle/TrackedVehicleController.h + ${JOLT_PHYSICS_ROOT}/Physics/Vehicle/VehicleAntiRollBar.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Vehicle/VehicleAntiRollBar.h + ${JOLT_PHYSICS_ROOT}/Physics/Vehicle/VehicleCollisionTester.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Vehicle/VehicleCollisionTester.h + ${JOLT_PHYSICS_ROOT}/Physics/Vehicle/VehicleConstraint.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Vehicle/VehicleConstraint.h + ${JOLT_PHYSICS_ROOT}/Physics/Vehicle/VehicleController.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Vehicle/VehicleController.h + ${JOLT_PHYSICS_ROOT}/Physics/Vehicle/VehicleDifferential.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Vehicle/VehicleDifferential.h + ${JOLT_PHYSICS_ROOT}/Physics/Vehicle/VehicleEngine.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Vehicle/VehicleEngine.h + ${JOLT_PHYSICS_ROOT}/Physics/Vehicle/VehicleTrack.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Vehicle/VehicleTrack.h + ${JOLT_PHYSICS_ROOT}/Physics/Vehicle/VehicleTransmission.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Vehicle/VehicleTransmission.h + ${JOLT_PHYSICS_ROOT}/Physics/Vehicle/Wheel.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Vehicle/Wheel.h + ${JOLT_PHYSICS_ROOT}/Physics/Vehicle/WheeledVehicleController.cpp + ${JOLT_PHYSICS_ROOT}/Physics/Vehicle/WheeledVehicleController.h + ${JOLT_PHYSICS_ROOT}/RegisterTypes.cpp + ${JOLT_PHYSICS_ROOT}/RegisterTypes.h + ${JOLT_PHYSICS_ROOT}/Renderer/DebugRenderer.cpp + ${JOLT_PHYSICS_ROOT}/Renderer/DebugRenderer.h + ${JOLT_PHYSICS_ROOT}/Renderer/DebugRendererPlayback.cpp + ${JOLT_PHYSICS_ROOT}/Renderer/DebugRendererPlayback.h + ${JOLT_PHYSICS_ROOT}/Renderer/DebugRendererRecorder.cpp + ${JOLT_PHYSICS_ROOT}/Renderer/DebugRendererRecorder.h + ${JOLT_PHYSICS_ROOT}/Renderer/DebugRendererSimple.cpp + ${JOLT_PHYSICS_ROOT}/Renderer/DebugRendererSimple.h + ${JOLT_PHYSICS_ROOT}/Skeleton/SkeletalAnimation.cpp + ${JOLT_PHYSICS_ROOT}/Skeleton/SkeletalAnimation.h + ${JOLT_PHYSICS_ROOT}/Skeleton/Skeleton.cpp + ${JOLT_PHYSICS_ROOT}/Skeleton/Skeleton.h + ${JOLT_PHYSICS_ROOT}/Skeleton/SkeletonMapper.cpp + ${JOLT_PHYSICS_ROOT}/Skeleton/SkeletonMapper.h + ${JOLT_PHYSICS_ROOT}/Skeleton/SkeletonPose.cpp + ${JOLT_PHYSICS_ROOT}/Skeleton/SkeletonPose.h + ${JOLT_PHYSICS_ROOT}/TriangleGrouper/TriangleGrouper.h + ${JOLT_PHYSICS_ROOT}/TriangleGrouper/TriangleGrouperClosestCentroid.cpp + ${JOLT_PHYSICS_ROOT}/TriangleGrouper/TriangleGrouperClosestCentroid.h + ${JOLT_PHYSICS_ROOT}/TriangleGrouper/TriangleGrouperMorton.cpp + ${JOLT_PHYSICS_ROOT}/TriangleGrouper/TriangleGrouperMorton.h + ${JOLT_PHYSICS_ROOT}/TriangleSplitter/TriangleSplitter.cpp + ${JOLT_PHYSICS_ROOT}/TriangleSplitter/TriangleSplitter.h + ${JOLT_PHYSICS_ROOT}/TriangleSplitter/TriangleSplitterBinning.cpp + ${JOLT_PHYSICS_ROOT}/TriangleSplitter/TriangleSplitterBinning.h + ${JOLT_PHYSICS_ROOT}/TriangleSplitter/TriangleSplitterFixedLeafSize.cpp + ${JOLT_PHYSICS_ROOT}/TriangleSplitter/TriangleSplitterFixedLeafSize.h + ${JOLT_PHYSICS_ROOT}/TriangleSplitter/TriangleSplitterLongestAxis.cpp + ${JOLT_PHYSICS_ROOT}/TriangleSplitter/TriangleSplitterLongestAxis.h + ${JOLT_PHYSICS_ROOT}/TriangleSplitter/TriangleSplitterMean.cpp + ${JOLT_PHYSICS_ROOT}/TriangleSplitter/TriangleSplitterMean.h + ${JOLT_PHYSICS_ROOT}/TriangleSplitter/TriangleSplitterMorton.cpp + ${JOLT_PHYSICS_ROOT}/TriangleSplitter/TriangleSplitterMorton.h +) + +if ("${CMAKE_SYSTEM_NAME}" STREQUAL "Windows") + # Add natvis file + set(JOLT_PHYSICS_SRC_FILES ${JOLT_PHYSICS_SRC_FILES} ${JOLT_PHYSICS_ROOT}/Jolt.natvis) +endif() + +# Group source files +source_group(TREE ${JOLT_PHYSICS_ROOT} FILES ${JOLT_PHYSICS_SRC_FILES}) + +# Create Jolt lib +add_library(Jolt ${JOLT_PHYSICS_SRC_FILES}) + +if (BUILD_SHARED_LIBS) + # Set default visibility to hidden + set(CMAKE_CXX_VISIBILITY_PRESET hidden) + + if (GENERATE_DEBUG_SYMBOLS) + if (MSVC) + # MSVC specific option to enable PDB generation + set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} /DEBUG:FASTLINK") + else() + # Clang/GCC option to enable debug symbol generation + set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} -g") + endif() + endif() + + # Set linker flags for other build types to be the same as release + set(CMAKE_SHARED_LINKER_FLAGS_RELEASEASAN "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") + set(CMAKE_SHARED_LINKER_FLAGS_RELEASEUBSAN "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") + set(CMAKE_SHARED_LINKER_FLAGS_RELEASECOVERAGE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") + set(CMAKE_SHARED_LINKER_FLAGS_DISTRIBUTION "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") + + # Public define to instruct user code to import Jolt symbols (rather than use static linking) + target_compile_definitions(Jolt PUBLIC JPH_SHARED_LIBRARY) + + # Private define to instruct the library to export symbols for shared linking + target_compile_definitions(Jolt PRIVATE JPH_BUILD_SHARED_LIBRARY) +endif() + +target_include_directories(Jolt PUBLIC ${PHYSICS_REPO_ROOT}) +target_precompile_headers(Jolt PRIVATE ${JOLT_PHYSICS_ROOT}/Jolt.h) + +# Set the debug/non-debug build flags +target_compile_definitions(Jolt PUBLIC "$<$:_DEBUG>") +target_compile_definitions(Jolt PUBLIC "$<$:NDEBUG>") + +# ASAN should use the default allocators +target_compile_definitions(Jolt PUBLIC "$<$:JPH_DISABLE_TEMP_ALLOCATOR;JPH_DISABLE_CUSTOM_ALLOCATOR>") + +# Setting floating point exceptions +if (FLOATING_POINT_EXCEPTIONS_ENABLED AND "${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") + target_compile_definitions(Jolt PUBLIC "$<$:JPH_FLOATING_POINT_EXCEPTIONS_ENABLED>") +endif() + +# Setting the disable custom allocator flag +if (DISABLE_CUSTOM_ALLOCATOR) + target_compile_definitions(Jolt PUBLIC JPH_DISABLE_CUSTOM_ALLOCATOR) +endif() + +# Setting double precision flag +if (DOUBLE_PRECISION) + target_compile_definitions(Jolt PUBLIC JPH_DOUBLE_PRECISION) +endif() + +# Setting to attempt cross platform determinism +if (CROSS_PLATFORM_DETERMINISTIC) + target_compile_definitions(Jolt PUBLIC JPH_CROSS_PLATFORM_DETERMINISTIC) +endif() + +# Setting to determine number of bits in ObjectLayer +if (OBJECT_LAYER_BITS) + target_compile_definitions(Jolt PUBLIC JPH_OBJECT_LAYER_BITS=${OBJECT_LAYER_BITS}) +endif() + +# Setting to periodically trace broadphase stats to help determine if the broadphase layer configuration is optimal +if (TRACK_BROADPHASE_STATS) + target_compile_definitions(Jolt PUBLIC JPH_TRACK_BROADPHASE_STATS) +endif() + +# Setting to periodically trace narrowphase stats to help determine which collision queries could be optimized +if (TRACK_NARROWPHASE_STATS) + target_compile_definitions(Jolt PUBLIC JPH_TRACK_NARROWPHASE_STATS) +endif() + +# Enable the debug renderer +if (DEBUG_RENDERER_IN_DISTRIBUTION) + target_compile_definitions(Jolt PUBLIC "JPH_DEBUG_RENDERER") +elseif (DEBUG_RENDERER_IN_DEBUG_AND_RELEASE) + target_compile_definitions(Jolt PUBLIC "$<$:JPH_DEBUG_RENDERER>") +endif() + +# Enable the profiler +if (PROFILER_IN_DISTRIBUTION) + target_compile_definitions(Jolt PUBLIC "JPH_PROFILE_ENABLED") +elseif (PROFILER_IN_DEBUG_AND_RELEASE) + target_compile_definitions(Jolt PUBLIC "$<$:JPH_PROFILE_ENABLED>") +endif() + +# Emit the instruction set definitions to ensure that child projects use the same settings even if they override the used instruction sets (a mismatch causes link errors) +function(EMIT_X86_INSTRUCTION_SET_DEFINITIONS) + if (USE_AVX512) + target_compile_definitions(Jolt PUBLIC JPH_USE_AVX512) + endif() + if (USE_AVX2) + target_compile_definitions(Jolt PUBLIC JPH_USE_AVX2) + endif() + if (USE_AVX) + target_compile_definitions(Jolt PUBLIC JPH_USE_AVX) + endif() + if (USE_SSE4_1) + target_compile_definitions(Jolt PUBLIC JPH_USE_SSE4_1) + endif() + if (USE_SSE4_2) + target_compile_definitions(Jolt PUBLIC JPH_USE_SSE4_2) + endif() + if (USE_LZCNT) + target_compile_definitions(Jolt PUBLIC JPH_USE_LZCNT) + endif() + if (USE_TZCNT) + target_compile_definitions(Jolt PUBLIC JPH_USE_TZCNT) + endif() + if (USE_F16C) + target_compile_definitions(Jolt PUBLIC JPH_USE_F16C) + endif() + if (USE_FMADD AND NOT CROSS_PLATFORM_DETERMINISTIC) + target_compile_definitions(Jolt PUBLIC JPH_USE_FMADD) + endif() +endfunction() + +# Add the compiler commandline flags to select the right instruction sets +if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") + if ("${CMAKE_VS_PLATFORM_NAME}" STREQUAL "x86" OR "${CMAKE_VS_PLATFORM_NAME}" STREQUAL "x64") + if (USE_AVX512) + target_compile_options(Jolt PUBLIC /arch:AVX512) + elseif (USE_AVX2) + target_compile_options(Jolt PUBLIC /arch:AVX2) + elseif (USE_AVX) + target_compile_options(Jolt PUBLIC /arch:AVX) + endif() + EMIT_X86_INSTRUCTION_SET_DEFINITIONS() + endif() +else() + if (XCODE) + # XCode builds for multiple architectures, we can't set global flags + elseif (CROSS_COMPILE_ARM OR CMAKE_OSX_ARCHITECTURES MATCHES "arm64" OR "${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "aarch64") + # ARM64 uses no special commandline flags + elseif ("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "x86_64" OR "${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "AMD64") + # x64 + if (USE_AVX512) + target_compile_options(Jolt PUBLIC -mavx512f -mavx512vl -mavx512dq -mavx2 -mbmi -mpopcnt -mlzcnt -mf16c) + elseif (USE_AVX2) + target_compile_options(Jolt PUBLIC -mavx2 -mbmi -mpopcnt -mlzcnt -mf16c) + elseif (USE_AVX) + target_compile_options(Jolt PUBLIC -mavx -mpopcnt) + elseif (USE_SSE4_2) + target_compile_options(Jolt PUBLIC -msse4.2 -mpopcnt) + elseif (USE_SSE4_1) + target_compile_options(Jolt PUBLIC -msse4.1) + else() + target_compile_options(Jolt PUBLIC -msse2) + endif() + if (USE_LZCNT) + target_compile_options(Jolt PUBLIC -mlzcnt) + endif() + if (USE_TZCNT) + target_compile_options(Jolt PUBLIC -mbmi) + endif() + if (USE_F16C) + target_compile_options(Jolt PUBLIC -mf16c) + endif() + if (USE_FMADD AND NOT CROSS_PLATFORM_DETERMINISTIC) + target_compile_options(Jolt PUBLIC -mfma) + endif() + + # On 32-bit builds we need to default to using SSE instructions, the x87 FPU instructions have higher intermediate precision + # which will cause problems in the collision detection code (the effect is similar to leaving FMA on, search for + # JPH_PRECISE_MATH_ON for the locations where this is a problem). + if (NOT MSVC) + target_compile_options(Jolt PUBLIC -mfpmath=sse) + endif() + + EMIT_X86_INSTRUCTION_SET_DEFINITIONS() + endif() +endif() diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Jolt.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Jolt.h new file mode 100644 index 00000000000..10747189599 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Jolt.h @@ -0,0 +1,16 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +// Project includes +#include +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Jolt.natvis b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Jolt.natvis new file mode 100644 index 00000000000..4252263dc02 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Jolt.natvis @@ -0,0 +1,86 @@ + + + + r={(int)r}, g={(int)g}, b={(int)b}, a={(int)a} + + + {x}, {y} + + + {x}, {y}, {z} + + + {x}, {y}, {z}, {w} + + + {mF32[0]}, {mF32[1]}, {mF32[2]}, L^2={mF32[0]*mF32[0]+mF32[1]*mF32[1]+mF32[2]*mF32[2]} + + + {mF64[0]}, {mF64[1]}, {mF64[2]}, L^2={mF64[0]*mF64[0]+mF64[1]*mF64[1]+mF64[2]*mF64[2]} + + + {mF32[0]}, {mF32[1]}, {mF32[2]}, {mF32[3]}, L^2={mF32[0]*mF32[0]+mF32[1]*mF32[1]+mF32[2]*mF32[2]+mF32[3]*mF32[3]} + + + {mU32[0]}, {mU32[1]}, {mU32[2]}, {mU32[3]} + + + {mValue} + + + {mCol[0].mF32[0]}, {mCol[1].mF32[0]}, {mCol[2].mF32[0]}, {mCol[3].mF32[0]} | {mCol[0].mF32[1]}, {mCol[1].mF32[1]}, {mCol[2].mF32[1]}, {mCol[3].mF32[1]} | {mCol[0].mF32[2]}, {mCol[1].mF32[2]}, {mCol[2].mF32[2]}, {mCol[3].mF32[2]} + + + {mCol[0].mF32[0]}, {mCol[1].mF32[0]}, {mCol[2].mF32[0]}, {mCol[3].mF32[0]} + + + {mCol[0].mF32[1]}, {mCol[1].mF32[1]}, {mCol[2].mF32[1]}, {mCol[3].mF32[1]} + + + {mCol[0].mF32[2]}, {mCol[1].mF32[2]}, {mCol[2].mF32[2]}, {mCol[3].mF32[2]} + + + {mCol[0].mF32[3]}, {mCol[1].mF32[3]}, {mCol[2].mF32[3]}, {mCol[3].mF32[3]} + + + + + {mCol[0].mF32[0]}, {mCol[1].mF32[0]}, {mCol[2].mF32[0]}, {mCol3.mF64[0]} | {mCol[0].mF32[1]}, {mCol[1].mF32[1]}, {mCol[2].mF32[1]}, {mCol3.mF64[1]} | {mCol[0].mF32[2]}, {mCol[1].mF32[2]}, {mCol[2].mF32[2]}, {mCol3.mF64[2]} + + + {mCol[0].mF32[0]}, {mCol[1].mF32[0]}, {mCol[2].mF32[0]}, {mCol3.mF64[0]} + + + {mCol[0].mF32[1]}, {mCol[1].mF32[1]}, {mCol[2].mF32[1]}, {mCol3.mF64[1]} + + + {mCol[0].mF32[2]}, {mCol[1].mF32[2]}, {mCol[2].mF32[2]}, {mCol3.mF64[2]} + + + {mCol[0].mF32[3]}, {mCol[1].mF32[3]}, {mCol[2].mF32[3]}, 1} + + + + + min=({mMin}), max=({mMax}) + + + {mID} + + + {mDebugName}: p=({mPosition.mF32[0],g}, {mPosition.mF32[1],g}, {mPosition.mF32[2],g}), r=({mRotation.mValue.mF32[0],g}, {mRotation.mValue.mF32[1],g}, {mRotation.mValue.mF32[2],g}, {mRotation.mValue.mF32[3],g}), v=({mLinearVelocity.mF32[0],g}, {mLinearVelocity.mF32[1],g}, {mLinearVelocity.mF32[2],g}), w=({mAngularVelocity.mF32[0],g}, {mAngularVelocity.mF32[1],g}, {mAngularVelocity.mF32[2],g}) + + + bodies={mBodies._Mypair._Myval2._Mylast - mBodies._Mypair._Myval2._Myfirst}, active={mActiveBodies._Mypair._Myval2._Mylast - mActiveBodies._Mypair._Myval2._Myfirst} + + + size={mSize} + + mSize + + mSize + (value_type *)mElements + + + + diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/DMat44.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/DMat44.h new file mode 100644 index 00000000000..65c9687160b --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/DMat44.h @@ -0,0 +1,158 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2022 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +/// Holds a 4x4 matrix of floats with the last column consisting of doubles +class [[nodiscard]] alignas(JPH_DVECTOR_ALIGNMENT) DMat44 +{ +public: + JPH_OVERRIDE_NEW_DELETE + + // Underlying column type + using Type = Vec4::Type; + using DType = DVec3::Type; + using DTypeArg = DVec3::TypeArg; + + // Argument type + using ArgType = DMat44Arg; + + /// Constructor + DMat44() = default; ///< Intentionally not initialized for performance reasons + JPH_INLINE DMat44(Vec4Arg inC1, Vec4Arg inC2, Vec4Arg inC3, DVec3Arg inC4); + DMat44(const DMat44 &inM2) = default; + DMat44 & operator = (const DMat44 &inM2) = default; + JPH_INLINE explicit DMat44(Mat44Arg inM); + JPH_INLINE DMat44(Mat44Arg inRot, DVec3Arg inT); + JPH_INLINE DMat44(Type inC1, Type inC2, Type inC3, DTypeArg inC4); + + /// Zero matrix + static JPH_INLINE DMat44 sZero(); + + /// Identity matrix + static JPH_INLINE DMat44 sIdentity(); + + /// Rotate from quaternion + static JPH_INLINE DMat44 sRotation(QuatArg inQuat) { return DMat44(Mat44::sRotation(inQuat), DVec3::sZero()); } + + /// Get matrix that translates + static JPH_INLINE DMat44 sTranslation(DVec3Arg inV) { return DMat44(Vec4(1, 0, 0, 0), Vec4(0, 1, 0, 0), Vec4(0, 0, 1, 0), inV); } + + /// Get matrix that rotates and translates + static JPH_INLINE DMat44 sRotationTranslation(QuatArg inR, DVec3Arg inT) { return DMat44(Mat44::sRotation(inR), inT); } + + /// Get inverse matrix of sRotationTranslation + static JPH_INLINE DMat44 sInverseRotationTranslation(QuatArg inR, DVec3Arg inT); + + /// Get matrix that scales (produces a matrix with (inV, 1) on its diagonal) + static JPH_INLINE DMat44 sScale(Vec3Arg inV) { return DMat44(Mat44::sScale(inV), DVec3::sZero()); } + + /// Convert to Mat44 rounding to nearest + JPH_INLINE Mat44 ToMat44() const { return Mat44(mCol[0], mCol[1], mCol[2], Vec3(mCol3)); } + + /// Comparison + JPH_INLINE bool operator == (DMat44Arg inM2) const; + JPH_INLINE bool operator != (DMat44Arg inM2) const { return !(*this == inM2); } + + /// Test if two matrices are close + JPH_INLINE bool IsClose(DMat44Arg inM2, float inMaxDistSq = 1.0e-12f) const; + + /// Multiply matrix by matrix + JPH_INLINE DMat44 operator * (Mat44Arg inM) const; + + /// Multiply matrix by matrix + JPH_INLINE DMat44 operator * (DMat44Arg inM) const; + + /// Multiply vector by matrix + JPH_INLINE DVec3 operator * (Vec3Arg inV) const; + + /// Multiply vector by matrix + JPH_INLINE DVec3 operator * (DVec3Arg inV) const; + + /// Multiply vector by only 3x3 part of the matrix + JPH_INLINE Vec3 Multiply3x3(Vec3Arg inV) const { return GetRotation().Multiply3x3(inV); } + + /// Multiply vector by only 3x3 part of the matrix + JPH_INLINE DVec3 Multiply3x3(DVec3Arg inV) const; + + /// Multiply vector by only 3x3 part of the transpose of the matrix (\f$result = this^T \: inV\f$) + JPH_INLINE Vec3 Multiply3x3Transposed(Vec3Arg inV) const { return GetRotation().Multiply3x3Transposed(inV); } + + /// Scale a matrix: result = this * Mat44::sScale(inScale) + JPH_INLINE DMat44 PreScaled(Vec3Arg inScale) const; + + /// Scale a matrix: result = Mat44::sScale(inScale) * this + JPH_INLINE DMat44 PostScaled(Vec3Arg inScale) const; + + /// Pre multiply by translation matrix: result = this * Mat44::sTranslation(inTranslation) + JPH_INLINE DMat44 PreTranslated(Vec3Arg inTranslation) const; + + /// Pre multiply by translation matrix: result = this * Mat44::sTranslation(inTranslation) + JPH_INLINE DMat44 PreTranslated(DVec3Arg inTranslation) const; + + /// Post multiply by translation matrix: result = Mat44::sTranslation(inTranslation) * this (i.e. add inTranslation to the 4-th column) + JPH_INLINE DMat44 PostTranslated(Vec3Arg inTranslation) const; + + /// Post multiply by translation matrix: result = Mat44::sTranslation(inTranslation) * this (i.e. add inTranslation to the 4-th column) + JPH_INLINE DMat44 PostTranslated(DVec3Arg inTranslation) const; + + /// Access to the columns + JPH_INLINE Vec3 GetAxisX() const { return Vec3(mCol[0]); } + JPH_INLINE void SetAxisX(Vec3Arg inV) { mCol[0] = Vec4(inV, 0.0f); } + JPH_INLINE Vec3 GetAxisY() const { return Vec3(mCol[1]); } + JPH_INLINE void SetAxisY(Vec3Arg inV) { mCol[1] = Vec4(inV, 0.0f); } + JPH_INLINE Vec3 GetAxisZ() const { return Vec3(mCol[2]); } + JPH_INLINE void SetAxisZ(Vec3Arg inV) { mCol[2] = Vec4(inV, 0.0f); } + JPH_INLINE DVec3 GetTranslation() const { return mCol3; } + JPH_INLINE void SetTranslation(DVec3Arg inV) { mCol3 = inV; } + JPH_INLINE Vec3 GetColumn3(uint inCol) const { JPH_ASSERT(inCol < 3); return Vec3(mCol[inCol]); } + JPH_INLINE void SetColumn3(uint inCol, Vec3Arg inV) { JPH_ASSERT(inCol < 3); mCol[inCol] = Vec4(inV, 0.0f); } + JPH_INLINE Vec4 GetColumn4(uint inCol) const { JPH_ASSERT(inCol < 3); return mCol[inCol]; } + JPH_INLINE void SetColumn4(uint inCol, Vec4Arg inV) { JPH_ASSERT(inCol < 3); mCol[inCol] = inV; } + + /// Transpose 3x3 subpart of matrix + JPH_INLINE Mat44 Transposed3x3() const { return GetRotation().Transposed3x3(); } + + /// Inverse 4x4 matrix + JPH_INLINE DMat44 Inversed() const; + + /// Inverse 4x4 matrix when it only contains rotation and translation + JPH_INLINE DMat44 InversedRotationTranslation() const; + + /// Get rotation part only (note: retains the first 3 values from the bottom row) + JPH_INLINE Mat44 GetRotation() const { return Mat44(mCol[0], mCol[1], mCol[2], Vec4(0, 0, 0, 1)); } + + /// Updates the rotation part of this matrix (the first 3 columns) + JPH_INLINE void SetRotation(Mat44Arg inRotation); + + /// Convert to quaternion + JPH_INLINE Quat GetQuaternion() const { return GetRotation().GetQuaternion(); } + + /// Get matrix that transforms a direction with the same transform as this matrix (length is not preserved) + JPH_INLINE Mat44 GetDirectionPreservingMatrix() const { return GetRotation().Inversed3x3().Transposed3x3(); } + + /// Works identical to Mat44::Decompose + JPH_INLINE DMat44 Decompose(Vec3 &outScale) const { return DMat44(GetRotation().Decompose(outScale), mCol3); } + + /// To String + friend ostream & operator << (ostream &inStream, DMat44Arg inM) + { + inStream << inM.mCol[0] << ", " << inM.mCol[1] << ", " << inM.mCol[2] << ", " << inM.mCol3; + return inStream; + } + +private: + Vec4 mCol[3]; ///< Rotation columns + DVec3 mCol3; ///< Translation column, 4th element is assumed to be 1 +}; + +static_assert(is_trivial(), "Is supposed to be a trivial type!"); + +JPH_NAMESPACE_END + +#include "DMat44.inl" diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/DMat44.inl b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/DMat44.inl new file mode 100644 index 00000000000..462cf79114c --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/DMat44.inl @@ -0,0 +1,310 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +DMat44::DMat44(Vec4Arg inC1, Vec4Arg inC2, Vec4Arg inC3, DVec3Arg inC4) : + mCol { inC1, inC2, inC3 }, + mCol3(inC4) +{ +} + +DMat44::DMat44(Type inC1, Type inC2, Type inC3, DTypeArg inC4) : + mCol { inC1, inC2, inC3 }, + mCol3(inC4) +{ +} + +DMat44::DMat44(Mat44Arg inM) : + mCol { inM.GetColumn4(0), inM.GetColumn4(1), inM.GetColumn4(2) }, + mCol3(inM.GetTranslation()) +{ +} + +DMat44::DMat44(Mat44Arg inRot, DVec3Arg inT) : + mCol { inRot.GetColumn4(0), inRot.GetColumn4(1), inRot.GetColumn4(2) }, + mCol3(inT) +{ +} + +DMat44 DMat44::sZero() +{ + return DMat44(Vec4::sZero(), Vec4::sZero(), Vec4::sZero(), DVec3::sZero()); +} + +DMat44 DMat44::sIdentity() +{ + return DMat44(Vec4(1, 0, 0, 0), Vec4(0, 1, 0, 0), Vec4(0, 0, 1, 0), DVec3::sZero()); +} + +DMat44 DMat44::sInverseRotationTranslation(QuatArg inR, DVec3Arg inT) +{ + Mat44 m = Mat44::sRotation(inR.Conjugated()); + DMat44 dm(m, DVec3::sZero()); + dm.SetTranslation(-dm.Multiply3x3(inT)); + return dm; +} + +bool DMat44::operator == (DMat44Arg inM2) const +{ + return mCol[0] == inM2.mCol[0] + && mCol[1] == inM2.mCol[1] + && mCol[2] == inM2.mCol[2] + && mCol3 == inM2.mCol3; +} + +bool DMat44::IsClose(DMat44Arg inM2, float inMaxDistSq) const +{ + for (int i = 0; i < 3; ++i) + if (!mCol[i].IsClose(inM2.mCol[i], inMaxDistSq)) + return false; + return mCol3.IsClose(inM2.mCol3, double(inMaxDistSq)); +} + +DVec3 DMat44::operator * (Vec3Arg inV) const +{ +#if defined(JPH_USE_AVX) + __m128 t = _mm_mul_ps(mCol[0].mValue, _mm_shuffle_ps(inV.mValue, inV.mValue, _MM_SHUFFLE(0, 0, 0, 0))); + t = _mm_add_ps(t, _mm_mul_ps(mCol[1].mValue, _mm_shuffle_ps(inV.mValue, inV.mValue, _MM_SHUFFLE(1, 1, 1, 1)))); + t = _mm_add_ps(t, _mm_mul_ps(mCol[2].mValue, _mm_shuffle_ps(inV.mValue, inV.mValue, _MM_SHUFFLE(2, 2, 2, 2)))); + return DVec3::sFixW(_mm256_add_pd(mCol3.mValue, _mm256_cvtps_pd(t))); +#elif defined(JPH_USE_SSE) + __m128 t = _mm_mul_ps(mCol[0].mValue, _mm_shuffle_ps(inV.mValue, inV.mValue, _MM_SHUFFLE(0, 0, 0, 0))); + t = _mm_add_ps(t, _mm_mul_ps(mCol[1].mValue, _mm_shuffle_ps(inV.mValue, inV.mValue, _MM_SHUFFLE(1, 1, 1, 1)))); + t = _mm_add_ps(t, _mm_mul_ps(mCol[2].mValue, _mm_shuffle_ps(inV.mValue, inV.mValue, _MM_SHUFFLE(2, 2, 2, 2)))); + __m128d low = _mm_add_pd(mCol3.mValue.mLow, _mm_cvtps_pd(t)); + __m128d high = _mm_add_pd(mCol3.mValue.mHigh, _mm_cvtps_pd(_mm_shuffle_ps(t, t, _MM_SHUFFLE(2, 2, 2, 2)))); + return DVec3({ low, high }); +#elif defined(JPH_USE_NEON) + float32x4_t t = vmulq_f32(mCol[0].mValue, vdupq_laneq_f32(inV.mValue, 0)); + t = vmlaq_f32(t, mCol[1].mValue, vdupq_laneq_f32(inV.mValue, 1)); + t = vmlaq_f32(t, mCol[2].mValue, vdupq_laneq_f32(inV.mValue, 2)); + float64x2_t low = vaddq_f64(mCol3.mValue.val[0], vcvt_f64_f32(vget_low_f32(t))); + float64x2_t high = vaddq_f64(mCol3.mValue.val[1], vcvt_high_f64_f32(t)); + return DVec3::sFixW({ low, high }); +#else + return DVec3( + mCol3.mF64[0] + double(mCol[0].mF32[0] * inV.mF32[0] + mCol[1].mF32[0] * inV.mF32[1] + mCol[2].mF32[0] * inV.mF32[2]), + mCol3.mF64[1] + double(mCol[0].mF32[1] * inV.mF32[0] + mCol[1].mF32[1] * inV.mF32[1] + mCol[2].mF32[1] * inV.mF32[2]), + mCol3.mF64[2] + double(mCol[0].mF32[2] * inV.mF32[0] + mCol[1].mF32[2] * inV.mF32[1] + mCol[2].mF32[2] * inV.mF32[2])); +#endif +} + +DVec3 DMat44::operator * (DVec3Arg inV) const +{ +#if defined(JPH_USE_AVX) + __m256d t = _mm256_add_pd(mCol3.mValue, _mm256_mul_pd(_mm256_cvtps_pd(mCol[0].mValue), _mm256_set1_pd(inV.mF64[0]))); + t = _mm256_add_pd(t, _mm256_mul_pd(_mm256_cvtps_pd(mCol[1].mValue), _mm256_set1_pd(inV.mF64[1]))); + t = _mm256_add_pd(t, _mm256_mul_pd(_mm256_cvtps_pd(mCol[2].mValue), _mm256_set1_pd(inV.mF64[2]))); + return DVec3::sFixW(t); +#elif defined(JPH_USE_SSE) + __m128d xxxx = _mm_set1_pd(inV.mF64[0]); + __m128d yyyy = _mm_set1_pd(inV.mF64[1]); + __m128d zzzz = _mm_set1_pd(inV.mF64[2]); + __m128 col0 = mCol[0].mValue; + __m128 col1 = mCol[1].mValue; + __m128 col2 = mCol[2].mValue; + __m128d t_low = _mm_add_pd(mCol3.mValue.mLow, _mm_mul_pd(_mm_cvtps_pd(col0), xxxx)); + t_low = _mm_add_pd(t_low, _mm_mul_pd(_mm_cvtps_pd(col1), yyyy)); + t_low = _mm_add_pd(t_low, _mm_mul_pd(_mm_cvtps_pd(col2), zzzz)); + __m128d t_high = _mm_add_pd(mCol3.mValue.mHigh, _mm_mul_pd(_mm_cvtps_pd(_mm_shuffle_ps(col0, col0, _MM_SHUFFLE(2, 2, 2, 2))), xxxx)); + t_high = _mm_add_pd(t_high, _mm_mul_pd(_mm_cvtps_pd(_mm_shuffle_ps(col1, col1, _MM_SHUFFLE(2, 2, 2, 2))), yyyy)); + t_high = _mm_add_pd(t_high, _mm_mul_pd(_mm_cvtps_pd(_mm_shuffle_ps(col2, col2, _MM_SHUFFLE(2, 2, 2, 2))), zzzz)); + return DVec3({ t_low, t_high }); +#elif defined(JPH_USE_NEON) + float64x2_t xxxx = vdupq_laneq_f64(inV.mValue.val[0], 0); + float64x2_t yyyy = vdupq_laneq_f64(inV.mValue.val[0], 1); + float64x2_t zzzz = vdupq_laneq_f64(inV.mValue.val[1], 0); + float32x4_t col0 = mCol[0].mValue; + float32x4_t col1 = mCol[1].mValue; + float32x4_t col2 = mCol[2].mValue; + float64x2_t t_low = vaddq_f64(mCol3.mValue.val[0], vmulq_f64(vcvt_f64_f32(vget_low_f32(col0)), xxxx)); + t_low = vaddq_f64(t_low, vmulq_f64(vcvt_f64_f32(vget_low_f32(col1)), yyyy)); + t_low = vaddq_f64(t_low, vmulq_f64(vcvt_f64_f32(vget_low_f32(col2)), zzzz)); + float64x2_t t_high = vaddq_f64(mCol3.mValue.val[1], vmulq_f64(vcvt_high_f64_f32(col0), xxxx)); + t_high = vaddq_f64(t_high, vmulq_f64(vcvt_high_f64_f32(col1), yyyy)); + t_high = vaddq_f64(t_high, vmulq_f64(vcvt_high_f64_f32(col2), zzzz)); + return DVec3::sFixW({ t_low, t_high }); +#else + return DVec3( + mCol3.mF64[0] + double(mCol[0].mF32[0]) * inV.mF64[0] + double(mCol[1].mF32[0]) * inV.mF64[1] + double(mCol[2].mF32[0]) * inV.mF64[2], + mCol3.mF64[1] + double(mCol[0].mF32[1]) * inV.mF64[0] + double(mCol[1].mF32[1]) * inV.mF64[1] + double(mCol[2].mF32[1]) * inV.mF64[2], + mCol3.mF64[2] + double(mCol[0].mF32[2]) * inV.mF64[0] + double(mCol[1].mF32[2]) * inV.mF64[1] + double(mCol[2].mF32[2]) * inV.mF64[2]); +#endif +} + +DVec3 DMat44::Multiply3x3(DVec3Arg inV) const +{ +#if defined(JPH_USE_AVX) + __m256d t = _mm256_mul_pd(_mm256_cvtps_pd(mCol[0].mValue), _mm256_set1_pd(inV.mF64[0])); + t = _mm256_add_pd(t, _mm256_mul_pd(_mm256_cvtps_pd(mCol[1].mValue), _mm256_set1_pd(inV.mF64[1]))); + t = _mm256_add_pd(t, _mm256_mul_pd(_mm256_cvtps_pd(mCol[2].mValue), _mm256_set1_pd(inV.mF64[2]))); + return DVec3::sFixW(t); +#elif defined(JPH_USE_SSE) + __m128d xxxx = _mm_set1_pd(inV.mF64[0]); + __m128d yyyy = _mm_set1_pd(inV.mF64[1]); + __m128d zzzz = _mm_set1_pd(inV.mF64[2]); + __m128 col0 = mCol[0].mValue; + __m128 col1 = mCol[1].mValue; + __m128 col2 = mCol[2].mValue; + __m128d t_low = _mm_mul_pd(_mm_cvtps_pd(col0), xxxx); + t_low = _mm_add_pd(t_low, _mm_mul_pd(_mm_cvtps_pd(col1), yyyy)); + t_low = _mm_add_pd(t_low, _mm_mul_pd(_mm_cvtps_pd(col2), zzzz)); + __m128d t_high = _mm_mul_pd(_mm_cvtps_pd(_mm_shuffle_ps(col0, col0, _MM_SHUFFLE(2, 2, 2, 2))), xxxx); + t_high = _mm_add_pd(t_high, _mm_mul_pd(_mm_cvtps_pd(_mm_shuffle_ps(col1, col1, _MM_SHUFFLE(2, 2, 2, 2))), yyyy)); + t_high = _mm_add_pd(t_high, _mm_mul_pd(_mm_cvtps_pd(_mm_shuffle_ps(col2, col2, _MM_SHUFFLE(2, 2, 2, 2))), zzzz)); + return DVec3({ t_low, t_high }); +#elif defined(JPH_USE_NEON) + float64x2_t xxxx = vdupq_laneq_f64(inV.mValue.val[0], 0); + float64x2_t yyyy = vdupq_laneq_f64(inV.mValue.val[0], 1); + float64x2_t zzzz = vdupq_laneq_f64(inV.mValue.val[1], 0); + float32x4_t col0 = mCol[0].mValue; + float32x4_t col1 = mCol[1].mValue; + float32x4_t col2 = mCol[2].mValue; + float64x2_t t_low = vmulq_f64(vcvt_f64_f32(vget_low_f32(col0)), xxxx); + t_low = vaddq_f64(t_low, vmulq_f64(vcvt_f64_f32(vget_low_f32(col1)), yyyy)); + t_low = vaddq_f64(t_low, vmulq_f64(vcvt_f64_f32(vget_low_f32(col2)), zzzz)); + float64x2_t t_high = vmulq_f64(vcvt_high_f64_f32(col0), xxxx); + t_high = vaddq_f64(t_high, vmulq_f64(vcvt_high_f64_f32(col1), yyyy)); + t_high = vaddq_f64(t_high, vmulq_f64(vcvt_high_f64_f32(col2), zzzz)); + return DVec3::sFixW({ t_low, t_high }); +#else + return DVec3( + double(mCol[0].mF32[0]) * inV.mF64[0] + double(mCol[1].mF32[0]) * inV.mF64[1] + double(mCol[2].mF32[0]) * inV.mF64[2], + double(mCol[0].mF32[1]) * inV.mF64[0] + double(mCol[1].mF32[1]) * inV.mF64[1] + double(mCol[2].mF32[1]) * inV.mF64[2], + double(mCol[0].mF32[2]) * inV.mF64[0] + double(mCol[1].mF32[2]) * inV.mF64[1] + double(mCol[2].mF32[2]) * inV.mF64[2]); +#endif +} + +DMat44 DMat44::operator * (Mat44Arg inM) const +{ + DMat44 result; + + // Rotation part +#if defined(JPH_USE_SSE) + for (int i = 0; i < 3; ++i) + { + __m128 c = inM.GetColumn4(i).mValue; + __m128 t = _mm_mul_ps(mCol[0].mValue, _mm_shuffle_ps(c, c, _MM_SHUFFLE(0, 0, 0, 0))); + t = _mm_add_ps(t, _mm_mul_ps(mCol[1].mValue, _mm_shuffle_ps(c, c, _MM_SHUFFLE(1, 1, 1, 1)))); + t = _mm_add_ps(t, _mm_mul_ps(mCol[2].mValue, _mm_shuffle_ps(c, c, _MM_SHUFFLE(2, 2, 2, 2)))); + result.mCol[i].mValue = t; + } +#elif defined(JPH_USE_NEON) + for (int i = 0; i < 3; ++i) + { + Type c = inM.GetColumn4(i).mValue; + Type t = vmulq_f32(mCol[0].mValue, vdupq_laneq_f32(c, 0)); + t = vmlaq_f32(t, mCol[1].mValue, vdupq_laneq_f32(c, 1)); + t = vmlaq_f32(t, mCol[2].mValue, vdupq_laneq_f32(c, 2)); + result.mCol[i].mValue = t; + } +#else + for (int i = 0; i < 3; ++i) + { + Vec4 coli = inM.GetColumn4(i); + result.mCol[i] = mCol[0] * coli.mF32[0] + mCol[1] * coli.mF32[1] + mCol[2] * coli.mF32[2]; + } +#endif + + // Translation part + result.mCol3 = *this * inM.GetTranslation(); + + return result; +} + +DMat44 DMat44::operator * (DMat44Arg inM) const +{ + DMat44 result; + + // Rotation part +#if defined(JPH_USE_SSE) + for (int i = 0; i < 3; ++i) + { + __m128 c = inM.mCol[i].mValue; + __m128 t = _mm_mul_ps(mCol[0].mValue, _mm_shuffle_ps(c, c, _MM_SHUFFLE(0, 0, 0, 0))); + t = _mm_add_ps(t, _mm_mul_ps(mCol[1].mValue, _mm_shuffle_ps(c, c, _MM_SHUFFLE(1, 1, 1, 1)))); + t = _mm_add_ps(t, _mm_mul_ps(mCol[2].mValue, _mm_shuffle_ps(c, c, _MM_SHUFFLE(2, 2, 2, 2)))); + result.mCol[i].mValue = t; + } +#elif defined(JPH_USE_NEON) + for (int i = 0; i < 3; ++i) + { + Type c = inM.GetColumn4(i).mValue; + Type t = vmulq_f32(mCol[0].mValue, vdupq_laneq_f32(c, 0)); + t = vmlaq_f32(t, mCol[1].mValue, vdupq_laneq_f32(c, 1)); + t = vmlaq_f32(t, mCol[2].mValue, vdupq_laneq_f32(c, 2)); + result.mCol[i].mValue = t; + } +#else + for (int i = 0; i < 3; ++i) + { + Vec4 coli = inM.mCol[i]; + result.mCol[i] = mCol[0] * coli.mF32[0] + mCol[1] * coli.mF32[1] + mCol[2] * coli.mF32[2]; + } +#endif + + // Translation part + result.mCol3 = *this * inM.GetTranslation(); + + return result; +} + +void DMat44::SetRotation(Mat44Arg inRotation) +{ + mCol[0] = inRotation.GetColumn4(0); + mCol[1] = inRotation.GetColumn4(1); + mCol[2] = inRotation.GetColumn4(2); +} + +DMat44 DMat44::PreScaled(Vec3Arg inScale) const +{ + return DMat44(inScale.GetX() * mCol[0], inScale.GetY() * mCol[1], inScale.GetZ() * mCol[2], mCol3); +} + +DMat44 DMat44::PostScaled(Vec3Arg inScale) const +{ + Vec4 scale(inScale, 1); + return DMat44(scale * mCol[0], scale * mCol[1], scale * mCol[2], DVec3(scale) * mCol3); +} + +DMat44 DMat44::PreTranslated(Vec3Arg inTranslation) const +{ + return DMat44(mCol[0], mCol[1], mCol[2], GetTranslation() + Multiply3x3(inTranslation)); +} + +DMat44 DMat44::PreTranslated(DVec3Arg inTranslation) const +{ + return DMat44(mCol[0], mCol[1], mCol[2], GetTranslation() + Multiply3x3(inTranslation)); +} + +DMat44 DMat44::PostTranslated(Vec3Arg inTranslation) const +{ + return DMat44(mCol[0], mCol[1], mCol[2], GetTranslation() + inTranslation); +} + +DMat44 DMat44::PostTranslated(DVec3Arg inTranslation) const +{ + return DMat44(mCol[0], mCol[1], mCol[2], GetTranslation() + inTranslation); +} + +DMat44 DMat44::Inversed() const +{ + DMat44 m(GetRotation().Inversed3x3()); + m.mCol3 = -m.Multiply3x3(mCol3); + return m; +} + +DMat44 DMat44::InversedRotationTranslation() const +{ + DMat44 m(GetRotation().Transposed3x3()); + m.mCol3 = -m.Multiply3x3(mCol3); + return m; +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/DVec3.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/DVec3.h new file mode 100644 index 00000000000..58e0a06f337 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/DVec3.h @@ -0,0 +1,288 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +/// 3 component vector of doubles (stored as 4 vectors). +/// Note that we keep the 4th component the same as the 3rd component to avoid divisions by zero when JPH_FLOATING_POINT_EXCEPTIONS_ENABLED defined +class [[nodiscard]] alignas(JPH_DVECTOR_ALIGNMENT) DVec3 +{ +public: + JPH_OVERRIDE_NEW_DELETE + + // Underlying vector type +#if defined(JPH_USE_AVX) + using Type = __m256d; + using TypeArg = __m256d; +#elif defined(JPH_USE_SSE) + using Type = struct { __m128d mLow, mHigh; }; + using TypeArg = const Type &; +#elif defined(JPH_USE_NEON) + using Type = float64x2x2_t; + using TypeArg = const Type &; +#else + using Type = struct { double mData[4]; }; + using TypeArg = const Type &; +#endif + + // Argument type + using ArgType = DVec3Arg; + + /// Constructor + DVec3() = default; ///< Intentionally not initialized for performance reasons + DVec3(const DVec3 &inRHS) = default; + DVec3 & operator = (const DVec3 &inRHS) = default; + JPH_INLINE explicit DVec3(Vec3Arg inRHS); + JPH_INLINE explicit DVec3(Vec4Arg inRHS); + JPH_INLINE DVec3(TypeArg inRHS) : mValue(inRHS) { CheckW(); } + + /// Create a vector from 3 components + JPH_INLINE DVec3(double inX, double inY, double inZ); + + /// Load 3 doubles from memory + explicit JPH_INLINE DVec3(const Double3 &inV); + + /// Vector with all zeros + static JPH_INLINE DVec3 sZero(); + + /// Vectors with the principal axis + static JPH_INLINE DVec3 sAxisX() { return DVec3(1, 0, 0); } + static JPH_INLINE DVec3 sAxisY() { return DVec3(0, 1, 0); } + static JPH_INLINE DVec3 sAxisZ() { return DVec3(0, 0, 1); } + + /// Replicate inV across all components + static JPH_INLINE DVec3 sReplicate(double inV); + + /// Vector with all NaN's + static JPH_INLINE DVec3 sNaN(); + + /// Load 3 doubles from memory (reads 64 bits extra which it doesn't use) + static JPH_INLINE DVec3 sLoadDouble3Unsafe(const Double3 &inV); + + /// Store 3 doubles to memory + JPH_INLINE void StoreDouble3(Double3 *outV) const; + + /// Convert to float vector 3 rounding to nearest + JPH_INLINE explicit operator Vec3() const; + + /// Prepare to convert to float vector 3 rounding towards zero (returns DVec3 that can be converted to a Vec3 to get the rounding) + JPH_INLINE DVec3 PrepareRoundToZero() const; + + /// Prepare to convert to float vector 3 rounding towards positive/negative inf (returns DVec3 that can be converted to a Vec3 to get the rounding) + JPH_INLINE DVec3 PrepareRoundToInf() const; + + /// Convert to float vector 3 rounding down + JPH_INLINE Vec3 ToVec3RoundDown() const; + + /// Convert to float vector 3 rounding up + JPH_INLINE Vec3 ToVec3RoundUp() const; + + /// Return the minimum value of each of the components + static JPH_INLINE DVec3 sMin(DVec3Arg inV1, DVec3Arg inV2); + + /// Return the maximum of each of the components + static JPH_INLINE DVec3 sMax(DVec3Arg inV1, DVec3Arg inV2); + + /// Clamp a vector between min and max (component wise) + static JPH_INLINE DVec3 sClamp(DVec3Arg inV, DVec3Arg inMin, DVec3Arg inMax); + + /// Equals (component wise) + static JPH_INLINE DVec3 sEquals(DVec3Arg inV1, DVec3Arg inV2); + + /// Less than (component wise) + static JPH_INLINE DVec3 sLess(DVec3Arg inV1, DVec3Arg inV2); + + /// Less than or equal (component wise) + static JPH_INLINE DVec3 sLessOrEqual(DVec3Arg inV1, DVec3Arg inV2); + + /// Greater than (component wise) + static JPH_INLINE DVec3 sGreater(DVec3Arg inV1, DVec3Arg inV2); + + /// Greater than or equal (component wise) + static JPH_INLINE DVec3 sGreaterOrEqual(DVec3Arg inV1, DVec3Arg inV2); + + /// Calculates inMul1 * inMul2 + inAdd + static JPH_INLINE DVec3 sFusedMultiplyAdd(DVec3Arg inMul1, DVec3Arg inMul2, DVec3Arg inAdd); + + /// Component wise select, returns inV1 when highest bit of inControl = 0 and inV2 when highest bit of inControl = 1 + static JPH_INLINE DVec3 sSelect(DVec3Arg inV1, DVec3Arg inV2, DVec3Arg inControl); + + /// Logical or (component wise) + static JPH_INLINE DVec3 sOr(DVec3Arg inV1, DVec3Arg inV2); + + /// Logical xor (component wise) + static JPH_INLINE DVec3 sXor(DVec3Arg inV1, DVec3Arg inV2); + + /// Logical and (component wise) + static JPH_INLINE DVec3 sAnd(DVec3Arg inV1, DVec3Arg inV2); + + /// Store if X is true in bit 0, Y in bit 1, Z in bit 2 and W in bit 3 (true is when highest bit of component is set) + JPH_INLINE int GetTrues() const; + + /// Test if any of the components are true (true is when highest bit of component is set) + JPH_INLINE bool TestAnyTrue() const; + + /// Test if all components are true (true is when highest bit of component is set) + JPH_INLINE bool TestAllTrue() const; + + /// Get individual components +#if defined(JPH_USE_AVX) + JPH_INLINE double GetX() const { return _mm_cvtsd_f64(_mm256_castpd256_pd128(mValue)); } + JPH_INLINE double GetY() const { return mF64[1]; } + JPH_INLINE double GetZ() const { return mF64[2]; } +#elif defined(JPH_USE_SSE) + JPH_INLINE double GetX() const { return _mm_cvtsd_f64(mValue.mLow); } + JPH_INLINE double GetY() const { return mF64[1]; } + JPH_INLINE double GetZ() const { return _mm_cvtsd_f64(mValue.mHigh); } +#elif defined(JPH_USE_NEON) + JPH_INLINE double GetX() const { return vgetq_lane_f64(mValue.val[0], 0); } + JPH_INLINE double GetY() const { return vgetq_lane_f64(mValue.val[0], 1); } + JPH_INLINE double GetZ() const { return vgetq_lane_f64(mValue.val[1], 0); } +#else + JPH_INLINE double GetX() const { return mF64[0]; } + JPH_INLINE double GetY() const { return mF64[1]; } + JPH_INLINE double GetZ() const { return mF64[2]; } +#endif + + /// Set individual components + JPH_INLINE void SetX(double inX) { mF64[0] = inX; } + JPH_INLINE void SetY(double inY) { mF64[1] = inY; } + JPH_INLINE void SetZ(double inZ) { mF64[2] = mF64[3] = inZ; } // Assure Z and W are the same + + /// Set all components + JPH_INLINE void Set(double inX, double inY, double inZ) { *this = DVec3(inX, inY, inZ); } + + /// Get double component by index + JPH_INLINE double operator [] (uint inCoordinate) const { JPH_ASSERT(inCoordinate < 3); return mF64[inCoordinate]; } + + /// Set double component by index + JPH_INLINE void SetComponent(uint inCoordinate, double inValue) { JPH_ASSERT(inCoordinate < 3); mF64[inCoordinate] = inValue; mValue = sFixW(mValue); } // Assure Z and W are the same + + /// Comparison + JPH_INLINE bool operator == (DVec3Arg inV2) const; + JPH_INLINE bool operator != (DVec3Arg inV2) const { return !(*this == inV2); } + + /// Test if two vectors are close + JPH_INLINE bool IsClose(DVec3Arg inV2, double inMaxDistSq = 1.0e-24) const; + + /// Test if vector is near zero + JPH_INLINE bool IsNearZero(double inMaxDistSq = 1.0e-24) const; + + /// Test if vector is normalized + JPH_INLINE bool IsNormalized(double inTolerance = 1.0e-12) const; + + /// Test if vector contains NaN elements + JPH_INLINE bool IsNaN() const; + + /// Multiply two double vectors (component wise) + JPH_INLINE DVec3 operator * (DVec3Arg inV2) const; + + /// Multiply vector with double + JPH_INLINE DVec3 operator * (double inV2) const; + + /// Multiply vector with double + friend JPH_INLINE DVec3 operator * (double inV1, DVec3Arg inV2); + + /// Divide vector by double + JPH_INLINE DVec3 operator / (double inV2) const; + + /// Multiply vector with double + JPH_INLINE DVec3 & operator *= (double inV2); + + /// Multiply vector with vector + JPH_INLINE DVec3 & operator *= (DVec3Arg inV2); + + /// Divide vector by double + JPH_INLINE DVec3 & operator /= (double inV2); + + /// Add two vectors (component wise) + JPH_INLINE DVec3 operator + (Vec3Arg inV2) const; + + /// Add two double vectors (component wise) + JPH_INLINE DVec3 operator + (DVec3Arg inV2) const; + + /// Add two vectors (component wise) + JPH_INLINE DVec3 & operator += (Vec3Arg inV2); + + /// Add two double vectors (component wise) + JPH_INLINE DVec3 & operator += (DVec3Arg inV2); + + /// Negate + JPH_INLINE DVec3 operator - () const; + + /// Subtract two vectors (component wise) + JPH_INLINE DVec3 operator - (Vec3Arg inV2) const; + + /// Subtract two double vectors (component wise) + JPH_INLINE DVec3 operator - (DVec3Arg inV2) const; + + /// Add two vectors (component wise) + JPH_INLINE DVec3 & operator -= (Vec3Arg inV2); + + /// Add two double vectors (component wise) + JPH_INLINE DVec3 & operator -= (DVec3Arg inV2); + + /// Divide (component wise) + JPH_INLINE DVec3 operator / (DVec3Arg inV2) const; + + /// Return the absolute value of each of the components + JPH_INLINE DVec3 Abs() const; + + /// Reciprocal vector (1 / value) for each of the components + JPH_INLINE DVec3 Reciprocal() const; + + /// Cross product + JPH_INLINE DVec3 Cross(DVec3Arg inV2) const; + + /// Dot product + JPH_INLINE double Dot(DVec3Arg inV2) const; + + /// Squared length of vector + JPH_INLINE double LengthSq() const; + + /// Length of vector + JPH_INLINE double Length() const; + + /// Normalize vector + JPH_INLINE DVec3 Normalized() const; + + /// Component wise square root + JPH_INLINE DVec3 Sqrt() const; + + /// Get vector that contains the sign of each element (returns 1 if positive, -1 if negative) + JPH_INLINE DVec3 GetSign() const; + + /// To String + friend ostream & operator << (ostream &inStream, DVec3Arg inV) + { + inStream << inV.mF64[0] << ", " << inV.mF64[1] << ", " << inV.mF64[2]; + return inStream; + } + + /// Internal helper function that checks that W is equal to Z, so e.g. dividing by it should not generate div by 0 + JPH_INLINE void CheckW() const; + + /// Internal helper function that ensures that the Z component is replicated to the W component to prevent divisions by zero + static JPH_INLINE Type sFixW(TypeArg inValue); + + /// Representations of true and false for boolean operations + inline static const double cTrue = BitCast(~uint64(0)); + inline static const double cFalse = 0.0; + + union + { + Type mValue; + double mF64[4]; + }; +}; + +static_assert(is_trivial(), "Is supposed to be a trivial type!"); + +JPH_NAMESPACE_END + +#include "DVec3.inl" diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/DVec3.inl b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/DVec3.inl new file mode 100644 index 00000000000..08a17f8464a --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/DVec3.inl @@ -0,0 +1,921 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +// Create a std::hash for DVec3 +JPH_MAKE_HASHABLE(JPH::DVec3, t.GetX(), t.GetY(), t.GetZ()) + +JPH_NAMESPACE_BEGIN + +DVec3::DVec3(Vec3Arg inRHS) +{ +#if defined(JPH_USE_AVX) + mValue = _mm256_cvtps_pd(inRHS.mValue); +#elif defined(JPH_USE_SSE) + mValue.mLow = _mm_cvtps_pd(inRHS.mValue); + mValue.mHigh = _mm_cvtps_pd(_mm_shuffle_ps(inRHS.mValue, inRHS.mValue, _MM_SHUFFLE(2, 2, 2, 2))); +#elif defined(JPH_USE_NEON) + mValue.val[0] = vcvt_f64_f32(vget_low_f32(inRHS.mValue)); + mValue.val[1] = vcvt_high_f64_f32(inRHS.mValue); +#else + mF64[0] = (double)inRHS.GetX(); + mF64[1] = (double)inRHS.GetY(); + mF64[2] = (double)inRHS.GetZ(); + #ifdef JPH_FLOATING_POINT_EXCEPTIONS_ENABLED + mF64[3] = mF64[2]; + #endif +#endif +} + +DVec3::DVec3(Vec4Arg inRHS) : + DVec3(Vec3(inRHS)) +{ +} + +DVec3::DVec3(double inX, double inY, double inZ) +{ +#if defined(JPH_USE_AVX) + mValue = _mm256_set_pd(inZ, inZ, inY, inX); // Assure Z and W are the same +#elif defined(JPH_USE_SSE) + mValue.mLow = _mm_set_pd(inY, inX); + mValue.mHigh = _mm_set1_pd(inZ); +#elif defined(JPH_USE_NEON) + mValue.val[0] = vcombine_f64(vcreate_f64(*reinterpret_cast(&inX)), vcreate_f64(*reinterpret_cast(&inY))); + mValue.val[1] = vdupq_n_f64(inZ); +#else + mF64[0] = inX; + mF64[1] = inY; + mF64[2] = inZ; + #ifdef JPH_FLOATING_POINT_EXCEPTIONS_ENABLED + mF64[3] = mF64[2]; + #endif +#endif +} + +DVec3::DVec3(const Double3 &inV) +{ +#if defined(JPH_USE_AVX) + Type x = _mm256_castpd128_pd256(_mm_load_sd(&inV.x)); + Type y = _mm256_castpd128_pd256(_mm_load_sd(&inV.y)); + Type z = _mm256_broadcast_sd(&inV.z); + Type xy = _mm256_unpacklo_pd(x, y); + mValue = _mm256_blend_pd(xy, z, 0b1100); // Assure Z and W are the same +#elif defined(JPH_USE_SSE) + mValue.mLow = _mm_loadu_pd(&inV.x); + mValue.mHigh = _mm_set1_pd(inV.z); +#elif defined(JPH_USE_NEON) + mValue.val[0] = vld1q_f64(&inV.x); + mValue.val[1] = vdupq_n_f64(inV.z); +#else + mF64[0] = inV.x; + mF64[1] = inV.y; + mF64[2] = inV.z; + #ifdef JPH_FLOATING_POINT_EXCEPTIONS_ENABLED + mF64[3] = mF64[2]; + #endif +#endif +} + +void DVec3::CheckW() const +{ +#ifdef JPH_FLOATING_POINT_EXCEPTIONS_ENABLED + // Avoid asserts when both components are NaN + JPH_ASSERT(reinterpret_cast(mF64)[2] == reinterpret_cast(mF64)[3]); +#endif // JPH_FLOATING_POINT_EXCEPTIONS_ENABLED +} + +/// Internal helper function that ensures that the Z component is replicated to the W component to prevent divisions by zero +DVec3::Type DVec3::sFixW(TypeArg inValue) +{ +#ifdef JPH_FLOATING_POINT_EXCEPTIONS_ENABLED + #if defined(JPH_USE_AVX) + return _mm256_shuffle_pd(inValue, inValue, 2); + #elif defined(JPH_USE_SSE) + Type value; + value.mLow = inValue.mLow; + value.mHigh = _mm_shuffle_pd(inValue.mHigh, inValue.mHigh, 0); + return value; + #elif defined(JPH_USE_NEON) + Type value; + value.val[0] = inValue.val[0]; + value.val[1] = vdupq_laneq_f64(inValue.val[1], 0); + return value; + #else + Type value; + value.mData[0] = inValue.mData[0]; + value.mData[1] = inValue.mData[1]; + value.mData[2] = inValue.mData[2]; + value.mData[3] = inValue.mData[2]; + return value; + #endif +#else + return inValue; +#endif // JPH_FLOATING_POINT_EXCEPTIONS_ENABLED +} + +DVec3 DVec3::sZero() +{ +#if defined(JPH_USE_AVX) + return _mm256_setzero_pd(); +#elif defined(JPH_USE_SSE) + __m128d zero = _mm_setzero_pd(); + return DVec3({ zero, zero }); +#elif defined(JPH_USE_NEON) + float64x2_t zero = vdupq_n_f64(0.0); + return DVec3({ zero, zero }); +#else + return DVec3(0, 0, 0); +#endif +} + +DVec3 DVec3::sReplicate(double inV) +{ +#if defined(JPH_USE_AVX) + return _mm256_set1_pd(inV); +#elif defined(JPH_USE_SSE) + __m128d value = _mm_set1_pd(inV); + return DVec3({ value, value }); +#elif defined(JPH_USE_NEON) + float64x2_t value = vdupq_n_f64(inV); + return DVec3({ value, value }); +#else + return DVec3(inV, inV, inV); +#endif +} + +DVec3 DVec3::sNaN() +{ + return sReplicate(numeric_limits::quiet_NaN()); +} + +DVec3 DVec3::sLoadDouble3Unsafe(const Double3 &inV) +{ +#if defined(JPH_USE_AVX) + Type v = _mm256_loadu_pd(&inV.x); +#elif defined(JPH_USE_SSE) + Type v; + v.mLow = _mm_loadu_pd(&inV.x); + v.mHigh = _mm_set1_pd(inV.z); +#elif defined(JPH_USE_NEON) + Type v = vld1q_f64_x2(&inV.x); +#else + Type v = { inV.x, inV.y, inV.z }; +#endif + return sFixW(v); +} + +void DVec3::StoreDouble3(Double3 *outV) const +{ + outV->x = mF64[0]; + outV->y = mF64[1]; + outV->z = mF64[2]; +} + +DVec3::operator Vec3() const +{ +#if defined(JPH_USE_AVX) + return _mm256_cvtpd_ps(mValue); +#elif defined(JPH_USE_SSE) + __m128 low = _mm_cvtpd_ps(mValue.mLow); + __m128 high = _mm_cvtpd_ps(mValue.mHigh); + return _mm_shuffle_ps(low, high, _MM_SHUFFLE(1, 0, 1, 0)); +#elif defined(JPH_USE_NEON) + return vcvt_high_f32_f64(vcvtx_f32_f64(mValue.val[0]), mValue.val[1]); +#else + return Vec3((float)GetX(), (float)GetY(), (float)GetZ()); +#endif +} + +DVec3 DVec3::sMin(DVec3Arg inV1, DVec3Arg inV2) +{ +#if defined(JPH_USE_AVX) + return _mm256_min_pd(inV1.mValue, inV2.mValue); +#elif defined(JPH_USE_SSE) + return DVec3({ _mm_min_pd(inV1.mValue.mLow, inV2.mValue.mLow), _mm_min_pd(inV1.mValue.mHigh, inV2.mValue.mHigh) }); +#elif defined(JPH_USE_NEON) + return DVec3({ vminq_f64(inV1.mValue.val[0], inV2.mValue.val[0]), vminq_f64(inV1.mValue.val[1], inV2.mValue.val[1]) }); +#else + return DVec3(min(inV1.mF64[0], inV2.mF64[0]), + min(inV1.mF64[1], inV2.mF64[1]), + min(inV1.mF64[2], inV2.mF64[2])); +#endif +} + +DVec3 DVec3::sMax(DVec3Arg inV1, DVec3Arg inV2) +{ +#if defined(JPH_USE_AVX) + return _mm256_max_pd(inV1.mValue, inV2.mValue); +#elif defined(JPH_USE_SSE) + return DVec3({ _mm_max_pd(inV1.mValue.mLow, inV2.mValue.mLow), _mm_max_pd(inV1.mValue.mHigh, inV2.mValue.mHigh) }); +#elif defined(JPH_USE_NEON) + return DVec3({ vmaxq_f64(inV1.mValue.val[0], inV2.mValue.val[0]), vmaxq_f64(inV1.mValue.val[1], inV2.mValue.val[1]) }); +#else + return DVec3(max(inV1.mF64[0], inV2.mF64[0]), + max(inV1.mF64[1], inV2.mF64[1]), + max(inV1.mF64[2], inV2.mF64[2])); +#endif +} + +DVec3 DVec3::sClamp(DVec3Arg inV, DVec3Arg inMin, DVec3Arg inMax) +{ + return sMax(sMin(inV, inMax), inMin); +} + +DVec3 DVec3::sEquals(DVec3Arg inV1, DVec3Arg inV2) +{ +#if defined(JPH_USE_AVX) + return _mm256_cmp_pd(inV1.mValue, inV2.mValue, _CMP_EQ_OQ); +#elif defined(JPH_USE_SSE) + return DVec3({ _mm_cmpeq_pd(inV1.mValue.mLow, inV2.mValue.mLow), _mm_cmpeq_pd(inV1.mValue.mHigh, inV2.mValue.mHigh) }); +#elif defined(JPH_USE_NEON) + return DVec3({ vreinterpretq_u64_f64(vceqq_f64(inV1.mValue.val[0], inV2.mValue.val[0])), vreinterpretq_u64_f64(vceqq_f64(inV1.mValue.val[1], inV2.mValue.val[1])) }); +#else + return DVec3(inV1.mF64[0] == inV2.mF64[0]? cTrue : cFalse, + inV1.mF64[1] == inV2.mF64[1]? cTrue : cFalse, + inV1.mF64[2] == inV2.mF64[2]? cTrue : cFalse); +#endif +} + +DVec3 DVec3::sLess(DVec3Arg inV1, DVec3Arg inV2) +{ +#if defined(JPH_USE_AVX) + return _mm256_cmp_pd(inV1.mValue, inV2.mValue, _CMP_LT_OQ); +#elif defined(JPH_USE_SSE) + return DVec3({ _mm_cmplt_pd(inV1.mValue.mLow, inV2.mValue.mLow), _mm_cmplt_pd(inV1.mValue.mHigh, inV2.mValue.mHigh) }); +#elif defined(JPH_USE_NEON) + return DVec3({ vreinterpretq_u64_f64(vcltq_f64(inV1.mValue.val[0], inV2.mValue.val[0])), vreinterpretq_u64_f64(vcltq_f64(inV1.mValue.val[1], inV2.mValue.val[1])) }); +#else + return DVec3(inV1.mF64[0] < inV2.mF64[0]? cTrue : cFalse, + inV1.mF64[1] < inV2.mF64[1]? cTrue : cFalse, + inV1.mF64[2] < inV2.mF64[2]? cTrue : cFalse); +#endif +} + +DVec3 DVec3::sLessOrEqual(DVec3Arg inV1, DVec3Arg inV2) +{ +#if defined(JPH_USE_AVX) + return _mm256_cmp_pd(inV1.mValue, inV2.mValue, _CMP_LE_OQ); +#elif defined(JPH_USE_SSE) + return DVec3({ _mm_cmple_pd(inV1.mValue.mLow, inV2.mValue.mLow), _mm_cmple_pd(inV1.mValue.mHigh, inV2.mValue.mHigh) }); +#elif defined(JPH_USE_NEON) + return DVec3({ vreinterpretq_u64_f64(vcleq_f64(inV1.mValue.val[0], inV2.mValue.val[0])), vreinterpretq_u64_f64(vcleq_f64(inV1.mValue.val[1], inV2.mValue.val[1])) }); +#else + return DVec3(inV1.mF64[0] <= inV2.mF64[0]? cTrue : cFalse, + inV1.mF64[1] <= inV2.mF64[1]? cTrue : cFalse, + inV1.mF64[2] <= inV2.mF64[2]? cTrue : cFalse); +#endif +} + +DVec3 DVec3::sGreater(DVec3Arg inV1, DVec3Arg inV2) +{ +#if defined(JPH_USE_AVX) + return _mm256_cmp_pd(inV1.mValue, inV2.mValue, _CMP_GT_OQ); +#elif defined(JPH_USE_SSE) + return DVec3({ _mm_cmpgt_pd(inV1.mValue.mLow, inV2.mValue.mLow), _mm_cmpgt_pd(inV1.mValue.mHigh, inV2.mValue.mHigh) }); +#elif defined(JPH_USE_NEON) + return DVec3({ vreinterpretq_u64_f64(vcgtq_f64(inV1.mValue.val[0], inV2.mValue.val[0])), vreinterpretq_u64_f64(vcgtq_f64(inV1.mValue.val[1], inV2.mValue.val[1])) }); +#else + return DVec3(inV1.mF64[0] > inV2.mF64[0]? cTrue : cFalse, + inV1.mF64[1] > inV2.mF64[1]? cTrue : cFalse, + inV1.mF64[2] > inV2.mF64[2]? cTrue : cFalse); +#endif +} + +DVec3 DVec3::sGreaterOrEqual(DVec3Arg inV1, DVec3Arg inV2) +{ +#if defined(JPH_USE_AVX) + return _mm256_cmp_pd(inV1.mValue, inV2.mValue, _CMP_GE_OQ); +#elif defined(JPH_USE_SSE) + return DVec3({ _mm_cmpge_pd(inV1.mValue.mLow, inV2.mValue.mLow), _mm_cmpge_pd(inV1.mValue.mHigh, inV2.mValue.mHigh) }); +#elif defined(JPH_USE_NEON) + return DVec3({ vreinterpretq_u64_f64(vcgeq_f64(inV1.mValue.val[0], inV2.mValue.val[0])), vreinterpretq_u64_f64(vcgeq_f64(inV1.mValue.val[1], inV2.mValue.val[1])) }); +#else + return DVec3(inV1.mF64[0] >= inV2.mF64[0]? cTrue : cFalse, + inV1.mF64[1] >= inV2.mF64[1]? cTrue : cFalse, + inV1.mF64[2] >= inV2.mF64[2]? cTrue : cFalse); +#endif +} + +DVec3 DVec3::sFusedMultiplyAdd(DVec3Arg inMul1, DVec3Arg inMul2, DVec3Arg inAdd) +{ +#if defined(JPH_USE_AVX) + #ifdef JPH_USE_FMADD + return _mm256_fmadd_pd(inMul1.mValue, inMul2.mValue, inAdd.mValue); + #else + return _mm256_add_pd(_mm256_mul_pd(inMul1.mValue, inMul2.mValue), inAdd.mValue); + #endif +#elif defined(JPH_USE_NEON) + return DVec3({ vmlaq_f64(inAdd.mValue.val[0], inMul1.mValue.val[0], inMul2.mValue.val[0]), vmlaq_f64(inAdd.mValue.val[1], inMul1.mValue.val[1], inMul2.mValue.val[1]) }); +#else + return inMul1 * inMul2 + inAdd; +#endif +} + +DVec3 DVec3::sSelect(DVec3Arg inV1, DVec3Arg inV2, DVec3Arg inControl) +{ +#if defined(JPH_USE_AVX) + return _mm256_blendv_pd(inV1.mValue, inV2.mValue, inControl.mValue); +#elif defined(JPH_USE_SSE4_1) + Type v = { _mm_blendv_pd(inV1.mValue.mLow, inV2.mValue.mLow, inControl.mValue.mLow), _mm_blendv_pd(inV1.mValue.mHigh, inV2.mValue.mHigh, inControl.mValue.mHigh) }; + return sFixW(v); +#elif defined(JPH_USE_NEON) + Type v = { vbslq_f64(vshrq_n_s64(inControl.mValue.val[0], 63), inV2.mValue.val[0], inV1.mValue.val[0]), vbslq_f64(vshrq_n_s64(inControl.mValue.val[1], 63), inV2.mValue.val[1], inV1.mValue.val[1]) }; + return sFixW(v); +#else + DVec3 result; + for (int i = 0; i < 3; i++) + result.mF64[i] = BitCast(inControl.mF64[i])? inV2.mF64[i] : inV1.mF64[i]; +#ifdef JPH_FLOATING_POINT_EXCEPTIONS_ENABLED + result.mF64[3] = result.mF64[2]; +#endif // JPH_FLOATING_POINT_EXCEPTIONS_ENABLED + return result; +#endif +} + +DVec3 DVec3::sOr(DVec3Arg inV1, DVec3Arg inV2) +{ +#if defined(JPH_USE_AVX) + return _mm256_or_pd(inV1.mValue, inV2.mValue); +#elif defined(JPH_USE_SSE) + return DVec3({ _mm_or_pd(inV1.mValue.mLow, inV2.mValue.mLow), _mm_or_pd(inV1.mValue.mHigh, inV2.mValue.mHigh) }); +#elif defined(JPH_USE_NEON) + return DVec3({ vorrq_s64(inV1.mValue.val[0], inV2.mValue.val[0]), vorrq_s64(inV1.mValue.val[1], inV2.mValue.val[1]) }); +#else + return DVec3(BitCast(BitCast(inV1.mF64[0]) | BitCast(inV2.mF64[0])), + BitCast(BitCast(inV1.mF64[1]) | BitCast(inV2.mF64[1])), + BitCast(BitCast(inV1.mF64[2]) | BitCast(inV2.mF64[2]))); +#endif +} + +DVec3 DVec3::sXor(DVec3Arg inV1, DVec3Arg inV2) +{ +#if defined(JPH_USE_AVX) + return _mm256_xor_pd(inV1.mValue, inV2.mValue); +#elif defined(JPH_USE_SSE) + return DVec3({ _mm_xor_pd(inV1.mValue.mLow, inV2.mValue.mLow), _mm_xor_pd(inV1.mValue.mHigh, inV2.mValue.mHigh) }); +#elif defined(JPH_USE_NEON) + return DVec3({ veorq_s64(inV1.mValue.val[0], inV2.mValue.val[0]), veorq_s64(inV1.mValue.val[1], inV2.mValue.val[1]) }); +#else + return DVec3(BitCast(BitCast(inV1.mF64[0]) ^ BitCast(inV2.mF64[0])), + BitCast(BitCast(inV1.mF64[1]) ^ BitCast(inV2.mF64[1])), + BitCast(BitCast(inV1.mF64[2]) ^ BitCast(inV2.mF64[2]))); +#endif +} + +DVec3 DVec3::sAnd(DVec3Arg inV1, DVec3Arg inV2) +{ +#if defined(JPH_USE_AVX) + return _mm256_and_pd(inV1.mValue, inV2.mValue); +#elif defined(JPH_USE_SSE) + return DVec3({ _mm_and_pd(inV1.mValue.mLow, inV2.mValue.mLow), _mm_and_pd(inV1.mValue.mHigh, inV2.mValue.mHigh) }); +#elif defined(JPH_USE_NEON) + return DVec3({ vandq_s64(inV1.mValue.val[0], inV2.mValue.val[0]), vandq_s64(inV1.mValue.val[1], inV2.mValue.val[1]) }); +#else + return DVec3(BitCast(BitCast(inV1.mF64[0]) & BitCast(inV2.mF64[0])), + BitCast(BitCast(inV1.mF64[1]) & BitCast(inV2.mF64[1])), + BitCast(BitCast(inV1.mF64[2]) & BitCast(inV2.mF64[2]))); +#endif +} + +int DVec3::GetTrues() const +{ +#if defined(JPH_USE_AVX) + return _mm256_movemask_pd(mValue) & 0x7; +#elif defined(JPH_USE_SSE) + return (_mm_movemask_pd(mValue.mLow) + (_mm_movemask_pd(mValue.mHigh) << 2)) & 0x7; +#else + return int((BitCast(mF64[0]) >> 63) | ((BitCast(mF64[1]) >> 63) << 1) | ((BitCast(mF64[2]) >> 63) << 2)); +#endif +} + +bool DVec3::TestAnyTrue() const +{ + return GetTrues() != 0; +} + +bool DVec3::TestAllTrue() const +{ + return GetTrues() == 0x7; +} + +bool DVec3::operator == (DVec3Arg inV2) const +{ + return sEquals(*this, inV2).TestAllTrue(); +} + +bool DVec3::IsClose(DVec3Arg inV2, double inMaxDistSq) const +{ + return (inV2 - *this).LengthSq() <= inMaxDistSq; +} + +bool DVec3::IsNearZero(double inMaxDistSq) const +{ + return LengthSq() <= inMaxDistSq; +} + +DVec3 DVec3::operator * (DVec3Arg inV2) const +{ +#if defined(JPH_USE_AVX) + return _mm256_mul_pd(mValue, inV2.mValue); +#elif defined(JPH_USE_SSE) + return DVec3({ _mm_mul_pd(mValue.mLow, inV2.mValue.mLow), _mm_mul_pd(mValue.mHigh, inV2.mValue.mHigh) }); +#elif defined(JPH_USE_NEON) + return DVec3({ vmulq_f64(mValue.val[0], inV2.mValue.val[0]), vmulq_f64(mValue.val[1], inV2.mValue.val[1]) }); +#else + return DVec3(mF64[0] * inV2.mF64[0], mF64[1] * inV2.mF64[1], mF64[2] * inV2.mF64[2]); +#endif +} + +DVec3 DVec3::operator * (double inV2) const +{ +#if defined(JPH_USE_AVX) + return _mm256_mul_pd(mValue, _mm256_set1_pd(inV2)); +#elif defined(JPH_USE_SSE) + __m128d v = _mm_set1_pd(inV2); + return DVec3({ _mm_mul_pd(mValue.mLow, v), _mm_mul_pd(mValue.mHigh, v) }); +#elif defined(JPH_USE_NEON) + return DVec3({ vmulq_n_f64(mValue.val[0], inV2), vmulq_n_f64(mValue.val[1], inV2) }); +#else + return DVec3(mF64[0] * inV2, mF64[1] * inV2, mF64[2] * inV2); +#endif +} + +DVec3 operator * (double inV1, DVec3Arg inV2) +{ +#if defined(JPH_USE_AVX) + return _mm256_mul_pd(_mm256_set1_pd(inV1), inV2.mValue); +#elif defined(JPH_USE_SSE) + __m128d v = _mm_set1_pd(inV1); + return DVec3({ _mm_mul_pd(v, inV2.mValue.mLow), _mm_mul_pd(v, inV2.mValue.mHigh) }); +#elif defined(JPH_USE_NEON) + return DVec3({ vmulq_n_f64(inV2.mValue.val[0], inV1), vmulq_n_f64(inV2.mValue.val[1], inV1) }); +#else + return DVec3(inV1 * inV2.mF64[0], inV1 * inV2.mF64[1], inV1 * inV2.mF64[2]); +#endif +} + +DVec3 DVec3::operator / (double inV2) const +{ +#if defined(JPH_USE_AVX) + return _mm256_div_pd(mValue, _mm256_set1_pd(inV2)); +#elif defined(JPH_USE_SSE) + __m128d v = _mm_set1_pd(inV2); + return DVec3({ _mm_div_pd(mValue.mLow, v), _mm_div_pd(mValue.mHigh, v) }); +#elif defined(JPH_USE_NEON) + float64x2_t v = vdupq_n_f64(inV2); + return DVec3({ vdivq_f64(mValue.val[0], v), vdivq_f64(mValue.val[1], v) }); +#else + return DVec3(mF64[0] / inV2, mF64[1] / inV2, mF64[2] / inV2); +#endif +} + +DVec3 &DVec3::operator *= (double inV2) +{ +#if defined(JPH_USE_AVX) + mValue = _mm256_mul_pd(mValue, _mm256_set1_pd(inV2)); +#elif defined(JPH_USE_SSE) + __m128d v = _mm_set1_pd(inV2); + mValue.mLow = _mm_mul_pd(mValue.mLow, v); + mValue.mHigh = _mm_mul_pd(mValue.mHigh, v); +#elif defined(JPH_USE_NEON) + mValue.val[0] = vmulq_n_f64(mValue.val[0], inV2); + mValue.val[1] = vmulq_n_f64(mValue.val[1], inV2); +#else + for (int i = 0; i < 3; ++i) + mF64[i] *= inV2; + #ifdef JPH_FLOATING_POINT_EXCEPTIONS_ENABLED + mF64[3] = mF64[2]; + #endif +#endif + return *this; +} + +DVec3 &DVec3::operator *= (DVec3Arg inV2) +{ +#if defined(JPH_USE_AVX) + mValue = _mm256_mul_pd(mValue, inV2.mValue); +#elif defined(JPH_USE_SSE) + mValue.mLow = _mm_mul_pd(mValue.mLow, inV2.mValue.mLow); + mValue.mHigh = _mm_mul_pd(mValue.mHigh, inV2.mValue.mHigh); +#elif defined(JPH_USE_NEON) + mValue.val[0] = vmulq_f64(mValue.val[0], inV2.mValue.val[0]); + mValue.val[1] = vmulq_f64(mValue.val[1], inV2.mValue.val[1]); +#else + for (int i = 0; i < 3; ++i) + mF64[i] *= inV2.mF64[i]; + #ifdef JPH_FLOATING_POINT_EXCEPTIONS_ENABLED + mF64[3] = mF64[2]; + #endif +#endif + return *this; +} + +DVec3 &DVec3::operator /= (double inV2) +{ +#if defined(JPH_USE_AVX) + mValue = _mm256_div_pd(mValue, _mm256_set1_pd(inV2)); +#elif defined(JPH_USE_SSE) + __m128d v = _mm_set1_pd(inV2); + mValue.mLow = _mm_div_pd(mValue.mLow, v); + mValue.mHigh = _mm_div_pd(mValue.mHigh, v); +#elif defined(JPH_USE_NEON) + float64x2_t v = vdupq_n_f64(inV2); + mValue.val[0] = vdivq_f64(mValue.val[0], v); + mValue.val[1] = vdivq_f64(mValue.val[1], v); +#else + for (int i = 0; i < 3; ++i) + mF64[i] /= inV2; + #ifdef JPH_FLOATING_POINT_EXCEPTIONS_ENABLED + mF64[3] = mF64[2]; + #endif +#endif + return *this; +} + +DVec3 DVec3::operator + (Vec3Arg inV2) const +{ +#if defined(JPH_USE_AVX) + return _mm256_add_pd(mValue, _mm256_cvtps_pd(inV2.mValue)); +#elif defined(JPH_USE_SSE) + return DVec3({ _mm_add_pd(mValue.mLow, _mm_cvtps_pd(inV2.mValue)), _mm_add_pd(mValue.mHigh, _mm_cvtps_pd(_mm_shuffle_ps(inV2.mValue, inV2.mValue, _MM_SHUFFLE(2, 2, 2, 2)))) }); +#elif defined(JPH_USE_NEON) + return DVec3({ vaddq_f64(mValue.val[0], vcvt_f64_f32(vget_low_f32(inV2.mValue))), vaddq_f64(mValue.val[1], vcvt_high_f64_f32(inV2.mValue)) }); +#else + return DVec3(mF64[0] + inV2.mF32[0], mF64[1] + inV2.mF32[1], mF64[2] + inV2.mF32[2]); +#endif +} + +DVec3 DVec3::operator + (DVec3Arg inV2) const +{ +#if defined(JPH_USE_AVX) + return _mm256_add_pd(mValue, inV2.mValue); +#elif defined(JPH_USE_SSE) + return DVec3({ _mm_add_pd(mValue.mLow, inV2.mValue.mLow), _mm_add_pd(mValue.mHigh, inV2.mValue.mHigh) }); +#elif defined(JPH_USE_NEON) + return DVec3({ vaddq_f64(mValue.val[0], inV2.mValue.val[0]), vaddq_f64(mValue.val[1], inV2.mValue.val[1]) }); +#else + return DVec3(mF64[0] + inV2.mF64[0], mF64[1] + inV2.mF64[1], mF64[2] + inV2.mF64[2]); +#endif +} + +DVec3 &DVec3::operator += (Vec3Arg inV2) +{ +#if defined(JPH_USE_AVX) + mValue = _mm256_add_pd(mValue, _mm256_cvtps_pd(inV2.mValue)); +#elif defined(JPH_USE_SSE) + mValue.mLow = _mm_add_pd(mValue.mLow, _mm_cvtps_pd(inV2.mValue)); + mValue.mHigh = _mm_add_pd(mValue.mHigh, _mm_cvtps_pd(_mm_shuffle_ps(inV2.mValue, inV2.mValue, _MM_SHUFFLE(2, 2, 2, 2)))); +#elif defined(JPH_USE_NEON) + mValue.val[0] = vaddq_f64(mValue.val[0], vcvt_f64_f32(vget_low_f32(inV2.mValue))); + mValue.val[1] = vaddq_f64(mValue.val[1], vcvt_high_f64_f32(inV2.mValue)); +#else + for (int i = 0; i < 3; ++i) + mF64[i] += inV2.mF32[i]; + #ifdef JPH_FLOATING_POINT_EXCEPTIONS_ENABLED + mF64[3] = mF64[2]; + #endif +#endif + return *this; +} + +DVec3 &DVec3::operator += (DVec3Arg inV2) +{ +#if defined(JPH_USE_AVX) + mValue = _mm256_add_pd(mValue, inV2.mValue); +#elif defined(JPH_USE_SSE) + mValue.mLow = _mm_add_pd(mValue.mLow, inV2.mValue.mLow); + mValue.mHigh = _mm_add_pd(mValue.mHigh, inV2.mValue.mHigh); +#elif defined(JPH_USE_NEON) + mValue.val[0] = vaddq_f64(mValue.val[0], inV2.mValue.val[0]); + mValue.val[1] = vaddq_f64(mValue.val[1], inV2.mValue.val[1]); +#else + for (int i = 0; i < 3; ++i) + mF64[i] += inV2.mF64[i]; + #ifdef JPH_FLOATING_POINT_EXCEPTIONS_ENABLED + mF64[3] = mF64[2]; + #endif +#endif + return *this; +} + +DVec3 DVec3::operator - () const +{ +#if defined(JPH_USE_AVX) + return _mm256_sub_pd(_mm256_setzero_pd(), mValue); +#elif defined(JPH_USE_SSE) + __m128d zero = _mm_setzero_pd(); + return DVec3({ _mm_sub_pd(zero, mValue.mLow), _mm_sub_pd(zero, mValue.mHigh) }); +#elif defined(JPH_USE_NEON) + return DVec3({ vnegq_f64(mValue.val[0]), vnegq_f64(mValue.val[1]) }); +#else + return DVec3(-mF64[0], -mF64[1], -mF64[2]); +#endif +} + +DVec3 DVec3::operator - (Vec3Arg inV2) const +{ +#if defined(JPH_USE_AVX) + return _mm256_sub_pd(mValue, _mm256_cvtps_pd(inV2.mValue)); +#elif defined(JPH_USE_SSE) + return DVec3({ _mm_sub_pd(mValue.mLow, _mm_cvtps_pd(inV2.mValue)), _mm_sub_pd(mValue.mHigh, _mm_cvtps_pd(_mm_shuffle_ps(inV2.mValue, inV2.mValue, _MM_SHUFFLE(2, 2, 2, 2)))) }); +#elif defined(JPH_USE_NEON) + return DVec3({ vsubq_f64(mValue.val[0], vcvt_f64_f32(vget_low_f32(inV2.mValue))), vsubq_f64(mValue.val[1], vcvt_high_f64_f32(inV2.mValue)) }); +#else + return DVec3(mF64[0] - inV2.mF32[0], mF64[1] - inV2.mF32[1], mF64[2] - inV2.mF32[2]); +#endif +} + +DVec3 DVec3::operator - (DVec3Arg inV2) const +{ +#if defined(JPH_USE_AVX) + return _mm256_sub_pd(mValue, inV2.mValue); +#elif defined(JPH_USE_SSE) + return DVec3({ _mm_sub_pd(mValue.mLow, inV2.mValue.mLow), _mm_sub_pd(mValue.mHigh, inV2.mValue.mHigh) }); +#elif defined(JPH_USE_NEON) + return DVec3({ vsubq_f64(mValue.val[0], inV2.mValue.val[0]), vsubq_f64(mValue.val[1], inV2.mValue.val[1]) }); +#else + return DVec3(mF64[0] - inV2.mF64[0], mF64[1] - inV2.mF64[1], mF64[2] - inV2.mF64[2]); +#endif +} + +DVec3 &DVec3::operator -= (Vec3Arg inV2) +{ +#if defined(JPH_USE_AVX) + mValue = _mm256_sub_pd(mValue, _mm256_cvtps_pd(inV2.mValue)); +#elif defined(JPH_USE_SSE) + mValue.mLow = _mm_sub_pd(mValue.mLow, _mm_cvtps_pd(inV2.mValue)); + mValue.mHigh = _mm_sub_pd(mValue.mHigh, _mm_cvtps_pd(_mm_shuffle_ps(inV2.mValue, inV2.mValue, _MM_SHUFFLE(2, 2, 2, 2)))); +#elif defined(JPH_USE_NEON) + mValue.val[0] = vsubq_f64(mValue.val[0], vcvt_f64_f32(vget_low_f32(inV2.mValue))); + mValue.val[1] = vsubq_f64(mValue.val[1], vcvt_high_f64_f32(inV2.mValue)); +#else + for (int i = 0; i < 3; ++i) + mF64[i] -= inV2.mF32[i]; + #ifdef JPH_FLOATING_POINT_EXCEPTIONS_ENABLED + mF64[3] = mF64[2]; + #endif +#endif + return *this; +} + +DVec3 &DVec3::operator -= (DVec3Arg inV2) +{ +#if defined(JPH_USE_AVX) + mValue = _mm256_sub_pd(mValue, inV2.mValue); +#elif defined(JPH_USE_SSE) + mValue.mLow = _mm_sub_pd(mValue.mLow, inV2.mValue.mLow); + mValue.mHigh = _mm_sub_pd(mValue.mHigh, inV2.mValue.mHigh); +#elif defined(JPH_USE_NEON) + mValue.val[0] = vsubq_f64(mValue.val[0], inV2.mValue.val[0]); + mValue.val[1] = vsubq_f64(mValue.val[1], inV2.mValue.val[1]); +#else + for (int i = 0; i < 3; ++i) + mF64[i] -= inV2.mF64[i]; + #ifdef JPH_FLOATING_POINT_EXCEPTIONS_ENABLED + mF64[3] = mF64[2]; + #endif +#endif + return *this; +} + +DVec3 DVec3::operator / (DVec3Arg inV2) const +{ + inV2.CheckW(); +#if defined(JPH_USE_AVX) + return _mm256_div_pd(mValue, inV2.mValue); +#elif defined(JPH_USE_SSE) + return DVec3({ _mm_div_pd(mValue.mLow, inV2.mValue.mLow), _mm_div_pd(mValue.mHigh, inV2.mValue.mHigh) }); +#elif defined(JPH_USE_NEON) + return DVec3({ vdivq_f64(mValue.val[0], inV2.mValue.val[0]), vdivq_f64(mValue.val[1], inV2.mValue.val[1]) }); +#else + return DVec3(mF64[0] / inV2.mF64[0], mF64[1] / inV2.mF64[1], mF64[2] / inV2.mF64[2]); +#endif +} + +DVec3 DVec3::Abs() const +{ +#if defined(JPH_USE_AVX512) + return _mm256_range_pd(mValue, mValue, 0b1000); +#elif defined(JPH_USE_AVX) + return _mm256_max_pd(_mm256_sub_pd(_mm256_setzero_pd(), mValue), mValue); +#elif defined(JPH_USE_SSE) + __m128d zero = _mm_setzero_pd(); + return DVec3({ _mm_max_pd(_mm_sub_pd(zero, mValue.mLow), mValue.mLow), _mm_max_pd(_mm_sub_pd(zero, mValue.mHigh), mValue.mHigh) }); +#elif defined(JPH_USE_NEON) + return DVec3({ vabsq_f64(mValue.val[0]), vabsq_f64(mValue.val[1]) }); +#else + return DVec3(abs(mF64[0]), abs(mF64[1]), abs(mF64[2])); +#endif +} + +DVec3 DVec3::Reciprocal() const +{ + return sReplicate(1.0) / mValue; +} + +DVec3 DVec3::Cross(DVec3Arg inV2) const +{ +#if defined(JPH_USE_AVX2) + __m256d t1 = _mm256_permute4x64_pd(inV2.mValue, _MM_SHUFFLE(0, 0, 2, 1)); // Assure Z and W are the same + t1 = _mm256_mul_pd(t1, mValue); + __m256d t2 = _mm256_permute4x64_pd(mValue, _MM_SHUFFLE(0, 0, 2, 1)); // Assure Z and W are the same + t2 = _mm256_mul_pd(t2, inV2.mValue); + __m256d t3 = _mm256_sub_pd(t1, t2); + return _mm256_permute4x64_pd(t3, _MM_SHUFFLE(0, 0, 2, 1)); // Assure Z and W are the same +#else + return DVec3(mF64[1] * inV2.mF64[2] - mF64[2] * inV2.mF64[1], + mF64[2] * inV2.mF64[0] - mF64[0] * inV2.mF64[2], + mF64[0] * inV2.mF64[1] - mF64[1] * inV2.mF64[0]); +#endif +} + +double DVec3::Dot(DVec3Arg inV2) const +{ +#if defined(JPH_USE_AVX) + __m256d mul = _mm256_mul_pd(mValue, inV2.mValue); + __m128d xy = _mm256_castpd256_pd128(mul); + __m128d yx = _mm_shuffle_pd(xy, xy, 1); + __m128d sum = _mm_add_pd(xy, yx); + __m128d zw = _mm256_extractf128_pd(mul, 1); + sum = _mm_add_pd(sum, zw); + return _mm_cvtsd_f64(sum); +#elif defined(JPH_USE_SSE) + __m128d xy = _mm_mul_pd(mValue.mLow, inV2.mValue.mLow); + __m128d yx = _mm_shuffle_pd(xy, xy, 1); + __m128d sum = _mm_add_pd(xy, yx); + __m128d z = _mm_mul_sd(mValue.mHigh, inV2.mValue.mHigh); + sum = _mm_add_pd(sum, z); + return _mm_cvtsd_f64(sum); +#elif defined(JPH_USE_NEON) + float64x2_t mul_low = vmulq_f64(mValue.val[0], inV2.mValue.val[0]); + float64x2_t mul_high = vmulq_f64(mValue.val[1], inV2.mValue.val[1]); + return vaddvq_f64(mul_low) + vgetq_lane_f64(mul_high, 0); +#else + double dot = 0.0; + for (int i = 0; i < 3; i++) + dot += mF64[i] * inV2.mF64[i]; + return dot; +#endif +} + +double DVec3::LengthSq() const +{ + return Dot(*this); +} + +DVec3 DVec3::Sqrt() const +{ +#if defined(JPH_USE_AVX) + return _mm256_sqrt_pd(mValue); +#elif defined(JPH_USE_SSE) + return DVec3({ _mm_sqrt_pd(mValue.mLow), _mm_sqrt_pd(mValue.mHigh) }); +#elif defined(JPH_USE_NEON) + return DVec3({ vsqrtq_f64(mValue.val[0]), vsqrtq_f64(mValue.val[1]) }); +#else + return DVec3(sqrt(mF64[0]), sqrt(mF64[1]), sqrt(mF64[2])); +#endif +} + +double DVec3::Length() const +{ + return sqrt(Dot(*this)); +} + +DVec3 DVec3::Normalized() const +{ + return *this / Length(); +} + +bool DVec3::IsNormalized(double inTolerance) const +{ + return abs(LengthSq() - 1.0) <= inTolerance; +} + +bool DVec3::IsNaN() const +{ +#if defined(JPH_USE_AVX512) + return (_mm256_fpclass_pd_mask(mValue, 0b10000001) & 0x7) != 0; +#elif defined(JPH_USE_AVX) + return (_mm256_movemask_pd(_mm256_cmp_pd(mValue, mValue, _CMP_UNORD_Q)) & 0x7) != 0; +#elif defined(JPH_USE_SSE) + return ((_mm_movemask_pd(_mm_cmpunord_pd(mValue.mLow, mValue.mLow)) + (_mm_movemask_pd(_mm_cmpunord_pd(mValue.mHigh, mValue.mHigh)) << 2)) & 0x7) != 0; +#else + return isnan(mF64[0]) || isnan(mF64[1]) || isnan(mF64[2]); +#endif +} + +DVec3 DVec3::GetSign() const +{ +#if defined(JPH_USE_AVX512) + return _mm256_fixupimm_pd(mValue, mValue, _mm256_set1_epi32(0xA9A90A00), 0); +#elif defined(JPH_USE_AVX) + __m256d minus_one = _mm256_set1_pd(-1.0); + __m256d one = _mm256_set1_pd(1.0); + return _mm256_or_pd(_mm256_and_pd(mValue, minus_one), one); +#elif defined(JPH_USE_SSE) + __m128d minus_one = _mm_set1_pd(-1.0); + __m128d one = _mm_set1_pd(1.0); + return DVec3({ _mm_or_pd(_mm_and_pd(mValue.mLow, minus_one), one), _mm_or_pd(_mm_and_pd(mValue.mHigh, minus_one), one) }); +#elif defined(JPH_USE_NEON) + float64x2_t minus_one = vdupq_n_f64(-1.0f); + float64x2_t one = vdupq_n_f64(1.0f); + return DVec3({ vorrq_s64(vandq_s64(mValue.val[0], minus_one), one), vorrq_s64(vandq_s64(mValue.val[1], minus_one), one) }); +#else + return DVec3(std::signbit(mF64[0])? -1.0 : 1.0, + std::signbit(mF64[1])? -1.0 : 1.0, + std::signbit(mF64[2])? -1.0 : 1.0); +#endif +} + +DVec3 DVec3::PrepareRoundToZero() const +{ + // Float has 23 bit mantissa, double 52 bit mantissa => we lose 29 bits when converting from double to float + constexpr uint64 cDoubleToFloatMantissaLoss = (1U << 29) - 1; + +#if defined(JPH_USE_AVX) + return _mm256_and_pd(mValue, _mm256_castsi256_pd(_mm256_set1_epi64x(int64_t(~cDoubleToFloatMantissaLoss)))); +#elif defined(JPH_USE_SSE) + __m128d mask = _mm_castsi128_pd(_mm_set1_epi64x(int64_t(~cDoubleToFloatMantissaLoss))); + return DVec3({ _mm_and_pd(mValue.mLow, mask), _mm_and_pd(mValue.mHigh, mask) }); +#elif defined(JPH_USE_NEON) + float64x2_t mask = vreinterpretq_f64_u64(vdupq_n_u64(~cDoubleToFloatMantissaLoss)); + return DVec3({ vandq_s64(mValue.val[0], mask), vandq_s64(mValue.val[1], mask) }); +#else + double x = BitCast(BitCast(mF64[0]) & ~cDoubleToFloatMantissaLoss); + double y = BitCast(BitCast(mF64[1]) & ~cDoubleToFloatMantissaLoss); + double z = BitCast(BitCast(mF64[2]) & ~cDoubleToFloatMantissaLoss); + + return DVec3(x, y, z); +#endif +} + +DVec3 DVec3::PrepareRoundToInf() const +{ + // Float has 23 bit mantissa, double 52 bit mantissa => we lose 29 bits when converting from double to float + constexpr uint64 cDoubleToFloatMantissaLoss = (1U << 29) - 1; + +#if defined(JPH_USE_AVX512) + __m256i mantissa_loss = _mm256_set1_epi64x(cDoubleToFloatMantissaLoss); + __mmask8 is_zero = _mm256_testn_epi64_mask(_mm256_castpd_si256(mValue), mantissa_loss); + __m256d value_or_mantissa_loss = _mm256_or_pd(mValue, _mm256_castsi256_pd(mantissa_loss)); + return _mm256_mask_blend_pd(is_zero, value_or_mantissa_loss, mValue); +#elif defined(JPH_USE_AVX) + __m256i mantissa_loss = _mm256_set1_epi64x(cDoubleToFloatMantissaLoss); + __m256d value_and_mantissa_loss = _mm256_and_pd(mValue, _mm256_castsi256_pd(mantissa_loss)); + __m256d is_zero = _mm256_cmp_pd(value_and_mantissa_loss, _mm256_setzero_pd(), _CMP_EQ_OQ); + __m256d value_or_mantissa_loss = _mm256_or_pd(mValue, _mm256_castsi256_pd(mantissa_loss)); + return _mm256_blendv_pd(value_or_mantissa_loss, mValue, is_zero); +#elif defined(JPH_USE_SSE4_1) + __m128i mantissa_loss = _mm_set1_epi64x(cDoubleToFloatMantissaLoss); + __m128d zero = _mm_setzero_pd(); + __m128d value_and_mantissa_loss_low = _mm_and_pd(mValue.mLow, _mm_castsi128_pd(mantissa_loss)); + __m128d is_zero_low = _mm_cmpeq_pd(value_and_mantissa_loss_low, zero); + __m128d value_or_mantissa_loss_low = _mm_or_pd(mValue.mLow, _mm_castsi128_pd(mantissa_loss)); + __m128d value_and_mantissa_loss_high = _mm_and_pd(mValue.mHigh, _mm_castsi128_pd(mantissa_loss)); + __m128d is_zero_high = _mm_cmpeq_pd(value_and_mantissa_loss_high, zero); + __m128d value_or_mantissa_loss_high = _mm_or_pd(mValue.mHigh, _mm_castsi128_pd(mantissa_loss)); + return DVec3({ _mm_blendv_pd(value_or_mantissa_loss_low, mValue.mLow, is_zero_low), _mm_blendv_pd(value_or_mantissa_loss_high, mValue.mHigh, is_zero_high) }); +#elif defined(JPH_USE_NEON) + float64x2_t mantissa_loss = vreinterpretq_f64_u64(vdupq_n_u64(cDoubleToFloatMantissaLoss)); + float64x2_t zero = vdupq_n_f64(0.0); + float64x2_t value_and_mantissa_loss_low = vandq_s64(mValue.val[0], mantissa_loss); + float64x2_t is_zero_low = vceqq_f64(value_and_mantissa_loss_low, zero); + float64x2_t value_or_mantissa_loss_low = vorrq_s64(mValue.val[0], mantissa_loss); + float64x2_t value_and_mantissa_loss_high = vandq_s64(mValue.val[1], mantissa_loss); + float64x2_t value_low = vbslq_f64(is_zero_low, mValue.val[0], value_or_mantissa_loss_low); + float64x2_t is_zero_high = vceqq_f64(value_and_mantissa_loss_high, zero); + float64x2_t value_or_mantissa_loss_high = vorrq_s64(mValue.val[1], mantissa_loss); + float64x2_t value_high = vbslq_f64(is_zero_high, mValue.val[1], value_or_mantissa_loss_high); + return DVec3({ value_low, value_high }); +#else + uint64 ux = BitCast(mF64[0]); + uint64 uy = BitCast(mF64[1]); + uint64 uz = BitCast(mF64[2]); + + double x = BitCast((ux & cDoubleToFloatMantissaLoss) == 0? ux : (ux | cDoubleToFloatMantissaLoss)); + double y = BitCast((uy & cDoubleToFloatMantissaLoss) == 0? uy : (uy | cDoubleToFloatMantissaLoss)); + double z = BitCast((uz & cDoubleToFloatMantissaLoss) == 0? uz : (uz | cDoubleToFloatMantissaLoss)); + + return DVec3(x, y, z); +#endif +} + +Vec3 DVec3::ToVec3RoundDown() const +{ + DVec3 to_zero = PrepareRoundToZero(); + DVec3 to_inf = PrepareRoundToInf(); + return Vec3(DVec3::sSelect(to_zero, to_inf, DVec3::sLess(*this, DVec3::sZero()))); +} + +Vec3 DVec3::ToVec3RoundUp() const +{ + DVec3 to_zero = PrepareRoundToZero(); + DVec3 to_inf = PrepareRoundToInf(); + return Vec3(DVec3::sSelect(to_inf, to_zero, DVec3::sLess(*this, DVec3::sZero()))); +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Double3.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Double3.h new file mode 100644 index 00000000000..90d3c1642ca --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Double3.h @@ -0,0 +1,48 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +/// Class that holds 3 doubles. Used as a storage class. Convert to DVec3 for calculations. +class [[nodiscard]] Double3 +{ +public: + JPH_OVERRIDE_NEW_DELETE + + Double3() = default; ///< Intentionally not initialized for performance reasons + Double3(const Double3 &inRHS) = default; + Double3 & operator = (const Double3 &inRHS) = default; + Double3(double inX, double inY, double inZ) : x(inX), y(inY), z(inZ) { } + + double operator [] (int inCoordinate) const + { + JPH_ASSERT(inCoordinate < 3); + return *(&x + inCoordinate); + } + + bool operator == (const Double3 &inRHS) const + { + return x == inRHS.x && y == inRHS.y && z == inRHS.z; + } + + bool operator != (const Double3 &inRHS) const + { + return x != inRHS.x || y != inRHS.y || z != inRHS.z; + } + + double x; + double y; + double z; +}; + +static_assert(is_trivial(), "Is supposed to be a trivial type!"); + +JPH_NAMESPACE_END + +// Create a std::hash for Double3 +JPH_MAKE_HASHABLE(JPH::Double3, t.x, t.y, t.z) diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/DynMatrix.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/DynMatrix.h new file mode 100644 index 00000000000..76db294c782 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/DynMatrix.h @@ -0,0 +1,31 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2022 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +JPH_NAMESPACE_BEGIN + +/// Dynamic resizable matrix class +class [[nodiscard]] DynMatrix +{ +public: + /// Constructor + DynMatrix(const DynMatrix &) = default; + DynMatrix(uint inRows, uint inCols) : mRows(inRows), mCols(inCols) { mElements.resize(inRows * inCols); } + + /// Access an element + float operator () (uint inRow, uint inCol) const { JPH_ASSERT(inRow < mRows && inCol < mCols); return mElements[inRow * mCols + inCol]; } + float & operator () (uint inRow, uint inCol) { JPH_ASSERT(inRow < mRows && inCol < mCols); return mElements[inRow * mCols + inCol]; } + + /// Get dimensions + uint GetCols() const { return mCols; } + uint GetRows() const { return mRows; } + +private: + uint mRows; + uint mCols; + Array mElements; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/EigenValueSymmetric.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/EigenValueSymmetric.h new file mode 100644 index 00000000000..43436ee962d --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/EigenValueSymmetric.h @@ -0,0 +1,175 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +/// Function to determine the eigen vectors and values of a N x N real symmetric matrix +/// by Jacobi transformations. This method is most suitable for N < 10. +/// +/// Taken and adapted from Numerical Recipies paragraph 11.1 +/// +/// An eigen vector is a vector v for which \f$A \: v = \lambda \: v\f$ +/// +/// Where: +/// A: A square matrix. +/// \f$\lambda\f$: a non-zero constant value. +/// +/// @see https://en.wikipedia.org/wiki/Eigenvalues_and_eigenvectors +/// +/// Matrix is a matrix type, which has dimensions N x N. +/// @param inMatrix is the matrix of which to return the eigenvalues and vectors +/// @param outEigVec will contain a matrix whose columns contain the normalized eigenvectors (must be identity before call) +/// @param outEigVal will contain the eigenvalues +template +bool EigenValueSymmetric(const Matrix &inMatrix, Matrix &outEigVec, Vector &outEigVal) +{ + // This algorithm works with very small numbers and can trigger invalid float exceptions when not flushing denormals + FPFlushDenormals flush_denormals; + (void)flush_denormals; + + // Maximum number of sweeps to make + const int cMaxSweeps = 50; + + // Get problem dimension + const uint n = inMatrix.GetRows(); + + // Make sure the dimensions are right + JPH_ASSERT(inMatrix.GetRows() == n); + JPH_ASSERT(inMatrix.GetCols() == n); + JPH_ASSERT(outEigVec.GetRows() == n); + JPH_ASSERT(outEigVec.GetCols() == n); + JPH_ASSERT(outEigVal.GetRows() == n); + JPH_ASSERT(outEigVec.IsIdentity()); + + // Get the matrix in a so we can mess with it + Matrix a = inMatrix; + + Vector b, z; + + for (uint ip = 0; ip < n; ++ip) + { + // Initialize b to diagonal of a + b[ip] = a(ip, ip); + + // Initialize output to diagonal of a + outEigVal[ip] = a(ip, ip); + + // Reset z + z[ip] = 0.0f; + } + + for (int sweep = 0; sweep < cMaxSweeps; ++sweep) + { + // Get the sum of the off-diagonal elements of a + float sm = 0.0f; + for (uint ip = 0; ip < n - 1; ++ip) + for (uint iq = ip + 1; iq < n; ++iq) + sm += abs(a(ip, iq)); + + // Normal return, convergence to machine underflow + if (sm == 0.0f) + { + // Sanity checks + #ifdef JPH_ENABLE_ASSERTS + for (uint c = 0; c < n; ++c) + { + // Check if the eigenvector is normalized + JPH_ASSERT(outEigVec.GetColumn(c).IsNormalized()); + + // Check if inMatrix * eigen_vector = eigen_value * eigen_vector + Vector mat_eigvec = inMatrix * outEigVec.GetColumn(c); + Vector eigval_eigvec = outEigVal[c] * outEigVec.GetColumn(c); + JPH_ASSERT(mat_eigvec.IsClose(eigval_eigvec, max(mat_eigvec.LengthSq(), eigval_eigvec.LengthSq()) * 1.0e-6f)); + } + #endif + + // Success + return true; + } + + // On the first three sweeps use a fraction of the sum of the off diagonal elements as threshold + float tresh = sweep < 4? 0.2f * sm / Square(n) : 0.0f; + + for (uint ip = 0; ip < n - 1; ++ip) + for (uint iq = ip + 1; iq < n; ++iq) + { + float g = 100.0f * abs(a(ip, iq)); + + // After four sweeps, skip the rotation if the off-diagonal element is small + if (sweep > 4 + && abs(outEigVal[ip]) + g == abs(outEigVal[ip]) + && abs(outEigVal[iq]) + g == abs(outEigVal[iq])) + { + a(ip, iq) = 0.0f; + } + else if (abs(a(ip, iq)) > tresh) + { + float h = outEigVal[iq] - outEigVal[ip]; + + float t; + if (abs(h) + g == abs(h)) + { + t = a(ip, iq) / h; + } + else + { + float theta = 0.5f * h / a(ip, iq); // Warning: Can become inf if a(ip, iq) too small + t = 1.0f / (abs(theta) + sqrt(1.0f + theta * theta)); // Warning: Squaring large value can make it inf + if (theta < 0.0f) t = -t; + } + + float c = 1.0f / sqrt(1.0f + t * t); + float s = t * c; + float tau = s / (1.0f + c); + h = t * a(ip, iq); + + a(ip, iq) = 0.0f; + + // !Modification from Numerical Recipes! + // h can become infinite due to numerical overflow, this only happens when a(ip, iq) is very small + // so we can safely set a(ip, iq) to zero and skip the rotation, see lines marked with 'Warning' above. + if (!isnan(h)) + { + z[ip] -= h; + z[iq] += h; + + outEigVal[ip] -= h; + outEigVal[iq] += h; + + #define JPH_EVS_ROTATE(a, i, j, k, l) \ + g = a(i, j), \ + h = a(k, l), \ + a(i, j) = g - s * (h + g * tau), \ + a(k, l) = h + s * (g - h * tau) + + uint j; + for (j = 0; j < ip; ++j) JPH_EVS_ROTATE(a, j, ip, j, iq); + for (j = ip + 1; j < iq; ++j) JPH_EVS_ROTATE(a, ip, j, j, iq); + for (j = iq + 1; j < n; ++j) JPH_EVS_ROTATE(a, ip, j, iq, j); + for (j = 0; j < n; ++j) JPH_EVS_ROTATE(outEigVec, j, ip, j, iq); + + #undef JPH_EVS_ROTATE + } + } + } + + // Update eigenvalues with the sum of ta_pq and reinitialize z + for (uint ip = 0; ip < n; ++ip) + { + b[ip] += z[ip]; + outEigVal[ip] = b[ip]; + z[ip] = 0.0f; + } + } + + // Failure + JPH_ASSERT(false, "Too many iterations"); + return false; +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/FindRoot.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/FindRoot.h new file mode 100644 index 00000000000..21fef9f61f0 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/FindRoot.h @@ -0,0 +1,42 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +JPH_NAMESPACE_BEGIN + +/// Find the roots of \f$inA \: x^2 + inB \: x + inC = 0\f$. +/// @return The number of roots, actual roots in outX1 and outX2. +/// If number of roots returned is 1 then outX1 == outX2. +template +inline int FindRoot(const T inA, const T inB, const T inC, T &outX1, T &outX2) +{ + // Check if this is a linear equation + if (inA == T(0)) + { + // Check if this is a constant equation + if (inB == T(0)) + return 0; + + // Linear equation with 1 solution + outX1 = outX2 = -inC / inB; + return 1; + } + + // See Numerical Recipes in C, Chapter 5.6 Quadratic and Cubic Equations + T det = Square(inB) - T(4) * inA * inC; + if (det < T(0)) + return 0; + T q = (inB + Sign(inB) * sqrt(det)) / T(-2); + outX1 = q / inA; + if (q == T(0)) + { + outX2 = outX1; + return 1; + } + outX2 = inC / q; + return 2; +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Float2.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Float2.h new file mode 100644 index 00000000000..9c05d4139cc --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Float2.h @@ -0,0 +1,36 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +JPH_NAMESPACE_BEGIN + +/// Class that holds 2 floats, used as a storage class mainly. +class [[nodiscard]] Float2 +{ +public: + JPH_OVERRIDE_NEW_DELETE + + Float2() = default; ///< Intentionally not initialized for performance reasons + Float2(const Float2 &inRHS) = default; + Float2 & operator = (const Float2 &inRHS) = default; + Float2(float inX, float inY) : x(inX), y(inY) { } + + bool operator == (const Float2 &inRHS) const { return x == inRHS.x && y == inRHS.y; } + bool operator != (const Float2 &inRHS) const { return x != inRHS.x || y != inRHS.y; } + + /// To String + friend ostream & operator << (ostream &inStream, const Float2 &inV) + { + inStream << inV.x << ", " << inV.y; + return inStream; + } + + float x; + float y; +}; + +static_assert(is_trivial(), "Is supposed to be a trivial type!"); + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Float3.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Float3.h new file mode 100644 index 00000000000..e288201b971 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Float3.h @@ -0,0 +1,50 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +/// Class that holds 3 floats. Used as a storage class. Convert to Vec3 for calculations. +class [[nodiscard]] Float3 +{ +public: + JPH_OVERRIDE_NEW_DELETE + + Float3() = default; ///< Intentionally not initialized for performance reasons + Float3(const Float3 &inRHS) = default; + Float3 & operator = (const Float3 &inRHS) = default; + constexpr Float3(float inX, float inY, float inZ) : x(inX), y(inY), z(inZ) { } + + float operator [] (int inCoordinate) const + { + JPH_ASSERT(inCoordinate < 3); + return *(&x + inCoordinate); + } + + bool operator == (const Float3 &inRHS) const + { + return x == inRHS.x && y == inRHS.y && z == inRHS.z; + } + + bool operator != (const Float3 &inRHS) const + { + return x != inRHS.x || y != inRHS.y || z != inRHS.z; + } + + float x; + float y; + float z; +}; + +using VertexList = Array; + +static_assert(is_trivial(), "Is supposed to be a trivial type!"); + +JPH_NAMESPACE_END + +// Create a std::hash for Float3 +JPH_MAKE_HASHABLE(JPH::Float3, t.x, t.y, t.z) diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Float4.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Float4.h new file mode 100644 index 00000000000..30845f638a5 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Float4.h @@ -0,0 +1,33 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +JPH_NAMESPACE_BEGIN + +/// Class that holds 4 float values. Convert to Vec4 to perform calculations. +class [[nodiscard]] Float4 +{ +public: + JPH_OVERRIDE_NEW_DELETE + + Float4() = default; ///< Intentionally not initialized for performance reasons + Float4(const Float4 &inRHS) = default; + Float4(float inX, float inY, float inZ, float inW) : x(inX), y(inY), z(inZ), w(inW) { } + + float operator [] (int inCoordinate) const + { + JPH_ASSERT(inCoordinate < 4); + return *(&x + inCoordinate); + } + + float x; + float y; + float z; + float w; +}; + +static_assert(is_trivial(), "Is supposed to be a trivial type!"); + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/GaussianElimination.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/GaussianElimination.h new file mode 100644 index 00000000000..a2bfd38cb6e --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/GaussianElimination.h @@ -0,0 +1,102 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +JPH_NAMESPACE_BEGIN + +/// This function performs Gauss-Jordan elimination to solve a matrix equation. +/// A must be an NxN matrix and B must be an NxM matrix forming the equation A * x = B +/// on output B will contain x and A will be destroyed. +/// +/// This code can be used for example to compute the inverse of a matrix. +/// Set A to the matrix to invert, set B to identity and let GaussianElimination solve +/// the equation, on return B will be the inverse of A. And A is destroyed. +/// +/// Taken and adapted from Numerical Recipies in C paragraph 2.1 +template +bool GaussianElimination(MatrixA &ioA, MatrixB &ioB, float inTolerance = 1.0e-16f) +{ + // Get problem dimensions + const uint n = ioA.GetCols(); + const uint m = ioB.GetCols(); + + // Check matrix requirement + JPH_ASSERT(ioA.GetRows() == n); + JPH_ASSERT(ioB.GetRows() == n); + + // Create array for bookkeeping on pivoting + int *ipiv = (int *)JPH_STACK_ALLOC(n * sizeof(int)); + memset(ipiv, 0, n * sizeof(int)); + + for (uint i = 0; i < n; ++i) + { + // Initialize pivot element as the diagonal + uint pivot_row = i, pivot_col = i; + + // Determine pivot element + float largest_element = 0.0f; + for (uint j = 0; j < n; ++j) + if (ipiv[j] != 1) + for (uint k = 0; k < n; ++k) + { + if (ipiv[k] == 0) + { + float element = abs(ioA(j, k)); + if (element >= largest_element) + { + largest_element = element; + pivot_row = j; + pivot_col = k; + } + } + else if (ipiv[k] > 1) + { + return false; + } + } + + // Mark this column as used + ++ipiv[pivot_col]; + + // Exchange rows when needed so that the pivot element is at ioA(pivot_col, pivot_col) instead of at ioA(pivot_row, pivot_col) + if (pivot_row != pivot_col) + { + for (uint j = 0; j < n; ++j) + swap(ioA(pivot_row, j), ioA(pivot_col, j)); + for (uint j = 0; j < m; ++j) + swap(ioB(pivot_row, j), ioB(pivot_col, j)); + } + + // Get diagonal element that we are about to set to 1 + float diagonal_element = ioA(pivot_col, pivot_col); + if (abs(diagonal_element) < inTolerance) + return false; + + // Divide the whole row by the pivot element, making ioA(pivot_col, pivot_col) = 1 + for (uint j = 0; j < n; ++j) + ioA(pivot_col, j) /= diagonal_element; + for (uint j = 0; j < m; ++j) + ioB(pivot_col, j) /= diagonal_element; + ioA(pivot_col, pivot_col) = 1.0f; + + // Next reduce the rows, except for the pivot one, + // after this step the pivot_col column is zero except for the pivot element which is 1 + for (uint j = 0; j < n; ++j) + if (j != pivot_col) + { + float element = ioA(j, pivot_col); + for (uint k = 0; k < n; ++k) + ioA(j, k) -= ioA(pivot_col, k) * element; + for (uint k = 0; k < m; ++k) + ioB(j, k) -= ioB(pivot_col, k) * element; + ioA(j, pivot_col) = 0.0f; + } + } + + // Success + return true; +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/HalfFloat.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/HalfFloat.h new file mode 100644 index 00000000000..d28071e3038 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/HalfFloat.h @@ -0,0 +1,204 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +using HalfFloat = uint16; + +// Define half float constant values +static constexpr HalfFloat HALF_FLT_MAX = 0x7bff; +static constexpr HalfFloat HALF_FLT_MAX_NEGATIVE = 0xfbff; +static constexpr HalfFloat HALF_FLT_INF = 0x7c00; +static constexpr HalfFloat HALF_FLT_INF_NEGATIVE = 0xfc00; +static constexpr HalfFloat HALF_FLT_NANQ = 0x7e00; +static constexpr HalfFloat HALF_FLT_NANQ_NEGATIVE = 0xfe00; + +namespace HalfFloatConversion { + +// Layout of a float +static constexpr int FLOAT_SIGN_POS = 31; +static constexpr int FLOAT_EXPONENT_POS = 23; +static constexpr int FLOAT_EXPONENT_BITS = 8; +static constexpr int FLOAT_EXPONENT_MASK = (1 << FLOAT_EXPONENT_BITS) - 1; +static constexpr int FLOAT_EXPONENT_BIAS = 127; +static constexpr int FLOAT_MANTISSA_BITS = 23; +static constexpr int FLOAT_MANTISSA_MASK = (1 << FLOAT_MANTISSA_BITS) - 1; +static constexpr int FLOAT_EXPONENT_AND_MANTISSA_MASK = FLOAT_MANTISSA_MASK + (FLOAT_EXPONENT_MASK << FLOAT_EXPONENT_POS); + +// Layout of half float +static constexpr int HALF_FLT_SIGN_POS = 15; +static constexpr int HALF_FLT_EXPONENT_POS = 10; +static constexpr int HALF_FLT_EXPONENT_BITS = 5; +static constexpr int HALF_FLT_EXPONENT_MASK = (1 << HALF_FLT_EXPONENT_BITS) - 1; +static constexpr int HALF_FLT_EXPONENT_BIAS = 15; +static constexpr int HALF_FLT_MANTISSA_BITS = 10; +static constexpr int HALF_FLT_MANTISSA_MASK = (1 << HALF_FLT_MANTISSA_BITS) - 1; +static constexpr int HALF_FLT_EXPONENT_AND_MANTISSA_MASK = HALF_FLT_MANTISSA_MASK + (HALF_FLT_EXPONENT_MASK << HALF_FLT_EXPONENT_POS); + +/// Define half-float rounding modes +enum ERoundingMode +{ + ROUND_TO_NEG_INF, ///< Round to negative infinity + ROUND_TO_POS_INF, ///< Round to positive infinity + ROUND_TO_NEAREST, ///< Round to nearest value +}; + +/// Convert a float (32-bits) to a half float (16-bits), fallback version when no intrinsics available +template +inline HalfFloat FromFloatFallback(float inV) +{ + // Reinterpret the float as an uint32 + uint32 value = BitCast(inV); + + // Extract exponent + uint32 exponent = (value >> FLOAT_EXPONENT_POS) & FLOAT_EXPONENT_MASK; + + // Extract mantissa + uint32 mantissa = value & FLOAT_MANTISSA_MASK; + + // Extract the sign and move it into the right spot for the half float (so we can just or it in at the end) + HalfFloat hf_sign = HalfFloat(value >> (FLOAT_SIGN_POS - HALF_FLT_SIGN_POS)) & (1 << HALF_FLT_SIGN_POS); + + // Check NaN or INF + if (exponent == FLOAT_EXPONENT_MASK) // NaN or INF + return hf_sign | (mantissa == 0? HALF_FLT_INF : HALF_FLT_NANQ); + + // Rebias the exponent for half floats + int rebiased_exponent = int(exponent) - FLOAT_EXPONENT_BIAS + HALF_FLT_EXPONENT_BIAS; + + // Check overflow to infinity + if (rebiased_exponent >= HALF_FLT_EXPONENT_MASK) + { + bool round_up = RoundingMode == ROUND_TO_NEAREST || (hf_sign == 0) == (RoundingMode == ROUND_TO_POS_INF); + return hf_sign | (round_up? HALF_FLT_INF : HALF_FLT_MAX); + } + + // Check underflow to zero + if (rebiased_exponent < -HALF_FLT_MANTISSA_BITS) + { + bool round_up = RoundingMode != ROUND_TO_NEAREST && (hf_sign == 0) == (RoundingMode == ROUND_TO_POS_INF) && (value & FLOAT_EXPONENT_AND_MANTISSA_MASK) != 0; + return hf_sign | (round_up? 1 : 0); + } + + HalfFloat hf_exponent; + int shift; + if (rebiased_exponent <= 0) + { + // Underflow to denormalized number + hf_exponent = 0; + mantissa |= 1 << FLOAT_MANTISSA_BITS; // Add the implicit 1 bit to the mantissa + shift = FLOAT_MANTISSA_BITS - HALF_FLT_MANTISSA_BITS + 1 - rebiased_exponent; + } + else + { + // Normal half float + hf_exponent = HalfFloat(rebiased_exponent << HALF_FLT_EXPONENT_POS); + shift = FLOAT_MANTISSA_BITS - HALF_FLT_MANTISSA_BITS; + } + + // Compose the half float + HalfFloat hf_mantissa = HalfFloat(mantissa >> shift); + HalfFloat hf = hf_sign | hf_exponent | hf_mantissa; + + // Calculate the remaining bits that we're discarding + uint remainder = mantissa & ((1 << shift) - 1); + + if constexpr (RoundingMode == ROUND_TO_NEAREST) + { + // Round to nearest + uint round_threshold = 1 << (shift - 1); + if (remainder > round_threshold // Above threshold, we must always round + || (remainder == round_threshold && (hf_mantissa & 1))) // When equal, round to nearest even + hf++; // May overflow to infinity + } + else + { + // Round up or down (truncate) depending on the rounding mode + bool round_up = (hf_sign == 0) == (RoundingMode == ROUND_TO_POS_INF) && remainder != 0; + if (round_up) + hf++; // May overflow to infinity + } + + return hf; +} + +/// Convert a float (32-bits) to a half float (16-bits) +template +JPH_INLINE HalfFloat FromFloat(float inV) +{ +#ifdef JPH_USE_F16C + union + { + __m128i u128; + HalfFloat u16[8]; + } hf; + __m128 val = _mm_load_ss(&inV); + switch (RoundingMode) + { + case ROUND_TO_NEG_INF: + hf.u128 = _mm_cvtps_ph(val, _MM_FROUND_TO_NEG_INF); + break; + case ROUND_TO_POS_INF: + hf.u128 = _mm_cvtps_ph(val, _MM_FROUND_TO_POS_INF); + break; + case ROUND_TO_NEAREST: + hf.u128 = _mm_cvtps_ph(val, _MM_FROUND_TO_NEAREST_INT); + break; + } + return hf.u16[0]; +#else + return FromFloatFallback(inV); +#endif +} + +/// Convert 4 half floats (lower 64 bits) to floats, fallback version when no intrinsics available +inline Vec4 ToFloatFallback(UVec4Arg inValue) +{ + // Unpack half floats to 4 uint32's + UVec4 value = inValue.Expand4Uint16Lo(); + + // Normal half float path, extract the exponent and mantissa, shift them into place and update the exponent bias + UVec4 exponent_mantissa = UVec4::sAnd(value, UVec4::sReplicate(HALF_FLT_EXPONENT_AND_MANTISSA_MASK)).LogicalShiftLeft() + UVec4::sReplicate((FLOAT_EXPONENT_BIAS - HALF_FLT_EXPONENT_BIAS) << FLOAT_EXPONENT_POS); + + // Denormalized half float path, renormalize the float + UVec4 exponent_mantissa_denormalized = ((exponent_mantissa + UVec4::sReplicate(1 << FLOAT_EXPONENT_POS)).ReinterpretAsFloat() - UVec4::sReplicate((FLOAT_EXPONENT_BIAS - HALF_FLT_EXPONENT_BIAS + 1) << FLOAT_EXPONENT_POS).ReinterpretAsFloat()).ReinterpretAsInt(); + + // NaN / INF path, set all exponent bits + UVec4 exponent_mantissa_nan_inf = UVec4::sOr(exponent_mantissa, UVec4::sReplicate(FLOAT_EXPONENT_MASK << FLOAT_EXPONENT_POS)); + + // Get the exponent to determine which of the paths we should take + UVec4 exponent_mask = UVec4::sReplicate(HALF_FLT_EXPONENT_MASK << HALF_FLT_EXPONENT_POS); + UVec4 exponent = UVec4::sAnd(value, exponent_mask); + UVec4 is_denormalized = UVec4::sEquals(exponent, UVec4::sZero()); + UVec4 is_nan_inf = UVec4::sEquals(exponent, exponent_mask); + + // Select the correct result + UVec4 result_exponent_mantissa = UVec4::sSelect(UVec4::sSelect(exponent_mantissa, exponent_mantissa_nan_inf, is_nan_inf), exponent_mantissa_denormalized, is_denormalized); + + // Extract the sign bit and shift it to the left + UVec4 sign = UVec4::sAnd(value, UVec4::sReplicate(1 << HALF_FLT_SIGN_POS)).LogicalShiftLeft(); + + // Construct the float + return UVec4::sOr(sign, result_exponent_mantissa).ReinterpretAsFloat(); +} + +/// Convert 4 half floats (lower 64 bits) to floats +JPH_INLINE Vec4 ToFloat(UVec4Arg inValue) +{ +#if defined(JPH_USE_F16C) + return _mm_cvtph_ps(inValue.mValue); +#elif defined(JPH_USE_NEON) + return vcvt_f32_f16(vreinterpret_f16_f32(vget_low_f32(inValue.mValue))); +#else + return ToFloatFallback(inValue); +#endif +} + +} // HalfFloatConversion + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Mat44.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Mat44.h new file mode 100644 index 00000000000..1a1e254c564 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Mat44.h @@ -0,0 +1,243 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +/// Holds a 4x4 matrix of floats, but supports also operations on the 3x3 upper left part of the matrix. +class [[nodiscard]] alignas(JPH_VECTOR_ALIGNMENT) Mat44 +{ +public: + JPH_OVERRIDE_NEW_DELETE + + // Underlying column type + using Type = Vec4::Type; + + // Argument type + using ArgType = Mat44Arg; + + /// Constructor + Mat44() = default; ///< Intentionally not initialized for performance reasons + JPH_INLINE Mat44(Vec4Arg inC1, Vec4Arg inC2, Vec4Arg inC3, Vec4Arg inC4); + JPH_INLINE Mat44(Vec4Arg inC1, Vec4Arg inC2, Vec4Arg inC3, Vec3Arg inC4); + Mat44(const Mat44 &inM2) = default; + Mat44 & operator = (const Mat44 &inM2) = default; + JPH_INLINE Mat44(Type inC1, Type inC2, Type inC3, Type inC4); + + /// Zero matrix + static JPH_INLINE Mat44 sZero(); + + /// Identity matrix + static JPH_INLINE Mat44 sIdentity(); + + /// Matrix filled with NaN's + static JPH_INLINE Mat44 sNaN(); + + /// Load 16 floats from memory + static JPH_INLINE Mat44 sLoadFloat4x4(const Float4 *inV); + + /// Load 16 floats from memory, 16 bytes aligned + static JPH_INLINE Mat44 sLoadFloat4x4Aligned(const Float4 *inV); + + /// Rotate around X, Y or Z axis (angle in radians) + static JPH_INLINE Mat44 sRotationX(float inX); + static JPH_INLINE Mat44 sRotationY(float inY); + static JPH_INLINE Mat44 sRotationZ(float inZ); + + /// Rotate around arbitrary axis + static JPH_INLINE Mat44 sRotation(Vec3Arg inAxis, float inAngle); + + /// Rotate from quaternion + static JPH_INLINE Mat44 sRotation(QuatArg inQuat); + + /// Get matrix that translates + static JPH_INLINE Mat44 sTranslation(Vec3Arg inV); + + /// Get matrix that rotates and translates + static JPH_INLINE Mat44 sRotationTranslation(QuatArg inR, Vec3Arg inT); + + /// Get inverse matrix of sRotationTranslation + static JPH_INLINE Mat44 sInverseRotationTranslation(QuatArg inR, Vec3Arg inT); + + /// Get matrix that scales uniformly + static JPH_INLINE Mat44 sScale(float inScale); + + /// Get matrix that scales (produces a matrix with (inV, 1) on its diagonal) + static JPH_INLINE Mat44 sScale(Vec3Arg inV); + + /// Get outer product of inV and inV2 (equivalent to \f$inV1 \otimes inV2\f$) + static JPH_INLINE Mat44 sOuterProduct(Vec3Arg inV1, Vec3Arg inV2); + + /// Get matrix that represents a cross product \f$A \times B = \text{sCrossProduct}(A) \: B\f$ + static JPH_INLINE Mat44 sCrossProduct(Vec3Arg inV); + + /// Returns matrix ML so that \f$ML(q) \: p = q \: p\f$ (where p and q are quaternions) + static JPH_INLINE Mat44 sQuatLeftMultiply(QuatArg inQ); + + /// Returns matrix MR so that \f$MR(q) \: p = p \: q\f$ (where p and q are quaternions) + static JPH_INLINE Mat44 sQuatRightMultiply(QuatArg inQ); + + /// Returns a look at matrix that transforms from world space to view space + /// @param inPos Position of the camera + /// @param inTarget Target of the camera + /// @param inUp Up vector + static JPH_INLINE Mat44 sLookAt(Vec3Arg inPos, Vec3Arg inTarget, Vec3Arg inUp); + + /// Returns a right-handed perspective projection matrix + static JPH_INLINE Mat44 sPerspective(float inFovY, float inAspect, float inNear, float inFar); + + /// Get float component by element index + JPH_INLINE float operator () (uint inRow, uint inColumn) const { JPH_ASSERT(inRow < 4); JPH_ASSERT(inColumn < 4); return mCol[inColumn].mF32[inRow]; } + JPH_INLINE float & operator () (uint inRow, uint inColumn) { JPH_ASSERT(inRow < 4); JPH_ASSERT(inColumn < 4); return mCol[inColumn].mF32[inRow]; } + + /// Comparison + JPH_INLINE bool operator == (Mat44Arg inM2) const; + JPH_INLINE bool operator != (Mat44Arg inM2) const { return !(*this == inM2); } + + /// Test if two matrices are close + JPH_INLINE bool IsClose(Mat44Arg inM2, float inMaxDistSq = 1.0e-12f) const; + + /// Multiply matrix by matrix + JPH_INLINE Mat44 operator * (Mat44Arg inM) const; + + /// Multiply vector by matrix + JPH_INLINE Vec3 operator * (Vec3Arg inV) const; + JPH_INLINE Vec4 operator * (Vec4Arg inV) const; + + /// Multiply vector by only 3x3 part of the matrix + JPH_INLINE Vec3 Multiply3x3(Vec3Arg inV) const; + + /// Multiply vector by only 3x3 part of the transpose of the matrix (\f$result = this^T \: inV\f$) + JPH_INLINE Vec3 Multiply3x3Transposed(Vec3Arg inV) const; + + /// Multiply 3x3 matrix by 3x3 matrix + JPH_INLINE Mat44 Multiply3x3(Mat44Arg inM) const; + + /// Multiply transpose of 3x3 matrix by 3x3 matrix (\f$result = this^T \: inM\f$) + JPH_INLINE Mat44 Multiply3x3LeftTransposed(Mat44Arg inM) const; + + /// Multiply 3x3 matrix by the transpose of a 3x3 matrix (\f$result = this \: inM^T\f$) + JPH_INLINE Mat44 Multiply3x3RightTransposed(Mat44Arg inM) const; + + /// Multiply matrix with float + JPH_INLINE Mat44 operator * (float inV) const; + friend JPH_INLINE Mat44 operator * (float inV, Mat44Arg inM) { return inM * inV; } + + /// Multiply matrix with float + JPH_INLINE Mat44 & operator *= (float inV); + + /// Per element addition of matrix + JPH_INLINE Mat44 operator + (Mat44Arg inM) const; + + /// Negate + JPH_INLINE Mat44 operator - () const; + + /// Per element subtraction of matrix + JPH_INLINE Mat44 operator - (Mat44Arg inM) const; + + /// Per element addition of matrix + JPH_INLINE Mat44 & operator += (Mat44Arg inM); + + /// Access to the columns + JPH_INLINE Vec3 GetAxisX() const { return Vec3(mCol[0]); } + JPH_INLINE void SetAxisX(Vec3Arg inV) { mCol[0] = Vec4(inV, 0.0f); } + JPH_INLINE Vec3 GetAxisY() const { return Vec3(mCol[1]); } + JPH_INLINE void SetAxisY(Vec3Arg inV) { mCol[1] = Vec4(inV, 0.0f); } + JPH_INLINE Vec3 GetAxisZ() const { return Vec3(mCol[2]); } + JPH_INLINE void SetAxisZ(Vec3Arg inV) { mCol[2] = Vec4(inV, 0.0f); } + JPH_INLINE Vec3 GetTranslation() const { return Vec3(mCol[3]); } + JPH_INLINE void SetTranslation(Vec3Arg inV) { mCol[3] = Vec4(inV, 1.0f); } + JPH_INLINE Vec3 GetDiagonal3() const { return Vec3(mCol[0][0], mCol[1][1], mCol[2][2]); } + JPH_INLINE void SetDiagonal3(Vec3Arg inV) { mCol[0][0] = inV.GetX(); mCol[1][1] = inV.GetY(); mCol[2][2] = inV.GetZ(); } + JPH_INLINE Vec4 GetDiagonal4() const { return Vec4(mCol[0][0], mCol[1][1], mCol[2][2], mCol[3][3]); } + JPH_INLINE void SetDiagonal4(Vec4Arg inV) { mCol[0][0] = inV.GetX(); mCol[1][1] = inV.GetY(); mCol[2][2] = inV.GetZ(); mCol[3][3] = inV.GetW(); } + JPH_INLINE Vec3 GetColumn3(uint inCol) const { JPH_ASSERT(inCol < 4); return Vec3(mCol[inCol]); } + JPH_INLINE void SetColumn3(uint inCol, Vec3Arg inV) { JPH_ASSERT(inCol < 4); mCol[inCol] = Vec4(inV, inCol == 3? 1.0f : 0.0f); } + JPH_INLINE Vec4 GetColumn4(uint inCol) const { JPH_ASSERT(inCol < 4); return mCol[inCol]; } + JPH_INLINE void SetColumn4(uint inCol, Vec4Arg inV) { JPH_ASSERT(inCol < 4); mCol[inCol] = inV; } + + /// Store matrix to memory + JPH_INLINE void StoreFloat4x4(Float4 *outV) const; + + /// Transpose matrix + JPH_INLINE Mat44 Transposed() const; + + /// Transpose 3x3 subpart of matrix + JPH_INLINE Mat44 Transposed3x3() const; + + /// Inverse 4x4 matrix + JPH_INLINE Mat44 Inversed() const; + + /// Inverse 4x4 matrix when it only contains rotation and translation + JPH_INLINE Mat44 InversedRotationTranslation() const; + + /// Get the determinant of a 3x3 matrix + JPH_INLINE float GetDeterminant3x3() const; + + /// Get the adjoint of a 3x3 matrix + JPH_INLINE Mat44 Adjointed3x3() const; + + /// Inverse 3x3 matrix + JPH_INLINE Mat44 Inversed3x3() const; + + /// *this = inM.Inversed3x3(), returns false if the matrix is singular in which case *this is unchanged + JPH_INLINE bool SetInversed3x3(Mat44Arg inM); + + /// Get rotation part only (note: retains the first 3 values from the bottom row) + JPH_INLINE Mat44 GetRotation() const; + + /// Get rotation part only (note: also clears the bottom row) + JPH_INLINE Mat44 GetRotationSafe() const; + + /// Updates the rotation part of this matrix (the first 3 columns) + JPH_INLINE void SetRotation(Mat44Arg inRotation); + + /// Convert to quaternion + JPH_INLINE Quat GetQuaternion() const; + + /// Get matrix that transforms a direction with the same transform as this matrix (length is not preserved) + JPH_INLINE Mat44 GetDirectionPreservingMatrix() const { return GetRotation().Inversed3x3().Transposed3x3(); } + + /// Pre multiply by translation matrix: result = this * Mat44::sTranslation(inTranslation) + JPH_INLINE Mat44 PreTranslated(Vec3Arg inTranslation) const; + + /// Post multiply by translation matrix: result = Mat44::sTranslation(inTranslation) * this (i.e. add inTranslation to the 4-th column) + JPH_INLINE Mat44 PostTranslated(Vec3Arg inTranslation) const; + + /// Scale a matrix: result = this * Mat44::sScale(inScale) + JPH_INLINE Mat44 PreScaled(Vec3Arg inScale) const; + + /// Scale a matrix: result = Mat44::sScale(inScale) * this + JPH_INLINE Mat44 PostScaled(Vec3Arg inScale) const; + + /// Decompose a matrix into a rotation & translation part and into a scale part so that: + /// this = return_value * Mat44::sScale(outScale). + /// This equation only holds when the matrix is orthogonal, if it is not the returned matrix + /// will be made orthogonal using the modified Gram-Schmidt algorithm (see: https://en.wikipedia.org/wiki/Gram%E2%80%93Schmidt_process) + JPH_INLINE Mat44 Decompose(Vec3 &outScale) const; + +#ifndef JPH_DOUBLE_PRECISION + /// In single precision mode just return the matrix itself + JPH_INLINE Mat44 ToMat44() const { return *this; } +#endif // !JPH_DOUBLE_PRECISION + + /// To String + friend ostream & operator << (ostream &inStream, Mat44Arg inM) + { + inStream << inM.mCol[0] << ", " << inM.mCol[1] << ", " << inM.mCol[2] << ", " << inM.mCol[3]; + return inStream; + } + +private: + Vec4 mCol[4]; ///< Column +}; + +static_assert(is_trivial(), "Is supposed to be a trivial type!"); + +JPH_NAMESPACE_END + +#include "Mat44.inl" diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Mat44.inl b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Mat44.inl new file mode 100644 index 00000000000..76577b7153a --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Mat44.inl @@ -0,0 +1,952 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +#define JPH_EL(r, c) mCol[c].mF32[r] + +Mat44::Mat44(Vec4Arg inC1, Vec4Arg inC2, Vec4Arg inC3, Vec4Arg inC4) : + mCol { inC1, inC2, inC3, inC4 } +{ +} + +Mat44::Mat44(Vec4Arg inC1, Vec4Arg inC2, Vec4Arg inC3, Vec3Arg inC4) : + mCol { inC1, inC2, inC3, Vec4(inC4, 1.0f) } +{ +} + +Mat44::Mat44(Type inC1, Type inC2, Type inC3, Type inC4) : + mCol { inC1, inC2, inC3, inC4 } +{ +} + +Mat44 Mat44::sZero() +{ + return Mat44(Vec4::sZero(), Vec4::sZero(), Vec4::sZero(), Vec4::sZero()); +} + +Mat44 Mat44::sIdentity() +{ + return Mat44(Vec4(1, 0, 0, 0), Vec4(0, 1, 0, 0), Vec4(0, 0, 1, 0), Vec4(0, 0, 0, 1)); +} + +Mat44 Mat44::sNaN() +{ + return Mat44(Vec4::sNaN(), Vec4::sNaN(), Vec4::sNaN(), Vec4::sNaN()); +} + +Mat44 Mat44::sLoadFloat4x4(const Float4 *inV) +{ + Mat44 result; + for (int c = 0; c < 4; ++c) + result.mCol[c] = Vec4::sLoadFloat4(inV + c); + return result; +} + +Mat44 Mat44::sLoadFloat4x4Aligned(const Float4 *inV) +{ + Mat44 result; + for (int c = 0; c < 4; ++c) + result.mCol[c] = Vec4::sLoadFloat4Aligned(inV + c); + return result; +} + +Mat44 Mat44::sRotationX(float inX) +{ + Vec4 sv, cv; + Vec4::sReplicate(inX).SinCos(sv, cv); + float s = sv.GetX(), c = cv.GetX(); + return Mat44(Vec4(1, 0, 0, 0), Vec4(0, c, s, 0), Vec4(0, -s, c, 0), Vec4(0, 0, 0, 1)); +} + +Mat44 Mat44::sRotationY(float inY) +{ + Vec4 sv, cv; + Vec4::sReplicate(inY).SinCos(sv, cv); + float s = sv.GetX(), c = cv.GetX(); + return Mat44(Vec4(c, 0, -s, 0), Vec4(0, 1, 0, 0), Vec4(s, 0, c, 0), Vec4(0, 0, 0, 1)); +} + +Mat44 Mat44::sRotationZ(float inZ) +{ + Vec4 sv, cv; + Vec4::sReplicate(inZ).SinCos(sv, cv); + float s = sv.GetX(), c = cv.GetX(); + return Mat44(Vec4(c, s, 0, 0), Vec4(-s, c, 0, 0), Vec4(0, 0, 1, 0), Vec4(0, 0, 0, 1)); +} + +Mat44 Mat44::sRotation(QuatArg inQuat) +{ + JPH_ASSERT(inQuat.IsNormalized()); + + // See: https://en.wikipedia.org/wiki/Quaternions_and_spatial_rotation section 'Quaternion-derived rotation matrix' +#ifdef JPH_USE_SSE4_1 + __m128 xyzw = inQuat.mValue.mValue; + __m128 two_xyzw = _mm_add_ps(xyzw, xyzw); + __m128 yzxw = _mm_shuffle_ps(xyzw, xyzw, _MM_SHUFFLE(3, 0, 2, 1)); + __m128 two_yzxw = _mm_add_ps(yzxw, yzxw); + __m128 zxyw = _mm_shuffle_ps(xyzw, xyzw, _MM_SHUFFLE(3, 1, 0, 2)); + __m128 two_zxyw = _mm_add_ps(zxyw, zxyw); + __m128 wwww = _mm_shuffle_ps(xyzw, xyzw, _MM_SHUFFLE(3, 3, 3, 3)); + __m128 diagonal = _mm_sub_ps(_mm_sub_ps(_mm_set1_ps(1.0f), _mm_mul_ps(two_yzxw, yzxw)), _mm_mul_ps(two_zxyw, zxyw)); // (1 - 2 y^2 - 2 z^2, 1 - 2 x^2 - 2 z^2, 1 - 2 x^2 - 2 y^2, 1 - 4 w^2) + __m128 plus = _mm_add_ps(_mm_mul_ps(two_xyzw, zxyw), _mm_mul_ps(two_yzxw, wwww)); // 2 * (xz + yw, xy + zw, yz + xw, ww) + __m128 minus = _mm_sub_ps(_mm_mul_ps(two_yzxw, xyzw), _mm_mul_ps(two_zxyw, wwww)); // 2 * (xy - zw, yz - xw, xz - yw, 0) + + // Workaround for compiler changing _mm_sub_ps(_mm_mul_ps(...), ...) into a fused multiply sub instruction, resulting in w not being 0 + // There doesn't appear to be a reliable way to turn this off in Clang + minus = _mm_insert_ps(minus, minus, 0b1000); + + __m128 col0 = _mm_blend_ps(_mm_blend_ps(plus, diagonal, 0b0001), minus, 0b1100); // (1 - 2 y^2 - 2 z^2, 2 xy + 2 zw, 2 xz - 2 yw, 0) + __m128 col1 = _mm_blend_ps(_mm_blend_ps(diagonal, minus, 0b1001), plus, 0b0100); // (2 xy - 2 zw, 1 - 2 x^2 - 2 z^2, 2 yz + 2 xw, 0) + __m128 col2 = _mm_blend_ps(_mm_blend_ps(minus, plus, 0b0001), diagonal, 0b0100); // (2 xz + 2 yw, 2 yz - 2 xw, 1 - 2 x^2 - 2 y^2, 0) + __m128 col3 = _mm_set_ps(1, 0, 0, 0); + + return Mat44(col0, col1, col2, col3); +#else + float x = inQuat.GetX(); + float y = inQuat.GetY(); + float z = inQuat.GetZ(); + float w = inQuat.GetW(); + + float tx = x + x; // Note: Using x + x instead of 2.0f * x to force this function to return the same value as the SSE4.1 version across platforms. + float ty = y + y; + float tz = z + z; + + float xx = tx * x; + float yy = ty * y; + float zz = tz * z; + float xy = tx * y; + float xz = tx * z; + float xw = tx * w; + float yz = ty * z; + float yw = ty * w; + float zw = tz * w; + + return Mat44(Vec4((1.0f - yy) - zz, xy + zw, xz - yw, 0.0f), // Note: Added extra brackets to force this function to return the same value as the SSE4.1 version across platforms. + Vec4(xy - zw, (1.0f - zz) - xx, yz + xw, 0.0f), + Vec4(xz + yw, yz - xw, (1.0f - xx) - yy, 0.0f), + Vec4(0.0f, 0.0f, 0.0f, 1.0f)); +#endif +} + +Mat44 Mat44::sRotation(Vec3Arg inAxis, float inAngle) +{ + return sRotation(Quat::sRotation(inAxis, inAngle)); +} + +Mat44 Mat44::sTranslation(Vec3Arg inV) +{ + return Mat44(Vec4(1, 0, 0, 0), Vec4(0, 1, 0, 0), Vec4(0, 0, 1, 0), Vec4(inV, 1)); +} + +Mat44 Mat44::sRotationTranslation(QuatArg inR, Vec3Arg inT) +{ + Mat44 m = sRotation(inR); + m.SetTranslation(inT); + return m; +} + +Mat44 Mat44::sInverseRotationTranslation(QuatArg inR, Vec3Arg inT) +{ + Mat44 m = sRotation(inR.Conjugated()); + m.SetTranslation(-m.Multiply3x3(inT)); + return m; +} + +Mat44 Mat44::sScale(float inScale) +{ + return Mat44(Vec4(inScale, 0, 0, 0), Vec4(0, inScale, 0, 0), Vec4(0, 0, inScale, 0), Vec4(0, 0, 0, 1)); +} + +Mat44 Mat44::sScale(Vec3Arg inV) +{ + return Mat44(Vec4(inV.GetX(), 0, 0, 0), Vec4(0, inV.GetY(), 0, 0), Vec4(0, 0, inV.GetZ(), 0), Vec4(0, 0, 0, 1)); +} + +Mat44 Mat44::sOuterProduct(Vec3Arg inV1, Vec3Arg inV2) +{ + Vec4 v1(inV1, 0); + return Mat44(v1 * inV2.SplatX(), v1 * inV2.SplatY(), v1 * inV2.SplatZ(), Vec4(0, 0, 0, 1)); +} + +Mat44 Mat44::sCrossProduct(Vec3Arg inV) +{ +#ifdef JPH_USE_SSE4_1 + // Zero out the W component + __m128 zero = _mm_setzero_ps(); + __m128 v = _mm_blend_ps(inV.mValue, zero, 0b1000); + + // Negate + __m128 min_v = _mm_sub_ps(zero, v); + + return Mat44( + _mm_shuffle_ps(v, min_v, _MM_SHUFFLE(3, 1, 2, 3)), // [0, z, -y, 0] + _mm_shuffle_ps(min_v, v, _MM_SHUFFLE(3, 0, 3, 2)), // [-z, 0, x, 0] + _mm_blend_ps(_mm_shuffle_ps(v, v, _MM_SHUFFLE(3, 3, 3, 1)), _mm_shuffle_ps(min_v, min_v, _MM_SHUFFLE(3, 3, 0, 3)), 0b0010), // [y, -x, 0, 0] + Vec4(0, 0, 0, 1)); +#else + float x = inV.GetX(); + float y = inV.GetY(); + float z = inV.GetZ(); + + return Mat44( + Vec4(0, z, -y, 0), + Vec4(-z, 0, x, 0), + Vec4(y, -x, 0, 0), + Vec4(0, 0, 0, 1)); +#endif +} + +Mat44 Mat44::sLookAt(Vec3Arg inPos, Vec3Arg inTarget, Vec3Arg inUp) +{ + Vec3 direction = (inTarget - inPos).NormalizedOr(-Vec3::sAxisZ()); + Vec3 right = direction.Cross(inUp).NormalizedOr(Vec3::sAxisX()); + Vec3 up = right.Cross(direction); + + return Mat44(Vec4(right, 0), Vec4(up, 0), Vec4(-direction, 0), Vec4(inPos, 1)).InversedRotationTranslation(); +} + +Mat44 Mat44::sPerspective(float inFovY, float inAspect, float inNear, float inFar) +{ + float height = 1.0f / Tan(0.5f * inFovY); + float width = height / inAspect; + float range = inFar / (inNear - inFar); + + return Mat44(Vec4(width, 0.0f, 0.0f, 0.0f), Vec4(0.0f, height, 0.0f, 0.0f), Vec4(0.0f, 0.0f, range, -1.0f), Vec4(0.0f, 0.0f, range * inNear, 0.0f)); +} + +bool Mat44::operator == (Mat44Arg inM2) const +{ + return UVec4::sAnd( + UVec4::sAnd(Vec4::sEquals(mCol[0], inM2.mCol[0]), Vec4::sEquals(mCol[1], inM2.mCol[1])), + UVec4::sAnd(Vec4::sEquals(mCol[2], inM2.mCol[2]), Vec4::sEquals(mCol[3], inM2.mCol[3])) + ).TestAllTrue(); +} + +bool Mat44::IsClose(Mat44Arg inM2, float inMaxDistSq) const +{ + for (int i = 0; i < 4; ++i) + if (!mCol[i].IsClose(inM2.mCol[i], inMaxDistSq)) + return false; + return true; +} + +Mat44 Mat44::operator * (Mat44Arg inM) const +{ + Mat44 result; +#if defined(JPH_USE_SSE) + for (int i = 0; i < 4; ++i) + { + __m128 c = inM.mCol[i].mValue; + __m128 t = _mm_mul_ps(mCol[0].mValue, _mm_shuffle_ps(c, c, _MM_SHUFFLE(0, 0, 0, 0))); + t = _mm_add_ps(t, _mm_mul_ps(mCol[1].mValue, _mm_shuffle_ps(c, c, _MM_SHUFFLE(1, 1, 1, 1)))); + t = _mm_add_ps(t, _mm_mul_ps(mCol[2].mValue, _mm_shuffle_ps(c, c, _MM_SHUFFLE(2, 2, 2, 2)))); + t = _mm_add_ps(t, _mm_mul_ps(mCol[3].mValue, _mm_shuffle_ps(c, c, _MM_SHUFFLE(3, 3, 3, 3)))); + result.mCol[i].mValue = t; + } +#elif defined(JPH_USE_NEON) + for (int i = 0; i < 4; ++i) + { + Type c = inM.mCol[i].mValue; + Type t = vmulq_f32(mCol[0].mValue, vdupq_laneq_f32(c, 0)); + t = vmlaq_f32(t, mCol[1].mValue, vdupq_laneq_f32(c, 1)); + t = vmlaq_f32(t, mCol[2].mValue, vdupq_laneq_f32(c, 2)); + t = vmlaq_f32(t, mCol[3].mValue, vdupq_laneq_f32(c, 3)); + result.mCol[i].mValue = t; + } +#else + for (int i = 0; i < 4; ++i) + result.mCol[i] = mCol[0] * inM.mCol[i].mF32[0] + mCol[1] * inM.mCol[i].mF32[1] + mCol[2] * inM.mCol[i].mF32[2] + mCol[3] * inM.mCol[i].mF32[3]; +#endif + return result; +} + +Vec3 Mat44::operator * (Vec3Arg inV) const +{ +#if defined(JPH_USE_SSE) + __m128 t = _mm_mul_ps(mCol[0].mValue, _mm_shuffle_ps(inV.mValue, inV.mValue, _MM_SHUFFLE(0, 0, 0, 0))); + t = _mm_add_ps(t, _mm_mul_ps(mCol[1].mValue, _mm_shuffle_ps(inV.mValue, inV.mValue, _MM_SHUFFLE(1, 1, 1, 1)))); + t = _mm_add_ps(t, _mm_mul_ps(mCol[2].mValue, _mm_shuffle_ps(inV.mValue, inV.mValue, _MM_SHUFFLE(2, 2, 2, 2)))); + t = _mm_add_ps(t, mCol[3].mValue); + return Vec3::sFixW(t); +#elif defined(JPH_USE_NEON) + Type t = vmulq_f32(mCol[0].mValue, vdupq_laneq_f32(inV.mValue, 0)); + t = vmlaq_f32(t, mCol[1].mValue, vdupq_laneq_f32(inV.mValue, 1)); + t = vmlaq_f32(t, mCol[2].mValue, vdupq_laneq_f32(inV.mValue, 2)); + t = vaddq_f32(t, mCol[3].mValue); // Don't combine this with the first mul into a fused multiply add, causes precision issues + return Vec3::sFixW(t); +#else + return Vec3( + mCol[0].mF32[0] * inV.mF32[0] + mCol[1].mF32[0] * inV.mF32[1] + mCol[2].mF32[0] * inV.mF32[2] + mCol[3].mF32[0], + mCol[0].mF32[1] * inV.mF32[0] + mCol[1].mF32[1] * inV.mF32[1] + mCol[2].mF32[1] * inV.mF32[2] + mCol[3].mF32[1], + mCol[0].mF32[2] * inV.mF32[0] + mCol[1].mF32[2] * inV.mF32[1] + mCol[2].mF32[2] * inV.mF32[2] + mCol[3].mF32[2]); +#endif +} + +Vec4 Mat44::operator * (Vec4Arg inV) const +{ +#if defined(JPH_USE_SSE) + __m128 t = _mm_mul_ps(mCol[0].mValue, _mm_shuffle_ps(inV.mValue, inV.mValue, _MM_SHUFFLE(0, 0, 0, 0))); + t = _mm_add_ps(t, _mm_mul_ps(mCol[1].mValue, _mm_shuffle_ps(inV.mValue, inV.mValue, _MM_SHUFFLE(1, 1, 1, 1)))); + t = _mm_add_ps(t, _mm_mul_ps(mCol[2].mValue, _mm_shuffle_ps(inV.mValue, inV.mValue, _MM_SHUFFLE(2, 2, 2, 2)))); + t = _mm_add_ps(t, _mm_mul_ps(mCol[3].mValue, _mm_shuffle_ps(inV.mValue, inV.mValue, _MM_SHUFFLE(3, 3, 3, 3)))); + return t; +#elif defined(JPH_USE_NEON) + Type t = vmulq_f32(mCol[0].mValue, vdupq_laneq_f32(inV.mValue, 0)); + t = vmlaq_f32(t, mCol[1].mValue, vdupq_laneq_f32(inV.mValue, 1)); + t = vmlaq_f32(t, mCol[2].mValue, vdupq_laneq_f32(inV.mValue, 2)); + t = vmlaq_f32(t, mCol[3].mValue, vdupq_laneq_f32(inV.mValue, 3)); + return t; +#else + return Vec4( + mCol[0].mF32[0] * inV.mF32[0] + mCol[1].mF32[0] * inV.mF32[1] + mCol[2].mF32[0] * inV.mF32[2] + mCol[3].mF32[0] * inV.mF32[3], + mCol[0].mF32[1] * inV.mF32[0] + mCol[1].mF32[1] * inV.mF32[1] + mCol[2].mF32[1] * inV.mF32[2] + mCol[3].mF32[1] * inV.mF32[3], + mCol[0].mF32[2] * inV.mF32[0] + mCol[1].mF32[2] * inV.mF32[1] + mCol[2].mF32[2] * inV.mF32[2] + mCol[3].mF32[2] * inV.mF32[3], + mCol[0].mF32[3] * inV.mF32[0] + mCol[1].mF32[3] * inV.mF32[1] + mCol[2].mF32[3] * inV.mF32[2] + mCol[3].mF32[3] * inV.mF32[3]); +#endif +} + +Vec3 Mat44::Multiply3x3(Vec3Arg inV) const +{ +#if defined(JPH_USE_SSE) + __m128 t = _mm_mul_ps(mCol[0].mValue, _mm_shuffle_ps(inV.mValue, inV.mValue, _MM_SHUFFLE(0, 0, 0, 0))); + t = _mm_add_ps(t, _mm_mul_ps(mCol[1].mValue, _mm_shuffle_ps(inV.mValue, inV.mValue, _MM_SHUFFLE(1, 1, 1, 1)))); + t = _mm_add_ps(t, _mm_mul_ps(mCol[2].mValue, _mm_shuffle_ps(inV.mValue, inV.mValue, _MM_SHUFFLE(2, 2, 2, 2)))); + return Vec3::sFixW(t); +#elif defined(JPH_USE_NEON) + Type t = vmulq_f32(mCol[0].mValue, vdupq_laneq_f32(inV.mValue, 0)); + t = vmlaq_f32(t, mCol[1].mValue, vdupq_laneq_f32(inV.mValue, 1)); + t = vmlaq_f32(t, mCol[2].mValue, vdupq_laneq_f32(inV.mValue, 2)); + return Vec3::sFixW(t); +#else + return Vec3( + mCol[0].mF32[0] * inV.mF32[0] + mCol[1].mF32[0] * inV.mF32[1] + mCol[2].mF32[0] * inV.mF32[2], + mCol[0].mF32[1] * inV.mF32[0] + mCol[1].mF32[1] * inV.mF32[1] + mCol[2].mF32[1] * inV.mF32[2], + mCol[0].mF32[2] * inV.mF32[0] + mCol[1].mF32[2] * inV.mF32[1] + mCol[2].mF32[2] * inV.mF32[2]); +#endif +} + +Vec3 Mat44::Multiply3x3Transposed(Vec3Arg inV) const +{ +#if defined(JPH_USE_SSE4_1) + __m128 x = _mm_dp_ps(mCol[0].mValue, inV.mValue, 0x7f); + __m128 y = _mm_dp_ps(mCol[1].mValue, inV.mValue, 0x7f); + __m128 xy = _mm_blend_ps(x, y, 0b0010); + __m128 z = _mm_dp_ps(mCol[2].mValue, inV.mValue, 0x7f); + __m128 xyzz = _mm_blend_ps(xy, z, 0b1100); + return xyzz; +#else + return Transposed3x3().Multiply3x3(inV); +#endif +} + +Mat44 Mat44::Multiply3x3(Mat44Arg inM) const +{ + JPH_ASSERT(mCol[0][3] == 0.0f); + JPH_ASSERT(mCol[1][3] == 0.0f); + JPH_ASSERT(mCol[2][3] == 0.0f); + + Mat44 result; +#if defined(JPH_USE_SSE) + for (int i = 0; i < 3; ++i) + { + __m128 c = inM.mCol[i].mValue; + __m128 t = _mm_mul_ps(mCol[0].mValue, _mm_shuffle_ps(c, c, _MM_SHUFFLE(0, 0, 0, 0))); + t = _mm_add_ps(t, _mm_mul_ps(mCol[1].mValue, _mm_shuffle_ps(c, c, _MM_SHUFFLE(1, 1, 1, 1)))); + t = _mm_add_ps(t, _mm_mul_ps(mCol[2].mValue, _mm_shuffle_ps(c, c, _MM_SHUFFLE(2, 2, 2, 2)))); + result.mCol[i].mValue = t; + } +#elif defined(JPH_USE_NEON) + for (int i = 0; i < 3; ++i) + { + Type c = inM.mCol[i].mValue; + Type t = vmulq_f32(mCol[0].mValue, vdupq_laneq_f32(c, 0)); + t = vmlaq_f32(t, mCol[1].mValue, vdupq_laneq_f32(c, 1)); + t = vmlaq_f32(t, mCol[2].mValue, vdupq_laneq_f32(c, 2)); + result.mCol[i].mValue = t; + } +#else + for (int i = 0; i < 3; ++i) + result.mCol[i] = mCol[0] * inM.mCol[i].mF32[0] + mCol[1] * inM.mCol[i].mF32[1] + mCol[2] * inM.mCol[i].mF32[2]; +#endif + result.mCol[3] = Vec4(0, 0, 0, 1); + return result; +} + +Mat44 Mat44::Multiply3x3LeftTransposed(Mat44Arg inM) const +{ + // Transpose left hand side + Mat44 trans = Transposed3x3(); + + // Do 3x3 matrix multiply + Mat44 result; + result.mCol[0] = trans.mCol[0] * inM.mCol[0].SplatX() + trans.mCol[1] * inM.mCol[0].SplatY() + trans.mCol[2] * inM.mCol[0].SplatZ(); + result.mCol[1] = trans.mCol[0] * inM.mCol[1].SplatX() + trans.mCol[1] * inM.mCol[1].SplatY() + trans.mCol[2] * inM.mCol[1].SplatZ(); + result.mCol[2] = trans.mCol[0] * inM.mCol[2].SplatX() + trans.mCol[1] * inM.mCol[2].SplatY() + trans.mCol[2] * inM.mCol[2].SplatZ(); + result.mCol[3] = Vec4(0, 0, 0, 1); + return result; +} + +Mat44 Mat44::Multiply3x3RightTransposed(Mat44Arg inM) const +{ + JPH_ASSERT(mCol[0][3] == 0.0f); + JPH_ASSERT(mCol[1][3] == 0.0f); + JPH_ASSERT(mCol[2][3] == 0.0f); + + Mat44 result; + result.mCol[0] = mCol[0] * inM.mCol[0].SplatX() + mCol[1] * inM.mCol[1].SplatX() + mCol[2] * inM.mCol[2].SplatX(); + result.mCol[1] = mCol[0] * inM.mCol[0].SplatY() + mCol[1] * inM.mCol[1].SplatY() + mCol[2] * inM.mCol[2].SplatY(); + result.mCol[2] = mCol[0] * inM.mCol[0].SplatZ() + mCol[1] * inM.mCol[1].SplatZ() + mCol[2] * inM.mCol[2].SplatZ(); + result.mCol[3] = Vec4(0, 0, 0, 1); + return result; +} + +Mat44 Mat44::operator * (float inV) const +{ + Vec4 multiplier = Vec4::sReplicate(inV); + + Mat44 result; + for (int c = 0; c < 4; ++c) + result.mCol[c] = mCol[c] * multiplier; + return result; +} + +Mat44 &Mat44::operator *= (float inV) +{ + for (int c = 0; c < 4; ++c) + mCol[c] *= inV; + + return *this; +} + +Mat44 Mat44::operator + (Mat44Arg inM) const +{ + Mat44 result; + for (int i = 0; i < 4; ++i) + result.mCol[i] = mCol[i] + inM.mCol[i]; + return result; +} + +Mat44 Mat44::operator - () const +{ + Mat44 result; + for (int i = 0; i < 4; ++i) + result.mCol[i] = -mCol[i]; + return result; +} + +Mat44 Mat44::operator - (Mat44Arg inM) const +{ + Mat44 result; + for (int i = 0; i < 4; ++i) + result.mCol[i] = mCol[i] - inM.mCol[i]; + return result; +} + +Mat44 &Mat44::operator += (Mat44Arg inM) +{ + for (int c = 0; c < 4; ++c) + mCol[c] += inM.mCol[c]; + + return *this; +} + +void Mat44::StoreFloat4x4(Float4 *outV) const +{ + for (int c = 0; c < 4; ++c) + mCol[c].StoreFloat4(outV + c); +} + +Mat44 Mat44::Transposed() const +{ +#if defined(JPH_USE_SSE) + __m128 tmp1 = _mm_shuffle_ps(mCol[0].mValue, mCol[1].mValue, _MM_SHUFFLE(1, 0, 1, 0)); + __m128 tmp3 = _mm_shuffle_ps(mCol[0].mValue, mCol[1].mValue, _MM_SHUFFLE(3, 2, 3, 2)); + __m128 tmp2 = _mm_shuffle_ps(mCol[2].mValue, mCol[3].mValue, _MM_SHUFFLE(1, 0, 1, 0)); + __m128 tmp4 = _mm_shuffle_ps(mCol[2].mValue, mCol[3].mValue, _MM_SHUFFLE(3, 2, 3, 2)); + + Mat44 result; + result.mCol[0].mValue = _mm_shuffle_ps(tmp1, tmp2, _MM_SHUFFLE(2, 0, 2, 0)); + result.mCol[1].mValue = _mm_shuffle_ps(tmp1, tmp2, _MM_SHUFFLE(3, 1, 3, 1)); + result.mCol[2].mValue = _mm_shuffle_ps(tmp3, tmp4, _MM_SHUFFLE(2, 0, 2, 0)); + result.mCol[3].mValue = _mm_shuffle_ps(tmp3, tmp4, _MM_SHUFFLE(3, 1, 3, 1)); + return result; +#elif defined(JPH_USE_NEON) + float32x4x2_t tmp1 = vzipq_f32(mCol[0].mValue, mCol[2].mValue); + float32x4x2_t tmp2 = vzipq_f32(mCol[1].mValue, mCol[3].mValue); + float32x4x2_t tmp3 = vzipq_f32(tmp1.val[0], tmp2.val[0]); + float32x4x2_t tmp4 = vzipq_f32(tmp1.val[1], tmp2.val[1]); + + Mat44 result; + result.mCol[0].mValue = tmp3.val[0]; + result.mCol[1].mValue = tmp3.val[1]; + result.mCol[2].mValue = tmp4.val[0]; + result.mCol[3].mValue = tmp4.val[1]; + return result; +#else + Mat44 result; + for (int c = 0; c < 4; ++c) + for (int r = 0; r < 4; ++r) + result.mCol[r].mF32[c] = mCol[c].mF32[r]; + return result; +#endif +} + +Mat44 Mat44::Transposed3x3() const +{ +#if defined(JPH_USE_SSE) + __m128 zero = _mm_setzero_ps(); + __m128 tmp1 = _mm_shuffle_ps(mCol[0].mValue, mCol[1].mValue, _MM_SHUFFLE(1, 0, 1, 0)); + __m128 tmp3 = _mm_shuffle_ps(mCol[0].mValue, mCol[1].mValue, _MM_SHUFFLE(3, 2, 3, 2)); + __m128 tmp2 = _mm_shuffle_ps(mCol[2].mValue, zero, _MM_SHUFFLE(1, 0, 1, 0)); + __m128 tmp4 = _mm_shuffle_ps(mCol[2].mValue, zero, _MM_SHUFFLE(3, 2, 3, 2)); + + Mat44 result; + result.mCol[0].mValue = _mm_shuffle_ps(tmp1, tmp2, _MM_SHUFFLE(2, 0, 2, 0)); + result.mCol[1].mValue = _mm_shuffle_ps(tmp1, tmp2, _MM_SHUFFLE(3, 1, 3, 1)); + result.mCol[2].mValue = _mm_shuffle_ps(tmp3, tmp4, _MM_SHUFFLE(2, 0, 2, 0)); +#elif defined(JPH_USE_NEON) + float32x4x2_t tmp1 = vzipq_f32(mCol[0].mValue, mCol[2].mValue); + float32x4x2_t tmp2 = vzipq_f32(mCol[1].mValue, vdupq_n_f32(0)); + float32x4x2_t tmp3 = vzipq_f32(tmp1.val[0], tmp2.val[0]); + float32x4x2_t tmp4 = vzipq_f32(tmp1.val[1], tmp2.val[1]); + + Mat44 result; + result.mCol[0].mValue = tmp3.val[0]; + result.mCol[1].mValue = tmp3.val[1]; + result.mCol[2].mValue = tmp4.val[0]; +#else + Mat44 result; + for (int c = 0; c < 3; ++c) + { + for (int r = 0; r < 3; ++r) + result.mCol[c].mF32[r] = mCol[r].mF32[c]; + result.mCol[c].mF32[3] = 0; + } +#endif + result.mCol[3] = Vec4(0, 0, 0, 1); + return result; +} + +Mat44 Mat44::Inversed() const +{ +#if defined(JPH_USE_SSE) + // Algorithm from: http://download.intel.com/design/PentiumIII/sml/24504301.pdf + // Streaming SIMD Extensions - Inverse of 4x4 Matrix + // Adapted to load data using _mm_shuffle_ps instead of loading from memory + // Replaced _mm_rcp_ps with _mm_div_ps for better accuracy + + __m128 tmp1 = _mm_shuffle_ps(mCol[0].mValue, mCol[1].mValue, _MM_SHUFFLE(1, 0, 1, 0)); + __m128 row1 = _mm_shuffle_ps(mCol[2].mValue, mCol[3].mValue, _MM_SHUFFLE(1, 0, 1, 0)); + __m128 row0 = _mm_shuffle_ps(tmp1, row1, _MM_SHUFFLE(2, 0, 2, 0)); + row1 = _mm_shuffle_ps(row1, tmp1, _MM_SHUFFLE(3, 1, 3, 1)); + tmp1 = _mm_shuffle_ps(mCol[0].mValue, mCol[1].mValue, _MM_SHUFFLE(3, 2, 3, 2)); + __m128 row3 = _mm_shuffle_ps(mCol[2].mValue, mCol[3].mValue, _MM_SHUFFLE(3, 2, 3, 2)); + __m128 row2 = _mm_shuffle_ps(tmp1, row3, _MM_SHUFFLE(2, 0, 2, 0)); + row3 = _mm_shuffle_ps(row3, tmp1, _MM_SHUFFLE(3, 1, 3, 1)); + + tmp1 = _mm_mul_ps(row2, row3); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, _MM_SHUFFLE(2, 3, 0, 1)); + __m128 minor0 = _mm_mul_ps(row1, tmp1); + __m128 minor1 = _mm_mul_ps(row0, tmp1); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, _MM_SHUFFLE(1, 0, 3, 2)); + minor0 = _mm_sub_ps(_mm_mul_ps(row1, tmp1), minor0); + minor1 = _mm_sub_ps(_mm_mul_ps(row0, tmp1), minor1); + minor1 = _mm_shuffle_ps(minor1, minor1, _MM_SHUFFLE(1, 0, 3, 2)); + + tmp1 = _mm_mul_ps(row1, row2); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, _MM_SHUFFLE(2, 3, 0, 1)); + minor0 = _mm_add_ps(_mm_mul_ps(row3, tmp1), minor0); + __m128 minor3 = _mm_mul_ps(row0, tmp1); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, _MM_SHUFFLE(1, 0, 3, 2)); + minor0 = _mm_sub_ps(minor0, _mm_mul_ps(row3, tmp1)); + minor3 = _mm_sub_ps(_mm_mul_ps(row0, tmp1), minor3); + minor3 = _mm_shuffle_ps(minor3, minor3, _MM_SHUFFLE(1, 0, 3, 2)); + + tmp1 = _mm_mul_ps(_mm_shuffle_ps(row1, row1, _MM_SHUFFLE(1, 0, 3, 2)), row3); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, _MM_SHUFFLE(2, 3, 0, 1)); + row2 = _mm_shuffle_ps(row2, row2, _MM_SHUFFLE(1, 0, 3, 2)); + minor0 = _mm_add_ps(_mm_mul_ps(row2, tmp1), minor0); + __m128 minor2 = _mm_mul_ps(row0, tmp1); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, _MM_SHUFFLE(1, 0, 3, 2)); + minor0 = _mm_sub_ps(minor0, _mm_mul_ps(row2, tmp1)); + minor2 = _mm_sub_ps(_mm_mul_ps(row0, tmp1), minor2); + minor2 = _mm_shuffle_ps(minor2, minor2, _MM_SHUFFLE(1, 0, 3, 2)); + + tmp1 = _mm_mul_ps(row0, row1); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, _MM_SHUFFLE(2, 3, 0, 1)); + minor2 = _mm_add_ps(_mm_mul_ps(row3, tmp1), minor2); + minor3 = _mm_sub_ps(_mm_mul_ps(row2, tmp1), minor3); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, _MM_SHUFFLE(1, 0, 3, 2)); + minor2 = _mm_sub_ps(_mm_mul_ps(row3, tmp1), minor2); + minor3 = _mm_sub_ps(minor3, _mm_mul_ps(row2, tmp1)); + + tmp1 = _mm_mul_ps(row0, row3); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, _MM_SHUFFLE(2, 3, 0, 1)); + minor1 = _mm_sub_ps(minor1, _mm_mul_ps(row2, tmp1)); + minor2 = _mm_add_ps(_mm_mul_ps(row1, tmp1), minor2); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, _MM_SHUFFLE(1, 0, 3, 2)); + minor1 = _mm_add_ps(_mm_mul_ps(row2, tmp1), minor1); + minor2 = _mm_sub_ps(minor2, _mm_mul_ps(row1, tmp1)); + + tmp1 = _mm_mul_ps(row0, row2); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, _MM_SHUFFLE(2, 3, 0, 1)); + minor1 = _mm_add_ps(_mm_mul_ps(row3, tmp1), minor1); + minor3 = _mm_sub_ps(minor3, _mm_mul_ps(row1, tmp1)); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, _MM_SHUFFLE(1, 0, 3, 2)); + minor1 = _mm_sub_ps(minor1, _mm_mul_ps(row3, tmp1)); + minor3 = _mm_add_ps(_mm_mul_ps(row1, tmp1), minor3); + + __m128 det = _mm_mul_ps(row0, minor0); + det = _mm_add_ps(_mm_shuffle_ps(det, det, _MM_SHUFFLE(2, 3, 0, 1)), det); // Original code did (x + z) + (y + w), changed to (x + y) + (z + w) to match the ARM code below and make the result cross platform deterministic + det = _mm_add_ss(_mm_shuffle_ps(det, det, _MM_SHUFFLE(1, 0, 3, 2)), det); + det = _mm_div_ss(_mm_set_ss(1.0f), det); + det = _mm_shuffle_ps(det, det, _MM_SHUFFLE(0, 0, 0, 0)); + + Mat44 result; + result.mCol[0].mValue = _mm_mul_ps(det, minor0); + result.mCol[1].mValue = _mm_mul_ps(det, minor1); + result.mCol[2].mValue = _mm_mul_ps(det, minor2); + result.mCol[3].mValue = _mm_mul_ps(det, minor3); + return result; +#elif defined(JPH_USE_NEON) + // Adapted from the SSE version, there's surprising few articles about efficient ways of calculating an inverse for ARM on the internet + Type tmp1 = JPH_NEON_SHUFFLE_F32x4(mCol[0].mValue, mCol[1].mValue, 0, 1, 4, 5); + Type row1 = JPH_NEON_SHUFFLE_F32x4(mCol[2].mValue, mCol[3].mValue, 0, 1, 4, 5); + Type row0 = JPH_NEON_SHUFFLE_F32x4(tmp1, row1, 0, 2, 4, 6); + row1 = JPH_NEON_SHUFFLE_F32x4(row1, tmp1, 1, 3, 5, 7); + tmp1 = JPH_NEON_SHUFFLE_F32x4(mCol[0].mValue, mCol[1].mValue, 2, 3, 6, 7); + Type row3 = JPH_NEON_SHUFFLE_F32x4(mCol[2].mValue, mCol[3].mValue, 2, 3, 6, 7); + Type row2 = JPH_NEON_SHUFFLE_F32x4(tmp1, row3, 0, 2, 4, 6); + row3 = JPH_NEON_SHUFFLE_F32x4(row3, tmp1, 1, 3, 5, 7); + + tmp1 = vmulq_f32(row2, row3); + tmp1 = JPH_NEON_SHUFFLE_F32x4(tmp1, tmp1, 1, 0, 3, 2); + Type minor0 = vmulq_f32(row1, tmp1); + Type minor1 = vmulq_f32(row0, tmp1); + tmp1 = JPH_NEON_SHUFFLE_F32x4(tmp1, tmp1, 2, 3, 0, 1); + minor0 = vsubq_f32(vmulq_f32(row1, tmp1), minor0); + minor1 = vsubq_f32(vmulq_f32(row0, tmp1), minor1); + minor1 = JPH_NEON_SHUFFLE_F32x4(minor1, minor1, 2, 3, 0, 1); + + tmp1 = vmulq_f32(row1, row2); + tmp1 = JPH_NEON_SHUFFLE_F32x4(tmp1, tmp1, 1, 0, 3, 2); + minor0 = vaddq_f32(vmulq_f32(row3, tmp1), minor0); + Type minor3 = vmulq_f32(row0, tmp1); + tmp1 = JPH_NEON_SHUFFLE_F32x4(tmp1, tmp1, 2, 3, 0, 1); + minor0 = vsubq_f32(minor0, vmulq_f32(row3, tmp1)); + minor3 = vsubq_f32(vmulq_f32(row0, tmp1), minor3); + minor3 = JPH_NEON_SHUFFLE_F32x4(minor3, minor3, 2, 3, 0, 1); + + tmp1 = JPH_NEON_SHUFFLE_F32x4(row1, row1, 2, 3, 0, 1); + tmp1 = vmulq_f32(tmp1, row3); + tmp1 = JPH_NEON_SHUFFLE_F32x4(tmp1, tmp1, 1, 0, 3, 2); + row2 = JPH_NEON_SHUFFLE_F32x4(row2, row2, 2, 3, 0, 1); + minor0 = vaddq_f32(vmulq_f32(row2, tmp1), minor0); + Type minor2 = vmulq_f32(row0, tmp1); + tmp1 = JPH_NEON_SHUFFLE_F32x4(tmp1, tmp1, 2, 3, 0, 1); + minor0 = vsubq_f32(minor0, vmulq_f32(row2, tmp1)); + minor2 = vsubq_f32(vmulq_f32(row0, tmp1), minor2); + minor2 = JPH_NEON_SHUFFLE_F32x4(minor2, minor2, 2, 3, 0, 1); + + tmp1 = vmulq_f32(row0, row1); + tmp1 = JPH_NEON_SHUFFLE_F32x4(tmp1, tmp1, 1, 0, 3, 2); + minor2 = vaddq_f32(vmulq_f32(row3, tmp1), minor2); + minor3 = vsubq_f32(vmulq_f32(row2, tmp1), minor3); + tmp1 = JPH_NEON_SHUFFLE_F32x4(tmp1, tmp1, 2, 3, 0, 1); + minor2 = vsubq_f32(vmulq_f32(row3, tmp1), minor2); + minor3 = vsubq_f32(minor3, vmulq_f32(row2, tmp1)); + + tmp1 = vmulq_f32(row0, row3); + tmp1 = JPH_NEON_SHUFFLE_F32x4(tmp1, tmp1, 1, 0, 3, 2); + minor1 = vsubq_f32(minor1, vmulq_f32(row2, tmp1)); + minor2 = vaddq_f32(vmulq_f32(row1, tmp1), minor2); + tmp1 = JPH_NEON_SHUFFLE_F32x4(tmp1, tmp1, 2, 3, 0, 1); + minor1 = vaddq_f32(vmulq_f32(row2, tmp1), minor1); + minor2 = vsubq_f32(minor2, vmulq_f32(row1, tmp1)); + + tmp1 = vmulq_f32(row0, row2); + tmp1 = JPH_NEON_SHUFFLE_F32x4(tmp1, tmp1, 1, 0, 3, 2); + minor1 = vaddq_f32(vmulq_f32(row3, tmp1), minor1); + minor3 = vsubq_f32(minor3, vmulq_f32(row1, tmp1)); + tmp1 = JPH_NEON_SHUFFLE_F32x4(tmp1, tmp1, 2, 3, 0, 1); + minor1 = vsubq_f32(minor1, vmulq_f32(row3, tmp1)); + minor3 = vaddq_f32(vmulq_f32(row1, tmp1), minor3); + + Type det = vmulq_f32(row0, minor0); + det = vdupq_n_f32(vaddvq_f32(det)); + det = vdivq_f32(vdupq_n_f32(1.0f), det); + + Mat44 result; + result.mCol[0].mValue = vmulq_f32(det, minor0); + result.mCol[1].mValue = vmulq_f32(det, minor1); + result.mCol[2].mValue = vmulq_f32(det, minor2); + result.mCol[3].mValue = vmulq_f32(det, minor3); + return result; +#else + float m00 = JPH_EL(0, 0), m10 = JPH_EL(1, 0), m20 = JPH_EL(2, 0), m30 = JPH_EL(3, 0); + float m01 = JPH_EL(0, 1), m11 = JPH_EL(1, 1), m21 = JPH_EL(2, 1), m31 = JPH_EL(3, 1); + float m02 = JPH_EL(0, 2), m12 = JPH_EL(1, 2), m22 = JPH_EL(2, 2), m32 = JPH_EL(3, 2); + float m03 = JPH_EL(0, 3), m13 = JPH_EL(1, 3), m23 = JPH_EL(2, 3), m33 = JPH_EL(3, 3); + + float m10211120 = m10 * m21 - m11 * m20; + float m10221220 = m10 * m22 - m12 * m20; + float m10231320 = m10 * m23 - m13 * m20; + float m10311130 = m10 * m31 - m11 * m30; + float m10321230 = m10 * m32 - m12 * m30; + float m10331330 = m10 * m33 - m13 * m30; + float m11221221 = m11 * m22 - m12 * m21; + float m11231321 = m11 * m23 - m13 * m21; + float m11321231 = m11 * m32 - m12 * m31; + float m11331331 = m11 * m33 - m13 * m31; + float m12231322 = m12 * m23 - m13 * m22; + float m12331332 = m12 * m33 - m13 * m32; + float m20312130 = m20 * m31 - m21 * m30; + float m20322230 = m20 * m32 - m22 * m30; + float m20332330 = m20 * m33 - m23 * m30; + float m21322231 = m21 * m32 - m22 * m31; + float m21332331 = m21 * m33 - m23 * m31; + float m22332332 = m22 * m33 - m23 * m32; + + Vec4 col0(m11 * m22332332 - m12 * m21332331 + m13 * m21322231, -m10 * m22332332 + m12 * m20332330 - m13 * m20322230, m10 * m21332331 - m11 * m20332330 + m13 * m20312130, -m10 * m21322231 + m11 * m20322230 - m12 * m20312130); + Vec4 col1(-m01 * m22332332 + m02 * m21332331 - m03 * m21322231, m00 * m22332332 - m02 * m20332330 + m03 * m20322230, -m00 * m21332331 + m01 * m20332330 - m03 * m20312130, m00 * m21322231 - m01 * m20322230 + m02 * m20312130); + Vec4 col2(m01 * m12331332 - m02 * m11331331 + m03 * m11321231, -m00 * m12331332 + m02 * m10331330 - m03 * m10321230, m00 * m11331331 - m01 * m10331330 + m03 * m10311130, -m00 * m11321231 + m01 * m10321230 - m02 * m10311130); + Vec4 col3(-m01 * m12231322 + m02 * m11231321 - m03 * m11221221, m00 * m12231322 - m02 * m10231320 + m03 * m10221220, -m00 * m11231321 + m01 * m10231320 - m03 * m10211120, m00 * m11221221 - m01 * m10221220 + m02 * m10211120); + + float det = m00 * col0.mF32[0] + m01 * col0.mF32[1] + m02 * col0.mF32[2] + m03 * col0.mF32[3]; + + return Mat44(col0 / det, col1 / det, col2 / det, col3 / det); +#endif +} + +Mat44 Mat44::InversedRotationTranslation() const +{ + Mat44 m = Transposed3x3(); + m.SetTranslation(-m.Multiply3x3(GetTranslation())); + return m; +} + +float Mat44::GetDeterminant3x3() const +{ + return GetAxisX().Dot(GetAxisY().Cross(GetAxisZ())); +} + +Mat44 Mat44::Adjointed3x3() const +{ + return Mat44( + Vec4(JPH_EL(1, 1), JPH_EL(1, 2), JPH_EL(1, 0), 0) * Vec4(JPH_EL(2, 2), JPH_EL(2, 0), JPH_EL(2, 1), 0) + - Vec4(JPH_EL(1, 2), JPH_EL(1, 0), JPH_EL(1, 1), 0) * Vec4(JPH_EL(2, 1), JPH_EL(2, 2), JPH_EL(2, 0), 0), + Vec4(JPH_EL(0, 2), JPH_EL(0, 0), JPH_EL(0, 1), 0) * Vec4(JPH_EL(2, 1), JPH_EL(2, 2), JPH_EL(2, 0), 0) + - Vec4(JPH_EL(0, 1), JPH_EL(0, 2), JPH_EL(0, 0), 0) * Vec4(JPH_EL(2, 2), JPH_EL(2, 0), JPH_EL(2, 1), 0), + Vec4(JPH_EL(0, 1), JPH_EL(0, 2), JPH_EL(0, 0), 0) * Vec4(JPH_EL(1, 2), JPH_EL(1, 0), JPH_EL(1, 1), 0) + - Vec4(JPH_EL(0, 2), JPH_EL(0, 0), JPH_EL(0, 1), 0) * Vec4(JPH_EL(1, 1), JPH_EL(1, 2), JPH_EL(1, 0), 0), + Vec4(0, 0, 0, 1)); +} + +Mat44 Mat44::Inversed3x3() const +{ + float det = GetDeterminant3x3(); + + return Mat44( + (Vec4(JPH_EL(1, 1), JPH_EL(1, 2), JPH_EL(1, 0), 0) * Vec4(JPH_EL(2, 2), JPH_EL(2, 0), JPH_EL(2, 1), 0) + - Vec4(JPH_EL(1, 2), JPH_EL(1, 0), JPH_EL(1, 1), 0) * Vec4(JPH_EL(2, 1), JPH_EL(2, 2), JPH_EL(2, 0), 0)) / det, + (Vec4(JPH_EL(0, 2), JPH_EL(0, 0), JPH_EL(0, 1), 0) * Vec4(JPH_EL(2, 1), JPH_EL(2, 2), JPH_EL(2, 0), 0) + - Vec4(JPH_EL(0, 1), JPH_EL(0, 2), JPH_EL(0, 0), 0) * Vec4(JPH_EL(2, 2), JPH_EL(2, 0), JPH_EL(2, 1), 0)) / det, + (Vec4(JPH_EL(0, 1), JPH_EL(0, 2), JPH_EL(0, 0), 0) * Vec4(JPH_EL(1, 2), JPH_EL(1, 0), JPH_EL(1, 1), 0) + - Vec4(JPH_EL(0, 2), JPH_EL(0, 0), JPH_EL(0, 1), 0) * Vec4(JPH_EL(1, 1), JPH_EL(1, 2), JPH_EL(1, 0), 0)) / det, + Vec4(0, 0, 0, 1)); +} + +bool Mat44::SetInversed3x3(Mat44Arg inM) +{ + float det = inM.GetDeterminant3x3(); + + // If the determinant is zero the matrix is singular and we return false + if (det == 0.0f) + return false; + + // Finish calculating the inverse + *this = inM.Adjointed3x3(); + mCol[0] /= det; + mCol[1] /= det; + mCol[2] /= det; + return true; +} + +Quat Mat44::GetQuaternion() const +{ + float tr = mCol[0].mF32[0] + mCol[1].mF32[1] + mCol[2].mF32[2]; + + if (tr >= 0.0f) + { + float s = sqrt(tr + 1.0f); + float is = 0.5f / s; + return Quat( + (mCol[1].mF32[2] - mCol[2].mF32[1]) * is, + (mCol[2].mF32[0] - mCol[0].mF32[2]) * is, + (mCol[0].mF32[1] - mCol[1].mF32[0]) * is, + 0.5f * s); + } + else + { + int i = 0; + if (mCol[1].mF32[1] > mCol[0].mF32[0]) i = 1; + if (mCol[2].mF32[2] > mCol[i].mF32[i]) i = 2; + + if (i == 0) + { + float s = sqrt(mCol[0].mF32[0] - (mCol[1].mF32[1] + mCol[2].mF32[2]) + 1); + float is = 0.5f / s; + return Quat( + 0.5f * s, + (mCol[1].mF32[0] + mCol[0].mF32[1]) * is, + (mCol[0].mF32[2] + mCol[2].mF32[0]) * is, + (mCol[1].mF32[2] - mCol[2].mF32[1]) * is); + } + else if (i == 1) + { + float s = sqrt(mCol[1].mF32[1] - (mCol[2].mF32[2] + mCol[0].mF32[0]) + 1); + float is = 0.5f / s; + return Quat( + (mCol[1].mF32[0] + mCol[0].mF32[1]) * is, + 0.5f * s, + (mCol[2].mF32[1] + mCol[1].mF32[2]) * is, + (mCol[2].mF32[0] - mCol[0].mF32[2]) * is); + } + else + { + JPH_ASSERT(i == 2); + + float s = sqrt(mCol[2].mF32[2] - (mCol[0].mF32[0] + mCol[1].mF32[1]) + 1); + float is = 0.5f / s; + return Quat( + (mCol[0].mF32[2] + mCol[2].mF32[0]) * is, + (mCol[2].mF32[1] + mCol[1].mF32[2]) * is, + 0.5f * s, + (mCol[0].mF32[1] - mCol[1].mF32[0]) * is); + } + } +} + +Mat44 Mat44::sQuatLeftMultiply(QuatArg inQ) +{ + return Mat44( + Vec4(1, 1, -1, -1) * inQ.mValue.Swizzle(), + Vec4(-1, 1, 1, -1) * inQ.mValue.Swizzle(), + Vec4(1, -1, 1, -1) * inQ.mValue.Swizzle(), + inQ.mValue); +} + +Mat44 Mat44::sQuatRightMultiply(QuatArg inQ) +{ + return Mat44( + Vec4(1, -1, 1, -1) * inQ.mValue.Swizzle(), + Vec4(1, 1, -1, -1) * inQ.mValue.Swizzle(), + Vec4(-1, 1, 1, -1) * inQ.mValue.Swizzle(), + inQ.mValue); +} + +Mat44 Mat44::GetRotation() const +{ + JPH_ASSERT(mCol[0][3] == 0.0f); + JPH_ASSERT(mCol[1][3] == 0.0f); + JPH_ASSERT(mCol[2][3] == 0.0f); + + return Mat44(mCol[0], mCol[1], mCol[2], Vec4(0, 0, 0, 1)); +} + +Mat44 Mat44::GetRotationSafe() const +{ +#if defined(JPH_USE_AVX512) + return Mat44(_mm_maskz_mov_ps(0b0111, mCol[0].mValue), + _mm_maskz_mov_ps(0b0111, mCol[1].mValue), + _mm_maskz_mov_ps(0b0111, mCol[2].mValue), + Vec4(0, 0, 0, 1)); +#elif defined(JPH_USE_SSE4_1) + __m128 zero = _mm_setzero_ps(); + return Mat44(_mm_blend_ps(mCol[0].mValue, zero, 8), + _mm_blend_ps(mCol[1].mValue, zero, 8), + _mm_blend_ps(mCol[2].mValue, zero, 8), + Vec4(0, 0, 0, 1)); +#elif defined(JPH_USE_NEON) + return Mat44(vsetq_lane_f32(0, mCol[0].mValue, 3), + vsetq_lane_f32(0, mCol[1].mValue, 3), + vsetq_lane_f32(0, mCol[2].mValue, 3), + Vec4(0, 0, 0, 1)); +#else + return Mat44(Vec4(mCol[0].mF32[0], mCol[0].mF32[1], mCol[0].mF32[2], 0), + Vec4(mCol[1].mF32[0], mCol[1].mF32[1], mCol[1].mF32[2], 0), + Vec4(mCol[2].mF32[0], mCol[2].mF32[1], mCol[2].mF32[2], 0), + Vec4(0, 0, 0, 1)); +#endif +} + +void Mat44::SetRotation(Mat44Arg inRotation) +{ + mCol[0] = inRotation.mCol[0]; + mCol[1] = inRotation.mCol[1]; + mCol[2] = inRotation.mCol[2]; +} + +Mat44 Mat44::PreTranslated(Vec3Arg inTranslation) const +{ + return Mat44(mCol[0], mCol[1], mCol[2], Vec4(GetTranslation() + Multiply3x3(inTranslation), 1)); +} + +Mat44 Mat44::PostTranslated(Vec3Arg inTranslation) const +{ + return Mat44(mCol[0], mCol[1], mCol[2], Vec4(GetTranslation() + inTranslation, 1)); +} + +Mat44 Mat44::PreScaled(Vec3Arg inScale) const +{ + return Mat44(inScale.GetX() * mCol[0], inScale.GetY() * mCol[1], inScale.GetZ() * mCol[2], mCol[3]); +} + +Mat44 Mat44::PostScaled(Vec3Arg inScale) const +{ + Vec4 scale(inScale, 1); + return Mat44(scale * mCol[0], scale * mCol[1], scale * mCol[2], scale * mCol[3]); +} + +Mat44 Mat44::Decompose(Vec3 &outScale) const +{ + // Start the modified Gram-Schmidt algorithm + // X axis will just be normalized + Vec3 x = GetAxisX(); + + // Make Y axis perpendicular to X + Vec3 y = GetAxisY(); + float x_dot_x = x.LengthSq(); + y -= (x.Dot(y) / x_dot_x) * x; + + // Make Z axis perpendicular to X + Vec3 z = GetAxisZ(); + z -= (x.Dot(z) / x_dot_x) * x; + + // Make Z axis perpendicular to Y + float y_dot_y = y.LengthSq(); + z -= (y.Dot(z) / y_dot_y) * y; + + // Determine the scale + float z_dot_z = z.LengthSq(); + outScale = Vec3(x_dot_x, y_dot_y, z_dot_z).Sqrt(); + + // If the resulting x, y and z vectors don't form a right handed matrix, flip the z axis. + if (x.Cross(y).Dot(z) < 0.0f) + outScale.SetZ(-outScale.GetZ()); + + // Determine the rotation and translation + return Mat44(Vec4(x / outScale.GetX(), 0), Vec4(y / outScale.GetY(), 0), Vec4(z / outScale.GetZ(), 0), GetColumn4(3)); +} + +#undef JPH_EL + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Math.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Math.h new file mode 100644 index 00000000000..fe787f21086 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Math.h @@ -0,0 +1,203 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +JPH_NAMESPACE_BEGIN + +/// The constant \f$\pi\f$ +static constexpr float JPH_PI = 3.14159265358979323846f; + +/// Convert a value from degrees to radians +constexpr float DegreesToRadians(float inV) +{ + return inV * (JPH_PI / 180.0f); +} + +/// Convert a value from radians to degrees +constexpr float RadiansToDegrees(float inV) +{ + return inV * (180.0f / JPH_PI); +} + +/// Convert angle in radians to the range \f$[-\pi, \pi]\f$ +inline float CenterAngleAroundZero(float inV) +{ + if (inV < -JPH_PI) + { + do + inV += 2.0f * JPH_PI; + while (inV < -JPH_PI); + } + else if (inV > JPH_PI) + { + do + inV -= 2.0f * JPH_PI; + while (inV > JPH_PI); + } + JPH_ASSERT(inV >= -JPH_PI && inV <= JPH_PI); + return inV; +} + +/// Clamp a value between two values +template +constexpr T Clamp(T inV, T inMin, T inMax) +{ + return min(max(inV, inMin), inMax); +} + +/// Square a value +template +constexpr T Square(T inV) +{ + return inV * inV; +} + +/// Returns \f$inV^3\f$. +template +constexpr T Cubed(T inV) +{ + return inV * inV * inV; +} + +/// Get the sign of a value +template +constexpr T Sign(T inV) +{ + return inV < 0? T(-1) : T(1); +} + +/// Check if inV is a power of 2 +template +constexpr bool IsPowerOf2(T inV) +{ + return (inV & (inV - 1)) == 0; +} + +/// Align inV up to the next inAlignment bytes +template +inline T AlignUp(T inV, uint64 inAlignment) +{ + JPH_ASSERT(IsPowerOf2(inAlignment)); + return T((uint64(inV) + inAlignment - 1) & ~(inAlignment - 1)); +} + +/// Check if inV is inAlignment aligned +template +inline bool IsAligned(T inV, uint64 inAlignment) +{ + JPH_ASSERT(IsPowerOf2(inAlignment)); + return (uint64(inV) & (inAlignment - 1)) == 0; +} + +/// Compute number of trailing zero bits (how many low bits are zero) +inline uint CountTrailingZeros(uint32 inValue) +{ +#if defined(JPH_CPU_X86) || defined(JPH_CPU_WASM) + #if defined(JPH_USE_TZCNT) + return _tzcnt_u32(inValue); + #elif defined(JPH_COMPILER_MSVC) + if (inValue == 0) + return 32; + unsigned long result; + _BitScanForward(&result, inValue); + return result; + #else + if (inValue == 0) + return 32; + return __builtin_ctz(inValue); + #endif +#elif defined(JPH_CPU_ARM) + #if defined(JPH_COMPILER_MSVC) + if (inValue == 0) + return 32; + unsigned long result; + _BitScanForward(&result, inValue); + return result; + #else + return __builtin_clz(__builtin_bitreverse32(inValue)); + #endif +#elif defined(JPH_CPU_E2K) + return inValue ? __builtin_ctz(inValue) : 32; +#else + #error Undefined +#endif +} + +/// Compute the number of leading zero bits (how many high bits are zero) +inline uint CountLeadingZeros(uint32 inValue) +{ +#if defined(JPH_CPU_X86) || defined(JPH_CPU_WASM) + #if defined(JPH_USE_LZCNT) + return _lzcnt_u32(inValue); + #elif defined(JPH_COMPILER_MSVC) + if (inValue == 0) + return 32; + unsigned long result; + _BitScanReverse(&result, inValue); + return 31 - result; + #else + if (inValue == 0) + return 32; + return __builtin_clz(inValue); + #endif +#elif defined(JPH_CPU_ARM) + #if defined(JPH_COMPILER_MSVC) + return _CountLeadingZeros(inValue); + #else + return __builtin_clz(inValue); + #endif +#elif defined(JPH_CPU_E2K) + return inValue ? __builtin_clz(inValue) : 32; +#else + #error Undefined +#endif +} + +/// Count the number of 1 bits in a value +inline uint CountBits(uint32 inValue) +{ +#if defined(JPH_COMPILER_CLANG) || defined(JPH_COMPILER_GCC) + return __builtin_popcount(inValue); +#elif defined(JPH_COMPILER_MSVC) + #if defined(JPH_USE_SSE4_2) + return _mm_popcnt_u32(inValue); + #elif defined(JPH_USE_NEON) && (_MSC_VER >= 1930) // _CountOneBits not available on MSVC2019 + return _CountOneBits(inValue); + #else + inValue = inValue - ((inValue >> 1) & 0x55555555); + inValue = (inValue & 0x33333333) + ((inValue >> 2) & 0x33333333); + inValue = (inValue + (inValue >> 4)) & 0x0F0F0F0F; + return (inValue * 0x01010101) >> 24; + #endif +#else + #error Undefined +#endif +} + +/// Get the next higher power of 2 of a value, or the value itself if the value is already a power of 2 +inline uint32 GetNextPowerOf2(uint32 inValue) +{ + return inValue <= 1? uint32(1) : uint32(1) << (32 - CountLeadingZeros(inValue - 1)); +} + +// Simple implementation of C++20 std::bit_cast (unfortunately not constexpr) +template +JPH_INLINE To BitCast(const From &inValue) +{ + static_assert(std::is_trivially_constructible_v); + static_assert(sizeof(From) == sizeof(To)); + + union FromTo + { + To mTo; + From mFrom; + }; + + FromTo convert; + convert.mFrom = inValue; + return convert.mTo; +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/MathTypes.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/MathTypes.h new file mode 100644 index 00000000000..8d019ae427f --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/MathTypes.h @@ -0,0 +1,34 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +JPH_NAMESPACE_BEGIN + +class Vec3; +class DVec3; +class Vec4; +class UVec4; +class Vec8; +class UVec8; +class Quat; +class Mat44; +class DMat44; + +// Types to use for passing arguments to functions +using Vec3Arg = const Vec3; +#ifdef JPH_USE_AVX + using DVec3Arg = const DVec3; +#else + using DVec3Arg = const DVec3 &; +#endif +using Vec4Arg = const Vec4; +using UVec4Arg = const UVec4; +using Vec8Arg = const Vec8; +using UVec8Arg = const UVec8; +using QuatArg = const Quat; +using Mat44Arg = const Mat44 &; +using DMat44Arg = const DMat44 &; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Matrix.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Matrix.h new file mode 100644 index 00000000000..031665bbe71 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Matrix.h @@ -0,0 +1,259 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +JPH_NAMESPACE_BEGIN + +/// Templatized matrix class +template +class [[nodiscard]] Matrix +{ +public: + /// Constructor + inline Matrix() = default; + inline Matrix(const Matrix &inM2) { *this = inM2; } + + /// Dimensions + inline uint GetRows() const { return Rows; } + inline uint GetCols() const { return Cols; } + + /// Zero matrix + inline void SetZero() + { + for (uint c = 0; c < Cols; ++c) + mCol[c].SetZero(); + } + + inline static Matrix sZero() { Matrix m; m.SetZero(); return m; } + + /// Check if this matrix consists of all zeros + inline bool IsZero() const + { + for (uint c = 0; c < Cols; ++c) + if (!mCol[c].IsZero()) + return false; + + return true; + } + + /// Identity matrix + inline void SetIdentity() + { + // Clear matrix + SetZero(); + + // Set diagonal to 1 + for (uint rc = 0, min_rc = min(Rows, Cols); rc < min_rc; ++rc) + mCol[rc].mF32[rc] = 1.0f; + } + + inline static Matrix sIdentity() { Matrix m; m.SetIdentity(); return m; } + + /// Check if this matrix is identity + bool IsIdentity() const { return *this == sIdentity(); } + + /// Diagonal matrix + inline void SetDiagonal(const Vector &inV) + { + // Clear matrix + SetZero(); + + // Set diagonal + for (uint rc = 0, min_rc = min(Rows, Cols); rc < min_rc; ++rc) + mCol[rc].mF32[rc] = inV[rc]; + } + + inline static Matrix sDiagonal(const Vector &inV) + { + Matrix m; + m.SetDiagonal(inV); + return m; + } + + /// Copy a (part) of another matrix into this matrix + template + void CopyPart(const OtherMatrix &inM, uint inSourceRow, uint inSourceCol, uint inNumRows, uint inNumCols, uint inDestRow, uint inDestCol) + { + for (uint c = 0; c < inNumCols; ++c) + for (uint r = 0; r < inNumRows; ++r) + mCol[inDestCol + c].mF32[inDestRow + r] = inM(inSourceRow + r, inSourceCol + c); + } + + /// Get float component by element index + inline float operator () (uint inRow, uint inColumn) const + { + JPH_ASSERT(inRow < Rows); + JPH_ASSERT(inColumn < Cols); + return mCol[inColumn].mF32[inRow]; + } + + inline float & operator () (uint inRow, uint inColumn) + { + JPH_ASSERT(inRow < Rows); + JPH_ASSERT(inColumn < Cols); + return mCol[inColumn].mF32[inRow]; + } + + /// Comparison + inline bool operator == (const Matrix &inM2) const + { + for (uint c = 0; c < Cols; ++c) + if (mCol[c] != inM2.mCol[c]) + return false; + return true; + } + + inline bool operator != (const Matrix &inM2) const + { + for (uint c = 0; c < Cols; ++c) + if (mCol[c] != inM2.mCol[c]) + return true; + return false; + } + + /// Assignment + inline Matrix & operator = (const Matrix &inM2) + { + for (uint c = 0; c < Cols; ++c) + mCol[c] = inM2.mCol[c]; + return *this; + } + + /// Multiply matrix by matrix + template + inline Matrix operator * (const Matrix &inM) const + { + Matrix m; + for (uint c = 0; c < OtherCols; ++c) + for (uint r = 0; r < Rows; ++r) + { + float dot = 0.0f; + for (uint i = 0; i < Cols; ++i) + dot += mCol[i].mF32[r] * inM.mCol[c].mF32[i]; + m.mCol[c].mF32[r] = dot; + } + return m; + } + + /// Multiply vector by matrix + inline Vector operator * (const Vector &inV) const + { + Vector v; + for (uint r = 0; r < Rows; ++r) + { + float dot = 0.0f; + for (uint c = 0; c < Cols; ++c) + dot += mCol[c].mF32[r] * inV.mF32[c]; + v.mF32[r] = dot; + } + return v; + } + + /// Multiply matrix with float + inline Matrix operator * (float inV) const + { + Matrix m; + for (uint c = 0; c < Cols; ++c) + m.mCol[c] = mCol[c] * inV; + return m; + } + + inline friend Matrix operator * (float inV, const Matrix &inM) + { + return inM * inV; + } + + /// Per element addition of matrix + inline Matrix operator + (const Matrix &inM) const + { + Matrix m; + for (uint c = 0; c < Cols; ++c) + m.mCol[c] = mCol[c] + inM.mCol[c]; + return m; + } + + /// Per element subtraction of matrix + inline Matrix operator - (const Matrix &inM) const + { + Matrix m; + for (uint c = 0; c < Cols; ++c) + m.mCol[c] = mCol[c] - inM.mCol[c]; + return m; + } + + /// Transpose matrix + inline Matrix Transposed() const + { + Matrix m; + for (uint r = 0; r < Rows; ++r) + for (uint c = 0; c < Cols; ++c) + m.mCol[r].mF32[c] = mCol[c].mF32[r]; + return m; + } + + /// Inverse matrix + bool SetInversed(const Matrix &inM) + { + if constexpr (Rows != Cols) JPH_ASSERT(false); + Matrix copy(inM); + SetIdentity(); + return GaussianElimination(copy, *this); + } + + inline Matrix Inversed() const + { + Matrix m; + m.SetInversed(*this); + return m; + } + + /// To String + friend ostream & operator << (ostream &inStream, const Matrix &inM) + { + for (uint i = 0; i < Cols - 1; ++i) + inStream << inM.mCol[i] << ", "; + inStream << inM.mCol[Cols - 1]; + return inStream; + } + + /// Column access + const Vector & GetColumn(int inIdx) const { return mCol[inIdx]; } + Vector & GetColumn(int inIdx) { return mCol[inIdx]; } + + Vector mCol[Cols]; ///< Column +}; + +// The template specialization doesn't sit well with Doxygen +#ifndef JPH_PLATFORM_DOXYGEN + +/// Specialization of SetInversed for 2x2 matrix +template <> +inline bool Matrix<2, 2>::SetInversed(const Matrix<2, 2> &inM) +{ + // Fetch elements + float a = inM.mCol[0].mF32[0]; + float b = inM.mCol[1].mF32[0]; + float c = inM.mCol[0].mF32[1]; + float d = inM.mCol[1].mF32[1]; + + // Calculate determinant + float det = a * d - b * c; + if (det == 0.0f) + return false; + + // Construct inverse + mCol[0].mF32[0] = d / det; + mCol[1].mF32[0] = -b / det; + mCol[0].mF32[1] = -c / det; + mCol[1].mF32[1] = a / det; + return true; +} + +#endif // !JPH_PLATFORM_DOXYGEN + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Quat.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Quat.h new file mode 100644 index 00000000000..10564cab156 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Quat.h @@ -0,0 +1,255 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +JPH_NAMESPACE_BEGIN + +/// Quaternion class, quaternions are 4 dimensional vectors which can describe rotations in 3 dimensional +/// space if their length is 1. +/// +/// They are written as: +/// +/// \f$q = w + x \: i + y \: j + z \: k\f$ +/// +/// or in vector notation: +/// +/// \f$q = [w, v] = [w, x, y, z]\f$ +/// +/// Where: +/// +/// w = the real part +/// v = the imaginary part, (x, y, z) +/// +/// Note that we store the quaternion in a Vec4 as [x, y, z, w] because that makes +/// it easy to extract the rotation axis of the quaternion: +/// +/// q = [cos(angle / 2), sin(angle / 2) * rotation_axis] +class [[nodiscard]] alignas(JPH_VECTOR_ALIGNMENT) Quat +{ +public: + JPH_OVERRIDE_NEW_DELETE + + ///@name Constructors + ///@{ + inline Quat() = default; ///< Intentionally not initialized for performance reasons + Quat(const Quat &inRHS) = default; + Quat & operator = (const Quat &inRHS) = default; + inline Quat(float inX, float inY, float inZ, float inW) : mValue(inX, inY, inZ, inW) { } + inline explicit Quat(Vec4Arg inV) : mValue(inV) { } + ///@} + + ///@name Tests + ///@{ + + /// Check if two quaternions are exactly equal + inline bool operator == (QuatArg inRHS) const { return mValue == inRHS.mValue; } + + /// Check if two quaternions are different + inline bool operator != (QuatArg inRHS) const { return mValue != inRHS.mValue; } + + /// If this quaternion is close to inRHS. Note that q and -q represent the same rotation, this is not checked here. + inline bool IsClose(QuatArg inRHS, float inMaxDistSq = 1.0e-12f) const { return mValue.IsClose(inRHS.mValue, inMaxDistSq); } + + /// If the length of this quaternion is 1 +/- inTolerance + inline bool IsNormalized(float inTolerance = 1.0e-5f) const { return mValue.IsNormalized(inTolerance); } + + /// If any component of this quaternion is a NaN (not a number) + inline bool IsNaN() const { return mValue.IsNaN(); } + + ///@} + ///@name Get components + ///@{ + + /// Get X component (imaginary part i) + JPH_INLINE float GetX() const { return mValue.GetX(); } + + /// Get Y component (imaginary part j) + JPH_INLINE float GetY() const { return mValue.GetY(); } + + /// Get Z component (imaginary part k) + JPH_INLINE float GetZ() const { return mValue.GetZ(); } + + /// Get W component (real part) + JPH_INLINE float GetW() const { return mValue.GetW(); } + + /// Get the imaginary part of the quaternion + JPH_INLINE Vec3 GetXYZ() const { return Vec3(mValue); } + + /// Get the quaternion as a Vec4 + JPH_INLINE Vec4 GetXYZW() const { return mValue; } + + /// Set individual components + JPH_INLINE void SetX(float inX) { mValue.SetX(inX); } + JPH_INLINE void SetY(float inY) { mValue.SetY(inY); } + JPH_INLINE void SetZ(float inZ) { mValue.SetZ(inZ); } + JPH_INLINE void SetW(float inW) { mValue.SetW(inW); } + + /// Set all components + JPH_INLINE void Set(float inX, float inY, float inZ, float inW) { mValue.Set(inX, inY, inZ, inW); } + + ///@} + ///@name Default quaternions + ///@{ + + /// @return [0, 0, 0, 0] + JPH_INLINE static Quat sZero() { return Quat(Vec4::sZero()); } + + /// @return [1, 0, 0, 0] (or in storage format Quat(0, 0, 0, 1)) + JPH_INLINE static Quat sIdentity() { return Quat(0, 0, 0, 1); } + + ///@} + + /// Rotation from axis and angle + JPH_INLINE static Quat sRotation(Vec3Arg inAxis, float inAngle); + + /// Get axis and angle that represents this quaternion, outAngle will always be in the range \f$[0, \pi]\f$ + JPH_INLINE void GetAxisAngle(Vec3 &outAxis, float &outAngle) const; + + /// Create quaternion that rotates a vector from the direction of inFrom to the direction of inTo along the shortest path + /// @see https://www.euclideanspace.com/maths/algebra/vectors/angleBetween/index.htm + JPH_INLINE static Quat sFromTo(Vec3Arg inFrom, Vec3Arg inTo); + + /// Random unit quaternion + template + inline static Quat sRandom(Random &inRandom); + + /// Conversion from Euler angles. Rotation order is X then Y then Z (RotZ * RotY * RotX). Angles in radians. + inline static Quat sEulerAngles(Vec3Arg inAngles); + + /// Conversion to Euler angles. Rotation order is X then Y then Z (RotZ * RotY * RotX). Angles in radians. + inline Vec3 GetEulerAngles() const; + + ///@name Length / normalization operations + ///@{ + + /// Squared length of quaternion. + /// @return Squared length of quaternion (\f$|v|^2\f$) + JPH_INLINE float LengthSq() const { return mValue.LengthSq(); } + + /// Length of quaternion. + /// @return Length of quaternion (\f$|v|\f$) + JPH_INLINE float Length() const { return mValue.Length(); } + + /// Normalize the quaternion (make it length 1) + JPH_INLINE Quat Normalized() const { return Quat(mValue.Normalized()); } + + ///@} + ///@name Additions / multiplications + ///@{ + + JPH_INLINE void operator += (QuatArg inRHS) { mValue += inRHS.mValue; } + JPH_INLINE void operator -= (QuatArg inRHS) { mValue -= inRHS.mValue; } + JPH_INLINE void operator *= (float inValue) { mValue *= inValue; } + JPH_INLINE void operator /= (float inValue) { mValue /= inValue; } + JPH_INLINE Quat operator - () const { return Quat(-mValue); } + JPH_INLINE Quat operator + (QuatArg inRHS) const { return Quat(mValue + inRHS.mValue); } + JPH_INLINE Quat operator - (QuatArg inRHS) const { return Quat(mValue - inRHS.mValue); } + JPH_INLINE Quat operator * (QuatArg inRHS) const; + JPH_INLINE Quat operator * (float inValue) const { return Quat(mValue * inValue); } + inline friend Quat operator * (float inValue, QuatArg inRHS) { return Quat(inRHS.mValue * inValue); } + JPH_INLINE Quat operator / (float inValue) const { return Quat(mValue / inValue); } + + ///@} + + /// Rotate a vector by this quaternion + JPH_INLINE Vec3 operator * (Vec3Arg inValue) const; + + /// Rotate a vector by the inverse of this quaternion + JPH_INLINE Vec3 InverseRotate(Vec3Arg inValue) const; + + /// Rotate a the vector (1, 0, 0) with this quaternion + JPH_INLINE Vec3 RotateAxisX() const; + + /// Rotate a the vector (0, 1, 0) with this quaternion + JPH_INLINE Vec3 RotateAxisY() const; + + /// Rotate a the vector (0, 0, 1) with this quaternion + JPH_INLINE Vec3 RotateAxisZ() const; + + /// Dot product + JPH_INLINE float Dot(QuatArg inRHS) const { return mValue.Dot(inRHS.mValue); } + + /// The conjugate [w, -x, -y, -z] is the same as the inverse for unit quaternions + JPH_INLINE Quat Conjugated() const { return Quat(Vec4::sXor(mValue, UVec4(0x80000000, 0x80000000, 0x80000000, 0).ReinterpretAsFloat())); } + + /// Get inverse quaternion + JPH_INLINE Quat Inversed() const { return Conjugated() / Length(); } + + /// Ensures that the W component is positive by negating the entire quaternion if it is not. This is useful when you want to store a quaternion as a 3 vector by discarding W and reconstructing it as sqrt(1 - x^2 - y^2 - z^2). + JPH_INLINE Quat EnsureWPositive() const { return Quat(Vec4::sXor(mValue, Vec4::sAnd(mValue.SplatW(), UVec4::sReplicate(0x80000000).ReinterpretAsFloat()))); } + + /// Get a quaternion that is perpendicular to this quaternion + JPH_INLINE Quat GetPerpendicular() const { return Quat(Vec4(1, -1, 1, -1) * mValue.Swizzle()); } + + /// Get rotation angle around inAxis (uses Swing Twist Decomposition to get the twist quaternion and uses q(axis, angle) = [cos(angle / 2), axis * sin(angle / 2)]) + JPH_INLINE float GetRotationAngle(Vec3Arg inAxis) const { return GetW() == 0.0f? JPH_PI : 2.0f * ATan(GetXYZ().Dot(inAxis) / GetW()); } + + /// Swing Twist Decomposition: any quaternion can be split up as: + /// + /// \f[q = q_{swing} \: q_{twist}\f] + /// + /// where \f$q_{twist}\f$ rotates only around axis v. + /// + /// \f$q_{twist}\f$ is: + /// + /// \f[q_{twist} = \frac{[q_w, q_{ijk} \cdot v \: v]}{\left|[q_w, q_{ijk} \cdot v \: v]\right|}\f] + /// + /// where q_w is the real part of the quaternion and q_i the imaginary part (a 3 vector). + /// + /// The swing can then be calculated as: + /// + /// \f[q_{swing} = q \: q_{twist}^* \f] + /// + /// Where \f$q_{twist}^*\f$ = complex conjugate of \f$q_{twist}\f$ + JPH_INLINE Quat GetTwist(Vec3Arg inAxis) const; + + /// Decomposes quaternion into swing and twist component: + /// + /// \f$q = q_{swing} \: q_{twist}\f$ + /// + /// where \f$q_{swing} \: \hat{x} = q_{twist} \: \hat{y} = q_{twist} \: \hat{z} = 0\f$ + /// + /// In other words: + /// + /// - \f$q_{twist}\f$ only rotates around the X-axis. + /// - \f$q_{swing}\f$ only rotates around the Y and Z-axis. + /// + /// @see Gino van den Bergen - Rotational Joint Limits in Quaternion Space - GDC 2016 + JPH_INLINE void GetSwingTwist(Quat &outSwing, Quat &outTwist) const; + + /// Linear interpolation between two quaternions (for small steps). + /// @param inFraction is in the range [0, 1] + /// @param inDestination The destination quaternion + /// @return (1 - inFraction) * this + fraction * inDestination + JPH_INLINE Quat LERP(QuatArg inDestination, float inFraction) const; + + /// Spherical linear interpolation between two quaternions. + /// @param inFraction is in the range [0, 1] + /// @param inDestination The destination quaternion + /// @return When fraction is zero this quaternion is returned, when fraction is 1 inDestination is returned. + /// When fraction is between 0 and 1 an interpolation along the shortest path is returned. + JPH_INLINE Quat SLERP(QuatArg inDestination, float inFraction) const; + + /// Load 3 floats from memory (X, Y and Z component and then calculates W) reads 32 bits extra which it doesn't use + static JPH_INLINE Quat sLoadFloat3Unsafe(const Float3 &inV); + + /// Store 3 as floats to memory (X, Y and Z component) + JPH_INLINE void StoreFloat3(Float3 *outV) const; + + /// To String + friend ostream & operator << (ostream &inStream, QuatArg inQ) { inStream << inQ.mValue; return inStream; } + + /// 4 vector that stores [x, y, z, w] parts of the quaternion + Vec4 mValue; +}; + +static_assert(is_trivial(), "Is supposed to be a trivial type!"); + +JPH_NAMESPACE_END + +#include "Quat.inl" diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Quat.inl b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Quat.inl new file mode 100644 index 00000000000..72b78341b70 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Quat.inl @@ -0,0 +1,328 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +JPH_NAMESPACE_BEGIN + +Quat Quat::operator * (QuatArg inRHS) const +{ +#if defined(JPH_USE_SSE4_1) + // Taken from: http://momchil-velikov.blogspot.nl/2013/10/fast-sse-quternion-multiplication.html + __m128 abcd = mValue.mValue; + __m128 xyzw = inRHS.mValue.mValue; + + __m128 t0 = _mm_shuffle_ps(abcd, abcd, _MM_SHUFFLE(3, 3, 3, 3)); + __m128 t1 = _mm_shuffle_ps(xyzw, xyzw, _MM_SHUFFLE(2, 3, 0, 1)); + + __m128 t3 = _mm_shuffle_ps(abcd, abcd, _MM_SHUFFLE(0, 0, 0, 0)); + __m128 t4 = _mm_shuffle_ps(xyzw, xyzw, _MM_SHUFFLE(1, 0, 3, 2)); + + __m128 t5 = _mm_shuffle_ps(abcd, abcd, _MM_SHUFFLE(1, 1, 1, 1)); + __m128 t6 = _mm_shuffle_ps(xyzw, xyzw, _MM_SHUFFLE(2, 0, 3, 1)); + + // [d,d,d,d] * [z,w,x,y] = [dz,dw,dx,dy] + __m128 m0 = _mm_mul_ps(t0, t1); + + // [a,a,a,a] * [y,x,w,z] = [ay,ax,aw,az] + __m128 m1 = _mm_mul_ps(t3, t4); + + // [b,b,b,b] * [z,x,w,y] = [bz,bx,bw,by] + __m128 m2 = _mm_mul_ps(t5, t6); + + // [c,c,c,c] * [w,z,x,y] = [cw,cz,cx,cy] + __m128 t7 = _mm_shuffle_ps(abcd, abcd, _MM_SHUFFLE(2, 2, 2, 2)); + __m128 t8 = _mm_shuffle_ps(xyzw, xyzw, _MM_SHUFFLE(3, 2, 0, 1)); + __m128 m3 = _mm_mul_ps(t7, t8); + + // [dz,dw,dx,dy] + -[ay,ax,aw,az] = [dz+ay,dw-ax,dx+aw,dy-az] + __m128 e = _mm_addsub_ps(m0, m1); + + // [dx+aw,dz+ay,dy-az,dw-ax] + e = _mm_shuffle_ps(e, e, _MM_SHUFFLE(1, 3, 0, 2)); + + // [dx+aw,dz+ay,dy-az,dw-ax] + -[bz,bx,bw,by] = [dx+aw+bz,dz+ay-bx,dy-az+bw,dw-ax-by] + e = _mm_addsub_ps(e, m2); + + // [dz+ay-bx,dw-ax-by,dy-az+bw,dx+aw+bz] + e = _mm_shuffle_ps(e, e, _MM_SHUFFLE(2, 0, 1, 3)); + + // [dz+ay-bx,dw-ax-by,dy-az+bw,dx+aw+bz] + -[cw,cz,cx,cy] = [dz+ay-bx+cw,dw-ax-by-cz,dy-az+bw+cx,dx+aw+bz-cy] + e = _mm_addsub_ps(e, m3); + + // [dw-ax-by-cz,dz+ay-bx+cw,dy-az+bw+cx,dx+aw+bz-cy] + return Quat(Vec4(_mm_shuffle_ps(e, e, _MM_SHUFFLE(2, 3, 1, 0)))); +#else + float lx = mValue.GetX(); + float ly = mValue.GetY(); + float lz = mValue.GetZ(); + float lw = mValue.GetW(); + + float rx = inRHS.mValue.GetX(); + float ry = inRHS.mValue.GetY(); + float rz = inRHS.mValue.GetZ(); + float rw = inRHS.mValue.GetW(); + + float x = lw * rx + lx * rw + ly * rz - lz * ry; + float y = lw * ry - lx * rz + ly * rw + lz * rx; + float z = lw * rz + lx * ry - ly * rx + lz * rw; + float w = lw * rw - lx * rx - ly * ry - lz * rz; + + return Quat(x, y, z, w); +#endif +} + +Quat Quat::sRotation(Vec3Arg inAxis, float inAngle) +{ + // returns [inAxis * sin(0.5f * inAngle), cos(0.5f * inAngle)] + JPH_ASSERT(inAxis.IsNormalized()); + Vec4 s, c; + Vec4::sReplicate(0.5f * inAngle).SinCos(s, c); + return Quat(Vec4::sSelect(Vec4(inAxis) * s, c, UVec4(0, 0, 0, 0xffffffffU))); +} + +void Quat::GetAxisAngle(Vec3 &outAxis, float &outAngle) const +{ + JPH_ASSERT(IsNormalized()); + Quat w_pos = EnsureWPositive(); + float abs_w = w_pos.GetW(); + if (abs_w >= 1.0f) + { + outAxis = Vec3::sZero(); + outAngle = 0.0f; + } + else + { + outAngle = 2.0f * ACos(abs_w); + outAxis = w_pos.GetXYZ().NormalizedOr(Vec3::sZero()); + } +} + +Quat Quat::sFromTo(Vec3Arg inFrom, Vec3Arg inTo) +{ + /* + Uses (inFrom = v1, inTo = v2): + + angle = arcos(v1 . v2 / |v1||v2|) + axis = normalize(v1 x v2) + + Quaternion is then: + + s = sin(angle / 2) + x = axis.x * s + y = axis.y * s + z = axis.z * s + w = cos(angle / 2) + + Using identities: + + sin(2 * a) = 2 * sin(a) * cos(a) + cos(2 * a) = cos(a)^2 - sin(a)^2 + sin(a)^2 + cos(a)^2 = 1 + + This reduces to: + + x = (v1 x v2).x + y = (v1 x v2).y + z = (v1 x v2).z + w = |v1||v2| + v1 . v2 + + which then needs to be normalized because the whole equation was multiplied by 2 cos(angle / 2) + */ + + float len_v1_v2 = sqrt(inFrom.LengthSq() * inTo.LengthSq()); + float w = len_v1_v2 + inFrom.Dot(inTo); + + if (w == 0.0f) + { + if (len_v1_v2 == 0.0f) + { + // If either of the vectors has zero length, there is no rotation and we return identity + return Quat::sIdentity(); + } + else + { + // If vectors are perpendicular, take one of the many 180 degree rotations that exist + return Quat(Vec4(inFrom.GetNormalizedPerpendicular(), 0)); + } + } + + Vec3 v = inFrom.Cross(inTo); + return Quat(Vec4(v, w)).Normalized(); +} + +template +Quat Quat::sRandom(Random &inRandom) +{ + std::uniform_real_distribution zero_to_one(0.0f, 1.0f); + float x0 = zero_to_one(inRandom); + float r1 = sqrt(1.0f - x0), r2 = sqrt(x0); + std::uniform_real_distribution zero_to_two_pi(0.0f, 2.0f * JPH_PI); + Vec4 s, c; + Vec4(zero_to_two_pi(inRandom), zero_to_two_pi(inRandom), 0, 0).SinCos(s, c); + return Quat(s.GetX() * r1, c.GetX() * r1, s.GetY() * r2, c.GetY() * r2); +} + +Quat Quat::sEulerAngles(Vec3Arg inAngles) +{ + Vec4 half(0.5f * inAngles); + Vec4 s, c; + half.SinCos(s, c); + + float cx = c.GetX(); + float sx = s.GetX(); + float cy = c.GetY(); + float sy = s.GetY(); + float cz = c.GetZ(); + float sz = s.GetZ(); + + return Quat( + cz * sx * cy - sz * cx * sy, + cz * cx * sy + sz * sx * cy, + sz * cx * cy - cz * sx * sy, + cz * cx * cy + sz * sx * sy); +} + +Vec3 Quat::GetEulerAngles() const +{ + float y_sq = GetY() * GetY(); + + // X + float t0 = 2.0f * (GetW() * GetX() + GetY() * GetZ()); + float t1 = 1.0f - 2.0f * (GetX() * GetX() + y_sq); + + // Y + float t2 = 2.0f * (GetW() * GetY() - GetZ() * GetX()); + t2 = t2 > 1.0f? 1.0f : t2; + t2 = t2 < -1.0f? -1.0f : t2; + + // Z + float t3 = 2.0f * (GetW() * GetZ() + GetX() * GetY()); + float t4 = 1.0f - 2.0f * (y_sq + GetZ() * GetZ()); + + return Vec3(ATan2(t0, t1), ASin(t2), ATan2(t3, t4)); +} + +Quat Quat::GetTwist(Vec3Arg inAxis) const +{ + Quat twist(Vec4(GetXYZ().Dot(inAxis) * inAxis, GetW())); + float twist_len = twist.LengthSq(); + if (twist_len != 0.0f) + return twist / sqrt(twist_len); + else + return Quat::sIdentity(); +} + +void Quat::GetSwingTwist(Quat &outSwing, Quat &outTwist) const +{ + float x = GetX(), y = GetY(), z = GetZ(), w = GetW(); + float s = sqrt(Square(w) + Square(x)); + if (s != 0.0f) + { + outTwist = Quat(x / s, 0, 0, w / s); + outSwing = Quat(0, (w * y - x * z) / s, (w * z + x * y) / s, s); + } + else + { + // If both x and w are zero, this must be a 180 degree rotation around either y or z + outTwist = Quat::sIdentity(); + outSwing = *this; + } +} + +Quat Quat::LERP(QuatArg inDestination, float inFraction) const +{ + float scale0 = 1.0f - inFraction; + return Quat(Vec4::sReplicate(scale0) * mValue + Vec4::sReplicate(inFraction) * inDestination.mValue); +} + +Quat Quat::SLERP(QuatArg inDestination, float inFraction) const +{ + // Difference at which to LERP instead of SLERP + const float delta = 0.0001f; + + // Calc cosine + float sign_scale1 = 1.0f; + float cos_omega = Dot(inDestination); + + // Adjust signs (if necessary) + if (cos_omega < 0.0f) + { + cos_omega = -cos_omega; + sign_scale1 = -1.0f; + } + + // Calculate coefficients + float scale0, scale1; + if (1.0f - cos_omega > delta) + { + // Standard case (slerp) + float omega = ACos(cos_omega); + float sin_omega = Sin(omega); + scale0 = Sin((1.0f - inFraction) * omega) / sin_omega; + scale1 = sign_scale1 * Sin(inFraction * omega) / sin_omega; + } + else + { + // Quaternions are very close so we can do a linear interpolation + scale0 = 1.0f - inFraction; + scale1 = sign_scale1 * inFraction; + } + + // Interpolate between the two quaternions + return Quat(Vec4::sReplicate(scale0) * mValue + Vec4::sReplicate(scale1) * inDestination.mValue).Normalized(); +} + +Vec3 Quat::operator * (Vec3Arg inValue) const +{ + // Rotating a vector by a quaternion is done by: p' = q * p * q^-1 (q^-1 = conjugated(q) for a unit quaternion) + JPH_ASSERT(IsNormalized()); + return Vec3((*this * Quat(Vec4(inValue, 0)) * Conjugated()).mValue); +} + +Vec3 Quat::InverseRotate(Vec3Arg inValue) const +{ + JPH_ASSERT(IsNormalized()); + return Vec3((Conjugated() * Quat(Vec4(inValue, 0)) * *this).mValue); +} + +Vec3 Quat::RotateAxisX() const +{ + // This is *this * Vec3::sAxisX() written out: + JPH_ASSERT(IsNormalized()); + float x = GetX(), y = GetY(), z = GetZ(), w = GetW(); + float tx = 2.0f * x, tw = 2.0f * w; + return Vec3(tx * x + tw * w - 1.0f, tx * y + z * tw, tx * z - y * tw); +} + +Vec3 Quat::RotateAxisY() const +{ + // This is *this * Vec3::sAxisY() written out: + JPH_ASSERT(IsNormalized()); + float x = GetX(), y = GetY(), z = GetZ(), w = GetW(); + float ty = 2.0f * y, tw = 2.0f * w; + return Vec3(x * ty - z * tw, tw * w + ty * y - 1.0f, x * tw + ty * z); +} + +Vec3 Quat::RotateAxisZ() const +{ + // This is *this * Vec3::sAxisZ() written out: + JPH_ASSERT(IsNormalized()); + float x = GetX(), y = GetY(), z = GetZ(), w = GetW(); + float tz = 2.0f * z, tw = 2.0f * w; + return Vec3(x * tz + y * tw, y * tz - x * tw, tw * w + tz * z - 1.0f); +} + +void Quat::StoreFloat3(Float3 *outV) const +{ + JPH_ASSERT(IsNormalized()); + EnsureWPositive().GetXYZ().StoreFloat3(outV); +} + +Quat Quat::sLoadFloat3Unsafe(const Float3 &inV) +{ + Vec3 v = Vec3::sLoadFloat3Unsafe(inV); + float w = sqrt(max(1.0f - v.LengthSq(), 0.0f)); // It is possible that the length of v is a fraction above 1, and we don't want to introduce NaN's in that case so we clamp to 0 + return Quat(Vec4(v, w)); +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Real.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Real.h new file mode 100644 index 00000000000..7773abf7ef5 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Real.h @@ -0,0 +1,44 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2022 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +JPH_NAMESPACE_BEGIN + +#ifdef JPH_DOUBLE_PRECISION + +// Define real to double +using Real = double; +using Real3 = Double3; +using RVec3 = DVec3; +using RVec3Arg = DVec3Arg; +using RMat44 = DMat44; +using RMat44Arg = DMat44Arg; + +#define JPH_RVECTOR_ALIGNMENT JPH_DVECTOR_ALIGNMENT + +#else + +// Define real to float +using Real = float; +using Real3 = Float3; +using RVec3 = Vec3; +using RVec3Arg = Vec3Arg; +using RMat44 = Mat44; +using RMat44Arg = Mat44Arg; + +#define JPH_RVECTOR_ALIGNMENT JPH_VECTOR_ALIGNMENT + +#endif // JPH_DOUBLE_PRECISION + +// Put the 'real' operator in a namespace so that users can opt in to use it: +// using namespace JPH::literals; +namespace literals { + constexpr Real operator ""_r (long double inValue) { return Real(inValue); } +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Swizzle.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Swizzle.h new file mode 100644 index 00000000000..ad8dfbc144a --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Swizzle.h @@ -0,0 +1,19 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +JPH_NAMESPACE_BEGIN + +/// Enum indicating which component to use when swizzling +enum +{ + SWIZZLE_X = 0, ///< Use the X component + SWIZZLE_Y = 1, ///< Use the Y component + SWIZZLE_Z = 2, ///< Use the Z component + SWIZZLE_W = 3, ///< Use the W component + SWIZZLE_UNUSED = 2, ///< We always use the Z component when we don't specifically want to initialize a value, this is consistent with what is done in Vec3(x, y, z), Vec3(Float3 &) and Vec3::sLoadFloat3Unsafe +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Trigonometry.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Trigonometry.h new file mode 100644 index 00000000000..0a00564a8a4 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Trigonometry.h @@ -0,0 +1,59 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +JPH_NAMESPACE_BEGIN + +// Note that this file exists because std::sin etc. are not platform independent and will lead to non-deterministic simulation + +/// Sine of x (input in radians) +JPH_INLINE float Sin(float inX) +{ + Vec4 s, c; + Vec4::sReplicate(inX).SinCos(s, c); + return s.GetX(); +} + +/// Cosine of x (input in radians) +JPH_INLINE float Cos(float inX) +{ + Vec4 s, c; + Vec4::sReplicate(inX).SinCos(s, c); + return c.GetX(); +} + +/// Tangent of x (input in radians) +JPH_INLINE float Tan(float inX) +{ + return Vec4::sReplicate(inX).Tan().GetX(); +} + +/// Arc sine of x (returns value in the range [-PI / 2, PI / 2]) +/// Note that all input values will be clamped to the range [-1, 1] and this function will not return NaNs like std::asin +JPH_INLINE float ASin(float inX) +{ + return Vec4::sReplicate(inX).ASin().GetX(); +} + +/// Arc cosine of x (returns value in the range [0, PI]) +/// Note that all input values will be clamped to the range [-1, 1] and this function will not return NaNs like std::acos +JPH_INLINE float ACos(float inX) +{ + return Vec4::sReplicate(inX).ACos().GetX(); +} + +/// Arc tangent of x (returns value in the range [-PI / 2, PI / 2]) +JPH_INLINE float ATan(float inX) +{ + return Vec4::sReplicate(inX).ATan().GetX(); +} + +/// Arc tangent of y / x using the signs of the arguments to determine the correct quadrant (returns value in the range [-PI, PI]) +JPH_INLINE float ATan2(float inY, float inX) +{ + return Vec4::sATan2(Vec4::sReplicate(inY), Vec4::sReplicate(inX)).GetX(); +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/UVec4.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/UVec4.h new file mode 100644 index 00000000000..4855e2296c4 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/UVec4.h @@ -0,0 +1,220 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +class [[nodiscard]] alignas(JPH_VECTOR_ALIGNMENT) UVec4 +{ +public: + JPH_OVERRIDE_NEW_DELETE + + // Underlying vector type +#if defined(JPH_USE_SSE) + using Type = __m128i; +#elif defined(JPH_USE_NEON) + using Type = uint32x4_t; +#else + using Type = struct { uint32 mData[4]; }; +#endif + + /// Constructor + UVec4() = default; ///< Intentionally not initialized for performance reasons + UVec4(const UVec4 &inRHS) = default; + UVec4 & operator = (const UVec4 &inRHS) = default; + JPH_INLINE UVec4(Type inRHS) : mValue(inRHS) { } + + /// Create a vector from 4 integer components + JPH_INLINE UVec4(uint32 inX, uint32 inY, uint32 inZ, uint32 inW); + + /// Comparison + JPH_INLINE bool operator == (UVec4Arg inV2) const; + JPH_INLINE bool operator != (UVec4Arg inV2) const { return !(*this == inV2); } + + /// Swizzle the elements in inV + template + JPH_INLINE UVec4 Swizzle() const; + + /// Vector with all zeros + static JPH_INLINE UVec4 sZero(); + + /// Replicate int inV across all components + static JPH_INLINE UVec4 sReplicate(uint32 inV); + + /// Load 1 int from memory and place it in the X component, zeros Y, Z and W + static JPH_INLINE UVec4 sLoadInt(const uint32 *inV); + + /// Load 4 ints from memory + static JPH_INLINE UVec4 sLoadInt4(const uint32 *inV); + + /// Load 4 ints from memory, aligned to 16 bytes + static JPH_INLINE UVec4 sLoadInt4Aligned(const uint32 *inV); + + /// Gather 4 ints from memory at inBase + inOffsets[i] * Scale + template + static JPH_INLINE UVec4 sGatherInt4(const uint32 *inBase, UVec4Arg inOffsets); + + /// Return the minimum value of each of the components + static JPH_INLINE UVec4 sMin(UVec4Arg inV1, UVec4Arg inV2); + + /// Return the maximum of each of the components + static JPH_INLINE UVec4 sMax(UVec4Arg inV1, UVec4Arg inV2); + + /// Equals (component wise) + static JPH_INLINE UVec4 sEquals(UVec4Arg inV1, UVec4Arg inV2); + + /// Component wise select, returns inV1 when highest bit of inControl = 0 and inV2 when highest bit of inControl = 1 + static JPH_INLINE UVec4 sSelect(UVec4Arg inV1, UVec4Arg inV2, UVec4Arg inControl); + + /// Logical or (component wise) + static JPH_INLINE UVec4 sOr(UVec4Arg inV1, UVec4Arg inV2); + + /// Logical xor (component wise) + static JPH_INLINE UVec4 sXor(UVec4Arg inV1, UVec4Arg inV2); + + /// Logical and (component wise) + static JPH_INLINE UVec4 sAnd(UVec4Arg inV1, UVec4Arg inV2); + + /// Logical not (component wise) + static JPH_INLINE UVec4 sNot(UVec4Arg inV1); + + /// Sorts the elements in inIndex so that the values that correspond to trues in inValue are the first elements. + /// The remaining elements will be set to inValue.w. + /// I.e. if inValue = (true, false, true, false) and inIndex = (1, 2, 3, 4) the function returns (1, 3, 4, 4). + static JPH_INLINE UVec4 sSort4True(UVec4Arg inValue, UVec4Arg inIndex); + + /// Get individual components +#if defined(JPH_USE_SSE) + JPH_INLINE uint32 GetX() const { return uint32(_mm_cvtsi128_si32(mValue)); } + JPH_INLINE uint32 GetY() const { return mU32[1]; } + JPH_INLINE uint32 GetZ() const { return mU32[2]; } + JPH_INLINE uint32 GetW() const { return mU32[3]; } +#elif defined(JPH_USE_NEON) + JPH_INLINE uint32 GetX() const { return vgetq_lane_u32(mValue, 0); } + JPH_INLINE uint32 GetY() const { return vgetq_lane_u32(mValue, 1); } + JPH_INLINE uint32 GetZ() const { return vgetq_lane_u32(mValue, 2); } + JPH_INLINE uint32 GetW() const { return vgetq_lane_u32(mValue, 3); } +#else + JPH_INLINE uint32 GetX() const { return mU32[0]; } + JPH_INLINE uint32 GetY() const { return mU32[1]; } + JPH_INLINE uint32 GetZ() const { return mU32[2]; } + JPH_INLINE uint32 GetW() const { return mU32[3]; } +#endif + + /// Set individual components + JPH_INLINE void SetX(uint32 inX) { mU32[0] = inX; } + JPH_INLINE void SetY(uint32 inY) { mU32[1] = inY; } + JPH_INLINE void SetZ(uint32 inZ) { mU32[2] = inZ; } + JPH_INLINE void SetW(uint32 inW) { mU32[3] = inW; } + + /// Get component by index + JPH_INLINE uint32 operator [] (uint inCoordinate) const { JPH_ASSERT(inCoordinate < 4); return mU32[inCoordinate]; } + JPH_INLINE uint32 & operator [] (uint inCoordinate) { JPH_ASSERT(inCoordinate < 4); return mU32[inCoordinate]; } + + /// Multiplies each of the 4 integer components with an integer (discards any overflow) + JPH_INLINE UVec4 operator * (UVec4Arg inV2) const; + + /// Adds an integer value to all integer components (discards any overflow) + JPH_INLINE UVec4 operator + (UVec4Arg inV2); + + /// Add two integer vectors (component wise) + JPH_INLINE UVec4 & operator += (UVec4Arg inV2); + + /// Replicate the X component to all components + JPH_INLINE UVec4 SplatX() const; + + /// Replicate the Y component to all components + JPH_INLINE UVec4 SplatY() const; + + /// Replicate the Z component to all components + JPH_INLINE UVec4 SplatZ() const; + + /// Replicate the W component to all components + JPH_INLINE UVec4 SplatW() const; + + /// Convert each component from an int to a float + JPH_INLINE Vec4 ToFloat() const; + + /// Reinterpret UVec4 as a Vec4 (doesn't change the bits) + JPH_INLINE Vec4 ReinterpretAsFloat() const; + + /// Store 4 ints to memory + JPH_INLINE void StoreInt4(uint32 *outV) const; + + /// Store 4 ints to memory, aligned to 16 bytes + JPH_INLINE void StoreInt4Aligned(uint32 *outV) const; + + /// Test if any of the components are true (true is when highest bit of component is set) + JPH_INLINE bool TestAnyTrue() const; + + /// Test if any of X, Y or Z components are true (true is when highest bit of component is set) + JPH_INLINE bool TestAnyXYZTrue() const; + + /// Test if all components are true (true is when highest bit of component is set) + JPH_INLINE bool TestAllTrue() const; + + /// Test if X, Y and Z components are true (true is when highest bit of component is set) + JPH_INLINE bool TestAllXYZTrue() const; + + /// Count the number of components that are true (true is when highest bit of component is set) + JPH_INLINE int CountTrues() const; + + /// Store if X is true in bit 0, Y in bit 1, Z in bit 2 and W in bit 3 (true is when highest bit of component is set) + JPH_INLINE int GetTrues() const; + + /// Shift all components by Count bits to the left (filling with zeros from the left) + template + JPH_INLINE UVec4 LogicalShiftLeft() const; + + /// Shift all components by Count bits to the right (filling with zeros from the right) + template + JPH_INLINE UVec4 LogicalShiftRight() const; + + /// Shift all components by Count bits to the right (shifting in the value of the highest bit) + template + JPH_INLINE UVec4 ArithmeticShiftRight() const; + + /// Takes the lower 4 16 bits and expands them to X, Y, Z and W + JPH_INLINE UVec4 Expand4Uint16Lo() const; + + /// Takes the upper 4 16 bits and expands them to X, Y, Z and W + JPH_INLINE UVec4 Expand4Uint16Hi() const; + + /// Takes byte 0 .. 3 and expands them to X, Y, Z and W + JPH_INLINE UVec4 Expand4Byte0() const; + + /// Takes byte 4 .. 7 and expands them to X, Y, Z and W + JPH_INLINE UVec4 Expand4Byte4() const; + + /// Takes byte 8 .. 11 and expands them to X, Y, Z and W + JPH_INLINE UVec4 Expand4Byte8() const; + + /// Takes byte 12 .. 15 and expands them to X, Y, Z and W + JPH_INLINE UVec4 Expand4Byte12() const; + + /// Shift vector components by 4 - Count floats to the left, so if Count = 1 the resulting vector is (W, 0, 0, 0), when Count = 3 the resulting vector is (Y, Z, W, 0) + JPH_INLINE UVec4 ShiftComponents4Minus(int inCount) const; + + /// To String + friend ostream & operator << (ostream &inStream, UVec4Arg inV) + { + inStream << inV.mU32[0] << ", " << inV.mU32[1] << ", " << inV.mU32[2] << ", " << inV.mU32[3]; + return inStream; + } + + union + { + Type mValue; + uint32 mU32[4]; + }; +}; + +static_assert(is_trivial(), "Is supposed to be a trivial type!"); + +JPH_NAMESPACE_END + +#include "UVec4.inl" diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/UVec4.inl b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/UVec4.inl new file mode 100644 index 00000000000..e01d66b11cd --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/UVec4.inl @@ -0,0 +1,573 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +JPH_NAMESPACE_BEGIN + +UVec4::UVec4(uint32 inX, uint32 inY, uint32 inZ, uint32 inW) +{ +#if defined(JPH_USE_SSE) + mValue = _mm_set_epi32(int(inW), int(inZ), int(inY), int(inX)); +#elif defined(JPH_USE_NEON) + uint32x2_t xy = vcreate_u32(static_cast(inX) | (static_cast(inY) << 32)); + uint32x2_t zw = vcreate_u32(static_cast(inZ) | (static_cast(inW) << 32)); + mValue = vcombine_u32(xy, zw); +#else + mU32[0] = inX; + mU32[1] = inY; + mU32[2] = inZ; + mU32[3] = inW; +#endif +} + +bool UVec4::operator == (UVec4Arg inV2) const +{ + return sEquals(*this, inV2).TestAllTrue(); +} + +template +UVec4 UVec4::Swizzle() const +{ + static_assert(SwizzleX <= 3, "SwizzleX template parameter out of range"); + static_assert(SwizzleY <= 3, "SwizzleY template parameter out of range"); + static_assert(SwizzleZ <= 3, "SwizzleZ template parameter out of range"); + static_assert(SwizzleW <= 3, "SwizzleW template parameter out of range"); + +#if defined(JPH_USE_SSE) + return _mm_shuffle_epi32(mValue, _MM_SHUFFLE(SwizzleW, SwizzleZ, SwizzleY, SwizzleX)); +#elif defined(JPH_USE_NEON) + return JPH_NEON_SHUFFLE_F32x4(mValue, mValue, SwizzleX, SwizzleY, SwizzleZ, SwizzleW); +#else + return UVec4(mU32[SwizzleX], mU32[SwizzleY], mU32[SwizzleZ], mU32[SwizzleW]); +#endif +} + +UVec4 UVec4::sZero() +{ +#if defined(JPH_USE_SSE) + return _mm_setzero_si128(); +#elif defined(JPH_USE_NEON) + return vdupq_n_u32(0); +#else + return UVec4(0, 0, 0, 0); +#endif +} + +UVec4 UVec4::sReplicate(uint32 inV) +{ +#if defined(JPH_USE_SSE) + return _mm_set1_epi32(int(inV)); +#elif defined(JPH_USE_NEON) + return vdupq_n_u32(inV); +#else + return UVec4(inV, inV, inV, inV); +#endif +} + +UVec4 UVec4::sLoadInt(const uint32 *inV) +{ +#if defined(JPH_USE_SSE) + return _mm_castps_si128(_mm_load_ss(reinterpret_cast(inV))); +#elif defined(JPH_USE_NEON) + return vsetq_lane_u32(*inV, vdupq_n_u32(0), 0); +#else + return UVec4(*inV, 0, 0, 0); +#endif +} + +UVec4 UVec4::sLoadInt4(const uint32 *inV) +{ +#if defined(JPH_USE_SSE) + return _mm_loadu_si128(reinterpret_cast(inV)); +#elif defined(JPH_USE_NEON) + return vld1q_u32(inV); +#else + return UVec4(inV[0], inV[1], inV[2], inV[3]); +#endif +} + +UVec4 UVec4::sLoadInt4Aligned(const uint32 *inV) +{ +#if defined(JPH_USE_SSE) + return _mm_load_si128(reinterpret_cast(inV)); +#elif defined(JPH_USE_NEON) + return vld1q_u32(inV); // ARM doesn't make distinction between aligned or not +#else + return UVec4(inV[0], inV[1], inV[2], inV[3]); +#endif +} + +template +UVec4 UVec4::sGatherInt4(const uint32 *inBase, UVec4Arg inOffsets) +{ +#ifdef JPH_USE_AVX2 + return _mm_i32gather_epi32(reinterpret_cast(inBase), inOffsets.mValue, Scale); +#else + return Vec4::sGatherFloat4(reinterpret_cast(inBase), inOffsets).ReinterpretAsInt(); +#endif +} + +UVec4 UVec4::sMin(UVec4Arg inV1, UVec4Arg inV2) +{ +#if defined(JPH_USE_SSE4_1) + return _mm_min_epu32(inV1.mValue, inV2.mValue); +#elif defined(JPH_USE_NEON) + return vminq_u32(inV1.mValue, inV2.mValue); +#else + UVec4 result; + for (int i = 0; i < 4; i++) + result.mU32[i] = min(inV1.mU32[i], inV2.mU32[i]); + return result; +#endif +} + +UVec4 UVec4::sMax(UVec4Arg inV1, UVec4Arg inV2) +{ +#if defined(JPH_USE_SSE4_1) + return _mm_max_epu32(inV1.mValue, inV2.mValue); +#elif defined(JPH_USE_NEON) + return vmaxq_u32(inV1.mValue, inV2.mValue); +#else + UVec4 result; + for (int i = 0; i < 4; i++) + result.mU32[i] = max(inV1.mU32[i], inV2.mU32[i]); + return result; +#endif +} + +UVec4 UVec4::sEquals(UVec4Arg inV1, UVec4Arg inV2) +{ +#if defined(JPH_USE_SSE) + return _mm_cmpeq_epi32(inV1.mValue, inV2.mValue); +#elif defined(JPH_USE_NEON) + return vceqq_u32(inV1.mValue, inV2.mValue); +#else + return UVec4(inV1.mU32[0] == inV2.mU32[0]? 0xffffffffu : 0, + inV1.mU32[1] == inV2.mU32[1]? 0xffffffffu : 0, + inV1.mU32[2] == inV2.mU32[2]? 0xffffffffu : 0, + inV1.mU32[3] == inV2.mU32[3]? 0xffffffffu : 0); +#endif +} + +UVec4 UVec4::sSelect(UVec4Arg inV1, UVec4Arg inV2, UVec4Arg inControl) +{ +#if defined(JPH_USE_SSE4_1) + return _mm_castps_si128(_mm_blendv_ps(_mm_castsi128_ps(inV1.mValue), _mm_castsi128_ps(inV2.mValue), _mm_castsi128_ps(inControl.mValue))); +#elif defined(JPH_USE_NEON) + return vbslq_u32(vshrq_n_s32(inControl.mValue, 31), inV2.mValue, inV1.mValue); +#else + UVec4 result; + for (int i = 0; i < 4; i++) + result.mU32[i] = inControl.mU32[i] ? inV2.mU32[i] : inV1.mU32[i]; + return result; +#endif +} + +UVec4 UVec4::sOr(UVec4Arg inV1, UVec4Arg inV2) +{ +#if defined(JPH_USE_SSE) + return _mm_or_si128(inV1.mValue, inV2.mValue); +#elif defined(JPH_USE_NEON) + return vorrq_u32(inV1.mValue, inV2.mValue); +#else + return UVec4(inV1.mU32[0] | inV2.mU32[0], + inV1.mU32[1] | inV2.mU32[1], + inV1.mU32[2] | inV2.mU32[2], + inV1.mU32[3] | inV2.mU32[3]); +#endif +} + +UVec4 UVec4::sXor(UVec4Arg inV1, UVec4Arg inV2) +{ +#if defined(JPH_USE_SSE) + return _mm_xor_si128(inV1.mValue, inV2.mValue); +#elif defined(JPH_USE_NEON) + return veorq_u32(inV1.mValue, inV2.mValue); +#else + return UVec4(inV1.mU32[0] ^ inV2.mU32[0], + inV1.mU32[1] ^ inV2.mU32[1], + inV1.mU32[2] ^ inV2.mU32[2], + inV1.mU32[3] ^ inV2.mU32[3]); +#endif +} + +UVec4 UVec4::sAnd(UVec4Arg inV1, UVec4Arg inV2) +{ +#if defined(JPH_USE_SSE) + return _mm_and_si128(inV1.mValue, inV2.mValue); +#elif defined(JPH_USE_NEON) + return vandq_u32(inV1.mValue, inV2.mValue); +#else + return UVec4(inV1.mU32[0] & inV2.mU32[0], + inV1.mU32[1] & inV2.mU32[1], + inV1.mU32[2] & inV2.mU32[2], + inV1.mU32[3] & inV2.mU32[3]); +#endif +} + + +UVec4 UVec4::sNot(UVec4Arg inV1) +{ +#if defined(JPH_USE_AVX512) + return _mm_ternarylogic_epi32(inV1.mValue, inV1.mValue, inV1.mValue, 0b01010101); +#elif defined(JPH_USE_SSE) + return sXor(inV1, sReplicate(0xffffffff)); +#elif defined(JPH_USE_NEON) + return vmvnq_u32(inV1.mValue); +#else + return UVec4(~inV1.mU32[0], ~inV1.mU32[1], ~inV1.mU32[2], ~inV1.mU32[3]); +#endif +} + +UVec4 UVec4::sSort4True(UVec4Arg inValue, UVec4Arg inIndex) +{ + // If inValue.z is false then shift W to Z + UVec4 v = UVec4::sSelect(inIndex.Swizzle(), inIndex, inValue.SplatZ()); + + // If inValue.y is false then shift Z and further to Y and further + v = UVec4::sSelect(v.Swizzle(), v, inValue.SplatY()); + + // If inValue.x is false then shift X and further to Y and further + v = UVec4::sSelect(v.Swizzle(), v, inValue.SplatX()); + + return v; +} + +UVec4 UVec4::operator * (UVec4Arg inV2) const +{ +#if defined(JPH_USE_SSE4_1) + return _mm_mullo_epi32(mValue, inV2.mValue); +#elif defined(JPH_USE_NEON) + return vmulq_u32(mValue, inV2.mValue); +#else + UVec4 result; + for (int i = 0; i < 4; i++) + result.mU32[i] = mU32[i] * inV2.mU32[i]; + return result; +#endif +} + +UVec4 UVec4::operator + (UVec4Arg inV2) +{ +#if defined(JPH_USE_SSE) + return _mm_add_epi32(mValue, inV2.mValue); +#elif defined(JPH_USE_NEON) + return vaddq_u32(mValue, inV2.mValue); +#else + return UVec4(mU32[0] + inV2.mU32[0], + mU32[1] + inV2.mU32[1], + mU32[2] + inV2.mU32[2], + mU32[3] + inV2.mU32[3]); +#endif +} + +UVec4 &UVec4::operator += (UVec4Arg inV2) +{ +#if defined(JPH_USE_SSE) + mValue = _mm_add_epi32(mValue, inV2.mValue); +#elif defined(JPH_USE_NEON) + mValue = vaddq_u32(mValue, inV2.mValue); +#else + for (int i = 0; i < 4; ++i) + mU32[i] += inV2.mU32[i]; +#endif + return *this; +} + +UVec4 UVec4::SplatX() const +{ +#if defined(JPH_USE_SSE) + return _mm_shuffle_epi32(mValue, _MM_SHUFFLE(0, 0, 0, 0)); +#elif defined(JPH_USE_NEON) + return vdupq_laneq_u32(mValue, 0); +#else + return UVec4(mU32[0], mU32[0], mU32[0], mU32[0]); +#endif +} + +UVec4 UVec4::SplatY() const +{ +#if defined(JPH_USE_SSE) + return _mm_shuffle_epi32(mValue, _MM_SHUFFLE(1, 1, 1, 1)); +#elif defined(JPH_USE_NEON) + return vdupq_laneq_u32(mValue, 1); +#else + return UVec4(mU32[1], mU32[1], mU32[1], mU32[1]); +#endif +} + +UVec4 UVec4::SplatZ() const +{ +#if defined(JPH_USE_SSE) + return _mm_shuffle_epi32(mValue, _MM_SHUFFLE(2, 2, 2, 2)); +#elif defined(JPH_USE_NEON) + return vdupq_laneq_u32(mValue, 2); +#else + return UVec4(mU32[2], mU32[2], mU32[2], mU32[2]); +#endif +} + +UVec4 UVec4::SplatW() const +{ +#if defined(JPH_USE_SSE) + return _mm_shuffle_epi32(mValue, _MM_SHUFFLE(3, 3, 3, 3)); +#elif defined(JPH_USE_NEON) + return vdupq_laneq_u32(mValue, 3); +#else + return UVec4(mU32[3], mU32[3], mU32[3], mU32[3]); +#endif +} + +Vec4 UVec4::ToFloat() const +{ +#if defined(JPH_USE_SSE) + return _mm_cvtepi32_ps(mValue); +#elif defined(JPH_USE_NEON) + return vcvtq_f32_s32(mValue); +#else + return Vec4((float)mU32[0], (float)mU32[1], (float)mU32[2], (float)mU32[3]); +#endif +} + +Vec4 UVec4::ReinterpretAsFloat() const +{ +#if defined(JPH_USE_SSE) + return Vec4(_mm_castsi128_ps(mValue)); +#elif defined(JPH_USE_NEON) + return vreinterpretq_f32_s32(mValue); +#else + return *reinterpret_cast(this); +#endif +} + +void UVec4::StoreInt4(uint32 *outV) const +{ +#if defined(JPH_USE_SSE) + _mm_storeu_si128(reinterpret_cast<__m128i *>(outV), mValue); +#elif defined(JPH_USE_NEON) + vst1q_u32(outV, mValue); +#else + for (int i = 0; i < 4; ++i) + outV[i] = mU32[i]; +#endif +} + +void UVec4::StoreInt4Aligned(uint32 *outV) const +{ +#if defined(JPH_USE_SSE) + _mm_store_si128(reinterpret_cast<__m128i *>(outV), mValue); +#elif defined(JPH_USE_NEON) + vst1q_u32(outV, mValue); // ARM doesn't make distinction between aligned or not +#else + for (int i = 0; i < 4; ++i) + outV[i] = mU32[i]; +#endif +} + +int UVec4::CountTrues() const +{ +#if defined(JPH_USE_SSE) + return CountBits(_mm_movemask_ps(_mm_castsi128_ps(mValue))); +#elif defined(JPH_USE_NEON) + return vaddvq_u32(vshrq_n_u32(mValue, 31)); +#else + return (mU32[0] >> 31) + (mU32[1] >> 31) + (mU32[2] >> 31) + (mU32[3] >> 31); +#endif +} + +int UVec4::GetTrues() const +{ +#if defined(JPH_USE_SSE) + return _mm_movemask_ps(_mm_castsi128_ps(mValue)); +#elif defined(JPH_USE_NEON) + int32x4_t shift = JPH_NEON_INT32x4(0, 1, 2, 3); + return vaddvq_u32(vshlq_u32(vshrq_n_u32(mValue, 31), shift)); +#else + return (mU32[0] >> 31) | ((mU32[1] >> 31) << 1) | ((mU32[2] >> 31) << 2) | ((mU32[3] >> 31) << 3); +#endif +} + +bool UVec4::TestAnyTrue() const +{ + return GetTrues() != 0; +} + +bool UVec4::TestAnyXYZTrue() const +{ + return (GetTrues() & 0b111) != 0; +} + +bool UVec4::TestAllTrue() const +{ + return GetTrues() == 0b1111; +} + +bool UVec4::TestAllXYZTrue() const +{ + return (GetTrues() & 0b111) == 0b111; +} + +template +UVec4 UVec4::LogicalShiftLeft() const +{ + static_assert(Count <= 31, "Invalid shift"); + +#if defined(JPH_USE_SSE) + return _mm_slli_epi32(mValue, Count); +#elif defined(JPH_USE_NEON) + return vshlq_n_u32(mValue, Count); +#else + return UVec4(mU32[0] << Count, mU32[1] << Count, mU32[2] << Count, mU32[3] << Count); +#endif +} + +template +UVec4 UVec4::LogicalShiftRight() const +{ + static_assert(Count <= 31, "Invalid shift"); + +#if defined(JPH_USE_SSE) + return _mm_srli_epi32(mValue, Count); +#elif defined(JPH_USE_NEON) + return vshrq_n_u32(mValue, Count); +#else + return UVec4(mU32[0] >> Count, mU32[1] >> Count, mU32[2] >> Count, mU32[3] >> Count); +#endif +} + +template +UVec4 UVec4::ArithmeticShiftRight() const +{ + static_assert(Count <= 31, "Invalid shift"); + +#if defined(JPH_USE_SSE) + return _mm_srai_epi32(mValue, Count); +#elif defined(JPH_USE_NEON) + return vshrq_n_s32(mValue, Count); +#else + return UVec4(uint32(int32_t(mU32[0]) >> Count), + uint32(int32_t(mU32[1]) >> Count), + uint32(int32_t(mU32[2]) >> Count), + uint32(int32_t(mU32[3]) >> Count)); +#endif +} + +UVec4 UVec4::Expand4Uint16Lo() const +{ +#if defined(JPH_USE_SSE) + return _mm_unpacklo_epi16(mValue, _mm_castps_si128(_mm_setzero_ps())); +#elif defined(JPH_USE_NEON) + int16x4_t value = vget_low_s16(mValue); + int16x4_t zero = vdup_n_s16(0); + return vcombine_s16(vzip1_s16(value, zero), vzip2_s16(value, zero)); +#else + return UVec4(mU32[0] & 0xffff, + (mU32[0] >> 16) & 0xffff, + mU32[1] & 0xffff, + (mU32[1] >> 16) & 0xffff); +#endif +} + +UVec4 UVec4::Expand4Uint16Hi() const +{ +#if defined(JPH_USE_SSE) + return _mm_unpackhi_epi16(mValue, _mm_castps_si128(_mm_setzero_ps())); +#elif defined(JPH_USE_NEON) + int16x4_t value = vget_high_s16(mValue); + int16x4_t zero = vdup_n_s16(0); + return vcombine_s16(vzip1_s16(value, zero), vzip2_s16(value, zero)); +#else + return UVec4(mU32[2] & 0xffff, + (mU32[2] >> 16) & 0xffff, + mU32[3] & 0xffff, + (mU32[3] >> 16) & 0xffff); +#endif +} + +UVec4 UVec4::Expand4Byte0() const +{ +#if defined(JPH_USE_SSE4_1) + return _mm_shuffle_epi8(mValue, _mm_set_epi32(int(0xffffff03), int(0xffffff02), int(0xffffff01), int(0xffffff00))); +#elif defined(JPH_USE_NEON) + int8x16_t idx = JPH_NEON_INT8x16(0x00, 0x7f, 0x7f, 0x7f, 0x01, 0x7f, 0x7f, 0x7f, 0x02, 0x7f, 0x7f, 0x7f, 0x03, 0x7f, 0x7f, 0x7f); + return vreinterpretq_u32_s8(vqtbl1q_s8(vreinterpretq_s8_u32(mValue), idx)); +#else + UVec4 result; + for (int i = 0; i < 4; i++) + result.mU32[i] = (mU32[0] >> (i * 8)) & 0xff; + return result; +#endif +} + +UVec4 UVec4::Expand4Byte4() const +{ +#if defined(JPH_USE_SSE4_1) + return _mm_shuffle_epi8(mValue, _mm_set_epi32(int(0xffffff07), int(0xffffff06), int(0xffffff05), int(0xffffff04))); +#elif defined(JPH_USE_NEON) + int8x16_t idx = JPH_NEON_INT8x16(0x04, 0x7f, 0x7f, 0x7f, 0x05, 0x7f, 0x7f, 0x7f, 0x06, 0x7f, 0x7f, 0x7f, 0x07, 0x7f, 0x7f, 0x7f); + return vreinterpretq_u32_s8(vqtbl1q_s8(vreinterpretq_s8_u32(mValue), idx)); +#else + UVec4 result; + for (int i = 0; i < 4; i++) + result.mU32[i] = (mU32[1] >> (i * 8)) & 0xff; + return result; +#endif +} + +UVec4 UVec4::Expand4Byte8() const +{ +#if defined(JPH_USE_SSE4_1) + return _mm_shuffle_epi8(mValue, _mm_set_epi32(int(0xffffff0b), int(0xffffff0a), int(0xffffff09), int(0xffffff08))); +#elif defined(JPH_USE_NEON) + int8x16_t idx = JPH_NEON_INT8x16(0x08, 0x7f, 0x7f, 0x7f, 0x09, 0x7f, 0x7f, 0x7f, 0x0a, 0x7f, 0x7f, 0x7f, 0x0b, 0x7f, 0x7f, 0x7f); + return vreinterpretq_u32_s8(vqtbl1q_s8(vreinterpretq_s8_u32(mValue), idx)); +#else + UVec4 result; + for (int i = 0; i < 4; i++) + result.mU32[i] = (mU32[2] >> (i * 8)) & 0xff; + return result; +#endif +} + +UVec4 UVec4::Expand4Byte12() const +{ +#if defined(JPH_USE_SSE4_1) + return _mm_shuffle_epi8(mValue, _mm_set_epi32(int(0xffffff0f), int(0xffffff0e), int(0xffffff0d), int(0xffffff0c))); +#elif defined(JPH_USE_NEON) + int8x16_t idx = JPH_NEON_INT8x16(0x0c, 0x7f, 0x7f, 0x7f, 0x0d, 0x7f, 0x7f, 0x7f, 0x0e, 0x7f, 0x7f, 0x7f, 0x0f, 0x7f, 0x7f, 0x7f); + return vreinterpretq_u32_s8(vqtbl1q_s8(vreinterpretq_s8_u32(mValue), idx)); +#else + UVec4 result; + for (int i = 0; i < 4; i++) + result.mU32[i] = (mU32[3] >> (i * 8)) & 0xff; + return result; +#endif +} + +UVec4 UVec4::ShiftComponents4Minus(int inCount) const +{ +#if defined(JPH_USE_SSE4_1) || defined(JPH_USE_NEON) + alignas(UVec4) static constexpr uint32 sFourMinusXShuffle[5][4] = + { + { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }, + { 0x0f0e0d0c, 0xffffffff, 0xffffffff, 0xffffffff }, + { 0x0b0a0908, 0x0f0e0d0c, 0xffffffff, 0xffffffff }, + { 0x07060504, 0x0b0a0908, 0x0f0e0d0c, 0xffffffff }, + { 0x03020100, 0x07060504, 0x0b0a0908, 0x0f0e0d0c } + }; +#endif + +#if defined(JPH_USE_SSE4_1) + return _mm_shuffle_epi8(mValue, *reinterpret_cast(sFourMinusXShuffle[inCount])); +#elif defined(JPH_USE_NEON) + uint8x16_t idx = vreinterpretq_u8_u32(*reinterpret_cast(sFourMinusXShuffle[inCount])); + return vreinterpretq_u32_s8(vqtbl1q_s8(vreinterpretq_s8_u32(mValue), idx)); +#else + UVec4 result = UVec4::sZero(); + for (int i = 0; i < inCount; i++) + result.mU32[i] = mU32[i + 4 - inCount]; + return result; +#endif +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/UVec8.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/UVec8.h new file mode 100644 index 00000000000..bab31cbeb00 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/UVec8.h @@ -0,0 +1,100 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +class [[nodiscard]] UVec8 +{ +public: + JPH_OVERRIDE_NEW_DELETE + + UVec8() = default; ///< Intentionally not initialized for performance reasons + UVec8(const UVec8 &inRHS) = default; + JPH_INLINE UVec8(__m256i inRHS) : mValue(inRHS) { } + + /// Set 256 bit vector from 2 128 bit vectors + JPH_INLINE UVec8(UVec4Arg inLo, UVec4Arg inHi); + + /// Comparison + JPH_INLINE bool operator == (UVec8Arg inV2) const; + JPH_INLINE bool operator != (UVec8Arg inV2) const { return !(*this == inV2); } + + /// Replicate int across all components + static JPH_INLINE UVec8 sReplicate(uint32 inV); + + /// Replicate the X component of inV to all components + static JPH_INLINE UVec8 sSplatX(UVec4Arg inV); + + /// Replicate the Y component of inV to all components + static JPH_INLINE UVec8 sSplatY(UVec4Arg inV); + + /// Replicate the Z component of inV to all components + static JPH_INLINE UVec8 sSplatZ(UVec4Arg inV); + + /// Equals (component wise) + static JPH_INLINE UVec8 sEquals(UVec8Arg inV1, UVec8Arg inV2); + + /// Component wise select, returns inV1 when highest bit of inControl = 0 and inV2 when highest bit of inControl = 1 + static JPH_INLINE UVec8 sSelect(UVec8Arg inV1, UVec8Arg inV2, UVec8Arg inControl); + + /// Logical or + static JPH_INLINE UVec8 sOr(UVec8Arg inV1, UVec8Arg inV2); + + /// Logical xor + static JPH_INLINE UVec8 sXor(UVec8Arg inV1, UVec8Arg inV2); + + /// Logical and + static JPH_INLINE UVec8 sAnd(UVec8Arg inV1, UVec8Arg inV2); + + /// Get float component by index + JPH_INLINE uint32 operator [] (uint inCoordinate) const { JPH_ASSERT(inCoordinate < 8); return mU32[inCoordinate]; } + JPH_INLINE uint32 & operator [] (uint inCoordinate) { JPH_ASSERT(inCoordinate < 8); return mU32[inCoordinate]; } + + /// 256 bit variant of Vec::Swizzle (no cross 128 bit lane swizzle) + template + JPH_INLINE UVec8 Swizzle() const; + + /// Test if any of the components are true (true is when highest bit of component is set) + JPH_INLINE bool TestAnyTrue() const; + + /// Test if all components are true (true is when highest bit of component is set) + JPH_INLINE bool TestAllTrue() const; + + /// Fetch the lower 128 bit from a 256 bit variable + JPH_INLINE UVec4 LowerVec4() const; + + /// Fetch the higher 128 bit from a 256 bit variable + JPH_INLINE UVec4 UpperVec4() const; + + /// Converts int to float + JPH_INLINE Vec8 ToFloat() const; + + /// Shift all components by Count bits to the left (filling with zeros from the left) + template + JPH_INLINE UVec8 LogicalShiftLeft() const; + + /// Shift all components by Count bits to the right (filling with zeros from the right) + template + JPH_INLINE UVec8 LogicalShiftRight() const; + + /// Shift all components by Count bits to the right (shifting in the value of the highest bit) + template + JPH_INLINE UVec8 ArithmeticShiftRight() const; + + union + { + __m256i mValue; + uint32 mU32[8]; + }; +}; + +static_assert(is_trivial(), "Is supposed to be a trivial type!"); + +JPH_NAMESPACE_END + +#include "UVec8.inl" diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/UVec8.inl b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/UVec8.inl new file mode 100644 index 00000000000..0d7d3117c3d --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/UVec8.inl @@ -0,0 +1,138 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +JPH_NAMESPACE_BEGIN + +UVec8::UVec8(UVec4Arg inLo, UVec4Arg inHi) : + mValue(_mm256_insertf128_si256(_mm256_castsi128_si256(inLo.mValue), inHi.mValue, 1)) +{ +} + +bool UVec8::operator == (UVec8Arg inV2) const +{ + return sEquals(*this, inV2).TestAllTrue(); +} + +UVec8 UVec8::sReplicate(uint32 inV) +{ + return _mm256_set1_epi32(int(inV)); +} + +UVec8 UVec8::sSplatX(UVec4Arg inV) +{ + return _mm256_set1_epi32(inV.GetX()); +} + +UVec8 UVec8::sSplatY(UVec4Arg inV) +{ + return _mm256_set1_epi32(inV.GetY()); +} + +UVec8 UVec8::sSplatZ(UVec4Arg inV) +{ + return _mm256_set1_epi32(inV.GetZ()); +} + +UVec8 UVec8::sEquals(UVec8Arg inV1, UVec8Arg inV2) +{ +#ifdef JPH_USE_AVX2 + return _mm256_cmpeq_epi32(inV1.mValue, inV2.mValue); +#else + return UVec8(UVec4::sEquals(inV1.LowerVec4(), inV2.LowerVec4()), UVec4::sEquals(inV1.UpperVec4(), inV2.UpperVec4())); +#endif +} + +UVec8 UVec8::sSelect(UVec8Arg inV1, UVec8Arg inV2, UVec8Arg inControl) +{ + return _mm256_castps_si256(_mm256_blendv_ps(_mm256_castsi256_ps(inV1.mValue), _mm256_castsi256_ps(inV2.mValue), _mm256_castsi256_ps(inControl.mValue))); +} + +UVec8 UVec8::sOr(UVec8Arg inV1, UVec8Arg inV2) +{ + return _mm256_castps_si256(_mm256_or_ps(_mm256_castsi256_ps(inV1.mValue), _mm256_castsi256_ps(inV2.mValue))); +} + +UVec8 UVec8::sXor(UVec8Arg inV1, UVec8Arg inV2) +{ + return _mm256_castps_si256(_mm256_xor_ps(_mm256_castsi256_ps(inV1.mValue), _mm256_castsi256_ps(inV2.mValue))); +} + +UVec8 UVec8::sAnd(UVec8Arg inV1, UVec8Arg inV2) +{ + return _mm256_castps_si256(_mm256_and_ps(_mm256_castsi256_ps(inV1.mValue), _mm256_castsi256_ps(inV2.mValue))); +} + +template +UVec8 UVec8::Swizzle() const +{ + static_assert(SwizzleX <= 3, "SwizzleX template parameter out of range"); + static_assert(SwizzleY <= 3, "SwizzleY template parameter out of range"); + static_assert(SwizzleZ <= 3, "SwizzleZ template parameter out of range"); + static_assert(SwizzleW <= 3, "SwizzleW template parameter out of range"); + + return _mm256_castps_si256(_mm256_shuffle_ps(_mm256_castsi256_ps(mValue), _mm256_castsi256_ps(mValue), _MM_SHUFFLE(SwizzleW, SwizzleZ, SwizzleY, SwizzleX))); +} + +bool UVec8::TestAnyTrue() const +{ + return _mm256_movemask_ps(_mm256_castsi256_ps(mValue)) != 0; +} + +bool UVec8::TestAllTrue() const +{ + return _mm256_movemask_ps(_mm256_castsi256_ps(mValue)) == 0xff; +} + +UVec4 UVec8::LowerVec4() const +{ + return _mm256_castsi256_si128(mValue); +} + +UVec4 UVec8::UpperVec4() const +{ + return _mm_castps_si128(_mm256_extractf128_ps(_mm256_castsi256_ps(mValue), 1)); +} + +Vec8 UVec8::ToFloat() const +{ + return _mm256_cvtepi32_ps(mValue); +} + +template +UVec8 UVec8::LogicalShiftLeft() const +{ + static_assert(Count <= 31, "Invalid shift"); + +#ifdef JPH_USE_AVX2 + return _mm256_slli_epi32(mValue, Count); +#else + return UVec8(LowerVec4().LogicalShiftLeft(), UpperVec4().LogicalShiftLeft()); +#endif +} + +template +UVec8 UVec8::LogicalShiftRight() const +{ + static_assert(Count <= 31, "Invalid shift"); + +#ifdef JPH_USE_AVX2 + return _mm256_srli_epi32(mValue, Count); +#else + return UVec8(LowerVec4().LogicalShiftRight(), UpperVec4().LogicalShiftRight()); +#endif +} + +template +UVec8 UVec8::ArithmeticShiftRight() const +{ + static_assert(Count <= 31, "Invalid shift"); + +#ifdef JPH_USE_AVX2 + return _mm256_srai_epi32(mValue, Count); +#else + return UVec8(LowerVec4().ArithmeticShiftRight(), UpperVec4().ArithmeticShiftRight()); +#endif +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Vec3.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Vec3.cpp new file mode 100644 index 00000000000..31283d3250d --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Vec3.cpp @@ -0,0 +1,59 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include + +JPH_NAMESPACE_BEGIN + +static void sCreateVertices(std::unordered_set &ioVertices, Vec3Arg inDir1, Vec3Arg inDir2, Vec3Arg inDir3, int inLevel) +{ + Vec3 center1 = (inDir1 + inDir2).Normalized(); + Vec3 center2 = (inDir2 + inDir3).Normalized(); + Vec3 center3 = (inDir3 + inDir1).Normalized(); + + ioVertices.insert(center1); + ioVertices.insert(center2); + ioVertices.insert(center3); + + if (inLevel > 0) + { + int new_level = inLevel - 1; + sCreateVertices(ioVertices, inDir1, center1, center3, new_level); + sCreateVertices(ioVertices, center1, center2, center3, new_level); + sCreateVertices(ioVertices, center1, inDir2, center2, new_level); + sCreateVertices(ioVertices, center3, center2, inDir3, new_level); + } +} + +const std::vector Vec3::sUnitSphere = []() { + + const int level = 3; + + std::unordered_set verts; + + // Add unit axis + verts.insert(Vec3::sAxisX()); + verts.insert(-Vec3::sAxisX()); + verts.insert(Vec3::sAxisY()); + verts.insert(-Vec3::sAxisY()); + verts.insert(Vec3::sAxisZ()); + verts.insert(-Vec3::sAxisZ()); + + // Subdivide + sCreateVertices(verts, Vec3::sAxisX(), Vec3::sAxisY(), Vec3::sAxisZ(), level); + sCreateVertices(verts, -Vec3::sAxisX(), Vec3::sAxisY(), Vec3::sAxisZ(), level); + sCreateVertices(verts, Vec3::sAxisX(), -Vec3::sAxisY(), Vec3::sAxisZ(), level); + sCreateVertices(verts, -Vec3::sAxisX(), -Vec3::sAxisY(), Vec3::sAxisZ(), level); + sCreateVertices(verts, Vec3::sAxisX(), Vec3::sAxisY(), -Vec3::sAxisZ(), level); + sCreateVertices(verts, -Vec3::sAxisX(), Vec3::sAxisY(), -Vec3::sAxisZ(), level); + sCreateVertices(verts, Vec3::sAxisX(), -Vec3::sAxisY(), -Vec3::sAxisZ(), level); + sCreateVertices(verts, -Vec3::sAxisX(), -Vec3::sAxisY(), -Vec3::sAxisZ(), level); + + return std::vector(verts.begin(), verts.end()); +}(); + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Vec3.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Vec3.h new file mode 100644 index 00000000000..5ca71896542 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Vec3.h @@ -0,0 +1,294 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +/// 3 component vector (stored as 4 vectors). +/// Note that we keep the 4th component the same as the 3rd component to avoid divisions by zero when JPH_FLOATING_POINT_EXCEPTIONS_ENABLED defined +class [[nodiscard]] alignas(JPH_VECTOR_ALIGNMENT) Vec3 +{ +public: + JPH_OVERRIDE_NEW_DELETE + + // Underlying vector type +#if defined(JPH_USE_SSE) + using Type = __m128; +#elif defined(JPH_USE_NEON) + using Type = float32x4_t; +#else + using Type = Vec4::Type; +#endif + + // Argument type + using ArgType = Vec3Arg; + + /// Constructor + Vec3() = default; ///< Intentionally not initialized for performance reasons + Vec3(const Vec3 &inRHS) = default; + Vec3 & operator = (const Vec3 &inRHS) = default; + explicit JPH_INLINE Vec3(Vec4Arg inRHS); + JPH_INLINE Vec3(Type inRHS) : mValue(inRHS) { CheckW(); } + + /// Load 3 floats from memory + explicit JPH_INLINE Vec3(const Float3 &inV); + + /// Create a vector from 3 components + JPH_INLINE Vec3(float inX, float inY, float inZ); + + /// Vector with all zeros + static JPH_INLINE Vec3 sZero(); + + /// Vector with all NaN's + static JPH_INLINE Vec3 sNaN(); + + /// Vectors with the principal axis + static JPH_INLINE Vec3 sAxisX() { return Vec3(1, 0, 0); } + static JPH_INLINE Vec3 sAxisY() { return Vec3(0, 1, 0); } + static JPH_INLINE Vec3 sAxisZ() { return Vec3(0, 0, 1); } + + /// Replicate inV across all components + static JPH_INLINE Vec3 sReplicate(float inV); + + /// Load 3 floats from memory (reads 32 bits extra which it doesn't use) + static JPH_INLINE Vec3 sLoadFloat3Unsafe(const Float3 &inV); + + /// Return the minimum value of each of the components + static JPH_INLINE Vec3 sMin(Vec3Arg inV1, Vec3Arg inV2); + + /// Return the maximum of each of the components + static JPH_INLINE Vec3 sMax(Vec3Arg inV1, Vec3Arg inV2); + + /// Clamp a vector between min and max (component wise) + static JPH_INLINE Vec3 sClamp(Vec3Arg inV, Vec3Arg inMin, Vec3Arg inMax); + + /// Equals (component wise) + static JPH_INLINE UVec4 sEquals(Vec3Arg inV1, Vec3Arg inV2); + + /// Less than (component wise) + static JPH_INLINE UVec4 sLess(Vec3Arg inV1, Vec3Arg inV2); + + /// Less than or equal (component wise) + static JPH_INLINE UVec4 sLessOrEqual(Vec3Arg inV1, Vec3Arg inV2); + + /// Greater than (component wise) + static JPH_INLINE UVec4 sGreater(Vec3Arg inV1, Vec3Arg inV2); + + /// Greater than or equal (component wise) + static JPH_INLINE UVec4 sGreaterOrEqual(Vec3Arg inV1, Vec3Arg inV2); + + /// Calculates inMul1 * inMul2 + inAdd + static JPH_INLINE Vec3 sFusedMultiplyAdd(Vec3Arg inMul1, Vec3Arg inMul2, Vec3Arg inAdd); + + /// Component wise select, returns inV1 when highest bit of inControl = 0 and inV2 when highest bit of inControl = 1 + static JPH_INLINE Vec3 sSelect(Vec3Arg inV1, Vec3Arg inV2, UVec4Arg inControl); + + /// Logical or (component wise) + static JPH_INLINE Vec3 sOr(Vec3Arg inV1, Vec3Arg inV2); + + /// Logical xor (component wise) + static JPH_INLINE Vec3 sXor(Vec3Arg inV1, Vec3Arg inV2); + + /// Logical and (component wise) + static JPH_INLINE Vec3 sAnd(Vec3Arg inV1, Vec3Arg inV2); + + /// Get unit vector given spherical coordinates + /// inTheta \f$\in [0, \pi]\f$ is angle between vector and z-axis + /// inPhi \f$\in [0, 2 \pi]\f$ is the angle in the xy-plane starting from the x axis and rotating counter clockwise around the z-axis + static JPH_INLINE Vec3 sUnitSpherical(float inTheta, float inPhi); + + /// A set of vectors uniformly spanning the surface of a unit sphere, usable for debug purposes + JPH_EXPORT static const std::vector sUnitSphere; + + /// Get random unit vector + template + static inline Vec3 sRandom(Random &inRandom); + + /// Get individual components +#if defined(JPH_USE_SSE) + JPH_INLINE float GetX() const { return _mm_cvtss_f32(mValue); } + JPH_INLINE float GetY() const { return mF32[1]; } + JPH_INLINE float GetZ() const { return mF32[2]; } +#elif defined(JPH_USE_NEON) + JPH_INLINE float GetX() const { return vgetq_lane_f32(mValue, 0); } + JPH_INLINE float GetY() const { return vgetq_lane_f32(mValue, 1); } + JPH_INLINE float GetZ() const { return vgetq_lane_f32(mValue, 2); } +#else + JPH_INLINE float GetX() const { return mF32[0]; } + JPH_INLINE float GetY() const { return mF32[1]; } + JPH_INLINE float GetZ() const { return mF32[2]; } +#endif + + /// Set individual components + JPH_INLINE void SetX(float inX) { mF32[0] = inX; } + JPH_INLINE void SetY(float inY) { mF32[1] = inY; } + JPH_INLINE void SetZ(float inZ) { mF32[2] = mF32[3] = inZ; } // Assure Z and W are the same + + /// Set all components + JPH_INLINE void Set(float inX, float inY, float inZ) { *this = Vec3(inX, inY, inZ); } + + /// Get float component by index + JPH_INLINE float operator [] (uint inCoordinate) const { JPH_ASSERT(inCoordinate < 3); return mF32[inCoordinate]; } + + /// Set float component by index + JPH_INLINE void SetComponent(uint inCoordinate, float inValue) { JPH_ASSERT(inCoordinate < 3); mF32[inCoordinate] = inValue; mValue = sFixW(mValue); } // Assure Z and W are the same + + /// Comparison + JPH_INLINE bool operator == (Vec3Arg inV2) const; + JPH_INLINE bool operator != (Vec3Arg inV2) const { return !(*this == inV2); } + + /// Test if two vectors are close + JPH_INLINE bool IsClose(Vec3Arg inV2, float inMaxDistSq = 1.0e-12f) const; + + /// Test if vector is near zero + JPH_INLINE bool IsNearZero(float inMaxDistSq = 1.0e-12f) const; + + /// Test if vector is normalized + JPH_INLINE bool IsNormalized(float inTolerance = 1.0e-6f) const; + + /// Test if vector contains NaN elements + JPH_INLINE bool IsNaN() const; + + /// Multiply two float vectors (component wise) + JPH_INLINE Vec3 operator * (Vec3Arg inV2) const; + + /// Multiply vector with float + JPH_INLINE Vec3 operator * (float inV2) const; + + /// Multiply vector with float + friend JPH_INLINE Vec3 operator * (float inV1, Vec3Arg inV2); + + /// Divide vector by float + JPH_INLINE Vec3 operator / (float inV2) const; + + /// Multiply vector with float + JPH_INLINE Vec3 & operator *= (float inV2); + + /// Multiply vector with vector + JPH_INLINE Vec3 & operator *= (Vec3Arg inV2); + + /// Divide vector by float + JPH_INLINE Vec3 & operator /= (float inV2); + + /// Add two float vectors (component wise) + JPH_INLINE Vec3 operator + (Vec3Arg inV2) const; + + /// Add two float vectors (component wise) + JPH_INLINE Vec3 & operator += (Vec3Arg inV2); + + /// Negate + JPH_INLINE Vec3 operator - () const; + + /// Subtract two float vectors (component wise) + JPH_INLINE Vec3 operator - (Vec3Arg inV2) const; + + /// Add two float vectors (component wise) + JPH_INLINE Vec3 & operator -= (Vec3Arg inV2); + + /// Divide (component wise) + JPH_INLINE Vec3 operator / (Vec3Arg inV2) const; + + /// Swizzle the elements in inV + template + JPH_INLINE Vec3 Swizzle() const; + + /// Replicate the X component to all components + JPH_INLINE Vec4 SplatX() const; + + /// Replicate the Y component to all components + JPH_INLINE Vec4 SplatY() const; + + /// Replicate the Z component to all components + JPH_INLINE Vec4 SplatZ() const; + + /// Get index of component with lowest value + JPH_INLINE int GetLowestComponentIndex() const; + + /// Get index of component with highest value + JPH_INLINE int GetHighestComponentIndex() const; + + /// Return the absolute value of each of the components + JPH_INLINE Vec3 Abs() const; + + /// Reciprocal vector (1 / value) for each of the components + JPH_INLINE Vec3 Reciprocal() const; + + /// Cross product + JPH_INLINE Vec3 Cross(Vec3Arg inV2) const; + + /// Dot product, returns the dot product in X, Y and Z components + JPH_INLINE Vec3 DotV(Vec3Arg inV2) const; + + /// Dot product, returns the dot product in X, Y, Z and W components + JPH_INLINE Vec4 DotV4(Vec3Arg inV2) const; + + /// Dot product + JPH_INLINE float Dot(Vec3Arg inV2) const; + + /// Squared length of vector + JPH_INLINE float LengthSq() const; + + /// Length of vector + JPH_INLINE float Length() const; + + /// Normalize vector + JPH_INLINE Vec3 Normalized() const; + + /// Normalize vector or return inZeroValue if the length of the vector is zero + JPH_INLINE Vec3 NormalizedOr(Vec3Arg inZeroValue) const; + + /// Store 3 floats to memory + JPH_INLINE void StoreFloat3(Float3 *outV) const; + + /// Convert each component from a float to an int + JPH_INLINE UVec4 ToInt() const; + + /// Reinterpret Vec3 as a UVec4 (doesn't change the bits) + JPH_INLINE UVec4 ReinterpretAsInt() const; + + /// Get the minimum of X, Y and Z + JPH_INLINE float ReduceMin() const; + + /// Get the maximum of X, Y and Z + JPH_INLINE float ReduceMax() const; + + /// Component wise square root + JPH_INLINE Vec3 Sqrt() const; + + /// Get normalized vector that is perpendicular to this vector + JPH_INLINE Vec3 GetNormalizedPerpendicular() const; + + /// Get vector that contains the sign of each element (returns 1.0f if positive, -1.0f if negative) + JPH_INLINE Vec3 GetSign() const; + + /// To String + friend ostream & operator << (ostream &inStream, Vec3Arg inV) + { + inStream << inV.mF32[0] << ", " << inV.mF32[1] << ", " << inV.mF32[2]; + return inStream; + } + + /// Internal helper function that checks that W is equal to Z, so e.g. dividing by it should not generate div by 0 + JPH_INLINE void CheckW() const; + + /// Internal helper function that ensures that the Z component is replicated to the W component to prevent divisions by zero + static JPH_INLINE Type sFixW(Type inValue); + + union + { + Type mValue; + float mF32[4]; + }; +}; + +static_assert(is_trivial(), "Is supposed to be a trivial type!"); + +JPH_NAMESPACE_END + +#include "Vec3.inl" diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Vec3.inl b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Vec3.inl new file mode 100644 index 00000000000..d53f1828dc7 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Vec3.inl @@ -0,0 +1,845 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include +#include +#include + +JPH_SUPPRESS_WARNINGS_STD_BEGIN +#include +JPH_SUPPRESS_WARNINGS_STD_END + +// Create a std::hash for Vec3 +JPH_MAKE_HASHABLE(JPH::Vec3, t.GetX(), t.GetY(), t.GetZ()) + +JPH_NAMESPACE_BEGIN + +void Vec3::CheckW() const +{ +#ifdef JPH_FLOATING_POINT_EXCEPTIONS_ENABLED + // Avoid asserts when both components are NaN + JPH_ASSERT(reinterpret_cast(mF32)[2] == reinterpret_cast(mF32)[3]); +#endif // JPH_FLOATING_POINT_EXCEPTIONS_ENABLED +} + +JPH_INLINE Vec3::Type Vec3::sFixW(Type inValue) +{ +#ifdef JPH_FLOATING_POINT_EXCEPTIONS_ENABLED + #if defined(JPH_USE_SSE) + return _mm_shuffle_ps(inValue, inValue, _MM_SHUFFLE(2, 2, 1, 0)); + #elif defined(JPH_USE_NEON) + return JPH_NEON_SHUFFLE_F32x4(inValue, inValue, 0, 1, 2, 2); + #else + Type value; + value.mData[0] = inValue.mData[0]; + value.mData[1] = inValue.mData[1]; + value.mData[2] = inValue.mData[2]; + value.mData[3] = inValue.mData[2]; + return value; + #endif +#else + return inValue; +#endif // JPH_FLOATING_POINT_EXCEPTIONS_ENABLED +} + +Vec3::Vec3(Vec4Arg inRHS) : + mValue(sFixW(inRHS.mValue)) +{ +} + +Vec3::Vec3(const Float3 &inV) +{ +#if defined(JPH_USE_SSE) + Type x = _mm_load_ss(&inV.x); + Type y = _mm_load_ss(&inV.y); + Type z = _mm_load_ss(&inV.z); + Type xy = _mm_unpacklo_ps(x, y); + mValue = _mm_shuffle_ps(xy, z, _MM_SHUFFLE(0, 0, 1, 0)); // Assure Z and W are the same +#elif defined(JPH_USE_NEON) + float32x2_t xy = vld1_f32(&inV.x); + float32x2_t zz = vdup_n_f32(inV.z); // Assure Z and W are the same + mValue = vcombine_f32(xy, zz); +#else + mF32[0] = inV[0]; + mF32[1] = inV[1]; + mF32[2] = inV[2]; + #ifdef JPH_FLOATING_POINT_EXCEPTIONS_ENABLED + mF32[3] = inV[2]; + #endif +#endif +} + +Vec3::Vec3(float inX, float inY, float inZ) +{ +#if defined(JPH_USE_SSE) + mValue = _mm_set_ps(inZ, inZ, inY, inX); +#elif defined(JPH_USE_NEON) + uint32x2_t xy = vcreate_f32(static_cast(*reinterpret_cast(&inX)) | (static_cast(*reinterpret_cast(&inY)) << 32)); + uint32x2_t zz = vcreate_f32(static_cast(*reinterpret_cast(&inZ)) | (static_cast(*reinterpret_cast(&inZ)) << 32)); + mValue = vcombine_f32(xy, zz); +#else + mF32[0] = inX; + mF32[1] = inY; + mF32[2] = inZ; + #ifdef JPH_FLOATING_POINT_EXCEPTIONS_ENABLED + mF32[3] = inZ; + #endif +#endif +} + +template +Vec3 Vec3::Swizzle() const +{ + static_assert(SwizzleX <= 3, "SwizzleX template parameter out of range"); + static_assert(SwizzleY <= 3, "SwizzleY template parameter out of range"); + static_assert(SwizzleZ <= 3, "SwizzleZ template parameter out of range"); + +#if defined(JPH_USE_SSE) + return _mm_shuffle_ps(mValue, mValue, _MM_SHUFFLE(SwizzleZ, SwizzleZ, SwizzleY, SwizzleX)); // Assure Z and W are the same +#elif defined(JPH_USE_NEON) + return JPH_NEON_SHUFFLE_F32x4(mValue, mValue, SwizzleX, SwizzleY, SwizzleZ, SwizzleZ); +#else + return Vec3(mF32[SwizzleX], mF32[SwizzleY], mF32[SwizzleZ]); +#endif +} + +Vec3 Vec3::sZero() +{ +#if defined(JPH_USE_SSE) + return _mm_setzero_ps(); +#elif defined(JPH_USE_NEON) + return vdupq_n_f32(0); +#else + return Vec3(0, 0, 0); +#endif +} + +Vec3 Vec3::sReplicate(float inV) +{ +#if defined(JPH_USE_SSE) + return _mm_set1_ps(inV); +#elif defined(JPH_USE_NEON) + return vdupq_n_f32(inV); +#else + return Vec3(inV, inV, inV); +#endif +} + +Vec3 Vec3::sNaN() +{ + return sReplicate(numeric_limits::quiet_NaN()); +} + +Vec3 Vec3::sLoadFloat3Unsafe(const Float3 &inV) +{ +#if defined(JPH_USE_SSE) + Type v = _mm_loadu_ps(&inV.x); +#elif defined(JPH_USE_NEON) + Type v = vld1q_f32(&inV.x); +#else + Type v = { inV.x, inV.y, inV.z }; +#endif + return sFixW(v); +} + +Vec3 Vec3::sMin(Vec3Arg inV1, Vec3Arg inV2) +{ +#if defined(JPH_USE_SSE) + return _mm_min_ps(inV1.mValue, inV2.mValue); +#elif defined(JPH_USE_NEON) + return vminq_f32(inV1.mValue, inV2.mValue); +#else + return Vec3(min(inV1.mF32[0], inV2.mF32[0]), + min(inV1.mF32[1], inV2.mF32[1]), + min(inV1.mF32[2], inV2.mF32[2])); +#endif +} + +Vec3 Vec3::sMax(Vec3Arg inV1, Vec3Arg inV2) +{ +#if defined(JPH_USE_SSE) + return _mm_max_ps(inV1.mValue, inV2.mValue); +#elif defined(JPH_USE_NEON) + return vmaxq_f32(inV1.mValue, inV2.mValue); +#else + return Vec3(max(inV1.mF32[0], inV2.mF32[0]), + max(inV1.mF32[1], inV2.mF32[1]), + max(inV1.mF32[2], inV2.mF32[2])); +#endif +} + +Vec3 Vec3::sClamp(Vec3Arg inV, Vec3Arg inMin, Vec3Arg inMax) +{ + return sMax(sMin(inV, inMax), inMin); +} + +UVec4 Vec3::sEquals(Vec3Arg inV1, Vec3Arg inV2) +{ +#if defined(JPH_USE_SSE) + return _mm_castps_si128(_mm_cmpeq_ps(inV1.mValue, inV2.mValue)); +#elif defined(JPH_USE_NEON) + return vceqq_f32(inV1.mValue, inV2.mValue); +#else + uint32 z = inV1.mF32[2] == inV2.mF32[2]? 0xffffffffu : 0; + return UVec4(inV1.mF32[0] == inV2.mF32[0]? 0xffffffffu : 0, + inV1.mF32[1] == inV2.mF32[1]? 0xffffffffu : 0, + z, + z); +#endif +} + +UVec4 Vec3::sLess(Vec3Arg inV1, Vec3Arg inV2) +{ +#if defined(JPH_USE_SSE) + return _mm_castps_si128(_mm_cmplt_ps(inV1.mValue, inV2.mValue)); +#elif defined(JPH_USE_NEON) + return vcltq_f32(inV1.mValue, inV2.mValue); +#else + uint32 z = inV1.mF32[2] < inV2.mF32[2]? 0xffffffffu : 0; + return UVec4(inV1.mF32[0] < inV2.mF32[0]? 0xffffffffu : 0, + inV1.mF32[1] < inV2.mF32[1]? 0xffffffffu : 0, + z, + z); +#endif +} + +UVec4 Vec3::sLessOrEqual(Vec3Arg inV1, Vec3Arg inV2) +{ +#if defined(JPH_USE_SSE) + return _mm_castps_si128(_mm_cmple_ps(inV1.mValue, inV2.mValue)); +#elif defined(JPH_USE_NEON) + return vcleq_f32(inV1.mValue, inV2.mValue); +#else + uint32 z = inV1.mF32[2] <= inV2.mF32[2]? 0xffffffffu : 0; + return UVec4(inV1.mF32[0] <= inV2.mF32[0]? 0xffffffffu : 0, + inV1.mF32[1] <= inV2.mF32[1]? 0xffffffffu : 0, + z, + z); +#endif +} + +UVec4 Vec3::sGreater(Vec3Arg inV1, Vec3Arg inV2) +{ +#if defined(JPH_USE_SSE) + return _mm_castps_si128(_mm_cmpgt_ps(inV1.mValue, inV2.mValue)); +#elif defined(JPH_USE_NEON) + return vcgtq_f32(inV1.mValue, inV2.mValue); +#else + uint32 z = inV1.mF32[2] > inV2.mF32[2]? 0xffffffffu : 0; + return UVec4(inV1.mF32[0] > inV2.mF32[0]? 0xffffffffu : 0, + inV1.mF32[1] > inV2.mF32[1]? 0xffffffffu : 0, + z, + z); +#endif +} + +UVec4 Vec3::sGreaterOrEqual(Vec3Arg inV1, Vec3Arg inV2) +{ +#if defined(JPH_USE_SSE) + return _mm_castps_si128(_mm_cmpge_ps(inV1.mValue, inV2.mValue)); +#elif defined(JPH_USE_NEON) + return vcgeq_f32(inV1.mValue, inV2.mValue); +#else + uint32 z = inV1.mF32[2] >= inV2.mF32[2]? 0xffffffffu : 0; + return UVec4(inV1.mF32[0] >= inV2.mF32[0]? 0xffffffffu : 0, + inV1.mF32[1] >= inV2.mF32[1]? 0xffffffffu : 0, + z, + z); +#endif +} + +Vec3 Vec3::sFusedMultiplyAdd(Vec3Arg inMul1, Vec3Arg inMul2, Vec3Arg inAdd) +{ +#if defined(JPH_USE_SSE) + #ifdef JPH_USE_FMADD + return _mm_fmadd_ps(inMul1.mValue, inMul2.mValue, inAdd.mValue); + #else + return _mm_add_ps(_mm_mul_ps(inMul1.mValue, inMul2.mValue), inAdd.mValue); + #endif +#elif defined(JPH_USE_NEON) + return vmlaq_f32(inAdd.mValue, inMul1.mValue, inMul2.mValue); +#else + return Vec3(inMul1.mF32[0] * inMul2.mF32[0] + inAdd.mF32[0], + inMul1.mF32[1] * inMul2.mF32[1] + inAdd.mF32[1], + inMul1.mF32[2] * inMul2.mF32[2] + inAdd.mF32[2]); +#endif +} + +Vec3 Vec3::sSelect(Vec3Arg inV1, Vec3Arg inV2, UVec4Arg inControl) +{ +#if defined(JPH_USE_SSE4_1) + Type v = _mm_blendv_ps(inV1.mValue, inV2.mValue, _mm_castsi128_ps(inControl.mValue)); + return sFixW(v); +#elif defined(JPH_USE_NEON) + Type v = vbslq_f32(vshrq_n_s32(inControl.mValue, 31), inV2.mValue, inV1.mValue); + return sFixW(v); +#else + Vec3 result; + for (int i = 0; i < 3; i++) + result.mF32[i] = inControl.mU32[i] ? inV2.mF32[i] : inV1.mF32[i]; +#ifdef JPH_FLOATING_POINT_EXCEPTIONS_ENABLED + result.mF32[3] = result.mF32[2]; +#endif // JPH_FLOATING_POINT_EXCEPTIONS_ENABLED + return result; +#endif +} + +Vec3 Vec3::sOr(Vec3Arg inV1, Vec3Arg inV2) +{ +#if defined(JPH_USE_SSE) + return _mm_or_ps(inV1.mValue, inV2.mValue); +#elif defined(JPH_USE_NEON) + return vorrq_s32(inV1.mValue, inV2.mValue); +#else + return Vec3(UVec4::sOr(inV1.ReinterpretAsInt(), inV2.ReinterpretAsInt()).ReinterpretAsFloat()); +#endif +} + +Vec3 Vec3::sXor(Vec3Arg inV1, Vec3Arg inV2) +{ +#if defined(JPH_USE_SSE) + return _mm_xor_ps(inV1.mValue, inV2.mValue); +#elif defined(JPH_USE_NEON) + return veorq_s32(inV1.mValue, inV2.mValue); +#else + return Vec3(UVec4::sXor(inV1.ReinterpretAsInt(), inV2.ReinterpretAsInt()).ReinterpretAsFloat()); +#endif +} + +Vec3 Vec3::sAnd(Vec3Arg inV1, Vec3Arg inV2) +{ +#if defined(JPH_USE_SSE) + return _mm_and_ps(inV1.mValue, inV2.mValue); +#elif defined(JPH_USE_NEON) + return vandq_s32(inV1.mValue, inV2.mValue); +#else + return Vec3(UVec4::sAnd(inV1.ReinterpretAsInt(), inV2.ReinterpretAsInt()).ReinterpretAsFloat()); +#endif +} + +Vec3 Vec3::sUnitSpherical(float inTheta, float inPhi) +{ + Vec4 s, c; + Vec4(inTheta, inPhi, 0, 0).SinCos(s, c); + return Vec3(s.GetX() * c.GetY(), s.GetX() * s.GetY(), c.GetX()); +} + +template +Vec3 Vec3::sRandom(Random &inRandom) +{ + std::uniform_real_distribution zero_to_one(0.0f, 1.0f); + float theta = JPH_PI * zero_to_one(inRandom); + float phi = 2.0f * JPH_PI * zero_to_one(inRandom); + return sUnitSpherical(theta, phi); +} + +bool Vec3::operator == (Vec3Arg inV2) const +{ + return sEquals(*this, inV2).TestAllXYZTrue(); +} + +bool Vec3::IsClose(Vec3Arg inV2, float inMaxDistSq) const +{ + return (inV2 - *this).LengthSq() <= inMaxDistSq; +} + +bool Vec3::IsNearZero(float inMaxDistSq) const +{ + return LengthSq() <= inMaxDistSq; +} + +Vec3 Vec3::operator * (Vec3Arg inV2) const +{ +#if defined(JPH_USE_SSE) + return _mm_mul_ps(mValue, inV2.mValue); +#elif defined(JPH_USE_NEON) + return vmulq_f32(mValue, inV2.mValue); +#else + return Vec3(mF32[0] * inV2.mF32[0], mF32[1] * inV2.mF32[1], mF32[2] * inV2.mF32[2]); +#endif +} + +Vec3 Vec3::operator * (float inV2) const +{ +#if defined(JPH_USE_SSE) + return _mm_mul_ps(mValue, _mm_set1_ps(inV2)); +#elif defined(JPH_USE_NEON) + return vmulq_n_f32(mValue, inV2); +#else + return Vec3(mF32[0] * inV2, mF32[1] * inV2, mF32[2] * inV2); +#endif +} + +Vec3 operator * (float inV1, Vec3Arg inV2) +{ +#if defined(JPH_USE_SSE) + return _mm_mul_ps(_mm_set1_ps(inV1), inV2.mValue); +#elif defined(JPH_USE_NEON) + return vmulq_n_f32(inV2.mValue, inV1); +#else + return Vec3(inV1 * inV2.mF32[0], inV1 * inV2.mF32[1], inV1 * inV2.mF32[2]); +#endif +} + +Vec3 Vec3::operator / (float inV2) const +{ +#if defined(JPH_USE_SSE) + return _mm_div_ps(mValue, _mm_set1_ps(inV2)); +#elif defined(JPH_USE_NEON) + return vdivq_f32(mValue, vdupq_n_f32(inV2)); +#else + return Vec3(mF32[0] / inV2, mF32[1] / inV2, mF32[2] / inV2); +#endif +} + +Vec3 &Vec3::operator *= (float inV2) +{ +#if defined(JPH_USE_SSE) + mValue = _mm_mul_ps(mValue, _mm_set1_ps(inV2)); +#elif defined(JPH_USE_NEON) + mValue = vmulq_n_f32(mValue, inV2); +#else + for (int i = 0; i < 3; ++i) + mF32[i] *= inV2; + #ifdef JPH_FLOATING_POINT_EXCEPTIONS_ENABLED + mF32[3] = mF32[2]; + #endif +#endif + return *this; +} + +Vec3 &Vec3::operator *= (Vec3Arg inV2) +{ +#if defined(JPH_USE_SSE) + mValue = _mm_mul_ps(mValue, inV2.mValue); +#elif defined(JPH_USE_NEON) + mValue = vmulq_f32(mValue, inV2.mValue); +#else + for (int i = 0; i < 3; ++i) + mF32[i] *= inV2.mF32[i]; + #ifdef JPH_FLOATING_POINT_EXCEPTIONS_ENABLED + mF32[3] = mF32[2]; + #endif +#endif + return *this; +} + +Vec3 &Vec3::operator /= (float inV2) +{ +#if defined(JPH_USE_SSE) + mValue = _mm_div_ps(mValue, _mm_set1_ps(inV2)); +#elif defined(JPH_USE_NEON) + mValue = vdivq_f32(mValue, vdupq_n_f32(inV2)); +#else + for (int i = 0; i < 3; ++i) + mF32[i] /= inV2; + #ifdef JPH_FLOATING_POINT_EXCEPTIONS_ENABLED + mF32[3] = mF32[2]; + #endif +#endif + return *this; +} + +Vec3 Vec3::operator + (Vec3Arg inV2) const +{ +#if defined(JPH_USE_SSE) + return _mm_add_ps(mValue, inV2.mValue); +#elif defined(JPH_USE_NEON) + return vaddq_f32(mValue, inV2.mValue); +#else + return Vec3(mF32[0] + inV2.mF32[0], mF32[1] + inV2.mF32[1], mF32[2] + inV2.mF32[2]); +#endif +} + +Vec3 &Vec3::operator += (Vec3Arg inV2) +{ +#if defined(JPH_USE_SSE) + mValue = _mm_add_ps(mValue, inV2.mValue); +#elif defined(JPH_USE_NEON) + mValue = vaddq_f32(mValue, inV2.mValue); +#else + for (int i = 0; i < 3; ++i) + mF32[i] += inV2.mF32[i]; + #ifdef JPH_FLOATING_POINT_EXCEPTIONS_ENABLED + mF32[3] = mF32[2]; + #endif +#endif + return *this; +} + +Vec3 Vec3::operator - () const +{ +#if defined(JPH_USE_SSE) + return _mm_sub_ps(_mm_setzero_ps(), mValue); +#elif defined(JPH_USE_NEON) + return vnegq_f32(mValue); +#else + return Vec3(-mF32[0], -mF32[1], -mF32[2]); +#endif +} + +Vec3 Vec3::operator - (Vec3Arg inV2) const +{ +#if defined(JPH_USE_SSE) + return _mm_sub_ps(mValue, inV2.mValue); +#elif defined(JPH_USE_NEON) + return vsubq_f32(mValue, inV2.mValue); +#else + return Vec3(mF32[0] - inV2.mF32[0], mF32[1] - inV2.mF32[1], mF32[2] - inV2.mF32[2]); +#endif +} + +Vec3 &Vec3::operator -= (Vec3Arg inV2) +{ +#if defined(JPH_USE_SSE) + mValue = _mm_sub_ps(mValue, inV2.mValue); +#elif defined(JPH_USE_NEON) + mValue = vsubq_f32(mValue, inV2.mValue); +#else + for (int i = 0; i < 3; ++i) + mF32[i] -= inV2.mF32[i]; + #ifdef JPH_FLOATING_POINT_EXCEPTIONS_ENABLED + mF32[3] = mF32[2]; + #endif +#endif + return *this; +} + +Vec3 Vec3::operator / (Vec3Arg inV2) const +{ + inV2.CheckW(); // Check W equals Z to avoid div by zero +#if defined(JPH_USE_SSE) + return _mm_div_ps(mValue, inV2.mValue); +#elif defined(JPH_USE_NEON) + return vdivq_f32(mValue, inV2.mValue); +#else + return Vec3(mF32[0] / inV2.mF32[0], mF32[1] / inV2.mF32[1], mF32[2] / inV2.mF32[2]); +#endif +} + +Vec4 Vec3::SplatX() const +{ +#if defined(JPH_USE_SSE) + return _mm_shuffle_ps(mValue, mValue, _MM_SHUFFLE(0, 0, 0, 0)); +#elif defined(JPH_USE_NEON) + return vdupq_laneq_f32(mValue, 0); +#else + return Vec4(mF32[0], mF32[0], mF32[0], mF32[0]); +#endif +} + +Vec4 Vec3::SplatY() const +{ +#if defined(JPH_USE_SSE) + return _mm_shuffle_ps(mValue, mValue, _MM_SHUFFLE(1, 1, 1, 1)); +#elif defined(JPH_USE_NEON) + return vdupq_laneq_f32(mValue, 1); +#else + return Vec4(mF32[1], mF32[1], mF32[1], mF32[1]); +#endif +} + +Vec4 Vec3::SplatZ() const +{ +#if defined(JPH_USE_SSE) + return _mm_shuffle_ps(mValue, mValue, _MM_SHUFFLE(2, 2, 2, 2)); +#elif defined(JPH_USE_NEON) + return vdupq_laneq_f32(mValue, 2); +#else + return Vec4(mF32[2], mF32[2], mF32[2], mF32[2]); +#endif +} + +int Vec3::GetLowestComponentIndex() const +{ + return GetX() < GetY() ? (GetZ() < GetX() ? 2 : 0) : (GetZ() < GetY() ? 2 : 1); +} + +int Vec3::GetHighestComponentIndex() const +{ + return GetX() > GetY() ? (GetZ() > GetX() ? 2 : 0) : (GetZ() > GetY() ? 2 : 1); +} + +Vec3 Vec3::Abs() const +{ +#if defined(JPH_USE_AVX512) + return _mm_range_ps(mValue, mValue, 0b1000); +#elif defined(JPH_USE_SSE) + return _mm_max_ps(_mm_sub_ps(_mm_setzero_ps(), mValue), mValue); +#elif defined(JPH_USE_NEON) + return vabsq_f32(mValue); +#else + return Vec3(abs(mF32[0]), abs(mF32[1]), abs(mF32[2])); +#endif +} + +Vec3 Vec3::Reciprocal() const +{ + return sReplicate(1.0f) / mValue; +} + +Vec3 Vec3::Cross(Vec3Arg inV2) const +{ +#if defined(JPH_USE_SSE) + Type t1 = _mm_shuffle_ps(inV2.mValue, inV2.mValue, _MM_SHUFFLE(0, 0, 2, 1)); // Assure Z and W are the same + t1 = _mm_mul_ps(t1, mValue); + Type t2 = _mm_shuffle_ps(mValue, mValue, _MM_SHUFFLE(0, 0, 2, 1)); // Assure Z and W are the same + t2 = _mm_mul_ps(t2, inV2.mValue); + Type t3 = _mm_sub_ps(t1, t2); + return _mm_shuffle_ps(t3, t3, _MM_SHUFFLE(0, 0, 2, 1)); // Assure Z and W are the same +#elif defined(JPH_USE_NEON) + Type t1 = JPH_NEON_SHUFFLE_F32x4(inV2.mValue, inV2.mValue, 1, 2, 0, 0); // Assure Z and W are the same + t1 = vmulq_f32(t1, mValue); + Type t2 = JPH_NEON_SHUFFLE_F32x4(mValue, mValue, 1, 2, 0, 0); // Assure Z and W are the same + t2 = vmulq_f32(t2, inV2.mValue); + Type t3 = vsubq_f32(t1, t2); + return JPH_NEON_SHUFFLE_F32x4(t3, t3, 1, 2, 0, 0); // Assure Z and W are the same +#else + return Vec3(mF32[1] * inV2.mF32[2] - mF32[2] * inV2.mF32[1], + mF32[2] * inV2.mF32[0] - mF32[0] * inV2.mF32[2], + mF32[0] * inV2.mF32[1] - mF32[1] * inV2.mF32[0]); +#endif +} + +Vec3 Vec3::DotV(Vec3Arg inV2) const +{ +#if defined(JPH_USE_SSE4_1) + return _mm_dp_ps(mValue, inV2.mValue, 0x7f); +#elif defined(JPH_USE_NEON) + float32x4_t mul = vmulq_f32(mValue, inV2.mValue); + mul = vsetq_lane_f32(0, mul, 3); + return vdupq_n_f32(vaddvq_f32(mul)); +#else + float dot = 0.0f; + for (int i = 0; i < 3; i++) + dot += mF32[i] * inV2.mF32[i]; + return Vec3::sReplicate(dot); +#endif +} + +Vec4 Vec3::DotV4(Vec3Arg inV2) const +{ +#if defined(JPH_USE_SSE4_1) + return _mm_dp_ps(mValue, inV2.mValue, 0x7f); +#elif defined(JPH_USE_NEON) + float32x4_t mul = vmulq_f32(mValue, inV2.mValue); + mul = vsetq_lane_f32(0, mul, 3); + return vdupq_n_f32(vaddvq_f32(mul)); +#else + float dot = 0.0f; + for (int i = 0; i < 3; i++) + dot += mF32[i] * inV2.mF32[i]; + return Vec4::sReplicate(dot); +#endif +} + +float Vec3::Dot(Vec3Arg inV2) const +{ +#if defined(JPH_USE_SSE4_1) + return _mm_cvtss_f32(_mm_dp_ps(mValue, inV2.mValue, 0x7f)); +#elif defined(JPH_USE_NEON) + float32x4_t mul = vmulq_f32(mValue, inV2.mValue); + mul = vsetq_lane_f32(0, mul, 3); + return vaddvq_f32(mul); +#else + float dot = 0.0f; + for (int i = 0; i < 3; i++) + dot += mF32[i] * inV2.mF32[i]; + return dot; +#endif +} + +float Vec3::LengthSq() const +{ +#if defined(JPH_USE_SSE4_1) + return _mm_cvtss_f32(_mm_dp_ps(mValue, mValue, 0x7f)); +#elif defined(JPH_USE_NEON) + float32x4_t mul = vmulq_f32(mValue, mValue); + mul = vsetq_lane_f32(0, mul, 3); + return vaddvq_f32(mul); +#else + float len_sq = 0.0f; + for (int i = 0; i < 3; i++) + len_sq += mF32[i] * mF32[i]; + return len_sq; +#endif +} + +float Vec3::Length() const +{ +#if defined(JPH_USE_SSE4_1) + return _mm_cvtss_f32(_mm_sqrt_ss(_mm_dp_ps(mValue, mValue, 0x7f))); +#elif defined(JPH_USE_NEON) + float32x4_t mul = vmulq_f32(mValue, mValue); + mul = vsetq_lane_f32(0, mul, 3); + float32x2_t sum = vdup_n_f32(vaddvq_f32(mul)); + return vget_lane_f32(vsqrt_f32(sum), 0); +#else + return sqrt(LengthSq()); +#endif +} + +Vec3 Vec3::Sqrt() const +{ +#if defined(JPH_USE_SSE) + return _mm_sqrt_ps(mValue); +#elif defined(JPH_USE_NEON) + return vsqrtq_f32(mValue); +#else + return Vec3(sqrt(mF32[0]), sqrt(mF32[1]), sqrt(mF32[2])); +#endif +} + +Vec3 Vec3::Normalized() const +{ +#if defined(JPH_USE_SSE4_1) + return _mm_div_ps(mValue, _mm_sqrt_ps(_mm_dp_ps(mValue, mValue, 0x7f))); +#elif defined(JPH_USE_NEON) + float32x4_t mul = vmulq_f32(mValue, mValue); + mul = vsetq_lane_f32(0, mul, 3); + float32x4_t sum = vdupq_n_f32(vaddvq_f32(mul)); + return vdivq_f32(mValue, vsqrtq_f32(sum)); +#else + return *this / Length(); +#endif +} + +Vec3 Vec3::NormalizedOr(Vec3Arg inZeroValue) const +{ +#if defined(JPH_USE_SSE4_1) + Type len_sq = _mm_dp_ps(mValue, mValue, 0x7f); + Type is_zero = _mm_cmpeq_ps(len_sq, _mm_setzero_ps()); +#ifdef JPH_FLOATING_POINT_EXCEPTIONS_ENABLED + if (_mm_movemask_ps(is_zero) == 0xf) + return inZeroValue; + else + return _mm_div_ps(mValue, _mm_sqrt_ps(len_sq)); +#else + return _mm_blendv_ps(_mm_div_ps(mValue, _mm_sqrt_ps(len_sq)), inZeroValue.mValue, is_zero); +#endif // JPH_FLOATING_POINT_EXCEPTIONS_ENABLED +#elif defined(JPH_USE_NEON) + float32x4_t mul = vmulq_f32(mValue, mValue); + mul = vsetq_lane_f32(0, mul, 3); + float32x4_t sum = vdupq_n_f32(vaddvq_f32(mul)); + float32x4_t len = vsqrtq_f32(sum); + float32x4_t is_zero = vceqq_f32(len, vdupq_n_f32(0)); + return vbslq_f32(is_zero, inZeroValue.mValue, vdivq_f32(mValue, len)); +#else + float len_sq = LengthSq(); + if (len_sq == 0.0f) + return inZeroValue; + else + return *this / sqrt(len_sq); +#endif +} + +bool Vec3::IsNormalized(float inTolerance) const +{ + return abs(LengthSq() - 1.0f) <= inTolerance; +} + +bool Vec3::IsNaN() const +{ +#if defined(JPH_USE_AVX512) + return (_mm_fpclass_ps_mask(mValue, 0b10000001) & 0x7) != 0; +#elif defined(JPH_USE_SSE) + return (_mm_movemask_ps(_mm_cmpunord_ps(mValue, mValue)) & 0x7) != 0; +#elif defined(JPH_USE_NEON) + uint32x4_t mask = JPH_NEON_UINT32x4(1, 1, 1, 0); + uint32x4_t is_equal = vceqq_f32(mValue, mValue); // If a number is not equal to itself it's a NaN + return vaddvq_u32(vandq_u32(is_equal, mask)) != 3; +#else + return isnan(mF32[0]) || isnan(mF32[1]) || isnan(mF32[2]); +#endif +} + +void Vec3::StoreFloat3(Float3 *outV) const +{ +#if defined(JPH_USE_SSE) + _mm_store_ss(&outV->x, mValue); + Vec3 t = Swizzle(); + _mm_store_ss(&outV->y, t.mValue); + t = t.Swizzle(); + _mm_store_ss(&outV->z, t.mValue); +#elif defined(JPH_USE_NEON) + float32x2_t xy = vget_low_f32(mValue); + vst1_f32(&outV->x, xy); + vst1q_lane_f32(&outV->z, mValue, 2); +#else + outV->x = mF32[0]; + outV->y = mF32[1]; + outV->z = mF32[2]; +#endif +} + +UVec4 Vec3::ToInt() const +{ +#if defined(JPH_USE_SSE) + return _mm_cvttps_epi32(mValue); +#elif defined(JPH_USE_NEON) + return vcvtq_u32_f32(mValue); +#else + return UVec4(uint32(mF32[0]), uint32(mF32[1]), uint32(mF32[2]), uint32(mF32[3])); +#endif +} + +UVec4 Vec3::ReinterpretAsInt() const +{ +#if defined(JPH_USE_SSE) + return UVec4(_mm_castps_si128(mValue)); +#elif defined(JPH_USE_NEON) + return vreinterpretq_u32_f32(mValue); +#else + return *reinterpret_cast(this); +#endif +} + +float Vec3::ReduceMin() const +{ + Vec3 v = sMin(mValue, Swizzle()); + v = sMin(v, v.Swizzle()); + return v.GetX(); +} + +float Vec3::ReduceMax() const +{ + Vec3 v = sMax(mValue, Swizzle()); + v = sMax(v, v.Swizzle()); + return v.GetX(); +} + +Vec3 Vec3::GetNormalizedPerpendicular() const +{ + if (abs(mF32[0]) > abs(mF32[1])) + { + float len = sqrt(mF32[0] * mF32[0] + mF32[2] * mF32[2]); + return Vec3(mF32[2], 0.0f, -mF32[0]) / len; + } + else + { + float len = sqrt(mF32[1] * mF32[1] + mF32[2] * mF32[2]); + return Vec3(0.0f, mF32[2], -mF32[1]) / len; + } +} + +Vec3 Vec3::GetSign() const +{ +#if defined(JPH_USE_AVX512) + return _mm_fixupimm_ps(mValue, mValue, _mm_set1_epi32(0xA9A90A00), 0); +#elif defined(JPH_USE_SSE) + Type minus_one = _mm_set1_ps(-1.0f); + Type one = _mm_set1_ps(1.0f); + return _mm_or_ps(_mm_and_ps(mValue, minus_one), one); +#elif defined(JPH_USE_NEON) + Type minus_one = vdupq_n_f32(-1.0f); + Type one = vdupq_n_f32(1.0f); + return vorrq_s32(vandq_s32(mValue, minus_one), one); +#else + return Vec3(signbit(mF32[0])? -1.0f : 1.0f, + signbit(mF32[1])? -1.0f : 1.0f, + signbit(mF32[2])? -1.0f : 1.0f); +#endif +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Vec4.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Vec4.h new file mode 100644 index 00000000000..b369f8e3c61 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Vec4.h @@ -0,0 +1,283 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +class [[nodiscard]] alignas(JPH_VECTOR_ALIGNMENT) Vec4 +{ +public: + JPH_OVERRIDE_NEW_DELETE + + // Underlying vector type +#if defined(JPH_USE_SSE) + using Type = __m128; +#elif defined(JPH_USE_NEON) + using Type = float32x4_t; +#else + using Type = struct { float mData[4]; }; +#endif + + /// Constructor + Vec4() = default; ///< Intentionally not initialized for performance reasons + Vec4(const Vec4 &inRHS) = default; + Vec4 & operator = (const Vec4 &inRHS) = default; + explicit JPH_INLINE Vec4(Vec3Arg inRHS); ///< WARNING: W component undefined! + JPH_INLINE Vec4(Vec3Arg inRHS, float inW); + JPH_INLINE Vec4(Type inRHS) : mValue(inRHS) { } + + /// Create a vector from 4 components + JPH_INLINE Vec4(float inX, float inY, float inZ, float inW); + + /// Vector with all zeros + static JPH_INLINE Vec4 sZero(); + + /// Vector with all NaN's + static JPH_INLINE Vec4 sNaN(); + + /// Replicate inV across all components + static JPH_INLINE Vec4 sReplicate(float inV); + + /// Load 4 floats from memory + static JPH_INLINE Vec4 sLoadFloat4(const Float4 *inV); + + /// Load 4 floats from memory, 16 bytes aligned + static JPH_INLINE Vec4 sLoadFloat4Aligned(const Float4 *inV); + + /// Gather 4 floats from memory at inBase + inOffsets[i] * Scale + template + static JPH_INLINE Vec4 sGatherFloat4(const float *inBase, UVec4Arg inOffsets); + + /// Return the minimum value of each of the components + static JPH_INLINE Vec4 sMin(Vec4Arg inV1, Vec4Arg inV2); + + /// Return the maximum of each of the components + static JPH_INLINE Vec4 sMax(Vec4Arg inV1, Vec4Arg inV2); + + /// Equals (component wise) + static JPH_INLINE UVec4 sEquals(Vec4Arg inV1, Vec4Arg inV2); + + /// Less than (component wise) + static JPH_INLINE UVec4 sLess(Vec4Arg inV1, Vec4Arg inV2); + + /// Less than or equal (component wise) + static JPH_INLINE UVec4 sLessOrEqual(Vec4Arg inV1, Vec4Arg inV2); + + /// Greater than (component wise) + static JPH_INLINE UVec4 sGreater(Vec4Arg inV1, Vec4Arg inV2); + + /// Greater than or equal (component wise) + static JPH_INLINE UVec4 sGreaterOrEqual(Vec4Arg inV1, Vec4Arg inV2); + + /// Calculates inMul1 * inMul2 + inAdd + static JPH_INLINE Vec4 sFusedMultiplyAdd(Vec4Arg inMul1, Vec4Arg inMul2, Vec4Arg inAdd); + + /// Component wise select, returns inV1 when highest bit of inControl = 0 and inV2 when highest bit of inControl = 1 + static JPH_INLINE Vec4 sSelect(Vec4Arg inV1, Vec4Arg inV2, UVec4Arg inControl); + + /// Logical or (component wise) + static JPH_INLINE Vec4 sOr(Vec4Arg inV1, Vec4Arg inV2); + + /// Logical xor (component wise) + static JPH_INLINE Vec4 sXor(Vec4Arg inV1, Vec4Arg inV2); + + /// Logical and (component wise) + static JPH_INLINE Vec4 sAnd(Vec4Arg inV1, Vec4Arg inV2); + + /// Sort the four elements of ioValue and sort ioIndex at the same time. + /// Based on a sorting network: http://en.wikipedia.org/wiki/Sorting_network + static JPH_INLINE void sSort4(Vec4 &ioValue, UVec4 &ioIndex); + + /// Reverse sort the four elements of ioValue (highest first) and sort ioIndex at the same time. + /// Based on a sorting network: http://en.wikipedia.org/wiki/Sorting_network + static JPH_INLINE void sSort4Reverse(Vec4 &ioValue, UVec4 &ioIndex); + + /// Get individual components +#if defined(JPH_USE_SSE) + JPH_INLINE float GetX() const { return _mm_cvtss_f32(mValue); } + JPH_INLINE float GetY() const { return mF32[1]; } + JPH_INLINE float GetZ() const { return mF32[2]; } + JPH_INLINE float GetW() const { return mF32[3]; } +#elif defined(JPH_USE_NEON) + JPH_INLINE float GetX() const { return vgetq_lane_f32(mValue, 0); } + JPH_INLINE float GetY() const { return vgetq_lane_f32(mValue, 1); } + JPH_INLINE float GetZ() const { return vgetq_lane_f32(mValue, 2); } + JPH_INLINE float GetW() const { return vgetq_lane_f32(mValue, 3); } +#else + JPH_INLINE float GetX() const { return mF32[0]; } + JPH_INLINE float GetY() const { return mF32[1]; } + JPH_INLINE float GetZ() const { return mF32[2]; } + JPH_INLINE float GetW() const { return mF32[3]; } +#endif + + /// Set individual components + JPH_INLINE void SetX(float inX) { mF32[0] = inX; } + JPH_INLINE void SetY(float inY) { mF32[1] = inY; } + JPH_INLINE void SetZ(float inZ) { mF32[2] = inZ; } + JPH_INLINE void SetW(float inW) { mF32[3] = inW; } + + /// Set all components + JPH_INLINE void Set(float inX, float inY, float inZ, float inW) { *this = Vec4(inX, inY, inZ, inW); } + + /// Get float component by index + JPH_INLINE float operator [] (uint inCoordinate) const { JPH_ASSERT(inCoordinate < 4); return mF32[inCoordinate]; } + JPH_INLINE float & operator [] (uint inCoordinate) { JPH_ASSERT(inCoordinate < 4); return mF32[inCoordinate]; } + + /// Comparison + JPH_INLINE bool operator == (Vec4Arg inV2) const; + JPH_INLINE bool operator != (Vec4Arg inV2) const { return !(*this == inV2); } + + /// Test if two vectors are close + JPH_INLINE bool IsClose(Vec4Arg inV2, float inMaxDistSq = 1.0e-12f) const; + + /// Test if vector is normalized + JPH_INLINE bool IsNormalized(float inTolerance = 1.0e-6f) const; + + /// Test if vector contains NaN elements + JPH_INLINE bool IsNaN() const; + + /// Multiply two float vectors (component wise) + JPH_INLINE Vec4 operator * (Vec4Arg inV2) const; + + /// Multiply vector with float + JPH_INLINE Vec4 operator * (float inV2) const; + + /// Multiply vector with float + friend JPH_INLINE Vec4 operator * (float inV1, Vec4Arg inV2); + + /// Divide vector by float + JPH_INLINE Vec4 operator / (float inV2) const; + + /// Multiply vector with float + JPH_INLINE Vec4 & operator *= (float inV2); + + /// Multiply vector with vector + JPH_INLINE Vec4 & operator *= (Vec4Arg inV2); + + /// Divide vector by float + JPH_INLINE Vec4 & operator /= (float inV2); + + /// Add two float vectors (component wise) + JPH_INLINE Vec4 operator + (Vec4Arg inV2) const; + + /// Add two float vectors (component wise) + JPH_INLINE Vec4 & operator += (Vec4Arg inV2); + + /// Negate + JPH_INLINE Vec4 operator - () const; + + /// Subtract two float vectors (component wise) + JPH_INLINE Vec4 operator - (Vec4Arg inV2) const; + + /// Add two float vectors (component wise) + JPH_INLINE Vec4 & operator -= (Vec4Arg inV2); + + /// Divide (component wise) + JPH_INLINE Vec4 operator / (Vec4Arg inV2) const; + + /// Swizzle the elements in inV + template + JPH_INLINE Vec4 Swizzle() const; + + /// Replicate the X component to all components + JPH_INLINE Vec4 SplatX() const; + + /// Replicate the Y component to all components + JPH_INLINE Vec4 SplatY() const; + + /// Replicate the Z component to all components + JPH_INLINE Vec4 SplatZ() const; + + /// Replicate the W component to all components + JPH_INLINE Vec4 SplatW() const; + + /// Return the absolute value of each of the components + JPH_INLINE Vec4 Abs() const; + + /// Reciprocal vector (1 / value) for each of the components + JPH_INLINE Vec4 Reciprocal() const; + + /// Dot product, returns the dot product in X, Y and Z components + JPH_INLINE Vec4 DotV(Vec4Arg inV2) const; + + /// Dot product + JPH_INLINE float Dot(Vec4Arg inV2) const; + + /// Squared length of vector + JPH_INLINE float LengthSq() const; + + /// Length of vector + JPH_INLINE float Length() const; + + /// Normalize vector + JPH_INLINE Vec4 Normalized() const; + + /// Store 4 floats to memory + JPH_INLINE void StoreFloat4(Float4 *outV) const; + + /// Convert each component from a float to an int + JPH_INLINE UVec4 ToInt() const; + + /// Reinterpret Vec4 as a UVec4 (doesn't change the bits) + JPH_INLINE UVec4 ReinterpretAsInt() const; + + /// Store if X is negative in bit 0, Y in bit 1, Z in bit 2 and W in bit 3 + JPH_INLINE int GetSignBits() const; + + /// Get the minimum of X, Y, Z and W + JPH_INLINE float ReduceMin() const; + + /// Get the maximum of X, Y, Z and W + JPH_INLINE float ReduceMax() const; + + /// Component wise square root + JPH_INLINE Vec4 Sqrt() const; + + /// Get vector that contains the sign of each element (returns 1.0f if positive, -1.0f if negative) + JPH_INLINE Vec4 GetSign() const; + + /// Calculate the sine and cosine for each element of this vector (input in radians) + inline void SinCos(Vec4 &outSin, Vec4 &outCos) const; + + /// Calculate the tangent for each element of this vector (input in radians) + inline Vec4 Tan() const; + + /// Calculate the arc sine for each element of this vector (returns value in the range [-PI / 2, PI / 2]) + /// Note that all input values will be clamped to the range [-1, 1] and this function will not return NaNs like std::asin + inline Vec4 ASin() const; + + /// Calculate the arc cosine for each element of this vector (returns value in the range [0, PI]) + /// Note that all input values will be clamped to the range [-1, 1] and this function will not return NaNs like std::acos + inline Vec4 ACos() const; + + /// Calculate the arc tangent for each element of this vector (returns value in the range [-PI / 2, PI / 2]) + inline Vec4 ATan() const; + + /// Calculate the arc tangent of y / x using the signs of the arguments to determine the correct quadrant (returns value in the range [-PI, PI]) + inline static Vec4 sATan2(Vec4Arg inY, Vec4Arg inX); + + /// To String + friend ostream & operator << (ostream &inStream, Vec4Arg inV) + { + inStream << inV.mF32[0] << ", " << inV.mF32[1] << ", " << inV.mF32[2] << ", " << inV.mF32[3]; + return inStream; + } + + union + { + Type mValue; + float mF32[4]; + }; +}; + +static_assert(is_trivial(), "Is supposed to be a trivial type!"); + +JPH_NAMESPACE_END + +#include "Vec4.inl" diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Vec4.inl b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Vec4.inl new file mode 100644 index 00000000000..9fb6fc6d001 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Vec4.inl @@ -0,0 +1,970 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +// Constructor +Vec4::Vec4(Vec3Arg inRHS) : + mValue(inRHS.mValue) +{ +} + +Vec4::Vec4(Vec3Arg inRHS, float inW) +{ +#if defined(JPH_USE_SSE4_1) + mValue = _mm_blend_ps(inRHS.mValue, _mm_set1_ps(inW), 8); +#elif defined(JPH_USE_NEON) + mValue = vsetq_lane_f32(inW, inRHS.mValue, 3); +#else + for (int i = 0; i < 3; i++) + mF32[i] = inRHS.mF32[i]; + mF32[3] = inW; +#endif +} + +Vec4::Vec4(float inX, float inY, float inZ, float inW) +{ +#if defined(JPH_USE_SSE) + mValue = _mm_set_ps(inW, inZ, inY, inX); +#elif defined(JPH_USE_NEON) + uint32x2_t xy = vcreate_f32(static_cast(*reinterpret_cast(&inX)) | (static_cast(*reinterpret_cast(&inY)) << 32)); + uint32x2_t zw = vcreate_f32(static_cast(*reinterpret_cast(&inZ)) | (static_cast(*reinterpret_cast(&inW)) << 32)); + mValue = vcombine_f32(xy, zw); +#else + mF32[0] = inX; + mF32[1] = inY; + mF32[2] = inZ; + mF32[3] = inW; +#endif +} + +template +Vec4 Vec4::Swizzle() const +{ + static_assert(SwizzleX <= 3, "SwizzleX template parameter out of range"); + static_assert(SwizzleY <= 3, "SwizzleY template parameter out of range"); + static_assert(SwizzleZ <= 3, "SwizzleZ template parameter out of range"); + static_assert(SwizzleW <= 3, "SwizzleW template parameter out of range"); + +#if defined(JPH_USE_SSE) + return _mm_shuffle_ps(mValue, mValue, _MM_SHUFFLE(SwizzleW, SwizzleZ, SwizzleY, SwizzleX)); +#elif defined(JPH_USE_NEON) + return JPH_NEON_SHUFFLE_F32x4(mValue, mValue, SwizzleX, SwizzleY, SwizzleZ, SwizzleW); +#else + return Vec4(mF32[SwizzleX], mF32[SwizzleY], mF32[SwizzleZ], mF32[SwizzleW]); +#endif +} + +Vec4 Vec4::sZero() +{ +#if defined(JPH_USE_SSE) + return _mm_setzero_ps(); +#elif defined(JPH_USE_NEON) + return vdupq_n_f32(0); +#else + return Vec4(0, 0, 0, 0); +#endif +} + +Vec4 Vec4::sReplicate(float inV) +{ +#if defined(JPH_USE_SSE) + return _mm_set1_ps(inV); +#elif defined(JPH_USE_NEON) + return vdupq_n_f32(inV); +#else + return Vec4(inV, inV, inV, inV); +#endif +} + +Vec4 Vec4::sNaN() +{ + return sReplicate(numeric_limits::quiet_NaN()); +} + +Vec4 Vec4::sLoadFloat4(const Float4 *inV) +{ +#if defined(JPH_USE_SSE) + return _mm_loadu_ps(&inV->x); +#elif defined(JPH_USE_NEON) + return vld1q_f32(&inV->x); +#else + return Vec4(inV->x, inV->y, inV->z, inV->w); +#endif +} + +Vec4 Vec4::sLoadFloat4Aligned(const Float4 *inV) +{ +#if defined(JPH_USE_SSE) + return _mm_load_ps(&inV->x); +#elif defined(JPH_USE_NEON) + return vld1q_f32(&inV->x); +#else + return Vec4(inV->x, inV->y, inV->z, inV->w); +#endif +} + +template +Vec4 Vec4::sGatherFloat4(const float *inBase, UVec4Arg inOffsets) +{ +#if defined(JPH_USE_SSE) + #ifdef JPH_USE_AVX2 + return _mm_i32gather_ps(inBase, inOffsets.mValue, Scale); + #else + const uint8 *base = reinterpret_cast(inBase); + Type x = _mm_load_ss(reinterpret_cast(base + inOffsets.GetX() * Scale)); + Type y = _mm_load_ss(reinterpret_cast(base + inOffsets.GetY() * Scale)); + Type xy = _mm_unpacklo_ps(x, y); + Type z = _mm_load_ss(reinterpret_cast(base + inOffsets.GetZ() * Scale)); + Type w = _mm_load_ss(reinterpret_cast(base + inOffsets.GetW() * Scale)); + Type zw = _mm_unpacklo_ps(z, w); + return _mm_movelh_ps(xy, zw); + #endif +#else + const uint8 *base = reinterpret_cast(inBase); + float x = *reinterpret_cast(base + inOffsets.GetX() * Scale); + float y = *reinterpret_cast(base + inOffsets.GetY() * Scale); + float z = *reinterpret_cast(base + inOffsets.GetZ() * Scale); + float w = *reinterpret_cast(base + inOffsets.GetW() * Scale); + return Vec4(x, y, z, w); +#endif +} + +Vec4 Vec4::sMin(Vec4Arg inV1, Vec4Arg inV2) +{ +#if defined(JPH_USE_SSE) + return _mm_min_ps(inV1.mValue, inV2.mValue); +#elif defined(JPH_USE_NEON) + return vminq_f32(inV1.mValue, inV2.mValue); +#else + return Vec4(min(inV1.mF32[0], inV2.mF32[0]), + min(inV1.mF32[1], inV2.mF32[1]), + min(inV1.mF32[2], inV2.mF32[2]), + min(inV1.mF32[3], inV2.mF32[3])); +#endif +} + +Vec4 Vec4::sMax(Vec4Arg inV1, Vec4Arg inV2) +{ +#if defined(JPH_USE_SSE) + return _mm_max_ps(inV1.mValue, inV2.mValue); +#elif defined(JPH_USE_NEON) + return vmaxq_f32(inV1.mValue, inV2.mValue); +#else + return Vec4(max(inV1.mF32[0], inV2.mF32[0]), + max(inV1.mF32[1], inV2.mF32[1]), + max(inV1.mF32[2], inV2.mF32[2]), + max(inV1.mF32[3], inV2.mF32[3])); +#endif +} + +UVec4 Vec4::sEquals(Vec4Arg inV1, Vec4Arg inV2) +{ +#if defined(JPH_USE_SSE) + return _mm_castps_si128(_mm_cmpeq_ps(inV1.mValue, inV2.mValue)); +#elif defined(JPH_USE_NEON) + return vceqq_f32(inV1.mValue, inV2.mValue); +#else + return UVec4(inV1.mF32[0] == inV2.mF32[0]? 0xffffffffu : 0, + inV1.mF32[1] == inV2.mF32[1]? 0xffffffffu : 0, + inV1.mF32[2] == inV2.mF32[2]? 0xffffffffu : 0, + inV1.mF32[3] == inV2.mF32[3]? 0xffffffffu : 0); +#endif +} + +UVec4 Vec4::sLess(Vec4Arg inV1, Vec4Arg inV2) +{ +#if defined(JPH_USE_SSE) + return _mm_castps_si128(_mm_cmplt_ps(inV1.mValue, inV2.mValue)); +#elif defined(JPH_USE_NEON) + return vcltq_f32(inV1.mValue, inV2.mValue); +#else + return UVec4(inV1.mF32[0] < inV2.mF32[0]? 0xffffffffu : 0, + inV1.mF32[1] < inV2.mF32[1]? 0xffffffffu : 0, + inV1.mF32[2] < inV2.mF32[2]? 0xffffffffu : 0, + inV1.mF32[3] < inV2.mF32[3]? 0xffffffffu : 0); +#endif +} + +UVec4 Vec4::sLessOrEqual(Vec4Arg inV1, Vec4Arg inV2) +{ +#if defined(JPH_USE_SSE) + return _mm_castps_si128(_mm_cmple_ps(inV1.mValue, inV2.mValue)); +#elif defined(JPH_USE_NEON) + return vcleq_f32(inV1.mValue, inV2.mValue); +#else + return UVec4(inV1.mF32[0] <= inV2.mF32[0]? 0xffffffffu : 0, + inV1.mF32[1] <= inV2.mF32[1]? 0xffffffffu : 0, + inV1.mF32[2] <= inV2.mF32[2]? 0xffffffffu : 0, + inV1.mF32[3] <= inV2.mF32[3]? 0xffffffffu : 0); +#endif +} + +UVec4 Vec4::sGreater(Vec4Arg inV1, Vec4Arg inV2) +{ +#if defined(JPH_USE_SSE) + return _mm_castps_si128(_mm_cmpgt_ps(inV1.mValue, inV2.mValue)); +#elif defined(JPH_USE_NEON) + return vcgtq_f32(inV1.mValue, inV2.mValue); +#else + return UVec4(inV1.mF32[0] > inV2.mF32[0]? 0xffffffffu : 0, + inV1.mF32[1] > inV2.mF32[1]? 0xffffffffu : 0, + inV1.mF32[2] > inV2.mF32[2]? 0xffffffffu : 0, + inV1.mF32[3] > inV2.mF32[3]? 0xffffffffu : 0); +#endif +} + +UVec4 Vec4::sGreaterOrEqual(Vec4Arg inV1, Vec4Arg inV2) +{ +#if defined(JPH_USE_SSE) + return _mm_castps_si128(_mm_cmpge_ps(inV1.mValue, inV2.mValue)); +#elif defined(JPH_USE_NEON) + return vcgeq_f32(inV1.mValue, inV2.mValue); +#else + return UVec4(inV1.mF32[0] >= inV2.mF32[0]? 0xffffffffu : 0, + inV1.mF32[1] >= inV2.mF32[1]? 0xffffffffu : 0, + inV1.mF32[2] >= inV2.mF32[2]? 0xffffffffu : 0, + inV1.mF32[3] >= inV2.mF32[3]? 0xffffffffu : 0); +#endif +} + +Vec4 Vec4::sFusedMultiplyAdd(Vec4Arg inMul1, Vec4Arg inMul2, Vec4Arg inAdd) +{ +#if defined(JPH_USE_SSE) + #ifdef JPH_USE_FMADD + return _mm_fmadd_ps(inMul1.mValue, inMul2.mValue, inAdd.mValue); + #else + return _mm_add_ps(_mm_mul_ps(inMul1.mValue, inMul2.mValue), inAdd.mValue); + #endif +#elif defined(JPH_USE_NEON) + return vmlaq_f32(inAdd.mValue, inMul1.mValue, inMul2.mValue); +#else + return Vec4(inMul1.mF32[0] * inMul2.mF32[0] + inAdd.mF32[0], + inMul1.mF32[1] * inMul2.mF32[1] + inAdd.mF32[1], + inMul1.mF32[2] * inMul2.mF32[2] + inAdd.mF32[2], + inMul1.mF32[3] * inMul2.mF32[3] + inAdd.mF32[3]); +#endif +} + +Vec4 Vec4::sSelect(Vec4Arg inV1, Vec4Arg inV2, UVec4Arg inControl) +{ +#if defined(JPH_USE_SSE4_1) + return _mm_blendv_ps(inV1.mValue, inV2.mValue, _mm_castsi128_ps(inControl.mValue)); +#elif defined(JPH_USE_NEON) + return vbslq_f32(vshrq_n_s32(inControl.mValue, 31), inV2.mValue, inV1.mValue); +#else + Vec4 result; + for (int i = 0; i < 4; i++) + result.mF32[i] = inControl.mU32[i] ? inV2.mF32[i] : inV1.mF32[i]; + return result; +#endif +} + +Vec4 Vec4::sOr(Vec4Arg inV1, Vec4Arg inV2) +{ +#if defined(JPH_USE_SSE) + return _mm_or_ps(inV1.mValue, inV2.mValue); +#elif defined(JPH_USE_NEON) + return vorrq_s32(inV1.mValue, inV2.mValue); +#else + return UVec4::sOr(inV1.ReinterpretAsInt(), inV2.ReinterpretAsInt()).ReinterpretAsFloat(); +#endif +} + +Vec4 Vec4::sXor(Vec4Arg inV1, Vec4Arg inV2) +{ +#if defined(JPH_USE_SSE) + return _mm_xor_ps(inV1.mValue, inV2.mValue); +#elif defined(JPH_USE_NEON) + return veorq_s32(inV1.mValue, inV2.mValue); +#else + return UVec4::sXor(inV1.ReinterpretAsInt(), inV2.ReinterpretAsInt()).ReinterpretAsFloat(); +#endif +} + +Vec4 Vec4::sAnd(Vec4Arg inV1, Vec4Arg inV2) +{ +#if defined(JPH_USE_SSE) + return _mm_and_ps(inV1.mValue, inV2.mValue); +#elif defined(JPH_USE_NEON) + return vandq_s32(inV1.mValue, inV2.mValue); +#else + return UVec4::sAnd(inV1.ReinterpretAsInt(), inV2.ReinterpretAsInt()).ReinterpretAsFloat(); +#endif +} + +void Vec4::sSort4(Vec4 &ioValue, UVec4 &ioIndex) +{ + // Pass 1, test 1st vs 3rd, 2nd vs 4th + Vec4 v1 = ioValue.Swizzle(); + UVec4 i1 = ioIndex.Swizzle(); + UVec4 c1 = sLess(ioValue, v1).Swizzle(); + ioValue = sSelect(ioValue, v1, c1); + ioIndex = UVec4::sSelect(ioIndex, i1, c1); + + // Pass 2, test 1st vs 2nd, 3rd vs 4th + Vec4 v2 = ioValue.Swizzle(); + UVec4 i2 = ioIndex.Swizzle(); + UVec4 c2 = sLess(ioValue, v2).Swizzle(); + ioValue = sSelect(ioValue, v2, c2); + ioIndex = UVec4::sSelect(ioIndex, i2, c2); + + // Pass 3, test 2nd vs 3rd component + Vec4 v3 = ioValue.Swizzle(); + UVec4 i3 = ioIndex.Swizzle(); + UVec4 c3 = sLess(ioValue, v3).Swizzle(); + ioValue = sSelect(ioValue, v3, c3); + ioIndex = UVec4::sSelect(ioIndex, i3, c3); +} + +void Vec4::sSort4Reverse(Vec4 &ioValue, UVec4 &ioIndex) +{ + // Pass 1, test 1st vs 3rd, 2nd vs 4th + Vec4 v1 = ioValue.Swizzle(); + UVec4 i1 = ioIndex.Swizzle(); + UVec4 c1 = sGreater(ioValue, v1).Swizzle(); + ioValue = sSelect(ioValue, v1, c1); + ioIndex = UVec4::sSelect(ioIndex, i1, c1); + + // Pass 2, test 1st vs 2nd, 3rd vs 4th + Vec4 v2 = ioValue.Swizzle(); + UVec4 i2 = ioIndex.Swizzle(); + UVec4 c2 = sGreater(ioValue, v2).Swizzle(); + ioValue = sSelect(ioValue, v2, c2); + ioIndex = UVec4::sSelect(ioIndex, i2, c2); + + // Pass 3, test 2nd vs 3rd component + Vec4 v3 = ioValue.Swizzle(); + UVec4 i3 = ioIndex.Swizzle(); + UVec4 c3 = sGreater(ioValue, v3).Swizzle(); + ioValue = sSelect(ioValue, v3, c3); + ioIndex = UVec4::sSelect(ioIndex, i3, c3); +} + +bool Vec4::operator == (Vec4Arg inV2) const +{ + return sEquals(*this, inV2).TestAllTrue(); +} + +bool Vec4::IsClose(Vec4Arg inV2, float inMaxDistSq) const +{ + return (inV2 - *this).LengthSq() <= inMaxDistSq; +} + +bool Vec4::IsNormalized(float inTolerance) const +{ + return abs(LengthSq() - 1.0f) <= inTolerance; +} + +bool Vec4::IsNaN() const +{ +#if defined(JPH_USE_AVX512) + return _mm_fpclass_ps_mask(mValue, 0b10000001) != 0; +#elif defined(JPH_USE_SSE) + return _mm_movemask_ps(_mm_cmpunord_ps(mValue, mValue)) != 0; +#elif defined(JPH_USE_NEON) + uint32x4_t is_equal = vceqq_f32(mValue, mValue); // If a number is not equal to itself it's a NaN + return vaddvq_u32(vshrq_n_u32(is_equal, 31)) != 4; +#else + return isnan(mF32[0]) || isnan(mF32[1]) || isnan(mF32[2]) || isnan(mF32[3]); +#endif +} + +Vec4 Vec4::operator * (Vec4Arg inV2) const +{ +#if defined(JPH_USE_SSE) + return _mm_mul_ps(mValue, inV2.mValue); +#elif defined(JPH_USE_NEON) + return vmulq_f32(mValue, inV2.mValue); +#else + return Vec4(mF32[0] * inV2.mF32[0], + mF32[1] * inV2.mF32[1], + mF32[2] * inV2.mF32[2], + mF32[3] * inV2.mF32[3]); +#endif +} + +Vec4 Vec4::operator * (float inV2) const +{ +#if defined(JPH_USE_SSE) + return _mm_mul_ps(mValue, _mm_set1_ps(inV2)); +#elif defined(JPH_USE_NEON) + return vmulq_n_f32(mValue, inV2); +#else + return Vec4(mF32[0] * inV2, mF32[1] * inV2, mF32[2] * inV2, mF32[3] * inV2); +#endif +} + +/// Multiply vector with float +Vec4 operator * (float inV1, Vec4Arg inV2) +{ +#if defined(JPH_USE_SSE) + return _mm_mul_ps(_mm_set1_ps(inV1), inV2.mValue); +#elif defined(JPH_USE_NEON) + return vmulq_n_f32(inV2.mValue, inV1); +#else + return Vec4(inV1 * inV2.mF32[0], + inV1 * inV2.mF32[1], + inV1 * inV2.mF32[2], + inV1 * inV2.mF32[3]); +#endif +} + +Vec4 Vec4::operator / (float inV2) const +{ +#if defined(JPH_USE_SSE) + return _mm_div_ps(mValue, _mm_set1_ps(inV2)); +#elif defined(JPH_USE_NEON) + return vdivq_f32(mValue, vdupq_n_f32(inV2)); +#else + return Vec4(mF32[0] / inV2, mF32[1] / inV2, mF32[2] / inV2, mF32[3] / inV2); +#endif +} + +Vec4 &Vec4::operator *= (float inV2) +{ +#if defined(JPH_USE_SSE) + mValue = _mm_mul_ps(mValue, _mm_set1_ps(inV2)); +#elif defined(JPH_USE_NEON) + mValue = vmulq_n_f32(mValue, inV2); +#else + for (int i = 0; i < 4; ++i) + mF32[i] *= inV2; +#endif + return *this; +} + +Vec4 &Vec4::operator *= (Vec4Arg inV2) +{ +#if defined(JPH_USE_SSE) + mValue = _mm_mul_ps(mValue, inV2.mValue); +#elif defined(JPH_USE_NEON) + mValue = vmulq_f32(mValue, inV2.mValue); +#else + for (int i = 0; i < 4; ++i) + mF32[i] *= inV2.mF32[i]; +#endif + return *this; +} + +Vec4 &Vec4::operator /= (float inV2) +{ +#if defined(JPH_USE_SSE) + mValue = _mm_div_ps(mValue, _mm_set1_ps(inV2)); +#elif defined(JPH_USE_NEON) + mValue = vdivq_f32(mValue, vdupq_n_f32(inV2)); +#else + for (int i = 0; i < 4; ++i) + mF32[i] /= inV2; +#endif + return *this; +} + +Vec4 Vec4::operator + (Vec4Arg inV2) const +{ +#if defined(JPH_USE_SSE) + return _mm_add_ps(mValue, inV2.mValue); +#elif defined(JPH_USE_NEON) + return vaddq_f32(mValue, inV2.mValue); +#else + return Vec4(mF32[0] + inV2.mF32[0], + mF32[1] + inV2.mF32[1], + mF32[2] + inV2.mF32[2], + mF32[3] + inV2.mF32[3]); +#endif +} + +Vec4 &Vec4::operator += (Vec4Arg inV2) +{ +#if defined(JPH_USE_SSE) + mValue = _mm_add_ps(mValue, inV2.mValue); +#elif defined(JPH_USE_NEON) + mValue = vaddq_f32(mValue, inV2.mValue); +#else + for (int i = 0; i < 4; ++i) + mF32[i] += inV2.mF32[i]; +#endif + return *this; +} + +Vec4 Vec4::operator - () const +{ +#if defined(JPH_USE_SSE) + return _mm_sub_ps(_mm_setzero_ps(), mValue); +#elif defined(JPH_USE_NEON) + return vnegq_f32(mValue); +#else + return Vec4(-mF32[0], -mF32[1], -mF32[2], -mF32[3]); +#endif +} + +Vec4 Vec4::operator - (Vec4Arg inV2) const +{ +#if defined(JPH_USE_SSE) + return _mm_sub_ps(mValue, inV2.mValue); +#elif defined(JPH_USE_NEON) + return vsubq_f32(mValue, inV2.mValue); +#else + return Vec4(mF32[0] - inV2.mF32[0], + mF32[1] - inV2.mF32[1], + mF32[2] - inV2.mF32[2], + mF32[3] - inV2.mF32[3]); +#endif +} + +Vec4 &Vec4::operator -= (Vec4Arg inV2) +{ +#if defined(JPH_USE_SSE) + mValue = _mm_sub_ps(mValue, inV2.mValue); +#elif defined(JPH_USE_NEON) + mValue = vsubq_f32(mValue, inV2.mValue); +#else + for (int i = 0; i < 4; ++i) + mF32[i] -= inV2.mF32[i]; +#endif + return *this; +} + +Vec4 Vec4::operator / (Vec4Arg inV2) const +{ +#if defined(JPH_USE_SSE) + return _mm_div_ps(mValue, inV2.mValue); +#elif defined(JPH_USE_NEON) + return vdivq_f32(mValue, inV2.mValue); +#else + return Vec4(mF32[0] / inV2.mF32[0], + mF32[1] / inV2.mF32[1], + mF32[2] / inV2.mF32[2], + mF32[3] / inV2.mF32[3]); +#endif +} + +Vec4 Vec4::SplatX() const +{ +#if defined(JPH_USE_SSE) + return _mm_shuffle_ps(mValue, mValue, _MM_SHUFFLE(0, 0, 0, 0)); +#elif defined(JPH_USE_NEON) + return vdupq_laneq_f32(mValue, 0); +#else + return Vec4(mF32[0], mF32[0], mF32[0], mF32[0]); +#endif +} + +Vec4 Vec4::SplatY() const +{ +#if defined(JPH_USE_SSE) + return _mm_shuffle_ps(mValue, mValue, _MM_SHUFFLE(1, 1, 1, 1)); +#elif defined(JPH_USE_NEON) + return vdupq_laneq_f32(mValue, 1); +#else + return Vec4(mF32[1], mF32[1], mF32[1], mF32[1]); +#endif +} + +Vec4 Vec4::SplatZ() const +{ +#if defined(JPH_USE_SSE) + return _mm_shuffle_ps(mValue, mValue, _MM_SHUFFLE(2, 2, 2, 2)); +#elif defined(JPH_USE_NEON) + return vdupq_laneq_f32(mValue, 2); +#else + return Vec4(mF32[2], mF32[2], mF32[2], mF32[2]); +#endif +} + +Vec4 Vec4::SplatW() const +{ +#if defined(JPH_USE_SSE) + return _mm_shuffle_ps(mValue, mValue, _MM_SHUFFLE(3, 3, 3, 3)); +#elif defined(JPH_USE_NEON) + return vdupq_laneq_f32(mValue, 3); +#else + return Vec4(mF32[3], mF32[3], mF32[3], mF32[3]); +#endif +} + +Vec4 Vec4::Abs() const +{ +#if defined(JPH_USE_AVX512) + return _mm_range_ps(mValue, mValue, 0b1000); +#elif defined(JPH_USE_SSE) + return _mm_max_ps(_mm_sub_ps(_mm_setzero_ps(), mValue), mValue); +#elif defined(JPH_USE_NEON) + return vabsq_f32(mValue); +#else + return Vec4(abs(mF32[0]), abs(mF32[1]), abs(mF32[2]), abs(mF32[3])); +#endif +} + +Vec4 Vec4::Reciprocal() const +{ + return sReplicate(1.0f) / mValue; +} + +Vec4 Vec4::DotV(Vec4Arg inV2) const +{ +#if defined(JPH_USE_SSE4_1) + return _mm_dp_ps(mValue, inV2.mValue, 0xff); +#elif defined(JPH_USE_NEON) + float32x4_t mul = vmulq_f32(mValue, inV2.mValue); + return vdupq_n_f32(vaddvq_f32(mul)); +#else + // Brackets placed so that the order is consistent with the vectorized version + return Vec4::sReplicate((mF32[0] * inV2.mF32[0] + mF32[1] * inV2.mF32[1]) + (mF32[2] * inV2.mF32[2] + mF32[3] * inV2.mF32[3])); +#endif +} + +float Vec4::Dot(Vec4Arg inV2) const +{ +#if defined(JPH_USE_SSE4_1) + return _mm_cvtss_f32(_mm_dp_ps(mValue, inV2.mValue, 0xff)); +#elif defined(JPH_USE_NEON) + float32x4_t mul = vmulq_f32(mValue, inV2.mValue); + return vaddvq_f32(mul); +#else + // Brackets placed so that the order is consistent with the vectorized version + return (mF32[0] * inV2.mF32[0] + mF32[1] * inV2.mF32[1]) + (mF32[2] * inV2.mF32[2] + mF32[3] * inV2.mF32[3]); +#endif +} + +float Vec4::LengthSq() const +{ +#if defined(JPH_USE_SSE4_1) + return _mm_cvtss_f32(_mm_dp_ps(mValue, mValue, 0xff)); +#elif defined(JPH_USE_NEON) + float32x4_t mul = vmulq_f32(mValue, mValue); + return vaddvq_f32(mul); +#else + // Brackets placed so that the order is consistent with the vectorized version + return (mF32[0] * mF32[0] + mF32[1] * mF32[1]) + (mF32[2] * mF32[2] + mF32[3] * mF32[3]); +#endif +} + +float Vec4::Length() const +{ +#if defined(JPH_USE_SSE4_1) + return _mm_cvtss_f32(_mm_sqrt_ss(_mm_dp_ps(mValue, mValue, 0xff))); +#elif defined(JPH_USE_NEON) + float32x4_t mul = vmulq_f32(mValue, mValue); + float32x2_t sum = vdup_n_f32(vaddvq_f32(mul)); + return vget_lane_f32(vsqrt_f32(sum), 0); +#else + // Brackets placed so that the order is consistent with the vectorized version + return sqrt((mF32[0] * mF32[0] + mF32[1] * mF32[1]) + (mF32[2] * mF32[2] + mF32[3] * mF32[3])); +#endif +} + +Vec4 Vec4::Sqrt() const +{ +#if defined(JPH_USE_SSE) + return _mm_sqrt_ps(mValue); +#elif defined(JPH_USE_NEON) + return vsqrtq_f32(mValue); +#else + return Vec4(sqrt(mF32[0]), sqrt(mF32[1]), sqrt(mF32[2]), sqrt(mF32[3])); +#endif +} + + +Vec4 Vec4::GetSign() const +{ +#if defined(JPH_USE_AVX512) + return _mm_fixupimm_ps(mValue, mValue, _mm_set1_epi32(0xA9A90A00), 0); +#elif defined(JPH_USE_SSE) + Type minus_one = _mm_set1_ps(-1.0f); + Type one = _mm_set1_ps(1.0f); + return _mm_or_ps(_mm_and_ps(mValue, minus_one), one); +#elif defined(JPH_USE_NEON) + Type minus_one = vdupq_n_f32(-1.0f); + Type one = vdupq_n_f32(1.0f); + return vorrq_s32(vandq_s32(mValue, minus_one), one); +#else + return Vec4(signbit(mF32[0])? -1.0f : 1.0f, + signbit(mF32[1])? -1.0f : 1.0f, + signbit(mF32[2])? -1.0f : 1.0f, + signbit(mF32[3])? -1.0f : 1.0f); +#endif +} + +Vec4 Vec4::Normalized() const +{ +#if defined(JPH_USE_SSE4_1) + return _mm_div_ps(mValue, _mm_sqrt_ps(_mm_dp_ps(mValue, mValue, 0xff))); +#elif defined(JPH_USE_NEON) + float32x4_t mul = vmulq_f32(mValue, mValue); + float32x4_t sum = vdupq_n_f32(vaddvq_f32(mul)); + return vdivq_f32(mValue, vsqrtq_f32(sum)); +#else + return *this / Length(); +#endif +} + +void Vec4::StoreFloat4(Float4 *outV) const +{ +#if defined(JPH_USE_SSE) + _mm_storeu_ps(&outV->x, mValue); +#elif defined(JPH_USE_NEON) + vst1q_f32(&outV->x, mValue); +#else + for (int i = 0; i < 4; ++i) + (&outV->x)[i] = mF32[i]; +#endif +} + +UVec4 Vec4::ToInt() const +{ +#if defined(JPH_USE_SSE) + return _mm_cvttps_epi32(mValue); +#elif defined(JPH_USE_NEON) + return vcvtq_u32_f32(mValue); +#else + return UVec4(uint32(mF32[0]), uint32(mF32[1]), uint32(mF32[2]), uint32(mF32[3])); +#endif +} + +UVec4 Vec4::ReinterpretAsInt() const +{ +#if defined(JPH_USE_SSE) + return UVec4(_mm_castps_si128(mValue)); +#elif defined(JPH_USE_NEON) + return vreinterpretq_u32_f32(mValue); +#else + return *reinterpret_cast(this); +#endif +} + +int Vec4::GetSignBits() const +{ +#if defined(JPH_USE_SSE) + return _mm_movemask_ps(mValue); +#elif defined(JPH_USE_NEON) + int32x4_t shift = JPH_NEON_INT32x4(0, 1, 2, 3); + return vaddvq_u32(vshlq_u32(vshrq_n_u32(vreinterpretq_u32_f32(mValue), 31), shift)); +#else + return (signbit(mF32[0])? 1 : 0) | (signbit(mF32[1])? 2 : 0) | (signbit(mF32[2])? 4 : 0) | (signbit(mF32[3])? 8 : 0); +#endif +} + +float Vec4::ReduceMin() const +{ + Vec4 v = sMin(mValue, Swizzle()); + v = sMin(v, v.Swizzle()); + return v.GetX(); +} + +float Vec4::ReduceMax() const +{ + Vec4 v = sMax(mValue, Swizzle()); + v = sMax(v, v.Swizzle()); + return v.GetX(); +} + +void Vec4::SinCos(Vec4 &outSin, Vec4 &outCos) const +{ + // Implementation based on sinf.c from the cephes library, combines sinf and cosf in a single function, changes octants to quadrants and vectorizes it + // Original implementation by Stephen L. Moshier (See: http://www.moshier.net/) + + // Make argument positive and remember sign for sin only since cos is symmetric around x (highest bit of a float is the sign bit) + UVec4 sin_sign = UVec4::sAnd(ReinterpretAsInt(), UVec4::sReplicate(0x80000000U)); + Vec4 x = Vec4::sXor(*this, sin_sign.ReinterpretAsFloat()); + + // x / (PI / 2) rounded to nearest int gives us the quadrant closest to x + UVec4 quadrant = (0.6366197723675814f * x + Vec4::sReplicate(0.5f)).ToInt(); + + // Make x relative to the closest quadrant. + // This does x = x - quadrant * PI / 2 using a two step Cody-Waite argument reduction. + // This improves the accuracy of the result by avoiding loss of significant bits in the subtraction. + // We start with x = x - quadrant * PI / 2, PI / 2 in hexadecimal notation is 0x3fc90fdb, we remove the lowest 16 bits to + // get 0x3fc90000 (= 1.5703125) this means we can now multiply with a number of up to 2^16 without losing any bits. + // This leaves us with: x = (x - quadrant * 1.5703125) - quadrant * (PI / 2 - 1.5703125). + // PI / 2 - 1.5703125 in hexadecimal is 0x39fdaa22, stripping the lowest 12 bits we get 0x39fda000 (= 0.0004837512969970703125) + // This leaves uw with: x = ((x - quadrant * 1.5703125) - quadrant * 0.0004837512969970703125) - quadrant * (PI / 2 - 1.5703125 - 0.0004837512969970703125) + // See: https://stackoverflow.com/questions/42455143/sine-cosine-modular-extended-precision-arithmetic + // After this we have x in the range [-PI / 4, PI / 4]. + Vec4 float_quadrant = quadrant.ToFloat(); + x = ((x - float_quadrant * 1.5703125f) - float_quadrant * 0.0004837512969970703125f) - float_quadrant * 7.549789948768648e-8f; + + // Calculate x2 = x^2 + Vec4 x2 = x * x; + + // Taylor expansion: + // Cos(x) = 1 - x^2/2! + x^4/4! - x^6/6! + x^8/8! + ... = (((x2/8!- 1/6!) * x2 + 1/4!) * x2 - 1/2!) * x2 + 1 + Vec4 taylor_cos = ((2.443315711809948e-5f * x2 - Vec4::sReplicate(1.388731625493765e-3f)) * x2 + Vec4::sReplicate(4.166664568298827e-2f)) * x2 * x2 - 0.5f * x2 + Vec4::sReplicate(1.0f); + // Sin(x) = x - x^3/3! + x^5/5! - x^7/7! + ... = ((-x2/7! + 1/5!) * x2 - 1/3!) * x2 * x + x + Vec4 taylor_sin = ((-1.9515295891e-4f * x2 + Vec4::sReplicate(8.3321608736e-3f)) * x2 - Vec4::sReplicate(1.6666654611e-1f)) * x2 * x + x; + + // The lowest 2 bits of quadrant indicate the quadrant that we are in. + // Let x be the original input value and x' our value that has been mapped to the range [-PI / 4, PI / 4]. + // since cos(x) = sin(x - PI / 2) and since we want to use the Taylor expansion as close as possible to 0, + // we can alternate between using the Taylor expansion for sin and cos according to the following table: + // + // quadrant sin(x) cos(x) + // XXX00b sin(x') cos(x') + // XXX01b cos(x') -sin(x') + // XXX10b -sin(x') -cos(x') + // XXX11b -cos(x') sin(x') + // + // So: sin_sign = bit2, cos_sign = bit1 ^ bit2, bit1 determines if we use sin or cos Taylor expansion + UVec4 bit1 = quadrant.LogicalShiftLeft<31>(); + UVec4 bit2 = UVec4::sAnd(quadrant.LogicalShiftLeft<30>(), UVec4::sReplicate(0x80000000U)); + + // Select which one of the results is sin and which one is cos + Vec4 s = Vec4::sSelect(taylor_sin, taylor_cos, bit1); + Vec4 c = Vec4::sSelect(taylor_cos, taylor_sin, bit1); + + // Update the signs + sin_sign = UVec4::sXor(sin_sign, bit2); + UVec4 cos_sign = UVec4::sXor(bit1, bit2); + + // Correct the signs + outSin = Vec4::sXor(s, sin_sign.ReinterpretAsFloat()); + outCos = Vec4::sXor(c, cos_sign.ReinterpretAsFloat()); +} + +Vec4 Vec4::Tan() const +{ + // Implementation based on tanf.c from the cephes library, see Vec4::SinCos for further details + // Original implementation by Stephen L. Moshier (See: http://www.moshier.net/) + + // Make argument positive + UVec4 tan_sign = UVec4::sAnd(ReinterpretAsInt(), UVec4::sReplicate(0x80000000U)); + Vec4 x = Vec4::sXor(*this, tan_sign.ReinterpretAsFloat()); + + // x / (PI / 2) rounded to nearest int gives us the quadrant closest to x + UVec4 quadrant = (0.6366197723675814f * x + Vec4::sReplicate(0.5f)).ToInt(); + + // Remap x to range [-PI / 4, PI / 4], see Vec4::SinCos + Vec4 float_quadrant = quadrant.ToFloat(); + x = ((x - float_quadrant * 1.5703125f) - float_quadrant * 0.0004837512969970703125f) - float_quadrant * 7.549789948768648e-8f; + + // Calculate x2 = x^2 + Vec4 x2 = x * x; + + // Roughly equivalent to the Taylor expansion: + // Tan(x) = x + x^3/3 + 2*x^5/15 + 17*x^7/315 + 62*x^9/2835 + ... + Vec4 tan = + (((((9.38540185543e-3f * x2 + Vec4::sReplicate(3.11992232697e-3f)) * x2 + Vec4::sReplicate(2.44301354525e-2f)) * x2 + + Vec4::sReplicate(5.34112807005e-2f)) * x2 + Vec4::sReplicate(1.33387994085e-1f)) * x2 + Vec4::sReplicate(3.33331568548e-1f)) * x2 * x + x; + + // For the 2nd and 4th quadrant we need to invert the value + UVec4 bit1 = quadrant.LogicalShiftLeft<31>(); + tan = Vec4::sSelect(tan, Vec4::sReplicate(-1.0f) / (tan JPH_IF_FLOATING_POINT_EXCEPTIONS_ENABLED(+ Vec4::sReplicate(FLT_MIN))), bit1); // Add small epsilon to prevent div by zero, works because tan is always positive + + // Put the sign back + return Vec4::sXor(tan, tan_sign.ReinterpretAsFloat()); +} + +Vec4 Vec4::ASin() const +{ + // Implementation based on asinf.c from the cephes library + // Original implementation by Stephen L. Moshier (See: http://www.moshier.net/) + + // Make argument positive + UVec4 asin_sign = UVec4::sAnd(ReinterpretAsInt(), UVec4::sReplicate(0x80000000U)); + Vec4 a = Vec4::sXor(*this, asin_sign.ReinterpretAsFloat()); + + // ASin is not defined outside the range [-1, 1] but it often happens that a value is slightly above 1 so we just clamp here + a = Vec4::sMin(a, Vec4::sReplicate(1.0f)); + + // When |x| <= 0.5 we use the asin approximation as is + Vec4 z1 = a * a; + Vec4 x1 = a; + + // When |x| > 0.5 we use the identity asin(x) = PI / 2 - 2 * asin(sqrt((1 - x) / 2)) + Vec4 z2 = 0.5f * (Vec4::sReplicate(1.0f) - a); + Vec4 x2 = z2.Sqrt(); + + // Select which of the two situations we have + UVec4 greater = Vec4::sGreater(a, Vec4::sReplicate(0.5f)); + Vec4 z = Vec4::sSelect(z1, z2, greater); + Vec4 x = Vec4::sSelect(x1, x2, greater); + + // Polynomial approximation of asin + z = ((((4.2163199048e-2f * z + Vec4::sReplicate(2.4181311049e-2f)) * z + Vec4::sReplicate(4.5470025998e-2f)) * z + Vec4::sReplicate(7.4953002686e-2f)) * z + Vec4::sReplicate(1.6666752422e-1f)) * z * x + x; + + // If |x| > 0.5 we need to apply the remainder of the identity above + z = Vec4::sSelect(z, Vec4::sReplicate(0.5f * JPH_PI) - (z + z), greater); + + // Put the sign back + return Vec4::sXor(z, asin_sign.ReinterpretAsFloat()); +} + +Vec4 Vec4::ACos() const +{ + // Not the most accurate, but simple + return Vec4::sReplicate(0.5f * JPH_PI) - ASin(); +} + +Vec4 Vec4::ATan() const +{ + // Implementation based on atanf.c from the cephes library + // Original implementation by Stephen L. Moshier (See: http://www.moshier.net/) + + // Make argument positive + UVec4 atan_sign = UVec4::sAnd(ReinterpretAsInt(), UVec4::sReplicate(0x80000000U)); + Vec4 x = Vec4::sXor(*this, atan_sign.ReinterpretAsFloat()); + Vec4 y = Vec4::sZero(); + + // If x > Tan(PI / 8) + UVec4 greater1 = Vec4::sGreater(x, Vec4::sReplicate(0.4142135623730950f)); + Vec4 x1 = (x - Vec4::sReplicate(1.0f)) / (x + Vec4::sReplicate(1.0f)); + + // If x > Tan(3 * PI / 8) + UVec4 greater2 = Vec4::sGreater(x, Vec4::sReplicate(2.414213562373095f)); + Vec4 x2 = Vec4::sReplicate(-1.0f) / (x JPH_IF_FLOATING_POINT_EXCEPTIONS_ENABLED(+ Vec4::sReplicate(FLT_MIN))); // Add small epsilon to prevent div by zero, works because x is always positive + + // Apply first if + x = Vec4::sSelect(x, x1, greater1); + y = Vec4::sSelect(y, Vec4::sReplicate(0.25f * JPH_PI), greater1); + + // Apply second if + x = Vec4::sSelect(x, x2, greater2); + y = Vec4::sSelect(y, Vec4::sReplicate(0.5f * JPH_PI), greater2); + + // Polynomial approximation + Vec4 z = x * x; + y += (((8.05374449538e-2f * z - Vec4::sReplicate(1.38776856032e-1f)) * z + Vec4::sReplicate(1.99777106478e-1f)) * z - Vec4::sReplicate(3.33329491539e-1f)) * z * x + x; + + // Put the sign back + return Vec4::sXor(y, atan_sign.ReinterpretAsFloat()); +} + +Vec4 Vec4::sATan2(Vec4Arg inY, Vec4Arg inX) +{ + UVec4 sign_mask = UVec4::sReplicate(0x80000000U); + + // Determine absolute value and sign of y + UVec4 y_sign = UVec4::sAnd(inY.ReinterpretAsInt(), sign_mask); + Vec4 y_abs = Vec4::sXor(inY, y_sign.ReinterpretAsFloat()); + + // Determine absolute value and sign of x + UVec4 x_sign = UVec4::sAnd(inX.ReinterpretAsInt(), sign_mask); + Vec4 x_abs = Vec4::sXor(inX, x_sign.ReinterpretAsFloat()); + + // Always divide smallest / largest to avoid dividing by zero + UVec4 x_is_numerator = Vec4::sLess(x_abs, y_abs); + Vec4 numerator = Vec4::sSelect(y_abs, x_abs, x_is_numerator); + Vec4 denominator = Vec4::sSelect(x_abs, y_abs, x_is_numerator); + Vec4 atan = (numerator / denominator).ATan(); + + // If we calculated x / y instead of y / x the result is PI / 2 - result (note that this is true because we know the result is positive because the input was positive) + atan = Vec4::sSelect(atan, Vec4::sReplicate(0.5f * JPH_PI) - atan, x_is_numerator); + + // Now we need to map to the correct quadrant + // x_sign y_sign result + // +1 +1 atan + // -1 +1 -atan + PI + // -1 -1 atan - PI + // +1 -1 -atan + // This can be written as: x_sign * y_sign * (atan - (x_sign < 0? PI : 0)) + atan -= Vec4::sAnd(x_sign.ArithmeticShiftRight<31>().ReinterpretAsFloat(), Vec4::sReplicate(JPH_PI)); + atan = Vec4::sXor(atan, UVec4::sXor(x_sign, y_sign).ReinterpretAsFloat()); + return atan; +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Vec8.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Vec8.h new file mode 100644 index 00000000000..639d0415cb3 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Vec8.h @@ -0,0 +1,112 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +class [[nodiscard]] Vec8 +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Constructor + Vec8() = default; ///< Intentionally not initialized for performance reasons + Vec8(const Vec8 &inRHS) = default; + JPH_INLINE Vec8(__m256 inRHS) : mValue(inRHS) { } + + /// Set 256 bit vector from 2 128 bit vectors + JPH_INLINE Vec8(Vec4Arg inLo, Vec4Arg inHi); + + /// Vector with all zeros + static JPH_INLINE Vec8 sZero(); + + /// Replicate across all components + static JPH_INLINE Vec8 sReplicate(float inV); + + /// Replicate the X component of inV to all components + static JPH_INLINE Vec8 sSplatX(Vec4Arg inV); + + /// Replicate the Y component of inV to all components + static JPH_INLINE Vec8 sSplatY(Vec4Arg inV); + + /// Replicate the Z component of inV to all components + static JPH_INLINE Vec8 sSplatZ(Vec4Arg inV); + + /// Calculates inMul1 * inMul2 + inAdd + static JPH_INLINE Vec8 sFusedMultiplyAdd(Vec8Arg inMul1, Vec8Arg inMul2, Vec8Arg inAdd); + + /// Component wise select, returns inV1 when highest bit of inControl = 0 and inV2 when highest bit of inControl = 1 + static JPH_INLINE Vec8 sSelect(Vec8Arg inV1, Vec8Arg inV2, UVec8Arg inControl); + + /// Component wise min + static JPH_INLINE Vec8 sMin(Vec8Arg inV1, Vec8Arg inV2); + + /// Component wise max + static JPH_INLINE Vec8 sMax(Vec8Arg inV1, Vec8Arg inV2); + + /// Less than + static JPH_INLINE UVec8 sLess(Vec8Arg inV1, Vec8Arg inV2); + + /// Greater than + static JPH_INLINE UVec8 sGreater(Vec8Arg inV1, Vec8Arg inV2); + + /// Load from memory + static JPH_INLINE Vec8 sLoadFloat8(const float *inV); + + /// Load 8 floats from memory, 32 bytes aligned + static JPH_INLINE Vec8 sLoadFloat8Aligned(const float *inV); + + /// Get float component by index + JPH_INLINE float operator [] (uint inCoordinate) const { JPH_ASSERT(inCoordinate < 8); return mF32[inCoordinate]; } + JPH_INLINE float & operator [] (uint inCoordinate) { JPH_ASSERT(inCoordinate < 8); return mF32[inCoordinate]; } + + /// Multiply two float vectors + JPH_INLINE Vec8 operator * (Vec8Arg inV2) const; + + /// Multiply vector by float + JPH_INLINE Vec8 operator * (float inV2) const; + + /// Add two float vectors + JPH_INLINE Vec8 operator + (Vec8Arg inV2) const; + + /// Subtract two float vectors + JPH_INLINE Vec8 operator - (Vec8Arg inV2) const; + + /// Divide + JPH_INLINE Vec8 operator / (Vec8Arg inV2) const; + + /// Reciprocal vector + JPH_INLINE Vec8 Reciprocal() const; + + /// 256 bit variant of Vec::Swizzle (no cross 128 bit lane swizzle) + template + JPH_INLINE Vec8 Swizzle() const; + + /// Get absolute value of all components + JPH_INLINE Vec8 Abs() const; + + /// Fetch the lower 128 bit from a 256 bit variable + JPH_INLINE Vec4 LowerVec4() const; + + /// Fetch the higher 128 bit from a 256 bit variable + JPH_INLINE Vec4 UpperVec4() const; + + /// Get the minimum value of the 8 floats + JPH_INLINE float ReduceMin() const; + + union + { + __m256 mValue; + float mF32[8]; + }; +}; + +static_assert(is_trivial(), "Is supposed to be a trivial type!"); + +JPH_NAMESPACE_END + +#include "Vec8.inl" diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Vec8.inl b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Vec8.inl new file mode 100644 index 00000000000..f4d462ad051 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Vec8.inl @@ -0,0 +1,148 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +JPH_NAMESPACE_BEGIN + +Vec8::Vec8(Vec4Arg inLo, Vec4Arg inHi) : + mValue(_mm256_insertf128_ps(_mm256_castps128_ps256(inLo.mValue), inHi.mValue, 1)) +{ +} + +Vec8 Vec8::sZero() +{ + return _mm256_setzero_ps(); +} + +Vec8 Vec8::sReplicate(float inV) +{ + return _mm256_set1_ps(inV); +} + +Vec8 Vec8::sSplatX(Vec4Arg inV) +{ + return _mm256_set1_ps(inV.GetX()); +} + +Vec8 Vec8::sSplatY(Vec4Arg inV) +{ + return _mm256_set1_ps(inV.GetY()); +} + +Vec8 Vec8::sSplatZ(Vec4Arg inV) +{ + return _mm256_set1_ps(inV.GetZ()); +} + +Vec8 Vec8::sFusedMultiplyAdd(Vec8Arg inMul1, Vec8Arg inMul2, Vec8Arg inAdd) +{ +#ifdef JPH_USE_FMADD + return _mm256_fmadd_ps(inMul1.mValue, inMul2.mValue, inAdd.mValue); +#else + return _mm256_add_ps(_mm256_mul_ps(inMul1.mValue, inMul2.mValue), inAdd.mValue); +#endif +} + +Vec8 Vec8::sSelect(Vec8Arg inV1, Vec8Arg inV2, UVec8Arg inControl) +{ + return _mm256_blendv_ps(inV1.mValue, inV2.mValue, _mm256_castsi256_ps(inControl.mValue)); +} + +Vec8 Vec8::sMin(Vec8Arg inV1, Vec8Arg inV2) +{ + return _mm256_min_ps(inV1.mValue, inV2.mValue); +} + +Vec8 Vec8::sMax(Vec8Arg inV1, Vec8Arg inV2) +{ + return _mm256_max_ps(inV1.mValue, inV2.mValue); +} + +UVec8 Vec8::sLess(Vec8Arg inV1, Vec8Arg inV2) +{ + return _mm256_castps_si256(_mm256_cmp_ps(inV1.mValue, inV2.mValue, _CMP_LT_OQ)); +} + +UVec8 Vec8::sGreater(Vec8Arg inV1, Vec8Arg inV2) +{ + return _mm256_castps_si256(_mm256_cmp_ps(inV1.mValue, inV2.mValue, _CMP_GT_OQ)); +} + +Vec8 Vec8::sLoadFloat8(const float *inV) +{ + return _mm256_loadu_ps(inV); +} + +Vec8 Vec8::sLoadFloat8Aligned(const float *inV) +{ + return _mm256_load_ps(inV); +} + +Vec8 Vec8::operator * (Vec8Arg inV2) const +{ + return _mm256_mul_ps(mValue, inV2.mValue); +} + +Vec8 Vec8::operator * (float inV2) const +{ + return _mm256_mul_ps(mValue, _mm256_set1_ps(inV2)); +} + +Vec8 Vec8::operator + (Vec8Arg inV2) const +{ + return _mm256_add_ps(mValue, inV2.mValue); +} + +Vec8 Vec8::operator - (Vec8Arg inV2) const +{ + return _mm256_sub_ps(mValue, inV2.mValue); +} + +Vec8 Vec8::operator / (Vec8Arg inV2) const +{ + return _mm256_div_ps(mValue, inV2.mValue); +} + +Vec8 Vec8::Reciprocal() const +{ + return Vec8::sReplicate(1.0f) / mValue; +} + +template +Vec8 Vec8::Swizzle() const +{ + static_assert(SwizzleX <= 3, "SwizzleX template parameter out of range"); + static_assert(SwizzleY <= 3, "SwizzleY template parameter out of range"); + static_assert(SwizzleZ <= 3, "SwizzleZ template parameter out of range"); + static_assert(SwizzleW <= 3, "SwizzleW template parameter out of range"); + + return _mm256_shuffle_ps(mValue, mValue, _MM_SHUFFLE(SwizzleW, SwizzleZ, SwizzleY, SwizzleX)); +} + +Vec8 Vec8::Abs() const +{ +#if defined(JPH_USE_AVX512) + return _mm256_range_ps(mValue, mValue, 0b1000); +#else + return _mm256_max_ps(_mm256_sub_ps(_mm256_setzero_ps(), mValue), mValue); +#endif +} + +Vec4 Vec8::LowerVec4() const +{ + return _mm256_castps256_ps128(mValue); +} + +Vec4 Vec8::UpperVec4() const +{ + return _mm256_extractf128_ps(mValue, 1); +} + +float Vec8::ReduceMin() const +{ + return Vec4::sMin(LowerVec4(), UpperVec4()).ReduceMin(); +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Vector.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Vector.h new file mode 100644 index 00000000000..5880a2c87c8 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Vector.h @@ -0,0 +1,216 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +JPH_NAMESPACE_BEGIN + +/// Templatized vector class +template +class [[nodiscard]] Vector +{ +public: + /// Constructor + inline Vector() = default; + inline Vector(const Vector &inRHS) { *this = inRHS; } + + /// Dimensions + inline uint GetRows() const { return Rows; } + + /// Vector with all zeros + inline void SetZero() + { + for (uint r = 0; r < Rows; ++r) + mF32[r] = 0.0f; + } + + inline static Vector sZero() { Vector v; v.SetZero(); return v; } + + /// Copy a (part) of another vector into this vector + template + void CopyPart(const OtherVector &inV, uint inSourceRow, uint inNumRows, uint inDestRow) + { + for (uint r = 0; r < inNumRows; ++r) + mF32[inDestRow + r] = inV[inSourceRow + r]; + } + + /// Get float component by index + inline float operator [] (uint inCoordinate) const + { + JPH_ASSERT(inCoordinate < Rows); + return mF32[inCoordinate]; + } + + inline float & operator [] (uint inCoordinate) + { + JPH_ASSERT(inCoordinate < Rows); + return mF32[inCoordinate]; + } + + /// Comparison + inline bool operator == (const Vector &inV2) const + { + for (uint r = 0; r < Rows; ++r) + if (mF32[r] != inV2.mF32[r]) + return false; + return true; + } + + inline bool operator != (const Vector &inV2) const + { + for (uint r = 0; r < Rows; ++r) + if (mF32[r] != inV2.mF32[r]) + return true; + return false; + } + + /// Test if vector consists of all zeros + inline bool IsZero() const + { + for (uint r = 0; r < Rows; ++r) + if (mF32[r] != 0.0f) + return false; + return true; + } + + /// Test if two vectors are close to each other + inline bool IsClose(const Vector &inV2, float inMaxDistSq = 1.0e-12f) + { + return (inV2 - *this).LengthSq() <= inMaxDistSq; + } + + /// Assignment + inline Vector & operator = (const Vector &inV2) + { + for (uint r = 0; r < Rows; ++r) + mF32[r] = inV2.mF32[r]; + return *this; + } + + /// Multiply vector with float + inline Vector operator * (const float inV2) const + { + Vector v; + for (uint r = 0; r < Rows; ++r) + v.mF32[r] = mF32[r] * inV2; + return v; + } + + inline Vector & operator *= (const float inV2) + { + for (uint r = 0; r < Rows; ++r) + mF32[r] *= inV2; + return *this; + } + + /// Multiply vector with float + inline friend Vector operator * (const float inV1, const Vector &inV2) + { + return inV2 * inV1; + } + + /// Divide vector by float + inline Vector operator / (float inV2) const + { + Vector v; + for (uint r = 0; r < Rows; ++r) + v.mF32[r] = mF32[r] / inV2; + return v; + } + + inline Vector & operator /= (float inV2) + { + for (uint r = 0; r < Rows; ++r) + mF32[r] /= inV2; + return *this; + } + + /// Add two float vectors (component wise) + inline Vector operator + (const Vector &inV2) const + { + Vector v; + for (uint r = 0; r < Rows; ++r) + v.mF32[r] = mF32[r] + inV2.mF32[r]; + return v; + } + + inline Vector & operator += (const Vector &inV2) + { + for (uint r = 0; r < Rows; ++r) + mF32[r] += inV2.mF32[r]; + return *this; + } + + /// Negate + inline Vector operator - () const + { + Vector v; + for (uint r = 0; r < Rows; ++r) + v.mF32[r] = -mF32[r]; + return v; + } + + /// Subtract two float vectors (component wise) + inline Vector operator - (const Vector &inV2) const + { + Vector v; + for (uint r = 0; r < Rows; ++r) + v.mF32[r] = mF32[r] - inV2.mF32[r]; + return v; + } + + inline Vector & operator -= (const Vector &inV2) + { + for (uint r = 0; r < Rows; ++r) + mF32[r] -= inV2.mF32[r]; + return *this; + } + + /// Dot product + inline float Dot(const Vector &inV2) const + { + float dot = 0.0f; + for (uint r = 0; r < Rows; ++r) + dot += mF32[r] * inV2.mF32[r]; + return dot; + } + + /// Squared length of vector + inline float LengthSq() const + { + return Dot(*this); + } + + /// Length of vector + inline float Length() const + { + return sqrt(LengthSq()); + } + + /// Check if vector is normalized + inline bool IsNormalized(float inToleranceSq = 1.0e-6f) + { + return abs(LengthSq() - 1.0f) <= inToleranceSq; + } + + /// Normalize vector + inline Vector Normalized() const + { + return *this / Length(); + } + + /// To String + friend ostream & operator << (ostream &inStream, const Vector &inV) + { + inStream << "["; + for (uint i = 0; i < Rows - 1; ++i) + inStream << inV.mF32[i] << ", "; + inStream << inV.mF32[Rows - 1] << "]"; + return inStream; + } + + float mF32[Rows]; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/GetPrimitiveTypeOfType.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/GetPrimitiveTypeOfType.h new file mode 100644 index 00000000000..15431c76ea0 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/GetPrimitiveTypeOfType.h @@ -0,0 +1,54 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +/// Helper functions to get the underlying RTTI type of a type (so e.g. Array will return sometype) +template +const RTTI *GetPrimitiveTypeOfType(T *) +{ + return GetRTTIOfType((T *)nullptr); +} + +template +const RTTI *GetPrimitiveTypeOfType(T **) +{ + return GetRTTIOfType((T *)nullptr); +} + +template +const RTTI *GetPrimitiveTypeOfType(Ref *) +{ + return GetRTTIOfType((T *)nullptr); +} + +template +const RTTI *GetPrimitiveTypeOfType(RefConst *) +{ + return GetRTTIOfType((T *)nullptr); +} + +template +const RTTI *GetPrimitiveTypeOfType(Array *) +{ + return GetPrimitiveTypeOfType((T *)nullptr); +} + +template +const RTTI *GetPrimitiveTypeOfType(StaticArray *) +{ + return GetPrimitiveTypeOfType((T *)nullptr); +} + +template +const RTTI *GetPrimitiveTypeOfType(T (*)[N]) +{ + return GetPrimitiveTypeOfType((T *)nullptr); +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStream.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStream.cpp new file mode 100644 index 00000000000..18d952a67dc --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStream.cpp @@ -0,0 +1,34 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include + +JPH_NAMESPACE_BEGIN + +// Define macro to declare functions for a specific primitive type +#define JPH_DECLARE_PRIMITIVE(name) \ + bool OSIsType(name *, int inArrayDepth, EOSDataType inDataType, const char *inClassName) \ + { \ + return inArrayDepth == 0 && inDataType == EOSDataType::T_##name; \ + } \ + bool OSReadData(IObjectStreamIn &ioStream, name &outPrimitive) \ + { \ + return ioStream.ReadPrimitiveData(outPrimitive); \ + } \ + void OSWriteDataType(IObjectStreamOut &ioStream, name *) \ + { \ + ioStream.WriteDataType(EOSDataType::T_##name); \ + } \ + void OSWriteData(IObjectStreamOut &ioStream, const name &inPrimitive) \ + { \ + ioStream.HintNextItem(); \ + ioStream.WritePrimitiveData(inPrimitive); \ + } + +// This file uses the JPH_DECLARE_PRIMITIVE macro to define all types +#include + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStream.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStream.h new file mode 100644 index 00000000000..e539cce83d8 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStream.h @@ -0,0 +1,329 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +/// Base class for object stream input and output streams. +class JPH_EXPORT ObjectStream : public NonCopyable +{ +public: + /// Stream type + enum class EStreamType + { + Text, + Binary, + }; + +protected: + /// Destructor + virtual ~ObjectStream() = default; + + /// Identifier for objects + using Identifier = uint32; + + static constexpr int sVersion = 1; + static constexpr int sRevision = 0; + static constexpr Identifier sNullIdentifier = 0; +}; + +/// Interface class for reading from an object stream +class JPH_EXPORT IObjectStreamIn : public ObjectStream +{ +public: + ///@name Input type specific operations + virtual bool ReadDataType(EOSDataType &outType) = 0; + virtual bool ReadName(String &outName) = 0; + virtual bool ReadIdentifier(Identifier &outIdentifier) = 0; + virtual bool ReadCount(uint32 &outCount) = 0; + + ///@name Read primitives + virtual bool ReadPrimitiveData(uint8 &outPrimitive) = 0; + virtual bool ReadPrimitiveData(uint16 &outPrimitive) = 0; + virtual bool ReadPrimitiveData(int &outPrimitive) = 0; + virtual bool ReadPrimitiveData(uint32 &outPrimitive) = 0; + virtual bool ReadPrimitiveData(uint64 &outPrimitive) = 0; + virtual bool ReadPrimitiveData(float &outPrimitive) = 0; + virtual bool ReadPrimitiveData(double &outPrimitive) = 0; + virtual bool ReadPrimitiveData(bool &outPrimitive) = 0; + virtual bool ReadPrimitiveData(String &outPrimitive) = 0; + virtual bool ReadPrimitiveData(Float3 &outPrimitive) = 0; + virtual bool ReadPrimitiveData(Double3 &outPrimitive) = 0; + virtual bool ReadPrimitiveData(Vec3 &outPrimitive) = 0; + virtual bool ReadPrimitiveData(DVec3 &outPrimitive) = 0; + virtual bool ReadPrimitiveData(Vec4 &outPrimitive) = 0; + virtual bool ReadPrimitiveData(Quat &outPrimitive) = 0; + virtual bool ReadPrimitiveData(Mat44 &outPrimitive) = 0; + virtual bool ReadPrimitiveData(DMat44 &outPrimitive) = 0; + + ///@name Read compounds + virtual bool ReadClassData(const char *inClassName, void *inInstance) = 0; + virtual bool ReadPointerData(const RTTI *inRTTI, void **inPointer, int inRefCountOffset = -1) = 0; +}; + +/// Interface class for writing to an object stream +class JPH_EXPORT IObjectStreamOut : public ObjectStream +{ +public: + ///@name Output type specific operations + virtual void WriteDataType(EOSDataType inType) = 0; + virtual void WriteName(const char *inName) = 0; + virtual void WriteIdentifier(Identifier inIdentifier) = 0; + virtual void WriteCount(uint32 inCount) = 0; + + ///@name Write primitives + virtual void WritePrimitiveData(const uint8 &inPrimitive) = 0; + virtual void WritePrimitiveData(const uint16 &inPrimitive) = 0; + virtual void WritePrimitiveData(const int &inPrimitive) = 0; + virtual void WritePrimitiveData(const uint32 &inPrimitive) = 0; + virtual void WritePrimitiveData(const uint64 &inPrimitive) = 0; + virtual void WritePrimitiveData(const float &inPrimitive) = 0; + virtual void WritePrimitiveData(const double &inPrimitive) = 0; + virtual void WritePrimitiveData(const bool &inPrimitive) = 0; + virtual void WritePrimitiveData(const String &inPrimitive) = 0; + virtual void WritePrimitiveData(const Float3 &inPrimitive) = 0; + virtual void WritePrimitiveData(const Double3 &inPrimitive) = 0; + virtual void WritePrimitiveData(const Vec3 &inPrimitive) = 0; + virtual void WritePrimitiveData(const DVec3 &inPrimitive) = 0; + virtual void WritePrimitiveData(const Vec4 &inPrimitive) = 0; + virtual void WritePrimitiveData(const Quat &inPrimitive) = 0; + virtual void WritePrimitiveData(const Mat44 &inPrimitive) = 0; + virtual void WritePrimitiveData(const DMat44 &inPrimitive) = 0; + + ///@name Write compounds + virtual void WritePointerData(const RTTI *inRTTI, const void *inPointer) = 0; + virtual void WriteClassData(const RTTI *inRTTI, const void *inInstance) = 0; + + ///@name Layout hints (for text output) + virtual void HintNextItem() { /* Default is do nothing */ } + virtual void HintIndentUp() { /* Default is do nothing */ } + virtual void HintIndentDown() { /* Default is do nothing */ } +}; + +// Define macro to declare functions for a specific primitive type +#define JPH_DECLARE_PRIMITIVE(name) \ + JPH_EXPORT bool OSIsType(name *, int inArrayDepth, EOSDataType inDataType, const char *inClassName); \ + JPH_EXPORT bool OSReadData(IObjectStreamIn &ioStream, name &outPrimitive); \ + JPH_EXPORT void OSWriteDataType(IObjectStreamOut &ioStream, name *); \ + JPH_EXPORT void OSWriteData(IObjectStreamOut &ioStream, const name &inPrimitive); + +// This file uses the JPH_DECLARE_PRIMITIVE macro to define all types +#include + +// Define serialization templates +template +bool OSIsType(Array *, int inArrayDepth, EOSDataType inDataType, const char *inClassName) +{ + return (inArrayDepth > 0 && OSIsType(static_cast(nullptr), inArrayDepth - 1, inDataType, inClassName)); +} + +template +bool OSIsType(StaticArray *, int inArrayDepth, EOSDataType inDataType, const char *inClassName) +{ + return (inArrayDepth > 0 && OSIsType(static_cast(nullptr), inArrayDepth - 1, inDataType, inClassName)); +} + +template +bool OSIsType(T (*)[N], int inArrayDepth, EOSDataType inDataType, const char *inClassName) +{ + return (inArrayDepth > 0 && OSIsType(static_cast(nullptr), inArrayDepth - 1, inDataType, inClassName)); +} + +template +bool OSIsType(Ref *, int inArrayDepth, EOSDataType inDataType, const char *inClassName) +{ + return OSIsType(static_cast(nullptr), inArrayDepth, inDataType, inClassName); +} + +template +bool OSIsType(RefConst *, int inArrayDepth, EOSDataType inDataType, const char *inClassName) +{ + return OSIsType(static_cast(nullptr), inArrayDepth, inDataType, inClassName); +} + +/// Define serialization templates for dynamic arrays +template +bool OSReadData(IObjectStreamIn &ioStream, Array &inArray) +{ + bool continue_reading = true; + + // Read array length + uint32 array_length; + continue_reading = ioStream.ReadCount(array_length); + + // Read array items + if (continue_reading) + { + inArray.clear(); + inArray.resize(array_length); + for (uint32 el = 0; el < array_length && continue_reading; ++el) + continue_reading = OSReadData(ioStream, inArray[el]); + } + + return continue_reading; +} + +/// Define serialization templates for static arrays +template +bool OSReadData(IObjectStreamIn &ioStream, StaticArray &inArray) +{ + bool continue_reading = true; + + // Read array length + uint32 array_length; + continue_reading = ioStream.ReadCount(array_length); + + // Check if we can fit this many elements + if (array_length > N) + return false; + + // Read array items + if (continue_reading) + { + inArray.clear(); + inArray.resize(array_length); + for (uint32 el = 0; el < array_length && continue_reading; ++el) + continue_reading = OSReadData(ioStream, inArray[el]); + } + + return continue_reading; +} + +/// Define serialization templates for C style arrays +template +bool OSReadData(IObjectStreamIn &ioStream, T (&inArray)[N]) +{ + bool continue_reading = true; + + // Read array length + uint32 array_length; + continue_reading = ioStream.ReadCount(array_length); + if (array_length != N) + return false; + + // Read array items + for (uint32 el = 0; el < N && continue_reading; ++el) + continue_reading = OSReadData(ioStream, inArray[el]); + + return continue_reading; +} + +/// Define serialization templates for references +template +bool OSReadData(IObjectStreamIn &ioStream, Ref &inRef) +{ + return ioStream.ReadPointerData(JPH_RTTI(T), inRef.InternalGetPointer(), T::sInternalGetRefCountOffset()); +} + +template +bool OSReadData(IObjectStreamIn &ioStream, RefConst &inRef) +{ + return ioStream.ReadPointerData(JPH_RTTI(T), inRef.InternalGetPointer(), T::sInternalGetRefCountOffset()); +} + +// Define serialization templates for dynamic arrays +template +void OSWriteDataType(IObjectStreamOut &ioStream, Array *) +{ + ioStream.WriteDataType(EOSDataType::Array); + OSWriteDataType(ioStream, static_cast(nullptr)); +} + +template +void OSWriteData(IObjectStreamOut &ioStream, const Array &inArray) +{ + // Write size of array + ioStream.HintNextItem(); + ioStream.WriteCount(static_cast(inArray.size())); + + // Write data in array + ioStream.HintIndentUp(); + for (const T &v : inArray) + OSWriteData(ioStream, v); + ioStream.HintIndentDown(); +} + +/// Define serialization templates for static arrays +template +void OSWriteDataType(IObjectStreamOut &ioStream, StaticArray *) +{ + ioStream.WriteDataType(EOSDataType::Array); + OSWriteDataType(ioStream, static_cast(nullptr)); +} + +template +void OSWriteData(IObjectStreamOut &ioStream, const StaticArray &inArray) +{ + // Write size of array + ioStream.HintNextItem(); + ioStream.WriteCount(inArray.size()); + + // Write data in array + ioStream.HintIndentUp(); + for (const typename StaticArray::value_type &v : inArray) + OSWriteData(ioStream, v); + ioStream.HintIndentDown(); +} + +/// Define serialization templates for C style arrays +template +void OSWriteDataType(IObjectStreamOut &ioStream, T (*)[N]) +{ + ioStream.WriteDataType(EOSDataType::Array); + OSWriteDataType(ioStream, static_cast(nullptr)); +} + +template +void OSWriteData(IObjectStreamOut &ioStream, const T (&inArray)[N]) +{ + // Write size of array + ioStream.HintNextItem(); + ioStream.WriteCount(uint32(N)); + + // Write data in array + ioStream.HintIndentUp(); + for (const T &v : inArray) + OSWriteData(ioStream, v); + ioStream.HintIndentDown(); +} + +/// Define serialization templates for references +template +void OSWriteDataType(IObjectStreamOut &ioStream, Ref *) +{ + OSWriteDataType(ioStream, static_cast(nullptr)); +} + +template +void OSWriteData(IObjectStreamOut &ioStream, const Ref &inRef) +{ + if (inRef != nullptr) + ioStream.WritePointerData(GetRTTI(inRef.GetPtr()), inRef.GetPtr()); + else + ioStream.WritePointerData(nullptr, nullptr); +} + +template +void OSWriteDataType(IObjectStreamOut &ioStream, RefConst *) +{ + OSWriteDataType(ioStream, static_cast(nullptr)); +} + +template +void OSWriteData(IObjectStreamOut &ioStream, const RefConst &inRef) +{ + if (inRef != nullptr) + ioStream.WritePointerData(GetRTTI(inRef.GetPtr()), inRef.GetPtr()); + else + ioStream.WritePointerData(nullptr, nullptr); +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamBinaryIn.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamBinaryIn.cpp new file mode 100644 index 00000000000..3f37e861064 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamBinaryIn.cpp @@ -0,0 +1,230 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include + +JPH_NAMESPACE_BEGIN + +ObjectStreamBinaryIn::ObjectStreamBinaryIn(istream &inStream) : + ObjectStreamIn(inStream) +{ +} + +bool ObjectStreamBinaryIn::ReadDataType(EOSDataType &outType) +{ + uint32 type; + mStream.read((char *)&type, sizeof(type)); + if (mStream.fail()) return false; + outType = (EOSDataType)type; + return true; +} + +bool ObjectStreamBinaryIn::ReadName(String &outName) +{ + return ReadPrimitiveData(outName); +} + +bool ObjectStreamBinaryIn::ReadIdentifier(Identifier &outIdentifier) +{ + Identifier id; + mStream.read((char *)&id, sizeof(id)); + if (mStream.fail()) return false; + outIdentifier = id; + return true; +} + +bool ObjectStreamBinaryIn::ReadCount(uint32 &outCount) +{ + uint32 count; + mStream.read((char *)&count, sizeof(count)); + if (mStream.fail()) return false; + outCount = count; + return true; +} + +bool ObjectStreamBinaryIn::ReadPrimitiveData(uint8 &outPrimitive) +{ + uint8 primitive; + mStream.read((char *)&primitive, sizeof(primitive)); + if (mStream.fail()) return false; + outPrimitive = primitive; + return true; +} + +bool ObjectStreamBinaryIn::ReadPrimitiveData(uint16 &outPrimitive) +{ + uint16 primitive; + mStream.read((char *)&primitive, sizeof(primitive)); + if (mStream.fail()) return false; + outPrimitive = primitive; + return true; +} + +bool ObjectStreamBinaryIn::ReadPrimitiveData(int &outPrimitive) +{ + int primitive; + mStream.read((char *)&primitive, sizeof(primitive)); + if (mStream.fail()) return false; + outPrimitive = primitive; + return true; +} + +bool ObjectStreamBinaryIn::ReadPrimitiveData(uint32 &outPrimitive) +{ + uint32 primitive; + mStream.read((char *)&primitive, sizeof(primitive)); + if (mStream.fail()) return false; + outPrimitive = primitive; + return true; +} + +bool ObjectStreamBinaryIn::ReadPrimitiveData(uint64 &outPrimitive) +{ + uint64 primitive; + mStream.read((char *)&primitive, sizeof(primitive)); + if (mStream.fail()) return false; + outPrimitive = primitive; + return true; +} + +bool ObjectStreamBinaryIn::ReadPrimitiveData(float &outPrimitive) +{ + float primitive; + mStream.read((char *)&primitive, sizeof(primitive)); + if (mStream.fail()) return false; + outPrimitive = primitive; + return true; +} + +bool ObjectStreamBinaryIn::ReadPrimitiveData(double &outPrimitive) +{ + double primitive; + mStream.read((char *)&primitive, sizeof(primitive)); + if (mStream.fail()) return false; + outPrimitive = primitive; + return true; +} + +bool ObjectStreamBinaryIn::ReadPrimitiveData(bool &outPrimitive) +{ + bool primitive; + mStream.read((char *)&primitive, sizeof(primitive)); + if (mStream.fail()) return false; + outPrimitive = primitive; + return true; +} + +bool ObjectStreamBinaryIn::ReadPrimitiveData(String &outPrimitive) +{ + // Read length or ID of string + uint32 len; + if (!ReadPrimitiveData(len)) + return false; + + // Check empty string + if (len == 0) + { + outPrimitive.clear(); + return true; + } + + // Check if it is an ID in the string table + if (len & 0x80000000) + { + StringTable::iterator i = mStringTable.find(len); + if (i == mStringTable.end()) + return false; + outPrimitive = i->second; + return true; + } + + // Read the string + char *data = (char *)JPH_STACK_ALLOC(len + 1); + mStream.read(data, len); + if (mStream.fail()) return false; + data[len] = 0; + outPrimitive = data; + + // Insert string in table + mStringTable.try_emplace(mNextStringID, outPrimitive); + mNextStringID++; + return true; +} + +bool ObjectStreamBinaryIn::ReadPrimitiveData(Float3 &outPrimitive) +{ + Float3 primitive; + mStream.read((char *)&primitive, sizeof(Float3)); + if (mStream.fail()) return false; + outPrimitive = primitive; + return true; +} + +bool ObjectStreamBinaryIn::ReadPrimitiveData(Double3 &outPrimitive) +{ + Double3 primitive; + mStream.read((char *)&primitive, sizeof(Double3)); + if (mStream.fail()) return false; + outPrimitive = primitive; + return true; +} + +bool ObjectStreamBinaryIn::ReadPrimitiveData(Vec3 &outPrimitive) +{ + Float3 primitive; + mStream.read((char *)&primitive, sizeof(Float3)); + if (mStream.fail()) return false; + outPrimitive = Vec3(primitive); // Use Float3 constructor so that we initialize W too + return true; +} + +bool ObjectStreamBinaryIn::ReadPrimitiveData(DVec3 &outPrimitive) +{ + Double3 primitive; + mStream.read((char *)&primitive, sizeof(Double3)); + if (mStream.fail()) return false; + outPrimitive = DVec3(primitive); // Use Float3 constructor so that we initialize W too + return true; +} + +bool ObjectStreamBinaryIn::ReadPrimitiveData(Vec4 &outPrimitive) +{ + Vec4 primitive; + mStream.read((char *)&primitive, sizeof(primitive)); + if (mStream.fail()) return false; + outPrimitive = primitive; + return true; +} + +bool ObjectStreamBinaryIn::ReadPrimitiveData(Quat &outPrimitive) +{ + Quat primitive; + mStream.read((char *)&primitive, sizeof(primitive)); + if (mStream.fail()) return false; + outPrimitive = primitive; + return true; +} + +bool ObjectStreamBinaryIn::ReadPrimitiveData(Mat44 &outPrimitive) +{ + Mat44 primitive; + mStream.read((char *)&primitive, sizeof(primitive)); + if (mStream.fail()) return false; + outPrimitive = primitive; + return true; +} + +bool ObjectStreamBinaryIn::ReadPrimitiveData(DMat44 &outPrimitive) +{ + Vec4 c0, c1, c2; + DVec3 c3; + if (!ReadPrimitiveData(c0) || !ReadPrimitiveData(c1) || !ReadPrimitiveData(c2) || !ReadPrimitiveData(c3)) + return false; + outPrimitive = DMat44(c0, c1, c2, c3); + return true; +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamBinaryIn.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamBinaryIn.h new file mode 100644 index 00000000000..553282e62ef --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamBinaryIn.h @@ -0,0 +1,51 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +/// Implementation of ObjectStream binary input stream. +class JPH_EXPORT ObjectStreamBinaryIn : public ObjectStreamIn +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Constructor + explicit ObjectStreamBinaryIn(istream &inStream); + + ///@name Input type specific operations + virtual bool ReadDataType(EOSDataType &outType) override; + virtual bool ReadName(String &outName) override; + virtual bool ReadIdentifier(Identifier &outIdentifier) override; + virtual bool ReadCount(uint32 &outCount) override; + + virtual bool ReadPrimitiveData(uint8 &outPrimitive) override; + virtual bool ReadPrimitiveData(uint16 &outPrimitive) override; + virtual bool ReadPrimitiveData(int &outPrimitive) override; + virtual bool ReadPrimitiveData(uint32 &outPrimitive) override; + virtual bool ReadPrimitiveData(uint64 &outPrimitive) override; + virtual bool ReadPrimitiveData(float &outPrimitive) override; + virtual bool ReadPrimitiveData(double &outPrimitive) override; + virtual bool ReadPrimitiveData(bool &outPrimitive) override; + virtual bool ReadPrimitiveData(String &outPrimitive) override; + virtual bool ReadPrimitiveData(Float3 &outPrimitive) override; + virtual bool ReadPrimitiveData(Double3 &outPrimitive) override; + virtual bool ReadPrimitiveData(Vec3 &outPrimitive) override; + virtual bool ReadPrimitiveData(DVec3 &outPrimitive) override; + virtual bool ReadPrimitiveData(Vec4 &outPrimitive) override; + virtual bool ReadPrimitiveData(Quat &outPrimitive) override; + virtual bool ReadPrimitiveData(Mat44 &outPrimitive) override; + virtual bool ReadPrimitiveData(DMat44 &outPrimitive) override; + +private: + using StringTable = UnorderedMap; + + StringTable mStringTable; + uint32 mNextStringID = 0x80000000; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamBinaryOut.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamBinaryOut.cpp new file mode 100644 index 00000000000..83f584c1cdc --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamBinaryOut.cpp @@ -0,0 +1,150 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include + +JPH_NAMESPACE_BEGIN + +ObjectStreamBinaryOut::ObjectStreamBinaryOut(ostream &inStream) : + ObjectStreamOut(inStream) +{ + String header; + header = StringFormat("BOS%2d.%02d", ObjectStream::sVersion, ObjectStream::sRevision); + mStream.write(header.c_str(), header.size()); +} + +void ObjectStreamBinaryOut::WriteDataType(EOSDataType inType) +{ + mStream.write((const char *)&inType, sizeof(inType)); +} + +void ObjectStreamBinaryOut::WriteName(const char *inName) +{ + WritePrimitiveData(String(inName)); +} + +void ObjectStreamBinaryOut::WriteIdentifier(Identifier inIdentifier) +{ + mStream.write((const char *)&inIdentifier, sizeof(inIdentifier)); +} + +void ObjectStreamBinaryOut::WriteCount(uint32 inCount) +{ + mStream.write((const char *)&inCount, sizeof(inCount)); +} + +void ObjectStreamBinaryOut::WritePrimitiveData(const uint8 &inPrimitive) +{ + mStream.write((const char *)&inPrimitive, sizeof(inPrimitive)); +} + +void ObjectStreamBinaryOut::WritePrimitiveData(const uint16 &inPrimitive) +{ + mStream.write((const char *)&inPrimitive, sizeof(inPrimitive)); +} + +void ObjectStreamBinaryOut::WritePrimitiveData(const int &inPrimitive) +{ + mStream.write((const char *)&inPrimitive, sizeof(inPrimitive)); +} + +void ObjectStreamBinaryOut::WritePrimitiveData(const uint32 &inPrimitive) +{ + mStream.write((const char *)&inPrimitive, sizeof(inPrimitive)); +} + +void ObjectStreamBinaryOut::WritePrimitiveData(const uint64 &inPrimitive) +{ + mStream.write((const char *)&inPrimitive, sizeof(inPrimitive)); +} + +void ObjectStreamBinaryOut::WritePrimitiveData(const float &inPrimitive) +{ + mStream.write((const char *)&inPrimitive, sizeof(inPrimitive)); +} + +void ObjectStreamBinaryOut::WritePrimitiveData(const double &inPrimitive) +{ + mStream.write((const char *)&inPrimitive, sizeof(inPrimitive)); +} + +void ObjectStreamBinaryOut::WritePrimitiveData(const bool &inPrimitive) +{ + mStream.write((const char *)&inPrimitive, sizeof(inPrimitive)); +} + +void ObjectStreamBinaryOut::WritePrimitiveData(const String &inPrimitive) +{ + // Empty strings are trivial + if (inPrimitive.empty()) + { + WritePrimitiveData((uint32)0); + return; + } + + // Check if we've already written this string + StringTable::iterator i = mStringTable.find(inPrimitive); + if (i != mStringTable.end()) + { + WritePrimitiveData(i->second); + return; + } + + // Insert string in table + mStringTable.try_emplace(inPrimitive, mNextStringID); + mNextStringID++; + + // Write string + uint32 len = min((uint32)inPrimitive.size(), (uint32)0x7fffffff); + WritePrimitiveData(len); + mStream.write(inPrimitive.c_str(), len); +} + +void ObjectStreamBinaryOut::WritePrimitiveData(const Float3 &inPrimitive) +{ + mStream.write((const char *)&inPrimitive, sizeof(Float3)); +} + +void ObjectStreamBinaryOut::WritePrimitiveData(const Double3 &inPrimitive) +{ + mStream.write((const char *)&inPrimitive, sizeof(Double3)); +} + +void ObjectStreamBinaryOut::WritePrimitiveData(const Vec3 &inPrimitive) +{ + mStream.write((const char *)&inPrimitive, 3 * sizeof(float)); +} + +void ObjectStreamBinaryOut::WritePrimitiveData(const DVec3 &inPrimitive) +{ + mStream.write((const char *)&inPrimitive, 3 * sizeof(double)); +} + +void ObjectStreamBinaryOut::WritePrimitiveData(const Vec4 &inPrimitive) +{ + mStream.write((const char *)&inPrimitive, sizeof(inPrimitive)); +} + +void ObjectStreamBinaryOut::WritePrimitiveData(const Quat &inPrimitive) +{ + mStream.write((const char *)&inPrimitive, sizeof(inPrimitive)); +} + +void ObjectStreamBinaryOut::WritePrimitiveData(const Mat44 &inPrimitive) +{ + mStream.write((const char *)&inPrimitive, sizeof(inPrimitive)); +} + +void ObjectStreamBinaryOut::WritePrimitiveData(const DMat44 &inPrimitive) +{ + WritePrimitiveData(inPrimitive.GetColumn4(0)); + WritePrimitiveData(inPrimitive.GetColumn4(1)); + WritePrimitiveData(inPrimitive.GetColumn4(2)); + WritePrimitiveData(inPrimitive.GetTranslation()); +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamBinaryOut.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamBinaryOut.h new file mode 100644 index 00000000000..1c720512009 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamBinaryOut.h @@ -0,0 +1,51 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +/// Implementation of ObjectStream binary output stream. +class JPH_EXPORT ObjectStreamBinaryOut : public ObjectStreamOut +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Constructor and destructor + explicit ObjectStreamBinaryOut(ostream &inStream); + + ///@name Output type specific operations + virtual void WriteDataType(EOSDataType inType) override; + virtual void WriteName(const char *inName) override; + virtual void WriteIdentifier(Identifier inIdentifier) override; + virtual void WriteCount(uint32 inCount) override; + + virtual void WritePrimitiveData(const uint8 &inPrimitive) override; + virtual void WritePrimitiveData(const uint16 &inPrimitive) override; + virtual void WritePrimitiveData(const int &inPrimitive) override; + virtual void WritePrimitiveData(const uint32 &inPrimitive) override; + virtual void WritePrimitiveData(const uint64 &inPrimitive) override; + virtual void WritePrimitiveData(const float &inPrimitive) override; + virtual void WritePrimitiveData(const double &inPrimitive) override; + virtual void WritePrimitiveData(const bool &inPrimitive) override; + virtual void WritePrimitiveData(const String &inPrimitive) override; + virtual void WritePrimitiveData(const Float3 &inPrimitive) override; + virtual void WritePrimitiveData(const Double3 &inPrimitive) override; + virtual void WritePrimitiveData(const Vec3 &inPrimitive) override; + virtual void WritePrimitiveData(const DVec3 &inPrimitive) override; + virtual void WritePrimitiveData(const Vec4 &inPrimitive) override; + virtual void WritePrimitiveData(const Quat &inPrimitive) override; + virtual void WritePrimitiveData(const Mat44 &inPrimitive) override; + virtual void WritePrimitiveData(const DMat44 &inPrimitive) override; + +private: + using StringTable = UnorderedMap; + + StringTable mStringTable; + uint32 mNextStringID = 0x80000000; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamIn.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamIn.cpp new file mode 100644 index 00000000000..45ea2857b2a --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamIn.cpp @@ -0,0 +1,617 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +ObjectStreamIn::ObjectStreamIn(istream &inStream) : + mStream(inStream) +{ +} + +bool ObjectStreamIn::GetInfo(istream &inStream, EStreamType &outType, int &outVersion, int &outRevision) +{ + // Read header and check if it is the correct format, e.g. "TOS 1.00" + char header[9]; + memset(header, 0, 9); + inStream.read(header, 8); + if ((header[0] == 'B' || header[0] == 'T') && header[1] == 'O' && header[2] == 'S' + && (header[3] == ' ' || isdigit(header[3])) && isdigit(header[4]) + && header[5] == '.' && isdigit(header[6]) && isdigit(header[7])) + { + // Check if this is a binary or text objectfile + switch (header[0]) + { + case 'T': outType = ObjectStream::EStreamType::Text; break; + case 'B': outType = ObjectStream::EStreamType::Binary; break; + default: JPH_ASSERT(false); break; + } + + // Extract version and revision + header[5] = '\0'; + outVersion = atoi(&header[3]); + outRevision = atoi(&header[6]); + + return true; + } + + Trace("ObjectStreamIn: Not a valid object stream."); + return false; +} + +ObjectStreamIn *ObjectStreamIn::Open(istream &inStream) +{ + // Check if file is an ObjectStream of the correct version and revision + EStreamType type; + int version; + int revision; + if (GetInfo(inStream, type, version, revision)) + { + if (version == sVersion && revision == sRevision) + { + // Create an input stream of the correct type + switch (type) + { + case EStreamType::Text: return new ObjectStreamTextIn(inStream); + case EStreamType::Binary: return new ObjectStreamBinaryIn(inStream); + default: JPH_ASSERT(false); + } + } + else + { + Trace("ObjectStreamIn: Different version stream (%d.%02d, expected %d.%02d).", version, revision, sVersion, sRevision); + } + } + + return nullptr; +} + +void *ObjectStreamIn::Read(const RTTI *inRTTI) +{ + using ObjectSet = UnorderedSet; + + // Read all information on the stream + void *main_object = nullptr; + bool continue_reading = true; + for (;;) + { + // Get type of next operation + EOSDataType data_type; + if (!ReadDataType(data_type)) + break; + + if (data_type == EOSDataType::Declare) + { + // Read type declaration + if (!ReadRTTI()) + { + Trace("ObjectStreamIn: Fatal error while reading class description for class %s.", inRTTI->GetName()); + continue_reading = false; + break; + } + } + else if (data_type == EOSDataType::Object) + { + const RTTI *rtti; + void *object = ReadObject(rtti); + if (!main_object && object) + { + // This is the first and thus main object of the file. + if (rtti->IsKindOf(inRTTI)) + { + // Object is of correct type + main_object = object; + } + else + { + Trace("ObjectStreamIn: Main object of different type. Expected %s, but found %s.", inRTTI->GetName(), rtti->GetName()); + continue_reading = false; + break; + } + } + } + else + { + // Invalid or out of place token found + Trace("ObjectStreamIn: Invalid or out of place token found."); + continue_reading = false; + break; + } + } + + // Resolve links (pointer, references) + if (continue_reading) + { + // Resolve links + ObjectSet referenced_objects; + for (Link &link : mUnresolvedLinks) + { + IdentifierMap::const_iterator j = mIdentifierMap.find(link.mIdentifier); + if (j != mIdentifierMap.end() && j->second.mRTTI->IsKindOf(link.mRTTI)) + { + const ObjectInfo &obj_info = j->second; + + // Set pointer + *link.mPointer = obj_info.mInstance; + + // Increment refcount if it was a referencing pointer + if (link.mRefCountOffset != -1) + ++(*(uint32 *)(((uint8 *)obj_info.mInstance) + link.mRefCountOffset)); + + // Add referenced object to the list + if (referenced_objects.find(obj_info.mInstance) == referenced_objects.end()) + referenced_objects.insert(obj_info.mInstance); + } + else + { + // Referenced object not found, set pointer to nullptr + Trace("ObjectStreamIn: Setting incorrect pointer to class of type %s to nullptr.", link.mRTTI->GetName()); + *link.mPointer = nullptr; + } + } + + // Release unreferenced objects except the main object + for (const IdentifierMap::value_type &j : mIdentifierMap) + { + const ObjectInfo &obj_info = j.second; + + if (obj_info.mInstance != main_object) + { + ObjectSet::const_iterator k = referenced_objects.find(obj_info.mInstance); + if (k == referenced_objects.end()) + { + Trace("ObjectStreamIn: Releasing unreferenced object of type %s.", obj_info.mRTTI->GetName()); + obj_info.mRTTI->DestructObject(obj_info.mInstance); + } + } + } + + return main_object; + } + else + { + // Release all objects if a fatal error occurred + for (const IdentifierMap::value_type &i : mIdentifierMap) + { + const ObjectInfo &obj_info = i.second; + obj_info.mRTTI->DestructObject(obj_info.mInstance); + } + + return nullptr; + } +} + +void *ObjectStreamIn::ReadObject(const RTTI *& outRTTI) +{ + // Read the object class + void *object = nullptr; + String class_name; + if (ReadName(class_name)) + { + // Get class description + ClassDescriptionMap::iterator i = mClassDescriptionMap.find(class_name); + if (i != mClassDescriptionMap.end()) + { + const ClassDescription &class_desc = i->second; + + // Read object identifier + Identifier identifier; + if (ReadIdentifier(identifier)) + { + // Check if this object can be read or must be skipped + if (identifier != sNullIdentifier + && class_desc.mRTTI + && !class_desc.mRTTI->IsAbstract()) + { + // Create object instance + outRTTI = class_desc.mRTTI; + object = outRTTI->CreateObject(); + + // Read object attributes + if (ReadClassData(class_desc, object)) + { + // Add object to identifier map + mIdentifierMap.try_emplace(identifier, object, outRTTI); + } + else + { + // Fatal error while reading attributes, release object + outRTTI->DestructObject(object); + object = nullptr; + } + } + else + { + // Skip this object + // TODO: This operation can fail, but there is no check yet + Trace("ObjectStreamIn: Found uncreatable object %s.", class_name.c_str()); + ReadClassData(class_desc, nullptr); + } + } + } + else + { + // TODO: This is a fatal error, but this function has no way of indicating this + Trace("ObjectStreamIn: Found object of unknown class %s.", class_name.c_str()); + } + } + + return object; +} + +bool ObjectStreamIn::ReadRTTI() +{ + // Read class name and find it's attribute info + String class_name; + if (!ReadName(class_name)) + return false; + + // Find class + const RTTI *rtti = Factory::sInstance->Find(class_name.c_str()); + if (rtti == nullptr) + Trace("ObjectStreamIn: Unknown class: \"%s\".", class_name.c_str()); + + // Insert class description + ClassDescription &class_desc = mClassDescriptionMap.try_emplace(class_name, rtti).first->second; + + // Read the number of entries in the description + uint32 count; + if (!ReadCount(count)) + return false; + + // Read the entries + for (uint32 i = 0; i < count; ++i) + { + AttributeDescription attribute; + + // Read name + String attribute_name; + if (!ReadName(attribute_name)) + return false; + + // Read type + if (!ReadDataType(attribute.mSourceType)) + return false; + + // Read array depth + while (attribute.mSourceType == EOSDataType::Array) + { + ++attribute.mArrayDepth; + if (!ReadDataType(attribute.mSourceType)) + return false; + } + + // Read instance/pointer class name + if ((attribute.mSourceType == EOSDataType::Instance || attribute.mSourceType == EOSDataType::Pointer) + && !ReadName(attribute.mClassName)) + return false; + + // Find attribute in rtti + if (rtti) + { + // Find attribute index + for (int idx = 0; idx < rtti->GetAttributeCount(); ++idx) + { + const SerializableAttribute &attr = rtti->GetAttribute(idx); + if (strcmp(attr.GetName(), attribute_name.c_str()) == 0) + { + attribute.mIndex = idx; + break; + } + } + + // Check if attribute is of expected type + if (attribute.mIndex >= 0) + { + const SerializableAttribute &attr = rtti->GetAttribute(attribute.mIndex); + if (attr.IsType(attribute.mArrayDepth, attribute.mSourceType, attribute.mClassName.c_str())) + { + // No conversion needed + attribute.mDestinationType = attribute.mSourceType; + } + else if (attribute.mArrayDepth == 0 && attribute.mClassName.empty()) + { + // Try to apply type conversions + if (attribute.mSourceType == EOSDataType::T_Vec3 && attr.IsType(0, EOSDataType::T_DVec3, "")) + attribute.mDestinationType = EOSDataType::T_DVec3; + else if (attribute.mSourceType == EOSDataType::T_DVec3 && attr.IsType(0, EOSDataType::T_Vec3, "")) + attribute.mDestinationType = EOSDataType::T_Vec3; + else + attribute.mIndex = -1; + } + else + { + // No conversion exists + attribute.mIndex = -1; + } + } + } + + // Add attribute to the class description + class_desc.mAttributes.push_back(attribute); + } + + return true; +} + +bool ObjectStreamIn::ReadClassData(const char *inClassName, void *inInstance) +{ + // Find the class description + ClassDescriptionMap::iterator i = mClassDescriptionMap.find(inClassName); + if (i != mClassDescriptionMap.end()) + return ReadClassData(i->second, inInstance); + + return false; +} + +bool ObjectStreamIn::ReadClassData(const ClassDescription &inClassDesc, void *inInstance) +{ + // Read data for this class + bool continue_reading = true; + + for (const AttributeDescription &attr_desc : inClassDesc.mAttributes) + { + // Read or skip the attribute data + if (attr_desc.mIndex >= 0 && inInstance) + { + const SerializableAttribute &attr = inClassDesc.mRTTI->GetAttribute(attr_desc.mIndex); + if (attr_desc.mSourceType == attr_desc.mDestinationType) + { + continue_reading = attr.ReadData(*this, inInstance); + } + else if (attr_desc.mSourceType == EOSDataType::T_Vec3 && attr_desc.mDestinationType == EOSDataType::T_DVec3) + { + // Vec3 to DVec3 + Vec3 tmp; + continue_reading = ReadPrimitiveData(tmp); + if (continue_reading) + *attr.GetMemberPointer(inInstance) = DVec3(tmp); + } + else if (attr_desc.mSourceType == EOSDataType::T_DVec3 && attr_desc.mDestinationType == EOSDataType::T_Vec3) + { + // DVec3 to Vec3 + DVec3 tmp; + continue_reading = ReadPrimitiveData(tmp); + if (continue_reading) + *attr.GetMemberPointer(inInstance) = Vec3(tmp); + } + else + { + JPH_ASSERT(false); // Unknown conversion + continue_reading = SkipAttributeData(attr_desc.mArrayDepth, attr_desc.mSourceType, attr_desc.mClassName.c_str()); + } + } + else + continue_reading = SkipAttributeData(attr_desc.mArrayDepth, attr_desc.mSourceType, attr_desc.mClassName.c_str()); + + if (!continue_reading) + break; + } + + return continue_reading; +} + +bool ObjectStreamIn::ReadPointerData(const RTTI *inRTTI, void **inPointer, int inRefCountOffset) +{ + Identifier identifier; + if (ReadIdentifier(identifier)) + { + if (identifier == sNullIdentifier) + { + // Set nullptr pointer + inPointer = nullptr; + } + else + { + // Put pointer on the list to be resolved later on + Link &link = mUnresolvedLinks.emplace_back(); + link.mPointer = inPointer; + link.mRefCountOffset = inRefCountOffset; + link.mIdentifier = identifier; + link.mRTTI = inRTTI; + } + + return true; + } + + return false; +} + +bool ObjectStreamIn::SkipAttributeData(int inArrayDepth, EOSDataType inDataType, const char *inClassName) +{ + bool continue_reading = true; + + // Get number of items to read + uint32 count = 1; + for (; inArrayDepth > 0; --inArrayDepth) + { + uint32 temporary; + if (ReadCount(temporary)) + { + // Multiply for multi dimensional arrays + count *= temporary; + } + else + { + // Fatal error while reading array size + continue_reading = false; + break; + } + } + + // Read data for all items + if (continue_reading) + { + if (inDataType == EOSDataType::Instance) + { + // Get the class description + ClassDescriptionMap::iterator i = mClassDescriptionMap.find(inClassName); + if (i != mClassDescriptionMap.end()) + { + for (; count > 0 && continue_reading; --count) + continue_reading = ReadClassData(i->second, nullptr); + } + else + { + continue_reading = false; + Trace("ObjectStreamIn: Found instance of unknown class %s.", inClassName); + } + } + else + { + for (; count > 0 && continue_reading; --count) + { + switch (inDataType) + { + case EOSDataType::Pointer: + { + Identifier temporary; + continue_reading = ReadIdentifier(temporary); + break; + } + + case EOSDataType::T_uint8: + { + uint8 temporary; + continue_reading = ReadPrimitiveData(temporary); + break; + } + + case EOSDataType::T_uint16: + { + uint16 temporary; + continue_reading = ReadPrimitiveData(temporary); + break; + } + + case EOSDataType::T_int: + { + int temporary; + continue_reading = ReadPrimitiveData(temporary); + break; + } + + case EOSDataType::T_uint32: + { + uint32 temporary; + continue_reading = ReadPrimitiveData(temporary); + break; + } + + case EOSDataType::T_uint64: + { + uint64 temporary; + continue_reading = ReadPrimitiveData(temporary); + break; + } + + case EOSDataType::T_float: + { + float temporary; + continue_reading = ReadPrimitiveData(temporary); + break; + } + + case EOSDataType::T_double: + { + double temporary; + continue_reading = ReadPrimitiveData(temporary); + break; + } + + case EOSDataType::T_bool: + { + bool temporary; + continue_reading = ReadPrimitiveData(temporary); + break; + } + + case EOSDataType::T_String: + { + String temporary; + continue_reading = ReadPrimitiveData(temporary); + break; + } + + case EOSDataType::T_Float3: + { + Float3 temporary; + continue_reading = ReadPrimitiveData(temporary); + break; + } + + case EOSDataType::T_Double3: + { + Double3 temporary; + continue_reading = ReadPrimitiveData(temporary); + break; + } + + case EOSDataType::T_Vec3: + { + Vec3 temporary; + continue_reading = ReadPrimitiveData(temporary); + break; + } + + case EOSDataType::T_DVec3: + { + DVec3 temporary; + continue_reading = ReadPrimitiveData(temporary); + break; + } + + case EOSDataType::T_Vec4: + { + Vec4 temporary; + continue_reading = ReadPrimitiveData(temporary); + break; + } + + case EOSDataType::T_Quat: + { + Quat temporary; + continue_reading = ReadPrimitiveData(temporary); + break; + } + + case EOSDataType::T_Mat44: + { + Mat44 temporary; + continue_reading = ReadPrimitiveData(temporary); + break; + } + + case EOSDataType::T_DMat44: + { + DMat44 temporary; + continue_reading = ReadPrimitiveData(temporary); + break; + } + + case EOSDataType::Array: + case EOSDataType::Object: + case EOSDataType::Declare: + case EOSDataType::Instance: + case EOSDataType::Invalid: + default: + continue_reading = false; + break; + } + } + } + } + + return continue_reading; +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamIn.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamIn.h new file mode 100644 index 00000000000..542aa71e0f2 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamIn.h @@ -0,0 +1,144 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include +#include + +JPH_SUPPRESS_WARNINGS_STD_BEGIN +#include +JPH_SUPPRESS_WARNINGS_STD_END + +JPH_NAMESPACE_BEGIN + +/// ObjectStreamIn contains all logic for reading an object from disk. It is the base +/// class for the text and binary input streams (ObjectStreamTextIn and ObjectStreamBinaryIn). +class JPH_EXPORT ObjectStreamIn : public IObjectStreamIn +{ +private: + struct ClassDescription; + +public: + /// Main function to read an object from a stream + template + static bool sReadObject(istream &inStream, T *&outObject) + { + // Create the input stream + bool result = false; + ObjectStreamIn *stream = ObjectStreamIn::Open(inStream); + if (stream) + { + // Read the object + outObject = (T *)stream->Read(JPH_RTTI(T)); + result = (outObject != nullptr); + delete stream; + } + return result; + } + + /// Main function to read an object from a stream (reference counting pointer version) + template + static bool sReadObject(istream &inStream, Ref &outObject) + { + T *object = nullptr; + bool result = sReadObject(inStream, object); + outObject = object; + return result; + } + + /// Main function to read an object from a file + template + static bool sReadObject(const char *inFileName, T *&outObject) + { + std::ifstream stream; + stream.open(inFileName, std::ifstream::in | std::ifstream::binary); + if (!stream.is_open()) + return false; + return sReadObject(stream, outObject); + } + + /// Main function to read an object from a file (reference counting pointer version) + template + static bool sReadObject(const char *inFileName, Ref &outObject) + { + T *object = nullptr; + bool result = sReadObject(inFileName, object); + outObject = object; + return result; + } + + ////////////////////////////////////////////////////// + // EVERYTHING BELOW THIS SHOULD NOT DIRECTLY BE CALLED + ////////////////////////////////////////////////////// + + ///@name Serialization operations + void * Read(const RTTI *inRTTI); + void * ReadObject(const RTTI *& outRTTI); + bool ReadRTTI(); + virtual bool ReadClassData(const char *inClassName, void *inInstance) override; + bool ReadClassData(const ClassDescription &inClassDesc, void *inInstance); + virtual bool ReadPointerData(const RTTI *inRTTI, void **inPointer, int inRefCountOffset = -1) override; + bool SkipAttributeData(int inArrayDepth, EOSDataType inDataType, const char *inClassName); + +protected: + /// Constructor + explicit ObjectStreamIn(istream &inStream); + + /// Determine the type and version of an object stream + static bool GetInfo(istream &inStream, EStreamType &outType, int &outVersion, int &outRevision); + + /// Static constructor + static ObjectStreamIn * Open(istream &inStream); + + istream & mStream; + +private: + /// Class descriptions + struct AttributeDescription + { + int mArrayDepth = 0; + EOSDataType mSourceType = EOSDataType::Invalid; + EOSDataType mDestinationType = EOSDataType::Invalid; + String mClassName; + int mIndex = -1; + }; + + struct ClassDescription + { + ClassDescription() = default; + explicit ClassDescription(const RTTI *inRTTI) : mRTTI(inRTTI) { } + + const RTTI * mRTTI = nullptr; + Array mAttributes; + }; + + struct ObjectInfo + { + ObjectInfo() = default; + ObjectInfo(void *inInstance, const RTTI *inRTTI) : mInstance(inInstance), mRTTI(inRTTI) { } + + void * mInstance = nullptr; + const RTTI * mRTTI = nullptr; + }; + + struct Link + { + void ** mPointer; + int mRefCountOffset; + Identifier mIdentifier; + const RTTI * mRTTI; + }; + + using IdentifierMap = UnorderedMap; + using ClassDescriptionMap = UnorderedMap; + + ClassDescriptionMap mClassDescriptionMap; + IdentifierMap mIdentifierMap; ///< Links identifier to an object pointer + Array mUnresolvedLinks; ///< All pointers (links) are resolved after reading the entire file, e.g. when all object exist +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamOut.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamOut.cpp new file mode 100644 index 00000000000..a9401acc469 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamOut.cpp @@ -0,0 +1,164 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +ObjectStreamOut::ObjectStreamOut(ostream &inStream) : + mStream(inStream) +{ +// Add all primitives to the class set +#define JPH_DECLARE_PRIMITIVE(name) mClassSet.insert(JPH_RTTI(name)); +#include +} + +ObjectStreamOut *ObjectStreamOut::Open(EStreamType inType, ostream &inStream) +{ + switch (inType) + { + case EStreamType::Text: return new ObjectStreamTextOut(inStream); + case EStreamType::Binary: return new ObjectStreamBinaryOut(inStream); + default: JPH_ASSERT(false); + } + return nullptr; +} + +bool ObjectStreamOut::Write(const void *inObject, const RTTI *inRTTI) +{ + // Assign a new identifier to the object and write it + mIdentifierMap.try_emplace(inObject, mNextIdentifier, inRTTI); + mNextIdentifier++; + WriteObject(inObject); + + // Write all linked objects + while (!mObjectQueue.empty() && !mStream.fail()) + { + const void *linked_object = mObjectQueue.front(); + WriteObject(linked_object); + mObjectQueue.pop(); + } + return !mStream.fail(); +} + +void ObjectStreamOut::WriteObject(const void *inObject) +{ + // Find object identifier + IdentifierMap::iterator i = mIdentifierMap.find(inObject); + JPH_ASSERT(i != mIdentifierMap.end()); + + // Write class description and associated descriptions + QueueRTTI(i->second.mRTTI); + while (!mClassQueue.empty() && !mStream.fail()) + { + WriteRTTI(mClassQueue.front()); + mClassQueue.pop(); + } + + HintNextItem(); + HintNextItem(); + + // Write object header. + WriteDataType(EOSDataType::Object); + WriteName(i->second.mRTTI->GetName()); + WriteIdentifier(i->second.mIdentifier); + + // Write attribute data + WriteClassData(i->second.mRTTI, inObject); +} + +void ObjectStreamOut::QueueRTTI(const RTTI *inRTTI) +{ + ClassSet::const_iterator i = mClassSet.find(inRTTI); + if (i == mClassSet.end()) + { + mClassSet.insert(inRTTI); + mClassQueue.push(inRTTI); + } +} + +void ObjectStreamOut::WriteRTTI(const RTTI *inRTTI) +{ + HintNextItem(); + HintNextItem(); + + // Write class header. E.g. in text mode: "class " + WriteDataType(EOSDataType::Declare); + WriteName(inRTTI->GetName()); + WriteCount(inRTTI->GetAttributeCount()); + + // Write class attribute info + HintIndentUp(); + for (int attr_index = 0; attr_index < inRTTI->GetAttributeCount(); ++attr_index) + { + // Get attribute + const SerializableAttribute &attr = inRTTI->GetAttribute(attr_index); + + // Write definition of attribute class if undefined + const RTTI *rtti = attr.GetMemberPrimitiveType(); + if (rtti != nullptr) + QueueRTTI(rtti); + + HintNextItem(); + + // Write attribute information. + WriteName(attr.GetName()); + attr.WriteDataType(*this); + } + HintIndentDown(); +} + +void ObjectStreamOut::WriteClassData(const RTTI *inRTTI, const void *inInstance) +{ + JPH_ASSERT(inInstance); + + // Write attributes + HintIndentUp(); + for (int attr_index = 0; attr_index < inRTTI->GetAttributeCount(); ++attr_index) + { + // Get attribute + const SerializableAttribute &attr = inRTTI->GetAttribute(attr_index); + attr.WriteData(*this, inInstance); + } + HintIndentDown(); +} + +void ObjectStreamOut::WritePointerData(const RTTI *inRTTI, const void *inPointer) +{ + Identifier identifier; + + if (inPointer) + { + // Check if this object has an identifier + IdentifierMap::iterator i = mIdentifierMap.find(inPointer); + if (i != mIdentifierMap.end()) + { + // Object already has an identifier + identifier = i->second.mIdentifier; + } + else + { + // Assign a new identifier to this object and queue it for serialization + identifier = mNextIdentifier++; + mIdentifierMap.try_emplace(inPointer, identifier, inRTTI); + mObjectQueue.push(inPointer); + } + } + else + { + // Write nullptr pointer + identifier = sNullIdentifier; + } + + // Write the identifier + HintNextItem(); + WriteIdentifier(identifier); +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamOut.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamOut.h new file mode 100644 index 00000000000..e85ba5377ae --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamOut.h @@ -0,0 +1,100 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include +#include + +JPH_SUPPRESS_WARNINGS_STD_BEGIN +#include +#include +JPH_SUPPRESS_WARNINGS_STD_END + +JPH_NAMESPACE_BEGIN + +template using Queue = std::queue>>; + +/// ObjectStreamOut contains all logic for writing an object to disk. It is the base +/// class for the text and binary output streams (ObjectStreamTextOut and ObjectStreamBinaryOut). +class JPH_EXPORT ObjectStreamOut : public IObjectStreamOut +{ +private: + struct ObjectInfo; + +public: + /// Main function to write an object to a stream + template + static bool sWriteObject(ostream &inStream, ObjectStream::EStreamType inType, const T &inObject) + { + // Create the output stream + bool result = false; + ObjectStreamOut *stream = ObjectStreamOut::Open(inType, inStream); + if (stream) + { + // Write the object to the stream + result = stream->Write((void *)&inObject, GetRTTI(&inObject)); + delete stream; + } + + return result; + } + + /// Main function to write an object to a file + template + static bool sWriteObject(const char *inFileName, ObjectStream::EStreamType inType, const T &inObject) + { + std::ofstream stream; + stream.open(inFileName, std::ofstream::out | std::ofstream::trunc | std::ofstream::binary); + if (!stream.is_open()) + return false; + return sWriteObject(stream, inType, inObject); + } + + ////////////////////////////////////////////////////// + // EVERYTHING BELOW THIS SHOULD NOT DIRECTLY BE CALLED + ////////////////////////////////////////////////////// + + ///@name Serialization operations + bool Write(const void *inObject, const RTTI *inRTTI); + void WriteObject(const void *inObject); + void QueueRTTI(const RTTI *inRTTI); + void WriteRTTI(const RTTI *inRTTI); + virtual void WriteClassData(const RTTI *inRTTI, const void *inInstance) override; + virtual void WritePointerData(const RTTI *inRTTI, const void *inPointer) override; + +protected: + /// Static constructor + static ObjectStreamOut * Open(EStreamType inType, ostream &inStream); + + /// Constructor + explicit ObjectStreamOut(ostream &inStream); + + ostream & mStream; + +private: + struct ObjectInfo + { + ObjectInfo() : mIdentifier(0), mRTTI(nullptr) { } + ObjectInfo(Identifier inIdentifier, const RTTI *inRTTI) : mIdentifier(inIdentifier), mRTTI(inRTTI) { } + + Identifier mIdentifier; + const RTTI * mRTTI; + }; + + using IdentifierMap = UnorderedMap; + using ClassSet = UnorderedSet; + using ObjectQueue = Queue; + using ClassQueue = Queue; + + Identifier mNextIdentifier = sNullIdentifier + 1; ///< Next free identifier for this stream + IdentifierMap mIdentifierMap; ///< Links object pointer to an identifier + ObjectQueue mObjectQueue; ///< Queue of objects to be written + ClassSet mClassSet; ///< List of classes already written + ClassQueue mClassQueue; ///< List of classes waiting to be written +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamTextIn.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamTextIn.cpp new file mode 100644 index 00000000000..2f9eb383e5b --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamTextIn.cpp @@ -0,0 +1,392 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include + +JPH_NAMESPACE_BEGIN + +ObjectStreamTextIn::ObjectStreamTextIn(istream &inStream) : + ObjectStreamIn(inStream) +{ +} + +bool ObjectStreamTextIn::ReadDataType(EOSDataType &outType) +{ + String token; + if (ReadWord(token)) + { + transform(token.begin(), token.end(), token.begin(), [](char inValue) { return (char)tolower(inValue); }); + if (token == "declare") + outType = EOSDataType::Declare; + else if (token == "object") + outType = EOSDataType::Object; + else if (token == "instance") + outType = EOSDataType::Instance; + else if (token == "pointer") + outType = EOSDataType::Pointer; + else if (token == "array") + outType = EOSDataType::Array; + else if (token == "uint8") + outType = EOSDataType::T_uint8; + else if (token == "uint16") + outType = EOSDataType::T_uint16; + else if (token == "int") + outType = EOSDataType::T_int; + else if (token == "uint32") + outType = EOSDataType::T_uint32; + else if (token == "uint64") + outType = EOSDataType::T_uint64; + else if (token == "float") + outType = EOSDataType::T_float; + else if (token == "double") + outType = EOSDataType::T_double; + else if (token == "bool") + outType = EOSDataType::T_bool; + else if (token == "string") + outType = EOSDataType::T_String; + else if (token == "float3") + outType = EOSDataType::T_Float3; + else if (token == "double3") + outType = EOSDataType::T_Double3; + else if (token == "vec3") + outType = EOSDataType::T_Vec3; + else if (token == "dvec3") + outType = EOSDataType::T_DVec3; + else if (token == "vec4") + outType = EOSDataType::T_Vec4; + else if (token == "quat") + outType = EOSDataType::T_Quat; + else if (token == "mat44") + outType = EOSDataType::T_Mat44; + else if (token == "dmat44") + outType = EOSDataType::T_DMat44; + else + { + Trace("ObjectStreamTextIn: Found unknown data type."); + return false; + } + return true; + } + return false; +} + +bool ObjectStreamTextIn::ReadName(String &outName) +{ + return ReadWord(outName); +} + +bool ObjectStreamTextIn::ReadIdentifier(Identifier &outIdentifier) +{ + String token; + if (!ReadWord(token)) + return false; + outIdentifier = (uint32)std::strtoul(token.c_str(), nullptr, 16); + if (errno == ERANGE) + { + outIdentifier = sNullIdentifier; + return false; + } + return true; +} + +bool ObjectStreamTextIn::ReadCount(uint32 &outCount) +{ + return ReadPrimitiveData(outCount); +} + +bool ObjectStreamTextIn::ReadPrimitiveData(uint8 &outPrimitive) +{ + String token; + if (!ReadWord(token)) + return false; + uint32 temporary; + IStringStream stream(token); + stream >> temporary; + if (!stream.fail()) + { + outPrimitive = (uint8)temporary; + return true; + } + return false; +} + +bool ObjectStreamTextIn::ReadPrimitiveData(uint16 &outPrimitive) +{ + String token; + if (!ReadWord(token)) + return false; + uint32 temporary; + IStringStream stream(token); + stream >> temporary; + if (!stream.fail()) + { + outPrimitive = (uint16)temporary; + return true; + } + return false; +} + +bool ObjectStreamTextIn::ReadPrimitiveData(int &outPrimitive) +{ + String token; + if (!ReadWord(token)) + return false; + IStringStream stream(token); + stream >> outPrimitive; + return !stream.fail(); +} + +bool ObjectStreamTextIn::ReadPrimitiveData(uint32 &outPrimitive) +{ + String token; + if (!ReadWord(token)) + return false; + IStringStream stream(token); + stream >> outPrimitive; + return !stream.fail(); +} + +bool ObjectStreamTextIn::ReadPrimitiveData(uint64 &outPrimitive) +{ + String token; + if (!ReadWord(token)) + return false; + IStringStream stream(token); + stream >> outPrimitive; + return !stream.fail(); +} + +bool ObjectStreamTextIn::ReadPrimitiveData(float &outPrimitive) +{ + String token; + if (!ReadWord(token)) + return false; + IStringStream stream(token); + stream >> outPrimitive; + return !stream.fail(); +} + +bool ObjectStreamTextIn::ReadPrimitiveData(double &outPrimitive) +{ + String token; + if (!ReadWord(token)) + return false; + IStringStream stream(token); + stream >> outPrimitive; + return !stream.fail(); +} + +bool ObjectStreamTextIn::ReadPrimitiveData(bool &outPrimitive) +{ + String token; + if (!ReadWord(token)) + return false; + transform(token.begin(), token.end(), token.begin(), [](char inValue) { return (char)tolower(inValue); }); + outPrimitive = token == "true"; + return outPrimitive || token == "false"; +} + +bool ObjectStreamTextIn::ReadPrimitiveData(String &outPrimitive) +{ + outPrimitive.clear(); + + char c; + + // Skip whitespace + for (;;) + { + if (!ReadChar(c)) + return false; + + if (!isspace(c)) + break; + } + + // Check if it is a opening quote + if (c != '\"') + return false; + + // Read string and interpret special characters + String result; + bool escaped = false; + for (;;) + { + if (!ReadChar(c)) + break; + + switch (c) + { + case '\n': + case '\t': + break; + + case '\\': + if (escaped) + { + result += '\\'; + escaped = false; + } + else + escaped = true; + break; + + case 'n': + if (escaped) + { + result += '\n'; + escaped = false; + } + else + result += 'n'; + break; + + case 't': + if (escaped) + { + result += '\t'; + escaped = false; + } + else + result += 't'; + break; + + case '\"': + if (escaped) + { + result += '\"'; + escaped = false; + } + else + { + // Found closing double quote + outPrimitive = result; + return true; + } + break; + + default: + if (escaped) + escaped = false; + else + result += c; + break; + } + } + + return false; +} + +bool ObjectStreamTextIn::ReadPrimitiveData(Float3 &outPrimitive) +{ + float x, y, z; + if (!ReadPrimitiveData(x) || !ReadPrimitiveData(y) || !ReadPrimitiveData(z)) + return false; + outPrimitive = Float3(x, y, z); + return true; +} + +bool ObjectStreamTextIn::ReadPrimitiveData(Double3 &outPrimitive) +{ + double x, y, z; + if (!ReadPrimitiveData(x) || !ReadPrimitiveData(y) || !ReadPrimitiveData(z)) + return false; + outPrimitive = Double3(x, y, z); + return true; +} + +bool ObjectStreamTextIn::ReadPrimitiveData(Vec3 &outPrimitive) +{ + float x, y, z; + if (!ReadPrimitiveData(x) || !ReadPrimitiveData(y) || !ReadPrimitiveData(z)) + return false; + outPrimitive = Vec3(x, y, z); + return true; +} + +bool ObjectStreamTextIn::ReadPrimitiveData(DVec3 &outPrimitive) +{ + double x, y, z; + if (!ReadPrimitiveData(x) || !ReadPrimitiveData(y) || !ReadPrimitiveData(z)) + return false; + outPrimitive = DVec3(x, y, z); + return true; +} + +bool ObjectStreamTextIn::ReadPrimitiveData(Vec4 &outPrimitive) +{ + float x, y, z, w; + if (!ReadPrimitiveData(x) || !ReadPrimitiveData(y) || !ReadPrimitiveData(z) || !ReadPrimitiveData(w)) + return false; + outPrimitive = Vec4(x, y, z, w); + return true; +} + +bool ObjectStreamTextIn::ReadPrimitiveData(Quat &outPrimitive) +{ + float x, y, z, w; + if (!ReadPrimitiveData(x) || !ReadPrimitiveData(y) || !ReadPrimitiveData(z) || !ReadPrimitiveData(w)) + return false; + outPrimitive = Quat(x, y, z, w); + return true; +} + +bool ObjectStreamTextIn::ReadPrimitiveData(Mat44 &outPrimitive) +{ + Vec4 c0, c1, c2, c3; + if (!ReadPrimitiveData(c0) || !ReadPrimitiveData(c1) || !ReadPrimitiveData(c2) || !ReadPrimitiveData(c3)) + return false; + outPrimitive = Mat44(c0, c1, c2, c3); + return true; +} + +bool ObjectStreamTextIn::ReadPrimitiveData(DMat44 &outPrimitive) +{ + Vec4 c0, c1, c2; + DVec3 c3; + if (!ReadPrimitiveData(c0) || !ReadPrimitiveData(c1) || !ReadPrimitiveData(c2) || !ReadPrimitiveData(c3)) + return false; + outPrimitive = DMat44(c0, c1, c2, c3); + return true; +} + +bool ObjectStreamTextIn::ReadChar(char &outChar) +{ + mStream.get(outChar); + return !mStream.eof(); +} + +bool ObjectStreamTextIn::ReadWord(String &outWord) +{ + outWord.clear(); + + char c; + + // Skip whitespace + for (;;) + { + if (!ReadChar(c)) + return false; + + if (!isspace(c)) + break; + } + + // Read word + for (;;) + { + outWord += c; + + if (!ReadChar(c)) + break; + + if (isspace(c)) + break; + } + + return !outWord.empty(); +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamTextIn.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamTextIn.h new file mode 100644 index 00000000000..5c7e74283a0 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamTextIn.h @@ -0,0 +1,49 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +/// Implementation of ObjectStream text input stream. +class JPH_EXPORT ObjectStreamTextIn : public ObjectStreamIn +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Constructor + explicit ObjectStreamTextIn(istream &inStream); + + ///@name Input type specific operations + virtual bool ReadDataType(EOSDataType &outType) override; + virtual bool ReadName(String &outName) override; + virtual bool ReadIdentifier(Identifier &outIdentifier) override; + virtual bool ReadCount(uint32 &outCount) override; + + virtual bool ReadPrimitiveData(uint8 &outPrimitive) override; + virtual bool ReadPrimitiveData(uint16 &outPrimitive) override; + virtual bool ReadPrimitiveData(int &outPrimitive) override; + virtual bool ReadPrimitiveData(uint32 &outPrimitive) override; + virtual bool ReadPrimitiveData(uint64 &outPrimitive) override; + virtual bool ReadPrimitiveData(float &outPrimitive) override; + virtual bool ReadPrimitiveData(double &outPrimitive) override; + virtual bool ReadPrimitiveData(bool &outPrimitive) override; + virtual bool ReadPrimitiveData(String &outPrimitive) override; + virtual bool ReadPrimitiveData(Float3 &outPrimitive) override; + virtual bool ReadPrimitiveData(Double3 &outPrimitive) override; + virtual bool ReadPrimitiveData(Vec3 &outPrimitive) override; + virtual bool ReadPrimitiveData(DVec3 &outPrimitive) override; + virtual bool ReadPrimitiveData(Vec4 &outPrimitive) override; + virtual bool ReadPrimitiveData(Quat &outPrimitive) override; + virtual bool ReadPrimitiveData(Mat44 &outPrimitive) override; + virtual bool ReadPrimitiveData(DMat44 &outPrimitive) override; + +private: + bool ReadChar(char &outChar); + bool ReadWord(String &outWord); +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamTextOut.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamTextOut.cpp new file mode 100644 index 00000000000..1a2eeaf79e2 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamTextOut.cpp @@ -0,0 +1,227 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include + +JPH_NAMESPACE_BEGIN + +ObjectStreamTextOut::ObjectStreamTextOut(ostream &inStream) : + ObjectStreamOut(inStream) +{ + WriteWord(StringFormat("TOS%2d.%02d", ObjectStream::sVersion, ObjectStream::sRevision)); +} + +void ObjectStreamTextOut::WriteDataType(EOSDataType inType) +{ + switch (inType) + { + case EOSDataType::Declare: WriteWord("declare "); break; + case EOSDataType::Object: WriteWord("object "); break; + case EOSDataType::Instance: WriteWord("instance "); break; + case EOSDataType::Pointer: WriteWord("pointer "); break; + case EOSDataType::Array: WriteWord("array "); break; + case EOSDataType::T_uint8: WriteWord("uint8"); break; + case EOSDataType::T_uint16: WriteWord("uint16"); break; + case EOSDataType::T_int: WriteWord("int"); break; + case EOSDataType::T_uint32: WriteWord("uint32"); break; + case EOSDataType::T_uint64: WriteWord("uint64"); break; + case EOSDataType::T_float: WriteWord("float"); break; + case EOSDataType::T_double: WriteWord("double"); break; + case EOSDataType::T_bool: WriteWord("bool"); break; + case EOSDataType::T_String: WriteWord("string"); break; + case EOSDataType::T_Float3: WriteWord("float3"); break; + case EOSDataType::T_Double3: WriteWord("double3"); break; + case EOSDataType::T_Vec3: WriteWord("vec3"); break; + case EOSDataType::T_DVec3: WriteWord("dvec3"); break; + case EOSDataType::T_Vec4: WriteWord("vec4"); break; + case EOSDataType::T_Quat: WriteWord("quat"); break; + case EOSDataType::T_Mat44: WriteWord("mat44"); break; + case EOSDataType::T_DMat44: WriteWord("dmat44"); break; + case EOSDataType::Invalid: + default: JPH_ASSERT(false); break; + } +} + +void ObjectStreamTextOut::WriteName(const char *inName) +{ + WriteWord(String(inName) + " "); +} + +void ObjectStreamTextOut::WriteIdentifier(Identifier inIdentifier) +{ + WriteWord(StringFormat("%08X", inIdentifier)); +} + +void ObjectStreamTextOut::WriteCount(uint32 inCount) +{ + WriteWord(std::to_string(inCount)); +} + +void ObjectStreamTextOut::WritePrimitiveData(const uint8 &inPrimitive) +{ + WriteWord(std::to_string(inPrimitive)); +} + +void ObjectStreamTextOut::WritePrimitiveData(const uint16 &inPrimitive) +{ + WriteWord(std::to_string(inPrimitive)); +} + +void ObjectStreamTextOut::WritePrimitiveData(const int &inPrimitive) +{ + WriteWord(std::to_string(inPrimitive)); +} + +void ObjectStreamTextOut::WritePrimitiveData(const uint32 &inPrimitive) +{ + WriteWord(std::to_string(inPrimitive)); +} + +void ObjectStreamTextOut::WritePrimitiveData(const uint64 &inPrimitive) +{ + WriteWord(std::to_string(inPrimitive)); +} + +void ObjectStreamTextOut::WritePrimitiveData(const float &inPrimitive) +{ + std::ostringstream stream; + stream.precision(9); + stream << inPrimitive; + WriteWord(stream.str()); +} + +void ObjectStreamTextOut::WritePrimitiveData(const double &inPrimitive) +{ + std::ostringstream stream; + stream.precision(17); + stream << inPrimitive; + WriteWord(stream.str()); +} + +void ObjectStreamTextOut::WritePrimitiveData(const bool &inPrimitive) +{ + WriteWord(inPrimitive? "true" : "false"); +} + +void ObjectStreamTextOut::WritePrimitiveData(const Float3 &inPrimitive) +{ + WritePrimitiveData(inPrimitive.x); + WriteChar(' '); + WritePrimitiveData(inPrimitive.y); + WriteChar(' '); + WritePrimitiveData(inPrimitive.z); +} + +void ObjectStreamTextOut::WritePrimitiveData(const Double3 &inPrimitive) +{ + WritePrimitiveData(inPrimitive.x); + WriteChar(' '); + WritePrimitiveData(inPrimitive.y); + WriteChar(' '); + WritePrimitiveData(inPrimitive.z); +} + +void ObjectStreamTextOut::WritePrimitiveData(const Vec3 &inPrimitive) +{ + WritePrimitiveData(inPrimitive.GetX()); + WriteChar(' '); + WritePrimitiveData(inPrimitive.GetY()); + WriteChar(' '); + WritePrimitiveData(inPrimitive.GetZ()); +} + +void ObjectStreamTextOut::WritePrimitiveData(const DVec3 &inPrimitive) +{ + WritePrimitiveData(inPrimitive.GetX()); + WriteChar(' '); + WritePrimitiveData(inPrimitive.GetY()); + WriteChar(' '); + WritePrimitiveData(inPrimitive.GetZ()); +} + +void ObjectStreamTextOut::WritePrimitiveData(const Vec4 &inPrimitive) +{ + WritePrimitiveData(inPrimitive.GetX()); + WriteChar(' '); + WritePrimitiveData(inPrimitive.GetY()); + WriteChar(' '); + WritePrimitiveData(inPrimitive.GetZ()); + WriteChar(' '); + WritePrimitiveData(inPrimitive.GetW()); +} + +void ObjectStreamTextOut::WritePrimitiveData(const Quat &inPrimitive) +{ + WritePrimitiveData(inPrimitive.GetX()); + WriteChar(' '); + WritePrimitiveData(inPrimitive.GetY()); + WriteChar(' '); + WritePrimitiveData(inPrimitive.GetZ()); + WriteChar(' '); + WritePrimitiveData(inPrimitive.GetW()); +} + +void ObjectStreamTextOut::WritePrimitiveData(const Mat44 &inPrimitive) +{ + WritePrimitiveData(inPrimitive.GetColumn4(0)); + WriteChar(' '); + WritePrimitiveData(inPrimitive.GetColumn4(1)); + WriteChar(' '); + WritePrimitiveData(inPrimitive.GetColumn4(2)); + WriteChar(' '); + WritePrimitiveData(inPrimitive.GetColumn4(3)); +} + +void ObjectStreamTextOut::WritePrimitiveData(const DMat44 &inPrimitive) +{ + WritePrimitiveData(inPrimitive.GetColumn4(0)); + WriteChar(' '); + WritePrimitiveData(inPrimitive.GetColumn4(1)); + WriteChar(' '); + WritePrimitiveData(inPrimitive.GetColumn4(2)); + WriteChar(' '); + WritePrimitiveData(inPrimitive.GetTranslation()); +} + +void ObjectStreamTextOut::WritePrimitiveData(const String &inPrimitive) +{ + String temporary(inPrimitive); + StringReplace(temporary, "\\", "\\\\"); + StringReplace(temporary, "\n", "\\n"); + StringReplace(temporary, "\t", "\\t"); + StringReplace(temporary, "\"", "\\\""); + WriteWord(String("\"") + temporary + String("\"")); +} + +void ObjectStreamTextOut::HintNextItem() +{ + WriteWord("\r\n"); + for (int i = 0; i < mIndentation; ++i) + WriteWord(" "); +} + +void ObjectStreamTextOut::HintIndentUp() +{ + ++mIndentation; +} + +void ObjectStreamTextOut::HintIndentDown() +{ + --mIndentation; +} + +void ObjectStreamTextOut::WriteChar(char inChar) +{ + mStream.put(inChar); +} + +void ObjectStreamTextOut::WriteWord(const string_view &inWord) +{ + mStream << inWord; +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamTextOut.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamTextOut.h new file mode 100644 index 00000000000..30829a9deef --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamTextOut.h @@ -0,0 +1,56 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +/// Implementation of ObjectStream text output stream. +class JPH_EXPORT ObjectStreamTextOut : public ObjectStreamOut +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Constructor and destructor + explicit ObjectStreamTextOut(ostream &inStream); + + ///@name Output type specific operations + virtual void WriteDataType(EOSDataType inType) override; + virtual void WriteName(const char *inName) override; + virtual void WriteIdentifier(Identifier inIdentifier) override; + virtual void WriteCount(uint32 inCount) override; + + virtual void WritePrimitiveData(const uint8 &inPrimitive) override; + virtual void WritePrimitiveData(const uint16 &inPrimitive) override; + virtual void WritePrimitiveData(const int &inPrimitive) override; + virtual void WritePrimitiveData(const uint32 &inPrimitive) override; + virtual void WritePrimitiveData(const uint64 &inPrimitive) override; + virtual void WritePrimitiveData(const float &inPrimitive) override; + virtual void WritePrimitiveData(const double &inPrimitive) override; + virtual void WritePrimitiveData(const bool &inPrimitive) override; + virtual void WritePrimitiveData(const String &inPrimitive) override; + virtual void WritePrimitiveData(const Float3 &inPrimitive) override; + virtual void WritePrimitiveData(const Double3 &inPrimitive) override; + virtual void WritePrimitiveData(const Vec3 &inPrimitive) override; + virtual void WritePrimitiveData(const DVec3 &inPrimitive) override; + virtual void WritePrimitiveData(const Vec4 &inPrimitive) override; + virtual void WritePrimitiveData(const Quat &inPrimitive) override; + virtual void WritePrimitiveData(const Mat44 &inPrimitive) override; + virtual void WritePrimitiveData(const DMat44 &inPrimitive) override; + + ///@name Layout hints (for text output) + virtual void HintNextItem() override; + virtual void HintIndentUp() override; + virtual void HintIndentDown() override; + +private: + void WriteChar(char inChar); + void WriteWord(const string_view &inWord); + + int mIndentation = 0; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamTypes.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamTypes.h new file mode 100644 index 00000000000..6fb08af1710 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamTypes.h @@ -0,0 +1,24 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +// Note: Order is important, an enum is created and its value is stored in a binary stream! +JPH_DECLARE_PRIMITIVE(uint8) +JPH_DECLARE_PRIMITIVE(uint16) +JPH_DECLARE_PRIMITIVE(int) +JPH_DECLARE_PRIMITIVE(uint32) +JPH_DECLARE_PRIMITIVE(uint64) +JPH_DECLARE_PRIMITIVE(float) +JPH_DECLARE_PRIMITIVE(bool) +JPH_DECLARE_PRIMITIVE(String) +JPH_DECLARE_PRIMITIVE(Float3) +JPH_DECLARE_PRIMITIVE(Vec3) +JPH_DECLARE_PRIMITIVE(Vec4) +JPH_DECLARE_PRIMITIVE(Quat) +JPH_DECLARE_PRIMITIVE(Mat44) +JPH_DECLARE_PRIMITIVE(double) +JPH_DECLARE_PRIMITIVE(DVec3) +JPH_DECLARE_PRIMITIVE(DMat44) +JPH_DECLARE_PRIMITIVE(Double3) + +#undef JPH_DECLARE_PRIMITIVE diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/SerializableAttribute.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/SerializableAttribute.h new file mode 100644 index 00000000000..8701eedff4e --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/SerializableAttribute.h @@ -0,0 +1,107 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +JPH_NAMESPACE_BEGIN + +class RTTI; +class IObjectStreamIn; +class IObjectStreamOut; + +/// Data type +enum class EOSDataType +{ + /// Control codes + Declare, ///< Used to declare the attributes of a new object type + Object, ///< Start of a new object + Instance, ///< Used in attribute declaration, indicates that an object is an instanced attribute (no pointer) + Pointer, ///< Used in attribute declaration, indicates that an object is a pointer attribute + Array, ///< Used in attribute declaration, indicates that this is an array of objects + + // Basic types (primitives) + #define JPH_DECLARE_PRIMITIVE(name) T_##name, + + // This file uses the JPH_DECLARE_PRIMITIVE macro to define all types + #include + + // Error values for read functions + Invalid, ///< Next token on the stream was not a valid data type +}; + +/// Attributes are members of classes that need to be serialized. +class SerializableAttribute +{ +public: + ///@ Serialization functions + using pGetMemberPrimitiveType = const RTTI * (*)(); + using pIsType = bool (*)(int inArrayDepth, EOSDataType inDataType, const char *inClassName); + using pReadData = bool (*)(IObjectStreamIn &ioStream, void *inObject); + using pWriteData = void (*)(IObjectStreamOut &ioStream, const void *inObject); + using pWriteDataType = void (*)(IObjectStreamOut &ioStream); + + /// Constructor + SerializableAttribute(const char *inName, uint inMemberOffset, pGetMemberPrimitiveType inGetMemberPrimitiveType, pIsType inIsType, pReadData inReadData, pWriteData inWriteData, pWriteDataType inWriteDataType) : mName(inName), mMemberOffset(inMemberOffset), mGetMemberPrimitiveType(inGetMemberPrimitiveType), mIsType(inIsType), mReadData(inReadData), mWriteData(inWriteData), mWriteDataType(inWriteDataType) { } + + /// Construct from other attribute with base class offset + SerializableAttribute(const SerializableAttribute &inOther, int inBaseOffset) : mName(inOther.mName), mMemberOffset(inOther.mMemberOffset + inBaseOffset), mGetMemberPrimitiveType(inOther.mGetMemberPrimitiveType), mIsType(inOther.mIsType), mReadData(inOther.mReadData), mWriteData(inOther.mWriteData), mWriteDataType(inOther.mWriteDataType) { } + + /// Name of the attribute + void SetName(const char *inName) { mName = inName; } + const char * GetName() const { return mName; } + + /// Access to the memory location that contains the member + template + inline T * GetMemberPointer(void *inObject) const { return reinterpret_cast(reinterpret_cast(inObject) + mMemberOffset); } + template + inline const T * GetMemberPointer(const void *inObject) const { return reinterpret_cast(reinterpret_cast(inObject) + mMemberOffset); } + + /// In case this attribute contains an RTTI type, return it (note that a Array will return the rtti of sometype) + const RTTI * GetMemberPrimitiveType() const + { + return mGetMemberPrimitiveType(); + } + + /// Check if this attribute is of a specific type + bool IsType(int inArrayDepth, EOSDataType inDataType, const char *inClassName) const + { + return mIsType(inArrayDepth, inDataType, inClassName); + } + + /// Read the data for this attribute into attribute containing class inObject + bool ReadData(IObjectStreamIn &ioStream, void *inObject) const + { + return mReadData(ioStream, GetMemberPointer(inObject)); + } + + /// Write the data for this attribute from attribute containing class inObject + void WriteData(IObjectStreamOut &ioStream, const void *inObject) const + { + mWriteData(ioStream, GetMemberPointer(inObject)); + } + + /// Write the data type of this attribute to a stream + void WriteDataType(IObjectStreamOut &ioStream) const + { + mWriteDataType(ioStream); + } + +private: + // Name of the attribute + const char * mName; + + // Offset of the member relative to the class + uint mMemberOffset; + + // In case this attribute contains an RTTI type, return it (note that a Array will return the rtti of sometype) + pGetMemberPrimitiveType mGetMemberPrimitiveType; + + // Serialization operations + pIsType mIsType; + pReadData mReadData; + pWriteData mWriteData; + pWriteDataType mWriteDataType; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/SerializableAttributeEnum.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/SerializableAttributeEnum.h new file mode 100644 index 00000000000..be1ca34bf93 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/SerializableAttributeEnum.h @@ -0,0 +1,58 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +JPH_NAMESPACE_BEGIN + +////////////////////////////////////////////////////////////////////////////////////////// +// Macros to add properties to be serialized +////////////////////////////////////////////////////////////////////////////////////////// + +template +inline void AddSerializableAttributeEnum(RTTI &inRTTI, uint inOffset, const char *inName) +{ + inRTTI.AddAttribute(SerializableAttribute(inName, inOffset, + []() -> const RTTI * + { + return nullptr; + }, + [](int inArrayDepth, EOSDataType inDataType, [[maybe_unused]] const char *inClassName) + { + return inArrayDepth == 0 && inDataType == EOSDataType::T_uint32; + }, + [](IObjectStreamIn &ioStream, void *inObject) + { + uint32 temporary; + if (OSReadData(ioStream, temporary)) + { + *reinterpret_cast(inObject) = static_cast(temporary); + return true; + } + return false; + }, + [](IObjectStreamOut &ioStream, const void *inObject) + { + static_assert(sizeof(MemberType) <= sizeof(uint32)); + uint32 temporary = uint32(*reinterpret_cast(inObject)); + OSWriteData(ioStream, temporary); + }, + [](IObjectStreamOut &ioStream) + { + ioStream.WriteDataType(EOSDataType::T_uint32); + })); +} + +// JPH_ADD_ENUM_ATTRIBUTE_WITH_ALIAS +#define JPH_ADD_ENUM_ATTRIBUTE_WITH_ALIAS(class_name, member_name, alias_name) \ + AddSerializableAttributeEnum(inRTTI, offsetof(class_name, member_name), alias_name); + +// JPH_ADD_ENUM_ATTRIBUTE +#define JPH_ADD_ENUM_ATTRIBUTE(class_name, member_name) \ + JPH_ADD_ENUM_ATTRIBUTE_WITH_ALIAS(class_name, member_name, #member_name); + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/SerializableAttributeTyped.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/SerializableAttributeTyped.h new file mode 100644 index 00000000000..23d5dfe7cd2 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/SerializableAttributeTyped.h @@ -0,0 +1,51 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +////////////////////////////////////////////////////////////////////////////////////////// +// Macros to add properties to be serialized +////////////////////////////////////////////////////////////////////////////////////////// + +template +inline void AddSerializableAttributeTyped(RTTI &inRTTI, uint inOffset, const char *inName) +{ + inRTTI.AddAttribute(SerializableAttribute(inName, inOffset, + []() + { + return GetPrimitiveTypeOfType((MemberType *)nullptr); + }, + [](int inArrayDepth, EOSDataType inDataType, const char *inClassName) + { + return OSIsType((MemberType *)nullptr, inArrayDepth, inDataType, inClassName); + }, + [](IObjectStreamIn &ioStream, void *inObject) + { + return OSReadData(ioStream, *reinterpret_cast(inObject)); + }, + [](IObjectStreamOut &ioStream, const void *inObject) + { + OSWriteData(ioStream, *reinterpret_cast(inObject)); + }, + [](IObjectStreamOut &ioStream) + { + OSWriteDataType(ioStream, (MemberType *)nullptr); + })); +} + +// JPH_ADD_ATTRIBUTE +#define JPH_ADD_ATTRIBUTE_WITH_ALIAS(class_name, member_name, alias_name) \ + AddSerializableAttributeTyped(inRTTI, offsetof(class_name, member_name), alias_name); + +// JPH_ADD_ATTRIBUTE +#define JPH_ADD_ATTRIBUTE(class_name, member_name) \ + JPH_ADD_ATTRIBUTE_WITH_ALIAS(class_name, member_name, #member_name) + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/SerializableObject.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/SerializableObject.cpp new file mode 100644 index 00000000000..98d3b3cf8d0 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/SerializableObject.cpp @@ -0,0 +1,15 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include + +JPH_NAMESPACE_BEGIN + +JPH_IMPLEMENT_SERIALIZABLE_ABSTRACT(SerializableObject) +{ +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/SerializableObject.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/SerializableObject.h new file mode 100644 index 00000000000..5ff3f749614 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/SerializableObject.h @@ -0,0 +1,155 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +////////////////////////////////////////////////////////////////////////////////////////// +// Helper macros +////////////////////////////////////////////////////////////////////////////////////////// + +// JPH_DECLARE_SERIALIZATION_FUNCTIONS +#define JPH_DECLARE_SERIALIZATION_FUNCTIONS(linkage, prefix, class_name) \ + linkage prefix bool OSReadData(IObjectStreamIn &ioStream, class_name &inInstance); \ + linkage prefix bool OSReadData(IObjectStreamIn &ioStream, class_name *&inPointer); \ + linkage prefix bool OSIsType(class_name *, int inArrayDepth, EOSDataType inDataType, const char *inClassName); \ + linkage prefix bool OSIsType(class_name **, int inArrayDepth, EOSDataType inDataType, const char *inClassName); \ + linkage prefix void OSWriteData(IObjectStreamOut &ioStream, const class_name &inInstance); \ + linkage prefix void OSWriteData(IObjectStreamOut &ioStream, class_name *const &inPointer); \ + linkage prefix void OSWriteDataType(IObjectStreamOut &ioStream, class_name *); \ + linkage prefix void OSWriteDataType(IObjectStreamOut &ioStream, class_name **); + +// JPH_IMPLEMENT_SERIALIZATION_FUNCTIONS +#define JPH_IMPLEMENT_SERIALIZATION_FUNCTIONS(class_name) \ + bool OSReadData(IObjectStreamIn &ioStream, class_name &inInstance) \ + { \ + return ioStream.ReadClassData(#class_name, (void *)&inInstance); \ + } \ + bool OSReadData(IObjectStreamIn &ioStream, class_name *&inPointer) \ + { \ + return ioStream.ReadPointerData(JPH_RTTI(class_name), (void **)&inPointer); \ + } \ + bool OSIsType(class_name *, int inArrayDepth, EOSDataType inDataType, const char *inClassName) \ + { \ + return inArrayDepth == 0 && inDataType == EOSDataType::Instance && strcmp(inClassName, #class_name) == 0; \ + } \ + bool OSIsType(class_name **, int inArrayDepth, EOSDataType inDataType, const char *inClassName) \ + { \ + return inArrayDepth == 0 && inDataType == EOSDataType::Pointer && strcmp(inClassName, #class_name) == 0; \ + } \ + void OSWriteData(IObjectStreamOut &ioStream, const class_name &inInstance) \ + { \ + ioStream.WriteClassData(JPH_RTTI(class_name), (void *)&inInstance); \ + } \ + void OSWriteData(IObjectStreamOut &ioStream, class_name *const &inPointer) \ + { \ + if (inPointer) \ + ioStream.WritePointerData(GetRTTI(inPointer), (void *)inPointer); \ + else \ + ioStream.WritePointerData(nullptr, nullptr); \ + } \ + void OSWriteDataType(IObjectStreamOut &ioStream, class_name *) \ + { \ + ioStream.WriteDataType(EOSDataType::Instance); \ + ioStream.WriteName(#class_name); \ + } \ + void OSWriteDataType(IObjectStreamOut &ioStream, class_name **) \ + { \ + ioStream.WriteDataType(EOSDataType::Pointer); \ + ioStream.WriteName(#class_name); \ + } + +////////////////////////////////////////////////////////////////////////////////////////// +// Use these macros on non-virtual objects to make them serializable +////////////////////////////////////////////////////////////////////////////////////////// + +// JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL +#define JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(linkage, class_name) \ +public: \ + JPH_DECLARE_RTTI_NON_VIRTUAL(linkage, class_name) \ + JPH_DECLARE_SERIALIZATION_FUNCTIONS(linkage, friend, class_name) \ + +// JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL +#define JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(class_name) \ + JPH_IMPLEMENT_SERIALIZATION_FUNCTIONS(class_name) \ + JPH_IMPLEMENT_RTTI_NON_VIRTUAL(class_name) \ + +////////////////////////////////////////////////////////////////////////////////////////// +// Same as above, but when you cannot insert the declaration in the class itself +////////////////////////////////////////////////////////////////////////////////////////// + +// JPH_DECLARE_SERIALIZABLE_OUTSIDE_CLASS +#define JPH_DECLARE_SERIALIZABLE_OUTSIDE_CLASS(linkage, class_name) \ + JPH_DECLARE_RTTI_OUTSIDE_CLASS(linkage, class_name) \ + JPH_DECLARE_SERIALIZATION_FUNCTIONS(linkage, extern, class_name) \ + +// JPH_IMPLEMENT_SERIALIZABLE_OUTSIDE_CLASS +#define JPH_IMPLEMENT_SERIALIZABLE_OUTSIDE_CLASS(class_name) \ + JPH_IMPLEMENT_SERIALIZATION_FUNCTIONS(class_name) \ + JPH_IMPLEMENT_RTTI_OUTSIDE_CLASS(class_name) \ + +////////////////////////////////////////////////////////////////////////////////////////// +// Same as above, but for classes that have virtual functions +////////////////////////////////////////////////////////////////////////////////////////// + +// JPH_DECLARE_SERIALIZABLE_VIRTUAL - Use for concrete, non-base classes +#define JPH_DECLARE_SERIALIZABLE_VIRTUAL(linkage, class_name) \ +public: \ + JPH_DECLARE_RTTI_VIRTUAL(linkage, class_name) \ + JPH_DECLARE_SERIALIZATION_FUNCTIONS(linkage, friend, class_name) \ + +// JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL +#define JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(class_name) \ + JPH_IMPLEMENT_SERIALIZATION_FUNCTIONS(class_name) \ + JPH_IMPLEMENT_RTTI_VIRTUAL(class_name) \ + +// JPH_DECLARE_SERIALIZABLE_ABSTRACT - Use for abstract, non-base classes +#define JPH_DECLARE_SERIALIZABLE_ABSTRACT(linkage, class_name) \ +public: \ + JPH_DECLARE_RTTI_ABSTRACT(linkage, class_name) \ + JPH_DECLARE_SERIALIZATION_FUNCTIONS(linkage, friend, class_name) \ + +// JPH_IMPLEMENT_SERIALIZABLE_ABSTRACT +#define JPH_IMPLEMENT_SERIALIZABLE_ABSTRACT(class_name) \ + JPH_IMPLEMENT_SERIALIZATION_FUNCTIONS(class_name) \ + JPH_IMPLEMENT_RTTI_ABSTRACT(class_name) \ + +// JPH_DECLARE_SERIALIZABLE_VIRTUAL_BASE - Use for concrete base classes +#define JPH_DECLARE_SERIALIZABLE_VIRTUAL_BASE(linkage, class_name) \ +public: \ + JPH_DECLARE_RTTI_VIRTUAL_BASE(linkage, class_name) \ + JPH_DECLARE_SERIALIZATION_FUNCTIONS(linkage, friend, class_name) \ + +// JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL_BASE +#define JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL_BASE(class_name) \ + JPH_IMPLEMENT_SERIALIZATION_FUNCTIONS(class_name) \ + JPH_IMPLEMENT_RTTI_VIRTUAL_BASE(class_name) \ + +// JPH_DECLARE_SERIALIZABLE_ABSTRACT_BASE - Use for abstract base class +#define JPH_DECLARE_SERIALIZABLE_ABSTRACT_BASE(linkage, class_name) \ +public: \ + JPH_DECLARE_RTTI_ABSTRACT_BASE(linkage, class_name) \ + JPH_DECLARE_SERIALIZATION_FUNCTIONS(linkage, friend, class_name) \ + +// JPH_IMPLEMENT_SERIALIZABLE_ABSTRACT_BASE +#define JPH_IMPLEMENT_SERIALIZABLE_ABSTRACT_BASE(class_name) \ + JPH_IMPLEMENT_SERIALIZATION_FUNCTIONS(class_name) \ + JPH_IMPLEMENT_RTTI_ABSTRACT_BASE(class_name) + +/// Classes must be derived from SerializableObject if you want to be able to save pointers or +/// reference counting pointers to objects of this or derived classes. The type will automatically +/// be determined during serialization and upon deserialization it will be restored correctly. +class JPH_EXPORT SerializableObject : public NonCopyable +{ + JPH_DECLARE_SERIALIZABLE_ABSTRACT_BASE(JPH_EXPORT, SerializableObject) + +public: + /// Constructor + virtual ~SerializableObject() = default; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/TypeDeclarations.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/TypeDeclarations.cpp new file mode 100644 index 00000000000..a1d5ac6ac17 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/TypeDeclarations.cpp @@ -0,0 +1,55 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include + +JPH_NAMESPACE_BEGIN + +JPH_IMPLEMENT_RTTI_OUTSIDE_CLASS(uint8) { } +JPH_IMPLEMENT_RTTI_OUTSIDE_CLASS(uint16) { } +JPH_IMPLEMENT_RTTI_OUTSIDE_CLASS(int) { } +JPH_IMPLEMENT_RTTI_OUTSIDE_CLASS(uint32) { } +JPH_IMPLEMENT_RTTI_OUTSIDE_CLASS(uint64) { } +JPH_IMPLEMENT_RTTI_OUTSIDE_CLASS(float) { } +JPH_IMPLEMENT_RTTI_OUTSIDE_CLASS(double) { } +JPH_IMPLEMENT_RTTI_OUTSIDE_CLASS(bool) { } +JPH_IMPLEMENT_RTTI_OUTSIDE_CLASS(String) { } +JPH_IMPLEMENT_RTTI_OUTSIDE_CLASS(Float3) { } +JPH_IMPLEMENT_RTTI_OUTSIDE_CLASS(Double3) { } +JPH_IMPLEMENT_RTTI_OUTSIDE_CLASS(Vec3) { } +JPH_IMPLEMENT_RTTI_OUTSIDE_CLASS(DVec3) { } +JPH_IMPLEMENT_RTTI_OUTSIDE_CLASS(Vec4) { } +JPH_IMPLEMENT_RTTI_OUTSIDE_CLASS(Quat) { } +JPH_IMPLEMENT_RTTI_OUTSIDE_CLASS(Mat44) { } +JPH_IMPLEMENT_RTTI_OUTSIDE_CLASS(DMat44) { } + +JPH_IMPLEMENT_SERIALIZABLE_OUTSIDE_CLASS(Color) +{ + JPH_ADD_ATTRIBUTE(Color, r) + JPH_ADD_ATTRIBUTE(Color, g) + JPH_ADD_ATTRIBUTE(Color, b) + JPH_ADD_ATTRIBUTE(Color, a) +} + +JPH_IMPLEMENT_SERIALIZABLE_OUTSIDE_CLASS(AABox) +{ + JPH_ADD_ATTRIBUTE(AABox, mMin) + JPH_ADD_ATTRIBUTE(AABox, mMax) +} + +JPH_IMPLEMENT_SERIALIZABLE_OUTSIDE_CLASS(Triangle) +{ + JPH_ADD_ATTRIBUTE(Triangle, mV) + JPH_ADD_ATTRIBUTE(Triangle, mMaterialIndex) +} + +JPH_IMPLEMENT_SERIALIZABLE_OUTSIDE_CLASS(IndexedTriangle) +{ + JPH_ADD_ATTRIBUTE(IndexedTriangle, mIdx) + JPH_ADD_ATTRIBUTE(IndexedTriangle, mMaterialIndex) +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/TypeDeclarations.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/TypeDeclarations.h new file mode 100644 index 00000000000..2a61c306be5 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/TypeDeclarations.h @@ -0,0 +1,41 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +JPH_DECLARE_RTTI_OUTSIDE_CLASS(JPH_EXPORT, uint8); +JPH_DECLARE_RTTI_OUTSIDE_CLASS(JPH_EXPORT, uint16); +JPH_DECLARE_RTTI_OUTSIDE_CLASS(JPH_EXPORT, int); +JPH_DECLARE_RTTI_OUTSIDE_CLASS(JPH_EXPORT, uint32); +JPH_DECLARE_RTTI_OUTSIDE_CLASS(JPH_EXPORT, uint64); +JPH_DECLARE_RTTI_OUTSIDE_CLASS(JPH_EXPORT, float); +JPH_DECLARE_RTTI_OUTSIDE_CLASS(JPH_EXPORT, double); +JPH_DECLARE_RTTI_OUTSIDE_CLASS(JPH_EXPORT, bool); +JPH_DECLARE_RTTI_OUTSIDE_CLASS(JPH_EXPORT, String); +JPH_DECLARE_RTTI_OUTSIDE_CLASS(JPH_EXPORT, Float3); +JPH_DECLARE_RTTI_OUTSIDE_CLASS(JPH_EXPORT, Double3); +JPH_DECLARE_RTTI_OUTSIDE_CLASS(JPH_EXPORT, Vec3); +JPH_DECLARE_RTTI_OUTSIDE_CLASS(JPH_EXPORT, DVec3); +JPH_DECLARE_RTTI_OUTSIDE_CLASS(JPH_EXPORT, Vec4); +JPH_DECLARE_RTTI_OUTSIDE_CLASS(JPH_EXPORT, Quat); +JPH_DECLARE_RTTI_OUTSIDE_CLASS(JPH_EXPORT, Mat44); +JPH_DECLARE_RTTI_OUTSIDE_CLASS(JPH_EXPORT, DMat44); +JPH_DECLARE_SERIALIZABLE_OUTSIDE_CLASS(JPH_EXPORT, Color); +JPH_DECLARE_SERIALIZABLE_OUTSIDE_CLASS(JPH_EXPORT, AABox); +JPH_DECLARE_SERIALIZABLE_OUTSIDE_CLASS(JPH_EXPORT, Triangle); +JPH_DECLARE_SERIALIZABLE_OUTSIDE_CLASS(JPH_EXPORT, IndexedTriangle); + +JPH_NAMESPACE_END + +// These need to be added after all types have been registered or else clang under linux will not find GetRTTIOfType for the type +#include +#include diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/AllowedDOFs.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/AllowedDOFs.h new file mode 100644 index 00000000000..8445cb18633 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/AllowedDOFs.h @@ -0,0 +1,68 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2023 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +JPH_NAMESPACE_BEGIN + +/// Enum used in BodyCreationSettings and MotionProperties to indicate which degrees of freedom a body has +enum class EAllowedDOFs : uint8 +{ + None = 0b000000, ///< No degrees of freedom are allowed. Note that this is not valid and will crash. Use a static body instead. + All = 0b111111, ///< All degrees of freedom are allowed + TranslationX = 0b000001, ///< Body can move in world space X axis + TranslationY = 0b000010, ///< Body can move in world space Y axis + TranslationZ = 0b000100, ///< Body can move in world space Z axis + RotationX = 0b001000, ///< Body can rotate around world space X axis + RotationY = 0b010000, ///< Body can rotate around world space Y axis + RotationZ = 0b100000, ///< Body can rotate around world space Z axis + Plane2D = TranslationX | TranslationY | RotationZ, ///< Body can only move in X and Y axis and rotate around Z axis +}; + +/// Bitwise OR operator for EAllowedDOFs +constexpr EAllowedDOFs operator | (EAllowedDOFs inLHS, EAllowedDOFs inRHS) +{ + return EAllowedDOFs(uint8(inLHS) | uint8(inRHS)); +} + +/// Bitwise AND operator for EAllowedDOFs +constexpr EAllowedDOFs operator & (EAllowedDOFs inLHS, EAllowedDOFs inRHS) +{ + return EAllowedDOFs(uint8(inLHS) & uint8(inRHS)); +} + +/// Bitwise XOR operator for EAllowedDOFs +constexpr EAllowedDOFs operator ^ (EAllowedDOFs inLHS, EAllowedDOFs inRHS) +{ + return EAllowedDOFs(uint8(inLHS) ^ uint8(inRHS)); +} + +/// Bitwise NOT operator for EAllowedDOFs +constexpr EAllowedDOFs operator ~ (EAllowedDOFs inAllowedDOFs) +{ + return EAllowedDOFs(~uint8(inAllowedDOFs)); +} + +/// Bitwise OR assignment operator for EAllowedDOFs +constexpr EAllowedDOFs & operator |= (EAllowedDOFs &ioLHS, EAllowedDOFs inRHS) +{ + ioLHS = ioLHS | inRHS; + return ioLHS; +} + +/// Bitwise AND assignment operator for EAllowedDOFs +constexpr EAllowedDOFs & operator &= (EAllowedDOFs &ioLHS, EAllowedDOFs inRHS) +{ + ioLHS = ioLHS & inRHS; + return ioLHS; +} + +/// Bitwise XOR assignment operator for EAllowedDOFs +constexpr EAllowedDOFs & operator ^= (EAllowedDOFs &ioLHS, EAllowedDOFs inRHS) +{ + ioLHS = ioLHS ^ inRHS; + return ioLHS; +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/Body.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/Body.cpp new file mode 100644 index 00000000000..085ae427c33 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/Body.cpp @@ -0,0 +1,413 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef JPH_DEBUG_RENDERER + #include +#endif // JPH_DEBUG_RENDERER + +JPH_NAMESPACE_BEGIN + +static const SphereShape sFixedToWorldShape(FLT_EPSILON); +Body Body::sFixedToWorld(false); + +Body::Body(bool) : + mPosition(Vec3::sZero()), + mRotation(Quat::sIdentity()), + mShape(&sFixedToWorldShape), // Dummy shape + mFriction(0.0f), + mRestitution(0.0f), + mObjectLayer(cObjectLayerInvalid), + mMotionType(EMotionType::Static) +{ + sFixedToWorldShape.SetEmbedded(); +} + +void Body::SetMotionType(EMotionType inMotionType) +{ + if (mMotionType == inMotionType) + return; + + JPH_ASSERT(inMotionType == EMotionType::Static || mMotionProperties != nullptr, "Body needs to be created with mAllowDynamicOrKinematic set tot true"); + JPH_ASSERT(inMotionType != EMotionType::Static || !IsActive(), "Deactivate body first"); + JPH_ASSERT(inMotionType == EMotionType::Dynamic || !IsSoftBody(), "Soft bodies can only be dynamic, you can make individual vertices kinematic by setting their inverse mass to 0"); + + // Store new motion type + mMotionType = inMotionType; + + if (mMotionProperties != nullptr) + { + // Update cache + JPH_IF_ENABLE_ASSERTS(mMotionProperties->mCachedMotionType = inMotionType;) + + switch (inMotionType) + { + case EMotionType::Static: + // Stop the object + mMotionProperties->mLinearVelocity = Vec3::sZero(); + mMotionProperties->mAngularVelocity = Vec3::sZero(); + [[fallthrough]]; + + case EMotionType::Kinematic: + // Cancel forces + mMotionProperties->ResetForce(); + mMotionProperties->ResetTorque(); + break; + + case EMotionType::Dynamic: + break; + } + } +} + +void Body::SetAllowSleeping(bool inAllow) +{ + mMotionProperties->mAllowSleeping = inAllow; + if (inAllow) + ResetSleepTimer(); +} + +void Body::MoveKinematic(RVec3Arg inTargetPosition, QuatArg inTargetRotation, float inDeltaTime) +{ + JPH_ASSERT(IsRigidBody()); // Only valid for rigid bodies + JPH_ASSERT(!IsStatic()); + JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sPositionAccess, BodyAccess::EAccess::Read)); + + // Calculate center of mass at end situation + RVec3 new_com = inTargetPosition + inTargetRotation * mShape->GetCenterOfMass(); + + // Calculate delta position and rotation + Vec3 delta_pos = Vec3(new_com - mPosition); + Quat delta_rotation = inTargetRotation * mRotation.Conjugated(); + + mMotionProperties->MoveKinematic(delta_pos, delta_rotation, inDeltaTime); +} + +void Body::CalculateWorldSpaceBoundsInternal() +{ + mBounds = mShape->GetWorldSpaceBounds(GetCenterOfMassTransform(), Vec3::sReplicate(1.0f)); +} + +void Body::SetPositionAndRotationInternal(RVec3Arg inPosition, QuatArg inRotation, bool inResetSleepTimer) +{ + JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sPositionAccess, BodyAccess::EAccess::ReadWrite)); + + mPosition = inPosition + inRotation * mShape->GetCenterOfMass(); + mRotation = inRotation; + + // Initialize bounding box + CalculateWorldSpaceBoundsInternal(); + + // Reset sleeping test + if (inResetSleepTimer && mMotionProperties != nullptr) + ResetSleepTimer(); +} + +void Body::UpdateCenterOfMassInternal(Vec3Arg inPreviousCenterOfMass, bool inUpdateMassProperties) +{ + // Update center of mass position so the world position for this body stays the same + mPosition += mRotation * (mShape->GetCenterOfMass() - inPreviousCenterOfMass); + + // Recalculate mass and inertia if requested + if (inUpdateMassProperties && mMotionProperties != nullptr) + mMotionProperties->SetMassProperties(mMotionProperties->GetAllowedDOFs(), mShape->GetMassProperties()); +} + +void Body::SetShapeInternal(const Shape *inShape, bool inUpdateMassProperties) +{ + JPH_ASSERT(IsRigidBody()); // Only valid for rigid bodies + JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sPositionAccess, BodyAccess::EAccess::ReadWrite)); + + // Get the old center of mass + Vec3 old_com = mShape->GetCenterOfMass(); + + // Update the shape + mShape = inShape; + + // Update center of mass + UpdateCenterOfMassInternal(old_com, inUpdateMassProperties); + + // Recalculate bounding box + CalculateWorldSpaceBoundsInternal(); +} + +ECanSleep Body::UpdateSleepStateInternal(float inDeltaTime, float inMaxMovement, float inTimeBeforeSleep) +{ + // Check override & sensors will never go to sleep (they would stop detecting collisions with sleeping bodies) + if (!mMotionProperties->mAllowSleeping || IsSensor()) + return ECanSleep::CannotSleep; + + // Get the points to test + RVec3 points[3]; + GetSleepTestPoints(points); + +#ifdef JPH_DOUBLE_PRECISION + // Get base offset for spheres + DVec3 offset = mMotionProperties->GetSleepTestOffset(); +#endif // JPH_DOUBLE_PRECISION + + for (int i = 0; i < 3; ++i) + { + Sphere &sphere = mMotionProperties->mSleepTestSpheres[i]; + + // Make point relative to base offset +#ifdef JPH_DOUBLE_PRECISION + Vec3 p = Vec3(points[i] - offset); +#else + Vec3 p = points[i]; +#endif // JPH_DOUBLE_PRECISION + + // Encapsulate the point in a sphere + sphere.EncapsulatePoint(p); + + // Test if it exceeded the max movement + if (sphere.GetRadius() > inMaxMovement) + { + // Body is not sleeping, reset test + mMotionProperties->ResetSleepTestSpheres(points); + return ECanSleep::CannotSleep; + } + } + + return mMotionProperties->AccumulateSleepTime(inDeltaTime, inTimeBeforeSleep); +} + +bool Body::ApplyBuoyancyImpulse(RVec3Arg inSurfacePosition, Vec3Arg inSurfaceNormal, float inBuoyancy, float inLinearDrag, float inAngularDrag, Vec3Arg inFluidVelocity, Vec3Arg inGravity, float inDeltaTime) +{ + JPH_PROFILE_FUNCTION(); + + JPH_ASSERT(IsRigidBody()); // Only implemented for rigid bodies currently + + // We follow the approach from 'Game Programming Gems 6' 2.5 Exact Buoyancy for Polyhedra + // All quantities below are in world space + + // For GetSubmergedVolume we transform the surface relative to the body position for increased precision + Mat44 rotation = Mat44::sRotation(mRotation); + Plane surface_relative_to_body = Plane::sFromPointAndNormal(inSurfacePosition - mPosition, inSurfaceNormal); + + // Calculate amount of volume that is submerged and what the center of buoyancy is + float total_volume, submerged_volume; + Vec3 relative_center_of_buoyancy; + mShape->GetSubmergedVolume(rotation, Vec3::sReplicate(1.0f), surface_relative_to_body, total_volume, submerged_volume, relative_center_of_buoyancy JPH_IF_DEBUG_RENDERER(, mPosition)); + + // If we're not submerged, there's no point in doing the rest of the calculations + if (submerged_volume > 0.0f) + { + #ifdef JPH_DEBUG_RENDERER + // Draw submerged volume properties + if (Shape::sDrawSubmergedVolumes) + { + RVec3 center_of_buoyancy = mPosition + relative_center_of_buoyancy; + DebugRenderer::sInstance->DrawMarker(center_of_buoyancy, Color::sWhite, 2.0f); + DebugRenderer::sInstance->DrawText3D(center_of_buoyancy, StringFormat("%.3f / %.3f", (double)submerged_volume, (double)total_volume)); + } + #endif // JPH_DEBUG_RENDERER + + // When buoyancy is 1 we want neutral buoyancy, this means that the density of the liquid is the same as the density of the body at that point. + // Buoyancy > 1 should make the object float, < 1 should make it sink. + float inverse_mass = mMotionProperties->GetInverseMass(); + float fluid_density = inBuoyancy / (total_volume * inverse_mass); + + // Buoyancy force = Density of Fluid * Submerged volume * Magnitude of gravity * Up direction (eq 2.5.1) + // Impulse = Force * Delta time + // We should apply this at the center of buoyancy (= center of mass of submerged volume) + Vec3 buoyancy_impulse = -fluid_density * submerged_volume * mMotionProperties->GetGravityFactor() * inGravity * inDeltaTime; + + // Calculate the velocity of the center of buoyancy relative to the fluid + Vec3 linear_velocity = mMotionProperties->GetLinearVelocity(); + Vec3 angular_velocity = mMotionProperties->GetAngularVelocity(); + Vec3 center_of_buoyancy_velocity = linear_velocity + angular_velocity.Cross(relative_center_of_buoyancy); + Vec3 relative_center_of_buoyancy_velocity = inFluidVelocity - center_of_buoyancy_velocity; + + // Here we deviate from the article, instead of eq 2.5.14 we use a quadratic drag formula: https://en.wikipedia.org/wiki/Drag_%28physics%29 + // Drag force = 0.5 * Fluid Density * (Velocity of fluid - Velocity of center of buoyancy)^2 * Linear Drag * Area Facing the Relative Fluid Velocity + // Again Impulse = Force * Delta Time + // We should apply this at the center of buoyancy (= center of mass for submerged volume with no center of mass offset) + + // Get size of local bounding box + Vec3 size = mShape->GetLocalBounds().GetSize(); + + // Determine area of the local space bounding box in the direction of the relative velocity between the fluid and the center of buoyancy + float area = 0.0f; + float relative_center_of_buoyancy_velocity_len_sq = relative_center_of_buoyancy_velocity.LengthSq(); + if (relative_center_of_buoyancy_velocity_len_sq > 1.0e-12f) + { + Vec3 local_relative_center_of_buoyancy_velocity = GetRotation().Conjugated() * relative_center_of_buoyancy_velocity; + area = local_relative_center_of_buoyancy_velocity.Abs().Dot(size.Swizzle() * size.Swizzle()) / sqrt(relative_center_of_buoyancy_velocity_len_sq); + } + + // Calculate the impulse + Vec3 drag_impulse = (0.5f * fluid_density * inLinearDrag * area * inDeltaTime) * relative_center_of_buoyancy_velocity * relative_center_of_buoyancy_velocity.Length(); + + // Clamp magnitude against current linear velocity to prevent overshoot + float linear_velocity_len_sq = linear_velocity.LengthSq(); + float drag_delta_linear_velocity_len_sq = (drag_impulse * inverse_mass).LengthSq(); + if (drag_delta_linear_velocity_len_sq > linear_velocity_len_sq) + drag_impulse *= sqrt(linear_velocity_len_sq / drag_delta_linear_velocity_len_sq); + + // Calculate the resulting delta linear velocity due to buoyancy and drag + Vec3 delta_linear_velocity = (drag_impulse + buoyancy_impulse) * inverse_mass; + mMotionProperties->AddLinearVelocityStep(delta_linear_velocity); + + // Determine average width of the body (across the three axis) + float l = (size.GetX() + size.GetY() + size.GetZ()) / 3.0f; + + // Drag torque = -Angular Drag * Mass * Submerged volume / Total volume * (Average width of body)^2 * Angular velocity (eq 2.5.15) + Vec3 drag_angular_impulse = (-inAngularDrag * submerged_volume / total_volume * inDeltaTime * Square(l) / inverse_mass) * angular_velocity; + Mat44 inv_inertia = GetInverseInertia(); + Vec3 drag_delta_angular_velocity = inv_inertia * drag_angular_impulse; + + // Clamp magnitude against the current angular velocity to prevent overshoot + float angular_velocity_len_sq = angular_velocity.LengthSq(); + float drag_delta_angular_velocity_len_sq = drag_delta_angular_velocity.LengthSq(); + if (drag_delta_angular_velocity_len_sq > angular_velocity_len_sq) + drag_delta_angular_velocity *= sqrt(angular_velocity_len_sq / drag_delta_angular_velocity_len_sq); + + // Calculate total delta angular velocity due to drag and buoyancy + Vec3 delta_angular_velocity = drag_delta_angular_velocity + inv_inertia * relative_center_of_buoyancy.Cross(buoyancy_impulse + drag_impulse); + mMotionProperties->AddAngularVelocityStep(delta_angular_velocity); + return true; + } + + return false; +} + +void Body::SaveState(StateRecorder &inStream) const +{ + // Only write properties that can change at runtime + inStream.Write(mPosition); + inStream.Write(mRotation); + + if (mMotionProperties != nullptr) + { + if (IsSoftBody()) + static_cast(mMotionProperties)->SaveState(inStream); + else + mMotionProperties->SaveState(inStream); + } +} + +void Body::RestoreState(StateRecorder &inStream) +{ + inStream.Read(mPosition); + inStream.Read(mRotation); + + if (mMotionProperties != nullptr) + { + if (IsSoftBody()) + static_cast(mMotionProperties)->RestoreState(inStream); + else + mMotionProperties->RestoreState(inStream); + + JPH_IF_ENABLE_ASSERTS(mMotionProperties->mCachedMotionType = mMotionType); + } + + // Initialize bounding box + CalculateWorldSpaceBoundsInternal(); +} + +BodyCreationSettings Body::GetBodyCreationSettings() const +{ + JPH_ASSERT(IsRigidBody()); + + BodyCreationSettings result; + + result.mPosition = GetPosition(); + result.mRotation = GetRotation(); + result.mLinearVelocity = mMotionProperties != nullptr? mMotionProperties->GetLinearVelocity() : Vec3::sZero(); + result.mAngularVelocity = mMotionProperties != nullptr? mMotionProperties->GetAngularVelocity() : Vec3::sZero(); + result.mObjectLayer = GetObjectLayer(); + result.mUserData = mUserData; + result.mCollisionGroup = GetCollisionGroup(); + result.mMotionType = GetMotionType(); + result.mAllowedDOFs = mMotionProperties != nullptr? mMotionProperties->GetAllowedDOFs() : EAllowedDOFs::All; + result.mAllowDynamicOrKinematic = mMotionProperties != nullptr; + result.mIsSensor = IsSensor(); + result.mCollideKinematicVsNonDynamic = GetCollideKinematicVsNonDynamic(); + result.mUseManifoldReduction = GetUseManifoldReduction(); + result.mApplyGyroscopicForce = GetApplyGyroscopicForce(); + result.mMotionQuality = mMotionProperties != nullptr? mMotionProperties->GetMotionQuality() : EMotionQuality::Discrete; + result.mEnhancedInternalEdgeRemoval = GetEnhancedInternalEdgeRemoval(); + result.mAllowSleeping = mMotionProperties != nullptr? GetAllowSleeping() : true; + result.mFriction = GetFriction(); + result.mRestitution = GetRestitution(); + result.mLinearDamping = mMotionProperties != nullptr? mMotionProperties->GetLinearDamping() : 0.0f; + result.mAngularDamping = mMotionProperties != nullptr? mMotionProperties->GetAngularDamping() : 0.0f; + result.mMaxLinearVelocity = mMotionProperties != nullptr? mMotionProperties->GetMaxLinearVelocity() : 0.0f; + result.mMaxAngularVelocity = mMotionProperties != nullptr? mMotionProperties->GetMaxAngularVelocity() : 0.0f; + result.mGravityFactor = mMotionProperties != nullptr? mMotionProperties->GetGravityFactor() : 1.0f; + result.mNumVelocityStepsOverride = mMotionProperties != nullptr? mMotionProperties->GetNumVelocityStepsOverride() : 0; + result.mNumPositionStepsOverride = mMotionProperties != nullptr? mMotionProperties->GetNumPositionStepsOverride() : 0; + result.mOverrideMassProperties = EOverrideMassProperties::MassAndInertiaProvided; + + // Invert inertia and mass + if (mMotionProperties != nullptr) + { + float inv_mass = mMotionProperties->GetInverseMassUnchecked(); + Mat44 inv_inertia = mMotionProperties->GetLocalSpaceInverseInertiaUnchecked(); + + // Get mass + result.mMassPropertiesOverride.mMass = inv_mass != 0.0f? 1.0f / inv_mass : FLT_MAX; + + // Get inertia + Mat44 inertia; + if (inertia.SetInversed3x3(inv_inertia)) + { + // Inertia was invertible, we can use it + result.mMassPropertiesOverride.mInertia = inertia; + } + else + { + // Prevent division by zero + Vec3 diagonal = Vec3::sMax(inv_inertia.GetDiagonal3(), Vec3::sReplicate(FLT_MIN)); + result.mMassPropertiesOverride.mInertia = Mat44::sScale(diagonal.Reciprocal()); + } + } + else + { + result.mMassPropertiesOverride.mMass = FLT_MAX; + result.mMassPropertiesOverride.mInertia = Mat44::sScale(Vec3::sReplicate(FLT_MAX)); + } + + result.SetShape(GetShape()); + + return result; +} + +SoftBodyCreationSettings Body::GetSoftBodyCreationSettings() const +{ + JPH_ASSERT(IsSoftBody()); + + SoftBodyCreationSettings result; + + result.mPosition = GetPosition(); + result.mRotation = GetRotation(); + result.mUserData = mUserData; + result.mObjectLayer = GetObjectLayer(); + result.mCollisionGroup = GetCollisionGroup(); + result.mFriction = GetFriction(); + result.mRestitution = GetRestitution(); + const SoftBodyMotionProperties *mp = static_cast(mMotionProperties); + result.mNumIterations = mp->GetNumIterations(); + result.mLinearDamping = mp->GetLinearDamping(); + result.mMaxLinearVelocity = mp->GetMaxLinearVelocity(); + result.mGravityFactor = mp->GetGravityFactor(); + result.mPressure = mp->GetPressure(); + result.mUpdatePosition = mp->GetUpdatePosition(); + result.mSettings = mp->GetSettings(); + + return result; +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/Body.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/Body.h new file mode 100644 index 00000000000..509f55f665c --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/Body.h @@ -0,0 +1,388 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +class StateRecorder; +class BodyCreationSettings; +class SoftBodyCreationSettings; + +/// A rigid body that can be simulated using the physics system +/// +/// Note that internally all properties (position, velocity etc.) are tracked relative to the center of mass of the object to simplify the simulation of the object. +/// +/// The offset between the position of the body and the center of mass position of the body is GetShape()->GetCenterOfMass(). +/// The functions that get/set the position of the body all indicate if they are relative to the center of mass or to the original position in which the shape was created. +/// +/// The linear velocity is also velocity of the center of mass, to correct for this: \f$VelocityCOM = Velocity - AngularVelocity \times ShapeCOM\f$. +class alignas(JPH_RVECTOR_ALIGNMENT) JPH_EXPORT_GCC_BUG_WORKAROUND Body : public NonCopyable +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Default constructor + Body() = default; + + /// Destructor + ~Body() { JPH_ASSERT(mMotionProperties == nullptr); } + + /// Get the id of this body + inline const BodyID & GetID() const { return mID; } + + /// Get the type of body (rigid or soft) + inline EBodyType GetBodyType() const { return mBodyType; } + + /// Check if this body is a rigid body + inline bool IsRigidBody() const { return mBodyType == EBodyType::RigidBody; } + + /// Check if this body is a soft body + inline bool IsSoftBody() const { return mBodyType == EBodyType::SoftBody; } + + /// If this body is currently actively simulating (true) or sleeping (false) + inline bool IsActive() const { return mMotionProperties != nullptr && mMotionProperties->mIndexInActiveBodies != cInactiveIndex; } + + /// Check if this body is static (not movable) + inline bool IsStatic() const { return mMotionType == EMotionType::Static; } + + /// Check if this body is kinematic (keyframed), which means that it will move according to its current velocity, but forces don't affect it + inline bool IsKinematic() const { return mMotionType == EMotionType::Kinematic; } + + /// Check if this body is dynamic, which means that it moves and forces can act on it + inline bool IsDynamic() const { return mMotionType == EMotionType::Dynamic; } + + /// Check if a body could be made kinematic or dynamic (if it was created dynamic or with mAllowDynamicOrKinematic set to true) + inline bool CanBeKinematicOrDynamic() const { return mMotionProperties != nullptr; } + + /// Change the body to a sensor. A sensor will receive collision callbacks, but will not cause any collision responses and can be used as a trigger volume. + /// The cheapest sensor (in terms of CPU usage) is a sensor with motion type Static (they can be moved around using BodyInterface::SetPosition/SetPositionAndRotation). + /// These sensors will only detect collisions with active Dynamic or Kinematic bodies. As soon as a body go to sleep, the contact point with the sensor will be lost. + /// If you make a sensor Dynamic or Kinematic and activate them, the sensor will be able to detect collisions with sleeping bodies too. An active sensor will never go to sleep automatically. + /// When you make a Dynamic or Kinematic sensor, make sure it is in an ObjectLayer that does not collide with Static bodies or other sensors to avoid extra overhead in the broad phase. + inline void SetIsSensor(bool inIsSensor) { JPH_ASSERT(IsRigidBody()); if (inIsSensor) mFlags.fetch_or(uint8(EFlags::IsSensor), memory_order_relaxed); else mFlags.fetch_and(uint8(~uint8(EFlags::IsSensor)), memory_order_relaxed); } + + /// Check if this body is a sensor. + inline bool IsSensor() const { return (mFlags.load(memory_order_relaxed) & uint8(EFlags::IsSensor)) != 0; } + + /// If kinematic objects can generate contact points against other kinematic or static objects. + /// Note that turning this on can be CPU intensive as much more collision detection work will be done without any effect on the simulation (kinematic objects are not affected by other kinematic/static objects). + /// This can be used to make sensors detect static objects. Note that the sensor must be kinematic and active for it to detect static objects. + inline void SetCollideKinematicVsNonDynamic(bool inCollide) { JPH_ASSERT(IsRigidBody()); if (inCollide) mFlags.fetch_or(uint8(EFlags::CollideKinematicVsNonDynamic), memory_order_relaxed); else mFlags.fetch_and(uint8(~uint8(EFlags::CollideKinematicVsNonDynamic)), memory_order_relaxed); } + + /// Check if kinematic objects can generate contact points against other kinematic or static objects. + inline bool GetCollideKinematicVsNonDynamic() const { return (mFlags.load(memory_order_relaxed) & uint8(EFlags::CollideKinematicVsNonDynamic)) != 0; } + + /// If PhysicsSettings::mUseManifoldReduction is true, this allows turning off manifold reduction for this specific body. + /// Manifold reduction by default will combine contacts with similar normals that come from different SubShapeIDs (e.g. different triangles in a mesh shape or different compound shapes). + /// If the application requires tracking exactly which SubShapeIDs are in contact, you can turn off manifold reduction. Note that this comes at a performance cost. + /// Consider using BodyInterface::SetUseManifoldReduction if the body could already be in contact with other bodies to ensure that the contact cache is invalidated and you get the correct contact callbacks. + inline void SetUseManifoldReduction(bool inUseReduction) { JPH_ASSERT(IsRigidBody()); if (inUseReduction) mFlags.fetch_or(uint8(EFlags::UseManifoldReduction), memory_order_relaxed); else mFlags.fetch_and(uint8(~uint8(EFlags::UseManifoldReduction)), memory_order_relaxed); } + + /// Check if this body can use manifold reduction. + inline bool GetUseManifoldReduction() const { return (mFlags.load(memory_order_relaxed) & uint8(EFlags::UseManifoldReduction)) != 0; } + + /// Checks if the combination of this body and inBody2 should use manifold reduction + inline bool GetUseManifoldReductionWithBody(const Body &inBody2) const { return ((mFlags.load(memory_order_relaxed) & inBody2.mFlags.load(memory_order_relaxed)) & uint8(EFlags::UseManifoldReduction)) != 0; } + + /// Set to indicate that the gyroscopic force should be applied to this body (aka Dzhanibekov effect, see https://en.wikipedia.org/wiki/Tennis_racket_theorem) + inline void SetApplyGyroscopicForce(bool inApply) { JPH_ASSERT(IsRigidBody()); if (inApply) mFlags.fetch_or(uint8(EFlags::ApplyGyroscopicForce), memory_order_relaxed); else mFlags.fetch_and(uint8(~uint8(EFlags::ApplyGyroscopicForce)), memory_order_relaxed); } + + /// Check if the gyroscopic force is being applied for this body + inline bool GetApplyGyroscopicForce() const { return (mFlags.load(memory_order_relaxed) & uint8(EFlags::ApplyGyroscopicForce)) != 0; } + + /// Set to indicate that extra effort should be made to try to remove ghost contacts (collisions with internal edges of a mesh). This is more expensive but makes bodies move smoother over a mesh with convex edges. + inline void SetEnhancedInternalEdgeRemoval(bool inApply) { JPH_ASSERT(IsRigidBody()); if (inApply) mFlags.fetch_or(uint8(EFlags::EnhancedInternalEdgeRemoval), memory_order_relaxed); else mFlags.fetch_and(uint8(~uint8(EFlags::EnhancedInternalEdgeRemoval)), memory_order_relaxed); } + + /// Check if enhanced internal edge removal is turned on + inline bool GetEnhancedInternalEdgeRemoval() const { return (mFlags.load(memory_order_relaxed) & uint8(EFlags::EnhancedInternalEdgeRemoval)) != 0; } + + /// Checks if the combination of this body and inBody2 should use enhanced internal edge removal + inline bool GetEnhancedInternalEdgeRemovalWithBody(const Body &inBody2) const { return ((mFlags.load(memory_order_relaxed) | inBody2.mFlags.load(memory_order_relaxed)) & uint8(EFlags::EnhancedInternalEdgeRemoval)) != 0; } + + /// Get the bodies motion type. + inline EMotionType GetMotionType() const { return mMotionType; } + + /// Set the motion type of this body. Consider using BodyInterface::SetMotionType instead of this function if the body may be active or if it needs to be activated. + void SetMotionType(EMotionType inMotionType); + + /// Get broadphase layer, this determines in which broad phase sub-tree the object is placed + inline BroadPhaseLayer GetBroadPhaseLayer() const { return mBroadPhaseLayer; } + + /// Get object layer, this determines which other objects it collides with + inline ObjectLayer GetObjectLayer() const { return mObjectLayer; } + + /// Collision group and sub-group ID, determines which other objects it collides with + const CollisionGroup & GetCollisionGroup() const { return mCollisionGroup; } + CollisionGroup & GetCollisionGroup() { return mCollisionGroup; } + void SetCollisionGroup(const CollisionGroup &inGroup) { mCollisionGroup = inGroup; } + + /// If this body can go to sleep. Note that disabling sleeping on a sleeping object will not wake it up. + bool GetAllowSleeping() const { return mMotionProperties->mAllowSleeping; } + void SetAllowSleeping(bool inAllow); + + /// Resets the sleep timer. This does not wake up the body if it is sleeping, but allows resetting the system that detects when a body is sleeping. + inline void ResetSleepTimer(); + + /// Friction (dimensionless number, usually between 0 and 1, 0 = no friction, 1 = friction force equals force that presses the two bodies together). Note that bodies can have negative friction but the combined friction (see PhysicsSystem::SetCombineFriction) should never go below zero. + inline float GetFriction() const { return mFriction; } + void SetFriction(float inFriction) { mFriction = inFriction; } + + /// Restitution (dimensionless number, usually between 0 and 1, 0 = completely inelastic collision response, 1 = completely elastic collision response). Note that bodies can have negative restitution but the combined restitution (see PhysicsSystem::SetCombineRestitution) should never go below zero. + inline float GetRestitution() const { return mRestitution; } + void SetRestitution(float inRestitution) { mRestitution = inRestitution; } + + /// Get world space linear velocity of the center of mass (unit: m/s) + inline Vec3 GetLinearVelocity() const { return !IsStatic()? mMotionProperties->GetLinearVelocity() : Vec3::sZero(); } + + /// Set world space linear velocity of the center of mass (unit: m/s) + void SetLinearVelocity(Vec3Arg inLinearVelocity) { JPH_ASSERT(!IsStatic()); mMotionProperties->SetLinearVelocity(inLinearVelocity); } + + /// Set world space linear velocity of the center of mass, will make sure the value is clamped against the maximum linear velocity + void SetLinearVelocityClamped(Vec3Arg inLinearVelocity) { JPH_ASSERT(!IsStatic()); mMotionProperties->SetLinearVelocityClamped(inLinearVelocity); } + + /// Get world space angular velocity of the center of mass (unit: rad/s) + inline Vec3 GetAngularVelocity() const { return !IsStatic()? mMotionProperties->GetAngularVelocity() : Vec3::sZero(); } + + /// Set world space angular velocity of the center of mass (unit: rad/s) + void SetAngularVelocity(Vec3Arg inAngularVelocity) { JPH_ASSERT(!IsStatic()); mMotionProperties->SetAngularVelocity(inAngularVelocity); } + + /// Set world space angular velocity of the center of mass, will make sure the value is clamped against the maximum angular velocity + void SetAngularVelocityClamped(Vec3Arg inAngularVelocity) { JPH_ASSERT(!IsStatic()); mMotionProperties->SetAngularVelocityClamped(inAngularVelocity); } + + /// Velocity of point inPoint (in center of mass space, e.g. on the surface of the body) of the body (unit: m/s) + inline Vec3 GetPointVelocityCOM(Vec3Arg inPointRelativeToCOM) const { return !IsStatic()? mMotionProperties->GetPointVelocityCOM(inPointRelativeToCOM) : Vec3::sZero(); } + + /// Velocity of point inPoint (in world space, e.g. on the surface of the body) of the body (unit: m/s) + inline Vec3 GetPointVelocity(RVec3Arg inPoint) const { JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sPositionAccess, BodyAccess::EAccess::Read)); return GetPointVelocityCOM(Vec3(inPoint - mPosition)); } + + /// Add force (unit: N) at center of mass for the next time step, will be reset after the next call to PhysicsSystem::Update + inline void AddForce(Vec3Arg inForce) { JPH_ASSERT(IsDynamic()); (Vec3::sLoadFloat3Unsafe(mMotionProperties->mForce) + inForce).StoreFloat3(&mMotionProperties->mForce); } + + /// Add force (unit: N) at inPosition for the next time step, will be reset after the next call to PhysicsSystem::Update + inline void AddForce(Vec3Arg inForce, RVec3Arg inPosition); + + /// Add torque (unit: N m) for the next time step, will be reset after the next call to PhysicsSystem::Update + inline void AddTorque(Vec3Arg inTorque) { JPH_ASSERT(IsDynamic()); (Vec3::sLoadFloat3Unsafe(mMotionProperties->mTorque) + inTorque).StoreFloat3(&mMotionProperties->mTorque); } + + // Get the total amount of force applied to the center of mass this time step (through AddForce calls). Note that it will reset to zero after PhysicsSystem::Update. + inline Vec3 GetAccumulatedForce() const { JPH_ASSERT(IsDynamic()); return mMotionProperties->GetAccumulatedForce(); } + + // Get the total amount of torque applied to the center of mass this time step (through AddForce/AddTorque calls). Note that it will reset to zero after PhysicsSystem::Update. + inline Vec3 GetAccumulatedTorque() const { JPH_ASSERT(IsDynamic()); return mMotionProperties->GetAccumulatedTorque(); } + + // Reset the total accumulated force, not that this will be done automatically after every time step. + JPH_INLINE void ResetForce() { JPH_ASSERT(IsDynamic()); return mMotionProperties->ResetForce(); } + + // Reset the total accumulated torque, not that this will be done automatically after every time step. + JPH_INLINE void ResetTorque() { JPH_ASSERT(IsDynamic()); return mMotionProperties->ResetTorque(); } + + // Reset the current velocity and accumulated force and torque. + JPH_INLINE void ResetMotion() { JPH_ASSERT(!IsStatic()); return mMotionProperties->ResetMotion(); } + + /// Get inverse inertia tensor in world space + inline Mat44 GetInverseInertia() const; + + /// Add impulse to center of mass (unit: kg m/s) + inline void AddImpulse(Vec3Arg inImpulse); + + /// Add impulse to point in world space (unit: kg m/s) + inline void AddImpulse(Vec3Arg inImpulse, RVec3Arg inPosition); + + /// Add angular impulse in world space (unit: N m s) + inline void AddAngularImpulse(Vec3Arg inAngularImpulse); + + /// Set velocity of body such that it will be positioned at inTargetPosition/Rotation in inDeltaTime seconds. + void MoveKinematic(RVec3Arg inTargetPosition, QuatArg inTargetRotation, float inDeltaTime); + + /// Applies an impulse to the body that simulates fluid buoyancy and drag + /// @param inSurfacePosition Position of the fluid surface in world space + /// @param inSurfaceNormal Normal of the fluid surface (should point up) + /// @param inBuoyancy The buoyancy factor for the body. 1 = neutral body, < 1 sinks, > 1 floats. Note that we don't use the fluid density since it is harder to configure than a simple number between [0, 2] + /// @param inLinearDrag Linear drag factor that slows down the body when in the fluid (approx. 0.5) + /// @param inAngularDrag Angular drag factor that slows down rotation when the body is in the fluid (approx. 0.01) + /// @param inFluidVelocity The average velocity of the fluid (in m/s) in which the body resides + /// @param inGravity The gravity vector (pointing down) + /// @param inDeltaTime Delta time of the next simulation step (in s) + /// @return true if an impulse was applied, false if the body was not in the fluid + bool ApplyBuoyancyImpulse(RVec3Arg inSurfacePosition, Vec3Arg inSurfaceNormal, float inBuoyancy, float inLinearDrag, float inAngularDrag, Vec3Arg inFluidVelocity, Vec3Arg inGravity, float inDeltaTime); + + /// Check if this body has been added to the physics system + inline bool IsInBroadPhase() const { return (mFlags.load(memory_order_relaxed) & uint8(EFlags::IsInBroadPhase)) != 0; } + + /// Check if this body has been changed in such a way that the collision cache should be considered invalid for any body interacting with this body + inline bool IsCollisionCacheInvalid() const { return (mFlags.load(memory_order_relaxed) & uint8(EFlags::InvalidateContactCache)) != 0; } + + /// Get the shape of this body + inline const Shape * GetShape() const { return mShape; } + + /// World space position of the body + inline RVec3 GetPosition() const { JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sPositionAccess, BodyAccess::EAccess::Read)); return mPosition - mRotation * mShape->GetCenterOfMass(); } + + /// World space rotation of the body + inline Quat GetRotation() const { JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sPositionAccess, BodyAccess::EAccess::Read)); return mRotation; } + + /// Calculates the transform of this body + inline RMat44 GetWorldTransform() const; + + /// Gets the world space position of this body's center of mass + inline RVec3 GetCenterOfMassPosition() const { JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sPositionAccess, BodyAccess::EAccess::Read)); return mPosition; } + + /// Calculates the transform for this body's center of mass + inline RMat44 GetCenterOfMassTransform() const; + + /// Calculates the inverse of the transform for this body's center of mass + inline RMat44 GetInverseCenterOfMassTransform() const; + + /// Get world space bounding box + inline const AABox & GetWorldSpaceBounds() const { return mBounds; } + + /// Access to the motion properties + const MotionProperties *GetMotionProperties() const { JPH_ASSERT(!IsStatic()); return mMotionProperties; } + MotionProperties * GetMotionProperties() { JPH_ASSERT(!IsStatic()); return mMotionProperties; } + + /// Access to the motion properties (version that does not check if the object is kinematic or dynamic) + const MotionProperties *GetMotionPropertiesUnchecked() const { return mMotionProperties; } + MotionProperties * GetMotionPropertiesUnchecked() { return mMotionProperties; } + + /// Access to the user data, can be used for anything by the application + uint64 GetUserData() const { return mUserData; } + void SetUserData(uint64 inUserData) { mUserData = inUserData; } + + /// Get surface normal of a particular sub shape and its world space surface position on this body + inline Vec3 GetWorldSpaceSurfaceNormal(const SubShapeID &inSubShapeID, RVec3Arg inPosition) const; + + /// Get the transformed shape of this body, which can be used to do collision detection outside of a body lock + inline TransformedShape GetTransformedShape() const { JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sPositionAccess, BodyAccess::EAccess::Read)); return TransformedShape(mPosition, mRotation, mShape, mID); } + + /// Debug function to convert a body back to a body creation settings object to be able to save/recreate the body later + BodyCreationSettings GetBodyCreationSettings() const; + + /// Debug function to convert a soft body back to a soft body creation settings object to be able to save/recreate the body later + SoftBodyCreationSettings GetSoftBodyCreationSettings() const; + + /// A dummy body that can be used by constraints to attach a constraint to the world instead of another body + static Body sFixedToWorld; + + ///@name THESE FUNCTIONS ARE FOR INTERNAL USE ONLY AND SHOULD NOT BE CALLED BY THE APPLICATION + ///@{ + + /// Helper function for BroadPhase::FindCollidingPairs that returns true when two bodies can collide + /// It assumes that body 1 is dynamic and active and guarantees that it body 1 collides with body 2 that body 2 will not collide with body 1 in order to avoid finding duplicate collision pairs + static inline bool sFindCollidingPairsCanCollide(const Body &inBody1, const Body &inBody2); + + /// Update position using an Euler step (used during position integrate & constraint solving) + inline void AddPositionStep(Vec3Arg inLinearVelocityTimesDeltaTime) { JPH_ASSERT(IsRigidBody()); JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sPositionAccess, BodyAccess::EAccess::ReadWrite)); mPosition += mMotionProperties->LockTranslation(inLinearVelocityTimesDeltaTime); JPH_ASSERT(!mPosition.IsNaN()); } + inline void SubPositionStep(Vec3Arg inLinearVelocityTimesDeltaTime) { JPH_ASSERT(IsRigidBody()); JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sPositionAccess, BodyAccess::EAccess::ReadWrite)); mPosition -= mMotionProperties->LockTranslation(inLinearVelocityTimesDeltaTime); JPH_ASSERT(!mPosition.IsNaN()); } + + /// Update rotation using an Euler step (using during position integrate & constraint solving) + inline void AddRotationStep(Vec3Arg inAngularVelocityTimesDeltaTime); + inline void SubRotationStep(Vec3Arg inAngularVelocityTimesDeltaTime); + + /// Flag if body is in the broadphase (should only be called by the BroadPhase) + inline void SetInBroadPhaseInternal(bool inInBroadPhase) { if (inInBroadPhase) mFlags.fetch_or(uint8(EFlags::IsInBroadPhase), memory_order_relaxed); else mFlags.fetch_and(uint8(~uint8(EFlags::IsInBroadPhase)), memory_order_relaxed); } + + /// Invalidate the contact cache (should only be called by the BodyManager), will be reset the next simulation step. Returns true if the contact cache was still valid. + inline bool InvalidateContactCacheInternal() { return (mFlags.fetch_or(uint8(EFlags::InvalidateContactCache), memory_order_relaxed) & uint8(EFlags::InvalidateContactCache)) == 0; } + + /// Reset the collision cache invalid flag (should only be called by the BodyManager). + inline void ValidateContactCacheInternal() { JPH_IF_ENABLE_ASSERTS(uint8 old_val = ) mFlags.fetch_and(uint8(~uint8(EFlags::InvalidateContactCache)), memory_order_relaxed); JPH_ASSERT((old_val & uint8(EFlags::InvalidateContactCache)) != 0); } + + /// Updates world space bounding box (should only be called by the PhysicsSystem) + void CalculateWorldSpaceBoundsInternal(); + + /// Function to update body's position (should only be called by the BodyInterface since it also requires updating the broadphase) + void SetPositionAndRotationInternal(RVec3Arg inPosition, QuatArg inRotation, bool inResetSleepTimer = true); + + /// Updates the center of mass and optionally mass properties after shifting the center of mass or changes to the shape (should only be called by the BodyInterface since it also requires updating the broadphase) + /// @param inPreviousCenterOfMass Center of mass of the shape before the alterations + /// @param inUpdateMassProperties When true, the mass and inertia tensor is recalculated + void UpdateCenterOfMassInternal(Vec3Arg inPreviousCenterOfMass, bool inUpdateMassProperties); + + /// Function to update a body's shape (should only be called by the BodyInterface since it also requires updating the broadphase) + /// @param inShape The new shape for this body + /// @param inUpdateMassProperties When true, the mass and inertia tensor is recalculated + void SetShapeInternal(const Shape *inShape, bool inUpdateMassProperties); + + /// Access to the index in the BodyManager::mActiveBodies list + uint32 GetIndexInActiveBodiesInternal() const { return mMotionProperties != nullptr? mMotionProperties->mIndexInActiveBodies : cInactiveIndex; } + + /// Update eligibility for sleeping + ECanSleep UpdateSleepStateInternal(float inDeltaTime, float inMaxMovement, float inTimeBeforeSleep); + + /// Saving state for replay + void SaveState(StateRecorder &inStream) const; + + /// Restoring state for replay + void RestoreState(StateRecorder &inStream); + + ///@} + + static constexpr uint32 cInactiveIndex = MotionProperties::cInactiveIndex; ///< Constant indicating that body is not active + +private: + friend class BodyManager; + + explicit Body(bool); ///< Alternative constructor that initializes all members + + inline void GetSleepTestPoints(RVec3 *outPoints) const; ///< Determine points to test for checking if body is sleeping: COM, COM + largest bounding box axis, COM + second largest bounding box axis + + enum class EFlags : uint8 + { + IsSensor = 1 << 0, ///< If this object is a sensor. A sensor will receive collision callbacks, but will not cause any collision responses and can be used as a trigger volume. + CollideKinematicVsNonDynamic = 1 << 1, ///< If kinematic objects can generate contact points against other kinematic or static objects. + IsInBroadPhase = 1 << 2, ///< Set this bit to indicate that the body is in the broadphase + InvalidateContactCache = 1 << 3, ///< Set this bit to indicate that all collision caches for this body are invalid, will be reset the next simulation step. + UseManifoldReduction = 1 << 4, ///< Set this bit to indicate that this body can use manifold reduction (if PhysicsSettings::mUseManifoldReduction is true) + ApplyGyroscopicForce = 1 << 5, ///< Set this bit to indicate that the gyroscopic force should be applied to this body (aka Dzhanibekov effect, see https://en.wikipedia.org/wiki/Tennis_racket_theorem) + EnhancedInternalEdgeRemoval = 1 << 6, ///< Set this bit to indicate that enhanced internal edge removal should be used for this body (see BodyCreationSettings::mEnhancedInternalEdgeRemoval) + }; + + // 16 byte aligned + RVec3 mPosition; ///< World space position of center of mass + Quat mRotation; ///< World space rotation of center of mass + AABox mBounds; ///< World space bounding box of the body + + // 8 byte aligned + RefConst mShape; ///< Shape representing the volume of this body + MotionProperties * mMotionProperties = nullptr; ///< If this is a keyframed or dynamic object, this object holds all information about the movement + uint64 mUserData = 0; ///< User data, can be used for anything by the application + CollisionGroup mCollisionGroup; ///< The collision group this body belongs to (determines if two objects can collide) + + // 4 byte aligned + float mFriction; ///< Friction of the body (dimensionless number, usually between 0 and 1, 0 = no friction, 1 = friction force equals force that presses the two bodies together). Note that bodies can have negative friction but the combined friction (see PhysicsSystem::SetCombineFriction) should never go below zero. + float mRestitution; ///< Restitution of body (dimensionless number, usually between 0 and 1, 0 = completely inelastic collision response, 1 = completely elastic collision response). Note that bodies can have negative restitution but the combined restitution (see PhysicsSystem::SetCombineRestitution) should never go below zero. + BodyID mID; ///< ID of the body (index in the bodies array) + + // 2 or 4 bytes aligned + ObjectLayer mObjectLayer; ///< The collision layer this body belongs to (determines if two objects can collide) + + // 1 byte aligned + EBodyType mBodyType; ///< Type of body (rigid or soft) + BroadPhaseLayer mBroadPhaseLayer; ///< The broad phase layer this body belongs to + EMotionType mMotionType; ///< Type of motion (static, dynamic or kinematic) + atomic mFlags = 0; ///< See EFlags for possible flags + + // 122 bytes up to here (64-bit mode, single precision, 16-bit ObjectLayer) +}; + +static_assert(JPH_CPU_ADDRESS_BITS != 64 || sizeof(Body) == JPH_IF_SINGLE_PRECISION_ELSE(128, 160), "Body size is incorrect"); +static_assert(alignof(Body) == JPH_RVECTOR_ALIGNMENT, "Body should properly align"); + +JPH_NAMESPACE_END + +#include "Body.inl" diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/Body.inl b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/Body.inl new file mode 100644 index 00000000000..51bc64878c3 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/Body.inl @@ -0,0 +1,197 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +JPH_NAMESPACE_BEGIN + +RMat44 Body::GetWorldTransform() const +{ + JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sPositionAccess, BodyAccess::EAccess::Read)); + + return RMat44::sRotationTranslation(mRotation, mPosition).PreTranslated(-mShape->GetCenterOfMass()); +} + +RMat44 Body::GetCenterOfMassTransform() const +{ + JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sPositionAccess, BodyAccess::EAccess::Read)); + + return RMat44::sRotationTranslation(mRotation, mPosition); +} + +RMat44 Body::GetInverseCenterOfMassTransform() const +{ + JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sPositionAccess, BodyAccess::EAccess::Read)); + + return RMat44::sInverseRotationTranslation(mRotation, mPosition); +} + +inline bool Body::sFindCollidingPairsCanCollide(const Body &inBody1, const Body &inBody2) +{ + // First body should never be a soft body + JPH_ASSERT(!inBody1.IsSoftBody()); + + // One of these conditions must be true + // - We always allow detecting collisions between kinematic and non-dynamic bodies + // - One of the bodies must be dynamic to collide + // - A kinematic object can collide with a sensor + if (!inBody1.GetCollideKinematicVsNonDynamic() + && !inBody2.GetCollideKinematicVsNonDynamic() + && (!inBody1.IsDynamic() && !inBody2.IsDynamic()) + && !(inBody1.IsKinematic() && inBody2.IsSensor()) + && !(inBody2.IsKinematic() && inBody1.IsSensor())) + return false; + + // Check that body 1 is active + uint32 body1_index_in_active_bodies = inBody1.GetIndexInActiveBodiesInternal(); + JPH_ASSERT(!inBody1.IsStatic() && body1_index_in_active_bodies != Body::cInactiveIndex, "This function assumes that Body 1 is active"); + + // If the pair A, B collides we need to ensure that the pair B, A does not collide or else we will handle the collision twice. + // If A is the same body as B we don't want to collide (1) + // If A is dynamic / kinematic and B is static we should collide (2) + // If A is dynamic / kinematic and B is dynamic / kinematic we should only collide if + // - A is active and B is not active (3) + // - A is active and B will become active during this simulation step (4) + // - A is active and B is active, we require a condition that makes A, B collide and B, A not (5) + // + // In order to implement this we use the index in the active body list and make use of the fact that + // a body not in the active list has Body.Index = 0xffffffff which is the highest possible value for an uint32. + // + // Because we know that A is active we know that A.Index != 0xffffffff: + // (1) Because A.Index != 0xffffffff, if A.Index = B.Index then A = B, so to collide A.Index != B.Index + // (2) A.Index != 0xffffffff, B.Index = 0xffffffff (because it's static and cannot be in the active list), so to collide A.Index != B.Index + // (3) A.Index != 0xffffffff, B.Index = 0xffffffff (because it's not yet active), so to collide A.Index != B.Index + // (4) A.Index != 0xffffffff, B.Index = 0xffffffff currently. But it can activate during the Broad/NarrowPhase step at which point it + // will be added to the end of the active list which will make B.Index > A.Index (this holds only true when we don't deactivate + // bodies during the Broad/NarrowPhase step), so to collide A.Index < B.Index. + // (5) As tie breaker we can use the same condition A.Index < B.Index to collide, this means that if A, B collides then B, A won't + static_assert(Body::cInactiveIndex == 0xffffffff, "The algorithm below uses this value"); + if (!inBody2.IsSoftBody() && body1_index_in_active_bodies >= inBody2.GetIndexInActiveBodiesInternal()) + return false; + JPH_ASSERT(inBody1.GetID() != inBody2.GetID(), "Read the comment above, A and B are the same body which should not be possible!"); + + // Check collision group filter + if (!inBody1.GetCollisionGroup().CanCollide(inBody2.GetCollisionGroup())) + return false; + + return true; +} + +void Body::AddRotationStep(Vec3Arg inAngularVelocityTimesDeltaTime) +{ + JPH_ASSERT(IsRigidBody()); + JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sPositionAccess, BodyAccess::EAccess::ReadWrite)); + + // This used to use the equation: d/dt R(t) = 1/2 * w(t) * R(t) so that R(t + dt) = R(t) + 1/2 * w(t) * R(t) * dt + // See: Appendix B of An Introduction to Physically Based Modeling: Rigid Body Simulation II-Nonpenetration Constraints + // URL: https://www.cs.cmu.edu/~baraff/sigcourse/notesd2.pdf + // But this is a first order approximation and does not work well for kinematic ragdolls that are driven to a new + // pose if the poses differ enough. So now we split w(t) * dt into an axis and angle part and create a quaternion with it. + // Note that the resulting quaternion is normalized since otherwise numerical drift will eventually make the rotation non-normalized. + float len = inAngularVelocityTimesDeltaTime.Length(); + if (len > 1.0e-6f) + { + mRotation = (Quat::sRotation(inAngularVelocityTimesDeltaTime / len, len) * mRotation).Normalized(); + JPH_ASSERT(!mRotation.IsNaN()); + } +} + +void Body::SubRotationStep(Vec3Arg inAngularVelocityTimesDeltaTime) +{ + JPH_ASSERT(IsRigidBody()); + JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sPositionAccess, BodyAccess::EAccess::ReadWrite)); + + // See comment at Body::AddRotationStep + float len = inAngularVelocityTimesDeltaTime.Length(); + if (len > 1.0e-6f) + { + mRotation = (Quat::sRotation(inAngularVelocityTimesDeltaTime / len, -len) * mRotation).Normalized(); + JPH_ASSERT(!mRotation.IsNaN()); + } +} + +Vec3 Body::GetWorldSpaceSurfaceNormal(const SubShapeID &inSubShapeID, RVec3Arg inPosition) const +{ + RMat44 inv_com = GetInverseCenterOfMassTransform(); + return inv_com.Multiply3x3Transposed(mShape->GetSurfaceNormal(inSubShapeID, Vec3(inv_com * inPosition))).Normalized(); +} + +Mat44 Body::GetInverseInertia() const +{ + JPH_ASSERT(IsDynamic()); + + return GetMotionProperties()->GetInverseInertiaForRotation(Mat44::sRotation(mRotation)); +} + +void Body::AddForce(Vec3Arg inForce, RVec3Arg inPosition) +{ + AddForce(inForce); + AddTorque(Vec3(inPosition - mPosition).Cross(inForce)); +} + +void Body::AddImpulse(Vec3Arg inImpulse) +{ + JPH_ASSERT(IsDynamic()); + + SetLinearVelocityClamped(mMotionProperties->GetLinearVelocity() + inImpulse * mMotionProperties->GetInverseMass()); +} + +void Body::AddImpulse(Vec3Arg inImpulse, RVec3Arg inPosition) +{ + JPH_ASSERT(IsDynamic()); + + SetLinearVelocityClamped(mMotionProperties->GetLinearVelocity() + inImpulse * mMotionProperties->GetInverseMass()); + + SetAngularVelocityClamped(mMotionProperties->GetAngularVelocity() + mMotionProperties->MultiplyWorldSpaceInverseInertiaByVector(mRotation, Vec3(inPosition - mPosition).Cross(inImpulse))); +} + +void Body::AddAngularImpulse(Vec3Arg inAngularImpulse) +{ + JPH_ASSERT(IsDynamic()); + + SetAngularVelocityClamped(mMotionProperties->GetAngularVelocity() + mMotionProperties->MultiplyWorldSpaceInverseInertiaByVector(mRotation, inAngularImpulse)); +} + +void Body::GetSleepTestPoints(RVec3 *outPoints) const +{ + JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sPositionAccess, BodyAccess::EAccess::Read)); + + // Center of mass is the first position + outPoints[0] = mPosition; + + // The second and third position are on the largest axis of the bounding box + Vec3 extent = mShape->GetLocalBounds().GetExtent(); + int lowest_component = extent.GetLowestComponentIndex(); + Mat44 rotation = Mat44::sRotation(mRotation); + switch (lowest_component) + { + case 0: + outPoints[1] = mPosition + extent.GetY() * rotation.GetColumn3(1); + outPoints[2] = mPosition + extent.GetZ() * rotation.GetColumn3(2); + break; + + case 1: + outPoints[1] = mPosition + extent.GetX() * rotation.GetColumn3(0); + outPoints[2] = mPosition + extent.GetZ() * rotation.GetColumn3(2); + break; + + case 2: + outPoints[1] = mPosition + extent.GetX() * rotation.GetColumn3(0); + outPoints[2] = mPosition + extent.GetY() * rotation.GetColumn3(1); + break; + + default: + JPH_ASSERT(false); + break; + } +} + +void Body::ResetSleepTimer() +{ + RVec3 points[3]; + GetSleepTestPoints(points); + mMotionProperties->ResetSleepTestSpheres(points); +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyAccess.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyAccess.cpp new file mode 100644 index 00000000000..b343e717c5c --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyAccess.cpp @@ -0,0 +1,18 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include + +#ifdef JPH_ENABLE_ASSERTS + +JPH_NAMESPACE_BEGIN + +thread_local BodyAccess::EAccess BodyAccess::sVelocityAccess = BodyAccess::EAccess::ReadWrite; +thread_local BodyAccess::EAccess BodyAccess::sPositionAccess = BodyAccess::EAccess::ReadWrite; + +JPH_NAMESPACE_END + +#endif diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyAccess.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyAccess.h new file mode 100644 index 00000000000..426460aac5e --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyAccess.h @@ -0,0 +1,55 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#ifdef JPH_ENABLE_ASSERTS + +JPH_NAMESPACE_BEGIN + +class BodyAccess +{ +public: + /// Access rules, used to detect race conditions during simulation + enum class EAccess : uint8 + { + None = 0, + Read = 1, + ReadWrite = 3, + }; + + /// Grant a scope specific access rights on the current thread + class Grant + { + public: + inline Grant(EAccess inVelocity, EAccess inPosition) + { + JPH_ASSERT(sVelocityAccess == EAccess::ReadWrite); + JPH_ASSERT(sPositionAccess == EAccess::ReadWrite); + + sVelocityAccess = inVelocity; + sPositionAccess = inPosition; + } + + inline ~Grant() + { + sVelocityAccess = EAccess::ReadWrite; + sPositionAccess = EAccess::ReadWrite; + } + }; + + /// Check if we have permission + static bool sCheckRights(EAccess inRights, EAccess inDesiredRights) + { + return (uint8(inRights) & uint8(inDesiredRights)) == uint8(inDesiredRights); + } + + // Various permissions that can be granted + static thread_local EAccess sVelocityAccess; + static thread_local EAccess sPositionAccess; +}; + +JPH_NAMESPACE_END + +#endif // JPH_ENABLE_ASSERTS diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyActivationListener.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyActivationListener.h new file mode 100644 index 00000000000..2c8808a1505 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyActivationListener.h @@ -0,0 +1,28 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +JPH_NAMESPACE_BEGIN + +class BodyID; + +/// A listener class that receives events when a body activates or deactivates. +/// It can be registered with the BodyManager (or PhysicsSystem). +class BodyActivationListener +{ +public: + /// Ensure virtual destructor + virtual ~BodyActivationListener() = default; + + /// Called whenever a body activates, note this can be called from any thread so make sure your code is thread safe. + /// At the time of the callback the body inBodyID will be locked and no bodies can be written/activated/deactivated from the callback. + virtual void OnBodyActivated(const BodyID &inBodyID, uint64 inBodyUserData) = 0; + + /// Called whenever a body deactivates, note this can be called from any thread so make sure your code is thread safe. + /// At the time of the callback the body inBodyID will be locked and no bodies can be written/activated/deactivated from the callback. + virtual void OnBodyDeactivated(const BodyID &inBodyID, uint64 inBodyUserData) = 0; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyCreationSettings.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyCreationSettings.cpp new file mode 100644 index 00000000000..9b6d3f92c41 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyCreationSettings.cpp @@ -0,0 +1,234 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(BodyCreationSettings) +{ + JPH_ADD_ATTRIBUTE(BodyCreationSettings, mPosition) + JPH_ADD_ATTRIBUTE(BodyCreationSettings, mRotation) + JPH_ADD_ATTRIBUTE(BodyCreationSettings, mLinearVelocity) + JPH_ADD_ATTRIBUTE(BodyCreationSettings, mAngularVelocity) + JPH_ADD_ATTRIBUTE(BodyCreationSettings, mUserData) + JPH_ADD_ATTRIBUTE(BodyCreationSettings, mShape) + JPH_ADD_ATTRIBUTE(BodyCreationSettings, mCollisionGroup) + JPH_ADD_ENUM_ATTRIBUTE(BodyCreationSettings, mObjectLayer) + JPH_ADD_ENUM_ATTRIBUTE(BodyCreationSettings, mMotionType) + JPH_ADD_ENUM_ATTRIBUTE(BodyCreationSettings, mAllowedDOFs) + JPH_ADD_ATTRIBUTE(BodyCreationSettings, mAllowDynamicOrKinematic) + JPH_ADD_ATTRIBUTE(BodyCreationSettings, mIsSensor) + JPH_ADD_ATTRIBUTE_WITH_ALIAS(BodyCreationSettings, mCollideKinematicVsNonDynamic, "mSensorDetectsStatic") // This is the old name to keep backwards compatibility + JPH_ADD_ATTRIBUTE(BodyCreationSettings, mUseManifoldReduction) + JPH_ADD_ATTRIBUTE(BodyCreationSettings, mApplyGyroscopicForce) + JPH_ADD_ENUM_ATTRIBUTE(BodyCreationSettings, mMotionQuality) + JPH_ADD_ATTRIBUTE(BodyCreationSettings, mEnhancedInternalEdgeRemoval) + JPH_ADD_ATTRIBUTE(BodyCreationSettings, mAllowSleeping) + JPH_ADD_ATTRIBUTE(BodyCreationSettings, mFriction) + JPH_ADD_ATTRIBUTE(BodyCreationSettings, mRestitution) + JPH_ADD_ATTRIBUTE(BodyCreationSettings, mLinearDamping) + JPH_ADD_ATTRIBUTE(BodyCreationSettings, mAngularDamping) + JPH_ADD_ATTRIBUTE(BodyCreationSettings, mMaxLinearVelocity) + JPH_ADD_ATTRIBUTE(BodyCreationSettings, mMaxAngularVelocity) + JPH_ADD_ATTRIBUTE(BodyCreationSettings, mGravityFactor) + JPH_ADD_ATTRIBUTE(BodyCreationSettings, mNumVelocityStepsOverride) + JPH_ADD_ATTRIBUTE(BodyCreationSettings, mNumPositionStepsOverride) + JPH_ADD_ENUM_ATTRIBUTE(BodyCreationSettings, mOverrideMassProperties) + JPH_ADD_ATTRIBUTE(BodyCreationSettings, mInertiaMultiplier) + JPH_ADD_ATTRIBUTE(BodyCreationSettings, mMassPropertiesOverride) +} + +void BodyCreationSettings::SaveBinaryState(StreamOut &inStream) const +{ + inStream.Write(mPosition); + inStream.Write(mRotation); + inStream.Write(mLinearVelocity); + inStream.Write(mAngularVelocity); + mCollisionGroup.SaveBinaryState(inStream); + inStream.Write(mObjectLayer); + inStream.Write(mMotionType); + inStream.Write(mAllowedDOFs); + inStream.Write(mAllowDynamicOrKinematic); + inStream.Write(mIsSensor); + inStream.Write(mCollideKinematicVsNonDynamic); + inStream.Write(mUseManifoldReduction); + inStream.Write(mApplyGyroscopicForce); + inStream.Write(mMotionQuality); + inStream.Write(mEnhancedInternalEdgeRemoval); + inStream.Write(mAllowSleeping); + inStream.Write(mFriction); + inStream.Write(mRestitution); + inStream.Write(mLinearDamping); + inStream.Write(mAngularDamping); + inStream.Write(mMaxLinearVelocity); + inStream.Write(mMaxAngularVelocity); + inStream.Write(mGravityFactor); + inStream.Write(mNumVelocityStepsOverride); + inStream.Write(mNumPositionStepsOverride); + inStream.Write(mOverrideMassProperties); + inStream.Write(mInertiaMultiplier); + mMassPropertiesOverride.SaveBinaryState(inStream); +} + +void BodyCreationSettings::RestoreBinaryState(StreamIn &inStream) +{ + inStream.Read(mPosition); + inStream.Read(mRotation); + inStream.Read(mLinearVelocity); + inStream.Read(mAngularVelocity); + mCollisionGroup.RestoreBinaryState(inStream); + inStream.Read(mObjectLayer); + inStream.Read(mMotionType); + inStream.Read(mAllowedDOFs); + inStream.Read(mAllowDynamicOrKinematic); + inStream.Read(mIsSensor); + inStream.Read(mCollideKinematicVsNonDynamic); + inStream.Read(mUseManifoldReduction); + inStream.Read(mApplyGyroscopicForce); + inStream.Read(mMotionQuality); + inStream.Read(mEnhancedInternalEdgeRemoval); + inStream.Read(mAllowSleeping); + inStream.Read(mFriction); + inStream.Read(mRestitution); + inStream.Read(mLinearDamping); + inStream.Read(mAngularDamping); + inStream.Read(mMaxLinearVelocity); + inStream.Read(mMaxAngularVelocity); + inStream.Read(mGravityFactor); + inStream.Read(mNumVelocityStepsOverride); + inStream.Read(mNumPositionStepsOverride); + inStream.Read(mOverrideMassProperties); + inStream.Read(mInertiaMultiplier); + mMassPropertiesOverride.RestoreBinaryState(inStream); +} + +Shape::ShapeResult BodyCreationSettings::ConvertShapeSettings() +{ + // If we already have a shape, return it + if (mShapePtr != nullptr) + { + mShape = nullptr; + + Shape::ShapeResult result; + result.Set(const_cast(mShapePtr.GetPtr())); + return result; + } + + // Check if we have shape settings + if (mShape == nullptr) + { + Shape::ShapeResult result; + result.SetError("No shape present!"); + return result; + } + + // Create the shape + Shape::ShapeResult result = mShape->Create(); + if (result.IsValid()) + mShapePtr = result.Get(); + mShape = nullptr; + return result; +} + +const Shape *BodyCreationSettings::GetShape() const +{ + // If we already have a shape, return it + if (mShapePtr != nullptr) + return mShapePtr; + + // Check if we have shape settings + if (mShape == nullptr) + return nullptr; + + // Create the shape + Shape::ShapeResult result = mShape->Create(); + if (result.IsValid()) + return result.Get(); + + Trace("Error: %s", result.GetError().c_str()); + JPH_ASSERT(false, "An error occurred during shape creation. Use ConvertShapeSettings() to convert the shape and get the error!"); + return nullptr; +} + +MassProperties BodyCreationSettings::GetMassProperties() const +{ + // Calculate mass properties + MassProperties mass_properties; + switch (mOverrideMassProperties) + { + case EOverrideMassProperties::CalculateMassAndInertia: + mass_properties = GetShape()->GetMassProperties(); + mass_properties.mInertia *= mInertiaMultiplier; + mass_properties.mInertia(3, 3) = 1.0f; + break; + case EOverrideMassProperties::CalculateInertia: + mass_properties = GetShape()->GetMassProperties(); + mass_properties.ScaleToMass(mMassPropertiesOverride.mMass); + mass_properties.mInertia *= mInertiaMultiplier; + mass_properties.mInertia(3, 3) = 1.0f; + break; + case EOverrideMassProperties::MassAndInertiaProvided: + mass_properties = mMassPropertiesOverride; + break; + } + return mass_properties; +} + +void BodyCreationSettings::SaveWithChildren(StreamOut &inStream, ShapeToIDMap *ioShapeMap, MaterialToIDMap *ioMaterialMap, GroupFilterToIDMap *ioGroupFilterMap) const +{ + // Save creation settings + SaveBinaryState(inStream); + + // Save shape + if (ioShapeMap != nullptr && ioMaterialMap != nullptr) + GetShape()->SaveWithChildren(inStream, *ioShapeMap, *ioMaterialMap); + else + inStream.Write(~uint32(0)); + + // Save group filter + StreamUtils::SaveObjectReference(inStream, mCollisionGroup.GetGroupFilter(), ioGroupFilterMap); +} + +BodyCreationSettings::BCSResult BodyCreationSettings::sRestoreWithChildren(StreamIn &inStream, IDToShapeMap &ioShapeMap, IDToMaterialMap &ioMaterialMap, IDToGroupFilterMap &ioGroupFilterMap) +{ + BCSResult result; + + // Read creation settings + BodyCreationSettings settings; + settings.RestoreBinaryState(inStream); + if (inStream.IsEOF() || inStream.IsFailed()) + { + result.SetError("Error reading body creation settings"); + return result; + } + + // Read shape + Shape::ShapeResult shape_result = Shape::sRestoreWithChildren(inStream, ioShapeMap, ioMaterialMap); + if (shape_result.HasError()) + { + result.SetError(shape_result.GetError()); + return result; + } + settings.SetShape(shape_result.Get()); + + // Read group filter + Result gfresult = StreamUtils::RestoreObjectReference(inStream, ioGroupFilterMap); + if (gfresult.HasError()) + { + result.SetError(gfresult.GetError()); + return result; + } + settings.mCollisionGroup.SetGroupFilter(gfresult.Get()); + + result.Set(settings); + return result; +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyCreationSettings.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyCreationSettings.h new file mode 100644 index 00000000000..a0395473fe8 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyCreationSettings.h @@ -0,0 +1,124 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +class StreamIn; +class StreamOut; + +/// Enum used in BodyCreationSettings to indicate how mass and inertia should be calculated +enum class EOverrideMassProperties : uint8 +{ + CalculateMassAndInertia, ///< Tells the system to calculate the mass and inertia based on density + CalculateInertia, ///< Tells the system to take the mass from mMassPropertiesOverride and to calculate the inertia based on density of the shapes and to scale it to the provided mass + MassAndInertiaProvided ///< Tells the system to take the mass and inertia from mMassPropertiesOverride +}; + +/// Settings for constructing a rigid body +class JPH_EXPORT BodyCreationSettings +{ +public: + JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, BodyCreationSettings) + + /// Constructor + BodyCreationSettings() = default; + BodyCreationSettings(const ShapeSettings *inShape, RVec3Arg inPosition, QuatArg inRotation, EMotionType inMotionType, ObjectLayer inObjectLayer) : mPosition(inPosition), mRotation(inRotation), mObjectLayer(inObjectLayer), mMotionType(inMotionType), mShape(inShape) { } + BodyCreationSettings(const Shape *inShape, RVec3Arg inPosition, QuatArg inRotation, EMotionType inMotionType, ObjectLayer inObjectLayer) : mPosition(inPosition), mRotation(inRotation), mObjectLayer(inObjectLayer), mMotionType(inMotionType), mShapePtr(inShape) { } + + /// Access to the shape settings object. This contains serializable (non-runtime optimized) information about the Shape. + const ShapeSettings * GetShapeSettings() const { return mShape; } + void SetShapeSettings(const ShapeSettings *inShape) { mShape = inShape; mShapePtr = nullptr; } + + /// Convert ShapeSettings object into a Shape object. This will free the ShapeSettings object and make the object ready for runtime. Serialization is no longer possible after this. + Shape::ShapeResult ConvertShapeSettings(); + + /// Access to the run-time shape object. Will convert from ShapeSettings object if needed. + const Shape * GetShape() const; + void SetShape(const Shape *inShape) { mShapePtr = inShape; mShape = nullptr; } + + /// Check if the mass properties of this body will be calculated (only relevant for kinematic or dynamic objects that need a MotionProperties object) + bool HasMassProperties() const { return mAllowDynamicOrKinematic || mMotionType != EMotionType::Static; } + + /// Calculate (or return when overridden) the mass and inertia for this body + MassProperties GetMassProperties() const; + + /// Saves the state of this object in binary form to inStream. Doesn't store the shape nor the group filter. + void SaveBinaryState(StreamOut &inStream) const; + + /// Restore the state of this object from inStream. Doesn't restore the shape nor the group filter. + void RestoreBinaryState(StreamIn &inStream); + + using GroupFilterToIDMap = StreamUtils::ObjectToIDMap; + using IDToGroupFilterMap = StreamUtils::IDToObjectMap; + using ShapeToIDMap = Shape::ShapeToIDMap; + using IDToShapeMap = Shape::IDToShapeMap; + using MaterialToIDMap = StreamUtils::ObjectToIDMap; + using IDToMaterialMap = StreamUtils::IDToObjectMap; + + /// Save body creation settings, its shape, materials and group filter. Pass in an empty map in ioShapeMap / ioMaterialMap / ioGroupFilterMap or reuse the same map while saving multiple shapes to the same stream in order to avoid writing duplicates. + /// Pass nullptr to ioShapeMap and ioMaterial map to skip saving shapes + /// Pass nullptr to ioGroupFilterMap to skip saving group filters + void SaveWithChildren(StreamOut &inStream, ShapeToIDMap *ioShapeMap, MaterialToIDMap *ioMaterialMap, GroupFilterToIDMap *ioGroupFilterMap) const; + + using BCSResult = Result; + + /// Restore body creation settings, its shape, materials and group filter. Pass in an empty map in ioShapeMap / ioMaterialMap / ioGroupFilterMap or reuse the same map while reading multiple shapes from the same stream in order to restore duplicates. + static BCSResult sRestoreWithChildren(StreamIn &inStream, IDToShapeMap &ioShapeMap, IDToMaterialMap &ioMaterialMap, IDToGroupFilterMap &ioGroupFilterMap); + + RVec3 mPosition = RVec3::sZero(); ///< Position of the body (not of the center of mass) + Quat mRotation = Quat::sIdentity(); ///< Rotation of the body + Vec3 mLinearVelocity = Vec3::sZero(); ///< World space linear velocity of the center of mass (m/s) + Vec3 mAngularVelocity = Vec3::sZero(); ///< World space angular velocity (rad/s) + + /// User data value (can be used by application) + uint64 mUserData = 0; + + ///@name Collision settings + ObjectLayer mObjectLayer = 0; ///< The collision layer this body belongs to (determines if two objects can collide) + CollisionGroup mCollisionGroup; ///< The collision group this body belongs to (determines if two objects can collide) + + ///@name Simulation properties + EMotionType mMotionType = EMotionType::Dynamic; ///< Motion type, determines if the object is static, dynamic or kinematic + EAllowedDOFs mAllowedDOFs = EAllowedDOFs::All; ///< Which degrees of freedom this body has (can be used to limit simulation to 2D) + bool mAllowDynamicOrKinematic = false; ///< When this body is created as static, this setting tells the system to create a MotionProperties object so that the object can be switched to kinematic or dynamic + bool mIsSensor = false; ///< If this body is a sensor. A sensor will receive collision callbacks, but will not cause any collision responses and can be used as a trigger volume. See description at Body::SetIsSensor. + bool mCollideKinematicVsNonDynamic = false; ///< If kinematic objects can generate contact points against other kinematic or static objects. See description at Body::SetCollideKinematicVsNonDynamic. + bool mUseManifoldReduction = true; ///< If this body should use manifold reduction (see description at Body::SetUseManifoldReduction) + bool mApplyGyroscopicForce = false; ///< Set to indicate that the gyroscopic force should be applied to this body (aka Dzhanibekov effect, see https://en.wikipedia.org/wiki/Tennis_racket_theorem) + EMotionQuality mMotionQuality = EMotionQuality::Discrete; ///< Motion quality, or how well it detects collisions when it has a high velocity + bool mEnhancedInternalEdgeRemoval = false; ///< Set to indicate that extra effort should be made to try to remove ghost contacts (collisions with internal edges of a mesh). This is more expensive but makes bodies move smoother over a mesh with convex edges. + bool mAllowSleeping = true; ///< If this body can go to sleep or not + float mFriction = 0.2f; ///< Friction of the body (dimensionless number, usually between 0 and 1, 0 = no friction, 1 = friction force equals force that presses the two bodies together). Note that bodies can have negative friction but the combined friction (see PhysicsSystem::SetCombineFriction) should never go below zero. + float mRestitution = 0.0f; ///< Restitution of body (dimensionless number, usually between 0 and 1, 0 = completely inelastic collision response, 1 = completely elastic collision response). Note that bodies can have negative restitution but the combined restitution (see PhysicsSystem::SetCombineRestitution) should never go below zero. + float mLinearDamping = 0.05f; ///< Linear damping: dv/dt = -c * v. c must be between 0 and 1 but is usually close to 0. + float mAngularDamping = 0.05f; ///< Angular damping: dw/dt = -c * w. c must be between 0 and 1 but is usually close to 0. + float mMaxLinearVelocity = 500.0f; ///< Maximum linear velocity that this body can reach (m/s) + float mMaxAngularVelocity = 0.25f * JPH_PI * 60.0f; ///< Maximum angular velocity that this body can reach (rad/s) + float mGravityFactor = 1.0f; ///< Value to multiply gravity with for this body + uint mNumVelocityStepsOverride = 0; ///< Used only when this body is dynamic and colliding. Override for the number of solver velocity iterations to run, 0 means use the default in PhysicsSettings::mNumVelocitySteps. The number of iterations to use is the max of all contacts and constraints in the island. + uint mNumPositionStepsOverride = 0; ///< Used only when this body is dynamic and colliding. Override for the number of solver position iterations to run, 0 means use the default in PhysicsSettings::mNumPositionSteps. The number of iterations to use is the max of all contacts and constraints in the island. + + ///@name Mass properties of the body (by default calculated by the shape) + EOverrideMassProperties mOverrideMassProperties = EOverrideMassProperties::CalculateMassAndInertia; ///< Determines how mMassPropertiesOverride will be used + float mInertiaMultiplier = 1.0f; ///< When calculating the inertia (not when it is provided) the calculated inertia will be multiplied by this value + MassProperties mMassPropertiesOverride; ///< Contains replacement mass settings which override the automatically calculated values + +private: + /// Collision volume for the body + RefConst mShape; ///< Shape settings, can be serialized. Mutually exclusive with mShapePtr + RefConst mShapePtr; ///< Actual shape, cannot be serialized. Mutually exclusive with mShape +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyFilter.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyFilter.h new file mode 100644 index 00000000000..6f95e031843 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyFilter.h @@ -0,0 +1,102 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +JPH_NAMESPACE_BEGIN + +class Body; + +/// Class function to filter out bodies, returns true if test should collide with body +class BodyFilter : public NonCopyable +{ +public: + /// Destructor + virtual ~BodyFilter() = default; + + /// Filter function. Returns true if we should collide with inBodyID + virtual bool ShouldCollide([[maybe_unused]] const BodyID &inBodyID) const + { + return true; + } + + /// Filter function. Returns true if we should collide with inBody (this is called after the body is locked and makes it possible to filter based on body members) + virtual bool ShouldCollideLocked([[maybe_unused]] const Body &inBody) const + { + return true; + } +}; + +/// A simple body filter implementation that ignores a single, specified body +class IgnoreSingleBodyFilter : public BodyFilter +{ +public: + /// Constructor, pass the body you want to ignore + explicit IgnoreSingleBodyFilter(const BodyID &inBodyID) : + mBodyID(inBodyID) + { + } + + /// Filter function. Returns true if we should collide with inBodyID + virtual bool ShouldCollide(const BodyID &inBodyID) const override + { + return mBodyID != inBodyID; + } + +private: + BodyID mBodyID; +}; + +/// A simple body filter implementation that ignores multiple, specified bodies +class IgnoreMultipleBodiesFilter : public BodyFilter +{ +public: + /// Remove all bodies from the filter + void Clear() + { + mBodyIDs.clear(); + } + + /// Reserve space for inSize body ID's + void Reserve(uint inSize) + { + mBodyIDs.reserve(inSize); + } + + /// Add a body to be ignored + void IgnoreBody(const BodyID &inBodyID) + { + mBodyIDs.push_back(inBodyID); + } + + /// Filter function. Returns true if we should collide with inBodyID + virtual bool ShouldCollide(const BodyID &inBodyID) const override + { + return find(mBodyIDs.begin(), mBodyIDs.end(), inBodyID) == mBodyIDs.end(); + } + +private: + Array mBodyIDs; +}; + +#ifdef JPH_DEBUG_RENDERER +/// Class function to filter out bodies for debug rendering, returns true if body should be rendered +class BodyDrawFilter : public NonCopyable +{ +public: + /// Destructor + virtual ~BodyDrawFilter() = default; + + /// Filter function. Returns true if inBody should be rendered + virtual bool ShouldDraw([[maybe_unused]] const Body& inBody) const + { + return true; + } +}; +#endif // JPH_DEBUG_RENDERER + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyID.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyID.h new file mode 100644 index 00000000000..f56d96b7b99 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyID.h @@ -0,0 +1,100 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +/// ID of a body. This is a way of reasoning about bodies in a multithreaded simulation while avoiding race conditions. +class BodyID +{ +public: + JPH_OVERRIDE_NEW_DELETE + + static constexpr uint32 cInvalidBodyID = 0xffffffff; ///< The value for an invalid body ID + static constexpr uint32 cBroadPhaseBit = 0x00800000; ///< This bit is used by the broadphase + static constexpr uint32 cMaxBodyIndex = 0x7fffff; ///< Maximum value for body index (also the maximum amount of bodies supported - 1) + static constexpr uint8 cMaxSequenceNumber = 0xff; ///< Maximum value for the sequence number + + /// Construct invalid body ID + BodyID() : + mID(cInvalidBodyID) + { + } + + /// Construct from index and sequence number combined in a single uint32 (use with care!) + explicit BodyID(uint32 inID) : + mID(inID) + { + JPH_ASSERT((inID & cBroadPhaseBit) == 0 || inID == cInvalidBodyID); // Check bit used by broadphase + } + + /// Construct from index and sequence number + explicit BodyID(uint32 inID, uint8 inSequenceNumber) : + mID((uint32(inSequenceNumber) << 24) | inID) + { + JPH_ASSERT(inID < cMaxBodyIndex); // Should not use bit pattern for invalid ID and should not use the broadphase bit + } + + /// Get index in body array + inline uint32 GetIndex() const + { + return mID & cMaxBodyIndex; + } + + /// Get sequence number of body. + /// The sequence number can be used to check if a body ID with the same body index has been reused by another body. + /// It is mainly used in multi threaded situations where a body is removed and its body index is immediately reused by a body created from another thread. + /// Functions querying the broadphase can (after acquiring a body lock) detect that the body has been removed (we assume that this won't happen more than 128 times in a row). + inline uint8 GetSequenceNumber() const + { + return uint8(mID >> 24); + } + + /// Returns the index and sequence number combined in an uint32 + inline uint32 GetIndexAndSequenceNumber() const + { + return mID; + } + + /// Check if the ID is valid + inline bool IsInvalid() const + { + return mID == cInvalidBodyID; + } + + /// Equals check + inline bool operator == (const BodyID &inRHS) const + { + return mID == inRHS.mID; + } + + /// Not equals check + inline bool operator != (const BodyID &inRHS) const + { + return mID != inRHS.mID; + } + + /// Smaller than operator, can be used for sorting bodies + inline bool operator < (const BodyID &inRHS) const + { + return mID < inRHS.mID; + } + + /// Greater than operator, can be used for sorting bodies + inline bool operator > (const BodyID &inRHS) const + { + return mID > inRHS.mID; + } + +private: + uint32 mID; +}; + +JPH_NAMESPACE_END + +// Create a std::hash for BodyID +JPH_MAKE_HASHABLE(JPH::BodyID, t.GetIndexAndSequenceNumber()) diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyInterface.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyInterface.cpp new file mode 100644 index 00000000000..3c98e1fd312 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyInterface.cpp @@ -0,0 +1,1002 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +Body *BodyInterface::CreateBody(const BodyCreationSettings &inSettings) +{ + Body *body = mBodyManager->AllocateBody(inSettings); + if (!mBodyManager->AddBody(body)) + { + mBodyManager->FreeBody(body); + return nullptr; + } + return body; +} + +Body *BodyInterface::CreateSoftBody(const SoftBodyCreationSettings &inSettings) +{ + Body *body = mBodyManager->AllocateSoftBody(inSettings); + if (!mBodyManager->AddBody(body)) + { + mBodyManager->FreeBody(body); + return nullptr; + } + return body; +} + +Body *BodyInterface::CreateBodyWithID(const BodyID &inBodyID, const BodyCreationSettings &inSettings) +{ + Body *body = mBodyManager->AllocateBody(inSettings); + if (!mBodyManager->AddBodyWithCustomID(body, inBodyID)) + { + mBodyManager->FreeBody(body); + return nullptr; + } + return body; +} + +Body *BodyInterface::CreateSoftBodyWithID(const BodyID &inBodyID, const SoftBodyCreationSettings &inSettings) +{ + Body *body = mBodyManager->AllocateSoftBody(inSettings); + if (!mBodyManager->AddBodyWithCustomID(body, inBodyID)) + { + mBodyManager->FreeBody(body); + return nullptr; + } + return body; +} + +Body *BodyInterface::CreateBodyWithoutID(const BodyCreationSettings &inSettings) const +{ + return mBodyManager->AllocateBody(inSettings); +} + +Body *BodyInterface::CreateSoftBodyWithoutID(const SoftBodyCreationSettings &inSettings) const +{ + return mBodyManager->AllocateSoftBody(inSettings); +} + +void BodyInterface::DestroyBodyWithoutID(Body *inBody) const +{ + mBodyManager->FreeBody(inBody); +} + +bool BodyInterface::AssignBodyID(Body *ioBody) +{ + return mBodyManager->AddBody(ioBody); +} + +bool BodyInterface::AssignBodyID(Body *ioBody, const BodyID &inBodyID) +{ + return mBodyManager->AddBodyWithCustomID(ioBody, inBodyID); +} + +Body *BodyInterface::UnassignBodyID(const BodyID &inBodyID) +{ + Body *body = nullptr; + mBodyManager->RemoveBodies(&inBodyID, 1, &body); + return body; +} + +void BodyInterface::UnassignBodyIDs(const BodyID *inBodyIDs, int inNumber, Body **outBodies) +{ + mBodyManager->RemoveBodies(inBodyIDs, inNumber, outBodies); +} + +void BodyInterface::DestroyBody(const BodyID &inBodyID) +{ + mBodyManager->DestroyBodies(&inBodyID, 1); +} + +void BodyInterface::DestroyBodies(const BodyID *inBodyIDs, int inNumber) +{ + mBodyManager->DestroyBodies(inBodyIDs, inNumber); +} + +void BodyInterface::AddBody(const BodyID &inBodyID, EActivation inActivationMode) +{ + BodyLockWrite lock(*mBodyLockInterface, inBodyID); + if (lock.Succeeded()) + { + const Body &body = lock.GetBody(); + + // Add to broadphase + BodyID id = inBodyID; + BroadPhase::AddState add_state = mBroadPhase->AddBodiesPrepare(&id, 1); + mBroadPhase->AddBodiesFinalize(&id, 1, add_state); + + // Optionally activate body + if (inActivationMode == EActivation::Activate && !body.IsStatic()) + mBodyManager->ActivateBodies(&inBodyID, 1); + } +} + +void BodyInterface::RemoveBody(const BodyID &inBodyID) +{ + BodyLockWrite lock(*mBodyLockInterface, inBodyID); + if (lock.Succeeded()) + { + const Body &body = lock.GetBody(); + + // Deactivate body + if (body.IsActive()) + mBodyManager->DeactivateBodies(&inBodyID, 1); + + // Remove from broadphase + BodyID id = inBodyID; + mBroadPhase->RemoveBodies(&id, 1); + } +} + +bool BodyInterface::IsAdded(const BodyID &inBodyID) const +{ + BodyLockRead lock(*mBodyLockInterface, inBodyID); + return lock.SucceededAndIsInBroadPhase(); +} + +BodyID BodyInterface::CreateAndAddBody(const BodyCreationSettings &inSettings, EActivation inActivationMode) +{ + const Body *b = CreateBody(inSettings); + if (b == nullptr) + return BodyID(); // Out of bodies + AddBody(b->GetID(), inActivationMode); + return b->GetID(); +} + +BodyID BodyInterface::CreateAndAddSoftBody(const SoftBodyCreationSettings &inSettings, EActivation inActivationMode) +{ + const Body *b = CreateSoftBody(inSettings); + if (b == nullptr) + return BodyID(); // Out of bodies + AddBody(b->GetID(), inActivationMode); + return b->GetID(); +} + +BodyInterface::AddState BodyInterface::AddBodiesPrepare(BodyID *ioBodies, int inNumber) +{ + return mBroadPhase->AddBodiesPrepare(ioBodies, inNumber); +} + +void BodyInterface::AddBodiesFinalize(BodyID *ioBodies, int inNumber, AddState inAddState, EActivation inActivationMode) +{ + BodyLockMultiWrite lock(*mBodyLockInterface, ioBodies, inNumber); + + // Add to broadphase + mBroadPhase->AddBodiesFinalize(ioBodies, inNumber, inAddState); + + // Optionally activate bodies + if (inActivationMode == EActivation::Activate) + mBodyManager->ActivateBodies(ioBodies, inNumber); +} + +void BodyInterface::AddBodiesAbort(BodyID *ioBodies, int inNumber, AddState inAddState) +{ + mBroadPhase->AddBodiesAbort(ioBodies, inNumber, inAddState); +} + +void BodyInterface::RemoveBodies(BodyID *ioBodies, int inNumber) +{ + BodyLockMultiWrite lock(*mBodyLockInterface, ioBodies, inNumber); + + // Deactivate bodies + mBodyManager->DeactivateBodies(ioBodies, inNumber); + + // Remove from broadphase + mBroadPhase->RemoveBodies(ioBodies, inNumber); +} + +void BodyInterface::ActivateBody(const BodyID &inBodyID) +{ + BodyLockWrite lock(*mBodyLockInterface, inBodyID); + if (lock.Succeeded()) + { + const Body &body = lock.GetBody(); + + if (!body.IsActive()) + mBodyManager->ActivateBodies(&inBodyID, 1); + } +} + +void BodyInterface::ActivateBodies(const BodyID *inBodyIDs, int inNumber) +{ + BodyLockMultiWrite lock(*mBodyLockInterface, inBodyIDs, inNumber); + + mBodyManager->ActivateBodies(inBodyIDs, inNumber); +} + +void BodyInterface::ActivateBodiesInAABox(const AABox &inBox, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) +{ + AllHitCollisionCollector collector; + mBroadPhase->CollideAABox(inBox, collector, inBroadPhaseLayerFilter, inObjectLayerFilter); + ActivateBodies(collector.mHits.data(), (int)collector.mHits.size()); +} + +void BodyInterface::DeactivateBody(const BodyID &inBodyID) +{ + BodyLockWrite lock(*mBodyLockInterface, inBodyID); + if (lock.Succeeded()) + { + const Body &body = lock.GetBody(); + + if (body.IsActive()) + mBodyManager->DeactivateBodies(&inBodyID, 1); + } +} + +void BodyInterface::DeactivateBodies(const BodyID *inBodyIDs, int inNumber) +{ + BodyLockMultiWrite lock(*mBodyLockInterface, inBodyIDs, inNumber); + + mBodyManager->DeactivateBodies(inBodyIDs, inNumber); +} + +bool BodyInterface::IsActive(const BodyID &inBodyID) const +{ + BodyLockRead lock(*mBodyLockInterface, inBodyID); + return lock.Succeeded() && lock.GetBody().IsActive(); +} + +TwoBodyConstraint *BodyInterface::CreateConstraint(const TwoBodyConstraintSettings *inSettings, const BodyID &inBodyID1, const BodyID &inBodyID2) +{ + BodyID constraint_bodies[] = { inBodyID1, inBodyID2 }; + BodyLockMultiWrite lock(*mBodyLockInterface, constraint_bodies, 2); + + Body *body1 = lock.GetBody(0); + Body *body2 = lock.GetBody(1); + + JPH_ASSERT(body1 != body2); + JPH_ASSERT(body1 != nullptr || body2 != nullptr); + + return inSettings->Create(body1 != nullptr? *body1 : Body::sFixedToWorld, body2 != nullptr? *body2 : Body::sFixedToWorld); +} + +void BodyInterface::ActivateConstraint(const TwoBodyConstraint *inConstraint) +{ + BodyID bodies[] = { inConstraint->GetBody1()->GetID(), inConstraint->GetBody2()->GetID() }; + ActivateBodies(bodies, 2); +} + +RefConst BodyInterface::GetShape(const BodyID &inBodyID) const +{ + RefConst shape; + BodyLockRead lock(*mBodyLockInterface, inBodyID); + if (lock.Succeeded()) + shape = lock.GetBody().GetShape(); + return shape; +} + +void BodyInterface::SetShape(const BodyID &inBodyID, const Shape *inShape, bool inUpdateMassProperties, EActivation inActivationMode) const +{ + BodyLockWrite lock(*mBodyLockInterface, inBodyID); + if (lock.Succeeded()) + { + Body &body = lock.GetBody(); + + // Check if shape actually changed + if (body.GetShape() != inShape) + { + // Update the shape + body.SetShapeInternal(inShape, inUpdateMassProperties); + + // Flag collision cache invalid for this body + mBodyManager->InvalidateContactCacheForBody(body); + + // Notify broadphase of change + if (body.IsInBroadPhase()) + { + BodyID id = body.GetID(); + mBroadPhase->NotifyBodiesAABBChanged(&id, 1); + } + + // Optionally activate body + if (inActivationMode == EActivation::Activate && !body.IsStatic()) + mBodyManager->ActivateBodies(&inBodyID, 1); + } + } +} + +void BodyInterface::NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inPreviousCenterOfMass, bool inUpdateMassProperties, EActivation inActivationMode) const +{ + BodyLockWrite lock(*mBodyLockInterface, inBodyID); + if (lock.Succeeded()) + { + Body &body = lock.GetBody(); + + // Update center of mass, mass and inertia + body.UpdateCenterOfMassInternal(inPreviousCenterOfMass, inUpdateMassProperties); + + // Recalculate bounding box + body.CalculateWorldSpaceBoundsInternal(); + + // Flag collision cache invalid for this body + mBodyManager->InvalidateContactCacheForBody(body); + + // Notify broadphase of change + if (body.IsInBroadPhase()) + { + BodyID id = body.GetID(); + mBroadPhase->NotifyBodiesAABBChanged(&id, 1); + } + + // Optionally activate body + if (inActivationMode == EActivation::Activate && !body.IsStatic()) + mBodyManager->ActivateBodies(&inBodyID, 1); + } +} + +void BodyInterface::SetObjectLayer(const BodyID &inBodyID, ObjectLayer inLayer) +{ + BodyLockWrite lock(*mBodyLockInterface, inBodyID); + if (lock.Succeeded()) + { + Body &body = lock.GetBody(); + + // Check if layer actually changed, updating the broadphase is rather expensive + if (body.GetObjectLayer() != inLayer) + { + // Update the layer on the body + mBodyManager->SetBodyObjectLayerInternal(body, inLayer); + + // Notify broadphase of change + if (body.IsInBroadPhase()) + { + BodyID id = body.GetID(); + mBroadPhase->NotifyBodiesLayerChanged(&id, 1); + } + } + } +} + +ObjectLayer BodyInterface::GetObjectLayer(const BodyID &inBodyID) const +{ + BodyLockRead lock(*mBodyLockInterface, inBodyID); + if (lock.Succeeded()) + return lock.GetBody().GetObjectLayer(); + else + return cObjectLayerInvalid; +} + +void BodyInterface::SetPositionAndRotation(const BodyID &inBodyID, RVec3Arg inPosition, QuatArg inRotation, EActivation inActivationMode) +{ + BodyLockWrite lock(*mBodyLockInterface, inBodyID); + if (lock.Succeeded()) + { + Body &body = lock.GetBody(); + + // Update the position + body.SetPositionAndRotationInternal(inPosition, inRotation); + + // Notify broadphase of change + if (body.IsInBroadPhase()) + { + BodyID id = body.GetID(); + mBroadPhase->NotifyBodiesAABBChanged(&id, 1); + } + + // Optionally activate body + if (inActivationMode == EActivation::Activate && !body.IsStatic()) + mBodyManager->ActivateBodies(&inBodyID, 1); + } +} + +void BodyInterface::SetPositionAndRotationWhenChanged(const BodyID &inBodyID, RVec3Arg inPosition, QuatArg inRotation, EActivation inActivationMode) +{ + BodyLockWrite lock(*mBodyLockInterface, inBodyID); + if (lock.Succeeded()) + { + Body &body = lock.GetBody(); + + // Check if there is enough change + if (!body.GetPosition().IsClose(inPosition) + || !body.GetRotation().IsClose(inRotation)) + { + // Update the position + body.SetPositionAndRotationInternal(inPosition, inRotation); + + // Notify broadphase of change + if (body.IsInBroadPhase()) + { + BodyID id = body.GetID(); + mBroadPhase->NotifyBodiesAABBChanged(&id, 1); + } + + // Optionally activate body + if (inActivationMode == EActivation::Activate && !body.IsStatic()) + mBodyManager->ActivateBodies(&inBodyID, 1); + } + } +} + +void BodyInterface::GetPositionAndRotation(const BodyID &inBodyID, RVec3 &outPosition, Quat &outRotation) const +{ + BodyLockRead lock(*mBodyLockInterface, inBodyID); + if (lock.Succeeded()) + { + const Body &body = lock.GetBody(); + outPosition = body.GetPosition(); + outRotation = body.GetRotation(); + } + else + { + outPosition = RVec3::sZero(); + outRotation = Quat::sIdentity(); + } +} + +void BodyInterface::SetPosition(const BodyID &inBodyID, RVec3Arg inPosition, EActivation inActivationMode) +{ + BodyLockWrite lock(*mBodyLockInterface, inBodyID); + if (lock.Succeeded()) + { + Body &body = lock.GetBody(); + + // Update the position + body.SetPositionAndRotationInternal(inPosition, body.GetRotation()); + + // Notify broadphase of change + if (body.IsInBroadPhase()) + { + BodyID id = body.GetID(); + mBroadPhase->NotifyBodiesAABBChanged(&id, 1); + } + + // Optionally activate body + if (inActivationMode == EActivation::Activate && !body.IsStatic()) + mBodyManager->ActivateBodies(&inBodyID, 1); + } +} + +RVec3 BodyInterface::GetPosition(const BodyID &inBodyID) const +{ + BodyLockRead lock(*mBodyLockInterface, inBodyID); + if (lock.Succeeded()) + return lock.GetBody().GetPosition(); + else + return RVec3::sZero(); +} + +RVec3 BodyInterface::GetCenterOfMassPosition(const BodyID &inBodyID) const +{ + BodyLockRead lock(*mBodyLockInterface, inBodyID); + if (lock.Succeeded()) + return lock.GetBody().GetCenterOfMassPosition(); + else + return RVec3::sZero(); +} + +void BodyInterface::SetRotation(const BodyID &inBodyID, QuatArg inRotation, EActivation inActivationMode) +{ + BodyLockWrite lock(*mBodyLockInterface, inBodyID); + if (lock.Succeeded()) + { + Body &body = lock.GetBody(); + + // Update the position + body.SetPositionAndRotationInternal(body.GetPosition(), inRotation); + + // Notify broadphase of change + if (body.IsInBroadPhase()) + { + BodyID id = body.GetID(); + mBroadPhase->NotifyBodiesAABBChanged(&id, 1); + } + + // Optionally activate body + if (inActivationMode == EActivation::Activate && !body.IsStatic()) + mBodyManager->ActivateBodies(&inBodyID, 1); + } +} + +Quat BodyInterface::GetRotation(const BodyID &inBodyID) const +{ + BodyLockRead lock(*mBodyLockInterface, inBodyID); + if (lock.Succeeded()) + return lock.GetBody().GetRotation(); + else + return Quat::sIdentity(); +} + +RMat44 BodyInterface::GetWorldTransform(const BodyID &inBodyID) const +{ + BodyLockRead lock(*mBodyLockInterface, inBodyID); + if (lock.Succeeded()) + return lock.GetBody().GetWorldTransform(); + else + return RMat44::sIdentity(); +} + +RMat44 BodyInterface::GetCenterOfMassTransform(const BodyID &inBodyID) const +{ + BodyLockRead lock(*mBodyLockInterface, inBodyID); + if (lock.Succeeded()) + return lock.GetBody().GetCenterOfMassTransform(); + else + return RMat44::sIdentity(); +} + +void BodyInterface::MoveKinematic(const BodyID &inBodyID, RVec3Arg inTargetPosition, QuatArg inTargetRotation, float inDeltaTime) +{ + BodyLockWrite lock(*mBodyLockInterface, inBodyID); + if (lock.Succeeded()) + { + Body &body = lock.GetBody(); + + body.MoveKinematic(inTargetPosition, inTargetRotation, inDeltaTime); + + if (!body.IsActive() && (!body.GetLinearVelocity().IsNearZero() || !body.GetAngularVelocity().IsNearZero())) + mBodyManager->ActivateBodies(&inBodyID, 1); + } +} + +void BodyInterface::SetLinearAndAngularVelocity(const BodyID &inBodyID, Vec3Arg inLinearVelocity, Vec3Arg inAngularVelocity) +{ + BodyLockWrite lock(*mBodyLockInterface, inBodyID); + if (lock.Succeeded()) + { + Body &body = lock.GetBody(); + if (!body.IsStatic()) + { + body.SetLinearVelocityClamped(inLinearVelocity); + body.SetAngularVelocityClamped(inAngularVelocity); + + if (!body.IsActive() && (!inLinearVelocity.IsNearZero() || !inAngularVelocity.IsNearZero())) + mBodyManager->ActivateBodies(&inBodyID, 1); + } + } +} + +void BodyInterface::GetLinearAndAngularVelocity(const BodyID &inBodyID, Vec3 &outLinearVelocity, Vec3 &outAngularVelocity) const +{ + BodyLockRead lock(*mBodyLockInterface, inBodyID); + if (lock.Succeeded()) + { + const Body &body = lock.GetBody(); + if (!body.IsStatic()) + { + outLinearVelocity = body.GetLinearVelocity(); + outAngularVelocity = body.GetAngularVelocity(); + return; + } + } + + outLinearVelocity = outAngularVelocity = Vec3::sZero(); +} + +void BodyInterface::SetLinearVelocity(const BodyID &inBodyID, Vec3Arg inLinearVelocity) +{ + BodyLockWrite lock(*mBodyLockInterface, inBodyID); + if (lock.Succeeded()) + { + Body &body = lock.GetBody(); + if (!body.IsStatic()) + { + body.SetLinearVelocityClamped(inLinearVelocity); + + if (!body.IsActive() && !inLinearVelocity.IsNearZero()) + mBodyManager->ActivateBodies(&inBodyID, 1); + } + } +} + +Vec3 BodyInterface::GetLinearVelocity(const BodyID &inBodyID) const +{ + BodyLockRead lock(*mBodyLockInterface, inBodyID); + if (lock.Succeeded()) + { + const Body &body = lock.GetBody(); + if (!body.IsStatic()) + return body.GetLinearVelocity(); + } + + return Vec3::sZero(); +} + +void BodyInterface::AddLinearVelocity(const BodyID &inBodyID, Vec3Arg inLinearVelocity) +{ + BodyLockWrite lock(*mBodyLockInterface, inBodyID); + if (lock.Succeeded()) + { + Body &body = lock.GetBody(); + if (!body.IsStatic()) + { + body.SetLinearVelocityClamped(body.GetLinearVelocity() + inLinearVelocity); + + if (!body.IsActive() && !body.GetLinearVelocity().IsNearZero()) + mBodyManager->ActivateBodies(&inBodyID, 1); + } + } +} + +void BodyInterface::AddLinearAndAngularVelocity(const BodyID &inBodyID, Vec3Arg inLinearVelocity, Vec3Arg inAngularVelocity) +{ + BodyLockWrite lock(*mBodyLockInterface, inBodyID); + if (lock.Succeeded()) + { + Body &body = lock.GetBody(); + if (!body.IsStatic()) + { + body.SetLinearVelocityClamped(body.GetLinearVelocity() + inLinearVelocity); + body.SetAngularVelocityClamped(body.GetAngularVelocity() + inAngularVelocity); + + if (!body.IsActive() && (!body.GetLinearVelocity().IsNearZero() || !body.GetAngularVelocity().IsNearZero())) + mBodyManager->ActivateBodies(&inBodyID, 1); + } + } +} + +void BodyInterface::SetAngularVelocity(const BodyID &inBodyID, Vec3Arg inAngularVelocity) +{ + BodyLockWrite lock(*mBodyLockInterface, inBodyID); + if (lock.Succeeded()) + { + Body &body = lock.GetBody(); + if (!body.IsStatic()) + { + body.SetAngularVelocityClamped(inAngularVelocity); + + if (!body.IsActive() && !inAngularVelocity.IsNearZero()) + mBodyManager->ActivateBodies(&inBodyID, 1); + } + } +} + +Vec3 BodyInterface::GetAngularVelocity(const BodyID &inBodyID) const +{ + BodyLockRead lock(*mBodyLockInterface, inBodyID); + if (lock.Succeeded()) + { + const Body &body = lock.GetBody(); + if (!body.IsStatic()) + return body.GetAngularVelocity(); + } + + return Vec3::sZero(); +} + +Vec3 BodyInterface::GetPointVelocity(const BodyID &inBodyID, RVec3Arg inPoint) const +{ + BodyLockRead lock(*mBodyLockInterface, inBodyID); + if (lock.Succeeded()) + { + const Body &body = lock.GetBody(); + if (!body.IsStatic()) + return body.GetPointVelocity(inPoint); + } + + return Vec3::sZero(); +} + +void BodyInterface::AddForce(const BodyID &inBodyID, Vec3Arg inForce) +{ + BodyLockWrite lock(*mBodyLockInterface, inBodyID); + if (lock.Succeeded()) + { + Body &body = lock.GetBody(); + if (body.IsDynamic()) + { + body.AddForce(inForce); + + if (!body.IsActive()) + mBodyManager->ActivateBodies(&inBodyID, 1); + } + } +} + +void BodyInterface::AddForce(const BodyID &inBodyID, Vec3Arg inForce, RVec3Arg inPoint) +{ + BodyLockWrite lock(*mBodyLockInterface, inBodyID); + if (lock.Succeeded()) + { + Body &body = lock.GetBody(); + if (body.IsDynamic()) + { + body.AddForce(inForce, inPoint); + + if (!body.IsActive()) + mBodyManager->ActivateBodies(&inBodyID, 1); + } + } +} + +void BodyInterface::AddTorque(const BodyID &inBodyID, Vec3Arg inTorque) +{ + BodyLockWrite lock(*mBodyLockInterface, inBodyID); + if (lock.Succeeded()) + { + Body &body = lock.GetBody(); + if (body.IsDynamic()) + { + body.AddTorque(inTorque); + + if (!body.IsActive()) + mBodyManager->ActivateBodies(&inBodyID, 1); + } + } +} + +void BodyInterface::AddForceAndTorque(const BodyID &inBodyID, Vec3Arg inForce, Vec3Arg inTorque) +{ + BodyLockWrite lock(*mBodyLockInterface, inBodyID); + if (lock.Succeeded()) + { + Body &body = lock.GetBody(); + if (body.IsDynamic()) + { + body.AddForce(inForce); + body.AddTorque(inTorque); + + if (!body.IsActive()) + mBodyManager->ActivateBodies(&inBodyID, 1); + } + } +} + +void BodyInterface::AddImpulse(const BodyID &inBodyID, Vec3Arg inImpulse) +{ + BodyLockWrite lock(*mBodyLockInterface, inBodyID); + if (lock.Succeeded()) + { + Body &body = lock.GetBody(); + if (body.IsDynamic()) + { + body.AddImpulse(inImpulse); + + if (!body.IsActive()) + mBodyManager->ActivateBodies(&inBodyID, 1); + } + } +} + +void BodyInterface::AddImpulse(const BodyID &inBodyID, Vec3Arg inImpulse, RVec3Arg inPoint) +{ + BodyLockWrite lock(*mBodyLockInterface, inBodyID); + if (lock.Succeeded()) + { + Body &body = lock.GetBody(); + if (body.IsDynamic()) + { + body.AddImpulse(inImpulse, inPoint); + + if (!body.IsActive()) + mBodyManager->ActivateBodies(&inBodyID, 1); + } + } +} + +void BodyInterface::AddAngularImpulse(const BodyID &inBodyID, Vec3Arg inAngularImpulse) +{ + BodyLockWrite lock(*mBodyLockInterface, inBodyID); + if (lock.Succeeded()) + { + Body &body = lock.GetBody(); + if (body.IsDynamic()) + { + body.AddAngularImpulse(inAngularImpulse); + + if (!body.IsActive()) + mBodyManager->ActivateBodies(&inBodyID, 1); + } + } +} + +void BodyInterface::SetPositionRotationAndVelocity(const BodyID &inBodyID, RVec3Arg inPosition, QuatArg inRotation, Vec3Arg inLinearVelocity, Vec3Arg inAngularVelocity) +{ + BodyLockWrite lock(*mBodyLockInterface, inBodyID); + if (lock.Succeeded()) + { + Body &body = lock.GetBody(); + + // Update the position + body.SetPositionAndRotationInternal(inPosition, inRotation); + + // Notify broadphase of change + if (body.IsInBroadPhase()) + { + BodyID id = body.GetID(); + mBroadPhase->NotifyBodiesAABBChanged(&id, 1); + } + + if (!body.IsStatic()) + { + body.SetLinearVelocityClamped(inLinearVelocity); + body.SetAngularVelocityClamped(inAngularVelocity); + + // Optionally activate body + if (!body.IsActive() && (!inLinearVelocity.IsNearZero() || !inAngularVelocity.IsNearZero())) + mBodyManager->ActivateBodies(&inBodyID, 1); + } + } +} + +void BodyInterface::SetMotionType(const BodyID &inBodyID, EMotionType inMotionType, EActivation inActivationMode) +{ + BodyLockWrite lock(*mBodyLockInterface, inBodyID); + if (lock.Succeeded()) + { + Body &body = lock.GetBody(); + + // Deactivate if we're making the body static + if (body.IsActive() && inMotionType == EMotionType::Static) + mBodyManager->DeactivateBodies(&inBodyID, 1); + + body.SetMotionType(inMotionType); + + // Activate body if requested + if (inMotionType != EMotionType::Static && inActivationMode == EActivation::Activate && !body.IsActive()) + mBodyManager->ActivateBodies(&inBodyID, 1); + } +} + +EBodyType BodyInterface::GetBodyType(const BodyID &inBodyID) const +{ + BodyLockRead lock(*mBodyLockInterface, inBodyID); + if (lock.Succeeded()) + return lock.GetBody().GetBodyType(); + else + return EBodyType::RigidBody; +} + +EMotionType BodyInterface::GetMotionType(const BodyID &inBodyID) const +{ + BodyLockRead lock(*mBodyLockInterface, inBodyID); + if (lock.Succeeded()) + return lock.GetBody().GetMotionType(); + else + return EMotionType::Static; +} + +void BodyInterface::SetMotionQuality(const BodyID &inBodyID, EMotionQuality inMotionQuality) +{ + BodyLockWrite lock(*mBodyLockInterface, inBodyID); + if (lock.Succeeded()) + mBodyManager->SetMotionQuality(lock.GetBody(), inMotionQuality); +} + +EMotionQuality BodyInterface::GetMotionQuality(const BodyID &inBodyID) const +{ + BodyLockRead lock(*mBodyLockInterface, inBodyID); + if (lock.Succeeded() && !lock.GetBody().IsStatic()) + return lock.GetBody().GetMotionProperties()->GetMotionQuality(); + else + return EMotionQuality::Discrete; +} + +Mat44 BodyInterface::GetInverseInertia(const BodyID &inBodyID) const +{ + BodyLockRead lock(*mBodyLockInterface, inBodyID); + if (lock.Succeeded()) + return lock.GetBody().GetInverseInertia(); + else + return Mat44::sIdentity(); +} + +void BodyInterface::SetRestitution(const BodyID &inBodyID, float inRestitution) +{ + BodyLockWrite lock(*mBodyLockInterface, inBodyID); + if (lock.Succeeded()) + lock.GetBody().SetRestitution(inRestitution); +} + +float BodyInterface::GetRestitution(const BodyID &inBodyID) const +{ + BodyLockRead lock(*mBodyLockInterface, inBodyID); + if (lock.Succeeded()) + return lock.GetBody().GetRestitution(); + else + return 0.0f; +} + +void BodyInterface::SetFriction(const BodyID &inBodyID, float inFriction) +{ + BodyLockWrite lock(*mBodyLockInterface, inBodyID); + if (lock.Succeeded()) + lock.GetBody().SetFriction(inFriction); +} + +float BodyInterface::GetFriction(const BodyID &inBodyID) const +{ + BodyLockRead lock(*mBodyLockInterface, inBodyID); + if (lock.Succeeded()) + return lock.GetBody().GetFriction(); + else + return 0.0f; +} + +void BodyInterface::SetGravityFactor(const BodyID &inBodyID, float inGravityFactor) +{ + BodyLockWrite lock(*mBodyLockInterface, inBodyID); + if (lock.Succeeded() && lock.GetBody().GetMotionPropertiesUnchecked() != nullptr) + lock.GetBody().GetMotionPropertiesUnchecked()->SetGravityFactor(inGravityFactor); +} + +float BodyInterface::GetGravityFactor(const BodyID &inBodyID) const +{ + BodyLockRead lock(*mBodyLockInterface, inBodyID); + if (lock.Succeeded() && lock.GetBody().GetMotionPropertiesUnchecked() != nullptr) + return lock.GetBody().GetMotionPropertiesUnchecked()->GetGravityFactor(); + else + return 1.0f; +} + +void BodyInterface::SetUseManifoldReduction(const BodyID &inBodyID, bool inUseReduction) +{ + BodyLockWrite lock(*mBodyLockInterface, inBodyID); + if (lock.Succeeded()) + { + Body &body = lock.GetBody(); + if (body.GetUseManifoldReduction() != inUseReduction) + { + body.SetUseManifoldReduction(inUseReduction); + + // Flag collision cache invalid for this body + mBodyManager->InvalidateContactCacheForBody(body); + } + } +} + +bool BodyInterface::GetUseManifoldReduction(const BodyID &inBodyID) const +{ + BodyLockRead lock(*mBodyLockInterface, inBodyID); + if (lock.Succeeded()) + return lock.GetBody().GetUseManifoldReduction(); + else + return true; +} + +TransformedShape BodyInterface::GetTransformedShape(const BodyID &inBodyID) const +{ + BodyLockRead lock(*mBodyLockInterface, inBodyID); + if (lock.Succeeded()) + return lock.GetBody().GetTransformedShape(); + else + return TransformedShape(); +} + +uint64 BodyInterface::GetUserData(const BodyID &inBodyID) const +{ + BodyLockRead lock(*mBodyLockInterface, inBodyID); + if (lock.Succeeded()) + return lock.GetBody().GetUserData(); + else + return 0; +} + +void BodyInterface::SetUserData(const BodyID &inBodyID, uint64 inUserData) const +{ + BodyLockWrite lock(*mBodyLockInterface, inBodyID); + if (lock.Succeeded()) + lock.GetBody().SetUserData(inUserData); +} + +const PhysicsMaterial *BodyInterface::GetMaterial(const BodyID &inBodyID, const SubShapeID &inSubShapeID) const +{ + BodyLockRead lock(*mBodyLockInterface, inBodyID); + if (lock.Succeeded()) + return lock.GetBody().GetShape()->GetMaterial(inSubShapeID); + else + return PhysicsMaterial::sDefault; +} + +void BodyInterface::InvalidateContactCache(const BodyID &inBodyID) +{ + BodyLockWrite lock(*mBodyLockInterface, inBodyID); + if (lock.Succeeded()) + mBodyManager->InvalidateContactCacheForBody(lock.GetBody()); +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyInterface.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyInterface.h new file mode 100644 index 00000000000..3bdee8305ef --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyInterface.h @@ -0,0 +1,278 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +class Body; +class BodyCreationSettings; +class SoftBodyCreationSettings; +class BodyLockInterface; +class BroadPhase; +class BodyManager; +class TransformedShape; +class PhysicsMaterial; +class SubShapeID; +class Shape; +class TwoBodyConstraintSettings; +class TwoBodyConstraint; +class BroadPhaseLayerFilter; +class AABox; + +/// Class that provides operations on bodies using a body ID. Note that if you need to do multiple operations on a single body, it is more efficient to lock the body once and combine the operations. +/// All quantities are in world space unless otherwise specified. +class JPH_EXPORT BodyInterface : public NonCopyable +{ +public: + /// Initialize the interface (should only be called by PhysicsSystem) + void Init(BodyLockInterface &inBodyLockInterface, BodyManager &inBodyManager, BroadPhase &inBroadPhase) { mBodyLockInterface = &inBodyLockInterface; mBodyManager = &inBodyManager; mBroadPhase = &inBroadPhase; } + + /// Create a rigid body + /// @return Created body or null when out of bodies + Body * CreateBody(const BodyCreationSettings &inSettings); + + /// Create a soft body + /// @return Created body or null when out of bodies + Body * CreateSoftBody(const SoftBodyCreationSettings &inSettings); + + /// Create a rigid body with specified ID. This function can be used if a simulation is to run in sync between clients or if a simulation needs to be restored exactly. + /// The ID created on the server can be replicated to the client and used to create a deterministic simulation. + /// @return Created body or null when the body ID is invalid or a body of the same ID already exists. + Body * CreateBodyWithID(const BodyID &inBodyID, const BodyCreationSettings &inSettings); + + /// Create a soft body with specified ID. See comments at CreateBodyWithID. + Body * CreateSoftBodyWithID(const BodyID &inBodyID, const SoftBodyCreationSettings &inSettings); + + /// Advanced use only. Creates a rigid body without specifying an ID. This body cannot be added to the physics system until it has been assigned a body ID. + /// This can be used to decouple allocation from registering the body. A call to CreateBodyWithoutID followed by AssignBodyID is equivalent to calling CreateBodyWithID. + /// @return Created body + Body * CreateBodyWithoutID(const BodyCreationSettings &inSettings) const; + + /// Advanced use only. Creates a body without specifying an ID. See comments at CreateBodyWithoutID. + Body * CreateSoftBodyWithoutID(const SoftBodyCreationSettings &inSettings) const; + + /// Advanced use only. Destroy a body previously created with CreateBodyWithoutID that hasn't gotten an ID yet through the AssignBodyID function, + /// or a body that has had its body ID unassigned through UnassignBodyIDs. Bodies that have an ID should be destroyed through DestroyBody. + void DestroyBodyWithoutID(Body *inBody) const; + + /// Advanced use only. Assigns the next available body ID to a body that was created using CreateBodyWithoutID. After this call, the body can be added to the physics system. + /// @return false if the body already has an ID or out of body ids. + bool AssignBodyID(Body *ioBody); + + /// Advanced use only. Assigns a body ID to a body that was created using CreateBodyWithoutID. After this call, the body can be added to the physics system. + /// @return false if the body already has an ID or if the ID is not valid. + bool AssignBodyID(Body *ioBody, const BodyID &inBodyID); + + /// Advanced use only. See UnassignBodyIDs. Unassigns the ID of a single body. + Body * UnassignBodyID(const BodyID &inBodyID); + + /// Advanced use only. Removes a number of body IDs from their bodies and returns the body pointers. Before calling this, the body should have been removed from the physics system. + /// The body can be destroyed through DestroyBodyWithoutID. This can be used to decouple deallocation. A call to UnassignBodyIDs followed by calls to DestroyBodyWithoutID is equivalent to calling DestroyBodies. + /// @param inBodyIDs A list of body IDs + /// @param inNumber Number of bodies in the list + /// @param outBodies If not null on input, this will contain a list of body pointers corresponding to inBodyIDs that can be destroyed afterwards (caller assumes ownership over these). + void UnassignBodyIDs(const BodyID *inBodyIDs, int inNumber, Body **outBodies); + + /// Destroy a body + void DestroyBody(const BodyID &inBodyID); + + /// Destroy multiple bodies + void DestroyBodies(const BodyID *inBodyIDs, int inNumber); + + /// Add body to the physics system. + /// Note that if you need to add multiple bodies, use the AddBodiesPrepare/AddBodiesFinalize function. + /// Adding many bodies, one at a time, results in a really inefficient broadphase until PhysicsSystem::OptimizeBroadPhase is called or when PhysicsSystem::Update rebuilds the tree! + /// After adding, to get a body by ID use the BodyLockRead or BodyLockWrite interface! + void AddBody(const BodyID &inBodyID, EActivation inActivationMode); + + /// Remove body from the physics system. + void RemoveBody(const BodyID &inBodyID); + + /// Check if a body has been added to the physics system. + bool IsAdded(const BodyID &inBodyID) const; + + /// Combines CreateBody and AddBody + /// @return Created body ID or an invalid ID when out of bodies + BodyID CreateAndAddBody(const BodyCreationSettings &inSettings, EActivation inActivationMode); + + /// Combines CreateSoftBody and AddBody + /// @return Created body ID or an invalid ID when out of bodies + BodyID CreateAndAddSoftBody(const SoftBodyCreationSettings &inSettings, EActivation inActivationMode); + + /// Broadphase add state handle, used to keep track of a batch while adding to the broadphase. + using AddState = void *; + + ///@name Batch adding interface, see Broadphase for further documentation. + /// Note that ioBodies array must be kept constant while the add is in progress. + ///@{ + AddState AddBodiesPrepare(BodyID *ioBodies, int inNumber); + void AddBodiesFinalize(BodyID *ioBodies, int inNumber, AddState inAddState, EActivation inActivationMode); + void AddBodiesAbort(BodyID *ioBodies, int inNumber, AddState inAddState); + void RemoveBodies(BodyID *ioBodies, int inNumber); + ///@} + + ///@name Activate / deactivate a body + ///@{ + void ActivateBody(const BodyID &inBodyID); + void ActivateBodies(const BodyID *inBodyIDs, int inNumber); + void ActivateBodiesInAABox(const AABox &inBox, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter); + void DeactivateBody(const BodyID &inBodyID); + void DeactivateBodies(const BodyID *inBodyIDs, int inNumber); + bool IsActive(const BodyID &inBodyID) const; + ///@} + + /// Create a two body constraint + TwoBodyConstraint * CreateConstraint(const TwoBodyConstraintSettings *inSettings, const BodyID &inBodyID1, const BodyID &inBodyID2); + + /// Activate non-static bodies attached to a constraint + void ActivateConstraint(const TwoBodyConstraint *inConstraint); + + ///@name Access to the shape of a body + ///@{ + + /// Get the current shape + RefConst GetShape(const BodyID &inBodyID) const; + + /// Set a new shape on the body + /// @param inBodyID Body ID of body that had its shape changed + /// @param inShape The new shape + /// @param inUpdateMassProperties When true, the mass and inertia tensor is recalculated + /// @param inActivationMode Weather or not to activate the body + void SetShape(const BodyID &inBodyID, const Shape *inShape, bool inUpdateMassProperties, EActivation inActivationMode) const; + + /// Notify all systems to indicate that a shape has changed (usable for MutableCompoundShapes) + /// @param inBodyID Body ID of body that had its shape changed + /// @param inPreviousCenterOfMass Center of mass of the shape before the alterations + /// @param inUpdateMassProperties When true, the mass and inertia tensor is recalculated + /// @param inActivationMode Weather or not to activate the body + void NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inPreviousCenterOfMass, bool inUpdateMassProperties, EActivation inActivationMode) const; + ///@} + + ///@name Object layer of a body + ///@{ + void SetObjectLayer(const BodyID &inBodyID, ObjectLayer inLayer); + ObjectLayer GetObjectLayer(const BodyID &inBodyID) const; + ///@} + + ///@name Position and rotation of a body + ///@{ + void SetPositionAndRotation(const BodyID &inBodyID, RVec3Arg inPosition, QuatArg inRotation, EActivation inActivationMode); + void SetPositionAndRotationWhenChanged(const BodyID &inBodyID, RVec3Arg inPosition, QuatArg inRotation, EActivation inActivationMode); ///< Will only update the position/rotation and activate the body when the difference is larger than a very small number. This avoids updating the broadphase/waking up a body when the resulting position/orientation doesn't really change. + void GetPositionAndRotation(const BodyID &inBodyID, RVec3 &outPosition, Quat &outRotation) const; + void SetPosition(const BodyID &inBodyID, RVec3Arg inPosition, EActivation inActivationMode); + RVec3 GetPosition(const BodyID &inBodyID) const; + RVec3 GetCenterOfMassPosition(const BodyID &inBodyID) const; + void SetRotation(const BodyID &inBodyID, QuatArg inRotation, EActivation inActivationMode); + Quat GetRotation(const BodyID &inBodyID) const; + RMat44 GetWorldTransform(const BodyID &inBodyID) const; + RMat44 GetCenterOfMassTransform(const BodyID &inBodyID) const; + ///@} + + /// Set velocity of body such that it will be positioned at inTargetPosition/Rotation in inDeltaTime seconds (will activate body if needed) + void MoveKinematic(const BodyID &inBodyID, RVec3Arg inTargetPosition, QuatArg inTargetRotation, float inDeltaTime); + + /// Linear or angular velocity (functions will activate body if needed). + /// Note that the linear velocity is the velocity of the center of mass, which may not coincide with the position of your object, to correct for this: \f$VelocityCOM = Velocity - AngularVelocity \times ShapeCOM\f$ + void SetLinearAndAngularVelocity(const BodyID &inBodyID, Vec3Arg inLinearVelocity, Vec3Arg inAngularVelocity); + void GetLinearAndAngularVelocity(const BodyID &inBodyID, Vec3 &outLinearVelocity, Vec3 &outAngularVelocity) const; + void SetLinearVelocity(const BodyID &inBodyID, Vec3Arg inLinearVelocity); + Vec3 GetLinearVelocity(const BodyID &inBodyID) const; + void AddLinearVelocity(const BodyID &inBodyID, Vec3Arg inLinearVelocity); ///< Add velocity to current velocity + void AddLinearAndAngularVelocity(const BodyID &inBodyID, Vec3Arg inLinearVelocity, Vec3Arg inAngularVelocity); ///< Add linear and angular to current velocities + void SetAngularVelocity(const BodyID &inBodyID, Vec3Arg inAngularVelocity); + Vec3 GetAngularVelocity(const BodyID &inBodyID) const; + Vec3 GetPointVelocity(const BodyID &inBodyID, RVec3Arg inPoint) const; ///< Velocity of point inPoint (in world space, e.g. on the surface of the body) of the body + + /// Set the complete motion state of a body. + /// Note that the linear velocity is the velocity of the center of mass, which may not coincide with the position of your object, to correct for this: \f$VelocityCOM = Velocity - AngularVelocity \times ShapeCOM\f$ + void SetPositionRotationAndVelocity(const BodyID &inBodyID, RVec3Arg inPosition, QuatArg inRotation, Vec3Arg inLinearVelocity, Vec3Arg inAngularVelocity); + + ///@name Add forces to the body + ///@{ + void AddForce(const BodyID &inBodyID, Vec3Arg inForce); ///< See Body::AddForce + void AddForce(const BodyID &inBodyID, Vec3Arg inForce, RVec3Arg inPoint); ///< Applied at inPoint + void AddTorque(const BodyID &inBodyID, Vec3Arg inTorque); ///< See Body::AddTorque + void AddForceAndTorque(const BodyID &inBodyID, Vec3Arg inForce, Vec3Arg inTorque); ///< A combination of Body::AddForce and Body::AddTorque + ///@} + + ///@name Add an impulse to the body + ///@{ + void AddImpulse(const BodyID &inBodyID, Vec3Arg inImpulse); ///< Applied at center of mass + void AddImpulse(const BodyID &inBodyID, Vec3Arg inImpulse, RVec3Arg inPoint); ///< Applied at inPoint + void AddAngularImpulse(const BodyID &inBodyID, Vec3Arg inAngularImpulse); + ///@} + + ///@name Body type + ///@{ + EBodyType GetBodyType(const BodyID &inBodyID) const; + ///@} + + ///@name Body motion type + ///@{ + void SetMotionType(const BodyID &inBodyID, EMotionType inMotionType, EActivation inActivationMode); + EMotionType GetMotionType(const BodyID &inBodyID) const; + ///@} + + ///@name Body motion quality + ///@{ + void SetMotionQuality(const BodyID &inBodyID, EMotionQuality inMotionQuality); + EMotionQuality GetMotionQuality(const BodyID &inBodyID) const; + ///@} + + /// Get inverse inertia tensor in world space + Mat44 GetInverseInertia(const BodyID &inBodyID) const; + + ///@name Restitution + ///@{ + void SetRestitution(const BodyID &inBodyID, float inRestitution); + float GetRestitution(const BodyID &inBodyID) const; + ///@} + + ///@name Friction + ///@{ + void SetFriction(const BodyID &inBodyID, float inFriction); + float GetFriction(const BodyID &inBodyID) const; + ///@} + + ///@name Gravity factor + ///@{ + void SetGravityFactor(const BodyID &inBodyID, float inGravityFactor); + float GetGravityFactor(const BodyID &inBodyID) const; + ///@} + + ///@name Manifold reduction + ///@{ + void SetUseManifoldReduction(const BodyID &inBodyID, bool inUseReduction); + bool GetUseManifoldReduction(const BodyID &inBodyID) const; + ///@} + + /// Get transform and shape for this body, used to perform collision detection + TransformedShape GetTransformedShape(const BodyID &inBodyID) const; + + /// Get the user data for a body + uint64 GetUserData(const BodyID &inBodyID) const; + void SetUserData(const BodyID &inBodyID, uint64 inUserData) const; + + /// Get the material for a particular sub shape + const PhysicsMaterial * GetMaterial(const BodyID &inBodyID, const SubShapeID &inSubShapeID) const; + + /// Set the Body::EFlags::InvalidateContactCache flag for the specified body. This means that the collision cache is invalid for any body pair involving that body until the next physics step. + void InvalidateContactCache(const BodyID &inBodyID); + +private: + BodyLockInterface * mBodyLockInterface = nullptr; + BodyManager * mBodyManager = nullptr; + BroadPhase * mBroadPhase = nullptr; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyLock.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyLock.h new file mode 100644 index 00000000000..eccfec4d1e8 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyLock.h @@ -0,0 +1,111 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +/// Base class for locking bodies for the duration of the scope of this class (do not use directly) +template +class BodyLockBase : public NonCopyable +{ +public: + /// Constructor will lock the body + BodyLockBase(const BodyLockInterface &inBodyLockInterface, const BodyID &inBodyID) : + mBodyLockInterface(inBodyLockInterface) + { + if (inBodyID == BodyID()) + { + // Invalid body id + mBodyLockMutex = nullptr; + mBody = nullptr; + } + else + { + // Get mutex + mBodyLockMutex = Write? inBodyLockInterface.LockWrite(inBodyID) : inBodyLockInterface.LockRead(inBodyID); + + // Get a reference to the body or nullptr when it is no longer valid + mBody = inBodyLockInterface.TryGetBody(inBodyID); + } + } + + /// Explicitly release the lock (normally this is done in the destructor) + inline void ReleaseLock() + { + if (mBodyLockMutex != nullptr) + { + if (Write) + mBodyLockInterface.UnlockWrite(mBodyLockMutex); + else + mBodyLockInterface.UnlockRead(mBodyLockMutex); + + mBodyLockMutex = nullptr; + mBody = nullptr; + } + } + + /// Destructor will unlock the body + ~BodyLockBase() + { + ReleaseLock(); + } + + /// Test if the lock was successful (if the body ID was valid) + inline bool Succeeded() const + { + return mBody != nullptr; + } + + /// Test if the lock was successful (if the body ID was valid) and the body is still in the broad phase + inline bool SucceededAndIsInBroadPhase() const + { + return mBody != nullptr && mBody->IsInBroadPhase(); + } + + /// Access the body + inline BodyType & GetBody() const + { + JPH_ASSERT(mBody != nullptr, "Should check Succeeded() first"); + return *mBody; + } + +private: + const BodyLockInterface & mBodyLockInterface; + SharedMutex * mBodyLockMutex; + BodyType * mBody; +}; + +/// A body lock takes a body ID and locks the underlying body so that other threads cannot access its members +/// +/// The common usage pattern is: +/// +/// BodyLockInterface lock_interface = physics_system.GetBodyLockInterface(); // Or non-locking interface if the lock is already taken +/// BodyID body_id = ...; // Obtain ID to body +/// +/// // Scoped lock +/// { +/// BodyLockRead lock(lock_interface, body_id); +/// if (lock.Succeeded()) // body_id may no longer be valid +/// { +/// const Body &body = lock.GetBody(); +/// +/// // Do something with body +/// ... +/// } +/// } +class BodyLockRead : public BodyLockBase +{ + using BodyLockBase::BodyLockBase; +}; + +/// Specialization that locks a body for writing to. @see BodyLockRead for usage patterns. +class BodyLockWrite : public BodyLockBase +{ + using BodyLockBase::BodyLockBase; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyLockInterface.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyLockInterface.h new file mode 100644 index 00000000000..b65951fc8cc --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyLockInterface.h @@ -0,0 +1,134 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + + +#pragma once + +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +/// Base class interface for locking a body. Usually you will use BodyLockRead / BodyLockWrite / BodyLockMultiRead / BodyLockMultiWrite instead. +class BodyLockInterface : public NonCopyable +{ +public: + /// Redefine MutexMask + using MutexMask = BodyManager::MutexMask; + + /// Constructor + explicit BodyLockInterface(BodyManager &inBodyManager) : mBodyManager(inBodyManager) { } + virtual ~BodyLockInterface() = default; + + ///@name Locking functions + ///@{ + virtual SharedMutex * LockRead(const BodyID &inBodyID) const = 0; + virtual void UnlockRead(SharedMutex *inMutex) const = 0; + virtual SharedMutex * LockWrite(const BodyID &inBodyID) const = 0; + virtual void UnlockWrite(SharedMutex *inMutex) const = 0; + ///@} + + /// Get the mask needed to lock all bodies + inline MutexMask GetAllBodiesMutexMask() const + { + return mBodyManager.GetAllBodiesMutexMask(); + } + + ///@name Batch locking functions + ///@{ + virtual MutexMask GetMutexMask(const BodyID *inBodies, int inNumber) const = 0; + virtual void LockRead(MutexMask inMutexMask) const = 0; + virtual void UnlockRead(MutexMask inMutexMask) const = 0; + virtual void LockWrite(MutexMask inMutexMask) const = 0; + virtual void UnlockWrite(MutexMask inMutexMask) const = 0; + ///@} + + /// Convert body ID to body + inline Body * TryGetBody(const BodyID &inBodyID) const { return mBodyManager.TryGetBody(inBodyID); } + +protected: + BodyManager & mBodyManager; +}; + +/// Implementation that performs no locking (assumes the lock has already been taken) +class BodyLockInterfaceNoLock final : public BodyLockInterface +{ +public: + using BodyLockInterface::BodyLockInterface; + + ///@name Locking functions + virtual SharedMutex * LockRead([[maybe_unused]] const BodyID &inBodyID) const override { return nullptr; } + virtual void UnlockRead([[maybe_unused]] SharedMutex *inMutex) const override { /* Nothing to do */ } + virtual SharedMutex * LockWrite([[maybe_unused]] const BodyID &inBodyID) const override { return nullptr; } + virtual void UnlockWrite([[maybe_unused]] SharedMutex *inMutex) const override { /* Nothing to do */ } + + ///@name Batch locking functions + virtual MutexMask GetMutexMask([[maybe_unused]] const BodyID *inBodies, [[maybe_unused]] int inNumber) const override { return 0; } + virtual void LockRead([[maybe_unused]] MutexMask inMutexMask) const override { /* Nothing to do */ } + virtual void UnlockRead([[maybe_unused]] MutexMask inMutexMask) const override { /* Nothing to do */ } + virtual void LockWrite([[maybe_unused]] MutexMask inMutexMask) const override { /* Nothing to do */ } + virtual void UnlockWrite([[maybe_unused]] MutexMask inMutexMask) const override { /* Nothing to do */ } +}; + +/// Implementation that uses the body manager to lock the correct mutex for a body +class BodyLockInterfaceLocking final : public BodyLockInterface +{ +public: + using BodyLockInterface::BodyLockInterface; + + ///@name Locking functions + virtual SharedMutex * LockRead(const BodyID &inBodyID) const override + { + SharedMutex &mutex = mBodyManager.GetMutexForBody(inBodyID); + PhysicsLock::sLockShared(mutex JPH_IF_ENABLE_ASSERTS(, &mBodyManager, EPhysicsLockTypes::PerBody)); + return &mutex; + } + + virtual void UnlockRead(SharedMutex *inMutex) const override + { + PhysicsLock::sUnlockShared(*inMutex JPH_IF_ENABLE_ASSERTS(, &mBodyManager, EPhysicsLockTypes::PerBody)); + } + + virtual SharedMutex * LockWrite(const BodyID &inBodyID) const override + { + SharedMutex &mutex = mBodyManager.GetMutexForBody(inBodyID); + PhysicsLock::sLock(mutex JPH_IF_ENABLE_ASSERTS(, &mBodyManager, EPhysicsLockTypes::PerBody)); + return &mutex; + } + + virtual void UnlockWrite(SharedMutex *inMutex) const override + { + PhysicsLock::sUnlock(*inMutex JPH_IF_ENABLE_ASSERTS(, &mBodyManager, EPhysicsLockTypes::PerBody)); + } + + ///@name Batch locking functions + virtual MutexMask GetMutexMask(const BodyID *inBodies, int inNumber) const override + { + return mBodyManager.GetMutexMask(inBodies, inNumber); + } + + virtual void LockRead(MutexMask inMutexMask) const override + { + mBodyManager.LockRead(inMutexMask); + } + + virtual void UnlockRead(MutexMask inMutexMask) const override + { + mBodyManager.UnlockRead(inMutexMask); + } + + virtual void LockWrite(MutexMask inMutexMask) const override + { + mBodyManager.LockWrite(inMutexMask); + } + + virtual void UnlockWrite(MutexMask inMutexMask) const override + { + mBodyManager.UnlockWrite(inMutexMask); + } +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyLockMulti.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyLockMulti.h new file mode 100644 index 00000000000..5872729c029 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyLockMulti.h @@ -0,0 +1,104 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +/// Base class for locking multiple bodies for the duration of the scope of this class (do not use directly) +template +class BodyLockMultiBase : public NonCopyable +{ +public: + /// Redefine MutexMask + using MutexMask = BodyLockInterface::MutexMask; + + /// Constructor will lock the bodies + BodyLockMultiBase(const BodyLockInterface &inBodyLockInterface, const BodyID *inBodyIDs, int inNumber) : + mBodyLockInterface(inBodyLockInterface), + mMutexMask(inBodyLockInterface.GetMutexMask(inBodyIDs, inNumber)), + mBodyIDs(inBodyIDs), + mNumBodyIDs(inNumber) + { + if (mMutexMask != 0) + { + // Get mutex + if (Write) + inBodyLockInterface.LockWrite(mMutexMask); + else + inBodyLockInterface.LockRead(mMutexMask); + } + } + + /// Destructor will unlock the bodies + ~BodyLockMultiBase() + { + if (mMutexMask != 0) + { + if (Write) + mBodyLockInterface.UnlockWrite(mMutexMask); + else + mBodyLockInterface.UnlockRead(mMutexMask); + } + } + + /// Access the body (returns null if body was not properly locked) + inline BodyType * GetBody(int inBodyIndex) const + { + // Range check + JPH_ASSERT(inBodyIndex >= 0 && inBodyIndex < mNumBodyIDs); + + // Get body ID + const BodyID &body_id = mBodyIDs[inBodyIndex]; + if (body_id.IsInvalid()) + return nullptr; + + // Get a reference to the body or nullptr when it is no longer valid + return mBodyLockInterface.TryGetBody(body_id); + } + +private: + const BodyLockInterface & mBodyLockInterface; + MutexMask mMutexMask; + const BodyID * mBodyIDs; + int mNumBodyIDs; +}; + +/// A multi body lock takes a number of body IDs and locks the underlying bodies so that other threads cannot access its members +/// +/// The common usage pattern is: +/// +/// BodyLockInterface lock_interface = physics_system.GetBodyLockInterface(); // Or non-locking interface if the lock is already taken +/// const BodyID *body_id = ...; // Obtain IDs to bodies +/// int num_body_ids = ...; +/// +/// // Scoped lock +/// { +/// BodyLockMultiRead lock(lock_interface, body_ids, num_body_ids); +/// for (int i = 0; i < num_body_ids; ++i) +/// { +/// const Body *body = lock.GetBody(i); +/// if (body != nullptr) +/// { +/// const Body &body = lock.Body(); +/// +/// // Do something with body +/// ... +/// } +/// } +/// } +class BodyLockMultiRead : public BodyLockMultiBase +{ + using BodyLockMultiBase::BodyLockMultiBase; +}; + +/// Specialization that locks multiple bodies for writing to. @see BodyLockMultiRead for usage patterns. +class BodyLockMultiWrite : public BodyLockMultiBase +{ + using BodyLockMultiBase::BodyLockMultiBase; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyManager.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyManager.cpp new file mode 100644 index 00000000000..09995ac7614 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyManager.cpp @@ -0,0 +1,1149 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef JPH_DEBUG_RENDERER + #include + #include +#endif // JPH_DEBUG_RENDERER + +JPH_NAMESPACE_BEGIN + +#ifdef JPH_ENABLE_ASSERTS + static thread_local bool sOverrideAllowActivation = false; + static thread_local bool sOverrideAllowDeactivation = false; + + bool BodyManager::sGetOverrideAllowActivation() + { + return sOverrideAllowActivation; + } + + void BodyManager::sSetOverrideAllowActivation(bool inValue) + { + sOverrideAllowActivation = inValue; + } + + bool BodyManager::sGetOverrideAllowDeactivation() + { + return sOverrideAllowDeactivation; + } + + void BodyManager::sSetOverrideAllowDeactivation(bool inValue) + { + sOverrideAllowDeactivation = inValue; + } +#endif + +// Helper class that combines a body and its motion properties +class BodyWithMotionProperties : public Body +{ +public: + JPH_OVERRIDE_NEW_DELETE + + MotionProperties mMotionProperties; +}; + +// Helper class that combines a soft body its motion properties and shape +class SoftBodyWithMotionPropertiesAndShape : public Body +{ +public: + SoftBodyWithMotionPropertiesAndShape() + { + mShape.SetEmbedded(); + } + + SoftBodyMotionProperties mMotionProperties; + SoftBodyShape mShape; +}; + +inline void BodyManager::sDeleteBody(Body *inBody) +{ + if (inBody->mMotionProperties != nullptr) + { + JPH_IF_ENABLE_ASSERTS(inBody->mMotionProperties = nullptr;) + if (inBody->IsSoftBody()) + { + inBody->mShape = nullptr; // Release the shape to avoid assertion on shape destruction because of embedded object with refcount > 0 + delete static_cast(inBody); + } + else + delete static_cast(inBody); + } + else + delete inBody; +} + +BodyManager::~BodyManager() +{ + UniqueLock lock(mBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::BodiesList)); + + // Destroy any bodies that are still alive + for (Body *b : mBodies) + if (sIsValidBodyPointer(b)) + sDeleteBody(b); + + for (BodyID *active_bodies : mActiveBodies) + delete [] active_bodies; +} + +void BodyManager::Init(uint inMaxBodies, uint inNumBodyMutexes, const BroadPhaseLayerInterface &inLayerInterface) +{ + UniqueLock lock(mBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::BodiesList)); + + // Num body mutexes must be a power of two and not bigger than our MutexMask + uint num_body_mutexes = Clamp(GetNextPowerOf2(inNumBodyMutexes == 0? 2 * thread::hardware_concurrency() : inNumBodyMutexes), 1, sizeof(MutexMask) * 8); + + // Allocate the body mutexes + mBodyMutexes.Init(num_body_mutexes); + + // Allocate space for bodies + mBodies.reserve(inMaxBodies); + + // Allocate space for active bodies + for (BodyID *&active_bodies : mActiveBodies) + { + JPH_ASSERT(active_bodies == nullptr); + active_bodies = new BodyID [inMaxBodies]; + } + + // Allocate space for sequence numbers + mBodySequenceNumbers.resize(inMaxBodies); + + // Keep layer interface + mBroadPhaseLayerInterface = &inLayerInterface; +} + +uint BodyManager::GetNumBodies() const +{ + UniqueLock lock(mBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::BodiesList)); + + return mNumBodies; +} + +BodyManager::BodyStats BodyManager::GetBodyStats() const +{ + UniqueLock lock(mBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::BodiesList)); + + BodyStats stats; + stats.mNumBodies = mNumBodies; + stats.mMaxBodies = uint(mBodies.capacity()); + + for (const Body *body : mBodies) + if (sIsValidBodyPointer(body)) + { + if (body->IsSoftBody()) + { + stats.mNumSoftBodies++; + if (body->IsActive()) + stats.mNumActiveSoftBodies++; + } + else + { + switch (body->GetMotionType()) + { + case EMotionType::Static: + stats.mNumBodiesStatic++; + break; + + case EMotionType::Dynamic: + stats.mNumBodiesDynamic++; + if (body->IsActive()) + stats.mNumActiveBodiesDynamic++; + break; + + case EMotionType::Kinematic: + stats.mNumBodiesKinematic++; + if (body->IsActive()) + stats.mNumActiveBodiesKinematic++; + break; + } + } + } + + return stats; +} + +Body *BodyManager::AllocateBody(const BodyCreationSettings &inBodyCreationSettings) const +{ + // Fill in basic properties + Body *body; + if (inBodyCreationSettings.HasMassProperties()) + { + BodyWithMotionProperties *bmp = new BodyWithMotionProperties; + body = bmp; + body->mMotionProperties = &bmp->mMotionProperties; + } + else + { + body = new Body; + } + body->mBodyType = EBodyType::RigidBody; + body->mShape = inBodyCreationSettings.GetShape(); + body->mUserData = inBodyCreationSettings.mUserData; + body->SetFriction(inBodyCreationSettings.mFriction); + body->SetRestitution(inBodyCreationSettings.mRestitution); + body->mMotionType = inBodyCreationSettings.mMotionType; + if (inBodyCreationSettings.mIsSensor) + body->SetIsSensor(true); + if (inBodyCreationSettings.mCollideKinematicVsNonDynamic) + body->SetCollideKinematicVsNonDynamic(true); + if (inBodyCreationSettings.mUseManifoldReduction) + body->SetUseManifoldReduction(true); + if (inBodyCreationSettings.mApplyGyroscopicForce) + body->SetApplyGyroscopicForce(true); + if (inBodyCreationSettings.mEnhancedInternalEdgeRemoval) + body->SetEnhancedInternalEdgeRemoval(true); + SetBodyObjectLayerInternal(*body, inBodyCreationSettings.mObjectLayer); + body->mObjectLayer = inBodyCreationSettings.mObjectLayer; + body->mCollisionGroup = inBodyCreationSettings.mCollisionGroup; + + if (inBodyCreationSettings.HasMassProperties()) + { + MotionProperties *mp = body->mMotionProperties; + mp->SetLinearDamping(inBodyCreationSettings.mLinearDamping); + mp->SetAngularDamping(inBodyCreationSettings.mAngularDamping); + mp->SetMaxLinearVelocity(inBodyCreationSettings.mMaxLinearVelocity); + mp->SetMaxAngularVelocity(inBodyCreationSettings.mMaxAngularVelocity); + mp->SetMassProperties(inBodyCreationSettings.mAllowedDOFs, inBodyCreationSettings.GetMassProperties()); + mp->SetLinearVelocity(inBodyCreationSettings.mLinearVelocity); // Needs to happen after setting the max linear/angular velocity and setting allowed DOFs + mp->SetAngularVelocity(inBodyCreationSettings.mAngularVelocity); + mp->SetGravityFactor(inBodyCreationSettings.mGravityFactor); + mp->SetNumVelocityStepsOverride(inBodyCreationSettings.mNumVelocityStepsOverride); + mp->SetNumPositionStepsOverride(inBodyCreationSettings.mNumPositionStepsOverride); + mp->mMotionQuality = inBodyCreationSettings.mMotionQuality; + mp->mAllowSleeping = inBodyCreationSettings.mAllowSleeping; + JPH_IF_ENABLE_ASSERTS(mp->mCachedBodyType = body->mBodyType;) + JPH_IF_ENABLE_ASSERTS(mp->mCachedMotionType = body->mMotionType;) + } + + // Position body + body->SetPositionAndRotationInternal(inBodyCreationSettings.mPosition, inBodyCreationSettings.mRotation); + + return body; +} + +/// Create a soft body using creation settings. The returned body will not be part of the body manager yet. +Body *BodyManager::AllocateSoftBody(const SoftBodyCreationSettings &inSoftBodyCreationSettings) const +{ + // Fill in basic properties + SoftBodyWithMotionPropertiesAndShape *bmp = new SoftBodyWithMotionPropertiesAndShape; + SoftBodyMotionProperties *mp = &bmp->mMotionProperties; + SoftBodyShape *shape = &bmp->mShape; + Body *body = bmp; + shape->mSoftBodyMotionProperties = mp; + body->mBodyType = EBodyType::SoftBody; + body->mMotionProperties = mp; + body->mShape = shape; + body->mUserData = inSoftBodyCreationSettings.mUserData; + body->SetFriction(inSoftBodyCreationSettings.mFriction); + body->SetRestitution(inSoftBodyCreationSettings.mRestitution); + body->mMotionType = EMotionType::Dynamic; + SetBodyObjectLayerInternal(*body, inSoftBodyCreationSettings.mObjectLayer); + body->mObjectLayer = inSoftBodyCreationSettings.mObjectLayer; + body->mCollisionGroup = inSoftBodyCreationSettings.mCollisionGroup; + mp->SetLinearDamping(inSoftBodyCreationSettings.mLinearDamping); + mp->SetAngularDamping(0); + mp->SetMaxLinearVelocity(inSoftBodyCreationSettings.mMaxLinearVelocity); + mp->SetMaxAngularVelocity(FLT_MAX); + mp->SetLinearVelocity(Vec3::sZero()); + mp->SetAngularVelocity(Vec3::sZero()); + mp->SetGravityFactor(inSoftBodyCreationSettings.mGravityFactor); + mp->mMotionQuality = EMotionQuality::Discrete; + mp->mAllowSleeping = inSoftBodyCreationSettings.mAllowSleeping; + JPH_IF_ENABLE_ASSERTS(mp->mCachedBodyType = body->mBodyType;) + JPH_IF_ENABLE_ASSERTS(mp->mCachedMotionType = body->mMotionType;) + mp->Initialize(inSoftBodyCreationSettings); + + body->SetPositionAndRotationInternal(inSoftBodyCreationSettings.mPosition, inSoftBodyCreationSettings.mMakeRotationIdentity? Quat::sIdentity() : inSoftBodyCreationSettings.mRotation); + + return body; +} + +void BodyManager::FreeBody(Body *inBody) const +{ + JPH_ASSERT(inBody->GetID().IsInvalid(), "This function should only be called on a body that doesn't have an ID yet, use DestroyBody otherwise"); + + sDeleteBody(inBody); +} + +bool BodyManager::AddBody(Body *ioBody) +{ + // Return error when body was already added + if (!ioBody->GetID().IsInvalid()) + return false; + + // Determine next free index + uint32 idx; + { + UniqueLock lock(mBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::BodiesList)); + + if (mBodyIDFreeListStart != cBodyIDFreeListEnd) + { + // Pop an item from the freelist + JPH_ASSERT(mBodyIDFreeListStart & cIsFreedBody); + idx = uint32(mBodyIDFreeListStart >> cFreedBodyIndexShift); + JPH_ASSERT(!sIsValidBodyPointer(mBodies[idx])); + mBodyIDFreeListStart = uintptr_t(mBodies[idx]); + mBodies[idx] = ioBody; + } + else + { + if (mBodies.size() < mBodies.capacity()) + { + // Allocate a new entry, note that the array should not actually resize since we've reserved it at init time + idx = uint32(mBodies.size()); + mBodies.push_back(ioBody); + } + else + { + // Out of bodies + return false; + } + } + + // Update cached number of bodies + mNumBodies++; + } + + // Get next sequence number and assign the ID + uint8 seq_no = GetNextSequenceNumber(idx); + ioBody->mID = BodyID(idx, seq_no); + return true; +} + +bool BodyManager::AddBodyWithCustomID(Body *ioBody, const BodyID &inBodyID) +{ + // Return error when body was already added + if (!ioBody->GetID().IsInvalid()) + return false; + + { + UniqueLock lock(mBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::BodiesList)); + + // Check if index is beyond the max body ID + uint32 idx = inBodyID.GetIndex(); + if (idx >= mBodies.capacity()) + return false; // Return error + + if (idx < mBodies.size()) + { + // Body array entry has already been allocated, check if there's a free body here + if (sIsValidBodyPointer(mBodies[idx])) + return false; // Return error + + // Remove the entry from the freelist + uintptr_t idx_start = mBodyIDFreeListStart >> cFreedBodyIndexShift; + if (idx == idx_start) + { + // First entry, easy to remove, the start of the list is our next + mBodyIDFreeListStart = uintptr_t(mBodies[idx]); + } + else + { + // Loop over the freelist and find the entry in the freelist pointing to our index + // TODO: This is O(N), see if this becomes a performance problem (don't want to put the freed bodies in a double linked list) + uintptr_t cur, next; + for (cur = idx_start; cur != cBodyIDFreeListEnd >> cFreedBodyIndexShift; cur = next) + { + next = uintptr_t(mBodies[cur]) >> cFreedBodyIndexShift; + if (next == idx) + { + mBodies[cur] = mBodies[idx]; + break; + } + } + JPH_ASSERT(cur != cBodyIDFreeListEnd >> cFreedBodyIndexShift); + } + + // Put the body in the slot + mBodies[idx] = ioBody; + } + else + { + // Ensure that all body IDs up to this body ID have been allocated and added to the free list + while (idx > mBodies.size()) + { + // Push the id onto the freelist + mBodies.push_back((Body *)mBodyIDFreeListStart); + mBodyIDFreeListStart = (uintptr_t(mBodies.size() - 1) << cFreedBodyIndexShift) | cIsFreedBody; + } + + // Add the element to the list + mBodies.push_back(ioBody); + } + + // Update cached number of bodies + mNumBodies++; + } + + // Assign the ID + ioBody->mID = inBodyID; + return true; +} + +Body *BodyManager::RemoveBodyInternal(const BodyID &inBodyID) +{ + // Get body + uint32 idx = inBodyID.GetIndex(); + Body *body = mBodies[idx]; + + // Validate that it can be removed + JPH_ASSERT(body->GetID() == inBodyID); + JPH_ASSERT(!body->IsActive()); + JPH_ASSERT(!body->IsInBroadPhase()); + + // Push the id onto the freelist + mBodies[idx] = (Body *)mBodyIDFreeListStart; + mBodyIDFreeListStart = (uintptr_t(idx) << cFreedBodyIndexShift) | cIsFreedBody; + + return body; +} + +#if defined(_DEBUG) && defined(JPH_ENABLE_ASSERTS) + +void BodyManager::ValidateFreeList() const +{ + // Check that the freelist is correct + size_t num_freed = 0; + for (uintptr_t start = mBodyIDFreeListStart; start != cBodyIDFreeListEnd; start = uintptr_t(mBodies[start >> cFreedBodyIndexShift])) + { + JPH_ASSERT(start & cIsFreedBody); + num_freed++; + } + JPH_ASSERT(mNumBodies == mBodies.size() - num_freed); +} + +#endif // defined(_DEBUG) && _defined(JPH_ENABLE_ASSERTS) + +void BodyManager::RemoveBodies(const BodyID *inBodyIDs, int inNumber, Body **outBodies) +{ + // Don't take lock if no bodies are to be destroyed + if (inNumber <= 0) + return; + + UniqueLock lock(mBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::BodiesList)); + + // Update cached number of bodies + JPH_ASSERT(mNumBodies >= (uint)inNumber); + mNumBodies -= inNumber; + + for (const BodyID *b = inBodyIDs, *b_end = inBodyIDs + inNumber; b < b_end; b++) + { + // Remove body + Body *body = RemoveBodyInternal(*b); + + // Clear the ID + body->mID = BodyID(); + + // Return the body to the caller + if (outBodies != nullptr) + { + *outBodies = body; + ++outBodies; + } + } + +#if defined(_DEBUG) && defined(JPH_ENABLE_ASSERTS) + ValidateFreeList(); +#endif // defined(_DEBUG) && _defined(JPH_ENABLE_ASSERTS) +} + +void BodyManager::DestroyBodies(const BodyID *inBodyIDs, int inNumber) +{ + // Don't take lock if no bodies are to be destroyed + if (inNumber <= 0) + return; + + UniqueLock lock(mBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::BodiesList)); + + // Update cached number of bodies + JPH_ASSERT(mNumBodies >= (uint)inNumber); + mNumBodies -= inNumber; + + for (const BodyID *b = inBodyIDs, *b_end = inBodyIDs + inNumber; b < b_end; b++) + { + // Remove body + Body *body = RemoveBodyInternal(*b); + + // Free the body + sDeleteBody(body); + } + +#if defined(_DEBUG) && defined(JPH_ENABLE_ASSERTS) + ValidateFreeList(); +#endif // defined(_DEBUG) && _defined(JPH_ENABLE_ASSERTS) +} + +void BodyManager::AddBodyToActiveBodies(Body &ioBody) +{ + // Select the correct array to use + int type = (int)ioBody.GetBodyType(); + atomic &num_active_bodies = mNumActiveBodies[type]; + BodyID *active_bodies = mActiveBodies[type]; + + MotionProperties *mp = ioBody.mMotionProperties; + mp->mIndexInActiveBodies = num_active_bodies; + JPH_ASSERT(num_active_bodies < GetMaxBodies()); + active_bodies[num_active_bodies] = ioBody.GetID(); + num_active_bodies++; // Increment atomic after setting the body ID so that PhysicsSystem::JobFindCollisions (which doesn't lock the mActiveBodiesMutex) will only read valid IDs + + // Count CCD bodies + if (mp->GetMotionQuality() == EMotionQuality::LinearCast) + mNumActiveCCDBodies++; +} + +void BodyManager::RemoveBodyFromActiveBodies(Body &ioBody) +{ + // Select the correct array to use + int type = (int)ioBody.GetBodyType(); + atomic &num_active_bodies = mNumActiveBodies[type]; + BodyID *active_bodies = mActiveBodies[type]; + + uint32 last_body_index = num_active_bodies - 1; + MotionProperties *mp = ioBody.mMotionProperties; + if (mp->mIndexInActiveBodies != last_body_index) + { + // This is not the last body, use the last body to fill the hole + BodyID last_body_id = active_bodies[last_body_index]; + active_bodies[mp->mIndexInActiveBodies] = last_body_id; + + // Update that body's index in the active list + Body &last_body = *mBodies[last_body_id.GetIndex()]; + JPH_ASSERT(last_body.mMotionProperties->mIndexInActiveBodies == last_body_index); + last_body.mMotionProperties->mIndexInActiveBodies = mp->mIndexInActiveBodies; + } + + // Mark this body as no longer active + mp->mIndexInActiveBodies = Body::cInactiveIndex; + + // Remove unused element from active bodies list + --num_active_bodies; + + // Count CCD bodies + if (mp->GetMotionQuality() == EMotionQuality::LinearCast) + mNumActiveCCDBodies--; +} + +void BodyManager::ActivateBodies(const BodyID *inBodyIDs, int inNumber) +{ + // Don't take lock if no bodies are to be activated + if (inNumber <= 0) + return; + + UniqueLock lock(mActiveBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::ActiveBodiesList)); + + JPH_ASSERT(!mActiveBodiesLocked || sOverrideAllowActivation); + + for (const BodyID *b = inBodyIDs, *b_end = inBodyIDs + inNumber; b < b_end; b++) + if (!b->IsInvalid()) + { + BodyID body_id = *b; + Body &body = *mBodies[body_id.GetIndex()]; + + JPH_ASSERT(body.GetID() == body_id); + JPH_ASSERT(body.IsInBroadPhase()); + + if (!body.IsStatic() + && body.mMotionProperties->mIndexInActiveBodies == Body::cInactiveIndex) + { + // Reset sleeping + body.ResetSleepTimer(); + + AddBodyToActiveBodies(body); + + // Call activation listener + if (mActivationListener != nullptr) + mActivationListener->OnBodyActivated(body_id, body.GetUserData()); + } + } +} + +void BodyManager::DeactivateBodies(const BodyID *inBodyIDs, int inNumber) +{ + // Don't take lock if no bodies are to be deactivated + if (inNumber <= 0) + return; + + UniqueLock lock(mActiveBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::ActiveBodiesList)); + + JPH_ASSERT(!mActiveBodiesLocked || sOverrideAllowDeactivation); + + for (const BodyID *b = inBodyIDs, *b_end = inBodyIDs + inNumber; b < b_end; b++) + if (!b->IsInvalid()) + { + BodyID body_id = *b; + Body &body = *mBodies[body_id.GetIndex()]; + + JPH_ASSERT(body.GetID() == body_id); + JPH_ASSERT(body.IsInBroadPhase()); + + if (body.mMotionProperties != nullptr + && body.mMotionProperties->mIndexInActiveBodies != Body::cInactiveIndex) + { + // Remove the body from the active bodies list + RemoveBodyFromActiveBodies(body); + + // Mark this body as no longer active + body.mMotionProperties->mIslandIndex = Body::cInactiveIndex; + + // Reset velocity + body.mMotionProperties->mLinearVelocity = Vec3::sZero(); + body.mMotionProperties->mAngularVelocity = Vec3::sZero(); + + // Call activation listener + if (mActivationListener != nullptr) + mActivationListener->OnBodyDeactivated(body_id, body.GetUserData()); + } + } +} + +void BodyManager::SetMotionQuality(Body &ioBody, EMotionQuality inMotionQuality) +{ + MotionProperties *mp = ioBody.GetMotionPropertiesUnchecked(); + if (mp != nullptr && mp->GetMotionQuality() != inMotionQuality) + { + UniqueLock lock(mActiveBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::ActiveBodiesList)); + + JPH_ASSERT(!mActiveBodiesLocked); + + bool is_active = ioBody.IsActive(); + if (is_active && mp->GetMotionQuality() == EMotionQuality::LinearCast) + --mNumActiveCCDBodies; + + mp->mMotionQuality = inMotionQuality; + + if (is_active && mp->GetMotionQuality() == EMotionQuality::LinearCast) + ++mNumActiveCCDBodies; + } +} + +void BodyManager::GetActiveBodies(EBodyType inType, BodyIDVector &outBodyIDs) const +{ + JPH_PROFILE_FUNCTION(); + + UniqueLock lock(mActiveBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::ActiveBodiesList)); + + const BodyID *active_bodies = mActiveBodies[(int)inType]; + outBodyIDs.assign(active_bodies, active_bodies + mNumActiveBodies[(int)inType]); +} + +void BodyManager::GetBodyIDs(BodyIDVector &outBodies) const +{ + JPH_PROFILE_FUNCTION(); + + UniqueLock lock(mBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::BodiesList)); + + // Reserve space for all bodies + outBodies.clear(); + outBodies.reserve(mNumBodies); + + // Iterate the list and find the bodies that are not null + for (const Body *b : mBodies) + if (sIsValidBodyPointer(b)) + outBodies.push_back(b->GetID()); + + // Validate that our reservation was correct + JPH_ASSERT(outBodies.size() == mNumBodies); +} + +void BodyManager::SetBodyActivationListener(BodyActivationListener *inListener) +{ + UniqueLock lock(mActiveBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::ActiveBodiesList)); + + mActivationListener = inListener; +} + +BodyManager::MutexMask BodyManager::GetMutexMask(const BodyID *inBodies, int inNumber) const +{ + JPH_ASSERT(sizeof(MutexMask) * 8 >= mBodyMutexes.GetNumMutexes(), "MutexMask must have enough bits"); + + if (inNumber >= (int)mBodyMutexes.GetNumMutexes()) + { + // Just lock everything if there are too many bodies + return GetAllBodiesMutexMask(); + } + else + { + MutexMask mask = 0; + for (const BodyID *b = inBodies, *b_end = inBodies + inNumber; b < b_end; ++b) + if (!b->IsInvalid()) + { + uint32 index = mBodyMutexes.GetMutexIndex(b->GetIndex()); + mask |= (MutexMask(1) << index); + } + return mask; + } +} + +void BodyManager::LockRead(MutexMask inMutexMask) const +{ + JPH_IF_ENABLE_ASSERTS(PhysicsLock::sCheckLock(this, EPhysicsLockTypes::PerBody)); + + int index = 0; + for (MutexMask mask = inMutexMask; mask != 0; mask >>= 1, index++) + if (mask & 1) + mBodyMutexes.GetMutexByIndex(index).lock_shared(); +} + +void BodyManager::UnlockRead(MutexMask inMutexMask) const +{ + JPH_IF_ENABLE_ASSERTS(PhysicsLock::sCheckUnlock(this, EPhysicsLockTypes::PerBody)); + + int index = 0; + for (MutexMask mask = inMutexMask; mask != 0; mask >>= 1, index++) + if (mask & 1) + mBodyMutexes.GetMutexByIndex(index).unlock_shared(); +} + +void BodyManager::LockWrite(MutexMask inMutexMask) const +{ + JPH_IF_ENABLE_ASSERTS(PhysicsLock::sCheckLock(this, EPhysicsLockTypes::PerBody)); + + int index = 0; + for (MutexMask mask = inMutexMask; mask != 0; mask >>= 1, index++) + if (mask & 1) + mBodyMutexes.GetMutexByIndex(index).lock(); +} + +void BodyManager::UnlockWrite(MutexMask inMutexMask) const +{ + JPH_IF_ENABLE_ASSERTS(PhysicsLock::sCheckUnlock(this, EPhysicsLockTypes::PerBody)); + + int index = 0; + for (MutexMask mask = inMutexMask; mask != 0; mask >>= 1, index++) + if (mask & 1) + mBodyMutexes.GetMutexByIndex(index).unlock(); +} + +void BodyManager::LockAllBodies() const +{ + JPH_IF_ENABLE_ASSERTS(PhysicsLock::sCheckLock(this, EPhysicsLockTypes::PerBody)); + mBodyMutexes.LockAll(); + + PhysicsLock::sLock(mBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::BodiesList)); +} + +void BodyManager::UnlockAllBodies() const +{ + PhysicsLock::sUnlock(mBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::BodiesList)); + + JPH_IF_ENABLE_ASSERTS(PhysicsLock::sCheckUnlock(this, EPhysicsLockTypes::PerBody)); + mBodyMutexes.UnlockAll(); +} + +void BodyManager::SaveState(StateRecorder &inStream, const StateRecorderFilter *inFilter) const +{ + { + LockAllBodies(); + + // Determine which bodies to save + Array bodies; + bodies.reserve(mNumBodies); + for (const Body *b : mBodies) + if (sIsValidBodyPointer(b) && b->IsInBroadPhase() && (inFilter == nullptr || inFilter->ShouldSaveBody(*b))) + bodies.push_back(b); + + // Write state of bodies + uint32 num_bodies = (uint32)bodies.size(); + inStream.Write(num_bodies); + for (const Body *b : bodies) + { + inStream.Write(b->GetID()); + inStream.Write(b->IsActive()); + b->SaveState(inStream); + } + + UnlockAllBodies(); + } +} + +bool BodyManager::RestoreState(StateRecorder &inStream) +{ + BodyIDVector bodies_to_activate, bodies_to_deactivate; + + { + LockAllBodies(); + + if (inStream.IsValidating()) + { + // Read state of bodies, note this reads it in a way to be consistent with validation + uint32 old_num_bodies = 0; + for (const Body *b : mBodies) + if (sIsValidBodyPointer(b) && b->IsInBroadPhase()) + ++old_num_bodies; + uint32 num_bodies = old_num_bodies; // Initialize to current value for validation + inStream.Read(num_bodies); + if (num_bodies != old_num_bodies) + { + JPH_ASSERT(false, "Cannot handle adding/removing bodies"); + UnlockAllBodies(); + return false; + } + + for (Body *b : mBodies) + if (sIsValidBodyPointer(b) && b->IsInBroadPhase()) + { + BodyID body_id = b->GetID(); // Initialize to current value for validation + inStream.Read(body_id); + if (body_id != b->GetID()) + { + JPH_ASSERT(false, "Cannot handle adding/removing bodies"); + UnlockAllBodies(); + return false; + } + bool is_active = b->IsActive(); // Initialize to current value for validation + inStream.Read(is_active); + if (is_active != b->IsActive()) + { + if (is_active) + bodies_to_activate.push_back(body_id); + else + bodies_to_deactivate.push_back(body_id); + } + b->RestoreState(inStream); + } + } + else + { + // Not validating, we can be a bit more loose, read number of bodies + uint32 num_bodies = 0; + inStream.Read(num_bodies); + + // Iterate over the stored bodies and restore their state + for (uint32 idx = 0; idx < num_bodies; ++idx) + { + BodyID body_id; + inStream.Read(body_id); + Body *b = TryGetBody(body_id); + if (b == nullptr) + { + JPH_ASSERT(false, "Restoring state for non-existing body"); + UnlockAllBodies(); + return false; + } + bool is_active; + inStream.Read(is_active); + if (is_active != b->IsActive()) + { + if (is_active) + bodies_to_activate.push_back(body_id); + else + bodies_to_deactivate.push_back(body_id); + } + b->RestoreState(inStream); + } + } + + UnlockAllBodies(); + } + + { + UniqueLock lock(mActiveBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::ActiveBodiesList)); + + for (BodyID body_id : bodies_to_activate) + { + Body *body = TryGetBody(body_id); + AddBodyToActiveBodies(*body); + } + + for (BodyID body_id : bodies_to_deactivate) + { + Body *body = TryGetBody(body_id); + RemoveBodyFromActiveBodies(*body); + } + } + + return true; +} + +void BodyManager::SaveBodyState(const Body &inBody, StateRecorder &inStream) const +{ + inStream.Write(inBody.IsActive()); + + inBody.SaveState(inStream); +} + +void BodyManager::RestoreBodyState(Body &ioBody, StateRecorder &inStream) +{ + bool is_active = ioBody.IsActive(); + inStream.Read(is_active); + + ioBody.RestoreState(inStream); + + if (is_active != ioBody.IsActive()) + { + UniqueLock lock(mActiveBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::ActiveBodiesList)); + + JPH_ASSERT(!mActiveBodiesLocked || sOverrideAllowActivation); + + if (is_active) + AddBodyToActiveBodies(ioBody); + else + RemoveBodyFromActiveBodies(ioBody); + } +} + +#ifdef JPH_DEBUG_RENDERER +void BodyManager::Draw(const DrawSettings &inDrawSettings, const PhysicsSettings &inPhysicsSettings, DebugRenderer *inRenderer, const BodyDrawFilter *inBodyFilter) +{ + JPH_PROFILE_FUNCTION(); + + LockAllBodies(); + + for (const Body *body : mBodies) + if (sIsValidBodyPointer(body) && body->IsInBroadPhase() && (!inBodyFilter || inBodyFilter->ShouldDraw(*body))) + { + JPH_ASSERT(mBodies[body->GetID().GetIndex()] == body); + + bool is_sensor = body->IsSensor(); + + // Determine drawing mode + Color color; + if (is_sensor) + color = Color::sYellow; + else + switch (inDrawSettings.mDrawShapeColor) + { + case EShapeColor::InstanceColor: + // Each instance has own color + color = Color::sGetDistinctColor(body->mID.GetIndex()); + break; + + case EShapeColor::ShapeTypeColor: + color = ShapeFunctions::sGet(body->GetShape()->GetSubType()).mColor; + break; + + case EShapeColor::MotionTypeColor: + // Determine color based on motion type + switch (body->mMotionType) + { + case EMotionType::Static: + color = Color::sGrey; + break; + + case EMotionType::Kinematic: + color = Color::sGreen; + break; + + case EMotionType::Dynamic: + color = Color::sGetDistinctColor(body->mID.GetIndex()); + break; + + default: + JPH_ASSERT(false); + color = Color::sBlack; + break; + } + break; + + case EShapeColor::SleepColor: + // Determine color based on motion type + switch (body->mMotionType) + { + case EMotionType::Static: + color = Color::sGrey; + break; + + case EMotionType::Kinematic: + color = body->IsActive()? Color::sGreen : Color::sRed; + break; + + case EMotionType::Dynamic: + color = body->IsActive()? Color::sYellow : Color::sRed; + break; + + default: + JPH_ASSERT(false); + color = Color::sBlack; + break; + } + break; + + case EShapeColor::IslandColor: + // Determine color based on motion type + switch (body->mMotionType) + { + case EMotionType::Static: + color = Color::sGrey; + break; + + case EMotionType::Kinematic: + case EMotionType::Dynamic: + { + uint32 idx = body->GetMotionProperties()->GetIslandIndexInternal(); + color = idx != Body::cInactiveIndex? Color::sGetDistinctColor(idx) : Color::sLightGrey; + } + break; + + default: + JPH_ASSERT(false); + color = Color::sBlack; + break; + } + break; + + case EShapeColor::MaterialColor: + color = Color::sWhite; + break; + + default: + JPH_ASSERT(false); + color = Color::sBlack; + break; + } + + // Draw the results of GetSupportFunction + if (inDrawSettings.mDrawGetSupportFunction) + body->mShape->DrawGetSupportFunction(inRenderer, body->GetCenterOfMassTransform(), Vec3::sReplicate(1.0f), color, inDrawSettings.mDrawSupportDirection); + + // Draw the results of GetSupportingFace + if (inDrawSettings.mDrawGetSupportingFace) + body->mShape->DrawGetSupportingFace(inRenderer, body->GetCenterOfMassTransform(), Vec3::sReplicate(1.0f)); + + // Draw the shape + if (inDrawSettings.mDrawShape) + body->mShape->Draw(inRenderer, body->GetCenterOfMassTransform(), Vec3::sReplicate(1.0f), color, inDrawSettings.mDrawShapeColor == EShapeColor::MaterialColor, inDrawSettings.mDrawShapeWireframe || is_sensor); + + // Draw bounding box + if (inDrawSettings.mDrawBoundingBox) + inRenderer->DrawWireBox(body->mBounds, color); + + // Draw center of mass transform + if (inDrawSettings.mDrawCenterOfMassTransform) + inRenderer->DrawCoordinateSystem(body->GetCenterOfMassTransform(), 0.2f); + + // Draw world transform + if (inDrawSettings.mDrawWorldTransform) + inRenderer->DrawCoordinateSystem(body->GetWorldTransform(), 0.2f); + + // Draw world space linear and angular velocity + if (inDrawSettings.mDrawVelocity) + { + RVec3 pos = body->GetCenterOfMassPosition(); + inRenderer->DrawArrow(pos, pos + body->GetLinearVelocity(), Color::sGreen, 0.1f); + inRenderer->DrawArrow(pos, pos + body->GetAngularVelocity(), Color::sRed, 0.1f); + } + + if (inDrawSettings.mDrawMassAndInertia && body->IsDynamic()) + { + const MotionProperties *mp = body->GetMotionProperties(); + if (mp->GetInverseMass() > 0.0f + && !Vec3::sEquals(mp->GetInverseInertiaDiagonal(), Vec3::sZero()).TestAnyXYZTrue()) + { + // Invert mass again + float mass = 1.0f / mp->GetInverseMass(); + + // Invert diagonal again + Vec3 diagonal = mp->GetInverseInertiaDiagonal().Reciprocal(); + + // Determine how big of a box has the equivalent inertia + Vec3 box_size = MassProperties::sGetEquivalentSolidBoxSize(mass, diagonal); + + // Draw box with equivalent inertia + inRenderer->DrawWireBox(body->GetCenterOfMassTransform() * Mat44::sRotation(mp->GetInertiaRotation()), AABox(-0.5f * box_size, 0.5f * box_size), Color::sOrange); + + // Draw mass + inRenderer->DrawText3D(body->GetCenterOfMassPosition(), StringFormat("%.2f", (double)mass), Color::sOrange, 0.2f); + } + } + + if (inDrawSettings.mDrawSleepStats && body->IsDynamic() && body->IsActive()) + { + // Draw stats to know which bodies could go to sleep + String text = StringFormat("t: %.1f", (double)body->mMotionProperties->mSleepTestTimer); + uint8 g = uint8(Clamp(255.0f * body->mMotionProperties->mSleepTestTimer / inPhysicsSettings.mTimeBeforeSleep, 0.0f, 255.0f)); + Color sleep_color = Color(0, 255 - g, g); + inRenderer->DrawText3D(body->GetCenterOfMassPosition(), text, sleep_color, 0.2f); + for (int i = 0; i < 3; ++i) + inRenderer->DrawWireSphere(JPH_IF_DOUBLE_PRECISION(body->mMotionProperties->GetSleepTestOffset() +) body->mMotionProperties->mSleepTestSpheres[i].GetCenter(), body->mMotionProperties->mSleepTestSpheres[i].GetRadius(), sleep_color); + } + + if (body->IsSoftBody()) + { + const SoftBodyMotionProperties *mp = static_cast(body->GetMotionProperties()); + RMat44 com = body->GetCenterOfMassTransform(); + + if (inDrawSettings.mDrawSoftBodyVertices) + mp->DrawVertices(inRenderer, com); + + if (inDrawSettings.mDrawSoftBodyVertexVelocities) + mp->DrawVertexVelocities(inRenderer, com); + + if (inDrawSettings.mDrawSoftBodyEdgeConstraints) + mp->DrawEdgeConstraints(inRenderer, com); + + if (inDrawSettings.mDrawSoftBodyBendConstraints) + mp->DrawBendConstraints(inRenderer, com); + + if (inDrawSettings.mDrawSoftBodyVolumeConstraints) + mp->DrawVolumeConstraints(inRenderer, com); + + if (inDrawSettings.mDrawSoftBodySkinConstraints) + mp->DrawSkinConstraints(inRenderer, com); + + if (inDrawSettings.mDrawSoftBodyLRAConstraints) + mp->DrawLRAConstraints(inRenderer, com); + + if (inDrawSettings.mDrawSoftBodyPredictedBounds) + mp->DrawPredictedBounds(inRenderer, com); + } + } + + UnlockAllBodies(); +} +#endif // JPH_DEBUG_RENDERER + +void BodyManager::InvalidateContactCacheForBody(Body &ioBody) +{ + // If this is the first time we flip the collision cache invalid flag, we need to add it to an internal list to ensure we reset the flag at the end of the physics update + if (ioBody.InvalidateContactCacheInternal()) + { + lock_guard lock(mBodiesCacheInvalidMutex); + mBodiesCacheInvalid.push_back(ioBody.GetID()); + } +} + +void BodyManager::ValidateContactCacheForAllBodies() +{ + lock_guard lock(mBodiesCacheInvalidMutex); + + for (const BodyID &b : mBodiesCacheInvalid) + { + // The body may have been removed between the call to InvalidateContactCacheForBody and this call, so check if it still exists + Body *body = TryGetBody(b); + if (body != nullptr) + body->ValidateContactCacheInternal(); + } + mBodiesCacheInvalid.clear(); +} + +#ifdef _DEBUG +void BodyManager::ValidateActiveBodyBounds() +{ + UniqueLock lock(mActiveBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::ActiveBodiesList)); + + for (uint type = 0; type < cBodyTypeCount; ++type) + for (BodyID *id = mActiveBodies[type], *id_end = mActiveBodies[type] + mNumActiveBodies[type]; id < id_end; ++id) + { + const Body *body = mBodies[id->GetIndex()]; + AABox cached = body->GetWorldSpaceBounds(); + AABox calculated = body->GetShape()->GetWorldSpaceBounds(body->GetCenterOfMassTransform(), Vec3::sReplicate(1.0f)); + JPH_ASSERT(cached == calculated); + } +} +#endif // _DEBUG + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyManager.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyManager.h new file mode 100644 index 00000000000..a1b859679c9 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyManager.h @@ -0,0 +1,364 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +// Classes +class BodyCreationSettings; +class SoftBodyCreationSettings; +class BodyActivationListener; +class StateRecorderFilter; +struct PhysicsSettings; +#ifdef JPH_DEBUG_RENDERER +class DebugRenderer; +class BodyDrawFilter; +#endif // JPH_DEBUG_RENDERER + +/// Array of bodies +using BodyVector = Array; + +/// Array of body ID's +using BodyIDVector = Array; + +/// Class that contains all bodies +class JPH_EXPORT BodyManager : public NonCopyable +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Destructor + ~BodyManager(); + + /// Initialize the manager + void Init(uint inMaxBodies, uint inNumBodyMutexes, const BroadPhaseLayerInterface &inLayerInterface); + + /// Gets the current amount of bodies that are in the body manager + uint GetNumBodies() const; + + /// Gets the max bodies that we can support + uint GetMaxBodies() const { return uint(mBodies.capacity()); } + + /// Helper struct that counts the number of bodies of each type + struct BodyStats + { + uint mNumBodies = 0; ///< Total number of bodies in the body manager + uint mMaxBodies = 0; ///< Max allowed number of bodies in the body manager (as configured in Init(...)) + + uint mNumBodiesStatic = 0; ///< Number of static bodies + + uint mNumBodiesDynamic = 0; ///< Number of dynamic bodies + uint mNumActiveBodiesDynamic = 0; ///< Number of dynamic bodies that are currently active + + uint mNumBodiesKinematic = 0; ///< Number of kinematic bodies + uint mNumActiveBodiesKinematic = 0; ///< Number of kinematic bodies that are currently active + + uint mNumSoftBodies = 0; ///< Number of soft bodies + uint mNumActiveSoftBodies = 0; ///< Number of soft bodies that are currently active + }; + + /// Get stats about the bodies in the body manager (slow, iterates through all bodies) + BodyStats GetBodyStats() const; + + /// Create a body using creation settings. The returned body will not be part of the body manager yet. + Body * AllocateBody(const BodyCreationSettings &inBodyCreationSettings) const; + + /// Create a soft body using creation settings. The returned body will not be part of the body manager yet. + Body * AllocateSoftBody(const SoftBodyCreationSettings &inSoftBodyCreationSettings) const; + + /// Free a body that has not been added to the body manager yet (if it has, use DestroyBodies). + void FreeBody(Body *inBody) const; + + /// Add a body to the body manager, assigning it the next available ID. Returns false if no more IDs are available. + bool AddBody(Body *ioBody); + + /// Add a body to the body manager, assigning it a custom ID. Returns false if the ID is not valid. + bool AddBodyWithCustomID(Body *ioBody, const BodyID &inBodyID); + + /// Remove a list of bodies from the body manager + void RemoveBodies(const BodyID *inBodyIDs, int inNumber, Body **outBodies); + + /// Remove a set of bodies from the body manager and frees them. + void DestroyBodies(const BodyID *inBodyIDs, int inNumber); + + /// Activate a list of bodies. + /// This function should only be called when an exclusive lock for the bodies are held. + void ActivateBodies(const BodyID *inBodyIDs, int inNumber); + + /// Deactivate a list of bodies. + /// This function should only be called when an exclusive lock for the bodies are held. + void DeactivateBodies(const BodyID *inBodyIDs, int inNumber); + + /// Update the motion quality for a body + void SetMotionQuality(Body &ioBody, EMotionQuality inMotionQuality); + + /// Get copy of the list of active bodies under protection of a lock. + void GetActiveBodies(EBodyType inType, BodyIDVector &outBodyIDs) const; + + /// Get the list of active bodies. Note: Not thread safe. The active bodies list can change at any moment. + const BodyID * GetActiveBodiesUnsafe(EBodyType inType) const { return mActiveBodies[int(inType)]; } + + /// Get the number of active bodies. + uint32 GetNumActiveBodies(EBodyType inType) const { return mNumActiveBodies[int(inType)]; } + + /// Get the number of active bodies that are using continuous collision detection + uint32 GetNumActiveCCDBodies() const { return mNumActiveCCDBodies; } + + /// Listener that is notified whenever a body is activated/deactivated + void SetBodyActivationListener(BodyActivationListener *inListener); + BodyActivationListener * GetBodyActivationListener() const { return mActivationListener; } + + /// Check if this is a valid body pointer. When a body is freed the memory that the pointer occupies is reused to store a freelist. + static inline bool sIsValidBodyPointer(const Body *inBody) { return (uintptr_t(inBody) & cIsFreedBody) == 0; } + + /// Get all bodies. Note that this can contain invalid body pointers, call sIsValidBodyPointer to check. + const BodyVector & GetBodies() const { return mBodies; } + + /// Get all bodies. Note that this can contain invalid body pointers, call sIsValidBodyPointer to check. + BodyVector & GetBodies() { return mBodies; } + + /// Get all body IDs under the protection of a lock + void GetBodyIDs(BodyIDVector &outBodies) const; + + /// Access a body (not protected by lock) + const Body & GetBody(const BodyID &inID) const { return *mBodies[inID.GetIndex()]; } + + /// Access a body (not protected by lock) + Body & GetBody(const BodyID &inID) { return *mBodies[inID.GetIndex()]; } + + /// Access a body, will return a nullptr if the body ID is no longer valid (not protected by lock) + const Body * TryGetBody(const BodyID &inID) const + { + uint32 idx = inID.GetIndex(); + if (idx >= mBodies.size()) + return nullptr; + + const Body *body = mBodies[idx]; + if (sIsValidBodyPointer(body) && body->GetID() == inID) + return body; + + return nullptr; + } + + /// Access a body, will return a nullptr if the body ID is no longer valid (not protected by lock) + Body * TryGetBody(const BodyID &inID) + { + uint32 idx = inID.GetIndex(); + if (idx >= mBodies.size()) + return nullptr; + + Body *body = mBodies[idx]; + if (sIsValidBodyPointer(body) && body->GetID() == inID) + return body; + + return nullptr; + } + + /// Access the mutex for a single body + SharedMutex & GetMutexForBody(const BodyID &inID) const { return mBodyMutexes.GetMutexByObjectIndex(inID.GetIndex()); } + + /// Bodies are protected using an array of mutexes (so a fixed number, not 1 per body). Each bit in this mask indicates a locked mutex. + using MutexMask = uint64; + + ///@name Batch body mutex access (do not use directly) + ///@{ + MutexMask GetAllBodiesMutexMask() const { return mBodyMutexes.GetNumMutexes() == sizeof(MutexMask) * 8? ~MutexMask(0) : (MutexMask(1) << mBodyMutexes.GetNumMutexes()) - 1; } + MutexMask GetMutexMask(const BodyID *inBodies, int inNumber) const; + void LockRead(MutexMask inMutexMask) const; + void UnlockRead(MutexMask inMutexMask) const; + void LockWrite(MutexMask inMutexMask) const; + void UnlockWrite(MutexMask inMutexMask) const; + ///@} + + /// Lock all bodies. This should only be done during PhysicsSystem::Update(). + void LockAllBodies() const; + + /// Unlock all bodies. This should only be done during PhysicsSystem::Update(). + void UnlockAllBodies() const; + + /// Function to update body's layer (should only be called by the BodyInterface since it also requires updating the broadphase) + inline void SetBodyObjectLayerInternal(Body &ioBody, ObjectLayer inLayer) const { ioBody.mObjectLayer = inLayer; ioBody.mBroadPhaseLayer = mBroadPhaseLayerInterface->GetBroadPhaseLayer(inLayer); } + + /// Set the Body::EFlags::InvalidateContactCache flag for the specified body. This means that the collision cache is invalid for any body pair involving that body until the next physics step. + void InvalidateContactCacheForBody(Body &ioBody); + + /// Reset the Body::EFlags::InvalidateContactCache flag for all bodies. All contact pairs in the contact cache will now by valid again. + void ValidateContactCacheForAllBodies(); + + /// Saving state for replay + void SaveState(StateRecorder &inStream, const StateRecorderFilter *inFilter) const; + + /// Restoring state for replay. Returns false if failed. + bool RestoreState(StateRecorder &inStream); + + /// Save the state of a single body for replay + void SaveBodyState(const Body &inBody, StateRecorder &inStream) const; + + /// Save the state of a single body for replay + void RestoreBodyState(Body &inBody, StateRecorder &inStream); + + enum class EShapeColor + { + InstanceColor, ///< Random color per instance + ShapeTypeColor, ///< Convex = green, scaled = yellow, compound = orange, mesh = red + MotionTypeColor, ///< Static = grey, keyframed = green, dynamic = random color per instance + SleepColor, ///< Static = grey, keyframed = green, dynamic = yellow, sleeping = red + IslandColor, ///< Static = grey, active = random color per island, sleeping = light grey + MaterialColor, ///< Color as defined by the PhysicsMaterial of the shape + }; + +#ifdef JPH_DEBUG_RENDERER + /// Draw settings + struct DrawSettings + { + bool mDrawGetSupportFunction = false; ///< Draw the GetSupport() function, used for convex collision detection + bool mDrawSupportDirection = false; ///< When drawing the support function, also draw which direction mapped to a specific support point + bool mDrawGetSupportingFace = false; ///< Draw the faces that were found colliding during collision detection + bool mDrawShape = true; ///< Draw the shapes of all bodies + bool mDrawShapeWireframe = false; ///< When mDrawShape is true and this is true, the shapes will be drawn in wireframe instead of solid. + EShapeColor mDrawShapeColor = EShapeColor::MotionTypeColor; ///< Coloring scheme to use for shapes + bool mDrawBoundingBox = false; ///< Draw a bounding box per body + bool mDrawCenterOfMassTransform = false; ///< Draw the center of mass for each body + bool mDrawWorldTransform = false; ///< Draw the world transform (which can be different than the center of mass) for each body + bool mDrawVelocity = false; ///< Draw the velocity vector for each body + bool mDrawMassAndInertia = false; ///< Draw the mass and inertia (as the box equivalent) for each body + bool mDrawSleepStats = false; ///< Draw stats regarding the sleeping algorithm of each body + bool mDrawSoftBodyVertices = false; ///< Draw the vertices of soft bodies + bool mDrawSoftBodyVertexVelocities = false; ///< Draw the velocities of the vertices of soft bodies + bool mDrawSoftBodyEdgeConstraints = false; ///< Draw the edge constraints of soft bodies + bool mDrawSoftBodyBendConstraints = false; ///< Draw the bend constraints of soft bodies + bool mDrawSoftBodyVolumeConstraints = false; ///< Draw the volume constraints of soft bodies + bool mDrawSoftBodySkinConstraints = false; ///< Draw the skin constraints of soft bodies + bool mDrawSoftBodyLRAConstraints = false; ///< Draw the LRA constraints of soft bodies + bool mDrawSoftBodyPredictedBounds = false; ///< Draw the predicted bounds of soft bodies + }; + + /// Draw the state of the bodies (debugging purposes) + void Draw(const DrawSettings &inSettings, const PhysicsSettings &inPhysicsSettings, DebugRenderer *inRenderer, const BodyDrawFilter *inBodyFilter = nullptr); +#endif // JPH_DEBUG_RENDERER + +#ifdef JPH_ENABLE_ASSERTS + /// Lock the active body list, asserts when Activate/DeactivateBody is called. + void SetActiveBodiesLocked(bool inLocked) { mActiveBodiesLocked = inLocked; } + + /// Per thread override of the locked state, to be used by the PhysicsSystem only! + class GrantActiveBodiesAccess + { + public: + inline GrantActiveBodiesAccess(bool inAllowActivation, bool inAllowDeactivation) + { + JPH_ASSERT(!sGetOverrideAllowActivation()); + sSetOverrideAllowActivation(inAllowActivation); + + JPH_ASSERT(!sGetOverrideAllowDeactivation()); + sSetOverrideAllowDeactivation(inAllowDeactivation); + } + + inline ~GrantActiveBodiesAccess() + { + sSetOverrideAllowActivation(false); + sSetOverrideAllowDeactivation(false); + } + }; +#endif + +#ifdef _DEBUG + /// Validate if the cached bounding boxes are correct for all active bodies + void ValidateActiveBodyBounds(); +#endif // _DEBUG + +private: + /// Increment and get the sequence number of the body +#ifdef JPH_COMPILER_CLANG + __attribute__((no_sanitize("implicit-conversion"))) // We intentionally overflow the uint8 sequence number +#endif + inline uint8 GetNextSequenceNumber(int inBodyIndex) { return ++mBodySequenceNumbers[inBodyIndex]; } + + /// Add a single body to mActiveBodies, note doesn't lock the active body mutex! + inline void AddBodyToActiveBodies(Body &ioBody); + + /// Remove a single body from mActiveBodies, note doesn't lock the active body mutex! + inline void RemoveBodyFromActiveBodies(Body &ioBody); + + /// Helper function to remove a body from the manager + JPH_INLINE Body * RemoveBodyInternal(const BodyID &inBodyID); + + /// Helper function to delete a body (which could actually be a BodyWithMotionProperties) + inline static void sDeleteBody(Body *inBody); + +#if defined(_DEBUG) && defined(JPH_ENABLE_ASSERTS) + /// Function to check that the free list is not corrupted + void ValidateFreeList() const; +#endif // defined(_DEBUG) && _defined(JPH_ENABLE_ASSERTS) + + /// List of pointers to all bodies. Contains invalid pointers for deleted bodies, check with sIsValidBodyPointer. Note that this array is reserved to the max bodies that is passed in the Init function so that adding bodies will not reallocate the array. + BodyVector mBodies; + + /// Current number of allocated bodies + uint mNumBodies = 0; + + /// Indicates that there are no more freed body IDs + static constexpr uintptr_t cBodyIDFreeListEnd = ~uintptr_t(0); + + /// Bit that indicates a pointer in mBodies is actually the index of the next freed body. We use the lowest bit because we know that Bodies need to be 16 byte aligned so addresses can never end in a 1 bit. + static constexpr uintptr_t cIsFreedBody = uintptr_t(1); + + /// Amount of bits to shift to get an index to the next freed body + static constexpr uint cFreedBodyIndexShift = 1; + + /// Index of first entry in mBodies that is unused + uintptr_t mBodyIDFreeListStart = cBodyIDFreeListEnd; + + /// Protects mBodies array (but not the bodies it points to), mNumBodies and mBodyIDFreeListStart + mutable Mutex mBodiesMutex; + + /// An array of mutexes protecting the bodies in the mBodies array + using BodyMutexes = MutexArray; + mutable BodyMutexes mBodyMutexes; + + /// List of next sequence number for a body ID + Array mBodySequenceNumbers; + + /// Mutex that protects the mActiveBodies array + mutable Mutex mActiveBodiesMutex; + + /// List of all active dynamic bodies (size is equal to max amount of bodies) + BodyID * mActiveBodies[cBodyTypeCount] = { }; + + /// How many bodies there are in the list of active bodies + atomic mNumActiveBodies[cBodyTypeCount] = { }; + + /// How many of the active bodies have continuous collision detection enabled + uint32 mNumActiveCCDBodies = 0; + + /// Mutex that protects the mBodiesCacheInvalid array + mutable Mutex mBodiesCacheInvalidMutex; + + /// List of all bodies that should have their cache invalidated + BodyIDVector mBodiesCacheInvalid; + + /// Listener that is notified whenever a body is activated/deactivated + BodyActivationListener * mActivationListener = nullptr; + + /// Cached broadphase layer interface + const BroadPhaseLayerInterface *mBroadPhaseLayerInterface = nullptr; + +#ifdef JPH_ENABLE_ASSERTS + static bool sGetOverrideAllowActivation(); + static void sSetOverrideAllowActivation(bool inValue); + + static bool sGetOverrideAllowDeactivation(); + static void sSetOverrideAllowDeactivation(bool inValue); + + /// Debug system that tries to limit changes to active bodies during the PhysicsSystem::Update() + bool mActiveBodiesLocked = false; +#endif +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyPair.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyPair.h new file mode 100644 index 00000000000..8ac849a49a8 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyPair.h @@ -0,0 +1,36 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +JPH_NAMESPACE_BEGIN + +/// Structure that holds a body pair +struct alignas(uint64) BodyPair +{ + JPH_OVERRIDE_NEW_DELETE + + /// Constructor + BodyPair() = default; + BodyPair(BodyID inA, BodyID inB) : mBodyA(inA), mBodyB(inB) { } + + /// Equals operator + bool operator == (const BodyPair &inRHS) const { return *reinterpret_cast(this) == *reinterpret_cast(&inRHS); } + + /// Smaller than operator, used for consistently ordering body pairs + bool operator < (const BodyPair &inRHS) const { return *reinterpret_cast(this) < *reinterpret_cast(&inRHS); } + + /// Get the hash value of this object + uint64 GetHash() const { return Hash64(*reinterpret_cast(this)); } + + BodyID mBodyA; + BodyID mBodyB; +}; + +static_assert(sizeof(BodyPair) == sizeof(uint64), "Mismatch in class size"); + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyType.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyType.h new file mode 100644 index 00000000000..984af06ad23 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyType.h @@ -0,0 +1,19 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2023 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +JPH_NAMESPACE_BEGIN + +/// Type of body +enum class EBodyType : uint8 +{ + RigidBody, ///< Rigid body consisting of a rigid shape + SoftBody, ///< Soft body consisting of a deformable shape +}; + +/// How many types of bodies there are +static constexpr uint cBodyTypeCount = 2; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/MassProperties.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/MassProperties.cpp new file mode 100644 index 00000000000..91df6b0df3a --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/MassProperties.cpp @@ -0,0 +1,185 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(MassProperties) +{ + JPH_ADD_ATTRIBUTE(MassProperties, mMass) + JPH_ADD_ATTRIBUTE(MassProperties, mInertia) +} + +bool MassProperties::DecomposePrincipalMomentsOfInertia(Mat44 &outRotation, Vec3 &outDiagonal) const +{ + // Using eigendecomposition to get the principal components of the inertia tensor + // See: https://en.wikipedia.org/wiki/Eigendecomposition_of_a_matrix + Matrix<3, 3> inertia; + inertia.CopyPart(mInertia, 0, 0, 3, 3, 0, 0); + Matrix<3, 3> eigen_vec = Matrix<3, 3>::sIdentity(); + Vector<3> eigen_val; + if (!EigenValueSymmetric(inertia, eigen_vec, eigen_val)) + return false; + + // Sort so that the biggest value goes first + int indices[] = { 0, 1, 2 }; + InsertionSort(indices, indices + 3, [&eigen_val](int inLeft, int inRight) { return eigen_val[inLeft] > eigen_val[inRight]; }); + + // Convert to a regular Mat44 and Vec3 + outRotation = Mat44::sIdentity(); + for (int i = 0; i < 3; ++i) + { + outRotation.SetColumn3(i, Vec3(reinterpret_cast(eigen_vec.GetColumn(indices[i])))); + outDiagonal.SetComponent(i, eigen_val[indices[i]]); + } + + // Make sure that the rotation matrix is a right handed matrix + if (outRotation.GetAxisX().Cross(outRotation.GetAxisY()).Dot(outRotation.GetAxisZ()) < 0.0f) + outRotation.SetAxisZ(-outRotation.GetAxisZ()); + +#ifdef JPH_ENABLE_ASSERTS + // Validate that the solution is correct, for each axis we want to make sure that the difference in inertia is + // smaller than some fraction of the inertia itself in that axis + Mat44 new_inertia = outRotation * Mat44::sScale(outDiagonal) * outRotation.Inversed(); + for (int i = 0; i < 3; ++i) + JPH_ASSERT(new_inertia.GetColumn3(i).IsClose(mInertia.GetColumn3(i), mInertia.GetColumn3(i).LengthSq() * 1.0e-10f)); +#endif + + return true; +} + +void MassProperties::SetMassAndInertiaOfSolidBox(Vec3Arg inBoxSize, float inDensity) +{ + // Calculate mass + mMass = inBoxSize.GetX() * inBoxSize.GetY() * inBoxSize.GetZ() * inDensity; + + // Calculate inertia + Vec3 size_sq = inBoxSize * inBoxSize; + Vec3 scale = (size_sq.Swizzle() + size_sq.Swizzle()) * (mMass / 12.0f); + mInertia = Mat44::sScale(scale); +} + +void MassProperties::ScaleToMass(float inMass) +{ + if (mMass > 0.0f) + { + // Calculate how much we have to scale the inertia tensor + float mass_scale = inMass / mMass; + + // Update mass + mMass = inMass; + + // Update inertia tensor + for (int i = 0; i < 3; ++i) + mInertia.SetColumn4(i, mInertia.GetColumn4(i) * mass_scale); + } + else + { + // Just set the mass + mMass = inMass; + } +} + +Vec3 MassProperties::sGetEquivalentSolidBoxSize(float inMass, Vec3Arg inInertiaDiagonal) +{ + // Moment of inertia of a solid box has diagonal: + // mass / 12 * [size_y^2 + size_z^2, size_x^2 + size_z^2, size_x^2 + size_y^2] + // Solving for size_x, size_y and size_y (diagonal and mass are known): + Vec3 diagonal = inInertiaDiagonal * (12.0f / inMass); + return Vec3(sqrt(0.5f * (-diagonal[0] + diagonal[1] + diagonal[2])), sqrt(0.5f * (diagonal[0] - diagonal[1] + diagonal[2])), sqrt(0.5f * (diagonal[0] + diagonal[1] - diagonal[2]))); +} + +void MassProperties::Scale(Vec3Arg inScale) +{ + // See: https://en.wikipedia.org/wiki/Moment_of_inertia#Inertia_tensor + // The diagonal of the inertia tensor can be calculated like this: + // Ixx = sum_{k = 1 to n}(m_k * (y_k^2 + z_k^2)) + // Iyy = sum_{k = 1 to n}(m_k * (x_k^2 + z_k^2)) + // Izz = sum_{k = 1 to n}(m_k * (x_k^2 + y_k^2)) + // + // We want to isolate the terms x_k, y_k and z_k: + // d = [0.5, 0.5, 0.5].[Ixx, Iyy, Izz] + // [sum_{k = 1 to n}(m_k * x_k^2), sum_{k = 1 to n}(m_k * y_k^2), sum_{k = 1 to n}(m_k * z_k^2)] = [d, d, d] - [Ixx, Iyy, Izz] + Vec3 diagonal = mInertia.GetDiagonal3(); + Vec3 xyz_sq = Vec3::sReplicate(Vec3::sReplicate(0.5f).Dot(diagonal)) - diagonal; + + // When scaling a shape these terms change like this: + // sum_{k = 1 to n}(m_k * (scale_x * x_k)^2) = scale_x^2 * sum_{k = 1 to n}(m_k * x_k^2) + // Same for y_k and z_k + // Using these terms we can calculate the new diagonal of the inertia tensor: + Vec3 xyz_scaled_sq = inScale * inScale * xyz_sq; + float i_xx = xyz_scaled_sq.GetY() + xyz_scaled_sq.GetZ(); + float i_yy = xyz_scaled_sq.GetX() + xyz_scaled_sq.GetZ(); + float i_zz = xyz_scaled_sq.GetX() + xyz_scaled_sq.GetY(); + + // The off diagonal elements are calculated like: + // Ixy = -sum_{k = 1 to n}(x_k y_k) + // Ixz = -sum_{k = 1 to n}(x_k z_k) + // Iyz = -sum_{k = 1 to n}(y_k z_k) + // Scaling these is simple: + float i_xy = inScale.GetX() * inScale.GetY() * mInertia(0, 1); + float i_xz = inScale.GetX() * inScale.GetZ() * mInertia(0, 2); + float i_yz = inScale.GetY() * inScale.GetZ() * mInertia(1, 2); + + // Update inertia tensor + mInertia(0, 0) = i_xx; + mInertia(0, 1) = i_xy; + mInertia(1, 0) = i_xy; + mInertia(1, 1) = i_yy; + mInertia(0, 2) = i_xz; + mInertia(2, 0) = i_xz; + mInertia(1, 2) = i_yz; + mInertia(2, 1) = i_yz; + mInertia(2, 2) = i_zz; + + // Mass scales linear with volume (note that the scaling can be negative and we don't want the mass to become negative) + float mass_scale = abs(inScale.GetX() * inScale.GetY() * inScale.GetZ()); + mMass *= mass_scale; + + // Inertia scales linear with mass. This updates the m_k terms above. + mInertia *= mass_scale; + + // Ensure that the bottom right element is a 1 again + mInertia(3, 3) = 1.0f; +} + +void MassProperties::Rotate(Mat44Arg inRotation) +{ + mInertia = inRotation.Multiply3x3(mInertia).Multiply3x3RightTransposed(inRotation); +} + +void MassProperties::Translate(Vec3Arg inTranslation) +{ + // Transform the inertia using the parallel axis theorem: I' = I + m * (translation^2 E - translation translation^T) + // Where I is the original body's inertia and E the identity matrix + // See: https://en.wikipedia.org/wiki/Parallel_axis_theorem + mInertia += mMass * (Mat44::sScale(inTranslation.Dot(inTranslation)) - Mat44::sOuterProduct(inTranslation, inTranslation)); + + // Ensure that inertia is a 3x3 matrix, adding inertias causes the bottom right element to change + mInertia.SetColumn4(3, Vec4(0, 0, 0, 1)); +} + +void MassProperties::SaveBinaryState(StreamOut &inStream) const +{ + inStream.Write(mMass); + inStream.Write(mInertia); +} + +void MassProperties::RestoreBinaryState(StreamIn &inStream) +{ + inStream.Read(mMass); + inStream.Read(mInertia); +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/MassProperties.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/MassProperties.h new file mode 100644 index 00000000000..a72a901db10 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/MassProperties.h @@ -0,0 +1,58 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +class StreamIn; +class StreamOut; + +/// Describes the mass and inertia properties of a body. Used during body construction only. +class JPH_EXPORT MassProperties +{ +public: + JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, MassProperties) + + /// Using eigendecomposition, decompose the inertia tensor into a diagonal matrix D and a right-handed rotation matrix R so that the inertia tensor is \f$R \: D \: R^{-1}\f$. + /// @see https://en.wikipedia.org/wiki/Moment_of_inertia section 'Principal axes' + /// @param outRotation The rotation matrix R + /// @param outDiagonal The diagonal of the diagonal matrix D + /// @return True if successful, false if failed + bool DecomposePrincipalMomentsOfInertia(Mat44 &outRotation, Vec3 &outDiagonal) const; + + /// Set the mass and inertia of a box with edge size inBoxSize and density inDensity + void SetMassAndInertiaOfSolidBox(Vec3Arg inBoxSize, float inDensity); + + /// Set the mass and scale the inertia tensor to match the mass + void ScaleToMass(float inMass); + + /// Calculates the size of the solid box that has an inertia tensor diagonal inInertiaDiagonal + static Vec3 sGetEquivalentSolidBoxSize(float inMass, Vec3Arg inInertiaDiagonal); + + /// Rotate the inertia by 3x3 matrix inRotation + void Rotate(Mat44Arg inRotation); + + /// Translate the inertia by a vector inTranslation + void Translate(Vec3Arg inTranslation); + + /// Scale the mass and inertia by inScale, note that elements can be < 0 to flip the shape + void Scale(Vec3Arg inScale); + + /// Saves the state of this object in binary form to inStream. + void SaveBinaryState(StreamOut &inStream) const; + + /// Restore the state of this object from inStream. + void RestoreBinaryState(StreamIn &inStream); + + /// Mass of the shape (kg) + float mMass = 0.0f; + + /// Inertia tensor of the shape (kg m^2) + Mat44 mInertia = Mat44::sZero(); +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/MotionProperties.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/MotionProperties.cpp new file mode 100644 index 00000000000..1acc274bb83 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/MotionProperties.cpp @@ -0,0 +1,90 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include + +JPH_NAMESPACE_BEGIN + +void MotionProperties::SetMassProperties(EAllowedDOFs inAllowedDOFs, const MassProperties &inMassProperties) +{ + // Store allowed DOFs + mAllowedDOFs = inAllowedDOFs; + + // Decompose DOFs + uint allowed_translation_axis = uint(inAllowedDOFs) & 0b111; + uint allowed_rotation_axis = (uint(inAllowedDOFs) >> 3) & 0b111; + + // Set inverse mass + if (allowed_translation_axis == 0) + { + // No translation possible + mInvMass = 0.0f; + } + else + { + JPH_ASSERT(inMassProperties.mMass > 0.0f); + mInvMass = 1.0f / inMassProperties.mMass; + } + + if (allowed_rotation_axis == 0) + { + // No rotation possible + mInvInertiaDiagonal = Vec3::sZero(); + mInertiaRotation = Quat::sIdentity(); + } + else + { + // Set inverse inertia + Mat44 rotation; + Vec3 diagonal; + if (inMassProperties.DecomposePrincipalMomentsOfInertia(rotation, diagonal) + && !diagonal.IsNearZero()) + { + mInvInertiaDiagonal = diagonal.Reciprocal(); + mInertiaRotation = rotation.GetQuaternion(); + } + else + { + // Failed! Fall back to inertia tensor of sphere with radius 1. + mInvInertiaDiagonal = Vec3::sReplicate(2.5f * mInvMass); + mInertiaRotation = Quat::sIdentity(); + } + } + + JPH_ASSERT(mInvMass != 0.0f || mInvInertiaDiagonal != Vec3::sZero(), "Can't lock all axes, use a static body for this. This will crash with a division by zero later!"); +} + +void MotionProperties::SaveState(StateRecorder &inStream) const +{ + // Only write properties that can change at runtime + inStream.Write(mLinearVelocity); + inStream.Write(mAngularVelocity); + inStream.Write(mForce); + inStream.Write(mTorque); +#ifdef JPH_DOUBLE_PRECISION + inStream.Write(mSleepTestOffset); +#endif // JPH_DOUBLE_PRECISION + inStream.Write(mSleepTestSpheres); + inStream.Write(mSleepTestTimer); + inStream.Write(mAllowSleeping); +} + +void MotionProperties::RestoreState(StateRecorder &inStream) +{ + inStream.Read(mLinearVelocity); + inStream.Read(mAngularVelocity); + inStream.Read(mForce); + inStream.Read(mTorque); +#ifdef JPH_DOUBLE_PRECISION + inStream.Read(mSleepTestOffset); +#endif // JPH_DOUBLE_PRECISION + inStream.Read(mSleepTestSpheres); + inStream.Read(mSleepTestTimer); + inStream.Read(mAllowSleeping); +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/MotionProperties.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/MotionProperties.h new file mode 100644 index 00000000000..2ed814843e9 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/MotionProperties.h @@ -0,0 +1,278 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +class StateRecorder; + +/// Enum that determines if an object can go to sleep +enum class ECanSleep +{ + CannotSleep = 0, ///< Object cannot go to sleep + CanSleep = 1, ///< Object can go to sleep +}; + +/// The Body class only keeps track of state for static bodies, the MotionProperties class keeps the additional state needed for a moving Body. It has a 1-on-1 relationship with the body. +class JPH_EXPORT MotionProperties +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Motion quality, or how well it detects collisions when it has a high velocity + EMotionQuality GetMotionQuality() const { return mMotionQuality; } + + /// Get the allowed degrees of freedom that this body has (this can be changed by calling SetMassProperties) + inline EAllowedDOFs GetAllowedDOFs() const { return mAllowedDOFs; } + + /// If this body can go to sleep. + inline bool GetAllowSleeping() const { return mAllowSleeping; } + + /// Get world space linear velocity of the center of mass + inline Vec3 GetLinearVelocity() const { JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sVelocityAccess, BodyAccess::EAccess::Read)); return mLinearVelocity; } + + /// Set world space linear velocity of the center of mass + void SetLinearVelocity(Vec3Arg inLinearVelocity) { JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sVelocityAccess, BodyAccess::EAccess::ReadWrite)); JPH_ASSERT(inLinearVelocity.Length() <= mMaxLinearVelocity); mLinearVelocity = LockTranslation(inLinearVelocity); } + + /// Set world space linear velocity of the center of mass, will make sure the value is clamped against the maximum linear velocity + void SetLinearVelocityClamped(Vec3Arg inLinearVelocity) { mLinearVelocity = LockTranslation(inLinearVelocity); ClampLinearVelocity(); } + + /// Get world space angular velocity of the center of mass + inline Vec3 GetAngularVelocity() const { JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sVelocityAccess, BodyAccess::EAccess::Read)); return mAngularVelocity; } + + /// Set world space angular velocity of the center of mass + void SetAngularVelocity(Vec3Arg inAngularVelocity) { JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sVelocityAccess, BodyAccess::EAccess::ReadWrite)); JPH_ASSERT(inAngularVelocity.Length() <= mMaxAngularVelocity); mAngularVelocity = LockAngular(inAngularVelocity); } + + /// Set world space angular velocity of the center of mass, will make sure the value is clamped against the maximum angular velocity + void SetAngularVelocityClamped(Vec3Arg inAngularVelocity) { mAngularVelocity = LockAngular(inAngularVelocity); ClampAngularVelocity(); } + + /// Set velocity of body such that it will be rotate/translate by inDeltaPosition/Rotation in inDeltaTime seconds. + inline void MoveKinematic(Vec3Arg inDeltaPosition, QuatArg inDeltaRotation, float inDeltaTime); + + ///@name Velocity limits + ///@{ + + /// Maximum linear velocity that a body can achieve. Used to prevent the system from exploding. + inline float GetMaxLinearVelocity() const { return mMaxLinearVelocity; } + inline void SetMaxLinearVelocity(float inLinearVelocity) { JPH_ASSERT(inLinearVelocity >= 0.0f); mMaxLinearVelocity = inLinearVelocity; } + + /// Maximum angular velocity that a body can achieve. Used to prevent the system from exploding. + inline float GetMaxAngularVelocity() const { return mMaxAngularVelocity; } + inline void SetMaxAngularVelocity(float inAngularVelocity) { JPH_ASSERT(inAngularVelocity >= 0.0f); mMaxAngularVelocity = inAngularVelocity; } + ///@} + + /// Clamp velocity according to limit + inline void ClampLinearVelocity(); + inline void ClampAngularVelocity(); + + /// Get linear damping: dv/dt = -c * v. c must be between 0 and 1 but is usually close to 0. + inline float GetLinearDamping() const { return mLinearDamping; } + void SetLinearDamping(float inLinearDamping) { JPH_ASSERT(inLinearDamping >= 0.0f); mLinearDamping = inLinearDamping; } + + /// Get angular damping: dw/dt = -c * w. c must be between 0 and 1 but is usually close to 0. + inline float GetAngularDamping() const { return mAngularDamping; } + void SetAngularDamping(float inAngularDamping) { JPH_ASSERT(inAngularDamping >= 0.0f); mAngularDamping = inAngularDamping; } + + /// Get gravity factor (1 = normal gravity, 0 = no gravity) + inline float GetGravityFactor() const { return mGravityFactor; } + void SetGravityFactor(float inGravityFactor) { mGravityFactor = inGravityFactor; } + + /// Set the mass and inertia tensor + void SetMassProperties(EAllowedDOFs inAllowedDOFs, const MassProperties &inMassProperties); + + /// Get inverse mass (1 / mass). Should only be called on a dynamic object (static or kinematic bodies have infinite mass so should be treated as 1 / mass = 0) + inline float GetInverseMass() const { JPH_ASSERT(mCachedMotionType == EMotionType::Dynamic); return mInvMass; } + inline float GetInverseMassUnchecked() const { return mInvMass; } + + /// Set the inverse mass (1 / mass). + /// Note that mass and inertia are linearly related (e.g. inertia of a sphere with mass m and radius r is \f$2/5 \: m \: r^2\f$). + /// If you change mass, inertia should probably change as well. See MassProperties::ScaleToMass. + /// If all your translation degrees of freedom are restricted, make sure this is zero (see EAllowedDOFs). + void SetInverseMass(float inInverseMass) { mInvMass = inInverseMass; } + + /// Diagonal of inverse inertia matrix: D. Should only be called on a dynamic object (static or kinematic bodies have infinite mass so should be treated as D = 0) + inline Vec3 GetInverseInertiaDiagonal() const { JPH_ASSERT(mCachedMotionType == EMotionType::Dynamic); return mInvInertiaDiagonal; } + + /// Rotation (R) that takes inverse inertia diagonal to local space: \f$I_{body}^{-1} = R \: D \: R^{-1}\f$ + inline Quat GetInertiaRotation() const { return mInertiaRotation; } + + /// Set the inverse inertia tensor in local space by setting the diagonal and the rotation: \f$I_{body}^{-1} = R \: D \: R^{-1}\f$. + /// Note that mass and inertia are linearly related (e.g. inertia of a sphere with mass m and radius r is \f$2/5 \: m \: r^2\f$). + /// If you change inertia, mass should probably change as well. See MassProperties::ScaleToMass. + /// If all your rotation degrees of freedom are restricted, make sure this is zero (see EAllowedDOFs). + void SetInverseInertia(Vec3Arg inDiagonal, QuatArg inRot) { mInvInertiaDiagonal = inDiagonal; mInertiaRotation = inRot; } + + /// Get inverse inertia matrix (\f$I_{body}^{-1}\f$). Will be a matrix of zeros for a static or kinematic object. + inline Mat44 GetLocalSpaceInverseInertia() const; + + /// Same as GetLocalSpaceInverseInertia() but doesn't check if the body is dynamic + inline Mat44 GetLocalSpaceInverseInertiaUnchecked() const; + + /// Get inverse inertia matrix (\f$I^{-1}\f$) for a given object rotation (translation will be ignored). Zero if object is static or kinematic. + inline Mat44 GetInverseInertiaForRotation(Mat44Arg inRotation) const; + + /// Multiply a vector with the inverse world space inertia tensor (\f$I_{world}^{-1}\f$). Zero if object is static or kinematic. + JPH_INLINE Vec3 MultiplyWorldSpaceInverseInertiaByVector(QuatArg inBodyRotation, Vec3Arg inV) const; + + /// Velocity of point inPoint (in center of mass space, e.g. on the surface of the body) of the body (unit: m/s) + JPH_INLINE Vec3 GetPointVelocityCOM(Vec3Arg inPointRelativeToCOM) const { return mLinearVelocity + mAngularVelocity.Cross(inPointRelativeToCOM); } + + // Get the total amount of force applied to the center of mass this time step (through Body::AddForce calls). Note that it will reset to zero after PhysicsSystem::Update. + JPH_INLINE Vec3 GetAccumulatedForce() const { return Vec3::sLoadFloat3Unsafe(mForce); } + + // Get the total amount of torque applied to the center of mass this time step (through Body::AddForce/Body::AddTorque calls). Note that it will reset to zero after PhysicsSystem::Update. + JPH_INLINE Vec3 GetAccumulatedTorque() const { return Vec3::sLoadFloat3Unsafe(mTorque); } + + // Reset the total accumulated force, not that this will be done automatically after every time step. + JPH_INLINE void ResetForce() { mForce = Float3(0, 0, 0); } + + // Reset the total accumulated torque, not that this will be done automatically after every time step. + JPH_INLINE void ResetTorque() { mTorque = Float3(0, 0, 0); } + + // Reset the current velocity and accumulated force and torque. + JPH_INLINE void ResetMotion() + { + JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sVelocityAccess, BodyAccess::EAccess::ReadWrite)); + mLinearVelocity = mAngularVelocity = Vec3::sZero(); + mForce = mTorque = Float3(0, 0, 0); + } + + /// Returns a vector where the linear components that are not allowed by mAllowedDOFs are set to 0 and the rest to 0xffffffff + JPH_INLINE UVec4 GetLinearDOFsMask() const + { + UVec4 mask(uint32(EAllowedDOFs::TranslationX), uint32(EAllowedDOFs::TranslationY), uint32(EAllowedDOFs::TranslationZ), 0); + return UVec4::sEquals(UVec4::sAnd(UVec4::sReplicate(uint32(mAllowedDOFs)), mask), mask); + } + + /// Takes a translation vector inV and returns a vector where the components that are not allowed by mAllowedDOFs are set to 0 + JPH_INLINE Vec3 LockTranslation(Vec3Arg inV) const + { + return Vec3::sAnd(inV, Vec3(GetLinearDOFsMask().ReinterpretAsFloat())); + } + + /// Returns a vector where the angular components that are not allowed by mAllowedDOFs are set to 0 and the rest to 0xffffffff + JPH_INLINE UVec4 GetAngularDOFsMask() const + { + UVec4 mask(uint32(EAllowedDOFs::RotationX), uint32(EAllowedDOFs::RotationY), uint32(EAllowedDOFs::RotationZ), 0); + return UVec4::sEquals(UVec4::sAnd(UVec4::sReplicate(uint32(mAllowedDOFs)), mask), mask); + } + + /// Takes an angular velocity / torque vector inV and returns a vector where the components that are not allowed by mAllowedDOFs are set to 0 + JPH_INLINE Vec3 LockAngular(Vec3Arg inV) const + { + return Vec3::sAnd(inV, Vec3(GetAngularDOFsMask().ReinterpretAsFloat())); + } + + /// Used only when this body is dynamic and colliding. Override for the number of solver velocity iterations to run, 0 means use the default in PhysicsSettings::mNumVelocitySteps. The number of iterations to use is the max of all contacts and constraints in the island. + void SetNumVelocityStepsOverride(uint inN) { JPH_ASSERT(inN < 256); mNumVelocityStepsOverride = uint8(inN); } + uint GetNumVelocityStepsOverride() const { return mNumVelocityStepsOverride; } + + /// Used only when this body is dynamic and colliding. Override for the number of solver position iterations to run, 0 means use the default in PhysicsSettings::mNumPositionSteps. The number of iterations to use is the max of all contacts and constraints in the island. + void SetNumPositionStepsOverride(uint inN) { JPH_ASSERT(inN < 256); mNumPositionStepsOverride = uint8(inN); } + uint GetNumPositionStepsOverride() const { return mNumPositionStepsOverride; } + + //////////////////////////////////////////////////////////// + // FUNCTIONS BELOW THIS LINE ARE FOR INTERNAL USE ONLY + //////////////////////////////////////////////////////////// + + ///@name Update linear and angular velocity (used during constraint solving) + ///@{ + inline void AddLinearVelocityStep(Vec3Arg inLinearVelocityChange) { JPH_DET_LOG("AddLinearVelocityStep: " << inLinearVelocityChange); JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sVelocityAccess, BodyAccess::EAccess::ReadWrite)); mLinearVelocity = LockTranslation(mLinearVelocity + inLinearVelocityChange); JPH_ASSERT(!mLinearVelocity.IsNaN()); } + inline void SubLinearVelocityStep(Vec3Arg inLinearVelocityChange) { JPH_DET_LOG("SubLinearVelocityStep: " << inLinearVelocityChange); JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sVelocityAccess, BodyAccess::EAccess::ReadWrite)); mLinearVelocity = LockTranslation(mLinearVelocity - inLinearVelocityChange); JPH_ASSERT(!mLinearVelocity.IsNaN()); } + inline void AddAngularVelocityStep(Vec3Arg inAngularVelocityChange) { JPH_DET_LOG("AddAngularVelocityStep: " << inAngularVelocityChange); JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sVelocityAccess, BodyAccess::EAccess::ReadWrite)); mAngularVelocity += inAngularVelocityChange; JPH_ASSERT(!mAngularVelocity.IsNaN()); } + inline void SubAngularVelocityStep(Vec3Arg inAngularVelocityChange) { JPH_DET_LOG("SubAngularVelocityStep: " << inAngularVelocityChange); JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sVelocityAccess, BodyAccess::EAccess::ReadWrite)); mAngularVelocity -= inAngularVelocityChange; JPH_ASSERT(!mAngularVelocity.IsNaN()); } + ///@} + + /// Apply the gyroscopic force (aka Dzhanibekov effect, see https://en.wikipedia.org/wiki/Tennis_racket_theorem) + inline void ApplyGyroscopicForceInternal(QuatArg inBodyRotation, float inDeltaTime); + + /// Apply all accumulated forces, torques and drag (should only be called by the PhysicsSystem) + inline void ApplyForceTorqueAndDragInternal(QuatArg inBodyRotation, Vec3Arg inGravity, float inDeltaTime); + + /// Access to the island index + uint32 GetIslandIndexInternal() const { return mIslandIndex; } + void SetIslandIndexInternal(uint32 inIndex) { mIslandIndex = inIndex; } + + /// Access to the index in the active bodies array + uint32 GetIndexInActiveBodiesInternal() const { return mIndexInActiveBodies; } + +#ifdef JPH_DOUBLE_PRECISION + inline DVec3 GetSleepTestOffset() const { return DVec3::sLoadDouble3Unsafe(mSleepTestOffset); } +#endif // JPH_DOUBLE_PRECISION + + /// Reset spheres to center around inPoints with radius 0 + inline void ResetSleepTestSpheres(const RVec3 *inPoints); + + /// Reset the sleep test timer without resetting the sleep test spheres + inline void ResetSleepTestTimer() { mSleepTestTimer = 0.0f; } + + /// Accumulate sleep time and return if a body can go to sleep + inline ECanSleep AccumulateSleepTime(float inDeltaTime, float inTimeBeforeSleep); + + /// Saving state for replay + void SaveState(StateRecorder &inStream) const; + + /// Restoring state for replay + void RestoreState(StateRecorder &inStream); + + static constexpr uint32 cInactiveIndex = uint32(-1); ///< Constant indicating that body is not active + +private: + friend class BodyManager; + friend class Body; + + // 1st cache line + // 16 byte aligned + Vec3 mLinearVelocity { Vec3::sZero() }; ///< World space linear velocity of the center of mass (m/s) + Vec3 mAngularVelocity { Vec3::sZero() }; ///< World space angular velocity (rad/s) + Vec3 mInvInertiaDiagonal; ///< Diagonal of inverse inertia matrix: D + Quat mInertiaRotation; ///< Rotation (R) that takes inverse inertia diagonal to local space: Ibody^-1 = R * D * R^-1 + + // 2nd cache line + // 4 byte aligned + Float3 mForce { 0, 0, 0 }; ///< Accumulated world space force (N). Note loaded through intrinsics so ensure that the 4 bytes after this are readable! + Float3 mTorque { 0, 0, 0 }; ///< Accumulated world space torque (N m). Note loaded through intrinsics so ensure that the 4 bytes after this are readable! + float mInvMass; ///< Inverse mass of the object (1/kg) + float mLinearDamping; ///< Linear damping: dv/dt = -c * v. c must be between 0 and 1 but is usually close to 0. + float mAngularDamping; ///< Angular damping: dw/dt = -c * w. c must be between 0 and 1 but is usually close to 0. + float mMaxLinearVelocity; ///< Maximum linear velocity that this body can reach (m/s) + float mMaxAngularVelocity; ///< Maximum angular velocity that this body can reach (rad/s) + float mGravityFactor; ///< Factor to multiply gravity with + uint32 mIndexInActiveBodies = cInactiveIndex; ///< If the body is active, this is the index in the active body list or cInactiveIndex if it is not active (note that there are 2 lists, one for rigid and one for soft bodies) + uint32 mIslandIndex = cInactiveIndex; ///< Index of the island that this body is part of, when the body has not yet been updated or is not active this is cInactiveIndex + + // 1 byte aligned + EMotionQuality mMotionQuality; ///< Motion quality, or how well it detects collisions when it has a high velocity + bool mAllowSleeping; ///< If this body can go to sleep + EAllowedDOFs mAllowedDOFs = EAllowedDOFs::All; ///< Allowed degrees of freedom for this body + uint8 mNumVelocityStepsOverride = 0; ///< Used only when this body is dynamic and colliding. Override for the number of solver velocity iterations to run, 0 means use the default in PhysicsSettings::mNumVelocitySteps. The number of iterations to use is the max of all contacts and constraints in the island. + uint8 mNumPositionStepsOverride = 0; ///< Used only when this body is dynamic and colliding. Override for the number of solver position iterations to run, 0 means use the default in PhysicsSettings::mNumPositionSteps. The number of iterations to use is the max of all contacts and constraints in the island. + + // 3rd cache line (least frequently used) + // 4 byte aligned (or 8 byte if running in double precision) +#ifdef JPH_DOUBLE_PRECISION + Double3 mSleepTestOffset; ///< mSleepTestSpheres are relative to this offset to prevent floating point inaccuracies. Warning: Loaded using sLoadDouble3Unsafe which will read 8 extra bytes. +#endif // JPH_DOUBLE_PRECISION + Sphere mSleepTestSpheres[3]; ///< Measure motion for 3 points on the body to see if it is resting: COM, COM + largest bounding box axis, COM + second largest bounding box axis + float mSleepTestTimer; ///< How long this body has been within the movement tolerance + +#ifdef JPH_ENABLE_ASSERTS + EBodyType mCachedBodyType; ///< Copied from Body::mBodyType and cached for asserting purposes + EMotionType mCachedMotionType; ///< Copied from Body::mMotionType and cached for asserting purposes +#endif +}; + +JPH_NAMESPACE_END + +#include "MotionProperties.inl" diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/MotionProperties.inl b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/MotionProperties.inl new file mode 100644 index 00000000000..1fb044ef48e --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/MotionProperties.inl @@ -0,0 +1,168 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +JPH_NAMESPACE_BEGIN + +void MotionProperties::MoveKinematic(Vec3Arg inDeltaPosition, QuatArg inDeltaRotation, float inDeltaTime) +{ + JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sVelocityAccess, BodyAccess::EAccess::ReadWrite)); + JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sPositionAccess, BodyAccess::EAccess::Read)); + JPH_ASSERT(mCachedBodyType == EBodyType::RigidBody); + JPH_ASSERT(mCachedMotionType != EMotionType::Static); + + // Calculate required linear velocity + mLinearVelocity = LockTranslation(inDeltaPosition / inDeltaTime); + + // Calculate required angular velocity + Vec3 axis; + float angle; + inDeltaRotation.GetAxisAngle(axis, angle); + mAngularVelocity = LockAngular(axis * (angle / inDeltaTime)); +} + +void MotionProperties::ClampLinearVelocity() +{ + JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sVelocityAccess, BodyAccess::EAccess::ReadWrite)); + + float len_sq = mLinearVelocity.LengthSq(); + JPH_ASSERT(isfinite(len_sq)); + if (len_sq > Square(mMaxLinearVelocity)) + mLinearVelocity *= mMaxLinearVelocity / sqrt(len_sq); +} + +void MotionProperties::ClampAngularVelocity() +{ + JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sVelocityAccess, BodyAccess::EAccess::ReadWrite)); + + float len_sq = mAngularVelocity.LengthSq(); + JPH_ASSERT(isfinite(len_sq)); + if (len_sq > Square(mMaxAngularVelocity)) + mAngularVelocity *= mMaxAngularVelocity / sqrt(len_sq); +} + +inline Mat44 MotionProperties::GetLocalSpaceInverseInertiaUnchecked() const +{ + Mat44 rotation = Mat44::sRotation(mInertiaRotation); + Mat44 rotation_mul_scale_transposed(mInvInertiaDiagonal.SplatX() * rotation.GetColumn4(0), mInvInertiaDiagonal.SplatY() * rotation.GetColumn4(1), mInvInertiaDiagonal.SplatZ() * rotation.GetColumn4(2), Vec4(0, 0, 0, 1)); + return rotation.Multiply3x3RightTransposed(rotation_mul_scale_transposed); +} + +inline Mat44 MotionProperties::GetLocalSpaceInverseInertia() const +{ + JPH_ASSERT(mCachedMotionType == EMotionType::Dynamic); + return GetLocalSpaceInverseInertiaUnchecked(); +} + +Mat44 MotionProperties::GetInverseInertiaForRotation(Mat44Arg inRotation) const +{ + JPH_ASSERT(mCachedMotionType == EMotionType::Dynamic); + + Mat44 rotation = inRotation.Multiply3x3(Mat44::sRotation(mInertiaRotation)); + Mat44 rotation_mul_scale_transposed(mInvInertiaDiagonal.SplatX() * rotation.GetColumn4(0), mInvInertiaDiagonal.SplatY() * rotation.GetColumn4(1), mInvInertiaDiagonal.SplatZ() * rotation.GetColumn4(2), Vec4(0, 0, 0, 1)); + Mat44 inverse_inertia = rotation.Multiply3x3RightTransposed(rotation_mul_scale_transposed); + + // We need to mask out both the rows and columns of DOFs that are not allowed + Vec4 angular_dofs_mask = GetAngularDOFsMask().ReinterpretAsFloat(); + inverse_inertia.SetColumn4(0, Vec4::sAnd(inverse_inertia.GetColumn4(0), Vec4::sAnd(angular_dofs_mask, angular_dofs_mask.SplatX()))); + inverse_inertia.SetColumn4(1, Vec4::sAnd(inverse_inertia.GetColumn4(1), Vec4::sAnd(angular_dofs_mask, angular_dofs_mask.SplatY()))); + inverse_inertia.SetColumn4(2, Vec4::sAnd(inverse_inertia.GetColumn4(2), Vec4::sAnd(angular_dofs_mask, angular_dofs_mask.SplatZ()))); + + return inverse_inertia; +} + +Vec3 MotionProperties::MultiplyWorldSpaceInverseInertiaByVector(QuatArg inBodyRotation, Vec3Arg inV) const +{ + JPH_ASSERT(mCachedMotionType == EMotionType::Dynamic); + + // Mask out columns of DOFs that are not allowed + Vec3 angular_dofs_mask = Vec3(GetAngularDOFsMask().ReinterpretAsFloat()); + Vec3 v = Vec3::sAnd(inV, angular_dofs_mask); + + // Multiply vector by inverse inertia + Mat44 rotation = Mat44::sRotation(inBodyRotation * mInertiaRotation); + Vec3 result = rotation.Multiply3x3(mInvInertiaDiagonal * rotation.Multiply3x3Transposed(v)); + + // Mask out rows of DOFs that are not allowed + return Vec3::sAnd(result, angular_dofs_mask); +} + +void MotionProperties::ApplyGyroscopicForceInternal(QuatArg inBodyRotation, float inDeltaTime) +{ + JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sVelocityAccess, BodyAccess::EAccess::ReadWrite)); + JPH_ASSERT(mCachedBodyType == EBodyType::RigidBody); + JPH_ASSERT(mCachedMotionType == EMotionType::Dynamic); + + // Calculate local space inertia tensor (a diagonal in local space) + UVec4 is_zero = Vec3::sEquals(mInvInertiaDiagonal, Vec3::sZero()); + Vec3 denominator = Vec3::sSelect(mInvInertiaDiagonal, Vec3::sReplicate(1.0f), is_zero); + Vec3 nominator = Vec3::sSelect(Vec3::sReplicate(1.0f), Vec3::sZero(), is_zero); + Vec3 local_inertia = nominator / denominator; // Avoid dividing by zero, inertia in this axis will be zero + + // Calculate local space angular momentum + Quat inertia_space_to_world_space = inBodyRotation * mInertiaRotation; + Vec3 local_angular_velocity = inertia_space_to_world_space.Conjugated() * mAngularVelocity; + Vec3 local_momentum = local_inertia * local_angular_velocity; + + // The gyroscopic force applies a torque: T = -w x I w where w is angular velocity and I the inertia tensor + // Calculate the new angular momentum by applying the gyroscopic force and make sure the new magnitude is the same as the old one + // to avoid introducing energy into the system due to the Euler step + Vec3 new_local_momentum = local_momentum - inDeltaTime * local_angular_velocity.Cross(local_momentum); + float new_local_momentum_len_sq = new_local_momentum.LengthSq(); + new_local_momentum = new_local_momentum_len_sq > 0.0f? new_local_momentum * sqrt(local_momentum.LengthSq() / new_local_momentum_len_sq) : Vec3::sZero(); + + // Convert back to world space angular velocity + mAngularVelocity = inertia_space_to_world_space * (mInvInertiaDiagonal * new_local_momentum); +} + +void MotionProperties::ApplyForceTorqueAndDragInternal(QuatArg inBodyRotation, Vec3Arg inGravity, float inDeltaTime) +{ + JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sVelocityAccess, BodyAccess::EAccess::ReadWrite)); + JPH_ASSERT(mCachedBodyType == EBodyType::RigidBody); + JPH_ASSERT(mCachedMotionType == EMotionType::Dynamic); + + // Update linear velocity + mLinearVelocity = LockTranslation(mLinearVelocity + inDeltaTime * (mGravityFactor * inGravity + mInvMass * GetAccumulatedForce())); + + // Update angular velocity + mAngularVelocity += inDeltaTime * MultiplyWorldSpaceInverseInertiaByVector(inBodyRotation, GetAccumulatedTorque()); + + // Linear damping: dv/dt = -c * v + // Solution: v(t) = v(0) * e^(-c * t) or v2 = v1 * e^(-c * dt) + // Taylor expansion of e^(-c * dt) = 1 - c * dt + ... + // Since dt is usually in the order of 1/60 and c is a low number too this approximation is good enough + mLinearVelocity *= max(0.0f, 1.0f - mLinearDamping * inDeltaTime); + mAngularVelocity *= max(0.0f, 1.0f - mAngularDamping * inDeltaTime); + + // Clamp velocities + ClampLinearVelocity(); + ClampAngularVelocity(); +} + +void MotionProperties::ResetSleepTestSpheres(const RVec3 *inPoints) +{ +#ifdef JPH_DOUBLE_PRECISION + // Make spheres relative to the first point and initialize them to zero radius + DVec3 offset = inPoints[0]; + offset.StoreDouble3(&mSleepTestOffset); + mSleepTestSpheres[0] = Sphere(Vec3::sZero(), 0.0f); + for (int i = 1; i < 3; ++i) + mSleepTestSpheres[i] = Sphere(Vec3(inPoints[i] - offset), 0.0f); +#else + // Initialize the spheres to zero radius around the supplied points + for (int i = 0; i < 3; ++i) + mSleepTestSpheres[i] = Sphere(inPoints[i], 0.0f); +#endif + + mSleepTestTimer = 0.0f; +} + +ECanSleep MotionProperties::AccumulateSleepTime(float inDeltaTime, float inTimeBeforeSleep) +{ + mSleepTestTimer += inDeltaTime; + return mSleepTestTimer >= inTimeBeforeSleep? ECanSleep::CanSleep : ECanSleep::CannotSleep; +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/MotionQuality.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/MotionQuality.h new file mode 100644 index 00000000000..b1ba343c06a --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/MotionQuality.h @@ -0,0 +1,31 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +JPH_NAMESPACE_BEGIN + +/// Motion quality, or how well it detects collisions when it has a high velocity +enum class EMotionQuality : uint8 +{ + /// Update the body in discrete steps. Body will tunnel throuh thin objects if its velocity is high enough. + /// This is the cheapest way of simulating a body. + Discrete, + + /// Update the body using linear casting. When stepping the body, its collision shape is cast from + /// start to destination using the starting rotation. The body will not be able to tunnel through thin + /// objects at high velocity, but tunneling is still possible if the body is long and thin and has high + /// angular velocity. Time is stolen from the object (which means it will move up to the first collision + /// and will not bounce off the surface until the next integration step). This will make the body appear + /// to go slower when it collides with high velocity. In order to not get stuck, the body is always + /// allowed to move by a fraction of it's inner radius, which may eventually lead it to pass through geometry. + /// + /// Note that if you're using a collision listener, you can receive contact added/persisted notifications of contacts + /// that may in the end not happen. This happens between bodies that are using casting: If bodies A and B collide at t1 + /// and B and C collide at t2 where t2 < t1 and A and C don't collide. In this case you may receive an incorrect contact + /// point added callback between A and B (which will be removed the next frame). + LinearCast, +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/MotionType.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/MotionType.h new file mode 100644 index 00000000000..6de0d8c8ec7 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/MotionType.h @@ -0,0 +1,17 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +JPH_NAMESPACE_BEGIN + +/// Motion type of a physics body +enum class EMotionType : uint8 +{ + Static, ///< Non movable + Kinematic, ///< Movable using velocities only, does not respond to forces + Dynamic, ///< Responds to forces as a normal physics object +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Character/Character.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Character/Character.cpp new file mode 100644 index 00000000000..6bb8611e909 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Character/Character.cpp @@ -0,0 +1,317 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +static inline const BodyLockInterface &sGetBodyLockInterface(const PhysicsSystem *inSystem, bool inLockBodies) +{ + return inLockBodies? static_cast(inSystem->GetBodyLockInterface()) : static_cast(inSystem->GetBodyLockInterfaceNoLock()); +} + +static inline BodyInterface &sGetBodyInterface(PhysicsSystem *inSystem, bool inLockBodies) +{ + return inLockBodies? inSystem->GetBodyInterface() : inSystem->GetBodyInterfaceNoLock(); +} + +static inline const NarrowPhaseQuery &sGetNarrowPhaseQuery(const PhysicsSystem *inSystem, bool inLockBodies) +{ + return inLockBodies? inSystem->GetNarrowPhaseQuery() : inSystem->GetNarrowPhaseQueryNoLock(); +} + +Character::Character(const CharacterSettings *inSettings, RVec3Arg inPosition, QuatArg inRotation, uint64 inUserData, PhysicsSystem *inSystem) : + CharacterBase(inSettings, inSystem), + mLayer(inSettings->mLayer) +{ + // Construct rigid body + BodyCreationSettings settings(mShape, inPosition, inRotation, EMotionType::Dynamic, mLayer); + settings.mAllowedDOFs = EAllowedDOFs::TranslationX | EAllowedDOFs::TranslationY | EAllowedDOFs::TranslationZ; + settings.mOverrideMassProperties = EOverrideMassProperties::MassAndInertiaProvided; + settings.mMassPropertiesOverride.mMass = inSettings->mMass; + settings.mFriction = inSettings->mFriction; + settings.mGravityFactor = inSettings->mGravityFactor; + settings.mUserData = inUserData; + const Body *body = mSystem->GetBodyInterface().CreateBody(settings); + if (body != nullptr) + mBodyID = body->GetID(); +} + +Character::~Character() +{ + // Destroy the body + mSystem->GetBodyInterface().DestroyBody(mBodyID); +} + +void Character::AddToPhysicsSystem(EActivation inActivationMode, bool inLockBodies) +{ + sGetBodyInterface(mSystem, inLockBodies).AddBody(mBodyID, inActivationMode); +} + +void Character::RemoveFromPhysicsSystem(bool inLockBodies) +{ + sGetBodyInterface(mSystem, inLockBodies).RemoveBody(mBodyID); +} + +void Character::Activate(bool inLockBodies) +{ + sGetBodyInterface(mSystem, inLockBodies).ActivateBody(mBodyID); +} + +void Character::CheckCollision(RMat44Arg inCenterOfMassTransform, Vec3Arg inMovementDirection, float inMaxSeparationDistance, const Shape *inShape, RVec3Arg inBaseOffset, CollideShapeCollector &ioCollector, bool inLockBodies) const +{ + // Create query broadphase layer filter + DefaultBroadPhaseLayerFilter broadphase_layer_filter = mSystem->GetDefaultBroadPhaseLayerFilter(mLayer); + + // Create query object layer filter + DefaultObjectLayerFilter object_layer_filter = mSystem->GetDefaultLayerFilter(mLayer); + + // Ignore my own body + IgnoreSingleBodyFilter body_filter(mBodyID); + + // Settings for collide shape + CollideShapeSettings settings; + settings.mMaxSeparationDistance = inMaxSeparationDistance; + settings.mActiveEdgeMode = EActiveEdgeMode::CollideOnlyWithActive; + settings.mActiveEdgeMovementDirection = inMovementDirection; + settings.mBackFaceMode = EBackFaceMode::IgnoreBackFaces; + + sGetNarrowPhaseQuery(mSystem, inLockBodies).CollideShape(inShape, Vec3::sReplicate(1.0f), inCenterOfMassTransform, settings, inBaseOffset, ioCollector, broadphase_layer_filter, object_layer_filter, body_filter); +} + +void Character::CheckCollision(RVec3Arg inPosition, QuatArg inRotation, Vec3Arg inMovementDirection, float inMaxSeparationDistance, const Shape *inShape, RVec3Arg inBaseOffset, CollideShapeCollector &ioCollector, bool inLockBodies) const +{ + // Calculate center of mass transform + RMat44 center_of_mass = RMat44::sRotationTranslation(inRotation, inPosition).PreTranslated(inShape->GetCenterOfMass()); + + CheckCollision(center_of_mass, inMovementDirection, inMaxSeparationDistance, inShape, inBaseOffset, ioCollector, inLockBodies); +} + +void Character::CheckCollision(const Shape *inShape, float inMaxSeparationDistance, RVec3Arg inBaseOffset, CollideShapeCollector &ioCollector, bool inLockBodies) const +{ + // Determine position and velocity of body + RMat44 query_transform; + Vec3 velocity; + { + BodyLockRead lock(sGetBodyLockInterface(mSystem, inLockBodies), mBodyID); + if (!lock.Succeeded()) + return; + + const Body &body = lock.GetBody(); + + // Correct the center of mass transform for the difference between the old and new center of mass shape + query_transform = body.GetCenterOfMassTransform().PreTranslated(inShape->GetCenterOfMass() - mShape->GetCenterOfMass()); + velocity = body.GetLinearVelocity(); + } + + CheckCollision(query_transform, velocity, inMaxSeparationDistance, inShape, inBaseOffset, ioCollector, inLockBodies); +} + +void Character::PostSimulation(float inMaxSeparationDistance, bool inLockBodies) +{ + // Get character position, rotation and velocity + RVec3 char_pos; + Quat char_rot; + Vec3 char_vel; + { + BodyLockRead lock(sGetBodyLockInterface(mSystem, inLockBodies), mBodyID); + if (!lock.Succeeded()) + return; + const Body &body = lock.GetBody(); + char_pos = body.GetPosition(); + char_rot = body.GetRotation(); + char_vel = body.GetLinearVelocity(); + } + + // Collector that finds the hit with the normal that is the most 'up' + class MyCollector : public CollideShapeCollector + { + public: + // Constructor + explicit MyCollector(Vec3Arg inUp, RVec3 inBaseOffset) : mBaseOffset(inBaseOffset), mUp(inUp) { } + + // See: CollectorType::AddHit + virtual void AddHit(const CollideShapeResult &inResult) override + { + Vec3 normal = -inResult.mPenetrationAxis.Normalized(); + float dot = normal.Dot(mUp); + if (dot > mBestDot) // Find the hit that is most aligned with the up vector + { + mGroundBodyID = inResult.mBodyID2; + mGroundBodySubShapeID = inResult.mSubShapeID2; + mGroundPosition = mBaseOffset + inResult.mContactPointOn2; + mGroundNormal = normal; + mBestDot = dot; + } + } + + BodyID mGroundBodyID; + SubShapeID mGroundBodySubShapeID; + RVec3 mGroundPosition = RVec3::sZero(); + Vec3 mGroundNormal = Vec3::sZero(); + + private: + RVec3 mBaseOffset; + Vec3 mUp; + float mBestDot = -FLT_MAX; + }; + + // Collide shape + MyCollector collector(mUp, char_pos); + CheckCollision(char_pos, char_rot, char_vel, inMaxSeparationDistance, mShape, char_pos, collector, inLockBodies); + + // Copy results + mGroundBodyID = collector.mGroundBodyID; + mGroundBodySubShapeID = collector.mGroundBodySubShapeID; + mGroundPosition = collector.mGroundPosition; + mGroundNormal = collector.mGroundNormal; + + // Get additional data from body + BodyLockRead lock(sGetBodyLockInterface(mSystem, inLockBodies), mGroundBodyID); + if (lock.Succeeded()) + { + const Body &body = lock.GetBody(); + + // Update ground state + RMat44 inv_transform = RMat44::sInverseRotationTranslation(char_rot, char_pos); + if (mSupportingVolume.SignedDistance(Vec3(inv_transform * mGroundPosition)) > 0.0f) + mGroundState = EGroundState::NotSupported; + else if (IsSlopeTooSteep(mGroundNormal)) + mGroundState = EGroundState::OnSteepGround; + else + mGroundState = EGroundState::OnGround; + + // Copy other body properties + mGroundMaterial = body.GetShape()->GetMaterial(mGroundBodySubShapeID); + mGroundVelocity = body.GetPointVelocity(mGroundPosition); + mGroundUserData = body.GetUserData(); + } + else + { + mGroundState = EGroundState::InAir; + mGroundMaterial = PhysicsMaterial::sDefault; + mGroundVelocity = Vec3::sZero(); + mGroundUserData = 0; + } +} + +void Character::SetLinearAndAngularVelocity(Vec3Arg inLinearVelocity, Vec3Arg inAngularVelocity, bool inLockBodies) +{ + sGetBodyInterface(mSystem, inLockBodies).SetLinearAndAngularVelocity(mBodyID, inLinearVelocity, inAngularVelocity); +} + +Vec3 Character::GetLinearVelocity(bool inLockBodies) const +{ + return sGetBodyInterface(mSystem, inLockBodies).GetLinearVelocity(mBodyID); +} + +void Character::SetLinearVelocity(Vec3Arg inLinearVelocity, bool inLockBodies) +{ + sGetBodyInterface(mSystem, inLockBodies).SetLinearVelocity(mBodyID, inLinearVelocity); +} + +void Character::AddLinearVelocity(Vec3Arg inLinearVelocity, bool inLockBodies) +{ + sGetBodyInterface(mSystem, inLockBodies).AddLinearVelocity(mBodyID, inLinearVelocity); +} + +void Character::AddImpulse(Vec3Arg inImpulse, bool inLockBodies) +{ + sGetBodyInterface(mSystem, inLockBodies).AddImpulse(mBodyID, inImpulse); +} + +void Character::GetPositionAndRotation(RVec3 &outPosition, Quat &outRotation, bool inLockBodies) const +{ + sGetBodyInterface(mSystem, inLockBodies).GetPositionAndRotation(mBodyID, outPosition, outRotation); +} + +void Character::SetPositionAndRotation(RVec3Arg inPosition, QuatArg inRotation, EActivation inActivationMode, bool inLockBodies) const +{ + sGetBodyInterface(mSystem, inLockBodies).SetPositionAndRotation(mBodyID, inPosition, inRotation, inActivationMode); +} + +RVec3 Character::GetPosition(bool inLockBodies) const +{ + return sGetBodyInterface(mSystem, inLockBodies).GetPosition(mBodyID); +} + +void Character::SetPosition(RVec3Arg inPosition, EActivation inActivationMode, bool inLockBodies) +{ + sGetBodyInterface(mSystem, inLockBodies).SetPosition(mBodyID, inPosition, inActivationMode); +} + +Quat Character::GetRotation(bool inLockBodies) const +{ + return sGetBodyInterface(mSystem, inLockBodies).GetRotation(mBodyID); +} + +void Character::SetRotation(QuatArg inRotation, EActivation inActivationMode, bool inLockBodies) +{ + sGetBodyInterface(mSystem, inLockBodies).SetRotation(mBodyID, inRotation, inActivationMode); +} + +RVec3 Character::GetCenterOfMassPosition(bool inLockBodies) const +{ + return sGetBodyInterface(mSystem, inLockBodies).GetCenterOfMassPosition(mBodyID); +} + +RMat44 Character::GetWorldTransform(bool inLockBodies) const +{ + return sGetBodyInterface(mSystem, inLockBodies).GetWorldTransform(mBodyID); +} + +void Character::SetLayer(ObjectLayer inLayer, bool inLockBodies) +{ + mLayer = inLayer; + + sGetBodyInterface(mSystem, inLockBodies).SetObjectLayer(mBodyID, inLayer); +} + +bool Character::SetShape(const Shape *inShape, float inMaxPenetrationDepth, bool inLockBodies) +{ + if (inMaxPenetrationDepth < FLT_MAX) + { + // Collector that checks if there is anything in the way while switching to inShape + class MyCollector : public CollideShapeCollector + { + public: + // Constructor + explicit MyCollector(float inMaxPenetrationDepth) : mMaxPenetrationDepth(inMaxPenetrationDepth) { } + + // See: CollectorType::AddHit + virtual void AddHit(const CollideShapeResult &inResult) override + { + if (inResult.mPenetrationDepth > mMaxPenetrationDepth) + { + mHadCollision = true; + ForceEarlyOut(); + } + } + + float mMaxPenetrationDepth; + bool mHadCollision = false; + }; + + // Test if anything is in the way of switching + RVec3 char_pos = GetPosition(inLockBodies); + MyCollector collector(inMaxPenetrationDepth); + CheckCollision(inShape, 0.0f, char_pos, collector, inLockBodies); + if (collector.mHadCollision) + return false; + } + + // Switch the shape + mShape = inShape; + sGetBodyInterface(mSystem, inLockBodies).SetShape(mBodyID, mShape, false, EActivation::Activate); + return true; +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Character/Character.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Character/Character.h new file mode 100644 index 00000000000..a47d75581b0 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Character/Character.h @@ -0,0 +1,139 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +JPH_NAMESPACE_BEGIN + +/// Contains the configuration of a character +class JPH_EXPORT CharacterSettings : public CharacterBaseSettings +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Layer that this character will be added to + ObjectLayer mLayer = 0; + + /// Mass of the character + float mMass = 80.0f; + + /// Friction for the character + float mFriction = 0.2f; + + /// Value to multiply gravity with for this character + float mGravityFactor = 1.0f; +}; + +/// Runtime character object. +/// This object usually represents the player or a humanoid AI. It uses a single rigid body, +/// usually with a capsule shape to simulate movement and collision for the character. +/// The character is a keyframed object, the application controls it by setting the velocity. +class JPH_EXPORT Character : public CharacterBase +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Constructor + /// @param inSettings The settings for the character + /// @param inPosition Initial position for the character + /// @param inRotation Initial rotation for the character (usually only around Y) + /// @param inUserData Application specific value + /// @param inSystem Physics system that this character will be added to later + Character(const CharacterSettings *inSettings, RVec3Arg inPosition, QuatArg inRotation, uint64 inUserData, PhysicsSystem *inSystem); + + /// Destructor + virtual ~Character() override; + + /// Add bodies and constraints to the system and optionally activate the bodies + void AddToPhysicsSystem(EActivation inActivationMode = EActivation::Activate, bool inLockBodies = true); + + /// Remove bodies and constraints from the system + void RemoveFromPhysicsSystem(bool inLockBodies = true); + + /// Wake up the character + void Activate(bool inLockBodies = true); + + /// Needs to be called after every PhysicsSystem::Update + /// @param inMaxSeparationDistance Max distance between the floor and the character to still consider the character standing on the floor + /// @param inLockBodies If the collision query should use the locking body interface (true) or the non locking body interface (false) + void PostSimulation(float inMaxSeparationDistance, bool inLockBodies = true); + + /// Control the velocity of the character + void SetLinearAndAngularVelocity(Vec3Arg inLinearVelocity, Vec3Arg inAngularVelocity, bool inLockBodies = true); + + /// Get the linear velocity of the character (m / s) + Vec3 GetLinearVelocity(bool inLockBodies = true) const; + + /// Set the linear velocity of the character (m / s) + void SetLinearVelocity(Vec3Arg inLinearVelocity, bool inLockBodies = true); + + /// Add world space linear velocity to current velocity (m / s) + void AddLinearVelocity(Vec3Arg inLinearVelocity, bool inLockBodies = true); + + /// Add impulse to the center of mass of the character + void AddImpulse(Vec3Arg inImpulse, bool inLockBodies = true); + + /// Get the body associated with this character + BodyID GetBodyID() const { return mBodyID; } + + /// Get position / rotation of the body + void GetPositionAndRotation(RVec3 &outPosition, Quat &outRotation, bool inLockBodies = true) const; + + /// Set the position / rotation of the body, optionally activating it. + void SetPositionAndRotation(RVec3Arg inPosition, QuatArg inRotation, EActivation inActivationMode = EActivation::Activate, bool inLockBodies = true) const; + + /// Get the position of the character + RVec3 GetPosition(bool inLockBodies = true) const; + + /// Set the position of the character, optionally activating it. + void SetPosition(RVec3Arg inPostion, EActivation inActivationMode = EActivation::Activate, bool inLockBodies = true); + + /// Get the rotation of the character + Quat GetRotation(bool inLockBodies = true) const; + + /// Set the rotation of the character, optionally activating it. + void SetRotation(QuatArg inRotation, EActivation inActivationMode = EActivation::Activate, bool inLockBodies = true); + + /// Position of the center of mass of the underlying rigid body + RVec3 GetCenterOfMassPosition(bool inLockBodies = true) const; + + /// Calculate the world transform of the character + RMat44 GetWorldTransform(bool inLockBodies = true) const; + + /// Update the layer of the character + void SetLayer(ObjectLayer inLayer, bool inLockBodies = true); + + /// Switch the shape of the character (e.g. for stance). When inMaxPenetrationDepth is not FLT_MAX, it checks + /// if the new shape collides before switching shape. Returns true if the switch succeeded. + bool SetShape(const Shape *inShape, float inMaxPenetrationDepth, bool inLockBodies = true); + + /// @brief Get all contacts for the character at a particular location + /// @param inPosition Position to test. + /// @param inRotation Rotation at which to test the shape. + /// @param inMovementDirection A hint in which direction the character is moving, will be used to calculate a proper normal. + /// @param inMaxSeparationDistance How much distance around the character you want to report contacts in (can be 0 to match the character exactly). + /// @param inShape Shape to test collision with. + /// @param inBaseOffset All hit results will be returned relative to this offset, can be zero to get results in world position, but when you're testing far from the origin you get better precision by picking a position that's closer e.g. GetPosition() since floats are most accurate near the origin + /// @param ioCollector Collision collector that receives the collision results. + /// @param inLockBodies If the collision query should use the locking body interface (true) or the non locking body interface (false) + void CheckCollision(RVec3Arg inPosition, QuatArg inRotation, Vec3Arg inMovementDirection, float inMaxSeparationDistance, const Shape *inShape, RVec3Arg inBaseOffset, CollideShapeCollector &ioCollector, bool inLockBodies = true) const; + +private: + /// Check collisions between inShape and the world using the center of mass transform + void CheckCollision(RMat44Arg inCenterOfMassTransform, Vec3Arg inMovementDirection, float inMaxSeparationDistance, const Shape *inShape, RVec3Arg inBaseOffset, CollideShapeCollector &ioCollector, bool inLockBodies) const; + + /// Check collisions between inShape and the world using the current position / rotation of the character + void CheckCollision(const Shape *inShape, float inMaxSeparationDistance, RVec3Arg inBaseOffset, CollideShapeCollector &ioCollector, bool inLockBodies) const; + + /// The body of this character + BodyID mBodyID; + + /// The layer the body is in + ObjectLayer mLayer; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Character/CharacterBase.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Character/CharacterBase.cpp new file mode 100644 index 00000000000..d3142506509 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Character/CharacterBase.cpp @@ -0,0 +1,59 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include + +JPH_NAMESPACE_BEGIN + +CharacterBase::CharacterBase(const CharacterBaseSettings *inSettings, PhysicsSystem *inSystem) : + mSystem(inSystem), + mShape(inSettings->mShape), + mUp(inSettings->mUp), + mSupportingVolume(inSettings->mSupportingVolume) +{ + // Initialize max slope angle + SetMaxSlopeAngle(inSettings->mMaxSlopeAngle); +} + +const char *CharacterBase::sToString(EGroundState inState) +{ + switch (inState) + { + case EGroundState::OnGround: return "OnGround"; + case EGroundState::OnSteepGround: return "OnSteepGround"; + case EGroundState::NotSupported: return "NotSupported"; + case EGroundState::InAir: return "InAir"; + } + + JPH_ASSERT(false); + return "Unknown"; +} + +void CharacterBase::SaveState(StateRecorder &inStream) const +{ + inStream.Write(mGroundState); + inStream.Write(mGroundBodyID); + inStream.Write(mGroundBodySubShapeID); + inStream.Write(mGroundPosition); + inStream.Write(mGroundNormal); + inStream.Write(mGroundVelocity); + // Can't save user data (may be a pointer) and material +} + +void CharacterBase::RestoreState(StateRecorder &inStream) +{ + inStream.Read(mGroundState); + inStream.Read(mGroundBodyID); + inStream.Read(mGroundBodySubShapeID); + inStream.Read(mGroundPosition); + inStream.Read(mGroundNormal); + inStream.Read(mGroundVelocity); + mGroundUserData = 0; // Cannot restore user data + mGroundMaterial = PhysicsMaterial::sDefault; // Cannot restore material +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Character/CharacterBase.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Character/CharacterBase.h new file mode 100644 index 00000000000..2603352d4cf --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Character/CharacterBase.h @@ -0,0 +1,154 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +class PhysicsSystem; +class StateRecorder; + +/// Base class for configuration of a character +class JPH_EXPORT CharacterBaseSettings : public RefTarget +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Constructor + CharacterBaseSettings() = default; + CharacterBaseSettings(const CharacterBaseSettings &inSettings) = default; + CharacterBaseSettings & operator = (const CharacterBaseSettings &inSettings) = default; + + /// Virtual destructor + virtual ~CharacterBaseSettings() = default; + + /// Vector indicating the up direction of the character + Vec3 mUp = Vec3::sAxisY(); + + /// Plane, defined in local space relative to the character. Every contact behind this plane can support the + /// character, every contact in front of this plane is treated as only colliding with the player. + /// Default: Accept any contact. + Plane mSupportingVolume { Vec3::sAxisY(), -1.0e10f }; + + /// Maximum angle of slope that character can still walk on (radians). + float mMaxSlopeAngle = DegreesToRadians(50.0f); + + /// Initial shape that represents the character's volume. + /// Usually this is a capsule, make sure the shape is made so that the bottom of the shape is at (0, 0, 0). + RefConst mShape; +}; + +/// Base class for character class +class JPH_EXPORT CharacterBase : public RefTarget, public NonCopyable +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Constructor + CharacterBase(const CharacterBaseSettings *inSettings, PhysicsSystem *inSystem); + + /// Destructor + virtual ~CharacterBase() = default; + + /// Set the maximum angle of slope that character can still walk on (radians) + void SetMaxSlopeAngle(float inMaxSlopeAngle) { mCosMaxSlopeAngle = Cos(inMaxSlopeAngle); } + float GetCosMaxSlopeAngle() const { return mCosMaxSlopeAngle; } + + /// Set the up vector for the character + void SetUp(Vec3Arg inUp) { mUp = inUp; } + Vec3 GetUp() const { return mUp; } + + /// Check if the normal of the ground surface is too steep to walk on + bool IsSlopeTooSteep(Vec3Arg inNormal) const + { + // If cos max slope angle is close to one the system is turned off, + // otherwise check the angle between the up and normal vector + return mCosMaxSlopeAngle < cNoMaxSlopeAngle && inNormal.Dot(mUp) < mCosMaxSlopeAngle; + } + + /// Get the current shape that the character is using. + const Shape * GetShape() const { return mShape; } + + enum class EGroundState + { + OnGround, ///< Character is on the ground and can move freely. + OnSteepGround, ///< Character is on a slope that is too steep and can't climb up any further. The caller should start applying downward velocity if sliding from the slope is desired. + NotSupported, ///< Character is touching an object, but is not supported by it and should fall. The GetGroundXXX functions will return information about the touched object. + InAir, ///< Character is in the air and is not touching anything. + }; + + /// Debug function to convert enum values to string + static const char * sToString(EGroundState inState); + + ///@name Properties of the ground this character is standing on + + /// Current ground state + EGroundState GetGroundState() const { return mGroundState; } + + /// Returns true if the player is supported by normal or steep ground + bool IsSupported() const { return mGroundState == EGroundState::OnGround || mGroundState == EGroundState::OnSteepGround; } + + /// Get the contact point with the ground + RVec3 GetGroundPosition() const { return mGroundPosition; } + + /// Get the contact normal with the ground + Vec3 GetGroundNormal() const { return mGroundNormal; } + + /// Velocity in world space of ground + Vec3 GetGroundVelocity() const { return mGroundVelocity; } + + /// Material that the character is standing on + const PhysicsMaterial * GetGroundMaterial() const { return mGroundMaterial; } + + /// BodyID of the object the character is standing on. Note may have been removed! + BodyID GetGroundBodyID() const { return mGroundBodyID; } + + /// Sub part of the body that we're standing on. + SubShapeID GetGroundSubShapeID() const { return mGroundBodySubShapeID; } + + /// User data value of the body that we're standing on + uint64 GetGroundUserData() const { return mGroundUserData; } + + // Saving / restoring state for replay + virtual void SaveState(StateRecorder &inStream) const; + virtual void RestoreState(StateRecorder &inStream); + +protected: + // Cached physics system + PhysicsSystem * mSystem; + + // The shape that the body currently has + RefConst mShape; + + // The character's world space up axis + Vec3 mUp; + + // Every contact behind this plane can support the character + Plane mSupportingVolume; + + // Beyond this value there is no max slope + static constexpr float cNoMaxSlopeAngle = 0.9999f; + + // Cosine of the maximum angle of slope that character can still walk on + float mCosMaxSlopeAngle; + + // Ground properties + EGroundState mGroundState = EGroundState::InAir; + BodyID mGroundBodyID; + SubShapeID mGroundBodySubShapeID; + RVec3 mGroundPosition = RVec3::sZero(); + Vec3 mGroundNormal = Vec3::sZero(); + Vec3 mGroundVelocity = Vec3::sZero(); + RefConst mGroundMaterial = PhysicsMaterial::sDefault; + uint64 mGroundUserData = 0; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Character/CharacterVirtual.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Character/CharacterVirtual.cpp new file mode 100644 index 00000000000..b4b12ecfd29 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Character/CharacterVirtual.cpp @@ -0,0 +1,1512 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef JPH_DEBUG_RENDERER + #include +#endif // JPH_DEBUG_RENDERER + +JPH_NAMESPACE_BEGIN + +CharacterVirtual::CharacterVirtual(const CharacterVirtualSettings *inSettings, RVec3Arg inPosition, QuatArg inRotation, uint64 inUserData, PhysicsSystem *inSystem) : + CharacterBase(inSettings, inSystem), + mBackFaceMode(inSettings->mBackFaceMode), + mPredictiveContactDistance(inSettings->mPredictiveContactDistance), + mMaxCollisionIterations(inSettings->mMaxCollisionIterations), + mMaxConstraintIterations(inSettings->mMaxConstraintIterations), + mMinTimeRemaining(inSettings->mMinTimeRemaining), + mCollisionTolerance(inSettings->mCollisionTolerance), + mCharacterPadding(inSettings->mCharacterPadding), + mMaxNumHits(inSettings->mMaxNumHits), + mHitReductionCosMaxAngle(inSettings->mHitReductionCosMaxAngle), + mPenetrationRecoverySpeed(inSettings->mPenetrationRecoverySpeed), + mShapeOffset(inSettings->mShapeOffset), + mPosition(inPosition), + mRotation(inRotation), + mUserData(inUserData) +{ + // Copy settings + SetMaxStrength(inSettings->mMaxStrength); + SetMass(inSettings->mMass); +} + +void CharacterVirtual::GetAdjustedBodyVelocity(const Body& inBody, Vec3 &outLinearVelocity, Vec3 &outAngularVelocity) const +{ + // Get real velocity of body + if (!inBody.IsStatic()) + { + const MotionProperties *mp = inBody.GetMotionPropertiesUnchecked(); + outLinearVelocity = mp->GetLinearVelocity(); + outAngularVelocity = mp->GetAngularVelocity(); + } + else + { + outLinearVelocity = outAngularVelocity = Vec3::sZero(); + } + + // Allow application to override + if (mListener != nullptr) + mListener->OnAdjustBodyVelocity(this, inBody, outLinearVelocity, outAngularVelocity); +} + +Vec3 CharacterVirtual::CalculateCharacterGroundVelocity(RVec3Arg inCenterOfMass, Vec3Arg inLinearVelocity, Vec3Arg inAngularVelocity, float inDeltaTime) const +{ + // Get angular velocity + float angular_velocity_len_sq = inAngularVelocity.LengthSq(); + if (angular_velocity_len_sq < 1.0e-12f) + return inLinearVelocity; + float angular_velocity_len = sqrt(angular_velocity_len_sq); + + // Calculate the rotation that the object will make in the time step + Quat rotation = Quat::sRotation(inAngularVelocity / angular_velocity_len, angular_velocity_len * inDeltaTime); + + // Calculate where the new character position will be + RVec3 new_position = inCenterOfMass + rotation * Vec3(mPosition - inCenterOfMass); + + // Calculate the velocity + return inLinearVelocity + Vec3(new_position - mPosition) / inDeltaTime; +} + +template +void CharacterVirtual::sFillContactProperties(const CharacterVirtual *inCharacter, Contact &outContact, const Body &inBody, Vec3Arg inUp, RVec3Arg inBaseOffset, const taCollector &inCollector, const CollideShapeResult &inResult) +{ + // Get adjusted body velocity + Vec3 linear_velocity, angular_velocity; + inCharacter->GetAdjustedBodyVelocity(inBody, linear_velocity, angular_velocity); + + outContact.mPosition = inBaseOffset + inResult.mContactPointOn2; + outContact.mLinearVelocity = linear_velocity + angular_velocity.Cross(Vec3(outContact.mPosition - inBody.GetCenterOfMassPosition())); // Calculate point velocity + outContact.mContactNormal = -inResult.mPenetrationAxis.NormalizedOr(Vec3::sZero()); + outContact.mSurfaceNormal = inCollector.GetContext()->GetWorldSpaceSurfaceNormal(inResult.mSubShapeID2, outContact.mPosition); + if (outContact.mContactNormal.Dot(outContact.mSurfaceNormal) < 0.0f) + outContact.mSurfaceNormal = -outContact.mSurfaceNormal; // Flip surface normal if we're hitting a back face + if (outContact.mContactNormal.Dot(inUp) > outContact.mSurfaceNormal.Dot(inUp)) + outContact.mSurfaceNormal = outContact.mContactNormal; // Replace surface normal with contact normal if the contact normal is pointing more upwards + outContact.mDistance = -inResult.mPenetrationDepth; + outContact.mBodyB = inResult.mBodyID2; + outContact.mSubShapeIDB = inResult.mSubShapeID2; + outContact.mMotionTypeB = inBody.GetMotionType(); + outContact.mIsSensorB = inBody.IsSensor(); + outContact.mUserData = inBody.GetUserData(); + outContact.mMaterial = inCollector.GetContext()->GetMaterial(inResult.mSubShapeID2); +} + +void CharacterVirtual::ContactCollector::AddHit(const CollideShapeResult &inResult) +{ + // If we exceed our contact limit, try to clean up near-duplicate contacts + if (mContacts.size() == mMaxHits) + { + // Flag that we hit this code path + mMaxHitsExceeded = true; + + // Check if we can do reduction + if (mHitReductionCosMaxAngle > -1.0f) + { + // Loop all contacts and find similar contacts + for (int i = (int)mContacts.size() - 1; i >= 0; --i) + { + Contact &contact_i = mContacts[i]; + for (int j = i - 1; j >= 0; --j) + { + Contact &contact_j = mContacts[j]; + if (contact_i.mBodyB == contact_j.mBodyB // Same body + && contact_i.mContactNormal.Dot(contact_j.mContactNormal) > mHitReductionCosMaxAngle) // Very similar contact normals + { + // Remove the contact with the biggest distance + bool i_is_last = i == (int)mContacts.size() - 1; + if (contact_i.mDistance > contact_j.mDistance) + { + // Remove i + if (!i_is_last) + contact_i = mContacts.back(); + mContacts.pop_back(); + + // Break out of the loop, i is now an element that we already processed + break; + } + else + { + // Remove j + contact_j = mContacts.back(); + mContacts.pop_back(); + + // If i was the last element, we just moved it into position j. Break out of the loop, we'll see it again later. + if (i_is_last) + break; + } + } + } + } + } + + if (mContacts.size() == mMaxHits) + { + // There are still too many hits, give up! + ForceEarlyOut(); + return; + } + } + + BodyLockRead lock(mSystem->GetBodyLockInterface(), inResult.mBodyID2); + if (lock.SucceededAndIsInBroadPhase()) + { + mContacts.emplace_back(); + Contact &contact = mContacts.back(); + sFillContactProperties(mCharacter, contact, lock.GetBody(), mUp, mBaseOffset, *this, inResult); + contact.mFraction = 0.0f; + } +} + +void CharacterVirtual::ContactCastCollector::AddHit(const ShapeCastResult &inResult) +{ + // Should not have gotten here without a lower fraction + JPH_ASSERT(inResult.mFraction < mContact.mFraction); + + if (inResult.mFraction > 0.0f // Ignore collisions at fraction = 0 + && inResult.mPenetrationAxis.Dot(mDisplacement) > 0.0f) // Ignore penetrations that we're moving away from + { + // Test if this contact should be ignored + for (const IgnoredContact &c : mIgnoredContacts) + if (c.mBodyID == inResult.mBodyID2 && c.mSubShapeID == inResult.mSubShapeID2) + return; + + Contact contact; + + // Lock body only while we fetch contact properties + { + BodyLockRead lock(mSystem->GetBodyLockInterface(), inResult.mBodyID2); + if (!lock.SucceededAndIsInBroadPhase()) + return; + + // Sweeps don't result in OnContactAdded callbacks so we can ignore sensors here + const Body &body = lock.GetBody(); + if (body.IsSensor()) + return; + + // Convert the hit result into a contact + sFillContactProperties(mCharacter, contact, body, mUp, mBaseOffset, *this, inResult); + } + + contact.mFraction = inResult.mFraction; + + // Check if the contact that will make us penetrate more than the allowed tolerance + if (contact.mDistance + contact.mContactNormal.Dot(mDisplacement) < -mCharacter->mCollisionTolerance + && mCharacter->ValidateContact(contact)) + { + mContact = contact; + UpdateEarlyOutFraction(contact.mFraction); + } + } +} + +void CharacterVirtual::CheckCollision(RVec3Arg inPosition, QuatArg inRotation, Vec3Arg inMovementDirection, float inMaxSeparationDistance, const Shape *inShape, RVec3Arg inBaseOffset, CollideShapeCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter) const +{ + // Query shape transform + RMat44 transform = GetCenterOfMassTransform(inPosition, inRotation, inShape); + + // Settings for collide shape + CollideShapeSettings settings; + settings.mActiveEdgeMode = EActiveEdgeMode::CollideOnlyWithActive; + settings.mBackFaceMode = mBackFaceMode; + settings.mActiveEdgeMovementDirection = inMovementDirection; + settings.mMaxSeparationDistance = mCharacterPadding + inMaxSeparationDistance; + + // Collide shape + mSystem->GetNarrowPhaseQuery().CollideShape(inShape, Vec3::sReplicate(1.0f), transform, settings, inBaseOffset, ioCollector, inBroadPhaseLayerFilter, inObjectLayerFilter, inBodyFilter, inShapeFilter); +} + +void CharacterVirtual::GetContactsAtPosition(RVec3Arg inPosition, Vec3Arg inMovementDirection, const Shape *inShape, TempContactList &outContacts, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter) const +{ + // Remove previous results + outContacts.clear(); + + // Collide shape + ContactCollector collector(mSystem, this, mMaxNumHits, mHitReductionCosMaxAngle, mUp, mPosition, outContacts); + CheckCollision(inPosition, mRotation, inMovementDirection, mPredictiveContactDistance, inShape, mPosition, collector, inBroadPhaseLayerFilter, inObjectLayerFilter, inBodyFilter, inShapeFilter); + + // The broadphase bounding boxes will not be deterministic, which means that the order in which the contacts are received by the collector is not deterministic. + // Therefore we need to sort the contacts to preserve determinism. Note that currently this will fail if we exceed mMaxNumHits hits. + QuickSort(outContacts.begin(), outContacts.end(), ContactOrderingPredicate()); + + // Flag if we exceeded the max number of hits + mMaxHitsExceeded = collector.mMaxHitsExceeded; + + // Reduce distance to contact by padding to ensure we stay away from the object by a little margin + // (this will make collision detection cheaper - especially for sweep tests as they won't hit the surface if we're properly sliding) + for (Contact &c : outContacts) + c.mDistance -= mCharacterPadding; +} + +void CharacterVirtual::RemoveConflictingContacts(TempContactList &ioContacts, IgnoredContactList &outIgnoredContacts) const +{ + // Only use this algorithm if we're penetrating further than this (due to numerical precision issues we can always penetrate a little bit and we don't want to discard contacts if they just have a tiny penetration) + // We do need to account for padding (see GetContactsAtPosition) that is removed from the contact distances, to compensate we add it to the cMinRequiredPenetration + const float cMinRequiredPenetration = 1.25f * mCharacterPadding; + + // Discard conflicting penetrating contacts + for (size_t c1 = 0; c1 < ioContacts.size(); c1++) + { + Contact &contact1 = ioContacts[c1]; + if (contact1.mDistance <= -cMinRequiredPenetration) // Only for penetrations + for (size_t c2 = c1 + 1; c2 < ioContacts.size(); c2++) + { + Contact &contact2 = ioContacts[c2]; + if (contact1.mBodyB == contact2.mBodyB // Only same body + && contact2.mDistance <= -cMinRequiredPenetration // Only for penetrations + && contact1.mContactNormal.Dot(contact2.mContactNormal) < 0.0f) // Only opposing normals + { + // Discard contacts with the least amount of penetration + if (contact1.mDistance < contact2.mDistance) + { + // Discard the 2nd contact + outIgnoredContacts.emplace_back(contact2.mBodyB, contact2.mSubShapeIDB); + ioContacts.erase(ioContacts.begin() + c2); + c2--; + } + else + { + // Discard the first contact + outIgnoredContacts.emplace_back(contact1.mBodyB, contact1.mSubShapeIDB); + ioContacts.erase(ioContacts.begin() + c1); + c1--; + break; + } + } + } + } +} + +bool CharacterVirtual::ValidateContact(const Contact &inContact) const +{ + if (mListener == nullptr) + return true; + + return mListener->OnContactValidate(this, inContact.mBodyB, inContact.mSubShapeIDB); +} + +template +inline static bool sCorrectFractionForCharacterPadding(const Shape *inShape, Mat44Arg inStart, Vec3Arg inDisplacement, const T &inPolygon, float &ioFraction) +{ + if (inShape->GetType() == EShapeType::Convex) + { + // Get the support function for the shape we're casting + const ConvexShape *convex_shape = static_cast(inShape); + ConvexShape::SupportBuffer buffer; + const ConvexShape::Support *support = convex_shape->GetSupportFunction(ConvexShape::ESupportMode::IncludeConvexRadius, buffer, Vec3::sReplicate(1.0f)); + + // Cast the shape against the polygon + GJKClosestPoint gjk; + return gjk.CastShape(inStart, inDisplacement, cDefaultCollisionTolerance, *support, inPolygon, ioFraction); + } + else if (inShape->GetSubType() == EShapeSubType::RotatedTranslated) + { + const RotatedTranslatedShape *rt_shape = static_cast(inShape); + return sCorrectFractionForCharacterPadding(rt_shape->GetInnerShape(), inStart * Mat44::sRotation(rt_shape->GetRotation()), inDisplacement, inPolygon, ioFraction); + } + else + { + JPH_ASSERT(false, "Not supported yet!"); + return false; + } +} + +bool CharacterVirtual::GetFirstContactForSweep(RVec3Arg inPosition, Vec3Arg inDisplacement, Contact &outContact, const IgnoredContactList &inIgnoredContacts, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter) const +{ + // Too small distance -> skip checking + float displacement_len_sq = inDisplacement.LengthSq(); + if (displacement_len_sq < 1.0e-8f) + return false; + + // Calculate start transform + RMat44 start = GetCenterOfMassTransform(inPosition, mRotation, mShape); + + // Settings for the cast + ShapeCastSettings settings; + settings.mBackFaceModeTriangles = mBackFaceMode; + settings.mBackFaceModeConvex = EBackFaceMode::IgnoreBackFaces; + settings.mActiveEdgeMode = EActiveEdgeMode::CollideOnlyWithActive; + settings.mUseShrunkenShapeAndConvexRadius = true; + settings.mReturnDeepestPoint = false; + + // Calculate how much extra fraction we need to add to the cast to account for the character padding + float character_padding_fraction = mCharacterPadding / sqrt(displacement_len_sq); + + // Cast shape + Contact contact; + contact.mFraction = 1.0f + character_padding_fraction; + ContactCastCollector collector(mSystem, this, inDisplacement, mUp, inIgnoredContacts, start.GetTranslation(), contact); + collector.ResetEarlyOutFraction(contact.mFraction); + RShapeCast shape_cast(mShape, Vec3::sReplicate(1.0f), start, inDisplacement); + mSystem->GetNarrowPhaseQuery().CastShape(shape_cast, settings, start.GetTranslation(), collector, inBroadPhaseLayerFilter, inObjectLayerFilter, inBodyFilter, inShapeFilter); + if (contact.mBodyB.IsInvalid()) + return false; + + // Store contact + outContact = contact; + + // Fetch the face we're colliding with + TransformedShape ts = mSystem->GetBodyInterface().GetTransformedShape(outContact.mBodyB); + Shape::SupportingFace face; + ts.GetSupportingFace(outContact.mSubShapeIDB, -outContact.mContactNormal, start.GetTranslation(), face); + + bool corrected = false; + if (face.size() >= 2) + { + // Inflate the colliding face by the character padding + PolygonConvexSupport polygon(face); + AddConvexRadius add_cvx(polygon, mCharacterPadding); + + // Correct fraction to hit this inflated face instead of the inner shape + corrected = sCorrectFractionForCharacterPadding(mShape, start.GetRotation(), inDisplacement, add_cvx, outContact.mFraction); + } + if (!corrected) + { + // When there's only a single contact point or when we were unable to correct the fraction, + // we can just move the fraction back so that the character and its padding don't hit the contact point anymore + outContact.mFraction = max(0.0f, outContact.mFraction - character_padding_fraction); + } + + // Ensure that we never return a fraction that's bigger than 1 (which could happen due to float precision issues). + outContact.mFraction = min(outContact.mFraction, 1.0f); + + return true; +} + +void CharacterVirtual::DetermineConstraints(TempContactList &inContacts, float inDeltaTime, ConstraintList &outConstraints) const +{ + for (Contact &c : inContacts) + { + Vec3 contact_velocity = c.mLinearVelocity; + + // Penetrating contact: Add a contact velocity that pushes the character out at the desired speed + if (c.mDistance < 0.0f) + contact_velocity -= c.mContactNormal * c.mDistance * mPenetrationRecoverySpeed / inDeltaTime; + + // Convert to a constraint + outConstraints.emplace_back(); + Constraint &constraint = outConstraints.back(); + constraint.mContact = &c; + constraint.mLinearVelocity = contact_velocity; + constraint.mPlane = Plane(c.mContactNormal, c.mDistance); + + // Next check if the angle is too steep and if it is add an additional constraint that holds the character back + if (IsSlopeTooSteep(c.mSurfaceNormal)) + { + // Only take planes that point up. + // Note that we use the contact normal to allow for better sliding as the surface normal may be in the opposite direction of movement. + float dot = c.mContactNormal.Dot(mUp); + if (dot > 1.0e-3f) // Add a little slack, if the normal is perfectly horizontal we already have our vertical plane. + { + // Mark the slope constraint as steep + constraint.mIsSteepSlope = true; + + // Make horizontal normal + Vec3 normal = (c.mContactNormal - dot * mUp).Normalized(); + + // Create a secondary constraint that blocks horizontal movement + outConstraints.emplace_back(); + Constraint &vertical_constraint = outConstraints.back(); + vertical_constraint.mContact = &c; + vertical_constraint.mLinearVelocity = contact_velocity.Dot(normal) * normal; // Project the contact velocity on the new normal so that both planes push at an equal rate + vertical_constraint.mPlane = Plane(normal, c.mDistance / normal.Dot(c.mContactNormal)); // Calculate the distance we have to travel horizontally to hit the contact plane + } + } + } +} + +bool CharacterVirtual::HandleContact(Vec3Arg inVelocity, Constraint &ioConstraint, float inDeltaTime) const +{ + Contact &contact = *ioConstraint.mContact; + + // Validate the contact point + if (!ValidateContact(contact)) + return false; + + // Send contact added event + CharacterContactSettings settings; + if (mListener != nullptr) + mListener->OnContactAdded(this, contact.mBodyB, contact.mSubShapeIDB, contact.mPosition, -contact.mContactNormal, settings); + contact.mCanPushCharacter = settings.mCanPushCharacter; + + // We don't have any further interaction with sensors beyond an OnContactAdded notification + if (contact.mIsSensorB) + return false; + + // If body B cannot receive an impulse, we're done + if (!settings.mCanReceiveImpulses || contact.mMotionTypeB != EMotionType::Dynamic) + return true; + + // Lock the body we're colliding with + BodyLockWrite lock(mSystem->GetBodyLockInterface(), contact.mBodyB); + if (!lock.SucceededAndIsInBroadPhase()) + return false; // Body has been removed, we should not collide with it anymore + const Body &body = lock.GetBody(); + + // Calculate the velocity that we want to apply at B so that it will start moving at the character's speed at the contact point + constexpr float cDamping = 0.9f; + constexpr float cPenetrationResolution = 0.4f; + Vec3 relative_velocity = inVelocity - contact.mLinearVelocity; + float projected_velocity = relative_velocity.Dot(contact.mContactNormal); + float delta_velocity = -projected_velocity * cDamping - min(contact.mDistance, 0.0f) * cPenetrationResolution / inDeltaTime; + + // Don't apply impulses if we're separating + if (delta_velocity < 0.0f) + return true; + + // Determine mass properties of the body we're colliding with + const MotionProperties *motion_properties = body.GetMotionProperties(); + RVec3 center_of_mass = body.GetCenterOfMassPosition(); + Mat44 inverse_inertia = body.GetInverseInertia(); + float inverse_mass = motion_properties->GetInverseMass(); + + // Calculate the inverse of the mass of body B as seen at the contact point in the direction of the contact normal + Vec3 jacobian = Vec3(contact.mPosition - center_of_mass).Cross(contact.mContactNormal); + float inv_effective_mass = inverse_inertia.Multiply3x3(jacobian).Dot(jacobian) + inverse_mass; + + // Impulse P = M dv + float impulse = delta_velocity / inv_effective_mass; + + // Clamp the impulse according to the character strength, character strength is a force in newtons, P = F dt + float max_impulse = mMaxStrength * inDeltaTime; + impulse = min(impulse, max_impulse); + + // Calculate the world space impulse to apply + Vec3 world_impulse = -impulse * contact.mContactNormal; + + // Cancel impulse in down direction (we apply gravity later) + float impulse_dot_up = world_impulse.Dot(mUp); + if (impulse_dot_up < 0.0f) + world_impulse -= impulse_dot_up * mUp; + + // Now apply the impulse (body is already locked so we use the no-lock interface) + mSystem->GetBodyInterfaceNoLock().AddImpulse(contact.mBodyB, world_impulse, contact.mPosition); + return true; +} + +void CharacterVirtual::SolveConstraints(Vec3Arg inVelocity, float inDeltaTime, float inTimeRemaining, ConstraintList &ioConstraints, IgnoredContactList &ioIgnoredContacts, float &outTimeSimulated, Vec3 &outDisplacement, TempAllocator &inAllocator +#ifdef JPH_DEBUG_RENDERER + , bool inDrawConstraints +#endif // JPH_DEBUG_RENDERER + ) const +{ + // If there are no constraints we can immediately move to our target + if (ioConstraints.empty()) + { + outDisplacement = inVelocity * inTimeRemaining; + outTimeSimulated = inTimeRemaining; + return; + } + + // Create array that holds the constraints in order of time of impact (sort will happen later) + std::vector> sorted_constraints(inAllocator); + sorted_constraints.resize(ioConstraints.size()); + for (size_t index = 0; index < sorted_constraints.size(); index++) + sorted_constraints[index] = &ioConstraints[index]; + + // This is the velocity we use for the displacement, if we hit something it will be shortened + Vec3 velocity = inVelocity; + + // Keep track of the last velocity that was applied to the character so that we can detect when the velocity reverses + Vec3 last_velocity = inVelocity; + + // Start with no displacement + outDisplacement = Vec3::sZero(); + outTimeSimulated = 0.0f; + + // These are the contacts that we hit previously without moving a significant distance + std::vector> previous_contacts(inAllocator); + previous_contacts.resize(mMaxConstraintIterations); + int num_previous_contacts = 0; + + // Loop for a max amount of iterations + for (uint iteration = 0; iteration < mMaxConstraintIterations; iteration++) + { + // Calculate time of impact for all constraints + for (Constraint &c : ioConstraints) + { + // Project velocity on plane direction + c.mProjectedVelocity = c.mPlane.GetNormal().Dot(c.mLinearVelocity - velocity); + if (c.mProjectedVelocity < 1.0e-6f) + { + c.mTOI = FLT_MAX; + } + else + { + // Distance to plane + float dist = c.mPlane.SignedDistance(outDisplacement); + + if (dist - c.mProjectedVelocity * inTimeRemaining > -1.0e-4f) + { + // Too little penetration, accept the movement + c.mTOI = FLT_MAX; + } + else + { + // Calculate time of impact + c.mTOI = max(0.0f, dist / c.mProjectedVelocity); + } + } + } + + // Sort constraints on proximity + QuickSort(sorted_constraints.begin(), sorted_constraints.end(), [](const Constraint *inLHS, const Constraint *inRHS) { + // If both constraints hit at t = 0 then order the one that will push the character furthest first + // Note that because we add velocity to penetrating contacts, this will also resolve contacts that penetrate the most + if (inLHS->mTOI <= 0.0f && inRHS->mTOI <= 0.0f) + return inLHS->mProjectedVelocity > inRHS->mProjectedVelocity; + + // Then sort on time of impact + if (inLHS->mTOI != inRHS->mTOI) + return inLHS->mTOI < inRHS->mTOI; + + // As a tie breaker sort static first so it has the most influence + return inLHS->mContact->mMotionTypeB > inRHS->mContact->mMotionTypeB; + }); + + // Find the first valid constraint + Constraint *constraint = nullptr; + for (Constraint *c : sorted_constraints) + { + // Take the first contact and see if we can reach it + if (c->mTOI >= inTimeRemaining) + { + // We can reach our goal! + outDisplacement += velocity * inTimeRemaining; + outTimeSimulated += inTimeRemaining; + return; + } + + // Test if this contact was discarded by the contact callback before + if (c->mContact->mWasDiscarded) + continue; + + // Check if we made contact with this before + if (!c->mContact->mHadCollision) + { + // Handle the contact + if (!HandleContact(velocity, *c, inDeltaTime)) + { + // Constraint should be ignored, remove it from the list + c->mContact->mWasDiscarded = true; + + // Mark it as ignored for GetFirstContactForSweep + ioIgnoredContacts.emplace_back(c->mContact->mBodyB, c->mContact->mSubShapeIDB); + continue; + } + + c->mContact->mHadCollision = true; + } + + // Cancel velocity of constraint if it cannot push the character + if (!c->mContact->mCanPushCharacter) + c->mLinearVelocity = Vec3::sZero(); + + // We found the first constraint that we want to collide with + constraint = c; + break; + } + + if (constraint == nullptr) + { + // All constraints were discarded, we can reach our goal! + outDisplacement += velocity * inTimeRemaining; + outTimeSimulated += inTimeRemaining; + return; + } + + // Move to the contact + outDisplacement += velocity * constraint->mTOI; + inTimeRemaining -= constraint->mTOI; + outTimeSimulated += constraint->mTOI; + + // If there's not enough time left to be simulated, bail + if (inTimeRemaining < mMinTimeRemaining) + return; + + // If we've moved significantly, clear all previous contacts + if (constraint->mTOI > 1.0e-4f) + num_previous_contacts = 0; + + // Get the normal of the plane we're hitting + Vec3 plane_normal = constraint->mPlane.GetNormal(); + + // If we're hitting a steep slope we cancel the velocity towards the slope first so that we don't end up sliding up the slope + // (we may hit the slope before the vertical wall constraint we added which will result in a small movement up causing jitter in the character movement) + if (constraint->mIsSteepSlope) + { + // We're hitting a steep slope, create a vertical plane that blocks any further movement up the slope (note: not normalized) + Vec3 vertical_plane_normal = plane_normal - plane_normal.Dot(mUp) * mUp; + + // Get the relative velocity between the character and the constraint + Vec3 relative_velocity = velocity - constraint->mLinearVelocity; + + // Remove velocity towards the slope + velocity = velocity - min(0.0f, relative_velocity.Dot(vertical_plane_normal)) * vertical_plane_normal / vertical_plane_normal.LengthSq(); + } + + // Get the relative velocity between the character and the constraint + Vec3 relative_velocity = velocity - constraint->mLinearVelocity; + + // Calculate new velocity if we cancel the relative velocity in the normal direction + Vec3 new_velocity = velocity - relative_velocity.Dot(plane_normal) * plane_normal; + + // Find the normal of the previous contact that we will violate the most if we move in this new direction + float highest_penetration = 0.0f; + Constraint *other_constraint = nullptr; + for (Constraint **c = previous_contacts.data(); c < previous_contacts.data() + num_previous_contacts; ++c) + if (*c != constraint) + { + // Calculate how much we will penetrate if we move in this direction + Vec3 other_normal = (*c)->mPlane.GetNormal(); + float penetration = ((*c)->mLinearVelocity - new_velocity).Dot(other_normal); + if (penetration > highest_penetration) + { + // We don't want parallel or anti-parallel normals as that will cause our cross product below to become zero. Slack is approx 10 degrees. + float dot = other_normal.Dot(plane_normal); + if (dot < 0.984f && dot > -0.984f) + { + highest_penetration = penetration; + other_constraint = *c; + } + } + } + + // Check if we found a 2nd constraint + if (other_constraint != nullptr) + { + // Calculate the sliding direction and project the new velocity onto that sliding direction + Vec3 other_normal = other_constraint->mPlane.GetNormal(); + Vec3 slide_dir = plane_normal.Cross(other_normal).Normalized(); + Vec3 velocity_in_slide_dir = new_velocity.Dot(slide_dir) * slide_dir; + + // Cancel the constraint velocity in the other constraint plane's direction so that we won't try to apply it again and keep ping ponging between planes + constraint->mLinearVelocity -= min(0.0f, constraint->mLinearVelocity.Dot(other_normal)) * other_normal; + + // Cancel the other constraints velocity in this constraint plane's direction so that we won't try to apply it again and keep ping ponging between planes + other_constraint->mLinearVelocity -= min(0.0f, other_constraint->mLinearVelocity.Dot(plane_normal)) * plane_normal; + + // Calculate the velocity of this constraint perpendicular to the slide direction + Vec3 perpendicular_velocity = constraint->mLinearVelocity - constraint->mLinearVelocity.Dot(slide_dir) * slide_dir; + + // Calculate the velocity of the other constraint perpendicular to the slide direction + Vec3 other_perpendicular_velocity = other_constraint->mLinearVelocity - other_constraint->mLinearVelocity.Dot(slide_dir) * slide_dir; + + // Add all components together + new_velocity = velocity_in_slide_dir + perpendicular_velocity + other_perpendicular_velocity; + } + + // Allow application to modify calculated velocity + if (mListener != nullptr) + mListener->OnContactSolve(this, constraint->mContact->mBodyB, constraint->mContact->mSubShapeIDB, constraint->mContact->mPosition, constraint->mContact->mContactNormal, constraint->mContact->mLinearVelocity, constraint->mContact->mMaterial, velocity, new_velocity); + +#ifdef JPH_DEBUG_RENDERER + if (inDrawConstraints) + { + // Calculate where to draw + RVec3 offset = mPosition + Vec3(0, 0, 2.5f * (iteration + 1)); + + // Draw constraint plane + DebugRenderer::sInstance->DrawPlane(offset, constraint->mPlane.GetNormal(), Color::sCyan, 1.0f); + + // Draw 2nd constraint plane + if (other_constraint != nullptr) + DebugRenderer::sInstance->DrawPlane(offset, other_constraint->mPlane.GetNormal(), Color::sBlue, 1.0f); + + // Draw starting velocity + DebugRenderer::sInstance->DrawArrow(offset, offset + velocity, Color::sGreen, 0.05f); + + // Draw resulting velocity + DebugRenderer::sInstance->DrawArrow(offset, offset + new_velocity, Color::sRed, 0.05f); + } +#endif // JPH_DEBUG_RENDERER + + // Update the velocity + velocity = new_velocity; + + // Add the contact to the list so that next iteration we can avoid violating it again + previous_contacts[num_previous_contacts] = constraint; + num_previous_contacts++; + + // Check early out + if (constraint->mProjectedVelocity < 1.0e-8f // Constraint should not be pushing, otherwise there may be other constraints that are pushing us + && velocity.LengthSq() < 1.0e-8f) // There's not enough velocity left + return; + + // If the constraint has velocity we accept the new velocity, otherwise check that we didn't reverse velocity + if (!constraint->mLinearVelocity.IsNearZero(1.0e-8f)) + last_velocity = constraint->mLinearVelocity; + else if (velocity.Dot(last_velocity) < 0.0f) + return; + } +} + +void CharacterVirtual::UpdateSupportingContact(bool inSkipContactVelocityCheck, TempAllocator &inAllocator) +{ + // Flag contacts as having a collision if they're close enough but ignore contacts we're moving away from. + // Note that if we did MoveShape before we want to preserve any contacts that it marked as colliding + for (Contact &c : mActiveContacts) + if (!c.mWasDiscarded + && !c.mHadCollision + && c.mDistance < mCollisionTolerance + && (inSkipContactVelocityCheck || c.mSurfaceNormal.Dot(mLinearVelocity - c.mLinearVelocity) <= 1.0e-4f)) + { + if (ValidateContact(c) && !c.mIsSensorB) + c.mHadCollision = true; + else + c.mWasDiscarded = true; + } + + // Calculate transform that takes us to character local space + RMat44 inv_transform = RMat44::sInverseRotationTranslation(mRotation, mPosition); + + // Determine if we're supported or not + int num_supported = 0; + int num_sliding = 0; + int num_avg_normal = 0; + Vec3 avg_normal = Vec3::sZero(); + Vec3 avg_velocity = Vec3::sZero(); + const Contact *supporting_contact = nullptr; + float max_cos_angle = -FLT_MAX; + const Contact *deepest_contact = nullptr; + float smallest_distance = FLT_MAX; + for (const Contact &c : mActiveContacts) + if (c.mHadCollision) + { + // Calculate the angle between the plane normal and the up direction + float cos_angle = c.mSurfaceNormal.Dot(mUp); + + // Find the deepest contact + if (c.mDistance < smallest_distance) + { + deepest_contact = &c; + smallest_distance = c.mDistance; + } + + // If this contact is in front of our plane, we cannot be supported by it + if (mSupportingVolume.SignedDistance(Vec3(inv_transform * c.mPosition)) > 0.0f) + continue; + + // Find the contact with the normal that is pointing most upwards and store it + if (max_cos_angle < cos_angle) + { + supporting_contact = &c; + max_cos_angle = cos_angle; + } + + // Check if this is a sliding or supported contact + bool is_supported = mCosMaxSlopeAngle > cNoMaxSlopeAngle || cos_angle >= mCosMaxSlopeAngle; + if (is_supported) + num_supported++; + else + num_sliding++; + + // If the angle between the two is less than 85 degrees we also use it to calculate the average normal + if (cos_angle >= 0.08f) + { + avg_normal += c.mSurfaceNormal; + num_avg_normal++; + + // For static or dynamic objects or for contacts that don't support us just take the contact velocity + if (c.mMotionTypeB != EMotionType::Kinematic || !is_supported) + avg_velocity += c.mLinearVelocity; + else + { + // For keyframed objects that support us calculate the velocity at our position rather than at the contact position so that we properly follow the object + BodyLockRead lock(mSystem->GetBodyLockInterface(), c.mBodyB); + if (lock.SucceededAndIsInBroadPhase()) + { + const Body &body = lock.GetBody(); + + // Get adjusted body velocity + Vec3 linear_velocity, angular_velocity; + GetAdjustedBodyVelocity(body, linear_velocity, angular_velocity); + + // Calculate the ground velocity + avg_velocity += CalculateCharacterGroundVelocity(body.GetCenterOfMassPosition(), linear_velocity, angular_velocity, mLastDeltaTime); + } + else + { + // Fall back to contact velocity + avg_velocity += c.mLinearVelocity; + } + } + } + } + + // Take either the most supporting contact or the deepest contact + const Contact *best_contact = supporting_contact != nullptr? supporting_contact : deepest_contact; + + // Calculate average normal and velocity + if (num_avg_normal >= 1) + { + mGroundNormal = avg_normal.Normalized(); + mGroundVelocity = avg_velocity / float(num_avg_normal); + } + else if (best_contact != nullptr) + { + mGroundNormal = best_contact->mSurfaceNormal; + mGroundVelocity = best_contact->mLinearVelocity; + } + else + { + mGroundNormal = Vec3::sZero(); + mGroundVelocity = Vec3::sZero(); + } + + // Copy contact properties + if (best_contact != nullptr) + { + mGroundBodyID = best_contact->mBodyB; + mGroundBodySubShapeID = best_contact->mSubShapeIDB; + mGroundPosition = best_contact->mPosition; + mGroundMaterial = best_contact->mMaterial; + mGroundUserData = best_contact->mUserData; + } + else + { + mGroundBodyID = BodyID(); + mGroundBodySubShapeID = SubShapeID(); + mGroundPosition = RVec3::sZero(); + mGroundMaterial = PhysicsMaterial::sDefault; + mGroundUserData = 0; + } + + // Determine ground state + if (num_supported > 0) + { + // We made contact with something that supports us + mGroundState = EGroundState::OnGround; + } + else if (num_sliding > 0) + { + if ((mLinearVelocity - deepest_contact->mLinearVelocity).Dot(mUp) > 1.0e-4f) + { + // We cannot be on ground if we're moving upwards relative to the ground + mGroundState = EGroundState::OnSteepGround; + } + else + { + // If we're sliding down, we may actually be standing on multiple sliding contacts in such a way that we can't slide off, in this case we're also supported + + // Convert the contacts into constraints + TempContactList contacts(mActiveContacts.begin(), mActiveContacts.end(), inAllocator); + ConstraintList constraints(inAllocator); + constraints.reserve(contacts.size() * 2); + DetermineConstraints(contacts, mLastDeltaTime, constraints); + + // Solve the displacement using these constraints, this is used to check if we didn't move at all because we are supported + Vec3 displacement; + float time_simulated; + IgnoredContactList ignored_contacts(inAllocator); + ignored_contacts.reserve(contacts.size()); + SolveConstraints(-mUp, 1.0f, 1.0f, constraints, ignored_contacts, time_simulated, displacement, inAllocator); + + // If we're blocked then we're supported, otherwise we're sliding + float min_required_displacement_sq = Square(0.6f * mLastDeltaTime); + if (time_simulated < 0.001f || displacement.LengthSq() < min_required_displacement_sq) + mGroundState = EGroundState::OnGround; + else + mGroundState = EGroundState::OnSteepGround; + } + } + else + { + // Not supported by anything + mGroundState = best_contact != nullptr? EGroundState::NotSupported : EGroundState::InAir; + } +} + +void CharacterVirtual::StoreActiveContacts(const TempContactList &inContacts, TempAllocator &inAllocator) +{ + mActiveContacts.assign(inContacts.begin(), inContacts.end()); + + UpdateSupportingContact(true, inAllocator); +} + +void CharacterVirtual::MoveShape(RVec3 &ioPosition, Vec3Arg inVelocity, float inDeltaTime, ContactList *outActiveContacts, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter, TempAllocator &inAllocator +#ifdef JPH_DEBUG_RENDERER + , bool inDrawConstraints +#endif // JPH_DEBUG_RENDERER + ) const +{ + JPH_DET_LOG("CharacterVirtual::MoveShape: pos: " << ioPosition << " vel: " << inVelocity << " dt: " << inDeltaTime); + + Vec3 movement_direction = inVelocity.NormalizedOr(Vec3::sZero()); + + float time_remaining = inDeltaTime; + for (uint iteration = 0; iteration < mMaxCollisionIterations && time_remaining >= mMinTimeRemaining; iteration++) + { + JPH_DET_LOG("iter: " << iteration << " time: " << time_remaining); + + // Determine contacts in the neighborhood + TempContactList contacts(inAllocator); + contacts.reserve(mMaxNumHits); + GetContactsAtPosition(ioPosition, movement_direction, mShape, contacts, inBroadPhaseLayerFilter, inObjectLayerFilter, inBodyFilter, inShapeFilter); + +#ifdef JPH_ENABLE_DETERMINISM_LOG + for (const Contact &c : contacts) + JPH_DET_LOG("contact: " << c.mPosition << " vel: " << c.mLinearVelocity << " cnormal: " << c.mContactNormal << " snormal: " << c.mSurfaceNormal << " dist: " << c.mDistance << " fraction: " << c.mFraction << " body: " << c.mBodyB << " subshape: " << c.mSubShapeIDB); +#endif // JPH_ENABLE_DETERMINISM_LOG + + // Remove contacts with the same body that have conflicting normals + IgnoredContactList ignored_contacts(inAllocator); + ignored_contacts.reserve(contacts.size()); + RemoveConflictingContacts(contacts, ignored_contacts); + + // Convert contacts into constraints + ConstraintList constraints(inAllocator); + constraints.reserve(contacts.size() * 2); + DetermineConstraints(contacts, inDeltaTime, constraints); + +#ifdef JPH_DEBUG_RENDERER + bool draw_constraints = inDrawConstraints && iteration == 0; + if (draw_constraints) + { + for (const Constraint &c : constraints) + { + // Draw contact point + DebugRenderer::sInstance->DrawMarker(c.mContact->mPosition, Color::sYellow, 0.05f); + Vec3 dist_to_plane = -c.mPlane.GetConstant() * c.mPlane.GetNormal(); + + // Draw arrow towards surface that we're hitting + DebugRenderer::sInstance->DrawArrow(c.mContact->mPosition, c.mContact->mPosition - dist_to_plane, Color::sYellow, 0.05f); + + // Draw plane around the player position indicating the space that we can move + DebugRenderer::sInstance->DrawPlane(mPosition + dist_to_plane, c.mPlane.GetNormal(), Color::sCyan, 1.0f); + DebugRenderer::sInstance->DrawArrow(mPosition + dist_to_plane, mPosition + dist_to_plane + c.mContact->mSurfaceNormal, Color::sRed, 0.05f); + } + } +#endif // JPH_DEBUG_RENDERER + + // Solve the displacement using these constraints + Vec3 displacement; + float time_simulated; + SolveConstraints(inVelocity, inDeltaTime, time_remaining, constraints, ignored_contacts, time_simulated, displacement, inAllocator + #ifdef JPH_DEBUG_RENDERER + , draw_constraints + #endif // JPH_DEBUG_RENDERER + ); + + // Store the contacts now that the colliding ones have been marked + if (outActiveContacts != nullptr) + outActiveContacts->assign(contacts.begin(), contacts.end()); + + // Do a sweep to test if the path is really unobstructed + Contact cast_contact; + if (GetFirstContactForSweep(ioPosition, displacement, cast_contact, ignored_contacts, inBroadPhaseLayerFilter, inObjectLayerFilter, inBodyFilter, inShapeFilter)) + { + displacement *= cast_contact.mFraction; + time_simulated *= cast_contact.mFraction; + } + + // Update the position + ioPosition += displacement; + time_remaining -= time_simulated; + + // If the displacement during this iteration was too small we assume we cannot further progress this update + if (displacement.LengthSq() < 1.0e-8f) + break; + } +} + +Vec3 CharacterVirtual::CancelVelocityTowardsSteepSlopes(Vec3Arg inDesiredVelocity) const +{ + // If we're not pushing against a steep slope, return the desired velocity + // Note: This is important as WalkStairs overrides the ground state to OnGround when its first check fails but the second succeeds + if (mGroundState == CharacterVirtual::EGroundState::OnGround + || mGroundState == CharacterVirtual::EGroundState::InAir) + return inDesiredVelocity; + + Vec3 desired_velocity = inDesiredVelocity; + for (const Contact &c : mActiveContacts) + if (c.mHadCollision + && IsSlopeTooSteep(c.mSurfaceNormal)) + { + // Note that we use the contact normal to allow for better sliding as the surface normal may be in the opposite direction of movement. + Vec3 normal = c.mContactNormal; + + // Remove normal vertical component + normal -= normal.Dot(mUp) * mUp; + + // Cancel horizontal movement in opposite direction + float dot = normal.Dot(desired_velocity); + if (dot < 0.0f) + desired_velocity -= (dot * normal) / normal.LengthSq(); + } + return desired_velocity; +} + +void CharacterVirtual::Update(float inDeltaTime, Vec3Arg inGravity, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter, TempAllocator &inAllocator) +{ + // If there's no delta time, we don't need to do anything + if (inDeltaTime <= 0.0f) + return; + + // Remember delta time for checking if we're supported by the ground + mLastDeltaTime = inDeltaTime; + + // Slide the shape through the world + MoveShape(mPosition, mLinearVelocity, inDeltaTime, &mActiveContacts, inBroadPhaseLayerFilter, inObjectLayerFilter, inBodyFilter, inShapeFilter, inAllocator + #ifdef JPH_DEBUG_RENDERER + , sDrawConstraints + #endif // JPH_DEBUG_RENDERER + ); + + // Determine the object that we're standing on + UpdateSupportingContact(false, inAllocator); + + // If we're on the ground + if (!mGroundBodyID.IsInvalid() && mMass > 0.0f) + { + // Add the impulse to the ground due to gravity: P = F dt = M g dt + float normal_dot_gravity = mGroundNormal.Dot(inGravity); + if (normal_dot_gravity < 0.0f) + { + Vec3 world_impulse = -(mMass * normal_dot_gravity / inGravity.Length() * inDeltaTime) * inGravity; + mSystem->GetBodyInterface().AddImpulse(mGroundBodyID, world_impulse, mGroundPosition); + } + } +} + +void CharacterVirtual::RefreshContacts(const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter, TempAllocator &inAllocator) +{ + // Determine the contacts + TempContactList contacts(inAllocator); + contacts.reserve(mMaxNumHits); + GetContactsAtPosition(mPosition, mLinearVelocity.NormalizedOr(Vec3::sZero()), mShape, contacts, inBroadPhaseLayerFilter, inObjectLayerFilter, inBodyFilter, inShapeFilter); + + StoreActiveContacts(contacts, inAllocator); +} + +void CharacterVirtual::UpdateGroundVelocity() +{ + BodyLockRead lock(mSystem->GetBodyLockInterface(), mGroundBodyID); + if (lock.SucceededAndIsInBroadPhase()) + { + const Body &body = lock.GetBody(); + + // Get adjusted body velocity + Vec3 linear_velocity, angular_velocity; + GetAdjustedBodyVelocity(body, linear_velocity, angular_velocity); + + // Calculate the ground velocity + mGroundVelocity = CalculateCharacterGroundVelocity(body.GetCenterOfMassPosition(), linear_velocity, angular_velocity, mLastDeltaTime); + } +} + +void CharacterVirtual::MoveToContact(RVec3Arg inPosition, const Contact &inContact, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter, TempAllocator &inAllocator) +{ + // Set the new position + SetPosition(inPosition); + + // Determine the contacts + TempContactList contacts(inAllocator); + contacts.reserve(mMaxNumHits + 1); // +1 because we can add one extra below + GetContactsAtPosition(mPosition, mLinearVelocity.NormalizedOr(Vec3::sZero()), mShape, contacts, inBroadPhaseLayerFilter, inObjectLayerFilter, inBodyFilter, inShapeFilter); + + // Ensure that we mark inContact as colliding + bool found_contact = false; + for (Contact &c : contacts) + if (c.mBodyB == inContact.mBodyB + && c.mSubShapeIDB == inContact.mSubShapeIDB) + { + c.mHadCollision = true; + found_contact = true; + } + if (!found_contact) + { + contacts.push_back(inContact); + + Contact © = contacts.back(); + copy.mHadCollision = true; + } + + StoreActiveContacts(contacts, inAllocator); + JPH_ASSERT(mGroundState != EGroundState::InAir); +} + +bool CharacterVirtual::SetShape(const Shape *inShape, float inMaxPenetrationDepth, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter, TempAllocator &inAllocator) +{ + if (mShape == nullptr || mSystem == nullptr) + { + // It hasn't been initialized yet + mShape = inShape; + return true; + } + + if (inShape != mShape && inShape != nullptr) + { + if (inMaxPenetrationDepth < FLT_MAX) + { + // Check collision around the new shape + TempContactList contacts(inAllocator); + contacts.reserve(mMaxNumHits); + GetContactsAtPosition(mPosition, mLinearVelocity.NormalizedOr(Vec3::sZero()), inShape, contacts, inBroadPhaseLayerFilter, inObjectLayerFilter, inBodyFilter, inShapeFilter); + + // Test if this results in penetration, if so cancel the transition + for (const Contact &c : contacts) + if (c.mDistance < -inMaxPenetrationDepth) + return false; + + StoreActiveContacts(contacts, inAllocator); + } + + // Set new shape + mShape = inShape; + } + + return mShape == inShape; +} + +bool CharacterVirtual::CanWalkStairs(Vec3Arg inLinearVelocity) const +{ + // We can only walk stairs if we're supported + if (!IsSupported()) + return false; + + // Check if there's enough horizontal velocity to trigger a stair walk + Vec3 horizontal_velocity = inLinearVelocity - inLinearVelocity.Dot(mUp) * mUp; + if (horizontal_velocity.IsNearZero(1.0e-6f)) + return false; + + // Check contacts for steep slopes + for (const Contact &c : mActiveContacts) + if (c.mHadCollision + && c.mSurfaceNormal.Dot(horizontal_velocity - c.mLinearVelocity) < 0.0f // Pushing into the contact + && IsSlopeTooSteep(c.mSurfaceNormal)) // Slope too steep + return true; + + return false; +} + +bool CharacterVirtual::WalkStairs(float inDeltaTime, Vec3Arg inStepUp, Vec3Arg inStepForward, Vec3Arg inStepForwardTest, Vec3Arg inStepDownExtra, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter, TempAllocator &inAllocator) +{ + // Move up + Vec3 up = inStepUp; + Contact contact; + IgnoredContactList dummy_ignored_contacts(inAllocator); + if (GetFirstContactForSweep(mPosition, up, contact, dummy_ignored_contacts, inBroadPhaseLayerFilter, inObjectLayerFilter, inBodyFilter, inShapeFilter)) + { + if (contact.mFraction < 1.0e-6f) + return false; // No movement, cancel + + // Limit up movement to the first contact point + up *= contact.mFraction; + } + RVec3 up_position = mPosition + up; + +#ifdef JPH_DEBUG_RENDERER + // Draw sweep up + if (sDrawWalkStairs) + DebugRenderer::sInstance->DrawArrow(mPosition, up_position, Color::sWhite, 0.01f); +#endif // JPH_DEBUG_RENDERER + + // Collect normals of steep slopes that we would like to walk stairs on. + // We need to do this before calling MoveShape because it will update mActiveContacts. + Vec3 character_velocity = inStepForward / inDeltaTime; + Vec3 horizontal_velocity = character_velocity - character_velocity.Dot(mUp) * mUp; + std::vector> steep_slope_normals(inAllocator); + steep_slope_normals.reserve(mActiveContacts.size()); + for (const Contact &c : mActiveContacts) + if (c.mHadCollision + && c.mSurfaceNormal.Dot(horizontal_velocity - c.mLinearVelocity) < 0.0f // Pushing into the contact + && IsSlopeTooSteep(c.mSurfaceNormal)) // Slope too steep + steep_slope_normals.push_back(c.mSurfaceNormal); + if (steep_slope_normals.empty()) + return false; // No steep slopes, cancel + + // Horizontal movement + RVec3 new_position = up_position; + MoveShape(new_position, character_velocity, inDeltaTime, nullptr, inBroadPhaseLayerFilter, inObjectLayerFilter, inBodyFilter, inShapeFilter, inAllocator); + Vec3 horizontal_movement = Vec3(new_position - up_position); + float horizontal_movement_sq = horizontal_movement.LengthSq(); + if (horizontal_movement_sq < 1.0e-8f) + return false; // No movement, cancel + + // Check if we made any progress towards any of the steep slopes, if not we just slid along the slope + // so we need to cancel the stair walk or else we will move faster than we should as we've done + // normal movement first and then stair walk. + bool made_progress = false; + float max_dot = -0.05f * inStepForward.Length(); + for (const Vec3 &normal : steep_slope_normals) + if (normal.Dot(horizontal_movement) < max_dot) + { + // We moved more than 5% of the forward step against a steep slope, accept this as progress + made_progress = true; + break; + } + if (!made_progress) + return false; + +#ifdef JPH_DEBUG_RENDERER + // Draw horizontal sweep + if (sDrawWalkStairs) + DebugRenderer::sInstance->DrawArrow(up_position, new_position, Color::sWhite, 0.01f); +#endif // JPH_DEBUG_RENDERER + + // Move down towards the floor. + // Note that we travel the same amount down as we traveled up with the specified extra + Vec3 down = -up + inStepDownExtra; + if (!GetFirstContactForSweep(new_position, down, contact, dummy_ignored_contacts, inBroadPhaseLayerFilter, inObjectLayerFilter, inBodyFilter, inShapeFilter)) + return false; // No floor found, we're in mid air, cancel stair walk + +#ifdef JPH_DEBUG_RENDERER + // Draw sweep down + if (sDrawWalkStairs) + { + RVec3 debug_pos = new_position + contact.mFraction * down; + DebugRenderer::sInstance->DrawArrow(new_position, debug_pos, Color::sWhite, 0.01f); + DebugRenderer::sInstance->DrawArrow(contact.mPosition, contact.mPosition + contact.mSurfaceNormal, Color::sWhite, 0.01f); + mShape->Draw(DebugRenderer::sInstance, GetCenterOfMassTransform(debug_pos, mRotation, mShape), Vec3::sReplicate(1.0f), Color::sWhite, false, true); + } +#endif // JPH_DEBUG_RENDERER + + // Test for floor that will support the character + if (IsSlopeTooSteep(contact.mSurfaceNormal)) + { + // If no test position was provided, we cancel the stair walk + if (inStepForwardTest.IsNearZero()) + return false; + + // Delta time may be very small, so it may be that we hit the edge of a step and the normal is too horizontal. + // In order to judge if the floor is flat further along the sweep, we test again for a floor at inStepForwardTest + // and check if the normal is valid there. + RVec3 test_position = up_position; + MoveShape(test_position, inStepForwardTest / inDeltaTime, inDeltaTime, nullptr, inBroadPhaseLayerFilter, inObjectLayerFilter, inBodyFilter, inShapeFilter, inAllocator); + float test_horizontal_movement_sq = Vec3(test_position - up_position).LengthSq(); + if (test_horizontal_movement_sq <= horizontal_movement_sq + 1.0e-8f) + return false; // We didn't move any further than in the previous test + + #ifdef JPH_DEBUG_RENDERER + // Draw 2nd sweep horizontal + if (sDrawWalkStairs) + DebugRenderer::sInstance->DrawArrow(up_position, test_position, Color::sCyan, 0.01f); + #endif // JPH_DEBUG_RENDERER + + // Then sweep down + Contact test_contact; + if (!GetFirstContactForSweep(test_position, down, test_contact, dummy_ignored_contacts, inBroadPhaseLayerFilter, inObjectLayerFilter, inBodyFilter, inShapeFilter)) + return false; + + #ifdef JPH_DEBUG_RENDERER + // Draw 2nd sweep down + if (sDrawWalkStairs) + { + RVec3 debug_pos = test_position + test_contact.mFraction * down; + DebugRenderer::sInstance->DrawArrow(test_position, debug_pos, Color::sCyan, 0.01f); + DebugRenderer::sInstance->DrawArrow(test_contact.mPosition, test_contact.mPosition + test_contact.mSurfaceNormal, Color::sCyan, 0.01f); + mShape->Draw(DebugRenderer::sInstance, GetCenterOfMassTransform(debug_pos, mRotation, mShape), Vec3::sReplicate(1.0f), Color::sCyan, false, true); + } + #endif // JPH_DEBUG_RENDERER + + if (IsSlopeTooSteep(test_contact.mSurfaceNormal)) + return false; + } + + // Calculate new down position + down *= contact.mFraction; + new_position += down; + + // Move the character to the new location + MoveToContact(new_position, contact, inBroadPhaseLayerFilter, inObjectLayerFilter, inBodyFilter, inShapeFilter, inAllocator); + + // Override ground state to 'on ground', it is possible that the contact normal is too steep, but in this case the inStepForwardTest has found a contact normal that is not too steep + mGroundState = EGroundState::OnGround; + + return true; +} + +bool CharacterVirtual::StickToFloor(Vec3Arg inStepDown, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter, TempAllocator &inAllocator) +{ + // Try to find the floor + Contact contact; + IgnoredContactList dummy_ignored_contacts(inAllocator); + if (!GetFirstContactForSweep(mPosition, inStepDown, contact, dummy_ignored_contacts, inBroadPhaseLayerFilter, inObjectLayerFilter, inBodyFilter, inShapeFilter)) + return false; // If no floor found, don't update our position + + // Calculate new position + RVec3 new_position = mPosition + contact.mFraction * inStepDown; + +#ifdef JPH_DEBUG_RENDERER + // Draw sweep down + if (sDrawStickToFloor) + { + DebugRenderer::sInstance->DrawArrow(mPosition, new_position, Color::sOrange, 0.01f); + mShape->Draw(DebugRenderer::sInstance, GetCenterOfMassTransform(new_position, mRotation, mShape), Vec3::sReplicate(1.0f), Color::sOrange, false, true); + } +#endif // JPH_DEBUG_RENDERER + + // Move the character to the new location + MoveToContact(new_position, contact, inBroadPhaseLayerFilter, inObjectLayerFilter, inBodyFilter, inShapeFilter, inAllocator); + return true; +} + +void CharacterVirtual::ExtendedUpdate(float inDeltaTime, Vec3Arg inGravity, const ExtendedUpdateSettings &inSettings, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter, TempAllocator &inAllocator) +{ + // Update the velocity + Vec3 desired_velocity = mLinearVelocity; + mLinearVelocity = CancelVelocityTowardsSteepSlopes(desired_velocity); + + // Remember old position + RVec3 old_position = mPosition; + + // Track if on ground before the update + bool ground_to_air = IsSupported(); + + // Update the character position (instant, do not have to wait for physics update) + Update(inDeltaTime, inGravity, inBroadPhaseLayerFilter, inObjectLayerFilter, inBodyFilter, inShapeFilter, inAllocator); + + // ... and that we got into air after + if (IsSupported()) + ground_to_air = false; + + // If stick to floor enabled and we're going from supported to not supported + if (ground_to_air && !inSettings.mStickToFloorStepDown.IsNearZero()) + { + // If we're not moving up, stick to the floor + float velocity = Vec3(mPosition - old_position).Dot(mUp) / inDeltaTime; + if (velocity <= 1.0e-6f) + StickToFloor(inSettings.mStickToFloorStepDown, inBroadPhaseLayerFilter, inObjectLayerFilter, inBodyFilter, inShapeFilter, inAllocator); + } + + // If walk stairs enabled + if (!inSettings.mWalkStairsStepUp.IsNearZero()) + { + // Calculate how much we wanted to move horizontally + Vec3 desired_horizontal_step = desired_velocity * inDeltaTime; + desired_horizontal_step -= desired_horizontal_step.Dot(mUp) * mUp; + float desired_horizontal_step_len = desired_horizontal_step.Length(); + if (desired_horizontal_step_len > 0.0f) + { + // Calculate how much we moved horizontally + Vec3 achieved_horizontal_step = Vec3(mPosition - old_position); + achieved_horizontal_step -= achieved_horizontal_step.Dot(mUp) * mUp; + + // Only count movement in the direction of the desired movement + // (otherwise we find it ok if we're sliding downhill while we're trying to climb uphill) + Vec3 step_forward_normalized = desired_horizontal_step / desired_horizontal_step_len; + achieved_horizontal_step = max(0.0f, achieved_horizontal_step.Dot(step_forward_normalized)) * step_forward_normalized; + float achieved_horizontal_step_len = achieved_horizontal_step.Length(); + + // If we didn't move as far as we wanted and we're against a slope that's too steep + if (achieved_horizontal_step_len + 1.0e-4f < desired_horizontal_step_len + && CanWalkStairs(desired_velocity)) + { + // Calculate how much we should step forward + // Note that we clamp the step forward to a minimum distance. This is done because at very high frame rates the delta time + // may be very small, causing a very small step forward. If the step becomes small enough, we may not move far enough + // horizontally to actually end up at the top of the step. + Vec3 step_forward = step_forward_normalized * max(inSettings.mWalkStairsMinStepForward, desired_horizontal_step_len - achieved_horizontal_step_len); + + // Calculate how far to scan ahead for a floor. This is only used in case the floor normal at step_forward is too steep. + // In that case an additional check will be performed at this distance to check if that normal is not too steep. + // Start with the ground normal in the horizontal plane and normalizing it + Vec3 step_forward_test = -mGroundNormal; + step_forward_test -= step_forward_test.Dot(mUp) * mUp; + step_forward_test = step_forward_test.NormalizedOr(step_forward_normalized); + + // If this normalized vector and the character forward vector is bigger than a preset angle, we use the character forward vector instead of the ground normal + // to do our forward test + if (step_forward_test.Dot(step_forward_normalized) < inSettings.mWalkStairsCosAngleForwardContact) + step_forward_test = step_forward_normalized; + + // Calculate the correct magnitude for the test vector + step_forward_test *= inSettings.mWalkStairsStepForwardTest; + + WalkStairs(inDeltaTime, inSettings.mWalkStairsStepUp, step_forward, step_forward_test, inSettings.mWalkStairsStepDownExtra, inBroadPhaseLayerFilter, inObjectLayerFilter, inBodyFilter, inShapeFilter, inAllocator); + } + } + } +} + +void CharacterVirtual::Contact::SaveState(StateRecorder &inStream) const +{ + inStream.Write(mPosition); + inStream.Write(mLinearVelocity); + inStream.Write(mContactNormal); + inStream.Write(mSurfaceNormal); + inStream.Write(mDistance); + inStream.Write(mFraction); + inStream.Write(mBodyB); + inStream.Write(mSubShapeIDB); + inStream.Write(mMotionTypeB); + inStream.Write(mHadCollision); + inStream.Write(mWasDiscarded); + inStream.Write(mCanPushCharacter); + // Cannot store user data (may be a pointer) and material +} + +void CharacterVirtual::Contact::RestoreState(StateRecorder &inStream) +{ + inStream.Read(mPosition); + inStream.Read(mLinearVelocity); + inStream.Read(mContactNormal); + inStream.Read(mSurfaceNormal); + inStream.Read(mDistance); + inStream.Read(mFraction); + inStream.Read(mBodyB); + inStream.Read(mSubShapeIDB); + inStream.Read(mMotionTypeB); + inStream.Read(mHadCollision); + inStream.Read(mWasDiscarded); + inStream.Read(mCanPushCharacter); + mUserData = 0; // Cannot restore user data + mMaterial = PhysicsMaterial::sDefault; // Cannot restore material +} + +void CharacterVirtual::SaveState(StateRecorder &inStream) const +{ + CharacterBase::SaveState(inStream); + + inStream.Write(mPosition); + inStream.Write(mRotation); + inStream.Write(mLinearVelocity); + inStream.Write(mLastDeltaTime); + inStream.Write(mMaxHitsExceeded); + + // Store contacts that had collision, we're using it at the beginning of the step in CancelVelocityTowardsSteepSlopes + uint32 num_contacts = 0; + for (const Contact &c : mActiveContacts) + if (c.mHadCollision) + ++num_contacts; + inStream.Write(num_contacts); + for (const Contact &c : mActiveContacts) + if (c.mHadCollision) + c.SaveState(inStream); +} + +void CharacterVirtual::RestoreState(StateRecorder &inStream) +{ + CharacterBase::RestoreState(inStream); + + inStream.Read(mPosition); + inStream.Read(mRotation); + inStream.Read(mLinearVelocity); + inStream.Read(mLastDeltaTime); + inStream.Read(mMaxHitsExceeded); + + // When validating remove contacts that don't have collision since we didn't save them + if (inStream.IsValidating()) + for (int i = (int)mActiveContacts.size() - 1; i >= 0; --i) + if (!mActiveContacts[i].mHadCollision) + mActiveContacts.erase(mActiveContacts.begin() + i); + + uint32 num_contacts = (uint32)mActiveContacts.size(); + inStream.Read(num_contacts); + mActiveContacts.resize(num_contacts); + for (Contact &c : mActiveContacts) + c.RestoreState(inStream); +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Character/CharacterVirtual.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Character/CharacterVirtual.h new file mode 100644 index 00000000000..a0a126eed6d --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Character/CharacterVirtual.h @@ -0,0 +1,502 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +class CharacterVirtual; + +/// Contains the configuration of a character +class JPH_EXPORT CharacterVirtualSettings : public CharacterBaseSettings +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Character mass (kg). Used to push down objects with gravity when the character is standing on top. + float mMass = 70.0f; + + /// Maximum force with which the character can push other bodies (N). + float mMaxStrength = 100.0f; + + /// An extra offset applied to the shape in local space. This allows applying an extra offset to the shape in local space. + Vec3 mShapeOffset = Vec3::sZero(); + + ///@name Movement settings + EBackFaceMode mBackFaceMode = EBackFaceMode::CollideWithBackFaces; ///< When colliding with back faces, the character will not be able to move through back facing triangles. Use this if you have triangles that need to collide on both sides. + float mPredictiveContactDistance = 0.1f; ///< How far to scan outside of the shape for predictive contacts. A value of 0 will most likely cause the character to get stuck as it cannot properly calculate a sliding direction anymore. A value that's too high will cause ghost collisions. + uint mMaxCollisionIterations = 5; ///< Max amount of collision loops + uint mMaxConstraintIterations = 15; ///< How often to try stepping in the constraint solving + float mMinTimeRemaining = 1.0e-4f; ///< Early out condition: If this much time is left to simulate we are done + float mCollisionTolerance = 1.0e-3f; ///< How far we're willing to penetrate geometry + float mCharacterPadding = 0.02f; ///< How far we try to stay away from the geometry, this ensures that the sweep will hit as little as possible lowering the collision cost and reducing the risk of getting stuck + uint mMaxNumHits = 256; ///< Max num hits to collect in order to avoid excess of contact points collection + float mHitReductionCosMaxAngle = 0.999f; ///< Cos(angle) where angle is the maximum angle between two hits contact normals that are allowed to be merged during hit reduction. Default is around 2.5 degrees. Set to -1 to turn off. + float mPenetrationRecoverySpeed = 1.0f; ///< This value governs how fast a penetration will be resolved, 0 = nothing is resolved, 1 = everything in one update +}; + +/// This class contains settings that allow you to override the behavior of a character's collision response +class CharacterContactSettings +{ +public: + bool mCanPushCharacter = true; ///< True when the object can push the virtual character + bool mCanReceiveImpulses = true; ///< True when the virtual character can apply impulses (push) the body +}; + +/// This class receives callbacks when a virtual character hits something. +class JPH_EXPORT CharacterContactListener +{ +public: + /// Destructor + virtual ~CharacterContactListener() = default; + + /// Callback to adjust the velocity of a body as seen by the character. Can be adjusted to e.g. implement a conveyor belt or an inertial dampener system of a sci-fi space ship. + /// Note that inBody2 is locked during the callback so you can read its properties freely. + virtual void OnAdjustBodyVelocity(const CharacterVirtual *inCharacter, const Body &inBody2, Vec3 &ioLinearVelocity, Vec3 &ioAngularVelocity) { /* Do nothing, the linear and angular velocity are already filled in */ } + + /// Checks if a character can collide with specified body. Return true if the contact is valid. + virtual bool OnContactValidate(const CharacterVirtual *inCharacter, const BodyID &inBodyID2, const SubShapeID &inSubShapeID2) { return true; } + + /// Called whenever the character collides with a body. + /// @param inCharacter Character that is being solved + /// @param inBodyID2 Body ID of body that is being hit + /// @param inSubShapeID2 Sub shape ID of shape that is being hit + /// @param inContactPosition World space contact position + /// @param inContactNormal World space contact normal + /// @param ioSettings Settings returned by the contact callback to indicate how the character should behave + virtual void OnContactAdded(const CharacterVirtual *inCharacter, const BodyID &inBodyID2, const SubShapeID &inSubShapeID2, RVec3Arg inContactPosition, Vec3Arg inContactNormal, CharacterContactSettings &ioSettings) { /* Default do nothing */ } + + /// Called whenever a contact is being used by the solver. Allows the listener to override the resulting character velocity (e.g. by preventing sliding along certain surfaces). + /// @param inCharacter Character that is being solved + /// @param inBodyID2 Body ID of body that is being hit + /// @param inSubShapeID2 Sub shape ID of shape that is being hit + /// @param inContactPosition World space contact position + /// @param inContactNormal World space contact normal + /// @param inContactVelocity World space velocity of contact point (e.g. for a moving platform) + /// @param inContactMaterial Material of contact point + /// @param inCharacterVelocity World space velocity of the character prior to hitting this contact + /// @param ioNewCharacterVelocity Contains the calculated world space velocity of the character after hitting this contact, this velocity slides along the surface of the contact. Can be modified by the listener to provide an alternative velocity. + virtual void OnContactSolve(const CharacterVirtual *inCharacter, const BodyID &inBodyID2, const SubShapeID &inSubShapeID2, RVec3Arg inContactPosition, Vec3Arg inContactNormal, Vec3Arg inContactVelocity, const PhysicsMaterial *inContactMaterial, Vec3Arg inCharacterVelocity, Vec3 &ioNewCharacterVelocity) { /* Default do nothing */ } +}; + +/// Runtime character object. +/// This object usually represents the player. Contrary to the Character class it doesn't use a rigid body but moves doing collision checks only (hence the name virtual). +/// The advantage of this is that you can determine when the character moves in the frame (usually this has to happen at a very particular point in the frame) +/// but the downside is that other objects don't see this virtual character. In order to make this work it is recommended to pair a CharacterVirtual with a Character that +/// moves along. This Character should be keyframed (or at least have no gravity) and move along with the CharacterVirtual so that other rigid bodies can collide with it. +class JPH_EXPORT CharacterVirtual : public CharacterBase +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Constructor + /// @param inSettings The settings for the character + /// @param inPosition Initial position for the character + /// @param inRotation Initial rotation for the character (usually only around the up-axis) + /// @param inUserData Application specific value + /// @param inSystem Physics system that this character will be added to later + CharacterVirtual(const CharacterVirtualSettings *inSettings, RVec3Arg inPosition, QuatArg inRotation, uint64 inUserData, PhysicsSystem *inSystem); + + /// Constructor without user data + CharacterVirtual(const CharacterVirtualSettings *inSettings, RVec3Arg inPosition, QuatArg inRotation, PhysicsSystem *inSystem) : CharacterVirtual(inSettings, inPosition, inRotation, 0, inSystem) { } + + /// Set the contact listener + void SetListener(CharacterContactListener *inListener) { mListener = inListener; } + + /// Get the current contact listener + CharacterContactListener * GetListener() const { return mListener; } + + /// Get the linear velocity of the character (m / s) + Vec3 GetLinearVelocity() const { return mLinearVelocity; } + + /// Set the linear velocity of the character (m / s) + void SetLinearVelocity(Vec3Arg inLinearVelocity) { mLinearVelocity = inLinearVelocity; } + + /// Get the position of the character + RVec3 GetPosition() const { return mPosition; } + + /// Set the position of the character + void SetPosition(RVec3Arg inPosition) { mPosition = inPosition; } + + /// Get the rotation of the character + Quat GetRotation() const { return mRotation; } + + /// Set the rotation of the character + void SetRotation(QuatArg inRotation) { mRotation = inRotation; } + + /// Calculate the world transform of the character + RMat44 GetWorldTransform() const { return RMat44::sRotationTranslation(mRotation, mPosition); } + + /// Calculates the transform for this character's center of mass + RMat44 GetCenterOfMassTransform() const { return GetCenterOfMassTransform(mPosition, mRotation, mShape); } + + /// Character mass (kg) + float GetMass() const { return mMass; } + void SetMass(float inMass) { mMass = inMass; } + + /// Maximum force with which the character can push other bodies (N) + float GetMaxStrength() const { return mMaxStrength; } + void SetMaxStrength(float inMaxStrength) { mMaxStrength = inMaxStrength; } + + /// This value governs how fast a penetration will be resolved, 0 = nothing is resolved, 1 = everything in one update + float GetPenetrationRecoverySpeed() const { return mPenetrationRecoverySpeed; } + void SetPenetrationRecoverySpeed(float inSpeed) { mPenetrationRecoverySpeed = inSpeed; } + + /// Character padding + float GetCharacterPadding() const { return mCharacterPadding; } + + /// Max num hits to collect in order to avoid excess of contact points collection + uint GetMaxNumHits() const { return mMaxNumHits; } + void SetMaxNumHits(uint inMaxHits) { mMaxNumHits = inMaxHits; } + + /// Cos(angle) where angle is the maximum angle between two hits contact normals that are allowed to be merged during hit reduction. Default is around 2.5 degrees. Set to -1 to turn off. + float GetHitReductionCosMaxAngle() const { return mHitReductionCosMaxAngle; } + void SetHitReductionCosMaxAngle(float inCosMaxAngle) { mHitReductionCosMaxAngle = inCosMaxAngle; } + + /// Returns if we exceeded the maximum number of hits during the last collision check and had to discard hits based on distance. + /// This can be used to find areas that have too complex geometry for the character to navigate properly. + /// To solve you can either increase the max number of hits or simplify the geometry. Note that the character simulation will + /// try to do its best to select the most relevant contacts to avoid the character from getting stuck. + bool GetMaxHitsExceeded() const { return mMaxHitsExceeded; } + + /// An extra offset applied to the shape in local space. This allows applying an extra offset to the shape in local space. Note that setting it on the fly can cause the shape to teleport into collision. + Vec3 GetShapeOffset() const { return mShapeOffset; } + void SetShapeOffset(Vec3Arg inShapeOffset) { mShapeOffset = inShapeOffset; } + + /// Access to the user data, can be used for anything by the application + uint64 GetUserData() const { return mUserData; } + void SetUserData(uint64 inUserData) { mUserData = inUserData; } + + /// This function can be called prior to calling Update() to convert a desired velocity into a velocity that won't make the character move further onto steep slopes. + /// This velocity can then be set on the character using SetLinearVelocity() + /// @param inDesiredVelocity Velocity to clamp against steep walls + /// @return A new velocity vector that won't make the character move up steep slopes + Vec3 CancelVelocityTowardsSteepSlopes(Vec3Arg inDesiredVelocity) const; + + /// This is the main update function. It moves the character according to its current velocity (the character is similar to a kinematic body in the sense + /// that you set the velocity and the character will follow unless collision is blocking the way). Note it's your own responsibility to apply gravity to the character velocity! + /// Different surface materials (like ice) can be emulated by getting the ground material and adjusting the velocity and/or the max slope angle accordingly every frame. + /// @param inDeltaTime Time step to simulate. + /// @param inGravity Gravity vector (m/s^2). This gravity vector is only used when the character is standing on top of another object to apply downward force. + /// @param inBroadPhaseLayerFilter Filter that is used to check if the character collides with something in the broadphase. + /// @param inObjectLayerFilter Filter that is used to check if a character collides with a layer. + /// @param inBodyFilter Filter that is used to check if a character collides with a body. + /// @param inShapeFilter Filter that is used to check if a character collides with a subshape. + /// @param inAllocator An allocator for temporary allocations. All memory will be freed by the time this function returns. + void Update(float inDeltaTime, Vec3Arg inGravity, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter, TempAllocator &inAllocator); + + /// This function will return true if the character has moved into a slope that is too steep (e.g. a vertical wall). + /// You would call WalkStairs to attempt to step up stairs. + /// @param inLinearVelocity The linear velocity that the player desired. This is used to determine if we're pushing into a step. + bool CanWalkStairs(Vec3Arg inLinearVelocity) const; + + /// When stair walking is needed, you can call the WalkStairs function to cast up, forward and down again to try to find a valid position + /// @param inDeltaTime Time step to simulate. + /// @param inStepUp The direction and distance to step up (this corresponds to the max step height) + /// @param inStepForward The direction and distance to step forward after the step up + /// @param inStepForwardTest When running at a high frequency, inStepForward can be very small and it's likely that you hit the side of the stairs on the way down. This could produce a normal that violates the max slope angle. If this happens, we test again using this distance from the up position to see if we find a valid slope. + /// @param inStepDownExtra An additional translation that is added when stepping down at the end. Allows you to step further down than up. Set to zero if you don't want this. Should be in the opposite direction of up. + /// @param inBroadPhaseLayerFilter Filter that is used to check if the character collides with something in the broadphase. + /// @param inObjectLayerFilter Filter that is used to check if a character collides with a layer. + /// @param inBodyFilter Filter that is used to check if a character collides with a body. + /// @param inShapeFilter Filter that is used to check if a character collides with a subshape. + /// @param inAllocator An allocator for temporary allocations. All memory will be freed by the time this function returns. + /// @return true if the stair walk was successful + bool WalkStairs(float inDeltaTime, Vec3Arg inStepUp, Vec3Arg inStepForward, Vec3Arg inStepForwardTest, Vec3Arg inStepDownExtra, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter, TempAllocator &inAllocator); + + /// This function can be used to artificially keep the character to the floor. Normally when a character is on a small step and starts moving horizontally, the character will + /// lose contact with the floor because the initial vertical velocity is zero while the horizontal velocity is quite high. To prevent the character from losing contact with the floor, + /// we do an additional collision check downwards and if we find the floor within a certain distance, we project the character onto the floor. + /// @param inStepDown Max amount to project the character downwards (if no floor is found within this distance, the function will return false) + /// @param inBroadPhaseLayerFilter Filter that is used to check if the character collides with something in the broadphase. + /// @param inObjectLayerFilter Filter that is used to check if a character collides with a layer. + /// @param inBodyFilter Filter that is used to check if a character collides with a body. + /// @param inShapeFilter Filter that is used to check if a character collides with a subshape. + /// @param inAllocator An allocator for temporary allocations. All memory will be freed by the time this function returns. + /// @return True if the character was successfully projected onto the floor. + bool StickToFloor(Vec3Arg inStepDown, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter, TempAllocator &inAllocator); + + /// Settings struct with settings for ExtendedUpdate + struct ExtendedUpdateSettings + { + Vec3 mStickToFloorStepDown { 0, -0.5f, 0 }; ///< See StickToFloor inStepDown parameter. Can be zero to turn off. + Vec3 mWalkStairsStepUp { 0, 0.4f, 0 }; ///< See WalkStairs inStepUp parameter. Can be zero to turn off. + float mWalkStairsMinStepForward { 0.02f }; ///< See WalkStairs inStepForward parameter. Note that the parameter only indicates a magnitude, direction is taken from current velocity. + float mWalkStairsStepForwardTest { 0.15f }; ///< See WalkStairs inStepForwardTest parameter. Note that the parameter only indicates a magnitude, direction is taken from current velocity. + float mWalkStairsCosAngleForwardContact { Cos(DegreesToRadians(75.0f)) }; ///< Cos(angle) where angle is the maximum angle between the ground normal in the horizontal plane and the character forward vector where we're willing to adjust the step forward test towards the contact normal. + Vec3 mWalkStairsStepDownExtra { Vec3::sZero() }; ///< See WalkStairs inStepDownExtra + }; + + /// This function combines Update, StickToFloor and WalkStairs. This function serves as an example of how these functions could be combined. + /// Before calling, call SetLinearVelocity to update the horizontal/vertical speed of the character, typically this is: + /// - When on OnGround and not moving away from ground: velocity = GetGroundVelocity() + horizontal speed as input by player + optional vertical jump velocity + delta time * gravity + /// - Else: velocity = current vertical velocity + horizontal speed as input by player + delta time * gravity + /// @param inDeltaTime Time step to simulate. + /// @param inGravity Gravity vector (m/s^2). This gravity vector is only used when the character is standing on top of another object to apply downward force. + /// @param inSettings A structure containing settings for the algorithm. + /// @param inBroadPhaseLayerFilter Filter that is used to check if the character collides with something in the broadphase. + /// @param inObjectLayerFilter Filter that is used to check if a character collides with a layer. + /// @param inBodyFilter Filter that is used to check if a character collides with a body. + /// @param inShapeFilter Filter that is used to check if a character collides with a subshape. + /// @param inAllocator An allocator for temporary allocations. All memory will be freed by the time this function returns. + void ExtendedUpdate(float inDeltaTime, Vec3Arg inGravity, const ExtendedUpdateSettings &inSettings, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter, TempAllocator &inAllocator); + + /// This function can be used after a character has teleported to determine the new contacts with the world. + void RefreshContacts(const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter, TempAllocator &inAllocator); + + /// Use the ground body ID to get an updated estimate of the ground velocity. This function can be used if the ground body has moved / changed velocity and you want a new estimate of the ground velocity. + /// It will not perform collision detection, so is less accurate than RefreshContacts but a lot faster. + void UpdateGroundVelocity(); + + /// Switch the shape of the character (e.g. for stance). + /// @param inShape The shape to switch to. + /// @param inMaxPenetrationDepth When inMaxPenetrationDepth is not FLT_MAX, it checks if the new shape collides before switching shape. This is the max penetration we're willing to accept after the switch. + /// @param inBroadPhaseLayerFilter Filter that is used to check if the character collides with something in the broadphase. + /// @param inObjectLayerFilter Filter that is used to check if a character collides with a layer. + /// @param inBodyFilter Filter that is used to check if a character collides with a body. + /// @param inShapeFilter Filter that is used to check if a character collides with a subshape. + /// @param inAllocator An allocator for temporary allocations. All memory will be freed by the time this function returns. + /// @return Returns true if the switch succeeded. + bool SetShape(const Shape *inShape, float inMaxPenetrationDepth, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter, TempAllocator &inAllocator); + + /// @brief Get all contacts for the character at a particular location + /// @param inPosition Position to test, note that this position will be corrected for the character padding. + /// @param inRotation Rotation at which to test the shape. + /// @param inMovementDirection A hint in which direction the character is moving, will be used to calculate a proper normal. + /// @param inMaxSeparationDistance How much distance around the character you want to report contacts in (can be 0 to match the character exactly). + /// @param inShape Shape to test collision with. + /// @param inBaseOffset All hit results will be returned relative to this offset, can be zero to get results in world position, but when you're testing far from the origin you get better precision by picking a position that's closer e.g. GetPosition() since floats are most accurate near the origin + /// @param ioCollector Collision collector that receives the collision results. + /// @param inBroadPhaseLayerFilter Filter that is used to check if the character collides with something in the broadphase. + /// @param inObjectLayerFilter Filter that is used to check if a character collides with a layer. + /// @param inBodyFilter Filter that is used to check if a character collides with a body. + /// @param inShapeFilter Filter that is used to check if a character collides with a subshape. + void CheckCollision(RVec3Arg inPosition, QuatArg inRotation, Vec3Arg inMovementDirection, float inMaxSeparationDistance, const Shape *inShape, RVec3Arg inBaseOffset, CollideShapeCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter) const; + + // Saving / restoring state for replay + virtual void SaveState(StateRecorder &inStream) const override; + virtual void RestoreState(StateRecorder &inStream) override; + +#ifdef JPH_DEBUG_RENDERER + static inline bool sDrawConstraints = false; ///< Draw the current state of the constraints for iteration 0 when creating them + static inline bool sDrawWalkStairs = false; ///< Draw the state of the walk stairs algorithm + static inline bool sDrawStickToFloor = false; ///< Draw the state of the stick to floor algorithm +#endif + + // Encapsulates a collision contact + struct Contact + { + // Saving / restoring state for replay + void SaveState(StateRecorder &inStream) const; + void RestoreState(StateRecorder &inStream); + + RVec3 mPosition; ///< Position where the character makes contact + Vec3 mLinearVelocity; ///< Velocity of the contact point + Vec3 mContactNormal; ///< Contact normal, pointing towards the character + Vec3 mSurfaceNormal; ///< Surface normal of the contact + float mDistance; ///< Distance to the contact <= 0 means that it is an actual contact, > 0 means predictive + float mFraction; ///< Fraction along the path where this contact takes place + BodyID mBodyB; ///< ID of body we're colliding with + SubShapeID mSubShapeIDB; ///< Sub shape ID of body we're colliding with + EMotionType mMotionTypeB; ///< Motion type of B, used to determine the priority of the contact + bool mIsSensorB; ///< If B is a sensor + uint64 mUserData; ///< User data of B + const PhysicsMaterial * mMaterial; ///< Material of B + bool mHadCollision = false; ///< If the character actually collided with the contact (can be false if a predictive contact never becomes a real one) + bool mWasDiscarded = false; ///< If the contact validate callback chose to discard this contact + bool mCanPushCharacter = true; ///< When true, the velocity of the contact point can push the character + }; + + using TempContactList = std::vector>; + using ContactList = Array; + + /// Access to the internal list of contacts that the character has found. + const ContactList & GetActiveContacts() const { return mActiveContacts; } + +private: + // Sorting predicate for making contact order deterministic + struct ContactOrderingPredicate + { + inline bool operator () (const Contact &inLHS, const Contact &inRHS) const + { + if (inLHS.mBodyB != inRHS.mBodyB) + return inLHS.mBodyB < inRHS.mBodyB; + + return inLHS.mSubShapeIDB.GetValue() < inRHS.mSubShapeIDB.GetValue(); + } + }; + + // A contact that needs to be ignored + struct IgnoredContact + { + IgnoredContact() = default; + IgnoredContact(const BodyID &inBodyID, const SubShapeID &inSubShapeID) : mBodyID(inBodyID), mSubShapeID(inSubShapeID) { } + + BodyID mBodyID; ///< ID of body we're colliding with + SubShapeID mSubShapeID; ///< Sub shape of body we're colliding with + }; + + using IgnoredContactList = std::vector>; + + // A constraint that limits the movement of the character + struct Constraint + { + Contact * mContact; ///< Contact that this constraint was generated from + float mTOI; ///< Calculated time of impact (can be negative if penetrating) + float mProjectedVelocity; ///< Velocity of the contact projected on the contact normal (negative if separating) + Vec3 mLinearVelocity; ///< Velocity of the contact (can contain a corrective velocity to resolve penetration) + Plane mPlane; ///< Plane around the origin that describes how far we can displace (from the origin) + bool mIsSteepSlope = false; ///< If this constraint belongs to a steep slope + }; + + using ConstraintList = std::vector>; + + // Collision collector that collects hits for CollideShape + class ContactCollector : public CollideShapeCollector + { + public: + ContactCollector(PhysicsSystem *inSystem, const CharacterVirtual *inCharacter, uint inMaxHits, float inHitReductionCosMaxAngle, Vec3Arg inUp, RVec3Arg inBaseOffset, TempContactList &outContacts) : mBaseOffset(inBaseOffset), mUp(inUp), mSystem(inSystem), mCharacter(inCharacter), mContacts(outContacts), mMaxHits(inMaxHits), mHitReductionCosMaxAngle(inHitReductionCosMaxAngle) { } + + virtual void AddHit(const CollideShapeResult &inResult) override; + + RVec3 mBaseOffset; + Vec3 mUp; + PhysicsSystem * mSystem; + const CharacterVirtual * mCharacter; + TempContactList & mContacts; + uint mMaxHits; + float mHitReductionCosMaxAngle; + bool mMaxHitsExceeded = false; + }; + + // A collision collector that collects hits for CastShape + class ContactCastCollector : public CastShapeCollector + { + public: + ContactCastCollector(PhysicsSystem *inSystem, const CharacterVirtual *inCharacter, Vec3Arg inDisplacement, Vec3Arg inUp, const IgnoredContactList &inIgnoredContacts, RVec3Arg inBaseOffset, Contact &outContact) : mBaseOffset(inBaseOffset), mDisplacement(inDisplacement), mUp(inUp), mSystem(inSystem), mCharacter(inCharacter), mIgnoredContacts(inIgnoredContacts), mContact(outContact) { } + + virtual void AddHit(const ShapeCastResult &inResult) override; + + RVec3 mBaseOffset; + Vec3 mDisplacement; + Vec3 mUp; + PhysicsSystem * mSystem; + const CharacterVirtual * mCharacter; + const IgnoredContactList & mIgnoredContacts; + Contact & mContact; + }; + + // Helper function to convert a Jolt collision result into a contact + template + inline static void sFillContactProperties(const CharacterVirtual *inCharacter, Contact &outContact, const Body &inBody, Vec3Arg inUp, RVec3Arg inBaseOffset, const taCollector &inCollector, const CollideShapeResult &inResult); + + // Move the shape from ioPosition and try to displace it by inVelocity * inDeltaTime, this will try to slide the shape along the world geometry + void MoveShape(RVec3 &ioPosition, Vec3Arg inVelocity, float inDeltaTime, ContactList *outActiveContacts, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter, TempAllocator &inAllocator + #ifdef JPH_DEBUG_RENDERER + , bool inDrawConstraints = false + #endif // JPH_DEBUG_RENDERER + ) const; + + // Ask the callback if inContact is a valid contact point + bool ValidateContact(const Contact &inContact) const; + + // Tests the shape for collision around inPosition + void GetContactsAtPosition(RVec3Arg inPosition, Vec3Arg inMovementDirection, const Shape *inShape, TempContactList &outContacts, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter) const; + + // Remove penetrating contacts with the same body that have conflicting normals, leaving these will make the character mover get stuck + void RemoveConflictingContacts(TempContactList &ioContacts, IgnoredContactList &outIgnoredContacts) const; + + // Convert contacts into constraints. The character is assumed to start at the origin and the constraints are planes around the origin that confine the movement of the character. + void DetermineConstraints(TempContactList &inContacts, float inDeltaTime, ConstraintList &outConstraints) const; + + // Use the constraints to solve the displacement of the character. This will slide the character on the planes around the origin for as far as possible. + void SolveConstraints(Vec3Arg inVelocity, float inDeltaTime, float inTimeRemaining, ConstraintList &ioConstraints, IgnoredContactList &ioIgnoredContacts, float &outTimeSimulated, Vec3 &outDisplacement, TempAllocator &inAllocator + #ifdef JPH_DEBUG_RENDERER + , bool inDrawConstraints = false + #endif // JPH_DEBUG_RENDERER + ) const; + + // Get the velocity of a body adjusted by the contact listener + void GetAdjustedBodyVelocity(const Body& inBody, Vec3 &outLinearVelocity, Vec3 &outAngularVelocity) const; + + // Calculate the ground velocity of the character assuming it's standing on an object with specified linear and angular velocity and with specified center of mass. + // Note that we don't just take the point velocity because a point on an object with angular velocity traces an arc, + // so if you just take point velocity * delta time you get an error that accumulates over time + Vec3 CalculateCharacterGroundVelocity(RVec3Arg inCenterOfMass, Vec3Arg inLinearVelocity, Vec3Arg inAngularVelocity, float inDeltaTime) const; + + // Handle contact with physics object that we're colliding against + bool HandleContact(Vec3Arg inVelocity, Constraint &ioConstraint, float inDeltaTime) const; + + // Does a swept test of the shape from inPosition with displacement inDisplacement, returns true if there was a collision + bool GetFirstContactForSweep(RVec3Arg inPosition, Vec3Arg inDisplacement, Contact &outContact, const IgnoredContactList &inIgnoredContacts, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter) const; + + // Store contacts so that we have proper ground information + void StoreActiveContacts(const TempContactList &inContacts, TempAllocator &inAllocator); + + // This function will determine which contacts are touching the character and will calculate the one that is supporting us + void UpdateSupportingContact(bool inSkipContactVelocityCheck, TempAllocator &inAllocator); + + /// This function can be called after moving the character to a new colliding position + void MoveToContact(RVec3Arg inPosition, const Contact &inContact, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter, TempAllocator &inAllocator); + + // This function returns the actual center of mass of the shape, not corrected for the character padding + inline RMat44 GetCenterOfMassTransform(RVec3Arg inPosition, QuatArg inRotation, const Shape *inShape) const + { + return RMat44::sRotationTranslation(inRotation, inPosition).PreTranslated(mShapeOffset + inShape->GetCenterOfMass()).PostTranslated(mCharacterPadding * mUp); + } + + // Our main listener for contacts + CharacterContactListener * mListener = nullptr; + + // Movement settings + EBackFaceMode mBackFaceMode; // When colliding with back faces, the character will not be able to move through back facing triangles. Use this if you have triangles that need to collide on both sides. + float mPredictiveContactDistance; // How far to scan outside of the shape for predictive contacts. A value of 0 will most likely cause the character to get stuck as it cannot properly calculate a sliding direction anymore. A value that's too high will cause ghost collisions. + uint mMaxCollisionIterations; // Max amount of collision loops + uint mMaxConstraintIterations; // How often to try stepping in the constraint solving + float mMinTimeRemaining; // Early out condition: If this much time is left to simulate we are done + float mCollisionTolerance; // How far we're willing to penetrate geometry + float mCharacterPadding; // How far we try to stay away from the geometry, this ensures that the sweep will hit as little as possible lowering the collision cost and reducing the risk of getting stuck + uint mMaxNumHits; // Max num hits to collect in order to avoid excess of contact points collection + float mHitReductionCosMaxAngle; // Cos(angle) where angle is the maximum angle between two hits contact normals that are allowed to be merged during hit reduction. Default is around 2.5 degrees. Set to -1 to turn off. + float mPenetrationRecoverySpeed; // This value governs how fast a penetration will be resolved, 0 = nothing is resolved, 1 = everything in one update + + // Character mass (kg) + float mMass; + + // Maximum force with which the character can push other bodies (N) + float mMaxStrength; + + // An extra offset applied to the shape in local space. This allows applying an extra offset to the shape in local space. + Vec3 mShapeOffset = Vec3::sZero(); + + // Current position (of the base, not the center of mass) + RVec3 mPosition = RVec3::sZero(); + + // Current rotation (of the base, not of the center of mass) + Quat mRotation = Quat::sIdentity(); + + // Current linear velocity + Vec3 mLinearVelocity = Vec3::sZero(); + + // List of contacts that were active in the last frame + ContactList mActiveContacts; + + // Remembers the delta time of the last update + float mLastDeltaTime = 1.0f / 60.0f; + + // Remember if we exceeded the maximum number of hits and had to remove similar contacts + mutable bool mMaxHitsExceeded = false; + + // User data, can be used for anything by the application + uint64 mUserData = 0; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/AABoxCast.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/AABoxCast.h new file mode 100644 index 00000000000..a1cedf1ad92 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/AABoxCast.h @@ -0,0 +1,20 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +/// Structure that holds AABox moving linearly through 3d space +struct AABoxCast +{ + JPH_OVERRIDE_NEW_DELETE + + AABox mBox; ///< Axis aligned box at starting location + Vec3 mDirection; ///< Direction and length of the cast (anything beyond this length will not be reported as a hit) +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ActiveEdgeMode.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ActiveEdgeMode.h new file mode 100644 index 00000000000..30f96aeb123 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ActiveEdgeMode.h @@ -0,0 +1,17 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +JPH_NAMESPACE_BEGIN + +/// How to treat active/inactive edges. +/// An active edge is an edge that either has no neighbouring edge or if the angle between the two connecting faces is too large, see: ActiveEdges +enum class EActiveEdgeMode : uint8 +{ + CollideOnlyWithActive, ///< Do not collide with inactive edges. For physics simulation, this gives less ghost collisions. + CollideWithAll, ///< Collide with all edges. Use this when you're interested in all collisions. +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ActiveEdges.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ActiveEdges.h new file mode 100644 index 00000000000..7e51d2a63b7 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ActiveEdges.h @@ -0,0 +1,114 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +/// An active edge is an edge that either has no neighbouring edge or if the angle between the two connecting faces is too large. +namespace ActiveEdges +{ + /// Helper function to check if an edge is active or not + /// @param inNormal1 Triangle normal of triangle on the left side of the edge (when looking along the edge from the top) + /// @param inNormal2 Triangle normal of triangle on the right side of the edge + /// @param inEdgeDirection Vector that points along the edge + /// @param inCosThresholdAngle Cosine of the threshold angle (if the angle between the two triangles is bigger than this, the edge is active, note that a concave edge is always inactive) + inline static bool IsEdgeActive(Vec3Arg inNormal1, Vec3Arg inNormal2, Vec3Arg inEdgeDirection, float inCosThresholdAngle) + { + // If normals are opposite the edges are active (the triangles are back to back) + float cos_angle_normals = inNormal1.Dot(inNormal2); + if (cos_angle_normals < -0.999848f) // cos(179 degrees) + return true; + + // Check if concave edge, if so we are not active + if (inNormal1.Cross(inNormal2).Dot(inEdgeDirection) < 0.0f) + return false; + + // Convex edge, active when angle bigger than threshold + return cos_angle_normals < inCosThresholdAngle; + } + + /// Replace normal by triangle normal if a hit is hitting an inactive edge + /// @param inV0 , inV1 , inV2 form the triangle + /// @param inTriangleNormal is the normal of the provided triangle (does not need to be normalized) + /// @param inActiveEdges bit 0 = edge v0..v1 is active, bit 1 = edge v1..v2 is active, bit 2 = edge v2..v0 is active + /// @param inPoint Collision point on the triangle + /// @param inNormal Collision normal on the triangle (does not need to be normalized) + /// @param inMovementDirection Can be zero. This gives an indication of in which direction the motion is to determine if when we hit an inactive edge/triangle we should return the triangle normal. + /// @return Returns inNormal if an active edge was hit, otherwise returns inTriangleNormal + inline static Vec3 FixNormal(Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2, Vec3Arg inTriangleNormal, uint8 inActiveEdges, Vec3Arg inPoint, Vec3Arg inNormal, Vec3Arg inMovementDirection) + { + // Check: All of the edges are active, we have the correct normal already. No need to call this function! + JPH_ASSERT(inActiveEdges != 0b111); + + // If inNormal would affect movement less than inTriangleNormal use inNormal + // This is done since it is really hard to make a distinction between sliding over a horizontal triangulated grid and hitting an edge (in this case you want to use the triangle normal) + // and sliding over a triangulated grid and grazing a vertical triangle with an inactive edge (in this case using the triangle normal will cause the object to bounce back so we want to use the calculated normal). + // To solve this we take a movement hint to give an indication of what direction our object is moving. If the edge normal results in less motion difference than the triangle normal we use the edge normal. + float normal_length = inNormal.Length(); + float triangle_normal_length = inTriangleNormal.Length(); + if (inMovementDirection.Dot(inNormal) * triangle_normal_length < inMovementDirection.Dot(inTriangleNormal) * normal_length) + return inNormal; + + // Check: None of the edges are active, we need to use the triangle normal + if (inActiveEdges == 0) + return inTriangleNormal; + + // Some edges are active. + // If normal is parallel to the triangle normal we don't need to check the active edges. + if (inTriangleNormal.Dot(inNormal) > 0.999848f * normal_length * triangle_normal_length) // cos(1 degree) + return inNormal; + + const float cEpsilon = 1.0e-4f; + const float cOneMinusEpsilon = 1.0f - cEpsilon; + + uint colliding_edge; + + // Test where the contact point is in the triangle + float u, v, w; + ClosestPoint::GetBaryCentricCoordinates(inV0 - inPoint, inV1 - inPoint, inV2 - inPoint, u, v, w); + if (u > cOneMinusEpsilon) + { + // Colliding with v0, edge 0 or 2 needs to be active + colliding_edge = 0b101; + } + else if (v > cOneMinusEpsilon) + { + // Colliding with v1, edge 0 or 1 needs to be active + colliding_edge = 0b011; + } + else if (w > cOneMinusEpsilon) + { + // Colliding with v2, edge 1 or 2 needs to be active + colliding_edge = 0b110; + } + else if (u < cEpsilon) + { + // Colliding with edge v1, v2, edge 1 needs to be active + colliding_edge = 0b010; + } + else if (v < cEpsilon) + { + // Colliding with edge v0, v2, edge 2 needs to be active + colliding_edge = 0b100; + } + else if (w < cEpsilon) + { + // Colliding with edge v0, v1, edge 0 needs to be active + colliding_edge = 0b001; + } + else + { + // Interior hit + return inTriangleNormal; + } + + // If this edge is active, use the provided normal instead of the triangle normal + return (inActiveEdges & colliding_edge) != 0? inNormal : inTriangleNormal; + } +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BackFaceMode.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BackFaceMode.h new file mode 100644 index 00000000000..441dcd89a5f --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BackFaceMode.h @@ -0,0 +1,16 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +JPH_NAMESPACE_BEGIN + +/// How collision detection functions will treat back facing triangles +enum class EBackFaceMode : uint8 +{ + IgnoreBackFaces, ///< Ignore collision with back facing surfaces/triangles + CollideWithBackFaces, ///< Collide with back facing surfaces/triangles +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhase.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhase.cpp new file mode 100644 index 00000000000..1317d13393c --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhase.cpp @@ -0,0 +1,16 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include + +JPH_NAMESPACE_BEGIN + +void BroadPhase::Init(BodyManager *inBodyManager, const BroadPhaseLayerInterface &inLayerInterface) +{ + mBodyManager = inBodyManager; +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhase.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhase.h new file mode 100644 index 00000000000..8b6506e901d --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhase.h @@ -0,0 +1,112 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +JPH_NAMESPACE_BEGIN + +// Shorthand function to ifdef out code if broadphase stats tracking is off +#ifdef JPH_TRACK_BROADPHASE_STATS + #define JPH_IF_TRACK_BROADPHASE_STATS(...) __VA_ARGS__ +#else + #define JPH_IF_TRACK_BROADPHASE_STATS(...) +#endif // JPH_TRACK_BROADPHASE_STATS + +class BodyManager; +struct BodyPair; + +using BodyPairCollector = CollisionCollector; + +/// Used to do coarse collision detection operations to quickly prune out bodies that will not collide. +class JPH_EXPORT BroadPhase : public BroadPhaseQuery +{ +public: + /// Initialize the broadphase. + /// @param inBodyManager The body manager singleton + /// @param inLayerInterface Interface that maps object layers to broadphase layers. + /// Note that the broadphase takes a pointer to the data inside inObjectToBroadPhaseLayer so this object should remain static. + virtual void Init(BodyManager *inBodyManager, const BroadPhaseLayerInterface &inLayerInterface); + + /// Should be called after many objects have been inserted to make the broadphase more efficient, usually done on startup only + virtual void Optimize() { /* Optionally overridden by implementation */ } + + /// Must be called just before updating the broadphase when none of the body mutexes are locked + virtual void FrameSync() { /* Optionally overridden by implementation */ } + + /// Must be called before UpdatePrepare to prevent modifications from being made to the tree + virtual void LockModifications() { /* Optionally overridden by implementation */ } + + /// Context used during broadphase update + struct UpdateState { void *mData[4]; }; + + /// Update the broadphase, needs to be called frequently to update the internal state when bodies have been modified. + /// The UpdatePrepare() function can run in a background thread without influencing the broadphase + virtual UpdateState UpdatePrepare() { return UpdateState(); } + + /// Finalizing the update will quickly apply the changes + virtual void UpdateFinalize([[maybe_unused]] const UpdateState &inUpdateState) { /* Optionally overridden by implementation */ } + + /// Must be called after UpdateFinalize to allow modifications to the broadphase + virtual void UnlockModifications() { /* Optionally overridden by implementation */ } + + /// Handle used during adding bodies to the broadphase + using AddState = void *; + + /// Prepare adding inNumber bodies at ioBodies to the broadphase, returns a handle that should be used in AddBodiesFinalize/Abort. + /// This can be done on a background thread without influencing the broadphase. + /// ioBodies may be shuffled around by this function and should be kept that way until AddBodiesFinalize/Abort is called. + virtual AddState AddBodiesPrepare([[maybe_unused]] BodyID *ioBodies, [[maybe_unused]] int inNumber) { return nullptr; } // By default the broadphase doesn't support this + + /// Finalize adding bodies to the broadphase, supply the return value of AddBodiesPrepare in inAddState. + /// Please ensure that the ioBodies array passed to AddBodiesPrepare is unmodified and passed again to this function. + virtual void AddBodiesFinalize(BodyID *ioBodies, int inNumber, AddState inAddState) = 0; + + /// Abort adding bodies to the broadphase, supply the return value of AddBodiesPrepare in inAddState. + /// This can be done on a background thread without influencing the broadphase. + /// Please ensure that the ioBodies array passed to AddBodiesPrepare is unmodified and passed again to this function. + virtual void AddBodiesAbort([[maybe_unused]] BodyID *ioBodies, [[maybe_unused]] int inNumber, [[maybe_unused]] AddState inAddState) { /* By default nothing needs to be done */ } + + /// Remove inNumber bodies in ioBodies from the broadphase. + /// ioBodies may be shuffled around by this function. + virtual void RemoveBodies(BodyID *ioBodies, int inNumber) = 0; + + /// Call whenever the aabb of a body changes (can change order of ioBodies array) + /// inTakeLock should be false if we're between LockModifications/UnlockModificiations in which case care needs to be taken to not call this between UpdatePrepare/UpdateFinalize + virtual void NotifyBodiesAABBChanged(BodyID *ioBodies, int inNumber, bool inTakeLock = true) = 0; + + /// Call whenever the layer (and optionally the aabb as well) of a body changes (can change order of ioBodies array) + virtual void NotifyBodiesLayerChanged(BodyID *ioBodies, int inNumber) = 0; + + /// Find all colliding pairs between dynamic bodies + /// Note that this function is very specifically tailored for the PhysicsSystem::Update function, hence it is not part of the BroadPhaseQuery interface. + /// One of the assumptions it can make is that no locking is needed during the query as it will only be called during a very particular part of the update. + /// @param ioActiveBodies is a list of bodies for which we need to find colliding pairs (this function can change the order of the ioActiveBodies array). This can be a subset of the set of active bodies in the system. + /// @param inNumActiveBodies is the size of the ioActiveBodies array. + /// @param inSpeculativeContactDistance Distance at which speculative contact points will be created. + /// @param inObjectVsBroadPhaseLayerFilter is the filter that determines if an object can collide with a broadphase layer. + /// @param inObjectLayerPairFilter is the filter that determines if two objects can collide. + /// @param ioPairCollector receives callbacks for every body pair found. + virtual void FindCollidingPairs(BodyID *ioActiveBodies, int inNumActiveBodies, float inSpeculativeContactDistance, const ObjectVsBroadPhaseLayerFilter &inObjectVsBroadPhaseLayerFilter, const ObjectLayerPairFilter &inObjectLayerPairFilter, BodyPairCollector &ioPairCollector) const = 0; + + /// Same as BroadPhaseQuery::CastAABox but can be implemented in a way to take no broad phase locks. + virtual void CastAABoxNoLock(const AABoxCast &inBox, CastShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const = 0; + + /// Get the bounding box of all objects in the broadphase + virtual AABox GetBounds() const = 0; + +#ifdef JPH_TRACK_BROADPHASE_STATS + /// Trace the collected broadphase stats in CSV form. + /// This report can be used to judge and tweak the efficiency of the broadphase. + virtual void ReportStats() { /* Can be implemented by derived classes */ } +#endif // JPH_TRACK_BROADPHASE_STATS + +protected: + /// Link to the body manager that manages the bodies in this broadphase + BodyManager * mBodyManager = nullptr; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhaseBruteForce.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhaseBruteForce.cpp new file mode 100644 index 00000000000..10cf30a20c8 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhaseBruteForce.cpp @@ -0,0 +1,313 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +void BroadPhaseBruteForce::AddBodiesFinalize(BodyID *ioBodies, int inNumber, AddState inAddState) +{ + lock_guard lock(mMutex); + + BodyVector &bodies = mBodyManager->GetBodies(); + + // Allocate space + uint32 idx = (uint32)mBodyIDs.size(); + mBodyIDs.resize(idx + inNumber); + + // Add bodies + for (const BodyID *b = ioBodies, *b_end = ioBodies + inNumber; b < b_end; ++b) + { + Body &body = *bodies[b->GetIndex()]; + + // Validate that body ID is consistent with array index + JPH_ASSERT(body.GetID() == *b); + JPH_ASSERT(!body.IsInBroadPhase()); + + // Add it to the list + mBodyIDs[idx] = body.GetID(); + ++idx; + + // Indicate body is in the broadphase + body.SetInBroadPhaseInternal(true); + } + + // Resort + QuickSort(mBodyIDs.begin(), mBodyIDs.end()); +} + +void BroadPhaseBruteForce::RemoveBodies(BodyID *ioBodies, int inNumber) +{ + lock_guard lock(mMutex); + + BodyVector &bodies = mBodyManager->GetBodies(); + + JPH_ASSERT((int)mBodyIDs.size() >= inNumber); + + // Remove bodies + for (const BodyID *b = ioBodies, *b_end = ioBodies + inNumber; b < b_end; ++b) + { + Body &body = *bodies[b->GetIndex()]; + + // Validate that body ID is consistent with array index + JPH_ASSERT(body.GetID() == *b); + JPH_ASSERT(body.IsInBroadPhase()); + + // Find body id + Array::iterator it = lower_bound(mBodyIDs.begin(), mBodyIDs.end(), body.GetID()); + JPH_ASSERT(it != mBodyIDs.end()); + + // Remove element + mBodyIDs.erase(it); + + // Indicate body is no longer in the broadphase + body.SetInBroadPhaseInternal(false); + } +} + +void BroadPhaseBruteForce::NotifyBodiesAABBChanged(BodyID *ioBodies, int inNumber, bool inTakeLock) +{ + // Do nothing, we directly reference the body +} + +void BroadPhaseBruteForce::NotifyBodiesLayerChanged(BodyID * ioBodies, int inNumber) +{ + // Do nothing, we directly reference the body +} + +void BroadPhaseBruteForce::CastRay(const RayCast &inRay, RayCastBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const +{ + shared_lock lock(mMutex); + + // Load ray + Vec3 origin(inRay.mOrigin); + RayInvDirection inv_direction(inRay.mDirection); + + // For all bodies + float early_out_fraction = ioCollector.GetEarlyOutFraction(); + for (BodyID b : mBodyIDs) + { + const Body &body = mBodyManager->GetBody(b); + + // Test layer + if (inObjectLayerFilter.ShouldCollide(body.GetObjectLayer())) + { + // Test intersection with ray + const AABox &bounds = body.GetWorldSpaceBounds(); + float fraction = RayAABox(origin, inv_direction, bounds.mMin, bounds.mMax); + if (fraction < early_out_fraction) + { + // Store hit + BroadPhaseCastResult result { b, fraction }; + ioCollector.AddHit(result); + if (ioCollector.ShouldEarlyOut()) + break; + early_out_fraction = ioCollector.GetEarlyOutFraction(); + } + } + } +} + +void BroadPhaseBruteForce::CollideAABox(const AABox &inBox, CollideShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const +{ + shared_lock lock(mMutex); + + // For all bodies + for (BodyID b : mBodyIDs) + { + const Body &body = mBodyManager->GetBody(b); + + // Test layer + if (inObjectLayerFilter.ShouldCollide(body.GetObjectLayer())) + { + // Test intersection with box + const AABox &bounds = body.GetWorldSpaceBounds(); + if (bounds.Overlaps(inBox)) + { + // Store hit + ioCollector.AddHit(b); + if (ioCollector.ShouldEarlyOut()) + break; + } + } + } +} + +void BroadPhaseBruteForce::CollideSphere(Vec3Arg inCenter, float inRadius, CollideShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const +{ + shared_lock lock(mMutex); + + float radius_sq = Square(inRadius); + + // For all bodies + for (BodyID b : mBodyIDs) + { + const Body &body = mBodyManager->GetBody(b); + + // Test layer + if (inObjectLayerFilter.ShouldCollide(body.GetObjectLayer())) + { + // Test intersection with box + const AABox &bounds = body.GetWorldSpaceBounds(); + if (bounds.GetSqDistanceTo(inCenter) <= radius_sq) + { + // Store hit + ioCollector.AddHit(b); + if (ioCollector.ShouldEarlyOut()) + break; + } + } + } +} + +void BroadPhaseBruteForce::CollidePoint(Vec3Arg inPoint, CollideShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const +{ + shared_lock lock(mMutex); + + // For all bodies + for (BodyID b : mBodyIDs) + { + const Body &body = mBodyManager->GetBody(b); + + // Test layer + if (inObjectLayerFilter.ShouldCollide(body.GetObjectLayer())) + { + // Test intersection with box + const AABox &bounds = body.GetWorldSpaceBounds(); + if (bounds.Contains(inPoint)) + { + // Store hit + ioCollector.AddHit(b); + if (ioCollector.ShouldEarlyOut()) + break; + } + } + } +} + +void BroadPhaseBruteForce::CollideOrientedBox(const OrientedBox &inBox, CollideShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const +{ + shared_lock lock(mMutex); + + // For all bodies + for (BodyID b : mBodyIDs) + { + const Body &body = mBodyManager->GetBody(b); + + // Test layer + if (inObjectLayerFilter.ShouldCollide(body.GetObjectLayer())) + { + // Test intersection with box + const AABox &bounds = body.GetWorldSpaceBounds(); + if (inBox.Overlaps(bounds)) + { + // Store hit + ioCollector.AddHit(b); + if (ioCollector.ShouldEarlyOut()) + break; + } + } + } +} + +void BroadPhaseBruteForce::CastAABoxNoLock(const AABoxCast &inBox, CastShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const +{ + shared_lock lock(mMutex); + + // Load box + Vec3 origin(inBox.mBox.GetCenter()); + Vec3 extent(inBox.mBox.GetExtent()); + RayInvDirection inv_direction(inBox.mDirection); + + // For all bodies + float early_out_fraction = ioCollector.GetPositiveEarlyOutFraction(); + for (BodyID b : mBodyIDs) + { + const Body &body = mBodyManager->GetBody(b); + + // Test layer + if (inObjectLayerFilter.ShouldCollide(body.GetObjectLayer())) + { + // Test intersection with ray + const AABox &bounds = body.GetWorldSpaceBounds(); + float fraction = RayAABox(origin, inv_direction, bounds.mMin - extent, bounds.mMax + extent); + if (fraction < early_out_fraction) + { + // Store hit + BroadPhaseCastResult result { b, fraction }; + ioCollector.AddHit(result); + if (ioCollector.ShouldEarlyOut()) + break; + early_out_fraction = ioCollector.GetPositiveEarlyOutFraction(); + } + } + } +} + +void BroadPhaseBruteForce::CastAABox(const AABoxCast &inBox, CastShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const +{ + CastAABoxNoLock(inBox, ioCollector, inBroadPhaseLayerFilter, inObjectLayerFilter); +} + +void BroadPhaseBruteForce::FindCollidingPairs(BodyID *ioActiveBodies, int inNumActiveBodies, float inSpeculativeContactDistance, const ObjectVsBroadPhaseLayerFilter &inObjectVsBroadPhaseLayerFilter, const ObjectLayerPairFilter &inObjectLayerPairFilter, BodyPairCollector &ioPairCollector) const +{ + shared_lock lock(mMutex); + + // Loop through all active bodies + size_t num_bodies = mBodyIDs.size(); + for (int b1 = 0; b1 < inNumActiveBodies; ++b1) + { + BodyID b1_id = ioActiveBodies[b1]; + const Body &body1 = mBodyManager->GetBody(b1_id); + const ObjectLayer layer1 = body1.GetObjectLayer(); + + // Expand the bounding box by the speculative contact distance + AABox bounds1 = body1.GetWorldSpaceBounds(); + bounds1.ExpandBy(Vec3::sReplicate(inSpeculativeContactDistance)); + + // For all other bodies + for (size_t b2 = 0; b2 < num_bodies; ++b2) + { + // Check if bodies can collide + BodyID b2_id = mBodyIDs[b2]; + const Body &body2 = mBodyManager->GetBody(b2_id); + if (!Body::sFindCollidingPairsCanCollide(body1, body2)) + continue; + + // Check if layers can collide + const ObjectLayer layer2 = body2.GetObjectLayer(); + if (!inObjectLayerPairFilter.ShouldCollide(layer1, layer2)) + continue; + + // Check if bounds overlap + const AABox &bounds2 = body2.GetWorldSpaceBounds(); + if (!bounds1.Overlaps(bounds2)) + continue; + + // Store overlapping pair + ioPairCollector.AddHit({ b1_id, b2_id }); + } + } +} + +AABox BroadPhaseBruteForce::GetBounds() const +{ + shared_lock lock(mMutex); + + AABox bounds; + for (BodyID b : mBodyIDs) + bounds.Encapsulate(mBodyManager->GetBody(b).GetWorldSpaceBounds()); + return bounds; +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhaseBruteForce.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhaseBruteForce.h new file mode 100644 index 00000000000..c3e20f5c8b8 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhaseBruteForce.h @@ -0,0 +1,38 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +JPH_NAMESPACE_BEGIN + +/// Test BroadPhase implementation that does not do anything to speed up the operations. Can be used as a reference implementation. +class JPH_EXPORT BroadPhaseBruteForce final : public BroadPhase +{ +public: + JPH_OVERRIDE_NEW_DELETE + + // Implementing interface of BroadPhase (see BroadPhase for documentation) + virtual void AddBodiesFinalize(BodyID *ioBodies, int inNumber, AddState inAddState) override; + virtual void RemoveBodies(BodyID *ioBodies, int inNumber) override; + virtual void NotifyBodiesAABBChanged(BodyID *ioBodies, int inNumber, bool inTakeLock) override; + virtual void NotifyBodiesLayerChanged(BodyID *ioBodies, int inNumber) override; + virtual void CastRay(const RayCast &inRay, RayCastBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const override; + virtual void CollideAABox(const AABox &inBox, CollideShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const override; + virtual void CollideSphere(Vec3Arg inCenter, float inRadius, CollideShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const override; + virtual void CollidePoint(Vec3Arg inPoint, CollideShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const override; + virtual void CollideOrientedBox(const OrientedBox &inBox, CollideShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const override; + virtual void CastAABoxNoLock(const AABoxCast &inBox, CastShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const override; + virtual void CastAABox(const AABoxCast &inBox, CastShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const override; + virtual void FindCollidingPairs(BodyID *ioActiveBodies, int inNumActiveBodies, float inSpeculativeContactDistance, const ObjectVsBroadPhaseLayerFilter &inObjectVsBroadPhaseLayerFilter, const ObjectLayerPairFilter &inObjectLayerPairFilter, BodyPairCollector &ioPairCollector) const override; + virtual AABox GetBounds() const override; + +private: + Array mBodyIDs; + mutable SharedMutex mMutex; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhaseLayer.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhaseLayer.h new file mode 100644 index 00000000000..bd22cc3a5a6 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhaseLayer.h @@ -0,0 +1,148 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +JPH_NAMESPACE_BEGIN + +/// An object layer can be mapped to a broadphase layer. Objects with the same broadphase layer will end up in the same sub structure (usually a tree) of the broadphase. +/// When there are many layers, this reduces the total amount of sub structures the broad phase needs to manage. Usually you want objects that don't collide with each other +/// in different broad phase layers, but there could be exceptions if objects layers only contain a minor amount of objects so it is not beneficial to give each layer its +/// own sub structure in the broadphase. +/// Note: This class requires explicit casting from and to Type to avoid confusion with ObjectLayer +class BroadPhaseLayer +{ +public: + using Type = uint8; + + JPH_INLINE BroadPhaseLayer() = default; + JPH_INLINE explicit constexpr BroadPhaseLayer(Type inValue) : mValue(inValue) { } + JPH_INLINE constexpr BroadPhaseLayer(const BroadPhaseLayer &) = default; + JPH_INLINE BroadPhaseLayer & operator = (const BroadPhaseLayer &) = default; + + JPH_INLINE constexpr bool operator == (const BroadPhaseLayer &inRHS) const + { + return mValue == inRHS.mValue; + } + + JPH_INLINE constexpr bool operator != (const BroadPhaseLayer &inRHS) const + { + return mValue != inRHS.mValue; + } + + JPH_INLINE constexpr bool operator < (const BroadPhaseLayer &inRHS) const + { + return mValue < inRHS.mValue; + } + + JPH_INLINE explicit constexpr operator Type() const + { + return mValue; + } + + JPH_INLINE Type GetValue() const + { + return mValue; + } + +private: + Type mValue; +}; + +/// Constant value used to indicate an invalid broad phase layer +static constexpr BroadPhaseLayer cBroadPhaseLayerInvalid(0xff); + +/// Interface that the application should implement to allow mapping object layers to broadphase layers +class BroadPhaseLayerInterface : public NonCopyable +{ +public: + /// Destructor + virtual ~BroadPhaseLayerInterface() = default; + + /// Return the number of broadphase layers there are + virtual uint GetNumBroadPhaseLayers() const = 0; + + /// Convert an object layer to the corresponding broadphase layer + virtual BroadPhaseLayer GetBroadPhaseLayer(ObjectLayer inLayer) const = 0; + +#if defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED) + /// Get the user readable name of a broadphase layer (debugging purposes) + virtual const char * GetBroadPhaseLayerName(BroadPhaseLayer inLayer) const = 0; +#endif // JPH_EXTERNAL_PROFILE || JPH_PROFILE_ENABLED +}; + +/// Class to test if an object can collide with a broadphase layer. Used while finding collision pairs. +class ObjectVsBroadPhaseLayerFilter : public NonCopyable +{ +public: + /// Destructor + virtual ~ObjectVsBroadPhaseLayerFilter() = default; + + /// Returns true if an object layer should collide with a broadphase layer + virtual bool ShouldCollide([[maybe_unused]] ObjectLayer inLayer1, [[maybe_unused]] BroadPhaseLayer inLayer2) const + { + return true; + } +}; + +/// Filter class for broadphase layers +class BroadPhaseLayerFilter : public NonCopyable +{ +public: + /// Destructor + virtual ~BroadPhaseLayerFilter() = default; + + /// Function to filter out broadphase layers when doing collision query test (return true to allow testing against objects with this layer) + virtual bool ShouldCollide([[maybe_unused]] BroadPhaseLayer inLayer) const + { + return true; + } +}; + +/// Default filter class that uses the pair filter in combination with a specified layer to filter layers +class DefaultBroadPhaseLayerFilter : public BroadPhaseLayerFilter +{ +public: + /// Constructor + DefaultBroadPhaseLayerFilter(const ObjectVsBroadPhaseLayerFilter &inObjectVsBroadPhaseLayerFilter, ObjectLayer inLayer) : + mObjectVsBroadPhaseLayerFilter(inObjectVsBroadPhaseLayerFilter), + mLayer(inLayer) + { + } + + // See BroadPhaseLayerFilter::ShouldCollide + virtual bool ShouldCollide(BroadPhaseLayer inLayer) const override + { + return mObjectVsBroadPhaseLayerFilter.ShouldCollide(mLayer, inLayer); + } + +private: + const ObjectVsBroadPhaseLayerFilter &mObjectVsBroadPhaseLayerFilter; + ObjectLayer mLayer; +}; + +/// Allows objects from a specific broad phase layer only +class SpecifiedBroadPhaseLayerFilter : public BroadPhaseLayerFilter +{ +public: + /// Constructor + explicit SpecifiedBroadPhaseLayerFilter(BroadPhaseLayer inLayer) : + mLayer(inLayer) + { + } + + // See BroadPhaseLayerFilter::ShouldCollide + virtual bool ShouldCollide(BroadPhaseLayer inLayer) const override + { + return mLayer == inLayer; + } + +private: + BroadPhaseLayer mLayer; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhaseLayerInterfaceMask.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhaseLayerInterfaceMask.h new file mode 100644 index 00000000000..15a894bb12c --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhaseLayerInterfaceMask.h @@ -0,0 +1,92 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2023 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +JPH_NAMESPACE_BEGIN + +/// BroadPhaseLayerInterface implementation. +/// This defines a mapping between object and broadphase layers. +/// This implementation works together with ObjectLayerPairFilterMask and ObjectVsBroadPhaseLayerFilterMask. +/// A broadphase layer is suitable for an object if its group & inGroupsToInclude is not zero and its group & inGroupsToExclude is zero. +/// The broadphase layers are iterated from lowest to highest value and the first one that matches is taken. If none match then it takes the last layer. +class BroadPhaseLayerInterfaceMask : public BroadPhaseLayerInterface +{ +public: + JPH_OVERRIDE_NEW_DELETE + + explicit BroadPhaseLayerInterfaceMask(uint inNumBroadPhaseLayers) + { + JPH_ASSERT(inNumBroadPhaseLayers > 0); + mMapping.resize(inNumBroadPhaseLayers); + +#if defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED) + mBroadPhaseLayerNames.resize(inNumBroadPhaseLayers, "Undefined"); +#endif // JPH_EXTERNAL_PROFILE || JPH_PROFILE_ENABLED + } + + // Configures a broadphase layer. + void ConfigureLayer(BroadPhaseLayer inBroadPhaseLayer, uint32 inGroupsToInclude, uint32 inGroupsToExclude) + { + JPH_ASSERT((BroadPhaseLayer::Type)inBroadPhaseLayer < (uint)mMapping.size()); + Mapping &m = mMapping[(BroadPhaseLayer::Type)inBroadPhaseLayer]; + m.mGroupsToInclude = inGroupsToInclude; + m.mGroupsToExclude = inGroupsToExclude; + } + + virtual uint GetNumBroadPhaseLayers() const override + { + return (uint)mMapping.size(); + } + + virtual BroadPhaseLayer GetBroadPhaseLayer(ObjectLayer inLayer) const override + { + // Try to find the first broadphase layer that matches + uint32 group = ObjectLayerPairFilterMask::sGetGroup(inLayer); + for (const Mapping &m : mMapping) + if ((group & m.mGroupsToInclude) != 0 && (group & m.mGroupsToExclude) == 0) + return BroadPhaseLayer(BroadPhaseLayer::Type(&m - mMapping.data())); + + // Fall back to the last broadphase layer + return BroadPhaseLayer(BroadPhaseLayer::Type(mMapping.size() - 1)); + } + + /// Returns true if an object layer should collide with a broadphase layer, this function is being called from ObjectVsBroadPhaseLayerFilterMask + inline bool ShouldCollide(ObjectLayer inLayer1, BroadPhaseLayer inLayer2) const + { + uint32 mask = ObjectLayerPairFilterMask::sGetMask(inLayer1); + const Mapping &m = mMapping[(BroadPhaseLayer::Type)inLayer2]; + return &m == &mMapping.back() // Last layer may collide with anything + || (m.mGroupsToInclude & mask) != 0; // Mask allows it to collide with objects that could reside in this layer + } + +#if defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED) + void SetBroadPhaseLayerName(BroadPhaseLayer inLayer, const char *inName) + { + mBroadPhaseLayerNames[(BroadPhaseLayer::Type)inLayer] = inName; + } + + virtual const char * GetBroadPhaseLayerName(BroadPhaseLayer inLayer) const override + { + return mBroadPhaseLayerNames[(BroadPhaseLayer::Type)inLayer]; + } +#endif // JPH_EXTERNAL_PROFILE || JPH_PROFILE_ENABLED + +private: + struct Mapping + { + uint32 mGroupsToInclude = 0; + uint32 mGroupsToExclude = ~uint32(0); + }; + Array mMapping; + +#if defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED) + Array mBroadPhaseLayerNames; +#endif // JPH_EXTERNAL_PROFILE || JPH_PROFILE_ENABLED +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhaseLayerInterfaceTable.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhaseLayerInterfaceTable.h new file mode 100644 index 00000000000..e777a08589b --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhaseLayerInterfaceTable.h @@ -0,0 +1,64 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2023 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +/// BroadPhaseLayerInterface implementation. +/// This defines a mapping between object and broadphase layers. +/// This implementation uses a simple table +class BroadPhaseLayerInterfaceTable : public BroadPhaseLayerInterface +{ +public: + JPH_OVERRIDE_NEW_DELETE + + BroadPhaseLayerInterfaceTable(uint inNumObjectLayers, uint inNumBroadPhaseLayers) : + mNumBroadPhaseLayers(inNumBroadPhaseLayers) + { + mObjectToBroadPhase.resize(inNumObjectLayers, BroadPhaseLayer(0)); +#if defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED) + mBroadPhaseLayerNames.resize(inNumBroadPhaseLayers, "Undefined"); +#endif // JPH_EXTERNAL_PROFILE || JPH_PROFILE_ENABLED + } + + void MapObjectToBroadPhaseLayer(ObjectLayer inObjectLayer, BroadPhaseLayer inBroadPhaseLayer) + { + JPH_ASSERT((BroadPhaseLayer::Type)inBroadPhaseLayer < mNumBroadPhaseLayers); + mObjectToBroadPhase[inObjectLayer] = inBroadPhaseLayer; + } + + virtual uint GetNumBroadPhaseLayers() const override + { + return mNumBroadPhaseLayers; + } + + virtual BroadPhaseLayer GetBroadPhaseLayer(ObjectLayer inLayer) const override + { + return mObjectToBroadPhase[inLayer]; + } + +#if defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED) + void SetBroadPhaseLayerName(BroadPhaseLayer inLayer, const char *inName) + { + mBroadPhaseLayerNames[(BroadPhaseLayer::Type)inLayer] = inName; + } + + virtual const char * GetBroadPhaseLayerName(BroadPhaseLayer inLayer) const override + { + return mBroadPhaseLayerNames[(BroadPhaseLayer::Type)inLayer]; + } +#endif // JPH_EXTERNAL_PROFILE || JPH_PROFILE_ENABLED + +private: + uint mNumBroadPhaseLayers; + Array mObjectToBroadPhase; +#if defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED) + Array mBroadPhaseLayerNames; +#endif // JPH_EXTERNAL_PROFILE || JPH_PROFILE_ENABLED +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhaseQuadTree.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhaseQuadTree.cpp new file mode 100644 index 00000000000..0c3d480ba30 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhaseQuadTree.cpp @@ -0,0 +1,609 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include +#include +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +BroadPhaseQuadTree::~BroadPhaseQuadTree() +{ + delete [] mLayers; +} + +void BroadPhaseQuadTree::Init(BodyManager *inBodyManager, const BroadPhaseLayerInterface &inLayerInterface) +{ + BroadPhase::Init(inBodyManager, inLayerInterface); + + // Store input parameters + mBroadPhaseLayerInterface = &inLayerInterface; + mNumLayers = inLayerInterface.GetNumBroadPhaseLayers(); + JPH_ASSERT(mNumLayers < (BroadPhaseLayer::Type)cBroadPhaseLayerInvalid); + +#ifdef JPH_ENABLE_ASSERTS + // Store lock context + mLockContext = inBodyManager; +#endif // JPH_ENABLE_ASSERTS + + // Store max bodies + mMaxBodies = inBodyManager->GetMaxBodies(); + + // Initialize tracking data + mTracking.resize(mMaxBodies); + + // Init allocator + // Estimate the amount of nodes we're going to need + uint32 num_leaves = (uint32)(mMaxBodies + 1) / 2; // Assume 50% fill + uint32 num_leaves_plus_internal_nodes = num_leaves + (num_leaves + 2) / 3; // = Sum(num_leaves * 4^-i) with i = [0, Inf]. + mAllocator.Init(2 * num_leaves_plus_internal_nodes, 256); // We use double the amount of nodes while rebuilding the tree during Update() + + // Init sub trees + mLayers = new QuadTree [mNumLayers]; + for (uint l = 0; l < mNumLayers; ++l) + { + mLayers[l].Init(mAllocator); + +#if defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED) + // Set the name of the layer + mLayers[l].SetName(inLayerInterface.GetBroadPhaseLayerName(BroadPhaseLayer(BroadPhaseLayer::Type(l)))); +#endif // JPH_EXTERNAL_PROFILE || JPH_PROFILE_ENABLED + } +} + +void BroadPhaseQuadTree::FrameSync() +{ + JPH_PROFILE_FUNCTION(); + + // Take a unique lock on the old query lock so that we know no one is using the old nodes anymore. + // Note that nothing should be locked at this point to avoid risking a lock inversion deadlock. + // Note that in other places where we lock this mutex we don't use SharedLock to detect lock inversions. As long as + // nothing else is locked this is safe. This is why BroadPhaseQuery should be the highest priority lock. + UniqueLock root_lock(mQueryLocks[mQueryLockIdx ^ 1] JPH_IF_ENABLE_ASSERTS(, mLockContext, EPhysicsLockTypes::BroadPhaseQuery)); + + for (BroadPhaseLayer::Type l = 0; l < mNumLayers; ++l) + mLayers[l].DiscardOldTree(); +} + +void BroadPhaseQuadTree::Optimize() +{ + JPH_PROFILE_FUNCTION(); + + FrameSync(); + + LockModifications(); + + for (uint l = 0; l < mNumLayers; ++l) + { + QuadTree &tree = mLayers[l]; + if (tree.HasBodies()) + { + QuadTree::UpdateState update_state; + tree.UpdatePrepare(mBodyManager->GetBodies(), mTracking, update_state, true); + tree.UpdateFinalize(mBodyManager->GetBodies(), mTracking, update_state); + } + } + + UnlockModifications(); + + mNextLayerToUpdate = 0; +} + +void BroadPhaseQuadTree::LockModifications() +{ + // From this point on we prevent modifications to the tree + PhysicsLock::sLock(mUpdateMutex JPH_IF_ENABLE_ASSERTS(, mLockContext, EPhysicsLockTypes::BroadPhaseUpdate)); +} + +BroadPhase::UpdateState BroadPhaseQuadTree::UpdatePrepare() +{ + // LockModifications should have been called + JPH_ASSERT(mUpdateMutex.is_locked()); + + // Create update state + UpdateState update_state; + UpdateStateImpl *update_state_impl = reinterpret_cast(&update_state); + + // Loop until we've seen all layers + for (uint iteration = 0; iteration < mNumLayers; ++iteration) + { + // Get the layer + QuadTree &tree = mLayers[mNextLayerToUpdate]; + mNextLayerToUpdate = (mNextLayerToUpdate + 1) % mNumLayers; + + // If it is dirty we update this one + if (tree.HasBodies() && tree.IsDirty() && tree.CanBeUpdated()) + { + update_state_impl->mTree = &tree; + tree.UpdatePrepare(mBodyManager->GetBodies(), mTracking, update_state_impl->mUpdateState, false); + return update_state; + } + } + + // Nothing to update + update_state_impl->mTree = nullptr; + return update_state; +} + +void BroadPhaseQuadTree::UpdateFinalize(const UpdateState &inUpdateState) +{ + // LockModifications should have been called + JPH_ASSERT(mUpdateMutex.is_locked()); + + // Test if a tree was updated + const UpdateStateImpl *update_state_impl = reinterpret_cast(&inUpdateState); + if (update_state_impl->mTree == nullptr) + return; + + update_state_impl->mTree->UpdateFinalize(mBodyManager->GetBodies(), mTracking, update_state_impl->mUpdateState); + + // Make all queries from now on use the new lock + mQueryLockIdx = mQueryLockIdx ^ 1; +} + +void BroadPhaseQuadTree::UnlockModifications() +{ + // From this point on we allow modifications to the tree again + PhysicsLock::sUnlock(mUpdateMutex JPH_IF_ENABLE_ASSERTS(, mLockContext, EPhysicsLockTypes::BroadPhaseUpdate)); +} + +BroadPhase::AddState BroadPhaseQuadTree::AddBodiesPrepare(BodyID *ioBodies, int inNumber) +{ + JPH_PROFILE_FUNCTION(); + + JPH_ASSERT(inNumber > 0); + + const BodyVector &bodies = mBodyManager->GetBodies(); + JPH_ASSERT(mMaxBodies == mBodyManager->GetMaxBodies()); + + LayerState *state = new LayerState [mNumLayers]; + + // Sort bodies on layer + Body * const * const bodies_ptr = bodies.data(); // C pointer or else sort is incredibly slow in debug mode + QuickSort(ioBodies, ioBodies + inNumber, [bodies_ptr](BodyID inLHS, BodyID inRHS) { return bodies_ptr[inLHS.GetIndex()]->GetBroadPhaseLayer() < bodies_ptr[inRHS.GetIndex()]->GetBroadPhaseLayer(); }); + + BodyID *b_start = ioBodies, *b_end = ioBodies + inNumber; + while (b_start < b_end) + { + // Get broadphase layer + BroadPhaseLayer::Type broadphase_layer = (BroadPhaseLayer::Type)bodies[b_start->GetIndex()]->GetBroadPhaseLayer(); + JPH_ASSERT(broadphase_layer < mNumLayers); + + // Find first body with different layer + BodyID *b_mid = std::upper_bound(b_start, b_end, broadphase_layer, [bodies_ptr](BroadPhaseLayer::Type inLayer, BodyID inBodyID) { return inLayer < (BroadPhaseLayer::Type)bodies_ptr[inBodyID.GetIndex()]->GetBroadPhaseLayer(); }); + + // Keep track of state for this layer + LayerState &layer_state = state[broadphase_layer]; + layer_state.mBodyStart = b_start; + layer_state.mBodyEnd = b_mid; + + // Insert all bodies of the same layer + mLayers[broadphase_layer].AddBodiesPrepare(bodies, mTracking, b_start, int(b_mid - b_start), layer_state.mAddState); + + // Keep track in which tree we placed the object + for (const BodyID *b = b_start; b < b_mid; ++b) + { + uint32 index = b->GetIndex(); + JPH_ASSERT(bodies[index]->GetID() == *b, "Provided BodyID doesn't match BodyID in body manager"); + JPH_ASSERT(!bodies[index]->IsInBroadPhase()); + Tracking &t = mTracking[index]; + JPH_ASSERT(t.mBroadPhaseLayer == (BroadPhaseLayer::Type)cBroadPhaseLayerInvalid); + t.mBroadPhaseLayer = broadphase_layer; + JPH_ASSERT(t.mObjectLayer == cObjectLayerInvalid); + t.mObjectLayer = bodies[index]->GetObjectLayer(); + } + + // Repeat + b_start = b_mid; + } + + return state; +} + +void BroadPhaseQuadTree::AddBodiesFinalize(BodyID *ioBodies, int inNumber, AddState inAddState) +{ + JPH_PROFILE_FUNCTION(); + + // This cannot run concurrently with UpdatePrepare()/UpdateFinalize() + SharedLock lock(mUpdateMutex JPH_IF_ENABLE_ASSERTS(, mLockContext, EPhysicsLockTypes::BroadPhaseUpdate)); + + BodyVector &bodies = mBodyManager->GetBodies(); + JPH_ASSERT(mMaxBodies == mBodyManager->GetMaxBodies()); + + LayerState *state = (LayerState *)inAddState; + + for (BroadPhaseLayer::Type broadphase_layer = 0; broadphase_layer < mNumLayers; broadphase_layer++) + { + const LayerState &l = state[broadphase_layer]; + if (l.mBodyStart != nullptr) + { + // Insert all bodies of the same layer + mLayers[broadphase_layer].AddBodiesFinalize(mTracking, int(l.mBodyEnd - l.mBodyStart), l.mAddState); + + // Mark added to broadphase + for (const BodyID *b = l.mBodyStart; b < l.mBodyEnd; ++b) + { + uint32 index = b->GetIndex(); + JPH_ASSERT(bodies[index]->GetID() == *b, "Provided BodyID doesn't match BodyID in body manager"); + JPH_ASSERT(mTracking[index].mBroadPhaseLayer == broadphase_layer); + JPH_ASSERT(mTracking[index].mObjectLayer == bodies[index]->GetObjectLayer()); + JPH_ASSERT(!bodies[index]->IsInBroadPhase()); + bodies[index]->SetInBroadPhaseInternal(true); + } + } + } + + delete [] state; +} + +void BroadPhaseQuadTree::AddBodiesAbort(BodyID *ioBodies, int inNumber, AddState inAddState) +{ + JPH_PROFILE_FUNCTION(); + + JPH_IF_ENABLE_ASSERTS(const BodyVector &bodies = mBodyManager->GetBodies();) + JPH_ASSERT(mMaxBodies == mBodyManager->GetMaxBodies()); + + LayerState *state = (LayerState *)inAddState; + + for (BroadPhaseLayer::Type broadphase_layer = 0; broadphase_layer < mNumLayers; broadphase_layer++) + { + const LayerState &l = state[broadphase_layer]; + if (l.mBodyStart != nullptr) + { + // Insert all bodies of the same layer + mLayers[broadphase_layer].AddBodiesAbort(mTracking, l.mAddState); + + // Reset bookkeeping + for (const BodyID *b = l.mBodyStart; b < l.mBodyEnd; ++b) + { + uint32 index = b->GetIndex(); + JPH_ASSERT(bodies[index]->GetID() == *b, "Provided BodyID doesn't match BodyID in body manager"); + JPH_ASSERT(!bodies[index]->IsInBroadPhase()); + Tracking &t = mTracking[index]; + JPH_ASSERT(t.mBroadPhaseLayer == broadphase_layer); + t.mBroadPhaseLayer = (BroadPhaseLayer::Type)cBroadPhaseLayerInvalid; + t.mObjectLayer = cObjectLayerInvalid; + } + } + } + + delete [] state; +} + +void BroadPhaseQuadTree::RemoveBodies(BodyID *ioBodies, int inNumber) +{ + JPH_PROFILE_FUNCTION(); + + // This cannot run concurrently with UpdatePrepare()/UpdateFinalize() + SharedLock lock(mUpdateMutex JPH_IF_ENABLE_ASSERTS(, mLockContext, EPhysicsLockTypes::BroadPhaseUpdate)); + + JPH_ASSERT(inNumber > 0); + + BodyVector &bodies = mBodyManager->GetBodies(); + JPH_ASSERT(mMaxBodies == mBodyManager->GetMaxBodies()); + + // Sort bodies on layer + Tracking *tracking = mTracking.data(); // C pointer or else sort is incredibly slow in debug mode + QuickSort(ioBodies, ioBodies + inNumber, [tracking](BodyID inLHS, BodyID inRHS) { return tracking[inLHS.GetIndex()].mBroadPhaseLayer < tracking[inRHS.GetIndex()].mBroadPhaseLayer; }); + + BodyID *b_start = ioBodies, *b_end = ioBodies + inNumber; + while (b_start < b_end) + { + // Get broad phase layer + BroadPhaseLayer::Type broadphase_layer = mTracking[b_start->GetIndex()].mBroadPhaseLayer; + JPH_ASSERT(broadphase_layer != (BroadPhaseLayer::Type)cBroadPhaseLayerInvalid); + + // Find first body with different layer + BodyID *b_mid = std::upper_bound(b_start, b_end, broadphase_layer, [tracking](BroadPhaseLayer::Type inLayer, BodyID inBodyID) { return inLayer < tracking[inBodyID.GetIndex()].mBroadPhaseLayer; }); + + // Remove all bodies of the same layer + mLayers[broadphase_layer].RemoveBodies(bodies, mTracking, b_start, int(b_mid - b_start)); + + for (const BodyID *b = b_start; b < b_mid; ++b) + { + // Reset bookkeeping + uint32 index = b->GetIndex(); + Tracking &t = tracking[index]; + t.mBroadPhaseLayer = (BroadPhaseLayer::Type)cBroadPhaseLayerInvalid; + t.mObjectLayer = cObjectLayerInvalid; + + // Mark removed from broadphase + JPH_ASSERT(bodies[index]->IsInBroadPhase()); + bodies[index]->SetInBroadPhaseInternal(false); + } + + // Repeat + b_start = b_mid; + } +} + +void BroadPhaseQuadTree::NotifyBodiesAABBChanged(BodyID *ioBodies, int inNumber, bool inTakeLock) +{ + JPH_PROFILE_FUNCTION(); + + JPH_ASSERT(inNumber > 0); + + // This cannot run concurrently with UpdatePrepare()/UpdateFinalize() + if (inTakeLock) + PhysicsLock::sLockShared(mUpdateMutex JPH_IF_ENABLE_ASSERTS(, mLockContext, EPhysicsLockTypes::BroadPhaseUpdate)); + else + JPH_ASSERT(mUpdateMutex.is_locked()); + + const BodyVector &bodies = mBodyManager->GetBodies(); + JPH_ASSERT(mMaxBodies == mBodyManager->GetMaxBodies()); + + // Sort bodies on layer + const Tracking *tracking = mTracking.data(); // C pointer or else sort is incredibly slow in debug mode + QuickSort(ioBodies, ioBodies + inNumber, [tracking](BodyID inLHS, BodyID inRHS) { return tracking[inLHS.GetIndex()].mBroadPhaseLayer < tracking[inRHS.GetIndex()].mBroadPhaseLayer; }); + + BodyID *b_start = ioBodies, *b_end = ioBodies + inNumber; + while (b_start < b_end) + { + // Get broadphase layer + BroadPhaseLayer::Type broadphase_layer = tracking[b_start->GetIndex()].mBroadPhaseLayer; + JPH_ASSERT(broadphase_layer != (BroadPhaseLayer::Type)cBroadPhaseLayerInvalid); + + // Find first body with different layer + BodyID *b_mid = std::upper_bound(b_start, b_end, broadphase_layer, [tracking](BroadPhaseLayer::Type inLayer, BodyID inBodyID) { return inLayer < tracking[inBodyID.GetIndex()].mBroadPhaseLayer; }); + + // Nodify all bodies of the same layer changed + mLayers[broadphase_layer].NotifyBodiesAABBChanged(bodies, mTracking, b_start, int(b_mid - b_start)); + + // Repeat + b_start = b_mid; + } + + if (inTakeLock) + PhysicsLock::sUnlockShared(mUpdateMutex JPH_IF_ENABLE_ASSERTS(, mLockContext, EPhysicsLockTypes::BroadPhaseUpdate)); +} + +void BroadPhaseQuadTree::NotifyBodiesLayerChanged(BodyID *ioBodies, int inNumber) +{ + JPH_PROFILE_FUNCTION(); + + JPH_ASSERT(inNumber > 0); + + // First sort the bodies that actually changed layer to beginning of the array + const BodyVector &bodies = mBodyManager->GetBodies(); + JPH_ASSERT(mMaxBodies == mBodyManager->GetMaxBodies()); + for (BodyID *body_id = ioBodies + inNumber - 1; body_id >= ioBodies; --body_id) + { + uint32 index = body_id->GetIndex(); + JPH_ASSERT(bodies[index]->GetID() == *body_id, "Provided BodyID doesn't match BodyID in body manager"); + const Body *body = bodies[index]; + BroadPhaseLayer::Type broadphase_layer = (BroadPhaseLayer::Type)body->GetBroadPhaseLayer(); + JPH_ASSERT(broadphase_layer < mNumLayers); + if (mTracking[index].mBroadPhaseLayer == broadphase_layer) + { + // Update tracking information + mTracking[index].mObjectLayer = body->GetObjectLayer(); + + // Move the body to the end, layer didn't change + swap(*body_id, ioBodies[inNumber - 1]); + --inNumber; + } + } + + if (inNumber > 0) + { + // Changing layer requires us to remove from one tree and add to another, so this is equivalent to removing all bodies first and then adding them again + RemoveBodies(ioBodies, inNumber); + AddState add_state = AddBodiesPrepare(ioBodies, inNumber); + AddBodiesFinalize(ioBodies, inNumber, add_state); + } +} + +void BroadPhaseQuadTree::CastRay(const RayCast &inRay, RayCastBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const +{ + JPH_PROFILE_FUNCTION(); + + JPH_ASSERT(mMaxBodies == mBodyManager->GetMaxBodies()); + + // Prevent this from running in parallel with node deletion in FrameSync(), see notes there + shared_lock lock(mQueryLocks[mQueryLockIdx]); + + // Loop over all layers and test the ones that could hit + for (BroadPhaseLayer::Type l = 0; l < mNumLayers; ++l) + { + const QuadTree &tree = mLayers[l]; + if (tree.HasBodies() && inBroadPhaseLayerFilter.ShouldCollide(BroadPhaseLayer(l))) + { + JPH_PROFILE(tree.GetName()); + tree.CastRay(inRay, ioCollector, inObjectLayerFilter, mTracking); + if (ioCollector.ShouldEarlyOut()) + break; + } + } +} + +void BroadPhaseQuadTree::CollideAABox(const AABox &inBox, CollideShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const +{ + JPH_PROFILE_FUNCTION(); + + JPH_ASSERT(mMaxBodies == mBodyManager->GetMaxBodies()); + + // Prevent this from running in parallel with node deletion in FrameSync(), see notes there + shared_lock lock(mQueryLocks[mQueryLockIdx]); + + // Loop over all layers and test the ones that could hit + for (BroadPhaseLayer::Type l = 0; l < mNumLayers; ++l) + { + const QuadTree &tree = mLayers[l]; + if (tree.HasBodies() && inBroadPhaseLayerFilter.ShouldCollide(BroadPhaseLayer(l))) + { + JPH_PROFILE(tree.GetName()); + tree.CollideAABox(inBox, ioCollector, inObjectLayerFilter, mTracking); + if (ioCollector.ShouldEarlyOut()) + break; + } + } +} + +void BroadPhaseQuadTree::CollideSphere(Vec3Arg inCenter, float inRadius, CollideShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const +{ + JPH_PROFILE_FUNCTION(); + + JPH_ASSERT(mMaxBodies == mBodyManager->GetMaxBodies()); + + // Prevent this from running in parallel with node deletion in FrameSync(), see notes there + shared_lock lock(mQueryLocks[mQueryLockIdx]); + + // Loop over all layers and test the ones that could hit + for (BroadPhaseLayer::Type l = 0; l < mNumLayers; ++l) + { + const QuadTree &tree = mLayers[l]; + if (tree.HasBodies() && inBroadPhaseLayerFilter.ShouldCollide(BroadPhaseLayer(l))) + { + JPH_PROFILE(tree.GetName()); + tree.CollideSphere(inCenter, inRadius, ioCollector, inObjectLayerFilter, mTracking); + if (ioCollector.ShouldEarlyOut()) + break; + } + } +} + +void BroadPhaseQuadTree::CollidePoint(Vec3Arg inPoint, CollideShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const +{ + JPH_PROFILE_FUNCTION(); + + JPH_ASSERT(mMaxBodies == mBodyManager->GetMaxBodies()); + + // Prevent this from running in parallel with node deletion in FrameSync(), see notes there + shared_lock lock(mQueryLocks[mQueryLockIdx]); + + // Loop over all layers and test the ones that could hit + for (BroadPhaseLayer::Type l = 0; l < mNumLayers; ++l) + { + const QuadTree &tree = mLayers[l]; + if (tree.HasBodies() && inBroadPhaseLayerFilter.ShouldCollide(BroadPhaseLayer(l))) + { + JPH_PROFILE(tree.GetName()); + tree.CollidePoint(inPoint, ioCollector, inObjectLayerFilter, mTracking); + if (ioCollector.ShouldEarlyOut()) + break; + } + } +} + +void BroadPhaseQuadTree::CollideOrientedBox(const OrientedBox &inBox, CollideShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const +{ + JPH_PROFILE_FUNCTION(); + + JPH_ASSERT(mMaxBodies == mBodyManager->GetMaxBodies()); + + // Prevent this from running in parallel with node deletion in FrameSync(), see notes there + shared_lock lock(mQueryLocks[mQueryLockIdx]); + + // Loop over all layers and test the ones that could hit + for (BroadPhaseLayer::Type l = 0; l < mNumLayers; ++l) + { + const QuadTree &tree = mLayers[l]; + if (tree.HasBodies() && inBroadPhaseLayerFilter.ShouldCollide(BroadPhaseLayer(l))) + { + JPH_PROFILE(tree.GetName()); + tree.CollideOrientedBox(inBox, ioCollector, inObjectLayerFilter, mTracking); + if (ioCollector.ShouldEarlyOut()) + break; + } + } +} + +void BroadPhaseQuadTree::CastAABoxNoLock(const AABoxCast &inBox, CastShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const +{ + JPH_PROFILE_FUNCTION(); + + JPH_ASSERT(mMaxBodies == mBodyManager->GetMaxBodies()); + + // Loop over all layers and test the ones that could hit + for (BroadPhaseLayer::Type l = 0; l < mNumLayers; ++l) + { + const QuadTree &tree = mLayers[l]; + if (tree.HasBodies() && inBroadPhaseLayerFilter.ShouldCollide(BroadPhaseLayer(l))) + { + JPH_PROFILE(tree.GetName()); + tree.CastAABox(inBox, ioCollector, inObjectLayerFilter, mTracking); + if (ioCollector.ShouldEarlyOut()) + break; + } + } +} + +void BroadPhaseQuadTree::CastAABox(const AABoxCast &inBox, CastShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const +{ + // Prevent this from running in parallel with node deletion in FrameSync(), see notes there + shared_lock lock(mQueryLocks[mQueryLockIdx]); + + CastAABoxNoLock(inBox, ioCollector, inBroadPhaseLayerFilter, inObjectLayerFilter); +} + +void BroadPhaseQuadTree::FindCollidingPairs(BodyID *ioActiveBodies, int inNumActiveBodies, float inSpeculativeContactDistance, const ObjectVsBroadPhaseLayerFilter &inObjectVsBroadPhaseLayerFilter, const ObjectLayerPairFilter &inObjectLayerPairFilter, BodyPairCollector &ioPairCollector) const +{ + JPH_PROFILE_FUNCTION(); + + const BodyVector &bodies = mBodyManager->GetBodies(); + JPH_ASSERT(mMaxBodies == mBodyManager->GetMaxBodies()); + + // Note that we don't take any locks at this point. We know that the tree is not going to be swapped or deleted while finding collision pairs due to the way the jobs are scheduled in the PhysicsSystem::Update. + + // Sort bodies on layer + const Tracking *tracking = mTracking.data(); // C pointer or else sort is incredibly slow in debug mode + QuickSort(ioActiveBodies, ioActiveBodies + inNumActiveBodies, [tracking](BodyID inLHS, BodyID inRHS) { return tracking[inLHS.GetIndex()].mObjectLayer < tracking[inRHS.GetIndex()].mObjectLayer; }); + + BodyID *b_start = ioActiveBodies, *b_end = ioActiveBodies + inNumActiveBodies; + while (b_start < b_end) + { + // Get broadphase layer + ObjectLayer object_layer = tracking[b_start->GetIndex()].mObjectLayer; + JPH_ASSERT(object_layer != cObjectLayerInvalid); + + // Find first body with different layer + BodyID *b_mid = std::upper_bound(b_start, b_end, object_layer, [tracking](ObjectLayer inLayer, BodyID inBodyID) { return inLayer < tracking[inBodyID.GetIndex()].mObjectLayer; }); + + // Loop over all layers and test the ones that could hit + for (BroadPhaseLayer::Type l = 0; l < mNumLayers; ++l) + { + const QuadTree &tree = mLayers[l]; + if (tree.HasBodies() && inObjectVsBroadPhaseLayerFilter.ShouldCollide(object_layer, BroadPhaseLayer(l))) + { + JPH_PROFILE(tree.GetName()); + tree.FindCollidingPairs(bodies, b_start, int(b_mid - b_start), inSpeculativeContactDistance, ioPairCollector, inObjectLayerPairFilter); + } + } + + // Repeat + b_start = b_mid; + } +} + +AABox BroadPhaseQuadTree::GetBounds() const +{ + // Prevent this from running in parallel with node deletion in FrameSync(), see notes there + shared_lock lock(mQueryLocks[mQueryLockIdx]); + + AABox bounds; + for (BroadPhaseLayer::Type l = 0; l < mNumLayers; ++l) + bounds.Encapsulate(mLayers[l].GetBounds()); + return bounds; +} + +#ifdef JPH_TRACK_BROADPHASE_STATS + +void BroadPhaseQuadTree::ReportStats() +{ + Trace("Query Type, Filter Description, Tree Name, Num Queries, Total Time (%%), Total Time Excl. Collector (%%), Nodes Visited, Bodies Visited, Hits Reported, Hits Reported vs Bodies Visited (%%), Hits Reported vs Nodes Visited"); + + uint64 total_ticks = 0; + for (BroadPhaseLayer::Type l = 0; l < mNumLayers; ++l) + total_ticks += mLayers[l].GetTicks100Pct(); + + for (BroadPhaseLayer::Type l = 0; l < mNumLayers; ++l) + mLayers[l].ReportStats(total_ticks); +} + +#endif // JPH_TRACK_BROADPHASE_STATS + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhaseQuadTree.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhaseQuadTree.h new file mode 100644 index 00000000000..ae97c10f0e3 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhaseQuadTree.h @@ -0,0 +1,108 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +/// Fast SIMD based quad tree BroadPhase that is multithreading aware and tries to do a minimal amount of locking. +class JPH_EXPORT BroadPhaseQuadTree final : public BroadPhase +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Destructor + virtual ~BroadPhaseQuadTree() override; + + // Implementing interface of BroadPhase (see BroadPhase for documentation) + virtual void Init(BodyManager *inBodyManager, const BroadPhaseLayerInterface &inLayerInterface) override; + virtual void Optimize() override; + virtual void FrameSync() override; + virtual void LockModifications() override; + virtual UpdateState UpdatePrepare() override; + virtual void UpdateFinalize(const UpdateState &inUpdateState) override; + virtual void UnlockModifications() override; + virtual AddState AddBodiesPrepare(BodyID *ioBodies, int inNumber) override; + virtual void AddBodiesFinalize(BodyID *ioBodies, int inNumber, AddState inAddState) override; + virtual void AddBodiesAbort(BodyID *ioBodies, int inNumber, AddState inAddState) override; + virtual void RemoveBodies(BodyID *ioBodies, int inNumber) override; + virtual void NotifyBodiesAABBChanged(BodyID *ioBodies, int inNumber, bool inTakeLock) override; + virtual void NotifyBodiesLayerChanged(BodyID *ioBodies, int inNumber) override; + virtual void CastRay(const RayCast &inRay, RayCastBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const override; + virtual void CollideAABox(const AABox &inBox, CollideShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const override; + virtual void CollideSphere(Vec3Arg inCenter, float inRadius, CollideShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const override; + virtual void CollidePoint(Vec3Arg inPoint, CollideShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const override; + virtual void CollideOrientedBox(const OrientedBox &inBox, CollideShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const override; + virtual void CastAABoxNoLock(const AABoxCast &inBox, CastShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const override; + virtual void CastAABox(const AABoxCast &inBox, CastShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const override; + virtual void FindCollidingPairs(BodyID *ioActiveBodies, int inNumActiveBodies, float inSpeculativeContactDistance, const ObjectVsBroadPhaseLayerFilter &inObjectVsBroadPhaseLayerFilter, const ObjectLayerPairFilter &inObjectLayerPairFilter, BodyPairCollector &ioPairCollector) const override; + virtual AABox GetBounds() const override; +#ifdef JPH_TRACK_BROADPHASE_STATS + virtual void ReportStats() override; +#endif // JPH_TRACK_BROADPHASE_STATS + +private: + /// Helper struct for AddBodies handle + struct LayerState + { + JPH_OVERRIDE_NEW_DELETE + + BodyID * mBodyStart = nullptr; + BodyID * mBodyEnd; + QuadTree::AddState mAddState; + }; + + using Tracking = QuadTree::Tracking; + using TrackingVector = QuadTree::TrackingVector; + +#ifdef JPH_ENABLE_ASSERTS + /// Context used to lock a physics lock + PhysicsLockContext mLockContext = nullptr; +#endif // JPH_ENABLE_ASSERTS + + /// Max amount of bodies we support + size_t mMaxBodies = 0; + + /// Array that for each BodyID keeps track of where it is located in which tree + TrackingVector mTracking; + + /// Node allocator for all trees + QuadTree::Allocator mAllocator; + + /// Information about broad phase layers + const BroadPhaseLayerInterface *mBroadPhaseLayerInterface = nullptr; + + /// One tree per object layer + QuadTree * mLayers; + uint mNumLayers; + + /// UpdateState implementation for this tree used during UpdatePrepare/Finalize() + struct UpdateStateImpl + { + QuadTree * mTree; + QuadTree::UpdateState mUpdateState; + }; + + static_assert(sizeof(UpdateStateImpl) <= sizeof(UpdateState)); + static_assert(alignof(UpdateStateImpl) <= alignof(UpdateState)); + + /// Mutex that prevents object modification during UpdatePrepare/Finalize() + SharedMutex mUpdateMutex; + + /// We double buffer all trees so that we can query while building the next one and we destroy the old tree the next physics update. + /// This structure ensures that we wait for queries that are still using the old tree. + mutable SharedMutex mQueryLocks[2]; + + /// This index indicates which lock is currently active, it alternates between 0 and 1 + atomic mQueryLockIdx { 0 }; + + /// This is the next tree to update in UpdatePrepare() + uint32 mNextLayerToUpdate = 0; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhaseQuery.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhaseQuery.h new file mode 100644 index 00000000000..10085e66fe8 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhaseQuery.h @@ -0,0 +1,53 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +struct RayCast; +class BroadPhaseCastResult; +class AABox; +class OrientedBox; +struct AABoxCast; + +// Various collector configurations +using RayCastBodyCollector = CollisionCollector; +using CastShapeBodyCollector = CollisionCollector; +using CollideShapeBodyCollector = CollisionCollector; + +/// Interface to the broadphase that can perform collision queries. These queries will only test the bounding box of the body to quickly determine a potential set of colliding bodies. +/// The shapes of the bodies are not tested, if you want this then you should use the NarrowPhaseQuery interface. +class JPH_EXPORT BroadPhaseQuery : public NonCopyable +{ +public: + /// Virtual destructor + virtual ~BroadPhaseQuery() = default; + + /// Cast a ray and add any hits to ioCollector + virtual void CastRay(const RayCast &inRay, RayCastBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter = { }, const ObjectLayerFilter &inObjectLayerFilter = { }) const = 0; + + /// Get bodies intersecting with inBox and any hits to ioCollector + virtual void CollideAABox(const AABox &inBox, CollideShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter = { }, const ObjectLayerFilter &inObjectLayerFilter = { }) const = 0; + + /// Get bodies intersecting with a sphere and any hits to ioCollector + virtual void CollideSphere(Vec3Arg inCenter, float inRadius, CollideShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter = { }, const ObjectLayerFilter &inObjectLayerFilter = { }) const = 0; + + /// Get bodies intersecting with a point and any hits to ioCollector + virtual void CollidePoint(Vec3Arg inPoint, CollideShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter = { }, const ObjectLayerFilter &inObjectLayerFilter = { }) const = 0; + + /// Get bodies intersecting with an oriented box and any hits to ioCollector + virtual void CollideOrientedBox(const OrientedBox &inBox, CollideShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter = { }, const ObjectLayerFilter &inObjectLayerFilter = { }) const = 0; + + /// Cast a box and add any hits to ioCollector + virtual void CastAABox(const AABoxCast &inBox, CastShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter = { }, const ObjectLayerFilter &inObjectLayerFilter = { }) const = 0; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/ObjectVsBroadPhaseLayerFilterMask.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/ObjectVsBroadPhaseLayerFilterMask.h new file mode 100644 index 00000000000..20305a5f0ca --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/ObjectVsBroadPhaseLayerFilterMask.h @@ -0,0 +1,35 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2023 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +/// Class that determines if an object layer can collide with a broadphase layer. +/// This implementation works together with BroadPhaseLayerInterfaceMask and ObjectLayerPairFilterMask +class ObjectVsBroadPhaseLayerFilterMask : public ObjectVsBroadPhaseLayerFilter +{ +public: + JPH_OVERRIDE_NEW_DELETE + +/// Constructor + ObjectVsBroadPhaseLayerFilterMask(const BroadPhaseLayerInterfaceMask &inBroadPhaseLayerInterface) : + mBroadPhaseLayerInterface(inBroadPhaseLayerInterface) + { + } + + /// Returns true if an object layer should collide with a broadphase layer + virtual bool ShouldCollide(ObjectLayer inLayer1, BroadPhaseLayer inLayer2) const override + { + // Just defer to BroadPhaseLayerInterface + return mBroadPhaseLayerInterface.ShouldCollide(inLayer1, inLayer2); + } + +private: + const BroadPhaseLayerInterfaceMask &mBroadPhaseLayerInterface; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/ObjectVsBroadPhaseLayerFilterTable.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/ObjectVsBroadPhaseLayerFilterTable.h new file mode 100644 index 00000000000..532ce6da0ed --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/ObjectVsBroadPhaseLayerFilterTable.h @@ -0,0 +1,66 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2023 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +/// Class that determines if an object layer can collide with a broadphase layer. +/// This implementation uses a table and constructs itself from an ObjectLayerPairFilter and a BroadPhaseLayerInterface. +class ObjectVsBroadPhaseLayerFilterTable : public ObjectVsBroadPhaseLayerFilter +{ +private: + /// Get which bit corresponds to the pair (inLayer1, inLayer2) + uint GetBit(ObjectLayer inLayer1, BroadPhaseLayer inLayer2) const + { + // Calculate at which bit the entry for this pair resides + return inLayer1 * mNumBroadPhaseLayers + (BroadPhaseLayer::Type)inLayer2; + } + +public: + JPH_OVERRIDE_NEW_DELETE + + /// Construct the table + /// @param inBroadPhaseLayerInterface The broad phase layer interface that maps object layers to broad phase layers + /// @param inNumBroadPhaseLayers Number of broad phase layers + /// @param inObjectLayerPairFilter The object layer pair filter that determines which object layers can collide + /// @param inNumObjectLayers Number of object layers + ObjectVsBroadPhaseLayerFilterTable(const BroadPhaseLayerInterface &inBroadPhaseLayerInterface, uint inNumBroadPhaseLayers, const ObjectLayerPairFilter &inObjectLayerPairFilter, uint inNumObjectLayers) : + mNumBroadPhaseLayers(inNumBroadPhaseLayers) + { + // Resize table and set all entries to false + mTable.resize((inNumBroadPhaseLayers * inNumObjectLayers + 7) / 8, 0); + + // Loop over all object layer pairs + for (ObjectLayer o1 = 0; o1 < inNumObjectLayers; ++o1) + for (ObjectLayer o2 = 0; o2 < inNumObjectLayers; ++o2) + { + // Get the broad phase layer for the second object layer + BroadPhaseLayer b2 = inBroadPhaseLayerInterface.GetBroadPhaseLayer(o2); + JPH_ASSERT((BroadPhaseLayer::Type)b2 < inNumBroadPhaseLayers); + + // If the object layers collide then so should the object and broadphase layer + if (inObjectLayerPairFilter.ShouldCollide(o1, o2)) + { + uint bit = GetBit(o1, b2); + mTable[bit >> 3] |= 1 << (bit & 0b111); + } + } + } + + /// Returns true if an object layer should collide with a broadphase layer + virtual bool ShouldCollide(ObjectLayer inLayer1, BroadPhaseLayer inLayer2) const override + { + uint bit = GetBit(inLayer1, inLayer2); + return (mTable[bit >> 3] & (1 << (bit & 0b111))) != 0; + } + +private: + uint mNumBroadPhaseLayers; ///< The total number of broadphase layers + Array mTable; ///< The table of bits that indicates which layers collide +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/QuadTree.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/QuadTree.cpp new file mode 100644 index 00000000000..4c1e740c7f2 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/QuadTree.cpp @@ -0,0 +1,1684 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef JPH_DUMP_BROADPHASE_TREE +JPH_SUPPRESS_WARNINGS_STD_BEGIN +#include +JPH_SUPPRESS_WARNINGS_STD_END +#endif // JPH_DUMP_BROADPHASE_TREE + +JPH_NAMESPACE_BEGIN + +//////////////////////////////////////////////////////////////////////////////////////////////////////// +// QuadTree::Node +//////////////////////////////////////////////////////////////////////////////////////////////////////// + +QuadTree::Node::Node(bool inIsChanged) : + mIsChanged(inIsChanged) +{ + // First reset bounds + Vec4 val = Vec4::sReplicate(cLargeFloat); + val.StoreFloat4((Float4 *)&mBoundsMinX); + val.StoreFloat4((Float4 *)&mBoundsMinY); + val.StoreFloat4((Float4 *)&mBoundsMinZ); + val = Vec4::sReplicate(-cLargeFloat); + val.StoreFloat4((Float4 *)&mBoundsMaxX); + val.StoreFloat4((Float4 *)&mBoundsMaxY); + val.StoreFloat4((Float4 *)&mBoundsMaxZ); + + // Reset child node ids + mChildNodeID[0] = NodeID::sInvalid(); + mChildNodeID[1] = NodeID::sInvalid(); + mChildNodeID[2] = NodeID::sInvalid(); + mChildNodeID[3] = NodeID::sInvalid(); +} + +void QuadTree::Node::GetChildBounds(int inChildIndex, AABox &outBounds) const +{ + // Read bounding box in order min -> max + outBounds.mMin = Vec3(mBoundsMinX[inChildIndex], mBoundsMinY[inChildIndex], mBoundsMinZ[inChildIndex]); + outBounds.mMax = Vec3(mBoundsMaxX[inChildIndex], mBoundsMaxY[inChildIndex], mBoundsMaxZ[inChildIndex]); +} + +void QuadTree::Node::SetChildBounds(int inChildIndex, const AABox &inBounds) +{ + // Set max first (this keeps the bounding box invalid for reading threads) + mBoundsMaxZ[inChildIndex] = inBounds.mMax.GetZ(); + mBoundsMaxY[inChildIndex] = inBounds.mMax.GetY(); + mBoundsMaxX[inChildIndex] = inBounds.mMax.GetX(); + + // Then set min (and make box valid) + mBoundsMinZ[inChildIndex] = inBounds.mMin.GetZ(); + mBoundsMinY[inChildIndex] = inBounds.mMin.GetY(); + mBoundsMinX[inChildIndex] = inBounds.mMin.GetX(); // Min X becomes valid last +} + +void QuadTree::Node::InvalidateChildBounds(int inChildIndex) +{ + // First we make the box invalid by setting the min to cLargeFloat + mBoundsMinX[inChildIndex] = cLargeFloat; // Min X becomes invalid first + mBoundsMinY[inChildIndex] = cLargeFloat; + mBoundsMinZ[inChildIndex] = cLargeFloat; + + // Then we reset the max values too + mBoundsMaxX[inChildIndex] = -cLargeFloat; + mBoundsMaxY[inChildIndex] = -cLargeFloat; + mBoundsMaxZ[inChildIndex] = -cLargeFloat; +} + +void QuadTree::Node::GetNodeBounds(AABox &outBounds) const +{ + // Get first child bounds + GetChildBounds(0, outBounds); + + // Encapsulate other child bounds + for (int child_idx = 1; child_idx < 4; ++child_idx) + { + AABox tmp; + GetChildBounds(child_idx, tmp); + outBounds.Encapsulate(tmp); + } +} + +bool QuadTree::Node::EncapsulateChildBounds(int inChildIndex, const AABox &inBounds) +{ + bool changed = AtomicMin(mBoundsMinX[inChildIndex], inBounds.mMin.GetX()); + changed |= AtomicMin(mBoundsMinY[inChildIndex], inBounds.mMin.GetY()); + changed |= AtomicMin(mBoundsMinZ[inChildIndex], inBounds.mMin.GetZ()); + changed |= AtomicMax(mBoundsMaxX[inChildIndex], inBounds.mMax.GetX()); + changed |= AtomicMax(mBoundsMaxY[inChildIndex], inBounds.mMax.GetY()); + changed |= AtomicMax(mBoundsMaxZ[inChildIndex], inBounds.mMax.GetZ()); + return changed; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////// +// QuadTree +//////////////////////////////////////////////////////////////////////////////////////////////////////// + +const float QuadTree::cLargeFloat = 1.0e30f; +const AABox QuadTree::cInvalidBounds(Vec3::sReplicate(cLargeFloat), Vec3::sReplicate(-cLargeFloat)); + +void QuadTree::GetBodyLocation(const TrackingVector &inTracking, BodyID inBodyID, uint32 &outNodeIdx, uint32 &outChildIdx) const +{ + uint32 body_location = inTracking[inBodyID.GetIndex()].mBodyLocation; + JPH_ASSERT(body_location != Tracking::cInvalidBodyLocation); + outNodeIdx = body_location & 0x3fffffff; + outChildIdx = body_location >> 30; + JPH_ASSERT(mAllocator->Get(outNodeIdx).mChildNodeID[outChildIdx] == inBodyID, "Make sure that the body is in the node where it should be"); +} + +void QuadTree::SetBodyLocation(TrackingVector &ioTracking, BodyID inBodyID, uint32 inNodeIdx, uint32 inChildIdx) const +{ + JPH_ASSERT(inNodeIdx <= 0x3fffffff); + JPH_ASSERT(inChildIdx < 4); + JPH_ASSERT(mAllocator->Get(inNodeIdx).mChildNodeID[inChildIdx] == inBodyID, "Make sure that the body is in the node where it should be"); + ioTracking[inBodyID.GetIndex()].mBodyLocation = inNodeIdx + (inChildIdx << 30); + +#ifdef JPH_ENABLE_ASSERTS + uint32 v1, v2; + GetBodyLocation(ioTracking, inBodyID, v1, v2); + JPH_ASSERT(v1 == inNodeIdx); + JPH_ASSERT(v2 == inChildIdx); +#endif +} + +void QuadTree::sInvalidateBodyLocation(TrackingVector &ioTracking, BodyID inBodyID) +{ + ioTracking[inBodyID.GetIndex()].mBodyLocation = Tracking::cInvalidBodyLocation; +} + +QuadTree::~QuadTree() +{ + // Get rid of any nodes that are still to be freed + DiscardOldTree(); + + // Get the current root node + const RootNode &root_node = GetCurrentRoot(); + + // Collect all bodies + Allocator::Batch free_batch; + NodeID node_stack[cStackSize]; + node_stack[0] = root_node.GetNodeID(); + JPH_ASSERT(node_stack[0].IsValid()); + if (node_stack[0].IsNode()) + { + int top = 0; + do + { + // Process node + NodeID node_id = node_stack[top]; + JPH_ASSERT(!node_id.IsBody()); + uint32 node_idx = node_id.GetNodeIndex(); + const Node &node = mAllocator->Get(node_idx); + + // Recurse and get all child nodes + for (NodeID child_node_id : node.mChildNodeID) + if (child_node_id.IsValid() && child_node_id.IsNode()) + { + JPH_ASSERT(top < cStackSize); + node_stack[top] = child_node_id; + top++; + } + + // Mark node to be freed + mAllocator->AddObjectToBatch(free_batch, node_idx); + --top; + } + while (top >= 0); + } + + // Now free all nodes + mAllocator->DestructObjectBatch(free_batch); +} + +uint32 QuadTree::AllocateNode(bool inIsChanged) +{ + uint32 index = mAllocator->ConstructObject(inIsChanged); + if (index == Allocator::cInvalidObjectIndex) + { + Trace("QuadTree: Out of nodes!"); + std::abort(); + } + return index; +} + +void QuadTree::Init(Allocator &inAllocator) +{ + // Store allocator + mAllocator = &inAllocator; + + // Allocate root node + mRootNode[mRootNodeIndex].mIndex = AllocateNode(false); +} + +void QuadTree::DiscardOldTree() +{ + // Check if there is an old tree + RootNode &old_root_node = mRootNode[mRootNodeIndex ^ 1]; + if (old_root_node.mIndex != cInvalidNodeIndex) + { + // Clear the root + old_root_node.mIndex = cInvalidNodeIndex; + + // Now free all old nodes + mAllocator->DestructObjectBatch(mFreeNodeBatch); + + // Clear the batch + mFreeNodeBatch = Allocator::Batch(); + } +} + +AABox QuadTree::GetBounds() const +{ + uint32 node_idx = GetCurrentRoot().mIndex; + JPH_ASSERT(node_idx != cInvalidNodeIndex); + const Node &node = mAllocator->Get(node_idx); + + AABox bounds; + node.GetNodeBounds(bounds); + return bounds; +} + +void QuadTree::UpdatePrepare(const BodyVector &inBodies, TrackingVector &ioTracking, UpdateState &outUpdateState, bool inFullRebuild) +{ +#ifdef JPH_ENABLE_ASSERTS + // We only read positions + BodyAccess::Grant grant(BodyAccess::EAccess::None, BodyAccess::EAccess::Read); +#endif + + // Assert we have no nodes pending deletion, this means DiscardOldTree wasn't called yet + JPH_ASSERT(mFreeNodeBatch.mNumObjects == 0); + + // Mark tree non-dirty + mIsDirty = false; + + // Get the current root node + const RootNode &root_node = GetCurrentRoot(); + +#ifdef JPH_DUMP_BROADPHASE_TREE + DumpTree(root_node.GetNodeID(), StringFormat("%s_PRE", mName).c_str()); +#endif + + // Assert sane data +#ifdef _DEBUG + ValidateTree(inBodies, ioTracking, root_node.mIndex, mNumBodies); +#endif + + // Create space for all body ID's + NodeID *node_ids = new NodeID [mNumBodies]; + NodeID *cur_node_id = node_ids; + + // Collect all bodies + NodeID node_stack[cStackSize]; + node_stack[0] = root_node.GetNodeID(); + JPH_ASSERT(node_stack[0].IsValid()); + int top = 0; + do + { + // Check if node is a body + NodeID node_id = node_stack[top]; + if (node_id.IsBody()) + { + // Validate that we're still in the right layer + #ifdef JPH_ENABLE_ASSERTS + uint32 body_index = node_id.GetBodyID().GetIndex(); + JPH_ASSERT(ioTracking[body_index].mObjectLayer == inBodies[body_index]->GetObjectLayer()); + #endif + + // Store body + *cur_node_id = node_id; + ++cur_node_id; + } + else + { + // Process normal node + uint32 node_idx = node_id.GetNodeIndex(); + const Node &node = mAllocator->Get(node_idx); + + if (!node.mIsChanged && !inFullRebuild) + { + // Node is unchanged, treat it as a whole + *cur_node_id = node_id; + ++cur_node_id; + } + else + { + // Node is changed, recurse and get all children + for (NodeID child_node_id : node.mChildNodeID) + if (child_node_id.IsValid()) + { + if (top < cStackSize) + { + node_stack[top] = child_node_id; + top++; + } + else + { + JPH_ASSERT(false); // Out of stack space, this must be a very deep tree. Are you batch adding bodies to the broadphase? + + // Falling back to adding the node as a whole + *cur_node_id = child_node_id; + ++cur_node_id; + } + } + + // Mark node to be freed + mAllocator->AddObjectToBatch(mFreeNodeBatch, node_idx); + } + } + --top; + } + while (top >= 0); + + // Check that our book keeping matches + uint32 num_node_ids = uint32(cur_node_id - node_ids); + JPH_ASSERT(inFullRebuild? num_node_ids == mNumBodies : num_node_ids <= mNumBodies); + + // This will be the new root node id + NodeID root_node_id; + + if (num_node_ids > 0) + { + // We mark the first 5 levels (max 1024 nodes) of the newly built tree as 'changed' so that + // those nodes get recreated every time when we rebuild the tree. This balances the amount of + // time we spend on rebuilding the tree ('unchanged' nodes will be put in the new tree as a whole) + // vs the quality of the built tree. + constexpr uint cMaxDepthMarkChanged = 5; + + // Build new tree + AABox root_bounds; + root_node_id = BuildTree(inBodies, ioTracking, node_ids, num_node_ids, cMaxDepthMarkChanged, root_bounds); + + if (root_node_id.IsBody()) + { + // For a single body we need to allocate a new root node + uint32 root_idx = AllocateNode(false); + Node &root = mAllocator->Get(root_idx); + root.SetChildBounds(0, root_bounds); + root.mChildNodeID[0] = root_node_id; + SetBodyLocation(ioTracking, root_node_id.GetBodyID(), root_idx, 0); + root_node_id = NodeID::sFromNodeIndex(root_idx); + } + } + else + { + // Empty tree, create root node + uint32 root_idx = AllocateNode(false); + root_node_id = NodeID::sFromNodeIndex(root_idx); + } + + // Delete temporary data + delete [] node_ids; + + outUpdateState.mRootNodeID = root_node_id; +} + +void QuadTree::UpdateFinalize([[maybe_unused]] const BodyVector &inBodies, [[maybe_unused]] const TrackingVector &inTracking, const UpdateState &inUpdateState) +{ + // Tree building is complete, now we switch the old with the new tree + uint32 new_root_idx = mRootNodeIndex ^ 1; + RootNode &new_root_node = mRootNode[new_root_idx]; + { + // Note: We don't need to lock here as the old tree stays available so any queries + // that use it can continue using it until DiscardOldTree is called. This slot + // should be empty and unused at this moment. + JPH_ASSERT(new_root_node.mIndex == cInvalidNodeIndex); + new_root_node.mIndex = inUpdateState.mRootNodeID.GetNodeIndex(); + } + + // All queries that start from now on will use this new tree + mRootNodeIndex = new_root_idx; + +#ifdef JPH_DUMP_BROADPHASE_TREE + DumpTree(new_root_node.GetNodeID(), StringFormat("%s_POST", mName).c_str()); +#endif + +#ifdef _DEBUG + ValidateTree(inBodies, inTracking, new_root_node.mIndex, mNumBodies); +#endif +} + +void QuadTree::sPartition(NodeID *ioNodeIDs, Vec3 *ioNodeCenters, int inNumber, int &outMidPoint) +{ + // Handle trivial case + if (inNumber <= 4) + { + outMidPoint = inNumber / 2; + return; + } + + // Calculate bounding box of box centers + Vec3 center_min = Vec3::sReplicate(cLargeFloat); + Vec3 center_max = Vec3::sReplicate(-cLargeFloat); + for (const Vec3 *c = ioNodeCenters, *c_end = ioNodeCenters + inNumber; c < c_end; ++c) + { + Vec3 center = *c; + center_min = Vec3::sMin(center_min, center); + center_max = Vec3::sMax(center_max, center); + } + + // Calculate split plane + int dimension = (center_max - center_min).GetHighestComponentIndex(); + float split = 0.5f * (center_min + center_max)[dimension]; + + // Divide bodies + int start = 0, end = inNumber; + while (start < end) + { + // Search for first element that is on the right hand side of the split plane + while (start < end && ioNodeCenters[start][dimension] < split) + ++start; + + // Search for the first element that is on the left hand side of the split plane + while (start < end && ioNodeCenters[end - 1][dimension] >= split) + --end; + + if (start < end) + { + // Swap the two elements + swap(ioNodeIDs[start], ioNodeIDs[end - 1]); + swap(ioNodeCenters[start], ioNodeCenters[end - 1]); + ++start; + --end; + } + } + JPH_ASSERT(start == end); + + if (start > 0 && start < inNumber) + { + // Success! + outMidPoint = start; + } + else + { + // Failed to divide bodies + outMidPoint = inNumber / 2; + } +} + +void QuadTree::sPartition4(NodeID *ioNodeIDs, Vec3 *ioNodeCenters, int inBegin, int inEnd, int *outSplit) +{ + NodeID *node_ids = ioNodeIDs + inBegin; + Vec3 *node_centers = ioNodeCenters + inBegin; + int number = inEnd - inBegin; + + // Partition entire range + sPartition(node_ids, node_centers, number, outSplit[2]); + + // Partition lower half + sPartition(node_ids, node_centers, outSplit[2], outSplit[1]); + + // Partition upper half + sPartition(node_ids + outSplit[2], node_centers + outSplit[2], number - outSplit[2], outSplit[3]); + + // Convert to proper range + outSplit[0] = inBegin; + outSplit[1] += inBegin; + outSplit[2] += inBegin; + outSplit[3] += outSplit[2]; + outSplit[4] = inEnd; +} + +AABox QuadTree::GetNodeOrBodyBounds(const BodyVector &inBodies, NodeID inNodeID) const +{ + if (inNodeID.IsNode()) + { + // It is a node + uint32 node_idx = inNodeID.GetNodeIndex(); + const Node &node = mAllocator->Get(node_idx); + + AABox bounds; + node.GetNodeBounds(bounds); + return bounds; + } + else + { + // It is a body + return inBodies[inNodeID.GetBodyID().GetIndex()]->GetWorldSpaceBounds(); + } +} + +QuadTree::NodeID QuadTree::BuildTree(const BodyVector &inBodies, TrackingVector &ioTracking, NodeID *ioNodeIDs, int inNumber, uint inMaxDepthMarkChanged, AABox &outBounds) +{ + // Trivial case: No bodies in tree + if (inNumber == 0) + { + outBounds = cInvalidBounds; + return NodeID::sInvalid(); + } + + // Trivial case: When we have 1 body or node, return it + if (inNumber == 1) + { + if (ioNodeIDs->IsNode()) + { + // When returning an existing node as root, ensure that no parent has been set + Node &node = mAllocator->Get(ioNodeIDs->GetNodeIndex()); + node.mParentNodeIndex = cInvalidNodeIndex; + } + outBounds = GetNodeOrBodyBounds(inBodies, *ioNodeIDs); + return *ioNodeIDs; + } + + // Calculate centers of all bodies that are to be inserted + Vec3 *centers = new Vec3 [inNumber]; + JPH_ASSERT(IsAligned(centers, JPH_VECTOR_ALIGNMENT)); + Vec3 *c = centers; + for (const NodeID *n = ioNodeIDs, *n_end = ioNodeIDs + inNumber; n < n_end; ++n, ++c) + *c = GetNodeOrBodyBounds(inBodies, *n).GetCenter(); + + // The algorithm is a recursive tree build, but to avoid the call overhead we keep track of a stack here + struct StackEntry + { + uint32 mNodeIdx; // Node index of node that is generated + int mChildIdx; // Index of child that we're currently processing + int mSplit[5]; // Indices where the node ID's have been split to form 4 partitions + uint32 mDepth; // Depth of this node in the tree + Vec3 mNodeBoundsMin; // Bounding box of this node, accumulated while iterating over children + Vec3 mNodeBoundsMax; + }; + static_assert(sizeof(StackEntry) == 64); + StackEntry stack[cStackSize / 4]; // We don't process 4 at a time in this loop but 1, so the stack can be 4x as small + int top = 0; + + // Create root node + stack[0].mNodeIdx = AllocateNode(inMaxDepthMarkChanged > 0); + stack[0].mChildIdx = -1; + stack[0].mDepth = 0; + stack[0].mNodeBoundsMin = Vec3::sReplicate(cLargeFloat); + stack[0].mNodeBoundsMax = Vec3::sReplicate(-cLargeFloat); + sPartition4(ioNodeIDs, centers, 0, inNumber, stack[0].mSplit); + + for (;;) + { + StackEntry &cur_stack = stack[top]; + + // Next child + cur_stack.mChildIdx++; + + // Check if all children processed + if (cur_stack.mChildIdx >= 4) + { + // Terminate if there's nothing left to pop + if (top <= 0) + break; + + // Add our bounds to our parents bounds + StackEntry &prev_stack = stack[top - 1]; + prev_stack.mNodeBoundsMin = Vec3::sMin(prev_stack.mNodeBoundsMin, cur_stack.mNodeBoundsMin); + prev_stack.mNodeBoundsMax = Vec3::sMax(prev_stack.mNodeBoundsMax, cur_stack.mNodeBoundsMax); + + // Store parent node + Node &node = mAllocator->Get(cur_stack.mNodeIdx); + node.mParentNodeIndex = prev_stack.mNodeIdx; + + // Store this node's properties in the parent node + Node &parent_node = mAllocator->Get(prev_stack.mNodeIdx); + parent_node.mChildNodeID[prev_stack.mChildIdx] = NodeID::sFromNodeIndex(cur_stack.mNodeIdx); + parent_node.SetChildBounds(prev_stack.mChildIdx, AABox(cur_stack.mNodeBoundsMin, cur_stack.mNodeBoundsMax)); + + // Pop entry from stack + --top; + } + else + { + // Get low and high index to bodies to process + int low = cur_stack.mSplit[cur_stack.mChildIdx]; + int high = cur_stack.mSplit[cur_stack.mChildIdx + 1]; + int num_bodies = high - low; + + if (num_bodies == 1) + { + // Get body info + NodeID child_node_id = ioNodeIDs[low]; + AABox bounds = GetNodeOrBodyBounds(inBodies, child_node_id); + + // Update node + Node &node = mAllocator->Get(cur_stack.mNodeIdx); + node.mChildNodeID[cur_stack.mChildIdx] = child_node_id; + node.SetChildBounds(cur_stack.mChildIdx, bounds); + + if (child_node_id.IsNode()) + { + // Update parent for this node + Node &child_node = mAllocator->Get(child_node_id.GetNodeIndex()); + child_node.mParentNodeIndex = cur_stack.mNodeIdx; + } + else + { + // Set location in tracking + SetBodyLocation(ioTracking, child_node_id.GetBodyID(), cur_stack.mNodeIdx, cur_stack.mChildIdx); + } + + // Encapsulate bounding box in parent + cur_stack.mNodeBoundsMin = Vec3::sMin(cur_stack.mNodeBoundsMin, bounds.mMin); + cur_stack.mNodeBoundsMax = Vec3::sMax(cur_stack.mNodeBoundsMax, bounds.mMax); + } + else if (num_bodies > 1) + { + // Allocate new node + StackEntry &new_stack = stack[++top]; + JPH_ASSERT(top < cStackSize / 4); + uint32 next_depth = cur_stack.mDepth + 1; + new_stack.mNodeIdx = AllocateNode(inMaxDepthMarkChanged > next_depth); + new_stack.mChildIdx = -1; + new_stack.mDepth = next_depth; + new_stack.mNodeBoundsMin = Vec3::sReplicate(cLargeFloat); + new_stack.mNodeBoundsMax = Vec3::sReplicate(-cLargeFloat); + sPartition4(ioNodeIDs, centers, low, high, new_stack.mSplit); + } + } + } + + // Delete temporary data + delete [] centers; + + // Store bounding box of root + outBounds.mMin = stack[0].mNodeBoundsMin; + outBounds.mMax = stack[0].mNodeBoundsMax; + + // Return root + return NodeID::sFromNodeIndex(stack[0].mNodeIdx); +} + +void QuadTree::MarkNodeAndParentsChanged(uint32 inNodeIndex) +{ + uint32 node_idx = inNodeIndex; + + do + { + // If node has changed, parent will be too + Node &node = mAllocator->Get(node_idx); + if (node.mIsChanged) + break; + + // Mark node as changed + node.mIsChanged = true; + + // Get our parent + node_idx = node.mParentNodeIndex; + } + while (node_idx != cInvalidNodeIndex); +} + +void QuadTree::WidenAndMarkNodeAndParentsChanged(uint32 inNodeIndex, const AABox &inNewBounds) +{ + uint32 node_idx = inNodeIndex; + + for (;;) + { + // Mark node as changed + Node &node = mAllocator->Get(node_idx); + node.mIsChanged = true; + + // Get our parent + uint32 parent_idx = node.mParentNodeIndex; + if (parent_idx == cInvalidNodeIndex) + break; + + // Find which child of the parent we're in + Node &parent_node = mAllocator->Get(parent_idx); + NodeID node_id = NodeID::sFromNodeIndex(node_idx); + int child_idx = -1; + for (int i = 0; i < 4; ++i) + if (parent_node.mChildNodeID[i] == node_id) + { + // Found one, set the node index and child index and update the bounding box too + child_idx = i; + break; + } + JPH_ASSERT(child_idx != -1, "Nodes don't get removed from the tree, we must have found it"); + + // To avoid any race conditions with other threads we only enlarge bounding boxes + if (!parent_node.EncapsulateChildBounds(child_idx, inNewBounds)) + { + // No changes to bounding box, only marking as changed remains to be done + if (!parent_node.mIsChanged) + MarkNodeAndParentsChanged(parent_idx); + break; + } + + // Update node index + node_idx = parent_idx; + } +} + +bool QuadTree::TryInsertLeaf(TrackingVector &ioTracking, int inNodeIndex, NodeID inLeafID, const AABox &inLeafBounds, int inLeafNumBodies) +{ + // Tentively assign the node as parent + bool leaf_is_node = inLeafID.IsNode(); + if (leaf_is_node) + { + uint32 leaf_idx = inLeafID.GetNodeIndex(); + mAllocator->Get(leaf_idx).mParentNodeIndex = inNodeIndex; + } + + // Fetch node that we're adding to + Node &node = mAllocator->Get(inNodeIndex); + + // Find an empty child + for (uint32 child_idx = 0; child_idx < 4; ++child_idx) + if (node.mChildNodeID[child_idx].CompareExchange(NodeID::sInvalid(), inLeafID)) // Check if we can claim it + { + // We managed to add it to the node + + // If leaf was a body, we need to update its bookkeeping + if (!leaf_is_node) + SetBodyLocation(ioTracking, inLeafID.GetBodyID(), inNodeIndex, child_idx); + + // Now set the bounding box making the child valid for queries + node.SetChildBounds(child_idx, inLeafBounds); + + // Widen the bounds for our parents too + WidenAndMarkNodeAndParentsChanged(inNodeIndex, inLeafBounds); + + // Update body counter + mNumBodies += inLeafNumBodies; + + // And we're done + return true; + } + + return false; +} + +bool QuadTree::TryCreateNewRoot(TrackingVector &ioTracking, atomic &ioRootNodeIndex, NodeID inLeafID, const AABox &inLeafBounds, int inLeafNumBodies) +{ + // Fetch old root + uint32 root_idx = ioRootNodeIndex; + Node &root = mAllocator->Get(root_idx); + + // Create new root, mark this new root as changed as we're not creating a very efficient tree at this point + uint32 new_root_idx = AllocateNode(true); + Node &new_root = mAllocator->Get(new_root_idx); + + // First child is current root, note that since the tree may be modified concurrently we cannot assume that the bounds of our child will be correct so we set a very large bounding box + new_root.mChildNodeID[0] = NodeID::sFromNodeIndex(root_idx); + new_root.SetChildBounds(0, AABox(Vec3::sReplicate(-cLargeFloat), Vec3::sReplicate(cLargeFloat))); + + // Second child is new leaf + new_root.mChildNodeID[1] = inLeafID; + new_root.SetChildBounds(1, inLeafBounds); + + // Tentatively assign new root as parent + bool leaf_is_node = inLeafID.IsNode(); + if (leaf_is_node) + { + uint32 leaf_idx = inLeafID.GetNodeIndex(); + mAllocator->Get(leaf_idx).mParentNodeIndex = new_root_idx; + } + + // Try to swap it + if (ioRootNodeIndex.compare_exchange_strong(root_idx, new_root_idx)) + { + // We managed to set the new root + + // If leaf was a body, we need to update its bookkeeping + if (!leaf_is_node) + SetBodyLocation(ioTracking, inLeafID.GetBodyID(), new_root_idx, 1); + + // Store parent node for old root + root.mParentNodeIndex = new_root_idx; + + // Update body counter + mNumBodies += inLeafNumBodies; + + // And we're done + return true; + } + + // Failed to swap, someone else must have created a new root, try again + mAllocator->DestructObject(new_root_idx); + return false; +} + +void QuadTree::AddBodiesPrepare(const BodyVector &inBodies, TrackingVector &ioTracking, BodyID *ioBodyIDs, int inNumber, AddState &outState) +{ + // Assert sane input + JPH_ASSERT(ioBodyIDs != nullptr); + JPH_ASSERT(inNumber > 0); + +#ifdef JPH_ENABLE_ASSERTS + // Below we just cast the body ID's to node ID's, check here that that is valid + for (const BodyID *b = ioBodyIDs, *b_end = ioBodyIDs + inNumber; b < b_end; ++b) + NodeID::sFromBodyID(*b); +#endif + + // Build subtree for the new bodies, note that we mark all nodes as 'not changed' + // so they will stay together as a batch and will make the tree rebuild cheaper + outState.mLeafID = BuildTree(inBodies, ioTracking, (NodeID *)ioBodyIDs, inNumber, 0, outState.mLeafBounds); + +#ifdef _DEBUG + if (outState.mLeafID.IsNode()) + ValidateTree(inBodies, ioTracking, outState.mLeafID.GetNodeIndex(), inNumber); +#endif +} + +void QuadTree::AddBodiesFinalize(TrackingVector &ioTracking, int inNumberBodies, const AddState &inState) +{ + // Assert sane input + JPH_ASSERT(inNumberBodies > 0); + + // Mark tree dirty + mIsDirty = true; + + // Get the current root node + RootNode &root_node = GetCurrentRoot(); + + for (;;) + { + // Check if we can insert the body in the root + if (TryInsertLeaf(ioTracking, root_node.mIndex, inState.mLeafID, inState.mLeafBounds, inNumberBodies)) + return; + + // Check if we can create a new root + if (TryCreateNewRoot(ioTracking, root_node.mIndex, inState.mLeafID, inState.mLeafBounds, inNumberBodies)) + return; + } +} + +void QuadTree::AddBodiesAbort(TrackingVector &ioTracking, const AddState &inState) +{ + // Collect all bodies + Allocator::Batch free_batch; + NodeID node_stack[cStackSize]; + node_stack[0] = inState.mLeafID; + JPH_ASSERT(node_stack[0].IsValid()); + int top = 0; + do + { + // Check if node is a body + NodeID child_node_id = node_stack[top]; + if (child_node_id.IsBody()) + { + // Reset location of body + sInvalidateBodyLocation(ioTracking, child_node_id.GetBodyID()); + } + else + { + // Process normal node + uint32 node_idx = child_node_id.GetNodeIndex(); + const Node &node = mAllocator->Get(node_idx); + for (NodeID sub_child_node_id : node.mChildNodeID) + if (sub_child_node_id.IsValid()) + { + JPH_ASSERT(top < cStackSize); + node_stack[top] = sub_child_node_id; + top++; + } + + // Mark it to be freed + mAllocator->AddObjectToBatch(free_batch, node_idx); + } + --top; + } + while (top >= 0); + + // Now free all nodes as a single batch + mAllocator->DestructObjectBatch(free_batch); +} + +void QuadTree::RemoveBodies([[maybe_unused]] const BodyVector &inBodies, TrackingVector &ioTracking, const BodyID *ioBodyIDs, int inNumber) +{ + // Assert sane input + JPH_ASSERT(ioBodyIDs != nullptr); + JPH_ASSERT(inNumber > 0); + + // Mark tree dirty + mIsDirty = true; + + for (const BodyID *cur = ioBodyIDs, *end = ioBodyIDs + inNumber; cur < end; ++cur) + { + // Check if BodyID is correct + JPH_ASSERT(inBodies[cur->GetIndex()]->GetID() == *cur, "Provided BodyID doesn't match BodyID in body manager"); + + // Get location of body + uint32 node_idx, child_idx; + GetBodyLocation(ioTracking, *cur, node_idx, child_idx); + + // First we reset our internal bookkeeping + sInvalidateBodyLocation(ioTracking, *cur); + + // Then we make the bounding box invalid, no queries can find this node anymore + Node &node = mAllocator->Get(node_idx); + node.InvalidateChildBounds(child_idx); + + // Finally we reset the child id, this makes the node available for adds again + node.mChildNodeID[child_idx] = NodeID::sInvalid(); + + // We don't need to bubble up our bounding box changes to our parents since we never make volumes smaller, only bigger + // But we do need to mark the nodes as changed so that the tree can be rebuilt + MarkNodeAndParentsChanged(node_idx); + } + + mNumBodies -= inNumber; +} + +void QuadTree::NotifyBodiesAABBChanged(const BodyVector &inBodies, const TrackingVector &inTracking, const BodyID *ioBodyIDs, int inNumber) +{ + // Assert sane input + JPH_ASSERT(ioBodyIDs != nullptr); + JPH_ASSERT(inNumber > 0); + + for (const BodyID *cur = ioBodyIDs, *end = ioBodyIDs + inNumber; cur < end; ++cur) + { + // Check if BodyID is correct + const Body *body = inBodies[cur->GetIndex()]; + JPH_ASSERT(body->GetID() == *cur, "Provided BodyID doesn't match BodyID in body manager"); + + // Get the new bounding box + const AABox &new_bounds = body->GetWorldSpaceBounds(); + + // Get location of body + uint32 node_idx, child_idx; + GetBodyLocation(inTracking, *cur, node_idx, child_idx); + + // Widen bounds for node + Node &node = mAllocator->Get(node_idx); + if (node.EncapsulateChildBounds(child_idx, new_bounds)) + { + // Mark tree dirty + mIsDirty = true; + + // If bounds changed, widen the bounds for our parents too + WidenAndMarkNodeAndParentsChanged(node_idx, new_bounds); + } + } +} + +template +JPH_INLINE void QuadTree::WalkTree(const ObjectLayerFilter &inObjectLayerFilter, const TrackingVector &inTracking, Visitor &ioVisitor JPH_IF_TRACK_BROADPHASE_STATS(, LayerToStats &ioStats)) const +{ + // Get the root + const RootNode &root_node = GetCurrentRoot(); + +#ifdef JPH_TRACK_BROADPHASE_STATS + // Start tracking stats + int bodies_visited = 0; + int hits_collected = 0; + int nodes_visited = 0; + uint64 collector_ticks = 0; + + uint64 start = GetProcessorTickCount(); +#endif // JPH_TRACK_BROADPHASE_STATS + + NodeID node_stack[cStackSize]; + node_stack[0] = root_node.GetNodeID(); + int top = 0; + do + { + // Check if node is a body + NodeID child_node_id = node_stack[top]; + if (child_node_id.IsBody()) + { + // Track amount of bodies visited + JPH_IF_TRACK_BROADPHASE_STATS(++bodies_visited;) + + BodyID body_id = child_node_id.GetBodyID(); + ObjectLayer object_layer = inTracking[body_id.GetIndex()].mObjectLayer; // We're not taking a lock on the body, so it may be in the process of being removed so check if the object layer is invalid + if (object_layer != cObjectLayerInvalid && inObjectLayerFilter.ShouldCollide(object_layer)) + { + JPH_PROFILE("VisitBody"); + + // Track amount of hits + JPH_IF_TRACK_BROADPHASE_STATS(++hits_collected;) + + // Start track time the collector takes + JPH_IF_TRACK_BROADPHASE_STATS(uint64 collector_start = GetProcessorTickCount();) + + // We found a body we collide with, call our visitor + ioVisitor.VisitBody(body_id, top); + + // End track time the collector takes + JPH_IF_TRACK_BROADPHASE_STATS(collector_ticks += GetProcessorTickCount() - collector_start;) + + // Check if we're done + if (ioVisitor.ShouldAbort()) + break; + } + } + else if (child_node_id.IsValid()) + { + JPH_IF_TRACK_BROADPHASE_STATS(++nodes_visited;) + + // Check if stack can hold more nodes + if (top + 4 < cStackSize) + { + // Process normal node + const Node &node = mAllocator->Get(child_node_id.GetNodeIndex()); + JPH_ASSERT(IsAligned(&node, JPH_CACHE_LINE_SIZE)); + + // Load bounds of 4 children + Vec4 bounds_minx = Vec4::sLoadFloat4Aligned((const Float4 *)&node.mBoundsMinX); + Vec4 bounds_miny = Vec4::sLoadFloat4Aligned((const Float4 *)&node.mBoundsMinY); + Vec4 bounds_minz = Vec4::sLoadFloat4Aligned((const Float4 *)&node.mBoundsMinZ); + Vec4 bounds_maxx = Vec4::sLoadFloat4Aligned((const Float4 *)&node.mBoundsMaxX); + Vec4 bounds_maxy = Vec4::sLoadFloat4Aligned((const Float4 *)&node.mBoundsMaxY); + Vec4 bounds_maxz = Vec4::sLoadFloat4Aligned((const Float4 *)&node.mBoundsMaxZ); + + // Load ids for 4 children + UVec4 child_ids = UVec4::sLoadInt4Aligned((const uint32 *)&node.mChildNodeID[0]); + + // Check which sub nodes to visit + int num_results = ioVisitor.VisitNodes(bounds_minx, bounds_miny, bounds_minz, bounds_maxx, bounds_maxy, bounds_maxz, child_ids, top); + child_ids.StoreInt4((uint32 *)&node_stack[top]); + top += num_results; + } + else + JPH_ASSERT(false, "Stack full!"); + } + + // Fetch next node until we find one that the visitor wants to see + do + --top; + while (top >= 0 && !ioVisitor.ShouldVisitNode(top)); + } + while (top >= 0); + +#ifdef JPH_TRACK_BROADPHASE_STATS + // Calculate total time the broadphase walk took + uint64 total_ticks = GetProcessorTickCount() - start; + + // Update stats under lock protection (slow!) + { + unique_lock lock(mStatsMutex); + Stat &s = ioStats[inObjectLayerFilter.GetDescription()]; + s.mNumQueries++; + s.mNodesVisited += nodes_visited; + s.mBodiesVisited += bodies_visited; + s.mHitsReported += hits_collected; + s.mTotalTicks += total_ticks; + s.mCollectorTicks += collector_ticks; + } +#endif // JPH_TRACK_BROADPHASE_STATS +} + +void QuadTree::CastRay(const RayCast &inRay, RayCastBodyCollector &ioCollector, const ObjectLayerFilter &inObjectLayerFilter, const TrackingVector &inTracking) const +{ + class Visitor + { + public: + /// Constructor + JPH_INLINE Visitor(const RayCast &inRay, RayCastBodyCollector &ioCollector) : + mOrigin(inRay.mOrigin), + mInvDirection(inRay.mDirection), + mCollector(ioCollector) + { + mFractionStack[0] = -1; + } + + /// Returns true if further processing of the tree should be aborted + JPH_INLINE bool ShouldAbort() const + { + return mCollector.ShouldEarlyOut(); + } + + /// Returns true if this node / body should be visited, false if no hit can be generated + JPH_INLINE bool ShouldVisitNode(int inStackTop) const + { + return mFractionStack[inStackTop] < mCollector.GetEarlyOutFraction(); + } + + /// Visit nodes, returns number of hits found and sorts ioChildNodeIDs so that they are at the beginning of the vector. + JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioChildNodeIDs, int inStackTop) + { + // Test the ray against 4 bounding boxes + Vec4 fraction = RayAABox4(mOrigin, mInvDirection, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ); + + // Sort so that highest values are first (we want to first process closer hits and we process stack top to bottom) + return SortReverseAndStore(fraction, mCollector.GetEarlyOutFraction(), ioChildNodeIDs, &mFractionStack[inStackTop]); + } + + /// Visit a body, returns false if the algorithm should terminate because no hits can be generated anymore + JPH_INLINE void VisitBody(const BodyID &inBodyID, int inStackTop) + { + // Store potential hit with body + BroadPhaseCastResult result { inBodyID, mFractionStack[inStackTop] }; + mCollector.AddHit(result); + } + + private: + Vec3 mOrigin; + RayInvDirection mInvDirection; + RayCastBodyCollector & mCollector; + float mFractionStack[cStackSize]; + }; + + Visitor visitor(inRay, ioCollector); + WalkTree(inObjectLayerFilter, inTracking, visitor JPH_IF_TRACK_BROADPHASE_STATS(, mCastRayStats)); +} + +void QuadTree::CollideAABox(const AABox &inBox, CollideShapeBodyCollector &ioCollector, const ObjectLayerFilter &inObjectLayerFilter, const TrackingVector &inTracking) const +{ + class Visitor + { + public: + /// Constructor + JPH_INLINE Visitor(const AABox &inBox, CollideShapeBodyCollector &ioCollector) : + mBox(inBox), + mCollector(ioCollector) + { + } + + /// Returns true if further processing of the tree should be aborted + JPH_INLINE bool ShouldAbort() const + { + return mCollector.ShouldEarlyOut(); + } + + /// Returns true if this node / body should be visited, false if no hit can be generated + JPH_INLINE bool ShouldVisitNode(int inStackTop) const + { + return true; + } + + /// Visit nodes, returns number of hits found and sorts ioChildNodeIDs so that they are at the beginning of the vector. + JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioChildNodeIDs, int inStackTop) const + { + // Test the box vs 4 boxes + UVec4 hitting = AABox4VsBox(mBox, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ); + return CountAndSortTrues(hitting, ioChildNodeIDs); + } + + /// Visit a body, returns false if the algorithm should terminate because no hits can be generated anymore + JPH_INLINE void VisitBody(const BodyID &inBodyID, int inStackTop) + { + // Store potential hit with body + mCollector.AddHit(inBodyID); + } + + private: + const AABox & mBox; + CollideShapeBodyCollector & mCollector; + }; + + Visitor visitor(inBox, ioCollector); + WalkTree(inObjectLayerFilter, inTracking, visitor JPH_IF_TRACK_BROADPHASE_STATS(, mCollideAABoxStats)); +} + +void QuadTree::CollideSphere(Vec3Arg inCenter, float inRadius, CollideShapeBodyCollector &ioCollector, const ObjectLayerFilter &inObjectLayerFilter, const TrackingVector &inTracking) const +{ + class Visitor + { + public: + /// Constructor + JPH_INLINE Visitor(Vec3Arg inCenter, float inRadius, CollideShapeBodyCollector &ioCollector) : + mCenterX(inCenter.SplatX()), + mCenterY(inCenter.SplatY()), + mCenterZ(inCenter.SplatZ()), + mRadiusSq(Vec4::sReplicate(Square(inRadius))), + mCollector(ioCollector) + { + } + + /// Returns true if further processing of the tree should be aborted + JPH_INLINE bool ShouldAbort() const + { + return mCollector.ShouldEarlyOut(); + } + + /// Returns true if this node / body should be visited, false if no hit can be generated + JPH_INLINE bool ShouldVisitNode(int inStackTop) const + { + return true; + } + + /// Visit nodes, returns number of hits found and sorts ioChildNodeIDs so that they are at the beginning of the vector. + JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioChildNodeIDs, int inStackTop) const + { + // Test 4 boxes vs sphere + UVec4 hitting = AABox4VsSphere(mCenterX, mCenterY, mCenterZ, mRadiusSq, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ); + return CountAndSortTrues(hitting, ioChildNodeIDs); + } + + /// Visit a body, returns false if the algorithm should terminate because no hits can be generated anymore + JPH_INLINE void VisitBody(const BodyID &inBodyID, int inStackTop) + { + // Store potential hit with body + mCollector.AddHit(inBodyID); + } + + private: + Vec4 mCenterX; + Vec4 mCenterY; + Vec4 mCenterZ; + Vec4 mRadiusSq; + CollideShapeBodyCollector & mCollector; + }; + + Visitor visitor(inCenter, inRadius, ioCollector); + WalkTree(inObjectLayerFilter, inTracking, visitor JPH_IF_TRACK_BROADPHASE_STATS(, mCollideSphereStats)); +} + +void QuadTree::CollidePoint(Vec3Arg inPoint, CollideShapeBodyCollector &ioCollector, const ObjectLayerFilter &inObjectLayerFilter, const TrackingVector &inTracking) const +{ + class Visitor + { + public: + /// Constructor + JPH_INLINE Visitor(Vec3Arg inPoint, CollideShapeBodyCollector &ioCollector) : + mPoint(inPoint), + mCollector(ioCollector) + { + } + + /// Returns true if further processing of the tree should be aborted + JPH_INLINE bool ShouldAbort() const + { + return mCollector.ShouldEarlyOut(); + } + + /// Returns true if this node / body should be visited, false if no hit can be generated + JPH_INLINE bool ShouldVisitNode(int inStackTop) const + { + return true; + } + + /// Visit nodes, returns number of hits found and sorts ioChildNodeIDs so that they are at the beginning of the vector. + JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioChildNodeIDs, int inStackTop) const + { + // Test if point overlaps with box + UVec4 hitting = AABox4VsPoint(mPoint, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ); + return CountAndSortTrues(hitting, ioChildNodeIDs); + } + + /// Visit a body, returns false if the algorithm should terminate because no hits can be generated anymore + JPH_INLINE void VisitBody(const BodyID &inBodyID, int inStackTop) + { + // Store potential hit with body + mCollector.AddHit(inBodyID); + } + + private: + Vec3 mPoint; + CollideShapeBodyCollector & mCollector; + }; + + Visitor visitor(inPoint, ioCollector); + WalkTree(inObjectLayerFilter, inTracking, visitor JPH_IF_TRACK_BROADPHASE_STATS(, mCollidePointStats)); +} + +void QuadTree::CollideOrientedBox(const OrientedBox &inBox, CollideShapeBodyCollector &ioCollector, const ObjectLayerFilter &inObjectLayerFilter, const TrackingVector &inTracking) const +{ + class Visitor + { + public: + /// Constructor + JPH_INLINE Visitor(const OrientedBox &inBox, CollideShapeBodyCollector &ioCollector) : + mBox(inBox), + mCollector(ioCollector) + { + } + + /// Returns true if further processing of the tree should be aborted + JPH_INLINE bool ShouldAbort() const + { + return mCollector.ShouldEarlyOut(); + } + + /// Returns true if this node / body should be visited, false if no hit can be generated + JPH_INLINE bool ShouldVisitNode(int inStackTop) const + { + return true; + } + + /// Visit nodes, returns number of hits found and sorts ioChildNodeIDs so that they are at the beginning of the vector. + JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioChildNodeIDs, int inStackTop) const + { + // Test if point overlaps with box + UVec4 hitting = AABox4VsBox(mBox, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ); + return CountAndSortTrues(hitting, ioChildNodeIDs); + } + + /// Visit a body, returns false if the algorithm should terminate because no hits can be generated anymore + JPH_INLINE void VisitBody(const BodyID &inBodyID, int inStackTop) + { + // Store potential hit with body + mCollector.AddHit(inBodyID); + } + + private: + OrientedBox mBox; + CollideShapeBodyCollector & mCollector; + }; + + Visitor visitor(inBox, ioCollector); + WalkTree(inObjectLayerFilter, inTracking, visitor JPH_IF_TRACK_BROADPHASE_STATS(, mCollideOrientedBoxStats)); +} + +void QuadTree::CastAABox(const AABoxCast &inBox, CastShapeBodyCollector &ioCollector, const ObjectLayerFilter &inObjectLayerFilter, const TrackingVector &inTracking) const +{ + class Visitor + { + public: + /// Constructor + JPH_INLINE Visitor(const AABoxCast &inBox, CastShapeBodyCollector &ioCollector) : + mOrigin(inBox.mBox.GetCenter()), + mExtent(inBox.mBox.GetExtent()), + mInvDirection(inBox.mDirection), + mCollector(ioCollector) + { + mFractionStack[0] = -1; + } + + /// Returns true if further processing of the tree should be aborted + JPH_INLINE bool ShouldAbort() const + { + return mCollector.ShouldEarlyOut(); + } + + /// Returns true if this node / body should be visited, false if no hit can be generated + JPH_INLINE bool ShouldVisitNode(int inStackTop) const + { + return mFractionStack[inStackTop] < mCollector.GetPositiveEarlyOutFraction(); + } + + /// Visit nodes, returns number of hits found and sorts ioChildNodeIDs so that they are at the beginning of the vector. + JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioChildNodeIDs, int inStackTop) + { + // Enlarge them by the casted aabox extents + Vec4 bounds_min_x = inBoundsMinX, bounds_min_y = inBoundsMinY, bounds_min_z = inBoundsMinZ, bounds_max_x = inBoundsMaxX, bounds_max_y = inBoundsMaxY, bounds_max_z = inBoundsMaxZ; + AABox4EnlargeWithExtent(mExtent, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z); + + // Test 4 children + Vec4 fraction = RayAABox4(mOrigin, mInvDirection, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z); + + // Sort so that highest values are first (we want to first process closer hits and we process stack top to bottom) + return SortReverseAndStore(fraction, mCollector.GetPositiveEarlyOutFraction(), ioChildNodeIDs, &mFractionStack[inStackTop]); + } + + /// Visit a body, returns false if the algorithm should terminate because no hits can be generated anymore + JPH_INLINE void VisitBody(const BodyID &inBodyID, int inStackTop) + { + // Store potential hit with body + BroadPhaseCastResult result { inBodyID, mFractionStack[inStackTop] }; + mCollector.AddHit(result); + } + + private: + Vec3 mOrigin; + Vec3 mExtent; + RayInvDirection mInvDirection; + CastShapeBodyCollector & mCollector; + float mFractionStack[cStackSize]; + }; + + Visitor visitor(inBox, ioCollector); + WalkTree(inObjectLayerFilter, inTracking, visitor JPH_IF_TRACK_BROADPHASE_STATS(, mCastAABoxStats)); +} + +void QuadTree::FindCollidingPairs(const BodyVector &inBodies, const BodyID *inActiveBodies, int inNumActiveBodies, float inSpeculativeContactDistance, BodyPairCollector &ioPairCollector, const ObjectLayerPairFilter &inObjectLayerPairFilter) const +{ + // Note that we don't lock the tree at this point. We know that the tree is not going to be swapped or deleted while finding collision pairs due to the way the jobs are scheduled in the PhysicsSystem::Update. + // We double check this at the end of the function. + const RootNode &root_node = GetCurrentRoot(); + JPH_ASSERT(root_node.mIndex != cInvalidNodeIndex); + + // Assert sane input + JPH_ASSERT(inActiveBodies != nullptr); + JPH_ASSERT(inNumActiveBodies > 0); + + NodeID node_stack[cStackSize]; + + // Loop over all active bodies + for (int b1 = 0; b1 < inNumActiveBodies; ++b1) + { + BodyID b1_id = inActiveBodies[b1]; + const Body &body1 = *inBodies[b1_id.GetIndex()]; + JPH_ASSERT(!body1.IsStatic()); + + // Expand the bounding box by the speculative contact distance + AABox bounds1 = body1.GetWorldSpaceBounds(); + bounds1.ExpandBy(Vec3::sReplicate(inSpeculativeContactDistance)); + + // Test each body with the tree + node_stack[0] = root_node.GetNodeID(); + int top = 0; + do + { + // Check if node is a body + NodeID child_node_id = node_stack[top]; + if (child_node_id.IsBody()) + { + // Don't collide with self + BodyID b2_id = child_node_id.GetBodyID(); + if (b1_id != b2_id) + { + // Collision between dynamic pairs need to be picked up only once + const Body &body2 = *inBodies[b2_id.GetIndex()]; + if (inObjectLayerPairFilter.ShouldCollide(body1.GetObjectLayer(), body2.GetObjectLayer()) + && Body::sFindCollidingPairsCanCollide(body1, body2) + && bounds1.Overlaps(body2.GetWorldSpaceBounds())) // In the broadphase we widen the bounding box when a body moves, do a final check to see if the bounding boxes actually overlap + { + // Store potential hit between bodies + ioPairCollector.AddHit({ b1_id, b2_id }); + } + } + } + else if (child_node_id.IsValid()) + { + // Process normal node + const Node &node = mAllocator->Get(child_node_id.GetNodeIndex()); + JPH_ASSERT(IsAligned(&node, JPH_CACHE_LINE_SIZE)); + + // Get bounds of 4 children + Vec4 bounds_minx = Vec4::sLoadFloat4Aligned((const Float4 *)&node.mBoundsMinX); + Vec4 bounds_miny = Vec4::sLoadFloat4Aligned((const Float4 *)&node.mBoundsMinY); + Vec4 bounds_minz = Vec4::sLoadFloat4Aligned((const Float4 *)&node.mBoundsMinZ); + Vec4 bounds_maxx = Vec4::sLoadFloat4Aligned((const Float4 *)&node.mBoundsMaxX); + Vec4 bounds_maxy = Vec4::sLoadFloat4Aligned((const Float4 *)&node.mBoundsMaxY); + Vec4 bounds_maxz = Vec4::sLoadFloat4Aligned((const Float4 *)&node.mBoundsMaxZ); + + // Test overlap + UVec4 overlap = AABox4VsBox(bounds1, bounds_minx, bounds_miny, bounds_minz, bounds_maxx, bounds_maxy, bounds_maxz); + int num_results = overlap.CountTrues(); + if (num_results > 0) + { + // Load ids for 4 children + UVec4 child_ids = UVec4::sLoadInt4Aligned((const uint32 *)&node.mChildNodeID[0]); + + // Sort so that overlaps are first + child_ids = UVec4::sSort4True(overlap, child_ids); + + // Push them onto the stack + if (top + 4 < cStackSize) + { + child_ids.StoreInt4((uint32 *)&node_stack[top]); + top += num_results; + } + else + JPH_ASSERT(false, "Stack full!"); + } + } + --top; + } + while (top >= 0); + } + + // Test that the root node was not swapped while finding collision pairs. + // This would mean that UpdateFinalize/DiscardOldTree ran during collision detection which should not be possible due to the way the jobs are scheduled. + JPH_ASSERT(root_node.mIndex != cInvalidNodeIndex); + JPH_ASSERT(&root_node == &GetCurrentRoot()); +} + +#ifdef _DEBUG + +void QuadTree::ValidateTree(const BodyVector &inBodies, const TrackingVector &inTracking, uint32 inNodeIndex, uint32 inNumExpectedBodies) const +{ + JPH_PROFILE_FUNCTION(); + + // Root should be valid + JPH_ASSERT(inNodeIndex != cInvalidNodeIndex); + + // To avoid call overhead, create a stack in place + struct StackEntry + { + uint32 mNodeIndex; + uint32 mParentNodeIndex; + }; + StackEntry stack[cStackSize]; + stack[0].mNodeIndex = inNodeIndex; + stack[0].mParentNodeIndex = cInvalidNodeIndex; + int top = 0; + + uint32 num_bodies = 0; + + do + { + // Copy entry from the stack + StackEntry cur_stack = stack[top]; + + // Validate parent + const Node &node = mAllocator->Get(cur_stack.mNodeIndex); + JPH_ASSERT(node.mParentNodeIndex == cur_stack.mParentNodeIndex); + + // Validate that when a parent is not-changed that all of its children are also + JPH_ASSERT(cur_stack.mParentNodeIndex == cInvalidNodeIndex || mAllocator->Get(cur_stack.mParentNodeIndex).mIsChanged || !node.mIsChanged); + + // Loop children + for (uint32 i = 0; i < 4; ++i) + { + NodeID child_node_id = node.mChildNodeID[i]; + if (child_node_id.IsValid()) + { + if (child_node_id.IsNode()) + { + // Child is a node, recurse + uint32 child_idx = child_node_id.GetNodeIndex(); + JPH_ASSERT(top < cStackSize); + StackEntry &new_entry = stack[top++]; + new_entry.mNodeIndex = child_idx; + new_entry.mParentNodeIndex = cur_stack.mNodeIndex; + + // Validate that the bounding box is bigger or equal to the bounds in the tree + // Bounding box could also be invalid if all children of our child were removed + AABox child_bounds; + node.GetChildBounds(i, child_bounds); + AABox real_child_bounds; + mAllocator->Get(child_idx).GetNodeBounds(real_child_bounds); + JPH_ASSERT(child_bounds.Contains(real_child_bounds) || !real_child_bounds.IsValid()); + } + else + { + // Increment number of bodies found + ++num_bodies; + + // Check if tracker matches position of body + uint32 node_idx, child_idx; + GetBodyLocation(inTracking, child_node_id.GetBodyID(), node_idx, child_idx); + JPH_ASSERT(node_idx == cur_stack.mNodeIndex); + JPH_ASSERT(child_idx == i); + + // Validate that the body bounds are bigger or equal to the bounds in the tree + AABox body_bounds; + node.GetChildBounds(i, body_bounds); + const Body *body = inBodies[child_node_id.GetBodyID().GetIndex()]; + AABox cached_body_bounds = body->GetWorldSpaceBounds(); + AABox real_body_bounds = body->GetShape()->GetWorldSpaceBounds(body->GetCenterOfMassTransform(), Vec3::sReplicate(1.0f)); + JPH_ASSERT(cached_body_bounds == real_body_bounds); // Check that cached body bounds are up to date + JPH_ASSERT(body_bounds.Contains(real_body_bounds)); + } + } + } + --top; + } + while (top >= 0); + + // Check that the amount of bodies in the tree matches our counter + JPH_ASSERT(num_bodies == inNumExpectedBodies); +} + +#endif + +#ifdef JPH_DUMP_BROADPHASE_TREE + +void QuadTree::DumpTree(const NodeID &inRoot, const char *inFileNamePrefix) const +{ + // Open DOT file + std::ofstream f; + f.open(StringFormat("%s.dot", inFileNamePrefix).c_str(), std::ofstream::out | std::ofstream::trunc); + if (!f.is_open()) + return; + + // Write header + f << "digraph {\n"; + + // Iterate the entire tree + NodeID node_stack[cStackSize]; + node_stack[0] = inRoot; + JPH_ASSERT(node_stack[0].IsValid()); + int top = 0; + do + { + // Check if node is a body + NodeID node_id = node_stack[top]; + if (node_id.IsBody()) + { + // Output body + String body_id = ConvertToString(node_id.GetBodyID().GetIndex()); + f << "body" << body_id << "[label = \"Body " << body_id << "\"]\n"; + } + else + { + // Process normal node + uint32 node_idx = node_id.GetNodeIndex(); + const Node &node = mAllocator->Get(node_idx); + + // Get bounding box + AABox bounds; + node.GetNodeBounds(bounds); + + // Output node + String node_str = ConvertToString(node_idx); + f << "node" << node_str << "[label = \"Node " << node_str << "\nVolume: " << ConvertToString(bounds.GetVolume()) << "\" color=" << (node.mIsChanged? "red" : "black") << "]\n"; + + // Recurse and get all children + for (NodeID child_node_id : node.mChildNodeID) + if (child_node_id.IsValid()) + { + JPH_ASSERT(top < cStackSize); + node_stack[top] = child_node_id; + top++; + + // Output link + f << "node" << node_str << " -> "; + if (child_node_id.IsBody()) + f << "body" << ConvertToString(child_node_id.GetBodyID().GetIndex()); + else + f << "node" << ConvertToString(child_node_id.GetNodeIndex()); + f << "\n"; + } + } + --top; + } + while (top >= 0); + + // Finish DOT file + f << "}\n"; + f.close(); + + // Convert to svg file + String cmd = StringFormat("dot %s.dot -Tsvg -o %s.svg", inFileNamePrefix, inFileNamePrefix); + system(cmd.c_str()); +} + +#endif // JPH_DUMP_BROADPHASE_TREE + +#ifdef JPH_TRACK_BROADPHASE_STATS + +uint64 QuadTree::GetTicks100Pct(const LayerToStats &inLayer) const +{ + uint64 total_ticks = 0; + for (const LayerToStats::value_type &kv : inLayer) + total_ticks += kv.second.mTotalTicks; + return total_ticks; +} + +void QuadTree::ReportStats(const char *inName, const LayerToStats &inLayer, uint64 inTicks100Pct) const +{ + for (const LayerToStats::value_type &kv : inLayer) + { + double total_pct = 100.0 * double(kv.second.mTotalTicks) / double(inTicks100Pct); + double total_pct_excl_collector = 100.0 * double(kv.second.mTotalTicks - kv.second.mCollectorTicks) / double(inTicks100Pct); + double hits_reported_vs_bodies_visited = kv.second.mBodiesVisited > 0? 100.0 * double(kv.second.mHitsReported) / double(kv.second.mBodiesVisited) : 100.0; + double hits_reported_vs_nodes_visited = kv.second.mNodesVisited > 0? double(kv.second.mHitsReported) / double(kv.second.mNodesVisited) : -1.0; + + std::stringstream str; + str << inName << ", " << kv.first << ", " << mName << ", " << kv.second.mNumQueries << ", " << total_pct << ", " << total_pct_excl_collector << ", " << kv.second.mNodesVisited << ", " << kv.second.mBodiesVisited << ", " << kv.second.mHitsReported << ", " << hits_reported_vs_bodies_visited << ", " << hits_reported_vs_nodes_visited; + Trace(str.str().c_str()); + } +} + +uint64 QuadTree::GetTicks100Pct() const +{ + uint64 total_ticks = 0; + total_ticks += GetTicks100Pct(mCastRayStats); + total_ticks += GetTicks100Pct(mCollideAABoxStats); + total_ticks += GetTicks100Pct(mCollideSphereStats); + total_ticks += GetTicks100Pct(mCollidePointStats); + total_ticks += GetTicks100Pct(mCollideOrientedBoxStats); + total_ticks += GetTicks100Pct(mCastAABoxStats); + return total_ticks; +} + +void QuadTree::ReportStats(uint64 inTicks100Pct) const +{ + unique_lock lock(mStatsMutex); + ReportStats("RayCast", mCastRayStats, inTicks100Pct); + ReportStats("CollideAABox", mCollideAABoxStats, inTicks100Pct); + ReportStats("CollideSphere", mCollideSphereStats, inTicks100Pct); + ReportStats("CollidePoint", mCollidePointStats, inTicks100Pct); + ReportStats("CollideOrientedBox", mCollideOrientedBoxStats, inTicks100Pct); + ReportStats("CastAABox", mCastAABoxStats, inTicks100Pct); +} + +#endif // JPH_TRACK_BROADPHASE_STATS + +uint QuadTree::GetMaxTreeDepth(const NodeID &inNodeID) const +{ + // Reached a leaf? + if (!inNodeID.IsValid() || inNodeID.IsBody()) + return 0; + + // Recurse to children + uint max_depth = 0; + const Node &node = mAllocator->Get(inNodeID.GetNodeIndex()); + for (NodeID child_node_id : node.mChildNodeID) + max_depth = max(max_depth, GetMaxTreeDepth(child_node_id)); + return max_depth + 1; +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/QuadTree.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/QuadTree.h new file mode 100644 index 00000000000..053ea4f7a39 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/QuadTree.h @@ -0,0 +1,388 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include +#include +#include + +//#define JPH_DUMP_BROADPHASE_TREE + +JPH_NAMESPACE_BEGIN + +/// Internal tree structure in broadphase, is essentially a quad AABB tree. +/// Tree is lockless (except for UpdatePrepare/Finalize() function), modifying objects in the tree will widen the aabbs of parent nodes to make the node fit. +/// During the UpdatePrepare/Finalize() call the tree is rebuilt to achieve a tight fit again. +class JPH_EXPORT QuadTree : public NonCopyable +{ +public: + JPH_OVERRIDE_NEW_DELETE + +private: + // Forward declare + class AtomicNodeID; + + /// Class that points to either a body or a node in the tree + class NodeID + { + public: + JPH_OVERRIDE_NEW_DELETE + + /// Default constructor does not initialize + inline NodeID() = default; + + /// Construct a node ID + static inline NodeID sInvalid() { return NodeID(cInvalidNodeIndex); } + static inline NodeID sFromBodyID(BodyID inID) { NodeID node_id(inID.GetIndexAndSequenceNumber()); JPH_ASSERT(node_id.IsBody()); return node_id; } + static inline NodeID sFromNodeIndex(uint32 inIdx) { NodeID node_id(inIdx | cIsNode); JPH_ASSERT(node_id.IsNode()); return node_id; } + + /// Check what type of ID it is + inline bool IsValid() const { return mID != cInvalidNodeIndex; } + inline bool IsBody() const { return (mID & cIsNode) == 0; } + inline bool IsNode() const { return (mID & cIsNode) != 0; } + + /// Get body or node index + inline BodyID GetBodyID() const { JPH_ASSERT(IsBody()); return BodyID(mID); } + inline uint32 GetNodeIndex() const { JPH_ASSERT(IsNode()); return mID & ~cIsNode; } + + /// Comparison + inline bool operator == (const BodyID &inRHS) const { return mID == inRHS.GetIndexAndSequenceNumber(); } + inline bool operator == (const NodeID &inRHS) const { return mID == inRHS.mID; } + + private: + friend class AtomicNodeID; + + inline explicit NodeID(uint32 inID) : mID(inID) { } + + static const uint32 cIsNode = BodyID::cBroadPhaseBit; ///< If this bit is set it means that the ID refers to a node, otherwise it refers to a body + + uint32 mID; + }; + + static_assert(sizeof(NodeID) == sizeof(BodyID), "Body id's should have the same size as NodeIDs"); + + /// A NodeID that uses atomics to store the value + class AtomicNodeID + { + public: + /// Constructor + AtomicNodeID() = default; + explicit AtomicNodeID(const NodeID &inRHS) : mID(inRHS.mID) { } + + /// Assignment + inline void operator = (const NodeID &inRHS) { mID = inRHS.mID; } + + /// Getting the value + inline operator NodeID () const { return NodeID(mID); } + + /// Check if the ID is valid + inline bool IsValid() const { return mID != cInvalidNodeIndex; } + + /// Comparison + inline bool operator == (const BodyID &inRHS) const { return mID == inRHS.GetIndexAndSequenceNumber(); } + inline bool operator == (const NodeID &inRHS) const { return mID == inRHS.mID; } + + /// Atomically compare and swap value. Expects inOld value, replaces with inNew value or returns false + inline bool CompareExchange(NodeID inOld, NodeID inNew) { return mID.compare_exchange_strong(inOld.mID, inNew.mID); } + + private: + atomic mID; + }; + + /// Class that represents a node in the tree + class Node + { + public: + /// Construct node + explicit Node(bool inIsChanged); + + /// Get bounding box encapsulating all children + void GetNodeBounds(AABox &outBounds) const; + + /// Get bounding box in a consistent way with the functions below (check outBounds.IsValid() before using the box) + void GetChildBounds(int inChildIndex, AABox &outBounds) const; + + /// Set the bounds in such a way that other threads will either see a fully correct bounding box or a bounding box with no volume + void SetChildBounds(int inChildIndex, const AABox &inBounds); + + /// Invalidate bounding box in such a way that other threads will not temporarily see a very large bounding box + void InvalidateChildBounds(int inChildIndex); + + /// Encapsulate inBounds in node bounds, returns true if there were changes + bool EncapsulateChildBounds(int inChildIndex, const AABox &inBounds); + + /// Bounding box for child nodes or bodies (all initially set to invalid so no collision test will ever traverse to the leaf) + atomic mBoundsMinX[4]; + atomic mBoundsMinY[4]; + atomic mBoundsMinZ[4]; + atomic mBoundsMaxX[4]; + atomic mBoundsMaxY[4]; + atomic mBoundsMaxZ[4]; + + /// Index of child node or body ID. + AtomicNodeID mChildNodeID[4]; + + /// Index of the parent node. + /// Note: This value is unreliable during the UpdatePrepare/Finalize() function as a node may be relinked to the newly built tree. + atomic mParentNodeIndex = cInvalidNodeIndex; + + /// If this part of the tree has changed, if not, we will treat this sub tree as a single body during the UpdatePrepare/Finalize(). + /// If any changes are made to an object inside this sub tree then the direct path from the body to the top of the tree will become changed. + atomic mIsChanged; + + // Padding to align to 124 bytes + uint32 mPadding = 0; + }; + + // Maximum size of the stack during tree walk + static constexpr int cStackSize = 128; + + static_assert(sizeof(atomic) == 4, "Assuming that an atomic doesn't add any additional storage"); + static_assert(sizeof(atomic) == 4, "Assuming that an atomic doesn't add any additional storage"); + static_assert(is_trivially_destructible(), "Assuming that we don't have a destructor"); + +public: + /// Class that allocates tree nodes, can be shared between multiple trees + using Allocator = FixedSizeFreeList; + + static_assert(Allocator::ObjectStorageSize == 128, "Node should be 128 bytes"); + + /// Data to track location of a Body in the tree + struct Tracking + { + /// Constructor to satisfy the vector class + Tracking() = default; + Tracking(const Tracking &inRHS) : mBroadPhaseLayer(inRHS.mBroadPhaseLayer.load()), mObjectLayer(inRHS.mObjectLayer.load()), mBodyLocation(inRHS.mBodyLocation.load()) { } + + /// Invalid body location identifier + static const uint32 cInvalidBodyLocation = 0xffffffff; + + atomic mBroadPhaseLayer = (BroadPhaseLayer::Type)cBroadPhaseLayerInvalid; + atomic mObjectLayer = cObjectLayerInvalid; + atomic mBodyLocation { cInvalidBodyLocation }; + }; + + using TrackingVector = Array; + + /// Destructor + ~QuadTree(); + +#if defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED) + /// Name of the tree for debugging purposes + void SetName(const char *inName) { mName = inName; } + inline const char * GetName() const { return mName; } +#endif // JPH_EXTERNAL_PROFILE || JPH_PROFILE_ENABLED + + /// Check if there is anything in the tree + inline bool HasBodies() const { return mNumBodies != 0; } + + /// Check if the tree needs an UpdatePrepare/Finalize() + inline bool IsDirty() const { return mIsDirty; } + + /// Check if this tree can get an UpdatePrepare/Finalize() or if it needs a DiscardOldTree() first + inline bool CanBeUpdated() const { return mFreeNodeBatch.mNumObjects == 0; } + + /// Initialization + void Init(Allocator &inAllocator); + + struct UpdateState + { + NodeID mRootNodeID; ///< This will be the new root node id + }; + + /// Will throw away the previous frame's nodes so that we can start building a new tree in the background + void DiscardOldTree(); + + /// Get the bounding box for this tree + AABox GetBounds() const; + + /// Update the broadphase, needs to be called regularly to achieve a tight fit of the tree when bodies have been modified. + /// UpdatePrepare() will build the tree, UpdateFinalize() will lock the root of the tree shortly and swap the trees and afterwards clean up temporary data structures. + void UpdatePrepare(const BodyVector &inBodies, TrackingVector &ioTracking, UpdateState &outUpdateState, bool inFullRebuild); + void UpdateFinalize(const BodyVector &inBodies, const TrackingVector &inTracking, const UpdateState &inUpdateState); + + /// Temporary data structure to pass information between AddBodiesPrepare and AddBodiesFinalize/Abort + struct AddState + { + NodeID mLeafID = NodeID::sInvalid(); + AABox mLeafBounds; + }; + + /// Prepare adding inNumber bodies at ioBodyIDs to the quad tree, returns the state in outState that should be used in AddBodiesFinalize. + /// This can be done on a background thread without influencing the broadphase. + /// ioBodyIDs may be shuffled around by this function. + void AddBodiesPrepare(const BodyVector &inBodies, TrackingVector &ioTracking, BodyID *ioBodyIDs, int inNumber, AddState &outState); + + /// Finalize adding bodies to the quadtree, supply the same number of bodies as in AddBodiesPrepare. + void AddBodiesFinalize(TrackingVector &ioTracking, int inNumberBodies, const AddState &inState); + + /// Abort adding bodies to the quadtree, supply the same bodies and state as in AddBodiesPrepare. + /// This can be done on a background thread without influencing the broadphase. + void AddBodiesAbort(TrackingVector &ioTracking, const AddState &inState); + + /// Remove inNumber bodies in ioBodyIDs from the quadtree. + void RemoveBodies(const BodyVector &inBodies, TrackingVector &ioTracking, const BodyID *ioBodyIDs, int inNumber); + + /// Call whenever the aabb of a body changes. + void NotifyBodiesAABBChanged(const BodyVector &inBodies, const TrackingVector &inTracking, const BodyID *ioBodyIDs, int inNumber); + + /// Cast a ray and get the intersecting bodies in ioCollector. + void CastRay(const RayCast &inRay, RayCastBodyCollector &ioCollector, const ObjectLayerFilter &inObjectLayerFilter, const TrackingVector &inTracking) const; + + /// Get bodies intersecting with inBox in ioCollector + void CollideAABox(const AABox &inBox, CollideShapeBodyCollector &ioCollector, const ObjectLayerFilter &inObjectLayerFilter, const TrackingVector &inTracking) const; + + /// Get bodies intersecting with a sphere in ioCollector + void CollideSphere(Vec3Arg inCenter, float inRadius, CollideShapeBodyCollector &ioCollector, const ObjectLayerFilter &inObjectLayerFilter, const TrackingVector &inTracking) const; + + /// Get bodies intersecting with a point and any hits to ioCollector + void CollidePoint(Vec3Arg inPoint, CollideShapeBodyCollector &ioCollector, const ObjectLayerFilter &inObjectLayerFilter, const TrackingVector &inTracking) const; + + /// Get bodies intersecting with an oriented box and any hits to ioCollector + void CollideOrientedBox(const OrientedBox &inBox, CollideShapeBodyCollector &ioCollector, const ObjectLayerFilter &inObjectLayerFilter, const TrackingVector &inTracking) const; + + /// Cast a box and get intersecting bodies in ioCollector + void CastAABox(const AABoxCast &inBox, CastShapeBodyCollector &ioCollector, const ObjectLayerFilter &inObjectLayerFilter, const TrackingVector &inTracking) const; + + /// Find all colliding pairs between dynamic bodies, calls ioPairCollector for every pair found + void FindCollidingPairs(const BodyVector &inBodies, const BodyID *inActiveBodies, int inNumActiveBodies, float inSpeculativeContactDistance, BodyPairCollector &ioPairCollector, const ObjectLayerPairFilter &inObjectLayerPairFilter) const; + +#ifdef JPH_TRACK_BROADPHASE_STATS + /// Sum up all the ticks spent in the various layers + uint64 GetTicks100Pct() const; + + /// Trace the stats of this tree to the TTY + void ReportStats(uint64 inTicks100Pct) const; +#endif // JPH_TRACK_BROADPHASE_STATS + +private: + /// Constants + static const uint32 cInvalidNodeIndex = 0xffffffff; ///< Value used to indicate node index is invalid + static const float cLargeFloat; ///< A large floating point number that is small enough to not cause any overflows + static const AABox cInvalidBounds; ///< Invalid bounding box using cLargeFloat + + /// We alternate between two trees in order to let collision queries complete in parallel to adding/removing objects to the tree + struct RootNode + { + /// Get the ID of the root node + inline NodeID GetNodeID() const { return NodeID::sFromNodeIndex(mIndex); } + + /// Index of the root node of the tree (this is always a node, never a body id) + atomic mIndex { cInvalidNodeIndex }; + }; + + /// Caches location of body inBodyID in the tracker, body can be found in mNodes[inNodeIdx].mChildNodeID[inChildIdx] + void GetBodyLocation(const TrackingVector &inTracking, BodyID inBodyID, uint32 &outNodeIdx, uint32 &outChildIdx) const; + void SetBodyLocation(TrackingVector &ioTracking, BodyID inBodyID, uint32 inNodeIdx, uint32 inChildIdx) const; + static void sInvalidateBodyLocation(TrackingVector &ioTracking, BodyID inBodyID); + + /// Get the current root of the tree + JPH_INLINE const RootNode & GetCurrentRoot() const { return mRootNode[mRootNodeIndex]; } + JPH_INLINE RootNode & GetCurrentRoot() { return mRootNode[mRootNodeIndex]; } + + /// Depending on if inNodeID is a body or tree node return the bounding box + inline AABox GetNodeOrBodyBounds(const BodyVector &inBodies, NodeID inNodeID) const; + + /// Mark node and all of its parents as changed + inline void MarkNodeAndParentsChanged(uint32 inNodeIndex); + + /// Widen parent bounds of node inNodeIndex to encapsulate inNewBounds, also mark node and all of its parents as changed + inline void WidenAndMarkNodeAndParentsChanged(uint32 inNodeIndex, const AABox &inNewBounds); + + /// Allocate a new node + inline uint32 AllocateNode(bool inIsChanged); + + /// Try to insert a new leaf to the tree at inNodeIndex + inline bool TryInsertLeaf(TrackingVector &ioTracking, int inNodeIndex, NodeID inLeafID, const AABox &inLeafBounds, int inLeafNumBodies); + + /// Try to replace the existing root with a new root that contains both the existing root and the new leaf + inline bool TryCreateNewRoot(TrackingVector &ioTracking, atomic &ioRootNodeIndex, NodeID inLeafID, const AABox &inLeafBounds, int inLeafNumBodies); + + /// Build a tree for ioBodyIDs, returns the NodeID of the root (which will be the ID of a single body if inNumber = 1). All tree levels up to inMaxDepthMarkChanged will be marked as 'changed'. + NodeID BuildTree(const BodyVector &inBodies, TrackingVector &ioTracking, NodeID *ioNodeIDs, int inNumber, uint inMaxDepthMarkChanged, AABox &outBounds); + + /// Sorts ioNodeIDs spatially into 2 groups. Second groups starts at ioNodeIDs + outMidPoint. + /// After the function returns ioNodeIDs and ioNodeCenters will be shuffled + static void sPartition(NodeID *ioNodeIDs, Vec3 *ioNodeCenters, int inNumber, int &outMidPoint); + + /// Sorts ioNodeIDs from inBegin to (but excluding) inEnd spatially into 4 groups. + /// outSplit needs to be 5 ints long, when the function returns each group runs from outSplit[i] to (but excluding) outSplit[i + 1] + /// After the function returns ioNodeIDs and ioNodeCenters will be shuffled + static void sPartition4(NodeID *ioNodeIDs, Vec3 *ioNodeCenters, int inBegin, int inEnd, int *outSplit); + +#ifdef _DEBUG + /// Validate that the tree is consistent. + /// Note: This function only works if the tree is not modified while we're traversing it. + void ValidateTree(const BodyVector &inBodies, const TrackingVector &inTracking, uint32 inNodeIndex, uint32 inNumExpectedBodies) const; +#endif + +#ifdef JPH_DUMP_BROADPHASE_TREE + /// Dump the tree in DOT format (see: https://graphviz.org/) + void DumpTree(const NodeID &inRoot, const char *inFileNamePrefix) const; +#endif + +#ifdef JPH_TRACK_BROADPHASE_STATS + /// Mutex protecting the various LayerToStats members + mutable Mutex mStatsMutex; + + struct Stat + { + uint64 mNumQueries = 0; + uint64 mNodesVisited = 0; + uint64 mBodiesVisited = 0; + uint64 mHitsReported = 0; + uint64 mTotalTicks = 0; + uint64 mCollectorTicks = 0; + }; + + using LayerToStats = UnorderedMap; + + /// Sum up all the ticks in a layer + uint64 GetTicks100Pct(const LayerToStats &inLayer) const; + + /// Trace the stats of a single query type to the TTY + void ReportStats(const char *inName, const LayerToStats &inLayer, uint64 inTicks100Pct) const; + + mutable LayerToStats mCastRayStats; + mutable LayerToStats mCollideAABoxStats; + mutable LayerToStats mCollideSphereStats; + mutable LayerToStats mCollidePointStats; + mutable LayerToStats mCollideOrientedBoxStats; + mutable LayerToStats mCastAABoxStats; +#endif // JPH_TRACK_BROADPHASE_STATS + + /// Debug function to get the depth of the tree from node inNodeID + uint GetMaxTreeDepth(const NodeID &inNodeID) const; + + /// Walk the node tree calling the Visitor::VisitNodes for each node encountered and Visitor::VisitBody for each body encountered + template + JPH_INLINE void WalkTree(const ObjectLayerFilter &inObjectLayerFilter, const TrackingVector &inTracking, Visitor &ioVisitor JPH_IF_TRACK_BROADPHASE_STATS(, LayerToStats &ioStats)) const; + +#if defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED) + /// Name of this tree for debugging purposes + const char * mName = "Layer"; +#endif // JPH_EXTERNAL_PROFILE || JPH_PROFILE_ENABLED + + /// Number of bodies currently in the tree + atomic mNumBodies { 0 }; + + /// We alternate between two tree root nodes. When updating, we activate the new tree and we keep the old tree alive. + /// for queries that are in progress until the next time DiscardOldTree() is called. + RootNode mRootNode[2]; + atomic mRootNodeIndex { 0 }; + + /// Allocator that controls adding / freeing nodes + Allocator * mAllocator = nullptr; + + /// This is a list of nodes that must be deleted after the trees are swapped and the old tree is no longer in use + Allocator::Batch mFreeNodeBatch; + + /// Flag to keep track of changes to the broadphase, if false, we don't need to UpdatePrepare/Finalize() + atomic mIsDirty = false; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CastConvexVsTriangles.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CastConvexVsTriangles.cpp new file mode 100644 index 00000000000..a69208a04dd --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CastConvexVsTriangles.cpp @@ -0,0 +1,109 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +CastConvexVsTriangles::CastConvexVsTriangles(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, Vec3Arg inScale, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, CastShapeCollector &ioCollector) : + mShapeCast(inShapeCast), + mShapeCastSettings(inShapeCastSettings), + mCenterOfMassTransform2(inCenterOfMassTransform2), + mScale(inScale), + mSubShapeIDCreator1(inSubShapeIDCreator1), + mCollector(ioCollector) +{ + JPH_ASSERT(inShapeCast.mShape->GetType() == EShapeType::Convex); + + // Determine if shape is inside out or not + mScaleSign = ScaleHelpers::IsInsideOut(inScale)? -1.0f : 1.0f; +} + +void CastConvexVsTriangles::Cast(Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2, uint8 inActiveEdges, const SubShapeID &inSubShapeID2) +{ + JPH_PROFILE_FUNCTION(); + + // Scale triangle + Vec3 v0 = mScale * inV0; + Vec3 v1 = mScale * inV1; + Vec3 v2 = mScale * inV2; + + // Calculate triangle normal + Vec3 triangle_normal = mScaleSign * (v1 - v0).Cross(v2 - v0); + + // Backface check + bool back_facing = triangle_normal.Dot(mShapeCast.mDirection) > 0.0f; + if (mShapeCastSettings.mBackFaceModeTriangles == EBackFaceMode::IgnoreBackFaces && back_facing) + return; + + // Create triangle support function + TriangleConvexSupport triangle { v0, v1, v2 }; + + // Check if we already created the cast shape support function + if (mSupport == nullptr) + { + // Determine if we want to use the actual shape or a shrunken shape with convex radius + ConvexShape::ESupportMode support_mode = mShapeCastSettings.mUseShrunkenShapeAndConvexRadius? ConvexShape::ESupportMode::ExcludeConvexRadius : ConvexShape::ESupportMode::Default; + + // Create support function + mSupport = static_cast(mShapeCast.mShape)->GetSupportFunction(support_mode, mSupportBuffer, mShapeCast.mScale); + } + + EPAPenetrationDepth epa; + float fraction = mCollector.GetEarlyOutFraction(); + Vec3 contact_point_a, contact_point_b, contact_normal; + if (epa.CastShape(mShapeCast.mCenterOfMassStart, mShapeCast.mDirection, mShapeCastSettings.mCollisionTolerance, mShapeCastSettings.mPenetrationTolerance, *mSupport, triangle, mSupport->GetConvexRadius(), 0.0f, mShapeCastSettings.mReturnDeepestPoint, fraction, contact_point_a, contact_point_b, contact_normal)) + { + // Check if we have enabled active edge detection + if (mShapeCastSettings.mActiveEdgeMode == EActiveEdgeMode::CollideOnlyWithActive && inActiveEdges != 0b111) + { + // Convert the active edge velocity hint to local space + Vec3 active_edge_movement_direction = mCenterOfMassTransform2.Multiply3x3Transposed(mShapeCastSettings.mActiveEdgeMovementDirection); + + // Update the contact normal to account for active edges + // Note that we flip the triangle normal as the penetration axis is pointing towards the triangle instead of away + contact_normal = ActiveEdges::FixNormal(v0, v1, v2, back_facing? triangle_normal : -triangle_normal, inActiveEdges, contact_point_b, contact_normal, active_edge_movement_direction); + } + + // Convert to world space + contact_point_a = mCenterOfMassTransform2 * contact_point_a; + contact_point_b = mCenterOfMassTransform2 * contact_point_b; + Vec3 contact_normal_world = mCenterOfMassTransform2.Multiply3x3(contact_normal); + + // Its a hit, store the sub shape id's + ShapeCastResult result(fraction, contact_point_a, contact_point_b, contact_normal_world, back_facing, mSubShapeIDCreator1.GetID(), inSubShapeID2, TransformedShape::sGetBodyID(mCollector.GetContext())); + + // Early out if this hit is deeper than the collector's early out value + if (fraction == 0.0f && -result.mPenetrationDepth >= mCollector.GetEarlyOutFraction()) + return; + + // Gather faces + if (mShapeCastSettings.mCollectFacesMode == ECollectFacesMode::CollectFaces) + { + // Get supporting face of shape 1 + Mat44 transform_1_to_2 = mShapeCast.mCenterOfMassStart; + transform_1_to_2.SetTranslation(transform_1_to_2.GetTranslation() + fraction * mShapeCast.mDirection); + static_cast(mShapeCast.mShape)->GetSupportingFace(SubShapeID(), transform_1_to_2.Multiply3x3Transposed(-contact_normal), mShapeCast.mScale, mCenterOfMassTransform2 * transform_1_to_2, result.mShape1Face); + + // Get face of the triangle + triangle.GetSupportingFace(contact_normal, result.mShape2Face); + + // Convert to world space + for (Vec3 &p : result.mShape2Face) + p = mCenterOfMassTransform2 * p; + } + + JPH_IF_TRACK_NARROWPHASE_STATS(TrackNarrowPhaseCollector track;) + mCollector.AddHit(result); + } +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CastConvexVsTriangles.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CastConvexVsTriangles.h new file mode 100644 index 00000000000..24e3a99b4e8 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CastConvexVsTriangles.h @@ -0,0 +1,46 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +JPH_NAMESPACE_BEGIN + +/// Collision detection helper that casts a convex object vs one or more triangles +class JPH_EXPORT CastConvexVsTriangles +{ +public: + /// Constructor + /// @param inShapeCast The shape to cast against the triangles and its start and direction + /// @param inShapeCastSettings Settings for performing the cast + /// @param inScale Local space scale for the shape to cast against. + /// @param inCenterOfMassTransform2 Is the center of mass transform of shape 2 (excluding scale), this is used to provide a transform to the shape cast result so that local quantities can be transformed into world space. + /// @param inSubShapeIDCreator1 Class that tracks the current sub shape ID for the casting shape + /// @param ioCollector The collector that receives the results. + CastConvexVsTriangles(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, Vec3Arg inScale, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, CastShapeCollector &ioCollector); + + /// Cast convex object with a single triangle + /// @param inV0 , inV1 , inV2: CCW triangle vertices + /// @param inActiveEdges bit 0 = edge v0..v1 is active, bit 1 = edge v1..v2 is active, bit 2 = edge v2..v0 is active + /// An active edge is an edge that is not connected to another triangle in such a way that it is impossible to collide with the edge + /// @param inSubShapeID2 The sub shape ID for the triangle + void Cast(Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2, uint8 inActiveEdges, const SubShapeID &inSubShapeID2); + +protected: + const ShapeCast & mShapeCast; + const ShapeCastSettings & mShapeCastSettings; + const Mat44 & mCenterOfMassTransform2; + Vec3 mScale; + SubShapeIDCreator mSubShapeIDCreator1; + CastShapeCollector & mCollector; + +private: + ConvexShape::SupportBuffer mSupportBuffer; ///< Buffer that holds the support function of the cast shape + const ConvexShape::Support * mSupport = nullptr; ///< Support function of the cast shape + float mScaleSign; ///< Sign of the scale, -1 if object is inside out, 1 if not +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CastResult.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CastResult.h new file mode 100644 index 00000000000..6bb49feb7c8 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CastResult.h @@ -0,0 +1,37 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +JPH_NAMESPACE_BEGIN + +/// Structure that holds a ray cast or other object cast hit +class BroadPhaseCastResult +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Function required by the CollisionCollector. A smaller fraction is considered to be a 'better hit'. For rays/cast shapes we can just use the collision fraction. + inline float GetEarlyOutFraction() const { return mFraction; } + + /// Reset this result so it can be reused for a new cast. + inline void Reset() { mBodyID = BodyID(); mFraction = 1.0f + FLT_EPSILON; } + + BodyID mBodyID; ///< Body that was hit + float mFraction = 1.0f + FLT_EPSILON; ///< Hit fraction of the ray/object [0, 1], HitPoint = Start + mFraction * (End - Start) +}; + +/// Specialization of cast result against a shape +class RayCastResult : public BroadPhaseCastResult +{ +public: + JPH_OVERRIDE_NEW_DELETE + + SubShapeID mSubShapeID2; ///< Sub shape ID of shape that we collided against +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CastSphereVsTriangles.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CastSphereVsTriangles.cpp new file mode 100644 index 00000000000..166883be8da --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CastSphereVsTriangles.cpp @@ -0,0 +1,223 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +CastSphereVsTriangles::CastSphereVsTriangles(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, Vec3Arg inScale, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, CastShapeCollector &ioCollector) : + mStart(inShapeCast.mCenterOfMassStart.GetTranslation()), + mDirection(inShapeCast.mDirection), + mShapeCastSettings(inShapeCastSettings), + mCenterOfMassTransform2(inCenterOfMassTransform2), + mScale(inScale), + mSubShapeIDCreator1(inSubShapeIDCreator1), + mCollector(ioCollector) +{ + // Cast to sphere shape + JPH_ASSERT(inShapeCast.mShape->GetSubType() == EShapeSubType::Sphere); + const SphereShape *sphere = static_cast(inShapeCast.mShape); + + // Scale the radius + mRadius = sphere->GetRadius() * abs(inShapeCast.mScale.GetX()); + + // Determine if shape is inside out or not + mScaleSign = ScaleHelpers::IsInsideOut(inScale)? -1.0f : 1.0f; +} + +void CastSphereVsTriangles::AddHit(bool inBackFacing, const SubShapeID &inSubShapeID2, float inFraction, Vec3Arg inContactPointA, Vec3Arg inContactPointB, Vec3Arg inContactNormal) +{ + // Convert to world space + Vec3 contact_point_a = mCenterOfMassTransform2 * (mStart + inContactPointA); + Vec3 contact_point_b = mCenterOfMassTransform2 * (mStart + inContactPointB); + Vec3 contact_normal_world = mCenterOfMassTransform2.Multiply3x3(inContactNormal); + + // Its a hit, store the sub shape id's + ShapeCastResult result(inFraction, contact_point_a, contact_point_b, contact_normal_world, inBackFacing, mSubShapeIDCreator1.GetID(), inSubShapeID2, TransformedShape::sGetBodyID(mCollector.GetContext())); + + // Note: We don't gather faces here because that's only useful if both shapes have a face. Since the sphere always has only 1 contact point, the manifold is always a point. + + JPH_IF_TRACK_NARROWPHASE_STATS(TrackNarrowPhaseCollector track;) + mCollector.AddHit(result); +} + +void CastSphereVsTriangles::AddHitWithActiveEdgeDetection(Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2, bool inBackFacing, Vec3Arg inTriangleNormal, uint8 inActiveEdges, const SubShapeID &inSubShapeID2, float inFraction, Vec3Arg inContactPointA, Vec3Arg inContactPointB, Vec3Arg inContactNormal) +{ + // Check if we have enabled active edge detection + Vec3 contact_normal = inContactNormal; + if (mShapeCastSettings.mActiveEdgeMode == EActiveEdgeMode::CollideOnlyWithActive && inActiveEdges != 0b111) + { + // Convert the active edge velocity hint to local space + Vec3 active_edge_movement_direction = mCenterOfMassTransform2.Multiply3x3Transposed(mShapeCastSettings.mActiveEdgeMovementDirection); + + // Update the contact normal to account for active edges + // Note that we flip the triangle normal as the penetration axis is pointing towards the triangle instead of away + contact_normal = ActiveEdges::FixNormal(inV0, inV1, inV2, inBackFacing? inTriangleNormal : -inTriangleNormal, inActiveEdges, inContactPointB, inContactNormal, active_edge_movement_direction); + } + + AddHit(inBackFacing, inSubShapeID2, inFraction, inContactPointA, inContactPointB, contact_normal); +} + +// This is a simplified version of the ray cylinder test from: Real Time Collision Detection - Christer Ericson +// Chapter 5.3.7, page 194-197. Some conditions have been removed as we're not interested in hitting the caps of the cylinder. +// Note that the ray origin is assumed to be the origin here. +float CastSphereVsTriangles::RayCylinder(Vec3Arg inRayDirection, Vec3Arg inCylinderA, Vec3Arg inCylinderB, float inRadius) const +{ + // Calculate cylinder axis + Vec3 axis = inCylinderB - inCylinderA; + + // Make ray start relative to cylinder side A (moving cylinder A to the origin) + Vec3 start = -inCylinderA; + + // Test if segment is fully on the A side of the cylinder + float start_dot_axis = start.Dot(axis); + float direction_dot_axis = inRayDirection.Dot(axis); + float end_dot_axis = start_dot_axis + direction_dot_axis; + if (start_dot_axis < 0.0f && end_dot_axis < 0.0f) + return FLT_MAX; + + // Test if segment is fully on the B side of the cylinder + float axis_len_sq = axis.LengthSq(); + if (start_dot_axis > axis_len_sq && end_dot_axis > axis_len_sq) + return FLT_MAX; + + // Calculate a, b and c, the factors for quadratic equation + // We're basically solving the ray: x = start + direction * t + // The closest point to x on the segment A B is: w = (x . axis) * axis / (axis . axis) + // The distance between x and w should be radius: (x - w) . (x - w) = radius^2 + // Solving this gives the following: + float a = axis_len_sq * inRayDirection.LengthSq() - Square(direction_dot_axis); + if (abs(a) < 1.0e-6f) + return FLT_MAX; // Segment runs parallel to cylinder axis, stop processing, we will either hit at fraction = 0 or we'll hit a vertex + float b = axis_len_sq * start.Dot(inRayDirection) - direction_dot_axis * start_dot_axis; // should be multiplied by 2, instead we'll divide a and c by 2 when we solve the quadratic equation + float c = axis_len_sq * (start.LengthSq() - Square(inRadius)) - Square(start_dot_axis); + float det = Square(b) - a * c; // normally 4 * a * c but since both a and c need to be divided by 2 we lose the 4 + if (det < 0.0f) + return FLT_MAX; // No solution to quadractic equation + + // Solve fraction t where the ray hits the cylinder + float t = -(b + sqrt(det)) / a; // normally divided by 2 * a but since a should be divided by 2 we lose the 2 + if (t < 0.0f || t > 1.0f) + return FLT_MAX; // Intersection lies outside segment + if (start_dot_axis + t * direction_dot_axis < 0.0f || start_dot_axis + t * direction_dot_axis > axis_len_sq) + return FLT_MAX; // Intersection outside the end point of the cylinder, stop processing, we will possibly hit a vertex + return t; +} + +void CastSphereVsTriangles::Cast(Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2, uint8 inActiveEdges, const SubShapeID &inSubShapeID2) +{ + JPH_PROFILE_FUNCTION(); + + // Scale triangle and make it relative to the start of the cast + Vec3 v0 = mScale * inV0 - mStart; + Vec3 v1 = mScale * inV1 - mStart; + Vec3 v2 = mScale * inV2 - mStart; + + // Calculate triangle normal + Vec3 triangle_normal = mScaleSign * (v1 - v0).Cross(v2 - v0); + float triangle_normal_len = triangle_normal.Length(); + if (triangle_normal_len == 0.0f) + return; // Degenerate triangle + triangle_normal /= triangle_normal_len; + + // Backface check + float normal_dot_direction = triangle_normal.Dot(mDirection); + bool back_facing = normal_dot_direction > 0.0f; + if (mShapeCastSettings.mBackFaceModeTriangles == EBackFaceMode::IgnoreBackFaces && back_facing) + return; + + // Test if distance between the sphere and plane of triangle is smaller or equal than the radius + if (abs(v0.Dot(triangle_normal)) <= mRadius) + { + // Check if the sphere intersects at the start of the cast + uint32 closest_feature; + Vec3 q = ClosestPoint::GetClosestPointOnTriangle(v0, v1, v2, closest_feature); + float q_len_sq = q.LengthSq(); + if (q_len_sq <= Square(mRadius)) + { + // Early out if this hit is deeper than the collector's early out value + float q_len = sqrt(q_len_sq); + float penetration_depth = mRadius - q_len; + if (-penetration_depth >= mCollector.GetEarlyOutFraction()) + return; + + // Generate contact point + Vec3 contact_normal = q_len > 0.0f? q / q_len : Vec3::sAxisY(); + Vec3 contact_point_a = q + contact_normal * penetration_depth; + Vec3 contact_point_b = q; + AddHitWithActiveEdgeDetection(v0, v1, v2, back_facing, triangle_normal, inActiveEdges, inSubShapeID2, 0.0f, contact_point_a, contact_point_b, contact_normal); + return; + } + } + else + { + // Check if cast is not parallel to the plane of the triangle + float abs_normal_dot_direction = abs(normal_dot_direction); + if (abs_normal_dot_direction > 1.0e-6f) + { + // Calculate the point on the sphere that will hit the triangle's plane first and calculate a fraction where it will do so + Vec3 d = Sign(normal_dot_direction) * mRadius * triangle_normal; + float plane_intersection = (v0 - d).Dot(triangle_normal) / normal_dot_direction; + + // Check if sphere will hit in the interval that we're interested in + if (plane_intersection * abs_normal_dot_direction < -mRadius // Sphere hits the plane before the sweep, cannot intersect + || plane_intersection >= mCollector.GetEarlyOutFraction()) // Sphere hits the plane after the sweep / early out fraction, cannot intersect + return; + + // We can only report an interior hit if we're hitting the plane during our sweep and not before + if (plane_intersection >= 0.0f) + { + // Calculate the point of contact on the plane + Vec3 p = d + plane_intersection * mDirection; + + // Check if this is an interior point + float u, v, w; + if (ClosestPoint::GetBaryCentricCoordinates(v0 - p, v1 - p, v2 - p, u, v, w) + && u >= 0.0f && v >= 0.0f && w >= 0.0f) + { + // Interior point, we found the collision point. We don't need to check active edges. + AddHit(back_facing, inSubShapeID2, plane_intersection, p, p, back_facing? triangle_normal : -triangle_normal); + return; + } + } + } + } + + // Test 3 edges + float fraction = RayCylinder(mDirection, v0, v1, mRadius); + fraction = min(fraction, RayCylinder(mDirection, v1, v2, mRadius)); + fraction = min(fraction, RayCylinder(mDirection, v2, v0, mRadius)); + + // Test 3 vertices + fraction = min(fraction, RaySphere(Vec3::sZero(), mDirection, v0, mRadius)); + fraction = min(fraction, RaySphere(Vec3::sZero(), mDirection, v1, mRadius)); + fraction = min(fraction, RaySphere(Vec3::sZero(), mDirection, v2, mRadius)); + + // Check if we have a collision + JPH_ASSERT(fraction >= 0.0f); + if (fraction < mCollector.GetEarlyOutFraction()) + { + // Calculate the center of the sphere at the point of contact + Vec3 p = fraction * mDirection; + + // Get contact point and normal + uint32 closest_feature; + Vec3 q = ClosestPoint::GetClosestPointOnTriangle(v0 - p, v1 - p, v2 - p, closest_feature); + Vec3 contact_normal = q.Normalized(); + Vec3 contact_point_ab = p + q; + AddHitWithActiveEdgeDetection(v0, v1, v2, back_facing, triangle_normal, inActiveEdges, inSubShapeID2, fraction, contact_point_ab, contact_point_ab, contact_normal); + } +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CastSphereVsTriangles.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CastSphereVsTriangles.h new file mode 100644 index 00000000000..624e44e5132 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CastSphereVsTriangles.h @@ -0,0 +1,49 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +/// Collision detection helper that casts a sphere vs one or more triangles +class JPH_EXPORT CastSphereVsTriangles +{ +public: + /// Constructor + /// @param inShapeCast The sphere to cast against the triangles and its start and direction + /// @param inShapeCastSettings Settings for performing the cast + /// @param inScale Local space scale for the shape to cast against. + /// @param inCenterOfMassTransform2 Is the center of mass transform of shape 2 (excluding scale), this is used to provide a transform to the shape cast result so that local quantities can be transformed into world space. + /// @param inSubShapeIDCreator1 Class that tracks the current sub shape ID for the casting shape + /// @param ioCollector The collector that receives the results. + CastSphereVsTriangles(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, Vec3Arg inScale, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, CastShapeCollector &ioCollector); + + /// Cast sphere with a single triangle + /// @param inV0 , inV1 , inV2: CCW triangle vertices + /// @param inActiveEdges bit 0 = edge v0..v1 is active, bit 1 = edge v1..v2 is active, bit 2 = edge v2..v0 is active + /// An active edge is an edge that is not connected to another triangle in such a way that it is impossible to collide with the edge + /// @param inSubShapeID2 The sub shape ID for the triangle + void Cast(Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2, uint8 inActiveEdges, const SubShapeID &inSubShapeID2); + +protected: + Vec3 mStart; ///< Starting location of the sphere + Vec3 mDirection; ///< Direction and length of movement of sphere + float mRadius; ///< Scaled radius of sphere + const ShapeCastSettings & mShapeCastSettings; + const Mat44 & mCenterOfMassTransform2; + Vec3 mScale; + SubShapeIDCreator mSubShapeIDCreator1; + CastShapeCollector & mCollector; + +private: + void AddHit(bool inBackFacing, const SubShapeID &inSubShapeID2, float inFraction, Vec3Arg inContactPointA, Vec3Arg inContactPointB, Vec3Arg inContactNormal); + void AddHitWithActiveEdgeDetection(Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2, bool inBackFacing, Vec3Arg inTriangleNormal, uint8 inActiveEdges, const SubShapeID &inSubShapeID2, float inFraction, Vec3Arg inContactPointA, Vec3Arg inContactPointB, Vec3Arg inContactNormal); + float RayCylinder(Vec3Arg inRayDirection, Vec3Arg inCylinderA, Vec3Arg inCylinderB, float inRadius) const; + + float mScaleSign; ///< Sign of the scale, -1 if object is inside out, 1 if not +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollectFacesMode.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollectFacesMode.h new file mode 100644 index 00000000000..4cac6256db1 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollectFacesMode.h @@ -0,0 +1,16 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +JPH_NAMESPACE_BEGIN + +/// Whether or not to collect faces, used by CastShape and CollideShape +enum class ECollectFacesMode : uint8 +{ + CollectFaces, ///< mShape1/2Face is desired + NoFaces ///< mShape1/2Face is not desired +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollideConvexVsTriangles.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollideConvexVsTriangles.cpp new file mode 100644 index 00000000000..f00b0023aa9 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollideConvexVsTriangles.cpp @@ -0,0 +1,150 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +CollideConvexVsTriangles::CollideConvexVsTriangles(const ConvexShape *inShape1, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeID &inSubShapeID1, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector) : + mCollideShapeSettings(inCollideShapeSettings), + mCollector(ioCollector), + mShape1(inShape1), + mScale1(inScale1), + mScale2(inScale2), + mTransform1(inCenterOfMassTransform1), + mSubShapeID1(inSubShapeID1) +{ + // Get transforms + Mat44 inverse_transform2 = inCenterOfMassTransform2.InversedRotationTranslation(); + Mat44 transform1_to_2 = inverse_transform2 * inCenterOfMassTransform1; + mTransform2To1 = transform1_to_2.InversedRotationTranslation(); + + // Calculate bounds + mBoundsOf1 = inShape1->GetLocalBounds().Scaled(inScale1); + mBoundsOf1.ExpandBy(Vec3::sReplicate(inCollideShapeSettings.mMaxSeparationDistance)); + mBoundsOf1InSpaceOf2 = mBoundsOf1.Transformed(transform1_to_2); // Convert bounding box of 1 into space of 2 + + // Determine if shape 2 is inside out or not + mScaleSign2 = ScaleHelpers::IsInsideOut(inScale2)? -1.0f : 1.0f; +} + +void CollideConvexVsTriangles::Collide(Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2, uint8 inActiveEdges, const SubShapeID &inSubShapeID2) +{ + JPH_PROFILE_FUNCTION(); + + // Scale triangle and transform it to the space of 1 + Vec3 v0 = mTransform2To1 * (mScale2 * inV0); + Vec3 v1 = mTransform2To1 * (mScale2 * inV1); + Vec3 v2 = mTransform2To1 * (mScale2 * inV2); + + // Calculate triangle normal + Vec3 triangle_normal = mScaleSign2 * (v1 - v0).Cross(v2 - v0); + + // Backface check + bool back_facing = triangle_normal.Dot(v0) > 0.0f; + if (mCollideShapeSettings.mBackFaceMode == EBackFaceMode::IgnoreBackFaces && back_facing) + return; + + // Get bounding box for triangle + AABox triangle_bbox = AABox::sFromTwoPoints(v0, v1); + triangle_bbox.Encapsulate(v2); + + // Get intersection between triangle and shape box, if there is none, we're done + if (!triangle_bbox.Overlaps(mBoundsOf1)) + return; + + // Create triangle support function + TriangleConvexSupport triangle(v0, v1, v2); + + // Perform collision detection + // Note: As we don't remember the penetration axis from the last iteration, and it is likely that the shape (A) we're colliding the triangle (B) against is in front of the triangle, + // and the penetration axis is the shortest distance along to push B out of collision, we use the inverse of the triangle normal as an initial penetration axis. This has been seen + // to improve performance by approx. 5% over using a fixed axis like (1, 0, 0). + Vec3 penetration_axis = -triangle_normal, point1, point2; + EPAPenetrationDepth pen_depth; + EPAPenetrationDepth::EStatus status; + + // Get the support function + if (mShape1ExCvxRadius == nullptr) + mShape1ExCvxRadius = mShape1->GetSupportFunction(ConvexShape::ESupportMode::ExcludeConvexRadius, mBufferExCvxRadius, mScale1); + + // Perform GJK step + status = pen_depth.GetPenetrationDepthStepGJK(*mShape1ExCvxRadius, mShape1ExCvxRadius->GetConvexRadius() + mCollideShapeSettings.mMaxSeparationDistance, triangle, 0.0f, mCollideShapeSettings.mCollisionTolerance, penetration_axis, point1, point2); + + // Check result of collision detection + if (status == EPAPenetrationDepth::EStatus::NotColliding) + return; + else if (status == EPAPenetrationDepth::EStatus::Indeterminate) + { + // Need to run expensive EPA algorithm + + // Get the support function + if (mShape1IncCvxRadius == nullptr) + mShape1IncCvxRadius = mShape1->GetSupportFunction(ConvexShape::ESupportMode::IncludeConvexRadius, mBufferIncCvxRadius, mScale1); + + // Add convex radius + AddConvexRadius shape1_add_max_separation_distance(*mShape1IncCvxRadius, mCollideShapeSettings.mMaxSeparationDistance); + + // Perform EPA step + if (!pen_depth.GetPenetrationDepthStepEPA(shape1_add_max_separation_distance, triangle, mCollideShapeSettings.mPenetrationTolerance, penetration_axis, point1, point2)) + return; + } + + // Check if the penetration is bigger than the early out fraction + float penetration_depth = (point2 - point1).Length() - mCollideShapeSettings.mMaxSeparationDistance; + if (-penetration_depth >= mCollector.GetEarlyOutFraction()) + return; + + // Correct point1 for the added separation distance + float penetration_axis_len = penetration_axis.Length(); + if (penetration_axis_len > 0.0f) + point1 -= penetration_axis * (mCollideShapeSettings.mMaxSeparationDistance / penetration_axis_len); + + // Check if we have enabled active edge detection + if (mCollideShapeSettings.mActiveEdgeMode == EActiveEdgeMode::CollideOnlyWithActive && inActiveEdges != 0b111) + { + // Convert the active edge velocity hint to local space + Vec3 active_edge_movement_direction = mTransform1.Multiply3x3Transposed(mCollideShapeSettings.mActiveEdgeMovementDirection); + + // Update the penetration axis to account for active edges + // Note that we flip the triangle normal as the penetration axis is pointing towards the triangle instead of away + penetration_axis = ActiveEdges::FixNormal(v0, v1, v2, back_facing? triangle_normal : -triangle_normal, inActiveEdges, point2, penetration_axis, active_edge_movement_direction); + } + + // Convert to world space + point1 = mTransform1 * point1; + point2 = mTransform1 * point2; + Vec3 penetration_axis_world = mTransform1.Multiply3x3(penetration_axis); + + // Create collision result + CollideShapeResult result(point1, point2, penetration_axis_world, penetration_depth, mSubShapeID1, inSubShapeID2, TransformedShape::sGetBodyID(mCollector.GetContext())); + + // Gather faces + if (mCollideShapeSettings.mCollectFacesMode == ECollectFacesMode::CollectFaces) + { + // Get supporting face of shape 1 + mShape1->GetSupportingFace(SubShapeID(), -penetration_axis, mScale1, mTransform1, result.mShape1Face); + + // Get face of the triangle + result.mShape2Face.resize(3); + result.mShape2Face[0] = mTransform1 * v0; + result.mShape2Face[1] = mTransform1 * v1; + result.mShape2Face[2] = mTransform1 * v2; + } + + // Notify the collector + JPH_IF_TRACK_NARROWPHASE_STATS(TrackNarrowPhaseCollector track;) + mCollector.AddHit(result); +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollideConvexVsTriangles.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollideConvexVsTriangles.h new file mode 100644 index 00000000000..65cff73931f --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollideConvexVsTriangles.h @@ -0,0 +1,56 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +class CollideShapeSettings; + +/// Collision detection helper that collides a convex object vs one or more triangles +class JPH_EXPORT CollideConvexVsTriangles +{ +public: + /// Constructor + /// @param inShape1 The convex shape to collide against triangles + /// @param inScale1 Local space scale for the convex object + /// @param inScale2 Local space scale for the triangles + /// @param inCenterOfMassTransform1 Transform that takes the center of mass of 1 into world space + /// @param inCenterOfMassTransform2 Transform that takes the center of mass of 2 into world space + /// @param inSubShapeID1 Sub shape ID of the convex object + /// @param inCollideShapeSettings Settings for the collide shape query + /// @param ioCollector The collector that will receive the results + CollideConvexVsTriangles(const ConvexShape *inShape1, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeID &inSubShapeID1, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector); + + /// Collide convex object with a single triangle + /// @param inV0 , inV1 , inV2: CCW triangle vertices + /// @param inActiveEdges bit 0 = edge v0..v1 is active, bit 1 = edge v1..v2 is active, bit 2 = edge v2..v0 is active + /// An active edge is an edge that is not connected to another triangle in such a way that it is impossible to collide with the edge + /// @param inSubShapeID2 The sub shape ID for the triangle + void Collide(Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2, uint8 inActiveEdges, const SubShapeID &inSubShapeID2); + +protected: + const CollideShapeSettings & mCollideShapeSettings; ///< Settings for this collision operation + CollideShapeCollector & mCollector; ///< The collector that will receive the results + const ConvexShape * mShape1; ///< The shape that we're colliding with + Vec3 mScale1; ///< The scale of the shape (in shape local space) of the shape we're colliding with + Vec3 mScale2; ///< The scale of the shape (in shape local space) of the shape we're colliding against + Mat44 mTransform1; ///< Transform of the shape we're colliding with + Mat44 mTransform2To1; ///< Transform that takes a point in space of the colliding shape to the shape we're colliding with + AABox mBoundsOf1; ///< Bounds of the colliding shape in local space + AABox mBoundsOf1InSpaceOf2; ///< Bounds of the colliding shape in space of shape we're colliding with + SubShapeID mSubShapeID1; ///< Sub shape ID of colliding shape + float mScaleSign2; ///< Sign of the scale of object 2, -1 if object is inside out, 1 if not + ConvexShape::SupportBuffer mBufferExCvxRadius; ///< Buffer that holds the support function data excluding convex radius + ConvexShape::SupportBuffer mBufferIncCvxRadius; ///< Buffer that holds the support function data including convex radius + const ConvexShape::Support * mShape1ExCvxRadius = nullptr; ///< Actual support function object excluding convex radius + const ConvexShape::Support * mShape1IncCvxRadius = nullptr; ///< Actual support function object including convex radius +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollidePointResult.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollidePointResult.h new file mode 100644 index 00000000000..8601b3c40a0 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollidePointResult.h @@ -0,0 +1,25 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +JPH_NAMESPACE_BEGIN + +/// Structure that holds the result of colliding a point against a shape +class CollidePointResult +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Function required by the CollisionCollector. A smaller fraction is considered to be a 'better hit'. For point queries there is no sensible return value. + inline float GetEarlyOutFraction() const { return 0.0f; } + + BodyID mBodyID; ///< Body that was hit + SubShapeID mSubShapeID2; ///< Sub shape ID of shape that we collided against +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollideShape.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollideShape.h new file mode 100644 index 00000000000..2c5f8f217ff --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollideShape.h @@ -0,0 +1,105 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +/// Class that contains all information of two colliding shapes +class CollideShapeResult +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Default constructor + CollideShapeResult() = default; + + /// Constructor + CollideShapeResult(Vec3Arg inContactPointOn1, Vec3Arg inContactPointOn2, Vec3Arg inPenetrationAxis, float inPenetrationDepth, const SubShapeID &inSubShapeID1, const SubShapeID &inSubShapeID2, const BodyID &inBodyID2) : + mContactPointOn1(inContactPointOn1), + mContactPointOn2(inContactPointOn2), + mPenetrationAxis(inPenetrationAxis), + mPenetrationDepth(inPenetrationDepth), + mSubShapeID1(inSubShapeID1), + mSubShapeID2(inSubShapeID2), + mBodyID2(inBodyID2) + { + } + + /// Function required by the CollisionCollector. A smaller fraction is considered to be a 'better hit'. We use -penetration depth to get the hit with the biggest penetration depth + inline float GetEarlyOutFraction() const { return -mPenetrationDepth; } + + /// Reverses the hit result, swapping contact point 1 with contact point 2 etc. + inline CollideShapeResult Reversed() const + { + CollideShapeResult result; + result.mContactPointOn2 = mContactPointOn1; + result.mContactPointOn1 = mContactPointOn2; + result.mPenetrationAxis = -mPenetrationAxis; + result.mPenetrationDepth = mPenetrationDepth; + result.mSubShapeID2 = mSubShapeID1; + result.mSubShapeID1 = mSubShapeID2; + result.mBodyID2 = mBodyID2; + result.mShape2Face = mShape1Face; + result.mShape1Face = mShape2Face; + return result; + } + + using Face = StaticArray; + + Vec3 mContactPointOn1; ///< Contact point on the surface of shape 1 (in world space or relative to base offset) + Vec3 mContactPointOn2; ///< Contact point on the surface of shape 2 (in world space or relative to base offset). If the penetration depth is 0, this will be the same as mContactPointOn1. + Vec3 mPenetrationAxis; ///< Direction to move shape 2 out of collision along the shortest path (magnitude is meaningless, in world space). You can use -mPenetrationAxis.Normalized() as contact normal. + float mPenetrationDepth; ///< Penetration depth (move shape 2 by this distance to resolve the collision) + SubShapeID mSubShapeID1; ///< Sub shape ID that identifies the face on shape 1 + SubShapeID mSubShapeID2; ///< Sub shape ID that identifies the face on shape 2 + BodyID mBodyID2; ///< BodyID to which shape 2 belongs to + Face mShape1Face; ///< Colliding face on shape 1 (optional result, in world space or relative to base offset) + Face mShape2Face; ///< Colliding face on shape 2 (optional result, in world space or relative to base offset) +}; + +/// Settings to be passed with a collision query +class CollideSettingsBase +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// How active edges (edges that a moving object should bump into) are handled + EActiveEdgeMode mActiveEdgeMode = EActiveEdgeMode::CollideOnlyWithActive; + + /// If colliding faces should be collected or only the collision point + ECollectFacesMode mCollectFacesMode = ECollectFacesMode::NoFaces; + + /// If objects are closer than this distance, they are considered to be colliding (used for GJK) (unit: meter) + float mCollisionTolerance = cDefaultCollisionTolerance; + + /// A factor that determines the accuracy of the penetration depth calculation. If the change of the squared distance is less than tolerance * current_penetration_depth^2 the algorithm will terminate. (unit: dimensionless) + float mPenetrationTolerance = cDefaultPenetrationTolerance; + + /// When mActiveEdgeMode is CollideOnlyWithActive a movement direction can be provided. When hitting an inactive edge, the system will select the triangle normal as penetration depth only if it impedes the movement less than with the calculated penetration depth. + Vec3 mActiveEdgeMovementDirection = Vec3::sZero(); +}; + +/// Settings to be passed with a collision query +class CollideShapeSettings : public CollideSettingsBase +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// When > 0 contacts in the vicinity of the query shape can be found. All nearest contacts that are not further away than this distance will be found (unit: meter) + float mMaxSeparationDistance = 0.0f; + + /// How backfacing triangles should be treated + EBackFaceMode mBackFaceMode = EBackFaceMode::IgnoreBackFaces; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollideSoftBodyVerticesVsTriangles.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollideSoftBodyVerticesVsTriangles.h new file mode 100644 index 00000000000..4385e15cddf --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollideSoftBodyVerticesVsTriangles.h @@ -0,0 +1,98 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2024 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +JPH_NAMESPACE_BEGIN + +/// Collision detection helper that collides soft body vertices vs triangles +class JPH_EXPORT CollideSoftBodyVerticesVsTriangles +{ +public: + CollideSoftBodyVerticesVsTriangles(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale) : + mTransform(inCenterOfMassTransform * Mat44::sScale(inScale)), + mInvTransform(mTransform.Inversed()), + mNormalSign(ScaleHelpers::IsInsideOut(inScale)? -1.0f : 1.0f) + { + } + + JPH_INLINE void StartVertex(const SoftBodyVertex &inVertex) + { + mLocalPosition = mInvTransform * inVertex.mPosition; + mClosestDistanceSq = FLT_MAX; + } + + JPH_INLINE void ProcessTriangle(Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2) + { + // Get the closest point from the vertex to the triangle + uint32 set; + Vec3 closest_point = ClosestPoint::GetClosestPointOnTriangle(inV0 - mLocalPosition, inV1 - mLocalPosition, inV2 - mLocalPosition, set); + float dist_sq = closest_point.LengthSq(); + if (dist_sq < mClosestDistanceSq) + { + mV0 = inV0; + mV1 = inV1; + mV2 = inV2; + mClosestPoint = closest_point; + mClosestDistanceSq = dist_sq; + mSet = set; + } + } + + JPH_INLINE void FinishVertex(SoftBodyVertex &ioVertex, int inCollidingShapeIndex) const + { + if (mClosestDistanceSq < FLT_MAX) + { + // Convert triangle to world space + Vec3 v0 = mTransform * mV0; + Vec3 v1 = mTransform * mV1; + Vec3 v2 = mTransform * mV2; + Vec3 triangle_normal = mNormalSign * (v1 - v0).Cross(v2 - v0).NormalizedOr(Vec3::sAxisY()); + + if (mSet == 0b111) + { + // Closest is interior to the triangle, use plane as collision plane but don't allow more than 0.1 m penetration + // because otherwise a triangle half a level a way will have a huge penetration if it is back facing + float penetration = min(triangle_normal.Dot(v0 - ioVertex.mPosition), 0.1f); + if (penetration > ioVertex.mLargestPenetration) + { + ioVertex.mLargestPenetration = penetration; + ioVertex.mCollisionPlane = Plane::sFromPointAndNormal(v0, triangle_normal); + ioVertex.mCollidingShapeIndex = inCollidingShapeIndex; + } + } + else + { + // Closest point is on an edge or vertex, use closest point as collision plane + Vec3 closest_point = mTransform * (mLocalPosition + mClosestPoint); + Vec3 normal = ioVertex.mPosition - closest_point; + if (normal.Dot(triangle_normal) > 0.0f) // Ignore back facing edges + { + float normal_length = normal.Length(); + float penetration = -normal_length; + if (penetration > ioVertex.mLargestPenetration) + { + ioVertex.mLargestPenetration = penetration; + ioVertex.mCollisionPlane = Plane::sFromPointAndNormal(closest_point, normal_length > 0.0f? normal / normal_length : triangle_normal); + ioVertex.mCollidingShapeIndex = inCollidingShapeIndex; + } + } + } + } + } + + Mat44 mTransform; + Mat44 mInvTransform; + Vec3 mLocalPosition; + Vec3 mV0, mV1, mV2; + Vec3 mClosestPoint; + float mNormalSign; + float mClosestDistanceSq; + uint32 mSet; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollideSphereVsTriangles.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollideSphereVsTriangles.cpp new file mode 100644 index 00000000000..92ed28a7b49 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollideSphereVsTriangles.cpp @@ -0,0 +1,123 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +static constexpr uint8 sClosestFeatureToActiveEdgesMask[] = { + 0b000, // 0b000: Invalid, guarded by an assert + 0b101, // 0b001: Vertex 1 -> edge 1 or 3 + 0b011, // 0b010: Vertex 2 -> edge 1 or 2 + 0b001, // 0b011: Vertex 1 & 2 -> edge 1 + 0b110, // 0b100: Vertex 3 -> edge 2 or 3 + 0b100, // 0b101: Vertex 1 & 3 -> edge 3 + 0b010, // 0b110: Vertex 2 & 3 -> edge 2 + // 0b111: Vertex 1, 2 & 3 -> interior, guarded by an if +}; + +CollideSphereVsTriangles::CollideSphereVsTriangles(const SphereShape *inShape1, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeID &inSubShapeID1, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector) : + mCollideShapeSettings(inCollideShapeSettings), + mCollector(ioCollector), + mShape1(inShape1), + mScale2(inScale2), + mTransform2(inCenterOfMassTransform2), + mSubShapeID1(inSubShapeID1) +{ + // Calculate the center of the sphere in the space of 2 + mSphereCenterIn2 = inCenterOfMassTransform2.Multiply3x3Transposed(inCenterOfMassTransform1.GetTranslation() - inCenterOfMassTransform2.GetTranslation()); + + // Determine if shape 2 is inside out or not + mScaleSign2 = ScaleHelpers::IsInsideOut(inScale2)? -1.0f : 1.0f; + + // Check that the sphere is uniformly scaled + JPH_ASSERT(ScaleHelpers::IsUniformScale(inScale1.Abs())); + mRadius = abs(inScale1.GetX()) * inShape1->GetRadius(); + mRadiusPlusMaxSeparationSq = Square(mRadius + inCollideShapeSettings.mMaxSeparationDistance); +} + +void CollideSphereVsTriangles::Collide(Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2, uint8 inActiveEdges, const SubShapeID &inSubShapeID2) +{ + JPH_PROFILE_FUNCTION(); + + // Scale triangle and make it relative to the center of the sphere + Vec3 v0 = mScale2 * inV0 - mSphereCenterIn2; + Vec3 v1 = mScale2 * inV1 - mSphereCenterIn2; + Vec3 v2 = mScale2 * inV2 - mSphereCenterIn2; + + // Calculate triangle normal + Vec3 triangle_normal = mScaleSign2 * (v1 - v0).Cross(v2 - v0); + + // Backface check + bool back_facing = triangle_normal.Dot(v0) > 0.0f; + if (mCollideShapeSettings.mBackFaceMode == EBackFaceMode::IgnoreBackFaces && back_facing) + return; + + // Check if we collide with the sphere + uint32 closest_feature; + Vec3 point2 = ClosestPoint::GetClosestPointOnTriangle(v0, v1, v2, closest_feature); + float point2_len_sq = point2.LengthSq(); + if (point2_len_sq > mRadiusPlusMaxSeparationSq) + return; + + // Calculate penetration depth + float penetration_depth = mRadius - sqrt(point2_len_sq); + if (-penetration_depth >= mCollector.GetEarlyOutFraction()) + return; + + // Calculate penetration axis, direction along which to push 2 to move it out of collision (this is always away from the sphere center) + Vec3 penetration_axis = point2.NormalizedOr(Vec3::sAxisY()); + + // Calculate the point on the sphere + Vec3 point1 = mRadius * penetration_axis; + + // Check if we have enabled active edge detection + JPH_ASSERT(closest_feature != 0); + if (mCollideShapeSettings.mActiveEdgeMode == EActiveEdgeMode::CollideOnlyWithActive + && closest_feature != 0b111 // For an interior hit we should already have the right normal + && (inActiveEdges & sClosestFeatureToActiveEdgesMask[closest_feature]) == 0) // If we didn't hit an active edge we should take the triangle normal + { + // Convert the active edge velocity hint to local space + Vec3 active_edge_movement_direction = mTransform2.Multiply3x3Transposed(mCollideShapeSettings.mActiveEdgeMovementDirection); + + // See ActiveEdges::FixNormal. If penetration_axis affects the movement less than the triangle normal we keep penetration_axis. + Vec3 new_penetration_axis = back_facing? triangle_normal : -triangle_normal; + if (active_edge_movement_direction.Dot(penetration_axis) * new_penetration_axis.Length() >= active_edge_movement_direction.Dot(new_penetration_axis)) + penetration_axis = new_penetration_axis; + } + + // Convert to world space + point1 = mTransform2 * (mSphereCenterIn2 + point1); + point2 = mTransform2 * (mSphereCenterIn2 + point2); + Vec3 penetration_axis_world = mTransform2.Multiply3x3(penetration_axis); + + // Create collision result + CollideShapeResult result(point1, point2, penetration_axis_world, penetration_depth, mSubShapeID1, inSubShapeID2, TransformedShape::sGetBodyID(mCollector.GetContext())); + + // Gather faces + if (mCollideShapeSettings.mCollectFacesMode == ECollectFacesMode::CollectFaces) + { + // The sphere doesn't have a supporting face + + // Get face of triangle 2 + result.mShape2Face.resize(3); + result.mShape2Face[0] = mTransform2 * (mSphereCenterIn2 + v0); + result.mShape2Face[1] = mTransform2 * (mSphereCenterIn2 + v1); + result.mShape2Face[2] = mTransform2 * (mSphereCenterIn2 + v2); + } + + // Notify the collector + JPH_IF_TRACK_NARROWPHASE_STATS(TrackNarrowPhaseCollector track;) + mCollector.AddHit(result); +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollideSphereVsTriangles.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollideSphereVsTriangles.h new file mode 100644 index 00000000000..13175f4b31c --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollideSphereVsTriangles.h @@ -0,0 +1,50 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +class CollideShapeSettings; + +/// Collision detection helper that collides a sphere vs one or more triangles +class JPH_EXPORT CollideSphereVsTriangles +{ +public: + /// Constructor + /// @param inShape1 The sphere to collide against triangles + /// @param inScale1 Local space scale for the sphere + /// @param inScale2 Local space scale for the triangles + /// @param inCenterOfMassTransform1 Transform that takes the center of mass of 1 into world space + /// @param inCenterOfMassTransform2 Transform that takes the center of mass of 2 into world space + /// @param inSubShapeID1 Sub shape ID of the convex object + /// @param inCollideShapeSettings Settings for the collide shape query + /// @param ioCollector The collector that will receive the results + CollideSphereVsTriangles(const SphereShape *inShape1, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeID &inSubShapeID1, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector); + + /// Collide sphere with a single triangle + /// @param inV0 , inV1 , inV2: CCW triangle vertices + /// @param inActiveEdges bit 0 = edge v0..v1 is active, bit 1 = edge v1..v2 is active, bit 2 = edge v2..v0 is active + /// An active edge is an edge that is not connected to another triangle in such a way that it is impossible to collide with the edge + /// @param inSubShapeID2 The sub shape ID for the triangle + void Collide(Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2, uint8 inActiveEdges, const SubShapeID &inSubShapeID2); + +protected: + const CollideShapeSettings & mCollideShapeSettings; ///< Settings for this collision operation + CollideShapeCollector & mCollector; ///< The collector that will receive the results + const SphereShape * mShape1; ///< The shape that we're colliding with + Vec3 mScale2; ///< The scale of the shape (in shape local space) of the shape we're colliding against + Mat44 mTransform2; ///< Transform of the shape we're colliding against + Vec3 mSphereCenterIn2; ///< The center of the sphere in the space of 2 + SubShapeID mSubShapeID1; ///< Sub shape ID of colliding shape + float mScaleSign2; ///< Sign of the scale of object 2, -1 if object is inside out, 1 if not + float mRadius; ///< Radius of the sphere + float mRadiusPlusMaxSeparationSq; ///< (Radius + Max SeparationDistance)^2 +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollisionCollector.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollisionCollector.h new file mode 100644 index 00000000000..274e8f03284 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollisionCollector.h @@ -0,0 +1,102 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +JPH_NAMESPACE_BEGIN + +class Body; +class TransformedShape; + +/// Traits to use for CastRay +class CollisionCollectorTraitsCastRay +{ +public: + /// For rays the early out fraction is the fraction along the line to order hits. + static constexpr float InitialEarlyOutFraction = 1.0f + FLT_EPSILON; ///< Furthest hit: Fraction is 1 + epsilon + static constexpr float ShouldEarlyOutFraction = 0.0f; ///< Closest hit: Fraction is 0 +}; + +/// Traits to use for CastShape +class CollisionCollectorTraitsCastShape +{ +public: + /// For rays the early out fraction is the fraction along the line to order hits. + static constexpr float InitialEarlyOutFraction = 1.0f + FLT_EPSILON; ///< Furthest hit: Fraction is 1 + epsilon + static constexpr float ShouldEarlyOutFraction = -FLT_MAX; ///< Deepest hit: Penetration is infinite +}; + +/// Traits to use for CollideShape +class CollisionCollectorTraitsCollideShape +{ +public: + /// For shape collisions we use -penetration depth to order hits. + static constexpr float InitialEarlyOutFraction = FLT_MAX; ///< Most shallow hit: Separation is infinite + static constexpr float ShouldEarlyOutFraction = -FLT_MAX; ///< Deepest hit: Penetration is infinite +}; + +/// Traits to use for CollidePoint +using CollisionCollectorTraitsCollidePoint = CollisionCollectorTraitsCollideShape; + +/// Virtual interface that allows collecting multiple collision results +template +class CollisionCollector +{ +public: + /// Declare ResultType so that derived classes can use it + using ResultType = ResultTypeArg; + + /// Default constructor + CollisionCollector() = default; + + /// Constructor to initialize from another collector + template + explicit CollisionCollector(const CollisionCollector &inRHS) : mEarlyOutFraction(inRHS.GetEarlyOutFraction()), mContext(inRHS.GetContext()) { } + CollisionCollector(const CollisionCollector &inRHS) = default; + + /// Destructor + virtual ~CollisionCollector() = default; + + /// If you want to reuse this collector, call Reset() + virtual void Reset() { mEarlyOutFraction = TraitsType::InitialEarlyOutFraction; } + + /// When running a query through the NarrowPhaseQuery class, this will be called for every body that is potentially colliding. + /// It allows collecting additional information needed by the collision collector implementation from the body under lock protection + /// before AddHit is called (e.g. the user data pointer or the velocity of the body). + virtual void OnBody([[maybe_unused]] const Body &inBody) { /* Collects nothing by default */ } + + /// Set by the collision detection functions to the current TransformedShape that we're colliding against before calling the AddHit function + void SetContext(const TransformedShape *inContext) { mContext = inContext; } + const TransformedShape *GetContext() const { return mContext; } + + /// This function will be called for every hit found, it's up to the application to decide how to store the hit + virtual void AddHit(const ResultType &inResult) = 0; + + /// Update the early out fraction (should be lower than before) + inline void UpdateEarlyOutFraction(float inFraction) { JPH_ASSERT(inFraction <= mEarlyOutFraction); mEarlyOutFraction = inFraction; } + + /// Reset the early out fraction to a specific value + inline void ResetEarlyOutFraction(float inFraction = TraitsType::InitialEarlyOutFraction) { mEarlyOutFraction = inFraction; } + + /// Force the collision detection algorithm to terminate as soon as possible. Call this from the AddHit function when a satisfying hit is found. + inline void ForceEarlyOut() { mEarlyOutFraction = TraitsType::ShouldEarlyOutFraction; } + + /// When true, the collector will no longer accept any additional hits and the collision detection routine should early out as soon as possible + inline bool ShouldEarlyOut() const { return mEarlyOutFraction <= TraitsType::ShouldEarlyOutFraction; } + + /// Get the current early out value + inline float GetEarlyOutFraction() const { return mEarlyOutFraction; } + + /// Get the current early out value but make sure it's bigger than zero, this is used for shape casting as negative values are used for penetration + inline float GetPositiveEarlyOutFraction() const { return max(FLT_MIN, mEarlyOutFraction); } + +private: + /// The early out fraction determines the fraction below which the collector is still accepting a hit (can be used to reduce the amount of work) + float mEarlyOutFraction = TraitsType::InitialEarlyOutFraction; + + /// Set by the collision detection functions to the current TransformedShape of the body that we're colliding against before calling the AddHit function + const TransformedShape *mContext = nullptr; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollisionCollectorImpl.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollisionCollectorImpl.h new file mode 100644 index 00000000000..7bdd5da2797 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollisionCollectorImpl.h @@ -0,0 +1,134 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +JPH_NAMESPACE_BEGIN + +/// Simple implementation that collects all hits and optionally sorts them on distance +template +class AllHitCollisionCollector : public CollectorType +{ +public: + /// Redeclare ResultType + using ResultType = typename CollectorType::ResultType; + + // See: CollectorType::Reset + virtual void Reset() override + { + CollectorType::Reset(); + + mHits.clear(); + } + + // See: CollectorType::AddHit + virtual void AddHit(const ResultType &inResult) override + { + mHits.push_back(inResult); + } + + /// Order hits on closest first + void Sort() + { + QuickSort(mHits.begin(), mHits.end(), [](const ResultType &inLHS, const ResultType &inRHS) { return inLHS.GetEarlyOutFraction() < inRHS.GetEarlyOutFraction(); }); + } + + /// Check if any hits were collected + inline bool HadHit() const + { + return !mHits.empty(); + } + + Array mHits; +}; + +/// Simple implementation that collects the closest / deepest hit +template +class ClosestHitCollisionCollector : public CollectorType +{ +public: + /// Redeclare ResultType + using ResultType = typename CollectorType::ResultType; + + // See: CollectorType::Reset + virtual void Reset() override + { + CollectorType::Reset(); + + mHadHit = false; + } + + // See: CollectorType::AddHit + virtual void AddHit(const ResultType &inResult) override + { + float early_out = inResult.GetEarlyOutFraction(); + if (!mHadHit || early_out < mHit.GetEarlyOutFraction()) + { + // Update early out fraction + CollectorType::UpdateEarlyOutFraction(early_out); + + // Store hit + mHit = inResult; + mHadHit = true; + } + } + + /// Check if this collector has had a hit + inline bool HadHit() const + { + return mHadHit; + } + + ResultType mHit; + +private: + bool mHadHit = false; +}; + +/// Simple implementation that collects any hit +template +class AnyHitCollisionCollector : public CollectorType +{ +public: + /// Redeclare ResultType + using ResultType = typename CollectorType::ResultType; + + // See: CollectorType::Reset + virtual void Reset() override + { + CollectorType::Reset(); + + mHadHit = false; + } + + // See: CollectorType::AddHit + virtual void AddHit(const ResultType &inResult) override + { + // Test that the collector is not collecting more hits after forcing an early out + JPH_ASSERT(!mHadHit); + + // Abort any further testing + CollectorType::ForceEarlyOut(); + + // Store hit + mHit = inResult; + mHadHit = true; + } + + /// Check if this collector has had a hit + inline bool HadHit() const + { + return mHadHit; + } + + ResultType mHit; + +private: + bool mHadHit = false; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollisionDispatch.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollisionDispatch.cpp new file mode 100644 index 00000000000..259a9526e8b --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollisionDispatch.cpp @@ -0,0 +1,107 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include + +JPH_NAMESPACE_BEGIN + +CollisionDispatch::CollideShape CollisionDispatch::sCollideShape[NumSubShapeTypes][NumSubShapeTypes]; +CollisionDispatch::CastShape CollisionDispatch::sCastShape[NumSubShapeTypes][NumSubShapeTypes]; + +void CollisionDispatch::sInit() +{ + for (uint i = 0; i < NumSubShapeTypes; ++i) + for (uint j = 0; j < NumSubShapeTypes; ++j) + { + if (sCollideShape[i][j] == nullptr) + sCollideShape[i][j] = [](const Shape *, const Shape *, Vec3Arg, Vec3Arg, Mat44Arg, Mat44Arg, const SubShapeIDCreator &, const SubShapeIDCreator &, const CollideShapeSettings &, CollideShapeCollector &, const ShapeFilter &) + { + JPH_ASSERT(false, "Unsupported shape pair"); + }; + + if (sCastShape[i][j] == nullptr) + sCastShape[i][j] = [](const ShapeCast &, const ShapeCastSettings &, const Shape *, Vec3Arg, const ShapeFilter &, Mat44Arg, const SubShapeIDCreator &, const SubShapeIDCreator &, CastShapeCollector &) + { + JPH_ASSERT(false, "Unsupported shape pair"); + }; + } +} + +void CollisionDispatch::sReversedCollideShape(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter) +{ + // A collision collector that flips the collision results + class ReversedCollector : public CollideShapeCollector + { + public: + explicit ReversedCollector(CollideShapeCollector &ioCollector) : + CollideShapeCollector(ioCollector), + mCollector(ioCollector) + { + } + + virtual void AddHit(const CollideShapeResult &inResult) override + { + // Add the reversed hit + mCollector.AddHit(inResult.Reversed()); + + // If our chained collector updated its early out fraction, we need to follow + UpdateEarlyOutFraction(mCollector.GetEarlyOutFraction()); + } + + private: + CollideShapeCollector & mCollector; + }; + + ReversedShapeFilter shape_filter(inShapeFilter); + ReversedCollector collector(ioCollector); + sCollideShapeVsShape(inShape2, inShape1, inScale2, inScale1, inCenterOfMassTransform2, inCenterOfMassTransform1, inSubShapeIDCreator2, inSubShapeIDCreator1, inCollideShapeSettings, collector, shape_filter); +} + +void CollisionDispatch::sReversedCastShape(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector) +{ + // A collision collector that flips the collision results + class ReversedCollector : public CastShapeCollector + { + public: + explicit ReversedCollector(CastShapeCollector &ioCollector, Vec3Arg inWorldDirection) : + CastShapeCollector(ioCollector), + mCollector(ioCollector), + mWorldDirection(inWorldDirection) + { + } + + virtual void AddHit(const ShapeCastResult &inResult) override + { + // Add the reversed hit + mCollector.AddHit(inResult.Reversed(mWorldDirection)); + + // If our chained collector updated its early out fraction, we need to follow + UpdateEarlyOutFraction(mCollector.GetEarlyOutFraction()); + } + + private: + CastShapeCollector & mCollector; + Vec3 mWorldDirection; + }; + + // Reverse the shape cast (shape cast is in local space to shape 2) + Mat44 com_start_inv = inShapeCast.mCenterOfMassStart.InversedRotationTranslation(); + ShapeCast local_shape_cast(inShape, inScale, com_start_inv, -com_start_inv.Multiply3x3(inShapeCast.mDirection)); + + // Calculate the center of mass of shape 1 at start of sweep + Mat44 shape1_com = inCenterOfMassTransform2 * inShapeCast.mCenterOfMassStart; + + // Calculate the world space direction vector of the shape cast + Vec3 world_direction = -inCenterOfMassTransform2.Multiply3x3(inShapeCast.mDirection); + + // Forward the cast + ReversedShapeFilter shape_filter(inShapeFilter); + ReversedCollector collector(ioCollector, world_direction); + sCastShapeVsShapeLocalSpace(local_shape_cast, inShapeCastSettings, inShapeCast.mShape, inShapeCast.mScale, shape_filter, shape1_com, inSubShapeIDCreator2, inSubShapeIDCreator1, collector); +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollisionDispatch.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollisionDispatch.h new file mode 100644 index 00000000000..2b8f5ad457e --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollisionDispatch.h @@ -0,0 +1,97 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +class CollideShapeSettings; + +/// Dispatch function, main function to handle collisions between shapes +class JPH_EXPORT CollisionDispatch +{ +public: + /// Collide 2 shapes and pass any collision on to ioCollector + /// @param inShape1 The first shape + /// @param inShape2 The second shape + /// @param inScale1 Local space scale of shape 1 + /// @param inScale2 Local space scale of shape 2 + /// @param inCenterOfMassTransform1 Transform to transform center of mass of shape 1 into world space + /// @param inCenterOfMassTransform2 Transform to transform center of mass of shape 2 into world space + /// @param inSubShapeIDCreator1 Class that tracks the current sub shape ID for shape 1 + /// @param inSubShapeIDCreator2 Class that tracks the current sub shape ID for shape 2 + /// @param inCollideShapeSettings Options for the CollideShape test + /// @param ioCollector The collector that receives the results. + /// @param inShapeFilter allows selectively disabling collisions between pairs of (sub) shapes. + static inline void sCollideShapeVsShape(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter = { }) + { + JPH_IF_TRACK_NARROWPHASE_STATS(TrackNarrowPhaseStat track(NarrowPhaseStat::sCollideShape[(int)inShape1->GetSubType()][(int)inShape2->GetSubType()]);) + + // Only test shape if it passes the shape filter + if (inShapeFilter.ShouldCollide(inShape1, inSubShapeIDCreator1.GetID(), inShape2, inSubShapeIDCreator2.GetID())) + sCollideShape[(int)inShape1->GetSubType()][(int)inShape2->GetSubType()](inShape1, inShape2, inScale1, inScale2, inCenterOfMassTransform1, inCenterOfMassTransform2, inSubShapeIDCreator1, inSubShapeIDCreator2, inCollideShapeSettings, ioCollector, inShapeFilter); + } + + /// Cast a shape against this shape, passes any hits found to ioCollector. + /// Note: This version takes the shape cast in local space relative to the center of mass of inShape, take a look at sCastShapeVsShapeWorldSpace if you have a shape cast in world space. + /// @param inShapeCastLocal The shape to cast against the other shape and its start and direction. + /// @param inShapeCastSettings Settings for performing the cast + /// @param inShape The shape to cast against. + /// @param inScale Local space scale for the shape to cast against. + /// @param inShapeFilter allows selectively disabling collisions between pairs of (sub) shapes. + /// @param inCenterOfMassTransform2 Is the center of mass transform of shape 2 (excluding scale), this is used to provide a transform to the shape cast result so that local hit result quantities can be transformed into world space. + /// @param inSubShapeIDCreator1 Class that tracks the current sub shape ID for the casting shape + /// @param inSubShapeIDCreator2 Class that tracks the current sub shape ID for the shape we're casting against + /// @param ioCollector The collector that receives the results. + static inline void sCastShapeVsShapeLocalSpace(const ShapeCast &inShapeCastLocal, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector) + { + JPH_IF_TRACK_NARROWPHASE_STATS(TrackNarrowPhaseStat track(NarrowPhaseStat::sCastShape[(int)inShapeCastLocal.mShape->GetSubType()][(int)inShape->GetSubType()]);) + + // Only test shape if it passes the shape filter + if (inShapeFilter.ShouldCollide(inShapeCastLocal.mShape, inSubShapeIDCreator1.GetID(), inShape, inSubShapeIDCreator2.GetID())) + sCastShape[(int)inShapeCastLocal.mShape->GetSubType()][(int)inShape->GetSubType()](inShapeCastLocal, inShapeCastSettings, inShape, inScale, inShapeFilter, inCenterOfMassTransform2, inSubShapeIDCreator1, inSubShapeIDCreator2, ioCollector); + } + + /// See: sCastShapeVsShapeLocalSpace. + /// The only difference is that the shape cast (inShapeCastWorld) is provided in world space. + /// Note: A shape cast contains the center of mass start of the shape, if you have the world transform of the shape you probably want to construct it using ShapeCast::sFromWorldTransform. + static inline void sCastShapeVsShapeWorldSpace(const ShapeCast &inShapeCastWorld, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector) + { + ShapeCast local_shape_cast = inShapeCastWorld.PostTransformed(inCenterOfMassTransform2.InversedRotationTranslation()); + sCastShapeVsShapeLocalSpace(local_shape_cast, inShapeCastSettings, inShape, inScale, inShapeFilter, inCenterOfMassTransform2, inSubShapeIDCreator1, inSubShapeIDCreator2, ioCollector); + } + + /// Function that collides 2 shapes (see sCollideShapeVsShape) + using CollideShape = void (*)(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter); + + /// Function that casts a shape vs another shape (see sCastShapeVsShapeLocalSpace) + using CastShape = void (*)(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector); + + /// Initialize all collision functions with a function that asserts and returns no collision + static void sInit(); + + /// Register a collide shape function in the collision table + static void sRegisterCollideShape(EShapeSubType inType1, EShapeSubType inType2, CollideShape inFunction) { sCollideShape[(int)inType1][(int)inType2] = inFunction; } + + /// Register a cast shape function in the collision table + static void sRegisterCastShape(EShapeSubType inType1, EShapeSubType inType2, CastShape inFunction) { sCastShape[(int)inType1][(int)inType2] = inFunction; } + + /// An implementation of CollideShape that swaps inShape1 and inShape2 and swaps the result back, can be registered if the collision function only exists the other way around + static void sReversedCollideShape(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter); + + /// An implementation of CastShape that swaps inShape1 and inShape2 and swaps the result back, can be registered if the collision function only exists the other way around + static void sReversedCastShape(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector); + +private: + static CollideShape sCollideShape[NumSubShapeTypes][NumSubShapeTypes]; + static CastShape sCastShape[NumSubShapeTypes][NumSubShapeTypes]; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollisionGroup.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollisionGroup.cpp new file mode 100644 index 00000000000..a8c98b6dc4c --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollisionGroup.cpp @@ -0,0 +1,33 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(CollisionGroup) +{ + JPH_ADD_ATTRIBUTE(CollisionGroup, mGroupFilter) + JPH_ADD_ATTRIBUTE(CollisionGroup, mGroupID) + JPH_ADD_ATTRIBUTE(CollisionGroup, mSubGroupID) +} + +void CollisionGroup::SaveBinaryState(StreamOut &inStream) const +{ + inStream.Write(mGroupID); + inStream.Write(mSubGroupID); +} + +void CollisionGroup::RestoreBinaryState(StreamIn &inStream) +{ + inStream.Read(mGroupID); + inStream.Read(mSubGroupID); +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollisionGroup.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollisionGroup.h new file mode 100644 index 00000000000..266242ff052 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollisionGroup.h @@ -0,0 +1,94 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +JPH_NAMESPACE_BEGIN + +class StreamIn; +class StreamOut; + +/// Two objects collide with each other if: +/// - Both don't have a group filter +/// - The first group filter says that the objects can collide +/// - Or if there's no filter for the first object, the second group filter says the objects can collide +class JPH_EXPORT CollisionGroup +{ +public: + JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, CollisionGroup) + + using GroupID = uint32; + using SubGroupID = uint32; + + static const GroupID cInvalidGroup = ~GroupID(0); + static const SubGroupID cInvalidSubGroup = ~SubGroupID(0); + + /// Default constructor + CollisionGroup() = default; + + /// Construct with all properties + CollisionGroup(const GroupFilter *inFilter, GroupID inGroupID, SubGroupID inSubGroupID) : mGroupFilter(inFilter), mGroupID(inGroupID), mSubGroupID(inSubGroupID) { } + + /// Set the collision group filter + inline void SetGroupFilter(const GroupFilter *inFilter) + { + mGroupFilter = inFilter; + } + + /// Get the collision group filter + inline const GroupFilter *GetGroupFilter() const + { + return mGroupFilter; + } + + /// Set the main group id for this object + inline void SetGroupID(GroupID inID) + { + mGroupID = inID; + } + + inline GroupID GetGroupID() const + { + return mGroupID; + } + + /// Add this object to a sub group + inline void SetSubGroupID(SubGroupID inID) + { + mSubGroupID = inID; + } + + inline SubGroupID GetSubGroupID() const + { + return mSubGroupID; + } + + /// Check if this object collides with another object + bool CanCollide(const CollisionGroup &inOther) const + { + // Call the CanCollide function of the first group filter that's not null + if (mGroupFilter != nullptr) + return mGroupFilter->CanCollide(*this, inOther); + else if (inOther.mGroupFilter != nullptr) + return inOther.mGroupFilter->CanCollide(inOther, *this); + else + return true; + } + + /// Saves the state of this object in binary form to inStream. Does not save group filter. + void SaveBinaryState(StreamOut &inStream) const; + + /// Restore the state of this object from inStream. Does not save group filter. + void RestoreBinaryState(StreamIn &inStream); + +private: + RefConst mGroupFilter; + GroupID mGroupID = cInvalidGroup; + SubGroupID mSubGroupID = cInvalidSubGroup; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ContactListener.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ContactListener.h new file mode 100644 index 00000000000..d347dc47571 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ContactListener.h @@ -0,0 +1,114 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +JPH_NAMESPACE_BEGIN + +class Body; +class CollideShapeResult; + +/// Array of contact points +using ContactPoints = StaticArray; + +/// Manifold class, describes the contact surface between two bodies +class ContactManifold +{ +public: + /// Swaps shape 1 and 2 + ContactManifold SwapShapes() const { return { mBaseOffset, -mWorldSpaceNormal, mPenetrationDepth, mSubShapeID2, mSubShapeID1, mRelativeContactPointsOn2, mRelativeContactPointsOn1 }; } + + /// Access to the world space contact positions + inline RVec3 GetWorldSpaceContactPointOn1(uint inIndex) const { return mBaseOffset + mRelativeContactPointsOn1[inIndex]; } + inline RVec3 GetWorldSpaceContactPointOn2(uint inIndex) const { return mBaseOffset + mRelativeContactPointsOn2[inIndex]; } + + RVec3 mBaseOffset; ///< Offset to which all the contact points are relative + Vec3 mWorldSpaceNormal; ///< Normal for this manifold, direction along which to move body 2 out of collision along the shortest path + float mPenetrationDepth; ///< Penetration depth (move shape 2 by this distance to resolve the collision). If this value is negative, this is a speculative contact point and may not actually result in a velocity change as during solving the bodies may not actually collide. + SubShapeID mSubShapeID1; ///< Sub shapes that formed this manifold (note that when multiple manifolds are combined because they're coplanar, we lose some information here because we only keep track of one sub shape pair that we encounter, see description at Body::SetUseManifoldReduction) + SubShapeID mSubShapeID2; + ContactPoints mRelativeContactPointsOn1; ///< Contact points on the surface of shape 1 relative to mBaseOffset. + ContactPoints mRelativeContactPointsOn2; ///< Contact points on the surface of shape 2 relative to mBaseOffset. If there's no penetration, this will be the same as mRelativeContactPointsOn1. If there is penetration they will be different. +}; + +/// When a contact point is added or persisted, the callback gets a chance to override certain properties of the contact constraint. +/// The values are filled in with their defaults by the system so the callback doesn't need to modify anything, but it can if it wants to. +class ContactSettings +{ +public: + float mCombinedFriction; ///< Combined friction for the body pair (see: PhysicsSystem::SetCombineFriction) + float mCombinedRestitution; ///< Combined restitution for the body pair (see: PhysicsSystem::SetCombineRestitution) + float mInvMassScale1 = 1.0f; ///< Scale factor for the inverse mass of body 1 (0 = infinite mass, 1 = use original mass, 2 = body has half the mass). For the same contact pair, you should strive to keep the value the same over time. + float mInvInertiaScale1 = 1.0f; ///< Scale factor for the inverse inertia of body 1 (usually same as mInvMassScale1) + float mInvMassScale2 = 1.0f; ///< Scale factor for the inverse mass of body 2 (0 = infinite mass, 1 = use original mass, 2 = body has half the mass). For the same contact pair, you should strive to keep the value the same over time. + float mInvInertiaScale2 = 1.0f; ///< Scale factor for the inverse inertia of body 2 (usually same as mInvMassScale2) + bool mIsSensor; ///< If the contact should be treated as a sensor vs body contact (no collision response) + Vec3 mRelativeLinearSurfaceVelocity = Vec3::sZero(); ///< Relative linear surface velocity between the bodies (world space surface velocity of body 2 - world space surface velocity of body 1), can be used to create a conveyor belt effect + Vec3 mRelativeAngularSurfaceVelocity = Vec3::sZero(); ///< Relative angular surface velocity between the bodies (world space angular surface velocity of body 2 - world space angular surface velocity of body 1). Note that this angular velocity is relative to the center of mass of body 1, so if you want it relative to body 2's center of mass you need to add body 2 angular velocity x (body 1 world space center of mass - body 2 world space center of mass) to mRelativeLinearSurfaceVelocity. +}; + +/// Return value for the OnContactValidate callback. Determines if the contact is being processed or not. +/// Results are ordered so that the strongest accept has the lowest number and the strongest reject the highest number (which allows for easy combining of results) +enum class ValidateResult +{ + AcceptAllContactsForThisBodyPair, ///< Accept this and any further contact points for this body pair + AcceptContact, ///< Accept this contact only (and continue calling this callback for every contact manifold for the same body pair) + RejectContact, ///< Reject this contact only (but process any other contact manifolds for the same body pair) + RejectAllContactsForThisBodyPair ///< Rejects this and any further contact points for this body pair +}; + +/// A listener class that receives collision contact events. +/// It can be registered with the ContactConstraintManager (or PhysicsSystem). +/// Note that contact listener callbacks are called from multiple threads at the same time when all bodies are locked, you're only allowed to read from the bodies and you can't change physics state. +class ContactListener +{ +public: + /// Ensure virtual destructor + virtual ~ContactListener() = default; + + /// Called after detecting a collision between a body pair, but before calling OnContactAdded and before adding the contact constraint. + /// If the function rejects the contact, the contact will not be added and any other contacts between this body pair will not be processed. + /// This function will only be called once per PhysicsSystem::Update per body pair and may not be called again the next update + /// if a contact persists and no new contact pairs between sub shapes are found. + /// This is a rather expensive time to reject a contact point since a lot of the collision detection has happened already, make sure you + /// filter out the majority of undesired body pairs through the ObjectLayerPairFilter that is registered on the PhysicsSystem. + /// Note that this callback is called when all bodies are locked, so don't use any locking functions! + /// Body 1 will have a motion type that is larger or equal than body 2's motion type (order from large to small: dynamic -> kinematic -> static). When motion types are equal, they are ordered by BodyID. + /// The collision result (inCollisionResult) is reported relative to inBaseOffset. + virtual ValidateResult OnContactValidate([[maybe_unused]] const Body &inBody1, [[maybe_unused]] const Body &inBody2, [[maybe_unused]] RVec3Arg inBaseOffset, [[maybe_unused]] const CollideShapeResult &inCollisionResult) { return ValidateResult::AcceptAllContactsForThisBodyPair; } + + /// Called whenever a new contact point is detected. + /// Note that this callback is called when all bodies are locked, so don't use any locking functions! + /// Body 1 and 2 will be sorted such that body 1 ID < body 2 ID, so body 1 may not be dynamic. + /// Note that only active bodies will report contacts, as soon as a body goes to sleep the contacts between that body and all other + /// bodies will receive an OnContactRemoved callback, if this is the case then Body::IsActive() will return false during the callback. + /// When contacts are added, the constraint solver has not run yet, so the collision impulse is unknown at that point. + /// The velocities of inBody1 and inBody2 are the velocities before the contact has been resolved, so you can use this to + /// estimate the collision impulse to e.g. determine the volume of the impact sound to play (see: EstimateCollisionResponse). + virtual void OnContactAdded([[maybe_unused]] const Body &inBody1, [[maybe_unused]] const Body &inBody2, [[maybe_unused]] const ContactManifold &inManifold, [[maybe_unused]] ContactSettings &ioSettings) { /* Do nothing */ } + + /// Called whenever a contact is detected that was also detected last update. + /// Note that this callback is called when all bodies are locked, so don't use any locking functions! + /// Body 1 and 2 will be sorted such that body 1 ID < body 2 ID, so body 1 may not be dynamic. + /// If the structure of the shape of a body changes between simulation steps (e.g. by adding/removing a child shape of a compound shape), + /// it is possible that the same sub shape ID used to identify the removed child shape is now reused for a different child shape. The physics + /// system cannot detect this, so may send a 'contact persisted' callback even though the contact is now on a different child shape. You can + /// detect this by keeping the old shape (before adding/removing a part) around until the next PhysicsSystem::Update (when the OnContactPersisted + /// callbacks are triggered) and resolving the sub shape ID against both the old and new shape to see if they still refer to the same child shape. + virtual void OnContactPersisted([[maybe_unused]] const Body &inBody1, [[maybe_unused]] const Body &inBody2, [[maybe_unused]] const ContactManifold &inManifold, [[maybe_unused]] ContactSettings &ioSettings) { /* Do nothing */ } + + /// Called whenever a contact was detected last update but is not detected anymore. + /// Note that this callback is called when all bodies are locked, so don't use any locking functions! + /// Note that we're using BodyID's since the bodies may have been removed at the time of callback. + /// Body 1 and 2 will be sorted such that body 1 ID < body 2 ID, so body 1 may not be dynamic. + /// The sub shape ID were created in the previous simulation step too, so if the structure of a shape changes (e.g. by adding/removing a child shape of a compound shape), + /// the sub shape ID may not be valid / may not point to the same sub shape anymore. + /// If you want to know if this is the last contact between the two bodies, use PhysicsSystem::WereBodiesInContact. + virtual void OnContactRemoved([[maybe_unused]] const SubShapeIDPair &inSubShapePair) { /* Do nothing */ } +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/EstimateCollisionResponse.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/EstimateCollisionResponse.cpp new file mode 100644 index 00000000000..53cf12b5d41 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/EstimateCollisionResponse.cpp @@ -0,0 +1,213 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include + +JPH_NAMESPACE_BEGIN + +void EstimateCollisionResponse(const Body &inBody1, const Body &inBody2, const ContactManifold &inManifold, CollisionEstimationResult &outResult, float inCombinedFriction, float inCombinedRestitution, float inMinVelocityForRestitution, uint inNumIterations) +{ + // Note this code is based on AxisConstraintPart, see that class for more comments on the math + + ContactPoints::size_type num_points = inManifold.mRelativeContactPointsOn1.size(); + JPH_ASSERT(num_points == inManifold.mRelativeContactPointsOn2.size()); + + // Start with zero impulses + outResult.mImpulses.resize(num_points); + memset(outResult.mImpulses.data(), 0, num_points * sizeof(CollisionEstimationResult::Impulse)); + + // Calculate friction directions + outResult.mTangent1 = inManifold.mWorldSpaceNormal.GetNormalizedPerpendicular(); + outResult.mTangent2 = inManifold.mWorldSpaceNormal.Cross(outResult.mTangent1); + + // Get body velocities + EMotionType motion_type1 = inBody1.GetMotionType(); + const MotionProperties *motion_properties1 = inBody1.GetMotionPropertiesUnchecked(); + if (motion_type1 != EMotionType::Static) + { + outResult.mLinearVelocity1 = motion_properties1->GetLinearVelocity(); + outResult.mAngularVelocity1 = motion_properties1->GetAngularVelocity(); + } + else + outResult.mLinearVelocity1 = outResult.mAngularVelocity1 = Vec3::sZero(); + + EMotionType motion_type2 = inBody2.GetMotionType(); + const MotionProperties *motion_properties2 = inBody2.GetMotionPropertiesUnchecked(); + if (motion_type2 != EMotionType::Static) + { + outResult.mLinearVelocity2 = motion_properties2->GetLinearVelocity(); + outResult.mAngularVelocity2 = motion_properties2->GetAngularVelocity(); + } + else + outResult.mLinearVelocity2 = outResult.mAngularVelocity2 = Vec3::sZero(); + + // Get inverse mass and inertia + float inv_m1, inv_m2; + Mat44 inv_i1, inv_i2; + if (motion_type1 == EMotionType::Dynamic) + { + inv_m1 = motion_properties1->GetInverseMass(); + inv_i1 = inBody1.GetInverseInertia(); + } + else + { + inv_m1 = 0.0f; + inv_i1 = Mat44::sZero(); + } + + if (motion_type2 == EMotionType::Dynamic) + { + inv_m2 = motion_properties2->GetInverseMass(); + inv_i2 = inBody2.GetInverseInertia(); + } + else + { + inv_m2 = 0.0f; + inv_i2 = Mat44::sZero(); + } + + // Get center of masses relative to the base offset + Vec3 com1 = Vec3(inBody1.GetCenterOfMassPosition() - inManifold.mBaseOffset); + Vec3 com2 = Vec3(inBody2.GetCenterOfMassPosition() - inManifold.mBaseOffset); + + struct AxisConstraint + { + inline void Initialize(Vec3Arg inR1, Vec3Arg inR2, Vec3Arg inWorldSpaceNormal, float inInvM1, float inInvM2, Mat44Arg inInvI1, Mat44Arg inInvI2) + { + // Calculate effective mass: K^-1 = (J M^-1 J^T)^-1 + mR1PlusUxAxis = inR1.Cross(inWorldSpaceNormal); + mR2xAxis = inR2.Cross(inWorldSpaceNormal); + mInvI1_R1PlusUxAxis = inInvI1.Multiply3x3(mR1PlusUxAxis); + mInvI2_R2xAxis = inInvI2.Multiply3x3(mR2xAxis); + mEffectiveMass = 1.0f / (inInvM1 + mInvI1_R1PlusUxAxis.Dot(mR1PlusUxAxis) + inInvM2 + mInvI2_R2xAxis.Dot(mR2xAxis)); + mBias = 0.0f; + } + + inline float SolveGetLambda(Vec3Arg inWorldSpaceNormal, const CollisionEstimationResult &inResult) const + { + // Calculate jacobian multiplied by linear/angular velocity + float jv = inWorldSpaceNormal.Dot(inResult.mLinearVelocity1 - inResult.mLinearVelocity2) + mR1PlusUxAxis.Dot(inResult.mAngularVelocity1) - mR2xAxis.Dot(inResult.mAngularVelocity2); + + // Lagrange multiplier is: + // + // lambda = -K^-1 (J v + b) + return mEffectiveMass * (jv - mBias); + } + + inline void SolveApplyLambda(Vec3Arg inWorldSpaceNormal, float inInvM1, float inInvM2, float inLambda, CollisionEstimationResult &ioResult) const + { + // Apply impulse to body velocities + ioResult.mLinearVelocity1 -= (inLambda * inInvM1) * inWorldSpaceNormal; + ioResult.mAngularVelocity1 -= inLambda * mInvI1_R1PlusUxAxis; + ioResult.mLinearVelocity2 += (inLambda * inInvM2) * inWorldSpaceNormal; + ioResult.mAngularVelocity2 += inLambda * mInvI2_R2xAxis; + } + + inline void Solve(Vec3Arg inWorldSpaceNormal, float inInvM1, float inInvM2, float inMinLambda, float inMaxLambda, float &ioTotalLambda, CollisionEstimationResult &ioResult) const + { + // Calculate new total lambda + float total_lambda = ioTotalLambda + SolveGetLambda(inWorldSpaceNormal, ioResult); + + // Clamp impulse + total_lambda = Clamp(total_lambda, inMinLambda, inMaxLambda); + + SolveApplyLambda(inWorldSpaceNormal, inInvM1, inInvM2, total_lambda - ioTotalLambda, ioResult); + + ioTotalLambda = total_lambda; + } + + Vec3 mR1PlusUxAxis; + Vec3 mR2xAxis; + Vec3 mInvI1_R1PlusUxAxis; + Vec3 mInvI2_R2xAxis; + float mEffectiveMass; + float mBias; + }; + + struct Constraint + { + AxisConstraint mContact; + AxisConstraint mFriction1; + AxisConstraint mFriction2; + }; + + // Initialize the constraint properties + Constraint constraints[ContactPoints::Capacity]; + for (uint c = 0; c < num_points; ++c) + { + Constraint &constraint = constraints[c]; + + // Calculate contact points relative to body 1 and 2 + Vec3 p = 0.5f * (inManifold.mRelativeContactPointsOn1[c] + inManifold.mRelativeContactPointsOn2[c]); + Vec3 r1 = p - com1; + Vec3 r2 = p - com2; + + // Initialize contact constraint + constraint.mContact.Initialize(r1, r2, inManifold.mWorldSpaceNormal, inv_m1, inv_m2, inv_i1, inv_i2); + + // Handle elastic collisions + if (inCombinedRestitution > 0.0f) + { + // Calculate velocity of contact point + Vec3 relative_velocity = outResult.mLinearVelocity2 + outResult.mAngularVelocity2.Cross(r2) - outResult.mLinearVelocity1 - outResult.mAngularVelocity1.Cross(r1); + float normal_velocity = relative_velocity.Dot(inManifold.mWorldSpaceNormal); + + // If it is big enough, apply restitution + if (normal_velocity < -inMinVelocityForRestitution) + constraint.mContact.mBias = inCombinedRestitution * normal_velocity; + } + + if (inCombinedFriction > 0.0f) + { + // Initialize friction constraints + constraint.mFriction1.Initialize(r1, r2, outResult.mTangent1, inv_m1, inv_m2, inv_i1, inv_i2); + constraint.mFriction2.Initialize(r1, r2, outResult.mTangent2, inv_m1, inv_m2, inv_i1, inv_i2); + } + } + + // If there's only 1 contact point, we only need 1 iteration + int num_iterations = inCombinedFriction <= 0.0f && num_points == 1? 1 : inNumIterations; + + // Solve iteratively + for (int iteration = 0; iteration < num_iterations; ++iteration) + { + // Solve friction constraints first + if (inCombinedFriction > 0.0f && iteration > 0) // For first iteration the contact impulse is zero so there's no point in applying friction + for (uint c = 0; c < num_points; ++c) + { + const Constraint &constraint = constraints[c]; + CollisionEstimationResult::Impulse &impulse = outResult.mImpulses[c]; + + float lambda1 = impulse.mFrictionImpulse1 + constraint.mFriction1.SolveGetLambda(outResult.mTangent1, outResult); + float lambda2 = impulse.mFrictionImpulse2 + constraint.mFriction2.SolveGetLambda(outResult.mTangent2, outResult); + + // Calculate max impulse based on contact impulse + float max_impulse = inCombinedFriction * impulse.mContactImpulse; + + // If the total lambda that we will apply is too large, scale it back + float total_lambda_sq = Square(lambda1) + Square(lambda2); + if (total_lambda_sq > Square(max_impulse)) + { + float scale = max_impulse / sqrt(total_lambda_sq); + lambda1 *= scale; + lambda2 *= scale; + } + + constraint.mFriction1.SolveApplyLambda(outResult.mTangent1, inv_m1, inv_m2, lambda1 - impulse.mFrictionImpulse1, outResult); + constraint.mFriction2.SolveApplyLambda(outResult.mTangent2, inv_m1, inv_m2, lambda2 - impulse.mFrictionImpulse2, outResult); + + impulse.mFrictionImpulse1 = lambda1; + impulse.mFrictionImpulse2 = lambda2; + } + + // Solve contact constraints last + for (uint c = 0; c < num_points; ++c) + constraints[c].mContact.Solve(inManifold.mWorldSpaceNormal, inv_m1, inv_m2, 0.0f, FLT_MAX, outResult.mImpulses[c].mContactImpulse, outResult); + } +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/EstimateCollisionResponse.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/EstimateCollisionResponse.h new file mode 100644 index 00000000000..45098d1477e --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/EstimateCollisionResponse.h @@ -0,0 +1,48 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2023 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +/// A structure that contains the estimated contact and friction impulses and the resulting body velocities +struct CollisionEstimationResult +{ + Vec3 mLinearVelocity1; ///< The estimated linear velocity of body 1 after collision + Vec3 mAngularVelocity1; ///< The estimated angular velocity of body 1 after collision + Vec3 mLinearVelocity2; ///< The estimated linear velocity of body 2 after collision + Vec3 mAngularVelocity2; ///< The estimated angular velocity of body 2 after collision + + Vec3 mTangent1; ///< Normalized tangent of contact normal + Vec3 mTangent2; ///< Second normalized tangent of contact normal (forms a basis with mTangent1 and mWorldSpaceNormal) + + struct Impulse + { + float mContactImpulse; ///< Estimated contact impulses (kg m / s) + float mFrictionImpulse1; ///< Estimated friction impulses in the direction of tangent 1 (kg m / s) + float mFrictionImpulse2; ///< Estimated friction impulses in the direction of tangent 2 (kg m / s) + }; + + using Impulses = StaticArray; + + Impulses mImpulses; +}; + +/// This function estimates the contact impulses and body velocity changes as a result of a collision. +/// It can be used in the ContactListener::OnContactAdded to determine the strength of the collision to e.g. play a sound or trigger a particle system. +/// This function is accurate when two bodies collide but will not be accurate when more than 2 bodies collide at the same time as it does not know about these other collisions. +/// +/// @param inBody1 Colliding body 1 +/// @param inBody2 Colliding body 2 +/// @param inManifold The collision manifold +/// @param outResult A structure that contains the estimated contact and friction impulses and the resulting body velocities +/// @param inCombinedFriction The combined friction of body 1 and body 2 (see ContactSettings::mCombinedFriction) +/// @param inCombinedRestitution The combined restitution of body 1 and body 2 (see ContactSettings::mCombinedRestitution) +/// @param inMinVelocityForRestitution Minimal velocity required for restitution to be applied (see PhysicsSettings::mMinVelocityForRestitution) +/// @param inNumIterations Number of iterations to use for the impulse estimation (see PhysicsSettings::mNumVelocitySteps, note you can probably use a lower number for a decent estimate). If you set the number of iterations to 1 then no friction will be calculated. +JPH_EXPORT void EstimateCollisionResponse(const Body &inBody1, const Body &inBody2, const ContactManifold &inManifold, CollisionEstimationResult &outResult, float inCombinedFriction, float inCombinedRestitution, float inMinVelocityForRestitution = 1.0f, uint inNumIterations = 10); + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/GroupFilter.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/GroupFilter.cpp new file mode 100644 index 00000000000..80c7620fdad --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/GroupFilter.cpp @@ -0,0 +1,32 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include + +JPH_NAMESPACE_BEGIN + +JPH_IMPLEMENT_SERIALIZABLE_ABSTRACT_BASE(GroupFilter) +{ + JPH_ADD_BASE_CLASS(GroupFilter, SerializableObject) +} + +void GroupFilter::SaveBinaryState(StreamOut &inStream) const +{ + inStream.Write(GetRTTI()->GetHash()); +} + +void GroupFilter::RestoreBinaryState(StreamIn &inStream) +{ + // RTTI hash is read in sRestoreFromBinaryState +} + +GroupFilter::GroupFilterResult GroupFilter::sRestoreFromBinaryState(StreamIn &inStream) +{ + return StreamUtils::RestoreObject(inStream, &GroupFilter::RestoreBinaryState); +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/GroupFilter.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/GroupFilter.h new file mode 100644 index 00000000000..f0ad835e008 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/GroupFilter.h @@ -0,0 +1,41 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +JPH_NAMESPACE_BEGIN + +class CollisionGroup; +class StreamIn; +class StreamOut; + +/// Abstract class that checks if two CollisionGroups collide +class JPH_EXPORT GroupFilter : public SerializableObject, public RefTarget +{ +public: + JPH_DECLARE_SERIALIZABLE_ABSTRACT(JPH_EXPORT, GroupFilter) + + /// Virtual destructor + virtual ~GroupFilter() override = default; + + /// Check if two groups collide + virtual bool CanCollide(const CollisionGroup &inGroup1, const CollisionGroup &inGroup2) const = 0; + + /// Saves the contents of the group filter in binary form to inStream. + virtual void SaveBinaryState(StreamOut &inStream) const; + + using GroupFilterResult = Result>; + + /// Creates a GroupFilter of the correct type and restores its contents from the binary stream inStream. + static GroupFilterResult sRestoreFromBinaryState(StreamIn &inStream); + +protected: + /// This function should not be called directly, it is used by sRestoreFromBinaryState. + virtual void RestoreBinaryState(StreamIn &inStream); +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/GroupFilterTable.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/GroupFilterTable.cpp new file mode 100644 index 00000000000..dd69d27c852 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/GroupFilterTable.cpp @@ -0,0 +1,38 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(GroupFilterTable) +{ + JPH_ADD_BASE_CLASS(GroupFilterTable, GroupFilter) + + JPH_ADD_ATTRIBUTE(GroupFilterTable, mNumSubGroups) + JPH_ADD_ATTRIBUTE(GroupFilterTable, mTable) +} + +void GroupFilterTable::SaveBinaryState(StreamOut &inStream) const +{ + GroupFilter::SaveBinaryState(inStream); + + inStream.Write(mNumSubGroups); + inStream.Write(mTable); +} + +void GroupFilterTable::RestoreBinaryState(StreamIn &inStream) +{ + GroupFilter::RestoreBinaryState(inStream); + + inStream.Read(mNumSubGroups); + inStream.Read(mTable); +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/GroupFilterTable.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/GroupFilterTable.h new file mode 100644 index 00000000000..62ce34ce55f --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/GroupFilterTable.h @@ -0,0 +1,130 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +JPH_NAMESPACE_BEGIN + +/// Implementation of GroupFilter that stores a bit table with one bit per sub shape ID pair to determine if they collide or not +/// +/// The collision rules: +/// - If one of the objects is in the cInvalidGroup the objects will collide. +/// - If the objects are in different groups they will collide. +/// - If they're in the same group but their collision filter is different they will not collide. +/// - If they're in the same group and their collision filters match, we'll use the SubGroupID and the table below. +/// +/// For N = 6 sub groups the table will look like: +/// +/// sub group 1 ---> +/// sub group 2 x..... +/// | ox.... +/// | oox... +/// V ooox.. +/// oooox. +/// ooooox +/// +/// * 'x' means sub group 1 == sub group 2 and we define this to never collide. +/// * 'o' is a bit that we have to store that defines if the sub groups collide or not. +/// * '.' is a bit we don't need to store because the table is symmetric, we take care that group 2 > group 1 by swapping sub group 1 and sub group 2 if needed. +/// +/// The total number of bits we need to store is (N * (N - 1)) / 2 +class JPH_EXPORT GroupFilterTable final : public GroupFilter +{ + JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, GroupFilterTable) + +private: + using GroupID = CollisionGroup::GroupID; + using SubGroupID = CollisionGroup::SubGroupID; + + /// Get which bit corresponds to the pair (inSubGroup1, inSubGroup2) + int GetBit(SubGroupID inSubGroup1, SubGroupID inSubGroup2) const + { + JPH_ASSERT(inSubGroup1 != inSubGroup2); + + // We store the lower left half only, so swap the inputs when trying to access the top right half + if (inSubGroup1 > inSubGroup2) + swap(inSubGroup1, inSubGroup2); + + JPH_ASSERT(inSubGroup2 < mNumSubGroups); + + // Calculate at which bit the entry for this pair resides + // We use the fact that a row always starts at inSubGroup2 * (inSubGroup2 - 1) / 2 + // (this is the amount of bits needed to store a table of inSubGroup2 entries) + return (inSubGroup2 * (inSubGroup2 - 1)) / 2 + inSubGroup1; + } + +public: + /// Constructs the table with inNumSubGroups subgroups, initially all collision pairs are enabled except when the sub group ID is the same + explicit GroupFilterTable(uint inNumSubGroups = 0) : + mNumSubGroups(inNumSubGroups) + { + // By default everything collides + int table_size = ((inNumSubGroups * (inNumSubGroups - 1)) / 2 + 7) / 8; + mTable.resize(table_size, 0xff); + } + + /// Copy constructor + GroupFilterTable(const GroupFilterTable &inRHS) : mNumSubGroups(inRHS.mNumSubGroups), mTable(inRHS.mTable) { } + + /// Disable collision between two sub groups + void DisableCollision(SubGroupID inSubGroup1, SubGroupID inSubGroup2) + { + int bit = GetBit(inSubGroup1, inSubGroup2); + mTable[bit >> 3] &= (0xff ^ (1 << (bit & 0b111))); + } + + /// Enable collision between two sub groups + void EnableCollision(SubGroupID inSubGroup1, SubGroupID inSubGroup2) + { + int bit = GetBit(inSubGroup1, inSubGroup2); + mTable[bit >> 3] |= 1 << (bit & 0b111); + } + + /// Check if the collision between two subgroups is enabled + inline bool IsCollisionEnabled(SubGroupID inSubGroup1, SubGroupID inSubGroup2) const + { + // Test if the bit is set for this group pair + int bit = GetBit(inSubGroup1, inSubGroup2); + return (mTable[bit >> 3] & (1 << (bit & 0b111))) != 0; + } + + /// Checks if two CollisionGroups collide + virtual bool CanCollide(const CollisionGroup &inGroup1, const CollisionGroup &inGroup2) const override + { + // If one of the groups is cInvalidGroup the objects will collide (note that the if following this if will ensure that group2 is not cInvalidGroup) + if (inGroup1.GetGroupID() == CollisionGroup::cInvalidGroup) + return true; + + // If the objects are in different groups, they collide + if (inGroup1.GetGroupID() != inGroup2.GetGroupID()) + return true; + + // If the collision filters do not match, but they're in the same group we ignore the collision + if (inGroup1.GetGroupFilter() != inGroup2.GetGroupFilter()) + return false; + + // If they are in the same sub group, they don't collide + if (inGroup1.GetSubGroupID() == inGroup2.GetSubGroupID()) + return false; + + // Check the bit table + return IsCollisionEnabled(inGroup1.GetSubGroupID(), inGroup2.GetSubGroupID()); + } + + // See: GroupFilter::SaveBinaryState + virtual void SaveBinaryState(StreamOut &inStream) const override; + +protected: + // See: GroupFilter::RestoreBinaryState + virtual void RestoreBinaryState(StreamIn &inStream) override; + +private: + uint mNumSubGroups; ///< The number of subgroups that this group filter supports + Array mTable; ///< The table of bits that indicates which pairs collide +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/InternalEdgeRemovingCollector.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/InternalEdgeRemovingCollector.h new file mode 100644 index 00000000000..46e37534c72 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/InternalEdgeRemovingCollector.h @@ -0,0 +1,233 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2024 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +//#define JPH_INTERNAL_EDGE_REMOVING_COLLECTOR_DEBUG + +#ifdef JPH_INTERNAL_EDGE_REMOVING_COLLECTOR_DEBUG +#include +#endif // JPH_INTERNAL_EDGE_REMOVING_COLLECTOR_DEBUG + +JPH_NAMESPACE_BEGIN + +/// Removes internal edges from collision results. Can be used to filter out 'ghost collisions'. +/// Based on: Contact generation for meshes - Pierre Terdiman (https://www.codercorner.com/MeshContacts.pdf) +class InternalEdgeRemovingCollector : public CollideShapeCollector +{ + static constexpr uint cMaxDelayedResults = 16; + static constexpr uint cMaxVoidedFeatures = 128; + + /// Check if a vertex is voided + inline bool IsVoided(Vec3 inV) const + { + for (const Float3 &vf : mVoidedFeatures) + if (inV.IsClose(Vec3::sLoadFloat3Unsafe(vf), 1.0e-8f)) + return true; + return false; + } + + /// Add all vertices of a face to the voided features + inline void VoidFeatures(const CollideShapeResult &inResult) + { + for (const Vec3 &v : inResult.mShape2Face) + if (!IsVoided(v)) + { + if (mVoidedFeatures.size() == cMaxVoidedFeatures) + break; + Float3 f; + v.StoreFloat3(&f); + mVoidedFeatures.push_back(f); + } + } + + /// Call the chained collector + inline void Chain(const CollideShapeResult &inResult) + { + // Make sure the chained collector has the same context as we do + mChainedCollector.SetContext(GetContext()); + + // Forward the hit + mChainedCollector.AddHit(inResult); + + // If our chained collector updated its early out fraction, we need to follow + UpdateEarlyOutFraction(mChainedCollector.GetEarlyOutFraction()); + } + + /// Call the chained collector and void all features of inResult + inline void ChainAndVoid(const CollideShapeResult &inResult) + { + Chain(inResult); + VoidFeatures(inResult); + + #ifdef JPH_INTERNAL_EDGE_REMOVING_COLLECTOR_DEBUG + DebugRenderer::sInstance->DrawWirePolygon(RMat44::sIdentity(), inResult.mShape2Face, Color::sGreen); + DebugRenderer::sInstance->DrawArrow(RVec3(inResult.mContactPointOn2), RVec3(inResult.mContactPointOn2) + inResult.mPenetrationAxis.NormalizedOr(Vec3::sZero()), Color::sGreen, 0.1f); + #endif // JPH_INTERNAL_EDGE_REMOVING_COLLECTOR_DEBUG + } + +public: + /// Constructor, configures a collector to be called with all the results that do not hit internal edges + explicit InternalEdgeRemovingCollector(CollideShapeCollector &inChainedCollector) : + mChainedCollector(inChainedCollector) + { + } + + // See: CollideShapeCollector::Reset + virtual void Reset() override + { + CollideShapeCollector::Reset(); + + mChainedCollector.Reset(); + + mVoidedFeatures.clear(); + mDelayedResults.clear(); + } + + // See: CollideShapeCollector::OnBody + virtual void OnBody(const Body &inBody) override + { + // Just forward the call to our chained collector + mChainedCollector.OnBody(inBody); + } + + // See: CollideShapeCollector::AddHit + virtual void AddHit(const CollideShapeResult &inResult) override + { + // We only support welding when the shape is a triangle or has more vertices so that we can calculate a normal + if (inResult.mShape2Face.size() < 3) + return ChainAndVoid(inResult); + + // Get the triangle normal of shape 2 face + Vec3 triangle_normal = (inResult.mShape2Face[1] - inResult.mShape2Face[0]).Cross(inResult.mShape2Face[2] - inResult.mShape2Face[0]); + float triangle_normal_len = triangle_normal.Length(); + if (triangle_normal_len < 1e-6f) + return ChainAndVoid(inResult); + + // If the triangle normal matches the contact normal within 1 degree, we can process the contact immediately + // We make the assumption here that if the contact normal and the triangle normal align that the we're dealing with a 'face contact' + Vec3 contact_normal = -inResult.mPenetrationAxis; + float contact_normal_len = inResult.mPenetrationAxis.Length(); + if (triangle_normal.Dot(contact_normal) > 0.999848f * contact_normal_len * triangle_normal_len) // cos(1 degree) + return ChainAndVoid(inResult); + + // Delayed processing + if (mDelayedResults.size() == cMaxDelayedResults) + return ChainAndVoid(inResult); + mDelayedResults.push_back(inResult); + } + + /// After all hits have been added, call this function to process the delayed results + void Flush() + { + // Sort on biggest penetration depth first + uint sorted_indices[cMaxDelayedResults]; + for (uint i = 0; i < uint(mDelayedResults.size()); ++i) + sorted_indices[i] = i; + QuickSort(sorted_indices, sorted_indices + mDelayedResults.size(), [this](uint inLHS, uint inRHS) { return mDelayedResults[inLHS].mPenetrationDepth > mDelayedResults[inRHS].mPenetrationDepth; }); + + // Loop over all results + for (uint i = 0; i < uint(mDelayedResults.size()); ++i) + { + const CollideShapeResult &r = mDelayedResults[sorted_indices[i]]; + + // Determine which vertex or which edge is the closest to the contact point + float best_dist_sq = FLT_MAX; + uint best_v1_idx = 0; + uint best_v2_idx = 0; + uint num_v = uint(r.mShape2Face.size()); + uint v1_idx = num_v - 1; + Vec3 v1 = r.mShape2Face[v1_idx] - r.mContactPointOn2; + for (uint v2_idx = 0; v2_idx < num_v; ++v2_idx) + { + Vec3 v2 = r.mShape2Face[v2_idx] - r.mContactPointOn2; + Vec3 v1_v2 = v2 - v1; + float denominator = v1_v2.LengthSq(); + if (denominator < Square(FLT_EPSILON)) + { + // Degenerate, assume v1 is closest, v2 will be tested in a later iteration + float v1_len_sq = v1.LengthSq(); + if (v1_len_sq < best_dist_sq) + { + best_dist_sq = v1_len_sq; + best_v1_idx = v1_idx; + best_v2_idx = v1_idx; + } + } + else + { + // Taken from ClosestPoint::GetBaryCentricCoordinates + float fraction = -v1.Dot(v1_v2) / denominator; + if (fraction < 1.0e-6f) + { + // Closest lies on v1 + float v1_len_sq = v1.LengthSq(); + if (v1_len_sq < best_dist_sq) + { + best_dist_sq = v1_len_sq; + best_v1_idx = v1_idx; + best_v2_idx = v1_idx; + } + } + else if (fraction < 1.0f - 1.0e-6f) + { + // Closest lies on the line segment v1, v2 + Vec3 closest = v1 + fraction * v1_v2; + float closest_len_sq = closest.LengthSq(); + if (closest_len_sq < best_dist_sq) + { + best_dist_sq = closest_len_sq; + best_v1_idx = v1_idx; + best_v2_idx = v2_idx; + } + } + // else closest is v2, but v2 will be tested in a later iteration + } + + v1_idx = v2_idx; + v1 = v2; + } + + // Check if this vertex/edge is voided + bool voided = IsVoided(r.mShape2Face[best_v1_idx]) + && (best_v1_idx == best_v2_idx || IsVoided(r.mShape2Face[best_v2_idx])); + + #ifdef JPH_INTERNAL_EDGE_REMOVING_COLLECTOR_DEBUG + Color color = voided? Color::sRed : Color::sYellow; + DebugRenderer::sInstance->DrawText3D(RVec3(r.mContactPointOn2), StringFormat("%d: %g", i, r.mPenetrationDepth), color, 0.1f); + DebugRenderer::sInstance->DrawWirePolygon(RMat44::sIdentity(), r.mShape2Face, color); + DebugRenderer::sInstance->DrawArrow(RVec3(r.mContactPointOn2), RVec3(r.mContactPointOn2) + r.mPenetrationAxis.NormalizedOr(Vec3::sZero()), color, 0.1f); + DebugRenderer::sInstance->DrawMarker(RVec3(r.mShape2Face[best_v1_idx]), IsVoided(r.mShape2Face[best_v1_idx])? Color::sRed : Color::sYellow, 0.1f); + DebugRenderer::sInstance->DrawMarker(RVec3(r.mShape2Face[best_v2_idx]), IsVoided(r.mShape2Face[best_v2_idx])? Color::sRed : Color::sYellow, 0.1f); + #endif // JPH_INTERNAL_EDGE_REMOVING_COLLECTOR_DEBUG + + // No voided features, accept the contact + if (!voided) + Chain(r); + + // Void the features of this face + VoidFeatures(r); + } + } + + /// Version of CollisionDispatch::sCollideShapeVsShape that removes internal edges + static void sCollideShapeVsShape(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter = { }) + { + JPH_ASSERT(inCollideShapeSettings.mCollectFacesMode == ECollectFacesMode::CollectFaces); // Won't work without collecting faces + + InternalEdgeRemovingCollector wrapper(ioCollector); + CollisionDispatch::sCollideShapeVsShape(inShape1, inShape2, inScale1, inScale2, inCenterOfMassTransform1, inCenterOfMassTransform2, inSubShapeIDCreator1, inSubShapeIDCreator2, inCollideShapeSettings, wrapper, inShapeFilter); + wrapper.Flush(); + } + +private: + CollideShapeCollector & mChainedCollector; + StaticArray mVoidedFeatures; // Read with Vec3::sLoadFloat3Unsafe so must not be the last member + StaticArray mDelayedResults; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ManifoldBetweenTwoFaces.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ManifoldBetweenTwoFaces.cpp new file mode 100644 index 00000000000..3331f5d7a03 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ManifoldBetweenTwoFaces.cpp @@ -0,0 +1,237 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#ifdef JPH_DEBUG_RENDERER + #include +#endif // JPH_DEBUG_RENDERER + +JPH_NAMESPACE_BEGIN + +void PruneContactPoints(Vec3Arg inPenetrationAxis, ContactPoints &ioContactPointsOn1, ContactPoints &ioContactPointsOn2 JPH_IF_DEBUG_RENDERER(, RVec3Arg inCenterOfMass)) +{ + // Makes no sense to call this with 4 or less points + JPH_ASSERT(ioContactPointsOn1.size() > 4); + + // Both arrays should have the same size + JPH_ASSERT(ioContactPointsOn1.size() == ioContactPointsOn2.size()); + + // Penetration axis must be normalized + JPH_ASSERT(inPenetrationAxis.IsNormalized()); + + // We use a heuristic of (distance to center of mass) * (penetration depth) to find the contact point that we should keep + // Neither of those two terms should ever become zero, so we clamp against this minimum value + constexpr float cMinDistanceSq = 1.0e-6f; // 1 mm + + ContactPoints projected; + StaticArray penetration_depth_sq; + for (ContactPoints::size_type i = 0; i < ioContactPointsOn1.size(); ++i) + { + // Project contact points on the plane through inCenterOfMass with normal inPenetrationAxis and center around the center of mass of body 1 + // (note that since all points are relative to inCenterOfMass we can project onto the plane through the origin) + Vec3 v1 = ioContactPointsOn1[i]; + projected.push_back(v1 - v1.Dot(inPenetrationAxis) * inPenetrationAxis); + + // Calculate penetration depth^2 of each point and clamp against the minimal distance + Vec3 v2 = ioContactPointsOn2[i]; + penetration_depth_sq.push_back(max(cMinDistanceSq, (v2 - v1).LengthSq())); + } + + // Find the point that is furthest away from the center of mass (its torque will have the biggest influence) + // and the point that has the deepest penetration depth. Use the heuristic (distance to center of mass) * (penetration depth) for this. + uint point1 = 0; + float val = max(cMinDistanceSq, projected[0].LengthSq()) * penetration_depth_sq[0]; + for (uint i = 0; i < projected.size(); ++i) + { + float v = max(cMinDistanceSq, projected[i].LengthSq()) * penetration_depth_sq[i]; + if (v > val) + { + val = v; + point1 = i; + } + } + Vec3 point1v = projected[point1]; + + // Find point furthest from the first point forming a line segment with point1. Again combine this with the heuristic + // for deepest point as per above. + uint point2 = uint(-1); + val = -FLT_MAX; + for (uint i = 0; i < projected.size(); ++i) + if (i != point1) + { + float v = max(cMinDistanceSq, (projected[i] - point1v).LengthSq()) * penetration_depth_sq[i]; + if (v > val) + { + val = v; + point2 = i; + } + } + JPH_ASSERT(point2 != uint(-1)); + Vec3 point2v = projected[point2]; + + // Find furthest points on both sides of the line segment in order to maximize the area + uint point3 = uint(-1); + uint point4 = uint(-1); + float min_val = 0.0f; + float max_val = 0.0f; + Vec3 perp = (point2v - point1v).Cross(inPenetrationAxis); + for (uint i = 0; i < projected.size(); ++i) + if (i != point1 && i != point2) + { + float v = perp.Dot(projected[i] - point1v); + if (v < min_val) + { + min_val = v; + point3 = i; + } + else if (v > max_val) + { + max_val = v; + point4 = i; + } + } + + // Add points to array (in order so they form a polygon) + StaticArray points_to_keep_on_1, points_to_keep_on_2; + points_to_keep_on_1.push_back(ioContactPointsOn1[point1]); + points_to_keep_on_2.push_back(ioContactPointsOn2[point1]); + if (point3 != uint(-1)) + { + points_to_keep_on_1.push_back(ioContactPointsOn1[point3]); + points_to_keep_on_2.push_back(ioContactPointsOn2[point3]); + } + points_to_keep_on_1.push_back(ioContactPointsOn1[point2]); + points_to_keep_on_2.push_back(ioContactPointsOn2[point2]); + if (point4 != uint(-1)) + { + JPH_ASSERT(point3 != point4); + points_to_keep_on_1.push_back(ioContactPointsOn1[point4]); + points_to_keep_on_2.push_back(ioContactPointsOn2[point4]); + } + +#ifdef JPH_DEBUG_RENDERER + if (ContactConstraintManager::sDrawContactPointReduction) + { + // Draw input polygon + DebugRenderer::sInstance->DrawWirePolygon(RMat44::sTranslation(inCenterOfMass), ioContactPointsOn1, Color::sOrange, 0.05f); + + // Draw primary axis + DebugRenderer::sInstance->DrawArrow(inCenterOfMass + ioContactPointsOn1[point1], inCenterOfMass + ioContactPointsOn1[point2], Color::sRed, 0.05f); + + // Draw contact points we kept + for (Vec3 p : points_to_keep_on_1) + DebugRenderer::sInstance->DrawMarker(inCenterOfMass + p, Color::sGreen, 0.1f); + } +#endif // JPH_DEBUG_RENDERER + + // Copy the points back to the input buffer + ioContactPointsOn1 = points_to_keep_on_1; + ioContactPointsOn2 = points_to_keep_on_2; +} + +void ManifoldBetweenTwoFaces(Vec3Arg inContactPoint1, Vec3Arg inContactPoint2, Vec3Arg inPenetrationAxis, float inMaxContactDistanceSq , const ConvexShape::SupportingFace &inShape1Face, const ConvexShape::SupportingFace &inShape2Face, ContactPoints &outContactPoints1, ContactPoints &outContactPoints2 JPH_IF_DEBUG_RENDERER(, RVec3Arg inCenterOfMass)) +{ +#ifdef JPH_DEBUG_RENDERER + if (ContactConstraintManager::sDrawContactPoint) + { + RVec3 cp1 = inCenterOfMass + inContactPoint1; + RVec3 cp2 = inCenterOfMass + inContactPoint2; + + // Draw contact points + DebugRenderer::sInstance->DrawMarker(cp1, Color::sRed, 0.1f); + DebugRenderer::sInstance->DrawMarker(cp2, Color::sGreen, 0.1f); + + // Draw contact normal + DebugRenderer::sInstance->DrawArrow(cp1, cp1 + inPenetrationAxis.Normalized(), Color::sRed, 0.05f); + } +#endif // JPH_DEBUG_RENDERER + + // Remember size before adding new points, to check at the end if we added some + ContactPoints::size_type old_size = outContactPoints1.size(); + + // Check if both shapes have polygon faces + if (inShape1Face.size() >= 2 // The dynamic shape needs to have at least 2 points or else there can never be more than 1 contact point + && inShape2Face.size() >= 3) // The dynamic/static shape needs to have at least 3 points (in the case that it has 2 points only if the edges match exactly you can have 2 contact points, but this situation is unstable anyhow) + { + // Clip the polygon of face 2 against that of 1 + ConvexShape::SupportingFace clipped_face; + if (inShape1Face.size() >= 3) + ClipPolyVsPoly(inShape2Face, inShape1Face, inPenetrationAxis, clipped_face); + else if (inShape1Face.size() == 2) + ClipPolyVsEdge(inShape2Face, inShape1Face[0], inShape1Face[1], inPenetrationAxis, clipped_face); + + // Project the points back onto the plane of shape 1 face and only keep those that are behind the plane + Vec3 plane_origin = inShape1Face[0]; + Vec3 plane_normal; + Vec3 first_edge = inShape1Face[1] - plane_origin; + if (inShape1Face.size() >= 3) + { + // Three vertices, can just calculate the normal + plane_normal = first_edge.Cross(inShape1Face[2] - plane_origin); + } + else + { + // Two vertices, first find a perpendicular to the edge and penetration axis and then use the perpendicular together with the edge to form a normal + plane_normal = first_edge.Cross(inPenetrationAxis).Cross(first_edge); + } + + // Check if the plane normal has any length, if not the clipped shape is so small that we'll just use the contact points + float plane_normal_len_sq = plane_normal.LengthSq(); + if (plane_normal_len_sq > 0.0f) + { + // Discard points of faces that are too far away to collide + for (Vec3 p2 : clipped_face) + { + float distance = (p2 - plane_origin).Dot(plane_normal); // Note should divide by length of plane_normal (unnormalized here) + if (distance <= 0.0f || Square(distance) < inMaxContactDistanceSq * plane_normal_len_sq) // Must be close enough to plane, note we correct for not dividing by plane normal length here + { + // Project point back on shape 1 using the normal, note we correct for not dividing by plane normal length here: + // p1 = p2 - (distance / sqrt(plane_normal_len_sq)) * (plane_normal / sqrt(plane_normal_len_sq)); + Vec3 p1 = p2 - (distance / plane_normal_len_sq) * plane_normal; + + outContactPoints1.push_back(p1); + outContactPoints2.push_back(p2); + } + } + } + + #ifdef JPH_DEBUG_RENDERER + if (ContactConstraintManager::sDrawSupportingFaces) + { + RMat44 com = RMat44::sTranslation(inCenterOfMass); + + // Draw clipped poly + DebugRenderer::sInstance->DrawWirePolygon(com, clipped_face, Color::sOrange); + + // Draw supporting faces + DebugRenderer::sInstance->DrawWirePolygon(com, inShape1Face, Color::sRed, 0.05f); + DebugRenderer::sInstance->DrawWirePolygon(com, inShape2Face, Color::sGreen, 0.05f); + + // Draw normal + if (plane_normal_len_sq > 0.0f) + { + RVec3 plane_origin_ws = inCenterOfMass + plane_origin; + DebugRenderer::sInstance->DrawArrow(plane_origin_ws, plane_origin_ws + plane_normal / sqrt(plane_normal_len_sq), Color::sYellow, 0.05f); + } + + // Draw contact points that remain after distance check + for (ContactPoints::size_type p = old_size; p < outContactPoints1.size(); ++p) + DebugRenderer::sInstance->DrawMarker(inCenterOfMass + outContactPoints1[p], Color::sYellow, 0.1f); + } + #endif // JPH_DEBUG_RENDERER + } + + // If the clipping result is empty, use the contact point itself + if (outContactPoints1.size() == old_size) + { + outContactPoints1.push_back(inContactPoint1); + outContactPoints2.push_back(inContactPoint2); + } +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ManifoldBetweenTwoFaces.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ManifoldBetweenTwoFaces.h new file mode 100644 index 00000000000..72a5b8f0c14 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ManifoldBetweenTwoFaces.h @@ -0,0 +1,44 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +JPH_NAMESPACE_BEGIN + +/// Remove contact points if there are > 4 (no more than 4 are needed for a stable solution) +/// @param inPenetrationAxis is the world space penetration axis (must be normalized) +/// @param ioContactPointsOn1 The contact points on shape 1 relative to inCenterOfMass +/// @param ioContactPointsOn2 The contact points on shape 2 relative to inCenterOfMass +/// On output ioContactPointsOn1/2 are reduced to 4 or less points +#ifdef JPH_DEBUG_RENDERER +/// @param inCenterOfMass Center of mass position of body 1 +#endif +JPH_EXPORT void PruneContactPoints(Vec3Arg inPenetrationAxis, ContactPoints &ioContactPointsOn1, ContactPoints &ioContactPointsOn2 +#ifdef JPH_DEBUG_RENDERER + , RVec3Arg inCenterOfMass +#endif + ); + +/// Determine contact points between 2 faces of 2 shapes and return them in outContactPoints 1 & 2 +/// @param inContactPoint1 The contact point on shape 1 relative to inCenterOfMass +/// @param inContactPoint2 The contact point on shape 2 relative to inCenterOfMass +/// @param inPenetrationAxis The local space penetration axis in world space +/// @param inMaxContactDistanceSq After face 2 is clipped against face 1, each remaining point on face 2 is tested against the plane of face 1. If the distance^2 on the positive side of the plane is larger than this distance, the point will be discarded as a contact point. +/// @param inShape1Face The supporting faces on shape 1 relative to inCenterOfMass +/// @param inShape2Face The supporting faces on shape 2 relative to inCenterOfMass +/// @param outContactPoints1 Returns the contact points between the two shapes for shape 1 relative to inCenterOfMass (any existing points in the output array are left as is) +/// @param outContactPoints2 Returns the contact points between the two shapes for shape 2 relative to inCenterOfMass (any existing points in the output array are left as is) +#ifdef JPH_DEBUG_RENDERER +/// @param inCenterOfMass Center of mass position of body 1 +#endif +JPH_EXPORT void ManifoldBetweenTwoFaces(Vec3Arg inContactPoint1, Vec3Arg inContactPoint2, Vec3Arg inPenetrationAxis, float inMaxContactDistanceSq, const ConvexShape::SupportingFace &inShape1Face, const ConvexShape::SupportingFace &inShape2Face, ContactPoints &outContactPoints1, ContactPoints &outContactPoints2 +#ifdef JPH_DEBUG_RENDERER + , RVec3Arg inCenterOfMass +#endif + ); + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/NarrowPhaseQuery.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/NarrowPhaseQuery.cpp new file mode 100644 index 00000000000..d2b9663e3a4 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/NarrowPhaseQuery.cpp @@ -0,0 +1,412 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +bool NarrowPhaseQuery::CastRay(const RRayCast &inRay, RayCastResult &ioHit, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter) const +{ + JPH_PROFILE_FUNCTION(); + + class MyCollector : public RayCastBodyCollector + { + public: + MyCollector(const RRayCast &inRay, RayCastResult &ioHit, const BodyLockInterface &inBodyLockInterface, const BodyFilter &inBodyFilter) : + mRay(inRay), + mHit(ioHit), + mBodyLockInterface(inBodyLockInterface), + mBodyFilter(inBodyFilter) + { + UpdateEarlyOutFraction(ioHit.mFraction); + } + + virtual void AddHit(const ResultType &inResult) override + { + JPH_ASSERT(inResult.mFraction < mHit.mFraction, "This hit should not have been passed on to the collector"); + + // Only test shape if it passes the body filter + if (mBodyFilter.ShouldCollide(inResult.mBodyID)) + { + // Lock the body + BodyLockRead lock(mBodyLockInterface, inResult.mBodyID); + if (lock.SucceededAndIsInBroadPhase()) // Race condition: body could have been removed since it has been found in the broadphase, ensures body is in the broadphase while we call the callbacks + { + const Body &body = lock.GetBody(); + + // Check body filter again now that we've locked the body + if (mBodyFilter.ShouldCollideLocked(body)) + { + // Collect the transformed shape + TransformedShape ts = body.GetTransformedShape(); + + // Release the lock now, we have all the info we need in the transformed shape + lock.ReleaseLock(); + + // Do narrow phase collision check + if (ts.CastRay(mRay, mHit)) + { + // Test that we didn't find a further hit by accident + JPH_ASSERT(mHit.mFraction >= 0.0f && mHit.mFraction < GetEarlyOutFraction()); + + // Update early out fraction based on narrow phase collector + UpdateEarlyOutFraction(mHit.mFraction); + } + } + } + } + } + + RRayCast mRay; + RayCastResult & mHit; + const BodyLockInterface & mBodyLockInterface; + const BodyFilter & mBodyFilter; + }; + + // Do broadphase test, note that the broadphase uses floats so we drop precision here + MyCollector collector(inRay, ioHit, *mBodyLockInterface, inBodyFilter); + mBroadPhaseQuery->CastRay(RayCast(inRay), collector, inBroadPhaseLayerFilter, inObjectLayerFilter); + return ioHit.mFraction <= 1.0f; +} + +void NarrowPhaseQuery::CastRay(const RRayCast &inRay, const RayCastSettings &inRayCastSettings, CastRayCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter) const +{ + JPH_PROFILE_FUNCTION(); + + class MyCollector : public RayCastBodyCollector + { + public: + MyCollector(const RRayCast &inRay, const RayCastSettings &inRayCastSettings, CastRayCollector &ioCollector, const BodyLockInterface &inBodyLockInterface, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter) : + RayCastBodyCollector(ioCollector), + mRay(inRay), + mRayCastSettings(inRayCastSettings), + mCollector(ioCollector), + mBodyLockInterface(inBodyLockInterface), + mBodyFilter(inBodyFilter), + mShapeFilter(inShapeFilter) + { + } + + virtual void AddHit(const ResultType &inResult) override + { + JPH_ASSERT(inResult.mFraction < mCollector.GetEarlyOutFraction(), "This hit should not have been passed on to the collector"); + + // Only test shape if it passes the body filter + if (mBodyFilter.ShouldCollide(inResult.mBodyID)) + { + // Lock the body + BodyLockRead lock(mBodyLockInterface, inResult.mBodyID); + if (lock.SucceededAndIsInBroadPhase()) // Race condition: body could have been removed since it has been found in the broadphase, ensures body is in the broadphase while we call the callbacks + { + const Body &body = lock.GetBody(); + + // Check body filter again now that we've locked the body + if (mBodyFilter.ShouldCollideLocked(body)) + { + // Collect the transformed shape + TransformedShape ts = body.GetTransformedShape(); + + // Notify collector of new body + mCollector.OnBody(body); + + // Release the lock now, we have all the info we need in the transformed shape + lock.ReleaseLock(); + + // Do narrow phase collision check + ts.CastRay(mRay, mRayCastSettings, mCollector, mShapeFilter); + + // Update early out fraction based on narrow phase collector + UpdateEarlyOutFraction(mCollector.GetEarlyOutFraction()); + } + } + } + } + + RRayCast mRay; + RayCastSettings mRayCastSettings; + CastRayCollector & mCollector; + const BodyLockInterface & mBodyLockInterface; + const BodyFilter & mBodyFilter; + const ShapeFilter & mShapeFilter; + }; + + // Do broadphase test, note that the broadphase uses floats so we drop precision here + MyCollector collector(inRay, inRayCastSettings, ioCollector, *mBodyLockInterface, inBodyFilter, inShapeFilter); + mBroadPhaseQuery->CastRay(RayCast(inRay), collector, inBroadPhaseLayerFilter, inObjectLayerFilter); +} + +void NarrowPhaseQuery::CollidePoint(RVec3Arg inPoint, CollidePointCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter) const +{ + JPH_PROFILE_FUNCTION(); + + class MyCollector : public CollideShapeBodyCollector + { + public: + MyCollector(RVec3Arg inPoint, CollidePointCollector &ioCollector, const BodyLockInterface &inBodyLockInterface, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter) : + CollideShapeBodyCollector(ioCollector), + mPoint(inPoint), + mCollector(ioCollector), + mBodyLockInterface(inBodyLockInterface), + mBodyFilter(inBodyFilter), + mShapeFilter(inShapeFilter) + { + } + + virtual void AddHit(const ResultType &inResult) override + { + // Only test shape if it passes the body filter + if (mBodyFilter.ShouldCollide(inResult)) + { + // Lock the body + BodyLockRead lock(mBodyLockInterface, inResult); + if (lock.SucceededAndIsInBroadPhase()) // Race condition: body could have been removed since it has been found in the broadphase, ensures body is in the broadphase while we call the callbacks + { + const Body &body = lock.GetBody(); + + // Check body filter again now that we've locked the body + if (mBodyFilter.ShouldCollideLocked(body)) + { + // Collect the transformed shape + TransformedShape ts = body.GetTransformedShape(); + + // Notify collector of new body + mCollector.OnBody(body); + + // Release the lock now, we have all the info we need in the transformed shape + lock.ReleaseLock(); + + // Do narrow phase collision check + ts.CollidePoint(mPoint, mCollector, mShapeFilter); + + // Update early out fraction based on narrow phase collector + UpdateEarlyOutFraction(mCollector.GetEarlyOutFraction()); + } + } + } + } + + RVec3 mPoint; + CollidePointCollector & mCollector; + const BodyLockInterface & mBodyLockInterface; + const BodyFilter & mBodyFilter; + const ShapeFilter & mShapeFilter; + }; + + // Do broadphase test (note: truncates double to single precision since the broadphase uses single precision) + MyCollector collector(inPoint, ioCollector, *mBodyLockInterface, inBodyFilter, inShapeFilter); + mBroadPhaseQuery->CollidePoint(Vec3(inPoint), collector, inBroadPhaseLayerFilter, inObjectLayerFilter); +} + +void NarrowPhaseQuery::CollideShape(const Shape *inShape, Vec3Arg inShapeScale, RMat44Arg inCenterOfMassTransform, const CollideShapeSettings &inCollideShapeSettings, RVec3Arg inBaseOffset, CollideShapeCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter) const +{ + JPH_PROFILE_FUNCTION(); + + class MyCollector : public CollideShapeBodyCollector + { + public: + MyCollector(const Shape *inShape, Vec3Arg inShapeScale, RMat44Arg inCenterOfMassTransform, const CollideShapeSettings &inCollideShapeSettings, RVec3Arg inBaseOffset, CollideShapeCollector &ioCollector, const BodyLockInterface &inBodyLockInterface, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter) : + CollideShapeBodyCollector(ioCollector), + mShape(inShape), + mShapeScale(inShapeScale), + mCenterOfMassTransform(inCenterOfMassTransform), + mCollideShapeSettings(inCollideShapeSettings), + mBaseOffset(inBaseOffset), + mCollector(ioCollector), + mBodyLockInterface(inBodyLockInterface), + mBodyFilter(inBodyFilter), + mShapeFilter(inShapeFilter) + { + } + + virtual void AddHit(const ResultType &inResult) override + { + // Only test shape if it passes the body filter + if (mBodyFilter.ShouldCollide(inResult)) + { + // Lock the body + BodyLockRead lock(mBodyLockInterface, inResult); + if (lock.SucceededAndIsInBroadPhase()) // Race condition: body could have been removed since it has been found in the broadphase, ensures body is in the broadphase while we call the callbacks + { + const Body &body = lock.GetBody(); + + // Check body filter again now that we've locked the body + if (mBodyFilter.ShouldCollideLocked(body)) + { + // Collect the transformed shape + TransformedShape ts = body.GetTransformedShape(); + + // Notify collector of new body + mCollector.OnBody(body); + + // Release the lock now, we have all the info we need in the transformed shape + lock.ReleaseLock(); + + // Do narrow phase collision check + ts.CollideShape(mShape, mShapeScale, mCenterOfMassTransform, mCollideShapeSettings, mBaseOffset, mCollector, mShapeFilter); + + // Update early out fraction based on narrow phase collector + UpdateEarlyOutFraction(mCollector.GetEarlyOutFraction()); + } + } + } + } + + const Shape * mShape; + Vec3 mShapeScale; + RMat44 mCenterOfMassTransform; + const CollideShapeSettings & mCollideShapeSettings; + RVec3 mBaseOffset; + CollideShapeCollector & mCollector; + const BodyLockInterface & mBodyLockInterface; + const BodyFilter & mBodyFilter; + const ShapeFilter & mShapeFilter; + }; + + // Calculate bounds for shape and expand by max separation distance + AABox bounds = inShape->GetWorldSpaceBounds(inCenterOfMassTransform, inShapeScale); + bounds.ExpandBy(Vec3::sReplicate(inCollideShapeSettings.mMaxSeparationDistance)); + + // Do broadphase test + MyCollector collector(inShape, inShapeScale, inCenterOfMassTransform, inCollideShapeSettings, inBaseOffset, ioCollector, *mBodyLockInterface, inBodyFilter, inShapeFilter); + mBroadPhaseQuery->CollideAABox(bounds, collector, inBroadPhaseLayerFilter, inObjectLayerFilter); +} + +void NarrowPhaseQuery::CastShape(const RShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, RVec3Arg inBaseOffset, CastShapeCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter) const +{ + JPH_PROFILE_FUNCTION(); + + class MyCollector : public CastShapeBodyCollector + { + public: + MyCollector(const RShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, RVec3Arg inBaseOffset, CastShapeCollector &ioCollector, const BodyLockInterface &inBodyLockInterface, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter) : + CastShapeBodyCollector(ioCollector), + mShapeCast(inShapeCast), + mShapeCastSettings(inShapeCastSettings), + mBaseOffset(inBaseOffset), + mCollector(ioCollector), + mBodyLockInterface(inBodyLockInterface), + mBodyFilter(inBodyFilter), + mShapeFilter(inShapeFilter) + { + } + + virtual void AddHit(const ResultType &inResult) override + { + JPH_ASSERT(inResult.mFraction <= max(0.0f, mCollector.GetEarlyOutFraction()), "This hit should not have been passed on to the collector"); + + // Only test shape if it passes the body filter + if (mBodyFilter.ShouldCollide(inResult.mBodyID)) + { + // Lock the body + BodyLockRead lock(mBodyLockInterface, inResult.mBodyID); + if (lock.SucceededAndIsInBroadPhase()) // Race condition: body could have been removed since it has been found in the broadphase, ensures body is in the broadphase while we call the callbacks + { + const Body &body = lock.GetBody(); + + // Check body filter again now that we've locked the body + if (mBodyFilter.ShouldCollideLocked(body)) + { + // Collect the transformed shape + TransformedShape ts = body.GetTransformedShape(); + + // Notify collector of new body + mCollector.OnBody(body); + + // Release the lock now, we have all the info we need in the transformed shape + lock.ReleaseLock(); + + // Do narrow phase collision check + ts.CastShape(mShapeCast, mShapeCastSettings, mBaseOffset, mCollector, mShapeFilter); + + // Update early out fraction based on narrow phase collector + UpdateEarlyOutFraction(mCollector.GetEarlyOutFraction()); + } + } + } + } + + RShapeCast mShapeCast; + const ShapeCastSettings & mShapeCastSettings; + RVec3 mBaseOffset; + CastShapeCollector & mCollector; + const BodyLockInterface & mBodyLockInterface; + const BodyFilter & mBodyFilter; + const ShapeFilter & mShapeFilter; + }; + + // Do broadphase test + MyCollector collector(inShapeCast, inShapeCastSettings, inBaseOffset, ioCollector, *mBodyLockInterface, inBodyFilter, inShapeFilter); + mBroadPhaseQuery->CastAABox({ inShapeCast.mShapeWorldBounds, inShapeCast.mDirection }, collector, inBroadPhaseLayerFilter, inObjectLayerFilter); +} + +void NarrowPhaseQuery::CollectTransformedShapes(const AABox &inBox, TransformedShapeCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter) const +{ + class MyCollector : public CollideShapeBodyCollector + { + public: + MyCollector(const AABox &inBox, TransformedShapeCollector &ioCollector, const BodyLockInterface &inBodyLockInterface, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter) : + CollideShapeBodyCollector(ioCollector), + mBox(inBox), + mCollector(ioCollector), + mBodyLockInterface(inBodyLockInterface), + mBodyFilter(inBodyFilter), + mShapeFilter(inShapeFilter) + { + } + + virtual void AddHit(const ResultType &inResult) override + { + // Only test shape if it passes the body filter + if (mBodyFilter.ShouldCollide(inResult)) + { + // Lock the body + BodyLockRead lock(mBodyLockInterface, inResult); + if (lock.SucceededAndIsInBroadPhase()) // Race condition: body could have been removed since it has been found in the broadphase, ensures body is in the broadphase while we call the callbacks + { + const Body &body = lock.GetBody(); + + // Check body filter again now that we've locked the body + if (mBodyFilter.ShouldCollideLocked(body)) + { + // Collect the transformed shape + TransformedShape ts = body.GetTransformedShape(); + + // Notify collector of new body + mCollector.OnBody(body); + + // Release the lock now, we have all the info we need in the transformed shape + lock.ReleaseLock(); + + // Do narrow phase collision check + ts.CollectTransformedShapes(mBox, mCollector, mShapeFilter); + + // Update early out fraction based on narrow phase collector + UpdateEarlyOutFraction(mCollector.GetEarlyOutFraction()); + } + } + } + } + + const AABox & mBox; + TransformedShapeCollector & mCollector; + const BodyLockInterface & mBodyLockInterface; + const BodyFilter & mBodyFilter; + const ShapeFilter & mShapeFilter; + }; + + // Do broadphase test + MyCollector collector(inBox, ioCollector, *mBodyLockInterface, inBodyFilter, inShapeFilter); + mBroadPhaseQuery->CollideAABox(inBox, collector, inBroadPhaseLayerFilter, inObjectLayerFilter); +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/NarrowPhaseQuery.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/NarrowPhaseQuery.h new file mode 100644 index 00000000000..0967b21a1b1 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/NarrowPhaseQuery.h @@ -0,0 +1,74 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +class Shape; +class CollideShapeSettings; +class RayCastResult; + +/// Class that provides an interface for doing precise collision detection against the broad and then the narrow phase. +/// Unlike a BroadPhaseQuery, the NarrowPhaseQuery will test against shapes and will return collision information against triangles, spheres etc. +class JPH_EXPORT NarrowPhaseQuery : public NonCopyable +{ +public: + /// Initialize the interface (should only be called by PhysicsSystem) + void Init(BodyLockInterface &inBodyLockInterface, BroadPhaseQuery &inBroadPhaseQuery) { mBodyLockInterface = &inBodyLockInterface; mBroadPhaseQuery = &inBroadPhaseQuery; } + + /// Cast a ray and find the closest hit. Returns true if it finds a hit. Hits further than ioHit.mFraction will not be considered and in this case ioHit will remain unmodified (and the function will return false). + /// Convex objects will be treated as solid (meaning if the ray starts inside, you'll get a hit fraction of 0) and back face hits against triangles are returned. + /// If you want the surface normal of the hit use Body::GetWorldSpaceSurfaceNormal(ioHit.mSubShapeID2, inRay.GetPointOnRay(ioHit.mFraction)) on body with ID ioHit.mBodyID. + bool CastRay(const RRayCast &inRay, RayCastResult &ioHit, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter = { }, const ObjectLayerFilter &inObjectLayerFilter = { }, const BodyFilter &inBodyFilter = { }) const; + + /// Cast a ray, allows collecting multiple hits. Note that this version is more flexible but also slightly slower than the CastRay function that returns only a single hit. + /// If you want the surface normal of the hit use Body::GetWorldSpaceSurfaceNormal(collected sub shape ID, inRay.GetPointOnRay(collected fraction)) on body with collected body ID. + void CastRay(const RRayCast &inRay, const RayCastSettings &inRayCastSettings, CastRayCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter = { }, const ObjectLayerFilter &inObjectLayerFilter = { }, const BodyFilter &inBodyFilter = { }, const ShapeFilter &inShapeFilter = { }) const; + + /// Check if inPoint is inside any shapes. For this tests all shapes are treated as if they were solid. + /// For a mesh shape, this test will only provide sensible information if the mesh is a closed manifold. + /// For each shape that collides, ioCollector will receive a hit + void CollidePoint(RVec3Arg inPoint, CollidePointCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter = { }, const ObjectLayerFilter &inObjectLayerFilter = { }, const BodyFilter &inBodyFilter = { }, const ShapeFilter &inShapeFilter = { }) const; + + /// Collide a shape with the system + /// @param inShape Shape to test + /// @param inShapeScale Scale in local space of shape + /// @param inCenterOfMassTransform Center of mass transform for the shape + /// @param inCollideShapeSettings Settings + /// @param inBaseOffset All hit results will be returned relative to this offset, can be zero to get results in world position, but when you're testing far from the origin you get better precision by picking a position that's closer e.g. inCenterOfMassTransform.GetTranslation() since floats are most accurate near the origin + /// @param ioCollector Collector that receives the hits + /// @param inBroadPhaseLayerFilter Filter that filters at broadphase level + /// @param inObjectLayerFilter Filter that filters at layer level + /// @param inBodyFilter Filter that filters at body level + /// @param inShapeFilter Filter that filters at shape level + void CollideShape(const Shape *inShape, Vec3Arg inShapeScale, RMat44Arg inCenterOfMassTransform, const CollideShapeSettings &inCollideShapeSettings, RVec3Arg inBaseOffset, CollideShapeCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter = { }, const ObjectLayerFilter &inObjectLayerFilter = { }, const BodyFilter &inBodyFilter = { }, const ShapeFilter &inShapeFilter = { }) const; + + /// Cast a shape and report any hits to ioCollector + /// @param inShapeCast The shape cast and its position and direction + /// @param inShapeCastSettings Settings for the shape cast + /// @param inBaseOffset All hit results will be returned relative to this offset, can be zero to get results in world position, but when you're testing far from the origin you get better precision by picking a position that's closer e.g. inShapeCast.mCenterOfMassStart.GetTranslation() since floats are most accurate near the origin + /// @param ioCollector Collector that receives the hits + /// @param inBroadPhaseLayerFilter Filter that filters at broadphase level + /// @param inObjectLayerFilter Filter that filters at layer level + /// @param inBodyFilter Filter that filters at body level + /// @param inShapeFilter Filter that filters at shape level + void CastShape(const RShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, RVec3Arg inBaseOffset, CastShapeCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter = { }, const ObjectLayerFilter &inObjectLayerFilter = { }, const BodyFilter &inBodyFilter = { }, const ShapeFilter &inShapeFilter = { }) const; + + /// Collect all leaf transformed shapes that fall inside world space box inBox + void CollectTransformedShapes(const AABox &inBox, TransformedShapeCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter = { }, const ObjectLayerFilter &inObjectLayerFilter = { }, const BodyFilter &inBodyFilter = { }, const ShapeFilter &inShapeFilter = { }) const; + +private: + BodyLockInterface * mBodyLockInterface = nullptr; + BroadPhaseQuery * mBroadPhaseQuery = nullptr; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/NarrowPhaseStats.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/NarrowPhaseStats.cpp new file mode 100644 index 00000000000..69c61137253 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/NarrowPhaseStats.cpp @@ -0,0 +1,62 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include + +#ifdef JPH_TRACK_NARROWPHASE_STATS + +JPH_NAMESPACE_BEGIN + +NarrowPhaseStat NarrowPhaseStat::sCollideShape[NumSubShapeTypes][NumSubShapeTypes]; +NarrowPhaseStat NarrowPhaseStat::sCastShape[NumSubShapeTypes][NumSubShapeTypes]; + +thread_local TrackNarrowPhaseStat *TrackNarrowPhaseStat::sRoot = nullptr; + +void NarrowPhaseStat::ReportStats(const char *inName, EShapeSubType inType1, EShapeSubType inType2, uint64 inTicks100Pct) const +{ + double total_pct = 100.0 * double(mTotalTicks) / double(inTicks100Pct); + double total_pct_excl_children = 100.0 * double(mTotalTicks - mChildTicks) / double(inTicks100Pct); + + std::stringstream str; + str << inName << ", " << sSubShapeTypeNames[(int)inType1] << ", " << sSubShapeTypeNames[(int)inType2] << ", " << mNumQueries << ", " << total_pct << ", " << total_pct_excl_children << ", " << total_pct_excl_children / mNumQueries << ", " << mHitsReported; + Trace(str.str().c_str()); +} + +void NarrowPhaseStat::sReportStats() +{ + Trace("Query Type, Shape Type 1, Shape Type 2, Num Queries, Total Time (%%), Total Time Excl Children (%%), Total Time Excl. Children / Query (%%), Hits Reported"); + + uint64 total_ticks = 0; + for (EShapeSubType t1 : sAllSubShapeTypes) + for (EShapeSubType t2 : sAllSubShapeTypes) + { + const NarrowPhaseStat &collide_stat = sCollideShape[(int)t1][(int)t2]; + total_ticks += collide_stat.mTotalTicks - collide_stat.mChildTicks; + + const NarrowPhaseStat &cast_stat = sCastShape[(int)t1][(int)t2]; + total_ticks += cast_stat.mTotalTicks - cast_stat.mChildTicks; + } + + for (EShapeSubType t1 : sAllSubShapeTypes) + for (EShapeSubType t2 : sAllSubShapeTypes) + { + const NarrowPhaseStat &stat = sCollideShape[(int)t1][(int)t2]; + if (stat.mNumQueries > 0) + stat.ReportStats("CollideShape", t1, t2, total_ticks); + } + + for (EShapeSubType t1 : sAllSubShapeTypes) + for (EShapeSubType t2 : sAllSubShapeTypes) + { + const NarrowPhaseStat &stat = sCastShape[(int)t1][(int)t2]; + if (stat.mNumQueries > 0) + stat.ReportStats("CastShape", t1, t2, total_ticks); + } +} + +JPH_NAMESPACE_END + +#endif // JPH_TRACK_NARROWPHASE_STATS diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/NarrowPhaseStats.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/NarrowPhaseStats.h new file mode 100644 index 00000000000..813933bb754 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/NarrowPhaseStats.h @@ -0,0 +1,110 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +JPH_SUPPRESS_WARNING_PUSH +JPH_CLANG_SUPPRESS_WARNING("-Wc++98-compat-pedantic") + +// Shorthand function to ifdef out code if narrow phase stats tracking is off +#ifdef JPH_TRACK_NARROWPHASE_STATS + #define JPH_IF_TRACK_NARROWPHASE_STATS(...) __VA_ARGS__ +#else + #define JPH_IF_TRACK_NARROWPHASE_STATS(...) +#endif // JPH_TRACK_NARROWPHASE_STATS + +JPH_SUPPRESS_WARNING_POP + +#ifdef JPH_TRACK_NARROWPHASE_STATS + +JPH_NAMESPACE_BEGIN + +/// Structure that tracks narrow phase timing information for a particular combination of shapes +class NarrowPhaseStat +{ +public: + /// Trace an individual stat in CSV form. + void ReportStats(const char *inName, EShapeSubType inType1, EShapeSubType inType2, uint64 inTicks100Pct) const; + + /// Trace the collected broadphase stats in CSV form. + /// This report can be used to judge and tweak the efficiency of the broadphase. + static void sReportStats(); + + atomic mNumQueries = 0; + atomic mHitsReported = 0; + atomic mTotalTicks = 0; + atomic mChildTicks = 0; + + static NarrowPhaseStat sCollideShape[NumSubShapeTypes][NumSubShapeTypes]; + static NarrowPhaseStat sCastShape[NumSubShapeTypes][NumSubShapeTypes]; +}; + +/// Object that tracks the start and end of a narrow phase operation +class TrackNarrowPhaseStat +{ +public: + TrackNarrowPhaseStat(NarrowPhaseStat &inStat) : + mStat(inStat), + mParent(sRoot), + mStart(GetProcessorTickCount()) + { + // Make this the new root of the chain + sRoot = this; + } + + ~TrackNarrowPhaseStat() + { + uint64 delta_ticks = GetProcessorTickCount() - mStart; + + // Notify parent of time spent in child + if (mParent != nullptr) + mParent->mStat.mChildTicks += delta_ticks; + + // Increment stats at this level + mStat.mNumQueries++; + mStat.mTotalTicks += delta_ticks; + + // Restore root pointer + JPH_ASSERT(sRoot == this); + sRoot = mParent; + } + + NarrowPhaseStat & mStat; + TrackNarrowPhaseStat * mParent; + uint64 mStart; + + static thread_local TrackNarrowPhaseStat *sRoot; +}; + +/// Object that tracks the start and end of a hit being processed by a collision collector +class TrackNarrowPhaseCollector +{ +public: + TrackNarrowPhaseCollector() : + mStart(GetProcessorTickCount()) + { + } + + ~TrackNarrowPhaseCollector() + { + // Mark time spent in collector as 'child' time for the parent + uint64 delta_ticks = GetProcessorTickCount() - mStart; + if (TrackNarrowPhaseStat::sRoot != nullptr) + TrackNarrowPhaseStat::sRoot->mStat.mChildTicks += delta_ticks; + + // Notify all parents of a hit + for (TrackNarrowPhaseStat *track = TrackNarrowPhaseStat::sRoot; track != nullptr; track = track->mParent) + track->mStat.mHitsReported++; + } + +private: + uint64 mStart; +}; + +JPH_NAMESPACE_END + +#endif // JPH_TRACK_NARROWPHASE_STATS diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ObjectLayer.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ObjectLayer.h new file mode 100644 index 00000000000..f684e09e637 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ObjectLayer.h @@ -0,0 +1,111 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +/// Layer that objects can be in, determines which other objects it can collide with +#ifndef JPH_OBJECT_LAYER_BITS + #define JPH_OBJECT_LAYER_BITS 16 +#endif // JPH_OBJECT_LAYER_BITS +#if JPH_OBJECT_LAYER_BITS == 16 + using ObjectLayer = uint16; +#elif JPH_OBJECT_LAYER_BITS == 32 + using ObjectLayer = uint32; +#else + #error "JPH_OBJECT_LAYER_BITS must be 16 or 32" +#endif + +/// Constant value used to indicate an invalid object layer +static constexpr ObjectLayer cObjectLayerInvalid = ObjectLayer(~ObjectLayer(0U)); + +/// Filter class for object layers +class ObjectLayerFilter : public NonCopyable +{ +public: + /// Destructor + virtual ~ObjectLayerFilter() = default; + + /// Function to filter out object layers when doing collision query test (return true to allow testing against objects with this layer) + virtual bool ShouldCollide([[maybe_unused]] ObjectLayer inLayer) const + { + return true; + } + +#ifdef JPH_TRACK_BROADPHASE_STATS + /// Get a string that describes this filter for stat tracking purposes + virtual String GetDescription() const + { + return "No Description"; + } +#endif // JPH_TRACK_BROADPHASE_STATS +}; + +/// Filter class to test if two objects can collide based on their object layer. Used while finding collision pairs. +class ObjectLayerPairFilter : public NonCopyable +{ +public: + /// Destructor + virtual ~ObjectLayerPairFilter() = default; + + /// Returns true if two layers can collide + virtual bool ShouldCollide([[maybe_unused]] ObjectLayer inLayer1, [[maybe_unused]] ObjectLayer inLayer2) const + { + return true; + } +}; + +/// Default filter class that uses the pair filter in combination with a specified layer to filter layers +class DefaultObjectLayerFilter : public ObjectLayerFilter +{ +public: + /// Constructor + DefaultObjectLayerFilter(const ObjectLayerPairFilter &inObjectLayerPairFilter, ObjectLayer inLayer) : + mObjectLayerPairFilter(inObjectLayerPairFilter), + mLayer(inLayer) + { + } + + /// Copy constructor + DefaultObjectLayerFilter(const DefaultObjectLayerFilter &inRHS) : + mObjectLayerPairFilter(inRHS.mObjectLayerPairFilter), + mLayer(inRHS.mLayer) + { + } + + // See ObjectLayerFilter::ShouldCollide + virtual bool ShouldCollide(ObjectLayer inLayer) const override + { + return mObjectLayerPairFilter.ShouldCollide(mLayer, inLayer); + } + +private: + const ObjectLayerPairFilter & mObjectLayerPairFilter; + ObjectLayer mLayer; +}; + +/// Allows objects from a specific layer only +class SpecifiedObjectLayerFilter : public ObjectLayerFilter +{ +public: + /// Constructor + explicit SpecifiedObjectLayerFilter(ObjectLayer inLayer) : + mLayer(inLayer) + { + } + + // See ObjectLayerFilter::ShouldCollide + virtual bool ShouldCollide(ObjectLayer inLayer) const override + { + return mLayer == inLayer; + } + +private: + ObjectLayer mLayer; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ObjectLayerPairFilterMask.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ObjectLayerPairFilterMask.h new file mode 100644 index 00000000000..dc3494c2ee3 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ObjectLayerPairFilterMask.h @@ -0,0 +1,52 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2023 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +/// Filter class to test if two objects can collide based on their object layer. Used while finding collision pairs. +/// Uses group bits and mask bits. Two layers can collide if Object1.Group & Object2.Mask is non-zero and Object2.Group & Object1.Mask is non-zero. +/// The behavior is similar to that in e.g. Bullet. +/// This implementation works together with BroadPhaseLayerInterfaceMask and ObjectVsBroadPhaseLayerFilterMask +class ObjectLayerPairFilterMask : public ObjectLayerPairFilter +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Number of bits for the group and mask bits + static constexpr uint32 cNumBits = JPH_OBJECT_LAYER_BITS / 2; + static constexpr uint32 cMask = (1 << cNumBits) - 1; + + /// Construct an ObjectLayer from a group and mask bits + static ObjectLayer sGetObjectLayer(uint32 inGroup, uint32 inMask = cMask) + { + JPH_ASSERT((inGroup & ~cMask) == 0); + JPH_ASSERT((inMask & ~cMask) == 0); + return ObjectLayer((inGroup & cMask) | (inMask << cNumBits)); + } + + /// Get the group bits from an ObjectLayer + static inline uint32 sGetGroup(ObjectLayer inObjectLayer) + { + return uint32(inObjectLayer) & cMask; + } + + /// Get the mask bits from an ObjectLayer + static inline uint32 sGetMask(ObjectLayer inObjectLayer) + { + return uint32(inObjectLayer) >> cNumBits; + } + + /// Returns true if two layers can collide + virtual bool ShouldCollide(ObjectLayer inObject1, ObjectLayer inObject2) const override + { + return (sGetGroup(inObject1) & sGetMask(inObject2)) != 0 + && (sGetGroup(inObject2) & sGetMask(inObject1)) != 0; + } +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ObjectLayerPairFilterTable.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ObjectLayerPairFilterTable.h new file mode 100644 index 00000000000..1d62178af7e --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ObjectLayerPairFilterTable.h @@ -0,0 +1,78 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2023 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +/// Filter class to test if two objects can collide based on their object layer. Used while finding collision pairs. +/// This implementation uses a table to determine if two layers can collide. +class ObjectLayerPairFilterTable : public ObjectLayerPairFilter +{ +private: + /// Get which bit corresponds to the pair (inLayer1, inLayer2) + uint GetBit(ObjectLayer inLayer1, ObjectLayer inLayer2) const + { + // We store the lower left half only, so swap the inputs when trying to access the top right half + if (inLayer1 > inLayer2) + swap(inLayer1, inLayer2); + + JPH_ASSERT(inLayer2 < mNumObjectLayers); + + // Calculate at which bit the entry for this pair resides + // We use the fact that a row always starts at inLayer2 * (inLayer2 + 1) / 2 + // (this is the amount of bits needed to store a table of inLayer2 entries) + return (inLayer2 * (inLayer2 + 1)) / 2 + inLayer1; + } + +public: + JPH_OVERRIDE_NEW_DELETE + + /// Constructs the table with inNumObjectLayers Layers, initially all layer pairs are disabled + explicit ObjectLayerPairFilterTable(uint inNumObjectLayers) : + mNumObjectLayers(inNumObjectLayers) + { + // By default nothing collides + // For the first layer we only need to store 1 bit, for the second 2 bits, for the third 3 bits, etc. + // We use the formula Sum_i=1^N i = N * (N + 1) / 2 to calculate the size of the table + int table_size = (inNumObjectLayers * (inNumObjectLayers + 1) / 2 + 7) / 8; + mTable.resize(table_size, 0); + } + + /// Get the number of object layers + uint GetNumObjectLayers() const + { + return mNumObjectLayers; + } + + /// Disable collision between two object layers + void DisableCollision(ObjectLayer inLayer1, ObjectLayer inLayer2) + { + uint bit = GetBit(inLayer1, inLayer2); + mTable[bit >> 3] &= (0xff ^ (1 << (bit & 0b111))); + } + + /// Enable collision between two object layers + void EnableCollision(ObjectLayer inLayer1, ObjectLayer inLayer2) + { + uint bit = GetBit(inLayer1, inLayer2); + mTable[bit >> 3] |= 1 << (bit & 0b111); + } + + /// Returns true if two layers can collide + virtual bool ShouldCollide(ObjectLayer inObject1, ObjectLayer inObject2) const override + { + // Test if the bit is set for this group pair + uint bit = GetBit(inObject1, inObject2); + return (mTable[bit >> 3] & (1 << (bit & 0b111))) != 0; + } + +private: + uint mNumObjectLayers; ///< The number of layers that this table supports + Array mTable; ///< The table of bits that indicates which layers collide +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/PhysicsMaterial.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/PhysicsMaterial.cpp new file mode 100644 index 00000000000..17a982e7499 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/PhysicsMaterial.cpp @@ -0,0 +1,35 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +RefConst PhysicsMaterial::sDefault; + +JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(PhysicsMaterial) +{ + JPH_ADD_BASE_CLASS(PhysicsMaterial, SerializableObject) +} + +void PhysicsMaterial::SaveBinaryState(StreamOut &inStream) const +{ + inStream.Write(GetRTTI()->GetHash()); +} + +void PhysicsMaterial::RestoreBinaryState(StreamIn &inStream) +{ + // RTTI hash is read in sRestoreFromBinaryState +} + +PhysicsMaterial::PhysicsMaterialResult PhysicsMaterial::sRestoreFromBinaryState(StreamIn &inStream) +{ + return StreamUtils::RestoreObject(inStream, &PhysicsMaterial::RestoreBinaryState); +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/PhysicsMaterial.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/PhysicsMaterial.h new file mode 100644 index 00000000000..d24e344d1a6 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/PhysicsMaterial.h @@ -0,0 +1,52 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +class StreamIn; +class StreamOut; + +/// This structure describes the surface of (part of) a shape. You should inherit from it to define additional +/// information that is interesting for the simulation. The 2 materials involved in a contact could be used +/// to decide which sound or particle effects to play. +/// +/// If you inherit from this material, don't forget to create a suitable default material in sDefault +class JPH_EXPORT PhysicsMaterial : public SerializableObject, public RefTarget +{ +public: + JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, PhysicsMaterial) + + /// Virtual destructor + virtual ~PhysicsMaterial() override = default; + + /// Default material that is used when a shape has no materials defined + static RefConst sDefault; + + // Properties + virtual const char * GetDebugName() const { return "Unknown"; } + virtual Color GetDebugColor() const { return Color::sGrey; } + + /// Saves the contents of the material in binary form to inStream. + virtual void SaveBinaryState(StreamOut &inStream) const; + + using PhysicsMaterialResult = Result>; + + /// Creates a PhysicsMaterial of the correct type and restores its contents from the binary stream inStream. + static PhysicsMaterialResult sRestoreFromBinaryState(StreamIn &inStream); + +protected: + /// This function should not be called directly, it is used by sRestoreFromBinaryState. + virtual void RestoreBinaryState(StreamIn &inStream); +}; + +using PhysicsMaterialList = Array>; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/PhysicsMaterialSimple.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/PhysicsMaterialSimple.cpp new file mode 100644 index 00000000000..02a569822c6 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/PhysicsMaterialSimple.cpp @@ -0,0 +1,38 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(PhysicsMaterialSimple) +{ + JPH_ADD_BASE_CLASS(PhysicsMaterialSimple, PhysicsMaterial) + + JPH_ADD_ATTRIBUTE(PhysicsMaterialSimple, mDebugName) + JPH_ADD_ATTRIBUTE(PhysicsMaterialSimple, mDebugColor) +} + +void PhysicsMaterialSimple::SaveBinaryState(StreamOut &inStream) const +{ + PhysicsMaterial::SaveBinaryState(inStream); + + inStream.Write(mDebugName); + inStream.Write(mDebugColor); +} + +void PhysicsMaterialSimple::RestoreBinaryState(StreamIn &inStream) +{ + PhysicsMaterial::RestoreBinaryState(inStream); + + inStream.Read(mDebugName); + inStream.Read(mDebugColor); +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/PhysicsMaterialSimple.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/PhysicsMaterialSimple.h new file mode 100644 index 00000000000..21c1e634a56 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/PhysicsMaterialSimple.h @@ -0,0 +1,37 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +/// Sample implementation of PhysicsMaterial that just holds the needed properties directly +class JPH_EXPORT PhysicsMaterialSimple : public PhysicsMaterial +{ +public: + JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, PhysicsMaterialSimple) + + /// Constructor + PhysicsMaterialSimple() = default; + PhysicsMaterialSimple(const string_view &inName, ColorArg inColor) : mDebugName(inName), mDebugColor(inColor) { } + + // Properties + virtual const char * GetDebugName() const override { return mDebugName.c_str(); } + virtual Color GetDebugColor() const override { return mDebugColor; } + + // See: PhysicsMaterial::SaveBinaryState + virtual void SaveBinaryState(StreamOut &inStream) const override; + +protected: + // See: PhysicsMaterial::RestoreBinaryState + virtual void RestoreBinaryState(StreamIn &inStream) override; + +private: + String mDebugName; ///< Name of the material, used for debugging purposes + Color mDebugColor = Color::sGrey; ///< Color of the material, used to render the shapes +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/RayCast.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/RayCast.h new file mode 100644 index 00000000000..052c815296a --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/RayCast.h @@ -0,0 +1,81 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +/// Structure that holds a single ray cast +template +struct RayCastT +{ + JPH_OVERRIDE_NEW_DELETE + + /// Constructors + RayCastT() = default; // Allow raycast to be created uninitialized + RayCastT(typename Vec::ArgType inOrigin, Vec3Arg inDirection) : mOrigin(inOrigin), mDirection(inDirection) { } + RayCastT(const RayCastT &) = default; + + /// Transform this ray using inTransform + RayCastType Transformed(typename Mat::ArgType inTransform) const + { + Vec ray_origin = inTransform * mOrigin; + Vec3 ray_direction(inTransform * (mOrigin + mDirection) - ray_origin); + return { ray_origin, ray_direction }; + } + + /// Translate ray using inTranslation + RayCastType Translated(typename Vec::ArgType inTranslation) const + { + return { inTranslation + mOrigin, mDirection }; + } + + /// Get point with fraction inFraction on ray (0 = start of ray, 1 = end of ray) + inline Vec GetPointOnRay(float inFraction) const + { + return mOrigin + inFraction * mDirection; + } + + Vec mOrigin; ///< Origin of the ray + Vec3 mDirection; ///< Direction and length of the ray (anything beyond this length will not be reported as a hit) +}; + +struct RayCast : public RayCastT +{ + using RayCastT::RayCastT; +}; + +struct RRayCast : public RayCastT +{ + using RayCastT::RayCastT; + + /// Convert from RayCast, converts single to double precision + explicit RRayCast(const RayCast &inRay) : + RRayCast(RVec3(inRay.mOrigin), inRay.mDirection) + { + } + + /// Convert to RayCast, which implies casting from double precision to single precision + explicit operator RayCast() const + { + return RayCast(Vec3(mOrigin), mDirection); + } +}; + +/// Settings to be passed with a ray cast +class RayCastSettings +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// How backfacing triangles should be treated + EBackFaceMode mBackFaceMode = EBackFaceMode::IgnoreBackFaces; + + /// If convex shapes should be treated as solid. When true, a ray starting inside a convex shape will generate a hit at fraction 0. + bool mTreatConvexAsSolid = true; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/BoxShape.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/BoxShape.cpp new file mode 100644 index 00000000000..da6b08c4ff5 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/BoxShape.cpp @@ -0,0 +1,318 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef JPH_DEBUG_RENDERER + #include +#endif // JPH_DEBUG_RENDERER + +JPH_NAMESPACE_BEGIN + +JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(BoxShapeSettings) +{ + JPH_ADD_BASE_CLASS(BoxShapeSettings, ConvexShapeSettings) + + JPH_ADD_ATTRIBUTE(BoxShapeSettings, mHalfExtent) + JPH_ADD_ATTRIBUTE(BoxShapeSettings, mConvexRadius) +} + +static const Vec3 sUnitBoxTriangles[] = { + Vec3(-1, 1, -1), Vec3(-1, 1, 1), Vec3(1, 1, 1), + Vec3(-1, 1, -1), Vec3(1, 1, 1), Vec3(1, 1, -1), + Vec3(-1, -1, -1), Vec3(1, -1, -1), Vec3(1, -1, 1), + Vec3(-1, -1, -1), Vec3(1, -1, 1), Vec3(-1, -1, 1), + Vec3(-1, 1, -1), Vec3(-1, -1, -1), Vec3(-1, -1, 1), + Vec3(-1, 1, -1), Vec3(-1, -1, 1), Vec3(-1, 1, 1), + Vec3(1, 1, 1), Vec3(1, -1, 1), Vec3(1, -1, -1), + Vec3(1, 1, 1), Vec3(1, -1, -1), Vec3(1, 1, -1), + Vec3(-1, 1, 1), Vec3(-1, -1, 1), Vec3(1, -1, 1), + Vec3(-1, 1, 1), Vec3(1, -1, 1), Vec3(1, 1, 1), + Vec3(-1, 1, -1), Vec3(1, 1, -1), Vec3(1, -1, -1), + Vec3(-1, 1, -1), Vec3(1, -1, -1), Vec3(-1, -1, -1) +}; + +ShapeSettings::ShapeResult BoxShapeSettings::Create() const +{ + if (mCachedResult.IsEmpty()) + Ref shape = new BoxShape(*this, mCachedResult); + return mCachedResult; +} + +BoxShape::BoxShape(const BoxShapeSettings &inSettings, ShapeResult &outResult) : + ConvexShape(EShapeSubType::Box, inSettings, outResult), + mHalfExtent(inSettings.mHalfExtent), + mConvexRadius(inSettings.mConvexRadius) +{ + // Check convex radius + if (inSettings.mConvexRadius < 0.0f + || inSettings.mHalfExtent.ReduceMin() <= inSettings.mConvexRadius) + { + outResult.SetError("Invalid convex radius"); + return; + } + + // Result is valid + outResult.Set(this); +} + +class BoxShape::Box final : public Support +{ +public: + Box(const AABox &inBox, float inConvexRadius) : + mBox(inBox), + mConvexRadius(inConvexRadius) + { + static_assert(sizeof(Box) <= sizeof(SupportBuffer), "Buffer size too small"); + JPH_ASSERT(IsAligned(this, alignof(Box))); + } + + virtual Vec3 GetSupport(Vec3Arg inDirection) const override + { + return mBox.GetSupport(inDirection); + } + + virtual float GetConvexRadius() const override + { + return mConvexRadius; + } + +private: + AABox mBox; + float mConvexRadius; +}; + +const ConvexShape::Support *BoxShape::GetSupportFunction(ESupportMode inMode, SupportBuffer &inBuffer, Vec3Arg inScale) const +{ + // Scale our half extents + Vec3 scaled_half_extent = inScale.Abs() * mHalfExtent; + + switch (inMode) + { + case ESupportMode::IncludeConvexRadius: + case ESupportMode::Default: + { + // Make box out of our half extents + AABox box = AABox(-scaled_half_extent, scaled_half_extent); + JPH_ASSERT(box.IsValid()); + return new (&inBuffer) Box(box, 0.0f); + } + + case ESupportMode::ExcludeConvexRadius: + { + // Reduce the box by our convex radius + float convex_radius = ScaleHelpers::ScaleConvexRadius(mConvexRadius, inScale); + Vec3 convex_radius3 = Vec3::sReplicate(convex_radius); + Vec3 reduced_half_extent = scaled_half_extent - convex_radius3; + AABox box = AABox(-reduced_half_extent, reduced_half_extent); + JPH_ASSERT(box.IsValid()); + return new (&inBuffer) Box(box, convex_radius); + } + } + + JPH_ASSERT(false); + return nullptr; +} + +void BoxShape::GetSupportingFace(const SubShapeID &inSubShapeID, Vec3Arg inDirection, Vec3Arg inScale, Mat44Arg inCenterOfMassTransform, SupportingFace &outVertices) const +{ + JPH_ASSERT(inSubShapeID.IsEmpty(), "Invalid subshape ID"); + + Vec3 scaled_half_extent = inScale.Abs() * mHalfExtent; + AABox box(-scaled_half_extent, scaled_half_extent); + box.GetSupportingFace(inDirection, outVertices); + + // Transform to world space + for (Vec3 &v : outVertices) + v = inCenterOfMassTransform * v; +} + +MassProperties BoxShape::GetMassProperties() const +{ + MassProperties p; + p.SetMassAndInertiaOfSolidBox(2.0f * mHalfExtent, GetDensity()); + return p; +} + +Vec3 BoxShape::GetSurfaceNormal(const SubShapeID &inSubShapeID, Vec3Arg inLocalSurfacePosition) const +{ + JPH_ASSERT(inSubShapeID.IsEmpty(), "Invalid subshape ID"); + + // Get component that is closest to the surface of the box + int index = (inLocalSurfacePosition.Abs() - mHalfExtent).Abs().GetLowestComponentIndex(); + + // Calculate normal + Vec3 normal = Vec3::sZero(); + normal.SetComponent(index, inLocalSurfacePosition[index] > 0.0f? 1.0f : -1.0f); + return normal; +} + +#ifdef JPH_DEBUG_RENDERER +void BoxShape::Draw(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inUseMaterialColors, bool inDrawWireframe) const +{ + DebugRenderer::EDrawMode draw_mode = inDrawWireframe? DebugRenderer::EDrawMode::Wireframe : DebugRenderer::EDrawMode::Solid; + inRenderer->DrawBox(inCenterOfMassTransform * Mat44::sScale(inScale.Abs()), GetLocalBounds(), inUseMaterialColors? GetMaterial()->GetDebugColor() : inColor, DebugRenderer::ECastShadow::On, draw_mode); +} +#endif // JPH_DEBUG_RENDERER + +bool BoxShape::CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const +{ + // Test hit against box + float fraction = max(RayAABox(inRay.mOrigin, RayInvDirection(inRay.mDirection), -mHalfExtent, mHalfExtent), 0.0f); + if (fraction < ioHit.mFraction) + { + ioHit.mFraction = fraction; + ioHit.mSubShapeID2 = inSubShapeIDCreator.GetID(); + return true; + } + return false; +} + +void BoxShape::CastRay(const RayCast &inRay, const RayCastSettings &inRayCastSettings, const SubShapeIDCreator &inSubShapeIDCreator, CastRayCollector &ioCollector, const ShapeFilter &inShapeFilter) const +{ + // Test shape filter + if (!inShapeFilter.ShouldCollide(this, inSubShapeIDCreator.GetID())) + return; + + float min_fraction, max_fraction; + RayAABox(inRay.mOrigin, RayInvDirection(inRay.mDirection), -mHalfExtent, mHalfExtent, min_fraction, max_fraction); + if (min_fraction <= max_fraction // Ray should intersect + && max_fraction >= 0.0f // End of ray should be inside box + && min_fraction < ioCollector.GetEarlyOutFraction()) // Start of ray should be before early out fraction + { + // Better hit than the current hit + RayCastResult hit; + hit.mBodyID = TransformedShape::sGetBodyID(ioCollector.GetContext()); + hit.mSubShapeID2 = inSubShapeIDCreator.GetID(); + + // Check front side + if (inRayCastSettings.mTreatConvexAsSolid || min_fraction > 0.0f) + { + hit.mFraction = max(0.0f, min_fraction); + ioCollector.AddHit(hit); + } + + // Check back side hit + if (inRayCastSettings.mBackFaceMode == EBackFaceMode::CollideWithBackFaces + && max_fraction < ioCollector.GetEarlyOutFraction()) + { + hit.mFraction = max_fraction; + ioCollector.AddHit(hit); + } + } +} + +void BoxShape::CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter) const +{ + // Test shape filter + if (!inShapeFilter.ShouldCollide(this, inSubShapeIDCreator.GetID())) + return; + + if (Vec3::sLessOrEqual(inPoint.Abs(), mHalfExtent).TestAllXYZTrue()) + ioCollector.AddHit({ TransformedShape::sGetBodyID(ioCollector.GetContext()), inSubShapeIDCreator.GetID() }); +} + +void BoxShape::CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, SoftBodyVertex *ioVertices, uint inNumVertices, [[maybe_unused]] float inDeltaTime, [[maybe_unused]] Vec3Arg inDisplacementDueToGravity, int inCollidingShapeIndex) const +{ + Mat44 inverse_transform = inCenterOfMassTransform.InversedRotationTranslation(); + Vec3 half_extent = inScale.Abs() * mHalfExtent; + + for (SoftBodyVertex *v = ioVertices, *sbv_end = ioVertices + inNumVertices; v < sbv_end; ++v) + if (v->mInvMass > 0.0f) + { + // Convert to local space + Vec3 local_pos = inverse_transform * v->mPosition; + + // Clamp point to inside box + Vec3 clamped_point = Vec3::sMax(Vec3::sMin(local_pos, half_extent), -half_extent); + + // Test if point was inside + if (clamped_point == local_pos) + { + // Calculate closest distance to surface + Vec3 delta = half_extent - local_pos.Abs(); + int index = delta.GetLowestComponentIndex(); + float penetration = delta[index]; + if (penetration > v->mLargestPenetration) + { + v->mLargestPenetration = penetration; + + // Calculate contact point and normal + Vec3 possible_normals[] = { Vec3::sAxisX(), Vec3::sAxisY(), Vec3::sAxisZ() }; + Vec3 normal = local_pos.GetSign() * possible_normals[index]; + Vec3 point = normal * half_extent; + + // Store collision + v->mCollisionPlane = Plane::sFromPointAndNormal(point, normal).GetTransformed(inCenterOfMassTransform); + v->mCollidingShapeIndex = inCollidingShapeIndex; + } + } + else + { + // Calculate normal + Vec3 normal = local_pos - clamped_point; + float normal_length = normal.Length(); + + // Penetration will be negative since we're not penetrating + float penetration = -normal_length; + if (penetration > v->mLargestPenetration) + { + normal /= normal_length; + + v->mLargestPenetration = penetration; + + // Store collision + v->mCollisionPlane = Plane::sFromPointAndNormal(clamped_point, normal).GetTransformed(inCenterOfMassTransform); + v->mCollidingShapeIndex = inCollidingShapeIndex; + } + } + } +} + +void BoxShape::GetTrianglesStart(GetTrianglesContext &ioContext, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) const +{ + new (&ioContext) GetTrianglesContextVertexList(inPositionCOM, inRotation, inScale, Mat44::sScale(mHalfExtent), sUnitBoxTriangles, sizeof(sUnitBoxTriangles) / sizeof(Vec3), GetMaterial()); +} + +int BoxShape::GetTrianglesNext(GetTrianglesContext &ioContext, int inMaxTrianglesRequested, Float3 *outTriangleVertices, const PhysicsMaterial **outMaterials) const +{ + return ((GetTrianglesContextVertexList &)ioContext).GetTrianglesNext(inMaxTrianglesRequested, outTriangleVertices, outMaterials); +} + +void BoxShape::SaveBinaryState(StreamOut &inStream) const +{ + ConvexShape::SaveBinaryState(inStream); + + inStream.Write(mHalfExtent); + inStream.Write(mConvexRadius); +} + +void BoxShape::RestoreBinaryState(StreamIn &inStream) +{ + ConvexShape::RestoreBinaryState(inStream); + + inStream.Read(mHalfExtent); + inStream.Read(mConvexRadius); +} + +void BoxShape::sRegister() +{ + ShapeFunctions &f = ShapeFunctions::sGet(EShapeSubType::Box); + f.mConstruct = []() -> Shape * { return new BoxShape; }; + f.mColor = Color::sGreen; +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/BoxShape.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/BoxShape.h new file mode 100644 index 00000000000..dc53772d987 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/BoxShape.h @@ -0,0 +1,115 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +JPH_NAMESPACE_BEGIN + +/// Class that constructs a BoxShape +class JPH_EXPORT BoxShapeSettings final : public ConvexShapeSettings +{ +public: + JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, BoxShapeSettings) + + /// Default constructor for deserialization + BoxShapeSettings() = default; + + /// Create a box with half edge length inHalfExtent and convex radius inConvexRadius. + /// (internally the convex radius will be subtracted from the half extent so the total box will not grow with the convex radius). + BoxShapeSettings(Vec3Arg inHalfExtent, float inConvexRadius = cDefaultConvexRadius, const PhysicsMaterial *inMaterial = nullptr) : ConvexShapeSettings(inMaterial), mHalfExtent(inHalfExtent), mConvexRadius(inConvexRadius) { } + + // See: ShapeSettings + virtual ShapeResult Create() const override; + + Vec3 mHalfExtent = Vec3::sZero(); ///< Half the size of the box (including convex radius) + float mConvexRadius = 0.0f; +}; + +/// A box, centered around the origin +class JPH_EXPORT BoxShape final : public ConvexShape +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Constructor + BoxShape() : ConvexShape(EShapeSubType::Box) { } + BoxShape(const BoxShapeSettings &inSettings, ShapeResult &outResult); + + /// Create a box with half edge length inHalfExtent and convex radius inConvexRadius. + /// (internally the convex radius will be subtracted from the half extent so the total box will not grow with the convex radius). + BoxShape(Vec3Arg inHalfExtent, float inConvexRadius = cDefaultConvexRadius, const PhysicsMaterial *inMaterial = nullptr) : ConvexShape(EShapeSubType::Box, inMaterial), mHalfExtent(inHalfExtent), mConvexRadius(inConvexRadius) { JPH_ASSERT(inConvexRadius >= 0.0f); JPH_ASSERT(inHalfExtent.ReduceMin() >= inConvexRadius); } + + /// Get half extent of box + Vec3 GetHalfExtent() const { return mHalfExtent; } + + // See Shape::GetLocalBounds + virtual AABox GetLocalBounds() const override { return AABox(-mHalfExtent, mHalfExtent); } + + // See Shape::GetInnerRadius + virtual float GetInnerRadius() const override { return mHalfExtent.ReduceMin(); } + + // See Shape::GetMassProperties + virtual MassProperties GetMassProperties() const override; + + // See Shape::GetSurfaceNormal + virtual Vec3 GetSurfaceNormal(const SubShapeID &inSubShapeID, Vec3Arg inLocalSurfacePosition) const override; + + // See Shape::GetSupportingFace + virtual void GetSupportingFace(const SubShapeID &inSubShapeID, Vec3Arg inDirection, Vec3Arg inScale, Mat44Arg inCenterOfMassTransform, SupportingFace &outVertices) const override; + + // See ConvexShape::GetSupportFunction + virtual const Support * GetSupportFunction(ESupportMode inMode, SupportBuffer &inBuffer, Vec3Arg inScale) const override; + +#ifdef JPH_DEBUG_RENDERER + // See Shape::Draw + virtual void Draw(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inUseMaterialColors, bool inDrawWireframe) const override; +#endif // JPH_DEBUG_RENDERER + + // See Shape::CastRay + virtual bool CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const override; + virtual void CastRay(const RayCast &inRay, const RayCastSettings &inRayCastSettings, const SubShapeIDCreator &inSubShapeIDCreator, CastRayCollector &ioCollector, const ShapeFilter &inShapeFilter = { }) const override; + + // See: Shape::CollidePoint + virtual void CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter = { }) const override; + + // See: Shape::CollideSoftBodyVertices + virtual void CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, SoftBodyVertex *ioVertices, uint inNumVertices, float inDeltaTime, Vec3Arg inDisplacementDueToGravity, int inCollidingShapeIndex) const override; + + // See Shape::GetTrianglesStart + virtual void GetTrianglesStart(GetTrianglesContext &ioContext, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) const override; + + // See Shape::GetTrianglesNext + virtual int GetTrianglesNext(GetTrianglesContext &ioContext, int inMaxTrianglesRequested, Float3 *outTriangleVertices, const PhysicsMaterial **outMaterials = nullptr) const override; + + // See Shape + virtual void SaveBinaryState(StreamOut &inStream) const override; + + // See Shape::GetStats + virtual Stats GetStats() const override { return Stats(sizeof(*this), 12); } + + // See Shape::GetVolume + virtual float GetVolume() const override { return GetLocalBounds().GetVolume(); } + + /// Get the convex radius of this box + float GetConvexRadius() const { return mConvexRadius; } + + // Register shape functions with the registry + static void sRegister(); + +protected: + // See: Shape::RestoreBinaryState + virtual void RestoreBinaryState(StreamIn &inStream) override; + +private: + // Class for GetSupportFunction + class Box; + + Vec3 mHalfExtent = Vec3::sZero(); ///< Half the size of the box (including convex radius) + float mConvexRadius = 0.0f; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/CapsuleShape.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/CapsuleShape.cpp new file mode 100644 index 00000000000..5f050ea63a5 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/CapsuleShape.cpp @@ -0,0 +1,446 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef JPH_DEBUG_RENDERER + #include +#endif // JPH_DEBUG_RENDERER + +JPH_NAMESPACE_BEGIN + +JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(CapsuleShapeSettings) +{ + JPH_ADD_BASE_CLASS(CapsuleShapeSettings, ConvexShapeSettings) + + JPH_ADD_ATTRIBUTE(CapsuleShapeSettings, mRadius) + JPH_ADD_ATTRIBUTE(CapsuleShapeSettings, mHalfHeightOfCylinder) +} + +static const int cCapsuleDetailLevel = 2; + +static const std::vector sCapsuleTopTriangles = []() { + std::vector verts; + GetTrianglesContextVertexList::sCreateHalfUnitSphereTop(verts, cCapsuleDetailLevel); + return verts; +}(); + +static const std::vector sCapsuleMiddleTriangles = []() { + std::vector verts; + GetTrianglesContextVertexList::sCreateUnitOpenCylinder(verts, cCapsuleDetailLevel); + return verts; +}(); + +static const std::vector sCapsuleBottomTriangles = []() { + std::vector verts; + GetTrianglesContextVertexList::sCreateHalfUnitSphereBottom(verts, cCapsuleDetailLevel); + return verts; +}(); + +ShapeSettings::ShapeResult CapsuleShapeSettings::Create() const +{ + if (mCachedResult.IsEmpty()) + { + Ref shape; + if (IsValid() && IsSphere()) + { + // If the capsule has no height, use a sphere instead + shape = new SphereShape(mRadius, mMaterial); + mCachedResult.Set(shape); + } + else + shape = new CapsuleShape(*this, mCachedResult); + } + return mCachedResult; +} + +CapsuleShape::CapsuleShape(const CapsuleShapeSettings &inSettings, ShapeResult &outResult) : + ConvexShape(EShapeSubType::Capsule, inSettings, outResult), + mRadius(inSettings.mRadius), + mHalfHeightOfCylinder(inSettings.mHalfHeightOfCylinder) +{ + if (inSettings.mHalfHeightOfCylinder <= 0.0f) + { + outResult.SetError("Invalid height"); + return; + } + + if (inSettings.mRadius <= 0.0f) + { + outResult.SetError("Invalid radius"); + return; + } + + outResult.Set(this); +} + +class CapsuleShape::CapsuleNoConvex final : public Support +{ +public: + CapsuleNoConvex(Vec3Arg inHalfHeightOfCylinder, float inConvexRadius) : + mHalfHeightOfCylinder(inHalfHeightOfCylinder), + mConvexRadius(inConvexRadius) + { + static_assert(sizeof(CapsuleNoConvex) <= sizeof(SupportBuffer), "Buffer size too small"); + JPH_ASSERT(IsAligned(this, alignof(CapsuleNoConvex))); + } + + virtual Vec3 GetSupport(Vec3Arg inDirection) const override + { + if (inDirection.GetY() > 0) + return mHalfHeightOfCylinder; + else + return -mHalfHeightOfCylinder; + } + + virtual float GetConvexRadius() const override + { + return mConvexRadius; + } + +private: + Vec3 mHalfHeightOfCylinder; + float mConvexRadius; +}; + +class CapsuleShape::CapsuleWithConvex final : public Support +{ +public: + CapsuleWithConvex(Vec3Arg inHalfHeightOfCylinder, float inRadius) : + mHalfHeightOfCylinder(inHalfHeightOfCylinder), + mRadius(inRadius) + { + static_assert(sizeof(CapsuleWithConvex) <= sizeof(SupportBuffer), "Buffer size too small"); + JPH_ASSERT(IsAligned(this, alignof(CapsuleWithConvex))); + } + + virtual Vec3 GetSupport(Vec3Arg inDirection) const override + { + float len = inDirection.Length(); + Vec3 radius = len > 0.0f? inDirection * (mRadius / len) : Vec3::sZero(); + + if (inDirection.GetY() > 0) + return radius + mHalfHeightOfCylinder; + else + return radius - mHalfHeightOfCylinder; + } + + virtual float GetConvexRadius() const override + { + return 0.0f; + } + +private: + Vec3 mHalfHeightOfCylinder; + float mRadius; +}; + +const ConvexShape::Support *CapsuleShape::GetSupportFunction(ESupportMode inMode, SupportBuffer &inBuffer, Vec3Arg inScale) const +{ + JPH_ASSERT(IsValidScale(inScale)); + + // Get scaled capsule + Vec3 abs_scale = inScale.Abs(); + float scale = abs_scale.GetX(); + Vec3 scaled_half_height_of_cylinder = Vec3(0, scale * mHalfHeightOfCylinder, 0); + float scaled_radius = scale * mRadius; + + switch (inMode) + { + case ESupportMode::IncludeConvexRadius: + return new (&inBuffer) CapsuleWithConvex(scaled_half_height_of_cylinder, scaled_radius); + + case ESupportMode::ExcludeConvexRadius: + case ESupportMode::Default: + return new (&inBuffer) CapsuleNoConvex(scaled_half_height_of_cylinder, scaled_radius); + } + + JPH_ASSERT(false); + return nullptr; +} + +void CapsuleShape::GetSupportingFace(const SubShapeID &inSubShapeID, Vec3Arg inDirection, Vec3Arg inScale, Mat44Arg inCenterOfMassTransform, SupportingFace &outVertices) const +{ + JPH_ASSERT(inSubShapeID.IsEmpty(), "Invalid subshape ID"); + JPH_ASSERT(IsValidScale(inScale)); + + // Get direction in horizontal plane + Vec3 direction = inDirection; + direction.SetComponent(1, 0.0f); + + // Check zero vector, in this case we're hitting from top/bottom so there's no supporting face + float len = direction.Length(); + if (len == 0.0f) + return; + + // Get scaled capsule + Vec3 abs_scale = inScale.Abs(); + float scale = abs_scale.GetX(); + Vec3 scaled_half_height_of_cylinder = Vec3(0, scale * mHalfHeightOfCylinder, 0); + float scaled_radius = scale * mRadius; + + // Get support point for top and bottom sphere in the opposite of 'direction' (including convex radius) + Vec3 support = (scaled_radius / len) * direction; + Vec3 support_top = scaled_half_height_of_cylinder - support; + Vec3 support_bottom = -scaled_half_height_of_cylinder - support; + + // Get projection on inDirection + // Note that inDirection is not normalized, so we need to divide by inDirection.Length() to get the actual projection + // We've multiplied both sides of the if below with inDirection.Length() + float proj_top = support_top.Dot(inDirection); + float proj_bottom = support_bottom.Dot(inDirection); + + // If projection is roughly equal then return line, otherwise we return nothing as there's only 1 point + if (abs(proj_top - proj_bottom) < cCapsuleProjectionSlop * inDirection.Length()) + { + outVertices.push_back(inCenterOfMassTransform * support_top); + outVertices.push_back(inCenterOfMassTransform * support_bottom); + } +} + +MassProperties CapsuleShape::GetMassProperties() const +{ + MassProperties p; + + float density = GetDensity(); + + // Calculate inertia and mass according to: + // https://www.gamedev.net/resources/_/technical/math-and-physics/capsule-inertia-tensor-r3856 + // Note that there is an error in eq 14, H^2/2 should be H^2/4 in Ixx and Izz, eq 12 does contain the correct value + float radius_sq = Square(mRadius); + float height = 2.0f * mHalfHeightOfCylinder; + float cylinder_mass = JPH_PI * height * radius_sq * density; + float hemisphere_mass = (2.0f * JPH_PI / 3.0f) * radius_sq * mRadius * density; + + // From cylinder + float height_sq = Square(height); + float inertia_y = radius_sq * cylinder_mass * 0.5f; + float inertia_xz = inertia_y * 0.5f + cylinder_mass * height_sq / 12.0f; + + // From hemispheres + float temp = hemisphere_mass * 4.0f * radius_sq / 5.0f; + inertia_y += temp; + inertia_xz += temp + hemisphere_mass * (0.5f * height_sq + (3.0f / 4.0f) * height * mRadius); + + // Mass is cylinder + hemispheres + p.mMass = cylinder_mass + hemisphere_mass * 2.0f; + + // Set inertia + p.mInertia = Mat44::sScale(Vec3(inertia_xz, inertia_y, inertia_xz)); + + return p; +} + +Vec3 CapsuleShape::GetSurfaceNormal(const SubShapeID &inSubShapeID, Vec3Arg inLocalSurfacePosition) const +{ + JPH_ASSERT(inSubShapeID.IsEmpty(), "Invalid subshape ID"); + + if (inLocalSurfacePosition.GetY() > mHalfHeightOfCylinder) + return (inLocalSurfacePosition - Vec3(0, mHalfHeightOfCylinder, 0)).Normalized(); + else if (inLocalSurfacePosition.GetY() < -mHalfHeightOfCylinder) + return (inLocalSurfacePosition - Vec3(0, -mHalfHeightOfCylinder, 0)).Normalized(); + else + return Vec3(inLocalSurfacePosition.GetX(), 0, inLocalSurfacePosition.GetZ()).NormalizedOr(Vec3::sAxisX()); +} + +AABox CapsuleShape::GetLocalBounds() const +{ + Vec3 extent = Vec3::sReplicate(mRadius) + Vec3(0, mHalfHeightOfCylinder, 0); + return AABox(-extent, extent); +} + +AABox CapsuleShape::GetWorldSpaceBounds(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale) const +{ + JPH_ASSERT(IsValidScale(inScale)); + + Vec3 abs_scale = inScale.Abs(); + float scale = abs_scale.GetX(); + Vec3 extent = Vec3::sReplicate(scale * mRadius); + Vec3 height = Vec3(0, scale * mHalfHeightOfCylinder, 0); + Vec3 p1 = inCenterOfMassTransform * -height; + Vec3 p2 = inCenterOfMassTransform * height; + return AABox(Vec3::sMin(p1, p2) - extent, Vec3::sMax(p1, p2) + extent); +} + +#ifdef JPH_DEBUG_RENDERER +void CapsuleShape::Draw(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inUseMaterialColors, bool inDrawWireframe) const +{ + DebugRenderer::EDrawMode draw_mode = inDrawWireframe? DebugRenderer::EDrawMode::Wireframe : DebugRenderer::EDrawMode::Solid; + inRenderer->DrawCapsule(inCenterOfMassTransform * Mat44::sScale(inScale.Abs().GetX()), mHalfHeightOfCylinder, mRadius, inUseMaterialColors? GetMaterial()->GetDebugColor() : inColor, DebugRenderer::ECastShadow::On, draw_mode); +} +#endif // JPH_DEBUG_RENDERER + +bool CapsuleShape::CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const +{ + // Test ray against capsule + float fraction = RayCapsule(inRay.mOrigin, inRay.mDirection, mHalfHeightOfCylinder, mRadius); + if (fraction < ioHit.mFraction) + { + ioHit.mFraction = fraction; + ioHit.mSubShapeID2 = inSubShapeIDCreator.GetID(); + return true; + } + return false; +} + +void CapsuleShape::CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter) const +{ + // Test shape filter + if (!inShapeFilter.ShouldCollide(this, inSubShapeIDCreator.GetID())) + return; + + float radius_sq = Square(mRadius); + + // Get vertical distance to the top/bottom sphere centers + float delta_y = abs(inPoint.GetY()) - mHalfHeightOfCylinder; + + // Get distance in horizontal plane + float xz_sq = Square(inPoint.GetX()) + Square(inPoint.GetZ()); + + // Check if the point is in one of the two spheres + bool in_sphere = xz_sq + Square(delta_y) <= radius_sq; + + // Check if the point is in the cylinder in the middle + bool in_cylinder = delta_y <= 0.0f && xz_sq <= radius_sq; + + if (in_sphere || in_cylinder) + ioCollector.AddHit({ TransformedShape::sGetBodyID(ioCollector.GetContext()), inSubShapeIDCreator.GetID() }); +} + +void CapsuleShape::CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, SoftBodyVertex *ioVertices, uint inNumVertices, [[maybe_unused]] float inDeltaTime, [[maybe_unused]] Vec3Arg inDisplacementDueToGravity, int inCollidingShapeIndex) const +{ + JPH_ASSERT(IsValidScale(inScale)); + + Mat44 inverse_transform = inCenterOfMassTransform.InversedRotationTranslation(); + + // Get scaled capsule + float scale = abs(inScale.GetX()); + float half_height_of_cylinder = scale * mHalfHeightOfCylinder; + float radius = scale * mRadius; + + for (SoftBodyVertex *v = ioVertices, *sbv_end = ioVertices + inNumVertices; v < sbv_end; ++v) + if (v->mInvMass > 0.0f) + { + // Calculate penetration + Vec3 local_pos = inverse_transform * v->mPosition; + if (abs(local_pos.GetY()) <= half_height_of_cylinder) + { + // Near cylinder + Vec3 normal = local_pos; + normal.SetY(0.0f); + float normal_length = normal.Length(); + float penetration = radius - normal_length; + if (penetration > v->mLargestPenetration) + { + v->mLargestPenetration = penetration; + + // Calculate contact point and normal + normal = normal_length > 0.0f? normal / normal_length : Vec3::sAxisX(); + Vec3 point = radius * normal; + + // Store collision + v->mCollisionPlane = Plane::sFromPointAndNormal(point, normal).GetTransformed(inCenterOfMassTransform); + v->mCollidingShapeIndex = inCollidingShapeIndex; + } + } + else + { + // Near cap + Vec3 center = Vec3(0, Sign(local_pos.GetY()) * half_height_of_cylinder, 0); + Vec3 delta = local_pos - center; + float distance = delta.Length(); + float penetration = radius - distance; + if (penetration > v->mLargestPenetration) + { + v->mLargestPenetration = penetration; + + // Calculate contact point and normal + Vec3 normal = delta / distance; + Vec3 point = center + radius * normal; + + // Store collision + v->mCollisionPlane = Plane::sFromPointAndNormal(point, normal).GetTransformed(inCenterOfMassTransform); + v->mCollidingShapeIndex = inCollidingShapeIndex; + } + } + } +} + +void CapsuleShape::TransformShape(Mat44Arg inCenterOfMassTransform, TransformedShapeCollector &ioCollector) const +{ + Vec3 scale; + Mat44 transform = inCenterOfMassTransform.Decompose(scale); + TransformedShape ts(RVec3(transform.GetTranslation()), transform.GetQuaternion(), this, BodyID(), SubShapeIDCreator()); + ts.SetShapeScale(ScaleHelpers::MakeUniformScale(scale.Abs())); + ioCollector.AddHit(ts); +} + +void CapsuleShape::GetTrianglesStart(GetTrianglesContext &ioContext, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) const +{ + JPH_ASSERT(IsValidScale(inScale)); + + Vec3 abs_scale = inScale.Abs(); + float scale = abs_scale.GetX(); + + GetTrianglesContextMultiVertexList *context = new (&ioContext) GetTrianglesContextMultiVertexList(false, GetMaterial()); + + Mat44 world_matrix = Mat44::sRotationTranslation(inRotation, inPositionCOM) * Mat44::sScale(scale); + + Mat44 top_matrix = world_matrix * Mat44(Vec4(mRadius, 0, 0, 0), Vec4(0, mRadius, 0, 0), Vec4(0, 0, mRadius, 0), Vec4(0, mHalfHeightOfCylinder, 0, 1)); + context->AddPart(top_matrix, sCapsuleTopTriangles.data(), sCapsuleTopTriangles.size()); + + Mat44 middle_matrix = world_matrix * Mat44::sScale(Vec3(mRadius, mHalfHeightOfCylinder, mRadius)); + context->AddPart(middle_matrix, sCapsuleMiddleTriangles.data(), sCapsuleMiddleTriangles.size()); + + Mat44 bottom_matrix = world_matrix * Mat44(Vec4(mRadius, 0, 0, 0), Vec4(0, mRadius, 0, 0), Vec4(0, 0, mRadius, 0), Vec4(0, -mHalfHeightOfCylinder, 0, 1)); + context->AddPart(bottom_matrix, sCapsuleBottomTriangles.data(), sCapsuleBottomTriangles.size()); +} + +int CapsuleShape::GetTrianglesNext(GetTrianglesContext &ioContext, int inMaxTrianglesRequested, Float3 *outTriangleVertices, const PhysicsMaterial **outMaterials) const +{ + return ((GetTrianglesContextMultiVertexList &)ioContext).GetTrianglesNext(inMaxTrianglesRequested, outTriangleVertices, outMaterials); +} + +void CapsuleShape::SaveBinaryState(StreamOut &inStream) const +{ + ConvexShape::SaveBinaryState(inStream); + + inStream.Write(mRadius); + inStream.Write(mHalfHeightOfCylinder); +} + +void CapsuleShape::RestoreBinaryState(StreamIn &inStream) +{ + ConvexShape::RestoreBinaryState(inStream); + + inStream.Read(mRadius); + inStream.Read(mHalfHeightOfCylinder); +} + +bool CapsuleShape::IsValidScale(Vec3Arg inScale) const +{ + return ConvexShape::IsValidScale(inScale) && ScaleHelpers::IsUniformScale(inScale.Abs()); +} + +void CapsuleShape::sRegister() +{ + ShapeFunctions &f = ShapeFunctions::sGet(EShapeSubType::Capsule); + f.mConstruct = []() -> Shape * { return new CapsuleShape; }; + f.mColor = Color::sGreen; +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/CapsuleShape.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/CapsuleShape.h new file mode 100644 index 00000000000..73281d0febb --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/CapsuleShape.h @@ -0,0 +1,128 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +/// Class that constructs a CapsuleShape +class JPH_EXPORT CapsuleShapeSettings final : public ConvexShapeSettings +{ + JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, CapsuleShapeSettings) + + /// Default constructor for deserialization + CapsuleShapeSettings() = default; + + /// Create a capsule centered around the origin with one sphere cap at (0, -inHalfHeightOfCylinder, 0) and the other at (0, inHalfHeightOfCylinder, 0) + CapsuleShapeSettings(float inHalfHeightOfCylinder, float inRadius, const PhysicsMaterial *inMaterial = nullptr) : ConvexShapeSettings(inMaterial), mRadius(inRadius), mHalfHeightOfCylinder(inHalfHeightOfCylinder) { } + + /// Check if this is a valid capsule shape + bool IsValid() const { return mRadius > 0.0f && mHalfHeightOfCylinder >= 0.0f; } + + /// Checks if the settings of this capsule make this shape a sphere + bool IsSphere() const { return mHalfHeightOfCylinder == 0.0f; } + + // See: ShapeSettings + virtual ShapeResult Create() const override; + + float mRadius = 0.0f; + float mHalfHeightOfCylinder = 0.0f; +}; + +/// A capsule, implemented as a line segment with convex radius +class JPH_EXPORT CapsuleShape final : public ConvexShape +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Constructor + CapsuleShape() : ConvexShape(EShapeSubType::Capsule) { } + CapsuleShape(const CapsuleShapeSettings &inSettings, ShapeResult &outResult); + + /// Create a capsule centered around the origin with one sphere cap at (0, -inHalfHeightOfCylinder, 0) and the other at (0, inHalfHeightOfCylinder, 0) + CapsuleShape(float inHalfHeightOfCylinder, float inRadius, const PhysicsMaterial *inMaterial = nullptr) : ConvexShape(EShapeSubType::Capsule, inMaterial), mRadius(inRadius), mHalfHeightOfCylinder(inHalfHeightOfCylinder) { JPH_ASSERT(inHalfHeightOfCylinder > 0.0f); JPH_ASSERT(inRadius > 0.0f); } + + /// Radius of the cylinder + float GetRadius() const { return mRadius; } + + /// Get half of the height of the cylinder + float GetHalfHeightOfCylinder() const { return mHalfHeightOfCylinder; } + + // See Shape::GetLocalBounds + virtual AABox GetLocalBounds() const override; + + // See Shape::GetWorldSpaceBounds + virtual AABox GetWorldSpaceBounds(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale) const override; + using Shape::GetWorldSpaceBounds; + + // See Shape::GetInnerRadius + virtual float GetInnerRadius() const override { return mRadius; } + + // See Shape::GetMassProperties + virtual MassProperties GetMassProperties() const override; + + // See Shape::GetSurfaceNormal + virtual Vec3 GetSurfaceNormal(const SubShapeID &inSubShapeID, Vec3Arg inLocalSurfacePosition) const override; + + // See Shape::GetSupportingFace + virtual void GetSupportingFace(const SubShapeID &inSubShapeID, Vec3Arg inDirection, Vec3Arg inScale, Mat44Arg inCenterOfMassTransform, SupportingFace &outVertices) const override; + + // See ConvexShape::GetSupportFunction + virtual const Support * GetSupportFunction(ESupportMode inMode, SupportBuffer &inBuffer, Vec3Arg inScale) const override; + +#ifdef JPH_DEBUG_RENDERER + // See Shape::Draw + virtual void Draw(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inUseMaterialColors, bool inDrawWireframe) const override; +#endif // JPH_DEBUG_RENDERER + + // See Shape::CastRay + using ConvexShape::CastRay; + virtual bool CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const override; + + // See: Shape::CollidePoint + virtual void CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter = { }) const override; + + // See: Shape::CollideSoftBodyVertices + virtual void CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, SoftBodyVertex *ioVertices, uint inNumVertices, float inDeltaTime, Vec3Arg inDisplacementDueToGravity, int inCollidingShapeIndex) const override; + + // See Shape::TransformShape + virtual void TransformShape(Mat44Arg inCenterOfMassTransform, TransformedShapeCollector &ioCollector) const override; + + // See Shape::GetTrianglesStart + virtual void GetTrianglesStart(GetTrianglesContext &ioContext, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) const override; + + // See Shape::GetTrianglesNext + virtual int GetTrianglesNext(GetTrianglesContext &ioContext, int inMaxTrianglesRequested, Float3 *outTriangleVertices, const PhysicsMaterial **outMaterials = nullptr) const override; + + // See Shape + virtual void SaveBinaryState(StreamOut &inStream) const override; + + // See Shape::GetStats + virtual Stats GetStats() const override { return Stats(sizeof(*this), 0); } + + // See Shape::GetVolume + virtual float GetVolume() const override { return 4.0f / 3.0f * JPH_PI * Cubed(mRadius) + 2.0f * JPH_PI * mHalfHeightOfCylinder * Square(mRadius); } + + // See Shape::IsValidScale + virtual bool IsValidScale(Vec3Arg inScale) const override; + + // Register shape functions with the registry + static void sRegister(); + +protected: + // See: Shape::RestoreBinaryState + virtual void RestoreBinaryState(StreamIn &inStream) override; + +private: + // Classes for GetSupportFunction + class CapsuleNoConvex; + class CapsuleWithConvex; + + float mRadius = 0.0f; + float mHalfHeightOfCylinder = 0.0f; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/CompoundShape.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/CompoundShape.cpp new file mode 100644 index 00000000000..20a61f28a52 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/CompoundShape.cpp @@ -0,0 +1,398 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef JPH_DEBUG_RENDERER + #include +#endif // JPH_DEBUG_RENDERER + +JPH_NAMESPACE_BEGIN + +JPH_IMPLEMENT_SERIALIZABLE_ABSTRACT(CompoundShapeSettings) +{ + JPH_ADD_BASE_CLASS(CompoundShapeSettings, ShapeSettings) + + JPH_ADD_ATTRIBUTE(CompoundShapeSettings, mSubShapes) +} + +JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(CompoundShapeSettings::SubShapeSettings) +{ + JPH_ADD_ATTRIBUTE(CompoundShapeSettings::SubShapeSettings, mShape) + JPH_ADD_ATTRIBUTE(CompoundShapeSettings::SubShapeSettings, mPosition) + JPH_ADD_ATTRIBUTE(CompoundShapeSettings::SubShapeSettings, mRotation) + JPH_ADD_ATTRIBUTE(CompoundShapeSettings::SubShapeSettings, mUserData) +} + +void CompoundShapeSettings::AddShape(Vec3Arg inPosition, QuatArg inRotation, const ShapeSettings *inShape, uint32 inUserData) +{ + // Add shape + SubShapeSettings shape; + shape.mPosition = inPosition; + shape.mRotation = inRotation; + shape.mShape = inShape; + shape.mUserData = inUserData; + mSubShapes.push_back(shape); +} + +void CompoundShapeSettings::AddShape(Vec3Arg inPosition, QuatArg inRotation, const Shape *inShape, uint32 inUserData) +{ + // Add shape + SubShapeSettings shape; + shape.mPosition = inPosition; + shape.mRotation = inRotation; + shape.mShapePtr = inShape; + shape.mUserData = inUserData; + mSubShapes.push_back(shape); +} + +bool CompoundShape::MustBeStatic() const +{ + for (const SubShape &shape : mSubShapes) + if (shape.mShape->MustBeStatic()) + return true; + + return false; +} + +MassProperties CompoundShape::GetMassProperties() const +{ + MassProperties p; + + // Calculate mass and inertia + p.mMass = 0.0f; + p.mInertia = Mat44::sZero(); + for (const SubShape &shape : mSubShapes) + { + // Rotate and translate inertia of child into place + MassProperties child = shape.mShape->GetMassProperties(); + child.Rotate(Mat44::sRotation(shape.GetRotation())); + child.Translate(shape.GetPositionCOM()); + + // Accumulate mass and inertia + p.mMass += child.mMass; + p.mInertia += child.mInertia; + } + + // Ensure that inertia is a 3x3 matrix, adding inertias causes the bottom right element to change + p.mInertia.SetColumn4(3, Vec4(0, 0, 0, 1)); + + return p; +} + +AABox CompoundShape::GetWorldSpaceBounds(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale) const +{ + if (mSubShapes.size() <= 10) + { + AABox bounds; + for (const SubShape &shape : mSubShapes) + { + Mat44 transform = inCenterOfMassTransform * shape.GetLocalTransformNoScale(inScale); + bounds.Encapsulate(shape.mShape->GetWorldSpaceBounds(transform, shape.TransformScale(inScale))); + } + return bounds; + } + else + { + // If there are too many shapes, use the base class function (this will result in a slightly wider bounding box) + return Shape::GetWorldSpaceBounds(inCenterOfMassTransform, inScale); + } +} + +uint CompoundShape::GetSubShapeIDBitsRecursive() const +{ + // Add max of child bits to our bits + uint child_bits = 0; + for (const SubShape &shape : mSubShapes) + child_bits = max(child_bits, shape.mShape->GetSubShapeIDBitsRecursive()); + return child_bits + GetSubShapeIDBits(); +} + +const PhysicsMaterial *CompoundShape::GetMaterial(const SubShapeID &inSubShapeID) const +{ + // Decode sub shape index + SubShapeID remainder; + uint32 index = GetSubShapeIndexFromID(inSubShapeID, remainder); + + // Pass call on + return mSubShapes[index].mShape->GetMaterial(remainder); +} + +uint64 CompoundShape::GetSubShapeUserData(const SubShapeID &inSubShapeID) const +{ + // Decode sub shape index + SubShapeID remainder; + uint32 index = GetSubShapeIndexFromID(inSubShapeID, remainder); + if (index >= mSubShapes.size()) + return 0; // No longer valid index + + // Pass call on + return mSubShapes[index].mShape->GetSubShapeUserData(remainder); +} + +TransformedShape CompoundShape::GetSubShapeTransformedShape(const SubShapeID &inSubShapeID, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale, SubShapeID &outRemainder) const +{ + // Get the sub shape + const SubShape &sub_shape = mSubShapes[GetSubShapeIndexFromID(inSubShapeID, outRemainder)]; + + // Calculate transform for sub shape + Vec3 position = inPositionCOM + inRotation * (inScale * sub_shape.GetPositionCOM()); + Quat rotation = inRotation * sub_shape.GetRotation(); + Vec3 scale = sub_shape.TransformScale(inScale); + + // Return transformed shape + TransformedShape ts(RVec3(position), rotation, sub_shape.mShape, BodyID()); + ts.SetShapeScale(scale); + return ts; +} + +Vec3 CompoundShape::GetSurfaceNormal(const SubShapeID &inSubShapeID, Vec3Arg inLocalSurfacePosition) const +{ + // Decode sub shape index + SubShapeID remainder; + uint32 index = GetSubShapeIndexFromID(inSubShapeID, remainder); + + // Transform surface position to local space and pass call on + const SubShape &shape = mSubShapes[index]; + Mat44 transform = Mat44::sInverseRotationTranslation(shape.GetRotation(), shape.GetPositionCOM()); + Vec3 normal = shape.mShape->GetSurfaceNormal(remainder, transform * inLocalSurfacePosition); + + // Transform normal to this shape's space + return transform.Multiply3x3Transposed(normal); +} + +void CompoundShape::GetSupportingFace(const SubShapeID &inSubShapeID, Vec3Arg inDirection, Vec3Arg inScale, Mat44Arg inCenterOfMassTransform, SupportingFace &outVertices) const +{ + // Decode sub shape index + SubShapeID remainder; + uint32 index = GetSubShapeIndexFromID(inSubShapeID, remainder); + + // Apply transform and pass on to sub shape + const SubShape &shape = mSubShapes[index]; + Mat44 transform = shape.GetLocalTransformNoScale(inScale); + shape.mShape->GetSupportingFace(remainder, transform.Multiply3x3Transposed(inDirection), shape.TransformScale(inScale), inCenterOfMassTransform * transform, outVertices); +} + +void CompoundShape::GetSubmergedVolume(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, const Plane &inSurface, float &outTotalVolume, float &outSubmergedVolume, Vec3 &outCenterOfBuoyancy JPH_IF_DEBUG_RENDERER(, RVec3Arg inBaseOffset)) const +{ + outTotalVolume = 0.0f; + outSubmergedVolume = 0.0f; + outCenterOfBuoyancy = Vec3::sZero(); + + for (const SubShape &shape : mSubShapes) + { + // Get center of mass transform of child + Mat44 transform = inCenterOfMassTransform * shape.GetLocalTransformNoScale(inScale); + + // Recurse to child + float total_volume, submerged_volume; + Vec3 center_of_buoyancy; + shape.mShape->GetSubmergedVolume(transform, shape.TransformScale(inScale), inSurface, total_volume, submerged_volume, center_of_buoyancy JPH_IF_DEBUG_RENDERER(, inBaseOffset)); + + // Accumulate volumes + outTotalVolume += total_volume; + outSubmergedVolume += submerged_volume; + + // The center of buoyancy is the weighted average of the center of buoyancy of our child shapes + outCenterOfBuoyancy += submerged_volume * center_of_buoyancy; + } + + if (outSubmergedVolume > 0.0f) + outCenterOfBuoyancy /= outSubmergedVolume; + +#ifdef JPH_DEBUG_RENDERER + // Draw center of buoyancy + if (sDrawSubmergedVolumes) + DebugRenderer::sInstance->DrawWireSphere(inBaseOffset + outCenterOfBuoyancy, 0.05f, Color::sRed, 1); +#endif // JPH_DEBUG_RENDERER +} + +#ifdef JPH_DEBUG_RENDERER +void CompoundShape::Draw(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inUseMaterialColors, bool inDrawWireframe) const +{ + for (const SubShape &shape : mSubShapes) + { + Mat44 transform = shape.GetLocalTransformNoScale(inScale); + shape.mShape->Draw(inRenderer, inCenterOfMassTransform * transform, shape.TransformScale(inScale), inColor, inUseMaterialColors, inDrawWireframe); + } +} + +void CompoundShape::DrawGetSupportFunction(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inDrawSupportDirection) const +{ + for (const SubShape &shape : mSubShapes) + { + Mat44 transform = shape.GetLocalTransformNoScale(inScale); + shape.mShape->DrawGetSupportFunction(inRenderer, inCenterOfMassTransform * transform, shape.TransformScale(inScale), inColor, inDrawSupportDirection); + } +} + +void CompoundShape::DrawGetSupportingFace(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale) const +{ + for (const SubShape &shape : mSubShapes) + { + Mat44 transform = shape.GetLocalTransformNoScale(inScale); + shape.mShape->DrawGetSupportingFace(inRenderer, inCenterOfMassTransform * transform, shape.TransformScale(inScale)); + } +} +#endif // JPH_DEBUG_RENDERER + +void CompoundShape::CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, SoftBodyVertex *ioVertices, uint inNumVertices, float inDeltaTime, Vec3Arg inDisplacementDueToGravity, int inCollidingShapeIndex) const +{ + for (const SubShape &shape : mSubShapes) + { + Mat44 transform = shape.GetLocalTransformNoScale(inScale); + shape.mShape->CollideSoftBodyVertices(inCenterOfMassTransform * transform, shape.TransformScale(inScale), ioVertices, inNumVertices, inDeltaTime, inDisplacementDueToGravity, inCollidingShapeIndex); + } +} + +void CompoundShape::TransformShape(Mat44Arg inCenterOfMassTransform, TransformedShapeCollector &ioCollector) const +{ + for (const SubShape &shape : mSubShapes) + shape.mShape->TransformShape(inCenterOfMassTransform * Mat44::sRotationTranslation(shape.GetRotation(), shape.GetPositionCOM()), ioCollector); +} + +void CompoundShape::sCastCompoundVsShape(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector) +{ + JPH_PROFILE_FUNCTION(); + + // Fetch compound shape from cast shape + JPH_ASSERT(inShapeCast.mShape->GetType() == EShapeType::Compound); + const CompoundShape *compound = static_cast(inShapeCast.mShape); + + // Number of sub shapes + int n = (int)compound->mSubShapes.size(); + + // Determine amount of bits for sub shape + uint sub_shape_bits = compound->GetSubShapeIDBits(); + + // Recurse to sub shapes + for (int i = 0; i < n; ++i) + { + const SubShape &shape = compound->mSubShapes[i]; + + // Create ID for sub shape + SubShapeIDCreator shape1_sub_shape_id = inSubShapeIDCreator1.PushID(i, sub_shape_bits); + + // Transform the shape cast and update the shape + Mat44 transform = inShapeCast.mCenterOfMassStart * shape.GetLocalTransformNoScale(inShapeCast.mScale); + Vec3 scale = shape.TransformScale(inShapeCast.mScale); + ShapeCast shape_cast(shape.mShape, scale, transform, inShapeCast.mDirection); + + CollisionDispatch::sCastShapeVsShapeLocalSpace(shape_cast, inShapeCastSettings, inShape, inScale, inShapeFilter, inCenterOfMassTransform2, shape1_sub_shape_id, inSubShapeIDCreator2, ioCollector); + + if (ioCollector.ShouldEarlyOut()) + break; + } +} + +void CompoundShape::SaveBinaryState(StreamOut &inStream) const +{ + Shape::SaveBinaryState(inStream); + + inStream.Write(mCenterOfMass); + inStream.Write(mLocalBounds.mMin); + inStream.Write(mLocalBounds.mMax); + inStream.Write(mInnerRadius); + + // Write sub shapes + inStream.Write(mSubShapes, [](const SubShape &inElement, StreamOut &inS) { + inS.Write(inElement.mUserData); + inS.Write(inElement.mPositionCOM); + inS.Write(inElement.mRotation); + }); +} + +void CompoundShape::RestoreBinaryState(StreamIn &inStream) +{ + Shape::RestoreBinaryState(inStream); + + inStream.Read(mCenterOfMass); + inStream.Read(mLocalBounds.mMin); + inStream.Read(mLocalBounds.mMax); + inStream.Read(mInnerRadius); + + // Read sub shapes + inStream.Read(mSubShapes, [](StreamIn &inS, SubShape &outElement) { + inS.Read(outElement.mUserData); + inS.Read(outElement.mPositionCOM); + inS.Read(outElement.mRotation); + outElement.mIsRotationIdentity = outElement.mRotation == Float3(0, 0, 0); + }); +} + +void CompoundShape::SaveSubShapeState(ShapeList &outSubShapes) const +{ + outSubShapes.clear(); + outSubShapes.reserve(mSubShapes.size()); + for (const SubShape &shape : mSubShapes) + outSubShapes.push_back(shape.mShape); +} + +void CompoundShape::RestoreSubShapeState(const ShapeRefC *inSubShapes, uint inNumShapes) +{ + JPH_ASSERT(mSubShapes.size() == inNumShapes); + for (uint i = 0; i < inNumShapes; ++i) + mSubShapes[i].mShape = inSubShapes[i]; +} + +Shape::Stats CompoundShape::GetStatsRecursive(VisitedShapes &ioVisitedShapes) const +{ + // Get own stats + Stats stats = Shape::GetStatsRecursive(ioVisitedShapes); + + // Add child stats + for (const SubShape &shape : mSubShapes) + { + Stats child_stats = shape.mShape->GetStatsRecursive(ioVisitedShapes); + stats.mSizeBytes += child_stats.mSizeBytes; + stats.mNumTriangles += child_stats.mNumTriangles; + } + + return stats; +} + +float CompoundShape::GetVolume() const +{ + float volume = 0.0f; + for (const SubShape &shape : mSubShapes) + volume += shape.mShape->GetVolume(); + return volume; +} + +bool CompoundShape::IsValidScale(Vec3Arg inScale) const +{ + if (!Shape::IsValidScale(inScale)) + return false; + + for (const SubShape &shape : mSubShapes) + { + // Test if the scale is non-uniform and the shape is rotated + if (!shape.IsValidScale(inScale)) + return false; + + // Test the child shape + if (!shape.mShape->IsValidScale(shape.TransformScale(inScale))) + return false; + } + + return true; +} + +void CompoundShape::sRegister() +{ + for (EShapeSubType s1 : sCompoundSubShapeTypes) + for (EShapeSubType s2 : sAllSubShapeTypes) + CollisionDispatch::sRegisterCastShape(s1, s2, sCastCompoundVsShape); +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/CompoundShape.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/CompoundShape.h new file mode 100644 index 00000000000..7694bf4e000 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/CompoundShape.h @@ -0,0 +1,344 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +class CollideShapeSettings; +class OrientedBox; + +/// Base class settings to construct a compound shape +class JPH_EXPORT CompoundShapeSettings : public ShapeSettings +{ +public: + JPH_DECLARE_SERIALIZABLE_ABSTRACT(JPH_EXPORT, CompoundShapeSettings) + + /// Constructor. Use AddShape to add the parts. + CompoundShapeSettings() = default; + + /// Add a shape to the compound. + void AddShape(Vec3Arg inPosition, QuatArg inRotation, const ShapeSettings *inShape, uint32 inUserData = 0); + + /// Add a shape to the compound. Variant that uses a concrete shape, which means this object cannot be serialized. + void AddShape(Vec3Arg inPosition, QuatArg inRotation, const Shape *inShape, uint32 inUserData = 0); + + struct SubShapeSettings + { + JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, SubShapeSettings) + + RefConst mShape; ///< Sub shape (either this or mShapePtr needs to be filled up) + RefConst mShapePtr; ///< Sub shape (either this or mShape needs to be filled up) + Vec3 mPosition; ///< Position of the sub shape + Quat mRotation; ///< Rotation of the sub shape + uint32 mUserData = 0; ///< User data value (can be used by the application for any purpose) + }; + + using SubShapes = Array; + + SubShapes mSubShapes; +}; + +/// Base class for a compound shape +class JPH_EXPORT CompoundShape : public Shape +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Constructor + explicit CompoundShape(EShapeSubType inSubType) : Shape(EShapeType::Compound, inSubType) { } + CompoundShape(EShapeSubType inSubType, const ShapeSettings &inSettings, ShapeResult &outResult) : Shape(EShapeType::Compound, inSubType, inSettings, outResult) { } + + // See Shape::GetCenterOfMass + virtual Vec3 GetCenterOfMass() const override { return mCenterOfMass; } + + // See Shape::MustBeStatic + virtual bool MustBeStatic() const override; + + // See Shape::GetLocalBounds + virtual AABox GetLocalBounds() const override { return mLocalBounds; } + + // See Shape::GetSubShapeIDBitsRecursive + virtual uint GetSubShapeIDBitsRecursive() const override; + + // See Shape::GetWorldSpaceBounds + virtual AABox GetWorldSpaceBounds(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale) const override; + using Shape::GetWorldSpaceBounds; + + // See Shape::GetInnerRadius + virtual float GetInnerRadius() const override { return mInnerRadius; } + + // See Shape::GetMassProperties + virtual MassProperties GetMassProperties() const override; + + // See Shape::GetMaterial + virtual const PhysicsMaterial * GetMaterial(const SubShapeID &inSubShapeID) const override; + + // See Shape::GetSubShapeUserData + virtual uint64 GetSubShapeUserData(const SubShapeID &inSubShapeID) const override; + + // See Shape::GetSubShapeTransformedShape + virtual TransformedShape GetSubShapeTransformedShape(const SubShapeID &inSubShapeID, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale, SubShapeID &outRemainder) const override; + + // See Shape::GetSurfaceNormal + virtual Vec3 GetSurfaceNormal(const SubShapeID &inSubShapeID, Vec3Arg inLocalSurfacePosition) const override; + + // See Shape::GetSupportingFace + virtual void GetSupportingFace(const SubShapeID &inSubShapeID, Vec3Arg inDirection, Vec3Arg inScale, Mat44Arg inCenterOfMassTransform, SupportingFace &outVertices) const override; + + // See Shape::GetSubmergedVolume + virtual void GetSubmergedVolume(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, const Plane &inSurface, float &outTotalVolume, float &outSubmergedVolume, Vec3 &outCenterOfBuoyancy JPH_IF_DEBUG_RENDERER(, RVec3Arg inBaseOffset)) const override; + +#ifdef JPH_DEBUG_RENDERER + // See Shape::Draw + virtual void Draw(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inUseMaterialColors, bool inDrawWireframe) const override; + + // See Shape::DrawGetSupportFunction + virtual void DrawGetSupportFunction(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inDrawSupportDirection) const override; + + // See Shape::DrawGetSupportingFace + virtual void DrawGetSupportingFace(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale) const override; +#endif // JPH_DEBUG_RENDERER + + // See: Shape::CollideSoftBodyVertices + virtual void CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, SoftBodyVertex *ioVertices, uint inNumVertices, float inDeltaTime, Vec3Arg inDisplacementDueToGravity, int inCollidingShapeIndex) const override; + + // See Shape::TransformShape + virtual void TransformShape(Mat44Arg inCenterOfMassTransform, TransformedShapeCollector &ioCollector) const override; + + // See Shape::GetTrianglesStart + virtual void GetTrianglesStart(GetTrianglesContext &ioContext, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) const override { JPH_ASSERT(false, "Cannot call on non-leaf shapes, use CollectTransformedShapes to collect the leaves first!"); } + + // See Shape::GetTrianglesNext + virtual int GetTrianglesNext(GetTrianglesContext &ioContext, int inMaxTrianglesRequested, Float3 *outTriangleVertices, const PhysicsMaterial **outMaterials = nullptr) const override { JPH_ASSERT(false, "Cannot call on non-leaf shapes, use CollectTransformedShapes to collect the leaves first!"); return 0; } + + /// Get which sub shape's bounding boxes overlap with an axis aligned box + /// @param inBox The axis aligned box to test against (relative to the center of mass of this shape) + /// @param outSubShapeIndices Buffer where to place the indices of the sub shapes that intersect + /// @param inMaxSubShapeIndices How many indices will fit in the buffer (normally you'd provide a buffer of GetNumSubShapes() indices) + /// @return How many indices were placed in outSubShapeIndices + virtual int GetIntersectingSubShapes(const AABox &inBox, uint *outSubShapeIndices, int inMaxSubShapeIndices) const = 0; + + /// Get which sub shape's bounding boxes overlap with an axis aligned box + /// @param inBox The axis aligned box to test against (relative to the center of mass of this shape) + /// @param outSubShapeIndices Buffer where to place the indices of the sub shapes that intersect + /// @param inMaxSubShapeIndices How many indices will fit in the buffer (normally you'd provide a buffer of GetNumSubShapes() indices) + /// @return How many indices were placed in outSubShapeIndices + virtual int GetIntersectingSubShapes(const OrientedBox &inBox, uint *outSubShapeIndices, int inMaxSubShapeIndices) const = 0; + + struct SubShape + { + /// Initialize sub shape from sub shape settings + /// @param inSettings Settings object + /// @param outResult Result object, only used in case of error + /// @return True on success, false on failure + bool FromSettings(const CompoundShapeSettings::SubShapeSettings &inSettings, ShapeResult &outResult) + { + if (inSettings.mShapePtr != nullptr) + { + // Use provided shape + mShape = inSettings.mShapePtr; + } + else + { + // Create child shape + ShapeResult child_result = inSettings.mShape->Create(); + if (!child_result.IsValid()) + { + outResult = child_result; + return false; + } + mShape = child_result.Get(); + } + + // Copy user data + mUserData = inSettings.mUserData; + + SetTransform(inSettings.mPosition, inSettings.mRotation, Vec3::sZero() /* Center of mass not yet calculated */); + return true; + } + + /// Update the transform of this sub shape + /// @param inPosition New position + /// @param inRotation New orientation + /// @param inCenterOfMass The center of mass of the compound shape + JPH_INLINE void SetTransform(Vec3Arg inPosition, QuatArg inRotation, Vec3Arg inCenterOfMass) + { + SetPositionCOM(inPosition - inCenterOfMass + inRotation * mShape->GetCenterOfMass()); + + mIsRotationIdentity = inRotation.IsClose(Quat::sIdentity()) || inRotation.IsClose(-Quat::sIdentity()); + SetRotation(mIsRotationIdentity? Quat::sIdentity() : inRotation); + } + + /// Get the local transform for this shape given the scale of the child shape + /// The total transform of the child shape will be GetLocalTransformNoScale(inScale) * Mat44::sScaling(TransformScale(inScale)) + /// @param inScale The scale of the child shape (in local space of this shape) + JPH_INLINE Mat44 GetLocalTransformNoScale(Vec3Arg inScale) const + { + JPH_ASSERT(IsValidScale(inScale)); + return Mat44::sRotationTranslation(GetRotation(), inScale * GetPositionCOM()); + } + + /// Test if inScale is valid for this sub shape + inline bool IsValidScale(Vec3Arg inScale) const + { + // We can always handle uniform scale or identity rotations + if (mIsRotationIdentity || ScaleHelpers::IsUniformScale(inScale)) + return true; + + return ScaleHelpers::CanScaleBeRotated(GetRotation(), inScale); + } + + /// Transform the scale to the local space of the child shape + inline Vec3 TransformScale(Vec3Arg inScale) const + { + // We don't need to transform uniform scale or if the rotation is identity + if (mIsRotationIdentity || ScaleHelpers::IsUniformScale(inScale)) + return inScale; + + return ScaleHelpers::RotateScale(GetRotation(), inScale); + } + + /// Compress the center of mass position + JPH_INLINE void SetPositionCOM(Vec3Arg inPositionCOM) + { + inPositionCOM.StoreFloat3(&mPositionCOM); + } + + /// Uncompress the center of mass position + JPH_INLINE Vec3 GetPositionCOM() const + { + return Vec3::sLoadFloat3Unsafe(mPositionCOM); + } + + /// Compress the rotation + JPH_INLINE void SetRotation(QuatArg inRotation) + { + inRotation.StoreFloat3(&mRotation); + } + + /// Uncompress the rotation + JPH_INLINE Quat GetRotation() const + { + return mIsRotationIdentity? Quat::sIdentity() : Quat::sLoadFloat3Unsafe(mRotation); + } + + RefConst mShape; + Float3 mPositionCOM; ///< Note: Position of center of mass of sub shape! + Float3 mRotation; ///< Note: X, Y, Z of rotation quaternion - note we read 4 bytes beyond this so make sure there's something there + uint32 mUserData; ///< User data value (put here because it falls in padding bytes) + bool mIsRotationIdentity; ///< If mRotation is close to identity (put here because it falls in padding bytes) + // 3 padding bytes left + }; + + static_assert(sizeof(SubShape) == (JPH_CPU_ADDRESS_BITS == 64? 40 : 36), "Compiler added unexpected padding"); + + using SubShapes = Array; + + /// Access to the sub shapes of this compound + const SubShapes & GetSubShapes() const { return mSubShapes; } + + /// Get the total number of sub shapes + uint GetNumSubShapes() const { return uint(mSubShapes.size()); } + + /// Access to a particular sub shape + const SubShape & GetSubShape(uint inIdx) const { return mSubShapes[inIdx]; } + + /// Get the user data associated with a shape in this compound + uint32 GetCompoundUserData(uint inIdx) const { return mSubShapes[inIdx].mUserData; } + + /// Set the user data associated with a shape in this compound + void SetCompoundUserData(uint inIdx, uint32 inUserData) { mSubShapes[inIdx].mUserData = inUserData; } + + /// Check if a sub shape ID is still valid for this shape + /// @param inSubShapeID Sub shape id that indicates the leaf shape relative to this shape + /// @return True if the ID is valid, false if not + inline bool IsSubShapeIDValid(SubShapeID inSubShapeID) const + { + SubShapeID remainder; + return inSubShapeID.PopID(GetSubShapeIDBits(), remainder) < mSubShapes.size(); + } + + /// Convert SubShapeID to sub shape index + /// @param inSubShapeID Sub shape id that indicates the leaf shape relative to this shape + /// @param outRemainder This is the sub shape ID for the sub shape of the compound after popping off the index + /// @return The index of the sub shape of this compound + inline uint32 GetSubShapeIndexFromID(SubShapeID inSubShapeID, SubShapeID &outRemainder) const + { + uint32 idx = inSubShapeID.PopID(GetSubShapeIDBits(), outRemainder); + JPH_ASSERT(idx < mSubShapes.size(), "Invalid SubShapeID"); + return idx; + } + + /// @brief Convert a sub shape index to a sub shape ID + /// @param inIdx Index of the sub shape of this compound + /// @param inParentSubShapeID Parent SubShapeID (describing the path to the compound shape) + /// @return A sub shape ID creator that contains the full path to the sub shape with index inIdx + inline SubShapeIDCreator GetSubShapeIDFromIndex(int inIdx, const SubShapeIDCreator &inParentSubShapeID) const + { + return inParentSubShapeID.PushID(inIdx, GetSubShapeIDBits()); + } + + // See Shape + virtual void SaveBinaryState(StreamOut &inStream) const override; + virtual void SaveSubShapeState(ShapeList &outSubShapes) const override; + virtual void RestoreSubShapeState(const ShapeRefC *inSubShapes, uint inNumShapes) override; + + // See Shape::GetStatsRecursive + virtual Stats GetStatsRecursive(VisitedShapes &ioVisitedShapes) const override; + + // See Shape::GetVolume + virtual float GetVolume() const override; + + // See Shape::IsValidScale + virtual bool IsValidScale(Vec3Arg inScale) const override; + + // Register shape functions with the registry + static void sRegister(); + +protected: + // See: Shape::RestoreBinaryState + virtual void RestoreBinaryState(StreamIn &inStream) override; + + // Visitors for collision detection + struct CastRayVisitor; + struct CastRayVisitorCollector; + struct CollidePointVisitor; + struct CastShapeVisitor; + struct CollectTransformedShapesVisitor; + struct CollideCompoundVsShapeVisitor; + struct CollideShapeVsCompoundVisitor; + template struct GetIntersectingSubShapesVisitor; + + /// Determine amount of bits needed to encode sub shape id + inline uint GetSubShapeIDBits() const + { + // Ensure we have enough bits to encode our shape [0, n - 1] + uint32 n = uint32(mSubShapes.size()) - 1; + return 32 - CountLeadingZeros(n); + } + + /// Determine the inner radius of this shape + inline void CalculateInnerRadius() + { + mInnerRadius = FLT_MAX; + for (const SubShape &s : mSubShapes) + mInnerRadius = min(mInnerRadius, s.mShape->GetInnerRadius()); + } + + Vec3 mCenterOfMass { Vec3::sZero() }; ///< Center of mass of the compound + AABox mLocalBounds; + SubShapes mSubShapes; + float mInnerRadius = FLT_MAX; ///< Smallest radius of GetInnerRadius() of child shapes + +private: + // Helper functions called by CollisionDispatch + static void sCastCompoundVsShape(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector); +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/CompoundShapeVisitors.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/CompoundShapeVisitors.h new file mode 100644 index 00000000000..ecccb658a4f --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/CompoundShapeVisitors.h @@ -0,0 +1,460 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +struct CompoundShape::CastRayVisitor +{ + JPH_INLINE CastRayVisitor(const RayCast &inRay, const CompoundShape *inShape, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) : + mRay(inRay), + mHit(ioHit), + mSubShapeIDCreator(inSubShapeIDCreator), + mSubShapeBits(inShape->GetSubShapeIDBits()) + { + // Determine ray properties of cast + mInvDirection.Set(inRay.mDirection); + } + + /// Returns true when collision detection should abort because it's not possible to find a better hit + JPH_INLINE bool ShouldAbort() const + { + return mHit.mFraction <= 0.0f; + } + + /// Test ray against 4 bounding boxes and returns the distance where the ray enters the bounding box + JPH_INLINE Vec4 TestBounds(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ) const + { + return RayAABox4(mRay.mOrigin, mInvDirection, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ); + } + + /// Test the ray against a single subshape + JPH_INLINE void VisitShape(const SubShape &inSubShape, uint32 inSubShapeIndex) + { + // Create ID for sub shape + SubShapeIDCreator shape2_sub_shape_id = mSubShapeIDCreator.PushID(inSubShapeIndex, mSubShapeBits); + + // Transform the ray + Mat44 transform = Mat44::sInverseRotationTranslation(inSubShape.GetRotation(), inSubShape.GetPositionCOM()); + RayCast ray = mRay.Transformed(transform); + if (inSubShape.mShape->CastRay(ray, shape2_sub_shape_id, mHit)) + mReturnValue = true; + } + + RayInvDirection mInvDirection; + const RayCast & mRay; + RayCastResult & mHit; + SubShapeIDCreator mSubShapeIDCreator; + uint mSubShapeBits; + bool mReturnValue = false; +}; + +struct CompoundShape::CastRayVisitorCollector +{ + JPH_INLINE CastRayVisitorCollector(const RayCast &inRay, const RayCastSettings &inRayCastSettings, const CompoundShape *inShape, const SubShapeIDCreator &inSubShapeIDCreator, CastRayCollector &ioCollector, const ShapeFilter &inShapeFilter) : + mRay(inRay), + mCollector(ioCollector), + mSubShapeIDCreator(inSubShapeIDCreator), + mSubShapeBits(inShape->GetSubShapeIDBits()), + mRayCastSettings(inRayCastSettings), + mShapeFilter(inShapeFilter) + { + // Determine ray properties of cast + mInvDirection.Set(inRay.mDirection); + } + + /// Returns true when collision detection should abort because it's not possible to find a better hit + JPH_INLINE bool ShouldAbort() const + { + return mCollector.ShouldEarlyOut(); + } + + /// Test ray against 4 bounding boxes and returns the distance where the ray enters the bounding box + JPH_INLINE Vec4 TestBounds(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ) const + { + return RayAABox4(mRay.mOrigin, mInvDirection, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ); + } + + /// Test the ray against a single subshape + JPH_INLINE void VisitShape(const SubShape &inSubShape, uint32 inSubShapeIndex) + { + // Create ID for sub shape + SubShapeIDCreator shape2_sub_shape_id = mSubShapeIDCreator.PushID(inSubShapeIndex, mSubShapeBits); + + // Transform the ray + Mat44 transform = Mat44::sInverseRotationTranslation(inSubShape.GetRotation(), inSubShape.GetPositionCOM()); + RayCast ray = mRay.Transformed(transform); + inSubShape.mShape->CastRay(ray, mRayCastSettings, shape2_sub_shape_id, mCollector, mShapeFilter); + } + + RayInvDirection mInvDirection; + const RayCast & mRay; + CastRayCollector & mCollector; + SubShapeIDCreator mSubShapeIDCreator; + uint mSubShapeBits; + RayCastSettings mRayCastSettings; + const ShapeFilter & mShapeFilter; +}; + +struct CompoundShape::CollidePointVisitor +{ + JPH_INLINE CollidePointVisitor(Vec3Arg inPoint, const CompoundShape *inShape, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter) : + mPoint(inPoint), + mSubShapeIDCreator(inSubShapeIDCreator), + mCollector(ioCollector), + mSubShapeBits(inShape->GetSubShapeIDBits()), + mShapeFilter(inShapeFilter) + { + } + + /// Returns true when collision detection should abort because it's not possible to find a better hit + JPH_INLINE bool ShouldAbort() const + { + return mCollector.ShouldEarlyOut(); + } + + /// Test if point overlaps with 4 boxes, returns true for the ones that do + JPH_INLINE UVec4 TestBounds(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ) const + { + return AABox4VsPoint(mPoint, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ); + } + + /// Test the point against a single subshape + JPH_INLINE void VisitShape(const SubShape &inSubShape, uint32 inSubShapeIndex) + { + // Create ID for sub shape + SubShapeIDCreator shape2_sub_shape_id = mSubShapeIDCreator.PushID(inSubShapeIndex, mSubShapeBits); + + // Transform the point + Mat44 transform = Mat44::sInverseRotationTranslation(inSubShape.GetRotation(), inSubShape.GetPositionCOM()); + inSubShape.mShape->CollidePoint(transform * mPoint, shape2_sub_shape_id, mCollector, mShapeFilter); + } + + Vec3 mPoint; + SubShapeIDCreator mSubShapeIDCreator; + CollidePointCollector & mCollector; + uint mSubShapeBits; + const ShapeFilter & mShapeFilter; +}; + +struct CompoundShape::CastShapeVisitor +{ + JPH_INLINE CastShapeVisitor(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const CompoundShape *inShape, Vec3Arg inScale, const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector) : + mBoxCenter(inShapeCast.mShapeWorldBounds.GetCenter()), + mBoxExtent(inShapeCast.mShapeWorldBounds.GetExtent()), + mScale(inScale), + mShapeCast(inShapeCast), + mShapeCastSettings(inShapeCastSettings), + mShapeFilter(inShapeFilter), + mCollector(ioCollector), + mCenterOfMassTransform2(inCenterOfMassTransform2), + mSubShapeIDCreator1(inSubShapeIDCreator1), + mSubShapeIDCreator2(inSubShapeIDCreator2), + mSubShapeBits(inShape->GetSubShapeIDBits()) + { + // Determine ray properties of cast + mInvDirection.Set(inShapeCast.mDirection); + } + + /// Returns true when collision detection should abort because it's not possible to find a better hit + JPH_INLINE bool ShouldAbort() const + { + return mCollector.ShouldEarlyOut(); + } + + /// Tests the shape cast against 4 bounding boxes, returns the distance along the shape cast where the shape first enters the bounding box + JPH_INLINE Vec4 TestBounds(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ) const + { + // Scale the bounding boxes + Vec4 bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z; + AABox4Scale(mScale, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z); + + // Enlarge them by the casted shape's box extents + AABox4EnlargeWithExtent(mBoxExtent, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z); + + // Test ray against the bounding boxes + return RayAABox4(mBoxCenter, mInvDirection, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z); + } + + /// Test the cast shape against a single subshape + JPH_INLINE void VisitShape(const SubShape &inSubShape, uint32 inSubShapeIndex) + { + JPH_ASSERT(inSubShape.IsValidScale(mScale)); + + // Create ID for sub shape + SubShapeIDCreator shape2_sub_shape_id = mSubShapeIDCreator2.PushID(inSubShapeIndex, mSubShapeBits); + + // Calculate the local transform for this sub shape + Mat44 local_transform = Mat44::sRotationTranslation(inSubShape.GetRotation(), mScale * inSubShape.GetPositionCOM()); + + // Transform the center of mass of 2 + Mat44 center_of_mass_transform2 = mCenterOfMassTransform2 * local_transform; + + // Transform the shape cast + ShapeCast shape_cast = mShapeCast.PostTransformed(local_transform.InversedRotationTranslation()); + + CollisionDispatch::sCastShapeVsShapeLocalSpace(shape_cast, mShapeCastSettings, inSubShape.mShape, inSubShape.TransformScale(mScale), mShapeFilter, center_of_mass_transform2, mSubShapeIDCreator1, shape2_sub_shape_id, mCollector); + } + + RayInvDirection mInvDirection; + Vec3 mBoxCenter; + Vec3 mBoxExtent; + Vec3 mScale; + const ShapeCast & mShapeCast; + const ShapeCastSettings & mShapeCastSettings; + const ShapeFilter & mShapeFilter; + CastShapeCollector & mCollector; + Mat44 mCenterOfMassTransform2; + SubShapeIDCreator mSubShapeIDCreator1; + SubShapeIDCreator mSubShapeIDCreator2; + uint mSubShapeBits; +}; + +struct CompoundShape::CollectTransformedShapesVisitor +{ + JPH_INLINE CollectTransformedShapesVisitor(const AABox &inBox, const CompoundShape *inShape, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale, const SubShapeIDCreator &inSubShapeIDCreator, TransformedShapeCollector &ioCollector, const ShapeFilter &inShapeFilter) : + mBox(inBox), + mLocalBox(Mat44::sInverseRotationTranslation(inRotation, inPositionCOM), inBox), + mPositionCOM(inPositionCOM), + mRotation(inRotation), + mScale(inScale), + mSubShapeIDCreator(inSubShapeIDCreator), + mCollector(ioCollector), + mSubShapeBits(inShape->GetSubShapeIDBits()), + mShapeFilter(inShapeFilter) + { + } + + /// Returns true when collision detection should abort because it's not possible to find a better hit + JPH_INLINE bool ShouldAbort() const + { + return mCollector.ShouldEarlyOut(); + } + + /// Tests 4 bounding boxes against the query box, returns true for the ones that collide + JPH_INLINE UVec4 TestBounds(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ) const + { + // Scale the bounding boxes of this node + Vec4 bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z; + AABox4Scale(mScale, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z); + + // Test which nodes collide + return AABox4VsBox(mLocalBox, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z); + } + + /// Collect the transformed sub shapes for a single subshape + JPH_INLINE void VisitShape(const SubShape &inSubShape, uint32 inSubShapeIndex) + { + JPH_ASSERT(inSubShape.IsValidScale(mScale)); + + // Create ID for sub shape + SubShapeIDCreator sub_shape_id = mSubShapeIDCreator.PushID(inSubShapeIndex, mSubShapeBits); + + // Calculate world transform for sub shape + Vec3 position = mPositionCOM + mRotation * (mScale * inSubShape.GetPositionCOM()); + Quat rotation = mRotation * inSubShape.GetRotation(); + + // Recurse to sub shape + inSubShape.mShape->CollectTransformedShapes(mBox, position, rotation, inSubShape.TransformScale(mScale), sub_shape_id, mCollector, mShapeFilter); + } + + AABox mBox; + OrientedBox mLocalBox; + Vec3 mPositionCOM; + Quat mRotation; + Vec3 mScale; + SubShapeIDCreator mSubShapeIDCreator; + TransformedShapeCollector & mCollector; + uint mSubShapeBits; + const ShapeFilter & mShapeFilter; +}; + +struct CompoundShape::CollideCompoundVsShapeVisitor +{ + JPH_INLINE CollideCompoundVsShapeVisitor(const CompoundShape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter) : + mCollideShapeSettings(inCollideShapeSettings), + mCollector(ioCollector), + mShape2(inShape2), + mScale1(inScale1), + mScale2(inScale2), + mTransform1(inCenterOfMassTransform1), + mTransform2(inCenterOfMassTransform2), + mSubShapeIDCreator1(inSubShapeIDCreator1), + mSubShapeIDCreator2(inSubShapeIDCreator2), + mSubShapeBits(inShape1->GetSubShapeIDBits()), + mShapeFilter(inShapeFilter) + { + // Get transform from shape 2 to shape 1 + Mat44 transform2_to_1 = inCenterOfMassTransform1.InversedRotationTranslation() * inCenterOfMassTransform2; + + // Convert bounding box of 2 into space of 1 + mBoundsOf2InSpaceOf1 = inShape2->GetLocalBounds().Scaled(inScale2).Transformed(transform2_to_1); + } + + /// Returns true when collision detection should abort because it's not possible to find a better hit + JPH_INLINE bool ShouldAbort() const + { + return mCollector.ShouldEarlyOut(); + } + + /// Tests the bounds of shape 2 vs 4 bounding boxes, returns true for the ones that intersect + JPH_INLINE UVec4 TestBounds(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ) const + { + // Scale the bounding boxes + Vec4 bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z; + AABox4Scale(mScale1, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z); + + // Test which boxes collide + return AABox4VsBox(mBoundsOf2InSpaceOf1, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z); + } + + /// Test the shape against a single subshape + JPH_INLINE void VisitShape(const SubShape &inSubShape, uint32 inSubShapeIndex) + { + // Get world transform of 1 + Mat44 transform1 = mTransform1 * inSubShape.GetLocalTransformNoScale(mScale1); + + // Create ID for sub shape + SubShapeIDCreator shape1_sub_shape_id = mSubShapeIDCreator1.PushID(inSubShapeIndex, mSubShapeBits); + + CollisionDispatch::sCollideShapeVsShape(inSubShape.mShape, mShape2, inSubShape.TransformScale(mScale1), mScale2, transform1, mTransform2, shape1_sub_shape_id, mSubShapeIDCreator2, mCollideShapeSettings, mCollector, mShapeFilter); + } + + const CollideShapeSettings & mCollideShapeSettings; + CollideShapeCollector & mCollector; + const Shape * mShape2; + Vec3 mScale1; + Vec3 mScale2; + Mat44 mTransform1; + Mat44 mTransform2; + AABox mBoundsOf2InSpaceOf1; + SubShapeIDCreator mSubShapeIDCreator1; + SubShapeIDCreator mSubShapeIDCreator2; + uint mSubShapeBits; + const ShapeFilter & mShapeFilter; +}; + +struct CompoundShape::CollideShapeVsCompoundVisitor +{ + JPH_INLINE CollideShapeVsCompoundVisitor(const Shape *inShape1, const CompoundShape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter) : + mCollideShapeSettings(inCollideShapeSettings), + mCollector(ioCollector), + mShape1(inShape1), + mScale1(inScale1), + mScale2(inScale2), + mTransform1(inCenterOfMassTransform1), + mTransform2(inCenterOfMassTransform2), + mSubShapeIDCreator1(inSubShapeIDCreator1), + mSubShapeIDCreator2(inSubShapeIDCreator2), + mSubShapeBits(inShape2->GetSubShapeIDBits()), + mShapeFilter(inShapeFilter) + { + // Get transform from shape 1 to shape 2 + Mat44 transform1_to_2 = inCenterOfMassTransform2.InversedRotationTranslation() * inCenterOfMassTransform1; + + // Convert bounding box of 1 into space of 2 + mBoundsOf1InSpaceOf2 = inShape1->GetLocalBounds().Scaled(inScale1).Transformed(transform1_to_2); + mBoundsOf1InSpaceOf2.ExpandBy(Vec3::sReplicate(inCollideShapeSettings.mMaxSeparationDistance)); + } + + /// Returns true when collision detection should abort because it's not possible to find a better hit + JPH_INLINE bool ShouldAbort() const + { + return mCollector.ShouldEarlyOut(); + } + + /// Tests the bounds of shape 1 vs 4 bounding boxes, returns true for the ones that intersect + JPH_INLINE UVec4 TestBounds(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ) const + { + // Scale the bounding boxes + Vec4 bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z; + AABox4Scale(mScale2, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z); + + // Test which bounding boxes collide + return AABox4VsBox(mBoundsOf1InSpaceOf2, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z); + } + + /// Test the shape against a single subshape + JPH_INLINE void VisitShape(const SubShape &inSubShape, uint32 inSubShapeIndex) + { + // Create ID for sub shape + SubShapeIDCreator shape2_sub_shape_id = mSubShapeIDCreator2.PushID(inSubShapeIndex, mSubShapeBits); + + // Get world transform of 2 + Mat44 transform2 = mTransform2 * inSubShape.GetLocalTransformNoScale(mScale2); + + CollisionDispatch::sCollideShapeVsShape(mShape1, inSubShape.mShape, mScale1, inSubShape.TransformScale(mScale2), mTransform1, transform2, mSubShapeIDCreator1, shape2_sub_shape_id, mCollideShapeSettings, mCollector, mShapeFilter); + } + + const CollideShapeSettings & mCollideShapeSettings; + CollideShapeCollector & mCollector; + const Shape * mShape1; + Vec3 mScale1; + Vec3 mScale2; + Mat44 mTransform1; + Mat44 mTransform2; + AABox mBoundsOf1InSpaceOf2; + SubShapeIDCreator mSubShapeIDCreator1; + SubShapeIDCreator mSubShapeIDCreator2; + uint mSubShapeBits; + const ShapeFilter & mShapeFilter; +}; + +template +struct CompoundShape::GetIntersectingSubShapesVisitor +{ + JPH_INLINE GetIntersectingSubShapesVisitor(const BoxType &inBox, uint *outSubShapeIndices, int inMaxSubShapeIndices) : + mBox(inBox), + mSubShapeIndices(outSubShapeIndices), + mMaxSubShapeIndices(inMaxSubShapeIndices) + { + } + + /// Returns true when collision detection should abort because the buffer is full + JPH_INLINE bool ShouldAbort() const + { + return mNumResults >= mMaxSubShapeIndices; + } + + /// Tests the box vs 4 bounding boxes, returns true for the ones that intersect + JPH_INLINE UVec4 TestBounds(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ) const + { + // Test which bounding boxes collide + return AABox4VsBox(mBox, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ); + } + + /// Records a hit + JPH_INLINE void VisitShape([[maybe_unused]] const SubShape &inSubShape, uint32 inSubShapeIndex) + { + JPH_ASSERT(mNumResults < mMaxSubShapeIndices); + *mSubShapeIndices++ = inSubShapeIndex; + mNumResults++; + } + + /// Get the number of indices that were found + JPH_INLINE int GetNumResults() const + { + return mNumResults; + } + +private: + BoxType mBox; + uint * mSubShapeIndices; + int mMaxSubShapeIndices; + int mNumResults = 0; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/ConvexHullShape.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/ConvexHullShape.cpp new file mode 100644 index 00000000000..d1de637173f --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/ConvexHullShape.cpp @@ -0,0 +1,1311 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(ConvexHullShapeSettings) +{ + JPH_ADD_BASE_CLASS(ConvexHullShapeSettings, ConvexShapeSettings) + + JPH_ADD_ATTRIBUTE(ConvexHullShapeSettings, mPoints) + JPH_ADD_ATTRIBUTE(ConvexHullShapeSettings, mMaxConvexRadius) + JPH_ADD_ATTRIBUTE(ConvexHullShapeSettings, mMaxErrorConvexRadius) + JPH_ADD_ATTRIBUTE(ConvexHullShapeSettings, mHullTolerance) +} + +ShapeSettings::ShapeResult ConvexHullShapeSettings::Create() const +{ + if (mCachedResult.IsEmpty()) + Ref shape = new ConvexHullShape(*this, mCachedResult); + return mCachedResult; +} + +ConvexHullShape::ConvexHullShape(const ConvexHullShapeSettings &inSettings, ShapeResult &outResult) : + ConvexShape(EShapeSubType::ConvexHull, inSettings, outResult), + mConvexRadius(inSettings.mMaxConvexRadius) +{ + using BuilderFace = ConvexHullBuilder::Face; + using Edge = ConvexHullBuilder::Edge; + using Faces = Array; + + // Check convex radius + if (mConvexRadius < 0.0f) + { + outResult.SetError("Invalid convex radius"); + return; + } + + // Build convex hull + const char *error = nullptr; + ConvexHullBuilder builder(inSettings.mPoints); + ConvexHullBuilder::EResult result = builder.Initialize(cMaxPointsInHull, inSettings.mHullTolerance, error); + if (result != ConvexHullBuilder::EResult::Success && result != ConvexHullBuilder::EResult::MaxVerticesReached) + { + outResult.SetError(error); + return; + } + const Faces &builder_faces = builder.GetFaces(); + + // Check the consistency of the resulting hull if we fully built it + if (result == ConvexHullBuilder::EResult::Success) + { + ConvexHullBuilder::Face *max_error_face; + float max_error_distance, coplanar_distance; + int max_error_idx; + builder.DetermineMaxError(max_error_face, max_error_distance, max_error_idx, coplanar_distance); + if (max_error_distance > 4.0f * max(coplanar_distance, inSettings.mHullTolerance)) // Coplanar distance could be bigger than the allowed tolerance if the points are far apart + { + outResult.SetError(StringFormat("Hull building failed, point %d had an error of %g (relative to tolerance: %g)", max_error_idx, (double)max_error_distance, double(max_error_distance / inSettings.mHullTolerance))); + return; + } + } + + // Calculate center of mass and volume + builder.GetCenterOfMassAndVolume(mCenterOfMass, mVolume); + + // Calculate covariance matrix + // See: + // - Why the inertia tensor is the inertia tensor - Jonathan Blow (http://number-none.com/blow/inertia/deriving_i.html) + // - How to find the inertia tensor (or other mass properties) of a 3D solid body represented by a triangle mesh (Draft) - Jonathan Blow, Atman J Binstock (http://number-none.com/blow/inertia/bb_inertia.doc) + Mat44 covariance_canonical(Vec4(1.0f / 60.0f, 1.0f / 120.0f, 1.0f / 120.0f, 0), Vec4(1.0f / 120.0f, 1.0f / 60.0f, 1.0f / 120.0f, 0), Vec4(1.0f / 120.0f, 1.0f / 120.0f, 1.0f / 60.0f, 0), Vec4(0, 0, 0, 1)); + Mat44 covariance_matrix = Mat44::sZero(); + for (BuilderFace *f : builder_faces) + { + // Fourth point of the tetrahedron is at the center of mass, we subtract it from the other points so we get a tetrahedron with one vertex at zero + // The first point on the face will be used to form a triangle fan + Edge *e = f->mFirstEdge; + Vec3 v1 = inSettings.mPoints[e->mStartIdx] - mCenterOfMass; + + // Get the 2nd point + e = e->mNextEdge; + Vec3 v2 = inSettings.mPoints[e->mStartIdx] - mCenterOfMass; + + // Loop over the triangle fan + for (e = e->mNextEdge; e != f->mFirstEdge; e = e->mNextEdge) + { + Vec3 v3 = inSettings.mPoints[e->mStartIdx] - mCenterOfMass; + + // Affine transform that transforms a unit tetrahedon (with vertices (0, 0, 0), (1, 0, 0), (0, 1, 0) and (0, 0, 1) to this tetrahedron + Mat44 a(Vec4(v1, 0), Vec4(v2, 0), Vec4(v3, 0), Vec4(0, 0, 0, 1)); + + // Calculate covariance matrix for this tetrahedron + float det_a = a.GetDeterminant3x3(); + Mat44 c = det_a * (a * covariance_canonical * a.Transposed()); + + // Add it + covariance_matrix += c; + + // Prepare for next triangle + v2 = v3; + } + } + + // Calculate inertia matrix assuming density is 1, note that element (3, 3) is garbage + mInertia = Mat44::sIdentity() * (covariance_matrix(0, 0) + covariance_matrix(1, 1) + covariance_matrix(2, 2)) - covariance_matrix; + + // Convert polygons from the builder to our internal representation + using VtxMap = UnorderedMap; + VtxMap vertex_map; + for (BuilderFace *builder_face : builder_faces) + { + // Determine where the vertices go + JPH_ASSERT(mVertexIdx.size() <= 0xFFFF); + uint16 first_vertex = (uint16)mVertexIdx.size(); + uint16 num_vertices = 0; + + // Loop over vertices in face + Edge *edge = builder_face->mFirstEdge; + do + { + // Remap to new index, not all points in the original input set are required to form the hull + uint8 new_idx; + int original_idx = edge->mStartIdx; + VtxMap::iterator m = vertex_map.find(original_idx); + if (m != vertex_map.end()) + { + // Found, reuse + new_idx = m->second; + } + else + { + // This is a new point + // Make relative to center of mass + Vec3 p = inSettings.mPoints[original_idx] - mCenterOfMass; + + // Update local bounds + mLocalBounds.Encapsulate(p); + + // Add to point list + JPH_ASSERT(mPoints.size() <= 0xff); + new_idx = (uint8)mPoints.size(); + mPoints.push_back({ p }); + vertex_map[original_idx] = new_idx; + } + + // Append to vertex list + JPH_ASSERT(mVertexIdx.size() < 0xffff); + mVertexIdx.push_back(new_idx); + num_vertices++; + + edge = edge->mNextEdge; + } while (edge != builder_face->mFirstEdge); + + // Add face + mFaces.push_back({ first_vertex, num_vertices }); + + // Add plane + Plane plane = Plane::sFromPointAndNormal(builder_face->mCentroid - mCenterOfMass, builder_face->mNormal.Normalized()); + mPlanes.push_back(plane); + } + + // Test if GetSupportFunction can support this many points + if (mPoints.size() > cMaxPointsInHull) + { + outResult.SetError(StringFormat("Internal error: Too many points in hull (%u), max allowed %d", (uint)mPoints.size(), cMaxPointsInHull)); + return; + } + + for (int p = 0; p < (int)mPoints.size(); ++p) + { + // For each point, find faces that use the point + Array faces; + for (int f = 0; f < (int)mFaces.size(); ++f) + { + const Face &face = mFaces[f]; + for (int v = 0; v < face.mNumVertices; ++v) + if (mVertexIdx[face.mFirstVertex + v] == p) + { + faces.push_back(f); + break; + } + } + + if (faces.size() < 2) + { + outResult.SetError("A point must be connected to 2 or more faces!"); + return; + } + + // Find the 3 normals that form the largest tetrahedron + // The largest tetrahedron we can get is ((1, 0, 0) x (0, 1, 0)) . (0, 0, 1) = 1, if the volume is only 5% of that, + // the three vectors are too coplanar and we fall back to using only 2 plane normals + float biggest_volume = 0.05f; + int best3[3] = { -1, -1, -1 }; + + // When using 2 normals, we get the two with the biggest angle between them with a minimal difference of 1 degree + // otherwise we fall back to just using 1 plane normal + float smallest_dot = Cos(DegreesToRadians(1.0f)); + int best2[2] = { -1, -1 }; + + for (int face1 = 0; face1 < (int)faces.size(); ++face1) + { + Vec3 normal1 = mPlanes[faces[face1]].GetNormal(); + for (int face2 = face1 + 1; face2 < (int)faces.size(); ++face2) + { + Vec3 normal2 = mPlanes[faces[face2]].GetNormal(); + Vec3 cross = normal1.Cross(normal2); + + // Determine the 2 face normals that are most apart + float dot = normal1.Dot(normal2); + if (dot < smallest_dot) + { + smallest_dot = dot; + best2[0] = faces[face1]; + best2[1] = faces[face2]; + } + + // Determine the 3 face normals that form the largest tetrahedron + for (int face3 = face2 + 1; face3 < (int)faces.size(); ++face3) + { + Vec3 normal3 = mPlanes[faces[face3]].GetNormal(); + float volume = abs(cross.Dot(normal3)); + if (volume > biggest_volume) + { + biggest_volume = volume; + best3[0] = faces[face1]; + best3[1] = faces[face2]; + best3[2] = faces[face3]; + } + } + } + } + + // If we didn't find 3 planes, use 2, if we didn't find 2 use 1 + if (best3[0] != -1) + faces = { best3[0], best3[1], best3[2] }; + else if (best2[0] != -1) + faces = { best2[0], best2[1] }; + else + faces = { faces[0] }; + + // Copy the faces to the points buffer + Point &point = mPoints[p]; + point.mNumFaces = (int)faces.size(); + for (int i = 0; i < (int)faces.size(); ++i) + point.mFaces[i] = faces[i]; + } + + // If the convex radius is already zero, there's no point in further reducing it + if (mConvexRadius > 0.0f) + { + // Find out how thin the hull is by walking over all planes and checking the thickness of the hull in that direction + float min_size = FLT_MAX; + for (const Plane &plane : mPlanes) + { + // Take the point that is furthest away from the plane as thickness of this hull + float max_dist = 0.0f; + for (const Point &point : mPoints) + { + float dist = -plane.SignedDistance(point.mPosition); // Point is always behind plane, so we need to negate + if (dist > max_dist) + max_dist = dist; + } + min_size = min(min_size, max_dist); + } + + // We need to fit in 2x the convex radius in min_size, so reduce the convex radius if it's bigger than that + mConvexRadius = min(mConvexRadius, 0.5f * min_size); + } + + // Now walk over all points and see if we have to further reduce the convex radius because of sharp edges + if (mConvexRadius > 0.0f) + { + for (const Point &point : mPoints) + if (point.mNumFaces != 1) // If we have a single face, shifting back is easy and we don't need to reduce the convex radius + { + // Get first two planes + Plane p1 = mPlanes[point.mFaces[0]]; + Plane p2 = mPlanes[point.mFaces[1]]; + Plane p3; + Vec3 offset_mask; + + if (point.mNumFaces == 3) + { + // Get third plane + p3 = mPlanes[point.mFaces[2]]; + + // All 3 planes will be offset by the convex radius + offset_mask = Vec3::sReplicate(1); + } + else + { + // Third plane has normal perpendicular to the other two planes and goes through the vertex position + JPH_ASSERT(point.mNumFaces == 2); + p3 = Plane::sFromPointAndNormal(point.mPosition, p1.GetNormal().Cross(p2.GetNormal())); + + // Only the first and 2nd plane will be offset, the 3rd plane is only there to guide the intersection point + offset_mask = Vec3(1, 1, 0); + } + + // Plane equation: point . normal + constant = 0 + // Offsetting the plane backwards with convex radius r: point . normal + constant + r = 0 + // To find the intersection 'point' of 3 planes we solve: + // |n1x n1y n1z| |x| | r + c1 | + // |n2x n2y n2z| |y| = - | r + c2 | <=> n point = -r (1, 1, 1) - (c1, c2, c3) + // |n3x n3y n3z| |z| | r + c3 | + // Where point = (x, y, z), n1x is the x component of the first plane, c1 = plane constant of plane 1, etc. + // The relation between how much the intersection point shifts as a function of r is: -r * n^-1 (1, 1, 1) = r * offset + // Where offset = -n^-1 (1, 1, 1) or -n^-1 (1, 1, 0) in case only the first 2 planes are offset + // The error that is introduced by a convex radius r is: error = r * |offset| - r + // So the max convex radius given error is: r = error / (|offset| - 1) + Mat44 n = Mat44(Vec4(p1.GetNormal(), 0), Vec4(p2.GetNormal(), 0), Vec4(p3.GetNormal(), 0), Vec4(0, 0, 0, 1)).Transposed(); + float det_n = n.GetDeterminant3x3(); + if (det_n == 0.0f) + { + // If the determinant is zero, the matrix is not invertible so no solution exists to move the point backwards and we have to choose a convex radius of zero + mConvexRadius = 0.0f; + break; + } + Mat44 adj_n = n.Adjointed3x3(); + float offset = ((adj_n * offset_mask) / det_n).Length(); + JPH_ASSERT(offset > 1.0f); + float max_convex_radius = inSettings.mMaxErrorConvexRadius / (offset - 1.0f); + mConvexRadius = min(mConvexRadius, max_convex_radius); + } + } + + // Calculate the inner radius by getting the minimum distance from the origin to the planes of the hull + mInnerRadius = FLT_MAX; + for (const Plane &p : mPlanes) + mInnerRadius = min(mInnerRadius, -p.GetConstant()); + mInnerRadius = max(0.0f, mInnerRadius); // Clamp against zero, this should do nothing as the shape is centered around the center of mass but for flat convex hulls there may be numerical round off issues + + outResult.Set(this); +} + +MassProperties ConvexHullShape::GetMassProperties() const +{ + MassProperties p; + + float density = GetDensity(); + + // Calculate mass + p.mMass = density * mVolume; + + // Calculate inertia matrix + p.mInertia = density * mInertia; + p.mInertia(3, 3) = 1.0f; + + return p; +} + +Vec3 ConvexHullShape::GetSurfaceNormal(const SubShapeID &inSubShapeID, Vec3Arg inLocalSurfacePosition) const +{ + JPH_ASSERT(inSubShapeID.IsEmpty(), "Invalid subshape ID"); + + const Plane &first_plane = mPlanes[0]; + Vec3 best_normal = first_plane.GetNormal(); + float best_dist = abs(first_plane.SignedDistance(inLocalSurfacePosition)); + + // Find the face that has the shortest distance to the surface point + for (Array::size_type i = 1; i < mFaces.size(); ++i) + { + const Plane &plane = mPlanes[i]; + Vec3 plane_normal = plane.GetNormal(); + float dist = abs(plane.SignedDistance(inLocalSurfacePosition)); + if (dist < best_dist) + { + best_dist = dist; + best_normal = plane_normal; + } + } + + return best_normal; +} + +class ConvexHullShape::HullNoConvex final : public Support +{ +public: + explicit HullNoConvex(float inConvexRadius) : + mConvexRadius(inConvexRadius) + { + static_assert(sizeof(HullNoConvex) <= sizeof(SupportBuffer), "Buffer size too small"); + JPH_ASSERT(IsAligned(this, alignof(HullNoConvex))); + } + + virtual Vec3 GetSupport(Vec3Arg inDirection) const override + { + // Find the point with the highest projection on inDirection + float best_dot = -FLT_MAX; + Vec3 best_point = Vec3::sZero(); + + for (Vec3 point : mPoints) + { + // Check if its support is bigger than the current max + float dot = point.Dot(inDirection); + if (dot > best_dot) + { + best_dot = dot; + best_point = point; + } + } + + return best_point; + } + + virtual float GetConvexRadius() const override + { + return mConvexRadius; + } + + using PointsArray = StaticArray; + + inline PointsArray & GetPoints() + { + return mPoints; + } + + const PointsArray & GetPoints() const + { + return mPoints; + } + +private: + float mConvexRadius; + PointsArray mPoints; +}; + +class ConvexHullShape::HullWithConvex final : public Support +{ +public: + explicit HullWithConvex(const ConvexHullShape *inShape) : + mShape(inShape) + { + static_assert(sizeof(HullWithConvex) <= sizeof(SupportBuffer), "Buffer size too small"); + JPH_ASSERT(IsAligned(this, alignof(HullWithConvex))); + } + + virtual Vec3 GetSupport(Vec3Arg inDirection) const override + { + // Find the point with the highest projection on inDirection + float best_dot = -FLT_MAX; + Vec3 best_point = Vec3::sZero(); + + for (const Point &point : mShape->mPoints) + { + // Check if its support is bigger than the current max + float dot = point.mPosition.Dot(inDirection); + if (dot > best_dot) + { + best_dot = dot; + best_point = point.mPosition; + } + } + + return best_point; + } + + virtual float GetConvexRadius() const override + { + return 0.0f; + } + +private: + const ConvexHullShape * mShape; +}; + +class ConvexHullShape::HullWithConvexScaled final : public Support +{ +public: + HullWithConvexScaled(const ConvexHullShape *inShape, Vec3Arg inScale) : + mShape(inShape), + mScale(inScale) + { + static_assert(sizeof(HullWithConvexScaled) <= sizeof(SupportBuffer), "Buffer size too small"); + JPH_ASSERT(IsAligned(this, alignof(HullWithConvexScaled))); + } + + virtual Vec3 GetSupport(Vec3Arg inDirection) const override + { + // Find the point with the highest projection on inDirection + float best_dot = -FLT_MAX; + Vec3 best_point = Vec3::sZero(); + + for (const Point &point : mShape->mPoints) + { + // Calculate scaled position + Vec3 pos = mScale * point.mPosition; + + // Check if its support is bigger than the current max + float dot = pos.Dot(inDirection); + if (dot > best_dot) + { + best_dot = dot; + best_point = pos; + } + } + + return best_point; + } + + virtual float GetConvexRadius() const override + { + return 0.0f; + } + +private: + const ConvexHullShape * mShape; + Vec3 mScale; +}; + +const ConvexShape::Support *ConvexHullShape::GetSupportFunction(ESupportMode inMode, SupportBuffer &inBuffer, Vec3Arg inScale) const +{ + // If there's no convex radius, we don't need to shrink the hull + if (mConvexRadius == 0.0f) + { + if (ScaleHelpers::IsNotScaled(inScale)) + return new (&inBuffer) HullWithConvex(this); + else + return new (&inBuffer) HullWithConvexScaled(this, inScale); + } + + switch (inMode) + { + case ESupportMode::IncludeConvexRadius: + case ESupportMode::Default: + if (ScaleHelpers::IsNotScaled(inScale)) + return new (&inBuffer) HullWithConvex(this); + else + return new (&inBuffer) HullWithConvexScaled(this, inScale); + + case ESupportMode::ExcludeConvexRadius: + if (ScaleHelpers::IsNotScaled(inScale)) + { + // Create support function + HullNoConvex *hull = new (&inBuffer) HullNoConvex(mConvexRadius); + HullNoConvex::PointsArray &transformed_points = hull->GetPoints(); + JPH_ASSERT(mPoints.size() <= cMaxPointsInHull, "Not enough space, this should have been caught during shape creation!"); + + for (const Point &point : mPoints) + { + Vec3 new_point; + + if (point.mNumFaces == 1) + { + // Simply shift back by the convex radius using our 1 plane + new_point = point.mPosition - mPlanes[point.mFaces[0]].GetNormal() * mConvexRadius; + } + else + { + // Get first two planes and offset inwards by convex radius + Plane p1 = mPlanes[point.mFaces[0]].Offset(-mConvexRadius); + Plane p2 = mPlanes[point.mFaces[1]].Offset(-mConvexRadius); + Plane p3; + + if (point.mNumFaces == 3) + { + // Get third plane and offset inwards by convex radius + p3 = mPlanes[point.mFaces[2]].Offset(-mConvexRadius); + } + else + { + // Third plane has normal perpendicular to the other two planes and goes through the vertex position + JPH_ASSERT(point.mNumFaces == 2); + p3 = Plane::sFromPointAndNormal(point.mPosition, p1.GetNormal().Cross(p2.GetNormal())); + } + + // Find intersection point between the three planes + if (!Plane::sIntersectPlanes(p1, p2, p3, new_point)) + { + // Fallback: Just push point back using the first plane + new_point = point.mPosition - p1.GetNormal() * mConvexRadius; + } + } + + // Add point + transformed_points.push_back(new_point); + } + + return hull; + } + else + { + // Calculate scaled convex radius + float convex_radius = ScaleHelpers::ScaleConvexRadius(mConvexRadius, inScale); + + // Create new support function + HullNoConvex *hull = new (&inBuffer) HullNoConvex(convex_radius); + HullNoConvex::PointsArray &transformed_points = hull->GetPoints(); + JPH_ASSERT(mPoints.size() <= cMaxPointsInHull, "Not enough space, this should have been caught during shape creation!"); + + // Precalculate inverse scale + Vec3 inv_scale = inScale.Reciprocal(); + + for (const Point &point : mPoints) + { + // Calculate scaled position + Vec3 pos = inScale * point.mPosition; + + // Transform normals for plane 1 with scale + Vec3 n1 = (inv_scale * mPlanes[point.mFaces[0]].GetNormal()).Normalized(); + + Vec3 new_point; + + if (point.mNumFaces == 1) + { + // Simply shift back by the convex radius using our 1 plane + new_point = pos - n1 * convex_radius; + } + else + { + // Transform normals for plane 2 with scale + Vec3 n2 = (inv_scale * mPlanes[point.mFaces[1]].GetNormal()).Normalized(); + + // Get first two planes and offset inwards by convex radius + Plane p1 = Plane::sFromPointAndNormal(pos, n1).Offset(-convex_radius); + Plane p2 = Plane::sFromPointAndNormal(pos, n2).Offset(-convex_radius); + Plane p3; + + if (point.mNumFaces == 3) + { + // Transform last normal with scale + Vec3 n3 = (inv_scale * mPlanes[point.mFaces[2]].GetNormal()).Normalized(); + + // Get third plane and offset inwards by convex radius + p3 = Plane::sFromPointAndNormal(pos, n3).Offset(-convex_radius); + } + else + { + // Third plane has normal perpendicular to the other two planes and goes through the vertex position + JPH_ASSERT(point.mNumFaces == 2); + p3 = Plane::sFromPointAndNormal(pos, n1.Cross(n2)); + } + + // Find intersection point between the three planes + if (!Plane::sIntersectPlanes(p1, p2, p3, new_point)) + { + // Fallback: Just push point back using the first plane + new_point = pos - n1 * convex_radius; + } + } + + // Add point + transformed_points.push_back(new_point); + } + + return hull; + } + } + + JPH_ASSERT(false); + return nullptr; +} + +void ConvexHullShape::GetSupportingFace(const SubShapeID &inSubShapeID, Vec3Arg inDirection, Vec3Arg inScale, Mat44Arg inCenterOfMassTransform, SupportingFace &outVertices) const +{ + JPH_ASSERT(inSubShapeID.IsEmpty(), "Invalid subshape ID"); + + Vec3 inv_scale = inScale.Reciprocal(); + + // Need to transform the plane normals using inScale + // Transforming a direction with matrix M is done through multiplying by (M^-1)^T + // In this case M is a diagonal matrix with the scale vector, so we need to multiply our normal by 1 / scale and renormalize afterwards + Vec3 plane0_normal = inv_scale * mPlanes[0].GetNormal(); + float best_dot = plane0_normal.Dot(inDirection) / plane0_normal.Length(); + int best_face_idx = 0; + + for (Array::size_type i = 1; i < mPlanes.size(); ++i) + { + Vec3 plane_normal = inv_scale * mPlanes[i].GetNormal(); + float dot = plane_normal.Dot(inDirection) / plane_normal.Length(); + if (dot < best_dot) + { + best_dot = dot; + best_face_idx = (int)i; + } + } + + // Get vertices + const Face &best_face = mFaces[best_face_idx]; + const uint8 *first_vtx = mVertexIdx.data() + best_face.mFirstVertex; + const uint8 *end_vtx = first_vtx + best_face.mNumVertices; + + // If we have more than 1/2 the capacity of outVertices worth of vertices, we start skipping vertices (note we can't fill the buffer completely since extra edges will be generated by clipping). + // TODO: This really needs a better algorithm to determine which vertices are important! + int max_vertices_to_return = outVertices.capacity() / 2; + int delta_vtx = (int(best_face.mNumVertices) + max_vertices_to_return) / max_vertices_to_return; + + // Calculate transform with scale + Mat44 transform = inCenterOfMassTransform.PreScaled(inScale); + + if (ScaleHelpers::IsInsideOut(inScale)) + { + // Flip winding of supporting face + for (const uint8 *v = end_vtx - 1; v >= first_vtx; v -= delta_vtx) + outVertices.push_back(transform * mPoints[*v].mPosition); + } + else + { + // Normal winding of supporting face + for (const uint8 *v = first_vtx; v < end_vtx; v += delta_vtx) + outVertices.push_back(transform * mPoints[*v].mPosition); + } +} + +void ConvexHullShape::GetSubmergedVolume(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, const Plane &inSurface, float &outTotalVolume, float &outSubmergedVolume, Vec3 &outCenterOfBuoyancy JPH_IF_DEBUG_RENDERER(, RVec3Arg inBaseOffset)) const +{ + // Trivially calculate total volume + Vec3 abs_scale = inScale.Abs(); + outTotalVolume = mVolume * abs_scale.GetX() * abs_scale.GetY() * abs_scale.GetZ(); + + // Check if shape has been scaled inside out + bool is_inside_out = ScaleHelpers::IsInsideOut(inScale); + + // Convert the points to world space and determine the distance to the surface + int num_points = int(mPoints.size()); + PolyhedronSubmergedVolumeCalculator::Point *buffer = (PolyhedronSubmergedVolumeCalculator::Point *)JPH_STACK_ALLOC(num_points * sizeof(PolyhedronSubmergedVolumeCalculator::Point)); + PolyhedronSubmergedVolumeCalculator submerged_vol_calc(inCenterOfMassTransform * Mat44::sScale(inScale), &mPoints[0].mPosition, sizeof(Point), num_points, inSurface, buffer JPH_IF_DEBUG_RENDERER(, inBaseOffset)); + + if (submerged_vol_calc.AreAllAbove()) + { + // We're above the water + outSubmergedVolume = 0.0f; + outCenterOfBuoyancy = Vec3::sZero(); + } + else if (submerged_vol_calc.AreAllBelow()) + { + // We're fully submerged + outSubmergedVolume = outTotalVolume; + outCenterOfBuoyancy = inCenterOfMassTransform.GetTranslation(); + } + else + { + // Calculate submerged volume + int reference_point_idx = submerged_vol_calc.GetReferencePointIdx(); + for (const Face &f : mFaces) + { + const uint8 *first_vtx = mVertexIdx.data() + f.mFirstVertex; + const uint8 *end_vtx = first_vtx + f.mNumVertices; + + // If any of the vertices of this face are the reference point, the volume will be zero so we can skip this face + bool degenerate = false; + for (const uint8 *v = first_vtx; v < end_vtx; ++v) + if (*v == reference_point_idx) + { + degenerate = true; + break; + } + if (degenerate) + continue; + + // Triangulate the face + int i1 = *first_vtx; + if (is_inside_out) + { + // Reverse winding + for (const uint8 *v = first_vtx + 2; v < end_vtx; ++v) + { + int i2 = *(v - 1); + int i3 = *v; + submerged_vol_calc.AddFace(i1, i3, i2); + } + } + else + { + // Normal winding + for (const uint8 *v = first_vtx + 2; v < end_vtx; ++v) + { + int i2 = *(v - 1); + int i3 = *v; + submerged_vol_calc.AddFace(i1, i2, i3); + } + } + } + + // Get the results + submerged_vol_calc.GetResult(outSubmergedVolume, outCenterOfBuoyancy); + } + +#ifdef JPH_DEBUG_RENDERER + // Draw center of buoyancy + if (sDrawSubmergedVolumes) + DebugRenderer::sInstance->DrawWireSphere(inBaseOffset + outCenterOfBuoyancy, 0.05f, Color::sRed, 1); +#endif // JPH_DEBUG_RENDERER +} + +#ifdef JPH_DEBUG_RENDERER +void ConvexHullShape::Draw(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inUseMaterialColors, bool inDrawWireframe) const +{ + if (mGeometry == nullptr) + { + Array triangles; + for (const Face &f : mFaces) + { + const uint8 *first_vtx = mVertexIdx.data() + f.mFirstVertex; + const uint8 *end_vtx = first_vtx + f.mNumVertices; + + // Draw first triangle of polygon + Vec3 v0 = mPoints[first_vtx[0]].mPosition; + Vec3 v1 = mPoints[first_vtx[1]].mPosition; + Vec3 v2 = mPoints[first_vtx[2]].mPosition; + Vec3 uv_direction = (v1 - v0).Normalized(); + triangles.push_back({ v0, v1, v2, Color::sWhite, v0, uv_direction }); + + // Draw any other triangles in this polygon + for (const uint8 *v = first_vtx + 3; v < end_vtx; ++v) + triangles.push_back({ v0, mPoints[*(v - 1)].mPosition, mPoints[*v].mPosition, Color::sWhite, v0, uv_direction }); + } + mGeometry = new DebugRenderer::Geometry(inRenderer->CreateTriangleBatch(triangles), GetLocalBounds()); + } + + // Test if the shape is scaled inside out + DebugRenderer::ECullMode cull_mode = ScaleHelpers::IsInsideOut(inScale)? DebugRenderer::ECullMode::CullFrontFace : DebugRenderer::ECullMode::CullBackFace; + + // Determine the draw mode + DebugRenderer::EDrawMode draw_mode = inDrawWireframe? DebugRenderer::EDrawMode::Wireframe : DebugRenderer::EDrawMode::Solid; + + // Draw the geometry + Color color = inUseMaterialColors? GetMaterial()->GetDebugColor() : inColor; + RMat44 transform = inCenterOfMassTransform.PreScaled(inScale); + inRenderer->DrawGeometry(transform, color, mGeometry, cull_mode, DebugRenderer::ECastShadow::On, draw_mode); + + // Draw the outline if requested + if (sDrawFaceOutlines) + for (const Face &f : mFaces) + { + const uint8 *first_vtx = mVertexIdx.data() + f.mFirstVertex; + const uint8 *end_vtx = first_vtx + f.mNumVertices; + + // Draw edges of face + inRenderer->DrawLine(transform * mPoints[*(end_vtx - 1)].mPosition, transform * mPoints[*first_vtx].mPosition, Color::sGrey); + for (const uint8 *v = first_vtx + 1; v < end_vtx; ++v) + inRenderer->DrawLine(transform * mPoints[*(v - 1)].mPosition, transform * mPoints[*v].mPosition, Color::sGrey); + } +} + +void ConvexHullShape::DrawShrunkShape(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale) const +{ + // Get the shrunk points + SupportBuffer buffer; + const HullNoConvex *support = mConvexRadius > 0.0f? static_cast(GetSupportFunction(ESupportMode::ExcludeConvexRadius, buffer, inScale)) : nullptr; + + RMat44 transform = inCenterOfMassTransform * Mat44::sScale(inScale); + + for (int p = 0; p < (int)mPoints.size(); ++p) + { + const Point &point = mPoints[p]; + RVec3 position = transform * point.mPosition; + RVec3 shrunk_point = support != nullptr? transform * support->GetPoints()[p] : position; + + // Draw difference between shrunk position and position + inRenderer->DrawLine(position, shrunk_point, Color::sGreen); + + // Draw face normals that are contributing + for (int i = 0; i < point.mNumFaces; ++i) + inRenderer->DrawLine(position, position + 0.1f * mPlanes[point.mFaces[i]].GetNormal(), Color::sYellow); + + // Draw point index + inRenderer->DrawText3D(position, ConvertToString(p), Color::sWhite, 0.1f); + } +} +#endif // JPH_DEBUG_RENDERER + +bool ConvexHullShape::CastRayHelper(const RayCast &inRay, float &outMinFraction, float &outMaxFraction) const +{ + if (mFaces.size() == 2) + { + // If we have only 2 faces, we're a flat convex hull and we need to test edges instead of planes + + // Check if plane is parallel to ray + const Plane &p = mPlanes.front(); + Vec3 plane_normal = p.GetNormal(); + float direction_projection = inRay.mDirection.Dot(plane_normal); + if (abs(direction_projection) >= 1.0e-12f) + { + // Calculate intersection point + float distance_to_plane = inRay.mOrigin.Dot(plane_normal) + p.GetConstant(); + float fraction = -distance_to_plane / direction_projection; + if (fraction < 0.0f || fraction > 1.0f) + { + // Does not hit plane, no hit + outMinFraction = 0.0f; + outMaxFraction = 1.0f + FLT_EPSILON; + return false; + } + Vec3 intersection_point = inRay.mOrigin + fraction * inRay.mDirection; + + // Test all edges to see if point is inside polygon + const Face &f = mFaces.front(); + const uint8 *first_vtx = mVertexIdx.data() + f.mFirstVertex; + const uint8 *end_vtx = first_vtx + f.mNumVertices; + Vec3 p1 = mPoints[*end_vtx].mPosition; + for (const uint8 *v = first_vtx; v < end_vtx; ++v) + { + Vec3 p2 = mPoints[*v].mPosition; + if ((p2 - p1).Cross(intersection_point - p1).Dot(plane_normal) < 0.0f) + { + // Outside polygon, no hit + outMinFraction = 0.0f; + outMaxFraction = 1.0f + FLT_EPSILON; + return false; + } + p1 = p2; + } + + // Inside polygon, a hit + outMinFraction = fraction; + outMaxFraction = fraction; + return true; + } + else + { + // Parallel ray doesn't hit + outMinFraction = 0.0f; + outMaxFraction = 1.0f + FLT_EPSILON; + return false; + } + } + else + { + // Clip ray against all planes + int fractions_set = 0; + bool all_inside = true; + float min_fraction = 0.0f, max_fraction = 1.0f + FLT_EPSILON; + for (const Plane &p : mPlanes) + { + // Check if the ray origin is behind this plane + Vec3 plane_normal = p.GetNormal(); + float distance_to_plane = inRay.mOrigin.Dot(plane_normal) + p.GetConstant(); + bool is_outside = distance_to_plane > 0.0f; + all_inside &= !is_outside; + + // Check if plane is parallel to ray + float direction_projection = inRay.mDirection.Dot(plane_normal); + if (abs(direction_projection) >= 1.0e-12f) + { + // Get intersection fraction between ray and plane + float fraction = -distance_to_plane / direction_projection; + + // Update interval of ray that is inside the hull + if (direction_projection < 0.0f) + { + min_fraction = max(fraction, min_fraction); + fractions_set |= 1; + } + else + { + max_fraction = min(fraction, max_fraction); + fractions_set |= 2; + } + } + else if (is_outside) + return false; // Outside the plane and parallel, no hit! + } + + // Test if both min and max have been set + if (fractions_set == 3) + { + // Output fractions + outMinFraction = min_fraction; + outMaxFraction = max_fraction; + + // Test if the infinite ray intersects with the hull (the length will be checked later) + return min_fraction <= max_fraction && max_fraction >= 0.0f; + } + else + { + // Degenerate case, either the ray is parallel to all planes or the ray has zero length + outMinFraction = 0.0f; + outMaxFraction = 1.0f + FLT_EPSILON; + + // Return if the origin is inside the hull + return all_inside; + } + } +} + +bool ConvexHullShape::CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const +{ + // Determine if ray hits the shape + float min_fraction, max_fraction; + if (CastRayHelper(inRay, min_fraction, max_fraction) + && min_fraction < ioHit.mFraction) // Check if this is a closer hit + { + // Better hit than the current hit + ioHit.mFraction = min_fraction; + ioHit.mSubShapeID2 = inSubShapeIDCreator.GetID(); + return true; + } + return false; +} + +void ConvexHullShape::CastRay(const RayCast &inRay, const RayCastSettings &inRayCastSettings, const SubShapeIDCreator &inSubShapeIDCreator, CastRayCollector &ioCollector, const ShapeFilter &inShapeFilter) const +{ + // Test shape filter + if (!inShapeFilter.ShouldCollide(this, inSubShapeIDCreator.GetID())) + return; + + // Determine if ray hits the shape + float min_fraction, max_fraction; + if (CastRayHelper(inRay, min_fraction, max_fraction) + && min_fraction < ioCollector.GetEarlyOutFraction()) // Check if this is closer than the early out fraction + { + // Better hit than the current hit + RayCastResult hit; + hit.mBodyID = TransformedShape::sGetBodyID(ioCollector.GetContext()); + hit.mSubShapeID2 = inSubShapeIDCreator.GetID(); + + // Check front side hit + if (inRayCastSettings.mTreatConvexAsSolid || min_fraction > 0.0f) + { + hit.mFraction = min_fraction; + ioCollector.AddHit(hit); + } + + // Check back side hit + if (inRayCastSettings.mBackFaceMode == EBackFaceMode::CollideWithBackFaces + && max_fraction < ioCollector.GetEarlyOutFraction()) + { + hit.mFraction = max_fraction; + ioCollector.AddHit(hit); + } + } +} + +void ConvexHullShape::CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter) const +{ + // Test shape filter + if (!inShapeFilter.ShouldCollide(this, inSubShapeIDCreator.GetID())) + return; + + // Check if point is behind all planes + for (const Plane &p : mPlanes) + if (p.SignedDistance(inPoint) > 0.0f) + return; + + // Point is inside + ioCollector.AddHit({ TransformedShape::sGetBodyID(ioCollector.GetContext()), inSubShapeIDCreator.GetID() }); +} + +void ConvexHullShape::CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, SoftBodyVertex *ioVertices, uint inNumVertices, [[maybe_unused]] float inDeltaTime, [[maybe_unused]] Vec3Arg inDisplacementDueToGravity, int inCollidingShapeIndex) const +{ + Mat44 inverse_transform = inCenterOfMassTransform.InversedRotationTranslation(); + + Vec3 inv_scale = inScale.Reciprocal(); + bool is_not_scaled = ScaleHelpers::IsNotScaled(inScale); + float scale_flip = ScaleHelpers::IsInsideOut(inScale)? -1.0f : 1.0f; + + for (SoftBodyVertex *v = ioVertices, *sbv_end = ioVertices + inNumVertices; v < sbv_end; ++v) + if (v->mInvMass > 0.0f) + { + Vec3 local_pos = inverse_transform * v->mPosition; + + // Find most facing plane + float max_distance = -FLT_MAX; + Vec3 max_plane_normal = Vec3::sZero(); + uint max_plane_idx = 0; + if (is_not_scaled) + { + // Without scale, it is trivial to calculate the distance to the hull + for (const Plane &p : mPlanes) + { + float distance = p.SignedDistance(local_pos); + if (distance > max_distance) + { + max_distance = distance; + max_plane_normal = p.GetNormal(); + max_plane_idx = uint(&p - mPlanes.data()); + } + } + } + else + { + // When there's scale we need to calculate the planes first + for (uint i = 0; i < (uint)mPlanes.size(); ++i) + { + // Calculate plane normal and point by scaling the original plane + Vec3 plane_normal = (inv_scale * mPlanes[i].GetNormal()).Normalized(); + Vec3 plane_point = inScale * mPoints[mVertexIdx[mFaces[i].mFirstVertex]].mPosition; + + float distance = plane_normal.Dot(local_pos - plane_point); + if (distance > max_distance) + { + max_distance = distance; + max_plane_normal = plane_normal; + max_plane_idx = i; + } + } + } + bool is_outside = max_distance > 0.0f; + + // Project point onto that plane + Vec3 closest_point = local_pos - max_distance * max_plane_normal; + + // Check edges if we're outside the hull (when inside we know the closest face is also the closest point to the surface) + if (is_outside) + { + // Loop over edges + float closest_point_dist_sq = FLT_MAX; + const Face &face = mFaces[max_plane_idx]; + for (const uint8 *v_start = &mVertexIdx[face.mFirstVertex], *v1 = v_start, *v_end = v_start + face.mNumVertices; v1 < v_end; ++v1) + { + // Find second point + const uint8 *v2 = v1 + 1; + if (v2 == v_end) + v2 = v_start; + + // Get edge points + Vec3 p1 = inScale * mPoints[*v1].mPosition; + Vec3 p2 = inScale * mPoints[*v2].mPosition; + + // Check if the position is outside the edge (if not, the face will be closer) + Vec3 edge_normal = (p2 - p1).Cross(max_plane_normal); + if (scale_flip * edge_normal.Dot(local_pos - p1) > 0.0f) + { + // Get closest point on edge + uint32 set; + Vec3 closest = ClosestPoint::GetClosestPointOnLine(p1 - local_pos, p2 - local_pos, set); + float distance_sq = closest.LengthSq(); + if (distance_sq < closest_point_dist_sq) + closest_point = local_pos + closest; + } + } + } + + // Check if this is the largest penetration + Vec3 normal = local_pos - closest_point; + float normal_length = normal.Length(); + float penetration = normal_length; + if (is_outside) + penetration = -penetration; + else + normal = -normal; + if (penetration > v->mLargestPenetration) + { + v->mLargestPenetration = penetration; + + // Calculate contact plane + normal = normal_length > 0.0f? normal / normal_length : max_plane_normal; + Plane plane = Plane::sFromPointAndNormal(closest_point, normal); + + // Store collision + v->mCollisionPlane = plane.GetTransformed(inCenterOfMassTransform); + v->mCollidingShapeIndex = inCollidingShapeIndex; + } + } +} + +class ConvexHullShape::CHSGetTrianglesContext +{ +public: + CHSGetTrianglesContext(Mat44Arg inTransform, bool inIsInsideOut) : mTransform(inTransform), mIsInsideOut(inIsInsideOut) { } + + Mat44 mTransform; + bool mIsInsideOut; + size_t mCurrentFace = 0; +}; + +void ConvexHullShape::GetTrianglesStart(GetTrianglesContext &ioContext, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) const +{ + static_assert(sizeof(CHSGetTrianglesContext) <= sizeof(GetTrianglesContext), "GetTrianglesContext too small"); + JPH_ASSERT(IsAligned(&ioContext, alignof(CHSGetTrianglesContext))); + + new (&ioContext) CHSGetTrianglesContext(Mat44::sRotationTranslation(inRotation, inPositionCOM) * Mat44::sScale(inScale), ScaleHelpers::IsInsideOut(inScale)); +} + +int ConvexHullShape::GetTrianglesNext(GetTrianglesContext &ioContext, int inMaxTrianglesRequested, Float3 *outTriangleVertices, const PhysicsMaterial **outMaterials) const +{ + static_assert(cGetTrianglesMinTrianglesRequested >= 12, "cGetTrianglesMinTrianglesRequested is too small"); + JPH_ASSERT(inMaxTrianglesRequested >= cGetTrianglesMinTrianglesRequested); + + CHSGetTrianglesContext &context = (CHSGetTrianglesContext &)ioContext; + + int total_num_triangles = 0; + for (; context.mCurrentFace < mFaces.size(); ++context.mCurrentFace) + { + const Face &f = mFaces[context.mCurrentFace]; + + const uint8 *first_vtx = mVertexIdx.data() + f.mFirstVertex; + const uint8 *end_vtx = first_vtx + f.mNumVertices; + + // Check if there is still room in the output buffer for this face + int num_triangles = f.mNumVertices - 2; + inMaxTrianglesRequested -= num_triangles; + if (inMaxTrianglesRequested < 0) + break; + total_num_triangles += num_triangles; + + // Get first triangle of polygon + Vec3 v0 = context.mTransform * mPoints[first_vtx[0]].mPosition; + Vec3 v1 = context.mTransform * mPoints[first_vtx[1]].mPosition; + Vec3 v2 = context.mTransform * mPoints[first_vtx[2]].mPosition; + v0.StoreFloat3(outTriangleVertices++); + if (context.mIsInsideOut) + { + // Store first triangle in this polygon flipped + v2.StoreFloat3(outTriangleVertices++); + v1.StoreFloat3(outTriangleVertices++); + + // Store other triangles in this polygon flipped + for (const uint8 *v = first_vtx + 3; v < end_vtx; ++v) + { + v0.StoreFloat3(outTriangleVertices++); + (context.mTransform * mPoints[*v].mPosition).StoreFloat3(outTriangleVertices++); + (context.mTransform * mPoints[*(v - 1)].mPosition).StoreFloat3(outTriangleVertices++); + } + } + else + { + // Store first triangle in this polygon + v1.StoreFloat3(outTriangleVertices++); + v2.StoreFloat3(outTriangleVertices++); + + // Store other triangles in this polygon + for (const uint8 *v = first_vtx + 3; v < end_vtx; ++v) + { + v0.StoreFloat3(outTriangleVertices++); + (context.mTransform * mPoints[*(v - 1)].mPosition).StoreFloat3(outTriangleVertices++); + (context.mTransform * mPoints[*v].mPosition).StoreFloat3(outTriangleVertices++); + } + } + } + + // Store materials + if (outMaterials != nullptr) + { + const PhysicsMaterial *material = GetMaterial(); + for (const PhysicsMaterial **m = outMaterials, **m_end = outMaterials + total_num_triangles; m < m_end; ++m) + *m = material; + } + + return total_num_triangles; +} + +void ConvexHullShape::SaveBinaryState(StreamOut &inStream) const +{ + ConvexShape::SaveBinaryState(inStream); + + inStream.Write(mCenterOfMass); + inStream.Write(mInertia); + inStream.Write(mLocalBounds.mMin); + inStream.Write(mLocalBounds.mMax); + inStream.Write(mPoints); + inStream.Write(mFaces); + inStream.Write(mPlanes); + inStream.Write(mVertexIdx); + inStream.Write(mConvexRadius); + inStream.Write(mVolume); + inStream.Write(mInnerRadius); +} + +void ConvexHullShape::RestoreBinaryState(StreamIn &inStream) +{ + ConvexShape::RestoreBinaryState(inStream); + + inStream.Read(mCenterOfMass); + inStream.Read(mInertia); + inStream.Read(mLocalBounds.mMin); + inStream.Read(mLocalBounds.mMax); + inStream.Read(mPoints); + inStream.Read(mFaces); + inStream.Read(mPlanes); + inStream.Read(mVertexIdx); + inStream.Read(mConvexRadius); + inStream.Read(mVolume); + inStream.Read(mInnerRadius); +} + +Shape::Stats ConvexHullShape::GetStats() const +{ + // Count number of triangles + uint triangle_count = 0; + for (const Face &f : mFaces) + triangle_count += f.mNumVertices - 2; + + return Stats( + sizeof(*this) + + mPoints.size() * sizeof(Point) + + mFaces.size() * sizeof(Face) + + mPlanes.size() * sizeof(Plane) + + mVertexIdx.size() * sizeof(uint8), + triangle_count); +} + +void ConvexHullShape::sRegister() +{ + ShapeFunctions &f = ShapeFunctions::sGet(EShapeSubType::ConvexHull); + f.mConstruct = []() -> Shape * { return new ConvexHullShape; }; + f.mColor = Color::sGreen; +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/ConvexHullShape.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/ConvexHullShape.h new file mode 100644 index 00000000000..8837a82d85f --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/ConvexHullShape.h @@ -0,0 +1,202 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include +#ifdef JPH_DEBUG_RENDERER + #include +#endif // JPH_DEBUG_RENDERER + +JPH_NAMESPACE_BEGIN + +/// Class that constructs a ConvexHullShape +class JPH_EXPORT ConvexHullShapeSettings final : public ConvexShapeSettings +{ +public: + JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, ConvexHullShapeSettings) + + /// Default constructor for deserialization + ConvexHullShapeSettings() = default; + + /// Create a convex hull from inPoints and maximum convex radius inMaxConvexRadius, the radius is automatically lowered if the hull requires it. + /// (internally this will be subtracted so the total size will not grow with the convex radius). + ConvexHullShapeSettings(const Vec3 *inPoints, int inNumPoints, float inMaxConvexRadius = cDefaultConvexRadius, const PhysicsMaterial *inMaterial = nullptr) : ConvexShapeSettings(inMaterial), mPoints(inPoints, inPoints + inNumPoints), mMaxConvexRadius(inMaxConvexRadius) { } + ConvexHullShapeSettings(const Array &inPoints, float inConvexRadius = cDefaultConvexRadius, const PhysicsMaterial *inMaterial = nullptr) : ConvexShapeSettings(inMaterial), mPoints(inPoints), mMaxConvexRadius(inConvexRadius) { } + + // See: ShapeSettings + virtual ShapeResult Create() const override; + + Array mPoints; ///< Points to create the hull from + float mMaxConvexRadius = 0.0f; ///< Convex radius as supplied by the constructor. Note that during hull creation the convex radius can be made smaller if the value is too big for the hull. + float mMaxErrorConvexRadius = 0.05f; ///< Maximum distance between the shrunk hull + convex radius and the actual hull. + float mHullTolerance = 1.0e-3f; ///< Points are allowed this far outside of the hull (increasing this yields a hull with less vertices). Note that the actual used value can be larger if the points of the hull are far apart. +}; + +/// A convex hull +class JPH_EXPORT ConvexHullShape final : public ConvexShape +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Maximum amount of points supported in a convex hull. Note that while constructing a hull, interior points are discarded so you can provide more points. + /// The ConvexHullShapeSettings::Create function will return an error when too many points are provided. + static constexpr int cMaxPointsInHull = 256; + + /// Constructor + ConvexHullShape() : ConvexShape(EShapeSubType::ConvexHull) { } + ConvexHullShape(const ConvexHullShapeSettings &inSettings, ShapeResult &outResult); + + // See Shape::GetCenterOfMass + virtual Vec3 GetCenterOfMass() const override { return mCenterOfMass; } + + // See Shape::GetLocalBounds + virtual AABox GetLocalBounds() const override { return mLocalBounds; } + + // See Shape::GetInnerRadius + virtual float GetInnerRadius() const override { return mInnerRadius; } + + // See Shape::GetMassProperties + virtual MassProperties GetMassProperties() const override; + + // See Shape::GetSurfaceNormal + virtual Vec3 GetSurfaceNormal(const SubShapeID &inSubShapeID, Vec3Arg inLocalSurfacePosition) const override; + + // See Shape::GetSupportingFace + virtual void GetSupportingFace(const SubShapeID &inSubShapeID, Vec3Arg inDirection, Vec3Arg inScale, Mat44Arg inCenterOfMassTransform, SupportingFace &outVertices) const override; + + // See ConvexShape::GetSupportFunction + virtual const Support * GetSupportFunction(ESupportMode inMode, SupportBuffer &inBuffer, Vec3Arg inScale) const override; + + // See Shape::GetSubmergedVolume + virtual void GetSubmergedVolume(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, const Plane &inSurface, float &outTotalVolume, float &outSubmergedVolume, Vec3 &outCenterOfBuoyancy JPH_IF_DEBUG_RENDERER(, RVec3Arg inBaseOffset)) const override; + +#ifdef JPH_DEBUG_RENDERER + // See Shape::Draw + virtual void Draw(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inUseMaterialColors, bool inDrawWireframe) const override; + + /// Debugging helper draw function that draws how all points are moved when a shape is shrunk by the convex radius + void DrawShrunkShape(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale) const; +#endif // JPH_DEBUG_RENDERER + + // See Shape::CastRay + virtual bool CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const override; + virtual void CastRay(const RayCast &inRay, const RayCastSettings &inRayCastSettings, const SubShapeIDCreator &inSubShapeIDCreator, CastRayCollector &ioCollector, const ShapeFilter &inShapeFilter = { }) const override; + + // See: Shape::CollidePoint + virtual void CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter = { }) const override; + + // See: Shape::CollideSoftBodyVertices + virtual void CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, SoftBodyVertex *ioVertices, uint inNumVertices, float inDeltaTime, Vec3Arg inDisplacementDueToGravity, int inCollidingShapeIndex) const override; + + // See Shape::GetTrianglesStart + virtual void GetTrianglesStart(GetTrianglesContext &ioContext, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) const override; + + // See Shape::GetTrianglesNext + virtual int GetTrianglesNext(GetTrianglesContext &ioContext, int inMaxTrianglesRequested, Float3 *outTriangleVertices, const PhysicsMaterial **outMaterials = nullptr) const override; + + // See Shape + virtual void SaveBinaryState(StreamOut &inStream) const override; + + // See Shape::GetStats + virtual Stats GetStats() const override; + + // See Shape::GetVolume + virtual float GetVolume() const override { return mVolume; } + + /// Get the convex radius of this convex hull + float GetConvexRadius() const { return mConvexRadius; } + + /// Get the planes of this convex hull + const Array & GetPlanes() const { return mPlanes; } + + /// Get the number of vertices in this convex hull + inline uint GetNumPoints() const { return uint(mPoints.size()); } + + /// Get a vertex of this convex hull relative to the center of mass + inline Vec3 GetPoint(uint inIndex) const { return mPoints[inIndex].mPosition; } + + /// Get the number of faces in this convex hull + inline uint GetNumFaces() const { return uint(mFaces.size()); } + + /// Get the number of vertices in a face + inline uint GetNumVerticesInFace(uint inFaceIndex) const { return mFaces[inFaceIndex].mNumVertices; } + + /// Get the vertices indices of a face + /// @param inFaceIndex Index of the face. + /// @param inMaxVertices Maximum number of vertices to return. + /// @param outVertices Array of vertices indices, must be at least inMaxVertices in size, the vertices are returned in counter clockwise order and the positions can be obtained using GetPoint(index). + /// @return Number of vertices in face, if this is bigger than inMaxVertices, not all vertices were retrieved. + inline uint GetFaceVertices(uint inFaceIndex, uint inMaxVertices, uint *outVertices) const + { + const Face &face = mFaces[inFaceIndex]; + const uint8 *first_vertex = mVertexIdx.data() + face.mFirstVertex; + uint num_vertices = min(face.mNumVertices, inMaxVertices); + for (uint i = 0; i < num_vertices; ++i) + outVertices[i] = first_vertex[i]; + return face.mNumVertices; + } + + // Register shape functions with the registry + static void sRegister(); + +#ifdef JPH_DEBUG_RENDERER + /// Draw the outlines of the faces of the convex hull when drawing the shape + inline static bool sDrawFaceOutlines = false; +#endif // JPH_DEBUG_RENDERER + +protected: + // See: Shape::RestoreBinaryState + virtual void RestoreBinaryState(StreamIn &inStream) override; + +private: + /// Helper function that returns the min and max fraction along the ray that hits the convex hull. Returns false if there is no hit. + bool CastRayHelper(const RayCast &inRay, float &outMinFraction, float &outMaxFraction) const; + + /// Class for GetTrianglesStart/Next + class CHSGetTrianglesContext; + + /// Classes for GetSupportFunction + class HullNoConvex; + class HullWithConvex; + class HullWithConvexScaled; + + struct Face + { + uint16 mFirstVertex; ///< First index in mVertexIdx to use + uint16 mNumVertices = 0; ///< Number of vertices in the mVertexIdx to use + }; + + static_assert(sizeof(Face) == 4, "Unexpected size"); + static_assert(alignof(Face) == 2, "Unexpected alignment"); + + struct Point + { + Vec3 mPosition; ///< Position of vertex + int mNumFaces = 0; ///< Number of faces in the face array below + int mFaces[3] = { -1, -1, -1 }; ///< Indices of 3 neighboring faces with the biggest difference in normal (used to shift vertices for convex radius) + }; + + static_assert(sizeof(Point) == 32, "Unexpected size"); + static_assert(alignof(Point) == JPH_VECTOR_ALIGNMENT, "Unexpected alignment"); + + Vec3 mCenterOfMass; ///< Center of mass of this convex hull + Mat44 mInertia; ///< Inertia matrix assuming density is 1 (needs to be multiplied by density) + AABox mLocalBounds; ///< Local bounding box for the convex hull + Array mPoints; ///< Points on the convex hull surface + Array mFaces; ///< Faces of the convex hull surface + Array mPlanes; ///< Planes for the faces (1-on-1 with mFaces array, separate because they need to be 16 byte aligned) + Array mVertexIdx; ///< A list of vertex indices (indexing in mPoints) for each of the faces + float mConvexRadius = 0.0f; ///< Convex radius + float mVolume; ///< Total volume of the convex hull + float mInnerRadius = FLT_MAX; ///< Radius of the biggest sphere that fits entirely in the convex hull + +#ifdef JPH_DEBUG_RENDERER + mutable DebugRenderer::GeometryRef mGeometry; +#endif // JPH_DEBUG_RENDERER +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/ConvexShape.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/ConvexShape.cpp new file mode 100644 index 00000000000..550723589f3 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/ConvexShape.cpp @@ -0,0 +1,559 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +JPH_IMPLEMENT_SERIALIZABLE_ABSTRACT(ConvexShapeSettings) +{ + JPH_ADD_BASE_CLASS(ConvexShapeSettings, ShapeSettings) + + JPH_ADD_ATTRIBUTE(ConvexShapeSettings, mDensity) + JPH_ADD_ATTRIBUTE(ConvexShapeSettings, mMaterial) +} + +const std::vector ConvexShape::sUnitSphereTriangles = []() { + const int level = 2; + + std::vector verts; + GetTrianglesContextVertexList::sCreateHalfUnitSphereTop(verts, level); + GetTrianglesContextVertexList::sCreateHalfUnitSphereBottom(verts, level); + return verts; +}(); + +void ConvexShape::sCollideConvexVsConvex(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, [[maybe_unused]] const ShapeFilter &inShapeFilter) +{ + JPH_PROFILE_FUNCTION(); + + // Get the shapes + JPH_ASSERT(inShape1->GetType() == EShapeType::Convex); + JPH_ASSERT(inShape2->GetType() == EShapeType::Convex); + const ConvexShape *shape1 = static_cast(inShape1); + const ConvexShape *shape2 = static_cast(inShape2); + + // Get transforms + Mat44 inverse_transform1 = inCenterOfMassTransform1.InversedRotationTranslation(); + Mat44 transform_2_to_1 = inverse_transform1 * inCenterOfMassTransform2; + + // Get bounding boxes + AABox shape1_bbox = shape1->GetLocalBounds().Scaled(inScale1); + shape1_bbox.ExpandBy(Vec3::sReplicate(inCollideShapeSettings.mMaxSeparationDistance)); + AABox shape2_bbox = shape2->GetLocalBounds().Scaled(inScale2); + + // Check if they overlap + if (!OrientedBox(transform_2_to_1, shape2_bbox).Overlaps(shape1_bbox)) + return; + + // Note: As we don't remember the penetration axis from the last iteration, and it is likely that shape2 is pushed out of + // collision relative to shape1 by comparing their COM's, we use that as an initial penetration axis: shape2.com - shape1.com + // This has been seen to improve performance by approx. 1% over using a fixed axis like (1, 0, 0). + Vec3 penetration_axis = transform_2_to_1.GetTranslation(); + + // Ensure that we do not pass in a near zero penetration axis + if (penetration_axis.IsNearZero()) + penetration_axis = Vec3::sAxisX(); + + Vec3 point1, point2; + EPAPenetrationDepth pen_depth; + EPAPenetrationDepth::EStatus status; + + // Scope to limit lifetime of SupportBuffer + { + // Create support function + SupportBuffer buffer1_excl_cvx_radius, buffer2_excl_cvx_radius; + const Support *shape1_excl_cvx_radius = shape1->GetSupportFunction(ConvexShape::ESupportMode::ExcludeConvexRadius, buffer1_excl_cvx_radius, inScale1); + const Support *shape2_excl_cvx_radius = shape2->GetSupportFunction(ConvexShape::ESupportMode::ExcludeConvexRadius, buffer2_excl_cvx_radius, inScale2); + + // Transform shape 2 in the space of shape 1 + TransformedConvexObject transformed2_excl_cvx_radius(transform_2_to_1, *shape2_excl_cvx_radius); + + // Perform GJK step + status = pen_depth.GetPenetrationDepthStepGJK(*shape1_excl_cvx_radius, shape1_excl_cvx_radius->GetConvexRadius() + inCollideShapeSettings.mMaxSeparationDistance, transformed2_excl_cvx_radius, shape2_excl_cvx_radius->GetConvexRadius(), inCollideShapeSettings.mCollisionTolerance, penetration_axis, point1, point2); + } + + // Check result of collision detection + switch (status) + { + case EPAPenetrationDepth::EStatus::Colliding: + break; + + case EPAPenetrationDepth::EStatus::NotColliding: + return; + + case EPAPenetrationDepth::EStatus::Indeterminate: + { + // Need to run expensive EPA algorithm + + // Create support function + SupportBuffer buffer1_incl_cvx_radius, buffer2_incl_cvx_radius; + const Support *shape1_incl_cvx_radius = shape1->GetSupportFunction(ConvexShape::ESupportMode::IncludeConvexRadius, buffer1_incl_cvx_radius, inScale1); + const Support *shape2_incl_cvx_radius = shape2->GetSupportFunction(ConvexShape::ESupportMode::IncludeConvexRadius, buffer2_incl_cvx_radius, inScale2); + + // Add separation distance + AddConvexRadius shape1_add_max_separation_distance(*shape1_incl_cvx_radius, inCollideShapeSettings.mMaxSeparationDistance); + + // Transform shape 2 in the space of shape 1 + TransformedConvexObject transformed2_incl_cvx_radius(transform_2_to_1, *shape2_incl_cvx_radius); + + // Perform EPA step + if (!pen_depth.GetPenetrationDepthStepEPA(shape1_add_max_separation_distance, transformed2_incl_cvx_radius, inCollideShapeSettings.mPenetrationTolerance, penetration_axis, point1, point2)) + return; + break; + } + } + + // Check if the penetration is bigger than the early out fraction + float penetration_depth = (point2 - point1).Length() - inCollideShapeSettings.mMaxSeparationDistance; + if (-penetration_depth >= ioCollector.GetEarlyOutFraction()) + return; + + // Correct point1 for the added separation distance + float penetration_axis_len = penetration_axis.Length(); + if (penetration_axis_len > 0.0f) + point1 -= penetration_axis * (inCollideShapeSettings.mMaxSeparationDistance / penetration_axis_len); + + // Convert to world space + point1 = inCenterOfMassTransform1 * point1; + point2 = inCenterOfMassTransform1 * point2; + Vec3 penetration_axis_world = inCenterOfMassTransform1.Multiply3x3(penetration_axis); + + // Create collision result + CollideShapeResult result(point1, point2, penetration_axis_world, penetration_depth, inSubShapeIDCreator1.GetID(), inSubShapeIDCreator2.GetID(), TransformedShape::sGetBodyID(ioCollector.GetContext())); + + // Gather faces + if (inCollideShapeSettings.mCollectFacesMode == ECollectFacesMode::CollectFaces) + { + // Get supporting face of shape 1 + shape1->GetSupportingFace(SubShapeID(), -penetration_axis, inScale1, inCenterOfMassTransform1, result.mShape1Face); + + // Get supporting face of shape 2 + shape2->GetSupportingFace(SubShapeID(), transform_2_to_1.Multiply3x3Transposed(penetration_axis), inScale2, inCenterOfMassTransform2, result.mShape2Face); + } + + // Notify the collector + JPH_IF_TRACK_NARROWPHASE_STATS(TrackNarrowPhaseCollector track;) + ioCollector.AddHit(result); +} + +bool ConvexShape::CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const +{ + // Note: This is a fallback routine, most convex shapes should implement a more performant version! + + JPH_PROFILE_FUNCTION(); + + // Create support function + SupportBuffer buffer; + const Support *support = GetSupportFunction(ConvexShape::ESupportMode::IncludeConvexRadius, buffer, Vec3::sReplicate(1.0f)); + + // Cast ray + GJKClosestPoint gjk; + if (gjk.CastRay(inRay.mOrigin, inRay.mDirection, cDefaultCollisionTolerance, *support, ioHit.mFraction)) + { + ioHit.mSubShapeID2 = inSubShapeIDCreator.GetID(); + return true; + } + + return false; +} + +void ConvexShape::CastRay(const RayCast &inRay, const RayCastSettings &inRayCastSettings, const SubShapeIDCreator &inSubShapeIDCreator, CastRayCollector &ioCollector, const ShapeFilter &inShapeFilter) const +{ + // Note: This is a fallback routine, most convex shapes should implement a more performant version! + + // Test shape filter + if (!inShapeFilter.ShouldCollide(this, inSubShapeIDCreator.GetID())) + return; + + // First do a normal raycast, limited to the early out fraction + RayCastResult hit; + hit.mFraction = ioCollector.GetEarlyOutFraction(); + if (CastRay(inRay, inSubShapeIDCreator, hit)) + { + // Check front side + if (inRayCastSettings.mTreatConvexAsSolid || hit.mFraction > 0.0f) + { + hit.mBodyID = TransformedShape::sGetBodyID(ioCollector.GetContext()); + ioCollector.AddHit(hit); + } + + // Check if we want back facing hits and the collector still accepts additional hits + if (inRayCastSettings.mBackFaceMode == EBackFaceMode::CollideWithBackFaces && !ioCollector.ShouldEarlyOut()) + { + // Invert the ray, going from the early out fraction back to the fraction where we found our forward hit + float start_fraction = min(1.0f, ioCollector.GetEarlyOutFraction()); + float delta_fraction = hit.mFraction - start_fraction; + if (delta_fraction < 0.0f) + { + RayCast inverted_ray { inRay.mOrigin + start_fraction * inRay.mDirection, delta_fraction * inRay.mDirection }; + + // Cast another ray + RayCastResult inverted_hit; + inverted_hit.mFraction = 1.0f; + if (CastRay(inverted_ray, inSubShapeIDCreator, inverted_hit) + && inverted_hit.mFraction > 0.0f) // Ignore hits with fraction 0, this means the ray ends inside the object and we don't want to report it as a back facing hit + { + // Invert fraction and rescale it to the fraction of the original ray + inverted_hit.mFraction = hit.mFraction + (inverted_hit.mFraction - 1.0f) * delta_fraction; + inverted_hit.mBodyID = TransformedShape::sGetBodyID(ioCollector.GetContext()); + ioCollector.AddHit(inverted_hit); + } + } + } + } +} + +void ConvexShape::CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter) const +{ + // Test shape filter + if (!inShapeFilter.ShouldCollide(this, inSubShapeIDCreator.GetID())) + return; + + // First test bounding box + if (GetLocalBounds().Contains(inPoint)) + { + // Create support function + SupportBuffer buffer; + const Support *support = GetSupportFunction(ConvexShape::ESupportMode::IncludeConvexRadius, buffer, Vec3::sReplicate(1.0f)); + + // Create support function for point + PointConvexSupport point { inPoint }; + + // Test intersection + GJKClosestPoint gjk; + Vec3 v = inPoint; + if (gjk.Intersects(*support, point, cDefaultCollisionTolerance, v)) + ioCollector.AddHit({ TransformedShape::sGetBodyID(ioCollector.GetContext()), inSubShapeIDCreator.GetID() }); + } +} + +void ConvexShape::sCastConvexVsConvex(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, [[maybe_unused]] const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector) +{ + JPH_PROFILE_FUNCTION(); + + // Only supported for convex shapes + JPH_ASSERT(inShapeCast.mShape->GetType() == EShapeType::Convex); + const ConvexShape *cast_shape = static_cast(inShapeCast.mShape); + + JPH_ASSERT(inShape->GetType() == EShapeType::Convex); + const ConvexShape *shape = static_cast(inShape); + + // Determine if we want to use the actual shape or a shrunken shape with convex radius + ConvexShape::ESupportMode support_mode = inShapeCastSettings.mUseShrunkenShapeAndConvexRadius? ConvexShape::ESupportMode::ExcludeConvexRadius : ConvexShape::ESupportMode::Default; + + // Create support function for shape to cast + SupportBuffer cast_buffer; + const Support *cast_support = cast_shape->GetSupportFunction(support_mode, cast_buffer, inShapeCast.mScale); + + // Create support function for target shape + SupportBuffer target_buffer; + const Support *target_support = shape->GetSupportFunction(support_mode, target_buffer, inScale); + + // Do a raycast against the result + EPAPenetrationDepth epa; + float fraction = ioCollector.GetEarlyOutFraction(); + Vec3 contact_point_a, contact_point_b, contact_normal; + if (epa.CastShape(inShapeCast.mCenterOfMassStart, inShapeCast.mDirection, inShapeCastSettings.mCollisionTolerance, inShapeCastSettings.mPenetrationTolerance, *cast_support, *target_support, cast_support->GetConvexRadius(), target_support->GetConvexRadius(), inShapeCastSettings.mReturnDeepestPoint, fraction, contact_point_a, contact_point_b, contact_normal) + && (inShapeCastSettings.mBackFaceModeConvex == EBackFaceMode::CollideWithBackFaces + || contact_normal.Dot(inShapeCast.mDirection) > 0.0f)) // Test if backfacing + { + // Convert to world space + contact_point_a = inCenterOfMassTransform2 * contact_point_a; + contact_point_b = inCenterOfMassTransform2 * contact_point_b; + Vec3 contact_normal_world = inCenterOfMassTransform2.Multiply3x3(contact_normal); + + ShapeCastResult result(fraction, contact_point_a, contact_point_b, contact_normal_world, false, inSubShapeIDCreator1.GetID(), inSubShapeIDCreator2.GetID(), TransformedShape::sGetBodyID(ioCollector.GetContext())); + + // Early out if this hit is deeper than the collector's early out value + if (fraction == 0.0f && -result.mPenetrationDepth >= ioCollector.GetEarlyOutFraction()) + return; + + // Gather faces + if (inShapeCastSettings.mCollectFacesMode == ECollectFacesMode::CollectFaces) + { + // Get supporting face of shape 1 + Mat44 transform_1_to_2 = inShapeCast.mCenterOfMassStart; + transform_1_to_2.SetTranslation(transform_1_to_2.GetTranslation() + fraction * inShapeCast.mDirection); + cast_shape->GetSupportingFace(SubShapeID(), transform_1_to_2.Multiply3x3Transposed(-contact_normal), inShapeCast.mScale, inCenterOfMassTransform2 * transform_1_to_2, result.mShape1Face); + + // Get supporting face of shape 2 + shape->GetSupportingFace(SubShapeID(), contact_normal, inScale, inCenterOfMassTransform2, result.mShape2Face); + } + + JPH_IF_TRACK_NARROWPHASE_STATS(TrackNarrowPhaseCollector track;) + ioCollector.AddHit(result); + } +} + +class ConvexShape::CSGetTrianglesContext +{ +public: + CSGetTrianglesContext(const ConvexShape *inShape, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) : + mLocalToWorld(Mat44::sRotationTranslation(inRotation, inPositionCOM) * Mat44::sScale(inScale)), + mIsInsideOut(ScaleHelpers::IsInsideOut(inScale)) + { + mSupport = inShape->GetSupportFunction(ESupportMode::IncludeConvexRadius, mSupportBuffer, Vec3::sReplicate(1.0f)); + } + + SupportBuffer mSupportBuffer; + const Support * mSupport; + Mat44 mLocalToWorld; + bool mIsInsideOut; + size_t mCurrentVertex = 0; +}; + +void ConvexShape::GetTrianglesStart(GetTrianglesContext &ioContext, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) const +{ + static_assert(sizeof(CSGetTrianglesContext) <= sizeof(GetTrianglesContext), "GetTrianglesContext too small"); + JPH_ASSERT(IsAligned(&ioContext, alignof(CSGetTrianglesContext))); + + new (&ioContext) CSGetTrianglesContext(this, inPositionCOM, inRotation, inScale); +} + +int ConvexShape::GetTrianglesNext(GetTrianglesContext &ioContext, int inMaxTrianglesRequested, Float3 *outTriangleVertices, const PhysicsMaterial **outMaterials) const +{ + JPH_ASSERT(inMaxTrianglesRequested >= cGetTrianglesMinTrianglesRequested); + + CSGetTrianglesContext &context = (CSGetTrianglesContext &)ioContext; + + int total_num_vertices = min(inMaxTrianglesRequested * 3, int(sUnitSphereTriangles.size() - context.mCurrentVertex)); + + if (context.mIsInsideOut) + { + // Store triangles flipped + for (const Vec3 *v = sUnitSphereTriangles.data() + context.mCurrentVertex, *v_end = v + total_num_vertices; v < v_end; v += 3) + { + (context.mLocalToWorld * context.mSupport->GetSupport(v[0])).StoreFloat3(outTriangleVertices++); + (context.mLocalToWorld * context.mSupport->GetSupport(v[2])).StoreFloat3(outTriangleVertices++); + (context.mLocalToWorld * context.mSupport->GetSupport(v[1])).StoreFloat3(outTriangleVertices++); + } + } + else + { + // Store triangles + for (const Vec3 *v = sUnitSphereTriangles.data() + context.mCurrentVertex, *v_end = v + total_num_vertices; v < v_end; v += 3) + { + (context.mLocalToWorld * context.mSupport->GetSupport(v[0])).StoreFloat3(outTriangleVertices++); + (context.mLocalToWorld * context.mSupport->GetSupport(v[1])).StoreFloat3(outTriangleVertices++); + (context.mLocalToWorld * context.mSupport->GetSupport(v[2])).StoreFloat3(outTriangleVertices++); + } + } + + context.mCurrentVertex += total_num_vertices; + int total_num_triangles = total_num_vertices / 3; + + // Store materials + if (outMaterials != nullptr) + { + const PhysicsMaterial *material = GetMaterial(); + for (const PhysicsMaterial **m = outMaterials, **m_end = outMaterials + total_num_triangles; m < m_end; ++m) + *m = material; + } + + return total_num_triangles; +} + +void ConvexShape::GetSubmergedVolume(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, const Plane &inSurface, float &outTotalVolume, float &outSubmergedVolume, Vec3 &outCenterOfBuoyancy JPH_IF_DEBUG_RENDERER(, RVec3Arg inBaseOffset)) const +{ + // Calculate total volume + Vec3 abs_scale = inScale.Abs(); + Vec3 extent = GetLocalBounds().GetExtent() * abs_scale; + outTotalVolume = 8.0f * extent.GetX() * extent.GetY() * extent.GetZ(); + + // Points of the bounding box + Vec3 points[] = + { + Vec3(-1, -1, -1), + Vec3( 1, -1, -1), + Vec3(-1, 1, -1), + Vec3( 1, 1, -1), + Vec3(-1, -1, 1), + Vec3( 1, -1, 1), + Vec3(-1, 1, 1), + Vec3( 1, 1, 1), + }; + + // Faces of the bounding box + using Face = int[5]; + #define MAKE_FACE(a, b, c, d) { a, b, c, d, ((1 << a) | (1 << b) | (1 << c) | (1 << d)) } // Last int is a bit mask that indicates which indices are used + Face faces[] = + { + MAKE_FACE(0, 2, 3, 1), + MAKE_FACE(4, 6, 2, 0), + MAKE_FACE(4, 5, 7, 6), + MAKE_FACE(1, 3, 7, 5), + MAKE_FACE(2, 6, 7, 3), + MAKE_FACE(0, 1, 5, 4), + }; + + PolyhedronSubmergedVolumeCalculator::Point *buffer = (PolyhedronSubmergedVolumeCalculator::Point *)JPH_STACK_ALLOC(8 * sizeof(PolyhedronSubmergedVolumeCalculator::Point)); + PolyhedronSubmergedVolumeCalculator submerged_vol_calc(inCenterOfMassTransform * Mat44::sScale(extent), points, sizeof(Vec3), 8, inSurface, buffer JPH_IF_DEBUG_RENDERER(, inBaseOffset)); + + if (submerged_vol_calc.AreAllAbove()) + { + // We're above the water + outSubmergedVolume = 0.0f; + outCenterOfBuoyancy = Vec3::sZero(); + } + else if (submerged_vol_calc.AreAllBelow()) + { + // We're fully submerged + outSubmergedVolume = outTotalVolume; + outCenterOfBuoyancy = inCenterOfMassTransform.GetTranslation(); + } + else + { + // Calculate submerged volume + int reference_point_bit = 1 << submerged_vol_calc.GetReferencePointIdx(); + for (const Face &f : faces) + { + // Test if this face includes the reference point + if ((f[4] & reference_point_bit) == 0) + { + // Triangulate the face (a quad) + submerged_vol_calc.AddFace(f[0], f[1], f[2]); + submerged_vol_calc.AddFace(f[0], f[2], f[3]); + } + } + + submerged_vol_calc.GetResult(outSubmergedVolume, outCenterOfBuoyancy); + } +} + +#ifdef JPH_DEBUG_RENDERER +void ConvexShape::DrawGetSupportFunction(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inDrawSupportDirection) const +{ + // Get the support function with convex radius + SupportBuffer buffer; + const Support *support = GetSupportFunction(ESupportMode::ExcludeConvexRadius, buffer, inScale); + AddConvexRadius add_convex(*support, support->GetConvexRadius()); + + // Draw the shape + DebugRenderer::GeometryRef geometry = inRenderer->CreateTriangleGeometryForConvex([&add_convex](Vec3Arg inDirection) { return add_convex.GetSupport(inDirection); }); + AABox bounds = geometry->mBounds.Transformed(inCenterOfMassTransform); + float lod_scale_sq = geometry->mBounds.GetExtent().LengthSq(); + inRenderer->DrawGeometry(inCenterOfMassTransform, bounds, lod_scale_sq, inColor, geometry); + + if (inDrawSupportDirection) + { + // Iterate on all directions and draw the support point and an arrow in the direction that was sampled to test if the support points make sense + for (Vec3 v : Vec3::sUnitSphere) + { + Vec3 direction = 0.05f * v; + Vec3 pos = add_convex.GetSupport(direction); + RVec3 from = inCenterOfMassTransform * pos; + RVec3 to = inCenterOfMassTransform * (pos + direction); + inRenderer->DrawMarker(from, Color::sWhite, 0.001f); + inRenderer->DrawArrow(from, to, Color::sWhite, 0.001f); + } + } +} + +void ConvexShape::DrawGetSupportingFace(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale) const +{ + // Sample directions and map which faces belong to which directions + using FaceToDirection = UnorderedMap>; + FaceToDirection faces; + for (Vec3 v : Vec3::sUnitSphere) + { + Vec3 direction = 0.05f * v; + + SupportingFace face; + GetSupportingFace(SubShapeID(), direction, inScale, Mat44::sIdentity(), face); + + if (!face.empty()) + { + JPH_ASSERT(face.size() >= 2, "The GetSupportingFace function should either return nothing or at least an edge"); + faces[face].push_back(direction); + } + } + + // Draw each face in a unique color and draw corresponding directions + int color_it = 0; + for (FaceToDirection::value_type &ftd : faces) + { + Color color = Color::sGetDistinctColor(color_it++); + + // Create copy of face (key in map is read only) + SupportingFace face = ftd.first; + + // Displace the face a little bit forward so it is easier to see + Vec3 normal = face.size() >= 3? (face[2] - face[1]).Cross(face[0] - face[1]).Normalized() : Vec3::sZero(); + Vec3 displacement = 0.001f * normal; + + // Transform face to world space and calculate center of mass + Vec3 com_ls = Vec3::sZero(); + for (Vec3 &v : face) + { + v = inCenterOfMassTransform.Multiply3x3(v + displacement); + com_ls += v; + } + RVec3 com = inCenterOfMassTransform.GetTranslation() + com_ls / (float)face.size(); + + // Draw the polygon and directions + inRenderer->DrawWirePolygon(RMat44::sTranslation(inCenterOfMassTransform.GetTranslation()), face, color, face.size() >= 3? 0.001f : 0.0f); + if (face.size() >= 3) + inRenderer->DrawArrow(com, com + inCenterOfMassTransform.Multiply3x3(normal), color, 0.01f); + for (Vec3 &v : ftd.second) + inRenderer->DrawArrow(com, com + inCenterOfMassTransform.Multiply3x3(-v), color, 0.001f); + } +} +#endif // JPH_DEBUG_RENDERER + +void ConvexShape::SaveBinaryState(StreamOut &inStream) const +{ + Shape::SaveBinaryState(inStream); + + inStream.Write(mDensity); +} + +void ConvexShape::RestoreBinaryState(StreamIn &inStream) +{ + Shape::RestoreBinaryState(inStream); + + inStream.Read(mDensity); +} + +void ConvexShape::SaveMaterialState(PhysicsMaterialList &outMaterials) const +{ + outMaterials.clear(); + outMaterials.push_back(mMaterial); +} + +void ConvexShape::RestoreMaterialState(const PhysicsMaterialRefC *inMaterials, uint inNumMaterials) +{ + JPH_ASSERT(inNumMaterials == 1); + mMaterial = inMaterials[0]; +} + +void ConvexShape::sRegister() +{ + for (EShapeSubType s1 : sConvexSubShapeTypes) + for (EShapeSubType s2 : sConvexSubShapeTypes) + { + CollisionDispatch::sRegisterCollideShape(s1, s2, sCollideConvexVsConvex); + CollisionDispatch::sRegisterCastShape(s1, s2, sCastConvexVsConvex); + } +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/ConvexShape.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/ConvexShape.h new file mode 100644 index 00000000000..562403b52a8 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/ConvexShape.h @@ -0,0 +1,150 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +class CollideShapeSettings; + +/// Class that constructs a ConvexShape (abstract) +class JPH_EXPORT ConvexShapeSettings : public ShapeSettings +{ +public: + JPH_DECLARE_SERIALIZABLE_ABSTRACT(JPH_EXPORT, ConvexShapeSettings) + + /// Constructor + ConvexShapeSettings() = default; + explicit ConvexShapeSettings(const PhysicsMaterial *inMaterial) : mMaterial(inMaterial) { } + + /// Set the density of the object in kg / m^3 + void SetDensity(float inDensity) { mDensity = inDensity; } + + // Properties + RefConst mMaterial; ///< Material assigned to this shape + float mDensity = 1000.0f; ///< Uniform density of the interior of the convex object (kg / m^3) +}; + +/// Base class for all convex shapes. Defines a virtual interface. +class JPH_EXPORT ConvexShape : public Shape +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Constructor + explicit ConvexShape(EShapeSubType inSubType) : Shape(EShapeType::Convex, inSubType) { } + ConvexShape(EShapeSubType inSubType, const ConvexShapeSettings &inSettings, ShapeResult &outResult) : Shape(EShapeType::Convex, inSubType, inSettings, outResult), mMaterial(inSettings.mMaterial), mDensity(inSettings.mDensity) { } + ConvexShape(EShapeSubType inSubType, const PhysicsMaterial *inMaterial) : Shape(EShapeType::Convex, inSubType), mMaterial(inMaterial) { } + + // See Shape::GetSubShapeIDBitsRecursive + virtual uint GetSubShapeIDBitsRecursive() const override { return 0; } // Convex shapes don't have sub shapes + + // See Shape::GetMaterial + virtual const PhysicsMaterial * GetMaterial(const SubShapeID &inSubShapeID) const override { JPH_ASSERT(inSubShapeID.IsEmpty(), "Invalid subshape ID"); return GetMaterial(); } + + // See Shape::CastRay + virtual bool CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const override; + virtual void CastRay(const RayCast &inRay, const RayCastSettings &inRayCastSettings, const SubShapeIDCreator &inSubShapeIDCreator, CastRayCollector &ioCollector, const ShapeFilter &inShapeFilter = { }) const override; + + // See: Shape::CollidePoint + virtual void CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter = { }) const override; + + // See Shape::GetTrianglesStart + virtual void GetTrianglesStart(GetTrianglesContext &ioContext, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) const override; + + // See Shape::GetTrianglesNext + virtual int GetTrianglesNext(GetTrianglesContext &ioContext, int inMaxTrianglesRequested, Float3 *outTriangleVertices, const PhysicsMaterial **outMaterials = nullptr) const override; + + // See Shape::GetSubmergedVolume + virtual void GetSubmergedVolume(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, const Plane &inSurface, float &outTotalVolume, float &outSubmergedVolume, Vec3 &outCenterOfBuoyancy JPH_IF_DEBUG_RENDERER(, RVec3Arg inBaseOffset)) const override; + + /// Function that provides an interface for GJK + class Support + { + public: + /// Warning: Virtual destructor will not be called on this object! + virtual ~Support() = default; + + /// Calculate the support vector for this convex shape (includes / excludes the convex radius depending on how this was obtained). + /// Support vector is relative to the center of mass of the shape. + virtual Vec3 GetSupport(Vec3Arg inDirection) const = 0; + + /// Convex radius of shape. Collision detection on penetrating shapes is much more expensive, + /// so you can add a radius around objects to increase the shape. This makes it far less likely that they will actually penetrate. + virtual float GetConvexRadius() const = 0; + }; + + /// Buffer to hold a Support object, used to avoid dynamic memory allocations + class alignas(16) SupportBuffer + { + public: + uint8 mData[4160]; + }; + + /// How the GetSupport function should behave + enum class ESupportMode + { + ExcludeConvexRadius, ///< Return the shape excluding the convex radius, Support::GetConvexRadius will return the convex radius if there is one, but adding this radius may not result in the most accurate/efficient representation of shapes with sharp edges + IncludeConvexRadius, ///< Return the shape including the convex radius, Support::GetSupport includes the convex radius if there is one, Support::GetConvexRadius will return 0 + Default, ///< Use both Support::GetSupport add Support::GetConvexRadius to get a support point that matches the original shape as accurately/efficiently as possible + }; + + /// Returns an object that provides the GetSupport function for this shape. + /// inMode determines if this support function includes or excludes the convex radius. + /// of the values returned by the GetSupport function. This improves numerical accuracy of the results. + /// inScale scales this shape in local space. + virtual const Support * GetSupportFunction(ESupportMode inMode, SupportBuffer &inBuffer, Vec3Arg inScale) const = 0; + + /// Material of the shape + void SetMaterial(const PhysicsMaterial *inMaterial) { mMaterial = inMaterial; } + const PhysicsMaterial * GetMaterial() const { return mMaterial != nullptr? mMaterial : PhysicsMaterial::sDefault; } + + /// Set density of the shape (kg / m^3) + void SetDensity(float inDensity) { mDensity = inDensity; } + + /// Get density of the shape (kg / m^3) + float GetDensity() const { return mDensity; } + +#ifdef JPH_DEBUG_RENDERER + // See Shape::DrawGetSupportFunction + virtual void DrawGetSupportFunction(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inDrawSupportDirection) const override; + + // See Shape::DrawGetSupportingFace + virtual void DrawGetSupportingFace(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale) const override; +#endif // JPH_DEBUG_RENDERER + + // See Shape + virtual void SaveBinaryState(StreamOut &inStream) const override; + virtual void SaveMaterialState(PhysicsMaterialList &outMaterials) const override; + virtual void RestoreMaterialState(const PhysicsMaterialRefC *inMaterials, uint inNumMaterials) override; + + // Register shape functions with the registry + static void sRegister(); + +protected: + // See: Shape::RestoreBinaryState + virtual void RestoreBinaryState(StreamIn &inStream) override; + + /// Vertex list that forms a unit sphere + static const std::vector sUnitSphereTriangles; + +private: + // Class for GetTrianglesStart/Next + class CSGetTrianglesContext; + + // Helper functions called by CollisionDispatch + static void sCollideConvexVsConvex(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter); + static void sCastConvexVsConvex(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector); + + // Properties + RefConst mMaterial; ///< Material assigned to this shape + float mDensity = 1000.0f; ///< Uniform density of the interior of the convex object (kg / m^3) +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/CylinderShape.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/CylinderShape.cpp new file mode 100644 index 00000000000..51a9f3c54bd --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/CylinderShape.cpp @@ -0,0 +1,417 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef JPH_DEBUG_RENDERER + #include +#endif // JPH_DEBUG_RENDERER + +JPH_NAMESPACE_BEGIN + +JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(CylinderShapeSettings) +{ + JPH_ADD_BASE_CLASS(CylinderShapeSettings, ConvexShapeSettings) + + JPH_ADD_ATTRIBUTE(CylinderShapeSettings, mHalfHeight) + JPH_ADD_ATTRIBUTE(CylinderShapeSettings, mRadius) + JPH_ADD_ATTRIBUTE(CylinderShapeSettings, mConvexRadius) +} + +// Approximation of top face with 8 vertices +static const float cSin45 = 0.70710678118654752440084436210485f; +static const Vec3 cTopFace[] = +{ + Vec3(0.0f, 1.0f, 1.0f), + Vec3(cSin45, 1.0f, cSin45), + Vec3(1.0f, 1.0f, 0.0f), + Vec3(cSin45, 1.0f, -cSin45), + Vec3(-0.0f, 1.0f, -1.0f), + Vec3(-cSin45, 1.0f, -cSin45), + Vec3(-1.0f, 1.0f, 0.0f), + Vec3(-cSin45, 1.0f, cSin45) +}; + +static const std::vector sUnitCylinderTriangles = []() { + std::vector verts; + + const Vec3 bottom_offset(0.0f, -2.0f, 0.0f); + + int num_verts = sizeof(cTopFace) / sizeof(Vec3); + for (int i = 0; i < num_verts; ++i) + { + Vec3 t1 = cTopFace[i]; + Vec3 t2 = cTopFace[(i + 1) % num_verts]; + Vec3 b1 = cTopFace[i] + bottom_offset; + Vec3 b2 = cTopFace[(i + 1) % num_verts] + bottom_offset; + + // Top + verts.emplace_back(0.0f, 1.0f, 0.0f); + verts.push_back(t1); + verts.push_back(t2); + + // Bottom + verts.emplace_back(0.0f, -1.0f, 0.0f); + verts.push_back(b2); + verts.push_back(b1); + + // Side + verts.push_back(t1); + verts.push_back(b1); + verts.push_back(t2); + + verts.push_back(t2); + verts.push_back(b1); + verts.push_back(b2); + } + + return verts; +}(); + +ShapeSettings::ShapeResult CylinderShapeSettings::Create() const +{ + if (mCachedResult.IsEmpty()) + Ref shape = new CylinderShape(*this, mCachedResult); + return mCachedResult; +} + +CylinderShape::CylinderShape(const CylinderShapeSettings &inSettings, ShapeResult &outResult) : + ConvexShape(EShapeSubType::Cylinder, inSettings, outResult), + mHalfHeight(inSettings.mHalfHeight), + mRadius(inSettings.mRadius), + mConvexRadius(inSettings.mConvexRadius) +{ + if (inSettings.mHalfHeight < inSettings.mConvexRadius) + { + outResult.SetError("Invalid height"); + return; + } + + if (inSettings.mRadius < inSettings.mConvexRadius) + { + outResult.SetError("Invalid radius"); + return; + } + + if (inSettings.mConvexRadius < 0.0f) + { + outResult.SetError("Invalid convex radius"); + return; + } + + outResult.Set(this); +} + +CylinderShape::CylinderShape(float inHalfHeight, float inRadius, float inConvexRadius, const PhysicsMaterial *inMaterial) : + ConvexShape(EShapeSubType::Cylinder, inMaterial), + mHalfHeight(inHalfHeight), + mRadius(inRadius), + mConvexRadius(inConvexRadius) +{ + JPH_ASSERT(inHalfHeight >= inConvexRadius); + JPH_ASSERT(inRadius >= inConvexRadius); + JPH_ASSERT(inConvexRadius >= 0.0f); +} + +class CylinderShape::Cylinder final : public Support +{ +public: + Cylinder(float inHalfHeight, float inRadius, float inConvexRadius) : + mHalfHeight(inHalfHeight), + mRadius(inRadius), + mConvexRadius(inConvexRadius) + { + static_assert(sizeof(Cylinder) <= sizeof(SupportBuffer), "Buffer size too small"); + JPH_ASSERT(IsAligned(this, alignof(Cylinder))); + } + + virtual Vec3 GetSupport(Vec3Arg inDirection) const override + { + // Support mapping, taken from: + // A Fast and Robust GJK Implementation for Collision Detection of Convex Objects - Gino van den Bergen + // page 8 + float x = inDirection.GetX(), y = inDirection.GetY(), z = inDirection.GetZ(); + float o = sqrt(Square(x) + Square(z)); + if (o > 0.0f) + return Vec3((mRadius * x) / o, Sign(y) * mHalfHeight, (mRadius * z) / o); + else + return Vec3(0, Sign(y) * mHalfHeight, 0); + } + + virtual float GetConvexRadius() const override + { + return mConvexRadius; + } + +private: + float mHalfHeight; + float mRadius; + float mConvexRadius; +}; + +const ConvexShape::Support *CylinderShape::GetSupportFunction(ESupportMode inMode, SupportBuffer &inBuffer, Vec3Arg inScale) const +{ + JPH_ASSERT(IsValidScale(inScale)); + + // Get scaled cylinder + Vec3 abs_scale = inScale.Abs(); + float scale_xz = abs_scale.GetX(); + float scale_y = abs_scale.GetY(); + float scaled_half_height = scale_y * mHalfHeight; + float scaled_radius = scale_xz * mRadius; + float scaled_convex_radius = ScaleHelpers::ScaleConvexRadius(mConvexRadius, inScale); + + switch (inMode) + { + case ESupportMode::IncludeConvexRadius: + case ESupportMode::Default: + return new (&inBuffer) Cylinder(scaled_half_height, scaled_radius, 0.0f); + + case ESupportMode::ExcludeConvexRadius: + return new (&inBuffer) Cylinder(scaled_half_height - scaled_convex_radius, scaled_radius - scaled_convex_radius, scaled_convex_radius); + } + + JPH_ASSERT(false); + return nullptr; +} + +void CylinderShape::GetSupportingFace(const SubShapeID &inSubShapeID, Vec3Arg inDirection, Vec3Arg inScale, Mat44Arg inCenterOfMassTransform, SupportingFace &outVertices) const +{ + JPH_ASSERT(inSubShapeID.IsEmpty(), "Invalid subshape ID"); + JPH_ASSERT(IsValidScale(inScale)); + + // Get scaled cylinder + Vec3 abs_scale = inScale.Abs(); + float scale_xz = abs_scale.GetX(); + float scale_y = abs_scale.GetY(); + float scaled_half_height = scale_y * mHalfHeight; + float scaled_radius = scale_xz * mRadius; + + float x = inDirection.GetX(), y = inDirection.GetY(), z = inDirection.GetZ(); + float o = sqrt(Square(x) + Square(z)); + + // If o / |y| > scaled_radius / scaled_half_height, we're hitting the side + if (o * scaled_half_height > scaled_radius * abs(y)) + { + // Hitting side + float f = -scaled_radius / o; + float vx = x * f; + float vz = z * f; + outVertices.push_back(inCenterOfMassTransform * Vec3(vx, scaled_half_height, vz)); + outVertices.push_back(inCenterOfMassTransform * Vec3(vx, -scaled_half_height, vz)); + } + else + { + // Hitting top or bottom + Vec3 multiplier = y < 0.0f? Vec3(scaled_radius, scaled_half_height, scaled_radius) : Vec3(-scaled_radius, -scaled_half_height, scaled_radius); + Mat44 transform = inCenterOfMassTransform.PreScaled(multiplier); + for (const Vec3 &v : cTopFace) + outVertices.push_back(transform * v); + } +} + +MassProperties CylinderShape::GetMassProperties() const +{ + MassProperties p; + + // Mass is surface of circle * height + float radius_sq = Square(mRadius); + float height = 2.0f * mHalfHeight; + p.mMass = JPH_PI * radius_sq * height * GetDensity(); + + // Inertia according to https://en.wikipedia.org/wiki/List_of_moments_of_inertia: + float inertia_y = radius_sq * p.mMass * 0.5f; + float inertia_x = inertia_y * 0.5f + p.mMass * height * height / 12.0f; + float inertia_z = inertia_x; + + // Set inertia + p.mInertia = Mat44::sScale(Vec3(inertia_x, inertia_y, inertia_z)); + + return p; +} + +Vec3 CylinderShape::GetSurfaceNormal(const SubShapeID &inSubShapeID, Vec3Arg inLocalSurfacePosition) const +{ + JPH_ASSERT(inSubShapeID.IsEmpty(), "Invalid subshape ID"); + + // Calculate distance to infinite cylinder surface + Vec3 local_surface_position_xz(inLocalSurfacePosition.GetX(), 0, inLocalSurfacePosition.GetZ()); + float local_surface_position_xz_len = local_surface_position_xz.Length(); + float distance_to_curved_surface = abs(local_surface_position_xz_len - mRadius); + + // Calculate distance to top or bottom plane + float distance_to_top_or_bottom = abs(abs(inLocalSurfacePosition.GetY()) - mHalfHeight); + + // Return normal according to closest surface + if (distance_to_curved_surface < distance_to_top_or_bottom) + return local_surface_position_xz / local_surface_position_xz_len; + else + return inLocalSurfacePosition.GetY() > 0.0f? Vec3::sAxisY() : -Vec3::sAxisY(); +} + +AABox CylinderShape::GetLocalBounds() const +{ + Vec3 extent = Vec3(mRadius, mHalfHeight, mRadius); + return AABox(-extent, extent); +} + +#ifdef JPH_DEBUG_RENDERER +void CylinderShape::Draw(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inUseMaterialColors, bool inDrawWireframe) const +{ + DebugRenderer::EDrawMode draw_mode = inDrawWireframe? DebugRenderer::EDrawMode::Wireframe : DebugRenderer::EDrawMode::Solid; + inRenderer->DrawCylinder(inCenterOfMassTransform * Mat44::sScale(inScale.Abs()), mHalfHeight, mRadius, inUseMaterialColors? GetMaterial()->GetDebugColor() : inColor, DebugRenderer::ECastShadow::On, draw_mode); +} +#endif // JPH_DEBUG_RENDERER + +bool CylinderShape::CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const +{ + // Test ray against capsule + float fraction = RayCylinder(inRay.mOrigin, inRay.mDirection, mHalfHeight, mRadius); + if (fraction < ioHit.mFraction) + { + ioHit.mFraction = fraction; + ioHit.mSubShapeID2 = inSubShapeIDCreator.GetID(); + return true; + } + return false; +} + +void CylinderShape::CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter) const +{ + // Test shape filter + if (!inShapeFilter.ShouldCollide(this, inSubShapeIDCreator.GetID())) + return; + + // Check if the point is in the cylinder + if (abs(inPoint.GetY()) <= mHalfHeight // Within the height + && Square(inPoint.GetX()) + Square(inPoint.GetZ()) <= Square(mRadius)) // Within the radius + ioCollector.AddHit({ TransformedShape::sGetBodyID(ioCollector.GetContext()), inSubShapeIDCreator.GetID() }); +} + +void CylinderShape::CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, SoftBodyVertex *ioVertices, uint inNumVertices, [[maybe_unused]] float inDeltaTime, [[maybe_unused]] Vec3Arg inDisplacementDueToGravity, int inCollidingShapeIndex) const +{ + JPH_ASSERT(IsValidScale(inScale)); + + Mat44 inverse_transform = inCenterOfMassTransform.InversedRotationTranslation(); + + // Get scaled cylinder + Vec3 abs_scale = inScale.Abs(); + float half_height = abs_scale.GetY() * mHalfHeight; + float radius = abs_scale.GetX() * mRadius; + + for (SoftBodyVertex *v = ioVertices, *sbv_end = ioVertices + inNumVertices; v < sbv_end; ++v) + if (v->mInvMass > 0.0f) + { + Vec3 local_pos = inverse_transform * v->mPosition; + + // Calculate penetration into side surface + Vec3 side_normal = local_pos; + side_normal.SetY(0.0f); + float side_normal_length = side_normal.Length(); + float side_penetration = radius - side_normal_length; + + // Calculate penetration into top or bottom plane + float top_penetration = half_height - abs(local_pos.GetY()); + + Vec3 point, normal; + if (side_penetration < 0.0f && top_penetration < 0.0f) + { + // We're outside the cylinder height and radius + point = side_normal * (radius / side_normal_length) + Vec3(0, half_height * Sign(local_pos.GetY()), 0); + normal = (local_pos - point).NormalizedOr(Vec3::sAxisY()); + } + else if (side_penetration < top_penetration) + { + // Side surface is closest + normal = side_normal_length > 0.0f? side_normal / side_normal_length : Vec3::sAxisX(); + point = radius * normal; + } + else + { + // Top or bottom plane is closest + normal = Vec3(0, Sign(local_pos.GetY()), 0); + point = half_height * normal; + } + + // Calculate penetration + Plane plane = Plane::sFromPointAndNormal(point, normal); + float penetration = -plane.SignedDistance(local_pos); + if (penetration > v->mLargestPenetration) + { + v->mLargestPenetration = penetration; + + // Store collision + v->mCollisionPlane = plane.GetTransformed(inCenterOfMassTransform); + v->mCollidingShapeIndex = inCollidingShapeIndex; + } + } +} + +void CylinderShape::TransformShape(Mat44Arg inCenterOfMassTransform, TransformedShapeCollector &ioCollector) const +{ + Vec3 scale; + Mat44 transform = inCenterOfMassTransform.Decompose(scale); + TransformedShape ts(RVec3(transform.GetTranslation()), transform.GetQuaternion(), this, BodyID(), SubShapeIDCreator()); + Vec3 abs_scale = scale.Abs(); + float xz = 0.5f * (abs_scale.GetX() + abs_scale.GetZ()); + ts.SetShapeScale(Vec3(xz, abs_scale.GetY(), xz)); + ioCollector.AddHit(ts); +} + +void CylinderShape::GetTrianglesStart(GetTrianglesContext &ioContext, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) const +{ + Mat44 unit_cylinder_transform(Vec4(mRadius, 0, 0, 0), Vec4(0, mHalfHeight, 0, 0), Vec4(0, 0, mRadius, 0), Vec4(0, 0, 0, 1)); + new (&ioContext) GetTrianglesContextVertexList(inPositionCOM, inRotation, inScale, unit_cylinder_transform, sUnitCylinderTriangles.data(), sUnitCylinderTriangles.size(), GetMaterial()); +} + +int CylinderShape::GetTrianglesNext(GetTrianglesContext &ioContext, int inMaxTrianglesRequested, Float3 *outTriangleVertices, const PhysicsMaterial **outMaterials) const +{ + return ((GetTrianglesContextVertexList &)ioContext).GetTrianglesNext(inMaxTrianglesRequested, outTriangleVertices, outMaterials); +} + +void CylinderShape::SaveBinaryState(StreamOut &inStream) const +{ + ConvexShape::SaveBinaryState(inStream); + + inStream.Write(mHalfHeight); + inStream.Write(mRadius); + inStream.Write(mConvexRadius); +} + +void CylinderShape::RestoreBinaryState(StreamIn &inStream) +{ + ConvexShape::RestoreBinaryState(inStream); + + inStream.Read(mHalfHeight); + inStream.Read(mRadius); + inStream.Read(mConvexRadius); +} + +bool CylinderShape::IsValidScale(Vec3Arg inScale) const +{ + // X and Z need same scale + Vec3 abs_scale = inScale.Abs(); + return ConvexShape::IsValidScale(inScale) && abs_scale.Swizzle().IsClose(abs_scale, ScaleHelpers::cScaleToleranceSq); +} + +void CylinderShape::sRegister() +{ + ShapeFunctions &f = ShapeFunctions::sGet(EShapeSubType::Cylinder); + f.mConstruct = []() -> Shape * { return new CylinderShape; }; + f.mColor = Color::sGreen; +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/CylinderShape.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/CylinderShape.h new file mode 100644 index 00000000000..52108b13e8a --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/CylinderShape.h @@ -0,0 +1,126 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +JPH_NAMESPACE_BEGIN + +/// Class that constructs a CylinderShape +class JPH_EXPORT CylinderShapeSettings final : public ConvexShapeSettings +{ +public: + JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, CylinderShapeSettings) + + /// Default constructor for deserialization + CylinderShapeSettings() = default; + + /// Create a shape centered around the origin with one top at (0, -inHalfHeight, 0) and the other at (0, inHalfHeight, 0) and radius inRadius. + /// (internally the convex radius will be subtracted from the cylinder the total cylinder will not grow with the convex radius, but the edges of the cylinder will be rounded a bit). + CylinderShapeSettings(float inHalfHeight, float inRadius, float inConvexRadius = cDefaultConvexRadius, const PhysicsMaterial *inMaterial = nullptr) : ConvexShapeSettings(inMaterial), mHalfHeight(inHalfHeight), mRadius(inRadius), mConvexRadius(inConvexRadius) { } + + // See: ShapeSettings + virtual ShapeResult Create() const override; + + float mHalfHeight = 0.0f; + float mRadius = 0.0f; + float mConvexRadius = 0.0f; +}; + +/// A cylinder +class JPH_EXPORT CylinderShape final : public ConvexShape +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Constructor + CylinderShape() : ConvexShape(EShapeSubType::Cylinder) { } + CylinderShape(const CylinderShapeSettings &inSettings, ShapeResult &outResult); + + /// Create a shape centered around the origin with one top at (0, -inHalfHeight, 0) and the other at (0, inHalfHeight, 0) and radius inRadius. + /// (internally the convex radius will be subtracted from the cylinder the total cylinder will not grow with the convex radius, but the edges of the cylinder will be rounded a bit). + CylinderShape(float inHalfHeight, float inRadius, float inConvexRadius = cDefaultConvexRadius, const PhysicsMaterial *inMaterial = nullptr); + + /// Get half height of cylinder + float GetHalfHeight() const { return mHalfHeight; } + + /// Get radius of cylinder + float GetRadius() const { return mRadius; } + + // See Shape::GetLocalBounds + virtual AABox GetLocalBounds() const override; + + // See Shape::GetInnerRadius + virtual float GetInnerRadius() const override { return min(mHalfHeight, mRadius); } + + // See Shape::GetMassProperties + virtual MassProperties GetMassProperties() const override; + + // See Shape::GetSurfaceNormal + virtual Vec3 GetSurfaceNormal(const SubShapeID &inSubShapeID, Vec3Arg inLocalSurfacePosition) const override; + + // See Shape::GetSupportingFace + virtual void GetSupportingFace(const SubShapeID &inSubShapeID, Vec3Arg inDirection, Vec3Arg inScale, Mat44Arg inCenterOfMassTransform, SupportingFace &outVertices) const override; + + // See ConvexShape::GetSupportFunction + virtual const Support * GetSupportFunction(ESupportMode inMode, SupportBuffer &inBuffer, Vec3Arg inScale) const override; + +#ifdef JPH_DEBUG_RENDERER + // See Shape::Draw + virtual void Draw(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inUseMaterialColors, bool inDrawWireframe) const override; +#endif // JPH_DEBUG_RENDERER + + // See Shape::CastRay + using ConvexShape::CastRay; + virtual bool CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const override; + + // See: Shape::CollidePoint + virtual void CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter = { }) const override; + + // See: Shape::CollideSoftBodyVertices + virtual void CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, SoftBodyVertex *ioVertices, uint inNumVertices, float inDeltaTime, Vec3Arg inDisplacementDueToGravity, int inCollidingShapeIndex) const override; + + // See Shape::TransformShape + virtual void TransformShape(Mat44Arg inCenterOfMassTransform, TransformedShapeCollector &ioCollector) const override; + + // See Shape::GetTrianglesStart + virtual void GetTrianglesStart(GetTrianglesContext &ioContext, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) const override; + + // See Shape::GetTrianglesNext + virtual int GetTrianglesNext(GetTrianglesContext &ioContext, int inMaxTrianglesRequested, Float3 *outTriangleVertices, const PhysicsMaterial **outMaterials = nullptr) const override; + + // See Shape + virtual void SaveBinaryState(StreamOut &inStream) const override; + + // See Shape::GetStats + virtual Stats GetStats() const override { return Stats(sizeof(*this), 0); } + + // See Shape::GetVolume + virtual float GetVolume() const override { return 2.0f * JPH_PI * mHalfHeight * Square(mRadius); } + + /// Get the convex radius of this cylinder + float GetConvexRadius() const { return mConvexRadius; } + + // See Shape::IsValidScale + virtual bool IsValidScale(Vec3Arg inScale) const override; + + // Register shape functions with the registry + static void sRegister(); + +protected: + // See: Shape::RestoreBinaryState + virtual void RestoreBinaryState(StreamIn &inStream) override; + +private: + // Class for GetSupportFunction + class Cylinder; + + float mHalfHeight = 0.0f; + float mRadius = 0.0f; + float mConvexRadius = 0.0f; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/DecoratedShape.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/DecoratedShape.cpp new file mode 100644 index 00000000000..339f7836376 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/DecoratedShape.cpp @@ -0,0 +1,87 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include + +JPH_NAMESPACE_BEGIN + +JPH_IMPLEMENT_SERIALIZABLE_ABSTRACT(DecoratedShapeSettings) +{ + JPH_ADD_BASE_CLASS(DecoratedShapeSettings, ShapeSettings) + + JPH_ADD_ATTRIBUTE(DecoratedShapeSettings, mInnerShape) +} + +DecoratedShape::DecoratedShape(EShapeSubType inSubType, const DecoratedShapeSettings &inSettings, ShapeResult &outResult) : + Shape(EShapeType::Decorated, inSubType, inSettings, outResult) +{ + // Check that there's a shape + if (inSettings.mInnerShape == nullptr && inSettings.mInnerShapePtr == nullptr) + { + outResult.SetError("Inner shape is null!"); + return; + } + + if (inSettings.mInnerShapePtr != nullptr) + { + // Use provided shape + mInnerShape = inSettings.mInnerShapePtr; + } + else + { + // Create child shape + ShapeResult child_result = inSettings.mInnerShape->Create(); + if (!child_result.IsValid()) + { + outResult = child_result; + return; + } + mInnerShape = child_result.Get(); + } +} + +const PhysicsMaterial *DecoratedShape::GetMaterial(const SubShapeID &inSubShapeID) const +{ + return mInnerShape->GetMaterial(inSubShapeID); +} + +void DecoratedShape::GetSupportingFace(const SubShapeID &inSubShapeID, Vec3Arg inDirection, Vec3Arg inScale, Mat44Arg inCenterOfMassTransform, SupportingFace &outVertices) const +{ + mInnerShape->GetSupportingFace(inSubShapeID, inDirection, inScale, inCenterOfMassTransform, outVertices); +} + +uint64 DecoratedShape::GetSubShapeUserData(const SubShapeID &inSubShapeID) const +{ + return mInnerShape->GetSubShapeUserData(inSubShapeID); +} + +void DecoratedShape::SaveSubShapeState(ShapeList &outSubShapes) const +{ + outSubShapes.clear(); + outSubShapes.push_back(mInnerShape); +} + +void DecoratedShape::RestoreSubShapeState(const ShapeRefC *inSubShapes, uint inNumShapes) +{ + JPH_ASSERT(inNumShapes == 1); + mInnerShape = inSubShapes[0]; +} + +Shape::Stats DecoratedShape::GetStatsRecursive(VisitedShapes &ioVisitedShapes) const +{ + // Get own stats + Stats stats = Shape::GetStatsRecursive(ioVisitedShapes); + + // Add child stats + Stats child_stats = mInnerShape->GetStatsRecursive(ioVisitedShapes); + stats.mSizeBytes += child_stats.mSizeBytes; + stats.mNumTriangles += child_stats.mNumTriangles; + + return stats; +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/DecoratedShape.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/DecoratedShape.h new file mode 100644 index 00000000000..5c97dcebc9d --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/DecoratedShape.h @@ -0,0 +1,70 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +/// Class that constructs a DecoratedShape +class JPH_EXPORT DecoratedShapeSettings : public ShapeSettings +{ + JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, DecoratedShapeSettings) + + /// Default constructor for deserialization + DecoratedShapeSettings() = default; + + /// Constructor that decorates another shape + explicit DecoratedShapeSettings(const ShapeSettings *inShape) : mInnerShape(inShape) { } + explicit DecoratedShapeSettings(const Shape *inShape) : mInnerShapePtr(inShape) { } + + RefConst mInnerShape; ///< Sub shape (either this or mShapePtr needs to be filled up) + RefConst mInnerShapePtr; ///< Sub shape (either this or mShape needs to be filled up) +}; + +/// Base class for shapes that decorate another shape with extra functionality (e.g. scale, translation etc.) +class JPH_EXPORT DecoratedShape : public Shape +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Constructor + explicit DecoratedShape(EShapeSubType inSubType) : Shape(EShapeType::Decorated, inSubType) { } + DecoratedShape(EShapeSubType inSubType, const Shape *inInnerShape) : Shape(EShapeType::Decorated, inSubType), mInnerShape(inInnerShape) { } + DecoratedShape(EShapeSubType inSubType, const DecoratedShapeSettings &inSettings, ShapeResult &outResult); + + /// Access to the decorated inner shape + const Shape * GetInnerShape() const { return mInnerShape; } + + // See Shape::MustBeStatic + virtual bool MustBeStatic() const override { return mInnerShape->MustBeStatic(); } + + // See Shape::GetCenterOfMass + virtual Vec3 GetCenterOfMass() const override { return mInnerShape->GetCenterOfMass(); } + + // See Shape::GetSubShapeIDBitsRecursive + virtual uint GetSubShapeIDBitsRecursive() const override { return mInnerShape->GetSubShapeIDBitsRecursive(); } + + // See Shape::GetMaterial + virtual const PhysicsMaterial * GetMaterial(const SubShapeID &inSubShapeID) const override; + + // See Shape::GetSupportingFace + virtual void GetSupportingFace(const SubShapeID &inSubShapeID, Vec3Arg inDirection, Vec3Arg inScale, Mat44Arg inCenterOfMassTransform, SupportingFace &outVertices) const override; + + // See Shape::GetSubShapeUserData + virtual uint64 GetSubShapeUserData(const SubShapeID &inSubShapeID) const override; + + // See Shape + virtual void SaveSubShapeState(ShapeList &outSubShapes) const override; + virtual void RestoreSubShapeState(const ShapeRefC *inSubShapes, uint inNumShapes) override; + + // See Shape::GetStatsRecursive + virtual Stats GetStatsRecursive(VisitedShapes &ioVisitedShapes) const override; + +protected: + RefConst mInnerShape; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/GetTrianglesContext.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/GetTrianglesContext.h new file mode 100644 index 00000000000..c594ece6486 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/GetTrianglesContext.h @@ -0,0 +1,244 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +class PhysicsMaterial; + +/// Implementation of GetTrianglesStart/Next that uses a fixed list of vertices for the triangles. These are transformed into world space when getting the triangles. +class GetTrianglesContextVertexList +{ +public: + /// Constructor, to be called in GetTrianglesStart + GetTrianglesContextVertexList(Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale, Mat44Arg inLocalTransform, const Vec3 *inTriangleVertices, size_t inNumTriangleVertices, const PhysicsMaterial *inMaterial) : + mLocalToWorld(Mat44::sRotationTranslation(inRotation, inPositionCOM) * Mat44::sScale(inScale) * inLocalTransform), + mTriangleVertices(inTriangleVertices), + mNumTriangleVertices(inNumTriangleVertices), + mMaterial(inMaterial), + mIsInsideOut(ScaleHelpers::IsInsideOut(inScale)) + { + static_assert(sizeof(GetTrianglesContextVertexList) <= sizeof(Shape::GetTrianglesContext), "GetTrianglesContext too small"); + JPH_ASSERT(IsAligned(this, alignof(GetTrianglesContextVertexList))); + JPH_ASSERT(inNumTriangleVertices % 3 == 0); + } + + /// @see Shape::GetTrianglesNext + int GetTrianglesNext(int inMaxTrianglesRequested, Float3 *outTriangleVertices, const PhysicsMaterial **outMaterials) + { + JPH_ASSERT(inMaxTrianglesRequested >= Shape::cGetTrianglesMinTrianglesRequested); + + int total_num_vertices = min(inMaxTrianglesRequested * 3, int(mNumTriangleVertices - mCurrentVertex)); + + if (mIsInsideOut) + { + // Store triangles flipped + for (const Vec3 *v = mTriangleVertices + mCurrentVertex, *v_end = v + total_num_vertices; v < v_end; v += 3) + { + (mLocalToWorld * v[0]).StoreFloat3(outTriangleVertices++); + (mLocalToWorld * v[2]).StoreFloat3(outTriangleVertices++); + (mLocalToWorld * v[1]).StoreFloat3(outTriangleVertices++); + } + } + else + { + // Store triangles + for (const Vec3 *v = mTriangleVertices + mCurrentVertex, *v_end = v + total_num_vertices; v < v_end; v += 3) + { + (mLocalToWorld * v[0]).StoreFloat3(outTriangleVertices++); + (mLocalToWorld * v[1]).StoreFloat3(outTriangleVertices++); + (mLocalToWorld * v[2]).StoreFloat3(outTriangleVertices++); + } + } + + // Update the current vertex to point to the next vertex to get + mCurrentVertex += total_num_vertices; + int total_num_triangles = total_num_vertices / 3; + + // Store materials + if (outMaterials != nullptr) + for (const PhysicsMaterial **m = outMaterials, **m_end = outMaterials + total_num_triangles; m < m_end; ++m) + *m = mMaterial; + + return total_num_triangles; + } + + /// Helper function that creates a vertex list of a half unit sphere (top part) + static void sCreateHalfUnitSphereTop(std::vector &ioVertices, int inDetailLevel) + { + sCreateUnitSphereHelper(ioVertices, Vec3::sAxisX(), Vec3::sAxisY(), Vec3::sAxisZ(), inDetailLevel); + sCreateUnitSphereHelper(ioVertices, Vec3::sAxisY(), -Vec3::sAxisX(), Vec3::sAxisZ(), inDetailLevel); + sCreateUnitSphereHelper(ioVertices, Vec3::sAxisY(), Vec3::sAxisX(), -Vec3::sAxisZ(), inDetailLevel); + sCreateUnitSphereHelper(ioVertices, -Vec3::sAxisX(), Vec3::sAxisY(), -Vec3::sAxisZ(), inDetailLevel); + } + + /// Helper function that creates a vertex list of a half unit sphere (bottom part) + static void sCreateHalfUnitSphereBottom(std::vector &ioVertices, int inDetailLevel) + { + sCreateUnitSphereHelper(ioVertices, -Vec3::sAxisX(), -Vec3::sAxisY(), Vec3::sAxisZ(), inDetailLevel); + sCreateUnitSphereHelper(ioVertices, -Vec3::sAxisY(), Vec3::sAxisX(), Vec3::sAxisZ(), inDetailLevel); + sCreateUnitSphereHelper(ioVertices, Vec3::sAxisX(), -Vec3::sAxisY(), -Vec3::sAxisZ(), inDetailLevel); + sCreateUnitSphereHelper(ioVertices, -Vec3::sAxisY(), -Vec3::sAxisX(), -Vec3::sAxisZ(), inDetailLevel); + } + + /// Helper function that creates an open cylinder of half height 1 and radius 1 + static void sCreateUnitOpenCylinder(std::vector &ioVertices, int inDetailLevel) + { + const Vec3 bottom_offset(0.0f, -2.0f, 0.0f); + int num_verts = 4 * (1 << inDetailLevel); + for (int i = 0; i < num_verts; ++i) + { + float angle1 = 2.0f * JPH_PI * (float(i) / num_verts); + float angle2 = 2.0f * JPH_PI * (float(i + 1) / num_verts); + + Vec3 t1(Sin(angle1), 1.0f, Cos(angle1)); + Vec3 t2(Sin(angle2), 1.0f, Cos(angle2)); + Vec3 b1 = t1 + bottom_offset; + Vec3 b2 = t2 + bottom_offset; + + ioVertices.push_back(t1); + ioVertices.push_back(b1); + ioVertices.push_back(t2); + + ioVertices.push_back(t2); + ioVertices.push_back(b1); + ioVertices.push_back(b2); + } + } + +private: + /// Recursive helper function for creating a sphere + static void sCreateUnitSphereHelper(std::vector &ioVertices, Vec3Arg inV1, Vec3Arg inV2, Vec3Arg inV3, int inLevel) + { + Vec3 center1 = (inV1 + inV2).Normalized(); + Vec3 center2 = (inV2 + inV3).Normalized(); + Vec3 center3 = (inV3 + inV1).Normalized(); + + if (inLevel > 0) + { + int new_level = inLevel - 1; + sCreateUnitSphereHelper(ioVertices, inV1, center1, center3, new_level); + sCreateUnitSphereHelper(ioVertices, center1, center2, center3, new_level); + sCreateUnitSphereHelper(ioVertices, center1, inV2, center2, new_level); + sCreateUnitSphereHelper(ioVertices, center3, center2, inV3, new_level); + } + else + { + ioVertices.push_back(inV1); + ioVertices.push_back(inV2); + ioVertices.push_back(inV3); + } + } + + Mat44 mLocalToWorld; + const Vec3 * mTriangleVertices; + size_t mNumTriangleVertices; + size_t mCurrentVertex = 0; + const PhysicsMaterial * mMaterial; + bool mIsInsideOut; +}; + +/// Implementation of GetTrianglesStart/Next that uses a multiple fixed lists of vertices for the triangles. These are transformed into world space when getting the triangles. +class GetTrianglesContextMultiVertexList +{ +public: + /// Constructor, to be called in GetTrianglesStart + GetTrianglesContextMultiVertexList(bool inIsInsideOut, const PhysicsMaterial *inMaterial) : + mMaterial(inMaterial), + mIsInsideOut(inIsInsideOut) + { + static_assert(sizeof(GetTrianglesContextMultiVertexList) <= sizeof(Shape::GetTrianglesContext), "GetTrianglesContext too small"); + JPH_ASSERT(IsAligned(this, alignof(GetTrianglesContextMultiVertexList))); + } + + /// Add a mesh part and its transform + void AddPart(Mat44Arg inLocalToWorld, const Vec3 *inTriangleVertices, size_t inNumTriangleVertices) + { + JPH_ASSERT(inNumTriangleVertices % 3 == 0); + + mParts.push_back({ inLocalToWorld, inTriangleVertices, inNumTriangleVertices }); + } + + /// @see Shape::GetTrianglesNext + int GetTrianglesNext(int inMaxTrianglesRequested, Float3 *outTriangleVertices, const PhysicsMaterial **outMaterials) + { + JPH_ASSERT(inMaxTrianglesRequested >= Shape::cGetTrianglesMinTrianglesRequested); + + int total_num_vertices = 0; + int max_vertices_requested = inMaxTrianglesRequested * 3; + + // Loop over parts + for (; mCurrentPart < mParts.size(); ++mCurrentPart) + { + const Part &part = mParts[mCurrentPart]; + + // Calculate how many vertices to take from this part + int part_num_vertices = min(max_vertices_requested, int(part.mNumTriangleVertices - mCurrentVertex)); + if (part_num_vertices == 0) + break; + + max_vertices_requested -= part_num_vertices; + total_num_vertices += part_num_vertices; + + if (mIsInsideOut) + { + // Store triangles flipped + for (const Vec3 *v = part.mTriangleVertices + mCurrentVertex, *v_end = v + part_num_vertices; v < v_end; v += 3) + { + (part.mLocalToWorld * v[0]).StoreFloat3(outTriangleVertices++); + (part.mLocalToWorld * v[2]).StoreFloat3(outTriangleVertices++); + (part.mLocalToWorld * v[1]).StoreFloat3(outTriangleVertices++); + } + } + else + { + // Store triangles + for (const Vec3 *v = part.mTriangleVertices + mCurrentVertex, *v_end = v + part_num_vertices; v < v_end; v += 3) + { + (part.mLocalToWorld * v[0]).StoreFloat3(outTriangleVertices++); + (part.mLocalToWorld * v[1]).StoreFloat3(outTriangleVertices++); + (part.mLocalToWorld * v[2]).StoreFloat3(outTriangleVertices++); + } + } + + // Update the current vertex to point to the next vertex to get + mCurrentVertex += part_num_vertices; + + // Check if we completed this part + if (mCurrentVertex < part.mNumTriangleVertices) + break; + + // Reset current vertex for the next part + mCurrentVertex = 0; + } + + int total_num_triangles = total_num_vertices / 3; + + // Store materials + if (outMaterials != nullptr) + for (const PhysicsMaterial **m = outMaterials, **m_end = outMaterials + total_num_triangles; m < m_end; ++m) + *m = mMaterial; + + return total_num_triangles; + } + +private: + struct Part + { + Mat44 mLocalToWorld; + const Vec3 * mTriangleVertices; + size_t mNumTriangleVertices; + }; + + StaticArray mParts; + uint mCurrentPart = 0; + size_t mCurrentVertex = 0; + const PhysicsMaterial * mMaterial; + bool mIsInsideOut; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/HeightFieldShape.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/HeightFieldShape.cpp new file mode 100644 index 00000000000..7a0352f60be --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/HeightFieldShape.cpp @@ -0,0 +1,2624 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//#define JPH_DEBUG_HEIGHT_FIELD + +JPH_NAMESPACE_BEGIN + +#ifdef JPH_DEBUG_RENDERER +bool HeightFieldShape::sDrawTriangleOutlines = false; +#endif // JPH_DEBUG_RENDERER + +using namespace HeightFieldShapeConstants; + +JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(HeightFieldShapeSettings) +{ + JPH_ADD_BASE_CLASS(HeightFieldShapeSettings, ShapeSettings) + + JPH_ADD_ATTRIBUTE(HeightFieldShapeSettings, mHeightSamples) + JPH_ADD_ATTRIBUTE(HeightFieldShapeSettings, mOffset) + JPH_ADD_ATTRIBUTE(HeightFieldShapeSettings, mScale) + JPH_ADD_ATTRIBUTE(HeightFieldShapeSettings, mMinHeightValue) + JPH_ADD_ATTRIBUTE(HeightFieldShapeSettings, mMaxHeightValue) + JPH_ADD_ATTRIBUTE(HeightFieldShapeSettings, mSampleCount) + JPH_ADD_ATTRIBUTE(HeightFieldShapeSettings, mBlockSize) + JPH_ADD_ATTRIBUTE(HeightFieldShapeSettings, mBitsPerSample) + JPH_ADD_ATTRIBUTE(HeightFieldShapeSettings, mMaterialIndices) + JPH_ADD_ATTRIBUTE(HeightFieldShapeSettings, mMaterials) + JPH_ADD_ATTRIBUTE(HeightFieldShapeSettings, mActiveEdgeCosThresholdAngle) +} + +const uint HeightFieldShape::sGridOffsets[] = +{ + 0, // level: 0, max x/y: 0, offset: 0 + 1, // level: 1, max x/y: 1, offset: 1 + 5, // level: 2, max x/y: 3, offset: 1 + 4 + 21, // level: 3, max x/y: 7, offset: 1 + 4 + 16 + 85, // level: 4, max x/y: 15, offset: 1 + 4 + 16 + 64 + 341, // level: 5, max x/y: 31, offset: 1 + 4 + 16 + 64 + 256 + 1365, // level: 6, max x/y: 63, offset: 1 + 4 + 16 + 64 + 256 + 1024 + 5461, // level: 7, max x/y: 127, offset: 1 + 4 + 16 + 64 + 256 + 1024 + 4096 + 21845, // level: 8, max x/y: 255, offset: 1 + 4 + 16 + 64 + 256 + 1024 + 4096 + ... + 87381, // level: 9, max x/y: 511, offset: 1 + 4 + 16 + 64 + 256 + 1024 + 4096 + ... + 349525, // level: 10, max x/y: 1023, offset: 1 + 4 + 16 + 64 + 256 + 1024 + 4096 + ... + 1398101, // level: 11, max x/y: 2047, offset: 1 + 4 + 16 + 64 + 256 + 1024 + 4096 + ... + 5592405, // level: 12, max x/y: 4095, offset: 1 + 4 + 16 + 64 + 256 + 1024 + 4096 + ... + 22369621, // level: 13, max x/y: 8191, offset: 1 + 4 + 16 + 64 + 256 + 1024 + 4096 + ... + 89478485, // level: 14, max x/y: 16383, offset: 1 + 4 + 16 + 64 + 256 + 1024 + 4096 + ... +}; + +HeightFieldShapeSettings::HeightFieldShapeSettings(const float *inSamples, Vec3Arg inOffset, Vec3Arg inScale, uint32 inSampleCount, const uint8 *inMaterialIndices, const PhysicsMaterialList &inMaterialList) : + mOffset(inOffset), + mScale(inScale), + mSampleCount(inSampleCount) +{ + mHeightSamples.resize(inSampleCount * inSampleCount); + memcpy(&mHeightSamples[0], inSamples, inSampleCount * inSampleCount * sizeof(float)); + + if (!inMaterialList.empty() && inMaterialIndices != nullptr) + { + mMaterialIndices.resize(Square(inSampleCount - 1)); + memcpy(&mMaterialIndices[0], inMaterialIndices, Square(inSampleCount - 1) * sizeof(uint8)); + mMaterials = inMaterialList; + } + else + { + JPH_ASSERT(inMaterialList.empty()); + JPH_ASSERT(inMaterialIndices == nullptr); + } +} + +ShapeSettings::ShapeResult HeightFieldShapeSettings::Create() const +{ + if (mCachedResult.IsEmpty()) + Ref shape = new HeightFieldShape(*this, mCachedResult); + return mCachedResult; +} + +void HeightFieldShapeSettings::DetermineMinAndMaxSample(float &outMinValue, float &outMaxValue, float &outQuantizationScale) const +{ + // Determine min and max value + outMinValue = mMinHeightValue; + outMaxValue = mMaxHeightValue; + for (float h : mHeightSamples) + if (h != cNoCollisionValue) + { + outMinValue = min(outMinValue, h); + outMaxValue = max(outMaxValue, h); + } + + // Prevent dividing by zero by setting a minimal height difference + float height_diff = max(outMaxValue - outMinValue, 1.0e-6f); + + // Calculate the scale factor to quantize to 16 bits + outQuantizationScale = float(cMaxHeightValue16) / height_diff; +} + +uint32 HeightFieldShapeSettings::CalculateBitsPerSampleForError(float inMaxError) const +{ + // Start with 1 bit per sample + uint32 bits_per_sample = 1; + + // Determine total range + float min_value, max_value, scale; + DetermineMinAndMaxSample(min_value, max_value, scale); + if (min_value < max_value) + { + // Loop over all blocks + for (uint y = 0; y < mSampleCount; y += mBlockSize) + for (uint x = 0; x < mSampleCount; x += mBlockSize) + { + // Determine min and max block value + take 1 sample border just like we do while building the hierarchical grids + float block_min_value = FLT_MAX, block_max_value = -FLT_MAX; + for (uint bx = x; bx < min(x + mBlockSize + 1, mSampleCount); ++bx) + for (uint by = y; by < min(y + mBlockSize + 1, mSampleCount); ++by) + { + float h = mHeightSamples[by * mSampleCount + bx]; + if (h != cNoCollisionValue) + { + block_min_value = min(block_min_value, h); + block_max_value = max(block_max_value, h); + } + } + + if (block_min_value < block_max_value) + { + // Quantize then dequantize block min/max value + block_min_value = min_value + floor((block_min_value - min_value) * scale) / scale; + block_max_value = min_value + ceil((block_max_value - min_value) * scale) / scale; + float block_height = block_max_value - block_min_value; + + // Loop over the block again + for (uint bx = x; bx < x + mBlockSize; ++bx) + for (uint by = y; by < y + mBlockSize; ++by) + { + // Get the height + float height = mHeightSamples[by * mSampleCount + bx]; + if (height != cNoCollisionValue) + { + for (;;) + { + // Determine bitmask for sample + uint32 sample_mask = (1 << bits_per_sample) - 1; + + // Quantize + float quantized_height = floor((height - block_min_value) * float(sample_mask) / block_height); + quantized_height = Clamp(quantized_height, 0.0f, float(sample_mask - 1)); + + // Dequantize and check error + float dequantized_height = block_min_value + (quantized_height + 0.5f) * block_height / float(sample_mask); + if (abs(dequantized_height - height) <= inMaxError) + break; + + // Not accurate enough, increase bits per sample + bits_per_sample++; + + // Don't go above 8 bits per sample + if (bits_per_sample == 8) + return bits_per_sample; + } + } + } + } + } + + } + + return bits_per_sample; +} + +void HeightFieldShape::CalculateActiveEdges(uint inX, uint inY, uint inSizeX, uint inSizeY, const float *inHeights, uint inHeightsStartX, uint inHeightsStartY, uint inHeightsStride, float inHeightsScale, float inActiveEdgeCosThresholdAngle, TempAllocator &inAllocator) +{ + // Allocate temporary buffer for normals + uint normals_size = 2 * inSizeX * inSizeY * sizeof(Vec3); + Vec3 *normals = (Vec3 *)inAllocator.Allocate(normals_size); + + // Calculate triangle normals and make normals zero for triangles that are missing + Vec3 *out_normal = normals; + for (uint y = 0; y < inSizeY; ++y) + for (uint x = 0; x < inSizeX; ++x) + { + // Get height on diagonal + const float *height_samples = inHeights + (inY - inHeightsStartY + y) * inHeightsStride + (inX - inHeightsStartX + x); + float x1y1_h = height_samples[0]; + float x2y2_h = height_samples[inHeightsStride + 1]; + if (x1y1_h != cNoCollisionValue && x2y2_h != cNoCollisionValue) + { + // Calculate normal for lower left triangle (e.g. T1A) + float x1y2_h = height_samples[inHeightsStride]; + if (x1y2_h != cNoCollisionValue) + { + Vec3 x2y2_minus_x1y2(mScale.GetX(), inHeightsScale * (x2y2_h - x1y2_h), 0); + Vec3 x1y1_minus_x1y2(0, inHeightsScale * (x1y1_h - x1y2_h), -mScale.GetZ()); + out_normal[0] = x2y2_minus_x1y2.Cross(x1y1_minus_x1y2).Normalized(); + } + else + out_normal[0] = Vec3::sZero(); + + // Calculate normal for upper right triangle (e.g. T1B) + float x2y1_h = height_samples[1]; + if (x2y1_h != cNoCollisionValue) + { + Vec3 x1y1_minus_x2y1(-mScale.GetX(), inHeightsScale * (x1y1_h - x2y1_h), 0); + Vec3 x2y2_minus_x2y1(0, inHeightsScale * (x2y2_h - x2y1_h), mScale.GetZ()); + out_normal[1] = x1y1_minus_x2y1.Cross(x2y2_minus_x2y1).Normalized(); + } + else + out_normal[1] = Vec3::sZero(); + } + else + { + out_normal[0] = Vec3::sZero(); + out_normal[1] = Vec3::sZero(); + } + + out_normal += 2; + } + + // Calculate active edges + const Vec3 *in_normal = normals; + uint global_bit_pos = 3 * (inY * (mSampleCount - 1) + inX); + for (uint y = 0; y < inSizeY; ++y) + { + for (uint x = 0; x < inSizeX; ++x) + { + // Get vertex heights + const float *height_samples = inHeights + (inY - inHeightsStartY + y) * inHeightsStride + (inX - inHeightsStartX + x); + float x1y1_h = height_samples[0]; + float x1y2_h = height_samples[inHeightsStride]; + float x2y2_h = height_samples[inHeightsStride + 1]; + bool x1y1_valid = x1y1_h != cNoCollisionValue; + bool x1y2_valid = x1y2_h != cNoCollisionValue; + bool x2y2_valid = x2y2_h != cNoCollisionValue; + + // Calculate the edge flags (3 bits) + // See diagram in the next function for the edge numbering + uint16 edge_mask = 0b111; + uint16 edge_flags = 0; + + // Edge 0 + if (x == 0) + edge_mask &= 0b110; // We need normal x - 1 which we didn't calculate, don't update this edge + else if (x1y1_valid && x1y2_valid) + { + Vec3 edge0_direction(0, inHeightsScale * (x1y2_h - x1y1_h), mScale.GetZ()); + if (ActiveEdges::IsEdgeActive(in_normal[0], in_normal[-1], edge0_direction, inActiveEdgeCosThresholdAngle)) + edge_flags |= 0b001; + } + + // Edge 1 + if (y == inSizeY - 1) + edge_mask &= 0b101; // We need normal y + 1 which we didn't calculate, don't update this edge + else if (x1y2_valid && x2y2_valid) + { + Vec3 edge1_direction(mScale.GetX(), inHeightsScale * (x2y2_h - x1y2_h), 0); + if (ActiveEdges::IsEdgeActive(in_normal[0], in_normal[2 * inSizeX + 1], edge1_direction, inActiveEdgeCosThresholdAngle)) + edge_flags |= 0b010; + } + + // Edge 2 + if (x1y1_valid && x2y2_valid) + { + Vec3 edge2_direction(-mScale.GetX(), inHeightsScale * (x1y1_h - x2y2_h), -mScale.GetZ()); + if (ActiveEdges::IsEdgeActive(in_normal[0], in_normal[1], edge2_direction, inActiveEdgeCosThresholdAngle)) + edge_flags |= 0b100; + } + + // Store the edge flags in the array + uint byte_pos = global_bit_pos >> 3; + uint bit_pos = global_bit_pos & 0b111; + uint8 *edge_flags_ptr = &mActiveEdges[byte_pos]; + uint16 combined_edge_flags = uint16(edge_flags_ptr[0]) | uint16(uint16(edge_flags_ptr[1]) << 8); + combined_edge_flags &= ~(edge_mask << bit_pos); + combined_edge_flags |= edge_flags << bit_pos; + edge_flags_ptr[0] = uint8(combined_edge_flags); + edge_flags_ptr[1] = uint8(combined_edge_flags >> 8); + + in_normal += 2; + global_bit_pos += 3; + } + + global_bit_pos += 3 * (mSampleCount - 1 - inSizeX); + } + + // Free temporary buffer for normals + inAllocator.Free(normals, normals_size); +} + +void HeightFieldShape::CalculateActiveEdges(const HeightFieldShapeSettings &inSettings) +{ + /* + Store active edges. The triangles are organized like this: + x ---> + + y + + + | \ T1B | \ T2B + | e0 e2 | \ + | | T1A \ | T2A \ + V +--e1---+-------+ + | \ T3B | \ T4B + | \ | \ + | T3A \ | T4A \ + +-------+-------+ + We store active edges e0 .. e2 as bits 0 .. 2. + We store triangles horizontally then vertically (order T1A, T2A, T3A and T4A). + The top edge and right edge of the heightfield are always active so we do not need to store them, + therefore we only need to store (mSampleCount - 1)^2 * 3-bit + The triangles T1B, T2B, T3B and T4B do not need to be stored, their active edges can be constructed from adjacent triangles. + Add 1 byte padding so we can always read 1 uint16 to get the bits that cross an 8 bit boundary + */ + mActiveEdges.resize((Square(mSampleCount - 1) * 3 + 7) / 8 + 1); + + // Make all edges active (if mSampleCount is bigger than inSettings.mSampleCount we need to fill up the padding, + // also edges at x = 0 and y = inSettings.mSampleCount - 1 are not updated) + memset(mActiveEdges.data(), 0xff, mActiveEdges.size()); + + // Now clear the edges that are not active + TempAllocatorMalloc allocator; + CalculateActiveEdges(0, 0, inSettings.mSampleCount - 1, inSettings.mSampleCount - 1, inSettings.mHeightSamples.data(), 0, 0, inSettings.mSampleCount, inSettings.mScale.GetY(), inSettings.mActiveEdgeCosThresholdAngle, allocator); +} + +void HeightFieldShape::StoreMaterialIndices(const HeightFieldShapeSettings &inSettings) +{ + // We need to account for any rounding of the sample count to the nearest block size + uint in_count_min_1 = inSettings.mSampleCount - 1; + uint out_count_min_1 = mSampleCount - 1; + + mNumBitsPerMaterialIndex = 32 - CountLeadingZeros((uint32)mMaterials.size() - 1); + mMaterialIndices.resize(((Square(out_count_min_1) * mNumBitsPerMaterialIndex + 7) >> 3) + 1); // Add 1 byte so we don't read out of bounds when reading an uint16 + + for (uint y = 0; y < out_count_min_1; ++y) + for (uint x = 0; x < out_count_min_1; ++x) + { + // Read material + uint16 material_index = x < in_count_min_1 && y < in_count_min_1? uint16(inSettings.mMaterialIndices[x + y * in_count_min_1]) : 0; + + // Calculate byte and bit position where the material index needs to go + uint sample_pos = x + y * out_count_min_1; + uint bit_pos = sample_pos * mNumBitsPerMaterialIndex; + uint byte_pos = bit_pos >> 3; + bit_pos &= 0b111; + + // Write the material index + material_index <<= bit_pos; + JPH_ASSERT(byte_pos + 1 < mMaterialIndices.size()); + mMaterialIndices[byte_pos] |= uint8(material_index); + mMaterialIndices[byte_pos + 1] |= uint8(material_index >> 8); + } +} + +void HeightFieldShape::CacheValues() +{ + mSampleMask = uint8((uint32(1) << mBitsPerSample) - 1); +} + +HeightFieldShape::HeightFieldShape(const HeightFieldShapeSettings &inSettings, ShapeResult &outResult) : + Shape(EShapeType::HeightField, EShapeSubType::HeightField, inSettings, outResult), + mOffset(inSettings.mOffset), + mScale(inSettings.mScale), + mSampleCount(((inSettings.mSampleCount + inSettings.mBlockSize - 1) / inSettings.mBlockSize) * inSettings.mBlockSize), // Round sample count to nearest block size + mBlockSize(inSettings.mBlockSize), + mBitsPerSample(uint8(inSettings.mBitsPerSample)), + mMaterials(inSettings.mMaterials) +{ + CacheValues(); + + // Check block size + if (mBlockSize < 2 || mBlockSize > 8) + { + outResult.SetError("HeightFieldShape: Block size must be in the range [2, 8]!"); + return; + } + + // Check bits per sample + if (inSettings.mBitsPerSample < 1 || inSettings.mBitsPerSample > 8) + { + outResult.SetError("HeightFieldShape: Bits per sample must be in the range [1, 8]!"); + return; + } + + // We stop at mBlockSize x mBlockSize height sample blocks + uint num_blocks = GetNumBlocks(); + + // We want at least 1 grid layer + if (num_blocks < 2) + { + outResult.SetError("HeightFieldShape: Sample count too low!"); + return; + } + + // Check that we don't overflow our 32 bit 'properties' + if (num_blocks > (1 << cNumBitsXY)) + { + outResult.SetError("HeightFieldShape: Sample count too high!"); + return; + } + + // Check if we're not exceeding the amount of sub shape id bits + if (GetSubShapeIDBitsRecursive() > SubShapeID::MaxBits) + { + outResult.SetError("HeightFieldShape: Size exceeds the amount of available sub shape ID bits!"); + return; + } + + if (!mMaterials.empty()) + { + // Validate materials + if (mMaterials.size() > 256) + { + outResult.SetError("Supporting max 256 materials per height field"); + return; + } + for (uint8 s : inSettings.mMaterialIndices) + if (s >= mMaterials.size()) + { + outResult.SetError(StringFormat("Material %u is beyond material list (size: %u)", s, (uint)mMaterials.size())); + return; + } + } + else + { + // No materials assigned, validate that no materials have been specified + if (!inSettings.mMaterialIndices.empty()) + { + outResult.SetError("No materials present, mMaterialIndices should be empty"); + return; + } + } + + // Determine range + float min_value, max_value, scale; + inSettings.DetermineMinAndMaxSample(min_value, max_value, scale); + if (min_value > max_value) + { + // If there is no collision with this heightmap, leave everything empty + mMaterials.clear(); + outResult.Set(this); + return; + } + + // Quantize to uint16 + Array quantized_samples; + quantized_samples.reserve(mSampleCount * mSampleCount); + for (uint y = 0; y < inSettings.mSampleCount; ++y) + { + for (uint x = 0; x < inSettings.mSampleCount; ++x) + { + float h = inSettings.mHeightSamples[x + y * inSettings.mSampleCount]; + if (h == cNoCollisionValue) + { + quantized_samples.push_back(cNoCollisionValue16); + } + else + { + // Floor the quantized height to get a lower bound for the quantized value + int quantized_height = (int)floor(scale * (h - min_value)); + + // Ensure that the height says below the max height value so we can safely add 1 to get the upper bound for the quantized value + quantized_height = Clamp(quantized_height, 0, int(cMaxHeightValue16 - 1)); + + quantized_samples.push_back(uint16(quantized_height)); + } + } + // Pad remaining columns with no collision + for (uint x = inSettings.mSampleCount; x < mSampleCount; ++x) + quantized_samples.push_back(cNoCollisionValue16); + } + // Pad remaining rows with no collision + for (uint y = inSettings.mSampleCount; y < mSampleCount; ++y) + for (uint x = 0; x < mSampleCount; ++x) + quantized_samples.push_back(cNoCollisionValue16); + + // Update offset and scale to account for the compression to uint16 + if (min_value <= max_value) // Only when there was collision + { + // In GetPosition we always add 0.5 to the quantized sample in order to reduce the average error. + // We want to be able to exactly quantize min_value (this is important in case the heightfield is entirely flat) so we subtract that value from min_value. + min_value -= 0.5f / (scale * mSampleMask); + + mOffset.SetY(mOffset.GetY() + mScale.GetY() * min_value); + } + mScale.SetY(mScale.GetY() / scale); + + // Calculate amount of grids + uint max_level = sGetMaxLevel(num_blocks); + + // Temporary data structure used during creating of a hierarchy of grids + struct Range + { + uint16 mMin; + uint16 mMax; + }; + + // Reserve size for temporary range data + reserve 1 extra for a 1x1 grid that we won't store but use for calculating the bounding box + Array> ranges; + ranges.resize(max_level + 1); + + // Calculate highest detail grid by combining mBlockSize x mBlockSize height samples + Array *cur_range_vector = &ranges.back(); + uint num_blocks_pow2 = GetNextPowerOf2(num_blocks); // We calculate the range blocks as if the heightfield was a power of 2, when we save the range blocks we'll ignore the extra samples (this makes downsampling easier) + cur_range_vector->resize(num_blocks_pow2 * num_blocks_pow2); + Range *range_dst = &cur_range_vector->front(); + for (uint y = 0; y < num_blocks_pow2; ++y) + for (uint x = 0; x < num_blocks_pow2; ++x) + { + range_dst->mMin = 0xffff; + range_dst->mMax = 0; + uint max_bx = x == num_blocks_pow2 - 1? mBlockSize : mBlockSize + 1; // for interior blocks take 1 more because the triangles connect to the next block so we must include their height too + uint max_by = y == num_blocks_pow2 - 1? mBlockSize : mBlockSize + 1; + for (uint by = 0; by < max_by; ++by) + for (uint bx = 0; bx < max_bx; ++bx) + { + uint sx = x * mBlockSize + bx; + uint sy = y * mBlockSize + by; + if (sx < mSampleCount && sy < mSampleCount) + { + uint16 h = quantized_samples[sy * mSampleCount + sx]; + if (h != cNoCollisionValue16) + { + range_dst->mMin = min(range_dst->mMin, h); + range_dst->mMax = max(range_dst->mMax, uint16(h + 1)); // Add 1 to the max so we know the real value is between mMin and mMax + } + } + } + ++range_dst; + } + + // Calculate remaining grids + for (uint n = num_blocks_pow2 >> 1; n >= 1; n >>= 1) + { + // Get source buffer + const Range *range_src = &cur_range_vector->front(); + + // Previous array element + --cur_range_vector; + + // Make space for this grid + cur_range_vector->resize(n * n); + + // Get target buffer + range_dst = &cur_range_vector->front(); + + // Combine the results of 2x2 ranges + for (uint y = 0; y < n; ++y) + for (uint x = 0; x < n; ++x) + { + range_dst->mMin = 0xffff; + range_dst->mMax = 0; + for (uint by = 0; by < 2; ++by) + for (uint bx = 0; bx < 2; ++bx) + { + const Range &r = range_src[(y * 2 + by) * n * 2 + x * 2 + bx]; + range_dst->mMin = min(range_dst->mMin, r.mMin); + range_dst->mMax = max(range_dst->mMax, r.mMax); + } + ++range_dst; + } + } + JPH_ASSERT(cur_range_vector == &ranges.front()); + + // Store global range for bounding box calculation + mMinSample = ranges[0][0].mMin; + mMaxSample = ranges[0][0].mMax; + +#ifdef JPH_ENABLE_ASSERTS + // Validate that we did not lose range along the way + uint16 minv = 0xffff, maxv = 0; + for (uint16 v : quantized_samples) + if (v != cNoCollisionValue16) + { + minv = min(minv, v); + maxv = max(maxv, uint16(v + 1)); + } + JPH_ASSERT(mMinSample == minv && mMaxSample == maxv); +#endif + + // Now erase the first element, we need a 2x2 grid to start with + ranges.erase(ranges.begin()); + + // Create blocks + uint max_stride = (num_blocks + 1) >> 1; + mRangeBlocks.reserve(sGridOffsets[ranges.size()]); + for (uint level = 0; level < ranges.size(); ++level) + { + JPH_ASSERT(mRangeBlocks.size() == sGridOffsets[level]); + + uint in_n = 1 << level; + uint out_n = min(in_n, max_stride); // At the most detailed level we store a non-power of 2 number of blocks + + for (uint y = 0; y < out_n; ++y) + for (uint x = 0; x < out_n; ++x) + { + // Convert from 2x2 Range structure to 1 RangeBlock structure + RangeBlock rb; + for (uint by = 0; by < 2; ++by) + for (uint bx = 0; bx < 2; ++bx) + { + uint src_pos = (y * 2 + by) * 2 * in_n + (x * 2 + bx); + uint dst_pos = by * 2 + bx; + rb.mMin[dst_pos] = ranges[level][src_pos].mMin; + rb.mMax[dst_pos] = ranges[level][src_pos].mMax; + } + + // Add this block + mRangeBlocks.push_back(rb); + } + } + JPH_ASSERT(mRangeBlocks.size() == sGridOffsets[ranges.size() - 1] + Square(max_stride)); + + // Quantize height samples + mHeightSamples.resize((mSampleCount * mSampleCount * inSettings.mBitsPerSample + 7) / 8 + 1); + int sample = 0; + for (uint y = 0; y < mSampleCount; ++y) + for (uint x = 0; x < mSampleCount; ++x) + { + uint32 output_value; + + float h = x < inSettings.mSampleCount && y < inSettings.mSampleCount? inSettings.mHeightSamples[x + y * inSettings.mSampleCount] : cNoCollisionValue; + if (h == cNoCollisionValue) + { + // No collision + output_value = mSampleMask; + } + else + { + // Get range of block so we know what range to compress to + uint bx = x / mBlockSize; + uint by = y / mBlockSize; + const Range &range = ranges.back()[by * num_blocks_pow2 + bx]; + JPH_ASSERT(range.mMin < range.mMax); + + // Quantize to mBitsPerSample bits, note that mSampleMask is reserved for indicating that there's no collision. + // We divide the range into mSampleMask segments and use the mid points of these segments as the quantized values. + // This results in a lower error than if we had quantized our data using the lowest point of all these segments. + float h_min = min_value + range.mMin / scale; + float h_delta = float(range.mMax - range.mMin) / scale; + float quantized_height = floor((h - h_min) * float(mSampleMask) / h_delta); + output_value = uint32(Clamp((int)quantized_height, 0, int(mSampleMask) - 1)); // mSampleMask is reserved as 'no collision value' + } + + // Store the sample + uint byte_pos = sample >> 3; + uint bit_pos = sample & 0b111; + output_value <<= bit_pos; + mHeightSamples[byte_pos] |= uint8(output_value); + mHeightSamples[byte_pos + 1] |= uint8(output_value >> 8); + sample += inSettings.mBitsPerSample; + } + + // Calculate the active edges + CalculateActiveEdges(inSettings); + + // Compress material indices + if (mMaterials.size() > 1) + StoreMaterialIndices(inSettings); + + outResult.Set(this); +} + +inline void HeightFieldShape::sGetRangeBlockOffsetAndStride(uint inNumBlocks, uint inMaxLevel, uint &outRangeBlockOffset, uint &outRangeBlockStride) +{ + outRangeBlockOffset = sGridOffsets[inMaxLevel - 1]; + outRangeBlockStride = (inNumBlocks + 1) >> 1; +} + +inline void HeightFieldShape::GetRangeBlock(uint inBlockX, uint inBlockY, uint inRangeBlockOffset, uint inRangeBlockStride, RangeBlock *&outBlock, uint &outIndexInBlock) +{ + JPH_ASSERT(inBlockX < GetNumBlocks() && inBlockY < GetNumBlocks()); + + // Convert to location of range block + uint rbx = inBlockX >> 1; + uint rby = inBlockY >> 1; + outIndexInBlock = ((inBlockY & 1) << 1) + (inBlockX & 1); + + outBlock = &mRangeBlocks[inRangeBlockOffset + rby * inRangeBlockStride + rbx]; +} + +inline void HeightFieldShape::GetBlockOffsetAndScale(uint inBlockX, uint inBlockY, uint inRangeBlockOffset, uint inRangeBlockStride, float &outBlockOffset, float &outBlockScale) const +{ + JPH_ASSERT(inBlockX < GetNumBlocks() && inBlockY < GetNumBlocks()); + + // Convert to location of range block + uint rbx = inBlockX >> 1; + uint rby = inBlockY >> 1; + uint n = ((inBlockY & 1) << 1) + (inBlockX & 1); + + // Calculate offset and scale + const RangeBlock &block = mRangeBlocks[inRangeBlockOffset + rby * inRangeBlockStride + rbx]; + outBlockOffset = float(block.mMin[n]); + outBlockScale = float(block.mMax[n] - block.mMin[n]) / float(mSampleMask); +} + +inline uint8 HeightFieldShape::GetHeightSample(uint inX, uint inY) const +{ + JPH_ASSERT(inX < mSampleCount); + JPH_ASSERT(inY < mSampleCount); + + // Determine bit position of sample + uint sample = (inY * mSampleCount + inX) * uint(mBitsPerSample); + uint byte_pos = sample >> 3; + uint bit_pos = sample & 0b111; + + // Fetch the height sample value + JPH_ASSERT(byte_pos + 1 < mHeightSamples.size()); + const uint8 *height_samples = mHeightSamples.data() + byte_pos; + uint16 height_sample = uint16(height_samples[0]) | uint16(uint16(height_samples[1]) << 8); + return uint8(height_sample >> bit_pos) & mSampleMask; +} + +inline Vec3 HeightFieldShape::GetPosition(uint inX, uint inY, float inBlockOffset, float inBlockScale, bool &outNoCollision) const +{ + // Get quantized value + uint8 height_sample = GetHeightSample(inX, inY); + outNoCollision = height_sample == mSampleMask; + + // Add 0.5 to the quantized value to minimize the error (see constructor) + return mOffset + mScale * Vec3(float(inX), inBlockOffset + (0.5f + height_sample) * inBlockScale, float(inY)); +} + +Vec3 HeightFieldShape::GetPosition(uint inX, uint inY) const +{ + // Test if there are any samples + if (mHeightSamples.empty()) + return mOffset + mScale * Vec3(float(inX), 0.0f, float(inY)); + + // Get block location + uint bx = inX / mBlockSize; + uint by = inY / mBlockSize; + + // Calculate offset and stride + uint num_blocks = GetNumBlocks(); + uint range_block_offset, range_block_stride; + sGetRangeBlockOffsetAndStride(num_blocks, sGetMaxLevel(num_blocks), range_block_offset, range_block_stride); + + float offset, scale; + GetBlockOffsetAndScale(bx, by, range_block_offset, range_block_stride, offset, scale); + + bool no_collision; + return GetPosition(inX, inY, offset, scale, no_collision); +} + +bool HeightFieldShape::IsNoCollision(uint inX, uint inY) const +{ + return mHeightSamples.empty() || GetHeightSample(inX, inY) == mSampleMask; +} + +bool HeightFieldShape::ProjectOntoSurface(Vec3Arg inLocalPosition, Vec3 &outSurfacePosition, SubShapeID &outSubShapeID) const +{ + // Check if we have collision + if (mHeightSamples.empty()) + return false; + + // Convert coordinate to integer space + Vec3 integer_space = (inLocalPosition - mOffset) / mScale; + + // Get x coordinate and fraction + float x_frac = integer_space.GetX(); + if (x_frac < 0.0f || x_frac >= mSampleCount - 1) + return false; + uint x = (uint)floor(x_frac); + x_frac -= x; + + // Get y coordinate and fraction + float y_frac = integer_space.GetZ(); + if (y_frac < 0.0f || y_frac >= mSampleCount - 1) + return false; + uint y = (uint)floor(y_frac); + y_frac -= y; + + // If one of the diagonal points doesn't have collision, we don't have a height at this location + if (IsNoCollision(x, y) || IsNoCollision(x + 1, y + 1)) + return false; + + if (y_frac >= x_frac) + { + // Left bottom triangle, test the 3rd point + if (IsNoCollision(x, y + 1)) + return false; + + // Interpolate height value + Vec3 v1 = GetPosition(x, y); + Vec3 v2 = GetPosition(x, y + 1); + Vec3 v3 = GetPosition(x + 1, y + 1); + outSurfacePosition = v1 + y_frac * (v2 - v1) + x_frac * (v3 - v2); + SubShapeIDCreator creator; + outSubShapeID = EncodeSubShapeID(creator, x, y, 0); + return true; + } + else + { + // Right top triangle, test the third point + if (IsNoCollision(x + 1, y)) + return false; + + // Interpolate height value + Vec3 v1 = GetPosition(x, y); + Vec3 v2 = GetPosition(x + 1, y + 1); + Vec3 v3 = GetPosition(x + 1, y); + outSurfacePosition = v1 + y_frac * (v2 - v3) + x_frac * (v3 - v1); + SubShapeIDCreator creator; + outSubShapeID = EncodeSubShapeID(creator, x, y, 1); + return true; + } +} + +void HeightFieldShape::GetHeights(uint inX, uint inY, uint inSizeX, uint inSizeY, float *outHeights, uint inHeightsStride) const +{ + if (inSizeX == 0 || inSizeY == 0) + return; + + JPH_ASSERT(inX % mBlockSize == 0 && inY % mBlockSize == 0); + JPH_ASSERT(inX < mSampleCount && inY < mSampleCount); + JPH_ASSERT(inX + inSizeX <= mSampleCount && inY + inSizeY <= mSampleCount); + + // Test if there are any samples + if (mHeightSamples.empty()) + { + // No samples, return the offset + float offset = mOffset.GetY(); + for (uint y = 0; y < inSizeY; ++y, outHeights += inHeightsStride) + for (uint x = 0; x < inSizeX; ++x) + outHeights[x] = offset; + } + else + { + // Calculate offset and stride + uint num_blocks = GetNumBlocks(); + uint range_block_offset, range_block_stride; + sGetRangeBlockOffsetAndStride(num_blocks, sGetMaxLevel(num_blocks), range_block_offset, range_block_stride); + + // Loop over blocks + uint block_start_x = inX / mBlockSize; + uint block_start_y = inY / mBlockSize; + uint num_blocks_x = inSizeX / mBlockSize; + uint num_blocks_y = inSizeY / mBlockSize; + for (uint block_y = 0; block_y < num_blocks_y; ++block_y) + for (uint block_x = 0; block_x < num_blocks_x; ++block_x) + { + // Get offset and scale for block + float offset, scale; + GetBlockOffsetAndScale(block_start_x + block_x, block_start_y + block_y, range_block_offset, range_block_stride, offset, scale); + + // Adjust by global offset and scale + // Note: This is the math applied in GetPosition() written out to reduce calculations in the inner loop + scale *= mScale.GetY(); + offset = mOffset.GetY() + mScale.GetY() * offset + 0.5f * scale; + + // Loop over samples in block + for (uint sample_y = 0; sample_y < mBlockSize; ++sample_y) + for (uint sample_x = 0; sample_x < mBlockSize; ++sample_x) + { + // Calculate output coordinate + uint output_x = block_x * mBlockSize + sample_x; + uint output_y = block_y * mBlockSize + sample_y; + + // Get quantized value + uint8 height_sample = GetHeightSample(inX + output_x, inY + output_y); + + // Dequantize + float h = height_sample != mSampleMask? offset + height_sample * scale : cNoCollisionValue; + outHeights[output_y * inHeightsStride + output_x] = h; + } + } + } +} + +void HeightFieldShape::SetHeights(uint inX, uint inY, uint inSizeX, uint inSizeY, const float *inHeights, uint inHeightsStride, TempAllocator &inAllocator, float inActiveEdgeCosThresholdAngle) +{ + if (inSizeX == 0 || inSizeY == 0) + return; + + JPH_ASSERT(!mHeightSamples.empty()); + JPH_ASSERT(inX % mBlockSize == 0 && inY % mBlockSize == 0); + JPH_ASSERT(inX < mSampleCount && inY < mSampleCount); + JPH_ASSERT(inX + inSizeX <= mSampleCount && inY + inSizeY <= mSampleCount); + + // If we have a block in negative x/y direction, we will affect its range so we need to take it into account + bool need_temp_heights = false; + uint affected_x = inX; + uint affected_y = inY; + uint affected_size_x = inSizeX; + uint affected_size_y = inSizeY; + if (inX > 0) { affected_x -= mBlockSize; affected_size_x += mBlockSize; need_temp_heights = true; } + if (inY > 0) { affected_y -= mBlockSize; affected_size_y += mBlockSize; need_temp_heights = true; } + + // If we have a block in positive x/y direction, our ranges are affected by it so we need to take it into account + uint heights_size_x = affected_size_x; + uint heights_size_y = affected_size_y; + if (inX + inSizeX < mSampleCount) { heights_size_x += mBlockSize; need_temp_heights = true; } + if (inY + inSizeY < mSampleCount) { heights_size_y += mBlockSize; need_temp_heights = true; } + + // Get heights for affected area + const float *heights; + float *temp_heights; + if (need_temp_heights) + { + // Fetch the surrounding height data (note we're forced to recompress this data with a potentially different range so there will be some precision loss here) + temp_heights = (float *)inAllocator.Allocate(heights_size_x * heights_size_y * sizeof(float)); + heights = temp_heights; + + // We need to fill in the following areas: + // + // +-----------------+ + // | 2 | + // |---+---------+---| + // | | | | + // | 3 | 1 | 4 | + // | | | | + // |---+---------+---| + // | 5 | + // +-----------------+ + // + // 1. The area that is affected by the new heights (we just copy these) + // 2-5. These areas are either needed to calculate the range of the affected blocks or they need to be recompressed with a different range + uint offset_x = inX - affected_x; + uint offset_y = inY - affected_y; + + // Area 2 + GetHeights(affected_x, affected_y, heights_size_x, offset_y, temp_heights, heights_size_x); + float *area3_start = temp_heights + offset_y * heights_size_x; + + // Area 3 + GetHeights(affected_x, inY, offset_x, inSizeY, area3_start, heights_size_x); + + // Area 1 + float *area1_start = area3_start + offset_x; + for (uint y = 0; y < inSizeY; ++y, area1_start += heights_size_x, inHeights += inHeightsStride) + memcpy(area1_start, inHeights, inSizeX * sizeof(float)); + + // Area 4 + uint area4_x = inX + inSizeX; + GetHeights(area4_x, inY, affected_x + heights_size_x - area4_x, inSizeY, area3_start + area4_x - affected_x, heights_size_x); + + // Area 5 + uint area5_y = inY + inSizeY; + float *area5_start = temp_heights + (area5_y - affected_y) * heights_size_x; + GetHeights(affected_x, area5_y, heights_size_x, affected_y + heights_size_y - area5_y, area5_start, heights_size_x); + } + else + { + // We can directly use the input buffer because there are no extra edges to take into account + heights = inHeights; + heights_size_x = inHeightsStride; + temp_heights = nullptr; + } + + // Calculate offset and stride + uint num_blocks = GetNumBlocks(); + uint range_block_offset, range_block_stride; + uint max_level = sGetMaxLevel(num_blocks); + sGetRangeBlockOffsetAndStride(num_blocks, max_level, range_block_offset, range_block_stride); + + // Loop over blocks + uint block_start_x = affected_x / mBlockSize; + uint block_start_y = affected_y / mBlockSize; + uint num_blocks_x = affected_size_x / mBlockSize; + uint num_blocks_y = affected_size_y / mBlockSize; + for (uint block_y = 0, sample_start_y = 0; block_y < num_blocks_y; ++block_y, sample_start_y += mBlockSize) + for (uint block_x = 0, sample_start_x = 0; block_x < num_blocks_x; ++block_x, sample_start_x += mBlockSize) + { + // Determine quantized min and max value for block + // Note that we need to include 1 extra row in the positive x/y direction to account for connecting triangles + int min_value = 0xffff; + int max_value = 0; + uint sample_x_end = min(sample_start_x + mBlockSize + 1, mSampleCount - affected_x); + uint sample_y_end = min(sample_start_y + mBlockSize + 1, mSampleCount - affected_y); + for (uint sample_y = sample_start_y; sample_y < sample_y_end; ++sample_y) + for (uint sample_x = sample_start_x; sample_x < sample_x_end; ++sample_x) + { + float h = heights[sample_y * heights_size_x + sample_x]; + if (h != cNoCollisionValue) + { + int quantized_height = Clamp((int)floor((h - mOffset.GetY()) / mScale.GetY()), 0, int(cMaxHeightValue16 - 1)); + min_value = min(min_value, quantized_height); + max_value = max(max_value, quantized_height + 1); + } + } + if (min_value > max_value) + min_value = max_value = cNoCollisionValue16; + + // Update range for block + RangeBlock *range_block; + uint index_in_block; + GetRangeBlock(block_start_x + block_x, block_start_y + block_y, range_block_offset, range_block_stride, range_block, index_in_block); + range_block->mMin[index_in_block] = uint16(min_value); + range_block->mMax[index_in_block] = uint16(max_value); + + // Get offset and scale for block + float offset_block = float(min_value); + float scale_block = float(max_value - min_value) / float(mSampleMask); + + // Calculate scale and offset using the formula used in GetPosition() solved for the quantized height (excluding 0.5 because we round down while quantizing) + float scale = scale_block * mScale.GetY(); + float offset = mOffset.GetY() + offset_block * mScale.GetY(); + + // Loop over samples in block + sample_x_end = sample_start_x + mBlockSize; + sample_y_end = sample_start_y + mBlockSize; + for (uint sample_y = sample_start_y; sample_y < sample_y_end; ++sample_y) + for (uint sample_x = sample_start_x; sample_x < sample_x_end; ++sample_x) + { + // Quantize height + float h = heights[sample_y * heights_size_x + sample_x]; + uint8 quantized_height = h != cNoCollisionValue? uint8(Clamp((int)floor((h - offset) / scale), 0, int(mSampleMask) - 1)) : mSampleMask; + + // Determine bit position of sample + uint sample = ((affected_y + sample_y) * mSampleCount + affected_x + sample_x) * uint(mBitsPerSample); + uint byte_pos = sample >> 3; + uint bit_pos = sample & 0b111; + + // Update the height value sample + JPH_ASSERT(byte_pos + 1 < mHeightSamples.size()); + uint8 *height_samples = mHeightSamples.data() + byte_pos; + uint16 height_sample = uint16(height_samples[0]) | uint16(uint16(height_samples[1]) << 8); + height_sample &= ~(uint16(mSampleMask) << bit_pos); + height_sample |= uint16(quantized_height) << bit_pos; + height_samples[0] = uint8(height_sample); + height_samples[1] = uint8(height_sample >> 8); + } + } + + // Update active edges + // Note that we must take an extra row on all sides to account for connecting triangles + uint ae_x = inX > 1? inX - 2 : 0; + uint ae_y = inY > 1? inY - 2 : 0; + uint ae_sx = min(inX + inSizeX + 1, mSampleCount - 1) - ae_x; + uint ae_sy = min(inY + inSizeY + 1, mSampleCount - 1) - ae_y; + CalculateActiveEdges(ae_x, ae_y, ae_sx, ae_sy, heights, affected_x, affected_y, heights_size_x, 1.0f, inActiveEdgeCosThresholdAngle, inAllocator); + + // Free temporary buffer + if (temp_heights != nullptr) + inAllocator.Free(temp_heights, heights_size_x * heights_size_y * sizeof(float)); + + // Update hierarchy of range blocks + while (max_level > 1) + { + // Get offset and stride for destination blocks + uint dst_range_block_offset, dst_range_block_stride; + sGetRangeBlockOffsetAndStride(num_blocks >> 1, max_level - 1, dst_range_block_offset, dst_range_block_stride); + + // If we're starting halfway through a 2x2 block, we need to process one extra block since we take steps of 2 blocks below + uint block_x_end = (block_start_x & 1) && block_start_x + num_blocks_x < num_blocks? num_blocks_x + 1 : num_blocks_x; + uint block_y_end = (block_start_y & 1) && block_start_y + num_blocks_y < num_blocks? num_blocks_y + 1 : num_blocks_y; + + // Loop over all affected blocks + for (uint block_y = 0; block_y < block_y_end; block_y += 2) + for (uint block_x = 0; block_x < block_x_end; block_x += 2) + { + // Get source range block + RangeBlock *src_range_block; + uint index_in_src_block; + GetRangeBlock(block_start_x + block_x, block_start_y + block_y, range_block_offset, range_block_stride, src_range_block, index_in_src_block); + + // Determine quantized min and max value for the entire 2x2 block + uint16 min_value = 0xffff; + uint16 max_value = 0; + for (uint i = 0; i < 4; ++i) + if (src_range_block->mMin[i] != cNoCollisionValue16) + { + min_value = min(min_value, src_range_block->mMin[i]); + max_value = max(max_value, src_range_block->mMax[i]); + } + + // Write to destination block + RangeBlock *dst_range_block; + uint index_in_dst_block; + GetRangeBlock((block_start_x + block_x) >> 1, (block_start_y + block_y) >> 1, dst_range_block_offset, dst_range_block_stride, dst_range_block, index_in_dst_block); + dst_range_block->mMin[index_in_dst_block] = uint16(min_value); + dst_range_block->mMax[index_in_dst_block] = uint16(max_value); + } + + // Go up one level + --max_level; + num_blocks >>= 1; + block_start_x >>= 1; + block_start_y >>= 1; + num_blocks_x = min((num_blocks_x + 1) >> 1, num_blocks); + num_blocks_y = min((num_blocks_y + 1) >> 1, num_blocks); + + // Update stride and offset for source to old destination + range_block_offset = dst_range_block_offset; + range_block_stride = dst_range_block_stride; + } + + // Calculate new min and max sample for the entire height field + mMinSample = 0xffff; + mMaxSample = 0; + for (uint i = 0; i < 4; ++i) + if (mRangeBlocks[0].mMin[i] != cNoCollisionValue16) + { + mMinSample = min(mMinSample, mRangeBlocks[0].mMin[i]); + mMaxSample = max(mMaxSample, mRangeBlocks[0].mMax[i]); + } + +#ifdef JPH_DEBUG_RENDERER + // Invalidate temporary rendering data + mGeometry.clear(); +#endif +} + +void HeightFieldShape::GetMaterials(uint inX, uint inY, uint inSizeX, uint inSizeY, uint8 *outMaterials, uint inMaterialsStride) const +{ + if (inSizeX == 0 || inSizeY == 0) + return; + + if (mMaterialIndices.empty()) + { + // Return all 0's + for (uint y = 0; y < inSizeY; ++y) + { + uint8 *out_indices = outMaterials + y * inMaterialsStride; + for (uint x = 0; x < inSizeX; ++x) + *out_indices++ = 0; + } + return; + } + + JPH_ASSERT(inX < mSampleCount && inY < mSampleCount); + JPH_ASSERT(inX + inSizeX < mSampleCount && inY + inSizeY < mSampleCount); + + uint count_min_1 = mSampleCount - 1; + uint16 material_index_mask = uint16((1 << mNumBitsPerMaterialIndex) - 1); + + for (uint y = 0; y < inSizeY; ++y) + { + // Calculate input position + uint bit_pos = (inX + (inY + y) * count_min_1) * mNumBitsPerMaterialIndex; + const uint8 *in_indices = mMaterialIndices.data() + (bit_pos >> 3); + bit_pos &= 0b111; + + // Calculate output position + uint8 *out_indices = outMaterials + y * inMaterialsStride; + + for (uint x = 0; x < inSizeX; ++x) + { + // Get material index + uint16 material_index = uint16(in_indices[0]) + uint16(uint16(in_indices[1]) << 8); + material_index >>= bit_pos; + material_index &= material_index_mask; + *out_indices = uint8(material_index); + + // Go to the next index + bit_pos += mNumBitsPerMaterialIndex; + in_indices += bit_pos >> 3; + bit_pos &= 0b111; + ++out_indices; + } + } +} + +bool HeightFieldShape::SetMaterials(uint inX, uint inY, uint inSizeX, uint inSizeY, const uint8 *inMaterials, uint inMaterialsStride, const PhysicsMaterialList *inMaterialList, TempAllocator &inAllocator) +{ + if (inSizeX == 0 || inSizeY == 0) + return true; + + JPH_ASSERT(inX < mSampleCount && inY < mSampleCount); + JPH_ASSERT(inX + inSizeX < mSampleCount && inY + inSizeY < mSampleCount); + + // Remap materials + uint material_remap_table_size = uint(inMaterialList != nullptr? inMaterialList->size() : mMaterials.size()); + uint8 *material_remap_table = (uint8 *)inAllocator.Allocate(material_remap_table_size); + if (inMaterialList != nullptr) + { + // Conservatively reserve more space if the incoming material list is bigger + if (inMaterialList->size() > mMaterials.size()) + mMaterials.reserve(inMaterialList->size()); + + // Create a remap table + uint8 *remap_entry = material_remap_table; + for (const PhysicsMaterial *material : *inMaterialList) + { + // Try to find it in the existing list + PhysicsMaterialList::const_iterator it = std::find(mMaterials.begin(), mMaterials.end(), material); + if (it != mMaterials.end()) + { + // Found it, calculate index + *remap_entry = uint8(it - mMaterials.begin()); + } + else + { + // Not found, add it + if (mMaterials.size() >= 256) + { + // We can't have more than 256 materials since we use uint8 as indices + inAllocator.Free(material_remap_table, material_remap_table_size); + return false; + } + *remap_entry = uint8(mMaterials.size()); + mMaterials.push_back(material); + } + ++remap_entry; + } + } + else + { + // No remapping + for (uint i = 0; i < material_remap_table_size; ++i) + material_remap_table[i] = uint8(i); + } + + if (mMaterials.size() == 1) + { + // Only 1 material, we don't need to store the material indices + return true; + } + + // Check if we need to resize the material indices array + uint count_min_1 = mSampleCount - 1; + uint32 new_bits_per_material_index = 32 - CountLeadingZeros((uint32)mMaterials.size() - 1); + JPH_ASSERT(mNumBitsPerMaterialIndex <= 8 && new_bits_per_material_index <= 8); + if (new_bits_per_material_index != mNumBitsPerMaterialIndex) + { + // Resize the material indices array + mMaterialIndices.resize(((Square(count_min_1) * new_bits_per_material_index + 7) >> 3) + 1); // Add 1 byte so we don't read out of bounds when reading an uint16 + + // Calculate old and new mask + uint16 old_material_index_mask = uint16((1 << mNumBitsPerMaterialIndex) - 1); + uint16 new_material_index_mask = uint16((1 << new_bits_per_material_index) - 1); + + // Loop through the array backwards to avoid overwriting data + int in_bit_pos = (count_min_1 * count_min_1 - 1) * mNumBitsPerMaterialIndex; + const uint8 *in_indices = mMaterialIndices.data() + (in_bit_pos >> 3); + in_bit_pos &= 0b111; + int out_bit_pos = (count_min_1 * count_min_1 - 1) * new_bits_per_material_index; + uint8 *out_indices = mMaterialIndices.data() + (out_bit_pos >> 3); + out_bit_pos &= 0b111; + + while (out_indices >= mMaterialIndices.data()) + { + // Read the material index + uint16 material_index = uint16(in_indices[0]) + uint16(uint16(in_indices[1]) << 8); + material_index >>= in_bit_pos; + material_index &= old_material_index_mask; + + // Write the material index + uint16 output_data = uint16(out_indices[0]) + uint16(uint16(out_indices[1]) << 8); + output_data &= ~(new_material_index_mask << out_bit_pos); + output_data |= material_index << out_bit_pos; + out_indices[0] = uint8(output_data); + out_indices[1] = uint8(output_data >> 8); + + // Go to the previous index + in_bit_pos -= int(mNumBitsPerMaterialIndex); + in_indices += in_bit_pos >> 3; + in_bit_pos &= 0b111; + out_bit_pos -= int(new_bits_per_material_index); + out_indices += out_bit_pos >> 3; + out_bit_pos &= 0b111; + } + + // Accept the new bits per material index + mNumBitsPerMaterialIndex = new_bits_per_material_index; + } + + uint16 material_index_mask = uint16((1 << mNumBitsPerMaterialIndex) - 1); + for (uint y = 0; y < inSizeY; ++y) + { + // Calculate input position + const uint8 *in_indices = inMaterials + y * inMaterialsStride; + + // Calculate output position + uint bit_pos = (inX + (inY + y) * count_min_1) * mNumBitsPerMaterialIndex; + uint8 *out_indices = mMaterialIndices.data() + (bit_pos >> 3); + bit_pos &= 0b111; + + for (uint x = 0; x < inSizeX; ++x) + { + // Update material + uint16 output_data = uint16(out_indices[0]) + uint16(uint16(out_indices[1]) << 8); + output_data &= ~(material_index_mask << bit_pos); + output_data |= material_remap_table[*in_indices] << bit_pos; + out_indices[0] = uint8(output_data); + out_indices[1] = uint8(output_data >> 8); + + // Go to the next index + in_indices++; + bit_pos += mNumBitsPerMaterialIndex; + out_indices += bit_pos >> 3; + bit_pos &= 0b111; + } + } + + // Free the remapping table + inAllocator.Free(material_remap_table, material_remap_table_size); + return true; +} + +MassProperties HeightFieldShape::GetMassProperties() const +{ + // Object should always be static, return default mass properties + return MassProperties(); +} + +const PhysicsMaterial *HeightFieldShape::GetMaterial(uint inX, uint inY) const +{ + if (mMaterials.empty()) + return PhysicsMaterial::sDefault; + if (mMaterials.size() == 1) + return mMaterials[0]; + + uint count_min_1 = mSampleCount - 1; + JPH_ASSERT(inX < count_min_1); + JPH_ASSERT(inY < count_min_1); + + // Calculate at which bit the material index starts + uint bit_pos = (inX + inY * count_min_1) * mNumBitsPerMaterialIndex; + uint byte_pos = bit_pos >> 3; + bit_pos &= 0b111; + + // Read the material index + JPH_ASSERT(byte_pos + 1 < mMaterialIndices.size()); + const uint8 *material_indices = mMaterialIndices.data() + byte_pos; + uint16 material_index = uint16(material_indices[0]) + uint16(uint16(material_indices[1]) << 8); + material_index >>= bit_pos; + material_index &= (1 << mNumBitsPerMaterialIndex) - 1; + + // Return the material + return mMaterials[material_index]; +} + +uint HeightFieldShape::GetSubShapeIDBits() const +{ + // Need to store X, Y and 1 extra bit to specify the triangle number in the quad + return 2 * (32 - CountLeadingZeros(mSampleCount - 1)) + 1; +} + +SubShapeID HeightFieldShape::EncodeSubShapeID(const SubShapeIDCreator &inCreator, uint inX, uint inY, uint inTriangle) const +{ + return inCreator.PushID((inX + inY * mSampleCount) * 2 + inTriangle, GetSubShapeIDBits()).GetID(); +} + +void HeightFieldShape::DecodeSubShapeID(const SubShapeID &inSubShapeID, uint &outX, uint &outY, uint &outTriangle) const +{ + // Decode sub shape id + SubShapeID remainder; + uint32 id = inSubShapeID.PopID(GetSubShapeIDBits(), remainder); + JPH_ASSERT(remainder.IsEmpty(), "Invalid subshape ID"); + + // Get triangle index + outTriangle = id & 1; + id >>= 1; + + // Fetch the x and y coordinate + outX = id % mSampleCount; + outY = id / mSampleCount; +} + +const PhysicsMaterial *HeightFieldShape::GetMaterial(const SubShapeID &inSubShapeID) const +{ + // Decode ID + uint x, y, triangle; + DecodeSubShapeID(inSubShapeID, x, y, triangle); + + // Fetch the material + return GetMaterial(x, y); +} + +Vec3 HeightFieldShape::GetSurfaceNormal(const SubShapeID &inSubShapeID, Vec3Arg inLocalSurfacePosition) const +{ + // Decode ID + uint x, y, triangle; + DecodeSubShapeID(inSubShapeID, x, y, triangle); + + // Fetch vertices that both triangles share + Vec3 x1y1 = GetPosition(x, y); + Vec3 x2y2 = GetPosition(x + 1, y + 1); + + // Get normal depending on which triangle was selected + Vec3 normal; + if (triangle == 0) + { + Vec3 x1y2 = GetPosition(x, y + 1); + normal = (x2y2 - x1y2).Cross(x1y1 - x1y2); + } + else + { + Vec3 x2y1 = GetPosition(x + 1, y); + normal = (x1y1 - x2y1).Cross(x2y2 - x2y1); + } + + return normal.Normalized(); +} + +void HeightFieldShape::GetSupportingFace(const SubShapeID &inSubShapeID, Vec3Arg inDirection, Vec3Arg inScale, Mat44Arg inCenterOfMassTransform, SupportingFace &outVertices) const +{ + // Decode ID + uint x, y, triangle; + DecodeSubShapeID(inSubShapeID, x, y, triangle); + + // Fetch the triangle + outVertices.resize(3); + outVertices[0] = GetPosition(x, y); + Vec3 v2 = GetPosition(x + 1, y + 1); + if (triangle == 0) + { + outVertices[1] = GetPosition(x, y + 1); + outVertices[2] = v2; + } + else + { + outVertices[1] = v2; + outVertices[2] = GetPosition(x + 1, y); + } + + // Flip triangle if scaled inside out + if (ScaleHelpers::IsInsideOut(inScale)) + swap(outVertices[1], outVertices[2]); + + // Transform to world space + Mat44 transform = inCenterOfMassTransform.PreScaled(inScale); + for (Vec3 &v : outVertices) + v = transform * v; +} + +inline uint8 HeightFieldShape::GetEdgeFlags(uint inX, uint inY, uint inTriangle) const +{ + JPH_ASSERT(inX < mSampleCount - 1 && inY < mSampleCount - 1); + + if (inTriangle == 0) + { + // The edge flags for this triangle are directly stored, find the right 3 bits + uint bit_pos = 3 * (inX + inY * (mSampleCount - 1)); + uint byte_pos = bit_pos >> 3; + bit_pos &= 0b111; + JPH_ASSERT(byte_pos + 1 < mActiveEdges.size()); + const uint8 *active_edges = mActiveEdges.data() + byte_pos; + uint16 edge_flags = uint16(active_edges[0]) + uint16(uint16(active_edges[1]) << 8); + return uint8(edge_flags >> bit_pos) & 0b111; + } + else + { + // We don't store this triangle directly, we need to look at our three neighbours to construct the edge flags + uint8 edge0 = (GetEdgeFlags(inX, inY, 0) & 0b100) != 0? 0b001 : 0; // Diagonal edge + uint8 edge1 = inX == mSampleCount - 2 || (GetEdgeFlags(inX + 1, inY, 0) & 0b001) != 0? 0b010 : 0; // Vertical edge + uint8 edge2 = inY == 0 || (GetEdgeFlags(inX, inY - 1, 0) & 0b010) != 0? 0b100 : 0; // Horizontal edge + return edge0 | edge1 | edge2; + } +} + +AABox HeightFieldShape::GetLocalBounds() const +{ + if (mMinSample == cNoCollisionValue16) + { + // This whole height field shape doesn't have any collision, return the center point + Vec3 center = mOffset + 0.5f * mScale * Vec3(float(mSampleCount - 1), 0.0f, float(mSampleCount - 1)); + return AABox(center, center); + } + else + { + // Bounding box based on min and max sample height + Vec3 bmin = mOffset + mScale * Vec3(0.0f, float(mMinSample), 0.0f); + Vec3 bmax = mOffset + mScale * Vec3(float(mSampleCount - 1), float(mMaxSample), float(mSampleCount - 1)); + return AABox(bmin, bmax); + } +} + +#ifdef JPH_DEBUG_RENDERER +void HeightFieldShape::Draw(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inUseMaterialColors, bool inDrawWireframe) const +{ + // Don't draw anything if we don't have any collision + if (mHeightSamples.empty()) + return; + + // Reset the batch if we switch coloring mode + if (mCachedUseMaterialColors != inUseMaterialColors) + { + mGeometry.clear(); + mCachedUseMaterialColors = inUseMaterialColors; + } + + if (mGeometry.empty()) + { + // Divide terrain in triangle batches of max 64x64x2 triangles to allow better culling of the terrain + uint32 block_size = min(mSampleCount, 64); + for (uint32 by = 0; by < mSampleCount; by += block_size) + for (uint32 bx = 0; bx < mSampleCount; bx += block_size) + { + // Create vertices for a block + Array triangles; + triangles.resize(block_size * block_size * 2); + DebugRenderer::Triangle *out_tri = &triangles[0]; + for (uint32 y = by, max_y = min(by + block_size, mSampleCount - 1); y < max_y; ++y) + for (uint32 x = bx, max_x = min(bx + block_size, mSampleCount - 1); x < max_x; ++x) + if (!IsNoCollision(x, y) && !IsNoCollision(x + 1, y + 1)) + { + Vec3 x1y1 = GetPosition(x, y); + Vec3 x2y2 = GetPosition(x + 1, y + 1); + Color color = inUseMaterialColors? GetMaterial(x, y)->GetDebugColor() : Color::sWhite; + + if (!IsNoCollision(x, y + 1)) + { + Vec3 x1y2 = GetPosition(x, y + 1); + + x1y1.StoreFloat3(&out_tri->mV[0].mPosition); + x1y2.StoreFloat3(&out_tri->mV[1].mPosition); + x2y2.StoreFloat3(&out_tri->mV[2].mPosition); + + Vec3 normal = (x2y2 - x1y2).Cross(x1y1 - x1y2).Normalized(); + for (DebugRenderer::Vertex &v : out_tri->mV) + { + v.mColor = color; + v.mUV = Float2(0, 0); + normal.StoreFloat3(&v.mNormal); + } + + ++out_tri; + } + + if (!IsNoCollision(x + 1, y)) + { + Vec3 x2y1 = GetPosition(x + 1, y); + + x1y1.StoreFloat3(&out_tri->mV[0].mPosition); + x2y2.StoreFloat3(&out_tri->mV[1].mPosition); + x2y1.StoreFloat3(&out_tri->mV[2].mPosition); + + Vec3 normal = (x1y1 - x2y1).Cross(x2y2 - x2y1).Normalized(); + for (DebugRenderer::Vertex &v : out_tri->mV) + { + v.mColor = color; + v.mUV = Float2(0, 0); + normal.StoreFloat3(&v.mNormal); + } + + ++out_tri; + } + } + + // Resize triangles array to actual amount of triangles written + size_t num_triangles = out_tri - &triangles[0]; + triangles.resize(num_triangles); + + // Create batch + if (num_triangles > 0) + mGeometry.push_back(new DebugRenderer::Geometry(inRenderer->CreateTriangleBatch(triangles), DebugRenderer::sCalculateBounds(&triangles[0].mV[0], int(3 * num_triangles)))); + } + } + + // Get transform including scale + RMat44 transform = inCenterOfMassTransform.PreScaled(inScale); + + // Test if the shape is scaled inside out + DebugRenderer::ECullMode cull_mode = ScaleHelpers::IsInsideOut(inScale)? DebugRenderer::ECullMode::CullFrontFace : DebugRenderer::ECullMode::CullBackFace; + + // Determine the draw mode + DebugRenderer::EDrawMode draw_mode = inDrawWireframe? DebugRenderer::EDrawMode::Wireframe : DebugRenderer::EDrawMode::Solid; + + // Draw the geometry + for (const DebugRenderer::GeometryRef &b : mGeometry) + inRenderer->DrawGeometry(transform, inColor, b, cull_mode, DebugRenderer::ECastShadow::On, draw_mode); + + if (sDrawTriangleOutlines) + { + struct Visitor + { + JPH_INLINE explicit Visitor(const HeightFieldShape *inShape, DebugRenderer *inRenderer, RMat44Arg inTransform) : + mShape(inShape), + mRenderer(inRenderer), + mTransform(inTransform) + { + } + + JPH_INLINE bool ShouldAbort() const + { + return false; + } + + JPH_INLINE bool ShouldVisitRangeBlock([[maybe_unused]] int inStackTop) const + { + return true; + } + + JPH_INLINE int VisitRangeBlock(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, [[maybe_unused]] int inStackTop) const + { + UVec4 valid = Vec4::sLessOrEqual(inBoundsMinY, inBoundsMaxY); + return CountAndSortTrues(valid, ioProperties); + } + + JPH_INLINE void VisitTriangle(uint inX, uint inY, uint inTriangle, Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2) const + { + // Determine active edges + uint8 active_edges = mShape->GetEdgeFlags(inX, inY, inTriangle); + + // Loop through edges + Vec3 v[] = { inV0, inV1, inV2 }; + for (uint edge_idx = 0; edge_idx < 3; ++edge_idx) + { + RVec3 v1 = mTransform * v[edge_idx]; + RVec3 v2 = mTransform * v[(edge_idx + 1) % 3]; + + // Draw active edge as a green arrow, other edges as grey + if (active_edges & (1 << edge_idx)) + mRenderer->DrawArrow(v1, v2, Color::sGreen, 0.01f); + else + mRenderer->DrawLine(v1, v2, Color::sGrey); + } + } + + const HeightFieldShape *mShape; + DebugRenderer * mRenderer; + RMat44 mTransform; + }; + + Visitor visitor(this, inRenderer, inCenterOfMassTransform.PreScaled(inScale)); + WalkHeightField(visitor); + } +} +#endif // JPH_DEBUG_RENDERER + +class HeightFieldShape::DecodingContext +{ +public: + JPH_INLINE explicit DecodingContext(const HeightFieldShape *inShape) : + mShape(inShape) + { + static_assert(sizeof(sGridOffsets) / sizeof(uint) == cNumBitsXY + 1, "Offsets array is not long enough"); + + // Construct root stack entry + mPropertiesStack[0] = 0; // level: 0, x: 0, y: 0 + } + + template + JPH_INLINE void WalkHeightField(Visitor &ioVisitor) + { + // Early out if there's no collision + if (mShape->mHeightSamples.empty()) + return; + + // Precalculate values relating to sample count + uint32 sample_count = mShape->mSampleCount; + UVec4 sample_count_min_1 = UVec4::sReplicate(sample_count - 1); + + // Precalculate values relating to block size + uint32 block_size = mShape->mBlockSize; + uint32 block_size_plus_1 = block_size + 1; + uint num_blocks = mShape->GetNumBlocks(); + uint num_blocks_min_1 = num_blocks - 1; + uint max_level = HeightFieldShape::sGetMaxLevel(num_blocks); + uint32 max_stride = (num_blocks + 1) >> 1; + + // Precalculate range block offset and stride for GetBlockOffsetAndScale + uint range_block_offset, range_block_stride; + sGetRangeBlockOffsetAndStride(num_blocks, max_level, range_block_offset, range_block_stride); + + // Allocate space for vertices and 'no collision' flags + int array_size = Square(block_size_plus_1); + Vec3 *vertices = reinterpret_cast(JPH_STACK_ALLOC(array_size * sizeof(Vec3))); + bool *no_collision = reinterpret_cast(JPH_STACK_ALLOC(array_size * sizeof(bool))); + + // Splat offsets + Vec4 ox = mShape->mOffset.SplatX(); + Vec4 oy = mShape->mOffset.SplatY(); + Vec4 oz = mShape->mOffset.SplatZ(); + + // Splat scales + Vec4 sx = mShape->mScale.SplatX(); + Vec4 sy = mShape->mScale.SplatY(); + Vec4 sz = mShape->mScale.SplatZ(); + + do + { + // Decode properties + uint32 properties_top = mPropertiesStack[mTop]; + uint32 x = properties_top & cMaskBitsXY; + uint32 y = (properties_top >> cNumBitsXY) & cMaskBitsXY; + uint32 level = properties_top >> cLevelShift; + + if (level >= max_level) + { + // Determine actual range of samples (minus one because we eventually want to iterate over the triangles, not the samples) + uint32 min_x = x * block_size; + uint32 max_x = min_x + block_size; + uint32 min_y = y * block_size; + uint32 max_y = min_y + block_size; + + // Decompress vertices of block at (x, y) + Vec3 *dst_vertex = vertices; + bool *dst_no_collision = no_collision; + float block_offset, block_scale; + mShape->GetBlockOffsetAndScale(x, y, range_block_offset, range_block_stride, block_offset, block_scale); + for (uint32 v_y = min_y; v_y < max_y; ++v_y) + { + for (uint32 v_x = min_x; v_x < max_x; ++v_x) + { + *dst_vertex = mShape->GetPosition(v_x, v_y, block_offset, block_scale, *dst_no_collision); + ++dst_vertex; + ++dst_no_collision; + } + + // Skip last column, these values come from a different block + ++dst_vertex; + ++dst_no_collision; + } + + // Decompress block (x + 1, y) + uint32 max_x_decrement = 0; + if (x < num_blocks_min_1) + { + dst_vertex = vertices + block_size; + dst_no_collision = no_collision + block_size; + mShape->GetBlockOffsetAndScale(x + 1, y, range_block_offset, range_block_stride, block_offset, block_scale); + for (uint32 v_y = min_y; v_y < max_y; ++v_y) + { + *dst_vertex = mShape->GetPosition(max_x, v_y, block_offset, block_scale, *dst_no_collision); + dst_vertex += block_size_plus_1; + dst_no_collision += block_size_plus_1; + } + } + else + max_x_decrement = 1; // We don't have a next block, one less triangle to test + + // Decompress block (x, y + 1) + if (y < num_blocks_min_1) + { + uint start = block_size * block_size_plus_1; + dst_vertex = vertices + start; + dst_no_collision = no_collision + start; + mShape->GetBlockOffsetAndScale(x, y + 1, range_block_offset, range_block_stride, block_offset, block_scale); + for (uint32 v_x = min_x; v_x < max_x; ++v_x) + { + *dst_vertex = mShape->GetPosition(v_x, max_y, block_offset, block_scale, *dst_no_collision); + ++dst_vertex; + ++dst_no_collision; + } + + // Decompress single sample of block at (x + 1, y + 1) + if (x < num_blocks_min_1) + { + mShape->GetBlockOffsetAndScale(x + 1, y + 1, range_block_offset, range_block_stride, block_offset, block_scale); + *dst_vertex = mShape->GetPosition(max_x, max_y, block_offset, block_scale, *dst_no_collision); + } + } + else + --max_y; // We don't have a next block, one less triangle to test + + // Update max_x (we've been using it so we couldn't update it earlier) + max_x -= max_x_decrement; + + // We're going to divide the vertices in 4 blocks to do one more runtime sub-division, calculate the ranges of those blocks + struct Range + { + uint32 mMinX, mMinY, mNumTrianglesX, mNumTrianglesY; + }; + uint32 half_block_size = block_size >> 1; + uint32 block_size_x = max_x - min_x - half_block_size; + uint32 block_size_y = max_y - min_y - half_block_size; + Range ranges[] = + { + { 0, 0, half_block_size, half_block_size }, + { half_block_size, 0, block_size_x, half_block_size }, + { 0, half_block_size, half_block_size, block_size_y }, + { half_block_size, half_block_size, block_size_x, block_size_y }, + }; + + // Calculate the min and max of each of the blocks + Mat44 block_min, block_max; + for (int block = 0; block < 4; ++block) + { + // Get the range for this block + const Range &range = ranges[block]; + uint32 start = range.mMinX + range.mMinY * block_size_plus_1; + uint32 size_x_plus_1 = range.mNumTrianglesX + 1; + uint32 size_y_plus_1 = range.mNumTrianglesY + 1; + + // Calculate where to start reading + const Vec3 *src_vertex = vertices + start; + const bool *src_no_collision = no_collision + start; + uint32 stride = block_size_plus_1 - size_x_plus_1; + + // Start range with a very large inside-out box + Vec3 value_min = Vec3::sReplicate(1.0e30f); + Vec3 value_max = Vec3::sReplicate(-1.0e30f); + + // Loop over the samples to determine the min and max of this block + for (uint32 block_y = 0; block_y < size_y_plus_1; ++block_y) + { + for (uint32 block_x = 0; block_x < size_x_plus_1; ++block_x) + { + if (!*src_no_collision) + { + value_min = Vec3::sMin(value_min, *src_vertex); + value_max = Vec3::sMax(value_max, *src_vertex); + } + ++src_vertex; + ++src_no_collision; + } + src_vertex += stride; + src_no_collision += stride; + } + block_min.SetColumn4(block, Vec4(value_min)); + block_max.SetColumn4(block, Vec4(value_max)); + } + + #ifdef JPH_DEBUG_HEIGHT_FIELD + // Draw the bounding boxes of the sub-nodes + for (int block = 0; block < 4; ++block) + { + AABox bounds(block_min.GetColumn3(block), block_max.GetColumn3(block)); + if (bounds.IsValid()) + DebugRenderer::sInstance->DrawWireBox(bounds, Color::sYellow); + } + #endif // JPH_DEBUG_HEIGHT_FIELD + + // Transpose so we have the mins and maxes of each of the blocks in rows instead of columns + Mat44 transposed_min = block_min.Transposed(); + Mat44 transposed_max = block_max.Transposed(); + + // Check which blocks collide + // Note: At this point we don't use our own stack but we do allow the visitor to use its own stack + // to store collision distances so that we can still early out when no closer hits have been found. + UVec4 colliding_blocks(0, 1, 2, 3); + int num_results = ioVisitor.VisitRangeBlock(transposed_min.GetColumn4(0), transposed_min.GetColumn4(1), transposed_min.GetColumn4(2), transposed_max.GetColumn4(0), transposed_max.GetColumn4(1), transposed_max.GetColumn4(2), colliding_blocks, mTop); + + // Loop through the results backwards (closest first) + int result = num_results - 1; + while (result >= 0) + { + // Calculate the min and max of this block + uint32 block = colliding_blocks[result]; + const Range &range = ranges[block]; + uint32 block_min_x = min_x + range.mMinX; + uint32 block_max_x = block_min_x + range.mNumTrianglesX; + uint32 block_min_y = min_y + range.mMinY; + uint32 block_max_y = block_min_y + range.mNumTrianglesY; + + // Loop triangles + for (uint32 v_y = block_min_y; v_y < block_max_y; ++v_y) + for (uint32 v_x = block_min_x; v_x < block_max_x; ++v_x) + { + // Get first vertex + const int offset = (v_y - min_y) * block_size_plus_1 + (v_x - min_x); + const Vec3 *start_vertex = vertices + offset; + const bool *start_no_collision = no_collision + offset; + + // Check if vertices shared by both triangles have collision + if (!start_no_collision[0] && !start_no_collision[block_size_plus_1 + 1]) + { + // Loop 2 triangles + for (uint t = 0; t < 2; ++t) + { + // Determine triangle vertices + Vec3 v0, v1, v2; + if (t == 0) + { + // Check third vertex + if (start_no_collision[block_size_plus_1]) + continue; + + // Get vertices for triangle + v0 = start_vertex[0]; + v1 = start_vertex[block_size_plus_1]; + v2 = start_vertex[block_size_plus_1 + 1]; + } + else + { + // Check third vertex + if (start_no_collision[1]) + continue; + + // Get vertices for triangle + v0 = start_vertex[0]; + v1 = start_vertex[block_size_plus_1 + 1]; + v2 = start_vertex[1]; + } + + #ifdef JPH_DEBUG_HEIGHT_FIELD + DebugRenderer::sInstance->DrawWireTriangle(RVec3(v0), RVec3(v1), RVec3(v2), Color::sWhite); + #endif + + // Call visitor + ioVisitor.VisitTriangle(v_x, v_y, t, v0, v1, v2); + + // Check if we're done + if (ioVisitor.ShouldAbort()) + return; + } + } + } + + // Fetch next block until we find one that the visitor wants to see + do + --result; + while (result >= 0 && !ioVisitor.ShouldVisitRangeBlock(mTop + result)); + } + } + else + { + // Visit child grid + uint32 stride = min(1U << level, max_stride); // At the most detailed level we store a non-power of 2 number of blocks + uint32 offset = sGridOffsets[level] + stride * y + x; + + // Decode min/max height + UVec4 block = UVec4::sLoadInt4Aligned(reinterpret_cast(&mShape->mRangeBlocks[offset])); + Vec4 bounds_miny = oy + sy * block.Expand4Uint16Lo().ToFloat(); + Vec4 bounds_maxy = oy + sy * block.Expand4Uint16Hi().ToFloat(); + + // Calculate size of one cell at this grid level + UVec4 internal_cell_size = UVec4::sReplicate(block_size << (max_level - level - 1)); // subtract 1 from level because we have an internal grid of 2x2 + + // Calculate min/max x and z + UVec4 two_x = UVec4::sReplicate(2 * x); // multiply by two because we have an internal grid of 2x2 + Vec4 bounds_minx = ox + sx * (internal_cell_size * (two_x + UVec4(0, 1, 0, 1))).ToFloat(); + Vec4 bounds_maxx = ox + sx * UVec4::sMin(internal_cell_size * (two_x + UVec4(1, 2, 1, 2)), sample_count_min_1).ToFloat(); + + UVec4 two_y = UVec4::sReplicate(2 * y); + Vec4 bounds_minz = oz + sz * (internal_cell_size * (two_y + UVec4(0, 0, 1, 1))).ToFloat(); + Vec4 bounds_maxz = oz + sz * UVec4::sMin(internal_cell_size * (two_y + UVec4(1, 1, 2, 2)), sample_count_min_1).ToFloat(); + + // Calculate properties of child blocks + UVec4 properties = UVec4::sReplicate(((level + 1) << cLevelShift) + (y << (cNumBitsXY + 1)) + (x << 1)) + UVec4(0, 1, 1 << cNumBitsXY, (1 << cNumBitsXY) + 1); + + #ifdef JPH_DEBUG_HEIGHT_FIELD + // Draw boxes + for (int i = 0; i < 4; ++i) + { + AABox b(Vec3(bounds_minx[i], bounds_miny[i], bounds_minz[i]), Vec3(bounds_maxx[i], bounds_maxy[i], bounds_maxz[i])); + if (b.IsValid()) + DebugRenderer::sInstance->DrawWireBox(b, Color::sGreen); + } + #endif + + // Check which sub nodes to visit + int num_results = ioVisitor.VisitRangeBlock(bounds_minx, bounds_miny, bounds_minz, bounds_maxx, bounds_maxy, bounds_maxz, properties, mTop); + + // Push them onto the stack + JPH_ASSERT(mTop + 4 < cStackSize); + properties.StoreInt4(&mPropertiesStack[mTop]); + mTop += num_results; + } + + // Check if we're done + if (ioVisitor.ShouldAbort()) + return; + + // Fetch next node until we find one that the visitor wants to see + do + --mTop; + while (mTop >= 0 && !ioVisitor.ShouldVisitRangeBlock(mTop)); + } + while (mTop >= 0); + } + + // This can be used to have the visitor early out (ioVisitor.ShouldAbort() returns true) and later continue again (call WalkHeightField() again) + JPH_INLINE bool IsDoneWalking() const + { + return mTop < 0; + } + +private: + const HeightFieldShape * mShape; + int mTop = 0; + uint32 mPropertiesStack[cStackSize]; +}; + +template +void HeightFieldShape::WalkHeightField(Visitor &ioVisitor) const +{ + DecodingContext ctx(this); + ctx.WalkHeightField(ioVisitor); +} + +bool HeightFieldShape::CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const +{ + JPH_PROFILE_FUNCTION(); + + struct Visitor + { + JPH_INLINE explicit Visitor(const HeightFieldShape *inShape, const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) : + mHit(ioHit), + mRayOrigin(inRay.mOrigin), + mRayDirection(inRay.mDirection), + mRayInvDirection(inRay.mDirection), + mShape(inShape), + mSubShapeIDCreator(inSubShapeIDCreator) + { + } + + JPH_INLINE bool ShouldAbort() const + { + return mHit.mFraction <= 0.0f; + } + + JPH_INLINE bool ShouldVisitRangeBlock(int inStackTop) const + { + return mDistanceStack[inStackTop] < mHit.mFraction; + } + + JPH_INLINE int VisitRangeBlock(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, int inStackTop) + { + // Test bounds of 4 children + Vec4 distance = RayAABox4(mRayOrigin, mRayInvDirection, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ); + + // Sort so that highest values are first (we want to first process closer hits and we process stack top to bottom) + return SortReverseAndStore(distance, mHit.mFraction, ioProperties, &mDistanceStack[inStackTop]); + } + + JPH_INLINE void VisitTriangle(uint inX, uint inY, uint inTriangle, Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2) + { + float fraction = RayTriangle(mRayOrigin, mRayDirection, inV0, inV1, inV2); + if (fraction < mHit.mFraction) + { + // It's a closer hit + mHit.mFraction = fraction; + mHit.mSubShapeID2 = mShape->EncodeSubShapeID(mSubShapeIDCreator, inX, inY, inTriangle); + mReturnValue = true; + } + } + + RayCastResult & mHit; + Vec3 mRayOrigin; + Vec3 mRayDirection; + RayInvDirection mRayInvDirection; + const HeightFieldShape *mShape; + SubShapeIDCreator mSubShapeIDCreator; + bool mReturnValue = false; + float mDistanceStack[cStackSize]; + }; + + Visitor visitor(this, inRay, inSubShapeIDCreator, ioHit); + WalkHeightField(visitor); + + return visitor.mReturnValue; +} + +void HeightFieldShape::CastRay(const RayCast &inRay, const RayCastSettings &inRayCastSettings, const SubShapeIDCreator &inSubShapeIDCreator, CastRayCollector &ioCollector, const ShapeFilter &inShapeFilter) const +{ + JPH_PROFILE_FUNCTION(); + + // Test shape filter + if (!inShapeFilter.ShouldCollide(this, inSubShapeIDCreator.GetID())) + return; + + struct Visitor + { + JPH_INLINE explicit Visitor(const HeightFieldShape *inShape, const RayCast &inRay, const RayCastSettings &inRayCastSettings, const SubShapeIDCreator &inSubShapeIDCreator, CastRayCollector &ioCollector) : + mCollector(ioCollector), + mRayOrigin(inRay.mOrigin), + mRayDirection(inRay.mDirection), + mRayInvDirection(inRay.mDirection), + mBackFaceMode(inRayCastSettings.mBackFaceMode), + mShape(inShape), + mSubShapeIDCreator(inSubShapeIDCreator) + { + } + + JPH_INLINE bool ShouldAbort() const + { + return mCollector.ShouldEarlyOut(); + } + + JPH_INLINE bool ShouldVisitRangeBlock(int inStackTop) const + { + return mDistanceStack[inStackTop] < mCollector.GetEarlyOutFraction(); + } + + JPH_INLINE int VisitRangeBlock(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, int inStackTop) + { + // Test bounds of 4 children + Vec4 distance = RayAABox4(mRayOrigin, mRayInvDirection, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ); + + // Sort so that highest values are first (we want to first process closer hits and we process stack top to bottom) + return SortReverseAndStore(distance, mCollector.GetEarlyOutFraction(), ioProperties, &mDistanceStack[inStackTop]); + } + + JPH_INLINE void VisitTriangle(uint inX, uint inY, uint inTriangle, Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2) const + { + // Back facing check + if (mBackFaceMode == EBackFaceMode::IgnoreBackFaces && (inV2 - inV0).Cross(inV1 - inV0).Dot(mRayDirection) < 0) + return; + + // Check the triangle + float fraction = RayTriangle(mRayOrigin, mRayDirection, inV0, inV1, inV2); + if (fraction < mCollector.GetEarlyOutFraction()) + { + RayCastResult hit; + hit.mBodyID = TransformedShape::sGetBodyID(mCollector.GetContext()); + hit.mFraction = fraction; + hit.mSubShapeID2 = mShape->EncodeSubShapeID(mSubShapeIDCreator, inX, inY, inTriangle); + mCollector.AddHit(hit); + } + } + + CastRayCollector & mCollector; + Vec3 mRayOrigin; + Vec3 mRayDirection; + RayInvDirection mRayInvDirection; + EBackFaceMode mBackFaceMode; + const HeightFieldShape *mShape; + SubShapeIDCreator mSubShapeIDCreator; + float mDistanceStack[cStackSize]; + }; + + Visitor visitor(this, inRay, inRayCastSettings, inSubShapeIDCreator, ioCollector); + WalkHeightField(visitor); +} + +void HeightFieldShape::CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter) const +{ + // A height field doesn't have volume, so we can't test insideness +} + +void HeightFieldShape::CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, SoftBodyVertex *ioVertices, uint inNumVertices, [[maybe_unused]] float inDeltaTime, [[maybe_unused]] Vec3Arg inDisplacementDueToGravity, int inCollidingShapeIndex) const +{ + JPH_PROFILE_FUNCTION(); + + struct Visitor : public CollideSoftBodyVerticesVsTriangles + { + using CollideSoftBodyVerticesVsTriangles::CollideSoftBodyVerticesVsTriangles; + + JPH_INLINE bool ShouldAbort() const + { + return false; + } + + JPH_INLINE bool ShouldVisitRangeBlock([[maybe_unused]] int inStackTop) const + { + return mDistanceStack[inStackTop] < mClosestDistanceSq; + } + + JPH_INLINE int VisitRangeBlock(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, int inStackTop) + { + // Get distance to vertex + Vec4 dist_sq = AABox4DistanceSqToPoint(mLocalPosition, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ); + + // Sort so that highest values are first (we want to first process closer hits and we process stack top to bottom) + return SortReverseAndStore(dist_sq, mClosestDistanceSq, ioProperties, &mDistanceStack[inStackTop]); + } + + JPH_INLINE void VisitTriangle([[maybe_unused]] uint inX, [[maybe_unused]] uint inY, [[maybe_unused]] uint inTriangle, Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2) + { + ProcessTriangle(inV0, inV1, inV2); + } + + float mDistanceStack[cStackSize]; + }; + + Visitor visitor(inCenterOfMassTransform, inScale); + + for (SoftBodyVertex *v = ioVertices, *sbv_end = ioVertices + inNumVertices; v < sbv_end; ++v) + if (v->mInvMass > 0.0f) + { + visitor.StartVertex(*v); + WalkHeightField(visitor); + visitor.FinishVertex(*v, inCollidingShapeIndex); + } +} + +void HeightFieldShape::sCastConvexVsHeightField(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, [[maybe_unused]] const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector) +{ + JPH_PROFILE_FUNCTION(); + + struct Visitor : public CastConvexVsTriangles + { + using CastConvexVsTriangles::CastConvexVsTriangles; + + JPH_INLINE bool ShouldAbort() const + { + return mCollector.ShouldEarlyOut(); + } + + JPH_INLINE bool ShouldVisitRangeBlock(int inStackTop) const + { + return mDistanceStack[inStackTop] < mCollector.GetPositiveEarlyOutFraction(); + } + + JPH_INLINE int VisitRangeBlock(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, int inStackTop) + { + // Scale the bounding boxes of this node + Vec4 bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z; + AABox4Scale(mScale, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z); + + // Enlarge them by the casted shape's box extents + AABox4EnlargeWithExtent(mBoxExtent, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z); + + // Test bounds of 4 children + Vec4 distance = RayAABox4(mBoxCenter, mInvDirection, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z); + + // Clear distance for invalid bounds + distance = Vec4::sSelect(Vec4::sReplicate(FLT_MAX), distance, Vec4::sLessOrEqual(inBoundsMinY, inBoundsMaxY)); + + // Sort so that highest values are first (we want to first process closer hits and we process stack top to bottom) + return SortReverseAndStore(distance, mCollector.GetPositiveEarlyOutFraction(), ioProperties, &mDistanceStack[inStackTop]); + } + + JPH_INLINE void VisitTriangle(uint inX, uint inY, uint inTriangle, Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2) + { + // Create sub shape id for this part + SubShapeID triangle_sub_shape_id = mShape2->EncodeSubShapeID(mSubShapeIDCreator2, inX, inY, inTriangle); + + // Determine active edges + uint8 active_edges = mShape2->GetEdgeFlags(inX, inY, inTriangle); + + Cast(inV0, inV1, inV2, active_edges, triangle_sub_shape_id); + } + + const HeightFieldShape * mShape2; + RayInvDirection mInvDirection; + Vec3 mBoxCenter; + Vec3 mBoxExtent; + SubShapeIDCreator mSubShapeIDCreator2; + float mDistanceStack[cStackSize]; + }; + + JPH_ASSERT(inShape->GetSubType() == EShapeSubType::HeightField); + const HeightFieldShape *shape = static_cast(inShape); + + Visitor visitor(inShapeCast, inShapeCastSettings, inScale, inCenterOfMassTransform2, inSubShapeIDCreator1, ioCollector); + visitor.mShape2 = shape; + visitor.mInvDirection.Set(inShapeCast.mDirection); + visitor.mBoxCenter = inShapeCast.mShapeWorldBounds.GetCenter(); + visitor.mBoxExtent = inShapeCast.mShapeWorldBounds.GetExtent(); + visitor.mSubShapeIDCreator2 = inSubShapeIDCreator2; + shape->WalkHeightField(visitor); +} + +void HeightFieldShape::sCastSphereVsHeightField(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, [[maybe_unused]] const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector) +{ + JPH_PROFILE_FUNCTION(); + + struct Visitor : public CastSphereVsTriangles + { + using CastSphereVsTriangles::CastSphereVsTriangles; + + JPH_INLINE bool ShouldAbort() const + { + return mCollector.ShouldEarlyOut(); + } + + JPH_INLINE bool ShouldVisitRangeBlock(int inStackTop) const + { + return mDistanceStack[inStackTop] < mCollector.GetPositiveEarlyOutFraction(); + } + + JPH_INLINE int VisitRangeBlock(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, int inStackTop) + { + // Scale the bounding boxes of this node + Vec4 bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z; + AABox4Scale(mScale, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z); + + // Enlarge them by the radius of the sphere + AABox4EnlargeWithExtent(Vec3::sReplicate(mRadius), bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z); + + // Test bounds of 4 children + Vec4 distance = RayAABox4(mStart, mInvDirection, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z); + + // Clear distance for invalid bounds + distance = Vec4::sSelect(Vec4::sReplicate(FLT_MAX), distance, Vec4::sLessOrEqual(inBoundsMinY, inBoundsMaxY)); + + // Sort so that highest values are first (we want to first process closer hits and we process stack top to bottom) + return SortReverseAndStore(distance, mCollector.GetPositiveEarlyOutFraction(), ioProperties, &mDistanceStack[inStackTop]); + } + + JPH_INLINE void VisitTriangle(uint inX, uint inY, uint inTriangle, Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2) + { + // Create sub shape id for this part + SubShapeID triangle_sub_shape_id = mShape2->EncodeSubShapeID(mSubShapeIDCreator2, inX, inY, inTriangle); + + // Determine active edges + uint8 active_edges = mShape2->GetEdgeFlags(inX, inY, inTriangle); + + Cast(inV0, inV1, inV2, active_edges, triangle_sub_shape_id); + } + + const HeightFieldShape * mShape2; + RayInvDirection mInvDirection; + SubShapeIDCreator mSubShapeIDCreator2; + float mDistanceStack[cStackSize]; + }; + + JPH_ASSERT(inShape->GetSubType() == EShapeSubType::HeightField); + const HeightFieldShape *shape = static_cast(inShape); + + Visitor visitor(inShapeCast, inShapeCastSettings, inScale, inCenterOfMassTransform2, inSubShapeIDCreator1, ioCollector); + visitor.mShape2 = shape; + visitor.mInvDirection.Set(inShapeCast.mDirection); + visitor.mSubShapeIDCreator2 = inSubShapeIDCreator2; + shape->WalkHeightField(visitor); +} + +struct HeightFieldShape::HSGetTrianglesContext +{ + HSGetTrianglesContext(const HeightFieldShape *inShape, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) : + mDecodeCtx(inShape), + mShape(inShape), + mLocalBox(Mat44::sInverseRotationTranslation(inRotation, inPositionCOM), inBox), + mHeightFieldScale(inScale), + mLocalToWorld(Mat44::sRotationTranslation(inRotation, inPositionCOM) * Mat44::sScale(inScale)), + mIsInsideOut(ScaleHelpers::IsInsideOut(inScale)) + { + } + + bool ShouldAbort() const + { + return mShouldAbort; + } + + bool ShouldVisitRangeBlock([[maybe_unused]] int inStackTop) const + { + return true; + } + + int VisitRangeBlock(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, [[maybe_unused]] int inStackTop) const + { + // Scale the bounding boxes of this node + Vec4 bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z; + AABox4Scale(mHeightFieldScale, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z); + + // Test which nodes collide + UVec4 collides = AABox4VsBox(mLocalBox, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z); + + // Filter out invalid bounding boxes + collides = UVec4::sAnd(collides, Vec4::sLessOrEqual(inBoundsMinY, inBoundsMaxY)); + + return CountAndSortTrues(collides, ioProperties); + } + + void VisitTriangle(uint inX, uint inY, [[maybe_unused]] uint inTriangle, Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2) + { + // When the buffer is full and we cannot process the triangles, abort the height field walk. The next time GetTrianglesNext is called we will continue here. + if (mNumTrianglesFound + 1 > mMaxTrianglesRequested) + { + mShouldAbort = true; + return; + } + + // Store vertices as Float3 + if (mIsInsideOut) + { + // Reverse vertices + (mLocalToWorld * inV0).StoreFloat3(mTriangleVertices++); + (mLocalToWorld * inV2).StoreFloat3(mTriangleVertices++); + (mLocalToWorld * inV1).StoreFloat3(mTriangleVertices++); + } + else + { + // Normal scale + (mLocalToWorld * inV0).StoreFloat3(mTriangleVertices++); + (mLocalToWorld * inV1).StoreFloat3(mTriangleVertices++); + (mLocalToWorld * inV2).StoreFloat3(mTriangleVertices++); + } + + // Decode material + if (mMaterials != nullptr) + *mMaterials++ = mShape->GetMaterial(inX, inY); + + // Accumulate triangles found + mNumTrianglesFound++; + } + + DecodingContext mDecodeCtx; + const HeightFieldShape * mShape; + OrientedBox mLocalBox; + Vec3 mHeightFieldScale; + Mat44 mLocalToWorld; + int mMaxTrianglesRequested; + Float3 * mTriangleVertices; + int mNumTrianglesFound; + const PhysicsMaterial ** mMaterials; + bool mShouldAbort; + bool mIsInsideOut; +}; + +void HeightFieldShape::GetTrianglesStart(GetTrianglesContext &ioContext, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) const +{ + static_assert(sizeof(HSGetTrianglesContext) <= sizeof(GetTrianglesContext), "GetTrianglesContext too small"); + JPH_ASSERT(IsAligned(&ioContext, alignof(HSGetTrianglesContext))); + + new (&ioContext) HSGetTrianglesContext(this, inBox, inPositionCOM, inRotation, inScale); +} + +int HeightFieldShape::GetTrianglesNext(GetTrianglesContext &ioContext, int inMaxTrianglesRequested, Float3 *outTriangleVertices, const PhysicsMaterial **outMaterials) const +{ + static_assert(cGetTrianglesMinTrianglesRequested >= 1, "cGetTrianglesMinTrianglesRequested is too small"); + JPH_ASSERT(inMaxTrianglesRequested >= cGetTrianglesMinTrianglesRequested); + + // Check if we're done + HSGetTrianglesContext &context = (HSGetTrianglesContext &)ioContext; + if (context.mDecodeCtx.IsDoneWalking()) + return 0; + + // Store parameters on context + context.mMaxTrianglesRequested = inMaxTrianglesRequested; + context.mTriangleVertices = outTriangleVertices; + context.mMaterials = outMaterials; + context.mShouldAbort = false; // Reset the abort flag + context.mNumTrianglesFound = 0; + + // Continue (or start) walking the height field + context.mDecodeCtx.WalkHeightField(context); + return context.mNumTrianglesFound; +} + +void HeightFieldShape::sCollideConvexVsHeightField(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, [[maybe_unused]] const ShapeFilter &inShapeFilter) +{ + JPH_PROFILE_FUNCTION(); + + // Get the shapes + JPH_ASSERT(inShape1->GetType() == EShapeType::Convex); + JPH_ASSERT(inShape2->GetType() == EShapeType::HeightField); + const ConvexShape *shape1 = static_cast(inShape1); + const HeightFieldShape *shape2 = static_cast(inShape2); + + struct Visitor : public CollideConvexVsTriangles + { + using CollideConvexVsTriangles::CollideConvexVsTriangles; + + JPH_INLINE bool ShouldAbort() const + { + return mCollector.ShouldEarlyOut(); + } + + JPH_INLINE bool ShouldVisitRangeBlock([[maybe_unused]] int inStackTop) const + { + return true; + } + + JPH_INLINE int VisitRangeBlock(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, [[maybe_unused]] int inStackTop) const + { + // Scale the bounding boxes of this node + Vec4 bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z; + AABox4Scale(mScale2, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z); + + // Test which nodes collide + UVec4 collides = AABox4VsBox(mBoundsOf1InSpaceOf2, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z); + + // Filter out invalid bounding boxes + collides = UVec4::sAnd(collides, Vec4::sLessOrEqual(inBoundsMinY, inBoundsMaxY)); + + return CountAndSortTrues(collides, ioProperties); + } + + JPH_INLINE void VisitTriangle(uint inX, uint inY, uint inTriangle, Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2) + { + // Create ID for triangle + SubShapeID triangle_sub_shape_id = mShape2->EncodeSubShapeID(mSubShapeIDCreator2, inX, inY, inTriangle); + + // Determine active edges + uint8 active_edges = mShape2->GetEdgeFlags(inX, inY, inTriangle); + + Collide(inV0, inV1, inV2, active_edges, triangle_sub_shape_id); + } + + const HeightFieldShape * mShape2; + SubShapeIDCreator mSubShapeIDCreator2; + }; + + Visitor visitor(shape1, inScale1, inScale2, inCenterOfMassTransform1, inCenterOfMassTransform2, inSubShapeIDCreator1.GetID(), inCollideShapeSettings, ioCollector); + visitor.mShape2 = shape2; + visitor.mSubShapeIDCreator2 = inSubShapeIDCreator2; + shape2->WalkHeightField(visitor); +} + +void HeightFieldShape::sCollideSphereVsHeightField(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, [[maybe_unused]] const ShapeFilter &inShapeFilter) +{ + JPH_PROFILE_FUNCTION(); + + // Get the shapes + JPH_ASSERT(inShape1->GetSubType() == EShapeSubType::Sphere); + JPH_ASSERT(inShape2->GetType() == EShapeType::HeightField); + const SphereShape *shape1 = static_cast(inShape1); + const HeightFieldShape *shape2 = static_cast(inShape2); + + struct Visitor : public CollideSphereVsTriangles + { + using CollideSphereVsTriangles::CollideSphereVsTriangles; + + JPH_INLINE bool ShouldAbort() const + { + return mCollector.ShouldEarlyOut(); + } + + JPH_INLINE bool ShouldVisitRangeBlock([[maybe_unused]] int inStackTop) const + { + return true; + } + + JPH_INLINE int VisitRangeBlock(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, [[maybe_unused]] int inStackTop) const + { + // Scale the bounding boxes of this node + Vec4 bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z; + AABox4Scale(mScale2, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z); + + // Test which nodes collide + UVec4 collides = AABox4VsSphere(mSphereCenterIn2, mRadiusPlusMaxSeparationSq, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z); + + // Filter out invalid bounding boxes + collides = UVec4::sAnd(collides, Vec4::sLessOrEqual(inBoundsMinY, inBoundsMaxY)); + + return CountAndSortTrues(collides, ioProperties); + } + + JPH_INLINE void VisitTriangle(uint inX, uint inY, uint inTriangle, Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2) + { + // Create ID for triangle + SubShapeID triangle_sub_shape_id = mShape2->EncodeSubShapeID(mSubShapeIDCreator2, inX, inY, inTriangle); + + // Determine active edges + uint8 active_edges = mShape2->GetEdgeFlags(inX, inY, inTriangle); + + Collide(inV0, inV1, inV2, active_edges, triangle_sub_shape_id); + } + + const HeightFieldShape * mShape2; + SubShapeIDCreator mSubShapeIDCreator2; + }; + + Visitor visitor(shape1, inScale1, inScale2, inCenterOfMassTransform1, inCenterOfMassTransform2, inSubShapeIDCreator1.GetID(), inCollideShapeSettings, ioCollector); + visitor.mShape2 = shape2; + visitor.mSubShapeIDCreator2 = inSubShapeIDCreator2; + shape2->WalkHeightField(visitor); +} + +void HeightFieldShape::SaveBinaryState(StreamOut &inStream) const +{ + Shape::SaveBinaryState(inStream); + + inStream.Write(mOffset); + inStream.Write(mScale); + inStream.Write(mSampleCount); + inStream.Write(mBlockSize); + inStream.Write(mBitsPerSample); + inStream.Write(mMinSample); + inStream.Write(mMaxSample); + inStream.Write(mRangeBlocks); + inStream.Write(mHeightSamples); + inStream.Write(mActiveEdges); + inStream.Write(mMaterialIndices); + inStream.Write(mNumBitsPerMaterialIndex); +} + +void HeightFieldShape::RestoreBinaryState(StreamIn &inStream) +{ + Shape::RestoreBinaryState(inStream); + + inStream.Read(mOffset); + inStream.Read(mScale); + inStream.Read(mSampleCount); + inStream.Read(mBlockSize); + inStream.Read(mBitsPerSample); + inStream.Read(mMinSample); + inStream.Read(mMaxSample); + inStream.Read(mRangeBlocks); + inStream.Read(mHeightSamples); + inStream.Read(mActiveEdges); + inStream.Read(mMaterialIndices); + inStream.Read(mNumBitsPerMaterialIndex); + + CacheValues(); +} + +void HeightFieldShape::SaveMaterialState(PhysicsMaterialList &outMaterials) const +{ + outMaterials = mMaterials; +} + +void HeightFieldShape::RestoreMaterialState(const PhysicsMaterialRefC *inMaterials, uint inNumMaterials) +{ + mMaterials.assign(inMaterials, inMaterials + inNumMaterials); +} + +Shape::Stats HeightFieldShape::GetStats() const +{ + return Stats( + sizeof(*this) + + mMaterials.size() * sizeof(Ref) + + mRangeBlocks.size() * sizeof(RangeBlock) + + mHeightSamples.size() * sizeof(uint8) + + mActiveEdges.size() * sizeof(uint8) + + mMaterialIndices.size() * sizeof(uint8), + mHeightSamples.empty()? 0 : Square(mSampleCount - 1) * 2); +} + +void HeightFieldShape::sRegister() +{ + ShapeFunctions &f = ShapeFunctions::sGet(EShapeSubType::HeightField); + f.mConstruct = []() -> Shape * { return new HeightFieldShape; }; + f.mColor = Color::sPurple; + + for (EShapeSubType s : sConvexSubShapeTypes) + { + CollisionDispatch::sRegisterCollideShape(s, EShapeSubType::HeightField, sCollideConvexVsHeightField); + CollisionDispatch::sRegisterCastShape(s, EShapeSubType::HeightField, sCastConvexVsHeightField); + + CollisionDispatch::sRegisterCastShape(EShapeSubType::HeightField, s, CollisionDispatch::sReversedCastShape); + CollisionDispatch::sRegisterCollideShape(EShapeSubType::HeightField, s, CollisionDispatch::sReversedCollideShape); + } + + // Specialized collision functions + CollisionDispatch::sRegisterCollideShape(EShapeSubType::Sphere, EShapeSubType::HeightField, sCollideSphereVsHeightField); + CollisionDispatch::sRegisterCastShape(EShapeSubType::Sphere, EShapeSubType::HeightField, sCastSphereVsHeightField); +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/HeightFieldShape.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/HeightFieldShape.h new file mode 100644 index 00000000000..e3a855ed12f --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/HeightFieldShape.h @@ -0,0 +1,349 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#ifdef JPH_DEBUG_RENDERER + #include +#endif // JPH_DEBUG_RENDERER + +JPH_NAMESPACE_BEGIN + +class ConvexShape; +class CollideShapeSettings; +class TempAllocator; + +/// Constants for HeightFieldShape, this was moved out of the HeightFieldShape because of a linker bug +namespace HeightFieldShapeConstants +{ + /// Value used to create gaps in the height field + constexpr float cNoCollisionValue = FLT_MAX; + + /// Stack size to use during WalkHeightField + constexpr int cStackSize = 128; + + /// A position in the hierarchical grid is defined by a level (which grid), x and y position. We encode this in a single uint32 as: level << 28 | y << 14 | x + constexpr uint cNumBitsXY = 14; + constexpr uint cMaskBitsXY = (1 << cNumBitsXY) - 1; + constexpr uint cLevelShift = 2 * cNumBitsXY; + + /// When height samples are converted to 16 bit: + constexpr uint16 cNoCollisionValue16 = 0xffff; ///< This is the magic value for 'no collision' + constexpr uint16 cMaxHeightValue16 = 0xfffe; ///< This is the maximum allowed height value +}; + +/// Class that constructs a HeightFieldShape +class JPH_EXPORT HeightFieldShapeSettings final : public ShapeSettings +{ +public: + JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, HeightFieldShapeSettings) + + /// Default constructor for deserialization + HeightFieldShapeSettings() = default; + + /// Create a height field shape of inSampleCount * inSampleCount vertices. + /// The height field is a surface defined by: inOffset + inScale * (x, inSamples[y * inSampleCount + x], y). + /// where x and y are integers in the range x and y e [0, inSampleCount - 1]. + /// inSampleCount: inSampleCount / mBlockSize must be minimally 2 and a power of 2 is the most efficient in terms of performance and storage. + /// inSamples: inSampleCount^2 vertices. + /// inMaterialIndices: (inSampleCount - 1)^2 indices that index into inMaterialList. + HeightFieldShapeSettings(const float *inSamples, Vec3Arg inOffset, Vec3Arg inScale, uint32 inSampleCount, const uint8 *inMaterialIndices = nullptr, const PhysicsMaterialList &inMaterialList = PhysicsMaterialList()); + + // See: ShapeSettings + virtual ShapeResult Create() const override; + + /// Determine the minimal and maximal value of mHeightSamples (will ignore cNoCollisionValue) + /// @param outMinValue The minimal value of mHeightSamples or FLT_MAX if no samples have collision + /// @param outMaxValue The maximal value of mHeightSamples or -FLT_MAX if no samples have collision + /// @param outQuantizationScale (value - outMinValue) * outQuantizationScale quantizes a height sample to 16 bits + void DetermineMinAndMaxSample(float &outMinValue, float &outMaxValue, float &outQuantizationScale) const; + + /// Given mBlockSize, mSampleCount and mHeightSamples, calculate the amount of bits needed to stay below absolute error inMaxError + /// @param inMaxError Maximum allowed error in mHeightSamples after compression (note that this does not take mScale.Y into account) + /// @return Needed bits per sample in the range [1, 8]. + uint32 CalculateBitsPerSampleForError(float inMaxError) const; + + /// The height field is a surface defined by: mOffset + mScale * (x, mHeightSamples[y * mSampleCount + x], y). + /// where x and y are integers in the range x and y e [0, mSampleCount - 1]. + Vec3 mOffset = Vec3::sZero(); + Vec3 mScale = Vec3::sReplicate(1.0f); + uint32 mSampleCount = 0; + + /// Artificial minimal value of mHeightSamples, used for compression and can be used to update the terrain after creating with lower height values. If there are any lower values in mHeightSamples, this value will be ignored. + float mMinHeightValue = FLT_MAX; + + /// Artificial maximum value of mHeightSamples, used for compression and can be used to update the terrain after creating with higher height values. If there are any higher values in mHeightSamples, this value will be ignored. + float mMaxHeightValue = -FLT_MAX; + + /// The heightfield is divided in blocks of mBlockSize * mBlockSize * 2 triangles and the acceleration structure culls blocks only, + /// bigger block sizes reduce memory consumption but also reduce query performance. Sensible values are [2, 8], does not need to be + /// a power of 2. Note that at run-time we'll perform one more grid subdivision, so the effective block size is half of what is provided here. + uint32 mBlockSize = 2; + + /// How many bits per sample to use to compress the height field. Can be in the range [1, 8]. + /// Note that each sample is compressed relative to the min/max value of its block of mBlockSize * mBlockSize pixels so the effective precision is higher. + /// Also note that increasing mBlockSize saves more memory than reducing the amount of bits per sample. + uint32 mBitsPerSample = 8; + + /// An array of mSampleCount^2 height samples. Samples are stored in row major order, so the sample at (x, y) is at index y * mSampleCount + x. + Array mHeightSamples; + + /// An array of (mSampleCount - 1)^2 material indices. + Array mMaterialIndices; + + /// The materials of square at (x, y) is: mMaterials[mMaterialIndices[x + y * (mSampleCount - 1)]] + PhysicsMaterialList mMaterials; + + /// Cosine of the threshold angle (if the angle between the two triangles is bigger than this, the edge is active, note that a concave edge is always inactive). + /// Setting this value too small can cause ghost collisions with edges, setting it too big can cause depenetration artifacts (objects not depenetrating quickly). + /// Valid ranges are between cos(0 degrees) and cos(90 degrees). The default value is cos(5 degrees). + float mActiveEdgeCosThresholdAngle = 0.996195f; // cos(5 degrees) +}; + +/// A height field shape. Cannot be used as a dynamic object. +class JPH_EXPORT HeightFieldShape final : public Shape +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Constructor + HeightFieldShape() : Shape(EShapeType::HeightField, EShapeSubType::HeightField) { } + HeightFieldShape(const HeightFieldShapeSettings &inSettings, ShapeResult &outResult); + + // See Shape::MustBeStatic + virtual bool MustBeStatic() const override { return true; } + + /// Get the size of the height field. Note that this will always be rounded up to the nearest multiple of GetBlockSize(). + inline uint GetSampleCount() const { return mSampleCount; } + + /// Get the size of a block + inline uint GetBlockSize() const { return mBlockSize; } + + // See Shape::GetLocalBounds + virtual AABox GetLocalBounds() const override; + + // See Shape::GetSubShapeIDBitsRecursive + virtual uint GetSubShapeIDBitsRecursive() const override { return GetSubShapeIDBits(); } + + // See Shape::GetInnerRadius + virtual float GetInnerRadius() const override { return 0.0f; } + + // See Shape::GetMassProperties + virtual MassProperties GetMassProperties() const override; + + // See Shape::GetMaterial + virtual const PhysicsMaterial * GetMaterial(const SubShapeID &inSubShapeID) const override; + + /// Overload to get the material at a particular location + const PhysicsMaterial * GetMaterial(uint inX, uint inY) const; + + // See Shape::GetSurfaceNormal + virtual Vec3 GetSurfaceNormal(const SubShapeID &inSubShapeID, Vec3Arg inLocalSurfacePosition) const override; + + // See Shape::GetSupportingFace + virtual void GetSupportingFace(const SubShapeID &inSubShapeID, Vec3Arg inDirection, Vec3Arg inScale, Mat44Arg inCenterOfMassTransform, SupportingFace &outVertices) const override; + + // See Shape::GetSubmergedVolume + virtual void GetSubmergedVolume(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, const Plane &inSurface, float &outTotalVolume, float &outSubmergedVolume, Vec3 &outCenterOfBuoyancy JPH_IF_DEBUG_RENDERER(, RVec3Arg inBaseOffset)) const override { JPH_ASSERT(false, "Not supported"); } + +#ifdef JPH_DEBUG_RENDERER + // See Shape::Draw + virtual void Draw(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inUseMaterialColors, bool inDrawWireframe) const override; +#endif // JPH_DEBUG_RENDERER + + // See Shape::CastRay + virtual bool CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const override; + virtual void CastRay(const RayCast &inRay, const RayCastSettings &inRayCastSettings, const SubShapeIDCreator &inSubShapeIDCreator, CastRayCollector &ioCollector, const ShapeFilter &inShapeFilter = { }) const override; + + // See: Shape::CollidePoint + virtual void CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter = { }) const override; + + // See: Shape::CollideSoftBodyVertices + virtual void CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, SoftBodyVertex *ioVertices, uint inNumVertices, float inDeltaTime, Vec3Arg inDisplacementDueToGravity, int inCollidingShapeIndex) const override; + + // See Shape::GetTrianglesStart + virtual void GetTrianglesStart(GetTrianglesContext &ioContext, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) const override; + + // See Shape::GetTrianglesNext + virtual int GetTrianglesNext(GetTrianglesContext &ioContext, int inMaxTrianglesRequested, Float3 *outTriangleVertices, const PhysicsMaterial **outMaterials = nullptr) const override; + + /// Get height field position at sampled location (inX, inY). + /// where inX and inY are integers in the range inX e [0, mSampleCount - 1] and inY e [0, mSampleCount - 1]. + Vec3 GetPosition(uint inX, uint inY) const; + + /// Check if height field at sampled location (inX, inY) has collision (has a hole or not) + bool IsNoCollision(uint inX, uint inY) const; + + /// Projects inLocalPosition (a point in the space of the shape) along the Y axis onto the surface and returns it in outSurfacePosition. + /// When there is no surface position (because of a hole or because the point is outside the heightfield) the function will return false. + bool ProjectOntoSurface(Vec3Arg inLocalPosition, Vec3 &outSurfacePosition, SubShapeID &outSubShapeID) const; + + /// Get the height values of a block of data. + /// Note that the height values are decompressed so will be slightly different from what the shape was originally created with. + /// @param inX Start X position, must be a multiple of mBlockSize and in the range [0, mSampleCount - 1] + /// @param inY Start Y position, must be a multiple of mBlockSize and in the range [0, mSampleCount - 1] + /// @param inSizeX Number of samples in X direction, must be a multiple of mBlockSize and in the range [0, mSampleCount - inX] + /// @param inSizeY Number of samples in Y direction, must be a multiple of mBlockSize and in the range [0, mSampleCount - inX] + /// @param outHeights Returned height values, must be at least inSizeX * inSizeY floats. Values are returned in x-major order and can be cNoCollisionValue. + /// @param inHeightsStride Stride in floats between two consecutive rows of outHeights. + void GetHeights(uint inX, uint inY, uint inSizeX, uint inSizeY, float *outHeights, uint inHeightsStride) const; + + /// Set the height values of a block of data. + /// Note that this requires decompressing and recompressing a border of size mBlockSize in the negative x/y direction so will cause some precision loss. + /// @param inX Start X position, must be a multiple of mBlockSize and in the range [0, mSampleCount - 1] + /// @param inY Start Y position, must be a multiple of mBlockSize and in the range [0, mSampleCount - 1] + /// @param inSizeX Number of samples in X direction, must be a multiple of mBlockSize and in the range [0, mSampleCount - inX] + /// @param inSizeY Number of samples in Y direction, must be a multiple of mBlockSize and in the range [0, mSampleCount - inX] + /// @param inHeights The new height values to set, must be an array of inSizeX * inSizeY floats, can be cNoCollisionValue. + /// @param inHeightsStride Stride in floats between two consecutive rows of outHeights. + /// @param inAllocator Allocator to use for temporary memory + /// @param inActiveEdgeCosThresholdAngle Cosine of the threshold angle (if the angle between the two triangles is bigger than this, the edge is active, note that a concave edge is always inactive). + void SetHeights(uint inX, uint inY, uint inSizeX, uint inSizeY, const float *inHeights, uint inHeightsStride, TempAllocator &inAllocator, float inActiveEdgeCosThresholdAngle = 0.996195f); + + /// Get the current list of materials, the indices returned by GetMaterials() will index into this list. + const PhysicsMaterialList & GetMaterialList() const { return mMaterials; } + + /// Get the material indices of a block of data. + /// @param inX Start X position, must in the range [0, mSampleCount - 1] + /// @param inY Start Y position, must in the range [0, mSampleCount - 1] + /// @param inSizeX Number of samples in X direction + /// @param inSizeY Number of samples in Y direction + /// @param outMaterials Returned material indices, must be at least inSizeX * inSizeY uint8s. Values are returned in x-major order. + /// @param inMaterialsStride Stride in uint8s between two consecutive rows of outMaterials. + void GetMaterials(uint inX, uint inY, uint inSizeX, uint inSizeY, uint8 *outMaterials, uint inMaterialsStride) const; + + /// Set the material indices of a block of data. + /// @param inX Start X position, must in the range [0, mSampleCount - 1] + /// @param inY Start Y position, must in the range [0, mSampleCount - 1] + /// @param inSizeX Number of samples in X direction + /// @param inSizeY Number of samples in Y direction + /// @param inMaterials The new material indices, must be at least inSizeX * inSizeY uint8s. Values are returned in x-major order. + /// @param inMaterialsStride Stride in uint8s between two consecutive rows of inMaterials. + /// @param inMaterialList The material list to use for the new material indices or nullptr if the material list should not be updated + /// @param inAllocator Allocator to use for temporary memory + /// @return True if the material indices were set, false if the total number of materials exceeded 256 + bool SetMaterials(uint inX, uint inY, uint inSizeX, uint inSizeY, const uint8 *inMaterials, uint inMaterialsStride, const PhysicsMaterialList *inMaterialList, TempAllocator &inAllocator); + + // See Shape + virtual void SaveBinaryState(StreamOut &inStream) const override; + virtual void SaveMaterialState(PhysicsMaterialList &outMaterials) const override; + virtual void RestoreMaterialState(const PhysicsMaterialRefC *inMaterials, uint inNumMaterials) override; + + // See Shape::GetStats + virtual Stats GetStats() const override; + + // See Shape::GetVolume + virtual float GetVolume() const override { return 0; } + +#ifdef JPH_DEBUG_RENDERER + // Settings + static bool sDrawTriangleOutlines; +#endif // JPH_DEBUG_RENDERER + + // Register shape functions with the registry + static void sRegister(); + +protected: + // See: Shape::RestoreBinaryState + virtual void RestoreBinaryState(StreamIn &inStream) override; + +private: + class DecodingContext; ///< Context class for walking through all nodes of a heightfield + struct HSGetTrianglesContext; ///< Context class for GetTrianglesStart/Next + + /// Calculate commonly used values and store them in the shape + void CacheValues(); + + /// Calculate bit mask for all active edges in the heightfield for a specific region + void CalculateActiveEdges(uint inX, uint inY, uint inSizeX, uint inSizeY, const float *inHeights, uint inHeightsStartX, uint inHeightsStartY, uint inHeightsStride, float inHeightsScale, float inActiveEdgeCosThresholdAngle, TempAllocator &inAllocator); + + /// Calculate bit mask for all active edges in the heightfield + void CalculateActiveEdges(const HeightFieldShapeSettings &inSettings); + + /// Store material indices in the least amount of bits per index possible + void StoreMaterialIndices(const HeightFieldShapeSettings &inSettings); + + /// Get the amount of horizontal/vertical blocks + inline uint GetNumBlocks() const { return mSampleCount / mBlockSize; } + + /// Get the maximum level (amount of grids) of the tree + static inline uint sGetMaxLevel(uint inNumBlocks) { return 32 - CountLeadingZeros(inNumBlocks - 1); } + + /// Get the range block offset and stride for GetBlockOffsetAndScale + static inline void sGetRangeBlockOffsetAndStride(uint inNumBlocks, uint inMaxLevel, uint &outRangeBlockOffset, uint &outRangeBlockStride); + + /// For block (inBlockX, inBlockY) get the offset and scale needed to decode a uint8 height sample to a uint16 + inline void GetBlockOffsetAndScale(uint inBlockX, uint inBlockY, uint inRangeBlockOffset, uint inRangeBlockStride, float &outBlockOffset, float &outBlockScale) const; + + /// Get the height sample at position (inX, inY) + inline uint8 GetHeightSample(uint inX, uint inY) const; + + /// Faster version of GetPosition when block offset and scale are already known + inline Vec3 GetPosition(uint inX, uint inY, float inBlockOffset, float inBlockScale, bool &outNoCollision) const; + + /// Determine amount of bits needed to encode sub shape id + uint GetSubShapeIDBits() const; + + /// En/decode a sub shape ID. inX and inY specify the coordinate of the triangle. inTriangle == 0 is the lower triangle, inTriangle == 1 is the upper triangle. + inline SubShapeID EncodeSubShapeID(const SubShapeIDCreator &inCreator, uint inX, uint inY, uint inTriangle) const; + inline void DecodeSubShapeID(const SubShapeID &inSubShapeID, uint &outX, uint &outY, uint &outTriangle) const; + + /// Get the edge flags for a triangle + inline uint8 GetEdgeFlags(uint inX, uint inY, uint inTriangle) const; + + // Helper functions called by CollisionDispatch + static void sCollideConvexVsHeightField(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter); + static void sCollideSphereVsHeightField(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter); + static void sCastConvexVsHeightField(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector); + static void sCastSphereVsHeightField(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector); + + /// Visit the entire height field using a visitor pattern + /// Note: Used to be inlined but this triggers a bug in MSVC where it will not free the memory allocated by alloca which causes a stack overflow when WalkHeightField is called in a loop (clang does it correct) + template + void WalkHeightField(Visitor &ioVisitor) const; + + /// A block of 2x2 ranges used to form a hierarchical grid, ordered left top, right top, left bottom, right bottom + struct alignas(16) RangeBlock + { + uint16 mMin[4]; + uint16 mMax[4]; + }; + + /// For block (inBlockX, inBlockY) get the range block and the entry in the range block + inline void GetRangeBlock(uint inBlockX, uint inBlockY, uint inRangeBlockOffset, uint inRangeBlockStride, RangeBlock *&outBlock, uint &outIndexInBlock); + + /// Offset of first RangedBlock in grid per level + static const uint sGridOffsets[]; + + /// The height field is a surface defined by: mOffset + mScale * (x, mHeightSamples[y * mSampleCount + x], y). + /// where x and y are integers in the range x and y e [0, mSampleCount - 1]. + Vec3 mOffset = Vec3::sZero(); + Vec3 mScale = Vec3::sReplicate(1.0f); + + /// Height data + uint32 mSampleCount = 0; ///< See HeightFieldShapeSettings::mSampleCount + uint32 mBlockSize = 2; ///< See HeightFieldShapeSettings::mBlockSize + uint8 mBitsPerSample = 8; ///< See HeightFieldShapeSettings::mBitsPerSample + uint8 mSampleMask = 0xff; ///< All bits set for a sample: (1 << mBitsPerSample) - 1, used to indicate that there's no collision + uint16 mMinSample = HeightFieldShapeConstants::cNoCollisionValue16; ///< Min and max value in mHeightSamples quantized to 16 bit, for calculating bounding box + uint16 mMaxSample = HeightFieldShapeConstants::cNoCollisionValue16; + Array mRangeBlocks; ///< Hierarchical grid of range data describing the height variations within 1 block. The grid for level starts at offset sGridOffsets[] + Array mHeightSamples; ///< mBitsPerSample-bit height samples. Value [0, mMaxHeightValue] maps to highest detail grid in mRangeBlocks [mMin, mMax]. mNoCollisionValue is reserved to indicate no collision. + Array mActiveEdges; ///< (mSampleCount - 1)^2 * 3-bit active edge flags. + + /// Materials + PhysicsMaterialList mMaterials; ///< The materials of square at (x, y) is: mMaterials[mMaterialIndices[x + y * (mSampleCount - 1)]] + Array mMaterialIndices; ///< Compressed to the minimum amount of bits per material index (mSampleCount - 1) * (mSampleCount - 1) * mNumBitsPerMaterialIndex bits of data + uint32 mNumBitsPerMaterialIndex = 0; ///< Number of bits per material index + +#ifdef JPH_DEBUG_RENDERER + /// Temporary rendering data + mutable Array mGeometry; + mutable bool mCachedUseMaterialColors = false; ///< This is used to regenerate the triangle batch if the drawing settings change +#endif // JPH_DEBUG_RENDERER +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/MeshShape.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/MeshShape.cpp new file mode 100644 index 00000000000..cf5e864031e --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/MeshShape.cpp @@ -0,0 +1,1244 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +#ifdef JPH_DEBUG_RENDERER +bool MeshShape::sDrawTriangleGroups = false; +bool MeshShape::sDrawTriangleOutlines = false; +#endif // JPH_DEBUG_RENDERER + +JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(MeshShapeSettings) +{ + JPH_ADD_BASE_CLASS(MeshShapeSettings, ShapeSettings) + + JPH_ADD_ATTRIBUTE(MeshShapeSettings, mTriangleVertices) + JPH_ADD_ATTRIBUTE(MeshShapeSettings, mIndexedTriangles) + JPH_ADD_ATTRIBUTE(MeshShapeSettings, mMaterials) + JPH_ADD_ATTRIBUTE(MeshShapeSettings, mMaxTrianglesPerLeaf) + JPH_ADD_ATTRIBUTE(MeshShapeSettings, mActiveEdgeCosThresholdAngle) +} + +// Codecs this mesh shape is using +using TriangleCodec = TriangleCodecIndexed8BitPackSOA4Flags; +using NodeCodec = NodeCodecQuadTreeHalfFloat<1>; + +// Get header for tree +static JPH_INLINE const NodeCodec::Header *sGetNodeHeader(const ByteBuffer &inTree) +{ + return inTree.Get(0); +} + +// Get header for triangles +static JPH_INLINE const TriangleCodec::TriangleHeader *sGetTriangleHeader(const ByteBuffer &inTree) +{ + return inTree.Get(NodeCodec::HeaderSize); +} + +MeshShapeSettings::MeshShapeSettings(const TriangleList &inTriangles, PhysicsMaterialList inMaterials) : + mMaterials(std::move(inMaterials)) +{ + Indexify(inTriangles, mTriangleVertices, mIndexedTriangles); + + Sanitize(); +} + +MeshShapeSettings::MeshShapeSettings(VertexList inVertices, IndexedTriangleList inTriangles, PhysicsMaterialList inMaterials) : + mTriangleVertices(std::move(inVertices)), + mIndexedTriangles(std::move(inTriangles)), + mMaterials(std::move(inMaterials)) +{ + Sanitize(); +} + +void MeshShapeSettings::Sanitize() +{ + // Remove degenerate and duplicate triangles + UnorderedSet triangles; + triangles.reserve(mIndexedTriangles.size()); + TriangleCodec::ValidationContext validation_ctx(mIndexedTriangles, mTriangleVertices); + for (int t = (int)mIndexedTriangles.size() - 1; t >= 0; --t) + { + const IndexedTriangle &tri = mIndexedTriangles[t]; + + if (tri.IsDegenerate(mTriangleVertices) // Degenerate triangle + || validation_ctx.IsDegenerate(tri) // Triangle is degenerate in the quantized space + || !triangles.insert(tri.GetLowestIndexFirst()).second) // Duplicate triangle + { + // The order of triangles doesn't matter (gets reordered while building the tree), so we can just swap the last triangle into this slot + mIndexedTriangles[t] = mIndexedTriangles.back(); + mIndexedTriangles.pop_back(); + } + } +} + +ShapeSettings::ShapeResult MeshShapeSettings::Create() const +{ + if (mCachedResult.IsEmpty()) + Ref shape = new MeshShape(*this, mCachedResult); + return mCachedResult; +} + +MeshShape::MeshShape(const MeshShapeSettings &inSettings, ShapeResult &outResult) : + Shape(EShapeType::Mesh, EShapeSubType::Mesh, inSettings, outResult) +{ + // Check if there are any triangles + if (inSettings.mIndexedTriangles.empty()) + { + outResult.SetError("Need triangles to create a mesh shape!"); + return; + } + + // Check triangles + TriangleCodec::ValidationContext validation_ctx(inSettings.mIndexedTriangles, inSettings.mTriangleVertices); + for (int t = (int)inSettings.mIndexedTriangles.size() - 1; t >= 0; --t) + { + const IndexedTriangle &triangle = inSettings.mIndexedTriangles[t]; + if (triangle.IsDegenerate(inSettings.mTriangleVertices) + || validation_ctx.IsDegenerate(triangle)) + { + outResult.SetError(StringFormat("Triangle %d is degenerate!", t)); + return; + } + else + { + // Check vertex indices + for (uint32 idx : triangle.mIdx) + if (idx >= inSettings.mTriangleVertices.size()) + { + outResult.SetError(StringFormat("Vertex index %u is beyond vertex list (size: %u)", idx, (uint)inSettings.mTriangleVertices.size())); + return; + } + } + } + + // Copy materials + mMaterials = inSettings.mMaterials; + if (!mMaterials.empty()) + { + // Validate materials + if (mMaterials.size() > (1 << FLAGS_MATERIAL_BITS)) + { + outResult.SetError(StringFormat("Supporting max %d materials per mesh", 1 << FLAGS_MATERIAL_BITS)); + return; + } + for (const IndexedTriangle &t : inSettings.mIndexedTriangles) + if (t.mMaterialIndex >= mMaterials.size()) + { + outResult.SetError(StringFormat("Triangle material %u is beyond material list (size: %u)", t.mMaterialIndex, (uint)mMaterials.size())); + return; + } + } + else + { + // No materials assigned, validate that all triangles use material index 0 + for (const IndexedTriangle &t : inSettings.mIndexedTriangles) + if (t.mMaterialIndex != 0) + { + outResult.SetError("No materials present, all triangles should have material index 0"); + return; + } + } + + // Check max triangles + if (inSettings.mMaxTrianglesPerLeaf < 1 || inSettings.mMaxTrianglesPerLeaf > MaxTrianglesPerLeaf) + { + outResult.SetError("Invalid max triangles per leaf"); + return; + } + + // Fill in active edge bits + IndexedTriangleList indexed_triangles = inSettings.mIndexedTriangles; // Copy indices since we're adding the 'active edge' flag + sFindActiveEdges(inSettings, indexed_triangles); + + // Create triangle splitter + TriangleSplitterBinning splitter(inSettings.mTriangleVertices, indexed_triangles); + + // Build tree + AABBTreeBuilder builder(splitter, inSettings.mMaxTrianglesPerLeaf); + AABBTreeBuilderStats builder_stats; + AABBTreeBuilder::Node *root = builder.Build(builder_stats); + + // Convert to buffer + AABBTreeToBuffer buffer; + const char *error = nullptr; + if (!buffer.Convert(inSettings.mTriangleVertices, root, error)) + { + outResult.SetError(error); + delete root; + return; + } + + // Kill tree + delete root; + + // Move data to this class + mTree.swap(buffer.GetBuffer()); + + // Check if we're not exceeding the amount of sub shape id bits + if (GetSubShapeIDBitsRecursive() > SubShapeID::MaxBits) + { + outResult.SetError("Mesh is too big and exceeds the amount of available sub shape ID bits"); + return; + } + + outResult.Set(this); +} + +void MeshShape::sFindActiveEdges(const MeshShapeSettings &inSettings, IndexedTriangleList &ioIndices) +{ + // A struct to hold the two vertex indices of an edge + struct Edge + { + Edge(int inIdx1, int inIdx2) : mIdx1(min(inIdx1, inIdx2)), mIdx2(max(inIdx1, inIdx2)) { } + + uint GetIndexInTriangle(const IndexedTriangle &inTriangle) const + { + for (uint edge_idx = 0; edge_idx < 3; ++edge_idx) + { + Edge edge(inTriangle.mIdx[edge_idx], inTriangle.mIdx[(edge_idx + 1) % 3]); + if (*this == edge) + return edge_idx; + } + + JPH_ASSERT(false); + return ~uint(0); + } + + bool operator == (const Edge &inRHS) const + { + return mIdx1 == inRHS.mIdx1 && mIdx2 == inRHS.mIdx2; + } + + int mIdx1; + int mIdx2; + }; + + JPH_MAKE_HASH_STRUCT(Edge, EdgeHash, t.mIdx1, t.mIdx2) + + // A struct to hold the triangles that are connected to an edge + struct TriangleIndices + { + uint mNumTriangles = 0; + uint mTriangleIndices[2]; + }; + + // Build a list of edge to triangles + using EdgeToTriangle = UnorderedMap; + EdgeToTriangle edge_to_triangle; + edge_to_triangle.reserve(ioIndices.size() * 3); + for (uint triangle_idx = 0; triangle_idx < ioIndices.size(); ++triangle_idx) + { + IndexedTriangle &triangle = ioIndices[triangle_idx]; + for (uint edge_idx = 0; edge_idx < 3; ++edge_idx) + { + Edge edge(triangle.mIdx[edge_idx], triangle.mIdx[(edge_idx + 1) % 3]); + TriangleIndices &indices = edge_to_triangle[edge]; + if (indices.mNumTriangles < 2) + { + // Store index of triangle that connects to this edge + indices.mTriangleIndices[indices.mNumTriangles] = triangle_idx; + indices.mNumTriangles++; + } + else + { + // 3 or more triangles share an edge, mark this edge as active + uint32 mask = 1 << (edge_idx + FLAGS_ACTIVE_EGDE_SHIFT); + JPH_ASSERT((triangle.mMaterialIndex & mask) == 0); + triangle.mMaterialIndex |= mask; + } + } + } + + // Walk over all edges and determine which ones are active + for (const EdgeToTriangle::value_type &edge : edge_to_triangle) + { + uint num_active = 0; + if (edge.second.mNumTriangles == 1) + { + // Edge is not shared, it is an active edge + num_active = 1; + } + else if (edge.second.mNumTriangles == 2) + { + // Simple shared edge, determine if edge is active based on the two adjacent triangles + const IndexedTriangle &triangle1 = ioIndices[edge.second.mTriangleIndices[0]]; + const IndexedTriangle &triangle2 = ioIndices[edge.second.mTriangleIndices[1]]; + + // Find which edge this is for both triangles + uint edge_idx1 = edge.first.GetIndexInTriangle(triangle1); + uint edge_idx2 = edge.first.GetIndexInTriangle(triangle2); + + // Construct a plane for triangle 1 (e1 = edge vertex 1, e2 = edge vertex 2, op = opposing vertex) + Vec3 triangle1_e1 = Vec3(inSettings.mTriangleVertices[triangle1.mIdx[edge_idx1]]); + Vec3 triangle1_e2 = Vec3(inSettings.mTriangleVertices[triangle1.mIdx[(edge_idx1 + 1) % 3]]); + Vec3 triangle1_op = Vec3(inSettings.mTriangleVertices[triangle1.mIdx[(edge_idx1 + 2) % 3]]); + Plane triangle1_plane = Plane::sFromPointsCCW(triangle1_e1, triangle1_e2, triangle1_op); + + // Construct a plane for triangle 2 + Vec3 triangle2_e1 = Vec3(inSettings.mTriangleVertices[triangle2.mIdx[edge_idx2]]); + Vec3 triangle2_e2 = Vec3(inSettings.mTriangleVertices[triangle2.mIdx[(edge_idx2 + 1) % 3]]); + Vec3 triangle2_op = Vec3(inSettings.mTriangleVertices[triangle2.mIdx[(edge_idx2 + 2) % 3]]); + Plane triangle2_plane = Plane::sFromPointsCCW(triangle2_e1, triangle2_e2, triangle2_op); + + // Determine if the edge is active + num_active = ActiveEdges::IsEdgeActive(triangle1_plane.GetNormal(), triangle2_plane.GetNormal(), triangle1_e2 - triangle1_e1, inSettings.mActiveEdgeCosThresholdAngle)? 2 : 0; + } + else + { + // More edges incoming, we've already marked all edges beyond the 2nd as active + num_active = 2; + } + + // Mark edges of all original triangles active + for (uint i = 0; i < num_active; ++i) + { + uint triangle_idx = edge.second.mTriangleIndices[i]; + IndexedTriangle &triangle = ioIndices[triangle_idx]; + uint edge_idx = edge.first.GetIndexInTriangle(triangle); + uint32 mask = 1 << (edge_idx + FLAGS_ACTIVE_EGDE_SHIFT); + JPH_ASSERT((triangle.mMaterialIndex & mask) == 0); + triangle.mMaterialIndex |= mask; + } + } +} + +MassProperties MeshShape::GetMassProperties() const +{ + // Object should always be static, return default mass properties + return MassProperties(); +} + +void MeshShape::DecodeSubShapeID(const SubShapeID &inSubShapeID, const void *&outTriangleBlock, uint32 &outTriangleIndex) const +{ + // Get block + SubShapeID triangle_idx_subshape_id; + uint32 block_id = inSubShapeID.PopID(NodeCodec::DecodingContext::sTriangleBlockIDBits(mTree), triangle_idx_subshape_id); + outTriangleBlock = NodeCodec::DecodingContext::sGetTriangleBlockStart(&mTree[0], block_id); + + // Fetch the triangle index + SubShapeID remainder; + outTriangleIndex = triangle_idx_subshape_id.PopID(NumTriangleBits, remainder); + JPH_ASSERT(remainder.IsEmpty(), "Invalid subshape ID"); +} + +uint MeshShape::GetMaterialIndex(const SubShapeID &inSubShapeID) const +{ + // Decode ID + const void *block_start; + uint32 triangle_idx; + DecodeSubShapeID(inSubShapeID, block_start, triangle_idx); + + // Fetch the flags + uint8 flags = TriangleCodec::DecodingContext::sGetFlags(block_start, triangle_idx); + return flags & FLAGS_MATERIAL_MASK; +} + +const PhysicsMaterial *MeshShape::GetMaterial(const SubShapeID &inSubShapeID) const +{ + // Return the default material if there are no materials on this shape + if (mMaterials.empty()) + return PhysicsMaterial::sDefault; + + return mMaterials[GetMaterialIndex(inSubShapeID)]; +} + +Vec3 MeshShape::GetSurfaceNormal(const SubShapeID &inSubShapeID, Vec3Arg inLocalSurfacePosition) const +{ + // Decode ID + const void *block_start; + uint32 triangle_idx; + DecodeSubShapeID(inSubShapeID, block_start, triangle_idx); + + // Decode triangle + Vec3 v1, v2, v3; + const TriangleCodec::DecodingContext triangle_ctx(sGetTriangleHeader(mTree)); + triangle_ctx.GetTriangle(block_start, triangle_idx, v1, v2, v3); + + // Calculate normal + return (v3 - v2).Cross(v1 - v2).Normalized(); +} + +void MeshShape::GetSupportingFace(const SubShapeID &inSubShapeID, Vec3Arg inDirection, Vec3Arg inScale, Mat44Arg inCenterOfMassTransform, SupportingFace &outVertices) const +{ + // Decode ID + const void *block_start; + uint32 triangle_idx; + DecodeSubShapeID(inSubShapeID, block_start, triangle_idx); + + // Decode triangle + const TriangleCodec::DecodingContext triangle_ctx(sGetTriangleHeader(mTree)); + outVertices.resize(3); + triangle_ctx.GetTriangle(block_start, triangle_idx, outVertices[0], outVertices[1], outVertices[2]); + + // Flip triangle if scaled inside out + if (ScaleHelpers::IsInsideOut(inScale)) + swap(outVertices[1], outVertices[2]); + + // Calculate transform with scale + Mat44 transform = inCenterOfMassTransform.PreScaled(inScale); + + // Transform to world space + for (Vec3 &v : outVertices) + v = transform * v; +} + +AABox MeshShape::GetLocalBounds() const +{ + const NodeCodec::Header *header = sGetNodeHeader(mTree); + return AABox(Vec3::sLoadFloat3Unsafe(header->mRootBoundsMin), Vec3::sLoadFloat3Unsafe(header->mRootBoundsMax)); +} + +uint MeshShape::GetSubShapeIDBitsRecursive() const +{ + return NodeCodec::DecodingContext::sTriangleBlockIDBits(mTree) + NumTriangleBits; +} + +template +JPH_INLINE void MeshShape::WalkTree(Visitor &ioVisitor) const +{ + const NodeCodec::Header *header = sGetNodeHeader(mTree); + NodeCodec::DecodingContext node_ctx(header); + + const TriangleCodec::DecodingContext triangle_ctx(sGetTriangleHeader(mTree)); + const uint8 *buffer_start = &mTree[0]; + node_ctx.WalkTree(buffer_start, triangle_ctx, ioVisitor); +} + +template +JPH_INLINE void MeshShape::WalkTreePerTriangle(const SubShapeIDCreator &inSubShapeIDCreator2, Visitor &ioVisitor) const +{ + struct ChainedVisitor + { + JPH_INLINE ChainedVisitor(Visitor &ioVisitor, const SubShapeIDCreator &inSubShapeIDCreator2, uint inTriangleBlockIDBits) : + mVisitor(ioVisitor), + mSubShapeIDCreator2(inSubShapeIDCreator2), + mTriangleBlockIDBits(inTriangleBlockIDBits) + { + } + + JPH_INLINE bool ShouldAbort() const + { + return mVisitor.ShouldAbort(); + } + + JPH_INLINE bool ShouldVisitNode(int inStackTop) const + { + return mVisitor.ShouldVisitNode(inStackTop); + } + + JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, int inStackTop) + { + return mVisitor.VisitNodes(inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ, ioProperties, inStackTop); + } + + JPH_INLINE void VisitTriangles(const TriangleCodec::DecodingContext &ioContext, const void *inTriangles, int inNumTriangles, uint32 inTriangleBlockID) + { + // Create ID for triangle block + SubShapeIDCreator block_sub_shape_id = mSubShapeIDCreator2.PushID(inTriangleBlockID, mTriangleBlockIDBits); + + // Decode vertices and flags + JPH_ASSERT(inNumTriangles <= MaxTrianglesPerLeaf); + Vec3 vertices[MaxTrianglesPerLeaf * 3]; + uint8 flags[MaxTrianglesPerLeaf]; + ioContext.Unpack(inTriangles, inNumTriangles, vertices, flags); + + int triangle_idx = 0; + for (const Vec3 *v = vertices, *v_end = vertices + inNumTriangles * 3; v < v_end; v += 3, triangle_idx++) + { + // Determine active edges + uint8 active_edges = (flags[triangle_idx] >> FLAGS_ACTIVE_EGDE_SHIFT) & FLAGS_ACTIVE_EDGE_MASK; + + // Create ID for triangle + SubShapeIDCreator triangle_sub_shape_id = block_sub_shape_id.PushID(triangle_idx, NumTriangleBits); + + mVisitor.VisitTriangle(v[0], v[1], v[2], active_edges, triangle_sub_shape_id.GetID()); + + // Check if we should early out now + if (mVisitor.ShouldAbort()) + break; + } + } + + Visitor & mVisitor; + SubShapeIDCreator mSubShapeIDCreator2; + uint mTriangleBlockIDBits; + }; + + ChainedVisitor visitor(ioVisitor, inSubShapeIDCreator2, NodeCodec::DecodingContext::sTriangleBlockIDBits(mTree)); + WalkTree(visitor); +} + +#ifdef JPH_DEBUG_RENDERER +void MeshShape::Draw(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inUseMaterialColors, bool inDrawWireframe) const +{ + // Reset the batch if we switch coloring mode + if (mCachedTrianglesColoredPerGroup != sDrawTriangleGroups || mCachedUseMaterialColors != inUseMaterialColors) + { + mGeometry = nullptr; + mCachedTrianglesColoredPerGroup = sDrawTriangleGroups; + mCachedUseMaterialColors = inUseMaterialColors; + } + + if (mGeometry == nullptr) + { + struct Visitor + { + JPH_INLINE bool ShouldAbort() const + { + return false; + } + + JPH_INLINE bool ShouldVisitNode(int inStackTop) const + { + return true; + } + + JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, int inStackTop) + { + UVec4 valid = UVec4::sOr(UVec4::sOr(Vec4::sLess(inBoundsMinX, inBoundsMaxX), Vec4::sLess(inBoundsMinY, inBoundsMaxY)), Vec4::sLess(inBoundsMinZ, inBoundsMaxZ)); + return CountAndSortTrues(valid, ioProperties); + } + + JPH_INLINE void VisitTriangles(const TriangleCodec::DecodingContext &ioContext, const void *inTriangles, int inNumTriangles, [[maybe_unused]] uint32 inTriangleBlockID) + { + JPH_ASSERT(inNumTriangles <= MaxTrianglesPerLeaf); + Vec3 vertices[MaxTrianglesPerLeaf * 3]; + ioContext.Unpack(inTriangles, inNumTriangles, vertices); + + if (mDrawTriangleGroups || !mUseMaterialColors || mMaterials.empty()) + { + // Single color for mesh + Color color = mDrawTriangleGroups? Color::sGetDistinctColor(mColorIdx++) : (mUseMaterialColors? PhysicsMaterial::sDefault->GetDebugColor() : Color::sWhite); + for (const Vec3 *v = vertices, *v_end = vertices + inNumTriangles * 3; v < v_end; v += 3) + mTriangles.push_back({ v[0], v[1], v[2], color }); + } + else + { + // Per triangle color + uint8 flags[MaxTrianglesPerLeaf]; + TriangleCodec::DecodingContext::sGetFlags(inTriangles, inNumTriangles, flags); + + const uint8 *f = flags; + for (const Vec3 *v = vertices, *v_end = vertices + inNumTriangles * 3; v < v_end; v += 3, f++) + mTriangles.push_back({ v[0], v[1], v[2], mMaterials[*f & FLAGS_MATERIAL_MASK]->GetDebugColor() }); + } + } + + Array & mTriangles; + const PhysicsMaterialList & mMaterials; + bool mUseMaterialColors; + bool mDrawTriangleGroups; + int mColorIdx = 0; + }; + + Array triangles; + Visitor visitor { triangles, mMaterials, mCachedUseMaterialColors, mCachedTrianglesColoredPerGroup }; + WalkTree(visitor); + mGeometry = new DebugRenderer::Geometry(inRenderer->CreateTriangleBatch(triangles), GetLocalBounds()); + } + + // Test if the shape is scaled inside out + DebugRenderer::ECullMode cull_mode = ScaleHelpers::IsInsideOut(inScale)? DebugRenderer::ECullMode::CullFrontFace : DebugRenderer::ECullMode::CullBackFace; + + // Determine the draw mode + DebugRenderer::EDrawMode draw_mode = inDrawWireframe? DebugRenderer::EDrawMode::Wireframe : DebugRenderer::EDrawMode::Solid; + + // Draw the geometry + inRenderer->DrawGeometry(inCenterOfMassTransform * Mat44::sScale(inScale), inColor, mGeometry, cull_mode, DebugRenderer::ECastShadow::On, draw_mode); + + if (sDrawTriangleOutlines) + { + struct Visitor + { + JPH_INLINE Visitor(DebugRenderer *inRenderer, RMat44Arg inTransform) : + mRenderer(inRenderer), + mTransform(inTransform) + { + } + + JPH_INLINE bool ShouldAbort() const + { + return false; + } + + JPH_INLINE bool ShouldVisitNode(int inStackTop) const + { + return true; + } + + JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, int inStackTop) + { + UVec4 valid = UVec4::sOr(UVec4::sOr(Vec4::sLess(inBoundsMinX, inBoundsMaxX), Vec4::sLess(inBoundsMinY, inBoundsMaxY)), Vec4::sLess(inBoundsMinZ, inBoundsMaxZ)); + return CountAndSortTrues(valid, ioProperties); + } + + JPH_INLINE void VisitTriangles(const TriangleCodec::DecodingContext &ioContext, const void *inTriangles, int inNumTriangles, uint32 inTriangleBlockID) + { + // Decode vertices and flags + JPH_ASSERT(inNumTriangles <= MaxTrianglesPerLeaf); + Vec3 vertices[MaxTrianglesPerLeaf * 3]; + uint8 flags[MaxTrianglesPerLeaf]; + ioContext.Unpack(inTriangles, inNumTriangles, vertices, flags); + + // Loop through triangles + const uint8 *f = flags; + for (Vec3 *v = vertices, *v_end = vertices + inNumTriangles * 3; v < v_end; v += 3, ++f) + { + // Loop through edges + for (uint edge_idx = 0; edge_idx < 3; ++edge_idx) + { + RVec3 v1 = mTransform * v[edge_idx]; + RVec3 v2 = mTransform * v[(edge_idx + 1) % 3]; + + // Draw active edge as a green arrow, other edges as grey + if (*f & (1 << (edge_idx + FLAGS_ACTIVE_EGDE_SHIFT))) + mRenderer->DrawArrow(v1, v2, Color::sGreen, 0.01f); + else + mRenderer->DrawLine(v1, v2, Color::sGrey); + } + } + } + + DebugRenderer * mRenderer; + RMat44 mTransform; + }; + + Visitor visitor { inRenderer, inCenterOfMassTransform.PreScaled(inScale) }; + WalkTree(visitor); + } +} +#endif // JPH_DEBUG_RENDERER + +bool MeshShape::CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const +{ + JPH_PROFILE_FUNCTION(); + + struct Visitor + { + JPH_INLINE explicit Visitor(RayCastResult &ioHit) : + mHit(ioHit) + { + } + + JPH_INLINE bool ShouldAbort() const + { + return mHit.mFraction <= 0.0f; + } + + JPH_INLINE bool ShouldVisitNode(int inStackTop) const + { + return mDistanceStack[inStackTop] < mHit.mFraction; + } + + JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, int inStackTop) + { + // Test bounds of 4 children + Vec4 distance = RayAABox4(mRayOrigin, mRayInvDirection, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ); + + // Sort so that highest values are first (we want to first process closer hits and we process stack top to bottom) + return SortReverseAndStore(distance, mHit.mFraction, ioProperties, &mDistanceStack[inStackTop]); + } + + JPH_INLINE void VisitTriangles(const TriangleCodec::DecodingContext &ioContext, const void *inTriangles, int inNumTriangles, uint32 inTriangleBlockID) + { + // Test against triangles + uint32 triangle_idx; + float fraction = ioContext.TestRay(mRayOrigin, mRayDirection, inTriangles, inNumTriangles, mHit.mFraction, triangle_idx); + if (fraction < mHit.mFraction) + { + mHit.mFraction = fraction; + mHit.mSubShapeID2 = mSubShapeIDCreator.PushID(inTriangleBlockID, mTriangleBlockIDBits).PushID(triangle_idx, NumTriangleBits).GetID(); + mReturnValue = true; + } + } + + RayCastResult & mHit; + Vec3 mRayOrigin; + Vec3 mRayDirection; + RayInvDirection mRayInvDirection; + uint mTriangleBlockIDBits; + SubShapeIDCreator mSubShapeIDCreator; + bool mReturnValue = false; + float mDistanceStack[NodeCodec::StackSize]; + }; + + Visitor visitor(ioHit); + visitor.mRayOrigin = inRay.mOrigin; + visitor.mRayDirection = inRay.mDirection; + visitor.mRayInvDirection.Set(inRay.mDirection); + visitor.mTriangleBlockIDBits = NodeCodec::DecodingContext::sTriangleBlockIDBits(mTree); + visitor.mSubShapeIDCreator = inSubShapeIDCreator; + WalkTree(visitor); + + return visitor.mReturnValue; +} + +void MeshShape::CastRay(const RayCast &inRay, const RayCastSettings &inRayCastSettings, const SubShapeIDCreator &inSubShapeIDCreator, CastRayCollector &ioCollector, const ShapeFilter &inShapeFilter) const +{ + JPH_PROFILE_FUNCTION(); + + // Test shape filter + if (!inShapeFilter.ShouldCollide(this, inSubShapeIDCreator.GetID())) + return; + + struct Visitor + { + JPH_INLINE explicit Visitor(CastRayCollector &ioCollector) : + mCollector(ioCollector) + { + } + + JPH_INLINE bool ShouldAbort() const + { + return mCollector.ShouldEarlyOut(); + } + + JPH_INLINE bool ShouldVisitNode(int inStackTop) const + { + return mDistanceStack[inStackTop] < mCollector.GetEarlyOutFraction(); + } + + JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, int inStackTop) + { + // Test bounds of 4 children + Vec4 distance = RayAABox4(mRayOrigin, mRayInvDirection, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ); + + // Sort so that highest values are first (we want to first process closer hits and we process stack top to bottom) + return SortReverseAndStore(distance, mCollector.GetEarlyOutFraction(), ioProperties, &mDistanceStack[inStackTop]); + } + + JPH_INLINE void VisitTriangle(Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2, [[maybe_unused]] uint8 inActiveEdges, SubShapeID inSubShapeID2) + { + // Back facing check + if (mBackFaceMode == EBackFaceMode::IgnoreBackFaces && (inV2 - inV0).Cross(inV1 - inV0).Dot(mRayDirection) < 0) + return; + + // Check the triangle + float fraction = RayTriangle(mRayOrigin, mRayDirection, inV0, inV1, inV2); + if (fraction < mCollector.GetEarlyOutFraction()) + { + RayCastResult hit; + hit.mBodyID = TransformedShape::sGetBodyID(mCollector.GetContext()); + hit.mFraction = fraction; + hit.mSubShapeID2 = inSubShapeID2; + mCollector.AddHit(hit); + } + } + + CastRayCollector & mCollector; + Vec3 mRayOrigin; + Vec3 mRayDirection; + RayInvDirection mRayInvDirection; + EBackFaceMode mBackFaceMode; + float mDistanceStack[NodeCodec::StackSize]; + }; + + Visitor visitor(ioCollector); + visitor.mBackFaceMode = inRayCastSettings.mBackFaceMode; + visitor.mRayOrigin = inRay.mOrigin; + visitor.mRayDirection = inRay.mDirection; + visitor.mRayInvDirection.Set(inRay.mDirection); + WalkTreePerTriangle(inSubShapeIDCreator, visitor); +} + +void MeshShape::CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter) const +{ + sCollidePointUsingRayCast(*this, inPoint, inSubShapeIDCreator, ioCollector, inShapeFilter); +} + +void MeshShape::CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, SoftBodyVertex *ioVertices, uint inNumVertices, [[maybe_unused]] float inDeltaTime, [[maybe_unused]] Vec3Arg inDisplacementDueToGravity, int inCollidingShapeIndex) const +{ + JPH_PROFILE_FUNCTION(); + + struct Visitor : public CollideSoftBodyVerticesVsTriangles + { + using CollideSoftBodyVerticesVsTriangles::CollideSoftBodyVerticesVsTriangles; + + JPH_INLINE bool ShouldAbort() const + { + return false; + } + + JPH_INLINE bool ShouldVisitNode([[maybe_unused]] int inStackTop) const + { + return mDistanceStack[inStackTop] < mClosestDistanceSq; + } + + JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, int inStackTop) + { + // Get distance to vertex + Vec4 dist_sq = AABox4DistanceSqToPoint(mLocalPosition, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ); + + // Sort so that highest values are first (we want to first process closer hits and we process stack top to bottom) + return SortReverseAndStore(dist_sq, mClosestDistanceSq, ioProperties, &mDistanceStack[inStackTop]); + } + + JPH_INLINE void VisitTriangle(Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2, [[maybe_unused]] uint8 inActiveEdges, [[maybe_unused]] SubShapeID inSubShapeID2) + { + ProcessTriangle(inV0, inV1, inV2); + } + + float mDistanceStack[NodeCodec::StackSize]; + }; + + Visitor visitor(inCenterOfMassTransform, inScale); + + for (SoftBodyVertex *v = ioVertices, *sbv_end = ioVertices + inNumVertices; v < sbv_end; ++v) + if (v->mInvMass > 0.0f) + { + visitor.StartVertex(*v); + WalkTreePerTriangle(SubShapeIDCreator(), visitor); + visitor.FinishVertex(*v, inCollidingShapeIndex); + } +} + +void MeshShape::sCastConvexVsMesh(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, [[maybe_unused]] const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector) +{ + JPH_PROFILE_FUNCTION(); + + struct Visitor : public CastConvexVsTriangles + { + using CastConvexVsTriangles::CastConvexVsTriangles; + + JPH_INLINE bool ShouldAbort() const + { + return mCollector.ShouldEarlyOut(); + } + + JPH_INLINE bool ShouldVisitNode(int inStackTop) const + { + return mDistanceStack[inStackTop] < mCollector.GetPositiveEarlyOutFraction(); + } + + JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, int inStackTop) + { + // Scale the bounding boxes of this node + Vec4 bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z; + AABox4Scale(mScale, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z); + + // Enlarge them by the casted shape's box extents + AABox4EnlargeWithExtent(mBoxExtent, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z); + + // Test bounds of 4 children + Vec4 distance = RayAABox4(mBoxCenter, mInvDirection, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z); + + // Sort so that highest values are first (we want to first process closer hits and we process stack top to bottom) + return SortReverseAndStore(distance, mCollector.GetPositiveEarlyOutFraction(), ioProperties, &mDistanceStack[inStackTop]); + } + + JPH_INLINE void VisitTriangle(Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2, uint8 inActiveEdges, SubShapeID inSubShapeID2) + { + Cast(inV0, inV1, inV2, inActiveEdges, inSubShapeID2); + } + + RayInvDirection mInvDirection; + Vec3 mBoxCenter; + Vec3 mBoxExtent; + float mDistanceStack[NodeCodec::StackSize]; + }; + + JPH_ASSERT(inShape->GetSubType() == EShapeSubType::Mesh); + const MeshShape *shape = static_cast(inShape); + + Visitor visitor(inShapeCast, inShapeCastSettings, inScale, inCenterOfMassTransform2, inSubShapeIDCreator1, ioCollector); + visitor.mInvDirection.Set(inShapeCast.mDirection); + visitor.mBoxCenter = inShapeCast.mShapeWorldBounds.GetCenter(); + visitor.mBoxExtent = inShapeCast.mShapeWorldBounds.GetExtent(); + shape->WalkTreePerTriangle(inSubShapeIDCreator2, visitor); +} + +void MeshShape::sCastSphereVsMesh(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, [[maybe_unused]] const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector) +{ + JPH_PROFILE_FUNCTION(); + + struct Visitor : public CastSphereVsTriangles + { + using CastSphereVsTriangles::CastSphereVsTriangles; + + JPH_INLINE bool ShouldAbort() const + { + return mCollector.ShouldEarlyOut(); + } + + JPH_INLINE bool ShouldVisitNode(int inStackTop) const + { + return mDistanceStack[inStackTop] < mCollector.GetPositiveEarlyOutFraction(); + } + + JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, int inStackTop) + { + // Scale the bounding boxes of this node + Vec4 bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z; + AABox4Scale(mScale, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z); + + // Enlarge them by the radius of the sphere + AABox4EnlargeWithExtent(Vec3::sReplicate(mRadius), bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z); + + // Test bounds of 4 children + Vec4 distance = RayAABox4(mStart, mInvDirection, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z); + + // Sort so that highest values are first (we want to first process closer hits and we process stack top to bottom) + return SortReverseAndStore(distance, mCollector.GetPositiveEarlyOutFraction(), ioProperties, &mDistanceStack[inStackTop]); + } + + JPH_INLINE void VisitTriangle(Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2, uint8 inActiveEdges, SubShapeID inSubShapeID2) + { + Cast(inV0, inV1, inV2, inActiveEdges, inSubShapeID2); + } + + RayInvDirection mInvDirection; + float mDistanceStack[NodeCodec::StackSize]; + }; + + JPH_ASSERT(inShape->GetSubType() == EShapeSubType::Mesh); + const MeshShape *shape = static_cast(inShape); + + Visitor visitor(inShapeCast, inShapeCastSettings, inScale, inCenterOfMassTransform2, inSubShapeIDCreator1, ioCollector); + visitor.mInvDirection.Set(inShapeCast.mDirection); + shape->WalkTreePerTriangle(inSubShapeIDCreator2, visitor); +} + +struct MeshShape::MSGetTrianglesContext +{ + JPH_INLINE MSGetTrianglesContext(const MeshShape *inShape, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) : + mDecodeCtx(sGetNodeHeader(inShape->mTree)), + mShape(inShape), + mLocalBox(Mat44::sInverseRotationTranslation(inRotation, inPositionCOM), inBox), + mMeshScale(inScale), + mLocalToWorld(Mat44::sRotationTranslation(inRotation, inPositionCOM) * Mat44::sScale(inScale)), + mIsInsideOut(ScaleHelpers::IsInsideOut(inScale)) + { + } + + JPH_INLINE bool ShouldAbort() const + { + return mShouldAbort; + } + + JPH_INLINE bool ShouldVisitNode([[maybe_unused]] int inStackTop) const + { + return true; + } + + JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, [[maybe_unused]] int inStackTop) const + { + // Scale the bounding boxes of this node + Vec4 bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z; + AABox4Scale(mMeshScale, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z); + + // Test which nodes collide + UVec4 collides = AABox4VsBox(mLocalBox, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z); + return CountAndSortTrues(collides, ioProperties); + } + + JPH_INLINE void VisitTriangles(const TriangleCodec::DecodingContext &ioContext, const void *inTriangles, int inNumTriangles, [[maybe_unused]] uint32 inTriangleBlockID) + { + // When the buffer is full and we cannot process the triangles, abort the tree walk. The next time GetTrianglesNext is called we will continue here. + if (mNumTrianglesFound + inNumTriangles > mMaxTrianglesRequested) + { + mShouldAbort = true; + return; + } + + // Decode vertices + JPH_ASSERT(inNumTriangles <= MaxTrianglesPerLeaf); + Vec3 vertices[MaxTrianglesPerLeaf * 3]; + ioContext.Unpack(inTriangles, inNumTriangles, vertices); + + // Store vertices as Float3 + if (mIsInsideOut) + { + // Scaled inside out, flip the triangles + for (const Vec3 *v = vertices, *v_end = v + 3 * inNumTriangles; v < v_end; v += 3) + { + (mLocalToWorld * v[0]).StoreFloat3(mTriangleVertices++); + (mLocalToWorld * v[2]).StoreFloat3(mTriangleVertices++); + (mLocalToWorld * v[1]).StoreFloat3(mTriangleVertices++); + } + } + else + { + // Normal scale + for (const Vec3 *v = vertices, *v_end = v + 3 * inNumTriangles; v < v_end; ++v) + (mLocalToWorld * *v).StoreFloat3(mTriangleVertices++); + } + + if (mMaterials != nullptr) + { + if (mShape->mMaterials.empty()) + { + // No materials, output default + const PhysicsMaterial *default_material = PhysicsMaterial::sDefault; + for (int m = 0; m < inNumTriangles; ++m) + *mMaterials++ = default_material; + } + else + { + // Decode triangle flags + uint8 flags[MaxTrianglesPerLeaf]; + TriangleCodec::DecodingContext::sGetFlags(inTriangles, inNumTriangles, flags); + + // Store materials + for (const uint8 *f = flags, *f_end = f + inNumTriangles; f < f_end; ++f) + *mMaterials++ = mShape->mMaterials[*f & FLAGS_MATERIAL_MASK].GetPtr(); + } + } + + // Accumulate triangles found + mNumTrianglesFound += inNumTriangles; + } + + NodeCodec::DecodingContext mDecodeCtx; + const MeshShape * mShape; + OrientedBox mLocalBox; + Vec3 mMeshScale; + Mat44 mLocalToWorld; + int mMaxTrianglesRequested; + Float3 * mTriangleVertices; + int mNumTrianglesFound; + const PhysicsMaterial ** mMaterials; + bool mShouldAbort; + bool mIsInsideOut; +}; + +void MeshShape::GetTrianglesStart(GetTrianglesContext &ioContext, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) const +{ + static_assert(sizeof(MSGetTrianglesContext) <= sizeof(GetTrianglesContext), "GetTrianglesContext too small"); + JPH_ASSERT(IsAligned(&ioContext, alignof(MSGetTrianglesContext))); + + new (&ioContext) MSGetTrianglesContext(this, inBox, inPositionCOM, inRotation, inScale); +} + +int MeshShape::GetTrianglesNext(GetTrianglesContext &ioContext, int inMaxTrianglesRequested, Float3 *outTriangleVertices, const PhysicsMaterial **outMaterials) const +{ + static_assert(cGetTrianglesMinTrianglesRequested >= MaxTrianglesPerLeaf, "cGetTrianglesMinTrianglesRequested is too small"); + JPH_ASSERT(inMaxTrianglesRequested >= cGetTrianglesMinTrianglesRequested); + + // Check if we're done + MSGetTrianglesContext &context = (MSGetTrianglesContext &)ioContext; + if (context.mDecodeCtx.IsDoneWalking()) + return 0; + + // Store parameters on context + context.mMaxTrianglesRequested = inMaxTrianglesRequested; + context.mTriangleVertices = outTriangleVertices; + context.mMaterials = outMaterials; + context.mShouldAbort = false; // Reset the abort flag + context.mNumTrianglesFound = 0; + + // Continue (or start) walking the tree + const TriangleCodec::DecodingContext triangle_ctx(sGetTriangleHeader(mTree)); + const uint8 *buffer_start = &mTree[0]; + context.mDecodeCtx.WalkTree(buffer_start, triangle_ctx, context); + return context.mNumTrianglesFound; +} + +void MeshShape::sCollideConvexVsMesh(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, [[maybe_unused]] const ShapeFilter &inShapeFilter) +{ + JPH_PROFILE_FUNCTION(); + + // Get the shapes + JPH_ASSERT(inShape1->GetType() == EShapeType::Convex); + JPH_ASSERT(inShape2->GetType() == EShapeType::Mesh); + const ConvexShape *shape1 = static_cast(inShape1); + const MeshShape *shape2 = static_cast(inShape2); + + struct Visitor : public CollideConvexVsTriangles + { + using CollideConvexVsTriangles::CollideConvexVsTriangles; + + JPH_INLINE bool ShouldAbort() const + { + return mCollector.ShouldEarlyOut(); + } + + JPH_INLINE bool ShouldVisitNode([[maybe_unused]] int inStackTop) const + { + return true; + } + + JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, [[maybe_unused]] int inStackTop) const + { + // Scale the bounding boxes of this node + Vec4 bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z; + AABox4Scale(mScale2, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z); + + // Test which nodes collide + UVec4 collides = AABox4VsBox(mBoundsOf1InSpaceOf2, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z); + return CountAndSortTrues(collides, ioProperties); + } + + JPH_INLINE void VisitTriangle(Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2, uint8 inActiveEdges, SubShapeID inSubShapeID2) + { + Collide(inV0, inV1, inV2, inActiveEdges, inSubShapeID2); + } + }; + + Visitor visitor(shape1, inScale1, inScale2, inCenterOfMassTransform1, inCenterOfMassTransform2, inSubShapeIDCreator1.GetID(), inCollideShapeSettings, ioCollector); + shape2->WalkTreePerTriangle(inSubShapeIDCreator2, visitor); +} + +void MeshShape::sCollideSphereVsMesh(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, [[maybe_unused]] const ShapeFilter &inShapeFilter) +{ + JPH_PROFILE_FUNCTION(); + + // Get the shapes + JPH_ASSERT(inShape1->GetSubType() == EShapeSubType::Sphere); + JPH_ASSERT(inShape2->GetType() == EShapeType::Mesh); + const SphereShape *shape1 = static_cast(inShape1); + const MeshShape *shape2 = static_cast(inShape2); + + struct Visitor : public CollideSphereVsTriangles + { + using CollideSphereVsTriangles::CollideSphereVsTriangles; + + JPH_INLINE bool ShouldAbort() const + { + return mCollector.ShouldEarlyOut(); + } + + JPH_INLINE bool ShouldVisitNode([[maybe_unused]] int inStackTop) const + { + return true; + } + + JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, [[maybe_unused]] int inStackTop) const + { + // Scale the bounding boxes of this node + Vec4 bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z; + AABox4Scale(mScale2, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z); + + // Test which nodes collide + UVec4 collides = AABox4VsSphere(mSphereCenterIn2, mRadiusPlusMaxSeparationSq, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z); + return CountAndSortTrues(collides, ioProperties); + } + + JPH_INLINE void VisitTriangle(Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2, uint8 inActiveEdges, SubShapeID inSubShapeID2) + { + Collide(inV0, inV1, inV2, inActiveEdges, inSubShapeID2); + } + }; + + Visitor visitor(shape1, inScale1, inScale2, inCenterOfMassTransform1, inCenterOfMassTransform2, inSubShapeIDCreator1.GetID(), inCollideShapeSettings, ioCollector); + shape2->WalkTreePerTriangle(inSubShapeIDCreator2, visitor); +} + +void MeshShape::SaveBinaryState(StreamOut &inStream) const +{ + Shape::SaveBinaryState(inStream); + + inStream.Write(static_cast(mTree)); // Make sure we use the Array<> overload +} + +void MeshShape::RestoreBinaryState(StreamIn &inStream) +{ + Shape::RestoreBinaryState(inStream); + + inStream.Read(static_cast(mTree)); // Make sure we use the Array<> overload +} + +void MeshShape::SaveMaterialState(PhysicsMaterialList &outMaterials) const +{ + outMaterials = mMaterials; +} + +void MeshShape::RestoreMaterialState(const PhysicsMaterialRefC *inMaterials, uint inNumMaterials) +{ + mMaterials.assign(inMaterials, inMaterials + inNumMaterials); +} + +Shape::Stats MeshShape::GetStats() const +{ + // Walk the tree to count the triangles + struct Visitor + { + JPH_INLINE bool ShouldAbort() const + { + return false; + } + + JPH_INLINE bool ShouldVisitNode([[maybe_unused]] int inStackTop) const + { + return true; + } + + JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, [[maybe_unused]] int inStackTop) const + { + // Visit all valid children + UVec4 valid = UVec4::sOr(UVec4::sOr(Vec4::sLess(inBoundsMinX, inBoundsMaxX), Vec4::sLess(inBoundsMinY, inBoundsMaxY)), Vec4::sLess(inBoundsMinZ, inBoundsMaxZ)); + return CountAndSortTrues(valid, ioProperties); + } + + JPH_INLINE void VisitTriangles([[maybe_unused]] const TriangleCodec::DecodingContext &ioContext, [[maybe_unused]] const void *inTriangles, int inNumTriangles, [[maybe_unused]] uint32 inTriangleBlockID) + { + mNumTriangles += inNumTriangles; + } + + uint mNumTriangles = 0; + }; + + Visitor visitor; + WalkTree(visitor); + + return Stats(sizeof(*this) + mMaterials.size() * sizeof(Ref) + mTree.size() * sizeof(uint8), visitor.mNumTriangles); +} + +void MeshShape::sRegister() +{ + ShapeFunctions &f = ShapeFunctions::sGet(EShapeSubType::Mesh); + f.mConstruct = []() -> Shape * { return new MeshShape; }; + f.mColor = Color::sRed; + + for (EShapeSubType s : sConvexSubShapeTypes) + { + CollisionDispatch::sRegisterCollideShape(s, EShapeSubType::Mesh, sCollideConvexVsMesh); + CollisionDispatch::sRegisterCastShape(s, EShapeSubType::Mesh, sCastConvexVsMesh); + + CollisionDispatch::sRegisterCastShape(EShapeSubType::Mesh, s, CollisionDispatch::sReversedCastShape); + CollisionDispatch::sRegisterCollideShape(EShapeSubType::Mesh, s, CollisionDispatch::sReversedCollideShape); + } + + // Specialized collision functions + CollisionDispatch::sRegisterCollideShape(EShapeSubType::Sphere, EShapeSubType::Mesh, sCollideSphereVsMesh); + CollisionDispatch::sRegisterCastShape(EShapeSubType::Sphere, EShapeSubType::Mesh, sCastSphereVsMesh); +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/MeshShape.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/MeshShape.h new file mode 100644 index 00000000000..0483252345e --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/MeshShape.h @@ -0,0 +1,207 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include +#include +#include +#ifdef JPH_DEBUG_RENDERER + #include +#endif // JPH_DEBUG_RENDERER + +JPH_NAMESPACE_BEGIN + +class ConvexShape; +class CollideShapeSettings; + +/// Class that constructs a MeshShape +class JPH_EXPORT MeshShapeSettings final : public ShapeSettings +{ +public: + JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, MeshShapeSettings) + + /// Default constructor for deserialization + MeshShapeSettings() = default; + + /// Create a mesh shape. + MeshShapeSettings(const TriangleList &inTriangles, PhysicsMaterialList inMaterials = PhysicsMaterialList()); + MeshShapeSettings(VertexList inVertices, IndexedTriangleList inTriangles, PhysicsMaterialList inMaterials = PhysicsMaterialList()); + + /// Sanitize the mesh data. Remove duplicate and degenerate triangles. This is called automatically when constructing the MeshShapeSettings with a list of (indexed-) triangles. + void Sanitize(); + + // See: ShapeSettings + virtual ShapeResult Create() const override; + + /// Vertices belonging to mIndexedTriangles + VertexList mTriangleVertices; + + /// Original list of indexed triangles (triangles will be reordered internally in the mesh shape). + /// Triangles must be provided in counter clockwise order. + /// For simulation, the triangles are considered to be single sided. + /// For ray casts you can choose to make triangles double sided by setting RayCastSettings::mBackFaceMode to EBackFaceMode::CollideWithBackFaces. + /// For collide shape tests you can use CollideShapeSettings::mBackFaceMode and for shape casts you can use ShapeCastSettings::mBackFaceModeTriangles. + IndexedTriangleList mIndexedTriangles; + + /// Materials assigned to the triangles. Each triangle specifies which material it uses through its mMaterialIndex + PhysicsMaterialList mMaterials; + + /// Maximum number of triangles in each leaf of the axis aligned box tree. This is a balance between memory and performance. Can be in the range [1, MeshShape::MaxTrianglesPerLeaf]. + /// Sensible values are between 4 (for better performance) and 8 (for less memory usage). + uint mMaxTrianglesPerLeaf = 8; + + /// Cosine of the threshold angle (if the angle between the two triangles is bigger than this, the edge is active, note that a concave edge is always inactive). + /// Setting this value too small can cause ghost collisions with edges, setting it too big can cause depenetration artifacts (objects not depenetrating quickly). + /// Valid ranges are between cos(0 degrees) and cos(90 degrees). The default value is cos(5 degrees). + float mActiveEdgeCosThresholdAngle = 0.996195f; // cos(5 degrees) +}; + +/// A mesh shape, consisting of triangles. Mesh shapes are mostly used for static geometry. +/// They can be used by dynamic or kinematic objects but only if they don't collide with other mesh or heightfield shapes as those collisions are currently not supported. +/// Note that if you make a mesh shape a dynamic or kinematic object, you need to provide a mass yourself as mesh shapes don't need to form a closed hull so don't have a well defined volume from which the mass can be calculated. +class JPH_EXPORT MeshShape final : public Shape +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Constructor + MeshShape() : Shape(EShapeType::Mesh, EShapeSubType::Mesh) { } + MeshShape(const MeshShapeSettings &inSettings, ShapeResult &outResult); + + // See Shape::MustBeStatic + virtual bool MustBeStatic() const override { return true; } + + // See Shape::GetLocalBounds + virtual AABox GetLocalBounds() const override; + + // See Shape::GetSubShapeIDBitsRecursive + virtual uint GetSubShapeIDBitsRecursive() const override; + + // See Shape::GetInnerRadius + virtual float GetInnerRadius() const override { return 0.0f; } + + // See Shape::GetMassProperties + virtual MassProperties GetMassProperties() const override; + + // See Shape::GetMaterial + virtual const PhysicsMaterial * GetMaterial(const SubShapeID &inSubShapeID) const override; + + /// Get the list of all materials + const PhysicsMaterialList & GetMaterialList() const { return mMaterials; } + + /// Determine which material index a particular sub shape uses (note that if there are no materials this function will return 0 so check the array size) + /// Note: This could for example be used to create a decorator shape around a mesh shape that overrides the GetMaterial call to replace a material with another material. + uint GetMaterialIndex(const SubShapeID &inSubShapeID) const; + + // See Shape::GetSurfaceNormal + virtual Vec3 GetSurfaceNormal(const SubShapeID &inSubShapeID, Vec3Arg inLocalSurfacePosition) const override; + + // See Shape::GetSupportingFace + virtual void GetSupportingFace(const SubShapeID &inSubShapeID, Vec3Arg inDirection, Vec3Arg inScale, Mat44Arg inCenterOfMassTransform, SupportingFace &outVertices) const override; + +#ifdef JPH_DEBUG_RENDERER + // See Shape::Draw + virtual void Draw(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inUseMaterialColors, bool inDrawWireframe) const override; +#endif // JPH_DEBUG_RENDERER + + // See Shape::CastRay + virtual bool CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const override; + virtual void CastRay(const RayCast &inRay, const RayCastSettings &inRayCastSettings, const SubShapeIDCreator &inSubShapeIDCreator, CastRayCollector &ioCollector, const ShapeFilter &inShapeFilter = { }) const override; + + /// See: Shape::CollidePoint + /// Note that for CollidePoint to work for a mesh shape, the mesh needs to be closed (a manifold) or multiple non-intersecting manifolds. Triangles may be facing the interior of the manifold. + /// Insideness is tested by counting the amount of triangles encountered when casting an infinite ray from inPoint. If the number of hits is odd we're inside, if it's even we're outside. + virtual void CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter = { }) const override; + + // See: Shape::CollideSoftBodyVertices + virtual void CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, SoftBodyVertex *ioVertices, uint inNumVertices, float inDeltaTime, Vec3Arg inDisplacementDueToGravity, int inCollidingShapeIndex) const override; + + // See Shape::GetTrianglesStart + virtual void GetTrianglesStart(GetTrianglesContext &ioContext, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) const override; + + // See Shape::GetTrianglesNext + virtual int GetTrianglesNext(GetTrianglesContext &ioContext, int inMaxTrianglesRequested, Float3 *outTriangleVertices, const PhysicsMaterial **outMaterials = nullptr) const override; + + // See Shape::GetSubmergedVolume + virtual void GetSubmergedVolume(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, const Plane &inSurface, float &outTotalVolume, float &outSubmergedVolume, Vec3 &outCenterOfBuoyancy JPH_IF_DEBUG_RENDERER(, RVec3Arg inBaseOffset)) const override { JPH_ASSERT(false, "Not supported"); } + + // See Shape + virtual void SaveBinaryState(StreamOut &inStream) const override; + virtual void SaveMaterialState(PhysicsMaterialList &outMaterials) const override; + virtual void RestoreMaterialState(const PhysicsMaterialRefC *inMaterials, uint inNumMaterials) override; + + // See Shape::GetStats + virtual Stats GetStats() const override; + + // See Shape::GetVolume + virtual float GetVolume() const override { return 0; } + +#ifdef JPH_DEBUG_RENDERER + // Settings + static bool sDrawTriangleGroups; + static bool sDrawTriangleOutlines; +#endif // JPH_DEBUG_RENDERER + + // Register shape functions with the registry + static void sRegister(); + +protected: + // See: Shape::RestoreBinaryState + virtual void RestoreBinaryState(StreamIn &inStream) override; + +private: + struct MSGetTrianglesContext; ///< Context class for GetTrianglesStart/Next + + static constexpr int NumTriangleBits = 3; ///< How many bits to reserve to encode the triangle index + static constexpr int MaxTrianglesPerLeaf = 1 << NumTriangleBits; ///< Number of triangles that are stored max per leaf aabb node + + /// Find and flag active edges + static void sFindActiveEdges(const MeshShapeSettings &inSettings, IndexedTriangleList &ioIndices); + + /// Visit the entire tree using a visitor pattern + template + void WalkTree(Visitor &ioVisitor) const; + + /// Same as above but with a callback per triangle instead of per block of triangles + template + void WalkTreePerTriangle(const SubShapeIDCreator &inSubShapeIDCreator2, Visitor &ioVisitor) const; + + /// Decode a sub shape ID + inline void DecodeSubShapeID(const SubShapeID &inSubShapeID, const void *&outTriangleBlock, uint32 &outTriangleIndex) const; + + // Helper functions called by CollisionDispatch + static void sCollideConvexVsMesh(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter); + static void sCollideSphereVsMesh(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter); + static void sCastConvexVsMesh(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector); + static void sCastSphereVsMesh(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector); + + /// Materials assigned to the triangles. Each triangle specifies which material it uses through its mMaterialIndex + PhysicsMaterialList mMaterials; + + ByteBuffer mTree; ///< Resulting packed data structure + + /// 8 bit flags stored per triangle + enum ETriangleFlags + { + /// Material index + FLAGS_MATERIAL_BITS = 5, + FLAGS_MATERIAL_MASK = (1 << FLAGS_MATERIAL_BITS) - 1, + + /// Active edge bits + FLAGS_ACTIVE_EGDE_SHIFT = FLAGS_MATERIAL_BITS, + FLAGS_ACTIVE_EDGE_BITS = 3, + FLAGS_ACTIVE_EDGE_MASK = (1 << FLAGS_ACTIVE_EDGE_BITS) - 1 + }; + +#ifdef JPH_DEBUG_RENDERER + mutable DebugRenderer::GeometryRef mGeometry; ///< Debug rendering data + mutable bool mCachedTrianglesColoredPerGroup = false; ///< This is used to regenerate the triangle batch if the drawing settings change + mutable bool mCachedUseMaterialColors = false; ///< This is used to regenerate the triangle batch if the drawing settings change +#endif // JPH_DEBUG_RENDERER +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/MutableCompoundShape.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/MutableCompoundShape.cpp new file mode 100644 index 00000000000..61802e783dc --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/MutableCompoundShape.cpp @@ -0,0 +1,560 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(MutableCompoundShapeSettings) +{ + JPH_ADD_BASE_CLASS(MutableCompoundShapeSettings, CompoundShapeSettings) +} + +ShapeSettings::ShapeResult MutableCompoundShapeSettings::Create() const +{ + // Build a mutable compound shape + if (mCachedResult.IsEmpty()) + Ref shape = new MutableCompoundShape(*this, mCachedResult); + + return mCachedResult; +} + +MutableCompoundShape::MutableCompoundShape(const MutableCompoundShapeSettings &inSettings, ShapeResult &outResult) : + CompoundShape(EShapeSubType::MutableCompound, inSettings, outResult) +{ + mSubShapes.reserve(inSettings.mSubShapes.size()); + for (const CompoundShapeSettings::SubShapeSettings &shape : inSettings.mSubShapes) + { + // Start constructing the runtime sub shape + SubShape out_shape; + if (!out_shape.FromSettings(shape, outResult)) + return; + + mSubShapes.push_back(out_shape); + } + + AdjustCenterOfMass(); + + CalculateSubShapeBounds(0, (uint)mSubShapes.size()); + + // Check if we're not exceeding the amount of sub shape id bits + if (GetSubShapeIDBitsRecursive() > SubShapeID::MaxBits) + { + outResult.SetError("Compound hierarchy is too deep and exceeds the amount of available sub shape ID bits"); + return; + } + + outResult.Set(this); +} + +void MutableCompoundShape::AdjustCenterOfMass() +{ + // First calculate the delta of the center of mass + float mass = 0.0f; + Vec3 center_of_mass = Vec3::sZero(); + for (const CompoundShape::SubShape &sub_shape : mSubShapes) + { + MassProperties child = sub_shape.mShape->GetMassProperties(); + mass += child.mMass; + center_of_mass += sub_shape.GetPositionCOM() * child.mMass; + } + if (mass > 0.0f) + center_of_mass /= mass; + + // Now adjust all shapes to recenter around center of mass + for (CompoundShape::SubShape &sub_shape : mSubShapes) + sub_shape.SetPositionCOM(sub_shape.GetPositionCOM() - center_of_mass); + + // And adjust the center of mass for this shape in the opposite direction + mCenterOfMass += center_of_mass; +} + +void MutableCompoundShape::CalculateLocalBounds() +{ + uint num_blocks = GetNumBlocks(); + if (num_blocks > 0) + { + // Initialize min/max for first block + const Bounds *bounds = mSubShapeBounds.data(); + Vec4 min_x = bounds->mMinX; + Vec4 min_y = bounds->mMinY; + Vec4 min_z = bounds->mMinZ; + Vec4 max_x = bounds->mMaxX; + Vec4 max_y = bounds->mMaxY; + Vec4 max_z = bounds->mMaxZ; + + // Accumulate other blocks + const Bounds *bounds_end = bounds + num_blocks; + for (++bounds; bounds < bounds_end; ++bounds) + { + min_x = Vec4::sMin(min_x, bounds->mMinX); + min_y = Vec4::sMin(min_y, bounds->mMinY); + min_z = Vec4::sMin(min_z, bounds->mMinZ); + max_x = Vec4::sMax(max_x, bounds->mMaxX); + max_y = Vec4::sMax(max_y, bounds->mMaxY); + max_z = Vec4::sMax(max_z, bounds->mMaxZ); + } + + // Calculate resulting bounding box + mLocalBounds.mMin.SetX(min_x.ReduceMin()); + mLocalBounds.mMin.SetY(min_y.ReduceMin()); + mLocalBounds.mMin.SetZ(min_z.ReduceMin()); + mLocalBounds.mMax.SetX(max_x.ReduceMax()); + mLocalBounds.mMax.SetY(max_y.ReduceMax()); + mLocalBounds.mMax.SetZ(max_z.ReduceMax()); + } + else + { + // There are no subshapes, set the bounding box to invalid + mLocalBounds.SetEmpty(); + } + + // Cache the inner radius as it can take a while to recursively iterate over all sub shapes + CalculateInnerRadius(); +} + +void MutableCompoundShape::EnsureSubShapeBoundsCapacity() +{ + // Check if we have enough space + uint new_capacity = ((uint)mSubShapes.size() + 3) >> 2; + if (mSubShapeBounds.size() < new_capacity) + mSubShapeBounds.resize(new_capacity); +} + +void MutableCompoundShape::CalculateSubShapeBounds(uint inStartIdx, uint inNumber) +{ + // Ensure that we have allocated the required space for mSubShapeBounds + EnsureSubShapeBoundsCapacity(); + + // Loop over blocks of 4 sub shapes + for (uint sub_shape_idx_start = inStartIdx & ~uint(3), sub_shape_idx_end = inStartIdx + inNumber; sub_shape_idx_start < sub_shape_idx_end; sub_shape_idx_start += 4) + { + Mat44 bounds_min; + Mat44 bounds_max; + + AABox sub_shape_bounds; + for (uint col = 0; col < 4; ++col) + { + uint sub_shape_idx = sub_shape_idx_start + col; + if (sub_shape_idx < mSubShapes.size()) // else reuse sub_shape_bounds from previous iteration + { + const SubShape &sub_shape = mSubShapes[sub_shape_idx]; + + // Tranform the shape's bounds into our local space + Mat44 transform = Mat44::sRotationTranslation(sub_shape.GetRotation(), sub_shape.GetPositionCOM()); + + // Get the bounding box + sub_shape_bounds = sub_shape.mShape->GetWorldSpaceBounds(transform, Vec3::sReplicate(1.0f)); + } + + // Put the bounds as columns in a matrix + bounds_min.SetColumn3(col, sub_shape_bounds.mMin); + bounds_max.SetColumn3(col, sub_shape_bounds.mMax); + } + + // Transpose to go to strucucture of arrays format + Mat44 bounds_min_t = bounds_min.Transposed(); + Mat44 bounds_max_t = bounds_max.Transposed(); + + // Store in our bounds array + Bounds &bounds = mSubShapeBounds[sub_shape_idx_start >> 2]; + bounds.mMinX = bounds_min_t.GetColumn4(0); + bounds.mMinY = bounds_min_t.GetColumn4(1); + bounds.mMinZ = bounds_min_t.GetColumn4(2); + bounds.mMaxX = bounds_max_t.GetColumn4(0); + bounds.mMaxY = bounds_max_t.GetColumn4(1); + bounds.mMaxZ = bounds_max_t.GetColumn4(2); + } + + CalculateLocalBounds(); +} + +uint MutableCompoundShape::AddShape(Vec3Arg inPosition, QuatArg inRotation, const Shape *inShape, uint32 inUserData) +{ + SubShape sub_shape; + sub_shape.mShape = inShape; + sub_shape.mUserData = inUserData; + sub_shape.SetTransform(inPosition, inRotation, mCenterOfMass); + mSubShapes.push_back(sub_shape); + uint shape_idx = (uint)mSubShapes.size() - 1; + + CalculateSubShapeBounds(shape_idx, 1); + + return shape_idx; +} + +void MutableCompoundShape::RemoveShape(uint inIndex) +{ + mSubShapes.erase(mSubShapes.begin() + inIndex); + + uint num_bounds = (uint)mSubShapes.size() - inIndex; + if (num_bounds > 0) + CalculateSubShapeBounds(inIndex, num_bounds); + else + CalculateLocalBounds(); +} + +void MutableCompoundShape::ModifyShape(uint inIndex, Vec3Arg inPosition, QuatArg inRotation) +{ + SubShape &sub_shape = mSubShapes[inIndex]; + sub_shape.SetTransform(inPosition, inRotation, mCenterOfMass); + + CalculateSubShapeBounds(inIndex, 1); +} + +void MutableCompoundShape::ModifyShape(uint inIndex, Vec3Arg inPosition, QuatArg inRotation, const Shape *inShape) +{ + SubShape &sub_shape = mSubShapes[inIndex]; + sub_shape.mShape = inShape; + sub_shape.SetTransform(inPosition, inRotation, mCenterOfMass); + + CalculateSubShapeBounds(inIndex, 1); +} + +void MutableCompoundShape::ModifyShapes(uint inStartIndex, uint inNumber, const Vec3 *inPositions, const Quat *inRotations, uint inPositionStride, uint inRotationStride) +{ + JPH_ASSERT(inStartIndex + inNumber <= mSubShapes.size()); + + const Vec3 *pos = inPositions; + const Quat *rot = inRotations; + for (SubShape *dest = &mSubShapes[inStartIndex], *dest_end = dest + inNumber; dest < dest_end; ++dest) + { + // Update transform + dest->SetTransform(*pos, *rot, mCenterOfMass); + + // Advance pointer in position / rotation buffer + pos = reinterpret_cast(reinterpret_cast(pos) + inPositionStride); + rot = reinterpret_cast(reinterpret_cast(rot) + inRotationStride); + } + + CalculateSubShapeBounds(inStartIndex, inNumber); +} + +template +inline void MutableCompoundShape::WalkSubShapes(Visitor &ioVisitor) const +{ + // Loop over all blocks of 4 bounding boxes + for (uint block = 0, num_blocks = GetNumBlocks(); block < num_blocks; ++block) + { + // Test the bounding boxes + const Bounds &bounds = mSubShapeBounds[block]; + typename Visitor::Result result = ioVisitor.TestBlock(bounds.mMinX, bounds.mMinY, bounds.mMinZ, bounds.mMaxX, bounds.mMaxY, bounds.mMaxZ); + + // Check if any of the bounding boxes collided + if (ioVisitor.ShouldVisitBlock(result)) + { + // Go through the individual boxes + uint sub_shape_start_idx = block << 2; + for (uint col = 0, max_col = min(4, (uint)mSubShapes.size() - sub_shape_start_idx); col < max_col; ++col) // Don't read beyond the end of the subshapes array + if (ioVisitor.ShouldVisitSubShape(result, col)) // Because the early out fraction can change, we need to retest every shape + { + // Test sub shape + uint sub_shape_idx = sub_shape_start_idx + col; + const SubShape &sub_shape = mSubShapes[sub_shape_idx]; + ioVisitor.VisitShape(sub_shape, sub_shape_idx); + + // If no better collision is available abort + if (ioVisitor.ShouldAbort()) + break; + } + } + } +} + +bool MutableCompoundShape::CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const +{ + JPH_PROFILE_FUNCTION(); + + struct Visitor : public CastRayVisitor + { + using CastRayVisitor::CastRayVisitor; + + using Result = Vec4; + + JPH_INLINE Result TestBlock(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ) const + { + return TestBounds(inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ); + } + + JPH_INLINE bool ShouldVisitBlock(Vec4Arg inResult) const + { + UVec4 closer = Vec4::sLess(inResult, Vec4::sReplicate(mHit.mFraction)); + return closer.TestAnyTrue(); + } + + JPH_INLINE bool ShouldVisitSubShape(Vec4Arg inResult, uint inIndexInBlock) const + { + return inResult[inIndexInBlock] < mHit.mFraction; + } + }; + + Visitor visitor(inRay, this, inSubShapeIDCreator, ioHit); + WalkSubShapes(visitor); + return visitor.mReturnValue; +} + +void MutableCompoundShape::CastRay(const RayCast &inRay, const RayCastSettings &inRayCastSettings, const SubShapeIDCreator &inSubShapeIDCreator, CastRayCollector &ioCollector, const ShapeFilter &inShapeFilter) const +{ + JPH_PROFILE_FUNCTION(); + + // Test shape filter + if (!inShapeFilter.ShouldCollide(this, inSubShapeIDCreator.GetID())) + return; + + struct Visitor : public CastRayVisitorCollector + { + using CastRayVisitorCollector::CastRayVisitorCollector; + + using Result = Vec4; + + JPH_INLINE Result TestBlock(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ) const + { + return TestBounds(inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ); + } + + JPH_INLINE bool ShouldVisitBlock(Vec4Arg inResult) const + { + UVec4 closer = Vec4::sLess(inResult, Vec4::sReplicate(mCollector.GetEarlyOutFraction())); + return closer.TestAnyTrue(); + } + + JPH_INLINE bool ShouldVisitSubShape(Vec4Arg inResult, uint inIndexInBlock) const + { + return inResult[inIndexInBlock] < mCollector.GetEarlyOutFraction(); + } + }; + + Visitor visitor(inRay, inRayCastSettings, this, inSubShapeIDCreator, ioCollector, inShapeFilter); + WalkSubShapes(visitor); +} + +void MutableCompoundShape::CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter) const +{ + JPH_PROFILE_FUNCTION(); + + // Test shape filter + if (!inShapeFilter.ShouldCollide(this, inSubShapeIDCreator.GetID())) + return; + + struct Visitor : public CollidePointVisitor + { + using CollidePointVisitor::CollidePointVisitor; + + using Result = UVec4; + + JPH_INLINE Result TestBlock(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ) const + { + return TestBounds(inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ); + } + + JPH_INLINE bool ShouldVisitBlock(UVec4Arg inResult) const + { + return inResult.TestAnyTrue(); + } + + JPH_INLINE bool ShouldVisitSubShape(UVec4Arg inResult, uint inIndexInBlock) const + { + return inResult[inIndexInBlock] != 0; + } + }; + + Visitor visitor(inPoint, this, inSubShapeIDCreator, ioCollector, inShapeFilter); + WalkSubShapes(visitor); +} + +void MutableCompoundShape::sCastShapeVsCompound(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector) +{ + JPH_PROFILE_FUNCTION(); + + struct Visitor : public CastShapeVisitor + { + using CastShapeVisitor::CastShapeVisitor; + + using Result = Vec4; + + JPH_INLINE Result TestBlock(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ) const + { + return TestBounds(inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ); + } + + JPH_INLINE bool ShouldVisitBlock(Vec4Arg inResult) const + { + UVec4 closer = Vec4::sLess(inResult, Vec4::sReplicate(mCollector.GetPositiveEarlyOutFraction())); + return closer.TestAnyTrue(); + } + + JPH_INLINE bool ShouldVisitSubShape(Vec4Arg inResult, uint inIndexInBlock) const + { + return inResult[inIndexInBlock] < mCollector.GetPositiveEarlyOutFraction(); + } + }; + + JPH_ASSERT(inShape->GetSubType() == EShapeSubType::MutableCompound); + const MutableCompoundShape *shape = static_cast(inShape); + + Visitor visitor(inShapeCast, inShapeCastSettings, shape, inScale, inShapeFilter, inCenterOfMassTransform2, inSubShapeIDCreator1, inSubShapeIDCreator2, ioCollector); + shape->WalkSubShapes(visitor); +} + +void MutableCompoundShape::CollectTransformedShapes(const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale, const SubShapeIDCreator &inSubShapeIDCreator, TransformedShapeCollector &ioCollector, const ShapeFilter &inShapeFilter) const +{ + JPH_PROFILE_FUNCTION(); + + // Test shape filter + if (!inShapeFilter.ShouldCollide(this, inSubShapeIDCreator.GetID())) + return; + + struct Visitor : public CollectTransformedShapesVisitor + { + using CollectTransformedShapesVisitor::CollectTransformedShapesVisitor; + + using Result = UVec4; + + JPH_INLINE Result TestBlock(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ) const + { + return TestBounds(inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ); + } + + JPH_INLINE bool ShouldVisitBlock(UVec4Arg inResult) const + { + return inResult.TestAnyTrue(); + } + + JPH_INLINE bool ShouldVisitSubShape(UVec4Arg inResult, uint inIndexInBlock) const + { + return inResult[inIndexInBlock] != 0; + } + }; + + Visitor visitor(inBox, this, inPositionCOM, inRotation, inScale, inSubShapeIDCreator, ioCollector, inShapeFilter); + WalkSubShapes(visitor); +} + +int MutableCompoundShape::GetIntersectingSubShapes(const AABox &inBox, uint *outSubShapeIndices, int inMaxSubShapeIndices) const +{ + JPH_PROFILE_FUNCTION(); + + GetIntersectingSubShapesVisitorMC visitor(inBox, outSubShapeIndices, inMaxSubShapeIndices); + WalkSubShapes(visitor); + return visitor.GetNumResults(); +} + +int MutableCompoundShape::GetIntersectingSubShapes(const OrientedBox &inBox, uint *outSubShapeIndices, int inMaxSubShapeIndices) const +{ + JPH_PROFILE_FUNCTION(); + + GetIntersectingSubShapesVisitorMC visitor(inBox, outSubShapeIndices, inMaxSubShapeIndices); + WalkSubShapes(visitor); + return visitor.GetNumResults(); +} + +void MutableCompoundShape::sCollideCompoundVsShape(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter) +{ + JPH_PROFILE_FUNCTION(); + + JPH_ASSERT(inShape1->GetSubType() == EShapeSubType::MutableCompound); + const MutableCompoundShape *shape1 = static_cast(inShape1); + + struct Visitor : public CollideCompoundVsShapeVisitor + { + using CollideCompoundVsShapeVisitor::CollideCompoundVsShapeVisitor; + + using Result = UVec4; + + JPH_INLINE Result TestBlock(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ) const + { + return TestBounds(inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ); + } + + JPH_INLINE bool ShouldVisitBlock(UVec4Arg inResult) const + { + return inResult.TestAnyTrue(); + } + + JPH_INLINE bool ShouldVisitSubShape(UVec4Arg inResult, uint inIndexInBlock) const + { + return inResult[inIndexInBlock] != 0; + } + }; + + Visitor visitor(shape1, inShape2, inScale1, inScale2, inCenterOfMassTransform1, inCenterOfMassTransform2, inSubShapeIDCreator1, inSubShapeIDCreator2, inCollideShapeSettings, ioCollector, inShapeFilter); + shape1->WalkSubShapes(visitor); +} + +void MutableCompoundShape::sCollideShapeVsCompound(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter) +{ + JPH_PROFILE_FUNCTION(); + + JPH_ASSERT(inShape2->GetSubType() == EShapeSubType::MutableCompound); + const MutableCompoundShape *shape2 = static_cast(inShape2); + + struct Visitor : public CollideShapeVsCompoundVisitor + { + using CollideShapeVsCompoundVisitor::CollideShapeVsCompoundVisitor; + + using Result = UVec4; + + JPH_INLINE Result TestBlock(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ) const + { + return TestBounds(inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ); + } + + JPH_INLINE bool ShouldVisitBlock(UVec4Arg inResult) const + { + return inResult.TestAnyTrue(); + } + + JPH_INLINE bool ShouldVisitSubShape(UVec4Arg inResult, uint inIndexInBlock) const + { + return inResult[inIndexInBlock] != 0; + } + }; + + Visitor visitor(inShape1, shape2, inScale1, inScale2, inCenterOfMassTransform1, inCenterOfMassTransform2, inSubShapeIDCreator1, inSubShapeIDCreator2, inCollideShapeSettings, ioCollector, inShapeFilter); + shape2->WalkSubShapes(visitor); +} + +void MutableCompoundShape::SaveBinaryState(StreamOut &inStream) const +{ + CompoundShape::SaveBinaryState(inStream); + + // Write bounds + uint bounds_size = (((uint)mSubShapes.size() + 3) >> 2) * sizeof(Bounds); + inStream.WriteBytes(mSubShapeBounds.data(), bounds_size); +} + +void MutableCompoundShape::RestoreBinaryState(StreamIn &inStream) +{ + CompoundShape::RestoreBinaryState(inStream); + + // Ensure that we have allocated the required space for mSubShapeBounds + EnsureSubShapeBoundsCapacity(); + + // Read bounds + uint bounds_size = (((uint)mSubShapes.size() + 3) >> 2) * sizeof(Bounds); + inStream.ReadBytes(mSubShapeBounds.data(), bounds_size); +} + +void MutableCompoundShape::sRegister() +{ + ShapeFunctions &f = ShapeFunctions::sGet(EShapeSubType::MutableCompound); + f.mConstruct = []() -> Shape * { return new MutableCompoundShape; }; + f.mColor = Color::sDarkOrange; + + for (EShapeSubType s : sAllSubShapeTypes) + { + CollisionDispatch::sRegisterCollideShape(EShapeSubType::MutableCompound, s, sCollideCompoundVsShape); + CollisionDispatch::sRegisterCollideShape(s, EShapeSubType::MutableCompound, sCollideShapeVsCompound); + CollisionDispatch::sRegisterCastShape(s, EShapeSubType::MutableCompound, sCastShapeVsCompound); + } +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/MutableCompoundShape.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/MutableCompoundShape.h new file mode 100644 index 00000000000..eeba481b616 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/MutableCompoundShape.h @@ -0,0 +1,160 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +class CollideShapeSettings; + +/// Class that constructs a MutableCompoundShape. +class JPH_EXPORT MutableCompoundShapeSettings final : public CompoundShapeSettings +{ +public: + JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, MutableCompoundShapeSettings) + + // See: ShapeSettings + virtual ShapeResult Create() const override; +}; + +/// A compound shape, sub shapes can be rotated and translated. +/// This shape is optimized for adding / removing and changing the rotation / translation of sub shapes but is less efficient in querying. +/// Shifts all child objects so that they're centered around the center of mass (which needs to be kept up to date by calling AdjustCenterOfMass). +/// +/// Note: If you're using MutableCompoundShapes and are querying data while modifying the shape you'll have a race condition. +/// In this case it is best to create a new MutableCompoundShape and set the new shape on the body using BodyInterface::SetShape. If a +/// query is still working on the old shape, it will have taken a reference and keep the old shape alive until the query finishes. +class JPH_EXPORT MutableCompoundShape final : public CompoundShape +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Constructor + MutableCompoundShape() : CompoundShape(EShapeSubType::MutableCompound) { } + MutableCompoundShape(const MutableCompoundShapeSettings &inSettings, ShapeResult &outResult); + + // See Shape::CastRay + virtual bool CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const override; + virtual void CastRay(const RayCast &inRay, const RayCastSettings &inRayCastSettings, const SubShapeIDCreator &inSubShapeIDCreator, CastRayCollector &ioCollector, const ShapeFilter &inShapeFilter = { }) const override; + + // See: Shape::CollidePoint + virtual void CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter = { }) const override; + + // See Shape::CollectTransformedShapes + virtual void CollectTransformedShapes(const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale, const SubShapeIDCreator &inSubShapeIDCreator, TransformedShapeCollector &ioCollector, const ShapeFilter &inShapeFilter) const override; + + // See: CompoundShape::GetIntersectingSubShapes + virtual int GetIntersectingSubShapes(const AABox &inBox, uint *outSubShapeIndices, int inMaxSubShapeIndices) const override; + + // See: CompoundShape::GetIntersectingSubShapes + virtual int GetIntersectingSubShapes(const OrientedBox &inBox, uint *outSubShapeIndices, int inMaxSubShapeIndices) const override; + + // See Shape + virtual void SaveBinaryState(StreamOut &inStream) const override; + + // See Shape::GetStats + virtual Stats GetStats() const override { return Stats(sizeof(*this) + mSubShapes.size() * sizeof(SubShape) + mSubShapeBounds.size() * sizeof(Bounds), 0); } + + ///@{ + /// @name Mutating shapes. Note that this is not thread safe, so you need to ensure that any bodies that use this shape are locked at the time of modification using BodyLockWrite. After modification you need to call BodyInterface::NotifyShapeChanged to update the broadphase and collision caches. + + /// Adding a new shape + /// @return The index of the newly added shape + uint AddShape(Vec3Arg inPosition, QuatArg inRotation, const Shape *inShape, uint32 inUserData = 0); + + /// Remove a shape by index + void RemoveShape(uint inIndex); + + /// Modify the position / orientation of a shape + void ModifyShape(uint inIndex, Vec3Arg inPosition, QuatArg inRotation); + + /// Modify the position / orientation and shape at the same time + void ModifyShape(uint inIndex, Vec3Arg inPosition, QuatArg inRotation, const Shape *inShape); + + /// @brief Batch set positions / orientations, this avoids duplicate work due to bounding box calculation. + /// @param inStartIndex Index of first shape to update + /// @param inNumber Number of shapes to update + /// @param inPositions A list of positions with arbitrary stride + /// @param inRotations A list of orientations with arbitrary stride + /// @param inPositionStride The position stride (the number of bytes between the first and second element) + /// @param inRotationStride The orientation stride (the number of bytes between the first and second element) + void ModifyShapes(uint inStartIndex, uint inNumber, const Vec3 *inPositions, const Quat *inRotations, uint inPositionStride = sizeof(Vec3), uint inRotationStride = sizeof(Quat)); + + /// Recalculate the center of mass and shift all objects so they're centered around it + /// (this needs to be done of dynamic bodies and if the center of mass changes significantly due to adding / removing / repositioning sub shapes or else the simulation will look unnatural) + /// Note that after adjusting the center of mass of an object you need to call BodyInterface::NotifyShapeChanged and Constraint::NotifyShapeChanged on the relevant bodies / constraints. + void AdjustCenterOfMass(); + + ///@} + + // Register shape functions with the registry + static void sRegister(); + +protected: + // See: Shape::RestoreBinaryState + virtual void RestoreBinaryState(StreamIn &inStream) override; + +private: + // Visitor for GetIntersectingSubShapes + template + struct GetIntersectingSubShapesVisitorMC : public GetIntersectingSubShapesVisitor + { + using GetIntersectingSubShapesVisitor::GetIntersectingSubShapesVisitor; + + using Result = UVec4; + + JPH_INLINE Result TestBlock(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ) const + { + return GetIntersectingSubShapesVisitor::TestBounds(inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ); + } + + JPH_INLINE bool ShouldVisitBlock(UVec4Arg inResult) const + { + return inResult.TestAnyTrue(); + } + + JPH_INLINE bool ShouldVisitSubShape(UVec4Arg inResult, uint inIndexInBlock) const + { + return inResult[inIndexInBlock] != 0; + } + }; + + /// Get the number of blocks of 4 bounding boxes + inline uint GetNumBlocks() const { return ((uint)mSubShapes.size() + 3) >> 2; } + + /// Ensure that the mSubShapeBounds has enough space to store bounding boxes equivalent to the number of shapes in mSubShapes + void EnsureSubShapeBoundsCapacity(); + + /// Update mSubShapeBounds + /// @param inStartIdx First sub shape to update + /// @param inNumber Number of shapes to update + void CalculateSubShapeBounds(uint inStartIdx, uint inNumber); + + /// Calculate mLocalBounds from mSubShapeBounds + void CalculateLocalBounds(); + + template + JPH_INLINE void WalkSubShapes(Visitor &ioVisitor) const; ///< Walk the sub shapes and call Visitor::VisitShape for each sub shape encountered + + // Helper functions called by CollisionDispatch + static void sCollideCompoundVsShape(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter); + static void sCollideShapeVsCompound(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter); + static void sCastShapeVsCompound(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector); + + struct Bounds + { + Vec4 mMinX; + Vec4 mMinY; + Vec4 mMinZ; + Vec4 mMaxX; + Vec4 mMaxY; + Vec4 mMaxZ; + }; + + Array mSubShapeBounds; ///< Bounding boxes of all sub shapes in SOA format (in blocks of 4 boxes), MinX 0..3, MinY 0..3, MinZ 0..3, MaxX 0..3, MaxY 0..3, MaxZ 0..3, MinX 4..7, MinY 4..7, ... +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/OffsetCenterOfMassShape.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/OffsetCenterOfMassShape.cpp new file mode 100644 index 00000000000..942eed0d91e --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/OffsetCenterOfMassShape.cpp @@ -0,0 +1,217 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(OffsetCenterOfMassShapeSettings) +{ + JPH_ADD_BASE_CLASS(OffsetCenterOfMassShapeSettings, DecoratedShapeSettings) + + JPH_ADD_ATTRIBUTE(OffsetCenterOfMassShapeSettings, mOffset) +} + +ShapeSettings::ShapeResult OffsetCenterOfMassShapeSettings::Create() const +{ + if (mCachedResult.IsEmpty()) + Ref shape = new OffsetCenterOfMassShape(*this, mCachedResult); + return mCachedResult; +} + +OffsetCenterOfMassShape::OffsetCenterOfMassShape(const OffsetCenterOfMassShapeSettings &inSettings, ShapeResult &outResult) : + DecoratedShape(EShapeSubType::OffsetCenterOfMass, inSettings, outResult), + mOffset(inSettings.mOffset) +{ + if (outResult.HasError()) + return; + + outResult.Set(this); +} + +AABox OffsetCenterOfMassShape::GetLocalBounds() const +{ + AABox bounds = mInnerShape->GetLocalBounds(); + bounds.mMin -= mOffset; + bounds.mMax -= mOffset; + return bounds; +} + +AABox OffsetCenterOfMassShape::GetWorldSpaceBounds(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale) const +{ + return mInnerShape->GetWorldSpaceBounds(inCenterOfMassTransform.PreTranslated(-inScale * mOffset), inScale); +} + +TransformedShape OffsetCenterOfMassShape::GetSubShapeTransformedShape(const SubShapeID &inSubShapeID, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale, SubShapeID &outRemainder) const +{ + // We don't use any bits in the sub shape ID + outRemainder = inSubShapeID; + + TransformedShape ts(RVec3(inPositionCOM - inRotation * (inScale * mOffset)), inRotation, mInnerShape, BodyID()); + ts.SetShapeScale(inScale); + return ts; +} + +Vec3 OffsetCenterOfMassShape::GetSurfaceNormal(const SubShapeID &inSubShapeID, Vec3Arg inLocalSurfacePosition) const +{ + // Transform surface position to local space and pass call on + return mInnerShape->GetSurfaceNormal(inSubShapeID, inLocalSurfacePosition + mOffset); +} + +void OffsetCenterOfMassShape::GetSupportingFace(const SubShapeID &inSubShapeID, Vec3Arg inDirection, Vec3Arg inScale, Mat44Arg inCenterOfMassTransform, SupportingFace &outVertices) const +{ + mInnerShape->GetSupportingFace(inSubShapeID, inDirection, inScale, inCenterOfMassTransform.PreTranslated(-inScale * mOffset), outVertices); +} + +void OffsetCenterOfMassShape::GetSubmergedVolume(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, const Plane &inSurface, float &outTotalVolume, float &outSubmergedVolume, Vec3 &outCenterOfBuoyancy JPH_IF_DEBUG_RENDERER(, RVec3Arg inBaseOffset)) const +{ + mInnerShape->GetSubmergedVolume(inCenterOfMassTransform.PreTranslated(-inScale * mOffset), inScale, inSurface, outTotalVolume, outSubmergedVolume, outCenterOfBuoyancy JPH_IF_DEBUG_RENDERER(, inBaseOffset)); +} + +#ifdef JPH_DEBUG_RENDERER +void OffsetCenterOfMassShape::Draw(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inUseMaterialColors, bool inDrawWireframe) const +{ + mInnerShape->Draw(inRenderer, inCenterOfMassTransform.PreTranslated(-inScale * mOffset), inScale, inColor, inUseMaterialColors, inDrawWireframe); +} + +void OffsetCenterOfMassShape::DrawGetSupportFunction(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inDrawSupportDirection) const +{ + mInnerShape->DrawGetSupportFunction(inRenderer, inCenterOfMassTransform.PreTranslated(-inScale * mOffset), inScale, inColor, inDrawSupportDirection); +} + +void OffsetCenterOfMassShape::DrawGetSupportingFace(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale) const +{ + mInnerShape->DrawGetSupportingFace(inRenderer, inCenterOfMassTransform.PreTranslated(-inScale * mOffset), inScale); +} +#endif // JPH_DEBUG_RENDERER + +bool OffsetCenterOfMassShape::CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const +{ + // Transform the ray to local space + RayCast ray = inRay; + ray.mOrigin += mOffset; + + return mInnerShape->CastRay(ray, inSubShapeIDCreator, ioHit); +} + +void OffsetCenterOfMassShape::CastRay(const RayCast &inRay, const RayCastSettings &inRayCastSettings, const SubShapeIDCreator &inSubShapeIDCreator, CastRayCollector &ioCollector, const ShapeFilter &inShapeFilter) const +{ + // Test shape filter + if (!inShapeFilter.ShouldCollide(this, inSubShapeIDCreator.GetID())) + return; + + // Transform the ray to local space + RayCast ray = inRay; + ray.mOrigin += mOffset; + + return mInnerShape->CastRay(ray, inRayCastSettings, inSubShapeIDCreator, ioCollector, inShapeFilter); +} + +void OffsetCenterOfMassShape::CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter) const +{ + // Test shape filter + if (!inShapeFilter.ShouldCollide(this, inSubShapeIDCreator.GetID())) + return; + + // Pass the point on to the inner shape in local space + mInnerShape->CollidePoint(inPoint + mOffset, inSubShapeIDCreator, ioCollector, inShapeFilter); +} + +void OffsetCenterOfMassShape::CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, SoftBodyVertex *ioVertices, uint inNumVertices, float inDeltaTime, Vec3Arg inDisplacementDueToGravity, int inCollidingShapeIndex) const +{ + mInnerShape->CollideSoftBodyVertices(inCenterOfMassTransform.PreTranslated(-inScale * mOffset), inScale, ioVertices, inNumVertices, inDeltaTime, inDisplacementDueToGravity, inCollidingShapeIndex); +} + +void OffsetCenterOfMassShape::CollectTransformedShapes(const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale, const SubShapeIDCreator &inSubShapeIDCreator, TransformedShapeCollector &ioCollector, const ShapeFilter &inShapeFilter) const +{ + // Test shape filter + if (!inShapeFilter.ShouldCollide(this, inSubShapeIDCreator.GetID())) + return; + + mInnerShape->CollectTransformedShapes(inBox, inPositionCOM - inRotation * (inScale * mOffset), inRotation, inScale, inSubShapeIDCreator, ioCollector, inShapeFilter); +} + +void OffsetCenterOfMassShape::TransformShape(Mat44Arg inCenterOfMassTransform, TransformedShapeCollector &ioCollector) const +{ + mInnerShape->TransformShape(inCenterOfMassTransform.PreTranslated(-mOffset), ioCollector); +} + +void OffsetCenterOfMassShape::sCollideOffsetCenterOfMassVsShape(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter) +{ + JPH_ASSERT(inShape1->GetSubType() == EShapeSubType::OffsetCenterOfMass); + const OffsetCenterOfMassShape *shape1 = static_cast(inShape1); + + CollisionDispatch::sCollideShapeVsShape(shape1->mInnerShape, inShape2, inScale1, inScale2, inCenterOfMassTransform1.PreTranslated(-inScale1 * shape1->mOffset), inCenterOfMassTransform2, inSubShapeIDCreator1, inSubShapeIDCreator2, inCollideShapeSettings, ioCollector, inShapeFilter); +} + +void OffsetCenterOfMassShape::sCollideShapeVsOffsetCenterOfMass(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter) +{ + JPH_ASSERT(inShape2->GetSubType() == EShapeSubType::OffsetCenterOfMass); + const OffsetCenterOfMassShape *shape2 = static_cast(inShape2); + + CollisionDispatch::sCollideShapeVsShape(inShape1, shape2->mInnerShape, inScale1, inScale2, inCenterOfMassTransform1, inCenterOfMassTransform2.PreTranslated(-inScale2 * shape2->mOffset), inSubShapeIDCreator1, inSubShapeIDCreator2, inCollideShapeSettings, ioCollector, inShapeFilter); +} + +void OffsetCenterOfMassShape::sCastOffsetCenterOfMassVsShape(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector) +{ + // Fetch offset center of mass shape from cast shape + JPH_ASSERT(inShapeCast.mShape->GetSubType() == EShapeSubType::OffsetCenterOfMass); + const OffsetCenterOfMassShape *shape1 = static_cast(inShapeCast.mShape); + + // Transform the shape cast and update the shape + ShapeCast shape_cast(shape1->mInnerShape, inShapeCast.mScale, inShapeCast.mCenterOfMassStart.PreTranslated(-inShapeCast.mScale * shape1->mOffset), inShapeCast.mDirection); + + CollisionDispatch::sCastShapeVsShapeLocalSpace(shape_cast, inShapeCastSettings, inShape, inScale, inShapeFilter, inCenterOfMassTransform2, inSubShapeIDCreator1, inSubShapeIDCreator2, ioCollector); +} + +void OffsetCenterOfMassShape::sCastShapeVsOffsetCenterOfMass(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector) +{ + JPH_ASSERT(inShape->GetSubType() == EShapeSubType::OffsetCenterOfMass); + const OffsetCenterOfMassShape *shape = static_cast(inShape); + + // Transform the shape cast + ShapeCast shape_cast = inShapeCast.PostTransformed(Mat44::sTranslation(inScale * shape->mOffset)); + + CollisionDispatch::sCastShapeVsShapeLocalSpace(shape_cast, inShapeCastSettings, shape->mInnerShape, inScale, inShapeFilter, inCenterOfMassTransform2.PreTranslated(-inScale * shape->mOffset), inSubShapeIDCreator1, inSubShapeIDCreator2, ioCollector); +} + +void OffsetCenterOfMassShape::SaveBinaryState(StreamOut &inStream) const +{ + DecoratedShape::SaveBinaryState(inStream); + + inStream.Write(mOffset); +} + +void OffsetCenterOfMassShape::RestoreBinaryState(StreamIn &inStream) +{ + DecoratedShape::RestoreBinaryState(inStream); + + inStream.Read(mOffset); +} + +void OffsetCenterOfMassShape::sRegister() +{ + ShapeFunctions &f = ShapeFunctions::sGet(EShapeSubType::OffsetCenterOfMass); + f.mConstruct = []() -> Shape * { return new OffsetCenterOfMassShape; }; + f.mColor = Color::sCyan; + + for (EShapeSubType s : sAllSubShapeTypes) + { + CollisionDispatch::sRegisterCollideShape(EShapeSubType::OffsetCenterOfMass, s, sCollideOffsetCenterOfMassVsShape); + CollisionDispatch::sRegisterCollideShape(s, EShapeSubType::OffsetCenterOfMass, sCollideShapeVsOffsetCenterOfMass); + CollisionDispatch::sRegisterCastShape(EShapeSubType::OffsetCenterOfMass, s, sCastOffsetCenterOfMassVsShape); + CollisionDispatch::sRegisterCastShape(s, EShapeSubType::OffsetCenterOfMass, sCastShapeVsOffsetCenterOfMass); + } +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/OffsetCenterOfMassShape.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/OffsetCenterOfMassShape.h new file mode 100644 index 00000000000..3e828181cb5 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/OffsetCenterOfMassShape.h @@ -0,0 +1,143 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +class CollideShapeSettings; + +/// Class that constructs an OffsetCenterOfMassShape +class JPH_EXPORT OffsetCenterOfMassShapeSettings final : public DecoratedShapeSettings +{ +public: + JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, OffsetCenterOfMassShapeSettings) + + /// Constructor + OffsetCenterOfMassShapeSettings() = default; + + /// Construct with shape settings, can be serialized. + OffsetCenterOfMassShapeSettings(Vec3Arg inOffset, const ShapeSettings *inShape) : DecoratedShapeSettings(inShape), mOffset(inOffset) { } + + /// Variant that uses a concrete shape, which means this object cannot be serialized. + OffsetCenterOfMassShapeSettings(Vec3Arg inOffset, const Shape *inShape): DecoratedShapeSettings(inShape), mOffset(inOffset) { } + + // See: ShapeSettings + virtual ShapeResult Create() const override; + + Vec3 mOffset; ///< Offset to be applied to the center of mass of the child shape +}; + +/// This shape will shift the center of mass of a child shape, it can e.g. be used to lower the center of mass of an unstable object like a boat to make it stable +class JPH_EXPORT OffsetCenterOfMassShape final : public DecoratedShape +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Constructor + OffsetCenterOfMassShape() : DecoratedShape(EShapeSubType::OffsetCenterOfMass) { } + OffsetCenterOfMassShape(const OffsetCenterOfMassShapeSettings &inSettings, ShapeResult &outResult); + OffsetCenterOfMassShape(const Shape *inShape, Vec3Arg inOffset) : DecoratedShape(EShapeSubType::OffsetCenterOfMass, inShape), mOffset(inOffset) { } + + /// Access the offset that is applied to the center of mass + Vec3 GetOffset() const { return mOffset; } + + // See Shape::GetCenterOfMass + virtual Vec3 GetCenterOfMass() const override { return mInnerShape->GetCenterOfMass() + mOffset; } + + // See Shape::GetLocalBounds + virtual AABox GetLocalBounds() const override; + + // See Shape::GetWorldSpaceBounds + virtual AABox GetWorldSpaceBounds(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale) const override; + using Shape::GetWorldSpaceBounds; + + // See Shape::GetInnerRadius + virtual float GetInnerRadius() const override { return mInnerShape->GetInnerRadius(); } + + // See Shape::GetMassProperties + virtual MassProperties GetMassProperties() const override + { + MassProperties mp = mInnerShape->GetMassProperties(); + mp.Translate(mOffset); + return mp; + } + + // See Shape::GetSubShapeTransformedShape + virtual TransformedShape GetSubShapeTransformedShape(const SubShapeID &inSubShapeID, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale, SubShapeID &outRemainder) const override; + + // See Shape::GetSurfaceNormal + virtual Vec3 GetSurfaceNormal(const SubShapeID &inSubShapeID, Vec3Arg inLocalSurfacePosition) const override; + + // See Shape::GetSupportingFace + virtual void GetSupportingFace(const SubShapeID &inSubShapeID, Vec3Arg inDirection, Vec3Arg inScale, Mat44Arg inCenterOfMassTransform, SupportingFace &outVertices) const override; + + // See Shape::GetSubmergedVolume + virtual void GetSubmergedVolume(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, const Plane &inSurface, float &outTotalVolume, float &outSubmergedVolume, Vec3 &outCenterOfBuoyancy JPH_IF_DEBUG_RENDERER(, RVec3Arg inBaseOffset)) const override; + +#ifdef JPH_DEBUG_RENDERER + // See Shape::Draw + virtual void Draw(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inUseMaterialColors, bool inDrawWireframe) const override; + + // See Shape::DrawGetSupportFunction + virtual void DrawGetSupportFunction(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inDrawSupportDirection) const override; + + // See Shape::DrawGetSupportingFace + virtual void DrawGetSupportingFace(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale) const override; +#endif // JPH_DEBUG_RENDERER + + // See Shape::CastRay + virtual bool CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const override; + virtual void CastRay(const RayCast &inRay, const RayCastSettings &inRayCastSettings, const SubShapeIDCreator &inSubShapeIDCreator, CastRayCollector &ioCollector, const ShapeFilter &inShapeFilter = { }) const override; + + // See: Shape::CollidePoint + virtual void CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter = { }) const override; + + // See: Shape::CollideSoftBodyVertices + virtual void CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, SoftBodyVertex *ioVertices, uint inNumVertices, float inDeltaTime, Vec3Arg inDisplacementDueToGravity, int inCollidingShapeIndex) const override; + + // See Shape::CollectTransformedShapes + virtual void CollectTransformedShapes(const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale, const SubShapeIDCreator &inSubShapeIDCreator, TransformedShapeCollector &ioCollector, const ShapeFilter &inShapeFilter) const override; + + // See Shape::TransformShape + virtual void TransformShape(Mat44Arg inCenterOfMassTransform, TransformedShapeCollector &ioCollector) const override; + + // See Shape::GetTrianglesStart + virtual void GetTrianglesStart(GetTrianglesContext &ioContext, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) const override { JPH_ASSERT(false, "Cannot call on non-leaf shapes, use CollectTransformedShapes to collect the leaves first!"); } + + // See Shape::GetTrianglesNext + virtual int GetTrianglesNext(GetTrianglesContext &ioContext, int inMaxTrianglesRequested, Float3 *outTriangleVertices, const PhysicsMaterial **outMaterials = nullptr) const override { JPH_ASSERT(false, "Cannot call on non-leaf shapes, use CollectTransformedShapes to collect the leaves first!"); return 0; } + + // See Shape + virtual void SaveBinaryState(StreamOut &inStream) const override; + + // See Shape::GetStats + virtual Stats GetStats() const override { return Stats(sizeof(*this), 0); } + + // See Shape::GetVolume + virtual float GetVolume() const override { return mInnerShape->GetVolume(); } + + // See Shape::IsValidScale + virtual bool IsValidScale(Vec3Arg inScale) const override { return mInnerShape->IsValidScale(inScale); } + + // Register shape functions with the registry + static void sRegister(); + +protected: + // See: Shape::RestoreBinaryState + virtual void RestoreBinaryState(StreamIn &inStream) override; + +private: + // Helper functions called by CollisionDispatch + static void sCollideOffsetCenterOfMassVsShape(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter); + static void sCollideShapeVsOffsetCenterOfMass(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter); + static void sCastOffsetCenterOfMassVsShape(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector); + static void sCastShapeVsOffsetCenterOfMass(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector); + + Vec3 mOffset; ///< Offset of the center of mass +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/PolyhedronSubmergedVolumeCalculator.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/PolyhedronSubmergedVolumeCalculator.h new file mode 100644 index 00000000000..96e69f0b19b --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/PolyhedronSubmergedVolumeCalculator.h @@ -0,0 +1,319 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#ifdef JPH_DEBUG_RENDERER + #include +#endif // JPH_DEBUG_RENDERER + +JPH_NAMESPACE_BEGIN + +/// This class calculates the intersection between a fluid surface and a polyhedron and returns the submerged volume and its center of buoyancy +/// Construct this class and then one by one add all faces of the polyhedron using the AddFace function. After all faces have been added the result +/// can be gotten through GetResult. +class PolyhedronSubmergedVolumeCalculator +{ +private: + // Calculate submerged volume * 6 and center of mass * 4 for a tetrahedron with 4 vertices submerged + // inV1 .. inV4 are submerged + inline static void sTetrahedronVolume4(Vec3Arg inV1, Vec3Arg inV2, Vec3Arg inV3, Vec3Arg inV4, float &outVolumeTimes6, Vec3 &outCenterTimes4) + { + // Calculate center of mass and mass of this tetrahedron, + // see: https://en.wikipedia.org/wiki/Tetrahedron#Volume + outVolumeTimes6 = max((inV1 - inV4).Dot((inV2 - inV4).Cross(inV3 - inV4)), 0.0f); // All contributions should be positive because we use a reference point that is on the surface of the hull + outCenterTimes4 = inV1 + inV2 + inV3 + inV4; + } + + // Get the intersection point with a plane. + // inV1 is inD1 distance away from the plane, inV2 is inD2 distance away from the plane + inline static Vec3 sGetPlaneIntersection(Vec3Arg inV1, float inD1, Vec3Arg inV2, float inD2) + { + JPH_ASSERT(Sign(inD1) != Sign(inD2), "Assuming both points are on opposite ends of the plane"); + float delta = inD1 - inD2; + if (abs(delta) < 1.0e-6f) + return inV1; // Parallel to plane, just pick a point + else + return inV1 + inD1 * (inV2 - inV1) / delta; + } + + // Calculate submerged volume * 6 and center of mass * 4 for a tetrahedron with 1 vertex submerged + // inV1 is submerged, inV2 .. inV4 are not + // inD1 .. inD4 are the distances from the points to the plane + inline JPH_IF_NOT_DEBUG_RENDERER(static) void sTetrahedronVolume1(Vec3Arg inV1, float inD1, Vec3Arg inV2, float inD2, Vec3Arg inV3, float inD3, Vec3Arg inV4, float inD4, float &outVolumeTimes6, Vec3 &outCenterTimes4) + { + // A tetrahedron with 1 point submerged is cut along 3 edges forming a new tetrahedron + Vec3 v2 = sGetPlaneIntersection(inV1, inD1, inV2, inD2); + Vec3 v3 = sGetPlaneIntersection(inV1, inD1, inV3, inD3); + Vec3 v4 = sGetPlaneIntersection(inV1, inD1, inV4, inD4); + + #ifdef JPH_DEBUG_RENDERER + // Draw intersection between tetrahedron and surface + if (Shape::sDrawSubmergedVolumes) + { + RVec3 v2w = mBaseOffset + v2; + RVec3 v3w = mBaseOffset + v3; + RVec3 v4w = mBaseOffset + v4; + + DebugRenderer::sInstance->DrawTriangle(v4w, v3w, v2w, Color::sGreen); + DebugRenderer::sInstance->DrawWireTriangle(v4w, v3w, v2w, Color::sWhite); + } + #endif // JPH_DEBUG_RENDERER + + sTetrahedronVolume4(inV1, v2, v3, v4, outVolumeTimes6, outCenterTimes4); + } + + // Calculate submerged volume * 6 and center of mass * 4 for a tetrahedron with 2 vertices submerged + // inV1, inV2 are submerged, inV3, inV4 are not + // inD1 .. inD4 are the distances from the points to the plane + inline JPH_IF_NOT_DEBUG_RENDERER(static) void sTetrahedronVolume2(Vec3Arg inV1, float inD1, Vec3Arg inV2, float inD2, Vec3Arg inV3, float inD3, Vec3Arg inV4, float inD4, float &outVolumeTimes6, Vec3 &outCenterTimes4) + { + // A tetrahedron with 2 points submerged is cut along 4 edges forming a quad + Vec3 c = sGetPlaneIntersection(inV1, inD1, inV3, inD3); + Vec3 d = sGetPlaneIntersection(inV1, inD1, inV4, inD4); + Vec3 e = sGetPlaneIntersection(inV2, inD2, inV4, inD4); + Vec3 f = sGetPlaneIntersection(inV2, inD2, inV3, inD3); + + #ifdef JPH_DEBUG_RENDERER + // Draw intersection between tetrahedron and surface + if (Shape::sDrawSubmergedVolumes) + { + RVec3 cw = mBaseOffset + c; + RVec3 dw = mBaseOffset + d; + RVec3 ew = mBaseOffset + e; + RVec3 fw = mBaseOffset + f; + + DebugRenderer::sInstance->DrawTriangle(cw, ew, dw, Color::sGreen); + DebugRenderer::sInstance->DrawTriangle(cw, fw, ew, Color::sGreen); + DebugRenderer::sInstance->DrawWireTriangle(cw, ew, dw, Color::sWhite); + DebugRenderer::sInstance->DrawWireTriangle(cw, fw, ew, Color::sWhite); + } + #endif // JPH_DEBUG_RENDERER + + // We pick point c as reference (which is on the cut off surface) + // This leaves us with three tetrahedrons to sum up (any faces that are in the same plane as c will have zero volume) + Vec3 center1, center2, center3; + float volume1, volume2, volume3; + sTetrahedronVolume4(e, f, inV2, c, volume1, center1); + sTetrahedronVolume4(e, inV1, d, c, volume2, center2); + sTetrahedronVolume4(e, inV2, inV1, c, volume3, center3); + + // Tally up the totals + outVolumeTimes6 = volume1 + volume2 + volume3; + outCenterTimes4 = outVolumeTimes6 > 0.0f? (volume1 * center1 + volume2 * center2 + volume3 * center3) / outVolumeTimes6 : Vec3::sZero(); + } + + // Calculate submerged volume * 6 and center of mass * 4 for a tetrahedron with 3 vertices submerged + // inV1, inV2, inV3 are submerged, inV4 is not + // inD1 .. inD4 are the distances from the points to the plane + inline JPH_IF_NOT_DEBUG_RENDERER(static) void sTetrahedronVolume3(Vec3Arg inV1, float inD1, Vec3Arg inV2, float inD2, Vec3Arg inV3, float inD3, Vec3Arg inV4, float inD4, float &outVolumeTimes6, Vec3 &outCenterTimes4) + { + // A tetrahedron with 1 point above the surface is cut along 3 edges forming a new tetrahedron + Vec3 v1 = sGetPlaneIntersection(inV1, inD1, inV4, inD4); + Vec3 v2 = sGetPlaneIntersection(inV2, inD2, inV4, inD4); + Vec3 v3 = sGetPlaneIntersection(inV3, inD3, inV4, inD4); + + #ifdef JPH_DEBUG_RENDERER + // Draw intersection between tetrahedron and surface + if (Shape::sDrawSubmergedVolumes) + { + RVec3 v1w = mBaseOffset + v1; + RVec3 v2w = mBaseOffset + v2; + RVec3 v3w = mBaseOffset + v3; + + DebugRenderer::sInstance->DrawTriangle(v3w, v2w, v1w, Color::sGreen); + DebugRenderer::sInstance->DrawWireTriangle(v3w, v2w, v1w, Color::sWhite); + } + #endif // JPH_DEBUG_RENDERER + + Vec3 dry_center, total_center; + float dry_volume, total_volume; + + // We first calculate the part that is above the surface + sTetrahedronVolume4(v1, v2, v3, inV4, dry_volume, dry_center); + + // Calculate the total volume + sTetrahedronVolume4(inV1, inV2, inV3, inV4, total_volume, total_center); + + // From this we can calculate the center and volume of the submerged part + outVolumeTimes6 = max(total_volume - dry_volume, 0.0f); + outCenterTimes4 = outVolumeTimes6 > 0.0f? (total_center * total_volume - dry_center * dry_volume) / outVolumeTimes6 : Vec3::sZero(); + } + +public: + /// A helper class that contains cached information about a polyhedron vertex + class Point + { + public: + Vec3 mPosition; ///< World space position of vertex + float mDistanceToSurface; ///< Signed distance to the surface (> 0 is above, < 0 is below) + bool mAboveSurface; ///< If the point is above the surface (mDistanceToSurface > 0) + }; + + /// Constructor + /// @param inTransform Transform to transform all incoming points with + /// @param inPoints Array of points that are part of the polyhedron + /// @param inPointStride Amount of bytes between each point (should usually be sizeof(Vec3)) + /// @param inNumPoints The amount of points + /// @param inSurface The plane that forms the fluid surface (normal should point up) + /// @param ioBuffer A temporary buffer of Point's that should have inNumPoints entries and should stay alive while this class is alive +#ifdef JPH_DEBUG_RENDERER + /// @param inBaseOffset The offset to transform inTransform to world space (in double precision mode this can be used to shift the whole operation closer to the origin). Only used for debug drawing. +#endif // JPH_DEBUG_RENDERER + PolyhedronSubmergedVolumeCalculator(const Mat44 &inTransform, const Vec3 *inPoints, int inPointStride, int inNumPoints, const Plane &inSurface, Point *ioBuffer +#ifdef JPH_DEBUG_RENDERER // Not using JPH_IF_DEBUG_RENDERER for Doxygen + , RVec3 inBaseOffset +#endif // JPH_DEBUG_RENDERER + ) : + mPoints(ioBuffer) +#ifdef JPH_DEBUG_RENDERER + , mBaseOffset(inBaseOffset) +#endif // JPH_DEBUG_RENDERER + { + // Convert the points to world space and determine the distance to the surface + float reference_dist = FLT_MAX; + for (int p = 0; p < inNumPoints; ++p) + { + // Calculate values + Vec3 transformed_point = inTransform * *reinterpret_cast(reinterpret_cast(inPoints) + p * inPointStride); + float dist = inSurface.SignedDistance(transformed_point); + bool above = dist >= 0.0f; + + // Keep track if all are above or below + mAllAbove &= above; + mAllBelow &= !above; + + // Calculate lowest point, we use this to create tetrahedrons out of all faces + if (reference_dist > dist) + { + mReferencePointIdx = p; + reference_dist = dist; + } + + // Store values + ioBuffer->mPosition = transformed_point; + ioBuffer->mDistanceToSurface = dist; + ioBuffer->mAboveSurface = above; + ++ioBuffer; + } + } + + /// Check if all points are above the surface. Should be used as early out. + inline bool AreAllAbove() const + { + return mAllAbove; + } + + /// Check if all points are below the surface. Should be used as early out. + inline bool AreAllBelow() const + { + return mAllBelow; + } + + /// Get the lowest point of the polyhedron. Used to form the 4th vertex to make a tetrahedron out of a polyhedron face. + inline int GetReferencePointIdx() const + { + return mReferencePointIdx; + } + + /// Add a polyhedron face. Supply the indices of the points that form the face (in counter clockwise order). + void AddFace(int inIdx1, int inIdx2, int inIdx3) + { + JPH_ASSERT(inIdx1 != mReferencePointIdx && inIdx2 != mReferencePointIdx && inIdx3 != mReferencePointIdx, "A face using the reference point will not contribute to the volume"); + + // Find the points + const Point &ref = mPoints[mReferencePointIdx]; + const Point &p1 = mPoints[inIdx1]; + const Point &p2 = mPoints[inIdx2]; + const Point &p3 = mPoints[inIdx3]; + + // Determine which vertices are submerged + uint code = (p1.mAboveSurface? 0 : 0b001) | (p2.mAboveSurface? 0 : 0b010) | (p3.mAboveSurface? 0 : 0b100); + + float volume; + Vec3 center; + switch (code) + { + case 0b000: + // One point submerged + sTetrahedronVolume1(ref.mPosition, ref.mDistanceToSurface, p3.mPosition, p3.mDistanceToSurface, p2.mPosition, p2.mDistanceToSurface, p1.mPosition, p1.mDistanceToSurface, volume, center); + break; + + case 0b001: + // Two points submerged + sTetrahedronVolume2(ref.mPosition, ref.mDistanceToSurface, p1.mPosition, p1.mDistanceToSurface, p3.mPosition, p3.mDistanceToSurface, p2.mPosition, p2.mDistanceToSurface, volume, center); + break; + + case 0b010: + // Two points submerged + sTetrahedronVolume2(ref.mPosition, ref.mDistanceToSurface, p2.mPosition, p2.mDistanceToSurface, p1.mPosition, p1.mDistanceToSurface, p3.mPosition, p3.mDistanceToSurface, volume, center); + break; + + case 0b100: + // Two points submerged + sTetrahedronVolume2(ref.mPosition, ref.mDistanceToSurface, p3.mPosition, p3.mDistanceToSurface, p2.mPosition, p2.mDistanceToSurface, p1.mPosition, p1.mDistanceToSurface, volume, center); + break; + + case 0b011: + // Three points submerged + sTetrahedronVolume3(ref.mPosition, ref.mDistanceToSurface, p2.mPosition, p2.mDistanceToSurface, p1.mPosition, p1.mDistanceToSurface, p3.mPosition, p3.mDistanceToSurface, volume, center); + break; + + case 0b101: + // Three points submerged + sTetrahedronVolume3(ref.mPosition, ref.mDistanceToSurface, p1.mPosition, p1.mDistanceToSurface, p3.mPosition, p3.mDistanceToSurface, p2.mPosition, p2.mDistanceToSurface, volume, center); + break; + + case 0b110: + // Three points submerged + sTetrahedronVolume3(ref.mPosition, ref.mDistanceToSurface, p3.mPosition, p3.mDistanceToSurface, p2.mPosition, p2.mDistanceToSurface, p1.mPosition, p1.mDistanceToSurface, volume, center); + break; + + case 0b111: + // Four points submerged + sTetrahedronVolume4(ref.mPosition, p3.mPosition, p2.mPosition, p1.mPosition, volume, center); + break; + + default: + // Should not be possible + JPH_ASSERT(false); + volume = 0.0f; + center = Vec3::sZero(); + break; + } + + mSubmergedVolume += volume; + mCenterOfBuoyancy += volume * center; + } + + /// Call after all faces have been added. Returns the submerged volume and the center of buoyancy for the submerged volume. + void GetResult(float &outSubmergedVolume, Vec3 &outCenterOfBuoyancy) const + { + outCenterOfBuoyancy = mSubmergedVolume > 0.0f? mCenterOfBuoyancy / (4.0f * mSubmergedVolume) : Vec3::sZero(); // Do this before dividing submerged volume by 6 to get correct weight factor + outSubmergedVolume = mSubmergedVolume / 6.0f; + } + +private: + // The precalculated points for this polyhedron + const Point * mPoints; + + // If all points are above/below the surface + bool mAllBelow = true; + bool mAllAbove = true; + + // The lowest point + int mReferencePointIdx = 0; + + // Aggregator for submerged volume and center of buoyancy + float mSubmergedVolume = 0.0f; + Vec3 mCenterOfBuoyancy = Vec3::sZero(); + +#ifdef JPH_DEBUG_RENDERER + // Base offset used for drawing + RVec3 mBaseOffset; +#endif +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/RotatedTranslatedShape.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/RotatedTranslatedShape.cpp new file mode 100644 index 00000000000..7d6e06a019f --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/RotatedTranslatedShape.cpp @@ -0,0 +1,315 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(RotatedTranslatedShapeSettings) +{ + JPH_ADD_BASE_CLASS(RotatedTranslatedShapeSettings, DecoratedShapeSettings) + + JPH_ADD_ATTRIBUTE(RotatedTranslatedShapeSettings, mPosition) + JPH_ADD_ATTRIBUTE(RotatedTranslatedShapeSettings, mRotation) +} + +ShapeSettings::ShapeResult RotatedTranslatedShapeSettings::Create() const +{ + if (mCachedResult.IsEmpty()) + Ref shape = new RotatedTranslatedShape(*this, mCachedResult); + return mCachedResult; +} + +RotatedTranslatedShape::RotatedTranslatedShape(const RotatedTranslatedShapeSettings &inSettings, ShapeResult &outResult) : + DecoratedShape(EShapeSubType::RotatedTranslated, inSettings, outResult) +{ + if (outResult.HasError()) + return; + + // Calculate center of mass position + mCenterOfMass = inSettings.mPosition + inSettings.mRotation * mInnerShape->GetCenterOfMass(); + + // Store rotation (position is always zero because we center around the center of mass) + mRotation = inSettings.mRotation; + mIsRotationIdentity = mRotation.IsClose(Quat::sIdentity()); + + outResult.Set(this); +} + +RotatedTranslatedShape::RotatedTranslatedShape(Vec3Arg inPosition, QuatArg inRotation, const Shape *inShape) : + DecoratedShape(EShapeSubType::RotatedTranslated, inShape) +{ + // Calculate center of mass position + mCenterOfMass = inPosition + inRotation * mInnerShape->GetCenterOfMass(); + + // Store rotation (position is always zero because we center around the center of mass) + mRotation = inRotation; + mIsRotationIdentity = mRotation.IsClose(Quat::sIdentity()); +} + +MassProperties RotatedTranslatedShape::GetMassProperties() const +{ + // Rotate inertia of child into place + MassProperties p = mInnerShape->GetMassProperties(); + p.Rotate(Mat44::sRotation(mRotation)); + return p; +} + +AABox RotatedTranslatedShape::GetLocalBounds() const +{ + return mInnerShape->GetLocalBounds().Transformed(Mat44::sRotation(mRotation)); +} + +AABox RotatedTranslatedShape::GetWorldSpaceBounds(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale) const +{ + Mat44 transform = inCenterOfMassTransform * Mat44::sRotation(mRotation); + return mInnerShape->GetWorldSpaceBounds(transform, TransformScale(inScale)); +} + +TransformedShape RotatedTranslatedShape::GetSubShapeTransformedShape(const SubShapeID &inSubShapeID, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale, SubShapeID &outRemainder) const +{ + // We don't use any bits in the sub shape ID + outRemainder = inSubShapeID; + + TransformedShape ts(RVec3(inPositionCOM), inRotation * mRotation, mInnerShape, BodyID()); + ts.SetShapeScale(TransformScale(inScale)); + return ts; +} + +Vec3 RotatedTranslatedShape::GetSurfaceNormal(const SubShapeID &inSubShapeID, Vec3Arg inLocalSurfacePosition) const +{ + // Transform surface position to local space and pass call on + Mat44 transform = Mat44::sRotation(mRotation.Conjugated()); + Vec3 normal = mInnerShape->GetSurfaceNormal(inSubShapeID, transform * inLocalSurfacePosition); + + // Transform normal to this shape's space + return transform.Multiply3x3Transposed(normal); +} + +void RotatedTranslatedShape::GetSupportingFace(const SubShapeID &inSubShapeID, Vec3Arg inDirection, Vec3Arg inScale, Mat44Arg inCenterOfMassTransform, SupportingFace &outVertices) const +{ + Mat44 transform = Mat44::sRotation(mRotation); + mInnerShape->GetSupportingFace(inSubShapeID, transform.Multiply3x3Transposed(inDirection), TransformScale(inScale), inCenterOfMassTransform * transform, outVertices); +} + +void RotatedTranslatedShape::GetSubmergedVolume(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, const Plane &inSurface, float &outTotalVolume, float &outSubmergedVolume, Vec3 &outCenterOfBuoyancy JPH_IF_DEBUG_RENDERER(, RVec3Arg inBaseOffset)) const +{ + // Get center of mass transform of child + Mat44 transform = inCenterOfMassTransform * Mat44::sRotation(mRotation); + + // Recurse to child + mInnerShape->GetSubmergedVolume(transform, TransformScale(inScale), inSurface, outTotalVolume, outSubmergedVolume, outCenterOfBuoyancy JPH_IF_DEBUG_RENDERER(, inBaseOffset)); +} + +#ifdef JPH_DEBUG_RENDERER +void RotatedTranslatedShape::Draw(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inUseMaterialColors, bool inDrawWireframe) const +{ + mInnerShape->Draw(inRenderer, inCenterOfMassTransform * Mat44::sRotation(mRotation), TransformScale(inScale), inColor, inUseMaterialColors, inDrawWireframe); +} + +void RotatedTranslatedShape::DrawGetSupportFunction(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inDrawSupportDirection) const +{ + mInnerShape->DrawGetSupportFunction(inRenderer, inCenterOfMassTransform * Mat44::sRotation(mRotation), TransformScale(inScale), inColor, inDrawSupportDirection); +} + +void RotatedTranslatedShape::DrawGetSupportingFace(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale) const +{ + mInnerShape->DrawGetSupportingFace(inRenderer, inCenterOfMassTransform * Mat44::sRotation(mRotation), TransformScale(inScale)); +} +#endif // JPH_DEBUG_RENDERER + +bool RotatedTranslatedShape::CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const +{ + // Transform the ray + Mat44 transform = Mat44::sRotation(mRotation.Conjugated()); + RayCast ray = inRay.Transformed(transform); + + return mInnerShape->CastRay(ray, inSubShapeIDCreator, ioHit); +} + +void RotatedTranslatedShape::CastRay(const RayCast &inRay, const RayCastSettings &inRayCastSettings, const SubShapeIDCreator &inSubShapeIDCreator, CastRayCollector &ioCollector, const ShapeFilter &inShapeFilter) const +{ + // Test shape filter + if (!inShapeFilter.ShouldCollide(this, inSubShapeIDCreator.GetID())) + return; + + // Transform the ray + Mat44 transform = Mat44::sRotation(mRotation.Conjugated()); + RayCast ray = inRay.Transformed(transform); + + return mInnerShape->CastRay(ray, inRayCastSettings, inSubShapeIDCreator, ioCollector, inShapeFilter); +} + +void RotatedTranslatedShape::CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter) const +{ + // Test shape filter + if (!inShapeFilter.ShouldCollide(this, inSubShapeIDCreator.GetID())) + return; + + // Transform the point + Mat44 transform = Mat44::sRotation(mRotation.Conjugated()); + mInnerShape->CollidePoint(transform * inPoint, inSubShapeIDCreator, ioCollector, inShapeFilter); +} + +void RotatedTranslatedShape::CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, SoftBodyVertex *ioVertices, uint inNumVertices, float inDeltaTime, Vec3Arg inDisplacementDueToGravity, int inCollidingShapeIndex) const +{ + mInnerShape->CollideSoftBodyVertices(inCenterOfMassTransform * Mat44::sRotation(mRotation), inScale, ioVertices, inNumVertices, inDeltaTime, inDisplacementDueToGravity, inCollidingShapeIndex); +} + +void RotatedTranslatedShape::CollectTransformedShapes(const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale, const SubShapeIDCreator &inSubShapeIDCreator, TransformedShapeCollector &ioCollector, const ShapeFilter &inShapeFilter) const +{ + // Test shape filter + if (!inShapeFilter.ShouldCollide(this, inSubShapeIDCreator.GetID())) + return; + + mInnerShape->CollectTransformedShapes(inBox, inPositionCOM, inRotation * mRotation, TransformScale(inScale), inSubShapeIDCreator, ioCollector, inShapeFilter); +} + +void RotatedTranslatedShape::TransformShape(Mat44Arg inCenterOfMassTransform, TransformedShapeCollector &ioCollector) const +{ + mInnerShape->TransformShape(inCenterOfMassTransform * Mat44::sRotation(mRotation), ioCollector); +} + +void RotatedTranslatedShape::sCollideRotatedTranslatedVsShape(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter) +{ + JPH_ASSERT(inShape1->GetSubType() == EShapeSubType::RotatedTranslated); + const RotatedTranslatedShape *shape1 = static_cast(inShape1); + + // Get world transform of 1 + Mat44 transform1 = inCenterOfMassTransform1 * Mat44::sRotation(shape1->mRotation); + + CollisionDispatch::sCollideShapeVsShape(shape1->mInnerShape, inShape2, shape1->TransformScale(inScale1), inScale2, transform1, inCenterOfMassTransform2, inSubShapeIDCreator1, inSubShapeIDCreator2, inCollideShapeSettings, ioCollector, inShapeFilter); +} + +void RotatedTranslatedShape::sCollideShapeVsRotatedTranslated(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter) +{ + JPH_ASSERT(inShape2->GetSubType() == EShapeSubType::RotatedTranslated); + const RotatedTranslatedShape *shape2 = static_cast(inShape2); + + // Get world transform of 2 + Mat44 transform2 = inCenterOfMassTransform2 * Mat44::sRotation(shape2->mRotation); + + CollisionDispatch::sCollideShapeVsShape(inShape1, shape2->mInnerShape, inScale1, shape2->TransformScale(inScale2), inCenterOfMassTransform1, transform2, inSubShapeIDCreator1, inSubShapeIDCreator2, inCollideShapeSettings, ioCollector, inShapeFilter); +} + +void RotatedTranslatedShape::sCollideRotatedTranslatedVsRotatedTranslated(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter) +{ + JPH_ASSERT(inShape1->GetSubType() == EShapeSubType::RotatedTranslated); + const RotatedTranslatedShape *shape1 = static_cast(inShape1); + JPH_ASSERT(inShape2->GetSubType() == EShapeSubType::RotatedTranslated); + const RotatedTranslatedShape *shape2 = static_cast(inShape2); + + // Get world transform of 1 and 2 + Mat44 transform1 = inCenterOfMassTransform1 * Mat44::sRotation(shape1->mRotation); + Mat44 transform2 = inCenterOfMassTransform2 * Mat44::sRotation(shape2->mRotation); + + CollisionDispatch::sCollideShapeVsShape(shape1->mInnerShape, shape2->mInnerShape, shape1->TransformScale(inScale1), shape2->TransformScale(inScale2), transform1, transform2, inSubShapeIDCreator1, inSubShapeIDCreator2, inCollideShapeSettings, ioCollector, inShapeFilter); +} + +void RotatedTranslatedShape::sCastRotatedTranslatedVsShape(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector) +{ + // Fetch rotated translated shape from cast shape + JPH_ASSERT(inShapeCast.mShape->GetSubType() == EShapeSubType::RotatedTranslated); + const RotatedTranslatedShape *shape1 = static_cast(inShapeCast.mShape); + + // Transform the shape cast and update the shape + Mat44 transform = inShapeCast.mCenterOfMassStart * Mat44::sRotation(shape1->mRotation); + Vec3 scale = shape1->TransformScale(inShapeCast.mScale); + ShapeCast shape_cast(shape1->mInnerShape, scale, transform, inShapeCast.mDirection); + + CollisionDispatch::sCastShapeVsShapeLocalSpace(shape_cast, inShapeCastSettings, inShape, inScale, inShapeFilter, inCenterOfMassTransform2, inSubShapeIDCreator1, inSubShapeIDCreator2, ioCollector); +} + +void RotatedTranslatedShape::sCastShapeVsRotatedTranslated(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector) +{ + JPH_ASSERT(inShape->GetSubType() == EShapeSubType::RotatedTranslated); + const RotatedTranslatedShape *shape = static_cast(inShape); + + // Determine the local transform + Mat44 local_transform = Mat44::sRotation(shape->mRotation); + + // Transform the shape cast + ShapeCast shape_cast = inShapeCast.PostTransformed(local_transform.Transposed3x3()); + + CollisionDispatch::sCastShapeVsShapeLocalSpace(shape_cast, inShapeCastSettings, shape->mInnerShape, shape->TransformScale(inScale), inShapeFilter, inCenterOfMassTransform2 * local_transform, inSubShapeIDCreator1, inSubShapeIDCreator2, ioCollector); +} + +void RotatedTranslatedShape::sCastRotatedTranslatedVsRotatedTranslated(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector) +{ + JPH_ASSERT(inShapeCast.mShape->GetSubType() == EShapeSubType::RotatedTranslated); + const RotatedTranslatedShape *shape1 = static_cast(inShapeCast.mShape); + JPH_ASSERT(inShape->GetSubType() == EShapeSubType::RotatedTranslated); + const RotatedTranslatedShape *shape2 = static_cast(inShape); + + // Determine the local transform of shape 2 + Mat44 local_transform2 = Mat44::sRotation(shape2->mRotation); + Mat44 local_transform2_transposed = local_transform2.Transposed3x3(); + + // Transform the shape cast and update the shape + Mat44 transform = (local_transform2_transposed * inShapeCast.mCenterOfMassStart) * Mat44::sRotation(shape1->mRotation); + Vec3 scale = shape1->TransformScale(inShapeCast.mScale); + ShapeCast shape_cast(shape1->mInnerShape, scale, transform, local_transform2_transposed.Multiply3x3(inShapeCast.mDirection)); + + CollisionDispatch::sCastShapeVsShapeLocalSpace(shape_cast, inShapeCastSettings, shape2->mInnerShape, shape2->TransformScale(inScale), inShapeFilter, inCenterOfMassTransform2 * local_transform2, inSubShapeIDCreator1, inSubShapeIDCreator2, ioCollector); +} + +void RotatedTranslatedShape::SaveBinaryState(StreamOut &inStream) const +{ + DecoratedShape::SaveBinaryState(inStream); + + inStream.Write(mCenterOfMass); + inStream.Write(mRotation); +} + +void RotatedTranslatedShape::RestoreBinaryState(StreamIn &inStream) +{ + DecoratedShape::RestoreBinaryState(inStream); + + inStream.Read(mCenterOfMass); + inStream.Read(mRotation); + mIsRotationIdentity = mRotation.IsClose(Quat::sIdentity()); +} + +bool RotatedTranslatedShape::IsValidScale(Vec3Arg inScale) const +{ + if (!DecoratedShape::IsValidScale(inScale)) + return false; + + if (mIsRotationIdentity || ScaleHelpers::IsUniformScale(inScale)) + return mInnerShape->IsValidScale(inScale); + + if (!ScaleHelpers::CanScaleBeRotated(mRotation, inScale)) + return false; + + return mInnerShape->IsValidScale(ScaleHelpers::RotateScale(mRotation, inScale)); +} + +void RotatedTranslatedShape::sRegister() +{ + ShapeFunctions &f = ShapeFunctions::sGet(EShapeSubType::RotatedTranslated); + f.mConstruct = []() -> Shape * { return new RotatedTranslatedShape; }; + f.mColor = Color::sBlue; + + for (EShapeSubType s : sAllSubShapeTypes) + { + CollisionDispatch::sRegisterCollideShape(EShapeSubType::RotatedTranslated, s, sCollideRotatedTranslatedVsShape); + CollisionDispatch::sRegisterCollideShape(s, EShapeSubType::RotatedTranslated, sCollideShapeVsRotatedTranslated); + CollisionDispatch::sRegisterCastShape(EShapeSubType::RotatedTranslated, s, sCastRotatedTranslatedVsShape); + CollisionDispatch::sRegisterCastShape(s, EShapeSubType::RotatedTranslated, sCastShapeVsRotatedTranslated); + } + + CollisionDispatch::sRegisterCollideShape(EShapeSubType::RotatedTranslated, EShapeSubType::RotatedTranslated, sCollideRotatedTranslatedVsRotatedTranslated); + CollisionDispatch::sRegisterCastShape(EShapeSubType::RotatedTranslated, EShapeSubType::RotatedTranslated, sCastRotatedTranslatedVsRotatedTranslated); +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/RotatedTranslatedShape.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/RotatedTranslatedShape.h new file mode 100644 index 00000000000..c37dbca82c8 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/RotatedTranslatedShape.h @@ -0,0 +1,158 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +JPH_NAMESPACE_BEGIN + +class CollideShapeSettings; + +/// Class that constructs a RotatedTranslatedShape +class JPH_EXPORT RotatedTranslatedShapeSettings final : public DecoratedShapeSettings +{ +public: + JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, RotatedTranslatedShapeSettings) + + /// Constructor + RotatedTranslatedShapeSettings() = default; + + /// Construct with shape settings, can be serialized. + RotatedTranslatedShapeSettings(Vec3Arg inPosition, QuatArg inRotation, const ShapeSettings *inShape) : DecoratedShapeSettings(inShape), mPosition(inPosition), mRotation(inRotation) { } + + /// Variant that uses a concrete shape, which means this object cannot be serialized. + RotatedTranslatedShapeSettings(Vec3Arg inPosition, QuatArg inRotation, const Shape *inShape): DecoratedShapeSettings(inShape), mPosition(inPosition), mRotation(inRotation) { } + + // See: ShapeSettings + virtual ShapeResult Create() const override; + + Vec3 mPosition; ///< Position of the sub shape + Quat mRotation; ///< Rotation of the sub shape +}; + +/// A rotated translated shape will rotate and translate a child shape. +/// Shifts the child object so that it is centered around the center of mass. +class JPH_EXPORT RotatedTranslatedShape final : public DecoratedShape +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Constructor + RotatedTranslatedShape() : DecoratedShape(EShapeSubType::RotatedTranslated) { } + RotatedTranslatedShape(const RotatedTranslatedShapeSettings &inSettings, ShapeResult &outResult); + RotatedTranslatedShape(Vec3Arg inPosition, QuatArg inRotation, const Shape *inShape); + + /// Access the rotation that is applied to the inner shape + Quat GetRotation() const { return mRotation; } + + /// Access the translation that has been applied to the inner shape + Vec3 GetPosition() const { return mCenterOfMass - mRotation * mInnerShape->GetCenterOfMass(); } + + // See Shape::GetCenterOfMass + virtual Vec3 GetCenterOfMass() const override { return mCenterOfMass; } + + // See Shape::GetLocalBounds + virtual AABox GetLocalBounds() const override; + + // See Shape::GetWorldSpaceBounds + virtual AABox GetWorldSpaceBounds(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale) const override; + using Shape::GetWorldSpaceBounds; + + // See Shape::GetInnerRadius + virtual float GetInnerRadius() const override { return mInnerShape->GetInnerRadius(); } + + // See Shape::GetMassProperties + virtual MassProperties GetMassProperties() const override; + + // See Shape::GetSubShapeTransformedShape + virtual TransformedShape GetSubShapeTransformedShape(const SubShapeID &inSubShapeID, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale, SubShapeID &outRemainder) const override; + + // See Shape::GetSurfaceNormal + virtual Vec3 GetSurfaceNormal(const SubShapeID &inSubShapeID, Vec3Arg inLocalSurfacePosition) const override; + + // See Shape::GetSupportingFace + virtual void GetSupportingFace(const SubShapeID &inSubShapeID, Vec3Arg inDirection, Vec3Arg inScale, Mat44Arg inCenterOfMassTransform, SupportingFace &outVertices) const override; + + // See Shape::GetSubmergedVolume + virtual void GetSubmergedVolume(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, const Plane &inSurface, float &outTotalVolume, float &outSubmergedVolume, Vec3 &outCenterOfBuoyancy JPH_IF_DEBUG_RENDERER(, RVec3Arg inBaseOffset)) const override; + +#ifdef JPH_DEBUG_RENDERER + // See Shape::Draw + virtual void Draw(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inUseMaterialColors, bool inDrawWireframe) const override; + + // See Shape::DrawGetSupportFunction + virtual void DrawGetSupportFunction(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inDrawSupportDirection) const override; + + // See Shape::DrawGetSupportingFace + virtual void DrawGetSupportingFace(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale) const override; +#endif // JPH_DEBUG_RENDERER + + // See Shape::CastRay + virtual bool CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const override; + virtual void CastRay(const RayCast &inRay, const RayCastSettings &inRayCastSettings, const SubShapeIDCreator &inSubShapeIDCreator, CastRayCollector &ioCollector, const ShapeFilter &inShapeFilter = { }) const override; + + // See: Shape::CollidePoint + virtual void CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter = { }) const override; + + // See: Shape::CollideSoftBodyVertices + virtual void CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, SoftBodyVertex *ioVertices, uint inNumVertices, float inDeltaTime, Vec3Arg inDisplacementDueToGravity, int inCollidingShapeIndex) const override; + + // See Shape::CollectTransformedShapes + virtual void CollectTransformedShapes(const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale, const SubShapeIDCreator &inSubShapeIDCreator, TransformedShapeCollector &ioCollector, const ShapeFilter &inShapeFilter) const override; + + // See Shape::TransformShape + virtual void TransformShape(Mat44Arg inCenterOfMassTransform, TransformedShapeCollector &ioCollector) const override; + + // See Shape::GetTrianglesStart + virtual void GetTrianglesStart(GetTrianglesContext &ioContext, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) const override { JPH_ASSERT(false, "Cannot call on non-leaf shapes, use CollectTransformedShapes to collect the leaves first!"); } + + // See Shape::GetTrianglesNext + virtual int GetTrianglesNext(GetTrianglesContext &ioContext, int inMaxTrianglesRequested, Float3 *outTriangleVertices, const PhysicsMaterial **outMaterials = nullptr) const override { JPH_ASSERT(false, "Cannot call on non-leaf shapes, use CollectTransformedShapes to collect the leaves first!"); return 0; } + + // See Shape + virtual void SaveBinaryState(StreamOut &inStream) const override; + + // See Shape::GetStats + virtual Stats GetStats() const override { return Stats(sizeof(*this), 0); } + + // See Shape::GetVolume + virtual float GetVolume() const override { return mInnerShape->GetVolume(); } + + // See Shape::IsValidScale + virtual bool IsValidScale(Vec3Arg inScale) const override; + + // Register shape functions with the registry + static void sRegister(); + +protected: + // See: Shape::RestoreBinaryState + virtual void RestoreBinaryState(StreamIn &inStream) override; + +private: + // Helper functions called by CollisionDispatch + static void sCollideRotatedTranslatedVsShape(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter); + static void sCollideShapeVsRotatedTranslated(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter); + static void sCollideRotatedTranslatedVsRotatedTranslated(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter); + static void sCastRotatedTranslatedVsShape(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector); + static void sCastShapeVsRotatedTranslated(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector); + static void sCastRotatedTranslatedVsRotatedTranslated(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector); + + /// Transform the scale to the local space of the child shape + inline Vec3 TransformScale(Vec3Arg inScale) const + { + // We don't need to transform uniform scale or if the rotation is identity + if (mIsRotationIdentity || ScaleHelpers::IsUniformScale(inScale)) + return inScale; + + return ScaleHelpers::RotateScale(mRotation, inScale); + } + + bool mIsRotationIdentity; ///< If mRotation is close to identity (put here because it falls in padding bytes) + Vec3 mCenterOfMass; ///< Position of the center of mass + Quat mRotation; ///< Rotation of the child shape +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/ScaleHelpers.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/ScaleHelpers.h new file mode 100644 index 00000000000..ba20c251965 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/ScaleHelpers.h @@ -0,0 +1,68 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +/// Helper functions to get properties of a scaling vector +namespace ScaleHelpers +{ + /// The tolerance used to check if components of the scale vector are the same + static constexpr float cScaleToleranceSq = 1.0e-8f; + + /// Test if a scale is identity + inline bool IsNotScaled(Vec3Arg inScale) { return inScale.IsClose(Vec3::sReplicate(1.0f), cScaleToleranceSq); } + + /// Test if a scale is uniform + inline bool IsUniformScale(Vec3Arg inScale) { return inScale.Swizzle().IsClose(inScale, cScaleToleranceSq); } + + /// Scale the convex radius of an object + inline float ScaleConvexRadius(float inConvexRadius, Vec3Arg inScale) { return min(inConvexRadius * inScale.Abs().ReduceMin(), cDefaultConvexRadius); } + + /// Test if a scale flips an object inside out (which requires flipping all normals and polygon windings) + inline bool IsInsideOut(Vec3Arg inScale) { return (CountBits(Vec3::sLess(inScale, Vec3::sZero()).GetTrues() & 0x7) & 1) != 0; } + + /// Get the average scale if inScale, used to make the scale uniform when a shape doesn't support non-uniform scale + inline Vec3 MakeUniformScale(Vec3Arg inScale) { return Vec3::sReplicate((inScale.GetX() + inScale.GetY() + inScale.GetZ()) / 3.0f); } + + /// Checks in scale can be rotated to child shape + /// @param inRotation Rotation of child shape + /// @param inScale Scale in local space of parent shape + /// @return True if the scale is valid (no shearing introduced) + inline bool CanScaleBeRotated(QuatArg inRotation, Vec3Arg inScale) + { + // inScale is a scale in local space of the shape, so the transform for the shape (ignoring translation) is: T = Mat44::sScale(inScale) * mRotation. + // when we pass the scale to the child it needs to be local to the child, so we want T = mRotation * Mat44::sScale(ChildScale). + // Solving for ChildScale: ChildScale = mRotation^-1 * Mat44::sScale(inScale) * mRotation = mRotation^T * Mat44::sScale(inScale) * mRotation + // If any of the off diagonal elements are non-zero, it means the scale / rotation is not compatible. + Mat44 r = Mat44::sRotation(inRotation); + Mat44 child_scale = r.Multiply3x3LeftTransposed(r.PostScaled(inScale)); + + // Get the columns, but zero the diagonal + Vec4 zero = Vec4::sZero(); + Vec4 c0 = Vec4::sSelect(child_scale.GetColumn4(0), zero, UVec4(0xffffffff, 0, 0, 0)).Abs(); + Vec4 c1 = Vec4::sSelect(child_scale.GetColumn4(1), zero, UVec4(0, 0xffffffff, 0, 0)).Abs(); + Vec4 c2 = Vec4::sSelect(child_scale.GetColumn4(2), zero, UVec4(0, 0, 0xffffffff, 0)).Abs(); + + // Check if all elements are less than epsilon + Vec4 epsilon = Vec4::sReplicate(1.0e-6f); + return UVec4::sAnd(UVec4::sAnd(Vec4::sLess(c0, epsilon), Vec4::sLess(c1, epsilon)), Vec4::sLess(c2, epsilon)).TestAllTrue(); + } + + /// Adjust scale for rotated child shape + /// @param inRotation Rotation of child shape + /// @param inScale Scale in local space of parent shape + /// @return Rotated scale + inline Vec3 RotateScale(QuatArg inRotation, Vec3Arg inScale) + { + // Get the diagonal of mRotation^T * Mat44::sScale(inScale) * mRotation (see comment at CanScaleBeRotated) + Mat44 r = Mat44::sRotation(inRotation); + return r.Multiply3x3LeftTransposed(r.PostScaled(inScale)).GetDiagonal3(); + } +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/ScaledShape.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/ScaledShape.cpp new file mode 100644 index 00000000000..10b1c83f3a5 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/ScaledShape.cpp @@ -0,0 +1,226 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(ScaledShapeSettings) +{ + JPH_ADD_BASE_CLASS(ScaledShapeSettings, DecoratedShapeSettings) + + JPH_ADD_ATTRIBUTE(ScaledShapeSettings, mScale) +} + +ShapeSettings::ShapeResult ScaledShapeSettings::Create() const +{ + if (mCachedResult.IsEmpty()) + Ref shape = new ScaledShape(*this, mCachedResult); + return mCachedResult; +} + +ScaledShape::ScaledShape(const ScaledShapeSettings &inSettings, ShapeResult &outResult) : + DecoratedShape(EShapeSubType::Scaled, inSettings, outResult), + mScale(inSettings.mScale) +{ + if (outResult.HasError()) + return; + + outResult.Set(this); +} + +MassProperties ScaledShape::GetMassProperties() const +{ + MassProperties p = mInnerShape->GetMassProperties(); + p.Scale(mScale); + return p; +} + +AABox ScaledShape::GetLocalBounds() const +{ + return mInnerShape->GetLocalBounds().Scaled(mScale); +} + +AABox ScaledShape::GetWorldSpaceBounds(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale) const +{ + return mInnerShape->GetWorldSpaceBounds(inCenterOfMassTransform, inScale * mScale); +} + +TransformedShape ScaledShape::GetSubShapeTransformedShape(const SubShapeID &inSubShapeID, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale, SubShapeID &outRemainder) const +{ + // We don't use any bits in the sub shape ID + outRemainder = inSubShapeID; + + TransformedShape ts(RVec3(inPositionCOM), inRotation, mInnerShape, BodyID()); + ts.SetShapeScale(inScale * mScale); + return ts; +} + +Vec3 ScaledShape::GetSurfaceNormal(const SubShapeID &inSubShapeID, Vec3Arg inLocalSurfacePosition) const +{ + // Transform the surface point to local space and pass the query on + Vec3 normal = mInnerShape->GetSurfaceNormal(inSubShapeID, inLocalSurfacePosition / mScale); + + // Need to transform the plane normals using inScale + // Transforming a direction with matrix M is done through multiplying by (M^-1)^T + // In this case M is a diagonal matrix with the scale vector, so we need to multiply our normal by 1 / scale and renormalize afterwards + return (normal / mScale).Normalized(); +} + +void ScaledShape::GetSupportingFace(const SubShapeID &inSubShapeID, Vec3Arg inDirection, Vec3Arg inScale, Mat44Arg inCenterOfMassTransform, SupportingFace &outVertices) const +{ + mInnerShape->GetSupportingFace(inSubShapeID, inDirection, inScale * mScale, inCenterOfMassTransform, outVertices); +} + +void ScaledShape::GetSubmergedVolume(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, const Plane &inSurface, float &outTotalVolume, float &outSubmergedVolume, Vec3 &outCenterOfBuoyancy JPH_IF_DEBUG_RENDERER(, RVec3Arg inBaseOffset)) const +{ + mInnerShape->GetSubmergedVolume(inCenterOfMassTransform, inScale * mScale, inSurface, outTotalVolume, outSubmergedVolume, outCenterOfBuoyancy JPH_IF_DEBUG_RENDERER(, inBaseOffset)); +} + +#ifdef JPH_DEBUG_RENDERER +void ScaledShape::Draw(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inUseMaterialColors, bool inDrawWireframe) const +{ + mInnerShape->Draw(inRenderer, inCenterOfMassTransform, inScale * mScale, inColor, inUseMaterialColors, inDrawWireframe); +} + +void ScaledShape::DrawGetSupportFunction(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inDrawSupportDirection) const +{ + mInnerShape->DrawGetSupportFunction(inRenderer, inCenterOfMassTransform, inScale * mScale, inColor, inDrawSupportDirection); +} + +void ScaledShape::DrawGetSupportingFace(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale) const +{ + mInnerShape->DrawGetSupportingFace(inRenderer, inCenterOfMassTransform, inScale * mScale); +} +#endif // JPH_DEBUG_RENDERER + +bool ScaledShape::CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const +{ + Vec3 inv_scale = mScale.Reciprocal(); + RayCast scaled_ray { inv_scale * inRay.mOrigin, inv_scale * inRay.mDirection }; + return mInnerShape->CastRay(scaled_ray, inSubShapeIDCreator, ioHit); +} + +void ScaledShape::CastRay(const RayCast &inRay, const RayCastSettings &inRayCastSettings, const SubShapeIDCreator &inSubShapeIDCreator, CastRayCollector &ioCollector, const ShapeFilter &inShapeFilter) const +{ + // Test shape filter + if (!inShapeFilter.ShouldCollide(this, inSubShapeIDCreator.GetID())) + return; + + Vec3 inv_scale = mScale.Reciprocal(); + RayCast scaled_ray { inv_scale * inRay.mOrigin, inv_scale * inRay.mDirection }; + return mInnerShape->CastRay(scaled_ray, inRayCastSettings, inSubShapeIDCreator, ioCollector, inShapeFilter); +} + +void ScaledShape::CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter) const +{ + // Test shape filter + if (!inShapeFilter.ShouldCollide(this, inSubShapeIDCreator.GetID())) + return; + + Vec3 inv_scale = mScale.Reciprocal(); + mInnerShape->CollidePoint(inv_scale * inPoint, inSubShapeIDCreator, ioCollector, inShapeFilter); +} + +void ScaledShape::CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, SoftBodyVertex *ioVertices, uint inNumVertices, float inDeltaTime, Vec3Arg inDisplacementDueToGravity, int inCollidingShapeIndex) const +{ + mInnerShape->CollideSoftBodyVertices(inCenterOfMassTransform, inScale * mScale, ioVertices, inNumVertices, inDeltaTime, inDisplacementDueToGravity, inCollidingShapeIndex); +} + +void ScaledShape::CollectTransformedShapes(const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale, const SubShapeIDCreator &inSubShapeIDCreator, TransformedShapeCollector &ioCollector, const ShapeFilter &inShapeFilter) const +{ + // Test shape filter + if (!inShapeFilter.ShouldCollide(this, inSubShapeIDCreator.GetID())) + return; + + mInnerShape->CollectTransformedShapes(inBox, inPositionCOM, inRotation, inScale * mScale, inSubShapeIDCreator, ioCollector, inShapeFilter); +} + +void ScaledShape::TransformShape(Mat44Arg inCenterOfMassTransform, TransformedShapeCollector &ioCollector) const +{ + mInnerShape->TransformShape(inCenterOfMassTransform * Mat44::sScale(mScale), ioCollector); +} + +void ScaledShape::SaveBinaryState(StreamOut &inStream) const +{ + DecoratedShape::SaveBinaryState(inStream); + + inStream.Write(mScale); +} + +void ScaledShape::RestoreBinaryState(StreamIn &inStream) +{ + DecoratedShape::RestoreBinaryState(inStream); + + inStream.Read(mScale); +} + +float ScaledShape::GetVolume() const +{ + return abs(mScale.GetX() * mScale.GetY() * mScale.GetZ()) * mInnerShape->GetVolume(); +} + +bool ScaledShape::IsValidScale(Vec3Arg inScale) const +{ + return mInnerShape->IsValidScale(inScale * mScale); +} + +void ScaledShape::sCollideScaledVsShape(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter) +{ + JPH_ASSERT(inShape1->GetSubType() == EShapeSubType::Scaled); + const ScaledShape *shape1 = static_cast(inShape1); + + CollisionDispatch::sCollideShapeVsShape(shape1->GetInnerShape(), inShape2, inScale1 * shape1->GetScale(), inScale2, inCenterOfMassTransform1, inCenterOfMassTransform2, inSubShapeIDCreator1, inSubShapeIDCreator2, inCollideShapeSettings, ioCollector, inShapeFilter); +} + +void ScaledShape::sCollideShapeVsScaled(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter) +{ + JPH_ASSERT(inShape2->GetSubType() == EShapeSubType::Scaled); + const ScaledShape *shape2 = static_cast(inShape2); + + CollisionDispatch::sCollideShapeVsShape(inShape1, shape2->GetInnerShape(), inScale1, inScale2 * shape2->GetScale(), inCenterOfMassTransform1, inCenterOfMassTransform2, inSubShapeIDCreator1, inSubShapeIDCreator2, inCollideShapeSettings, ioCollector, inShapeFilter); +} + +void ScaledShape::sCastScaledVsShape(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector) +{ + JPH_ASSERT(inShapeCast.mShape->GetSubType() == EShapeSubType::Scaled); + const ScaledShape *shape = static_cast(inShapeCast.mShape); + + ShapeCast scaled_cast(shape->GetInnerShape(), inShapeCast.mScale * shape->GetScale(), inShapeCast.mCenterOfMassStart, inShapeCast.mDirection); + CollisionDispatch::sCastShapeVsShapeLocalSpace(scaled_cast, inShapeCastSettings, inShape, inScale, inShapeFilter, inCenterOfMassTransform2, inSubShapeIDCreator1, inSubShapeIDCreator2, ioCollector); +} + +void ScaledShape::sCastShapeVsScaled(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector) +{ + JPH_ASSERT(inShape->GetSubType() == EShapeSubType::Scaled); + const ScaledShape *shape = static_cast(inShape); + + CollisionDispatch::sCastShapeVsShapeLocalSpace(inShapeCast, inShapeCastSettings, shape->mInnerShape, inScale * shape->mScale, inShapeFilter, inCenterOfMassTransform2, inSubShapeIDCreator1, inSubShapeIDCreator2, ioCollector); +} + +void ScaledShape::sRegister() +{ + ShapeFunctions &f = ShapeFunctions::sGet(EShapeSubType::Scaled); + f.mConstruct = []() -> Shape * { return new ScaledShape; }; + f.mColor = Color::sYellow; + + for (EShapeSubType s : sAllSubShapeTypes) + { + CollisionDispatch::sRegisterCollideShape(EShapeSubType::Scaled, s, sCollideScaledVsShape); + CollisionDispatch::sRegisterCollideShape(s, EShapeSubType::Scaled, sCollideShapeVsScaled); + CollisionDispatch::sRegisterCastShape(EShapeSubType::Scaled, s, sCastScaledVsShape); + CollisionDispatch::sRegisterCastShape(s, EShapeSubType::Scaled, sCastShapeVsScaled); + } +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/ScaledShape.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/ScaledShape.h new file mode 100644 index 00000000000..ea9b3435175 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/ScaledShape.h @@ -0,0 +1,140 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +class SubShapeIDCreator; +class CollideShapeSettings; + +/// Class that constructs a ScaledShape +class JPH_EXPORT ScaledShapeSettings final : public DecoratedShapeSettings +{ + JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, ScaledShapeSettings) + + /// Default constructor for deserialization + ScaledShapeSettings() = default; + + /// Constructor that decorates another shape with a scale + ScaledShapeSettings(const ShapeSettings *inShape, Vec3Arg inScale) : DecoratedShapeSettings(inShape), mScale(inScale) { } + + /// Variant that uses a concrete shape, which means this object cannot be serialized. + ScaledShapeSettings(const Shape *inShape, Vec3Arg inScale) : DecoratedShapeSettings(inShape), mScale(inScale) { } + + // See: ShapeSettings + virtual ShapeResult Create() const override; + + Vec3 mScale = Vec3(1, 1, 1); +}; + +/// A shape that scales a child shape in local space of that shape. The scale can be non-uniform and can even turn it inside out when one or three components of the scale are negative. +class JPH_EXPORT ScaledShape final : public DecoratedShape +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Constructor + ScaledShape() : DecoratedShape(EShapeSubType::Scaled) { } + ScaledShape(const ScaledShapeSettings &inSettings, ShapeResult &outResult); + + /// Constructor that decorates another shape with a scale + ScaledShape(const Shape *inShape, Vec3Arg inScale) : DecoratedShape(EShapeSubType::Scaled, inShape), mScale(inScale) { } + + /// Get the scale + Vec3 GetScale() const { return mScale; } + + // See Shape::GetCenterOfMass + virtual Vec3 GetCenterOfMass() const override { return mScale * mInnerShape->GetCenterOfMass(); } + + // See Shape::GetLocalBounds + virtual AABox GetLocalBounds() const override; + + // See Shape::GetWorldSpaceBounds + virtual AABox GetWorldSpaceBounds(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale) const override; + using Shape::GetWorldSpaceBounds; + + // See Shape::GetInnerRadius + virtual float GetInnerRadius() const override { return mScale.ReduceMin() * mInnerShape->GetInnerRadius(); } + + // See Shape::GetMassProperties + virtual MassProperties GetMassProperties() const override; + + // See Shape::GetSubShapeTransformedShape + virtual TransformedShape GetSubShapeTransformedShape(const SubShapeID &inSubShapeID, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale, SubShapeID &outRemainder) const override; + + // See Shape::GetSurfaceNormal + virtual Vec3 GetSurfaceNormal(const SubShapeID &inSubShapeID, Vec3Arg inLocalSurfacePosition) const override; + + // See Shape::GetSupportingFace + virtual void GetSupportingFace(const SubShapeID &inSubShapeID, Vec3Arg inDirection, Vec3Arg inScale, Mat44Arg inCenterOfMassTransform, SupportingFace &outVertices) const override; + + // See Shape::GetSubmergedVolume + virtual void GetSubmergedVolume(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, const Plane &inSurface, float &outTotalVolume, float &outSubmergedVolume, Vec3 &outCenterOfBuoyancy JPH_IF_DEBUG_RENDERER(, RVec3Arg inBaseOffset)) const override; + +#ifdef JPH_DEBUG_RENDERER + // See Shape::Draw + virtual void Draw(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inUseMaterialColors, bool inDrawWireframe) const override; + + // See Shape::DrawGetSupportFunction + virtual void DrawGetSupportFunction(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inDrawSupportDirection) const override; + + // See Shape::DrawGetSupportingFace + virtual void DrawGetSupportingFace(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale) const override; +#endif // JPH_DEBUG_RENDERER + + // See Shape::CastRay + virtual bool CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const override; + virtual void CastRay(const RayCast &inRay, const RayCastSettings &inRayCastSettings, const SubShapeIDCreator &inSubShapeIDCreator, CastRayCollector &ioCollector, const ShapeFilter &inShapeFilter = { }) const override; + + // See: Shape::CollidePoint + virtual void CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter = { }) const override; + + // See: Shape::CollideSoftBodyVertices + virtual void CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, SoftBodyVertex *ioVertices, uint inNumVertices, float inDeltaTime, Vec3Arg inDisplacementDueToGravity, int inCollidingShapeIndex) const override; + + // See Shape::CollectTransformedShapes + virtual void CollectTransformedShapes(const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale, const SubShapeIDCreator &inSubShapeIDCreator, TransformedShapeCollector &ioCollector, const ShapeFilter &inShapeFilter) const override; + + // See Shape::TransformShape + virtual void TransformShape(Mat44Arg inCenterOfMassTransform, TransformedShapeCollector &ioCollector) const override; + + // See Shape::GetTrianglesStart + virtual void GetTrianglesStart(GetTrianglesContext &ioContext, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) const override { JPH_ASSERT(false, "Cannot call on non-leaf shapes, use CollectTransformedShapes to collect the leaves first!"); } + + // See Shape::GetTrianglesNext + virtual int GetTrianglesNext(GetTrianglesContext &ioContext, int inMaxTrianglesRequested, Float3 *outTriangleVertices, const PhysicsMaterial **outMaterials = nullptr) const override { JPH_ASSERT(false, "Cannot call on non-leaf shapes, use CollectTransformedShapes to collect the leaves first!"); return 0; } + + // See Shape + virtual void SaveBinaryState(StreamOut &inStream) const override; + + // See Shape::GetStats + virtual Stats GetStats() const override { return Stats(sizeof(*this), 0); } + + // See Shape::GetVolume + virtual float GetVolume() const override; + + // See Shape::IsValidScale + virtual bool IsValidScale(Vec3Arg inScale) const override; + + // Register shape functions with the registry + static void sRegister(); + +protected: + // See: Shape::RestoreBinaryState + virtual void RestoreBinaryState(StreamIn &inStream) override; + +private: + // Helper functions called by CollisionDispatch + static void sCollideScaledVsShape(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter); + static void sCollideShapeVsScaled(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter); + static void sCastScaledVsShape(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector); + static void sCastShapeVsScaled(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector); + + Vec3 mScale = Vec3(1, 1, 1); +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/Shape.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/Shape.cpp new file mode 100644 index 00000000000..8412156f7b6 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/Shape.cpp @@ -0,0 +1,309 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +JPH_IMPLEMENT_SERIALIZABLE_ABSTRACT_BASE(ShapeSettings) +{ + JPH_ADD_BASE_CLASS(ShapeSettings, SerializableObject) + + JPH_ADD_ATTRIBUTE(ShapeSettings, mUserData) +} + +#ifdef JPH_DEBUG_RENDERER +bool Shape::sDrawSubmergedVolumes = false; +#endif // JPH_DEBUG_RENDERER + +ShapeFunctions ShapeFunctions::sRegistry[NumSubShapeTypes]; + +TransformedShape Shape::GetSubShapeTransformedShape(const SubShapeID &inSubShapeID, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale, SubShapeID &outRemainder) const +{ + // We have reached the leaf shape so there is no remainder + outRemainder = SubShapeID(); + + // Just return the transformed shape for this shape + TransformedShape ts(RVec3(inPositionCOM), inRotation, this, BodyID()); + ts.SetShapeScale(inScale); + return ts; +} + +void Shape::CollectTransformedShapes(const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale, const SubShapeIDCreator &inSubShapeIDCreator, TransformedShapeCollector &ioCollector, const ShapeFilter &inShapeFilter) const +{ + // Test shape filter + if (!inShapeFilter.ShouldCollide(this, inSubShapeIDCreator.GetID())) + return; + + TransformedShape ts(RVec3(inPositionCOM), inRotation, this, TransformedShape::sGetBodyID(ioCollector.GetContext()), inSubShapeIDCreator); + ts.SetShapeScale(inScale); + ioCollector.AddHit(ts); +} + +void Shape::TransformShape(Mat44Arg inCenterOfMassTransform, TransformedShapeCollector &ioCollector) const +{ + Vec3 scale; + Mat44 transform = inCenterOfMassTransform.Decompose(scale); + TransformedShape ts(RVec3(transform.GetTranslation()), transform.GetQuaternion(), this, BodyID(), SubShapeIDCreator()); + ts.SetShapeScale(scale); + ioCollector.AddHit(ts); +} + +void Shape::SaveBinaryState(StreamOut &inStream) const +{ + inStream.Write(mShapeSubType); + inStream.Write(mUserData); +} + +void Shape::RestoreBinaryState(StreamIn &inStream) +{ + // Type hash read by sRestoreFromBinaryState + inStream.Read(mUserData); +} + +Shape::ShapeResult Shape::sRestoreFromBinaryState(StreamIn &inStream) +{ + ShapeResult result; + + // Read the type of the shape + EShapeSubType shape_sub_type; + inStream.Read(shape_sub_type); + if (inStream.IsEOF() || inStream.IsFailed()) + { + result.SetError("Failed to read type id"); + return result; + } + + // Construct and read the data of the shape + Ref shape = ShapeFunctions::sGet(shape_sub_type).mConstruct(); + shape->RestoreBinaryState(inStream); + if (inStream.IsEOF() || inStream.IsFailed()) + { + result.SetError("Failed to restore shape"); + return result; + } + + result.Set(shape); + return result; +} + +void Shape::SaveWithChildren(StreamOut &inStream, ShapeToIDMap &ioShapeMap, MaterialToIDMap &ioMaterialMap) const +{ + ShapeToIDMap::const_iterator shape_id_iter = ioShapeMap.find(this); + if (shape_id_iter == ioShapeMap.end()) + { + // Write shape ID of this shape + uint32 shape_id = (uint32)ioShapeMap.size(); + ioShapeMap[this] = shape_id; + inStream.Write(shape_id); + + // Write the shape itself + SaveBinaryState(inStream); + + // Write the ID's of all sub shapes + ShapeList sub_shapes; + SaveSubShapeState(sub_shapes); + inStream.Write(sub_shapes.size()); + for (const Shape *shape : sub_shapes) + { + if (shape == nullptr) + inStream.Write(~uint32(0)); + else + shape->SaveWithChildren(inStream, ioShapeMap, ioMaterialMap); + } + + // Write the materials + PhysicsMaterialList materials; + SaveMaterialState(materials); + StreamUtils::SaveObjectArray(inStream, materials, &ioMaterialMap); + } + else + { + // Known shape, just write the ID + inStream.Write(shape_id_iter->second); + } +} + +Shape::ShapeResult Shape::sRestoreWithChildren(StreamIn &inStream, IDToShapeMap &ioShapeMap, IDToMaterialMap &ioMaterialMap) +{ + ShapeResult result; + + // Read ID of this shape + uint32 shape_id; + inStream.Read(shape_id); + if (inStream.IsEOF() || inStream.IsFailed()) + { + result.SetError("Failed to read shape id"); + return result; + } + + // Check nullptr shape + if (shape_id == ~uint32(0)) + { + result.Set(nullptr); + return result; + } + + // Check if we already read this shape + if (shape_id < ioShapeMap.size()) + { + result.Set(ioShapeMap[shape_id]); + return result; + } + + // Read the shape + result = sRestoreFromBinaryState(inStream); + if (result.HasError()) + return result; + JPH_ASSERT(ioShapeMap.size() == shape_id); // Assert that this is the next ID in the map + ioShapeMap.push_back(result.Get()); + + // Read the sub shapes + size_t len; + inStream.Read(len); + if (inStream.IsEOF() || inStream.IsFailed()) + { + result.SetError("Failed to read stream"); + return result; + } + ShapeList sub_shapes; + sub_shapes.reserve(len); + for (size_t i = 0; i < len; ++i) + { + ShapeResult sub_shape_result = sRestoreWithChildren(inStream, ioShapeMap, ioMaterialMap); + if (sub_shape_result.HasError()) + return sub_shape_result; + sub_shapes.push_back(sub_shape_result.Get()); + } + result.Get()->RestoreSubShapeState(sub_shapes.data(), (uint)sub_shapes.size()); + + // Read the materials + Result mlresult = StreamUtils::RestoreObjectArray(inStream, ioMaterialMap); + if (mlresult.HasError()) + { + result.SetError(mlresult.GetError()); + return result; + } + const PhysicsMaterialList &materials = mlresult.Get(); + result.Get()->RestoreMaterialState(materials.data(), (uint)materials.size()); + + return result; +} + +Shape::Stats Shape::GetStatsRecursive(VisitedShapes &ioVisitedShapes) const +{ + Stats stats = GetStats(); + + // If shape is already visited, don't count its size again + if (!ioVisitedShapes.insert(this).second) + stats.mSizeBytes = 0; + + return stats; +} + +Shape::ShapeResult Shape::ScaleShape(Vec3Arg inScale) const +{ + const Vec3 unit_scale = Vec3::sReplicate(1.0f); + + if (inScale.IsNearZero()) + { + ShapeResult result; + result.SetError("Can't use zero scale!"); + return result; + } + + // First test if we can just wrap this shape in a scaled shape + if (IsValidScale(inScale)) + { + // Test if the scale is near unit + ShapeResult result; + if (inScale.IsClose(unit_scale)) + result.Set(const_cast(this)); + else + result.Set(new ScaledShape(this, inScale)); + return result; + } + + // Collect the leaf shapes and their transforms + struct Collector : TransformedShapeCollector + { + virtual void AddHit(const ResultType &inResult) override + { + mShapes.push_back(inResult); + } + + Array mShapes; + }; + Collector collector; + TransformShape(Mat44::sScale(inScale) * Mat44::sTranslation(GetCenterOfMass()), collector); + + // Construct a compound shape + StaticCompoundShapeSettings compound; + compound.mSubShapes.reserve(collector.mShapes.size()); + for (const TransformedShape &ts : collector.mShapes) + { + const Shape *shape = ts.mShape; + + // Construct a scaled shape if scale is not unit + Vec3 scale = ts.GetShapeScale(); + if (!scale.IsClose(unit_scale)) + shape = new ScaledShape(shape, scale); + + // Add the shape + compound.AddShape(Vec3(ts.mShapePositionCOM) - ts.mShapeRotation * shape->GetCenterOfMass(), ts.mShapeRotation, shape); + } + + return compound.Create(); +} + +void Shape::sCollidePointUsingRayCast(const Shape &inShape, Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter) +{ + // First test if we're inside our bounding box + AABox bounds = inShape.GetLocalBounds(); + if (bounds.Contains(inPoint)) + { + // A collector that just counts the number of hits + class HitCountCollector : public CastRayCollector + { + public: + virtual void AddHit(const RayCastResult &inResult) override + { + // Store the last sub shape ID so that we can provide something to our outer hit collector + mSubShapeID = inResult.mSubShapeID2; + + ++mHitCount; + } + + int mHitCount = 0; + SubShapeID mSubShapeID; + }; + HitCountCollector collector; + + // Configure the raycast + RayCastSettings settings; + settings.mBackFaceMode = EBackFaceMode::CollideWithBackFaces; + + // Cast a ray that's 10% longer than the height of our bounding box + inShape.CastRay(RayCast { inPoint, 1.1f * bounds.GetSize().GetY() * Vec3::sAxisY() }, settings, inSubShapeIDCreator, collector, inShapeFilter); + + // Odd amount of hits means inside + if ((collector.mHitCount & 1) == 1) + ioCollector.AddHit({ TransformedShape::sGetBodyID(ioCollector.GetContext()), collector.mSubShapeID }); + } +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/Shape.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/Shape.h new file mode 100644 index 00000000000..f7abc1e68c1 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/Shape.h @@ -0,0 +1,447 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +struct RayCast; +class RayCastSettings; +struct ShapeCast; +class ShapeCastSettings; +class RayCastResult; +class ShapeCastResult; +class CollidePointResult; +class CollideShapeResult; +class SubShapeIDCreator; +class SubShapeID; +class PhysicsMaterial; +class TransformedShape; +class Plane; +class SoftBodyVertex; +class Shape; +class StreamOut; +class StreamIn; +#ifdef JPH_DEBUG_RENDERER +class DebugRenderer; +#endif // JPH_DEBUG_RENDERER + +using CastRayCollector = CollisionCollector; +using CastShapeCollector = CollisionCollector; +using CollidePointCollector = CollisionCollector; +using CollideShapeCollector = CollisionCollector; +using TransformedShapeCollector = CollisionCollector; + +using ShapeRefC = RefConst; +using ShapeList = Array; +using PhysicsMaterialRefC = RefConst; +using PhysicsMaterialList = Array; + +/// Shapes are categorized in groups, each shape can return which group it belongs to through its Shape::GetType function. +enum class EShapeType : uint8 +{ + Convex, ///< Used by ConvexShape, all shapes that use the generic convex vs convex collision detection system (box, sphere, capsule, tapered capsule, cylinder, triangle) + Compound, ///< Used by CompoundShape + Decorated, ///< Used by DecoratedShape + Mesh, ///< Used by MeshShape + HeightField, ///< Used by HeightFieldShape + SoftBody, ///< Used by SoftBodyShape + + // User defined shapes + User1, + User2, + User3, + User4, +}; + +/// This enumerates all shape types, each shape can return its type through Shape::GetSubType +enum class EShapeSubType : uint8 +{ + // Convex shapes + Sphere, + Box, + Triangle, + Capsule, + TaperedCapsule, + Cylinder, + ConvexHull, + + // Compound shapes + StaticCompound, + MutableCompound, + + // Decorated shapes + RotatedTranslated, + Scaled, + OffsetCenterOfMass, + + // Other shapes + Mesh, + HeightField, + SoftBody, + + // User defined shapes + User1, + User2, + User3, + User4, + User5, + User6, + User7, + User8, + + // User defined convex shapes + UserConvex1, + UserConvex2, + UserConvex3, + UserConvex4, + UserConvex5, + UserConvex6, + UserConvex7, + UserConvex8, +}; + +// Sets of shape sub types +static constexpr EShapeSubType sAllSubShapeTypes[] = { EShapeSubType::Sphere, EShapeSubType::Box, EShapeSubType::Triangle, EShapeSubType::Capsule, EShapeSubType::TaperedCapsule, EShapeSubType::Cylinder, EShapeSubType::ConvexHull, EShapeSubType::StaticCompound, EShapeSubType::MutableCompound, EShapeSubType::RotatedTranslated, EShapeSubType::Scaled, EShapeSubType::OffsetCenterOfMass, EShapeSubType::Mesh, EShapeSubType::HeightField, EShapeSubType::SoftBody, EShapeSubType::User1, EShapeSubType::User2, EShapeSubType::User3, EShapeSubType::User4, EShapeSubType::User5, EShapeSubType::User6, EShapeSubType::User7, EShapeSubType::User8, EShapeSubType::UserConvex1, EShapeSubType::UserConvex2, EShapeSubType::UserConvex3, EShapeSubType::UserConvex4, EShapeSubType::UserConvex5, EShapeSubType::UserConvex6, EShapeSubType::UserConvex7, EShapeSubType::UserConvex8 }; +static constexpr EShapeSubType sConvexSubShapeTypes[] = { EShapeSubType::Sphere, EShapeSubType::Box, EShapeSubType::Triangle, EShapeSubType::Capsule, EShapeSubType::TaperedCapsule, EShapeSubType::Cylinder, EShapeSubType::ConvexHull, EShapeSubType::UserConvex1, EShapeSubType::UserConvex2, EShapeSubType::UserConvex3, EShapeSubType::UserConvex4, EShapeSubType::UserConvex5, EShapeSubType::UserConvex6, EShapeSubType::UserConvex7, EShapeSubType::UserConvex8 }; +static constexpr EShapeSubType sCompoundSubShapeTypes[] = { EShapeSubType::StaticCompound, EShapeSubType::MutableCompound }; +static constexpr EShapeSubType sDecoratorSubShapeTypes[] = { EShapeSubType::RotatedTranslated, EShapeSubType::Scaled, EShapeSubType::OffsetCenterOfMass }; + +/// How many shape types we support +static constexpr uint NumSubShapeTypes = uint(size(sAllSubShapeTypes)); + +/// Names of sub shape types +static constexpr const char *sSubShapeTypeNames[] = { "Sphere", "Box", "Triangle", "Capsule", "TaperedCapsule", "Cylinder", "ConvexHull", "StaticCompound", "MutableCompound", "RotatedTranslated", "Scaled", "OffsetCenterOfMass", "Mesh", "HeightField", "SoftBody", "User1", "User2", "User3", "User4", "User5", "User6", "User7", "User8", "UserConvex1", "UserConvex2", "UserConvex3", "UserConvex4", "UserConvex5", "UserConvex6", "UserConvex7", "UserConvex8" }; +static_assert(size(sSubShapeTypeNames) == NumSubShapeTypes); + +/// Class that can construct shapes and that is serializable using the ObjectStream system. +/// Can be used to store shape data in 'uncooked' form (i.e. in a form that is still human readable and authorable). +/// Once the shape has been created using the Create() function, the data will be moved into the Shape class +/// in a form that is optimized for collision detection. After this, the ShapeSettings object is no longer needed +/// and can be destroyed. Each shape class has a derived class of the ShapeSettings object to store shape specific +/// data. +class JPH_EXPORT ShapeSettings : public SerializableObject, public RefTarget +{ +public: + JPH_DECLARE_SERIALIZABLE_ABSTRACT(JPH_EXPORT, ShapeSettings) + + using ShapeResult = Result>; + + /// Create a shape according to the settings specified by this object. + virtual ShapeResult Create() const = 0; + + /// When creating a shape, the result is cached so that calling Create() again will return the same shape. + /// If you make changes to the ShapeSettings you need to call this function to clear the cached result to allow Create() to build a new shape. + void ClearCachedResult() { mCachedResult.Clear(); } + + /// User data (to be used freely by the application) + uint64 mUserData = 0; + +protected: + mutable ShapeResult mCachedResult; +}; + +/// Function table for functions on shapes +class JPH_EXPORT ShapeFunctions +{ +public: + /// Construct a shape + Shape * (*mConstruct)() = nullptr; + + /// Color of the shape when drawing + Color mColor = Color::sBlack; + + /// Get an entry in the registry for a particular sub type + static inline ShapeFunctions & sGet(EShapeSubType inSubType) { return sRegistry[int(inSubType)]; } + +private: + static ShapeFunctions sRegistry[NumSubShapeTypes]; +}; + +/// Base class for all shapes (collision volume of a body). Defines a virtual interface for collision detection. +class JPH_EXPORT Shape : public RefTarget, public NonCopyable +{ +public: + JPH_OVERRIDE_NEW_DELETE + + using ShapeResult = ShapeSettings::ShapeResult; + + /// Constructor + Shape(EShapeType inType, EShapeSubType inSubType) : mShapeType(inType), mShapeSubType(inSubType) { } + Shape(EShapeType inType, EShapeSubType inSubType, const ShapeSettings &inSettings, [[maybe_unused]] ShapeResult &outResult) : mUserData(inSettings.mUserData), mShapeType(inType), mShapeSubType(inSubType) { } + + /// Destructor + virtual ~Shape() = default; + + /// Get type + inline EShapeType GetType() const { return mShapeType; } + inline EShapeSubType GetSubType() const { return mShapeSubType; } + + /// User data (to be used freely by the application) + uint64 GetUserData() const { return mUserData; } + void SetUserData(uint64 inUserData) { mUserData = inUserData; } + + /// Check if this shape can only be used to create a static body or if it can also be dynamic/kinematic + virtual bool MustBeStatic() const { return false; } + + /// All shapes are centered around their center of mass. This function returns the center of mass position that needs to be applied to transform the shape to where it was created. + virtual Vec3 GetCenterOfMass() const { return Vec3::sZero(); } + + /// Get local bounding box including convex radius, this box is centered around the center of mass rather than the world transform + virtual AABox GetLocalBounds() const = 0; + + /// Get the max number of sub shape ID bits that are needed to be able to address any leaf shape in this shape. Used mainly for checking that it is smaller or equal than SubShapeID::MaxBits. + virtual uint GetSubShapeIDBitsRecursive() const = 0; + + /// Get world space bounds including convex radius. + /// This shape is scaled by inScale in local space first. + /// This function can be overridden to return a closer fitting world space bounding box, by default it will just transform what GetLocalBounds() returns. + virtual AABox GetWorldSpaceBounds(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale) const { return GetLocalBounds().Scaled(inScale).Transformed(inCenterOfMassTransform); } + + /// Get world space bounds including convex radius. + AABox GetWorldSpaceBounds(DMat44Arg inCenterOfMassTransform, Vec3Arg inScale) const + { + // Use single precision version using the rotation only + AABox bounds = GetWorldSpaceBounds(inCenterOfMassTransform.GetRotation(), inScale); + + // Apply translation + bounds.Translate(inCenterOfMassTransform.GetTranslation()); + + return bounds; + } + + /// Returns the radius of the biggest sphere that fits entirely in the shape. In case this shape consists of multiple sub shapes, it returns the smallest sphere of the parts. + /// This can be used as a measure of how far the shape can be moved without risking going through geometry. + virtual float GetInnerRadius() const = 0; + + /// Calculate the mass and inertia of this shape + virtual MassProperties GetMassProperties() const = 0; + + /// Get the material assigned to a particular sub shape ID + virtual const PhysicsMaterial * GetMaterial(const SubShapeID &inSubShapeID) const = 0; + + /// Get the surface normal of a particular sub shape ID and point on surface (all vectors are relative to center of mass for this shape). + /// Note: When you have a CollideShapeResult or ShapeCastResult you should use -mPenetrationAxis.Normalized() as contact normal as GetSurfaceNormal will only return face normals (and not vertex or edge normals). + virtual Vec3 GetSurfaceNormal(const SubShapeID &inSubShapeID, Vec3Arg inLocalSurfacePosition) const = 0; + + /// Type definition for a supporting face + using SupportingFace = StaticArray; + + /// Get the vertices of the face that faces inDirection the most (includes any convex radius). Note that this function can only return faces of + /// convex shapes or triangles, which is why a sub shape ID to get to that leaf must be provided. + /// @param inSubShapeID Sub shape ID of target shape + /// @param inDirection Direction that the face should be facing (in local space to this shape) + /// @param inCenterOfMassTransform Transform to transform outVertices with + /// @param inScale Scale of this shape + /// @param outVertices Resulting face. The returned face can be empty if the shape doesn't have polygons to return (e.g. because it's a sphere). The face will be returned in world space. + virtual void GetSupportingFace([[maybe_unused]] const SubShapeID &inSubShapeID, [[maybe_unused]] Vec3Arg inDirection, [[maybe_unused]] Vec3Arg inScale, [[maybe_unused]] Mat44Arg inCenterOfMassTransform, [[maybe_unused]] SupportingFace &outVertices) const { /* Nothing */ } + + /// Get the user data of a particular sub shape ID + virtual uint64 GetSubShapeUserData([[maybe_unused]] const SubShapeID &inSubShapeID) const { return mUserData; } + + /// Get the direct child sub shape and its transform for a sub shape ID. + /// @param inSubShapeID Sub shape ID that indicates the path to the leaf shape + /// @param inPositionCOM The position of the center of mass of this shape + /// @param inRotation The orientation of this shape + /// @param inScale Scale of this shape + /// @param outRemainder The remainder of the sub shape ID after removing the sub shape + /// @return Direct child sub shape and its transform, note that the body ID and sub shape ID will be invalid + virtual TransformedShape GetSubShapeTransformedShape(const SubShapeID &inSubShapeID, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale, SubShapeID &outRemainder) const; + + /// Gets the properties needed to do buoyancy calculations for a body using this shape + /// @param inCenterOfMassTransform Transform that takes this shape (centered around center of mass) to world space (or a desired other space) + /// @param inScale Scale in local space of the shape + /// @param inSurface The surface plane of the liquid relative to inCenterOfMassTransform + /// @param outTotalVolume On return this contains the total volume of the shape + /// @param outSubmergedVolume On return this contains the submerged volume of the shape + /// @param outCenterOfBuoyancy On return this contains the world space center of mass of the submerged volume +#ifdef JPH_DEBUG_RENDERER + /// @param inBaseOffset The offset to transform inCenterOfMassTransform to world space (in double precision mode this can be used to shift the whole operation closer to the origin). Only used for debug drawing. +#endif + virtual void GetSubmergedVolume(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, const Plane &inSurface, float &outTotalVolume, float &outSubmergedVolume, Vec3 &outCenterOfBuoyancy +#ifdef JPH_DEBUG_RENDERER // Not using JPH_IF_DEBUG_RENDERER for Doxygen + , RVec3Arg inBaseOffset +#endif + ) const = 0; + +#ifdef JPH_DEBUG_RENDERER + /// Draw the shape at a particular location with a particular color (debugging purposes) + virtual void Draw(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inUseMaterialColors, bool inDrawWireframe) const = 0; + + /// Draw the results of the GetSupportFunction with the convex radius added back on to show any errors introduced by this process (only relevant for convex shapes) + virtual void DrawGetSupportFunction([[maybe_unused]] DebugRenderer *inRenderer, [[maybe_unused]] RMat44Arg inCenterOfMassTransform, [[maybe_unused]] Vec3Arg inScale, [[maybe_unused]] ColorArg inColor, [[maybe_unused]] bool inDrawSupportDirection) const { /* Only implemented for convex shapes */ } + + /// Draw the results of the GetSupportingFace function to show any errors introduced by this process (only relevant for convex shapes) + virtual void DrawGetSupportingFace([[maybe_unused]] DebugRenderer *inRenderer, [[maybe_unused]] RMat44Arg inCenterOfMassTransform, [[maybe_unused]] Vec3Arg inScale) const { /* Only implemented for convex shapes */ } +#endif // JPH_DEBUG_RENDERER + + /// Cast a ray against this shape, returns true if it finds a hit closer than ioHit.mFraction and updates that fraction. Otherwise ioHit is left untouched and the function returns false. + /// Note that the ray should be relative to the center of mass of this shape (i.e. subtract Shape::GetCenterOfMass() from RayCast::mOrigin if you want to cast against the shape in the space it was created). + /// Convex objects will be treated as solid (meaning if the ray starts inside, you'll get a hit fraction of 0) and back face hits against triangles are returned. + /// If you want the surface normal of the hit use GetSurfaceNormal(ioHit.mSubShapeID2, inRay.GetPointOnRay(ioHit.mFraction)). + virtual bool CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const = 0; + + /// Cast a ray against this shape. Allows returning multiple hits through ioCollector. Note that this version is more flexible but also slightly slower than the CastRay function that returns only a single hit. + /// If you want the surface normal of the hit use GetSurfaceNormal(collected sub shape ID, inRay.GetPointOnRay(collected faction)). + virtual void CastRay(const RayCast &inRay, const RayCastSettings &inRayCastSettings, const SubShapeIDCreator &inSubShapeIDCreator, CastRayCollector &ioCollector, const ShapeFilter &inShapeFilter = { }) const = 0; + + /// Check if inPoint is inside this shape. For this tests all shapes are treated as if they were solid. + /// Note that inPoint should be relative to the center of mass of this shape (i.e. subtract Shape::GetCenterOfMass() from inPoint if you want to test against the shape in the space it was created). + /// For a mesh shape, this test will only provide sensible information if the mesh is a closed manifold. + /// For each shape that collides, ioCollector will receive a hit. + virtual void CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter = { }) const = 0; + + /// Collides all vertices of a soft body with this shape and updates SoftBodyVertex::mCollisionPlane, SoftBodyVertex::mCollidingShapeIndex and SoftBodyVertex::mLargestPenetration if a collision with more penetration was found. + /// @param inCenterOfMassTransform Center of mass transform for this shape relative to the vertices. + /// @param inScale The scale to use for this shape + /// @param ioVertices The vertices of the soft body + /// @param inNumVertices The number of vertices in ioVertices + /// @param inDeltaTime Delta time of this time step (can be used to extrapolate the position using the velocity of the particle) + /// @param inDisplacementDueToGravity Displacement due to gravity during this time step + /// @param inCollidingShapeIndex Value to store in SoftBodyVertex::mCollidingShapeIndex when a collision was found + virtual void CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, SoftBodyVertex *ioVertices, uint inNumVertices, float inDeltaTime, Vec3Arg inDisplacementDueToGravity, int inCollidingShapeIndex) const = 0; + + /// Collect the leaf transformed shapes of all leaf shapes of this shape. + /// inBox is the world space axis aligned box which leaf shapes should collide with. + /// inPositionCOM/inRotation/inScale describes the transform of this shape. + /// inSubShapeIDCeator represents the current sub shape ID of this shape. + virtual void CollectTransformedShapes(const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale, const SubShapeIDCreator &inSubShapeIDCreator, TransformedShapeCollector &ioCollector, const ShapeFilter &inShapeFilter) const; + + /// Transforms this shape and all of its children with inTransform, resulting shape(s) are passed to ioCollector. + /// Note that not all shapes support all transforms (especially true for scaling), the resulting shape will try to match the transform as accurately as possible. + /// @param inCenterOfMassTransform The transform (rotation, translation, scale) that the center of mass of the shape should get + /// @param ioCollector The transformed shapes will be passed to this collector + virtual void TransformShape(Mat44Arg inCenterOfMassTransform, TransformedShapeCollector &ioCollector) const; + + /// Scale this shape. Note that not all shapes support all scales, this will return a shape that matches the scale as accurately as possible. See Shape::IsValidScale for more information. + /// @param inScale The scale to use for this shape (note: this scale is applied to the entire shape in the space it was created, most other functions apply the scale in the space of the leaf shapes and from the center of mass!) + ShapeResult ScaleShape(Vec3Arg inScale) const; + + /// An opaque buffer that holds shape specific information during GetTrianglesStart/Next. + struct alignas(16) GetTrianglesContext { uint8 mData[4288]; }; + + /// This is the minimum amount of triangles that should be requested through GetTrianglesNext. + static constexpr int cGetTrianglesMinTrianglesRequested = 32; + + /// To start iterating over triangles, call this function first. + /// ioContext is a temporary buffer and should remain untouched until the last call to GetTrianglesNext. + /// inBox is the world space bounding in which you want to get the triangles. + /// inPositionCOM/inRotation/inScale describes the transform of this shape. + /// To get the actual triangles call GetTrianglesNext. + virtual void GetTrianglesStart(GetTrianglesContext &ioContext, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) const = 0; + + /// Call this repeatedly to get all triangles in the box. + /// outTriangleVertices should be large enough to hold 3 * inMaxTriangleRequested entries. + /// outMaterials (if it is not null) should contain inMaxTrianglesRequested entries. + /// The function returns the amount of triangles that it found (which will be <= inMaxTrianglesRequested), or 0 if there are no more triangles. + /// Note that the function can return a value < inMaxTrianglesRequested and still have more triangles to process (triangles can be returned in blocks). + /// Note that the function may return triangles outside of the requested box, only coarse culling is performed on the returned triangles. + virtual int GetTrianglesNext(GetTrianglesContext &ioContext, int inMaxTrianglesRequested, Float3 *outTriangleVertices, const PhysicsMaterial **outMaterials = nullptr) const = 0; + + ///@name Binary serialization of the shape. Note that this saves the 'cooked' shape in a format which will not be backwards compatible for newer library versions. + /// In this case you need to recreate the shape from the ShapeSettings object and save it again. The user is expected to call SaveBinaryState followed by SaveMaterialState and SaveSubShapeState. + /// The stream should be stored as is and the material and shape list should be saved using the applications own serialization system (e.g. by assigning an ID to each pointer). + /// When restoring data, call sRestoreFromBinaryState to get the shape and then call RestoreMaterialState and RestoreSubShapeState to restore the pointers to the external objects. + /// Alternatively you can use SaveWithChildren and sRestoreWithChildren to save and restore the shape and all its child shapes and materials in a single stream. + ///@{ + + /// Saves the contents of the shape in binary form to inStream. + virtual void SaveBinaryState(StreamOut &inStream) const; + + /// Creates a Shape of the correct type and restores its contents from the binary stream inStream. + static ShapeResult sRestoreFromBinaryState(StreamIn &inStream); + + /// Outputs the material references that this shape has to outMaterials. + virtual void SaveMaterialState([[maybe_unused]] PhysicsMaterialList &outMaterials) const { /* By default do nothing */ } + + /// Restore the material references after calling sRestoreFromBinaryState. Note that the exact same materials need to be provided in the same order as returned by SaveMaterialState. + virtual void RestoreMaterialState([[maybe_unused]] const PhysicsMaterialRefC *inMaterials, uint inNumMaterials) { JPH_ASSERT(inNumMaterials == 0); } + + /// Outputs the shape references that this shape has to outSubShapes. + virtual void SaveSubShapeState([[maybe_unused]] ShapeList &outSubShapes) const { /* By default do nothing */ } + + /// Restore the shape references after calling sRestoreFromBinaryState. Note that the exact same shapes need to be provided in the same order as returned by SaveSubShapeState. + virtual void RestoreSubShapeState([[maybe_unused]] const ShapeRefC *inSubShapes, uint inNumShapes) { JPH_ASSERT(inNumShapes == 0); } + + using ShapeToIDMap = StreamUtils::ObjectToIDMap; + using IDToShapeMap = StreamUtils::IDToObjectMap; + using MaterialToIDMap = StreamUtils::ObjectToIDMap; + using IDToMaterialMap = StreamUtils::IDToObjectMap; + + /// Save this shape, all its children and its materials. Pass in an empty map in ioShapeMap / ioMaterialMap or reuse the same map while saving multiple shapes to the same stream in order to avoid writing duplicates. + void SaveWithChildren(StreamOut &inStream, ShapeToIDMap &ioShapeMap, MaterialToIDMap &ioMaterialMap) const; + + /// Restore a shape, all its children and materials. Pass in an empty map in ioShapeMap / ioMaterialMap or reuse the same map while reading multiple shapes from the same stream in order to restore duplicates. + static ShapeResult sRestoreWithChildren(StreamIn &inStream, IDToShapeMap &ioShapeMap, IDToMaterialMap &ioMaterialMap); + + ///@} + + /// Class that holds information about the shape that can be used for logging / data collection purposes + struct Stats + { + Stats(size_t inSizeBytes, uint inNumTriangles) : mSizeBytes(inSizeBytes), mNumTriangles(inNumTriangles) { } + + size_t mSizeBytes; ///< Amount of memory used by this shape (size in bytes) + uint mNumTriangles; ///< Number of triangles in this shape (when applicable) + }; + + /// Get stats of this shape. Use for logging / data collection purposes only. Does not add values from child shapes, use GetStatsRecursive for this. + virtual Stats GetStats() const = 0; + + using VisitedShapes = UnorderedSet; + + /// Get the combined stats of this shape and its children. + /// @param ioVisitedShapes is used to track which shapes have already been visited, to avoid calculating the wrong memory size. + virtual Stats GetStatsRecursive(VisitedShapes &ioVisitedShapes) const; + + ///< Volume of this shape (m^3). Note that for compound shapes the volume may be incorrect since child shapes can overlap which is not accounted for. + virtual float GetVolume() const = 0; + + /// Test if inScale is a valid scale for this shape. Some shapes can only be scaled uniformly, compound shapes cannot handle shapes + /// being rotated and scaled (this would cause shearing), scale can never be zero. When the scale is invalid, the function will return false. + /// + /// Here's a list of supported scales: + /// * SphereShape: Scale must be uniform (signs of scale are ignored). + /// * BoxShape: Any scale supported (signs of scale are ignored). + /// * TriangleShape: Any scale supported when convex radius is zero, otherwise only uniform scale supported. + /// * CapsuleShape: Scale must be uniform (signs of scale are ignored). + /// * TaperedCapsuleShape: Scale must be uniform (sign of Y scale can be used to flip the capsule). + /// * CylinderShape: Scale must be uniform in XZ plane, Y can scale independently (signs of scale are ignored). + /// * RotatedTranslatedShape: Scale must not cause shear in the child shape. + /// * CompoundShape: Scale must not cause shear in any of the child shapes. + virtual bool IsValidScale(Vec3Arg inScale) const { return !inScale.IsNearZero(); } + +#ifdef JPH_DEBUG_RENDERER + /// Debug helper which draws the intersection between water and the shapes, the center of buoyancy and the submerged volume + static bool sDrawSubmergedVolumes; +#endif // JPH_DEBUG_RENDERER + +protected: + /// This function should not be called directly, it is used by sRestoreFromBinaryState. + virtual void RestoreBinaryState(StreamIn &inStream); + + /// A fallback version of CollidePoint that uses a ray cast and counts the number of hits to determine if the point is inside the shape. Odd number of hits means inside, even number of hits means outside. + static void sCollidePointUsingRayCast(const Shape &inShape, Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter); + +private: + uint64 mUserData = 0; + EShapeType mShapeType; + EShapeSubType mShapeSubType; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/SphereShape.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/SphereShape.cpp new file mode 100644 index 00000000000..28577f7ec67 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/SphereShape.cpp @@ -0,0 +1,352 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef JPH_DEBUG_RENDERER + #include +#endif // JPH_DEBUG_RENDERER + +JPH_NAMESPACE_BEGIN + +JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(SphereShapeSettings) +{ + JPH_ADD_BASE_CLASS(SphereShapeSettings, ConvexShapeSettings) + + JPH_ADD_ATTRIBUTE(SphereShapeSettings, mRadius) +} + +ShapeSettings::ShapeResult SphereShapeSettings::Create() const +{ + if (mCachedResult.IsEmpty()) + Ref shape = new SphereShape(*this, mCachedResult); + return mCachedResult; +} + +SphereShape::SphereShape(const SphereShapeSettings &inSettings, ShapeResult &outResult) : + ConvexShape(EShapeSubType::Sphere, inSettings, outResult), + mRadius(inSettings.mRadius) +{ + if (inSettings.mRadius <= 0.0f) + { + outResult.SetError("Invalid radius"); + return; + } + + outResult.Set(this); +} + +float SphereShape::GetScaledRadius(Vec3Arg inScale) const +{ + JPH_ASSERT(IsValidScale(inScale)); + + Vec3 abs_scale = inScale.Abs(); + return abs_scale.GetX() * mRadius; +} + +AABox SphereShape::GetLocalBounds() const +{ + Vec3 half_extent = Vec3::sReplicate(mRadius); + return AABox(-half_extent, half_extent); +} + +AABox SphereShape::GetWorldSpaceBounds(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale) const +{ + float scaled_radius = GetScaledRadius(inScale); + Vec3 half_extent = Vec3::sReplicate(scaled_radius); + AABox bounds(-half_extent, half_extent); + bounds.Translate(inCenterOfMassTransform.GetTranslation()); + return bounds; +} + +class SphereShape::SphereNoConvex final : public Support +{ +public: + explicit SphereNoConvex(float inRadius) : + mRadius(inRadius) + { + static_assert(sizeof(SphereNoConvex) <= sizeof(SupportBuffer), "Buffer size too small"); + JPH_ASSERT(IsAligned(this, alignof(SphereNoConvex))); + } + + virtual Vec3 GetSupport(Vec3Arg inDirection) const override + { + return Vec3::sZero(); + } + + virtual float GetConvexRadius() const override + { + return mRadius; + } + +private: + float mRadius; +}; + +class SphereShape::SphereWithConvex final : public Support +{ +public: + explicit SphereWithConvex(float inRadius) : + mRadius(inRadius) + { + static_assert(sizeof(SphereWithConvex) <= sizeof(SupportBuffer), "Buffer size too small"); + JPH_ASSERT(IsAligned(this, alignof(SphereWithConvex))); + } + + virtual Vec3 GetSupport(Vec3Arg inDirection) const override + { + float len = inDirection.Length(); + return len > 0.0f? (mRadius / len) * inDirection : Vec3::sZero(); + } + + virtual float GetConvexRadius() const override + { + return 0.0f; + } + +private: + float mRadius; +}; + +const ConvexShape::Support *SphereShape::GetSupportFunction(ESupportMode inMode, SupportBuffer &inBuffer, Vec3Arg inScale) const +{ + float scaled_radius = GetScaledRadius(inScale); + + switch (inMode) + { + case ESupportMode::IncludeConvexRadius: + return new (&inBuffer) SphereWithConvex(scaled_radius); + + case ESupportMode::ExcludeConvexRadius: + case ESupportMode::Default: + return new (&inBuffer) SphereNoConvex(scaled_radius); + } + + JPH_ASSERT(false); + return nullptr; +} + +MassProperties SphereShape::GetMassProperties() const +{ + MassProperties p; + + // Calculate mass + float r2 = mRadius * mRadius; + p.mMass = (4.0f / 3.0f * JPH_PI) * mRadius * r2 * GetDensity(); + + // Calculate inertia + float inertia = (2.0f / 5.0f) * p.mMass * r2; + p.mInertia = Mat44::sScale(inertia); + + return p; +} + +Vec3 SphereShape::GetSurfaceNormal(const SubShapeID &inSubShapeID, Vec3Arg inLocalSurfacePosition) const +{ + JPH_ASSERT(inSubShapeID.IsEmpty(), "Invalid subshape ID"); + + float len = inLocalSurfacePosition.Length(); + return len != 0.0f? inLocalSurfacePosition / len : Vec3::sAxisY(); +} + +void SphereShape::GetSubmergedVolume(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, const Plane &inSurface, float &outTotalVolume, float &outSubmergedVolume, Vec3 &outCenterOfBuoyancy JPH_IF_DEBUG_RENDERER(, RVec3Arg inBaseOffset)) const +{ + float scaled_radius = GetScaledRadius(inScale); + outTotalVolume = (4.0f / 3.0f * JPH_PI) * Cubed(scaled_radius); + + float distance_to_surface = inSurface.SignedDistance(inCenterOfMassTransform.GetTranslation()); + if (distance_to_surface >= scaled_radius) + { + // Above surface + outSubmergedVolume = 0.0f; + outCenterOfBuoyancy = Vec3::sZero(); + } + else if (distance_to_surface <= -scaled_radius) + { + // Under surface + outSubmergedVolume = outTotalVolume; + outCenterOfBuoyancy = inCenterOfMassTransform.GetTranslation(); + } + else + { + // Intersecting surface + + // Calculate submerged volume, see: https://en.wikipedia.org/wiki/Spherical_cap + float h = scaled_radius - distance_to_surface; + outSubmergedVolume = (JPH_PI / 3.0f) * Square(h) * (3.0f * scaled_radius - h); + + // Calculate center of buoyancy, see: http://mathworld.wolfram.com/SphericalCap.html (eq 10) + float z = (3.0f / 4.0f) * Square(2.0f * scaled_radius - h) / (3.0f * scaled_radius - h); + outCenterOfBuoyancy = inCenterOfMassTransform.GetTranslation() - z * inSurface.GetNormal(); // Negative normal since we want the portion under the water + + #ifdef JPH_DEBUG_RENDERER + // Draw intersection between sphere and water plane + if (sDrawSubmergedVolumes) + { + Vec3 circle_center = inCenterOfMassTransform.GetTranslation() - distance_to_surface * inSurface.GetNormal(); + float circle_radius = sqrt(Square(scaled_radius) - Square(distance_to_surface)); + DebugRenderer::sInstance->DrawPie(inBaseOffset + circle_center, circle_radius, inSurface.GetNormal(), inSurface.GetNormal().GetNormalizedPerpendicular(), -JPH_PI, JPH_PI, Color::sGreen, DebugRenderer::ECastShadow::Off); + } + #endif // JPH_DEBUG_RENDERER + } + +#ifdef JPH_DEBUG_RENDERER + // Draw center of buoyancy + if (sDrawSubmergedVolumes) + DebugRenderer::sInstance->DrawWireSphere(inBaseOffset + outCenterOfBuoyancy, 0.05f, Color::sRed, 1); +#endif // JPH_DEBUG_RENDERER +} + +#ifdef JPH_DEBUG_RENDERER +void SphereShape::Draw(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inUseMaterialColors, bool inDrawWireframe) const +{ + DebugRenderer::EDrawMode draw_mode = inDrawWireframe? DebugRenderer::EDrawMode::Wireframe : DebugRenderer::EDrawMode::Solid; + inRenderer->DrawUnitSphere(inCenterOfMassTransform * Mat44::sScale(mRadius * inScale.Abs().GetX()), inUseMaterialColors? GetMaterial()->GetDebugColor() : inColor, DebugRenderer::ECastShadow::On, draw_mode); +} +#endif // JPH_DEBUG_RENDERER + +bool SphereShape::CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const +{ + float fraction = RaySphere(inRay.mOrigin, inRay.mDirection, Vec3::sZero(), mRadius); + if (fraction < ioHit.mFraction) + { + ioHit.mFraction = fraction; + ioHit.mSubShapeID2 = inSubShapeIDCreator.GetID(); + return true; + } + return false; +} + +void SphereShape::CastRay(const RayCast &inRay, const RayCastSettings &inRayCastSettings, const SubShapeIDCreator &inSubShapeIDCreator, CastRayCollector &ioCollector, const ShapeFilter &inShapeFilter) const +{ + // Test shape filter + if (!inShapeFilter.ShouldCollide(this, inSubShapeIDCreator.GetID())) + return; + + float min_fraction, max_fraction; + int num_results = RaySphere(inRay.mOrigin, inRay.mDirection, Vec3::sZero(), mRadius, min_fraction, max_fraction); + if (num_results > 0 // Ray should intersect + && max_fraction >= 0.0f // End of ray should be inside sphere + && min_fraction < ioCollector.GetEarlyOutFraction()) // Start of ray should be before early out fraction + { + // Better hit than the current hit + RayCastResult hit; + hit.mBodyID = TransformedShape::sGetBodyID(ioCollector.GetContext()); + hit.mSubShapeID2 = inSubShapeIDCreator.GetID(); + + // Check front side hit + if (inRayCastSettings.mTreatConvexAsSolid || min_fraction > 0.0f) + { + hit.mFraction = max(0.0f, min_fraction); + ioCollector.AddHit(hit); + } + + // Check back side hit + if (inRayCastSettings.mBackFaceMode == EBackFaceMode::CollideWithBackFaces + && num_results > 1 // Ray should have 2 intersections + && max_fraction < ioCollector.GetEarlyOutFraction()) // End of ray should be before early out fraction + { + hit.mFraction = max_fraction; + ioCollector.AddHit(hit); + } + } +} + +void SphereShape::CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter) const +{ + // Test shape filter + if (!inShapeFilter.ShouldCollide(this, inSubShapeIDCreator.GetID())) + return; + + if (inPoint.LengthSq() <= Square(mRadius)) + ioCollector.AddHit({ TransformedShape::sGetBodyID(ioCollector.GetContext()), inSubShapeIDCreator.GetID() }); +} + +void SphereShape::CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, SoftBodyVertex *ioVertices, uint inNumVertices, [[maybe_unused]] float inDeltaTime, [[maybe_unused]] Vec3Arg inDisplacementDueToGravity, int inCollidingShapeIndex) const +{ + Vec3 center = inCenterOfMassTransform.GetTranslation(); + float radius = GetScaledRadius(inScale); + + for (SoftBodyVertex *v = ioVertices, *sbv_end = ioVertices + inNumVertices; v < sbv_end; ++v) + if (v->mInvMass > 0.0f) + { + // Calculate penetration + Vec3 delta = v->mPosition - center; + float distance = delta.Length(); + float penetration = radius - distance; + if (penetration > v->mLargestPenetration) + { + v->mLargestPenetration = penetration; + + // Calculate contact point and normal + Vec3 normal = distance > 0.0f? delta / distance : Vec3::sAxisY(); + Vec3 point = center + radius * normal; + + // Store collision + v->mCollisionPlane = Plane::sFromPointAndNormal(point, normal); + v->mCollidingShapeIndex = inCollidingShapeIndex; + } + } +} + +void SphereShape::TransformShape(Mat44Arg inCenterOfMassTransform, TransformedShapeCollector &ioCollector) const +{ + Vec3 scale; + Mat44 transform = inCenterOfMassTransform.Decompose(scale); + TransformedShape ts(RVec3(transform.GetTranslation()), transform.GetQuaternion(), this, BodyID(), SubShapeIDCreator()); + ts.SetShapeScale(ScaleHelpers::MakeUniformScale(scale.Abs())); + ioCollector.AddHit(ts); +} + +void SphereShape::GetTrianglesStart(GetTrianglesContext &ioContext, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) const +{ + float scaled_radius = GetScaledRadius(inScale); + new (&ioContext) GetTrianglesContextVertexList(inPositionCOM, inRotation, Vec3::sReplicate(1.0f), Mat44::sScale(scaled_radius), sUnitSphereTriangles.data(), sUnitSphereTriangles.size(), GetMaterial()); +} + +int SphereShape::GetTrianglesNext(GetTrianglesContext &ioContext, int inMaxTrianglesRequested, Float3 *outTriangleVertices, const PhysicsMaterial **outMaterials) const +{ + return ((GetTrianglesContextVertexList &)ioContext).GetTrianglesNext(inMaxTrianglesRequested, outTriangleVertices, outMaterials); +} + +void SphereShape::SaveBinaryState(StreamOut &inStream) const +{ + ConvexShape::SaveBinaryState(inStream); + + inStream.Write(mRadius); +} + +void SphereShape::RestoreBinaryState(StreamIn &inStream) +{ + ConvexShape::RestoreBinaryState(inStream); + + inStream.Read(mRadius); +} + +bool SphereShape::IsValidScale(Vec3Arg inScale) const +{ + return ConvexShape::IsValidScale(inScale) && ScaleHelpers::IsUniformScale(inScale.Abs()); +} + +void SphereShape::sRegister() +{ + ShapeFunctions &f = ShapeFunctions::sGet(EShapeSubType::Sphere); + f.mConstruct = []() -> Shape * { return new SphereShape; }; + f.mColor = Color::sGreen; +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/SphereShape.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/SphereShape.h new file mode 100644 index 00000000000..9d976948a2d --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/SphereShape.h @@ -0,0 +1,125 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +/// Class that constructs a SphereShape +class JPH_EXPORT SphereShapeSettings final : public ConvexShapeSettings +{ +public: + JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, SphereShapeSettings) + + /// Default constructor for deserialization + SphereShapeSettings() = default; + + /// Create a sphere with radius inRadius + SphereShapeSettings(float inRadius, const PhysicsMaterial *inMaterial = nullptr) : ConvexShapeSettings(inMaterial), mRadius(inRadius) { } + + // See: ShapeSettings + virtual ShapeResult Create() const override; + + float mRadius = 0.0f; +}; + +/// A sphere, centered around the origin. +/// Note that it is implemented as a point with convex radius. +class JPH_EXPORT SphereShape final : public ConvexShape +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Constructor + SphereShape() : ConvexShape(EShapeSubType::Sphere) { } + SphereShape(const SphereShapeSettings &inSettings, ShapeResult &outResult); + + /// Create a sphere with radius inRadius + SphereShape(float inRadius, const PhysicsMaterial *inMaterial = nullptr) : ConvexShape(EShapeSubType::Sphere, inMaterial), mRadius(inRadius) { JPH_ASSERT(inRadius > 0.0f); } + + /// Radius of the sphere + float GetRadius() const { return mRadius; } + + // See Shape::GetLocalBounds + virtual AABox GetLocalBounds() const override; + + // See Shape::GetWorldSpaceBounds + virtual AABox GetWorldSpaceBounds(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale) const override; + using Shape::GetWorldSpaceBounds; + + // See Shape::GetInnerRadius + virtual float GetInnerRadius() const override { return mRadius; } + + // See Shape::GetMassProperties + virtual MassProperties GetMassProperties() const override; + + // See Shape::GetSurfaceNormal + virtual Vec3 GetSurfaceNormal(const SubShapeID &inSubShapeID, Vec3Arg inLocalSurfacePosition) const override; + + // See Shape::GetSupportingFace + virtual void GetSupportingFace([[maybe_unused]] const SubShapeID &inSubShapeID, [[maybe_unused]] Vec3Arg inDirection, [[maybe_unused]] Vec3Arg inScale, [[maybe_unused]] Mat44Arg inCenterOfMassTransform, [[maybe_unused]] SupportingFace &outVertices) const override { /* Hit is always a single point, no point in returning anything */ } + + // See ConvexShape::GetSupportFunction + virtual const Support * GetSupportFunction(ESupportMode inMode, SupportBuffer &inBuffer, Vec3Arg inScale) const override; + + // See Shape::GetSubmergedVolume + virtual void GetSubmergedVolume(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, const Plane &inSurface, float &outTotalVolume, float &outSubmergedVolume, Vec3 &outCenterOfBuoyancy JPH_IF_DEBUG_RENDERER(, RVec3Arg inBaseOffset)) const override; + +#ifdef JPH_DEBUG_RENDERER + // See Shape::Draw + virtual void Draw(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inUseMaterialColors, bool inDrawWireframe) const override; +#endif // JPH_DEBUG_RENDERER + + // See Shape::CastRay + virtual bool CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const override; + virtual void CastRay(const RayCast &inRay, const RayCastSettings &inRayCastSettings, const SubShapeIDCreator &inSubShapeIDCreator, CastRayCollector &ioCollector, const ShapeFilter &inShapeFilter = { }) const override; + + // See: Shape::CollidePoint + virtual void CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter = { }) const override; + + // See: Shape::CollideSoftBodyVertices + virtual void CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, SoftBodyVertex *ioVertices, uint inNumVertices, float inDeltaTime, Vec3Arg inDisplacementDueToGravity, int inCollidingShapeIndex) const override; + + // See Shape::TransformShape + virtual void TransformShape(Mat44Arg inCenterOfMassTransform, TransformedShapeCollector &ioCollector) const override; + + // See Shape::GetTrianglesStart + virtual void GetTrianglesStart(GetTrianglesContext &ioContext, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) const override; + + // See Shape::GetTrianglesNext + virtual int GetTrianglesNext(GetTrianglesContext &ioContext, int inMaxTrianglesRequested, Float3 *outTriangleVertices, const PhysicsMaterial **outMaterials = nullptr) const override; + + // See Shape + virtual void SaveBinaryState(StreamOut &inStream) const override; + + // See Shape::GetStats + virtual Stats GetStats() const override { return Stats(sizeof(*this), 0); } + + // See Shape::GetVolume + virtual float GetVolume() const override { return 4.0f / 3.0f * JPH_PI * Cubed(mRadius); } + + // See Shape::IsValidScale + virtual bool IsValidScale(Vec3Arg inScale) const override; + + // Register shape functions with the registry + static void sRegister(); + +protected: + // See: Shape::RestoreBinaryState + virtual void RestoreBinaryState(StreamIn &inStream) override; + +private: + // Get the radius of this sphere scaled by inScale + inline float GetScaledRadius(Vec3Arg inScale) const; + + // Classes for GetSupportFunction + class SphereNoConvex; + class SphereWithConvex; + + float mRadius = 0.0f; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/StaticCompoundShape.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/StaticCompoundShape.cpp new file mode 100644 index 00000000000..01e369bab32 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/StaticCompoundShape.cpp @@ -0,0 +1,675 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(StaticCompoundShapeSettings) +{ + JPH_ADD_BASE_CLASS(StaticCompoundShapeSettings, CompoundShapeSettings) +} + +ShapeSettings::ShapeResult StaticCompoundShapeSettings::Create(TempAllocator &inTempAllocator) const +{ + if (mCachedResult.IsEmpty()) + { + if (mSubShapes.size() == 0) + { + // It's an error to create a compound with no subshapes (the compound cannot encode this) + mCachedResult.SetError("Compound needs a sub shape!"); + } + else if (mSubShapes.size() == 1) + { + // If there's only 1 part we don't need a StaticCompoundShape + const SubShapeSettings &s = mSubShapes[0]; + if (s.mPosition == Vec3::sZero() + && s.mRotation == Quat::sIdentity()) + { + // No rotation or translation, we can use the shape directly + if (s.mShapePtr != nullptr) + mCachedResult.Set(const_cast(s.mShapePtr.GetPtr())); + else if (s.mShape != nullptr) + mCachedResult = s.mShape->Create(); + else + mCachedResult.SetError("Sub shape is null!"); + } + else + { + // We can use a RotatedTranslatedShape instead + RotatedTranslatedShapeSettings settings; + settings.mPosition = s.mPosition; + settings.mRotation = s.mRotation; + settings.mInnerShape = s.mShape; + settings.mInnerShapePtr = s.mShapePtr; + Ref shape = new RotatedTranslatedShape(settings, mCachedResult); + } + } + else + { + // Build a regular compound shape + Ref shape = new StaticCompoundShape(*this, inTempAllocator, mCachedResult); + } + } + return mCachedResult; +} + +ShapeSettings::ShapeResult StaticCompoundShapeSettings::Create() const +{ + TempAllocatorMalloc allocator; + return Create(allocator); +} + +void StaticCompoundShape::Node::SetChildInvalid(uint inIndex) +{ + // Make this an invalid node + mNodeProperties[inIndex] = INVALID_NODE; + + // Make bounding box invalid + mBoundsMinX[inIndex] = HALF_FLT_MAX; + mBoundsMinY[inIndex] = HALF_FLT_MAX; + mBoundsMinZ[inIndex] = HALF_FLT_MAX; + mBoundsMaxX[inIndex] = HALF_FLT_MAX; + mBoundsMaxY[inIndex] = HALF_FLT_MAX; + mBoundsMaxZ[inIndex] = HALF_FLT_MAX; +} + +void StaticCompoundShape::Node::SetChildBounds(uint inIndex, const AABox &inBounds) +{ + mBoundsMinX[inIndex] = HalfFloatConversion::FromFloat(inBounds.mMin.GetX()); + mBoundsMinY[inIndex] = HalfFloatConversion::FromFloat(inBounds.mMin.GetY()); + mBoundsMinZ[inIndex] = HalfFloatConversion::FromFloat(inBounds.mMin.GetZ()); + mBoundsMaxX[inIndex] = HalfFloatConversion::FromFloat(inBounds.mMax.GetX()); + mBoundsMaxY[inIndex] = HalfFloatConversion::FromFloat(inBounds.mMax.GetY()); + mBoundsMaxZ[inIndex] = HalfFloatConversion::FromFloat(inBounds.mMax.GetZ()); +} + +void StaticCompoundShape::sPartition(uint *ioBodyIdx, AABox *ioBounds, int inNumber, int &outMidPoint) +{ + // Handle trivial case + if (inNumber <= 4) + { + outMidPoint = inNumber / 2; + return; + } + + // Calculate bounding box of box centers + Vec3 center_min = Vec3::sReplicate(FLT_MAX); + Vec3 center_max = Vec3::sReplicate(-FLT_MAX); + for (const AABox *b = ioBounds, *b_end = ioBounds + inNumber; b < b_end; ++b) + { + Vec3 center = b->GetCenter(); + center_min = Vec3::sMin(center_min, center); + center_max = Vec3::sMax(center_max, center); + } + + // Calculate split plane + int dimension = (center_max - center_min).GetHighestComponentIndex(); + float split = 0.5f * (center_min + center_max)[dimension]; + + // Divide bodies + int start = 0, end = inNumber; + while (start < end) + { + // Search for first element that is on the right hand side of the split plane + while (start < end && ioBounds[start].GetCenter()[dimension] < split) + ++start; + + // Search for the first element that is on the left hand side of the split plane + while (start < end && ioBounds[end - 1].GetCenter()[dimension] >= split) + --end; + + if (start < end) + { + // Swap the two elements + swap(ioBodyIdx[start], ioBodyIdx[end - 1]); + swap(ioBounds[start], ioBounds[end - 1]); + ++start; + --end; + } + } + JPH_ASSERT(start == end); + + if (start > 0 && start < inNumber) + { + // Success! + outMidPoint = start; + } + else + { + // Failed to divide bodies + outMidPoint = inNumber / 2; + } +} + +void StaticCompoundShape::sPartition4(uint *ioBodyIdx, AABox *ioBounds, int inBegin, int inEnd, int *outSplit) +{ + uint *body_idx = ioBodyIdx + inBegin; + AABox *node_bounds = ioBounds + inBegin; + int number = inEnd - inBegin; + + // Partition entire range + sPartition(body_idx, node_bounds, number, outSplit[2]); + + // Partition lower half + sPartition(body_idx, node_bounds, outSplit[2], outSplit[1]); + + // Partition upper half + sPartition(body_idx + outSplit[2], node_bounds + outSplit[2], number - outSplit[2], outSplit[3]); + + // Convert to proper range + outSplit[0] = inBegin; + outSplit[1] += inBegin; + outSplit[2] += inBegin; + outSplit[3] += outSplit[2]; + outSplit[4] = inEnd; +} + +StaticCompoundShape::StaticCompoundShape(const StaticCompoundShapeSettings &inSettings, TempAllocator &inTempAllocator, ShapeResult &outResult) : + CompoundShape(EShapeSubType::StaticCompound, inSettings, outResult) +{ + // Check that there's at least 1 shape + uint num_subshapes = (uint)inSettings.mSubShapes.size(); + if (num_subshapes < 2) + { + outResult.SetError("Compound needs at least 2 sub shapes, otherwise you should use a RotatedTranslatedShape!"); + return; + } + + // Keep track of total mass to calculate center of mass + float mass = 0.0f; + + mSubShapes.resize(num_subshapes); + for (uint i = 0; i < num_subshapes; ++i) + { + const CompoundShapeSettings::SubShapeSettings &shape = inSettings.mSubShapes[i]; + + // Start constructing the runtime sub shape + SubShape &out_shape = mSubShapes[i]; + if (!out_shape.FromSettings(shape, outResult)) + return; + + // Calculate mass properties of child + MassProperties child = out_shape.mShape->GetMassProperties(); + + // Accumulate center of mass + mass += child.mMass; + mCenterOfMass += out_shape.GetPositionCOM() * child.mMass; + } + + if (mass > 0.0f) + mCenterOfMass /= mass; + + // Cache the inner radius as it can take a while to recursively iterate over all sub shapes + CalculateInnerRadius(); + + // Temporary storage for the bounding boxes of all shapes + uint bounds_size = num_subshapes * sizeof(AABox); + AABox *bounds = (AABox *)inTempAllocator.Allocate(bounds_size); + + // Temporary storage for body indexes (we're shuffling them) + uint body_idx_size = num_subshapes * sizeof(uint); + uint *body_idx = (uint *)inTempAllocator.Allocate(body_idx_size); + + // Shift all shapes so that the center of mass is now at the origin and calculate bounds + for (uint i = 0; i < num_subshapes; ++i) + { + SubShape &shape = mSubShapes[i]; + + // Shift the shape so it's centered around our center of mass + shape.SetPositionCOM(shape.GetPositionCOM() - mCenterOfMass); + + // Transform the shape's bounds into our local space + Mat44 transform = Mat44::sRotationTranslation(shape.GetRotation(), shape.GetPositionCOM()); + AABox shape_bounds = shape.mShape->GetWorldSpaceBounds(transform, Vec3::sReplicate(1.0f)); + + // Store bounds and body index for tree construction + bounds[i] = shape_bounds; + body_idx[i] = i; + + // Update our local bounds + mLocalBounds.Encapsulate(shape_bounds); + } + + // The algorithm is a recursive tree build, but to avoid the call overhead we keep track of a stack here + struct StackEntry + { + uint32 mNodeIdx; // Node index of node that is generated + int mChildIdx; // Index of child that we're currently processing + int mSplit[5]; // Indices where the node ID's have been split to form 4 partitions + AABox mBounds; // Bounding box of this node + }; + uint stack_size = num_subshapes * sizeof(StackEntry); + StackEntry *stack = (StackEntry *)inTempAllocator.Allocate(stack_size); + int top = 0; + + // Reserve enough space so that every sub shape gets its own leaf node + uint next_node_idx = 0; + mNodes.resize(num_subshapes + (num_subshapes + 2) / 3); // = Sum(num_subshapes * 4^-i) with i = [0, Inf]. + + // Create root node + stack[0].mNodeIdx = next_node_idx++; + stack[0].mChildIdx = -1; + stack[0].mBounds = AABox(); + sPartition4(body_idx, bounds, 0, num_subshapes, stack[0].mSplit); + + for (;;) + { + StackEntry &cur_stack = stack[top]; + + // Next child + cur_stack.mChildIdx++; + + // Check if all children processed + if (cur_stack.mChildIdx >= 4) + { + // Terminate if there's nothing left to pop + if (top <= 0) + break; + + // Add our bounds to our parents bounds + StackEntry &prev_stack = stack[top - 1]; + prev_stack.mBounds.Encapsulate(cur_stack.mBounds); + + // Store this node's properties in the parent node + Node &parent_node = mNodes[prev_stack.mNodeIdx]; + parent_node.mNodeProperties[prev_stack.mChildIdx] = cur_stack.mNodeIdx; + parent_node.SetChildBounds(prev_stack.mChildIdx, cur_stack.mBounds); + + // Pop entry from stack + --top; + } + else + { + // Get low and high index to bodies to process + int low = cur_stack.mSplit[cur_stack.mChildIdx]; + int high = cur_stack.mSplit[cur_stack.mChildIdx + 1]; + int num_bodies = high - low; + + if (num_bodies == 0) + { + // Mark invalid + Node &node = mNodes[cur_stack.mNodeIdx]; + node.SetChildInvalid(cur_stack.mChildIdx); + } + else if (num_bodies == 1) + { + // Get body info + uint child_node_idx = body_idx[low]; + const AABox &child_bounds = bounds[low]; + + // Update node + Node &node = mNodes[cur_stack.mNodeIdx]; + node.mNodeProperties[cur_stack.mChildIdx] = child_node_idx | IS_SUBSHAPE; + node.SetChildBounds(cur_stack.mChildIdx, child_bounds); + + // Encapsulate bounding box in parent + cur_stack.mBounds.Encapsulate(child_bounds); + } + else + { + // Allocate new node + StackEntry &new_stack = stack[++top]; + JPH_ASSERT(top < (int)num_subshapes); + new_stack.mNodeIdx = next_node_idx++; + new_stack.mChildIdx = -1; + new_stack.mBounds = AABox(); + sPartition4(body_idx, bounds, low, high, new_stack.mSplit); + } + } + } + + // Resize nodes to actual size + JPH_ASSERT(next_node_idx <= mNodes.size()); + mNodes.resize(next_node_idx); + mNodes.shrink_to_fit(); + + // Free temporary memory + inTempAllocator.Free(stack, stack_size); + inTempAllocator.Free(body_idx, body_idx_size); + inTempAllocator.Free(bounds, bounds_size); + + // Check if we ran out of bits for addressing a node + if (next_node_idx > IS_SUBSHAPE) + { + outResult.SetError("Compound hierarchy has too many nodes"); + return; + } + + // Check if we're not exceeding the amount of sub shape id bits + if (GetSubShapeIDBitsRecursive() > SubShapeID::MaxBits) + { + outResult.SetError("Compound hierarchy is too deep and exceeds the amount of available sub shape ID bits"); + return; + } + + outResult.Set(this); +} + +template +inline void StaticCompoundShape::WalkTree(Visitor &ioVisitor) const +{ + uint32 node_stack[cStackSize]; + node_stack[0] = 0; + int top = 0; + do + { + // Test if the node is valid, the node should rarely be invalid but it is possible when testing + // a really large box against the tree that the invalid nodes will intersect with the box + uint32 node_properties = node_stack[top]; + if (node_properties != INVALID_NODE) + { + // Test if node contains triangles + bool is_node = (node_properties & IS_SUBSHAPE) == 0; + if (is_node) + { + const Node &node = mNodes[node_properties]; + + // Unpack bounds + UVec4 bounds_minxy = UVec4::sLoadInt4(reinterpret_cast(&node.mBoundsMinX[0])); + Vec4 bounds_minx = HalfFloatConversion::ToFloat(bounds_minxy); + Vec4 bounds_miny = HalfFloatConversion::ToFloat(bounds_minxy.Swizzle()); + + UVec4 bounds_minzmaxx = UVec4::sLoadInt4(reinterpret_cast(&node.mBoundsMinZ[0])); + Vec4 bounds_minz = HalfFloatConversion::ToFloat(bounds_minzmaxx); + Vec4 bounds_maxx = HalfFloatConversion::ToFloat(bounds_minzmaxx.Swizzle()); + + UVec4 bounds_maxyz = UVec4::sLoadInt4(reinterpret_cast(&node.mBoundsMaxY[0])); + Vec4 bounds_maxy = HalfFloatConversion::ToFloat(bounds_maxyz); + Vec4 bounds_maxz = HalfFloatConversion::ToFloat(bounds_maxyz.Swizzle()); + + // Load properties for 4 children + UVec4 properties = UVec4::sLoadInt4(&node.mNodeProperties[0]); + + // Check which sub nodes to visit + int num_results = ioVisitor.VisitNodes(bounds_minx, bounds_miny, bounds_minz, bounds_maxx, bounds_maxy, bounds_maxz, properties, top); + + // Push them onto the stack + JPH_ASSERT(top + 4 < cStackSize); + properties.StoreInt4(&node_stack[top]); + top += num_results; + } + else + { + // Points to a sub shape + uint32 sub_shape_idx = node_properties ^ IS_SUBSHAPE; + const SubShape &sub_shape = mSubShapes[sub_shape_idx]; + + ioVisitor.VisitShape(sub_shape, sub_shape_idx); + } + + // Check if we're done + if (ioVisitor.ShouldAbort()) + break; + } + + // Fetch next node until we find one that the visitor wants to see + do + --top; + while (top >= 0 && !ioVisitor.ShouldVisitNode(top)); + } + while (top >= 0); +} + +bool StaticCompoundShape::CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const +{ + JPH_PROFILE_FUNCTION(); + + struct Visitor : public CastRayVisitor + { + using CastRayVisitor::CastRayVisitor; + + JPH_INLINE bool ShouldVisitNode(int inStackTop) const + { + return mDistanceStack[inStackTop] < mHit.mFraction; + } + + JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, int inStackTop) + { + // Test bounds of 4 children + Vec4 distance = TestBounds(inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ); + + // Sort so that highest values are first (we want to first process closer hits and we process stack top to bottom) + return SortReverseAndStore(distance, mHit.mFraction, ioProperties, &mDistanceStack[inStackTop]); + } + + float mDistanceStack[cStackSize]; + }; + + Visitor visitor(inRay, this, inSubShapeIDCreator, ioHit); + WalkTree(visitor); + return visitor.mReturnValue; +} + +void StaticCompoundShape::CastRay(const RayCast &inRay, const RayCastSettings &inRayCastSettings, const SubShapeIDCreator &inSubShapeIDCreator, CastRayCollector &ioCollector, const ShapeFilter &inShapeFilter) const +{ + // Test shape filter + if (!inShapeFilter.ShouldCollide(this, inSubShapeIDCreator.GetID())) + return; + + JPH_PROFILE_FUNCTION(); + + struct Visitor : public CastRayVisitorCollector + { + using CastRayVisitorCollector::CastRayVisitorCollector; + + JPH_INLINE bool ShouldVisitNode(int inStackTop) const + { + return mDistanceStack[inStackTop] < mCollector.GetEarlyOutFraction(); + } + + JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, int inStackTop) + { + // Test bounds of 4 children + Vec4 distance = TestBounds(inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ); + + // Sort so that highest values are first (we want to first process closer hits and we process stack top to bottom) + return SortReverseAndStore(distance, mCollector.GetEarlyOutFraction(), ioProperties, &mDistanceStack[inStackTop]); + } + + float mDistanceStack[cStackSize]; + }; + + Visitor visitor(inRay, inRayCastSettings, this, inSubShapeIDCreator, ioCollector, inShapeFilter); + WalkTree(visitor); +} + +void StaticCompoundShape::CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter) const +{ + JPH_PROFILE_FUNCTION(); + + struct Visitor : public CollidePointVisitor + { + using CollidePointVisitor::CollidePointVisitor; + + JPH_INLINE bool ShouldVisitNode([[maybe_unused]] int inStackTop) const + { + return true; + } + + JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, [[maybe_unused]] int inStackTop) const + { + // Test if point overlaps with box + UVec4 collides = TestBounds(inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ); + return CountAndSortTrues(collides, ioProperties); + } + }; + + Visitor visitor(inPoint, this, inSubShapeIDCreator, ioCollector, inShapeFilter); + WalkTree(visitor); +} + +void StaticCompoundShape::sCastShapeVsCompound(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector) +{ + JPH_PROFILE_FUNCTION(); + + struct Visitor : public CastShapeVisitor + { + using CastShapeVisitor::CastShapeVisitor; + + JPH_INLINE bool ShouldVisitNode(int inStackTop) const + { + return mDistanceStack[inStackTop] < mCollector.GetPositiveEarlyOutFraction(); + } + + JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, int inStackTop) + { + // Test bounds of 4 children + Vec4 distance = TestBounds(inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ); + + // Sort so that highest values are first (we want to first process closer hits and we process stack top to bottom) + return SortReverseAndStore(distance, mCollector.GetPositiveEarlyOutFraction(), ioProperties, &mDistanceStack[inStackTop]); + } + + float mDistanceStack[cStackSize]; + }; + + JPH_ASSERT(inShape->GetSubType() == EShapeSubType::StaticCompound); + const StaticCompoundShape *shape = static_cast(inShape); + + Visitor visitor(inShapeCast, inShapeCastSettings, shape, inScale, inShapeFilter, inCenterOfMassTransform2, inSubShapeIDCreator1, inSubShapeIDCreator2, ioCollector); + shape->WalkTree(visitor); +} + +void StaticCompoundShape::CollectTransformedShapes(const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale, const SubShapeIDCreator &inSubShapeIDCreator, TransformedShapeCollector &ioCollector, const ShapeFilter &inShapeFilter) const +{ + JPH_PROFILE_FUNCTION(); + + // Test shape filter + if (!inShapeFilter.ShouldCollide(this, inSubShapeIDCreator.GetID())) + return; + + struct Visitor : public CollectTransformedShapesVisitor + { + using CollectTransformedShapesVisitor::CollectTransformedShapesVisitor; + + JPH_INLINE bool ShouldVisitNode([[maybe_unused]] int inStackTop) const + { + return true; + } + + JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, [[maybe_unused]] int inStackTop) const + { + // Test which nodes collide + UVec4 collides = TestBounds(inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ); + return CountAndSortTrues(collides, ioProperties); + } + }; + + Visitor visitor(inBox, this, inPositionCOM, inRotation, inScale, inSubShapeIDCreator, ioCollector, inShapeFilter); + WalkTree(visitor); +} + +int StaticCompoundShape::GetIntersectingSubShapes(const AABox &inBox, uint *outSubShapeIndices, int inMaxSubShapeIndices) const +{ + JPH_PROFILE_FUNCTION(); + + GetIntersectingSubShapesVisitorSC visitor(inBox, outSubShapeIndices, inMaxSubShapeIndices); + WalkTree(visitor); + return visitor.GetNumResults(); +} + +int StaticCompoundShape::GetIntersectingSubShapes(const OrientedBox &inBox, uint *outSubShapeIndices, int inMaxSubShapeIndices) const +{ + JPH_PROFILE_FUNCTION(); + + GetIntersectingSubShapesVisitorSC visitor(inBox, outSubShapeIndices, inMaxSubShapeIndices); + WalkTree(visitor); + return visitor.GetNumResults(); +} + +void StaticCompoundShape::sCollideCompoundVsShape(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter) +{ + JPH_PROFILE_FUNCTION(); + + JPH_ASSERT(inShape1->GetSubType() == EShapeSubType::StaticCompound); + const StaticCompoundShape *shape1 = static_cast(inShape1); + + struct Visitor : public CollideCompoundVsShapeVisitor + { + using CollideCompoundVsShapeVisitor::CollideCompoundVsShapeVisitor; + + JPH_INLINE bool ShouldVisitNode([[maybe_unused]] int inStackTop) const + { + return true; + } + + JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, [[maybe_unused]] int inStackTop) const + { + // Test which nodes collide + UVec4 collides = TestBounds(inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ); + return CountAndSortTrues(collides, ioProperties); + } + }; + + Visitor visitor(shape1, inShape2, inScale1, inScale2, inCenterOfMassTransform1, inCenterOfMassTransform2, inSubShapeIDCreator1, inSubShapeIDCreator2, inCollideShapeSettings, ioCollector, inShapeFilter); + shape1->WalkTree(visitor); +} + +void StaticCompoundShape::sCollideShapeVsCompound(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter) +{ + JPH_PROFILE_FUNCTION(); + + struct Visitor : public CollideShapeVsCompoundVisitor + { + using CollideShapeVsCompoundVisitor::CollideShapeVsCompoundVisitor; + + JPH_INLINE bool ShouldVisitNode([[maybe_unused]] int inStackTop) const + { + return true; + } + + JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, [[maybe_unused]] int inStackTop) const + { + // Test which nodes collide + UVec4 collides = TestBounds(inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ); + return CountAndSortTrues(collides, ioProperties); + } + }; + + JPH_ASSERT(inShape2->GetSubType() == EShapeSubType::StaticCompound); + const StaticCompoundShape *shape2 = static_cast(inShape2); + + Visitor visitor(inShape1, shape2, inScale1, inScale2, inCenterOfMassTransform1, inCenterOfMassTransform2, inSubShapeIDCreator1, inSubShapeIDCreator2, inCollideShapeSettings, ioCollector, inShapeFilter); + shape2->WalkTree(visitor); +} + +void StaticCompoundShape::SaveBinaryState(StreamOut &inStream) const +{ + CompoundShape::SaveBinaryState(inStream); + + inStream.Write(mNodes); +} + +void StaticCompoundShape::RestoreBinaryState(StreamIn &inStream) +{ + CompoundShape::RestoreBinaryState(inStream); + + inStream.Read(mNodes); +} + +void StaticCompoundShape::sRegister() +{ + ShapeFunctions &f = ShapeFunctions::sGet(EShapeSubType::StaticCompound); + f.mConstruct = []() -> Shape * { return new StaticCompoundShape; }; + f.mColor = Color::sOrange; + + for (EShapeSubType s : sAllSubShapeTypes) + { + CollisionDispatch::sRegisterCollideShape(EShapeSubType::StaticCompound, s, sCollideCompoundVsShape); + CollisionDispatch::sRegisterCollideShape(s, EShapeSubType::StaticCompound, sCollideShapeVsCompound); + CollisionDispatch::sRegisterCastShape(s, EShapeSubType::StaticCompound, sCastShapeVsCompound); + } +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/StaticCompoundShape.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/StaticCompoundShape.h new file mode 100644 index 00000000000..c3b7b7e76e2 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/StaticCompoundShape.h @@ -0,0 +1,139 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +class CollideShapeSettings; +class TempAllocator; + +/// Class that constructs a StaticCompoundShape. Note that if you only want a compound of 1 shape, use a RotatedTranslatedShape instead. +class JPH_EXPORT StaticCompoundShapeSettings final : public CompoundShapeSettings +{ +public: + JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, StaticCompoundShapeSettings) + + // See: ShapeSettings + virtual ShapeResult Create() const override; + + /// Specialization of Create() function that allows specifying a temp allocator to avoid temporary memory allocations on the heap + ShapeResult Create(TempAllocator &inTempAllocator) const; +}; + +/// A compound shape, sub shapes can be rotated and translated. +/// Sub shapes cannot be modified once the shape is constructed. +/// Shifts all child objects so that they're centered around the center of mass. +class JPH_EXPORT StaticCompoundShape final : public CompoundShape +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Constructor + StaticCompoundShape() : CompoundShape(EShapeSubType::StaticCompound) { } + StaticCompoundShape(const StaticCompoundShapeSettings &inSettings, TempAllocator &inTempAllocator, ShapeResult &outResult); + + // See Shape::CastRay + virtual bool CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const override; + virtual void CastRay(const RayCast &inRay, const RayCastSettings &inRayCastSettings, const SubShapeIDCreator &inSubShapeIDCreator, CastRayCollector &ioCollector, const ShapeFilter &inShapeFilter = { }) const override; + + // See: Shape::CollidePoint + virtual void CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter = { }) const override; + + // See Shape::CollectTransformedShapes + virtual void CollectTransformedShapes(const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale, const SubShapeIDCreator &inSubShapeIDCreator, TransformedShapeCollector &ioCollector, const ShapeFilter &inShapeFilter) const override; + + // See: CompoundShape::GetIntersectingSubShapes + virtual int GetIntersectingSubShapes(const AABox &inBox, uint *outSubShapeIndices, int inMaxSubShapeIndices) const override; + + // See: CompoundShape::GetIntersectingSubShapes + virtual int GetIntersectingSubShapes(const OrientedBox &inBox, uint *outSubShapeIndices, int inMaxSubShapeIndices) const override; + + // See Shape + virtual void SaveBinaryState(StreamOut &inStream) const override; + + // See Shape::GetStats + virtual Stats GetStats() const override { return Stats(sizeof(*this) + mSubShapes.size() * sizeof(SubShape) + mNodes.size() * sizeof(Node), 0); } + + // Register shape functions with the registry + static void sRegister(); + +protected: + // See: Shape::RestoreBinaryState + virtual void RestoreBinaryState(StreamIn &inStream) override; + +private: + // Visitor for GetIntersectingSubShapes + template + struct GetIntersectingSubShapesVisitorSC : public GetIntersectingSubShapesVisitor + { + using GetIntersectingSubShapesVisitor::GetIntersectingSubShapesVisitor; + + JPH_INLINE bool ShouldVisitNode(int inStackTop) const + { + return true; + } + + JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, int inStackTop) + { + // Test if point overlaps with box + UVec4 collides = GetIntersectingSubShapesVisitor::TestBounds(inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ); + return CountAndSortTrues(collides, ioProperties); + } + }; + + /// Sorts ioBodyIdx spatially into 2 groups. Second groups starts at ioBodyIdx + outMidPoint. + /// After the function returns ioBodyIdx and ioBounds will be shuffled + static void sPartition(uint *ioBodyIdx, AABox *ioBounds, int inNumber, int &outMidPoint); + + /// Sorts ioBodyIdx from inBegin to (but excluding) inEnd spatially into 4 groups. + /// outSplit needs to be 5 ints long, when the function returns each group runs from outSplit[i] to (but excluding) outSplit[i + 1] + /// After the function returns ioBodyIdx and ioBounds will be shuffled + static void sPartition4(uint *ioBodyIdx, AABox *ioBounds, int inBegin, int inEnd, int *outSplit); + + // Helper functions called by CollisionDispatch + static void sCollideCompoundVsShape(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter); + static void sCollideShapeVsCompound(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter); + static void sCastShapeVsCompound(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector); + + // Maximum size of the stack during tree walk + static constexpr int cStackSize = 128; + + template + JPH_INLINE void WalkTree(Visitor &ioVisitor) const; ///< Walk the node tree calling the Visitor::VisitNodes for each node encountered and Visitor::VisitShape for each sub shape encountered + + /// Bits used in Node::mNodeProperties + enum : uint32 + { + IS_SUBSHAPE = 0x80000000, ///< If this bit is set, the other bits index in mSubShape, otherwise in mNodes + INVALID_NODE = 0x7fffffff, ///< Signifies an invalid node + }; + + /// Node structure + struct Node + { + void SetChildBounds(uint inIndex, const AABox &inBounds); ///< Set bounding box for child inIndex to inBounds + void SetChildInvalid(uint inIndex); ///< Mark the child inIndex as invalid and set its bounding box to invalid + + HalfFloat mBoundsMinX[4]; ///< 4 child bounding boxes + HalfFloat mBoundsMinY[4]; + HalfFloat mBoundsMinZ[4]; + HalfFloat mBoundsMaxX[4]; + HalfFloat mBoundsMaxY[4]; + HalfFloat mBoundsMaxZ[4]; + uint32 mNodeProperties[4]; ///< 4 child node properties + }; + + static_assert(sizeof(Node) == 64, "Node should be 64 bytes"); + + using Nodes = Array; + + Nodes mNodes; ///< Quad tree node structure +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/SubShapeID.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/SubShapeID.h new file mode 100644 index 00000000000..f1e8d3713bc --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/SubShapeID.h @@ -0,0 +1,138 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +JPH_NAMESPACE_BEGIN + +/// @brief A sub shape id contains a path to an element (usually a triangle or other primitive type) of a compound shape +/// +/// Each sub shape knows how many bits it needs to encode its ID, so knows how many bits to take from the sub shape ID. +/// +/// For example: +/// * We have a CompoundShape A with 5 child shapes (identify sub shape using 3 bits AAA) +/// * One of its child shapes is CompoundShape B which has 3 child shapes (identify sub shape using 2 bits BB) +/// * One of its child shapes is MeshShape C which contains enough triangles to need 7 bits to identify a triangle (identify sub shape using 7 bits CCCCCCC, note that MeshShape is block based and sorts triangles spatially, you can't assume that the first triangle will have bit pattern 0000000). +/// +/// The bit pattern of the sub shape ID to identify a triangle in MeshShape C will then be CCCCCCCBBAAA. +/// +/// A sub shape ID will become invalid when the structure of the shape changes. For example, if a child shape is removed from a compound shape, the sub shape ID will no longer be valid. +/// This can be a problem when caching sub shape IDs from one frame to the next. See comments at ContactListener::OnContactPersisted / OnContactRemoved. +class SubShapeID +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Underlying storage type + using Type = uint32; + + /// Type that is bigger than the underlying storage type for operations that would otherwise overflow + using BiggerType = uint64; + + static_assert(sizeof(BiggerType) > sizeof(Type), "The calculation below assumes BiggerType is a bigger type than Type"); + + /// How many bits we can store in this ID + static constexpr uint MaxBits = 8 * sizeof(Type); + + /// Constructor + SubShapeID() = default; + + /// Get the next id in the chain of ids (pops parents before children) + Type PopID(uint inBits, SubShapeID &outRemainder) const + { + Type mask_bits = Type((BiggerType(1) << inBits) - 1); + Type fill_bits = Type(BiggerType(cEmpty) << (MaxBits - inBits)); // Fill left side bits with 1 so that if there's no remainder all bits will be set, note that we do this using a BiggerType since on intel 0xffffffff << 32 == 0xffffffff + Type v = mValue & mask_bits; + outRemainder = SubShapeID(Type(BiggerType(mValue) >> inBits) | fill_bits); + return v; + } + + /// Get the value of the path to the sub shape ID + inline Type GetValue() const + { + return mValue; + } + + /// Set the value of the sub shape ID (use with care!) + inline void SetValue(Type inValue) + { + mValue = inValue; + } + + /// Check if there is any bits of subshape ID left. + /// Note that this is not a 100% guarantee as the subshape ID could consist of all 1 bits. Use for asserts only. + inline bool IsEmpty() const + { + return mValue == cEmpty; + } + + /// Check equal + inline bool operator == (const SubShapeID &inRHS) const + { + return mValue == inRHS.mValue; + } + + /// Check not-equal + inline bool operator != (const SubShapeID &inRHS) const + { + return mValue != inRHS.mValue; + } + +private: + friend class SubShapeIDCreator; + + /// An empty SubShapeID has all bits set + static constexpr Type cEmpty = ~Type(0); + + /// Constructor + explicit SubShapeID(const Type &inValue) : mValue(inValue) { } + + /// Adds an id at a particular position in the chain + /// (this should really only be called by the SubShapeIDCreator) + void PushID(Type inValue, uint inFirstBit, uint inBits) + { + // First clear the bits + mValue &= ~(Type((BiggerType(1) << inBits) - 1) << inFirstBit); + + // Then set them to the new value + mValue |= inValue << inFirstBit; + } + + Type mValue = cEmpty; +}; + +/// A sub shape id creator can be used to create a new sub shape id by recursing through the shape +/// hierarchy and pushing new ID's onto the chain +class SubShapeIDCreator +{ +public: + /// Add a new id to the chain of id's and return it + SubShapeIDCreator PushID(uint inValue, uint inBits) const + { + JPH_ASSERT(inValue < (SubShapeID::BiggerType(1) << inBits)); + SubShapeIDCreator copy = *this; + copy.mID.PushID(inValue, mCurrentBit, inBits); + copy.mCurrentBit += inBits; + JPH_ASSERT(copy.mCurrentBit <= SubShapeID::MaxBits); + return copy; + } + + // Get the resulting sub shape ID + const SubShapeID & GetID() const + { + return mID; + } + + /// Get the number of bits that have been written to the sub shape ID so far + inline uint GetNumBitsWritten() const + { + return mCurrentBit; + } + +private: + SubShapeID mID; + uint mCurrentBit = 0; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/SubShapeIDPair.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/SubShapeIDPair.h new file mode 100644 index 00000000000..ccad840b6ad --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/SubShapeIDPair.h @@ -0,0 +1,80 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +/// A pair of bodies and their sub shape ID's. Can be used as a key in a map to find a contact point. +class SubShapeIDPair +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Constructor + SubShapeIDPair() = default; + SubShapeIDPair(const BodyID &inBody1ID, const SubShapeID &inSubShapeID1, const BodyID &inBody2ID, const SubShapeID &inSubShapeID2) : mBody1ID(inBody1ID), mSubShapeID1(inSubShapeID1), mBody2ID(inBody2ID), mSubShapeID2(inSubShapeID2) { } + SubShapeIDPair & operator = (const SubShapeIDPair &) = default; + SubShapeIDPair(const SubShapeIDPair &) = default; + + /// Equality operator + inline bool operator == (const SubShapeIDPair &inRHS) const + { + return UVec4::sLoadInt4(reinterpret_cast(this)) == UVec4::sLoadInt4(reinterpret_cast(&inRHS)); + } + + /// Less than operator, used to consistently order contact points for a deterministic simulation + inline bool operator < (const SubShapeIDPair &inRHS) const + { + if (mBody1ID != inRHS.mBody1ID) + return mBody1ID < inRHS.mBody1ID; + + if (mSubShapeID1.GetValue() != inRHS.mSubShapeID1.GetValue()) + return mSubShapeID1.GetValue() < inRHS.mSubShapeID1.GetValue(); + + if (mBody2ID != inRHS.mBody2ID) + return mBody2ID < inRHS.mBody2ID; + + return mSubShapeID2.GetValue() < inRHS.mSubShapeID2.GetValue(); + } + + const BodyID & GetBody1ID() const { return mBody1ID; } + const SubShapeID & GetSubShapeID1() const { return mSubShapeID1; } + const BodyID & GetBody2ID() const { return mBody2ID; } + const SubShapeID & GetSubShapeID2() const { return mSubShapeID2; } + + uint64 GetHash() const { return HashBytes(this, sizeof(SubShapeIDPair)); } + +private: + BodyID mBody1ID; + SubShapeID mSubShapeID1; + BodyID mBody2ID; + SubShapeID mSubShapeID2; +}; + +static_assert(sizeof(SubShapeIDPair) == 16, "Unexpected size"); +static_assert(alignof(SubShapeIDPair) == 4, "Assuming 4 byte aligned"); + +JPH_NAMESPACE_END + +JPH_SUPPRESS_WARNINGS_STD_BEGIN + +namespace std +{ + /// Declare std::hash for SubShapeIDPair, note that std::hash is platform dependent and we need this one to be consistent because we sort on it in the ContactConstraintManager + template <> + struct hash + { + inline size_t operator () (const JPH::SubShapeIDPair &inRHS) const + { + return static_cast(inRHS.GetHash()); + } + }; +} + +JPH_SUPPRESS_WARNINGS_STD_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/TaperedCapsuleShape.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/TaperedCapsuleShape.cpp new file mode 100644 index 00000000000..98386418520 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/TaperedCapsuleShape.cpp @@ -0,0 +1,458 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef JPH_DEBUG_RENDERER + #include +#endif // JPH_DEBUG_RENDERER + +JPH_NAMESPACE_BEGIN + +JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(TaperedCapsuleShapeSettings) +{ + JPH_ADD_BASE_CLASS(TaperedCapsuleShapeSettings, ConvexShapeSettings) + + JPH_ADD_ATTRIBUTE(TaperedCapsuleShapeSettings, mHalfHeightOfTaperedCylinder) + JPH_ADD_ATTRIBUTE(TaperedCapsuleShapeSettings, mTopRadius) + JPH_ADD_ATTRIBUTE(TaperedCapsuleShapeSettings, mBottomRadius) +} + +bool TaperedCapsuleShapeSettings::IsSphere() const +{ + return max(mTopRadius, mBottomRadius) >= 2.0f * mHalfHeightOfTaperedCylinder + min(mTopRadius, mBottomRadius); +} + +ShapeSettings::ShapeResult TaperedCapsuleShapeSettings::Create() const +{ + if (mCachedResult.IsEmpty()) + { + Ref shape; + if (IsValid() && IsSphere()) + { + // Determine sphere center and radius + float radius, center; + if (mTopRadius > mBottomRadius) + { + radius = mTopRadius; + center = mHalfHeightOfTaperedCylinder; + } + else + { + radius = mBottomRadius; + center = -mHalfHeightOfTaperedCylinder; + } + + // Create sphere + shape = new SphereShape(radius, mMaterial); + + // Offset sphere if needed + if (abs(center) > 1.0e-6f) + { + RotatedTranslatedShapeSettings rot_trans(Vec3(0, center, 0), Quat::sIdentity(), shape); + mCachedResult = rot_trans.Create(); + } + else + mCachedResult.Set(shape); + } + else + { + // Normal tapered capsule shape + shape = new TaperedCapsuleShape(*this, mCachedResult); + } + } + return mCachedResult; +} + +TaperedCapsuleShapeSettings::TaperedCapsuleShapeSettings(float inHalfHeightOfTaperedCylinder, float inTopRadius, float inBottomRadius, const PhysicsMaterial *inMaterial) : + ConvexShapeSettings(inMaterial), + mHalfHeightOfTaperedCylinder(inHalfHeightOfTaperedCylinder), + mTopRadius(inTopRadius), + mBottomRadius(inBottomRadius) +{ +} + +TaperedCapsuleShape::TaperedCapsuleShape(const TaperedCapsuleShapeSettings &inSettings, ShapeResult &outResult) : + ConvexShape(EShapeSubType::TaperedCapsule, inSettings, outResult), + mTopRadius(inSettings.mTopRadius), + mBottomRadius(inSettings.mBottomRadius) +{ + if (mTopRadius <= 0.0f) + { + outResult.SetError("Invalid top radius"); + return; + } + + if (mBottomRadius <= 0.0f) + { + outResult.SetError("Invalid bottom radius"); + return; + } + + if (inSettings.mHalfHeightOfTaperedCylinder <= 0.0f) + { + outResult.SetError("Invalid height"); + return; + } + + // If this goes off one of the sphere ends falls totally inside the other and you should use a sphere instead + if (inSettings.IsSphere()) + { + outResult.SetError("One sphere embedded in other sphere, please use sphere shape instead"); + return; + } + + // Approximation: The center of mass is exactly half way between the top and bottom cap of the tapered capsule + mTopCenter = inSettings.mHalfHeightOfTaperedCylinder + 0.5f * (mBottomRadius - mTopRadius); + mBottomCenter = -inSettings.mHalfHeightOfTaperedCylinder + 0.5f * (mBottomRadius - mTopRadius); + + // Calculate center of mass + mCenterOfMass = Vec3(0, inSettings.mHalfHeightOfTaperedCylinder - mTopCenter, 0); + + // Calculate convex radius + mConvexRadius = min(mTopRadius, mBottomRadius); + JPH_ASSERT(mConvexRadius > 0.0f); + + // Calculate the sin and tan of the angle that the cone surface makes with the Y axis + // See: TaperedCapsuleShape.gliffy + mSinAlpha = (mBottomRadius - mTopRadius) / (mTopCenter - mBottomCenter); + JPH_ASSERT(mSinAlpha >= -1.0f && mSinAlpha <= 1.0f); + mTanAlpha = Tan(ASin(mSinAlpha)); + + outResult.Set(this); +} + +class TaperedCapsuleShape::TaperedCapsule final : public Support +{ +public: + TaperedCapsule(Vec3Arg inTopCenter, Vec3Arg inBottomCenter, float inTopRadius, float inBottomRadius, float inConvexRadius) : + mTopCenter(inTopCenter), + mBottomCenter(inBottomCenter), + mTopRadius(inTopRadius), + mBottomRadius(inBottomRadius), + mConvexRadius(inConvexRadius) + { + static_assert(sizeof(TaperedCapsule) <= sizeof(SupportBuffer), "Buffer size too small"); + JPH_ASSERT(IsAligned(this, alignof(TaperedCapsule))); + } + + virtual Vec3 GetSupport(Vec3Arg inDirection) const override + { + // Check zero vector + float len = inDirection.Length(); + if (len == 0.0f) + return mTopCenter + Vec3(0, mTopRadius, 0); // Return top + + // Check if the support of the top sphere or bottom sphere is bigger + Vec3 support_top = mTopCenter + (mTopRadius / len) * inDirection; + Vec3 support_bottom = mBottomCenter + (mBottomRadius / len) * inDirection; + if (support_top.Dot(inDirection) > support_bottom.Dot(inDirection)) + return support_top; + else + return support_bottom; + } + + virtual float GetConvexRadius() const override + { + return mConvexRadius; + } + +private: + Vec3 mTopCenter; + Vec3 mBottomCenter; + float mTopRadius; + float mBottomRadius; + float mConvexRadius; +}; + +const ConvexShape::Support *TaperedCapsuleShape::GetSupportFunction(ESupportMode inMode, SupportBuffer &inBuffer, Vec3Arg inScale) const +{ + JPH_ASSERT(IsValidScale(inScale)); + + // Get scaled tapered capsule + Vec3 abs_scale = inScale.Abs(); + float scale_xz = abs_scale.GetX(); + float scale_y = inScale.GetY(); // The sign of y is important as it flips the tapered capsule + Vec3 scaled_top_center = Vec3(0, scale_y * mTopCenter, 0); + Vec3 scaled_bottom_center = Vec3(0, scale_y * mBottomCenter, 0); + float scaled_top_radius = scale_xz * mTopRadius; + float scaled_bottom_radius = scale_xz * mBottomRadius; + float scaled_convex_radius = scale_xz * mConvexRadius; + + switch (inMode) + { + case ESupportMode::IncludeConvexRadius: + return new (&inBuffer) TaperedCapsule(scaled_top_center, scaled_bottom_center, scaled_top_radius, scaled_bottom_radius, 0.0f); + + case ESupportMode::ExcludeConvexRadius: + case ESupportMode::Default: + { + // Get radii reduced by convex radius + float tr = scaled_top_radius - scaled_convex_radius; + float br = scaled_bottom_radius - scaled_convex_radius; + JPH_ASSERT(tr >= 0.0f && br >= 0.0f); + JPH_ASSERT(tr == 0.0f || br == 0.0f, "Convex radius should be that of the smallest sphere"); + return new (&inBuffer) TaperedCapsule(scaled_top_center, scaled_bottom_center, tr, br, scaled_convex_radius); + } + } + + JPH_ASSERT(false); + return nullptr; +} + +void TaperedCapsuleShape::GetSupportingFace(const SubShapeID &inSubShapeID, Vec3Arg inDirection, Vec3Arg inScale, Mat44Arg inCenterOfMassTransform, SupportingFace &outVertices) const +{ + JPH_ASSERT(inSubShapeID.IsEmpty(), "Invalid subshape ID"); + JPH_ASSERT(IsValidScale(inScale)); + + // Check zero vector + float len = inDirection.Length(); + if (len == 0.0f) + return; + + // Get scaled tapered capsule + Vec3 abs_scale = inScale.Abs(); + float scale_xz = abs_scale.GetX(); + float scale_y = inScale.GetY(); // The sign of y is important as it flips the tapered capsule + Vec3 scaled_top_center = Vec3(0, scale_y * mTopCenter, 0); + Vec3 scaled_bottom_center = Vec3(0, scale_y * mBottomCenter, 0); + float scaled_top_radius = scale_xz * mTopRadius; + float scaled_bottom_radius = scale_xz * mBottomRadius; + + // Get support point for top and bottom sphere in the opposite of inDirection (including convex radius) + Vec3 support_top = scaled_top_center - (scaled_top_radius / len) * inDirection; + Vec3 support_bottom = scaled_bottom_center - (scaled_bottom_radius / len) * inDirection; + + // Get projection on inDirection + float proj_top = support_top.Dot(inDirection); + float proj_bottom = support_bottom.Dot(inDirection); + + // If projection is roughly equal then return line, otherwise we return nothing as there's only 1 point + if (abs(proj_top - proj_bottom) < cCapsuleProjectionSlop * len) + { + outVertices.push_back(inCenterOfMassTransform * support_top); + outVertices.push_back(inCenterOfMassTransform * support_bottom); + } +} + +MassProperties TaperedCapsuleShape::GetMassProperties() const +{ + AABox box = GetInertiaApproximation(); + + MassProperties p; + p.SetMassAndInertiaOfSolidBox(box.GetSize(), GetDensity()); + return p; +} + +Vec3 TaperedCapsuleShape::GetSurfaceNormal(const SubShapeID &inSubShapeID, Vec3Arg inLocalSurfacePosition) const +{ + JPH_ASSERT(inSubShapeID.IsEmpty(), "Invalid subshape ID"); + + // See: TaperedCapsuleShape.gliffy + // We need to calculate ty and by in order to see if the position is on the top or bottom sphere + // sin(alpha) = by / br = ty / tr + // => by = sin(alpha) * br, ty = sin(alpha) * tr + + if (inLocalSurfacePosition.GetY() > mTopCenter + mSinAlpha * mTopRadius) + return (inLocalSurfacePosition - Vec3(0, mTopCenter, 0)).Normalized(); + else if (inLocalSurfacePosition.GetY() < mBottomCenter + mSinAlpha * mBottomRadius) + return (inLocalSurfacePosition - Vec3(0, mBottomCenter, 0)).Normalized(); + else + { + // Get perpendicular vector to the surface in the xz plane + Vec3 perpendicular = Vec3(inLocalSurfacePosition.GetX(), 0, inLocalSurfacePosition.GetZ()).NormalizedOr(Vec3::sAxisX()); + + // We know that the perpendicular has length 1 and that it needs a y component where tan(alpha) = y / 1 in order to align it to the surface + perpendicular.SetY(mTanAlpha); + return perpendicular.Normalized(); + } +} + +AABox TaperedCapsuleShape::GetLocalBounds() const +{ + float max_radius = max(mTopRadius, mBottomRadius); + return AABox(Vec3(-max_radius, mBottomCenter - mBottomRadius, -max_radius), Vec3(max_radius, mTopCenter + mTopRadius, max_radius)); +} + +AABox TaperedCapsuleShape::GetWorldSpaceBounds(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale) const +{ + JPH_ASSERT(IsValidScale(inScale)); + + Vec3 abs_scale = inScale.Abs(); + float scale_xz = abs_scale.GetX(); + float scale_y = inScale.GetY(); // The sign of y is important as it flips the tapered capsule + Vec3 bottom_extent = Vec3::sReplicate(scale_xz * mBottomRadius); + Vec3 bottom_center = inCenterOfMassTransform * Vec3(0, scale_y * mBottomCenter, 0); + Vec3 top_extent = Vec3::sReplicate(scale_xz * mTopRadius); + Vec3 top_center = inCenterOfMassTransform * Vec3(0, scale_y * mTopCenter, 0); + Vec3 p1 = Vec3::sMin(top_center - top_extent, bottom_center - bottom_extent); + Vec3 p2 = Vec3::sMax(top_center + top_extent, bottom_center + bottom_extent); + return AABox(p1, p2); +} + +void TaperedCapsuleShape::CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, SoftBodyVertex *ioVertices, uint inNumVertices, [[maybe_unused]] float inDeltaTime, [[maybe_unused]] Vec3Arg inDisplacementDueToGravity, int inCollidingShapeIndex) const +{ + JPH_ASSERT(IsValidScale(inScale)); + + Mat44 inverse_transform = inCenterOfMassTransform.InversedRotationTranslation(); + + // Get scaled tapered capsule + Vec3 abs_scale = inScale.Abs(); + float scale_y = abs_scale.GetY(); + float scale_xz = abs_scale.GetX(); + Vec3 scale_y_flip(1, Sign(inScale.GetY()), 1); + Vec3 scaled_top_center(0, scale_y * mTopCenter, 0); + Vec3 scaled_bottom_center(0, scale_y * mBottomCenter, 0); + float scaled_top_radius = scale_xz * mTopRadius; + float scaled_bottom_radius = scale_xz * mBottomRadius; + + for (SoftBodyVertex *v = ioVertices, *sbv_end = ioVertices + inNumVertices; v < sbv_end; ++v) + if (v->mInvMass > 0.0f) + { + Vec3 local_pos = scale_y_flip * (inverse_transform * v->mPosition); + + Vec3 position, normal; + + // If the vertex is inside the cone starting at the top center pointing along the y-axis with angle PI/2 - alpha then the closest point is on the top sphere + // This corresponds to: Dot(y-axis, (local_pos - top_center) / |local_pos - top_center|) >= cos(PI/2 - alpha) + // <=> (local_pos - top_center).y >= sin(alpha) * |local_pos - top_center| + Vec3 top_center_to_local_pos = local_pos - scaled_top_center; + float top_center_to_local_pos_len = top_center_to_local_pos.Length(); + if (top_center_to_local_pos.GetY() >= mSinAlpha * top_center_to_local_pos_len) + { + // Top sphere + normal = top_center_to_local_pos_len != 0.0f? top_center_to_local_pos / top_center_to_local_pos_len : Vec3::sAxisY(); + position = scaled_top_center + scaled_top_radius * normal; + } + else + { + // If the vertex is outside the cone starting at the bottom center pointing along the y-axis with angle PI/2 - alpha then the closest point is on the bottom sphere + // This corresponds to: Dot(y-axis, (local_pos - bottom_center) / |local_pos - bottom_center|) <= cos(PI/2 - alpha) + // <=> (local_pos - bottom_center).y <= sin(alpha) * |local_pos - bottom_center| + Vec3 bottom_center_to_local_pos = local_pos - scaled_bottom_center; + float bottom_center_to_local_pos_len = bottom_center_to_local_pos.Length(); + if (bottom_center_to_local_pos.GetY() <= mSinAlpha * bottom_center_to_local_pos_len) + { + // Bottom sphere + normal = bottom_center_to_local_pos_len != 0.0f? bottom_center_to_local_pos / bottom_center_to_local_pos_len : -Vec3::sAxisY(); + } + else + { + // Tapered cylinder + normal = Vec3(local_pos.GetX(), 0, local_pos.GetZ()).NormalizedOr(Vec3::sAxisX()); + normal.SetY(mTanAlpha); + normal = normal.NormalizedOr(Vec3::sAxisX()); + } + position = scaled_bottom_center + scaled_bottom_radius * normal; + } + + Plane plane = Plane::sFromPointAndNormal(position, normal); + float penetration = -plane.SignedDistance(local_pos); + if (penetration > v->mLargestPenetration) + { + v->mLargestPenetration = penetration; + + // Need to flip the normal's y if capsule is flipped (this corresponds to flipping both the point and the normal around y) + plane.SetNormal(scale_y_flip * plane.GetNormal()); + + // Store collision + v->mCollisionPlane = plane.GetTransformed(inCenterOfMassTransform); + v->mCollidingShapeIndex = inCollidingShapeIndex; + } + } +} + +#ifdef JPH_DEBUG_RENDERER +void TaperedCapsuleShape::Draw(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inUseMaterialColors, bool inDrawWireframe) const +{ + if (mGeometry == nullptr) + { + SupportBuffer buffer; + const Support *support = GetSupportFunction(ESupportMode::IncludeConvexRadius, buffer, Vec3::sReplicate(1.0f)); + mGeometry = inRenderer->CreateTriangleGeometryForConvex([support](Vec3Arg inDirection) { return support->GetSupport(inDirection); }); + } + + // Preserve flip along y axis but make sure we're not inside out + Vec3 scale = ScaleHelpers::IsInsideOut(inScale)? Vec3(-1, 1, 1) * inScale : inScale; + RMat44 world_transform = inCenterOfMassTransform * Mat44::sScale(scale); + + AABox bounds = Shape::GetWorldSpaceBounds(inCenterOfMassTransform, inScale); + + float lod_scale_sq = Square(max(mTopRadius, mBottomRadius)); + + Color color = inUseMaterialColors? GetMaterial()->GetDebugColor() : inColor; + + DebugRenderer::EDrawMode draw_mode = inDrawWireframe? DebugRenderer::EDrawMode::Wireframe : DebugRenderer::EDrawMode::Solid; + + inRenderer->DrawGeometry(world_transform, bounds, lod_scale_sq, color, mGeometry, DebugRenderer::ECullMode::CullBackFace, DebugRenderer::ECastShadow::On, draw_mode); +} +#endif // JPH_DEBUG_RENDERER + +AABox TaperedCapsuleShape::GetInertiaApproximation() const +{ + // TODO: For now the mass and inertia is that of a box + float avg_radius = 0.5f * (mTopRadius + mBottomRadius); + return AABox(Vec3(-avg_radius, mBottomCenter - mBottomRadius, -avg_radius), Vec3(avg_radius, mTopCenter + mTopRadius, avg_radius)); +} + +void TaperedCapsuleShape::TransformShape(Mat44Arg inCenterOfMassTransform, TransformedShapeCollector &ioCollector) const +{ + Vec3 scale; + Mat44 transform = inCenterOfMassTransform.Decompose(scale); + TransformedShape ts(RVec3(transform.GetTranslation()), transform.GetQuaternion(), this, BodyID(), SubShapeIDCreator()); + ts.SetShapeScale(scale.GetSign() * ScaleHelpers::MakeUniformScale(scale.Abs())); + ioCollector.AddHit(ts); +} + +void TaperedCapsuleShape::SaveBinaryState(StreamOut &inStream) const +{ + ConvexShape::SaveBinaryState(inStream); + + inStream.Write(mCenterOfMass); + inStream.Write(mTopRadius); + inStream.Write(mBottomRadius); + inStream.Write(mTopCenter); + inStream.Write(mBottomCenter); + inStream.Write(mConvexRadius); + inStream.Write(mSinAlpha); + inStream.Write(mTanAlpha); +} + +void TaperedCapsuleShape::RestoreBinaryState(StreamIn &inStream) +{ + ConvexShape::RestoreBinaryState(inStream); + + inStream.Read(mCenterOfMass); + inStream.Read(mTopRadius); + inStream.Read(mBottomRadius); + inStream.Read(mTopCenter); + inStream.Read(mBottomCenter); + inStream.Read(mConvexRadius); + inStream.Read(mSinAlpha); + inStream.Read(mTanAlpha); +} + +bool TaperedCapsuleShape::IsValidScale(Vec3Arg inScale) const +{ + return ConvexShape::IsValidScale(inScale) && ScaleHelpers::IsUniformScale(inScale.Abs()); +} + +void TaperedCapsuleShape::sRegister() +{ + ShapeFunctions &f = ShapeFunctions::sGet(EShapeSubType::TaperedCapsule); + f.mConstruct = []() -> Shape * { return new TaperedCapsuleShape; }; + f.mColor = Color::sGreen; +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/TaperedCapsuleShape.gliffy b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/TaperedCapsuleShape.gliffy new file mode 100644 index 00000000000..3e5221b8655 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/TaperedCapsuleShape.gliffy @@ -0,0 +1 @@ +{"contentType":"application/gliffy+json","version":"1.1","metadata":{"title":"untitled","revision":0,"exportBorder":false},"embeddedResources":{"index":0,"resources":[]},"stage":{"objects":[{"x":870,"y":406,"rotation":0,"id":62,"uid":"com.gliffy.shape.basic.basic_v1.default.line","width":100,"height":100,"lockAspectRatio":false,"lockShape":false,"order":62,"graphic":{"type":"Line","Line":{"strokeWidth":2,"strokeColor":"#000000","fillColor":"none","dashStyle":"1.0,1.0","startArrow":0,"endArrow":0,"startArrowRotation":"auto","endArrowRotation":"auto","ortho":false,"interpolationType":"linear","cornerRadius":null,"controlPath":[[1.5,5.5],[1.5,-46.196228102251325]],"lockSegments":{}}},"children":null,"constraints":{"constraints":[],"startConstraint":{"type":"StartPositionConstraint","StartPositionConstraint":{"nodeId":4,"px":0.5,"py":0.5}}},"linkMap":[]},{"x":410,"y":406,"rotation":0,"id":60,"uid":"com.gliffy.shape.basic.basic_v1.default.line","width":100,"height":100,"lockAspectRatio":false,"lockShape":false,"order":60,"graphic":{"type":"Line","Line":{"strokeWidth":2,"strokeColor":"#000000","fillColor":"none","dashStyle":"1.0,1.0","startArrow":0,"endArrow":0,"startArrowRotation":"auto","endArrowRotation":"auto","ortho":false,"interpolationType":"linear","cornerRadius":null,"controlPath":[[0,5.5],[0,-49.03668490108288]],"lockSegments":{}}},"children":null,"constraints":{"constraints":[],"startConstraint":{"type":"StartPositionConstraint","StartPositionConstraint":{"nodeId":0,"px":0.5,"py":0.5}}},"linkMap":[]},{"x":614,"y":385,"rotation":0,"id":58,"uid":"com.gliffy.shape.basic.basic_v1.default.line","width":100,"height":100,"lockAspectRatio":false,"lockShape":false,"order":58,"graphic":{"type":"Line","Line":{"strokeWidth":2,"strokeColor":"#000000","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":2,"startArrowRotation":"auto","endArrowRotation":"auto","ortho":false,"interpolationType":"linear","cornerRadius":null,"controlPath":[[0,0],[-204.06126531020038,0]],"lockSegments":{}}},"children":null,"linkMap":[]},{"x":626,"y":385,"rotation":0,"id":57,"uid":"com.gliffy.shape.basic.basic_v1.default.line","width":100,"height":100,"lockAspectRatio":false,"lockShape":false,"order":57,"graphic":{"type":"Line","Line":{"strokeWidth":2,"strokeColor":"#000000","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":2,"startArrowRotation":"auto","endArrowRotation":"auto","ortho":false,"interpolationType":"linear","cornerRadius":null,"controlPath":[[0,0],[248.0020161208372,0]],"lockSegments":{}}},"children":null,"linkMap":[]},{"x":818,"y":520,"rotation":0,"id":55,"uid":"com.gliffy.shape.basic.basic_v1.default.line","width":100,"height":100,"lockAspectRatio":false,"lockShape":false,"order":55,"graphic":{"type":"Line","Line":{"strokeWidth":2,"strokeColor":"#000000","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":2,"startArrowRotation":"auto","endArrowRotation":"auto","ortho":false,"interpolationType":"linear","cornerRadius":null,"controlPath":[[0,0],[-48,76]],"lockSegments":{}}},"children":null,"linkMap":[]},{"x":830,"y":502,"rotation":0,"id":54,"uid":"com.gliffy.shape.basic.basic_v1.default.line","width":100,"height":100,"lockAspectRatio":false,"lockShape":false,"order":54,"graphic":{"type":"Line","Line":{"strokeWidth":2,"strokeColor":"#000000","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":2,"startArrowRotation":"auto","endArrowRotation":"auto","ortho":false,"interpolationType":"linear","cornerRadius":null,"controlPath":[[0,0],[48,-82]],"lockSegments":{}}},"children":null,"linkMap":[]},{"x":373,"y":410,"rotation":0,"id":50,"uid":"com.gliffy.shape.basic.basic_v1.default.text","width":28,"height":23,"lockAspectRatio":false,"lockShape":false,"order":19,"graphic":{"type":"Text","Text":{"tid":null,"valign":"middle","overflow":"none","vposition":"none","hposition":"none","html":"

        tx

        ","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null,"linkMap":[]},{"x":387,"y":392,"rotation":0,"id":49,"uid":"com.gliffy.shape.basic.basic_v1.default.text","width":26,"height":23,"lockAspectRatio":false,"lockShape":false,"order":18,"graphic":{"type":"Text","Text":{"tid":null,"valign":"middle","overflow":"none","vposition":"none","hposition":"none","html":"

        ty

        ","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null,"linkMap":[]},{"x":722,"y":488,"rotation":0,"id":45,"uid":"com.gliffy.shape.basic.basic_v1.default.text","width":20,"height":20,"lockAspectRatio":false,"lockShape":false,"order":16,"graphic":{"type":"Text","Text":{"tid":null,"valign":"middle","overflow":"none","vposition":"none","hposition":"none","html":"

        bx

        ","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null,"linkMap":[]},{"x":440,"y":411.5,"rotation":0,"id":40,"uid":"com.gliffy.shape.basic.basic_v1.default.text","width":29,"height":28.500000000000004,"lockAspectRatio":false,"lockShape":false,"order":13,"graphic":{"type":"Text","Text":{"tid":null,"valign":"middle","overflow":"none","vposition":"none","hposition":"none","html":"

        α

        ","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null,"linkMap":[]},{"x":411,"y":414,"rotation":0,"id":34,"uid":"com.gliffy.shape.basic.basic_v1.default.line","width":100,"height":100,"lockAspectRatio":false,"lockShape":false,"order":12,"graphic":{"type":"Line","Line":{"strokeWidth":2,"strokeColor":"#000000","fillColor":"none","dashStyle":"1.0,1.0","startArrow":0,"endArrow":0,"startArrowRotation":"auto","endArrowRotation":"auto","ortho":false,"interpolationType":"linear","cornerRadius":null,"controlPath":[[-1,-2.5],[349,180]],"lockSegments":{}}},"children":null,"constraints":{"constraints":[],"startConstraint":{"type":"StartPositionConstraint","StartPositionConstraint":{"nodeId":0,"px":0.5,"py":0.5}}},"linkMap":[]},{"x":744,"y":613,"rotation":0,"id":32,"uid":"com.gliffy.shape.basic.basic_v1.default.line","width":100,"height":100,"lockAspectRatio":false,"lockShape":false,"order":11,"graphic":{"type":"Line","Line":{"strokeWidth":2,"strokeColor":"#000000","fillColor":"none","dashStyle":"1.0,1.0","startArrow":0,"endArrow":0,"startArrowRotation":"auto","endArrowRotation":"auto","ortho":false,"interpolationType":"linear","cornerRadius":null,"controlPath":[[0,0],[-1.1368683772161603e-13,-201.00995000248122]],"lockSegments":{}}},"children":null,"linkMap":[]},{"x":394,"y":436,"rotation":0,"id":30,"uid":"com.gliffy.shape.basic.basic_v1.default.line","width":100,"height":100,"lockAspectRatio":false,"lockShape":false,"order":10,"graphic":{"type":"Line","Line":{"strokeWidth":2,"strokeColor":"#000000","fillColor":"none","dashStyle":"1.0,1.0","startArrow":0,"endArrow":0,"startArrowRotation":"auto","endArrowRotation":"auto","ortho":false,"interpolationType":"linear","cornerRadius":null,"controlPath":[[0,0],[0,-26.076809620810593]],"lockSegments":{}}},"children":null,"linkMap":[]},{"x":607,"y":368.5,"rotation":0,"id":26,"uid":"com.gliffy.shape.basic.basic_v1.default.text","width":20,"height":30,"lockAspectRatio":false,"lockShape":false,"order":9,"graphic":{"type":"Text","Text":{"tid":null,"valign":"middle","overflow":"none","vposition":"none","hposition":"none","html":"

        h

        ","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null,"linkMap":[]},{"x":401,"y":412.5,"rotation":0,"id":25,"uid":"com.gliffy.shape.basic.basic_v1.default.text","width":20,"height":30,"lockAspectRatio":false,"lockShape":false,"order":8,"graphic":{"type":"Text","Text":{"tid":null,"valign":"middle","overflow":"none","vposition":"none","hposition":"none","html":"

        tr

        ","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null,"linkMap":[]},{"x":808.5,"y":500,"rotation":0,"id":23,"uid":"com.gliffy.shape.basic.basic_v1.default.text","width":49,"height":27,"lockAspectRatio":false,"lockShape":false,"order":7,"graphic":{"type":"Text","Text":{"tid":null,"valign":"middle","overflow":"none","vposition":"none","hposition":"none","html":"

        br - tr

        ","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null,"linkMap":[]},{"x":402,"y":416,"rotation":0,"id":21,"uid":"com.gliffy.shape.basic.basic_v1.default.line","width":100,"height":100,"lockAspectRatio":false,"lockShape":false,"order":6,"graphic":{"type":"Line","Line":{"strokeWidth":2,"strokeColor":"#000000","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":0,"startArrowRotation":"auto","endArrowRotation":"auto","ortho":false,"interpolationType":"linear","cornerRadius":null,"controlPath":[[8,-4.5],[-7,21]],"lockSegments":{}}},"children":null,"constraints":{"constraints":[],"startConstraint":{"type":"StartPositionConstraint","StartPositionConstraint":{"nodeId":0,"px":0.5,"py":0.5}}},"linkMap":[]},{"x":347,"y":412,"rotation":0,"id":16,"uid":"com.gliffy.shape.basic.basic_v1.default.line","width":100,"height":100,"lockAspectRatio":false,"lockShape":false,"order":5,"graphic":{"type":"Line","Line":{"strokeWidth":2,"strokeColor":"#000000","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":0,"startArrowRotation":"auto","endArrowRotation":"auto","ortho":false,"interpolationType":"linear","cornerRadius":null,"controlPath":[[0,0],[523.5,2.5]],"lockSegments":{}}},"children":null,"linkMap":[]},{"x":854,"y":433,"rotation":0,"id":14,"uid":"com.gliffy.shape.basic.basic_v1.default.line","width":100,"height":100,"lockAspectRatio":false,"lockShape":false,"order":4,"graphic":{"type":"Line","Line":{"strokeWidth":2,"strokeColor":"#000000","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":0,"startArrowRotation":"auto","endArrowRotation":"auto","ortho":false,"interpolationType":"linear","cornerRadius":null,"controlPath":[[17.5,-21.5],[-106,184]],"lockSegments":{}}},"children":null,"constraints":{"constraints":[],"startConstraint":{"type":"StartPositionConstraint","StartPositionConstraint":{"nodeId":4,"px":0.5,"py":0.5}}},"linkMap":[]},{"x":395,"y":437,"rotation":0,"id":9,"uid":"com.gliffy.shape.basic.basic_v1.default.line","width":100,"height":100,"lockAspectRatio":false,"lockShape":false,"order":3,"graphic":{"type":"Line","Line":{"strokeWidth":2,"strokeColor":"#000000","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":0,"startArrowRotation":"auto","endArrowRotation":"auto","ortho":false,"interpolationType":"linear","cornerRadius":null,"controlPath":[[-52,-25],[695,360]],"lockSegments":{}}},"children":null,"linkMap":[]},{"x":395,"y":384,"rotation":0,"id":7,"uid":"com.gliffy.shape.basic.basic_v1.default.line","width":100,"height":100,"lockAspectRatio":false,"lockShape":false,"order":2,"graphic":{"type":"Line","Line":{"strokeWidth":2,"strokeColor":"#000000","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":0,"startArrowRotation":"auto","endArrowRotation":"auto","ortho":false,"interpolationType":"linear","cornerRadius":null,"controlPath":[[-49,26],[703,-359]],"lockSegments":{}}},"children":null,"linkMap":[]},{"x":630,"y":169.99999999999997,"rotation":0,"id":4,"uid":"com.gliffy.shape.basic.basic_v1.default.circle","width":483.00000000000006,"height":483.00000000000006,"lockAspectRatio":true,"lockShape":false,"order":1,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.ellipse.basic_v1","strokeWidth":2,"strokeColor":"#333333","fillColor":"#FFFFFF","gradient":false,"dropShadow":false,"state":0,"shadowX":0,"shadowY":0,"opacity":1}},"children":[],"linkMap":[]},{"x":380,"y":381.5,"rotation":0,"id":0,"uid":"com.gliffy.shape.basic.basic_v1.default.circle","width":60,"height":60,"lockAspectRatio":true,"lockShape":false,"order":0,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.ellipse.basic_v1","strokeWidth":2,"strokeColor":"#333333","fillColor":"#FFFFFF","gradient":false,"dropShadow":false,"state":0,"shadowX":0,"shadowY":0,"opacity":1}},"children":[],"linkMap":[]},{"x":728,"y":612,"rotation":0,"id":41,"uid":"com.gliffy.shape.basic.basic_v1.default.text","width":29,"height":28.500000000000004,"lockAspectRatio":false,"lockShape":false,"order":14,"graphic":{"type":"Text","Text":{"tid":null,"valign":"middle","overflow":"none","vposition":"none","hposition":"none","html":"

        α

        ","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null,"linkMap":[]},{"x":375,"y":432.5,"rotation":0,"id":42,"uid":"com.gliffy.shape.basic.basic_v1.default.text","width":29,"height":28.500000000000004,"lockAspectRatio":false,"lockShape":false,"order":15,"graphic":{"type":"Text","Text":{"tid":null,"valign":"middle","overflow":"none","vposition":"none","hposition":"none","html":"

        α

        ","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null,"linkMap":[]},{"x":753,"y":595.5,"rotation":0,"id":53,"uid":"com.gliffy.shape.basic.basic_v1.default.text","width":20,"height":30,"lockAspectRatio":false,"lockShape":false,"order":20,"graphic":{"type":"Text","Text":{"tid":null,"valign":"middle","overflow":"none","vposition":"none","hposition":"none","html":"

        tr

        ","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null,"linkMap":[]},{"x":794,"y":400,"rotation":0,"id":65,"uid":"com.gliffy.shape.basic.basic_v1.default.line","width":100,"height":100,"lockAspectRatio":false,"lockShape":false,"order":65,"graphic":{"type":"Line","Line":{"strokeWidth":2,"strokeColor":"#000000","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":2,"startArrowRotation":"auto","endArrowRotation":"auto","ortho":false,"interpolationType":"linear","cornerRadius":null,"controlPath":[[0,0],[-48.01041553663117,0]],"lockSegments":{}}},"children":null,"linkMap":[]},{"x":790,"y":393.25,"rotation":0,"id":46,"uid":"com.gliffy.shape.basic.basic_v1.default.text","width":29,"height":14,"lockAspectRatio":false,"lockShape":false,"order":17,"graphic":{"type":"Text","Text":{"tid":null,"valign":"middle","overflow":"none","vposition":"none","hposition":"none","html":"

        by

        ","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null,"linkMap":[]},{"x":818,"y":400,"rotation":0,"id":66,"uid":"com.gliffy.shape.basic.basic_v1.default.line","width":100,"height":100,"lockAspectRatio":false,"lockShape":false,"order":66,"graphic":{"type":"Line","Line":{"strokeWidth":2,"strokeColor":"#000000","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":2,"startArrowRotation":"auto","endArrowRotation":"auto","ortho":false,"interpolationType":"linear","cornerRadius":null,"controlPath":[[0,0],[54.230987451824944,0]],"lockSegments":{}}},"children":null,"linkMap":[]}],"background":"#FFFFFF","width":1113,"height":802,"maxWidth":5000,"maxHeight":5000,"nodeIndex":69,"autoFit":true,"exportBorder":false,"gridOn":true,"snapToGrid":false,"drawingGuidesOn":false,"pageBreaksOn":false,"printGridOn":false,"printPaper":"LETTER","printShrinkToFit":false,"printPortrait":true,"shapeStyles":{"com.gliffy.shape.basic.basic_v1.default":{"fill":"#FFFFFF","stroke":"#333333","strokeWidth":2}},"lineStyles":{"global":{"dashStyle":null,"endArrow":2,"startArrow":0}},"textStyles":{},"themeData":null}} \ No newline at end of file diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/TaperedCapsuleShape.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/TaperedCapsuleShape.h new file mode 100644 index 00000000000..6dad3c2e9b1 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/TaperedCapsuleShape.h @@ -0,0 +1,125 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#ifdef JPH_DEBUG_RENDERER + #include +#endif // JPH_DEBUG_RENDERER + +JPH_NAMESPACE_BEGIN + +/// Class that constructs a TaperedCapsuleShape +class JPH_EXPORT TaperedCapsuleShapeSettings final : public ConvexShapeSettings +{ + JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, TaperedCapsuleShapeSettings) + + /// Default constructor for deserialization + TaperedCapsuleShapeSettings() = default; + + /// Create a tapered capsule centered around the origin with one sphere cap at (0, -inHalfHeightOfTaperedCylinder, 0) with radius inBottomRadius and the other at (0, inHalfHeightOfTaperedCylinder, 0) with radius inTopRadius + TaperedCapsuleShapeSettings(float inHalfHeightOfTaperedCylinder, float inTopRadius, float inBottomRadius, const PhysicsMaterial *inMaterial = nullptr); + + /// Check if the settings are valid + bool IsValid() const { return mTopRadius > 0.0f && mBottomRadius > 0.0f && mHalfHeightOfTaperedCylinder >= 0.0f; } + + /// Checks if the settings of this tapered capsule make this shape a sphere + bool IsSphere() const; + + // See: ShapeSettings + virtual ShapeResult Create() const override; + + float mHalfHeightOfTaperedCylinder = 0.0f; + float mTopRadius = 0.0f; + float mBottomRadius = 0.0f; +}; + +/// A capsule with different top and bottom radii +class JPH_EXPORT TaperedCapsuleShape final : public ConvexShape +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Constructor + TaperedCapsuleShape() : ConvexShape(EShapeSubType::TaperedCapsule) { } + TaperedCapsuleShape(const TaperedCapsuleShapeSettings &inSettings, ShapeResult &outResult); + + // See Shape::GetCenterOfMass + virtual Vec3 GetCenterOfMass() const override { return mCenterOfMass; } + + // See Shape::GetLocalBounds + virtual AABox GetLocalBounds() const override; + + // See Shape::GetWorldSpaceBounds + virtual AABox GetWorldSpaceBounds(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale) const override; + using Shape::GetWorldSpaceBounds; + + // See Shape::GetInnerRadius + virtual float GetInnerRadius() const override { return min(mTopRadius, mBottomRadius); } + + // See Shape::GetMassProperties + virtual MassProperties GetMassProperties() const override; + + // See Shape::GetSurfaceNormal + virtual Vec3 GetSurfaceNormal(const SubShapeID &inSubShapeID, Vec3Arg inLocalSurfacePosition) const override; + + // See Shape::GetSupportingFace + virtual void GetSupportingFace(const SubShapeID &inSubShapeID, Vec3Arg inDirection, Vec3Arg inScale, Mat44Arg inCenterOfMassTransform, SupportingFace &outVertices) const override; + + // See ConvexShape::GetSupportFunction + virtual const Support * GetSupportFunction(ESupportMode inMode, SupportBuffer &inBuffer, Vec3Arg inScale) const override; + + // See: Shape::CollideSoftBodyVertices + virtual void CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, SoftBodyVertex *ioVertices, uint inNumVertices, float inDeltaTime, Vec3Arg inDisplacementDueToGravity, int inCollidingShapeIndex) const override; + +#ifdef JPH_DEBUG_RENDERER + // See Shape::Draw + virtual void Draw(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inUseMaterialColors, bool inDrawWireframe) const override; +#endif // JPH_DEBUG_RENDERER + + // See Shape::TransformShape + virtual void TransformShape(Mat44Arg inCenterOfMassTransform, TransformedShapeCollector &ioCollector) const override; + + // See Shape + virtual void SaveBinaryState(StreamOut &inStream) const override; + + // See Shape::GetStats + virtual Stats GetStats() const override { return Stats(sizeof(*this), 0); } + + // See Shape::GetVolume + virtual float GetVolume() const override { return GetLocalBounds().GetVolume(); } // Volume is approximate! + + // See Shape::IsValidScale + virtual bool IsValidScale(Vec3Arg inScale) const override; + + // Register shape functions with the registry + static void sRegister(); + +protected: + // See: Shape::RestoreBinaryState + virtual void RestoreBinaryState(StreamIn &inStream) override; + +private: + // Class for GetSupportFunction + class TaperedCapsule; + + /// Returns box that approximates the inertia + AABox GetInertiaApproximation() const; + + Vec3 mCenterOfMass = Vec3::sZero(); + float mTopRadius = 0.0f; + float mBottomRadius = 0.0f; + float mTopCenter = 0.0f; + float mBottomCenter = 0.0f; + float mConvexRadius = 0.0f; + float mSinAlpha = 0.0f; + float mTanAlpha = 0.0f; + +#ifdef JPH_DEBUG_RENDERER + mutable DebugRenderer::GeometryRef mGeometry; +#endif // JPH_DEBUG_RENDERER +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/TriangleShape.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/TriangleShape.cpp new file mode 100644 index 00000000000..b70179647d1 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/TriangleShape.cpp @@ -0,0 +1,413 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef JPH_DEBUG_RENDERER + #include +#endif // JPH_DEBUG_RENDERER + +JPH_NAMESPACE_BEGIN + +JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(TriangleShapeSettings) +{ + JPH_ADD_BASE_CLASS(TriangleShapeSettings, ConvexShapeSettings) + + JPH_ADD_ATTRIBUTE(TriangleShapeSettings, mV1) + JPH_ADD_ATTRIBUTE(TriangleShapeSettings, mV2) + JPH_ADD_ATTRIBUTE(TriangleShapeSettings, mV3) + JPH_ADD_ATTRIBUTE(TriangleShapeSettings, mConvexRadius) +} + +ShapeSettings::ShapeResult TriangleShapeSettings::Create() const +{ + if (mCachedResult.IsEmpty()) + Ref shape = new TriangleShape(*this, mCachedResult); + return mCachedResult; +} + +TriangleShape::TriangleShape(const TriangleShapeSettings &inSettings, ShapeResult &outResult) : + ConvexShape(EShapeSubType::Triangle, inSettings, outResult), + mV1(inSettings.mV1), + mV2(inSettings.mV2), + mV3(inSettings.mV3), + mConvexRadius(inSettings.mConvexRadius) +{ + if (inSettings.mConvexRadius < 0.0f) + { + outResult.SetError("Invalid convex radius"); + return; + } + + outResult.Set(this); +} + +AABox TriangleShape::GetLocalBounds() const +{ + AABox bounds(mV1, mV1); + bounds.Encapsulate(mV2); + bounds.Encapsulate(mV3); + bounds.ExpandBy(Vec3::sReplicate(mConvexRadius)); + return bounds; +} + +AABox TriangleShape::GetWorldSpaceBounds(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale) const +{ + JPH_ASSERT(IsValidScale(inScale)); + + Vec3 v1 = inCenterOfMassTransform * (inScale * mV1); + Vec3 v2 = inCenterOfMassTransform * (inScale * mV2); + Vec3 v3 = inCenterOfMassTransform * (inScale * mV3); + + AABox bounds(v1, v1); + bounds.Encapsulate(v2); + bounds.Encapsulate(v3); + bounds.ExpandBy(inScale * mConvexRadius); + return bounds; +} + +class TriangleShape::TriangleNoConvex final : public Support +{ +public: + TriangleNoConvex(Vec3Arg inV1, Vec3Arg inV2, Vec3Arg inV3) : + mTriangleSuport(inV1, inV2, inV3) + { + static_assert(sizeof(TriangleNoConvex) <= sizeof(SupportBuffer), "Buffer size too small"); + JPH_ASSERT(IsAligned(this, alignof(TriangleNoConvex))); + } + + virtual Vec3 GetSupport(Vec3Arg inDirection) const override + { + return mTriangleSuport.GetSupport(inDirection); + } + + virtual float GetConvexRadius() const override + { + return 0.0f; + } + +private: + TriangleConvexSupport mTriangleSuport; +}; + +class TriangleShape::TriangleWithConvex final : public Support +{ +public: + TriangleWithConvex(Vec3Arg inV1, Vec3Arg inV2, Vec3Arg inV3, float inConvexRadius) : + mConvexRadius(inConvexRadius), + mTriangleSuport(inV1, inV2, inV3) + { + static_assert(sizeof(TriangleWithConvex) <= sizeof(SupportBuffer), "Buffer size too small"); + JPH_ASSERT(IsAligned(this, alignof(TriangleWithConvex))); + } + + virtual Vec3 GetSupport(Vec3Arg inDirection) const override + { + Vec3 support = mTriangleSuport.GetSupport(inDirection); + float len = inDirection.Length(); + if (len > 0.0f) + support += (mConvexRadius / len) * inDirection; + return support; + } + + virtual float GetConvexRadius() const override + { + return mConvexRadius; + } + +private: + float mConvexRadius; + TriangleConvexSupport mTriangleSuport; +}; + +const ConvexShape::Support *TriangleShape::GetSupportFunction(ESupportMode inMode, SupportBuffer &inBuffer, Vec3Arg inScale) const +{ + switch (inMode) + { + case ESupportMode::IncludeConvexRadius: + case ESupportMode::Default: + if (mConvexRadius > 0.0f) + return new (&inBuffer) TriangleWithConvex(inScale * mV1, inScale * mV2, inScale * mV3, mConvexRadius); + [[fallthrough]]; + + case ESupportMode::ExcludeConvexRadius: + return new (&inBuffer) TriangleNoConvex(inScale * mV1, inScale * mV2, inScale * mV3); + } + + JPH_ASSERT(false); + return nullptr; +} + +void TriangleShape::GetSupportingFace(const SubShapeID &inSubShapeID, Vec3Arg inDirection, Vec3Arg inScale, Mat44Arg inCenterOfMassTransform, SupportingFace &outVertices) const +{ + JPH_ASSERT(inSubShapeID.IsEmpty(), "Invalid subshape ID"); + + // Calculate transform with scale + Mat44 transform = inCenterOfMassTransform.PreScaled(inScale); + + // Flip triangle if scaled inside out + if (ScaleHelpers::IsInsideOut(inScale)) + { + outVertices.push_back(transform * mV1); + outVertices.push_back(transform * mV3); + outVertices.push_back(transform * mV2); + } + else + { + outVertices.push_back(transform * mV1); + outVertices.push_back(transform * mV2); + outVertices.push_back(transform * mV3); + } +} + +MassProperties TriangleShape::GetMassProperties() const +{ + // Object should always be static, return default mass properties + return MassProperties(); +} + +Vec3 TriangleShape::GetSurfaceNormal(const SubShapeID &inSubShapeID, Vec3Arg inLocalSurfacePosition) const +{ + JPH_ASSERT(inSubShapeID.IsEmpty(), "Invalid subshape ID"); + + Vec3 cross = (mV2 - mV1).Cross(mV3 - mV1); + float len = cross.Length(); + return len != 0.0f? cross / len : Vec3::sAxisY(); +} + +void TriangleShape::GetSubmergedVolume(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, const Plane &inSurface, float &outTotalVolume, float &outSubmergedVolume, Vec3 &outCenterOfBuoyancy JPH_IF_DEBUG_RENDERER(, RVec3Arg inBaseOffset)) const +{ + // A triangle has no volume + outTotalVolume = outSubmergedVolume = 0.0f; + outCenterOfBuoyancy = Vec3::sZero(); +} + +#ifdef JPH_DEBUG_RENDERER +void TriangleShape::Draw(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inUseMaterialColors, bool inDrawWireframe) const +{ + RVec3 v1 = inCenterOfMassTransform * (inScale * mV1); + RVec3 v2 = inCenterOfMassTransform * (inScale * mV2); + RVec3 v3 = inCenterOfMassTransform * (inScale * mV3); + + if (ScaleHelpers::IsInsideOut(inScale)) + swap(v1, v2); + + if (inDrawWireframe) + inRenderer->DrawWireTriangle(v1, v2, v3, inUseMaterialColors? GetMaterial()->GetDebugColor() : inColor); + else + inRenderer->DrawTriangle(v1, v2, v3, inUseMaterialColors? GetMaterial()->GetDebugColor() : inColor); +} +#endif // JPH_DEBUG_RENDERER + +bool TriangleShape::CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const +{ + float fraction = RayTriangle(inRay.mOrigin, inRay.mDirection, mV1, mV2, mV3); + if (fraction < ioHit.mFraction) + { + ioHit.mFraction = fraction; + ioHit.mSubShapeID2 = inSubShapeIDCreator.GetID(); + return true; + } + return false; +} + +void TriangleShape::CastRay(const RayCast &inRay, const RayCastSettings &inRayCastSettings, const SubShapeIDCreator &inSubShapeIDCreator, CastRayCollector &ioCollector, const ShapeFilter &inShapeFilter) const +{ + // Test shape filter + if (!inShapeFilter.ShouldCollide(this, inSubShapeIDCreator.GetID())) + return; + + // Back facing check + if (inRayCastSettings.mBackFaceMode == EBackFaceMode::IgnoreBackFaces && (mV2 - mV1).Cross(mV3 - mV1).Dot(inRay.mDirection) > 0.0f) + return; + + // Test ray against triangle + float fraction = RayTriangle(inRay.mOrigin, inRay.mDirection, mV1, mV2, mV3); + if (fraction < ioCollector.GetEarlyOutFraction()) + { + // Better hit than the current hit + RayCastResult hit; + hit.mBodyID = TransformedShape::sGetBodyID(ioCollector.GetContext()); + hit.mFraction = fraction; + hit.mSubShapeID2 = inSubShapeIDCreator.GetID(); + ioCollector.AddHit(hit); + } +} + +void TriangleShape::CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter) const +{ + // Can't be inside a triangle +} + +void TriangleShape::CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, SoftBodyVertex *ioVertices, uint inNumVertices, [[maybe_unused]] float inDeltaTime, [[maybe_unused]] Vec3Arg inDisplacementDueToGravity, int inCollidingShapeIndex) const +{ + CollideSoftBodyVerticesVsTriangles collider(inCenterOfMassTransform, inScale); + + for (SoftBodyVertex *v = ioVertices, *sbv_end = ioVertices + inNumVertices; v < sbv_end; ++v) + if (v->mInvMass > 0.0f) + { + collider.StartVertex(*v); + collider.ProcessTriangle(mV1, mV2, mV3); + collider.FinishVertex(*v, inCollidingShapeIndex); + } +} + +void TriangleShape::sCollideConvexVsTriangle(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, [[maybe_unused]] const ShapeFilter &inShapeFilter) +{ + JPH_ASSERT(inShape1->GetType() == EShapeType::Convex); + const ConvexShape *shape1 = static_cast(inShape1); + JPH_ASSERT(inShape2->GetSubType() == EShapeSubType::Triangle); + const TriangleShape *shape2 = static_cast(inShape2); + + CollideConvexVsTriangles collider(shape1, inScale1, inScale2, inCenterOfMassTransform1, inCenterOfMassTransform2, inSubShapeIDCreator1.GetID(), inCollideShapeSettings, ioCollector); + collider.Collide(shape2->mV1, shape2->mV2, shape2->mV3, 0b111, inSubShapeIDCreator2.GetID()); +} + +void TriangleShape::sCollideSphereVsTriangle(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, [[maybe_unused]] const ShapeFilter &inShapeFilter) +{ + JPH_ASSERT(inShape1->GetSubType() == EShapeSubType::Sphere); + const SphereShape *shape1 = static_cast(inShape1); + JPH_ASSERT(inShape2->GetSubType() == EShapeSubType::Triangle); + const TriangleShape *shape2 = static_cast(inShape2); + + CollideSphereVsTriangles collider(shape1, inScale1, inScale2, inCenterOfMassTransform1, inCenterOfMassTransform2, inSubShapeIDCreator1.GetID(), inCollideShapeSettings, ioCollector); + collider.Collide(shape2->mV1, shape2->mV2, shape2->mV3, 0b111, inSubShapeIDCreator2.GetID()); +} + +void TriangleShape::sCastConvexVsTriangle(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, [[maybe_unused]] const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector) +{ + JPH_ASSERT(inShape->GetSubType() == EShapeSubType::Triangle); + const TriangleShape *shape = static_cast(inShape); + + CastConvexVsTriangles caster(inShapeCast, inShapeCastSettings, inScale, inCenterOfMassTransform2, inSubShapeIDCreator1, ioCollector); + caster.Cast(shape->mV1, shape->mV2, shape->mV3, 0b111, inSubShapeIDCreator2.GetID()); +} + +void TriangleShape::sCastSphereVsTriangle(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, [[maybe_unused]] const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector) +{ + JPH_ASSERT(inShape->GetSubType() == EShapeSubType::Triangle); + const TriangleShape *shape = static_cast(inShape); + + CastSphereVsTriangles caster(inShapeCast, inShapeCastSettings, inScale, inCenterOfMassTransform2, inSubShapeIDCreator1, ioCollector); + caster.Cast(shape->mV1, shape->mV2, shape->mV3, 0b111, inSubShapeIDCreator2.GetID()); +} + +void TriangleShape::TransformShape(Mat44Arg inCenterOfMassTransform, TransformedShapeCollector &ioCollector) const +{ + Vec3 scale; + Mat44 transform = inCenterOfMassTransform.Decompose(scale); + TransformedShape ts(RVec3(transform.GetTranslation()), transform.GetQuaternion(), this, BodyID(), SubShapeIDCreator()); + ts.SetShapeScale(mConvexRadius == 0.0f? scale : scale.GetSign() * ScaleHelpers::MakeUniformScale(scale.Abs())); + ioCollector.AddHit(ts); +} + +class TriangleShape::TSGetTrianglesContext +{ +public: + TSGetTrianglesContext(Vec3Arg inV1, Vec3Arg inV2, Vec3Arg inV3) : mV1(inV1), mV2(inV2), mV3(inV3) { } + + Vec3 mV1; + Vec3 mV2; + Vec3 mV3; + + bool mIsDone = false; +}; + +void TriangleShape::GetTrianglesStart(GetTrianglesContext &ioContext, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) const +{ + static_assert(sizeof(TSGetTrianglesContext) <= sizeof(GetTrianglesContext), "GetTrianglesContext too small"); + JPH_ASSERT(IsAligned(&ioContext, alignof(TSGetTrianglesContext))); + + Mat44 m = Mat44::sRotationTranslation(inRotation, inPositionCOM) * Mat44::sScale(inScale); + + new (&ioContext) TSGetTrianglesContext(m * mV1, m * mV2, m * mV3); +} + +int TriangleShape::GetTrianglesNext(GetTrianglesContext &ioContext, int inMaxTrianglesRequested, Float3 *outTriangleVertices, const PhysicsMaterial **outMaterials) const +{ + static_assert(cGetTrianglesMinTrianglesRequested >= 3, "cGetTrianglesMinTrianglesRequested is too small"); + JPH_ASSERT(inMaxTrianglesRequested >= cGetTrianglesMinTrianglesRequested); + + TSGetTrianglesContext &context = (TSGetTrianglesContext &)ioContext; + + // Only return the triangle the 1st time + if (context.mIsDone) + return 0; + context.mIsDone = true; + + // Store triangle + context.mV1.StoreFloat3(outTriangleVertices); + context.mV2.StoreFloat3(outTriangleVertices + 1); + context.mV3.StoreFloat3(outTriangleVertices + 2); + + // Store material + if (outMaterials != nullptr) + *outMaterials = GetMaterial(); + + return 1; +} + +void TriangleShape::SaveBinaryState(StreamOut &inStream) const +{ + ConvexShape::SaveBinaryState(inStream); + + inStream.Write(mV1); + inStream.Write(mV2); + inStream.Write(mV3); + inStream.Write(mConvexRadius); +} + +void TriangleShape::RestoreBinaryState(StreamIn &inStream) +{ + ConvexShape::RestoreBinaryState(inStream); + + inStream.Read(mV1); + inStream.Read(mV2); + inStream.Read(mV3); + inStream.Read(mConvexRadius); +} + +bool TriangleShape::IsValidScale(Vec3Arg inScale) const +{ + return ConvexShape::IsValidScale(inScale) && (mConvexRadius == 0.0f || ScaleHelpers::IsUniformScale(inScale.Abs())); +} + +void TriangleShape::sRegister() +{ + ShapeFunctions &f = ShapeFunctions::sGet(EShapeSubType::Triangle); + f.mConstruct = []() -> Shape * { return new TriangleShape; }; + f.mColor = Color::sGreen; + + for (EShapeSubType s : sConvexSubShapeTypes) + { + CollisionDispatch::sRegisterCollideShape(s, EShapeSubType::Triangle, sCollideConvexVsTriangle); + CollisionDispatch::sRegisterCastShape(s, EShapeSubType::Triangle, sCastConvexVsTriangle); + } + + // Specialized collision functions + CollisionDispatch::sRegisterCollideShape(EShapeSubType::Sphere, EShapeSubType::Triangle, sCollideSphereVsTriangle); + CollisionDispatch::sRegisterCastShape(EShapeSubType::Sphere, EShapeSubType::Triangle, sCastSphereVsTriangle); +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/TriangleShape.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/TriangleShape.h new file mode 100644 index 00000000000..990e6e051b8 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/TriangleShape.h @@ -0,0 +1,138 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +/// Class that constructs a TriangleShape +class JPH_EXPORT TriangleShapeSettings final : public ConvexShapeSettings +{ +public: + JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, TriangleShapeSettings) + + /// Default constructor for deserialization + TriangleShapeSettings() = default; + + /// Create a triangle with points (inV1, inV2, inV3) (counter clockwise) and convex radius inConvexRadius. + /// Note that the convex radius is currently only used for shape vs shape collision, for all other purposes the triangle is infinitely thin. + TriangleShapeSettings(Vec3Arg inV1, Vec3Arg inV2, Vec3Arg inV3, float inConvexRadius = 0.0f, const PhysicsMaterial *inMaterial = nullptr) : ConvexShapeSettings(inMaterial), mV1(inV1), mV2(inV2), mV3(inV3), mConvexRadius(inConvexRadius) { } + + // See: ShapeSettings + virtual ShapeResult Create() const override; + + Vec3 mV1; + Vec3 mV2; + Vec3 mV3; + float mConvexRadius = 0.0f; +}; + +/// A single triangle, not the most efficient way of creating a world filled with triangles but can be used as a query shape for example. +class JPH_EXPORT TriangleShape final : public ConvexShape +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Constructor + TriangleShape() : ConvexShape(EShapeSubType::Triangle) { } + TriangleShape(const TriangleShapeSettings &inSettings, ShapeResult &outResult); + + /// Create a triangle with points (inV1, inV2, inV3) (counter clockwise) and convex radius inConvexRadius. + /// Note that the convex radius is currently only used for shape vs shape collision, for all other purposes the triangle is infinitely thin. + TriangleShape(Vec3Arg inV1, Vec3Arg inV2, Vec3Arg inV3, float inConvexRadius = 0.0f, const PhysicsMaterial *inMaterial = nullptr) : ConvexShape(EShapeSubType::Triangle, inMaterial), mV1(inV1), mV2(inV2), mV3(inV3), mConvexRadius(inConvexRadius) { JPH_ASSERT(inConvexRadius >= 0.0f); } + + /// Convex radius + float GetConvexRadius() const { return mConvexRadius; } + + // See Shape::GetLocalBounds + virtual AABox GetLocalBounds() const override; + + // See Shape::GetWorldSpaceBounds + virtual AABox GetWorldSpaceBounds(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale) const override; + using Shape::GetWorldSpaceBounds; + + // See Shape::GetInnerRadius + virtual float GetInnerRadius() const override { return mConvexRadius; } + + // See Shape::GetMassProperties + virtual MassProperties GetMassProperties() const override; + + // See Shape::GetSurfaceNormal + virtual Vec3 GetSurfaceNormal(const SubShapeID &inSubShapeID, Vec3Arg inLocalSurfacePosition) const override; + + // See Shape::GetSupportingFace + virtual void GetSupportingFace(const SubShapeID &inSubShapeID, Vec3Arg inDirection, Vec3Arg inScale, Mat44Arg inCenterOfMassTransform, SupportingFace &outVertices) const override; + + // See ConvexShape::GetSupportFunction + virtual const Support * GetSupportFunction(ESupportMode inMode, SupportBuffer &inBuffer, Vec3Arg inScale) const override; + + // See Shape::GetSubmergedVolume + virtual void GetSubmergedVolume(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, const Plane &inSurface, float &outTotalVolume, float &outSubmergedVolume, Vec3 &outCenterOfBuoyancy JPH_IF_DEBUG_RENDERER(, RVec3Arg inBaseOffset)) const override; + +#ifdef JPH_DEBUG_RENDERER + // See Shape::Draw + virtual void Draw(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inUseMaterialColors, bool inDrawWireframe) const override; +#endif // JPH_DEBUG_RENDERER + + // See Shape::CastRay + virtual bool CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const override; + virtual void CastRay(const RayCast &inRay, const RayCastSettings &inRayCastSettings, const SubShapeIDCreator &inSubShapeIDCreator, CastRayCollector &ioCollector, const ShapeFilter &inShapeFilter = { }) const override; + + // See: Shape::CollidePoint + virtual void CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter = { }) const override; + + // See: Shape::CollideSoftBodyVertices + virtual void CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, SoftBodyVertex *ioVertices, uint inNumVertices, float inDeltaTime, Vec3Arg inDisplacementDueToGravity, int inCollidingShapeIndex) const override; + + // See Shape::TransformShape + virtual void TransformShape(Mat44Arg inCenterOfMassTransform, TransformedShapeCollector &ioCollector) const override; + + // See Shape::GetTrianglesStart + virtual void GetTrianglesStart(GetTrianglesContext &ioContext, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) const override; + + // See Shape::GetTrianglesNext + virtual int GetTrianglesNext(GetTrianglesContext &ioContext, int inMaxTrianglesRequested, Float3 *outTriangleVertices, const PhysicsMaterial **outMaterials = nullptr) const override; + + // See Shape + virtual void SaveBinaryState(StreamOut &inStream) const override; + + // See Shape::GetStats + virtual Stats GetStats() const override { return Stats(sizeof(*this), 1); } + + // See Shape::GetVolume + virtual float GetVolume() const override { return 0; } + + // See Shape::IsValidScale + virtual bool IsValidScale(Vec3Arg inScale) const override; + + // Register shape functions with the registry + static void sRegister(); + +protected: + // See: Shape::RestoreBinaryState + virtual void RestoreBinaryState(StreamIn &inStream) override; + +private: + // Helper functions called by CollisionDispatch + static void sCollideConvexVsTriangle(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter); + static void sCollideSphereVsTriangle(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter); + static void sCastConvexVsTriangle(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector); + static void sCastSphereVsTriangle(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector); + + // Context for GetTrianglesStart/Next + class TSGetTrianglesContext; + + // Classes for GetSupportFunction + class TriangleNoConvex; + class TriangleWithConvex; + + Vec3 mV1; + Vec3 mV2; + Vec3 mV3; + float mConvexRadius = 0.0f; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ShapeCast.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ShapeCast.h new file mode 100644 index 00000000000..e7741b84d82 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ShapeCast.h @@ -0,0 +1,170 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +/// Structure that holds a single shape cast (a shape moving along a linear path in 3d space with no rotation) +template +struct ShapeCastT +{ + JPH_OVERRIDE_NEW_DELETE + + /// Constructor + ShapeCastT(const Shape *inShape, Vec3Arg inScale, typename Mat::ArgType inCenterOfMassStart, Vec3Arg inDirection, const AABox &inWorldSpaceBounds) : + mShape(inShape), + mScale(inScale), + mCenterOfMassStart(inCenterOfMassStart), + mDirection(inDirection), + mShapeWorldBounds(inWorldSpaceBounds) + { + } + + /// Constructor + ShapeCastT(const Shape *inShape, Vec3Arg inScale, typename Mat::ArgType inCenterOfMassStart, Vec3Arg inDirection) : + ShapeCastT(inShape, inScale, inCenterOfMassStart, inDirection, inShape->GetWorldSpaceBounds(inCenterOfMassStart, inScale)) + { + } + + /// Construct a shape cast using a world transform for a shape instead of a center of mass transform + static inline ShapeCastType sFromWorldTransform(const Shape *inShape, Vec3Arg inScale, typename Mat::ArgType inWorldTransform, Vec3Arg inDirection) + { + return ShapeCastType(inShape, inScale, inWorldTransform.PreTranslated(inShape->GetCenterOfMass()), inDirection); + } + + /// Transform this shape cast using inTransform. Multiply transform on the left left hand side. + ShapeCastType PostTransformed(typename Mat::ArgType inTransform) const + { + Mat44 start = inTransform * mCenterOfMassStart; + Vec3 direction = inTransform.Multiply3x3(mDirection); + return { mShape, mScale, start, direction }; + } + + /// Translate this shape cast by inTranslation. + ShapeCastType PostTranslated(typename Vec::ArgType inTranslation) const + { + return { mShape, mScale, mCenterOfMassStart.PostTranslated(inTranslation), mDirection }; + } + + /// Get point with fraction inFraction on ray from mCenterOfMassStart to mCenterOfMassStart + mDirection (0 = start of ray, 1 = end of ray) + inline Vec GetPointOnRay(float inFraction) const + { + return mCenterOfMassStart.GetTranslation() + inFraction * mDirection; + } + + const Shape * mShape; ///< Shape that's being cast (cannot be mesh shape). Note that this structure does not assume ownership over the shape for performance reasons. + const Vec3 mScale; ///< Scale in local space of the shape being cast + const Mat mCenterOfMassStart; ///< Start position and orientation of the center of mass of the shape (construct using sFromWorldTransform if you have a world transform for your shape) + const Vec3 mDirection; ///< Direction and length of the cast (anything beyond this length will not be reported as a hit) + const AABox mShapeWorldBounds; ///< Cached shape's world bounds, calculated in constructor +}; + +struct ShapeCast : public ShapeCastT +{ + using ShapeCastT::ShapeCastT; +}; + +struct RShapeCast : public ShapeCastT +{ + using ShapeCastT::ShapeCastT; + + /// Convert from ShapeCast, converts single to double precision + explicit RShapeCast(const ShapeCast &inCast) : + RShapeCast(inCast.mShape, inCast.mScale, RMat44(inCast.mCenterOfMassStart), inCast.mDirection, inCast.mShapeWorldBounds) + { + } + + /// Convert to ShapeCast, which implies casting from double precision to single precision + explicit operator ShapeCast() const + { + return ShapeCast(mShape, mScale, mCenterOfMassStart.ToMat44(), mDirection, mShapeWorldBounds); + } +}; + +/// Settings to be passed with a shape cast +class ShapeCastSettings : public CollideSettingsBase +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// How backfacing triangles should be treated (should we report moving out of a triangle?) + EBackFaceMode mBackFaceModeTriangles = EBackFaceMode::IgnoreBackFaces; + + /// How backfacing convex objects should be treated (should we report starting inside an object and moving out?) + EBackFaceMode mBackFaceModeConvex = EBackFaceMode::IgnoreBackFaces; + + /// Indicates if we want to shrink the shape by the convex radius and then expand it again. This speeds up collision detection and gives a more accurate normal at the cost of a more 'rounded' shape. + bool mUseShrunkenShapeAndConvexRadius = false; + + /// When true, and the shape is intersecting at the beginning of the cast (fraction = 0) then this will calculate the deepest penetration point (costing additional CPU time) + bool mReturnDeepestPoint = false; +}; + +/// Result of a shape cast test +class ShapeCastResult : public CollideShapeResult +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Default constructor + ShapeCastResult() = default; + + /// Constructor + /// @param inFraction Fraction at which the cast hit + /// @param inContactPoint1 Contact point on shape 1 + /// @param inContactPoint2 Contact point on shape 2 + /// @param inContactNormalOrPenetrationDepth Contact normal pointing from shape 1 to 2 or penetration depth vector when the objects are penetrating (also from 1 to 2) + /// @param inBackFaceHit If this hit was a back face hit + /// @param inSubShapeID1 Sub shape id for shape 1 + /// @param inSubShapeID2 Sub shape id for shape 2 + /// @param inBodyID2 BodyID that was hit + ShapeCastResult(float inFraction, Vec3Arg inContactPoint1, Vec3Arg inContactPoint2, Vec3Arg inContactNormalOrPenetrationDepth, bool inBackFaceHit, const SubShapeID &inSubShapeID1, const SubShapeID &inSubShapeID2, const BodyID &inBodyID2) : + CollideShapeResult(inContactPoint1, inContactPoint2, inContactNormalOrPenetrationDepth, (inContactPoint2 - inContactPoint1).Length(), inSubShapeID1, inSubShapeID2, inBodyID2), + mFraction(inFraction), + mIsBackFaceHit(inBackFaceHit) + { + } + + /// Function required by the CollisionCollector. A smaller fraction is considered to be a 'better hit'. For rays/cast shapes we can just use the collision fraction. The fraction and penetration depth are combined in such a way that deeper hits at fraction 0 go first. + inline float GetEarlyOutFraction() const { return mFraction > 0.0f? mFraction : -mPenetrationDepth; } + + /// Reverses the hit result, swapping contact point 1 with contact point 2 etc. + /// @param inWorldSpaceCastDirection Direction of the shape cast in world space + ShapeCastResult Reversed(Vec3Arg inWorldSpaceCastDirection) const + { + // Calculate by how much to shift the contact points + Vec3 delta = mFraction * inWorldSpaceCastDirection; + + ShapeCastResult result; + result.mContactPointOn2 = mContactPointOn1 - delta; + result.mContactPointOn1 = mContactPointOn2 - delta; + result.mPenetrationAxis = -mPenetrationAxis; + result.mPenetrationDepth = mPenetrationDepth; + result.mSubShapeID2 = mSubShapeID1; + result.mSubShapeID1 = mSubShapeID2; + result.mBodyID2 = mBodyID2; + result.mFraction = mFraction; + result.mIsBackFaceHit = mIsBackFaceHit; + + result.mShape2Face.resize(mShape1Face.size()); + for (Face::size_type i = 0; i < mShape1Face.size(); ++i) + result.mShape2Face[i] = mShape1Face[i] - delta; + + result.mShape1Face.resize(mShape2Face.size()); + for (Face::size_type i = 0; i < mShape2Face.size(); ++i) + result.mShape1Face[i] = mShape2Face[i] - delta; + + return result; + } + + float mFraction; ///< This is the fraction where the shape hit the other shape: CenterOfMassOnHit = Start + value * (End - Start) + bool mIsBackFaceHit; ///< True if the shape was hit from the back side +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ShapeFilter.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ShapeFilter.h new file mode 100644 index 00000000000..0b5ebbefd3d --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ShapeFilter.h @@ -0,0 +1,72 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +JPH_NAMESPACE_BEGIN + +class Shape; +class SubShapeID; + +/// Filter class +class ShapeFilter : public NonCopyable +{ +public: + /// Destructor + virtual ~ShapeFilter() = default; + + /// Filter function to determine if we should collide with a shape. Returns true if the filter passes. + /// This overload is called when the query doesn't have a source shape (e.g. ray cast / collide point) + /// @param inShape2 Shape we're colliding against + /// @param inSubShapeIDOfShape2 The sub shape ID that will lead from the root shape to inShape2 (i.e. the shape of mBodyID2) + virtual bool ShouldCollide([[maybe_unused]] const Shape *inShape2, [[maybe_unused]] const SubShapeID &inSubShapeIDOfShape2) const + { + return true; + } + + /// Filter function to determine if two shapes should collide. Returns true if the filter passes. + /// This overload is called when querying a shape vs a shape (e.g. collide object / cast object). + /// It is called at each level of the shape hierarchy, so if you have a compound shape with a box, this function will be called twice. + /// It will not be called on triangles that are part of another shape, i.e a mesh shape will not trigger a callback per triangle. You can filter out individual triangles in the CollisionCollector::AddHit function by their sub shape ID. + /// @param inShape1 1st shape that is colliding + /// @param inSubShapeIDOfShape1 The sub shape ID that will lead from the root shape to inShape1 (i.e. the shape that is used to collide or cast against shape 2) + /// @param inShape2 2nd shape that is colliding + /// @param inSubShapeIDOfShape2 The sub shape ID that will lead from the root shape to inShape2 (i.e. the shape of mBodyID2) + virtual bool ShouldCollide([[maybe_unused]] const Shape *inShape1, [[maybe_unused]] const SubShapeID &inSubShapeIDOfShape1, [[maybe_unused]] const Shape *inShape2, [[maybe_unused]] const SubShapeID &inSubShapeIDOfShape2) const + { + return true; + } + + /// Set by the collision detection functions to the body ID of the body that we're colliding against before calling the ShouldCollide function + mutable BodyID mBodyID2; +}; + +/// Helper class to reverse the order of the shapes in the ShouldCollide function +class ReversedShapeFilter : public ShapeFilter +{ +public: + /// Constructor + explicit ReversedShapeFilter(const ShapeFilter &inFilter) : mFilter(inFilter) + { + mBodyID2 = inFilter.mBodyID2; + } + + virtual bool ShouldCollide(const Shape *inShape2, const SubShapeID &inSubShapeIDOfShape2) const override + { + return mFilter.ShouldCollide(inShape2, inSubShapeIDOfShape2); + } + + virtual bool ShouldCollide(const Shape *inShape1, const SubShapeID &inSubShapeIDOfShape1, const Shape *inShape2, const SubShapeID &inSubShapeIDOfShape2) const override + { + return mFilter.ShouldCollide(inShape2, inSubShapeIDOfShape2, inShape1, inSubShapeIDOfShape1); + } + +private: + const ShapeFilter & mFilter; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/SortReverseAndStore.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/SortReverseAndStore.h new file mode 100644 index 00000000000..4a073096c26 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/SortReverseAndStore.h @@ -0,0 +1,48 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +JPH_NAMESPACE_BEGIN + +/// This function will sort values from high to low and only keep the ones that are less than inMaxValue +/// @param inValues Values to be sorted +/// @param inMaxValue Values need to be less than this to keep them +/// @param ioIdentifiers 4 identifiers that will be sorted in the same way as the values +/// @param outValues The values are stored here from high to low +/// @return The number of values that were kept +JPH_INLINE int SortReverseAndStore(Vec4Arg inValues, float inMaxValue, UVec4 &ioIdentifiers, float *outValues) +{ + // Sort so that highest values are first (we want to first process closer hits and we process stack top to bottom) + Vec4 values = inValues; + Vec4::sSort4Reverse(values, ioIdentifiers); + + // Count how many results are less than the max value + UVec4 closer = Vec4::sLess(values, Vec4::sReplicate(inMaxValue)); + int num_results = closer.CountTrues(); + + // Shift the values so that only the ones that are less than max are kept + values = values.ReinterpretAsInt().ShiftComponents4Minus(num_results).ReinterpretAsFloat(); + ioIdentifiers = ioIdentifiers.ShiftComponents4Minus(num_results); + + // Store the values + values.StoreFloat4(reinterpret_cast(outValues)); + + return num_results; +} + +/// Shift the elements so that the identifiers that correspond with the trues in inValue come first +/// @param inValue Values to test for true or false +/// @param ioIdentifiers the identifiers that are shifted, on return they are shifted +/// @return The number of trues +JPH_INLINE int CountAndSortTrues(UVec4Arg inValue, UVec4 &ioIdentifiers) +{ + // Sort the hits + ioIdentifiers = UVec4::sSort4True(inValue, ioIdentifiers); + + // Return the amount of hits + return inValue.CountTrues(); +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/TransformedShape.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/TransformedShape.cpp new file mode 100644 index 00000000000..470387b52a6 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/TransformedShape.cpp @@ -0,0 +1,180 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +bool TransformedShape::CastRay(const RRayCast &inRay, RayCastResult &ioHit) const +{ + if (mShape != nullptr) + { + // Transform the ray to local space, note that this drops precision which is possible because we're in local space now + RayCast ray(inRay.Transformed(GetInverseCenterOfMassTransform())); + + // Scale the ray + Vec3 inv_scale = GetShapeScale().Reciprocal(); + ray.mOrigin *= inv_scale; + ray.mDirection *= inv_scale; + + // Cast the ray on the shape + SubShapeIDCreator sub_shape_id(mSubShapeIDCreator); + if (mShape->CastRay(ray, sub_shape_id, ioHit)) + { + // Set body ID on the hit result + ioHit.mBodyID = mBodyID; + + return true; + } + } + + return false; +} + +void TransformedShape::CastRay(const RRayCast &inRay, const RayCastSettings &inRayCastSettings, CastRayCollector &ioCollector, const ShapeFilter &inShapeFilter) const +{ + if (mShape != nullptr) + { + // Set the context on the collector and filter + ioCollector.SetContext(this); + inShapeFilter.mBodyID2 = mBodyID; + + // Transform the ray to local space, note that this drops precision which is possible because we're in local space now + RayCast ray(inRay.Transformed(GetInverseCenterOfMassTransform())); + + // Scale the ray + Vec3 inv_scale = GetShapeScale().Reciprocal(); + ray.mOrigin *= inv_scale; + ray.mDirection *= inv_scale; + + // Cast the ray on the shape + SubShapeIDCreator sub_shape_id(mSubShapeIDCreator); + mShape->CastRay(ray, inRayCastSettings, sub_shape_id, ioCollector, inShapeFilter); + } +} + +void TransformedShape::CollidePoint(RVec3Arg inPoint, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter) const +{ + if (mShape != nullptr) + { + // Set the context on the collector and filter + ioCollector.SetContext(this); + inShapeFilter.mBodyID2 = mBodyID; + + // Transform and scale the point to local space + Vec3 point = Vec3(GetInverseCenterOfMassTransform() * inPoint) / GetShapeScale(); + + // Do point collide on the shape + SubShapeIDCreator sub_shape_id(mSubShapeIDCreator); + mShape->CollidePoint(point, sub_shape_id, ioCollector, inShapeFilter); + } +} + +void TransformedShape::CollideShape(const Shape *inShape, Vec3Arg inShapeScale, RMat44Arg inCenterOfMassTransform, const CollideShapeSettings &inCollideShapeSettings, RVec3Arg inBaseOffset, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter) const +{ + if (mShape != nullptr) + { + // Set the context on the collector and filter + ioCollector.SetContext(this); + inShapeFilter.mBodyID2 = mBodyID; + + SubShapeIDCreator sub_shape_id1, sub_shape_id2(mSubShapeIDCreator); + Mat44 transform1 = inCenterOfMassTransform.PostTranslated(-inBaseOffset).ToMat44(); + Mat44 transform2 = GetCenterOfMassTransform().PostTranslated(-inBaseOffset).ToMat44(); + CollisionDispatch::sCollideShapeVsShape(inShape, mShape, inShapeScale, GetShapeScale(), transform1, transform2, sub_shape_id1, sub_shape_id2, inCollideShapeSettings, ioCollector, inShapeFilter); + } +} + +void TransformedShape::CastShape(const RShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, RVec3Arg inBaseOffset, CastShapeCollector &ioCollector, const ShapeFilter &inShapeFilter) const +{ + if (mShape != nullptr) + { + // Set the context on the collector and filter + ioCollector.SetContext(this); + inShapeFilter.mBodyID2 = mBodyID; + + // Get the shape cast relative to the base offset and convert it to floats + ShapeCast shape_cast(inShapeCast.PostTranslated(-inBaseOffset)); + + // Get center of mass of object we're casting against relative to the base offset and convert it to floats + Mat44 center_of_mass_transform2 = GetCenterOfMassTransform().PostTranslated(-inBaseOffset).ToMat44(); + + SubShapeIDCreator sub_shape_id1, sub_shape_id2(mSubShapeIDCreator); + CollisionDispatch::sCastShapeVsShapeWorldSpace(shape_cast, inShapeCastSettings, mShape, GetShapeScale(), inShapeFilter, center_of_mass_transform2, sub_shape_id1, sub_shape_id2, ioCollector); + } +} + +void TransformedShape::CollectTransformedShapes(const AABox &inBox, TransformedShapeCollector &ioCollector, const ShapeFilter &inShapeFilter) const +{ + if (mShape != nullptr) + { + struct MyCollector : public TransformedShapeCollector + { + MyCollector(TransformedShapeCollector &ioCollector, RVec3 inShapePositionCOM) : + TransformedShapeCollector(ioCollector), + mCollector(ioCollector), + mShapePositionCOM(inShapePositionCOM) + { + } + + virtual void AddHit(const TransformedShape &inResult) override + { + // Apply the center of mass offset + TransformedShape ts = inResult; + ts.mShapePositionCOM += mShapePositionCOM; + + // Pass hit on to child collector + mCollector.AddHit(ts); + + // Update early out fraction based on child collector + UpdateEarlyOutFraction(mCollector.GetEarlyOutFraction()); + } + + TransformedShapeCollector & mCollector; + RVec3 mShapePositionCOM; + }; + + // Set the context on the collector + ioCollector.SetContext(this); + + // Wrap the collector so we can add the center of mass precision, we do this to avoid losing precision because CollectTransformedShapes uses single precision floats + MyCollector collector(ioCollector, mShapePositionCOM); + + // Take box to local space for the shape + AABox box = inBox; + box.Translate(-mShapePositionCOM); + + mShape->CollectTransformedShapes(box, Vec3::sZero(), mShapeRotation, GetShapeScale(), mSubShapeIDCreator, collector, inShapeFilter); + } +} + +void TransformedShape::GetTrianglesStart(GetTrianglesContext &ioContext, const AABox &inBox, RVec3Arg inBaseOffset) const +{ + if (mShape != nullptr) + { + // Take box to local space for the shape + AABox box = inBox; + box.Translate(-inBaseOffset); + + mShape->GetTrianglesStart(ioContext, box, Vec3(mShapePositionCOM - inBaseOffset), mShapeRotation, GetShapeScale()); + } +} + +int TransformedShape::GetTrianglesNext(GetTrianglesContext &ioContext, int inMaxTrianglesRequested, Float3 *outTriangleVertices, const PhysicsMaterial **outMaterials) const +{ + if (mShape != nullptr) + return mShape->GetTrianglesNext(ioContext, inMaxTrianglesRequested, outTriangleVertices, outMaterials); + else + return 0; +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/TransformedShape.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/TransformedShape.h new file mode 100644 index 00000000000..887a8ee4411 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/TransformedShape.h @@ -0,0 +1,194 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +struct RRayCast; +struct RShapeCast; +class CollideShapeSettings; +class RayCastResult; + +/// Temporary data structure that contains a shape and a transform. +/// This structure can be obtained from a body (e.g. after a broad phase query) under lock protection. +/// The lock can then be released and collision detection operations can be safely performed since +/// the class takes a reference on the shape and does not use anything from the body anymore. +class JPH_EXPORT TransformedShape +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Constructor + TransformedShape() = default; + TransformedShape(RVec3Arg inPositionCOM, QuatArg inRotation, const Shape *inShape, const BodyID &inBodyID, const SubShapeIDCreator &inSubShapeIDCreator = SubShapeIDCreator()) : mShapePositionCOM(inPositionCOM), mShapeRotation(inRotation), mShape(inShape), mBodyID(inBodyID), mSubShapeIDCreator(inSubShapeIDCreator) { } + + /// Cast a ray and find the closest hit. Returns true if it finds a hit. Hits further than ioHit.mFraction will not be considered and in this case ioHit will remain unmodified (and the function will return false). + /// Convex objects will be treated as solid (meaning if the ray starts inside, you'll get a hit fraction of 0) and back face hits are returned. + /// If you want the surface normal of the hit use GetWorldSpaceSurfaceNormal(ioHit.mSubShapeID2, inRay.GetPointOnRay(ioHit.mFraction)) on this object. + bool CastRay(const RRayCast &inRay, RayCastResult &ioHit) const; + + /// Cast a ray, allows collecting multiple hits. Note that this version is more flexible but also slightly slower than the CastRay function that returns only a single hit. + /// If you want the surface normal of the hit use GetWorldSpaceSurfaceNormal(collected sub shape ID, inRay.GetPointOnRay(collected fraction)) on this object. + void CastRay(const RRayCast &inRay, const RayCastSettings &inRayCastSettings, CastRayCollector &ioCollector, const ShapeFilter &inShapeFilter = { }) const; + + /// Check if inPoint is inside any shapes. For this tests all shapes are treated as if they were solid. + /// For a mesh shape, this test will only provide sensible information if the mesh is a closed manifold. + /// For each shape that collides, ioCollector will receive a hit + void CollidePoint(RVec3Arg inPoint, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter = { }) const; + + /// Collide a shape and report any hits to ioCollector + /// @param inShape Shape to test + /// @param inShapeScale Scale in local space of shape + /// @param inCenterOfMassTransform Center of mass transform for the shape + /// @param inCollideShapeSettings Settings + /// @param inBaseOffset All hit results will be returned relative to this offset, can be zero to get results in world position, but when you're testing far from the origin you get better precision by picking a position that's closer e.g. mShapePositionCOM since floats are most accurate near the origin + /// @param ioCollector Collector that receives the hits + /// @param inShapeFilter Filter that allows you to reject collisions + void CollideShape(const Shape *inShape, Vec3Arg inShapeScale, RMat44Arg inCenterOfMassTransform, const CollideShapeSettings &inCollideShapeSettings, RVec3Arg inBaseOffset, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter = { }) const; + + /// Cast a shape and report any hits to ioCollector + /// @param inShapeCast The shape cast and its position and direction + /// @param inShapeCastSettings Settings for the shape cast + /// @param inBaseOffset All hit results will be returned relative to this offset, can be zero to get results in world position, but when you're testing far from the origin you get better precision by picking a position that's closer e.g. mShapePositionCOM or inShapeCast.mCenterOfMassStart.GetTranslation() since floats are most accurate near the origin + /// @param ioCollector Collector that receives the hits + /// @param inShapeFilter Filter that allows you to reject collisions + void CastShape(const RShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, RVec3Arg inBaseOffset, CastShapeCollector &ioCollector, const ShapeFilter &inShapeFilter = { }) const; + + /// Collect the leaf transformed shapes of all leaf shapes of this shape + /// inBox is the world space axis aligned box which leaf shapes should collide with + void CollectTransformedShapes(const AABox &inBox, TransformedShapeCollector &ioCollector, const ShapeFilter &inShapeFilter = { }) const; + + /// Use the context from Shape + using GetTrianglesContext = Shape::GetTrianglesContext; + + /// To start iterating over triangles, call this function first. + /// To get the actual triangles call GetTrianglesNext. + /// @param ioContext A temporary buffer and should remain untouched until the last call to GetTrianglesNext. + /// @param inBox The world space bounding in which you want to get the triangles. + /// @param inBaseOffset All hit results will be returned relative to this offset, can be zero to get results in world position, but when you're testing far from the origin you get better precision by picking a position that's closer e.g. inBox.GetCenter() since floats are most accurate near the origin + void GetTrianglesStart(GetTrianglesContext &ioContext, const AABox &inBox, RVec3Arg inBaseOffset) const; + + /// Call this repeatedly to get all triangles in the box. + /// outTriangleVertices should be large enough to hold 3 * inMaxTriangleRequested entries + /// outMaterials (if it is not null) should contain inMaxTrianglesRequested entries + /// The function returns the amount of triangles that it found (which will be <= inMaxTrianglesRequested), or 0 if there are no more triangles. + /// Note that the function can return a value < inMaxTrianglesRequested and still have more triangles to process (triangles can be returned in blocks) + /// Note that the function may return triangles outside of the requested box, only coarse culling is performed on the returned triangles + int GetTrianglesNext(GetTrianglesContext &ioContext, int inMaxTrianglesRequested, Float3 *outTriangleVertices, const PhysicsMaterial **outMaterials = nullptr) const; + + /// Get/set the scale of the shape as a Vec3 + inline Vec3 GetShapeScale() const { return Vec3::sLoadFloat3Unsafe(mShapeScale); } + inline void SetShapeScale(Vec3Arg inScale) { inScale.StoreFloat3(&mShapeScale); } + + /// Calculates the transform for this shapes's center of mass (excluding scale) + inline RMat44 GetCenterOfMassTransform() const { return RMat44::sRotationTranslation(mShapeRotation, mShapePositionCOM); } + + /// Calculates the inverse of the transform for this shape's center of mass (excluding scale) + inline RMat44 GetInverseCenterOfMassTransform() const { return RMat44::sInverseRotationTranslation(mShapeRotation, mShapePositionCOM); } + + /// Sets the world transform (including scale) of this transformed shape (not from the center of mass but in the space the shape was created) + inline void SetWorldTransform(RVec3Arg inPosition, QuatArg inRotation, Vec3Arg inScale) + { + mShapePositionCOM = inPosition + inRotation * (inScale * mShape->GetCenterOfMass()); + mShapeRotation = inRotation; + SetShapeScale(inScale); + } + + /// Sets the world transform (including scale) of this transformed shape (not from the center of mass but in the space the shape was created) + inline void SetWorldTransform(RMat44Arg inTransform) + { + Vec3 scale; + RMat44 rot_trans = inTransform.Decompose(scale); + SetWorldTransform(rot_trans.GetTranslation(), rot_trans.GetQuaternion(), scale); + } + + /// Calculates the world transform including scale of this shape (not from the center of mass but in the space the shape was created) + inline RMat44 GetWorldTransform() const + { + RMat44 transform = RMat44::sRotation(mShapeRotation).PreScaled(GetShapeScale()); + transform.SetTranslation(mShapePositionCOM - transform.Multiply3x3(mShape->GetCenterOfMass())); + return transform; + } + + /// Get the world space bounding box for this transformed shape + AABox GetWorldSpaceBounds() const { return mShape != nullptr? mShape->GetWorldSpaceBounds(GetCenterOfMassTransform(), GetShapeScale()) : AABox(); } + + /// Make inSubShapeID relative to mShape. When mSubShapeIDCreator is not empty, this is needed in order to get the correct path to the sub shape. + inline SubShapeID MakeSubShapeIDRelativeToShape(const SubShapeID &inSubShapeID) const + { + // Take off the sub shape ID part that comes from mSubShapeIDCreator and validate that it is the same + SubShapeID sub_shape_id; + uint num_bits_written = mSubShapeIDCreator.GetNumBitsWritten(); + JPH_IF_ENABLE_ASSERTS(uint32 root_id =) inSubShapeID.PopID(num_bits_written, sub_shape_id); + JPH_ASSERT(root_id == (mSubShapeIDCreator.GetID().GetValue() & ((1 << num_bits_written) - 1))); + return sub_shape_id; + } + + /// Get surface normal of a particular sub shape and its world space surface position on this body. + /// Note: When you have a CollideShapeResult or ShapeCastResult you should use -mPenetrationAxis.Normalized() as contact normal as GetWorldSpaceSurfaceNormal will only return face normals (and not vertex or edge normals). + inline Vec3 GetWorldSpaceSurfaceNormal(const SubShapeID &inSubShapeID, RVec3Arg inPosition) const + { + RMat44 inv_com = GetInverseCenterOfMassTransform(); + Vec3 scale = GetShapeScale(); // See comment at ScaledShape::GetSurfaceNormal for the math behind the scaling of the normal + return inv_com.Multiply3x3Transposed(mShape->GetSurfaceNormal(MakeSubShapeIDRelativeToShape(inSubShapeID), Vec3(inv_com * inPosition) / scale) / scale).Normalized(); + } + + /// Get the vertices of the face that faces inDirection the most (includes any convex radius). Note that this function can only return faces of + /// convex shapes or triangles, which is why a sub shape ID to get to that leaf must be provided. + /// @param inSubShapeID Sub shape ID of target shape + /// @param inDirection Direction that the face should be facing (in world space) + /// @param inBaseOffset The vertices will be returned relative to this offset, can be zero to get results in world position, but when you're testing far from the origin you get better precision by picking a position that's closer e.g. mShapePositionCOM since floats are most accurate near the origin + /// @param outVertices Resulting face. Note the returned face can have a single point if the shape doesn't have polygons to return (e.g. because it's a sphere). The face will be returned in world space. + void GetSupportingFace(const SubShapeID &inSubShapeID, Vec3Arg inDirection, RVec3Arg inBaseOffset, Shape::SupportingFace &outVertices) const + { + Mat44 com = GetCenterOfMassTransform().PostTranslated(-inBaseOffset).ToMat44(); + mShape->GetSupportingFace(MakeSubShapeIDRelativeToShape(inSubShapeID), com.Multiply3x3Transposed(inDirection), GetShapeScale(), com, outVertices); + } + + /// Get material of a particular sub shape + inline const PhysicsMaterial *GetMaterial(const SubShapeID &inSubShapeID) const + { + return mShape->GetMaterial(MakeSubShapeIDRelativeToShape(inSubShapeID)); + } + + /// Get the user data of a particular sub shape + inline uint64 GetSubShapeUserData(const SubShapeID &inSubShapeID) const + { + return mShape->GetSubShapeUserData(MakeSubShapeIDRelativeToShape(inSubShapeID)); + } + + /// Get the direct child sub shape and its transform for a sub shape ID. + /// @param inSubShapeID Sub shape ID that indicates the path to the leaf shape + /// @param outRemainder The remainder of the sub shape ID after removing the sub shape + /// @return Direct child sub shape and its transform, note that the body ID and sub shape ID will be invalid + TransformedShape GetSubShapeTransformedShape(const SubShapeID &inSubShapeID, SubShapeID &outRemainder) const + { + TransformedShape ts = mShape->GetSubShapeTransformedShape(inSubShapeID, Vec3::sZero(), mShapeRotation, GetShapeScale(), outRemainder); + ts.mShapePositionCOM += mShapePositionCOM; + return ts; + } + + /// Helper function to return the body id from a transformed shape. If the transformed shape is null an invalid body ID will be returned. + inline static BodyID sGetBodyID(const TransformedShape *inTS) { return inTS != nullptr? inTS->mBodyID : BodyID(); } + + RVec3 mShapePositionCOM; ///< Center of mass world position of the shape + Quat mShapeRotation; ///< Rotation of the shape + RefConst mShape; ///< The shape itself + Float3 mShapeScale { 1, 1, 1 }; ///< Not stored as Vec3 to get a nicely packed structure + BodyID mBodyID; ///< Optional body ID from which this shape comes + SubShapeIDCreator mSubShapeIDCreator; ///< Optional sub shape ID creator for the shape (can be used when expanding compound shapes into multiple transformed shapes) +}; + +static_assert(JPH_CPU_ADDRESS_BITS != 64 || sizeof(TransformedShape) == JPH_IF_SINGLE_PRECISION_ELSE(64, 96), "Not properly packed"); +static_assert(alignof(TransformedShape) == JPH_RVECTOR_ALIGNMENT, "Not properly aligned"); + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/CalculateSolverSteps.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/CalculateSolverSteps.h new file mode 100644 index 00000000000..857eddfb8e1 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/CalculateSolverSteps.h @@ -0,0 +1,66 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2023 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +/// Class used to calculate the total number of velocity and position steps +class CalculateSolverSteps +{ +public: + /// Constructor + JPH_INLINE explicit CalculateSolverSteps(const PhysicsSettings &inSettings) : mSettings(inSettings) { } + + /// Combine the number of velocity and position steps for this body/constraint with the current values + template + JPH_INLINE void operator () (const Type *inObject) + { + uint num_velocity_steps = inObject->GetNumVelocityStepsOverride(); + mNumVelocitySteps = max(mNumVelocitySteps, num_velocity_steps); + mApplyDefaultVelocity |= num_velocity_steps == 0; + + uint num_position_steps = inObject->GetNumPositionStepsOverride(); + mNumPositionSteps = max(mNumPositionSteps, num_position_steps); + mApplyDefaultPosition |= num_position_steps == 0; + } + + /// Must be called after all bodies/constraints have been processed + JPH_INLINE void Finalize() + { + // If we have a default velocity/position step count, take the max of the default and the overrides + if (mApplyDefaultVelocity) + mNumVelocitySteps = max(mNumVelocitySteps, mSettings.mNumVelocitySteps); + if (mApplyDefaultPosition) + mNumPositionSteps = max(mNumPositionSteps, mSettings.mNumPositionSteps); + } + + /// Get the results of the calculation + JPH_INLINE uint GetNumPositionSteps() const { return mNumPositionSteps; } + JPH_INLINE uint GetNumVelocitySteps() const { return mNumVelocitySteps; } + +private: + const PhysicsSettings & mSettings; + + uint mNumVelocitySteps = 0; + uint mNumPositionSteps = 0; + + bool mApplyDefaultVelocity = false; + bool mApplyDefaultPosition = false; +}; + +/// Dummy class to replace the steps calculator when we don't need the result +class DummyCalculateSolverSteps +{ +public: + template + JPH_INLINE void operator () (const Type *) const + { + /* Nothing to do */ + } +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConeConstraint.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConeConstraint.cpp new file mode 100644 index 00000000000..9889fa643de --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConeConstraint.cpp @@ -0,0 +1,246 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#include +#include +#ifdef JPH_DEBUG_RENDERER + #include +#endif // JPH_DEBUG_RENDERER + +JPH_NAMESPACE_BEGIN + +JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(ConeConstraintSettings) +{ + JPH_ADD_BASE_CLASS(ConeConstraintSettings, TwoBodyConstraintSettings) + + JPH_ADD_ENUM_ATTRIBUTE(ConeConstraintSettings, mSpace) + JPH_ADD_ATTRIBUTE(ConeConstraintSettings, mPoint1) + JPH_ADD_ATTRIBUTE(ConeConstraintSettings, mTwistAxis1) + JPH_ADD_ATTRIBUTE(ConeConstraintSettings, mPoint2) + JPH_ADD_ATTRIBUTE(ConeConstraintSettings, mTwistAxis2) + JPH_ADD_ATTRIBUTE(ConeConstraintSettings, mHalfConeAngle) +} + +void ConeConstraintSettings::SaveBinaryState(StreamOut &inStream) const +{ + ConstraintSettings::SaveBinaryState(inStream); + + inStream.Write(mSpace); + inStream.Write(mPoint1); + inStream.Write(mTwistAxis1); + inStream.Write(mPoint2); + inStream.Write(mTwistAxis2); + inStream.Write(mHalfConeAngle); +} + +void ConeConstraintSettings::RestoreBinaryState(StreamIn &inStream) +{ + ConstraintSettings::RestoreBinaryState(inStream); + + inStream.Read(mSpace); + inStream.Read(mPoint1); + inStream.Read(mTwistAxis1); + inStream.Read(mPoint2); + inStream.Read(mTwistAxis2); + inStream.Read(mHalfConeAngle); +} + +TwoBodyConstraint *ConeConstraintSettings::Create(Body &inBody1, Body &inBody2) const +{ + return new ConeConstraint(inBody1, inBody2, *this); +} + +ConeConstraint::ConeConstraint(Body &inBody1, Body &inBody2, const ConeConstraintSettings &inSettings) : + TwoBodyConstraint(inBody1, inBody2, inSettings) +{ + // Store limits + SetHalfConeAngle(inSettings.mHalfConeAngle); + + // Initialize rotation axis to perpendicular of twist axis in case the angle between the twist axis is 0 in the first frame + mWorldSpaceRotationAxis = inSettings.mTwistAxis1.GetNormalizedPerpendicular(); + + if (inSettings.mSpace == EConstraintSpace::WorldSpace) + { + // If all properties were specified in world space, take them to local space now + RMat44 inv_transform1 = inBody1.GetInverseCenterOfMassTransform(); + mLocalSpacePosition1 = Vec3(inv_transform1 * inSettings.mPoint1); + mLocalSpaceTwistAxis1 = inv_transform1.Multiply3x3(inSettings.mTwistAxis1); + + RMat44 inv_transform2 = inBody2.GetInverseCenterOfMassTransform(); + mLocalSpacePosition2 = Vec3(inv_transform2 * inSettings.mPoint2); + mLocalSpaceTwistAxis2 = inv_transform2.Multiply3x3(inSettings.mTwistAxis2); + } + else + { + // Properties already in local space + mLocalSpacePosition1 = Vec3(inSettings.mPoint1); + mLocalSpacePosition2 = Vec3(inSettings.mPoint2); + mLocalSpaceTwistAxis1 = inSettings.mTwistAxis1; + mLocalSpaceTwistAxis2 = inSettings.mTwistAxis2; + + // If they were in local space, we need to take the initial rotation axis to world space + mWorldSpaceRotationAxis = inBody1.GetRotation() * mWorldSpaceRotationAxis; + } +} + +void ConeConstraint::NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM) +{ + if (mBody1->GetID() == inBodyID) + mLocalSpacePosition1 -= inDeltaCOM; + else if (mBody2->GetID() == inBodyID) + mLocalSpacePosition2 -= inDeltaCOM; +} + +void ConeConstraint::CalculateRotationConstraintProperties(Mat44Arg inRotation1, Mat44Arg inRotation2) +{ + // Rotation is along the cross product of both twist axis + Vec3 twist1 = inRotation1.Multiply3x3(mLocalSpaceTwistAxis1); + Vec3 twist2 = inRotation2.Multiply3x3(mLocalSpaceTwistAxis2); + + // Calculate dot product between twist axis, if it's smaller than the cone angle we need to correct + mCosTheta = twist1.Dot(twist2); + if (mCosTheta < mCosHalfConeAngle) + { + // Rotation axis is defined by the two twist axis + Vec3 rot_axis = twist2.Cross(twist1); + + // If we can't find a rotation axis because the twist is too small, we'll use last frame's rotation axis + float len = rot_axis.Length(); + if (len > 0.0f) + mWorldSpaceRotationAxis = rot_axis / len; + + mAngleConstraintPart.CalculateConstraintProperties(*mBody1, *mBody2, mWorldSpaceRotationAxis); + } + else + mAngleConstraintPart.Deactivate(); +} + +void ConeConstraint::SetupVelocityConstraint(float inDeltaTime) +{ + Mat44 rotation1 = Mat44::sRotation(mBody1->GetRotation()); + Mat44 rotation2 = Mat44::sRotation(mBody2->GetRotation()); + mPointConstraintPart.CalculateConstraintProperties(*mBody1, rotation1, mLocalSpacePosition1, *mBody2, rotation2, mLocalSpacePosition2); + CalculateRotationConstraintProperties(rotation1, rotation2); +} + +void ConeConstraint::ResetWarmStart() +{ + mPointConstraintPart.Deactivate(); + mAngleConstraintPart.Deactivate(); +} + +void ConeConstraint::WarmStartVelocityConstraint(float inWarmStartImpulseRatio) +{ + // Warm starting: Apply previous frame impulse + mPointConstraintPart.WarmStart(*mBody1, *mBody2, inWarmStartImpulseRatio); + mAngleConstraintPart.WarmStart(*mBody1, *mBody2, inWarmStartImpulseRatio); +} + +bool ConeConstraint::SolveVelocityConstraint(float inDeltaTime) +{ + bool pos = mPointConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2); + + bool rot = false; + if (mAngleConstraintPart.IsActive()) + rot = mAngleConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2, mWorldSpaceRotationAxis, 0, FLT_MAX); + + return pos || rot; +} + +bool ConeConstraint::SolvePositionConstraint(float inDeltaTime, float inBaumgarte) +{ + mPointConstraintPart.CalculateConstraintProperties(*mBody1, Mat44::sRotation(mBody1->GetRotation()), mLocalSpacePosition1, *mBody2, Mat44::sRotation(mBody2->GetRotation()), mLocalSpacePosition2); + bool pos = mPointConstraintPart.SolvePositionConstraint(*mBody1, *mBody2, inBaumgarte); + + bool rot = false; + CalculateRotationConstraintProperties(Mat44::sRotation(mBody1->GetRotation()), Mat44::sRotation(mBody2->GetRotation())); + if (mAngleConstraintPart.IsActive()) + rot = mAngleConstraintPart.SolvePositionConstraint(*mBody1, *mBody2, mCosTheta - mCosHalfConeAngle, inBaumgarte); + + return pos || rot; +} + +#ifdef JPH_DEBUG_RENDERER +void ConeConstraint::DrawConstraint(DebugRenderer *inRenderer) const +{ + RMat44 transform1 = mBody1->GetCenterOfMassTransform(); + RMat44 transform2 = mBody2->GetCenterOfMassTransform(); + + RVec3 p1 = transform1 * mLocalSpacePosition1; + RVec3 p2 = transform2 * mLocalSpacePosition2; + + // Draw constraint + inRenderer->DrawMarker(p1, Color::sRed, 0.1f); + inRenderer->DrawMarker(p2, Color::sGreen, 0.1f); + + // Draw twist axis + inRenderer->DrawLine(p1, p1 + mDrawConstraintSize * transform1.Multiply3x3(mLocalSpaceTwistAxis1), Color::sRed); + inRenderer->DrawLine(p2, p2 + mDrawConstraintSize * transform2.Multiply3x3(mLocalSpaceTwistAxis2), Color::sGreen); +} + +void ConeConstraint::DrawConstraintLimits(DebugRenderer *inRenderer) const +{ + // Get constraint properties in world space + RMat44 transform1 = mBody1->GetCenterOfMassTransform(); + RVec3 position1 = transform1 * mLocalSpacePosition1; + Vec3 twist_axis1 = transform1.Multiply3x3(mLocalSpaceTwistAxis1); + Vec3 normal_axis1 = transform1.Multiply3x3(mLocalSpaceTwistAxis1.GetNormalizedPerpendicular()); + + inRenderer->DrawOpenCone(position1, twist_axis1, normal_axis1, ACos(mCosHalfConeAngle), mDrawConstraintSize * mCosHalfConeAngle, Color::sPurple, DebugRenderer::ECastShadow::Off); +} +#endif // JPH_DEBUG_RENDERER + +void ConeConstraint::SaveState(StateRecorder &inStream) const +{ + TwoBodyConstraint::SaveState(inStream); + + mPointConstraintPart.SaveState(inStream); + mAngleConstraintPart.SaveState(inStream); + inStream.Write(mWorldSpaceRotationAxis); // When twist is too small, the rotation is used from last frame so we need to store it +} + +void ConeConstraint::RestoreState(StateRecorder &inStream) +{ + TwoBodyConstraint::RestoreState(inStream); + + mPointConstraintPart.RestoreState(inStream); + mAngleConstraintPart.RestoreState(inStream); + inStream.Read(mWorldSpaceRotationAxis); +} + +Ref ConeConstraint::GetConstraintSettings() const +{ + ConeConstraintSettings *settings = new ConeConstraintSettings; + ToConstraintSettings(*settings); + settings->mSpace = EConstraintSpace::LocalToBodyCOM; + settings->mPoint1 = RVec3(mLocalSpacePosition1); + settings->mTwistAxis1 = mLocalSpaceTwistAxis1; + settings->mPoint2 = RVec3(mLocalSpacePosition2); + settings->mTwistAxis2 = mLocalSpaceTwistAxis2; + settings->mHalfConeAngle = ACos(mCosHalfConeAngle); + return settings; +} + +Mat44 ConeConstraint::GetConstraintToBody1Matrix() const +{ + Vec3 perp = mLocalSpaceTwistAxis1.GetNormalizedPerpendicular(); + Vec3 perp2 = mLocalSpaceTwistAxis1.Cross(perp); + return Mat44(Vec4(mLocalSpaceTwistAxis1, 0), Vec4(perp, 0), Vec4(perp2, 0), Vec4(mLocalSpacePosition1, 1)); +} + +Mat44 ConeConstraint::GetConstraintToBody2Matrix() const +{ + // Note: Incorrect in rotation around the twist axis (the perpendicular does not match that of body 1), + // this should not matter as we're not limiting rotation around the twist axis. + Vec3 perp = mLocalSpaceTwistAxis2.GetNormalizedPerpendicular(); + Vec3 perp2 = mLocalSpaceTwistAxis2.Cross(perp); + return Mat44(Vec4(mLocalSpaceTwistAxis2, 0), Vec4(perp, 0), Vec4(perp2, 0), Vec4(mLocalSpacePosition2, 1)); +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConeConstraint.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConeConstraint.h new file mode 100644 index 00000000000..630d089dd1f --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConeConstraint.h @@ -0,0 +1,133 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +/// Cone constraint settings, used to create a cone constraint +class JPH_EXPORT ConeConstraintSettings final : public TwoBodyConstraintSettings +{ +public: + JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, ConeConstraintSettings) + + // See: ConstraintSettings::SaveBinaryState + virtual void SaveBinaryState(StreamOut &inStream) const override; + + /// Create an instance of this constraint + virtual TwoBodyConstraint * Create(Body &inBody1, Body &inBody2) const override; + + /// This determines in which space the constraint is setup, all properties below should be in the specified space + EConstraintSpace mSpace = EConstraintSpace::WorldSpace; + + /// Body 1 constraint reference frame (space determined by mSpace) + RVec3 mPoint1 = RVec3::sZero(); + Vec3 mTwistAxis1 = Vec3::sAxisX(); + + /// Body 2 constraint reference frame (space determined by mSpace) + RVec3 mPoint2 = RVec3::sZero(); + Vec3 mTwistAxis2 = Vec3::sAxisX(); + + /// Half of maximum angle between twist axis of body 1 and 2 + float mHalfConeAngle = 0.0f; + +protected: + // See: ConstraintSettings::RestoreBinaryState + virtual void RestoreBinaryState(StreamIn &inStream) override; +}; + +/// A cone constraint constraints 2 bodies to a single point and limits the swing between the twist axis within a cone: +/// +/// t1 . t2 <= cos(theta) +/// +/// Where: +/// +/// t1 = twist axis of body 1. +/// t2 = twist axis of body 2. +/// theta = half cone angle (angle from the principal axis of the cone to the edge). +/// +/// Calculating the Jacobian: +/// +/// Constraint equation: +/// +/// C = t1 . t2 - cos(theta) +/// +/// Derivative: +/// +/// d/dt C = d/dt (t1 . t2) = (d/dt t1) . t2 + t1 . (d/dt t2) = (w1 x t1) . t2 + t1 . (w2 x t2) = (t1 x t2) . w1 + (t2 x t1) . w2 +/// +/// d/dt C = J v = [0, -t2 x t1, 0, t2 x t1] [v1, w1, v2, w2] +/// +/// Where J is the Jacobian. +/// +/// Note that this is the exact same equation as used in AngleConstraintPart if we use t2 x t1 as the world space axis +class JPH_EXPORT ConeConstraint final : public TwoBodyConstraint +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Construct cone constraint + ConeConstraint(Body &inBody1, Body &inBody2, const ConeConstraintSettings &inSettings); + + // Generic interface of a constraint + virtual EConstraintSubType GetSubType() const override { return EConstraintSubType::Cone; } + virtual void NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM) override; + virtual void SetupVelocityConstraint(float inDeltaTime) override; + virtual void ResetWarmStart() override; + virtual void WarmStartVelocityConstraint(float inWarmStartImpulseRatio) override; + virtual bool SolveVelocityConstraint(float inDeltaTime) override; + virtual bool SolvePositionConstraint(float inDeltaTime, float inBaumgarte) override; +#ifdef JPH_DEBUG_RENDERER + virtual void DrawConstraint(DebugRenderer *inRenderer) const override; + virtual void DrawConstraintLimits(DebugRenderer *inRenderer) const override; +#endif // JPH_DEBUG_RENDERER + virtual void SaveState(StateRecorder &inStream) const override; + virtual void RestoreState(StateRecorder &inStream) override; + virtual Ref GetConstraintSettings() const override; + + // See: TwoBodyConstraint + virtual Mat44 GetConstraintToBody1Matrix() const override; + virtual Mat44 GetConstraintToBody2Matrix() const override; + + /// Update maximum angle between body 1 and 2 (see ConeConstraintSettings) + void SetHalfConeAngle(float inHalfConeAngle) { JPH_ASSERT(inHalfConeAngle >= 0.0f && inHalfConeAngle <= JPH_PI); mCosHalfConeAngle = Cos(inHalfConeAngle); } + float GetCosHalfConeAngle() const { return mCosHalfConeAngle; } + + ///@name Get Lagrange multiplier from last physics update (the linear/angular impulse applied to satisfy the constraint) + inline Vec3 GetTotalLambdaPosition() const { return mPointConstraintPart.GetTotalLambda(); } + inline float GetTotalLambdaRotation() const { return mAngleConstraintPart.GetTotalLambda(); } + +private: + // Internal helper function to calculate the values below + void CalculateRotationConstraintProperties(Mat44Arg inRotation1, Mat44Arg inRotation2); + + // CONFIGURATION PROPERTIES FOLLOW + + // Local space constraint positions + Vec3 mLocalSpacePosition1; + Vec3 mLocalSpacePosition2; + + // Local space constraint axis + Vec3 mLocalSpaceTwistAxis1; + Vec3 mLocalSpaceTwistAxis2; + + // Angular limits + float mCosHalfConeAngle; + + // RUN TIME PROPERTIES FOLLOW + + // Axis and angle of rotation between the two bodies + Vec3 mWorldSpaceRotationAxis; + float mCosTheta; + + // The constraint parts + PointConstraintPart mPointConstraintPart; + AngleConstraintPart mAngleConstraintPart; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/Constraint.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/Constraint.cpp new file mode 100644 index 00000000000..b81a8d81a71 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/Constraint.cpp @@ -0,0 +1,73 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(ConstraintSettings) +{ + JPH_ADD_BASE_CLASS(ConstraintSettings, SerializableObject) + + JPH_ADD_ATTRIBUTE(ConstraintSettings, mEnabled) + JPH_ADD_ATTRIBUTE(ConstraintSettings, mDrawConstraintSize) + JPH_ADD_ATTRIBUTE(ConstraintSettings, mConstraintPriority) + JPH_ADD_ATTRIBUTE(ConstraintSettings, mNumVelocityStepsOverride) + JPH_ADD_ATTRIBUTE(ConstraintSettings, mNumPositionStepsOverride) + JPH_ADD_ATTRIBUTE(ConstraintSettings, mUserData) +} + +void ConstraintSettings::SaveBinaryState(StreamOut &inStream) const +{ + inStream.Write(GetRTTI()->GetHash()); + inStream.Write(mEnabled); + inStream.Write(mDrawConstraintSize); + inStream.Write(mConstraintPriority); + inStream.Write(mNumVelocityStepsOverride); + inStream.Write(mNumPositionStepsOverride); +} + +void ConstraintSettings::RestoreBinaryState(StreamIn &inStream) +{ + // Type hash read by sRestoreFromBinaryState + inStream.Read(mEnabled); + inStream.Read(mDrawConstraintSize); + inStream.Read(mConstraintPriority); + inStream.Read(mNumVelocityStepsOverride); + inStream.Read(mNumPositionStepsOverride); +} + +ConstraintSettings::ConstraintResult ConstraintSettings::sRestoreFromBinaryState(StreamIn &inStream) +{ + return StreamUtils::RestoreObject(inStream, &ConstraintSettings::RestoreBinaryState); +} + +void Constraint::SaveState(StateRecorder &inStream) const +{ + inStream.Write(mEnabled); +} + +void Constraint::RestoreState(StateRecorder &inStream) +{ + inStream.Read(mEnabled); +} + +void Constraint::ToConstraintSettings(ConstraintSettings &outSettings) const +{ + outSettings.mEnabled = mEnabled; + outSettings.mConstraintPriority = mConstraintPriority; + outSettings.mNumVelocityStepsOverride = mNumVelocityStepsOverride; + outSettings.mNumPositionStepsOverride = mNumPositionStepsOverride; + outSettings.mUserData = mUserData; +#ifdef JPH_DEBUG_RENDERER + outSettings.mDrawConstraintSize = mDrawConstraintSize; +#endif // JPH_DEBUG_RENDERER +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/Constraint.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/Constraint.h new file mode 100644 index 00000000000..f8827cf68d7 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/Constraint.h @@ -0,0 +1,238 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +class BodyID; +class IslandBuilder; +class LargeIslandSplitter; +class BodyManager; +class StateRecorder; +class StreamIn; +class StreamOut; +#ifdef JPH_DEBUG_RENDERER +class DebugRenderer; +#endif // JPH_DEBUG_RENDERER + +/// Enum to identify constraint type +enum class EConstraintType +{ + Constraint, + TwoBodyConstraint, +}; + +/// Enum to identify constraint sub type +enum class EConstraintSubType +{ + Fixed, + Point, + Hinge, + Slider, + Distance, + Cone, + SwingTwist, + SixDOF, + Path, + Vehicle, + RackAndPinion, + Gear, + Pulley, + + /// User defined constraint types start here + User1, + User2, + User3, + User4 +}; + +/// Certain constraints support setting them up in local or world space. This governs what is used. +enum class EConstraintSpace +{ + LocalToBodyCOM, ///< All constraint properties are specified in local space to center of mass of the bodies that are being constrained (so e.g. 'constraint position 1' will be local to body 1 COM, 'constraint position 2' will be local to body 2 COM). Note that this means you need to subtract Shape::GetCenterOfMass() from positions! + WorldSpace, ///< All constraint properties are specified in world space +}; + +/// Class used to store the configuration of a constraint. Allows run-time creation of constraints. +class JPH_EXPORT ConstraintSettings : public SerializableObject, public RefTarget +{ +public: + JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, ConstraintSettings) + + using ConstraintResult = Result>; + + /// Saves the contents of the constraint settings in binary form to inStream. + virtual void SaveBinaryState(StreamOut &inStream) const; + + /// Creates a constraint of the correct type and restores its contents from the binary stream inStream. + static ConstraintResult sRestoreFromBinaryState(StreamIn &inStream); + + /// If this constraint is enabled initially. Use Constraint::SetEnabled to toggle after creation. + bool mEnabled = true; + + /// Priority of the constraint when solving. Higher numbers have are more likely to be solved correctly. + /// Note that if you want a deterministic simulation and you cannot guarantee the order in which constraints are added/removed, you can make the priority for all constraints unique to get a deterministic ordering. + uint32 mConstraintPriority = 0; + + /// Used only when the constraint is active. Override for the number of solver velocity iterations to run, 0 means use the default in PhysicsSettings::mNumVelocitySteps. The number of iterations to use is the max of all contacts and constraints in the island. + uint mNumVelocityStepsOverride = 0; + + /// Used only when the constraint is active. Override for the number of solver position iterations to run, 0 means use the default in PhysicsSettings::mNumPositionSteps. The number of iterations to use is the max of all contacts and constraints in the island. + uint mNumPositionStepsOverride = 0; + + /// Size of constraint when drawing it through the debug renderer + float mDrawConstraintSize = 1.0f; + + /// User data value (can be used by application) + uint64 mUserData = 0; + +protected: + /// This function should not be called directly, it is used by sRestoreFromBinaryState. + virtual void RestoreBinaryState(StreamIn &inStream); +}; + +/// Base class for all physics constraints. A constraint removes one or more degrees of freedom for a rigid body. +class JPH_EXPORT Constraint : public RefTarget, public NonCopyable +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Constructor + explicit Constraint(const ConstraintSettings &inSettings) : +#ifdef JPH_DEBUG_RENDERER + mDrawConstraintSize(inSettings.mDrawConstraintSize), +#endif // JPH_DEBUG_RENDERER + mConstraintPriority(inSettings.mConstraintPriority), + mNumVelocityStepsOverride(uint8(inSettings.mNumVelocityStepsOverride)), + mNumPositionStepsOverride(uint8(inSettings.mNumPositionStepsOverride)), + mEnabled(inSettings.mEnabled), + mUserData(inSettings.mUserData) + { + JPH_ASSERT(inSettings.mNumVelocityStepsOverride < 256); + JPH_ASSERT(inSettings.mNumPositionStepsOverride < 256); + } + + /// Virtual destructor + virtual ~Constraint() = default; + + /// Get the type of a constraint + virtual EConstraintType GetType() const { return EConstraintType::Constraint; } + + /// Get the sub type of a constraint + virtual EConstraintSubType GetSubType() const = 0; + + /// Priority of the constraint when solving. Higher numbers have are more likely to be solved correctly. + /// Note that if you want a deterministic simulation and you cannot guarantee the order in which constraints are added/removed, you can make the priority for all constraints unique to get a deterministic ordering. + uint32 GetConstraintPriority() const { return mConstraintPriority; } + void SetConstraintPriority(uint32 inPriority) { mConstraintPriority = inPriority; } + + /// Used only when the constraint is active. Override for the number of solver velocity iterations to run, 0 means use the default in PhysicsSettings::mNumVelocitySteps. The number of iterations to use is the max of all contacts and constraints in the island. + void SetNumVelocityStepsOverride(uint inN) { JPH_ASSERT(inN < 256); mNumVelocityStepsOverride = uint8(inN); } + uint GetNumVelocityStepsOverride() const { return mNumVelocityStepsOverride; } + + /// Used only when the constraint is active. Override for the number of solver position iterations to run, 0 means use the default in PhysicsSettings::mNumPositionSteps. The number of iterations to use is the max of all contacts and constraints in the island. + void SetNumPositionStepsOverride(uint inN) { JPH_ASSERT(inN < 256); mNumPositionStepsOverride = uint8(inN); } + uint GetNumPositionStepsOverride() const { return mNumPositionStepsOverride; } + + /// Enable / disable this constraint. This can e.g. be used to implement a breakable constraint by detecting that the constraint impulse + /// (see e.g. PointConstraint::GetTotalLambdaPosition) went over a certain limit and then disabling the constraint. + /// Note that although a disabled constraint will not affect the simulation in any way anymore, it does incur some processing overhead. + /// Alternatively you can remove a constraint from the constraint manager (which may be more costly if you want to disable the constraint for a short while). + void SetEnabled(bool inEnabled) { mEnabled = inEnabled; } + + /// Test if a constraint is enabled. + bool GetEnabled() const { return mEnabled; } + + /// Access to the user data, can be used for anything by the application + uint64 GetUserData() const { return mUserData; } + void SetUserData(uint64 inUserData) { mUserData = inUserData; } + + /// Notify the constraint that the shape of a body has changed and that its center of mass has moved by inDeltaCOM. + /// Bodies don't know which constraints are connected to them so the user is responsible for notifying the relevant constraints when a body changes. + /// @param inBodyID ID of the body that has changed + /// @param inDeltaCOM The delta of the center of mass of the body (shape->GetCenterOfMass() - shape_before_change->GetCenterOfMass()) + virtual void NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM) = 0; + + /// Notify the system that the configuration of the bodies and/or constraint has changed enough so that the warm start impulses should not be applied the next frame. + /// You can use this function for example when repositioning a ragdoll through Ragdoll::SetPose in such a way that the orientation of the bodies completely changes so that + /// the previous frame impulses are no longer a good approximation of what the impulses will be in the next frame. Calling this function when there are no big changes + /// will result in the constraints being much 'softer' than usual so they are more easily violated (e.g. a long chain of bodies might sag a bit if you call this every frame). + virtual void ResetWarmStart() = 0; + + ///@name Solver interface + ///@{ + virtual bool IsActive() const { return mEnabled; } + virtual void SetupVelocityConstraint(float inDeltaTime) = 0; + virtual void WarmStartVelocityConstraint(float inWarmStartImpulseRatio) = 0; + virtual bool SolveVelocityConstraint(float inDeltaTime) = 0; + virtual bool SolvePositionConstraint(float inDeltaTime, float inBaumgarte) = 0; + ///@} + + /// Link bodies that are connected by this constraint in the island builder + virtual void BuildIslands(uint32 inConstraintIndex, IslandBuilder &ioBuilder, BodyManager &inBodyManager) = 0; + + /// Link bodies that are connected by this constraint in the same split. Returns the split index. + virtual uint BuildIslandSplits(LargeIslandSplitter &ioSplitter) const = 0; + +#ifdef JPH_DEBUG_RENDERER + // Drawing interface + virtual void DrawConstraint(DebugRenderer *inRenderer) const = 0; + virtual void DrawConstraintLimits([[maybe_unused]] DebugRenderer *inRenderer) const { } + virtual void DrawConstraintReferenceFrame([[maybe_unused]] DebugRenderer *inRenderer) const { } + + /// Size of constraint when drawing it through the debug renderer + float GetDrawConstraintSize() const { return mDrawConstraintSize; } + void SetDrawConstraintSize(float inSize) { mDrawConstraintSize = inSize; } +#endif // JPH_DEBUG_RENDERER + + /// Saving state for replay + virtual void SaveState(StateRecorder &inStream) const; + + /// Restoring state for replay + virtual void RestoreState(StateRecorder &inStream); + + /// Debug function to convert a constraint to its settings, note that this will not save to which bodies the constraint is connected to + virtual Ref GetConstraintSettings() const = 0; + +protected: + /// Helper function to copy settings back to constraint settings for this base class + void ToConstraintSettings(ConstraintSettings &outSettings) const; + +#ifdef JPH_DEBUG_RENDERER + /// Size of constraint when drawing it through the debug renderer + float mDrawConstraintSize; +#endif // JPH_DEBUG_RENDERER + +private: + friend class ConstraintManager; + + /// Index that indicates this constraint is not in the constraint manager + static constexpr uint32 cInvalidConstraintIndex = 0xffffffff; + + /// Index in the mConstraints list of the ConstraintManager for easy finding + uint32 mConstraintIndex = cInvalidConstraintIndex; + + /// Priority of the constraint when solving. Higher numbers have are more likely to be solved correctly. + uint32 mConstraintPriority = 0; + + /// Used only when the constraint is active. Override for the number of solver velocity iterations to run, 0 means use the default in PhysicsSettings::mNumVelocitySteps. The number of iterations to use is the max of all contacts and constraints in the island. + uint8 mNumVelocityStepsOverride = 0; + + /// Used only when the constraint is active. Override for the number of solver position iterations to run, 0 means use the default in PhysicsSettings::mNumPositionSteps. The number of iterations to use is the max of all contacts and constraints in the island. + uint8 mNumPositionStepsOverride = 0; + + /// If this constraint is currently enabled + bool mEnabled = true; + + /// User data value (can be used by application) + uint64 mUserData; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintManager.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintManager.cpp new file mode 100644 index 00000000000..509bddbd972 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintManager.cpp @@ -0,0 +1,289 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +void ConstraintManager::Add(Constraint **inConstraints, int inNumber) +{ + UniqueLock lock(mConstraintsMutex JPH_IF_ENABLE_ASSERTS(, mLockContext, EPhysicsLockTypes::ConstraintsList)); + + mConstraints.reserve(mConstraints.size() + inNumber); + + for (Constraint **c = inConstraints, **c_end = inConstraints + inNumber; c < c_end; ++c) + { + Constraint *constraint = *c; + + // Assume this constraint has not been added yet + JPH_ASSERT(constraint->mConstraintIndex == Constraint::cInvalidConstraintIndex); + + // Add to the list + constraint->mConstraintIndex = uint32(mConstraints.size()); + mConstraints.push_back(constraint); + } +} + +void ConstraintManager::Remove(Constraint **inConstraints, int inNumber) +{ + UniqueLock lock(mConstraintsMutex JPH_IF_ENABLE_ASSERTS(, mLockContext, EPhysicsLockTypes::ConstraintsList)); + + for (Constraint **c = inConstraints, **c_end = inConstraints + inNumber; c < c_end; ++c) + { + Constraint *constraint = *c; + + // Reset constraint index for this constraint + uint32 this_constraint_idx = constraint->mConstraintIndex; + constraint->mConstraintIndex = Constraint::cInvalidConstraintIndex; + JPH_ASSERT(this_constraint_idx != Constraint::cInvalidConstraintIndex); + + // Check if this constraint is somewhere in the middle of the constraints, in this case we need to move the last constraint to this position + uint32 last_constraint_idx = uint32(mConstraints.size() - 1); + if (this_constraint_idx < last_constraint_idx) + { + Constraint *last_constraint = mConstraints[last_constraint_idx]; + last_constraint->mConstraintIndex = this_constraint_idx; + mConstraints[this_constraint_idx] = last_constraint; + } + + // Pop last constraint + mConstraints.pop_back(); + } +} + +Constraints ConstraintManager::GetConstraints() const +{ + UniqueLock lock(mConstraintsMutex JPH_IF_ENABLE_ASSERTS(, mLockContext, EPhysicsLockTypes::ConstraintsList)); + + Constraints copy = mConstraints; + return copy; +} + +void ConstraintManager::GetActiveConstraints(uint32 inStartConstraintIdx, uint32 inEndConstraintIdx, Constraint **outActiveConstraints, uint32 &outNumActiveConstraints) const +{ + JPH_PROFILE_FUNCTION(); + + JPH_ASSERT(inEndConstraintIdx <= mConstraints.size()); + + uint32 num_active_constraints = 0; + for (uint32 constraint_idx = inStartConstraintIdx; constraint_idx < inEndConstraintIdx; ++constraint_idx) + { + Constraint *c = mConstraints[constraint_idx]; + JPH_ASSERT(c->mConstraintIndex == constraint_idx); + if (c->IsActive()) + { + *(outActiveConstraints++) = c; + num_active_constraints++; + } + } + + outNumActiveConstraints = num_active_constraints; +} + +void ConstraintManager::sBuildIslands(Constraint **inActiveConstraints, uint32 inNumActiveConstraints, IslandBuilder &ioBuilder, BodyManager &inBodyManager) +{ + JPH_PROFILE_FUNCTION(); + + for (uint32 constraint_idx = 0; constraint_idx < inNumActiveConstraints; ++constraint_idx) + { + Constraint *c = inActiveConstraints[constraint_idx]; + c->BuildIslands(constraint_idx, ioBuilder, inBodyManager); + } +} + +void ConstraintManager::sSortConstraints(Constraint **inActiveConstraints, uint32 *inConstraintIdxBegin, uint32 *inConstraintIdxEnd) +{ + JPH_PROFILE_FUNCTION(); + + QuickSort(inConstraintIdxBegin, inConstraintIdxEnd, [inActiveConstraints](uint32 inLHS, uint32 inRHS) { + const Constraint *lhs = inActiveConstraints[inLHS]; + const Constraint *rhs = inActiveConstraints[inRHS]; + + if (lhs->GetConstraintPriority() != rhs->GetConstraintPriority()) + return lhs->GetConstraintPriority() < rhs->GetConstraintPriority(); + + return lhs->mConstraintIndex < rhs->mConstraintIndex; + }); +} + +void ConstraintManager::sSetupVelocityConstraints(Constraint **inActiveConstraints, uint32 inNumActiveConstraints, float inDeltaTime) +{ + JPH_PROFILE_FUNCTION(); + + for (Constraint **c = inActiveConstraints, **c_end = inActiveConstraints + inNumActiveConstraints; c < c_end; ++c) + (*c)->SetupVelocityConstraint(inDeltaTime); +} + +template +void ConstraintManager::sWarmStartVelocityConstraints(Constraint **inActiveConstraints, const uint32 *inConstraintIdxBegin, const uint32 *inConstraintIdxEnd, float inWarmStartImpulseRatio, ConstraintCallback &ioCallback) +{ + JPH_PROFILE_FUNCTION(); + + for (const uint32 *constraint_idx = inConstraintIdxBegin; constraint_idx < inConstraintIdxEnd; ++constraint_idx) + { + Constraint *c = inActiveConstraints[*constraint_idx]; + ioCallback(c); + c->WarmStartVelocityConstraint(inWarmStartImpulseRatio); + } +} + +// Specialize for the two constraint callback types +template void ConstraintManager::sWarmStartVelocityConstraints(Constraint **inActiveConstraints, const uint32 *inConstraintIdxBegin, const uint32 *inConstraintIdxEnd, float inWarmStartImpulseRatio, CalculateSolverSteps &ioCallback); +template void ConstraintManager::sWarmStartVelocityConstraints(Constraint **inActiveConstraints, const uint32 *inConstraintIdxBegin, const uint32 *inConstraintIdxEnd, float inWarmStartImpulseRatio, DummyCalculateSolverSteps &ioCallback); + +bool ConstraintManager::sSolveVelocityConstraints(Constraint **inActiveConstraints, const uint32 *inConstraintIdxBegin, const uint32 *inConstraintIdxEnd, float inDeltaTime) +{ + JPH_PROFILE_FUNCTION(); + + bool any_impulse_applied = false; + + for (const uint32 *constraint_idx = inConstraintIdxBegin; constraint_idx < inConstraintIdxEnd; ++constraint_idx) + { + Constraint *c = inActiveConstraints[*constraint_idx]; + any_impulse_applied |= c->SolveVelocityConstraint(inDeltaTime); + } + + return any_impulse_applied; +} + +bool ConstraintManager::sSolvePositionConstraints(Constraint **inActiveConstraints, const uint32 *inConstraintIdxBegin, const uint32 *inConstraintIdxEnd, float inDeltaTime, float inBaumgarte) +{ + JPH_PROFILE_FUNCTION(); + + bool any_impulse_applied = false; + + for (const uint32 *constraint_idx = inConstraintIdxBegin; constraint_idx < inConstraintIdxEnd; ++constraint_idx) + { + Constraint *c = inActiveConstraints[*constraint_idx]; + any_impulse_applied |= c->SolvePositionConstraint(inDeltaTime, inBaumgarte); + } + + return any_impulse_applied; +} + +#ifdef JPH_DEBUG_RENDERER +void ConstraintManager::DrawConstraints(DebugRenderer *inRenderer) const +{ + JPH_PROFILE_FUNCTION(); + + UniqueLock lock(mConstraintsMutex JPH_IF_ENABLE_ASSERTS(, mLockContext, EPhysicsLockTypes::ConstraintsList)); + + for (const Ref &c : mConstraints) + c->DrawConstraint(inRenderer); +} + +void ConstraintManager::DrawConstraintLimits(DebugRenderer *inRenderer) const +{ + JPH_PROFILE_FUNCTION(); + + UniqueLock lock(mConstraintsMutex JPH_IF_ENABLE_ASSERTS(, mLockContext, EPhysicsLockTypes::ConstraintsList)); + + for (const Ref &c : mConstraints) + c->DrawConstraintLimits(inRenderer); +} + +void ConstraintManager::DrawConstraintReferenceFrame(DebugRenderer *inRenderer) const +{ + JPH_PROFILE_FUNCTION(); + + UniqueLock lock(mConstraintsMutex JPH_IF_ENABLE_ASSERTS(, mLockContext, EPhysicsLockTypes::ConstraintsList)); + + for (const Ref &c : mConstraints) + c->DrawConstraintReferenceFrame(inRenderer); +} +#endif // JPH_DEBUG_RENDERER + +void ConstraintManager::SaveState(StateRecorder &inStream, const StateRecorderFilter *inFilter) const +{ + UniqueLock lock(mConstraintsMutex JPH_IF_ENABLE_ASSERTS(, mLockContext, EPhysicsLockTypes::ConstraintsList)); + + // Write state of constraints + if (inFilter != nullptr) + { + // Determine which constraints to save + Array constraints; + constraints.reserve(mConstraints.size()); + for (const Ref &c : mConstraints) + if (inFilter->ShouldSaveConstraint(*c)) + constraints.push_back(c); + + // Save them + uint32 num_constraints = (uint32)constraints.size(); + inStream.Write(num_constraints); + for (const Constraint *c : constraints) + { + inStream.Write(c->mConstraintIndex); + c->SaveState(inStream); + } + } + else + { + // Save all constraints + uint32 num_constraints = (uint32)mConstraints.size(); + inStream.Write(num_constraints); + for (const Ref &c : mConstraints) + { + inStream.Write(c->mConstraintIndex); + c->SaveState(inStream); + } + } +} + +bool ConstraintManager::RestoreState(StateRecorder &inStream) +{ + UniqueLock lock(mConstraintsMutex JPH_IF_ENABLE_ASSERTS(, mLockContext, EPhysicsLockTypes::ConstraintsList)); + + if (inStream.IsValidating()) + { + // Read state of constraints + uint32 num_constraints = (uint32)mConstraints.size(); // Initialize to current value for validation + inStream.Read(num_constraints); + if (num_constraints != mConstraints.size()) + { + JPH_ASSERT(false, "Cannot handle adding/removing constraints"); + return false; + } + for (const Ref &c : mConstraints) + { + uint32 constraint_index = c->mConstraintIndex; + inStream.Read(constraint_index); + if (constraint_index != c->mConstraintIndex) + { + JPH_ASSERT(false, "Unexpected constraint index"); + return false; + } + c->RestoreState(inStream); + } + } + else + { + // Not validating, use more flexible reading, read number of constraints + uint32 num_constraints = 0; + inStream.Read(num_constraints); + + for (uint32 idx = 0; idx < num_constraints; ++idx) + { + uint32 constraint_index; + inStream.Read(constraint_index); + if (mConstraints.size() <= constraint_index) + { + JPH_ASSERT(false, "Restoring state for non-existing constraint"); + return false; + } + mConstraints[constraint_index]->RestoreState(inStream); + } + } + + return true; +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintManager.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintManager.h new file mode 100644 index 00000000000..7be8b06926c --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintManager.h @@ -0,0 +1,99 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +class IslandBuilder; +class BodyManager; +class StateRecorderFilter; +#ifdef JPH_DEBUG_RENDERER +class DebugRenderer; +#endif // JPH_DEBUG_RENDERER + +/// A list of constraints +using Constraints = Array>; + +/// A constraint manager manages all constraints of the same type +class JPH_EXPORT ConstraintManager : public NonCopyable +{ +public: + JPH_OVERRIDE_NEW_DELETE + +#ifdef JPH_ENABLE_ASSERTS + /// Constructor + ConstraintManager(PhysicsLockContext inContext) : mLockContext(inContext) { } +#endif // JPH_ENABLE_ASSERTS + + /// Add a new constraint. This is thread safe. + /// Note that the inConstraints array is allowed to have nullptrs, these will be ignored. + void Add(Constraint **inConstraints, int inNumber); + + /// Remove a constraint. This is thread safe. + /// Note that the inConstraints array is allowed to have nullptrs, these will be ignored. + void Remove(Constraint **inConstraint, int inNumber); + + /// Get a list of all constraints + Constraints GetConstraints() const; + + /// Get total number of constraints + inline uint32 GetNumConstraints() const { return uint32(mConstraints.size()); } + + /// Determine the active constraints of a subset of the constraints + void GetActiveConstraints(uint32 inStartConstraintIdx, uint32 inEndConstraintIdx, Constraint **outActiveConstraints, uint32 &outNumActiveConstraints) const; + + /// Link bodies to form islands + static void sBuildIslands(Constraint **inActiveConstraints, uint32 inNumActiveConstraints, IslandBuilder &ioBuilder, BodyManager &inBodyManager); + + /// In order to have a deterministic simulation, we need to sort the constraints of an island before solving them + static void sSortConstraints(Constraint **inActiveConstraints, uint32 *inConstraintIdxBegin, uint32 *inConstraintIdxEnd); + + /// Prior to solving the velocity constraints, you must call SetupVelocityConstraints once to precalculate values that are independent of velocity + static void sSetupVelocityConstraints(Constraint **inActiveConstraints, uint32 inNumActiveConstraints, float inDeltaTime); + + /// Apply last frame's impulses, must be called prior to SolveVelocityConstraints + template + static void sWarmStartVelocityConstraints(Constraint **inActiveConstraints, const uint32 *inConstraintIdxBegin, const uint32 *inConstraintIdxEnd, float inWarmStartImpulseRatio, ConstraintCallback &ioCallback); + + /// This function is called multiple times to iteratively come to a solution that meets all velocity constraints + static bool sSolveVelocityConstraints(Constraint **inActiveConstraints, const uint32 *inConstraintIdxBegin, const uint32 *inConstraintIdxEnd, float inDeltaTime); + + /// This function is called multiple times to iteratively come to a solution that meets all position constraints + static bool sSolvePositionConstraints(Constraint **inActiveConstraints, const uint32 *inConstraintIdxBegin, const uint32 *inConstraintIdxEnd, float inDeltaTime, float inBaumgarte); + +#ifdef JPH_DEBUG_RENDERER + /// Draw all constraints + void DrawConstraints(DebugRenderer *inRenderer) const; + + /// Draw all constraint limits + void DrawConstraintLimits(DebugRenderer *inRenderer) const; + + /// Draw all constraint reference frames + void DrawConstraintReferenceFrame(DebugRenderer *inRenderer) const; +#endif // JPH_DEBUG_RENDERER + + /// Save state of constraints + void SaveState(StateRecorder &inStream, const StateRecorderFilter *inFilter) const; + + /// Restore the state of constraints. Returns false if failed. + bool RestoreState(StateRecorder &inStream); + + /// Lock all constraints. This should only be done during PhysicsSystem::Update(). + void LockAllConstraints() { PhysicsLock::sLock(mConstraintsMutex JPH_IF_ENABLE_ASSERTS(, mLockContext, EPhysicsLockTypes::ConstraintsList)); } + void UnlockAllConstraints() { PhysicsLock::sUnlock(mConstraintsMutex JPH_IF_ENABLE_ASSERTS(, mLockContext, EPhysicsLockTypes::ConstraintsList)); } + +private: +#ifdef JPH_ENABLE_ASSERTS + PhysicsLockContext mLockContext; +#endif // JPH_ENABLE_ASSERTS + Constraints mConstraints; + mutable Mutex mConstraintsMutex; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/AngleConstraintPart.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/AngleConstraintPart.h new file mode 100644 index 00000000000..f7493102e2a --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/AngleConstraintPart.h @@ -0,0 +1,257 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +/// Constraint that constrains rotation along 1 axis +/// +/// Based on: "Constraints Derivation for Rigid Body Simulation in 3D" - Daniel Chappuis, see section 2.4.5 +/// +/// Constraint equation (eq 108): +/// +/// \f[C = \theta(t) - \theta_{min}\f] +/// +/// Jacobian (eq 109): +/// +/// \f[J = \begin{bmatrix}0 & -a^T & 0 & a^T\end{bmatrix}\f] +/// +/// Used terms (here and below, everything in world space):\n +/// a = axis around which rotation is constrained (normalized).\n +/// x1, x2 = center of mass for the bodies.\n +/// v = [v1, w1, v2, w2].\n +/// v1, v2 = linear velocity of body 1 and 2.\n +/// w1, w2 = angular velocity of body 1 and 2.\n +/// M = mass matrix, a diagonal matrix of the mass and inertia with diagonal [m1, I1, m2, I2].\n +/// \f$K^{-1} = \left( J M^{-1} J^T \right)^{-1}\f$ = effective mass.\n +/// b = velocity bias.\n +/// \f$\beta\f$ = baumgarte constant. +class AngleConstraintPart +{ + /// Internal helper function to update velocities of bodies after Lagrange multiplier is calculated + JPH_INLINE bool ApplyVelocityStep(Body &ioBody1, Body &ioBody2, float inLambda) const + { + // Apply impulse if delta is not zero + if (inLambda != 0.0f) + { + // Calculate velocity change due to constraint + // + // Impulse: + // P = J^T lambda + // + // Euler velocity integration: + // v' = v + M^-1 P + if (ioBody1.IsDynamic()) + ioBody1.GetMotionProperties()->SubAngularVelocityStep(inLambda * mInvI1_Axis); + if (ioBody2.IsDynamic()) + ioBody2.GetMotionProperties()->AddAngularVelocityStep(inLambda * mInvI2_Axis); + return true; + } + + return false; + } + + /// Internal helper function to calculate the inverse effective mass + JPH_INLINE float CalculateInverseEffectiveMass(const Body &inBody1, const Body &inBody2, Vec3Arg inWorldSpaceAxis) + { + JPH_ASSERT(inWorldSpaceAxis.IsNormalized(1.0e-4f)); + + // Calculate properties used below + mInvI1_Axis = inBody1.IsDynamic()? inBody1.GetMotionProperties()->MultiplyWorldSpaceInverseInertiaByVector(inBody1.GetRotation(), inWorldSpaceAxis) : Vec3::sZero(); + mInvI2_Axis = inBody2.IsDynamic()? inBody2.GetMotionProperties()->MultiplyWorldSpaceInverseInertiaByVector(inBody2.GetRotation(), inWorldSpaceAxis) : Vec3::sZero(); + + // Calculate inverse effective mass: K = J M^-1 J^T + return inWorldSpaceAxis.Dot(mInvI1_Axis + mInvI2_Axis); + } + +public: + /// Calculate properties used during the functions below + /// @param inBody1 The first body that this constraint is attached to + /// @param inBody2 The second body that this constraint is attached to + /// @param inWorldSpaceAxis The axis of rotation along which the constraint acts (normalized) + /// Set the following terms to zero if you don't want to drive the constraint to zero with a spring: + /// @param inBias Bias term (b) for the constraint impulse: lambda = J v + b + inline void CalculateConstraintProperties(const Body &inBody1, const Body &inBody2, Vec3Arg inWorldSpaceAxis, float inBias = 0.0f) + { + float inv_effective_mass = CalculateInverseEffectiveMass(inBody1, inBody2, inWorldSpaceAxis); + + if (inv_effective_mass == 0.0f) + Deactivate(); + else + { + mEffectiveMass = 1.0f / inv_effective_mass; + mSpringPart.CalculateSpringPropertiesWithBias(inBias); + } + } + + /// Calculate properties used during the functions below + /// @param inDeltaTime Time step + /// @param inBody1 The first body that this constraint is attached to + /// @param inBody2 The second body that this constraint is attached to + /// @param inWorldSpaceAxis The axis of rotation along which the constraint acts (normalized) + /// Set the following terms to zero if you don't want to drive the constraint to zero with a spring: + /// @param inBias Bias term (b) for the constraint impulse: lambda = J v + b + /// @param inC Value of the constraint equation (C) + /// @param inFrequency Oscillation frequency (Hz) + /// @param inDamping Damping factor (0 = no damping, 1 = critical damping) + inline void CalculateConstraintPropertiesWithFrequencyAndDamping(float inDeltaTime, const Body &inBody1, const Body &inBody2, Vec3Arg inWorldSpaceAxis, float inBias, float inC, float inFrequency, float inDamping) + { + float inv_effective_mass = CalculateInverseEffectiveMass(inBody1, inBody2, inWorldSpaceAxis); + + if (inv_effective_mass == 0.0f) + Deactivate(); + else + mSpringPart.CalculateSpringPropertiesWithFrequencyAndDamping(inDeltaTime, inv_effective_mass, inBias, inC, inFrequency, inDamping, mEffectiveMass); + } + + /// Calculate properties used during the functions below + /// @param inDeltaTime Time step + /// @param inBody1 The first body that this constraint is attached to + /// @param inBody2 The second body that this constraint is attached to + /// @param inWorldSpaceAxis The axis of rotation along which the constraint acts (normalized) + /// Set the following terms to zero if you don't want to drive the constraint to zero with a spring: + /// @param inBias Bias term (b) for the constraint impulse: lambda = J v + b + /// @param inC Value of the constraint equation (C) + /// @param inStiffness Spring stiffness k. + /// @param inDamping Spring damping coefficient c. + inline void CalculateConstraintPropertiesWithStiffnessAndDamping(float inDeltaTime, const Body &inBody1, const Body &inBody2, Vec3Arg inWorldSpaceAxis, float inBias, float inC, float inStiffness, float inDamping) + { + float inv_effective_mass = CalculateInverseEffectiveMass(inBody1, inBody2, inWorldSpaceAxis); + + if (inv_effective_mass == 0.0f) + Deactivate(); + else + mSpringPart.CalculateSpringPropertiesWithStiffnessAndDamping(inDeltaTime, inv_effective_mass, inBias, inC, inStiffness, inDamping, mEffectiveMass); + } + + /// Selects one of the above functions based on the spring settings + inline void CalculateConstraintPropertiesWithSettings(float inDeltaTime, const Body &inBody1, const Body &inBody2, Vec3Arg inWorldSpaceAxis, float inBias, float inC, const SpringSettings &inSpringSettings) + { + float inv_effective_mass = CalculateInverseEffectiveMass(inBody1, inBody2, inWorldSpaceAxis); + + if (inv_effective_mass == 0.0f) + Deactivate(); + else if (inSpringSettings.mMode == ESpringMode::FrequencyAndDamping) + mSpringPart.CalculateSpringPropertiesWithFrequencyAndDamping(inDeltaTime, inv_effective_mass, inBias, inC, inSpringSettings.mFrequency, inSpringSettings.mDamping, mEffectiveMass); + else + mSpringPart.CalculateSpringPropertiesWithStiffnessAndDamping(inDeltaTime, inv_effective_mass, inBias, inC, inSpringSettings.mStiffness, inSpringSettings.mDamping, mEffectiveMass); + } + + /// Deactivate this constraint + inline void Deactivate() + { + mEffectiveMass = 0.0f; + mTotalLambda = 0.0f; + } + + /// Check if constraint is active + inline bool IsActive() const + { + return mEffectiveMass != 0.0f; + } + + /// Must be called from the WarmStartVelocityConstraint call to apply the previous frame's impulses + /// @param ioBody1 The first body that this constraint is attached to + /// @param ioBody2 The second body that this constraint is attached to + /// @param inWarmStartImpulseRatio Ratio of new step to old time step (dt_new / dt_old) for scaling the lagrange multiplier of the previous frame + inline void WarmStart(Body &ioBody1, Body &ioBody2, float inWarmStartImpulseRatio) + { + mTotalLambda *= inWarmStartImpulseRatio; + ApplyVelocityStep(ioBody1, ioBody2, mTotalLambda); + } + + /// Iteratively update the velocity constraint. Makes sure d/dt C(...) = 0, where C is the constraint equation. + /// @param ioBody1 The first body that this constraint is attached to + /// @param ioBody2 The second body that this constraint is attached to + /// @param inWorldSpaceAxis The axis of rotation along which the constraint acts (normalized) + /// @param inMinLambda Minimum angular impulse to apply (N m s) + /// @param inMaxLambda Maximum angular impulse to apply (N m s) + inline bool SolveVelocityConstraint(Body &ioBody1, Body &ioBody2, Vec3Arg inWorldSpaceAxis, float inMinLambda, float inMaxLambda) + { + // Lagrange multiplier is: + // + // lambda = -K^-1 (J v + b) + float lambda = mEffectiveMass * (inWorldSpaceAxis.Dot(ioBody1.GetAngularVelocity() - ioBody2.GetAngularVelocity()) - mSpringPart.GetBias(mTotalLambda)); + float new_lambda = Clamp(mTotalLambda + lambda, inMinLambda, inMaxLambda); // Clamp impulse + lambda = new_lambda - mTotalLambda; // Lambda potentially got clamped, calculate the new impulse to apply + mTotalLambda = new_lambda; // Store accumulated impulse + + return ApplyVelocityStep(ioBody1, ioBody2, lambda); + } + + /// Return lagrange multiplier + float GetTotalLambda() const + { + return mTotalLambda; + } + + /// Iteratively update the position constraint. Makes sure C(...) == 0. + /// @param ioBody1 The first body that this constraint is attached to + /// @param ioBody2 The second body that this constraint is attached to + /// @param inC Value of the constraint equation (C) + /// @param inBaumgarte Baumgarte constant (fraction of the error to correct) + inline bool SolvePositionConstraint(Body &ioBody1, Body &ioBody2, float inC, float inBaumgarte) const + { + // Only apply position constraint when the constraint is hard, otherwise the velocity bias will fix the constraint + if (inC != 0.0f && !mSpringPart.IsActive()) + { + // Calculate lagrange multiplier (lambda) for Baumgarte stabilization: + // + // lambda = -K^-1 * beta / dt * C + // + // We should divide by inDeltaTime, but we should multiply by inDeltaTime in the Euler step below so they're cancelled out + float lambda = -mEffectiveMass * inBaumgarte * inC; + + // Directly integrate velocity change for one time step + // + // Euler velocity integration: + // dv = M^-1 P + // + // Impulse: + // P = J^T lambda + // + // Euler position integration: + // x' = x + dv * dt + // + // Note we don't accumulate velocities for the stabilization. This is using the approach described in 'Modeling and + // Solving Constraints' by Erin Catto presented at GDC 2007. On slide 78 it is suggested to split up the Baumgarte + // stabilization for positional drift so that it does not actually add to the momentum. We combine an Euler velocity + // integrate + a position integrate and then discard the velocity change. + if (ioBody1.IsDynamic()) + ioBody1.SubRotationStep(lambda * mInvI1_Axis); + if (ioBody2.IsDynamic()) + ioBody2.AddRotationStep(lambda * mInvI2_Axis); + return true; + } + + return false; + } + + /// Save state of this constraint part + void SaveState(StateRecorder &inStream) const + { + inStream.Write(mTotalLambda); + } + + /// Restore state of this constraint part + void RestoreState(StateRecorder &inStream) + { + inStream.Read(mTotalLambda); + } + +private: + Vec3 mInvI1_Axis; + Vec3 mInvI2_Axis; + float mEffectiveMass = 0.0f; + SpringPart mSpringPart; + float mTotalLambda = 0.0f; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/AxisConstraintPart.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/AxisConstraintPart.h new file mode 100644 index 00000000000..541050b91b9 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/AxisConstraintPart.h @@ -0,0 +1,682 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +/// Constraint that constrains motion along 1 axis +/// +/// @see "Constraints Derivation for Rigid Body Simulation in 3D" - Daniel Chappuis, section 2.1.1 +/// (we're not using the approximation of eq 27 but instead add the U term as in eq 55) +/// +/// Constraint equation (eq 25): +/// +/// \f[C = (p_2 - p_1) \cdot n\f] +/// +/// Jacobian (eq 28): +/// +/// \f[J = \begin{bmatrix} -n^T & (-(r_1 + u) \times n)^T & n^T & (r_2 \times n)^T \end{bmatrix}\f] +/// +/// Used terms (here and below, everything in world space):\n +/// n = constraint axis (normalized).\n +/// p1, p2 = constraint points.\n +/// r1 = p1 - x1.\n +/// r2 = p2 - x2.\n +/// u = x2 + r2 - x1 - r1 = p2 - p1.\n +/// x1, x2 = center of mass for the bodies.\n +/// v = [v1, w1, v2, w2].\n +/// v1, v2 = linear velocity of body 1 and 2.\n +/// w1, w2 = angular velocity of body 1 and 2.\n +/// M = mass matrix, a diagonal matrix of the mass and inertia with diagonal [m1, I1, m2, I2].\n +/// \f$K^{-1} = \left( J M^{-1} J^T \right)^{-1}\f$ = effective mass.\n +/// b = velocity bias.\n +/// \f$\beta\f$ = baumgarte constant. +class AxisConstraintPart +{ + /// Internal helper function to update velocities of bodies after Lagrange multiplier is calculated + template + JPH_INLINE bool ApplyVelocityStep(MotionProperties *ioMotionProperties1, float inInvMass1, MotionProperties *ioMotionProperties2, float inInvMass2, Vec3Arg inWorldSpaceAxis, float inLambda) const + { + // Apply impulse if delta is not zero + if (inLambda != 0.0f) + { + // Calculate velocity change due to constraint + // + // Impulse: + // P = J^T lambda + // + // Euler velocity integration: + // v' = v + M^-1 P + if constexpr (Type1 == EMotionType::Dynamic) + { + ioMotionProperties1->SubLinearVelocityStep((inLambda * inInvMass1) * inWorldSpaceAxis); + ioMotionProperties1->SubAngularVelocityStep(inLambda * Vec3::sLoadFloat3Unsafe(mInvI1_R1PlusUxAxis)); + } + if constexpr (Type2 == EMotionType::Dynamic) + { + ioMotionProperties2->AddLinearVelocityStep((inLambda * inInvMass2) * inWorldSpaceAxis); + ioMotionProperties2->AddAngularVelocityStep(inLambda * Vec3::sLoadFloat3Unsafe(mInvI2_R2xAxis)); + } + return true; + } + + return false; + } + + /// Internal helper function to calculate the inverse effective mass + template + JPH_INLINE float TemplatedCalculateInverseEffectiveMass(float inInvMass1, Mat44Arg inInvI1, Vec3Arg inR1PlusU, float inInvMass2, Mat44Arg inInvI2, Vec3Arg inR2, Vec3Arg inWorldSpaceAxis) + { + JPH_ASSERT(inWorldSpaceAxis.IsNormalized(1.0e-5f)); + + // Calculate properties used below + Vec3 r1_plus_u_x_axis; + if constexpr (Type1 != EMotionType::Static) + { + r1_plus_u_x_axis = inR1PlusU.Cross(inWorldSpaceAxis); + r1_plus_u_x_axis.StoreFloat3(&mR1PlusUxAxis); + } + else + { + #ifdef _DEBUG + Vec3::sNaN().StoreFloat3(&mR1PlusUxAxis); + #endif + } + + Vec3 r2_x_axis; + if constexpr (Type2 != EMotionType::Static) + { + r2_x_axis = inR2.Cross(inWorldSpaceAxis); + r2_x_axis.StoreFloat3(&mR2xAxis); + } + else + { + #ifdef _DEBUG + Vec3::sNaN().StoreFloat3(&mR2xAxis); + #endif + } + + // Calculate inverse effective mass: K = J M^-1 J^T + float inv_effective_mass; + + if constexpr (Type1 == EMotionType::Dynamic) + { + Vec3 invi1_r1_plus_u_x_axis = inInvI1.Multiply3x3(r1_plus_u_x_axis); + invi1_r1_plus_u_x_axis.StoreFloat3(&mInvI1_R1PlusUxAxis); + inv_effective_mass = inInvMass1 + invi1_r1_plus_u_x_axis.Dot(r1_plus_u_x_axis); + } + else + { + (void)r1_plus_u_x_axis; // Fix compiler warning: Not using this (it's not calculated either) + JPH_IF_DEBUG(Vec3::sNaN().StoreFloat3(&mInvI1_R1PlusUxAxis);) + inv_effective_mass = 0.0f; + } + + if constexpr (Type2 == EMotionType::Dynamic) + { + Vec3 invi2_r2_x_axis = inInvI2.Multiply3x3(r2_x_axis); + invi2_r2_x_axis.StoreFloat3(&mInvI2_R2xAxis); + inv_effective_mass += inInvMass2 + invi2_r2_x_axis.Dot(r2_x_axis); + } + else + { + (void)r2_x_axis; // Fix compiler warning: Not using this (it's not calculated either) + JPH_IF_DEBUG(Vec3::sNaN().StoreFloat3(&mInvI2_R2xAxis);) + } + + return inv_effective_mass; + } + + /// Internal helper function to calculate the inverse effective mass + JPH_INLINE float CalculateInverseEffectiveMass(const Body &inBody1, Vec3Arg inR1PlusU, const Body &inBody2, Vec3Arg inR2, Vec3Arg inWorldSpaceAxis) + { + // Dispatch to the correct templated form + switch (inBody1.GetMotionType()) + { + case EMotionType::Dynamic: + { + const MotionProperties *mp1 = inBody1.GetMotionPropertiesUnchecked(); + float inv_m1 = mp1->GetInverseMass(); + Mat44 inv_i1 = inBody1.GetInverseInertia(); + switch (inBody2.GetMotionType()) + { + case EMotionType::Dynamic: + return TemplatedCalculateInverseEffectiveMass(inv_m1, inv_i1, inR1PlusU, inBody2.GetMotionPropertiesUnchecked()->GetInverseMass(), inBody2.GetInverseInertia(), inR2, inWorldSpaceAxis); + + case EMotionType::Kinematic: + return TemplatedCalculateInverseEffectiveMass(inv_m1, inv_i1, inR1PlusU, 0 /* Will not be used */, Mat44() /* Will not be used */, inR2, inWorldSpaceAxis); + + case EMotionType::Static: + return TemplatedCalculateInverseEffectiveMass(inv_m1, inv_i1, inR1PlusU, 0 /* Will not be used */, Mat44() /* Will not be used */, inR2, inWorldSpaceAxis); + + default: + break; + } + break; + } + + case EMotionType::Kinematic: + JPH_ASSERT(inBody2.IsDynamic()); + return TemplatedCalculateInverseEffectiveMass(0 /* Will not be used */, Mat44() /* Will not be used */, inR1PlusU, inBody2.GetMotionPropertiesUnchecked()->GetInverseMass(), inBody2.GetInverseInertia(), inR2, inWorldSpaceAxis); + + case EMotionType::Static: + JPH_ASSERT(inBody2.IsDynamic()); + return TemplatedCalculateInverseEffectiveMass(0 /* Will not be used */, Mat44() /* Will not be used */, inR1PlusU, inBody2.GetMotionPropertiesUnchecked()->GetInverseMass(), inBody2.GetInverseInertia(), inR2, inWorldSpaceAxis); + + default: + break; + } + + JPH_ASSERT(false); + return 0.0f; + } + + /// Internal helper function to calculate the inverse effective mass, version that supports mass scaling + JPH_INLINE float CalculateInverseEffectiveMassWithMassOverride(const Body &inBody1, float inInvMass1, float inInvInertiaScale1, Vec3Arg inR1PlusU, const Body &inBody2, float inInvMass2, float inInvInertiaScale2, Vec3Arg inR2, Vec3Arg inWorldSpaceAxis) + { + // Dispatch to the correct templated form + switch (inBody1.GetMotionType()) + { + case EMotionType::Dynamic: + { + Mat44 inv_i1 = inInvInertiaScale1 * inBody1.GetInverseInertia(); + switch (inBody2.GetMotionType()) + { + case EMotionType::Dynamic: + return TemplatedCalculateInverseEffectiveMass(inInvMass1, inv_i1, inR1PlusU, inInvMass2, inInvInertiaScale2 * inBody2.GetInverseInertia(), inR2, inWorldSpaceAxis); + + case EMotionType::Kinematic: + return TemplatedCalculateInverseEffectiveMass(inInvMass1, inv_i1, inR1PlusU, 0 /* Will not be used */, Mat44() /* Will not be used */, inR2, inWorldSpaceAxis); + + case EMotionType::Static: + return TemplatedCalculateInverseEffectiveMass(inInvMass1, inv_i1, inR1PlusU, 0 /* Will not be used */, Mat44() /* Will not be used */, inR2, inWorldSpaceAxis); + + default: + break; + } + break; + } + + case EMotionType::Kinematic: + JPH_ASSERT(inBody2.IsDynamic()); + return TemplatedCalculateInverseEffectiveMass(0 /* Will not be used */, Mat44() /* Will not be used */, inR1PlusU, inInvMass2, inInvInertiaScale2 * inBody2.GetInverseInertia(), inR2, inWorldSpaceAxis); + + case EMotionType::Static: + JPH_ASSERT(inBody2.IsDynamic()); + return TemplatedCalculateInverseEffectiveMass(0 /* Will not be used */, Mat44() /* Will not be used */, inR1PlusU, inInvMass2, inInvInertiaScale2 * inBody2.GetInverseInertia(), inR2, inWorldSpaceAxis); + + default: + break; + } + + JPH_ASSERT(false); + return 0.0f; + } + +public: + /// Templated form of CalculateConstraintProperties with the motion types baked in + template + JPH_INLINE void TemplatedCalculateConstraintProperties(float inInvMass1, Mat44Arg inInvI1, Vec3Arg inR1PlusU, float inInvMass2, Mat44Arg inInvI2, Vec3Arg inR2, Vec3Arg inWorldSpaceAxis, float inBias = 0.0f) + { + float inv_effective_mass = TemplatedCalculateInverseEffectiveMass(inInvMass1, inInvI1, inR1PlusU, inInvMass2, inInvI2, inR2, inWorldSpaceAxis); + + if (inv_effective_mass == 0.0f) + Deactivate(); + else + { + mEffectiveMass = 1.0f / inv_effective_mass; + mSpringPart.CalculateSpringPropertiesWithBias(inBias); + } + + JPH_DET_LOG("TemplatedCalculateConstraintProperties: invM1: " << inInvMass1 << " invI1: " << inInvI1 << " r1PlusU: " << inR1PlusU << " invM2: " << inInvMass2 << " invI2: " << inInvI2 << " r2: " << inR2 << " bias: " << inBias << " r1PlusUxAxis: " << mR1PlusUxAxis << " r2xAxis: " << mR2xAxis << " invI1_R1PlusUxAxis: " << mInvI1_R1PlusUxAxis << " invI2_R2xAxis: " << mInvI2_R2xAxis << " effectiveMass: " << mEffectiveMass << " totalLambda: " << mTotalLambda); + } + + /// Calculate properties used during the functions below + /// @param inBody1 The first body that this constraint is attached to + /// @param inBody2 The second body that this constraint is attached to + /// @param inR1PlusU See equations above (r1 + u) + /// @param inR2 See equations above (r2) + /// @param inWorldSpaceAxis Axis along which the constraint acts (normalized, pointing from body 1 to 2) + /// @param inBias Bias term (b) for the constraint impulse: lambda = J v + b + inline void CalculateConstraintProperties(const Body &inBody1, Vec3Arg inR1PlusU, const Body &inBody2, Vec3Arg inR2, Vec3Arg inWorldSpaceAxis, float inBias = 0.0f) + { + float inv_effective_mass = CalculateInverseEffectiveMass(inBody1, inR1PlusU, inBody2, inR2, inWorldSpaceAxis); + + if (inv_effective_mass == 0.0f) + Deactivate(); + else + { + mEffectiveMass = 1.0f / inv_effective_mass; + mSpringPart.CalculateSpringPropertiesWithBias(inBias); + } + } + + /// Calculate properties used during the functions below, version that supports mass scaling + /// @param inBody1 The first body that this constraint is attached to + /// @param inBody2 The second body that this constraint is attached to + /// @param inInvMass1 The inverse mass of body 1 (only used when body 1 is dynamic) + /// @param inInvMass2 The inverse mass of body 2 (only used when body 2 is dynamic) + /// @param inInvInertiaScale1 Scale factor for the inverse inertia of body 1 + /// @param inInvInertiaScale2 Scale factor for the inverse inertia of body 2 + /// @param inR1PlusU See equations above (r1 + u) + /// @param inR2 See equations above (r2) + /// @param inWorldSpaceAxis Axis along which the constraint acts (normalized, pointing from body 1 to 2) + /// @param inBias Bias term (b) for the constraint impulse: lambda = J v + b + inline void CalculateConstraintPropertiesWithMassOverride(const Body &inBody1, float inInvMass1, float inInvInertiaScale1, Vec3Arg inR1PlusU, const Body &inBody2, float inInvMass2, float inInvInertiaScale2, Vec3Arg inR2, Vec3Arg inWorldSpaceAxis, float inBias = 0.0f) + { + float inv_effective_mass = CalculateInverseEffectiveMassWithMassOverride(inBody1, inInvMass1, inInvInertiaScale1, inR1PlusU, inBody2, inInvMass2, inInvInertiaScale2, inR2, inWorldSpaceAxis); + + if (inv_effective_mass == 0.0f) + Deactivate(); + else + { + mEffectiveMass = 1.0f / inv_effective_mass; + mSpringPart.CalculateSpringPropertiesWithBias(inBias); + } + } + + /// Calculate properties used during the functions below + /// @param inDeltaTime Time step + /// @param inBody1 The first body that this constraint is attached to + /// @param inBody2 The second body that this constraint is attached to + /// @param inR1PlusU See equations above (r1 + u) + /// @param inR2 See equations above (r2) + /// @param inWorldSpaceAxis Axis along which the constraint acts (normalized, pointing from body 1 to 2) + /// @param inBias Bias term (b) for the constraint impulse: lambda = J v + b + /// @param inC Value of the constraint equation (C). + /// @param inFrequency Oscillation frequency (Hz). + /// @param inDamping Damping factor (0 = no damping, 1 = critical damping). + inline void CalculateConstraintPropertiesWithFrequencyAndDamping(float inDeltaTime, const Body &inBody1, Vec3Arg inR1PlusU, const Body &inBody2, Vec3Arg inR2, Vec3Arg inWorldSpaceAxis, float inBias, float inC, float inFrequency, float inDamping) + { + float inv_effective_mass = CalculateInverseEffectiveMass(inBody1, inR1PlusU, inBody2, inR2, inWorldSpaceAxis); + + if (inv_effective_mass == 0.0f) + Deactivate(); + else + mSpringPart.CalculateSpringPropertiesWithFrequencyAndDamping(inDeltaTime, inv_effective_mass, inBias, inC, inFrequency, inDamping, mEffectiveMass); + } + + /// Calculate properties used during the functions below + /// @param inDeltaTime Time step + /// @param inBody1 The first body that this constraint is attached to + /// @param inBody2 The second body that this constraint is attached to + /// @param inR1PlusU See equations above (r1 + u) + /// @param inR2 See equations above (r2) + /// @param inWorldSpaceAxis Axis along which the constraint acts (normalized, pointing from body 1 to 2) + /// @param inBias Bias term (b) for the constraint impulse: lambda = J v + b + /// @param inC Value of the constraint equation (C). + /// @param inStiffness Spring stiffness k. + /// @param inDamping Spring damping coefficient c. + inline void CalculateConstraintPropertiesWithStiffnessAndDamping(float inDeltaTime, const Body &inBody1, Vec3Arg inR1PlusU, const Body &inBody2, Vec3Arg inR2, Vec3Arg inWorldSpaceAxis, float inBias, float inC, float inStiffness, float inDamping) + { + float inv_effective_mass = CalculateInverseEffectiveMass(inBody1, inR1PlusU, inBody2, inR2, inWorldSpaceAxis); + + if (inv_effective_mass == 0.0f) + Deactivate(); + else + mSpringPart.CalculateSpringPropertiesWithStiffnessAndDamping(inDeltaTime, inv_effective_mass, inBias, inC, inStiffness, inDamping, mEffectiveMass); + } + + /// Selects one of the above functions based on the spring settings + inline void CalculateConstraintPropertiesWithSettings(float inDeltaTime, const Body &inBody1, Vec3Arg inR1PlusU, const Body &inBody2, Vec3Arg inR2, Vec3Arg inWorldSpaceAxis, float inBias, float inC, const SpringSettings &inSpringSettings) + { + float inv_effective_mass = CalculateInverseEffectiveMass(inBody1, inR1PlusU, inBody2, inR2, inWorldSpaceAxis); + + if (inv_effective_mass == 0.0f) + Deactivate(); + else if (inSpringSettings.mMode == ESpringMode::FrequencyAndDamping) + mSpringPart.CalculateSpringPropertiesWithFrequencyAndDamping(inDeltaTime, inv_effective_mass, inBias, inC, inSpringSettings.mFrequency, inSpringSettings.mDamping, mEffectiveMass); + else + mSpringPart.CalculateSpringPropertiesWithStiffnessAndDamping(inDeltaTime, inv_effective_mass, inBias, inC, inSpringSettings.mStiffness, inSpringSettings.mDamping, mEffectiveMass); + } + + /// Deactivate this constraint + inline void Deactivate() + { + mEffectiveMass = 0.0f; + mTotalLambda = 0.0f; + } + + /// Check if constraint is active + inline bool IsActive() const + { + return mEffectiveMass != 0.0f; + } + + /// Templated form of WarmStart with the motion types baked in + template + inline void TemplatedWarmStart(MotionProperties *ioMotionProperties1, float inInvMass1, MotionProperties *ioMotionProperties2, float inInvMass2, Vec3Arg inWorldSpaceAxis, float inWarmStartImpulseRatio) + { + mTotalLambda *= inWarmStartImpulseRatio; + + ApplyVelocityStep(ioMotionProperties1, inInvMass1, ioMotionProperties2, inInvMass2, inWorldSpaceAxis, mTotalLambda); + } + + /// Must be called from the WarmStartVelocityConstraint call to apply the previous frame's impulses + /// @param ioBody1 The first body that this constraint is attached to + /// @param ioBody2 The second body that this constraint is attached to + /// @param inWorldSpaceAxis Axis along which the constraint acts (normalized) + /// @param inWarmStartImpulseRatio Ratio of new step to old time step (dt_new / dt_old) for scaling the lagrange multiplier of the previous frame + inline void WarmStart(Body &ioBody1, Body &ioBody2, Vec3Arg inWorldSpaceAxis, float inWarmStartImpulseRatio) + { + EMotionType motion_type1 = ioBody1.GetMotionType(); + MotionProperties *motion_properties1 = ioBody1.GetMotionPropertiesUnchecked(); + + EMotionType motion_type2 = ioBody2.GetMotionType(); + MotionProperties *motion_properties2 = ioBody2.GetMotionPropertiesUnchecked(); + + // Dispatch to the correct templated form + // Note: Warm starting doesn't differentiate between kinematic/static bodies so we handle both as static bodies + if (motion_type1 == EMotionType::Dynamic) + { + if (motion_type2 == EMotionType::Dynamic) + TemplatedWarmStart(motion_properties1, motion_properties1->GetInverseMass(), motion_properties2, motion_properties2->GetInverseMass(), inWorldSpaceAxis, inWarmStartImpulseRatio); + else + TemplatedWarmStart(motion_properties1, motion_properties1->GetInverseMass(), motion_properties2, 0.0f /* Unused */, inWorldSpaceAxis, inWarmStartImpulseRatio); + } + else + { + JPH_ASSERT(motion_type2 == EMotionType::Dynamic); + TemplatedWarmStart(motion_properties1, 0.0f /* Unused */, motion_properties2, motion_properties2->GetInverseMass(), inWorldSpaceAxis, inWarmStartImpulseRatio); + } + } + + /// Templated form of SolveVelocityConstraint with the motion types baked in, part 1: get the total lambda + template + JPH_INLINE float TemplatedSolveVelocityConstraintGetTotalLambda(const MotionProperties *ioMotionProperties1, const MotionProperties *ioMotionProperties2, Vec3Arg inWorldSpaceAxis) const + { + // Calculate jacobian multiplied by linear velocity + float jv; + if constexpr (Type1 != EMotionType::Static && Type2 != EMotionType::Static) + jv = inWorldSpaceAxis.Dot(ioMotionProperties1->GetLinearVelocity() - ioMotionProperties2->GetLinearVelocity()); + else if constexpr (Type1 != EMotionType::Static) + jv = inWorldSpaceAxis.Dot(ioMotionProperties1->GetLinearVelocity()); + else if constexpr (Type2 != EMotionType::Static) + jv = inWorldSpaceAxis.Dot(-ioMotionProperties2->GetLinearVelocity()); + else + JPH_ASSERT(false); // Static vs static is nonsensical! + + // Calculate jacobian multiplied by angular velocity + if constexpr (Type1 != EMotionType::Static) + jv += Vec3::sLoadFloat3Unsafe(mR1PlusUxAxis).Dot(ioMotionProperties1->GetAngularVelocity()); + if constexpr (Type2 != EMotionType::Static) + jv -= Vec3::sLoadFloat3Unsafe(mR2xAxis).Dot(ioMotionProperties2->GetAngularVelocity()); + + // Lagrange multiplier is: + // + // lambda = -K^-1 (J v + b) + float lambda = mEffectiveMass * (jv - mSpringPart.GetBias(mTotalLambda)); + + // Return the total accumulated lambda + return mTotalLambda + lambda; + } + + /// Templated form of SolveVelocityConstraint with the motion types baked in, part 2: apply new lambda + template + JPH_INLINE bool TemplatedSolveVelocityConstraintApplyLambda(MotionProperties *ioMotionProperties1, float inInvMass1, MotionProperties *ioMotionProperties2, float inInvMass2, Vec3Arg inWorldSpaceAxis, float inTotalLambda) + { + float delta_lambda = inTotalLambda - mTotalLambda; // Calculate change in lambda + mTotalLambda = inTotalLambda; // Store accumulated impulse + + return ApplyVelocityStep(ioMotionProperties1, inInvMass1, ioMotionProperties2, inInvMass2, inWorldSpaceAxis, delta_lambda); + } + + /// Templated form of SolveVelocityConstraint with the motion types baked in + template + inline bool TemplatedSolveVelocityConstraint(MotionProperties *ioMotionProperties1, float inInvMass1, MotionProperties *ioMotionProperties2, float inInvMass2, Vec3Arg inWorldSpaceAxis, float inMinLambda, float inMaxLambda) + { + float total_lambda = TemplatedSolveVelocityConstraintGetTotalLambda(ioMotionProperties1, ioMotionProperties2, inWorldSpaceAxis); + + // Clamp impulse to specified range + total_lambda = Clamp(total_lambda, inMinLambda, inMaxLambda); + + return TemplatedSolveVelocityConstraintApplyLambda(ioMotionProperties1, inInvMass1, ioMotionProperties2, inInvMass2, inWorldSpaceAxis, total_lambda); + } + + /// Iteratively update the velocity constraint. Makes sure d/dt C(...) = 0, where C is the constraint equation. + /// @param ioBody1 The first body that this constraint is attached to + /// @param ioBody2 The second body that this constraint is attached to + /// @param inWorldSpaceAxis Axis along which the constraint acts (normalized) + /// @param inMinLambda Minimum value of constraint impulse to apply (N s) + /// @param inMaxLambda Maximum value of constraint impulse to apply (N s) + inline bool SolveVelocityConstraint(Body &ioBody1, Body &ioBody2, Vec3Arg inWorldSpaceAxis, float inMinLambda, float inMaxLambda) + { + EMotionType motion_type1 = ioBody1.GetMotionType(); + MotionProperties *motion_properties1 = ioBody1.GetMotionPropertiesUnchecked(); + + EMotionType motion_type2 = ioBody2.GetMotionType(); + MotionProperties *motion_properties2 = ioBody2.GetMotionPropertiesUnchecked(); + + // Dispatch to the correct templated form + switch (motion_type1) + { + case EMotionType::Dynamic: + switch (motion_type2) + { + case EMotionType::Dynamic: + return TemplatedSolveVelocityConstraint(motion_properties1, motion_properties1->GetInverseMass(), motion_properties2, motion_properties2->GetInverseMass(), inWorldSpaceAxis, inMinLambda, inMaxLambda); + + case EMotionType::Kinematic: + return TemplatedSolveVelocityConstraint(motion_properties1, motion_properties1->GetInverseMass(), motion_properties2, 0.0f /* Unused */, inWorldSpaceAxis, inMinLambda, inMaxLambda); + + case EMotionType::Static: + return TemplatedSolveVelocityConstraint(motion_properties1, motion_properties1->GetInverseMass(), motion_properties2, 0.0f /* Unused */, inWorldSpaceAxis, inMinLambda, inMaxLambda); + + default: + JPH_ASSERT(false); + break; + } + break; + + case EMotionType::Kinematic: + JPH_ASSERT(motion_type2 == EMotionType::Dynamic); + return TemplatedSolveVelocityConstraint(motion_properties1, 0.0f /* Unused */, motion_properties2, motion_properties2->GetInverseMass(), inWorldSpaceAxis, inMinLambda, inMaxLambda); + + case EMotionType::Static: + JPH_ASSERT(motion_type2 == EMotionType::Dynamic); + return TemplatedSolveVelocityConstraint(motion_properties1, 0.0f /* Unused */, motion_properties2, motion_properties2->GetInverseMass(), inWorldSpaceAxis, inMinLambda, inMaxLambda); + + default: + JPH_ASSERT(false); + break; + } + + return false; + } + + /// Iteratively update the velocity constraint. Makes sure d/dt C(...) = 0, where C is the constraint equation. + /// @param ioBody1 The first body that this constraint is attached to + /// @param ioBody2 The second body that this constraint is attached to + /// @param inInvMass1 The inverse mass of body 1 (only used when body 1 is dynamic) + /// @param inInvMass2 The inverse mass of body 2 (only used when body 2 is dynamic) + /// @param inWorldSpaceAxis Axis along which the constraint acts (normalized) + /// @param inMinLambda Minimum value of constraint impulse to apply (N s) + /// @param inMaxLambda Maximum value of constraint impulse to apply (N s) + inline bool SolveVelocityConstraintWithMassOverride(Body &ioBody1, float inInvMass1, Body &ioBody2, float inInvMass2, Vec3Arg inWorldSpaceAxis, float inMinLambda, float inMaxLambda) + { + EMotionType motion_type1 = ioBody1.GetMotionType(); + MotionProperties *motion_properties1 = ioBody1.GetMotionPropertiesUnchecked(); + + EMotionType motion_type2 = ioBody2.GetMotionType(); + MotionProperties *motion_properties2 = ioBody2.GetMotionPropertiesUnchecked(); + + // Dispatch to the correct templated form + switch (motion_type1) + { + case EMotionType::Dynamic: + switch (motion_type2) + { + case EMotionType::Dynamic: + return TemplatedSolveVelocityConstraint(motion_properties1, inInvMass1, motion_properties2, inInvMass2, inWorldSpaceAxis, inMinLambda, inMaxLambda); + + case EMotionType::Kinematic: + return TemplatedSolveVelocityConstraint(motion_properties1, inInvMass1, motion_properties2, 0.0f /* Unused */, inWorldSpaceAxis, inMinLambda, inMaxLambda); + + case EMotionType::Static: + return TemplatedSolveVelocityConstraint(motion_properties1, inInvMass1, motion_properties2, 0.0f /* Unused */, inWorldSpaceAxis, inMinLambda, inMaxLambda); + + default: + JPH_ASSERT(false); + break; + } + break; + + case EMotionType::Kinematic: + JPH_ASSERT(motion_type2 == EMotionType::Dynamic); + return TemplatedSolveVelocityConstraint(motion_properties1, 0.0f /* Unused */, motion_properties2, inInvMass2, inWorldSpaceAxis, inMinLambda, inMaxLambda); + + case EMotionType::Static: + JPH_ASSERT(motion_type2 == EMotionType::Dynamic); + return TemplatedSolveVelocityConstraint(motion_properties1, 0.0f /* Unused */, motion_properties2, inInvMass2, inWorldSpaceAxis, inMinLambda, inMaxLambda); + + default: + JPH_ASSERT(false); + break; + } + + return false; + } + + /// Iteratively update the position constraint. Makes sure C(...) = 0. + /// @param ioBody1 The first body that this constraint is attached to + /// @param ioBody2 The second body that this constraint is attached to + /// @param inWorldSpaceAxis Axis along which the constraint acts (normalized) + /// @param inC Value of the constraint equation (C) + /// @param inBaumgarte Baumgarte constant (fraction of the error to correct) + inline bool SolvePositionConstraint(Body &ioBody1, Body &ioBody2, Vec3Arg inWorldSpaceAxis, float inC, float inBaumgarte) const + { + // Only apply position constraint when the constraint is hard, otherwise the velocity bias will fix the constraint + if (inC != 0.0f && !mSpringPart.IsActive()) + { + // Calculate lagrange multiplier (lambda) for Baumgarte stabilization: + // + // lambda = -K^-1 * beta / dt * C + // + // We should divide by inDeltaTime, but we should multiply by inDeltaTime in the Euler step below so they're cancelled out + float lambda = -mEffectiveMass * inBaumgarte * inC; + + // Directly integrate velocity change for one time step + // + // Euler velocity integration: + // dv = M^-1 P + // + // Impulse: + // P = J^T lambda + // + // Euler position integration: + // x' = x + dv * dt + // + // Note we don't accumulate velocities for the stabilization. This is using the approach described in 'Modeling and + // Solving Constraints' by Erin Catto presented at GDC 2007. On slide 78 it is suggested to split up the Baumgarte + // stabilization for positional drift so that it does not actually add to the momentum. We combine an Euler velocity + // integrate + a position integrate and then discard the velocity change. + if (ioBody1.IsDynamic()) + { + ioBody1.SubPositionStep((lambda * ioBody1.GetMotionProperties()->GetInverseMass()) * inWorldSpaceAxis); + ioBody1.SubRotationStep(lambda * Vec3::sLoadFloat3Unsafe(mInvI1_R1PlusUxAxis)); + } + if (ioBody2.IsDynamic()) + { + ioBody2.AddPositionStep((lambda * ioBody2.GetMotionProperties()->GetInverseMass()) * inWorldSpaceAxis); + ioBody2.AddRotationStep(lambda * Vec3::sLoadFloat3Unsafe(mInvI2_R2xAxis)); + } + return true; + } + + return false; + } + + /// Iteratively update the position constraint. Makes sure C(...) = 0. + /// @param ioBody1 The first body that this constraint is attached to + /// @param ioBody2 The second body that this constraint is attached to + /// @param inInvMass1 The inverse mass of body 1 (only used when body 1 is dynamic) + /// @param inInvMass2 The inverse mass of body 2 (only used when body 2 is dynamic) + /// @param inWorldSpaceAxis Axis along which the constraint acts (normalized) + /// @param inC Value of the constraint equation (C) + /// @param inBaumgarte Baumgarte constant (fraction of the error to correct) + inline bool SolvePositionConstraintWithMassOverride(Body &ioBody1, float inInvMass1, Body &ioBody2, float inInvMass2, Vec3Arg inWorldSpaceAxis, float inC, float inBaumgarte) const + { + // Only apply position constraint when the constraint is hard, otherwise the velocity bias will fix the constraint + if (inC != 0.0f && !mSpringPart.IsActive()) + { + // Calculate lagrange multiplier (lambda) for Baumgarte stabilization: + // + // lambda = -K^-1 * beta / dt * C + // + // We should divide by inDeltaTime, but we should multiply by inDeltaTime in the Euler step below so they're cancelled out + float lambda = -mEffectiveMass * inBaumgarte * inC; + + // Directly integrate velocity change for one time step + // + // Euler velocity integration: + // dv = M^-1 P + // + // Impulse: + // P = J^T lambda + // + // Euler position integration: + // x' = x + dv * dt + // + // Note we don't accumulate velocities for the stabilization. This is using the approach described in 'Modeling and + // Solving Constraints' by Erin Catto presented at GDC 2007. On slide 78 it is suggested to split up the Baumgarte + // stabilization for positional drift so that it does not actually add to the momentum. We combine an Euler velocity + // integrate + a position integrate and then discard the velocity change. + if (ioBody1.IsDynamic()) + { + ioBody1.SubPositionStep((lambda * inInvMass1) * inWorldSpaceAxis); + ioBody1.SubRotationStep(lambda * Vec3::sLoadFloat3Unsafe(mInvI1_R1PlusUxAxis)); + } + if (ioBody2.IsDynamic()) + { + ioBody2.AddPositionStep((lambda * inInvMass2) * inWorldSpaceAxis); + ioBody2.AddRotationStep(lambda * Vec3::sLoadFloat3Unsafe(mInvI2_R2xAxis)); + } + return true; + } + + return false; + } + + /// Override total lagrange multiplier, can be used to set the initial value for warm starting + inline void SetTotalLambda(float inLambda) + { + mTotalLambda = inLambda; + } + + /// Return lagrange multiplier + inline float GetTotalLambda() const + { + return mTotalLambda; + } + + /// Save state of this constraint part + void SaveState(StateRecorder &inStream) const + { + inStream.Write(mTotalLambda); + } + + /// Restore state of this constraint part + void RestoreState(StateRecorder &inStream) + { + inStream.Read(mTotalLambda); + } + +private: + Float3 mR1PlusUxAxis; + Float3 mR2xAxis; + Float3 mInvI1_R1PlusUxAxis; + Float3 mInvI2_R2xAxis; + float mEffectiveMass = 0.0f; + SpringPart mSpringPart; + float mTotalLambda = 0.0f; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/DualAxisConstraintPart.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/DualAxisConstraintPart.h new file mode 100644 index 00000000000..c0ebe5ac42e --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/DualAxisConstraintPart.h @@ -0,0 +1,276 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +/** + Constrains movement on 2 axis + + @see "Constraints Derivation for Rigid Body Simulation in 3D" - Daniel Chappuis, section 2.3.1 + + Constraint equation (eq 51): + + \f[C = \begin{bmatrix} (p_2 - p_1) \cdot n_1 \\ (p_2 - p_1) \cdot n_2\end{bmatrix}\f] + + Jacobian (transposed) (eq 55): + + \f[J^T = \begin{bmatrix} + -n_1 & -n_2 \\ + -(r_1 + u) \times n_1 & -(r_1 + u) \times n_2 \\ + n_1 & n_2 \\ + r_2 \times n_1 & r_2 \times n_2 + \end{bmatrix}\f] + + Used terms (here and below, everything in world space):\n + n1, n2 = constraint axis (normalized).\n + p1, p2 = constraint points.\n + r1 = p1 - x1.\n + r2 = p2 - x2.\n + u = x2 + r2 - x1 - r1 = p2 - p1.\n + x1, x2 = center of mass for the bodies.\n + v = [v1, w1, v2, w2].\n + v1, v2 = linear velocity of body 1 and 2.\n + w1, w2 = angular velocity of body 1 and 2.\n + M = mass matrix, a diagonal matrix of the mass and inertia with diagonal [m1, I1, m2, I2].\n + \f$K^{-1} = \left( J M^{-1} J^T \right)^{-1}\f$ = effective mass.\n + b = velocity bias.\n + \f$\beta\f$ = baumgarte constant. +**/ +class DualAxisConstraintPart +{ +public: + using Vec2 = Vector<2>; + using Mat22 = Matrix<2, 2>; + +private: + /// Internal helper function to update velocities of bodies after Lagrange multiplier is calculated + JPH_INLINE bool ApplyVelocityStep(Body &ioBody1, Body &ioBody2, Vec3Arg inN1, Vec3Arg inN2, const Vec2 &inLambda) const + { + // Apply impulse if delta is not zero + if (!inLambda.IsZero()) + { + // Calculate velocity change due to constraint + // + // Impulse: + // P = J^T lambda + // + // Euler velocity integration: + // v' = v + M^-1 P + Vec3 impulse = inN1 * inLambda[0] + inN2 * inLambda[1]; + if (ioBody1.IsDynamic()) + { + MotionProperties *mp1 = ioBody1.GetMotionProperties(); + mp1->SubLinearVelocityStep(mp1->GetInverseMass() * impulse); + mp1->SubAngularVelocityStep(mInvI1_R1PlusUxN1 * inLambda[0] + mInvI1_R1PlusUxN2 * inLambda[1]); + } + if (ioBody2.IsDynamic()) + { + MotionProperties *mp2 = ioBody2.GetMotionProperties(); + mp2->AddLinearVelocityStep(mp2->GetInverseMass() * impulse); + mp2->AddAngularVelocityStep(mInvI2_R2xN1 * inLambda[0] + mInvI2_R2xN2 * inLambda[1]); + } + return true; + } + + return false; + } + + /// Internal helper function to calculate the lagrange multiplier + inline void CalculateLagrangeMultiplier(const Body &inBody1, const Body &inBody2, Vec3Arg inN1, Vec3Arg inN2, Vec2 &outLambda) const + { + // Calculate lagrange multiplier: + // + // lambda = -K^-1 (J v + b) + Vec3 delta_lin = inBody1.GetLinearVelocity() - inBody2.GetLinearVelocity(); + Vec2 jv; + jv[0] = inN1.Dot(delta_lin) + mR1PlusUxN1.Dot(inBody1.GetAngularVelocity()) - mR2xN1.Dot(inBody2.GetAngularVelocity()); + jv[1] = inN2.Dot(delta_lin) + mR1PlusUxN2.Dot(inBody1.GetAngularVelocity()) - mR2xN2.Dot(inBody2.GetAngularVelocity()); + outLambda = mEffectiveMass * jv; + } + +public: + /// Calculate properties used during the functions below + /// All input vectors are in world space + inline void CalculateConstraintProperties(const Body &inBody1, Mat44Arg inRotation1, Vec3Arg inR1PlusU, const Body &inBody2, Mat44Arg inRotation2, Vec3Arg inR2, Vec3Arg inN1, Vec3Arg inN2) + { + JPH_ASSERT(inN1.IsNormalized(1.0e-5f)); + JPH_ASSERT(inN2.IsNormalized(1.0e-5f)); + + // Calculate properties used during constraint solving + mR1PlusUxN1 = inR1PlusU.Cross(inN1); + mR1PlusUxN2 = inR1PlusU.Cross(inN2); + mR2xN1 = inR2.Cross(inN1); + mR2xN2 = inR2.Cross(inN2); + + // Calculate effective mass: K^-1 = (J M^-1 J^T)^-1, eq 59 + Mat22 inv_effective_mass; + if (inBody1.IsDynamic()) + { + const MotionProperties *mp1 = inBody1.GetMotionProperties(); + Mat44 inv_i1 = mp1->GetInverseInertiaForRotation(inRotation1); + mInvI1_R1PlusUxN1 = inv_i1.Multiply3x3(mR1PlusUxN1); + mInvI1_R1PlusUxN2 = inv_i1.Multiply3x3(mR1PlusUxN2); + + inv_effective_mass(0, 0) = mp1->GetInverseMass() + mR1PlusUxN1.Dot(mInvI1_R1PlusUxN1); + inv_effective_mass(0, 1) = mR1PlusUxN1.Dot(mInvI1_R1PlusUxN2); + inv_effective_mass(1, 0) = mR1PlusUxN2.Dot(mInvI1_R1PlusUxN1); + inv_effective_mass(1, 1) = mp1->GetInverseMass() + mR1PlusUxN2.Dot(mInvI1_R1PlusUxN2); + } + else + { + JPH_IF_DEBUG(mInvI1_R1PlusUxN1 = Vec3::sNaN();) + JPH_IF_DEBUG(mInvI1_R1PlusUxN2 = Vec3::sNaN();) + + inv_effective_mass = Mat22::sZero(); + } + + if (inBody2.IsDynamic()) + { + const MotionProperties *mp2 = inBody2.GetMotionProperties(); + Mat44 inv_i2 = mp2->GetInverseInertiaForRotation(inRotation2); + mInvI2_R2xN1 = inv_i2.Multiply3x3(mR2xN1); + mInvI2_R2xN2 = inv_i2.Multiply3x3(mR2xN2); + + inv_effective_mass(0, 0) += mp2->GetInverseMass() + mR2xN1.Dot(mInvI2_R2xN1); + inv_effective_mass(0, 1) += mR2xN1.Dot(mInvI2_R2xN2); + inv_effective_mass(1, 0) += mR2xN2.Dot(mInvI2_R2xN1); + inv_effective_mass(1, 1) += mp2->GetInverseMass() + mR2xN2.Dot(mInvI2_R2xN2); + } + else + { + JPH_IF_DEBUG(mInvI2_R2xN1 = Vec3::sNaN();) + JPH_IF_DEBUG(mInvI2_R2xN2 = Vec3::sNaN();) + } + + if (!mEffectiveMass.SetInversed(inv_effective_mass)) + Deactivate(); + } + + /// Deactivate this constraint + inline void Deactivate() + { + mEffectiveMass.SetZero(); + mTotalLambda.SetZero(); + } + + /// Check if constraint is active + inline bool IsActive() const + { + return !mEffectiveMass.IsZero(); + } + + /// Must be called from the WarmStartVelocityConstraint call to apply the previous frame's impulses + /// All input vectors are in world space + inline void WarmStart(Body &ioBody1, Body &ioBody2, Vec3Arg inN1, Vec3Arg inN2, float inWarmStartImpulseRatio) + { + mTotalLambda *= inWarmStartImpulseRatio; + ApplyVelocityStep(ioBody1, ioBody2, inN1, inN2, mTotalLambda); + } + + /// Iteratively update the velocity constraint. Makes sure d/dt C(...) = 0, where C is the constraint equation. + /// All input vectors are in world space + inline bool SolveVelocityConstraint(Body &ioBody1, Body &ioBody2, Vec3Arg inN1, Vec3Arg inN2) + { + Vec2 lambda; + CalculateLagrangeMultiplier(ioBody1, ioBody2, inN1, inN2, lambda); + + // Store accumulated lambda + mTotalLambda += lambda; + + return ApplyVelocityStep(ioBody1, ioBody2, inN1, inN2, lambda); + } + + /// Iteratively update the position constraint. Makes sure C(...) = 0. + /// All input vectors are in world space + inline bool SolvePositionConstraint(Body &ioBody1, Body &ioBody2, Vec3Arg inU, Vec3Arg inN1, Vec3Arg inN2, float inBaumgarte) const + { + Vec2 c; + c[0] = inU.Dot(inN1); + c[1] = inU.Dot(inN2); + if (!c.IsZero()) + { + // Calculate lagrange multiplier (lambda) for Baumgarte stabilization: + // + // lambda = -K^-1 * beta / dt * C + // + // We should divide by inDeltaTime, but we should multiply by inDeltaTime in the Euler step below so they're cancelled out + Vec2 lambda = -inBaumgarte * (mEffectiveMass * c); + + // Directly integrate velocity change for one time step + // + // Euler velocity integration: + // dv = M^-1 P + // + // Impulse: + // P = J^T lambda + // + // Euler position integration: + // x' = x + dv * dt + // + // Note we don't accumulate velocities for the stabilization. This is using the approach described in 'Modeling and + // Solving Constraints' by Erin Catto presented at GDC 2007. On slide 78 it is suggested to split up the Baumgarte + // stabilization for positional drift so that it does not actually add to the momentum. We combine an Euler velocity + // integrate + a position integrate and then discard the velocity change. + Vec3 impulse = inN1 * lambda[0] + inN2 * lambda[1]; + if (ioBody1.IsDynamic()) + { + ioBody1.SubPositionStep(ioBody1.GetMotionProperties()->GetInverseMass() * impulse); + ioBody1.SubRotationStep(mInvI1_R1PlusUxN1 * lambda[0] + mInvI1_R1PlusUxN2 * lambda[1]); + } + if (ioBody2.IsDynamic()) + { + ioBody2.AddPositionStep(ioBody2.GetMotionProperties()->GetInverseMass() * impulse); + ioBody2.AddRotationStep(mInvI2_R2xN1 * lambda[0] + mInvI2_R2xN2 * lambda[1]); + } + return true; + } + + return false; + } + + /// Override total lagrange multiplier, can be used to set the initial value for warm starting + inline void SetTotalLambda(const Vec2 &inLambda) + { + mTotalLambda = inLambda; + } + + /// Return lagrange multiplier + inline const Vec2 & GetTotalLambda() const + { + return mTotalLambda; + } + + /// Save state of this constraint part + void SaveState(StateRecorder &inStream) const + { + inStream.Write(mTotalLambda); + } + + /// Restore state of this constraint part + void RestoreState(StateRecorder &inStream) + { + inStream.Read(mTotalLambda); + } + +private: + Vec3 mR1PlusUxN1; + Vec3 mR1PlusUxN2; + Vec3 mR2xN1; + Vec3 mR2xN2; + Vec3 mInvI1_R1PlusUxN1; + Vec3 mInvI1_R1PlusUxN2; + Vec3 mInvI2_R2xN1; + Vec3 mInvI2_R2xN2; + Mat22 mEffectiveMass; + Vec2 mTotalLambda { Vec2::sZero() }; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/GearConstraintPart.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/GearConstraintPart.h new file mode 100644 index 00000000000..6e277a64b56 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/GearConstraintPart.h @@ -0,0 +1,195 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +JPH_NAMESPACE_BEGIN + +/// Constraint that constrains two rotations using a gear (rotating in opposite direction) +/// +/// Constraint equation: +/// +/// C = Rotation1(t) + r Rotation2(t) +/// +/// Derivative: +/// +/// d/dt C = 0 +/// <=> w1 . a + r w2 . b = 0 +/// +/// Jacobian: +/// +/// \f[J = \begin{bmatrix}0 & a^T & 0 & r b^T\end{bmatrix}\f] +/// +/// Used terms (here and below, everything in world space):\n +/// a = axis around which body 1 rotates (normalized).\n +/// b = axis along which body 2 slides (normalized).\n +/// Rotation1(t) = rotation around a of body 1.\n +/// Rotation2(t) = rotation around b of body 2.\n +/// r = ratio between rotation for body 1 and 2.\n +/// v = [v1, w1, v2, w2].\n +/// v1, v2 = linear velocity of body 1 and 2.\n +/// w1, w2 = angular velocity of body 1 and 2.\n +/// M = mass matrix, a diagonal matrix of the mass and inertia with diagonal [m1, I1, m2, I2].\n +/// \f$K^{-1} = \left( J M^{-1} J^T \right)^{-1}\f$ = effective mass.\n +/// \f$\beta\f$ = baumgarte constant. +class GearConstraintPart +{ + /// Internal helper function to update velocities of bodies after Lagrange multiplier is calculated + JPH_INLINE bool ApplyVelocityStep(Body &ioBody1, Body &ioBody2, float inLambda) const + { + // Apply impulse if delta is not zero + if (inLambda != 0.0f) + { + // Calculate velocity change due to constraint + // + // Impulse: + // P = J^T lambda + // + // Euler velocity integration: + // v' = v + M^-1 P + ioBody1.GetMotionProperties()->AddAngularVelocityStep(inLambda * mInvI1_A); + ioBody2.GetMotionProperties()->AddAngularVelocityStep(inLambda * mInvI2_B); + return true; + } + + return false; + } + +public: + /// Calculate properties used during the functions below + /// @param inBody1 The first body that this constraint is attached to + /// @param inBody2 The second body that this constraint is attached to + /// @param inWorldSpaceHingeAxis1 The axis around which body 1 rotates + /// @param inWorldSpaceHingeAxis2 The axis around which body 2 rotates + /// @param inRatio The ratio between rotation and translation + inline void CalculateConstraintProperties(const Body &inBody1, Vec3Arg inWorldSpaceHingeAxis1, const Body &inBody2, Vec3Arg inWorldSpaceHingeAxis2, float inRatio) + { + JPH_ASSERT(inWorldSpaceHingeAxis1.IsNormalized(1.0e-4f)); + JPH_ASSERT(inWorldSpaceHingeAxis2.IsNormalized(1.0e-4f)); + + // Calculate: I1^-1 a + mInvI1_A = inBody1.GetMotionProperties()->MultiplyWorldSpaceInverseInertiaByVector(inBody1.GetRotation(), inWorldSpaceHingeAxis1); + + // Calculate: I2^-1 b + mInvI2_B = inBody2.GetMotionProperties()->MultiplyWorldSpaceInverseInertiaByVector(inBody2.GetRotation(), inWorldSpaceHingeAxis2); + + // K^-1 = 1 / (J M^-1 J^T) = 1 / (a^T I1^-1 a + r^2 * b^T I2^-1 b) + float inv_effective_mass = (inWorldSpaceHingeAxis1.Dot(mInvI1_A) + inWorldSpaceHingeAxis2.Dot(mInvI2_B) * Square(inRatio)); + if (inv_effective_mass == 0.0f) + Deactivate(); + else + mEffectiveMass = 1.0f / inv_effective_mass; + } + + /// Deactivate this constraint + inline void Deactivate() + { + mEffectiveMass = 0.0f; + mTotalLambda = 0.0f; + } + + /// Check if constraint is active + inline bool IsActive() const + { + return mEffectiveMass != 0.0f; + } + + /// Must be called from the WarmStartVelocityConstraint call to apply the previous frame's impulses + /// @param ioBody1 The first body that this constraint is attached to + /// @param ioBody2 The second body that this constraint is attached to + /// @param inWarmStartImpulseRatio Ratio of new step to old time step (dt_new / dt_old) for scaling the lagrange multiplier of the previous frame + inline void WarmStart(Body &ioBody1, Body &ioBody2, float inWarmStartImpulseRatio) + { + mTotalLambda *= inWarmStartImpulseRatio; + ApplyVelocityStep(ioBody1, ioBody2, mTotalLambda); + } + + /// Iteratively update the velocity constraint. Makes sure d/dt C(...) = 0, where C is the constraint equation. + /// @param ioBody1 The first body that this constraint is attached to + /// @param ioBody2 The second body that this constraint is attached to + /// @param inWorldSpaceHingeAxis1 The axis around which body 1 rotates + /// @param inWorldSpaceHingeAxis2 The axis around which body 2 rotates + /// @param inRatio The ratio between rotation and translation + inline bool SolveVelocityConstraint(Body &ioBody1, Vec3Arg inWorldSpaceHingeAxis1, Body &ioBody2, Vec3Arg inWorldSpaceHingeAxis2, float inRatio) + { + // Lagrange multiplier is: + // + // lambda = -K^-1 (J v + b) + float lambda = -mEffectiveMass * (inWorldSpaceHingeAxis1.Dot(ioBody1.GetAngularVelocity()) + inRatio * inWorldSpaceHingeAxis2.Dot(ioBody2.GetAngularVelocity())); + mTotalLambda += lambda; // Store accumulated impulse + + return ApplyVelocityStep(ioBody1, ioBody2, lambda); + } + + /// Return lagrange multiplier + float GetTotalLambda() const + { + return mTotalLambda; + } + + /// Iteratively update the position constraint. Makes sure C(...) == 0. + /// @param ioBody1 The first body that this constraint is attached to + /// @param ioBody2 The second body that this constraint is attached to + /// @param inC Value of the constraint equation (C) + /// @param inBaumgarte Baumgarte constant (fraction of the error to correct) + inline bool SolvePositionConstraint(Body &ioBody1, Body &ioBody2, float inC, float inBaumgarte) const + { + // Only apply position constraint when the constraint is hard, otherwise the velocity bias will fix the constraint + if (inC != 0.0f) + { + // Calculate lagrange multiplier (lambda) for Baumgarte stabilization: + // + // lambda = -K^-1 * beta / dt * C + // + // We should divide by inDeltaTime, but we should multiply by inDeltaTime in the Euler step below so they're cancelled out + float lambda = -mEffectiveMass * inBaumgarte * inC; + + // Directly integrate velocity change for one time step + // + // Euler velocity integration: + // dv = M^-1 P + // + // Impulse: + // P = J^T lambda + // + // Euler position integration: + // x' = x + dv * dt + // + // Note we don't accumulate velocities for the stabilization. This is using the approach described in 'Modeling and + // Solving Constraints' by Erin Catto presented at GDC 2007. On slide 78 it is suggested to split up the Baumgarte + // stabilization for positional drift so that it does not actually add to the momentum. We combine an Euler velocity + // integrate + a position integrate and then discard the velocity change. + if (ioBody1.IsDynamic()) + ioBody1.AddRotationStep(lambda * mInvI1_A); + if (ioBody2.IsDynamic()) + ioBody2.AddRotationStep(lambda * mInvI2_B); + return true; + } + + return false; + } + + /// Save state of this constraint part + void SaveState(StateRecorder &inStream) const + { + inStream.Write(mTotalLambda); + } + + /// Restore state of this constraint part + void RestoreState(StateRecorder &inStream) + { + inStream.Read(mTotalLambda); + } + +private: + Vec3 mInvI1_A; + Vec3 mInvI2_B; + float mEffectiveMass = 0.0f; + float mTotalLambda = 0.0f; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/HingeRotationConstraintPart.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/HingeRotationConstraintPart.h new file mode 100644 index 00000000000..5f566f70179 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/HingeRotationConstraintPart.h @@ -0,0 +1,222 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +/** + Constrains rotation around 2 axis so that it only allows rotation around 1 axis + + Based on: "Constraints Derivation for Rigid Body Simulation in 3D" - Daniel Chappuis, section 2.4.1 + + Constraint equation (eq 87): + + \f[C = \begin{bmatrix}a_1 \cdot b_2 \\ a_1 \cdot c_2\end{bmatrix}\f] + + Jacobian (eq 90): + + \f[J = \begin{bmatrix} + 0 & -b_2 \times a_1 & 0 & b_2 \times a_1 \\ + 0 & -c_2 \times a_1 & 0 & c2 \times a_1 + \end{bmatrix}\f] + + Used terms (here and below, everything in world space):\n + a1 = hinge axis on body 1.\n + b2, c2 = axis perpendicular to hinge axis on body 2.\n + x1, x2 = center of mass for the bodies.\n + v = [v1, w1, v2, w2].\n + v1, v2 = linear velocity of body 1 and 2.\n + w1, w2 = angular velocity of body 1 and 2.\n + M = mass matrix, a diagonal matrix of the mass and inertia with diagonal [m1, I1, m2, I2].\n + \f$K^{-1} = \left( J M^{-1} J^T \right)^{-1}\f$ = effective mass.\n + b = velocity bias.\n + \f$\beta\f$ = baumgarte constant.\n + E = identity matrix. +**/ +class HingeRotationConstraintPart +{ +public: + using Vec2 = Vector<2>; + using Mat22 = Matrix<2, 2>; + +private: + /// Internal helper function to update velocities of bodies after Lagrange multiplier is calculated + JPH_INLINE bool ApplyVelocityStep(Body &ioBody1, Body &ioBody2, const Vec2 &inLambda) const + { + // Apply impulse if delta is not zero + if (!inLambda.IsZero()) + { + // Calculate velocity change due to constraint + // + // Impulse: + // P = J^T lambda + // + // Euler velocity integration: + // v' = v + M^-1 P + Vec3 impulse = mB2xA1 * inLambda[0] + mC2xA1 * inLambda[1]; + if (ioBody1.IsDynamic()) + ioBody1.GetMotionProperties()->SubAngularVelocityStep(mInvI1.Multiply3x3(impulse)); + if (ioBody2.IsDynamic()) + ioBody2.GetMotionProperties()->AddAngularVelocityStep(mInvI2.Multiply3x3(impulse)); + return true; + } + + return false; + } + +public: + /// Calculate properties used during the functions below + inline void CalculateConstraintProperties(const Body &inBody1, Mat44Arg inRotation1, Vec3Arg inWorldSpaceHingeAxis1, const Body &inBody2, Mat44Arg inRotation2, Vec3Arg inWorldSpaceHingeAxis2) + { + JPH_ASSERT(inWorldSpaceHingeAxis1.IsNormalized(1.0e-5f)); + JPH_ASSERT(inWorldSpaceHingeAxis2.IsNormalized(1.0e-5f)); + + // Calculate hinge axis in world space + mA1 = inWorldSpaceHingeAxis1; + Vec3 a2 = inWorldSpaceHingeAxis2; + float dot = mA1.Dot(a2); + if (dot <= 1.0e-3f) + { + // World space axes are more than 90 degrees apart, get a perpendicular vector in the plane formed by mA1 and a2 as hinge axis until the rotation is less than 90 degrees + Vec3 perp = a2 - dot * mA1; + if (perp.LengthSq() < 1.0e-6f) + { + // mA1 ~ -a2, take random perpendicular + perp = mA1.GetNormalizedPerpendicular(); + } + + // Blend in a little bit from mA1 so we're less than 90 degrees apart + a2 = (0.99f * perp.Normalized() + 0.01f * mA1).Normalized(); + } + mB2 = a2.GetNormalizedPerpendicular(); + mC2 = a2.Cross(mB2); + + // Calculate properties used during constraint solving + mInvI1 = inBody1.IsDynamic()? inBody1.GetMotionProperties()->GetInverseInertiaForRotation(inRotation1) : Mat44::sZero(); + mInvI2 = inBody2.IsDynamic()? inBody2.GetMotionProperties()->GetInverseInertiaForRotation(inRotation2) : Mat44::sZero(); + mB2xA1 = mB2.Cross(mA1); + mC2xA1 = mC2.Cross(mA1); + + // Calculate effective mass: K^-1 = (J M^-1 J^T)^-1 + Mat44 summed_inv_inertia = mInvI1 + mInvI2; + Mat22 inv_effective_mass; + inv_effective_mass(0, 0) = mB2xA1.Dot(summed_inv_inertia.Multiply3x3(mB2xA1)); + inv_effective_mass(0, 1) = mB2xA1.Dot(summed_inv_inertia.Multiply3x3(mC2xA1)); + inv_effective_mass(1, 0) = mC2xA1.Dot(summed_inv_inertia.Multiply3x3(mB2xA1)); + inv_effective_mass(1, 1) = mC2xA1.Dot(summed_inv_inertia.Multiply3x3(mC2xA1)); + if (!mEffectiveMass.SetInversed(inv_effective_mass)) + Deactivate(); + } + + /// Deactivate this constraint + inline void Deactivate() + { + mEffectiveMass.SetZero(); + mTotalLambda.SetZero(); + } + + /// Must be called from the WarmStartVelocityConstraint call to apply the previous frame's impulses + inline void WarmStart(Body &ioBody1, Body &ioBody2, float inWarmStartImpulseRatio) + { + mTotalLambda *= inWarmStartImpulseRatio; + ApplyVelocityStep(ioBody1, ioBody2, mTotalLambda); + } + + /// Iteratively update the velocity constraint. Makes sure d/dt C(...) = 0, where C is the constraint equation. + inline bool SolveVelocityConstraint(Body &ioBody1, Body &ioBody2) + { + // Calculate lagrange multiplier: + // + // lambda = -K^-1 (J v + b) + Vec3 delta_ang = ioBody1.GetAngularVelocity() - ioBody2.GetAngularVelocity(); + Vec2 jv; + jv[0] = mB2xA1.Dot(delta_ang); + jv[1] = mC2xA1.Dot(delta_ang); + Vec2 lambda = mEffectiveMass * jv; + + // Store accumulated lambda + mTotalLambda += lambda; + + return ApplyVelocityStep(ioBody1, ioBody2, lambda); + } + + /// Iteratively update the position constraint. Makes sure C(...) = 0. + inline bool SolvePositionConstraint(Body &ioBody1, Body &ioBody2, float inBaumgarte) const + { + // Constraint needs Axis of body 1 perpendicular to both B and C from body 2 (which are both perpendicular to the Axis of body 2) + Vec2 c; + c[0] = mA1.Dot(mB2); + c[1] = mA1.Dot(mC2); + if (!c.IsZero()) + { + // Calculate lagrange multiplier (lambda) for Baumgarte stabilization: + // + // lambda = -K^-1 * beta / dt * C + // + // We should divide by inDeltaTime, but we should multiply by inDeltaTime in the Euler step below so they're cancelled out + Vec2 lambda = -inBaumgarte * (mEffectiveMass * c); + + // Directly integrate velocity change for one time step + // + // Euler velocity integration: + // dv = M^-1 P + // + // Impulse: + // P = J^T lambda + // + // Euler position integration: + // x' = x + dv * dt + // + // Note we don't accumulate velocities for the stabilization. This is using the approach described in 'Modeling and + // Solving Constraints' by Erin Catto presented at GDC 2007. On slide 78 it is suggested to split up the Baumgarte + // stabilization for positional drift so that it does not actually add to the momentum. We combine an Euler velocity + // integrate + a position integrate and then discard the velocity change. + Vec3 impulse = mB2xA1 * lambda[0] + mC2xA1 * lambda[1]; + if (ioBody1.IsDynamic()) + ioBody1.SubRotationStep(mInvI1.Multiply3x3(impulse)); + if (ioBody2.IsDynamic()) + ioBody2.AddRotationStep(mInvI2.Multiply3x3(impulse)); + return true; + } + + return false; + } + + /// Return lagrange multiplier + const Vec2 & GetTotalLambda() const + { + return mTotalLambda; + } + + /// Save state of this constraint part + void SaveState(StateRecorder &inStream) const + { + inStream.Write(mTotalLambda); + } + + /// Restore state of this constraint part + void RestoreState(StateRecorder &inStream) + { + inStream.Read(mTotalLambda); + } + +private: + Vec3 mA1; ///< World space hinge axis for body 1 + Vec3 mB2; ///< World space perpendiculars of hinge axis for body 2 + Vec3 mC2; + Mat44 mInvI1; + Mat44 mInvI2; + Vec3 mB2xA1; + Vec3 mC2xA1; + Mat22 mEffectiveMass; + Vec2 mTotalLambda { Vec2::sZero() }; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/IndependentAxisConstraintPart.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/IndependentAxisConstraintPart.h new file mode 100644 index 00000000000..5b752c6c693 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/IndependentAxisConstraintPart.h @@ -0,0 +1,246 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2022 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +JPH_NAMESPACE_BEGIN + +/// Constraint part to an AxisConstraintPart but both bodies have an independent axis on which the force is applied. +/// +/// Constraint equation: +/// +/// \f[C = (x_1 + r_1 - f_1) . n_1 + r (x_2 + r_2 - f_2) \cdot n_2\f] +/// +/// Calculating the Jacobian: +/// +/// \f[dC/dt = (v_1 + w_1 \times r_1) \cdot n_1 + (x_1 + r_1 - f_1) \cdot d n_1/dt + r (v_2 + w_2 \times r_2) \cdot n_2 + r (x_2 + r_2 - f_2) \cdot d n_2/dt\f] +/// +/// Assuming that d n1/dt and d n2/dt are small this becomes: +/// +/// \f[(v_1 + w_1 \times r_1) \cdot n_1 + r (v_2 + w_2 \times r_2) \cdot n_2\f] +/// \f[= v_1 \cdot n_1 + r_1 \times n_1 \cdot w_1 + r v_2 \cdot n_2 + r r_2 \times n_2 \cdot w_2\f] +/// +/// Jacobian: +/// +/// \f[J = \begin{bmatrix}n_1 & r_1 \times n_1 & r n_2 & r r_2 \times n_2\end{bmatrix}\f] +/// +/// Effective mass: +/// +/// \f[K = m_1^{-1} + r_1 \times n_1 I_1^{-1} r_1 \times n_1 + r^2 m_2^{-1} + r^2 r_2 \times n_2 I_2^{-1} r_2 \times n_2\f] +/// +/// Used terms (here and below, everything in world space):\n +/// n1 = (x1 + r1 - f1) / |x1 + r1 - f1|, axis along which the force is applied for body 1\n +/// n2 = (x2 + r2 - f2) / |x2 + r2 - f2|, axis along which the force is applied for body 2\n +/// r = ratio how forces are applied between bodies.\n +/// x1, x2 = center of mass for the bodies.\n +/// v = [v1, w1, v2, w2].\n +/// v1, v2 = linear velocity of body 1 and 2.\n +/// w1, w2 = angular velocity of body 1 and 2.\n +/// M = mass matrix, a diagonal matrix of the mass and inertia with diagonal [m1, I1, m2, I2].\n +/// \f$K^{-1} = \left( J M^{-1} J^T \right)^{-1}\f$ = effective mass.\n +/// b = velocity bias.\n +/// \f$\beta\f$ = baumgarte constant. +class IndependentAxisConstraintPart +{ + /// Internal helper function to update velocities of bodies after Lagrange multiplier is calculated + JPH_INLINE bool ApplyVelocityStep(Body &ioBody1, Body &ioBody2, Vec3Arg inN1, Vec3Arg inN2, float inRatio, float inLambda) const + { + // Apply impulse if delta is not zero + if (inLambda != 0.0f) + { + // Calculate velocity change due to constraint + // + // Impulse: + // P = J^T lambda + // + // Euler velocity integration: + // v' = v + M^-1 P + if (ioBody1.IsDynamic()) + { + MotionProperties *mp1 = ioBody1.GetMotionProperties(); + mp1->AddLinearVelocityStep((mp1->GetInverseMass() * inLambda) * inN1); + mp1->AddAngularVelocityStep(mInvI1_R1xN1 * inLambda); + } + if (ioBody2.IsDynamic()) + { + MotionProperties *mp2 = ioBody2.GetMotionProperties(); + mp2->AddLinearVelocityStep((inRatio * mp2->GetInverseMass() * inLambda) * inN2); + mp2->AddAngularVelocityStep(mInvI2_RatioR2xN2 * inLambda); + } + return true; + } + + return false; + } + +public: + /// Calculate properties used during the functions below + /// @param inBody1 The first body that this constraint is attached to + /// @param inBody2 The second body that this constraint is attached to + /// @param inR1 The position on which the constraint operates on body 1 relative to COM + /// @param inN1 The world space normal in which the constraint operates for body 1 + /// @param inR2 The position on which the constraint operates on body 1 relative to COM + /// @param inN2 The world space normal in which the constraint operates for body 2 + /// @param inRatio The ratio how forces are applied between bodies + inline void CalculateConstraintProperties(const Body &inBody1, const Body &inBody2, Vec3Arg inR1, Vec3Arg inN1, Vec3Arg inR2, Vec3Arg inN2, float inRatio) + { + JPH_ASSERT(inN1.IsNormalized(1.0e-4f) && inN2.IsNormalized(1.0e-4f)); + + float inv_effective_mass = 0.0f; + + if (!inBody1.IsStatic()) + { + const MotionProperties *mp1 = inBody1.GetMotionProperties(); + + mR1xN1 = inR1.Cross(inN1); + mInvI1_R1xN1 = mp1->MultiplyWorldSpaceInverseInertiaByVector(inBody1.GetRotation(), mR1xN1); + + inv_effective_mass += mp1->GetInverseMass() + mInvI1_R1xN1.Dot(mR1xN1); + } + + if (!inBody2.IsStatic()) + { + const MotionProperties *mp2 = inBody2.GetMotionProperties(); + + mRatioR2xN2 = inRatio * inR2.Cross(inN2); + mInvI2_RatioR2xN2 = mp2->MultiplyWorldSpaceInverseInertiaByVector(inBody2.GetRotation(), mRatioR2xN2); + + inv_effective_mass += Square(inRatio) * mp2->GetInverseMass() + mInvI2_RatioR2xN2.Dot(mRatioR2xN2); + } + + // Calculate inverse effective mass: K = J M^-1 J^T + if (inv_effective_mass == 0.0f) + Deactivate(); + else + mEffectiveMass = 1.0f / inv_effective_mass; + } + + /// Deactivate this constraint + inline void Deactivate() + { + mEffectiveMass = 0.0f; + mTotalLambda = 0.0f; + } + + /// Check if constraint is active + inline bool IsActive() const + { + return mEffectiveMass != 0.0f; + } + + /// Must be called from the WarmStartVelocityConstraint call to apply the previous frame's impulses + /// @param ioBody1 The first body that this constraint is attached to + /// @param ioBody2 The second body that this constraint is attached to + /// @param inN1 The world space normal in which the constraint operates for body 1 + /// @param inN2 The world space normal in which the constraint operates for body 2 + /// @param inRatio The ratio how forces are applied between bodies + /// @param inWarmStartImpulseRatio Ratio of new step to old time step (dt_new / dt_old) for scaling the lagrange multiplier of the previous frame + inline void WarmStart(Body &ioBody1, Body &ioBody2, Vec3Arg inN1, Vec3Arg inN2, float inRatio, float inWarmStartImpulseRatio) + { + mTotalLambda *= inWarmStartImpulseRatio; + ApplyVelocityStep(ioBody1, ioBody2, inN1, inN2, inRatio, mTotalLambda); + } + + /// Iteratively update the velocity constraint. Makes sure d/dt C(...) = 0, where C is the constraint equation. + /// @param ioBody1 The first body that this constraint is attached to + /// @param ioBody2 The second body that this constraint is attached to + /// @param inN1 The world space normal in which the constraint operates for body 1 + /// @param inN2 The world space normal in which the constraint operates for body 2 + /// @param inRatio The ratio how forces are applied between bodies + /// @param inMinLambda Minimum angular impulse to apply (N m s) + /// @param inMaxLambda Maximum angular impulse to apply (N m s) + inline bool SolveVelocityConstraint(Body &ioBody1, Body &ioBody2, Vec3Arg inN1, Vec3Arg inN2, float inRatio, float inMinLambda, float inMaxLambda) + { + // Lagrange multiplier is: + // + // lambda = -K^-1 (J v + b) + float lambda = -mEffectiveMass * (inN1.Dot(ioBody1.GetLinearVelocity()) + mR1xN1.Dot(ioBody1.GetAngularVelocity()) + inRatio * inN2.Dot(ioBody2.GetLinearVelocity()) + mRatioR2xN2.Dot(ioBody2.GetAngularVelocity())); + float new_lambda = Clamp(mTotalLambda + lambda, inMinLambda, inMaxLambda); // Clamp impulse + lambda = new_lambda - mTotalLambda; // Lambda potentially got clamped, calculate the new impulse to apply + mTotalLambda = new_lambda; // Store accumulated impulse + + return ApplyVelocityStep(ioBody1, ioBody2, inN1, inN2, inRatio, lambda); + } + + /// Return lagrange multiplier + float GetTotalLambda() const + { + return mTotalLambda; + } + + /// Iteratively update the position constraint. Makes sure C(...) == 0. + /// @param ioBody1 The first body that this constraint is attached to + /// @param ioBody2 The second body that this constraint is attached to + /// @param inN1 The world space normal in which the constraint operates for body 1 + /// @param inN2 The world space normal in which the constraint operates for body 2 + /// @param inRatio The ratio how forces are applied between bodies + /// @param inC Value of the constraint equation (C) + /// @param inBaumgarte Baumgarte constant (fraction of the error to correct) + inline bool SolvePositionConstraint(Body &ioBody1, Body &ioBody2, Vec3Arg inN1, Vec3Arg inN2, float inRatio, float inC, float inBaumgarte) const + { + if (inC != 0.0f) + { + // Calculate lagrange multiplier (lambda) for Baumgarte stabilization: + // + // lambda = -K^-1 * beta / dt * C + // + // We should divide by inDeltaTime, but we should multiply by inDeltaTime in the Euler step below so they're cancelled out + float lambda = -mEffectiveMass * inBaumgarte * inC; + + // Directly integrate velocity change for one time step + // + // Euler velocity integration: + // dv = M^-1 P + // + // Impulse: + // P = J^T lambda + // + // Euler position integration: + // x' = x + dv * dt + // + // Note we don't accumulate velocities for the stabilization. This is using the approach described in 'Modeling and + // Solving Constraints' by Erin Catto presented at GDC 2007. On slide 78 it is suggested to split up the Baumgarte + // stabilization for positional drift so that it does not actually add to the momentum. We combine an Euler velocity + // integrate + a position integrate and then discard the velocity change. + if (ioBody1.IsDynamic()) + { + ioBody1.AddPositionStep((lambda * ioBody1.GetMotionPropertiesUnchecked()->GetInverseMass()) * inN1); + ioBody1.AddRotationStep(lambda * mInvI1_R1xN1); + } + if (ioBody2.IsDynamic()) + { + ioBody2.AddPositionStep((lambda * inRatio * ioBody2.GetMotionPropertiesUnchecked()->GetInverseMass()) * inN2); + ioBody2.AddRotationStep(lambda * mInvI2_RatioR2xN2); + } + return true; + } + + return false; + } + + /// Save state of this constraint part + void SaveState(StateRecorder &inStream) const + { + inStream.Write(mTotalLambda); + } + + /// Restore state of this constraint part + void RestoreState(StateRecorder &inStream) + { + inStream.Read(mTotalLambda); + } + +private: + Vec3 mR1xN1; + Vec3 mInvI1_R1xN1; + Vec3 mRatioR2xN2; + Vec3 mInvI2_RatioR2xN2; + float mEffectiveMass = 0.0f; + float mTotalLambda = 0.0f; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/PointConstraintPart.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/PointConstraintPart.h new file mode 100644 index 00000000000..62f663f9e5a --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/PointConstraintPart.h @@ -0,0 +1,239 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +JPH_NAMESPACE_BEGIN + +/// Constrains movement along 3 axis +/// +/// @see "Constraints Derivation for Rigid Body Simulation in 3D" - Daniel Chappuis, section 2.2.1 +/// +/// Constraint equation (eq 45): +/// +/// \f[C = p_2 - p_1\f] +/// +/// Jacobian (transposed) (eq 47): +/// +/// \f[J^T = \begin{bmatrix}-E & r1x & E & -r2x^T\end{bmatrix} +/// = \begin{bmatrix}-E^T \\ r1x^T \\ E^T \\ -r2x^T\end{bmatrix} +/// = \begin{bmatrix}-E \\ -r1x \\ E \\ r2x\end{bmatrix}\f] +/// +/// Used terms (here and below, everything in world space):\n +/// p1, p2 = constraint points.\n +/// r1 = p1 - x1.\n +/// r2 = p2 - x2.\n +/// r1x = 3x3 matrix for which r1x v = r1 x v (cross product).\n +/// x1, x2 = center of mass for the bodies.\n +/// v = [v1, w1, v2, w2].\n +/// v1, v2 = linear velocity of body 1 and 2.\n +/// w1, w2 = angular velocity of body 1 and 2.\n +/// M = mass matrix, a diagonal matrix of the mass and inertia with diagonal [m1, I1, m2, I2].\n +/// \f$K^{-1} = \left( J M^{-1} J^T \right)^{-1}\f$ = effective mass.\n +/// b = velocity bias.\n +/// \f$\beta\f$ = baumgarte constant.\n +/// E = identity matrix. +class PointConstraintPart +{ + JPH_INLINE bool ApplyVelocityStep(Body &ioBody1, Body &ioBody2, Vec3Arg inLambda) const + { + // Apply impulse if delta is not zero + if (inLambda != Vec3::sZero()) + { + // Calculate velocity change due to constraint + // + // Impulse: + // P = J^T lambda + // + // Euler velocity integration: + // v' = v + M^-1 P + if (ioBody1.IsDynamic()) + { + MotionProperties *mp1 = ioBody1.GetMotionProperties(); + mp1->SubLinearVelocityStep(mp1->GetInverseMass() * inLambda); + mp1->SubAngularVelocityStep(mInvI1_R1X * inLambda); + } + if (ioBody2.IsDynamic()) + { + MotionProperties *mp2 = ioBody2.GetMotionProperties(); + mp2->AddLinearVelocityStep(mp2->GetInverseMass() * inLambda); + mp2->AddAngularVelocityStep(mInvI2_R2X * inLambda); + } + return true; + } + + return false; + } + +public: + /// Calculate properties used during the functions below + /// @param inBody1 The first body that this constraint is attached to + /// @param inBody2 The second body that this constraint is attached to + /// @param inRotation1 The 3x3 rotation matrix for body 1 (translation part is ignored) + /// @param inRotation2 The 3x3 rotation matrix for body 2 (translation part is ignored) + /// @param inR1 Local space vector from center of mass to constraint point for body 1 + /// @param inR2 Local space vector from center of mass to constraint point for body 2 + inline void CalculateConstraintProperties(const Body &inBody1, Mat44Arg inRotation1, Vec3Arg inR1, const Body &inBody2, Mat44Arg inRotation2, Vec3Arg inR2) + { + // Positions where the point constraint acts on (middle point between center of masses) in world space + mR1 = inRotation1.Multiply3x3(inR1); + mR2 = inRotation2.Multiply3x3(inR2); + + // Calculate effective mass: K^-1 = (J M^-1 J^T)^-1 + // Using: I^-1 = R * Ibody^-1 * R^T + float summed_inv_mass; + Mat44 inv_effective_mass; + if (inBody1.IsDynamic()) + { + const MotionProperties *mp1 = inBody1.GetMotionProperties(); + Mat44 inv_i1 = mp1->GetInverseInertiaForRotation(inRotation1); + summed_inv_mass = mp1->GetInverseMass(); + + Mat44 r1x = Mat44::sCrossProduct(mR1); + mInvI1_R1X = inv_i1.Multiply3x3(r1x); + inv_effective_mass = r1x.Multiply3x3(inv_i1).Multiply3x3RightTransposed(r1x); + } + else + { + JPH_IF_DEBUG(mInvI1_R1X = Mat44::sNaN();) + + summed_inv_mass = 0.0f; + inv_effective_mass = Mat44::sZero(); + } + + if (inBody2.IsDynamic()) + { + const MotionProperties *mp2 = inBody2.GetMotionProperties(); + Mat44 inv_i2 = mp2->GetInverseInertiaForRotation(inRotation2); + summed_inv_mass += mp2->GetInverseMass(); + + Mat44 r2x = Mat44::sCrossProduct(mR2); + mInvI2_R2X = inv_i2.Multiply3x3(r2x); + inv_effective_mass += r2x.Multiply3x3(inv_i2).Multiply3x3RightTransposed(r2x); + } + else + { + JPH_IF_DEBUG(mInvI2_R2X = Mat44::sNaN();) + } + + inv_effective_mass += Mat44::sScale(summed_inv_mass); + if (!mEffectiveMass.SetInversed3x3(inv_effective_mass)) + Deactivate(); + } + + /// Deactivate this constraint + inline void Deactivate() + { + mEffectiveMass = Mat44::sZero(); + mTotalLambda = Vec3::sZero(); + } + + /// Check if constraint is active + inline bool IsActive() const + { + return mEffectiveMass(3, 3) != 0.0f; + } + + /// Must be called from the WarmStartVelocityConstraint call to apply the previous frame's impulses + /// @param ioBody1 The first body that this constraint is attached to + /// @param ioBody2 The second body that this constraint is attached to + /// @param inWarmStartImpulseRatio Ratio of new step to old time step (dt_new / dt_old) for scaling the lagrange multiplier of the previous frame + inline void WarmStart(Body &ioBody1, Body &ioBody2, float inWarmStartImpulseRatio) + { + mTotalLambda *= inWarmStartImpulseRatio; + ApplyVelocityStep(ioBody1, ioBody2, mTotalLambda); + } + + /// Iteratively update the velocity constraint. Makes sure d/dt C(...) = 0, where C is the constraint equation. + /// @param ioBody1 The first body that this constraint is attached to + /// @param ioBody2 The second body that this constraint is attached to + inline bool SolveVelocityConstraint(Body &ioBody1, Body &ioBody2) + { + // Calculate lagrange multiplier: + // + // lambda = -K^-1 (J v + b) + Vec3 lambda = mEffectiveMass * (ioBody1.GetLinearVelocity() - mR1.Cross(ioBody1.GetAngularVelocity()) - ioBody2.GetLinearVelocity() + mR2.Cross(ioBody2.GetAngularVelocity())); + mTotalLambda += lambda; // Store accumulated lambda + return ApplyVelocityStep(ioBody1, ioBody2, lambda); + } + + /// Iteratively update the position constraint. Makes sure C(...) = 0. + /// @param ioBody1 The first body that this constraint is attached to + /// @param ioBody2 The second body that this constraint is attached to + /// @param inBaumgarte Baumgarte constant (fraction of the error to correct) + inline bool SolvePositionConstraint(Body &ioBody1, Body &ioBody2, float inBaumgarte) const + { + Vec3 separation = (Vec3(ioBody2.GetCenterOfMassPosition() - ioBody1.GetCenterOfMassPosition()) + mR2 - mR1); + if (separation != Vec3::sZero()) + { + // Calculate lagrange multiplier (lambda) for Baumgarte stabilization: + // + // lambda = -K^-1 * beta / dt * C + // + // We should divide by inDeltaTime, but we should multiply by inDeltaTime in the Euler step below so they're cancelled out + Vec3 lambda = mEffectiveMass * -inBaumgarte * separation; + + // Directly integrate velocity change for one time step + // + // Euler velocity integration: + // dv = M^-1 P + // + // Impulse: + // P = J^T lambda + // + // Euler position integration: + // x' = x + dv * dt + // + // Note we don't accumulate velocities for the stabilization. This is using the approach described in 'Modeling and + // Solving Constraints' by Erin Catto presented at GDC 2007. On slide 78 it is suggested to split up the Baumgarte + // stabilization for positional drift so that it does not actually add to the momentum. We combine an Euler velocity + // integrate + a position integrate and then discard the velocity change. + if (ioBody1.IsDynamic()) + { + ioBody1.SubPositionStep(ioBody1.GetMotionProperties()->GetInverseMass() * lambda); + ioBody1.SubRotationStep(mInvI1_R1X * lambda); + } + if (ioBody2.IsDynamic()) + { + ioBody2.AddPositionStep(ioBody2.GetMotionProperties()->GetInverseMass() * lambda); + ioBody2.AddRotationStep(mInvI2_R2X * lambda); + } + + return true; + } + + return false; + } + + /// Return lagrange multiplier + Vec3 GetTotalLambda() const + { + return mTotalLambda; + } + + /// Save state of this constraint part + void SaveState(StateRecorder &inStream) const + { + inStream.Write(mTotalLambda); + } + + /// Restore state of this constraint part + void RestoreState(StateRecorder &inStream) + { + inStream.Read(mTotalLambda); + } + +private: + Vec3 mR1; + Vec3 mR2; + Mat44 mInvI1_R1X; + Mat44 mInvI2_R2X; + Mat44 mEffectiveMass; + Vec3 mTotalLambda { Vec3::sZero() }; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/RackAndPinionConstraintPart.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/RackAndPinionConstraintPart.h new file mode 100644 index 00000000000..641f4867a11 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/RackAndPinionConstraintPart.h @@ -0,0 +1,196 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +JPH_NAMESPACE_BEGIN + +/// Constraint that constrains a rotation to a translation +/// +/// Constraint equation: +/// +/// C = Theta(t) - r d(t) +/// +/// Derivative: +/// +/// d/dt C = 0 +/// <=> w1 . a - r v2 . b = 0 +/// +/// Jacobian: +/// +/// \f[J = \begin{bmatrix}0 & a^T & -r b^T & 0\end{bmatrix}\f] +/// +/// Used terms (here and below, everything in world space):\n +/// a = axis around which body 1 rotates (normalized).\n +/// b = axis along which body 2 slides (normalized).\n +/// Theta(t) = rotation around a of body 1.\n +/// d(t) = distance body 2 slides.\n +/// r = ratio between rotation and translation.\n +/// v = [v1, w1, v2, w2].\n +/// v1, v2 = linear velocity of body 1 and 2.\n +/// w1, w2 = angular velocity of body 1 and 2.\n +/// M = mass matrix, a diagonal matrix of the mass and inertia with diagonal [m1, I1, m2, I2].\n +/// \f$K^{-1} = \left( J M^{-1} J^T \right)^{-1}\f$ = effective mass.\n +/// \f$\beta\f$ = baumgarte constant. +class RackAndPinionConstraintPart +{ + /// Internal helper function to update velocities of bodies after Lagrange multiplier is calculated + JPH_INLINE bool ApplyVelocityStep(Body &ioBody1, Body &ioBody2, float inLambda) const + { + // Apply impulse if delta is not zero + if (inLambda != 0.0f) + { + // Calculate velocity change due to constraint + // + // Impulse: + // P = J^T lambda + // + // Euler velocity integration: + // v' = v + M^-1 P + ioBody1.GetMotionProperties()->AddAngularVelocityStep(inLambda * mInvI1_A); + ioBody2.GetMotionProperties()->SubLinearVelocityStep(inLambda * mRatio_InvM2_B); + return true; + } + + return false; + } + +public: + /// Calculate properties used during the functions below + /// @param inBody1 The first body that this constraint is attached to + /// @param inBody2 The second body that this constraint is attached to + /// @param inWorldSpaceHingeAxis The axis around which body 1 rotates + /// @param inWorldSpaceSliderAxis The axis along which body 2 slides + /// @param inRatio The ratio between rotation and translation + inline void CalculateConstraintProperties(const Body &inBody1, Vec3Arg inWorldSpaceHingeAxis, const Body &inBody2, Vec3Arg inWorldSpaceSliderAxis, float inRatio) + { + JPH_ASSERT(inWorldSpaceHingeAxis.IsNormalized(1.0e-4f)); + JPH_ASSERT(inWorldSpaceSliderAxis.IsNormalized(1.0e-4f)); + + // Calculate: I1^-1 a + mInvI1_A = inBody1.GetMotionProperties()->MultiplyWorldSpaceInverseInertiaByVector(inBody1.GetRotation(), inWorldSpaceHingeAxis); + + // Calculate: r/m2 b + float inv_m2 = inBody2.GetMotionProperties()->GetInverseMass(); + mRatio_InvM2_B = inRatio * inv_m2 * inWorldSpaceSliderAxis; + + // K^-1 = 1 / (J M^-1 J^T) = 1 / (a^T I1^-1 a + 1/m2 * r^2 * b . b) + float inv_effective_mass = (inWorldSpaceHingeAxis.Dot(mInvI1_A) + inv_m2 * Square(inRatio)); + if (inv_effective_mass == 0.0f) + Deactivate(); + else + mEffectiveMass = 1.0f / inv_effective_mass; + } + + /// Deactivate this constraint + inline void Deactivate() + { + mEffectiveMass = 0.0f; + mTotalLambda = 0.0f; + } + + /// Check if constraint is active + inline bool IsActive() const + { + return mEffectiveMass != 0.0f; + } + + /// Must be called from the WarmStartVelocityConstraint call to apply the previous frame's impulses + /// @param ioBody1 The first body that this constraint is attached to + /// @param ioBody2 The second body that this constraint is attached to + /// @param inWarmStartImpulseRatio Ratio of new step to old time step (dt_new / dt_old) for scaling the lagrange multiplier of the previous frame + inline void WarmStart(Body &ioBody1, Body &ioBody2, float inWarmStartImpulseRatio) + { + mTotalLambda *= inWarmStartImpulseRatio; + ApplyVelocityStep(ioBody1, ioBody2, mTotalLambda); + } + + /// Iteratively update the velocity constraint. Makes sure d/dt C(...) = 0, where C is the constraint equation. + /// @param ioBody1 The first body that this constraint is attached to + /// @param ioBody2 The second body that this constraint is attached to + /// @param inWorldSpaceHingeAxis The axis around which body 1 rotates + /// @param inWorldSpaceSliderAxis The axis along which body 2 slides + /// @param inRatio The ratio between rotation and translation + inline bool SolveVelocityConstraint(Body &ioBody1, Vec3Arg inWorldSpaceHingeAxis, Body &ioBody2, Vec3Arg inWorldSpaceSliderAxis, float inRatio) + { + // Lagrange multiplier is: + // + // lambda = -K^-1 (J v + b) + float lambda = mEffectiveMass * (inRatio * inWorldSpaceSliderAxis.Dot(ioBody2.GetLinearVelocity()) - inWorldSpaceHingeAxis.Dot(ioBody1.GetAngularVelocity())); + mTotalLambda += lambda; // Store accumulated impulse + + return ApplyVelocityStep(ioBody1, ioBody2, lambda); + } + + /// Return lagrange multiplier + float GetTotalLambda() const + { + return mTotalLambda; + } + + /// Iteratively update the position constraint. Makes sure C(...) == 0. + /// @param ioBody1 The first body that this constraint is attached to + /// @param ioBody2 The second body that this constraint is attached to + /// @param inC Value of the constraint equation (C) + /// @param inBaumgarte Baumgarte constant (fraction of the error to correct) + inline bool SolvePositionConstraint(Body &ioBody1, Body &ioBody2, float inC, float inBaumgarte) const + { + // Only apply position constraint when the constraint is hard, otherwise the velocity bias will fix the constraint + if (inC != 0.0f) + { + // Calculate lagrange multiplier (lambda) for Baumgarte stabilization: + // + // lambda = -K^-1 * beta / dt * C + // + // We should divide by inDeltaTime, but we should multiply by inDeltaTime in the Euler step below so they're cancelled out + float lambda = -mEffectiveMass * inBaumgarte * inC; + + // Directly integrate velocity change for one time step + // + // Euler velocity integration: + // dv = M^-1 P + // + // Impulse: + // P = J^T lambda + // + // Euler position integration: + // x' = x + dv * dt + // + // Note we don't accumulate velocities for the stabilization. This is using the approach described in 'Modeling and + // Solving Constraints' by Erin Catto presented at GDC 2007. On slide 78 it is suggested to split up the Baumgarte + // stabilization for positional drift so that it does not actually add to the momentum. We combine an Euler velocity + // integrate + a position integrate and then discard the velocity change. + if (ioBody1.IsDynamic()) + ioBody1.AddRotationStep(lambda * mInvI1_A); + if (ioBody2.IsDynamic()) + ioBody2.SubPositionStep(lambda * mRatio_InvM2_B); + return true; + } + + return false; + } + + /// Save state of this constraint part + void SaveState(StateRecorder &inStream) const + { + inStream.Write(mTotalLambda); + } + + /// Restore state of this constraint part + void RestoreState(StateRecorder &inStream) + { + inStream.Read(mTotalLambda); + } + +private: + Vec3 mInvI1_A; + Vec3 mRatio_InvM2_B; + float mEffectiveMass = 0.0f; + float mTotalLambda = 0.0f; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/RotationEulerConstraintPart.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/RotationEulerConstraintPart.h new file mode 100644 index 00000000000..34319e980f3 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/RotationEulerConstraintPart.h @@ -0,0 +1,270 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +JPH_NAMESPACE_BEGIN + +/// Constrains rotation around all axis so that only translation is allowed +/// +/// Based on: "Constraints Derivation for Rigid Body Simulation in 3D" - Daniel Chappuis, section 2.5.1 +/// +/// Constraint equation (eq 129): +/// +/// \f[C = \begin{bmatrix}\Delta\theta_x, \Delta\theta_y, \Delta\theta_z\end{bmatrix}\f] +/// +/// Jacobian (eq 131): +/// +/// \f[J = \begin{bmatrix}0 & -E & 0 & E\end{bmatrix}\f] +/// +/// Used terms (here and below, everything in world space):\n +/// delta_theta_* = difference in rotation between initial rotation of bodies 1 and 2.\n +/// x1, x2 = center of mass for the bodies.\n +/// v = [v1, w1, v2, w2].\n +/// v1, v2 = linear velocity of body 1 and 2.\n +/// w1, w2 = angular velocity of body 1 and 2.\n +/// M = mass matrix, a diagonal matrix of the mass and inertia with diagonal [m1, I1, m2, I2].\n +/// \f$K^{-1} = \left( J M^{-1} J^T \right)^{-1}\f$ = effective mass.\n +/// b = velocity bias.\n +/// \f$\beta\f$ = baumgarte constant.\n +/// E = identity matrix.\n +class RotationEulerConstraintPart +{ +private: + /// Internal helper function to update velocities of bodies after Lagrange multiplier is calculated + JPH_INLINE bool ApplyVelocityStep(Body &ioBody1, Body &ioBody2, Vec3Arg inLambda) const + { + // Apply impulse if delta is not zero + if (inLambda != Vec3::sZero()) + { + // Calculate velocity change due to constraint + // + // Impulse: + // P = J^T lambda + // + // Euler velocity integration: + // v' = v + M^-1 P + if (ioBody1.IsDynamic()) + ioBody1.GetMotionProperties()->SubAngularVelocityStep(mInvI1.Multiply3x3(inLambda)); + if (ioBody2.IsDynamic()) + ioBody2.GetMotionProperties()->AddAngularVelocityStep(mInvI2.Multiply3x3(inLambda)); + return true; + } + + return false; + } + +public: + /// Return inverse of initial rotation from body 1 to body 2 in body 1 space + static Quat sGetInvInitialOrientation(const Body &inBody1, const Body &inBody2) + { + // q20 = q10 r0 + // <=> r0 = q10^-1 q20 + // <=> r0^-1 = q20^-1 q10 + // + // where: + // + // q20 = initial orientation of body 2 + // q10 = initial orientation of body 1 + // r0 = initial rotation from body 1 to body 2 + return inBody2.GetRotation().Conjugated() * inBody1.GetRotation(); + } + + /// @brief Return inverse of initial rotation from body 1 to body 2 in body 1 space + /// @param inAxisX1 Reference axis X for body 1 + /// @param inAxisY1 Reference axis Y for body 1 + /// @param inAxisX2 Reference axis X for body 2 + /// @param inAxisY2 Reference axis Y for body 2 + static Quat sGetInvInitialOrientationXY(Vec3Arg inAxisX1, Vec3Arg inAxisY1, Vec3Arg inAxisX2, Vec3Arg inAxisY2) + { + // Store inverse of initial rotation from body 1 to body 2 in body 1 space: + // + // q20 = q10 r0 + // <=> r0 = q10^-1 q20 + // <=> r0^-1 = q20^-1 q10 + // + // where: + // + // q10, q20 = world space initial orientation of body 1 and 2 + // r0 = initial rotation from body 1 to body 2 in local space of body 1 + // + // We can also write this in terms of the constraint matrices: + // + // q20 c2 = q10 c1 + // <=> q20 = q10 c1 c2^-1 + // => r0 = c1 c2^-1 + // <=> r0^-1 = c2 c1^-1 + // + // where: + // + // c1, c2 = matrix that takes us from body 1 and 2 COM to constraint space 1 and 2 + if (inAxisX1 == inAxisX2 && inAxisY1 == inAxisY2) + { + // Axis are the same -> identity transform + return Quat::sIdentity(); + } + else + { + Mat44 constraint1(Vec4(inAxisX1, 0), Vec4(inAxisY1, 0), Vec4(inAxisX1.Cross(inAxisY1), 0), Vec4(0, 0, 0, 1)); + Mat44 constraint2(Vec4(inAxisX2, 0), Vec4(inAxisY2, 0), Vec4(inAxisX2.Cross(inAxisY2), 0), Vec4(0, 0, 0, 1)); + return constraint2.GetQuaternion() * constraint1.GetQuaternion().Conjugated(); + } + } + + /// @brief Return inverse of initial rotation from body 1 to body 2 in body 1 space + /// @param inAxisX1 Reference axis X for body 1 + /// @param inAxisZ1 Reference axis Z for body 1 + /// @param inAxisX2 Reference axis X for body 2 + /// @param inAxisZ2 Reference axis Z for body 2 + static Quat sGetInvInitialOrientationXZ(Vec3Arg inAxisX1, Vec3Arg inAxisZ1, Vec3Arg inAxisX2, Vec3Arg inAxisZ2) + { + // See comment at sGetInvInitialOrientationXY + if (inAxisX1 == inAxisX2 && inAxisZ1 == inAxisZ2) + { + return Quat::sIdentity(); + } + else + { + Mat44 constraint1(Vec4(inAxisX1, 0), Vec4(inAxisZ1.Cross(inAxisX1), 0), Vec4(inAxisZ1, 0), Vec4(0, 0, 0, 1)); + Mat44 constraint2(Vec4(inAxisX2, 0), Vec4(inAxisZ2.Cross(inAxisX2), 0), Vec4(inAxisZ2, 0), Vec4(0, 0, 0, 1)); + return constraint2.GetQuaternion() * constraint1.GetQuaternion().Conjugated(); + } + } + + /// Calculate properties used during the functions below + inline void CalculateConstraintProperties(const Body &inBody1, Mat44Arg inRotation1, const Body &inBody2, Mat44Arg inRotation2) + { + // Calculate properties used during constraint solving + mInvI1 = inBody1.IsDynamic()? inBody1.GetMotionProperties()->GetInverseInertiaForRotation(inRotation1) : Mat44::sZero(); + mInvI2 = inBody2.IsDynamic()? inBody2.GetMotionProperties()->GetInverseInertiaForRotation(inRotation2) : Mat44::sZero(); + + // Calculate effective mass: K^-1 = (J M^-1 J^T)^-1 + if (!mEffectiveMass.SetInversed3x3(mInvI1 + mInvI2)) + Deactivate(); + } + + /// Deactivate this constraint + inline void Deactivate() + { + mEffectiveMass = Mat44::sZero(); + mTotalLambda = Vec3::sZero(); + } + + /// Check if constraint is active + inline bool IsActive() const + { + return mEffectiveMass(3, 3) != 0.0f; + } + + /// Must be called from the WarmStartVelocityConstraint call to apply the previous frame's impulses + inline void WarmStart(Body &ioBody1, Body &ioBody2, float inWarmStartImpulseRatio) + { + mTotalLambda *= inWarmStartImpulseRatio; + ApplyVelocityStep(ioBody1, ioBody2, mTotalLambda); + } + + /// Iteratively update the velocity constraint. Makes sure d/dt C(...) = 0, where C is the constraint equation. + inline bool SolveVelocityConstraint(Body &ioBody1, Body &ioBody2) + { + // Calculate lagrange multiplier: + // + // lambda = -K^-1 (J v + b) + Vec3 lambda = mEffectiveMass.Multiply3x3(ioBody1.GetAngularVelocity() - ioBody2.GetAngularVelocity()); + mTotalLambda += lambda; + return ApplyVelocityStep(ioBody1, ioBody2, lambda); + } + + /// Iteratively update the position constraint. Makes sure C(...) = 0. + inline bool SolvePositionConstraint(Body &ioBody1, Body &ioBody2, QuatArg inInvInitialOrientation, float inBaumgarte) const + { + // Calculate difference in rotation + // + // The rotation should be: + // + // q2 = q1 r0 + // + // But because of drift the actual rotation is + // + // q2 = diff q1 r0 + // <=> diff = q2 r0^-1 q1^-1 + // + // Where: + // q1 = current rotation of body 1 + // q2 = current rotation of body 2 + // diff = error that needs to be reduced to zero + Quat diff = ioBody2.GetRotation() * inInvInitialOrientation * ioBody1.GetRotation().Conjugated(); + + // A quaternion can be seen as: + // + // q = [sin(theta / 2) * v, cos(theta/2)] + // + // Where: + // v = rotation vector + // theta = rotation angle + // + // If we assume theta is small (error is small) then sin(x) = x so an approximation of the error angles is: + Vec3 error = 2.0f * diff.EnsureWPositive().GetXYZ(); + if (error != Vec3::sZero()) + { + // Calculate lagrange multiplier (lambda) for Baumgarte stabilization: + // + // lambda = -K^-1 * beta / dt * C + // + // We should divide by inDeltaTime, but we should multiply by inDeltaTime in the Euler step below so they're cancelled out + Vec3 lambda = -inBaumgarte * mEffectiveMass * error; + + // Directly integrate velocity change for one time step + // + // Euler velocity integration: + // dv = M^-1 P + // + // Impulse: + // P = J^T lambda + // + // Euler position integration: + // x' = x + dv * dt + // + // Note we don't accumulate velocities for the stabilization. This is using the approach described in 'Modeling and + // Solving Constraints' by Erin Catto presented at GDC 2007. On slide 78 it is suggested to split up the Baumgarte + // stabilization for positional drift so that it does not actually add to the momentum. We combine an Euler velocity + // integrate + a position integrate and then discard the velocity change. + if (ioBody1.IsDynamic()) + ioBody1.SubRotationStep(mInvI1.Multiply3x3(lambda)); + if (ioBody2.IsDynamic()) + ioBody2.AddRotationStep(mInvI2.Multiply3x3(lambda)); + return true; + } + + return false; + } + + /// Return lagrange multiplier + Vec3 GetTotalLambda() const + { + return mTotalLambda; + } + + /// Save state of this constraint part + void SaveState(StateRecorder &inStream) const + { + inStream.Write(mTotalLambda); + } + + /// Restore state of this constraint part + void RestoreState(StateRecorder &inStream) + { + inStream.Read(mTotalLambda); + } + +private: + Mat44 mInvI1; + Mat44 mInvI2; + Mat44 mEffectiveMass; + Vec3 mTotalLambda { Vec3::sZero() }; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/RotationQuatConstraintPart.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/RotationQuatConstraintPart.h new file mode 100644 index 00000000000..e7fb9672d82 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/RotationQuatConstraintPart.h @@ -0,0 +1,246 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +JPH_NAMESPACE_BEGIN + +/// Quaternion based constraint that constrains rotation around all axis so that only translation is allowed. +/// +/// NOTE: This constraint part is more expensive than the RotationEulerConstraintPart and slightly more correct since +/// RotationEulerConstraintPart::SolvePositionConstraint contains an approximation. In practice the difference +/// is small, so the RotationEulerConstraintPart is probably the better choice. +/// +/// Rotation is fixed between bodies like this: +/// +/// q2 = q1 r0 +/// +/// Where: +/// q1, q2 = world space quaternions representing rotation of body 1 and 2. +/// r0 = initial rotation between bodies in local space of body 1, this can be calculated by: +/// +/// q20 = q10 r0 +/// <=> r0 = q10^* q20 +/// +/// Where: +/// q10, q20 = initial world space rotations of body 1 and 2. +/// q10^* = conjugate of quaternion q10 (which is the same as the inverse for a unit quaternion) +/// +/// We exclusively use the conjugate below: +/// +/// r0^* = q20^* q10 +/// +/// The error in the rotation is (in local space of body 1): +/// +/// q2 = q1 error r0 +/// <=> error = q1^* q2 r0^* +/// +/// The imaginary part of the quaternion represents the rotation axis * sin(angle / 2). The real part of the quaternion +/// does not add any additional information (we know the quaternion in normalized) and we're removing 3 degrees of freedom +/// so we want 3 parameters. Therefore we define the constraint equation like: +/// +/// C = A q1^* q2 r0^* = 0 +/// +/// Where (if you write a quaternion as [real-part, i-part, j-part, k-part]): +/// +/// [0, 1, 0, 0] +/// A = [0, 0, 1, 0] +/// [0, 0, 0, 1] +/// +/// or in our case since we store a quaternion like [i-part, j-part, k-part, real-part]: +/// +/// [1, 0, 0, 0] +/// A = [0, 1, 0, 0] +/// [0, 0, 1, 0] +/// +/// Time derivative: +/// +/// d/dt C = A (q1^* d/dt(q2) + d/dt(q1^*) q2) r0^* +/// = A (q1^* (1/2 W2 q2) + (1/2 W1 q1)^* q2) r0^* +/// = 1/2 A (q1^* W2 q2 + q1^* W1^* q2) r0^* +/// = 1/2 A (q1^* W2 q2 - q1^* W1 * q2) r0^* +/// = 1/2 A ML(q1^*) MR(q2 r0^*) (W2 - W1) +/// = 1/2 A ML(q1^*) MR(q2 r0^*) A^T (w2 - w1) +/// +/// Where: +/// W1 = [0, w1], W2 = [0, w2] (converting angular velocity to imaginary part of quaternion). +/// w1, w2 = angular velocity of body 1 and 2. +/// d/dt(q) = 1/2 W q (time derivative of a quaternion). +/// W^* = -W (conjugate negates angular velocity as quaternion). +/// ML(q): 4x4 matrix so that q * p = ML(q) * p, where q and p are quaternions. +/// MR(p): 4x4 matrix so that q * p = MR(p) * q, where q and p are quaternions. +/// A^T: Transpose of A. +/// +/// Jacobian: +/// +/// J = [0, -1/2 A ML(q1^*) MR(q2 r0^*) A^T, 0, 1/2 A ML(q1^*) MR(q2 r0^*) A^T] +/// = [0, -JP, 0, JP] +/// +/// Suggested reading: +/// - 3D Constraint Derivations for Impulse Solvers - Marijn Tamis +/// - Game Physics Pearls - Section 9 - Quaternion Based Constraints - Claude Lacoursiere +class RotationQuatConstraintPart +{ +private: + /// Internal helper function to update velocities of bodies after Lagrange multiplier is calculated + JPH_INLINE bool ApplyVelocityStep(Body &ioBody1, Body &ioBody2, Vec3Arg inLambda) const + { + // Apply impulse if delta is not zero + if (inLambda != Vec3::sZero()) + { + // Calculate velocity change due to constraint + // + // Impulse: + // P = J^T lambda + // + // Euler velocity integration: + // v' = v + M^-1 P + if (ioBody1.IsDynamic()) + ioBody1.GetMotionProperties()->SubAngularVelocityStep(mInvI1_JPT.Multiply3x3(inLambda)); + if (ioBody2.IsDynamic()) + ioBody2.GetMotionProperties()->AddAngularVelocityStep(mInvI2_JPT.Multiply3x3(inLambda)); + return true; + } + + return false; + } + +public: + /// Return inverse of initial rotation from body 1 to body 2 in body 1 space + static Quat sGetInvInitialOrientation(const Body &inBody1, const Body &inBody2) + { + // q20 = q10 r0 + // <=> r0 = q10^-1 q20 + // <=> r0^-1 = q20^-1 q10 + // + // where: + // + // q20 = initial orientation of body 2 + // q10 = initial orientation of body 1 + // r0 = initial rotation from body 1 to body 2 + return inBody2.GetRotation().Conjugated() * inBody1.GetRotation(); + } + + /// Calculate properties used during the functions below + inline void CalculateConstraintProperties(const Body &inBody1, Mat44Arg inRotation1, const Body &inBody2, Mat44Arg inRotation2, QuatArg inInvInitialOrientation) + { + // Calculate: JP = 1/2 A ML(q1^*) MR(q2 r0^*) A^T + Mat44 jp = (Mat44::sQuatLeftMultiply(0.5f * inBody1.GetRotation().Conjugated()) * Mat44::sQuatRightMultiply(inBody2.GetRotation() * inInvInitialOrientation)).GetRotationSafe(); + + // Calculate properties used during constraint solving + Mat44 inv_i1 = inBody1.IsDynamic()? inBody1.GetMotionProperties()->GetInverseInertiaForRotation(inRotation1) : Mat44::sZero(); + Mat44 inv_i2 = inBody2.IsDynamic()? inBody2.GetMotionProperties()->GetInverseInertiaForRotation(inRotation2) : Mat44::sZero(); + mInvI1_JPT = inv_i1.Multiply3x3RightTransposed(jp); + mInvI2_JPT = inv_i2.Multiply3x3RightTransposed(jp); + + // Calculate effective mass: K^-1 = (J M^-1 J^T)^-1 + // = (JP * I1^-1 * JP^T + JP * I2^-1 * JP^T)^-1 + // = (JP * (I1^-1 + I2^-1) * JP^T)^-1 + if (!mEffectiveMass.SetInversed3x3(jp.Multiply3x3(inv_i1 + inv_i2).Multiply3x3RightTransposed(jp))) + Deactivate(); + else + mEffectiveMass_JP = mEffectiveMass.Multiply3x3(jp); + } + + /// Deactivate this constraint + inline void Deactivate() + { + mEffectiveMass = Mat44::sZero(); + mEffectiveMass_JP = Mat44::sZero(); + mTotalLambda = Vec3::sZero(); + } + + /// Check if constraint is active + inline bool IsActive() const + { + return mEffectiveMass(3, 3) != 0.0f; + } + + /// Must be called from the WarmStartVelocityConstraint call to apply the previous frame's impulses + inline void WarmStart(Body &ioBody1, Body &ioBody2, float inWarmStartImpulseRatio) + { + mTotalLambda *= inWarmStartImpulseRatio; + ApplyVelocityStep(ioBody1, ioBody2, mTotalLambda); + } + + /// Iteratively update the velocity constraint. Makes sure d/dt C(...) = 0, where C is the constraint equation. + inline bool SolveVelocityConstraint(Body &ioBody1, Body &ioBody2) + { + // Calculate lagrange multiplier: + // + // lambda = -K^-1 (J v + b) + Vec3 lambda = mEffectiveMass_JP.Multiply3x3(ioBody1.GetAngularVelocity() - ioBody2.GetAngularVelocity()); + mTotalLambda += lambda; + return ApplyVelocityStep(ioBody1, ioBody2, lambda); + } + + /// Iteratively update the position constraint. Makes sure C(...) = 0. + inline bool SolvePositionConstraint(Body &ioBody1, Body &ioBody2, QuatArg inInvInitialOrientation, float inBaumgarte) const + { + // Calculate constraint equation + Vec3 c = (ioBody1.GetRotation().Conjugated() * ioBody2.GetRotation() * inInvInitialOrientation).GetXYZ(); + if (c != Vec3::sZero()) + { + // Calculate lagrange multiplier (lambda) for Baumgarte stabilization: + // + // lambda = -K^-1 * beta / dt * C + // + // We should divide by inDeltaTime, but we should multiply by inDeltaTime in the Euler step below so they're cancelled out + Vec3 lambda = -inBaumgarte * mEffectiveMass * c; + + // Directly integrate velocity change for one time step + // + // Euler velocity integration: + // dv = M^-1 P + // + // Impulse: + // P = J^T lambda + // + // Euler position integration: + // x' = x + dv * dt + // + // Note we don't accumulate velocities for the stabilization. This is using the approach described in 'Modeling and + // Solving Constraints' by Erin Catto presented at GDC 2007. On slide 78 it is suggested to split up the Baumgarte + // stabilization for positional drift so that it does not actually add to the momentum. We combine an Euler velocity + // integrate + a position integrate and then discard the velocity change. + if (ioBody1.IsDynamic()) + ioBody1.SubRotationStep(mInvI1_JPT.Multiply3x3(lambda)); + if (ioBody2.IsDynamic()) + ioBody2.AddRotationStep(mInvI2_JPT.Multiply3x3(lambda)); + return true; + } + + return false; + } + + /// Return lagrange multiplier + Vec3 GetTotalLambda() const + { + return mTotalLambda; + } + + /// Save state of this constraint part + void SaveState(StateRecorder &inStream) const + { + inStream.Write(mTotalLambda); + } + + /// Restore state of this constraint part + void RestoreState(StateRecorder &inStream) + { + inStream.Read(mTotalLambda); + } + +private: + Mat44 mInvI1_JPT; + Mat44 mInvI2_JPT; + Mat44 mEffectiveMass; + Mat44 mEffectiveMass_JP; + Vec3 mTotalLambda { Vec3::sZero() }; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/SpringPart.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/SpringPart.h new file mode 100644 index 00000000000..0a8a4a9730c --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/SpringPart.h @@ -0,0 +1,169 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +JPH_NAMESPACE_BEGIN +#ifndef JPH_PLATFORM_DOXYGEN // Somehow Doxygen gets confused and thinks the parameters to CalculateSpringProperties belong to this macro +JPH_MSVC_SUPPRESS_WARNING(4723) // potential divide by 0 - caused by line: outEffectiveMass = 1.0f / inInvEffectiveMass, note that JPH_NAMESPACE_BEGIN already pushes the warning state +#endif // !JPH_PLATFORM_DOXYGEN + +/// Class used in other constraint parts to calculate the required bias factor in the lagrange multiplier for creating springs +class SpringPart +{ +private: + JPH_INLINE void CalculateSpringPropertiesHelper(float inDeltaTime, float inInvEffectiveMass, float inBias, float inC, float inStiffness, float inDamping, float &outEffectiveMass) + { + // Soft constraints as per: Soft Constraints: Reinventing The Spring - Erin Catto - GDC 2011 + + // Note that the calculation of beta and gamma below are based on the solution of an implicit Euler integration scheme + // This scheme is unconditionally stable but has built in damping, so even when you set the damping ratio to 0 there will still + // be damping. See page 16 and 32. + + // Calculate softness (gamma in the slides) + // See page 34 and note that the gamma needs to be divided by delta time since we're working with impulses rather than forces: + // softness = 1 / (dt * (c + dt * k)) + // Note that the spring stiffness is k and the spring damping is c + mSoftness = 1.0f / (inDeltaTime * (inDamping + inDeltaTime * inStiffness)); + + // Calculate bias factor (baumgarte stabilization): + // beta = dt * k / (c + dt * k) = dt * k^2 * softness + // b = beta / dt * C = dt * k * softness * C + mBias = inBias + inDeltaTime * inStiffness * mSoftness * inC; + + // Update the effective mass, see post by Erin Catto: http://www.bulletphysics.org/Bullet/phpBB3/viewtopic.php?f=4&t=1354 + // + // Newton's Law: + // M * (v2 - v1) = J^T * lambda + // + // Velocity constraint with softness and Baumgarte: + // J * v2 + softness * lambda + b = 0 + // + // where b = beta * C / dt + // + // We know everything except v2 and lambda. + // + // First solve Newton's law for v2 in terms of lambda: + // + // v2 = v1 + M^-1 * J^T * lambda + // + // Substitute this expression into the velocity constraint: + // + // J * (v1 + M^-1 * J^T * lambda) + softness * lambda + b = 0 + // + // Now collect coefficients of lambda: + // + // (J * M^-1 * J^T + softness) * lambda = - J * v1 - b + // + // Now we define: + // + // K = J * M^-1 * J^T + softness + // + // So our new effective mass is K^-1 + outEffectiveMass = 1.0f / (inInvEffectiveMass + mSoftness); + } + +public: + /// Turn off the spring and set a bias only + /// + /// @param inBias Bias term (b) for the constraint impulse: lambda = J v + b + inline void CalculateSpringPropertiesWithBias(float inBias) + { + mSoftness = 0.0f; + mBias = inBias; + } + + /// Calculate spring properties based on frequency and damping ratio + /// + /// @param inDeltaTime Time step + /// @param inInvEffectiveMass Inverse effective mass K + /// @param inBias Bias term (b) for the constraint impulse: lambda = J v + b + /// @param inC Value of the constraint equation (C). Set to zero if you don't want to drive the constraint to zero with a spring. + /// @param inFrequency Oscillation frequency (Hz). Set to zero if you don't want to drive the constraint to zero with a spring. + /// @param inDamping Damping factor (0 = no damping, 1 = critical damping). Set to zero if you don't want to drive the constraint to zero with a spring. + /// @param outEffectiveMass On return, this contains the new effective mass K^-1 + inline void CalculateSpringPropertiesWithFrequencyAndDamping(float inDeltaTime, float inInvEffectiveMass, float inBias, float inC, float inFrequency, float inDamping, float &outEffectiveMass) + { + outEffectiveMass = 1.0f / inInvEffectiveMass; + + if (inFrequency > 0.0f) + { + // Calculate angular frequency + float omega = 2.0f * JPH_PI * inFrequency; + + // Calculate spring stiffness k and damping constant c (page 45) + float k = outEffectiveMass * Square(omega); + float c = 2.0f * outEffectiveMass * inDamping * omega; + + CalculateSpringPropertiesHelper(inDeltaTime, inInvEffectiveMass, inBias, inC, k, c, outEffectiveMass); + } + else + { + CalculateSpringPropertiesWithBias(inBias); + } + } + + /// Calculate spring properties with spring Stiffness (k) and damping (c), this is based on the spring equation: F = -k * x - c * v + /// + /// @param inDeltaTime Time step + /// @param inInvEffectiveMass Inverse effective mass K + /// @param inBias Bias term (b) for the constraint impulse: lambda = J v + b + /// @param inC Value of the constraint equation (C). Set to zero if you don't want to drive the constraint to zero with a spring. + /// @param inStiffness Spring stiffness k. Set to zero if you don't want to drive the constraint to zero with a spring. + /// @param inDamping Spring damping coefficient c. Set to zero if you don't want to drive the constraint to zero with a spring. + /// @param outEffectiveMass On return, this contains the new effective mass K^-1 + inline void CalculateSpringPropertiesWithStiffnessAndDamping(float inDeltaTime, float inInvEffectiveMass, float inBias, float inC, float inStiffness, float inDamping, float &outEffectiveMass) + { + if (inStiffness > 0.0f) + { + CalculateSpringPropertiesHelper(inDeltaTime, inInvEffectiveMass, inBias, inC, inStiffness, inDamping, outEffectiveMass); + } + else + { + outEffectiveMass = 1.0f / inInvEffectiveMass; + + CalculateSpringPropertiesWithBias(inBias); + } + } + + /// Returns if this spring is active + inline bool IsActive() const + { + return mSoftness != 0.0f; + } + + /// Get total bias b, including supplied bias and bias for spring: lambda = J v + b + inline float GetBias(float inTotalLambda) const + { + // Remainder of post by Erin Catto: http://www.bulletphysics.org/Bullet/phpBB3/viewtopic.php?f=4&t=1354 + // + // Each iteration we are not computing the whole impulse, we are computing an increment to the impulse and we are updating the velocity. + // Also, as we solve each constraint we get a perfect v2, but then some other constraint will come along and mess it up. + // So we want to patch up the constraint while acknowledging the accumulated impulse and the damaged velocity. + // To help with that we use P for the accumulated impulse and lambda as the update. Mathematically we have: + // + // M * (v2new - v2damaged) = J^T * lambda + // J * v2new + softness * (total_lambda + lambda) + b = 0 + // + // If we solve this we get: + // + // v2new = v2damaged + M^-1 * J^T * lambda + // J * (v2damaged + M^-1 * J^T * lambda) + softness * total_lambda + softness * lambda + b = 0 + // + // (J * M^-1 * J^T + softness) * lambda = -(J * v2damaged + softness * total_lambda + b) + // + // So our lagrange multiplier becomes: + // + // lambda = -K^-1 (J v + softness * total_lambda + b) + // + // So we return the bias: softness * total_lambda + b + return mSoftness * inTotalLambda + mBias; + } + +private: + float mBias = 0.0f; + float mSoftness = 0.0f; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/SwingTwistConstraintPart.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/SwingTwistConstraintPart.h new file mode 100644 index 00000000000..c2a47547f5e --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/SwingTwistConstraintPart.h @@ -0,0 +1,597 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +/// How the swing limit behaves +enum class ESwingType : uint8 +{ + Cone, ///< Swing is limited by a cone shape, note that this cone starts to deform for larger swing angles. Cone limits only support limits that are symmetric around 0. + Pyramid, ///< Swing is limited by a pyramid shape, note that this pyramid starts to deform for larger swing angles. +}; + +/// Quaternion based constraint that decomposes the rotation in constraint space in swing and twist: q = q_swing * q_twist +/// where q_swing.x = 0 and where q_twist.y = q_twist.z = 0 +/// +/// - Rotation around the twist (x-axis) is within [inTwistMinAngle, inTwistMaxAngle]. +/// - Rotation around the swing axis (y and z axis) are limited to an ellipsoid in quaternion space formed by the equation: +/// +/// (q_swing.y / sin(inSwingYHalfAngle / 2))^2 + (q_swing.z / sin(inSwingZHalfAngle / 2))^2 <= 1 +/// +/// Which roughly corresponds to an elliptic cone shape with major axis (inSwingYHalfAngle, inSwingZHalfAngle). +/// +/// In case inSwingYHalfAngle = 0, the rotation around Y will be constrained to 0 and the rotation around Z +/// will be constrained between [-inSwingZHalfAngle, inSwingZHalfAngle]. Vice versa if inSwingZHalfAngle = 0. +class SwingTwistConstraintPart +{ +public: + /// Override the swing type + void SetSwingType(ESwingType inSwingType) + { + mSwingType = inSwingType; + } + + /// Get the swing type for this part + ESwingType GetSwingType() const + { + return mSwingType; + } + + /// Set limits for this constraint (see description above for parameters) + void SetLimits(float inTwistMinAngle, float inTwistMaxAngle, float inSwingYMinAngle, float inSwingYMaxAngle, float inSwingZMinAngle, float inSwingZMaxAngle) + { + constexpr float cLockedAngle = DegreesToRadians(0.5f); + constexpr float cFreeAngle = DegreesToRadians(179.5f); + + // Assume sane input + JPH_ASSERT(inTwistMinAngle <= inTwistMaxAngle); + JPH_ASSERT(inSwingYMinAngle <= inSwingYMaxAngle); + JPH_ASSERT(inSwingZMinAngle <= inSwingZMaxAngle); + JPH_ASSERT(inSwingYMinAngle >= -JPH_PI && inSwingYMaxAngle <= JPH_PI); + JPH_ASSERT(inSwingZMinAngle >= -JPH_PI && inSwingZMaxAngle <= JPH_PI); + + // Calculate the sine and cosine of the half angles + Vec4 half_twist = 0.5f * Vec4(inTwistMinAngle, inTwistMaxAngle, 0, 0); + Vec4 twist_s, twist_c; + half_twist.SinCos(twist_s, twist_c); + Vec4 half_swing = 0.5f * Vec4(inSwingYMinAngle, inSwingYMaxAngle, inSwingZMinAngle, inSwingZMaxAngle); + Vec4 swing_s, swing_c; + half_swing.SinCos(swing_s, swing_c); + + // Store half angles for pyramid limit + mSwingYHalfMinAngle = half_swing.GetX(); + mSwingYHalfMaxAngle = half_swing.GetY(); + mSwingZHalfMinAngle = half_swing.GetZ(); + mSwingZHalfMaxAngle = half_swing.GetW(); + + // Store axis flags which are used at runtime to quickly decided which constraints to apply + mRotationFlags = 0; + if (inTwistMinAngle > -cLockedAngle && inTwistMaxAngle < cLockedAngle) + { + mRotationFlags |= TwistXLocked; + mSinTwistHalfMinAngle = 0.0f; + mSinTwistHalfMaxAngle = 0.0f; + mCosTwistHalfMinAngle = 1.0f; + mCosTwistHalfMaxAngle = 1.0f; + } + else if (inTwistMinAngle < -cFreeAngle && inTwistMaxAngle > cFreeAngle) + { + mRotationFlags |= TwistXFree; + mSinTwistHalfMinAngle = -1.0f; + mSinTwistHalfMaxAngle = 1.0f; + mCosTwistHalfMinAngle = 0.0f; + mCosTwistHalfMaxAngle = 0.0f; + } + else + { + mSinTwistHalfMinAngle = twist_s.GetX(); + mSinTwistHalfMaxAngle = twist_s.GetY(); + mCosTwistHalfMinAngle = twist_c.GetX(); + mCosTwistHalfMaxAngle = twist_c.GetY(); + } + + if (inSwingYMinAngle > -cLockedAngle && inSwingYMaxAngle < cLockedAngle) + { + mRotationFlags |= SwingYLocked; + mSinSwingYHalfMinAngle = 0.0f; + mSinSwingYHalfMaxAngle = 0.0f; + mCosSwingYHalfMinAngle = 1.0f; + mCosSwingYHalfMaxAngle = 1.0f; + } + else if (inSwingYMinAngle < -cFreeAngle && inSwingYMaxAngle > cFreeAngle) + { + mRotationFlags |= SwingYFree; + mSinSwingYHalfMinAngle = -1.0f; + mSinSwingYHalfMaxAngle = 1.0f; + mCosSwingYHalfMinAngle = 0.0f; + mCosSwingYHalfMaxAngle = 0.0f; + } + else + { + mSinSwingYHalfMinAngle = swing_s.GetX(); + mSinSwingYHalfMaxAngle = swing_s.GetY(); + mCosSwingYHalfMinAngle = swing_c.GetX(); + mCosSwingYHalfMaxAngle = swing_c.GetY(); + JPH_ASSERT(mSinSwingYHalfMinAngle <= mSinSwingYHalfMaxAngle); + } + + if (inSwingZMinAngle > -cLockedAngle && inSwingZMaxAngle < cLockedAngle) + { + mRotationFlags |= SwingZLocked; + mSinSwingZHalfMinAngle = 0.0f; + mSinSwingZHalfMaxAngle = 0.0f; + mCosSwingZHalfMinAngle = 1.0f; + mCosSwingZHalfMaxAngle = 1.0f; + } + else if (inSwingZMinAngle < -cFreeAngle && inSwingZMaxAngle > cFreeAngle) + { + mRotationFlags |= SwingZFree; + mSinSwingZHalfMinAngle = -1.0f; + mSinSwingZHalfMaxAngle = 1.0f; + mCosSwingZHalfMinAngle = 0.0f; + mCosSwingZHalfMaxAngle = 0.0f; + } + else + { + mSinSwingZHalfMinAngle = swing_s.GetZ(); + mSinSwingZHalfMaxAngle = swing_s.GetW(); + mCosSwingZHalfMinAngle = swing_c.GetZ(); + mCosSwingZHalfMaxAngle = swing_c.GetW(); + JPH_ASSERT(mSinSwingZHalfMinAngle <= mSinSwingZHalfMaxAngle); + } + } + + /// Flags to indicate which axis got clamped by ClampSwingTwist + static constexpr uint cClampedTwistMin = 1 << 0; + static constexpr uint cClampedTwistMax = 1 << 1; + static constexpr uint cClampedSwingYMin = 1 << 2; + static constexpr uint cClampedSwingYMax = 1 << 3; + static constexpr uint cClampedSwingZMin = 1 << 4; + static constexpr uint cClampedSwingZMax = 1 << 5; + + /// Helper function to determine if we're clamped against the min or max limit + static JPH_INLINE bool sDistanceToMinShorter(float inDeltaMin, float inDeltaMax) + { + // We're outside of the limits, get actual delta to min/max range + // Note that a swing/twist of -1 and 1 represent the same angle, so if the difference is bigger than 1, the shortest angle is the other way around (2 - difference) + // We should actually be working with angles rather than sin(angle / 2). When the difference is small the approximation is accurate, but + // when working with extreme values the calculation is off and e.g. when the limit is between 0 and 180 a value of approx -60 will clamp + // to 180 rather than 0 (you'd expect anything > -90 to go to 0). + inDeltaMin = abs(inDeltaMin); + if (inDeltaMin > 1.0f) inDeltaMin = 2.0f - inDeltaMin; + inDeltaMax = abs(inDeltaMax); + if (inDeltaMax > 1.0f) inDeltaMax = 2.0f - inDeltaMax; + return inDeltaMin < inDeltaMax; + } + + /// Clamp twist and swing against the constraint limits, returns which parts were clamped (everything assumed in constraint space) + inline void ClampSwingTwist(Quat &ioSwing, Quat &ioTwist, uint &outClampedAxis) const + { + // Start with not clamped + outClampedAxis = 0; + + // Check that swing and twist quaternions don't contain rotations around the wrong axis + JPH_ASSERT(ioSwing.GetX() == 0.0f); + JPH_ASSERT(ioTwist.GetY() == 0.0f); + JPH_ASSERT(ioTwist.GetZ() == 0.0f); + + // Ensure quaternions have w > 0 + bool negate_swing = ioSwing.GetW() < 0.0f; + if (negate_swing) + ioSwing = -ioSwing; + bool negate_twist = ioTwist.GetW() < 0.0f; + if (negate_twist) + ioTwist = -ioTwist; + + if (mRotationFlags & TwistXLocked) + { + // Twist axis is locked, clamp whenever twist is not identity + outClampedAxis |= ioTwist.GetX() != 0.0f? (cClampedTwistMin | cClampedTwistMax) : 0; + ioTwist = Quat::sIdentity(); + } + else if ((mRotationFlags & TwistXFree) == 0) + { + // Twist axis has limit, clamp whenever out of range + float delta_min = mSinTwistHalfMinAngle - ioTwist.GetX(); + float delta_max = ioTwist.GetX() - mSinTwistHalfMaxAngle; + if (delta_min > 0.0f || delta_max > 0.0f) + { + // Pick the twist that corresponds to the smallest delta + if (sDistanceToMinShorter(delta_min, delta_max)) + { + ioTwist = Quat(mSinTwistHalfMinAngle, 0, 0, mCosTwistHalfMinAngle); + outClampedAxis |= cClampedTwistMin; + } + else + { + ioTwist = Quat(mSinTwistHalfMaxAngle, 0, 0, mCosTwistHalfMaxAngle); + outClampedAxis |= cClampedTwistMax; + } + } + } + + // Clamp swing + if (mRotationFlags & SwingYLocked) + { + if (mRotationFlags & SwingZLocked) + { + // Both swing Y and Z are disabled, no degrees of freedom in swing + outClampedAxis |= ioSwing.GetY() != 0.0f? (cClampedSwingYMin | cClampedSwingYMax) : 0; + outClampedAxis |= ioSwing.GetZ() != 0.0f? (cClampedSwingZMin | cClampedSwingZMax) : 0; + ioSwing = Quat::sIdentity(); + } + else + { + // Swing Y angle disabled, only 1 degree of freedom in swing + outClampedAxis |= ioSwing.GetY() != 0.0f? (cClampedSwingYMin | cClampedSwingYMax) : 0; + float delta_min = mSinSwingZHalfMinAngle - ioSwing.GetZ(); + float delta_max = ioSwing.GetZ() - mSinSwingZHalfMaxAngle; + if (delta_min > 0.0f || delta_max > 0.0f) + { + // Pick the swing that corresponds to the smallest delta + if (sDistanceToMinShorter(delta_min, delta_max)) + { + ioSwing = Quat(0, 0, mSinSwingZHalfMinAngle, mCosSwingZHalfMinAngle); + outClampedAxis |= cClampedSwingZMin; + } + else + { + ioSwing = Quat(0, 0, mSinSwingZHalfMaxAngle, mCosSwingZHalfMaxAngle); + outClampedAxis |= cClampedSwingZMax; + } + } + else if ((outClampedAxis & cClampedSwingYMin) != 0) + { + float z = ioSwing.GetZ(); + ioSwing = Quat(0, 0, z, sqrt(1.0f - Square(z))); + } + } + } + else if (mRotationFlags & SwingZLocked) + { + // Swing Z angle disabled, only 1 degree of freedom in swing + outClampedAxis |= ioSwing.GetZ() != 0.0f? (cClampedSwingZMin | cClampedSwingZMax) : 0; + float delta_min = mSinSwingYHalfMinAngle - ioSwing.GetY(); + float delta_max = ioSwing.GetY() - mSinSwingYHalfMaxAngle; + if (delta_min > 0.0f || delta_max > 0.0f) + { + // Pick the swing that corresponds to the smallest delta + if (sDistanceToMinShorter(delta_min, delta_max)) + { + ioSwing = Quat(0, mSinSwingYHalfMinAngle, 0, mCosSwingYHalfMinAngle); + outClampedAxis |= cClampedSwingYMin; + } + else + { + ioSwing = Quat(0, mSinSwingYHalfMaxAngle, 0, mCosSwingYHalfMaxAngle); + outClampedAxis |= cClampedSwingYMax; + } + } + else if ((outClampedAxis & cClampedSwingZMin) != 0) + { + float y = ioSwing.GetY(); + ioSwing = Quat(0, y, 0, sqrt(1.0f - Square(y))); + } + } + else + { + // Two degrees of freedom + if (mSwingType == ESwingType::Cone) + { + // Use ellipse to solve limits + Ellipse ellipse(mSinSwingYHalfMaxAngle, mSinSwingZHalfMaxAngle); + Float2 point(ioSwing.GetY(), ioSwing.GetZ()); + if (!ellipse.IsInside(point)) + { + Float2 closest = ellipse.GetClosestPoint(point); + ioSwing = Quat(0, closest.x, closest.y, sqrt(max(0.0f, 1.0f - Square(closest.x) - Square(closest.y)))); + outClampedAxis |= cClampedSwingYMin | cClampedSwingYMax | cClampedSwingZMin | cClampedSwingZMax; // We're not using the flags on which side we got clamped here + } + } + else + { + // Use pyramid to solve limits + // The quaternion rotating by angle y around the Y axis then rotating by angle z around the Z axis is: + // q = Quat::sRotation(Vec3::sAxisZ(), z) * Quat::sRotation(Vec3::sAxisY(), y) + // [q.x, q.y, q.z, q.w] = [-sin(y / 2) * sin(z / 2), sin(y / 2) * cos(z / 2), cos(y / 2) * sin(z / 2), cos(y / 2) * cos(z / 2)] + // So we can calculate y / 2 = atan2(q.y, q.w) and z / 2 = atan2(q.z, q.w) + Vec4 half_angle = Vec4::sATan2(ioSwing.GetXYZW().Swizzle(), ioSwing.GetXYZW().SplatW()); + Vec4 min_half_angle(mSwingYHalfMinAngle, mSwingYHalfMinAngle, mSwingZHalfMinAngle, mSwingZHalfMinAngle); + Vec4 max_half_angle(mSwingYHalfMaxAngle, mSwingYHalfMaxAngle, mSwingZHalfMaxAngle, mSwingZHalfMaxAngle); + Vec4 clamped_half_angle = Vec4::sMin(Vec4::sMax(half_angle, min_half_angle), max_half_angle); + UVec4 unclamped = Vec4::sEquals(half_angle, clamped_half_angle); + if (!unclamped.TestAllTrue()) + { + // We now calculate the quaternion again using the formula for q above, + // but we leave out the x component in order to not introduce twist + Vec4 s, c; + clamped_half_angle.SinCos(s, c); + ioSwing = Quat(0, s.GetY() * c.GetZ(), c.GetY() * s.GetZ(), c.GetY() * c.GetZ()).Normalized(); + outClampedAxis |= cClampedSwingYMin | cClampedSwingYMax | cClampedSwingZMin | cClampedSwingZMax; // We're not using the flags on which side we got clamped here + } + } + } + + // Flip sign back + if (negate_swing) + ioSwing = -ioSwing; + if (negate_twist) + ioTwist = -ioTwist; + + JPH_ASSERT(ioSwing.IsNormalized()); + JPH_ASSERT(ioTwist.IsNormalized()); + } + + /// Calculate properties used during the functions below + /// @param inBody1 The first body that this constraint is attached to + /// @param inBody2 The second body that this constraint is attached to + /// @param inConstraintRotation The current rotation of the constraint in constraint space + /// @param inConstraintToWorld Rotates from constraint space into world space + inline void CalculateConstraintProperties(const Body &inBody1, const Body &inBody2, QuatArg inConstraintRotation, QuatArg inConstraintToWorld) + { + // Decompose into swing and twist + Quat q_swing, q_twist; + inConstraintRotation.GetSwingTwist(q_swing, q_twist); + + // Clamp against joint limits + Quat q_clamped_swing = q_swing, q_clamped_twist = q_twist; + uint clamped_axis; + ClampSwingTwist(q_clamped_swing, q_clamped_twist, clamped_axis); + + if (mRotationFlags & SwingYLocked) + { + Quat twist_to_world = inConstraintToWorld * q_swing; + mWorldSpaceSwingLimitYRotationAxis = twist_to_world.RotateAxisY(); + mWorldSpaceSwingLimitZRotationAxis = twist_to_world.RotateAxisZ(); + + if (mRotationFlags & SwingZLocked) + { + // Swing fully locked + mSwingLimitYConstraintPart.CalculateConstraintProperties(inBody1, inBody2, mWorldSpaceSwingLimitYRotationAxis); + mSwingLimitZConstraintPart.CalculateConstraintProperties(inBody1, inBody2, mWorldSpaceSwingLimitZRotationAxis); + } + else + { + // Swing only locked around Y + mSwingLimitYConstraintPart.CalculateConstraintProperties(inBody1, inBody2, mWorldSpaceSwingLimitYRotationAxis); + if ((clamped_axis & (cClampedSwingZMin | cClampedSwingZMax)) != 0) + { + if ((clamped_axis & cClampedSwingZMin) != 0) + mWorldSpaceSwingLimitZRotationAxis = -mWorldSpaceSwingLimitZRotationAxis; // Flip axis if hitting min limit because the impulse limit is going to be between [-FLT_MAX, 0] + mSwingLimitZConstraintPart.CalculateConstraintProperties(inBody1, inBody2, mWorldSpaceSwingLimitZRotationAxis); + } + else + mSwingLimitZConstraintPart.Deactivate(); + } + } + else if (mRotationFlags & SwingZLocked) + { + // Swing only locked around Z + Quat twist_to_world = inConstraintToWorld * q_swing; + mWorldSpaceSwingLimitYRotationAxis = twist_to_world.RotateAxisY(); + mWorldSpaceSwingLimitZRotationAxis = twist_to_world.RotateAxisZ(); + + if ((clamped_axis & (cClampedSwingYMin | cClampedSwingYMax)) != 0) + { + if ((clamped_axis & cClampedSwingYMin) != 0) + mWorldSpaceSwingLimitYRotationAxis = -mWorldSpaceSwingLimitYRotationAxis; // Flip axis if hitting min limit because the impulse limit is going to be between [-FLT_MAX, 0] + mSwingLimitYConstraintPart.CalculateConstraintProperties(inBody1, inBody2, mWorldSpaceSwingLimitYRotationAxis); + } + else + mSwingLimitYConstraintPart.Deactivate(); + mSwingLimitZConstraintPart.CalculateConstraintProperties(inBody1, inBody2, mWorldSpaceSwingLimitZRotationAxis); + } + else if ((mRotationFlags & SwingYZFree) != SwingYZFree) + { + // Swing has limits around Y and Z + if ((clamped_axis & (cClampedSwingYMin | cClampedSwingYMax | cClampedSwingZMin | cClampedSwingZMax)) != 0) + { + // Calculate axis of rotation from clamped swing to swing + Vec3 current = (inConstraintToWorld * q_swing).RotateAxisX(); + Vec3 desired = (inConstraintToWorld * q_clamped_swing).RotateAxisX(); + mWorldSpaceSwingLimitYRotationAxis = desired.Cross(current); + float len = mWorldSpaceSwingLimitYRotationAxis.Length(); + if (len != 0.0f) + { + mWorldSpaceSwingLimitYRotationAxis /= len; + mSwingLimitYConstraintPart.CalculateConstraintProperties(inBody1, inBody2, mWorldSpaceSwingLimitYRotationAxis); + } + else + mSwingLimitYConstraintPart.Deactivate(); + } + else + mSwingLimitYConstraintPart.Deactivate(); + mSwingLimitZConstraintPart.Deactivate(); + } + else + { + // No swing limits + mSwingLimitYConstraintPart.Deactivate(); + mSwingLimitZConstraintPart.Deactivate(); + } + + if (mRotationFlags & TwistXLocked) + { + // Twist locked, always activate constraint + mWorldSpaceTwistLimitRotationAxis = (inConstraintToWorld * q_swing).RotateAxisX(); + mTwistLimitConstraintPart.CalculateConstraintProperties(inBody1, inBody2, mWorldSpaceTwistLimitRotationAxis); + } + else if ((mRotationFlags & TwistXFree) == 0) + { + // Twist has limits + if ((clamped_axis & (cClampedTwistMin | cClampedTwistMax)) != 0) + { + mWorldSpaceTwistLimitRotationAxis = (inConstraintToWorld * q_swing).RotateAxisX(); + if ((clamped_axis & cClampedTwistMin) != 0) + mWorldSpaceTwistLimitRotationAxis = -mWorldSpaceTwistLimitRotationAxis; // Flip axis if hitting min limit because the impulse limit is going to be between [-FLT_MAX, 0] + mTwistLimitConstraintPart.CalculateConstraintProperties(inBody1, inBody2, mWorldSpaceTwistLimitRotationAxis); + } + else + mTwistLimitConstraintPart.Deactivate(); + } + else + { + // No twist limits + mTwistLimitConstraintPart.Deactivate(); + } + } + + /// Deactivate this constraint + void Deactivate() + { + mSwingLimitYConstraintPart.Deactivate(); + mSwingLimitZConstraintPart.Deactivate(); + mTwistLimitConstraintPart.Deactivate(); + } + + /// Check if constraint is active + inline bool IsActive() const + { + return mSwingLimitYConstraintPart.IsActive() || mSwingLimitZConstraintPart.IsActive() || mTwistLimitConstraintPart.IsActive(); + } + + /// Must be called from the WarmStartVelocityConstraint call to apply the previous frame's impulses + inline void WarmStart(Body &ioBody1, Body &ioBody2, float inWarmStartImpulseRatio) + { + mSwingLimitYConstraintPart.WarmStart(ioBody1, ioBody2, inWarmStartImpulseRatio); + mSwingLimitZConstraintPart.WarmStart(ioBody1, ioBody2, inWarmStartImpulseRatio); + mTwistLimitConstraintPart.WarmStart(ioBody1, ioBody2, inWarmStartImpulseRatio); + } + + /// Iteratively update the velocity constraint. Makes sure d/dt C(...) = 0, where C is the constraint equation. + inline bool SolveVelocityConstraint(Body &ioBody1, Body &ioBody2) + { + bool impulse = false; + + // Solve swing constraint + if (mSwingLimitYConstraintPart.IsActive()) + impulse |= mSwingLimitYConstraintPart.SolveVelocityConstraint(ioBody1, ioBody2, mWorldSpaceSwingLimitYRotationAxis, -FLT_MAX, mSinSwingYHalfMinAngle == mSinSwingYHalfMaxAngle? FLT_MAX : 0.0f); + + if (mSwingLimitZConstraintPart.IsActive()) + impulse |= mSwingLimitZConstraintPart.SolveVelocityConstraint(ioBody1, ioBody2, mWorldSpaceSwingLimitZRotationAxis, -FLT_MAX, mSinSwingZHalfMinAngle == mSinSwingZHalfMaxAngle? FLT_MAX : 0.0f); + + // Solve twist constraint + if (mTwistLimitConstraintPart.IsActive()) + impulse |= mTwistLimitConstraintPart.SolveVelocityConstraint(ioBody1, ioBody2, mWorldSpaceTwistLimitRotationAxis, -FLT_MAX, mSinTwistHalfMinAngle == mSinTwistHalfMaxAngle? FLT_MAX : 0.0f); + + return impulse; + } + + /// Iteratively update the position constraint. Makes sure C(...) = 0. + /// @param ioBody1 The first body that this constraint is attached to + /// @param ioBody2 The second body that this constraint is attached to + /// @param inConstraintRotation The current rotation of the constraint in constraint space + /// @param inConstraintToBody1 , inConstraintToBody2 Rotates from constraint space to body 1/2 space + /// @param inBaumgarte Baumgarte constant (fraction of the error to correct) + inline bool SolvePositionConstraint(Body &ioBody1, Body &ioBody2, QuatArg inConstraintRotation, QuatArg inConstraintToBody1, QuatArg inConstraintToBody2, float inBaumgarte) const + { + Quat q_swing, q_twist; + inConstraintRotation.GetSwingTwist(q_swing, q_twist); + + uint clamped_axis; + ClampSwingTwist(q_swing, q_twist, clamped_axis); + + // Solve rotation violations + if (clamped_axis != 0) + { + RotationEulerConstraintPart part; + Quat inv_initial_orientation = inConstraintToBody2 * (inConstraintToBody1 * q_swing * q_twist).Conjugated(); + part.CalculateConstraintProperties(ioBody1, Mat44::sRotation(ioBody1.GetRotation()), ioBody2, Mat44::sRotation(ioBody2.GetRotation())); + return part.SolvePositionConstraint(ioBody1, ioBody2, inv_initial_orientation, inBaumgarte); + } + + return false; + } + + /// Return lagrange multiplier for swing + inline float GetTotalSwingYLambda() const + { + return mSwingLimitYConstraintPart.GetTotalLambda(); + } + + inline float GetTotalSwingZLambda() const + { + return mSwingLimitZConstraintPart.GetTotalLambda(); + } + + /// Return lagrange multiplier for twist + inline float GetTotalTwistLambda() const + { + return mTwistLimitConstraintPart.GetTotalLambda(); + } + + /// Save state of this constraint part + void SaveState(StateRecorder &inStream) const + { + mSwingLimitYConstraintPart.SaveState(inStream); + mSwingLimitZConstraintPart.SaveState(inStream); + mTwistLimitConstraintPart.SaveState(inStream); + } + + /// Restore state of this constraint part + void RestoreState(StateRecorder &inStream) + { + mSwingLimitYConstraintPart.RestoreState(inStream); + mSwingLimitZConstraintPart.RestoreState(inStream); + mTwistLimitConstraintPart.RestoreState(inStream); + } + +private: + // CONFIGURATION PROPERTIES FOLLOW + + enum ERotationFlags + { + /// Indicates that axis is completely locked (cannot rotate around this axis) + TwistXLocked = 1 << 0, + SwingYLocked = 1 << 1, + SwingZLocked = 1 << 2, + + /// Indicates that axis is completely free (can rotate around without limits) + TwistXFree = 1 << 3, + SwingYFree = 1 << 4, + SwingZFree = 1 << 5, + SwingYZFree = SwingYFree | SwingZFree + }; + + uint8 mRotationFlags; + + // Constants + ESwingType mSwingType = ESwingType::Cone; + float mSinTwistHalfMinAngle; + float mSinTwistHalfMaxAngle; + float mCosTwistHalfMinAngle; + float mCosTwistHalfMaxAngle; + float mSwingYHalfMinAngle; + float mSwingYHalfMaxAngle; + float mSwingZHalfMinAngle; + float mSwingZHalfMaxAngle; + float mSinSwingYHalfMinAngle; + float mSinSwingYHalfMaxAngle; + float mSinSwingZHalfMinAngle; + float mSinSwingZHalfMaxAngle; + float mCosSwingYHalfMinAngle; + float mCosSwingYHalfMaxAngle; + float mCosSwingZHalfMinAngle; + float mCosSwingZHalfMaxAngle; + + // RUN TIME PROPERTIES FOLLOW + + /// Rotation axis for the angle constraint parts + Vec3 mWorldSpaceSwingLimitYRotationAxis; + Vec3 mWorldSpaceSwingLimitZRotationAxis; + Vec3 mWorldSpaceTwistLimitRotationAxis; + + /// The constraint parts + AngleConstraintPart mSwingLimitYConstraintPart; + AngleConstraintPart mSwingLimitZConstraintPart; + AngleConstraintPart mTwistLimitConstraintPart; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ContactConstraintManager.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ContactConstraintManager.cpp new file mode 100644 index 00000000000..f3f96888347 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ContactConstraintManager.cpp @@ -0,0 +1,1718 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef JPH_DEBUG_RENDERER + #include +#endif // JPH_DEBUG_RENDERER + +JPH_NAMESPACE_BEGIN + +using namespace literals; + +#ifdef JPH_DEBUG_RENDERER +bool ContactConstraintManager::sDrawContactPoint = false; +bool ContactConstraintManager::sDrawSupportingFaces = false; +bool ContactConstraintManager::sDrawContactPointReduction = false; +bool ContactConstraintManager::sDrawContactManifolds = false; +#endif // JPH_DEBUG_RENDERER + +//#define JPH_MANIFOLD_CACHE_DEBUG + +//////////////////////////////////////////////////////////////////////////////////////////////////////// +// ContactConstraintManager::WorldContactPoint +//////////////////////////////////////////////////////////////////////////////////////////////////////// + +void ContactConstraintManager::WorldContactPoint::CalculateNonPenetrationConstraintProperties(const Body &inBody1, float inInvMass1, float inInvInertiaScale1, const Body &inBody2, float inInvMass2, float inInvInertiaScale2, RVec3Arg inWorldSpacePosition1, RVec3Arg inWorldSpacePosition2, Vec3Arg inWorldSpaceNormal) +{ + // Calculate collision points relative to body + RVec3 p = 0.5_r * (inWorldSpacePosition1 + inWorldSpacePosition2); + Vec3 r1 = Vec3(p - inBody1.GetCenterOfMassPosition()); + Vec3 r2 = Vec3(p - inBody2.GetCenterOfMassPosition()); + + mNonPenetrationConstraint.CalculateConstraintPropertiesWithMassOverride(inBody1, inInvMass1, inInvInertiaScale1, r1, inBody2, inInvMass2, inInvInertiaScale2, r2, inWorldSpaceNormal); +} + +template +JPH_INLINE void ContactConstraintManager::WorldContactPoint::TemplatedCalculateFrictionAndNonPenetrationConstraintProperties(float inDeltaTime, const Body &inBody1, const Body &inBody2, float inInvM1, float inInvM2, Mat44Arg inInvI1, Mat44Arg inInvI2, RVec3Arg inWorldSpacePosition1, RVec3Arg inWorldSpacePosition2, Vec3Arg inWorldSpaceNormal, Vec3Arg inWorldSpaceTangent1, Vec3Arg inWorldSpaceTangent2, const ContactSettings &inSettings, float inMinVelocityForRestitution) +{ + JPH_DET_LOG("TemplatedCalculateFrictionAndNonPenetrationConstraintProperties: p1: " << inWorldSpacePosition1 << " p2: " << inWorldSpacePosition2 + << " normal: " << inWorldSpaceNormal << " tangent1: " << inWorldSpaceTangent1 << " tangent2: " << inWorldSpaceTangent2 + << " restitution: " << inSettings.mCombinedRestitution << " friction: " << inSettings.mCombinedFriction << " minv: " << inMinVelocityForRestitution + << " surface_vel: " << inSettings.mRelativeLinearSurfaceVelocity << " surface_ang: " << inSettings.mRelativeAngularSurfaceVelocity); + + // Calculate collision points relative to body + RVec3 p = 0.5_r * (inWorldSpacePosition1 + inWorldSpacePosition2); + Vec3 r1 = Vec3(p - inBody1.GetCenterOfMassPosition()); + Vec3 r2 = Vec3(p - inBody2.GetCenterOfMassPosition()); + + // Calculate velocity of collision points + Vec3 relative_velocity; + if constexpr (Type1 != EMotionType::Static && Type2 != EMotionType::Static) + relative_velocity = inBody2.GetMotionPropertiesUnchecked()->GetPointVelocityCOM(r2) - inBody1.GetMotionPropertiesUnchecked()->GetPointVelocityCOM(r1); + else if constexpr (Type1 != EMotionType::Static) + relative_velocity = -inBody1.GetMotionPropertiesUnchecked()->GetPointVelocityCOM(r1); + else if constexpr (Type2 != EMotionType::Static) + relative_velocity = inBody2.GetMotionPropertiesUnchecked()->GetPointVelocityCOM(r2); + else + { + JPH_ASSERT(false); // Static vs static makes no sense + relative_velocity = Vec3::sZero(); + } + float normal_velocity = relative_velocity.Dot(inWorldSpaceNormal); + + // How much the shapes are penetrating (> 0 if penetrating, < 0 if separated) + float penetration = Vec3(inWorldSpacePosition1 - inWorldSpacePosition2).Dot(inWorldSpaceNormal); + + // If there is no penetration, this is a speculative contact and we will apply a bias to the contact constraint + // so that the constraint becomes relative_velocity . contact normal > -penetration / delta_time + // instead of relative_velocity . contact normal > 0 + // See: GDC 2013: "Physics for Game Programmers; Continuous Collision" - Erin Catto + float speculative_contact_velocity_bias = max(0.0f, -penetration / inDeltaTime); + + // Determine if the velocity is big enough for restitution + float normal_velocity_bias; + if (inSettings.mCombinedRestitution > 0.0f && normal_velocity < -inMinVelocityForRestitution) + { + // We have a velocity that is big enough for restitution. This is where speculative contacts don't work + // great as we have to decide now if we're going to apply the restitution or not. If the relative + // velocity is big enough for a hit, we apply the restitution (in the end, due to other constraints, + // the objects may actually not collide and we will have applied restitution incorrectly). Another + // artifact that occurs because of this approximation is that the object will bounce from its current + // position rather than from a position where it is touching the other object. This causes the object + // to appear to move faster for 1 frame (the opposite of time stealing). + if (normal_velocity < -speculative_contact_velocity_bias) + normal_velocity_bias = inSettings.mCombinedRestitution * normal_velocity; + else + // In this case we have predicted that we don't hit the other object, but if we do (due to other constraints changing velocities) + // the speculative contact will prevent penetration but will not apply restitution leading to another artifact. + normal_velocity_bias = speculative_contact_velocity_bias; + } + else + { + // No restitution. We can safely apply our contact velocity bias. + normal_velocity_bias = speculative_contact_velocity_bias; + } + + mNonPenetrationConstraint.TemplatedCalculateConstraintProperties(inInvM1, inInvI1, r1, inInvM2, inInvI2, r2, inWorldSpaceNormal, normal_velocity_bias); + + // Calculate friction part + if (inSettings.mCombinedFriction > 0.0f) + { + // Get surface velocity relative to tangents + Vec3 ws_surface_velocity = inSettings.mRelativeLinearSurfaceVelocity + inSettings.mRelativeAngularSurfaceVelocity.Cross(r1); + float surface_velocity1 = inWorldSpaceTangent1.Dot(ws_surface_velocity); + float surface_velocity2 = inWorldSpaceTangent2.Dot(ws_surface_velocity); + + // Implement friction as 2 AxisContraintParts + mFrictionConstraint1.TemplatedCalculateConstraintProperties(inInvM1, inInvI1, r1, inInvM2, inInvI2, r2, inWorldSpaceTangent1, surface_velocity1); + mFrictionConstraint2.TemplatedCalculateConstraintProperties(inInvM1, inInvI1, r1, inInvM2, inInvI2, r2, inWorldSpaceTangent2, surface_velocity2); + } + else + { + // Turn off friction constraint + mFrictionConstraint1.Deactivate(); + mFrictionConstraint2.Deactivate(); + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////// +// ContactConstraintManager::ContactConstraint +//////////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifdef JPH_DEBUG_RENDERER +void ContactConstraintManager::ContactConstraint::Draw(DebugRenderer *inRenderer, ColorArg inManifoldColor) const +{ + if (mContactPoints.empty()) + return; + + // Get body transforms + RMat44 transform_body1 = mBody1->GetCenterOfMassTransform(); + RMat44 transform_body2 = mBody2->GetCenterOfMassTransform(); + + RVec3 prev_point = transform_body1 * Vec3::sLoadFloat3Unsafe(mContactPoints.back().mContactPoint->mPosition1); + for (const WorldContactPoint &wcp : mContactPoints) + { + // Test if any lambda from the previous frame was transferred + float radius = wcp.mNonPenetrationConstraint.GetTotalLambda() == 0.0f + && wcp.mFrictionConstraint1.GetTotalLambda() == 0.0f + && wcp.mFrictionConstraint2.GetTotalLambda() == 0.0f? 0.1f : 0.2f; + + RVec3 next_point = transform_body1 * Vec3::sLoadFloat3Unsafe(wcp.mContactPoint->mPosition1); + inRenderer->DrawMarker(next_point, Color::sCyan, radius); + inRenderer->DrawMarker(transform_body2 * Vec3::sLoadFloat3Unsafe(wcp.mContactPoint->mPosition2), Color::sPurple, radius); + + // Draw edge + inRenderer->DrawArrow(prev_point, next_point, inManifoldColor, 0.05f); + prev_point = next_point; + } + + // Draw normal + RVec3 wp = transform_body1 * Vec3::sLoadFloat3Unsafe(mContactPoints[0].mContactPoint->mPosition1); + inRenderer->DrawArrow(wp, wp + GetWorldSpaceNormal(), Color::sRed, 0.05f); + + // Get tangents + Vec3 t1, t2; + GetTangents(t1, t2); + + // Draw tangents + inRenderer->DrawLine(wp, wp + t1, Color::sGreen); + inRenderer->DrawLine(wp, wp + t2, Color::sBlue); +} +#endif // JPH_DEBUG_RENDERER + +//////////////////////////////////////////////////////////////////////////////////////////////////////// +// ContactConstraintManager::CachedContactPoint +//////////////////////////////////////////////////////////////////////////////////////////////////////// + +void ContactConstraintManager::CachedContactPoint::SaveState(StateRecorder &inStream) const +{ + inStream.Write(mPosition1); + inStream.Write(mPosition2); + inStream.Write(mNonPenetrationLambda); + inStream.Write(mFrictionLambda); +} + +void ContactConstraintManager::CachedContactPoint::RestoreState(StateRecorder &inStream) +{ + inStream.Read(mPosition1); + inStream.Read(mPosition2); + inStream.Read(mNonPenetrationLambda); + inStream.Read(mFrictionLambda); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////// +// ContactConstraintManager::CachedManifold +//////////////////////////////////////////////////////////////////////////////////////////////////////// + +void ContactConstraintManager::CachedManifold::SaveState(StateRecorder &inStream) const +{ + inStream.Write(mContactNormal); +} + +void ContactConstraintManager::CachedManifold::RestoreState(StateRecorder &inStream) +{ + inStream.Read(mContactNormal); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////// +// ContactConstraintManager::CachedBodyPair +//////////////////////////////////////////////////////////////////////////////////////////////////////// + +void ContactConstraintManager::CachedBodyPair::SaveState(StateRecorder &inStream) const +{ + inStream.Write(mDeltaPosition); + inStream.Write(mDeltaRotation); +} + +void ContactConstraintManager::CachedBodyPair::RestoreState(StateRecorder &inStream) +{ + inStream.Read(mDeltaPosition); + inStream.Read(mDeltaRotation); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////// +// ContactConstraintManager::ManifoldCache +//////////////////////////////////////////////////////////////////////////////////////////////////////// + +void ContactConstraintManager::ManifoldCache::Init(uint inMaxBodyPairs, uint inMaxContactConstraints, uint inCachedManifoldsSize) +{ + mAllocator.Init(inMaxBodyPairs * sizeof(BodyPairMap::KeyValue) + inCachedManifoldsSize); + mCachedManifolds.Init(GetNextPowerOf2(inMaxContactConstraints)); + mCachedBodyPairs.Init(GetNextPowerOf2(inMaxBodyPairs)); +} + +void ContactConstraintManager::ManifoldCache::Clear() +{ + JPH_PROFILE_FUNCTION(); + + mCachedManifolds.Clear(); + mCachedBodyPairs.Clear(); + mAllocator.Clear(); + +#ifdef JPH_ENABLE_ASSERTS + // Mark as incomplete + mIsFinalized = false; +#endif +} + +void ContactConstraintManager::ManifoldCache::Prepare(uint inExpectedNumBodyPairs, uint inExpectedNumManifolds) +{ + // Minimum amount of buckets to use in the hash map + constexpr uint32 cMinBuckets = 1024; + + // Use the next higher power of 2 of amount of objects in the cache from last frame to determine the amount of buckets in this frame + mCachedManifolds.SetNumBuckets(min(max(cMinBuckets, GetNextPowerOf2(inExpectedNumManifolds)), mCachedManifolds.GetMaxBuckets())); + mCachedBodyPairs.SetNumBuckets(min(max(cMinBuckets, GetNextPowerOf2(inExpectedNumBodyPairs)), mCachedBodyPairs.GetMaxBuckets())); +} + +const ContactConstraintManager::MKeyValue *ContactConstraintManager::ManifoldCache::Find(const SubShapeIDPair &inKey, uint64 inKeyHash) const +{ + JPH_ASSERT(mIsFinalized); + return mCachedManifolds.Find(inKey, inKeyHash); +} + +ContactConstraintManager::MKeyValue *ContactConstraintManager::ManifoldCache::Create(ContactAllocator &ioContactAllocator, const SubShapeIDPair &inKey, uint64 inKeyHash, int inNumContactPoints) +{ + JPH_ASSERT(!mIsFinalized); + MKeyValue *kv = mCachedManifolds.Create(ioContactAllocator, inKey, inKeyHash, CachedManifold::sGetRequiredExtraSize(inNumContactPoints)); + if (kv == nullptr) + { + ioContactAllocator.mErrors |= EPhysicsUpdateError::ManifoldCacheFull; + return nullptr; + } + kv->GetValue().mNumContactPoints = uint16(inNumContactPoints); + ++ioContactAllocator.mNumManifolds; + return kv; +} + +ContactConstraintManager::MKVAndCreated ContactConstraintManager::ManifoldCache::FindOrCreate(ContactAllocator &ioContactAllocator, const SubShapeIDPair &inKey, uint64 inKeyHash, int inNumContactPoints) +{ + MKeyValue *kv = const_cast(mCachedManifolds.Find(inKey, inKeyHash)); + if (kv != nullptr) + return { kv, false }; + + return { Create(ioContactAllocator, inKey, inKeyHash, inNumContactPoints), true }; +} + +uint32 ContactConstraintManager::ManifoldCache::ToHandle(const MKeyValue *inKeyValue) const +{ + JPH_ASSERT(!mIsFinalized); + return mCachedManifolds.ToHandle(inKeyValue); +} + +const ContactConstraintManager::MKeyValue *ContactConstraintManager::ManifoldCache::FromHandle(uint32 inHandle) const +{ + JPH_ASSERT(mIsFinalized); + return mCachedManifolds.FromHandle(inHandle); +} + +const ContactConstraintManager::BPKeyValue *ContactConstraintManager::ManifoldCache::Find(const BodyPair &inKey, uint64 inKeyHash) const +{ + JPH_ASSERT(mIsFinalized); + return mCachedBodyPairs.Find(inKey, inKeyHash); +} + +ContactConstraintManager::BPKeyValue *ContactConstraintManager::ManifoldCache::Create(ContactAllocator &ioContactAllocator, const BodyPair &inKey, uint64 inKeyHash) +{ + JPH_ASSERT(!mIsFinalized); + BPKeyValue *kv = mCachedBodyPairs.Create(ioContactAllocator, inKey, inKeyHash, 0); + if (kv == nullptr) + { + ioContactAllocator.mErrors |= EPhysicsUpdateError::BodyPairCacheFull; + return nullptr; + } + ++ioContactAllocator.mNumBodyPairs; + return kv; +} + +void ContactConstraintManager::ManifoldCache::GetAllBodyPairsSorted(Array &outAll) const +{ + JPH_ASSERT(mIsFinalized); + mCachedBodyPairs.GetAllKeyValues(outAll); + + // Sort by key + QuickSort(outAll.begin(), outAll.end(), [](const BPKeyValue *inLHS, const BPKeyValue *inRHS) { + return inLHS->GetKey() < inRHS->GetKey(); + }); +} + +void ContactConstraintManager::ManifoldCache::GetAllManifoldsSorted(const CachedBodyPair &inBodyPair, Array &outAll) const +{ + JPH_ASSERT(mIsFinalized); + + // Iterate through the attached manifolds + for (uint32 handle = inBodyPair.mFirstCachedManifold; handle != ManifoldMap::cInvalidHandle; handle = FromHandle(handle)->GetValue().mNextWithSameBodyPair) + { + const MKeyValue *kv = mCachedManifolds.FromHandle(handle); + outAll.push_back(kv); + } + + // Sort by key + QuickSort(outAll.begin(), outAll.end(), [](const MKeyValue *inLHS, const MKeyValue *inRHS) { + return inLHS->GetKey() < inRHS->GetKey(); + }); +} + +void ContactConstraintManager::ManifoldCache::GetAllCCDManifoldsSorted(Array &outAll) const +{ + mCachedManifolds.GetAllKeyValues(outAll); + + for (int i = (int)outAll.size() - 1; i >= 0; --i) + if ((outAll[i]->GetValue().mFlags & (uint16)CachedManifold::EFlags::CCDContact) == 0) + { + outAll[i] = outAll.back(); + outAll.pop_back(); + } + + // Sort by key + QuickSort(outAll.begin(), outAll.end(), [](const MKeyValue *inLHS, const MKeyValue *inRHS) { + return inLHS->GetKey() < inRHS->GetKey(); + }); +} + +void ContactConstraintManager::ManifoldCache::ContactPointRemovedCallbacks(ContactListener *inListener) +{ + JPH_PROFILE_FUNCTION(); + + for (MKeyValue &kv : mCachedManifolds) + if ((kv.GetValue().mFlags & uint16(CachedManifold::EFlags::ContactPersisted)) == 0) + inListener->OnContactRemoved(kv.GetKey()); +} + +#ifdef JPH_ENABLE_ASSERTS + +void ContactConstraintManager::ManifoldCache::Finalize() +{ + mIsFinalized = true; + +#ifdef JPH_MANIFOLD_CACHE_DEBUG + Trace("ManifoldMap:"); + mCachedManifolds.TraceStats(); + Trace("BodyPairMap:"); + mCachedBodyPairs.TraceStats(); +#endif // JPH_MANIFOLD_CACHE_DEBUG +} + +#endif + +void ContactConstraintManager::ManifoldCache::SaveState(StateRecorder &inStream, const StateRecorderFilter *inFilter) const +{ + JPH_ASSERT(mIsFinalized); + + // Get contents of cache + Array all_bp; + GetAllBodyPairsSorted(all_bp); + + // Determine which ones to save + Array selected_bp; + if (inFilter == nullptr) + selected_bp = std::move(all_bp); + else + { + selected_bp.reserve(all_bp.size()); + for (const BPKeyValue *bp_kv : all_bp) + if (inFilter->ShouldSaveContact(bp_kv->GetKey().mBodyA, bp_kv->GetKey().mBodyB)) + selected_bp.push_back(bp_kv); + } + + // Write body pairs + size_t num_body_pairs = selected_bp.size(); + inStream.Write(num_body_pairs); + for (const BPKeyValue *bp_kv : selected_bp) + { + // Write body pair key + inStream.Write(bp_kv->GetKey()); + + // Write body pair + const CachedBodyPair &bp = bp_kv->GetValue(); + bp.SaveState(inStream); + + // Get attached manifolds + Array all_m; + GetAllManifoldsSorted(bp, all_m); + + // Write num manifolds + size_t num_manifolds = all_m.size(); + inStream.Write(num_manifolds); + + // Write all manifolds + for (const MKeyValue *m_kv : all_m) + { + // Write key + inStream.Write(m_kv->GetKey()); + const CachedManifold &cm = m_kv->GetValue(); + JPH_ASSERT((cm.mFlags & (uint16)CachedManifold::EFlags::CCDContact) == 0); + + // Write amount of contacts + inStream.Write(cm.mNumContactPoints); + + // Write manifold + cm.SaveState(inStream); + + // Write contact points + for (uint32 i = 0; i < cm.mNumContactPoints; ++i) + cm.mContactPoints[i].SaveState(inStream); + } + } + + // Get CCD manifolds + Array all_m; + GetAllCCDManifoldsSorted(all_m); + + // Determine which ones to save + Array selected_m; + if (inFilter == nullptr) + selected_m = std::move(all_m); + else + { + selected_m.reserve(all_m.size()); + for (const MKeyValue *m_kv : all_m) + if (inFilter->ShouldSaveContact(m_kv->GetKey().GetBody1ID(), m_kv->GetKey().GetBody2ID())) + selected_m.push_back(m_kv); + } + + // Write all CCD manifold keys + size_t num_manifolds = selected_m.size(); + inStream.Write(num_manifolds); + for (const MKeyValue *m_kv : selected_m) + inStream.Write(m_kv->GetKey()); +} + +bool ContactConstraintManager::ManifoldCache::RestoreState(const ManifoldCache &inReadCache, StateRecorder &inStream) +{ + JPH_ASSERT(!mIsFinalized); + + bool success = true; + + // Create a contact allocator for restoring the contact cache + ContactAllocator contact_allocator(GetContactAllocator()); + + // When validating, get all existing body pairs + Array all_bp; + if (inStream.IsValidating()) + inReadCache.GetAllBodyPairsSorted(all_bp); + + // Read amount of body pairs + size_t num_body_pairs; + if (inStream.IsValidating()) + num_body_pairs = all_bp.size(); + inStream.Read(num_body_pairs); + + // Read entire cache + for (size_t i = 0; i < num_body_pairs; ++i) + { + // Read key + BodyPair body_pair_key; + if (inStream.IsValidating() && i < all_bp.size()) + body_pair_key = all_bp[i]->GetKey(); + inStream.Read(body_pair_key); + + // Create new entry for this body pair + uint64 body_pair_hash = body_pair_key.GetHash(); + BPKeyValue *bp_kv = Create(contact_allocator, body_pair_key, body_pair_hash); + if (bp_kv == nullptr) + { + // Out of cache space + success = false; + break; + } + CachedBodyPair &bp = bp_kv->GetValue(); + + // Read body pair + if (inStream.IsValidating() && i < all_bp.size()) + memcpy(&bp, &all_bp[i]->GetValue(), sizeof(CachedBodyPair)); + bp.RestoreState(inStream); + + // When validating, get all existing manifolds + Array all_m; + if (inStream.IsValidating()) + inReadCache.GetAllManifoldsSorted(all_bp[i]->GetValue(), all_m); + + // Read amount of manifolds + size_t num_manifolds; + if (inStream.IsValidating()) + num_manifolds = all_m.size(); + inStream.Read(num_manifolds); + + uint32 handle = ManifoldMap::cInvalidHandle; + for (size_t j = 0; j < num_manifolds; ++j) + { + // Read key + SubShapeIDPair sub_shape_key; + if (inStream.IsValidating() && j < all_m.size()) + sub_shape_key = all_m[j]->GetKey(); + inStream.Read(sub_shape_key); + uint64 sub_shape_key_hash = sub_shape_key.GetHash(); + + // Read amount of contact points + uint16 num_contact_points; + if (inStream.IsValidating() && j < all_m.size()) + num_contact_points = all_m[j]->GetValue().mNumContactPoints; + inStream.Read(num_contact_points); + + // Read manifold + MKeyValue *m_kv = Create(contact_allocator, sub_shape_key, sub_shape_key_hash, num_contact_points); + if (m_kv == nullptr) + { + // Out of cache space + success = false; + break; + } + CachedManifold &cm = m_kv->GetValue(); + if (inStream.IsValidating() && j < all_m.size()) + { + memcpy(&cm, &all_m[j]->GetValue(), CachedManifold::sGetRequiredTotalSize(num_contact_points)); + cm.mNumContactPoints = uint16(num_contact_points); // Restore num contact points + } + cm.RestoreState(inStream); + cm.mNextWithSameBodyPair = handle; + handle = ToHandle(m_kv); + + // Read contact points + for (uint32 k = 0; k < num_contact_points; ++k) + cm.mContactPoints[k].RestoreState(inStream); + } + bp.mFirstCachedManifold = handle; + } + + // When validating, get all existing CCD manifolds + Array all_m; + if (inStream.IsValidating()) + inReadCache.GetAllCCDManifoldsSorted(all_m); + + // Read amount of CCD manifolds + size_t num_manifolds; + if (inStream.IsValidating()) + num_manifolds = all_m.size(); + inStream.Read(num_manifolds); + + for (size_t j = 0; j < num_manifolds; ++j) + { + // Read key + SubShapeIDPair sub_shape_key; + if (inStream.IsValidating() && j < all_m.size()) + sub_shape_key = all_m[j]->GetKey(); + inStream.Read(sub_shape_key); + uint64 sub_shape_key_hash = sub_shape_key.GetHash(); + + // Create CCD manifold + MKeyValue *m_kv = Create(contact_allocator, sub_shape_key, sub_shape_key_hash, 0); + if (m_kv == nullptr) + { + // Out of cache space + success = false; + break; + } + CachedManifold &cm = m_kv->GetValue(); + cm.mFlags |= (uint16)CachedManifold::EFlags::CCDContact; + } + +#ifdef JPH_ENABLE_ASSERTS + mIsFinalized = true; +#endif + + return success; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////// +// ContactConstraintManager +//////////////////////////////////////////////////////////////////////////////////////////////////////// + +ContactConstraintManager::ContactConstraintManager(const PhysicsSettings &inPhysicsSettings) : + mPhysicsSettings(inPhysicsSettings) +{ +#ifdef JPH_ENABLE_ASSERTS + // For the first frame mark this empty buffer as finalized + mCache[mCacheWriteIdx ^ 1].Finalize(); +#endif +} + +ContactConstraintManager::~ContactConstraintManager() +{ + JPH_ASSERT(mConstraints == nullptr); +} + +void ContactConstraintManager::Init(uint inMaxBodyPairs, uint inMaxContactConstraints) +{ + mMaxConstraints = inMaxContactConstraints; + + // Calculate worst case cache usage + uint cached_manifolds_size = inMaxContactConstraints * (sizeof(CachedManifold) + (MaxContactPoints - 1) * sizeof(CachedContactPoint)); + + // Init the caches + mCache[0].Init(inMaxBodyPairs, inMaxContactConstraints, cached_manifolds_size); + mCache[1].Init(inMaxBodyPairs, inMaxContactConstraints, cached_manifolds_size); +} + +void ContactConstraintManager::PrepareConstraintBuffer(PhysicsUpdateContext *inContext) +{ + // Store context + mUpdateContext = inContext; + + // Allocate temporary constraint buffer + JPH_ASSERT(mConstraints == nullptr); + mConstraints = (ContactConstraint *)inContext->mTempAllocator->Allocate(mMaxConstraints * sizeof(ContactConstraint)); +} + +template +JPH_INLINE void ContactConstraintManager::TemplatedCalculateFrictionAndNonPenetrationConstraintProperties(ContactConstraint &ioConstraint, const ContactSettings &inSettings, float inDeltaTime, RMat44Arg inTransformBody1, RMat44Arg inTransformBody2, const Body &inBody1, const Body &inBody2) +{ + // Calculate scaled mass and inertia + Mat44 inv_i1; + if constexpr (Type1 == EMotionType::Dynamic) + { + const MotionProperties *mp1 = inBody1.GetMotionPropertiesUnchecked(); + inv_i1 = inSettings.mInvInertiaScale1 * mp1->GetInverseInertiaForRotation(inTransformBody1.GetRotation()); + } + else + { + inv_i1 = Mat44::sZero(); + } + + Mat44 inv_i2; + if constexpr (Type2 == EMotionType::Dynamic) + { + const MotionProperties *mp2 = inBody2.GetMotionPropertiesUnchecked(); + inv_i2 = inSettings.mInvInertiaScale2 * mp2->GetInverseInertiaForRotation(inTransformBody2.GetRotation()); + } + else + { + inv_i2 = Mat44::sZero(); + } + + // Calculate tangents + Vec3 t1, t2; + ioConstraint.GetTangents(t1, t2); + + Vec3 ws_normal = ioConstraint.GetWorldSpaceNormal(); + + // Setup velocity constraint properties + float min_velocity_for_restitution = mPhysicsSettings.mMinVelocityForRestitution; + for (WorldContactPoint &wcp : ioConstraint.mContactPoints) + { + RVec3 p1 = inTransformBody1 * Vec3::sLoadFloat3Unsafe(wcp.mContactPoint->mPosition1); + RVec3 p2 = inTransformBody2 * Vec3::sLoadFloat3Unsafe(wcp.mContactPoint->mPosition2); + wcp.TemplatedCalculateFrictionAndNonPenetrationConstraintProperties(inDeltaTime, inBody1, inBody2, ioConstraint.mInvMass1, ioConstraint.mInvMass2, inv_i1, inv_i2, p1, p2, ws_normal, t1, t2, inSettings, min_velocity_for_restitution); + } +} + +inline void ContactConstraintManager::CalculateFrictionAndNonPenetrationConstraintProperties(ContactConstraint &ioConstraint, const ContactSettings &inSettings, float inDeltaTime, RMat44Arg inTransformBody1, RMat44Arg inTransformBody2, const Body &inBody1, const Body &inBody2) +{ + // Dispatch to the correct templated form + switch (inBody1.GetMotionType()) + { + case EMotionType::Dynamic: + switch (inBody2.GetMotionType()) + { + case EMotionType::Dynamic: + TemplatedCalculateFrictionAndNonPenetrationConstraintProperties(ioConstraint, inSettings, inDeltaTime, inTransformBody1, inTransformBody2, inBody1, inBody2); + break; + + case EMotionType::Kinematic: + TemplatedCalculateFrictionAndNonPenetrationConstraintProperties(ioConstraint, inSettings, inDeltaTime, inTransformBody1, inTransformBody2, inBody1, inBody2); + break; + + case EMotionType::Static: + TemplatedCalculateFrictionAndNonPenetrationConstraintProperties(ioConstraint, inSettings, inDeltaTime, inTransformBody1, inTransformBody2, inBody1, inBody2); + break; + + default: + JPH_ASSERT(false); + break; + } + break; + + case EMotionType::Kinematic: + JPH_ASSERT(inBody2.IsDynamic()); + TemplatedCalculateFrictionAndNonPenetrationConstraintProperties(ioConstraint, inSettings, inDeltaTime, inTransformBody1, inTransformBody2, inBody1, inBody2); + break; + + case EMotionType::Static: + JPH_ASSERT(inBody2.IsDynamic()); + TemplatedCalculateFrictionAndNonPenetrationConstraintProperties(ioConstraint, inSettings, inDeltaTime, inTransformBody1, inTransformBody2, inBody1, inBody2); + break; + + default: + JPH_ASSERT(false); + break; + } +} + +void ContactConstraintManager::GetContactsFromCache(ContactAllocator &ioContactAllocator, Body &inBody1, Body &inBody2, bool &outPairHandled, bool &outConstraintCreated) +{ + JPH_PROFILE_FUNCTION(); + + // Start with nothing found and not handled + outConstraintCreated = false; + outPairHandled = false; + + // Swap bodies so that body 1 id < body 2 id + Body *body1, *body2; + if (inBody1.GetID() < inBody2.GetID()) + { + body1 = &inBody1; + body2 = &inBody2; + } + else + { + body1 = &inBody2; + body2 = &inBody1; + } + + // Find the cached body pair + BodyPair body_pair_key(body1->GetID(), body2->GetID()); + uint64 body_pair_hash = body_pair_key.GetHash(); + const ManifoldCache &read_cache = mCache[mCacheWriteIdx ^ 1]; + const BPKeyValue *kv = read_cache.Find(body_pair_key, body_pair_hash); + if (kv == nullptr) + return; + const CachedBodyPair &input_cbp = kv->GetValue(); + + // Get relative translation + Quat inv_r1 = body1->GetRotation().Conjugated(); + Vec3 delta_position = inv_r1 * Vec3(body2->GetCenterOfMassPosition() - body1->GetCenterOfMassPosition()); + + // Get old position delta + Vec3 old_delta_position = Vec3::sLoadFloat3Unsafe(input_cbp.mDeltaPosition); + + // Check if bodies are still roughly in the same relative position + if ((delta_position - old_delta_position).LengthSq() > mPhysicsSettings.mBodyPairCacheMaxDeltaPositionSq) + return; + + // Determine relative orientation + Quat delta_rotation = inv_r1 * body2->GetRotation(); + + // Reconstruct old quaternion delta + Quat old_delta_rotation = Quat::sLoadFloat3Unsafe(input_cbp.mDeltaRotation); + + // Check if bodies are still roughly in the same relative orientation + // The delta between 2 quaternions p and q is: p q^* = [rotation_axis * sin(angle / 2), cos(angle / 2)] + // From the W component we can extract the angle: cos(angle / 2) = px * qx + py * qy + pz * qz + pw * qw = p . q + // Since we want to abort if the rotation is smaller than -angle or bigger than angle, we can write the comparison as |p . q| < cos(angle / 2) + if (abs(delta_rotation.Dot(old_delta_rotation)) < mPhysicsSettings.mBodyPairCacheCosMaxDeltaRotationDiv2) + return; + + // The cache is valid, return that we've handled this body pair + outPairHandled = true; + + // Copy the cached body pair to this frame + ManifoldCache &write_cache = mCache[mCacheWriteIdx]; + BPKeyValue *output_bp_kv = write_cache.Create(ioContactAllocator, body_pair_key, body_pair_hash); + if (output_bp_kv == nullptr) + return; // Out of cache space + CachedBodyPair *output_cbp = &output_bp_kv->GetValue(); + memcpy(output_cbp, &input_cbp, sizeof(CachedBodyPair)); + + // If there were no contacts, we have handled the contact + if (input_cbp.mFirstCachedManifold == ManifoldMap::cInvalidHandle) + return; + + // Get body transforms + RMat44 transform_body1 = body1->GetCenterOfMassTransform(); + RMat44 transform_body2 = body2->GetCenterOfMassTransform(); + + // Get time step + float delta_time = mUpdateContext->mStepDeltaTime; + + // Copy manifolds + uint32 output_handle = ManifoldMap::cInvalidHandle; + uint32 input_handle = input_cbp.mFirstCachedManifold; + do + { + JPH_PROFILE("Add Constraint From Cached Manifold"); + + // Find the existing manifold + const MKeyValue *input_kv = read_cache.FromHandle(input_handle); + const SubShapeIDPair &input_key = input_kv->GetKey(); + const CachedManifold &input_cm = input_kv->GetValue(); + JPH_ASSERT(input_cm.mNumContactPoints > 0); // There should be contact points in this manifold! + + // Create room for manifold in write buffer and copy data + uint64 input_hash = input_key.GetHash(); + MKeyValue *output_kv = write_cache.Create(ioContactAllocator, input_key, input_hash, input_cm.mNumContactPoints); + if (output_kv == nullptr) + break; // Out of cache space + CachedManifold *output_cm = &output_kv->GetValue(); + memcpy(output_cm, &input_cm, CachedManifold::sGetRequiredTotalSize(input_cm.mNumContactPoints)); + + // Link the object under the body pairs + output_cm->mNextWithSameBodyPair = output_handle; + output_handle = write_cache.ToHandle(output_kv); + + // Calculate default contact settings + ContactSettings settings; + settings.mCombinedFriction = mCombineFriction(*body1, input_key.GetSubShapeID1(), *body2, input_key.GetSubShapeID2()); + settings.mCombinedRestitution = mCombineRestitution(*body1, input_key.GetSubShapeID1(), *body2, input_key.GetSubShapeID2()); + settings.mIsSensor = body1->IsSensor() || body2->IsSensor(); + + // Calculate world space contact normal + Vec3 world_space_normal = transform_body2.Multiply3x3(Vec3::sLoadFloat3Unsafe(output_cm->mContactNormal)).Normalized(); + + // Call contact listener to update settings + if (mContactListener != nullptr) + { + // Convert constraint to manifold structure for callback + ContactManifold manifold; + manifold.mWorldSpaceNormal = world_space_normal; + manifold.mSubShapeID1 = input_key.GetSubShapeID1(); + manifold.mSubShapeID2 = input_key.GetSubShapeID2(); + manifold.mBaseOffset = transform_body1.GetTranslation(); + manifold.mRelativeContactPointsOn1.resize(output_cm->mNumContactPoints); + manifold.mRelativeContactPointsOn2.resize(output_cm->mNumContactPoints); + Mat44 local_transform_body2 = transform_body2.PostTranslated(-manifold.mBaseOffset).ToMat44(); + float penetration_depth = -FLT_MAX; + for (uint32 i = 0; i < output_cm->mNumContactPoints; ++i) + { + const CachedContactPoint &ccp = output_cm->mContactPoints[i]; + manifold.mRelativeContactPointsOn1[i] = transform_body1.Multiply3x3(Vec3::sLoadFloat3Unsafe(ccp.mPosition1)); + manifold.mRelativeContactPointsOn2[i] = local_transform_body2 * Vec3::sLoadFloat3Unsafe(ccp.mPosition2); + penetration_depth = max(penetration_depth, (manifold.mRelativeContactPointsOn1[0] - manifold.mRelativeContactPointsOn2[0]).Dot(world_space_normal)); + } + manifold.mPenetrationDepth = penetration_depth; // We don't have the penetration depth anymore, estimate it + + // Notify callback + mContactListener->OnContactPersisted(*body1, *body2, manifold, settings); + } + + JPH_ASSERT(settings.mIsSensor || !(body1->IsSensor() || body2->IsSensor()), "Sensors cannot be converted into regular bodies by a contact callback!"); + if (!settings.mIsSensor // If one of the bodies is a sensor, don't actually create the constraint + && ((body1->IsDynamic() && settings.mInvMassScale1 != 0.0f) // One of the bodies must have mass to be able to create a contact constraint + || (body2->IsDynamic() && settings.mInvMassScale2 != 0.0f))) + { + // Add contact constraint in world space for the solver + uint32 constraint_idx = mNumConstraints++; + if (constraint_idx >= mMaxConstraints) + { + ioContactAllocator.mErrors |= EPhysicsUpdateError::ContactConstraintsFull; + break; + } + + // A constraint will be created + outConstraintCreated = true; + + ContactConstraint &constraint = mConstraints[constraint_idx]; + new (&constraint) ContactConstraint(); + constraint.mBody1 = body1; + constraint.mBody2 = body2; + constraint.mSortKey = input_hash; + world_space_normal.StoreFloat3(&constraint.mWorldSpaceNormal); + constraint.mCombinedFriction = settings.mCombinedFriction; + constraint.mInvMass1 = body1->GetMotionPropertiesUnchecked() != nullptr? settings.mInvMassScale1 * body1->GetMotionPropertiesUnchecked()->GetInverseMassUnchecked() : 0.0f; + constraint.mInvInertiaScale1 = settings.mInvInertiaScale1; + constraint.mInvMass2 = body2->GetMotionPropertiesUnchecked() != nullptr? settings.mInvMassScale2 * body2->GetMotionPropertiesUnchecked()->GetInverseMassUnchecked() : 0.0f; + constraint.mInvInertiaScale2 = settings.mInvInertiaScale2; + constraint.mContactPoints.resize(output_cm->mNumContactPoints); + for (uint32 i = 0; i < output_cm->mNumContactPoints; ++i) + { + CachedContactPoint &ccp = output_cm->mContactPoints[i]; + WorldContactPoint &wcp = constraint.mContactPoints[i]; + wcp.mNonPenetrationConstraint.SetTotalLambda(ccp.mNonPenetrationLambda); + wcp.mFrictionConstraint1.SetTotalLambda(ccp.mFrictionLambda[0]); + wcp.mFrictionConstraint2.SetTotalLambda(ccp.mFrictionLambda[1]); + wcp.mContactPoint = &ccp; + } + + JPH_DET_LOG("GetContactsFromCache: id1: " << constraint.mBody1->GetID() << " id2: " << constraint.mBody2->GetID() << " key: " << constraint.mSortKey); + + // Calculate friction and non-penetration constraint properties for all contact points + CalculateFrictionAndNonPenetrationConstraintProperties(constraint, settings, delta_time, transform_body1, transform_body2, *body1, *body2); + + // Notify island builder + mUpdateContext->mIslandBuilder->LinkContact(constraint_idx, body1->GetIndexInActiveBodiesInternal(), body2->GetIndexInActiveBodiesInternal()); + + #ifdef JPH_DEBUG_RENDERER + // Draw the manifold + if (sDrawContactManifolds) + constraint.Draw(DebugRenderer::sInstance, Color::sYellow); + #endif // JPH_DEBUG_RENDERER + } + + // Mark contact as persisted so that we won't fire OnContactRemoved callbacks + input_cm.mFlags |= (uint16)CachedManifold::EFlags::ContactPersisted; + + // Fetch the next manifold + input_handle = input_cm.mNextWithSameBodyPair; + } + while (input_handle != ManifoldMap::cInvalidHandle); + output_cbp->mFirstCachedManifold = output_handle; +} + +ContactConstraintManager::BodyPairHandle ContactConstraintManager::AddBodyPair(ContactAllocator &ioContactAllocator, const Body &inBody1, const Body &inBody2) +{ + JPH_PROFILE_FUNCTION(); + + // Swap bodies so that body 1 id < body 2 id + const Body *body1, *body2; + if (inBody1.GetID() < inBody2.GetID()) + { + body1 = &inBody1; + body2 = &inBody2; + } + else + { + body1 = &inBody2; + body2 = &inBody1; + } + + // Add an entry + BodyPair body_pair_key(body1->GetID(), body2->GetID()); + uint64 body_pair_hash = body_pair_key.GetHash(); + BPKeyValue *body_pair_kv = mCache[mCacheWriteIdx].Create(ioContactAllocator, body_pair_key, body_pair_hash); + if (body_pair_kv == nullptr) + return nullptr; // Out of cache space + CachedBodyPair *cbp = &body_pair_kv->GetValue(); + cbp->mFirstCachedManifold = ManifoldMap::cInvalidHandle; + + // Get relative translation + Quat inv_r1 = body1->GetRotation().Conjugated(); + Vec3 delta_position = inv_r1 * Vec3(body2->GetCenterOfMassPosition() - body1->GetCenterOfMassPosition()); + + // Store it + delta_position.StoreFloat3(&cbp->mDeltaPosition); + + // Determine relative orientation + Quat delta_rotation = inv_r1 * body2->GetRotation(); + + // Store it + delta_rotation.StoreFloat3(&cbp->mDeltaRotation); + + return cbp; +} + +template +bool ContactConstraintManager::TemplatedAddContactConstraint(ContactAllocator &ioContactAllocator, BodyPairHandle inBodyPairHandle, Body &inBody1, Body &inBody2, const ContactManifold &inManifold) +{ + // Calculate hash + SubShapeIDPair key { inBody1.GetID(), inManifold.mSubShapeID1, inBody2.GetID(), inManifold.mSubShapeID2 }; + uint64 key_hash = key.GetHash(); + + // Determine number of contact points + int num_contact_points = (int)inManifold.mRelativeContactPointsOn1.size(); + JPH_ASSERT(num_contact_points <= MaxContactPoints); + JPH_ASSERT(num_contact_points == (int)inManifold.mRelativeContactPointsOn2.size()); + + // Reserve space for new contact cache entry + // Note that for dynamic vs dynamic we always require the first body to have a lower body id to get a consistent key + // under which to look up the contact + ManifoldCache &write_cache = mCache[mCacheWriteIdx]; + MKeyValue *new_manifold_kv = write_cache.Create(ioContactAllocator, key, key_hash, num_contact_points); + if (new_manifold_kv == nullptr) + return false; // Out of cache space + CachedManifold *new_manifold = &new_manifold_kv->GetValue(); + + // Transform the world space normal to the space of body 2 (this is usually the static body) + RMat44 inverse_transform_body2 = inBody2.GetInverseCenterOfMassTransform(); + inverse_transform_body2.Multiply3x3(inManifold.mWorldSpaceNormal).Normalized().StoreFloat3(&new_manifold->mContactNormal); + + // Settings object that gets passed to the callback + ContactSettings settings; + settings.mCombinedFriction = mCombineFriction(inBody1, inManifold.mSubShapeID1, inBody2, inManifold.mSubShapeID2); + settings.mCombinedRestitution = mCombineRestitution(inBody1, inManifold.mSubShapeID1, inBody2, inManifold.mSubShapeID2); + settings.mIsSensor = inBody1.IsSensor() || inBody2.IsSensor(); + + // Get the contact points for the old cache entry + const ManifoldCache &read_cache = mCache[mCacheWriteIdx ^ 1]; + const MKeyValue *old_manifold_kv = read_cache.Find(key, key_hash); + const CachedContactPoint *ccp_start; + const CachedContactPoint *ccp_end; + if (old_manifold_kv != nullptr) + { + // Call point persisted listener + if (mContactListener != nullptr) + mContactListener->OnContactPersisted(inBody1, inBody2, inManifold, settings); + + // Fetch the contact points from the old manifold + const CachedManifold *old_manifold = &old_manifold_kv->GetValue(); + ccp_start = old_manifold->mContactPoints; + ccp_end = ccp_start + old_manifold->mNumContactPoints; + + // Mark contact as persisted so that we won't fire OnContactRemoved callbacks + old_manifold->mFlags |= (uint16)CachedManifold::EFlags::ContactPersisted; + } + else + { + // Call point added listener + if (mContactListener != nullptr) + mContactListener->OnContactAdded(inBody1, inBody2, inManifold, settings); + + // No contact points available from old manifold + ccp_start = nullptr; + ccp_end = nullptr; + } + + // Get inverse transform for body 1 + RMat44 inverse_transform_body1 = inBody1.GetInverseCenterOfMassTransform(); + + bool contact_constraint_created = false; + + // If one of the bodies is a sensor, don't actually create the constraint + JPH_ASSERT(settings.mIsSensor || !(inBody1.IsSensor() || inBody2.IsSensor()), "Sensors cannot be converted into regular bodies by a contact callback!"); + if (!settings.mIsSensor + && ((inBody1.IsDynamic() && settings.mInvMassScale1 != 0.0f) // One of the bodies must have mass to be able to create a contact constraint + || (inBody2.IsDynamic() && settings.mInvMassScale2 != 0.0f))) + { + // Add contact constraint + uint32 constraint_idx = mNumConstraints++; + if (constraint_idx >= mMaxConstraints) + { + ioContactAllocator.mErrors |= EPhysicsUpdateError::ContactConstraintsFull; + + // Manifold has been created already, we're not filling it in, so we need to reset the contact number of points. + // Note that we don't hook it up to the body pair cache so that it won't be used as a cache during the next simulation. + new_manifold->mNumContactPoints = 0; + return false; + } + + // We will create a contact constraint + contact_constraint_created = true; + + ContactConstraint &constraint = mConstraints[constraint_idx]; + new (&constraint) ContactConstraint(); + constraint.mBody1 = &inBody1; + constraint.mBody2 = &inBody2; + constraint.mSortKey = key_hash; + inManifold.mWorldSpaceNormal.StoreFloat3(&constraint.mWorldSpaceNormal); + constraint.mCombinedFriction = settings.mCombinedFriction; + constraint.mInvMass1 = inBody1.GetMotionPropertiesUnchecked() != nullptr? settings.mInvMassScale1 * inBody1.GetMotionPropertiesUnchecked()->GetInverseMassUnchecked() : 0.0f; + constraint.mInvInertiaScale1 = settings.mInvInertiaScale1; + constraint.mInvMass2 = inBody2.GetMotionPropertiesUnchecked() != nullptr? settings.mInvMassScale2 * inBody2.GetMotionPropertiesUnchecked()->GetInverseMassUnchecked() : 0.0f; + constraint.mInvInertiaScale2 = settings.mInvInertiaScale2; + + JPH_DET_LOG("TemplatedAddContactConstraint: id1: " << constraint.mBody1->GetID() << " id2: " << constraint.mBody2->GetID() << " key: " << constraint.mSortKey); + + // Notify island builder + mUpdateContext->mIslandBuilder->LinkContact(constraint_idx, inBody1.GetIndexInActiveBodiesInternal(), inBody2.GetIndexInActiveBodiesInternal()); + + // Get time step + float delta_time = mUpdateContext->mStepDeltaTime; + + // Calculate scaled mass and inertia + float inv_m1; + Mat44 inv_i1; + if constexpr (Type1 == EMotionType::Dynamic) + { + const MotionProperties *mp1 = inBody1.GetMotionPropertiesUnchecked(); + inv_m1 = settings.mInvMassScale1 * mp1->GetInverseMass(); + inv_i1 = settings.mInvInertiaScale1 * mp1->GetInverseInertiaForRotation(inverse_transform_body1.Transposed3x3()); + } + else + { + inv_m1 = 0.0f; + inv_i1 = Mat44::sZero(); + } + + float inv_m2; + Mat44 inv_i2; + if constexpr (Type2 == EMotionType::Dynamic) + { + const MotionProperties *mp2 = inBody2.GetMotionPropertiesUnchecked(); + inv_m2 = settings.mInvMassScale2 * mp2->GetInverseMass(); + inv_i2 = settings.mInvInertiaScale2 * mp2->GetInverseInertiaForRotation(inverse_transform_body2.Transposed3x3()); + } + else + { + inv_m2 = 0.0f; + inv_i2 = Mat44::sZero(); + } + + // Calculate tangents + Vec3 t1, t2; + constraint.GetTangents(t1, t2); + + constraint.mContactPoints.resize(num_contact_points); + for (int i = 0; i < num_contact_points; ++i) + { + // Convert to world space and set positions + WorldContactPoint &wcp = constraint.mContactPoints[i]; + RVec3 p1_ws = inManifold.mBaseOffset + inManifold.mRelativeContactPointsOn1[i]; + RVec3 p2_ws = inManifold.mBaseOffset + inManifold.mRelativeContactPointsOn2[i]; + + // Convert to local space to the body + Vec3 p1_ls = Vec3(inverse_transform_body1 * p1_ws); + Vec3 p2_ls = Vec3(inverse_transform_body2 * p2_ws); + + // Check if we have a close contact point from last update + bool lambda_set = false; + for (const CachedContactPoint *ccp = ccp_start; ccp < ccp_end; ccp++) + if (Vec3::sLoadFloat3Unsafe(ccp->mPosition1).IsClose(p1_ls, mPhysicsSettings.mContactPointPreserveLambdaMaxDistSq) + && Vec3::sLoadFloat3Unsafe(ccp->mPosition2).IsClose(p2_ls, mPhysicsSettings.mContactPointPreserveLambdaMaxDistSq)) + { + // Get lambdas from previous frame + wcp.mNonPenetrationConstraint.SetTotalLambda(ccp->mNonPenetrationLambda); + wcp.mFrictionConstraint1.SetTotalLambda(ccp->mFrictionLambda[0]); + wcp.mFrictionConstraint2.SetTotalLambda(ccp->mFrictionLambda[1]); + lambda_set = true; + break; + } + if (!lambda_set) + { + wcp.mNonPenetrationConstraint.SetTotalLambda(0.0f); + wcp.mFrictionConstraint1.SetTotalLambda(0.0f); + wcp.mFrictionConstraint2.SetTotalLambda(0.0f); + } + + // Create new contact point + CachedContactPoint &cp = new_manifold->mContactPoints[i]; + p1_ls.StoreFloat3(&cp.mPosition1); + p2_ls.StoreFloat3(&cp.mPosition2); + wcp.mContactPoint = &cp; + + // Setup velocity constraint + wcp.TemplatedCalculateFrictionAndNonPenetrationConstraintProperties(delta_time, inBody1, inBody2, inv_m1, inv_m2, inv_i1, inv_i2, p1_ws, p2_ws, inManifold.mWorldSpaceNormal, t1, t2, settings, mPhysicsSettings.mMinVelocityForRestitution); + } + + #ifdef JPH_DEBUG_RENDERER + // Draw the manifold + if (sDrawContactManifolds) + constraint.Draw(DebugRenderer::sInstance, Color::sOrange); + #endif // JPH_DEBUG_RENDERER + } + else + { + // Store the contact manifold in the cache + for (int i = 0; i < num_contact_points; ++i) + { + // Convert to local space to the body + Vec3 p1 = Vec3(inverse_transform_body1 * (inManifold.mBaseOffset + inManifold.mRelativeContactPointsOn1[i])); + Vec3 p2 = Vec3(inverse_transform_body2 * (inManifold.mBaseOffset + inManifold.mRelativeContactPointsOn2[i])); + + // Create new contact point + CachedContactPoint &cp = new_manifold->mContactPoints[i]; + p1.StoreFloat3(&cp.mPosition1); + p2.StoreFloat3(&cp.mPosition2); + + // Reset contact impulses, we haven't applied any + cp.mNonPenetrationLambda = 0.0f; + cp.mFrictionLambda[0] = 0.0f; + cp.mFrictionLambda[1] = 0.0f; + } + } + + // Store cached contact point in body pair cache + CachedBodyPair *cbp = reinterpret_cast(inBodyPairHandle); + new_manifold->mNextWithSameBodyPair = cbp->mFirstCachedManifold; + cbp->mFirstCachedManifold = write_cache.ToHandle(new_manifold_kv); + + // A contact constraint was added + return contact_constraint_created; +} + +bool ContactConstraintManager::AddContactConstraint(ContactAllocator &ioContactAllocator, BodyPairHandle inBodyPairHandle, Body &inBody1, Body &inBody2, const ContactManifold &inManifold) +{ + JPH_PROFILE_FUNCTION(); + + JPH_DET_LOG("AddContactConstraint: id1: " << inBody1.GetID() << " id2: " << inBody2.GetID() + << " subshape1: " << inManifold.mSubShapeID1 << " subshape2: " << inManifold.mSubShapeID2 + << " normal: " << inManifold.mWorldSpaceNormal << " pendepth: " << inManifold.mPenetrationDepth); + + JPH_ASSERT(inManifold.mWorldSpaceNormal.IsNormalized()); + + // Swap bodies so that body 1 id < body 2 id + const ContactManifold *manifold; + Body *body1, *body2; + ContactManifold temp; + if (inBody2.GetID() < inBody1.GetID()) + { + body1 = &inBody2; + body2 = &inBody1; + temp = inManifold.SwapShapes(); + manifold = &temp; + } + else + { + body1 = &inBody1; + body2 = &inBody2; + manifold = &inManifold; + } + + // Dispatch to the correct templated form + // Note: Non-dynamic vs non-dynamic can happen in this case due to one body being a sensor, so we need to have an extended switch case here + switch (body1->GetMotionType()) + { + case EMotionType::Dynamic: + { + switch (body2->GetMotionType()) + { + case EMotionType::Dynamic: + return TemplatedAddContactConstraint(ioContactAllocator, inBodyPairHandle, *body1, *body2, *manifold); + + case EMotionType::Kinematic: + return TemplatedAddContactConstraint(ioContactAllocator, inBodyPairHandle, *body1, *body2, *manifold); + + case EMotionType::Static: + return TemplatedAddContactConstraint(ioContactAllocator, inBodyPairHandle, *body1, *body2, *manifold); + + default: + JPH_ASSERT(false); + break; + } + break; + } + + case EMotionType::Kinematic: + switch (body2->GetMotionType()) + { + case EMotionType::Dynamic: + return TemplatedAddContactConstraint(ioContactAllocator, inBodyPairHandle, *body1, *body2, *manifold); + + case EMotionType::Kinematic: + return TemplatedAddContactConstraint(ioContactAllocator, inBodyPairHandle, *body1, *body2, *manifold); + + case EMotionType::Static: + return TemplatedAddContactConstraint(ioContactAllocator, inBodyPairHandle, *body1, *body2, *manifold); + + default: + JPH_ASSERT(false); + break; + } + break; + + case EMotionType::Static: + switch (body2->GetMotionType()) + { + case EMotionType::Dynamic: + return TemplatedAddContactConstraint(ioContactAllocator, inBodyPairHandle, *body1, *body2, *manifold); + + case EMotionType::Kinematic: + return TemplatedAddContactConstraint(ioContactAllocator, inBodyPairHandle, *body1, *body2, *manifold); + + case EMotionType::Static: // Static vs static not possible + default: + JPH_ASSERT(false); + break; + } + break; + + default: + JPH_ASSERT(false); + break; + } + + return false; +} + +void ContactConstraintManager::OnCCDContactAdded(ContactAllocator &ioContactAllocator, const Body &inBody1, const Body &inBody2, const ContactManifold &inManifold, ContactSettings &outSettings) +{ + JPH_ASSERT(inManifold.mWorldSpaceNormal.IsNormalized()); + + // Calculate contact settings + outSettings.mCombinedFriction = mCombineFriction(inBody1, inManifold.mSubShapeID1, inBody2, inManifold.mSubShapeID2); + outSettings.mCombinedRestitution = mCombineRestitution(inBody1, inManifold.mSubShapeID1, inBody2, inManifold.mSubShapeID2); + outSettings.mIsSensor = false; // For now, no sensors are supported during CCD + + // The remainder of this function only deals with calling contact callbacks, if there's no contact callback we also don't need to do this work + if (mContactListener != nullptr) + { + // Swap bodies so that body 1 id < body 2 id + const ContactManifold *manifold; + const Body *body1, *body2; + ContactManifold temp; + if (inBody2.GetID() < inBody1.GetID()) + { + body1 = &inBody2; + body2 = &inBody1; + temp = inManifold.SwapShapes(); + manifold = &temp; + } + else + { + body1 = &inBody1; + body2 = &inBody2; + manifold = &inManifold; + } + + // Calculate hash + SubShapeIDPair key { body1->GetID(), manifold->mSubShapeID1, body2->GetID(), manifold->mSubShapeID2 }; + uint64 key_hash = key.GetHash(); + + // Check if we already created this contact this physics update + ManifoldCache &write_cache = mCache[mCacheWriteIdx]; + MKVAndCreated new_manifold_kv = write_cache.FindOrCreate(ioContactAllocator, key, key_hash, 0); + if (new_manifold_kv.second) + { + // This contact is new for this physics update, check if previous update we already had this contact. + const ManifoldCache &read_cache = mCache[mCacheWriteIdx ^ 1]; + const MKeyValue *old_manifold_kv = read_cache.Find(key, key_hash); + if (old_manifold_kv == nullptr) + { + // New contact + mContactListener->OnContactAdded(*body1, *body2, *manifold, outSettings); + } + else + { + // Existing contact + mContactListener->OnContactPersisted(*body1, *body2, *manifold, outSettings); + + // Mark contact as persisted so that we won't fire OnContactRemoved callbacks + old_manifold_kv->GetValue().mFlags |= (uint16)CachedManifold::EFlags::ContactPersisted; + } + + // Check if the cache is full + if (new_manifold_kv.first != nullptr) + { + // We don't store any contact points in this manifold as it is not for caching impulses, we only need to know that the contact was created + CachedManifold &new_manifold = new_manifold_kv.first->GetValue(); + new_manifold.mContactNormal = { 0, 0, 0 }; + new_manifold.mFlags |= (uint16)CachedManifold::EFlags::CCDContact; + } + } + else + { + // Already found this contact this physics update. + // Note that we can trigger OnContactPersisted multiple times per physics update, but otherwise we have no way of obtaining the settings + mContactListener->OnContactPersisted(*body1, *body2, *manifold, outSettings); + } + + // If we swapped body1 and body2 we need to swap the mass scales back + if (manifold == &temp) + { + swap(outSettings.mInvMassScale1, outSettings.mInvMassScale2); + swap(outSettings.mInvInertiaScale1, outSettings.mInvInertiaScale2); + // Note we do not need to negate the relative surface velocity as it is not applied by the CCD collision constraint + } + } + + JPH_ASSERT(outSettings.mIsSensor || !(inBody1.IsSensor() || inBody2.IsSensor()), "Sensors cannot be converted into regular bodies by a contact callback!"); +} + +void ContactConstraintManager::SortContacts(uint32 *inConstraintIdxBegin, uint32 *inConstraintIdxEnd) const +{ + JPH_PROFILE_FUNCTION(); + + QuickSort(inConstraintIdxBegin, inConstraintIdxEnd, [this](uint32 inLHS, uint32 inRHS) { + const ContactConstraint &lhs = mConstraints[inLHS]; + const ContactConstraint &rhs = mConstraints[inRHS]; + + // Most of the time the sort key will be different so we sort on that + if (lhs.mSortKey != rhs.mSortKey) + return lhs.mSortKey < rhs.mSortKey; + + // If they're equal we use the IDs of body 1 to order + if (lhs.mBody1 != rhs.mBody1) + return lhs.mBody1->GetID() < rhs.mBody1->GetID(); + + // If they're still equal we use the IDs of body 2 to order + if (lhs.mBody2 != rhs.mBody2) + return lhs.mBody2->GetID() < rhs.mBody2->GetID(); + + JPH_ASSERT(inLHS == inRHS, "Hash collision, ordering will be inconsistent"); + return false; + }); +} + +void ContactConstraintManager::FinalizeContactCacheAndCallContactPointRemovedCallbacks(uint inExpectedNumBodyPairs, uint inExpectedNumManifolds) +{ + JPH_PROFILE_FUNCTION(); + +#ifdef JPH_ENABLE_ASSERTS + // Mark cache as finalized + ManifoldCache &old_write_cache = mCache[mCacheWriteIdx]; + old_write_cache.Finalize(); + + // Check that the count of body pairs and manifolds that we tracked outside of the cache (to avoid contention on an atomic) is correct + JPH_ASSERT(old_write_cache.GetNumBodyPairs() == inExpectedNumBodyPairs); + JPH_ASSERT(old_write_cache.GetNumManifolds() == inExpectedNumManifolds); +#endif + + // Buffers are now complete, make write buffer the read buffer + mCacheWriteIdx ^= 1; + + // Get the old read cache / new write cache + ManifoldCache &old_read_cache = mCache[mCacheWriteIdx]; + + // Call the contact point removal callbacks + if (mContactListener != nullptr) + old_read_cache.ContactPointRemovedCallbacks(mContactListener); + + // We're done with the old read cache now + old_read_cache.Clear(); + + // Use the amount of contacts from the last iteration to determine the amount of buckets to use in the hash map for the next iteration + old_read_cache.Prepare(inExpectedNumBodyPairs, inExpectedNumManifolds); +} + +bool ContactConstraintManager::WereBodiesInContact(const BodyID &inBody1ID, const BodyID &inBody2ID) const +{ + // The body pair needs to be in the cache and it needs to have a manifold (otherwise it's just a record indicating that there are no collisions) + const ManifoldCache &read_cache = mCache[mCacheWriteIdx ^ 1]; + BodyPair key; + if (inBody1ID < inBody2ID) + key = BodyPair(inBody1ID, inBody2ID); + else + key = BodyPair(inBody2ID, inBody1ID); + uint64 key_hash = key.GetHash(); + const BPKeyValue *kv = read_cache.Find(key, key_hash); + return kv != nullptr && kv->GetValue().mFirstCachedManifold != ManifoldMap::cInvalidHandle; +} + +template +JPH_INLINE void ContactConstraintManager::sWarmStartConstraint(ContactConstraint &ioConstraint, MotionProperties *ioMotionProperties1, MotionProperties *ioMotionProperties2, float inWarmStartImpulseRatio) +{ + // Calculate tangents + Vec3 t1, t2; + ioConstraint.GetTangents(t1, t2); + + Vec3 ws_normal = ioConstraint.GetWorldSpaceNormal(); + + for (WorldContactPoint &wcp : ioConstraint.mContactPoints) + { + // Warm starting: Apply impulse from last frame + if (wcp.mFrictionConstraint1.IsActive() || wcp.mFrictionConstraint2.IsActive()) + { + wcp.mFrictionConstraint1.TemplatedWarmStart(ioMotionProperties1, ioConstraint.mInvMass1, ioMotionProperties2, ioConstraint.mInvMass2, t1, inWarmStartImpulseRatio); + wcp.mFrictionConstraint2.TemplatedWarmStart(ioMotionProperties1, ioConstraint.mInvMass1, ioMotionProperties2, ioConstraint.mInvMass2, t2, inWarmStartImpulseRatio); + } + wcp.mNonPenetrationConstraint.TemplatedWarmStart(ioMotionProperties1, ioConstraint.mInvMass1, ioMotionProperties2, ioConstraint.mInvMass2, ws_normal, inWarmStartImpulseRatio); + } +} + +template +void ContactConstraintManager::WarmStartVelocityConstraints(const uint32 *inConstraintIdxBegin, const uint32 *inConstraintIdxEnd, float inWarmStartImpulseRatio, MotionPropertiesCallback &ioCallback) +{ + JPH_PROFILE_FUNCTION(); + + for (const uint32 *constraint_idx = inConstraintIdxBegin; constraint_idx < inConstraintIdxEnd; ++constraint_idx) + { + ContactConstraint &constraint = mConstraints[*constraint_idx]; + + // Fetch bodies + Body &body1 = *constraint.mBody1; + EMotionType motion_type1 = body1.GetMotionType(); + MotionProperties *motion_properties1 = body1.GetMotionPropertiesUnchecked(); + + Body &body2 = *constraint.mBody2; + EMotionType motion_type2 = body2.GetMotionType(); + MotionProperties *motion_properties2 = body2.GetMotionPropertiesUnchecked(); + + // Dispatch to the correct templated form + // Note: Warm starting doesn't differentiate between kinematic/static bodies so we handle both as static bodies + if (motion_type1 == EMotionType::Dynamic) + { + if (motion_type2 == EMotionType::Dynamic) + { + sWarmStartConstraint(constraint, motion_properties1, motion_properties2, inWarmStartImpulseRatio); + + ioCallback(motion_properties2); + } + else + sWarmStartConstraint(constraint, motion_properties1, motion_properties2, inWarmStartImpulseRatio); + + ioCallback(motion_properties1); + } + else + { + JPH_ASSERT(motion_type2 == EMotionType::Dynamic); + + sWarmStartConstraint(constraint, motion_properties1, motion_properties2, inWarmStartImpulseRatio); + + ioCallback(motion_properties2); + } + } +} + +// Specialize for the two body callback types +template void ContactConstraintManager::WarmStartVelocityConstraints(const uint32 *inConstraintIdxBegin, const uint32 *inConstraintIdxEnd, float inWarmStartImpulseRatio, CalculateSolverSteps &ioCallback); +template void ContactConstraintManager::WarmStartVelocityConstraints(const uint32 *inConstraintIdxBegin, const uint32 *inConstraintIdxEnd, float inWarmStartImpulseRatio, DummyCalculateSolverSteps &ioCallback); + +template +JPH_INLINE bool ContactConstraintManager::sSolveVelocityConstraint(ContactConstraint &ioConstraint, MotionProperties *ioMotionProperties1, MotionProperties *ioMotionProperties2) +{ + bool any_impulse_applied = false; + + // Calculate tangents + Vec3 t1, t2; + ioConstraint.GetTangents(t1, t2); + + // First apply all friction constraints (non-penetration is more important than friction) + for (WorldContactPoint &wcp : ioConstraint.mContactPoints) + { + // Check if friction is enabled + if (wcp.mFrictionConstraint1.IsActive() || wcp.mFrictionConstraint2.IsActive()) + { + // Calculate impulse to stop motion in tangential direction + float lambda1 = wcp.mFrictionConstraint1.TemplatedSolveVelocityConstraintGetTotalLambda(ioMotionProperties1, ioMotionProperties2, t1); + float lambda2 = wcp.mFrictionConstraint2.TemplatedSolveVelocityConstraintGetTotalLambda(ioMotionProperties1, ioMotionProperties2, t2); + float total_lambda_sq = Square(lambda1) + Square(lambda2); + + // Calculate max impulse that can be applied. Note that we're using the non-penetration impulse from the previous iteration here. + // We do this because non-penetration is more important so is solved last (the last things that are solved in an iterative solver + // contribute the most). + float max_lambda_f = ioConstraint.mCombinedFriction * wcp.mNonPenetrationConstraint.GetTotalLambda(); + + // If the total lambda that we will apply is too large, scale it back + if (total_lambda_sq > Square(max_lambda_f)) + { + float scale = max_lambda_f / sqrt(total_lambda_sq); + lambda1 *= scale; + lambda2 *= scale; + } + + // Apply the friction impulse + if (wcp.mFrictionConstraint1.TemplatedSolveVelocityConstraintApplyLambda(ioMotionProperties1, ioConstraint.mInvMass1, ioMotionProperties2, ioConstraint.mInvMass2, t1, lambda1)) + any_impulse_applied = true; + if (wcp.mFrictionConstraint2.TemplatedSolveVelocityConstraintApplyLambda(ioMotionProperties1, ioConstraint.mInvMass1, ioMotionProperties2, ioConstraint.mInvMass2, t2, lambda2)) + any_impulse_applied = true; + } + } + + Vec3 ws_normal = ioConstraint.GetWorldSpaceNormal(); + + // Then apply all non-penetration constraints + for (WorldContactPoint &wcp : ioConstraint.mContactPoints) + { + // Solve non penetration velocities + if (wcp.mNonPenetrationConstraint.TemplatedSolveVelocityConstraint(ioMotionProperties1, ioConstraint.mInvMass1, ioMotionProperties2, ioConstraint.mInvMass2, ws_normal, 0.0f, FLT_MAX)) + any_impulse_applied = true; + } + + return any_impulse_applied; +} + +bool ContactConstraintManager::SolveVelocityConstraints(const uint32 *inConstraintIdxBegin, const uint32 *inConstraintIdxEnd) +{ + JPH_PROFILE_FUNCTION(); + + bool any_impulse_applied = false; + + for (const uint32 *constraint_idx = inConstraintIdxBegin; constraint_idx < inConstraintIdxEnd; ++constraint_idx) + { + ContactConstraint &constraint = mConstraints[*constraint_idx]; + + // Fetch bodies + Body &body1 = *constraint.mBody1; + EMotionType motion_type1 = body1.GetMotionType(); + MotionProperties *motion_properties1 = body1.GetMotionPropertiesUnchecked(); + + Body &body2 = *constraint.mBody2; + EMotionType motion_type2 = body2.GetMotionType(); + MotionProperties *motion_properties2 = body2.GetMotionPropertiesUnchecked(); + + // Dispatch to the correct templated form + switch (motion_type1) + { + case EMotionType::Dynamic: + switch (motion_type2) + { + case EMotionType::Dynamic: + any_impulse_applied |= sSolveVelocityConstraint(constraint, motion_properties1, motion_properties2); + break; + + case EMotionType::Kinematic: + any_impulse_applied |= sSolveVelocityConstraint(constraint, motion_properties1, motion_properties2); + break; + + case EMotionType::Static: + any_impulse_applied |= sSolveVelocityConstraint(constraint, motion_properties1, motion_properties2); + break; + + default: + JPH_ASSERT(false); + break; + } + break; + + case EMotionType::Kinematic: + JPH_ASSERT(motion_type2 == EMotionType::Dynamic); + any_impulse_applied |= sSolveVelocityConstraint(constraint, motion_properties1, motion_properties2); + break; + + case EMotionType::Static: + JPH_ASSERT(motion_type2 == EMotionType::Dynamic); + any_impulse_applied |= sSolveVelocityConstraint(constraint, motion_properties1, motion_properties2); + break; + + default: + JPH_ASSERT(false); + break; + } + } + + return any_impulse_applied; +} + +void ContactConstraintManager::StoreAppliedImpulses(const uint32 *inConstraintIdxBegin, const uint32 *inConstraintIdxEnd) const +{ + // Copy back total applied impulse to cache for the next frame + for (const uint32 *constraint_idx = inConstraintIdxBegin; constraint_idx < inConstraintIdxEnd; ++constraint_idx) + { + const ContactConstraint &constraint = mConstraints[*constraint_idx]; + + for (const WorldContactPoint &wcp : constraint.mContactPoints) + { + wcp.mContactPoint->mNonPenetrationLambda = wcp.mNonPenetrationConstraint.GetTotalLambda(); + wcp.mContactPoint->mFrictionLambda[0] = wcp.mFrictionConstraint1.GetTotalLambda(); + wcp.mContactPoint->mFrictionLambda[1] = wcp.mFrictionConstraint2.GetTotalLambda(); + } + } +} + +bool ContactConstraintManager::SolvePositionConstraints(const uint32 *inConstraintIdxBegin, const uint32 *inConstraintIdxEnd) +{ + JPH_PROFILE_FUNCTION(); + + bool any_impulse_applied = false; + + for (const uint32 *constraint_idx = inConstraintIdxBegin; constraint_idx < inConstraintIdxEnd; ++constraint_idx) + { + ContactConstraint &constraint = mConstraints[*constraint_idx]; + + // Fetch bodies + Body &body1 = *constraint.mBody1; + Body &body2 = *constraint.mBody2; + + // Get transforms + RMat44 transform1 = body1.GetCenterOfMassTransform(); + RMat44 transform2 = body2.GetCenterOfMassTransform(); + + Vec3 ws_normal = constraint.GetWorldSpaceNormal(); + + for (WorldContactPoint &wcp : constraint.mContactPoints) + { + // Calculate new contact point positions in world space (the bodies may have moved) + RVec3 p1 = transform1 * Vec3::sLoadFloat3Unsafe(wcp.mContactPoint->mPosition1); + RVec3 p2 = transform2 * Vec3::sLoadFloat3Unsafe(wcp.mContactPoint->mPosition2); + + // Calculate separation along the normal (negative if interpenetrating) + // Allow a little penetration by default (PhysicsSettings::mPenetrationSlop) to avoid jittering between contact/no-contact which wipes out the contact cache and warm start impulses + // Clamp penetration to a max PhysicsSettings::mMaxPenetrationDistance so that we don't apply a huge impulse if we're penetrating a lot + float separation = max(Vec3(p2 - p1).Dot(ws_normal) + mPhysicsSettings.mPenetrationSlop, -mPhysicsSettings.mMaxPenetrationDistance); + + // Only enforce constraint when separation < 0 (otherwise we're apart) + if (separation < 0.0f) + { + // Update constraint properties (bodies may have moved) + wcp.CalculateNonPenetrationConstraintProperties(body1, constraint.mInvMass1, constraint.mInvInertiaScale1, body2, constraint.mInvMass2, constraint.mInvInertiaScale2, p1, p2, ws_normal); + + // Solve position errors + if (wcp.mNonPenetrationConstraint.SolvePositionConstraintWithMassOverride(body1, constraint.mInvMass1, body2, constraint.mInvMass2, ws_normal, separation, mPhysicsSettings.mBaumgarte)) + any_impulse_applied = true; + } + } + } + + return any_impulse_applied; +} + +void ContactConstraintManager::RecycleConstraintBuffer() +{ + // Reset constraint array + mNumConstraints = 0; +} + +void ContactConstraintManager::FinishConstraintBuffer() +{ + // Free constraints buffer + mUpdateContext->mTempAllocator->Free(mConstraints, mMaxConstraints * sizeof(ContactConstraint)); + mConstraints = nullptr; + mNumConstraints = 0; + + // Reset update context + mUpdateContext = nullptr; +} + +void ContactConstraintManager::SaveState(StateRecorder &inStream, const StateRecorderFilter *inFilter) const +{ + mCache[mCacheWriteIdx ^ 1].SaveState(inStream, inFilter); +} + +bool ContactConstraintManager::RestoreState(StateRecorder &inStream) +{ + bool success = mCache[mCacheWriteIdx].RestoreState(mCache[mCacheWriteIdx ^ 1], inStream); + mCacheWriteIdx ^= 1; + mCache[mCacheWriteIdx].Clear(); + return success; +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ContactConstraintManager.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ContactConstraintManager.h new file mode 100644 index 00000000000..cb9d7d80f5c --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ContactConstraintManager.h @@ -0,0 +1,513 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +JPH_SUPPRESS_WARNINGS_STD_BEGIN +#include +JPH_SUPPRESS_WARNINGS_STD_END + +JPH_NAMESPACE_BEGIN + +struct PhysicsSettings; +class PhysicsUpdateContext; + +class JPH_EXPORT ContactConstraintManager : public NonCopyable +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Constructor + explicit ContactConstraintManager(const PhysicsSettings &inPhysicsSettings); + ~ContactConstraintManager(); + + /// Initialize the system. + /// @param inMaxBodyPairs Maximum amount of body pairs to process (anything else will fall through the world), this number should generally be much higher than the max amount of contact points as there will be lots of bodies close that are not actually touching + /// @param inMaxContactConstraints Maximum amount of contact constraints to process (anything else will fall through the world) + void Init(uint inMaxBodyPairs, uint inMaxContactConstraints); + + /// Listener that is notified whenever a contact point between two bodies is added/updated/removed + void SetContactListener(ContactListener *inListener) { mContactListener = inListener; } + ContactListener * GetContactListener() const { return mContactListener; } + + /// Callback function to combine the restitution or friction of two bodies + /// Note that when merging manifolds (when PhysicsSettings::mUseManifoldReduction is true) you will only get a callback for the merged manifold. + /// It is not possible in that case to get all sub shape ID pairs that were colliding, you'll get the first encountered pair. + using CombineFunction = float (*)(const Body &inBody1, const SubShapeID &inSubShapeID1, const Body &inBody2, const SubShapeID &inSubShapeID2); + + /// Set the function that combines the friction of two bodies and returns it + /// Default method is the geometric mean: sqrt(friction1 * friction2). + void SetCombineFriction(CombineFunction inCombineFriction) { mCombineFriction = inCombineFriction; } + CombineFunction GetCombineFriction() const { return mCombineFriction; } + + /// Set the function that combines the restitution of two bodies and returns it + /// Default method is max(restitution1, restitution1) + void SetCombineRestitution(CombineFunction inCombineRestitution) { mCombineRestitution = inCombineRestitution; } + CombineFunction GetCombineRestitution() const { return mCombineRestitution; } + + /// Get the max number of contact constraints that are allowed + uint32 GetMaxConstraints() const { return mMaxConstraints; } + + /// Check with the listener if inBody1 and inBody2 could collide, returns false if not + inline ValidateResult ValidateContactPoint(const Body &inBody1, const Body &inBody2, RVec3Arg inBaseOffset, const CollideShapeResult &inCollisionResult) const + { + if (mContactListener == nullptr) + return ValidateResult::AcceptAllContactsForThisBodyPair; + + return mContactListener->OnContactValidate(inBody1, inBody2, inBaseOffset, inCollisionResult); + } + + /// Sets up the constraint buffer. Should be called before starting collision detection. + void PrepareConstraintBuffer(PhysicsUpdateContext *inContext); + + /// Max 4 contact points are needed for a stable manifold + static const int MaxContactPoints = 4; + + /// Contacts are allocated in a lock free hash map + class ContactAllocator : public LFHMAllocatorContext + { + public: + using LFHMAllocatorContext::LFHMAllocatorContext; + + uint mNumBodyPairs = 0; ///< Total number of body pairs added using this allocator + uint mNumManifolds = 0; ///< Total number of manifolds added using this allocator + EPhysicsUpdateError mErrors = EPhysicsUpdateError::None; ///< Errors reported on this allocator + }; + + /// Get a new allocator context for storing contacts. Note that you should call this once and then add multiple contacts using the context. + ContactAllocator GetContactAllocator() { return mCache[mCacheWriteIdx].GetContactAllocator(); } + + /// Check if the contact points from the previous frame are reusable and if so copy them. + /// When the cache was usable and the pair has been handled: outPairHandled = true. + /// When a contact constraint was produced: outConstraintCreated = true. + void GetContactsFromCache(ContactAllocator &ioContactAllocator, Body &inBody1, Body &inBody2, bool &outPairHandled, bool &outConstraintCreated); + + /// Handle used to keep track of the current body pair + using BodyPairHandle = void *; + + /// Create a handle for a colliding body pair so that contact constraints can be added between them. + /// Needs to be called once per body pair per frame before calling AddContactConstraint. + BodyPairHandle AddBodyPair(ContactAllocator &ioContactAllocator, const Body &inBody1, const Body &inBody2); + + /// Add a contact constraint for this frame. + /// + /// @param ioContactAllocator The allocator that reserves memory for the contacts + /// @param inBodyPair The handle for the contact cache for this body pair + /// @param inBody1 The first body that is colliding + /// @param inBody2 The second body that is colliding + /// @param inManifold The manifold that describes the collision + /// @return true if a contact constraint was created (can be false in the case of a sensor) + /// + /// This is using the approach described in 'Modeling and Solving Constraints' by Erin Catto presented at GDC 2009 (and later years with slight modifications). + /// We're using the formulas from slide 50 - 53 combined. + /// + /// Euler velocity integration: + /// + /// v1' = v1 + M^-1 P + /// + /// Impulse: + /// + /// P = J^T lambda + /// + /// Constraint force: + /// + /// lambda = -K^-1 J v1 + /// + /// Inverse effective mass: + /// + /// K = J M^-1 J^T + /// + /// Constraint equation (limits movement in 1 axis): + /// + /// C = (p2 - p1) . n + /// + /// Jacobian (for position constraint) + /// + /// J = [-n, -r1 x n, n, r2 x n] + /// + /// n = contact normal (pointing away from body 1). + /// p1, p2 = positions of collision on body 1 and 2. + /// r1, r2 = contact point relative to center of mass of body 1 and body 2 (r1 = p1 - x1, r2 = p2 - x2). + /// v1, v2 = (linear velocity, angular velocity): 6 vectors containing linear and angular velocity for body 1 and 2. + /// M = mass matrix, a diagonal matrix of the mass and inertia with diagonal [m1, I1, m2, I2]. + bool AddContactConstraint(ContactAllocator &ioContactAllocator, BodyPairHandle inBodyPair, Body &inBody1, Body &inBody2, const ContactManifold &inManifold); + + /// Finalizes the contact cache, the contact cache that was generated during the calls to AddContactConstraint in this update + /// will be used from now on to read from. After finalizing the contact cache, the contact removed callbacks will be called. + /// inExpectedNumBodyPairs / inExpectedNumManifolds are the amount of body pairs / manifolds found in the previous step and is + /// used to determine the amount of buckets the contact cache hash map will use in the next update. + void FinalizeContactCacheAndCallContactPointRemovedCallbacks(uint inExpectedNumBodyPairs, uint inExpectedNumManifolds); + + /// Check if 2 bodies were in contact during the last simulation step. Since contacts are only detected between active bodies, at least one of the bodies must be active. + /// Uses the read collision cache to determine if 2 bodies are in contact. + bool WereBodiesInContact(const BodyID &inBody1ID, const BodyID &inBody2ID) const; + + /// Get the number of contact constraints that were found + uint32 GetNumConstraints() const { return min(mNumConstraints, mMaxConstraints); } + + /// Sort contact constraints deterministically + void SortContacts(uint32 *inConstraintIdxBegin, uint32 *inConstraintIdxEnd) const; + + /// Get the affected bodies for a given constraint + inline void GetAffectedBodies(uint32 inConstraintIdx, const Body *&outBody1, const Body *&outBody2) const + { + const ContactConstraint &constraint = mConstraints[inConstraintIdx]; + outBody1 = constraint.mBody1; + outBody2 = constraint.mBody2; + } + + /// Apply last frame's impulses as an initial guess for this frame's impulses + template + void WarmStartVelocityConstraints(const uint32 *inConstraintIdxBegin, const uint32 *inConstraintIdxEnd, float inWarmStartImpulseRatio, MotionPropertiesCallback &ioCallback); + + /// Solve velocity constraints, when almost nothing changes this should only apply very small impulses + /// since we're warm starting with the total impulse applied in the last frame above. + /// + /// Friction wise we're using the Coulomb friction model which says that: + /// + /// |F_T| <= mu |F_N| + /// + /// Where F_T is the tangential force, F_N is the normal force and mu is the friction coefficient + /// + /// In impulse terms this becomes: + /// + /// |lambda_T| <= mu |lambda_N| + /// + /// And the constraint that needs to be applied is exactly the same as a non penetration constraint + /// except that we use a tangent instead of a normal. The tangent should point in the direction of the + /// tangential velocity of the point: + /// + /// J = [-T, -r1 x T, T, r2 x T] + /// + /// Where T is the tangent. + /// + /// See slide 42 and 43. + /// + /// Restitution is implemented as a velocity bias (see slide 41): + /// + /// b = e v_n^- + /// + /// e = the restitution coefficient, v_n^- is the normal velocity prior to the collision + /// + /// Restitution is only applied when v_n^- is large enough and the points are moving towards collision + bool SolveVelocityConstraints(const uint32 *inConstraintIdxBegin, const uint32 *inConstraintIdxEnd); + + /// Save back the lambdas to the contact cache for the next warm start + void StoreAppliedImpulses(const uint32 *inConstraintIdxBegin, const uint32 *inConstraintIdxEnd) const; + + /// Solve position constraints. + /// This is using the approach described in 'Modeling and Solving Constraints' by Erin Catto presented at GDC 2007. + /// On slide 78 it is suggested to split up the Baumgarte stabilization for positional drift so that it does not + /// actually add to the momentum. We combine an Euler velocity integrate + a position integrate and then discard the velocity + /// change. + /// + /// Constraint force: + /// + /// lambda = -K^-1 b + /// + /// Baumgarte stabilization: + /// + /// b = beta / dt C + /// + /// beta = baumgarte stabilization factor. + /// dt = delta time. + bool SolvePositionConstraints(const uint32 *inConstraintIdxBegin, const uint32 *inConstraintIdxEnd); + + /// Recycle the constraint buffer. Should be called between collision simulation steps. + void RecycleConstraintBuffer(); + + /// Terminate the constraint buffer. Should be called after simulation ends. + void FinishConstraintBuffer(); + + /// Called by continuous collision detection to notify the contact listener that a contact was added + /// @param ioContactAllocator The allocator that reserves memory for the contacts + /// @param inBody1 The first body that is colliding + /// @param inBody2 The second body that is colliding + /// @param inManifold The manifold that describes the collision + /// @param outSettings The calculated contact settings (may be overridden by the contact listener) + void OnCCDContactAdded(ContactAllocator &ioContactAllocator, const Body &inBody1, const Body &inBody2, const ContactManifold &inManifold, ContactSettings &outSettings); + +#ifdef JPH_DEBUG_RENDERER + // Drawing properties + static bool sDrawContactPoint; + static bool sDrawSupportingFaces; + static bool sDrawContactPointReduction; + static bool sDrawContactManifolds; +#endif // JPH_DEBUG_RENDERER + + /// Saving state for replay + void SaveState(StateRecorder &inStream, const StateRecorderFilter *inFilter) const; + + /// Restoring state for replay. Returns false when failed. + bool RestoreState(StateRecorder &inStream); + +private: + /// Local space contact point, used for caching impulses + class CachedContactPoint + { + public: + /// Saving / restoring state for replay + void SaveState(StateRecorder &inStream) const; + void RestoreState(StateRecorder &inStream); + + /// Local space positions on body 1 and 2. + /// Note: these values are read through sLoadFloat3Unsafe. + Float3 mPosition1; + Float3 mPosition2; + + /// Total applied impulse during the last update that it was used + float mNonPenetrationLambda; + Vector<2> mFrictionLambda; + }; + + static_assert(sizeof(CachedContactPoint) == 36, "Unexpected size"); + static_assert(alignof(CachedContactPoint) == 4, "Assuming 4 byte aligned"); + + /// A single cached manifold + class CachedManifold + { + public: + /// Calculate size in bytes needed beyond the size of the class to store inNumContactPoints + static int sGetRequiredExtraSize(int inNumContactPoints) { return max(0, inNumContactPoints - 1) * sizeof(CachedContactPoint); } + + /// Calculate total class size needed for storing inNumContactPoints + static int sGetRequiredTotalSize(int inNumContactPoints) { return sizeof(CachedManifold) + sGetRequiredExtraSize(inNumContactPoints); } + + /// Saving / restoring state for replay + void SaveState(StateRecorder &inStream) const; + void RestoreState(StateRecorder &inStream); + + /// Handle to next cached contact points in ManifoldCache::mCachedManifolds for the same body pair + uint32 mNextWithSameBodyPair; + + /// Contact normal in the space of 2. + /// Note: this value is read through sLoadFloat3Unsafe. + Float3 mContactNormal; + + /// Flags for this cached manifold + enum class EFlags : uint16 + { + ContactPersisted = 1, ///< If this cache entry was reused in the next simulation update + CCDContact = 2 ///< This is a cached manifold reported by continuous collision detection and was only used to create a contact callback + }; + + /// @see EFlags + mutable atomic mFlags { 0 }; + + /// Number of contact points in the array below + uint16 mNumContactPoints; + + /// Contact points that this manifold consists of + CachedContactPoint mContactPoints[1]; + }; + + static_assert(sizeof(CachedManifold) == 56, "This structure is expect to not contain any waste due to alignment"); + static_assert(alignof(CachedManifold) == 4, "Assuming 4 byte aligned"); + + /// Define a map that maps SubShapeIDPair -> manifold + using ManifoldMap = LockFreeHashMap; + using MKeyValue = ManifoldMap::KeyValue; + using MKVAndCreated = pair; + + /// Start of list of contact points for a particular pair of bodies + class CachedBodyPair + { + public: + /// Saving / restoring state for replay + void SaveState(StateRecorder &inStream) const; + void RestoreState(StateRecorder &inStream); + + /// Local space position difference from Body A to Body B. + /// Note: this value is read through sLoadFloat3Unsafe + Float3 mDeltaPosition; + + /// Local space rotation difference from Body A to Body B, fourth component of quaternion is not stored but is guaranteed >= 0. + /// Note: this value is read through sLoadFloat3Unsafe + Float3 mDeltaRotation; + + /// Handle to first manifold in ManifoldCache::mCachedManifolds + uint32 mFirstCachedManifold; + }; + + static_assert(sizeof(CachedBodyPair) == 28, "Unexpected size"); + static_assert(alignof(CachedBodyPair) == 4, "Assuming 4 byte aligned"); + + /// Define a map that maps BodyPair -> CachedBodyPair + using BodyPairMap = LockFreeHashMap; + using BPKeyValue = BodyPairMap::KeyValue; + + /// Holds all caches that are needed to quickly find cached body pairs / manifolds + class ManifoldCache + { + public: + /// Initialize the cache + void Init(uint inMaxBodyPairs, uint inMaxContactConstraints, uint inCachedManifoldsSize); + + /// Reset all entries from the cache + void Clear(); + + /// Prepare cache before creating new contacts. + /// inExpectedNumBodyPairs / inExpectedNumManifolds are the amount of body pairs / manifolds found in the previous step and is used to determine the amount of buckets the contact cache hash map will use. + void Prepare(uint inExpectedNumBodyPairs, uint inExpectedNumManifolds); + + /// Get a new allocator context for storing contacts. Note that you should call this once and then add multiple contacts using the context. + ContactAllocator GetContactAllocator() { return ContactAllocator(mAllocator, cAllocatorBlockSize); } + + /// Find / create cached entry for SubShapeIDPair -> CachedManifold + const MKeyValue * Find(const SubShapeIDPair &inKey, uint64 inKeyHash) const; + MKeyValue * Create(ContactAllocator &ioContactAllocator, const SubShapeIDPair &inKey, uint64 inKeyHash, int inNumContactPoints); + MKVAndCreated FindOrCreate(ContactAllocator &ioContactAllocator, const SubShapeIDPair &inKey, uint64 inKeyHash, int inNumContactPoints); + uint32 ToHandle(const MKeyValue *inKeyValue) const; + const MKeyValue * FromHandle(uint32 inHandle) const; + + /// Find / create entry for BodyPair -> CachedBodyPair + const BPKeyValue * Find(const BodyPair &inKey, uint64 inKeyHash) const; + BPKeyValue * Create(ContactAllocator &ioContactAllocator, const BodyPair &inKey, uint64 inKeyHash); + void GetAllBodyPairsSorted(Array &outAll) const; + void GetAllManifoldsSorted(const CachedBodyPair &inBodyPair, Array &outAll) const; + void GetAllCCDManifoldsSorted(Array &outAll) const; + void ContactPointRemovedCallbacks(ContactListener *inListener); + +#ifdef JPH_ENABLE_ASSERTS + /// Get the amount of manifolds in the cache + uint GetNumManifolds() const { return mCachedManifolds.GetNumKeyValues(); } + + /// Get the amount of body pairs in the cache + uint GetNumBodyPairs() const { return mCachedBodyPairs.GetNumKeyValues(); } + + /// Before a cache is finalized you can only do Create(), after only Find() or Clear() + void Finalize(); +#endif + + /// Saving / restoring state for replay + void SaveState(StateRecorder &inStream, const StateRecorderFilter *inFilter) const; + bool RestoreState(const ManifoldCache &inReadCache, StateRecorder &inStream); + + private: + /// Block size used when allocating new blocks in the contact cache + static constexpr uint32 cAllocatorBlockSize = 4096; + + /// Allocator used by both mCachedManifolds and mCachedBodyPairs, this makes it more likely that a body pair and its manifolds are close in memory + LFHMAllocator mAllocator; + + /// Simple hash map for SubShapeIDPair -> CachedManifold + ManifoldMap mCachedManifolds { mAllocator }; + + /// Simple hash map for BodyPair -> CachedBodyPair + BodyPairMap mCachedBodyPairs { mAllocator }; + +#ifdef JPH_ENABLE_ASSERTS + bool mIsFinalized = false; ///< Marks if this buffer is complete +#endif + }; + + ManifoldCache mCache[2]; ///< We have one cache to read from and one to write to + int mCacheWriteIdx = 0; ///< Which cache we're currently writing to + + /// World space contact point, used for solving penetrations + class WorldContactPoint + { + public: + /// Calculate constraint properties below + void CalculateNonPenetrationConstraintProperties(const Body &inBody1, float inInvMass1, float inInvInertiaScale1, const Body &inBody2, float inInvMass2, float inInvInertiaScale2, RVec3Arg inWorldSpacePosition1, RVec3Arg inWorldSpacePosition2, Vec3Arg inWorldSpaceNormal); + + template + JPH_INLINE void TemplatedCalculateFrictionAndNonPenetrationConstraintProperties(float inDeltaTime, const Body &inBody1, const Body &inBody2, float inInvM1, float inInvM2, Mat44Arg inInvI1, Mat44Arg inInvI2, RVec3Arg inWorldSpacePosition1, RVec3Arg inWorldSpacePosition2, Vec3Arg inWorldSpaceNormal, Vec3Arg inWorldSpaceTangent1, Vec3Arg inWorldSpaceTangent2, const ContactSettings &inSettings, float inMinVelocityForRestitution); + + /// The constraint parts + AxisConstraintPart mNonPenetrationConstraint; + AxisConstraintPart mFrictionConstraint1; + AxisConstraintPart mFrictionConstraint2; + + /// Contact cache + CachedContactPoint * mContactPoint; + }; + + using WorldContactPoints = StaticArray; + + /// Contact constraint class, used for solving penetrations + class ContactConstraint + { + public: + #ifdef JPH_DEBUG_RENDERER + /// Draw the state of the contact constraint + void Draw(DebugRenderer *inRenderer, ColorArg inManifoldColor) const; + #endif // JPH_DEBUG_RENDERER + + /// Convert the world space normal to a Vec3 + JPH_INLINE Vec3 GetWorldSpaceNormal() const + { + return Vec3::sLoadFloat3Unsafe(mWorldSpaceNormal); + } + + /// Get the tangents for this contact constraint + JPH_INLINE void GetTangents(Vec3 &outTangent1, Vec3 &outTangent2) const + { + Vec3 ws_normal = GetWorldSpaceNormal(); + outTangent1 = ws_normal.GetNormalizedPerpendicular(); + outTangent2 = ws_normal.Cross(outTangent1); + } + + Body * mBody1; + Body * mBody2; + uint64 mSortKey; + Float3 mWorldSpaceNormal; + float mCombinedFriction; + float mInvMass1; + float mInvInertiaScale1; + float mInvMass2; + float mInvInertiaScale2; + WorldContactPoints mContactPoints; + }; + + /// Internal helper function to calculate the friction and non-penetration constraint properties. Templated to the motion type to reduce the amount of branches and calculations. + template + JPH_INLINE void TemplatedCalculateFrictionAndNonPenetrationConstraintProperties(ContactConstraint &ioConstraint, const ContactSettings &inSettings, float inDeltaTime, RMat44Arg inTransformBody1, RMat44Arg inTransformBody2, const Body &inBody1, const Body &inBody2); + + /// Internal helper function to calculate the friction and non-penetration constraint properties. + inline void CalculateFrictionAndNonPenetrationConstraintProperties(ContactConstraint &ioConstraint, const ContactSettings &inSettings, float inDeltaTime, RMat44Arg inTransformBody1, RMat44Arg inTransformBody2, const Body &inBody1, const Body &inBody2); + + /// Internal helper function to add a contact constraint. Templated to the motion type to reduce the amount of branches and calculations. + template + bool TemplatedAddContactConstraint(ContactAllocator &ioContactAllocator, BodyPairHandle inBodyPairHandle, Body &inBody1, Body &inBody2, const ContactManifold &inManifold); + + /// Internal helper function to warm start contact constraint. Templated to the motion type to reduce the amount of branches and calculations. + template + JPH_INLINE static void sWarmStartConstraint(ContactConstraint &ioConstraint, MotionProperties *ioMotionProperties1, MotionProperties *ioMotionProperties2, float inWarmStartImpulseRatio); + + /// Internal helper function to solve a single contact constraint. Templated to the motion type to reduce the amount of branches and calculations. + template + JPH_INLINE static bool sSolveVelocityConstraint(ContactConstraint &ioConstraint, MotionProperties *ioMotionProperties1, MotionProperties *ioMotionProperties2); + + /// The main physics settings instance + const PhysicsSettings & mPhysicsSettings; + + /// Listener that is notified whenever a contact point between two bodies is added/updated/removed + ContactListener * mContactListener = nullptr; + + /// Functions that are used to combine friction and restitution of 2 bodies + CombineFunction mCombineFriction = [](const Body &inBody1, const SubShapeID &, const Body &inBody2, const SubShapeID &) { return sqrt(inBody1.GetFriction() * inBody2.GetFriction()); }; + CombineFunction mCombineRestitution = [](const Body &inBody1, const SubShapeID &, const Body &inBody2, const SubShapeID &) { return max(inBody1.GetRestitution(), inBody2.GetRestitution()); }; + + /// The constraints that were added this frame + ContactConstraint * mConstraints = nullptr; + uint32 mMaxConstraints = 0; + atomic mNumConstraints { 0 }; + + /// Context used for this physics update + PhysicsUpdateContext * mUpdateContext; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/DistanceConstraint.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/DistanceConstraint.cpp new file mode 100644 index 00000000000..e70bfb24ced --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/DistanceConstraint.cpp @@ -0,0 +1,266 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#include +#include +#ifdef JPH_DEBUG_RENDERER + #include +#endif // JPH_DEBUG_RENDERER + +JPH_NAMESPACE_BEGIN + +using namespace literals; + +JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(DistanceConstraintSettings) +{ + JPH_ADD_BASE_CLASS(DistanceConstraintSettings, TwoBodyConstraintSettings) + + JPH_ADD_ENUM_ATTRIBUTE(DistanceConstraintSettings, mSpace) + JPH_ADD_ATTRIBUTE(DistanceConstraintSettings, mPoint1) + JPH_ADD_ATTRIBUTE(DistanceConstraintSettings, mPoint2) + JPH_ADD_ATTRIBUTE(DistanceConstraintSettings, mMinDistance) + JPH_ADD_ATTRIBUTE(DistanceConstraintSettings, mMaxDistance) + JPH_ADD_ENUM_ATTRIBUTE_WITH_ALIAS(DistanceConstraintSettings, mLimitsSpringSettings.mMode, "mSpringMode") + JPH_ADD_ATTRIBUTE_WITH_ALIAS(DistanceConstraintSettings, mLimitsSpringSettings.mFrequency, "mFrequency") // Renaming attributes to stay compatible with old versions of the library + JPH_ADD_ATTRIBUTE_WITH_ALIAS(DistanceConstraintSettings, mLimitsSpringSettings.mDamping, "mDamping") +} + +void DistanceConstraintSettings::SaveBinaryState(StreamOut &inStream) const +{ + ConstraintSettings::SaveBinaryState(inStream); + + inStream.Write(mSpace); + inStream.Write(mPoint1); + inStream.Write(mPoint2); + inStream.Write(mMinDistance); + inStream.Write(mMaxDistance); + mLimitsSpringSettings.SaveBinaryState(inStream); +} + +void DistanceConstraintSettings::RestoreBinaryState(StreamIn &inStream) +{ + ConstraintSettings::RestoreBinaryState(inStream); + + inStream.Read(mSpace); + inStream.Read(mPoint1); + inStream.Read(mPoint2); + inStream.Read(mMinDistance); + inStream.Read(mMaxDistance); + mLimitsSpringSettings.RestoreBinaryState(inStream); +} + +TwoBodyConstraint *DistanceConstraintSettings::Create(Body &inBody1, Body &inBody2) const +{ + return new DistanceConstraint(inBody1, inBody2, *this); +} + +DistanceConstraint::DistanceConstraint(Body &inBody1, Body &inBody2, const DistanceConstraintSettings &inSettings) : + TwoBodyConstraint(inBody1, inBody2, inSettings), + mMinDistance(inSettings.mMinDistance), + mMaxDistance(inSettings.mMaxDistance) +{ + if (inSettings.mSpace == EConstraintSpace::WorldSpace) + { + // If all properties were specified in world space, take them to local space now + mLocalSpacePosition1 = Vec3(inBody1.GetInverseCenterOfMassTransform() * inSettings.mPoint1); + mLocalSpacePosition2 = Vec3(inBody2.GetInverseCenterOfMassTransform() * inSettings.mPoint2); + mWorldSpacePosition1 = inSettings.mPoint1; + mWorldSpacePosition2 = inSettings.mPoint2; + } + else + { + // If properties were specified in local space, we need to calculate world space positions + mLocalSpacePosition1 = Vec3(inSettings.mPoint1); + mLocalSpacePosition2 = Vec3(inSettings.mPoint2); + mWorldSpacePosition1 = inBody1.GetCenterOfMassTransform() * inSettings.mPoint1; + mWorldSpacePosition2 = inBody2.GetCenterOfMassTransform() * inSettings.mPoint2; + } + + // Store distance we want to keep between the world space points + float distance = Vec3(mWorldSpacePosition2 - mWorldSpacePosition1).Length(); + float min_distance, max_distance; + if (mMinDistance < 0.0f && mMaxDistance < 0.0f) + { + min_distance = max_distance = distance; + } + else + { + min_distance = mMinDistance < 0.0f? min(distance, mMaxDistance) : mMinDistance; + max_distance = mMaxDistance < 0.0f? max(distance, mMinDistance) : mMaxDistance; + } + SetDistance(min_distance, max_distance); + + // Most likely gravity is going to tear us apart (this is only used when the distance between the points = 0) + mWorldSpaceNormal = Vec3::sAxisY(); + + // Store spring settings + SetLimitsSpringSettings(inSettings.mLimitsSpringSettings); +} + +void DistanceConstraint::NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM) +{ + if (mBody1->GetID() == inBodyID) + mLocalSpacePosition1 -= inDeltaCOM; + else if (mBody2->GetID() == inBodyID) + mLocalSpacePosition2 -= inDeltaCOM; +} + +void DistanceConstraint::CalculateConstraintProperties(float inDeltaTime) +{ + // Update world space positions (the bodies may have moved) + mWorldSpacePosition1 = mBody1->GetCenterOfMassTransform() * mLocalSpacePosition1; + mWorldSpacePosition2 = mBody2->GetCenterOfMassTransform() * mLocalSpacePosition2; + + // Calculate world space normal + Vec3 delta = Vec3(mWorldSpacePosition2 - mWorldSpacePosition1); + float delta_len = delta.Length(); + if (delta_len > 0.0f) + mWorldSpaceNormal = delta / delta_len; + + // Calculate points relative to body + // r1 + u = (p1 - x1) + (p2 - p1) = p2 - x1 + Vec3 r1_plus_u = Vec3(mWorldSpacePosition2 - mBody1->GetCenterOfMassPosition()); + Vec3 r2 = Vec3(mWorldSpacePosition2 - mBody2->GetCenterOfMassPosition()); + + if (mMinDistance == mMaxDistance) + { + mAxisConstraint.CalculateConstraintPropertiesWithSettings(inDeltaTime, *mBody1, r1_plus_u, *mBody2, r2, mWorldSpaceNormal, 0.0f, delta_len - mMinDistance, mLimitsSpringSettings); + + // Single distance, allow constraint forces in both directions + mMinLambda = -FLT_MAX; + mMaxLambda = FLT_MAX; + } + else if (delta_len <= mMinDistance) + { + mAxisConstraint.CalculateConstraintPropertiesWithSettings(inDeltaTime, *mBody1, r1_plus_u, *mBody2, r2, mWorldSpaceNormal, 0.0f, delta_len - mMinDistance, mLimitsSpringSettings); + + // Allow constraint forces to make distance bigger only + mMinLambda = 0; + mMaxLambda = FLT_MAX; + } + else if (delta_len >= mMaxDistance) + { + mAxisConstraint.CalculateConstraintPropertiesWithSettings(inDeltaTime, *mBody1, r1_plus_u, *mBody2, r2, mWorldSpaceNormal, 0.0f, delta_len - mMaxDistance, mLimitsSpringSettings); + + // Allow constraint forces to make distance smaller only + mMinLambda = -FLT_MAX; + mMaxLambda = 0; + } + else + mAxisConstraint.Deactivate(); +} + +void DistanceConstraint::SetupVelocityConstraint(float inDeltaTime) +{ + CalculateConstraintProperties(inDeltaTime); +} + +void DistanceConstraint::ResetWarmStart() +{ + mAxisConstraint.Deactivate(); +} + +void DistanceConstraint::WarmStartVelocityConstraint(float inWarmStartImpulseRatio) +{ + mAxisConstraint.WarmStart(*mBody1, *mBody2, mWorldSpaceNormal, inWarmStartImpulseRatio); +} + +bool DistanceConstraint::SolveVelocityConstraint(float inDeltaTime) +{ + if (mAxisConstraint.IsActive()) + return mAxisConstraint.SolveVelocityConstraint(*mBody1, *mBody2, mWorldSpaceNormal, mMinLambda, mMaxLambda); + else + return false; +} + +bool DistanceConstraint::SolvePositionConstraint(float inDeltaTime, float inBaumgarte) +{ + if (mLimitsSpringSettings.mFrequency <= 0.0f) // When the spring is active, we don't need to solve the position constraint + { + float distance = Vec3(mWorldSpacePosition2 - mWorldSpacePosition1).Dot(mWorldSpaceNormal); + + // Calculate position error + float position_error = 0.0f; + if (distance < mMinDistance) + position_error = distance - mMinDistance; + else if (distance > mMaxDistance) + position_error = distance - mMaxDistance; + + if (position_error != 0.0f) + { + // Update constraint properties (bodies may have moved) + CalculateConstraintProperties(inDeltaTime); + + return mAxisConstraint.SolvePositionConstraint(*mBody1, *mBody2, mWorldSpaceNormal, position_error, inBaumgarte); + } + } + + return false; +} + +#ifdef JPH_DEBUG_RENDERER +void DistanceConstraint::DrawConstraint(DebugRenderer *inRenderer) const +{ + // Draw constraint + Vec3 delta = Vec3(mWorldSpacePosition2 - mWorldSpacePosition1); + float len = delta.Length(); + if (len < mMinDistance) + { + RVec3 real_end_pos = mWorldSpacePosition1 + (len > 0.0f? delta * mMinDistance / len : Vec3(0, len, 0)); + inRenderer->DrawLine(mWorldSpacePosition1, mWorldSpacePosition2, Color::sGreen); + inRenderer->DrawLine(mWorldSpacePosition2, real_end_pos, Color::sYellow); + } + else if (len > mMaxDistance) + { + RVec3 real_end_pos = mWorldSpacePosition1 + (len > 0.0f? delta * mMaxDistance / len : Vec3(0, len, 0)); + inRenderer->DrawLine(mWorldSpacePosition1, real_end_pos, Color::sGreen); + inRenderer->DrawLine(real_end_pos, mWorldSpacePosition2, Color::sRed); + } + else + inRenderer->DrawLine(mWorldSpacePosition1, mWorldSpacePosition2, Color::sGreen); + + // Draw constraint end points + inRenderer->DrawMarker(mWorldSpacePosition1, Color::sWhite, 0.1f); + inRenderer->DrawMarker(mWorldSpacePosition2, Color::sWhite, 0.1f); + + // Draw current length + inRenderer->DrawText3D(0.5_r * (mWorldSpacePosition1 + mWorldSpacePosition2), StringFormat("%.2f", (double)len)); +} +#endif // JPH_DEBUG_RENDERER + +void DistanceConstraint::SaveState(StateRecorder &inStream) const +{ + TwoBodyConstraint::SaveState(inStream); + + mAxisConstraint.SaveState(inStream); + inStream.Write(mWorldSpaceNormal); // When distance = 0, the normal is used from last frame so we need to store it +} + +void DistanceConstraint::RestoreState(StateRecorder &inStream) +{ + TwoBodyConstraint::RestoreState(inStream); + + mAxisConstraint.RestoreState(inStream); + inStream.Read(mWorldSpaceNormal); +} + +Ref DistanceConstraint::GetConstraintSettings() const +{ + DistanceConstraintSettings *settings = new DistanceConstraintSettings; + ToConstraintSettings(*settings); + settings->mSpace = EConstraintSpace::LocalToBodyCOM; + settings->mPoint1 = RVec3(mLocalSpacePosition1); + settings->mPoint2 = RVec3(mLocalSpacePosition2); + settings->mMinDistance = mMinDistance; + settings->mMaxDistance = mMaxDistance; + settings->mLimitsSpringSettings = mLimitsSpringSettings; + return settings; +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/DistanceConstraint.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/DistanceConstraint.h new file mode 100644 index 00000000000..fc9d3526714 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/DistanceConstraint.h @@ -0,0 +1,120 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +JPH_NAMESPACE_BEGIN + +/// Distance constraint settings, used to create a distance constraint +class JPH_EXPORT DistanceConstraintSettings final : public TwoBodyConstraintSettings +{ +public: + JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, DistanceConstraintSettings) + + // See: ConstraintSettings::SaveBinaryState + virtual void SaveBinaryState(StreamOut &inStream) const override; + + /// Create an instance of this constraint + virtual TwoBodyConstraint * Create(Body &inBody1, Body &inBody2) const override; + + /// This determines in which space the constraint is setup, all properties below should be in the specified space + EConstraintSpace mSpace = EConstraintSpace::WorldSpace; + + /// Body 1 constraint reference frame (space determined by mSpace). + /// Constraint will keep mPoint1 (a point on body 1) and mPoint2 (a point on body 2) at the same distance. + /// Note that this constraint can be used as a cheap PointConstraint by setting mPoint1 = mPoint2 (but this removes only 1 degree of freedom instead of 3). + RVec3 mPoint1 = RVec3::sZero(); + + /// Body 2 constraint reference frame (space determined by mSpace) + RVec3 mPoint2 = RVec3::sZero(); + + /// Ability to override the distance range at which the two points are kept apart. If the value is negative, it will be replaced by the distance between mPoint1 and mPoint2 (works only if mSpace is world space). + float mMinDistance = -1.0f; + float mMaxDistance = -1.0f; + + /// When enabled, this makes the limits soft. When the constraint exceeds the limits, a spring force will pull it back. + SpringSettings mLimitsSpringSettings; + +protected: + // See: ConstraintSettings::RestoreBinaryState + virtual void RestoreBinaryState(StreamIn &inStream) override; +}; + +/// This constraint is a stiff spring that holds 2 points at a fixed distance from each other +class JPH_EXPORT DistanceConstraint final : public TwoBodyConstraint +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Construct distance constraint + DistanceConstraint(Body &inBody1, Body &inBody2, const DistanceConstraintSettings &inSettings); + + // Generic interface of a constraint + virtual EConstraintSubType GetSubType() const override { return EConstraintSubType::Distance; } + virtual void NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM) override; + virtual void SetupVelocityConstraint(float inDeltaTime) override; + virtual void ResetWarmStart() override; + virtual void WarmStartVelocityConstraint(float inWarmStartImpulseRatio) override; + virtual bool SolveVelocityConstraint(float inDeltaTime) override; + virtual bool SolvePositionConstraint(float inDeltaTime, float inBaumgarte) override; +#ifdef JPH_DEBUG_RENDERER + virtual void DrawConstraint(DebugRenderer *inRenderer) const override; +#endif // JPH_DEBUG_RENDERER + virtual void SaveState(StateRecorder &inStream) const override; + virtual void RestoreState(StateRecorder &inStream) override; + virtual Ref GetConstraintSettings() const override; + + // See: TwoBodyConstraint + virtual Mat44 GetConstraintToBody1Matrix() const override { return Mat44::sTranslation(mLocalSpacePosition1); } + virtual Mat44 GetConstraintToBody2Matrix() const override { return Mat44::sTranslation(mLocalSpacePosition2); } // Note: Incorrect rotation as we don't track the original rotation difference, should not matter though as the constraint is not limiting rotation. + + /// Update the minimum and maximum distance for the constraint + void SetDistance(float inMinDistance, float inMaxDistance) { JPH_ASSERT(inMinDistance <= inMaxDistance); mMinDistance = inMinDistance; mMaxDistance = inMaxDistance; } + float GetMinDistance() const { return mMinDistance; } + float GetMaxDistance() const { return mMaxDistance; } + + /// Update the limits spring settings + const SpringSettings & GetLimitsSpringSettings() const { return mLimitsSpringSettings; } + SpringSettings & GetLimitsSpringSettings() { return mLimitsSpringSettings; } + void SetLimitsSpringSettings(const SpringSettings &inLimitsSpringSettings) { mLimitsSpringSettings = inLimitsSpringSettings; } + + ///@name Get Lagrange multiplier from last physics update (the linear impulse applied to satisfy the constraint) + inline float GetTotalLambdaPosition() const { return mAxisConstraint.GetTotalLambda(); } + +private: + // Internal helper function to calculate the values below + void CalculateConstraintProperties(float inDeltaTime); + + // CONFIGURATION PROPERTIES FOLLOW + + // Local space constraint positions + Vec3 mLocalSpacePosition1; + Vec3 mLocalSpacePosition2; + + // Min/max distance that must be kept between the world space points + float mMinDistance; + float mMaxDistance; + + // Soft constraint limits + SpringSettings mLimitsSpringSettings; + + // RUN TIME PROPERTIES FOLLOW + + // World space positions and normal + RVec3 mWorldSpacePosition1; + RVec3 mWorldSpacePosition2; + Vec3 mWorldSpaceNormal; + + // Depending on if the distance < min or distance > max we can apply forces to prevent further violations + float mMinLambda; + float mMaxLambda; + + // The constraint part + AxisConstraintPart mAxisConstraint; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/FixedConstraint.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/FixedConstraint.cpp new file mode 100644 index 00000000000..b0639851672 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/FixedConstraint.cpp @@ -0,0 +1,215 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#ifdef JPH_DEBUG_RENDERER + #include +#endif // JPH_DEBUG_RENDERER + +JPH_NAMESPACE_BEGIN + +using namespace literals; + +JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(FixedConstraintSettings) +{ + JPH_ADD_BASE_CLASS(FixedConstraintSettings, TwoBodyConstraintSettings) + + JPH_ADD_ENUM_ATTRIBUTE(FixedConstraintSettings, mSpace) + JPH_ADD_ATTRIBUTE(FixedConstraintSettings, mAutoDetectPoint) + JPH_ADD_ATTRIBUTE(FixedConstraintSettings, mPoint1) + JPH_ADD_ATTRIBUTE(FixedConstraintSettings, mAxisX1) + JPH_ADD_ATTRIBUTE(FixedConstraintSettings, mAxisY1) + JPH_ADD_ATTRIBUTE(FixedConstraintSettings, mPoint2) + JPH_ADD_ATTRIBUTE(FixedConstraintSettings, mAxisX2) + JPH_ADD_ATTRIBUTE(FixedConstraintSettings, mAxisY2) +} + +void FixedConstraintSettings::SaveBinaryState(StreamOut &inStream) const +{ + ConstraintSettings::SaveBinaryState(inStream); + + inStream.Write(mSpace); + inStream.Write(mAutoDetectPoint); + inStream.Write(mPoint1); + inStream.Write(mAxisX1); + inStream.Write(mAxisY1); + inStream.Write(mPoint2); + inStream.Write(mAxisX2); + inStream.Write(mAxisY2); +} + +void FixedConstraintSettings::RestoreBinaryState(StreamIn &inStream) +{ + ConstraintSettings::RestoreBinaryState(inStream); + + inStream.Read(mSpace); + inStream.Read(mAutoDetectPoint); + inStream.Read(mPoint1); + inStream.Read(mAxisX1); + inStream.Read(mAxisY1); + inStream.Read(mPoint2); + inStream.Read(mAxisX2); + inStream.Read(mAxisY2); +} + +TwoBodyConstraint *FixedConstraintSettings::Create(Body &inBody1, Body &inBody2) const +{ + return new FixedConstraint(inBody1, inBody2, *this); +} + +FixedConstraint::FixedConstraint(Body &inBody1, Body &inBody2, const FixedConstraintSettings &inSettings) : + TwoBodyConstraint(inBody1, inBody2, inSettings) +{ + // Store inverse of initial rotation from body 1 to body 2 in body 1 space + mInvInitialOrientation = RotationEulerConstraintPart::sGetInvInitialOrientationXY(inSettings.mAxisX1, inSettings.mAxisY1, inSettings.mAxisX2, inSettings.mAxisY2); + + if (inSettings.mSpace == EConstraintSpace::WorldSpace) + { + if (inSettings.mAutoDetectPoint) + { + // Determine anchor point: If any of the bodies can never be dynamic use the other body as anchor point + RVec3 anchor; + if (!inBody1.CanBeKinematicOrDynamic()) + anchor = inBody2.GetCenterOfMassPosition(); + else if (!inBody2.CanBeKinematicOrDynamic()) + anchor = inBody1.GetCenterOfMassPosition(); + else + { + // Otherwise use weighted anchor point towards the lightest body + Real inv_m1 = Real(inBody1.GetMotionPropertiesUnchecked()->GetInverseMassUnchecked()); + Real inv_m2 = Real(inBody2.GetMotionPropertiesUnchecked()->GetInverseMassUnchecked()); + Real total_inv_mass = inv_m1 + inv_m2; + if (total_inv_mass != 0.0_r) + anchor = (inv_m1 * inBody1.GetCenterOfMassPosition() + inv_m2 * inBody2.GetCenterOfMassPosition()) / (inv_m1 + inv_m2); + else + anchor = inBody1.GetCenterOfMassPosition(); + } + + // Store local positions + mLocalSpacePosition1 = Vec3(inBody1.GetInverseCenterOfMassTransform() * anchor); + mLocalSpacePosition2 = Vec3(inBody2.GetInverseCenterOfMassTransform() * anchor); + } + else + { + // Store local positions + mLocalSpacePosition1 = Vec3(inBody1.GetInverseCenterOfMassTransform() * inSettings.mPoint1); + mLocalSpacePosition2 = Vec3(inBody2.GetInverseCenterOfMassTransform() * inSettings.mPoint2); + } + + // Constraints were specified in world space, so we should have replaced c1 with q10^-1 c1 and c2 with q20^-1 c2 + // => r0^-1 = (q20^-1 c2) (q10^-1 c1)^1 = q20^-1 (c2 c1^-1) q10 + mInvInitialOrientation = inBody2.GetRotation().Conjugated() * mInvInitialOrientation * inBody1.GetRotation(); + } + else + { + // Store local positions + mLocalSpacePosition1 = Vec3(inSettings.mPoint1); + mLocalSpacePosition2 = Vec3(inSettings.mPoint2); + } +} + +void FixedConstraint::NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM) +{ + if (mBody1->GetID() == inBodyID) + mLocalSpacePosition1 -= inDeltaCOM; + else if (mBody2->GetID() == inBodyID) + mLocalSpacePosition2 -= inDeltaCOM; +} + +void FixedConstraint::SetupVelocityConstraint(float inDeltaTime) +{ + // Calculate constraint values that don't change when the bodies don't change position + Mat44 rotation1 = Mat44::sRotation(mBody1->GetRotation()); + Mat44 rotation2 = Mat44::sRotation(mBody2->GetRotation()); + mRotationConstraintPart.CalculateConstraintProperties(*mBody1, rotation1, *mBody2, rotation2); + mPointConstraintPart.CalculateConstraintProperties(*mBody1, rotation1, mLocalSpacePosition1, *mBody2, rotation2, mLocalSpacePosition2); +} + +void FixedConstraint::ResetWarmStart() +{ + mRotationConstraintPart.Deactivate(); + mPointConstraintPart.Deactivate(); +} + +void FixedConstraint::WarmStartVelocityConstraint(float inWarmStartImpulseRatio) +{ + // Warm starting: Apply previous frame impulse + mRotationConstraintPart.WarmStart(*mBody1, *mBody2, inWarmStartImpulseRatio); + mPointConstraintPart.WarmStart(*mBody1, *mBody2, inWarmStartImpulseRatio); +} + +bool FixedConstraint::SolveVelocityConstraint(float inDeltaTime) +{ + // Solve rotation constraint + bool rot = mRotationConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2); + + // Solve position constraint + bool pos = mPointConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2); + + return rot || pos; +} + +bool FixedConstraint::SolvePositionConstraint(float inDeltaTime, float inBaumgarte) +{ + // Solve rotation constraint + mRotationConstraintPart.CalculateConstraintProperties(*mBody1, Mat44::sRotation(mBody1->GetRotation()), *mBody2, Mat44::sRotation(mBody2->GetRotation())); + bool rot = mRotationConstraintPart.SolvePositionConstraint(*mBody1, *mBody2, mInvInitialOrientation, inBaumgarte); + + // Solve position constraint + mPointConstraintPart.CalculateConstraintProperties(*mBody1, Mat44::sRotation(mBody1->GetRotation()), mLocalSpacePosition1, *mBody2, Mat44::sRotation(mBody2->GetRotation()), mLocalSpacePosition2); + bool pos = mPointConstraintPart.SolvePositionConstraint(*mBody1, *mBody2, inBaumgarte); + + return rot || pos; +} + +#ifdef JPH_DEBUG_RENDERER +void FixedConstraint::DrawConstraint(DebugRenderer *inRenderer) const +{ + RMat44 com1 = mBody1->GetCenterOfMassTransform(); + RMat44 com2 = mBody2->GetCenterOfMassTransform(); + + RVec3 anchor1 = com1 * mLocalSpacePosition1; + RVec3 anchor2 = com2 * mLocalSpacePosition2; + + // Draw constraint + inRenderer->DrawLine(com1.GetTranslation(), anchor1, Color::sGreen); + inRenderer->DrawLine(com2.GetTranslation(), anchor2, Color::sBlue); +} +#endif // JPH_DEBUG_RENDERER + +void FixedConstraint::SaveState(StateRecorder &inStream) const +{ + TwoBodyConstraint::SaveState(inStream); + + mRotationConstraintPart.SaveState(inStream); + mPointConstraintPart.SaveState(inStream); +} + +void FixedConstraint::RestoreState(StateRecorder &inStream) +{ + TwoBodyConstraint::RestoreState(inStream); + + mRotationConstraintPart.RestoreState(inStream); + mPointConstraintPart.RestoreState(inStream); +} + +Ref FixedConstraint::GetConstraintSettings() const +{ + FixedConstraintSettings *settings = new FixedConstraintSettings; + ToConstraintSettings(*settings); + settings->mSpace = EConstraintSpace::LocalToBodyCOM; + settings->mPoint1 = RVec3(mLocalSpacePosition1); + settings->mAxisX1 = Vec3::sAxisX(); + settings->mAxisY1 = Vec3::sAxisY(); + settings->mPoint2 = RVec3(mLocalSpacePosition2); + settings->mAxisX2 = mInvInitialOrientation.RotateAxisX(); + settings->mAxisY2 = mInvInitialOrientation.RotateAxisY(); + return settings; +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/FixedConstraint.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/FixedConstraint.h new file mode 100644 index 00000000000..16d54d3c6da --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/FixedConstraint.h @@ -0,0 +1,96 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +/// Fixed constraint settings, used to create a fixed constraint +class JPH_EXPORT FixedConstraintSettings final : public TwoBodyConstraintSettings +{ +public: + JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, FixedConstraintSettings) + + // See: ConstraintSettings::SaveBinaryState + virtual void SaveBinaryState(StreamOut &inStream) const override; + + /// Create an instance of this constraint + virtual TwoBodyConstraint * Create(Body &inBody1, Body &inBody2) const override; + + /// This determines in which space the constraint is setup, all properties below should be in the specified space + EConstraintSpace mSpace = EConstraintSpace::WorldSpace; + + /// When mSpace is WorldSpace mPoint1 and mPoint2 can be automatically calculated based on the positions of the bodies when the constraint is created (they will be fixated in their current relative position/orientation). Set this to false if you want to supply the attachment points yourself. + bool mAutoDetectPoint = false; + + /// Body 1 constraint reference frame (space determined by mSpace) + RVec3 mPoint1 = RVec3::sZero(); + Vec3 mAxisX1 = Vec3::sAxisX(); + Vec3 mAxisY1 = Vec3::sAxisY(); + + /// Body 2 constraint reference frame (space determined by mSpace) + RVec3 mPoint2 = RVec3::sZero(); + Vec3 mAxisX2 = Vec3::sAxisX(); + Vec3 mAxisY2 = Vec3::sAxisY(); + +protected: + // See: ConstraintSettings::RestoreBinaryState + virtual void RestoreBinaryState(StreamIn &inStream) override; +}; + +/// A fixed constraint welds two bodies together removing all degrees of freedom between them. +/// This variant uses Euler angles for the rotation constraint. +class JPH_EXPORT FixedConstraint final : public TwoBodyConstraint +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Constructor + FixedConstraint(Body &inBody1, Body &inBody2, const FixedConstraintSettings &inSettings); + + // Generic interface of a constraint + virtual EConstraintSubType GetSubType() const override { return EConstraintSubType::Fixed; } + virtual void NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM) override; + virtual void SetupVelocityConstraint(float inDeltaTime) override; + virtual void ResetWarmStart() override; + virtual void WarmStartVelocityConstraint(float inWarmStartImpulseRatio) override; + virtual bool SolveVelocityConstraint(float inDeltaTime) override; + virtual bool SolvePositionConstraint(float inDeltaTime, float inBaumgarte) override; +#ifdef JPH_DEBUG_RENDERER + virtual void DrawConstraint(DebugRenderer *inRenderer) const override; +#endif // JPH_DEBUG_RENDERER + virtual void SaveState(StateRecorder &inStream) const override; + virtual void RestoreState(StateRecorder &inStream) override; + virtual Ref GetConstraintSettings() const override; + + // See: TwoBodyConstraint + virtual Mat44 GetConstraintToBody1Matrix() const override { return Mat44::sTranslation(mLocalSpacePosition1); } + virtual Mat44 GetConstraintToBody2Matrix() const override { return Mat44::sRotationTranslation(mInvInitialOrientation, mLocalSpacePosition2); } + + ///@name Get Lagrange multiplier from last physics update (the linear/angular impulse applied to satisfy the constraint) + inline Vec3 GetTotalLambdaPosition() const { return mPointConstraintPart.GetTotalLambda(); } + inline Vec3 GetTotalLambdaRotation() const { return mRotationConstraintPart.GetTotalLambda(); } + +private: + // CONFIGURATION PROPERTIES FOLLOW + + // Local space constraint positions + Vec3 mLocalSpacePosition1; + Vec3 mLocalSpacePosition2; + + // Inverse of initial rotation from body 1 to body 2 in body 1 space + Quat mInvInitialOrientation; + + // RUN TIME PROPERTIES FOLLOW + + // The constraint parts + RotationEulerConstraintPart mRotationConstraintPart; + PointConstraintPart mPointConstraintPart; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/GearConstraint.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/GearConstraint.cpp new file mode 100644 index 00000000000..676bee65c64 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/GearConstraint.cpp @@ -0,0 +1,188 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#include +#include +#include +#ifdef JPH_DEBUG_RENDERER + #include +#endif // JPH_DEBUG_RENDERER + +JPH_NAMESPACE_BEGIN + +JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(GearConstraintSettings) +{ + JPH_ADD_BASE_CLASS(GearConstraintSettings, TwoBodyConstraintSettings) + + JPH_ADD_ENUM_ATTRIBUTE(GearConstraintSettings, mSpace) + JPH_ADD_ATTRIBUTE(GearConstraintSettings, mHingeAxis1) + JPH_ADD_ATTRIBUTE(GearConstraintSettings, mHingeAxis2) + JPH_ADD_ATTRIBUTE(GearConstraintSettings, mRatio) +} + +void GearConstraintSettings::SaveBinaryState(StreamOut &inStream) const +{ + ConstraintSettings::SaveBinaryState(inStream); + + inStream.Write(mSpace); + inStream.Write(mHingeAxis1); + inStream.Write(mHingeAxis2); + inStream.Write(mRatio); +} + +void GearConstraintSettings::RestoreBinaryState(StreamIn &inStream) +{ + ConstraintSettings::RestoreBinaryState(inStream); + + inStream.Read(mSpace); + inStream.Read(mHingeAxis1); + inStream.Read(mHingeAxis2); + inStream.Read(mRatio); +} + +TwoBodyConstraint *GearConstraintSettings::Create(Body &inBody1, Body &inBody2) const +{ + return new GearConstraint(inBody1, inBody2, *this); +} + +GearConstraint::GearConstraint(Body &inBody1, Body &inBody2, const GearConstraintSettings &inSettings) : + TwoBodyConstraint(inBody1, inBody2, inSettings), + mLocalSpaceHingeAxis1(inSettings.mHingeAxis1), + mLocalSpaceHingeAxis2(inSettings.mHingeAxis2), + mRatio(inSettings.mRatio) +{ + if (inSettings.mSpace == EConstraintSpace::WorldSpace) + { + // If all properties were specified in world space, take them to local space now + mLocalSpaceHingeAxis1 = inBody1.GetInverseCenterOfMassTransform().Multiply3x3(mLocalSpaceHingeAxis1).Normalized(); + mLocalSpaceHingeAxis2 = inBody2.GetInverseCenterOfMassTransform().Multiply3x3(mLocalSpaceHingeAxis2).Normalized(); + } +} + +void GearConstraint::CalculateConstraintProperties(Mat44Arg inRotation1, Mat44Arg inRotation2) +{ + // Calculate world space normals + mWorldSpaceHingeAxis1 = inRotation1 * mLocalSpaceHingeAxis1; + mWorldSpaceHingeAxis2 = inRotation2 * mLocalSpaceHingeAxis2; + + mGearConstraintPart.CalculateConstraintProperties(*mBody1, mWorldSpaceHingeAxis1, *mBody2, mWorldSpaceHingeAxis2, mRatio); +} + +void GearConstraint::SetupVelocityConstraint(float inDeltaTime) +{ + // Calculate constraint properties that are constant while bodies don't move + Mat44 rotation1 = Mat44::sRotation(mBody1->GetRotation()); + Mat44 rotation2 = Mat44::sRotation(mBody2->GetRotation()); + CalculateConstraintProperties(rotation1, rotation2); +} + +void GearConstraint::ResetWarmStart() +{ + mGearConstraintPart.Deactivate(); +} + +void GearConstraint::WarmStartVelocityConstraint(float inWarmStartImpulseRatio) +{ + // Warm starting: Apply previous frame impulse + mGearConstraintPart.WarmStart(*mBody1, *mBody2, inWarmStartImpulseRatio); +} + +bool GearConstraint::SolveVelocityConstraint(float inDeltaTime) +{ + return mGearConstraintPart.SolveVelocityConstraint(*mBody1, mWorldSpaceHingeAxis1, *mBody2, mWorldSpaceHingeAxis2, mRatio); +} + +bool GearConstraint::SolvePositionConstraint(float inDeltaTime, float inBaumgarte) +{ + if (mGear1Constraint == nullptr || mGear2Constraint == nullptr) + return false; + + float gear1rot; + if (mGear1Constraint->GetSubType() == EConstraintSubType::Hinge) + { + gear1rot = static_cast(mGear1Constraint.GetPtr())->GetCurrentAngle(); + } + else + { + JPH_ASSERT(false, "Unsupported"); + return false; + } + + float gear2rot; + if (mGear2Constraint->GetSubType() == EConstraintSubType::Hinge) + { + gear2rot = static_cast(mGear2Constraint.GetPtr())->GetCurrentAngle(); + } + else + { + JPH_ASSERT(false, "Unsupported"); + return false; + } + + float error = CenterAngleAroundZero(fmod(gear1rot + mRatio * gear2rot, 2.0f * JPH_PI)); + if (error == 0.0f) + return false; + + Mat44 rotation1 = Mat44::sRotation(mBody1->GetRotation()); + Mat44 rotation2 = Mat44::sRotation(mBody2->GetRotation()); + CalculateConstraintProperties(rotation1, rotation2); + return mGearConstraintPart.SolvePositionConstraint(*mBody1, *mBody2, error, inBaumgarte); +} + +#ifdef JPH_DEBUG_RENDERER +void GearConstraint::DrawConstraint(DebugRenderer *inRenderer) const +{ + RMat44 transform1 = mBody1->GetCenterOfMassTransform(); + RMat44 transform2 = mBody2->GetCenterOfMassTransform(); + + // Draw constraint axis + inRenderer->DrawArrow(transform1.GetTranslation(), transform1 * mLocalSpaceHingeAxis1, Color::sGreen, 0.01f); + inRenderer->DrawArrow(transform2.GetTranslation(), transform2 * mLocalSpaceHingeAxis2, Color::sBlue, 0.01f); +} + +#endif // JPH_DEBUG_RENDERER + +void GearConstraint::SaveState(StateRecorder &inStream) const +{ + TwoBodyConstraint::SaveState(inStream); + + mGearConstraintPart.SaveState(inStream); +} + +void GearConstraint::RestoreState(StateRecorder &inStream) +{ + TwoBodyConstraint::RestoreState(inStream); + + mGearConstraintPart.RestoreState(inStream); +} + +Ref GearConstraint::GetConstraintSettings() const +{ + GearConstraintSettings *settings = new GearConstraintSettings; + ToConstraintSettings(*settings); + settings->mSpace = EConstraintSpace::LocalToBodyCOM; + settings->mHingeAxis1 = mLocalSpaceHingeAxis1; + settings->mHingeAxis2 = mLocalSpaceHingeAxis2; + settings->mRatio = mRatio; + return settings; +} + +Mat44 GearConstraint::GetConstraintToBody1Matrix() const +{ + Vec3 perp = mLocalSpaceHingeAxis1.GetNormalizedPerpendicular(); + return Mat44(Vec4(mLocalSpaceHingeAxis1, 0), Vec4(perp, 0), Vec4(mLocalSpaceHingeAxis1.Cross(perp), 0), Vec4(0, 0, 0, 1)); +} + +Mat44 GearConstraint::GetConstraintToBody2Matrix() const +{ + Vec3 perp = mLocalSpaceHingeAxis2.GetNormalizedPerpendicular(); + return Mat44(Vec4(mLocalSpaceHingeAxis2, 0), Vec4(perp, 0), Vec4(mLocalSpaceHingeAxis2.Cross(perp), 0), Vec4(0, 0, 0, 1)); +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/GearConstraint.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/GearConstraint.h new file mode 100644 index 00000000000..8b6233b1aaf --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/GearConstraint.h @@ -0,0 +1,116 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +JPH_NAMESPACE_BEGIN + +/// Gear constraint settings +class JPH_EXPORT GearConstraintSettings final : public TwoBodyConstraintSettings +{ +public: + JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, GearConstraintSettings) + + // See: ConstraintSettings::SaveBinaryState + virtual void SaveBinaryState(StreamOut &inStream) const override; + + /// Create an instance of this constraint. + virtual TwoBodyConstraint * Create(Body &inBody1, Body &inBody2) const override; + + /// Defines the ratio between the rotation of both gears + /// The ratio is defined as: Gear1Rotation(t) = -ratio * Gear2Rotation(t) + /// @param inNumTeethGear1 Number of teeth that body 1 has + /// @param inNumTeethGear2 Number of teeth that body 2 has + void SetRatio(int inNumTeethGear1, int inNumTeethGear2) + { + mRatio = float(inNumTeethGear2) / float(inNumTeethGear1); + } + + /// This determines in which space the constraint is setup, all properties below should be in the specified space + EConstraintSpace mSpace = EConstraintSpace::WorldSpace; + + /// Body 1 constraint reference frame (space determined by mSpace). + Vec3 mHingeAxis1 = Vec3::sAxisX(); + + /// Body 2 constraint reference frame (space determined by mSpace) + Vec3 mHingeAxis2 = Vec3::sAxisX(); + + /// Ratio between both gears, see SetRatio. + float mRatio = 1.0f; + +protected: + // See: ConstraintSettings::RestoreBinaryState + virtual void RestoreBinaryState(StreamIn &inStream) override; +}; + +/// A gear constraint constrains the rotation of body1 to the rotation of body 2 using a gear. +/// Note that this constraint needs to be used in conjunction with a two hinge constraints. +class JPH_EXPORT GearConstraint final : public TwoBodyConstraint +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Construct gear constraint + GearConstraint(Body &inBody1, Body &inBody2, const GearConstraintSettings &inSettings); + + // Generic interface of a constraint + virtual EConstraintSubType GetSubType() const override { return EConstraintSubType::Gear; } + virtual void NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM) override { /* Do nothing */ } + virtual void SetupVelocityConstraint(float inDeltaTime) override; + virtual void ResetWarmStart() override; + virtual void WarmStartVelocityConstraint(float inWarmStartImpulseRatio) override; + virtual bool SolveVelocityConstraint(float inDeltaTime) override; + virtual bool SolvePositionConstraint(float inDeltaTime, float inBaumgarte) override; +#ifdef JPH_DEBUG_RENDERER + virtual void DrawConstraint(DebugRenderer *inRenderer) const override; +#endif // JPH_DEBUG_RENDERER + virtual void SaveState(StateRecorder &inStream) const override; + virtual void RestoreState(StateRecorder &inStream) override; + virtual Ref GetConstraintSettings() const override; + + // See: TwoBodyConstraint + virtual Mat44 GetConstraintToBody1Matrix() const override; + virtual Mat44 GetConstraintToBody2Matrix() const override; + + /// The constraints that constrain both gears (2 hinges), optional and used to calculate the rotation error and fix numerical drift. + void SetConstraints(const Constraint *inGear1, const Constraint *inGear2) { mGear1Constraint = inGear1; mGear2Constraint = inGear2; } + + ///@name Get Lagrange multiplier from last physics update (the angular impulse applied to satisfy the constraint) + inline float GetTotalLambda() const { return mGearConstraintPart.GetTotalLambda(); } + +private: + // Internal helper function to calculate the values below + void CalculateConstraintProperties(Mat44Arg inRotation1, Mat44Arg inRotation2); + + // CONFIGURATION PROPERTIES FOLLOW + + // Local space hinge axis for body 1 + Vec3 mLocalSpaceHingeAxis1; + + // Local space hinge axis for body 2 + Vec3 mLocalSpaceHingeAxis2; + + // Ratio between gear 1 and 2 + float mRatio; + + // The constraints that constrain both gears (2 hinges), optional and used to calculate the rotation error and fix numerical drift. + RefConst mGear1Constraint; + RefConst mGear2Constraint; + + // RUN TIME PROPERTIES FOLLOW + + // World space hinge axis for body 1 + Vec3 mWorldSpaceHingeAxis1; + + // World space hinge axis for body 2 + Vec3 mWorldSpaceHingeAxis2; + + // The constraint parts + GearConstraintPart mGearConstraintPart; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/HingeConstraint.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/HingeConstraint.cpp new file mode 100644 index 00000000000..82465736295 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/HingeConstraint.cpp @@ -0,0 +1,424 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#include +#include +#include +#ifdef JPH_DEBUG_RENDERER + #include +#endif // JPH_DEBUG_RENDERER + +JPH_NAMESPACE_BEGIN + +JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(HingeConstraintSettings) +{ + JPH_ADD_BASE_CLASS(HingeConstraintSettings, TwoBodyConstraintSettings) + + JPH_ADD_ENUM_ATTRIBUTE(HingeConstraintSettings, mSpace) + JPH_ADD_ATTRIBUTE(HingeConstraintSettings, mPoint1) + JPH_ADD_ATTRIBUTE(HingeConstraintSettings, mHingeAxis1) + JPH_ADD_ATTRIBUTE(HingeConstraintSettings, mNormalAxis1) + JPH_ADD_ATTRIBUTE(HingeConstraintSettings, mPoint2) + JPH_ADD_ATTRIBUTE(HingeConstraintSettings, mHingeAxis2) + JPH_ADD_ATTRIBUTE(HingeConstraintSettings, mNormalAxis2) + JPH_ADD_ATTRIBUTE(HingeConstraintSettings, mLimitsMin) + JPH_ADD_ATTRIBUTE(HingeConstraintSettings, mLimitsMax) + JPH_ADD_ATTRIBUTE(HingeConstraintSettings, mLimitsSpringSettings) + JPH_ADD_ATTRIBUTE(HingeConstraintSettings, mMaxFrictionTorque) + JPH_ADD_ATTRIBUTE(HingeConstraintSettings, mMotorSettings) +} + +void HingeConstraintSettings::SaveBinaryState(StreamOut &inStream) const +{ + ConstraintSettings::SaveBinaryState(inStream); + + inStream.Write(mSpace); + inStream.Write(mPoint1); + inStream.Write(mHingeAxis1); + inStream.Write(mNormalAxis1); + inStream.Write(mPoint2); + inStream.Write(mHingeAxis2); + inStream.Write(mNormalAxis2); + inStream.Write(mLimitsMin); + inStream.Write(mLimitsMax); + inStream.Write(mMaxFrictionTorque); + mLimitsSpringSettings.SaveBinaryState(inStream); + mMotorSettings.SaveBinaryState(inStream); +} + +void HingeConstraintSettings::RestoreBinaryState(StreamIn &inStream) +{ + ConstraintSettings::RestoreBinaryState(inStream); + + inStream.Read(mSpace); + inStream.Read(mPoint1); + inStream.Read(mHingeAxis1); + inStream.Read(mNormalAxis1); + inStream.Read(mPoint2); + inStream.Read(mHingeAxis2); + inStream.Read(mNormalAxis2); + inStream.Read(mLimitsMin); + inStream.Read(mLimitsMax); + inStream.Read(mMaxFrictionTorque); + mLimitsSpringSettings.RestoreBinaryState(inStream); + mMotorSettings.RestoreBinaryState(inStream);} + +TwoBodyConstraint *HingeConstraintSettings::Create(Body &inBody1, Body &inBody2) const +{ + return new HingeConstraint(inBody1, inBody2, *this); +} + +HingeConstraint::HingeConstraint(Body &inBody1, Body &inBody2, const HingeConstraintSettings &inSettings) : + TwoBodyConstraint(inBody1, inBody2, inSettings), + mMaxFrictionTorque(inSettings.mMaxFrictionTorque), + mMotorSettings(inSettings.mMotorSettings) +{ + // Store limits + JPH_ASSERT(inSettings.mLimitsMin != inSettings.mLimitsMax || inSettings.mLimitsSpringSettings.mFrequency > 0.0f, "Better use a fixed constraint in this case"); + SetLimits(inSettings.mLimitsMin, inSettings.mLimitsMax); + + // Store inverse of initial rotation from body 1 to body 2 in body 1 space + mInvInitialOrientation = RotationEulerConstraintPart::sGetInvInitialOrientationXZ(inSettings.mNormalAxis1, inSettings.mHingeAxis1, inSettings.mNormalAxis2, inSettings.mHingeAxis2); + + if (inSettings.mSpace == EConstraintSpace::WorldSpace) + { + // If all properties were specified in world space, take them to local space now + RMat44 inv_transform1 = inBody1.GetInverseCenterOfMassTransform(); + mLocalSpacePosition1 = Vec3(inv_transform1 * inSettings.mPoint1); + mLocalSpaceHingeAxis1 = inv_transform1.Multiply3x3(inSettings.mHingeAxis1).Normalized(); + mLocalSpaceNormalAxis1 = inv_transform1.Multiply3x3(inSettings.mNormalAxis1).Normalized(); + + RMat44 inv_transform2 = inBody2.GetInverseCenterOfMassTransform(); + mLocalSpacePosition2 = Vec3(inv_transform2 * inSettings.mPoint2); + mLocalSpaceHingeAxis2 = inv_transform2.Multiply3x3(inSettings.mHingeAxis2).Normalized(); + mLocalSpaceNormalAxis2 = inv_transform2.Multiply3x3(inSettings.mNormalAxis2).Normalized(); + + // Constraints were specified in world space, so we should have replaced c1 with q10^-1 c1 and c2 with q20^-1 c2 + // => r0^-1 = (q20^-1 c2) (q10^-1 c1)^1 = q20^-1 (c2 c1^-1) q10 + mInvInitialOrientation = inBody2.GetRotation().Conjugated() * mInvInitialOrientation * inBody1.GetRotation(); + } + else + { + mLocalSpacePosition1 = Vec3(inSettings.mPoint1); + mLocalSpaceHingeAxis1 = inSettings.mHingeAxis1; + mLocalSpaceNormalAxis1 = inSettings.mNormalAxis1; + + mLocalSpacePosition2 = Vec3(inSettings.mPoint2); + mLocalSpaceHingeAxis2 = inSettings.mHingeAxis2; + mLocalSpaceNormalAxis2 = inSettings.mNormalAxis2; + } + + // Store spring settings + SetLimitsSpringSettings(inSettings.mLimitsSpringSettings); +} + +void HingeConstraint::NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM) +{ + if (mBody1->GetID() == inBodyID) + mLocalSpacePosition1 -= inDeltaCOM; + else if (mBody2->GetID() == inBodyID) + mLocalSpacePosition2 -= inDeltaCOM; +} + +float HingeConstraint::GetCurrentAngle() const +{ + // See: CalculateA1AndTheta + Quat rotation1 = mBody1->GetRotation(); + Quat diff = mBody2->GetRotation() * mInvInitialOrientation * rotation1.Conjugated(); + return diff.GetRotationAngle(rotation1 * mLocalSpaceHingeAxis1); +} + +void HingeConstraint::SetLimits(float inLimitsMin, float inLimitsMax) +{ + JPH_ASSERT(inLimitsMin <= 0.0f && inLimitsMin >= -JPH_PI); + JPH_ASSERT(inLimitsMax >= 0.0f && inLimitsMax <= JPH_PI); + mLimitsMin = inLimitsMin; + mLimitsMax = inLimitsMax; + mHasLimits = mLimitsMin > -JPH_PI && mLimitsMax < JPH_PI; +} + +void HingeConstraint::CalculateA1AndTheta() +{ + if (mHasLimits || mMotorState != EMotorState::Off || mMaxFrictionTorque > 0.0f) + { + Quat rotation1 = mBody1->GetRotation(); + + // Calculate relative rotation in world space + // + // The rest rotation is: + // + // q2 = q1 r0 + // + // But the actual rotation is + // + // q2 = diff q1 r0 + // <=> diff = q2 r0^-1 q1^-1 + // + // Where: + // q1 = current rotation of body 1 + // q2 = current rotation of body 2 + // diff = relative rotation in world space + Quat diff = mBody2->GetRotation() * mInvInitialOrientation * rotation1.Conjugated(); + + // Calculate hinge axis in world space + mA1 = rotation1 * mLocalSpaceHingeAxis1; + + // Get rotation angle around the hinge axis + mTheta = diff.GetRotationAngle(mA1); + } +} + +void HingeConstraint::CalculateRotationLimitsConstraintProperties(float inDeltaTime) +{ + // Apply constraint if outside of limits + if (mHasLimits && (mTheta <= mLimitsMin || mTheta >= mLimitsMax)) + mRotationLimitsConstraintPart.CalculateConstraintPropertiesWithSettings(inDeltaTime, *mBody1, *mBody2, mA1, 0.0f, GetSmallestAngleToLimit(), mLimitsSpringSettings); + else + mRotationLimitsConstraintPart.Deactivate(); +} + +void HingeConstraint::CalculateMotorConstraintProperties(float inDeltaTime) +{ + switch (mMotorState) + { + case EMotorState::Off: + if (mMaxFrictionTorque > 0.0f) + mMotorConstraintPart.CalculateConstraintProperties(*mBody1, *mBody2, mA1); + else + mMotorConstraintPart.Deactivate(); + break; + + case EMotorState::Velocity: + mMotorConstraintPart.CalculateConstraintProperties(*mBody1, *mBody2, mA1, -mTargetAngularVelocity); + break; + + case EMotorState::Position: + if (mMotorSettings.mSpringSettings.HasStiffness()) + mMotorConstraintPart.CalculateConstraintPropertiesWithSettings(inDeltaTime, *mBody1, *mBody2, mA1, 0.0f, CenterAngleAroundZero(mTheta - mTargetAngle), mMotorSettings.mSpringSettings); + else + mMotorConstraintPart.Deactivate(); + break; + } +} + +void HingeConstraint::SetupVelocityConstraint(float inDeltaTime) +{ + // Cache constraint values that are valid until the bodies move + Mat44 rotation1 = Mat44::sRotation(mBody1->GetRotation()); + Mat44 rotation2 = Mat44::sRotation(mBody2->GetRotation()); + mPointConstraintPart.CalculateConstraintProperties(*mBody1, rotation1, mLocalSpacePosition1, *mBody2, rotation2, mLocalSpacePosition2); + mRotationConstraintPart.CalculateConstraintProperties(*mBody1, rotation1, rotation1.Multiply3x3(mLocalSpaceHingeAxis1), *mBody2, rotation2, rotation2.Multiply3x3(mLocalSpaceHingeAxis2)); + CalculateA1AndTheta(); + CalculateRotationLimitsConstraintProperties(inDeltaTime); + CalculateMotorConstraintProperties(inDeltaTime); +} + +void HingeConstraint::ResetWarmStart() +{ + mMotorConstraintPart.Deactivate(); + mPointConstraintPart.Deactivate(); + mRotationConstraintPart.Deactivate(); + mRotationLimitsConstraintPart.Deactivate(); +} + +void HingeConstraint::WarmStartVelocityConstraint(float inWarmStartImpulseRatio) +{ + // Warm starting: Apply previous frame impulse + mMotorConstraintPart.WarmStart(*mBody1, *mBody2, inWarmStartImpulseRatio); + mPointConstraintPart.WarmStart(*mBody1, *mBody2, inWarmStartImpulseRatio); + mRotationConstraintPart.WarmStart(*mBody1, *mBody2, inWarmStartImpulseRatio); + mRotationLimitsConstraintPart.WarmStart(*mBody1, *mBody2, inWarmStartImpulseRatio); +} + +float HingeConstraint::GetSmallestAngleToLimit() const +{ + float dist_to_min = CenterAngleAroundZero(mTheta - mLimitsMin); + float dist_to_max = CenterAngleAroundZero(mTheta - mLimitsMax); + return abs(dist_to_min) < abs(dist_to_max)? dist_to_min : dist_to_max; +} + +bool HingeConstraint::IsMinLimitClosest() const +{ + float dist_to_min = CenterAngleAroundZero(mTheta - mLimitsMin); + float dist_to_max = CenterAngleAroundZero(mTheta - mLimitsMax); + return abs(dist_to_min) < abs(dist_to_max); +} + +bool HingeConstraint::SolveVelocityConstraint(float inDeltaTime) +{ + // Solve motor + bool motor = false; + if (mMotorConstraintPart.IsActive()) + { + switch (mMotorState) + { + case EMotorState::Off: + { + float max_lambda = mMaxFrictionTorque * inDeltaTime; + motor = mMotorConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2, mA1, -max_lambda, max_lambda); + break; + } + + case EMotorState::Velocity: + case EMotorState::Position: + motor = mMotorConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2, mA1, inDeltaTime * mMotorSettings.mMinTorqueLimit, inDeltaTime * mMotorSettings.mMaxTorqueLimit); + break; + } + } + + // Solve point constraint + bool pos = mPointConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2); + + // Solve rotation constraint + bool rot = mRotationConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2); + + // Solve rotation limits + bool limit = false; + if (mRotationLimitsConstraintPart.IsActive()) + { + float min_lambda, max_lambda; + if (mLimitsMin == mLimitsMax) + { + min_lambda = -FLT_MAX; + max_lambda = FLT_MAX; + } + else if (IsMinLimitClosest()) + { + min_lambda = 0.0f; + max_lambda = FLT_MAX; + } + else + { + min_lambda = -FLT_MAX; + max_lambda = 0.0f; + } + limit = mRotationLimitsConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2, mA1, min_lambda, max_lambda); + } + + return motor || pos || rot || limit; +} + +bool HingeConstraint::SolvePositionConstraint(float inDeltaTime, float inBaumgarte) +{ + // Motor operates on velocities only, don't call SolvePositionConstraint + + // Solve point constraint + mPointConstraintPart.CalculateConstraintProperties(*mBody1, Mat44::sRotation(mBody1->GetRotation()), mLocalSpacePosition1, *mBody2, Mat44::sRotation(mBody2->GetRotation()), mLocalSpacePosition2); + bool pos = mPointConstraintPart.SolvePositionConstraint(*mBody1, *mBody2, inBaumgarte); + + // Solve rotation constraint + Mat44 rotation1 = Mat44::sRotation(mBody1->GetRotation()); // Note that previous call to GetRotation() is out of date since the rotation has changed + Mat44 rotation2 = Mat44::sRotation(mBody2->GetRotation()); + mRotationConstraintPart.CalculateConstraintProperties(*mBody1, rotation1, rotation1.Multiply3x3(mLocalSpaceHingeAxis1), *mBody2, rotation2, rotation2.Multiply3x3(mLocalSpaceHingeAxis2)); + bool rot = mRotationConstraintPart.SolvePositionConstraint(*mBody1, *mBody2, inBaumgarte); + + // Solve rotation limits + bool limit = false; + if (mHasLimits && mLimitsSpringSettings.mFrequency <= 0.0f) + { + CalculateA1AndTheta(); + CalculateRotationLimitsConstraintProperties(inDeltaTime); + if (mRotationLimitsConstraintPart.IsActive()) + limit = mRotationLimitsConstraintPart.SolvePositionConstraint(*mBody1, *mBody2, GetSmallestAngleToLimit(), inBaumgarte); + } + + return pos || rot || limit; +} + +#ifdef JPH_DEBUG_RENDERER +void HingeConstraint::DrawConstraint(DebugRenderer *inRenderer) const +{ + RMat44 transform1 = mBody1->GetCenterOfMassTransform(); + RMat44 transform2 = mBody2->GetCenterOfMassTransform(); + + // Draw constraint + RVec3 constraint_pos1 = transform1 * mLocalSpacePosition1; + inRenderer->DrawMarker(constraint_pos1, Color::sRed, 0.1f); + inRenderer->DrawLine(constraint_pos1, transform1 * (mLocalSpacePosition1 + mDrawConstraintSize * mLocalSpaceHingeAxis1), Color::sRed); + + RVec3 constraint_pos2 = transform2 * mLocalSpacePosition2; + inRenderer->DrawMarker(constraint_pos2, Color::sGreen, 0.1f); + inRenderer->DrawLine(constraint_pos2, transform2 * (mLocalSpacePosition2 + mDrawConstraintSize * mLocalSpaceHingeAxis2), Color::sGreen); + inRenderer->DrawLine(constraint_pos2, transform2 * (mLocalSpacePosition2 + mDrawConstraintSize * mLocalSpaceNormalAxis2), Color::sWhite); +} + +void HingeConstraint::DrawConstraintLimits(DebugRenderer *inRenderer) const +{ + if (mHasLimits && mLimitsMax > mLimitsMin) + { + // Get constraint properties in world space + RMat44 transform1 = mBody1->GetCenterOfMassTransform(); + RVec3 position1 = transform1 * mLocalSpacePosition1; + Vec3 hinge_axis1 = transform1.Multiply3x3(mLocalSpaceHingeAxis1); + Vec3 normal_axis1 = transform1.Multiply3x3(mLocalSpaceNormalAxis1); + + inRenderer->DrawPie(position1, mDrawConstraintSize, hinge_axis1, normal_axis1, mLimitsMin, mLimitsMax, Color::sPurple, DebugRenderer::ECastShadow::Off); + } +} +#endif // JPH_DEBUG_RENDERER + +void HingeConstraint::SaveState(StateRecorder &inStream) const +{ + TwoBodyConstraint::SaveState(inStream); + + mMotorConstraintPart.SaveState(inStream); + mRotationConstraintPart.SaveState(inStream); + mPointConstraintPart.SaveState(inStream); + mRotationLimitsConstraintPart.SaveState(inStream); + + inStream.Write(mMotorState); + inStream.Write(mTargetAngularVelocity); + inStream.Write(mTargetAngle); +} + +void HingeConstraint::RestoreState(StateRecorder &inStream) +{ + TwoBodyConstraint::RestoreState(inStream); + + mMotorConstraintPart.RestoreState(inStream); + mRotationConstraintPart.RestoreState(inStream); + mPointConstraintPart.RestoreState(inStream); + mRotationLimitsConstraintPart.RestoreState(inStream); + + inStream.Read(mMotorState); + inStream.Read(mTargetAngularVelocity); + inStream.Read(mTargetAngle); +} + + +Ref HingeConstraint::GetConstraintSettings() const +{ + HingeConstraintSettings *settings = new HingeConstraintSettings; + ToConstraintSettings(*settings); + settings->mSpace = EConstraintSpace::LocalToBodyCOM; + settings->mPoint1 = RVec3(mLocalSpacePosition1); + settings->mHingeAxis1 = mLocalSpaceHingeAxis1; + settings->mNormalAxis1 = mLocalSpaceNormalAxis1; + settings->mPoint2 = RVec3(mLocalSpacePosition2); + settings->mHingeAxis2 = mLocalSpaceHingeAxis2; + settings->mNormalAxis2 = mLocalSpaceNormalAxis2; + settings->mLimitsMin = mLimitsMin; + settings->mLimitsMax = mLimitsMax; + settings->mLimitsSpringSettings = mLimitsSpringSettings; + settings->mMaxFrictionTorque = mMaxFrictionTorque; + settings->mMotorSettings = mMotorSettings; + return settings; +} + +Mat44 HingeConstraint::GetConstraintToBody1Matrix() const +{ + return Mat44(Vec4(mLocalSpaceHingeAxis1, 0), Vec4(mLocalSpaceNormalAxis1, 0), Vec4(mLocalSpaceHingeAxis1.Cross(mLocalSpaceNormalAxis1), 0), Vec4(mLocalSpacePosition1, 1)); +} + +Mat44 HingeConstraint::GetConstraintToBody2Matrix() const +{ + return Mat44(Vec4(mLocalSpaceHingeAxis2, 0), Vec4(mLocalSpaceNormalAxis2, 0), Vec4(mLocalSpaceHingeAxis2.Cross(mLocalSpaceNormalAxis2), 0), Vec4(mLocalSpacePosition2, 1)); +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/HingeConstraint.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/HingeConstraint.h new file mode 100644 index 00000000000..edf48d0e02f --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/HingeConstraint.h @@ -0,0 +1,182 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +/// Hinge constraint settings, used to create a hinge constraint +class JPH_EXPORT HingeConstraintSettings final : public TwoBodyConstraintSettings +{ +public: + JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, HingeConstraintSettings) + + // See: ConstraintSettings::SaveBinaryState + virtual void SaveBinaryState(StreamOut &inStream) const override; + + /// Create an instance of this constraint + virtual TwoBodyConstraint * Create(Body &inBody1, Body &inBody2) const override; + + /// This determines in which space the constraint is setup, all properties below should be in the specified space + EConstraintSpace mSpace = EConstraintSpace::WorldSpace; + + /// Body 1 constraint reference frame (space determined by mSpace). + /// Hinge axis is the axis where rotation is allowed, normal axis defines the 0 angle of the hinge. + RVec3 mPoint1 = RVec3::sZero(); + Vec3 mHingeAxis1 = Vec3::sAxisY(); + Vec3 mNormalAxis1 = Vec3::sAxisX(); + + /// Body 2 constraint reference frame (space determined by mSpace) + RVec3 mPoint2 = RVec3::sZero(); + Vec3 mHingeAxis2 = Vec3::sAxisY(); + Vec3 mNormalAxis2 = Vec3::sAxisX(); + + /// Bodies are assumed to be placed so that the hinge angle = 0, movement will be limited between [mLimitsMin, mLimitsMax] where mLimitsMin e [-pi, 0] and mLimitsMax e [0, pi]. + /// Both angles are in radians. + float mLimitsMin = -JPH_PI; + float mLimitsMax = JPH_PI; + + /// When enabled, this makes the limits soft. When the constraint exceeds the limits, a spring force will pull it back. + SpringSettings mLimitsSpringSettings; + + /// Maximum amount of torque (N m) to apply as friction when the constraint is not powered by a motor + float mMaxFrictionTorque = 0.0f; + + /// In case the constraint is powered, this determines the motor settings around the hinge axis + MotorSettings mMotorSettings; + +protected: + // See: ConstraintSettings::RestoreBinaryState + virtual void RestoreBinaryState(StreamIn &inStream) override; +}; + +/// A hinge constraint constrains 2 bodies on a single point and allows only a single axis of rotation +class JPH_EXPORT HingeConstraint final : public TwoBodyConstraint +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Construct hinge constraint + HingeConstraint(Body &inBody1, Body &inBody2, const HingeConstraintSettings &inSettings); + + // Generic interface of a constraint + virtual EConstraintSubType GetSubType() const override { return EConstraintSubType::Hinge; } + virtual void NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM) override; + virtual void SetupVelocityConstraint(float inDeltaTime) override; + virtual void ResetWarmStart() override; + virtual void WarmStartVelocityConstraint(float inWarmStartImpulseRatio) override; + virtual bool SolveVelocityConstraint(float inDeltaTime) override; + virtual bool SolvePositionConstraint(float inDeltaTime, float inBaumgarte) override; +#ifdef JPH_DEBUG_RENDERER + virtual void DrawConstraint(DebugRenderer *inRenderer) const override; + virtual void DrawConstraintLimits(DebugRenderer *inRenderer) const override; +#endif // JPH_DEBUG_RENDERER + virtual void SaveState(StateRecorder &inStream) const override; + virtual void RestoreState(StateRecorder &inStream) override; + virtual Ref GetConstraintSettings() const override; + + // See: TwoBodyConstraint + virtual Mat44 GetConstraintToBody1Matrix() const override; + virtual Mat44 GetConstraintToBody2Matrix() const override; + + /// Get the current rotation angle from the rest position + float GetCurrentAngle() const; + + // Friction control + void SetMaxFrictionTorque(float inFrictionTorque) { mMaxFrictionTorque = inFrictionTorque; } + float GetMaxFrictionTorque() const { return mMaxFrictionTorque; } + + // Motor settings + MotorSettings & GetMotorSettings() { return mMotorSettings; } + const MotorSettings & GetMotorSettings() const { return mMotorSettings; } + + // Motor controls + void SetMotorState(EMotorState inState) { JPH_ASSERT(inState == EMotorState::Off || mMotorSettings.IsValid()); mMotorState = inState; } + EMotorState GetMotorState() const { return mMotorState; } + void SetTargetAngularVelocity(float inAngularVelocity) { mTargetAngularVelocity = inAngularVelocity; } ///< rad/s + float GetTargetAngularVelocity() const { return mTargetAngularVelocity; } + void SetTargetAngle(float inAngle) { mTargetAngle = mHasLimits? Clamp(inAngle, mLimitsMin, mLimitsMax) : inAngle; } ///< rad + float GetTargetAngle() const { return mTargetAngle; } + + /// Update the rotation limits of the hinge, value in radians (see HingeConstraintSettings) + void SetLimits(float inLimitsMin, float inLimitsMax); + float GetLimitsMin() const { return mLimitsMin; } + float GetLimitsMax() const { return mLimitsMax; } + bool HasLimits() const { return mHasLimits; } + + /// Update the limits spring settings + const SpringSettings & GetLimitsSpringSettings() const { return mLimitsSpringSettings; } + SpringSettings & GetLimitsSpringSettings() { return mLimitsSpringSettings; } + void SetLimitsSpringSettings(const SpringSettings &inLimitsSpringSettings) { mLimitsSpringSettings = inLimitsSpringSettings; } + + ///@name Get Lagrange multiplier from last physics update (the linear/angular impulse applied to satisfy the constraint) + inline Vec3 GetTotalLambdaPosition() const { return mPointConstraintPart.GetTotalLambda(); } + inline Vector<2> GetTotalLambdaRotation() const { return mRotationConstraintPart.GetTotalLambda(); } + inline float GetTotalLambdaRotationLimits() const { return mRotationLimitsConstraintPart.GetTotalLambda(); } + inline float GetTotalLambdaMotor() const { return mMotorConstraintPart.GetTotalLambda(); } + +private: + // Internal helper function to calculate the values below + void CalculateA1AndTheta(); + void CalculateRotationLimitsConstraintProperties(float inDeltaTime); + void CalculateMotorConstraintProperties(float inDeltaTime); + inline float GetSmallestAngleToLimit() const; + inline bool IsMinLimitClosest() const; + + // CONFIGURATION PROPERTIES FOLLOW + + // Local space constraint positions + Vec3 mLocalSpacePosition1; + Vec3 mLocalSpacePosition2; + + // Local space hinge directions + Vec3 mLocalSpaceHingeAxis1; + Vec3 mLocalSpaceHingeAxis2; + + // Local space normal direction (direction relative to which to draw constraint limits) + Vec3 mLocalSpaceNormalAxis1; + Vec3 mLocalSpaceNormalAxis2; + + // Inverse of initial relative orientation between bodies (which defines hinge angle = 0) + Quat mInvInitialOrientation; + + // Hinge limits + bool mHasLimits; + float mLimitsMin; + float mLimitsMax; + + // Soft constraint limits + SpringSettings mLimitsSpringSettings; + + // Friction + float mMaxFrictionTorque; + + // Motor controls + MotorSettings mMotorSettings; + EMotorState mMotorState = EMotorState::Off; + float mTargetAngularVelocity = 0.0f; + float mTargetAngle = 0.0f; + + // RUN TIME PROPERTIES FOLLOW + + // Current rotation around the hinge axis + float mTheta = 0.0f; + + // World space hinge axis for body 1 + Vec3 mA1; + + // The constraint parts + PointConstraintPart mPointConstraintPart; + HingeRotationConstraintPart mRotationConstraintPart; + AngleConstraintPart mRotationLimitsConstraintPart; + AngleConstraintPart mMotorConstraintPart; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/MotorSettings.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/MotorSettings.cpp new file mode 100644 index 00000000000..e4daecdd7d5 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/MotorSettings.cpp @@ -0,0 +1,43 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(MotorSettings) +{ + JPH_ADD_ENUM_ATTRIBUTE_WITH_ALIAS(MotorSettings, mSpringSettings.mMode, "mSpringMode") + JPH_ADD_ATTRIBUTE_WITH_ALIAS(MotorSettings, mSpringSettings.mFrequency, "mFrequency") // Renaming attributes to stay compatible with old versions of the library + JPH_ADD_ATTRIBUTE_WITH_ALIAS(MotorSettings, mSpringSettings.mDamping, "mDamping") + JPH_ADD_ATTRIBUTE(MotorSettings, mMinForceLimit) + JPH_ADD_ATTRIBUTE(MotorSettings, mMaxForceLimit) + JPH_ADD_ATTRIBUTE(MotorSettings, mMinTorqueLimit) + JPH_ADD_ATTRIBUTE(MotorSettings, mMaxTorqueLimit) +} + +void MotorSettings::SaveBinaryState(StreamOut &inStream) const +{ + mSpringSettings.SaveBinaryState(inStream); + inStream.Write(mMinForceLimit); + inStream.Write(mMaxForceLimit); + inStream.Write(mMinTorqueLimit); + inStream.Write(mMaxTorqueLimit); +} + +void MotorSettings::RestoreBinaryState(StreamIn &inStream) +{ + mSpringSettings.RestoreBinaryState(inStream); + inStream.Read(mMinForceLimit); + inStream.Read(mMaxForceLimit); + inStream.Read(mMinTorqueLimit); + inStream.Read(mMaxTorqueLimit); +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/MotorSettings.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/MotorSettings.h new file mode 100644 index 00000000000..99484b7d005 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/MotorSettings.h @@ -0,0 +1,66 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +class StreamIn; +class StreamOut; + +enum class EMotorState +{ + Off, ///< Motor is off + Velocity, ///< Motor will drive to target velocity + Position ///< Motor will drive to target position +}; + +/// Class that contains the settings for a constraint motor. +/// See the main page of the API documentation for more information on how to configure a motor. +class JPH_EXPORT MotorSettings +{ +public: + JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, MotorSettings) + + /// Constructor + MotorSettings() = default; + MotorSettings(const MotorSettings &) = default; + MotorSettings & operator = (const MotorSettings &) = default; + MotorSettings(float inFrequency, float inDamping) : mSpringSettings(ESpringMode::FrequencyAndDamping, inFrequency, inDamping) { JPH_ASSERT(IsValid()); } + MotorSettings(float inFrequency, float inDamping, float inForceLimit, float inTorqueLimit) : mSpringSettings(ESpringMode::FrequencyAndDamping, inFrequency, inDamping), mMinForceLimit(-inForceLimit), mMaxForceLimit(inForceLimit), mMinTorqueLimit(-inTorqueLimit), mMaxTorqueLimit(inTorqueLimit) { JPH_ASSERT(IsValid()); } + + /// Set asymmetric force limits + void SetForceLimits(float inMin, float inMax) { JPH_ASSERT(inMin <= inMax); mMinForceLimit = inMin; mMaxForceLimit = inMax; } + + /// Set asymmetric torque limits + void SetTorqueLimits(float inMin, float inMax) { JPH_ASSERT(inMin <= inMax); mMinTorqueLimit = inMin; mMaxTorqueLimit = inMax; } + + /// Set symmetric force limits + void SetForceLimit(float inLimit) { mMinForceLimit = -inLimit; mMaxForceLimit = inLimit; } + + /// Set symmetric torque limits + void SetTorqueLimit(float inLimit) { mMinTorqueLimit = -inLimit; mMaxTorqueLimit = inLimit; } + + /// Check if settings are valid + bool IsValid() const { return mSpringSettings.mFrequency >= 0.0f && mSpringSettings.mDamping >= 0.0f && mMinForceLimit <= mMaxForceLimit && mMinTorqueLimit <= mMaxTorqueLimit; } + + /// Saves the contents of the motor settings in binary form to inStream. + void SaveBinaryState(StreamOut &inStream) const; + + /// Restores contents from the binary stream inStream. + void RestoreBinaryState(StreamIn &inStream); + + // Settings + SpringSettings mSpringSettings { ESpringMode::FrequencyAndDamping, 2.0f, 1.0f }; ///< Settings for the spring that is used to drive to the position target (not used when motor is a velocity motor). + float mMinForceLimit = -FLT_MAX; ///< Minimum force to apply in case of a linear constraint (N). Usually this is -mMaxForceLimit unless you want a motor that can e.g. push but not pull. Not used when motor is an angular motor. + float mMaxForceLimit = FLT_MAX; ///< Maximum force to apply in case of a linear constraint (N). Not used when motor is an angular motor. + float mMinTorqueLimit = -FLT_MAX; ///< Minimum torque to apply in case of a angular constraint (N m). Usually this is -mMaxTorqueLimit unless you want a motor that can e.g. push but not pull. Not used when motor is a position motor. + float mMaxTorqueLimit = FLT_MAX; ///< Maximum torque to apply in case of a angular constraint (N m). Not used when motor is a position motor. +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PathConstraint.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PathConstraint.cpp new file mode 100644 index 00000000000..e5b6eb7da03 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PathConstraint.cpp @@ -0,0 +1,458 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#include +#include +#include +#ifdef JPH_DEBUG_RENDERER + #include +#endif // JPH_DEBUG_RENDERER + +JPH_NAMESPACE_BEGIN + +JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(PathConstraintSettings) +{ + JPH_ADD_BASE_CLASS(PathConstraintSettings, TwoBodyConstraintSettings) + + JPH_ADD_ATTRIBUTE(PathConstraintSettings, mPath) + JPH_ADD_ATTRIBUTE(PathConstraintSettings, mPathPosition) + JPH_ADD_ATTRIBUTE(PathConstraintSettings, mPathRotation) + JPH_ADD_ATTRIBUTE(PathConstraintSettings, mPathFraction) + JPH_ADD_ATTRIBUTE(PathConstraintSettings, mMaxFrictionForce) + JPH_ADD_ATTRIBUTE(PathConstraintSettings, mPositionMotorSettings) + JPH_ADD_ENUM_ATTRIBUTE(PathConstraintSettings, mRotationConstraintType) +} + +void PathConstraintSettings::SaveBinaryState(StreamOut &inStream) const +{ + ConstraintSettings::SaveBinaryState(inStream); + + mPath->SaveBinaryState(inStream); + inStream.Write(mPathPosition); + inStream.Write(mPathRotation); + inStream.Write(mPathFraction); + inStream.Write(mMaxFrictionForce); + inStream.Write(mRotationConstraintType); + mPositionMotorSettings.SaveBinaryState(inStream); +} + +void PathConstraintSettings::RestoreBinaryState(StreamIn &inStream) +{ + ConstraintSettings::RestoreBinaryState(inStream); + + PathConstraintPath::PathResult result = PathConstraintPath::sRestoreFromBinaryState(inStream); + if (!result.HasError()) + mPath = result.Get(); + inStream.Read(mPathPosition); + inStream.Read(mPathRotation); + inStream.Read(mPathFraction); + inStream.Read(mMaxFrictionForce); + inStream.Read(mRotationConstraintType); + mPositionMotorSettings.RestoreBinaryState(inStream); +} + +TwoBodyConstraint *PathConstraintSettings::Create(Body &inBody1, Body &inBody2) const +{ + return new PathConstraint(inBody1, inBody2, *this); +} + +PathConstraint::PathConstraint(Body &inBody1, Body &inBody2, const PathConstraintSettings &inSettings) : + TwoBodyConstraint(inBody1, inBody2, inSettings), + mRotationConstraintType(inSettings.mRotationConstraintType), + mMaxFrictionForce(inSettings.mMaxFrictionForce), + mPositionMotorSettings(inSettings.mPositionMotorSettings) +{ + // Calculate transform that takes us from the path start to center of mass space of body 1 + mPathToBody1 = Mat44::sRotationTranslation(inSettings.mPathRotation, inSettings.mPathPosition - inBody1.GetShape()->GetCenterOfMass()); + + SetPath(inSettings.mPath, inSettings.mPathFraction); +} + +void PathConstraint::NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM) +{ + if (mBody1->GetID() == inBodyID) + mPathToBody1.SetTranslation(mPathToBody1.GetTranslation() - inDeltaCOM); + else if (mBody2->GetID() == inBodyID) + mPathToBody2.SetTranslation(mPathToBody2.GetTranslation() - inDeltaCOM); +} + +void PathConstraint::SetPath(const PathConstraintPath *inPath, float inPathFraction) +{ + mPath = inPath; + mPathFraction = inPathFraction; + + if (mPath != nullptr) + { + // Get the point on the path for this fraction + Vec3 path_point, path_tangent, path_normal, path_binormal; + mPath->GetPointOnPath(mPathFraction, path_point, path_tangent, path_normal, path_binormal); + + // Construct the matrix that takes us from the closest point on the path to body 2 center of mass space + Mat44 closest_point_to_path(Vec4(path_tangent, 0), Vec4(path_binormal, 0), Vec4(path_normal, 0), Vec4(path_point, 1)); + Mat44 cp_to_body1 = mPathToBody1 * closest_point_to_path; + mPathToBody2 = (mBody2->GetInverseCenterOfMassTransform() * mBody1->GetCenterOfMassTransform()).ToMat44() * cp_to_body1; + + // Calculate initial orientation + if (mRotationConstraintType == EPathRotationConstraintType::FullyConstrained) + mInvInitialOrientation = RotationEulerConstraintPart::sGetInvInitialOrientation(*mBody1, *mBody2); + } +} + +void PathConstraint::CalculateConstraintProperties(float inDeltaTime) +{ + // Get transforms of body 1 and 2 + RMat44 transform1 = mBody1->GetCenterOfMassTransform(); + RMat44 transform2 = mBody2->GetCenterOfMassTransform(); + + // Get the transform of the path transform as seen from body 1 in world space + RMat44 path_to_world_1 = transform1 * mPathToBody1; + + // Get the transform of from the point on path that body 2 is attached to in world space + RMat44 path_to_world_2 = transform2 * mPathToBody2; + + // Calculate new closest point on path + RVec3 position2 = path_to_world_2.GetTranslation(); + Vec3 position2_local_to_path = Vec3(path_to_world_1.InversedRotationTranslation() * position2); + mPathFraction = mPath->GetClosestPoint(position2_local_to_path, mPathFraction); + + // Get the point on the path for this fraction + Vec3 path_point, path_tangent, path_normal, path_binormal; + mPath->GetPointOnPath(mPathFraction, path_point, path_tangent, path_normal, path_binormal); + + // Calculate R1 and R2 + RVec3 path_point_ws = path_to_world_1 * path_point; + mR1 = Vec3(path_point_ws - mBody1->GetCenterOfMassPosition()); + mR2 = Vec3(position2 - mBody2->GetCenterOfMassPosition()); + + // Calculate U = X2 + R2 - X1 - R1 + mU = Vec3(position2 - path_point_ws); + + // Calculate world space normals + mPathNormal = path_to_world_1.Multiply3x3(path_normal); + mPathBinormal = path_to_world_1.Multiply3x3(path_binormal); + + // Calculate slide axis + mPathTangent = path_to_world_1.Multiply3x3(path_tangent); + + // Prepare constraint part for position constraint to slide along the path + mPositionConstraintPart.CalculateConstraintProperties(*mBody1, transform1.GetRotation(), mR1 + mU, *mBody2, transform2.GetRotation(), mR2, mPathNormal, mPathBinormal); + + // Check if closest point is on the boundary of the path and if so apply limit + if (!mPath->IsLooping() && (mPathFraction <= 0.0f || mPathFraction >= mPath->GetPathMaxFraction())) + mPositionLimitsConstraintPart.CalculateConstraintProperties(*mBody1, mR1 + mU, *mBody2, mR2, mPathTangent); + else + mPositionLimitsConstraintPart.Deactivate(); + + // Prepare rotation constraint part + switch (mRotationConstraintType) + { + case EPathRotationConstraintType::Free: + // No rotational limits + break; + + case EPathRotationConstraintType::ConstrainAroundTangent: + mHingeConstraintPart.CalculateConstraintProperties(*mBody1, transform1.GetRotation(), mPathTangent, *mBody2, transform2.GetRotation(), path_to_world_2.GetAxisX()); + break; + + case EPathRotationConstraintType::ConstrainAroundNormal: + mHingeConstraintPart.CalculateConstraintProperties(*mBody1, transform1.GetRotation(), mPathNormal, *mBody2, transform2.GetRotation(), path_to_world_2.GetAxisZ()); + break; + + case EPathRotationConstraintType::ConstrainAroundBinormal: + mHingeConstraintPart.CalculateConstraintProperties(*mBody1, transform1.GetRotation(), mPathBinormal, *mBody2, transform2.GetRotation(), path_to_world_2.GetAxisY()); + break; + + case EPathRotationConstraintType::ConstrainToPath: + // We need to calculate the inverse of the rotation from body 1 to body 2 for the current path position (see: RotationEulerConstraintPart::sGetInvInitialOrientation) + // RotationBody2 = RotationBody1 * InitialOrientation <=> InitialOrientation^-1 = RotationBody2^-1 * RotationBody1 + // We can express RotationBody2 in terms of RotationBody1: RotationBody2 = RotationBody1 * PathToBody1 * RotationClosestPointOnPath * PathToBody2^-1 + // Combining these two: InitialOrientation^-1 = PathToBody2 * (PathToBody1 * RotationClosestPointOnPath)^-1 + mInvInitialOrientation = mPathToBody2.Multiply3x3RightTransposed(mPathToBody1.Multiply3x3(Mat44(Vec4(path_tangent, 0), Vec4(path_binormal, 0), Vec4(path_normal, 0), Vec4::sZero()))).GetQuaternion(); + [[fallthrough]]; + + case EPathRotationConstraintType::FullyConstrained: + mRotationConstraintPart.CalculateConstraintProperties(*mBody1, transform1.GetRotation(), *mBody2, transform2.GetRotation()); + break; + } + + // Motor properties + switch (mPositionMotorState) + { + case EMotorState::Off: + if (mMaxFrictionForce > 0.0f) + mPositionMotorConstraintPart.CalculateConstraintProperties(*mBody1, mR1 + mU, *mBody2, mR2, mPathTangent); + else + mPositionMotorConstraintPart.Deactivate(); + break; + + case EMotorState::Velocity: + mPositionMotorConstraintPart.CalculateConstraintProperties(*mBody1, mR1 + mU, *mBody2, mR2, mPathTangent, -mTargetVelocity); + break; + + case EMotorState::Position: + if (mPositionMotorSettings.mSpringSettings.HasStiffness()) + { + // Calculate constraint value to drive to + float c; + if (mPath->IsLooping()) + { + float max_fraction = mPath->GetPathMaxFraction(); + c = fmod(mPathFraction - mTargetPathFraction, max_fraction); + float half_max_fraction = 0.5f * max_fraction; + if (c > half_max_fraction) + c -= max_fraction; + else if (c < -half_max_fraction) + c += max_fraction; + } + else + c = mPathFraction - mTargetPathFraction; + mPositionMotorConstraintPart.CalculateConstraintPropertiesWithSettings(inDeltaTime, *mBody1, mR1 + mU, *mBody2, mR2, mPathTangent, 0.0f, c, mPositionMotorSettings.mSpringSettings); + } + else + mPositionMotorConstraintPart.Deactivate(); + break; + } +} + +void PathConstraint::SetupVelocityConstraint(float inDeltaTime) +{ + CalculateConstraintProperties(inDeltaTime); +} + +void PathConstraint::ResetWarmStart() +{ + mPositionMotorConstraintPart.Deactivate(); + mPositionConstraintPart.Deactivate(); + mPositionLimitsConstraintPart.Deactivate(); + mHingeConstraintPart.Deactivate(); + mRotationConstraintPart.Deactivate(); +} + +void PathConstraint::WarmStartVelocityConstraint(float inWarmStartImpulseRatio) +{ + // Warm starting: Apply previous frame impulse + mPositionMotorConstraintPart.WarmStart(*mBody1, *mBody2, mPathTangent, inWarmStartImpulseRatio); + mPositionConstraintPart.WarmStart(*mBody1, *mBody2, mPathNormal, mPathBinormal, inWarmStartImpulseRatio); + mPositionLimitsConstraintPart.WarmStart(*mBody1, *mBody2, mPathTangent, inWarmStartImpulseRatio); + + switch (mRotationConstraintType) + { + case EPathRotationConstraintType::Free: + // No rotational limits + break; + + case EPathRotationConstraintType::ConstrainAroundTangent: + case EPathRotationConstraintType::ConstrainAroundNormal: + case EPathRotationConstraintType::ConstrainAroundBinormal: + mHingeConstraintPart.WarmStart(*mBody1, *mBody2, inWarmStartImpulseRatio); + break; + + case EPathRotationConstraintType::ConstrainToPath: + case EPathRotationConstraintType::FullyConstrained: + mRotationConstraintPart.WarmStart(*mBody1, *mBody2, inWarmStartImpulseRatio); + break; + } +} + +bool PathConstraint::SolveVelocityConstraint(float inDeltaTime) +{ + // Solve motor + bool motor = false; + if (mPositionMotorConstraintPart.IsActive()) + { + switch (mPositionMotorState) + { + case EMotorState::Off: + { + float max_lambda = mMaxFrictionForce * inDeltaTime; + motor = mPositionMotorConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2, mPathTangent, -max_lambda, max_lambda); + break; + } + + case EMotorState::Velocity: + case EMotorState::Position: + motor = mPositionMotorConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2, mPathTangent, inDeltaTime * mPositionMotorSettings.mMinForceLimit, inDeltaTime * mPositionMotorSettings.mMaxForceLimit); + break; + } + } + + // Solve position constraint along 2 axis + bool pos = mPositionConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2, mPathNormal, mPathBinormal); + + // Solve limits along path axis + bool limit = false; + if (mPositionLimitsConstraintPart.IsActive()) + { + if (mPathFraction <= 0.0f) + limit = mPositionLimitsConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2, mPathTangent, 0, FLT_MAX); + else + { + JPH_ASSERT(mPathFraction >= mPath->GetPathMaxFraction()); + limit = mPositionLimitsConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2, mPathTangent, -FLT_MAX, 0); + } + } + + // Solve rotational constraint + // Note, this is not entirely correct, we should apply a velocity constraint so that the body will actually follow the path + // by looking at the derivative of the tangent, normal or binormal but we don't. This means the position constraint solver + // will need to correct the orientation error that builds up, which in turn means that the simulation is not physically correct. + bool rot = false; + switch (mRotationConstraintType) + { + case EPathRotationConstraintType::Free: + // No rotational limits + break; + + case EPathRotationConstraintType::ConstrainAroundTangent: + case EPathRotationConstraintType::ConstrainAroundNormal: + case EPathRotationConstraintType::ConstrainAroundBinormal: + rot = mHingeConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2); + break; + + case EPathRotationConstraintType::ConstrainToPath: + case EPathRotationConstraintType::FullyConstrained: + rot = mRotationConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2); + break; + } + + return motor || pos || limit || rot; +} + +bool PathConstraint::SolvePositionConstraint(float inDeltaTime, float inBaumgarte) +{ + // Update constraint properties (bodies may have moved) + CalculateConstraintProperties(inDeltaTime); + + // Solve position constraint along 2 axis + bool pos = mPositionConstraintPart.SolvePositionConstraint(*mBody1, *mBody2, mU, mPathNormal, mPathBinormal, inBaumgarte); + + // Solve limits along path axis + bool limit = false; + if (mPositionLimitsConstraintPart.IsActive()) + { + if (mPathFraction <= 0.0f) + limit = mPositionLimitsConstraintPart.SolvePositionConstraint(*mBody1, *mBody2, mPathTangent, mU.Dot(mPathTangent), inBaumgarte); + else + { + JPH_ASSERT(mPathFraction >= mPath->GetPathMaxFraction()); + limit = mPositionLimitsConstraintPart.SolvePositionConstraint(*mBody1, *mBody2, mPathTangent, mU.Dot(mPathTangent), inBaumgarte); + } + } + + // Solve rotational constraint + bool rot = false; + switch (mRotationConstraintType) + { + case EPathRotationConstraintType::Free: + // No rotational limits + break; + + case EPathRotationConstraintType::ConstrainAroundTangent: + case EPathRotationConstraintType::ConstrainAroundNormal: + case EPathRotationConstraintType::ConstrainAroundBinormal: + rot = mHingeConstraintPart.SolvePositionConstraint(*mBody1, *mBody2, inBaumgarte); + break; + + case EPathRotationConstraintType::ConstrainToPath: + case EPathRotationConstraintType::FullyConstrained: + rot = mRotationConstraintPart.SolvePositionConstraint(*mBody1, *mBody2, mInvInitialOrientation, inBaumgarte); + break; + } + + return pos || limit || rot; +} + +#ifdef JPH_DEBUG_RENDERER +void PathConstraint::DrawConstraint(DebugRenderer *inRenderer) const +{ + if (mPath != nullptr) + { + // Draw the path in world space + RMat44 path_to_world = mBody1->GetCenterOfMassTransform() * mPathToBody1; + mPath->DrawPath(inRenderer, path_to_world); + + // Draw anchor point of both bodies in world space + RVec3 x1 = mBody1->GetCenterOfMassPosition() + mR1; + RVec3 x2 = mBody2->GetCenterOfMassPosition() + mR2; + inRenderer->DrawMarker(x1, Color::sYellow, 0.1f); + inRenderer->DrawMarker(x2, Color::sYellow, 0.1f); + inRenderer->DrawArrow(x1, x1 + mPathTangent, Color::sBlue, 0.1f); + inRenderer->DrawArrow(x1, x1 + mPathNormal, Color::sRed, 0.1f); + inRenderer->DrawArrow(x1, x1 + mPathBinormal, Color::sGreen, 0.1f); + inRenderer->DrawText3D(x1, StringFormat("%.1f", (double)mPathFraction)); + + // Draw motor + switch (mPositionMotorState) + { + case EMotorState::Position: + { + // Draw target marker + Vec3 position, tangent, normal, binormal; + mPath->GetPointOnPath(mTargetPathFraction, position, tangent, normal, binormal); + inRenderer->DrawMarker(path_to_world * position, Color::sYellow, 1.0f); + break; + } + + case EMotorState::Velocity: + { + RVec3 position = mBody2->GetCenterOfMassPosition() + mR2; + inRenderer->DrawArrow(position, position + mPathTangent * mTargetVelocity, Color::sRed, 0.1f); + break; + } + + case EMotorState::Off: + break; + } + } +} +#endif // JPH_DEBUG_RENDERER + +void PathConstraint::SaveState(StateRecorder &inStream) const +{ + TwoBodyConstraint::SaveState(inStream); + + mPositionConstraintPart.SaveState(inStream); + mPositionLimitsConstraintPart.SaveState(inStream); + mPositionMotorConstraintPart.SaveState(inStream); + mHingeConstraintPart.SaveState(inStream); + mRotationConstraintPart.SaveState(inStream); + + inStream.Write(mMaxFrictionForce); + inStream.Write(mPositionMotorSettings); + inStream.Write(mPositionMotorState); + inStream.Write(mTargetVelocity); + inStream.Write(mTargetPathFraction); + inStream.Write(mPathFraction); +} + +void PathConstraint::RestoreState(StateRecorder &inStream) +{ + TwoBodyConstraint::RestoreState(inStream); + + mPositionConstraintPart.RestoreState(inStream); + mPositionLimitsConstraintPart.RestoreState(inStream); + mPositionMotorConstraintPart.RestoreState(inStream); + mHingeConstraintPart.RestoreState(inStream); + mRotationConstraintPart.RestoreState(inStream); + + inStream.Read(mMaxFrictionForce); + inStream.Read(mPositionMotorSettings); + inStream.Read(mPositionMotorState); + inStream.Read(mTargetVelocity); + inStream.Read(mTargetPathFraction); + inStream.Read(mPathFraction); +} + +Ref PathConstraint::GetConstraintSettings() const +{ + JPH_ASSERT(false); // Not implemented yet + return nullptr; +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PathConstraint.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PathConstraint.h new file mode 100644 index 00000000000..6301b852330 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PathConstraint.h @@ -0,0 +1,186 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +/// How to constrain the rotation of the body to a PathConstraint +enum class EPathRotationConstraintType +{ + Free, ///< Do not constrain the rotation of the body at all + ConstrainAroundTangent, ///< Only allow rotation around the tangent vector (following the path) + ConstrainAroundNormal, ///< Only allow rotation around the normal vector (perpendicular to the path) + ConstrainAroundBinormal, ///< Only allow rotation around the binormal vector (perpendicular to the path) + ConstrainToPath, ///< Fully constrain the rotation of body 2 to the path (following the tangent and normal of the path) + FullyConstrained, ///< Fully constrain the rotation of the body 2 to the rotation of body 1 +}; + +/// Path constraint settings, used to constrain the degrees of freedom between two bodies to a path +/// +/// The requirements of the path are that: +/// * Tangent, normal and bi-normal form an orthonormal basis with: tangent cross bi-normal = normal +/// * The path points along the tangent vector +/// * The path is continuous so doesn't contain any sharp corners +/// +/// The reason for all this is that the constraint acts like a slider constraint with the sliding axis being the tangent vector (the assumption here is that delta time will be small enough so that the path is linear for that delta time). +class JPH_EXPORT PathConstraintSettings final : public TwoBodyConstraintSettings +{ +public: + JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, PathConstraintSettings) + + // See: ConstraintSettings::SaveBinaryState + virtual void SaveBinaryState(StreamOut &inStream) const override; + + /// Create an instance of this constraint + virtual TwoBodyConstraint * Create(Body &inBody1, Body &inBody2) const override; + + /// The path that constrains the two bodies + RefConst mPath; + + /// The position of the path start relative to world transform of body 1 + Vec3 mPathPosition = Vec3::sZero(); + + /// The rotation of the path start relative to world transform of body 1 + Quat mPathRotation = Quat::sIdentity(); + + /// The fraction along the path that corresponds to the initial position of body 2. Usually this is 0, the beginning of the path. But if you want to start an object halfway the path you can calculate this with mPath->GetClosestPoint(point on path to attach body to). + float mPathFraction = 0.0f; + + /// Maximum amount of friction force to apply (N) when not driven by a motor. + float mMaxFrictionForce = 0.0f; + + /// In case the constraint is powered, this determines the motor settings along the path + MotorSettings mPositionMotorSettings; + + /// How to constrain the rotation of the body to the path + EPathRotationConstraintType mRotationConstraintType = EPathRotationConstraintType::Free; + +protected: + // See: ConstraintSettings::RestoreBinaryState + virtual void RestoreBinaryState(StreamIn &inStream) override; +}; + +/// Path constraint, used to constrain the degrees of freedom between two bodies to a path +class JPH_EXPORT PathConstraint final : public TwoBodyConstraint +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Construct point constraint + PathConstraint(Body &inBody1, Body &inBody2, const PathConstraintSettings &inSettings); + + // Generic interface of a constraint + virtual EConstraintSubType GetSubType() const override { return EConstraintSubType::Path; } + virtual void NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM) override; + virtual void SetupVelocityConstraint(float inDeltaTime) override; + virtual void ResetWarmStart() override; + virtual void WarmStartVelocityConstraint(float inWarmStartImpulseRatio) override; + virtual bool SolveVelocityConstraint(float inDeltaTime) override; + virtual bool SolvePositionConstraint(float inDeltaTime, float inBaumgarte) override; +#ifdef JPH_DEBUG_RENDERER + virtual void DrawConstraint(DebugRenderer *inRenderer) const override; +#endif // JPH_DEBUG_RENDERER + virtual void SaveState(StateRecorder &inStream) const override; + virtual void RestoreState(StateRecorder &inStream) override; + virtual bool IsActive() const override { return TwoBodyConstraint::IsActive() && mPath != nullptr; } + virtual Ref GetConstraintSettings() const override; + + // See: TwoBodyConstraint + virtual Mat44 GetConstraintToBody1Matrix() const override { return mPathToBody1; } + virtual Mat44 GetConstraintToBody2Matrix() const override { return mPathToBody2; } + + /// Update the path for this constraint + void SetPath(const PathConstraintPath *inPath, float inPathFraction); + + /// Access to the current path + const PathConstraintPath * GetPath() const { return mPath; } + + /// Access to the current fraction along the path e [0, GetPath()->GetMaxPathFraction()] + float GetPathFraction() const { return mPathFraction; } + + /// Friction control + void SetMaxFrictionForce(float inFrictionForce) { mMaxFrictionForce = inFrictionForce; } + float GetMaxFrictionForce() const { return mMaxFrictionForce; } + + /// Position motor settings + MotorSettings & GetPositionMotorSettings() { return mPositionMotorSettings; } + const MotorSettings & GetPositionMotorSettings() const { return mPositionMotorSettings; } + + // Position motor controls (drives body 2 along the path) + void SetPositionMotorState(EMotorState inState) { JPH_ASSERT(inState == EMotorState::Off || mPositionMotorSettings.IsValid()); mPositionMotorState = inState; } + EMotorState GetPositionMotorState() const { return mPositionMotorState; } + void SetTargetVelocity(float inVelocity) { mTargetVelocity = inVelocity; } + float GetTargetVelocity() const { return mTargetVelocity; } + void SetTargetPathFraction(float inFraction) { JPH_ASSERT(mPath->IsLooping() || (inFraction >= 0.0f && inFraction <= mPath->GetPathMaxFraction())); mTargetPathFraction = inFraction; } + float GetTargetPathFraction() const { return mTargetPathFraction; } + + ///@name Get Lagrange multiplier from last physics update (the linear/angular impulse applied to satisfy the constraint) + inline Vector<2> GetTotalLambdaPosition() const { return mPositionConstraintPart.GetTotalLambda(); } + inline float GetTotalLambdaPositionLimits() const { return mPositionLimitsConstraintPart.GetTotalLambda(); } + inline float GetTotalLambdaMotor() const { return mPositionMotorConstraintPart.GetTotalLambda(); } + inline Vector<2> GetTotalLambdaRotationHinge() const { return mHingeConstraintPart.GetTotalLambda(); } + inline Vec3 GetTotalLambdaRotation() const { return mRotationConstraintPart.GetTotalLambda(); } + +private: + // Internal helper function to calculate the values below + void CalculateConstraintProperties(float inDeltaTime); + + // CONFIGURATION PROPERTIES FOLLOW + + RefConst mPath; ///< The path that attaches the two bodies + Mat44 mPathToBody1; ///< Transform that takes a quantity from path space to body 1 center of mass space + Mat44 mPathToBody2; ///< Transform that takes a quantity from path space to body 2 center of mass space + EPathRotationConstraintType mRotationConstraintType; ///< How to constrain the rotation of the path + + // Friction + float mMaxFrictionForce; + + // Motor controls + MotorSettings mPositionMotorSettings; + EMotorState mPositionMotorState = EMotorState::Off; + float mTargetVelocity = 0.0f; + float mTargetPathFraction = 0.0f; + + // RUN TIME PROPERTIES FOLLOW + + // Positions where the point constraint acts on in world space + Vec3 mR1; + Vec3 mR2; + + // X2 + R2 - X1 - R1 + Vec3 mU; + + // World space path tangent + Vec3 mPathTangent; + + // Normals to the path tangent + Vec3 mPathNormal; + Vec3 mPathBinormal; + + // Inverse of initial rotation from body 1 to body 2 in body 1 space (only used when rotation constraint type is FullyConstrained) + Quat mInvInitialOrientation; + + // Current fraction along the path where body 2 is attached + float mPathFraction = 0.0f; + + // Translation constraint parts + DualAxisConstraintPart mPositionConstraintPart; ///< Constraint part that keeps the movement along the tangent of the path + AxisConstraintPart mPositionLimitsConstraintPart; ///< Constraint part that prevents movement beyond the beginning and end of the path + AxisConstraintPart mPositionMotorConstraintPart; ///< Constraint to drive the object along the path or to apply friction + + // Rotation constraint parts + HingeRotationConstraintPart mHingeConstraintPart; ///< Constraint part that removes 2 degrees of rotation freedom + RotationEulerConstraintPart mRotationConstraintPart; ///< Constraint part that removes all rotational freedom +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PathConstraintPath.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PathConstraintPath.cpp new file mode 100644 index 00000000000..69c0a82f3bd --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PathConstraintPath.cpp @@ -0,0 +1,85 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#ifdef JPH_DEBUG_RENDERER + #include +#endif // JPH_DEBUG_RENDERER + +JPH_NAMESPACE_BEGIN + +JPH_IMPLEMENT_SERIALIZABLE_ABSTRACT(PathConstraintPath) +{ + JPH_ADD_BASE_CLASS(PathConstraintPath, SerializableObject) +} + +#ifdef JPH_DEBUG_RENDERER +// Helper function to transform the results of GetPointOnPath to world space +static inline void sTransformPathPoint(RMat44Arg inTransform, Vec3Arg inPosition, RVec3 &outPosition, Vec3 &ioNormal, Vec3 &ioBinormal) +{ + outPosition = inTransform * inPosition; + ioNormal = inTransform.Multiply3x3(ioNormal); + ioBinormal = inTransform.Multiply3x3(ioBinormal); +} + +// Helper function to draw a path segment +static inline void sDrawPathSegment(DebugRenderer *inRenderer, RVec3Arg inPrevPosition, RVec3Arg inPosition, Vec3Arg inNormal, Vec3Arg inBinormal) +{ + inRenderer->DrawLine(inPrevPosition, inPosition, Color::sWhite); + inRenderer->DrawArrow(inPosition, inPosition + 0.1f * inNormal, Color::sRed, 0.02f); + inRenderer->DrawArrow(inPosition, inPosition + 0.1f * inBinormal, Color::sGreen, 0.02f); +} + +void PathConstraintPath::DrawPath(DebugRenderer *inRenderer, RMat44Arg inBaseTransform) const +{ + // Calculate first point + Vec3 lfirst_pos, first_tangent, first_normal, first_binormal; + GetPointOnPath(0.0f, lfirst_pos, first_tangent, first_normal, first_binormal); + RVec3 first_pos; + sTransformPathPoint(inBaseTransform, lfirst_pos, first_pos, first_normal, first_binormal); + + float t_max = GetPathMaxFraction(); + + // Draw the segments + RVec3 prev_pos = first_pos; + for (float t = 0.1f; t < t_max; t += 0.1f) + { + Vec3 lpos, tangent, normal, binormal; + GetPointOnPath(t, lpos, tangent, normal, binormal); + RVec3 pos; + sTransformPathPoint(inBaseTransform, lpos, pos, normal, binormal); + sDrawPathSegment(inRenderer, prev_pos, pos, normal, binormal); + prev_pos = pos; + } + + // Draw last point + Vec3 lpos, tangent, normal, binormal; + GetPointOnPath(t_max, lpos, tangent, normal, binormal); + RVec3 pos; + sTransformPathPoint(inBaseTransform, lpos, pos, normal, binormal); + sDrawPathSegment(inRenderer, prev_pos, pos, normal, binormal); +} +#endif // JPH_DEBUG_RENDERER + +void PathConstraintPath::SaveBinaryState(StreamOut &inStream) const +{ + inStream.Write(GetRTTI()->GetHash()); + inStream.Write(mIsLooping); +} + +void PathConstraintPath::RestoreBinaryState(StreamIn &inStream) +{ + // Type hash read by sRestoreFromBinaryState + inStream.Read(mIsLooping); +} + +PathConstraintPath::PathResult PathConstraintPath::sRestoreFromBinaryState(StreamIn &inStream) +{ + return StreamUtils::RestoreObject(inStream, &PathConstraintPath::RestoreBinaryState); +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PathConstraintPath.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PathConstraintPath.h new file mode 100644 index 00000000000..06f10408a09 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PathConstraintPath.h @@ -0,0 +1,71 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +class StreamIn; +class StreamOut; +#ifdef JPH_DEBUG_RENDERER +class DebugRenderer; +#endif // JPH_DEBUG_RENDERER + +/// The path for a path constraint. It allows attaching two bodies to each other while giving the second body the freedom to move along a path relative to the first. +class JPH_EXPORT PathConstraintPath : public SerializableObject, public RefTarget +{ +public: + JPH_DECLARE_SERIALIZABLE_ABSTRACT(JPH_EXPORT, PathConstraintPath) + + using PathResult = Result>; + + /// Virtual destructor to ensure that derived types get their destructors called + virtual ~PathConstraintPath() override = default; + + /// Gets the max fraction along the path. I.e. sort of the length of the path. + virtual float GetPathMaxFraction() const = 0; + + /// Get the globally closest point on the curve (Could be slow!) + /// @param inPosition Position to find closest point for + /// @param inFractionHint Last known fraction along the path (can be used to speed up the search) + /// @return Fraction of closest point along the path + virtual float GetClosestPoint(Vec3Arg inPosition, float inFractionHint) const = 0; + + /// Given the fraction along the path, get the point, tangent and normal. + /// @param inFraction Fraction along the path [0, GetPathMaxFraction()]. + /// @param outPathPosition Returns the closest position to inSearchPosition on the path. + /// @param outPathTangent Returns the tangent to the path at outPathPosition (the vector that follows the direction of the path) + /// @param outPathNormal Return the normal to the path at outPathPosition (a vector that's perpendicular to outPathTangent) + /// @param outPathBinormal Returns the binormal to the path at outPathPosition (a vector so that normal cross tangent = binormal) + virtual void GetPointOnPath(float inFraction, Vec3 &outPathPosition, Vec3 &outPathTangent, Vec3 &outPathNormal, Vec3 &outPathBinormal) const = 0; + + /// If the path is looping or not. If a path is looping, the first and last point are automatically connected to each other. They should not be the same points. + void SetIsLooping(bool inIsLooping) { mIsLooping = inIsLooping; } + bool IsLooping() const { return mIsLooping; } + +#ifdef JPH_DEBUG_RENDERER + /// Draw the path relative to inBaseTransform. Used for debug purposes. + void DrawPath(DebugRenderer *inRenderer, RMat44Arg inBaseTransform) const; +#endif // JPH_DEBUG_RENDERER + + /// Saves the contents of the path in binary form to inStream. + virtual void SaveBinaryState(StreamOut &inStream) const; + + /// Creates a Shape of the correct type and restores its contents from the binary stream inStream. + static PathResult sRestoreFromBinaryState(StreamIn &inStream); + +protected: + /// This function should not be called directly, it is used by sRestoreFromBinaryState. + virtual void RestoreBinaryState(StreamIn &inStream); + +private: + /// If the path is looping or not. If a path is looping, the first and last point are automatically connected to each other. They should not be the same points. + bool mIsLooping = false; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PathConstraintPathHermite.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PathConstraintPathHermite.cpp new file mode 100644 index 00000000000..55c1f33ad82 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PathConstraintPathHermite.cpp @@ -0,0 +1,308 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(PathConstraintPathHermite::Point) +{ + JPH_ADD_ATTRIBUTE(PathConstraintPathHermite::Point, mPosition) + JPH_ADD_ATTRIBUTE(PathConstraintPathHermite::Point, mTangent) + JPH_ADD_ATTRIBUTE(PathConstraintPathHermite::Point, mNormal) +} + +JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(PathConstraintPathHermite) +{ + JPH_ADD_BASE_CLASS(PathConstraintPathHermite, PathConstraintPath) + + JPH_ADD_ATTRIBUTE(PathConstraintPathHermite, mPoints) +} + +// Calculate position and tangent for a Cubic Hermite Spline segment +static inline void sCalculatePositionAndTangent(Vec3Arg inP1, Vec3Arg inM1, Vec3Arg inP2, Vec3Arg inM2, float inT, Vec3 &outPosition, Vec3 &outTangent) +{ + // Calculate factors for Cubic Hermite Spline + // See: https://en.wikipedia.org/wiki/Cubic_Hermite_spline + float t2 = inT * inT; + float t3 = inT * t2; + float h00 = 2.0f * t3 - 3.0f * t2 + 1.0f; + float h10 = t3 - 2.0f * t2 + inT; + float h01 = -2.0f * t3 + 3.0f * t2; + float h11 = t3 - t2; + + // Calculate d/dt for factors to calculate the tangent + float ddt_h00 = 6.0f * (t2 - inT); + float ddt_h10 = 3.0f * t2 - 4.0f * inT + 1.0f; + float ddt_h01 = -ddt_h00; + float ddt_h11 = 3.0f * t2 - 2.0f * inT; + + outPosition = h00 * inP1 + h10 * inM1 + h01 * inP2 + h11 * inM2; + outTangent = ddt_h00 * inP1 + ddt_h10 * inM1 + ddt_h01 * inP2 + ddt_h11 * inM2; +} + +// Calculate the closest point to the origin for a Cubic Hermite Spline segment +// This is used to get an estimate for the interval in which the closest point can be found, +// the interval [0, 1] is too big for Newton Raphson to work on because it is solving a 5th degree polynomial which may +// have multiple local minima that are not the root. This happens especially when the path is straight (tangents aligned with inP2 - inP1). +// Based on the bisection method: https://en.wikipedia.org/wiki/Bisection_method +static inline void sCalculateClosestPointThroughBisection(Vec3Arg inP1, Vec3Arg inM1, Vec3Arg inP2, Vec3Arg inM2, float &outTMin, float &outTMax) +{ + outTMin = 0.0f; + outTMax = 1.0f; + + // To get the closest point of the curve to the origin we need to solve: + // d/dt P(t) . P(t) = 0 for t, where P(t) is the point on the curve segment + // Using d/dt (a(t) . b(t)) = d/dt a(t) . b(t) + a(t) . d/dt b(t) + // See: https://proofwiki.org/wiki/Derivative_of_Dot_Product_of_Vector-Valued_Functions + // d/dt P(t) . P(t) = 2 P(t) d/dt P(t) = 2 P(t) . Tangent(t) + + // Calculate the derivative at t = 0, we know P(0) = inP1 and Tangent(0) = inM1 + float ddt_min = inP1.Dot(inM1); // Leaving out factor 2, we're only interested in the root + if (abs(ddt_min) < 1.0e-6f) + { + // Derivative is near zero, we found our root + outTMax = 0.0f; + return; + } + bool ddt_min_negative = ddt_min < 0.0f; + + // Calculate derivative at t = 1, we know P(1) = inP2 and Tangent(1) = inM2 + float ddt_max = inP2.Dot(inM2); + if (abs(ddt_max) < 1.0e-6f) + { + // Derivative is near zero, we found our root + outTMin = 1.0f; + return; + } + bool ddt_max_negative = ddt_max < 0.0f; + + // If the signs of the derivative are not different, this algorithm can't find the root + if (ddt_min_negative == ddt_max_negative) + return; + + // With 4 iterations we'll get a result accurate to 1 / 2^4 = 0.0625 + for (int iteration = 0; iteration < 4; ++iteration) + { + float t_mid = 0.5f * (outTMin + outTMax); + Vec3 position, tangent; + sCalculatePositionAndTangent(inP1, inM1, inP2, inM2, t_mid, position, tangent); + float ddt_mid = position.Dot(tangent); + if (abs(ddt_mid) < 1.0e-6f) + { + // Derivative is near zero, we found our root + outTMin = outTMax = t_mid; + return; + } + bool ddt_mid_negative = ddt_mid < 0.0f; + + // Update the search interval so that the signs of the derivative at both ends of the interval are still different + if (ddt_mid_negative == ddt_min_negative) + outTMin = t_mid; + else + outTMax = t_mid; + } +} + +// Calculate the closest point to the origin for a Cubic Hermite Spline segment +// Only considers the range t e [inTMin, inTMax] and will stop as soon as the closest point falls outside of that range +static inline float sCalculateClosestPointThroughNewtonRaphson(Vec3Arg inP1, Vec3Arg inM1, Vec3Arg inP2, Vec3Arg inM2, float inTMin, float inTMax, float &outDistanceSq) +{ + // This is the closest position on the curve to the origin that we found + Vec3 position; + + // Calculate the size of the interval + float interval = inTMax - inTMin; + + // Start in the middle of the interval + float t = 0.5f * (inTMin + inTMax); + + // Do max 10 iterations to prevent taking too much CPU time + for (int iteration = 0; iteration < 10; ++iteration) + { + // Calculate derivative at t, see comment at sCalculateClosestPointThroughBisection for derivation of the equations + Vec3 tangent; + sCalculatePositionAndTangent(inP1, inM1, inP2, inM2, t, position, tangent); + float ddt = position.Dot(tangent); // Leaving out factor 2, we're only interested in the root + + // Calculate derivative of ddt: d^2/dt P(t) . P(t) = d/dt (2 P(t) . Tangent(t)) + // = 2 (d/dt P(t)) . Tangent(t) + P(t) . d/dt Tangent(t)) = 2 (Tangent(t) . Tangent(t) + P(t) . d/dt Tangent(t)) + float d2dt_h00 = 12.0f * t - 6.0f; + float d2dt_h10 = 6.0f * t - 4.0f; + float d2dt_h01 = -d2dt_h00; + float d2dt_h11 = 6.0f * t - 2.0f; + Vec3 ddt_tangent = d2dt_h00 * inP1 + d2dt_h10 * inM1 + d2dt_h01 * inP2 + d2dt_h11 * inM2; + float d2dt = tangent.Dot(tangent) + position.Dot(ddt_tangent); // Leaving out factor 2, because we left it out above too + + // If d2dt is zero, the curve is flat and there are multiple t's for which we are closest to the origin, stop now + if (d2dt == 0.0f) + break; + + // Do a Newton Raphson step + // See: https://en.wikipedia.org/wiki/Newton%27s_method + // Clamp against [-interval, interval] to avoid overshooting too much, we're not interested outside the interval + float delta = Clamp(-ddt / d2dt, -interval, interval); + + // If we're stepping away further from t e [inTMin, inTMax] stop now + if ((t > inTMax && delta > 0.0f) || (t < inTMin && delta < 0.0f)) + break; + + // If we've converged, stop now + t += delta; + if (abs(delta) < 1.0e-4f) + break; + } + + // Calculate the distance squared for the origin to the curve + outDistanceSq = position.LengthSq(); + return t; +} + +void PathConstraintPathHermite::GetIndexAndT(float inFraction, int &outIndex, float &outT) const +{ + int num_points = int(mPoints.size()); + + // Start by truncating the fraction to get the index and storing the remainder in t + int index = int(trunc(inFraction)); + float t = inFraction - float(index); + + if (IsLooping()) + { + JPH_ASSERT(!mPoints.front().mPosition.IsClose(mPoints.back().mPosition), "A looping path should have a different first and last point!"); + + // Make sure index is positive by adding a multiple of num_points + if (index < 0) + index += (-index / num_points + 1) * num_points; + + // Index needs to be modulo num_points + index = index % num_points; + } + else + { + // Clamp against range of points + if (index < 0) + { + index = 0; + t = 0.0f; + } + else if (index >= num_points - 1) + { + index = num_points - 2; + t = 1.0f; + } + } + + outIndex = index; + outT = t; +} + +float PathConstraintPathHermite::GetClosestPoint(Vec3Arg inPosition, float inFractionHint) const +{ + JPH_PROFILE_FUNCTION(); + + int num_points = int(mPoints.size()); + + // Start with last point on the path, in the non-looping case we won't be visiting this point + float best_dist_sq = (mPoints[num_points - 1].mPosition - inPosition).LengthSq(); + float best_t = float(num_points - 1); + + // Loop over all points + for (int i = 0, max_i = IsLooping()? num_points : num_points - 1; i < max_i; ++i) + { + const Point &p1 = mPoints[i]; + const Point &p2 = mPoints[(i + 1) % num_points]; + + // Make the curve relative to inPosition + Vec3 p1_pos = p1.mPosition - inPosition; + Vec3 p2_pos = p2.mPosition - inPosition; + + // Get distance to p1 + float dist_sq = p1_pos.LengthSq(); + if (dist_sq < best_dist_sq) + { + best_t = float(i); + best_dist_sq = dist_sq; + } + + // First find an interval for the closest point so that we can start doing Newton Raphson steps + float t_min, t_max; + sCalculateClosestPointThroughBisection(p1_pos, p1.mTangent, p2_pos, p2.mTangent, t_min, t_max); + + if (t_min == t_max) + { + // If the function above returned no interval then it found the root already and we can just calculate the distance + Vec3 position, tangent; + sCalculatePositionAndTangent(p1_pos, p1.mTangent, p2_pos, p2.mTangent, t_min, position, tangent); + dist_sq = position.LengthSq(); + if (dist_sq < best_dist_sq) + { + best_t = float(i) + t_min; + best_dist_sq = dist_sq; + } + } + else + { + // Get closest distance along curve segment + float t = sCalculateClosestPointThroughNewtonRaphson(p1_pos, p1.mTangent, p2_pos, p2.mTangent, t_min, t_max, dist_sq); + if (t >= 0.0f && t <= 1.0f && dist_sq < best_dist_sq) + { + best_t = float(i) + t; + best_dist_sq = dist_sq; + } + } + } + + return best_t; +} + +void PathConstraintPathHermite::GetPointOnPath(float inFraction, Vec3 &outPathPosition, Vec3 &outPathTangent, Vec3 &outPathNormal, Vec3 &outPathBinormal) const +{ + JPH_PROFILE_FUNCTION(); + + // Determine which hermite spline segment we need + int index; + float t; + GetIndexAndT(inFraction, index, t); + + // Get the points on the segment + const Point &p1 = mPoints[index]; + const Point &p2 = mPoints[(index + 1) % int(mPoints.size())]; + + // Calculate the position and tangent on the path + Vec3 tangent; + sCalculatePositionAndTangent(p1.mPosition, p1.mTangent, p2.mPosition, p2.mTangent, t, outPathPosition, tangent); + outPathTangent = tangent.Normalized(); + + // Just linearly interpolate the normal + Vec3 normal = (1.0f - t) * p1.mNormal + t * p2.mNormal; + + // Calculate binormal + outPathBinormal = normal.Cross(outPathTangent).Normalized(); + + // Recalculate normal so it is perpendicular to both (linear interpolation will cause it not to be) + outPathNormal = outPathTangent.Cross(outPathBinormal); + JPH_ASSERT(outPathNormal.IsNormalized()); +} + +void PathConstraintPathHermite::SaveBinaryState(StreamOut &inStream) const +{ + PathConstraintPath::SaveBinaryState(inStream); + + inStream.Write(mPoints); +} + +void PathConstraintPathHermite::RestoreBinaryState(StreamIn &inStream) +{ + PathConstraintPath::RestoreBinaryState(inStream); + + inStream.Read(mPoints); +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PathConstraintPathHermite.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PathConstraintPathHermite.h new file mode 100644 index 00000000000..aaf2ed8f752 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PathConstraintPathHermite.h @@ -0,0 +1,54 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +/// A path that follows a Hermite spline +class JPH_EXPORT PathConstraintPathHermite final : public PathConstraintPath +{ +public: + JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, PathConstraintPathHermite) + + // See PathConstraintPath::GetPathMaxFraction + virtual float GetPathMaxFraction() const override { return float(IsLooping()? mPoints.size() : mPoints.size() - 1); } + + // See PathConstraintPath::GetClosestPoint + virtual float GetClosestPoint(Vec3Arg inPosition, float inFractionHint) const override; + + // See PathConstraintPath::GetPointOnPath + virtual void GetPointOnPath(float inFraction, Vec3 &outPathPosition, Vec3 &outPathTangent, Vec3 &outPathNormal, Vec3 &outPathBinormal) const override; + + /// Adds a point to the path + void AddPoint(Vec3Arg inPosition, Vec3Arg inTangent, Vec3Arg inNormal) { mPoints.push_back({ inPosition, inTangent, inNormal}); } + + // See: PathConstraintPath::SaveBinaryState + virtual void SaveBinaryState(StreamOut &inStream) const override; + + struct Point + { + JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, Point) + + Vec3 mPosition; ///< Position on the path + Vec3 mTangent; ///< Tangent of the path, does not need to be normalized (in the direction of the path) + Vec3 mNormal; ///< Normal of the path (together with the tangent along the curve this forms a basis for the constraint) + }; + +protected: + // See: PathConstraintPath::RestoreBinaryState + virtual void RestoreBinaryState(StreamIn &inStream) override; + +private: + /// Helper function that returns the index of the path segment and the fraction t on the path segment based on the full path fraction + inline void GetIndexAndT(float inFraction, int &outIndex, float &outT) const; + + using Points = Array; + + Points mPoints; ///< Points on the Hermite spline +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PointConstraint.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PointConstraint.cpp new file mode 100644 index 00000000000..74d0ecd7c74 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PointConstraint.cpp @@ -0,0 +1,157 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#include +#include +#ifdef JPH_DEBUG_RENDERER + #include +#endif // JPH_DEBUG_RENDERER + +JPH_NAMESPACE_BEGIN + +JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(PointConstraintSettings) +{ + JPH_ADD_BASE_CLASS(PointConstraintSettings, TwoBodyConstraintSettings) + + JPH_ADD_ENUM_ATTRIBUTE(PointConstraintSettings, mSpace) + JPH_ADD_ATTRIBUTE(PointConstraintSettings, mPoint1) + JPH_ADD_ATTRIBUTE(PointConstraintSettings, mPoint2) +} + +void PointConstraintSettings::SaveBinaryState(StreamOut &inStream) const +{ + ConstraintSettings::SaveBinaryState(inStream); + + inStream.Write(mSpace); + inStream.Write(mPoint1); + inStream.Write(mPoint2); +} + +void PointConstraintSettings::RestoreBinaryState(StreamIn &inStream) +{ + ConstraintSettings::RestoreBinaryState(inStream); + + inStream.Read(mSpace); + inStream.Read(mPoint1); + inStream.Read(mPoint2); +} + +TwoBodyConstraint *PointConstraintSettings::Create(Body &inBody1, Body &inBody2) const +{ + return new PointConstraint(inBody1, inBody2, *this); +} + +PointConstraint::PointConstraint(Body &inBody1, Body &inBody2, const PointConstraintSettings &inSettings) : + TwoBodyConstraint(inBody1, inBody2, inSettings) +{ + if (inSettings.mSpace == EConstraintSpace::WorldSpace) + { + // If all properties were specified in world space, take them to local space now + mLocalSpacePosition1 = Vec3(inBody1.GetInverseCenterOfMassTransform() * inSettings.mPoint1); + mLocalSpacePosition2 = Vec3(inBody2.GetInverseCenterOfMassTransform() * inSettings.mPoint2); + } + else + { + mLocalSpacePosition1 = Vec3(inSettings.mPoint1); + mLocalSpacePosition2 = Vec3(inSettings.mPoint2); + } +} + +void PointConstraint::NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM) +{ + if (mBody1->GetID() == inBodyID) + mLocalSpacePosition1 -= inDeltaCOM; + else if (mBody2->GetID() == inBodyID) + mLocalSpacePosition2 -= inDeltaCOM; +} + +void PointConstraint::SetPoint1(EConstraintSpace inSpace, RVec3Arg inPoint1) +{ + if (inSpace == EConstraintSpace::WorldSpace) + mLocalSpacePosition1 = Vec3(mBody1->GetInverseCenterOfMassTransform() * inPoint1); + else + mLocalSpacePosition1 = Vec3(inPoint1); +} + +void PointConstraint::SetPoint2(EConstraintSpace inSpace, RVec3Arg inPoint2) +{ + if (inSpace == EConstraintSpace::WorldSpace) + mLocalSpacePosition2 = Vec3(mBody2->GetInverseCenterOfMassTransform() * inPoint2); + else + mLocalSpacePosition2 = Vec3(inPoint2); +} + +void PointConstraint::CalculateConstraintProperties() +{ + mPointConstraintPart.CalculateConstraintProperties(*mBody1, Mat44::sRotation(mBody1->GetRotation()), mLocalSpacePosition1, *mBody2, Mat44::sRotation(mBody2->GetRotation()), mLocalSpacePosition2); +} + +void PointConstraint::SetupVelocityConstraint(float inDeltaTime) +{ + CalculateConstraintProperties(); +} + +void PointConstraint::ResetWarmStart() +{ + mPointConstraintPart.Deactivate(); +} + +void PointConstraint::WarmStartVelocityConstraint(float inWarmStartImpulseRatio) +{ + // Warm starting: Apply previous frame impulse + mPointConstraintPart.WarmStart(*mBody1, *mBody2, inWarmStartImpulseRatio); +} + +bool PointConstraint::SolveVelocityConstraint(float inDeltaTime) +{ + return mPointConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2); +} + +bool PointConstraint::SolvePositionConstraint(float inDeltaTime, float inBaumgarte) +{ + // Update constraint properties (bodies may have moved) + CalculateConstraintProperties(); + + return mPointConstraintPart.SolvePositionConstraint(*mBody1, *mBody2, inBaumgarte); +} + +#ifdef JPH_DEBUG_RENDERER +void PointConstraint::DrawConstraint(DebugRenderer *inRenderer) const +{ + // Draw constraint + inRenderer->DrawMarker(mBody1->GetCenterOfMassTransform() * mLocalSpacePosition1, Color::sRed, 0.1f); + inRenderer->DrawMarker(mBody2->GetCenterOfMassTransform() * mLocalSpacePosition2, Color::sGreen, 0.1f); +} +#endif // JPH_DEBUG_RENDERER + +void PointConstraint::SaveState(StateRecorder &inStream) const +{ + TwoBodyConstraint::SaveState(inStream); + + mPointConstraintPart.SaveState(inStream); +} + +void PointConstraint::RestoreState(StateRecorder &inStream) +{ + TwoBodyConstraint::RestoreState(inStream); + + mPointConstraintPart.RestoreState(inStream); +} + +Ref PointConstraint::GetConstraintSettings() const +{ + PointConstraintSettings *settings = new PointConstraintSettings; + ToConstraintSettings(*settings); + settings->mSpace = EConstraintSpace::LocalToBodyCOM; + settings->mPoint1 = RVec3(mLocalSpacePosition1); + settings->mPoint2 = RVec3(mLocalSpacePosition2); + return settings; +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PointConstraint.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PointConstraint.h new file mode 100644 index 00000000000..f88756931ec --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PointConstraint.h @@ -0,0 +1,94 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +JPH_NAMESPACE_BEGIN + +/// Point constraint settings, used to create a point constraint +class JPH_EXPORT PointConstraintSettings final : public TwoBodyConstraintSettings +{ +public: + JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, PointConstraintSettings) + + // See: ConstraintSettings::SaveBinaryState + virtual void SaveBinaryState(StreamOut &inStream) const override; + + /// Create an instance of this constraint + virtual TwoBodyConstraint * Create(Body &inBody1, Body &inBody2) const override; + + /// This determines in which space the constraint is setup, all properties below should be in the specified space + EConstraintSpace mSpace = EConstraintSpace::WorldSpace; + + /// Body 1 constraint position (space determined by mSpace). + RVec3 mPoint1 = RVec3::sZero(); + + /// Body 2 constraint position (space determined by mSpace). + /// Note: Normally you would set mPoint1 = mPoint2 if the bodies are already placed how you want to constrain them (if mSpace = world space). + RVec3 mPoint2 = RVec3::sZero(); + +protected: + // See: ConstraintSettings::RestoreBinaryState + virtual void RestoreBinaryState(StreamIn &inStream) override; +}; + +/// A point constraint constrains 2 bodies on a single point (removing 3 degrees of freedom) +class JPH_EXPORT PointConstraint final : public TwoBodyConstraint +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Construct point constraint + PointConstraint(Body &inBody1, Body &inBody2, const PointConstraintSettings &inSettings); + + // Generic interface of a constraint + virtual EConstraintSubType GetSubType() const override { return EConstraintSubType::Point; } + virtual void NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM) override; + virtual void SetupVelocityConstraint(float inDeltaTime) override; + virtual void ResetWarmStart() override; + virtual void WarmStartVelocityConstraint(float inWarmStartImpulseRatio) override; + virtual bool SolveVelocityConstraint(float inDeltaTime) override; + virtual bool SolvePositionConstraint(float inDeltaTime, float inBaumgarte) override; +#ifdef JPH_DEBUG_RENDERER + virtual void DrawConstraint(DebugRenderer *inRenderer) const override; +#endif // JPH_DEBUG_RENDERER + virtual void SaveState(StateRecorder &inStream) const override; + virtual void RestoreState(StateRecorder &inStream) override; + virtual Ref GetConstraintSettings() const override; + + /// Update the attachment point for body 1 + void SetPoint1(EConstraintSpace inSpace, RVec3Arg inPoint1); + + /// Update the attachment point for body 2 + void SetPoint2(EConstraintSpace inSpace, RVec3Arg inPoint2); + + /// Get the attachment point for body 1 relative to body 1 COM + inline Vec3 GetLocalSpacePoint1() const { return mLocalSpacePosition1; } + + /// Get the attachment point for body 2 relative to body 2 COM + inline Vec3 GetLocalSpacePoint2() const { return mLocalSpacePosition2; } + + // See: TwoBodyConstraint + virtual Mat44 GetConstraintToBody1Matrix() const override { return Mat44::sTranslation(mLocalSpacePosition1); } + virtual Mat44 GetConstraintToBody2Matrix() const override { return Mat44::sTranslation(mLocalSpacePosition2); } // Note: Incorrect rotation as we don't track the original rotation difference, should not matter though as the constraint is not limiting rotation. + + ///@name Get Lagrange multiplier from last physics update (the linear impulse applied to satisfy the constraint) + inline Vec3 GetTotalLambdaPosition() const { return mPointConstraintPart.GetTotalLambda(); } + +private: + // Internal helper function to calculate the values below + void CalculateConstraintProperties(); + + // Local space constraint positions + Vec3 mLocalSpacePosition1; + Vec3 mLocalSpacePosition2; + + // The constraint part + PointConstraintPart mPointConstraintPart; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PulleyConstraint.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PulleyConstraint.cpp new file mode 100644 index 00000000000..9d15c1d4dc1 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PulleyConstraint.cpp @@ -0,0 +1,253 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2022 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#include +#include +#ifdef JPH_DEBUG_RENDERER + #include +#endif // JPH_DEBUG_RENDERER + +JPH_NAMESPACE_BEGIN + +using namespace literals; + +JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(PulleyConstraintSettings) +{ + JPH_ADD_BASE_CLASS(PulleyConstraintSettings, TwoBodyConstraintSettings) + + JPH_ADD_ENUM_ATTRIBUTE(PulleyConstraintSettings, mSpace) + JPH_ADD_ATTRIBUTE(PulleyConstraintSettings, mBodyPoint1) + JPH_ADD_ATTRIBUTE(PulleyConstraintSettings, mFixedPoint1) + JPH_ADD_ATTRIBUTE(PulleyConstraintSettings, mBodyPoint2) + JPH_ADD_ATTRIBUTE(PulleyConstraintSettings, mFixedPoint2) + JPH_ADD_ATTRIBUTE(PulleyConstraintSettings, mRatio) + JPH_ADD_ATTRIBUTE(PulleyConstraintSettings, mMinLength) + JPH_ADD_ATTRIBUTE(PulleyConstraintSettings, mMaxLength) +} + +void PulleyConstraintSettings::SaveBinaryState(StreamOut &inStream) const +{ + ConstraintSettings::SaveBinaryState(inStream); + + inStream.Write(mSpace); + inStream.Write(mBodyPoint1); + inStream.Write(mFixedPoint1); + inStream.Write(mBodyPoint2); + inStream.Write(mFixedPoint2); + inStream.Write(mRatio); + inStream.Write(mMinLength); + inStream.Write(mMaxLength); +} + +void PulleyConstraintSettings::RestoreBinaryState(StreamIn &inStream) +{ + ConstraintSettings::RestoreBinaryState(inStream); + + inStream.Read(mSpace); + inStream.Read(mBodyPoint1); + inStream.Read(mFixedPoint1); + inStream.Read(mBodyPoint2); + inStream.Read(mFixedPoint2); + inStream.Read(mRatio); + inStream.Read(mMinLength); + inStream.Read(mMaxLength); +} + +TwoBodyConstraint *PulleyConstraintSettings::Create(Body &inBody1, Body &inBody2) const +{ + return new PulleyConstraint(inBody1, inBody2, *this); +} + +PulleyConstraint::PulleyConstraint(Body &inBody1, Body &inBody2, const PulleyConstraintSettings &inSettings) : + TwoBodyConstraint(inBody1, inBody2, inSettings), + mFixedPosition1(inSettings.mFixedPoint1), + mFixedPosition2(inSettings.mFixedPoint2), + mRatio(inSettings.mRatio), + mMinLength(inSettings.mMinLength), + mMaxLength(inSettings.mMaxLength) +{ + if (inSettings.mSpace == EConstraintSpace::WorldSpace) + { + // If all properties were specified in world space, take them to local space now + mLocalSpacePosition1 = Vec3(inBody1.GetInverseCenterOfMassTransform() * inSettings.mBodyPoint1); + mLocalSpacePosition2 = Vec3(inBody2.GetInverseCenterOfMassTransform() * inSettings.mBodyPoint2); + mWorldSpacePosition1 = inSettings.mBodyPoint1; + mWorldSpacePosition2 = inSettings.mBodyPoint2; + } + else + { + // If properties were specified in local space, we need to calculate world space positions + mLocalSpacePosition1 = Vec3(inSettings.mBodyPoint1); + mLocalSpacePosition2 = Vec3(inSettings.mBodyPoint2); + mWorldSpacePosition1 = inBody1.GetCenterOfMassTransform() * inSettings.mBodyPoint1; + mWorldSpacePosition2 = inBody2.GetCenterOfMassTransform() * inSettings.mBodyPoint2; + } + + // Calculate min/max length if it was not provided + float current_length = GetCurrentLength(); + if (mMinLength < 0.0f) + mMinLength = current_length; + if (mMaxLength < 0.0f) + mMaxLength = current_length; + + // Initialize the normals to a likely valid axis in case the fixed points overlap with the attachment points (most likely the fixed points are above both bodies) + mWorldSpaceNormal1 = mWorldSpaceNormal2 = -Vec3::sAxisY(); +} + +void PulleyConstraint::NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM) +{ + if (mBody1->GetID() == inBodyID) + mLocalSpacePosition1 -= inDeltaCOM; + else if (mBody2->GetID() == inBodyID) + mLocalSpacePosition2 -= inDeltaCOM; +} + +float PulleyConstraint::CalculatePositionsNormalsAndLength() +{ + // Update world space positions (the bodies may have moved) + mWorldSpacePosition1 = mBody1->GetCenterOfMassTransform() * mLocalSpacePosition1; + mWorldSpacePosition2 = mBody2->GetCenterOfMassTransform() * mLocalSpacePosition2; + + // Calculate world space normals + Vec3 delta1 = Vec3(mWorldSpacePosition1 - mFixedPosition1); + float delta1_len = delta1.Length(); + if (delta1_len > 0.0f) + mWorldSpaceNormal1 = delta1 / delta1_len; + + Vec3 delta2 = Vec3(mWorldSpacePosition2 - mFixedPosition2); + float delta2_len = delta2.Length(); + if (delta2_len > 0.0f) + mWorldSpaceNormal2 = delta2 / delta2_len; + + // Calculate length + return delta1_len + mRatio * delta2_len; +} + +void PulleyConstraint::CalculateConstraintProperties() +{ + // Calculate attachment points relative to COM + Vec3 r1 = Vec3(mWorldSpacePosition1 - mBody1->GetCenterOfMassPosition()); + Vec3 r2 = Vec3(mWorldSpacePosition2 - mBody2->GetCenterOfMassPosition()); + + mIndependentAxisConstraintPart.CalculateConstraintProperties(*mBody1, *mBody2, r1, mWorldSpaceNormal1, r2, mWorldSpaceNormal2, mRatio); +} + +void PulleyConstraint::SetupVelocityConstraint(float inDeltaTime) +{ + // Determine if the constraint is active + float current_length = CalculatePositionsNormalsAndLength(); + bool min_length_violation = current_length <= mMinLength; + bool max_length_violation = current_length >= mMaxLength; + if (min_length_violation || max_length_violation) + { + // Determine max lambda based on if the length is too big or small + mMinLambda = max_length_violation? -FLT_MAX : 0.0f; + mMaxLambda = min_length_violation? FLT_MAX : 0.0f; + + CalculateConstraintProperties(); + } + else + mIndependentAxisConstraintPart.Deactivate(); +} + +void PulleyConstraint::ResetWarmStart() +{ + mIndependentAxisConstraintPart.Deactivate(); +} + +void PulleyConstraint::WarmStartVelocityConstraint(float inWarmStartImpulseRatio) +{ + mIndependentAxisConstraintPart.WarmStart(*mBody1, *mBody2, mWorldSpaceNormal1, mWorldSpaceNormal2, mRatio, inWarmStartImpulseRatio); +} + +bool PulleyConstraint::SolveVelocityConstraint(float inDeltaTime) +{ + if (mIndependentAxisConstraintPart.IsActive()) + return mIndependentAxisConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2, mWorldSpaceNormal1, mWorldSpaceNormal2, mRatio, mMinLambda, mMaxLambda); + else + return false; +} + +bool PulleyConstraint::SolvePositionConstraint(float inDeltaTime, float inBaumgarte) +{ + // Calculate new length (bodies may have changed) + float current_length = CalculatePositionsNormalsAndLength(); + + float position_error = 0.0f; + if (current_length < mMinLength) + position_error = current_length - mMinLength; + else if (current_length > mMaxLength) + position_error = current_length - mMaxLength; + + if (position_error != 0.0f) + { + // Update constraint properties (bodies may have moved) + CalculateConstraintProperties(); + + return mIndependentAxisConstraintPart.SolvePositionConstraint(*mBody1, *mBody2, mWorldSpaceNormal1, mWorldSpaceNormal2, mRatio, position_error, inBaumgarte); + } + + return false; +} + +#ifdef JPH_DEBUG_RENDERER +void PulleyConstraint::DrawConstraint(DebugRenderer *inRenderer) const +{ + // Color according to length vs min/max length + float current_length = GetCurrentLength(); + Color color = Color::sGreen; + if (current_length < mMinLength) + color = Color::sYellow; + else if (current_length > mMaxLength) + color = Color::sRed; + + // Draw constraint + inRenderer->DrawLine(mWorldSpacePosition1, mFixedPosition1, color); + inRenderer->DrawLine(mFixedPosition1, mFixedPosition2, color); + inRenderer->DrawLine(mFixedPosition2, mWorldSpacePosition2, color); + + // Draw current length + inRenderer->DrawText3D(0.5_r * (mFixedPosition1 + mFixedPosition2), StringFormat("%.2f", (double)current_length)); +} +#endif // JPH_DEBUG_RENDERER + +void PulleyConstraint::SaveState(StateRecorder &inStream) const +{ + TwoBodyConstraint::SaveState(inStream); + + mIndependentAxisConstraintPart.SaveState(inStream); + inStream.Write(mWorldSpaceNormal1); // When distance to fixed point = 0, the normal is used from last frame so we need to store it + inStream.Write(mWorldSpaceNormal2); +} + +void PulleyConstraint::RestoreState(StateRecorder &inStream) +{ + TwoBodyConstraint::RestoreState(inStream); + + mIndependentAxisConstraintPart.RestoreState(inStream); + inStream.Read(mWorldSpaceNormal1); + inStream.Read(mWorldSpaceNormal2); +} + +Ref PulleyConstraint::GetConstraintSettings() const +{ + PulleyConstraintSettings *settings = new PulleyConstraintSettings; + ToConstraintSettings(*settings); + settings->mSpace = EConstraintSpace::LocalToBodyCOM; + settings->mBodyPoint1 = RVec3(mLocalSpacePosition1); + settings->mFixedPoint1 = mFixedPosition1; + settings->mBodyPoint2 = RVec3(mLocalSpacePosition2); + settings->mFixedPoint2 = mFixedPosition2; + settings->mRatio = mRatio; + settings->mMinLength = mMinLength; + settings->mMaxLength = mMaxLength; + return settings; +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PulleyConstraint.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PulleyConstraint.h new file mode 100644 index 00000000000..5f2523dae2b --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PulleyConstraint.h @@ -0,0 +1,137 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2022 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +JPH_NAMESPACE_BEGIN + +/// Pulley constraint settings, used to create a pulley constraint. +/// A pulley connects two bodies via two fixed world points to each other similar to a distance constraint. +/// We define Length1 = |BodyPoint1 - FixedPoint1| where Body1 is a point on body 1 in world space and FixedPoint1 a fixed point in world space +/// Length2 = |BodyPoint2 - FixedPoint2| +/// The constraint keeps the two line segments constrained so that +/// MinDistance <= Length1 + Ratio * Length2 <= MaxDistance +class JPH_EXPORT PulleyConstraintSettings final : public TwoBodyConstraintSettings +{ +public: + JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, PulleyConstraintSettings) + + // See: ConstraintSettings::SaveBinaryState + virtual void SaveBinaryState(StreamOut &inStream) const override; + + /// Create an instance of this constraint + virtual TwoBodyConstraint * Create(Body &inBody1, Body &inBody2) const override; + + /// This determines in which space the constraint is setup, specified properties below should be in the specified space + EConstraintSpace mSpace = EConstraintSpace::WorldSpace; + + /// Body 1 constraint attachment point (space determined by mSpace). + RVec3 mBodyPoint1 = RVec3::sZero(); + + /// Fixed world point to which body 1 is connected (always world space) + RVec3 mFixedPoint1 = RVec3::sZero(); + + /// Body 2 constraint attachment point (space determined by mSpace) + RVec3 mBodyPoint2 = RVec3::sZero(); + + /// Fixed world point to which body 2 is connected (always world space) + RVec3 mFixedPoint2 = RVec3::sZero(); + + /// Ratio between the two line segments (see formula above), can be used to create a block and tackle + float mRatio = 1.0f; + + /// The minimum length of the line segments (see formula above), use -1 to calculate the length based on the positions of the objects when the constraint is created. + float mMinLength = 0.0f; + + /// The maximum length of the line segments (see formula above), use -1 to calculate the length based on the positions of the objects when the constraint is created. + float mMaxLength = -1.0f; + +protected: + // See: ConstraintSettings::RestoreBinaryState + virtual void RestoreBinaryState(StreamIn &inStream) override; +}; + +/// A pulley constraint. +class JPH_EXPORT PulleyConstraint final : public TwoBodyConstraint +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Construct distance constraint + PulleyConstraint(Body &inBody1, Body &inBody2, const PulleyConstraintSettings &inSettings); + + // Generic interface of a constraint + virtual EConstraintSubType GetSubType() const override { return EConstraintSubType::Pulley; } + virtual void NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM) override; + virtual void SetupVelocityConstraint(float inDeltaTime) override; + virtual void ResetWarmStart() override; + virtual void WarmStartVelocityConstraint(float inWarmStartImpulseRatio) override; + virtual bool SolveVelocityConstraint(float inDeltaTime) override; + virtual bool SolvePositionConstraint(float inDeltaTime, float inBaumgarte) override; +#ifdef JPH_DEBUG_RENDERER + virtual void DrawConstraint(DebugRenderer *inRenderer) const override; +#endif // JPH_DEBUG_RENDERER + virtual void SaveState(StateRecorder &inStream) const override; + virtual void RestoreState(StateRecorder &inStream) override; + virtual Ref GetConstraintSettings() const override; + + // See: TwoBodyConstraint + virtual Mat44 GetConstraintToBody1Matrix() const override { return Mat44::sTranslation(mLocalSpacePosition1); } + virtual Mat44 GetConstraintToBody2Matrix() const override { return Mat44::sTranslation(mLocalSpacePosition2); } // Note: Incorrect rotation as we don't track the original rotation difference, should not matter though as the constraint is not limiting rotation. + + /// Update the minimum and maximum length for the constraint + void SetLength(float inMinLength, float inMaxLength) { JPH_ASSERT(inMinLength >= 0.0f && inMinLength <= inMaxLength); mMinLength = inMinLength; mMaxLength = inMaxLength; } + float GetMinLength() const { return mMinLength; } + float GetMaxLength() const { return mMaxLength; } + + /// Get the current length of both segments (multiplied by the ratio for segment 2) + float GetCurrentLength() const { return Vec3(mWorldSpacePosition1 - mFixedPosition1).Length() + mRatio * Vec3(mWorldSpacePosition2 - mFixedPosition2).Length(); } + + ///@name Get Lagrange multiplier from last physics update (the linear impulse applied to satisfy the constraint) + inline float GetTotalLambdaPosition() const { return mIndependentAxisConstraintPart.GetTotalLambda(); } + +private: + // Calculates world positions and normals and returns current length + float CalculatePositionsNormalsAndLength(); + + // Internal helper function to calculate the values below + void CalculateConstraintProperties(); + + // CONFIGURATION PROPERTIES FOLLOW + + // Local space constraint positions on the bodies + Vec3 mLocalSpacePosition1; + Vec3 mLocalSpacePosition2; + + // World space fixed positions + RVec3 mFixedPosition1; + RVec3 mFixedPosition2; + + /// Ratio between the two line segments + float mRatio; + + // The minimum/maximum length of the line segments + float mMinLength; + float mMaxLength; + + // RUN TIME PROPERTIES FOLLOW + + // World space positions and normal + RVec3 mWorldSpacePosition1; + RVec3 mWorldSpacePosition2; + Vec3 mWorldSpaceNormal1; + Vec3 mWorldSpaceNormal2; + + // Depending on if the length < min or length > max we can apply forces to prevent further violations + float mMinLambda; + float mMaxLambda; + + // The constraint part + IndependentAxisConstraintPart mIndependentAxisConstraintPart; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/RackAndPinionConstraint.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/RackAndPinionConstraint.cpp new file mode 100644 index 00000000000..8c54d48f4f1 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/RackAndPinionConstraint.cpp @@ -0,0 +1,189 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#include +#include +#include +#include +#ifdef JPH_DEBUG_RENDERER + #include +#endif // JPH_DEBUG_RENDERER + +JPH_NAMESPACE_BEGIN + +JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(RackAndPinionConstraintSettings) +{ + JPH_ADD_BASE_CLASS(RackAndPinionConstraintSettings, TwoBodyConstraintSettings) + + JPH_ADD_ENUM_ATTRIBUTE(RackAndPinionConstraintSettings, mSpace) + JPH_ADD_ATTRIBUTE(RackAndPinionConstraintSettings, mHingeAxis) + JPH_ADD_ATTRIBUTE(RackAndPinionConstraintSettings, mSliderAxis) + JPH_ADD_ATTRIBUTE(RackAndPinionConstraintSettings, mRatio) +} + +void RackAndPinionConstraintSettings::SaveBinaryState(StreamOut &inStream) const +{ + ConstraintSettings::SaveBinaryState(inStream); + + inStream.Write(mSpace); + inStream.Write(mHingeAxis); + inStream.Write(mSliderAxis); + inStream.Write(mRatio); +} + +void RackAndPinionConstraintSettings::RestoreBinaryState(StreamIn &inStream) +{ + ConstraintSettings::RestoreBinaryState(inStream); + + inStream.Read(mSpace); + inStream.Read(mHingeAxis); + inStream.Read(mSliderAxis); + inStream.Read(mRatio); +} + +TwoBodyConstraint *RackAndPinionConstraintSettings::Create(Body &inBody1, Body &inBody2) const +{ + return new RackAndPinionConstraint(inBody1, inBody2, *this); +} + +RackAndPinionConstraint::RackAndPinionConstraint(Body &inBody1, Body &inBody2, const RackAndPinionConstraintSettings &inSettings) : + TwoBodyConstraint(inBody1, inBody2, inSettings), + mLocalSpaceHingeAxis(inSettings.mHingeAxis), + mLocalSpaceSliderAxis(inSettings.mSliderAxis), + mRatio(inSettings.mRatio) +{ + if (inSettings.mSpace == EConstraintSpace::WorldSpace) + { + // If all properties were specified in world space, take them to local space now + mLocalSpaceHingeAxis = inBody1.GetInverseCenterOfMassTransform().Multiply3x3(mLocalSpaceHingeAxis).Normalized(); + mLocalSpaceSliderAxis = inBody2.GetInverseCenterOfMassTransform().Multiply3x3(mLocalSpaceSliderAxis).Normalized(); + } +} + +void RackAndPinionConstraint::CalculateConstraintProperties(Mat44Arg inRotation1, Mat44Arg inRotation2) +{ + // Calculate world space normals + mWorldSpaceHingeAxis = inRotation1 * mLocalSpaceHingeAxis; + mWorldSpaceSliderAxis = inRotation2 * mLocalSpaceSliderAxis; + + mRackAndPinionConstraintPart.CalculateConstraintProperties(*mBody1, mWorldSpaceHingeAxis, *mBody2, mWorldSpaceSliderAxis, mRatio); +} + +void RackAndPinionConstraint::SetupVelocityConstraint(float inDeltaTime) +{ + // Calculate constraint properties that are constant while bodies don't move + Mat44 rotation1 = Mat44::sRotation(mBody1->GetRotation()); + Mat44 rotation2 = Mat44::sRotation(mBody2->GetRotation()); + CalculateConstraintProperties(rotation1, rotation2); +} + +void RackAndPinionConstraint::ResetWarmStart() +{ + mRackAndPinionConstraintPart.Deactivate(); +} + +void RackAndPinionConstraint::WarmStartVelocityConstraint(float inWarmStartImpulseRatio) +{ + // Warm starting: Apply previous frame impulse + mRackAndPinionConstraintPart.WarmStart(*mBody1, *mBody2, inWarmStartImpulseRatio); +} + +bool RackAndPinionConstraint::SolveVelocityConstraint(float inDeltaTime) +{ + return mRackAndPinionConstraintPart.SolveVelocityConstraint(*mBody1, mWorldSpaceHingeAxis, *mBody2, mWorldSpaceSliderAxis, mRatio); +} + +bool RackAndPinionConstraint::SolvePositionConstraint(float inDeltaTime, float inBaumgarte) +{ + if (mRackConstraint == nullptr || mPinionConstraint == nullptr) + return false; + + float rotation; + if (mPinionConstraint->GetSubType() == EConstraintSubType::Hinge) + { + rotation = static_cast(mPinionConstraint.GetPtr())->GetCurrentAngle(); + } + else + { + JPH_ASSERT(false, "Unsupported"); + return false; + } + + float translation; + if (mRackConstraint->GetSubType() == EConstraintSubType::Slider) + { + translation = static_cast(mRackConstraint.GetPtr())->GetCurrentPosition(); + } + else + { + JPH_ASSERT(false, "Unsupported"); + return false; + } + + float error = CenterAngleAroundZero(fmod(rotation - mRatio * translation, 2.0f * JPH_PI)); + if (error == 0.0f) + return false; + + Mat44 rotation1 = Mat44::sRotation(mBody1->GetRotation()); + Mat44 rotation2 = Mat44::sRotation(mBody2->GetRotation()); + CalculateConstraintProperties(rotation1, rotation2); + return mRackAndPinionConstraintPart.SolvePositionConstraint(*mBody1, *mBody2, error, inBaumgarte); +} + +#ifdef JPH_DEBUG_RENDERER +void RackAndPinionConstraint::DrawConstraint(DebugRenderer *inRenderer) const +{ + RMat44 transform1 = mBody1->GetCenterOfMassTransform(); + RMat44 transform2 = mBody2->GetCenterOfMassTransform(); + + // Draw constraint axis + inRenderer->DrawArrow(transform1.GetTranslation(), transform1 * mLocalSpaceHingeAxis, Color::sGreen, 0.01f); + inRenderer->DrawArrow(transform2.GetTranslation(), transform2 * mLocalSpaceSliderAxis, Color::sBlue, 0.01f); +} + +#endif // JPH_DEBUG_RENDERER + +void RackAndPinionConstraint::SaveState(StateRecorder &inStream) const +{ + TwoBodyConstraint::SaveState(inStream); + + mRackAndPinionConstraintPart.SaveState(inStream); +} + +void RackAndPinionConstraint::RestoreState(StateRecorder &inStream) +{ + TwoBodyConstraint::RestoreState(inStream); + + mRackAndPinionConstraintPart.RestoreState(inStream); +} + +Ref RackAndPinionConstraint::GetConstraintSettings() const +{ + RackAndPinionConstraintSettings *settings = new RackAndPinionConstraintSettings; + ToConstraintSettings(*settings); + settings->mSpace = EConstraintSpace::LocalToBodyCOM; + settings->mHingeAxis = mLocalSpaceHingeAxis; + settings->mSliderAxis = mLocalSpaceSliderAxis; + settings->mRatio = mRatio; + return settings; +} + +Mat44 RackAndPinionConstraint::GetConstraintToBody1Matrix() const +{ + Vec3 perp = mLocalSpaceHingeAxis.GetNormalizedPerpendicular(); + return Mat44(Vec4(mLocalSpaceHingeAxis, 0), Vec4(perp, 0), Vec4(mLocalSpaceHingeAxis.Cross(perp), 0), Vec4(0, 0, 0, 1)); +} + +Mat44 RackAndPinionConstraint::GetConstraintToBody2Matrix() const +{ + Vec3 perp = mLocalSpaceSliderAxis.GetNormalizedPerpendicular(); + return Mat44(Vec4(mLocalSpaceSliderAxis, 0), Vec4(perp, 0), Vec4(mLocalSpaceSliderAxis.Cross(perp), 0), Vec4(0, 0, 0, 1)); +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/RackAndPinionConstraint.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/RackAndPinionConstraint.h new file mode 100644 index 00000000000..1a0e43efbc4 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/RackAndPinionConstraint.h @@ -0,0 +1,118 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +JPH_NAMESPACE_BEGIN + +/// Rack and pinion constraint (slider & gear) settings +class JPH_EXPORT RackAndPinionConstraintSettings final : public TwoBodyConstraintSettings +{ +public: + JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, RackAndPinionConstraintSettings) + + // See: ConstraintSettings::SaveBinaryState + virtual void SaveBinaryState(StreamOut &inStream) const override; + + /// Create an instance of this constraint. + /// Body1 should be the pinion (gear) and body 2 the rack (slider). + virtual TwoBodyConstraint * Create(Body &inBody1, Body &inBody2) const override; + + /// Defines the ratio between the rotation of the pinion and the translation of the rack. + /// The ratio is defined as: PinionRotation(t) = ratio * RackTranslation(t) + /// @param inNumTeethRack Number of teeth that the rack has + /// @param inRackLength Length of the rack + /// @param inNumTeethPinion Number of teeth the pinion has + void SetRatio(int inNumTeethRack, float inRackLength, int inNumTeethPinion) + { + mRatio = 2.0f * JPH_PI * inNumTeethRack / (inRackLength * inNumTeethPinion); + } + + /// This determines in which space the constraint is setup, all properties below should be in the specified space + EConstraintSpace mSpace = EConstraintSpace::WorldSpace; + + /// Body 1 (pinion) constraint reference frame (space determined by mSpace). + Vec3 mHingeAxis = Vec3::sAxisX(); + + /// Body 2 (rack) constraint reference frame (space determined by mSpace) + Vec3 mSliderAxis = Vec3::sAxisX(); + + /// Ratio between the rack and pinion, see SetRatio. + float mRatio = 1.0f; + +protected: + // See: ConstraintSettings::RestoreBinaryState + virtual void RestoreBinaryState(StreamIn &inStream) override; +}; + +/// A rack and pinion constraint constrains the rotation of body1 to the translation of body 2. +/// Note that this constraint needs to be used in conjunction with a hinge constraint for body 1 and a slider constraint for body 2. +class JPH_EXPORT RackAndPinionConstraint final : public TwoBodyConstraint +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Construct gear constraint + RackAndPinionConstraint(Body &inBody1, Body &inBody2, const RackAndPinionConstraintSettings &inSettings); + + // Generic interface of a constraint + virtual EConstraintSubType GetSubType() const override { return EConstraintSubType::RackAndPinion; } + virtual void NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM) override { /* Nothing */ } + virtual void SetupVelocityConstraint(float inDeltaTime) override; + virtual void ResetWarmStart() override; + virtual void WarmStartVelocityConstraint(float inWarmStartImpulseRatio) override; + virtual bool SolveVelocityConstraint(float inDeltaTime) override; + virtual bool SolvePositionConstraint(float inDeltaTime, float inBaumgarte) override; +#ifdef JPH_DEBUG_RENDERER + virtual void DrawConstraint(DebugRenderer *inRenderer) const override; +#endif // JPH_DEBUG_RENDERER + virtual void SaveState(StateRecorder &inStream) const override; + virtual void RestoreState(StateRecorder &inStream) override; + virtual Ref GetConstraintSettings() const override; + + // See: TwoBodyConstraint + virtual Mat44 GetConstraintToBody1Matrix() const override; + virtual Mat44 GetConstraintToBody2Matrix() const override; + + /// The constraints that constrain the rack and pinion (a slider and a hinge), optional and used to calculate the position error and fix numerical drift. + void SetConstraints(const Constraint *inPinion, const Constraint *inRack) { mPinionConstraint = inPinion; mRackConstraint = inRack; } + + ///@name Get Lagrange multiplier from last physics update (the linear/angular impulse applied to satisfy the constraint) + inline float GetTotalLambda() const { return mRackAndPinionConstraintPart.GetTotalLambda(); } + +private: + // Internal helper function to calculate the values below + void CalculateConstraintProperties(Mat44Arg inRotation1, Mat44Arg inRotation2); + + // CONFIGURATION PROPERTIES FOLLOW + + // Local space hinge axis + Vec3 mLocalSpaceHingeAxis; + + // Local space sliding direction + Vec3 mLocalSpaceSliderAxis; + + // Ratio between rack and pinion + float mRatio; + + // The constraints that constrain the rack and pinion (a slider and a hinge), optional and used to calculate the position error and fix numerical drift. + RefConst mPinionConstraint; + RefConst mRackConstraint; + + // RUN TIME PROPERTIES FOLLOW + + // World space hinge axis + Vec3 mWorldSpaceHingeAxis; + + // World space sliding direction + Vec3 mWorldSpaceSliderAxis; + + // The constraint parts + RackAndPinionConstraintPart mRackAndPinionConstraintPart; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/SixDOFConstraint.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/SixDOFConstraint.cpp new file mode 100644 index 00000000000..070a45e213d --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/SixDOFConstraint.cpp @@ -0,0 +1,900 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#include +#include +#include +#ifdef JPH_DEBUG_RENDERER + #include +#endif // JPH_DEBUG_RENDERER + +JPH_NAMESPACE_BEGIN + +JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(SixDOFConstraintSettings) +{ + JPH_ADD_BASE_CLASS(SixDOFConstraintSettings, TwoBodyConstraintSettings) + + JPH_ADD_ENUM_ATTRIBUTE(SixDOFConstraintSettings, mSpace) + JPH_ADD_ATTRIBUTE(SixDOFConstraintSettings, mPosition1) + JPH_ADD_ATTRIBUTE(SixDOFConstraintSettings, mAxisX1) + JPH_ADD_ATTRIBUTE(SixDOFConstraintSettings, mAxisY1) + JPH_ADD_ATTRIBUTE(SixDOFConstraintSettings, mPosition2) + JPH_ADD_ATTRIBUTE(SixDOFConstraintSettings, mAxisX2) + JPH_ADD_ATTRIBUTE(SixDOFConstraintSettings, mAxisY2) + JPH_ADD_ATTRIBUTE(SixDOFConstraintSettings, mMaxFriction) + JPH_ADD_ENUM_ATTRIBUTE(SixDOFConstraintSettings, mSwingType) + JPH_ADD_ATTRIBUTE(SixDOFConstraintSettings, mLimitMin) + JPH_ADD_ATTRIBUTE(SixDOFConstraintSettings, mLimitMax) + JPH_ADD_ATTRIBUTE(SixDOFConstraintSettings, mLimitsSpringSettings) + JPH_ADD_ATTRIBUTE(SixDOFConstraintSettings, mMotorSettings) +} + +void SixDOFConstraintSettings::SaveBinaryState(StreamOut &inStream) const +{ + ConstraintSettings::SaveBinaryState(inStream); + + inStream.Write(mSpace); + inStream.Write(mPosition1); + inStream.Write(mAxisX1); + inStream.Write(mAxisY1); + inStream.Write(mPosition2); + inStream.Write(mAxisX2); + inStream.Write(mAxisY2); + inStream.Write(mMaxFriction); + inStream.Write(mSwingType); + inStream.Write(mLimitMin); + inStream.Write(mLimitMax); + for (const SpringSettings &s : mLimitsSpringSettings) + s.SaveBinaryState(inStream); + for (const MotorSettings &m : mMotorSettings) + m.SaveBinaryState(inStream); +} + +void SixDOFConstraintSettings::RestoreBinaryState(StreamIn &inStream) +{ + ConstraintSettings::RestoreBinaryState(inStream); + + inStream.Read(mSpace); + inStream.Read(mPosition1); + inStream.Read(mAxisX1); + inStream.Read(mAxisY1); + inStream.Read(mPosition2); + inStream.Read(mAxisX2); + inStream.Read(mAxisY2); + inStream.Read(mMaxFriction); + inStream.Read(mSwingType); + inStream.Read(mLimitMin); + inStream.Read(mLimitMax); + for (SpringSettings &s : mLimitsSpringSettings) + s.RestoreBinaryState(inStream); + for (MotorSettings &m : mMotorSettings) + m.RestoreBinaryState(inStream); +} + +TwoBodyConstraint *SixDOFConstraintSettings::Create(Body &inBody1, Body &inBody2) const +{ + return new SixDOFConstraint(inBody1, inBody2, *this); +} + +void SixDOFConstraint::UpdateTranslationLimits() +{ + // Set to zero if the limits are inversed + for (int i = EAxis::TranslationX; i <= EAxis::TranslationZ; ++i) + if (mLimitMin[i] > mLimitMax[i]) + mLimitMin[i] = mLimitMax[i] = 0.0f; +} + +void SixDOFConstraint::UpdateRotationLimits() +{ + if (mSwingTwistConstraintPart.GetSwingType() == ESwingType::Cone) + { + // Cone swing upper limit needs to be positive + mLimitMax[EAxis::RotationY] = max(0.0f, mLimitMax[EAxis::RotationY]); + mLimitMax[EAxis::RotationZ] = max(0.0f, mLimitMax[EAxis::RotationZ]); + + // Cone swing limits only support symmetric ranges + mLimitMin[EAxis::RotationY] = -mLimitMax[EAxis::RotationY]; + mLimitMin[EAxis::RotationZ] = -mLimitMax[EAxis::RotationZ]; + } + + for (int i = EAxis::RotationX; i <= EAxis::RotationZ; ++i) + { + // Clamp to [-PI, PI] range + mLimitMin[i] = Clamp(mLimitMin[i], -JPH_PI, JPH_PI); + mLimitMax[i] = Clamp(mLimitMax[i], -JPH_PI, JPH_PI); + + // Set to zero if the limits are inversed + if (mLimitMin[i] > mLimitMax[i]) + mLimitMin[i] = mLimitMax[i] = 0.0f; + } + + // Pass limits on to constraint part + mSwingTwistConstraintPart.SetLimits(mLimitMin[EAxis::RotationX], mLimitMax[EAxis::RotationX], mLimitMin[EAxis::RotationY], mLimitMax[EAxis::RotationY], mLimitMin[EAxis::RotationZ], mLimitMax[EAxis::RotationZ]); +} + +void SixDOFConstraint::UpdateFixedFreeAxis() +{ + uint8 old_free_axis = mFreeAxis; + uint8 old_fixed_axis = mFixedAxis; + + // Cache which axis are fixed and which ones are free + mFreeAxis = 0; + mFixedAxis = 0; + for (int a = 0; a < EAxis::Num; ++a) + { + float limit = a >= EAxis::RotationX? JPH_PI : FLT_MAX; + + if (mLimitMin[a] >= mLimitMax[a]) + mFixedAxis |= 1 << a; + else if (mLimitMin[a] <= -limit && mLimitMax[a] >= limit) + mFreeAxis |= 1 << a; + } + + // On change we deactivate all constraints to reset warm starting + if (old_free_axis != mFreeAxis || old_fixed_axis != mFixedAxis) + { + for (AxisConstraintPart &c : mTranslationConstraintPart) + c.Deactivate(); + mPointConstraintPart.Deactivate(); + mSwingTwistConstraintPart.Deactivate(); + mRotationConstraintPart.Deactivate(); + for (AxisConstraintPart &c : mMotorTranslationConstraintPart) + c.Deactivate(); + for (AngleConstraintPart &c : mMotorRotationConstraintPart) + c.Deactivate(); + } +} + +SixDOFConstraint::SixDOFConstraint(Body &inBody1, Body &inBody2, const SixDOFConstraintSettings &inSettings) : + TwoBodyConstraint(inBody1, inBody2, inSettings) +{ + // Override swing type + mSwingTwistConstraintPart.SetSwingType(inSettings.mSwingType); + + // Calculate rotation needed to go from constraint space to body1 local space + Vec3 axis_z1 = inSettings.mAxisX1.Cross(inSettings.mAxisY1); + Mat44 c_to_b1(Vec4(inSettings.mAxisX1, 0), Vec4(inSettings.mAxisY1, 0), Vec4(axis_z1, 0), Vec4(0, 0, 0, 1)); + mConstraintToBody1 = c_to_b1.GetQuaternion(); + + // Calculate rotation needed to go from constraint space to body2 local space + Vec3 axis_z2 = inSettings.mAxisX2.Cross(inSettings.mAxisY2); + Mat44 c_to_b2(Vec4(inSettings.mAxisX2, 0), Vec4(inSettings.mAxisY2, 0), Vec4(axis_z2, 0), Vec4(0, 0, 0, 1)); + mConstraintToBody2 = c_to_b2.GetQuaternion(); + + if (inSettings.mSpace == EConstraintSpace::WorldSpace) + { + // If all properties were specified in world space, take them to local space now + mLocalSpacePosition1 = Vec3(inBody1.GetInverseCenterOfMassTransform() * inSettings.mPosition1); + mConstraintToBody1 = inBody1.GetRotation().Conjugated() * mConstraintToBody1; + + mLocalSpacePosition2 = Vec3(inBody2.GetInverseCenterOfMassTransform() * inSettings.mPosition2); + mConstraintToBody2 = inBody2.GetRotation().Conjugated() * mConstraintToBody2; + } + else + { + mLocalSpacePosition1 = Vec3(inSettings.mPosition1); + mLocalSpacePosition2 = Vec3(inSettings.mPosition2); + } + + // Copy translation and rotation limits + memcpy(mLimitMin, inSettings.mLimitMin, sizeof(mLimitMin)); + memcpy(mLimitMax, inSettings.mLimitMax, sizeof(mLimitMax)); + memcpy(mLimitsSpringSettings, inSettings.mLimitsSpringSettings, sizeof(mLimitsSpringSettings)); + UpdateTranslationLimits(); + UpdateRotationLimits(); + UpdateFixedFreeAxis(); + CacheHasSpringLimits(); + + // Store friction settings + memcpy(mMaxFriction, inSettings.mMaxFriction, sizeof(mMaxFriction)); + + // Store motor settings + for (int i = 0; i < EAxis::Num; ++i) + mMotorSettings[i] = inSettings.mMotorSettings[i]; + + // Cache if motors are active (motors are off initially, but we may have friction) + CacheTranslationMotorActive(); + CacheRotationMotorActive(); +} + +void SixDOFConstraint::NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM) +{ + if (mBody1->GetID() == inBodyID) + mLocalSpacePosition1 -= inDeltaCOM; + else if (mBody2->GetID() == inBodyID) + mLocalSpacePosition2 -= inDeltaCOM; +} + +void SixDOFConstraint::SetTranslationLimits(Vec3Arg inLimitMin, Vec3Arg inLimitMax) +{ + mLimitMin[EAxis::TranslationX] = inLimitMin.GetX(); + mLimitMin[EAxis::TranslationY] = inLimitMin.GetY(); + mLimitMin[EAxis::TranslationZ] = inLimitMin.GetZ(); + mLimitMax[EAxis::TranslationX] = inLimitMax.GetX(); + mLimitMax[EAxis::TranslationY] = inLimitMax.GetY(); + mLimitMax[EAxis::TranslationZ] = inLimitMax.GetZ(); + + UpdateTranslationLimits(); + UpdateFixedFreeAxis(); +} + +void SixDOFConstraint::SetRotationLimits(Vec3Arg inLimitMin, Vec3Arg inLimitMax) +{ + mLimitMin[EAxis::RotationX] = inLimitMin.GetX(); + mLimitMin[EAxis::RotationY] = inLimitMin.GetY(); + mLimitMin[EAxis::RotationZ] = inLimitMin.GetZ(); + mLimitMax[EAxis::RotationX] = inLimitMax.GetX(); + mLimitMax[EAxis::RotationY] = inLimitMax.GetY(); + mLimitMax[EAxis::RotationZ] = inLimitMax.GetZ(); + + UpdateRotationLimits(); + UpdateFixedFreeAxis(); +} + +void SixDOFConstraint::SetMaxFriction(EAxis inAxis, float inFriction) +{ + mMaxFriction[inAxis] = inFriction; + + if (inAxis >= EAxis::TranslationX && inAxis <= EAxis::TranslationZ) + CacheTranslationMotorActive(); + else + CacheRotationMotorActive(); +} + +void SixDOFConstraint::GetPositionConstraintProperties(Vec3 &outR1PlusU, Vec3 &outR2, Vec3 &outU) const +{ + RVec3 p1 = mBody1->GetCenterOfMassTransform() * mLocalSpacePosition1; + RVec3 p2 = mBody2->GetCenterOfMassTransform() * mLocalSpacePosition2; + outR1PlusU = Vec3(p2 - mBody1->GetCenterOfMassPosition()); // r1 + u = (p1 - x1) + (p2 - p1) = p2 - x1 + outR2 = Vec3(p2 - mBody2->GetCenterOfMassPosition()); + outU = Vec3(p2 - p1); +} + +Quat SixDOFConstraint::GetRotationInConstraintSpace() const +{ + // Let b1, b2 be the center of mass transform of body1 and body2 (For body1 this is mBody1->GetCenterOfMassTransform()) + // Let c1, c2 be the transform that takes a vector from constraint space to local space of body1 and body2 (For body1 this is Mat44::sRotationTranslation(mConstraintToBody1, mLocalSpacePosition1)) + // Let q be the rotation of the constraint in constraint space + // b2 takes a vector from the local space of body2 to world space + // To express this in terms of b1: b2 = b1 * c1 * q * c2^-1 + // c2^-1 goes from local body 2 space to constraint space + // q rotates the constraint + // c1 goes from constraint space to body 1 local space + // b1 goes from body 1 local space to world space + // So when the body rotations are given, q = (b1 * c1)^-1 * b2 c2 + // Or: q = (q1 * c1)^-1 * (q2 * c2) if we're only interested in rotations + return (mBody1->GetRotation() * mConstraintToBody1).Conjugated() * mBody2->GetRotation() * mConstraintToBody2; +} + +void SixDOFConstraint::CacheTranslationMotorActive() +{ + mTranslationMotorActive = mMotorState[EAxis::TranslationX] != EMotorState::Off + || mMotorState[EAxis::TranslationY] != EMotorState::Off + || mMotorState[EAxis::TranslationZ] != EMotorState::Off + || HasFriction(EAxis::TranslationX) + || HasFriction(EAxis::TranslationY) + || HasFriction(EAxis::TranslationZ); +} + +void SixDOFConstraint::CacheRotationMotorActive() +{ + mRotationMotorActive = mMotorState[EAxis::RotationX] != EMotorState::Off + || mMotorState[EAxis::RotationY] != EMotorState::Off + || mMotorState[EAxis::RotationZ] != EMotorState::Off + || HasFriction(EAxis::RotationX) + || HasFriction(EAxis::RotationY) + || HasFriction(EAxis::RotationZ); +} + +void SixDOFConstraint::CacheRotationPositionMotorActive() +{ + mRotationPositionMotorActive = 0; + for (int i = 0; i < 3; ++i) + if (mMotorState[EAxis::RotationX + i] == EMotorState::Position) + mRotationPositionMotorActive |= 1 << i; +} + +void SixDOFConstraint::CacheHasSpringLimits() +{ + mHasSpringLimits = mLimitsSpringSettings[EAxis::TranslationX].mFrequency > 0.0f + || mLimitsSpringSettings[EAxis::TranslationY].mFrequency > 0.0f + || mLimitsSpringSettings[EAxis::TranslationZ].mFrequency > 0.0f; +} + +void SixDOFConstraint::SetMotorState(EAxis inAxis, EMotorState inState) +{ + JPH_ASSERT(inState == EMotorState::Off || mMotorSettings[inAxis].IsValid()); + + if (mMotorState[inAxis] != inState) + { + mMotorState[inAxis] = inState; + + // Ensure that warm starting next frame doesn't apply any impulses (motor parts are repurposed for different modes) + if (inAxis >= EAxis::TranslationX && inAxis <= EAxis::TranslationZ) + { + mMotorTranslationConstraintPart[inAxis - EAxis::TranslationX].Deactivate(); + + CacheTranslationMotorActive(); + } + else + { + JPH_ASSERT(inAxis >= EAxis::RotationX && inAxis <= EAxis::RotationZ); + + mMotorRotationConstraintPart[inAxis - EAxis::RotationX].Deactivate(); + + CacheRotationMotorActive(); + CacheRotationPositionMotorActive(); + } + } +} + +void SixDOFConstraint::SetTargetOrientationCS(QuatArg inOrientation) +{ + Quat q_swing, q_twist; + inOrientation.GetSwingTwist(q_swing, q_twist); + + uint clamped_axis; + mSwingTwistConstraintPart.ClampSwingTwist(q_swing, q_twist, clamped_axis); + + if (clamped_axis != 0) + mTargetOrientation = q_swing * q_twist; + else + mTargetOrientation = inOrientation; +} + +void SixDOFConstraint::SetupVelocityConstraint(float inDeltaTime) +{ + // Get body rotations + Quat rotation1 = mBody1->GetRotation(); + Quat rotation2 = mBody2->GetRotation(); + + // Quaternion that rotates from body1's constraint space to world space + Quat constraint_body1_to_world = rotation1 * mConstraintToBody1; + + // Store world space axis of constraint space + Mat44 translation_axis_mat = Mat44::sRotation(constraint_body1_to_world); + for (int i = 0; i < 3; ++i) + mTranslationAxis[i] = translation_axis_mat.GetColumn3(i); + + if (IsTranslationFullyConstrained()) + { + // All translation locked: Setup point constraint + mPointConstraintPart.CalculateConstraintProperties(*mBody1, Mat44::sRotation(rotation1), mLocalSpacePosition1, *mBody2, Mat44::sRotation(rotation2), mLocalSpacePosition2); + } + else if (IsTranslationConstrained() || mTranslationMotorActive) + { + // Update world space positions (the bodies may have moved) + Vec3 r1_plus_u, r2, u; + GetPositionConstraintProperties(r1_plus_u, r2, u); + + // Setup axis constraint parts + for (int i = 0; i < 3; ++i) + { + EAxis axis = EAxis(EAxis::TranslationX + i); + + Vec3 translation_axis = mTranslationAxis[i]; + + // Calculate displacement along this axis + float d = translation_axis.Dot(u); + mDisplacement[i] = d; // Store for SolveVelocityConstraint + + // Setup limit constraint + bool constraint_active = false; + float constraint_value = 0.0f; + if (IsFixedAxis(axis)) + { + // When constraint is fixed it is always active + constraint_value = d - mLimitMin[i]; + constraint_active = true; + } + else if (!IsFreeAxis(axis)) + { + // When constraint is limited, it is only active when outside of the allowed range + if (d <= mLimitMin[i]) + { + constraint_value = d - mLimitMin[i]; + constraint_active = true; + } + else if (d >= mLimitMax[i]) + { + constraint_value = d - mLimitMax[i]; + constraint_active = true; + } + } + + if (constraint_active) + mTranslationConstraintPart[i].CalculateConstraintPropertiesWithSettings(inDeltaTime, *mBody1, r1_plus_u, *mBody2, r2, translation_axis, 0.0f, constraint_value, mLimitsSpringSettings[i]); + else + mTranslationConstraintPart[i].Deactivate(); + + // Setup motor constraint + switch (mMotorState[i]) + { + case EMotorState::Off: + if (HasFriction(axis)) + mMotorTranslationConstraintPart[i].CalculateConstraintProperties(*mBody1, r1_plus_u, *mBody2, r2, translation_axis); + else + mMotorTranslationConstraintPart[i].Deactivate(); + break; + + case EMotorState::Velocity: + mMotorTranslationConstraintPart[i].CalculateConstraintProperties(*mBody1, r1_plus_u, *mBody2, r2, translation_axis, -mTargetVelocity[i]); + break; + + case EMotorState::Position: + { + const SpringSettings &spring_settings = mMotorSettings[i].mSpringSettings; + if (spring_settings.HasStiffness()) + mMotorTranslationConstraintPart[i].CalculateConstraintPropertiesWithSettings(inDeltaTime, *mBody1, r1_plus_u, *mBody2, r2, translation_axis, 0.0f, translation_axis.Dot(u) - mTargetPosition[i], spring_settings); + else + mMotorTranslationConstraintPart[i].Deactivate(); + break; + } + } + } + } + + // Setup rotation constraints + if (IsRotationFullyConstrained()) + { + // All rotation locked: Setup rotation constraint + mRotationConstraintPart.CalculateConstraintProperties(*mBody1, Mat44::sRotation(mBody1->GetRotation()), *mBody2, Mat44::sRotation(mBody2->GetRotation())); + } + else if (IsRotationConstrained() || mRotationMotorActive) + { + // GetRotationInConstraintSpace without redoing the calculation of constraint_body1_to_world + Quat constraint_body2_to_world = mBody2->GetRotation() * mConstraintToBody2; + Quat q = constraint_body1_to_world.Conjugated() * constraint_body2_to_world; + + // Use swing twist constraint part + if (IsRotationConstrained()) + mSwingTwistConstraintPart.CalculateConstraintProperties(*mBody1, *mBody2, q, constraint_body1_to_world); + else + mSwingTwistConstraintPart.Deactivate(); + + if (mRotationMotorActive) + { + // Calculate rotation motor axis + Mat44 ws_axis = Mat44::sRotation(constraint_body2_to_world); + for (int i = 0; i < 3; ++i) + mRotationAxis[i] = ws_axis.GetColumn3(i); + + // Get target orientation along the shortest path from q + Quat target_orientation = q.Dot(mTargetOrientation) > 0.0f? mTargetOrientation : -mTargetOrientation; + + // The definition of the constraint rotation q: + // R2 * ConstraintToBody2 = R1 * ConstraintToBody1 * q (1) + // + // R2' is the rotation of body 2 when reaching the target_orientation: + // R2' * ConstraintToBody2 = R1 * ConstraintToBody1 * target_orientation (2) + // + // The difference in body 2 space: + // R2' = R2 * diff_body2 (3) + // + // We want to specify the difference in the constraint space of body 2: + // diff_body2 = ConstraintToBody2 * diff * ConstraintToBody2^* (4) + // + // Extracting R2' from 2: R2' = R1 * ConstraintToBody1 * target_orientation * ConstraintToBody2^* (5) + // Combining 3 & 4: R2' = R2 * ConstraintToBody2 * diff * ConstraintToBody2^* (6) + // Combining 1 & 6: R2' = R1 * ConstraintToBody1 * q * diff * ConstraintToBody2^* (7) + // Combining 5 & 7: R1 * ConstraintToBody1 * target_orientation * ConstraintToBody2^* = R1 * ConstraintToBody1 * q * diff * ConstraintToBody2^* + // <=> target_orientation = q * diff + // <=> diff = q^* * target_orientation + Quat diff = q.Conjugated() * target_orientation; + + // Project diff so that only rotation around axis that have a position motor are remaining + Quat projected_diff; + switch (mRotationPositionMotorActive) + { + case 0b001: + // Keep only rotation around X + projected_diff = diff.GetTwist(Vec3::sAxisX()); + break; + + case 0b010: + // Keep only rotation around Y + projected_diff = diff.GetTwist(Vec3::sAxisY()); + break; + + case 0b100: + // Keep only rotation around Z + projected_diff = diff.GetTwist(Vec3::sAxisZ()); + break; + + case 0b011: + // Remove rotation around Z + // q = swing_xy * twist_z <=> swing_xy = q * twist_z^* + projected_diff = diff * diff.GetTwist(Vec3::sAxisZ()).Conjugated(); + break; + + case 0b101: + // Remove rotation around Y + // q = swing_xz * twist_y <=> swing_xz = q * twist_y^* + projected_diff = diff * diff.GetTwist(Vec3::sAxisY()).Conjugated(); + break; + + case 0b110: + // Remove rotation around X + // q = swing_yz * twist_x <=> swing_yz = q * twist_x^* + projected_diff = diff * diff.GetTwist(Vec3::sAxisX()).Conjugated(); + break; + + case 0b111: + default: // All motors off is handled here but the results are unused + // Keep entire rotation + projected_diff = diff; + break; + } + + // Approximate error angles + // The imaginary part of a quaternion is rotation_axis * sin(angle / 2) + // If angle is small, sin(x) = x so angle[i] ~ 2.0f * rotation_axis[i] + // We'll be making small time steps, so if the angle is not small at least the sign will be correct and we'll move in the right direction + Vec3 rotation_error = -2.0f * projected_diff.GetXYZ(); + + // Setup motors + for (int i = 0; i < 3; ++i) + { + EAxis axis = EAxis(EAxis::RotationX + i); + + Vec3 rotation_axis = mRotationAxis[i]; + + switch (mMotorState[axis]) + { + case EMotorState::Off: + if (HasFriction(axis)) + mMotorRotationConstraintPart[i].CalculateConstraintProperties(*mBody1, *mBody2, rotation_axis); + else + mMotorRotationConstraintPart[i].Deactivate(); + break; + + case EMotorState::Velocity: + mMotorRotationConstraintPart[i].CalculateConstraintProperties(*mBody1, *mBody2, rotation_axis, -mTargetAngularVelocity[i]); + break; + + case EMotorState::Position: + { + const SpringSettings &spring_settings = mMotorSettings[axis].mSpringSettings; + if (spring_settings.HasStiffness()) + mMotorRotationConstraintPart[i].CalculateConstraintPropertiesWithSettings(inDeltaTime, *mBody1, *mBody2, rotation_axis, 0.0f, rotation_error[i], spring_settings); + else + mMotorRotationConstraintPart[i].Deactivate(); + break; + } + } + } + } + } +} + +void SixDOFConstraint::ResetWarmStart() +{ + for (AxisConstraintPart &c : mMotorTranslationConstraintPart) + c.Deactivate(); + for (AngleConstraintPart &c : mMotorRotationConstraintPart) + c.Deactivate(); + mRotationConstraintPart.Deactivate(); + mSwingTwistConstraintPart.Deactivate(); + mPointConstraintPart.Deactivate(); + for (AxisConstraintPart &c : mTranslationConstraintPart) + c.Deactivate(); +} + +void SixDOFConstraint::WarmStartVelocityConstraint(float inWarmStartImpulseRatio) +{ + // Warm start translation motors + if (mTranslationMotorActive) + for (int i = 0; i < 3; ++i) + if (mMotorTranslationConstraintPart[i].IsActive()) + mMotorTranslationConstraintPart[i].WarmStart(*mBody1, *mBody2, mTranslationAxis[i], inWarmStartImpulseRatio); + + // Warm start rotation motors + if (mRotationMotorActive) + for (AngleConstraintPart &c : mMotorRotationConstraintPart) + if (c.IsActive()) + c.WarmStart(*mBody1, *mBody2, inWarmStartImpulseRatio); + + // Warm start rotation constraints + if (IsRotationFullyConstrained()) + mRotationConstraintPart.WarmStart(*mBody1, *mBody2, inWarmStartImpulseRatio); + else if (IsRotationConstrained()) + mSwingTwistConstraintPart.WarmStart(*mBody1, *mBody2, inWarmStartImpulseRatio); + + // Warm start translation constraints + if (IsTranslationFullyConstrained()) + mPointConstraintPart.WarmStart(*mBody1, *mBody2, inWarmStartImpulseRatio); + else if (IsTranslationConstrained()) + for (int i = 0; i < 3; ++i) + if (mTranslationConstraintPart[i].IsActive()) + mTranslationConstraintPart[i].WarmStart(*mBody1, *mBody2, mTranslationAxis[i], inWarmStartImpulseRatio); +} + +bool SixDOFConstraint::SolveVelocityConstraint(float inDeltaTime) +{ + bool impulse = false; + + // Solve translation motor + if (mTranslationMotorActive) + for (int i = 0; i < 3; ++i) + if (mMotorTranslationConstraintPart[i].IsActive()) + switch (mMotorState[i]) + { + case EMotorState::Off: + { + // Apply friction only + float max_lambda = mMaxFriction[i] * inDeltaTime; + impulse |= mMotorTranslationConstraintPart[i].SolveVelocityConstraint(*mBody1, *mBody2, mTranslationAxis[i], -max_lambda, max_lambda); + break; + } + + case EMotorState::Velocity: + case EMotorState::Position: + // Drive motor + impulse |= mMotorTranslationConstraintPart[i].SolveVelocityConstraint(*mBody1, *mBody2, mTranslationAxis[i], inDeltaTime * mMotorSettings[i].mMinForceLimit, inDeltaTime * mMotorSettings[i].mMaxForceLimit); + break; + } + + // Solve rotation motor + if (mRotationMotorActive) + for (int i = 0; i < 3; ++i) + { + EAxis axis = EAxis(EAxis::RotationX + i); + if (mMotorRotationConstraintPart[i].IsActive()) + switch (mMotorState[axis]) + { + case EMotorState::Off: + { + // Apply friction only + float max_lambda = mMaxFriction[axis] * inDeltaTime; + impulse |= mMotorRotationConstraintPart[i].SolveVelocityConstraint(*mBody1, *mBody2, mRotationAxis[i], -max_lambda, max_lambda); + break; + } + + case EMotorState::Velocity: + case EMotorState::Position: + // Drive motor + impulse |= mMotorRotationConstraintPart[i].SolveVelocityConstraint(*mBody1, *mBody2, mRotationAxis[i], inDeltaTime * mMotorSettings[axis].mMinTorqueLimit, inDeltaTime * mMotorSettings[axis].mMaxTorqueLimit); + break; + } + } + + // Solve rotation constraint + if (IsRotationFullyConstrained()) + impulse |= mRotationConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2); + else if (IsRotationConstrained()) + impulse |= mSwingTwistConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2); + + // Solve position constraint + if (IsTranslationFullyConstrained()) + impulse |= mPointConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2); + else if (IsTranslationConstrained()) + for (int i = 0; i < 3; ++i) + if (mTranslationConstraintPart[i].IsActive()) + { + // If the axis is not fixed it must be limited (or else the constraint would not be active) + // Calculate the min and max constraint force based on on which side we're limited + float limit_min = -FLT_MAX, limit_max = FLT_MAX; + if (!IsFixedAxis(EAxis(EAxis::TranslationX + i))) + { + JPH_ASSERT(!IsFreeAxis(EAxis(EAxis::TranslationX + i))); + if (mDisplacement[i] <= mLimitMin[i]) + limit_min = 0; + else if (mDisplacement[i] >= mLimitMax[i]) + limit_max = 0; + } + + impulse |= mTranslationConstraintPart[i].SolveVelocityConstraint(*mBody1, *mBody2, mTranslationAxis[i], limit_min, limit_max); + } + + return impulse; +} + +bool SixDOFConstraint::SolvePositionConstraint(float inDeltaTime, float inBaumgarte) +{ + bool impulse = false; + + if (IsRotationFullyConstrained()) + { + // Rotation locked: Solve rotation constraint + + // Inverse of initial rotation from body 1 to body 2 in body 1 space + // Definition of initial orientation r0: q2 = q1 r0 + // Initial rotation (see: GetRotationInConstraintSpace): q2 = q1 c1 c2^-1 + // So: r0^-1 = (c1 c2^-1)^-1 = c2 * c1^-1 + Quat constraint_to_body1 = mConstraintToBody1 * Quat::sEulerAngles(GetRotationLimitsMin()); + Quat inv_initial_orientation = mConstraintToBody2 * constraint_to_body1.Conjugated(); + + // Solve rotation violations + mRotationConstraintPart.CalculateConstraintProperties(*mBody1, Mat44::sRotation(mBody1->GetRotation()), *mBody2, Mat44::sRotation(mBody2->GetRotation())); + impulse |= mRotationConstraintPart.SolvePositionConstraint(*mBody1, *mBody2, inv_initial_orientation, inBaumgarte); + } + else if (IsRotationConstrained()) + { + // Rotation partially constraint + + // Solve rotation violations + Quat q = GetRotationInConstraintSpace(); + impulse |= mSwingTwistConstraintPart.SolvePositionConstraint(*mBody1, *mBody2, q, mConstraintToBody1, mConstraintToBody2, inBaumgarte); + } + + // Solve position violations + if (IsTranslationFullyConstrained()) + { + // Translation locked: Solve point constraint + Vec3 local_space_position1 = mLocalSpacePosition1 + mConstraintToBody1 * GetTranslationLimitsMin(); + mPointConstraintPart.CalculateConstraintProperties(*mBody1, Mat44::sRotation(mBody1->GetRotation()), local_space_position1, *mBody2, Mat44::sRotation(mBody2->GetRotation()), mLocalSpacePosition2); + impulse |= mPointConstraintPart.SolvePositionConstraint(*mBody1, *mBody2, inBaumgarte); + } + else if (IsTranslationConstrained()) + { + // Translation partially locked: Solve per axis + for (int i = 0; i < 3; ++i) + if (mLimitsSpringSettings[i].mFrequency <= 0.0f) // If not soft limit + { + // Update world space positions (the bodies may have moved) + Vec3 r1_plus_u, r2, u; + GetPositionConstraintProperties(r1_plus_u, r2, u); + + // Quaternion that rotates from body1's constraint space to world space + Quat constraint_body1_to_world = mBody1->GetRotation() * mConstraintToBody1; + + // Calculate axis + Vec3 translation_axis; + switch (i) + { + case 0: translation_axis = constraint_body1_to_world.RotateAxisX(); break; + case 1: translation_axis = constraint_body1_to_world.RotateAxisY(); break; + default: JPH_ASSERT(i == 2); translation_axis = constraint_body1_to_world.RotateAxisZ(); break; + } + + // Determine position error + float error = 0.0f; + EAxis axis(EAxis(EAxis::TranslationX + i)); + if (IsFixedAxis(axis)) + error = u.Dot(translation_axis) - mLimitMin[axis]; + else if (!IsFreeAxis(axis)) + { + float displacement = u.Dot(translation_axis); + if (displacement <= mLimitMin[axis]) + error = displacement - mLimitMin[axis]; + else if (displacement >= mLimitMax[axis]) + error = displacement - mLimitMax[axis]; + } + + if (error != 0.0f) + { + // Setup axis constraint part and solve it + mTranslationConstraintPart[i].CalculateConstraintProperties(*mBody1, r1_plus_u, *mBody2, r2, translation_axis); + impulse |= mTranslationConstraintPart[i].SolvePositionConstraint(*mBody1, *mBody2, translation_axis, error, inBaumgarte); + } + } + } + + return impulse; +} + +#ifdef JPH_DEBUG_RENDERER +void SixDOFConstraint::DrawConstraint(DebugRenderer *inRenderer) const +{ + // Get constraint properties in world space + RVec3 position1 = mBody1->GetCenterOfMassTransform() * mLocalSpacePosition1; + Quat rotation1 = mBody1->GetRotation() * mConstraintToBody1; + Quat rotation2 = mBody2->GetRotation() * mConstraintToBody2; + + // Draw constraint orientation + inRenderer->DrawCoordinateSystem(RMat44::sRotationTranslation(rotation1, position1), mDrawConstraintSize); + + if ((IsRotationConstrained() || mRotationPositionMotorActive != 0) && !IsRotationFullyConstrained()) + { + // Draw current swing and twist + Quat q = GetRotationInConstraintSpace(); + Quat q_swing, q_twist; + q.GetSwingTwist(q_swing, q_twist); + inRenderer->DrawLine(position1, position1 + mDrawConstraintSize * (rotation1 * q_twist).RotateAxisY(), Color::sWhite); + inRenderer->DrawLine(position1, position1 + mDrawConstraintSize * (rotation1 * q_swing).RotateAxisX(), Color::sWhite); + } + + // Draw target rotation + Quat m_swing, m_twist; + mTargetOrientation.GetSwingTwist(m_swing, m_twist); + if (mMotorState[EAxis::RotationX] == EMotorState::Position) + inRenderer->DrawLine(position1, position1 + mDrawConstraintSize * (rotation1 * m_twist).RotateAxisY(), Color::sYellow); + if (mMotorState[EAxis::RotationY] == EMotorState::Position || mMotorState[EAxis::RotationZ] == EMotorState::Position) + inRenderer->DrawLine(position1, position1 + mDrawConstraintSize * (rotation1 * m_swing).RotateAxisX(), Color::sYellow); + + // Draw target angular velocity + Vec3 target_angular_velocity = Vec3::sZero(); + for (int i = 0; i < 3; ++i) + if (mMotorState[EAxis::RotationX + i] == EMotorState::Velocity) + target_angular_velocity.SetComponent(i, mTargetAngularVelocity[i]); + if (target_angular_velocity != Vec3::sZero()) + inRenderer->DrawArrow(position1, position1 + rotation2 * target_angular_velocity, Color::sRed, 0.1f); +} + +void SixDOFConstraint::DrawConstraintLimits(DebugRenderer *inRenderer) const +{ + // Get matrix that transforms from constraint space to world space + RMat44 constraint_body1_to_world = RMat44::sRotationTranslation(mBody1->GetRotation() * mConstraintToBody1, mBody1->GetCenterOfMassTransform() * mLocalSpacePosition1); + + // Draw limits + if (mSwingTwistConstraintPart.GetSwingType() == ESwingType::Pyramid) + inRenderer->DrawSwingPyramidLimits(constraint_body1_to_world, mLimitMin[EAxis::RotationY], mLimitMax[EAxis::RotationY], mLimitMin[EAxis::RotationZ], mLimitMax[EAxis::RotationZ], mDrawConstraintSize, Color::sGreen, DebugRenderer::ECastShadow::Off); + else + inRenderer->DrawSwingConeLimits(constraint_body1_to_world, mLimitMax[EAxis::RotationY], mLimitMax[EAxis::RotationZ], mDrawConstraintSize, Color::sGreen, DebugRenderer::ECastShadow::Off); + inRenderer->DrawPie(constraint_body1_to_world.GetTranslation(), mDrawConstraintSize, constraint_body1_to_world.GetAxisX(), constraint_body1_to_world.GetAxisY(), mLimitMin[EAxis::RotationX], mLimitMax[EAxis::RotationX], Color::sPurple, DebugRenderer::ECastShadow::Off); +} +#endif // JPH_DEBUG_RENDERER + +void SixDOFConstraint::SaveState(StateRecorder &inStream) const +{ + TwoBodyConstraint::SaveState(inStream); + + for (const AxisConstraintPart &c : mTranslationConstraintPart) + c.SaveState(inStream); + mPointConstraintPart.SaveState(inStream); + mSwingTwistConstraintPart.SaveState(inStream); + mRotationConstraintPart.SaveState(inStream); + for (const AxisConstraintPart &c : mMotorTranslationConstraintPart) + c.SaveState(inStream); + for (const AngleConstraintPart &c : mMotorRotationConstraintPart) + c.SaveState(inStream); + + inStream.Write(mMotorState); + inStream.Write(mTargetVelocity); + inStream.Write(mTargetAngularVelocity); + inStream.Write(mTargetPosition); + inStream.Write(mTargetOrientation); +} + +void SixDOFConstraint::RestoreState(StateRecorder &inStream) +{ + TwoBodyConstraint::RestoreState(inStream); + + for (AxisConstraintPart &c : mTranslationConstraintPart) + c.RestoreState(inStream); + mPointConstraintPart.RestoreState(inStream); + mSwingTwistConstraintPart.RestoreState(inStream); + mRotationConstraintPart.RestoreState(inStream); + for (AxisConstraintPart &c : mMotorTranslationConstraintPart) + c.RestoreState(inStream); + for (AngleConstraintPart &c : mMotorRotationConstraintPart) + c.RestoreState(inStream); + + inStream.Read(mMotorState); + inStream.Read(mTargetVelocity); + inStream.Read(mTargetAngularVelocity); + inStream.Read(mTargetPosition); + inStream.Read(mTargetOrientation); + + CacheTranslationMotorActive(); + CacheRotationMotorActive(); + CacheRotationPositionMotorActive(); +} + +Ref SixDOFConstraint::GetConstraintSettings() const +{ + SixDOFConstraintSettings *settings = new SixDOFConstraintSettings; + ToConstraintSettings(*settings); + settings->mSpace = EConstraintSpace::LocalToBodyCOM; + settings->mPosition1 = RVec3(mLocalSpacePosition1); + settings->mAxisX1 = mConstraintToBody1.RotateAxisX(); + settings->mAxisY1 = mConstraintToBody1.RotateAxisY(); + settings->mPosition2 = RVec3(mLocalSpacePosition2); + settings->mAxisX2 = mConstraintToBody2.RotateAxisX(); + settings->mAxisY2 = mConstraintToBody2.RotateAxisY(); + settings->mSwingType = mSwingTwistConstraintPart.GetSwingType(); + memcpy(settings->mLimitMin, mLimitMin, sizeof(mLimitMin)); + memcpy(settings->mLimitMax, mLimitMax, sizeof(mLimitMax)); + memcpy(settings->mMaxFriction, mMaxFriction, sizeof(mMaxFriction)); + for (int i = 0; i < EAxis::Num; ++i) + settings->mMotorSettings[i] = mMotorSettings[i]; + return settings; +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/SixDOFConstraint.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/SixDOFConstraint.h new file mode 100644 index 00000000000..1bc03eceda4 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/SixDOFConstraint.h @@ -0,0 +1,289 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +/// 6 Degree Of Freedom Constraint setup structure. Allows control over each of the 6 degrees of freedom. +class JPH_EXPORT SixDOFConstraintSettings final : public TwoBodyConstraintSettings +{ +public: + JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, SixDOFConstraintSettings) + + /// Constraint is split up into translation/rotation around X, Y and Z axis. + enum EAxis + { + TranslationX, + TranslationY, + TranslationZ, + + RotationX, + RotationY, + RotationZ, + + Num, + NumTranslation = TranslationZ + 1, + }; + + // See: ConstraintSettings::SaveBinaryState + virtual void SaveBinaryState(StreamOut &inStream) const override; + + /// Create an instance of this constraint + virtual TwoBodyConstraint * Create(Body &inBody1, Body &inBody2) const override; + + /// This determines in which space the constraint is setup, all properties below should be in the specified space + EConstraintSpace mSpace = EConstraintSpace::WorldSpace; + + /// Body 1 constraint reference frame (space determined by mSpace) + RVec3 mPosition1 = RVec3::sZero(); + Vec3 mAxisX1 = Vec3::sAxisX(); + Vec3 mAxisY1 = Vec3::sAxisY(); + + /// Body 2 constraint reference frame (space determined by mSpace) + RVec3 mPosition2 = RVec3::sZero(); + Vec3 mAxisX2 = Vec3::sAxisX(); + Vec3 mAxisY2 = Vec3::sAxisY(); + + /// Friction settings. + /// For translation: Max friction force in N. 0 = no friction. + /// For rotation: Max friction torque in Nm. 0 = no friction. + float mMaxFriction[EAxis::Num] = { 0, 0, 0, 0, 0, 0 }; + + /// The type of swing constraint that we want to use. + ESwingType mSwingType = ESwingType::Cone; + + /// Limits. + /// For translation: Min and max linear limits in m (0 is frame of body 1 and 2 coincide). + /// For rotation: Min and max angular limits in rad (0 is frame of body 1 and 2 coincide). See comments at Axis enum for limit ranges. + /// + /// Remove degree of freedom by setting min = FLT_MAX and max = -FLT_MAX. The constraint will be driven to 0 for this axis. + /// + /// Free movement over an axis is allowed when min = -FLT_MAX and max = FLT_MAX. + /// + /// Rotation limit around X-Axis: When limited, should be \f$\in [-\pi, \pi]\f$. Can be asymmetric around zero. + /// + /// Rotation limit around Y-Z Axis: Forms a pyramid or cone shaped limit: + /// * For pyramid, should be \f$\in [-\pi, \pi]\f$ and does not need to be symmetrical around zero. + /// * For cone should be \f$\in [0, \pi]\f$ and needs to be symmetrical around zero (min limit is assumed to be -max limit). + float mLimitMin[EAxis::Num] = { -FLT_MAX, -FLT_MAX, -FLT_MAX, -FLT_MAX, -FLT_MAX, -FLT_MAX }; + float mLimitMax[EAxis::Num] = { FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX }; + + /// When enabled, this makes the limits soft. When the constraint exceeds the limits, a spring force will pull it back. + /// Only soft translation limits are supported, soft rotation limits are not currently supported. + SpringSettings mLimitsSpringSettings[EAxis::NumTranslation]; + + /// Make axis free (unconstrained) + void MakeFreeAxis(EAxis inAxis) { mLimitMin[inAxis] = -FLT_MAX; mLimitMax[inAxis] = FLT_MAX; } + bool IsFreeAxis(EAxis inAxis) const { return mLimitMin[inAxis] == -FLT_MAX && mLimitMax[inAxis] == FLT_MAX; } + + /// Make axis fixed (fixed at value 0) + void MakeFixedAxis(EAxis inAxis) { mLimitMin[inAxis] = FLT_MAX; mLimitMax[inAxis] = -FLT_MAX; } + bool IsFixedAxis(EAxis inAxis) const { return mLimitMin[inAxis] >= mLimitMax[inAxis]; } + + /// Set a valid range for the constraint (if inMax < inMin, the axis will become fixed) + void SetLimitedAxis(EAxis inAxis, float inMin, float inMax) { mLimitMin[inAxis] = inMin; mLimitMax[inAxis] = inMax; } + + /// Motor settings for each axis + MotorSettings mMotorSettings[EAxis::Num]; + +protected: + // See: ConstraintSettings::RestoreBinaryState + virtual void RestoreBinaryState(StreamIn &inStream) override; +}; + +/// 6 Degree Of Freedom Constraint. Allows control over each of the 6 degrees of freedom. +class JPH_EXPORT SixDOFConstraint final : public TwoBodyConstraint +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Get Axis from settings class + using EAxis = SixDOFConstraintSettings::EAxis; + + /// Construct six DOF constraint + SixDOFConstraint(Body &inBody1, Body &inBody2, const SixDOFConstraintSettings &inSettings); + + /// Generic interface of a constraint + virtual EConstraintSubType GetSubType() const override { return EConstraintSubType::SixDOF; } + virtual void NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM) override; + virtual void SetupVelocityConstraint(float inDeltaTime) override; + virtual void ResetWarmStart() override; + virtual void WarmStartVelocityConstraint(float inWarmStartImpulseRatio) override; + virtual bool SolveVelocityConstraint(float inDeltaTime) override; + virtual bool SolvePositionConstraint(float inDeltaTime, float inBaumgarte) override; +#ifdef JPH_DEBUG_RENDERER + virtual void DrawConstraint(DebugRenderer *inRenderer) const override; + virtual void DrawConstraintLimits(DebugRenderer *inRenderer) const override; +#endif // JPH_DEBUG_RENDERER + virtual void SaveState(StateRecorder &inStream) const override; + virtual void RestoreState(StateRecorder &inStream) override; + virtual Ref GetConstraintSettings() const override; + + // See: TwoBodyConstraint + virtual Mat44 GetConstraintToBody1Matrix() const override { return Mat44::sRotationTranslation(mConstraintToBody1, mLocalSpacePosition1); } + virtual Mat44 GetConstraintToBody2Matrix() const override { return Mat44::sRotationTranslation(mConstraintToBody2, mLocalSpacePosition2); } + + /// Update the translation limits for this constraint + void SetTranslationLimits(Vec3Arg inLimitMin, Vec3Arg inLimitMax); + + /// Update the rotational limits for this constraint + void SetRotationLimits(Vec3Arg inLimitMin, Vec3Arg inLimitMax); + + /// Get constraint Limits + float GetLimitsMin(EAxis inAxis) const { return mLimitMin[inAxis]; } + float GetLimitsMax(EAxis inAxis) const { return mLimitMax[inAxis]; } + Vec3 GetTranslationLimitsMin() const { return Vec3::sLoadFloat3Unsafe(*reinterpret_cast(&mLimitMin[EAxis::TranslationX])); } + Vec3 GetTranslationLimitsMax() const { return Vec3::sLoadFloat3Unsafe(*reinterpret_cast(&mLimitMax[EAxis::TranslationX])); } + Vec3 GetRotationLimitsMin() const { return Vec3::sLoadFloat3Unsafe(*reinterpret_cast(&mLimitMin[EAxis::RotationX])); } + Vec3 GetRotationLimitsMax() const { return Vec3::sLoadFloat3Unsafe(*reinterpret_cast(&mLimitMax[EAxis::RotationX])); } + + /// Check which axis are fixed/free + inline bool IsFixedAxis(EAxis inAxis) const { return (mFixedAxis & (1 << inAxis)) != 0; } + inline bool IsFreeAxis(EAxis inAxis) const { return (mFreeAxis & (1 << inAxis)) != 0; } + + /// Update the limits spring settings + const SpringSettings & GetLimitsSpringSettings(EAxis inAxis) const { JPH_ASSERT(inAxis < EAxis::NumTranslation); return mLimitsSpringSettings[inAxis]; } + void SetLimitsSpringSettings(EAxis inAxis, const SpringSettings& inLimitsSpringSettings) { JPH_ASSERT(inAxis < EAxis::NumTranslation); mLimitsSpringSettings[inAxis] = inLimitsSpringSettings; CacheHasSpringLimits(); } + + /// Set the max friction for each axis + void SetMaxFriction(EAxis inAxis, float inFriction); + float GetMaxFriction(EAxis inAxis) const { return mMaxFriction[inAxis]; } + + /// Get rotation of constraint in constraint space + Quat GetRotationInConstraintSpace() const; + + /// Motor settings + MotorSettings & GetMotorSettings(EAxis inAxis) { return mMotorSettings[inAxis]; } + const MotorSettings & GetMotorSettings(EAxis inAxis) const { return mMotorSettings[inAxis]; } + + /// Motor controls. + /// Translation motors work in constraint space of body 1. + /// Rotation motors work in constraint space of body 2 (!). + void SetMotorState(EAxis inAxis, EMotorState inState); + EMotorState GetMotorState(EAxis inAxis) const { return mMotorState[inAxis]; } + + /// Set the target velocity in body 1 constraint space + Vec3 GetTargetVelocityCS() const { return mTargetVelocity; } + void SetTargetVelocityCS(Vec3Arg inVelocity) { mTargetVelocity = inVelocity; } + + /// Set the target angular velocity in body 2 constraint space (!) + void SetTargetAngularVelocityCS(Vec3Arg inAngularVelocity) { mTargetAngularVelocity = inAngularVelocity; } + Vec3 GetTargetAngularVelocityCS() const { return mTargetAngularVelocity; } + + /// Set the target position in body 1 constraint space + Vec3 GetTargetPositionCS() const { return mTargetPosition; } + void SetTargetPositionCS(Vec3Arg inPosition) { mTargetPosition = inPosition; } + + /// Set the target orientation in body 1 constraint space + void SetTargetOrientationCS(QuatArg inOrientation); + Quat GetTargetOrientationCS() const { return mTargetOrientation; } + + /// Set the target orientation in body space (R2 = R1 * inOrientation, where R1 and R2 are the world space rotations for body 1 and 2). + /// Solve: R2 * ConstraintToBody2 = R1 * ConstraintToBody1 * q (see SwingTwistConstraint::GetSwingTwist) and R2 = R1 * inOrientation for q. + void SetTargetOrientationBS(QuatArg inOrientation) { SetTargetOrientationCS(mConstraintToBody1.Conjugated() * inOrientation * mConstraintToBody2); } + + ///@name Get Lagrange multiplier from last physics update (the linear/angular impulse applied to satisfy the constraint) + inline Vec3 GetTotalLambdaPosition() const { return IsTranslationFullyConstrained()? mPointConstraintPart.GetTotalLambda() : Vec3(mTranslationConstraintPart[0].GetTotalLambda(), mTranslationConstraintPart[1].GetTotalLambda(), mTranslationConstraintPart[2].GetTotalLambda()); } + inline Vec3 GetTotalLambdaRotation() const { return IsRotationFullyConstrained()? mRotationConstraintPart.GetTotalLambda() : Vec3(mSwingTwistConstraintPart.GetTotalTwistLambda(), mSwingTwistConstraintPart.GetTotalSwingYLambda(), mSwingTwistConstraintPart.GetTotalSwingZLambda()); } + inline Vec3 GetTotalLambdaMotorTranslation() const { return Vec3(mMotorTranslationConstraintPart[0].GetTotalLambda(), mMotorTranslationConstraintPart[1].GetTotalLambda(), mMotorTranslationConstraintPart[2].GetTotalLambda()); } + inline Vec3 GetTotalLambdaMotorRotation() const { return Vec3(mMotorRotationConstraintPart[0].GetTotalLambda(), mMotorRotationConstraintPart[1].GetTotalLambda(), mMotorRotationConstraintPart[2].GetTotalLambda()); } + +private: + // Calculate properties needed for the position constraint + inline void GetPositionConstraintProperties(Vec3 &outR1PlusU, Vec3 &outR2, Vec3 &outU) const; + + // Sanitize the translation limits + inline void UpdateTranslationLimits(); + + // Propagate the rotation limits to the constraint part + inline void UpdateRotationLimits(); + + // Update the cached state of which axis are free and which ones are fixed + inline void UpdateFixedFreeAxis(); + + // Cache the state of mTranslationMotorActive + void CacheTranslationMotorActive(); + + // Cache the state of mRotationMotorActive + void CacheRotationMotorActive(); + + // Cache the state of mRotationPositionMotorActive + void CacheRotationPositionMotorActive(); + + /// Cache the state of mHasSpringLimits + void CacheHasSpringLimits(); + + // Constraint settings helper functions + inline bool IsTranslationConstrained() const { return (mFreeAxis & 0b111) != 0b111; } + inline bool IsTranslationFullyConstrained() const { return (mFixedAxis & 0b111) == 0b111 && !mHasSpringLimits; } + inline bool IsRotationConstrained() const { return (mFreeAxis & 0b111000) != 0b111000; } + inline bool IsRotationFullyConstrained() const { return (mFixedAxis & 0b111000) == 0b111000; } + inline bool HasFriction(EAxis inAxis) const { return !IsFixedAxis(inAxis) && mMaxFriction[inAxis] > 0.0f; } + + // CONFIGURATION PROPERTIES FOLLOW + + // Local space constraint positions + Vec3 mLocalSpacePosition1; + Vec3 mLocalSpacePosition2; + + // Transforms from constraint space to body space + Quat mConstraintToBody1; + Quat mConstraintToBody2; + + // Limits + uint8 mFreeAxis = 0; // Bitmask of free axis (bit 0 = TranslationX) + uint8 mFixedAxis = 0; // Bitmask of fixed axis (bit 0 = TranslationX) + bool mTranslationMotorActive = false; // If any of the translational frictions / motors are active + bool mRotationMotorActive = false; // If any of the rotational frictions / motors are active + uint8 mRotationPositionMotorActive = 0; // Bitmask of axis that have position motor active (bit 0 = RotationX) + bool mHasSpringLimits = false; // If any of the limit springs have a non-zero frequency/stiffness + float mLimitMin[EAxis::Num]; + float mLimitMax[EAxis::Num]; + SpringSettings mLimitsSpringSettings[EAxis::NumTranslation]; + + // Motor settings for each axis + MotorSettings mMotorSettings[EAxis::Num]; + + // Friction settings for each axis + float mMaxFriction[EAxis::Num]; + + // Motor controls + EMotorState mMotorState[EAxis::Num] = { EMotorState::Off, EMotorState::Off, EMotorState::Off, EMotorState::Off, EMotorState::Off, EMotorState::Off }; + Vec3 mTargetVelocity = Vec3::sZero(); + Vec3 mTargetAngularVelocity = Vec3::sZero(); + Vec3 mTargetPosition = Vec3::sZero(); + Quat mTargetOrientation = Quat::sIdentity(); + + // RUN TIME PROPERTIES FOLLOW + + // Constraint space axis in world space + Vec3 mTranslationAxis[3]; + Vec3 mRotationAxis[3]; + + // Translation displacement (valid when translation axis has a range limit) + float mDisplacement[3]; + + // Individual constraint parts for translation, or a combined point constraint part if all axis are fixed + AxisConstraintPart mTranslationConstraintPart[3]; + PointConstraintPart mPointConstraintPart; + + // Individual constraint parts for rotation or a combined constraint part if rotation is fixed + SwingTwistConstraintPart mSwingTwistConstraintPart; + RotationEulerConstraintPart mRotationConstraintPart; + + // Motor or friction constraints + AxisConstraintPart mMotorTranslationConstraintPart[3]; + AngleConstraintPart mMotorRotationConstraintPart[3]; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/SliderConstraint.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/SliderConstraint.cpp new file mode 100644 index 00000000000..75335c32cba --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/SliderConstraint.cpp @@ -0,0 +1,501 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#include +#include +#ifdef JPH_DEBUG_RENDERER + #include +#endif // JPH_DEBUG_RENDERER + +JPH_NAMESPACE_BEGIN + +using namespace literals; + +JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(SliderConstraintSettings) +{ + JPH_ADD_BASE_CLASS(SliderConstraintSettings, TwoBodyConstraintSettings) + + JPH_ADD_ENUM_ATTRIBUTE(SliderConstraintSettings, mSpace) + JPH_ADD_ATTRIBUTE(SliderConstraintSettings, mAutoDetectPoint) + JPH_ADD_ATTRIBUTE(SliderConstraintSettings, mPoint1) + JPH_ADD_ATTRIBUTE(SliderConstraintSettings, mSliderAxis1) + JPH_ADD_ATTRIBUTE(SliderConstraintSettings, mNormalAxis1) + JPH_ADD_ATTRIBUTE(SliderConstraintSettings, mPoint2) + JPH_ADD_ATTRIBUTE(SliderConstraintSettings, mSliderAxis2) + JPH_ADD_ATTRIBUTE(SliderConstraintSettings, mNormalAxis2) + JPH_ADD_ATTRIBUTE(SliderConstraintSettings, mLimitsMin) + JPH_ADD_ATTRIBUTE(SliderConstraintSettings, mLimitsMax) + JPH_ADD_ENUM_ATTRIBUTE_WITH_ALIAS(SliderConstraintSettings, mLimitsSpringSettings.mMode, "mSpringMode") + JPH_ADD_ATTRIBUTE_WITH_ALIAS(SliderConstraintSettings, mLimitsSpringSettings.mFrequency, "mFrequency") // Renaming attributes to stay compatible with old versions of the library + JPH_ADD_ATTRIBUTE_WITH_ALIAS(SliderConstraintSettings, mLimitsSpringSettings.mDamping, "mDamping") + JPH_ADD_ATTRIBUTE(SliderConstraintSettings, mMaxFrictionForce) + JPH_ADD_ATTRIBUTE(SliderConstraintSettings, mMotorSettings) +} + +void SliderConstraintSettings::SetSliderAxis(Vec3Arg inSliderAxis) +{ + JPH_ASSERT(mSpace == EConstraintSpace::WorldSpace); + + mSliderAxis1 = mSliderAxis2 = inSliderAxis; + mNormalAxis1 = mNormalAxis2 = inSliderAxis.GetNormalizedPerpendicular(); +} + +void SliderConstraintSettings::SaveBinaryState(StreamOut &inStream) const +{ + ConstraintSettings::SaveBinaryState(inStream); + + inStream.Write(mSpace); + inStream.Write(mAutoDetectPoint); + inStream.Write(mPoint1); + inStream.Write(mSliderAxis1); + inStream.Write(mNormalAxis1); + inStream.Write(mPoint2); + inStream.Write(mSliderAxis2); + inStream.Write(mNormalAxis2); + inStream.Write(mLimitsMin); + inStream.Write(mLimitsMax); + inStream.Write(mMaxFrictionForce); + mLimitsSpringSettings.SaveBinaryState(inStream); + mMotorSettings.SaveBinaryState(inStream); +} + +void SliderConstraintSettings::RestoreBinaryState(StreamIn &inStream) +{ + ConstraintSettings::RestoreBinaryState(inStream); + + inStream.Read(mSpace); + inStream.Read(mAutoDetectPoint); + inStream.Read(mPoint1); + inStream.Read(mSliderAxis1); + inStream.Read(mNormalAxis1); + inStream.Read(mPoint2); + inStream.Read(mSliderAxis2); + inStream.Read(mNormalAxis2); + inStream.Read(mLimitsMin); + inStream.Read(mLimitsMax); + inStream.Read(mMaxFrictionForce); + mLimitsSpringSettings.RestoreBinaryState(inStream); + mMotorSettings.RestoreBinaryState(inStream); +} + +TwoBodyConstraint *SliderConstraintSettings::Create(Body &inBody1, Body &inBody2) const +{ + return new SliderConstraint(inBody1, inBody2, *this); +} + +SliderConstraint::SliderConstraint(Body &inBody1, Body &inBody2, const SliderConstraintSettings &inSettings) : + TwoBodyConstraint(inBody1, inBody2, inSettings), + mMaxFrictionForce(inSettings.mMaxFrictionForce), + mMotorSettings(inSettings.mMotorSettings) +{ + // Store inverse of initial rotation from body 1 to body 2 in body 1 space + mInvInitialOrientation = RotationEulerConstraintPart::sGetInvInitialOrientationXY(inSettings.mSliderAxis1, inSettings.mNormalAxis1, inSettings.mSliderAxis2, inSettings.mNormalAxis2); + + if (inSettings.mSpace == EConstraintSpace::WorldSpace) + { + RMat44 inv_transform1 = inBody1.GetInverseCenterOfMassTransform(); + RMat44 inv_transform2 = inBody2.GetInverseCenterOfMassTransform(); + + if (inSettings.mAutoDetectPoint) + { + // Determine anchor point: If any of the bodies can never be dynamic use the other body as anchor point + RVec3 anchor; + if (!inBody1.CanBeKinematicOrDynamic()) + anchor = inBody2.GetCenterOfMassPosition(); + else if (!inBody2.CanBeKinematicOrDynamic()) + anchor = inBody1.GetCenterOfMassPosition(); + else + { + // Otherwise use weighted anchor point towards the lightest body + Real inv_m1 = Real(inBody1.GetMotionPropertiesUnchecked()->GetInverseMassUnchecked()); + Real inv_m2 = Real(inBody2.GetMotionPropertiesUnchecked()->GetInverseMassUnchecked()); + Real total_inv_mass = inv_m1 + inv_m2; + if (total_inv_mass != 0.0_r) + anchor = (inv_m1 * inBody1.GetCenterOfMassPosition() + inv_m2 * inBody2.GetCenterOfMassPosition()) / total_inv_mass; + else + anchor = inBody1.GetCenterOfMassPosition(); + } + + // Store local positions + mLocalSpacePosition1 = Vec3(inv_transform1 * anchor); + mLocalSpacePosition2 = Vec3(inv_transform2 * anchor); + } + else + { + // Store local positions + mLocalSpacePosition1 = Vec3(inv_transform1 * inSettings.mPoint1); + mLocalSpacePosition2 = Vec3(inv_transform2 * inSettings.mPoint2); + } + + // If all properties were specified in world space, take them to local space now + mLocalSpaceSliderAxis1 = inv_transform1.Multiply3x3(inSettings.mSliderAxis1).Normalized(); + mLocalSpaceNormal1 = inv_transform1.Multiply3x3(inSettings.mNormalAxis1).Normalized(); + + // Constraints were specified in world space, so we should have replaced c1 with q10^-1 c1 and c2 with q20^-1 c2 + // => r0^-1 = (q20^-1 c2) (q10^-1 c1)^1 = q20^-1 (c2 c1^-1) q10 + mInvInitialOrientation = inBody2.GetRotation().Conjugated() * mInvInitialOrientation * inBody1.GetRotation(); + } + else + { + // Store local positions + mLocalSpacePosition1 = Vec3(inSettings.mPoint1); + mLocalSpacePosition2 = Vec3(inSettings.mPoint2); + + // Store local space axis + mLocalSpaceSliderAxis1 = inSettings.mSliderAxis1; + mLocalSpaceNormal1 = inSettings.mNormalAxis1; + } + + // Calculate 2nd local space normal + mLocalSpaceNormal2 = mLocalSpaceSliderAxis1.Cross(mLocalSpaceNormal1); + + // Store limits + JPH_ASSERT(inSettings.mLimitsMin != inSettings.mLimitsMax || inSettings.mLimitsSpringSettings.mFrequency > 0.0f, "Better use a fixed constraint"); + SetLimits(inSettings.mLimitsMin, inSettings.mLimitsMax); + + // Store spring settings + SetLimitsSpringSettings(inSettings.mLimitsSpringSettings); +} + +void SliderConstraint::NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM) +{ + if (mBody1->GetID() == inBodyID) + mLocalSpacePosition1 -= inDeltaCOM; + else if (mBody2->GetID() == inBodyID) + mLocalSpacePosition2 -= inDeltaCOM; +} + +float SliderConstraint::GetCurrentPosition() const +{ + // See: CalculateR1R2U and CalculateSlidingAxisAndPosition + Vec3 r1 = mBody1->GetRotation() * mLocalSpacePosition1; + Vec3 r2 = mBody2->GetRotation() * mLocalSpacePosition2; + Vec3 u = Vec3(mBody2->GetCenterOfMassPosition() - mBody1->GetCenterOfMassPosition()) + r2 - r1; + return u.Dot(mBody1->GetRotation() * mLocalSpaceSliderAxis1); +} + +void SliderConstraint::SetLimits(float inLimitsMin, float inLimitsMax) +{ + JPH_ASSERT(inLimitsMin <= 0.0f); + JPH_ASSERT(inLimitsMax >= 0.0f); + mLimitsMin = inLimitsMin; + mLimitsMax = inLimitsMax; + mHasLimits = mLimitsMin != -FLT_MAX || mLimitsMax != FLT_MAX; +} + +void SliderConstraint::CalculateR1R2U(Mat44Arg inRotation1, Mat44Arg inRotation2) +{ + // Calculate points relative to body + mR1 = inRotation1 * mLocalSpacePosition1; + mR2 = inRotation2 * mLocalSpacePosition2; + + // Calculate X2 + R2 - X1 - R1 + mU = Vec3(mBody2->GetCenterOfMassPosition() - mBody1->GetCenterOfMassPosition()) + mR2 - mR1; +} + +void SliderConstraint::CalculatePositionConstraintProperties(Mat44Arg inRotation1, Mat44Arg inRotation2) +{ + // Calculate world space normals + mN1 = inRotation1 * mLocalSpaceNormal1; + mN2 = inRotation1 * mLocalSpaceNormal2; + + mPositionConstraintPart.CalculateConstraintProperties(*mBody1, inRotation1, mR1 + mU, *mBody2, inRotation2, mR2, mN1, mN2); +} + +void SliderConstraint::CalculateSlidingAxisAndPosition(Mat44Arg inRotation1) +{ + if (mHasLimits || mMotorState != EMotorState::Off || mMaxFrictionForce > 0.0f) + { + // Calculate world space slider axis + mWorldSpaceSliderAxis = inRotation1 * mLocalSpaceSliderAxis1; + + // Calculate slide distance along axis + mD = mU.Dot(mWorldSpaceSliderAxis); + } +} + +void SliderConstraint::CalculatePositionLimitsConstraintProperties(float inDeltaTime) +{ + // Check if distance is within limits + bool below_min = mD <= mLimitsMin; + if (mHasLimits && (below_min || mD >= mLimitsMax)) + mPositionLimitsConstraintPart.CalculateConstraintPropertiesWithSettings(inDeltaTime, *mBody1, mR1 + mU, *mBody2, mR2, mWorldSpaceSliderAxis, 0.0f, mD - (below_min? mLimitsMin : mLimitsMax), mLimitsSpringSettings); + else + mPositionLimitsConstraintPart.Deactivate(); +} + +void SliderConstraint::CalculateMotorConstraintProperties(float inDeltaTime) +{ + switch (mMotorState) + { + case EMotorState::Off: + if (mMaxFrictionForce > 0.0f) + mMotorConstraintPart.CalculateConstraintProperties(*mBody1, mR1 + mU, *mBody2, mR2, mWorldSpaceSliderAxis); + else + mMotorConstraintPart.Deactivate(); + break; + + case EMotorState::Velocity: + mMotorConstraintPart.CalculateConstraintProperties(*mBody1, mR1 + mU, *mBody2, mR2, mWorldSpaceSliderAxis, -mTargetVelocity); + break; + + case EMotorState::Position: + if (mMotorSettings.mSpringSettings.HasStiffness()) + mMotorConstraintPart.CalculateConstraintPropertiesWithSettings(inDeltaTime, *mBody1, mR1 + mU, *mBody2, mR2, mWorldSpaceSliderAxis, 0.0f, mD - mTargetPosition, mMotorSettings.mSpringSettings); + else + mMotorConstraintPart.Deactivate(); + break; + } +} + +void SliderConstraint::SetupVelocityConstraint(float inDeltaTime) +{ + // Calculate constraint properties that are constant while bodies don't move + Mat44 rotation1 = Mat44::sRotation(mBody1->GetRotation()); + Mat44 rotation2 = Mat44::sRotation(mBody2->GetRotation()); + CalculateR1R2U(rotation1, rotation2); + CalculatePositionConstraintProperties(rotation1, rotation2); + mRotationConstraintPart.CalculateConstraintProperties(*mBody1, rotation1, *mBody2, rotation2); + CalculateSlidingAxisAndPosition(rotation1); + CalculatePositionLimitsConstraintProperties(inDeltaTime); + CalculateMotorConstraintProperties(inDeltaTime); +} + +void SliderConstraint::ResetWarmStart() +{ + mMotorConstraintPart.Deactivate(); + mPositionConstraintPart.Deactivate(); + mRotationConstraintPart.Deactivate(); + mPositionLimitsConstraintPart.Deactivate(); +} + +void SliderConstraint::WarmStartVelocityConstraint(float inWarmStartImpulseRatio) +{ + // Warm starting: Apply previous frame impulse + mMotorConstraintPart.WarmStart(*mBody1, *mBody2, mWorldSpaceSliderAxis, inWarmStartImpulseRatio); + mPositionConstraintPart.WarmStart(*mBody1, *mBody2, mN1, mN2, inWarmStartImpulseRatio); + mRotationConstraintPart.WarmStart(*mBody1, *mBody2, inWarmStartImpulseRatio); + mPositionLimitsConstraintPart.WarmStart(*mBody1, *mBody2, mWorldSpaceSliderAxis, inWarmStartImpulseRatio); +} + +bool SliderConstraint::SolveVelocityConstraint(float inDeltaTime) +{ + // Solve motor + bool motor = false; + if (mMotorConstraintPart.IsActive()) + { + switch (mMotorState) + { + case EMotorState::Off: + { + float max_lambda = mMaxFrictionForce * inDeltaTime; + motor = mMotorConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2, mWorldSpaceSliderAxis, -max_lambda, max_lambda); + break; + } + + case EMotorState::Velocity: + case EMotorState::Position: + motor = mMotorConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2, mWorldSpaceSliderAxis, inDeltaTime * mMotorSettings.mMinForceLimit, inDeltaTime * mMotorSettings.mMaxForceLimit); + break; + } + } + + // Solve position constraint along 2 axis + bool pos = mPositionConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2, mN1, mN2); + + // Solve rotation constraint + bool rot = mRotationConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2); + + // Solve limits along slider axis + bool limit = false; + if (mPositionLimitsConstraintPart.IsActive()) + { + float min_lambda, max_lambda; + if (mLimitsMin == mLimitsMax) + { + min_lambda = -FLT_MAX; + max_lambda = FLT_MAX; + } + else if (mD <= mLimitsMin) + { + min_lambda = 0.0f; + max_lambda = FLT_MAX; + } + else + { + min_lambda = -FLT_MAX; + max_lambda = 0.0f; + } + limit = mPositionLimitsConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2, mWorldSpaceSliderAxis, min_lambda, max_lambda); + } + + return motor || pos || rot || limit; +} + +bool SliderConstraint::SolvePositionConstraint(float inDeltaTime, float inBaumgarte) +{ + // Motor operates on velocities only, don't call SolvePositionConstraint + + // Solve position constraint along 2 axis + Mat44 rotation1 = Mat44::sRotation(mBody1->GetRotation()); + Mat44 rotation2 = Mat44::sRotation(mBody2->GetRotation()); + CalculateR1R2U(rotation1, rotation2); + CalculatePositionConstraintProperties(rotation1, rotation2); + bool pos = mPositionConstraintPart.SolvePositionConstraint(*mBody1, *mBody2, mU, mN1, mN2, inBaumgarte); + + // Solve rotation constraint + mRotationConstraintPart.CalculateConstraintProperties(*mBody1, Mat44::sRotation(mBody1->GetRotation()), *mBody2, Mat44::sRotation(mBody2->GetRotation())); + bool rot = mRotationConstraintPart.SolvePositionConstraint(*mBody1, *mBody2, mInvInitialOrientation, inBaumgarte); + + // Solve limits along slider axis + bool limit = false; + if (mHasLimits && mLimitsSpringSettings.mFrequency <= 0.0f) + { + rotation1 = Mat44::sRotation(mBody1->GetRotation()); + rotation2 = Mat44::sRotation(mBody2->GetRotation()); + CalculateR1R2U(rotation1, rotation2); + CalculateSlidingAxisAndPosition(rotation1); + CalculatePositionLimitsConstraintProperties(inDeltaTime); + if (mPositionLimitsConstraintPart.IsActive()) + { + if (mD <= mLimitsMin) + limit = mPositionLimitsConstraintPart.SolvePositionConstraint(*mBody1, *mBody2, mWorldSpaceSliderAxis, mD - mLimitsMin, inBaumgarte); + else + { + JPH_ASSERT(mD >= mLimitsMax); + limit = mPositionLimitsConstraintPart.SolvePositionConstraint(*mBody1, *mBody2, mWorldSpaceSliderAxis, mD - mLimitsMax, inBaumgarte); + } + } + } + + return pos || rot || limit; +} + +#ifdef JPH_DEBUG_RENDERER +void SliderConstraint::DrawConstraint(DebugRenderer *inRenderer) const +{ + RMat44 transform1 = mBody1->GetCenterOfMassTransform(); + RMat44 transform2 = mBody2->GetCenterOfMassTransform(); + + // Transform the local positions into world space + Vec3 slider_axis = transform1.Multiply3x3(mLocalSpaceSliderAxis1); + RVec3 position1 = transform1 * mLocalSpacePosition1; + RVec3 position2 = transform2 * mLocalSpacePosition2; + + // Draw constraint + inRenderer->DrawMarker(position1, Color::sRed, 0.1f); + inRenderer->DrawMarker(position2, Color::sGreen, 0.1f); + inRenderer->DrawLine(position1, position2, Color::sGreen); + + // Draw motor + switch (mMotorState) + { + case EMotorState::Position: + inRenderer->DrawMarker(position1 + mTargetPosition * slider_axis, Color::sYellow, 1.0f); + break; + + case EMotorState::Velocity: + { + Vec3 cur_vel = (mBody2->GetLinearVelocity() - mBody1->GetLinearVelocity()).Dot(slider_axis) * slider_axis; + inRenderer->DrawLine(position2, position2 + cur_vel, Color::sBlue); + inRenderer->DrawArrow(position2 + cur_vel, position2 + mTargetVelocity * slider_axis, Color::sRed, 0.1f); + break; + } + + case EMotorState::Off: + break; + } +} + +void SliderConstraint::DrawConstraintLimits(DebugRenderer *inRenderer) const +{ + if (mHasLimits) + { + RMat44 transform1 = mBody1->GetCenterOfMassTransform(); + RMat44 transform2 = mBody2->GetCenterOfMassTransform(); + + // Transform the local positions into world space + Vec3 slider_axis = transform1.Multiply3x3(mLocalSpaceSliderAxis1); + RVec3 position1 = transform1 * mLocalSpacePosition1; + RVec3 position2 = transform2 * mLocalSpacePosition2; + + // Calculate the limits in world space + RVec3 limits_min = position1 + mLimitsMin * slider_axis; + RVec3 limits_max = position1 + mLimitsMax * slider_axis; + + inRenderer->DrawLine(limits_min, position1, Color::sWhite); + inRenderer->DrawLine(position2, limits_max, Color::sWhite); + + inRenderer->DrawMarker(limits_min, Color::sWhite, 0.1f); + inRenderer->DrawMarker(limits_max, Color::sWhite, 0.1f); + } +} +#endif // JPH_DEBUG_RENDERER + +void SliderConstraint::SaveState(StateRecorder &inStream) const +{ + TwoBodyConstraint::SaveState(inStream); + + mMotorConstraintPart.SaveState(inStream); + mPositionConstraintPart.SaveState(inStream); + mRotationConstraintPart.SaveState(inStream); + mPositionLimitsConstraintPart.SaveState(inStream); + + inStream.Write(mMotorState); + inStream.Write(mTargetVelocity); + inStream.Write(mTargetPosition); +} + +void SliderConstraint::RestoreState(StateRecorder &inStream) +{ + TwoBodyConstraint::RestoreState(inStream); + + mMotorConstraintPart.RestoreState(inStream); + mPositionConstraintPart.RestoreState(inStream); + mRotationConstraintPart.RestoreState(inStream); + mPositionLimitsConstraintPart.RestoreState(inStream); + + inStream.Read(mMotorState); + inStream.Read(mTargetVelocity); + inStream.Read(mTargetPosition); +} + +Ref SliderConstraint::GetConstraintSettings() const +{ + SliderConstraintSettings *settings = new SliderConstraintSettings; + ToConstraintSettings(*settings); + settings->mSpace = EConstraintSpace::LocalToBodyCOM; + settings->mPoint1 = RVec3(mLocalSpacePosition1); + settings->mSliderAxis1 = mLocalSpaceSliderAxis1; + settings->mNormalAxis1 = mLocalSpaceNormal1; + settings->mPoint2 = RVec3(mLocalSpacePosition2); + Mat44 inv_initial_rotation = Mat44::sRotation(mInvInitialOrientation); + settings->mSliderAxis2 = inv_initial_rotation.Multiply3x3(mLocalSpaceSliderAxis1); + settings->mNormalAxis2 = inv_initial_rotation.Multiply3x3(mLocalSpaceNormal1); + settings->mLimitsMin = mLimitsMin; + settings->mLimitsMax = mLimitsMax; + settings->mLimitsSpringSettings = mLimitsSpringSettings; + settings->mMaxFrictionForce = mMaxFrictionForce; + settings->mMotorSettings = mMotorSettings; + return settings; +} + +Mat44 SliderConstraint::GetConstraintToBody1Matrix() const +{ + return Mat44(Vec4(mLocalSpaceSliderAxis1, 0), Vec4(mLocalSpaceNormal1, 0), Vec4(mLocalSpaceNormal2, 0), Vec4(mLocalSpacePosition1, 1)); +} + +Mat44 SliderConstraint::GetConstraintToBody2Matrix() const +{ + Mat44 mat = Mat44::sRotation(mInvInitialOrientation).Multiply3x3(Mat44(Vec4(mLocalSpaceSliderAxis1, 0), Vec4(mLocalSpaceNormal1, 0), Vec4(mLocalSpaceNormal2, 0), Vec4(0, 0, 0, 1))); + mat.SetTranslation(mLocalSpacePosition2); + return mat; +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/SliderConstraint.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/SliderConstraint.h new file mode 100644 index 00000000000..2838517f256 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/SliderConstraint.h @@ -0,0 +1,198 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +/// Slider constraint settings, used to create a slider constraint +class JPH_EXPORT SliderConstraintSettings final : public TwoBodyConstraintSettings +{ +public: + JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, SliderConstraintSettings) + + // See: ConstraintSettings::SaveBinaryState + virtual void SaveBinaryState(StreamOut &inStream) const override; + + /// Create an instance of this constraint. + /// Note that the rotation constraint will be solved from body 1. This means that if body 1 and body 2 have different masses / inertias (kinematic body = infinite mass / inertia), body 1 should be the heaviest body. + virtual TwoBodyConstraint * Create(Body &inBody1, Body &inBody2) const override; + + /// Simple way of setting the slider and normal axis in world space (assumes the bodies are already oriented correctly when the constraint is created) + void SetSliderAxis(Vec3Arg inSliderAxis); + + /// This determines in which space the constraint is setup, all properties below should be in the specified space + EConstraintSpace mSpace = EConstraintSpace::WorldSpace; + + /// When mSpace is WorldSpace mPoint1 and mPoint2 can be automatically calculated based on the positions of the bodies when the constraint is created (the current relative position/orientation is chosen as the '0' position). Set this to false if you want to supply the attachment points yourself. + bool mAutoDetectPoint = false; + + /// Body 1 constraint reference frame (space determined by mSpace). + /// Slider axis is the axis along which movement is possible (direction), normal axis is a perpendicular vector to define the frame. + RVec3 mPoint1 = RVec3::sZero(); + Vec3 mSliderAxis1 = Vec3::sAxisX(); + Vec3 mNormalAxis1 = Vec3::sAxisY(); + + /// Body 2 constraint reference frame (space determined by mSpace) + RVec3 mPoint2 = RVec3::sZero(); + Vec3 mSliderAxis2 = Vec3::sAxisX(); + Vec3 mNormalAxis2 = Vec3::sAxisY(); + + /// When the bodies move so that mPoint1 coincides with mPoint2 the slider position is defined to be 0, movement will be limited between [mLimitsMin, mLimitsMax] where mLimitsMin e [-inf, 0] and mLimitsMax e [0, inf] + float mLimitsMin = -FLT_MAX; + float mLimitsMax = FLT_MAX; + + /// When enabled, this makes the limits soft. When the constraint exceeds the limits, a spring force will pull it back. + SpringSettings mLimitsSpringSettings; + + /// Maximum amount of friction force to apply (N) when not driven by a motor. + float mMaxFrictionForce = 0.0f; + + /// In case the constraint is powered, this determines the motor settings around the sliding axis + MotorSettings mMotorSettings; + +protected: + // See: ConstraintSettings::RestoreBinaryState + virtual void RestoreBinaryState(StreamIn &inStream) override; +}; + +/// A slider constraint allows movement in only 1 axis (and no rotation). Also known as a prismatic constraint. +class JPH_EXPORT SliderConstraint final : public TwoBodyConstraint +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Construct slider constraint + SliderConstraint(Body &inBody1, Body &inBody2, const SliderConstraintSettings &inSettings); + + // Generic interface of a constraint + virtual EConstraintSubType GetSubType() const override { return EConstraintSubType::Slider; } + virtual void NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM) override; + virtual void SetupVelocityConstraint(float inDeltaTime) override; + virtual void ResetWarmStart() override; + virtual void WarmStartVelocityConstraint(float inWarmStartImpulseRatio) override; + virtual bool SolveVelocityConstraint(float inDeltaTime) override; + virtual bool SolvePositionConstraint(float inDeltaTime, float inBaumgarte) override; +#ifdef JPH_DEBUG_RENDERER + virtual void DrawConstraint(DebugRenderer *inRenderer) const override; + virtual void DrawConstraintLimits(DebugRenderer *inRenderer) const override; +#endif // JPH_DEBUG_RENDERER + virtual void SaveState(StateRecorder &inStream) const override; + virtual void RestoreState(StateRecorder &inStream) override; + virtual Ref GetConstraintSettings() const override; + + // See: TwoBodyConstraint + virtual Mat44 GetConstraintToBody1Matrix() const override; + virtual Mat44 GetConstraintToBody2Matrix() const override; + + /// Get the current distance from the rest position + float GetCurrentPosition() const; + + /// Friction control + void SetMaxFrictionForce(float inFrictionForce) { mMaxFrictionForce = inFrictionForce; } + float GetMaxFrictionForce() const { return mMaxFrictionForce; } + + /// Motor settings + MotorSettings & GetMotorSettings() { return mMotorSettings; } + const MotorSettings & GetMotorSettings() const { return mMotorSettings; } + + // Motor controls + void SetMotorState(EMotorState inState) { JPH_ASSERT(inState == EMotorState::Off || mMotorSettings.IsValid()); mMotorState = inState; } + EMotorState GetMotorState() const { return mMotorState; } + void SetTargetVelocity(float inVelocity) { mTargetVelocity = inVelocity; } + float GetTargetVelocity() const { return mTargetVelocity; } + void SetTargetPosition(float inPosition) { mTargetPosition = mHasLimits? Clamp(inPosition, mLimitsMin, mLimitsMax) : inPosition; } + float GetTargetPosition() const { return mTargetPosition; } + + /// Update the limits of the slider constraint (see SliderConstraintSettings) + void SetLimits(float inLimitsMin, float inLimitsMax); + float GetLimitsMin() const { return mLimitsMin; } + float GetLimitsMax() const { return mLimitsMax; } + bool HasLimits() const { return mHasLimits; } + + /// Update the limits spring settings + const SpringSettings & GetLimitsSpringSettings() const { return mLimitsSpringSettings; } + SpringSettings & GetLimitsSpringSettings() { return mLimitsSpringSettings; } + void SetLimitsSpringSettings(const SpringSettings &inLimitsSpringSettings) { mLimitsSpringSettings = inLimitsSpringSettings; } + + ///@name Get Lagrange multiplier from last physics update (the linear/angular impulse applied to satisfy the constraint) + inline Vector<2> GetTotalLambdaPosition() const { return mPositionConstraintPart.GetTotalLambda(); } + inline float GetTotalLambdaPositionLimits() const { return mPositionLimitsConstraintPart.GetTotalLambda(); } + inline Vec3 GetTotalLambdaRotation() const { return mRotationConstraintPart.GetTotalLambda(); } + inline float GetTotalLambdaMotor() const { return mMotorConstraintPart.GetTotalLambda(); } + +private: + // Internal helper function to calculate the values below + void CalculateR1R2U(Mat44Arg inRotation1, Mat44Arg inRotation2); + void CalculateSlidingAxisAndPosition(Mat44Arg inRotation1); + void CalculatePositionConstraintProperties(Mat44Arg inRotation1, Mat44Arg inRotation2); + void CalculatePositionLimitsConstraintProperties(float inDeltaTime); + void CalculateMotorConstraintProperties(float inDeltaTime); + + // CONFIGURATION PROPERTIES FOLLOW + + // Local space constraint positions + Vec3 mLocalSpacePosition1; + Vec3 mLocalSpacePosition2; + + // Local space sliding direction + Vec3 mLocalSpaceSliderAxis1; + + // Local space normals to the sliding direction (in body 1 space) + Vec3 mLocalSpaceNormal1; + Vec3 mLocalSpaceNormal2; + + // Inverse of initial rotation from body 1 to body 2 in body 1 space + Quat mInvInitialOrientation; + + // Slider limits + bool mHasLimits; + float mLimitsMin; + float mLimitsMax; + + // Soft constraint limits + SpringSettings mLimitsSpringSettings; + + // Friction + float mMaxFrictionForce; + + // Motor controls + MotorSettings mMotorSettings; + EMotorState mMotorState = EMotorState::Off; + float mTargetVelocity = 0.0f; + float mTargetPosition = 0.0f; + + // RUN TIME PROPERTIES FOLLOW + + // Positions where the point constraint acts on (middle point between center of masses) + Vec3 mR1; + Vec3 mR2; + + // X2 + R2 - X1 - R1 + Vec3 mU; + + // World space sliding direction + Vec3 mWorldSpaceSliderAxis; + + // Normals to the slider axis + Vec3 mN1; + Vec3 mN2; + + // Distance along the slide axis + float mD = 0.0f; + + // The constraint parts + DualAxisConstraintPart mPositionConstraintPart; + RotationEulerConstraintPart mRotationConstraintPart; + AxisConstraintPart mPositionLimitsConstraintPart; + AxisConstraintPart mMotorConstraintPart; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/SpringSettings.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/SpringSettings.cpp new file mode 100644 index 00000000000..c2c32400fed --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/SpringSettings.cpp @@ -0,0 +1,35 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2023 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SpringSettings) +{ + JPH_ADD_ENUM_ATTRIBUTE(SpringSettings, mMode) + JPH_ADD_ATTRIBUTE(SpringSettings, mFrequency) + JPH_ADD_ATTRIBUTE(SpringSettings, mDamping) +} + +void SpringSettings::SaveBinaryState(StreamOut &inStream) const +{ + inStream.Write(mMode); + inStream.Write(mFrequency); + inStream.Write(mDamping); +} + +void SpringSettings::RestoreBinaryState(StreamIn &inStream) +{ + inStream.Read(mMode); + inStream.Read(mFrequency); + inStream.Read(mDamping); +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/SpringSettings.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/SpringSettings.h new file mode 100644 index 00000000000..b2f6b7e2430 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/SpringSettings.h @@ -0,0 +1,70 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2023 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +class StreamIn; +class StreamOut; + +/// Enum used by constraints to specify how the spring is defined +enum class ESpringMode : uint8 +{ + FrequencyAndDamping, ///< Frequency and damping are specified + StiffnessAndDamping, ///< Stiffness and damping are specified +}; + +/// Settings for a linear or angular spring +class JPH_EXPORT SpringSettings +{ +public: + JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, SpringSettings) + + /// Constructor + SpringSettings() = default; + SpringSettings(const SpringSettings &) = default; + SpringSettings & operator = (const SpringSettings &) = default; + SpringSettings(ESpringMode inMode, float inFrequencyOrStiffness, float inDamping) : mMode(inMode), mFrequency(inFrequencyOrStiffness), mDamping(inDamping) { } + + /// Saves the contents of the spring settings in binary form to inStream. + void SaveBinaryState(StreamOut &inStream) const; + + /// Restores contents from the binary stream inStream. + void RestoreBinaryState(StreamIn &inStream); + + /// Check if the spring has a valid frequency / stiffness, if not the spring will be hard + inline bool HasStiffness() const { return mFrequency > 0.0f; } + + /// Selects the way in which the spring is defined + /// If the mode is StiffnessAndDamping then mFrequency becomes the stiffness (k) and mDamping becomes the damping ratio (c) in the spring equation F = -k * x - c * v. Otherwise the properties are as documented. + ESpringMode mMode = ESpringMode::FrequencyAndDamping; + + union + { + /// Valid when mSpringMode = ESpringMode::FrequencyAndDamping. + /// If mFrequency > 0 the constraint will be soft and mFrequency specifies the oscillation frequency in Hz. + /// If mFrequency <= 0, mDamping is ignored and the constraint will have hard limits (as hard as the time step / the number of velocity / position solver steps allows). + float mFrequency = 0.0f; + + /// Valid when mSpringMode = ESpringMode::StiffnessAndDamping. + /// If mStiffness > 0 the constraint will be soft and mStiffness specifies the stiffness (k) in the spring equation F = -k * x - c * v for a linear or T = -k * theta - c * w for an angular spring. + /// If mStiffness <= 0, mDamping is ignored and the constraint will have hard limits (as hard as the time step / the number of velocity / position solver steps allows). + /// + /// Note that stiffness values are large numbers. To calculate a ballpark value for the needed stiffness you can use: + /// force = stiffness * delta_spring_length = mass * gravity <=> stiffness = mass * gravity / delta_spring_length. + /// So if your object weighs 1500 kg and the spring compresses by 2 meters, you need a stiffness in the order of 1500 * 9.81 / 2 ~ 7500 N/m. + float mStiffness; + }; + + /// When mSpringMode = ESpringMode::FrequencyAndDamping mDamping is the damping ratio (0 = no damping, 1 = critical damping). + /// When mSpringMode = ESpringMode::StiffnessAndDamping mDamping is the damping (c) in the spring equation F = -k * x - c * v for a linear or T = -k * theta - c * w for an angular spring. + /// Note that if you set mDamping = 0, you will not get an infinite oscillation. Because we integrate physics using an explicit Euler scheme, there is always energy loss. + /// This is done to keep the simulation from exploding, because with a damping of 0 and even the slightest rounding error, the oscillation could become bigger and bigger until the simulation explodes. + float mDamping = 0.0f; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/SwingTwistConstraint.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/SwingTwistConstraint.cpp new file mode 100644 index 00000000000..bcd74ff305a --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/SwingTwistConstraint.cpp @@ -0,0 +1,524 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#include +#include +#ifdef JPH_DEBUG_RENDERER + #include +#endif // JPH_DEBUG_RENDERER + +JPH_NAMESPACE_BEGIN + +JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(SwingTwistConstraintSettings) +{ + JPH_ADD_BASE_CLASS(SwingTwistConstraintSettings, TwoBodyConstraintSettings) + + JPH_ADD_ENUM_ATTRIBUTE(SwingTwistConstraintSettings, mSpace) + JPH_ADD_ATTRIBUTE(SwingTwistConstraintSettings, mPosition1) + JPH_ADD_ATTRIBUTE(SwingTwistConstraintSettings, mTwistAxis1) + JPH_ADD_ATTRIBUTE(SwingTwistConstraintSettings, mPlaneAxis1) + JPH_ADD_ATTRIBUTE(SwingTwistConstraintSettings, mPosition2) + JPH_ADD_ATTRIBUTE(SwingTwistConstraintSettings, mTwistAxis2) + JPH_ADD_ATTRIBUTE(SwingTwistConstraintSettings, mPlaneAxis2) + JPH_ADD_ENUM_ATTRIBUTE(SwingTwistConstraintSettings, mSwingType) + JPH_ADD_ATTRIBUTE(SwingTwistConstraintSettings, mNormalHalfConeAngle) + JPH_ADD_ATTRIBUTE(SwingTwistConstraintSettings, mPlaneHalfConeAngle) + JPH_ADD_ATTRIBUTE(SwingTwistConstraintSettings, mTwistMinAngle) + JPH_ADD_ATTRIBUTE(SwingTwistConstraintSettings, mTwistMaxAngle) + JPH_ADD_ATTRIBUTE(SwingTwistConstraintSettings, mMaxFrictionTorque) + JPH_ADD_ATTRIBUTE(SwingTwistConstraintSettings, mSwingMotorSettings) + JPH_ADD_ATTRIBUTE(SwingTwistConstraintSettings, mTwistMotorSettings) +} + +void SwingTwistConstraintSettings::SaveBinaryState(StreamOut &inStream) const +{ + ConstraintSettings::SaveBinaryState(inStream); + + inStream.Write(mSpace); + inStream.Write(mPosition1); + inStream.Write(mTwistAxis1); + inStream.Write(mPlaneAxis1); + inStream.Write(mPosition2); + inStream.Write(mTwistAxis2); + inStream.Write(mPlaneAxis2); + inStream.Write(mSwingType); + inStream.Write(mNormalHalfConeAngle); + inStream.Write(mPlaneHalfConeAngle); + inStream.Write(mTwistMinAngle); + inStream.Write(mTwistMaxAngle); + inStream.Write(mMaxFrictionTorque); + mSwingMotorSettings.SaveBinaryState(inStream); + mTwistMotorSettings.SaveBinaryState(inStream); +} + +void SwingTwistConstraintSettings::RestoreBinaryState(StreamIn &inStream) +{ + ConstraintSettings::RestoreBinaryState(inStream); + + inStream.Read(mSpace); + inStream.Read(mPosition1); + inStream.Read(mTwistAxis1); + inStream.Read(mPlaneAxis1); + inStream.Read(mPosition2); + inStream.Read(mTwistAxis2); + inStream.Read(mPlaneAxis2); + inStream.Read(mSwingType); + inStream.Read(mNormalHalfConeAngle); + inStream.Read(mPlaneHalfConeAngle); + inStream.Read(mTwistMinAngle); + inStream.Read(mTwistMaxAngle); + inStream.Read(mMaxFrictionTorque); + mSwingMotorSettings.RestoreBinaryState(inStream); + mTwistMotorSettings.RestoreBinaryState(inStream); +} + +TwoBodyConstraint *SwingTwistConstraintSettings::Create(Body &inBody1, Body &inBody2) const +{ + return new SwingTwistConstraint(inBody1, inBody2, *this); +} + +void SwingTwistConstraint::UpdateLimits() +{ + // Pass limits on to swing twist constraint part + mSwingTwistConstraintPart.SetLimits(mTwistMinAngle, mTwistMaxAngle, -mPlaneHalfConeAngle, mPlaneHalfConeAngle, -mNormalHalfConeAngle, mNormalHalfConeAngle); +} + +SwingTwistConstraint::SwingTwistConstraint(Body &inBody1, Body &inBody2, const SwingTwistConstraintSettings &inSettings) : + TwoBodyConstraint(inBody1, inBody2, inSettings), + mNormalHalfConeAngle(inSettings.mNormalHalfConeAngle), + mPlaneHalfConeAngle(inSettings.mPlaneHalfConeAngle), + mTwistMinAngle(inSettings.mTwistMinAngle), + mTwistMaxAngle(inSettings.mTwistMaxAngle), + mMaxFrictionTorque(inSettings.mMaxFrictionTorque), + mSwingMotorSettings(inSettings.mSwingMotorSettings), + mTwistMotorSettings(inSettings.mTwistMotorSettings) +{ + // Override swing type + mSwingTwistConstraintPart.SetSwingType(inSettings.mSwingType); + + // Calculate rotation needed to go from constraint space to body1 local space + Vec3 normal_axis1 = inSettings.mPlaneAxis1.Cross(inSettings.mTwistAxis1); + Mat44 c_to_b1(Vec4(inSettings.mTwistAxis1, 0), Vec4(normal_axis1, 0), Vec4(inSettings.mPlaneAxis1, 0), Vec4(0, 0, 0, 1)); + mConstraintToBody1 = c_to_b1.GetQuaternion(); + + // Calculate rotation needed to go from constraint space to body2 local space + Vec3 normal_axis2 = inSettings.mPlaneAxis2.Cross(inSettings.mTwistAxis2); + Mat44 c_to_b2(Vec4(inSettings.mTwistAxis2, 0), Vec4(normal_axis2, 0), Vec4(inSettings.mPlaneAxis2, 0), Vec4(0, 0, 0, 1)); + mConstraintToBody2 = c_to_b2.GetQuaternion(); + + if (inSettings.mSpace == EConstraintSpace::WorldSpace) + { + // If all properties were specified in world space, take them to local space now + mLocalSpacePosition1 = Vec3(inBody1.GetInverseCenterOfMassTransform() * inSettings.mPosition1); + mConstraintToBody1 = inBody1.GetRotation().Conjugated() * mConstraintToBody1; + + mLocalSpacePosition2 = Vec3(inBody2.GetInverseCenterOfMassTransform() * inSettings.mPosition2); + mConstraintToBody2 = inBody2.GetRotation().Conjugated() * mConstraintToBody2; + } + else + { + mLocalSpacePosition1 = Vec3(inSettings.mPosition1); + mLocalSpacePosition2 = Vec3(inSettings.mPosition2); + } + + UpdateLimits(); +} + +void SwingTwistConstraint::NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM) +{ + if (mBody1->GetID() == inBodyID) + mLocalSpacePosition1 -= inDeltaCOM; + else if (mBody2->GetID() == inBodyID) + mLocalSpacePosition2 -= inDeltaCOM; +} + +Quat SwingTwistConstraint::GetRotationInConstraintSpace() const +{ + // Let b1, b2 be the center of mass transform of body1 and body2 (For body1 this is mBody1->GetCenterOfMassTransform()) + // Let c1, c2 be the transform that takes a vector from constraint space to local space of body1 and body2 (For body1 this is Mat44::sRotationTranslation(mConstraintToBody1, mLocalSpacePosition1)) + // Let q be the rotation of the constraint in constraint space + // b2 takes a vector from the local space of body2 to world space + // To express this in terms of b1: b2 = b1 * c1 * q * c2^-1 + // c2^-1 goes from local body 2 space to constraint space + // q rotates the constraint + // c1 goes from constraint space to body 1 local space + // b1 goes from body 1 local space to world space + // So when the body rotations are given, q = (b1 * c1)^-1 * b2 c2 + // Or: q = (q1 * c1)^-1 * (q2 * c2) if we're only interested in rotations + Quat constraint_body1_to_world = mBody1->GetRotation() * mConstraintToBody1; + Quat constraint_body2_to_world = mBody2->GetRotation() * mConstraintToBody2; + return constraint_body1_to_world.Conjugated() * constraint_body2_to_world; +} + +void SwingTwistConstraint::SetSwingMotorState(EMotorState inState) +{ + JPH_ASSERT(inState == EMotorState::Off || mSwingMotorSettings.IsValid()); + + if (mSwingMotorState != inState) + { + mSwingMotorState = inState; + + // Ensure that warm starting next frame doesn't apply any impulses (motor parts are repurposed for different modes) + for (AngleConstraintPart &c : mMotorConstraintPart) + c.Deactivate(); + } +} + +void SwingTwistConstraint::SetTwistMotorState(EMotorState inState) +{ + JPH_ASSERT(inState == EMotorState::Off || mTwistMotorSettings.IsValid()); + + if (mTwistMotorState != inState) + { + mTwistMotorState = inState; + + // Ensure that warm starting next frame doesn't apply any impulses (motor parts are repurposed for different modes) + mMotorConstraintPart[0].Deactivate(); + } +} + +void SwingTwistConstraint::SetTargetOrientationCS(QuatArg inOrientation) +{ + Quat q_swing, q_twist; + inOrientation.GetSwingTwist(q_swing, q_twist); + + uint clamped_axis; + mSwingTwistConstraintPart.ClampSwingTwist(q_swing, q_twist, clamped_axis); + + if (clamped_axis != 0) + mTargetOrientation = q_swing * q_twist; + else + mTargetOrientation = inOrientation; +} + +void SwingTwistConstraint::SetupVelocityConstraint(float inDeltaTime) +{ + // Setup point constraint + Mat44 rotation1 = Mat44::sRotation(mBody1->GetRotation()); + Mat44 rotation2 = Mat44::sRotation(mBody2->GetRotation()); + mPointConstraintPart.CalculateConstraintProperties(*mBody1, rotation1, mLocalSpacePosition1, *mBody2, rotation2, mLocalSpacePosition2); + + // GetRotationInConstraintSpace written out since we reuse the sub expressions + Quat constraint_body1_to_world = mBody1->GetRotation() * mConstraintToBody1; + Quat constraint_body2_to_world = mBody2->GetRotation() * mConstraintToBody2; + Quat q = constraint_body1_to_world.Conjugated() * constraint_body2_to_world; + + // Calculate constraint properties for the swing twist limit + mSwingTwistConstraintPart.CalculateConstraintProperties(*mBody1, *mBody2, q, constraint_body1_to_world); + + if (mSwingMotorState != EMotorState::Off || mTwistMotorState != EMotorState::Off || mMaxFrictionTorque > 0.0f) + { + // Calculate rotation motor axis + Mat44 ws_axis = Mat44::sRotation(constraint_body2_to_world); + for (int i = 0; i < 3; ++i) + mWorldSpaceMotorAxis[i] = ws_axis.GetColumn3(i); + + Vec3 rotation_error; + if (mSwingMotorState == EMotorState::Position || mTwistMotorState == EMotorState::Position) + { + // Get target orientation along the shortest path from q + Quat target_orientation = q.Dot(mTargetOrientation) > 0.0f? mTargetOrientation : -mTargetOrientation; + + // The definition of the constraint rotation q: + // R2 * ConstraintToBody2 = R1 * ConstraintToBody1 * q (1) + // + // R2' is the rotation of body 2 when reaching the target_orientation: + // R2' * ConstraintToBody2 = R1 * ConstraintToBody1 * target_orientation (2) + // + // The difference in body 2 space: + // R2' = R2 * diff_body2 (3) + // + // We want to specify the difference in the constraint space of body 2: + // diff_body2 = ConstraintToBody2 * diff * ConstraintToBody2^* (4) + // + // Extracting R2' from 2: R2' = R1 * ConstraintToBody1 * target_orientation * ConstraintToBody2^* (5) + // Combining 3 & 4: R2' = R2 * ConstraintToBody2 * diff * ConstraintToBody2^* (6) + // Combining 1 & 6: R2' = R1 * ConstraintToBody1 * q * diff * ConstraintToBody2^* (7) + // Combining 5 & 7: R1 * ConstraintToBody1 * target_orientation * ConstraintToBody2^* = R1 * ConstraintToBody1 * q * diff * ConstraintToBody2^* + // <=> target_orientation = q * diff + // <=> diff = q^* * target_orientation + Quat diff = q.Conjugated() * target_orientation; + + // Approximate error angles + // The imaginary part of a quaternion is rotation_axis * sin(angle / 2) + // If angle is small, sin(x) = x so angle[i] ~ 2.0f * rotation_axis[i] + // We'll be making small time steps, so if the angle is not small at least the sign will be correct and we'll move in the right direction + rotation_error = -2.0f * diff.GetXYZ(); + } + + // Swing motor + switch (mSwingMotorState) + { + case EMotorState::Off: + if (mMaxFrictionTorque > 0.0f) + { + // Enable friction + for (int i = 1; i < 3; ++i) + mMotorConstraintPart[i].CalculateConstraintProperties(*mBody1, *mBody2, mWorldSpaceMotorAxis[i], 0.0f); + } + else + { + // Disable friction + for (AngleConstraintPart &c : mMotorConstraintPart) + c.Deactivate(); + } + break; + + case EMotorState::Velocity: + // Use motor to create angular velocity around desired axis + for (int i = 1; i < 3; ++i) + mMotorConstraintPart[i].CalculateConstraintProperties(*mBody1, *mBody2, mWorldSpaceMotorAxis[i], -mTargetAngularVelocity[i]); + break; + + case EMotorState::Position: + // Use motor to drive rotation error to zero + if (mSwingMotorSettings.mSpringSettings.HasStiffness()) + { + for (int i = 1; i < 3; ++i) + mMotorConstraintPart[i].CalculateConstraintPropertiesWithSettings(inDeltaTime, *mBody1, *mBody2, mWorldSpaceMotorAxis[i], 0.0f, rotation_error[i], mSwingMotorSettings.mSpringSettings); + } + else + { + for (int i = 1; i < 3; ++i) + mMotorConstraintPart[i].Deactivate(); + } + break; + } + + // Twist motor + switch (mTwistMotorState) + { + case EMotorState::Off: + if (mMaxFrictionTorque > 0.0f) + { + // Enable friction + mMotorConstraintPart[0].CalculateConstraintProperties(*mBody1, *mBody2, mWorldSpaceMotorAxis[0], 0.0f); + } + else + { + // Disable friction + mMotorConstraintPart[0].Deactivate(); + } + break; + + case EMotorState::Velocity: + // Use motor to create angular velocity around desired axis + mMotorConstraintPart[0].CalculateConstraintProperties(*mBody1, *mBody2, mWorldSpaceMotorAxis[0], -mTargetAngularVelocity[0]); + break; + + case EMotorState::Position: + // Use motor to drive rotation error to zero + if (mTwistMotorSettings.mSpringSettings.HasStiffness()) + mMotorConstraintPart[0].CalculateConstraintPropertiesWithSettings(inDeltaTime, *mBody1, *mBody2, mWorldSpaceMotorAxis[0], 0.0f, rotation_error[0], mTwistMotorSettings.mSpringSettings); + else + mMotorConstraintPart[0].Deactivate(); + break; + } + } + else + { + // Disable rotation motor + for (AngleConstraintPart &c : mMotorConstraintPart) + c.Deactivate(); + } +} + +void SwingTwistConstraint::ResetWarmStart() +{ + for (AngleConstraintPart &c : mMotorConstraintPart) + c.Deactivate(); + mSwingTwistConstraintPart.Deactivate(); + mPointConstraintPart.Deactivate(); +} + +void SwingTwistConstraint::WarmStartVelocityConstraint(float inWarmStartImpulseRatio) +{ + // Warm starting: Apply previous frame impulse + for (AngleConstraintPart &c : mMotorConstraintPart) + c.WarmStart(*mBody1, *mBody2, inWarmStartImpulseRatio); + mSwingTwistConstraintPart.WarmStart(*mBody1, *mBody2, inWarmStartImpulseRatio); + mPointConstraintPart.WarmStart(*mBody1, *mBody2, inWarmStartImpulseRatio); +} + +bool SwingTwistConstraint::SolveVelocityConstraint(float inDeltaTime) +{ + bool impulse = false; + + // Solve twist rotation motor + if (mMotorConstraintPart[0].IsActive()) + { + // Twist limits + float min_twist_limit, max_twist_limit; + if (mTwistMotorState == EMotorState::Off) + { + max_twist_limit = inDeltaTime * mMaxFrictionTorque; + min_twist_limit = -max_twist_limit; + } + else + { + min_twist_limit = inDeltaTime * mTwistMotorSettings.mMinTorqueLimit; + max_twist_limit = inDeltaTime * mTwistMotorSettings.mMaxTorqueLimit; + } + + impulse |= mMotorConstraintPart[0].SolveVelocityConstraint(*mBody1, *mBody2, mWorldSpaceMotorAxis[0], min_twist_limit, max_twist_limit); + } + + // Solve swing rotation motor + if (mMotorConstraintPart[1].IsActive()) + { + // Swing parts should turn on / off together + JPH_ASSERT(mMotorConstraintPart[2].IsActive()); + + // Swing limits + float min_swing_limit, max_swing_limit; + if (mSwingMotorState == EMotorState::Off) + { + max_swing_limit = inDeltaTime * mMaxFrictionTorque; + min_swing_limit = -max_swing_limit; + } + else + { + min_swing_limit = inDeltaTime * mSwingMotorSettings.mMinTorqueLimit; + max_swing_limit = inDeltaTime * mSwingMotorSettings.mMaxTorqueLimit; + } + + for (int i = 1; i < 3; ++i) + impulse |= mMotorConstraintPart[i].SolveVelocityConstraint(*mBody1, *mBody2, mWorldSpaceMotorAxis[i], min_swing_limit, max_swing_limit); + } + else + { + // Swing parts should turn on / off together + JPH_ASSERT(!mMotorConstraintPart[2].IsActive()); + } + + // Solve rotation limits + impulse |= mSwingTwistConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2); + + // Solve position constraint + impulse |= mPointConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2); + + return impulse; +} + +bool SwingTwistConstraint::SolvePositionConstraint(float inDeltaTime, float inBaumgarte) +{ + bool impulse = false; + + // Solve rotation violations + Quat q = GetRotationInConstraintSpace(); + impulse |= mSwingTwistConstraintPart.SolvePositionConstraint(*mBody1, *mBody2, q, mConstraintToBody1, mConstraintToBody2, inBaumgarte); + + // Solve position violations + mPointConstraintPart.CalculateConstraintProperties(*mBody1, Mat44::sRotation(mBody1->GetRotation()), mLocalSpacePosition1, *mBody2, Mat44::sRotation(mBody2->GetRotation()), mLocalSpacePosition2); + impulse |= mPointConstraintPart.SolvePositionConstraint(*mBody1, *mBody2, inBaumgarte); + + return impulse; +} + +#ifdef JPH_DEBUG_RENDERER +void SwingTwistConstraint::DrawConstraint(DebugRenderer *inRenderer) const +{ + // Get constraint properties in world space + RMat44 transform1 = mBody1->GetCenterOfMassTransform(); + RVec3 position1 = transform1 * mLocalSpacePosition1; + Quat rotation1 = mBody1->GetRotation() * mConstraintToBody1; + Quat rotation2 = mBody2->GetRotation() * mConstraintToBody2; + + // Draw constraint orientation + inRenderer->DrawCoordinateSystem(RMat44::sRotationTranslation(rotation1, position1), mDrawConstraintSize); + + // Draw current swing and twist + Quat q = GetRotationInConstraintSpace(); + Quat q_swing, q_twist; + q.GetSwingTwist(q_swing, q_twist); + inRenderer->DrawLine(position1, position1 + mDrawConstraintSize * (rotation1 * q_twist).RotateAxisY(), Color::sWhite); + inRenderer->DrawLine(position1, position1 + mDrawConstraintSize * (rotation1 * q_swing).RotateAxisX(), Color::sWhite); + + if (mSwingMotorState == EMotorState::Velocity || mTwistMotorState == EMotorState::Velocity) + { + // Draw target angular velocity + inRenderer->DrawArrow(position1, position1 + rotation2 * mTargetAngularVelocity, Color::sRed, 0.1f); + } + if (mSwingMotorState == EMotorState::Position || mTwistMotorState == EMotorState::Position) + { + // Draw motor swing and twist + Quat swing, twist; + mTargetOrientation.GetSwingTwist(swing, twist); + inRenderer->DrawLine(position1, position1 + mDrawConstraintSize * (rotation1 * twist).RotateAxisY(), Color::sYellow); + inRenderer->DrawLine(position1, position1 + mDrawConstraintSize * (rotation1 * swing).RotateAxisX(), Color::sCyan); + } +} + +void SwingTwistConstraint::DrawConstraintLimits(DebugRenderer *inRenderer) const +{ + // Get matrix that transforms from constraint space to world space + RMat44 constraint_to_world = RMat44::sRotationTranslation(mBody1->GetRotation() * mConstraintToBody1, mBody1->GetCenterOfMassTransform() * mLocalSpacePosition1); + + // Draw limits + if (mSwingTwistConstraintPart.GetSwingType() == ESwingType::Pyramid) + inRenderer->DrawSwingPyramidLimits(constraint_to_world, -mPlaneHalfConeAngle, mPlaneHalfConeAngle, -mNormalHalfConeAngle, mNormalHalfConeAngle, mDrawConstraintSize, Color::sGreen, DebugRenderer::ECastShadow::Off); + else + inRenderer->DrawSwingConeLimits(constraint_to_world, mPlaneHalfConeAngle, mNormalHalfConeAngle, mDrawConstraintSize, Color::sGreen, DebugRenderer::ECastShadow::Off); + inRenderer->DrawPie(constraint_to_world.GetTranslation(), mDrawConstraintSize, constraint_to_world.GetAxisX(), constraint_to_world.GetAxisY(), mTwistMinAngle, mTwistMaxAngle, Color::sPurple, DebugRenderer::ECastShadow::Off); +} +#endif // JPH_DEBUG_RENDERER + +void SwingTwistConstraint::SaveState(StateRecorder &inStream) const +{ + TwoBodyConstraint::SaveState(inStream); + + mPointConstraintPart.SaveState(inStream); + mSwingTwistConstraintPart.SaveState(inStream); + for (const AngleConstraintPart &c : mMotorConstraintPart) + c.SaveState(inStream); + + inStream.Write(mSwingMotorState); + inStream.Write(mTwistMotorState); + inStream.Write(mTargetAngularVelocity); + inStream.Write(mTargetOrientation); +} + +void SwingTwistConstraint::RestoreState(StateRecorder &inStream) +{ + TwoBodyConstraint::RestoreState(inStream); + + mPointConstraintPart.RestoreState(inStream); + mSwingTwistConstraintPart.RestoreState(inStream); + for (AngleConstraintPart &c : mMotorConstraintPart) + c.RestoreState(inStream); + + inStream.Read(mSwingMotorState); + inStream.Read(mTwistMotorState); + inStream.Read(mTargetAngularVelocity); + inStream.Read(mTargetOrientation); +} + +Ref SwingTwistConstraint::GetConstraintSettings() const +{ + SwingTwistConstraintSettings *settings = new SwingTwistConstraintSettings; + ToConstraintSettings(*settings); + settings->mSpace = EConstraintSpace::LocalToBodyCOM; + settings->mPosition1 = RVec3(mLocalSpacePosition1); + settings->mTwistAxis1 = mConstraintToBody1.RotateAxisX(); + settings->mPlaneAxis1 = mConstraintToBody1.RotateAxisZ(); + settings->mPosition2 = RVec3(mLocalSpacePosition2); + settings->mTwistAxis2 = mConstraintToBody2.RotateAxisX(); + settings->mPlaneAxis2 = mConstraintToBody2.RotateAxisZ(); + settings->mSwingType = mSwingTwistConstraintPart.GetSwingType(); + settings->mNormalHalfConeAngle = mNormalHalfConeAngle; + settings->mPlaneHalfConeAngle = mPlaneHalfConeAngle; + settings->mTwistMinAngle = mTwistMinAngle; + settings->mTwistMaxAngle = mTwistMaxAngle; + settings->mMaxFrictionTorque = mMaxFrictionTorque; + settings->mSwingMotorSettings = mSwingMotorSettings; + settings->mTwistMotorSettings = mTwistMotorSettings; + return settings; +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/SwingTwistConstraint.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/SwingTwistConstraint.h new file mode 100644 index 00000000000..d915c001534 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/SwingTwistConstraint.h @@ -0,0 +1,197 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +/// Swing twist constraint settings, used to create a swing twist constraint +/// All values in this structure are copied to the swing twist constraint and the settings object is no longer needed afterwards. +/// +/// This image describes the limit settings: +/// @image html Docs/SwingTwistConstraint.png +class JPH_EXPORT SwingTwistConstraintSettings final : public TwoBodyConstraintSettings +{ +public: + JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, SwingTwistConstraintSettings) + + // See: ConstraintSettings::SaveBinaryState + virtual void SaveBinaryState(StreamOut &inStream) const override; + + /// Create an instance of this constraint + virtual TwoBodyConstraint * Create(Body &inBody1, Body &inBody2) const override; + + /// This determines in which space the constraint is setup, all properties below should be in the specified space + EConstraintSpace mSpace = EConstraintSpace::WorldSpace; + + ///@name Body 1 constraint reference frame (space determined by mSpace) + RVec3 mPosition1 = RVec3::sZero(); + Vec3 mTwistAxis1 = Vec3::sAxisX(); + Vec3 mPlaneAxis1 = Vec3::sAxisY(); + + ///@name Body 2 constraint reference frame (space determined by mSpace) + RVec3 mPosition2 = RVec3::sZero(); + Vec3 mTwistAxis2 = Vec3::sAxisX(); + Vec3 mPlaneAxis2 = Vec3::sAxisY(); + + /// The type of swing constraint that we want to use. + ESwingType mSwingType = ESwingType::Cone; + + ///@name Swing rotation limits + float mNormalHalfConeAngle = 0.0f; ///< See image at Detailed Description. Angle in radians. + float mPlaneHalfConeAngle = 0.0f; ///< See image at Detailed Description. Angle in radians. + + ///@name Twist rotation limits + float mTwistMinAngle = 0.0f; ///< See image at Detailed Description. Angle in radians. Should be \f$\in [-\pi, \pi]\f$. + float mTwistMaxAngle = 0.0f; ///< See image at Detailed Description. Angle in radians. Should be \f$\in [-\pi, \pi]\f$. + + ///@name Friction + float mMaxFrictionTorque = 0.0f; ///< Maximum amount of torque (N m) to apply as friction when the constraint is not powered by a motor + + ///@name In case the constraint is powered, this determines the motor settings around the swing and twist axis + MotorSettings mSwingMotorSettings; + MotorSettings mTwistMotorSettings; + +protected: + // See: ConstraintSettings::RestoreBinaryState + virtual void RestoreBinaryState(StreamIn &inStream) override; +}; + +/// A swing twist constraint is a specialized constraint for humanoid ragdolls that allows limited rotation only +/// +/// @see SwingTwistConstraintSettings for a description of the limits +class JPH_EXPORT SwingTwistConstraint final : public TwoBodyConstraint +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Construct swing twist constraint + SwingTwistConstraint(Body &inBody1, Body &inBody2, const SwingTwistConstraintSettings &inSettings); + + ///@name Generic interface of a constraint + virtual EConstraintSubType GetSubType() const override { return EConstraintSubType::SwingTwist; } + virtual void NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM) override; + virtual void SetupVelocityConstraint(float inDeltaTime) override; + virtual void ResetWarmStart() override; + virtual void WarmStartVelocityConstraint(float inWarmStartImpulseRatio) override; + virtual bool SolveVelocityConstraint(float inDeltaTime) override; + virtual bool SolvePositionConstraint(float inDeltaTime, float inBaumgarte) override; +#ifdef JPH_DEBUG_RENDERER + virtual void DrawConstraint(DebugRenderer *inRenderer) const override; + virtual void DrawConstraintLimits(DebugRenderer *inRenderer) const override; +#endif // JPH_DEBUG_RENDERER + virtual void SaveState(StateRecorder &inStream) const override; + virtual void RestoreState(StateRecorder &inStream) override; + virtual Ref GetConstraintSettings() const override; + + // See: TwoBodyConstraint + virtual Mat44 GetConstraintToBody1Matrix() const override { return Mat44::sRotationTranslation(mConstraintToBody1, mLocalSpacePosition1); } + virtual Mat44 GetConstraintToBody2Matrix() const override { return Mat44::sRotationTranslation(mConstraintToBody2, mLocalSpacePosition2); } + + ///@name Constraint reference frame + inline Vec3 GetLocalSpacePosition1() const { return mLocalSpacePosition1; } + inline Vec3 GetLocalSpacePosition2() const { return mLocalSpacePosition2; } + inline Quat GetConstraintToBody1() const { return mConstraintToBody1; } + inline Quat GetConstraintToBody2() const { return mConstraintToBody2; } + + ///@name Constraint limits + inline float GetNormalHalfConeAngle() const { return mNormalHalfConeAngle; } + inline void SetNormalHalfConeAngle(float inAngle) { mNormalHalfConeAngle = inAngle; UpdateLimits(); } + inline float GetPlaneHalfConeAngle() const { return mPlaneHalfConeAngle; } + inline void SetPlaneHalfConeAngle(float inAngle) { mPlaneHalfConeAngle = inAngle; UpdateLimits(); } + inline float GetTwistMinAngle() const { return mTwistMinAngle; } + inline void SetTwistMinAngle(float inAngle) { mTwistMinAngle = inAngle; UpdateLimits(); } + inline float GetTwistMaxAngle() const { return mTwistMaxAngle; } + inline void SetTwistMaxAngle(float inAngle) { mTwistMaxAngle = inAngle; UpdateLimits(); } + + ///@name Motor settings + const MotorSettings & GetSwingMotorSettings() const { return mSwingMotorSettings; } + MotorSettings & GetSwingMotorSettings() { return mSwingMotorSettings; } + const MotorSettings & GetTwistMotorSettings() const { return mTwistMotorSettings; } + MotorSettings & GetTwistMotorSettings() { return mTwistMotorSettings; } + + ///@name Friction control + void SetMaxFrictionTorque(float inFrictionTorque) { mMaxFrictionTorque = inFrictionTorque; } + float GetMaxFrictionTorque() const { return mMaxFrictionTorque; } + + ///@name Motor controls + + /// Controls if the motors are on or off + void SetSwingMotorState(EMotorState inState); + EMotorState GetSwingMotorState() const { return mSwingMotorState; } + void SetTwistMotorState(EMotorState inState); + EMotorState GetTwistMotorState() const { return mTwistMotorState; } + + /// Set the target angular velocity of body 2 in constraint space of body 2 + void SetTargetAngularVelocityCS(Vec3Arg inAngularVelocity) { mTargetAngularVelocity = inAngularVelocity; } + Vec3 GetTargetAngularVelocityCS() const { return mTargetAngularVelocity; } + + /// Set the target orientation in constraint space (drives constraint to: GetRotationInConstraintSpace() == inOrientation) + void SetTargetOrientationCS(QuatArg inOrientation); + Quat GetTargetOrientationCS() const { return mTargetOrientation; } + + /// Set the target orientation in body space (R2 = R1 * inOrientation, where R1 and R2 are the world space rotations for body 1 and 2). + /// Solve: R2 * ConstraintToBody2 = R1 * ConstraintToBody1 * q (see SwingTwistConstraint::GetSwingTwist) and R2 = R1 * inOrientation for q. + void SetTargetOrientationBS(QuatArg inOrientation) { SetTargetOrientationCS(mConstraintToBody1.Conjugated() * inOrientation * mConstraintToBody2); } + + /// Get current rotation of constraint in constraint space. + /// Solve: R2 * ConstraintToBody2 = R1 * ConstraintToBody1 * q for q. + Quat GetRotationInConstraintSpace() const; + + ///@name Get Lagrange multiplier from last physics update (the linear/angular impulse applied to satisfy the constraint) + inline Vec3 GetTotalLambdaPosition() const { return mPointConstraintPart.GetTotalLambda(); } + inline float GetTotalLambdaTwist() const { return mSwingTwistConstraintPart.GetTotalTwistLambda(); } + inline float GetTotalLambdaSwingY() const { return mSwingTwistConstraintPart.GetTotalSwingYLambda(); } + inline float GetTotalLambdaSwingZ() const { return mSwingTwistConstraintPart.GetTotalSwingZLambda(); } + inline Vec3 GetTotalLambdaMotor() const { return Vec3(mMotorConstraintPart[0].GetTotalLambda(), mMotorConstraintPart[1].GetTotalLambda(), mMotorConstraintPart[2].GetTotalLambda()); } + +private: + // Update the limits in the swing twist constraint part + void UpdateLimits(); + + // CONFIGURATION PROPERTIES FOLLOW + + // Local space constraint positions + Vec3 mLocalSpacePosition1; + Vec3 mLocalSpacePosition2; + + // Transforms from constraint space to body space + Quat mConstraintToBody1; + Quat mConstraintToBody2; + + // Limits + float mNormalHalfConeAngle; + float mPlaneHalfConeAngle; + float mTwistMinAngle; + float mTwistMaxAngle; + + // Friction + float mMaxFrictionTorque; + + // Motor controls + MotorSettings mSwingMotorSettings; + MotorSettings mTwistMotorSettings; + EMotorState mSwingMotorState = EMotorState::Off; + EMotorState mTwistMotorState = EMotorState::Off; + Vec3 mTargetAngularVelocity = Vec3::sZero(); + Quat mTargetOrientation = Quat::sIdentity(); + + // RUN TIME PROPERTIES FOLLOW + + // Rotation axis for motor constraint parts + Vec3 mWorldSpaceMotorAxis[3]; + + // The constraint parts + PointConstraintPart mPointConstraintPart; + SwingTwistConstraintPart mSwingTwistConstraintPart; + AngleConstraintPart mMotorConstraintPart[3]; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/TwoBodyConstraint.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/TwoBodyConstraint.cpp new file mode 100644 index 00000000000..9ee7cc5d892 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/TwoBodyConstraint.cpp @@ -0,0 +1,56 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#include + +#ifdef JPH_DEBUG_RENDERER + #include +#endif // JPH_DEBUG_RENDERER + +JPH_NAMESPACE_BEGIN + +JPH_IMPLEMENT_SERIALIZABLE_ABSTRACT(TwoBodyConstraintSettings) +{ + JPH_ADD_BASE_CLASS(TwoBodyConstraintSettings, ConstraintSettings) +} + +void TwoBodyConstraint::BuildIslands(uint32 inConstraintIndex, IslandBuilder &ioBuilder, BodyManager &inBodyManager) +{ + // Activate bodies + BodyID body_ids[2]; + int num_bodies = 0; + if (mBody1->IsDynamic() && !mBody1->IsActive()) + body_ids[num_bodies++] = mBody1->GetID(); + if (mBody2->IsDynamic() && !mBody2->IsActive()) + body_ids[num_bodies++] = mBody2->GetID(); + if (num_bodies > 0) + inBodyManager.ActivateBodies(body_ids, num_bodies); + + // Link the bodies into the same island + ioBuilder.LinkConstraint(inConstraintIndex, mBody1->GetIndexInActiveBodiesInternal(), mBody2->GetIndexInActiveBodiesInternal()); +} + +uint TwoBodyConstraint::BuildIslandSplits(LargeIslandSplitter &ioSplitter) const +{ + return ioSplitter.AssignSplit(mBody1, mBody2); +} + +#ifdef JPH_DEBUG_RENDERER + +void TwoBodyConstraint::DrawConstraintReferenceFrame(DebugRenderer *inRenderer) const +{ + RMat44 transform1 = mBody1->GetCenterOfMassTransform() * GetConstraintToBody1Matrix(); + RMat44 transform2 = mBody2->GetCenterOfMassTransform() * GetConstraintToBody2Matrix(); + inRenderer->DrawCoordinateSystem(transform1, 1.1f * mDrawConstraintSize); + inRenderer->DrawCoordinateSystem(transform2, mDrawConstraintSize); +} + +#endif // JPH_DEBUG_RENDERER + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/TwoBodyConstraint.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/TwoBodyConstraint.h new file mode 100644 index 00000000000..028c073af8b --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/TwoBodyConstraint.h @@ -0,0 +1,65 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +JPH_NAMESPACE_BEGIN + +class TwoBodyConstraint; + +/// Base class for settings for all constraints that involve 2 bodies +class JPH_EXPORT TwoBodyConstraintSettings : public ConstraintSettings +{ +public: + JPH_DECLARE_SERIALIZABLE_ABSTRACT(JPH_EXPORT, TwoBodyConstraintSettings) + + /// Create an instance of this constraint + /// You can use Body::sFixedToWorld for inBody1 if you want to attach inBody2 to the world + virtual TwoBodyConstraint * Create(Body &inBody1, Body &inBody2) const = 0; +}; + +/// Base class for all constraints that involve 2 bodies. Body1 is usually considered the parent, Body2 the child. +class JPH_EXPORT TwoBodyConstraint : public Constraint +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Constructor + TwoBodyConstraint(Body &inBody1, Body &inBody2, const TwoBodyConstraintSettings &inSettings) : Constraint(inSettings), mBody1(&inBody1), mBody2(&inBody2) { } + + /// Get the type of a constraint + virtual EConstraintType GetType() const override { return EConstraintType::TwoBodyConstraint; } + + /// Solver interface + virtual bool IsActive() const override { return Constraint::IsActive() && (mBody1->IsActive() || mBody2->IsActive()) && (mBody2->IsDynamic() || mBody1->IsDynamic()); } +#ifdef JPH_DEBUG_RENDERER + virtual void DrawConstraintReferenceFrame(DebugRenderer *inRenderer) const override; +#endif // JPH_DEBUG_RENDERER + + /// Access to the connected bodies + Body * GetBody1() const { return mBody1; } + Body * GetBody2() const { return mBody2; } + + /// Calculates the transform that transforms from constraint space to body 1 space. The first column of the matrix is the primary constraint axis (e.g. the hinge axis / slider direction), second column the secondary etc. + virtual Mat44 GetConstraintToBody1Matrix() const = 0; + + /// Calculates the transform that transforms from constraint space to body 2 space. The first column of the matrix is the primary constraint axis (e.g. the hinge axis / slider direction), second column the secondary etc. + virtual Mat44 GetConstraintToBody2Matrix() const = 0; + + /// Link bodies that are connected by this constraint in the island builder + virtual void BuildIslands(uint32 inConstraintIndex, IslandBuilder &ioBuilder, BodyManager &inBodyManager) override; + + /// Link bodies that are connected by this constraint in the same split. Returns the split index. + virtual uint BuildIslandSplits(LargeIslandSplitter &ioSplitter) const override; + +protected: + /// The two bodies involved + Body * mBody1; + Body * mBody2; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/DeterminismLog.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/DeterminismLog.cpp new file mode 100644 index 00000000000..7985a36bf97 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/DeterminismLog.cpp @@ -0,0 +1,17 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2022 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include + +#ifdef JPH_ENABLE_DETERMINISM_LOG + +JPH_NAMESPACE_BEGIN + +DeterminismLog DeterminismLog::sLog; + +JPH_NAMESPACE_END + +#endif // JPH_ENABLE_DETERMINISM_LOG diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/DeterminismLog.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/DeterminismLog.h new file mode 100644 index 00000000000..e2930ff3cea --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/DeterminismLog.h @@ -0,0 +1,159 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2022 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +//#define JPH_ENABLE_DETERMINISM_LOG +#ifdef JPH_ENABLE_DETERMINISM_LOG + +#include +#include + +JPH_SUPPRESS_WARNINGS_STD_BEGIN +#include +#include +JPH_SUPPRESS_WARNINGS_STD_END + +JPH_NAMESPACE_BEGIN + +/// A simple class that logs the state of the simulation. The resulting text file can be used to diff between platforms and find issues in determinism. +class DeterminismLog +{ +private: + JPH_INLINE uint32 Convert(float inValue) const + { + return *(uint32 *)&inValue; + } + + JPH_INLINE uint64 Convert(double inValue) const + { + return *(uint64 *)&inValue; + } + +public: + DeterminismLog() + { + mLog.open("detlog.txt", std::ios::out | std::ios::trunc | std::ios::binary); // Binary because we don't want a difference between Unix and Windows line endings. + mLog.fill('0'); + } + + DeterminismLog & operator << (char inValue) + { + mLog << inValue; + return *this; + } + + DeterminismLog & operator << (const char *inValue) + { + mLog << std::dec << inValue; + return *this; + } + + DeterminismLog & operator << (const string &inValue) + { + mLog << std::dec << inValue; + return *this; + } + + DeterminismLog & operator << (const BodyID &inValue) + { + mLog << std::hex << std::setw(8) << inValue.GetIndexAndSequenceNumber(); + return *this; + } + + DeterminismLog & operator << (const SubShapeID &inValue) + { + mLog << std::hex << std::setw(8) << inValue.GetValue(); + return *this; + } + + DeterminismLog & operator << (float inValue) + { + mLog << std::hex << std::setw(8) << Convert(inValue); + return *this; + } + + DeterminismLog & operator << (int inValue) + { + mLog << inValue; + return *this; + } + + DeterminismLog & operator << (uint32 inValue) + { + mLog << std::hex << std::setw(8) << inValue; + return *this; + } + + DeterminismLog & operator << (uint64 inValue) + { + mLog << std::hex << std::setw(16) << inValue; + return *this; + } + + DeterminismLog & operator << (Vec3Arg inValue) + { + mLog << std::hex << std::setw(8) << Convert(inValue.GetX()) << " " << std::setw(8) << Convert(inValue.GetY()) << " " << std::setw(8) << Convert(inValue.GetZ()); + return *this; + } + + DeterminismLog & operator << (DVec3Arg inValue) + { + mLog << std::hex << std::setw(16) << Convert(inValue.GetX()) << " " << std::setw(16) << Convert(inValue.GetY()) << " " << std::setw(16) << Convert(inValue.GetZ()); + return *this; + } + + DeterminismLog & operator << (Vec4Arg inValue) + { + mLog << std::hex << std::setw(8) << Convert(inValue.GetX()) << " " << std::setw(8) << Convert(inValue.GetY()) << " " << std::setw(8) << Convert(inValue.GetZ()) << " " << std::setw(8) << Convert(inValue.GetW()); + return *this; + } + + DeterminismLog & operator << (const Float3 &inValue) + { + mLog << std::hex << std::setw(8) << Convert(inValue.x) << " " << std::setw(8) << Convert(inValue.y) << " " << std::setw(8) << Convert(inValue.z); + return *this; + } + + DeterminismLog & operator << (Mat44Arg inValue) + { + *this << inValue.GetColumn4(0) << " " << inValue.GetColumn4(1) << " " << inValue.GetColumn4(2) << " " << inValue.GetColumn4(3); + return *this; + } + + DeterminismLog & operator << (DMat44Arg inValue) + { + *this << inValue.GetColumn4(0) << " " << inValue.GetColumn4(1) << " " << inValue.GetColumn4(2) << " " << inValue.GetTranslation(); + return *this; + } + + DeterminismLog & operator << (QuatArg inValue) + { + *this << inValue.GetXYZW(); + return *this; + } + + // Singleton instance + static DeterminismLog sLog; + +private: + std::ofstream mLog; +}; + +/// Will log something to the determinism log, usage: JPH_DET_LOG("label " << value); +#define JPH_DET_LOG(...) DeterminismLog::sLog << __VA_ARGS__ << '\n' + +JPH_NAMESPACE_END + +#else + +JPH_SUPPRESS_WARNING_PUSH +JPH_SUPPRESS_WARNINGS + +/// By default we log nothing +#define JPH_DET_LOG(...) + +JPH_SUPPRESS_WARNING_POP + +#endif // JPH_ENABLE_DETERMINISM_LOG diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/EActivation.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/EActivation.h new file mode 100644 index 00000000000..08c10c20dd4 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/EActivation.h @@ -0,0 +1,16 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +JPH_NAMESPACE_BEGIN + +/// Enum used by AddBody to determine if the body needs to be initially active +enum class EActivation +{ + Activate, ///< Activate the body, making it part of the simulation + DontActivate ///< Leave activation state as it is (will not deactivate an active body) +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/EPhysicsUpdateError.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/EPhysicsUpdateError.h new file mode 100644 index 00000000000..c9edd6deda0 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/EPhysicsUpdateError.h @@ -0,0 +1,37 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2023 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +JPH_NAMESPACE_BEGIN + +/// Enum used by PhysicsSystem to report error conditions during the PhysicsSystem::Update call. This is a bit field, multiple errors can trigger in the same update. +enum class EPhysicsUpdateError : uint32 +{ + None = 0, ///< No errors + ManifoldCacheFull = 1 << 0, ///< The manifold cache is full, this means that the total number of contacts between bodies is too high. Some contacts were ignored. Increase inMaxContactConstraints in PhysicsSystem::Init. + BodyPairCacheFull = 1 << 1, ///< The body pair cache is full, this means that too many bodies contacted. Some contacts were ignored. Increase inMaxBodyPairs in PhysicsSystem::Init. + ContactConstraintsFull = 1 << 2, ///< The contact constraints buffer is full. Some contacts were ignored. Increase inMaxContactConstraints in PhysicsSystem::Init. +}; + +/// OR operator for EPhysicsUpdateError +inline EPhysicsUpdateError operator | (EPhysicsUpdateError inA, EPhysicsUpdateError inB) +{ + return static_cast(static_cast(inA) | static_cast(inB)); +} + +/// OR operator for EPhysicsUpdateError +inline EPhysicsUpdateError operator |= (EPhysicsUpdateError &ioA, EPhysicsUpdateError inB) +{ + ioA = ioA | inB; + return ioA; +} + +/// AND operator for EPhysicsUpdateError +inline EPhysicsUpdateError operator & (EPhysicsUpdateError inA, EPhysicsUpdateError inB) +{ + return static_cast(static_cast(inA) & static_cast(inB)); +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/IslandBuilder.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/IslandBuilder.cpp new file mode 100644 index 00000000000..ed1064df996 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/IslandBuilder.cpp @@ -0,0 +1,484 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +IslandBuilder::~IslandBuilder() +{ + JPH_ASSERT(mConstraintLinks == nullptr); + JPH_ASSERT(mContactLinks == nullptr); + JPH_ASSERT(mBodyIslands == nullptr); + JPH_ASSERT(mBodyIslandEnds == nullptr); + JPH_ASSERT(mConstraintIslands == nullptr); + JPH_ASSERT(mConstraintIslandEnds == nullptr); + JPH_ASSERT(mContactIslands == nullptr); + JPH_ASSERT(mContactIslandEnds == nullptr); + JPH_ASSERT(mIslandsSorted == nullptr); + + delete [] mBodyLinks; +} + +void IslandBuilder::Init(uint32 inMaxActiveBodies) +{ + mMaxActiveBodies = inMaxActiveBodies; + + // Link each body to itself, BuildBodyIslands() will restore this so that we don't need to do this each step + JPH_ASSERT(mBodyLinks == nullptr); + mBodyLinks = new BodyLink [mMaxActiveBodies]; + for (uint32 i = 0; i < mMaxActiveBodies; ++i) + mBodyLinks[i].mLinkedTo.store(i, memory_order_relaxed); +} + +void IslandBuilder::PrepareContactConstraints(uint32 inMaxContacts, TempAllocator *inTempAllocator) +{ + JPH_PROFILE_FUNCTION(); + + // Need to call Init first + JPH_ASSERT(mBodyLinks != nullptr); + + // Check that the builder has been reset + JPH_ASSERT(mNumContacts == 0); + JPH_ASSERT(mNumIslands == 0); + + // Create contact link buffer, not initialized so each contact needs to be explicitly set + JPH_ASSERT(mContactLinks == nullptr); + mContactLinks = (uint32 *)inTempAllocator->Allocate(inMaxContacts * sizeof(uint32)); + mMaxContacts = inMaxContacts; + +#ifdef JPH_VALIDATE_ISLAND_BUILDER + // Create validation structures + JPH_ASSERT(mLinkValidation == nullptr); + mLinkValidation = (LinkValidation *)inTempAllocator->Allocate(inMaxContacts * sizeof(LinkValidation)); + mNumLinkValidation = 0; +#endif +} + +void IslandBuilder::PrepareNonContactConstraints(uint32 inNumConstraints, TempAllocator *inTempAllocator) +{ + JPH_PROFILE_FUNCTION(); + + // Need to call Init first + JPH_ASSERT(mBodyLinks != nullptr); + + // Check that the builder has been reset + JPH_ASSERT(mNumIslands == 0); + + // Store number of constraints + mNumConstraints = inNumConstraints; + + // Create constraint link buffer, not initialized so each constraint needs to be explicitly set + JPH_ASSERT(mConstraintLinks == nullptr); + mConstraintLinks = (uint32 *)inTempAllocator->Allocate(inNumConstraints * sizeof(uint32)); +} + +uint32 IslandBuilder::GetLowestBodyIndex(uint32 inActiveBodyIndex) const +{ + uint32 index = inActiveBodyIndex; + for (;;) + { + uint32 link_to = mBodyLinks[index].mLinkedTo.load(memory_order_relaxed); + if (link_to == index) + break; + index = link_to; + } + return index; +} + +void IslandBuilder::LinkBodies(uint32 inFirst, uint32 inSecond) +{ + JPH_PROFILE_FUNCTION(); + + // Both need to be active, we don't want to create an island with static objects + if (inFirst >= mMaxActiveBodies || inSecond >= mMaxActiveBodies) + return; + +#ifdef JPH_VALIDATE_ISLAND_BUILDER + // Add link to the validation list + if (mNumLinkValidation < uint32(mMaxContacts)) + mLinkValidation[mNumLinkValidation++] = { inFirst, inSecond }; + else + JPH_ASSERT(false, "Out of links"); +#endif + + // Start the algorithm with the two bodies + uint32 first_link_to = inFirst; + uint32 second_link_to = inSecond; + + for (;;) + { + // Follow the chain until we get to the body with lowest index + // If the swap compare below fails, we'll keep searching from the lowest index for the new lowest index + first_link_to = GetLowestBodyIndex(first_link_to); + second_link_to = GetLowestBodyIndex(second_link_to); + + // If the targets are the same, the bodies are already connected + if (first_link_to != second_link_to) + { + // We always link the highest to the lowest + if (first_link_to < second_link_to) + { + // Attempt to link the second to the first + // Since we found this body to be at the end of the chain it must point to itself, and if it + // doesn't it has been reparented and we need to retry the algorithm + if (!mBodyLinks[second_link_to].mLinkedTo.compare_exchange_weak(second_link_to, first_link_to, memory_order_relaxed)) + continue; + } + else + { + // Attempt to link the first to the second + // Since we found this body to be at the end of the chain it must point to itself, and if it + // doesn't it has been reparented and we need to retry the algorithm + if (!mBodyLinks[first_link_to].mLinkedTo.compare_exchange_weak(first_link_to, second_link_to, memory_order_relaxed)) + continue; + } + } + + // Linking succeeded! + // Chains of bodies can become really long, resulting in an O(N) loop to find the lowest body index + // to prevent this we attempt to update the link of the bodies that were passed in to directly point + // to the lowest index that we found. If the value became lower than our lowest link, some other + // thread must have relinked these bodies in the mean time so we won't update the value. + uint32 lowest_link_to = min(first_link_to, second_link_to); + AtomicMin(mBodyLinks[inFirst].mLinkedTo, lowest_link_to, memory_order_relaxed); + AtomicMin(mBodyLinks[inSecond].mLinkedTo, lowest_link_to, memory_order_relaxed); + break; + } +} + +void IslandBuilder::LinkConstraint(uint32 inConstraintIndex, uint32 inFirst, uint32 inSecond) +{ + LinkBodies(inFirst, inSecond); + + JPH_ASSERT(inConstraintIndex < mNumConstraints); + uint32 min_value = min(inFirst, inSecond); // Use fact that invalid index is 0xffffffff, we want the active body of two + JPH_ASSERT(min_value != Body::cInactiveIndex); // At least one of the bodies must be active + mConstraintLinks[inConstraintIndex] = min_value; +} + +void IslandBuilder::LinkContact(uint32 inContactIndex, uint32 inFirst, uint32 inSecond) +{ + JPH_ASSERT(inContactIndex < mMaxContacts); + mContactLinks[inContactIndex] = min(inFirst, inSecond); // Use fact that invalid index is 0xffffffff, we want the active body of two +} + +#ifdef JPH_VALIDATE_ISLAND_BUILDER + +void IslandBuilder::ValidateIslands(uint32 inNumActiveBodies) const +{ + JPH_PROFILE_FUNCTION(); + + // Go through all links so far + for (uint32 i = 0; i < mNumLinkValidation; ++i) + { + // If the bodies in this link ended up in different groups we have a problem + if (mBodyLinks[mLinkValidation[i].mFirst].mIslandIndex != mBodyLinks[mLinkValidation[i].mSecond].mIslandIndex) + { + Trace("Fail: %u, %u", mLinkValidation[i].mFirst, mLinkValidation[i].mSecond); + Trace("Num Active: %u", inNumActiveBodies); + + for (uint32 j = 0; j < mNumLinkValidation; ++j) + Trace("builder.Link(%u, %u);", mLinkValidation[j].mFirst, mLinkValidation[j].mSecond); + + IslandBuilder tmp; + tmp.Init(inNumActiveBodies); + for (uint32 j = 0; j < mNumLinkValidation; ++j) + { + Trace("Link %u -> %u", mLinkValidation[j].mFirst, mLinkValidation[j].mSecond); + tmp.LinkBodies(mLinkValidation[j].mFirst, mLinkValidation[j].mSecond); + for (uint32 t = 0; t < inNumActiveBodies; ++t) + Trace("%u -> %u", t, (uint32)tmp.mBodyLinks[t].mLinkedTo); + } + + JPH_ASSERT(false, "IslandBuilder validation failed"); + } + } +} + +#endif + +void IslandBuilder::BuildBodyIslands(const BodyID *inActiveBodies, uint32 inNumActiveBodies, TempAllocator *inTempAllocator) +{ + JPH_PROFILE_FUNCTION(); + + // Store the amount of active bodies + mNumActiveBodies = inNumActiveBodies; + + // Create output arrays for body ID's, don't call constructors + JPH_ASSERT(mBodyIslands == nullptr); + mBodyIslands = (BodyID *)inTempAllocator->Allocate(inNumActiveBodies * sizeof(BodyID)); + + // Create output array for start index of each island. At this point we don't know how many islands there will be, but we know it cannot be more than inNumActiveBodies. + // Note: We allocate 1 extra entry because we always increment the count of the next island. + uint32 *body_island_starts = (uint32 *)inTempAllocator->Allocate((inNumActiveBodies + 1) * sizeof(uint32)); + + // First island always starts at 0 + body_island_starts[0] = 0; + + // Calculate island index for all bodies + JPH_ASSERT(mNumIslands == 0); + for (uint32 i = 0; i < inNumActiveBodies; ++i) + { + BodyLink &link = mBodyLinks[i]; + uint32 s = link.mLinkedTo.load(memory_order_relaxed); + if (s != i) + { + // Links to another body, take island index from other body (this must have been filled in already since we're looping from low to high) + JPH_ASSERT(s < uint32(i)); + uint32 island_index = mBodyLinks[s].mIslandIndex; + link.mIslandIndex = island_index; + + // Increment the start of the next island + body_island_starts[island_index + 1]++; + } + else + { + // Does not link to other body, this is the start of a new island + link.mIslandIndex = mNumIslands; + ++mNumIslands; + + // Set the start of the next island to 1 + body_island_starts[mNumIslands] = 1; + } + } + +#ifdef JPH_VALIDATE_ISLAND_BUILDER + ValidateIslands(inNumActiveBodies); +#endif + + // Make the start array absolute (so far we only counted) + for (uint32 island = 1; island < mNumIslands; ++island) + body_island_starts[island] += body_island_starts[island - 1]; + + // Convert the to a linear list grouped by island + for (uint32 i = 0; i < inNumActiveBodies; ++i) + { + BodyLink &link = mBodyLinks[i]; + + // Copy the body to the correct location in the array and increment it + uint32 &start = body_island_starts[link.mIslandIndex]; + mBodyIslands[start] = inActiveBodies[i]; + start++; + + // Reset linked to field for the next update + link.mLinkedTo.store(i, memory_order_relaxed); + } + + // We should now have a full array + JPH_ASSERT(mNumIslands == 0 || body_island_starts[mNumIslands - 1] == inNumActiveBodies); + + // We've incremented all body indices so that they now point at the end instead of the starts + JPH_ASSERT(mBodyIslandEnds == nullptr); + mBodyIslandEnds = body_island_starts; +} + +void IslandBuilder::BuildConstraintIslands(const uint32 *inConstraintToBody, uint32 inNumConstraints, uint32 *&outConstraints, uint32 *&outConstraintsEnd, TempAllocator *inTempAllocator) const +{ + JPH_PROFILE_FUNCTION(); + + // Check if there's anything to do + if (inNumConstraints == 0) + return; + + // Create output arrays for constraints + // Note: For the end indices we allocate 1 extra entry so we don't have to do an if in the inner loop + uint32 *constraints = (uint32 *)inTempAllocator->Allocate(inNumConstraints * sizeof(uint32)); + uint32 *constraint_ends = (uint32 *)inTempAllocator->Allocate((mNumIslands + 1) * sizeof(uint32)); + + // Reset sizes + for (uint32 island = 0; island < mNumIslands; ++island) + constraint_ends[island] = 0; + + // Loop over array and increment start relative position for the next island + for (uint32 constraint = 0; constraint < inNumConstraints; ++constraint) + { + uint32 body_idx = inConstraintToBody[constraint]; + uint32 next_island_idx = mBodyLinks[body_idx].mIslandIndex + 1; + JPH_ASSERT(next_island_idx <= mNumIslands); + constraint_ends[next_island_idx]++; + } + + // Make start positions absolute + for (uint32 island = 1; island < mNumIslands; ++island) + constraint_ends[island] += constraint_ends[island - 1]; + + // Loop over array and collect constraints + for (uint32 constraint = 0; constraint < inNumConstraints; ++constraint) + { + uint32 body_idx = inConstraintToBody[constraint]; + uint32 island_idx = mBodyLinks[body_idx].mIslandIndex; + constraints[constraint_ends[island_idx]++] = constraint; + } + + JPH_ASSERT(outConstraints == nullptr); + outConstraints = constraints; + JPH_ASSERT(outConstraintsEnd == nullptr); + outConstraintsEnd = constraint_ends; +} + +void IslandBuilder::SortIslands(TempAllocator *inTempAllocator) +{ + JPH_PROFILE_FUNCTION(); + + if (mNumContacts > 0 || mNumConstraints > 0) + { + // Allocate mapping table + JPH_ASSERT(mIslandsSorted == nullptr); + mIslandsSorted = (uint32 *)inTempAllocator->Allocate(mNumIslands * sizeof(uint32)); + + // Initialize index + for (uint32 island = 0; island < mNumIslands; ++island) + mIslandsSorted[island] = island; + + // Determine the sum of contact constraints / constraints per island + uint32 *num_constraints = (uint32 *)inTempAllocator->Allocate(mNumIslands * sizeof(uint32)); + if (mNumContacts > 0 && mNumConstraints > 0) + { + num_constraints[0] = mConstraintIslandEnds[0] + mContactIslandEnds[0]; + for (uint32 island = 1; island < mNumIslands; ++island) + num_constraints[island] = mConstraintIslandEnds[island] - mConstraintIslandEnds[island - 1] + + mContactIslandEnds[island] - mContactIslandEnds[island - 1]; + } + else if (mNumContacts > 0) + { + num_constraints[0] = mContactIslandEnds[0]; + for (uint32 island = 1; island < mNumIslands; ++island) + num_constraints[island] = mContactIslandEnds[island] - mContactIslandEnds[island - 1]; + } + else + { + num_constraints[0] = mConstraintIslandEnds[0]; + for (uint32 island = 1; island < mNumIslands; ++island) + num_constraints[island] = mConstraintIslandEnds[island] - mConstraintIslandEnds[island - 1]; + } + + // Sort so the biggest islands go first, this means that the jobs that take longest will be running + // first which improves the chance that all jobs finish at the same time. + QuickSort(mIslandsSorted, mIslandsSorted + mNumIslands, [num_constraints](uint32 inLHS, uint32 inRHS) { + return num_constraints[inLHS] > num_constraints[inRHS]; + }); + + inTempAllocator->Free(num_constraints, mNumIslands * sizeof(uint32)); + } +} + +void IslandBuilder::Finalize(const BodyID *inActiveBodies, uint32 inNumActiveBodies, uint32 inNumContacts, TempAllocator *inTempAllocator) +{ + JPH_PROFILE_FUNCTION(); + + mNumContacts = inNumContacts; + + BuildBodyIslands(inActiveBodies, inNumActiveBodies, inTempAllocator); + BuildConstraintIslands(mConstraintLinks, mNumConstraints, mConstraintIslands, mConstraintIslandEnds, inTempAllocator); + BuildConstraintIslands(mContactLinks, mNumContacts, mContactIslands, mContactIslandEnds, inTempAllocator); + SortIslands(inTempAllocator); + + mNumPositionSteps = (uint8 *)inTempAllocator->Allocate(mNumIslands * sizeof(uint8)); +} + +void IslandBuilder::GetBodiesInIsland(uint32 inIslandIndex, BodyID *&outBodiesBegin, BodyID *&outBodiesEnd) const +{ + JPH_ASSERT(inIslandIndex < mNumIslands); + uint32 sorted_index = mIslandsSorted != nullptr? mIslandsSorted[inIslandIndex] : inIslandIndex; + outBodiesBegin = sorted_index > 0? mBodyIslands + mBodyIslandEnds[sorted_index - 1] : mBodyIslands; + outBodiesEnd = mBodyIslands + mBodyIslandEnds[sorted_index]; +} + +bool IslandBuilder::GetConstraintsInIsland(uint32 inIslandIndex, uint32 *&outConstraintsBegin, uint32 *&outConstraintsEnd) const +{ + JPH_ASSERT(inIslandIndex < mNumIslands); + if (mNumConstraints == 0) + { + outConstraintsBegin = nullptr; + outConstraintsEnd = nullptr; + return false; + } + else + { + uint32 sorted_index = mIslandsSorted[inIslandIndex]; + outConstraintsBegin = sorted_index > 0? mConstraintIslands + mConstraintIslandEnds[sorted_index - 1] : mConstraintIslands; + outConstraintsEnd = mConstraintIslands + mConstraintIslandEnds[sorted_index]; + return outConstraintsBegin != outConstraintsEnd; + } +} + +bool IslandBuilder::GetContactsInIsland(uint32 inIslandIndex, uint32 *&outContactsBegin, uint32 *&outContactsEnd) const +{ + JPH_ASSERT(inIslandIndex < mNumIslands); + if (mNumContacts == 0) + { + outContactsBegin = nullptr; + outContactsEnd = nullptr; + return false; + } + else + { + uint32 sorted_index = mIslandsSorted[inIslandIndex]; + outContactsBegin = sorted_index > 0? mContactIslands + mContactIslandEnds[sorted_index - 1] : mContactIslands; + outContactsEnd = mContactIslands + mContactIslandEnds[sorted_index]; + return outContactsBegin != outContactsEnd; + } +} + +void IslandBuilder::ResetIslands(TempAllocator *inTempAllocator) +{ + JPH_PROFILE_FUNCTION(); + + inTempAllocator->Free(mNumPositionSteps, mNumIslands * sizeof(uint8)); + + if (mIslandsSorted != nullptr) + { + inTempAllocator->Free(mIslandsSorted, mNumIslands * sizeof(uint32)); + mIslandsSorted = nullptr; + } + + if (mContactIslands != nullptr) + { + inTempAllocator->Free(mContactIslandEnds, (mNumIslands + 1) * sizeof(uint32)); + mContactIslandEnds = nullptr; + inTempAllocator->Free(mContactIslands, mNumContacts * sizeof(uint32)); + mContactIslands = nullptr; + } + + if (mConstraintIslands != nullptr) + { + inTempAllocator->Free(mConstraintIslandEnds, (mNumIslands + 1) * sizeof(uint32)); + mConstraintIslandEnds = nullptr; + inTempAllocator->Free(mConstraintIslands, mNumConstraints * sizeof(uint32)); + mConstraintIslands = nullptr; + } + + inTempAllocator->Free(mBodyIslandEnds, (mNumActiveBodies + 1) * sizeof(uint32)); + mBodyIslandEnds = nullptr; + inTempAllocator->Free(mBodyIslands, mNumActiveBodies * sizeof(uint32)); + mBodyIslands = nullptr; + + inTempAllocator->Free(mConstraintLinks, mNumConstraints * sizeof(uint32)); + mConstraintLinks = nullptr; + +#ifdef JPH_VALIDATE_ISLAND_BUILDER + inTempAllocator->Free(mLinkValidation, mMaxContacts * sizeof(LinkValidation)); + mLinkValidation = nullptr; +#endif + + inTempAllocator->Free(mContactLinks, mMaxContacts * sizeof(uint32)); + mContactLinks = nullptr; + + mNumActiveBodies = 0; + mNumConstraints = 0; + mMaxContacts = 0; + mNumContacts = 0; + mNumIslands = 0; +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/IslandBuilder.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/IslandBuilder.h new file mode 100644 index 00000000000..4c2f097d60c --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/IslandBuilder.h @@ -0,0 +1,125 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +class TempAllocator; + +//#define JPH_VALIDATE_ISLAND_BUILDER + +/// Keeps track of connected bodies and builds islands for multithreaded velocity/position update +class IslandBuilder : public NonCopyable +{ +public: + /// Destructor + ~IslandBuilder(); + + /// Initialize the island builder with the maximum amount of bodies that could be active + void Init(uint32 inMaxActiveBodies); + + /// Prepare for simulation step by allocating space for the contact constraints + void PrepareContactConstraints(uint32 inMaxContactConstraints, TempAllocator *inTempAllocator); + + /// Prepare for simulation step by allocating space for the non-contact constraints + void PrepareNonContactConstraints(uint32 inNumConstraints, TempAllocator *inTempAllocator); + + /// Link two bodies by their index in the BodyManager::mActiveBodies list to form islands + void LinkBodies(uint32 inFirst, uint32 inSecond); + + /// Link a constraint to a body by their index in the BodyManager::mActiveBodies + void LinkConstraint(uint32 inConstraintIndex, uint32 inFirst, uint32 inSecond); + + /// Link a contact to a body by their index in the BodyManager::mActiveBodies + void LinkContact(uint32 inContactIndex, uint32 inFirst, uint32 inSecond); + + /// Finalize the islands after all bodies have been Link()-ed + void Finalize(const BodyID *inActiveBodies, uint32 inNumActiveBodies, uint32 inNumContacts, TempAllocator *inTempAllocator); + + /// Get the amount of islands formed + uint32 GetNumIslands() const { return mNumIslands; } + + /// Get iterator for a particular island, return false if there are no constraints + void GetBodiesInIsland(uint32 inIslandIndex, BodyID *&outBodiesBegin, BodyID *&outBodiesEnd) const; + bool GetConstraintsInIsland(uint32 inIslandIndex, uint32 *&outConstraintsBegin, uint32 *&outConstraintsEnd) const; + bool GetContactsInIsland(uint32 inIslandIndex, uint32 *&outContactsBegin, uint32 *&outContactsEnd) const; + + /// The number of position iterations for each island + void SetNumPositionSteps(uint32 inIslandIndex, uint inNumPositionSteps) { JPH_ASSERT(inIslandIndex < mNumIslands); JPH_ASSERT(inNumPositionSteps < 256); mNumPositionSteps[inIslandIndex] = uint8(inNumPositionSteps); } + uint GetNumPositionSteps(uint32 inIslandIndex) const { JPH_ASSERT(inIslandIndex < mNumIslands); return mNumPositionSteps[inIslandIndex]; } + + /// After you're done calling the three functions above, call this function to free associated data + void ResetIslands(TempAllocator *inTempAllocator); + +private: + /// Returns the index of the lowest body in the group + uint32 GetLowestBodyIndex(uint32 inActiveBodyIndex) const; + +#ifdef JPH_VALIDATE_ISLAND_BUILDER + /// Helper function to validate all islands so far generated + void ValidateIslands(uint32 inNumActiveBodies) const; +#endif + + // Helper functions to build various islands + void BuildBodyIslands(const BodyID *inActiveBodies, uint32 inNumActiveBodies, TempAllocator *inTempAllocator); + void BuildConstraintIslands(const uint32 *inConstraintToBody, uint32 inNumConstraints, uint32 *&outConstraints, uint32 *&outConstraintsEnd, TempAllocator *inTempAllocator) const; + + /// Sorts the islands so that the islands with most constraints go first + void SortIslands(TempAllocator *inTempAllocator); + + /// Intermediate data structure that for each body keeps track what the lowest index of the body is that it is connected to + struct BodyLink + { + JPH_OVERRIDE_NEW_DELETE + + atomic mLinkedTo; ///< An index in mBodyLinks pointing to another body in this island with a lower index than this body + uint32 mIslandIndex; ///< The island index of this body (filled in during Finalize) + }; + + // Intermediate data + BodyLink * mBodyLinks = nullptr; ///< Maps bodies to the first body in the island + uint32 * mConstraintLinks = nullptr; ///< Maps constraint index to body index (which maps to island index) + uint32 * mContactLinks = nullptr; ///< Maps contact constraint index to body index (which maps to island index) + + // Final data + BodyID * mBodyIslands = nullptr; ///< Bodies ordered by island + uint32 * mBodyIslandEnds = nullptr; ///< End index of each body island + + uint32 * mConstraintIslands = nullptr; ///< Constraints ordered by island + uint32 * mConstraintIslandEnds = nullptr; ///< End index of each constraint island + + uint32 * mContactIslands = nullptr; ///< Contacts ordered by island + uint32 * mContactIslandEnds = nullptr; ///< End index of each contact island + + uint32 * mIslandsSorted = nullptr; ///< A list of island indices in order of most constraints first + + uint8 * mNumPositionSteps = nullptr; ///< Number of position steps for each island + + // Counters + uint32 mMaxActiveBodies; ///< Maximum size of the active bodies list (see BodyManager::mActiveBodies) + uint32 mNumActiveBodies = 0; ///< Number of active bodies passed to + uint32 mNumConstraints = 0; ///< Size of the constraint list (see ConstraintManager::mConstraints) + uint32 mMaxContacts = 0; ///< Maximum amount of contacts supported + uint32 mNumContacts = 0; ///< Size of the contacts list (see ContactConstraintManager::mNumConstraints) + uint32 mNumIslands = 0; ///< Final number of islands + +#ifdef JPH_VALIDATE_ISLAND_BUILDER + /// Structure to keep track of all added links to validate that islands were generated correctly + struct LinkValidation + { + uint32 mFirst; + uint32 mSecond; + }; + + LinkValidation * mLinkValidation = nullptr; + atomic mNumLinkValidation; +#endif +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/LargeIslandSplitter.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/LargeIslandSplitter.cpp new file mode 100644 index 00000000000..ebe6fbfea21 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/LargeIslandSplitter.cpp @@ -0,0 +1,579 @@ +// SPDX-FileCopyrightText: 2023 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +//#define JPH_LARGE_ISLAND_SPLITTER_DEBUG + +JPH_NAMESPACE_BEGIN + +LargeIslandSplitter::EStatus LargeIslandSplitter::Splits::FetchNextBatch(uint32 &outConstraintsBegin, uint32 &outConstraintsEnd, uint32 &outContactsBegin, uint32 &outContactsEnd, bool &outFirstIteration) +{ + { + // First check if we can get a new batch (doing a relaxed read to avoid hammering an atomic with an atomic subtract) + // Note this also avoids overflowing the status counter if we're done but there's still one thread processing items + uint64 status = mStatus.load(memory_order_relaxed); + if (sGetIteration(status) >= mNumIterations) + return EStatus::AllBatchesDone; + + // Check for special value that indicates that the splits are still being built + // (note we do not check for this condition again below as we reset all splits before kicking off jobs that fetch batches of work) + if (status == StatusItemMask) + return EStatus::WaitingForBatch; + + uint item = sGetItem(status); + uint split_index = sGetSplit(status); + if (split_index == cNonParallelSplitIdx) + { + // Non parallel split needs to be taken as a single batch, only the thread that takes element 0 will do it + if (item != 0) + return EStatus::WaitingForBatch; + } + else + { + // Parallel split is split into batches + JPH_ASSERT(split_index < mNumSplits); + const Split &split = mSplits[split_index]; + if (item >= split.GetNumItems()) + return EStatus::WaitingForBatch; + } + } + + // Then try to actually get the batch + uint64 status = mStatus.fetch_add(cBatchSize, memory_order_acquire); + int iteration = sGetIteration(status); + if (iteration >= mNumIterations) + return EStatus::AllBatchesDone; + + uint split_index = sGetSplit(status); + JPH_ASSERT(split_index < mNumSplits || split_index == cNonParallelSplitIdx); + const Split &split = mSplits[split_index]; + uint item_begin = sGetItem(status); + if (split_index == cNonParallelSplitIdx) + { + if (item_begin == 0) + { + // Non-parallel split always goes as a single batch + outConstraintsBegin = split.mConstraintBufferBegin; + outConstraintsEnd = split.mConstraintBufferEnd; + outContactsBegin = split.mContactBufferBegin; + outContactsEnd = split.mContactBufferEnd; + outFirstIteration = iteration == 0; + return EStatus::BatchRetrieved; + } + else + { + // Otherwise we're done with this split + return EStatus::WaitingForBatch; + } + } + + // Parallel split is split into batches + uint num_constraints = split.GetNumConstraints(); + uint num_contacts = split.GetNumContacts(); + uint num_items = num_constraints + num_contacts; + if (item_begin >= num_items) + return EStatus::WaitingForBatch; + + uint item_end = min(item_begin + cBatchSize, num_items); + if (item_end >= num_constraints) + { + if (item_begin < num_constraints) + { + // Partially from constraints and partially from contacts + outConstraintsBegin = split.mConstraintBufferBegin + item_begin; + outConstraintsEnd = split.mConstraintBufferEnd; + } + else + { + // Only contacts + outConstraintsBegin = 0; + outConstraintsEnd = 0; + } + + outContactsBegin = split.mContactBufferBegin + (max(item_begin, num_constraints) - num_constraints); + outContactsEnd = split.mContactBufferBegin + (item_end - num_constraints); + } + else + { + // Only constraints + outConstraintsBegin = split.mConstraintBufferBegin + item_begin; + outConstraintsEnd = split.mConstraintBufferBegin + item_end; + + outContactsBegin = 0; + outContactsEnd = 0; + } + + outFirstIteration = iteration == 0; + return EStatus::BatchRetrieved; +} + +void LargeIslandSplitter::Splits::MarkBatchProcessed(uint inNumProcessed, bool &outLastIteration, bool &outFinalBatch) +{ + // We fetched this batch, nobody should change the split and or iteration until we mark the last batch as processed so we can safely get the current status + uint64 status = mStatus.load(memory_order_relaxed); + uint split_index = sGetSplit(status); + JPH_ASSERT(split_index < mNumSplits || split_index == cNonParallelSplitIdx); + const Split &split = mSplits[split_index]; + uint num_items_in_split = split.GetNumItems(); + + // Determine if this is the last iteration before possibly incrementing it + int iteration = sGetIteration(status); + outLastIteration = iteration == mNumIterations - 1; + + // Add the number of items we processed to the total number of items processed + // Note: This needs to happen after we read the status as other threads may update the status after we mark items as processed + JPH_ASSERT(inNumProcessed > 0); // Logic will break if we mark a block of 0 items as processed + uint total_items_processed = mItemsProcessed.fetch_add(inNumProcessed, memory_order_acq_rel) + inNumProcessed; + + // Check if we're at the end of the split + if (total_items_processed >= num_items_in_split) + { + JPH_ASSERT(total_items_processed == num_items_in_split); // Should not overflow, that means we're retiring more items than we should process + + // Set items processed back to 0 for the next split/iteration + mItemsProcessed.store(0, memory_order_release); + + // Determine next split + do + { + if (split_index == cNonParallelSplitIdx) + { + // At start of next iteration + split_index = 0; + ++iteration; + } + else + { + // At start of next split + ++split_index; + } + + // If we're beyond the end of splits, go to the non-parallel split + if (split_index >= mNumSplits) + split_index = cNonParallelSplitIdx; + } + while (iteration < mNumIterations + && mSplits[split_index].GetNumItems() == 0); // We don't support processing empty splits, skip to the next split in this case + + mStatus.store((uint64(iteration) << StatusIterationShift) | (uint64(split_index) << StatusSplitShift), memory_order_release); + } + + // Track if this is the final batch + outFinalBatch = iteration >= mNumIterations; +} + +LargeIslandSplitter::~LargeIslandSplitter() +{ + JPH_ASSERT(mSplitMasks == nullptr); + JPH_ASSERT(mContactAndConstraintsSplitIdx == nullptr); + JPH_ASSERT(mContactAndConstraintIndices == nullptr); + JPH_ASSERT(mSplitIslands == nullptr); +} + +void LargeIslandSplitter::Prepare(const IslandBuilder &inIslandBuilder, uint32 inNumActiveBodies, TempAllocator *inTempAllocator) +{ + JPH_PROFILE_FUNCTION(); + + // Count the total number of constraints and contacts that we will be putting in splits + mContactAndConstraintsSize = 0; + for (uint32 island = 0; island < inIslandBuilder.GetNumIslands(); ++island) + { + // Get the contacts in this island + uint32 *contacts_start, *contacts_end; + inIslandBuilder.GetContactsInIsland(island, contacts_start, contacts_end); + uint num_contacts_in_island = uint(contacts_end - contacts_start); + + // Get the constraints in this island + uint32 *constraints_start, *constraints_end; + inIslandBuilder.GetConstraintsInIsland(island, constraints_start, constraints_end); + uint num_constraints_in_island = uint(constraints_end - constraints_start); + + uint island_size = num_contacts_in_island + num_constraints_in_island; + if (island_size >= cLargeIslandTreshold) + { + mNumSplitIslands++; + mContactAndConstraintsSize += island_size; + } + else + break; // If this island doesn't have enough constraints, the next islands won't either since they're sorted from big to small + } + + if (mContactAndConstraintsSize > 0) + { + mNumActiveBodies = inNumActiveBodies; + + // Allocate split mask buffer + mSplitMasks = (SplitMask *)inTempAllocator->Allocate(mNumActiveBodies * sizeof(SplitMask)); + + // Allocate contact and constraint buffer + uint contact_and_constraint_indices_size = mContactAndConstraintsSize * sizeof(uint32); + mContactAndConstraintsSplitIdx = (uint32 *)inTempAllocator->Allocate(contact_and_constraint_indices_size); + mContactAndConstraintIndices = (uint32 *)inTempAllocator->Allocate(contact_and_constraint_indices_size); + + // Allocate island split buffer + mSplitIslands = (Splits *)inTempAllocator->Allocate(mNumSplitIslands * sizeof(Splits)); + + // Prevent any of the splits from being picked up as work + for (uint i = 0; i < mNumSplitIslands; ++i) + mSplitIslands[i].ResetStatus(); + } +} + +uint LargeIslandSplitter::AssignSplit(const Body *inBody1, const Body *inBody2) +{ + uint32 idx1 = inBody1->GetIndexInActiveBodiesInternal(); + uint32 idx2 = inBody2->GetIndexInActiveBodiesInternal(); + + // Test if either index is negative + if (idx1 == Body::cInactiveIndex || !inBody1->IsDynamic()) + { + // Body 1 is not active or a kinematic body, so we only need to set 1 body + JPH_ASSERT(idx2 < mNumActiveBodies); + SplitMask &mask = mSplitMasks[idx2]; + uint split = min(CountTrailingZeros(~uint32(mask)), cNonParallelSplitIdx); + mask |= SplitMask(1U << split); + return split; + } + else if (idx2 == Body::cInactiveIndex || !inBody2->IsDynamic()) + { + // Body 2 is not active or a kinematic body, so we only need to set 1 body + JPH_ASSERT(idx1 < mNumActiveBodies); + SplitMask &mask = mSplitMasks[idx1]; + uint split = min(CountTrailingZeros(~uint32(mask)), cNonParallelSplitIdx); + mask |= SplitMask(1U << split); + return split; + } + else + { + // If both bodies are active, we need to set 2 bodies + JPH_ASSERT(idx1 < mNumActiveBodies); + JPH_ASSERT(idx2 < mNumActiveBodies); + SplitMask &mask1 = mSplitMasks[idx1]; + SplitMask &mask2 = mSplitMasks[idx2]; + uint split = min(CountTrailingZeros((~uint32(mask1)) & (~uint32(mask2))), cNonParallelSplitIdx); + SplitMask mask = SplitMask(1U << split); + mask1 |= mask; + mask2 |= mask; + return split; + } +} + +uint LargeIslandSplitter::AssignToNonParallelSplit(const Body *inBody) +{ + uint32 idx = inBody->GetIndexInActiveBodiesInternal(); + if (idx != Body::cInactiveIndex) + { + JPH_ASSERT(idx < mNumActiveBodies); + mSplitMasks[idx] |= 1U << cNonParallelSplitIdx; + } + + return cNonParallelSplitIdx; +} + +bool LargeIslandSplitter::SplitIsland(uint32 inIslandIndex, const IslandBuilder &inIslandBuilder, const BodyManager &inBodyManager, const ContactConstraintManager &inContactManager, Constraint **inActiveConstraints, CalculateSolverSteps &ioStepsCalculator) +{ + JPH_PROFILE_FUNCTION(); + + // Get the contacts in this island + uint32 *contacts_start, *contacts_end; + inIslandBuilder.GetContactsInIsland(inIslandIndex, contacts_start, contacts_end); + uint num_contacts_in_island = uint(contacts_end - contacts_start); + + // Get the constraints in this island + uint32 *constraints_start, *constraints_end; + inIslandBuilder.GetConstraintsInIsland(inIslandIndex, constraints_start, constraints_end); + uint num_constraints_in_island = uint(constraints_end - constraints_start); + + // Check if it exceeds the threshold + uint island_size = num_contacts_in_island + num_constraints_in_island; + if (island_size < cLargeIslandTreshold) + return false; + + // Get bodies in this island + BodyID *bodies_start, *bodies_end; + inIslandBuilder.GetBodiesInIsland(inIslandIndex, bodies_start, bodies_end); + + // Reset the split mask for all bodies in this island + Body const * const *bodies = inBodyManager.GetBodies().data(); + for (const BodyID *b = bodies_start; b < bodies_end; ++b) + mSplitMasks[bodies[b->GetIndex()]->GetIndexInActiveBodiesInternal()] = 0; + + // Count the number of contacts and constraints per split + uint num_contacts_in_split[cNumSplits] = { }; + uint num_constraints_in_split[cNumSplits] = { }; + + // Get space to store split indices + uint offset = mContactAndConstraintsNextFree.fetch_add(island_size, memory_order_relaxed); + uint32 *contact_split_idx = mContactAndConstraintsSplitIdx + offset; + uint32 *constraint_split_idx = contact_split_idx + num_contacts_in_island; + + // Assign the contacts to a split + uint32 *cur_contact_split_idx = contact_split_idx; + for (const uint32 *c = contacts_start; c < contacts_end; ++c) + { + const Body *body1, *body2; + inContactManager.GetAffectedBodies(*c, body1, body2); + uint split = AssignSplit(body1, body2); + num_contacts_in_split[split]++; + *cur_contact_split_idx++ = split; + + if (body1->IsDynamic()) + ioStepsCalculator(body1->GetMotionPropertiesUnchecked()); + if (body2->IsDynamic()) + ioStepsCalculator(body2->GetMotionPropertiesUnchecked()); + } + + // Assign the constraints to a split + uint32 *cur_constraint_split_idx = constraint_split_idx; + for (const uint32 *c = constraints_start; c < constraints_end; ++c) + { + const Constraint *constraint = inActiveConstraints[*c]; + uint split = constraint->BuildIslandSplits(*this); + num_constraints_in_split[split]++; + *cur_constraint_split_idx++ = split; + + ioStepsCalculator(constraint); + } + + ioStepsCalculator.Finalize(); + + // Start with 0 splits + uint split_remap_table[cNumSplits]; + uint new_split_idx = mNextSplitIsland.fetch_add(1, memory_order_relaxed); + JPH_ASSERT(new_split_idx < mNumSplitIslands); + Splits &splits = mSplitIslands[new_split_idx]; + splits.mIslandIndex = inIslandIndex; + splits.mNumSplits = 0; + splits.mNumIterations = ioStepsCalculator.GetNumVelocitySteps() + 1; // Iteration 0 is used for warm starting + splits.mNumVelocitySteps = ioStepsCalculator.GetNumVelocitySteps(); + splits.mNumPositionSteps = ioStepsCalculator.GetNumPositionSteps(); + splits.mItemsProcessed.store(0, memory_order_release); + + // Allocate space to store the sorted constraint and contact indices per split + uint32 *constraint_buffer_cur[cNumSplits], *contact_buffer_cur[cNumSplits]; + for (uint s = 0; s < cNumSplits; ++s) + { + // If this split doesn't contain enough constraints and contacts, we will combine it with the non parallel split + if (num_constraints_in_split[s] + num_contacts_in_split[s] < cSplitCombineTreshold + && s < cNonParallelSplitIdx) // The non-parallel split cannot merge into itself + { + // Remap it + split_remap_table[s] = cNonParallelSplitIdx; + + // Add the counts to the non parallel split + num_contacts_in_split[cNonParallelSplitIdx] += num_contacts_in_split[s]; + num_constraints_in_split[cNonParallelSplitIdx] += num_constraints_in_split[s]; + } + else + { + // This split is valid, map it to the next empty slot + uint target_split; + if (s < cNonParallelSplitIdx) + target_split = splits.mNumSplits++; + else + target_split = cNonParallelSplitIdx; + Split &split = splits.mSplits[target_split]; + split_remap_table[s] = target_split; + + // Allocate space for contacts + split.mContactBufferBegin = offset; + split.mContactBufferEnd = split.mContactBufferBegin + num_contacts_in_split[s]; + + // Allocate space for constraints + split.mConstraintBufferBegin = split.mContactBufferEnd; + split.mConstraintBufferEnd = split.mConstraintBufferBegin + num_constraints_in_split[s]; + + // Store start for each split + contact_buffer_cur[target_split] = mContactAndConstraintIndices + split.mContactBufferBegin; + constraint_buffer_cur[target_split] = mContactAndConstraintIndices + split.mConstraintBufferBegin; + + // Update offset + offset = split.mConstraintBufferEnd; + } + } + + // Split the contacts + for (uint c = 0; c < num_contacts_in_island; ++c) + { + uint split = split_remap_table[contact_split_idx[c]]; + *contact_buffer_cur[split]++ = contacts_start[c]; + } + + // Split the constraints + for (uint c = 0; c < num_constraints_in_island; ++c) + { + uint split = split_remap_table[constraint_split_idx[c]]; + *constraint_buffer_cur[split]++ = constraints_start[c]; + } + +#ifdef JPH_LARGE_ISLAND_SPLITTER_DEBUG + // Trace the size of all splits + uint sum = 0; + String stats; + for (uint s = 0; s < cNumSplits; ++s) + { + // If we've processed all splits, jump to the non-parallel split + if (s >= splits.GetNumSplits()) + s = cNonParallelSplitIdx; + + const Split &split = splits.mSplits[s]; + stats += StringFormat("g:%d:%d:%d, ", s, split.GetNumContacts(), split.GetNumConstraints()); + sum += split.GetNumItems(); + } + stats += StringFormat("sum: %d", sum); + Trace(stats.c_str()); +#endif // JPH_LARGE_ISLAND_SPLITTER_DEBUG + +#ifdef JPH_ENABLE_ASSERTS + for (uint s = 0; s < cNumSplits; ++s) + { + // If there are no more splits, process the non-parallel split + if (s >= splits.mNumSplits) + s = cNonParallelSplitIdx; + + // Check that we wrote all elements + Split &split = splits.mSplits[s]; + JPH_ASSERT(contact_buffer_cur[s] == mContactAndConstraintIndices + split.mContactBufferEnd); + JPH_ASSERT(constraint_buffer_cur[s] == mContactAndConstraintIndices + split.mConstraintBufferEnd); + } + +#ifdef _DEBUG + // Validate that the splits are indeed not touching the same body + for (uint s = 0; s < splits.mNumSplits; ++s) + { + Array body_used(mNumActiveBodies, false); + + // Validate contacts + uint32 split_contacts_begin, split_contacts_end; + splits.GetContactsInSplit(s, split_contacts_begin, split_contacts_end); + for (uint32 *c = mContactAndConstraintIndices + split_contacts_begin; c < mContactAndConstraintIndices + split_contacts_end; ++c) + { + const Body *body1, *body2; + inContactManager.GetAffectedBodies(*c, body1, body2); + + uint32 idx1 = body1->GetIndexInActiveBodiesInternal(); + if (idx1 != Body::cInactiveIndex && body1->IsDynamic()) + { + JPH_ASSERT(!body_used[idx1]); + body_used[idx1] = true; + } + + uint32 idx2 = body2->GetIndexInActiveBodiesInternal(); + if (idx2 != Body::cInactiveIndex && body2->IsDynamic()) + { + JPH_ASSERT(!body_used[idx2]); + body_used[idx2] = true; + } + } + } +#endif // _DEBUG +#endif // JPH_ENABLE_ASSERTS + + // Allow other threads to pick up this split island now + splits.StartFirstBatch(); + return true; +} + +LargeIslandSplitter::EStatus LargeIslandSplitter::FetchNextBatch(uint &outSplitIslandIndex, uint32 *&outConstraintsBegin, uint32 *&outConstraintsEnd, uint32 *&outContactsBegin, uint32 *&outContactsEnd, bool &outFirstIteration) +{ + // We can't be done when all islands haven't been submitted yet + uint num_splits_created = mNextSplitIsland.load(memory_order_acquire); + bool all_done = num_splits_created == mNumSplitIslands; + + // Loop over all split islands to find work + uint32 constraints_begin, constraints_end, contacts_begin, contacts_end; + for (Splits *s = mSplitIslands; s < mSplitIslands + num_splits_created; ++s) + switch (s->FetchNextBatch(constraints_begin, constraints_end, contacts_begin, contacts_end, outFirstIteration)) + { + case EStatus::AllBatchesDone: + break; + + case EStatus::WaitingForBatch: + all_done = false; + break; + + case EStatus::BatchRetrieved: + outSplitIslandIndex = uint(s - mSplitIslands); + outConstraintsBegin = mContactAndConstraintIndices + constraints_begin; + outConstraintsEnd = mContactAndConstraintIndices + constraints_end; + outContactsBegin = mContactAndConstraintIndices + contacts_begin; + outContactsEnd = mContactAndConstraintIndices + contacts_end; + return EStatus::BatchRetrieved; + } + + return all_done? EStatus::AllBatchesDone : EStatus::WaitingForBatch; +} + +void LargeIslandSplitter::MarkBatchProcessed(uint inSplitIslandIndex, const uint32 *inConstraintsBegin, const uint32 *inConstraintsEnd, const uint32 *inContactsBegin, const uint32 *inContactsEnd, bool &outLastIteration, bool &outFinalBatch) +{ + uint num_items_processed = uint(inConstraintsEnd - inConstraintsBegin) + uint(inContactsEnd - inContactsBegin); + + JPH_ASSERT(inSplitIslandIndex < mNextSplitIsland.load(memory_order_relaxed)); + Splits &splits = mSplitIslands[inSplitIslandIndex]; + splits.MarkBatchProcessed(num_items_processed, outLastIteration, outFinalBatch); +} + +void LargeIslandSplitter::PrepareForSolvePositions() +{ + for (Splits *s = mSplitIslands, *s_end = mSplitIslands + mNumSplitIslands; s < s_end; ++s) + { + // Set the number of iterations to the number of position steps + s->mNumIterations = s->mNumPositionSteps; + + // We can start again from the first batch + s->StartFirstBatch(); + } +} + +void LargeIslandSplitter::Reset(TempAllocator *inTempAllocator) +{ + JPH_PROFILE_FUNCTION(); + + // Everything should have been used + JPH_ASSERT(mContactAndConstraintsNextFree.load(memory_order_relaxed) == mContactAndConstraintsSize); + JPH_ASSERT(mNextSplitIsland.load(memory_order_relaxed) == mNumSplitIslands); + + // Free split islands + if (mNumSplitIslands > 0) + { + inTempAllocator->Free(mSplitIslands, mNumSplitIslands * sizeof(Splits)); + mSplitIslands = nullptr; + + mNumSplitIslands = 0; + mNextSplitIsland.store(0, memory_order_relaxed); + } + + // Free contact and constraint buffers + if (mContactAndConstraintsSize > 0) + { + inTempAllocator->Free(mContactAndConstraintIndices, mContactAndConstraintsSize * sizeof(uint32)); + mContactAndConstraintIndices = nullptr; + + inTempAllocator->Free(mContactAndConstraintsSplitIdx, mContactAndConstraintsSize * sizeof(uint32)); + mContactAndConstraintsSplitIdx = nullptr; + + mContactAndConstraintsSize = 0; + mContactAndConstraintsNextFree.store(0, memory_order_relaxed); + } + + // Free split masks + if (mSplitMasks != nullptr) + { + inTempAllocator->Free(mSplitMasks, mNumActiveBodies * sizeof(SplitMask)); + mSplitMasks = nullptr; + + mNumActiveBodies = 0; + } +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/LargeIslandSplitter.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/LargeIslandSplitter.h new file mode 100644 index 00000000000..8e61d093055 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/LargeIslandSplitter.h @@ -0,0 +1,185 @@ +// SPDX-FileCopyrightText: 2023 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +JPH_NAMESPACE_BEGIN + +class Body; +class BodyID; +class IslandBuilder; +class TempAllocator; +class Constraint; +class BodyManager; +class ContactConstraintManager; +class CalculateSolverSteps; + +/// Assigns bodies in large islands to multiple groups that can run in parallel +/// +/// This basically implements what is described in: High-Performance Physical Simulations on Next-Generation Architecture with Many Cores by Chen et al. +/// See: http://web.eecs.umich.edu/~msmelyan/papers/physsim_onmanycore_itj.pdf section "PARALLELIZATION METHODOLOGY" +class LargeIslandSplitter : public NonCopyable +{ +private: + using SplitMask = uint32; + +public: + static constexpr uint cNumSplits = sizeof(SplitMask) * 8; + static constexpr uint cNonParallelSplitIdx = cNumSplits - 1; + static constexpr uint cLargeIslandTreshold = 128; ///< If the number of constraints + contacts in an island is larger than this, we will try to split the island + + /// Status code for retrieving a batch + enum class EStatus + { + WaitingForBatch, ///< Work is expected to be available later + BatchRetrieved, ///< Work is being returned + AllBatchesDone, ///< No further work is expected from this + }; + + /// Describes a split of constraints and contacts + struct Split + { + inline uint GetNumContacts() const { return mContactBufferEnd - mContactBufferBegin; } + inline uint GetNumConstraints() const { return mConstraintBufferEnd - mConstraintBufferBegin; } + inline uint GetNumItems() const { return GetNumContacts() + GetNumConstraints(); } + + uint32 mContactBufferBegin; ///< Begin of the contact buffer (offset relative to mContactAndConstraintIndices) + uint32 mContactBufferEnd; ///< End of the contact buffer + + uint32 mConstraintBufferBegin; ///< Begin of the constraint buffer (offset relative to mContactAndConstraintIndices) + uint32 mConstraintBufferEnd; ///< End of the constraint buffer + }; + + /// Structure that describes the resulting splits from the large island splitter + class Splits + { + public: + inline uint GetNumSplits() const + { + return mNumSplits; + } + + inline void GetConstraintsInSplit(uint inSplitIndex, uint32 &outConstraintsBegin, uint32 &outConstraintsEnd) const + { + const Split &split = mSplits[inSplitIndex]; + outConstraintsBegin = split.mConstraintBufferBegin; + outConstraintsEnd = split.mConstraintBufferEnd; + } + + inline void GetContactsInSplit(uint inSplitIndex, uint32 &outContactsBegin, uint32 &outContactsEnd) const + { + const Split &split = mSplits[inSplitIndex]; + outContactsBegin = split.mContactBufferBegin; + outContactsEnd = split.mContactBufferEnd; + } + + /// Reset current status so that no work can be picked up from this split + inline void ResetStatus() + { + mStatus.store(StatusItemMask, memory_order_relaxed); + } + + /// Make the first batch available to other threads + inline void StartFirstBatch() + { + uint split_index = mNumSplits > 0? 0 : cNonParallelSplitIdx; + mStatus.store(uint64(split_index) << StatusSplitShift, memory_order_release); + } + + /// Fetch the next batch to process + EStatus FetchNextBatch(uint32 &outConstraintsBegin, uint32 &outConstraintsEnd, uint32 &outContactsBegin, uint32 &outContactsEnd, bool &outFirstIteration); + + /// Mark a batch as processed + void MarkBatchProcessed(uint inNumProcessed, bool &outLastIteration, bool &outFinalBatch); + + enum EIterationStatus : uint64 + { + StatusIterationMask = 0xffff000000000000, + StatusIterationShift = 48, + StatusSplitMask = 0x0000ffff00000000, + StatusSplitShift = 32, + StatusItemMask = 0x00000000ffffffff, + }; + + static inline int sGetIteration(uint64 inStatus) + { + return int((inStatus & StatusIterationMask) >> StatusIterationShift); + } + + static inline uint sGetSplit(uint64 inStatus) + { + return uint((inStatus & StatusSplitMask) >> StatusSplitShift); + } + + static inline uint sGetItem(uint64 inStatus) + { + return uint(inStatus & StatusItemMask); + } + + Split mSplits[cNumSplits]; ///< Data per split + uint32 mIslandIndex; ///< Index of the island that was split + uint mNumSplits; ///< Number of splits that were created (excluding the non-parallel split) + int mNumIterations; ///< Number of iterations to do + int mNumVelocitySteps; ///< Number of velocity steps to do (cached for 2nd sub step) + int mNumPositionSteps; ///< Number of position steps to do + atomic mStatus; ///< Status of the split, see EIterationStatus + atomic mItemsProcessed; ///< Number of items that have been marked as processed + }; + +public: + /// Destructor + ~LargeIslandSplitter(); + + /// Prepare the island splitter by allocating memory + void Prepare(const IslandBuilder &inIslandBuilder, uint32 inNumActiveBodies, TempAllocator *inTempAllocator); + + /// Assign two bodies to a split. Returns the split index. + uint AssignSplit(const Body *inBody1, const Body *inBody2); + + /// Force a body to be in a non parallel split. Returns the split index. + uint AssignToNonParallelSplit(const Body *inBody); + + /// Splits up an island, the created splits will be added to the list of batches and can be fetched with FetchNextBatch. Returns false if the island did not need splitting. + bool SplitIsland(uint32 inIslandIndex, const IslandBuilder &inIslandBuilder, const BodyManager &inBodyManager, const ContactConstraintManager &inContactManager, Constraint **inActiveConstraints, CalculateSolverSteps &ioStepsCalculator); + + /// Fetch the next batch to process, returns a handle in outSplitIslandIndex that must be provided to MarkBatchProcessed when complete + EStatus FetchNextBatch(uint &outSplitIslandIndex, uint32 *&outConstraintsBegin, uint32 *&outConstraintsEnd, uint32 *&outContactsBegin, uint32 *&outContactsEnd, bool &outFirstIteration); + + /// Mark a batch as processed + void MarkBatchProcessed(uint inSplitIslandIndex, const uint32 *inConstraintsBegin, const uint32 *inConstraintsEnd, const uint32 *inContactsBegin, const uint32 *inContactsEnd, bool &outLastIteration, bool &outFinalBatch); + + /// Get the island index of the island that was split for a particular split island index + inline uint32 GetIslandIndex(uint inSplitIslandIndex) const + { + JPH_ASSERT(inSplitIslandIndex < mNumSplitIslands); + return mSplitIslands[inSplitIslandIndex].mIslandIndex; + } + + /// Prepare the island splitter for iterating over the split islands again for position solving. Marks all batches as startable. + void PrepareForSolvePositions(); + + /// Reset the island splitter + void Reset(TempAllocator *inTempAllocator); + +private: + static constexpr uint cSplitCombineTreshold = 32; ///< If the number of constraints + contacts in a split is lower than this, we will merge this split into the 'non-parallel split' + static constexpr uint cBatchSize = 16; ///< Number of items to process in a constraint batch + + uint32 mNumActiveBodies = 0; ///< Cached number of active bodies + + SplitMask * mSplitMasks = nullptr; ///< Bits that indicate for each body in the BodyManager::mActiveBodies list which split they already belong to + + uint32 * mContactAndConstraintsSplitIdx = nullptr; ///< Buffer to store the split index per constraint or contact + uint32 * mContactAndConstraintIndices = nullptr; ///< Buffer to store the ordered constraint indices per split + uint mContactAndConstraintsSize = 0; ///< Total size of mContactAndConstraintsSplitIdx and mContactAndConstraintIndices + atomic mContactAndConstraintsNextFree { 0 }; ///< Next element that is free in both buffers + + uint mNumSplitIslands = 0; ///< Total number of islands that required splitting + Splits * mSplitIslands = nullptr; ///< List of islands that required splitting + atomic mNextSplitIsland = 0; ///< Next split island to pick from mSplitIslands +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsLock.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsLock.cpp new file mode 100644 index 00000000000..690b5e58232 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsLock.cpp @@ -0,0 +1,17 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include + +#ifdef JPH_ENABLE_ASSERTS + +JPH_NAMESPACE_BEGIN + +thread_local PhysicsLock::LockData PhysicsLock::sLocks[4]; + +JPH_NAMESPACE_END + +#endif diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsLock.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsLock.h new file mode 100644 index 00000000000..62cf72e9962 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsLock.h @@ -0,0 +1,169 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +#ifdef JPH_ENABLE_ASSERTS + +/// This is the list of locks used by the physics engine, they need to be locked in a particular order (from top of the list to bottom of the list) in order to prevent deadlocks +enum class EPhysicsLockTypes +{ + BroadPhaseQuery = 1 << 0, + PerBody = 1 << 1, + BodiesList = 1 << 2, + BroadPhaseUpdate = 1 << 3, + ConstraintsList = 1 << 4, + ActiveBodiesList = 1 << 5, +}; + +/// A token that indicates the context of a lock (we use 1 per physics system and we use the body manager pointer because it's convenient) +class BodyManager; +using PhysicsLockContext = const BodyManager *; + +#endif // !JPH_ENABLE_ASSERTS + +/// Helpers to safely lock the different mutexes that are part of the physics system while preventing deadlock +/// Class that keeps track per thread which lock are taken and if the order of locking is correct +class PhysicsLock +{ +public: +#ifdef JPH_ENABLE_ASSERTS + /// Call before taking the lock + static inline void sCheckLock(PhysicsLockContext inContext, EPhysicsLockTypes inType) + { + uint32 &mutexes = sGetLockedMutexes(inContext); + JPH_ASSERT(uint32(inType) > mutexes, "A lock of same or higher priority was already taken, this can create a deadlock!"); + mutexes = mutexes | uint32(inType); + } + + /// Call after releasing the lock + static inline void sCheckUnlock(PhysicsLockContext inContext, EPhysicsLockTypes inType) + { + uint32 &mutexes = sGetLockedMutexes(inContext); + JPH_ASSERT((mutexes & uint32(inType)) != 0, "Mutex was not locked!"); + mutexes = mutexes & ~uint32(inType); + } +#endif // !JPH_ENABLE_ASSERTS + + template + static inline void sLock(LockType &inMutex JPH_IF_ENABLE_ASSERTS(, PhysicsLockContext inContext, EPhysicsLockTypes inType)) + { + JPH_IF_ENABLE_ASSERTS(sCheckLock(inContext, inType);) + inMutex.lock(); + } + + template + static inline void sUnlock(LockType &inMutex JPH_IF_ENABLE_ASSERTS(, PhysicsLockContext inContext, EPhysicsLockTypes inType)) + { + JPH_IF_ENABLE_ASSERTS(sCheckUnlock(inContext, inType);) + inMutex.unlock(); + } + + template + static inline void sLockShared(LockType &inMutex JPH_IF_ENABLE_ASSERTS(, PhysicsLockContext inContext, EPhysicsLockTypes inType)) + { + JPH_IF_ENABLE_ASSERTS(sCheckLock(inContext, inType);) + inMutex.lock_shared(); + } + + template + static inline void sUnlockShared(LockType &inMutex JPH_IF_ENABLE_ASSERTS(, PhysicsLockContext inContext, EPhysicsLockTypes inType)) + { + JPH_IF_ENABLE_ASSERTS(sCheckUnlock(inContext, inType);) + inMutex.unlock_shared(); + } + +#ifdef JPH_ENABLE_ASSERTS +private: + struct LockData + { + uint32 mLockedMutexes = 0; + PhysicsLockContext mContext = nullptr; + }; + + static thread_local LockData sLocks[4]; + + // Helper function to find the locked mutexes for a particular context + static uint32 & sGetLockedMutexes(PhysicsLockContext inContext) + { + // If we find a matching context we can use it + for (LockData &l : sLocks) + if (l.mContext == inContext) + return l.mLockedMutexes; + + // Otherwise we look for an entry that is not in use + for (LockData &l : sLocks) + if (l.mLockedMutexes == 0) + { + l.mContext = inContext; + return l.mLockedMutexes; + } + + JPH_ASSERT(false, "Too many physics systems locked at the same time!"); + return sLocks[0].mLockedMutexes; + } +#endif // !JPH_ENABLE_ASSERTS +}; + +/// Helper class that is similar to std::unique_lock +template +class UniqueLock : public NonCopyable +{ +public: + explicit UniqueLock(LockType &inLock JPH_IF_ENABLE_ASSERTS(, PhysicsLockContext inContext, EPhysicsLockTypes inType)) : + mLock(inLock) +#ifdef JPH_ENABLE_ASSERTS + , mContext(inContext), + mType(inType) +#endif // JPH_ENABLE_ASSERTS + { + PhysicsLock::sLock(mLock JPH_IF_ENABLE_ASSERTS(, mContext, mType)); + } + + ~UniqueLock() + { + PhysicsLock::sUnlock(mLock JPH_IF_ENABLE_ASSERTS(, mContext, mType)); + } + +private: + LockType & mLock; +#ifdef JPH_ENABLE_ASSERTS + PhysicsLockContext mContext; + EPhysicsLockTypes mType; +#endif // JPH_ENABLE_ASSERTS +}; + +/// Helper class that is similar to std::shared_lock +template +class SharedLock : public NonCopyable +{ +public: + explicit SharedLock(LockType &inLock JPH_IF_ENABLE_ASSERTS(, PhysicsLockContext inContext, EPhysicsLockTypes inType)) : + mLock(inLock) +#ifdef JPH_ENABLE_ASSERTS + , mContext(inContext) + , mType(inType) +#endif // JPH_ENABLE_ASSERTS + { + PhysicsLock::sLockShared(mLock JPH_IF_ENABLE_ASSERTS(, mContext, mType)); + } + + ~SharedLock() + { + PhysicsLock::sUnlockShared(mLock JPH_IF_ENABLE_ASSERTS(, mContext, mType)); + } + +private: + LockType & mLock; +#ifdef JPH_ENABLE_ASSERTS + PhysicsLockContext mContext; + EPhysicsLockTypes mType; +#endif // JPH_ENABLE_ASSERTS +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsScene.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsScene.cpp new file mode 100644 index 00000000000..ed7e11c354b --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsScene.cpp @@ -0,0 +1,261 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(PhysicsScene) +{ + JPH_ADD_ATTRIBUTE(PhysicsScene, mBodies) + JPH_ADD_ATTRIBUTE(PhysicsScene, mConstraints) + JPH_ADD_ATTRIBUTE(PhysicsScene, mSoftBodies) +} + +JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(PhysicsScene::ConnectedConstraint) +{ + JPH_ADD_ATTRIBUTE(PhysicsScene::ConnectedConstraint, mSettings) + JPH_ADD_ATTRIBUTE(PhysicsScene::ConnectedConstraint, mBody1) + JPH_ADD_ATTRIBUTE(PhysicsScene::ConnectedConstraint, mBody2) +} + +void PhysicsScene::AddBody(const BodyCreationSettings &inBody) +{ + mBodies.push_back(inBody); +} + +void PhysicsScene::AddConstraint(const TwoBodyConstraintSettings *inConstraint, uint32 inBody1, uint32 inBody2) +{ + mConstraints.emplace_back(inConstraint, inBody1, inBody2); +} + +void PhysicsScene::AddSoftBody(const SoftBodyCreationSettings &inSoftBody) +{ + mSoftBodies.push_back(inSoftBody); +} + +bool PhysicsScene::FixInvalidScales() +{ + const Vec3 unit_scale = Vec3::sReplicate(1.0f); + + bool success = true; + for (BodyCreationSettings &b : mBodies) + { + // Test if there is an invalid scale in the shape hierarchy + const Shape *shape = b.GetShape(); + if (!shape->IsValidScale(unit_scale)) + { + // Fix it up + Shape::ShapeResult result = shape->ScaleShape(unit_scale); + if (result.IsValid()) + b.SetShape(result.Get()); + else + success = false; + } + } + return success; +} + +bool PhysicsScene::CreateBodies(PhysicsSystem *inSystem) const +{ + BodyInterface &bi = inSystem->GetBodyInterface(); + + BodyIDVector body_ids; + body_ids.reserve(mBodies.size() + mSoftBodies.size()); + + // Create bodies + for (const BodyCreationSettings &b : mBodies) + { + const Body *body = bi.CreateBody(b); + if (body == nullptr) + break; + body_ids.push_back(body->GetID()); + } + + // Create soft bodies + for (const SoftBodyCreationSettings &b : mSoftBodies) + { + const Body *body = bi.CreateSoftBody(b); + if (body == nullptr) + break; + body_ids.push_back(body->GetID()); + } + + // Batch add bodies + BodyIDVector temp_body_ids = body_ids; // Body ID's get shuffled by AddBodiesPrepare + BodyInterface::AddState add_state = bi.AddBodiesPrepare(temp_body_ids.data(), (int)temp_body_ids.size()); + bi.AddBodiesFinalize(temp_body_ids.data(), (int)temp_body_ids.size(), add_state, EActivation::Activate); + + // If not all bodies are created, creating constraints will be unreliable + if (body_ids.size() != mBodies.size() + mSoftBodies.size()) + return false; + + // Create constraints + for (const ConnectedConstraint &cc : mConstraints) + { + BodyID body1_id = cc.mBody1 == cFixedToWorld? BodyID() : body_ids[cc.mBody1]; + BodyID body2_id = cc.mBody2 == cFixedToWorld? BodyID() : body_ids[cc.mBody2]; + Constraint *c = bi.CreateConstraint(cc.mSettings, body1_id, body2_id); + inSystem->AddConstraint(c); + } + + // Everything was created + return true; +} + +void PhysicsScene::SaveBinaryState(StreamOut &inStream, bool inSaveShapes, bool inSaveGroupFilter) const +{ + BodyCreationSettings::ShapeToIDMap shape_to_id; + BodyCreationSettings::MaterialToIDMap material_to_id; + BodyCreationSettings::GroupFilterToIDMap group_filter_to_id; + SoftBodyCreationSettings::SharedSettingsToIDMap settings_to_id; + + // Save bodies + inStream.Write((uint32)mBodies.size()); + for (const BodyCreationSettings &b : mBodies) + b.SaveWithChildren(inStream, inSaveShapes? &shape_to_id : nullptr, inSaveShapes? &material_to_id : nullptr, inSaveGroupFilter? &group_filter_to_id : nullptr); + + // Save constraints + inStream.Write((uint32)mConstraints.size()); + for (const ConnectedConstraint &cc : mConstraints) + { + cc.mSettings->SaveBinaryState(inStream); + inStream.Write(cc.mBody1); + inStream.Write(cc.mBody2); + } + + // Save soft bodies + inStream.Write((uint32)mSoftBodies.size()); + for (const SoftBodyCreationSettings &b : mSoftBodies) + b.SaveWithChildren(inStream, &settings_to_id, &material_to_id, inSaveGroupFilter? &group_filter_to_id : nullptr); +} + +PhysicsScene::PhysicsSceneResult PhysicsScene::sRestoreFromBinaryState(StreamIn &inStream) +{ + PhysicsSceneResult result; + + // Create scene + Ref scene = new PhysicsScene(); + + BodyCreationSettings::IDToShapeMap id_to_shape; + BodyCreationSettings::IDToMaterialMap id_to_material; + BodyCreationSettings::IDToGroupFilterMap id_to_group_filter; + SoftBodyCreationSettings::IDToSharedSettingsMap id_to_settings; + + // Reserve some memory to avoid frequent reallocations + id_to_shape.reserve(1024); + id_to_material.reserve(128); + id_to_group_filter.reserve(128); + + // Read bodies + uint32 len = 0; + inStream.Read(len); + scene->mBodies.resize(len); + for (BodyCreationSettings &b : scene->mBodies) + { + // Read creation settings + BodyCreationSettings::BCSResult bcs_result = BodyCreationSettings::sRestoreWithChildren(inStream, id_to_shape, id_to_material, id_to_group_filter); + if (bcs_result.HasError()) + { + result.SetError(bcs_result.GetError()); + return result; + } + b = bcs_result.Get(); + } + + // Read constraints + len = 0; + inStream.Read(len); + scene->mConstraints.resize(len); + for (ConnectedConstraint &cc : scene->mConstraints) + { + ConstraintSettings::ConstraintResult c_result = ConstraintSettings::sRestoreFromBinaryState(inStream); + if (c_result.HasError()) + { + result.SetError(c_result.GetError()); + return result; + } + cc.mSettings = static_cast(c_result.Get().GetPtr()); + inStream.Read(cc.mBody1); + inStream.Read(cc.mBody2); + } + + // Read soft bodies + len = 0; + inStream.Read(len); + scene->mSoftBodies.resize(len); + for (SoftBodyCreationSettings &b : scene->mSoftBodies) + { + // Read creation settings + SoftBodyCreationSettings::SBCSResult sbcs_result = SoftBodyCreationSettings::sRestoreWithChildren(inStream, id_to_settings, id_to_material, id_to_group_filter); + if (sbcs_result.HasError()) + { + result.SetError(sbcs_result.GetError()); + return result; + } + b = sbcs_result.Get(); + } + + result.Set(scene); + return result; +} + +void PhysicsScene::FromPhysicsSystem(const PhysicsSystem *inSystem) +{ + // This map will track where each body went in mBodies + using BodyIDToIdxMap = UnorderedMap; + BodyIDToIdxMap body_id_to_idx; + + // Map invalid ID + body_id_to_idx[BodyID()] = cFixedToWorld; + + // Get all bodies + BodyIDVector body_ids; + inSystem->GetBodies(body_ids); + + // Loop over all bodies + const BodyLockInterface &bli = inSystem->GetBodyLockInterface(); + for (const BodyID &id : body_ids) + { + BodyLockRead lock(bli, id); + if (lock.Succeeded()) + { + // Store location of body + body_id_to_idx[id] = (uint32)mBodies.size(); + + const Body &body = lock.GetBody(); + + // Convert to body creation settings + if (body.IsRigidBody()) + AddBody(body.GetBodyCreationSettings()); + else + AddSoftBody(body.GetSoftBodyCreationSettings()); + } + } + + // Loop over all constraints + Constraints constraints = inSystem->GetConstraints(); + for (const Constraint *c : constraints) + if (c->GetType() == EConstraintType::TwoBodyConstraint) + { + // Cast to two body constraint + const TwoBodyConstraint *tbc = static_cast(c); + + // Find the body indices + BodyIDToIdxMap::const_iterator b1 = body_id_to_idx.find(tbc->GetBody1()->GetID()); + BodyIDToIdxMap::const_iterator b2 = body_id_to_idx.find(tbc->GetBody2()->GetID()); + JPH_ASSERT(b1 != body_id_to_idx.end() && b2 != body_id_to_idx.end()); + + // Create constraint settings and add the constraint + Ref settings = c->GetConstraintSettings(); + AddConstraint(static_cast(settings.GetPtr()), b1->second, b2->second); + } +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsScene.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsScene.h new file mode 100644 index 00000000000..530b79d61e4 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsScene.h @@ -0,0 +1,104 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +class PhysicsSystem; + +/// Contains the creation settings of a set of bodies +class JPH_EXPORT PhysicsScene : public RefTarget +{ +public: + JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, PhysicsScene) + + /// Add a body to the scene + void AddBody(const BodyCreationSettings &inBody); + + /// Body constant to use to indicate that the constraint is attached to the fixed world + static constexpr uint32 cFixedToWorld = 0xffffffff; + + /// Add a constraint to the scene + /// @param inConstraint Constraint settings + /// @param inBody1 Index in the bodies list of first body to attach constraint to + /// @param inBody2 Index in the bodies list of the second body to attach constraint to + void AddConstraint(const TwoBodyConstraintSettings *inConstraint, uint32 inBody1, uint32 inBody2); + + /// Add a soft body to the scene + void AddSoftBody(const SoftBodyCreationSettings &inSoftBody); + + /// Get number of bodies in this scene + size_t GetNumBodies() const { return mBodies.size(); } + + /// Access to the body settings for this scene + const Array & GetBodies() const { return mBodies; } + Array & GetBodies() { return mBodies; } + + /// A constraint and how it is connected to the bodies in the scene + class ConnectedConstraint + { + public: + JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, ConnectedConstraint) + + ConnectedConstraint() = default; + ConnectedConstraint(const TwoBodyConstraintSettings *inSettings, uint inBody1, uint inBody2) : mSettings(inSettings), mBody1(inBody1), mBody2(inBody2) { } + + RefConst mSettings; ///< Constraint settings + uint32 mBody1; ///< Index of first body (in mBodies) + uint32 mBody2; ///< Index of second body (in mBodies) + }; + + /// Get number of constraints in this scene + size_t GetNumConstraints() const { return mConstraints.size(); } + + /// Access to the constraints for this scene + const Array & GetConstraints() const { return mConstraints; } + Array & GetConstraints() { return mConstraints; } + + /// Get number of bodies in this scene + size_t GetNumSoftBodies() const { return mSoftBodies.size(); } + + /// Access to the soft body settings for this scene + const Array & GetSoftBodies() const { return mSoftBodies; } + Array & GetSoftBodies() { return mSoftBodies; } + + /// Instantiate all bodies, returns false if not all bodies could be created + bool CreateBodies(PhysicsSystem *inSystem) const; + + /// Go through all body creation settings and fix shapes that are scaled incorrectly (note this will change the scene a bit). + /// @return False when not all scales could be fixed. + bool FixInvalidScales(); + + /// Saves the state of this object in binary form to inStream. + /// @param inStream The stream to save the state to + /// @param inSaveShapes If the shapes should be saved as well (these could be shared between physics scenes, in which case the calling application may want to write custom code to restore them) + /// @param inSaveGroupFilter If the group filter should be saved as well (these could be shared) + void SaveBinaryState(StreamOut &inStream, bool inSaveShapes, bool inSaveGroupFilter) const; + + using PhysicsSceneResult = Result>; + + /// Restore a saved scene from inStream + static PhysicsSceneResult sRestoreFromBinaryState(StreamIn &inStream); + + /// For debugging purposes: Construct a scene from the current state of the physics system + void FromPhysicsSystem(const PhysicsSystem *inSystem); + +private: + /// The bodies that are part of this scene + Array mBodies; + + /// Constraints that are part of this scene + Array mConstraints; + + /// Soft bodies that are part of this scene + Array mSoftBodies; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsSettings.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsSettings.h new file mode 100644 index 00000000000..eb8ecbbeaf8 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsSettings.h @@ -0,0 +1,119 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +JPH_NAMESPACE_BEGIN + +/// If objects are closer than this distance, they are considered to be colliding (used for GJK) (unit: meter) +constexpr float cDefaultCollisionTolerance = 1.0e-4f; + +/// A factor that determines the accuracy of the penetration depth calculation. If the change of the squared distance is less than tolerance * current_penetration_depth^2 the algorithm will terminate. (unit: dimensionless) +constexpr float cDefaultPenetrationTolerance = 1.0e-4f; ///< Stop when there's less than 1% change + +/// How much padding to add around objects +constexpr float cDefaultConvexRadius = 0.05f; + +/// Used by (Tapered)CapsuleShape to determine when supporting face is an edge rather than a point (unit: meter) +static constexpr float cCapsuleProjectionSlop = 0.02f; + +/// Maximum amount of jobs to allow +constexpr int cMaxPhysicsJobs = 2048; + +/// Maximum amount of barriers to allow +constexpr int cMaxPhysicsBarriers = 8; + +struct PhysicsSettings +{ + JPH_OVERRIDE_NEW_DELETE + + /// Size of body pairs array, corresponds to the maximum amount of potential body pairs that can be in flight at any time. + /// Setting this to a low value will use less memory but slow down simulation as threads may run out of narrow phase work. + int mMaxInFlightBodyPairs = 16384; + + /// How many PhysicsStepListeners to notify in 1 batch + int mStepListenersBatchSize = 8; + + /// How many step listener batches are needed before spawning another job (set to INT_MAX if no parallelism is desired) + int mStepListenerBatchesPerJob = 1; + + /// Baumgarte stabilization factor (how much of the position error to 'fix' in 1 update) (unit: dimensionless, 0 = nothing, 1 = 100%) + float mBaumgarte = 0.2f; + + /// Radius around objects inside which speculative contact points will be detected. Note that if this is too big + /// you will get ghost collisions as speculative contacts are based on the closest points during the collision detection + /// step which may not be the actual closest points by the time the two objects hit (unit: meters) + float mSpeculativeContactDistance = 0.02f; + + /// How much bodies are allowed to sink into each other (unit: meters) + float mPenetrationSlop = 0.02f; + + /// Fraction of its inner radius a body must move per step to enable casting for the LinearCast motion quality + float mLinearCastThreshold = 0.75f; + + /// Fraction of its inner radius a body may penetrate another body for the LinearCast motion quality + float mLinearCastMaxPenetration = 0.25f; + + /// Max squared distance to use to determine if two points are on the same plane for determining the contact manifold between two shape faces (unit: meter^2) + float mManifoldToleranceSq = 1.0e-6f; + + /// Maximum distance to correct in a single iteration when solving position constraints (unit: meters) + float mMaxPenetrationDistance = 0.2f; + + /// Maximum relative delta position for body pairs to be able to reuse collision results from last frame (units: meter^2) + float mBodyPairCacheMaxDeltaPositionSq = Square(0.001f); ///< 1 mm + + /// Maximum relative delta orientation for body pairs to be able to reuse collision results from last frame, stored as cos(max angle / 2) + float mBodyPairCacheCosMaxDeltaRotationDiv2 = 0.99984769515639123915701155881391f; ///< cos(2 degrees / 2) + + /// Maximum angle between normals that allows manifolds between different sub shapes of the same body pair to be combined + float mContactNormalCosMaxDeltaRotation = 0.99619469809174553229501040247389f; ///< cos(5 degree) + + /// Maximum allowed distance between old and new contact point to preserve contact forces for warm start (units: meter^2) + float mContactPointPreserveLambdaMaxDistSq = Square(0.01f); ///< 1 cm + + /// Number of solver velocity iterations to run + /// Note that this needs to be >= 2 in order for friction to work (friction is applied using the non-penetration impulse from the previous iteration) + uint mNumVelocitySteps = 10; + + /// Number of solver position iterations to run + uint mNumPositionSteps = 2; + + /// Minimal velocity needed before a collision can be elastic (unit: m) + float mMinVelocityForRestitution = 1.0f; + + /// Time before object is allowed to go to sleep (unit: seconds) + float mTimeBeforeSleep = 0.5f; + + /// Velocity of points on bounding box of object below which an object can be considered sleeping (unit: m/s) + float mPointVelocitySleepThreshold = 0.03f; + + /// By default the simulation is deterministic, it is possible to turn this off by setting this setting to false. This will make the simulation run faster but it will no longer be deterministic. + bool mDeterministicSimulation = true; + + ///@name These variables are mainly for debugging purposes, they allow turning on/off certain subsystems. You probably want to leave them alone. + ///@{ + + /// Whether or not to use warm starting for constraints (initially applying previous frames impulses) + bool mConstraintWarmStart = true; + + /// Whether or not to use the body pair cache, which removes the need for narrow phase collision detection when orientation between two bodies didn't change + bool mUseBodyPairContactCache = true; + + /// Whether or not to reduce manifolds with similar contact normals into one contact manifold (see description at Body::SetUseManifoldReduction) + bool mUseManifoldReduction = true; + + /// If we split up large islands into smaller parallel batches of work (to improve performance) + bool mUseLargeIslandSplitter = true; + + /// If objects can go to sleep or not + bool mAllowSleeping = true; + + /// When false, we prevent collision against non-active (shared) edges. Mainly for debugging the algorithm. + bool mCheckActiveEdges = true; + + ///@} +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsStepListener.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsStepListener.h new file mode 100644 index 00000000000..80cf0519bde --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsStepListener.h @@ -0,0 +1,27 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +JPH_NAMESPACE_BEGIN + +class PhysicsSystem; + +/// A listener class that receives a callback before every physics simulation step +class JPH_EXPORT PhysicsStepListener +{ +public: + /// Ensure virtual destructor + virtual ~PhysicsStepListener() = default; + + /// Called before every simulation step (received inCollisionSteps times for every PhysicsSystem::Update(...) call) + /// This is called while all body and constraint mutexes are locked. You can read/write bodies and constraints but not add/remove them. + /// Multiple listeners can be executed in parallel and it is the responsibility of the listener to avoid race conditions. + /// The best way to do this is to have each step listener operate on a subset of the bodies and constraints + /// and making sure that these bodies and constraints are not touched by any other step listener. + /// Note that this function is not called if there aren't any active bodies or when the physics system is updated with 0 delta time. + virtual void OnStep(float inDeltaTime, PhysicsSystem &inPhysicsSystem) = 0; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsSystem.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsSystem.cpp new file mode 100644 index 00000000000..be0281a3ee0 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsSystem.cpp @@ -0,0 +1,2703 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef JPH_DEBUG_RENDERER + #include +#endif // JPH_DEBUG_RENDERER + +JPH_NAMESPACE_BEGIN + +#ifdef JPH_DEBUG_RENDERER +bool PhysicsSystem::sDrawMotionQualityLinearCast = false; +#endif // JPH_DEBUG_RENDERER + +//#define BROAD_PHASE BroadPhaseBruteForce +#define BROAD_PHASE BroadPhaseQuadTree + +static const Color cColorUpdateBroadPhaseFinalize = Color::sGetDistinctColor(1); +static const Color cColorUpdateBroadPhasePrepare = Color::sGetDistinctColor(2); +static const Color cColorFindCollisions = Color::sGetDistinctColor(3); +static const Color cColorApplyGravity = Color::sGetDistinctColor(4); +static const Color cColorSetupVelocityConstraints = Color::sGetDistinctColor(5); +static const Color cColorBuildIslandsFromConstraints = Color::sGetDistinctColor(6); +static const Color cColorDetermineActiveConstraints = Color::sGetDistinctColor(7); +static const Color cColorFinalizeIslands = Color::sGetDistinctColor(8); +static const Color cColorContactRemovedCallbacks = Color::sGetDistinctColor(9); +static const Color cColorBodySetIslandIndex = Color::sGetDistinctColor(10); +static const Color cColorStartNextStep = Color::sGetDistinctColor(11); +static const Color cColorSolveVelocityConstraints = Color::sGetDistinctColor(12); +static const Color cColorPreIntegrateVelocity = Color::sGetDistinctColor(13); +static const Color cColorIntegrateVelocity = Color::sGetDistinctColor(14); +static const Color cColorPostIntegrateVelocity = Color::sGetDistinctColor(15); +static const Color cColorResolveCCDContacts = Color::sGetDistinctColor(16); +static const Color cColorSolvePositionConstraints = Color::sGetDistinctColor(17); +static const Color cColorFindCCDContacts = Color::sGetDistinctColor(18); +static const Color cColorStepListeners = Color::sGetDistinctColor(19); +static const Color cColorSoftBodyPrepare = Color::sGetDistinctColor(20); +static const Color cColorSoftBodyCollide = Color::sGetDistinctColor(21); +static const Color cColorSoftBodySimulate = Color::sGetDistinctColor(22); +static const Color cColorSoftBodyFinalize = Color::sGetDistinctColor(23); + +PhysicsSystem::~PhysicsSystem() +{ + // Remove broadphase + delete mBroadPhase; +} + +void PhysicsSystem::Init(uint inMaxBodies, uint inNumBodyMutexes, uint inMaxBodyPairs, uint inMaxContactConstraints, const BroadPhaseLayerInterface &inBroadPhaseLayerInterface, const ObjectVsBroadPhaseLayerFilter &inObjectVsBroadPhaseLayerFilter, const ObjectLayerPairFilter &inObjectLayerPairFilter) +{ + mObjectVsBroadPhaseLayerFilter = &inObjectVsBroadPhaseLayerFilter; + mObjectLayerPairFilter = &inObjectLayerPairFilter; + + // Initialize body manager + mBodyManager.Init(inMaxBodies, inNumBodyMutexes, inBroadPhaseLayerInterface); + + // Create broadphase + mBroadPhase = new BROAD_PHASE(); + mBroadPhase->Init(&mBodyManager, inBroadPhaseLayerInterface); + + // Init contact constraint manager + mContactManager.Init(inMaxBodyPairs, inMaxContactConstraints); + + // Init islands builder + mIslandBuilder.Init(inMaxBodies); + + // Initialize body interface + mBodyInterfaceLocking.Init(mBodyLockInterfaceLocking, mBodyManager, *mBroadPhase); + mBodyInterfaceNoLock.Init(mBodyLockInterfaceNoLock, mBodyManager, *mBroadPhase); + + // Initialize narrow phase query + mNarrowPhaseQueryLocking.Init(mBodyLockInterfaceLocking, *mBroadPhase); + mNarrowPhaseQueryNoLock.Init(mBodyLockInterfaceNoLock, *mBroadPhase); +} + +void PhysicsSystem::OptimizeBroadPhase() +{ + mBroadPhase->Optimize(); +} + +void PhysicsSystem::AddStepListener(PhysicsStepListener *inListener) +{ + lock_guard lock(mStepListenersMutex); + + JPH_ASSERT(find(mStepListeners.begin(), mStepListeners.end(), inListener) == mStepListeners.end()); + mStepListeners.push_back(inListener); +} + +void PhysicsSystem::RemoveStepListener(PhysicsStepListener *inListener) +{ + lock_guard lock(mStepListenersMutex); + + StepListeners::iterator i = find(mStepListeners.begin(), mStepListeners.end(), inListener); + JPH_ASSERT(i != mStepListeners.end()); + *i = mStepListeners.back(); + mStepListeners.pop_back(); +} + +EPhysicsUpdateError PhysicsSystem::Update(float inDeltaTime, int inCollisionSteps, TempAllocator *inTempAllocator, JobSystem *inJobSystem) +{ + JPH_PROFILE_FUNCTION(); + + JPH_DET_LOG("PhysicsSystem::Update: dt: " << inDeltaTime << " steps: " << inCollisionSteps); + + JPH_ASSERT(inCollisionSteps > 0); + JPH_ASSERT(inDeltaTime >= 0.0f); + + // Sync point for the broadphase. This will allow it to do clean up operations without having any mutexes locked yet. + mBroadPhase->FrameSync(); + + // If there are no active bodies or there's no time delta + uint32 num_active_rigid_bodies = mBodyManager.GetNumActiveBodies(EBodyType::RigidBody); + uint32 num_active_soft_bodies = mBodyManager.GetNumActiveBodies(EBodyType::SoftBody); + if ((num_active_rigid_bodies == 0 && num_active_soft_bodies == 0) || inDeltaTime <= 0.0f) + { + mBodyManager.LockAllBodies(); + + // Update broadphase + mBroadPhase->LockModifications(); + BroadPhase::UpdateState update_state = mBroadPhase->UpdatePrepare(); + mBroadPhase->UpdateFinalize(update_state); + mBroadPhase->UnlockModifications(); + + // Call contact removal callbacks from contacts that existed in the previous update + mContactManager.FinalizeContactCacheAndCallContactPointRemovedCallbacks(0, 0); + + mBodyManager.UnlockAllBodies(); + return EPhysicsUpdateError::None; + } + + // Calculate ratio between current and previous frame delta time to scale initial constraint forces + float step_delta_time = inDeltaTime / inCollisionSteps; + float warm_start_impulse_ratio = mPhysicsSettings.mConstraintWarmStart && mPreviousStepDeltaTime > 0.0f? step_delta_time / mPreviousStepDeltaTime : 0.0f; + mPreviousStepDeltaTime = step_delta_time; + + // Create the context used for passing information between jobs + PhysicsUpdateContext context(*inTempAllocator); + context.mPhysicsSystem = this; + context.mJobSystem = inJobSystem; + context.mBarrier = inJobSystem->CreateBarrier(); + context.mIslandBuilder = &mIslandBuilder; + context.mStepDeltaTime = step_delta_time; + context.mWarmStartImpulseRatio = warm_start_impulse_ratio; + context.mSteps.resize(inCollisionSteps); + + // Allocate space for body pairs + JPH_ASSERT(context.mBodyPairs == nullptr); + context.mBodyPairs = static_cast(inTempAllocator->Allocate(sizeof(BodyPair) * mPhysicsSettings.mMaxInFlightBodyPairs)); + + // Lock all bodies for write so that we can freely touch them + mStepListenersMutex.lock(); + mBodyManager.LockAllBodies(); + mBroadPhase->LockModifications(); + + // Get max number of concurrent jobs + int max_concurrency = context.GetMaxConcurrency(); + + // Calculate how many step listener jobs we spawn + int num_step_listener_jobs = mStepListeners.empty()? 0 : max(1, min((int)mStepListeners.size() / mPhysicsSettings.mStepListenersBatchSize / mPhysicsSettings.mStepListenerBatchesPerJob, max_concurrency)); + + // Number of gravity jobs depends on the amount of active bodies. + // Launch max 1 job per batch of active bodies + // Leave 1 thread for update broadphase prepare and 1 for determine active constraints + int num_apply_gravity_jobs = max(1, min(((int)num_active_rigid_bodies + cApplyGravityBatchSize - 1) / cApplyGravityBatchSize, max_concurrency - 2)); + + // Number of determine active constraints jobs to run depends on number of constraints. + // Leave 1 thread for update broadphase prepare and 1 for apply gravity + int num_determine_active_constraints_jobs = max(1, min(((int)mConstraintManager.GetNumConstraints() + cDetermineActiveConstraintsBatchSize - 1) / cDetermineActiveConstraintsBatchSize, max_concurrency - 2)); + + // Number of setup velocity constraints jobs to run depends on number of constraints. + int num_setup_velocity_constraints_jobs = max(1, min(((int)mConstraintManager.GetNumConstraints() + cSetupVelocityConstraintsBatchSize - 1) / cSetupVelocityConstraintsBatchSize, max_concurrency)); + + // Number of find collisions jobs to run depends on number of active bodies. + // Note that when we have more than 1 thread, we always spawn at least 2 find collisions jobs so that the first job can wait for build islands from constraints + // (which may activate additional bodies that need to be processed) while the second job can start processing collision work. + int num_find_collisions_jobs = max(max_concurrency == 1? 1 : 2, min(((int)num_active_rigid_bodies + cActiveBodiesBatchSize - 1) / cActiveBodiesBatchSize, max_concurrency)); + + // Number of integrate velocity jobs depends on number of active bodies. + int num_integrate_velocity_jobs = max(1, min(((int)num_active_rigid_bodies + cIntegrateVelocityBatchSize - 1) / cIntegrateVelocityBatchSize, max_concurrency)); + + { + JPH_PROFILE("Build Jobs"); + + // Iterate over collision steps + for (int step_idx = 0; step_idx < inCollisionSteps; ++step_idx) + { + bool is_first_step = step_idx == 0; + bool is_last_step = step_idx == inCollisionSteps - 1; + + PhysicsUpdateContext::Step &step = context.mSteps[step_idx]; + step.mContext = &context; + step.mIsFirst = is_first_step; + step.mIsLast = is_last_step; + + // Create job to do broadphase finalization + // This job must finish before integrating velocities. Until then the positions will not be updated neither will bodies be added / removed. + step.mUpdateBroadphaseFinalize = inJobSystem->CreateJob("UpdateBroadPhaseFinalize", cColorUpdateBroadPhaseFinalize, [&context, &step]() + { + // Validate that all find collision jobs have stopped + JPH_ASSERT(step.mActiveFindCollisionJobs == 0); + + // Finalize the broadphase update + context.mPhysicsSystem->mBroadPhase->UpdateFinalize(step.mBroadPhaseUpdateState); + + // Signal that it is done + step.mPreIntegrateVelocity.RemoveDependency(); + }, num_find_collisions_jobs + 2); // depends on: find collisions, broadphase prepare update, finish building jobs + + // The immediate jobs below are only immediate for the first step, the all finished job will kick them for the next step + int previous_step_dependency_count = is_first_step? 0 : 1; + + // Start job immediately: Start the prepare broadphase + // Must be done under body lock protection since the order is body locks then broadphase mutex + // If this is turned around the RemoveBody call will hang since it locks in that order + step.mBroadPhasePrepare = inJobSystem->CreateJob("UpdateBroadPhasePrepare", cColorUpdateBroadPhasePrepare, [&context, &step]() + { + // Prepare the broadphase update + step.mBroadPhaseUpdateState = context.mPhysicsSystem->mBroadPhase->UpdatePrepare(); + + // Now the finalize can run (if other dependencies are met too) + step.mUpdateBroadphaseFinalize.RemoveDependency(); + }, previous_step_dependency_count); + + // This job will find all collisions + step.mBodyPairQueues.resize(max_concurrency); + step.mMaxBodyPairsPerQueue = mPhysicsSettings.mMaxInFlightBodyPairs / max_concurrency; + step.mActiveFindCollisionJobs = ~PhysicsUpdateContext::JobMask(0) >> (sizeof(PhysicsUpdateContext::JobMask) * 8 - num_find_collisions_jobs); + step.mFindCollisions.resize(num_find_collisions_jobs); + for (int i = 0; i < num_find_collisions_jobs; ++i) + { + // Build islands from constraints may activate additional bodies, so the first job will wait for this to finish in order to not miss any active bodies + int num_dep_build_islands_from_constraints = i == 0? 1 : 0; + step.mFindCollisions[i] = inJobSystem->CreateJob("FindCollisions", cColorFindCollisions, [&step, i]() + { + step.mContext->mPhysicsSystem->JobFindCollisions(&step, i); + }, num_apply_gravity_jobs + num_determine_active_constraints_jobs + 1 + num_dep_build_islands_from_constraints); // depends on: apply gravity, determine active constraints, finish building jobs, build islands from constraints + } + + if (is_first_step) + { + #ifdef JPH_ENABLE_ASSERTS + // Don't allow write operations to the active bodies list + mBodyManager.SetActiveBodiesLocked(true); + #endif + + // Store the number of active bodies at the start of the step + step.mNumActiveBodiesAtStepStart = mBodyManager.GetNumActiveBodies(EBodyType::RigidBody); + + // Lock all constraints + mConstraintManager.LockAllConstraints(); + + // Allocate memory for storing the active constraints + JPH_ASSERT(context.mActiveConstraints == nullptr); + context.mActiveConstraints = static_cast(inTempAllocator->Allocate(mConstraintManager.GetNumConstraints() * sizeof(Constraint *))); + + // Prepare contact buffer + mContactManager.PrepareConstraintBuffer(&context); + + // Setup island builder + mIslandBuilder.PrepareContactConstraints(mContactManager.GetMaxConstraints(), context.mTempAllocator); + } + + // This job applies gravity to all active bodies + step.mApplyGravity.resize(num_apply_gravity_jobs); + for (int i = 0; i < num_apply_gravity_jobs; ++i) + step.mApplyGravity[i] = inJobSystem->CreateJob("ApplyGravity", cColorApplyGravity, [&context, &step]() + { + context.mPhysicsSystem->JobApplyGravity(&context, &step); + + JobHandle::sRemoveDependencies(step.mFindCollisions); + }, num_step_listener_jobs > 0? num_step_listener_jobs : previous_step_dependency_count); // depends on: step listeners (or previous step if no step listeners) + + // This job will setup velocity constraints for non-collision constraints + step.mSetupVelocityConstraints.resize(num_setup_velocity_constraints_jobs); + for (int i = 0; i < num_setup_velocity_constraints_jobs; ++i) + step.mSetupVelocityConstraints[i] = inJobSystem->CreateJob("SetupVelocityConstraints", cColorSetupVelocityConstraints, [&context, &step]() + { + context.mPhysicsSystem->JobSetupVelocityConstraints(context.mStepDeltaTime, &step); + + JobHandle::sRemoveDependencies(step.mSolveVelocityConstraints); + }, num_determine_active_constraints_jobs + 1); // depends on: determine active constraints, finish building jobs + + // This job will build islands from constraints + step.mBuildIslandsFromConstraints = inJobSystem->CreateJob("BuildIslandsFromConstraints", cColorBuildIslandsFromConstraints, [&context, &step]() + { + context.mPhysicsSystem->JobBuildIslandsFromConstraints(&context, &step); + + step.mFindCollisions[0].RemoveDependency(); // The first collisions job cannot start running until we've finished building islands and activated all bodies + step.mFinalizeIslands.RemoveDependency(); + }, num_determine_active_constraints_jobs + 1); // depends on: determine active constraints, finish building jobs + + // This job determines active constraints + step.mDetermineActiveConstraints.resize(num_determine_active_constraints_jobs); + for (int i = 0; i < num_determine_active_constraints_jobs; ++i) + step.mDetermineActiveConstraints[i] = inJobSystem->CreateJob("DetermineActiveConstraints", cColorDetermineActiveConstraints, [&context, &step]() + { + context.mPhysicsSystem->JobDetermineActiveConstraints(&step); + + step.mBuildIslandsFromConstraints.RemoveDependency(); + + // Kick these jobs last as they will use up all CPU cores leaving no space for the previous job, we prefer setup velocity constraints to finish first so we kick it first + JobHandle::sRemoveDependencies(step.mSetupVelocityConstraints); + JobHandle::sRemoveDependencies(step.mFindCollisions); + }, num_step_listener_jobs > 0? num_step_listener_jobs : previous_step_dependency_count); // depends on: step listeners (or previous step if no step listeners) + + // This job calls the step listeners + step.mStepListeners.resize(num_step_listener_jobs); + for (int i = 0; i < num_step_listener_jobs; ++i) + step.mStepListeners[i] = inJobSystem->CreateJob("StepListeners", cColorStepListeners, [&context, &step]() + { + // Call the step listeners + context.mPhysicsSystem->JobStepListeners(&step); + + // Kick apply gravity and determine active constraint jobs + JobHandle::sRemoveDependencies(step.mApplyGravity); + JobHandle::sRemoveDependencies(step.mDetermineActiveConstraints); + }, previous_step_dependency_count); + + // Unblock the previous step + if (!is_first_step) + context.mSteps[step_idx - 1].mStartNextStep.RemoveDependency(); + + // This job will finalize the simulation islands + step.mFinalizeIslands = inJobSystem->CreateJob("FinalizeIslands", cColorFinalizeIslands, [&context, &step]() + { + // Validate that all find collision jobs have stopped + JPH_ASSERT(step.mActiveFindCollisionJobs == 0); + + context.mPhysicsSystem->JobFinalizeIslands(&context); + + JobHandle::sRemoveDependencies(step.mSolveVelocityConstraints); + step.mBodySetIslandIndex.RemoveDependency(); + }, num_find_collisions_jobs + 2); // depends on: find collisions, build islands from constraints, finish building jobs + + // Unblock previous job + // Note: technically we could release find collisions here but we don't want to because that could make them run before 'setup velocity constraints' which means that job won't have a thread left + step.mBuildIslandsFromConstraints.RemoveDependency(); + + // This job will call the contact removed callbacks + step.mContactRemovedCallbacks = inJobSystem->CreateJob("ContactRemovedCallbacks", cColorContactRemovedCallbacks, [&context, &step]() + { + context.mPhysicsSystem->JobContactRemovedCallbacks(&step); + + if (step.mStartNextStep.IsValid()) + step.mStartNextStep.RemoveDependency(); + }, 1); // depends on the find ccd contacts + + // This job will set the island index on each body (only used for debug drawing purposes) + // It will also delete any bodies that have been destroyed in the last frame + step.mBodySetIslandIndex = inJobSystem->CreateJob("BodySetIslandIndex", cColorBodySetIslandIndex, [&context, &step]() + { + context.mPhysicsSystem->JobBodySetIslandIndex(); + + JobHandle::sRemoveDependencies(step.mSolvePositionConstraints); + }, 2); // depends on: finalize islands, finish building jobs + + // Job to start the next collision step + if (!is_last_step) + { + PhysicsUpdateContext::Step *next_step = &context.mSteps[step_idx + 1]; + step.mStartNextStep = inJobSystem->CreateJob("StartNextStep", cColorStartNextStep, [this, next_step]() + { + #ifdef _DEBUG + // Validate that the cached bounds are correct + mBodyManager.ValidateActiveBodyBounds(); + #endif // _DEBUG + + // Store the number of active bodies at the start of the step + next_step->mNumActiveBodiesAtStepStart = mBodyManager.GetNumActiveBodies(EBodyType::RigidBody); + + // Clear the large island splitter + TempAllocator *temp_allocator = next_step->mContext->mTempAllocator; + mLargeIslandSplitter.Reset(temp_allocator); + + // Clear the island builder + mIslandBuilder.ResetIslands(temp_allocator); + + // Setup island builder + mIslandBuilder.PrepareContactConstraints(mContactManager.GetMaxConstraints(), temp_allocator); + + // Restart the contact manager + mContactManager.RecycleConstraintBuffer(); + + // Kick the jobs of the next step (in the same order as the first step) + next_step->mBroadPhasePrepare.RemoveDependency(); + if (next_step->mStepListeners.empty()) + { + // Kick the gravity and active constraints jobs immediately + JobHandle::sRemoveDependencies(next_step->mApplyGravity); + JobHandle::sRemoveDependencies(next_step->mDetermineActiveConstraints); + } + else + { + // Kick the step listeners job first + JobHandle::sRemoveDependencies(next_step->mStepListeners); + } + }, 3); // depends on: update soft bodies, contact removed callbacks, finish building the previous step + } + + // This job will solve the velocity constraints + step.mSolveVelocityConstraints.resize(max_concurrency); + for (int i = 0; i < max_concurrency; ++i) + step.mSolveVelocityConstraints[i] = inJobSystem->CreateJob("SolveVelocityConstraints", cColorSolveVelocityConstraints, [&context, &step]() + { + context.mPhysicsSystem->JobSolveVelocityConstraints(&context, &step); + + step.mPreIntegrateVelocity.RemoveDependency(); + }, num_setup_velocity_constraints_jobs + 2); // depends on: finalize islands, setup velocity constraints, finish building jobs. + + // We prefer setup velocity constraints to finish first so we kick it first + JobHandle::sRemoveDependencies(step.mSetupVelocityConstraints); + JobHandle::sRemoveDependencies(step.mFindCollisions); + + // Finalize islands is a dependency on find collisions so it can go last + step.mFinalizeIslands.RemoveDependency(); + + // This job will prepare the position update of all active bodies + step.mPreIntegrateVelocity = inJobSystem->CreateJob("PreIntegrateVelocity", cColorPreIntegrateVelocity, [&context, &step]() + { + context.mPhysicsSystem->JobPreIntegrateVelocity(&context, &step); + + JobHandle::sRemoveDependencies(step.mIntegrateVelocity); + }, 2 + max_concurrency); // depends on: broadphase update finalize, solve velocity constraints, finish building jobs. + + // Unblock previous jobs + step.mUpdateBroadphaseFinalize.RemoveDependency(); + JobHandle::sRemoveDependencies(step.mSolveVelocityConstraints); + + // This job will update the positions of all active bodies + step.mIntegrateVelocity.resize(num_integrate_velocity_jobs); + for (int i = 0; i < num_integrate_velocity_jobs; ++i) + step.mIntegrateVelocity[i] = inJobSystem->CreateJob("IntegrateVelocity", cColorIntegrateVelocity, [&context, &step]() + { + context.mPhysicsSystem->JobIntegrateVelocity(&context, &step); + + step.mPostIntegrateVelocity.RemoveDependency(); + }, 2); // depends on: pre integrate velocity, finish building jobs. + + // Unblock previous job + step.mPreIntegrateVelocity.RemoveDependency(); + + // This job will finish the position update of all active bodies + step.mPostIntegrateVelocity = inJobSystem->CreateJob("PostIntegrateVelocity", cColorPostIntegrateVelocity, [&context, &step]() + { + context.mPhysicsSystem->JobPostIntegrateVelocity(&context, &step); + + step.mResolveCCDContacts.RemoveDependency(); + }, num_integrate_velocity_jobs + 1); // depends on: integrate velocity, finish building jobs + + // Unblock previous jobs + JobHandle::sRemoveDependencies(step.mIntegrateVelocity); + + // This job will update the positions and velocities for all bodies that need continuous collision detection + step.mResolveCCDContacts = inJobSystem->CreateJob("ResolveCCDContacts", cColorResolveCCDContacts, [&context, &step]() + { + context.mPhysicsSystem->JobResolveCCDContacts(&context, &step); + + JobHandle::sRemoveDependencies(step.mSolvePositionConstraints); + }, 2); // depends on: integrate velocities, detect ccd contacts (added dynamically), finish building jobs. + + // Unblock previous job + step.mPostIntegrateVelocity.RemoveDependency(); + + // Fixes up drift in positions and updates the broadphase with new body positions + step.mSolvePositionConstraints.resize(max_concurrency); + for (int i = 0; i < max_concurrency; ++i) + step.mSolvePositionConstraints[i] = inJobSystem->CreateJob("SolvePositionConstraints", cColorSolvePositionConstraints, [&context, &step]() + { + context.mPhysicsSystem->JobSolvePositionConstraints(&context, &step); + + // Kick the next step + if (step.mSoftBodyPrepare.IsValid()) + step.mSoftBodyPrepare.RemoveDependency(); + }, 3); // depends on: resolve ccd contacts, body set island index, finish building jobs. + + // Unblock previous jobs. + step.mResolveCCDContacts.RemoveDependency(); + step.mBodySetIslandIndex.RemoveDependency(); + + // The soft body prepare job will create other jobs if needed + step.mSoftBodyPrepare = inJobSystem->CreateJob("SoftBodyPrepare", cColorSoftBodyPrepare, [&context, &step]() + { + context.mPhysicsSystem->JobSoftBodyPrepare(&context, &step); + }, max_concurrency); // depends on: solve position constraints. + + // Unblock previous jobs + JobHandle::sRemoveDependencies(step.mSolvePositionConstraints); + } + } + + // Build the list of jobs to wait for + JobSystem::Barrier *barrier = context.mBarrier; + { + JPH_PROFILE("Build job barrier"); + + StaticArray handles; + for (const PhysicsUpdateContext::Step &step : context.mSteps) + { + if (step.mBroadPhasePrepare.IsValid()) + handles.push_back(step.mBroadPhasePrepare); + for (const JobHandle &h : step.mStepListeners) + handles.push_back(h); + for (const JobHandle &h : step.mDetermineActiveConstraints) + handles.push_back(h); + for (const JobHandle &h : step.mApplyGravity) + handles.push_back(h); + for (const JobHandle &h : step.mFindCollisions) + handles.push_back(h); + if (step.mUpdateBroadphaseFinalize.IsValid()) + handles.push_back(step.mUpdateBroadphaseFinalize); + for (const JobHandle &h : step.mSetupVelocityConstraints) + handles.push_back(h); + handles.push_back(step.mBuildIslandsFromConstraints); + handles.push_back(step.mFinalizeIslands); + handles.push_back(step.mBodySetIslandIndex); + for (const JobHandle &h : step.mSolveVelocityConstraints) + handles.push_back(h); + handles.push_back(step.mPreIntegrateVelocity); + for (const JobHandle &h : step.mIntegrateVelocity) + handles.push_back(h); + handles.push_back(step.mPostIntegrateVelocity); + handles.push_back(step.mResolveCCDContacts); + for (const JobHandle &h : step.mSolvePositionConstraints) + handles.push_back(h); + handles.push_back(step.mContactRemovedCallbacks); + if (step.mSoftBodyPrepare.IsValid()) + handles.push_back(step.mSoftBodyPrepare); + if (step.mStartNextStep.IsValid()) + handles.push_back(step.mStartNextStep); + } + barrier->AddJobs(handles.data(), handles.size()); + } + + // Wait until all jobs finish + // Note we don't just wait for the last job. If we would and another job + // would be scheduled in between there is the possibility of a deadlock. + // The other job could try to e.g. add/remove a body which would try to + // lock a body mutex while this thread has already locked the mutex + inJobSystem->WaitForJobs(barrier); + + // We're done with the barrier for this update + inJobSystem->DestroyBarrier(barrier); + +#ifdef _DEBUG + // Validate that the cached bounds are correct + mBodyManager.ValidateActiveBodyBounds(); +#endif // _DEBUG + + // Clear the large island splitter + mLargeIslandSplitter.Reset(inTempAllocator); + + // Clear the island builder + mIslandBuilder.ResetIslands(inTempAllocator); + + // Clear the contact manager + mContactManager.FinishConstraintBuffer(); + + // Free active constraints + inTempAllocator->Free(context.mActiveConstraints, mConstraintManager.GetNumConstraints() * sizeof(Constraint *)); + context.mActiveConstraints = nullptr; + + // Free body pairs + inTempAllocator->Free(context.mBodyPairs, sizeof(BodyPair) * mPhysicsSettings.mMaxInFlightBodyPairs); + context.mBodyPairs = nullptr; + + // Unlock the broadphase + mBroadPhase->UnlockModifications(); + + // Unlock all constraints + mConstraintManager.UnlockAllConstraints(); + +#ifdef JPH_ENABLE_ASSERTS + // Allow write operations to the active bodies list + mBodyManager.SetActiveBodiesLocked(false); +#endif + + // Unlock all bodies + mBodyManager.UnlockAllBodies(); + + // Unlock step listeners + mStepListenersMutex.unlock(); + + // Return any errors + EPhysicsUpdateError errors = static_cast(context.mErrors.load(memory_order_acquire)); + JPH_ASSERT(errors == EPhysicsUpdateError::None, "An error occurred during the physics update, see EPhysicsUpdateError for more information"); + return errors; +} + +void PhysicsSystem::JobStepListeners(PhysicsUpdateContext::Step *ioStep) +{ +#ifdef JPH_ENABLE_ASSERTS + // Read positions (broadphase updates concurrently so we can't write), read/write velocities + BodyAccess::Grant grant(BodyAccess::EAccess::ReadWrite, BodyAccess::EAccess::Read); + + // Can activate bodies only (we cache the amount of active bodies at the beginning of the step in mNumActiveBodiesAtStepStart so we cannot deactivate here) + BodyManager::GrantActiveBodiesAccess grant_active(true, false); +#endif + + float step_time = ioStep->mContext->mStepDeltaTime; + uint32 batch_size = mPhysicsSettings.mStepListenersBatchSize; + for (;;) + { + // Get the start of a new batch + uint32 batch = ioStep->mStepListenerReadIdx.fetch_add(batch_size); + if (batch >= mStepListeners.size()) + break; + + // Call the listeners + for (uint32 i = batch, i_end = min((uint32)mStepListeners.size(), batch + batch_size); i < i_end; ++i) + mStepListeners[i]->OnStep(step_time, *this); + } +} + +void PhysicsSystem::JobDetermineActiveConstraints(PhysicsUpdateContext::Step *ioStep) const +{ +#ifdef JPH_ENABLE_ASSERTS + // No body access + BodyAccess::Grant grant(BodyAccess::EAccess::None, BodyAccess::EAccess::None); +#endif + + uint32 num_constraints = mConstraintManager.GetNumConstraints(); + uint32 num_active_constraints; + Constraint **active_constraints = (Constraint **)JPH_STACK_ALLOC(cDetermineActiveConstraintsBatchSize * sizeof(Constraint *)); + + for (;;) + { + // Atomically fetch a batch of constraints + uint32 constraint_idx = ioStep->mDetermineActiveConstraintReadIdx.fetch_add(cDetermineActiveConstraintsBatchSize); + if (constraint_idx >= num_constraints) + break; + + // Calculate the end of the batch + uint32 constraint_idx_end = min(num_constraints, constraint_idx + cDetermineActiveConstraintsBatchSize); + + // Store the active constraints at the start of the step (bodies get activated during the step which in turn may activate constraints leading to an inconsistent shapshot) + mConstraintManager.GetActiveConstraints(constraint_idx, constraint_idx_end, active_constraints, num_active_constraints); + + // Copy the block of active constraints to the global list of active constraints + if (num_active_constraints > 0) + { + uint32 active_constraint_idx = ioStep->mNumActiveConstraints.fetch_add(num_active_constraints); + memcpy(ioStep->mContext->mActiveConstraints + active_constraint_idx, active_constraints, num_active_constraints * sizeof(Constraint *)); + } + } +} + +void PhysicsSystem::JobApplyGravity(const PhysicsUpdateContext *ioContext, PhysicsUpdateContext::Step *ioStep) +{ +#ifdef JPH_ENABLE_ASSERTS + // We update velocities and need the rotation to do so + BodyAccess::Grant grant(BodyAccess::EAccess::ReadWrite, BodyAccess::EAccess::Read); +#endif + + // Get list of active bodies that we had at the start of the physics update. + // Any body that is activated as part of the simulation step does not receive gravity this frame. + // Note that bodies may be activated during this job but not deactivated, this means that only elements + // will be added to the array. Since the array is made to not reallocate, this is a safe operation. + const BodyID *active_bodies = mBodyManager.GetActiveBodiesUnsafe(EBodyType::RigidBody); + uint32 num_active_bodies_at_step_start = ioStep->mNumActiveBodiesAtStepStart; + + // Fetch delta time once outside the loop + float delta_time = ioContext->mStepDeltaTime; + + // Update velocities from forces + for (;;) + { + // Atomically fetch a batch of bodies + uint32 active_body_idx = ioStep->mApplyGravityReadIdx.fetch_add(cApplyGravityBatchSize); + if (active_body_idx >= num_active_bodies_at_step_start) + break; + + // Calculate the end of the batch + uint32 active_body_idx_end = min(num_active_bodies_at_step_start, active_body_idx + cApplyGravityBatchSize); + + // Process the batch + while (active_body_idx < active_body_idx_end) + { + Body &body = mBodyManager.GetBody(active_bodies[active_body_idx]); + if (body.IsDynamic()) + { + MotionProperties *mp = body.GetMotionProperties(); + Quat rotation = body.GetRotation(); + + if (body.GetApplyGyroscopicForce()) + mp->ApplyGyroscopicForceInternal(rotation, delta_time); + + mp->ApplyForceTorqueAndDragInternal(rotation, mGravity, delta_time); + } + active_body_idx++; + } + } +} + +void PhysicsSystem::JobSetupVelocityConstraints(float inDeltaTime, PhysicsUpdateContext::Step *ioStep) const +{ +#ifdef JPH_ENABLE_ASSERTS + // We only read positions + BodyAccess::Grant grant(BodyAccess::EAccess::None, BodyAccess::EAccess::Read); +#endif + + uint32 num_constraints = ioStep->mNumActiveConstraints; + + for (;;) + { + // Atomically fetch a batch of constraints + uint32 constraint_idx = ioStep->mSetupVelocityConstraintsReadIdx.fetch_add(cSetupVelocityConstraintsBatchSize); + if (constraint_idx >= num_constraints) + break; + + ConstraintManager::sSetupVelocityConstraints(ioStep->mContext->mActiveConstraints + constraint_idx, min(cSetupVelocityConstraintsBatchSize, num_constraints - constraint_idx), inDeltaTime); + } +} + +void PhysicsSystem::JobBuildIslandsFromConstraints(PhysicsUpdateContext *ioContext, PhysicsUpdateContext::Step *ioStep) +{ +#ifdef JPH_ENABLE_ASSERTS + // We read constraints and positions + BodyAccess::Grant grant(BodyAccess::EAccess::None, BodyAccess::EAccess::Read); + + // Can only activate bodies + BodyManager::GrantActiveBodiesAccess grant_active(true, false); +#endif + + // Prepare the island builder + mIslandBuilder.PrepareNonContactConstraints(ioStep->mNumActiveConstraints, ioContext->mTempAllocator); + + // Build the islands + ConstraintManager::sBuildIslands(ioStep->mContext->mActiveConstraints, ioStep->mNumActiveConstraints, mIslandBuilder, mBodyManager); +} + +void PhysicsSystem::TrySpawnJobFindCollisions(PhysicsUpdateContext::Step *ioStep) const +{ + // Get how many jobs we can spawn and check if we can spawn more + uint max_jobs = ioStep->mBodyPairQueues.size(); + if (CountBits(ioStep->mActiveFindCollisionJobs) >= max_jobs) + return; + + // Count how many body pairs we have waiting + uint32 num_body_pairs = 0; + for (const PhysicsUpdateContext::BodyPairQueue &queue : ioStep->mBodyPairQueues) + num_body_pairs += queue.mWriteIdx - queue.mReadIdx; + + // Count how many active bodies we have waiting + uint32 num_active_bodies = mBodyManager.GetNumActiveBodies(EBodyType::RigidBody) - ioStep->mActiveBodyReadIdx; + + // Calculate how many jobs we would like + uint desired_num_jobs = min((num_body_pairs + cNarrowPhaseBatchSize - 1) / cNarrowPhaseBatchSize + (num_active_bodies + cActiveBodiesBatchSize - 1) / cActiveBodiesBatchSize, max_jobs); + + for (;;) + { + // Get the bit mask of active jobs and see if we can spawn more + PhysicsUpdateContext::JobMask current_active_jobs = ioStep->mActiveFindCollisionJobs; + if (CountBits(current_active_jobs) >= desired_num_jobs) + break; + + // Loop through all possible job indices + for (uint job_index = 0; job_index < max_jobs; ++job_index) + { + // Test if it has been started + PhysicsUpdateContext::JobMask job_mask = PhysicsUpdateContext::JobMask(1) << job_index; + if ((current_active_jobs & job_mask) == 0) + { + // Try to claim the job index + PhysicsUpdateContext::JobMask prev_value = ioStep->mActiveFindCollisionJobs.fetch_or(job_mask); + if ((prev_value & job_mask) == 0) + { + // Add dependencies from the find collisions job to the next jobs + ioStep->mUpdateBroadphaseFinalize.AddDependency(); + ioStep->mFinalizeIslands.AddDependency(); + + // Start the job + JobHandle job = ioStep->mContext->mJobSystem->CreateJob("FindCollisions", cColorFindCollisions, [step = ioStep, job_index]() + { + step->mContext->mPhysicsSystem->JobFindCollisions(step, job_index); + }); + + // Add the job to the job barrier so the main updating thread can execute the job too + ioStep->mContext->mBarrier->AddJob(job); + + // Spawn only 1 extra job at a time + return; + } + } + } + } +} + +static void sFinalizeContactAllocator(PhysicsUpdateContext::Step &ioStep, const ContactConstraintManager::ContactAllocator &inAllocator) +{ + // Atomically accumulate the number of found manifolds and body pairs + ioStep.mNumBodyPairs.fetch_add(inAllocator.mNumBodyPairs, memory_order_relaxed); + ioStep.mNumManifolds.fetch_add(inAllocator.mNumManifolds, memory_order_relaxed); + + // Combine update errors + ioStep.mContext->mErrors.fetch_or((uint32)inAllocator.mErrors, memory_order_relaxed); +} + +void PhysicsSystem::JobFindCollisions(PhysicsUpdateContext::Step *ioStep, int inJobIndex) +{ +#ifdef JPH_ENABLE_ASSERTS + // We read positions and read velocities (for elastic collisions) + BodyAccess::Grant grant(BodyAccess::EAccess::Read, BodyAccess::EAccess::Read); + + // Can only activate bodies + BodyManager::GrantActiveBodiesAccess grant_active(true, false); +#endif + + // Allocation context for allocating new contact points + ContactAllocator contact_allocator(mContactManager.GetContactAllocator()); + + // Determine initial queue to read pairs from if no broadphase work can be done + // (always start looking at results from the next job) + int read_queue_idx = (inJobIndex + 1) % ioStep->mBodyPairQueues.size(); + + for (;;) + { + // Check if there are active bodies to be processed + uint32 active_bodies_read_idx = ioStep->mActiveBodyReadIdx; + uint32 num_active_bodies = mBodyManager.GetNumActiveBodies(EBodyType::RigidBody); + if (active_bodies_read_idx < num_active_bodies) + { + // Take a batch of active bodies + uint32 active_bodies_read_idx_end = min(num_active_bodies, active_bodies_read_idx + cActiveBodiesBatchSize); + if (ioStep->mActiveBodyReadIdx.compare_exchange_strong(active_bodies_read_idx, active_bodies_read_idx_end)) + { + // Callback when a new body pair is found + class MyBodyPairCallback : public BodyPairCollector + { + public: + // Constructor + MyBodyPairCallback(PhysicsUpdateContext::Step *inStep, ContactAllocator &ioContactAllocator, int inJobIndex) : + mStep(inStep), + mContactAllocator(ioContactAllocator), + mJobIndex(inJobIndex) + { + } + + // Callback function when a body pair is found + virtual void AddHit(const BodyPair &inPair) override + { + // Check if we have space in our write queue + PhysicsUpdateContext::BodyPairQueue &queue = mStep->mBodyPairQueues[mJobIndex]; + uint32 body_pairs_in_queue = queue.mWriteIdx - queue.mReadIdx; + if (body_pairs_in_queue >= mStep->mMaxBodyPairsPerQueue) + { + // Buffer full, process the pair now + mStep->mContext->mPhysicsSystem->ProcessBodyPair(mContactAllocator, inPair); + } + else + { + // Store the pair in our own queue + mStep->mContext->mBodyPairs[mJobIndex * mStep->mMaxBodyPairsPerQueue + queue.mWriteIdx % mStep->mMaxBodyPairsPerQueue] = inPair; + ++queue.mWriteIdx; + } + } + + private: + PhysicsUpdateContext::Step * mStep; + ContactAllocator & mContactAllocator; + int mJobIndex; + }; + MyBodyPairCallback add_pair(ioStep, contact_allocator, inJobIndex); + + // Copy active bodies to temporary array, broadphase will reorder them + uint32 batch_size = active_bodies_read_idx_end - active_bodies_read_idx; + BodyID *active_bodies = (BodyID *)JPH_STACK_ALLOC(batch_size * sizeof(BodyID)); + memcpy(active_bodies, mBodyManager.GetActiveBodiesUnsafe(EBodyType::RigidBody) + active_bodies_read_idx, batch_size * sizeof(BodyID)); + + // Find pairs in the broadphase + mBroadPhase->FindCollidingPairs(active_bodies, batch_size, mPhysicsSettings.mSpeculativeContactDistance, *mObjectVsBroadPhaseLayerFilter, *mObjectLayerPairFilter, add_pair); + + // Check if we have enough pairs in the buffer to start a new job + const PhysicsUpdateContext::BodyPairQueue &queue = ioStep->mBodyPairQueues[inJobIndex]; + uint32 body_pairs_in_queue = queue.mWriteIdx - queue.mReadIdx; + if (body_pairs_in_queue >= cNarrowPhaseBatchSize) + TrySpawnJobFindCollisions(ioStep); + } + } + else + { + // Lockless loop to get the next body pair from the pairs buffer + const PhysicsUpdateContext *context = ioStep->mContext; + int first_read_queue_idx = read_queue_idx; + for (;;) + { + PhysicsUpdateContext::BodyPairQueue &queue = ioStep->mBodyPairQueues[read_queue_idx]; + + // Get the next pair to process + uint32 pair_idx = queue.mReadIdx; + + // If the pair hasn't been written yet + if (pair_idx >= queue.mWriteIdx) + { + // Go to the next queue + read_queue_idx = (read_queue_idx + 1) % ioStep->mBodyPairQueues.size(); + + // If we're back at the first queue, we've looked at all of them and found nothing + if (read_queue_idx == first_read_queue_idx) + { + // Collect information from the contact allocator and accumulate it in the step. + sFinalizeContactAllocator(*ioStep, contact_allocator); + + // Mark this job as inactive + ioStep->mActiveFindCollisionJobs.fetch_and(~PhysicsUpdateContext::JobMask(1 << inJobIndex)); + + // Trigger the next jobs + ioStep->mUpdateBroadphaseFinalize.RemoveDependency(); + ioStep->mFinalizeIslands.RemoveDependency(); + return; + } + + // Try again reading from the next queue + continue; + } + + // Copy the body pair out of the buffer + const BodyPair bp = context->mBodyPairs[read_queue_idx * ioStep->mMaxBodyPairsPerQueue + pair_idx % ioStep->mMaxBodyPairsPerQueue]; + + // Mark this pair as taken + if (queue.mReadIdx.compare_exchange_strong(pair_idx, pair_idx + 1)) + { + // Process the actual body pair + ProcessBodyPair(contact_allocator, bp); + break; + } + } + } + } +} + +void PhysicsSystem::ProcessBodyPair(ContactAllocator &ioContactAllocator, const BodyPair &inBodyPair) +{ + JPH_PROFILE_FUNCTION(); + + // Fetch body pair + Body *body1 = &mBodyManager.GetBody(inBodyPair.mBodyA); + Body *body2 = &mBodyManager.GetBody(inBodyPair.mBodyB); + JPH_ASSERT(body1->IsActive()); + + JPH_DET_LOG("ProcessBodyPair: id1: " << inBodyPair.mBodyA << " id2: " << inBodyPair.mBodyB << " p1: " << body1->GetCenterOfMassPosition() << " p2: " << body2->GetCenterOfMassPosition() << " r1: " << body1->GetRotation() << " r2: " << body2->GetRotation()); + + // Check for soft bodies + if (body2->IsSoftBody()) + { + // If the 2nd body is a soft body and not active, we activate it now + if (!body2->IsActive()) + mBodyManager.ActivateBodies(&inBodyPair.mBodyB, 1); + + // Soft body processing is done later in the pipeline + return; + } + + // Ensure that body1 has the higher motion type (i.e. dynamic trumps kinematic), this ensures that we do the collision detection in the space of a moving body, + // which avoids accuracy problems when testing a very large static object against a small dynamic object + // Ensure that body1 id < body2 id when motion types are the same. + if (body1->GetMotionType() < body2->GetMotionType() + || (body1->GetMotionType() == body2->GetMotionType() && inBodyPair.mBodyB < inBodyPair.mBodyA)) + swap(body1, body2); + + // Check if the contact points from the previous frame are reusable and if so copy them + bool pair_handled = false, constraint_created = false; + if (mPhysicsSettings.mUseBodyPairContactCache && !(body1->IsCollisionCacheInvalid() || body2->IsCollisionCacheInvalid())) + mContactManager.GetContactsFromCache(ioContactAllocator, *body1, *body2, pair_handled, constraint_created); + + // If the cache hasn't handled this body pair do actual collision detection + if (!pair_handled) + { + // Create entry in the cache for this body pair + // Needs to happen irrespective if we found a collision or not (we want to remember that no collision was found too) + ContactConstraintManager::BodyPairHandle body_pair_handle = mContactManager.AddBodyPair(ioContactAllocator, *body1, *body2); + if (body_pair_handle == nullptr) + return; // Out of cache space + + // Create the query settings + CollideShapeSettings settings; + settings.mCollectFacesMode = ECollectFacesMode::CollectFaces; + settings.mActiveEdgeMode = mPhysicsSettings.mCheckActiveEdges? EActiveEdgeMode::CollideOnlyWithActive : EActiveEdgeMode::CollideWithAll; + settings.mMaxSeparationDistance = body1->IsSensor() || body2->IsSensor()? 0.0f : mPhysicsSettings.mSpeculativeContactDistance; + settings.mActiveEdgeMovementDirection = body1->GetLinearVelocity() - body2->GetLinearVelocity(); + + // Get transforms relative to body1 + RVec3 offset = body1->GetCenterOfMassPosition(); + Mat44 transform1 = Mat44::sRotation(body1->GetRotation()); + Mat44 transform2 = body2->GetCenterOfMassTransform().PostTranslated(-offset).ToMat44(); + + if (mPhysicsSettings.mUseManifoldReduction // Check global flag + && body1->GetUseManifoldReductionWithBody(*body2)) // Check body flag + { + // Version WITH contact manifold reduction + + class MyManifold : public ContactManifold + { + public: + Vec3 mFirstWorldSpaceNormal; + }; + + // A temporary structure that allows us to keep track of the all manifolds between this body pair + using Manifolds = StaticArray; + + // Create collector + class ReductionCollideShapeCollector : public CollideShapeCollector + { + public: + ReductionCollideShapeCollector(PhysicsSystem *inSystem, const Body *inBody1, const Body *inBody2) : + mSystem(inSystem), + mBody1(inBody1), + mBody2(inBody2) + { + } + + virtual void AddHit(const CollideShapeResult &inResult) override + { + // The first body should be the one with the highest motion type + JPH_ASSERT(mBody1->GetMotionType() >= mBody2->GetMotionType()); + JPH_ASSERT(!ShouldEarlyOut()); + + // Test if we want to accept this hit + if (mValidateBodyPair) + { + switch (mSystem->mContactManager.ValidateContactPoint(*mBody1, *mBody2, mBody1->GetCenterOfMassPosition(), inResult)) + { + case ValidateResult::AcceptContact: + // We're just accepting this one, nothing to do + break; + + case ValidateResult::AcceptAllContactsForThisBodyPair: + // Accept and stop calling the validate callback + mValidateBodyPair = false; + break; + + case ValidateResult::RejectContact: + // Skip this contact + return; + + case ValidateResult::RejectAllContactsForThisBodyPair: + // Skip this and early out + ForceEarlyOut(); + return; + } + } + + // Calculate normal + Vec3 world_space_normal = inResult.mPenetrationAxis.Normalized(); + + // Check if we can add it to an existing manifold + Manifolds::iterator manifold; + float contact_normal_cos_max_delta_rot = mSystem->mPhysicsSettings.mContactNormalCosMaxDeltaRotation; + for (manifold = mManifolds.begin(); manifold != mManifolds.end(); ++manifold) + if (world_space_normal.Dot(manifold->mFirstWorldSpaceNormal) >= contact_normal_cos_max_delta_rot) + { + // Update average normal + manifold->mWorldSpaceNormal += world_space_normal; + manifold->mPenetrationDepth = max(manifold->mPenetrationDepth, inResult.mPenetrationDepth); + break; + } + if (manifold == mManifolds.end()) + { + // Check if array is full + if (mManifolds.size() == mManifolds.capacity()) + { + // Full, find manifold with least amount of penetration + manifold = mManifolds.begin(); + for (Manifolds::iterator m = mManifolds.begin() + 1; m < mManifolds.end(); ++m) + if (m->mPenetrationDepth < manifold->mPenetrationDepth) + manifold = m; + + // If this contacts penetration is smaller than the smallest manifold, we skip this contact + if (inResult.mPenetrationDepth < manifold->mPenetrationDepth) + return; + + // Replace the manifold + *manifold = { { mBody1->GetCenterOfMassPosition(), world_space_normal, inResult.mPenetrationDepth, inResult.mSubShapeID1, inResult.mSubShapeID2, { }, { } }, world_space_normal }; + } + else + { + // Not full, create new manifold + mManifolds.push_back({ { mBody1->GetCenterOfMassPosition(), world_space_normal, inResult.mPenetrationDepth, inResult.mSubShapeID1, inResult.mSubShapeID2, { }, { } }, world_space_normal }); + manifold = mManifolds.end() - 1; + } + } + + // Determine contact points + const PhysicsSettings &settings = mSystem->mPhysicsSettings; + ManifoldBetweenTwoFaces(inResult.mContactPointOn1, inResult.mContactPointOn2, inResult.mPenetrationAxis, Square(settings.mSpeculativeContactDistance) + settings.mManifoldToleranceSq, inResult.mShape1Face, inResult.mShape2Face, manifold->mRelativeContactPointsOn1, manifold->mRelativeContactPointsOn2 JPH_IF_DEBUG_RENDERER(, mBody1->GetCenterOfMassPosition())); + + // Prune if we have more than 32 points (this means we could run out of space in the next iteration) + if (manifold->mRelativeContactPointsOn1.size() > 32) + PruneContactPoints(manifold->mFirstWorldSpaceNormal, manifold->mRelativeContactPointsOn1, manifold->mRelativeContactPointsOn2 JPH_IF_DEBUG_RENDERER(, manifold->mBaseOffset)); + } + + PhysicsSystem * mSystem; + const Body * mBody1; + const Body * mBody2; + bool mValidateBodyPair = true; + Manifolds mManifolds; + }; + ReductionCollideShapeCollector collector(this, body1, body2); + + // Perform collision detection between the two shapes + SubShapeIDCreator part1, part2; + auto f = body1->GetEnhancedInternalEdgeRemovalWithBody(*body2)? InternalEdgeRemovingCollector::sCollideShapeVsShape : CollisionDispatch::sCollideShapeVsShape; + f(body1->GetShape(), body2->GetShape(), Vec3::sReplicate(1.0f), Vec3::sReplicate(1.0f), transform1, transform2, part1, part2, settings, collector, { }); + + // Add the contacts + for (ContactManifold &manifold : collector.mManifolds) + { + // Normalize the normal (is a sum of all normals from merged manifolds) + manifold.mWorldSpaceNormal = manifold.mWorldSpaceNormal.Normalized(); + + // If we still have too many points, prune them now + if (manifold.mRelativeContactPointsOn1.size() > 4) + PruneContactPoints(manifold.mWorldSpaceNormal, manifold.mRelativeContactPointsOn1, manifold.mRelativeContactPointsOn2 JPH_IF_DEBUG_RENDERER(, manifold.mBaseOffset)); + + // Actually add the contact points to the manager + constraint_created |= mContactManager.AddContactConstraint(ioContactAllocator, body_pair_handle, *body1, *body2, manifold); + } + } + else + { + // Version WITHOUT contact manifold reduction + + // Create collector + class NonReductionCollideShapeCollector : public CollideShapeCollector + { + public: + NonReductionCollideShapeCollector(PhysicsSystem *inSystem, ContactAllocator &ioContactAllocator, Body *inBody1, Body *inBody2, const ContactConstraintManager::BodyPairHandle &inPairHandle) : + mSystem(inSystem), + mContactAllocator(ioContactAllocator), + mBody1(inBody1), + mBody2(inBody2), + mBodyPairHandle(inPairHandle) + { + } + + virtual void AddHit(const CollideShapeResult &inResult) override + { + // The first body should be the one with the highest motion type + JPH_ASSERT(mBody1->GetMotionType() >= mBody2->GetMotionType()); + JPH_ASSERT(!ShouldEarlyOut()); + + // Test if we want to accept this hit + if (mValidateBodyPair) + { + switch (mSystem->mContactManager.ValidateContactPoint(*mBody1, *mBody2, mBody1->GetCenterOfMassPosition(), inResult)) + { + case ValidateResult::AcceptContact: + // We're just accepting this one, nothing to do + break; + + case ValidateResult::AcceptAllContactsForThisBodyPair: + // Accept and stop calling the validate callback + mValidateBodyPair = false; + break; + + case ValidateResult::RejectContact: + // Skip this contact + return; + + case ValidateResult::RejectAllContactsForThisBodyPair: + // Skip this and early out + ForceEarlyOut(); + return; + } + } + + // Determine contact points + ContactManifold manifold; + manifold.mBaseOffset = mBody1->GetCenterOfMassPosition(); + const PhysicsSettings &settings = mSystem->mPhysicsSettings; + ManifoldBetweenTwoFaces(inResult.mContactPointOn1, inResult.mContactPointOn2, inResult.mPenetrationAxis, Square(settings.mSpeculativeContactDistance) + settings.mManifoldToleranceSq, inResult.mShape1Face, inResult.mShape2Face, manifold.mRelativeContactPointsOn1, manifold.mRelativeContactPointsOn2 JPH_IF_DEBUG_RENDERER(, manifold.mBaseOffset)); + + // Calculate normal + manifold.mWorldSpaceNormal = inResult.mPenetrationAxis.Normalized(); + + // Store penetration depth + manifold.mPenetrationDepth = inResult.mPenetrationDepth; + + // Prune if we have more than 4 points + if (manifold.mRelativeContactPointsOn1.size() > 4) + PruneContactPoints(manifold.mWorldSpaceNormal, manifold.mRelativeContactPointsOn1, manifold.mRelativeContactPointsOn2 JPH_IF_DEBUG_RENDERER(, manifold.mBaseOffset)); + + // Set other properties + manifold.mSubShapeID1 = inResult.mSubShapeID1; + manifold.mSubShapeID2 = inResult.mSubShapeID2; + + // Actually add the contact points to the manager + mConstraintCreated |= mSystem->mContactManager.AddContactConstraint(mContactAllocator, mBodyPairHandle, *mBody1, *mBody2, manifold); + } + + PhysicsSystem * mSystem; + ContactAllocator & mContactAllocator; + Body * mBody1; + Body * mBody2; + ContactConstraintManager::BodyPairHandle mBodyPairHandle; + bool mValidateBodyPair = true; + bool mConstraintCreated = false; + }; + NonReductionCollideShapeCollector collector(this, ioContactAllocator, body1, body2, body_pair_handle); + + // Perform collision detection between the two shapes + SubShapeIDCreator part1, part2; + auto f = body1->GetEnhancedInternalEdgeRemovalWithBody(*body2)? InternalEdgeRemovingCollector::sCollideShapeVsShape : CollisionDispatch::sCollideShapeVsShape; + f(body1->GetShape(), body2->GetShape(), Vec3::sReplicate(1.0f), Vec3::sReplicate(1.0f), transform1, transform2, part1, part2, settings, collector, { }); + + constraint_created = collector.mConstraintCreated; + } + } + + // If a contact constraint was created, we need to do some extra work + if (constraint_created) + { + // Wake up sleeping bodies + BodyID body_ids[2]; + int num_bodies = 0; + if (body1->IsDynamic() && !body1->IsActive()) + body_ids[num_bodies++] = body1->GetID(); + if (body2->IsDynamic() && !body2->IsActive()) + body_ids[num_bodies++] = body2->GetID(); + if (num_bodies > 0) + mBodyManager.ActivateBodies(body_ids, num_bodies); + + // Link the two bodies + mIslandBuilder.LinkBodies(body1->GetIndexInActiveBodiesInternal(), body2->GetIndexInActiveBodiesInternal()); + } +} + +void PhysicsSystem::JobFinalizeIslands(PhysicsUpdateContext *ioContext) +{ +#ifdef JPH_ENABLE_ASSERTS + // We only touch island data + BodyAccess::Grant grant(BodyAccess::EAccess::None, BodyAccess::EAccess::None); +#endif + + // Finish collecting the islands, at this point the active body list doesn't change so it's safe to access + mIslandBuilder.Finalize(mBodyManager.GetActiveBodiesUnsafe(EBodyType::RigidBody), mBodyManager.GetNumActiveBodies(EBodyType::RigidBody), mContactManager.GetNumConstraints(), ioContext->mTempAllocator); + + // Prepare the large island splitter + if (mPhysicsSettings.mUseLargeIslandSplitter) + mLargeIslandSplitter.Prepare(mIslandBuilder, mBodyManager.GetNumActiveBodies(EBodyType::RigidBody), ioContext->mTempAllocator); +} + +void PhysicsSystem::JobBodySetIslandIndex() +{ +#ifdef JPH_ENABLE_ASSERTS + // We only touch island data + BodyAccess::Grant grant(BodyAccess::EAccess::None, BodyAccess::EAccess::None); +#endif + + // Loop through the result and tag all bodies with an island index + for (uint32 island_idx = 0, n = mIslandBuilder.GetNumIslands(); island_idx < n; ++island_idx) + { + BodyID *body_start, *body_end; + mIslandBuilder.GetBodiesInIsland(island_idx, body_start, body_end); + for (const BodyID *body = body_start; body < body_end; ++body) + mBodyManager.GetBody(*body).GetMotionProperties()->SetIslandIndexInternal(island_idx); + } +} + +JPH_SUPPRESS_WARNING_PUSH +JPH_CLANG_SUPPRESS_WARNING("-Wundefined-func-template") // ConstraintManager::sWarmStartVelocityConstraints / ContactConstraintManager::WarmStartVelocityConstraints is instantiated in the cpp file + +void PhysicsSystem::JobSolveVelocityConstraints(PhysicsUpdateContext *ioContext, PhysicsUpdateContext::Step *ioStep) +{ +#ifdef JPH_ENABLE_ASSERTS + // We update velocities and need to read positions to do so + BodyAccess::Grant grant(BodyAccess::EAccess::ReadWrite, BodyAccess::EAccess::Read); +#endif + + float delta_time = ioContext->mStepDeltaTime; + Constraint **active_constraints = ioContext->mActiveConstraints; + + // Only the first step to correct for the delta time difference in the previous update + float warm_start_impulse_ratio = ioStep->mIsFirst? ioContext->mWarmStartImpulseRatio : 1.0f; + + bool check_islands = true, check_split_islands = mPhysicsSettings.mUseLargeIslandSplitter; + do + { + // First try to get work from large islands + if (check_split_islands) + { + bool first_iteration; + uint split_island_index; + uint32 *constraints_begin, *constraints_end, *contacts_begin, *contacts_end; + switch (mLargeIslandSplitter.FetchNextBatch(split_island_index, constraints_begin, constraints_end, contacts_begin, contacts_end, first_iteration)) + { + case LargeIslandSplitter::EStatus::BatchRetrieved: + { + if (first_iteration) + { + // Iteration 0 is used to warm start the batch (we added 1 to the number of iterations in LargeIslandSplitter::SplitIsland) + DummyCalculateSolverSteps dummy; + ConstraintManager::sWarmStartVelocityConstraints(active_constraints, constraints_begin, constraints_end, warm_start_impulse_ratio, dummy); + mContactManager.WarmStartVelocityConstraints(contacts_begin, contacts_end, warm_start_impulse_ratio, dummy); + } + else + { + // Solve velocity constraints + ConstraintManager::sSolveVelocityConstraints(active_constraints, constraints_begin, constraints_end, delta_time); + mContactManager.SolveVelocityConstraints(contacts_begin, contacts_end); + } + + // Mark the batch as processed + bool last_iteration, final_batch; + mLargeIslandSplitter.MarkBatchProcessed(split_island_index, constraints_begin, constraints_end, contacts_begin, contacts_end, last_iteration, final_batch); + + // Save back the lambdas in the contact cache for the warm start of the next physics update + if (last_iteration) + mContactManager.StoreAppliedImpulses(contacts_begin, contacts_end); + + // We processed work, loop again + continue; + } + case LargeIslandSplitter::EStatus::WaitingForBatch: + break; + case LargeIslandSplitter::EStatus::AllBatchesDone: + check_split_islands = false; + break; + } + } + + // If that didn't succeed try to process an island + if (check_islands) + { + // Next island + uint32 island_idx = ioStep->mSolveVelocityConstraintsNextIsland++; + if (island_idx >= mIslandBuilder.GetNumIslands()) + { + // We processed all islands, stop checking islands + check_islands = false; + continue; + } + + JPH_PROFILE("Island"); + + // Get iterators for this island + uint32 *constraints_begin, *constraints_end, *contacts_begin, *contacts_end; + bool has_constraints = mIslandBuilder.GetConstraintsInIsland(island_idx, constraints_begin, constraints_end); + bool has_contacts = mIslandBuilder.GetContactsInIsland(island_idx, contacts_begin, contacts_end); + + // If we don't have any contacts or constraints, we know that none of the following islands have any contacts or constraints + // (because they're sorted by most constraints first). This means we're done. + if (!has_contacts && !has_constraints) + { + #ifdef JPH_ENABLE_ASSERTS + // Validate our assumption that the next islands don't have any constraints or contacts + for (; island_idx < mIslandBuilder.GetNumIslands(); ++island_idx) + { + JPH_ASSERT(!mIslandBuilder.GetConstraintsInIsland(island_idx, constraints_begin, constraints_end)); + JPH_ASSERT(!mIslandBuilder.GetContactsInIsland(island_idx, contacts_begin, contacts_end)); + } + #endif // JPH_ENABLE_ASSERTS + + check_islands = false; + continue; + } + + // Sorting is costly but needed for a deterministic simulation, allow the user to turn this off + if (mPhysicsSettings.mDeterministicSimulation) + { + // Sort constraints to give a deterministic simulation + ConstraintManager::sSortConstraints(active_constraints, constraints_begin, constraints_end); + + // Sort contacts to give a deterministic simulation + mContactManager.SortContacts(contacts_begin, contacts_end); + } + + // Split up large islands + CalculateSolverSteps steps_calculator(mPhysicsSettings); + if (mPhysicsSettings.mUseLargeIslandSplitter + && mLargeIslandSplitter.SplitIsland(island_idx, mIslandBuilder, mBodyManager, mContactManager, active_constraints, steps_calculator)) + continue; // Loop again to try to fetch the newly split island + + // We didn't create a split, just run the solver now for this entire island. Begin by warm starting. + ConstraintManager::sWarmStartVelocityConstraints(active_constraints, constraints_begin, constraints_end, warm_start_impulse_ratio, steps_calculator); + mContactManager.WarmStartVelocityConstraints(contacts_begin, contacts_end, warm_start_impulse_ratio, steps_calculator); + steps_calculator.Finalize(); + + // Store the number of position steps for later + mIslandBuilder.SetNumPositionSteps(island_idx, steps_calculator.GetNumPositionSteps()); + + // Solve velocity constraints + for (uint velocity_step = 0; velocity_step < steps_calculator.GetNumVelocitySteps(); ++velocity_step) + { + bool applied_impulse = ConstraintManager::sSolveVelocityConstraints(active_constraints, constraints_begin, constraints_end, delta_time); + applied_impulse |= mContactManager.SolveVelocityConstraints(contacts_begin, contacts_end); + if (!applied_impulse) + break; + } + + // Save back the lambdas in the contact cache for the warm start of the next physics update + mContactManager.StoreAppliedImpulses(contacts_begin, contacts_end); + + // We processed work, loop again + continue; + } + + // If we didn't find any work, give up a time slice + std::this_thread::yield(); + } + while (check_islands || check_split_islands); +} + +JPH_SUPPRESS_WARNING_POP + +void PhysicsSystem::JobPreIntegrateVelocity(PhysicsUpdateContext *ioContext, PhysicsUpdateContext::Step *ioStep) +{ + // Reserve enough space for all bodies that may need a cast + TempAllocator *temp_allocator = ioContext->mTempAllocator; + JPH_ASSERT(ioStep->mCCDBodies == nullptr); + ioStep->mCCDBodiesCapacity = mBodyManager.GetNumActiveCCDBodies(); + ioStep->mCCDBodies = (CCDBody *)temp_allocator->Allocate(ioStep->mCCDBodiesCapacity * sizeof(CCDBody)); + + // Initialize the mapping table between active body and CCD body + JPH_ASSERT(ioStep->mActiveBodyToCCDBody == nullptr); + ioStep->mNumActiveBodyToCCDBody = mBodyManager.GetNumActiveBodies(EBodyType::RigidBody); + ioStep->mActiveBodyToCCDBody = (int *)temp_allocator->Allocate(ioStep->mNumActiveBodyToCCDBody * sizeof(int)); + + // Prepare the split island builder for solving the position constraints + mLargeIslandSplitter.PrepareForSolvePositions(); +} + +void PhysicsSystem::JobIntegrateVelocity(const PhysicsUpdateContext *ioContext, PhysicsUpdateContext::Step *ioStep) +{ +#ifdef JPH_ENABLE_ASSERTS + // We update positions and need velocity to do so, we also clamp velocities so need to write to them + BodyAccess::Grant grant(BodyAccess::EAccess::ReadWrite, BodyAccess::EAccess::ReadWrite); +#endif + + float delta_time = ioContext->mStepDeltaTime; + const BodyID *active_bodies = mBodyManager.GetActiveBodiesUnsafe(EBodyType::RigidBody); + uint32 num_active_bodies = mBodyManager.GetNumActiveBodies(EBodyType::RigidBody); + uint32 num_active_bodies_after_find_collisions = ioStep->mActiveBodyReadIdx; + + // We can move bodies that are not part of an island. In this case we need to notify the broadphase of the movement. + static constexpr int cBodiesBatch = 64; + BodyID *bodies_to_update_bounds = (BodyID *)JPH_STACK_ALLOC(cBodiesBatch * sizeof(BodyID)); + int num_bodies_to_update_bounds = 0; + + for (;;) + { + // Atomically fetch a batch of bodies + uint32 active_body_idx = ioStep->mIntegrateVelocityReadIdx.fetch_add(cIntegrateVelocityBatchSize); + if (active_body_idx >= num_active_bodies) + break; + + // Calculate the end of the batch + uint32 active_body_idx_end = min(num_active_bodies, active_body_idx + cIntegrateVelocityBatchSize); + + // Process the batch + while (active_body_idx < active_body_idx_end) + { + // Update the positions using an Symplectic Euler step (which integrates using the updated velocity v1' rather + // than the original velocity v1): + // x1' = x1 + h * v1' + // At this point the active bodies list does not change, so it is safe to access the array. + BodyID body_id = active_bodies[active_body_idx]; + Body &body = mBodyManager.GetBody(body_id); + MotionProperties *mp = body.GetMotionProperties(); + + JPH_DET_LOG("JobIntegrateVelocity: id: " << body_id << " v: " << body.GetLinearVelocity() << " w: " << body.GetAngularVelocity()); + + // Clamp velocities (not for kinematic bodies) + if (body.IsDynamic()) + { + mp->ClampLinearVelocity(); + mp->ClampAngularVelocity(); + } + + // Update the rotation of the body according to the angular velocity + // For motion type discrete we need to do this anyway, for motion type linear cast we have multiple choices + // 1. Rotate the body first and then sweep + // 2. First sweep and then rotate the body at the end + // 3. Pick some in between rotation (e.g. half way), then sweep and finally rotate the remainder + // (1) has some clear advantages as when a long thin body hits a surface away from the center of mass, this will result in a large angular velocity and a limited reduction in linear velocity. + // When simulation the rotation first before doing the translation, the body will be able to rotate away from the contact point allowing the center of mass to approach the surface. When using + // approach (2) in this case what will happen is that we will immediately detect the same collision again (the body has not rotated and the body was already colliding at the end of the previous + // time step) resulting in a lot of stolen time and the body appearing to be frozen in an unnatural pose (like it is glued at an angle to the surface). (2) obviously has some negative side effects + // too as simulating the rotation first may cause it to tunnel through a small object that the linear cast might have otherwise detected. In any case a linear cast is not good for detecting + // tunneling due to angular rotation, so we don't care about that too much (you'd need a full cast to take angular effects into account). + body.AddRotationStep(body.GetAngularVelocity() * delta_time); + + // Get delta position + Vec3 delta_pos = body.GetLinearVelocity() * delta_time; + + // If the position should be updated (or if it is delayed because of CCD) + bool update_position = true; + + switch (mp->GetMotionQuality()) + { + case EMotionQuality::Discrete: + // No additional collision checking to be done + break; + + case EMotionQuality::LinearCast: + if (body.IsDynamic() // Kinematic bodies cannot be stopped + && !body.IsSensor()) // We don't support CCD sensors + { + // Determine inner radius (the smallest sphere that fits into the shape) + float inner_radius = body.GetShape()->GetInnerRadius(); + JPH_ASSERT(inner_radius > 0.0f, "The shape has no inner radius, this makes the shape unsuitable for the linear cast motion quality as we cannot move it without risking tunneling."); + + // Measure translation in this step and check if it above the threshold to perform a linear cast + float linear_cast_threshold_sq = Square(mPhysicsSettings.mLinearCastThreshold * inner_radius); + if (delta_pos.LengthSq() > linear_cast_threshold_sq) + { + // This body needs a cast + uint32 ccd_body_idx = ioStep->mNumCCDBodies++; + JPH_ASSERT(active_body_idx < ioStep->mNumActiveBodyToCCDBody); + ioStep->mActiveBodyToCCDBody[active_body_idx] = ccd_body_idx; + new (&ioStep->mCCDBodies[ccd_body_idx]) CCDBody(body_id, delta_pos, linear_cast_threshold_sq, min(mPhysicsSettings.mPenetrationSlop, mPhysicsSettings.mLinearCastMaxPenetration * inner_radius)); + + update_position = false; + } + } + break; + } + + if (update_position) + { + // Move the body now + body.AddPositionStep(delta_pos); + + // If the body was activated due to an earlier CCD step it will have an index in the active + // body list that it higher than the highest one we processed during FindCollisions + // which means it hasn't been assigned an island and will not be updated by an island + // this means that we need to update its bounds manually + if (mp->GetIndexInActiveBodiesInternal() >= num_active_bodies_after_find_collisions) + { + body.CalculateWorldSpaceBoundsInternal(); + bodies_to_update_bounds[num_bodies_to_update_bounds++] = body.GetID(); + if (num_bodies_to_update_bounds == cBodiesBatch) + { + // Buffer full, flush now + mBroadPhase->NotifyBodiesAABBChanged(bodies_to_update_bounds, num_bodies_to_update_bounds, false); + num_bodies_to_update_bounds = 0; + } + } + + // We did not create a CCD body + ioStep->mActiveBodyToCCDBody[active_body_idx] = -1; + } + + active_body_idx++; + } + } + + // Notify change bounds on requested bodies + if (num_bodies_to_update_bounds > 0) + mBroadPhase->NotifyBodiesAABBChanged(bodies_to_update_bounds, num_bodies_to_update_bounds, false); +} + +void PhysicsSystem::JobPostIntegrateVelocity(PhysicsUpdateContext *ioContext, PhysicsUpdateContext::Step *ioStep) const +{ + // Validate that our reservations were correct + JPH_ASSERT(ioStep->mNumCCDBodies <= mBodyManager.GetNumActiveCCDBodies()); + + if (ioStep->mNumCCDBodies == 0) + { + // No continuous collision detection jobs -> kick the next job ourselves + ioStep->mContactRemovedCallbacks.RemoveDependency(); + } + else + { + // Run the continuous collision detection jobs + int num_continuous_collision_jobs = min(int(ioStep->mNumCCDBodies + cNumCCDBodiesPerJob - 1) / cNumCCDBodiesPerJob, ioContext->GetMaxConcurrency()); + ioStep->mResolveCCDContacts.AddDependency(num_continuous_collision_jobs); + ioStep->mContactRemovedCallbacks.AddDependency(num_continuous_collision_jobs - 1); // Already had 1 dependency + for (int i = 0; i < num_continuous_collision_jobs; ++i) + { + JobHandle job = ioContext->mJobSystem->CreateJob("FindCCDContacts", cColorFindCCDContacts, [ioContext, ioStep]() + { + ioContext->mPhysicsSystem->JobFindCCDContacts(ioContext, ioStep); + + ioStep->mResolveCCDContacts.RemoveDependency(); + ioStep->mContactRemovedCallbacks.RemoveDependency(); + }); + ioContext->mBarrier->AddJob(job); + } + } +} + +// Helper function to calculate the motion of a body during this CCD step +inline static Vec3 sCalculateBodyMotion(const Body &inBody, float inDeltaTime) +{ + // If the body is linear casting, the body has not yet moved so we need to calculate its motion + if (inBody.IsDynamic() && inBody.GetMotionProperties()->GetMotionQuality() == EMotionQuality::LinearCast) + return inDeltaTime * inBody.GetLinearVelocity(); + + // Body has already moved, so we don't need to correct for anything + return Vec3::sZero(); +} + +// Helper function that finds the CCD body corresponding to a body (if it exists) +inline static PhysicsUpdateContext::Step::CCDBody *sGetCCDBody(const Body &inBody, PhysicsUpdateContext::Step *inStep) +{ + // Only rigid bodies can have a CCD body + if (!inBody.IsRigidBody()) + return nullptr; + + // If the body has no motion properties it cannot have a CCD body + const MotionProperties *motion_properties = inBody.GetMotionPropertiesUnchecked(); + if (motion_properties == nullptr) + return nullptr; + + // If it is not active it cannot have a CCD body + uint32 active_index = motion_properties->GetIndexInActiveBodiesInternal(); + if (active_index == Body::cInactiveIndex) + return nullptr; + + // Check if the active body has a corresponding CCD body + JPH_ASSERT(active_index < inStep->mNumActiveBodyToCCDBody); // Ensure that the body has a mapping to CCD body + int ccd_index = inStep->mActiveBodyToCCDBody[active_index]; + if (ccd_index < 0) + return nullptr; + + PhysicsUpdateContext::Step::CCDBody *ccd_body = &inStep->mCCDBodies[ccd_index]; + JPH_ASSERT(ccd_body->mBodyID1 == inBody.GetID(), "We found the wrong CCD body!"); + return ccd_body; +} + +void PhysicsSystem::JobFindCCDContacts(const PhysicsUpdateContext *ioContext, PhysicsUpdateContext::Step *ioStep) +{ +#ifdef JPH_ENABLE_ASSERTS + // We only read positions, but the validate callback may read body positions and velocities + BodyAccess::Grant grant(BodyAccess::EAccess::Read, BodyAccess::EAccess::Read); +#endif + + // Allocation context for allocating new contact points + ContactAllocator contact_allocator(mContactManager.GetContactAllocator()); + + // Settings + ShapeCastSettings settings; + settings.mUseShrunkenShapeAndConvexRadius = true; + settings.mBackFaceModeTriangles = EBackFaceMode::IgnoreBackFaces; + settings.mBackFaceModeConvex = EBackFaceMode::IgnoreBackFaces; + settings.mReturnDeepestPoint = true; + settings.mCollectFacesMode = ECollectFacesMode::CollectFaces; + settings.mActiveEdgeMode = mPhysicsSettings.mCheckActiveEdges? EActiveEdgeMode::CollideOnlyWithActive : EActiveEdgeMode::CollideWithAll; + + for (;;) + { + // Fetch the next body to cast + uint32 idx = ioStep->mNextCCDBody++; + if (idx >= ioStep->mNumCCDBodies) + break; + CCDBody &ccd_body = ioStep->mCCDBodies[idx]; + const Body &body = mBodyManager.GetBody(ccd_body.mBodyID1); + + // Filter out layers + DefaultBroadPhaseLayerFilter broadphase_layer_filter = GetDefaultBroadPhaseLayerFilter(body.GetObjectLayer()); + DefaultObjectLayerFilter object_layer_filter = GetDefaultLayerFilter(body.GetObjectLayer()); + + #ifdef JPH_DEBUG_RENDERER + // Draw start and end shape of cast + if (sDrawMotionQualityLinearCast) + { + RMat44 com = body.GetCenterOfMassTransform(); + body.GetShape()->Draw(DebugRenderer::sInstance, com, Vec3::sReplicate(1.0f), Color::sGreen, false, true); + DebugRenderer::sInstance->DrawArrow(com.GetTranslation(), com.GetTranslation() + ccd_body.mDeltaPosition, Color::sGreen, 0.1f); + body.GetShape()->Draw(DebugRenderer::sInstance, com.PostTranslated(ccd_body.mDeltaPosition), Vec3::sReplicate(1.0f), Color::sRed, false, true); + } + #endif // JPH_DEBUG_RENDERER + + // Create a collector that will find the maximum distance allowed to travel while not penetrating more than 'max penetration' + class CCDNarrowPhaseCollector : public CastShapeCollector + { + public: + CCDNarrowPhaseCollector(const BodyManager &inBodyManager, ContactConstraintManager &inContactConstraintManager, CCDBody &inCCDBody, ShapeCastResult &inResult, float inDeltaTime) : + mBodyManager(inBodyManager), + mContactConstraintManager(inContactConstraintManager), + mCCDBody(inCCDBody), + mResult(inResult), + mDeltaTime(inDeltaTime) + { + } + + virtual void AddHit(const ShapeCastResult &inResult) override + { + JPH_PROFILE_FUNCTION(); + + // Check if this is a possible earlier hit than the one before + float fraction = inResult.mFraction; + if (fraction < mCCDBody.mFractionPlusSlop) + { + // Normalize normal + Vec3 normal = inResult.mPenetrationAxis.Normalized(); + + // Calculate how much we can add to the fraction to penetrate the collision point by mMaxPenetration. + // Note that the normal is pointing towards body 2! + // Let the extra distance that we can travel along delta_pos be 'dist': mMaxPenetration / dist = cos(angle between normal and delta_pos) = normal . delta_pos / |delta_pos| + // <=> dist = mMaxPenetration * |delta_pos| / normal . delta_pos + // Converting to a faction: delta_fraction = dist / |delta_pos| = mLinearCastTreshold / normal . delta_pos + float denominator = normal.Dot(mCCDBody.mDeltaPosition); + if (denominator > mCCDBody.mMaxPenetration) // Avoid dividing by zero, if extra hit fraction > 1 there's also no point in continuing + { + float fraction_plus_slop = fraction + mCCDBody.mMaxPenetration / denominator; + if (fraction_plus_slop < mCCDBody.mFractionPlusSlop) + { + const Body &body2 = mBodyManager.GetBody(inResult.mBodyID2); + + // Check if we've already accepted all hits from this body + if (mValidateBodyPair) + { + // Validate the contact result + const Body &body1 = mBodyManager.GetBody(mCCDBody.mBodyID1); + ValidateResult validate_result = mContactConstraintManager.ValidateContactPoint(body1, body2, body1.GetCenterOfMassPosition(), inResult); // Note that the center of mass of body 1 is the start of the sweep and is used as base offset below + switch (validate_result) + { + case ValidateResult::AcceptContact: + // Just continue + break; + + case ValidateResult::AcceptAllContactsForThisBodyPair: + // Accept this and all following contacts from this body + mValidateBodyPair = false; + break; + + case ValidateResult::RejectContact: + return; + + case ValidateResult::RejectAllContactsForThisBodyPair: + // Reject this and all following contacts from this body + mRejectAll = true; + ForceEarlyOut(); + return; + } + } + + // This is the earliest hit so far, store it + mCCDBody.mContactNormal = normal; + mCCDBody.mBodyID2 = inResult.mBodyID2; + mCCDBody.mSubShapeID2 = inResult.mSubShapeID2; + mCCDBody.mFraction = fraction; + mCCDBody.mFractionPlusSlop = fraction_plus_slop; + mResult = inResult; + + // Result was assuming body 2 is not moving, but it is, so we need to correct for it + Vec3 movement2 = fraction * sCalculateBodyMotion(body2, mDeltaTime); + if (!movement2.IsNearZero()) + { + mResult.mContactPointOn1 += movement2; + mResult.mContactPointOn2 += movement2; + for (Vec3 &v : mResult.mShape1Face) + v += movement2; + for (Vec3 &v : mResult.mShape2Face) + v += movement2; + } + + // Update early out fraction + UpdateEarlyOutFraction(fraction_plus_slop); + } + } + } + } + + bool mValidateBodyPair; ///< If we still have to call the ValidateContactPoint for this body pair + bool mRejectAll; ///< Reject all further contacts between this body pair + + private: + const BodyManager & mBodyManager; + ContactConstraintManager & mContactConstraintManager; + CCDBody & mCCDBody; + ShapeCastResult & mResult; + float mDeltaTime; + BodyID mAcceptedBodyID; + }; + + // Narrowphase collector + ShapeCastResult cast_shape_result; + CCDNarrowPhaseCollector np_collector(mBodyManager, mContactManager, ccd_body, cast_shape_result, ioContext->mStepDeltaTime); + + // This collector wraps the narrowphase collector and collects the closest hit + class CCDBroadPhaseCollector : public CastShapeBodyCollector + { + public: + CCDBroadPhaseCollector(const CCDBody &inCCDBody, const Body &inBody1, const RShapeCast &inShapeCast, ShapeCastSettings &inShapeCastSettings, CCDNarrowPhaseCollector &ioCollector, const BodyManager &inBodyManager, PhysicsUpdateContext::Step *inStep, float inDeltaTime) : + mCCDBody(inCCDBody), + mBody1(inBody1), + mBody1Extent(inShapeCast.mShapeWorldBounds.GetExtent()), + mShapeCast(inShapeCast), + mShapeCastSettings(inShapeCastSettings), + mCollector(ioCollector), + mBodyManager(inBodyManager), + mStep(inStep), + mDeltaTime(inDeltaTime) + { + } + + virtual void AddHit(const BroadPhaseCastResult &inResult) override + { + JPH_PROFILE_FUNCTION(); + + JPH_ASSERT(inResult.mFraction <= GetEarlyOutFraction(), "This hit should not have been passed on to the collector"); + + // Test if we're colliding with ourselves + if (mBody1.GetID() == inResult.mBodyID) + return; + + // Avoid treating duplicates, if both bodies are doing CCD then only consider collision if body ID < other body ID + const Body &body2 = mBodyManager.GetBody(inResult.mBodyID); + const CCDBody *ccd_body2 = sGetCCDBody(body2, mStep); + if (ccd_body2 != nullptr && mCCDBody.mBodyID1 > ccd_body2->mBodyID1) + return; + + // Test group filter + if (!mBody1.GetCollisionGroup().CanCollide(body2.GetCollisionGroup())) + return; + + // TODO: For now we ignore sensors + if (body2.IsSensor()) + return; + + // Get relative movement of these two bodies + Vec3 direction = mShapeCast.mDirection - sCalculateBodyMotion(body2, mDeltaTime); + + // Test if the remaining movement is less than our movement threshold + if (direction.LengthSq() < mCCDBody.mLinearCastThresholdSq) + return; + + // Get the bounds of 2, widen it by the extent of 1 and test a ray to see if it hits earlier than the current early out fraction + AABox bounds = body2.GetWorldSpaceBounds(); + bounds.mMin -= mBody1Extent; + bounds.mMax += mBody1Extent; + float hit_fraction = RayAABox(Vec3(mShapeCast.mCenterOfMassStart.GetTranslation()), RayInvDirection(direction), bounds.mMin, bounds.mMax); + if (hit_fraction > GetPositiveEarlyOutFraction()) // If early out fraction <= 0, we have the possibility of finding a deeper hit so we need to clamp the early out fraction + return; + + // Reset collector (this is a new body pair) + mCollector.ResetEarlyOutFraction(GetEarlyOutFraction()); + mCollector.mValidateBodyPair = true; + mCollector.mRejectAll = false; + + // Provide direction as hint for the active edges algorithm + mShapeCastSettings.mActiveEdgeMovementDirection = direction; + + // Do narrow phase collision check + RShapeCast relative_cast(mShapeCast.mShape, mShapeCast.mScale, mShapeCast.mCenterOfMassStart, direction, mShapeCast.mShapeWorldBounds); + body2.GetTransformedShape().CastShape(relative_cast, mShapeCastSettings, mShapeCast.mCenterOfMassStart.GetTranslation(), mCollector); + + // Update early out fraction based on narrow phase collector + if (!mCollector.mRejectAll) + UpdateEarlyOutFraction(mCollector.GetEarlyOutFraction()); + } + + const CCDBody & mCCDBody; + const Body & mBody1; + Vec3 mBody1Extent; + RShapeCast mShapeCast; + ShapeCastSettings & mShapeCastSettings; + CCDNarrowPhaseCollector & mCollector; + const BodyManager & mBodyManager; + PhysicsUpdateContext::Step *mStep; + float mDeltaTime; + }; + + // Check if we collide with any other body. Note that we use the non-locking interface as we know the broadphase cannot be modified at this point. + RShapeCast shape_cast(body.GetShape(), Vec3::sReplicate(1.0f), body.GetCenterOfMassTransform(), ccd_body.mDeltaPosition); + CCDBroadPhaseCollector bp_collector(ccd_body, body, shape_cast, settings, np_collector, mBodyManager, ioStep, ioContext->mStepDeltaTime); + mBroadPhase->CastAABoxNoLock({ shape_cast.mShapeWorldBounds, shape_cast.mDirection }, bp_collector, broadphase_layer_filter, object_layer_filter); + + // Check if there was a hit + if (ccd_body.mFractionPlusSlop < 1.0f) + { + const Body &body2 = mBodyManager.GetBody(ccd_body.mBodyID2); + + // Determine contact manifold + ContactManifold manifold; + manifold.mBaseOffset = shape_cast.mCenterOfMassStart.GetTranslation(); + ManifoldBetweenTwoFaces(cast_shape_result.mContactPointOn1, cast_shape_result.mContactPointOn2, cast_shape_result.mPenetrationAxis, mPhysicsSettings.mManifoldToleranceSq, cast_shape_result.mShape1Face, cast_shape_result.mShape2Face, manifold.mRelativeContactPointsOn1, manifold.mRelativeContactPointsOn2 JPH_IF_DEBUG_RENDERER(, manifold.mBaseOffset)); + manifold.mSubShapeID1 = cast_shape_result.mSubShapeID1; + manifold.mSubShapeID2 = cast_shape_result.mSubShapeID2; + manifold.mPenetrationDepth = cast_shape_result.mPenetrationDepth; + manifold.mWorldSpaceNormal = ccd_body.mContactNormal; + + // Call contact point callbacks + mContactManager.OnCCDContactAdded(contact_allocator, body, body2, manifold, ccd_body.mContactSettings); + + if (ccd_body.mContactSettings.mIsSensor) + { + // If this is a sensor, we don't want to solve the contact + ccd_body.mFractionPlusSlop = 1.0f; + ccd_body.mBodyID2 = BodyID(); + } + else + { + // Calculate the average position from the manifold (this will result in the same impulse applied as when we apply impulses to all contact points) + if (manifold.mRelativeContactPointsOn2.size() > 1) + { + Vec3 average_contact_point = Vec3::sZero(); + for (const Vec3 &v : manifold.mRelativeContactPointsOn2) + average_contact_point += v; + average_contact_point /= (float)manifold.mRelativeContactPointsOn2.size(); + ccd_body.mContactPointOn2 = manifold.mBaseOffset + average_contact_point; + } + else + ccd_body.mContactPointOn2 = manifold.mBaseOffset + cast_shape_result.mContactPointOn2; + } + } + } + + // Collect information from the contact allocator and accumulate it in the step. + sFinalizeContactAllocator(*ioStep, contact_allocator); +} + +void PhysicsSystem::JobResolveCCDContacts(PhysicsUpdateContext *ioContext, PhysicsUpdateContext::Step *ioStep) +{ +#ifdef JPH_ENABLE_ASSERTS + // Read/write body access + BodyAccess::Grant grant(BodyAccess::EAccess::ReadWrite, BodyAccess::EAccess::ReadWrite); + + // We activate bodies that we collide with + BodyManager::GrantActiveBodiesAccess grant_active(true, false); +#endif + + uint32 num_active_bodies_after_find_collisions = ioStep->mActiveBodyReadIdx; + TempAllocator *temp_allocator = ioContext->mTempAllocator; + + // Check if there's anything to do + uint num_ccd_bodies = ioStep->mNumCCDBodies; + if (num_ccd_bodies > 0) + { + // Sort on fraction so that we process earliest collisions first + // This is needed to make the simulation deterministic and also to be able to stop contact processing + // between body pairs if an earlier hit was found involving the body by another CCD body + // (if it's body ID < this CCD body's body ID - see filtering logic in CCDBroadPhaseCollector) + CCDBody **sorted_ccd_bodies = (CCDBody **)temp_allocator->Allocate(num_ccd_bodies * sizeof(CCDBody *)); + { + JPH_PROFILE("Sort"); + + // We don't want to copy the entire struct (it's quite big), so we create a pointer array first + CCDBody *src_ccd_bodies = ioStep->mCCDBodies; + CCDBody **dst_ccd_bodies = sorted_ccd_bodies; + CCDBody **dst_ccd_bodies_end = dst_ccd_bodies + num_ccd_bodies; + while (dst_ccd_bodies < dst_ccd_bodies_end) + *(dst_ccd_bodies++) = src_ccd_bodies++; + + // Which we then sort + QuickSort(sorted_ccd_bodies, sorted_ccd_bodies + num_ccd_bodies, [](const CCDBody *inBody1, const CCDBody *inBody2) + { + if (inBody1->mFractionPlusSlop != inBody2->mFractionPlusSlop) + return inBody1->mFractionPlusSlop < inBody2->mFractionPlusSlop; + + return inBody1->mBodyID1 < inBody2->mBodyID1; + }); + } + + // We can collide with bodies that are not active, we track them here so we can activate them in one go at the end. + // This is also needed because we can't modify the active body array while we iterate it. + static constexpr int cBodiesBatch = 64; + BodyID *bodies_to_activate = (BodyID *)JPH_STACK_ALLOC(cBodiesBatch * sizeof(BodyID)); + int num_bodies_to_activate = 0; + + // We can move bodies that are not part of an island. In this case we need to notify the broadphase of the movement. + BodyID *bodies_to_update_bounds = (BodyID *)JPH_STACK_ALLOC(cBodiesBatch * sizeof(BodyID)); + int num_bodies_to_update_bounds = 0; + + for (uint i = 0; i < num_ccd_bodies; ++i) + { + const CCDBody *ccd_body = sorted_ccd_bodies[i]; + Body &body1 = mBodyManager.GetBody(ccd_body->mBodyID1); + MotionProperties *body_mp = body1.GetMotionProperties(); + + // If there was a hit + if (!ccd_body->mBodyID2.IsInvalid()) + { + Body &body2 = mBodyManager.GetBody(ccd_body->mBodyID2); + + // Determine if the other body has a CCD body + CCDBody *ccd_body2 = sGetCCDBody(body2, ioStep); + if (ccd_body2 != nullptr) + { + JPH_ASSERT(ccd_body2->mBodyID2 != ccd_body->mBodyID1, "If we collided with another body, that other body should have ignored collisions with us!"); + + // Check if the other body found a hit that is further away + if (ccd_body2->mFraction > ccd_body->mFraction) + { + // Reset the colliding body of the other CCD body. The other body will shorten its distance traveled and will not do any collision response (we'll do that). + // This means that at this point we have triggered a contact point add/persist for our further hit by accident for the other body. + // We accept this as calling the contact point callbacks here would require persisting the manifolds up to this point and doing the callbacks single threaded. + ccd_body2->mBodyID2 = BodyID(); + ccd_body2->mFractionPlusSlop = ccd_body->mFraction; + } + } + + // If the other body moved less than us before hitting something, we're not colliding with it so we again have triggered contact point add/persist callbacks by accident. + // We'll just move to the collision position anyway (as that's the last position we know is good), but we won't do any collision response. + if (ccd_body2 == nullptr || ccd_body2->mFraction >= ccd_body->mFraction) + { + const ContactSettings &contact_settings = ccd_body->mContactSettings; + + // Calculate contact point velocity for body 1 + Vec3 r1_plus_u = Vec3(ccd_body->mContactPointOn2 - (body1.GetCenterOfMassPosition() + ccd_body->mFraction * ccd_body->mDeltaPosition)); + Vec3 v1 = body1.GetPointVelocityCOM(r1_plus_u); + + // Calculate inverse mass for body 1 + float inv_m1 = contact_settings.mInvMassScale1 * body_mp->GetInverseMass(); + + if (body2.IsRigidBody()) + { + // Calculate contact point velocity for body 2 + Vec3 r2 = Vec3(ccd_body->mContactPointOn2 - body2.GetCenterOfMassPosition()); + Vec3 v2 = body2.GetPointVelocityCOM(r2); + + // Calculate relative contact velocity + Vec3 relative_velocity = v2 - v1; + float normal_velocity = relative_velocity.Dot(ccd_body->mContactNormal); + + // Calculate velocity bias due to restitution + float normal_velocity_bias; + if (contact_settings.mCombinedRestitution > 0.0f && normal_velocity < -mPhysicsSettings.mMinVelocityForRestitution) + normal_velocity_bias = contact_settings.mCombinedRestitution * normal_velocity; + else + normal_velocity_bias = 0.0f; + + // Get inverse mass of body 2 + float inv_m2 = body2.GetMotionPropertiesUnchecked() != nullptr? contact_settings.mInvMassScale2 * body2.GetMotionPropertiesUnchecked()->GetInverseMassUnchecked() : 0.0f; + + // Solve contact constraint + AxisConstraintPart contact_constraint; + contact_constraint.CalculateConstraintPropertiesWithMassOverride(body1, inv_m1, contact_settings.mInvInertiaScale1, r1_plus_u, body2, inv_m2, contact_settings.mInvInertiaScale2, r2, ccd_body->mContactNormal, normal_velocity_bias); + contact_constraint.SolveVelocityConstraintWithMassOverride(body1, inv_m1, body2, inv_m2, ccd_body->mContactNormal, -FLT_MAX, FLT_MAX); + + // Apply friction + if (contact_settings.mCombinedFriction > 0.0f) + { + // Calculate friction direction by removing normal velocity from the relative velocity + Vec3 friction_direction = relative_velocity - normal_velocity * ccd_body->mContactNormal; + float friction_direction_len_sq = friction_direction.LengthSq(); + if (friction_direction_len_sq > 1.0e-12f) + { + // Normalize friction direction + friction_direction /= sqrt(friction_direction_len_sq); + + // Calculate max friction impulse + float max_lambda_f = contact_settings.mCombinedFriction * contact_constraint.GetTotalLambda(); + + AxisConstraintPart friction; + friction.CalculateConstraintPropertiesWithMassOverride(body1, inv_m1, contact_settings.mInvInertiaScale1, r1_plus_u, body2, inv_m2, contact_settings.mInvInertiaScale2, r2, friction_direction); + friction.SolveVelocityConstraintWithMassOverride(body1, inv_m1, body2, inv_m2, friction_direction, -max_lambda_f, max_lambda_f); + } + } + + // Clamp velocity of body 2 + if (body2.IsDynamic()) + { + MotionProperties *body2_mp = body2.GetMotionProperties(); + body2_mp->ClampLinearVelocity(); + body2_mp->ClampAngularVelocity(); + } + } + else + { + SoftBodyMotionProperties *soft_mp = static_cast(body2.GetMotionProperties()); + const SoftBodyShape *soft_shape = static_cast(body2.GetShape()); + + // Convert the sub shape ID of the soft body to a face + uint32 face_idx = soft_shape->GetFaceIndex(ccd_body->mSubShapeID2); + const SoftBodyMotionProperties::Face &face = soft_mp->GetFace(face_idx); + + // Get vertices of the face + SoftBodyMotionProperties::Vertex &vtx0 = soft_mp->GetVertex(face.mVertex[0]); + SoftBodyMotionProperties::Vertex &vtx1 = soft_mp->GetVertex(face.mVertex[1]); + SoftBodyMotionProperties::Vertex &vtx2 = soft_mp->GetVertex(face.mVertex[2]); + + // Inverse mass of the face + float vtx0_mass = vtx0.mInvMass > 0.0f? 1.0f / vtx0.mInvMass : 1.0e10f; + float vtx1_mass = vtx1.mInvMass > 0.0f? 1.0f / vtx1.mInvMass : 1.0e10f; + float vtx2_mass = vtx2.mInvMass > 0.0f? 1.0f / vtx2.mInvMass : 1.0e10f; + float inv_m2 = 1.0f / (vtx0_mass + vtx1_mass + vtx2_mass); + + // Calculate barycentric coordinates of the contact point on the soft body's face + float u, v, w; + RMat44 inv_body2_transform = body2.GetInverseCenterOfMassTransform(); + Vec3 local_contact = Vec3(inv_body2_transform * ccd_body->mContactPointOn2); + ClosestPoint::GetBaryCentricCoordinates(vtx0.mPosition - local_contact, vtx1.mPosition - local_contact, vtx2.mPosition - local_contact, u, v, w); + + // Calculate contact point velocity for the face + Vec3 v2 = inv_body2_transform.Multiply3x3Transposed(u * vtx0.mVelocity + v * vtx1.mVelocity + w * vtx2.mVelocity); + float normal_velocity = (v2 - v1).Dot(ccd_body->mContactNormal); + + // Calculate velocity bias due to restitution + float normal_velocity_bias; + if (contact_settings.mCombinedRestitution > 0.0f && normal_velocity < -mPhysicsSettings.mMinVelocityForRestitution) + normal_velocity_bias = contact_settings.mCombinedRestitution * normal_velocity; + else + normal_velocity_bias = 0.0f; + + // Calculate resulting velocity change (the math here is similar to AxisConstraintPart but without an inertia term for body 2 as we treat it as a point mass) + Vec3 r1_plus_u_x_n = r1_plus_u.Cross(ccd_body->mContactNormal); + Vec3 invi1_r1_plus_u_x_n = contact_settings.mInvInertiaScale1 * body1.GetInverseInertia().Multiply3x3(r1_plus_u_x_n); + float jv = r1_plus_u_x_n.Dot(body_mp->GetAngularVelocity()) - normal_velocity - normal_velocity_bias; + float inv_effective_mass = inv_m1 + inv_m2 + invi1_r1_plus_u_x_n.Dot(r1_plus_u_x_n); + float lambda = jv / inv_effective_mass; + body_mp->SubLinearVelocityStep((lambda * inv_m1) * ccd_body->mContactNormal); + body_mp->SubAngularVelocityStep(lambda * invi1_r1_plus_u_x_n); + Vec3 delta_v2 = inv_body2_transform.Multiply3x3(lambda * ccd_body->mContactNormal); + vtx0.mVelocity += delta_v2 * vtx0.mInvMass; + vtx1.mVelocity += delta_v2 * vtx1.mInvMass; + vtx2.mVelocity += delta_v2 * vtx2.mInvMass; + } + + // Clamp velocity of body 1 + body_mp->ClampLinearVelocity(); + body_mp->ClampAngularVelocity(); + + // Activate the 2nd body if it is not already active + if (body2.IsDynamic() && !body2.IsActive()) + { + bodies_to_activate[num_bodies_to_activate++] = ccd_body->mBodyID2; + if (num_bodies_to_activate == cBodiesBatch) + { + // Batch is full, activate now + mBodyManager.ActivateBodies(bodies_to_activate, num_bodies_to_activate); + num_bodies_to_activate = 0; + } + } + + #ifdef JPH_DEBUG_RENDERER + if (sDrawMotionQualityLinearCast) + { + // Draw the collision location + RMat44 collision_transform = body1.GetCenterOfMassTransform().PostTranslated(ccd_body->mFraction * ccd_body->mDeltaPosition); + body1.GetShape()->Draw(DebugRenderer::sInstance, collision_transform, Vec3::sReplicate(1.0f), Color::sYellow, false, true); + + // Draw the collision location + slop + RMat44 collision_transform_plus_slop = body1.GetCenterOfMassTransform().PostTranslated(ccd_body->mFractionPlusSlop * ccd_body->mDeltaPosition); + body1.GetShape()->Draw(DebugRenderer::sInstance, collision_transform_plus_slop, Vec3::sReplicate(1.0f), Color::sOrange, false, true); + + // Draw contact normal + DebugRenderer::sInstance->DrawArrow(ccd_body->mContactPointOn2, ccd_body->mContactPointOn2 - ccd_body->mContactNormal, Color::sYellow, 0.1f); + + // Draw post contact velocity + DebugRenderer::sInstance->DrawArrow(collision_transform.GetTranslation(), collision_transform.GetTranslation() + body1.GetLinearVelocity(), Color::sOrange, 0.1f); + DebugRenderer::sInstance->DrawArrow(collision_transform.GetTranslation(), collision_transform.GetTranslation() + body1.GetAngularVelocity(), Color::sPurple, 0.1f); + } + #endif // JPH_DEBUG_RENDERER + } + } + + // Update body position + body1.AddPositionStep(ccd_body->mDeltaPosition * ccd_body->mFractionPlusSlop); + + // If the body was activated due to an earlier CCD step it will have an index in the active + // body list that it higher than the highest one we processed during FindCollisions + // which means it hasn't been assigned an island and will not be updated by an island + // this means that we need to update its bounds manually + if (body_mp->GetIndexInActiveBodiesInternal() >= num_active_bodies_after_find_collisions) + { + body1.CalculateWorldSpaceBoundsInternal(); + bodies_to_update_bounds[num_bodies_to_update_bounds++] = body1.GetID(); + if (num_bodies_to_update_bounds == cBodiesBatch) + { + // Buffer full, flush now + mBroadPhase->NotifyBodiesAABBChanged(bodies_to_update_bounds, num_bodies_to_update_bounds, false); + num_bodies_to_update_bounds = 0; + } + } + } + + // Activate the requested bodies + if (num_bodies_to_activate > 0) + mBodyManager.ActivateBodies(bodies_to_activate, num_bodies_to_activate); + + // Notify change bounds on requested bodies + if (num_bodies_to_update_bounds > 0) + mBroadPhase->NotifyBodiesAABBChanged(bodies_to_update_bounds, num_bodies_to_update_bounds, false); + + // Free the sorted ccd bodies + temp_allocator->Free(sorted_ccd_bodies, num_ccd_bodies * sizeof(CCDBody *)); + } + + // Ensure we free the CCD bodies array now, will not call the destructor! + temp_allocator->Free(ioStep->mActiveBodyToCCDBody, ioStep->mNumActiveBodyToCCDBody * sizeof(int)); + ioStep->mActiveBodyToCCDBody = nullptr; + ioStep->mNumActiveBodyToCCDBody = 0; + temp_allocator->Free(ioStep->mCCDBodies, ioStep->mCCDBodiesCapacity * sizeof(CCDBody)); + ioStep->mCCDBodies = nullptr; + ioStep->mCCDBodiesCapacity = 0; +} + +void PhysicsSystem::JobContactRemovedCallbacks(const PhysicsUpdateContext::Step *ioStep) +{ +#ifdef JPH_ENABLE_ASSERTS + // We don't touch any bodies + BodyAccess::Grant grant(BodyAccess::EAccess::None, BodyAccess::EAccess::None); +#endif + + // Reset the Body::EFlags::InvalidateContactCache flag for all bodies + mBodyManager.ValidateContactCacheForAllBodies(); + + // Finalize the contact cache (this swaps the read and write versions of the contact cache) + // Trigger all contact removed callbacks by looking at last step contact points that have not been flagged as reused + mContactManager.FinalizeContactCacheAndCallContactPointRemovedCallbacks(ioStep->mNumBodyPairs, ioStep->mNumManifolds); +} + +class PhysicsSystem::BodiesToSleep : public NonCopyable +{ +public: + static constexpr int cBodiesToSleepSize = 512; + static constexpr int cMaxBodiesToPutInBuffer = 128; + + inline BodiesToSleep(BodyManager &inBodyManager, BodyID *inBodiesToSleepBuffer) : mBodyManager(inBodyManager), mBodiesToSleepBuffer(inBodiesToSleepBuffer), mBodiesToSleepCur(inBodiesToSleepBuffer) { } + + inline ~BodiesToSleep() + { + // Flush the bodies to sleep buffer + int num_bodies_in_buffer = int(mBodiesToSleepCur - mBodiesToSleepBuffer); + if (num_bodies_in_buffer > 0) + mBodyManager.DeactivateBodies(mBodiesToSleepBuffer, num_bodies_in_buffer); + } + + inline void PutToSleep(const BodyID *inBegin, const BodyID *inEnd) + { + int num_bodies_to_sleep = int(inEnd - inBegin); + if (num_bodies_to_sleep > cMaxBodiesToPutInBuffer) + { + // Too many bodies, deactivate immediately + mBodyManager.DeactivateBodies(inBegin, num_bodies_to_sleep); + } + else + { + // Check if there's enough space in the bodies to sleep buffer + int num_bodies_in_buffer = int(mBodiesToSleepCur - mBodiesToSleepBuffer); + if (num_bodies_in_buffer + num_bodies_to_sleep > cBodiesToSleepSize) + { + // Flush the bodies to sleep buffer + mBodyManager.DeactivateBodies(mBodiesToSleepBuffer, num_bodies_in_buffer); + mBodiesToSleepCur = mBodiesToSleepBuffer; + } + + // Copy the bodies in the buffer + memcpy(mBodiesToSleepCur, inBegin, num_bodies_to_sleep * sizeof(BodyID)); + mBodiesToSleepCur += num_bodies_to_sleep; + } + } + +private: + BodyManager & mBodyManager; + BodyID * mBodiesToSleepBuffer; + BodyID * mBodiesToSleepCur; +}; + +void PhysicsSystem::CheckSleepAndUpdateBounds(uint32 inIslandIndex, const PhysicsUpdateContext *ioContext, const PhysicsUpdateContext::Step *ioStep, BodiesToSleep &ioBodiesToSleep) +{ + // Get the bodies that belong to this island + BodyID *bodies_begin, *bodies_end; + mIslandBuilder.GetBodiesInIsland(inIslandIndex, bodies_begin, bodies_end); + + // Only check sleeping in the last step + // Also resets force and torque used during the apply gravity phase + if (ioStep->mIsLast) + { + JPH_PROFILE("Check Sleeping"); + + static_assert(int(ECanSleep::CannotSleep) == 0 && int(ECanSleep::CanSleep) == 1, "Loop below makes this assumption"); + int all_can_sleep = mPhysicsSettings.mAllowSleeping? int(ECanSleep::CanSleep) : int(ECanSleep::CannotSleep); + + float time_before_sleep = mPhysicsSettings.mTimeBeforeSleep; + float max_movement = mPhysicsSettings.mPointVelocitySleepThreshold * time_before_sleep; + + for (const BodyID *body_id = bodies_begin; body_id < bodies_end; ++body_id) + { + Body &body = mBodyManager.GetBody(*body_id); + + // Update bounding box + body.CalculateWorldSpaceBoundsInternal(); + + // Update sleeping + all_can_sleep &= int(body.UpdateSleepStateInternal(ioContext->mStepDeltaTime, max_movement, time_before_sleep)); + + // Reset force and torque + MotionProperties *mp = body.GetMotionProperties(); + mp->ResetForce(); + mp->ResetTorque(); + } + + // If all bodies indicate they can sleep we can deactivate them + if (all_can_sleep == int(ECanSleep::CanSleep)) + ioBodiesToSleep.PutToSleep(bodies_begin, bodies_end); + } + else + { + JPH_PROFILE("Update Bounds"); + + // Update bounding box only for all other steps + for (const BodyID *body_id = bodies_begin; body_id < bodies_end; ++body_id) + { + Body &body = mBodyManager.GetBody(*body_id); + body.CalculateWorldSpaceBoundsInternal(); + } + } + + // Notify broadphase of changed objects (find ccd contacts can do linear casts in the next step, so we need to do this every step) + // Note: Shuffles the BodyID's around!!! + mBroadPhase->NotifyBodiesAABBChanged(bodies_begin, int(bodies_end - bodies_begin), false); +} + +void PhysicsSystem::JobSolvePositionConstraints(PhysicsUpdateContext *ioContext, PhysicsUpdateContext::Step *ioStep) +{ +#ifdef JPH_ENABLE_ASSERTS + // We fix up position errors + BodyAccess::Grant grant(BodyAccess::EAccess::None, BodyAccess::EAccess::ReadWrite); + + // Can only deactivate bodies + BodyManager::GrantActiveBodiesAccess grant_active(false, true); +#endif + + float delta_time = ioContext->mStepDeltaTime; + float baumgarte = mPhysicsSettings.mBaumgarte; + Constraint **active_constraints = ioContext->mActiveConstraints; + + // Keep a buffer of bodies that need to go to sleep in order to not constantly lock the active bodies mutex and create contention between all solving threads + BodiesToSleep bodies_to_sleep(mBodyManager, (BodyID *)JPH_STACK_ALLOC(BodiesToSleep::cBodiesToSleepSize * sizeof(BodyID))); + + bool check_islands = true, check_split_islands = mPhysicsSettings.mUseLargeIslandSplitter; + do + { + // First try to get work from large islands + if (check_split_islands) + { + bool first_iteration; + uint split_island_index; + uint32 *constraints_begin, *constraints_end, *contacts_begin, *contacts_end; + switch (mLargeIslandSplitter.FetchNextBatch(split_island_index, constraints_begin, constraints_end, contacts_begin, contacts_end, first_iteration)) + { + case LargeIslandSplitter::EStatus::BatchRetrieved: + // Solve the batch + ConstraintManager::sSolvePositionConstraints(active_constraints, constraints_begin, constraints_end, delta_time, baumgarte); + mContactManager.SolvePositionConstraints(contacts_begin, contacts_end); + + // Mark the batch as processed + bool last_iteration, final_batch; + mLargeIslandSplitter.MarkBatchProcessed(split_island_index, constraints_begin, constraints_end, contacts_begin, contacts_end, last_iteration, final_batch); + + // The final batch will update all bounds and check sleeping + if (final_batch) + CheckSleepAndUpdateBounds(mLargeIslandSplitter.GetIslandIndex(split_island_index), ioContext, ioStep, bodies_to_sleep); + + // We processed work, loop again + continue; + case LargeIslandSplitter::EStatus::WaitingForBatch: + break; + case LargeIslandSplitter::EStatus::AllBatchesDone: + check_split_islands = false; + break; + } + } + + // If that didn't succeed try to process an island + if (check_islands) + { + // Next island + uint32 island_idx = ioStep->mSolvePositionConstraintsNextIsland++; + if (island_idx >= mIslandBuilder.GetNumIslands()) + { + // We processed all islands, stop checking islands + check_islands = false; + continue; + } + + JPH_PROFILE("Island"); + + // Get iterators for this island + uint32 *constraints_begin, *constraints_end, *contacts_begin, *contacts_end; + mIslandBuilder.GetConstraintsInIsland(island_idx, constraints_begin, constraints_end); + mIslandBuilder.GetContactsInIsland(island_idx, contacts_begin, contacts_end); + + // If this island is a large island, it will be picked up as a batch and we don't need to do anything here + uint num_items = uint(constraints_end - constraints_begin) + uint(contacts_end - contacts_begin); + if (mPhysicsSettings.mUseLargeIslandSplitter + && num_items >= LargeIslandSplitter::cLargeIslandTreshold) + continue; + + // Check if this island needs solving + if (num_items > 0) + { + // Iterate + uint num_position_steps = mIslandBuilder.GetNumPositionSteps(island_idx); + for (uint position_step = 0; position_step < num_position_steps; ++position_step) + { + bool applied_impulse = ConstraintManager::sSolvePositionConstraints(active_constraints, constraints_begin, constraints_end, delta_time, baumgarte); + applied_impulse |= mContactManager.SolvePositionConstraints(contacts_begin, contacts_end); + if (!applied_impulse) + break; + } + } + + // After solving we will update all bounds and check sleeping + CheckSleepAndUpdateBounds(island_idx, ioContext, ioStep, bodies_to_sleep); + + // We processed work, loop again + continue; + } + + // If we didn't find any work, give up a time slice + std::this_thread::yield(); + } + while (check_islands || check_split_islands); +} + +void PhysicsSystem::JobSoftBodyPrepare(PhysicsUpdateContext *ioContext, PhysicsUpdateContext::Step *ioStep) +{ + JPH_PROFILE_FUNCTION(); + + { + #ifdef JPH_ENABLE_ASSERTS + // Reading soft body positions + BodyAccess::Grant grant(BodyAccess::EAccess::None, BodyAccess::EAccess::Read); + #endif + + // Get the active soft bodies + BodyIDVector active_bodies; + mBodyManager.GetActiveBodies(EBodyType::SoftBody, active_bodies); + + // Quit if there are no active soft bodies + if (active_bodies.empty()) + { + // Kick the next step + if (ioStep->mStartNextStep.IsValid()) + ioStep->mStartNextStep.RemoveDependency(); + return; + } + + // Sort to get a deterministic update order + QuickSort(active_bodies.begin(), active_bodies.end()); + + // Allocate soft body contexts + ioContext->mNumSoftBodies = (uint)active_bodies.size(); + ioContext->mSoftBodyUpdateContexts = (SoftBodyUpdateContext *)ioContext->mTempAllocator->Allocate(ioContext->mNumSoftBodies * sizeof(SoftBodyUpdateContext)); + + // Initialize soft body contexts + for (SoftBodyUpdateContext *sb_ctx = ioContext->mSoftBodyUpdateContexts, *sb_ctx_end = ioContext->mSoftBodyUpdateContexts + ioContext->mNumSoftBodies; sb_ctx < sb_ctx_end; ++sb_ctx) + { + new (sb_ctx) SoftBodyUpdateContext; + Body &body = mBodyManager.GetBody(active_bodies[sb_ctx - ioContext->mSoftBodyUpdateContexts]); + SoftBodyMotionProperties *mp = static_cast(body.GetMotionProperties()); + mp->InitializeUpdateContext(ioContext->mStepDeltaTime, body, *this, *sb_ctx); + } + } + + // We're ready to collide the first soft body + ioContext->mSoftBodyToCollide.store(0, memory_order_release); + + // Determine number of jobs to spawn + int num_soft_body_jobs = ioContext->GetMaxConcurrency(); + + // Create finalize job + ioStep->mSoftBodyFinalize = ioContext->mJobSystem->CreateJob("SoftBodyFinalize", cColorSoftBodyFinalize, [ioContext, ioStep]() + { + ioContext->mPhysicsSystem->JobSoftBodyFinalize(ioContext); + + // Kick the next step + if (ioStep->mStartNextStep.IsValid()) + ioStep->mStartNextStep.RemoveDependency(); + }, num_soft_body_jobs); // depends on: soft body simulate + ioContext->mBarrier->AddJob(ioStep->mSoftBodyFinalize); + + // Create simulate jobs + ioStep->mSoftBodySimulate.resize(num_soft_body_jobs); + for (int i = 0; i < num_soft_body_jobs; ++i) + ioStep->mSoftBodySimulate[i] = ioContext->mJobSystem->CreateJob("SoftBodySimulate", cColorSoftBodySimulate, [ioStep, i]() + { + ioStep->mContext->mPhysicsSystem->JobSoftBodySimulate(ioStep->mContext, i); + + ioStep->mSoftBodyFinalize.RemoveDependency(); + }, num_soft_body_jobs); // depends on: soft body collide + ioContext->mBarrier->AddJobs(ioStep->mSoftBodySimulate.data(), ioStep->mSoftBodySimulate.size()); + + // Create collision jobs + ioStep->mSoftBodyCollide.resize(num_soft_body_jobs); + for (int i = 0; i < num_soft_body_jobs; ++i) + ioStep->mSoftBodyCollide[i] = ioContext->mJobSystem->CreateJob("SoftBodyCollide", cColorSoftBodyCollide, [ioContext, ioStep]() + { + ioContext->mPhysicsSystem->JobSoftBodyCollide(ioContext); + + for (const JobHandle &h : ioStep->mSoftBodySimulate) + h.RemoveDependency(); + }); // depends on: nothing + ioContext->mBarrier->AddJobs(ioStep->mSoftBodyCollide.data(), ioStep->mSoftBodyCollide.size()); +} + +void PhysicsSystem::JobSoftBodyCollide(PhysicsUpdateContext *ioContext) const +{ +#ifdef JPH_ENABLE_ASSERTS + // Reading rigid body positions and velocities + BodyAccess::Grant grant(BodyAccess::EAccess::Read, BodyAccess::EAccess::Read); +#endif + + for (;;) + { + // Fetch the next soft body + uint sb_idx = ioContext->mSoftBodyToCollide.fetch_add(1, std::memory_order_acquire); + if (sb_idx >= ioContext->mNumSoftBodies) + break; + + // Do a broadphase check + SoftBodyUpdateContext &sb_ctx = ioContext->mSoftBodyUpdateContexts[sb_idx]; + sb_ctx.mMotionProperties->DetermineCollidingShapes(sb_ctx, *this, GetBodyLockInterfaceNoLock()); + } +} + +void PhysicsSystem::JobSoftBodySimulate(PhysicsUpdateContext *ioContext, uint inThreadIndex) const +{ +#ifdef JPH_ENABLE_ASSERTS + // Updating velocities of soft bodies, allow the contact listener to read the soft body state + BodyAccess::Grant grant(BodyAccess::EAccess::ReadWrite, BodyAccess::EAccess::Read); +#endif + + // Calculate at which body we start to distribute the workload across the threads + uint num_soft_bodies = ioContext->mNumSoftBodies; + uint start_idx = inThreadIndex * num_soft_bodies / ioContext->GetMaxConcurrency(); + + // Keep running partial updates until everything has been updated + uint status; + do + { + // Reset status + status = 0; + + // Update all soft bodies + for (uint i = 0; i < num_soft_bodies; ++i) + { + // Fetch the soft body context + SoftBodyUpdateContext &sb_ctx = ioContext->mSoftBodyUpdateContexts[(start_idx + i) % num_soft_bodies]; + + // To avoid trashing the cache too much, we prefer to stick to one soft body until we cannot progress it any further + uint sb_status; + do + { + sb_status = (uint)sb_ctx.mMotionProperties->ParallelUpdate(sb_ctx, mPhysicsSettings); + status |= sb_status; + } while (sb_status == (uint)SoftBodyMotionProperties::EStatus::DidWork); + } + + // If we didn't perform any work, yield the thread so that something else can run + if (!(status & (uint)SoftBodyMotionProperties::EStatus::DidWork)) + std::this_thread::yield(); + } + while (status != (uint)SoftBodyMotionProperties::EStatus::Done); +} + +void PhysicsSystem::JobSoftBodyFinalize(PhysicsUpdateContext *ioContext) +{ +#ifdef JPH_ENABLE_ASSERTS + // Updating rigid body velocities and soft body positions / velocities + BodyAccess::Grant grant(BodyAccess::EAccess::ReadWrite, BodyAccess::EAccess::ReadWrite); + + // Can activate and deactivate bodies + BodyManager::GrantActiveBodiesAccess grant_active(true, true); +#endif + + static constexpr int cBodiesBatch = 64; + BodyID *bodies_to_update_bounds = (BodyID *)JPH_STACK_ALLOC(cBodiesBatch * sizeof(BodyID)); + int num_bodies_to_update_bounds = 0; + BodyID *bodies_to_put_to_sleep = (BodyID *)JPH_STACK_ALLOC(cBodiesBatch * sizeof(BodyID)); + int num_bodies_to_put_to_sleep = 0; + + for (SoftBodyUpdateContext *sb_ctx = ioContext->mSoftBodyUpdateContexts, *sb_ctx_end = ioContext->mSoftBodyUpdateContexts + ioContext->mNumSoftBodies; sb_ctx < sb_ctx_end; ++sb_ctx) + { + // Apply the rigid body velocity deltas + sb_ctx->mMotionProperties->UpdateRigidBodyVelocities(*sb_ctx, GetBodyInterfaceNoLock()); + + // Update the position + sb_ctx->mBody->SetPositionAndRotationInternal(sb_ctx->mBody->GetPosition() + sb_ctx->mDeltaPosition, sb_ctx->mBody->GetRotation(), false); + + BodyID id = sb_ctx->mBody->GetID(); + bodies_to_update_bounds[num_bodies_to_update_bounds++] = id; + if (num_bodies_to_update_bounds == cBodiesBatch) + { + // Buffer full, flush now + mBroadPhase->NotifyBodiesAABBChanged(bodies_to_update_bounds, num_bodies_to_update_bounds, false); + num_bodies_to_update_bounds = 0; + } + + if (sb_ctx->mCanSleep == ECanSleep::CanSleep) + { + // This body should go to sleep + bodies_to_put_to_sleep[num_bodies_to_put_to_sleep++] = id; + if (num_bodies_to_put_to_sleep == cBodiesBatch) + { + mBodyManager.DeactivateBodies(bodies_to_put_to_sleep, num_bodies_to_put_to_sleep); + num_bodies_to_put_to_sleep = 0; + } + } + } + + // Notify change bounds on requested bodies + if (num_bodies_to_update_bounds > 0) + mBroadPhase->NotifyBodiesAABBChanged(bodies_to_update_bounds, num_bodies_to_update_bounds, false); + + // Notify bodies to go to sleep + if (num_bodies_to_put_to_sleep > 0) + mBodyManager.DeactivateBodies(bodies_to_put_to_sleep, num_bodies_to_put_to_sleep); + + // Free soft body contexts + ioContext->mTempAllocator->Free(ioContext->mSoftBodyUpdateContexts, ioContext->mNumSoftBodies * sizeof(SoftBodyUpdateContext)); +} + +void PhysicsSystem::SaveState(StateRecorder &inStream, EStateRecorderState inState, const StateRecorderFilter *inFilter) const +{ + JPH_PROFILE_FUNCTION(); + + inStream.Write(inState); + + if (uint8(inState) & uint8(EStateRecorderState::Global)) + { + inStream.Write(mPreviousStepDeltaTime); + inStream.Write(mGravity); + } + + if (uint8(inState) & uint8(EStateRecorderState::Bodies)) + mBodyManager.SaveState(inStream, inFilter); + + if (uint8(inState) & uint8(EStateRecorderState::Contacts)) + mContactManager.SaveState(inStream, inFilter); + + if (uint8(inState) & uint8(EStateRecorderState::Constraints)) + mConstraintManager.SaveState(inStream, inFilter); +} + +bool PhysicsSystem::RestoreState(StateRecorder &inStream) +{ + JPH_PROFILE_FUNCTION(); + + EStateRecorderState state = EStateRecorderState::All; // Set this value for validation. If a partial state is saved, validation will not work anyway. + inStream.Read(state); + + if (uint8(state) & uint8(EStateRecorderState::Global)) + { + inStream.Read(mPreviousStepDeltaTime); + inStream.Read(mGravity); + } + + if (uint8(state) & uint8(EStateRecorderState::Bodies)) + { + if (!mBodyManager.RestoreState(inStream)) + return false; + + // Update bounding boxes for all bodies in the broadphase + Array bodies; + for (const Body *b : mBodyManager.GetBodies()) + if (BodyManager::sIsValidBodyPointer(b) && b->IsInBroadPhase()) + bodies.push_back(b->GetID()); + if (!bodies.empty()) + mBroadPhase->NotifyBodiesAABBChanged(&bodies[0], (int)bodies.size()); + } + + if (uint8(state) & uint8(EStateRecorderState::Contacts)) + { + if (!mContactManager.RestoreState(inStream)) + return false; + } + + if (uint8(state) & uint8(EStateRecorderState::Constraints)) + { + if (!mConstraintManager.RestoreState(inStream)) + return false; + } + + return true; +} + +void PhysicsSystem::SaveBodyState(const Body &inBody, StateRecorder &inStream) const +{ + mBodyManager.SaveBodyState(inBody, inStream); +} + +void PhysicsSystem::RestoreBodyState(Body &ioBody, StateRecorder &inStream) +{ + mBodyManager.RestoreBodyState(ioBody, inStream); + + BodyID id = ioBody.GetID(); + mBroadPhase->NotifyBodiesAABBChanged(&id, 1); +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsSystem.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsSystem.h new file mode 100644 index 00000000000..3234e5b53f7 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsSystem.h @@ -0,0 +1,320 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +class JobSystem; +class StateRecorder; +class TempAllocator; +class PhysicsStepListener; +class SoftBodyContactListener; + +/// The main class for the physics system. It contains all rigid bodies and simulates them. +/// +/// The main simulation is performed by the Update() call on multiple threads (if the JobSystem is configured to use them). Please refer to the general architecture overview in the Docs folder for more information. +class JPH_EXPORT PhysicsSystem : public NonCopyable +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Constructor / Destructor + PhysicsSystem() : mContactManager(mPhysicsSettings) JPH_IF_ENABLE_ASSERTS(, mConstraintManager(&mBodyManager)) { } + ~PhysicsSystem(); + + /// Initialize the system. + /// @param inMaxBodies Maximum number of bodies to support. + /// @param inNumBodyMutexes Number of body mutexes to use. Should be a power of 2 in the range [1, 64], use 0 to auto detect. + /// @param inMaxBodyPairs Maximum amount of body pairs to process (anything else will fall through the world), this number should generally be much higher than the max amount of contact points as there will be lots of bodies close that are not actually touching. + /// @param inMaxContactConstraints Maximum amount of contact constraints to process (anything else will fall through the world). + /// @param inBroadPhaseLayerInterface Information on the mapping of object layers to broad phase layers. Since this is a virtual interface, the instance needs to stay alive during the lifetime of the PhysicsSystem. + /// @param inObjectVsBroadPhaseLayerFilter Filter callback function that is used to determine if an object layer collides with a broad phase layer. Since this is a virtual interface, the instance needs to stay alive during the lifetime of the PhysicsSystem. + /// @param inObjectLayerPairFilter Filter callback function that is used to determine if two object layers collide. Since this is a virtual interface, the instance needs to stay alive during the lifetime of the PhysicsSystem. + void Init(uint inMaxBodies, uint inNumBodyMutexes, uint inMaxBodyPairs, uint inMaxContactConstraints, const BroadPhaseLayerInterface &inBroadPhaseLayerInterface, const ObjectVsBroadPhaseLayerFilter &inObjectVsBroadPhaseLayerFilter, const ObjectLayerPairFilter &inObjectLayerPairFilter); + + /// Listener that is notified whenever a body is activated/deactivated + void SetBodyActivationListener(BodyActivationListener *inListener) { mBodyManager.SetBodyActivationListener(inListener); } + BodyActivationListener * GetBodyActivationListener() const { return mBodyManager.GetBodyActivationListener(); } + + /// Listener that is notified whenever a contact point between two bodies is added/updated/removed + void SetContactListener(ContactListener *inListener) { mContactManager.SetContactListener(inListener); } + ContactListener * GetContactListener() const { return mContactManager.GetContactListener(); } + + /// Listener that is notified whenever a contact point between a soft body and another body + void SetSoftBodyContactListener(SoftBodyContactListener *inListener) { mSoftBodyContactListener = inListener; } + SoftBodyContactListener * GetSoftBodyContactListener() const { return mSoftBodyContactListener; } + + /// Set the function that combines the friction of two bodies and returns it + /// Default method is the geometric mean: sqrt(friction1 * friction2). + void SetCombineFriction(ContactConstraintManager::CombineFunction inCombineFriction) { mContactManager.SetCombineFriction(inCombineFriction); } + ContactConstraintManager::CombineFunction GetCombineFriction() const { return mContactManager.GetCombineFriction(); } + + /// Set the function that combines the restitution of two bodies and returns it + /// Default method is max(restitution1, restitution1) + void SetCombineRestitution(ContactConstraintManager::CombineFunction inCombineRestition) { mContactManager.SetCombineRestitution(inCombineRestition); } + ContactConstraintManager::CombineFunction GetCombineRestitution() const { return mContactManager.GetCombineRestitution(); } + + /// Control the main constants of the physics simulation + void SetPhysicsSettings(const PhysicsSettings &inSettings) { mPhysicsSettings = inSettings; } + const PhysicsSettings & GetPhysicsSettings() const { return mPhysicsSettings; } + + /// Access to the body interface. This interface allows to to create / remove bodies and to change their properties. + const BodyInterface & GetBodyInterface() const { return mBodyInterfaceLocking; } + BodyInterface & GetBodyInterface() { return mBodyInterfaceLocking; } + const BodyInterface & GetBodyInterfaceNoLock() const { return mBodyInterfaceNoLock; } ///< Version that does not lock the bodies, use with great care! + BodyInterface & GetBodyInterfaceNoLock() { return mBodyInterfaceNoLock; } ///< Version that does not lock the bodies, use with great care! + + /// Access to the broadphase interface that allows coarse collision queries + const BroadPhaseQuery & GetBroadPhaseQuery() const { return *mBroadPhase; } + + /// Interface that allows fine collision queries against first the broad phase and then the narrow phase. + const NarrowPhaseQuery & GetNarrowPhaseQuery() const { return mNarrowPhaseQueryLocking; } + const NarrowPhaseQuery & GetNarrowPhaseQueryNoLock() const { return mNarrowPhaseQueryNoLock; } ///< Version that does not lock the bodies, use with great care! + + /// Add constraint to the world + void AddConstraint(Constraint *inConstraint) { mConstraintManager.Add(&inConstraint, 1); } + + /// Remove constraint from the world + void RemoveConstraint(Constraint *inConstraint) { mConstraintManager.Remove(&inConstraint, 1); } + + /// Batch add constraints. Note that the inConstraints array is allowed to have nullptrs, these will be ignored. + void AddConstraints(Constraint **inConstraints, int inNumber) { mConstraintManager.Add(inConstraints, inNumber); } + + /// Batch remove constraints. Note that the inConstraints array is allowed to have nullptrs, these will be ignored. + void RemoveConstraints(Constraint **inConstraints, int inNumber) { mConstraintManager.Remove(inConstraints, inNumber); } + + /// Get a list of all constraints + Constraints GetConstraints() const { return mConstraintManager.GetConstraints(); } + + /// Optimize the broadphase, needed only if you've added many bodies prior to calling Update() for the first time. + void OptimizeBroadPhase(); + + /// Adds a new step listener + void AddStepListener(PhysicsStepListener *inListener); + + /// Removes a step listener + void RemoveStepListener(PhysicsStepListener *inListener); + + /// Simulate the system. + /// The world steps for a total of inDeltaTime seconds. This is divided in inCollisionSteps iterations. + /// Each iteration consists of collision detection followed by an integration step. + /// This function internally spawns jobs using inJobSystem and waits for them to complete, so no jobs will be running when this function returns. + EPhysicsUpdateError Update(float inDeltaTime, int inCollisionSteps, TempAllocator *inTempAllocator, JobSystem *inJobSystem); + + /// Saving state for replay + void SaveState(StateRecorder &inStream, EStateRecorderState inState = EStateRecorderState::All, const StateRecorderFilter *inFilter = nullptr) const; + + /// Restoring state for replay. Returns false if failed. + bool RestoreState(StateRecorder &inStream); + + /// Saving state of a single body. + void SaveBodyState(const Body &inBody, StateRecorder &inStream) const; + + /// Restoring state of a single body. + void RestoreBodyState(Body &ioBody, StateRecorder &inStream); + +#ifdef JPH_DEBUG_RENDERER + // Drawing properties + static bool sDrawMotionQualityLinearCast; ///< Draw debug info for objects that perform continuous collision detection through the linear cast motion quality + + /// Draw the state of the bodies (debugging purposes) + void DrawBodies(const BodyManager::DrawSettings &inSettings, DebugRenderer *inRenderer, const BodyDrawFilter *inBodyFilter = nullptr) { mBodyManager.Draw(inSettings, mPhysicsSettings, inRenderer, inBodyFilter); } + + /// Draw the constraints only (debugging purposes) + void DrawConstraints(DebugRenderer *inRenderer) { mConstraintManager.DrawConstraints(inRenderer); } + + /// Draw the constraint limits only (debugging purposes) + void DrawConstraintLimits(DebugRenderer *inRenderer) { mConstraintManager.DrawConstraintLimits(inRenderer); } + + /// Draw the constraint reference frames only (debugging purposes) + void DrawConstraintReferenceFrame(DebugRenderer *inRenderer) { mConstraintManager.DrawConstraintReferenceFrame(inRenderer); } +#endif // JPH_DEBUG_RENDERER + + /// Set gravity value + void SetGravity(Vec3Arg inGravity) { mGravity = inGravity; } + Vec3 GetGravity() const { return mGravity; } + + /// Returns a locking interface that won't actually lock the body. Use with great care! + inline const BodyLockInterfaceNoLock & GetBodyLockInterfaceNoLock() const { return mBodyLockInterfaceNoLock; } + + /// Returns a locking interface that locks the body so other threads cannot modify it. + inline const BodyLockInterfaceLocking & GetBodyLockInterface() const { return mBodyLockInterfaceLocking; } + + /// Get an broadphase layer filter that uses the default pair filter and a specified object layer to determine if broadphase layers collide + DefaultBroadPhaseLayerFilter GetDefaultBroadPhaseLayerFilter(ObjectLayer inLayer) const { return DefaultBroadPhaseLayerFilter(*mObjectVsBroadPhaseLayerFilter, inLayer); } + + /// Get an object layer filter that uses the default pair filter and a specified layer to determine if layers collide + DefaultObjectLayerFilter GetDefaultLayerFilter(ObjectLayer inLayer) const { return DefaultObjectLayerFilter(*mObjectLayerPairFilter, inLayer); } + + /// Gets the current amount of bodies that are in the body manager + uint GetNumBodies() const { return mBodyManager.GetNumBodies(); } + + /// Gets the current amount of active bodies that are in the body manager + uint32 GetNumActiveBodies(EBodyType inType) const { return mBodyManager.GetNumActiveBodies(inType); } + + /// Get the maximum amount of bodies that this physics system supports + uint GetMaxBodies() const { return mBodyManager.GetMaxBodies(); } + + /// Helper struct that counts the number of bodies of each type + using BodyStats = BodyManager::BodyStats; + + /// Get stats about the bodies in the body manager (slow, iterates through all bodies) + BodyStats GetBodyStats() const { return mBodyManager.GetBodyStats(); } + + /// Get copy of the list of all bodies under protection of a lock. + /// @param outBodyIDs On return, this will contain the list of BodyIDs + void GetBodies(BodyIDVector &outBodyIDs) const { return mBodyManager.GetBodyIDs(outBodyIDs); } + + /// Get copy of the list of active bodies under protection of a lock. + /// @param inType The type of bodies to get + /// @param outBodyIDs On return, this will contain the list of BodyIDs + void GetActiveBodies(EBodyType inType, BodyIDVector &outBodyIDs) const { return mBodyManager.GetActiveBodies(inType, outBodyIDs); } + + /// Get the list of active bodies, use GetNumActiveBodies() to find out how long the list is. + /// Note: Not thread safe. The active bodies list can change at any moment when other threads are doing work. Use GetActiveBodies() if you need a thread safe version. + const BodyID * GetActiveBodiesUnsafe(EBodyType inType) const { return mBodyManager.GetActiveBodiesUnsafe(inType); } + + /// Check if 2 bodies were in contact during the last simulation step. Since contacts are only detected between active bodies, so at least one of the bodies must be active in order for this function to work. + /// It queries the state at the time of the last PhysicsSystem::Update and will return true if the bodies were in contact, even if one of the bodies was moved / removed afterwards. + /// This function can be called from any thread when the PhysicsSystem::Update is not running. During PhysicsSystem::Update this function is only valid during contact callbacks: + /// - During the ContactListener::OnContactAdded callback this function can be used to determine if a different contact pair between the bodies was active in the previous simulation step (function returns true) or if this is the first step that the bodies are touching (function returns false). + /// - During the ContactListener::OnContactRemoved callback this function can be used to determine if this is the last contact pair between the bodies (function returns false) or if there are other contacts still present (function returns true). + bool WereBodiesInContact(const BodyID &inBody1ID, const BodyID &inBody2ID) const { return mContactManager.WereBodiesInContact(inBody1ID, inBody2ID); } + + /// Get the bounding box of all bodies in the physics system + AABox GetBounds() const { return mBroadPhase->GetBounds(); } + +#ifdef JPH_TRACK_BROADPHASE_STATS + /// Trace the accumulated broadphase stats to the TTY + void ReportBroadphaseStats() { mBroadPhase->ReportStats(); } +#endif // JPH_TRACK_BROADPHASE_STATS + +private: + using CCDBody = PhysicsUpdateContext::Step::CCDBody; + + // Various job entry points + void JobStepListeners(PhysicsUpdateContext::Step *ioStep); + void JobDetermineActiveConstraints(PhysicsUpdateContext::Step *ioStep) const; + void JobApplyGravity(const PhysicsUpdateContext *ioContext, PhysicsUpdateContext::Step *ioStep); + void JobSetupVelocityConstraints(float inDeltaTime, PhysicsUpdateContext::Step *ioStep) const; + void JobBuildIslandsFromConstraints(PhysicsUpdateContext *ioContext, PhysicsUpdateContext::Step *ioStep); + void JobFindCollisions(PhysicsUpdateContext::Step *ioStep, int inJobIndex); + void JobFinalizeIslands(PhysicsUpdateContext *ioContext); + void JobBodySetIslandIndex(); + void JobSolveVelocityConstraints(PhysicsUpdateContext *ioContext, PhysicsUpdateContext::Step *ioStep); + void JobPreIntegrateVelocity(PhysicsUpdateContext *ioContext, PhysicsUpdateContext::Step *ioStep); + void JobIntegrateVelocity(const PhysicsUpdateContext *ioContext, PhysicsUpdateContext::Step *ioStep); + void JobPostIntegrateVelocity(PhysicsUpdateContext *ioContext, PhysicsUpdateContext::Step *ioStep) const; + void JobFindCCDContacts(const PhysicsUpdateContext *ioContext, PhysicsUpdateContext::Step *ioStep); + void JobResolveCCDContacts(PhysicsUpdateContext *ioContext, PhysicsUpdateContext::Step *ioStep); + void JobContactRemovedCallbacks(const PhysicsUpdateContext::Step *ioStep); + void JobSolvePositionConstraints(PhysicsUpdateContext *ioContext, PhysicsUpdateContext::Step *ioStep); + void JobSoftBodyPrepare(PhysicsUpdateContext *ioContext, PhysicsUpdateContext::Step *ioStep); + void JobSoftBodyCollide(PhysicsUpdateContext *ioContext) const; + void JobSoftBodySimulate(PhysicsUpdateContext *ioContext, uint inThreadIndex) const; + void JobSoftBodyFinalize(PhysicsUpdateContext *ioContext); + + /// Tries to spawn a new FindCollisions job if max concurrency hasn't been reached yet + void TrySpawnJobFindCollisions(PhysicsUpdateContext::Step *ioStep) const; + + using ContactAllocator = ContactConstraintManager::ContactAllocator; + + /// Process narrow phase for a single body pair + void ProcessBodyPair(ContactAllocator &ioContactAllocator, const BodyPair &inBodyPair); + + /// This helper batches up bodies that need to put to sleep to avoid contention on the activation mutex + class BodiesToSleep; + + /// Called at the end of JobSolveVelocityConstraints to check if bodies need to go to sleep and to update their bounding box in the broadphase + void CheckSleepAndUpdateBounds(uint32 inIslandIndex, const PhysicsUpdateContext *ioContext, const PhysicsUpdateContext::Step *ioStep, BodiesToSleep &ioBodiesToSleep); + + /// Number of constraints to process at once in JobDetermineActiveConstraints + static constexpr int cDetermineActiveConstraintsBatchSize = 64; + + /// Number of constraints to process at once in JobSetupVelocityConstraints, we want a low number of threads working on this so we take fairly large batches + static constexpr int cSetupVelocityConstraintsBatchSize = 256; + + /// Number of bodies to process at once in JobApplyGravity + static constexpr int cApplyGravityBatchSize = 64; + + /// Number of active bodies to test for collisions per batch + static constexpr int cActiveBodiesBatchSize = 16; + + /// Number of active bodies to integrate velocities for + static constexpr int cIntegrateVelocityBatchSize = 64; + + /// Number of contacts that need to be queued before another narrow phase job is started + static constexpr int cNarrowPhaseBatchSize = 16; + + /// Number of continuous collision shape casts that need to be queued before another job is started + static constexpr int cNumCCDBodiesPerJob = 4; + + /// Broadphase layer filter that decides if two objects can collide + const ObjectVsBroadPhaseLayerFilter *mObjectVsBroadPhaseLayerFilter = nullptr; + + /// Object layer filter that decides if two objects can collide + const ObjectLayerPairFilter *mObjectLayerPairFilter = nullptr; + + /// The body manager keeps track which bodies are in the simulation + BodyManager mBodyManager; + + /// Body locking interfaces + BodyLockInterfaceNoLock mBodyLockInterfaceNoLock { mBodyManager }; + BodyLockInterfaceLocking mBodyLockInterfaceLocking { mBodyManager }; + + /// Body interfaces + BodyInterface mBodyInterfaceNoLock; + BodyInterface mBodyInterfaceLocking; + + /// Narrow phase query interface + NarrowPhaseQuery mNarrowPhaseQueryNoLock; + NarrowPhaseQuery mNarrowPhaseQueryLocking; + + /// The broadphase does quick collision detection between body pairs + BroadPhase * mBroadPhase = nullptr; + + /// The soft body contact listener + SoftBodyContactListener * mSoftBodyContactListener = nullptr; + + /// Simulation settings + PhysicsSettings mPhysicsSettings; + + /// The contact manager resolves all contacts during a simulation step + ContactConstraintManager mContactManager; + + /// All non-contact constraints + ConstraintManager mConstraintManager; + + /// Keeps track of connected bodies and builds islands for multithreaded velocity/position update + IslandBuilder mIslandBuilder; + + /// Will split large islands into smaller groups of bodies that can be processed in parallel + LargeIslandSplitter mLargeIslandSplitter; + + /// Mutex protecting mStepListeners + Mutex mStepListenersMutex; + + /// List of physics step listeners + using StepListeners = Array; + StepListeners mStepListeners; + + /// This is the global gravity vector + Vec3 mGravity = Vec3(0, -9.81f, 0); + + /// Previous frame's delta time of one sub step to allow scaling previous frame's constraint impulses + float mPreviousStepDeltaTime = 0.0f; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsUpdateContext.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsUpdateContext.cpp new file mode 100644 index 00000000000..7c60ae6be66 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsUpdateContext.cpp @@ -0,0 +1,23 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include + +JPH_NAMESPACE_BEGIN + +PhysicsUpdateContext::PhysicsUpdateContext(TempAllocator &inTempAllocator) : + mTempAllocator(&inTempAllocator), + mSteps(inTempAllocator) +{ +} + +PhysicsUpdateContext::~PhysicsUpdateContext() +{ + JPH_ASSERT(mBodyPairs == nullptr); + JPH_ASSERT(mActiveConstraints == nullptr); +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsUpdateContext.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsUpdateContext.h new file mode 100644 index 00000000000..fbcdb261775 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsUpdateContext.h @@ -0,0 +1,172 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +class PhysicsSystem; +class IslandBuilder; +class Constraint; +class TempAllocator; +class SoftBodyUpdateContext; + +/// Information used during the Update call +class PhysicsUpdateContext : public NonCopyable +{ +public: + /// Destructor + explicit PhysicsUpdateContext(TempAllocator &inTempAllocator); + ~PhysicsUpdateContext(); + + static constexpr int cMaxConcurrency = 32; ///< Maximum supported amount of concurrent jobs + + using JobHandleArray = StaticArray; + + struct Step; + + struct BodyPairQueue + { + atomic mWriteIdx { 0 }; ///< Next index to write in mBodyPair array (need to add thread index * mMaxBodyPairsPerQueue and modulo mMaxBodyPairsPerQueue) + uint8 mPadding1[JPH_CACHE_LINE_SIZE - sizeof(atomic)];///< Moved to own cache line to avoid conflicts with consumer jobs + + atomic mReadIdx { 0 }; ///< Next index to read in mBodyPair array (need to add thread index * mMaxBodyPairsPerQueue and modulo mMaxBodyPairsPerQueue) + uint8 mPadding2[JPH_CACHE_LINE_SIZE - sizeof(atomic)];///< Moved to own cache line to avoid conflicts with producer/consumer jobs + }; + + using BodyPairQueues = StaticArray; + + using JobMask = uint32; ///< A mask that has as many bits as we can have concurrent jobs + static_assert(sizeof(JobMask) * 8 >= cMaxConcurrency); + + /// Structure that contains data needed for each collision step. + struct Step + { + Step() = default; + Step(const Step &) { JPH_ASSERT(false); } // vector needs a copy constructor, but we're never going to call it + + PhysicsUpdateContext *mContext; ///< The physics update context + + bool mIsFirst; ///< If this is the first step + bool mIsLast; ///< If this is the last step + + BroadPhase::UpdateState mBroadPhaseUpdateState; ///< Handle returned by Broadphase::UpdatePrepare + + uint32 mNumActiveBodiesAtStepStart; ///< Number of bodies that were active at the start of the physics update step. Only these bodies will receive gravity (they are the first N in the active body list). + + atomic mDetermineActiveConstraintReadIdx { 0 }; ///< Next constraint for determine active constraints + uint8 mPadding1[JPH_CACHE_LINE_SIZE - sizeof(atomic)];///< Padding to avoid sharing cache line with the next atomic + + atomic mNumActiveConstraints { 0 }; ///< Number of constraints in the mActiveConstraints array + uint8 mPadding2[JPH_CACHE_LINE_SIZE - sizeof(atomic)];///< Padding to avoid sharing cache line with the next atomic + + atomic mSetupVelocityConstraintsReadIdx { 0 }; ///< Next constraint for setting up velocity constraints + uint8 mPadding3[JPH_CACHE_LINE_SIZE - sizeof(atomic)];///< Padding to avoid sharing cache line with the next atomic + + atomic mStepListenerReadIdx { 0 }; ///< Next step listener to call + uint8 mPadding4[JPH_CACHE_LINE_SIZE - sizeof(atomic)];///< Padding to avoid sharing cache line with the next atomic + + atomic mApplyGravityReadIdx { 0 }; ///< Next body to apply gravity to + uint8 mPadding5[JPH_CACHE_LINE_SIZE - sizeof(atomic)];///< Padding to avoid sharing cache line with the next atomic + + atomic mActiveBodyReadIdx { 0 }; ///< Index of fist active body that has not yet been processed by the broadphase + uint8 mPadding6[JPH_CACHE_LINE_SIZE - sizeof(atomic)];///< Padding to avoid sharing cache line with the next atomic + + BodyPairQueues mBodyPairQueues; ///< Queues in which to put body pairs that need to be tested by the narrowphase + + uint32 mMaxBodyPairsPerQueue; ///< Amount of body pairs that we can queue per queue + + atomic mActiveFindCollisionJobs; ///< A bitmask that indicates which jobs are still active + + atomic mNumBodyPairs { 0 }; ///< The number of body pairs found in this step (used to size the contact cache in the next step) + atomic mNumManifolds { 0 }; ///< The number of manifolds found in this step (used to size the contact cache in the next step) + + atomic mSolveVelocityConstraintsNextIsland { 0 }; ///< Next island that needs to be processed for the solve velocity constraints step (doesn't need own cache line since position jobs don't run at same time) + atomic mSolvePositionConstraintsNextIsland { 0 }; ///< Next island that needs to be processed for the solve position constraints step (doesn't need own cache line since velocity jobs don't run at same time) + + /// Contains the information needed to cast a body through the scene to do continuous collision detection + struct CCDBody + { + CCDBody(BodyID inBodyID1, Vec3Arg inDeltaPosition, float inLinearCastThresholdSq, float inMaxPenetration) : mDeltaPosition(inDeltaPosition), mBodyID1(inBodyID1), mLinearCastThresholdSq(inLinearCastThresholdSq), mMaxPenetration(inMaxPenetration) { } + + Vec3 mDeltaPosition; ///< Desired rotation step + Vec3 mContactNormal; ///< World space normal of closest hit (only valid if mFractionPlusSlop < 1) + RVec3 mContactPointOn2; ///< World space contact point on body 2 of closest hit (only valid if mFractionPlusSlop < 1) + BodyID mBodyID1; ///< Body 1 (the body that is performing collision detection) + BodyID mBodyID2; ///< Body 2 (the body of the closest hit, only valid if mFractionPlusSlop < 1) + SubShapeID mSubShapeID2; ///< Sub shape of body 2 that was hit (only valid if mFractionPlusSlop < 1) + float mFraction = 1.0f; ///< Fraction at which the hit occurred + float mFractionPlusSlop = 1.0f; ///< Fraction at which the hit occurred + extra delta to allow body to penetrate by mMaxPenetration + float mLinearCastThresholdSq; ///< Maximum allowed squared movement before doing a linear cast (determined by inner radius of shape) + float mMaxPenetration; ///< Maximum allowed penetration (determined by inner radius of shape) + ContactSettings mContactSettings; ///< The contact settings for this contact + }; + atomic mIntegrateVelocityReadIdx { 0 }; ///< Next active body index to take when integrating velocities + CCDBody * mCCDBodies = nullptr; ///< List of bodies that need to do continuous collision detection + uint32 mCCDBodiesCapacity = 0; ///< Capacity of the mCCDBodies list + atomic mNumCCDBodies = 0; ///< Number of CCD bodies in mCCDBodies + atomic mNextCCDBody { 0 }; ///< Next unprocessed body index in mCCDBodies + int * mActiveBodyToCCDBody = nullptr; ///< A mapping between an index in BodyManager::mActiveBodies and the index in mCCDBodies + uint32 mNumActiveBodyToCCDBody = 0; ///< Number of indices in mActiveBodyToCCDBody + + // Jobs in order of execution (some run in parallel) + JobHandle mBroadPhasePrepare; ///< Prepares the new tree in the background + JobHandleArray mStepListeners; ///< Listeners to notify of the beginning of a physics step + JobHandleArray mDetermineActiveConstraints; ///< Determine which constraints will be active during this step + JobHandleArray mApplyGravity; ///< Update velocities of bodies with gravity + JobHandleArray mFindCollisions; ///< Find all collisions between active bodies an the world + JobHandle mUpdateBroadphaseFinalize; ///< Swap the newly built tree with the current tree + JobHandleArray mSetupVelocityConstraints; ///< Calculate properties for all constraints in the constraint manager + JobHandle mBuildIslandsFromConstraints; ///< Go over all constraints and assign the bodies they're attached to to an island + JobHandle mFinalizeIslands; ///< Finalize calculation simulation islands + JobHandle mBodySetIslandIndex; ///< Set the current island index on each body (not used by the simulation, only for drawing purposes) + JobHandleArray mSolveVelocityConstraints; ///< Solve the constraints in the velocity domain + JobHandle mPreIntegrateVelocity; ///< Setup integration of all body positions + JobHandleArray mIntegrateVelocity; ///< Integrate all body positions + JobHandle mPostIntegrateVelocity; ///< Finalize integration of all body positions + JobHandle mResolveCCDContacts; ///< Updates the positions and velocities for all bodies that need continuous collision detection + JobHandleArray mSolvePositionConstraints; ///< Solve all constraints in the position domain + JobHandle mContactRemovedCallbacks; ///< Calls the contact removed callbacks + JobHandle mSoftBodyPrepare; ///< Prepares updating the soft bodies + JobHandleArray mSoftBodyCollide; ///< Finds all colliding shapes for soft bodies + JobHandleArray mSoftBodySimulate; ///< Simulates all particles + JobHandle mSoftBodyFinalize; ///< Finalizes the soft body update + JobHandle mStartNextStep; ///< Job that kicks the next step (empty for the last step) + }; + + using Steps = std::vector>; + + /// Maximum amount of concurrent jobs on this machine + int GetMaxConcurrency() const { const int max_concurrency = PhysicsUpdateContext::cMaxConcurrency; return min(max_concurrency, mJobSystem->GetMaxConcurrency()); } ///< Need to put max concurrency in temp var as min requires a reference + + PhysicsSystem * mPhysicsSystem; ///< The physics system we belong to + TempAllocator * mTempAllocator; ///< Temporary allocator used during the update + JobSystem * mJobSystem; ///< Job system that processes jobs + JobSystem::Barrier * mBarrier; ///< Barrier used to wait for all physics jobs to complete + + float mStepDeltaTime; ///< Delta time for a simulation step (collision step) + float mWarmStartImpulseRatio; ///< Ratio of this step delta time vs last step + atomic mErrors { 0 }; ///< Errors that occurred during the update, actual type is EPhysicsUpdateError + + Constraint ** mActiveConstraints = nullptr; ///< Constraints that were active at the start of the physics update step (activating bodies can activate constraints and we need a consistent snapshot). Only these constraints will be resolved. + + BodyPair * mBodyPairs = nullptr; ///< A list of body pairs found by the broadphase + + IslandBuilder * mIslandBuilder; ///< Keeps track of connected bodies and builds islands for multithreaded velocity/position update + + Steps mSteps; + + uint mNumSoftBodies; ///< Number of active soft bodies in the simulation + SoftBodyUpdateContext * mSoftBodyUpdateContexts = nullptr; ///< Contexts for updating soft bodies + atomic mSoftBodyToCollide { 0 }; ///< Next soft body to take when running SoftBodyCollide jobs +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Ragdoll/Ragdoll.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Ragdoll/Ragdoll.cpp new file mode 100644 index 00000000000..f66212f2647 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Ragdoll/Ragdoll.cpp @@ -0,0 +1,699 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(RagdollSettings::Part) +{ + JPH_ADD_BASE_CLASS(RagdollSettings::Part, BodyCreationSettings) + + JPH_ADD_ATTRIBUTE(RagdollSettings::Part, mToParent) +} + +JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(RagdollSettings::AdditionalConstraint) +{ + JPH_ADD_ATTRIBUTE(RagdollSettings::AdditionalConstraint, mBodyIdx) + JPH_ADD_ATTRIBUTE(RagdollSettings::AdditionalConstraint, mConstraint) +} + +JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(RagdollSettings) +{ + JPH_ADD_ATTRIBUTE(RagdollSettings, mSkeleton) + JPH_ADD_ATTRIBUTE(RagdollSettings, mParts) + JPH_ADD_ATTRIBUTE(RagdollSettings, mAdditionalConstraints) +} + +static inline BodyInterface &sGetBodyInterface(PhysicsSystem *inSystem, bool inLockBodies) +{ + return inLockBodies? inSystem->GetBodyInterface() : inSystem->GetBodyInterfaceNoLock(); +} + +static inline const BodyLockInterface &sGetBodyLockInterface(const PhysicsSystem *inSystem, bool inLockBodies) +{ + return inLockBodies? static_cast(inSystem->GetBodyLockInterface()) : static_cast(inSystem->GetBodyLockInterfaceNoLock()); +} + +bool RagdollSettings::Stabilize() +{ + // Based on: Stop my Constraints from Blowing Up! - Oliver Strunk (Havok) + // Do 2 things: + // 1. Limit the mass ratios between parents and children (slide 16) + // 2. Increase the inertia of parents so that they're bigger or equal to the sum of their children (slide 34) + + // If we don't have any joints there's nothing to stabilize + if (mSkeleton->GetJointCount() == 0) + return true; + + // The skeleton can contain one or more static bodies. We can't modify the mass for those so we start a new stabilization chain for each joint under a static body until we reach the next static body. + // This array keeps track of which joints have been processed. + Array visited; + visited.resize(mSkeleton->GetJointCount()); + for (size_t v = 0; v < visited.size(); ++v) + { + // Mark static bodies as visited so we won't process these + Part &p = mParts[v]; + bool has_mass_properties = p.HasMassProperties(); + visited[v] = !has_mass_properties; + + if (has_mass_properties && p.mOverrideMassProperties != EOverrideMassProperties::MassAndInertiaProvided) + { + // Mass properties not yet calculated, do it now + p.mMassPropertiesOverride = p.GetMassProperties(); + p.mOverrideMassProperties = EOverrideMassProperties::MassAndInertiaProvided; + } + } + + // Find first unvisited part that either has no parent or that has a parent that was visited + for (int first_idx = 0; first_idx < mSkeleton->GetJointCount(); ++first_idx) + { + int first_idx_parent = mSkeleton->GetJoint(first_idx).mParentJointIndex; + if (!visited[first_idx] && (first_idx_parent == -1 || visited[first_idx_parent])) + { + // Find all children of first_idx and their children up to the next static part + int next_to_process = 0; + Array indices; + indices.reserve(mSkeleton->GetJointCount()); + visited[first_idx] = true; + indices.push_back(first_idx); + do + { + int parent_idx = indices[next_to_process++]; + for (int child_idx = 0; child_idx < mSkeleton->GetJointCount(); ++child_idx) + if (!visited[child_idx] && mSkeleton->GetJoint(child_idx).mParentJointIndex == parent_idx) + { + visited[child_idx] = true; + indices.push_back(child_idx); + } + } while (next_to_process < (int)indices.size()); + + // If there's only 1 body, we can't redistribute mass + if (indices.size() == 1) + continue; + + const float cMinMassRatio = 0.8f; + const float cMaxMassRatio = 1.2f; + + // Ensure that the mass ratio from parent to child is within a range + float total_mass_ratio = 1.0f; + Array mass_ratios; + mass_ratios.resize(mSkeleton->GetJointCount()); + mass_ratios[indices[0]] = 1.0f; + for (int i = 1; i < (int)indices.size(); ++i) + { + int child_idx = indices[i]; + int parent_idx = mSkeleton->GetJoint(child_idx).mParentJointIndex; + float ratio = mParts[child_idx].mMassPropertiesOverride.mMass / mParts[parent_idx].mMassPropertiesOverride.mMass; + mass_ratios[child_idx] = mass_ratios[parent_idx] * Clamp(ratio, cMinMassRatio, cMaxMassRatio); + total_mass_ratio += mass_ratios[child_idx]; + } + + // Calculate total mass of this chain + float total_mass = 0.0f; + for (int idx : indices) + total_mass += mParts[idx].mMassPropertiesOverride.mMass; + + // Calculate how much mass belongs to a ratio of 1 + float ratio_to_mass = total_mass / total_mass_ratio; + + // Adjust all masses and inertia tensors for the new mass + for (int i : indices) + { + Part &p = mParts[i]; + float old_mass = p.mMassPropertiesOverride.mMass; + float new_mass = mass_ratios[i] * ratio_to_mass; + p.mMassPropertiesOverride.mMass = new_mass; + p.mMassPropertiesOverride.mInertia *= new_mass / old_mass; + p.mMassPropertiesOverride.mInertia.SetColumn4(3, Vec4(0, 0, 0, 1)); + } + + const float cMaxInertiaIncrease = 2.0f; + + // Get the principal moments of inertia for all parts + struct Principal + { + Mat44 mRotation; + Vec3 mDiagonal; + float mChildSum = 0.0f; + }; + Array principals; + principals.resize(mParts.size()); + for (int i : indices) + if (!mParts[i].mMassPropertiesOverride.DecomposePrincipalMomentsOfInertia(principals[i].mRotation, principals[i].mDiagonal)) + { + JPH_ASSERT(false, "Failed to decompose the inertia tensor!"); + return false; + } + + // Calculate sum of child inertias + // Walk backwards so we sum the leaves first + for (int i = (int)indices.size() - 1; i > 0; --i) + { + int child_idx = indices[i]; + int parent_idx = mSkeleton->GetJoint(child_idx).mParentJointIndex; + principals[parent_idx].mChildSum += principals[child_idx].mDiagonal[0] + principals[child_idx].mChildSum; + } + + // Adjust inertia tensors for all parts + for (int i : indices) + { + Part &p = mParts[i]; + Principal &principal = principals[i]; + if (principal.mChildSum != 0.0f) + { + // Calculate minimum inertia this object should have based on it children + float minimum = min(cMaxInertiaIncrease * principal.mDiagonal[0], principal.mChildSum); + principal.mDiagonal = Vec3::sMax(principal.mDiagonal, Vec3::sReplicate(minimum)); + + // Recalculate moment of inertia in body space + p.mMassPropertiesOverride.mInertia = principal.mRotation * Mat44::sScale(principal.mDiagonal) * principal.mRotation.Inversed3x3(); + } + } + } + } + + return true; +} + +void RagdollSettings::DisableParentChildCollisions(const Mat44 *inJointMatrices, float inMinSeparationDistance) +{ + int joint_count = mSkeleton->GetJointCount(); + JPH_ASSERT(joint_count == (int)mParts.size()); + + // Create a group filter table that disables collisions between parent and child + Ref group_filter = new GroupFilterTable(joint_count); + for (int joint_idx = 0; joint_idx < joint_count; ++joint_idx) + { + int parent_joint = mSkeleton->GetJoint(joint_idx).mParentJointIndex; + if (parent_joint >= 0) + group_filter->DisableCollision(joint_idx, parent_joint); + } + + // If joint matrices are provided + if (inJointMatrices != nullptr) + { + // Loop over all joints + for (int j1 = 0; j1 < joint_count; ++j1) + { + // Shape and transform for joint 1 + const Part &part1 = mParts[j1]; + const Shape *shape1 = part1.GetShape(); + Vec3 scale1; + Mat44 com1 = (inJointMatrices[j1].PreTranslated(shape1->GetCenterOfMass())).Decompose(scale1); + + // Loop over all other joints + for (int j2 = j1 + 1; j2 < joint_count; ++j2) + if (group_filter->IsCollisionEnabled(j1, j2)) // Only if collision is still enabled we need to test + { + // Shape and transform for joint 2 + const Part &part2 = mParts[j2]; + const Shape *shape2 = part2.GetShape(); + Vec3 scale2; + Mat44 com2 = (inJointMatrices[j2].PreTranslated(shape2->GetCenterOfMass())).Decompose(scale2); + + // Collision settings + CollideShapeSettings settings; + settings.mActiveEdgeMode = EActiveEdgeMode::CollideWithAll; + settings.mBackFaceMode = EBackFaceMode::CollideWithBackFaces; + settings.mMaxSeparationDistance = inMinSeparationDistance; + + // Only check if one of the two bodies can become dynamic + if (part1.HasMassProperties() || part2.HasMassProperties()) + { + // If there is a collision, disable the collision between the joints + AnyHitCollisionCollector collector; + if (part1.HasMassProperties()) // Ensure that the first shape is always a dynamic one (we can't check mesh vs convex but we can check convex vs mesh) + CollisionDispatch::sCollideShapeVsShape(shape1, shape2, scale1, scale2, com1, com2, SubShapeIDCreator(), SubShapeIDCreator(), settings, collector); + else + CollisionDispatch::sCollideShapeVsShape(shape2, shape1, scale2, scale1, com2, com1, SubShapeIDCreator(), SubShapeIDCreator(), settings, collector); + if (collector.HadHit()) + group_filter->DisableCollision(j1, j2); + } + } + } + } + + // Loop over the body parts and assign them a sub group ID and the group filter + for (int joint_idx = 0; joint_idx < joint_count; ++joint_idx) + { + Part &part = mParts[joint_idx]; + part.mCollisionGroup.SetSubGroupID(joint_idx); + part.mCollisionGroup.SetGroupFilter(group_filter); + } +} + +void RagdollSettings::SaveBinaryState(StreamOut &inStream, bool inSaveShapes, bool inSaveGroupFilter) const +{ + BodyCreationSettings::ShapeToIDMap shape_to_id; + BodyCreationSettings::MaterialToIDMap material_to_id; + BodyCreationSettings::GroupFilterToIDMap group_filter_to_id; + + // Save skeleton + mSkeleton->SaveBinaryState(inStream); + + // Save parts + inStream.Write((uint32)mParts.size()); + for (const Part &p : mParts) + { + // Write body creation settings + p.SaveWithChildren(inStream, inSaveShapes? &shape_to_id : nullptr, inSaveShapes? &material_to_id : nullptr, inSaveGroupFilter? &group_filter_to_id : nullptr); + + // Save constraint + inStream.Write(p.mToParent != nullptr); + if (p.mToParent != nullptr) + p.mToParent->SaveBinaryState(inStream); + } + + // Save additional constraints + inStream.Write((uint32)mAdditionalConstraints.size()); + for (const AdditionalConstraint &c : mAdditionalConstraints) + { + // Save bodies indices + inStream.Write(c.mBodyIdx); + + // Save constraint + c.mConstraint->SaveBinaryState(inStream); + } +} + +RagdollSettings::RagdollResult RagdollSettings::sRestoreFromBinaryState(StreamIn &inStream) +{ + RagdollResult result; + + // Restore skeleton + Skeleton::SkeletonResult skeleton_result = Skeleton::sRestoreFromBinaryState(inStream); + if (skeleton_result.HasError()) + { + result.SetError(skeleton_result.GetError()); + return result; + } + + // Create ragdoll + Ref ragdoll = new RagdollSettings(); + ragdoll->mSkeleton = skeleton_result.Get(); + + BodyCreationSettings::IDToShapeMap id_to_shape; + BodyCreationSettings::IDToMaterialMap id_to_material; + BodyCreationSettings::IDToGroupFilterMap id_to_group_filter; + + // Reserve some memory to avoid frequent reallocations + id_to_shape.reserve(1024); + id_to_material.reserve(128); + id_to_group_filter.reserve(128); + + // Read parts + uint32 len = 0; + inStream.Read(len); + ragdoll->mParts.resize(len); + for (Part &p : ragdoll->mParts) + { + // Read creation settings + BodyCreationSettings::BCSResult bcs_result = BodyCreationSettings::sRestoreWithChildren(inStream, id_to_shape, id_to_material, id_to_group_filter); + if (bcs_result.HasError()) + { + result.SetError(bcs_result.GetError()); + return result; + } + static_cast(p) = bcs_result.Get(); + + // Read constraint + bool has_constraint = false; + inStream.Read(has_constraint); + if (has_constraint) + { + ConstraintSettings::ConstraintResult constraint_result = ConstraintSettings::sRestoreFromBinaryState(inStream); + if (constraint_result.HasError()) + { + result.SetError(constraint_result.GetError()); + return result; + } + p.mToParent = DynamicCast(constraint_result.Get().GetPtr()); + } + } + + // Read additional constraints + len = 0; + inStream.Read(len); + ragdoll->mAdditionalConstraints.resize(len); + for (AdditionalConstraint &c : ragdoll->mAdditionalConstraints) + { + // Read body indices + inStream.Read(c.mBodyIdx); + + // Read constraint + ConstraintSettings::ConstraintResult constraint_result = ConstraintSettings::sRestoreFromBinaryState(inStream); + if (constraint_result.HasError()) + { + result.SetError(constraint_result.GetError()); + return result; + } + c.mConstraint = DynamicCast(constraint_result.Get().GetPtr()); + } + + // Create mapping tables + ragdoll->CalculateBodyIndexToConstraintIndex(); + ragdoll->CalculateConstraintIndexToBodyIdxPair(); + + result.Set(ragdoll); + return result; +} + +Ragdoll *RagdollSettings::CreateRagdoll(CollisionGroup::GroupID inCollisionGroup, uint64 inUserData, PhysicsSystem *inSystem) const +{ + Ragdoll *r = new Ragdoll(inSystem); + r->mRagdollSettings = this; + r->mBodyIDs.reserve(mParts.size()); + r->mConstraints.reserve(mParts.size() + mAdditionalConstraints.size()); + + // Create bodies and constraints + BodyInterface &bi = inSystem->GetBodyInterface(); + Body **bodies = (Body **)JPH_STACK_ALLOC(mParts.size() * sizeof(Body *)); + int joint_idx = 0; + for (const Part &p : mParts) + { + Body *body2 = bi.CreateBody(p); + if (body2 == nullptr) + { + // Out of bodies, failed to create ragdoll + delete r; + return nullptr; + } + body2->GetCollisionGroup().SetGroupID(inCollisionGroup); + body2->SetUserData(inUserData); + + // Temporarily store body pointer for hooking up constraints + bodies[joint_idx] = body2; + + // Create constraint + if (p.mToParent != nullptr) + { + Body *body1 = bodies[mSkeleton->GetJoint(joint_idx).mParentJointIndex]; + r->mConstraints.push_back(p.mToParent->Create(*body1, *body2)); + } + + // Store body ID and constraint in parallel arrays + r->mBodyIDs.push_back(body2->GetID()); + + ++joint_idx; + } + + // Add additional constraints + for (const AdditionalConstraint &c : mAdditionalConstraints) + { + Body *body1 = bodies[c.mBodyIdx[0]]; + Body *body2 = bodies[c.mBodyIdx[1]]; + r->mConstraints.push_back(c.mConstraint->Create(*body1, *body2)); + } + + return r; +} + +void RagdollSettings::CalculateBodyIndexToConstraintIndex() +{ + mBodyIndexToConstraintIndex.clear(); + mBodyIndexToConstraintIndex.reserve(mParts.size()); + + int constraint_index = 0; + for (const Part &p : mParts) + { + if (p.mToParent != nullptr) + mBodyIndexToConstraintIndex.push_back(constraint_index++); + else + mBodyIndexToConstraintIndex.push_back(-1); + } +} + +void RagdollSettings::CalculateConstraintIndexToBodyIdxPair() +{ + mConstraintIndexToBodyIdxPair.clear(); + mConstraintIndexToBodyIdxPair.reserve(mParts.size() + mAdditionalConstraints.size()); + + // Add constraints between parts + int joint_idx = 0; + for (const Part &p : mParts) + { + if (p.mToParent != nullptr) + { + int parent_joint_idx = mSkeleton->GetJoint(joint_idx).mParentJointIndex; + mConstraintIndexToBodyIdxPair.emplace_back(parent_joint_idx, joint_idx); + } + + ++joint_idx; + } + + // Add additional constraints + for (const AdditionalConstraint &c : mAdditionalConstraints) + mConstraintIndexToBodyIdxPair.emplace_back(c.mBodyIdx[0], c.mBodyIdx[1]); +} + +Ragdoll::~Ragdoll() +{ + // Destroy all bodies + mSystem->GetBodyInterface().DestroyBodies(mBodyIDs.data(), (int)mBodyIDs.size()); +} + +void Ragdoll::AddToPhysicsSystem(EActivation inActivationMode, bool inLockBodies) +{ + // Scope for JPH_STACK_ALLOC + { + // Create copy of body ids since they will be shuffled + int num_bodies = (int)mBodyIDs.size(); + BodyID *bodies = (BodyID *)JPH_STACK_ALLOC(num_bodies * sizeof(BodyID)); + memcpy(bodies, mBodyIDs.data(), num_bodies * sizeof(BodyID)); + + // Insert bodies as a batch + BodyInterface &bi = sGetBodyInterface(mSystem, inLockBodies); + BodyInterface::AddState add_state = bi.AddBodiesPrepare(bodies, num_bodies); + bi.AddBodiesFinalize(bodies, num_bodies, add_state, inActivationMode); + } + + // Add all constraints + mSystem->AddConstraints((Constraint **)mConstraints.data(), (int)mConstraints.size()); +} + +void Ragdoll::RemoveFromPhysicsSystem(bool inLockBodies) +{ + // Remove all constraints before removing the bodies + mSystem->RemoveConstraints((Constraint **)mConstraints.data(), (int)mConstraints.size()); + + // Scope for JPH_STACK_ALLOC + { + // Create copy of body ids since they will be shuffled + int num_bodies = (int)mBodyIDs.size(); + BodyID *bodies = (BodyID *)JPH_STACK_ALLOC(num_bodies * sizeof(BodyID)); + memcpy(bodies, mBodyIDs.data(), num_bodies * sizeof(BodyID)); + + // Remove all bodies as a batch + sGetBodyInterface(mSystem, inLockBodies).RemoveBodies(bodies, num_bodies); + } +} + +void Ragdoll::Activate(bool inLockBodies) +{ + sGetBodyInterface(mSystem, inLockBodies).ActivateBodies(mBodyIDs.data(), (int)mBodyIDs.size()); +} + +bool Ragdoll::IsActive(bool inLockBodies) const +{ + // Lock the bodies + int body_count = (int)mBodyIDs.size(); + BodyLockMultiRead lock(sGetBodyLockInterface(mSystem, inLockBodies), mBodyIDs.data(), body_count); + + // Test if any body is active + for (int b = 0; b < body_count; ++b) + { + const Body *body = lock.GetBody(b); + if (body->IsActive()) + return true; + } + + return false; +} + +void Ragdoll::SetGroupID(CollisionGroup::GroupID inGroupID, bool inLockBodies) +{ + // Lock the bodies + int body_count = (int)mBodyIDs.size(); + BodyLockMultiWrite lock(sGetBodyLockInterface(mSystem, inLockBodies), mBodyIDs.data(), body_count); + + // Update group ID + for (int b = 0; b < body_count; ++b) + { + Body *body = lock.GetBody(b); + body->GetCollisionGroup().SetGroupID(inGroupID); + } +} + +void Ragdoll::SetPose(const SkeletonPose &inPose, bool inLockBodies) +{ + JPH_ASSERT(inPose.GetSkeleton() == mRagdollSettings->mSkeleton); + + SetPose(inPose.GetRootOffset(), inPose.GetJointMatrices().data(), inLockBodies); +} + +void Ragdoll::SetPose(RVec3Arg inRootOffset, const Mat44 *inJointMatrices, bool inLockBodies) +{ + // Move bodies instantly into the correct position + BodyInterface &bi = sGetBodyInterface(mSystem, inLockBodies); + for (int i = 0; i < (int)mBodyIDs.size(); ++i) + { + const Mat44 &joint = inJointMatrices[i]; + bi.SetPositionAndRotation(mBodyIDs[i], inRootOffset + joint.GetTranslation(), joint.GetQuaternion(), EActivation::DontActivate); + } +} + +void Ragdoll::GetPose(SkeletonPose &outPose, bool inLockBodies) +{ + JPH_ASSERT(outPose.GetSkeleton() == mRagdollSettings->mSkeleton); + + RVec3 root_offset; + GetPose(root_offset, outPose.GetJointMatrices().data(), inLockBodies); + outPose.SetRootOffset(root_offset); +} + +void Ragdoll::GetPose(RVec3 &outRootOffset, Mat44 *outJointMatrices, bool inLockBodies) +{ + // Lock the bodies + int body_count = (int)mBodyIDs.size(); + if (body_count == 0) + return; + BodyLockMultiRead lock(sGetBodyLockInterface(mSystem, inLockBodies), mBodyIDs.data(), body_count); + + // Get root matrix + const Body *root = lock.GetBody(0); + RMat44 root_transform = root->GetWorldTransform(); + outRootOffset = root_transform.GetTranslation(); + outJointMatrices[0] = Mat44(root_transform.GetColumn4(0), root_transform.GetColumn4(1), root_transform.GetColumn4(2), Vec4(0, 0, 0, 1)); + + // Get other matrices + for (int b = 1; b < body_count; ++b) + { + const Body *body = lock.GetBody(b); + RMat44 transform = body->GetWorldTransform(); + outJointMatrices[b] = Mat44(transform.GetColumn4(0), transform.GetColumn4(1), transform.GetColumn4(2), Vec4(Vec3(transform.GetTranslation() - outRootOffset), 1)); + } +} + +void Ragdoll::ResetWarmStart() +{ + for (TwoBodyConstraint *c : mConstraints) + c->ResetWarmStart(); +} + +void Ragdoll::DriveToPoseUsingKinematics(const SkeletonPose &inPose, float inDeltaTime, bool inLockBodies) +{ + JPH_ASSERT(inPose.GetSkeleton() == mRagdollSettings->mSkeleton); + + DriveToPoseUsingKinematics(inPose.GetRootOffset(), inPose.GetJointMatrices().data(), inDeltaTime, inLockBodies); +} + +void Ragdoll::DriveToPoseUsingKinematics(RVec3Arg inRootOffset, const Mat44 *inJointMatrices, float inDeltaTime, bool inLockBodies) +{ + // Move bodies into the correct position using kinematics + BodyInterface &bi = sGetBodyInterface(mSystem, inLockBodies); + for (int i = 0; i < (int)mBodyIDs.size(); ++i) + { + const Mat44 &joint = inJointMatrices[i]; + bi.MoveKinematic(mBodyIDs[i], inRootOffset + joint.GetTranslation(), joint.GetQuaternion(), inDeltaTime); + } +} + +void Ragdoll::DriveToPoseUsingMotors(const SkeletonPose &inPose) +{ + JPH_ASSERT(inPose.GetSkeleton() == mRagdollSettings->mSkeleton); + + // Move bodies into the correct position using constraints + for (int i = 0; i < (int)inPose.GetJointMatrices().size(); ++i) + { + int constraint_idx = mRagdollSettings->GetConstraintIndexForBodyIndex(i); + if (constraint_idx >= 0) + { + SwingTwistConstraint *constraint = (SwingTwistConstraint *)mConstraints[constraint_idx].GetPtr(); + + // Get desired rotation of this body relative to its parent + Quat joint_transform = inPose.GetJoint(i).mRotation; + + // Drive constraint to target + constraint->SetSwingMotorState(EMotorState::Position); + constraint->SetTwistMotorState(EMotorState::Position); + constraint->SetTargetOrientationBS(joint_transform); + } + } +} + +void Ragdoll::SetLinearAndAngularVelocity(Vec3Arg inLinearVelocity, Vec3Arg inAngularVelocity, bool inLockBodies) +{ + BodyInterface &bi = sGetBodyInterface(mSystem, inLockBodies); + for (BodyID body_id : mBodyIDs) + bi.SetLinearAndAngularVelocity(body_id, inLinearVelocity, inAngularVelocity); +} + +void Ragdoll::SetLinearVelocity(Vec3Arg inLinearVelocity, bool inLockBodies) +{ + BodyInterface &bi = sGetBodyInterface(mSystem, inLockBodies); + for (BodyID body_id : mBodyIDs) + bi.SetLinearVelocity(body_id, inLinearVelocity); +} + +void Ragdoll::AddLinearVelocity(Vec3Arg inLinearVelocity, bool inLockBodies) +{ + BodyInterface &bi = sGetBodyInterface(mSystem, inLockBodies); + for (BodyID body_id : mBodyIDs) + bi.AddLinearVelocity(body_id, inLinearVelocity); +} + +void Ragdoll::AddImpulse(Vec3Arg inImpulse, bool inLockBodies) +{ + BodyInterface &bi = sGetBodyInterface(mSystem, inLockBodies); + for (BodyID body_id : mBodyIDs) + bi.AddImpulse(body_id, inImpulse); +} + +void Ragdoll::GetRootTransform(RVec3 &outPosition, Quat &outRotation, bool inLockBodies) const +{ + BodyLockRead lock(sGetBodyLockInterface(mSystem, inLockBodies), mBodyIDs[0]); + if (lock.Succeeded()) + { + const Body &body = lock.GetBody(); + outPosition = body.GetPosition(); + outRotation = body.GetRotation(); + } + else + { + outPosition = RVec3::sZero(); + outRotation = Quat::sIdentity(); + } +} + +AABox Ragdoll::GetWorldSpaceBounds(bool inLockBodies) const +{ + // Lock the bodies + int body_count = (int)mBodyIDs.size(); + BodyLockMultiRead lock(sGetBodyLockInterface(mSystem, inLockBodies), mBodyIDs.data(), body_count); + + // Encapsulate all bodies + AABox bounds; + for (int b = 0; b < body_count; ++b) + { + const Body *body = lock.GetBody(b); + if (body != nullptr) + bounds.Encapsulate(body->GetWorldSpaceBounds()); + } + return bounds; +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Ragdoll/Ragdoll.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Ragdoll/Ragdoll.h new file mode 100644 index 00000000000..c31586f2562 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Ragdoll/Ragdoll.h @@ -0,0 +1,240 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +class Ragdoll; +class PhysicsSystem; + +/// Contains the structure of a ragdoll +class JPH_EXPORT RagdollSettings : public RefTarget +{ +public: + JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, RagdollSettings) + + /// Stabilize the constraints of the ragdoll + /// @return True on success, false on failure. + bool Stabilize(); + + /// After the ragdoll has been fully configured, call this function to automatically create and add a GroupFilterTable collision filter to all bodies + /// and configure them so that parent and children don't collide. + /// + /// This will: + /// - Create a GroupFilterTable and assign it to all of the bodies in a ragdoll. + /// - Each body in your ragdoll will get a SubGroupID that is equal to the joint index in the Skeleton that it is attached to. + /// - Loop over all joints in the Skeleton and call GroupFilterTable::DisableCollision(joint index, parent joint index). + /// - When a pose is provided through inJointMatrices the function will detect collisions between joints + /// (they must be separated by more than inMinSeparationDistance to be treated as not colliding) and automatically disable collisions. + /// + /// When you create an instance using Ragdoll::CreateRagdoll pass in a unique GroupID for each ragdoll (e.g. a simple counter), note that this number + /// should be unique throughout the PhysicsSystem, so if you have different types of ragdolls they should not share the same GroupID. + void DisableParentChildCollisions(const Mat44 *inJointMatrices = nullptr, float inMinSeparationDistance = 0.0f); + + /// Saves the state of this object in binary form to inStream. + /// @param inStream The stream to save the state to + /// @param inSaveShapes If the shapes should be saved as well (these could be shared between ragdolls, in which case the calling application may want to write custom code to restore them) + /// @param inSaveGroupFilter If the group filter should be saved as well (these could be shared) + void SaveBinaryState(StreamOut &inStream, bool inSaveShapes, bool inSaveGroupFilter) const; + + using RagdollResult = Result>; + + /// Restore a saved ragdoll from inStream + static RagdollResult sRestoreFromBinaryState(StreamIn &inStream); + + /// Create ragdoll instance from these settings + /// @return Newly created ragdoll or null when out of bodies + Ragdoll * CreateRagdoll(CollisionGroup::GroupID inCollisionGroup, uint64 inUserData, PhysicsSystem *inSystem) const; + + /// Access to the skeleton of this ragdoll + const Skeleton * GetSkeleton() const { return mSkeleton; } + Skeleton * GetSkeleton() { return mSkeleton; } + + /// Calculate the map needed for GetBodyIndexToConstraintIndex() + void CalculateBodyIndexToConstraintIndex(); + + /// Get table that maps a body index to the constraint index with which it is connected to its parent. -1 if there is no constraint associated with the body. + /// Note that this will only tell you which constraint connects the body to its parent, it will not look in the additional constraint list. + const Array & GetBodyIndexToConstraintIndex() const { return mBodyIndexToConstraintIndex; } + + /// Map a single body index to a constraint index + int GetConstraintIndexForBodyIndex(int inBodyIndex) const { return mBodyIndexToConstraintIndex[inBodyIndex]; } + + /// Calculate the map needed for GetConstraintIndexToBodyIdxPair() + void CalculateConstraintIndexToBodyIdxPair(); + + using BodyIdxPair = pair; + + /// Table that maps a constraint index (index in mConstraints) to the indices of the bodies that the constraint is connected to (index in mBodyIDs) + const Array & GetConstraintIndexToBodyIdxPair() const { return mConstraintIndexToBodyIdxPair; } + + /// Map a single constraint index (index in mConstraints) to the indices of the bodies that the constraint is connected to (index in mBodyIDs) + BodyIdxPair GetBodyIndicesForConstraintIndex(int inConstraintIndex) const { return mConstraintIndexToBodyIdxPair[inConstraintIndex]; } + + /// A single rigid body sub part of the ragdoll + class Part : public BodyCreationSettings + { + public: + JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, Part) + + Ref mToParent; + }; + + /// List of ragdoll parts + using PartVector = Array; ///< The constraint that connects this part to its parent part (should be null for the root) + + /// A constraint that connects two bodies in a ragdoll (for non parent child related constraints) + class AdditionalConstraint + { + public: + JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, AdditionalConstraint) + + /// Constructors + AdditionalConstraint() = default; + AdditionalConstraint(int inBodyIdx1, int inBodyIdx2, TwoBodyConstraintSettings *inConstraint) : mBodyIdx { inBodyIdx1, inBodyIdx2 }, mConstraint(inConstraint) { } + + int mBodyIdx[2]; ///< Indices of the bodies that this constraint connects + Ref mConstraint; ///< The constraint that connects these bodies + }; + + /// List of additional constraints + using AdditionalConstraintVector = Array; + + /// The skeleton for this ragdoll + Ref mSkeleton; + + /// For each of the joints, the body and constraint attaching it to its parent body (1-on-1 with mSkeleton.GetJoints()) + PartVector mParts; + + /// A list of constraints that connects two bodies in a ragdoll (for non parent child related constraints) + AdditionalConstraintVector mAdditionalConstraints; + +private: + /// Table that maps a body index (index in mBodyIDs) to the constraint index with which it is connected to its parent. -1 if there is no constraint associated with the body. + Array mBodyIndexToConstraintIndex; + + /// Table that maps a constraint index (index in mConstraints) to the indices of the bodies that the constraint is connected to (index in mBodyIDs) + Array mConstraintIndexToBodyIdxPair; +}; + +/// Runtime ragdoll information +class JPH_EXPORT Ragdoll : public RefTarget, public NonCopyable +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Constructor + explicit Ragdoll(PhysicsSystem *inSystem) : mSystem(inSystem) { } + + /// Destructor + ~Ragdoll(); + + /// Add bodies and constraints to the system and optionally activate the bodies + void AddToPhysicsSystem(EActivation inActivationMode, bool inLockBodies = true); + + /// Remove bodies and constraints from the system + void RemoveFromPhysicsSystem(bool inLockBodies = true); + + /// Wake up all bodies in the ragdoll + void Activate(bool inLockBodies = true); + + /// Check if one or more of the bodies in the ragdoll are active. + /// Note that this involves locking the bodies (if inLockBodies is true) and looping over them. An alternative and possibly faster + /// way could be to install a BodyActivationListener and count the number of active bodies of a ragdoll as they're activated / deactivated + /// (basically check if the body that activates / deactivates is in GetBodyIDs() and increment / decrement a counter). + bool IsActive(bool inLockBodies = true) const; + + /// Set the group ID on all bodies in the ragdoll + void SetGroupID(CollisionGroup::GroupID inGroupID, bool inLockBodies = true); + + /// Set the ragdoll to a pose (calls BodyInterface::SetPositionAndRotation to instantly move the ragdoll) + void SetPose(const SkeletonPose &inPose, bool inLockBodies = true); + + /// Lower level version of SetPose that directly takes the world space joint matrices + void SetPose(RVec3Arg inRootOffset, const Mat44 *inJointMatrices, bool inLockBodies = true); + + /// Get the ragdoll pose (uses the world transform of the bodies to calculate the pose) + void GetPose(SkeletonPose &outPose, bool inLockBodies = true); + + /// Lower level version of GetPose that directly returns the world space joint matrices + void GetPose(RVec3 &outRootOffset, Mat44 *outJointMatrices, bool inLockBodies = true); + + /// This function calls ResetWarmStart on all constraints. It can be used after calling SetPose to reset previous frames impulses. See: Constraint::ResetWarmStart. + void ResetWarmStart(); + + /// Drive the ragdoll to a specific pose by setting velocities on each of the bodies so that it will reach inPose in inDeltaTime + void DriveToPoseUsingKinematics(const SkeletonPose &inPose, float inDeltaTime, bool inLockBodies = true); + + /// Lower level version of DriveToPoseUsingKinematics that directly takes the world space joint matrices + void DriveToPoseUsingKinematics(RVec3Arg inRootOffset, const Mat44 *inJointMatrices, float inDeltaTime, bool inLockBodies = true); + + /// Drive the ragdoll to a specific pose by activating the motors on each constraint + void DriveToPoseUsingMotors(const SkeletonPose &inPose); + + /// Control the linear and velocity of all bodies in the ragdoll + void SetLinearAndAngularVelocity(Vec3Arg inLinearVelocity, Vec3Arg inAngularVelocity, bool inLockBodies = true); + + /// Set the world space linear velocity of all bodies in the ragdoll. + void SetLinearVelocity(Vec3Arg inLinearVelocity, bool inLockBodies = true); + + /// Add a world space velocity (in m/s) to all bodies in the ragdoll. + void AddLinearVelocity(Vec3Arg inLinearVelocity, bool inLockBodies = true); + + /// Add impulse to all bodies of the ragdoll (center of mass of each of them) + void AddImpulse(Vec3Arg inImpulse, bool inLockBodies = true); + + /// Get the position and orientation of the root of the ragdoll + void GetRootTransform(RVec3 &outPosition, Quat &outRotation, bool inLockBodies = true) const; + + /// Get number of bodies in the ragdoll + size_t GetBodyCount() const { return mBodyIDs.size(); } + + /// Access a body ID + BodyID GetBodyID(int inBodyIndex) const { return mBodyIDs[inBodyIndex]; } + + /// Access to the array of body IDs + const Array & GetBodyIDs() const { return mBodyIDs; } + + /// Get number of constraints in the ragdoll + size_t GetConstraintCount() const { return mConstraints.size(); } + + /// Access a constraint by index + TwoBodyConstraint * GetConstraint(int inConstraintIndex) { return mConstraints[inConstraintIndex]; } + + /// Access a constraint by index + const TwoBodyConstraint * GetConstraint(int inConstraintIndex) const { return mConstraints[inConstraintIndex]; } + + /// Get world space bounding box for all bodies of the ragdoll + AABox GetWorldSpaceBounds(bool inLockBodies = true) const; + + /// Get the settings object that created this ragdoll + const RagdollSettings * GetRagdollSettings() const { return mRagdollSettings; } + +private: + /// For RagdollSettings::CreateRagdoll function + friend class RagdollSettings; + + /// The settings that created this ragdoll + RefConst mRagdollSettings; + + /// The bodies and constraints that this ragdoll consists of (1-on-1 with mRagdollSettings->mParts) + Array mBodyIDs; + + /// Array of constraints that connect the bodies together + Array> mConstraints; + + /// Cached physics system + PhysicsSystem * mSystem; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyContactListener.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyContactListener.h new file mode 100644 index 00000000000..27e185375f3 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyContactListener.h @@ -0,0 +1,55 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2024 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +JPH_NAMESPACE_BEGIN + +class Body; +class SoftBodyManifold; + +/// Return value for the OnSoftBodyContactValidate callback. Determines if the contact will be processed or not. +enum class SoftBodyValidateResult +{ + AcceptContact, ///< Accept this contact + RejectContact, ///< Reject this contact +}; + +/// Contact settings for a soft body contact. +/// The values are filled in with their defaults by the system so the callback doesn't need to modify anything, but it can if it wants to. +class SoftBodyContactSettings +{ +public: + float mInvMassScale1 = 1.0f; ///< Scale factor for the inverse mass of the soft body (0 = infinite mass, 1 = use original mass, 2 = body has half the mass). For the same contact pair, you should strive to keep the value the same over time. + float mInvMassScale2 = 1.0f; ///< Scale factor for the inverse mass of the other body (0 = infinite mass, 1 = use original mass, 2 = body has half the mass). For the same contact pair, you should strive to keep the value the same over time. + float mInvInertiaScale2 = 1.0f; ///< Scale factor for the inverse inertia of the other body (usually same as mInvMassScale2) + bool mIsSensor; ///< If the contact should be treated as a sensor vs body contact (no collision response) +}; + +/// A listener class that receives collision contact events for soft bodies against rigid bodies. +/// It can be registered with the PhysicsSystem. +class SoftBodyContactListener +{ +public: + /// Ensure virtual destructor + virtual ~SoftBodyContactListener() = default; + + /// Called whenever the soft body's aabox overlaps with another body's aabox (so receiving this callback doesn't tell if any of the vertices will collide). + /// This callback can be used to change the behavior of the collision response for all vertices in the soft body or to completely reject the contact. + /// Note that this callback is called when all bodies are locked, so don't use any locking functions! + /// @param inSoftBody The soft body that collided. It is safe to access this as the soft body is only updated on the current thread. + /// @param inOtherBody The other body that collided. Note that accessing the position/orientation/velocity of inOtherBody may result in a race condition as other threads may be modifying the body at the same time. + /// @param ioSettings The settings for all contact points that are generated by this collision. + /// @return Whether the contact should be processed or not. + virtual SoftBodyValidateResult OnSoftBodyContactValidate([[maybe_unused]] const Body &inSoftBody, [[maybe_unused]] const Body &inOtherBody, [[maybe_unused]] SoftBodyContactSettings &ioSettings) { return SoftBodyValidateResult::AcceptContact; } + + /// Called after all contact points for a soft body have been handled. You only receive one callback per body pair per simulation step and can use inManifold to iterate through all contacts. + /// Note that this callback is called when all bodies are locked, so don't use any locking functions! + /// You will receive a single callback for a soft body per simulation step for performance reasons, this callback will apply to all vertices in the soft body. + /// @param inSoftBody The soft body that collided. It is safe to access this as the soft body is only updated on the current thread. + /// @param inManifold The manifold that describes the contact surface between the two bodies. Other bodies may be modified by other threads during this callback. + virtual void OnSoftBodyContactAdded([[maybe_unused]] const Body &inSoftBody, const SoftBodyManifold &inManifold) { /* Do nothing */ } +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyCreationSettings.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyCreationSettings.cpp new file mode 100644 index 00000000000..3ae026df64a --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyCreationSettings.cpp @@ -0,0 +1,122 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2023 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SoftBodyCreationSettings) +{ + JPH_ADD_ATTRIBUTE(SoftBodyCreationSettings, mSettings) + JPH_ADD_ATTRIBUTE(SoftBodyCreationSettings, mPosition) + JPH_ADD_ATTRIBUTE(SoftBodyCreationSettings, mRotation) + JPH_ADD_ATTRIBUTE(SoftBodyCreationSettings, mUserData) + JPH_ADD_ENUM_ATTRIBUTE(SoftBodyCreationSettings, mObjectLayer) + JPH_ADD_ATTRIBUTE(SoftBodyCreationSettings, mCollisionGroup) + JPH_ADD_ATTRIBUTE(SoftBodyCreationSettings, mNumIterations) + JPH_ADD_ATTRIBUTE(SoftBodyCreationSettings, mLinearDamping) + JPH_ADD_ATTRIBUTE(SoftBodyCreationSettings, mMaxLinearVelocity) + JPH_ADD_ATTRIBUTE(SoftBodyCreationSettings, mRestitution) + JPH_ADD_ATTRIBUTE(SoftBodyCreationSettings, mFriction) + JPH_ADD_ATTRIBUTE(SoftBodyCreationSettings, mPressure) + JPH_ADD_ATTRIBUTE(SoftBodyCreationSettings, mGravityFactor) + JPH_ADD_ATTRIBUTE(SoftBodyCreationSettings, mUpdatePosition) + JPH_ADD_ATTRIBUTE(SoftBodyCreationSettings, mMakeRotationIdentity) + JPH_ADD_ATTRIBUTE(SoftBodyCreationSettings, mAllowSleeping) +} + +void SoftBodyCreationSettings::SaveBinaryState(StreamOut &inStream) const +{ + inStream.Write(mPosition); + inStream.Write(mRotation); + inStream.Write(mUserData); + inStream.Write(mObjectLayer); + mCollisionGroup.SaveBinaryState(inStream); + inStream.Write(mNumIterations); + inStream.Write(mLinearDamping); + inStream.Write(mMaxLinearVelocity); + inStream.Write(mRestitution); + inStream.Write(mFriction); + inStream.Write(mPressure); + inStream.Write(mGravityFactor); + inStream.Write(mUpdatePosition); + inStream.Write(mMakeRotationIdentity); + inStream.Write(mAllowSleeping); +} + +void SoftBodyCreationSettings::RestoreBinaryState(StreamIn &inStream) +{ + inStream.Read(mPosition); + inStream.Read(mRotation); + inStream.Read(mUserData); + inStream.Read(mObjectLayer); + mCollisionGroup.RestoreBinaryState(inStream); + inStream.Read(mNumIterations); + inStream.Read(mLinearDamping); + inStream.Read(mMaxLinearVelocity); + inStream.Read(mRestitution); + inStream.Read(mFriction); + inStream.Read(mPressure); + inStream.Read(mGravityFactor); + inStream.Read(mUpdatePosition); + inStream.Read(mMakeRotationIdentity); + inStream.Read(mAllowSleeping); +} + +void SoftBodyCreationSettings::SaveWithChildren(StreamOut &inStream, SharedSettingsToIDMap *ioSharedSettingsMap, MaterialToIDMap *ioMaterialMap, GroupFilterToIDMap *ioGroupFilterMap) const +{ + // Save creation settings + SaveBinaryState(inStream); + + // Save shared settings + if (ioSharedSettingsMap != nullptr && ioMaterialMap != nullptr) + mSettings->SaveWithMaterials(inStream, *ioSharedSettingsMap, *ioMaterialMap); + else + inStream.Write(~uint32(0)); + + // Save group filter + StreamUtils::SaveObjectReference(inStream, mCollisionGroup.GetGroupFilter(), ioGroupFilterMap); +} + +SoftBodyCreationSettings::SBCSResult SoftBodyCreationSettings::sRestoreWithChildren(StreamIn &inStream, IDToSharedSettingsMap &ioSharedSettingsMap, IDToMaterialMap &ioMaterialMap, IDToGroupFilterMap &ioGroupFilterMap) +{ + SBCSResult result; + + // Read creation settings + SoftBodyCreationSettings settings; + settings.RestoreBinaryState(inStream); + if (inStream.IsEOF() || inStream.IsFailed()) + { + result.SetError("Error reading body creation settings"); + return result; + } + + // Read shared settings + SoftBodySharedSettings::SettingsResult settings_result = SoftBodySharedSettings::sRestoreWithMaterials(inStream, ioSharedSettingsMap, ioMaterialMap); + if (settings_result.HasError()) + { + result.SetError(settings_result.GetError()); + return result; + } + settings.mSettings = settings_result.Get(); + + // Read group filter + Result gfresult = StreamUtils::RestoreObjectReference(inStream, ioGroupFilterMap); + if (gfresult.HasError()) + { + result.SetError(gfresult.GetError()); + return result; + } + settings.mCollisionGroup.SetGroupFilter(gfresult.Get()); + + result.Set(settings); + return result; +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyCreationSettings.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyCreationSettings.h new file mode 100644 index 00000000000..9d153290e80 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyCreationSettings.h @@ -0,0 +1,73 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2023 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +/// This class contains the information needed to create a soft body object +/// Note: Soft bodies are still in development and come with several caveats. Read the Architecture and API documentation for more information! +class JPH_EXPORT SoftBodyCreationSettings +{ +public: + JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, SoftBodyCreationSettings) + + /// Constructor + SoftBodyCreationSettings() = default; + SoftBodyCreationSettings(const SoftBodySharedSettings *inSettings, RVec3Arg inPosition, QuatArg inRotation, ObjectLayer inObjectLayer) : mSettings(inSettings), mPosition(inPosition), mRotation(inRotation), mObjectLayer(inObjectLayer) { } + + /// Saves the state of this object in binary form to inStream. Doesn't store the shared settings nor the group filter. + void SaveBinaryState(StreamOut &inStream) const; + + /// Restore the state of this object from inStream. Doesn't restore the shared settings nor the group filter. + void RestoreBinaryState(StreamIn &inStream); + + using GroupFilterToIDMap = StreamUtils::ObjectToIDMap; + using IDToGroupFilterMap = StreamUtils::IDToObjectMap; + using SharedSettingsToIDMap = SoftBodySharedSettings::SharedSettingsToIDMap; + using IDToSharedSettingsMap = SoftBodySharedSettings::IDToSharedSettingsMap; + using MaterialToIDMap = StreamUtils::ObjectToIDMap; + using IDToMaterialMap = StreamUtils::IDToObjectMap; + + /// Save this body creation settings, its shared settings and group filter. Pass in an empty map in ioSharedSettingsMap / ioMaterialMap / ioGroupFilterMap or reuse the same map while saving multiple shapes to the same stream in order to avoid writing duplicates. + /// Pass nullptr to ioSharedSettingsMap and ioMaterial map to skip saving shared settings and materials + /// Pass nullptr to ioGroupFilterMap to skip saving group filters + void SaveWithChildren(StreamOut &inStream, SharedSettingsToIDMap *ioSharedSettingsMap, MaterialToIDMap *ioMaterialMap, GroupFilterToIDMap *ioGroupFilterMap) const; + + using SBCSResult = Result; + + /// Restore a shape, all its children and materials. Pass in an empty map in ioSharedSettingsMap / ioMaterialMap / ioGroupFilterMap or reuse the same map while reading multiple shapes from the same stream in order to restore duplicates. + static SBCSResult sRestoreWithChildren(StreamIn &inStream, IDToSharedSettingsMap &ioSharedSettingsMap, IDToMaterialMap &ioMaterialMap, IDToGroupFilterMap &ioGroupFilterMap); + + RefConst mSettings; ///< Defines the configuration of this soft body + + RVec3 mPosition { RVec3::sZero() }; ///< Initial position of the soft body + Quat mRotation { Quat::sIdentity() }; ///< Initial rotation of the soft body + + /// User data value (can be used by application) + uint64 mUserData = 0; + + ///@name Collision settings + ObjectLayer mObjectLayer = 0; ///< The collision layer this body belongs to (determines if two objects can collide) + CollisionGroup mCollisionGroup; ///< The collision group this body belongs to (determines if two objects can collide) + + uint32 mNumIterations = 5; ///< Number of solver iterations + float mLinearDamping = 0.1f; ///< Linear damping: dv/dt = -mLinearDamping * v + float mMaxLinearVelocity = 500.0f; ///< Maximum linear velocity that a vertex can reach (m/s) + float mRestitution = 0.0f; ///< Restitution when colliding + float mFriction = 0.2f; ///< Friction coefficient when colliding + float mPressure = 0.0f; ///< n * R * T, amount of substance * ideal gas constant * absolute temperature, see https://en.wikipedia.org/wiki/Pressure + float mGravityFactor = 1.0f; ///< Value to multiply gravity with for this body + bool mUpdatePosition = true; ///< Update the position of the body while simulating (set to false for something that is attached to the static world) + bool mMakeRotationIdentity = true; ///< Bake specified mRotation in the vertices and set the body rotation to identity (simulation is slightly more accurate if the rotation of a soft body is kept to identity) + bool mAllowSleeping = true; ///< If this body can go to sleep or not +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyManifold.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyManifold.h new file mode 100644 index 00000000000..ea3a65b3bf3 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyManifold.h @@ -0,0 +1,59 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2024 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +/// An interface to query which vertices of a soft body are colliding with other bodies +class SoftBodyManifold +{ +public: + /// Get the vertices of the soft body for iterating + const Array & GetVertices() const { return mVertices; } + + /// Check if a vertex has collided with something in this update + JPH_INLINE bool HasContact(const SoftBodyVertex &inVertex) const + { + return inVertex.mHasContact; + } + + /// Get the local space contact point (multiply by GetCenterOfMassTransform() of the soft body to get world space) + JPH_INLINE Vec3 GetLocalContactPoint(const SoftBodyVertex &inVertex) const + { + return inVertex.mPosition - inVertex.mCollisionPlane.SignedDistance(inVertex.mPosition) * inVertex.mCollisionPlane.GetNormal(); + } + + /// Get the contact normal for the vertex (assumes there is a contact). + JPH_INLINE Vec3 GetContactNormal(const SoftBodyVertex &inVertex) const + { + return -inVertex.mCollisionPlane.GetNormal(); + } + + /// Get the body with which the vertex has collided in this update + JPH_INLINE BodyID GetContactBodyID(const SoftBodyVertex &inVertex) const + { + return inVertex.mHasContact? mCollidingShapes[inVertex.mCollidingShapeIndex].mBodyID : BodyID(); + } + +private: + /// Allow SoftBodyMotionProperties to construct us + friend class SoftBodyMotionProperties; + + /// Constructor + explicit SoftBodyManifold(const SoftBodyMotionProperties *inMotionProperties) : + mVertices(inMotionProperties->mVertices), + mCollidingShapes(inMotionProperties->mCollidingShapes) + { + } + + using CollidingShape = SoftBodyMotionProperties::CollidingShape; + + const Array & mVertices; + const Array & mCollidingShapes; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyMotionProperties.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyMotionProperties.cpp new file mode 100644 index 00000000000..31b068455b9 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyMotionProperties.cpp @@ -0,0 +1,1089 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2023 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#include +#include +#ifdef JPH_DEBUG_RENDERER + #include +#endif // JPH_DEBUG_RENDERER + +JPH_NAMESPACE_BEGIN + +using namespace JPH::literals; + +void SoftBodyMotionProperties::CalculateMassAndInertia() +{ + MassProperties mp; + + for (const Vertex &v : mVertices) + if (v.mInvMass > 0.0f) + { + Vec3 pos = v.mPosition; + + // Accumulate mass + float mass = 1.0f / v.mInvMass; + mp.mMass += mass; + + // Inertia tensor, diagonal + // See equations https://en.wikipedia.org/wiki/Moment_of_inertia section 'Inertia Tensor' + for (int i = 0; i < 3; ++i) + mp.mInertia(i, i) += mass * (Square(pos[(i + 1) % 3]) + Square(pos[(i + 2) % 3])); + + // Inertia tensor off diagonal + for (int i = 0; i < 3; ++i) + for (int j = 0; j < 3; ++j) + if (i != j) + mp.mInertia(i, j) -= mass * pos[i] * pos[j]; + } + else + { + // If one vertex is kinematic, the entire body will have infinite mass and inertia + SetInverseMass(0.0f); + SetInverseInertia(Vec3::sZero(), Quat::sIdentity()); + return; + } + + SetMassProperties(EAllowedDOFs::All, mp); +} + +void SoftBodyMotionProperties::Initialize(const SoftBodyCreationSettings &inSettings) +{ + // Store settings + mSettings = inSettings.mSettings; + mNumIterations = inSettings.mNumIterations; + mPressure = inSettings.mPressure; + mUpdatePosition = inSettings.mUpdatePosition; + + // Initialize vertices + mVertices.resize(inSettings.mSettings->mVertices.size()); + Mat44 rotation = inSettings.mMakeRotationIdentity? Mat44::sRotation(inSettings.mRotation) : Mat44::sIdentity(); + for (Array::size_type v = 0, s = mVertices.size(); v < s; ++v) + { + const SoftBodySharedSettings::Vertex &in_vertex = inSettings.mSettings->mVertices[v]; + Vertex &out_vertex = mVertices[v]; + out_vertex.mPreviousPosition = out_vertex.mPosition = rotation * Vec3(in_vertex.mPosition); + out_vertex.mVelocity = rotation.Multiply3x3(Vec3(in_vertex.mVelocity)); + out_vertex.mCollidingShapeIndex = -1; + out_vertex.mHasContact = false; + out_vertex.mLargestPenetration = -FLT_MAX; + out_vertex.mInvMass = in_vertex.mInvMass; + mLocalBounds.Encapsulate(out_vertex.mPosition); + } + + // Allocate space for skinned vertices + if (!inSettings.mSettings->mSkinnedConstraints.empty()) + mSkinState.resize(mVertices.size()); + + // We don't know delta time yet, so we can't predict the bounds and use the local bounds as the predicted bounds + mLocalPredictedBounds = mLocalBounds; + + CalculateMassAndInertia(); +} + +float SoftBodyMotionProperties::GetVolumeTimesSix() const +{ + float six_volume = 0.0f; + for (const Face &f : mSettings->mFaces) + { + Vec3 x1 = mVertices[f.mVertex[0]].mPosition; + Vec3 x2 = mVertices[f.mVertex[1]].mPosition; + Vec3 x3 = mVertices[f.mVertex[2]].mPosition; + six_volume += x1.Cross(x2).Dot(x3); // We pick zero as the origin as this is the center of the bounding box so should give good accuracy + } + return six_volume; +} + +void SoftBodyMotionProperties::DetermineCollidingShapes(const SoftBodyUpdateContext &inContext, const PhysicsSystem &inSystem, const BodyLockInterface &inBodyLockInterface) +{ + JPH_PROFILE_FUNCTION(); + + struct Collector : public CollideShapeBodyCollector + { + Collector(const SoftBodyUpdateContext &inContext, const PhysicsSystem &inSystem, const BodyLockInterface &inBodyLockInterface, Array &ioHits) : + mContext(inContext), + mInverseTransform(inContext.mCenterOfMassTransform.InversedRotationTranslation()), + mBodyLockInterface(inBodyLockInterface), + mCombineFriction(inSystem.GetCombineFriction()), + mCombineRestitution(inSystem.GetCombineRestitution()), + mHits(ioHits) + { + } + + virtual void AddHit(const BodyID &inResult) override + { + BodyLockRead lock(mBodyLockInterface, inResult); + if (lock.Succeeded()) + { + const Body &soft_body = *mContext.mBody; + const Body &body = lock.GetBody(); + if (body.IsRigidBody() // TODO: We should support soft body vs soft body + && soft_body.GetCollisionGroup().CanCollide(body.GetCollisionGroup())) + { + // Call the contact listener to see if we should accept this contact + // If there is no contact listener then we can ignore the contact if the other body is a sensor + SoftBodyContactSettings settings; + settings.mIsSensor = body.IsSensor(); + if (mContext.mContactListener == nullptr? !settings.mIsSensor : mContext.mContactListener->OnSoftBodyContactValidate(soft_body, body, settings) == SoftBodyValidateResult::AcceptContact) + { + CollidingShape cs; + cs.mCenterOfMassTransform = (mInverseTransform * body.GetCenterOfMassTransform()).ToMat44(); + cs.mShape = body.GetShape(); + cs.mBodyID = inResult; + cs.mMotionType = body.GetMotionType(); + cs.mIsSensor = settings.mIsSensor; + cs.mUpdateVelocities = false; + cs.mFriction = mCombineFriction(soft_body, SubShapeID(), body, SubShapeID()); + cs.mRestitution = mCombineRestitution(soft_body, SubShapeID(), body, SubShapeID()); + if (cs.mMotionType == EMotionType::Dynamic) + { + const MotionProperties *mp = body.GetMotionProperties(); + cs.mInvMass = settings.mInvMassScale2 * mp->GetInverseMass(); + cs.mInvInertia = settings.mInvInertiaScale2 * mp->GetInverseInertiaForRotation(cs.mCenterOfMassTransform.GetRotation()); + cs.mSoftBodyInvMassScale = settings.mInvMassScale1; + cs.mOriginalLinearVelocity = cs.mLinearVelocity = mInverseTransform.Multiply3x3(mp->GetLinearVelocity()); + cs.mOriginalAngularVelocity = cs.mAngularVelocity = mInverseTransform.Multiply3x3(mp->GetAngularVelocity()); + } + mHits.push_back(cs); + } + } + } + } + + private: + const SoftBodyUpdateContext &mContext; + RMat44 mInverseTransform; + const BodyLockInterface & mBodyLockInterface; + ContactConstraintManager::CombineFunction mCombineFriction; + ContactConstraintManager::CombineFunction mCombineRestitution; + Array & mHits; + }; + + Collector collector(inContext, inSystem, inBodyLockInterface, mCollidingShapes); + AABox bounds = mLocalBounds; + bounds.Encapsulate(mLocalPredictedBounds); + bounds = bounds.Transformed(inContext.mCenterOfMassTransform); + bounds.ExpandBy(Vec3::sReplicate(mSettings->mVertexRadius)); + ObjectLayer layer = inContext.mBody->GetObjectLayer(); + DefaultBroadPhaseLayerFilter broadphase_layer_filter = inSystem.GetDefaultBroadPhaseLayerFilter(layer); + DefaultObjectLayerFilter object_layer_filter = inSystem.GetDefaultLayerFilter(layer); + inSystem.GetBroadPhaseQuery().CollideAABox(bounds, collector, broadphase_layer_filter, object_layer_filter); +} + +void SoftBodyMotionProperties::DetermineCollisionPlanes(const SoftBodyUpdateContext &inContext, uint inVertexStart, uint inNumVertices) +{ + JPH_PROFILE_FUNCTION(); + + // Generate collision planes + for (const CollidingShape &cs : mCollidingShapes) + cs.mShape->CollideSoftBodyVertices(cs.mCenterOfMassTransform, Vec3::sReplicate(1.0f), mVertices.data() + inVertexStart, inNumVertices, inContext.mDeltaTime, inContext.mDisplacementDueToGravity, int(&cs - mCollidingShapes.data())); +} + +void SoftBodyMotionProperties::ApplyPressure(const SoftBodyUpdateContext &inContext) +{ + JPH_PROFILE_FUNCTION(); + + float dt = inContext.mSubStepDeltaTime; + float pressure_coefficient = mPressure; + if (pressure_coefficient > 0.0f) + { + // Calculate total volume + float six_volume = GetVolumeTimesSix(); + if (six_volume > 0.0f) + { + // Apply pressure + // p = F / A = n R T / V (see https://en.wikipedia.org/wiki/Pressure) + // Our pressure coefficient is n R T so the impulse is: + // P = F dt = pressure_coefficient / V * A * dt + float coefficient = pressure_coefficient * dt / six_volume; // Need to still multiply by 6 for the volume + for (const Face &f : mSettings->mFaces) + { + Vec3 x1 = mVertices[f.mVertex[0]].mPosition; + Vec3 x2 = mVertices[f.mVertex[1]].mPosition; + Vec3 x3 = mVertices[f.mVertex[2]].mPosition; + + Vec3 impulse = coefficient * (x2 - x1).Cross(x3 - x1); // Area is half the cross product so need to still divide by 2 + for (uint32 i : f.mVertex) + { + Vertex &v = mVertices[i]; + v.mVelocity += v.mInvMass * impulse; // Want to divide by 3 because we spread over 3 vertices + } + } + } + } +} + +void SoftBodyMotionProperties::IntegratePositions(const SoftBodyUpdateContext &inContext) +{ + JPH_PROFILE_FUNCTION(); + + float dt = inContext.mSubStepDeltaTime; + float linear_damping = max(0.0f, 1.0f - GetLinearDamping() * dt); // See: MotionProperties::ApplyForceTorqueAndDragInternal + + // Integrate + Vec3 sub_step_gravity = inContext.mGravity * dt; + for (Vertex &v : mVertices) + if (v.mInvMass > 0.0f) + { + // Gravity + v.mVelocity += sub_step_gravity; + + // Damping + v.mVelocity *= linear_damping; + + // Integrate + v.mPreviousPosition = v.mPosition; + v.mPosition += v.mVelocity * dt; + } + else + { + // Integrate + v.mPreviousPosition = v.mPosition; + v.mPosition += v.mVelocity * dt; + } +} + +void SoftBodyMotionProperties::ApplyBendConstraints(const SoftBodyUpdateContext &inContext) +{ + JPH_PROFILE_FUNCTION(); + + float inv_dt_sq = 1.0f / Square(inContext.mSubStepDeltaTime); + + for (const DihedralBend &b : mSettings->mDihedralBendConstraints) + { + Vertex &v0 = mVertices[b.mVertex[0]]; + Vertex &v1 = mVertices[b.mVertex[1]]; + Vertex &v2 = mVertices[b.mVertex[2]]; + Vertex &v3 = mVertices[b.mVertex[3]]; + + // Get positions + Vec3 x0 = v0.mPosition; + Vec3 x1 = v1.mPosition; + Vec3 x2 = v2.mPosition; + Vec3 x3 = v3.mPosition; + + /* + x2 + e1/ \e3 + / \ + x0----x1 + \ e0 / + e2\ /e4 + x3 + */ + + // Calculate the shared edge of the triangles + Vec3 e = x1 - x0; + float e_len = e.Length(); + if (e_len < 1.0e-6f) + continue; + + // Calculate the normals of the triangles + Vec3 x1x2 = x2 - x1; + Vec3 x1x3 = x3 - x1; + Vec3 n1 = (x2 - x0).Cross(x1x2); + Vec3 n2 = x1x3.Cross(x3 - x0); + float n1_len_sq = n1.LengthSq(); + float n2_len_sq = n2.LengthSq(); + float n1_len_sq_n2_len_sq = n1_len_sq * n2_len_sq; + if (n1_len_sq_n2_len_sq < 1.0e-24f) + continue; + + // Calculate constraint equation + // As per "Strain Based Dynamics" Appendix A we need to negate the gradients when (n1 x n2) . e > 0, instead we make sure that the sign of the constraint equation is correct + float sign = Sign(n2.Cross(n1).Dot(e)); + float d = n1.Dot(n2) / sqrt(n1_len_sq_n2_len_sq); + float c = sign * ACos(d) - b.mInitialAngle; + + // Ensure the range is -PI to PI + if (c > JPH_PI) + c -= 2.0f * JPH_PI; + else if (c < -JPH_PI) + c += 2.0f * JPH_PI; + + // Calculate gradient of constraint equation + // Taken from "Strain Based Dynamics" - Matthias Muller et al. (Appendix A) + // with p1 = x2, p2 = x3, p3 = x0 and p4 = x1 + // which in turn is based on "Simulation of Clothing with Folds and Wrinkles" - R. Bridson et al. (Section 4) + n1 /= n1_len_sq; + n2 /= n2_len_sq; + Vec3 d0c = (x1x2.Dot(e) * n1 + x1x3.Dot(e) * n2) / e_len; + Vec3 d2c = e_len * n1; + Vec3 d3c = e_len * n2; + + // The sum of the gradients must be zero (see "Strain Based Dynamics" section 4) + Vec3 d1c = -d0c - d2c - d3c; + + // Get masses + float w0 = v0.mInvMass; + float w1 = v1.mInvMass; + float w2 = v2.mInvMass; + float w3 = v3.mInvMass; + + // Calculate -lambda + float denom = w0 * d0c.LengthSq() + w1 * d1c.LengthSq() + w2 * d2c.LengthSq() + w3 * d3c.LengthSq() + b.mCompliance * inv_dt_sq; + if (denom < 1.0e-12f) + continue; + float minus_lambda = c / denom; + + // Apply correction + v0.mPosition = x0 - minus_lambda * w0 * d0c; + v1.mPosition = x1 - minus_lambda * w1 * d1c; + v2.mPosition = x2 - minus_lambda * w2 * d2c; + v3.mPosition = x3 - minus_lambda * w3 * d3c; + } +} + +void SoftBodyMotionProperties::ApplyVolumeConstraints(const SoftBodyUpdateContext &inContext) +{ + JPH_PROFILE_FUNCTION(); + + float inv_dt_sq = 1.0f / Square(inContext.mSubStepDeltaTime); + + // Satisfy volume constraints + for (const Volume &v : mSettings->mVolumeConstraints) + { + Vertex &v1 = mVertices[v.mVertex[0]]; + Vertex &v2 = mVertices[v.mVertex[1]]; + Vertex &v3 = mVertices[v.mVertex[2]]; + Vertex &v4 = mVertices[v.mVertex[3]]; + + Vec3 x1 = v1.mPosition; + Vec3 x2 = v2.mPosition; + Vec3 x3 = v3.mPosition; + Vec3 x4 = v4.mPosition; + + // Calculate constraint equation + Vec3 x1x2 = x2 - x1; + Vec3 x1x3 = x3 - x1; + Vec3 x1x4 = x4 - x1; + float c = abs(x1x2.Cross(x1x3).Dot(x1x4)) - v.mSixRestVolume; + + // Calculate gradient of constraint equation + Vec3 d1c = (x4 - x2).Cross(x3 - x2); + Vec3 d2c = x1x3.Cross(x1x4); + Vec3 d3c = x1x4.Cross(x1x2); + Vec3 d4c = x1x2.Cross(x1x3); + + // Get masses + float w1 = v1.mInvMass; + float w2 = v2.mInvMass; + float w3 = v3.mInvMass; + float w4 = v4.mInvMass; + + // Calculate -lambda + float denom = w1 * d1c.LengthSq() + w2 * d2c.LengthSq() + w3 * d3c.LengthSq() + w4 * d4c.LengthSq() + v.mCompliance * inv_dt_sq; + if (denom < 1.0e-12f) + continue; + float minus_lambda = c / denom; + + // Apply correction + v1.mPosition = x1 - minus_lambda * w1 * d1c; + v2.mPosition = x2 - minus_lambda * w2 * d2c; + v3.mPosition = x3 - minus_lambda * w3 * d3c; + v4.mPosition = x4 - minus_lambda * w4 * d4c; + } +} + +void SoftBodyMotionProperties::ApplySkinConstraints([[maybe_unused]] const SoftBodyUpdateContext &inContext) +{ + // Early out if nothing to do + if (mSettings->mSkinnedConstraints.empty() || !mEnableSkinConstraints) + return; + + JPH_ASSERT(mSkinStateTransform == inContext.mCenterOfMassTransform, "Skinning state is stale, artifacts will show!"); + + // Apply the constraints + Vertex *vertices = mVertices.data(); + const SkinState *skin_states = mSkinState.data(); + for (const Skinned &s : mSettings->mSkinnedConstraints) + { + Vertex &vertex = vertices[s.mVertex]; + const SkinState &skin_state = skin_states[s.mVertex]; + float max_distance = s.mMaxDistance * mSkinnedMaxDistanceMultiplier; + if (max_distance > 0.0f) + { + // Move vertex if it violated the back stop + if (s.mBackStopDistance < max_distance) + { + // Center of the back stop sphere + Vec3 center = skin_state.mPosition - skin_state.mNormal * (s.mBackStopDistance + s.mBackStopRadius); + + // Check if we're inside the back stop sphere + Vec3 delta = vertex.mPosition - center; + float delta_len_sq = delta.LengthSq(); + if (delta_len_sq < Square(s.mBackStopRadius)) + { + // Push the vertex to the surface of the back stop sphere + float delta_len = sqrt(delta_len_sq); + vertex.mPosition = delta_len > 0.0f? + center + delta * (s.mBackStopRadius / delta_len) + : center + skin_state.mNormal * s.mBackStopRadius; + } + } + + // Clamp vertex distance to max distance from skinned position + if (max_distance < FLT_MAX) + { + Vec3 delta = vertex.mPosition - skin_state.mPosition; + float delta_len_sq = delta.LengthSq(); + float max_distance_sq = Square(max_distance); + if (delta_len_sq > max_distance_sq) + vertex.mPosition = skin_state.mPosition + delta * sqrt(max_distance_sq / delta_len_sq); + } + } + else + { + // Kinematic: Just update the vertex position + vertex.mPosition = skin_state.mPosition; + } + } +} + +void SoftBodyMotionProperties::ApplyEdgeConstraints(const SoftBodyUpdateContext &inContext, uint inStartIndex, uint inEndIndex) +{ + JPH_PROFILE_FUNCTION(); + + float inv_dt_sq = 1.0f / Square(inContext.mSubStepDeltaTime); + + // Satisfy edge constraints + const Array &edge_constraints = mSettings->mEdgeConstraints; + for (uint i = inStartIndex; i < inEndIndex; ++i) + { + const Edge &e = edge_constraints[i]; + Vertex &v0 = mVertices[e.mVertex[0]]; + Vertex &v1 = mVertices[e.mVertex[1]]; + + // Get positions + Vec3 x0 = v0.mPosition; + Vec3 x1 = v1.mPosition; + + // Calculate current length + Vec3 delta = x1 - x0; + float length = delta.Length(); + + // Apply correction + float denom = length * (v0.mInvMass + v1.mInvMass + e.mCompliance * inv_dt_sq); + if (denom < 1.0e-12f) + continue; + Vec3 correction = delta * (length - e.mRestLength) / denom; + v0.mPosition = x0 + v0.mInvMass * correction; + v1.mPosition = x1 - v1.mInvMass * correction; + } +} + +void SoftBodyMotionProperties::ApplyLRAConstraints() +{ + JPH_PROFILE_FUNCTION(); + + // Satisfy LRA constraints + Vertex *vertices = mVertices.data(); + for (const LRA &lra : mSettings->mLRAConstraints) + { + JPH_ASSERT(lra.mVertex[0] < mVertices.size()); + JPH_ASSERT(lra.mVertex[1] < mVertices.size()); + const Vertex &vertex0 = vertices[lra.mVertex[0]]; + Vertex &vertex1 = vertices[lra.mVertex[1]]; + + Vec3 x0 = vertex0.mPosition; + Vec3 delta = vertex1.mPosition - x0; + float delta_len_sq = delta.LengthSq(); + if (delta_len_sq > Square(lra.mMaxDistance)) + vertex1.mPosition = x0 + delta * lra.mMaxDistance / sqrt(delta_len_sq); + } +} + +void SoftBodyMotionProperties::ApplyCollisionConstraintsAndUpdateVelocities(const SoftBodyUpdateContext &inContext) +{ + JPH_PROFILE_FUNCTION(); + + float dt = inContext.mSubStepDeltaTime; + float restitution_treshold = -2.0f * inContext.mGravity.Length() * dt; + float vertex_radius = mSettings->mVertexRadius; + for (Vertex &v : mVertices) + if (v.mInvMass > 0.0f) + { + // Remember previous velocity for restitution calculations + Vec3 prev_v = v.mVelocity; + + // XPBD velocity update + v.mVelocity = (v.mPosition - v.mPreviousPosition) / dt; + + // Satisfy collision constraint + if (v.mCollidingShapeIndex >= 0) + { + // Check if there is a collision + float projected_distance = -v.mCollisionPlane.SignedDistance(v.mPosition) + vertex_radius; + if (projected_distance > 0.0f) + { + // Remember that there was a collision + v.mHasContact = true; + mHasContact = true; + + // Sensors should not have a collision response + CollidingShape &cs = mCollidingShapes[v.mCollidingShapeIndex]; + if (!cs.mIsSensor) + { + // Note that we already calculated the velocity, so this does not affect the velocity (next iteration starts by setting previous position to current position) + Vec3 contact_normal = v.mCollisionPlane.GetNormal(); + v.mPosition += contact_normal * projected_distance; + + // Apply friction as described in Detailed Rigid Body Simulation with Extended Position Based Dynamics - Matthias Muller et al. + // See section 3.6: + // Inverse mass: w1 = 1 / m1, w2 = 1 / m2 + (r2 x n)^T I^-1 (r2 x n) = 0 for a static object + // r2 are the contact point relative to the center of mass of body 2 + // Lagrange multiplier for contact: lambda = -c / (w1 + w2) + // Where c is the constraint equation (the distance to the plane, negative because penetrating) + // Contact normal force: fn = lambda / dt^2 + // Delta velocity due to friction dv = -vt / |vt| * min(dt * friction * fn * (w1 + w2), |vt|) = -vt * min(-friction * c / (|vt| * dt), 1) + // Note that I think there is an error in the paper, I added a mass term, see: https://github.com/matthias-research/pages/issues/29 + // Relative velocity: vr = v1 - v2 - omega2 x r2 + // Normal velocity: vn = vr . contact_normal + // Tangential velocity: vt = vr - contact_normal * vn + // Impulse: p = dv / (w1 + w2) + // Changes in particle velocities: + // v1 = v1 + p / m1 + // v2 = v2 - p / m2 (no change when colliding with a static body) + // w2 = w2 - I^-1 (r2 x p) (no change when colliding with a static body) + if (cs.mMotionType == EMotionType::Dynamic) + { + // Calculate normal and tangential velocity (equation 30) + Vec3 r2 = v.mPosition - cs.mCenterOfMassTransform.GetTranslation(); + Vec3 v2 = cs.GetPointVelocity(r2); + Vec3 relative_velocity = v.mVelocity - v2; + Vec3 v_normal = contact_normal * contact_normal.Dot(relative_velocity); + Vec3 v_tangential = relative_velocity - v_normal; + float v_tangential_length = v_tangential.Length(); + + // Calculate resulting inverse mass of vertex + float vertex_inv_mass = cs.mSoftBodyInvMassScale * v.mInvMass; + + // Calculate inverse effective mass + Vec3 r2_cross_n = r2.Cross(contact_normal); + float w2 = cs.mInvMass + r2_cross_n.Dot(cs.mInvInertia * r2_cross_n); + float w1_plus_w2 = vertex_inv_mass + w2; + + // Calculate delta relative velocity due to friction (modified equation 31) + Vec3 dv; + if (v_tangential_length > 0.0f) + dv = v_tangential * min(cs.mFriction * projected_distance / (v_tangential_length * dt), 1.0f); + else + dv = Vec3::sZero(); + + // Calculate delta relative velocity due to restitution (equation 35) + dv += v_normal; + float prev_v_normal = (prev_v - v2).Dot(contact_normal); + if (prev_v_normal < restitution_treshold) + dv += cs.mRestitution * prev_v_normal * contact_normal; + + // Calculate impulse + Vec3 p = dv / w1_plus_w2; + + // Apply impulse to particle + v.mVelocity -= p * vertex_inv_mass; + + // Apply impulse to rigid body + cs.mLinearVelocity += p * cs.mInvMass; + cs.mAngularVelocity += cs.mInvInertia * r2.Cross(p); + + // Mark that the velocities of the body we hit need to be updated + cs.mUpdateVelocities = true; + } + else + { + // Body is not movable, equations are simpler + + // Calculate normal and tangential velocity (equation 30) + Vec3 v_normal = contact_normal * contact_normal.Dot(v.mVelocity); + Vec3 v_tangential = v.mVelocity - v_normal; + float v_tangential_length = v_tangential.Length(); + + // Apply friction (modified equation 31) + if (v_tangential_length > 0.0f) + v.mVelocity -= v_tangential * min(cs.mFriction * projected_distance / (v_tangential_length * dt), 1.0f); + + // Apply restitution (equation 35) + v.mVelocity -= v_normal; + float prev_v_normal = prev_v.Dot(contact_normal); + if (prev_v_normal < restitution_treshold) + v.mVelocity -= cs.mRestitution * prev_v_normal * contact_normal; + } + } + } + } + } +} + +void SoftBodyMotionProperties::UpdateSoftBodyState(SoftBodyUpdateContext &ioContext, const PhysicsSettings &inPhysicsSettings) +{ + JPH_PROFILE_FUNCTION(); + + // Contact callback + if (mHasContact && ioContext.mContactListener != nullptr) + ioContext.mContactListener->OnSoftBodyContactAdded(*ioContext.mBody, SoftBodyManifold(this)); + + // Loop through vertices once more to update the global state + float dt = ioContext.mDeltaTime; + float max_linear_velocity_sq = Square(GetMaxLinearVelocity()); + float max_v_sq = 0.0f; + Vec3 linear_velocity = Vec3::sZero(), angular_velocity = Vec3::sZero(); + mLocalPredictedBounds = mLocalBounds = { }; + mHasContact = false; + for (Vertex &v : mVertices) + { + // Calculate max square velocity + float v_sq = v.mVelocity.LengthSq(); + max_v_sq = max(max_v_sq, v_sq); + + // Clamp if velocity is too high + if (v_sq > max_linear_velocity_sq) + v.mVelocity *= sqrt(max_linear_velocity_sq / v_sq); + + // Calculate local linear/angular velocity + linear_velocity += v.mVelocity; + angular_velocity += v.mPosition.Cross(v.mVelocity); + + // Update local bounding box + mLocalBounds.Encapsulate(v.mPosition); + + // Create predicted position for the next frame in order to detect collisions before they happen + mLocalPredictedBounds.Encapsulate(v.mPosition + v.mVelocity * dt + ioContext.mDisplacementDueToGravity); + + // Reset collision data for the next iteration + v.mCollidingShapeIndex = -1; + v.mHasContact = false; + v.mLargestPenetration = -FLT_MAX; + } + + // Calculate linear/angular velocity of the body by averaging all vertices and bringing the value to world space + float num_vertices_divider = float(max(int(mVertices.size()), 1)); + SetLinearVelocity(ioContext.mCenterOfMassTransform.Multiply3x3(linear_velocity / num_vertices_divider)); + SetAngularVelocity(ioContext.mCenterOfMassTransform.Multiply3x3(angular_velocity / num_vertices_divider)); + + if (mUpdatePosition) + { + // Shift the body so that the position is the center of the local bounds + Vec3 delta = mLocalBounds.GetCenter(); + ioContext.mDeltaPosition = ioContext.mCenterOfMassTransform.Multiply3x3(delta); + for (Vertex &v : mVertices) + v.mPosition -= delta; + + // Offset bounds to match new position + mLocalBounds.Translate(-delta); + mLocalPredictedBounds.Translate(-delta); + } + else + ioContext.mDeltaPosition = Vec3::sZero(); + + // Test if we should go to sleep + if (GetAllowSleeping()) + { + if (max_v_sq > inPhysicsSettings.mPointVelocitySleepThreshold) + { + ResetSleepTestTimer(); + ioContext.mCanSleep = ECanSleep::CannotSleep; + } + else + ioContext.mCanSleep = AccumulateSleepTime(dt, inPhysicsSettings.mTimeBeforeSleep); + } + else + ioContext.mCanSleep = ECanSleep::CannotSleep; +} + +void SoftBodyMotionProperties::UpdateRigidBodyVelocities(const SoftBodyUpdateContext &inContext, BodyInterface &inBodyInterface) +{ + JPH_PROFILE_FUNCTION(); + + // Write back velocity deltas + for (const CollidingShape &cs : mCollidingShapes) + if (cs.mUpdateVelocities) + inBodyInterface.AddLinearAndAngularVelocity(cs.mBodyID, inContext.mCenterOfMassTransform.Multiply3x3(cs.mLinearVelocity - cs.mOriginalLinearVelocity), inContext.mCenterOfMassTransform.Multiply3x3(cs.mAngularVelocity - cs.mOriginalAngularVelocity)); + + // Clear colliding shapes to avoid hanging on to references to shapes + mCollidingShapes.clear(); +} + +void SoftBodyMotionProperties::InitializeUpdateContext(float inDeltaTime, Body &inSoftBody, const PhysicsSystem &inSystem, SoftBodyUpdateContext &ioContext) +{ + JPH_PROFILE_FUNCTION(); + + // Store body + ioContext.mBody = &inSoftBody; + ioContext.mMotionProperties = this; + ioContext.mContactListener = inSystem.GetSoftBodyContactListener(); + + // Convert gravity to local space + ioContext.mCenterOfMassTransform = inSoftBody.GetCenterOfMassTransform(); + ioContext.mGravity = ioContext.mCenterOfMassTransform.Multiply3x3Transposed(GetGravityFactor() * inSystem.GetGravity()); + + // Calculate delta time for sub step + ioContext.mDeltaTime = inDeltaTime; + ioContext.mSubStepDeltaTime = inDeltaTime / mNumIterations; + + // Calculate total displacement we'll have due to gravity over all sub steps + // The total displacement as produced by our integrator can be written as: Sum(i * g * dt^2, i = 0..mNumIterations). + // This is bigger than 0.5 * g * dt^2 because we first increment the velocity and then update the position + // Using Sum(i, i = 0..n) = n * (n + 1) / 2 we can write this as: + ioContext.mDisplacementDueToGravity = (0.5f * mNumIterations * (mNumIterations + 1) * Square(ioContext.mSubStepDeltaTime)) * ioContext.mGravity; +} + +void SoftBodyMotionProperties::StartNextIteration(const SoftBodyUpdateContext &ioContext) +{ + ApplyPressure(ioContext); + + IntegratePositions(ioContext); + + ApplyBendConstraints(ioContext); + + ApplyVolumeConstraints(ioContext); +} + +SoftBodyMotionProperties::EStatus SoftBodyMotionProperties::ParallelDetermineCollisionPlanes(SoftBodyUpdateContext &ioContext) +{ + // Do a relaxed read first to see if there is any work to do (this prevents us from doing expensive atomic operations and also prevents us from continuously incrementing the counter and overflowing it) + uint num_vertices = (uint)mVertices.size(); + if (ioContext.mNextCollisionVertex.load(memory_order_relaxed) < num_vertices) + { + // Fetch next batch of vertices to process + uint next_vertex = ioContext.mNextCollisionVertex.fetch_add(SoftBodyUpdateContext::cVertexCollisionBatch, memory_order_acquire); + if (next_vertex < num_vertices) + { + // Process collision planes + uint num_vertices_to_process = min(SoftBodyUpdateContext::cVertexCollisionBatch, num_vertices - next_vertex); + DetermineCollisionPlanes(ioContext, next_vertex, num_vertices_to_process); + uint vertices_processed = ioContext.mNumCollisionVerticesProcessed.fetch_add(SoftBodyUpdateContext::cVertexCollisionBatch, memory_order_release) + num_vertices_to_process; + if (vertices_processed >= num_vertices) + { + // Start the first iteration + JPH_IF_ENABLE_ASSERTS(uint iteration =) ioContext.mNextIteration.fetch_add(1, memory_order_relaxed); + JPH_ASSERT(iteration == 0); + StartNextIteration(ioContext); + ioContext.mState.store(SoftBodyUpdateContext::EState::ApplyEdgeConstraints, memory_order_release); + } + return EStatus::DidWork; + } + } + return EStatus::NoWork; +} + +SoftBodyMotionProperties::EStatus SoftBodyMotionProperties::ParallelApplyEdgeConstraints(SoftBodyUpdateContext &ioContext, const PhysicsSettings &inPhysicsSettings) +{ + // Do a relaxed read first to see if there is any work to do (this prevents us from doing expensive atomic operations and also prevents us from continuously incrementing the counter and overflowing it) + uint num_groups = (uint)mSettings->mEdgeGroupEndIndices.size(); + JPH_ASSERT(num_groups > 0, "SoftBodySharedSettings::Optimize should have been called!"); + uint32 edge_group, edge_start_idx; + SoftBodyUpdateContext::sGetEdgeGroupAndStartIdx(ioContext.mNextEdgeConstraint.load(memory_order_relaxed), edge_group, edge_start_idx); + if (edge_group < num_groups) + { + uint edge_group_size = mSettings->GetEdgeGroupSize(edge_group); + if (edge_start_idx < edge_group_size || edge_group_size == 0) + { + // Fetch the next batch of edges to process + uint64 next_edge_batch = ioContext.mNextEdgeConstraint.fetch_add(SoftBodyUpdateContext::cEdgeConstraintBatch, memory_order_acquire); + SoftBodyUpdateContext::sGetEdgeGroupAndStartIdx(next_edge_batch, edge_group, edge_start_idx); + if (edge_group < num_groups) + { + bool non_parallel_group = edge_group == num_groups - 1; // Last group is the non-parallel group and goes as a whole + edge_group_size = mSettings->GetEdgeGroupSize(edge_group); + if (non_parallel_group? edge_start_idx == 0 : edge_start_idx < edge_group_size) + { + // Process edges + uint num_edges_to_process = non_parallel_group? edge_group_size : min(SoftBodyUpdateContext::cEdgeConstraintBatch, edge_group_size - edge_start_idx); + if (edge_group > 0) + edge_start_idx += mSettings->mEdgeGroupEndIndices[edge_group - 1]; + ApplyEdgeConstraints(ioContext, edge_start_idx, edge_start_idx + num_edges_to_process); + + // Test if we're at the end of this group + uint edge_constraints_processed = ioContext.mNumEdgeConstraintsProcessed.fetch_add(num_edges_to_process, memory_order_relaxed) + num_edges_to_process; + if (edge_constraints_processed >= edge_group_size) + { + // Non parallel group is the last group (which is also the only group that can be empty) + if (non_parallel_group || mSettings->GetEdgeGroupSize(edge_group + 1) == 0) + { + // Finish the iteration + ApplyLRAConstraints(); + + ApplyCollisionConstraintsAndUpdateVelocities(ioContext); + + ApplySkinConstraints(ioContext); + + uint iteration = ioContext.mNextIteration.fetch_add(1, memory_order_relaxed); + if (iteration < mNumIterations) + { + // Start a new iteration + StartNextIteration(ioContext); + + // Reset next edge to process + ioContext.mNumEdgeConstraintsProcessed.store(0, memory_order_relaxed); + ioContext.mNextEdgeConstraint.store(0, memory_order_release); + } + else + { + // On final iteration we update the state + UpdateSoftBodyState(ioContext, inPhysicsSettings); + + ioContext.mState.store(SoftBodyUpdateContext::EState::Done, memory_order_release); + return EStatus::Done; + } + } + else + { + // Next group + ioContext.mNumEdgeConstraintsProcessed.store(0, memory_order_relaxed); + ioContext.mNextEdgeConstraint.store(SoftBodyUpdateContext::sGetEdgeGroupStart(edge_group + 1), memory_order_release); + } + } + return EStatus::DidWork; + } + } + } + } + return EStatus::NoWork; +} + +SoftBodyMotionProperties::EStatus SoftBodyMotionProperties::ParallelUpdate(SoftBodyUpdateContext &ioContext, const PhysicsSettings &inPhysicsSettings) +{ + switch (ioContext.mState.load(memory_order_relaxed)) + { + case SoftBodyUpdateContext::EState::DetermineCollisionPlanes: + return ParallelDetermineCollisionPlanes(ioContext); + + case SoftBodyUpdateContext::EState::ApplyEdgeConstraints: + return ParallelApplyEdgeConstraints(ioContext, inPhysicsSettings); + + case SoftBodyUpdateContext::EState::Done: + return EStatus::Done; + + default: + JPH_ASSERT(false); + return EStatus::NoWork; + } +} + +void SoftBodyMotionProperties::SkinVertices(RMat44Arg inCenterOfMassTransform, const Mat44 *inJointMatrices, [[maybe_unused]] uint inNumJoints, bool inHardSkinAll, TempAllocator &ioTempAllocator) +{ + // Calculate the skin matrices + uint num_skin_matrices = uint(mSettings->mInvBindMatrices.size()); + uint skin_matrices_size = num_skin_matrices * sizeof(Mat44); + Mat44 *skin_matrices = (Mat44 *)ioTempAllocator.Allocate(skin_matrices_size); + const Mat44 *skin_matrices_end = skin_matrices + num_skin_matrices; + const InvBind *inv_bind_matrix = mSettings->mInvBindMatrices.data(); + for (Mat44 *s = skin_matrices; s < skin_matrices_end; ++s, ++inv_bind_matrix) + *s = inJointMatrices[inv_bind_matrix->mJointIndex] * inv_bind_matrix->mInvBind; + + // Skin the vertices + mSkinStateTransform = inCenterOfMassTransform; + JPH_IF_ENABLE_ASSERTS(uint num_vertices = uint(mSettings->mVertices.size());) + JPH_ASSERT(mSkinState.size() == num_vertices); + const SoftBodySharedSettings::Vertex *in_vertices = mSettings->mVertices.data(); + for (const Skinned &s : mSettings->mSkinnedConstraints) + { + // Get bind pose + JPH_ASSERT(s.mVertex < num_vertices); + Vec3 bind_pos = Vec3::sLoadFloat3Unsafe(in_vertices[s.mVertex].mPosition); + + // Skin vertex + Vec3 pos = Vec3::sZero(); + for (const SkinWeight &w : s.mWeights) + { + // We assume that the first zero weight is the end of the list + if (w.mWeight == 0.0f) + break; + + JPH_ASSERT(w.mInvBindIndex < num_skin_matrices); + pos += w.mWeight * (skin_matrices[w.mInvBindIndex] * bind_pos); + } + mSkinState[s.mVertex].mPosition = pos; + } + + // Calculate the normals + for (const Skinned &s : mSettings->mSkinnedConstraints) + { + Vec3 normal = Vec3::sZero(); + uint32 num_faces = s.mNormalInfo >> 24; + if (num_faces > 0) + { + // Calculate normal + const uint32 *f = &mSettings->mSkinnedConstraintNormals[s.mNormalInfo & 0xffffff]; + const uint32 *f_end = f + num_faces; + while (f < f_end) + { + const Face &face = mSettings->mFaces[*f]; + Vec3 v0 = mSkinState[face.mVertex[0]].mPosition; + Vec3 v1 = mSkinState[face.mVertex[1]].mPosition; + Vec3 v2 = mSkinState[face.mVertex[2]].mPosition; + normal += (v1 - v0).Cross(v2 - v0).NormalizedOr(Vec3::sZero()); + ++f; + } + normal = normal.NormalizedOr(Vec3::sZero()); + } + mSkinState[s.mVertex].mNormal = normal; + } + + ioTempAllocator.Free(skin_matrices, skin_matrices_size); + + if (inHardSkinAll) + { + // Hard skin all vertices and reset their velocities + for (const Skinned &s : mSettings->mSkinnedConstraints) + { + Vertex &vertex = mVertices[s.mVertex]; + vertex.mPosition = mSkinState[s.mVertex].mPosition; + vertex.mVelocity = Vec3::sZero(); + } + } + else if (!mEnableSkinConstraints) + { + // Hard skin only the kinematic vertices as we will not solve the skin constraints later + for (const Skinned &s : mSettings->mSkinnedConstraints) + if (s.mMaxDistance == 0.0f) + { + Vertex &vertex = mVertices[s.mVertex]; + vertex.mPosition = mSkinState[s.mVertex].mPosition; + } + } +} + +void SoftBodyMotionProperties::CustomUpdate(float inDeltaTime, Body &ioSoftBody, PhysicsSystem &inSystem) +{ + JPH_PROFILE_FUNCTION(); + + // Create update context + SoftBodyUpdateContext context; + InitializeUpdateContext(inDeltaTime, ioSoftBody, inSystem, context); + + // Determine bodies we're colliding with + DetermineCollidingShapes(context, inSystem, inSystem.GetBodyLockInterface()); + + // Call the internal update until it finishes + EStatus status; + const PhysicsSettings &settings = inSystem.GetPhysicsSettings(); + while ((status = ParallelUpdate(context, settings)) == EStatus::DidWork) + continue; + JPH_ASSERT(status == EStatus::Done); + + // Update the state of the bodies we've collided with + UpdateRigidBodyVelocities(context, inSystem.GetBodyInterface()); + + // Update position of the soft body + if (mUpdatePosition) + inSystem.GetBodyInterface().SetPosition(ioSoftBody.GetID(), ioSoftBody.GetPosition() + context.mDeltaPosition, EActivation::DontActivate); +} + +#ifdef JPH_DEBUG_RENDERER + +void SoftBodyMotionProperties::DrawVertices(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform) const +{ + for (const Vertex &v : mVertices) + inRenderer->DrawMarker(inCenterOfMassTransform * v.mPosition, v.mInvMass > 0.0f? Color::sGreen : Color::sRed, 0.05f); +} + +void SoftBodyMotionProperties::DrawVertexVelocities(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform) const +{ + for (const Vertex &v : mVertices) + inRenderer->DrawArrow(inCenterOfMassTransform * v.mPosition, inCenterOfMassTransform * (v.mPosition + v.mVelocity), Color::sYellow, 0.01f); +} + +void SoftBodyMotionProperties::DrawEdgeConstraints(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform) const +{ + for (const Edge &e : mSettings->mEdgeConstraints) + inRenderer->DrawLine(inCenterOfMassTransform * mVertices[e.mVertex[0]].mPosition, inCenterOfMassTransform * mVertices[e.mVertex[1]].mPosition, Color::sWhite); +} + +void SoftBodyMotionProperties::DrawBendConstraints(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform) const +{ + for (const DihedralBend &b : mSettings->mDihedralBendConstraints) + { + RVec3 x0 = inCenterOfMassTransform * mVertices[b.mVertex[0]].mPosition; + RVec3 x1 = inCenterOfMassTransform * mVertices[b.mVertex[1]].mPosition; + RVec3 x2 = inCenterOfMassTransform * mVertices[b.mVertex[2]].mPosition; + RVec3 x3 = inCenterOfMassTransform * mVertices[b.mVertex[3]].mPosition; + RVec3 c_edge = 0.5_r * (x0 + x1); + RVec3 c0 = (x0 + x1 + x2) / 3.0_r; + RVec3 c1 = (x0 + x1 + x3) / 3.0_r; + + inRenderer->DrawArrow(0.9_r * x0 + 0.1_r * x1, 0.1_r * x0 + 0.9_r * x1, Color::sDarkGreen, 0.01f); + inRenderer->DrawLine(c_edge, 0.1_r * c_edge + 0.9_r * c0, Color::sGreen); + inRenderer->DrawLine(c_edge, 0.1_r * c_edge + 0.9_r * c1, Color::sGreen); + } +} + +void SoftBodyMotionProperties::DrawVolumeConstraints(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform) const +{ + for (const Volume &v : mSettings->mVolumeConstraints) + { + RVec3 x1 = inCenterOfMassTransform * mVertices[v.mVertex[0]].mPosition; + RVec3 x2 = inCenterOfMassTransform * mVertices[v.mVertex[1]].mPosition; + RVec3 x3 = inCenterOfMassTransform * mVertices[v.mVertex[2]].mPosition; + RVec3 x4 = inCenterOfMassTransform * mVertices[v.mVertex[3]].mPosition; + + inRenderer->DrawTriangle(x1, x3, x2, Color::sYellow, DebugRenderer::ECastShadow::On); + inRenderer->DrawTriangle(x2, x3, x4, Color::sYellow, DebugRenderer::ECastShadow::On); + inRenderer->DrawTriangle(x1, x4, x3, Color::sYellow, DebugRenderer::ECastShadow::On); + inRenderer->DrawTriangle(x1, x2, x4, Color::sYellow, DebugRenderer::ECastShadow::On); + } +} + +void SoftBodyMotionProperties::DrawSkinConstraints(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform) const +{ + for (const Skinned &s : mSettings->mSkinnedConstraints) + { + const SkinState &skin_state = mSkinState[s.mVertex]; + DebugRenderer::sInstance->DrawArrow(mSkinStateTransform * skin_state.mPosition, mSkinStateTransform * (skin_state.mPosition + 0.1f * skin_state.mNormal), Color::sOrange, 0.01f); + DebugRenderer::sInstance->DrawLine(mSkinStateTransform * skin_state.mPosition, inCenterOfMassTransform * mVertices[s.mVertex].mPosition, Color::sBlue); + } +} + +void SoftBodyMotionProperties::DrawLRAConstraints(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform) const +{ + for (const LRA &l : mSettings->mLRAConstraints) + inRenderer->DrawLine(inCenterOfMassTransform * mVertices[l.mVertex[0]].mPosition, inCenterOfMassTransform * mVertices[l.mVertex[1]].mPosition, Color::sGrey); +} + +void SoftBodyMotionProperties::DrawPredictedBounds(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform) const +{ + inRenderer->DrawWireBox(inCenterOfMassTransform, mLocalPredictedBounds, Color::sRed); +} + +#endif // JPH_DEBUG_RENDERER + +void SoftBodyMotionProperties::SaveState(StateRecorder &inStream) const +{ + MotionProperties::SaveState(inStream); + + for (const Vertex &v : mVertices) + { + inStream.Write(v.mPreviousPosition); + inStream.Write(v.mPosition); + inStream.Write(v.mVelocity); + } + + inStream.Write(mLocalBounds.mMin); + inStream.Write(mLocalBounds.mMax); + inStream.Write(mLocalPredictedBounds.mMin); + inStream.Write(mLocalPredictedBounds.mMax); +} + +void SoftBodyMotionProperties::RestoreState(StateRecorder &inStream) +{ + MotionProperties::RestoreState(inStream); + + for (Vertex &v : mVertices) + { + inStream.Read(v.mPreviousPosition); + inStream.Read(v.mPosition); + inStream.Read(v.mVelocity); + } + + inStream.Read(mLocalBounds.mMin); + inStream.Read(mLocalBounds.mMax); + inStream.Read(mLocalPredictedBounds.mMin); + inStream.Read(mLocalPredictedBounds.mMax); +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyMotionProperties.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyMotionProperties.h new file mode 100644 index 00000000000..0124a33b3b1 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyMotionProperties.h @@ -0,0 +1,254 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2023 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +class PhysicsSystem; +class BodyInterface; +class BodyLockInterface; +struct PhysicsSettings; +class Body; +class Shape; +class SoftBodyCreationSettings; +class TempAllocator; +#ifdef JPH_DEBUG_RENDERER +class DebugRenderer; +#endif // JPH_DEBUG_RENDERER + +/// This class contains the runtime information of a soft body. +// +// Based on: XPBD, Extended Position Based Dynamics, Matthias Muller, Ten Minute Physics +// See: https://matthias-research.github.io/pages/tenMinutePhysics/09-xpbd.pdf +class JPH_EXPORT SoftBodyMotionProperties : public MotionProperties +{ +public: + using Vertex = SoftBodyVertex; + using Edge = SoftBodySharedSettings::Edge; + using Face = SoftBodySharedSettings::Face; + using DihedralBend = SoftBodySharedSettings::DihedralBend; + using Volume = SoftBodySharedSettings::Volume; + using InvBind = SoftBodySharedSettings::InvBind; + using SkinWeight = SoftBodySharedSettings::SkinWeight; + using Skinned = SoftBodySharedSettings::Skinned; + using LRA = SoftBodySharedSettings::LRA; + + /// Initialize the soft body motion properties + void Initialize(const SoftBodyCreationSettings &inSettings); + + /// Get the shared settings of the soft body + const SoftBodySharedSettings * GetSettings() const { return mSettings; } + + /// Get the vertices of the soft body + const Array & GetVertices() const { return mVertices; } + Array & GetVertices() { return mVertices; } + + /// Access an individual vertex + const Vertex & GetVertex(uint inIndex) const { return mVertices[inIndex]; } + Vertex & GetVertex(uint inIndex) { return mVertices[inIndex]; } + + /// Get the materials of the soft body + const PhysicsMaterialList & GetMaterials() const { return mSettings->mMaterials; } + + /// Get the faces of the soft body + const Array & GetFaces() const { return mSettings->mFaces; } + + /// Access to an individual face + const Face & GetFace(uint inIndex) const { return mSettings->mFaces[inIndex]; } + + /// Get the number of solver iterations + uint32 GetNumIterations() const { return mNumIterations; } + void SetNumIterations(uint32 inNumIterations) { mNumIterations = inNumIterations; } + + /// Get the pressure of the soft body + float GetPressure() const { return mPressure; } + void SetPressure(float inPressure) { mPressure = inPressure; } + + /// Update the position of the body while simulating (set to false for something that is attached to the static world) + bool GetUpdatePosition() const { return mUpdatePosition; } + void SetUpdatePosition(bool inUpdatePosition) { mUpdatePosition = inUpdatePosition; } + + /// Global setting to turn on/off skin constraints + bool GetEnableSkinConstraints() const { return mEnableSkinConstraints; } + void SetEnableSkinConstraints(bool inEnableSkinConstraints) { mEnableSkinConstraints = inEnableSkinConstraints; } + + /// Multiplier applied to Skinned::mMaxDistance to allow tightening or loosening of the skin constraints. 0 to hard skin all vertices. + float GetSkinnedMaxDistanceMultiplier() const { return mSkinnedMaxDistanceMultiplier; } + void SetSkinnedMaxDistanceMultiplier(float inSkinnedMaxDistanceMultiplier) { mSkinnedMaxDistanceMultiplier = inSkinnedMaxDistanceMultiplier; } + + /// Get local bounding box + const AABox & GetLocalBounds() const { return mLocalBounds; } + + /// Get the volume of the soft body. Note can become negative if the shape is inside out! + float GetVolume() const { return GetVolumeTimesSix() / 6.0f; } + + /// Calculate the total mass and inertia of this body based on the current state of the vertices + void CalculateMassAndInertia(); + +#ifdef JPH_DEBUG_RENDERER + /// Draw the state of a soft body + void DrawVertices(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform) const; + void DrawVertexVelocities(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform) const; + void DrawEdgeConstraints(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform) const; + void DrawBendConstraints(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform) const; + void DrawVolumeConstraints(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform) const; + void DrawSkinConstraints(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform) const; + void DrawLRAConstraints(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform) const; + void DrawPredictedBounds(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform) const; +#endif // JPH_DEBUG_RENDERER + + /// Saving state for replay + void SaveState(StateRecorder &inStream) const; + + /// Restoring state for replay + void RestoreState(StateRecorder &inStream); + + /// Skin vertices to supplied joints, information is used by the skinned constraints. + /// @param inCenterOfMassTransform Value of Body::GetCenterOfMassTransform(). + /// @param inJointMatrices The joint matrices must be expressed relative to inCenterOfMassTransform. + /// @param inNumJoints Indicates how large the inJointMatrices array is (used only for validating out of bounds). + /// @param inHardSkinAll Can be used to position all vertices on the skinned vertices and can be used to hard reset the soft body. + /// @param ioTempAllocator Allocator. + void SkinVertices(RMat44Arg inCenterOfMassTransform, const Mat44 *inJointMatrices, uint inNumJoints, bool inHardSkinAll, TempAllocator &ioTempAllocator); + + /// This function allows you to update the soft body immediately without going through the PhysicsSystem. + /// This is useful if the soft body is teleported and needs to 'settle' or it can be used if a the soft body + /// is not added to the PhysicsSystem and needs to be updated manually. One reason for not adding it to the + /// PhyicsSystem is that you might want to update a soft body immediately after updating an animated object + /// that has the soft body attached to it. If the soft body is added to the PhysicsSystem it will be updated + /// by it, so calling this function will effectively update it twice. Note that when you use this function, + /// only the current thread will be used, whereas if you update through the PhysicsSystem, multiple threads may + /// be used. + /// Note that this will bypass any sleep checks. Since the dynamic objects that the soft body touches + /// will not move during this call, there can be simulation artifacts if you call this function multiple times + /// without running the physics simulation step. + void CustomUpdate(float inDeltaTime, Body &ioSoftBody, PhysicsSystem &inSystem); + + //////////////////////////////////////////////////////////// + // FUNCTIONS BELOW THIS LINE ARE FOR INTERNAL USE ONLY + //////////////////////////////////////////////////////////// + + /// Initialize the update context. Not part of the public API. + void InitializeUpdateContext(float inDeltaTime, Body &inSoftBody, const PhysicsSystem &inSystem, SoftBodyUpdateContext &ioContext); + + /// Do a broad phase check and collect all bodies that can possibly collide with this soft body. Not part of the public API. + void DetermineCollidingShapes(const SoftBodyUpdateContext &inContext, const PhysicsSystem &inSystem, const BodyLockInterface &inBodyLockInterface); + + /// Return code for ParallelUpdate + enum class EStatus + { + NoWork = 1 << 0, ///< No work was done because other threads were still working on a batch that cannot run concurrently + DidWork = 1 << 1, ///< Work was done to progress the update + Done = 1 << 2, ///< All work is done + }; + + /// Update the soft body, will process a batch of work. Not part of the public API. + EStatus ParallelUpdate(SoftBodyUpdateContext &ioContext, const PhysicsSettings &inPhysicsSettings); + + /// Update the velocities of all rigid bodies that we collided with. Not part of the public API. + void UpdateRigidBodyVelocities(const SoftBodyUpdateContext &inContext, BodyInterface &inBodyInterface); + +private: + // SoftBodyManifold needs to have access to CollidingShape + friend class SoftBodyManifold; + + // Collect information about the colliding bodies + struct CollidingShape + { + /// Get the velocity of a point on this body + Vec3 GetPointVelocity(Vec3Arg inPointRelativeToCOM) const + { + return mLinearVelocity + mAngularVelocity.Cross(inPointRelativeToCOM); + } + + Mat44 mCenterOfMassTransform; ///< Transform of the body relative to the soft body + RefConst mShape; ///< Shape of the body we hit + BodyID mBodyID; ///< Body ID of the body we hit + EMotionType mMotionType; ///< Motion type of the body we hit + bool mIsSensor; ///< If the contact should be treated as a sensor vs body contact (no collision response) + float mInvMass; ///< Inverse mass of the body we hit + float mFriction; ///< Combined friction of the two bodies + float mRestitution; ///< Combined restitution of the two bodies + float mSoftBodyInvMassScale; ///< Scale factor for the inverse mass of the soft body vertices + bool mUpdateVelocities; ///< If the linear/angular velocity changed and the body needs to be updated + Mat44 mInvInertia; ///< Inverse inertia in local space to the soft body + Vec3 mLinearVelocity; ///< Linear velocity of the body in local space to the soft body + Vec3 mAngularVelocity; ///< Angular velocity of the body in local space to the soft body + Vec3 mOriginalLinearVelocity; ///< Linear velocity of the body in local space to the soft body at start + Vec3 mOriginalAngularVelocity; ///< Angular velocity of the body in local space to the soft body at start + }; + + // Information about the state of all skinned vertices + struct SkinState + { + Vec3 mPosition = Vec3::sNaN(); + Vec3 mNormal = Vec3::sNaN(); + }; + + /// Do a narrow phase check and determine the closest feature that we can collide with + void DetermineCollisionPlanes(const SoftBodyUpdateContext &inContext, uint inVertexStart, uint inNumVertices); + + /// Apply pressure force and update the vertex velocities + void ApplyPressure(const SoftBodyUpdateContext &inContext); + + /// Integrate the positions of all vertices by 1 sub step + void IntegratePositions(const SoftBodyUpdateContext &inContext); + + /// Enforce all bend constraints + void ApplyBendConstraints(const SoftBodyUpdateContext &inContext); + + /// Enforce all volume constraints + void ApplyVolumeConstraints(const SoftBodyUpdateContext &inContext); + + /// Enforce all skin constraints + void ApplySkinConstraints(const SoftBodyUpdateContext &inContext); + + /// Enforce all edge constraints + void ApplyEdgeConstraints(const SoftBodyUpdateContext &inContext, uint inStartIndex, uint inEndIndex); + + /// Enforce all LRA constraints + void ApplyLRAConstraints(); + + /// Enforce all collision constraints & update all velocities according the XPBD algorithm + void ApplyCollisionConstraintsAndUpdateVelocities(const SoftBodyUpdateContext &inContext); + + /// Update the state of the soft body (position, velocity, bounds) + void UpdateSoftBodyState(SoftBodyUpdateContext &ioContext, const PhysicsSettings &inPhysicsSettings); + + /// Executes tasks that need to run on the start of an iteration (i.e. the stuff that can't run in parallel) + void StartNextIteration(const SoftBodyUpdateContext &ioContext); + + /// Helper function for ParallelUpdate that works on batches of collision planes + EStatus ParallelDetermineCollisionPlanes(SoftBodyUpdateContext &ioContext); + + /// Helper function for ParallelUpdate that works on batches of edges + EStatus ParallelApplyEdgeConstraints(SoftBodyUpdateContext &ioContext, const PhysicsSettings &inPhysicsSettings); + + /// Returns 6 times the volume of the soft body + float GetVolumeTimesSix() const; + + RMat44 mSkinStateTransform = RMat44::sIdentity(); ///< The matrix that transforms mSkinState to world space + RefConst mSettings; ///< Configuration of the particles and constraints + Array mVertices; ///< Current state of all vertices in the simulation + Array mCollidingShapes; ///< List of colliding shapes retrieved during the last update + Array mSkinState; ///< List of skinned positions (1-on-1 with mVertices but only those that are used by the skinning constraints are filled in) + AABox mLocalBounds; ///< Bounding box of all vertices + AABox mLocalPredictedBounds; ///< Predicted bounding box for all vertices using extrapolation of velocity by last step delta time + uint32 mNumIterations; ///< Number of solver iterations + float mPressure; ///< n * R * T, amount of substance * ideal gas constant * absolute temperature, see https://en.wikipedia.org/wiki/Pressure + float mSkinnedMaxDistanceMultiplier = 1.0f; ///< Multiplier applied to Skinned::mMaxDistance to allow tightening or loosening of the skin constraints + bool mUpdatePosition; ///< Update the position of the body while simulating (set to false for something that is attached to the static world) + bool mHasContact = false; ///< True if the soft body has collided with anything in the last update + bool mEnableSkinConstraints = true; ///< If skin constraints are enabled +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyShape.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyShape.cpp new file mode 100644 index 00000000000..391bdef1bbb --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyShape.cpp @@ -0,0 +1,338 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2023 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef JPH_DEBUG_RENDERER + #include +#endif // JPH_DEBUG_RENDERER + +JPH_NAMESPACE_BEGIN + +uint SoftBodyShape::GetSubShapeIDBits() const +{ + // Ensure we have enough bits to encode our shape [0, n - 1] + uint32 n = (uint32)mSoftBodyMotionProperties->GetFaces().size() - 1; + return 32 - CountLeadingZeros(n); +} + +uint32 SoftBodyShape::GetFaceIndex(const SubShapeID &inSubShapeID) const +{ + SubShapeID remainder; + uint32 face_index = inSubShapeID.PopID(GetSubShapeIDBits(), remainder); + JPH_ASSERT(remainder.IsEmpty()); + return face_index; +} + +AABox SoftBodyShape::GetLocalBounds() const +{ + return mSoftBodyMotionProperties->GetLocalBounds(); +} + +bool SoftBodyShape::CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const +{ + JPH_PROFILE_FUNCTION(); + + uint num_triangle_bits = GetSubShapeIDBits(); + uint triangle_idx = uint(-1); + + const Array &vertices = mSoftBodyMotionProperties->GetVertices(); + for (const SoftBodyMotionProperties::Face &f : mSoftBodyMotionProperties->GetFaces()) + { + Vec3 x1 = vertices[f.mVertex[0]].mPosition; + Vec3 x2 = vertices[f.mVertex[1]].mPosition; + Vec3 x3 = vertices[f.mVertex[2]].mPosition; + + float fraction = RayTriangle(inRay.mOrigin, inRay.mDirection, x1, x2, x3); + if (fraction < ioHit.mFraction) + { + // Store fraction + ioHit.mFraction = fraction; + + // Store triangle index + triangle_idx = uint(&f - mSoftBodyMotionProperties->GetFaces().data()); + } + } + + if (triangle_idx == uint(-1)) + return false; + + ioHit.mSubShapeID2 = inSubShapeIDCreator.PushID(triangle_idx, num_triangle_bits).GetID(); + return true; +} + +void SoftBodyShape::CastRay(const RayCast &inRay, const RayCastSettings &inRayCastSettings, const SubShapeIDCreator &inSubShapeIDCreator, CastRayCollector &ioCollector, const ShapeFilter &inShapeFilter) const +{ + JPH_PROFILE_FUNCTION(); + + // Test shape filter + if (!inShapeFilter.ShouldCollide(this, inSubShapeIDCreator.GetID())) + return; + + uint num_triangle_bits = GetSubShapeIDBits(); + + const Array &vertices = mSoftBodyMotionProperties->GetVertices(); + for (const SoftBodyMotionProperties::Face &f : mSoftBodyMotionProperties->GetFaces()) + { + Vec3 x1 = vertices[f.mVertex[0]].mPosition; + Vec3 x2 = vertices[f.mVertex[1]].mPosition; + Vec3 x3 = vertices[f.mVertex[2]].mPosition; + + // Back facing check + if (inRayCastSettings.mBackFaceMode == EBackFaceMode::IgnoreBackFaces && (x2 - x1).Cross(x3 - x1).Dot(inRay.mDirection) > 0.0f) + return; + + // Test ray against triangle + float fraction = RayTriangle(inRay.mOrigin, inRay.mDirection, x1, x2, x3); + if (fraction < ioCollector.GetEarlyOutFraction()) + { + // Better hit than the current hit + RayCastResult hit; + hit.mBodyID = TransformedShape::sGetBodyID(ioCollector.GetContext()); + hit.mFraction = fraction; + hit.mSubShapeID2 = inSubShapeIDCreator.PushID(uint(&f - mSoftBodyMotionProperties->GetFaces().data()), num_triangle_bits).GetID(); + ioCollector.AddHit(hit); + } + } +} + +void SoftBodyShape::CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter) const +{ + sCollidePointUsingRayCast(*this, inPoint, inSubShapeIDCreator, ioCollector, inShapeFilter); +} + +void SoftBodyShape::CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, SoftBodyVertex *ioVertices, uint inNumVertices, float inDeltaTime, Vec3Arg inDisplacementDueToGravity, int inCollidingShapeIndex) const +{ + /* Not implemented */ +} + +const PhysicsMaterial *SoftBodyShape::GetMaterial(const SubShapeID &inSubShapeID) const +{ + SubShapeID remainder; + uint triangle_idx = inSubShapeID.PopID(GetSubShapeIDBits(), remainder); + JPH_ASSERT(remainder.IsEmpty()); + + const SoftBodyMotionProperties::Face &f = mSoftBodyMotionProperties->GetFace(triangle_idx); + return mSoftBodyMotionProperties->GetMaterials()[f.mMaterialIndex]; +} + +Vec3 SoftBodyShape::GetSurfaceNormal(const SubShapeID &inSubShapeID, Vec3Arg inLocalSurfacePosition) const +{ + SubShapeID remainder; + uint triangle_idx = inSubShapeID.PopID(GetSubShapeIDBits(), remainder); + JPH_ASSERT(remainder.IsEmpty()); + + const SoftBodyMotionProperties::Face &f = mSoftBodyMotionProperties->GetFace(triangle_idx); + const Array &vertices = mSoftBodyMotionProperties->GetVertices(); + + Vec3 x1 = vertices[f.mVertex[0]].mPosition; + Vec3 x2 = vertices[f.mVertex[1]].mPosition; + Vec3 x3 = vertices[f.mVertex[2]].mPosition; + + return (x2 - x1).Cross(x3 - x1).NormalizedOr(Vec3::sAxisY()); +} + +void SoftBodyShape::GetSupportingFace(const SubShapeID &inSubShapeID, Vec3Arg inDirection, Vec3Arg inScale, Mat44Arg inCenterOfMassTransform, SupportingFace &outVertices) const +{ + SubShapeID remainder; + uint triangle_idx = inSubShapeID.PopID(GetSubShapeIDBits(), remainder); + JPH_ASSERT(remainder.IsEmpty()); + + const SoftBodyMotionProperties::Face &f = mSoftBodyMotionProperties->GetFace(triangle_idx); + const Array &vertices = mSoftBodyMotionProperties->GetVertices(); + + for (uint32 i : f.mVertex) + outVertices.push_back(inCenterOfMassTransform * (inScale * vertices[i].mPosition)); +} + +void SoftBodyShape::GetSubmergedVolume(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, const Plane &inSurface, float &outTotalVolume, float &outSubmergedVolume, Vec3 &outCenterOfBuoyancy JPH_IF_DEBUG_RENDERER(, RVec3Arg inBaseOffset)) const +{ + outSubmergedVolume = 0.0f; + outTotalVolume = mSoftBodyMotionProperties->GetVolume(); + outCenterOfBuoyancy = Vec3::sZero(); +} + +#ifdef JPH_DEBUG_RENDERER + +void SoftBodyShape::Draw(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inUseMaterialColors, bool inDrawWireframe) const +{ + const Array &vertices = mSoftBodyMotionProperties->GetVertices(); + for (const SoftBodyMotionProperties::Face &f : mSoftBodyMotionProperties->GetFaces()) + { + RVec3 x1 = inCenterOfMassTransform * vertices[f.mVertex[0]].mPosition; + RVec3 x2 = inCenterOfMassTransform * vertices[f.mVertex[1]].mPosition; + RVec3 x3 = inCenterOfMassTransform * vertices[f.mVertex[2]].mPosition; + + inRenderer->DrawTriangle(x1, x2, x3, inColor, DebugRenderer::ECastShadow::On); + } +} + +#endif // JPH_DEBUG_RENDERER + +struct SoftBodyShape::SBSGetTrianglesContext +{ + Mat44 mCenterOfMassTransform; + int mTriangleIndex; +}; + +void SoftBodyShape::GetTrianglesStart(GetTrianglesContext &ioContext, [[maybe_unused]] const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) const +{ + SBSGetTrianglesContext &context = reinterpret_cast(ioContext); + context.mCenterOfMassTransform = Mat44::sRotationTranslation(inRotation, inPositionCOM) * Mat44::sScale(inScale); + context.mTriangleIndex = 0; +} + +int SoftBodyShape::GetTrianglesNext(GetTrianglesContext &ioContext, int inMaxTrianglesRequested, Float3 *outTriangleVertices, const PhysicsMaterial **outMaterials) const +{ + SBSGetTrianglesContext &context = reinterpret_cast(ioContext); + + const Array &faces = mSoftBodyMotionProperties->GetFaces(); + const Array &vertices = mSoftBodyMotionProperties->GetVertices(); + const PhysicsMaterialList &materials = mSoftBodyMotionProperties->GetMaterials(); + + int num_triangles = min(inMaxTrianglesRequested, (int)faces.size() - context.mTriangleIndex); + for (int i = 0; i < num_triangles; ++i) + { + const SoftBodyMotionProperties::Face &f = faces[context.mTriangleIndex + i]; + + Vec3 x1 = context.mCenterOfMassTransform * vertices[f.mVertex[0]].mPosition; + Vec3 x2 = context.mCenterOfMassTransform * vertices[f.mVertex[1]].mPosition; + Vec3 x3 = context.mCenterOfMassTransform * vertices[f.mVertex[2]].mPosition; + + x1.StoreFloat3(outTriangleVertices++); + x2.StoreFloat3(outTriangleVertices++); + x3.StoreFloat3(outTriangleVertices++); + + if (outMaterials != nullptr) + *outMaterials++ = materials[f.mMaterialIndex]; + } + + context.mTriangleIndex += num_triangles; + return num_triangles; +} + +Shape::Stats SoftBodyShape::GetStats() const +{ + return Stats(sizeof(*this), (uint)mSoftBodyMotionProperties->GetFaces().size()); +} + +float SoftBodyShape::GetVolume() const +{ + return mSoftBodyMotionProperties->GetVolume(); +} + +void SoftBodyShape::sCollideConvexVsSoftBody(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, [[maybe_unused]] const ShapeFilter &inShapeFilter) +{ + JPH_ASSERT(inShape1->GetType() == EShapeType::Convex); + const ConvexShape *shape1 = static_cast(inShape1); + JPH_ASSERT(inShape2->GetSubType() == EShapeSubType::SoftBody); + const SoftBodyShape *shape2 = static_cast(inShape2); + + const Array &vertices = shape2->mSoftBodyMotionProperties->GetVertices(); + const Array &faces = shape2->mSoftBodyMotionProperties->GetFaces(); + uint num_triangle_bits = shape2->GetSubShapeIDBits(); + + CollideConvexVsTriangles collider(shape1, inScale1, inScale2, inCenterOfMassTransform1, inCenterOfMassTransform2, inSubShapeIDCreator1.GetID(), inCollideShapeSettings, ioCollector); + for (const SoftBodyMotionProperties::Face &f : faces) + { + Vec3 x1 = vertices[f.mVertex[0]].mPosition; + Vec3 x2 = vertices[f.mVertex[1]].mPosition; + Vec3 x3 = vertices[f.mVertex[2]].mPosition; + + collider.Collide(x1, x2, x3, 0b111, inSubShapeIDCreator2.PushID(uint(&f - faces.data()), num_triangle_bits).GetID()); + } +} + +void SoftBodyShape::sCollideSphereVsSoftBody(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, [[maybe_unused]] const ShapeFilter &inShapeFilter) +{ + JPH_ASSERT(inShape1->GetSubType() == EShapeSubType::Sphere); + const SphereShape *shape1 = static_cast(inShape1); + JPH_ASSERT(inShape2->GetSubType() == EShapeSubType::SoftBody); + const SoftBodyShape *shape2 = static_cast(inShape2); + + const Array &vertices = shape2->mSoftBodyMotionProperties->GetVertices(); + const Array &faces = shape2->mSoftBodyMotionProperties->GetFaces(); + uint num_triangle_bits = shape2->GetSubShapeIDBits(); + + CollideSphereVsTriangles collider(shape1, inScale1, inScale2, inCenterOfMassTransform1, inCenterOfMassTransform2, inSubShapeIDCreator1.GetID(), inCollideShapeSettings, ioCollector); + for (const SoftBodyMotionProperties::Face &f : faces) + { + Vec3 x1 = vertices[f.mVertex[0]].mPosition; + Vec3 x2 = vertices[f.mVertex[1]].mPosition; + Vec3 x3 = vertices[f.mVertex[2]].mPosition; + + collider.Collide(x1, x2, x3, 0b111, inSubShapeIDCreator2.PushID(uint(&f - faces.data()), num_triangle_bits).GetID()); + } +} + +void SoftBodyShape::sCastConvexVsSoftBody(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, [[maybe_unused]] const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector) +{ + JPH_ASSERT(inShape->GetSubType() == EShapeSubType::SoftBody); + const SoftBodyShape *shape = static_cast(inShape); + + const Array &vertices = shape->mSoftBodyMotionProperties->GetVertices(); + const Array &faces = shape->mSoftBodyMotionProperties->GetFaces(); + uint num_triangle_bits = shape->GetSubShapeIDBits(); + + CastConvexVsTriangles caster(inShapeCast, inShapeCastSettings, inScale, inCenterOfMassTransform2, inSubShapeIDCreator1, ioCollector); + for (const SoftBodyMotionProperties::Face &f : faces) + { + Vec3 x1 = vertices[f.mVertex[0]].mPosition; + Vec3 x2 = vertices[f.mVertex[1]].mPosition; + Vec3 x3 = vertices[f.mVertex[2]].mPosition; + + caster.Cast(x1, x2, x3, 0b111, inSubShapeIDCreator2.PushID(uint(&f - faces.data()), num_triangle_bits).GetID()); + } +} + +void SoftBodyShape::sCastSphereVsSoftBody(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, [[maybe_unused]] const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector) +{ + JPH_ASSERT(inShape->GetSubType() == EShapeSubType::SoftBody); + const SoftBodyShape *shape = static_cast(inShape); + + const Array &vertices = shape->mSoftBodyMotionProperties->GetVertices(); + const Array &faces = shape->mSoftBodyMotionProperties->GetFaces(); + uint num_triangle_bits = shape->GetSubShapeIDBits(); + + CastSphereVsTriangles caster(inShapeCast, inShapeCastSettings, inScale, inCenterOfMassTransform2, inSubShapeIDCreator1, ioCollector); + for (const SoftBodyMotionProperties::Face &f : faces) + { + Vec3 x1 = vertices[f.mVertex[0]].mPosition; + Vec3 x2 = vertices[f.mVertex[1]].mPosition; + Vec3 x3 = vertices[f.mVertex[2]].mPosition; + + caster.Cast(x1, x2, x3, 0b111, inSubShapeIDCreator2.PushID(uint(&f - faces.data()), num_triangle_bits).GetID()); + } +} + +void SoftBodyShape::sRegister() +{ + ShapeFunctions &f = ShapeFunctions::sGet(EShapeSubType::SoftBody); + f.mConstruct = nullptr; // Not supposed to be constructed by users! + f.mColor = Color::sDarkGreen; + + for (EShapeSubType s : sConvexSubShapeTypes) + { + CollisionDispatch::sRegisterCollideShape(s, EShapeSubType::SoftBody, sCollideConvexVsSoftBody); + CollisionDispatch::sRegisterCastShape(s, EShapeSubType::SoftBody, sCastConvexVsSoftBody); + } + + // Specialized collision functions + CollisionDispatch::sRegisterCollideShape(EShapeSubType::Sphere, EShapeSubType::SoftBody, sCollideSphereVsSoftBody); + CollisionDispatch::sRegisterCastShape(EShapeSubType::Sphere, EShapeSubType::SoftBody, sCastSphereVsSoftBody); +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyShape.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyShape.h new file mode 100644 index 00000000000..605ec80a239 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyShape.h @@ -0,0 +1,73 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2023 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +class SoftBodyMotionProperties; +class CollideShapeSettings; + +/// Shape used exclusively for soft bodies. Adds the ability to perform collision checks against soft bodies. +class JPH_EXPORT SoftBodyShape final : public Shape +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Constructor + SoftBodyShape() : Shape(EShapeType::SoftBody, EShapeSubType::SoftBody) { } + + /// Determine amount of bits needed to encode sub shape id + uint GetSubShapeIDBits() const; + + /// Convert a sub shape ID back to a face index + uint32 GetFaceIndex(const SubShapeID &inSubShapeID) const; + + // See Shape + virtual bool MustBeStatic() const override { return false; } + virtual Vec3 GetCenterOfMass() const override { return Vec3::sZero(); } + virtual AABox GetLocalBounds() const override; + virtual uint GetSubShapeIDBitsRecursive() const override { return GetSubShapeIDBits(); } + virtual float GetInnerRadius() const override { return 0.0f; } + virtual MassProperties GetMassProperties() const override { return MassProperties(); } + virtual const PhysicsMaterial * GetMaterial(const SubShapeID &inSubShapeID) const override; + virtual Vec3 GetSurfaceNormal(const SubShapeID &inSubShapeID, Vec3Arg inLocalSurfacePosition) const override; + virtual void GetSupportingFace(const SubShapeID &inSubShapeID, Vec3Arg inDirection, Vec3Arg inScale, Mat44Arg inCenterOfMassTransform, SupportingFace &outVertices) const override; + virtual void GetSubmergedVolume(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, const Plane &inSurface, float &outTotalVolume, float &outSubmergedVolume, Vec3 &outCenterOfBuoyancy +#ifdef JPH_DEBUG_RENDERER // Not using JPH_IF_DEBUG_RENDERER for Doxygen + , RVec3Arg inBaseOffset +#endif + ) const override; +#ifdef JPH_DEBUG_RENDERER + virtual void Draw(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inUseMaterialColors, bool inDrawWireframe) const override; +#endif // JPH_DEBUG_RENDERER + virtual bool CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const override; + virtual void CastRay(const RayCast &inRay, const RayCastSettings &inRayCastSettings, const SubShapeIDCreator &inSubShapeIDCreator, CastRayCollector &ioCollector, const ShapeFilter &inShapeFilter = { }) const override; + virtual void CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter = { }) const override; + virtual void CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, SoftBodyVertex *ioVertices, uint inNumVertices, float inDeltaTime, Vec3Arg inDisplacementDueToGravity, int inCollidingShapeIndex) const override; + virtual void GetTrianglesStart(GetTrianglesContext &ioContext, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) const override; + virtual int GetTrianglesNext(GetTrianglesContext &ioContext, int inMaxTrianglesRequested, Float3 *outTriangleVertices, const PhysicsMaterial **outMaterials = nullptr) const override; + virtual Stats GetStats() const override; + virtual float GetVolume() const override; + + // Register shape functions with the registry + static void sRegister(); + +private: + // Helper functions called by CollisionDispatch + static void sCollideConvexVsSoftBody(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter); + static void sCollideSphereVsSoftBody(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter); + static void sCastConvexVsSoftBody(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector); + static void sCastSphereVsSoftBody(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector); + + struct SBSGetTrianglesContext; + + friend class BodyManager; + + const SoftBodyMotionProperties *mSoftBodyMotionProperties; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodySharedSettings.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodySharedSettings.cpp new file mode 100644 index 00000000000..91eae3fb247 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodySharedSettings.cpp @@ -0,0 +1,698 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2023 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +JPH_SUPPRESS_WARNINGS_STD_BEGIN +#include +JPH_SUPPRESS_WARNINGS_STD_END + +JPH_NAMESPACE_BEGIN + +template, class Compare = std::less> using PriorityQueue = std::priority_queue; + +JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SoftBodySharedSettings::Vertex) +{ + JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Vertex, mPosition) + JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Vertex, mVelocity) + JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Vertex, mInvMass) +} + +JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SoftBodySharedSettings::Face) +{ + JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Face, mVertex) + JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Face, mMaterialIndex) +} + +JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SoftBodySharedSettings::Edge) +{ + JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Edge, mVertex) + JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Edge, mRestLength) + JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Edge, mCompliance) +} + +JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SoftBodySharedSettings::DihedralBend) +{ + JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::DihedralBend, mVertex) + JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::DihedralBend, mCompliance) + JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::DihedralBend, mInitialAngle) +} + +JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SoftBodySharedSettings::Volume) +{ + JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Volume, mVertex) + JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Volume, mSixRestVolume) + JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Volume, mCompliance) +} + +JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SoftBodySharedSettings::InvBind) +{ + JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::InvBind, mJointIndex) + JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::InvBind, mInvBind) +} + +JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SoftBodySharedSettings::SkinWeight) +{ + JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::SkinWeight, mInvBindIndex) + JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::SkinWeight, mWeight) +} + +JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SoftBodySharedSettings::Skinned) +{ + JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Skinned, mVertex) + JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Skinned, mWeights) + JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Skinned, mMaxDistance) + JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Skinned, mBackStopDistance) + JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Skinned, mBackStopRadius) +} + +JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SoftBodySharedSettings::LRA) +{ + JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::LRA, mVertex) + JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::LRA, mMaxDistance) +} + +JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SoftBodySharedSettings) +{ + JPH_ADD_ATTRIBUTE(SoftBodySharedSettings, mVertices) + JPH_ADD_ATTRIBUTE(SoftBodySharedSettings, mFaces) + JPH_ADD_ATTRIBUTE(SoftBodySharedSettings, mEdgeConstraints) + JPH_ADD_ATTRIBUTE(SoftBodySharedSettings, mEdgeGroupEndIndices) + JPH_ADD_ATTRIBUTE(SoftBodySharedSettings, mDihedralBendConstraints) + JPH_ADD_ATTRIBUTE(SoftBodySharedSettings, mVolumeConstraints) + JPH_ADD_ATTRIBUTE(SoftBodySharedSettings, mSkinnedConstraints) + JPH_ADD_ATTRIBUTE(SoftBodySharedSettings, mInvBindMatrices) + JPH_ADD_ATTRIBUTE(SoftBodySharedSettings, mLRAConstraints) + JPH_ADD_ATTRIBUTE(SoftBodySharedSettings, mMaterials) + JPH_ADD_ATTRIBUTE(SoftBodySharedSettings, mVertexRadius) +} + +void SoftBodySharedSettings::CalculateClosestKinematic() +{ + // Check if we already calculated this + if (!mClosestKinematic.empty()) + return; + + // Reserve output size + mClosestKinematic.resize(mVertices.size()); + + // Create a list of connected vertices + Array> connectivity; + connectivity.resize(mVertices.size()); + for (const Edge &e : mEdgeConstraints) + { + connectivity[e.mVertex[0]].push_back(e.mVertex[1]); + connectivity[e.mVertex[1]].push_back(e.mVertex[0]); + } + + // Use Dijkstra's algorithm to find the closest kinematic vertex for each vertex + // See: https://en.wikipedia.org/wiki/Dijkstra's_algorithm + // + // An element in the open list + struct Open + { + // Order so that we get the shortest distance first + bool operator < (const Open &inRHS) const + { + return mDistance > inRHS.mDistance; + } + + uint32 mVertex; + float mDistance; + }; + + // Start with all kinematic elements + PriorityQueue to_visit; + for (uint32 v = 0; v < mVertices.size(); ++v) + if (mVertices[v].mInvMass == 0.0f) + { + mClosestKinematic[v].mVertex = v; + mClosestKinematic[v].mDistance = 0.0f; + to_visit.push({ v, 0.0f }); + } + + // Visit all vertices remembering the closest kinematic vertex and its distance + while (!to_visit.empty()) + { + // Pop element from the open list + Open current = to_visit.top(); + to_visit.pop(); + + // Loop through all of its connected vertices + for (uint32 v : connectivity[current.mVertex]) + { + // Calculate distance from the current vertex to this target vertex and check if it is smaller + float new_distance = current.mDistance + (Vec3(mVertices[v].mPosition) - Vec3(mVertices[current.mVertex].mPosition)).Length(); + if (new_distance < mClosestKinematic[v].mDistance) + { + // Remember new closest vertex + mClosestKinematic[v].mVertex = mClosestKinematic[current.mVertex].mVertex; + mClosestKinematic[v].mDistance = new_distance; + to_visit.push({ v, new_distance }); + } + } + } +} + +void SoftBodySharedSettings::CreateConstraints(const VertexAttributes *inVertexAttributes, uint inVertexAttributesLength, EBendType inBendType, float inAngleTolerance) +{ + struct EdgeHelper + { + uint32 mVertex[2]; + uint32 mEdgeIdx; + }; + + // Create list of all edges + Array edges; + edges.reserve(mFaces.size() * 3); + for (const Face &f : mFaces) + for (int i = 0; i < 3; ++i) + { + uint32 v0 = f.mVertex[i]; + uint32 v1 = f.mVertex[(i + 1) % 3]; + + EdgeHelper e; + e.mVertex[0] = min(v0, v1); + e.mVertex[1] = max(v0, v1); + e.mEdgeIdx = uint32(&f - mFaces.data()) * 3 + i; + edges.push_back(e); + } + + // Sort the edges + QuickSort(edges.begin(), edges.end(), [](const EdgeHelper &inLHS, const EdgeHelper &inRHS) { return inLHS.mVertex[0] < inRHS.mVertex[0] || (inLHS.mVertex[0] == inRHS.mVertex[0] && inLHS.mVertex[1] < inRHS.mVertex[1]); }); + + // Only add edges if one of the vertices is movable + auto add_edge = [this](uint32 inVtx1, uint32 inVtx2, float inCompliance1, float inCompliance2) { + if ((mVertices[inVtx1].mInvMass > 0.0f || mVertices[inVtx2].mInvMass > 0.0f) + && inCompliance1 < FLT_MAX && inCompliance2 < FLT_MAX) + { + Edge temp_edge; + temp_edge.mVertex[0] = inVtx1; + temp_edge.mVertex[1] = inVtx2; + temp_edge.mCompliance = 0.5f * (inCompliance1 + inCompliance2); + temp_edge.mRestLength = (Vec3(mVertices[inVtx2].mPosition) - Vec3(mVertices[inVtx1].mPosition)).Length(); + JPH_ASSERT(temp_edge.mRestLength > 0.0f); + mEdgeConstraints.push_back(temp_edge); + } + }; + + // Helper function to get the attributes of a vertex + auto attr = [inVertexAttributes, inVertexAttributesLength](uint32 inVertex) { + return inVertexAttributes[min(inVertex, inVertexAttributesLength - 1)]; + }; + + // Create the constraints + float sq_sin_tolerance = Square(Sin(inAngleTolerance)); + float sq_cos_tolerance = Square(Cos(inAngleTolerance)); + mEdgeConstraints.clear(); + mEdgeConstraints.reserve(edges.size()); + for (Array::size_type i = 0; i < edges.size(); ++i) + { + const EdgeHelper &e0 = edges[i]; + + // Get attributes for the vertices of the edge + const VertexAttributes &a0 = attr(e0.mVertex[0]); + const VertexAttributes &a1 = attr(e0.mVertex[1]); + + // Flag that indicates if this edge is a shear edge (if 2 triangles form a quad-like shape and this edge is on the diagonal) + bool is_shear = false; + + // Test if there are any shared edges + for (Array::size_type j = i + 1; j < edges.size(); ++j) + { + const EdgeHelper &e1 = edges[j]; + if (e0.mVertex[0] == e1.mVertex[0] && e0.mVertex[1] == e1.mVertex[1]) + { + // Get opposing vertices + const Face &f0 = mFaces[e0.mEdgeIdx / 3]; + const Face &f1 = mFaces[e1.mEdgeIdx / 3]; + uint32 vopposite0 = f0.mVertex[(e0.mEdgeIdx + 2) % 3]; + uint32 vopposite1 = f1.mVertex[(e1.mEdgeIdx + 2) % 3]; + const VertexAttributes &a_opposite0 = attr(vopposite0); + const VertexAttributes &a_opposite1 = attr(vopposite1); + + // Faces should be roughly in a plane + Vec3 n0 = (Vec3(mVertices[f0.mVertex[2]].mPosition) - Vec3(mVertices[f0.mVertex[0]].mPosition)).Cross(Vec3(mVertices[f0.mVertex[1]].mPosition) - Vec3(mVertices[f0.mVertex[0]].mPosition)); + Vec3 n1 = (Vec3(mVertices[f1.mVertex[2]].mPosition) - Vec3(mVertices[f1.mVertex[0]].mPosition)).Cross(Vec3(mVertices[f1.mVertex[1]].mPosition) - Vec3(mVertices[f1.mVertex[0]].mPosition)); + if (Square(n0.Dot(n1)) > sq_cos_tolerance * n0.LengthSq() * n1.LengthSq()) + { + // Faces should approximately form a quad + Vec3 e0_dir = Vec3(mVertices[vopposite0].mPosition) - Vec3(mVertices[e0.mVertex[0]].mPosition); + Vec3 e1_dir = Vec3(mVertices[vopposite1].mPosition) - Vec3(mVertices[e0.mVertex[0]].mPosition); + if (Square(e0_dir.Dot(e1_dir)) < sq_sin_tolerance * e0_dir.LengthSq() * e1_dir.LengthSq()) + { + // Shear constraint + add_edge(vopposite0, vopposite1, a_opposite0.mShearCompliance, a_opposite1.mShearCompliance); + is_shear = true; + } + } + + // Bend constraint + switch (inBendType) + { + case EBendType::None: + // Do nothing + break; + + case EBendType::Distance: + // Create an edge constraint to represent the bend constraint + // Use the bend compliance of the shared edge + if (!is_shear) + add_edge(vopposite0, vopposite1, a0.mBendCompliance, a1.mBendCompliance); + break; + + case EBendType::Dihedral: + // Test if both opposite vertices are free to move + if ((mVertices[vopposite0].mInvMass > 0.0f || mVertices[vopposite1].mInvMass > 0.0f) + && a0.mBendCompliance < FLT_MAX && a1.mBendCompliance < FLT_MAX) + { + // Create a bend constraint + // Use the bend compliance of the shared edge + mDihedralBendConstraints.emplace_back(e0.mVertex[0], e0.mVertex[1], vopposite0, vopposite1, 0.5f * (a0.mBendCompliance + a1.mBendCompliance)); + } + break; + } + } + else + { + // Start iterating from the first non-shared edge + i = j - 1; + break; + } + } + + // Create a edge constraint for the current edge + add_edge(e0.mVertex[0], e0.mVertex[1], is_shear? a0.mShearCompliance : a0.mCompliance, is_shear? a1.mShearCompliance : a1.mCompliance); + } + mEdgeConstraints.shrink_to_fit(); + + // Calculate the initial angle for all bend constraints + CalculateBendConstraintConstants(); + + // Check if any vertices have LRA constraints + bool has_lra_constraints = false; + for (const VertexAttributes *va = inVertexAttributes; va < inVertexAttributes + inVertexAttributesLength; ++va) + if (va->mLRAType != ELRAType::None) + { + has_lra_constraints = true; + break; + } + if (has_lra_constraints) + { + // Ensure we have calculated the closest kinematic vertex for each vertex + CalculateClosestKinematic(); + + // Find non-kinematic vertices + for (uint32 v = 0; v < (uint32)mVertices.size(); ++v) + if (mVertices[v].mInvMass > 0.0f) + { + // Check if a closest vertex was found + uint32 closest = mClosestKinematic[v].mVertex; + if (closest != 0xffffffff) + { + // Check which LRA constraint to create + const VertexAttributes &va = attr(v); + switch (va.mLRAType) + { + case ELRAType::None: + break; + + case ELRAType::EuclideanDistance: + mLRAConstraints.emplace_back(closest, v, va.mLRAMaxDistanceMultiplier * (Vec3(mVertices[closest].mPosition) - Vec3(mVertices[v].mPosition)).Length()); + break; + + case ELRAType::GeodesicDistance: + mLRAConstraints.emplace_back(closest, v, va.mLRAMaxDistanceMultiplier * mClosestKinematic[v].mDistance); + break; + } + } + } + } +} + +void SoftBodySharedSettings::CalculateEdgeLengths() +{ + for (Edge &e : mEdgeConstraints) + { + e.mRestLength = (Vec3(mVertices[e.mVertex[1]].mPosition) - Vec3(mVertices[e.mVertex[0]].mPosition)).Length(); + JPH_ASSERT(e.mRestLength > 0.0f); + } +} + +void SoftBodySharedSettings::CalculateLRALengths(float inMaxDistanceMultiplier) +{ + for (LRA &l : mLRAConstraints) + { + l.mMaxDistance = inMaxDistanceMultiplier * (Vec3(mVertices[l.mVertex[1]].mPosition) - Vec3(mVertices[l.mVertex[0]].mPosition)).Length(); + JPH_ASSERT(l.mMaxDistance > 0.0f); + } +} + +void SoftBodySharedSettings::CalculateBendConstraintConstants() +{ + for (DihedralBend &b : mDihedralBendConstraints) + { + // Get positions + Vec3 x0 = Vec3(mVertices[b.mVertex[0]].mPosition); + Vec3 x1 = Vec3(mVertices[b.mVertex[1]].mPosition); + Vec3 x2 = Vec3(mVertices[b.mVertex[2]].mPosition); + Vec3 x3 = Vec3(mVertices[b.mVertex[3]].mPosition); + + /* + x2 + e1/ \e3 + / \ + x0----x1 + \ e0 / + e2\ /e4 + x3 + */ + + // Calculate edges + Vec3 e0 = x1 - x0; + Vec3 e1 = x2 - x0; + Vec3 e2 = x3 - x0; + + // Normals of both triangles + Vec3 n1 = e0.Cross(e1); + Vec3 n2 = e2.Cross(e0); + float denom = sqrt(n1.LengthSq() * n2.LengthSq()); + if (denom < 1.0e-12f) + b.mInitialAngle = 0.0f; + else + { + float sign = Sign(n2.Cross(n1).Dot(e0)); + b.mInitialAngle = sign * ACos(n1.Dot(n2) / denom); + } + } +} + +void SoftBodySharedSettings::CalculateVolumeConstraintVolumes() +{ + for (Volume &v : mVolumeConstraints) + { + Vec3 x1(mVertices[v.mVertex[0]].mPosition); + Vec3 x2(mVertices[v.mVertex[1]].mPosition); + Vec3 x3(mVertices[v.mVertex[2]].mPosition); + Vec3 x4(mVertices[v.mVertex[3]].mPosition); + + Vec3 x1x2 = x2 - x1; + Vec3 x1x3 = x3 - x1; + Vec3 x1x4 = x4 - x1; + + v.mSixRestVolume = abs(x1x2.Cross(x1x3).Dot(x1x4)); + } +} + +void SoftBodySharedSettings::CalculateSkinnedConstraintNormals() +{ + // Clear any previous results + mSkinnedConstraintNormals.clear(); + + // If there are no skinned constraints, we're done + if (mSkinnedConstraints.empty()) + return; + + // First collect all vertices that are skinned + UnorderedSet skinned_vertices; + skinned_vertices.reserve(mSkinnedConstraints.size()); + for (const Skinned &s : mSkinnedConstraints) + skinned_vertices.insert(s.mVertex); + + // Now collect all faces that connect only to skinned vertices + UnorderedMap> connected_faces; + connected_faces.reserve(mVertices.size()); + for (const Face &f : mFaces) + { + // Must connect to only skinned vertices + bool valid = true; + for (uint32 v : f.mVertex) + valid &= skinned_vertices.find(v) != skinned_vertices.end(); + if (!valid) + continue; + + // Store faces that connect to vertices + for (uint32 v : f.mVertex) + connected_faces[v].insert(uint32(&f - mFaces.data())); + } + + // Populate the list of connecting faces per skinned vertex + mSkinnedConstraintNormals.reserve(mFaces.size()); + for (Skinned &s : mSkinnedConstraints) + { + uint32 start = uint32(mSkinnedConstraintNormals.size()); + JPH_ASSERT((start >> 24) == 0); + const UnorderedSet &faces = connected_faces[s.mVertex]; + uint32 num = uint32(faces.size()); + JPH_ASSERT(num < 256); + mSkinnedConstraintNormals.insert(mSkinnedConstraintNormals.end(), faces.begin(), faces.end()); + QuickSort(mSkinnedConstraintNormals.begin() + start, mSkinnedConstraintNormals.begin() + start + num); + s.mNormalInfo = start + (num << 24); + } + mSkinnedConstraintNormals.shrink_to_fit(); +} + +void SoftBodySharedSettings::Optimize(OptimizationResults &outResults) +{ + const uint cMaxNumGroups = 32; + const uint cNonParallelGroupIdx = cMaxNumGroups - 1; + const uint cMinimumSize = 2 * SoftBodyUpdateContext::cEdgeConstraintBatch; // There should be at least 2 batches, otherwise there's no point in parallelizing + + // Assign edges to non-overlapping groups + Array masks; + masks.resize(mVertices.size(), 0); + Array edge_groups[cMaxNumGroups]; + for (const Edge &e : mEdgeConstraints) + { + uint32 &mask1 = masks[e.mVertex[0]]; + uint32 &mask2 = masks[e.mVertex[1]]; + uint group = min(CountTrailingZeros((~mask1) & (~mask2)), cNonParallelGroupIdx); + uint32 mask = uint32(1U << group); + mask1 |= mask; + mask2 |= mask; + edge_groups[group].push_back(uint(&e - mEdgeConstraints.data())); + } + + // Merge groups that are too small into the non-parallel group + for (uint i = 0; i < cNonParallelGroupIdx; ++i) + if (edge_groups[i].size() < cMinimumSize) + { + edge_groups[cNonParallelGroupIdx].insert(edge_groups[cNonParallelGroupIdx].end(), edge_groups[i].begin(), edge_groups[i].end()); + edge_groups[i].clear(); + } + + // Make sure we know the closest kinematic vertex so we can sort + CalculateClosestKinematic(); + + // Sort the edge constraints + for (Array &group : edge_groups) + QuickSort(group.begin(), group.end(), [this](uint inLHS, uint inRHS) + { + const Edge &e1 = mEdgeConstraints[inLHS]; + const Edge &e2 = mEdgeConstraints[inRHS]; + + // First sort so that the edge with the smallest distance to a kinematic vertex comes first + float d1 = min(mClosestKinematic[e1.mVertex[0]].mDistance, mClosestKinematic[e1.mVertex[1]].mDistance); + float d2 = min(mClosestKinematic[e2.mVertex[0]].mDistance, mClosestKinematic[e2.mVertex[1]].mDistance); + if (d1 != d2) + return d1 < d2; + + // Order the edges so that the ones with the smallest index go first (hoping to get better cache locality when we process the edges). + // Note we could also re-order the vertices but that would be much more of a burden to the end user + return e1.GetMinVertexIndex() < e2.GetMinVertexIndex(); + }); + + // Assign the edges to groups and reorder them + Array temp_edges; + temp_edges.swap(mEdgeConstraints); + mEdgeConstraints.reserve(temp_edges.size()); + for (const Array &group : edge_groups) + if (!group.empty()) + { + for (uint idx : group) + { + mEdgeConstraints.push_back(temp_edges[idx]); + outResults.mEdgeRemap.push_back(idx); + } + mEdgeGroupEndIndices.push_back((uint)mEdgeConstraints.size()); + } + + // If there is no non-parallel group then add an empty group at the end + if (edge_groups[cNonParallelGroupIdx].empty()) + mEdgeGroupEndIndices.push_back((uint)mEdgeConstraints.size()); + + // Sort the bend constraints + outResults.mDihedralBendRemap.resize(mDihedralBendConstraints.size()); + for (int i = 0; i < (int)mDihedralBendConstraints.size(); ++i) + outResults.mDihedralBendRemap[i] = i; + QuickSort(outResults.mDihedralBendRemap.begin(), outResults.mDihedralBendRemap.end(), [this](uint inLHS, uint inRHS) + { + const DihedralBend &b1 = mDihedralBendConstraints[inLHS]; + const DihedralBend &b2 = mDihedralBendConstraints[inRHS]; + + // First sort so that the constraint with the smallest distance to a kinematic vertex comes first + float d1 = min( + min(mClosestKinematic[b1.mVertex[0]].mDistance, mClosestKinematic[b1.mVertex[1]].mDistance), + min(mClosestKinematic[b1.mVertex[2]].mDistance, mClosestKinematic[b1.mVertex[3]].mDistance)); + float d2 = min( + min(mClosestKinematic[b2.mVertex[0]].mDistance, mClosestKinematic[b2.mVertex[1]].mDistance), + min(mClosestKinematic[b2.mVertex[2]].mDistance, mClosestKinematic[b2.mVertex[3]].mDistance)); + if (d1 != d2) + return d1 < d2; + + // Order constraints so that the ones with the smallest index go first + return b1.GetMinVertexIndex() < b2.GetMinVertexIndex(); + }); + + // Reorder the bend constraints + Array temp_bends; + temp_bends.swap(mDihedralBendConstraints); + mDihedralBendConstraints.reserve(temp_bends.size()); + for (uint idx : outResults.mDihedralBendRemap) + mDihedralBendConstraints.push_back(temp_bends[idx]); + + // Free closest kinematic buffer + mClosestKinematic.clear(); + mClosestKinematic.shrink_to_fit(); +} + +Ref SoftBodySharedSettings::Clone() const +{ + Ref clone = new SoftBodySharedSettings; + clone->mVertices = mVertices; + clone->mFaces = mFaces; + clone->mEdgeConstraints = mEdgeConstraints; + clone->mEdgeGroupEndIndices = mEdgeGroupEndIndices; + clone->mDihedralBendConstraints = mDihedralBendConstraints; + clone->mVolumeConstraints = mVolumeConstraints; + clone->mSkinnedConstraints = mSkinnedConstraints; + clone->mSkinnedConstraintNormals = mSkinnedConstraintNormals; + clone->mInvBindMatrices = mInvBindMatrices; + clone->mLRAConstraints = mLRAConstraints; + clone->mMaterials = mMaterials; + clone->mVertexRadius = mVertexRadius; + return clone; +} + +void SoftBodySharedSettings::SaveBinaryState(StreamOut &inStream) const +{ + inStream.Write(mVertices); + inStream.Write(mFaces); + inStream.Write(mEdgeConstraints); + inStream.Write(mEdgeGroupEndIndices); + inStream.Write(mDihedralBendConstraints); + inStream.Write(mVolumeConstraints); + inStream.Write(mSkinnedConstraints); + inStream.Write(mSkinnedConstraintNormals); + inStream.Write(mLRAConstraints); + inStream.Write(mVertexRadius); + + // Can't write mInvBindMatrices directly because the class contains padding + inStream.Write(mInvBindMatrices, [](const InvBind &inElement, StreamOut &inS) { + inS.Write(inElement.mJointIndex); + inS.Write(inElement.mInvBind); + }); +} + +void SoftBodySharedSettings::RestoreBinaryState(StreamIn &inStream) +{ + inStream.Read(mVertices); + inStream.Read(mFaces); + inStream.Read(mEdgeConstraints); + inStream.Read(mEdgeGroupEndIndices); + inStream.Read(mDihedralBendConstraints); + inStream.Read(mVolumeConstraints); + inStream.Read(mSkinnedConstraints); + inStream.Read(mSkinnedConstraintNormals); + inStream.Read(mLRAConstraints); + inStream.Read(mVertexRadius); + + inStream.Read(mInvBindMatrices, [](StreamIn &inS, InvBind &outElement) { + inS.Read(outElement.mJointIndex); + inS.Read(outElement.mInvBind); + }); +} + +void SoftBodySharedSettings::SaveWithMaterials(StreamOut &inStream, SharedSettingsToIDMap &ioSettingsMap, MaterialToIDMap &ioMaterialMap) const +{ + SharedSettingsToIDMap::const_iterator settings_iter = ioSettingsMap.find(this); + if (settings_iter == ioSettingsMap.end()) + { + // Write settings ID + uint32 settings_id = (uint32)ioSettingsMap.size(); + ioSettingsMap[this] = settings_id; + inStream.Write(settings_id); + + // Write the settings + SaveBinaryState(inStream); + + // Write materials + StreamUtils::SaveObjectArray(inStream, mMaterials, &ioMaterialMap); + } + else + { + // Known settings, just write the ID + inStream.Write(settings_iter->second); + } +} + +SoftBodySharedSettings::SettingsResult SoftBodySharedSettings::sRestoreWithMaterials(StreamIn &inStream, IDToSharedSettingsMap &ioSettingsMap, IDToMaterialMap &ioMaterialMap) +{ + SettingsResult result; + + // Read settings id + uint32 settings_id; + inStream.Read(settings_id); + if (inStream.IsEOF() || inStream.IsFailed()) + { + result.SetError("Failed to read settings id"); + return result; + } + + // Check nullptr settings + if (settings_id == ~uint32(0)) + { + result.Set(nullptr); + return result; + } + + // Check if we already read this settings + if (settings_id < ioSettingsMap.size()) + { + result.Set(ioSettingsMap[settings_id]); + return result; + } + + // Create new object + Ref settings = new SoftBodySharedSettings; + + // Read state + settings->RestoreBinaryState(inStream); + + // Read materials + Result mlresult = StreamUtils::RestoreObjectArray(inStream, ioMaterialMap); + if (mlresult.HasError()) + { + result.SetError(mlresult.GetError()); + return result; + } + settings->mMaterials = mlresult.Get(); + + // Add the settings to the map + ioSettingsMap.push_back(settings); + + result.Set(settings); + return result; +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodySharedSettings.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodySharedSettings.h new file mode 100644 index 00000000000..250bd68917d --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodySharedSettings.h @@ -0,0 +1,322 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2023 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +/// This class defines the setup of all particles and their constraints. +/// It is used during the simulation and can be shared between multiple soft bodies. +class JPH_EXPORT SoftBodySharedSettings : public RefTarget +{ +public: + JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, SoftBodySharedSettings) + + /// Which type of bend constraint should be created + enum class EBendType + { + None, ///< No bend constraints will be created + Distance, ///< A simple distance constraint + Dihedral, ///< A dihedral bend constraint (most expensive, but also supports triangles that are initially not in the same plane) + }; + + /// The type of long range attachment constraint to create + enum class ELRAType + { + None, ///< Don't create a LRA constraint + EuclideanDistance, ///< Create a LRA constraint based on Euclidean distance between the closest kinematic vertex and this vertex + GeodesicDistance, ///< Create a LRA constraint based on the geodesic distance between the closest kinematic vertex and this vertex (follows the edge constraints) + }; + + /// Per vertex attributes used during the CreateConstraints function. + /// For an edge or shear constraint, the compliance is averaged between the two attached vertices. + /// For a bend constraint, the compliance is averaged between the two vertices on the shared edge. + struct JPH_EXPORT VertexAttributes + { + /// Constructor + VertexAttributes() = default; + VertexAttributes(float inCompliance, float inShearCompliance, float inBendCompliance, ELRAType inLRAType = ELRAType::None, float inLRAMaxDistanceMultiplier = 1.0f) : mCompliance(inCompliance), mShearCompliance(inShearCompliance), mBendCompliance(inBendCompliance), mLRAType(inLRAType), mLRAMaxDistanceMultiplier(inLRAMaxDistanceMultiplier) { } + + float mCompliance = 0.0f; ///< The compliance of the normal edges. Set to FLT_MAX to disable regular edges for any edge involving this vertex. + float mShearCompliance = 0.0f; ///< The compliance of the shear edges. Set to FLT_MAX to disable shear edges for any edge involving this vertex. + float mBendCompliance = FLT_MAX; ///< The compliance of the bend edges. Set to FLT_MAX to disable bend edges for any bend constraint involving this vertex. + ELRAType mLRAType = ELRAType::None; ///< The type of long range attachment constraint to create. + float mLRAMaxDistanceMultiplier = 1.0f; ///< Multiplier for the max distance of the LRA constraint, e.g. 1.01 means the max distance is 1% longer than the calculated distance in the rest pose. + }; + + /// Automatically create constraints based on the faces of the soft body + /// @param inVertexAttributes A list of attributes for each vertex (1-on-1 with mVertices, note that if the list is smaller than mVertices the last element will be repeated). This defines the properties of the constraints that are created. + /// @param inVertexAttributesLength The length of inVertexAttributes + /// @param inBendType The type of bend constraint to create + /// @param inAngleTolerance Shear edges are created when two connected triangles form a quad (are roughly in the same plane and form a square with roughly 90 degree angles). This defines the tolerance (in radians). + void CreateConstraints(const VertexAttributes *inVertexAttributes, uint inVertexAttributesLength, EBendType inBendType = EBendType::Distance, float inAngleTolerance = DegreesToRadians(8.0f)); + + /// Calculate the initial lengths of all springs of the edges of this soft body (if you use CreateConstraint, this is already done) + void CalculateEdgeLengths(); + + /// Calculate the max lengths for the long range attachment constraints based on Euclidean distance (if you use CreateConstraints, this is already done) + /// @param inMaxDistanceMultiplier Multiplier for the max distance of the LRA constraint, e.g. 1.01 means the max distance is 1% longer than the calculated distance in the rest pose. + void CalculateLRALengths(float inMaxDistanceMultiplier = 1.0f); + + /// Calculate the constants for the bend constraints (if you use CreateConstraints, this is already done) + void CalculateBendConstraintConstants(); + + /// Calculates the initial volume of all tetrahedra of this soft body + void CalculateVolumeConstraintVolumes(); + + /// Calculate information needed to be able to calculate the skinned constraint normals at run-time + void CalculateSkinnedConstraintNormals(); + + /// Information about the optimization of the soft body, the indices of certain elements may have changed. + class OptimizationResults + { + public: + Array mEdgeRemap; ///< Maps old edge index to new edge index + Array mDihedralBendRemap; ///< Maps old dihedral bend index to new dihedral bend index + }; + + /// Optimize the soft body settings for simulation. This will reorder constraints so they can be executed in parallel. + void Optimize(OptimizationResults &outResults); + + /// Optimize the soft body settings without results + void Optimize() { OptimizationResults results; Optimize(results); } + + /// Clone this object + Ref Clone() const; + + /// Saves the state of this object in binary form to inStream. Doesn't store the material list. + void SaveBinaryState(StreamOut &inStream) const; + + /// Restore the state of this object from inStream. Doesn't restore the material list. + void RestoreBinaryState(StreamIn &inStream); + + using SharedSettingsToIDMap = StreamUtils::ObjectToIDMap; + using IDToSharedSettingsMap = StreamUtils::IDToObjectMap; + using MaterialToIDMap = StreamUtils::ObjectToIDMap; + using IDToMaterialMap = StreamUtils::IDToObjectMap; + + /// Save this shared settings and its materials. Pass in an empty map ioSettingsMap / ioMaterialMap or reuse the same map while saving multiple settings objects to the same stream in order to avoid writing duplicates. + void SaveWithMaterials(StreamOut &inStream, SharedSettingsToIDMap &ioSettingsMap, MaterialToIDMap &ioMaterialMap) const; + + using SettingsResult = Result>; + + /// Restore a shape and materials. Pass in an empty map in ioSettingsMap / ioMaterialMap or reuse the same map while reading multiple settings objects from the same stream in order to restore duplicates. + static SettingsResult sRestoreWithMaterials(StreamIn &inStream, IDToSharedSettingsMap &ioSettingsMap, IDToMaterialMap &ioMaterialMap); + + /// A vertex is a particle, the data in this structure is only used during creation of the soft body and not during simulation + struct JPH_EXPORT Vertex + { + JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, Vertex) + + /// Constructor + Vertex() = default; + Vertex(const Float3 &inPosition, const Float3 &inVelocity = Float3(0, 0, 0), float inInvMass = 1.0f) : mPosition(inPosition), mVelocity(inVelocity), mInvMass(inInvMass) { } + + Float3 mPosition { 0, 0, 0 }; ///< Initial position of the vertex + Float3 mVelocity { 0, 0, 0 }; ///< Initial velocity of the vertex + float mInvMass = 1.0f; ///< Initial inverse of the mass of the vertex + }; + + /// A face defines the surface of the body + struct JPH_EXPORT Face + { + JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, Face) + + /// Constructor + Face() = default; + Face(uint32 inVertex1, uint32 inVertex2, uint32 inVertex3, uint32 inMaterialIndex = 0) : mVertex { inVertex1, inVertex2, inVertex3 }, mMaterialIndex(inMaterialIndex) { } + + /// Check if this is a degenerate face (a face which points to the same vertex twice) + bool IsDegenerate() const { return mVertex[0] == mVertex[1] || mVertex[0] == mVertex[2] || mVertex[1] == mVertex[2]; } + + uint32 mVertex[3]; ///< Indices of the vertices that form the face + uint32 mMaterialIndex = 0; ///< Index of the material of the face in SoftBodySharedSettings::mMaterials + }; + + /// An edge keeps two vertices at a constant distance using a spring: |x1 - x2| = rest length + struct JPH_EXPORT Edge + { + JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, Edge) + + /// Constructor + Edge() = default; + Edge(uint32 inVertex1, uint32 inVertex2, float inCompliance = 0.0f) : mVertex { inVertex1, inVertex2 }, mCompliance(inCompliance) { } + + /// Return the lowest vertex index of this constraint + uint32 GetMinVertexIndex() const { return min(mVertex[0], mVertex[1]); } + + uint32 mVertex[2]; ///< Indices of the vertices that form the edge + float mRestLength = 1.0f; ///< Rest length of the spring + float mCompliance = 0.0f; ///< Inverse of the stiffness of the spring + }; + + /** + * A dihedral bend constraint keeps the angle between two triangles constant along their shared edge. + * + * x2 + * / \ + * / t0 \ + * x0----x1 + * \ t1 / + * \ / + * x3 + * + * x0..x3 are the vertices, t0 and t1 are the triangles that share the edge x0..x1 + * + * Based on: + * - "Position Based Dynamics" - Matthias Muller et al. + * - "Strain Based Dynamics" - Matthias Muller et al. + * - "Simulation of Clothing with Folds and Wrinkles" - R. Bridson et al. + */ + struct JPH_EXPORT DihedralBend + { + JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, DihedralBend) + + /// Constructor + DihedralBend() = default; + DihedralBend(uint32 inVertex1, uint32 inVertex2, uint32 inVertex3, uint32 inVertex4, float inCompliance = 0.0f) : mVertex { inVertex1, inVertex2, inVertex3, inVertex4 }, mCompliance(inCompliance) { } + + /// Return the lowest vertex index of this constraint + uint32 GetMinVertexIndex() const { return min(min(mVertex[0], mVertex[1]), min(mVertex[2], mVertex[3])); } + + uint32 mVertex[4]; ///< Indices of the vertices of the 2 triangles that share an edge (the first 2 vertices are the shared edge) + float mCompliance = 0.0f; ///< Inverse of the stiffness of the constraint + float mInitialAngle = 0.0f; ///< Initial angle between the normals of the triangles (pi - dihedral angle). + }; + + /// Volume constraint, keeps the volume of a tetrahedron constant + struct JPH_EXPORT Volume + { + JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, Volume) + + /// Constructor + Volume() = default; + Volume(uint32 inVertex1, uint32 inVertex2, uint32 inVertex3, uint32 inVertex4, float inCompliance = 0.0f) : mVertex { inVertex1, inVertex2, inVertex3, inVertex4 }, mCompliance(inCompliance) { } + + /// Return the lowest vertex index of this constraint + uint32 GetMinVertexIndex() const { return min(min(mVertex[0], mVertex[1]), min(mVertex[2], mVertex[3])); } + + uint32 mVertex[4]; ///< Indices of the vertices that form the tetrhedron + float mSixRestVolume = 1.0f; ///< 6 times the rest volume of the tetrahedron (calculated by CalculateVolumeConstraintVolumes()) + float mCompliance = 0.0f; ///< Inverse of the stiffness of the constraint + }; + + /// An inverse bind matrix take a skinned vertex from its bind pose into joint local space + class JPH_EXPORT InvBind + { + JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, InvBind) + + public: + /// Constructor + InvBind() = default; + InvBind(uint32 inJointIndex, Mat44Arg inInvBind) : mJointIndex(inJointIndex), mInvBind(inInvBind) { } + + uint32 mJointIndex = 0; ///< Joint index to which this is attached + Mat44 mInvBind = Mat44::sIdentity(); ///< The inverse bind matrix, this takes a vertex in its bind pose (Vertex::mPosition) to joint local space + }; + + /// A joint and its skin weight + class JPH_EXPORT SkinWeight + { + JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, SkinWeight) + + public: + /// Constructor + SkinWeight() = default; + SkinWeight(uint32 inInvBindIndex, float inWeight) : mInvBindIndex(inInvBindIndex), mWeight(inWeight) { } + + uint32 mInvBindIndex = 0; ///< Index in mInvBindMatrices + float mWeight = 0.0f; ///< Weight with which it is skinned + }; + + /// A constraint that skins a vertex to joints and limits the distance that the simulated vertex can travel from this vertex + class JPH_EXPORT Skinned + { + JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, Skinned) + + public: + /// Constructor + Skinned() = default; + Skinned(uint32 inVertex, float inMaxDistance, float inBackStopDistance, float inBackStopRadius) : mVertex(inVertex), mMaxDistance(inMaxDistance), mBackStopDistance(inBackStopDistance), mBackStopRadius(inBackStopRadius) { } + + /// Normalize the weights so that they add up to 1 + void NormalizeWeights() + { + // Get the total weight + float total = 0.0f; + for (const SkinWeight &w : mWeights) + total += w.mWeight; + + // Normalize + if (total > 0.0f) + for (SkinWeight &w : mWeights) + w.mWeight /= total; + } + + uint32 mVertex = 0; ///< Index in mVertices which indicates which vertex is being skinned + SkinWeight mWeights[4]; ///< Skin weights, the bind pose of the vertex is assumed to be stored in Vertex::mPosition. The first weight that is zero indicates the end of the list. Weights should add up to 1. + float mMaxDistance = FLT_MAX; ///< Maximum distance that this vertex can reach from the skinned vertex, disabled when FLT_MAX. 0 when you want to hard skin the vertex to the skinned vertex. + float mBackStopDistance = FLT_MAX; ///< Disabled if mBackStopDistance >= mMaxDistance. The faces surrounding mVertex determine an average normal. mBackStopDistance behind the vertex in the opposite direction of this normal, the back stop sphere starts. The simulated vertex will be pushed out of this sphere and it can be used to approximate the volume of she skinned mesh behind the skinned vertex. + float mBackStopRadius = 40.0f; ///< Radius of the backstop sphere. By default this is a fairly large radius so the sphere approximates a plane. + uint32 mNormalInfo = 0; ///< Information needed to calculate the normal of this vertex, lowest 24 bit is start index in mSkinnedConstraintNormals, highest 8 bit is number of faces (generated by CalculateSkinnedConstraintNormals()) + }; + + /// A long range attachment constraint, this is a constraint that sets a max distance between a kinematic vertex and a dynamic vertex + /// See: "Long Range Attachments - A Method to Simulate Inextensible Clothing in Computer Games", Tae-Yong Kim, Nuttapong Chentanez and Matthias Mueller-Fischer + class JPH_EXPORT LRA + { + JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, LRA) + + public: + /// Constructor + LRA() = default; + LRA(uint32 inVertex1, uint32 inVertex2, float inMaxDistance) : mVertex { inVertex1, inVertex2 }, mMaxDistance(inMaxDistance) { } + + /// Return the lowest vertex index of this constraint + uint32 GetMinVertexIndex() const { return min(mVertex[0], mVertex[1]); } + + uint32 mVertex[2]; ///< The vertices that are connected. The first vertex should be kinematic, the 2nd dynamic. + float mMaxDistance = 0.0f; ///< The maximum distance between the vertices + }; + + /// Add a face to this soft body + void AddFace(const Face &inFace) { JPH_ASSERT(!inFace.IsDegenerate()); mFaces.push_back(inFace); } + + Array mVertices; ///< The list of vertices or particles of the body + Array mFaces; ///< The list of faces of the body + Array mEdgeConstraints; ///< The list of edges or springs of the body + Array mDihedralBendConstraints; ///< The list of dihedral bend constraints of the body + Array mVolumeConstraints; ///< The list of volume constraints of the body that keep the volume of tetrahedra in the soft body constant + Array mSkinnedConstraints; ///< The list of vertices that are constrained to a skinned vertex + Array mInvBindMatrices; ///< The list of inverse bind matrices for skinning vertices + Array mLRAConstraints; ///< The list of long range attachment constraints + PhysicsMaterialList mMaterials { PhysicsMaterial::sDefault }; ///< The materials of the faces of the body, referenced by Face::mMaterialIndex + float mVertexRadius = 0.0f; ///< How big the particles are, can be used to push the vertices a little bit away from the surface of other bodies to prevent z-fighting + +private: + friend class SoftBodyMotionProperties; + + /// Tracks the closest kinematic vertex + struct ClosestKinematic + { + uint32 mVertex = 0xffffffff; ///< Vertex index of closest kinematic vertex + float mDistance = FLT_MAX; ///< Distance to the closest kinematic vertex + }; + + /// Calculate the closest kinematic vertex array + void CalculateClosestKinematic(); + + /// Get the size of an edge group (edge groups can run in parallel) + uint GetEdgeGroupSize(uint inGroupIdx) const { return inGroupIdx == 0? mEdgeGroupEndIndices[0] : mEdgeGroupEndIndices[inGroupIdx] - mEdgeGroupEndIndices[inGroupIdx - 1]; } + + Array mClosestKinematic; ///< The closest kinematic vertex to each vertex in mVertices + Array mEdgeGroupEndIndices; ///< The start index of each group of edges that can be solved in parallel, calculated by Optimize() + Array mSkinnedConstraintNormals; ///< A list of indices in the mFaces array used by mSkinnedConstraints, calculated by CalculateSkinnedConstraintNormals() +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyUpdateContext.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyUpdateContext.h new file mode 100644 index 00000000000..cd50aea085c --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyUpdateContext.h @@ -0,0 +1,67 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2023 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +JPH_NAMESPACE_BEGIN + +class Body; +class SoftBodyMotionProperties; +class SoftBodyContactListener; + +/// Temporary data used by the update of a soft body +class SoftBodyUpdateContext : public NonCopyable +{ +public: + static constexpr uint cVertexCollisionBatch = 64; ///< Number of vertices to process in a batch in DetermineCollisionPlanes + static constexpr uint cEdgeConstraintBatch = 256; ///< Number of edge constraints to process in a batch in ApplyEdgeConstraints + + // Input + Body * mBody; ///< Body that is being updated + SoftBodyMotionProperties * mMotionProperties; ///< Motion properties of that body + SoftBodyContactListener * mContactListener; ///< Contact listener to fire callbacks to + RMat44 mCenterOfMassTransform; ///< Transform of the body relative to the soft body + Vec3 mGravity; ///< Gravity vector in local space of the soft body + Vec3 mDisplacementDueToGravity; ///< Displacement of the center of mass due to gravity in the current time step + float mDeltaTime; ///< Delta time for the current time step + float mSubStepDeltaTime; ///< Delta time for each sub step + + /// Describes progress in the current update + enum class EState + { + DetermineCollisionPlanes, ///< Determine collision planes for vertices in parallel + ApplyEdgeConstraints, ///< Apply edge constraints in parallel + Done ///< Update is finished + }; + + /// Construct the edge constraint iterator starting at a new group + static inline uint64 sGetEdgeGroupStart(uint32 inGroup) + { + return uint64(inGroup) << 32; + } + + /// Get the group and start index from the edge constraint iterator + static inline void sGetEdgeGroupAndStartIdx(uint64 inNextEdgeConstraint, uint32 &outGroup, uint32 &outStartIdx) + { + outGroup = uint32(inNextEdgeConstraint >> 32); + outStartIdx = uint32(inNextEdgeConstraint); + } + + // State of the update + atomic mState { EState::DetermineCollisionPlanes };///< Current state of the update + atomic mNextCollisionVertex { 0 }; ///< Next vertex to process for DetermineCollisionPlanes + atomic mNumCollisionVerticesProcessed { 0 }; ///< Number of vertices processed by DetermineCollisionPlanes, used to determine if we can start simulating + atomic mNextIteration { 0 }; ///< Next simulation iteration to process + atomic mNextEdgeConstraint { 0 }; ///< Next edge constraint group and start index to process + atomic mNumEdgeConstraintsProcessed { 0 }; ///< Number of edge constraints processed by ApplyEdgeConstraints, used to determine if we can go to the next group / iteration + + // Output + Vec3 mDeltaPosition; ///< Delta position of the body in the current time step, should be applied after the update + ECanSleep mCanSleep; ///< Can the body sleep? Should be applied after the update +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyVertex.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyVertex.h new file mode 100644 index 00000000000..34e7bb32fbb --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyVertex.h @@ -0,0 +1,28 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2023 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +/// Run time information for a single particle of a soft body +/// Note that at run-time you should only modify the inverse mass and/or velocity of a vertex to control the soft body. +/// Modifying the position can lead to missed collisions. +/// The other members are used internally by the soft body solver. +class SoftBodyVertex +{ +public: + Vec3 mPreviousPosition; ///< Position at the previous time step + Vec3 mPosition; ///< Position, relative to the center of mass of the soft body + Vec3 mVelocity; ///< Velocity, relative to the center of mass of the soft body + Plane mCollisionPlane; ///< Nearest collision plane, relative to the center of mass of the soft body + int mCollidingShapeIndex; ///< Index in the colliding shapes list of the body we may collide with + bool mHasContact; ///< True if the vertex has collided with anything in the last update + float mLargestPenetration; ///< Used while finding the collision plane, stores the largest penetration found so far + float mInvMass; ///< Inverse mass (1 / mass) +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/StateRecorder.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/StateRecorder.h new file mode 100644 index 00000000000..7ec0844e675 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/StateRecorder.h @@ -0,0 +1,66 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +JPH_NAMESPACE_BEGIN + +class Body; +class Constraint; +class BodyID; + +/// A bit field that determines which aspects of the simulation to save +enum class EStateRecorderState : uint8 +{ + None = 0, ///< Save nothing + Global = 1, ///< Save global physics system state (delta time, gravity, etc.) + Bodies = 2, ///< Save the state of bodies + Contacts = 4, ///< Save the state of contacts + Constraints = 8, ///< Save the state of constraints + All = Global | Bodies | Contacts | Constraints ///< Save all state +}; + +/// User callbacks that allow determining which parts of the simulation should be saved by a StateRecorder +class JPH_EXPORT StateRecorderFilter +{ +public: + /// Destructor + virtual ~StateRecorderFilter() = default; + + /// If the state of a specific body should be saved + virtual bool ShouldSaveBody([[maybe_unused]] const Body &inBody) const { return true; } + + /// If the state of a specific constraint should be saved + virtual bool ShouldSaveConstraint([[maybe_unused]] const Constraint &inConstraint) const { return true; } + + /// If the state of a specific contact should be saved + virtual bool ShouldSaveContact([[maybe_unused]] const BodyID &inBody1, [[maybe_unused]] const BodyID &inBody2) const { return true; } +}; + +/// Class that records the state of a physics system. Can be used to check if the simulation is deterministic by putting the recorder in validation mode. +/// Can be used to restore the state to an earlier point in time. Note that only the state that is modified by the simulation is saved, configuration settings +/// like body friction or restitution, motion quality etc. are not saved and need to be saved by the user if desired. +class JPH_EXPORT StateRecorder : public StreamIn, public StreamOut +{ +public: + /// Constructor + StateRecorder() = default; + StateRecorder(const StateRecorder &inRHS) : mIsValidating(inRHS.mIsValidating) { } + + /// Sets the stream in validation mode. In this case the physics system ensures that before it calls ReadBytes that it will + /// ensure that those bytes contain the current state. This makes it possible to step and save the state, restore to the previous + /// step and step again and when the recorded state is not the same it can restore the expected state and any byte that changes + /// due to a ReadBytes function can be caught to find out which part of the simulation is not deterministic. + /// Note that validation only works when saving the full state of the simulation (EStateRecorderState::All, StateRecorderFilter == nullptr). + void SetValidating(bool inValidating) { mIsValidating = inValidating; } + bool IsValidating() const { return mIsValidating; } + +private: + bool mIsValidating = false; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/StateRecorderImpl.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/StateRecorderImpl.cpp new file mode 100644 index 00000000000..7624135896e --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/StateRecorderImpl.cpp @@ -0,0 +1,90 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include + +JPH_NAMESPACE_BEGIN + +void StateRecorderImpl::WriteBytes(const void *inData, size_t inNumBytes) +{ + mStream.write((const char *)inData, inNumBytes); +} + +void StateRecorderImpl::Rewind() +{ + mStream.seekg(0, std::stringstream::beg); +} + +void StateRecorderImpl::Clear() +{ + mStream.clear(); + mStream.str({}); +} + +void StateRecorderImpl::ReadBytes(void *outData, size_t inNumBytes) +{ + if (IsValidating()) + { + // Read data in temporary buffer to compare with current value + void *data = JPH_STACK_ALLOC(inNumBytes); + mStream.read((char *)data, inNumBytes); + if (memcmp(data, outData, inNumBytes) != 0) + { + // Mismatch, print error + Trace("Mismatch reading %u bytes", (uint)inNumBytes); + for (size_t i = 0; i < inNumBytes; ++i) + { + int b1 = reinterpret_cast(outData)[i]; + int b2 = reinterpret_cast(data)[i]; + if (b1 != b2) + Trace("Offset %d: %02X -> %02X", i, b1, b2); + } + JPH_BREAKPOINT; + } + + // Copy the temporary data to the final destination + memcpy(outData, data, inNumBytes); + return; + } + + mStream.read((char *)outData, inNumBytes); +} + +bool StateRecorderImpl::IsEqual(StateRecorderImpl &inReference) +{ + // Get length of new state + mStream.seekg(0, std::stringstream::end); + std::streamoff this_len = mStream.tellg(); + mStream.seekg(0, std::stringstream::beg); + + // Get length of old state + inReference.mStream.seekg(0, std::stringstream::end); + std::streamoff reference_len = inReference.mStream.tellg(); + inReference.mStream.seekg(0, std::stringstream::beg); + + // Compare size + bool fail = reference_len != this_len; + if (fail) + { + Trace("Failed to properly recover state, different stream length!"); + return false; + } + + // Compare byte by byte + for (std::streamoff i = 0, l = this_len; !fail && i < l; ++i) + { + fail = inReference.mStream.get() != mStream.get(); + if (fail) + { + Trace("Failed to properly recover state, different at offset %d!", (int)i); + return false; + } + } + + return true; +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/StateRecorderImpl.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/StateRecorderImpl.h new file mode 100644 index 00000000000..b852679c0b9 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/StateRecorderImpl.h @@ -0,0 +1,47 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +/// Implementation of the StateRecorder class that uses a stringstream as underlying store and that implements checking if the state doesn't change upon reading +class JPH_EXPORT StateRecorderImpl final : public StateRecorder +{ +public: + /// Constructor + StateRecorderImpl() = default; + StateRecorderImpl(StateRecorderImpl &&inRHS) : StateRecorder(inRHS), mStream(std::move(inRHS.mStream)) { } + + /// Write a string of bytes to the binary stream + virtual void WriteBytes(const void *inData, size_t inNumBytes) override; + + /// Rewind the stream for reading + void Rewind(); + + /// Clear the stream for reuse + void Clear(); + + /// Read a string of bytes from the binary stream + virtual void ReadBytes(void *outData, size_t inNumBytes) override; + + // See StreamIn + virtual bool IsEOF() const override { return mStream.eof(); } + + // See StreamIn / StreamOut + virtual bool IsFailed() const override { return mStream.fail(); } + + /// Compare this state with a reference state and ensure they are the same + bool IsEqual(StateRecorderImpl &inReference); + + /// Convert the binary data to a string + string GetData() const { return mStream.str(); } + +private: + std::stringstream mStream; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/MotorcycleController.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/MotorcycleController.cpp new file mode 100644 index 00000000000..533d4cfc4ab --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/MotorcycleController.cpp @@ -0,0 +1,293 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2023 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#include +#include +#ifdef JPH_DEBUG_RENDERER + #include +#endif // JPH_DEBUG_RENDERER + +JPH_NAMESPACE_BEGIN + +JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(MotorcycleControllerSettings) +{ + JPH_ADD_BASE_CLASS(MotorcycleControllerSettings, VehicleControllerSettings) + + JPH_ADD_ATTRIBUTE(MotorcycleControllerSettings, mMaxLeanAngle) + JPH_ADD_ATTRIBUTE(MotorcycleControllerSettings, mLeanSpringConstant) + JPH_ADD_ATTRIBUTE(MotorcycleControllerSettings, mLeanSpringDamping) + JPH_ADD_ATTRIBUTE(MotorcycleControllerSettings, mLeanSpringIntegrationCoefficient) + JPH_ADD_ATTRIBUTE(MotorcycleControllerSettings, mLeanSpringIntegrationCoefficientDecay) + JPH_ADD_ATTRIBUTE(MotorcycleControllerSettings, mLeanSmoothingFactor) +} + +VehicleController *MotorcycleControllerSettings::ConstructController(VehicleConstraint &inConstraint) const +{ + return new MotorcycleController(*this, inConstraint); +} + +void MotorcycleControllerSettings::SaveBinaryState(StreamOut &inStream) const +{ + WheeledVehicleControllerSettings::SaveBinaryState(inStream); + + inStream.Write(mMaxLeanAngle); + inStream.Write(mLeanSpringConstant); + inStream.Write(mLeanSpringDamping); + inStream.Write(mLeanSpringIntegrationCoefficient); + inStream.Write(mLeanSpringIntegrationCoefficientDecay); + inStream.Write(mLeanSmoothingFactor); +} + +void MotorcycleControllerSettings::RestoreBinaryState(StreamIn &inStream) +{ + WheeledVehicleControllerSettings::RestoreBinaryState(inStream); + + inStream.Read(mMaxLeanAngle); + inStream.Read(mLeanSpringConstant); + inStream.Read(mLeanSpringDamping); + inStream.Read(mLeanSpringIntegrationCoefficient); + inStream.Read(mLeanSpringIntegrationCoefficientDecay); + inStream.Read(mLeanSmoothingFactor); +} + +MotorcycleController::MotorcycleController(const MotorcycleControllerSettings &inSettings, VehicleConstraint &inConstraint) : + WheeledVehicleController(inSettings, inConstraint), + mMaxLeanAngle(inSettings.mMaxLeanAngle), + mLeanSpringConstant(inSettings.mLeanSpringConstant), + mLeanSpringDamping(inSettings.mLeanSpringDamping), + mLeanSpringIntegrationCoefficient(inSettings.mLeanSpringIntegrationCoefficient), + mLeanSpringIntegrationCoefficientDecay(inSettings.mLeanSpringIntegrationCoefficientDecay), + mLeanSmoothingFactor(inSettings.mLeanSmoothingFactor) +{ +} + +float MotorcycleController::GetWheelBase() const +{ + float low = FLT_MAX, high = -FLT_MAX; + + for (const Wheel *w : mConstraint.GetWheels()) + { + const WheelSettings *s = w->GetSettings(); + + // Measure distance along the forward axis by looking at the fully extended suspension. + // If the suspension force point is active, use that instead. + Vec3 force_point = s->mEnableSuspensionForcePoint? s->mSuspensionForcePoint : s->mPosition + s->mSuspensionDirection * s->mSuspensionMaxLength; + float value = force_point.Dot(mConstraint.GetLocalForward()); + + // Update min and max + low = min(low, value); + high = max(high, value); + } + + return high - low; +} + +void MotorcycleController::PreCollide(float inDeltaTime, PhysicsSystem &inPhysicsSystem) +{ + WheeledVehicleController::PreCollide(inDeltaTime, inPhysicsSystem); + + const Body *body = mConstraint.GetVehicleBody(); + Vec3 forward = body->GetRotation() * mConstraint.GetLocalForward(); + float wheel_base = GetWheelBase(); + Vec3 world_up = mConstraint.GetWorldUp(); + + if (mEnableLeanController) + { + // Calculate the target lean vector, this is in the direction of the total applied impulse by the ground on the wheels + Vec3 target_lean = Vec3::sZero(); + for (const Wheel *w : mConstraint.GetWheels()) + if (w->HasContact()) + target_lean += w->GetContactNormal() * w->GetSuspensionLambda() + w->GetContactLateral() * w->GetLateralLambda(); + + // Normalize the impulse + target_lean = target_lean.NormalizedOr(world_up); + + // Smooth the impulse to avoid jittery behavior + mTargetLean = mLeanSmoothingFactor * mTargetLean + (1.0f - mLeanSmoothingFactor) * target_lean; + + // Remove forward component, we can only lean sideways + mTargetLean -= forward * mTargetLean.Dot(forward); + mTargetLean = mTargetLean.NormalizedOr(world_up); + + // Clamp the target lean against the max lean angle + Vec3 adjusted_world_up = world_up - forward * world_up.Dot(forward); + adjusted_world_up = adjusted_world_up.NormalizedOr(world_up); + float w_angle = -Sign(mTargetLean.Cross(adjusted_world_up).Dot(forward)) * ACos(mTargetLean.Dot(adjusted_world_up)); + if (abs(w_angle) > mMaxLeanAngle) + mTargetLean = Quat::sRotation(forward, Sign(w_angle) * mMaxLeanAngle) * adjusted_world_up; + + // Integrate the delta angle + Vec3 up = body->GetRotation() * mConstraint.GetLocalUp(); + float d_angle = -Sign(mTargetLean.Cross(up).Dot(forward)) * ACos(mTargetLean.Dot(up)); + mLeanSpringIntegratedDeltaAngle += d_angle * inDeltaTime; + } + else + { + // Controller not enabled, reset target lean + mTargetLean = world_up; + + // Reset integrated delta angle + mLeanSpringIntegratedDeltaAngle = 0; + } + + JPH_DET_LOG("WheeledVehicleController::PreCollide: mTargetLean: " << mTargetLean); + + // Calculate max steering angle based on the max lean angle we're willing to take + // See: https://en.wikipedia.org/wiki/Bicycle_and_motorcycle_dynamics#Leaning + // LeanAngle = Atan(Velocity^2 / (Gravity * TurnRadius)) + // And: https://en.wikipedia.org/wiki/Turning_radius (we're ignoring the tire width) + // The CasterAngle is the added according to https://en.wikipedia.org/wiki/Bicycle_and_motorcycle_dynamics#Turning (this is the same formula but without small angle approximation) + // TurnRadius = WheelBase / (Sin(SteerAngle) * Cos(CasterAngle)) + // => SteerAngle = ASin(WheelBase * Tan(LeanAngle) * Gravity / (Velocity^2 * Cos(CasterAngle)) + // The caster angle is different for each wheel so we can only calculate part of the equation here + float max_steer_angle_factor = wheel_base * Tan(mMaxLeanAngle) * inPhysicsSystem.GetGravity().Length(); + + // Calculate forward velocity + float velocity = body->GetLinearVelocity().Dot(forward); + float velocity_sq = Square(velocity); + + // Decompose steering into sign and direction + float steer_strength = abs(mRightInput); + float steer_sign = -Sign(mRightInput); + + for (Wheel *w_base : mConstraint.GetWheels()) + { + WheelWV *w = static_cast(w_base); + const WheelSettingsWV *s = w->GetSettings(); + + // Check if this wheel can steer + if (s->mMaxSteerAngle != 0.0f) + { + // Calculate cos(caster angle), the angle between the steering axis and the up vector + float cos_caster_angle = s->mSteeringAxis.Dot(mConstraint.GetLocalUp()); + + // Calculate steer angle + float steer_angle = steer_strength * w->GetSettings()->mMaxSteerAngle; + + // Clamp to max steering angle + if (mEnableLeanSteeringLimit + && velocity_sq > 1.0e-6f && cos_caster_angle > 1.0e-6f) + { + float max_steer_angle = ASin(max_steer_angle_factor / (velocity_sq * cos_caster_angle)); + steer_angle = min(steer_angle, max_steer_angle); + } + + // Set steering angle + w->SetSteerAngle(steer_sign * steer_angle); + } + } + + // Reset applied impulse + mAppliedImpulse = 0; +} + +bool MotorcycleController::SolveLongitudinalAndLateralConstraints(float inDeltaTime) +{ + bool impulse = WheeledVehicleController::SolveLongitudinalAndLateralConstraints(inDeltaTime); + + if (mEnableLeanController) + { + // Only apply a lean impulse if all wheels are in contact, otherwise we can easily spin out + bool all_in_contact = true; + for (const Wheel *w : mConstraint.GetWheels()) + if (!w->HasContact() || w->GetSuspensionLambda() <= 0.0f) + { + all_in_contact = false; + break; + } + + if (all_in_contact) + { + Body *body = mConstraint.GetVehicleBody(); + const MotionProperties *mp = body->GetMotionProperties(); + + Vec3 forward = body->GetRotation() * mConstraint.GetLocalForward(); + Vec3 up = body->GetRotation() * mConstraint.GetLocalUp(); + + // Calculate delta to target angle and derivative + float d_angle = -Sign(mTargetLean.Cross(up).Dot(forward)) * ACos(mTargetLean.Dot(up)); + float ddt_angle = body->GetAngularVelocity().Dot(forward); + + // Calculate impulse to apply to get to target lean angle + float total_impulse = (mLeanSpringConstant * d_angle - mLeanSpringDamping * ddt_angle + mLeanSpringIntegrationCoefficient * mLeanSpringIntegratedDeltaAngle) * inDeltaTime; + + // Remember angular velocity pre angular impulse + Vec3 old_w = mp->GetAngularVelocity(); + + // Apply impulse taking into account the impulse we've applied earlier + float delta_impulse = total_impulse - mAppliedImpulse; + body->AddAngularImpulse(delta_impulse * forward); + mAppliedImpulse = total_impulse; + + // Calculate delta angular velocity due to angular impulse + Vec3 dw = mp->GetAngularVelocity() - old_w; + Vec3 linear_acceleration = Vec3::sZero(); + float total_lambda = 0.0f; + for (Wheel *w_base : mConstraint.GetWheels()) + { + const WheelWV *w = static_cast(w_base); + + // We weigh the importance of each contact point according to the contact force + float lambda = w->GetSuspensionLambda(); + total_lambda += lambda; + + // Linear acceleration of contact point is dw x com_to_contact + Vec3 r = Vec3(w->GetContactPosition() - body->GetCenterOfMassPosition()); + linear_acceleration += lambda * dw.Cross(r); + } + + // Apply linear impulse to COM to cancel the average velocity change on the wheels due to the angular impulse + Vec3 linear_impulse = -linear_acceleration / (total_lambda * mp->GetInverseMass()); + body->AddImpulse(linear_impulse); + + // Return true if we applied an impulse + impulse |= delta_impulse != 0.0f; + } + else + { + // Decay the integrated angle because we won't be applying a torque this frame + // Uses 1st order Taylor approximation of e^(-decay * dt) = 1 - decay * dt + mLeanSpringIntegratedDeltaAngle *= max(0.0f, 1.0f - mLeanSpringIntegrationCoefficientDecay * inDeltaTime); + } + } + + return impulse; +} + +void MotorcycleController::SaveState(StateRecorder &inStream) const +{ + WheeledVehicleController::SaveState(inStream); + + inStream.Write(mTargetLean); +} + +void MotorcycleController::RestoreState(StateRecorder &inStream) +{ + WheeledVehicleController::RestoreState(inStream); + + inStream.Read(mTargetLean); +} + +#ifdef JPH_DEBUG_RENDERER + +void MotorcycleController::Draw(DebugRenderer *inRenderer) const +{ + WheeledVehicleController::Draw(inRenderer); + + // Draw current and desired lean angle + Body *body = mConstraint.GetVehicleBody(); + RVec3 center_of_mass = body->GetCenterOfMassPosition(); + Vec3 up = body->GetRotation() * mConstraint.GetLocalUp(); + inRenderer->DrawArrow(center_of_mass, center_of_mass + up, Color::sYellow, 0.1f); + inRenderer->DrawArrow(center_of_mass, center_of_mass + mTargetLean, Color::sRed, 0.1f); +} + +#endif // JPH_DEBUG_RENDERER + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/MotorcycleController.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/MotorcycleController.h new file mode 100644 index 00000000000..59ee17cdc9d --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/MotorcycleController.h @@ -0,0 +1,116 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2023 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +/// Settings of a two wheeled motorcycle (adds a spring to balance the motorcycle) +/// Note: The motor cycle controller is still in development and may need a lot of tweaks/hacks to work properly! +class JPH_EXPORT MotorcycleControllerSettings : public WheeledVehicleControllerSettings +{ +public: + JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, MotorcycleControllerSettings) + + // See: VehicleControllerSettings + virtual VehicleController * ConstructController(VehicleConstraint &inConstraint) const override; + virtual void SaveBinaryState(StreamOut &inStream) const override; + virtual void RestoreBinaryState(StreamIn &inStream) override; + + /// How far we're willing to make the bike lean over in turns (in radians) + float mMaxLeanAngle = DegreesToRadians(45.0f); + + /// Spring constant for the lean spring + float mLeanSpringConstant = 5000.0f; + + /// Spring damping constant for the lean spring + float mLeanSpringDamping = 1000.0f; + + /// The lean spring applies an additional force equal to this coefficient * Integral(delta angle, 0, t), this effectively makes the lean spring a PID controller + float mLeanSpringIntegrationCoefficient = 0.0f; + + /// How much to decay the angle integral when the wheels are not touching the floor: new_value = e^(-decay * t) * initial_value + float mLeanSpringIntegrationCoefficientDecay = 4.0f; + + /// How much to smooth the lean angle (0 = no smoothing, 1 = lean angle never changes) + /// Note that this is frame rate dependent because the formula is: smoothing_factor * previous + (1 - smoothing_factor) * current + float mLeanSmoothingFactor = 0.8f; +}; + +/// Runtime controller class +class JPH_EXPORT MotorcycleController : public WheeledVehicleController +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Constructor + MotorcycleController(const MotorcycleControllerSettings &inSettings, VehicleConstraint &inConstraint); + + /// Get the distance between the front and back wheels + float GetWheelBase() const; + + /// Enable or disable the lean spring. This allows you to temporarily disable the lean spring to allow the motorcycle to fall over. + void EnableLeanController(bool inEnable) { mEnableLeanController = inEnable; } + + /// Check if the lean spring is enabled. + bool IsLeanControllerEnabled() const { return mEnableLeanController; } + + /// Enable or disable the lean steering limit. When enabled (default) the steering angle is limited based on the vehicle speed to prevent steering that would cause an inertial force that causes the motorcycle to topple over. + void EnableLeanSteeringLimit(bool inEnable) { mEnableLeanSteeringLimit = inEnable; } + bool IsLeanSteeringLimitEnabled() const { return mEnableLeanSteeringLimit; } + + /// Spring constant for the lean spring + void SetLeanSpringConstant(float inConstant) { mLeanSpringConstant = inConstant; } + float GetLeanSpringConstant() const { return mLeanSpringConstant; } + + /// Spring damping constant for the lean spring + void SetLeanSpringDamping(float inDamping) { mLeanSpringDamping = inDamping; } + float GetLeanSpringDamping() const { return mLeanSpringDamping; } + + /// The lean spring applies an additional force equal to this coefficient * Integral(delta angle, 0, t), this effectively makes the lean spring a PID controller + void SetLeanSpringIntegrationCoefficient(float inCoefficient) { mLeanSpringIntegrationCoefficient = inCoefficient; } + float GetLeanSpringIntegrationCoefficient() const { return mLeanSpringIntegrationCoefficient; } + + /// How much to decay the angle integral when the wheels are not touching the floor: new_value = e^(-decay * t) * initial_value + void SetLeanSpringIntegrationCoefficientDecay(float inDecay) { mLeanSpringIntegrationCoefficientDecay = inDecay; } + float GetLeanSpringIntegrationCoefficientDecay() const { return mLeanSpringIntegrationCoefficientDecay; } + + /// How much to smooth the lean angle (0 = no smoothing, 1 = lean angle never changes) + /// Note that this is frame rate dependent because the formula is: smoothing_factor * previous + (1 - smoothing_factor) * current + void SetLeanSmoothingFactor(float inFactor) { mLeanSmoothingFactor = inFactor; } + float GetLeanSmoothingFactor() const { return mLeanSmoothingFactor; } + +protected: + // See: VehicleController + virtual void PreCollide(float inDeltaTime, PhysicsSystem &inPhysicsSystem) override; + virtual bool SolveLongitudinalAndLateralConstraints(float inDeltaTime) override; + virtual void SaveState(StateRecorder &inStream) const override; + virtual void RestoreState(StateRecorder &inStream) override; +#ifdef JPH_DEBUG_RENDERER + virtual void Draw(DebugRenderer *inRenderer) const override; +#endif // JPH_DEBUG_RENDERER + + // Configuration properties + bool mEnableLeanController = true; + bool mEnableLeanSteeringLimit = true; + float mMaxLeanAngle; + float mLeanSpringConstant; + float mLeanSpringDamping; + float mLeanSpringIntegrationCoefficient; + float mLeanSpringIntegrationCoefficientDecay; + float mLeanSmoothingFactor; + + // Run-time calculated target lean vector + Vec3 mTargetLean = Vec3::sZero(); + + // Integrated error for the lean spring + float mLeanSpringIntegratedDeltaAngle = 0.0f; + + // Run-time total angular impulse applied to turn the cycle towards the target lean angle + float mAppliedImpulse = 0.0f; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/TrackedVehicleController.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/TrackedVehicleController.cpp new file mode 100644 index 00000000000..c941a829571 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/TrackedVehicleController.cpp @@ -0,0 +1,531 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#include +#include +#ifdef JPH_DEBUG_RENDERER + #include +#endif // JPH_DEBUG_RENDERER + +JPH_NAMESPACE_BEGIN + +JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(TrackedVehicleControllerSettings) +{ + JPH_ADD_BASE_CLASS(TrackedVehicleControllerSettings, VehicleControllerSettings) + + JPH_ADD_ATTRIBUTE(TrackedVehicleControllerSettings, mEngine) + JPH_ADD_ATTRIBUTE(TrackedVehicleControllerSettings, mTransmission) + JPH_ADD_ATTRIBUTE(TrackedVehicleControllerSettings, mTracks) +} + +JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(WheelSettingsTV) +{ + JPH_ADD_ATTRIBUTE(WheelSettingsTV, mLongitudinalFriction) + JPH_ADD_ATTRIBUTE(WheelSettingsTV, mLateralFriction) +} + +void WheelSettingsTV::SaveBinaryState(StreamOut &inStream) const +{ + inStream.Write(mLongitudinalFriction); + inStream.Write(mLateralFriction); +} + +void WheelSettingsTV::RestoreBinaryState(StreamIn &inStream) +{ + inStream.Read(mLongitudinalFriction); + inStream.Read(mLateralFriction); +} + +WheelTV::WheelTV(const WheelSettingsTV &inSettings) : + Wheel(inSettings) +{ +} + +void WheelTV::CalculateAngularVelocity(const VehicleConstraint &inConstraint) +{ + const WheelSettingsTV *settings = GetSettings(); + const Wheels &wheels = inConstraint.GetWheels(); + const VehicleTrack &track = static_cast(inConstraint.GetController())->GetTracks()[mTrackIndex]; + + // Calculate angular velocity of this wheel + mAngularVelocity = track.mAngularVelocity * wheels[track.mDrivenWheel]->GetSettings()->mRadius / settings->mRadius; +} + +void WheelTV::Update(uint inWheelIndex, float inDeltaTime, const VehicleConstraint &inConstraint) +{ + CalculateAngularVelocity(inConstraint); + + // Update rotation of wheel + mAngle = fmod(mAngle + mAngularVelocity * inDeltaTime, 2.0f * JPH_PI); + + // Reset brake impulse, will be set during post collision again + mBrakeImpulse = 0.0f; + + if (mContactBody != nullptr) + { + // Friction at the point of this wheel between track and floor + const WheelSettingsTV *settings = GetSettings(); + VehicleConstraint::CombineFunction combine_friction = inConstraint.GetCombineFriction(); + mCombinedLongitudinalFriction = settings->mLongitudinalFriction; + mCombinedLateralFriction = settings->mLateralFriction; + combine_friction(inWheelIndex, mCombinedLongitudinalFriction, mCombinedLateralFriction, *mContactBody, mContactSubShapeID); + } + else + { + // No collision + mCombinedLongitudinalFriction = mCombinedLateralFriction = 0.0f; + } +} + +VehicleController *TrackedVehicleControllerSettings::ConstructController(VehicleConstraint &inConstraint) const +{ + return new TrackedVehicleController(*this, inConstraint); +} + +TrackedVehicleControllerSettings::TrackedVehicleControllerSettings() +{ + // Numbers guestimated from: https://en.wikipedia.org/wiki/M1_Abrams + mEngine.mMinRPM = 500.0f; + mEngine.mMaxRPM = 4000.0f; + mEngine.mMaxTorque = 500.0f; // Note actual torque for M1 is around 5000 but we need a reduced mass in order to keep the simulation sane + + mTransmission.mShiftDownRPM = 1000.0f; + mTransmission.mShiftUpRPM = 3500.0f; + mTransmission.mGearRatios = { 4.0f, 3.0f, 2.0f, 1.0f }; + mTransmission.mReverseGearRatios = { -4.0f, -3.0f }; +} + +void TrackedVehicleControllerSettings::SaveBinaryState(StreamOut &inStream) const +{ + mEngine.SaveBinaryState(inStream); + + mTransmission.SaveBinaryState(inStream); + + for (const VehicleTrackSettings &t : mTracks) + t.SaveBinaryState(inStream); +} + +void TrackedVehicleControllerSettings::RestoreBinaryState(StreamIn &inStream) +{ + mEngine.RestoreBinaryState(inStream); + + mTransmission.RestoreBinaryState(inStream); + + for (VehicleTrackSettings &t : mTracks) + t.RestoreBinaryState(inStream); +} + +TrackedVehicleController::TrackedVehicleController(const TrackedVehicleControllerSettings &inSettings, VehicleConstraint &inConstraint) : + VehicleController(inConstraint) +{ + // Copy engine settings + static_cast(mEngine) = inSettings.mEngine; + JPH_ASSERT(inSettings.mEngine.mMinRPM >= 0.0f); + JPH_ASSERT(inSettings.mEngine.mMinRPM <= inSettings.mEngine.mMaxRPM); + mEngine.SetCurrentRPM(mEngine.mMinRPM); + + // Copy transmission settings + static_cast(mTransmission) = inSettings.mTransmission; +#ifdef JPH_ENABLE_ASSERTS + for (float r : inSettings.mTransmission.mGearRatios) + JPH_ASSERT(r > 0.0f); + for (float r : inSettings.mTransmission.mReverseGearRatios) + JPH_ASSERT(r < 0.0f); +#endif // JPH_ENABLE_ASSERTS + JPH_ASSERT(inSettings.mTransmission.mSwitchTime >= 0.0f); + JPH_ASSERT(inSettings.mTransmission.mShiftDownRPM > 0.0f); + JPH_ASSERT(inSettings.mTransmission.mMode != ETransmissionMode::Auto || inSettings.mTransmission.mShiftUpRPM < inSettings.mEngine.mMaxRPM); + JPH_ASSERT(inSettings.mTransmission.mShiftUpRPM > inSettings.mTransmission.mShiftDownRPM); + + // Copy track settings + for (uint i = 0; i < size(mTracks); ++i) + { + const VehicleTrackSettings &d = inSettings.mTracks[i]; + static_cast(mTracks[i]) = d; + JPH_ASSERT(d.mInertia >= 0.0f); + JPH_ASSERT(d.mAngularDamping >= 0.0f); + JPH_ASSERT(d.mMaxBrakeTorque >= 0.0f); + JPH_ASSERT(d.mDifferentialRatio > 0.0f); + } +} + +bool TrackedVehicleController::AllowSleep() const +{ + return mForwardInput == 0.0f // No user input + && mTransmission.AllowSleep() // Transmission is not shifting + && mEngine.AllowSleep(); // Engine is idling +} + +void TrackedVehicleController::PreCollide(float inDeltaTime, PhysicsSystem &inPhysicsSystem) +{ + Wheels &wheels = mConstraint.GetWheels(); + + // Fill in track index + for (size_t t = 0; t < size(mTracks); ++t) + for (uint w : mTracks[t].mWheels) + static_cast(wheels[w])->mTrackIndex = (uint)t; + + // Angular damping: dw/dt = -c * w + // Solution: w(t) = w(0) * e^(-c * t) or w2 = w1 * e^(-c * dt) + // Taylor expansion of e^(-c * dt) = 1 - c * dt + ... + // Since dt is usually in the order of 1/60 and c is a low number too this approximation is good enough + for (VehicleTrack &t : mTracks) + t.mAngularVelocity *= max(0.0f, 1.0f - t.mAngularDamping * inDeltaTime); +} + +void TrackedVehicleController::SyncLeftRightTracks() +{ + // Apply left to right ratio according to track inertias + VehicleTrack &tl = mTracks[(int)ETrackSide::Left]; + VehicleTrack &tr = mTracks[(int)ETrackSide::Right]; + + if (mLeftRatio * mRightRatio > 0.0f) + { + // Solve: (tl.mAngularVelocity + dl) / (tr.mAngularVelocity + dr) = mLeftRatio / mRightRatio and dl * tr.mInertia = -dr * tl.mInertia, where dl/dr are the delta angular velocities for left and right tracks + float impulse = (mLeftRatio * tr.mAngularVelocity - mRightRatio * tl.mAngularVelocity) / (mLeftRatio * tr.mInertia + mRightRatio * tl.mInertia); + tl.mAngularVelocity += impulse * tl.mInertia; + tr.mAngularVelocity -= impulse * tr.mInertia; + } + else + { + // Solve: (tl.mAngularVelocity + dl) / (tr.mAngularVelocity + dr) = mLeftRatio / mRightRatio and dl * tr.mInertia = dr * tl.mInertia, where dl/dr are the delta angular velocities for left and right tracks + float impulse = (mLeftRatio * tr.mAngularVelocity - mRightRatio * tl.mAngularVelocity) / (mRightRatio * tl.mInertia - mLeftRatio * tr.mInertia); + tl.mAngularVelocity += impulse * tl.mInertia; + tr.mAngularVelocity += impulse * tr.mInertia; + } +} + +void TrackedVehicleController::PostCollide(float inDeltaTime, PhysicsSystem &inPhysicsSystem) +{ + JPH_PROFILE_FUNCTION(); + + Wheels &wheels = mConstraint.GetWheels(); + + // Update wheel angle, do this before applying torque to the wheels (as friction will slow them down again) + for (uint wheel_index = 0, num_wheels = (uint)wheels.size(); wheel_index < num_wheels; ++wheel_index) + { + WheelTV *w = static_cast(wheels[wheel_index]); + w->Update(wheel_index, inDeltaTime, mConstraint); + } + + // First calculate engine speed based on speed of all wheels + bool can_engine_apply_torque = false; + if (mTransmission.GetCurrentGear() != 0 && mTransmission.GetClutchFriction() > 1.0e-3f) + { + float transmission_ratio = mTransmission.GetCurrentRatio(); + bool forward = transmission_ratio >= 0.0f; + float fastest_wheel_speed = forward? -FLT_MAX : FLT_MAX; + for (const VehicleTrack &t : mTracks) + { + if (forward) + fastest_wheel_speed = max(fastest_wheel_speed, t.mAngularVelocity * t.mDifferentialRatio); + else + fastest_wheel_speed = min(fastest_wheel_speed, t.mAngularVelocity * t.mDifferentialRatio); + for (uint w : t.mWheels) + if (wheels[w]->HasContact()) + { + can_engine_apply_torque = true; + break; + } + } + + // Update RPM only if the tracks are connected to the engine + if (fastest_wheel_speed > -FLT_MAX && fastest_wheel_speed < FLT_MAX) + mEngine.SetCurrentRPM(fastest_wheel_speed * mTransmission.GetCurrentRatio() * VehicleEngine::cAngularVelocityToRPM); + } + else + { + // Update engine with damping + mEngine.ApplyDamping(inDeltaTime); + + // In auto transmission mode, don't accelerate the engine when switching gears + float forward_input = mTransmission.mMode == ETransmissionMode::Manual? abs(mForwardInput) : 0.0f; + + // Engine not connected to wheels, update RPM based on engine inertia alone + mEngine.ApplyTorque(mEngine.GetTorque(forward_input), inDeltaTime); + } + + // Update transmission + // Note: only allow switching gears up when the tracks are rolling in the same direction + mTransmission.Update(inDeltaTime, mEngine.GetCurrentRPM(), mForwardInput, mLeftRatio * mRightRatio > 0.0f && can_engine_apply_torque); + + // Calculate the amount of torque the transmission gives to the differentials + float transmission_ratio = mTransmission.GetCurrentRatio(); + float transmission_torque = mTransmission.GetClutchFriction() * transmission_ratio * mEngine.GetTorque(abs(mForwardInput)); + if (transmission_torque != 0.0f) + { + // Apply the transmission torque to the wheels + for (uint i = 0; i < size(mTracks); ++i) + { + VehicleTrack &t = mTracks[i]; + + // Get wheel rotation ratio for this track + float ratio = i == 0? mLeftRatio : mRightRatio; + + // Calculate the max angular velocity of the driven wheel of the track given current engine RPM + // Note this adds 0.1% slop to avoid numerical accuracy issues + float track_max_angular_velocity = mEngine.GetCurrentRPM() / (transmission_ratio * t.mDifferentialRatio * ratio * VehicleEngine::cAngularVelocityToRPM) * 1.001f; + + // Calculate torque on the driven wheel + float differential_torque = t.mDifferentialRatio * ratio * transmission_torque; + + // Apply torque to driven wheel + if (t.mAngularVelocity * track_max_angular_velocity < 0.0f || abs(t.mAngularVelocity) < abs(track_max_angular_velocity)) + t.mAngularVelocity += differential_torque * inDeltaTime / t.mInertia; + } + } + + // Ensure that we have the correct ratio between the two tracks + SyncLeftRightTracks(); + + // Braking + for (VehicleTrack &t : mTracks) + { + // Calculate brake torque + float brake_torque = mBrakeInput * t.mMaxBrakeTorque; + if (brake_torque > 0.0f) + { + // Calculate how much torque is needed to stop the track from rotating in this time step + float brake_torque_to_lock_track = abs(t.mAngularVelocity) * t.mInertia / inDeltaTime; + if (brake_torque > brake_torque_to_lock_track) + { + // Wheels are locked + t.mAngularVelocity = 0.0f; + brake_torque -= brake_torque_to_lock_track; + } + else + { + // Slow down the track + t.mAngularVelocity -= Sign(t.mAngularVelocity) * brake_torque * inDeltaTime / t.mInertia; + } + } + + if (brake_torque > 0.0f) + { + // Sum the radius of all wheels touching the floor + float total_radius = 0.0f; + for (uint wheel_index : t.mWheels) + { + const WheelTV *w = static_cast(wheels[wheel_index]); + + if (w->HasContact()) + total_radius += w->GetSettings()->mRadius; + } + + if (total_radius > 0.0f) + { + brake_torque /= total_radius; + for (uint wheel_index : t.mWheels) + { + WheelTV *w = static_cast(wheels[wheel_index]); + if (w->HasContact()) + { + // Impulse: p = F * dt = Torque / Wheel_Radius * dt, Torque = Total_Torque * Wheel_Radius / Summed_Radius => p = Total_Torque * dt / Summed_Radius + w->mBrakeImpulse = brake_torque * inDeltaTime; + } + } + } + } + } + + // Update wheel angular velocity based on that of the track + for (Wheel *w_base : wheels) + { + WheelTV *w = static_cast(w_base); + w->CalculateAngularVelocity(mConstraint); + } +} + +bool TrackedVehicleController::SolveLongitudinalAndLateralConstraints(float inDeltaTime) +{ + bool impulse = false; + + for (Wheel *w_base : mConstraint.GetWheels()) + if (w_base->HasContact()) + { + WheelTV *w = static_cast(w_base); + const WheelSettingsTV *settings = w->GetSettings(); + VehicleTrack &track = mTracks[w->mTrackIndex]; + + // Calculate max impulse that we can apply on the ground + float max_longitudinal_friction_impulse = w->mCombinedLongitudinalFriction * w->GetSuspensionLambda(); + + // Calculate relative velocity between wheel contact point and floor in longitudinal direction + Vec3 relative_velocity = mConstraint.GetVehicleBody()->GetPointVelocity(w->GetContactPosition()) - w->GetContactPointVelocity(); + float relative_longitudinal_velocity = relative_velocity.Dot(w->GetContactLongitudinal()); + + // Calculate brake force to apply + float min_longitudinal_impulse, max_longitudinal_impulse; + if (w->mBrakeImpulse != 0.0f) + { + // Limit brake force by max tire friction + float brake_impulse = min(w->mBrakeImpulse, max_longitudinal_friction_impulse); + + // Check which direction the brakes should be applied (we don't want to apply an impulse that would accelerate the vehicle) + if (relative_longitudinal_velocity >= 0.0f) + { + min_longitudinal_impulse = -brake_impulse; + max_longitudinal_impulse = 0.0f; + } + else + { + min_longitudinal_impulse = 0.0f; + max_longitudinal_impulse = brake_impulse; + } + + // Longitudinal impulse, note that we assume that once the wheels are locked that the brakes have more than enough torque to keep the wheels locked so we exclude any rotation deltas + impulse |= w->SolveLongitudinalConstraintPart(mConstraint, min_longitudinal_impulse, max_longitudinal_impulse); + } + else + { + // Assume we want to apply an angular impulse that makes the delta velocity between track and ground zero in one time step, calculate the amount of linear impulse needed to do that + float desired_angular_velocity = relative_longitudinal_velocity / settings->mRadius; + float linear_impulse = (track.mAngularVelocity - desired_angular_velocity) * track.mInertia / settings->mRadius; + + // Limit the impulse by max track friction + float prev_lambda = w->GetLongitudinalLambda(); + min_longitudinal_impulse = max_longitudinal_impulse = Clamp(prev_lambda + linear_impulse, -max_longitudinal_friction_impulse, max_longitudinal_friction_impulse); + + // Longitudinal impulse + impulse |= w->SolveLongitudinalConstraintPart(mConstraint, min_longitudinal_impulse, max_longitudinal_impulse); + + // Update the angular velocity of the track according to the lambda that was applied + track.mAngularVelocity -= (w->GetLongitudinalLambda() - prev_lambda) * settings->mRadius / track.mInertia; + SyncLeftRightTracks(); + } + } + + for (Wheel *w_base : mConstraint.GetWheels()) + if (w_base->HasContact()) + { + WheelTV *w = static_cast(w_base); + + // Update angular velocity of wheel for the next iteration + w->CalculateAngularVelocity(mConstraint); + + // Lateral friction + float max_lateral_friction_impulse = w->mCombinedLateralFriction * w->GetSuspensionLambda(); + impulse |= w->SolveLateralConstraintPart(mConstraint, -max_lateral_friction_impulse, max_lateral_friction_impulse); + } + + return impulse; +} + +#ifdef JPH_DEBUG_RENDERER + +void TrackedVehicleController::Draw(DebugRenderer *inRenderer) const +{ + float constraint_size = mConstraint.GetDrawConstraintSize(); + + // Draw RPM + Body *body = mConstraint.GetVehicleBody(); + Vec3 rpm_meter_up = body->GetRotation() * mConstraint.GetLocalUp(); + RVec3 rpm_meter_pos = body->GetPosition() + body->GetRotation() * mRPMMeterPosition; + Vec3 rpm_meter_fwd = body->GetRotation() * mConstraint.GetLocalForward(); + mEngine.DrawRPM(inRenderer, rpm_meter_pos, rpm_meter_fwd, rpm_meter_up, mRPMMeterSize, mTransmission.mShiftDownRPM, mTransmission.mShiftUpRPM); + + // Draw current vehicle state + String status = StringFormat("Forward: %.1f, LRatio: %.1f, RRatio: %.1f, Brake: %.1f\n" + "Gear: %d, Clutch: %.1f, EngineRPM: %.0f, V: %.1f km/h", + (double)mForwardInput, (double)mLeftRatio, (double)mRightRatio, (double)mBrakeInput, + mTransmission.GetCurrentGear(), (double)mTransmission.GetClutchFriction(), (double)mEngine.GetCurrentRPM(), (double)body->GetLinearVelocity().Length() * 3.6); + inRenderer->DrawText3D(body->GetPosition(), status, Color::sWhite, constraint_size); + + for (const VehicleTrack &t : mTracks) + { + const WheelTV *w = static_cast(mConstraint.GetWheels()[t.mDrivenWheel]); + const WheelSettings *settings = w->GetSettings(); + + // Calculate where the suspension attaches to the body in world space + RVec3 ws_position = body->GetCenterOfMassPosition() + body->GetRotation() * (settings->mPosition - body->GetShape()->GetCenterOfMass()); + + DebugRenderer::sInstance->DrawText3D(ws_position, StringFormat("W: %.1f", (double)t.mAngularVelocity), Color::sWhite, constraint_size); + } + + RMat44 body_transform = body->GetWorldTransform(); + + for (const Wheel *w_base : mConstraint.GetWheels()) + { + const WheelTV *w = static_cast(w_base); + const WheelSettings *settings = w->GetSettings(); + + // Calculate where the suspension attaches to the body in world space + RVec3 ws_position = body_transform * settings->mPosition; + Vec3 ws_direction = body_transform.Multiply3x3(settings->mSuspensionDirection); + + // Draw suspension + RVec3 min_suspension_pos = ws_position + ws_direction * settings->mSuspensionMinLength; + RVec3 max_suspension_pos = ws_position + ws_direction * settings->mSuspensionMaxLength; + inRenderer->DrawLine(ws_position, min_suspension_pos, Color::sRed); + inRenderer->DrawLine(min_suspension_pos, max_suspension_pos, Color::sGreen); + + // Draw current length + RVec3 wheel_pos = ws_position + ws_direction * w->GetSuspensionLength(); + inRenderer->DrawMarker(wheel_pos, w->GetSuspensionLength() < settings->mSuspensionMinLength? Color::sRed : Color::sGreen, constraint_size); + + // Draw wheel basis + Vec3 wheel_forward, wheel_up, wheel_right; + mConstraint.GetWheelLocalBasis(w, wheel_forward, wheel_up, wheel_right); + wheel_forward = body_transform.Multiply3x3(wheel_forward); + wheel_up = body_transform.Multiply3x3(wheel_up); + wheel_right = body_transform.Multiply3x3(wheel_right); + Vec3 steering_axis = body_transform.Multiply3x3(settings->mSteeringAxis); + inRenderer->DrawLine(wheel_pos, wheel_pos + wheel_forward, Color::sRed); + inRenderer->DrawLine(wheel_pos, wheel_pos + wheel_up, Color::sGreen); + inRenderer->DrawLine(wheel_pos, wheel_pos + wheel_right, Color::sBlue); + inRenderer->DrawLine(wheel_pos, wheel_pos + steering_axis, Color::sYellow); + + // Draw wheel + RMat44 wheel_transform(Vec4(wheel_up, 0.0f), Vec4(wheel_right, 0.0f), Vec4(wheel_forward, 0.0f), wheel_pos); + wheel_transform.SetRotation(wheel_transform.GetRotation() * Mat44::sRotationY(-w->GetRotationAngle())); + inRenderer->DrawCylinder(wheel_transform, settings->mWidth * 0.5f, settings->mRadius, w->GetSuspensionLength() <= settings->mSuspensionMinLength? Color::sRed : Color::sGreen, DebugRenderer::ECastShadow::Off, DebugRenderer::EDrawMode::Wireframe); + + if (w->HasContact()) + { + // Draw contact + inRenderer->DrawLine(w->GetContactPosition(), w->GetContactPosition() + w->GetContactNormal(), Color::sYellow); + inRenderer->DrawLine(w->GetContactPosition(), w->GetContactPosition() + w->GetContactLongitudinal(), Color::sRed); + inRenderer->DrawLine(w->GetContactPosition(), w->GetContactPosition() + w->GetContactLateral(), Color::sBlue); + + DebugRenderer::sInstance->DrawText3D(w->GetContactPosition(), StringFormat("S: %.2f", (double)w->GetSuspensionLength()), Color::sWhite, constraint_size); + } + } +} + +#endif // JPH_DEBUG_RENDERER + +void TrackedVehicleController::SaveState(StateRecorder &inStream) const +{ + inStream.Write(mForwardInput); + inStream.Write(mLeftRatio); + inStream.Write(mRightRatio); + inStream.Write(mBrakeInput); + + mEngine.SaveState(inStream); + mTransmission.SaveState(inStream); + + for (const VehicleTrack &t : mTracks) + t.SaveState(inStream); +} + +void TrackedVehicleController::RestoreState(StateRecorder &inStream) +{ + inStream.Read(mForwardInput); + inStream.Read(mLeftRatio); + inStream.Read(mRightRatio); + inStream.Read(mBrakeInput); + + mEngine.RestoreState(inStream); + mTransmission.RestoreState(inStream); + + for (VehicleTrack &t : mTracks) + t.RestoreState(inStream); +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/TrackedVehicleController.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/TrackedVehicleController.h new file mode 100644 index 00000000000..f5dff5276e5 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/TrackedVehicleController.h @@ -0,0 +1,166 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +class PhysicsSystem; + +/// WheelSettings object specifically for TrackedVehicleController +class JPH_EXPORT WheelSettingsTV : public WheelSettings +{ +public: + JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, WheelSettingsTV) + + // See: WheelSettings + virtual void SaveBinaryState(StreamOut &inStream) const override; + virtual void RestoreBinaryState(StreamIn &inStream) override; + + float mLongitudinalFriction = 4.0f; ///< Friction in forward direction of tire + float mLateralFriction = 2.0f; ///< Friction in sideway direction of tire +}; + +/// Wheel object specifically for TrackedVehicleController +class JPH_EXPORT WheelTV : public Wheel +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Constructor + explicit WheelTV(const WheelSettingsTV &inWheel); + + /// Override GetSettings and cast to the correct class + const WheelSettingsTV * GetSettings() const { return static_cast(mSettings.GetPtr()); } + + /// Update the angular velocity of the wheel based on the angular velocity of the track + void CalculateAngularVelocity(const VehicleConstraint &inConstraint); + + /// Update the wheel rotation based on the current angular velocity + void Update(uint inWheelIndex, float inDeltaTime, const VehicleConstraint &inConstraint); + + int mTrackIndex = -1; ///< Index in mTracks to which this wheel is attached (calculated on initialization) + float mCombinedLongitudinalFriction = 0.0f; ///< Combined friction coefficient in longitudinal direction (combines terrain and track) + float mCombinedLateralFriction = 0.0f; ///< Combined friction coefficient in lateral direction (combines terrain and track) + float mBrakeImpulse = 0.0f; ///< Amount of impulse that the brakes can apply to the floor (excluding friction), spread out from brake impulse applied on track +}; + +/// Settings of a vehicle with tank tracks +/// +/// Default settings are based around what I could find about the M1 Abrams tank. +/// Note to avoid issues with very heavy objects vs very light objects the mass of the tank should be a lot lower (say 10x) than that of a real tank. That means that the engine/brake torque is also 10x less. +class JPH_EXPORT TrackedVehicleControllerSettings : public VehicleControllerSettings +{ +public: + JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, TrackedVehicleControllerSettings) + + // Constructor + TrackedVehicleControllerSettings(); + + // See: VehicleControllerSettings + virtual VehicleController * ConstructController(VehicleConstraint &inConstraint) const override; + virtual void SaveBinaryState(StreamOut &inStream) const override; + virtual void RestoreBinaryState(StreamIn &inStream) override; + + VehicleEngineSettings mEngine; ///< The properties of the engine + VehicleTransmissionSettings mTransmission; ///< The properties of the transmission (aka gear box) + VehicleTrackSettings mTracks[(int)ETrackSide::Num]; ///< List of tracks and their properties +}; + +/// Runtime controller class for vehicle with tank tracks +class JPH_EXPORT TrackedVehicleController : public VehicleController +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Constructor + TrackedVehicleController(const TrackedVehicleControllerSettings &inSettings, VehicleConstraint &inConstraint); + + /// Set input from driver + /// @param inForward Value between -1 and 1 for auto transmission and value between 0 and 1 indicating desired driving direction and amount the gas pedal is pressed + /// @param inLeftRatio Value between -1 and 1 indicating an extra multiplier to the rotation rate of the left track (used for steering) + /// @param inRightRatio Value between -1 and 1 indicating an extra multiplier to the rotation rate of the right track (used for steering) + /// @param inBrake Value between 0 and 1 indicating how strong the brake pedal is pressed + void SetDriverInput(float inForward, float inLeftRatio, float inRightRatio, float inBrake) { JPH_ASSERT(inLeftRatio != 0.0f && inRightRatio != 0.0f); mForwardInput = inForward; mLeftRatio = inLeftRatio; mRightRatio = inRightRatio; mBrakeInput = inBrake; } + + /// Value between -1 and 1 for auto transmission and value between 0 and 1 indicating desired driving direction and amount the gas pedal is pressed + void SetForwardInput(float inForward) { mForwardInput = inForward; } + float GetForwardInput() const { return mForwardInput; } + + /// Value between -1 and 1 indicating an extra multiplier to the rotation rate of the left track (used for steering) + void SetLeftRatio(float inLeftRatio) { JPH_ASSERT(inLeftRatio != 0.0f); mLeftRatio = inLeftRatio; } + float GetLeftRatio() const { return mLeftRatio; } + + /// Value between -1 and 1 indicating an extra multiplier to the rotation rate of the right track (used for steering) + void SetRightRatio(float inRightRatio) { JPH_ASSERT(inRightRatio != 0.0f); mRightRatio = inRightRatio; } + float GetRightRatio() const { return mRightRatio; } + + /// Value between 0 and 1 indicating how strong the brake pedal is pressed + void SetBrakeInput(float inBrake) { mBrakeInput = inBrake; } + float GetBrakeInput() const { return mBrakeInput; } + + /// Get current engine state + const VehicleEngine & GetEngine() const { return mEngine; } + + /// Get current engine state (writable interface, allows you to make changes to the configuration which will take effect the next time step) + VehicleEngine & GetEngine() { return mEngine; } + + /// Get current transmission state + const VehicleTransmission & GetTransmission() const { return mTransmission; } + + /// Get current transmission state (writable interface, allows you to make changes to the configuration which will take effect the next time step) + VehicleTransmission & GetTransmission() { return mTransmission; } + + /// Get the tracks this vehicle has + const VehicleTracks & GetTracks() const { return mTracks; } + + /// Get the tracks this vehicle has (writable interface, allows you to make changes to the configuration which will take effect the next time step) + VehicleTracks & GetTracks() { return mTracks; } + +#ifdef JPH_DEBUG_RENDERER + /// Debug drawing of RPM meter + void SetRPMMeter(Vec3Arg inPosition, float inSize) { mRPMMeterPosition = inPosition; mRPMMeterSize = inSize; } +#endif // JPH_DEBUG_RENDERER + +protected: + /// Synchronize angular velocities of left and right tracks according to their ratios + void SyncLeftRightTracks(); + + // See: VehicleController + virtual Wheel * ConstructWheel(const WheelSettings &inWheel) const override { JPH_ASSERT(IsKindOf(&inWheel, JPH_RTTI(WheelSettingsTV))); return new WheelTV(static_cast(inWheel)); } + virtual bool AllowSleep() const override; + virtual void PreCollide(float inDeltaTime, PhysicsSystem &inPhysicsSystem) override; + virtual void PostCollide(float inDeltaTime, PhysicsSystem &inPhysicsSystem) override; + virtual bool SolveLongitudinalAndLateralConstraints(float inDeltaTime) override; + virtual void SaveState(StateRecorder &inStream) const override; + virtual void RestoreState(StateRecorder &inStream) override; +#ifdef JPH_DEBUG_RENDERER + virtual void Draw(DebugRenderer *inRenderer) const override; +#endif // JPH_DEBUG_RENDERER + + // Control information + float mForwardInput = 0.0f; ///< Value between -1 and 1 for auto transmission and value between 0 and 1 indicating desired driving direction and amount the gas pedal is pressed + float mLeftRatio = 1.0f; ///< Value between -1 and 1 indicating an extra multiplier to the rotation rate of the left track (used for steering) + float mRightRatio = 1.0f; ///< Value between -1 and 1 indicating an extra multiplier to the rotation rate of the right track (used for steering) + float mBrakeInput = 0.0f; ///< Value between 0 and 1 indicating how strong the brake pedal is pressed + + // Simulation information + VehicleEngine mEngine; ///< Engine state of the vehicle + VehicleTransmission mTransmission; ///< Transmission state of the vehicle + VehicleTracks mTracks; ///< Tracks of the vehicle + +#ifdef JPH_DEBUG_RENDERER + // Debug settings + Vec3 mRPMMeterPosition { 0, 1, 0 }; ///< Position (in local space of the body) of the RPM meter when drawing the constraint + float mRPMMeterSize = 0.5f; ///< Size of the RPM meter when drawing the constraint +#endif // JPH_DEBUG_RENDERER +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleAntiRollBar.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleAntiRollBar.cpp new file mode 100644 index 00000000000..859bcafa175 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleAntiRollBar.cpp @@ -0,0 +1,33 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include + +JPH_NAMESPACE_BEGIN + +JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(VehicleAntiRollBar) +{ + JPH_ADD_ATTRIBUTE(VehicleAntiRollBar, mLeftWheel) + JPH_ADD_ATTRIBUTE(VehicleAntiRollBar, mRightWheel) + JPH_ADD_ATTRIBUTE(VehicleAntiRollBar, mStiffness) +} + +void VehicleAntiRollBar::SaveBinaryState(StreamOut &inStream) const +{ + inStream.Write(mLeftWheel); + inStream.Write(mRightWheel); + inStream.Write(mStiffness); +} + +void VehicleAntiRollBar::RestoreBinaryState(StreamIn &inStream) +{ + inStream.Read(mLeftWheel); + inStream.Read(mRightWheel); + inStream.Read(mStiffness); +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleAntiRollBar.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleAntiRollBar.h new file mode 100644 index 00000000000..197c3a0d2ff --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleAntiRollBar.h @@ -0,0 +1,31 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +/// An anti rollbar is a stiff spring that connects two wheels to reduce the amount of roll the vehicle makes in sharp corners +/// See: https://en.wikipedia.org/wiki/Anti-roll_bar +class JPH_EXPORT VehicleAntiRollBar +{ +public: + JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, VehicleAntiRollBar) + + /// Saves the contents in binary form to inStream. + void SaveBinaryState(StreamOut &inStream) const; + + /// Restores the contents in binary form to inStream. + void RestoreBinaryState(StreamIn &inStream); + + int mLeftWheel = 0; ///< Index (in mWheels) that represents the left wheel of this anti-rollbar + int mRightWheel = 1; ///< Index (in mWheels) that represents the right wheel of this anti-rollbar + float mStiffness = 1000.0f; ///< Stiffness (spring constant in N/m) of anti rollbar, can be 0 to disable the anti-rollbar +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleCollisionTester.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleCollisionTester.cpp new file mode 100644 index 00000000000..56246835794 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleCollisionTester.cpp @@ -0,0 +1,376 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +bool VehicleCollisionTesterRay::Collide(PhysicsSystem &inPhysicsSystem, const VehicleConstraint &inVehicleConstraint, uint inWheelIndex, RVec3Arg inOrigin, Vec3Arg inDirection, const BodyID &inVehicleBodyID, Body *&outBody, SubShapeID &outSubShapeID, RVec3 &outContactPosition, Vec3 &outContactNormal, float &outSuspensionLength) const +{ + const DefaultBroadPhaseLayerFilter default_broadphase_layer_filter = inPhysicsSystem.GetDefaultBroadPhaseLayerFilter(mObjectLayer); + const BroadPhaseLayerFilter &broadphase_layer_filter = mBroadPhaseLayerFilter != nullptr? *mBroadPhaseLayerFilter : default_broadphase_layer_filter; + + const DefaultObjectLayerFilter default_object_layer_filter = inPhysicsSystem.GetDefaultLayerFilter(mObjectLayer); + const ObjectLayerFilter &object_layer_filter = mObjectLayerFilter != nullptr? *mObjectLayerFilter : default_object_layer_filter; + + const IgnoreSingleBodyFilter default_body_filter(inVehicleBodyID); + const BodyFilter &body_filter = mBodyFilter != nullptr? *mBodyFilter : default_body_filter; + + const WheelSettings *wheel_settings = inVehicleConstraint.GetWheel(inWheelIndex)->GetSettings(); + float wheel_radius = wheel_settings->mRadius; + float ray_length = wheel_settings->mSuspensionMaxLength + wheel_radius; + RRayCast ray { inOrigin, ray_length * inDirection }; + + class MyCollector : public CastRayCollector + { + public: + MyCollector(PhysicsSystem &inPhysicsSystem, const RRayCast &inRay, Vec3Arg inUpDirection, float inCosMaxSlopeAngle) : + mPhysicsSystem(inPhysicsSystem), + mRay(inRay), + mUpDirection(inUpDirection), + mCosMaxSlopeAngle(inCosMaxSlopeAngle) + { + } + + virtual void AddHit(const RayCastResult &inResult) override + { + // Test if this collision is closer than the previous one + if (inResult.mFraction < GetEarlyOutFraction()) + { + // Lock the body + BodyLockRead lock(mPhysicsSystem.GetBodyLockInterfaceNoLock(), inResult.mBodyID); + JPH_ASSERT(lock.Succeeded()); // When this runs all bodies are locked so this should not fail + const Body *body = &lock.GetBody(); + + if (body->IsSensor()) + return; + + // Test that we're not hitting a vertical wall + RVec3 contact_pos = mRay.GetPointOnRay(inResult.mFraction); + Vec3 normal = body->GetWorldSpaceSurfaceNormal(inResult.mSubShapeID2, contact_pos); + if (normal.Dot(mUpDirection) > mCosMaxSlopeAngle) + { + // Update early out fraction to this hit + UpdateEarlyOutFraction(inResult.mFraction); + + // Get the contact properties + mBody = body; + mSubShapeID2 = inResult.mSubShapeID2; + mContactPosition = contact_pos; + mContactNormal = normal; + } + } + } + + // Configuration + PhysicsSystem & mPhysicsSystem; + RRayCast mRay; + Vec3 mUpDirection; + float mCosMaxSlopeAngle; + + // Resulting closest collision + const Body * mBody = nullptr; + SubShapeID mSubShapeID2; + RVec3 mContactPosition; + Vec3 mContactNormal; + }; + + RayCastSettings settings; + + MyCollector collector(inPhysicsSystem, ray, mUp, mCosMaxSlopeAngle); + inPhysicsSystem.GetNarrowPhaseQueryNoLock().CastRay(ray, settings, collector, broadphase_layer_filter, object_layer_filter, body_filter); + if (collector.mBody == nullptr) + return false; + + outBody = const_cast(collector.mBody); + outSubShapeID = collector.mSubShapeID2; + outContactPosition = collector.mContactPosition; + outContactNormal = collector.mContactNormal; + outSuspensionLength = max(0.0f, ray_length * collector.GetEarlyOutFraction() - wheel_radius); + + return true; +} + +void VehicleCollisionTesterRay::PredictContactProperties(PhysicsSystem &inPhysicsSystem, const VehicleConstraint &inVehicleConstraint, uint inWheelIndex, RVec3Arg inOrigin, Vec3Arg inDirection, const BodyID &inVehicleBodyID, Body *&ioBody, SubShapeID &ioSubShapeID, RVec3 &ioContactPosition, Vec3 &ioContactNormal, float &ioSuspensionLength) const +{ + // Recalculate the contact points assuming the contact point is on an infinite plane + const WheelSettings *wheel_settings = inVehicleConstraint.GetWheel(inWheelIndex)->GetSettings(); + float d_dot_n = inDirection.Dot(ioContactNormal); + if (d_dot_n < -1.0e-6f) + { + // Reproject the contact position using the suspension ray and the plane formed by the contact position and normal + ioContactPosition = inOrigin + Vec3(ioContactPosition - inOrigin).Dot(ioContactNormal) / d_dot_n * inDirection; + + // The suspension length is simply the distance between the contact position and the suspension origin excluding the wheel radius + ioSuspensionLength = Clamp(Vec3(ioContactPosition - inOrigin).Dot(inDirection) - wheel_settings->mRadius, 0.0f, wheel_settings->mSuspensionMaxLength); + } + else + { + // If the normal is pointing away we assume there's no collision anymore + ioSuspensionLength = wheel_settings->mSuspensionMaxLength; + } +} + +bool VehicleCollisionTesterCastSphere::Collide(PhysicsSystem &inPhysicsSystem, const VehicleConstraint &inVehicleConstraint, uint inWheelIndex, RVec3Arg inOrigin, Vec3Arg inDirection, const BodyID &inVehicleBodyID, Body *&outBody, SubShapeID &outSubShapeID, RVec3 &outContactPosition, Vec3 &outContactNormal, float &outSuspensionLength) const +{ + const DefaultBroadPhaseLayerFilter default_broadphase_layer_filter = inPhysicsSystem.GetDefaultBroadPhaseLayerFilter(mObjectLayer); + const BroadPhaseLayerFilter &broadphase_layer_filter = mBroadPhaseLayerFilter != nullptr? *mBroadPhaseLayerFilter : default_broadphase_layer_filter; + + const DefaultObjectLayerFilter default_object_layer_filter = inPhysicsSystem.GetDefaultLayerFilter(mObjectLayer); + const ObjectLayerFilter &object_layer_filter = mObjectLayerFilter != nullptr? *mObjectLayerFilter : default_object_layer_filter; + + const IgnoreSingleBodyFilter default_body_filter(inVehicleBodyID); + const BodyFilter &body_filter = mBodyFilter != nullptr? *mBodyFilter : default_body_filter; + + SphereShape sphere(mRadius); + sphere.SetEmbedded(); + + const WheelSettings *wheel_settings = inVehicleConstraint.GetWheel(inWheelIndex)->GetSettings(); + float wheel_radius = wheel_settings->mRadius; + float shape_cast_length = wheel_settings->mSuspensionMaxLength + wheel_radius - mRadius; + RShapeCast shape_cast(&sphere, Vec3::sReplicate(1.0f), RMat44::sTranslation(inOrigin), inDirection * shape_cast_length); + + ShapeCastSettings settings; + settings.mUseShrunkenShapeAndConvexRadius = true; + settings.mReturnDeepestPoint = true; + + class MyCollector : public CastShapeCollector + { + public: + MyCollector(PhysicsSystem &inPhysicsSystem, const RShapeCast &inShapeCast, Vec3Arg inUpDirection, float inCosMaxSlopeAngle) : + mPhysicsSystem(inPhysicsSystem), + mShapeCast(inShapeCast), + mUpDirection(inUpDirection), + mCosMaxSlopeAngle(inCosMaxSlopeAngle) + { + } + + virtual void AddHit(const ShapeCastResult &inResult) override + { + // Test if this collision is closer/deeper than the previous one + float early_out = inResult.GetEarlyOutFraction(); + if (early_out < GetEarlyOutFraction()) + { + // Lock the body + BodyLockRead lock(mPhysicsSystem.GetBodyLockInterfaceNoLock(), inResult.mBodyID2); + JPH_ASSERT(lock.Succeeded()); // When this runs all bodies are locked so this should not fail + const Body *body = &lock.GetBody(); + + if (body->IsSensor()) + return; + + // Test that we're not hitting a vertical wall + Vec3 normal = -inResult.mPenetrationAxis.Normalized(); + if (normal.Dot(mUpDirection) > mCosMaxSlopeAngle) + { + // Update early out fraction to this hit + UpdateEarlyOutFraction(early_out); + + // Get the contact properties + mBody = body; + mSubShapeID2 = inResult.mSubShapeID2; + mContactPosition = mShapeCast.mCenterOfMassStart.GetTranslation() + inResult.mContactPointOn2; + mContactNormal = normal; + mFraction = inResult.mFraction; + } + } + } + + // Configuration + PhysicsSystem & mPhysicsSystem; + const RShapeCast & mShapeCast; + Vec3 mUpDirection; + float mCosMaxSlopeAngle; + + // Resulting closest collision + const Body * mBody = nullptr; + SubShapeID mSubShapeID2; + RVec3 mContactPosition; + Vec3 mContactNormal; + float mFraction; + }; + + MyCollector collector(inPhysicsSystem, shape_cast, mUp, mCosMaxSlopeAngle); + inPhysicsSystem.GetNarrowPhaseQueryNoLock().CastShape(shape_cast, settings, shape_cast.mCenterOfMassStart.GetTranslation(), collector, broadphase_layer_filter, object_layer_filter, body_filter); + if (collector.mBody == nullptr) + return false; + + outBody = const_cast(collector.mBody); + outSubShapeID = collector.mSubShapeID2; + outContactPosition = collector.mContactPosition; + outContactNormal = collector.mContactNormal; + outSuspensionLength = max(0.0f, shape_cast_length * collector.mFraction + mRadius - wheel_radius); + + return true; +} + +void VehicleCollisionTesterCastSphere::PredictContactProperties(PhysicsSystem &inPhysicsSystem, const VehicleConstraint &inVehicleConstraint, uint inWheelIndex, RVec3Arg inOrigin, Vec3Arg inDirection, const BodyID &inVehicleBodyID, Body *&ioBody, SubShapeID &ioSubShapeID, RVec3 &ioContactPosition, Vec3 &ioContactNormal, float &ioSuspensionLength) const +{ + // Recalculate the contact points assuming the contact point is on an infinite plane + const WheelSettings *wheel_settings = inVehicleConstraint.GetWheel(inWheelIndex)->GetSettings(); + float d_dot_n = inDirection.Dot(ioContactNormal); + if (d_dot_n < -1.0e-6f) + { + // Reproject the contact position using the suspension cast sphere and the plane formed by the contact position and normal + // This solves x = inOrigin + fraction * inDirection and (x - ioContactPosition) . ioContactNormal = mRadius for fraction + float oc_dot_n = Vec3(ioContactPosition - inOrigin).Dot(ioContactNormal); + float fraction = (mRadius + oc_dot_n) / d_dot_n; + ioContactPosition = inOrigin + fraction * inDirection - mRadius * ioContactNormal; + + // Calculate the new suspension length in the same way as the cast sphere normally does + ioSuspensionLength = Clamp(fraction + mRadius - wheel_settings->mRadius, 0.0f, wheel_settings->mSuspensionMaxLength); + } + else + { + // If the normal is pointing away we assume there's no collision anymore + ioSuspensionLength = wheel_settings->mSuspensionMaxLength; + } +} + +bool VehicleCollisionTesterCastCylinder::Collide(PhysicsSystem &inPhysicsSystem, const VehicleConstraint &inVehicleConstraint, uint inWheelIndex, RVec3Arg inOrigin, Vec3Arg inDirection, const BodyID &inVehicleBodyID, Body *&outBody, SubShapeID &outSubShapeID, RVec3 &outContactPosition, Vec3 &outContactNormal, float &outSuspensionLength) const +{ + const DefaultBroadPhaseLayerFilter default_broadphase_layer_filter = inPhysicsSystem.GetDefaultBroadPhaseLayerFilter(mObjectLayer); + const BroadPhaseLayerFilter &broadphase_layer_filter = mBroadPhaseLayerFilter != nullptr? *mBroadPhaseLayerFilter : default_broadphase_layer_filter; + + const DefaultObjectLayerFilter default_object_layer_filter = inPhysicsSystem.GetDefaultLayerFilter(mObjectLayer); + const ObjectLayerFilter &object_layer_filter = mObjectLayerFilter != nullptr? *mObjectLayerFilter : default_object_layer_filter; + + const IgnoreSingleBodyFilter default_body_filter(inVehicleBodyID); + const BodyFilter &body_filter = mBodyFilter != nullptr? *mBodyFilter : default_body_filter; + + const WheelSettings *wheel_settings = inVehicleConstraint.GetWheel(inWheelIndex)->GetSettings(); + float max_suspension_length = wheel_settings->mSuspensionMaxLength; + + // Get the wheel transform given that the cylinder rotates around the Y axis + RMat44 shape_cast_start = inVehicleConstraint.GetWheelWorldTransform(inWheelIndex, Vec3::sAxisY(), Vec3::sAxisX()); + shape_cast_start.SetTranslation(inOrigin); + + // Construct a cylinder with the dimensions of the wheel + float wheel_half_width = 0.5f * wheel_settings->mWidth; + CylinderShape cylinder(wheel_half_width, wheel_settings->mRadius, min(wheel_half_width, wheel_settings->mRadius) * mConvexRadiusFraction); + cylinder.SetEmbedded(); + + RShapeCast shape_cast(&cylinder, Vec3::sReplicate(1.0f), shape_cast_start, inDirection * max_suspension_length); + + ShapeCastSettings settings; + settings.mUseShrunkenShapeAndConvexRadius = true; + settings.mReturnDeepestPoint = true; + + class MyCollector : public CastShapeCollector + { + public: + MyCollector(PhysicsSystem &inPhysicsSystem, const RShapeCast &inShapeCast) : + mPhysicsSystem(inPhysicsSystem), + mShapeCast(inShapeCast) + { + } + + virtual void AddHit(const ShapeCastResult &inResult) override + { + // Test if this collision is closer/deeper than the previous one + float early_out = inResult.GetEarlyOutFraction(); + if (early_out < GetEarlyOutFraction()) + { + // Lock the body + BodyLockRead lock(mPhysicsSystem.GetBodyLockInterfaceNoLock(), inResult.mBodyID2); + JPH_ASSERT(lock.Succeeded()); // When this runs all bodies are locked so this should not fail + const Body *body = &lock.GetBody(); + + if (body->IsSensor()) + return; + + // Update early out fraction to this hit + UpdateEarlyOutFraction(early_out); + + // Get the contact properties + mBody = body; + mSubShapeID2 = inResult.mSubShapeID2; + mContactPosition = mShapeCast.mCenterOfMassStart.GetTranslation() + inResult.mContactPointOn2; + mContactNormal = -inResult.mPenetrationAxis.Normalized(); + mFraction = inResult.mFraction; + } + } + + // Configuration + PhysicsSystem & mPhysicsSystem; + const RShapeCast & mShapeCast; + + // Resulting closest collision + const Body * mBody = nullptr; + SubShapeID mSubShapeID2; + RVec3 mContactPosition; + Vec3 mContactNormal; + float mFraction; + }; + + MyCollector collector(inPhysicsSystem, shape_cast); + inPhysicsSystem.GetNarrowPhaseQueryNoLock().CastShape(shape_cast, settings, shape_cast.mCenterOfMassStart.GetTranslation(), collector, broadphase_layer_filter, object_layer_filter, body_filter); + if (collector.mBody == nullptr) + return false; + + outBody = const_cast(collector.mBody); + outSubShapeID = collector.mSubShapeID2; + outContactPosition = collector.mContactPosition; + outContactNormal = collector.mContactNormal; + outSuspensionLength = max_suspension_length * collector.mFraction; + + return true; +} + +void VehicleCollisionTesterCastCylinder::PredictContactProperties(PhysicsSystem &inPhysicsSystem, const VehicleConstraint &inVehicleConstraint, uint inWheelIndex, RVec3Arg inOrigin, Vec3Arg inDirection, const BodyID &inVehicleBodyID, Body *&ioBody, SubShapeID &ioSubShapeID, RVec3 &ioContactPosition, Vec3 &ioContactNormal, float &ioSuspensionLength) const +{ + // Recalculate the contact points assuming the contact point is on an infinite plane + const WheelSettings *wheel_settings = inVehicleConstraint.GetWheel(inWheelIndex)->GetSettings(); + float d_dot_n = inDirection.Dot(ioContactNormal); + if (d_dot_n < -1.0e-6f) + { + // Wheel size + float half_width = 0.5f * wheel_settings->mWidth; + float radius = wheel_settings->mRadius; + + // Get the inverse local space contact normal for a cylinder pointing along Y + RMat44 wheel_transform = inVehicleConstraint.GetWheelWorldTransform(inWheelIndex, Vec3::sAxisY(), Vec3::sAxisX()); + Vec3 inverse_local_normal = -wheel_transform.Multiply3x3Transposed(ioContactNormal); + + // Get the support point of this normal in local space of the cylinder + // See CylinderShape::Cylinder::GetSupport + float x = inverse_local_normal.GetX(), y = inverse_local_normal.GetY(), z = inverse_local_normal.GetZ(); + float o = sqrt(Square(x) + Square(z)); + Vec3 support_point; + if (o > 0.0f) + support_point = Vec3((radius * x) / o, Sign(y) * half_width, (radius * z) / o); + else + support_point = Vec3(0, Sign(y) * half_width, 0); + + // Rotate back to world space + support_point = wheel_transform.Multiply3x3(support_point); + + // Now we can use inOrigin + support_point as the start of a ray of our suspension to the contact plane + // as know that it is the first point on the wheel that will hit the plane + RVec3 origin = inOrigin + support_point; + + // Calculate contact position and suspension length, the is the same as VehicleCollisionTesterRay + // but we don't need to take the radius into account anymore + Vec3 oc(ioContactPosition - origin); + ioContactPosition = origin + oc.Dot(ioContactNormal) / d_dot_n * inDirection; + ioSuspensionLength = Clamp(oc.Dot(inDirection), 0.0f, wheel_settings->mSuspensionMaxLength); + } + else + { + // If the normal is pointing away we assume there's no collision anymore + ioSuspensionLength = wheel_settings->mSuspensionMaxLength; + } +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleCollisionTester.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleCollisionTester.h new file mode 100644 index 00000000000..7c222756c8b --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleCollisionTester.h @@ -0,0 +1,146 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +JPH_NAMESPACE_BEGIN + +class PhysicsSystem; +class VehicleConstraint; +class BroadPhaseLayerFilter; +class ObjectLayerFilter; +class BodyFilter; + +/// Class that does collision detection between wheels and ground +class JPH_EXPORT VehicleCollisionTester : public RefTarget, public NonCopyable +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Constructors + VehicleCollisionTester() = default; + explicit VehicleCollisionTester(ObjectLayer inObjectLayer) : mObjectLayer(inObjectLayer) { } + + /// Virtual destructor + virtual ~VehicleCollisionTester() = default; + + /// Object layer to use for collision detection, this is used when the filters are not overridden + ObjectLayer GetObjectLayer() const { return mObjectLayer; } + void SetObjectLayer(ObjectLayer inObjectLayer) { mObjectLayer = inObjectLayer; } + + /// Access to the broad phase layer filter, when set this overrides the object layer supplied in the constructor + void SetBroadPhaseLayerFilter(const BroadPhaseLayerFilter *inFilter) { mBroadPhaseLayerFilter = inFilter; } + const BroadPhaseLayerFilter * GetBroadPhaseLayerFilter() const { return mBroadPhaseLayerFilter; } + + /// Access to the object layer filter, when set this overrides the object layer supplied in the constructor + void SetObjectLayerFilter(const ObjectLayerFilter *inFilter) { mObjectLayerFilter = inFilter; } + const ObjectLayerFilter * GetObjectLayerFilter() const { return mObjectLayerFilter; } + + /// Access to the body filter, when set this overrides the default filter that filters out the vehicle body + void SetBodyFilter(const BodyFilter *inFilter) { mBodyFilter = inFilter; } + const BodyFilter * GetBodyFilter() const { return mBodyFilter; } + + /// Do a collision test with the world + /// @param inPhysicsSystem The physics system that should be tested against + /// @param inVehicleConstraint The vehicle constraint + /// @param inWheelIndex Index of the wheel that we're testing collision for + /// @param inOrigin Origin for the test, corresponds to the world space position for the suspension attachment point + /// @param inDirection Direction for the test (unit vector, world space) + /// @param inVehicleBodyID This body should be filtered out during collision detection to avoid self collisions + /// @param outBody Body that the wheel collided with + /// @param outSubShapeID Sub shape ID that the wheel collided with + /// @param outContactPosition Contact point between wheel and floor, in world space + /// @param outContactNormal Contact normal between wheel and floor, pointing away from the floor + /// @param outSuspensionLength New length of the suspension [0, inSuspensionMaxLength] + /// @return True when collision found, false if not + virtual bool Collide(PhysicsSystem &inPhysicsSystem, const VehicleConstraint &inVehicleConstraint, uint inWheelIndex, RVec3Arg inOrigin, Vec3Arg inDirection, const BodyID &inVehicleBodyID, Body *&outBody, SubShapeID &outSubShapeID, RVec3 &outContactPosition, Vec3 &outContactNormal, float &outSuspensionLength) const = 0; + + /// Do a cheap contact properties prediction based on the contact properties from the last collision test (provided as input parameters) + /// @param inPhysicsSystem The physics system that should be tested against + /// @param inVehicleConstraint The vehicle constraint + /// @param inWheelIndex Index of the wheel that we're testing collision for + /// @param inOrigin Origin for the test, corresponds to the world space position for the suspension attachment point + /// @param inDirection Direction for the test (unit vector, world space) + /// @param inVehicleBodyID The body ID for the vehicle itself + /// @param ioBody Body that the wheel previously collided with + /// @param ioSubShapeID Sub shape ID that the wheel collided with during the last check + /// @param ioContactPosition Contact point between wheel and floor during the last check, in world space + /// @param ioContactNormal Contact normal between wheel and floor during the last check, pointing away from the floor + /// @param ioSuspensionLength New length of the suspension [0, inSuspensionMaxLength] + virtual void PredictContactProperties(PhysicsSystem &inPhysicsSystem, const VehicleConstraint &inVehicleConstraint, uint inWheelIndex, RVec3Arg inOrigin, Vec3Arg inDirection, const BodyID &inVehicleBodyID, Body *&ioBody, SubShapeID &ioSubShapeID, RVec3 &ioContactPosition, Vec3 &ioContactNormal, float &ioSuspensionLength) const = 0; + +protected: + const BroadPhaseLayerFilter * mBroadPhaseLayerFilter = nullptr; + const ObjectLayerFilter * mObjectLayerFilter = nullptr; + const BodyFilter * mBodyFilter = nullptr; + ObjectLayer mObjectLayer = cObjectLayerInvalid; +}; + +/// Collision tester that tests collision using a raycast +class JPH_EXPORT VehicleCollisionTesterRay : public VehicleCollisionTester +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Constructor + /// @param inObjectLayer Object layer to test collision with + /// @param inUp World space up vector, used to avoid colliding with vertical walls. + /// @param inMaxSlopeAngle Max angle (rad) that is considered for colliding wheels. This is to avoid colliding with vertical walls. + VehicleCollisionTesterRay(ObjectLayer inObjectLayer, Vec3Arg inUp = Vec3::sAxisY(), float inMaxSlopeAngle = DegreesToRadians(80.0f)) : VehicleCollisionTester(inObjectLayer), mUp(inUp), mCosMaxSlopeAngle(Cos(inMaxSlopeAngle)) { } + + // See: VehicleCollisionTester + virtual bool Collide(PhysicsSystem &inPhysicsSystem, const VehicleConstraint &inVehicleConstraint, uint inWheelIndex, RVec3Arg inOrigin, Vec3Arg inDirection, const BodyID &inVehicleBodyID, Body *&outBody, SubShapeID &outSubShapeID, RVec3 &outContactPosition, Vec3 &outContactNormal, float &outSuspensionLength) const override; + virtual void PredictContactProperties(PhysicsSystem &inPhysicsSystem, const VehicleConstraint &inVehicleConstraint, uint inWheelIndex, RVec3Arg inOrigin, Vec3Arg inDirection, const BodyID &inVehicleBodyID, Body *&ioBody, SubShapeID &ioSubShapeID, RVec3 &ioContactPosition, Vec3 &ioContactNormal, float &ioSuspensionLength) const override; + +private: + Vec3 mUp; + float mCosMaxSlopeAngle; +}; + +/// Collision tester that tests collision using a sphere cast +class JPH_EXPORT VehicleCollisionTesterCastSphere : public VehicleCollisionTester +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Constructor + /// @param inObjectLayer Object layer to test collision with + /// @param inUp World space up vector, used to avoid colliding with vertical walls. + /// @param inRadius Radius of sphere + /// @param inMaxSlopeAngle Max angle (rad) that is considered for colliding wheels. This is to avoid colliding with vertical walls. + VehicleCollisionTesterCastSphere(ObjectLayer inObjectLayer, float inRadius, Vec3Arg inUp = Vec3::sAxisY(), float inMaxSlopeAngle = DegreesToRadians(80.0f)) : VehicleCollisionTester(inObjectLayer), mRadius(inRadius), mUp(inUp), mCosMaxSlopeAngle(Cos(inMaxSlopeAngle)) { } + + // See: VehicleCollisionTester + virtual bool Collide(PhysicsSystem &inPhysicsSystem, const VehicleConstraint &inVehicleConstraint, uint inWheelIndex, RVec3Arg inOrigin, Vec3Arg inDirection, const BodyID &inVehicleBodyID, Body *&outBody, SubShapeID &outSubShapeID, RVec3 &outContactPosition, Vec3 &outContactNormal, float &outSuspensionLength) const override; + virtual void PredictContactProperties(PhysicsSystem &inPhysicsSystem, const VehicleConstraint &inVehicleConstraint, uint inWheelIndex, RVec3Arg inOrigin, Vec3Arg inDirection, const BodyID &inVehicleBodyID, Body *&ioBody, SubShapeID &ioSubShapeID, RVec3 &ioContactPosition, Vec3 &ioContactNormal, float &ioSuspensionLength) const override; + +private: + float mRadius; + Vec3 mUp; + float mCosMaxSlopeAngle; +}; + +/// Collision tester that tests collision using a cylinder shape +class JPH_EXPORT VehicleCollisionTesterCastCylinder : public VehicleCollisionTester +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Constructor + /// @param inObjectLayer Object layer to test collision with + /// @param inConvexRadiusFraction Fraction of half the wheel width (or wheel radius if it is smaller) that is used as the convex radius + VehicleCollisionTesterCastCylinder(ObjectLayer inObjectLayer, float inConvexRadiusFraction = 0.1f) : VehicleCollisionTester(inObjectLayer), mConvexRadiusFraction(inConvexRadiusFraction) { JPH_ASSERT(mConvexRadiusFraction >= 0.0f && mConvexRadiusFraction <= 1.0f); } + + // See: VehicleCollisionTester + virtual bool Collide(PhysicsSystem &inPhysicsSystem, const VehicleConstraint &inVehicleConstraint, uint inWheelIndex, RVec3Arg inOrigin, Vec3Arg inDirection, const BodyID &inVehicleBodyID, Body *&outBody, SubShapeID &outSubShapeID, RVec3 &outContactPosition, Vec3 &outContactNormal, float &outSuspensionLength) const override; + virtual void PredictContactProperties(PhysicsSystem &inPhysicsSystem, const VehicleConstraint &inVehicleConstraint, uint inWheelIndex, RVec3Arg inOrigin, Vec3Arg inDirection, const BodyID &inVehicleBodyID, Body *&ioBody, SubShapeID &ioSubShapeID, RVec3 &ioContactPosition, Vec3 &ioContactNormal, float &ioSuspensionLength) const override; + +private: + float mConvexRadiusFraction; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleConstraint.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleConstraint.cpp new file mode 100644 index 00000000000..1be908457c0 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleConstraint.cpp @@ -0,0 +1,681 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(VehicleConstraintSettings) +{ + JPH_ADD_BASE_CLASS(VehicleConstraintSettings, ConstraintSettings) + + JPH_ADD_ATTRIBUTE(VehicleConstraintSettings, mUp) + JPH_ADD_ATTRIBUTE(VehicleConstraintSettings, mForward) + JPH_ADD_ATTRIBUTE(VehicleConstraintSettings, mMaxPitchRollAngle) + JPH_ADD_ATTRIBUTE(VehicleConstraintSettings, mWheels) + JPH_ADD_ATTRIBUTE(VehicleConstraintSettings, mAntiRollBars) + JPH_ADD_ATTRIBUTE(VehicleConstraintSettings, mController) +} + +void VehicleConstraintSettings::SaveBinaryState(StreamOut &inStream) const +{ + ConstraintSettings::SaveBinaryState(inStream); + + inStream.Write(mUp); + inStream.Write(mForward); + inStream.Write(mMaxPitchRollAngle); + + uint32 num_anti_rollbars = (uint32)mAntiRollBars.size(); + inStream.Write(num_anti_rollbars); + for (const VehicleAntiRollBar &r : mAntiRollBars) + r.SaveBinaryState(inStream); + + uint32 num_wheels = (uint32)mWheels.size(); + inStream.Write(num_wheels); + for (const WheelSettings *w : mWheels) + w->SaveBinaryState(inStream); + + inStream.Write(mController->GetRTTI()->GetHash()); + mController->SaveBinaryState(inStream); +} + +void VehicleConstraintSettings::RestoreBinaryState(StreamIn &inStream) +{ + ConstraintSettings::RestoreBinaryState(inStream); + + inStream.Read(mUp); + inStream.Read(mForward); + inStream.Read(mMaxPitchRollAngle); + + uint32 num_anti_rollbars = 0; + inStream.Read(num_anti_rollbars); + mAntiRollBars.resize(num_anti_rollbars); + for (VehicleAntiRollBar &r : mAntiRollBars) + r.RestoreBinaryState(inStream); + + uint32 num_wheels = 0; + inStream.Read(num_wheels); + mWheels.resize(num_wheels); + for (WheelSettings *w : mWheels) + w->RestoreBinaryState(inStream); + + uint32 hash = 0; + inStream.Read(hash); + const RTTI *rtti = Factory::sInstance->Find(hash); + mController = reinterpret_cast(rtti->CreateObject()); + mController->RestoreBinaryState(inStream); +} + +VehicleConstraint::VehicleConstraint(Body &inVehicleBody, const VehicleConstraintSettings &inSettings) : + Constraint(inSettings), + mBody(&inVehicleBody), + mForward(inSettings.mForward), + mUp(inSettings.mUp), + mWorldUp(inSettings.mUp) +{ + // Check sanity of incoming settings + JPH_ASSERT(inSettings.mUp.IsNormalized()); + JPH_ASSERT(inSettings.mForward.IsNormalized()); + JPH_ASSERT(!inSettings.mWheels.empty()); + + // Store max pitch/roll angle + SetMaxPitchRollAngle(inSettings.mMaxPitchRollAngle); + + // Copy anti-rollbar settings + mAntiRollBars.resize(inSettings.mAntiRollBars.size()); + for (uint i = 0; i < mAntiRollBars.size(); ++i) + { + const VehicleAntiRollBar &r = inSettings.mAntiRollBars[i]; + mAntiRollBars[i] = r; + JPH_ASSERT(r.mStiffness >= 0.0f); + } + + // Construct our controller class + mController = inSettings.mController->ConstructController(*this); + + // Create wheels + mWheels.resize(inSettings.mWheels.size()); + for (uint i = 0; i < mWheels.size(); ++i) + mWheels[i] = mController->ConstructWheel(*inSettings.mWheels[i]); + + // Use the body ID as a seed for the step counter so that not all vehicles will update at the same time + mCurrentStep = uint32(Hash64(inVehicleBody.GetID().GetIndex())); +} + +VehicleConstraint::~VehicleConstraint() +{ + // Destroy controller + delete mController; + + // Destroy our wheels + for (Wheel *w : mWheels) + delete w; +} + +void VehicleConstraint::GetWheelLocalBasis(const Wheel *inWheel, Vec3 &outForward, Vec3 &outUp, Vec3 &outRight) const +{ + const WheelSettings *settings = inWheel->mSettings; + + Quat steer_rotation = Quat::sRotation(settings->mSteeringAxis, inWheel->mSteerAngle); + outUp = steer_rotation * settings->mWheelUp; + outForward = steer_rotation * settings->mWheelForward; + outRight = outForward.Cross(outUp).Normalized(); + outForward = outUp.Cross(outRight).Normalized(); +} + +Mat44 VehicleConstraint::GetWheelLocalTransform(uint inWheelIndex, Vec3Arg inWheelRight, Vec3Arg inWheelUp) const +{ + JPH_ASSERT(inWheelIndex < mWheels.size()); + + const Wheel *wheel = mWheels[inWheelIndex]; + const WheelSettings *settings = wheel->mSettings; + + // Use the two vectors provided to calculate a matrix that takes us from wheel model space to X = right, Y = up, Z = forward (the space where we will rotate the wheel) + Mat44 wheel_to_rotational = Mat44(Vec4(inWheelRight, 0), Vec4(inWheelUp, 0), Vec4(inWheelUp.Cross(inWheelRight), 0), Vec4(0, 0, 0, 1)).Transposed(); + + // Calculate the matrix that takes us from the rotational space to vehicle local space + Vec3 local_forward, local_up, local_right; + GetWheelLocalBasis(wheel, local_forward, local_up, local_right); + Vec3 local_wheel_pos = settings->mPosition + settings->mSuspensionDirection * wheel->mSuspensionLength; + Mat44 rotational_to_local(Vec4(local_right, 0), Vec4(local_up, 0), Vec4(local_forward, 0), Vec4(local_wheel_pos, 1)); + + // Calculate transform of rotated wheel + return rotational_to_local * Mat44::sRotationX(wheel->mAngle) * wheel_to_rotational; +} + +RMat44 VehicleConstraint::GetWheelWorldTransform(uint inWheelIndex, Vec3Arg inWheelRight, Vec3Arg inWheelUp) const +{ + return mBody->GetWorldTransform() * GetWheelLocalTransform(inWheelIndex, inWheelRight, inWheelUp); +} + +void VehicleConstraint::OnStep(float inDeltaTime, PhysicsSystem &inPhysicsSystem) +{ + JPH_PROFILE_FUNCTION(); + + // Callback to higher-level systems. We do it before PreCollide, in case steering changes. + if (mPreStepCallback != nullptr) + mPreStepCallback(*this, inDeltaTime, inPhysicsSystem); + + // Calculate new world up vector by inverting gravity + mWorldUp = (-inPhysicsSystem.GetGravity()).NormalizedOr(mWorldUp); + + // Callback on our controller + mController->PreCollide(inDeltaTime, inPhysicsSystem); + + // Calculate if this constraint is active by checking if our main vehicle body is active or any of the bodies we touch are active + mIsActive = mBody->IsActive(); + + // Test how often we need to update the wheels + uint num_steps_between_collisions = mIsActive? mNumStepsBetweenCollisionTestActive : mNumStepsBetweenCollisionTestInactive; + + RMat44 body_transform = mBody->GetWorldTransform(); + + // Test collision for wheels + for (uint wheel_index = 0; wheel_index < mWheels.size(); ++wheel_index) + { + Wheel *w = mWheels[wheel_index]; + const WheelSettings *settings = w->mSettings; + + // Calculate suspension origin and direction + RVec3 ws_origin = body_transform * settings->mPosition; + Vec3 ws_direction = body_transform.Multiply3x3(settings->mSuspensionDirection); + + // Test if we need to update this wheel + if (num_steps_between_collisions == 0 + || (mCurrentStep + wheel_index) % num_steps_between_collisions != 0) + { + // Simplified wheel contact test + if (!w->mContactBodyID.IsInvalid()) + { + // Test if the body is still valid + w->mContactBody = inPhysicsSystem.GetBodyLockInterfaceNoLock().TryGetBody(w->mContactBodyID); + if (w->mContactBody == nullptr) + { + // It's not, forget the contact + w->mContactBodyID = BodyID(); + w->mContactSubShapeID = SubShapeID(); + w->mSuspensionLength = settings->mSuspensionMaxLength; + } + else + { + // Extrapolate the wheel contact properties + mVehicleCollisionTester->PredictContactProperties(inPhysicsSystem, *this, wheel_index, ws_origin, ws_direction, mBody->GetID(), w->mContactBody, w->mContactSubShapeID, w->mContactPosition, w->mContactNormal, w->mSuspensionLength); + } + } + } + else + { + // Full wheel contact test, start by resetting the contact data + w->mContactBodyID = BodyID(); + w->mContactBody = nullptr; + w->mContactSubShapeID = SubShapeID(); + w->mSuspensionLength = settings->mSuspensionMaxLength; + + // Test collision to find the floor + if (mVehicleCollisionTester->Collide(inPhysicsSystem, *this, wheel_index, ws_origin, ws_direction, mBody->GetID(), w->mContactBody, w->mContactSubShapeID, w->mContactPosition, w->mContactNormal, w->mSuspensionLength)) + { + // Store ID (pointer is not valid outside of the simulation step) + w->mContactBodyID = w->mContactBody->GetID(); + } + } + + if (w->mContactBody != nullptr) + { + // Store contact velocity, cache this as the contact body may be removed + w->mContactPointVelocity = w->mContactBody->GetPointVelocity(w->mContactPosition); + + // Determine plane constant for axle contact plane + w->mAxlePlaneConstant = RVec3(w->mContactNormal).Dot(ws_origin + w->mSuspensionLength * ws_direction); + + // Check if body is active, if so the entire vehicle should be active + mIsActive |= w->mContactBody->IsActive(); + + // Determine world space forward using steering angle and body rotation + Vec3 forward, up, right; + GetWheelLocalBasis(w, forward, up, right); + forward = body_transform.Multiply3x3(forward); + right = body_transform.Multiply3x3(right); + + // The longitudinal axis is in the up/forward plane + w->mContactLongitudinal = w->mContactNormal.Cross(right); + + // Make sure that the longitudinal axis is aligned with the forward axis + if (w->mContactLongitudinal.Dot(forward) < 0.0f) + w->mContactLongitudinal = -w->mContactLongitudinal; + + // Normalize it + w->mContactLongitudinal = w->mContactLongitudinal.NormalizedOr(w->mContactNormal.GetNormalizedPerpendicular()); + + // The lateral axis is perpendicular to contact normal and longitudinal axis + w->mContactLateral = w->mContactLongitudinal.Cross(w->mContactNormal).Normalized(); + } + } + + // Callback to higher-level systems. We do it immediately after wheel collision. + if (mPostCollideCallback != nullptr) + mPostCollideCallback(*this, inDeltaTime, inPhysicsSystem); + + // Calculate anti-rollbar impulses + for (const VehicleAntiRollBar &r : mAntiRollBars) + { + Wheel *lw = mWheels[r.mLeftWheel]; + Wheel *rw = mWheels[r.mRightWheel]; + + if (lw->mContactBody != nullptr && rw->mContactBody != nullptr) + { + // Calculate the impulse to apply based on the difference in suspension length + float difference = rw->mSuspensionLength - lw->mSuspensionLength; + float impulse = difference * r.mStiffness * inDeltaTime; + lw->mAntiRollBarImpulse = -impulse; + rw->mAntiRollBarImpulse = impulse; + } + else + { + // When one of the wheels is not on the ground we don't apply any impulses + lw->mAntiRollBarImpulse = rw->mAntiRollBarImpulse = 0.0f; + } + } + + // Callback on our controller + mController->PostCollide(inDeltaTime, inPhysicsSystem); + + // Callback to higher-level systems. We do it before the sleep section, in case velocities change. + if (mPostStepCallback != nullptr) + mPostStepCallback(*this, inDeltaTime, inPhysicsSystem); + + // If the wheels are rotating, we don't want to go to sleep yet + bool allow_sleep = mController->AllowSleep(); + if (allow_sleep) + for (const Wheel *w : mWheels) + if (abs(w->mAngularVelocity) > DegreesToRadians(10.0f)) + { + allow_sleep = false; + break; + } + if (mBody->GetAllowSleeping() != allow_sleep) + mBody->SetAllowSleeping(allow_sleep); + + // Increment step counter + ++mCurrentStep; +} + +void VehicleConstraint::BuildIslands(uint32 inConstraintIndex, IslandBuilder &ioBuilder, BodyManager &inBodyManager) +{ + // Find dynamic bodies that our wheels are touching + BodyID *body_ids = (BodyID *)JPH_STACK_ALLOC((mWheels.size() + 1) * sizeof(BodyID)); + int num_bodies = 0; + bool needs_to_activate = false; + for (const Wheel *w : mWheels) + if (w->mContactBody != nullptr) + { + // Avoid adding duplicates + bool duplicate = false; + BodyID id = w->mContactBody->GetID(); + for (int i = 0; i < num_bodies; ++i) + if (body_ids[i] == id) + { + duplicate = true; + break; + } + if (duplicate) + continue; + + if (w->mContactBody->IsDynamic()) + { + body_ids[num_bodies++] = id; + needs_to_activate |= !w->mContactBody->IsActive(); + } + } + + // Activate bodies, note that if we get here we have already told the system that we're active so that means our main body needs to be active too + if (!mBody->IsActive()) + { + // Our main body is not active, activate it too + body_ids[num_bodies] = mBody->GetID(); + inBodyManager.ActivateBodies(body_ids, num_bodies + 1); + } + else if (needs_to_activate) + { + // Only activate bodies the wheels are touching + inBodyManager.ActivateBodies(body_ids, num_bodies); + } + + // Link the bodies into the same island + uint32 min_active_index = Body::cInactiveIndex; + for (int i = 0; i < num_bodies; ++i) + { + const Body &body = inBodyManager.GetBody(body_ids[i]); + min_active_index = min(min_active_index, body.GetIndexInActiveBodiesInternal()); + ioBuilder.LinkBodies(mBody->GetIndexInActiveBodiesInternal(), body.GetIndexInActiveBodiesInternal()); + } + + // Link the constraint in the island + ioBuilder.LinkConstraint(inConstraintIndex, mBody->GetIndexInActiveBodiesInternal(), min_active_index); +} + +uint VehicleConstraint::BuildIslandSplits(LargeIslandSplitter &ioSplitter) const +{ + return ioSplitter.AssignToNonParallelSplit(mBody); +} + +void VehicleConstraint::CalculateSuspensionForcePoint(const Wheel &inWheel, Vec3 &outR1PlusU, Vec3 &outR2) const +{ + // Determine point to apply force to + RVec3 force_point; + if (inWheel.mSettings->mEnableSuspensionForcePoint) + force_point = mBody->GetWorldTransform() * inWheel.mSettings->mSuspensionForcePoint; + else + force_point = inWheel.mContactPosition; + + // Calculate r1 + u and r2 + outR1PlusU = Vec3(force_point - mBody->GetCenterOfMassPosition()); + outR2 = Vec3(force_point - inWheel.mContactBody->GetCenterOfMassPosition()); +} + +void VehicleConstraint::CalculatePitchRollConstraintProperties(RMat44Arg inBodyTransform) +{ + // Check if a limit was specified + if (mCosMaxPitchRollAngle > -1.0f) + { + // Calculate cos of angle between world up vector and vehicle up vector + Vec3 vehicle_up = inBodyTransform.Multiply3x3(mUp); + mCosPitchRollAngle = mWorldUp.Dot(vehicle_up); + if (mCosPitchRollAngle < mCosMaxPitchRollAngle) + { + // Calculate rotation axis to rotate vehicle towards up + Vec3 rotation_axis = mWorldUp.Cross(vehicle_up); + float len = rotation_axis.Length(); + if (len > 0.0f) + mPitchRollRotationAxis = rotation_axis / len; + + mPitchRollPart.CalculateConstraintProperties(*mBody, Body::sFixedToWorld, mPitchRollRotationAxis); + } + else + mPitchRollPart.Deactivate(); + } + else + mPitchRollPart.Deactivate(); +} + +void VehicleConstraint::SetupVelocityConstraint(float inDeltaTime) +{ + RMat44 body_transform = mBody->GetWorldTransform(); + + for (Wheel *w : mWheels) + if (w->mContactBody != nullptr) + { + const WheelSettings *settings = w->mSettings; + + Vec3 neg_contact_normal = -w->mContactNormal; + + Vec3 r1_plus_u, r2; + CalculateSuspensionForcePoint(*w, r1_plus_u, r2); + + // Suspension spring + if (settings->mSuspensionMaxLength > settings->mSuspensionMinLength) + { + float stiffness, damping; + if (settings->mSuspensionSpring.mMode == ESpringMode::FrequencyAndDamping) + { + // Calculate effective mass based on vehicle configuration (the stiffness of the spring should not be affected by the dynamics of the vehicle): K = 1 / (J M^-1 J^T) + // Note that if no suspension force point is supplied we don't know where the force is applied so we assume it is applied at average suspension length + Vec3 force_point = settings->mEnableSuspensionForcePoint? settings->mSuspensionForcePoint : settings->mPosition + 0.5f * (settings->mSuspensionMinLength + settings->mSuspensionMaxLength) * settings->mSuspensionDirection; + Vec3 force_point_x_neg_up = force_point.Cross(-mUp); + const MotionProperties *mp = mBody->GetMotionProperties(); + float effective_mass = 1.0f / (mp->GetInverseMass() + force_point_x_neg_up.Dot(mp->GetLocalSpaceInverseInertia().Multiply3x3(force_point_x_neg_up))); + + // Convert frequency and damping to stiffness and damping + float omega = 2.0f * JPH_PI * settings->mSuspensionSpring.mFrequency; + stiffness = effective_mass * Square(omega); + damping = 2.0f * effective_mass * settings->mSuspensionSpring.mDamping * omega; + } + else + { + // In this case we can simply copy the properties + stiffness = settings->mSuspensionSpring.mStiffness; + damping = settings->mSuspensionSpring.mDamping; + } + + // Calculate the damping and frequency of the suspension spring given the angle between the suspension direction and the contact normal + // If the angle between the suspension direction and the inverse of the contact normal is alpha then the force on the spring relates to the force along the contact normal as: + // + // Fspring = Fnormal * cos(alpha) + // + // The spring force is: + // + // Fspring = -k * x + // + // where k is the spring constant and x is the displacement of the spring. So we have: + // + // Fnormal * cos(alpha) = -k * x <=> Fnormal = -k / cos(alpha) * x + // + // So we can see this as a spring with spring constant: + // + // k' = k / cos(alpha) + // + // In the same way the velocity relates like: + // + // Vspring = Vnormal * cos(alpha) + // + // Which results in the modified damping constant c: + // + // c' = c / cos(alpha) + // + // Note that we clamp 1 / cos(alpha) to the range [0.1, 1] in order not to increase the stiffness / damping by too much. + Vec3 ws_direction = body_transform.Multiply3x3(settings->mSuspensionDirection); + float cos_angle = max(0.1f, ws_direction.Dot(neg_contact_normal)); + stiffness /= cos_angle; + damping /= cos_angle; + + // Get the value of the constraint equation + float c = w->mSuspensionLength - settings->mSuspensionMaxLength - settings->mSuspensionPreloadLength; + + w->mSuspensionPart.CalculateConstraintPropertiesWithStiffnessAndDamping(inDeltaTime, *mBody, r1_plus_u, *w->mContactBody, r2, neg_contact_normal, w->mAntiRollBarImpulse, c, stiffness, damping); + } + else + w->mSuspensionPart.Deactivate(); + + // Check if we reached the 'max up' position and if so add a hard velocity constraint that stops any further movement in the normal direction + if (w->mSuspensionLength < settings->mSuspensionMinLength) + w->mSuspensionMaxUpPart.CalculateConstraintProperties(*mBody, r1_plus_u, *w->mContactBody, r2, neg_contact_normal); + else + w->mSuspensionMaxUpPart.Deactivate(); + + // Friction and propulsion + w->mLongitudinalPart.CalculateConstraintProperties(*mBody, r1_plus_u, *w->mContactBody, r2, -w->mContactLongitudinal); + w->mLateralPart.CalculateConstraintProperties(*mBody, r1_plus_u, *w->mContactBody, r2, -w->mContactLateral); + } + else + { + // No contact -> disable everything + w->mSuspensionPart.Deactivate(); + w->mSuspensionMaxUpPart.Deactivate(); + w->mLongitudinalPart.Deactivate(); + w->mLateralPart.Deactivate(); + } + + CalculatePitchRollConstraintProperties(body_transform); +} + +void VehicleConstraint::ResetWarmStart() +{ + for (Wheel *w : mWheels) + { + w->mSuspensionPart.Deactivate(); + w->mSuspensionMaxUpPart.Deactivate(); + w->mLongitudinalPart.Deactivate(); + w->mLateralPart.Deactivate(); + } + + mPitchRollPart.Deactivate(); +} + +void VehicleConstraint::WarmStartVelocityConstraint(float inWarmStartImpulseRatio) +{ + for (Wheel *w : mWheels) + if (w->mContactBody != nullptr) + { + Vec3 neg_contact_normal = -w->mContactNormal; + + w->mSuspensionPart.WarmStart(*mBody, *w->mContactBody, neg_contact_normal, inWarmStartImpulseRatio); + w->mSuspensionMaxUpPart.WarmStart(*mBody, *w->mContactBody, neg_contact_normal, inWarmStartImpulseRatio); + w->mLongitudinalPart.WarmStart(*mBody, *w->mContactBody, -w->mContactLongitudinal, 0.0f); // Don't warm start the longitudinal part (the engine/brake force, we don't want to preserve anything from the last frame) + w->mLateralPart.WarmStart(*mBody, *w->mContactBody, -w->mContactLateral, inWarmStartImpulseRatio); + } + + mPitchRollPart.WarmStart(*mBody, Body::sFixedToWorld, inWarmStartImpulseRatio); +} + +bool VehicleConstraint::SolveVelocityConstraint(float inDeltaTime) +{ + bool impulse = false; + + // Solve suspension + for (Wheel *w : mWheels) + if (w->mContactBody != nullptr) + { + Vec3 neg_contact_normal = -w->mContactNormal; + + // Suspension spring, note that it can only push and not pull + if (w->mSuspensionPart.IsActive()) + impulse |= w->mSuspensionPart.SolveVelocityConstraint(*mBody, *w->mContactBody, neg_contact_normal, 0.0f, FLT_MAX); + + // When reaching the minimal suspension length only allow forces pushing the bodies away + if (w->mSuspensionMaxUpPart.IsActive()) + impulse |= w->mSuspensionMaxUpPart.SolveVelocityConstraint(*mBody, *w->mContactBody, neg_contact_normal, 0.0f, FLT_MAX); + } + + // Solve the horizontal movement of the vehicle + impulse |= mController->SolveLongitudinalAndLateralConstraints(inDeltaTime); + + // Apply the pitch / roll constraint to avoid the vehicle from toppling over + if (mPitchRollPart.IsActive()) + impulse |= mPitchRollPart.SolveVelocityConstraint(*mBody, Body::sFixedToWorld, mPitchRollRotationAxis, 0, FLT_MAX); + + return impulse; +} + +bool VehicleConstraint::SolvePositionConstraint(float inDeltaTime, float inBaumgarte) +{ + bool impulse = false; + + RMat44 body_transform = mBody->GetWorldTransform(); + + for (Wheel *w : mWheels) + if (w->mContactBody != nullptr) + { + const WheelSettings *settings = w->mSettings; + + // Check if we reached the 'max up' position now that the body has possibly moved + // We do this by calculating the axle position at minimum suspension length and making sure it does not go through the + // plane defined by the contact normal and the axle position when the contact happened + // TODO: This assumes that only the vehicle moved and not the ground as we kept the axle contact plane in world space + Vec3 ws_direction = body_transform.Multiply3x3(settings->mSuspensionDirection); + RVec3 ws_position = body_transform * settings->mPosition; + RVec3 min_suspension_pos = ws_position + settings->mSuspensionMinLength * ws_direction; + float max_up_error = float(RVec3(w->mContactNormal).Dot(min_suspension_pos) - w->mAxlePlaneConstant); + if (max_up_error < 0.0f) + { + Vec3 neg_contact_normal = -w->mContactNormal; + + // Recalculate constraint properties since the body may have moved + Vec3 r1_plus_u, r2; + CalculateSuspensionForcePoint(*w, r1_plus_u, r2); + w->mSuspensionMaxUpPart.CalculateConstraintProperties(*mBody, r1_plus_u, *w->mContactBody, r2, neg_contact_normal); + + impulse |= w->mSuspensionMaxUpPart.SolvePositionConstraint(*mBody, *w->mContactBody, neg_contact_normal, max_up_error, inBaumgarte); + } + } + + // Apply the pitch / roll constraint to avoid the vehicle from toppling over + CalculatePitchRollConstraintProperties(body_transform); + if (mPitchRollPart.IsActive()) + impulse |= mPitchRollPart.SolvePositionConstraint(*mBody, Body::sFixedToWorld, mCosPitchRollAngle - mCosMaxPitchRollAngle, inBaumgarte); + + return impulse; +} + +#ifdef JPH_DEBUG_RENDERER + +void VehicleConstraint::DrawConstraint(DebugRenderer *inRenderer) const +{ + mController->Draw(inRenderer); +} + +void VehicleConstraint::DrawConstraintLimits(DebugRenderer *inRenderer) const +{ +} + +#endif // JPH_DEBUG_RENDERER + +void VehicleConstraint::SaveState(StateRecorder &inStream) const +{ + Constraint::SaveState(inStream); + + mController->SaveState(inStream); + + for (const Wheel *w : mWheels) + { + inStream.Write(w->mAngularVelocity); + inStream.Write(w->mAngle); + inStream.Write(w->mContactBodyID); // Used by MotorcycleController::PreCollide + inStream.Write(w->mContactPosition); // Used by VehicleCollisionTester::PredictContactProperties + inStream.Write(w->mContactNormal); // Used by MotorcycleController::PreCollide + inStream.Write(w->mContactLateral); // Used by MotorcycleController::PreCollide + inStream.Write(w->mSuspensionLength); // Used by VehicleCollisionTester::PredictContactProperties + + w->mSuspensionPart.SaveState(inStream); + w->mSuspensionMaxUpPart.SaveState(inStream); + w->mLongitudinalPart.SaveState(inStream); + w->mLateralPart.SaveState(inStream); + } + + inStream.Write(mPitchRollRotationAxis); // When rotation is too small we use last frame so we need to store it + mPitchRollPart.SaveState(inStream); + inStream.Write(mCurrentStep); +} + +void VehicleConstraint::RestoreState(StateRecorder &inStream) +{ + Constraint::RestoreState(inStream); + + mController->RestoreState(inStream); + + for (Wheel *w : mWheels) + { + inStream.Read(w->mAngularVelocity); + inStream.Read(w->mAngle); + inStream.Read(w->mContactBodyID); + inStream.Read(w->mContactPosition); + inStream.Read(w->mContactNormal); + inStream.Read(w->mContactLateral); + inStream.Read(w->mSuspensionLength); + w->mContactBody = nullptr; // No longer valid + + w->mSuspensionPart.RestoreState(inStream); + w->mSuspensionMaxUpPart.RestoreState(inStream); + w->mLongitudinalPart.RestoreState(inStream); + w->mLateralPart.RestoreState(inStream); + } + + inStream.Read(mPitchRollRotationAxis); + mPitchRollPart.RestoreState(inStream); + inStream.Read(mCurrentStep); +} + +Ref VehicleConstraint::GetConstraintSettings() const +{ + JPH_ASSERT(false); // Not implemented yet + return nullptr; +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleConstraint.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleConstraint.h new file mode 100644 index 00000000000..e855b12a07d --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleConstraint.h @@ -0,0 +1,236 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +class PhysicsSystem; + +/// Configuration for constraint that simulates a wheeled vehicle. +/// +/// The properties in this constraint are largely based on "Car Physics for Games" by Marco Monster. +/// See: https://www.asawicki.info/Mirror/Car%20Physics%20for%20Games/Car%20Physics%20for%20Games.html +class JPH_EXPORT VehicleConstraintSettings : public ConstraintSettings +{ +public: + JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, VehicleConstraintSettings) + + /// Saves the contents of the constraint settings in binary form to inStream. + virtual void SaveBinaryState(StreamOut &inStream) const override; + + Vec3 mUp { 0, 1, 0 }; ///< Vector indicating the up direction of the vehicle (in local space to the body) + Vec3 mForward { 0, 0, 1 }; ///< Vector indicating forward direction of the vehicle (in local space to the body) + float mMaxPitchRollAngle = JPH_PI; ///< Defines the maximum pitch/roll angle (rad), can be used to avoid the car from getting upside down. The vehicle up direction will stay within a cone centered around the up axis with half top angle mMaxPitchRollAngle, set to pi to turn off. + Array> mWheels; ///< List of wheels and their properties + Array mAntiRollBars; ///< List of anti rollbars and their properties + Ref mController; ///< Defines how the vehicle can accelerate / decelerate + +protected: + /// This function should not be called directly, it is used by sRestoreFromBinaryState. + virtual void RestoreBinaryState(StreamIn &inStream) override; +}; + +/// Constraint that simulates a vehicle +/// Note: Don't forget to register the constraint as a StepListener with the PhysicsSystem! +/// +/// When the vehicle drives over very light objects (rubble) you may see the car body dip down. This is a known issue and is an artifact of the iterative solver that Jolt is using. +/// Basically if a light object is sandwiched between two heavy objects (the static floor and the car body), the light object is not able to transfer enough force from the ground to +/// the car body to keep the car body up. You can see this effect in the HeavyOnLightTest sample, the boxes on the right have a lot of penetration because they're on top of light objects. +/// +/// There are a couple of ways to improve this: +/// +/// 1. You can increase the number of velocity steps (global settings PhysicsSettings::mNumVelocitySteps or if you only want to increase it on +/// the vehicle you can use VehicleConstraintSettings::mNumVelocityStepsOverride). E.g. going from 10 to 30 steps in the HeavyOnLightTest sample makes the penetration a lot less. +/// The number of position steps can also be increased (the first prevents the body from going down, the second corrects it if the problem did +/// occur which inevitably happens due to numerical drift). This solution costs CPU cycles. +/// +/// 2. You can reduce the mass difference between the vehicle body and the rubble on the floor (by making the rubble heavier or the car lighter). +/// +/// 3. You could filter out collisions between the vehicle collision test and the rubble completely. This would make the wheels ignore the rubble but would cause the vehicle to drive +/// through it as if nothing happened. You could create fake wheels (keyframed bodies) that move along with the vehicle and that only collide with rubble (and not the vehicle or the ground). +/// This would cause the vehicle to push away the rubble without the rubble being able to affect the vehicle (unless it hits the main body of course). +/// +/// Note that when driving over rubble, you may see the wheel jump up and down quite quickly because one frame a collision is found and the next frame not. +/// To alleviate this, it may be needed to smooth the motion of the visual mesh for the wheel. +class JPH_EXPORT VehicleConstraint : public Constraint, public PhysicsStepListener +{ +public: + /// Constructor / destructor + VehicleConstraint(Body &inVehicleBody, const VehicleConstraintSettings &inSettings); + virtual ~VehicleConstraint() override; + + /// Get the type of a constraint + virtual EConstraintSubType GetSubType() const override { return EConstraintSubType::Vehicle; } + + /// Defines the maximum pitch/roll angle (rad), can be used to avoid the car from getting upside down. The vehicle up direction will stay within a cone centered around the up axis with half top angle mMaxPitchRollAngle, set to pi to turn off. + void SetMaxPitchRollAngle(float inMaxPitchRollAngle) { mCosMaxPitchRollAngle = Cos(inMaxPitchRollAngle); } + + /// Set the interface that tests collision between wheel and ground + void SetVehicleCollisionTester(const VehicleCollisionTester *inTester) { mVehicleCollisionTester = inTester; } + + /// Callback function to combine the friction of a tire with the friction of the body it is colliding with. + /// On input ioLongitudinalFriction and ioLateralFriction contain the friction of the tire, on output they should contain the combined friction with inBody2. + using CombineFunction = function; + + /// Set the function that combines the friction of two bodies and returns it + /// Default method is the geometric mean: sqrt(friction1 * friction2). + void SetCombineFriction(const CombineFunction &inCombineFriction) { mCombineFriction = inCombineFriction; } + const CombineFunction & GetCombineFriction() const { return mCombineFriction; } + + /// Callback function to notify of current stage in PhysicsStepListener::OnStep. + using StepCallback = function; + + /// Callback function to notify that PhysicsStepListener::OnStep has started for this vehicle. Default is to do nothing. + /// Can be used to allow higher-level code to e.g. control steering. This is the last moment that the position/orientation of the vehicle can be changed. + /// Wheel collision checks have not been performed yet. + const StepCallback & GetPreStepCallback() const { return mPreStepCallback; } + void SetPreStepCallback(const StepCallback &inPreStepCallback) { mPreStepCallback = inPreStepCallback; } + + /// Callback function to notify that PhysicsStepListener::OnStep has just completed wheel collision checks. Default is to do nothing. + /// Can be used to allow higher-level code to e.g. detect tire contact or to modify the velocity of the vehicle based on the wheel contacts. + /// You should not change the position of the vehicle in this callback as the wheel collision checks have already been performed. + const StepCallback & GetPostCollideCallback() const { return mPostCollideCallback; } + void SetPostCollideCallback(const StepCallback &inPostCollideCallback) { mPostCollideCallback = inPostCollideCallback; } + + /// Callback function to notify that PhysicsStepListener::OnStep has completed for this vehicle. Default is to do nothing. + /// Can be used to allow higher-level code to e.g. control the vehicle in the air. + /// You should not change the position of the vehicle in this callback as the wheel collision checks have already been performed. + const StepCallback & GetPostStepCallback() const { return mPostStepCallback; } + void SetPostStepCallback(const StepCallback &inPostStepCallback) { mPostStepCallback = inPostStepCallback; } + + /// Get the local space forward vector of the vehicle + Vec3 GetLocalForward() const { return mForward; } + + /// Get the local space up vector of the vehicle + Vec3 GetLocalUp() const { return mUp; } + + /// Vector indicating the world space up direction (used to limit vehicle pitch/roll), calculated every frame by inverting gravity + Vec3 GetWorldUp() const { return mWorldUp; } + + /// Access to the vehicle body + Body * GetVehicleBody() const { return mBody; } + + /// Access to the vehicle controller interface (determines acceleration / deceleration) + const VehicleController * GetController() const { return mController; } + + /// Access to the vehicle controller interface (determines acceleration / deceleration) + VehicleController * GetController() { return mController; } + + /// Get the state of the wheels + const Wheels & GetWheels() const { return mWheels; } + + /// Get the state of a wheels (writable interface, allows you to make changes to the configuration which will take effect the next time step) + Wheels & GetWheels() { return mWheels; } + + /// Get the state of a wheel + Wheel * GetWheel(uint inIdx) { return mWheels[inIdx]; } + const Wheel * GetWheel(uint inIdx) const { return mWheels[inIdx]; } + + /// Get the basis vectors for the wheel in local space to the vehicle body (note: basis does not rotate when the wheel rotates around its axis) + /// @param inWheel Wheel to fetch basis for + /// @param outForward Forward vector for the wheel + /// @param outUp Up vector for the wheel + /// @param outRight Right vector for the wheel + void GetWheelLocalBasis(const Wheel *inWheel, Vec3 &outForward, Vec3 &outUp, Vec3 &outRight) const; + + /// Get the transform of a wheel in local space to the vehicle body, returns a matrix that transforms a cylinder aligned with the Y axis in body space (not COM space) + /// @param inWheelIndex Index of the wheel to fetch + /// @param inWheelRight Unit vector that indicates right in model space of the wheel (so if you only have 1 wheel model, you probably want to specify the opposite direction for the left and right wheels) + /// @param inWheelUp Unit vector that indicates up in model space of the wheel + Mat44 GetWheelLocalTransform(uint inWheelIndex, Vec3Arg inWheelRight, Vec3Arg inWheelUp) const; + + /// Get the transform of a wheel in world space, returns a matrix that transforms a cylinder aligned with the Y axis in world space + /// @param inWheelIndex Index of the wheel to fetch + /// @param inWheelRight Unit vector that indicates right in model space of the wheel (so if you only have 1 wheel model, you probably want to specify the opposite direction for the left and right wheels) + /// @param inWheelUp Unit vector that indicates up in model space of the wheel + RMat44 GetWheelWorldTransform(uint inWheelIndex, Vec3Arg inWheelRight, Vec3Arg inWheelUp) const; + + /// Number of simulation steps between wheel collision tests when the vehicle is active. Default is 1. 0 = never, 1 = every step, 2 = every other step, etc. + /// Note that if a vehicle has multiple wheels and the number of steps > 1, the wheels will be tested in a round robin fashion. + /// If there are multiple vehicles, the tests will be spread out based on the BodyID of the vehicle. + /// If you set this to test less than every step, you may see simulation artifacts. This setting can be used to reduce the cost of simulating vehicles in the distance. + void SetNumStepsBetweenCollisionTestActive(uint inSteps) { mNumStepsBetweenCollisionTestActive = inSteps; } + uint GetNumStepsBetweenCollisionTestActive() const { return mNumStepsBetweenCollisionTestActive; } + + /// Number of simulation steps between wheel collision tests when the vehicle is inactive. Default is 1. 0 = never, 1 = every step, 2 = every other step, etc. + /// Note that if a vehicle has multiple wheels and the number of steps > 1, the wheels will be tested in a round robin fashion. + /// If there are multiple vehicles, the tests will be spread out based on the BodyID of the vehicle. + /// This number can be lower than the number of steps when the vehicle is active as the only purpose of this test is + /// to allow the vehicle to wake up in response to bodies moving into the wheels but not touching the body of the vehicle. + void SetNumStepsBetweenCollisionTestInactive(uint inSteps) { mNumStepsBetweenCollisionTestInactive = inSteps; } + uint GetNumStepsBetweenCollisionTestInactive() const { return mNumStepsBetweenCollisionTestInactive; } + + // Generic interface of a constraint + virtual bool IsActive() const override { return mIsActive && Constraint::IsActive(); } + virtual void NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM) override { /* Do nothing */ } + virtual void SetupVelocityConstraint(float inDeltaTime) override; + virtual void ResetWarmStart() override; + virtual void WarmStartVelocityConstraint(float inWarmStartImpulseRatio) override; + virtual bool SolveVelocityConstraint(float inDeltaTime) override; + virtual bool SolvePositionConstraint(float inDeltaTime, float inBaumgarte) override; + virtual void BuildIslands(uint32 inConstraintIndex, IslandBuilder &ioBuilder, BodyManager &inBodyManager) override; + virtual uint BuildIslandSplits(LargeIslandSplitter &ioSplitter) const override; +#ifdef JPH_DEBUG_RENDERER + virtual void DrawConstraint(DebugRenderer *inRenderer) const override; + virtual void DrawConstraintLimits(DebugRenderer *inRenderer) const override; +#endif // JPH_DEBUG_RENDERER + virtual void SaveState(StateRecorder &inStream) const override; + virtual void RestoreState(StateRecorder &inStream) override; + virtual Ref GetConstraintSettings() const override; + +private: + // See: PhysicsStepListener + virtual void OnStep(float inDeltaTime, PhysicsSystem &inPhysicsSystem) override; + + // Calculate the position where the suspension and traction forces should be applied in world space, relative to the center of mass of both bodies + void CalculateSuspensionForcePoint(const Wheel &inWheel, Vec3 &outR1PlusU, Vec3 &outR2) const; + + // Calculate the constraint properties for mPitchRollPart + void CalculatePitchRollConstraintProperties(RMat44Arg inBodyTransform); + + // Simulation information + Body * mBody; ///< Body of the vehicle + Vec3 mForward; ///< Local space forward vector for the vehicle + Vec3 mUp; ///< Local space up vector for the vehicle + Vec3 mWorldUp; ///< Vector indicating the world space up direction (used to limit vehicle pitch/roll) + Wheels mWheels; ///< Wheel states of the vehicle + Array mAntiRollBars; ///< Anti rollbars of the vehicle + VehicleController * mController; ///< Controls the acceleration / deceleration of the vehicle + bool mIsActive = false; ///< If this constraint is active + uint mNumStepsBetweenCollisionTestActive = 1; ///< Number of simulation steps between wheel collision tests when the vehicle is active + uint mNumStepsBetweenCollisionTestInactive = 1; ///< Number of simulation steps between wheel collision tests when the vehicle is inactive + uint mCurrentStep = 0; ///< Current step number, used to determine when to test a wheel + + // Prevent vehicle from toppling over + float mCosMaxPitchRollAngle; ///< Cos of the max pitch/roll angle + float mCosPitchRollAngle; ///< Cos of the current pitch/roll angle + Vec3 mPitchRollRotationAxis { 0, 1, 0 }; ///< Current axis along which to apply torque to prevent the car from toppling over + AngleConstraintPart mPitchRollPart; ///< Constraint part that prevents the car from toppling over + + // Interfaces + RefConst mVehicleCollisionTester; ///< Class that performs testing of collision for the wheels + CombineFunction mCombineFriction = [](uint, float &ioLongitudinalFriction, float &ioLateralFriction, const Body &inBody2, const SubShapeID &) + { + float body_friction = inBody2.GetFriction(); + + ioLongitudinalFriction = sqrt(ioLongitudinalFriction * body_friction); + ioLateralFriction = sqrt(ioLateralFriction * body_friction); + }; + + // Callbacks + StepCallback mPreStepCallback; + StepCallback mPostCollideCallback; + StepCallback mPostStepCallback; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleController.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleController.cpp new file mode 100644 index 00000000000..ffa8ff1a64b --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleController.cpp @@ -0,0 +1,17 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include + +JPH_NAMESPACE_BEGIN + +JPH_IMPLEMENT_SERIALIZABLE_ABSTRACT(VehicleControllerSettings) +{ + JPH_ADD_BASE_CLASS(VehicleControllerSettings, SerializableObject) +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleController.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleController.h new file mode 100644 index 00000000000..b916a8bfa0a --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleController.h @@ -0,0 +1,80 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include +#ifdef JPH_DEBUG_RENDERER + #include +#endif // JPH_DEBUG_RENDERER + +JPH_NAMESPACE_BEGIN + +class PhysicsSystem; +class VehicleController; +class VehicleConstraint; +class WheelSettings; +class Wheel; +class StateRecorder; + +/// Basic settings object for interface that controls acceleration / deceleration of the vehicle +class JPH_EXPORT VehicleControllerSettings : public SerializableObject, public RefTarget +{ +public: + JPH_DECLARE_SERIALIZABLE_ABSTRACT(JPH_EXPORT, VehicleControllerSettings) + + /// Saves the contents of the controller settings in binary form to inStream. + virtual void SaveBinaryState(StreamOut &inStream) const = 0; + + /// Restore the contents of the controller settings in binary form from inStream. + virtual void RestoreBinaryState(StreamIn &inStream) = 0; + + /// Create an instance of the vehicle controller class + virtual VehicleController * ConstructController(VehicleConstraint &inConstraint) const = 0; +}; + +/// Runtime data for interface that controls acceleration / deceleration of the vehicle +class JPH_EXPORT VehicleController : public RefTarget, public NonCopyable +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Constructor / destructor + explicit VehicleController(VehicleConstraint &inConstraint) : mConstraint(inConstraint) { } + virtual ~VehicleController() = default; + +protected: + // The functions below are only for the VehicleConstraint + friend class VehicleConstraint; + + // Create a new instance of wheel + virtual Wheel * ConstructWheel(const WheelSettings &inWheel) const = 0; + + // If the vehicle is allowed to go to sleep + virtual bool AllowSleep() const = 0; + + // Called before the wheel probes have been done + virtual void PreCollide(float inDeltaTime, PhysicsSystem &inPhysicsSystem) = 0; + + // Called after the wheel probes have been done + virtual void PostCollide(float inDeltaTime, PhysicsSystem &inPhysicsSystem) = 0; + + // Solve longitudinal and lateral constraint parts for all of the wheels + virtual bool SolveLongitudinalAndLateralConstraints(float inDeltaTime) = 0; + + // Saving state for replay + virtual void SaveState(StateRecorder &inStream) const = 0; + virtual void RestoreState(StateRecorder &inStream) = 0; + +#ifdef JPH_DEBUG_RENDERER + // Drawing interface + virtual void Draw(DebugRenderer *inRenderer) const = 0; +#endif // JPH_DEBUG_RENDERER + + VehicleConstraint & mConstraint; ///< The vehicle constraint we belong to +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleDifferential.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleDifferential.cpp new file mode 100644 index 00000000000..ef7cf4cb914 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleDifferential.cpp @@ -0,0 +1,81 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include + +JPH_NAMESPACE_BEGIN + +JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(VehicleDifferentialSettings) +{ + JPH_ADD_ATTRIBUTE(VehicleDifferentialSettings, mLeftWheel) + JPH_ADD_ATTRIBUTE(VehicleDifferentialSettings, mRightWheel) + JPH_ADD_ATTRIBUTE(VehicleDifferentialSettings, mDifferentialRatio) + JPH_ADD_ATTRIBUTE(VehicleDifferentialSettings, mLeftRightSplit) + JPH_ADD_ATTRIBUTE(VehicleDifferentialSettings, mLimitedSlipRatio) + JPH_ADD_ATTRIBUTE(VehicleDifferentialSettings, mEngineTorqueRatio) +} + +void VehicleDifferentialSettings::SaveBinaryState(StreamOut &inStream) const +{ + inStream.Write(mLeftWheel); + inStream.Write(mRightWheel); + inStream.Write(mDifferentialRatio); + inStream.Write(mLeftRightSplit); + inStream.Write(mLimitedSlipRatio); + inStream.Write(mEngineTorqueRatio); +} + +void VehicleDifferentialSettings::RestoreBinaryState(StreamIn &inStream) +{ + inStream.Read(mLeftWheel); + inStream.Read(mRightWheel); + inStream.Read(mDifferentialRatio); + inStream.Read(mLeftRightSplit); + inStream.Read(mLimitedSlipRatio); + inStream.Read(mEngineTorqueRatio); +} + +void VehicleDifferentialSettings::CalculateTorqueRatio(float inLeftAngularVelocity, float inRightAngularVelocity, float &outLeftTorqueFraction, float &outRightTorqueFraction) const +{ + // Start with the default torque ratio + outLeftTorqueFraction = 1.0f - mLeftRightSplit; + outRightTorqueFraction = mLeftRightSplit; + + if (mLimitedSlipRatio < FLT_MAX) + { + JPH_ASSERT(mLimitedSlipRatio > 1.0f); + + // This is a limited slip differential, adjust torque ratios according to wheel speeds + float omega_l = max(1.0e-3f, abs(inLeftAngularVelocity)); // prevent div by zero by setting a minimum velocity and ignoring that the wheels may be rotating in different directions + float omega_r = max(1.0e-3f, abs(inRightAngularVelocity)); + float omega_min = min(omega_l, omega_r); + float omega_max = max(omega_l, omega_r); + + // Map into a value that is 0 when the wheels are turning at an equal rate and 1 when the wheels are turning at mLimitedSlipRotationRatio + float alpha = min((omega_max / omega_min - 1.0f) / (mLimitedSlipRatio - 1.0f), 1.0f); + JPH_ASSERT(alpha >= 0.0f); + float one_min_alpha = 1.0f - alpha; + + if (omega_l < omega_r) + { + // Redirect more power to the left wheel + outLeftTorqueFraction = outLeftTorqueFraction * one_min_alpha + alpha; + outRightTorqueFraction = outRightTorqueFraction * one_min_alpha; + } + else + { + // Redirect more power to the right wheel + outLeftTorqueFraction = outLeftTorqueFraction * one_min_alpha; + outRightTorqueFraction = outRightTorqueFraction * one_min_alpha + alpha; + } + } + + // Assert the values add up to 1 + JPH_ASSERT(abs(outLeftTorqueFraction + outRightTorqueFraction - 1.0f) < 1.0e-6f); +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleDifferential.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleDifferential.h new file mode 100644 index 00000000000..34a5011530c --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleDifferential.h @@ -0,0 +1,39 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +class JPH_EXPORT VehicleDifferentialSettings +{ +public: + JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, VehicleDifferentialSettings) + + /// Saves the contents in binary form to inStream. + void SaveBinaryState(StreamOut &inStream) const; + + /// Restores the contents in binary form to inStream. + void RestoreBinaryState(StreamIn &inStream); + + /// Calculate the torque ratio between left and right wheel + /// @param inLeftAngularVelocity Angular velocity of left wheel (rad / s) + /// @param inRightAngularVelocity Angular velocity of right wheel (rad / s) + /// @param outLeftTorqueFraction Fraction of torque that should go to the left wheel + /// @param outRightTorqueFraction Fraction of torque that should go to the right wheel + void CalculateTorqueRatio(float inLeftAngularVelocity, float inRightAngularVelocity, float &outLeftTorqueFraction, float &outRightTorqueFraction) const; + + int mLeftWheel = -1; ///< Index (in mWheels) that represents the left wheel of this differential (can be -1 to indicate no wheel) + int mRightWheel = -1; ///< Index (in mWheels) that represents the right wheel of this differential (can be -1 to indicate no wheel) + float mDifferentialRatio = 3.42f; ///< Ratio between rotation speed of gear box and wheels + float mLeftRightSplit = 0.5f; ///< Defines how the engine torque is split across the left and right wheel (0 = left, 0.5 = center, 1 = right) + float mLimitedSlipRatio = 1.4f; ///< Ratio max / min wheel speed. When this ratio is exceeded, all torque gets distributed to the slowest moving wheel. This allows implementing a limited slip differential. Set to FLT_MAX for an open differential. Value should be > 1. + float mEngineTorqueRatio = 1.0f; ///< How much of the engines torque is applied to this differential (0 = none, 1 = full), make sure the sum of all differentials is 1. +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleEngine.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleEngine.cpp new file mode 100644 index 00000000000..1352cc0d51f --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleEngine.cpp @@ -0,0 +1,122 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#ifdef JPH_DEBUG_RENDERER + #include +#endif // JPH_DEBUG_RENDERER + +JPH_NAMESPACE_BEGIN + +JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(VehicleEngineSettings) +{ + JPH_ADD_ATTRIBUTE(VehicleEngineSettings, mMaxTorque) + JPH_ADD_ATTRIBUTE(VehicleEngineSettings, mMinRPM) + JPH_ADD_ATTRIBUTE(VehicleEngineSettings, mMaxRPM) + JPH_ADD_ATTRIBUTE(VehicleEngineSettings, mNormalizedTorque) +} + +VehicleEngineSettings::VehicleEngineSettings() +{ + mNormalizedTorque.Reserve(3); + mNormalizedTorque.AddPoint(0.0f, 0.8f); + mNormalizedTorque.AddPoint(0.66f, 1.0f); + mNormalizedTorque.AddPoint(1.0f, 0.8f); +} + +void VehicleEngineSettings::SaveBinaryState(StreamOut &inStream) const +{ + inStream.Write(mMaxTorque); + inStream.Write(mMinRPM); + inStream.Write(mMaxRPM); + mNormalizedTorque.SaveBinaryState(inStream); +} + +void VehicleEngineSettings::RestoreBinaryState(StreamIn &inStream) +{ + inStream.Read(mMaxTorque); + inStream.Read(mMinRPM); + inStream.Read(mMaxRPM); + mNormalizedTorque.RestoreBinaryState(inStream); +} + +void VehicleEngine::ApplyTorque(float inTorque, float inDeltaTime) +{ + // Accelerate engine using torque + mCurrentRPM += cAngularVelocityToRPM * inTorque * inDeltaTime / mInertia; + ClampRPM(); +} + +void VehicleEngine::ApplyDamping(float inDeltaTime) +{ + // Angular damping: dw/dt = -c * w + // Solution: w(t) = w(0) * e^(-c * t) or w2 = w1 * e^(-c * dt) + // Taylor expansion of e^(-c * dt) = 1 - c * dt + ... + // Since dt is usually in the order of 1/60 and c is a low number too this approximation is good enough + mCurrentRPM *= max(0.0f, 1.0f - mAngularDamping * inDeltaTime); + ClampRPM(); +} + +#ifdef JPH_DEBUG_RENDERER + +void VehicleEngine::DrawRPM(DebugRenderer *inRenderer, RVec3Arg inPosition, Vec3Arg inForward, Vec3Arg inUp, float inSize, float inShiftDownRPM, float inShiftUpRPM) const +{ + // Function to draw part of a pie + auto draw_pie = [this, inRenderer, inSize, inPosition, inForward, inUp](float inMinRPM, float inMaxRPM, Color inColor) { + inRenderer->DrawPie(inPosition, inSize, inForward, inUp, ConvertRPMToAngle(inMinRPM), ConvertRPMToAngle(inMaxRPM), inColor, DebugRenderer::ECastShadow::Off); + }; + + // Draw segment under min RPM + draw_pie(0, mMinRPM, Color::sGrey); + + // Draw segment until inShiftDownRPM + if (mCurrentRPM < inShiftDownRPM) + { + draw_pie(mMinRPM, mCurrentRPM, Color::sRed); + draw_pie(mCurrentRPM, inShiftDownRPM, Color::sDarkRed); + } + else + { + draw_pie(mMinRPM, inShiftDownRPM, Color::sRed); + } + + // Draw segment between inShiftDownRPM and inShiftUpRPM + if (mCurrentRPM > inShiftDownRPM && mCurrentRPM < inShiftUpRPM) + { + draw_pie(inShiftDownRPM, mCurrentRPM, Color::sOrange); + draw_pie(mCurrentRPM, inShiftUpRPM, Color::sDarkOrange); + } + else + { + draw_pie(inShiftDownRPM, inShiftUpRPM, mCurrentRPM <= inShiftDownRPM? Color::sDarkOrange : Color::sOrange); + } + + // Draw segment above inShiftUpRPM + if (mCurrentRPM > inShiftUpRPM) + { + draw_pie(inShiftUpRPM, mCurrentRPM, Color::sGreen); + draw_pie(mCurrentRPM, mMaxRPM, Color::sDarkGreen); + } + else + { + draw_pie(inShiftUpRPM, mMaxRPM, Color::sDarkGreen); + } +} + +#endif // JPH_DEBUG_RENDERER + +void VehicleEngine::SaveState(StateRecorder &inStream) const +{ + inStream.Write(mCurrentRPM); +} + +void VehicleEngine::RestoreState(StateRecorder &inStream) +{ + inStream.Read(mCurrentRPM); +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleEngine.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleEngine.h new file mode 100644 index 00000000000..25cf6391b9e --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleEngine.h @@ -0,0 +1,93 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +#ifdef JPH_DEBUG_RENDERER + class DebugRenderer; +#endif // JPH_DEBUG_RENDERER + +/// Generic properties for a vehicle engine +class JPH_EXPORT VehicleEngineSettings +{ +public: + JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, VehicleEngineSettings) + + /// Constructor + VehicleEngineSettings(); + + /// Saves the contents in binary form to inStream. + void SaveBinaryState(StreamOut &inStream) const; + + /// Restores the contents in binary form to inStream. + void RestoreBinaryState(StreamIn &inStream); + + float mMaxTorque = 500.0f; ///< Max amount of torque (Nm) that the engine can deliver + float mMinRPM = 1000.0f; ///< Min amount of revolutions per minute (rpm) the engine can produce without stalling + float mMaxRPM = 6000.0f; ///< Max amount of revolutions per minute (rpm) the engine can generate + LinearCurve mNormalizedTorque; ///< Y-axis: Curve that describes a ratio of the max torque the engine can produce (0 = 0, 1 = mMaxTorque). X-axis: the fraction of the RPM of the engine (0 = mMinRPM, 1 = mMaxRPM) + float mInertia = 0.5f; ///< Moment of inertia (kg m^2) of the engine + float mAngularDamping = 0.2f; ///< Angular damping factor of the wheel: dw/dt = -c * w +}; + +/// Runtime data for engine +class JPH_EXPORT VehicleEngine : public VehicleEngineSettings +{ +public: + /// Multiply an angular velocity (rad/s) with this value to get rounds per minute (RPM) + static constexpr float cAngularVelocityToRPM = 60.0f / (2.0f * JPH_PI); + + /// Clamp the RPM between min and max RPM + inline void ClampRPM() { mCurrentRPM = Clamp(mCurrentRPM, mMinRPM, mMaxRPM); } + + /// Current rotation speed of engine in rounds per minute + float GetCurrentRPM() const { return mCurrentRPM; } + + /// Update rotation speed of engine in rounds per minute + void SetCurrentRPM(float inRPM) { mCurrentRPM = inRPM; ClampRPM(); } + + /// Get current angular velocity of the engine in radians / second + inline float GetAngularVelocity() const { return mCurrentRPM / cAngularVelocityToRPM; } + + /// Get the amount of torque (N m) that the engine can supply + /// @param inAcceleration How much the gas pedal is pressed [0, 1] + float GetTorque(float inAcceleration) const { return inAcceleration * mMaxTorque * mNormalizedTorque.GetValue(mCurrentRPM / mMaxRPM); } + + /// Apply a torque to the engine rotation speed + /// @param inTorque Torque in N m + /// @param inDeltaTime Delta time in seconds + void ApplyTorque(float inTorque, float inDeltaTime); + + /// Update the engine RPM for damping + /// @param inDeltaTime Delta time in seconds + void ApplyDamping(float inDeltaTime); + +#ifdef JPH_DEBUG_RENDERER + // Function that converts RPM to an angle in radians for debugging purposes + float ConvertRPMToAngle(float inRPM) const { return (-0.75f + 1.5f * inRPM / mMaxRPM) * JPH_PI; } + + /// Debug draw a RPM meter + void DrawRPM(DebugRenderer *inRenderer, RVec3Arg inPosition, Vec3Arg inForward, Vec3Arg inUp, float inSize, float inShiftDownRPM, float inShiftUpRPM) const; +#endif // JPH_DEBUG_RENDERER + + /// If the engine is idle we allow the vehicle to sleep + bool AllowSleep() const { return mCurrentRPM <= 1.01f * mMinRPM; } + + /// Saving state for replay + void SaveState(StateRecorder &inStream) const; + void RestoreState(StateRecorder &inStream); + +private: + float mCurrentRPM = mMinRPM; ///< Current rotation speed of engine in rounds per minute +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleTrack.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleTrack.cpp new file mode 100644 index 00000000000..3bef2e80493 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleTrack.cpp @@ -0,0 +1,52 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include + +JPH_NAMESPACE_BEGIN + +JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(VehicleTrackSettings) +{ + JPH_ADD_ATTRIBUTE(VehicleTrackSettings, mDrivenWheel) + JPH_ADD_ATTRIBUTE(VehicleTrackSettings, mWheels) + JPH_ADD_ATTRIBUTE(VehicleTrackSettings, mInertia) + JPH_ADD_ATTRIBUTE(VehicleTrackSettings, mAngularDamping) + JPH_ADD_ATTRIBUTE(VehicleTrackSettings, mMaxBrakeTorque) + JPH_ADD_ATTRIBUTE(VehicleTrackSettings, mDifferentialRatio) +} + +void VehicleTrackSettings::SaveBinaryState(StreamOut &inStream) const +{ + inStream.Write(mDrivenWheel); + inStream.Write(mWheels); + inStream.Write(mInertia); + inStream.Write(mAngularDamping); + inStream.Write(mMaxBrakeTorque); + inStream.Write(mDifferentialRatio); +} + +void VehicleTrackSettings::RestoreBinaryState(StreamIn &inStream) +{ + inStream.Read(mDrivenWheel); + inStream.Read(mWheels); + inStream.Read(mInertia); + inStream.Read(mAngularDamping); + inStream.Read(mMaxBrakeTorque); + inStream.Read(mDifferentialRatio); +} + +void VehicleTrack::SaveState(StateRecorder &inStream) const +{ + inStream.Write(mAngularVelocity); +} + +void VehicleTrack::RestoreState(StateRecorder &inStream) +{ + inStream.Read(mAngularVelocity); +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleTrack.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleTrack.h new file mode 100644 index 00000000000..c90acccc660 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleTrack.h @@ -0,0 +1,56 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +/// On which side of the vehicle the track is located (for steering) +enum class ETrackSide : uint +{ + Left = 0, + Right = 1, + Num = 2 +}; + +/// Generic properties for tank tracks +class JPH_EXPORT VehicleTrackSettings +{ +public: + JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, VehicleTrackSettings) + + /// Saves the contents in binary form to inStream. + void SaveBinaryState(StreamOut &inStream) const; + + /// Restores the contents in binary form to inStream. + void RestoreBinaryState(StreamIn &inStream); + + uint mDrivenWheel; ///< Which wheel on the track is connected to the engine + Array mWheels; ///< Indices of wheels that are inside this track, should include the driven wheel too + float mInertia = 10.0f; ///< Moment of inertia (kg m^2) of the track and its wheels as seen on the driven wheel + float mAngularDamping = 0.5f; ///< Damping factor of track and its wheels: dw/dt = -c * w as seen on the driven wheel + float mMaxBrakeTorque = 15000.0f; ///< How much torque (Nm) the brakes can apply on the driven wheel + float mDifferentialRatio = 6.0f; ///< Ratio between rotation speed of gear box and driven wheel of track +}; + +/// Runtime data for tank tracks +class JPH_EXPORT VehicleTrack : public VehicleTrackSettings +{ +public: + /// Saving state for replay + void SaveState(StateRecorder &inStream) const; + void RestoreState(StateRecorder &inStream); + + float mAngularVelocity = 0.0f; ///< Angular velocity of the driven wheel, will determine the speed of the entire track +}; + +using VehicleTracks = VehicleTrack[(int)ETrackSide::Num]; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleTransmission.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleTransmission.cpp new file mode 100644 index 00000000000..43336a537f0 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleTransmission.cpp @@ -0,0 +1,159 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include + +JPH_NAMESPACE_BEGIN + +JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(VehicleTransmissionSettings) +{ + JPH_ADD_ENUM_ATTRIBUTE(VehicleTransmissionSettings, mMode) + JPH_ADD_ATTRIBUTE(VehicleTransmissionSettings, mGearRatios) + JPH_ADD_ATTRIBUTE(VehicleTransmissionSettings, mReverseGearRatios) + JPH_ADD_ATTRIBUTE(VehicleTransmissionSettings, mSwitchTime) + JPH_ADD_ATTRIBUTE(VehicleTransmissionSettings, mClutchReleaseTime) + JPH_ADD_ATTRIBUTE(VehicleTransmissionSettings, mSwitchLatency) + JPH_ADD_ATTRIBUTE(VehicleTransmissionSettings, mShiftUpRPM) + JPH_ADD_ATTRIBUTE(VehicleTransmissionSettings, mShiftDownRPM) + JPH_ADD_ATTRIBUTE(VehicleTransmissionSettings, mClutchStrength) +} + +void VehicleTransmissionSettings::SaveBinaryState(StreamOut &inStream) const +{ + inStream.Write(mMode); + inStream.Write(mGearRatios); + inStream.Write(mReverseGearRatios); + inStream.Write(mSwitchTime); + inStream.Write(mClutchReleaseTime); + inStream.Write(mSwitchLatency); + inStream.Write(mShiftUpRPM); + inStream.Write(mShiftDownRPM); + inStream.Write(mClutchStrength); +} + +void VehicleTransmissionSettings::RestoreBinaryState(StreamIn &inStream) +{ + inStream.Read(mMode); + inStream.Read(mGearRatios); + inStream.Read(mReverseGearRatios); + inStream.Read(mSwitchTime); + inStream.Read(mClutchReleaseTime); + inStream.Read(mSwitchLatency); + inStream.Read(mShiftUpRPM); + inStream.Read(mShiftDownRPM); + inStream.Read(mClutchStrength); +} + +void VehicleTransmission::Update(float inDeltaTime, float inCurrentRPM, float inForwardInput, bool inCanShiftUp) +{ + // Update current gear and calculate clutch friction + if (mMode == ETransmissionMode::Auto) + { + // Switch gears based on rpm + int old_gear = mCurrentGear; + if (mCurrentGear == 0 // In neutral + || inForwardInput * float(mCurrentGear) < 0.0f) // Changing between forward / reverse + { + // Switch to first gear or reverse depending on input + mCurrentGear = inForwardInput > 0.0f? 1 : (inForwardInput < 0.0f? -1 : 0); + } + else if (mGearSwitchLatencyTimeLeft == 0.0f) // If not in the timout after switching gears + { + if (inCanShiftUp && inCurrentRPM > mShiftUpRPM) + { + if (mCurrentGear < 0) + { + // Shift up, reverse + if (mCurrentGear > -(int)mReverseGearRatios.size()) + mCurrentGear--; + } + else + { + // Shift up, forward + if (mCurrentGear < (int)mGearRatios.size()) + mCurrentGear++; + } + } + else if (inCurrentRPM < mShiftDownRPM) + { + if (mCurrentGear < 0) + { + // Shift down, reverse + int max_gear = inForwardInput != 0.0f? -1 : 0; + if (mCurrentGear < max_gear) + mCurrentGear++; + } + else + { + // Shift down, forward + int min_gear = inForwardInput != 0.0f? 1 : 0; + if (mCurrentGear > min_gear) + mCurrentGear--; + } + } + } + + if (old_gear != mCurrentGear) + { + // We've shifted gear, start switch countdown + mGearSwitchTimeLeft = old_gear != 0? mSwitchTime : 0.0f; + mClutchReleaseTimeLeft = mClutchReleaseTime; + mGearSwitchLatencyTimeLeft = mSwitchLatency; + mClutchFriction = 0.0f; + } + else if (mGearSwitchTimeLeft > 0.0f) + { + // If still switching gears, count down + mGearSwitchTimeLeft = max(0.0f, mGearSwitchTimeLeft - inDeltaTime); + mClutchFriction = 0.0f; + } + else if (mClutchReleaseTimeLeft > 0.0f) + { + // After switching the gears we slowly release the clutch + mClutchReleaseTimeLeft = max(0.0f, mClutchReleaseTimeLeft - inDeltaTime); + mClutchFriction = 1.0f - mClutchReleaseTimeLeft / mClutchReleaseTime; + } + else + { + // Clutch has full friction + mClutchFriction = 1.0f; + + // Count down switch latency + mGearSwitchLatencyTimeLeft = max(0.0f, mGearSwitchLatencyTimeLeft - inDeltaTime); + } + } +} + +float VehicleTransmission::GetCurrentRatio() const +{ + if (mCurrentGear < 0) + return mReverseGearRatios[-mCurrentGear - 1]; + else if (mCurrentGear == 0) + return 0.0f; + else + return mGearRatios[mCurrentGear - 1]; +} + +void VehicleTransmission::SaveState(StateRecorder &inStream) const +{ + inStream.Write(mCurrentGear); + inStream.Write(mClutchFriction); + inStream.Write(mGearSwitchTimeLeft); + inStream.Write(mClutchReleaseTimeLeft); + inStream.Write(mGearSwitchLatencyTimeLeft); +} + +void VehicleTransmission::RestoreState(StateRecorder &inStream) +{ + inStream.Read(mCurrentGear); + inStream.Read(mClutchFriction); + inStream.Read(mGearSwitchTimeLeft); + inStream.Read(mClutchReleaseTimeLeft); + inStream.Read(mGearSwitchLatencyTimeLeft); +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleTransmission.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleTransmission.h new file mode 100644 index 00000000000..1d3306a8f9d --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleTransmission.h @@ -0,0 +1,87 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +/// How gears are shifted +enum class ETransmissionMode : uint8 +{ + Auto, ///< Automatically shift gear up and down + Manual, ///< Manual gear shift (call SetTransmissionInput) +}; + +/// Configuration for the transmission of a vehicle (gear box) +class JPH_EXPORT VehicleTransmissionSettings +{ +public: + JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, VehicleTransmissionSettings) + + /// Saves the contents in binary form to inStream. + void SaveBinaryState(StreamOut &inStream) const; + + /// Restores the contents in binary form to inStream. + void RestoreBinaryState(StreamIn &inStream); + + ETransmissionMode mMode = ETransmissionMode::Auto; ///< How to switch gears + Array mGearRatios { 2.66f, 1.78f, 1.3f, 1.0f, 0.74f }; ///< Ratio in rotation rate between engine and gear box, first element is 1st gear, 2nd element 2nd gear etc. + Array mReverseGearRatios { -2.90f }; ///< Ratio in rotation rate between engine and gear box when driving in reverse + float mSwitchTime = 0.5f; ///< How long it takes to switch gears (s), only used in auto mode + float mClutchReleaseTime = 0.3f; ///< How long it takes to release the clutch (go to full friction), only used in auto mode + float mSwitchLatency = 0.5f; ///< How long to wait after releasing the clutch before another switch is attempted (s), only used in auto mode + float mShiftUpRPM = 4000.0f; ///< If RPM of engine is bigger then this we will shift a gear up, only used in auto mode + float mShiftDownRPM = 2000.0f; ///< If RPM of engine is smaller then this we will shift a gear down, only used in auto mode + float mClutchStrength = 10.0f; ///< Strength of the clutch when fully engaged. Total torque a clutch applies is Torque = ClutchStrength * (Velocity Engine - Avg Velocity Wheels At Clutch) (units: k m^2 s^-1) +}; + +/// Runtime data for transmission +class JPH_EXPORT VehicleTransmission : public VehicleTransmissionSettings +{ +public: + /// Set input from driver regarding the transmission (only relevant when transmission is set to manual mode) + /// @param inCurrentGear Current gear, -1 = reverse, 0 = neutral, 1 = 1st gear etc. + /// @param inClutchFriction Value between 0 and 1 indicating how much friction the clutch gives (0 = no friction, 1 = full friction) + void Set(int inCurrentGear, float inClutchFriction) { mCurrentGear = inCurrentGear; mClutchFriction = inClutchFriction; } + + /// Update the current gear and clutch friction if the transmission is in auto mode + /// @param inDeltaTime Time step delta time in s + /// @param inCurrentRPM Current RPM for engine + /// @param inForwardInput Hint if the user wants to drive forward (> 0) or backwards (< 0) + /// @param inCanShiftUp Indicates if we want to allow the transmission to shift up (e.g. pass false if wheels are slipping) + void Update(float inDeltaTime, float inCurrentRPM, float inForwardInput, bool inCanShiftUp); + + /// Current gear, -1 = reverse, 0 = neutral, 1 = 1st gear etc. + int GetCurrentGear() const { return mCurrentGear; } + + /// Value between 0 and 1 indicating how much friction the clutch gives (0 = no friction, 1 = full friction) + float GetClutchFriction() const { return mClutchFriction; } + + /// If the auto box is currently switching gears + bool IsSwitchingGear() const { return mGearSwitchTimeLeft > 0.0f; } + + /// Return the transmission ratio based on the current gear (ratio between engine and differential) + float GetCurrentRatio() const; + + /// Only allow sleeping when the transmission is idle + bool AllowSleep() const { return mGearSwitchTimeLeft <= 0.0f && mClutchReleaseTimeLeft <= 0.0f && mGearSwitchLatencyTimeLeft <= 0.0f; } + + /// Saving state for replay + void SaveState(StateRecorder &inStream) const; + void RestoreState(StateRecorder &inStream); + +private: + int mCurrentGear = 0; ///< Current gear, -1 = reverse, 0 = neutral, 1 = 1st gear etc. + float mClutchFriction = 1.0f; ///< Value between 0 and 1 indicating how much friction the clutch gives (0 = no friction, 1 = full friction) + float mGearSwitchTimeLeft = 0.0f; ///< When switching gears this will be > 0 and will cause the engine to not provide any torque to the wheels for a short time (used for automatic gear switching only) + float mClutchReleaseTimeLeft = 0.0f; ///< After switching gears this will be > 0 and will cause the clutch friction to go from 0 to 1 (used for automatic gear switching only) + float mGearSwitchLatencyTimeLeft = 0.0f; ///< After releasing the clutch this will be > 0 and will prevent another gear switch (used for automatic gear switching only) +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/Wheel.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/Wheel.cpp new file mode 100644 index 00000000000..e43ffb8c9a4 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/Wheel.cpp @@ -0,0 +1,93 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(WheelSettings) +{ + JPH_ADD_ATTRIBUTE(WheelSettings, mSuspensionForcePoint) + JPH_ADD_ATTRIBUTE(WheelSettings, mPosition) + JPH_ADD_ATTRIBUTE(WheelSettings, mSuspensionDirection) + JPH_ADD_ATTRIBUTE(WheelSettings, mSteeringAxis) + JPH_ADD_ATTRIBUTE(WheelSettings, mWheelForward) + JPH_ADD_ATTRIBUTE(WheelSettings, mWheelUp) + JPH_ADD_ATTRIBUTE(WheelSettings, mSuspensionMinLength) + JPH_ADD_ATTRIBUTE(WheelSettings, mSuspensionMaxLength) + JPH_ADD_ATTRIBUTE(WheelSettings, mSuspensionPreloadLength) + JPH_ADD_ENUM_ATTRIBUTE_WITH_ALIAS(WheelSettings, mSuspensionSpring.mMode, "mSuspensionSpringMode") + JPH_ADD_ATTRIBUTE_WITH_ALIAS(WheelSettings, mSuspensionSpring.mFrequency, "mSuspensionFrequency") // Renaming attributes to stay compatible with old versions of the library + JPH_ADD_ATTRIBUTE_WITH_ALIAS(WheelSettings, mSuspensionSpring.mDamping, "mSuspensionDamping") + JPH_ADD_ATTRIBUTE(WheelSettings, mRadius) + JPH_ADD_ATTRIBUTE(WheelSettings, mWidth) + JPH_ADD_ATTRIBUTE(WheelSettings, mEnableSuspensionForcePoint) +} + +void WheelSettings::SaveBinaryState(StreamOut &inStream) const +{ + inStream.Write(mSuspensionForcePoint); + inStream.Write(mPosition); + inStream.Write(mSuspensionDirection); + inStream.Write(mSteeringAxis); + inStream.Write(mWheelForward); + inStream.Write(mWheelUp); + inStream.Write(mSuspensionMinLength); + inStream.Write(mSuspensionMaxLength); + inStream.Write(mSuspensionPreloadLength); + mSuspensionSpring.SaveBinaryState(inStream); + inStream.Write(mRadius); + inStream.Write(mWidth); + inStream.Write(mEnableSuspensionForcePoint); +} + +void WheelSettings::RestoreBinaryState(StreamIn &inStream) +{ + inStream.Read(mSuspensionForcePoint); + inStream.Read(mPosition); + inStream.Read(mSuspensionDirection); + inStream.Read(mSteeringAxis); + inStream.Read(mWheelForward); + inStream.Read(mWheelUp); + inStream.Read(mSuspensionMinLength); + inStream.Read(mSuspensionMaxLength); + inStream.Read(mSuspensionPreloadLength); + mSuspensionSpring.RestoreBinaryState(inStream); + inStream.Read(mRadius); + inStream.Read(mWidth); + inStream.Read(mEnableSuspensionForcePoint); +} + +Wheel::Wheel(const WheelSettings &inSettings) : + mSettings(&inSettings), + mSuspensionLength(inSettings.mSuspensionMaxLength) +{ + JPH_ASSERT(inSettings.mSuspensionDirection.IsNormalized()); + JPH_ASSERT(inSettings.mSteeringAxis.IsNormalized()); + JPH_ASSERT(inSettings.mWheelForward.IsNormalized()); + JPH_ASSERT(inSettings.mWheelUp.IsNormalized()); + JPH_ASSERT(inSettings.mSuspensionMinLength >= 0.0f); + JPH_ASSERT(inSettings.mSuspensionMaxLength >= inSettings.mSuspensionMinLength); + JPH_ASSERT(inSettings.mSuspensionPreloadLength >= 0.0f); + JPH_ASSERT(inSettings.mSuspensionSpring.mFrequency > 0.0f); + JPH_ASSERT(inSettings.mSuspensionSpring.mDamping >= 0.0f); + JPH_ASSERT(inSettings.mRadius > 0.0f); + JPH_ASSERT(inSettings.mWidth >= 0.0f); +} + +bool Wheel::SolveLongitudinalConstraintPart(const VehicleConstraint &inConstraint, float inMinImpulse, float inMaxImpulse) +{ + return mLongitudinalPart.SolveVelocityConstraint(*inConstraint.GetVehicleBody(), *mContactBody, -mContactLongitudinal, inMinImpulse, inMaxImpulse); +} + +bool Wheel::SolveLateralConstraintPart(const VehicleConstraint &inConstraint, float inMinImpulse, float inMaxImpulse) +{ + return mLateralPart.SolveVelocityConstraint(*inConstraint.GetVehicleBody(), *mContactBody, -mContactLateral, inMinImpulse, inMaxImpulse); +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/Wheel.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/Wheel.h new file mode 100644 index 00000000000..eb5f6874c30 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/Wheel.h @@ -0,0 +1,148 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +class VehicleConstraint; + +/// Base class for wheel settings, each VehicleController can implement a derived class of this +class JPH_EXPORT WheelSettings : public SerializableObject, public RefTarget +{ +public: + JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, WheelSettings) + + /// Saves the contents in binary form to inStream. + virtual void SaveBinaryState(StreamOut &inStream) const; + + /// Restores the contents in binary form to inStream. + virtual void RestoreBinaryState(StreamIn &inStream); + + Vec3 mPosition { 0, 0, 0 }; ///< Attachment point of wheel suspension in local space of the body + Vec3 mSuspensionForcePoint { 0, 0, 0 }; ///< Where tire forces (suspension and traction) are applied, in local space of the body. A good default is the center of the wheel in its neutral pose. See mEnableSuspensionForcePoint. + Vec3 mSuspensionDirection { 0, -1, 0 }; ///< Direction of the suspension in local space of the body, should point down + Vec3 mSteeringAxis { 0, 1, 0 }; ///< Direction of the steering axis in local space of the body, should point up (e.g. for a bike would be -mSuspensionDirection) + Vec3 mWheelUp { 0, 1, 0 }; ///< Up direction when the wheel is in the neutral steering position (usually VehicleConstraintSettings::mUp but can be used to give the wheel camber or for a bike would be -mSuspensionDirection) + Vec3 mWheelForward { 0, 0, 1 }; ///< Forward direction when the wheel is in the neutral steering position (usually VehicleConstraintSettings::mForward but can be used to give the wheel toe, does not need to be perpendicular to mWheelUp) + float mSuspensionMinLength = 0.3f; ///< How long the suspension is in max raised position relative to the attachment point (m) + float mSuspensionMaxLength = 0.5f; ///< How long the suspension is in max droop position relative to the attachment point (m) + float mSuspensionPreloadLength = 0.0f; ///< The natural length (m) of the suspension spring is defined as mSuspensionMaxLength + mSuspensionPreloadLength. Can be used to preload the suspension as the spring is compressed by mSuspensionPreloadLength when the suspension is in max droop position. Note that this means when the vehicle touches the ground there is a discontinuity so it will also make the vehicle more bouncy as we're updating with discrete time steps. + SpringSettings mSuspensionSpring { ESpringMode::FrequencyAndDamping, 1.5f, 0.5f }; ///< Settings for the suspension spring + float mRadius = 0.3f; ///< Radius of the wheel (m) + float mWidth = 0.1f; ///< Width of the wheel (m) + bool mEnableSuspensionForcePoint = false; ///< Enables mSuspensionForcePoint, if disabled, the forces are applied at the collision contact point. This leads to a more accurate simulation when interacting with dynamic objects but makes the vehicle less stable. When setting this to true, all forces will be applied to a fixed point on the vehicle body. +}; + +/// Base class for runtime data for a wheel, each VehicleController can implement a derived class of this +class JPH_EXPORT Wheel : public NonCopyable +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Constructor / destructor + explicit Wheel(const WheelSettings &inSettings); + virtual ~Wheel() = default; + + /// Get settings for the wheel + const WheelSettings * GetSettings() const { return mSettings; } + + /// Get the angular velocity (rad/s) for this wheel, note that positive means the wheel is rotating such that the car moves forward + float GetAngularVelocity() const { return mAngularVelocity; } + + /// Update the angular velocity (rad/s) + void SetAngularVelocity(float inVel) { mAngularVelocity = inVel; } + + /// Get the current rotation angle of the wheel in radians [0, 2 pi] + float GetRotationAngle() const { return mAngle; } + + /// Set the current rotation angle of the wheel in radians [0, 2 pi] + void SetRotationAngle(float inAngle) { mAngle = inAngle; } + + /// Get the current steer angle of the wheel in radians [-pi, pi], positive is to the left + float GetSteerAngle() const { return mSteerAngle; } + + /// Set the current steer angle of the wheel in radians [-pi, pi] + void SetSteerAngle(float inAngle) { mSteerAngle = inAngle; } + + /// Returns true if the wheel is touching an object + inline bool HasContact() const { return !mContactBodyID.IsInvalid(); } + + /// Returns the body ID of the body that this wheel is touching + BodyID GetContactBodyID() const { return mContactBodyID; } + + /// Returns the sub shape ID where we're contacting the body + SubShapeID GetContactSubShapeID() const { return mContactSubShapeID; } + + /// Returns the current contact position in world space (note by the time you call this the vehicle has moved) + RVec3 GetContactPosition() const { JPH_ASSERT(HasContact()); return mContactPosition; } + + /// Velocity of the contact point (m / s, not relative to the wheel but in world space) + Vec3 GetContactPointVelocity() const { JPH_ASSERT(HasContact()); return mContactPointVelocity; } + + /// Returns the current contact normal in world space (note by the time you call this the vehicle has moved) + Vec3 GetContactNormal() const { JPH_ASSERT(HasContact()); return mContactNormal; } + + /// Returns longitudinal direction (direction along the wheel relative to floor) in world space (note by the time you call this the vehicle has moved) + Vec3 GetContactLongitudinal() const { JPH_ASSERT(HasContact()); return mContactLongitudinal; } + + /// Returns lateral direction (sideways direction) in world space (note by the time you call this the vehicle has moved) + Vec3 GetContactLateral() const { JPH_ASSERT(HasContact()); return mContactLateral; } + + /// Get the length of the suspension for a wheel (m) relative to the suspension attachment point (hard point) + float GetSuspensionLength() const { return mSuspensionLength; } + + /// Check if the suspension hit its upper limit + bool HasHitHardPoint() const { return mSuspensionMaxUpPart.IsActive(); } + + /// Get the total impulse (N s) that was applied by the suspension + float GetSuspensionLambda() const { return mSuspensionPart.GetTotalLambda() + mSuspensionMaxUpPart.GetTotalLambda(); } + + /// Get total impulse (N s) applied along the forward direction of the wheel + float GetLongitudinalLambda() const { return mLongitudinalPart.GetTotalLambda(); } + + /// Get total impulse (N s) applied along the sideways direction of the wheel + float GetLateralLambda() const { return mLateralPart.GetTotalLambda(); } + + /// Internal function that should only be called by the controller. Used to apply impulses in the forward direction of the vehicle. + bool SolveLongitudinalConstraintPart(const VehicleConstraint &inConstraint, float inMinImpulse, float inMaxImpulse); + + /// Internal function that should only be called by the controller. Used to apply impulses in the sideways direction of the vehicle. + bool SolveLateralConstraintPart(const VehicleConstraint &inConstraint, float inMinImpulse, float inMaxImpulse); + +protected: + friend class VehicleConstraint; + + RefConst mSettings; ///< Configuration settings for this wheel + BodyID mContactBodyID; ///< ID of body for ground + SubShapeID mContactSubShapeID; ///< Sub shape ID for ground + Body * mContactBody = nullptr; ///< Body for ground + float mSuspensionLength; ///< Current length of the suspension + RVec3 mContactPosition; ///< Position of the contact point between wheel and ground + Vec3 mContactPointVelocity; ///< Velocity of the contact point (m / s, not relative to the wheel but in world space) + Vec3 mContactNormal; ///< Normal of the contact point between wheel and ground + Vec3 mContactLongitudinal; ///< Vector perpendicular to normal in the forward direction + Vec3 mContactLateral; ///< Vector perpendicular to normal and longitudinal direction in the right direction + Real mAxlePlaneConstant; ///< Constant for the contact plane of the axle, defined as ContactNormal . (WorldSpaceSuspensionPoint + SuspensionLength * WorldSpaceSuspensionDirection) + float mAntiRollBarImpulse = 0.0f; ///< Amount of impulse applied to the suspension from the anti-rollbars + + float mSteerAngle = 0.0f; ///< Rotation around the suspension direction, positive is to the left + float mAngularVelocity = 0.0f; ///< Rotation speed of wheel, positive when the wheels cause the vehicle to move forwards (rad/s) + float mAngle = 0.0f; ///< Current rotation of the wheel (rad, [0, 2 pi]) + + AxisConstraintPart mSuspensionPart; ///< Controls movement up/down along the contact normal + AxisConstraintPart mSuspensionMaxUpPart; ///< Adds a hard limit when reaching the minimal suspension length + AxisConstraintPart mLongitudinalPart; ///< Controls movement forward/backward + AxisConstraintPart mLateralPart; ///< Controls movement sideways (slip) +}; + +using Wheels = Array; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/WheeledVehicleController.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/WheeledVehicleController.cpp new file mode 100644 index 00000000000..8e4a71a4fb8 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/WheeledVehicleController.cpp @@ -0,0 +1,845 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#include +#include +#include +#include +#ifdef JPH_DEBUG_RENDERER + #include +#endif // JPH_DEBUG_RENDERER + +//#define JPH_TRACE_VEHICLE_STATS + +JPH_NAMESPACE_BEGIN + +JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(WheeledVehicleControllerSettings) +{ + JPH_ADD_BASE_CLASS(WheeledVehicleControllerSettings, VehicleControllerSettings) + + JPH_ADD_ATTRIBUTE(WheeledVehicleControllerSettings, mEngine) + JPH_ADD_ATTRIBUTE(WheeledVehicleControllerSettings, mTransmission) + JPH_ADD_ATTRIBUTE(WheeledVehicleControllerSettings, mDifferentials) + JPH_ADD_ATTRIBUTE(WheeledVehicleControllerSettings, mDifferentialLimitedSlipRatio) +} + +JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(WheelSettingsWV) +{ + JPH_ADD_ATTRIBUTE(WheelSettingsWV, mInertia) + JPH_ADD_ATTRIBUTE(WheelSettingsWV, mAngularDamping) + JPH_ADD_ATTRIBUTE(WheelSettingsWV, mMaxSteerAngle) + JPH_ADD_ATTRIBUTE(WheelSettingsWV, mLongitudinalFriction) + JPH_ADD_ATTRIBUTE(WheelSettingsWV, mLateralFriction) + JPH_ADD_ATTRIBUTE(WheelSettingsWV, mMaxBrakeTorque) + JPH_ADD_ATTRIBUTE(WheelSettingsWV, mMaxHandBrakeTorque) +} + +WheelSettingsWV::WheelSettingsWV() +{ + mLongitudinalFriction.Reserve(3); + mLongitudinalFriction.AddPoint(0.0f, 0.0f); + mLongitudinalFriction.AddPoint(0.06f, 1.2f); + mLongitudinalFriction.AddPoint(0.2f, 1.0f); + + mLateralFriction.Reserve(3); + mLateralFriction.AddPoint(0.0f, 0.0f); + mLateralFriction.AddPoint(3.0f, 1.2f); + mLateralFriction.AddPoint(20.0f, 1.0f); +} + +void WheelSettingsWV::SaveBinaryState(StreamOut &inStream) const +{ + inStream.Write(mInertia); + inStream.Write(mAngularDamping); + inStream.Write(mMaxSteerAngle); + mLongitudinalFriction.SaveBinaryState(inStream); + mLateralFriction.SaveBinaryState(inStream); + inStream.Write(mMaxBrakeTorque); + inStream.Write(mMaxHandBrakeTorque); +} + +void WheelSettingsWV::RestoreBinaryState(StreamIn &inStream) +{ + inStream.Read(mInertia); + inStream.Read(mAngularDamping); + inStream.Read(mMaxSteerAngle); + mLongitudinalFriction.RestoreBinaryState(inStream); + mLateralFriction.RestoreBinaryState(inStream); + inStream.Read(mMaxBrakeTorque); + inStream.Read(mMaxHandBrakeTorque); +} + +WheelWV::WheelWV(const WheelSettingsWV &inSettings) : + Wheel(inSettings) +{ + JPH_ASSERT(inSettings.mInertia >= 0.0f); + JPH_ASSERT(inSettings.mAngularDamping >= 0.0f); + JPH_ASSERT(abs(inSettings.mMaxSteerAngle) <= 0.5f * JPH_PI); + JPH_ASSERT(inSettings.mMaxBrakeTorque >= 0.0f); + JPH_ASSERT(inSettings.mMaxHandBrakeTorque >= 0.0f); +} + +void WheelWV::Update(uint inWheelIndex, float inDeltaTime, const VehicleConstraint &inConstraint) +{ + const WheelSettingsWV *settings = GetSettings(); + + // Angular damping: dw/dt = -c * w + // Solution: w(t) = w(0) * e^(-c * t) or w2 = w1 * e^(-c * dt) + // Taylor expansion of e^(-c * dt) = 1 - c * dt + ... + // Since dt is usually in the order of 1/60 and c is a low number too this approximation is good enough + mAngularVelocity *= max(0.0f, 1.0f - settings->mAngularDamping * inDeltaTime); + + // Update rotation of wheel + mAngle = fmod(mAngle + mAngularVelocity * inDeltaTime, 2.0f * JPH_PI); + + if (mContactBody != nullptr) + { + const Body *body = inConstraint.GetVehicleBody(); + + // Calculate relative velocity between wheel contact point and floor + Vec3 relative_velocity = body->GetPointVelocity(mContactPosition) - mContactPointVelocity; + + // Cancel relative velocity in the normal plane + relative_velocity -= mContactNormal.Dot(relative_velocity) * mContactNormal; + float relative_longitudinal_velocity = relative_velocity.Dot(mContactLongitudinal); + + // Calculate longitudinal friction based on difference between velocity of rolling wheel and drive surface + float relative_longitudinal_velocity_denom = Sign(relative_longitudinal_velocity) * max(1.0e-3f, abs(relative_longitudinal_velocity)); // Ensure we don't divide by zero + mLongitudinalSlip = abs((mAngularVelocity * settings->mRadius - relative_longitudinal_velocity) / relative_longitudinal_velocity_denom); + float longitudinal_slip_friction = settings->mLongitudinalFriction.GetValue(mLongitudinalSlip); + + // Calculate lateral friction based on slip angle + float relative_velocity_len = relative_velocity.Length(); + mLateralSlip = relative_velocity_len < 1.0e-3f ? 0.0f : ACos(abs(relative_longitudinal_velocity) / relative_velocity_len); + float lateral_slip_angle = RadiansToDegrees(mLateralSlip); + float lateral_slip_friction = settings->mLateralFriction.GetValue(lateral_slip_angle); + + // Tire friction + VehicleConstraint::CombineFunction combine_friction = inConstraint.GetCombineFriction(); + mCombinedLongitudinalFriction = longitudinal_slip_friction; + mCombinedLateralFriction = lateral_slip_friction; + combine_friction(inWheelIndex, mCombinedLongitudinalFriction, mCombinedLateralFriction, *mContactBody, mContactSubShapeID); + } + else + { + // No collision + mLongitudinalSlip = 0.0f; + mLateralSlip = 0.0f; + mCombinedLongitudinalFriction = mCombinedLateralFriction = 0.0f; + } +} + +VehicleController *WheeledVehicleControllerSettings::ConstructController(VehicleConstraint &inConstraint) const +{ + return new WheeledVehicleController(*this, inConstraint); +} + +void WheeledVehicleControllerSettings::SaveBinaryState(StreamOut &inStream) const +{ + mEngine.SaveBinaryState(inStream); + + mTransmission.SaveBinaryState(inStream); + + uint32 num_differentials = (uint32)mDifferentials.size(); + inStream.Write(num_differentials); + for (const VehicleDifferentialSettings &d : mDifferentials) + d.SaveBinaryState(inStream); + + inStream.Write(mDifferentialLimitedSlipRatio); +} + +void WheeledVehicleControllerSettings::RestoreBinaryState(StreamIn &inStream) +{ + mEngine.RestoreBinaryState(inStream); + + mTransmission.RestoreBinaryState(inStream); + + uint32 num_differentials = 0; + inStream.Read(num_differentials); + mDifferentials.resize(num_differentials); + for (VehicleDifferentialSettings &d : mDifferentials) + d.RestoreBinaryState(inStream); + + inStream.Read(mDifferentialLimitedSlipRatio); +} + +WheeledVehicleController::WheeledVehicleController(const WheeledVehicleControllerSettings &inSettings, VehicleConstraint &inConstraint) : + VehicleController(inConstraint) +{ + // Copy engine settings + static_cast(mEngine) = inSettings.mEngine; + JPH_ASSERT(inSettings.mEngine.mMinRPM >= 0.0f); + JPH_ASSERT(inSettings.mEngine.mMinRPM <= inSettings.mEngine.mMaxRPM); + mEngine.SetCurrentRPM(mEngine.mMinRPM); + + // Copy transmission settings + static_cast(mTransmission) = inSettings.mTransmission; +#ifdef JPH_ENABLE_ASSERTS + for (float r : inSettings.mTransmission.mGearRatios) + JPH_ASSERT(r > 0.0f); + for (float r : inSettings.mTransmission.mReverseGearRatios) + JPH_ASSERT(r < 0.0f); +#endif // JPH_ENABLE_ASSERTS + JPH_ASSERT(inSettings.mTransmission.mSwitchTime >= 0.0f); + JPH_ASSERT(inSettings.mTransmission.mShiftDownRPM > 0.0f); + JPH_ASSERT(inSettings.mTransmission.mMode != ETransmissionMode::Auto || inSettings.mTransmission.mShiftUpRPM < inSettings.mEngine.mMaxRPM); + JPH_ASSERT(inSettings.mTransmission.mShiftUpRPM > inSettings.mTransmission.mShiftDownRPM); + JPH_ASSERT(inSettings.mTransmission.mClutchStrength > 0.0f); + + // Copy differential settings + mDifferentials.resize(inSettings.mDifferentials.size()); + for (uint i = 0; i < mDifferentials.size(); ++i) + { + const VehicleDifferentialSettings &d = inSettings.mDifferentials[i]; + mDifferentials[i] = d; + JPH_ASSERT(d.mDifferentialRatio > 0.0f); + JPH_ASSERT(d.mLeftRightSplit >= 0.0f && d.mLeftRightSplit <= 1.0f); + JPH_ASSERT(d.mEngineTorqueRatio >= 0.0f); + JPH_ASSERT(d.mLimitedSlipRatio > 1.0f); + } + + mDifferentialLimitedSlipRatio = inSettings.mDifferentialLimitedSlipRatio; + JPH_ASSERT(mDifferentialLimitedSlipRatio > 1.0f); +} + +float WheeledVehicleController::GetWheelSpeedAtClutch() const +{ + float wheel_speed_at_clutch = 0.0f; + int num_driven_wheels = 0; + for (const VehicleDifferentialSettings &d : mDifferentials) + { + int wheels[] = { d.mLeftWheel, d.mRightWheel }; + for (int w : wheels) + if (w >= 0) + { + wheel_speed_at_clutch += mConstraint.GetWheel(w)->GetAngularVelocity() * d.mDifferentialRatio; + num_driven_wheels++; + } + } + return wheel_speed_at_clutch / float(num_driven_wheels) * VehicleEngine::cAngularVelocityToRPM * mTransmission.GetCurrentRatio(); +} + +bool WheeledVehicleController::AllowSleep() const +{ + return mForwardInput == 0.0f // No user input + && mTransmission.AllowSleep() // Transmission is not shifting + && mEngine.AllowSleep(); // Engine is idling +} + +void WheeledVehicleController::PreCollide(float inDeltaTime, PhysicsSystem &inPhysicsSystem) +{ + JPH_PROFILE_FUNCTION(); + +#ifdef JPH_TRACE_VEHICLE_STATS + static bool sTracedHeader = false; + if (!sTracedHeader) + { + Trace("Time, ForwardInput, Gear, ClutchFriction, EngineRPM, WheelRPM, Velocity (km/h)"); + sTracedHeader = true; + } + static float sTime = 0.0f; + sTime += inDeltaTime; + Trace("%.3f, %.1f, %d, %.1f, %.1f, %.1f, %.1f", sTime, mForwardInput, mTransmission.GetCurrentGear(), mTransmission.GetClutchFriction(), mEngine.GetCurrentRPM(), GetWheelSpeedAtClutch(), mConstraint.GetVehicleBody()->GetLinearVelocity().Length() * 3.6f); +#endif // JPH_TRACE_VEHICLE_STATS + + for (Wheel *w_base : mConstraint.GetWheels()) + { + WheelWV *w = static_cast(w_base); + + // Set steering angle + w->SetSteerAngle(-mRightInput * w->GetSettings()->mMaxSteerAngle); + } +} + +void WheeledVehicleController::PostCollide(float inDeltaTime, PhysicsSystem &inPhysicsSystem) +{ + JPH_PROFILE_FUNCTION(); + + // Remember old RPM so we can detect if we're increasing or decreasing + float old_engine_rpm = mEngine.GetCurrentRPM(); + + Wheels &wheels = mConstraint.GetWheels(); + + // Update wheel angle, do this before applying torque to the wheels (as friction will slow them down again) + for (uint wheel_index = 0, num_wheels = (uint)wheels.size(); wheel_index < num_wheels; ++wheel_index) + { + WheelWV *w = static_cast(wheels[wheel_index]); + w->Update(wheel_index, inDeltaTime, mConstraint); + } + + // In auto transmission mode, don't accelerate the engine when switching gears + float forward_input = abs(mForwardInput); + if (mTransmission.mMode == ETransmissionMode::Auto) + forward_input *= mTransmission.GetClutchFriction(); + + // Apply engine damping + mEngine.ApplyDamping(inDeltaTime); + + // Calculate engine torque + float engine_torque = mEngine.GetTorque(forward_input); + + // Define a struct that contains information about driven differentials (i.e. that have wheels connected) + struct DrivenDifferential + { + const VehicleDifferentialSettings * mDifferential; + float mAngularVelocity; + float mClutchToDifferentialTorqueRatio; + float mTempTorqueFactor; + }; + + // Collect driven differentials and their speeds + Array driven_differentials; + driven_differentials.reserve(mDifferentials.size()); + float differential_omega_min = FLT_MAX, differential_omega_max = 0.0f; + for (const VehicleDifferentialSettings &d : mDifferentials) + { + float avg_omega = 0.0f; + int avg_omega_denom = 0; + int indices[] = { d.mLeftWheel, d.mRightWheel }; + for (int idx : indices) + if (idx != -1) + { + avg_omega += wheels[idx]->GetAngularVelocity(); + avg_omega_denom++; + } + + if (avg_omega_denom > 0) + { + avg_omega = abs(avg_omega * d.mDifferentialRatio / float(avg_omega_denom)); // ignoring that the differentials may be rotating in different directions + driven_differentials.push_back({ &d, avg_omega, d.mEngineTorqueRatio, 0 }); + + // Remember min and max velocity + differential_omega_min = min(differential_omega_min, avg_omega); + differential_omega_max = max(differential_omega_max, avg_omega); + } + } + + if (mDifferentialLimitedSlipRatio < FLT_MAX // Limited slip differential needs to be turned on + && differential_omega_max > differential_omega_min) // There needs to be a velocity difference + { + // Calculate factor based on relative speed of a differential + float sum_factor = 0.0f; + for (DrivenDifferential &d : driven_differentials) + { + // Differential with max velocity gets factor 0, differential with min velocity 1 + d.mTempTorqueFactor = (differential_omega_max - d.mAngularVelocity) / (differential_omega_max - differential_omega_min); + sum_factor += d.mTempTorqueFactor; + } + + // Normalize the result + for (DrivenDifferential &d : driven_differentials) + d.mTempTorqueFactor /= sum_factor; + + // Prevent div by zero + differential_omega_min = max(1.0e-3f, differential_omega_min); + differential_omega_max = max(1.0e-3f, differential_omega_max); + + // Map into a value that is 0 when the wheels are turning at an equal rate and 1 when the wheels are turning at mDifferentialLimitedSlipRatio + float alpha = min((differential_omega_max / differential_omega_min - 1.0f) / (mDifferentialLimitedSlipRatio - 1.0f), 1.0f); + JPH_ASSERT(alpha >= 0.0f); + float one_min_alpha = 1.0f - alpha; + + // Update torque ratio for all differentials + for (DrivenDifferential &d : driven_differentials) + d.mClutchToDifferentialTorqueRatio = one_min_alpha * d.mClutchToDifferentialTorqueRatio + alpha * d.mTempTorqueFactor; + } + +#ifdef JPH_ENABLE_ASSERTS + // Assert the values add up to 1 + float sum_torque_factors = 0.0f; + for (DrivenDifferential &d : driven_differentials) + sum_torque_factors += d.mClutchToDifferentialTorqueRatio; + JPH_ASSERT(abs(sum_torque_factors - 1.0f) < 1.0e-6f); +#endif // JPH_ENABLE_ASSERTS + + // Define a struct that collects information about the wheels that connect to the engine + struct DrivenWheel + { + WheelWV * mWheel; + float mClutchToWheelRatio; + float mClutchToWheelTorqueRatio; + float mEstimatedAngularImpulse; + }; + Array driven_wheels; + driven_wheels.reserve(wheels.size()); + + // Collect driven wheels + float transmission_ratio = mTransmission.GetCurrentRatio(); + for (const DrivenDifferential &dd : driven_differentials) + { + VehicleDifferentialSettings d = *dd.mDifferential; + + WheelWV *wl = d.mLeftWheel != -1? static_cast(wheels[d.mLeftWheel]) : nullptr; + WheelWV *wr = d.mRightWheel != -1? static_cast(wheels[d.mRightWheel]) : nullptr; + + float clutch_to_wheel_ratio = transmission_ratio * d.mDifferentialRatio; + + if (wl != nullptr && wr != nullptr) + { + // Calculate torque ratio + float ratio_l, ratio_r; + d.CalculateTorqueRatio(wl->GetAngularVelocity(), wr->GetAngularVelocity(), ratio_l, ratio_r); + + // Add both wheels + driven_wheels.push_back({ wl, clutch_to_wheel_ratio, dd.mClutchToDifferentialTorqueRatio * ratio_l, 0.0f }); + driven_wheels.push_back({ wr, clutch_to_wheel_ratio, dd.mClutchToDifferentialTorqueRatio * ratio_r, 0.0f }); + } + else if (wl != nullptr) + { + // Only left wheel, all power to left + driven_wheels.push_back({ wl, clutch_to_wheel_ratio, dd.mClutchToDifferentialTorqueRatio, 0.0f }); + } + else if (wr != nullptr) + { + // Only right wheel, all power to right + driven_wheels.push_back({ wr, clutch_to_wheel_ratio, dd.mClutchToDifferentialTorqueRatio, 0.0f }); + } + } + + bool solved = false; + if (!driven_wheels.empty()) + { + // Define the torque at the clutch at time t as: + // + // tc(t):=S*(we(t)-sum(R(j)*ww(j,t),j,1,N)/N) + // + // Where: + // S is the total strength of clutch (= friction * strength) + // we(t) is the engine angular velocity at time t + // R(j) is the total gear ratio of clutch to wheel for wheel j + // ww(j,t) is the angular velocity of wheel j at time t + // N is the amount of wheels + // + // The torque that increases the engine angular velocity at time t is: + // + // te(t):=TE-tc(t) + // + // Where: + // TE is the torque delivered by the engine + // + // The torque that increases the wheel angular velocity for wheel i at time t is: + // + // tw(i,t):=TW(i)+R(i)*F(i)*tc(t) + // + // Where: + // TW(i) is the torque applied to the wheel outside of the engine (brake + torque due to friction with the ground) + // F(i) is the fraction of the engine torque applied from engine to wheel i + // + // Because the angular acceleration and torque are connected through: Torque = I * dw/dt + // + // We have the angular acceleration of the engine at time t: + // + // ddt_we(t):=te(t)/Ie + // + // Where: + // Ie is the inertia of the engine + // + // We have the angular acceleration of wheel i at time t: + // + // ddt_ww(i,t):=tw(i,t)/Iw(i) + // + // Where: + // Iw(i) is the inertia of wheel i + // + // We could take a simple Euler step to calculate the resulting accelerations but because the system is very stiff this turns out to be unstable, so we need to use implicit Euler instead: + // + // we(t+dt)=we(t)+dt*ddt_we(t+dt) + // + // and: + // + // ww(i,t+dt)=ww(i,t)+dt*ddt_ww(i,t+dt) + // + // Expanding both equations (the equations above are in wxMaxima format and this can easily be done by expand(%)): + // + // For wheel: + // + // ww(i,t+dt) + (S*dt*F(i)*R(i)*sum(R(j)*ww(j,t+dt),j,1,N))/(N*Iw(i)) - (S*dt*F(i)*R(i)*we(t+dt))/Iw(i) = ww(i,t)+(dt*TW(i))/Iw(i) + // + // For engine: + // + // we(t+dt) + (S*dt*we(t+dt))/Ie - (S*dt*sum(R(j)*ww(j,t+dt),j,1,N))/(Ie*N) = we(t)+(TE*dt)/Ie + // + // Defining a vector w(t) = (ww(1, t), ww(2, t), ..., ww(N, t), we(t)) we can write both equations as a matrix multiplication: + // + // a * w(t + dt) = b + // + // We then invert the matrix to get the new angular velocities. + + // Dimension of matrix is N + 1 + int n = (int)driven_wheels.size() + 1; + + // Last column of w is for the engine angular velocity + int engine = n - 1; + + // Define a and b + DynMatrix a(n, n); + DynMatrix b(n, 1); + + // Get number of driven wheels as a float + float num_driven_wheels_float = float(driven_wheels.size()); + + // Angular velocity of engine + float w_engine = mEngine.GetAngularVelocity(); + + // Calculate the total strength of the clutch + float clutch_strength = transmission_ratio != 0.0f? mTransmission.GetClutchFriction() * mTransmission.mClutchStrength : 0.0f; + + // dt / Ie + float dt_div_ie = inDeltaTime / mEngine.mInertia; + + // Calculate scale factor for impulses based on previous delta time + float impulse_scale = mPreviousDeltaTime > 0.0f? inDeltaTime / mPreviousDeltaTime : 0.0f; + + // Iterate the rows for the wheels + for (int i = 0; i < (int)driven_wheels.size(); ++i) + { + DrivenWheel &w_i = driven_wheels[i]; + const WheelSettingsWV *settings = w_i.mWheel->GetSettings(); + + // Get wheel inertia + float inertia = settings->mInertia; + + // S * R(i) + float s_r = clutch_strength * w_i.mClutchToWheelRatio; + + // dt * S * R(i) * F(i) / Iw + float dt_s_r_f_div_iw = inDeltaTime * s_r * w_i.mClutchToWheelTorqueRatio / inertia; + + // Fill in the columns of a for wheel j + for (int j = 0; j < (int)driven_wheels.size(); ++j) + { + const DrivenWheel &w_j = driven_wheels[j]; + a(i, j) = dt_s_r_f_div_iw * w_j.mClutchToWheelRatio / num_driven_wheels_float; + } + + // Add ww(i, t+dt) + a(i, i) += 1.0f; + + // Add the column for the engine + a(i, engine) = -dt_s_r_f_div_iw; + + // Calculate external angular impulse operating on the wheel: TW(i) * dt + float dt_tw = 0.0f; + + // Combine brake with hand brake torque + float brake_torque = mBrakeInput * settings->mMaxBrakeTorque + mHandBrakeInput * settings->mMaxHandBrakeTorque; + if (brake_torque > 0.0f) + { + // We're braking + // Calculate brake angular impulse + float sign; + if (w_i.mWheel->GetAngularVelocity() != 0.0f) + sign = Sign(w_i.mWheel->GetAngularVelocity()); + else + sign = Sign(mTransmission.GetCurrentRatio()); // When wheels have locked up use the transmission ratio to determine the sign + dt_tw = sign * inDeltaTime * brake_torque; + } + + if (w_i.mWheel->HasContact()) + { + // We have wheel contact with the floor + // Note that we don't know the torque due to the ground contact yet, so we use the impulse applied from the last frame to estimate it + // Wheel torque TW = force * radius = lambda / dt * radius + dt_tw += impulse_scale * w_i.mWheel->GetLongitudinalLambda() * settings->mRadius; + } + + w_i.mEstimatedAngularImpulse = dt_tw; + + // Fill in the constant b = ww(i,t)+(dt*TW(i))/Iw(i) + b(i, 0) = w_i.mWheel->GetAngularVelocity() - dt_tw / inertia; + + // To avoid looping over the wheels again, we also fill in the wheel columns of the engine row here + a(engine, i) = -dt_div_ie * s_r / num_driven_wheels_float; + } + + // Finalize the engine row + a(engine, engine) = (1.0f + dt_div_ie * clutch_strength); + b(engine, 0) = w_engine + dt_div_ie * engine_torque; + + // Solve the linear equation + if (GaussianElimination(a, b)) + { + // Update the angular velocities for the wheels + for (int i = 0; i < (int)driven_wheels.size(); ++i) + { + DrivenWheel &w_i = driven_wheels[i]; + const WheelSettingsWV *settings = w_i.mWheel->GetSettings(); + + // Get solved wheel angular velocity + float angular_velocity = b(i, 0); + + // We estimated TW and applied it in the equation above, but we haven't actually applied this torque yet so we undo it here. + // It will be applied when we solve the actual braking / the constraints with the floor. + angular_velocity += w_i.mEstimatedAngularImpulse / settings->mInertia; + + // Update angular velocity + w_i.mWheel->SetAngularVelocity(angular_velocity); + } + + // Update the engine RPM + mEngine.SetCurrentRPM(b(engine, 0) * VehicleEngine::cAngularVelocityToRPM); + + // The speeds have been solved + solved = true; + } + else + { + JPH_ASSERT(false, "New engine/wheel speeds could not be calculated!"); + } + } + + if (!solved) + { + // Engine not connected to wheels, apply all torque to engine rotation + mEngine.ApplyTorque(engine_torque, inDeltaTime); + } + + // Calculate if any of the wheels are slipping, this is used to prevent gear switching + bool wheels_slipping = false; + for (const DrivenWheel &w : driven_wheels) + wheels_slipping |= w.mClutchToWheelTorqueRatio > 0.0f && (!w.mWheel->HasContact() || w.mWheel->mLongitudinalSlip > 0.1f); + + // Only allow shifting up when we're not slipping and we're increasing our RPM. + // After a jump, we have a very high engine RPM but once we hit the ground the RPM should be decreasing and we don't want to shift up + // during that time. + bool can_shift_up = !wheels_slipping && mEngine.GetCurrentRPM() >= old_engine_rpm; + + // Update transmission + mTransmission.Update(inDeltaTime, mEngine.GetCurrentRPM(), mForwardInput, can_shift_up); + + // Braking + for (Wheel *w_base : wheels) + { + WheelWV *w = static_cast(w_base); + const WheelSettingsWV *settings = w->GetSettings(); + + // Combine brake with hand brake torque + float brake_torque = mBrakeInput * settings->mMaxBrakeTorque + mHandBrakeInput * settings->mMaxHandBrakeTorque; + if (brake_torque > 0.0f) + { + // Calculate how much torque is needed to stop the wheels from rotating in this time step + float brake_torque_to_lock_wheels = abs(w->GetAngularVelocity()) * settings->mInertia / inDeltaTime; + if (brake_torque > brake_torque_to_lock_wheels) + { + // Wheels are locked + w->SetAngularVelocity(0.0f); + w->mBrakeImpulse = (brake_torque - brake_torque_to_lock_wheels) * inDeltaTime / settings->mRadius; + } + else + { + // Slow down the wheels + w->ApplyTorque(-Sign(w->GetAngularVelocity()) * brake_torque, inDeltaTime); + w->mBrakeImpulse = 0.0f; + } + } + else + { + // Not braking + w->mBrakeImpulse = 0.0f; + } + } + + // Remember previous delta time so we can scale the impulses correctly + mPreviousDeltaTime = inDeltaTime; +} + +bool WheeledVehicleController::SolveLongitudinalAndLateralConstraints(float inDeltaTime) +{ + bool impulse = false; + + float *max_lateral_friction_impulse = (float *)JPH_STACK_ALLOC(mConstraint.GetWheels().size() * sizeof(float)); + + uint wheel_index = 0; + for (Wheel *w_base : mConstraint.GetWheels()) + { + if (w_base->HasContact()) + { + WheelWV *w = static_cast(w_base); + const WheelSettingsWV *settings = w->GetSettings(); + + // Calculate max impulse that we can apply on the ground + float max_longitudinal_friction_impulse; + mTireMaxImpulseCallback(wheel_index, + max_longitudinal_friction_impulse, max_lateral_friction_impulse[wheel_index], w->GetSuspensionLambda(), + w->mCombinedLongitudinalFriction, w->mCombinedLateralFriction, w->mLongitudinalSlip, w->mLateralSlip, inDeltaTime); + + // Calculate relative velocity between wheel contact point and floor in longitudinal direction + Vec3 relative_velocity = mConstraint.GetVehicleBody()->GetPointVelocity(w->GetContactPosition()) - w->GetContactPointVelocity(); + float relative_longitudinal_velocity = relative_velocity.Dot(w->GetContactLongitudinal()); + + // Calculate brake force to apply + float min_longitudinal_impulse, max_longitudinal_impulse; + if (w->mBrakeImpulse != 0.0f) + { + // Limit brake force by max tire friction + float brake_impulse = min(w->mBrakeImpulse, max_longitudinal_friction_impulse); + + // Check which direction the brakes should be applied (we don't want to apply an impulse that would accelerate the vehicle) + if (relative_longitudinal_velocity >= 0.0f) + { + min_longitudinal_impulse = -brake_impulse; + max_longitudinal_impulse = 0.0f; + } + else + { + min_longitudinal_impulse = 0.0f; + max_longitudinal_impulse = brake_impulse; + } + + // Longitudinal impulse, note that we assume that once the wheels are locked that the brakes have more than enough torque to keep the wheels locked so we exclude any rotation deltas + impulse |= w->SolveLongitudinalConstraintPart(mConstraint, min_longitudinal_impulse, max_longitudinal_impulse); + } + else + { + // Assume we want to apply an angular impulse that makes the delta velocity between wheel and ground zero in one time step, calculate the amount of linear impulse needed to do that + float desired_angular_velocity = relative_longitudinal_velocity / settings->mRadius; + float linear_impulse = (w->GetAngularVelocity() - desired_angular_velocity) * settings->mInertia / settings->mRadius; + + // Limit the impulse by max tire friction + float prev_lambda = w->GetLongitudinalLambda(); + min_longitudinal_impulse = max_longitudinal_impulse = Clamp(prev_lambda + linear_impulse, -max_longitudinal_friction_impulse, max_longitudinal_friction_impulse); + + // Longitudinal impulse + impulse |= w->SolveLongitudinalConstraintPart(mConstraint, min_longitudinal_impulse, max_longitudinal_impulse); + + // Update the angular velocity of the wheels according to the lambda that was applied + w->SetAngularVelocity(w->GetAngularVelocity() - (w->GetLongitudinalLambda() - prev_lambda) * settings->mRadius / settings->mInertia); + } + } + ++wheel_index; + } + + wheel_index = 0; + for (Wheel *w_base : mConstraint.GetWheels()) + { + if (w_base->HasContact()) + { + WheelWV *w = static_cast(w_base); + + // Lateral friction + float max_lateral_impulse = max_lateral_friction_impulse[wheel_index]; + impulse |= w->SolveLateralConstraintPart(mConstraint, -max_lateral_impulse, max_lateral_impulse); + } + ++wheel_index; + } + + return impulse; +} + +#ifdef JPH_DEBUG_RENDERER + +void WheeledVehicleController::Draw(DebugRenderer *inRenderer) const +{ + float constraint_size = mConstraint.GetDrawConstraintSize(); + + // Draw RPM + Body *body = mConstraint.GetVehicleBody(); + Vec3 rpm_meter_up = body->GetRotation() * mConstraint.GetLocalUp(); + RVec3 rpm_meter_pos = body->GetPosition() + body->GetRotation() * mRPMMeterPosition; + Vec3 rpm_meter_fwd = body->GetRotation() * mConstraint.GetLocalForward(); + mEngine.DrawRPM(inRenderer, rpm_meter_pos, rpm_meter_fwd, rpm_meter_up, mRPMMeterSize, mTransmission.mShiftDownRPM, mTransmission.mShiftUpRPM); + + if (mTransmission.GetCurrentRatio() != 0.0f) + { + // Calculate average wheel speed at clutch + float wheel_speed_at_clutch = GetWheelSpeedAtClutch(); + + // Draw the average wheel speed measured at clutch to compare engine RPM with wheel RPM + inRenderer->DrawLine(rpm_meter_pos, rpm_meter_pos + Quat::sRotation(rpm_meter_fwd, mEngine.ConvertRPMToAngle(wheel_speed_at_clutch)) * (rpm_meter_up * 1.1f * mRPMMeterSize), Color::sYellow); + } + + // Draw current vehicle state + String status = StringFormat("Forward: %.1f, Right: %.1f\nBrake: %.1f, HandBrake: %.1f\n" + "Gear: %d, Clutch: %.1f\nEngineRPM: %.0f, V: %.1f km/h", + (double)mForwardInput, (double)mRightInput, (double)mBrakeInput, (double)mHandBrakeInput, + mTransmission.GetCurrentGear(), (double)mTransmission.GetClutchFriction(), (double)mEngine.GetCurrentRPM(), (double)body->GetLinearVelocity().Length() * 3.6); + inRenderer->DrawText3D(body->GetPosition(), status, Color::sWhite, constraint_size); + + RMat44 body_transform = body->GetWorldTransform(); + + for (const Wheel *w_base : mConstraint.GetWheels()) + { + const WheelWV *w = static_cast(w_base); + const WheelSettings *settings = w->GetSettings(); + + // Calculate where the suspension attaches to the body in world space + RVec3 ws_position = body_transform * settings->mPosition; + Vec3 ws_direction = body_transform.Multiply3x3(settings->mSuspensionDirection); + + // Draw suspension + RVec3 min_suspension_pos = ws_position + ws_direction * settings->mSuspensionMinLength; + RVec3 max_suspension_pos = ws_position + ws_direction * settings->mSuspensionMaxLength; + inRenderer->DrawLine(ws_position, min_suspension_pos, Color::sRed); + inRenderer->DrawLine(min_suspension_pos, max_suspension_pos, Color::sGreen); + + // Draw current length + RVec3 wheel_pos = ws_position + ws_direction * w->GetSuspensionLength(); + inRenderer->DrawMarker(wheel_pos, w->GetSuspensionLength() < settings->mSuspensionMinLength? Color::sRed : Color::sGreen, constraint_size); + + // Draw wheel basis + Vec3 wheel_forward, wheel_up, wheel_right; + mConstraint.GetWheelLocalBasis(w, wheel_forward, wheel_up, wheel_right); + wheel_forward = body_transform.Multiply3x3(wheel_forward); + wheel_up = body_transform.Multiply3x3(wheel_up); + wheel_right = body_transform.Multiply3x3(wheel_right); + Vec3 steering_axis = body_transform.Multiply3x3(settings->mSteeringAxis); + inRenderer->DrawLine(wheel_pos, wheel_pos + wheel_forward, Color::sRed); + inRenderer->DrawLine(wheel_pos, wheel_pos + wheel_up, Color::sGreen); + inRenderer->DrawLine(wheel_pos, wheel_pos + wheel_right, Color::sBlue); + inRenderer->DrawLine(wheel_pos, wheel_pos + steering_axis, Color::sYellow); + + // Draw wheel + RMat44 wheel_transform(Vec4(wheel_up, 0.0f), Vec4(wheel_right, 0.0f), Vec4(wheel_forward, 0.0f), wheel_pos); + wheel_transform.SetRotation(wheel_transform.GetRotation() * Mat44::sRotationY(-w->GetRotationAngle())); + inRenderer->DrawCylinder(wheel_transform, settings->mWidth * 0.5f, settings->mRadius, w->GetSuspensionLength() <= settings->mSuspensionMinLength? Color::sRed : Color::sGreen, DebugRenderer::ECastShadow::Off, DebugRenderer::EDrawMode::Wireframe); + + if (w->HasContact()) + { + // Draw contact + inRenderer->DrawLine(w->GetContactPosition(), w->GetContactPosition() + w->GetContactNormal(), Color::sYellow); + inRenderer->DrawLine(w->GetContactPosition(), w->GetContactPosition() + w->GetContactLongitudinal(), Color::sRed); + inRenderer->DrawLine(w->GetContactPosition(), w->GetContactPosition() + w->GetContactLateral(), Color::sBlue); + + DebugRenderer::sInstance->DrawText3D(wheel_pos, StringFormat("W: %.1f, S: %.2f\nSlipLateral: %.1f, SlipLong: %.2f\nFrLateral: %.1f, FrLong: %.1f", (double)w->GetAngularVelocity(), (double)w->GetSuspensionLength(), (double)RadiansToDegrees(w->mLateralSlip), (double)w->mLongitudinalSlip, (double)w->mCombinedLateralFriction, (double)w->mCombinedLongitudinalFriction), Color::sWhite, constraint_size); + } + else + { + // Draw 'no hit' + DebugRenderer::sInstance->DrawText3D(wheel_pos, StringFormat("W: %.1f", (double)w->GetAngularVelocity()), Color::sRed, constraint_size); + } + } +} + +#endif // JPH_DEBUG_RENDERER + +void WheeledVehicleController::SaveState(StateRecorder &inStream) const +{ + inStream.Write(mForwardInput); + inStream.Write(mRightInput); + inStream.Write(mBrakeInput); + inStream.Write(mHandBrakeInput); + inStream.Write(mPreviousDeltaTime); + + mEngine.SaveState(inStream); + mTransmission.SaveState(inStream); +} + +void WheeledVehicleController::RestoreState(StateRecorder &inStream) +{ + inStream.Read(mForwardInput); + inStream.Read(mRightInput); + inStream.Read(mBrakeInput); + inStream.Read(mHandBrakeInput); + inStream.Read(mPreviousDeltaTime); + + mEngine.RestoreState(inStream); + mTransmission.RestoreState(inStream); +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/WheeledVehicleController.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/WheeledVehicleController.h new file mode 100644 index 00000000000..3d314e29c9b --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/WheeledVehicleController.h @@ -0,0 +1,199 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +class PhysicsSystem; + +/// WheelSettings object specifically for WheeledVehicleController +class JPH_EXPORT WheelSettingsWV : public WheelSettings +{ +public: + JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, WheelSettingsWV) + + /// Constructor + WheelSettingsWV(); + + // See: WheelSettings + virtual void SaveBinaryState(StreamOut &inStream) const override; + virtual void RestoreBinaryState(StreamIn &inStream) override; + + float mInertia = 0.9f; ///< Moment of inertia (kg m^2), for a cylinder this would be 0.5 * M * R^2 which is 0.9 for a wheel with a mass of 20 kg and radius 0.3 m + float mAngularDamping = 0.2f; ///< Angular damping factor of the wheel: dw/dt = -c * w + float mMaxSteerAngle = DegreesToRadians(70.0f); ///< How much this wheel can steer (radians) + LinearCurve mLongitudinalFriction; ///< On the Y-axis: friction in the forward direction of the tire. Friction is normally between 0 (no friction) and 1 (full friction) although friction can be a little bit higher than 1 because of the profile of a tire. On the X-axis: the slip ratio (fraction) defined as (omega_wheel * r_wheel - v_longitudinal) / |v_longitudinal|. You can see slip ratio as the amount the wheel is spinning relative to the floor: 0 means the wheel has full traction and is rolling perfectly in sync with the ground, 1 is for example when the wheel is locked and sliding over the ground. + LinearCurve mLateralFriction; ///< On the Y-axis: friction in the sideways direction of the tire. Friction is normally between 0 (no friction) and 1 (full friction) although friction can be a little bit higher than 1 because of the profile of a tire. On the X-axis: the slip angle (degrees) defined as angle between relative contact velocity and tire direction. + float mMaxBrakeTorque = 1500.0f; ///< How much torque (Nm) the brakes can apply to this wheel + float mMaxHandBrakeTorque = 4000.0f; ///< How much torque (Nm) the hand brake can apply to this wheel (usually only applied to the rear wheels) +}; + +/// Wheel object specifically for WheeledVehicleController +class JPH_EXPORT WheelWV : public Wheel +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Constructor + explicit WheelWV(const WheelSettingsWV &inWheel); + + /// Override GetSettings and cast to the correct class + const WheelSettingsWV * GetSettings() const { return static_cast(mSettings.GetPtr()); } + + /// Apply a torque (N m) to the wheel for a particular delta time + void ApplyTorque(float inTorque, float inDeltaTime) + { + mAngularVelocity += inTorque * inDeltaTime / GetSettings()->mInertia; + } + + /// Update the wheel rotation based on the current angular velocity + void Update(uint inWheelIndex, float inDeltaTime, const VehicleConstraint &inConstraint); + + float mLongitudinalSlip = 0.0f; ///< Velocity difference between ground and wheel relative to ground velocity + float mLateralSlip = 0.0f; ///< Angular difference (in radians) between ground and wheel relative to ground velocity + float mCombinedLongitudinalFriction = 0.0f; ///< Combined friction coefficient in longitudinal direction (combines terrain and tires) + float mCombinedLateralFriction = 0.0f; ///< Combined friction coefficient in lateral direction (combines terrain and tires) + float mBrakeImpulse = 0.0f; ///< Amount of impulse that the brakes can apply to the floor (excluding friction) +}; + +/// Settings of a vehicle with regular wheels +/// +/// The properties in this controller are largely based on "Car Physics for Games" by Marco Monster. +/// See: https://www.asawicki.info/Mirror/Car%20Physics%20for%20Games/Car%20Physics%20for%20Games.html +class JPH_EXPORT WheeledVehicleControllerSettings : public VehicleControllerSettings +{ +public: + JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, WheeledVehicleControllerSettings) + + // See: VehicleControllerSettings + virtual VehicleController * ConstructController(VehicleConstraint &inConstraint) const override; + virtual void SaveBinaryState(StreamOut &inStream) const override; + virtual void RestoreBinaryState(StreamIn &inStream) override; + + VehicleEngineSettings mEngine; ///< The properties of the engine + VehicleTransmissionSettings mTransmission; ///< The properties of the transmission (aka gear box) + Array mDifferentials; ///< List of differentials and their properties + float mDifferentialLimitedSlipRatio = 1.4f; ///< Ratio max / min average wheel speed of each differential (measured at the clutch). When the ratio is exceeded all torque gets distributed to the differential with the minimal average velocity. This allows implementing a limited slip differential between differentials. Set to FLT_MAX for an open differential. Value should be > 1. +}; + +/// Runtime controller class +class JPH_EXPORT WheeledVehicleController : public VehicleController +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Constructor + WheeledVehicleController(const WheeledVehicleControllerSettings &inSettings, VehicleConstraint &inConstraint); + + /// Typedefs + using Differentials = Array; + + /// Set input from driver + /// @param inForward Value between -1 and 1 for auto transmission and value between 0 and 1 indicating desired driving direction and amount the gas pedal is pressed + /// @param inRight Value between -1 and 1 indicating desired steering angle (1 = right) + /// @param inBrake Value between 0 and 1 indicating how strong the brake pedal is pressed + /// @param inHandBrake Value between 0 and 1 indicating how strong the hand brake is pulled + void SetDriverInput(float inForward, float inRight, float inBrake, float inHandBrake) { mForwardInput = inForward; mRightInput = inRight; mBrakeInput = inBrake; mHandBrakeInput = inHandBrake; } + + /// Value between -1 and 1 for auto transmission and value between 0 and 1 indicating desired driving direction and amount the gas pedal is pressed + void SetForwardInput(float inForward) { mForwardInput = inForward; } + float GetForwardInput() const { return mForwardInput; } + + /// Value between -1 and 1 indicating desired steering angle (1 = right) + void SetRightInput(float inRight) { mRightInput = inRight; } + float GetRightInput() const { return mRightInput; } + + /// Value between 0 and 1 indicating how strong the brake pedal is pressed + void SetBrakeInput(float inBrake) { mBrakeInput = inBrake; } + float GetBrakeInput() const { return mBrakeInput; } + + /// Value between 0 and 1 indicating how strong the hand brake is pulled + void SetHandBrakeInput(float inHandBrake) { mHandBrakeInput = inHandBrake; } + float GetHandBrakeInput() const { return mHandBrakeInput; } + + /// Get current engine state + const VehicleEngine & GetEngine() const { return mEngine; } + + /// Get current engine state (writable interface, allows you to make changes to the configuration which will take effect the next time step) + VehicleEngine & GetEngine() { return mEngine; } + + /// Get current transmission state + const VehicleTransmission & GetTransmission() const { return mTransmission; } + + /// Get current transmission state (writable interface, allows you to make changes to the configuration which will take effect the next time step) + VehicleTransmission & GetTransmission() { return mTransmission; } + + /// Get the differentials this vehicle has + const Differentials & GetDifferentials() const { return mDifferentials; } + + /// Get the differentials this vehicle has (writable interface, allows you to make changes to the configuration which will take effect the next time step) + Differentials & GetDifferentials() { return mDifferentials; } + + /// Ratio max / min average wheel speed of each differential (measured at the clutch). + float GetDifferentialLimitedSlipRatio() const { return mDifferentialLimitedSlipRatio; } + void SetDifferentialLimitedSlipRatio(float inV) { mDifferentialLimitedSlipRatio = inV; } + + /// Get the average wheel speed of all driven wheels (measured at the clutch) + float GetWheelSpeedAtClutch() const; + + /// Calculate max tire impulses by combining friction, slip, and suspension impulse. Note that the actual applied impulse may be lower (e.g. when the vehicle is stationary on a horizontal surface the actual impulse applied will be 0). + using TireMaxImpulseCallback = function; + const TireMaxImpulseCallback&GetTireMaxImpulseCallback() const { return mTireMaxImpulseCallback; } + void SetTireMaxImpulseCallback(const TireMaxImpulseCallback &inTireMaxImpulseCallback) { mTireMaxImpulseCallback = inTireMaxImpulseCallback; } + +#ifdef JPH_DEBUG_RENDERER + /// Debug drawing of RPM meter + void SetRPMMeter(Vec3Arg inPosition, float inSize) { mRPMMeterPosition = inPosition; mRPMMeterSize = inSize; } +#endif // JPH_DEBUG_RENDERER + +protected: + // See: VehicleController + virtual Wheel * ConstructWheel(const WheelSettings &inWheel) const override { JPH_ASSERT(IsKindOf(&inWheel, JPH_RTTI(WheelSettingsWV))); return new WheelWV(static_cast(inWheel)); } + virtual bool AllowSleep() const override; + virtual void PreCollide(float inDeltaTime, PhysicsSystem &inPhysicsSystem) override; + virtual void PostCollide(float inDeltaTime, PhysicsSystem &inPhysicsSystem) override; + virtual bool SolveLongitudinalAndLateralConstraints(float inDeltaTime) override; + virtual void SaveState(StateRecorder &inStream) const override; + virtual void RestoreState(StateRecorder &inStream) override; +#ifdef JPH_DEBUG_RENDERER + virtual void Draw(DebugRenderer *inRenderer) const override; +#endif // JPH_DEBUG_RENDERER + + // Control information + float mForwardInput = 0.0f; ///< Value between -1 and 1 for auto transmission and value between 0 and 1 indicating desired driving direction and amount the gas pedal is pressed + float mRightInput = 0.0f; ///< Value between -1 and 1 indicating desired steering angle + float mBrakeInput = 0.0f; ///< Value between 0 and 1 indicating how strong the brake pedal is pressed + float mHandBrakeInput = 0.0f; ///< Value between 0 and 1 indicating how strong the hand brake is pulled + + // Simulation information + VehicleEngine mEngine; ///< Engine state of the vehicle + VehicleTransmission mTransmission; ///< Transmission state of the vehicle + Differentials mDifferentials; ///< Differential states of the vehicle + float mDifferentialLimitedSlipRatio; ///< Ratio max / min average wheel speed of each differential (measured at the clutch). + float mPreviousDeltaTime = 0.0f; ///< Delta time of the last step + + // Callback that calculates the max impulse that the tire can apply to the ground + TireMaxImpulseCallback mTireMaxImpulseCallback = + [](uint, float &outLongitudinalImpulse, float &outLateralImpulse, float inSuspensionImpulse, float inLongitudinalFriction, float inLateralFriction, float, float, float) + { + outLongitudinalImpulse = inLongitudinalFriction * inSuspensionImpulse; + outLateralImpulse = inLateralFriction * inSuspensionImpulse; + }; + +#ifdef JPH_DEBUG_RENDERER + // Debug settings + Vec3 mRPMMeterPosition { 0, 1, 0 }; ///< Position (in local space of the body) of the RPM meter when drawing the constraint + float mRPMMeterSize = 0.5f; ///< Size of the RPM meter when drawing the constraint +#endif // JPH_DEBUG_RENDERER +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/RegisterTypes.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/RegisterTypes.cpp new file mode 100644 index 00000000000..d9f9b896931 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/RegisterTypes.cpp @@ -0,0 +1,197 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, Skeleton) +JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, SkeletalAnimation) +JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, CompoundShapeSettings) +JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, StaticCompoundShapeSettings) +JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, MutableCompoundShapeSettings) +JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, TriangleShapeSettings) +JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, SphereShapeSettings) +JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, BoxShapeSettings) +JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, CapsuleShapeSettings) +JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, TaperedCapsuleShapeSettings) +JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, CylinderShapeSettings) +JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, ScaledShapeSettings) +JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, MeshShapeSettings) +JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, ConvexHullShapeSettings) +JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, HeightFieldShapeSettings) +JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, RotatedTranslatedShapeSettings) +JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, OffsetCenterOfMassShapeSettings) +JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, RagdollSettings) +JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, PointConstraintSettings) +JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, SixDOFConstraintSettings) +JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, SliderConstraintSettings) +JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, SwingTwistConstraintSettings) +JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, DistanceConstraintSettings) +JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, HingeConstraintSettings) +JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, FixedConstraintSettings) +JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, ConeConstraintSettings) +JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, PathConstraintSettings) +JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, PathConstraintPath) +JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, PathConstraintPathHermite) +JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, VehicleConstraintSettings) +JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, WheeledVehicleControllerSettings) +JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, RackAndPinionConstraintSettings) +JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, GearConstraintSettings) +JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, PulleyConstraintSettings) +JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, MotorSettings) +JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, PhysicsScene) +JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, PhysicsMaterial) +JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, GroupFilter) +JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, GroupFilterTable) +JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, BodyCreationSettings) +JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, SoftBodyCreationSettings) + +JPH_NAMESPACE_BEGIN + +bool VerifyJoltVersionIDInternal(uint64 inVersionID) +{ + return inVersionID == JPH_VERSION_ID; +} + +void RegisterTypesInternal(uint64 inVersionID) +{ + // Version check + if (!VerifyJoltVersionIDInternal(inVersionID)) + { + Trace("Version mismatch, make sure you compile the client code with the same Jolt version and compiler definitions!"); + uint64 mismatch = JPH_VERSION_ID ^ inVersionID; + auto check_bit = [mismatch](int inBit, const char *inLabel) { if (mismatch & (uint64(1) << (inBit + 23))) Trace("Mismatching define %s.", inLabel); }; + check_bit(1, "JPH_DOUBLE_PRECISION"); + check_bit(2, "JPH_CROSS_PLATFORM_DETERMINISTIC"); + check_bit(3, "JPH_FLOATING_POINT_EXCEPTIONS_ENABLED"); + check_bit(4, "JPH_PROFILE_ENABLED"); + check_bit(5, "JPH_EXTERNAL_PROFILE"); + check_bit(6, "JPH_DEBUG_RENDERER"); + check_bit(7, "JPH_DISABLE_TEMP_ALLOCATOR"); + check_bit(8, "JPH_DISABLE_CUSTOM_ALLOCATOR"); + check_bit(9, "JPH_OBJECT_LAYER_BITS"); + check_bit(10, "JPH_ENABLE_ASSERTS"); + std::abort(); + } + +#ifndef JPH_DISABLE_CUSTOM_ALLOCATOR + JPH_ASSERT(Allocate != nullptr && Free != nullptr && AlignedAllocate != nullptr && AlignedFree != nullptr, "Need to supply an allocator first or call RegisterDefaultAllocator()"); +#endif // !JPH_DISABLE_CUSTOM_ALLOCATOR + + JPH_ASSERT(Factory::sInstance != nullptr, "Need to create a factory first!"); + + // Initialize dispatcher + CollisionDispatch::sInit(); + + // Register base classes first so that we can specialize them later + CompoundShape::sRegister(); + ConvexShape::sRegister(); + + // Register compounds before others so that we can specialize them later (register them in reverse order of collision complexity) + MutableCompoundShape::sRegister(); + StaticCompoundShape::sRegister(); + + // Leaf classes + TriangleShape::sRegister(); + SphereShape::sRegister(); + BoxShape::sRegister(); + CapsuleShape::sRegister(); + TaperedCapsuleShape::sRegister(); + CylinderShape::sRegister(); + MeshShape::sRegister(); + ConvexHullShape::sRegister(); + HeightFieldShape::sRegister(); + SoftBodyShape::sRegister(); + + // Register these last because their collision functions are simple so we want to execute them first (register them in reverse order of collision complexity) + RotatedTranslatedShape::sRegister(); + OffsetCenterOfMassShape::sRegister(); + ScaledShape::sRegister(); + + // Create list of all types + const RTTI *types[] = { + JPH_RTTI(SkeletalAnimation), + JPH_RTTI(Skeleton), + JPH_RTTI(CompoundShapeSettings), + JPH_RTTI(StaticCompoundShapeSettings), + JPH_RTTI(MutableCompoundShapeSettings), + JPH_RTTI(TriangleShapeSettings), + JPH_RTTI(SphereShapeSettings), + JPH_RTTI(BoxShapeSettings), + JPH_RTTI(CapsuleShapeSettings), + JPH_RTTI(TaperedCapsuleShapeSettings), + JPH_RTTI(CylinderShapeSettings), + JPH_RTTI(ScaledShapeSettings), + JPH_RTTI(MeshShapeSettings), + JPH_RTTI(ConvexHullShapeSettings), + JPH_RTTI(HeightFieldShapeSettings), + JPH_RTTI(RotatedTranslatedShapeSettings), + JPH_RTTI(OffsetCenterOfMassShapeSettings), + JPH_RTTI(RagdollSettings), + JPH_RTTI(PointConstraintSettings), + JPH_RTTI(SixDOFConstraintSettings), + JPH_RTTI(SliderConstraintSettings), + JPH_RTTI(SwingTwistConstraintSettings), + JPH_RTTI(DistanceConstraintSettings), + JPH_RTTI(HingeConstraintSettings), + JPH_RTTI(FixedConstraintSettings), + JPH_RTTI(ConeConstraintSettings), + JPH_RTTI(PathConstraintSettings), + JPH_RTTI(VehicleConstraintSettings), + JPH_RTTI(WheeledVehicleControllerSettings), + JPH_RTTI(PathConstraintPath), + JPH_RTTI(PathConstraintPathHermite), + JPH_RTTI(RackAndPinionConstraintSettings), + JPH_RTTI(GearConstraintSettings), + JPH_RTTI(PulleyConstraintSettings), + JPH_RTTI(MotorSettings), + JPH_RTTI(PhysicsScene), + JPH_RTTI(PhysicsMaterial), + JPH_RTTI(PhysicsMaterialSimple), + JPH_RTTI(GroupFilter), + JPH_RTTI(GroupFilterTable), + JPH_RTTI(BodyCreationSettings), + JPH_RTTI(SoftBodyCreationSettings) + }; + + // Register them all + Factory::sInstance->Register(types, (uint)size(types)); + + // Initialize default physics material + if (PhysicsMaterial::sDefault == nullptr) + PhysicsMaterial::sDefault = new PhysicsMaterialSimple("Default", Color::sGrey); +} + +void UnregisterTypes() +{ + // Unregister all types + if (Factory::sInstance != nullptr) + Factory::sInstance->Clear(); + + // Delete default physics material + PhysicsMaterial::sDefault = nullptr; +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/RegisterTypes.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/RegisterTypes.h new file mode 100644 index 00000000000..372ef7f4f40 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/RegisterTypes.h @@ -0,0 +1,29 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +JPH_NAMESPACE_BEGIN + +/// Internal helper function +JPH_EXPORT extern bool VerifyJoltVersionIDInternal(uint64 inVersionID); + +/// This function can be used to verify the library ABI is compatible with your +/// application. +/// Use it in this way: `assert(VerifyJoltVersionID());`. +/// Returns `false` if the library used is not compatible with your app. +JPH_INLINE bool VerifyJoltVersionID() { return VerifyJoltVersionIDInternal(JPH_VERSION_ID); } + +/// Internal helper function +JPH_EXPORT extern void RegisterTypesInternal(uint64 inVersionID); + +/// Register all physics types with the factory and install their collision handlers with the CollisionDispatch class. +/// If you have your own custom shape types you probably need to register their handlers with the CollisionDispatch before calling this function. +/// If you implement your own default material (PhysicsMaterial::sDefault) make sure to initialize it before this function or else this function will create one for you. +JPH_INLINE void RegisterTypes() { RegisterTypesInternal(JPH_VERSION_ID); } + +/// Unregisters all types with the factory and cleans up the default material +JPH_EXPORT extern void UnregisterTypes(); + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Renderer/DebugRenderer.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Renderer/DebugRenderer.cpp new file mode 100644 index 00000000000..44025aaff9f --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Renderer/DebugRenderer.cpp @@ -0,0 +1,1071 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#ifdef JPH_DEBUG_RENDERER + +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +DebugRenderer *DebugRenderer::sInstance = nullptr; + +// Number of LOD levels to create +static const int sMaxLevel = 4; + +// Distance for each LOD level, these are tweaked for an object of approx. size 1. Use the lod scale to scale these distances. +static const float sLODDistanceForLevel[] = { 5.0f, 10.0f, 40.0f, FLT_MAX }; + +DebugRenderer::Triangle::Triangle(Vec3Arg inV1, Vec3Arg inV2, Vec3Arg inV3, ColorArg inColor) +{ + // Set position + inV1.StoreFloat3(&mV[0].mPosition); + inV2.StoreFloat3(&mV[1].mPosition); + inV3.StoreFloat3(&mV[2].mPosition); + + // Set color + mV[0].mColor = mV[1].mColor = mV[2].mColor = inColor; + + // Calculate normal + Vec3 normal = (inV2 - inV1).Cross(inV3 - inV1); + float normal_len = normal.Length(); + if (normal_len > 0.0f) + normal /= normal_len; + Float3 normal3; + normal.StoreFloat3(&normal3); + mV[0].mNormal = mV[1].mNormal = mV[2].mNormal = normal3; + + // Reset UV's + mV[0].mUV = mV[1].mUV = mV[2].mUV = { 0, 0 }; +} + +DebugRenderer::Triangle::Triangle(Vec3Arg inV1, Vec3Arg inV2, Vec3Arg inV3, ColorArg inColor, Vec3Arg inUVOrigin, Vec3Arg inUVDirection) +{ + // Set position + inV1.StoreFloat3(&mV[0].mPosition); + inV2.StoreFloat3(&mV[1].mPosition); + inV3.StoreFloat3(&mV[2].mPosition); + + // Set color + mV[0].mColor = mV[1].mColor = mV[2].mColor = inColor; + + // Calculate normal + Vec3 normal = (inV2 - inV1).Cross(inV3 - inV1).Normalized(); + Float3 normal3; + normal.StoreFloat3(&normal3); + mV[0].mNormal = mV[1].mNormal = mV[2].mNormal = normal3; + + // Set UV's + Vec3 uv1 = inV1 - inUVOrigin; + Vec3 uv2 = inV2 - inUVOrigin; + Vec3 uv3 = inV3 - inUVOrigin; + Vec3 axis2 = normal.Cross(inUVDirection); + mV[0].mUV = { inUVDirection.Dot(uv1), axis2.Dot(uv1) }; + mV[1].mUV = { inUVDirection.Dot(uv2), axis2.Dot(uv2) }; + mV[2].mUV = { inUVDirection.Dot(uv3), axis2.Dot(uv3) }; +} + +DebugRenderer::DebugRenderer() +{ + // Store singleton + JPH_ASSERT(sInstance == nullptr); + sInstance = this; +} + +DebugRenderer::~DebugRenderer() +{ + JPH_ASSERT(sInstance == this); + sInstance = nullptr; +} + +void DebugRenderer::DrawWireBox(const AABox &inBox, ColorArg inColor) +{ + JPH_PROFILE_FUNCTION(); + + // 8 vertices + RVec3 v1(Real(inBox.mMin.GetX()), Real(inBox.mMin.GetY()), Real(inBox.mMin.GetZ())); + RVec3 v2(Real(inBox.mMin.GetX()), Real(inBox.mMin.GetY()), Real(inBox.mMax.GetZ())); + RVec3 v3(Real(inBox.mMin.GetX()), Real(inBox.mMax.GetY()), Real(inBox.mMin.GetZ())); + RVec3 v4(Real(inBox.mMin.GetX()), Real(inBox.mMax.GetY()), Real(inBox.mMax.GetZ())); + RVec3 v5(Real(inBox.mMax.GetX()), Real(inBox.mMin.GetY()), Real(inBox.mMin.GetZ())); + RVec3 v6(Real(inBox.mMax.GetX()), Real(inBox.mMin.GetY()), Real(inBox.mMax.GetZ())); + RVec3 v7(Real(inBox.mMax.GetX()), Real(inBox.mMax.GetY()), Real(inBox.mMin.GetZ())); + RVec3 v8(Real(inBox.mMax.GetX()), Real(inBox.mMax.GetY()), Real(inBox.mMax.GetZ())); + + // 12 edges + DrawLine(v1, v2, inColor); + DrawLine(v1, v3, inColor); + DrawLine(v1, v5, inColor); + DrawLine(v2, v4, inColor); + DrawLine(v2, v6, inColor); + DrawLine(v3, v4, inColor); + DrawLine(v3, v7, inColor); + DrawLine(v4, v8, inColor); + DrawLine(v5, v6, inColor); + DrawLine(v5, v7, inColor); + DrawLine(v6, v8, inColor); + DrawLine(v7, v8, inColor); +} + +void DebugRenderer::DrawWireBox(const OrientedBox &inBox, ColorArg inColor) +{ + JPH_PROFILE_FUNCTION(); + + // 8 vertices + RVec3 v1(inBox.mOrientation * Vec3(-inBox.mHalfExtents.GetX(), -inBox.mHalfExtents.GetY(), -inBox.mHalfExtents.GetZ())); + RVec3 v2(inBox.mOrientation * Vec3(-inBox.mHalfExtents.GetX(), -inBox.mHalfExtents.GetY(), inBox.mHalfExtents.GetZ())); + RVec3 v3(inBox.mOrientation * Vec3(-inBox.mHalfExtents.GetX(), inBox.mHalfExtents.GetY(), -inBox.mHalfExtents.GetZ())); + RVec3 v4(inBox.mOrientation * Vec3(-inBox.mHalfExtents.GetX(), inBox.mHalfExtents.GetY(), inBox.mHalfExtents.GetZ())); + RVec3 v5(inBox.mOrientation * Vec3(inBox.mHalfExtents.GetX(), -inBox.mHalfExtents.GetY(), -inBox.mHalfExtents.GetZ())); + RVec3 v6(inBox.mOrientation * Vec3(inBox.mHalfExtents.GetX(), -inBox.mHalfExtents.GetY(), inBox.mHalfExtents.GetZ())); + RVec3 v7(inBox.mOrientation * Vec3(inBox.mHalfExtents.GetX(), inBox.mHalfExtents.GetY(), -inBox.mHalfExtents.GetZ())); + RVec3 v8(inBox.mOrientation * Vec3(inBox.mHalfExtents.GetX(), inBox.mHalfExtents.GetY(), inBox.mHalfExtents.GetZ())); + + // 12 edges + DrawLine(v1, v2, inColor); + DrawLine(v1, v3, inColor); + DrawLine(v1, v5, inColor); + DrawLine(v2, v4, inColor); + DrawLine(v2, v6, inColor); + DrawLine(v3, v4, inColor); + DrawLine(v3, v7, inColor); + DrawLine(v4, v8, inColor); + DrawLine(v5, v6, inColor); + DrawLine(v5, v7, inColor); + DrawLine(v6, v8, inColor); + DrawLine(v7, v8, inColor); +} + +void DebugRenderer::DrawWireBox(RMat44Arg inMatrix, const AABox &inBox, ColorArg inColor) +{ + JPH_PROFILE_FUNCTION(); + + // 8 vertices + RVec3 v1 = inMatrix * Vec3(inBox.mMin.GetX(), inBox.mMin.GetY(), inBox.mMin.GetZ()); + RVec3 v2 = inMatrix * Vec3(inBox.mMin.GetX(), inBox.mMin.GetY(), inBox.mMax.GetZ()); + RVec3 v3 = inMatrix * Vec3(inBox.mMin.GetX(), inBox.mMax.GetY(), inBox.mMin.GetZ()); + RVec3 v4 = inMatrix * Vec3(inBox.mMin.GetX(), inBox.mMax.GetY(), inBox.mMax.GetZ()); + RVec3 v5 = inMatrix * Vec3(inBox.mMax.GetX(), inBox.mMin.GetY(), inBox.mMin.GetZ()); + RVec3 v6 = inMatrix * Vec3(inBox.mMax.GetX(), inBox.mMin.GetY(), inBox.mMax.GetZ()); + RVec3 v7 = inMatrix * Vec3(inBox.mMax.GetX(), inBox.mMax.GetY(), inBox.mMin.GetZ()); + RVec3 v8 = inMatrix * Vec3(inBox.mMax.GetX(), inBox.mMax.GetY(), inBox.mMax.GetZ()); + + // 12 edges + DrawLine(v1, v2, inColor); + DrawLine(v1, v3, inColor); + DrawLine(v1, v5, inColor); + DrawLine(v2, v4, inColor); + DrawLine(v2, v6, inColor); + DrawLine(v3, v4, inColor); + DrawLine(v3, v7, inColor); + DrawLine(v4, v8, inColor); + DrawLine(v5, v6, inColor); + DrawLine(v5, v7, inColor); + DrawLine(v6, v8, inColor); + DrawLine(v7, v8, inColor); +} + +void DebugRenderer::DrawMarker(RVec3Arg inPosition, ColorArg inColor, float inSize) +{ + JPH_PROFILE_FUNCTION(); + + Vec3 dx(inSize, 0, 0); + Vec3 dy(0, inSize, 0); + Vec3 dz(0, 0, inSize); + DrawLine(inPosition - dy, inPosition + dy, inColor); + DrawLine(inPosition - dx, inPosition + dx, inColor); + DrawLine(inPosition - dz, inPosition + dz, inColor); +} + +void DebugRenderer::DrawArrow(RVec3Arg inFrom, RVec3Arg inTo, ColorArg inColor, float inSize) +{ + JPH_PROFILE_FUNCTION(); + + // Draw base line + DrawLine(inFrom, inTo, inColor); + + if (inSize > 0.0f) + { + // Draw arrow head + Vec3 dir = Vec3(inTo - inFrom); + float len = dir.Length(); + if (len != 0.0f) + dir = dir * (inSize / len); + else + dir = Vec3(inSize, 0, 0); + Vec3 perp = inSize * dir.GetNormalizedPerpendicular(); + DrawLine(inTo - dir + perp, inTo, inColor); + DrawLine(inTo - dir - perp, inTo, inColor); + } +} + +void DebugRenderer::DrawCoordinateSystem(RMat44Arg inTransform, float inSize) +{ + JPH_PROFILE_FUNCTION(); + + DrawArrow(inTransform.GetTranslation(), inTransform * Vec3(inSize, 0, 0), Color::sRed, 0.1f * inSize); + DrawArrow(inTransform.GetTranslation(), inTransform * Vec3(0, inSize, 0), Color::sGreen, 0.1f * inSize); + DrawArrow(inTransform.GetTranslation(), inTransform * Vec3(0, 0, inSize), Color::sBlue, 0.1f * inSize); +} + +void DebugRenderer::DrawPlane(RVec3Arg inPoint, Vec3Arg inNormal, ColorArg inColor, float inSize) +{ + // Create orthogonal basis + Vec3 perp1 = inNormal.Cross(Vec3::sAxisY()).NormalizedOr(Vec3::sAxisX()); + Vec3 perp2 = perp1.Cross(inNormal).Normalized(); + perp1 = inNormal.Cross(perp2); + + // Calculate corners + RVec3 corner1 = inPoint + inSize * (perp1 + perp2); + RVec3 corner2 = inPoint + inSize * (perp1 - perp2); + RVec3 corner3 = inPoint + inSize * (-perp1 - perp2); + RVec3 corner4 = inPoint + inSize * (-perp1 + perp2); + + // Draw cross + DrawLine(corner1, corner3, inColor); + DrawLine(corner2, corner4, inColor); + + // Draw square + DrawLine(corner1, corner2, inColor); + DrawLine(corner2, corner3, inColor); + DrawLine(corner3, corner4, inColor); + DrawLine(corner4, corner1, inColor); + + // Draw normal + DrawArrow(inPoint, inPoint + inSize * inNormal, inColor, 0.1f * inSize); +} + +void DebugRenderer::DrawWireTriangle(RVec3Arg inV1, RVec3Arg inV2, RVec3Arg inV3, ColorArg inColor) +{ + JPH_PROFILE_FUNCTION(); + + DrawLine(inV1, inV2, inColor); + DrawLine(inV2, inV3, inColor); + DrawLine(inV3, inV1, inColor); +} + +void DebugRenderer::DrawWireSphere(RVec3Arg inCenter, float inRadius, ColorArg inColor, int inLevel) +{ + RMat44 matrix = RMat44::sTranslation(inCenter) * Mat44::sScale(inRadius); + + DrawWireUnitSphere(matrix, inColor, inLevel); +} + +void DebugRenderer::DrawWireUnitSphere(RMat44Arg inMatrix, ColorArg inColor, int inLevel) +{ + JPH_PROFILE_FUNCTION(); + + DrawWireUnitSphereRecursive(inMatrix, inColor, Vec3::sAxisX(), Vec3::sAxisY(), Vec3::sAxisZ(), inLevel); + DrawWireUnitSphereRecursive(inMatrix, inColor, -Vec3::sAxisX(), Vec3::sAxisY(), Vec3::sAxisZ(), inLevel); + DrawWireUnitSphereRecursive(inMatrix, inColor, Vec3::sAxisX(), -Vec3::sAxisY(), Vec3::sAxisZ(), inLevel); + DrawWireUnitSphereRecursive(inMatrix, inColor, -Vec3::sAxisX(), -Vec3::sAxisY(), Vec3::sAxisZ(), inLevel); + DrawWireUnitSphereRecursive(inMatrix, inColor, Vec3::sAxisX(), Vec3::sAxisY(), -Vec3::sAxisZ(), inLevel); + DrawWireUnitSphereRecursive(inMatrix, inColor, -Vec3::sAxisX(), Vec3::sAxisY(), -Vec3::sAxisZ(), inLevel); + DrawWireUnitSphereRecursive(inMatrix, inColor, Vec3::sAxisX(), -Vec3::sAxisY(), -Vec3::sAxisZ(), inLevel); + DrawWireUnitSphereRecursive(inMatrix, inColor, -Vec3::sAxisX(), -Vec3::sAxisY(), -Vec3::sAxisZ(), inLevel); +} + +void DebugRenderer::DrawWireUnitSphereRecursive(RMat44Arg inMatrix, ColorArg inColor, Vec3Arg inDir1, Vec3Arg inDir2, Vec3Arg inDir3, int inLevel) +{ + if (inLevel == 0) + { + RVec3 d1 = inMatrix * inDir1; + RVec3 d2 = inMatrix * inDir2; + RVec3 d3 = inMatrix * inDir3; + + DrawLine(d1, d2, inColor); + DrawLine(d2, d3, inColor); + DrawLine(d3, d1, inColor); + } + else + { + Vec3 center1 = (inDir1 + inDir2).Normalized(); + Vec3 center2 = (inDir2 + inDir3).Normalized(); + Vec3 center3 = (inDir3 + inDir1).Normalized(); + + DrawWireUnitSphereRecursive(inMatrix, inColor, inDir1, center1, center3, inLevel - 1); + DrawWireUnitSphereRecursive(inMatrix, inColor, center1, center2, center3, inLevel - 1); + DrawWireUnitSphereRecursive(inMatrix, inColor, center1, inDir2, center2, inLevel - 1); + DrawWireUnitSphereRecursive(inMatrix, inColor, center3, center2, inDir3, inLevel - 1); + } +} + +void DebugRenderer::Create8thSphereRecursive(Array &ioIndices, Array &ioVertices, Vec3Arg inDir1, uint32 &ioIdx1, Vec3Arg inDir2, uint32 &ioIdx2, Vec3Arg inDir3, uint32 &ioIdx3, const Float2 &inUV, SupportFunction inGetSupport, int inLevel) +{ + if (inLevel == 0) + { + if (ioIdx1 == 0xffffffff) + { + ioIdx1 = (uint32)ioVertices.size(); + Float3 position, normal; + inGetSupport(inDir1).StoreFloat3(&position); + inDir1.StoreFloat3(&normal); + ioVertices.push_back({ position, normal, inUV, Color::sWhite }); + } + + if (ioIdx2 == 0xffffffff) + { + ioIdx2 = (uint32)ioVertices.size(); + Float3 position, normal; + inGetSupport(inDir2).StoreFloat3(&position); + inDir2.StoreFloat3(&normal); + ioVertices.push_back({ position, normal, inUV, Color::sWhite }); + } + + if (ioIdx3 == 0xffffffff) + { + ioIdx3 = (uint32)ioVertices.size(); + Float3 position, normal; + inGetSupport(inDir3).StoreFloat3(&position); + inDir3.StoreFloat3(&normal); + ioVertices.push_back({ position, normal, inUV, Color::sWhite }); + } + + ioIndices.push_back(ioIdx1); + ioIndices.push_back(ioIdx2); + ioIndices.push_back(ioIdx3); + } + else + { + Vec3 center1 = (inDir1 + inDir2).Normalized(); + Vec3 center2 = (inDir2 + inDir3).Normalized(); + Vec3 center3 = (inDir3 + inDir1).Normalized(); + + uint32 idx1 = 0xffffffff; + uint32 idx2 = 0xffffffff; + uint32 idx3 = 0xffffffff; + + Create8thSphereRecursive(ioIndices, ioVertices, inDir1, ioIdx1, center1, idx1, center3, idx3, inUV, inGetSupport, inLevel - 1); + Create8thSphereRecursive(ioIndices, ioVertices, center1, idx1, center2, idx2, center3, idx3, inUV, inGetSupport, inLevel - 1); + Create8thSphereRecursive(ioIndices, ioVertices, center1, idx1, inDir2, ioIdx2, center2, idx2, inUV, inGetSupport, inLevel - 1); + Create8thSphereRecursive(ioIndices, ioVertices, center3, idx3, center2, idx2, inDir3, ioIdx3, inUV, inGetSupport, inLevel - 1); + } +} + +void DebugRenderer::Create8thSphere(Array &ioIndices, Array &ioVertices, Vec3Arg inDir1, Vec3Arg inDir2, Vec3Arg inDir3, const Float2 &inUV, SupportFunction inGetSupport, int inLevel) +{ + uint32 idx1 = 0xffffffff; + uint32 idx2 = 0xffffffff; + uint32 idx3 = 0xffffffff; + + Create8thSphereRecursive(ioIndices, ioVertices, inDir1, idx1, inDir2, idx2, inDir3, idx3, inUV, inGetSupport, inLevel); +} + +void DebugRenderer::CreateQuad(Array &ioIndices, Array &ioVertices, Vec3Arg inV1, Vec3Arg inV2, Vec3Arg inV3, Vec3Arg inV4) +{ + // Make room + uint32 start_idx = uint32(ioVertices.size()); + ioVertices.resize(start_idx + 4); + Vertex *vertices = &ioVertices[start_idx]; + + // Set position + inV1.StoreFloat3(&vertices[0].mPosition); + inV2.StoreFloat3(&vertices[1].mPosition); + inV3.StoreFloat3(&vertices[2].mPosition); + inV4.StoreFloat3(&vertices[3].mPosition); + + // Set color + vertices[0].mColor = vertices[1].mColor = vertices[2].mColor = vertices[3].mColor = Color::sWhite; + + // Calculate normal + Vec3 normal = (inV2 - inV1).Cross(inV3 - inV1).Normalized(); + Float3 normal3; + normal.StoreFloat3(&normal3); + vertices[0].mNormal = vertices[1].mNormal = vertices[2].mNormal = vertices[3].mNormal = normal3; + + // Set UV's + vertices[0].mUV = { 0, 0 }; + vertices[1].mUV = { 2, 0 }; + vertices[2].mUV = { 2, 2 }; + vertices[3].mUV = { 0, 2 }; + + // Set indices + ioIndices.push_back(start_idx); + ioIndices.push_back(start_idx + 1); + ioIndices.push_back(start_idx + 2); + + ioIndices.push_back(start_idx); + ioIndices.push_back(start_idx + 2); + ioIndices.push_back(start_idx + 3); +} + +void DebugRenderer::Initialize() +{ + // Box + { + Array box_vertices; + Array box_indices; + + // Get corner points + Vec3 v0 = Vec3(-1, 1, -1); + Vec3 v1 = Vec3( 1, 1, -1); + Vec3 v2 = Vec3( 1, 1, 1); + Vec3 v3 = Vec3(-1, 1, 1); + Vec3 v4 = Vec3(-1, -1, -1); + Vec3 v5 = Vec3( 1, -1, -1); + Vec3 v6 = Vec3( 1, -1, 1); + Vec3 v7 = Vec3(-1, -1, 1); + + // Top + CreateQuad(box_indices, box_vertices, v0, v3, v2, v1); + + // Bottom + CreateQuad(box_indices, box_vertices, v4, v5, v6, v7); + + // Left + CreateQuad(box_indices, box_vertices, v0, v4, v7, v3); + + // Right + CreateQuad(box_indices, box_vertices, v2, v6, v5, v1); + + // Front + CreateQuad(box_indices, box_vertices, v3, v7, v6, v2); + + // Back + CreateQuad(box_indices, box_vertices, v0, v1, v5, v4); + + mBox = new Geometry(CreateTriangleBatch(box_vertices, box_indices), AABox(Vec3(-1, -1, -1), Vec3(1, 1, 1))); + } + + // Support function that returns a unit sphere + auto sphere_support = [](Vec3Arg inDirection) { return inDirection; }; + + // Construct geometries + mSphere = new Geometry(AABox(Vec3(-1, -1, -1), Vec3(1, 1, 1))); + mCapsuleBottom = new Geometry(AABox(Vec3(-1, -1, -1), Vec3(1, 0, 1))); + mCapsuleTop = new Geometry(AABox(Vec3(-1, 0, -1), Vec3(1, 1, 1))); + mCapsuleMid = new Geometry(AABox(Vec3(-1, -1, -1), Vec3(1, 1, 1))); + mOpenCone = new Geometry(AABox(Vec3(-1, 0, -1), Vec3(1, 1, 1))); + mCylinder = new Geometry(AABox(Vec3(-1, -1, -1), Vec3(1, 1, 1))); + + // Iterate over levels + for (int level = sMaxLevel; level >= 1; --level) + { + // Determine at which distance this level should be active + float distance = sLODDistanceForLevel[sMaxLevel - level]; + + // Sphere + mSphere->mLODs.push_back({ CreateTriangleBatchForConvex(sphere_support, level), distance }); + + // Capsule bottom half sphere + { + Array capsule_bottom_vertices; + Array capsule_bottom_indices; + Create8thSphere(capsule_bottom_indices, capsule_bottom_vertices, -Vec3::sAxisX(), -Vec3::sAxisY(), Vec3::sAxisZ(), Float2(0.25f, 0.25f), sphere_support, level); + Create8thSphere(capsule_bottom_indices, capsule_bottom_vertices, -Vec3::sAxisY(), Vec3::sAxisX(), Vec3::sAxisZ(), Float2(0.25f, 0.75f), sphere_support, level); + Create8thSphere(capsule_bottom_indices, capsule_bottom_vertices, Vec3::sAxisX(), -Vec3::sAxisY(), -Vec3::sAxisZ(), Float2(0.25f, 0.25f), sphere_support, level); + Create8thSphere(capsule_bottom_indices, capsule_bottom_vertices, -Vec3::sAxisY(), -Vec3::sAxisX(), -Vec3::sAxisZ(), Float2(0.25f, 0.75f), sphere_support, level); + mCapsuleBottom->mLODs.push_back({ CreateTriangleBatch(capsule_bottom_vertices, capsule_bottom_indices), distance }); + } + + // Capsule top half sphere + { + Array capsule_top_vertices; + Array capsule_top_indices; + Create8thSphere(capsule_top_indices, capsule_top_vertices, Vec3::sAxisX(), Vec3::sAxisY(), Vec3::sAxisZ(), Float2(0.25f, 0.75f), sphere_support, level); + Create8thSphere(capsule_top_indices, capsule_top_vertices, Vec3::sAxisY(), -Vec3::sAxisX(), Vec3::sAxisZ(), Float2(0.25f, 0.25f), sphere_support, level); + Create8thSphere(capsule_top_indices, capsule_top_vertices, Vec3::sAxisY(), Vec3::sAxisX(), -Vec3::sAxisZ(), Float2(0.25f, 0.25f), sphere_support, level); + Create8thSphere(capsule_top_indices, capsule_top_vertices, -Vec3::sAxisX(), Vec3::sAxisY(), -Vec3::sAxisZ(), Float2(0.25f, 0.75f), sphere_support, level); + mCapsuleTop->mLODs.push_back({ CreateTriangleBatch(capsule_top_vertices, capsule_top_indices), distance }); + } + + // Capsule middle part + { + Array capsule_mid_vertices; + Array capsule_mid_indices; + for (int q = 0; q < 4; ++q) + { + Float2 uv = (q & 1) == 0? Float2(0.25f, 0.25f) : Float2(0.25f, 0.75f); + + uint32 start_idx = (uint32)capsule_mid_vertices.size(); + + int num_parts = 1 << level; + for (int i = 0; i <= num_parts; ++i) + { + float angle = 0.5f * JPH_PI * (float(q) + float(i) / num_parts); + float s = Sin(angle); + float c = Cos(angle); + Float3 vt(s, 1.0f, c); + Float3 vb(s, -1.0f, c); + Float3 n(s, 0, c); + + capsule_mid_vertices.push_back({ vt, n, uv, Color::sWhite }); + capsule_mid_vertices.push_back({ vb, n, uv, Color::sWhite }); + } + + for (int i = 0; i < num_parts; ++i) + { + uint32 start = start_idx + 2 * i; + + capsule_mid_indices.push_back(start); + capsule_mid_indices.push_back(start + 1); + capsule_mid_indices.push_back(start + 3); + + capsule_mid_indices.push_back(start); + capsule_mid_indices.push_back(start + 3); + capsule_mid_indices.push_back(start + 2); + } + } + mCapsuleMid->mLODs.push_back({ CreateTriangleBatch(capsule_mid_vertices, capsule_mid_indices), distance }); + } + + // Open cone + { + Array open_cone_vertices; + Array open_cone_indices; + for (int q = 0; q < 4; ++q) + { + Float2 uv = (q & 1) == 0? Float2(0.25f, 0.25f) : Float2(0.25f, 0.75f); + + uint32 start_idx = (uint32)open_cone_vertices.size(); + + int num_parts = 2 << level; + Float3 vt(0, 0, 0); + for (int i = 0; i <= num_parts; ++i) + { + // Calculate bottom vertex + float angle = 0.5f * JPH_PI * (float(q) + float(i) / num_parts); + float s = Sin(angle); + float c = Cos(angle); + Float3 vb(s, 1.0f, c); + + // Calculate normal + // perpendicular = Y cross vb (perpendicular to the plane in which 0, y and vb exists) + // normal = perpendicular cross vb (normal to the edge 0 vb) + Vec3 normal = Vec3(s, -Square(s) - Square(c), c).Normalized(); + Float3 n; normal.StoreFloat3(&n); + + open_cone_vertices.push_back({ vt, n, uv, Color::sWhite }); + open_cone_vertices.push_back({ vb, n, uv, Color::sWhite }); + } + + for (int i = 0; i < num_parts; ++i) + { + uint32 start = start_idx + 2 * i; + + open_cone_indices.push_back(start); + open_cone_indices.push_back(start + 1); + open_cone_indices.push_back(start + 3); + } + } + mOpenCone->mLODs.push_back({ CreateTriangleBatch(open_cone_vertices, open_cone_indices), distance }); + } + + // Cylinder + { + Array cylinder_vertices; + Array cylinder_indices; + for (int q = 0; q < 4; ++q) + { + Float2 uv = (q & 1) == 0? Float2(0.25f, 0.75f) : Float2(0.25f, 0.25f); + + uint32 center_start_idx = (uint32)cylinder_vertices.size(); + + Float3 nt(0.0f, 1.0f, 0.0f); + Float3 nb(0.0f, -1.0f, 0.0f); + cylinder_vertices.push_back({ Float3(0.0f, 1.0f, 0.0f), nt, uv, Color::sWhite }); + cylinder_vertices.push_back({ Float3(0.0f, -1.0f, 0.0f), nb, uv, Color::sWhite }); + + uint32 vtx_start_idx = (uint32)cylinder_vertices.size(); + + int num_parts = 1 << level; + for (int i = 0; i <= num_parts; ++i) + { + float angle = 0.5f * JPH_PI * (float(q) + float(i) / num_parts); + float s = Sin(angle); + float c = Cos(angle); + Float3 vt(s, 1.0f, c); + Float3 vb(s, -1.0f, c); + Float3 n(s, 0, c); + + cylinder_vertices.push_back({ vt, nt, uv, Color::sWhite }); + cylinder_vertices.push_back({ vb, nb, uv, Color::sWhite }); + cylinder_vertices.push_back({ vt, n, uv, Color::sWhite }); + cylinder_vertices.push_back({ vb, n, uv, Color::sWhite }); + } + + for (int i = 0; i < num_parts; ++i) + { + uint32 start = vtx_start_idx + 4 * i; + + // Top + cylinder_indices.push_back(center_start_idx); + cylinder_indices.push_back(start); + cylinder_indices.push_back(start + 4); + + // Bottom + cylinder_indices.push_back(center_start_idx + 1); + cylinder_indices.push_back(start + 5); + cylinder_indices.push_back(start + 1); + + // Side + cylinder_indices.push_back(start + 2); + cylinder_indices.push_back(start + 3); + cylinder_indices.push_back(start + 7); + + cylinder_indices.push_back(start + 2); + cylinder_indices.push_back(start + 7); + cylinder_indices.push_back(start + 6); + } + } + mCylinder->mLODs.push_back({ CreateTriangleBatch(cylinder_vertices, cylinder_indices), distance }); + } + } +} + +AABox DebugRenderer::sCalculateBounds(const Vertex *inVertices, int inVertexCount) +{ + AABox bounds; + for (const Vertex *v = inVertices, *v_end = inVertices + inVertexCount; v < v_end; ++v) + bounds.Encapsulate(Vec3(v->mPosition)); + return bounds; +} + +DebugRenderer::Batch DebugRenderer::CreateTriangleBatch(const VertexList &inVertices, const IndexedTriangleNoMaterialList &inTriangles) +{ + JPH_PROFILE_FUNCTION(); + + Array vertices; + + // Create render vertices + vertices.resize(inVertices.size()); + for (size_t v = 0; v < inVertices.size(); ++v) + { + vertices[v].mPosition = inVertices[v]; + vertices[v].mNormal = Float3(0, 0, 0); + vertices[v].mUV = Float2(0, 0); + vertices[v].mColor = Color::sWhite; + } + + // Calculate normals + for (size_t i = 0; i < inTriangles.size(); ++i) + { + const IndexedTriangleNoMaterial &tri = inTriangles[i]; + + // Calculate normal of face + Vec3 vtx[3]; + for (int j = 0; j < 3; ++j) + vtx[j] = Vec3::sLoadFloat3Unsafe(vertices[tri.mIdx[j]].mPosition); + Vec3 normal = ((vtx[1] - vtx[0]).Cross(vtx[2] - vtx[0])).Normalized(); + + // Add normal to all vertices in face + for (int j = 0; j < 3; ++j) + (Vec3::sLoadFloat3Unsafe(vertices[tri.mIdx[j]].mNormal) + normal).StoreFloat3(&vertices[tri.mIdx[j]].mNormal); + } + + // Renormalize vertex normals + for (size_t i = 0; i < vertices.size(); ++i) + Vec3::sLoadFloat3Unsafe(vertices[i].mNormal).Normalized().StoreFloat3(&vertices[i].mNormal); + + return CreateTriangleBatch(&vertices[0], (int)vertices.size(), &inTriangles[0].mIdx[0], (int)(3 * inTriangles.size())); +} + +DebugRenderer::Batch DebugRenderer::CreateTriangleBatchForConvex(SupportFunction inGetSupport, int inLevel, AABox *outBounds) +{ + JPH_PROFILE_FUNCTION(); + + Array vertices; + Array indices; + Create8thSphere(indices, vertices, Vec3::sAxisX(), Vec3::sAxisY(), Vec3::sAxisZ(), Float2(0.25f, 0.25f), inGetSupport, inLevel); + Create8thSphere(indices, vertices, Vec3::sAxisY(), -Vec3::sAxisX(), Vec3::sAxisZ(), Float2(0.25f, 0.75f), inGetSupport, inLevel); + Create8thSphere(indices, vertices, -Vec3::sAxisY(), Vec3::sAxisX(), Vec3::sAxisZ(), Float2(0.25f, 0.75f), inGetSupport, inLevel); + Create8thSphere(indices, vertices, -Vec3::sAxisX(), -Vec3::sAxisY(), Vec3::sAxisZ(), Float2(0.25f, 0.25f), inGetSupport, inLevel); + Create8thSphere(indices, vertices, Vec3::sAxisY(), Vec3::sAxisX(), -Vec3::sAxisZ(), Float2(0.25f, 0.75f), inGetSupport, inLevel); + Create8thSphere(indices, vertices, -Vec3::sAxisX(), Vec3::sAxisY(), -Vec3::sAxisZ(), Float2(0.25f, 0.25f), inGetSupport, inLevel); + Create8thSphere(indices, vertices, Vec3::sAxisX(), -Vec3::sAxisY(), -Vec3::sAxisZ(), Float2(0.25f, 0.25f), inGetSupport, inLevel); + Create8thSphere(indices, vertices, -Vec3::sAxisY(), -Vec3::sAxisX(), -Vec3::sAxisZ(), Float2(0.25f, 0.75f), inGetSupport, inLevel); + + if (outBounds != nullptr) + *outBounds = sCalculateBounds(&vertices[0], (int)vertices.size()); + + return CreateTriangleBatch(vertices, indices); +} + +DebugRenderer::GeometryRef DebugRenderer::CreateTriangleGeometryForConvex(SupportFunction inGetSupport) +{ + GeometryRef geometry; + + // Iterate over levels + for (int level = sMaxLevel; level >= 1; --level) + { + // Determine at which distance this level should be active + float distance = sLODDistanceForLevel[sMaxLevel - level]; + + // Create triangle batch and only calculate bounds for highest LOD level + AABox bounds; + Batch batch = CreateTriangleBatchForConvex(inGetSupport, level, geometry == nullptr? &bounds : nullptr); + + // Construct geometry in the first iteration + if (geometry == nullptr) + geometry = new Geometry(bounds); + + // Add the LOD + geometry->mLODs.push_back({ batch, distance }); + } + + return geometry; +} + +void DebugRenderer::DrawBox(const AABox &inBox, ColorArg inColor, ECastShadow inCastShadow, EDrawMode inDrawMode) +{ + JPH_PROFILE_FUNCTION(); + + RMat44 m = RMat44::sScale(inBox.GetExtent()); + m.SetTranslation(RVec3(inBox.GetCenter())); + DrawGeometry(m, inColor, mBox, ECullMode::CullBackFace, inCastShadow, inDrawMode); +} + +void DebugRenderer::DrawBox(RMat44Arg inMatrix, const AABox &inBox, ColorArg inColor, ECastShadow inCastShadow, EDrawMode inDrawMode) +{ + JPH_PROFILE_FUNCTION(); + + Mat44 m = Mat44::sScale(inBox.GetExtent()); + m.SetTranslation(inBox.GetCenter()); + DrawGeometry(inMatrix * m, inColor, mBox, ECullMode::CullBackFace, inCastShadow, inDrawMode); +} + +void DebugRenderer::DrawSphere(RVec3Arg inCenter, float inRadius, ColorArg inColor, ECastShadow inCastShadow, EDrawMode inDrawMode) +{ + JPH_PROFILE_FUNCTION(); + + RMat44 matrix = RMat44::sTranslation(inCenter) * Mat44::sScale(inRadius); + + DrawUnitSphere(matrix, inColor, inCastShadow, inDrawMode); +} + +void DebugRenderer::DrawUnitSphere(RMat44Arg inMatrix, ColorArg inColor, ECastShadow inCastShadow, EDrawMode inDrawMode) +{ + JPH_PROFILE_FUNCTION(); + + DrawGeometry(inMatrix, inColor, mSphere, ECullMode::CullBackFace, inCastShadow, inDrawMode); +} + +void DebugRenderer::DrawCapsule(RMat44Arg inMatrix, float inHalfHeightOfCylinder, float inRadius, ColorArg inColor, ECastShadow inCastShadow, EDrawMode inDrawMode) +{ + JPH_PROFILE_FUNCTION(); + + Mat44 scale_matrix = Mat44::sScale(inRadius); + + // Calculate world space bounding box + AABox local_bounds(Vec3(-inRadius, -inHalfHeightOfCylinder - inRadius, -inRadius), Vec3(inRadius, inHalfHeightOfCylinder + inRadius, inRadius)); + AABox world_bounds = local_bounds.Transformed(inMatrix); + + float radius_sq = Square(inRadius); + + // Draw bottom half sphere + RMat44 bottom_matrix = inMatrix * Mat44::sTranslation(Vec3(0, -inHalfHeightOfCylinder, 0)) * scale_matrix; + DrawGeometry(bottom_matrix, world_bounds, radius_sq, inColor, mCapsuleBottom, ECullMode::CullBackFace, inCastShadow, inDrawMode); + + // Draw top half sphere + RMat44 top_matrix = inMatrix * Mat44::sTranslation(Vec3(0, inHalfHeightOfCylinder, 0)) * scale_matrix; + DrawGeometry(top_matrix, world_bounds, radius_sq, inColor, mCapsuleTop, ECullMode::CullBackFace, inCastShadow, inDrawMode); + + // Draw middle part + DrawGeometry(inMatrix * Mat44::sScale(Vec3(inRadius, inHalfHeightOfCylinder, inRadius)), world_bounds, radius_sq, inColor, mCapsuleMid, ECullMode::CullBackFace, inCastShadow, inDrawMode); +} + +void DebugRenderer::DrawCylinder(RMat44Arg inMatrix, float inHalfHeight, float inRadius, ColorArg inColor, ECastShadow inCastShadow, EDrawMode inDrawMode) +{ + JPH_PROFILE_FUNCTION(); + + Mat44 local_transform(Vec4(inRadius, 0, 0, 0), Vec4(0, inHalfHeight, 0, 0), Vec4(0, 0, inRadius, 0), Vec4(0, 0, 0, 1)); + RMat44 transform = inMatrix * local_transform; + + DrawGeometry(transform, mCylinder->mBounds.Transformed(transform), Square(inRadius), inColor, mCylinder, ECullMode::CullBackFace, inCastShadow, inDrawMode); +} + +void DebugRenderer::DrawOpenCone(RVec3Arg inTop, Vec3Arg inAxis, Vec3Arg inPerpendicular, float inHalfAngle, float inLength, ColorArg inColor, ECastShadow inCastShadow, EDrawMode inDrawMode) +{ + JPH_PROFILE_FUNCTION(); + + JPH_ASSERT(inAxis.IsNormalized(1.0e-4f)); + JPH_ASSERT(inPerpendicular.IsNormalized(1.0e-4f)); + JPH_ASSERT(abs(inPerpendicular.Dot(inAxis)) < 1.0e-4f); + + Vec3 axis = Sign(inHalfAngle) * inLength * inAxis; + float scale = inLength * Tan(abs(inHalfAngle)); + if (scale != 0.0f) + { + Vec3 perp1 = scale * inPerpendicular; + Vec3 perp2 = scale * inAxis.Cross(inPerpendicular); + RMat44 transform(Vec4(perp1, 0), Vec4(axis, 0), Vec4(perp2, 0), inTop); + DrawGeometry(transform, inColor, mOpenCone, ECullMode::Off, inCastShadow, inDrawMode); + } +} + +DebugRenderer::Geometry *DebugRenderer::CreateSwingLimitGeometry(int inNumSegments, const Vec3 *inVertices) +{ + // Allocate space for vertices + int num_vertices = 2 * inNumSegments; + Vertex *vertices_start = (Vertex *)JPH_STACK_ALLOC(num_vertices * sizeof(Vertex)); + Vertex *vertices = vertices_start; + + for (int i = 0; i < inNumSegments; ++i) + { + // Get output vertices + Vertex &top = *(vertices++); + Vertex &bottom = *(vertices++); + + // Get local position + const Vec3 &pos = inVertices[i]; + + // Get local normal + const Vec3 &prev_pos = inVertices[(i + inNumSegments - 1) % inNumSegments]; + const Vec3 &next_pos = inVertices[(i + 1) % inNumSegments]; + Vec3 normal = 0.5f * (next_pos.Cross(pos).NormalizedOr(Vec3::sZero()) + pos.Cross(prev_pos).NormalizedOr(Vec3::sZero())); + + // Store top vertex + top.mPosition = { 0, 0, 0 }; + normal.StoreFloat3(&top.mNormal); + top.mColor = Color::sWhite; + top.mUV = { 0, 0 }; + + // Store bottom vertex + pos.StoreFloat3(&bottom.mPosition); + normal.StoreFloat3(&bottom.mNormal); + bottom.mColor = Color::sWhite; + bottom.mUV = { 0, 0 }; + } + + // Allocate space for indices + int num_indices = 3 * inNumSegments; + uint32 *indices_start = (uint32 *)JPH_STACK_ALLOC(num_indices * sizeof(uint32)); + uint32 *indices = indices_start; + + // Calculate indices + for (int i = 0; i < inNumSegments; ++i) + { + int first = 2 * i; + int second = (first + 3) % num_vertices; + int third = first + 1; + + // Triangle + *indices++ = first; + *indices++ = second; + *indices++ = third; + } + + // Convert to triangle batch + return new Geometry(CreateTriangleBatch(vertices_start, num_vertices, indices_start, num_indices), sCalculateBounds(vertices_start, num_vertices)); +} + +void DebugRenderer::DrawSwingConeLimits(RMat44Arg inMatrix, float inSwingYHalfAngle, float inSwingZHalfAngle, float inEdgeLength, ColorArg inColor, ECastShadow inCastShadow, EDrawMode inDrawMode) +{ + JPH_PROFILE_FUNCTION(); + + // Assert sane input + JPH_ASSERT(inSwingYHalfAngle >= 0.0f && inSwingYHalfAngle <= JPH_PI); + JPH_ASSERT(inSwingZHalfAngle >= 0.0f && inSwingZHalfAngle <= JPH_PI); + JPH_ASSERT(inEdgeLength > 0.0f); + + // Check cache + SwingConeLimits limits { inSwingYHalfAngle, inSwingZHalfAngle }; + GeometryRef &geometry = mSwingConeLimits[limits]; + if (geometry == nullptr) + { + SwingConeBatches::iterator it = mPrevSwingConeLimits.find(limits); + if (it != mPrevSwingConeLimits.end()) + geometry = it->second; + } + if (geometry == nullptr) + { + // Number of segments to draw the cone with + const int num_segments = 64; + int half_num_segments = num_segments / 2; + + // The y and z values of the quaternion are limited to an ellipse, e1 and e2 are the radii of this ellipse + float e1 = Sin(0.5f * inSwingZHalfAngle); + float e2 = Sin(0.5f * inSwingYHalfAngle); + + // Check if the limits will draw something + if ((e1 <= 0.0f && e2 <= 0.0f) || (e2 >= 1.0f && e1 >= 1.0f)) + return; + + // Calculate squared values + float e1_sq = Square(e1); + float e2_sq = Square(e2); + + // Calculate local space vertices for shape + Vec3 ls_vertices[num_segments]; + int tgt_vertex = 0; + for (int side_iter = 0; side_iter < 2; ++side_iter) + for (int segment_iter = 0; segment_iter < half_num_segments; ++segment_iter) + { + float y, z; + if (e2_sq > e1_sq) + { + // Trace the y value of the quaternion + y = e2 - 2.0f * segment_iter * e2 / half_num_segments; + + // Calculate the corresponding z value of the quaternion + float z_sq = e1_sq - e1_sq / e2_sq * Square(y); + z = z_sq <= 0.0f? 0.0f : sqrt(z_sq); + } + else + { + // Trace the z value of the quaternion + z = -e1 + 2.0f * segment_iter * e1 / half_num_segments; + + // Calculate the corresponding y value of the quaternion + float y_sq = e2_sq - e2_sq / e1_sq * Square(z); + y = y_sq <= 0.0f? 0.0f : sqrt(y_sq); + } + + // If we're tracing the opposite side, flip the values + if (side_iter == 1) + { + z = -z; + y = -y; + } + + // Create quaternion + Vec3 q_xyz(0, y, z); + float w = sqrt(1.0f - q_xyz.LengthSq()); + Quat q(Vec4(q_xyz, w)); + + // Store vertex + ls_vertices[tgt_vertex++] = q.RotateAxisX(); + } + + geometry = CreateSwingLimitGeometry(num_segments, ls_vertices); + } + + DrawGeometry(inMatrix * Mat44::sScale(inEdgeLength), inColor, geometry, ECullMode::Off, inCastShadow, inDrawMode); +} + +void DebugRenderer::DrawSwingPyramidLimits(RMat44Arg inMatrix, float inMinSwingYAngle, float inMaxSwingYAngle, float inMinSwingZAngle, float inMaxSwingZAngle, float inEdgeLength, ColorArg inColor, ECastShadow inCastShadow, EDrawMode inDrawMode) +{ + JPH_PROFILE_FUNCTION(); + + // Assert sane input + JPH_ASSERT(inMinSwingYAngle <= inMaxSwingYAngle && inMinSwingZAngle <= inMaxSwingZAngle); + JPH_ASSERT(inEdgeLength > 0.0f); + + // Check cache + SwingPyramidLimits limits { inMinSwingYAngle, inMaxSwingYAngle, inMinSwingZAngle, inMaxSwingZAngle }; + GeometryRef &geometry = mSwingPyramidLimits[limits]; + if (geometry == nullptr) + { + SwingPyramidBatches::iterator it = mPrevSwingPyramidLimits.find(limits); + if (it != mPrevSwingPyramidLimits.end()) + geometry = it->second; + } + if (geometry == nullptr) + { + // Number of segments to draw the cone with + const int num_segments = 64; + int quarter_num_segments = num_segments / 4; + + // Note that this is q = Quat::sRotation(Vec3::sAxisZ(), z) * Quat::sRotation(Vec3::sAxisY(), y) with q.x set to zero so we don't introduce twist + // This matches the calculation in SwingTwistConstraintPart::ClampSwingTwist + auto get_axis = [](float inY, float inZ) { + float hy = 0.5f * inY; + float hz = 0.5f * inZ; + float cos_hy = Cos(hy); + float cos_hz = Cos(hz); + return Quat(0, Sin(hy) * cos_hz, cos_hy * Sin(hz), cos_hy * cos_hz).Normalized().RotateAxisX(); + }; + + // Calculate local space vertices for shape + Vec3 ls_vertices[num_segments]; + int tgt_vertex = 0; + for (int segment_iter = 0; segment_iter < quarter_num_segments; ++segment_iter) + ls_vertices[tgt_vertex++] = get_axis(inMinSwingYAngle, inMaxSwingZAngle - (inMaxSwingZAngle - inMinSwingZAngle) * segment_iter / quarter_num_segments); + for (int segment_iter = 0; segment_iter < quarter_num_segments; ++segment_iter) + ls_vertices[tgt_vertex++] = get_axis(inMinSwingYAngle + (inMaxSwingYAngle - inMinSwingYAngle) * segment_iter / quarter_num_segments, inMinSwingZAngle); + for (int segment_iter = 0; segment_iter < quarter_num_segments; ++segment_iter) + ls_vertices[tgt_vertex++] = get_axis(inMaxSwingYAngle, inMinSwingZAngle + (inMaxSwingZAngle - inMinSwingZAngle) * segment_iter / quarter_num_segments); + for (int segment_iter = 0; segment_iter < quarter_num_segments; ++segment_iter) + ls_vertices[tgt_vertex++] = get_axis(inMaxSwingYAngle - (inMaxSwingYAngle - inMinSwingYAngle) * segment_iter / quarter_num_segments, inMaxSwingZAngle); + + geometry = CreateSwingLimitGeometry(num_segments, ls_vertices); + } + + DrawGeometry(inMatrix * Mat44::sScale(inEdgeLength), inColor, geometry, ECullMode::Off, inCastShadow, inDrawMode); +} + +void DebugRenderer::DrawPie(RVec3Arg inCenter, float inRadius, Vec3Arg inNormal, Vec3Arg inAxis, float inMinAngle, float inMaxAngle, ColorArg inColor, ECastShadow inCastShadow, EDrawMode inDrawMode) +{ + if (inMinAngle >= inMaxAngle) + return; + + JPH_PROFILE_FUNCTION(); + + JPH_ASSERT(inAxis.IsNormalized(1.0e-4f)); + JPH_ASSERT(inNormal.IsNormalized(1.0e-4f)); + JPH_ASSERT(abs(inNormal.Dot(inAxis)) < 1.0e-4f); + + // Pies have a unique batch based on the difference between min and max angle + float delta_angle = inMaxAngle - inMinAngle; + GeometryRef &geometry = mPieLimits[delta_angle]; + if (geometry == nullptr) + { + PieBatces::iterator it = mPrevPieLimits.find(delta_angle); + if (it != mPrevPieLimits.end()) + geometry = it->second; + } + if (geometry == nullptr) + { + int num_parts = (int)ceil(64.0f * delta_angle / (2.0f * JPH_PI)); + + Float3 normal = { 0, 1, 0 }; + Float3 center = { 0, 0, 0 }; + + // Allocate space for vertices + int num_vertices = num_parts + 2; + Vertex *vertices_start = (Vertex *)JPH_STACK_ALLOC(num_vertices * sizeof(Vertex)); + Vertex *vertices = vertices_start; + + // Center of circle + *vertices++ = { center, normal, { 0, 0 }, Color::sWhite }; + + // Outer edge of pie + for (int i = 0; i <= num_parts; ++i) + { + float angle = float(i) / float(num_parts) * delta_angle; + + Float3 pos = { Cos(angle), 0, Sin(angle) }; + *vertices++ = { pos, normal, { 0, 0 }, Color::sWhite }; + } + + // Allocate space for indices + int num_indices = num_parts * 3; + uint32 *indices_start = (uint32 *)JPH_STACK_ALLOC(num_indices * sizeof(uint32)); + uint32 *indices = indices_start; + + for (int i = 0; i < num_parts; ++i) + { + *indices++ = 0; + *indices++ = i + 1; + *indices++ = i + 2; + } + + // Convert to triangle batch + geometry = new Geometry(CreateTriangleBatch(vertices_start, num_vertices, indices_start, num_indices), sCalculateBounds(vertices_start, num_vertices)); + } + + // Construct matrix that transforms pie into world space + RMat44 matrix = RMat44(Vec4(inRadius * inAxis, 0), Vec4(inRadius * inNormal, 0), Vec4(inRadius * inNormal.Cross(inAxis), 0), inCenter) * Mat44::sRotationY(-inMinAngle); + + DrawGeometry(matrix, inColor, geometry, ECullMode::Off, inCastShadow, inDrawMode); +} + +void DebugRenderer::NextFrame() +{ + mPrevSwingConeLimits.clear(); + std::swap(mSwingConeLimits, mPrevSwingConeLimits); + + mPrevSwingPyramidLimits.clear(); + std::swap(mSwingPyramidLimits, mPrevSwingPyramidLimits); + + mPrevPieLimits.clear(); + std::swap(mPieLimits, mPrevPieLimits); +} + +JPH_NAMESPACE_END + +#endif // JPH_DEBUG_RENDERER diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Renderer/DebugRenderer.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Renderer/DebugRenderer.h new file mode 100644 index 00000000000..dd3e7bc8203 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Renderer/DebugRenderer.h @@ -0,0 +1,347 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#ifndef JPH_DEBUG_RENDERER + #error This file should only be included when JPH_DEBUG_RENDERER is defined +#endif // !JPH_DEBUG_RENDERER + +#ifndef JPH_DEBUG_RENDERER_EXPORT + // By default export the debug renderer + #define JPH_DEBUG_RENDERER_EXPORT JPH_EXPORT +#endif // !JPH_DEBUG_RENDERER_EXPORT + +#include +#include +#include +#include +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +class OrientedBox; + +/// Simple triangle renderer for debugging purposes. +/// +/// Inherit from this class to provide your own implementation. +/// +/// Implement the following virtual functions: +/// - DrawLine +/// - DrawTriangle +/// - DrawText3D +/// - CreateTriangleBatch +/// - DrawGeometry +/// +/// Make sure you call Initialize() from the constructor of your implementation. +/// +/// The CreateTriangleBatch is used to prepare a batch of triangles to be drawn by a single DrawGeometry call, +/// which means that Jolt can render a complex scene much more efficiently than when each triangle in that scene would have been drawn through DrawTriangle. +/// +/// Note that an implementation that implements CreateTriangleBatch and DrawGeometry is provided by DebugRendererSimple which can be used to start quickly. +class JPH_DEBUG_RENDERER_EXPORT DebugRenderer : public NonCopyable +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Constructor + DebugRenderer(); + virtual ~DebugRenderer(); + + /// Call once after frame is complete. Releases unused dynamically generated geometry assets. + void NextFrame(); + + /// Draw line + virtual void DrawLine(RVec3Arg inFrom, RVec3Arg inTo, ColorArg inColor) = 0; + + /// Draw wireframe box + void DrawWireBox(const AABox &inBox, ColorArg inColor); + void DrawWireBox(const OrientedBox &inBox, ColorArg inColor); + void DrawWireBox(RMat44Arg inMatrix, const AABox &inBox, ColorArg inColor); + + /// Draw a marker on a position + void DrawMarker(RVec3Arg inPosition, ColorArg inColor, float inSize); + + /// Draw an arrow + void DrawArrow(RVec3Arg inFrom, RVec3Arg inTo, ColorArg inColor, float inSize); + + /// Draw coordinate system (3 arrows, x = red, y = green, z = blue) + void DrawCoordinateSystem(RMat44Arg inTransform, float inSize = 1.0f); + + /// Draw a plane through inPoint with normal inNormal + void DrawPlane(RVec3Arg inPoint, Vec3Arg inNormal, ColorArg inColor, float inSize); + + /// Draw wireframe triangle + void DrawWireTriangle(RVec3Arg inV1, RVec3Arg inV2, RVec3Arg inV3, ColorArg inColor); + + /// Draw a wireframe polygon + template + void DrawWirePolygon(RMat44Arg inTransform, const VERTEX_ARRAY &inVertices, ColorArg inColor, float inArrowSize = 0.0f) { for (typename VERTEX_ARRAY::size_type i = 0; i < inVertices.size(); ++i) DrawArrow(inTransform * inVertices[i], inTransform * inVertices[(i + 1) % inVertices.size()], inColor, inArrowSize); } + + /// Draw wireframe sphere + void DrawWireSphere(RVec3Arg inCenter, float inRadius, ColorArg inColor, int inLevel = 3); + void DrawWireUnitSphere(RMat44Arg inMatrix, ColorArg inColor, int inLevel = 3); + + /// Enum that determines if a shadow should be cast or not + enum class ECastShadow + { + On, // This shape should cast a shadow + Off // This shape should not cast a shadow + }; + + /// Determines how triangles are drawn + enum class EDrawMode + { + Solid, ///< Draw as a solid shape + Wireframe, ///< Draw as wireframe + }; + + /// Draw a single back face culled triangle + virtual void DrawTriangle(RVec3Arg inV1, RVec3Arg inV2, RVec3Arg inV3, ColorArg inColor, ECastShadow inCastShadow = ECastShadow::Off) = 0; + + /// Draw a box + void DrawBox(const AABox &inBox, ColorArg inColor, ECastShadow inCastShadow = ECastShadow::On, EDrawMode inDrawMode = EDrawMode::Solid); + void DrawBox(RMat44Arg inMatrix, const AABox &inBox, ColorArg inColor, ECastShadow inCastShadow = ECastShadow::On, EDrawMode inDrawMode = EDrawMode::Solid); + + /// Draw a sphere + void DrawSphere(RVec3Arg inCenter, float inRadius, ColorArg inColor, ECastShadow inCastShadow = ECastShadow::On, EDrawMode inDrawMode = EDrawMode::Solid); + void DrawUnitSphere(RMat44Arg inMatrix, ColorArg inColor, ECastShadow inCastShadow = ECastShadow::On, EDrawMode inDrawMode = EDrawMode::Solid); + + /// Draw a capsule with one half sphere at (0, -inHalfHeightOfCylinder, 0) and the other half sphere at (0, inHalfHeightOfCylinder, 0) and radius inRadius. + /// The capsule will be transformed by inMatrix. + void DrawCapsule(RMat44Arg inMatrix, float inHalfHeightOfCylinder, float inRadius, ColorArg inColor, ECastShadow inCastShadow = ECastShadow::On, EDrawMode inDrawMode = EDrawMode::Solid); + + /// Draw a cylinder with top (0, inHalfHeight, 0) and bottom (0, -inHalfHeight, 0) and radius inRadius. + /// The cylinder will be transformed by inMatrix + void DrawCylinder(RMat44Arg inMatrix, float inHalfHeight, float inRadius, ColorArg inColor, ECastShadow inCastShadow = ECastShadow::On, EDrawMode inDrawMode = EDrawMode::Solid); + + /// Draw a bottomless cone. + /// @param inTop Top of cone, center of base is at inTop + inAxis. + /// @param inAxis Height and direction of cone + /// @param inPerpendicular Perpendicular vector to inAxis. + /// @param inHalfAngle Specifies the cone angle in radians (angle measured between inAxis and cone surface). + /// @param inLength The length of the cone. + /// @param inColor Color to use for drawing the cone. + /// @param inCastShadow determines if this geometry should cast a shadow or not. + /// @param inDrawMode determines if we draw the geometry solid or in wireframe. + void DrawOpenCone(RVec3Arg inTop, Vec3Arg inAxis, Vec3Arg inPerpendicular, float inHalfAngle, float inLength, ColorArg inColor, ECastShadow inCastShadow = ECastShadow::On, EDrawMode inDrawMode = EDrawMode::Solid); + + /// Draws cone rotation limits as used by the SwingTwistConstraintPart. + /// @param inMatrix Matrix that transforms from constraint space to world space + /// @param inSwingYHalfAngle See SwingTwistConstraintPart + /// @param inSwingZHalfAngle See SwingTwistConstraintPart + /// @param inEdgeLength Size of the edge of the cone shape + /// @param inColor Color to use for drawing the cone. + /// @param inCastShadow determines if this geometry should cast a shadow or not. + /// @param inDrawMode determines if we draw the geometry solid or in wireframe. + void DrawSwingConeLimits(RMat44Arg inMatrix, float inSwingYHalfAngle, float inSwingZHalfAngle, float inEdgeLength, ColorArg inColor, ECastShadow inCastShadow = ECastShadow::On, EDrawMode inDrawMode = EDrawMode::Solid); + + /// Draws rotation limits as used by the SwingTwistConstraintPart. + /// @param inMatrix Matrix that transforms from constraint space to world space + /// @param inMinSwingYAngle See SwingTwistConstraintPart + /// @param inMaxSwingYAngle See SwingTwistConstraintPart + /// @param inMinSwingZAngle See SwingTwistConstraintPart + /// @param inMaxSwingZAngle See SwingTwistConstraintPart + /// @param inEdgeLength Size of the edge of the cone shape + /// @param inColor Color to use for drawing the cone. + /// @param inCastShadow determines if this geometry should cast a shadow or not. + /// @param inDrawMode determines if we draw the geometry solid or in wireframe. + void DrawSwingPyramidLimits(RMat44Arg inMatrix, float inMinSwingYAngle, float inMaxSwingYAngle, float inMinSwingZAngle, float inMaxSwingZAngle, float inEdgeLength, ColorArg inColor, ECastShadow inCastShadow = ECastShadow::On, EDrawMode inDrawMode = EDrawMode::Solid); + + /// Draw a pie (part of a circle). + /// @param inCenter The center of the circle. + /// @param inRadius Radius of the circle. + /// @param inNormal The plane normal in which the pie resides. + /// @param inAxis The axis that defines an angle of 0 radians. + /// @param inMinAngle The pie will be drawn between [inMinAngle, inMaxAngle] (in radians). + /// @param inMaxAngle The pie will be drawn between [inMinAngle, inMaxAngle] (in radians). + /// @param inColor Color to use for drawing the pie. + /// @param inCastShadow determines if this geometry should cast a shadow or not. + /// @param inDrawMode determines if we draw the geometry solid or in wireframe. + void DrawPie(RVec3Arg inCenter, float inRadius, Vec3Arg inNormal, Vec3Arg inAxis, float inMinAngle, float inMaxAngle, ColorArg inColor, ECastShadow inCastShadow = ECastShadow::On, EDrawMode inDrawMode = EDrawMode::Solid); + + /// Singleton instance + static DebugRenderer * sInstance; + + /// Vertex format used by the triangle renderer + class Vertex + { + public: + Float3 mPosition; + Float3 mNormal; + Float2 mUV; + Color mColor; + }; + + /// A single triangle + class JPH_DEBUG_RENDERER_EXPORT Triangle + { + public: + Triangle() = default; + Triangle(Vec3Arg inV1, Vec3Arg inV2, Vec3Arg inV3, ColorArg inColor); + Triangle(Vec3Arg inV1, Vec3Arg inV2, Vec3Arg inV3, ColorArg inColor, Vec3Arg inUVOrigin, Vec3Arg inUVDirection); + + Vertex mV[3]; + }; + + /// Handle for a batch of triangles + using Batch = Ref; + + /// A single level of detail + class LOD + { + public: + Batch mTriangleBatch; + float mDistance; + }; + + /// A geometry primitive containing triangle batches for various lods + class Geometry : public RefTarget + { + public: + JPH_OVERRIDE_NEW_DELETE + + /// Constructor + Geometry(const AABox &inBounds) : mBounds(inBounds) { } + Geometry(const Batch &inBatch, const AABox &inBounds) : mBounds(inBounds) { mLODs.push_back({ inBatch, FLT_MAX }); } + + /// Determine which LOD to render + /// @param inCameraPosition Current position of the camera + /// @param inWorldSpaceBounds World space bounds for this geometry (transform mBounds by model space matrix) + /// @param inLODScaleSq is the squared scale of the model matrix, it is multiplied with the LOD distances in inGeometry to calculate the real LOD distance (so a number > 1 will force a higher LOD). + /// @return The selected LOD. + const LOD & GetLOD(Vec3Arg inCameraPosition, const AABox &inWorldSpaceBounds, float inLODScaleSq) const + { + float dist_sq = inWorldSpaceBounds.GetSqDistanceTo(inCameraPosition); + for (const LOD &lod : mLODs) + if (dist_sq <= inLODScaleSq * Square(lod.mDistance)) + return lod; + + return mLODs.back(); + } + + /// All level of details for this mesh + Array mLODs; + + /// Bounding box that encapsulates all LODs + AABox mBounds; + }; + + /// Handle for a lodded triangle batch + using GeometryRef = Ref; + + /// Calculate bounding box for a batch of triangles + static AABox sCalculateBounds(const Vertex *inVertices, int inVertexCount); + + /// Create a batch of triangles that can be drawn efficiently + virtual Batch CreateTriangleBatch(const Triangle *inTriangles, int inTriangleCount) = 0; + virtual Batch CreateTriangleBatch(const Vertex *inVertices, int inVertexCount, const uint32 *inIndices, int inIndexCount) = 0; + Batch CreateTriangleBatch(const Array &inTriangles) { return CreateTriangleBatch(inTriangles.empty()? nullptr : &inTriangles[0], int(inTriangles.size())); } + Batch CreateTriangleBatch(const Array &inVertices, const Array &inIndices) { return CreateTriangleBatch(inVertices.empty()? nullptr : &inVertices[0], int(inVertices.size()), inIndices.empty()? nullptr : &inIndices[0], int(inIndices.size())); } + Batch CreateTriangleBatch(const VertexList &inVertices, const IndexedTriangleNoMaterialList &inTriangles); + + /// Create a primitive for a convex shape using its support function + using SupportFunction = function; + Batch CreateTriangleBatchForConvex(SupportFunction inGetSupport, int inLevel, AABox *outBounds = nullptr); + GeometryRef CreateTriangleGeometryForConvex(SupportFunction inGetSupport); + + /// Determines which polygons are culled + enum class ECullMode + { + CullBackFace, ///< Don't draw backfacing polygons + CullFrontFace, ///< Don't draw front facing polygons + Off ///< Don't do culling and draw both sides + }; + + /// Draw some geometry + /// @param inModelMatrix is the matrix that transforms the geometry to world space. + /// @param inWorldSpaceBounds is the bounding box of the geometry after transforming it into world space. + /// @param inLODScaleSq is the squared scale of the model matrix, it is multiplied with the LOD distances in inGeometry to calculate the real LOD distance (so a number > 1 will force a higher LOD). + /// @param inModelColor is the color with which to multiply the vertex colors in inGeometry. + /// @param inGeometry The geometry to draw. + /// @param inCullMode determines which polygons are culled. + /// @param inCastShadow determines if this geometry should cast a shadow or not. + /// @param inDrawMode determines if we draw the geometry solid or in wireframe. + virtual void DrawGeometry(RMat44Arg inModelMatrix, const AABox &inWorldSpaceBounds, float inLODScaleSq, ColorArg inModelColor, const GeometryRef &inGeometry, ECullMode inCullMode = ECullMode::CullBackFace, ECastShadow inCastShadow = ECastShadow::On, EDrawMode inDrawMode = EDrawMode::Solid) = 0; + void DrawGeometry(RMat44Arg inModelMatrix, ColorArg inModelColor, const GeometryRef &inGeometry, ECullMode inCullMode = ECullMode::CullBackFace, ECastShadow inCastShadow = ECastShadow::On, EDrawMode inDrawMode = EDrawMode::Solid) { DrawGeometry(inModelMatrix, inGeometry->mBounds.Transformed(inModelMatrix), max(max(inModelMatrix.GetAxisX().LengthSq(), inModelMatrix.GetAxisY().LengthSq()), inModelMatrix.GetAxisZ().LengthSq()), inModelColor, inGeometry, inCullMode, inCastShadow, inDrawMode); } + + /// Draw text + virtual void DrawText3D(RVec3Arg inPosition, const string_view &inString, ColorArg inColor = Color::sWhite, float inHeight = 0.5f) = 0; + +protected: + /// Initialize the system, must be called from the constructor of the DebugRenderer implementation + void Initialize(); + +private: + /// Recursive helper function for DrawWireUnitSphere + void DrawWireUnitSphereRecursive(RMat44Arg inMatrix, ColorArg inColor, Vec3Arg inDir1, Vec3Arg inDir2, Vec3Arg inDir3, int inLevel); + + /// Helper functions to create a box + void CreateQuad(Array &ioIndices, Array &ioVertices, Vec3Arg inV1, Vec3Arg inV2, Vec3Arg inV3, Vec3Arg inV4); + + /// Helper functions to create a vertex and index buffer for a sphere + void Create8thSphereRecursive(Array &ioIndices, Array &ioVertices, Vec3Arg inDir1, uint32 &ioIdx1, Vec3Arg inDir2, uint32 &ioIdx2, Vec3Arg inDir3, uint32 &ioIdx3, const Float2 &inUV, SupportFunction inGetSupport, int inLevel); + void Create8thSphere(Array &ioIndices, Array &ioVertices, Vec3Arg inDir1, Vec3Arg inDir2, Vec3Arg inDir3, const Float2 &inUV, SupportFunction inGetSupport, int inLevel); + + /// Helper function for DrawSwingConeLimits and DrawSwingPyramidLimits + Geometry * CreateSwingLimitGeometry(int inNumSegments, const Vec3 *inVertices); + + // Predefined shapes + GeometryRef mBox; + GeometryRef mSphere; + GeometryRef mCapsuleTop; + GeometryRef mCapsuleMid; + GeometryRef mCapsuleBottom; + GeometryRef mOpenCone; + GeometryRef mCylinder; + + struct SwingConeLimits + { + bool operator == (const SwingConeLimits &inRHS) const + { + return mSwingYHalfAngle == inRHS.mSwingYHalfAngle + && mSwingZHalfAngle == inRHS.mSwingZHalfAngle; + } + + float mSwingYHalfAngle; + float mSwingZHalfAngle; + }; + + JPH_MAKE_HASH_STRUCT(SwingConeLimits, SwingConeLimitsHasher, t.mSwingYHalfAngle, t.mSwingZHalfAngle) + + using SwingConeBatches = UnorderedMap; + SwingConeBatches mSwingConeLimits; + SwingConeBatches mPrevSwingConeLimits; + + struct SwingPyramidLimits + { + bool operator == (const SwingPyramidLimits &inRHS) const + { + return mMinSwingYAngle == inRHS.mMinSwingYAngle + && mMaxSwingYAngle == inRHS.mMaxSwingYAngle + && mMinSwingZAngle == inRHS.mMinSwingZAngle + && mMaxSwingZAngle == inRHS.mMaxSwingZAngle; + } + + float mMinSwingYAngle; + float mMaxSwingYAngle; + float mMinSwingZAngle; + float mMaxSwingZAngle; + }; + + JPH_MAKE_HASH_STRUCT(SwingPyramidLimits, SwingPyramidLimitsHasher, t.mMinSwingYAngle, t.mMaxSwingYAngle, t.mMinSwingZAngle, t.mMaxSwingZAngle) + + using SwingPyramidBatches = UnorderedMap; + SwingPyramidBatches mSwingPyramidLimits; + SwingPyramidBatches mPrevSwingPyramidLimits; + + using PieBatces = UnorderedMap; + PieBatces mPieLimits; + PieBatces mPrevPieLimits; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Renderer/DebugRendererPlayback.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Renderer/DebugRendererPlayback.cpp new file mode 100644 index 00000000000..bea7e4e08f8 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Renderer/DebugRendererPlayback.cpp @@ -0,0 +1,168 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#ifdef JPH_DEBUG_RENDERER + +#include + +JPH_NAMESPACE_BEGIN + +void DebugRendererPlayback::Parse(StreamIn &inStream) +{ + using ECommand = DebugRendererRecorder::ECommand; + + for (;;) + { + // Read the next command + ECommand command; + inStream.Read(command); + + if (inStream.IsEOF() || inStream.IsFailed()) + return; + + if (command == ECommand::CreateBatch) + { + uint32 id; + inStream.Read(id); + + uint32 triangle_count; + inStream.Read(triangle_count); + + DebugRenderer::Triangle *triangles = new DebugRenderer::Triangle [triangle_count]; + inStream.ReadBytes(triangles, triangle_count * sizeof(DebugRenderer::Triangle)); + + mBatches.insert({ id, mRenderer.CreateTriangleBatch(triangles, triangle_count) }); + + delete [] triangles; + } + else if (command == ECommand::CreateBatchIndexed) + { + uint32 id; + inStream.Read(id); + + uint32 vertex_count; + inStream.Read(vertex_count); + + DebugRenderer::Vertex *vertices = new DebugRenderer::Vertex [vertex_count]; + inStream.ReadBytes(vertices, vertex_count * sizeof(DebugRenderer::Vertex)); + + uint32 index_count; + inStream.Read(index_count); + + uint32 *indices = new uint32 [index_count]; + inStream.ReadBytes(indices, index_count * sizeof(uint32)); + + mBatches.insert({ id, mRenderer.CreateTriangleBatch(vertices, vertex_count, indices, index_count) }); + + delete [] indices; + delete [] vertices; + } + else if (command == ECommand::CreateGeometry) + { + uint32 geometry_id; + inStream.Read(geometry_id); + + AABox bounds; + inStream.Read(bounds.mMin); + inStream.Read(bounds.mMax); + + DebugRenderer::GeometryRef geometry = new DebugRenderer::Geometry(bounds); + mGeometries[geometry_id] = geometry; + + uint32 num_lods; + inStream.Read(num_lods); + for (uint32 l = 0; l < num_lods; ++l) + { + DebugRenderer::LOD lod; + inStream.Read(lod.mDistance); + + uint32 batch_id; + inStream.Read(batch_id); + lod.mTriangleBatch = mBatches.find(batch_id)->second; + + geometry->mLODs.push_back(lod); + } + } + else if (command == ECommand::EndFrame) + { + mFrames.push_back({}); + Frame &frame = mFrames.back(); + + // Read all lines + uint32 num_lines = 0; + inStream.Read(num_lines); + frame.mLines.resize(num_lines); + for (DebugRendererRecorder::LineBlob &line : frame.mLines) + { + inStream.Read(line.mFrom); + inStream.Read(line.mTo); + inStream.Read(line.mColor); + } + + // Read all triangles + uint32 num_triangles = 0; + inStream.Read(num_triangles); + frame.mTriangles.resize(num_triangles); + for (DebugRendererRecorder::TriangleBlob &triangle : frame.mTriangles) + { + inStream.Read(triangle.mV1); + inStream.Read(triangle.mV2); + inStream.Read(triangle.mV3); + inStream.Read(triangle.mColor); + inStream.Read(triangle.mCastShadow); + } + + // Read all texts + uint32 num_texts = 0; + inStream.Read(num_texts); + frame.mTexts.resize(num_texts); + for (DebugRendererRecorder::TextBlob &text : frame.mTexts) + { + inStream.Read(text.mPosition); + inStream.Read(text.mString); + inStream.Read(text.mColor); + inStream.Read(text.mHeight); + } + + // Read all geometries + uint32 num_geometries = 0; + inStream.Read(num_geometries); + frame.mGeometries.resize(num_geometries); + for (DebugRendererRecorder::GeometryBlob &geom : frame.mGeometries) + { + inStream.Read(geom.mModelMatrix); + inStream.Read(geom.mModelColor); + inStream.Read(geom.mGeometryID); + inStream.Read(geom.mCullMode); + inStream.Read(geom.mCastShadow); + inStream.Read(geom.mDrawMode); + } + } + else + JPH_ASSERT(false); + } +} + +void DebugRendererPlayback::DrawFrame(uint inFrameNumber) const +{ + const Frame &frame = mFrames[inFrameNumber]; + + for (const DebugRendererRecorder::LineBlob &line : frame.mLines) + mRenderer.DrawLine(line.mFrom, line.mTo, line.mColor); + + for (const DebugRendererRecorder::TriangleBlob &triangle : frame.mTriangles) + mRenderer.DrawTriangle(triangle.mV1, triangle.mV2, triangle.mV3, triangle.mColor, triangle.mCastShadow); + + for (const DebugRendererRecorder::TextBlob &text : frame.mTexts) + mRenderer.DrawText3D(text.mPosition, text.mString, text.mColor, text.mHeight); + + for (const DebugRendererRecorder::GeometryBlob &geom : frame.mGeometries) + mRenderer.DrawGeometry(geom.mModelMatrix, geom.mModelColor, mGeometries.find(geom.mGeometryID)->second, geom.mCullMode, geom.mCastShadow, geom.mDrawMode); +} + +JPH_NAMESPACE_END + +#endif // JPH_DEBUG_RENDERER diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Renderer/DebugRendererPlayback.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Renderer/DebugRendererPlayback.h new file mode 100644 index 00000000000..23ed4542389 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Renderer/DebugRendererPlayback.h @@ -0,0 +1,48 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#ifndef JPH_DEBUG_RENDERER + #error This file should only be included when JPH_DEBUG_RENDERER is defined +#endif // !JPH_DEBUG_RENDERER + +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +/// Class that can read a recorded stream from DebugRendererRecorder and plays it back trough a DebugRenderer +class JPH_DEBUG_RENDERER_EXPORT DebugRendererPlayback +{ +public: + /// Constructor + DebugRendererPlayback(DebugRenderer &inRenderer) : mRenderer(inRenderer) { } + + /// Parse a stream of frames + void Parse(StreamIn &inStream); + + /// Get the number of parsed frames + uint GetNumFrames() const { return (uint)mFrames.size(); } + + /// Draw a frame + void DrawFrame(uint inFrameNumber) const; + +private: + /// The debug renderer we're using to do the actual rendering + DebugRenderer & mRenderer; + + /// Mapping of ID to batch + UnorderedMap mBatches; + + /// Mapping of ID to geometry + UnorderedMap mGeometries; + + /// The list of parsed frames + using Frame = DebugRendererRecorder::Frame; + Array mFrames; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Renderer/DebugRendererRecorder.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Renderer/DebugRendererRecorder.cpp new file mode 100644 index 00000000000..2e25912957f --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Renderer/DebugRendererRecorder.cpp @@ -0,0 +1,158 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#ifdef JPH_DEBUG_RENDERER + +#include + +JPH_NAMESPACE_BEGIN + +void DebugRendererRecorder::DrawLine(RVec3Arg inFrom, RVec3Arg inTo, ColorArg inColor) +{ + lock_guard lock(mMutex); + + mCurrentFrame.mLines.push_back({ inFrom, inTo, inColor }); +} + +void DebugRendererRecorder::DrawTriangle(RVec3Arg inV1, RVec3Arg inV2, RVec3Arg inV3, ColorArg inColor, ECastShadow inCastShadow) +{ + lock_guard lock(mMutex); + + mCurrentFrame.mTriangles.push_back({ inV1, inV2, inV3, inColor, inCastShadow }); +} + +DebugRenderer::Batch DebugRendererRecorder::CreateTriangleBatch(const Triangle *inTriangles, int inTriangleCount) +{ + if (inTriangles == nullptr || inTriangleCount == 0) + return new BatchImpl(0); + + lock_guard lock(mMutex); + + mStream.Write(ECommand::CreateBatch); + + uint32 batch_id = mNextBatchID++; + JPH_ASSERT(batch_id != 0); + mStream.Write(batch_id); + mStream.Write((uint32)inTriangleCount); + mStream.WriteBytes(inTriangles, inTriangleCount * sizeof(Triangle)); + + return new BatchImpl(batch_id); +} + +DebugRenderer::Batch DebugRendererRecorder::CreateTriangleBatch(const Vertex *inVertices, int inVertexCount, const uint32 *inIndices, int inIndexCount) +{ + if (inVertices == nullptr || inVertexCount == 0 || inIndices == nullptr || inIndexCount == 0) + return new BatchImpl(0); + + lock_guard lock(mMutex); + + mStream.Write(ECommand::CreateBatchIndexed); + + uint32 batch_id = mNextBatchID++; + JPH_ASSERT(batch_id != 0); + mStream.Write(batch_id); + mStream.Write((uint32)inVertexCount); + mStream.WriteBytes(inVertices, inVertexCount * sizeof(Vertex)); + mStream.Write((uint32)inIndexCount); + mStream.WriteBytes(inIndices, inIndexCount * sizeof(uint32)); + + return new BatchImpl(batch_id); +} + +void DebugRendererRecorder::DrawGeometry(RMat44Arg inModelMatrix, const AABox &inWorldSpaceBounds, float inLODScaleSq, ColorArg inModelColor, const GeometryRef &inGeometry, ECullMode inCullMode, ECastShadow inCastShadow, EDrawMode inDrawMode) +{ + lock_guard lock(mMutex); + + // See if this geometry was used before + uint32 &geometry_id = mGeometries[inGeometry]; + if (geometry_id == 0) + { + mStream.Write(ECommand::CreateGeometry); + + // Create a new ID + geometry_id = mNextGeometryID++; + JPH_ASSERT(geometry_id != 0); + mStream.Write(geometry_id); + + // Save bounds + mStream.Write(inGeometry->mBounds.mMin); + mStream.Write(inGeometry->mBounds.mMax); + + // Save the LODs + mStream.Write((uint32)inGeometry->mLODs.size()); + for (const LOD & lod : inGeometry->mLODs) + { + mStream.Write(lod.mDistance); + mStream.Write(static_cast(lod.mTriangleBatch.GetPtr())->mID); + } + } + + mCurrentFrame.mGeometries.push_back({ inModelMatrix, inModelColor, geometry_id, inCullMode, inCastShadow, inDrawMode }); +} + +void DebugRendererRecorder::DrawText3D(RVec3Arg inPosition, const string_view &inString, ColorArg inColor, float inHeight) +{ + lock_guard lock(mMutex); + + mCurrentFrame.mTexts.push_back({ inPosition, inString, inColor, inHeight }); +} + +void DebugRendererRecorder::EndFrame() +{ + lock_guard lock(mMutex); + + mStream.Write(ECommand::EndFrame); + + // Write all lines + mStream.Write((uint32)mCurrentFrame.mLines.size()); + for (const LineBlob &line : mCurrentFrame.mLines) + { + mStream.Write(line.mFrom); + mStream.Write(line.mTo); + mStream.Write(line.mColor); + } + mCurrentFrame.mLines.clear(); + + // Write all triangles + mStream.Write((uint32)mCurrentFrame.mTriangles.size()); + for (const TriangleBlob &triangle : mCurrentFrame.mTriangles) + { + mStream.Write(triangle.mV1); + mStream.Write(triangle.mV2); + mStream.Write(triangle.mV3); + mStream.Write(triangle.mColor); + mStream.Write(triangle.mCastShadow); + } + mCurrentFrame.mTriangles.clear(); + + // Write all texts + mStream.Write((uint32)mCurrentFrame.mTexts.size()); + for (const TextBlob &text : mCurrentFrame.mTexts) + { + mStream.Write(text.mPosition); + mStream.Write(text.mString); + mStream.Write(text.mColor); + mStream.Write(text.mHeight); + } + mCurrentFrame.mTexts.clear(); + + // Write all geometries + mStream.Write((uint32)mCurrentFrame.mGeometries.size()); + for (const GeometryBlob &geom : mCurrentFrame.mGeometries) + { + mStream.Write(geom.mModelMatrix); + mStream.Write(geom.mModelColor); + mStream.Write(geom.mGeometryID); + mStream.Write(geom.mCullMode); + mStream.Write(geom.mCastShadow); + mStream.Write(geom.mDrawMode); + } + mCurrentFrame.mGeometries.clear(); +} + +JPH_NAMESPACE_END + +#endif // JPH_DEBUG_RENDERER diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Renderer/DebugRendererRecorder.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Renderer/DebugRendererRecorder.h new file mode 100644 index 00000000000..9608e03c908 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Renderer/DebugRendererRecorder.h @@ -0,0 +1,130 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#ifndef JPH_DEBUG_RENDERER + #error This file should only be included when JPH_DEBUG_RENDERER is defined +#endif // !JPH_DEBUG_RENDERER + +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +/// Implementation of DebugRenderer that records the API invocations to be played back later +class JPH_DEBUG_RENDERER_EXPORT DebugRendererRecorder final : public DebugRenderer +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Constructor + DebugRendererRecorder(StreamOut &inStream) : mStream(inStream) { Initialize(); } + + /// Implementation of DebugRenderer interface + virtual void DrawLine(RVec3Arg inFrom, RVec3Arg inTo, ColorArg inColor) override; + virtual void DrawTriangle(RVec3Arg inV1, RVec3Arg inV2, RVec3Arg inV3, ColorArg inColor, ECastShadow inCastShadow) override; + virtual Batch CreateTriangleBatch(const Triangle *inTriangles, int inTriangleCount) override; + virtual Batch CreateTriangleBatch(const Vertex *inVertices, int inVertexCount, const uint32 *inIndices, int inIndexCount) override; + virtual void DrawGeometry(RMat44Arg inModelMatrix, const AABox &inWorldSpaceBounds, float inLODScaleSq, ColorArg inModelColor, const GeometryRef &inGeometry, ECullMode inCullMode, ECastShadow inCastShadow, EDrawMode inDrawMode) override; + virtual void DrawText3D(RVec3Arg inPosition, const string_view &inString, ColorArg inColor, float inHeight) override; + + /// Mark the end of a frame + void EndFrame(); + + /// Control commands written into the stream + enum class ECommand : uint8 + { + CreateBatch, + CreateBatchIndexed, + CreateGeometry, + EndFrame + }; + + /// Holds a single line segment + struct LineBlob + { + RVec3 mFrom; + RVec3 mTo; + Color mColor; + }; + + /// Holds a single triangle + struct TriangleBlob + { + RVec3 mV1; + RVec3 mV2; + RVec3 mV3; + Color mColor; + ECastShadow mCastShadow; + }; + + /// Holds a single text entry + struct TextBlob + { + TextBlob() = default; + TextBlob(RVec3Arg inPosition, const string_view &inString, ColorArg inColor, float inHeight) : mPosition(inPosition), mString(inString), mColor(inColor), mHeight(inHeight) { } + + RVec3 mPosition; + String mString; + Color mColor; + float mHeight; + }; + + /// Holds a single geometry draw call + struct GeometryBlob + { + RMat44 mModelMatrix; + Color mModelColor; + uint32 mGeometryID; + ECullMode mCullMode; + ECastShadow mCastShadow; + EDrawMode mDrawMode; + }; + + /// All information for a single frame + struct Frame + { + Array mLines; + Array mTriangles; + Array mTexts; + Array mGeometries; + }; + +private: + /// Implementation specific batch object + class BatchImpl : public RefTargetVirtual + { + public: + JPH_OVERRIDE_NEW_DELETE + + BatchImpl(uint32 inID) : mID(inID) { } + + virtual void AddRef() override { ++mRefCount; } + virtual void Release() override { if (--mRefCount == 0) delete this; } + + atomic mRefCount = 0; + uint32 mID; + }; + + /// Lock that prevents concurrent access to the internal structures + Mutex mMutex; + + /// Stream that recorded data will be sent to + StreamOut & mStream; + + /// Next available ID + uint32 mNextBatchID = 1; + uint32 mNextGeometryID = 1; + + /// Cached geometries and their IDs + UnorderedMap mGeometries; + + /// Data that is being accumulated for the current frame + Frame mCurrentFrame; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Renderer/DebugRendererSimple.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Renderer/DebugRendererSimple.cpp new file mode 100644 index 00000000000..a404d95a002 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Renderer/DebugRendererSimple.cpp @@ -0,0 +1,80 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2024 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#ifdef JPH_DEBUG_RENDERER + +#include + +JPH_NAMESPACE_BEGIN + +DebugRendererSimple::DebugRendererSimple() +{ + Initialize(); +} + +DebugRenderer::Batch DebugRendererSimple::CreateTriangleBatch(const Triangle *inTriangles, int inTriangleCount) +{ + BatchImpl *batch = new BatchImpl; + if (inTriangles == nullptr || inTriangleCount == 0) + return batch; + + batch->mTriangles.assign(inTriangles, inTriangles + inTriangleCount); + return batch; +} + +DebugRenderer::Batch DebugRendererSimple::CreateTriangleBatch(const Vertex *inVertices, int inVertexCount, const uint32 *inIndices, int inIndexCount) +{ + BatchImpl *batch = new BatchImpl; + if (inVertices == nullptr || inVertexCount == 0 || inIndices == nullptr || inIndexCount == 0) + return batch; + + // Convert indexed triangle list to triangle list + batch->mTriangles.resize(inIndexCount / 3); + for (size_t t = 0; t < batch->mTriangles.size(); ++t) + { + Triangle &triangle = batch->mTriangles[t]; + triangle.mV[0] = inVertices[inIndices[t * 3 + 0]]; + triangle.mV[1] = inVertices[inIndices[t * 3 + 1]]; + triangle.mV[2] = inVertices[inIndices[t * 3 + 2]]; + } + + return batch; +} + +void DebugRendererSimple::DrawGeometry(RMat44Arg inModelMatrix, const AABox &inWorldSpaceBounds, float inLODScaleSq, ColorArg inModelColor, const GeometryRef &inGeometry, ECullMode inCullMode, ECastShadow inCastShadow, EDrawMode inDrawMode) +{ + // Figure out which LOD to use + const LOD *lod = inGeometry->mLODs.data(); + if (mCameraPosSet) + lod = &inGeometry->GetLOD(Vec3(mCameraPos), inWorldSpaceBounds, inLODScaleSq); + + // Draw the batch + const BatchImpl *batch = static_cast(lod->mTriangleBatch.GetPtr()); + for (const Triangle &triangle : batch->mTriangles) + { + RVec3 v0 = inModelMatrix * Vec3(triangle.mV[0].mPosition); + RVec3 v1 = inModelMatrix * Vec3(triangle.mV[1].mPosition); + RVec3 v2 = inModelMatrix * Vec3(triangle.mV[2].mPosition); + Color color = inModelColor * triangle.mV[0].mColor; + + switch (inDrawMode) + { + case EDrawMode::Wireframe: + DrawLine(v0, v1, color); + DrawLine(v1, v2, color); + DrawLine(v2, v0, color); + break; + + case EDrawMode::Solid: + DrawTriangle(v0, v1, v2, color, inCastShadow); + break; + } + } +} + +JPH_NAMESPACE_END + +#endif // JPH_DEBUG_RENDERER diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Renderer/DebugRendererSimple.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Renderer/DebugRendererSimple.h new file mode 100644 index 00000000000..4a23ab758b7 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Renderer/DebugRendererSimple.h @@ -0,0 +1,88 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2024 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#ifndef JPH_DEBUG_RENDERER + #error This file should only be included when JPH_DEBUG_RENDERER is defined +#endif // !JPH_DEBUG_RENDERER + +#include + +JPH_NAMESPACE_BEGIN + +/// Inherit from this class to simplify implementing a debug renderer, start with this implementation: +/// +/// class MyDebugRenderer : public JPH::DebugRendererSimple +/// { +/// public: +/// virtual void DrawLine(JPH::RVec3Arg inFrom, JPH::RVec3Arg inTo, JPH::ColorArg inColor) override +/// { +/// // Implement +/// } +/// +/// virtual void DrawTriangle(JPH::RVec3Arg inV1, JPH::RVec3Arg inV2, JPH::RVec3Arg inV3, JPH::ColorArg inColor, ECastShadow inCastShadow) override +/// { +/// // Implement +/// } +/// +/// virtual void DrawText3D(JPH::RVec3Arg inPosition, const string_view &inString, JPH::ColorArg inColor, float inHeight) override +/// { +/// // Implement +/// } +/// }; +/// +/// Note that this class is meant to be a quick start for implementing a debug renderer, it is not the most efficient way to implement a debug renderer. +class JPH_DEBUG_RENDERER_EXPORT DebugRendererSimple : public DebugRenderer +{ +public: + JPH_OVERRIDE_NEW_DELETE + + /// Constructor + DebugRendererSimple(); + + /// Should be called every frame by the application to provide the camera position. + /// This is used to determine the correct LOD for rendering. + void SetCameraPos(RVec3Arg inCameraPos) + { + mCameraPos = inCameraPos; + mCameraPosSet = true; + } + + /// Fallback implementation that uses DrawLine to draw a triangle (override this if you have a version that renders solid triangles) + virtual void DrawTriangle(RVec3Arg inV1, RVec3Arg inV2, RVec3Arg inV3, ColorArg inColor, ECastShadow inCastShadow) override + { + DrawLine(inV1, inV2, inColor); + DrawLine(inV2, inV3, inColor); + DrawLine(inV3, inV1, inColor); + } + +protected: + /// Implementation of DebugRenderer interface + virtual Batch CreateTriangleBatch(const Triangle *inTriangles, int inTriangleCount) override; + virtual Batch CreateTriangleBatch(const Vertex *inVertices, int inVertexCount, const uint32 *inIndices, int inIndexCount) override; + virtual void DrawGeometry(RMat44Arg inModelMatrix, const AABox &inWorldSpaceBounds, float inLODScaleSq, ColorArg inModelColor, const GeometryRef &inGeometry, ECullMode inCullMode, ECastShadow inCastShadow, EDrawMode inDrawMode) override; + +private: + /// Implementation specific batch object + class BatchImpl : public RefTargetVirtual + { + public: + JPH_OVERRIDE_NEW_DELETE + + virtual void AddRef() override { ++mRefCount; } + virtual void Release() override { if (--mRefCount == 0) delete this; } + + Array mTriangles; + + private: + atomic mRefCount = 0; + }; + + /// Last provided camera position + RVec3 mCameraPos; + bool mCameraPosSet = false; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Skeleton/SkeletalAnimation.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Skeleton/SkeletalAnimation.cpp new file mode 100644 index 00000000000..4bf87007e9a --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Skeleton/SkeletalAnimation.cpp @@ -0,0 +1,110 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SkeletalAnimation::JointState) +{ + JPH_ADD_ATTRIBUTE(JointState, mRotation) + JPH_ADD_ATTRIBUTE(JointState, mTranslation) +} + +JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SkeletalAnimation::Keyframe) +{ + JPH_ADD_BASE_CLASS(Keyframe, JointState) + + JPH_ADD_ATTRIBUTE(Keyframe, mTime) +} + +JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SkeletalAnimation::AnimatedJoint) +{ + JPH_ADD_ATTRIBUTE(AnimatedJoint, mJointName) + JPH_ADD_ATTRIBUTE(AnimatedJoint, mKeyframes) +} + +JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SkeletalAnimation) +{ + JPH_ADD_ATTRIBUTE(SkeletalAnimation, mAnimatedJoints) + JPH_ADD_ATTRIBUTE(SkeletalAnimation, mIsLooping) +} + + +void SkeletalAnimation::JointState::FromMatrix(Mat44Arg inMatrix) +{ + mRotation = inMatrix.GetQuaternion(); + mTranslation = inMatrix.GetTranslation(); +} + +float SkeletalAnimation::GetDuration() const +{ + if (!mAnimatedJoints.empty() && !mAnimatedJoints[0].mKeyframes.empty()) + return mAnimatedJoints[0].mKeyframes.back().mTime; + else + return 0.0f; +} + +void SkeletalAnimation::ScaleJoints(float inScale) +{ + for (SkeletalAnimation::AnimatedJoint &j : mAnimatedJoints) + for (SkeletalAnimation::Keyframe &k : j.mKeyframes) + k.mTranslation *= inScale; +} + +void SkeletalAnimation::Sample(float inTime, SkeletonPose &ioPose) const +{ + // Correct time when animation is looping + JPH_ASSERT(inTime >= 0.0f); + float duration = GetDuration(); + float time = duration > 0.0f && mIsLooping? fmod(inTime, duration) : inTime; + + for (const AnimatedJoint &aj : mAnimatedJoints) + { + // Do binary search for keyframe + int high = (int)aj.mKeyframes.size(), low = -1; + while (high - low > 1) + { + int probe = (high + low) / 2; + if (aj.mKeyframes[probe].mTime < time) + low = probe; + else + high = probe; + } + + JointState &state = ioPose.GetJoint(ioPose.GetSkeleton()->GetJointIndex(aj.mJointName)); + + if (low == -1) + { + // Before first key, return first key + state = static_cast(aj.mKeyframes.front()); + } + else if (high == (int)aj.mKeyframes.size()) + { + // Beyond last key, return last key + state = static_cast(aj.mKeyframes.back()); + } + else + { + // Interpolate + const Keyframe &s1 = aj.mKeyframes[low]; + const Keyframe &s2 = aj.mKeyframes[low + 1]; + + float fraction = (time - s1.mTime) / (s2.mTime - s1.mTime); + JPH_ASSERT(fraction >= 0.0f && fraction <= 1.0f); + + state.mTranslation = (1.0f - fraction) * s1.mTranslation + fraction * s2.mTranslation; + JPH_ASSERT(s1.mRotation.IsNormalized()); + JPH_ASSERT(s2.mRotation.IsNormalized()); + state.mRotation = s1.mRotation.SLERP(s2.mRotation, fraction); + JPH_ASSERT(state.mRotation.IsNormalized()); + } + } +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Skeleton/SkeletalAnimation.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Skeleton/SkeletalAnimation.h new file mode 100644 index 00000000000..805912091e7 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Skeleton/SkeletalAnimation.h @@ -0,0 +1,77 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +JPH_NAMESPACE_BEGIN + +class SkeletonPose; + +/// Resource for a skinned animation +class JPH_EXPORT SkeletalAnimation : public RefTarget +{ +public: + JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, SkeletalAnimation) + + /// Contains the current state of a joint, a local space transformation relative to its parent joint + class JointState + { + public: + JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, JointState) + + /// Convert from a local space matrix + void FromMatrix(Mat44Arg inMatrix); + + /// Convert to matrix representation + inline Mat44 ToMatrix() const { return Mat44::sRotationTranslation(mRotation, mTranslation); } + + Quat mRotation = Quat::sIdentity(); ///< Local space rotation of the joint + Vec3 mTranslation = Vec3::sZero(); ///< Local space translation of the joint + }; + + /// Contains the state of a single joint at a particular time + class Keyframe : public JointState + { + public: + JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, Keyframe) + + float mTime = 0.0f; ///< Time of keyframe in seconds + }; + + using KeyframeVector = Array; + + /// Contains the animation for a single joint + class AnimatedJoint + { + public: + JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, AnimatedJoint) + + String mJointName; ///< Name of the joint + KeyframeVector mKeyframes; ///< List of keyframes over time + }; + + using AnimatedJointVector = Array; + + /// Get the length (in seconds) of this animation + float GetDuration() const; + + /// Scale the size of all joints by inScale + void ScaleJoints(float inScale); + + /// Get the (interpolated) joint transforms at time inTime + void Sample(float inTime, SkeletonPose &ioPose) const; + + /// Get joint samples + const AnimatedJointVector & GetAnimatedJoints() const { return mAnimatedJoints; } + AnimatedJointVector & GetAnimatedJoints() { return mAnimatedJoints; } + +private: + AnimatedJointVector mAnimatedJoints; ///< List of joints and keyframes + bool mIsLooping = true; ///< If this animation loops back to start +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Skeleton/Skeleton.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Skeleton/Skeleton.cpp new file mode 100644 index 00000000000..5ab7f56e792 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Skeleton/Skeleton.cpp @@ -0,0 +1,82 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(Skeleton::Joint) +{ + JPH_ADD_ATTRIBUTE(Joint, mName) + JPH_ADD_ATTRIBUTE(Joint, mParentName) +} + +JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(Skeleton) +{ + JPH_ADD_ATTRIBUTE(Skeleton, mJoints) +} + +int Skeleton::GetJointIndex(const string_view &inName) const +{ + for (int i = 0; i < (int)mJoints.size(); ++i) + if (mJoints[i].mName == inName) + return i; + + return -1; +} + +void Skeleton::CalculateParentJointIndices() +{ + for (Joint &j : mJoints) + j.mParentJointIndex = GetJointIndex(j.mParentName); +} + +bool Skeleton::AreJointsCorrectlyOrdered() const +{ + for (int i = 0; i < (int)mJoints.size(); ++i) + if (mJoints[i].mParentJointIndex >= i) + return false; + + return true; +} + +void Skeleton::SaveBinaryState(StreamOut &inStream) const +{ + inStream.Write((uint32)mJoints.size()); + for (const Joint &j : mJoints) + { + inStream.Write(j.mName); + inStream.Write(j.mParentJointIndex); + inStream.Write(j.mParentName); + } +} + +Skeleton::SkeletonResult Skeleton::sRestoreFromBinaryState(StreamIn &inStream) +{ + Ref skeleton = new Skeleton; + + uint32 len = 0; + inStream.Read(len); + skeleton->mJoints.resize(len); + for (Joint &j : skeleton->mJoints) + { + inStream.Read(j.mName); + inStream.Read(j.mParentJointIndex); + inStream.Read(j.mParentName); + } + + SkeletonResult result; + if (inStream.IsEOF() || inStream.IsFailed()) + result.SetError("Failed to read skeleton from stream"); + else + result.Set(skeleton); + return result; +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Skeleton/Skeleton.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Skeleton/Skeleton.h new file mode 100644 index 00000000000..3c2b84c6693 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Skeleton/Skeleton.h @@ -0,0 +1,72 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +class StreamIn; +class StreamOut; + +/// Resource that contains the joint hierarchy for a skeleton +class JPH_EXPORT Skeleton : public RefTarget +{ +public: + JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, Skeleton) + + using SkeletonResult = Result>; + + /// Declare internal structure for a joint + class Joint + { + public: + JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, Joint) + + Joint() = default; + Joint(const string_view &inName, const string_view &inParentName, int inParentJointIndex) : mName(inName), mParentName(inParentName), mParentJointIndex(inParentJointIndex) { } + + String mName; ///< Name of the joint + String mParentName; ///< Name of parent joint + int mParentJointIndex = -1; ///< Index of parent joint (in mJoints) or -1 if it has no parent + }; + + using JointVector = Array; + + ///@name Access to the joints + ///@{ + const JointVector & GetJoints() const { return mJoints; } + JointVector & GetJoints() { return mJoints; } + int GetJointCount() const { return (int)mJoints.size(); } + const Joint & GetJoint(int inJoint) const { return mJoints[inJoint]; } + Joint & GetJoint(int inJoint) { return mJoints[inJoint]; } + uint AddJoint(const string_view &inName, const string_view &inParentName = string_view()) { mJoints.emplace_back(inName, inParentName, -1); return (uint)mJoints.size() - 1; } + uint AddJoint(const string_view &inName, int inParentIndex) { mJoints.emplace_back(inName, inParentIndex >= 0? mJoints[inParentIndex].mName : String(), inParentIndex); return (uint)mJoints.size() - 1; } + ///@} + + /// Find joint by name + int GetJointIndex(const string_view &inName) const; + + /// Fill in parent joint indices based on name + void CalculateParentJointIndices(); + + /// Many of the algorithms that use the Skeleton class require that parent joints are in the mJoints array before their children. + /// This function returns true if this is the case, false if not. + bool AreJointsCorrectlyOrdered() const; + + /// Saves the state of this object in binary form to inStream. + void SaveBinaryState(StreamOut &inStream) const; + + /// Restore the state of this object from inStream. + static SkeletonResult sRestoreFromBinaryState(StreamIn &inStream); + +private: + /// Joints + JointVector mJoints; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Skeleton/SkeletonMapper.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Skeleton/SkeletonMapper.cpp new file mode 100644 index 00000000000..17f6a46de26 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Skeleton/SkeletonMapper.cpp @@ -0,0 +1,237 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include + +JPH_NAMESPACE_BEGIN + +void SkeletonMapper::Initialize(const Skeleton *inSkeleton1, const Mat44 *inNeutralPose1, const Skeleton *inSkeleton2, const Mat44 *inNeutralPose2, const CanMapJoint &inCanMapJoint) +{ + JPH_ASSERT(mMappings.empty() && mChains.empty() && mUnmapped.empty()); // Should not be initialized yet + + // Count joints + int n1 = inSkeleton1->GetJointCount(); + int n2 = inSkeleton2->GetJointCount(); + JPH_ASSERT(n1 <= n2, "Skeleton 1 should be the low detail skeleton!"); + + // Keep track of mapped joints (initialize to false) + Array mapped1(n1, false); + Array mapped2(n2, false); + + // Find joints that can be mapped directly + for (int j1 = 0; j1 < n1; ++j1) + for (int j2 = 0; j2 < n2; ++j2) + if (inCanMapJoint(inSkeleton1, j1, inSkeleton2, j2)) + { + // Calculate the transform that takes this joint from skeleton 1 to 2 + Mat44 joint_1_to_2 = inNeutralPose1[j1].Inversed() * inNeutralPose2[j2]; + + // Ensure bottom right element is 1 (numerical imprecision in the inverse can make this not so) + joint_1_to_2(3, 3) = 1.0f; + + mMappings.emplace_back(j1, j2, joint_1_to_2); + mapped1[j1] = true; + mapped2[j2] = true; + break; + } + + Array cur_chain; // Taken out of the loop to minimize amount of allocations + + // Find joint chains + for (int m1 = 0; m1 < (int)mMappings.size(); ++m1) + { + Array chain2; + int chain2_m = -1; + + for (int m2 = m1 + 1; m2 < (int)mMappings.size(); ++m2) + { + // Find the chain from back from m2 to m1 + int start = mMappings[m1].mJointIdx2; + int end = mMappings[m2].mJointIdx2; + int cur = end; + cur_chain.clear(); // Should preserve memory + do + { + cur_chain.push_back(cur); + cur = inSkeleton2->GetJoint(cur).mParentJointIndex; + } + while (cur >= 0 && cur != start && !mapped2[cur]); + cur_chain.push_back(start); + + if (cur == start // This should be the correct chain + && cur_chain.size() > 2 // It should have joints between the mapped joints + && cur_chain.size() > chain2.size()) // And it should be the longest so far + { + chain2.swap(cur_chain); + chain2_m = m2; + } + } + + if (!chain2.empty()) + { + // Get the chain for 1 + Array chain1; + int start = mMappings[m1].mJointIdx1; + int cur = mMappings[chain2_m].mJointIdx1; + do + { + chain1.push_back(cur); + cur = inSkeleton1->GetJoint(cur).mParentJointIndex; + } + while (cur >= 0 && cur != start && !mapped1[cur]); + chain1.push_back(start); + + // If the chain exists in 1 too + if (cur == start) + { + // Reverse the chains + reverse(chain1.begin(), chain1.end()); + reverse(chain2.begin(), chain2.end()); + + // Mark elements mapped + for (int j1 : chain1) + mapped1[j1] = true; + for (int j2 : chain2) + mapped2[j2] = true; + + // Insert the chain + mChains.emplace_back(std::move(chain1), std::move(chain2)); + } + } + } + + // Collect unmapped joints from 2 + for (int j2 = 0; j2 < n2; ++j2) + if (!mapped2[j2]) + mUnmapped.emplace_back(j2, inSkeleton2->GetJoint(j2).mParentJointIndex); +} + +void SkeletonMapper::LockTranslations(const Skeleton *inSkeleton2, const bool *inLockedTranslations, const Mat44 *inNeutralPose2) +{ + JPH_ASSERT(inSkeleton2->AreJointsCorrectlyOrdered()); + + int n = inSkeleton2->GetJointCount(); + + // Copy locked joints to array but don't actually include the first joint (this is physics driven) + for (int i = 0; i < n; ++i) + if (inLockedTranslations[i]) + { + Locked l; + l.mJointIdx = i; + l.mParentJointIdx = inSkeleton2->GetJoint(i).mParentJointIndex; + if (l.mParentJointIdx >= 0) + l.mTranslation = inNeutralPose2[l.mParentJointIdx].Inversed() * inNeutralPose2[i].GetTranslation(); + else + l.mTranslation = inNeutralPose2[i].GetTranslation(); + mLockedTranslations.push_back(l); + } +} + +void SkeletonMapper::LockAllTranslations(const Skeleton *inSkeleton2, const Mat44 *inNeutralPose2) +{ + JPH_ASSERT(!mMappings.empty(), "Call Initialize first!"); + JPH_ASSERT(inSkeleton2->AreJointsCorrectlyOrdered()); + + // The first mapping is the top most one (remember that joints should be ordered so that parents go before children). + // Because we created the mappings from the lowest joint first, this should contain the first mappable joint. + int root_idx = mMappings[0].mJointIdx2; + + // Create temp array to hold locked joints + int n = inSkeleton2->GetJointCount(); + bool *locked_translations = (bool *)JPH_STACK_ALLOC(n * sizeof(bool)); + memset(locked_translations, 0, n * sizeof(bool)); + + // Mark root as locked + locked_translations[root_idx] = true; + + // Loop over all joints and propagate the locked flag to all children + for (int i = root_idx + 1; i < n; ++i) + { + int parent_idx = inSkeleton2->GetJoint(i).mParentJointIndex; + if (parent_idx >= 0) + locked_translations[i] = locked_translations[parent_idx]; + } + + // Unmark root because we don't actually want to include this (this determines the position of the entire ragdoll) + locked_translations[root_idx] = false; + + // Call the generic function + LockTranslations(inSkeleton2, locked_translations, inNeutralPose2); +} + +void SkeletonMapper::Map(const Mat44 *inPose1ModelSpace, const Mat44 *inPose2LocalSpace, Mat44 *outPose2ModelSpace) const +{ + // Apply direct mappings + for (const Mapping &m : mMappings) + outPose2ModelSpace[m.mJointIdx2] = inPose1ModelSpace[m.mJointIdx1] * m.mJoint1To2; + + // Apply chain mappings + for (const Chain &c : mChains) + { + // Calculate end of chain given local space transforms of the joints of the chain + Mat44 &chain_start = outPose2ModelSpace[c.mJointIndices2.front()]; + Mat44 chain_end = chain_start; + for (int j = 1; j < (int)c.mJointIndices2.size(); ++j) + chain_end = chain_end * inPose2LocalSpace[c.mJointIndices2[j]]; + + // Calculate the direction in world space for skeleton 1 and skeleton 2 and the rotation between them + Vec3 actual = chain_end.GetTranslation() - chain_start.GetTranslation(); + Vec3 desired = inPose1ModelSpace[c.mJointIndices1.back()].GetTranslation() - inPose1ModelSpace[c.mJointIndices1.front()].GetTranslation(); + Quat rotation = Quat::sFromTo(actual, desired); + + // Rotate the start of the chain + chain_start.SetRotation(Mat44::sRotation(rotation) * chain_start.GetRotation()); + + // Update all joints but the first and the last joint using their local space transforms + for (int j = 1; j < (int)c.mJointIndices2.size() - 1; ++j) + { + int parent = c.mJointIndices2[j - 1]; + int child = c.mJointIndices2[j]; + outPose2ModelSpace[child] = outPose2ModelSpace[parent] * inPose2LocalSpace[child]; + } + } + + // All unmapped joints take the local pose and convert it to model space + for (const Unmapped &u : mUnmapped) + if (u.mParentJointIdx >= 0) + { + JPH_ASSERT(u.mParentJointIdx < u.mJointIdx, "Joints must be ordered: parents first"); + outPose2ModelSpace[u.mJointIdx] = outPose2ModelSpace[u.mParentJointIdx] * inPose2LocalSpace[u.mJointIdx]; + } + else + outPose2ModelSpace[u.mJointIdx] = inPose2LocalSpace[u.mJointIdx]; + + // Update all locked joint translations + for (const Locked &l : mLockedTranslations) + outPose2ModelSpace[l.mJointIdx].SetTranslation(outPose2ModelSpace[l.mParentJointIdx] * l.mTranslation); +} + +void SkeletonMapper::MapReverse(const Mat44 *inPose2ModelSpace, Mat44 *outPose1ModelSpace) const +{ + // Normally each joint in skeleton 1 should be present in the mapping, so we only need to apply the direct mappings + for (const Mapping &m : mMappings) + outPose1ModelSpace[m.mJointIdx1] = inPose2ModelSpace[m.mJointIdx2] * m.mJoint2To1; +} + +int SkeletonMapper::GetMappedJointIdx(int inJoint1Idx) const +{ + for (const Mapping &m : mMappings) + if (m.mJointIdx1 == inJoint1Idx) + return m.mJointIdx2; + + return -1; +} + +bool SkeletonMapper::IsJointTranslationLocked(int inJoint2Idx) const +{ + for (const Locked &l : mLockedTranslations) + if (l.mJointIdx == inJoint2Idx) + return true; + + return false; +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Skeleton/SkeletonMapper.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Skeleton/SkeletonMapper.h new file mode 100644 index 00000000000..05cc8866a4b --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Skeleton/SkeletonMapper.h @@ -0,0 +1,145 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2022 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +JPH_NAMESPACE_BEGIN + +/// Class that is able to map a low detail (ragdoll) skeleton to a high detail (animation) skeleton and vice versa +class JPH_EXPORT SkeletonMapper : public RefTarget +{ +public: + /// A joint that maps 1-on-1 to a joint in the other skeleton + class Mapping + { + public: + Mapping() = default; + Mapping(int inJointIdx1, int inJointIdx2, Mat44Arg inJoint1To2) : mJointIdx1(inJointIdx1), mJointIdx2(inJointIdx2), mJoint1To2(inJoint1To2), mJoint2To1(inJoint1To2.Inversed()) + { + // Ensure bottom right element is 1 (numerical imprecision in the inverse can make this not so) + mJoint2To1(3, 3) = 1.0f; + } + + int mJointIdx1; ///< Index of joint from skeleton 1 + int mJointIdx2; ///< Corresponding index of joint from skeleton 2 + Mat44 mJoint1To2; ///< Transforms this joint from skeleton 1 to 2 + Mat44 mJoint2To1; ///< Inverse of the transform above + }; + + /// A joint chain that starts with a 1-on-1 mapped joint and ends with a 1-on-1 mapped joint with intermediate joints that cannot be mapped + class Chain + { + public: + Chain() = default; + Chain(Array &&inJointIndices1, Array &&inJointIndices2) : mJointIndices1(std::move(inJointIndices1)), mJointIndices2(std::move(inJointIndices2)) { } + + Array mJointIndices1; ///< Joint chain from skeleton 1 + Array mJointIndices2; ///< Corresponding joint chain from skeleton 2 + }; + + /// Joints that could not be mapped from skeleton 1 to 2 + class Unmapped + { + public: + Unmapped() = default; + Unmapped(int inJointIdx, int inParentJointIdx) : mJointIdx(inJointIdx), mParentJointIdx(inParentJointIdx) { } + + int mJointIdx; ///< Joint index of unmappable joint + int mParentJointIdx; ///< Parent joint index of unmappable joint + }; + + /// Joints that should have their translation locked (fixed) + class Locked + { + public: + int mJointIdx; ///< Joint index of joint with locked translation (in skeleton 2) + int mParentJointIdx; ///< Parent joint index of joint with locked translation (in skeleton 2) + Vec3 mTranslation; ///< Translation of neutral pose + }; + + /// A function that is called to determine if a joint can be mapped from source to target skeleton + using CanMapJoint = function; + + /// Default function that checks if the names of the joints are equal + static bool sDefaultCanMapJoint(const Skeleton *inSkeleton1, int inIndex1, const Skeleton *inSkeleton2, int inIndex2) + { + return inSkeleton1->GetJoint(inIndex1).mName == inSkeleton2->GetJoint(inIndex2).mName; + } + + /// Initialize the skeleton mapper. Skeleton 1 should be the (low detail) ragdoll skeleton and skeleton 2 the (high detail) animation skeleton. + /// We assume that each joint in skeleton 1 can be mapped to a joint in skeleton 2 (if not mapping from animation skeleton to ragdoll skeleton will be undefined). + /// Skeleton 2 should have the same hierarchy as skeleton 1 but can contain extra joints between those in skeleton 1 and it can have extra joints at the root and leaves of the skeleton. + /// @param inSkeleton1 Source skeleton to map from. + /// @param inNeutralPose1 Neutral pose of the source skeleton (model space) + /// @param inSkeleton2 Target skeleton to map to. + /// @param inNeutralPose2 Neutral pose of the target skeleton (model space), inNeutralPose1 and inNeutralPose2 must match as closely as possible, preferably the position of the mappable joints should be identical. + /// @param inCanMapJoint Function that checks if joints in skeleton 1 and skeleton 2 are equal. + void Initialize(const Skeleton *inSkeleton1, const Mat44 *inNeutralPose1, const Skeleton *inSkeleton2, const Mat44 *inNeutralPose2, const CanMapJoint &inCanMapJoint = sDefaultCanMapJoint); + + /// This can be called so lock the translation of a specified set of joints in skeleton 2. + /// Because constraints are never 100% rigid, there's always a little bit of stretch in the ragdoll when the ragdoll is under stress. + /// Locking the translations of the pose will remove the visual stretch from the ragdoll but will introduce a difference between the + /// physical simulation and the visual representation. + /// @param inSkeleton2 Target skeleton to map to. + /// @param inLockedTranslations An array of bools the size of inSkeleton2->GetJointCount(), for each joint indicating if the joint is locked. + /// @param inNeutralPose2 Neutral pose to take reference translations from + void LockTranslations(const Skeleton *inSkeleton2, const bool *inLockedTranslations, const Mat44 *inNeutralPose2); + + /// After Initialize(), this can be called to lock the translation of all joints in skeleton 2 below the first mapped joint to those of the neutral pose. + /// Because constraints are never 100% rigid, there's always a little bit of stretch in the ragdoll when the ragdoll is under stress. + /// Locking the translations of the pose will remove the visual stretch from the ragdoll but will introduce a difference between the + /// physical simulation and the visual representation. + /// @param inSkeleton2 Target skeleton to map to. + /// @param inNeutralPose2 Neutral pose to take reference translations from + void LockAllTranslations(const Skeleton *inSkeleton2, const Mat44 *inNeutralPose2); + + /// Map a pose. Joints that were directly mappable will be copied in model space from pose 1 to pose 2. Any joints that are only present in skeleton 2 + /// will get their model space transform calculated through the local space transforms of pose 2. Joints that are part of a joint chain between two + /// mapped joints will be reoriented towards the next joint in skeleton 1. This means that it is possible for unmapped joints to have some animation, + /// but very extreme animation poses will show artifacts. + /// @param inPose1ModelSpace Pose on skeleton 1 in model space + /// @param inPose2LocalSpace Pose on skeleton 2 in local space (used for the joints that cannot be mapped) + /// @param outPose2ModelSpace Model space pose on skeleton 2 (the output of the mapping) + void Map(const Mat44 *inPose1ModelSpace, const Mat44 *inPose2LocalSpace, Mat44 *outPose2ModelSpace) const; + + /// Reverse map a pose, this will only use the mappings and not the chains (it assumes that all joints in skeleton 1 are mapped) + /// @param inPose2ModelSpace Model space pose on skeleton 2 + /// @param outPose1ModelSpace When the function returns this will contain the model space pose for skeleton 1 + void MapReverse(const Mat44 *inPose2ModelSpace, Mat44 *outPose1ModelSpace) const; + + /// Search through the directly mapped joints (mMappings) and find inJoint1Idx, returns the corresponding Joint2Idx or -1 if not found. + int GetMappedJointIdx(int inJoint1Idx) const; + + /// Search through the locked translations (mLockedTranslations) and find if joint inJoint2Idx is locked. + bool IsJointTranslationLocked(int inJoint2Idx) const; + + using MappingVector = Array; + using ChainVector = Array; + using UnmappedVector = Array; + using LockedVector = Array; + + ///@name Access to the mapped joints + ///@{ + const MappingVector & GetMappings() const { return mMappings; } + MappingVector & GetMappings() { return mMappings; } + const ChainVector & GetChains() const { return mChains; } + ChainVector & GetChains() { return mChains; } + const UnmappedVector & GetUnmapped() const { return mUnmapped; } + UnmappedVector & GetUnmapped() { return mUnmapped; } + const LockedVector & GetLockedTranslations() const { return mLockedTranslations; } + LockedVector & GetLockedTranslations() { return mLockedTranslations; } + ///@} + +private: + /// Joint mappings + MappingVector mMappings; + ChainVector mChains; + UnmappedVector mUnmapped; ///< Joint indices that could not be mapped from 1 to 2 (these are indices in 2) + LockedVector mLockedTranslations; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Skeleton/SkeletonPose.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Skeleton/SkeletonPose.cpp new file mode 100644 index 00000000000..c64bf049716 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Skeleton/SkeletonPose.cpp @@ -0,0 +1,87 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#ifdef JPH_DEBUG_RENDERER + #include +#endif // JPH_DEBUG_RENDERER + +JPH_NAMESPACE_BEGIN + +void SkeletonPose::SetSkeleton(const Skeleton *inSkeleton) +{ + mSkeleton = inSkeleton; + + mJoints.resize(mSkeleton->GetJointCount()); + mJointMatrices.resize(mSkeleton->GetJointCount()); +} + +void SkeletonPose::CalculateJointMatrices() +{ + for (int i = 0; i < (int)mJoints.size(); ++i) + { + mJointMatrices[i] = mJoints[i].ToMatrix(); + + int parent = mSkeleton->GetJoint(i).mParentJointIndex; + if (parent >= 0) + { + JPH_ASSERT(parent < i, "Joints must be ordered: parents first"); + mJointMatrices[i] = mJointMatrices[parent] * mJointMatrices[i]; + } + } +} + +void SkeletonPose::CalculateJointStates() +{ + for (int i = 0; i < (int)mJoints.size(); ++i) + { + Mat44 local_transform; + int parent = mSkeleton->GetJoint(i).mParentJointIndex; + if (parent >= 0) + local_transform = mJointMatrices[parent].Inversed() * mJointMatrices[i]; + else + local_transform = mJointMatrices[i]; + + JointState &joint = mJoints[i]; + joint.mTranslation = local_transform.GetTranslation(); + joint.mRotation = local_transform.GetQuaternion(); + } +} + +void SkeletonPose::CalculateLocalSpaceJointMatrices(Mat44 *outMatrices) const +{ + for (int i = 0; i < (int)mJoints.size(); ++i) + outMatrices[i] = mJoints[i].ToMatrix(); +} + +#ifdef JPH_DEBUG_RENDERER +void SkeletonPose::Draw(const DrawSettings &inDrawSettings, DebugRenderer *inRenderer, RMat44Arg inOffset) const +{ + RMat44 offset = inOffset * RMat44::sTranslation(mRootOffset); + + const Skeleton::JointVector &joints = mSkeleton->GetJoints(); + + for (int b = 0; b < mSkeleton->GetJointCount(); ++b) + { + RMat44 joint_transform = offset * mJointMatrices[b]; + + if (inDrawSettings.mDrawJoints) + { + int parent = joints[b].mParentJointIndex; + if (parent >= 0) + inRenderer->DrawLine(offset * mJointMatrices[parent].GetTranslation(), joint_transform.GetTranslation(), Color::sGreen); + } + + if (inDrawSettings.mDrawJointOrientations) + inRenderer->DrawCoordinateSystem(joint_transform, 0.05f); + + if (inDrawSettings.mDrawJointNames) + inRenderer->DrawText3D(joint_transform.GetTranslation(), joints[b].mName, Color::sWhite, 0.05f); + } +} +#endif // JPH_DEBUG_RENDERER + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Skeleton/SkeletonPose.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Skeleton/SkeletonPose.h new file mode 100644 index 00000000000..326227e447a --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Skeleton/SkeletonPose.h @@ -0,0 +1,82 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +JPH_NAMESPACE_BEGIN + +#ifdef JPH_DEBUG_RENDERER +class DebugRenderer; +#endif // JPH_DEBUG_RENDERER + +/// Instance of a skeleton, contains the pose the current skeleton is in +class JPH_EXPORT SkeletonPose +{ +public: + JPH_OVERRIDE_NEW_DELETE + + using JointState = SkeletalAnimation::JointState; + using JointStateVector = Array; + using Mat44Vector = Array; + + ///@name Skeleton + ///@{ + void SetSkeleton(const Skeleton *inSkeleton); + const Skeleton * GetSkeleton() const { return mSkeleton; } + ///@} + + /// Extra offset applied to the root (and therefore also to all of its children) + void SetRootOffset(RVec3Arg inOffset) { mRootOffset = inOffset; } + RVec3 GetRootOffset() const { return mRootOffset; } + + ///@name Properties of the joints + ///@{ + uint GetJointCount() const { return (uint)mJoints.size(); } + const JointStateVector & GetJoints() const { return mJoints; } + JointStateVector & GetJoints() { return mJoints; } + const JointState & GetJoint(int inJoint) const { return mJoints[inJoint]; } + JointState & GetJoint(int inJoint) { return mJoints[inJoint]; } + ///@} + + ///@name Joint matrices + ///@{ + const Mat44Vector & GetJointMatrices() const { return mJointMatrices; } + Mat44Vector & GetJointMatrices() { return mJointMatrices; } + const Mat44 & GetJointMatrix(int inJoint) const { return mJointMatrices[inJoint]; } + Mat44 & GetJointMatrix(int inJoint) { return mJointMatrices[inJoint]; } + ///@} + + /// Convert the joint states to joint matrices + void CalculateJointMatrices(); + + /// Convert joint matrices to joint states + void CalculateJointStates(); + + /// Outputs the joint matrices in local space (ensure that outMatrices has GetJointCount() elements, assumes that values in GetJoints() is up to date) + void CalculateLocalSpaceJointMatrices(Mat44 *outMatrices) const; + +#ifdef JPH_DEBUG_RENDERER + /// Draw settings + struct DrawSettings + { + bool mDrawJoints = true; + bool mDrawJointOrientations = true; + bool mDrawJointNames = false; + }; + + /// Draw current pose + void Draw(const DrawSettings &inDrawSettings, DebugRenderer *inRenderer, RMat44Arg inOffset = RMat44::sIdentity()) const; +#endif // JPH_DEBUG_RENDERER + +private: + RefConst mSkeleton; ///< Skeleton definition + RVec3 mRootOffset { RVec3::sZero() }; ///< Extra offset applied to the root (and therefore also to all of its children) + JointStateVector mJoints; ///< Local joint orientations (local to parent Joint) + Mat44Vector mJointMatrices; ///< Local joint matrices (local to world matrix) +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleGrouper/TriangleGrouper.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleGrouper/TriangleGrouper.h new file mode 100644 index 00000000000..9d75691f68c --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleGrouper/TriangleGrouper.h @@ -0,0 +1,27 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +JPH_NAMESPACE_BEGIN + +/// A class that groups triangles in batches of N (according to closeness) +class JPH_EXPORT TriangleGrouper : public NonCopyable +{ +public: + /// Virtual destructor + virtual ~TriangleGrouper() = default; + + /// Group a batch of indexed triangles + /// @param inVertices The list of vertices + /// @param inTriangles The list of indexed triangles (indexes into inVertices) + /// @param inGroupSize How big each group should be + /// @param outGroupedTriangleIndices An ordered list of indices (indexing into inTriangles), contains groups of inGroupSize large worth of indices to triangles that are grouped together. If the triangle count is not an exact multiple of inGroupSize the last batch will be smaller. + virtual void Group(const VertexList &inVertices, const IndexedTriangleList &inTriangles, int inGroupSize, Array &outGroupedTriangleIndices) = 0; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleGrouper/TriangleGrouperClosestCentroid.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleGrouper/TriangleGrouperClosestCentroid.cpp new file mode 100644 index 00000000000..b59cdda9adb --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleGrouper/TriangleGrouperClosestCentroid.cpp @@ -0,0 +1,95 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +void TriangleGrouperClosestCentroid::Group(const VertexList &inVertices, const IndexedTriangleList &inTriangles, int inGroupSize, Array &outGroupedTriangleIndices) +{ + const uint triangle_count = (uint)inTriangles.size(); + const uint num_batches = (triangle_count + inGroupSize - 1) / inGroupSize; + + Array centroids; + centroids.resize(triangle_count); + + outGroupedTriangleIndices.resize(triangle_count); + + for (uint t = 0; t < triangle_count; ++t) + { + // Store centroid + centroids[t] = inTriangles[t].GetCentroid(inVertices); + + // Initialize sort table + outGroupedTriangleIndices[t] = t; + } + + Array::iterator triangles_end = outGroupedTriangleIndices.end(); + + // Sort per batch + for (uint b = 0; b < num_batches - 1; ++b) + { + // Get iterators + Array::iterator batch_begin = outGroupedTriangleIndices.begin() + b * inGroupSize; + Array::iterator batch_end = batch_begin + inGroupSize; + Array::iterator batch_begin_plus_1 = batch_begin + 1; + Array::iterator batch_end_minus_1 = batch_end - 1; + + // Find triangle with centroid with lowest X coordinate + Array::iterator lowest_iter = batch_begin; + float lowest_val = centroids[*lowest_iter].GetX(); + for (Array::iterator other = batch_begin; other != triangles_end; ++other) + { + float val = centroids[*other].GetX(); + if (val < lowest_val) + { + lowest_iter = other; + lowest_val = val; + } + } + + // Make this triangle the first in a new batch + swap(*batch_begin, *lowest_iter); + Vec3 first_centroid = centroids[*batch_begin]; + + // Sort remaining triangles in batch on distance to first triangle + QuickSort(batch_begin_plus_1, batch_end, + [&first_centroid, ¢roids](uint inLHS, uint inRHS) + { + return (centroids[inLHS] - first_centroid).LengthSq() < (centroids[inRHS] - first_centroid).LengthSq(); + }); + + // Loop over remaining triangles + float furthest_dist = (centroids[*batch_end_minus_1] - first_centroid).LengthSq(); + for (Array::iterator other = batch_end; other != triangles_end; ++other) + { + // Check if this triangle is closer than the furthest triangle in the batch + float dist = (centroids[*other] - first_centroid).LengthSq(); + if (dist < furthest_dist) + { + // Replace furthest triangle + uint other_val = *other; + *other = *batch_end_minus_1; + + // Find first element that is bigger than this one and insert the current item before it + Array::iterator upper = std::upper_bound(batch_begin_plus_1, batch_end, dist, + [&first_centroid, ¢roids](float inLHS, uint inRHS) + { + return inLHS < (centroids[inRHS] - first_centroid).LengthSq(); + }); + copy_backward(upper, batch_end_minus_1, batch_end); + *upper = other_val; + + // Calculate new furthest distance + furthest_dist = (centroids[*batch_end_minus_1] - first_centroid).LengthSq(); + } + } + } +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleGrouper/TriangleGrouperClosestCentroid.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleGrouper/TriangleGrouperClosestCentroid.h new file mode 100644 index 00000000000..58322741646 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleGrouper/TriangleGrouperClosestCentroid.h @@ -0,0 +1,21 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +/// A class that groups triangles in batches of N. +/// Starts with centroid with lowest X coordinate and finds N closest centroids, this repeats until all groups have been found. +/// Time complexity: O(N^2) +class JPH_EXPORT TriangleGrouperClosestCentroid : public TriangleGrouper +{ +public: + // See: TriangleGrouper::Group + virtual void Group(const VertexList &inVertices, const IndexedTriangleList &inTriangles, int inGroupSize, Array &outGroupedTriangleIndices) override; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleGrouper/TriangleGrouperMorton.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleGrouper/TriangleGrouperMorton.cpp new file mode 100644 index 00000000000..a6e5a564426 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleGrouper/TriangleGrouperMorton.cpp @@ -0,0 +1,49 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +void TriangleGrouperMorton::Group(const VertexList &inVertices, const IndexedTriangleList &inTriangles, int inGroupSize, Array &outGroupedTriangleIndices) +{ + const uint triangle_count = (uint)inTriangles.size(); + + Array centroids; + centroids.resize(triangle_count); + + outGroupedTriangleIndices.resize(triangle_count); + + for (uint t = 0; t < triangle_count; ++t) + { + // Store centroid + centroids[t] = inTriangles[t].GetCentroid(inVertices); + + // Initialize sort table + outGroupedTriangleIndices[t] = t; + } + + // Get bounding box of all centroids + AABox centroid_bounds; + for (uint t = 0; t < triangle_count; ++t) + centroid_bounds.Encapsulate(centroids[t]); + + // Make sure box is not degenerate + centroid_bounds.EnsureMinimalEdgeLength(1.0e-5f); + + // Calculate morton code for each centroid + Array morton_codes; + morton_codes.resize(triangle_count); + for (uint t = 0; t < triangle_count; ++t) + morton_codes[t] = MortonCode::sGetMortonCode(centroids[t], centroid_bounds); + + // Sort triangles based on morton code + QuickSort(outGroupedTriangleIndices.begin(), outGroupedTriangleIndices.end(), [&morton_codes](uint inLHS, uint inRHS) { return morton_codes[inLHS] < morton_codes[inRHS]; }); +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleGrouper/TriangleGrouperMorton.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleGrouper/TriangleGrouperMorton.h new file mode 100644 index 00000000000..a35f9af004e --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleGrouper/TriangleGrouperMorton.h @@ -0,0 +1,20 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +/// A class that groups triangles in batches of N according to morton code of centroid. +/// Time complexity: O(N log(N)) +class JPH_EXPORT TriangleGrouperMorton : public TriangleGrouper +{ +public: + // See: TriangleGrouper::Group + virtual void Group(const VertexList &inVertices, const IndexedTriangleList &inTriangles, int inGroupSize, Array &outGroupedTriangleIndices) override; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitter.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitter.cpp new file mode 100644 index 00000000000..b7852a919c5 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitter.cpp @@ -0,0 +1,67 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include + +JPH_NAMESPACE_BEGIN + +TriangleSplitter::TriangleSplitter(const VertexList &inVertices, const IndexedTriangleList &inTriangles) : + mVertices(inVertices), + mTriangles(inTriangles) +{ + mSortedTriangleIdx.resize(inTriangles.size()); + mCentroids.resize(inTriangles.size()); + + for (uint t = 0; t < inTriangles.size(); ++t) + { + // Initially triangles start unsorted + mSortedTriangleIdx[t] = t; + + // Calculate centroid + inTriangles[t].GetCentroid(inVertices).StoreFloat3(&mCentroids[t]); + } +} + +bool TriangleSplitter::SplitInternal(const Range &inTriangles, uint inDimension, float inSplit, Range &outLeft, Range &outRight) +{ + // Divide triangles + uint start = inTriangles.mBegin, end = inTriangles.mEnd; + while (start < end) + { + // Search for first element that is on the right hand side of the split plane + while (start < end && mCentroids[mSortedTriangleIdx[start]][inDimension] < inSplit) + ++start; + + // Search for the first element that is on the left hand side of the split plane + while (start < end && mCentroids[mSortedTriangleIdx[end - 1]][inDimension] >= inSplit) + --end; + + if (start < end) + { + // Swap the two elements + swap(mSortedTriangleIdx[start], mSortedTriangleIdx[end - 1]); + ++start; + --end; + } + } + JPH_ASSERT(start == end); + +#ifdef JPH_ENABLE_ASSERTS + // Validate division algorithm + JPH_ASSERT(inTriangles.mBegin <= start); + JPH_ASSERT(start <= inTriangles.mEnd); + for (uint i = inTriangles.mBegin; i < start; ++i) + JPH_ASSERT(mCentroids[mSortedTriangleIdx[i]][inDimension] < inSplit); + for (uint i = start; i < inTriangles.mEnd; ++i) + JPH_ASSERT(mCentroids[mSortedTriangleIdx[i]][inDimension] >= inSplit); +#endif + + outLeft = Range(inTriangles.mBegin, start); + outRight = Range(start, inTriangles.mEnd); + return outLeft.Count() > 0 && outRight.Count() > 0; +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitter.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitter.h new file mode 100644 index 00000000000..a66672238fc --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitter.h @@ -0,0 +1,84 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +JPH_NAMESPACE_BEGIN + +/// A class that splits a triangle list into two parts for building a tree +class JPH_EXPORT TriangleSplitter : public NonCopyable +{ +public: + /// Constructor + TriangleSplitter(const VertexList &inVertices, const IndexedTriangleList &inTriangles); + + /// Virtual destructor + virtual ~TriangleSplitter() = default; + + struct Stats + { + const char * mSplitterName = nullptr; + int mLeafSize = 0; + }; + + /// Get stats of splitter + virtual void GetStats(Stats &outStats) const = 0; + + /// Helper struct to indicate triangle range before and after the split + struct Range + { + /// Constructor + Range() = default; + Range(uint inBegin, uint inEnd) : mBegin(inBegin), mEnd(inEnd) { } + + /// Get number of triangles in range + uint Count() const + { + return mEnd - mBegin; + } + + /// Start and end index (end = 1 beyond end) + uint mBegin; + uint mEnd; + }; + + /// Range of triangles to start with + Range GetInitialRange() const + { + return Range(0, (uint)mSortedTriangleIdx.size()); + } + + /// Split triangles into two groups left and right, returns false if no split could be made + /// @param inTriangles The range of triangles (in mSortedTriangleIdx) to process + /// @param outLeft On return this will contain the ranges for the left subpart. mSortedTriangleIdx may have been shuffled. + /// @param outRight On return this will contain the ranges for the right subpart. mSortedTriangleIdx may have been shuffled. + /// @return Returns true when a split was found + virtual bool Split(const Range &inTriangles, Range &outLeft, Range &outRight) = 0; + + /// Get the list of vertices + const VertexList & GetVertices() const + { + return mVertices; + } + + /// Get triangle by index + const IndexedTriangle & GetTriangle(uint inIdx) const + { + return mTriangles[mSortedTriangleIdx[inIdx]]; + } + +protected: + /// Helper function to split triangles based on dimension and split value + bool SplitInternal(const Range &inTriangles, uint inDimension, float inSplit, Range &outLeft, Range &outRight); + + const VertexList & mVertices; ///< Vertices of the indexed triangles + const IndexedTriangleList & mTriangles; ///< Unsorted triangles + Array mCentroids; ///< Unsorted centroids of triangles + Array mSortedTriangleIdx; ///< Indices to sort triangles +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterBinning.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterBinning.cpp new file mode 100644 index 00000000000..989520c45ba --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterBinning.cpp @@ -0,0 +1,112 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include + + JPH_NAMESPACE_BEGIN + +TriangleSplitterBinning::TriangleSplitterBinning(const VertexList &inVertices, const IndexedTriangleList &inTriangles, uint inMinNumBins, uint inMaxNumBins, uint inNumTrianglesPerBin) : + TriangleSplitter(inVertices, inTriangles), + mMinNumBins(inMinNumBins), + mMaxNumBins(inMaxNumBins), + mNumTrianglesPerBin(inNumTrianglesPerBin) +{ + mBins.resize(mMaxNumBins); +} + +bool TriangleSplitterBinning::Split(const Range &inTriangles, Range &outLeft, Range &outRight) +{ + // Calculate bounds for this range + AABox centroid_bounds; + for (uint t = inTriangles.mBegin; t < inTriangles.mEnd; ++t) + centroid_bounds.Encapsulate(Vec3(mCentroids[mSortedTriangleIdx[t]])); + + float best_cp = FLT_MAX; + uint best_dim = 0xffffffff; + float best_split = 0; + + // Bin in all dimensions + uint num_bins = Clamp(inTriangles.Count() / mNumTrianglesPerBin, mMinNumBins, mMaxNumBins); + for (uint dim = 0; dim < 3; ++dim) + { + float bounds_min = centroid_bounds.mMin[dim]; + float bounds_size = centroid_bounds.mMax[dim] - bounds_min; + + // Skip axis if too small + if (bounds_size < 1.0e-5f) + continue; + + // Initialize bins + for (uint b = 0; b < num_bins; ++b) + { + Bin &bin = mBins[b]; + bin.mBounds.SetEmpty(); + bin.mMinCentroid = bounds_min + bounds_size * (b + 1) / num_bins; + bin.mNumTriangles = 0; + } + + // Bin all triangles + for (uint t = inTriangles.mBegin; t < inTriangles.mEnd; ++t) + { + float centroid_pos = mCentroids[mSortedTriangleIdx[t]][dim]; + + // Select bin + uint bin_no = min(uint((centroid_pos - bounds_min) / bounds_size * num_bins), num_bins - 1); + Bin &bin = mBins[bin_no]; + + // Accumulate triangle in bin + bin.mBounds.Encapsulate(mVertices, GetTriangle(t)); + bin.mMinCentroid = min(bin.mMinCentroid, centroid_pos); + bin.mNumTriangles++; + } + + // Calculate totals left to right + AABox prev_bounds; + int prev_triangles = 0; + for (uint b = 0; b < num_bins; ++b) + { + Bin &bin = mBins[b]; + bin.mBoundsAccumulatedLeft = prev_bounds; // Don't include this node as we'll take a split on the left side of the bin + bin.mNumTrianglesAccumulatedLeft = prev_triangles; + prev_bounds.Encapsulate(bin.mBounds); + prev_triangles += bin.mNumTriangles; + } + + // Calculate totals right to left + prev_bounds.SetEmpty(); + prev_triangles = 0; + for (int b = num_bins - 1; b >= 0; --b) + { + Bin &bin = mBins[b]; + prev_bounds.Encapsulate(bin.mBounds); + prev_triangles += bin.mNumTriangles; + bin.mBoundsAccumulatedRight = prev_bounds; + bin.mNumTrianglesAccumulatedRight = prev_triangles; + } + + // Get best splitting plane + for (uint b = 1; b < num_bins; ++b) // Start at 1 since selecting bin 0 would result in everything ending up on the right side + { + // Calculate surface area heuristic and see if it is better than the current best + const Bin &bin = mBins[b]; + float cp = bin.mBoundsAccumulatedLeft.GetSurfaceArea() * bin.mNumTrianglesAccumulatedLeft + bin.mBoundsAccumulatedRight.GetSurfaceArea() * bin.mNumTrianglesAccumulatedRight; + if (cp < best_cp) + { + best_cp = cp; + best_dim = dim; + best_split = bin.mMinCentroid; + } + } + } + + // No split found? + if (best_dim == 0xffffffff) + return false; + + return SplitInternal(inTriangles, best_dim, best_split, outLeft, outRight); +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterBinning.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterBinning.h new file mode 100644 index 00000000000..2cb35c9cc7b --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterBinning.h @@ -0,0 +1,52 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +JPH_NAMESPACE_BEGIN + +/// Binning splitter approach taken from: Realtime Ray Tracing on GPU with BVH-based Packet Traversal by Johannes Gunther et al. +class JPH_EXPORT TriangleSplitterBinning : public TriangleSplitter +{ +public: + /// Constructor + TriangleSplitterBinning(const VertexList &inVertices, const IndexedTriangleList &inTriangles, uint inMinNumBins = 8, uint inMaxNumBins = 128, uint inNumTrianglesPerBin = 6); + + // See TriangleSplitter::GetStats + virtual void GetStats(Stats &outStats) const override + { + outStats.mSplitterName = "TriangleSplitterBinning"; + } + + // See TriangleSplitter::Split + virtual bool Split(const Range &inTriangles, Range &outLeft, Range &outRight) override; + +private: + // Configuration + const uint mMinNumBins; + const uint mMaxNumBins; + const uint mNumTrianglesPerBin; + + struct Bin + { + // Properties of this bin + AABox mBounds; + float mMinCentroid; + uint mNumTriangles; + + // Accumulated data from left most / right most bin to current (including this bin) + AABox mBoundsAccumulatedLeft; + AABox mBoundsAccumulatedRight; + uint mNumTrianglesAccumulatedLeft; + uint mNumTrianglesAccumulatedRight; + }; + + // Scratch area to store the bins + Array mBins; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterFixedLeafSize.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterFixedLeafSize.cpp new file mode 100644 index 00000000000..2e8ae7371f8 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterFixedLeafSize.cpp @@ -0,0 +1,170 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include + +JPH_NAMESPACE_BEGIN + +TriangleSplitterFixedLeafSize::TriangleSplitterFixedLeafSize(const VertexList &inVertices, const IndexedTriangleList &inTriangles, uint inLeafSize, uint inMinNumBins, uint inMaxNumBins, uint inNumTrianglesPerBin) : + TriangleSplitter(inVertices, inTriangles), + mLeafSize(inLeafSize), + mMinNumBins(inMinNumBins), + mMaxNumBins(inMaxNumBins), + mNumTrianglesPerBin(inNumTrianglesPerBin) +{ + // Group the triangles + TriangleGrouperClosestCentroid grouper; + grouper.Group(inVertices, inTriangles, mLeafSize, mSortedTriangleIdx); + + // Pad triangles so that we have a multiple of mLeafSize + const uint num_triangles = (uint)inTriangles.size(); + const uint num_groups = (num_triangles + mLeafSize - 1) / mLeafSize; + const uint last_triangle_idx = mSortedTriangleIdx.back(); + for (uint t = num_triangles, t_end = num_groups * mLeafSize; t < t_end; ++t) + mSortedTriangleIdx.push_back(last_triangle_idx); +} + +Vec3 TriangleSplitterFixedLeafSize::GetCentroidForGroup(uint inFirstTriangleInGroup) +{ + JPH_ASSERT(inFirstTriangleInGroup % mLeafSize == 0); + AABox box; + for (uint g = 0; g < mLeafSize; ++g) + box.Encapsulate(mVertices, GetTriangle(inFirstTriangleInGroup + g)); + return box.GetCenter(); +} + +bool TriangleSplitterFixedLeafSize::Split(const Range &inTriangles, Range &outLeft, Range &outRight) +{ + // Cannot split anything smaller than leaf size + JPH_ASSERT(inTriangles.Count() > mLeafSize); + JPH_ASSERT(inTriangles.Count() % mLeafSize == 0); + + // Calculate bounds for this range + AABox centroid_bounds; + for (uint t = inTriangles.mBegin; t < inTriangles.mEnd; t += mLeafSize) + centroid_bounds.Encapsulate(GetCentroidForGroup(t)); + + float best_cp = FLT_MAX; + uint best_dim = 0xffffffff; + float best_split = 0; + + // Bin in all dimensions + uint num_bins = Clamp(inTriangles.Count() / mNumTrianglesPerBin, mMinNumBins, mMaxNumBins); + Array bins(num_bins); + for (uint dim = 0; dim < 3; ++dim) + { + float bounds_min = centroid_bounds.mMin[dim]; + float bounds_size = centroid_bounds.mMax[dim] - bounds_min; + + // Skip axis if too small + if (bounds_size < 1.0e-5f) + continue; + + // Initialize bins + for (uint b = 0; b < num_bins; ++b) + { + Bin &bin = bins[b]; + bin.mBounds.SetEmpty(); + bin.mMinCentroid = bounds_min + bounds_size * (b + 1) / num_bins; + bin.mNumTriangles = 0; + } + + // Bin all triangles + for (uint t = inTriangles.mBegin; t < inTriangles.mEnd; t += mLeafSize) + { + // Calculate average centroid for group + float centroid_pos = GetCentroidForGroup(t)[dim]; + + // Select bin + uint bin_no = min(uint((centroid_pos - bounds_min) / bounds_size * num_bins), num_bins - 1); + Bin &bin = bins[bin_no]; + + // Put all triangles of group in same bin + for (uint g = 0; g < mLeafSize; ++g) + bin.mBounds.Encapsulate(mVertices, GetTriangle(t + g)); + bin.mMinCentroid = min(bin.mMinCentroid, centroid_pos); + bin.mNumTriangles += mLeafSize; + } + + // Calculate totals left to right + AABox prev_bounds; + int prev_triangles = 0; + for (uint b = 0; b < num_bins; ++b) + { + Bin &bin = bins[b]; + bin.mBoundsAccumulatedLeft = prev_bounds; // Don't include this node as we'll take a split on the left side of the bin + bin.mNumTrianglesAccumulatedLeft = prev_triangles; + prev_bounds.Encapsulate(bin.mBounds); + prev_triangles += bin.mNumTriangles; + } + + // Calculate totals right to left + prev_bounds.SetEmpty(); + prev_triangles = 0; + for (int b = num_bins - 1; b >= 0; --b) + { + Bin &bin = bins[b]; + prev_bounds.Encapsulate(bin.mBounds); + prev_triangles += bin.mNumTriangles; + bin.mBoundsAccumulatedRight = prev_bounds; + bin.mNumTrianglesAccumulatedRight = prev_triangles; + } + + // Get best splitting plane + for (uint b = 1; b < num_bins; ++b) // Start at 1 since selecting bin 0 would result in everything ending up on the right side + { + // Calculate surface area heuristic and see if it is better than the current best + const Bin &bin = bins[b]; + float cp = bin.mBoundsAccumulatedLeft.GetSurfaceArea() * bin.mNumTrianglesAccumulatedLeft + bin.mBoundsAccumulatedRight.GetSurfaceArea() * bin.mNumTrianglesAccumulatedRight; + if (cp < best_cp) + { + best_cp = cp; + best_dim = dim; + best_split = bin.mMinCentroid; + } + } + } + + // No split found? + if (best_dim == 0xffffffff) + return false; + + // Divide triangles + uint start = inTriangles.mBegin, end = inTriangles.mEnd; + while (start < end) + { + // Search for first element that is on the right hand side of the split plane + while (start < end && GetCentroidForGroup(start)[best_dim] < best_split) + start += mLeafSize; + + // Search for the first element that is on the left hand side of the split plane + while (start < end && GetCentroidForGroup(end - mLeafSize)[best_dim] >= best_split) + end -= mLeafSize; + + if (start < end) + { + // Swap the two elements + for (uint g = 0; g < mLeafSize; ++g) + swap(mSortedTriangleIdx[start + g], mSortedTriangleIdx[end - mLeafSize + g]); + start += mLeafSize; + end -= mLeafSize; + } + } + JPH_ASSERT(start == end); + + // No suitable split found, doing random split in half + if (start == inTriangles.mBegin || start == inTriangles.mEnd) + start = inTriangles.mBegin + (inTriangles.Count() / mLeafSize + 1) / 2 * mLeafSize; + + outLeft = Range(inTriangles.mBegin, start); + outRight = Range(start, inTriangles.mEnd); + JPH_ASSERT(outLeft.mEnd > outLeft.mBegin && outRight.mEnd > outRight.mBegin); + JPH_ASSERT(outLeft.Count() % mLeafSize == 0 && outRight.Count() % mLeafSize == 0); + return true; +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterFixedLeafSize.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterFixedLeafSize.h new file mode 100644 index 00000000000..029121daea5 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterFixedLeafSize.h @@ -0,0 +1,55 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +JPH_NAMESPACE_BEGIN + +/// Same as TriangleSplitterBinning, but ensuring that leaves have a fixed amount of triangles +/// The resulting tree should be suitable for processing on GPU where we want all threads to process an equal amount of triangles +class JPH_EXPORT TriangleSplitterFixedLeafSize : public TriangleSplitter +{ +public: + /// Constructor + TriangleSplitterFixedLeafSize(const VertexList &inVertices, const IndexedTriangleList &inTriangles, uint inLeafSize, uint inMinNumBins = 8, uint inMaxNumBins = 128, uint inNumTrianglesPerBin = 6); + + // See TriangleSplitter::GetStats + virtual void GetStats(Stats &outStats) const override + { + outStats.mSplitterName = "TriangleSplitterFixedLeafSize"; + outStats.mLeafSize = mLeafSize; + } + + // See TriangleSplitter::Split + virtual bool Split(const Range &inTriangles, Range &outLeft, Range &outRight) override; + +private: + /// Get centroid for group + Vec3 GetCentroidForGroup(uint inFirstTriangleInGroup); + + // Configuration + const uint mLeafSize; + const uint mMinNumBins; + const uint mMaxNumBins; + const uint mNumTrianglesPerBin; + + struct Bin + { + // Properties of this bin + AABox mBounds; + float mMinCentroid; + uint mNumTriangles; + + // Accumulated data from left most / right most bin to current (including this bin) + AABox mBoundsAccumulatedLeft; + AABox mBoundsAccumulatedRight; + uint mNumTrianglesAccumulatedLeft; + uint mNumTrianglesAccumulatedRight; + }; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterLongestAxis.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterLongestAxis.cpp new file mode 100644 index 00000000000..f8115ab8999 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterLongestAxis.cpp @@ -0,0 +1,31 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include + +JPH_NAMESPACE_BEGIN + +TriangleSplitterLongestAxis::TriangleSplitterLongestAxis(const VertexList &inVertices, const IndexedTriangleList &inTriangles) : + TriangleSplitter(inVertices, inTriangles) +{ +} + +bool TriangleSplitterLongestAxis::Split(const Range &inTriangles, Range &outLeft, Range &outRight) +{ + // Calculate bounding box for triangles + AABox bounds; + for (uint t = inTriangles.mBegin; t < inTriangles.mEnd; ++t) + bounds.Encapsulate(mVertices, GetTriangle(t)); + + // Calculate split plane + uint dimension = bounds.GetExtent().GetHighestComponentIndex(); + float split = bounds.GetCenter()[dimension]; + + return SplitInternal(inTriangles, dimension, split, outLeft, outRight); +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterLongestAxis.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterLongestAxis.h new file mode 100644 index 00000000000..daf0d437019 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterLongestAxis.h @@ -0,0 +1,28 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +/// Splitter using center of bounding box with longest axis +class JPH_EXPORT TriangleSplitterLongestAxis : public TriangleSplitter +{ +public: + /// Constructor + TriangleSplitterLongestAxis(const VertexList &inVertices, const IndexedTriangleList &inTriangles); + + // See TriangleSplitter::GetStats + virtual void GetStats(Stats &outStats) const override + { + outStats.mSplitterName = "TriangleSplitterLongestAxis"; + } + + // See TriangleSplitter::Split + virtual bool Split(const Range &inTriangles, Range &outLeft, Range &outRight) override; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterMean.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterMean.cpp new file mode 100644 index 00000000000..e884246fea5 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterMean.cpp @@ -0,0 +1,40 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include + +JPH_NAMESPACE_BEGIN + +TriangleSplitterMean::TriangleSplitterMean(const VertexList &inVertices, const IndexedTriangleList &inTriangles) : + TriangleSplitter(inVertices, inTriangles) +{ +} + +bool TriangleSplitterMean::Split(const Range &inTriangles, Range &outLeft, Range &outRight) +{ + // Calculate mean value for these triangles + Vec3 mean = Vec3::sZero(); + for (uint t = inTriangles.mBegin; t < inTriangles.mEnd; ++t) + mean += Vec3(mCentroids[mSortedTriangleIdx[t]]); + mean *= 1.0f / inTriangles.Count(); + + // Calculate deviation + Vec3 deviation = Vec3::sZero(); + for (uint t = inTriangles.mBegin; t < inTriangles.mEnd; ++t) + { + Vec3 delta = Vec3(mCentroids[mSortedTriangleIdx[t]]) - mean; + deviation += delta * delta; + } + deviation *= 1.0f / inTriangles.Count(); + + // Calculate split plane + uint dimension = deviation.GetHighestComponentIndex(); + float split = mean[dimension]; + + return SplitInternal(inTriangles, dimension, split, outLeft, outRight); +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterMean.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterMean.h new file mode 100644 index 00000000000..737d76e1c1f --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterMean.h @@ -0,0 +1,28 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +/// Splitter using mean of axis with biggest centroid deviation +class JPH_EXPORT TriangleSplitterMean : public TriangleSplitter +{ +public: + /// Constructor + TriangleSplitterMean(const VertexList &inVertices, const IndexedTriangleList &inTriangles); + + // See TriangleSplitter::GetStats + virtual void GetStats(Stats &outStats) const override + { + outStats.mSplitterName = "TriangleSplitterMean"; + } + + // See TriangleSplitter::Split + virtual bool Split(const Range &inTriangles, Range &outLeft, Range &outRight) override; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterMorton.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterMorton.cpp new file mode 100644 index 00000000000..35b0f4212b7 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterMorton.cpp @@ -0,0 +1,63 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include + +JPH_NAMESPACE_BEGIN + +TriangleSplitterMorton::TriangleSplitterMorton(const VertexList &inVertices, const IndexedTriangleList &inTriangles) : + TriangleSplitter(inVertices, inTriangles) +{ + // Calculate bounds of centroids + AABox bounds; + for (uint t = 0; t < inTriangles.size(); ++t) + bounds.Encapsulate(Vec3(mCentroids[t])); + + // Make sure box is not degenerate + bounds.EnsureMinimalEdgeLength(1.0e-5f); + + // Calculate morton codes + mMortonCodes.resize(inTriangles.size()); + for (uint t = 0; t < inTriangles.size(); ++t) + mMortonCodes[t] = MortonCode::sGetMortonCode(Vec3(mCentroids[t]), bounds); + + // Sort triangles on morton code + const Array &morton_codes = mMortonCodes; + QuickSort(mSortedTriangleIdx.begin(), mSortedTriangleIdx.end(), [&morton_codes](uint inLHS, uint inRHS) { return morton_codes[inLHS] < morton_codes[inRHS]; }); +} + +bool TriangleSplitterMorton::Split(const Range &inTriangles, Range &outLeft, Range &outRight) +{ + uint32 first_code = mMortonCodes[mSortedTriangleIdx[inTriangles.mBegin]]; + uint32 last_code = mMortonCodes[mSortedTriangleIdx[inTriangles.mEnd - 1]]; + + uint common_prefix = CountLeadingZeros(first_code ^ last_code); + + // Use binary search to find where the next bit differs + uint split = inTriangles.mBegin; // Initial guess + uint step = inTriangles.Count(); + do + { + step = (step + 1) >> 1; // Exponential decrease + uint new_split = split + step; // Proposed new position + if (new_split < inTriangles.mEnd) + { + uint32 split_code = mMortonCodes[mSortedTriangleIdx[new_split]]; + uint split_prefix = CountLeadingZeros(first_code ^ split_code); + if (split_prefix > common_prefix) + split = new_split; // Accept proposal + } + } + while (step > 1); + + outLeft = Range(inTriangles.mBegin, split + 1); + outRight = Range(split + 1, inTriangles.mEnd); + return outLeft.Count() > 0 && outRight.Count() > 0; +} + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterMorton.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterMorton.h new file mode 100644 index 00000000000..2f48c0ea96c --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterMorton.h @@ -0,0 +1,32 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +JPH_NAMESPACE_BEGIN + +/// Splitter using Morton codes, see: http://devblogs.nvidia.com/parallelforall/thinking-parallel-part-iii-tree-construction-gpu/ +class JPH_EXPORT TriangleSplitterMorton : public TriangleSplitter +{ +public: + /// Constructor + TriangleSplitterMorton(const VertexList &inVertices, const IndexedTriangleList &inTriangles); + + // See TriangleSplitter::GetStats + virtual void GetStats(Stats &outStats) const override + { + outStats.mSplitterName = "TriangleSplitterMorton"; + } + + // See TriangleSplitter::Split + virtual bool Split(const Range &inTriangles, Range &outLeft, Range &outRight) override; + +private: + // Precalculated Morton codes + Array mMortonCodes; +}; + +JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/JoltViewer/JoltViewer.cmake b/crates/joltc-sys-patched/JoltC/JoltPhysics/JoltViewer/JoltViewer.cmake new file mode 100644 index 00000000000..8ba517a47f4 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/JoltViewer/JoltViewer.cmake @@ -0,0 +1,20 @@ +# Root +set(JOLT_VIEWER_ROOT ${PHYSICS_REPO_ROOT}/JoltViewer) + +# Source files +set(JOLT_VIEWER_SRC_FILES + ${JOLT_VIEWER_ROOT}/JoltViewer.cmake + ${JOLT_VIEWER_ROOT}/JoltViewer.cpp + ${JOLT_VIEWER_ROOT}/JoltViewer.h +) + +# Group source files +source_group(TREE ${JOLT_VIEWER_ROOT} FILES ${JOLT_VIEWER_SRC_FILES}) + +# Create JoltViewer executable +add_executable(JoltViewer ${JOLT_VIEWER_SRC_FILES}) +target_include_directories(JoltViewer PUBLIC ${JOLT_VIEWER_ROOT}) +target_link_libraries(JoltViewer LINK_PUBLIC TestFramework d3d12.lib shcore.lib) + +# Set the correct working directory +set_property(TARGET JoltViewer PROPERTY VS_DEBUGGER_WORKING_DIRECTORY "${PHYSICS_REPO_ROOT}") diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/JoltViewer/JoltViewer.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/JoltViewer/JoltViewer.cpp new file mode 100644 index 00000000000..b60bac7be5a --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/JoltViewer/JoltViewer.cpp @@ -0,0 +1,154 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#include +#include +#include + +JPH_SUPPRESS_WARNINGS_STD_BEGIN +#include +JPH_SUPPRESS_WARNINGS_STD_END + +#ifndef JPH_DEBUG_RENDERER + // Hack to still compile DebugRenderer inside the test framework when Jolt is compiled without + #define JPH_DEBUG_RENDERER + #include + #include + #undef JPH_DEBUG_RENDERER +#endif + +JoltViewer::JoltViewer() +{ + // Get file name from commandline + String cmd_line = GetCommandLineA(); + Array args; + StringToVector(cmd_line, args, " "); + + // Check arguments + if (args.size() != 2 || args[1].empty()) + { + MessageBoxA(nullptr, "Usage: JoltViewer ", "Error", MB_OK); + return; + } + + // Open file + ifstream stream(args[1].c_str(), ifstream::in | ifstream::binary); + if (!stream.is_open()) + { + MessageBoxA(nullptr, "Could not open recording file", "Error", MB_OK); + return; + } + + // Parse the stream + StreamInWrapper wrapper(stream); + mRendererPlayback.Parse(wrapper); + if (mRendererPlayback.GetNumFrames() == 0) + { + MessageBoxA(nullptr, "Recording file did not contain any frames", "Error", MB_OK); + return; + } + + // Draw the first frame + mRendererPlayback.DrawFrame(0); + + // Start paused + Pause(true); + + // Create UI + UIElement *main_menu = mDebugUI->CreateMenu(); + mDebugUI->CreateTextButton(main_menu, "Help", [this](){ + UIElement *help = mDebugUI->CreateMenu(); + mDebugUI->CreateStaticText(help, + "ESC: Back to previous menu.\n" + "WASD + Mouse: Fly around. Hold Shift to speed up, Ctrl to slow down.\n" + "P: Pause / unpause simulation.\n" + "O: Single step simulation.\n" + ",: Step back.\n" + ".: Step forward.\n" + "Shift + ,: Play reverse.\n" + "Shift + .: Replay forward." + ); + mDebugUI->ShowMenu(help); + }); + mDebugUI->ShowMenu(main_menu); +} + +bool JoltViewer::UpdateFrame(float inDeltaTime) +{ + // If no frames were read, abort + if (mRendererPlayback.GetNumFrames() == 0) + return false; + + // Handle keyboard input + bool shift = mKeyboard->IsKeyPressed(DIK_LSHIFT) || mKeyboard->IsKeyPressed(DIK_RSHIFT); + for (int key = mKeyboard->GetFirstKey(); key != 0; key = mKeyboard->GetNextKey()) + switch (key) + { + case DIK_R: + // Restart + mCurrentFrame = 0; + mPlaybackMode = EPlaybackMode::Play; + Pause(true); + break; + + case DIK_O: + // Step + mPlaybackMode = EPlaybackMode::Play; + SingleStep(); + break; + + case DIK_COMMA: + // Back + mPlaybackMode = shift? EPlaybackMode::Rewind : EPlaybackMode::StepBack; + Pause(false); + break; + + case DIK_PERIOD: + // Forward + mPlaybackMode = shift? EPlaybackMode::Play : EPlaybackMode::StepForward; + Pause(false); + break; + } + + // If paused, do nothing + if (inDeltaTime > 0.0f) + { + // Determine new frame number + switch (mPlaybackMode) + { + case EPlaybackMode::StepForward: + mPlaybackMode = EPlaybackMode::Stop; + [[fallthrough]]; + + case EPlaybackMode::Play: + if (mCurrentFrame + 1 < mRendererPlayback.GetNumFrames()) + ++mCurrentFrame; + break; + + case EPlaybackMode::StepBack: + mPlaybackMode = EPlaybackMode::Stop; + [[fallthrough]]; + + case EPlaybackMode::Rewind: + if (mCurrentFrame > 0) + --mCurrentFrame; + break; + + case EPlaybackMode::Stop: + break; + } + + // Render the frame + mRendererPlayback.DrawFrame(mCurrentFrame); + } + + return true; +} + +ENTRY_POINT(JoltViewer, RegisterDefaultAllocator) diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/JoltViewer/JoltViewer.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/JoltViewer/JoltViewer.h new file mode 100644 index 00000000000..87f8a87e732 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/JoltViewer/JoltViewer.h @@ -0,0 +1,46 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#ifdef JPH_DEBUG_RENDERER + #include +#else + // Hack to still compile DebugRenderer inside the test framework when Jolt is compiled without + #define JPH_DEBUG_RENDERER + // Make sure the debug renderer symbols don't get imported or exported + #define JPH_DEBUG_RENDERER_EXPORT + #include + #undef JPH_DEBUG_RENDERER + #undef JPH_DEBUG_RENDERER_EXPORT +#endif + +using namespace std; + +// Application that views recordings produced by DebugRendererRecorder +class JoltViewer : public Application +{ +public: + // Constructor / destructor + JoltViewer(); + + // Update the application + virtual bool UpdateFrame(float inDeltaTime) override; + +private: + enum class EPlaybackMode + { + Rewind, + StepBack, + Stop, + StepForward, + Play + }; + + DebugRendererPlayback mRendererPlayback { *mDebugRenderer }; + + EPlaybackMode mPlaybackMode = EPlaybackMode::Play; // Current playback state. Indicates if we're playing or scrubbing back/forward. + uint mCurrentFrame = 0; +}; diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/LICENSE b/crates/joltc-sys-patched/JoltC/JoltPhysics/LICENSE new file mode 100644 index 00000000000..4f097684852 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/LICENSE @@ -0,0 +1,7 @@ +Copyright 2021 Jorrit Rouwe + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/PerformanceTest/ConvexVsMeshScene.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/PerformanceTest/ConvexVsMeshScene.h new file mode 100644 index 00000000000..f4c0857b3df --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/PerformanceTest/ConvexVsMeshScene.h @@ -0,0 +1,122 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +// Jolt includes +#include +#include +#include +#include +#include +#include + +// Local includes +#include "PerformanceTestScene.h" +#include "Layers.h" + +// A scene that drops a number of convex shapes on a sloping terrain made out of a mesh shape +class ConvexVsMeshScene : public PerformanceTestScene +{ +public: + virtual const char * GetName() const override + { + return "ConvexVsMesh"; + } + + virtual bool Load() override + { + const int n = 100; + const float cell_size = 3.0f; + const float max_height = 5.0f; + float center = n * cell_size / 2; + + // Create vertices + const int num_vertices = (n + 1) * (n + 1); + VertexList vertices; + vertices.resize(num_vertices); + for (int x = 0; x <= n; ++x) + for (int z = 0; z <= n; ++z) + { + float height = Sin(float(x) * 50.0f / n) * Cos(float(z) * 50.0f / n); + vertices[z * (n + 1) + x] = Float3(cell_size * x, max_height * height, cell_size * z); + } + + // Create regular grid of triangles + const int num_triangles = n * n * 2; + IndexedTriangleList indices; + indices.resize(num_triangles); + IndexedTriangle *next = indices.data(); + for (int x = 0; x < n; ++x) + for (int z = 0; z < n; ++z) + { + int start = (n + 1) * z + x; + + next->mIdx[0] = start; + next->mIdx[1] = start + n + 1; + next->mIdx[2] = start + 1; + next++; + + next->mIdx[0] = start + 1; + next->mIdx[1] = start + n + 1; + next->mIdx[2] = start + n + 2; + next++; + } + + // Create mesh shape settings + Ref mesh_shape_settings = new MeshShapeSettings(vertices, indices); + mesh_shape_settings->mMaxTrianglesPerLeaf = 4; + + // Create mesh shape creation settings + mMeshSettings.mMotionType = EMotionType::Static; + mMeshSettings.mObjectLayer = Layers::NON_MOVING; + mMeshSettings.mPosition = RVec3(Real(-center), Real(max_height), Real(-center)); + mMeshSettings.mFriction = 0.5f; + mMeshSettings.mRestitution = 0.6f; + mMeshSettings.SetShapeSettings(mesh_shape_settings); + + // Create other shapes + mShapes = { + new BoxShape(Vec3(0.5f, 0.75f, 1.0f)), + new SphereShape(0.5f), + new CapsuleShape(0.75f, 0.5f), + ConvexHullShapeSettings({ Vec3(0, 1, 0), Vec3(1, 0, 0), Vec3(-1, 0, 0), Vec3(0, 0, 1), Vec3(0, 0, -1) }).Create().Get(), + }; + + return true; + } + + virtual void StartTest(PhysicsSystem &inPhysicsSystem, EMotionQuality inMotionQuality) override + { + // Reduce the solver iteration count, the scene doesn't have any constraints so we don't need the default amount of iterations + PhysicsSettings settings = inPhysicsSystem.GetPhysicsSettings(); + settings.mNumVelocitySteps = 4; + settings.mNumPositionSteps = 1; + inPhysicsSystem.SetPhysicsSettings(settings); + + // Create background + BodyInterface &bi = inPhysicsSystem.GetBodyInterface(); + bi.CreateAndAddBody(mMeshSettings, EActivation::DontActivate); + + // Construct bodies + for (int x = -10; x <= 10; ++x) + for (int y = 0; y < (int)mShapes.size(); ++y) + for (int z = -10; z <= 10; ++z) + { + BodyCreationSettings creation_settings; + creation_settings.mMotionType = EMotionType::Dynamic; + creation_settings.mMotionQuality = inMotionQuality; + creation_settings.mObjectLayer = Layers::MOVING; + creation_settings.mPosition = RVec3(7.5_r * x, 15.0_r + 2.0_r * y, 7.5_r * z); + creation_settings.mFriction = 0.5f; + creation_settings.mRestitution = 0.6f; + creation_settings.SetShape(mShapes[y]); + bi.CreateAndAddBody(creation_settings, EActivation::Activate); + } + } + +private: + BodyCreationSettings mMeshSettings; + Array> mShapes; +}; diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/PerformanceTest/Layers.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/PerformanceTest/Layers.h new file mode 100644 index 00000000000..68dfb8ada09 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/PerformanceTest/Layers.h @@ -0,0 +1,100 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +/// Layer that objects can be in, determines which other objects it can collide with +namespace Layers +{ + static constexpr ObjectLayer NON_MOVING = 0; + static constexpr ObjectLayer MOVING = 1; + static constexpr ObjectLayer NUM_LAYERS = 2; +}; + +/// Class that determines if two object layers can collide +class ObjectLayerPairFilterImpl : public ObjectLayerPairFilter +{ +public: + virtual bool ShouldCollide(ObjectLayer inObject1, ObjectLayer inObject2) const override + { + switch (inObject1) + { + case Layers::NON_MOVING: + return inObject2 == Layers::MOVING; // Non moving only collides with moving + case Layers::MOVING: + return true; // Moving collides with everything + default: + JPH_ASSERT(false); + return false; + } + } +}; + +/// Broadphase layers +namespace BroadPhaseLayers +{ + static constexpr BroadPhaseLayer NON_MOVING(0); + static constexpr BroadPhaseLayer MOVING(1); + static constexpr uint NUM_LAYERS(2); +}; + +/// BroadPhaseLayerInterface implementation +class BPLayerInterfaceImpl final : public BroadPhaseLayerInterface +{ +public: + BPLayerInterfaceImpl() + { + // Create a mapping table from object to broad phase layer + mObjectToBroadPhase[Layers::NON_MOVING] = BroadPhaseLayers::NON_MOVING; + mObjectToBroadPhase[Layers::MOVING] = BroadPhaseLayers::MOVING; + } + + virtual uint GetNumBroadPhaseLayers() const override + { + return BroadPhaseLayers::NUM_LAYERS; + } + + virtual BroadPhaseLayer GetBroadPhaseLayer(ObjectLayer inLayer) const override + { + JPH_ASSERT(inLayer < Layers::NUM_LAYERS); + return mObjectToBroadPhase[inLayer]; + } + +#if defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED) + virtual const char * GetBroadPhaseLayerName(BroadPhaseLayer inLayer) const override + { + switch ((BroadPhaseLayer::Type)inLayer) + { + case (BroadPhaseLayer::Type)BroadPhaseLayers::NON_MOVING: return "NON_MOVING"; + case (BroadPhaseLayer::Type)BroadPhaseLayers::MOVING: return "MOVING"; + default: JPH_ASSERT(false); return "INVALID"; + } + } +#endif // JPH_EXTERNAL_PROFILE || JPH_PROFILE_ENABLED + +private: + BroadPhaseLayer mObjectToBroadPhase[Layers::NUM_LAYERS]; +}; + +/// Class that determines if an object layer can collide with a broadphase layer +class ObjectVsBroadPhaseLayerFilterImpl : public ObjectVsBroadPhaseLayerFilter +{ +public: + virtual bool ShouldCollide(ObjectLayer inLayer1, BroadPhaseLayer inLayer2) const override + { + switch (inLayer1) + { + case Layers::NON_MOVING: + return inLayer2 == BroadPhaseLayers::MOVING; + case Layers::MOVING: + return true; + default: + JPH_ASSERT(false); + return false; + } + } +}; diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/PerformanceTest/PerformanceTest.cmake b/crates/joltc-sys-patched/JoltC/JoltPhysics/PerformanceTest/PerformanceTest.cmake new file mode 100644 index 00000000000..29cec49f9ee --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/PerformanceTest/PerformanceTest.cmake @@ -0,0 +1,16 @@ +# Root +set(PERFORMANCE_TEST_ROOT ${PHYSICS_REPO_ROOT}/PerformanceTest) + +# Source files +set(PERFORMANCE_TEST_SRC_FILES + ${PERFORMANCE_TEST_ROOT}/PyramidScene.h + ${PERFORMANCE_TEST_ROOT}/PerformanceTest.cpp + ${PERFORMANCE_TEST_ROOT}/PerformanceTest.cmake + ${PERFORMANCE_TEST_ROOT}/PerformanceTestScene.h + ${PERFORMANCE_TEST_ROOT}/RagdollScene.h + ${PERFORMANCE_TEST_ROOT}/ConvexVsMeshScene.h + ${PERFORMANCE_TEST_ROOT}/Layers.h +) + +# Group source files +source_group(TREE ${PERFORMANCE_TEST_ROOT} FILES ${PERFORMANCE_TEST_SRC_FILES}) diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/PerformanceTest/PerformanceTest.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/PerformanceTest/PerformanceTest.cpp new file mode 100644 index 00000000000..de15ca25bea --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/PerformanceTest/PerformanceTest.cpp @@ -0,0 +1,464 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +// Jolt includes +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef JPH_DEBUG_RENDERER + #include + #include +#endif // JPH_DEBUG_RENDERER +#ifdef JPH_PLATFORM_ANDROID +#include +#include +#endif // JPH_PLATFORM_ANDROID + +// STL includes +JPH_SUPPRESS_WARNINGS_STD_BEGIN +#include +#include +#include +#include +#include +JPH_SUPPRESS_WARNINGS_STD_END + +using namespace JPH; +using namespace JPH::literals; +using namespace std; + +// Disable common warnings triggered by Jolt +JPH_SUPPRESS_WARNINGS + +// Local includes +#include "RagdollScene.h" +#include "ConvexVsMeshScene.h" +#include "PyramidScene.h" + +// Time step for physics +constexpr float cDeltaTime = 1.0f / 60.0f; + +static void TraceImpl(const char *inFMT, ...) +{ + // Format the message + va_list list; + va_start(list, inFMT); + char buffer[1024]; + vsnprintf(buffer, sizeof(buffer), inFMT, list); + va_end(list); + + // Print to the TTY +#ifndef JPH_PLATFORM_ANDROID + cout << buffer << endl; +#else + __android_log_write(ANDROID_LOG_INFO, "Jolt", buffer); +#endif +} + +// Program entry point +int main(int argc, char** argv) +{ + // Install callbacks + Trace = TraceImpl; + + // Register allocation hook + RegisterDefaultAllocator(); + + // Helper function that creates the default scene + auto create_ragdoll_scene = []{ return unique_ptr(new RagdollScene(4, 10, 0.6f)); }; + + // Parse command line parameters + int specified_quality = -1; + int specified_threads = -1; + uint max_iterations = 500; + bool disable_sleep = false; + bool enable_profiler = false; +#ifdef JPH_DEBUG_RENDERER + bool enable_debug_renderer = false; +#endif // JPH_DEBUG_RENDERER + bool enable_per_frame_recording = false; + bool record_state = false; + bool validate_state = false; + unique_ptr scene; + const char *validate_hash = nullptr; + int repeat = 1; + for (int argidx = 1; argidx < argc; ++argidx) + { + const char *arg = argv[argidx]; + + if (strncmp(arg, "-s=", 3) == 0) + { + // Parse scene + if (strcmp(arg + 3, "Ragdoll") == 0) + scene = create_ragdoll_scene(); + else if (strcmp(arg + 3, "RagdollSinglePile") == 0) + scene = unique_ptr(new RagdollScene(1, 160, 0.4f)); + else if (strcmp(arg + 3, "ConvexVsMesh") == 0) + scene = unique_ptr(new ConvexVsMeshScene); + else if (strcmp(arg + 3, "Pyramid") == 0) + scene = unique_ptr(new PyramidScene); + else + { + Trace("Invalid scene"); + return 1; + } + } + else if (strncmp(arg, "-i=", 3) == 0) + { + // Parse max iterations + max_iterations = (uint)atoi(arg + 3); + } + else if (strncmp(arg, "-q=", 3) == 0) + { + // Parse quality + if (strcmp(arg + 3, "Discrete") == 0) + specified_quality = 0; + else if (strcmp(arg + 3, "LinearCast") == 0) + specified_quality = 1; + else + { + Trace("Invalid quality"); + return 1; + } + } + else if (strncmp(arg, "-t=max", 6) == 0) + { + // Default to number of threads on the system + specified_threads = thread::hardware_concurrency(); + } + else if (strncmp(arg, "-t=", 3) == 0) + { + // Parse threads + specified_threads = atoi(arg + 3); + } + else if (strcmp(arg, "-no_sleep") == 0) + { + disable_sleep = true; + } + else if (strcmp(arg, "-p") == 0) + { + enable_profiler = true; + } + #ifdef JPH_DEBUG_RENDERER + else if (strcmp(arg, "-r") == 0) + { + enable_debug_renderer = true; + } + #endif // JPH_DEBUG_RENDERER + else if (strcmp(arg, "-f") == 0) + { + enable_per_frame_recording = true; + } + else if (strcmp(arg, "-rs") == 0) + { + record_state = true; + } + else if (strcmp(arg, "-vs") == 0) + { + validate_state = true; + } + else if (strncmp(arg, "-validate_hash=", 15) == 0) + { + validate_hash = arg + 15; + } + else if (strncmp(arg, "-repeat=", 8) == 0) + { + // Parse repeat count + repeat = atoi(arg + 8); + } + else if (strcmp(arg, "-h") == 0) + { + // Print usage + Trace("Usage:\n" + "-s=: Select scene (Ragdoll, RagdollSinglePile, ConvexVsMesh, Pyramid)\n" + "-i=: Number of physics steps to simulate (default 500)\n" + "-q=: Test only with specified quality (Discrete, LinearCast)\n" + "-t=: Test only with N threads (default is to iterate over 1 .. num hardware threads)\n" + "-t=max: Test with the number of threads available on the system\n" + "-p: Write out profiles\n" + "-r: Record debug renderer output for JoltViewer\n" + "-f: Record per frame timings\n" + "-no_sleep: Disable sleeping\n" + "-rs: Record state\n" + "-vs: Validate state\n" + "-validate_hash=: Validate hash (return 0 if successful, 1 if failed)\n" + "-repeat=: Repeat all tests times"); + return 0; + } + } + + // Create a factory + Factory::sInstance = new Factory(); + + // Register all Jolt physics types + RegisterTypes(); + + // Create temp allocator + TempAllocatorImpl temp_allocator(32 * 1024 * 1024); + + // Load the scene + if (scene == nullptr) + scene = create_ragdoll_scene(); + if (!scene->Load()) + return 1; + + // Show used instruction sets + Trace(GetConfigurationString()); + + // Output scene we're running + Trace("Running scene: %s", scene->GetName()); + + // Create mapping table from object layer to broadphase layer + BPLayerInterfaceImpl broad_phase_layer_interface; + + // Create class that filters object vs broadphase layers + ObjectVsBroadPhaseLayerFilterImpl object_vs_broadphase_layer_filter; + + // Create class that filters object vs object layers + ObjectLayerPairFilterImpl object_vs_object_layer_filter; + + // Start profiling this program + JPH_PROFILE_START("Main"); + + // Trace header + Trace("Motion Quality, Thread Count, Steps / Second, Hash"); + + // Repeat test + for (int r = 0; r < repeat; ++r) + { + // Iterate motion qualities + for (uint mq = 0; mq < 2; ++mq) + { + // Skip quality if another was specified + if (specified_quality != -1 && mq != (uint)specified_quality) + continue; + + // Determine motion quality + EMotionQuality motion_quality = mq == 0? EMotionQuality::Discrete : EMotionQuality::LinearCast; + String motion_quality_str = mq == 0? "Discrete" : "LinearCast"; + + // Determine which thread counts to test + Array thread_permutations; + if (specified_threads > 0) + thread_permutations.push_back((uint)specified_threads - 1); + else + for (uint num_threads = 0; num_threads < thread::hardware_concurrency(); ++num_threads) + thread_permutations.push_back(num_threads); + + // Test thread permutations + for (uint num_threads : thread_permutations) + { + // Create job system with desired number of threads + JobSystemThreadPool job_system(cMaxPhysicsJobs, cMaxPhysicsBarriers, num_threads); + + // Create physics system + PhysicsSystem physics_system; + physics_system.Init(10240, 0, 65536, 20480, broad_phase_layer_interface, object_vs_broadphase_layer_filter, object_vs_object_layer_filter); + + // Start test scene + scene->StartTest(physics_system, motion_quality); + + // Disable sleeping if requested + if (disable_sleep) + { + const BodyLockInterface &bli = physics_system.GetBodyLockInterfaceNoLock(); + BodyIDVector body_ids; + physics_system.GetBodies(body_ids); + for (BodyID id : body_ids) + { + BodyLockWrite lock(bli, id); + if (lock.Succeeded()) + { + Body &body = lock.GetBody(); + if (!body.IsStatic()) + body.SetAllowSleeping(false); + } + } + } + + // Optimize the broadphase to prevent an expensive first frame + physics_system.OptimizeBroadPhase(); + + // A tag used to identify the test + String tag = ToLower(motion_quality_str) + "_th" + ConvertToString(num_threads + 1); + + #ifdef JPH_DEBUG_RENDERER + // Open renderer output + ofstream renderer_file; + if (enable_debug_renderer) + renderer_file.open(("performance_test_" + tag + ".jor").c_str(), ofstream::out | ofstream::binary | ofstream::trunc); + StreamOutWrapper renderer_stream(renderer_file); + DebugRendererRecorder renderer(renderer_stream); + #endif // JPH_DEBUG_RENDERER + + // Open per frame timing output + ofstream per_frame_file; + if (enable_per_frame_recording) + { + per_frame_file.open(("per_frame_" + tag + ".csv").c_str(), ofstream::out | ofstream::trunc); + per_frame_file << "Frame, Time (ms)" << endl; + } + + ofstream record_state_file; + ifstream validate_state_file; + if (record_state) + record_state_file.open(("state_" + ToLower(motion_quality_str) + ".bin").c_str(), ofstream::out | ofstream::binary | ofstream::trunc); + else if (validate_state) + validate_state_file.open(("state_" + ToLower(motion_quality_str) + ".bin").c_str(), ifstream::in | ifstream::binary); + + chrono::nanoseconds total_duration(0); + + // Step the world for a fixed amount of iterations + for (uint iterations = 0; iterations < max_iterations; ++iterations) + { + JPH_PROFILE_NEXTFRAME(); + JPH_DET_LOG("Iteration: " << iterations); + + // Start measuring + chrono::high_resolution_clock::time_point clock_start = chrono::high_resolution_clock::now(); + + // Do a physics step + physics_system.Update(cDeltaTime, 1, &temp_allocator, &job_system); + + // Stop measuring + chrono::high_resolution_clock::time_point clock_end = chrono::high_resolution_clock::now(); + chrono::nanoseconds duration = chrono::duration_cast(clock_end - clock_start); + total_duration += duration; + + #ifdef JPH_DEBUG_RENDERER + if (enable_debug_renderer) + { + // Draw the state of the world + BodyManager::DrawSettings settings; + physics_system.DrawBodies(settings, &renderer); + + // Mark end of frame + renderer.EndFrame(); + } + #endif // JPH_DEBUG_RENDERER + + // Record time taken this iteration + if (enable_per_frame_recording) + per_frame_file << iterations << ", " << (1.0e-6 * duration.count()) << endl; + + // Dump profile information every 100 iterations + if (enable_profiler && iterations % 100 == 0) + { + JPH_PROFILE_DUMP(tag + "_it" + ConvertToString(iterations)); + } + + if (record_state) + { + // Record state + StateRecorderImpl recorder; + physics_system.SaveState(recorder); + + // Write to file + string data = recorder.GetData(); + size_t size = data.size(); + record_state_file.write((char *)&size, sizeof(size)); + record_state_file.write(data.data(), size); + } + else if (validate_state) + { + // Read state + size_t size = 0; + validate_state_file.read((char *)&size, sizeof(size)); + string data; + data.resize(size); + validate_state_file.read(data.data(), size); + + // Copy to validator + StateRecorderImpl validator; + validator.WriteBytes(data.data(), size); + + // Validate state + validator.SetValidating(true); + physics_system.RestoreState(validator); + } + + #ifdef JPH_ENABLE_DETERMINISM_LOG + const BodyLockInterface &bli = physics_system.GetBodyLockInterfaceNoLock(); + BodyIDVector body_ids; + physics_system.GetBodies(body_ids); + for (BodyID id : body_ids) + { + BodyLockRead lock(bli, id); + const Body &body = lock.GetBody(); + if (!body.IsStatic()) + JPH_DET_LOG(id << ": p: " << body.GetPosition() << " r: " << body.GetRotation() << " v: " << body.GetLinearVelocity() << " w: " << body.GetAngularVelocity()); + } + #endif // JPH_ENABLE_DETERMINISM_LOG + } + + // Calculate hash of all positions and rotations of the bodies + uint64 hash = HashBytes(nullptr, 0); // Ensure we start with the proper seed + BodyInterface &bi = physics_system.GetBodyInterfaceNoLock(); + BodyIDVector body_ids; + physics_system.GetBodies(body_ids); + for (BodyID id : body_ids) + { + RVec3 pos = bi.GetPosition(id); + hash = HashBytes(&pos, 3 * sizeof(Real), hash); + Quat rot = bi.GetRotation(id); + hash = HashBytes(&rot, sizeof(Quat), hash); + } + + // Convert hash to string + stringstream hash_stream; + hash_stream << "0x" << hex << hash << dec; + string hash_str = hash_stream.str(); + + // Stop test scene + scene->StopTest(physics_system); + + // Trace stat line + Trace("%s, %d, %f, %s", motion_quality_str.c_str(), num_threads + 1, double(max_iterations) / (1.0e-9 * total_duration.count()), hash_str.c_str()); + + // Check hash code + if (validate_hash != nullptr && hash_str != validate_hash) + { + Trace("Fail hash validation. Was: %s, expected: %s", hash_str.c_str(), validate_hash); + return 1; + } + } + } + } + +#ifdef JPH_TRACK_NARROWPHASE_STATS + NarrowPhaseStat::sReportStats(); +#endif // JPH_TRACK_NARROWPHASE_STATS + + // Unregisters all types with the factory and cleans up the default material + UnregisterTypes(); + + // Destroy the factory + delete Factory::sInstance; + Factory::sInstance = nullptr; + + // End profiling this program + JPH_PROFILE_END(); + + return 0; +} + +#ifdef JPH_PLATFORM_ANDROID + +// Main entry point for android +void android_main(struct android_app *ioApp) +{ + // Run the regular main function + const char *args[] = { "Unused", "-s=ConvexVsMesh", "-t=max" }; + main(size(args), (char **)args); +} + +#endif // JPH_PLATFORM_ANDROID diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/PerformanceTest/PerformanceTestScene.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/PerformanceTest/PerformanceTestScene.h new file mode 100644 index 00000000000..b912a4927b9 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/PerformanceTest/PerformanceTestScene.h @@ -0,0 +1,25 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +// Base class for a test scene to test performance +class PerformanceTestScene +{ +public: + // Virtual destructor + virtual ~PerformanceTestScene() { } + + // Get name of test for debug purposes + virtual const char * GetName() const = 0; + + // Load assets for the scene + virtual bool Load() { return true; } + + // Start a new test by adding objects to inPhysicsSystem + virtual void StartTest(PhysicsSystem &inPhysicsSystem, EMotionQuality inMotionQuality) = 0; + + // Stop a test and remove objects from inPhysicsSystem + virtual void StopTest(PhysicsSystem &inPhysicsSystem) { } +}; diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/PerformanceTest/PyramidScene.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/PerformanceTest/PyramidScene.h new file mode 100644 index 00000000000..469b902031f --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/PerformanceTest/PyramidScene.h @@ -0,0 +1,48 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2023 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +// Jolt includes +#include + +// Local includes +#include "PerformanceTestScene.h" +#include "Layers.h" + +// A scene that creates a pyramid of boxes to create a very large island +class PyramidScene : public PerformanceTestScene +{ +public: + virtual const char * GetName() const override + { + return "Pyramid"; + } + + virtual void StartTest(PhysicsSystem &inPhysicsSystem, EMotionQuality inMotionQuality) override + { + BodyInterface &bi = inPhysicsSystem.GetBodyInterface(); + + // Floor + bi.CreateAndAddBody(BodyCreationSettings(new BoxShape(Vec3(50.0f, 1.0f, 50.0f), 0.0f), RVec3(Vec3(0.0f, -1.0f, 0.0f)), Quat::sIdentity(), EMotionType::Static, Layers::NON_MOVING), EActivation::DontActivate); + + const float cBoxSize = 2.0f; + const float cBoxSeparation = 0.5f; + const float cHalfBoxSize = 0.5f * cBoxSize; + const int cPyramidHeight = 15; + + RefConst box_shape = new BoxShape(Vec3::sReplicate(cHalfBoxSize), 0.0f); // No convex radius to force more collisions + + // Pyramid + for (int i = 0; i < cPyramidHeight; ++i) + for (int j = i / 2; j < cPyramidHeight - (i + 1) / 2; ++j) + for (int k = i / 2; k < cPyramidHeight - (i + 1) / 2; ++k) + { + RVec3 position(Real(-cPyramidHeight + cBoxSize * j + (i & 1? cHalfBoxSize : 0.0f)), Real(1.0f + (cBoxSize + cBoxSeparation) * i), Real(-cPyramidHeight + cBoxSize * k + (i & 1? cHalfBoxSize : 0.0f))); + BodyCreationSettings settings(box_shape, position, Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING); + settings.mAllowSleeping = false; // No sleeping to force the large island to stay awake + bi.CreateAndAddBody(settings, EActivation::Activate); + } + } +}; diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/PerformanceTest/RagdollScene.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/PerformanceTest/RagdollScene.h new file mode 100644 index 00000000000..e728929bc4f --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/PerformanceTest/RagdollScene.h @@ -0,0 +1,140 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2021 Jorrit Rouwe +// SPDX-License-Identifier: MIT + +#pragma once + +// Jolt includes +#include +#include +#include +#include +#include + +// Local includes +#include "PerformanceTestScene.h" +#include "Layers.h" + +// A scene that loads a part of a Horizon Zero Dawn level and drops many ragdolls on the terrain (motors enabled) +class RagdollScene : public PerformanceTestScene +{ +public: + RagdollScene(int inNumPilesPerAxis, int inPileSize, float inVerticalSeparation) : mNumPilesPerAxis(inNumPilesPerAxis), mPileSize(inPileSize), mVerticalSeparation(inVerticalSeparation) { } + + virtual const char * GetName() const override + { + return mNumPilesPerAxis == 1? "RagdollSinglePile" : "Ragdoll"; + } + + virtual bool Load() override + { + // Load ragdoll + if (!ObjectStreamIn::sReadObject("Assets/Human.tof", mRagdollSettings)) + { + cerr << "Unable to load ragdoll" << endl; + return false; + } + for (BodyCreationSettings &body : mRagdollSettings->mParts) + body.mObjectLayer = Layers::MOVING; + + // Init ragdoll + mRagdollSettings->GetSkeleton()->CalculateParentJointIndices(); + mRagdollSettings->Stabilize(); + mRagdollSettings->CalculateBodyIndexToConstraintIndex(); + mRagdollSettings->CalculateConstraintIndexToBodyIdxPair(); + + // Load animation + if (!ObjectStreamIn::sReadObject("Assets/Human/dead_pose1.tof", mAnimation)) + { + cerr << "Unable to load animation" << endl; + return false; + } + + // Sample pose + mPose.SetSkeleton(mRagdollSettings->GetSkeleton()); + mAnimation->Sample(0.0f, mPose); + + // Read the background scene + if (!ObjectStreamIn::sReadObject("Assets/terrain2.bof", mBackground)) + { + cerr << "Unable to load terrain" << endl; + return false; + } + for (BodyCreationSettings &body : mBackground->GetBodies()) + body.mObjectLayer = Layers::NON_MOVING; + mBackground->FixInvalidScales(); + + return true; + } + + virtual void StartTest(PhysicsSystem &inPhysicsSystem, EMotionQuality inMotionQuality) override + { + // Test configuration + const Real cHorizontalSeparation = 4.0_r; + + // Set motion quality on ragdoll + for (BodyCreationSettings &body : mRagdollSettings->mParts) + body.mMotionQuality = inMotionQuality; + + // Add background geometry + mBackground->CreateBodies(&inPhysicsSystem); + + // Create ragdoll piles + mt19937 random; + uniform_real_distribution angle(0.0f, JPH_PI); + CollisionGroup::GroupID group_id = 1; + for (int row = 0; row < mNumPilesPerAxis; ++row) + for (int col = 0; col < mNumPilesPerAxis; ++col) + { + // Determine start location of ray + RVec3 start(cHorizontalSeparation * (col - (mNumPilesPerAxis - 1) / 2.0_r), 100, cHorizontalSeparation * (row - (mNumPilesPerAxis - 1) / 2.0_r)); + + // Cast ray down to terrain + RayCastResult hit; + Vec3 ray_direction(0, -200, 0); + RRayCast ray { start, ray_direction }; + if (inPhysicsSystem.GetNarrowPhaseQuery().CastRay(ray, hit, SpecifiedBroadPhaseLayerFilter(BroadPhaseLayers::NON_MOVING), SpecifiedObjectLayerFilter(Layers::NON_MOVING))) + start = ray.GetPointOnRay(hit.mFraction); + + for (int i = 0; i < mPileSize; ++i) + { + // Create ragdoll + Ref ragdoll = mRagdollSettings->CreateRagdoll(group_id++, 0, &inPhysicsSystem); + + // Override root + SkeletonPose pose_copy = mPose; + pose_copy.SetRootOffset(start); + SkeletonPose::JointState &root = pose_copy.GetJoint(0); + root.mTranslation = Vec3(0, mVerticalSeparation * (i + 1), 0); + root.mRotation = Quat::sRotation(Vec3::sAxisY(), angle(random)) * root.mRotation; + pose_copy.CalculateJointMatrices(); + + // Drive to pose + ragdoll->SetPose(pose_copy); + ragdoll->DriveToPoseUsingMotors(pose_copy); + ragdoll->AddToPhysicsSystem(EActivation::Activate); + + // Keep reference + mRagdolls.push_back(ragdoll); + } + } + } + + virtual void StopTest(PhysicsSystem &inPhysicsSystem) override + { + // Remove ragdolls + for (Ragdoll *ragdoll : mRagdolls) + ragdoll->RemoveFromPhysicsSystem(); + mRagdolls.clear(); + } + +private: + int mNumPilesPerAxis; + int mPileSize; + float mVerticalSeparation; + Ref mRagdollSettings; + Ref mAnimation; + SkeletonPose mPose; + Ref mBackground; + Array> mRagdolls; +}; diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/README.md b/crates/joltc-sys-patched/JoltC/JoltPhysics/README.md new file mode 100644 index 00000000000..f4d46900886 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/README.md @@ -0,0 +1,159 @@ +[![CLA assistant](https://cla-assistant.io/readme/badge/jrouwe/JoltPhysics)](https://cla-assistant.io/jrouwe/JoltPhysics) +[![Build Status](https://github.com/jrouwe/JoltPhysics/actions/workflows/build.yml/badge.svg)](https://github.com/jrouwe/JoltPhysics/actions/) +[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=jrouwe_JoltPhysics&metric=alert_status)](https://sonarcloud.io/dashboard?id=jrouwe_JoltPhysics) +[![Bugs](https://sonarcloud.io/api/project_badges/measure?project=jrouwe_JoltPhysics&metric=bugs)](https://sonarcloud.io/dashboard?id=jrouwe_JoltPhysics) +[![Code Smells](https://sonarcloud.io/api/project_badges/measure?project=jrouwe_JoltPhysics&metric=code_smells)](https://sonarcloud.io/dashboard?id=jrouwe_JoltPhysics) + +# Jolt Physics + +A multi core friendly rigid body physics and collision detection library suitable for games and VR applications, used by Horizon Forbidden West. + +[![Horizon Forbidden West Cover Art](https://jrouwe.nl/jolt/Horizon_Forbidden_West.png)](https://www.playstation.com/en-us/games/horizon-forbidden-west/) + +|[![Ragdoll Pile](https://img.youtube.com/vi/pwyCW0yNKMA/hqdefault.jpg)](https://www.youtube.com/watch?v=pwyCW0yNKMA)| +|:-| +|*A YouTube video showing a ragdoll pile simulated with Jolt Physics.*| + +For more demos and [videos](https://www.youtube.com/watch?v=pwyCW0yNKMA&list=PLYXVwtOr1CBxbA50jVg2dKUQvHW_5OOom) go to the [Samples](Docs/Samples.md) section. + +## Design Considerations + +So why create yet another physics engine? First of all, this has been a personal learning project and secondly I wanted to address some issues that I had with existing physics engines: + +* In games we usually need to do many more things than to simulate the physics world and we need to do this across multiple threads. We therefore place a lot of emphasis on concurrently accessing the physics simulation data outside of the main physics simulation update: + * Sections of the world can be loaded / unloaded in the background. A batch of physics bodies can be prepared on a background thread without locking or affecting the physics simulation and then inserted into the world all at once with a minimal impact on performance. + * Collision queries can run in parallel with other operations like insertion / removal of bodies. The query code is guaranteed to see a body in a consistent state, but when a body is changed during a collision query there is no guarantee if the change is visible to the query or not. If a thread modifies the position of a body and then does a collision query, it will immediately see the updated state (this is often a problem when working with a read version and a write version of the world). + * It is also possible to run collision queries in parallel to the main physics simulation by doing the broad phase query before the simulation step. This way, long running processes (like navigation mesh generation) can be spread out across multiple frames while still running the physics simulation every frame. +* One of the main sources of performance problems we found was waking up too many bodies while loading / unloading content. Therefore, bodies will not automatically wake up when created and neighboring bodies will not be woken up when bodies are removed. This can be triggered manually if desired. +* The simulation runs deterministically, so you could replicate a simulation to a remote client by merely replicating the inputs to the simulation. Read the [Deterministic Simulation](https://jrouwe.github.io/JoltPhysics/#deterministic-simulation) section to understand the limits of this. +* The simulation of this physics engine tries to simulate behavior of rigid bodies in the real world but makes approximations in the simulation so should mainly be used for games or VR simulations. + +## Features + +* Simulation of rigid bodies of various shapes using continuous collision detection: + * Sphere. + * Box. + * Capsule. + * Tapered-capsule. + * Cylinder. + * Convex hull. + * Compound. + * Mesh (triangle). + * Terrain (height field). +* Simulation of constraints between bodies: + * Fixed. + * Point. + * Distance (including springs). + * Hinge. + * Slider (also called prismatic). + * Cone. + * Rack and Pinion. + * Gear. + * Pulley. + * Smooth spline paths. + * Swing-twist (for humanoid shoulders). + * 6 DOF. +* Motors to drive the constraints. +* Collision detection: + * Casting rays. + * Testing shapes vs shapes. + * Casting a shape vs another shape. + * Broadphase only tests for quickly determining which objects may intersect. +* Sensors (trigger volumes). +* Animated ragdolls: + * Hard keying (kinematic only rigid bodies). + * Soft keying (setting velocities on dynamic rigid bodies). + * Driving constraint motors to an animated pose. + * Mapping a high detail (animation) skeleton onto a low detail (ragdoll) skeleton and vice versa. +* Game character simulation (capsule) + * Rigid body character. Moves during the physics simulation. Cheapest option and most accurate collision response between character and dynamic bodies. + * Virtual character. Does not have a rigid body in the world but simulates one using collision checks. Updated outside of the physics update for more control. Less accurate interaction with dynamic bodies. +* Vehicles + * Wheeled vehicles. + * Tracked vehicles. + * Motorcycles. +* Soft body simulation (e.g. a soft ball or piece of cloth). + * Edge constraints. + * Dihedral bend constraints. + * Tetrahedron volume constraints. + * Long range attachment constraints (also called tethers). + * Limiting the simulation to stay within a certain range of a skinned vertex. + * Internal pressure. + * Collision with simulated rigid bodies. + * Collision tests against soft bodies. +* Water buoyancy calculations. +* An optional double precision mode that allows large worlds. + +## Supported Platforms + +* Windows (VS2019, VS2022) x86/x64/ARM32/ARM64 (Desktop/UWP) +* Linux (tested on Ubuntu 22.04) x64/ARM64 +* FreeBSD +* Android (tested on Android 14) x86/x64/ARM32/ARM64 +* Platform Blue (a popular game console) x64 +* macOS (tested on Monterey) x64/ARM64 +* iOS (tested on iOS 15) x64/ARM64 +* WebAssembly, see [this](https://github.com/jrouwe/JoltPhysics.js) separate project. + +## Required CPU features + +* On x86 the minimal requirements are SSE2 but the library can be compiled using SSE4.1, SSE4.2, AVX, AVX2, or AVX512. +* On ARM64 the library by default compiles with NEON and FP16, on ARM32 it can be compiled without any special CPU instructions. + +## Documentation + +To learn more about Jolt go to the [Architecture and API documentation](https://jrouwe.github.io/JoltPhysics/). + +To get started, look at the [HelloWorld](HelloWorld/HelloWorld.cpp) example. A [HelloWorld example using CMake FetchContent](https://github.com/jrouwe/JoltPhysicsHelloWorld) is also available to show how you can integrate Jolt Physics in a CMake project. + +Some algorithms used by Jolt are described in detail in my GDC 2022 talk Architecting Jolt Physics for 'Horizon Forbidden West' ([slides](https://gdcvault.com/play/1027560/Architecting-Jolt-Physics-for-Horizon), [slides with speaker notes](https://jrouwe.nl/architectingjolt/ArchitectingJoltPhysics_Rouwe_Jorrit_Notes.pdf), [video](https://gdcvault.com/play/1027891/Architecting-Jolt-Physics-for-Horizon)). + +## Compiling + +* The library has been tested to compile with Cl (Visual Studio 2019-2022), Clang 10+ and GCC 9+. +* It uses C++17 and only depends on the standard template library. +* It doesn't make use of compiler generated RTTI or exceptions. +* If you want to run on Platform Blue you'll need to provide your own build environment and PlatformBlue.h file due to NDA requirements (see Core.h for further info). + +For build instructions go to the [Build](Build/README.md) section. When upgrading from an older version of the library go to the [Release Notes](Docs/ReleaseNotes.md) or [API Changes](Docs/APIChanges.md) sections. + +## Performance + +If you're interested in how Jolt scales with multiple CPUs and compares to other physics engines, take a look at [this document](https://jrouwe.nl/jolt/JoltPhysicsMulticoreScaling.pdf). + +## Folder Structure + +* Assets - This folder contains assets used by the TestFramework, Samples and JoltViewer. +* Build - Contains everything needed to build the library, see the [Build](Build/README.md) section. +* Docs - Contains documentation for the library. +* HelloWorld - A simple application demonstrating how to use the Jolt Physics library. +* Jolt - All source code for the library is in this folder. +* JoltViewer - It is possible to record the output of the physics engine using the DebugRendererRecorder class (a .jor file), this folder contains the source code to an application that can visualize a recording. This is useful for e.g. visualizing the output of the PerformanceTest from different platforms. Currently available on Windows only. +* PerformanceTest - Contains a simple application that runs a [performance test](Docs/PerformanceTest.md) and collects timing information. +* Samples - This contains the sample application, see the [Samples](Docs/Samples.md) section. Currently available on Windows only. +* TestFramework - A rendering framework to visualize the results of the physics engine. Used by Samples and JoltViewer. Currently available on Windows only. +* UnitTests - A set of unit tests to validate the behavior of the physics engine. +* WebIncludes - A number of JavaScript resources used by the internal profiling framework of the physics engine. + +## Bindings For Other Languages + +* C [here](https://github.com/michal-z/zig-gamedev/tree/main/libs/zphysics/libs) and [here](https://github.com/amerkoleci/JoltPhysicsSharp/tree/main/src/joltc) +* [C#](https://github.com/amerkoleci/JoltPhysicsSharp) +* [Java](https://github.com/aecsocket/jolt-java) +* [JavaScript](https://github.com/jrouwe/JoltPhysics.js) +* [Zig](https://github.com/michal-z/zig-gamedev/tree/main/libs/zphysics) + +## Integrations in Other Engines + +* [Godot](https://github.com/godot-jolt/godot-jolt) +* [Source Engine](https://github.com/Joshua-Ashton/VPhysics-Jolt) + +See [a list of projects that use Jolt Physics here](Docs/ProjectsUsingJolt.md). + +## License + +The project is distributed under the [MIT license](LICENSE). + +## Contributions + +All contributions are welcome! If you intend to make larger changes, please discuss first in the GitHub Discussion section. For non-trivial changes, we require that you agree to a [Contributor Agreement](ContributorAgreement.md). When you create a PR, [CLA assistant](https://cla-assistant.io/) will prompt you to sign it. diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/WebIncludes/profile_chart.css b/crates/joltc-sys-patched/JoltC/JoltPhysics/WebIncludes/profile_chart.css new file mode 100644 index 00000000000..ae60f3b3b7c --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/WebIncludes/profile_chart.css @@ -0,0 +1,36 @@ +html, body { + padding: 0px; + border: 0px; + margin: 0px; + width: 100%; + height: 100%; + overflow: hidden; +} + +canvas { + position: absolute; + top: 10px; + left: 10px; + padding: 0px; + border: 0px; + margin: 0px; +} + +#tooltip { + font: Courier New; + position: absolute; + background-color: white; + border: 1px; + border-style: solid; + border-color: black; + pointer-events: none; + padding: 5px; + font: 14px Arial; + visibility: hidden; + height: auto; +} + +.stat { + color: blue; + text-align: right; +} diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/WebIncludes/profile_chart.js b/crates/joltc-sys-patched/JoltC/JoltPhysics/WebIncludes/profile_chart.js new file mode 100644 index 00000000000..fa4c1c4bff0 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/WebIncludes/profile_chart.js @@ -0,0 +1,285 @@ +var canvas; +var ctx; +var tooltip; +var min_scale; +var scale; +var offset_x = 0; +var offset_y = 0; +var size_y; +var dragging = false; +var previous_x = 0; +var previous_y = 0; +var bar_height = 15; +var line_height = bar_height + 2; +var thread_separation = 6; +var thread_font_size = 12; +var thread_font = thread_font_size + "px Arial"; +var bar_font_size = 10; +var bar_font = bar_font_size + "px Arial"; +var end_cycle = 0; + +function drawChart() +{ + ctx.clearRect(0, 0, canvas.width, canvas.height); + + ctx.lineWidth = 1; + + var y = offset_y; + + for (var t = 0; t < threads.length; t++) + { + // Check if thread has samples + var thread = threads[t]; + if (thread.start.length == 0) + continue; + + // Draw thread name + y += thread_font_size; + ctx.font = thread_font; + ctx.fillStyle = "#000000"; + ctx.fillText(thread.thread_name, 0, y); + y += thread_separation; + + // Draw outlines for each bar of samples + ctx.fillStyle = "#c0c0c0"; + for (var d = 0; d <= thread.max_depth; d++) + ctx.fillRect(0, y + d * line_height, canvas.width, bar_height); + + // Draw samples + ctx.font = bar_font; + for (var s = 0; s < thread.start.length; s++) + { + // Cull bar + var rx = scale * (offset_x + thread.start[s]); + if (rx > canvas.width) // right of canvas + break; + var rw = scale * thread.cycles[s]; + if (rw < 0.5) // less than half pixel, skip + continue; + if (rx + rw < 0) // left of canvas + continue; + + // Draw bar + var ry = y + line_height * thread.depth[s]; + ctx.fillStyle = thread.color[s]; + ctx.fillRect(rx, ry, rw, bar_height); + ctx.strokeStyle = thread.darkened_color[s]; + ctx.strokeRect(rx, ry, rw, bar_height); + + // Get index in aggregated list + var a = thread.aggregator[s]; + + // Draw text + if (rw > aggregated.name_width[a]) + { + ctx.fillStyle = "#000000"; + ctx.fillText(aggregated.name[a], rx + (rw - aggregated.name_width[a]) / 2, ry + bar_height - 4); + } + } + + // Next line + y += line_height * (1 + thread.max_depth) + thread_separation; + } + + // Update size + size_y = y - offset_y; +} + +function drawTooltip(mouse_x, mouse_y) +{ + var y = offset_y; + + for (var t = 0; t < threads.length; t++) + { + // Check if thread has samples + var thread = threads[t]; + if (thread.start.length == 0) + continue; + + // Thead name + y += thread_font_size + thread_separation; + + // Draw samples + for (var s = 0; s < thread.start.length; s++) + { + // Cull bar + var rx = scale * (offset_x + thread.start[s]); + if (rx > mouse_x) + break; + var rw = scale * thread.cycles[s]; + if (rx + rw < mouse_x) + continue; + + var ry = y + line_height * thread.depth[s]; + if (mouse_y >= ry && mouse_y < ry + bar_height) + { + // Get index into aggregated list + var a = thread.aggregator[s]; + + // Found bar, fill in tooltip + tooltip.style.left = (canvas.offsetLeft + mouse_x) + "px"; + tooltip.style.top = (canvas.offsetTop + mouse_y) + "px"; + tooltip.style.visibility = "visible"; + tooltip.innerHTML = aggregated.name[a] + "
        " + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "
        Time:" + (1000000 * thread.cycles[s] / cycles_per_second).toFixed(2) + " µs
        Start:" + (1000000 * thread.start[s] / cycles_per_second).toFixed(2) + " µs
        End:" + (1000000 * (thread.start[s] + thread.cycles[s]) / cycles_per_second).toFixed(2) + " µs
        Avg. Time:" + (1000000 * aggregated.cycles_per_frame[a] / cycles_per_second / aggregated.calls[a]).toFixed(2) + " µs
        Min Time:" + (1000000 * aggregated.min_cycles[a] / cycles_per_second).toFixed(2) + " µs
        Max Time:" + (1000000 * aggregated.max_cycles[a] / cycles_per_second).toFixed(2) + " µs
        Time / Frame:" + (1000000 * aggregated.cycles_per_frame[a] / cycles_per_second).toFixed(2) + " µs
        Calls:" + aggregated.calls[a] + "
        "; + return; + } + } + + // Next line + y += line_height * (1 + thread.max_depth) + thread_separation; + } + + // No bar found, hide tooltip + tooltip.style.visibility = "hidden"; +} + +function onMouseDown(evt) +{ + dragging = true; + previous_x = evt.clientX, previous_y = evt.clientY; + tooltip.style.visibility = "hidden"; +} + +function onMouseUp(evt) +{ + dragging = false; +} + +function clampMotion() +{ + // Clamp horizontally + var min_offset_x = canvas.width / scale - end_cycle; + if (offset_x < min_offset_x) + offset_x = min_offset_x; + if (offset_x > 0) + offset_x = 0; + + // Clamp vertically + var min_offset_y = canvas.height - size_y; + if (offset_y < min_offset_y) + offset_y = min_offset_y; + if (offset_y > 0) + offset_y = 0; + + // Clamp scale + if (scale < min_scale) + scale = min_scale; + var max_scale = 1000 * min_scale; + if (scale > max_scale) + scale = max_scale; +} + +function onMouseMove(evt) +{ + if (dragging) + { + // Calculate new offset + offset_x += (evt.clientX - previous_x) / scale; + offset_y += evt.clientY - previous_y; + + clampMotion(); + + drawChart(); + } + else + drawTooltip(evt.clientX - canvas.offsetLeft, evt.clientY - canvas.offsetTop); + + previous_x = evt.clientX, previous_y = evt.clientY; +} + +function onScroll(evt) +{ + tooltip.style.visibility = "hidden"; + + var old_scale = scale; + if (evt.deltaY > 0) + scale /= 1.1; + else + scale *= 1.1; + + clampMotion(); + + // Ensure that event under mouse stays under mouse + var x = previous_x - canvas.offsetLeft; + offset_x += x / scale - x / old_scale; + + clampMotion(); + + drawChart(); +} + +function darkenColor(color) +{ + var i = parseInt(color.slice(1), 16); + + var r = i >> 16; + var g = (i >> 8) & 0xff; + var b = i & 0xff; + + r = Math.round(0.8 * r); + g = Math.round(0.8 * g); + b = Math.round(0.8 * b); + + i = (r << 16) + (g << 8) + b; + + return "#" + i.toString(16); +} + +function startChart() +{ + // Fetch elements + canvas = document.getElementById('canvas'); + ctx = canvas.getContext("2d"); + tooltip = document.getElementById('tooltip'); + + // Resize canvas to fill screen + canvas.width = document.body.offsetWidth - 20; + canvas.height = document.body.offsetHeight - 20; + + // Register mouse handlers + canvas.onmousedown = onMouseDown; + canvas.onmouseup = onMouseUp; + canvas.onmouseout = onMouseUp; + canvas.onmousemove = onMouseMove; + canvas.onwheel = onScroll; + + for (var t = 0; t < threads.length; t++) + { + var thread = threads[t]; + + // Calculate darkened colors + thread.darkened_color = new Array(thread.color.length); + for (var s = 0; s < thread.color.length; s++) + thread.darkened_color[s] = darkenColor(thread.color[s]); + + // Calculate max depth and end cycle + thread.max_depth = 0; + for (var s = 0; s < thread.start.length; s++) + { + thread.max_depth = Math.max(thread.max_depth, thread.depth[s]); + end_cycle = Math.max(end_cycle, thread.start[s] + thread.cycles[s]); + } + } + + // Calculate width of name strings + ctx.font = bar_font; + aggregated.name_width = new Array(aggregated.name.length); + for (var a = 0; a < aggregated.name.length; a++) + aggregated.name_width[a] = ctx.measureText(aggregated.name[a]).width; + + // Store scale properties + min_scale = canvas.width / end_cycle; + scale = min_scale; + + drawChart(); +} \ No newline at end of file diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/run_doxygen.bat b/crates/joltc-sys-patched/JoltC/JoltPhysics/run_doxygen.bat new file mode 100644 index 00000000000..8f8b85d3243 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/run_doxygen.bat @@ -0,0 +1,4 @@ +@echo off +del %~dp0%Build\Doxygen /s /q > NUL +doxygen +pause \ No newline at end of file diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/sonar-project.properties b/crates/joltc-sys-patched/JoltC/JoltPhysics/sonar-project.properties new file mode 100644 index 00000000000..82c213972bb --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/JoltPhysics/sonar-project.properties @@ -0,0 +1,14 @@ +sonar.projectKey=jrouwe_JoltPhysics +sonar.organization=jrouwe + +# This is the name and version displayed in the SonarCloud UI. +sonar.projectName=Jolt Physics +sonar.projectVersion=1.0 + +# Path is relative to the sonar-project.properties file. Replace "\" by "/" on Windows. +sonar.sources=Jolt + +# Encoding of the source code. Default is default system encoding +#sonar.sourceEncoding=UTF-8 + +sonar.cfamily.threads=2 diff --git a/crates/joltc-sys-patched/JoltC/LICENSE-APACHE b/crates/joltc-sys-patched/JoltC/LICENSE-APACHE new file mode 100644 index 00000000000..8dada3edaf5 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/crates/joltc-sys-patched/JoltC/LICENSE-MIT b/crates/joltc-sys-patched/JoltC/LICENSE-MIT new file mode 100644 index 00000000000..e769463c211 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/LICENSE-MIT @@ -0,0 +1,21 @@ +MIT License + +Portions Copyright (c) 2024 Second Half Games + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/crates/joltc-sys-patched/JoltC/README.md b/crates/joltc-sys-patched/JoltC/README.md new file mode 100644 index 00000000000..544fe9b5053 --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/README.md @@ -0,0 +1,45 @@ +# JoltC +C wrapper for [Jolt Physics](https://github.com/jrouwe/JoltPhysics) 5.0.0. + +Currently a work in progress. Bindings contain functions that we've needed as part of our game or the Rust bindings we're working on in [jolt-rust](https://github.com/SecondHalfGames/jolt-rust). + +## Goals +1. Sound C wrapper around current version of Jolt Physics +2. Headers suitable for usage in automatic binding generation tools (i.e. Rust `bindgen`, LuaJIT FFI) + +## Building +Use CMake: + +```bash +cmake -B build +cmake --build build +``` + +## Hello, world! +A port of Jolt's "HelloWorld" example is provided in [HelloWorld/main.cpp](HelloWorld/main.cpp). + +## Other C Wrappers +Other C wrappers for Jolt Physics include: +- "JoltC", part of the [zphysics] Zig library started by [Michal Ziulek][michal-ziulek] +- "JoltC", part of the [jolt-rs] Rust library started by [cohaereo] and a fork of the zphysics C wrapper +- "joltc", part of the [JoltPhysicsSharp] C# library started by [Amer Koleci][amerkoleci] + +The goal of this project is to be the first C wrapper around Jolt Physics that is not part of a larger binding project and to eliminate sources of undefined behavior. It's intended to be useful for any other language-specific bindings and to reduce the need to duplicate work. + +## License +Licensed under either of + +* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) +* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. + +### Contribution +Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. + +[zphysics]: https://github.com/zig-gamedev/zig-gamedev/tree/main/libs/zphysics +[jolt-rs]: https://github.com/cohaereo/jolt-rs +[JoltPhysicsSharp]: https://github.com/amerkoleci/JoltPhysicsSharp +[michal-ziulek]: https://github.com/michal-z +[amerkoleci]: https://github.com/amerkoleci +[cohaereo]: https://github.com/cohaereo \ No newline at end of file diff --git a/crates/joltc-sys-patched/JoltC/test-all-flags.sh b/crates/joltc-sys-patched/JoltC/test-all-flags.sh new file mode 100644 index 00000000000..ee80034da2d --- /dev/null +++ b/crates/joltc-sys-patched/JoltC/test-all-flags.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash + +set -euo pipefail + +run_tests() { + echo "Building with flags $1" + + cmake $1 -B build + cmake --build build + + ./build/Debug/HelloWorld +} + +run_tests "" +run_tests "-DDOUBLE_PRECISION=ON" +run_tests "-DOBJECT_LAYER_BITS=32" \ No newline at end of file diff --git a/crates/joltc-sys-patched/README.md b/crates/joltc-sys-patched/README.md new file mode 100644 index 00000000000..5b5400f9fee --- /dev/null +++ b/crates/joltc-sys-patched/README.md @@ -0,0 +1,10 @@ +# `joltc-sys` — Jolt bindings via [JoltC] +This crate contains unsafe bindings to JoltC. + +## Build Requirements +- CMake 3.16 or newer +- `libclang`, see the [bindgen guide](https://rust-lang.github.io/rust-bindgen/requirements.html) for installation steps. + +## Features +- `double-precision`: Enable higher precision simulation using doubles instead of floats. +- `object-layer-u32`: Changes the ObjectLayer type to use 32 bits instead of 16 bits. \ No newline at end of file diff --git a/crates/joltc-sys-patched/build.rs b/crates/joltc-sys-patched/build.rs new file mode 100644 index 00000000000..8f2695680f6 --- /dev/null +++ b/crates/joltc-sys-patched/build.rs @@ -0,0 +1,166 @@ +use std::env; +use std::path::Path; + +use anyhow::Context; + +fn main() { + let flags = build_flags(); + + build_joltc(); + link(); + shadow_msvcprt(); + generate_bindings(&flags).unwrap(); +} + +/// Create a stub msvcprt.lib in our output directory to shadow the system one. +/// +/// V8 explicitly links msvcprt.lib (the dynamic C++ CRT import library) via +/// `cargo:rustc-link-lib=dylib=msvcprt`. Its objects carry +/// `/failifmismatch:RuntimeLibrary=MD_DynamicRelease`, which conflicts with +/// Tracy and Jolt objects compiled with /MT (`MT_StaticRelease`). +/// +/// Neither lld-link nor MSVC link.exe can suppress /failifmismatch, even with +/// /FORCE. The workaround: place an empty msvcprt.lib in our link search path +/// (which comes before system paths). The linker finds our stub first and loads +/// no conflicting objects. All C++ stdlib symbols V8 needs are already provided +/// by the static CRT (libcpmt.lib) linked via +crt-static. +fn shadow_msvcprt() { + if !cfg!(windows) { + return; + } + + let out_dir = env::var("OUT_DIR").unwrap(); + let stub_path = Path::new(&out_dir).join("lib").join("msvcprt.lib"); + + // Write a minimal valid COFF archive (empty, no members). + // Format: 8-byte signature + first linker member (empty symbol table). + let mut data = Vec::new(); + // Archive signature + data.extend_from_slice(b"!\n"); + // First linker member header (60 bytes total) + // Name (16 bytes) + data.extend_from_slice(b"/ "); + // Date (12 bytes) + data.extend_from_slice(b"0 "); + // User ID (6 bytes) + data.extend_from_slice(b" "); + // Group ID (6 bytes) + data.extend_from_slice(b" "); + // Mode (8 bytes) + data.extend_from_slice(b"0 "); + // Size (10 bytes) - 4 bytes for the symbol count (0) + data.extend_from_slice(b"4 "); + // End marker (2 bytes) + data.extend_from_slice(b"`\n"); + // Content: number of symbols = 0 (big-endian u32) + data.extend_from_slice(&[0u8; 4]); + + std::fs::write(&stub_path, &data) + .expect("failed to write stub msvcprt.lib"); +} + +fn build_joltc() { + let mut config = cmake::Config::new("JoltC"); + + // We always have to build in Release. + // + // On Windows, Rust always links against the non-debug CRT. Using the Debug + // profile (which the cmake crate will sometimes pick by default) causes + // Jolt/JoltC to be linked against the debug CRT, causing linker issues. + // + // Forcing Jolt and JoltC to be compiled with the non-debug CRT (/MT) + // doesn't change enough about the build to work. + // + // As a nice side effect, this ensures that we build with a known + // configuration instead of accidentally enabling or disabling extra + // features just based on opt-level. + config.profile("Release"); + + // Jolt fails to compile via the cmake crate without specifying exception + // handling behavior under MSVC. + if cfg!(windows) { + config.cxxflag("/EHsc"); + } + + // Build Jolt with static CRT (/MT) to match the rest of SpacetimeDB. + // See shadow_msvcprt() for how we handle the V8 CRT mismatch. + if cfg!(windows) { + config.static_crt(true); + config.configure_arg("-DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded"); + config.configure_arg("-DCMAKE_POLICY_DEFAULT_CMP0091=NEW"); + } + + // Having IPO/LTO turned on breaks lld on Windows. + config.configure_arg("-DINTERPROCEDURAL_OPTIMIZATION=OFF"); + + // These feature flags go through CMake and affect compilation of both Jolt + // and JoltC. + if cfg!(feature = "double-precision") { + config.configure_arg("-DDOUBLE_PRECISION=ON"); + } + if cfg!(feature = "object-layer-u32") { + config.configure_arg("-DOBJECT_LAYER_BITS=32"); + } + + let mut dst = config.build(); + + // Jolt and JoltC put libraries in the 'lib' subfolder. This goes against + // the docs of the cmake crate, but it's possible that it's just mishandling + // an output path and not account for the install target's configurability. + dst.push("lib"); + + println!("cargo:rustc-link-search=native={}", dst.display()); +} + +fn link() { + println!("cargo:rustc-link-lib=Jolt"); + println!("cargo:rustc-link-lib=joltc"); +} + +/// Generate build flags specifically for generating bindings. +/// +/// This is redundant with Jolt and JoltC's CMake files, which do this mapping +/// for us, but we're unable to leverage that config when running bindgen. +fn build_flags() -> Vec<(&'static str, &'static str)> { + let mut flags = Vec::new(); + + // Force the debug renderer on. In the future, we might want to tie this to + // a crate feature. + flags.push(("JPH_DEBUG_RENDERER", "ON")); + + // It's important that these flags are never out of sync for Jolt and JoltC. + if cfg!(feature = "double-precision") { + flags.push(("JPC_DOUBLE_PRECISION", "ON")); + flags.push(("JPH_DOUBLE_PRECISION", "ON")); + } + + if cfg!(feature = "object-layer-u32") { + flags.push(("JPC_OBJECT_LAYER_BITS", "32")); + flags.push(("JPH_OBJECT_LAYER_BITS", "32")); + } + + flags +} + +fn generate_bindings(flags: &[(&'static str, &'static str)]) -> anyhow::Result<()> { + let mut builder = bindgen::Builder::default() + .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) + .header("JoltC/JoltC/JoltC.h") + .clang_arg("-IJoltC") + .allowlist_item("JPC_.*") + .default_enum_style(bindgen::EnumVariation::Consts) + .prepend_enum_name(false); + + for (key, value) in flags { + builder = builder.clang_arg(format!("-D{key}={value}")); + } + + let bindings = builder + .generate() + .context("failed to generate JoltC bindings")?; + + let out_path = Path::new(&env::var("OUT_DIR").unwrap()).join("bindings.rs"); + bindings + .write_to_file(out_path) + .context("Couldn't write bindings!") +} diff --git a/crates/joltc-sys-patched/src/generated.rs b/crates/joltc-sys-patched/src/generated.rs new file mode 100644 index 00000000000..a38a13a81db --- /dev/null +++ b/crates/joltc-sys-patched/src/generated.rs @@ -0,0 +1,5 @@ +#![allow(non_upper_case_globals)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] + +include!(concat!(env!("OUT_DIR"), "/bindings.rs")); diff --git a/crates/joltc-sys-patched/src/lib.rs b/crates/joltc-sys-patched/src/lib.rs new file mode 100644 index 00000000000..abf5115794b --- /dev/null +++ b/crates/joltc-sys-patched/src/lib.rs @@ -0,0 +1,40 @@ +mod generated; + +pub use generated::*; + +#[cfg(feature = "double-precision")] +pub type Real = f64; + +#[cfg(not(feature = "double-precision"))] +pub type Real = f32; + +macro_rules! ffi_default { + ($($c_struct:ident -> $default_fn:ident,)*) => { + $( + impl Default for $c_struct { + fn default() -> Self { + unsafe { + let mut settings = std::mem::MaybeUninit::<$c_struct>::zeroed(); + $default_fn(settings.as_mut_ptr()); + settings.assume_init() + } + } + } + )* + }; +} + +ffi_default! { + JPC_BodyCreationSettings -> JPC_BodyCreationSettings_default, + + // All of the ShapeSettings types + JPC_TriangleShapeSettings -> JPC_TriangleShapeSettings_default, + JPC_BoxShapeSettings -> JPC_BoxShapeSettings_default, + JPC_SphereShapeSettings -> JPC_SphereShapeSettings_default, + JPC_CapsuleShapeSettings -> JPC_CapsuleShapeSettings_default, + JPC_CylinderShapeSettings -> JPC_CylinderShapeSettings_default, + JPC_ConvexHullShapeSettings -> JPC_ConvexHullShapeSettings_default, + JPC_SubShapeSettings -> JPC_SubShapeSettings_default, + JPC_StaticCompoundShapeSettings -> JPC_StaticCompoundShapeSettings_default, + JPC_MutableCompoundShapeSettings -> JPC_MutableCompoundShapeSettings_default, +} diff --git a/crates/joltc-sys-patched/tests/framework/mod.rs b/crates/joltc-sys-patched/tests/framework/mod.rs new file mode 100644 index 00000000000..c42ca2fdcd1 --- /dev/null +++ b/crates/joltc-sys-patched/tests/framework/mod.rs @@ -0,0 +1,206 @@ +use std::ffi::{c_uint, c_void, CStr, CString}; +use std::ptr; + +// Everything prefixed with `JPC_` comes from the joltc_sys crate. +use joltc_sys::*; + +pub const OL_NON_MOVING: JPC_ObjectLayer = 0; +pub const OL_MOVING: JPC_ObjectLayer = 1; + +pub const BPL_NON_MOVING: JPC_BroadPhaseLayer = 0; +pub const BPL_MOVING: JPC_BroadPhaseLayer = 1; +pub const BPL_COUNT: JPC_BroadPhaseLayer = 2; + +#[allow(unused_variables)] +pub trait SmokeTest { + unsafe fn setup(system: *mut JPC_PhysicsSystem) -> Self; + + unsafe fn post_update(&mut self, system: *mut JPC_PhysicsSystem) -> bool { + false + } + + unsafe fn teardown(&mut self, system: *mut JPC_PhysicsSystem) {} +} + +pub fn create_box(settings: &JPC_BoxShapeSettings) -> Result<*mut JPC_Shape, CString> { + let mut shape: *mut JPC_Shape = ptr::null_mut(); + let mut err: *mut JPC_String = ptr::null_mut(); + + unsafe { + if JPC_BoxShapeSettings_Create(settings, &mut shape, &mut err) { + Ok(shape) + } else { + Err(CStr::from_ptr(JPC_String_c_str(err)).to_owned()) + } + } +} + +pub fn create_sphere(settings: &JPC_SphereShapeSettings) -> Result<*mut JPC_Shape, CString> { + let mut shape: *mut JPC_Shape = ptr::null_mut(); + let mut err: *mut JPC_String = ptr::null_mut(); + + unsafe { + if JPC_SphereShapeSettings_Create(settings, &mut shape, &mut err) { + Ok(shape) + } else { + Err(CStr::from_ptr(JPC_String_c_str(err)).to_owned()) + } + } +} + +pub fn create_convex_hull( + settings: &JPC_ConvexHullShapeSettings, +) -> Result<*mut JPC_Shape, CString> { + let mut shape: *mut JPC_Shape = ptr::null_mut(); + let mut err: *mut JPC_String = ptr::null_mut(); + + unsafe { + if JPC_ConvexHullShapeSettings_Create(settings, &mut shape, &mut err) { + Ok(shape) + } else { + Err(CStr::from_ptr(JPC_String_c_str(err)).to_owned()) + } + } +} + +pub fn vec3(x: f32, y: f32, z: f32) -> JPC_Vec3 { + JPC_Vec3 { x, y, z, _w: z } +} + +pub fn rvec3(x: Real, y: Real, z: Real) -> JPC_RVec3 { + JPC_RVec3 { x, y, z, _w: z } +} + +fn global_init() { + use std::sync::OnceLock; + + static INITIALIZED: OnceLock<()> = OnceLock::new(); + + INITIALIZED.get_or_init(|| unsafe { + JPC_RegisterDefaultAllocator(); + JPC_FactoryInit(); + JPC_RegisterTypes(); + }); +} + +pub fn run_test() { + global_init(); + + unsafe { + let temp_allocator = JPC_TempAllocatorImpl_new(10 * 1024 * 1024); + + let job_system = + JPC_JobSystemThreadPool_new2(JPC_MAX_PHYSICS_JOBS as _, JPC_MAX_PHYSICS_BARRIERS as _); + + let broad_phase_layer_interface = JPC_BroadPhaseLayerInterface_new(ptr::null(), BPL); + + let object_vs_broad_phase_layer_filter = + JPC_ObjectVsBroadPhaseLayerFilter_new(ptr::null_mut(), OVB); + + let object_vs_object_layer_filter = JPC_ObjectLayerPairFilter_new(ptr::null_mut(), OVO); + + let physics_system = JPC_PhysicsSystem_new(); + + let max_bodies = 1024; + let num_body_mutexes = 0; + let max_body_pairs = 1024; + let max_contact_constraints = 1024; + + JPC_PhysicsSystem_Init( + physics_system, + max_bodies, + num_body_mutexes, + max_body_pairs, + max_contact_constraints, + broad_phase_layer_interface, + object_vs_broad_phase_layer_filter, + object_vs_object_layer_filter, + ); + + let mut test = S::setup(physics_system); + + // TODO: register body activation listener + // TODO: register contact listener + + // TODO: PhysicsSystem::OptimizeBroadPhase + + let delta_time = 1.0 / 60.0; + let collision_steps = 1; + + loop { + JPC_PhysicsSystem_Update( + physics_system, + delta_time, + collision_steps, + temp_allocator, + job_system, + ); + + if !test.post_update(physics_system) { + break; + } + } + + test.teardown(physics_system); + + JPC_PhysicsSystem_delete(physics_system); + JPC_BroadPhaseLayerInterface_delete(broad_phase_layer_interface); + JPC_ObjectVsBroadPhaseLayerFilter_delete(object_vs_broad_phase_layer_filter); + JPC_ObjectLayerPairFilter_delete(object_vs_object_layer_filter); + + JPC_JobSystemThreadPool_delete(job_system); + JPC_TempAllocatorImpl_delete(temp_allocator); + } +} + +unsafe extern "C" fn bpl_get_num_broad_phase_layers(_this: *const c_void) -> c_uint { + BPL_COUNT as _ +} + +unsafe extern "C" fn bpl_get_broad_phase_layer( + _this: *const c_void, + layer: JPC_ObjectLayer, +) -> JPC_BroadPhaseLayer { + match layer { + OL_NON_MOVING => BPL_NON_MOVING, + OL_MOVING => BPL_MOVING, + _ => unreachable!(), + } +} + +const BPL: JPC_BroadPhaseLayerInterfaceFns = JPC_BroadPhaseLayerInterfaceFns { + GetNumBroadPhaseLayers: Some(bpl_get_num_broad_phase_layers as _), + GetBroadPhaseLayer: Some(bpl_get_broad_phase_layer as _), +}; + +unsafe extern "C" fn ovb_should_collide( + _this: *const c_void, + layer1: JPC_ObjectLayer, + layer2: JPC_BroadPhaseLayer, +) -> bool { + match layer1 { + OL_NON_MOVING => layer2 == BPL_MOVING, + OL_MOVING => true, + _ => unreachable!(), + } +} + +const OVB: JPC_ObjectVsBroadPhaseLayerFilterFns = JPC_ObjectVsBroadPhaseLayerFilterFns { + ShouldCollide: Some(ovb_should_collide as _), +}; + +unsafe extern "C" fn ovo_should_collide( + _this: *const c_void, + layer1: JPC_ObjectLayer, + layer2: JPC_ObjectLayer, +) -> bool { + match layer1 { + OL_NON_MOVING => layer2 == OL_MOVING, + OL_MOVING => true, + _ => unreachable!(), + } +} + +const OVO: JPC_ObjectLayerPairFilterFns = JPC_ObjectLayerPairFilterFns { + ShouldCollide: Some(ovo_should_collide as _), +}; diff --git a/crates/joltc-sys-patched/tests/smoke_test.rs b/crates/joltc-sys-patched/tests/smoke_test.rs new file mode 100644 index 00000000000..7e10361e007 --- /dev/null +++ b/crates/joltc-sys-patched/tests/smoke_test.rs @@ -0,0 +1,246 @@ +mod framework; + +use std::f32::consts::{PI, TAU}; +use std::mem; + +use joltc_sys::*; +use rand::Rng; + +use crate::framework::*; + +struct SetupTeardown; + +impl SmokeTest for SetupTeardown { + unsafe fn setup(_system: *mut JPC_PhysicsSystem) -> Self { + Self + } +} + +#[test] +fn setup_teardown() { + run_test::(); +} + +struct HelloShapes { + floor: JPC_BodyID, + sphere: JPC_BodyID, +} + +impl SmokeTest for HelloShapes { + unsafe fn setup(system: *mut JPC_PhysicsSystem) -> Self { + let body_interface = JPC_PhysicsSystem_GetBodyInterface(system); + + let floor_shape = create_box(&JPC_BoxShapeSettings { + HalfExtent: vec3(100.0, 1.0, 100.0), + ..Default::default() + }) + .unwrap(); + + let floor_settings = JPC_BodyCreationSettings { + Position: rvec3(0.0, -1.0, 0.0), + MotionType: JPC_MOTION_TYPE_STATIC, + ObjectLayer: OL_NON_MOVING, + Shape: floor_shape, + ..Default::default() + }; + + let floor = JPC_BodyInterface_CreateBody(body_interface, &floor_settings); + let floor_id = JPC_Body_GetID(floor); + JPC_BodyInterface_AddBody(body_interface, floor_id, JPC_ACTIVATION_DONT_ACTIVATE); + + let sphere_shape = create_sphere(&JPC_SphereShapeSettings { + Radius: 0.5, + ..Default::default() + }) + .unwrap(); + + let sphere_settings = JPC_BodyCreationSettings { + Position: rvec3(0.0, 2.0, 0.0), + MotionType: JPC_MOTION_TYPE_DYNAMIC, + ObjectLayer: OL_MOVING, + Shape: sphere_shape, + ..Default::default() + }; + + let sphere = JPC_BodyInterface_CreateBody(body_interface, &sphere_settings); + let sphere_id = JPC_Body_GetID(sphere); + JPC_BodyInterface_AddBody(body_interface, sphere_id, JPC_ACTIVATION_ACTIVATE); + + JPC_BodyInterface_SetLinearVelocity(body_interface, sphere_id, vec3(0.0, -5.0, 0.0)); + + Self { + sphere: sphere_id, + floor: floor_id, + } + } + + unsafe fn post_update(&mut self, system: *mut JPC_PhysicsSystem) -> bool { + let body_interface = JPC_PhysicsSystem_GetBodyInterface(system); + JPC_BodyInterface_IsActive(body_interface, self.sphere) + } + + unsafe fn teardown(&mut self, system: *mut JPC_PhysicsSystem) { + let body_interface = JPC_PhysicsSystem_GetBodyInterface(system); + + JPC_BodyInterface_RemoveBody(body_interface, self.floor); + JPC_BodyInterface_DestroyBody(body_interface, self.floor); + + JPC_BodyInterface_RemoveBody(body_interface, self.sphere); + JPC_BodyInterface_DestroyBody(body_interface, self.sphere); + } +} + +#[test] +fn hello_shapes() { + run_test::(); +} + +struct HelloConvexHull { + floor: JPC_BodyID, + hull: JPC_BodyID, +} + +impl SmokeTest for HelloConvexHull { + unsafe fn setup(system: *mut JPC_PhysicsSystem) -> Self { + let body_interface = JPC_PhysicsSystem_GetBodyInterface(system); + + let floor_shape = create_box(&JPC_BoxShapeSettings { + HalfExtent: vec3(100.0, 1.0, 100.0), + ..Default::default() + }) + .unwrap(); + + let floor_settings = JPC_BodyCreationSettings { + Position: rvec3(0.0, -1.0, 0.0), + MotionType: JPC_MOTION_TYPE_STATIC, + ObjectLayer: OL_NON_MOVING, + Shape: floor_shape, + ..Default::default() + }; + + let floor = JPC_BodyInterface_CreateBody(body_interface, &floor_settings); + let floor_id = JPC_Body_GetID(floor); + JPC_BodyInterface_AddBody(body_interface, floor_id, JPC_ACTIVATION_DONT_ACTIVATE); + + let mut rng = rand::thread_rng(); + let mut points = Vec::with_capacity(200); + for _ in 0..200 { + let theta = rng.gen_range(0.0..PI); + let phi = rng.gen_range(0.0..TAU); + points.push(vec3( + theta.sin() * phi.cos(), + theta.sin() * phi.sin(), + theta.cos(), + )); + } + + let hull_shape = create_convex_hull(&JPC_ConvexHullShapeSettings { + Points: points.as_ptr(), + PointsLen: points.len(), + ..Default::default() + }) + .unwrap(); + + let hull_settings = JPC_BodyCreationSettings { + Position: rvec3(0.0, 2.0, 0.0), + MotionType: JPC_MOTION_TYPE_DYNAMIC, + ObjectLayer: OL_MOVING, + Shape: hull_shape, + ..Default::default() + }; + + let hull = JPC_BodyInterface_CreateBody(body_interface, &hull_settings); + let hull_id = JPC_Body_GetID(hull); + JPC_BodyInterface_AddBody(body_interface, hull_id, JPC_ACTIVATION_ACTIVATE); + + JPC_BodyInterface_SetLinearVelocity(body_interface, hull_id, vec3(0.0, -5.0, 0.0)); + + Self { + hull: hull_id, + floor: floor_id, + } + } + + unsafe fn post_update(&mut self, system: *mut JPC_PhysicsSystem) -> bool { + let body_interface = JPC_PhysicsSystem_GetBodyInterface(system); + JPC_BodyInterface_IsActive(body_interface, self.hull) + } + + unsafe fn teardown(&mut self, system: *mut JPC_PhysicsSystem) { + let body_interface = JPC_PhysicsSystem_GetBodyInterface(system); + + JPC_BodyInterface_RemoveBody(body_interface, self.floor); + JPC_BodyInterface_DestroyBody(body_interface, self.floor); + + JPC_BodyInterface_RemoveBody(body_interface, self.hull); + JPC_BodyInterface_DestroyBody(body_interface, self.hull); + } +} + +#[test] +fn hello_convex_hull() { + run_test::(); +} + +struct NarrowPhaseRayCast { + sphere: JPC_BodyID, +} + +impl SmokeTest for NarrowPhaseRayCast { + unsafe fn setup(system: *mut JPC_PhysicsSystem) -> Self { + let body_interface = JPC_PhysicsSystem_GetBodyInterface(system); + + let sphere_shape = create_sphere(&JPC_SphereShapeSettings { + Radius: 0.5, + ..Default::default() + }) + .unwrap(); + + let sphere_settings = JPC_BodyCreationSettings { + Position: rvec3(0.0, 2.0, 0.0), + MotionType: JPC_MOTION_TYPE_DYNAMIC, + ObjectLayer: OL_MOVING, + Shape: sphere_shape, + ..Default::default() + }; + + let sphere = JPC_BodyInterface_CreateBody(body_interface, &sphere_settings); + let sphere_id = JPC_Body_GetID(sphere); + JPC_BodyInterface_AddBody(body_interface, sphere_id, JPC_ACTIVATION_ACTIVATE); + + let query = JPC_PhysicsSystem_GetNarrowPhaseQuery(system); + + let mut args = JPC_NarrowPhaseQuery_CastRayArgs { + Ray: JPC_RRayCast { + Origin: rvec3(1.0, 2.0, 0.0), + Direction: vec3(-2.0, 0.0, 0.0), + }, + ..mem::zeroed() + }; + let hit = JPC_NarrowPhaseQuery_CastRay(query, &mut args); + + assert!(hit, "ray should hit the sphere"); + assert!( + (args.Result.Fraction - 0.25).abs() < 0.01, + "ray should hit at around 0.25 fraction" + ); + + Self { sphere: sphere_id } + } + + unsafe fn post_update(&mut self, _system: *mut JPC_PhysicsSystem) -> bool { + false + } + + unsafe fn teardown(&mut self, system: *mut JPC_PhysicsSystem) { + let body_interface = JPC_PhysicsSystem_GetBodyInterface(system); + + JPC_BodyInterface_RemoveBody(body_interface, self.sphere); + JPC_BodyInterface_DestroyBody(body_interface, self.sphere); + } +} + +#[test] +fn narrow_phase_ray_cast() { + run_test::(); +} diff --git a/modules/physics-test-cs/Lib.cs b/modules/physics-test-cs/Lib.cs new file mode 100644 index 00000000000..c2510023e97 --- /dev/null +++ b/modules/physics-test-cs/Lib.cs @@ -0,0 +1,380 @@ +using SpacetimeDB; + +namespace SpacetimeDB.Modules.PhysicsTest; + +[Table(Accessor = "physics_config", Public = true)] +public partial struct PhysicsConfig +{ + [PrimaryKey] + public uint Id; // always 0 + public uint WorldId; + public uint GroundBodyId; +} + +[Table(Accessor = "physics_body", Public = true)] +public partial struct PhysicsBodyRow +{ + [PrimaryKey] + [AutoInc] + public uint Id; + public uint JoltBodyId; + public string Label; + public float PosX; + public float PosY; + public float PosZ; + public float RotX; + public float RotY; + public float RotZ; + public float RotW; + public float VelX; + public float VelY; + public float VelZ; +} + +[Table(Accessor = "vehicle", Public = true)] +public partial struct VehicleRow +{ + [PrimaryKey] + [AutoInc] + public uint Id; + public uint JoltVehicleId; + public string Label; + // Body transform + public float PosX, PosY, PosZ; + public float RotX, RotY, RotZ, RotW; + public float VelX, VelY, VelZ; + // Vehicle state + public float SpeedKmh; + public float EngineRpm; + public int CurrentGear; + // Wheel states (FL, FR, RL, RR) - positions for client rendering + public float WheelFL_PosX, WheelFL_PosY, WheelFL_PosZ; + public float WheelFL_RotX, WheelFL_RotY, WheelFL_RotZ, WheelFL_RotW; + public float WheelFR_PosX, WheelFR_PosY, WheelFR_PosZ; + public float WheelFR_RotX, WheelFR_RotY, WheelFR_RotZ, WheelFR_RotW; + public float WheelRL_PosX, WheelRL_PosY, WheelRL_PosZ; + public float WheelRL_RotX, WheelRL_RotY, WheelRL_RotZ, WheelRL_RotW; + public float WheelRR_PosX, WheelRR_PosY, WheelRR_PosZ; + public float WheelRR_RotX, WheelRR_RotY, WheelRR_RotZ, WheelRR_RotW; +} + +[Table(Accessor = "physics_tick_schedule", Scheduled = "PhysicsTick", ScheduledAt = nameof(ScheduledAt))] +public partial struct PhysicsTickSchedule +{ + [PrimaryKey] + [AutoInc] + public ulong ScheduledId; + public ScheduleAt ScheduledAt; +} + +public static partial class Module +{ + const float TICK_RATE = 1f / 30f; // 30 Hz + + [Reducer(ReducerKind.Init)] + public static void Init(ReducerContext ctx) + { + Log.Info("PhysicsTest: Initializing..."); + + // Create physics world with gravity + var worldId = Physics.CreateWorld(0f, -9.81f, 0f); + Log.Info($"PhysicsTest: Created world {worldId}"); + + // Create ground plane (static box, 100x1x100) + var groundId = Physics.CreateBody( + worldId, + PhysicsShapeType.Box, + halfExtents: (50f, 0.5f, 50f), + position: (0f, -0.5f, 0f), + motionType: PhysicsMotionType.Static, + friction: 0.5f + ); + Log.Info($"PhysicsTest: Created ground body {groundId}"); + + // Save config + ctx.Db.physics_config.Insert(new PhysicsConfig + { + Id = 0, + WorldId = worldId, + GroundBodyId = groundId, + }); + + // Create a few dynamic bodies to test + SpawnSphere(ctx, worldId, "ball_1", 0f, 10f, 0f, 0.5f, 1f, 0.8f); + SpawnSphere(ctx, worldId, "ball_2", 2f, 15f, 0f, 0.5f, 1f, 0.5f); + SpawnBox(ctx, worldId, "crate_1", -2f, 12f, 0f, 0.5f, 0.5f, 0.5f, 2f); + + // Schedule the physics tick loop + ctx.Db.physics_tick_schedule.Insert(new PhysicsTickSchedule + { + ScheduledAt = new ScheduleAt.Interval(TimeSpan.FromSeconds(TICK_RATE)), + }); + + Log.Info("PhysicsTest: Init complete. Physics tick scheduled at 30Hz."); + } + + static void SpawnSphere(ReducerContext ctx, uint worldId, string label, float x, float y, float z, float radius, float mass, float restitution) + { + var bodyId = Physics.CreateBody( + worldId, + PhysicsShapeType.Sphere, + radius: radius, + position: (x, y, z), + motionType: PhysicsMotionType.Dynamic, + mass: mass, + restitution: restitution, + friction: 0.5f + ); + + ctx.Db.physics_body.Insert(new PhysicsBodyRow + { + JoltBodyId = bodyId, + Label = label, + PosX = x, PosY = y, PosZ = z, + RotX = 0, RotY = 0, RotZ = 0, RotW = 1, + }); + + Log.Info($"PhysicsTest: Spawned sphere '{label}' bodyId={bodyId} at ({x},{y},{z})"); + } + + static void SpawnBox(ReducerContext ctx, uint worldId, string label, float x, float y, float z, float hx, float hy, float hz, float mass) + { + var bodyId = Physics.CreateBody( + worldId, + PhysicsShapeType.Box, + halfExtents: (hx, hy, hz), + position: (x, y, z), + motionType: PhysicsMotionType.Dynamic, + mass: mass, + restitution: 0.3f, + friction: 0.6f + ); + + ctx.Db.physics_body.Insert(new PhysicsBodyRow + { + JoltBodyId = bodyId, + Label = label, + PosX = x, PosY = y, PosZ = z, + RotX = 0, RotY = 0, RotZ = 0, RotW = 1, + }); + + Log.Info($"PhysicsTest: Spawned box '{label}' bodyId={bodyId} at ({x},{y},{z})"); + } + + [Reducer] + public static void PhysicsTick(ReducerContext ctx, PhysicsTickSchedule args) + { + var config = ctx.Db.physics_config.Id.Find(0); + if (config == null) + return; + + var worldId = config.Value.WorldId; + + // Step physics + Physics.Step(worldId, TICK_RATE); + + // Get all body transforms from Jolt + var states = Physics.GetAllBodyTransforms(worldId); + + // Update body table rows + foreach (var state in states) + { + foreach (var row in ctx.Db.physics_body.Iter()) + { + if (row.JoltBodyId == state.BodyId) + { + var updated = row; + updated.PosX = state.PosX; + updated.PosY = state.PosY; + updated.PosZ = state.PosZ; + updated.RotX = state.RotX; + updated.RotY = state.RotY; + updated.RotZ = state.RotZ; + updated.RotW = state.RotW; + updated.VelX = state.VelX; + updated.VelY = state.VelY; + updated.VelZ = state.VelZ; + ctx.Db.physics_body.Id.Update(updated); + break; + } + } + } + + // Sync vehicle states + SyncVehicleStates(ctx, worldId); + } + + [Reducer] + public static void SpawnBall(ReducerContext ctx, float x, float y, float z, float radius, float mass, float restitution) + { + var config = ctx.Db.physics_config.Id.Find(0); + if (config == null) + { + Log.Error("PhysicsTest: No physics world! Call Init first."); + return; + } + + var count = 0; + foreach (var _ in ctx.Db.physics_body.Iter()) count++; + + SpawnSphere(ctx, config.Value.WorldId, $"ball_{count}", x, y, z, radius, mass, restitution); + } + + [Reducer] + public static void ApplyImpulse(ReducerContext ctx, uint bodyTableId, float ix, float iy, float iz) + { + var config = ctx.Db.physics_config.Id.Find(0); + if (config == null) return; + + var body = ctx.Db.physics_body.Id.Find(bodyTableId); + if (body == null) + { + Log.Error($"PhysicsTest: Body {bodyTableId} not found"); + return; + } + + Physics.AddImpulse(config.Value.WorldId, body.Value.JoltBodyId, ix, iy, iz); + Log.Info($"PhysicsTest: Applied impulse ({ix},{iy},{iz}) to '{body.Value.Label}'"); + } + + // ── Vehicle Reducers ─────────────────────────────────────────── + + [Reducer] + public static void SpawnVehicle(ReducerContext ctx, float x, float y, float z, string label) + { + var config = ctx.Db.physics_config.Id.Find(0); + if (config == null) + { + Log.Error("PhysicsTest: No physics world!"); + return; + } + + var worldId = config.Value.WorldId; + + // Get default sedan params from Jolt + var vehicleParams = Physics.GetVehicleParamsDefault(); + if (vehicleParams.Length == 0) + { + Log.Error("PhysicsTest: Failed to get default vehicle params"); + return; + } + + var vehicleId = Physics.CreateVehicle(worldId, vehicleParams, position: (x, y, z)); + if (vehicleId == 0) + { + Log.Error("PhysicsTest: Failed to create vehicle"); + return; + } + + ctx.Db.vehicle.Insert(new VehicleRow + { + JoltVehicleId = vehicleId, + Label = label, + PosX = x, PosY = y, PosZ = z, + RotX = 0, RotY = 0, RotZ = 0, RotW = 1, + }); + + Log.Info($"PhysicsTest: Spawned vehicle '{label}' id={vehicleId} at ({x},{y},{z})"); + } + + [Reducer] + public static void SetVehicleInput(ReducerContext ctx, uint vehicleTableId, float forward, float right, float brake, float handbrake) + { + var config = ctx.Db.physics_config.Id.Find(0); + if (config == null) return; + + var vehicle = ctx.Db.vehicle.Id.Find(vehicleTableId); + if (vehicle == null) + { + Log.Error($"PhysicsTest: Vehicle {vehicleTableId} not found"); + return; + } + + Physics.SetVehicleInput(config.Value.WorldId, vehicle.Value.JoltVehicleId, forward, right, brake, handbrake); + if (forward != 0 || right != 0) + Log.Info($"VehicleInput: joltId={vehicle.Value.JoltVehicleId} fwd={forward} right={right}"); + } + + /// + /// Update vehicle rows from Jolt state. Called each PhysicsTick. + /// + static void SyncVehicleStates(ReducerContext ctx, uint worldId) + { + foreach (var row in ctx.Db.vehicle.Iter()) + { + var stateBytes = Physics.GetVehicleStateRaw(worldId, row.JoltVehicleId); + if (stateBytes == null) continue; + + var d = stateBytes.AsSpan(); + var updated = row; + + // Body transform (same layout as JPC_VehicleState) + int o = 0; + updated.PosX = ReadF(d, o); o += 4; + updated.PosY = ReadF(d, o); o += 4; + updated.PosZ = ReadF(d, o); o += 4; + updated.RotX = ReadF(d, o); o += 4; + updated.RotY = ReadF(d, o); o += 4; + updated.RotZ = ReadF(d, o); o += 4; + updated.RotW = ReadF(d, o); o += 4; + updated.VelX = ReadF(d, o); o += 4; + updated.VelY = ReadF(d, o); o += 4; + updated.VelZ = ReadF(d, o); o += 4; + updated.SpeedKmh = ReadF(d, o); o += 4; + updated.EngineRpm = ReadF(d, o); o += 4; + updated.CurrentGear = ReadI(d, o); o += 4; + + // 4 wheels: each has pos(3f), rot(4f), steer_angle(f), rotation_angle(f), angular_velocity(f), suspension_length(f), has_contact(bool+padding) + // JPC_WheelState = 11 floats + 1 bool (aligned to 4) = 48 bytes + ReadWheel(d, ref o, out updated.WheelFL_PosX, out updated.WheelFL_PosY, out updated.WheelFL_PosZ, + out updated.WheelFL_RotX, out updated.WheelFL_RotY, out updated.WheelFL_RotZ, out updated.WheelFL_RotW); + ReadWheel(d, ref o, out updated.WheelFR_PosX, out updated.WheelFR_PosY, out updated.WheelFR_PosZ, + out updated.WheelFR_RotX, out updated.WheelFR_RotY, out updated.WheelFR_RotZ, out updated.WheelFR_RotW); + ReadWheel(d, ref o, out updated.WheelRL_PosX, out updated.WheelRL_PosY, out updated.WheelRL_PosZ, + out updated.WheelRL_RotX, out updated.WheelRL_RotY, out updated.WheelRL_RotZ, out updated.WheelRL_RotW); + ReadWheel(d, ref o, out updated.WheelRR_PosX, out updated.WheelRR_PosY, out updated.WheelRR_PosZ, + out updated.WheelRR_RotX, out updated.WheelRR_RotY, out updated.WheelRR_RotZ, out updated.WheelRR_RotW); + + ctx.Db.vehicle.Id.Update(updated); + + // Debug: log wheel contact and position every ~1 second (every 30 ticks) + if (row.Id == 1) + { + // Re-read has_contact from the raw bytes for debugging + // Each wheel: 7 floats (pos+rot) + 4 floats (steer/rotation/angular/suspension) + 1 bool + 3 pad = 48 bytes + // Body: 13 floats = 52 bytes + int wheelStart = 52; // offset after body data + bool[] contacts = new bool[4]; + for (int w = 0; w < 4; w++) + { + int contactOffset = wheelStart + w * 48 + 44; // 11 floats = 44 bytes, then bool + contacts[w] = d[contactOffset] != 0; + } + Log.Info($"Vehicle pos=({updated.PosX:F2},{updated.PosY:F2},{updated.PosZ:F2}) speed={updated.SpeedKmh:F1} gear={updated.CurrentGear} rpm={updated.EngineRpm:F0} contacts=[{contacts[0]},{contacts[1]},{contacts[2]},{contacts[3]}]"); + } + } + } + + static float ReadF(ReadOnlySpan d, int offset) => + System.Buffers.Binary.BinaryPrimitives.ReadSingleLittleEndian(d[offset..]); + + static int ReadI(ReadOnlySpan d, int offset) => + System.Buffers.Binary.BinaryPrimitives.ReadInt32LittleEndian(d[offset..]); + + static void ReadWheel(ReadOnlySpan d, ref int o, + out float px, out float py, out float pz, + out float rx, out float ry, out float rz, out float rw) + { + px = ReadF(d, o); o += 4; + py = ReadF(d, o); o += 4; + pz = ReadF(d, o); o += 4; + rx = ReadF(d, o); o += 4; + ry = ReadF(d, o); o += 4; + rz = ReadF(d, o); o += 4; + rw = ReadF(d, o); o += 4; + // Skip: steer_angle, rotation_angle, angular_velocity, suspension_length (4 floats = 16 bytes) + o += 16; + // Skip: has_contact (bool + 3 bytes padding) + o += 4; + } +} diff --git a/modules/physics-test-cs/physics-test-cs.csproj b/modules/physics-test-cs/physics-test-cs.csproj new file mode 100644 index 00000000000..43eb99b6e86 --- /dev/null +++ b/modules/physics-test-cs/physics-test-cs.csproj @@ -0,0 +1,8 @@ + + + + + + + + From 807a0dba38e734ce3b9faf7908183a09a50310f8 Mon Sep 17 00:00:00 2001 From: twodreams Date: Mon, 16 Mar 2026 17:27:23 -0300 Subject: [PATCH 2/4] refactor: Replace vendored joltc-sys with git dependency Replace the vendored joltc-sys-patched folder (~577 files, 11MB) with a git dependency pointing to pbisogno/jolt-rust feature/spacetimedb branch. The Jolt Physics code now lives in its own fork chain: - pbisogno/jolt-rust (build.rs patches for Windows/SpacetimeDB) - pbisogno/JoltC (Vehicle API additions) - jrouwe/JoltPhysics (upstream, unchanged) Co-Authored-By: Claude Opus 4.6 --- Cargo.toml | 8 +- crates/joltc-sys-patched/.cargo-ok | 1 - crates/joltc-sys-patched/.cargo_vcs_info.json | 6 - crates/joltc-sys-patched/.gitignore | 1 - crates/joltc-sys-patched/Cargo.toml | 51 - crates/joltc-sys-patched/Cargo.toml.orig | 30 - crates/joltc-sys-patched/JoltC/.editorconfig | 6 - .../JoltC/.github/workflows/ci.yml | 54 - crates/joltc-sys-patched/JoltC/.gitignore | 1 - crates/joltc-sys-patched/JoltC/.gitmodules | 3 - crates/joltc-sys-patched/JoltC/CMakeLists.txt | 76 - .../JoltC/HelloWorld/README.md | 2 - .../JoltC/HelloWorld/main.cpp | 218 -- crates/joltc-sys-patched/JoltC/JoltC/Enums.h | 301 -- .../joltc-sys-patched/JoltC/JoltC/Functions.h | 819 ----- .../joltc-sys-patched/JoltC/JoltC/JoltC.cpp | 1526 --------- crates/joltc-sys-patched/JoltC/JoltC/JoltC.h | 12 - crates/joltc-sys-patched/JoltC/JoltC/Test.cpp | 50 - .../joltc-sys-patched/JoltC/JoltC/Vehicle.cpp | 375 --- .../joltc-sys-patched/JoltC/JoltC/Vehicle.h | 130 - .../JoltC/JoltPhysics/.clang-format | 1 - .../JoltC/JoltPhysics/.editorconfig | 13 - .../JoltC/JoltPhysics/.gitattributes | 18 - .../JoltC/JoltPhysics/.github/dependabot.yml | 9 - .../JoltPhysics/.github/workflows/build.yml | 322 -- .../.github/workflows/determinism_check.yml | 119 - .../JoltPhysics/.github/workflows/doxygen.yml | 26 - .../.github/workflows/sonar-cloud.yml | 59 - .../JoltC/JoltPhysics/.gitignore | 8 - .../JoltC/JoltPhysics/Build/.gitignore | 6 - .../JoltPhysics/Build/Android/.gitignore | 5 - .../Android/PerformanceTest/build.gradle | 51 - .../src/main/AndroidManifest.xml | 20 - .../src/main/cpp/CMakeLists.txt | 20 - .../Build/Android/UnitTests/build.gradle | 51 - .../UnitTests/src/main/AndroidManifest.xml | 20 - .../UnitTests/src/main/cpp/CMakeLists.txt | 20 - .../JoltPhysics/Build/Android/build.gradle | 17 - .../Build/Android/gradle.properties | 22 - .../Android/gradle/wrapper/gradle-wrapper.jar | Bin 59203 -> 0 bytes .../gradle/wrapper/gradle-wrapper.properties | 5 - .../JoltC/JoltPhysics/Build/Android/gradlew | 185 -- .../JoltPhysics/Build/Android/gradlew.bat | 89 - .../JoltPhysics/Build/Android/settings.gradle | 10 - .../JoltC/JoltPhysics/Build/CMakeLists.txt | 319 -- .../JoltC/JoltPhysics/Build/README.md | 237 -- .../Build/cmake_linux_clang_gcc.sh | 28 - .../JoltPhysics/Build/cmake_linux_mingw.sh | 19 - .../JoltPhysics/Build/cmake_vs2019_cl.bat | 3 - .../JoltPhysics/Build/cmake_vs2019_cl_arm.bat | 3 - .../Build/cmake_vs2019_cl_arm_32bit.bat | 3 - .../JoltPhysics/Build/cmake_vs2019_clang.bat | 10 - .../JoltPhysics/Build/cmake_vs2022_cl.bat | 3 - .../Build/cmake_vs2022_cl_32bit.bat | 3 - .../JoltPhysics/Build/cmake_vs2022_cl_arm.bat | 3 - .../Build/cmake_vs2022_cl_arm_32bit.bat | 3 - ...vs2022_cl_cross_platform_deterministic.bat | 3 - .../Build/cmake_vs2022_cl_double.bat | 3 - .../JoltPhysics/Build/cmake_vs2022_clang.bat | 10 - .../Build/cmake_vs2022_clang_double.bat | 10 - .../JoltPhysics/Build/cmake_vs2022_uwp.bat | 5 - .../Build/cmake_vs2022_uwp_arm.bat | 5 - .../JoltPhysics/Build/cmake_windows_mingw.sh | 19 - .../JoltPhysics/Build/cmake_xcode_ios.sh | 4 - .../JoltPhysics/Build/cmake_xcode_macos.sh | 4 - .../JoltPhysics/Build/iOS/UnitTestsInfo.plist | 34 - .../JoltC/JoltPhysics/ContributorAgreement.md | 123 - .../JoltC/JoltPhysics/Docs/APIChanges.md | 111 - .../JoltC/JoltPhysics/Docs/Architecture.md | 755 ----- .../JoltPhysics/Docs/Images/ActiveEdge.jpg | Bin 15391 -> 0 bytes .../Images/ActiveVsInactiveContactNormal.jpg | Bin 16028 -> 0 bytes .../JoltPhysics/Docs/Images/ConvexRadius.jpg | Bin 11149 -> 0 bytes .../JoltPhysics/Docs/Images/EllipsoidAABB.png | Bin 158978 -> 0 bytes .../Docs/Images/GhostCollision.jpg | Bin 16249 -> 0 bytes .../JoltPhysics/Docs/Images/LongAndThin.jpg | Bin 13674 -> 0 bytes .../JoltPhysics/Docs/Images/MotionQuality.jpg | Bin 33112 -> 0 bytes .../JoltPhysics/Docs/Images/MotorDamping.jpg | Bin 96716 -> 0 bytes .../Docs/Images/MotorFrequency.jpg | Bin 101436 -> 0 bytes .../Docs/Images/QuadTreeExample.png | Bin 65699 -> 0 bytes .../Docs/Images/ShapeCenterOfMass.jpg | Bin 30218 -> 0 bytes .../Docs/Images/SimulationIsland.jpg | Bin 19279 -> 0 bytes .../Docs/Images/SoftBodySkinnedConstraint.jpg | Bin 27634 -> 0 bytes .../JoltC/JoltPhysics/Docs/Logo.png | Bin 1937976 -> 0 bytes .../JoltC/JoltPhysics/Docs/LogoDark.png | Bin 2478107 -> 0 bytes .../JoltC/JoltPhysics/Docs/LogoSmall.png | Bin 14038 -> 0 bytes .../JoltC/JoltPhysics/Docs/PerformanceTest.md | 36 - .../Docs/PhysicsSystemUpdate.drawio | 537 ---- .../JoltPhysics/Docs/PhysicsSystemUpdate.svg | 4 - .../JoltPhysics/Docs/ProjectsUsingJolt.md | 16 - .../JoltC/JoltPhysics/Docs/ReleaseNotes.md | 209 -- .../JoltC/JoltPhysics/Docs/Samples.md | 168 - .../JoltPhysics/Docs/SwingTwistConstraint.png | Bin 92708 -> 0 bytes .../JoltC/JoltPhysics/Doxyfile | 2775 ----------------- .../JoltPhysics/HelloWorld/HelloWorld.cmake | 11 - .../JoltPhysics/HelloWorld/HelloWorld.cpp | 364 --- .../Jolt/AABBTree/AABBTreeBuilder.cpp | 225 -- .../Jolt/AABBTree/AABBTreeBuilder.h | 110 - .../Jolt/AABBTree/AABBTreeToBuffer.h | 245 -- .../NodeCodec/NodeCodecQuadTreeHalfFloat.h | 287 -- .../TriangleCodecIndexed8BitPackSOA4Flags.h | 456 --- .../JoltPhysics/Jolt/ConfigurationString.h | 71 - .../JoltC/JoltPhysics/Jolt/Core/ARMNeon.h | 88 - .../JoltC/JoltPhysics/Jolt/Core/Atomics.h | 44 - .../JoltC/JoltPhysics/Jolt/Core/ByteBuffer.h | 74 - .../JoltC/JoltPhysics/Jolt/Core/Color.cpp | 38 - .../JoltC/JoltPhysics/Jolt/Core/Color.h | 81 - .../JoltC/JoltPhysics/Jolt/Core/Core.h | 555 ---- .../JoltPhysics/Jolt/Core/FPControlWord.h | 135 - .../JoltC/JoltPhysics/Jolt/Core/FPException.h | 74 - .../JoltPhysics/Jolt/Core/FPFlushDenormals.h | 41 - .../JoltC/JoltPhysics/Jolt/Core/Factory.cpp | 87 - .../JoltC/JoltPhysics/Jolt/Core/Factory.h | 54 - .../JoltPhysics/Jolt/Core/FixedSizeFreeList.h | 120 - .../Jolt/Core/FixedSizeFreeList.inl | 211 -- .../JoltC/JoltPhysics/Jolt/Core/HashCombine.h | 97 - .../JoltPhysics/Jolt/Core/InsertionSort.h | 58 - .../JoltPhysics/Jolt/Core/IssueReporting.cpp | 31 - .../JoltPhysics/Jolt/Core/IssueReporting.h | 38 - .../JoltC/JoltPhysics/Jolt/Core/JobSystem.h | 305 -- .../JoltC/JoltPhysics/Jolt/Core/JobSystem.inl | 56 - .../Jolt/Core/JobSystemSingleThreaded.cpp | 65 - .../Jolt/Core/JobSystemSingleThreaded.h | 62 - .../Jolt/Core/JobSystemThreadPool.cpp | 354 --- .../Jolt/Core/JobSystemThreadPool.h | 92 - .../Jolt/Core/JobSystemWithBarrier.cpp | 227 -- .../Jolt/Core/JobSystemWithBarrier.h | 85 - .../JoltPhysics/Jolt/Core/LinearCurve.cpp | 51 - .../JoltC/JoltPhysics/Jolt/Core/LinearCurve.h | 67 - .../JoltPhysics/Jolt/Core/LockFreeHashMap.h | 182 -- .../JoltPhysics/Jolt/Core/LockFreeHashMap.inl | 351 --- .../JoltC/JoltPhysics/Jolt/Core/Memory.cpp | 74 - .../JoltC/JoltPhysics/Jolt/Core/Memory.h | 55 - .../JoltC/JoltPhysics/Jolt/Core/Mutex.h | 223 -- .../JoltC/JoltPhysics/Jolt/Core/MutexArray.h | 98 - .../JoltC/JoltPhysics/Jolt/Core/NonCopyable.h | 18 - .../JoltC/JoltPhysics/Jolt/Core/Profiler.cpp | 346 -- .../JoltC/JoltPhysics/Jolt/Core/Profiler.h | 284 -- .../JoltC/JoltPhysics/Jolt/Core/Profiler.inl | 89 - .../JoltC/JoltPhysics/Jolt/Core/QuickSort.h | 137 - .../JoltC/JoltPhysics/Jolt/Core/RTTI.cpp | 143 - .../JoltC/JoltPhysics/Jolt/Core/RTTI.h | 436 --- .../JoltC/JoltPhysics/Jolt/Core/Reference.h | 226 -- .../JoltC/JoltPhysics/Jolt/Core/Result.h | 177 -- .../Jolt/Core/STLAlignedAllocator.h | 66 - .../JoltPhysics/Jolt/Core/STLAllocator.h | 102 - .../JoltPhysics/Jolt/Core/STLTempAllocator.h | 77 - .../JoltC/JoltPhysics/Jolt/Core/Semaphore.cpp | 80 - .../JoltC/JoltPhysics/Jolt/Core/Semaphore.h | 51 - .../JoltC/JoltPhysics/Jolt/Core/StaticArray.h | 323 -- .../JoltC/JoltPhysics/Jolt/Core/StreamIn.h | 110 - .../JoltC/JoltPhysics/Jolt/Core/StreamOut.h | 86 - .../JoltC/JoltPhysics/Jolt/Core/StreamUtils.h | 167 - .../JoltPhysics/Jolt/Core/StreamWrapper.h | 53 - .../JoltPhysics/Jolt/Core/StringTools.cpp | 101 - .../JoltC/JoltPhysics/Jolt/Core/StringTools.h | 51 - .../JoltPhysics/Jolt/Core/TempAllocator.h | 121 - .../JoltPhysics/Jolt/Core/TickCounter.cpp | 34 - .../JoltC/JoltPhysics/Jolt/Core/TickCounter.h | 49 - .../JoltPhysics/Jolt/Core/UnorderedMap.h | 15 - .../JoltPhysics/Jolt/Core/UnorderedSet.h | 15 - .../JoltC/JoltPhysics/Jolt/Geometry/AABox.h | 304 -- .../JoltC/JoltPhysics/Jolt/Geometry/AABox4.h | 224 -- .../JoltPhysics/Jolt/Geometry/ClipPoly.h | 200 -- .../JoltPhysics/Jolt/Geometry/ClosestPoint.h | 498 --- .../Jolt/Geometry/ConvexHullBuilder.cpp | 1465 --------- .../Jolt/Geometry/ConvexHullBuilder.h | 276 -- .../Jolt/Geometry/ConvexHullBuilder2D.cpp | 336 -- .../Jolt/Geometry/ConvexHullBuilder2D.h | 105 - .../JoltPhysics/Jolt/Geometry/ConvexSupport.h | 188 -- .../Jolt/Geometry/EPAConvexHullBuilder.h | 844 ----- .../Jolt/Geometry/EPAPenetrationDepth.h | 546 ---- .../JoltC/JoltPhysics/Jolt/Geometry/Ellipse.h | 77 - .../Jolt/Geometry/GJKClosestPoint.h | 952 ------ .../Jolt/Geometry/IndexedTriangle.h | 115 - .../JoltPhysics/Jolt/Geometry/Indexify.cpp | 218 -- .../JoltPhysics/Jolt/Geometry/Indexify.h | 19 - .../JoltPhysics/Jolt/Geometry/MortonCode.h | 40 - .../JoltPhysics/Jolt/Geometry/OrientedBox.cpp | 178 -- .../JoltPhysics/Jolt/Geometry/OrientedBox.h | 39 - .../JoltC/JoltPhysics/Jolt/Geometry/Plane.h | 86 - .../JoltPhysics/Jolt/Geometry/RayAABox.h | 241 -- .../JoltPhysics/Jolt/Geometry/RayAABox8.h | 76 - .../JoltPhysics/Jolt/Geometry/RayCapsule.h | 37 - .../JoltPhysics/Jolt/Geometry/RayCylinder.h | 101 - .../JoltPhysics/Jolt/Geometry/RaySphere.h | 96 - .../JoltPhysics/Jolt/Geometry/RayTriangle.h | 158 - .../JoltPhysics/Jolt/Geometry/RayTriangle8.h | 91 - .../JoltC/JoltPhysics/Jolt/Geometry/Sphere.h | 72 - .../JoltPhysics/Jolt/Geometry/Triangle.h | 34 - .../JoltC/JoltPhysics/Jolt/Jolt.cmake | 634 ---- .../JoltC/JoltPhysics/Jolt/Jolt.h | 16 - .../JoltC/JoltPhysics/Jolt/Jolt.natvis | 86 - .../JoltC/JoltPhysics/Jolt/Math/DMat44.h | 158 - .../JoltC/JoltPhysics/Jolt/Math/DMat44.inl | 310 -- .../JoltC/JoltPhysics/Jolt/Math/DVec3.h | 288 -- .../JoltC/JoltPhysics/Jolt/Math/DVec3.inl | 921 ------ .../JoltC/JoltPhysics/Jolt/Math/Double3.h | 48 - .../JoltC/JoltPhysics/Jolt/Math/DynMatrix.h | 31 - .../Jolt/Math/EigenValueSymmetric.h | 175 -- .../JoltC/JoltPhysics/Jolt/Math/FindRoot.h | 42 - .../JoltC/JoltPhysics/Jolt/Math/Float2.h | 36 - .../JoltC/JoltPhysics/Jolt/Math/Float3.h | 50 - .../JoltC/JoltPhysics/Jolt/Math/Float4.h | 33 - .../Jolt/Math/GaussianElimination.h | 102 - .../JoltC/JoltPhysics/Jolt/Math/HalfFloat.h | 204 -- .../JoltC/JoltPhysics/Jolt/Math/Mat44.h | 243 -- .../JoltC/JoltPhysics/Jolt/Math/Mat44.inl | 952 ------ .../JoltC/JoltPhysics/Jolt/Math/Math.h | 203 -- .../JoltC/JoltPhysics/Jolt/Math/MathTypes.h | 34 - .../JoltC/JoltPhysics/Jolt/Math/Matrix.h | 259 -- .../JoltC/JoltPhysics/Jolt/Math/Quat.h | 255 -- .../JoltC/JoltPhysics/Jolt/Math/Quat.inl | 328 -- .../JoltC/JoltPhysics/Jolt/Math/Real.h | 44 - .../JoltC/JoltPhysics/Jolt/Math/Swizzle.h | 19 - .../JoltPhysics/Jolt/Math/Trigonometry.h | 59 - .../JoltC/JoltPhysics/Jolt/Math/UVec4.h | 220 -- .../JoltC/JoltPhysics/Jolt/Math/UVec4.inl | 573 ---- .../JoltC/JoltPhysics/Jolt/Math/UVec8.h | 100 - .../JoltC/JoltPhysics/Jolt/Math/UVec8.inl | 138 - .../JoltC/JoltPhysics/Jolt/Math/Vec3.cpp | 59 - .../JoltC/JoltPhysics/Jolt/Math/Vec3.h | 294 -- .../JoltC/JoltPhysics/Jolt/Math/Vec3.inl | 845 ----- .../JoltC/JoltPhysics/Jolt/Math/Vec4.h | 283 -- .../JoltC/JoltPhysics/Jolt/Math/Vec4.inl | 970 ------ .../JoltC/JoltPhysics/Jolt/Math/Vec8.h | 112 - .../JoltC/JoltPhysics/Jolt/Math/Vec8.inl | 148 - .../JoltC/JoltPhysics/Jolt/Math/Vector.h | 216 -- .../ObjectStream/GetPrimitiveTypeOfType.h | 54 - .../Jolt/ObjectStream/ObjectStream.cpp | 34 - .../Jolt/ObjectStream/ObjectStream.h | 329 -- .../ObjectStream/ObjectStreamBinaryIn.cpp | 230 -- .../Jolt/ObjectStream/ObjectStreamBinaryIn.h | 51 - .../ObjectStream/ObjectStreamBinaryOut.cpp | 150 - .../Jolt/ObjectStream/ObjectStreamBinaryOut.h | 51 - .../Jolt/ObjectStream/ObjectStreamIn.cpp | 617 ---- .../Jolt/ObjectStream/ObjectStreamIn.h | 144 - .../Jolt/ObjectStream/ObjectStreamOut.cpp | 164 - .../Jolt/ObjectStream/ObjectStreamOut.h | 100 - .../Jolt/ObjectStream/ObjectStreamTextIn.cpp | 392 --- .../Jolt/ObjectStream/ObjectStreamTextIn.h | 49 - .../Jolt/ObjectStream/ObjectStreamTextOut.cpp | 227 -- .../Jolt/ObjectStream/ObjectStreamTextOut.h | 56 - .../Jolt/ObjectStream/ObjectStreamTypes.h | 24 - .../Jolt/ObjectStream/SerializableAttribute.h | 107 - .../ObjectStream/SerializableAttributeEnum.h | 58 - .../ObjectStream/SerializableAttributeTyped.h | 51 - .../Jolt/ObjectStream/SerializableObject.cpp | 15 - .../Jolt/ObjectStream/SerializableObject.h | 155 - .../Jolt/ObjectStream/TypeDeclarations.cpp | 55 - .../Jolt/ObjectStream/TypeDeclarations.h | 41 - .../Jolt/Physics/Body/AllowedDOFs.h | 68 - .../JoltPhysics/Jolt/Physics/Body/Body.cpp | 413 --- .../JoltPhysics/Jolt/Physics/Body/Body.h | 388 --- .../JoltPhysics/Jolt/Physics/Body/Body.inl | 197 -- .../Jolt/Physics/Body/BodyAccess.cpp | 18 - .../Jolt/Physics/Body/BodyAccess.h | 55 - .../Physics/Body/BodyActivationListener.h | 28 - .../Physics/Body/BodyCreationSettings.cpp | 234 -- .../Jolt/Physics/Body/BodyCreationSettings.h | 124 - .../Jolt/Physics/Body/BodyFilter.h | 102 - .../JoltPhysics/Jolt/Physics/Body/BodyID.h | 100 - .../Jolt/Physics/Body/BodyInterface.cpp | 1002 ------ .../Jolt/Physics/Body/BodyInterface.h | 278 -- .../JoltPhysics/Jolt/Physics/Body/BodyLock.h | 111 - .../Jolt/Physics/Body/BodyLockInterface.h | 134 - .../Jolt/Physics/Body/BodyLockMulti.h | 104 - .../Jolt/Physics/Body/BodyManager.cpp | 1149 ------- .../Jolt/Physics/Body/BodyManager.h | 364 --- .../JoltPhysics/Jolt/Physics/Body/BodyPair.h | 36 - .../JoltPhysics/Jolt/Physics/Body/BodyType.h | 19 - .../Jolt/Physics/Body/MassProperties.cpp | 185 -- .../Jolt/Physics/Body/MassProperties.h | 58 - .../Jolt/Physics/Body/MotionProperties.cpp | 90 - .../Jolt/Physics/Body/MotionProperties.h | 278 -- .../Jolt/Physics/Body/MotionProperties.inl | 168 - .../Jolt/Physics/Body/MotionQuality.h | 31 - .../Jolt/Physics/Body/MotionType.h | 17 - .../Jolt/Physics/Character/Character.cpp | 317 -- .../Jolt/Physics/Character/Character.h | 139 - .../Jolt/Physics/Character/CharacterBase.cpp | 59 - .../Jolt/Physics/Character/CharacterBase.h | 154 - .../Physics/Character/CharacterVirtual.cpp | 1512 --------- .../Jolt/Physics/Character/CharacterVirtual.h | 502 --- .../Jolt/Physics/Collision/AABoxCast.h | 20 - .../Jolt/Physics/Collision/ActiveEdgeMode.h | 17 - .../Jolt/Physics/Collision/ActiveEdges.h | 114 - .../Jolt/Physics/Collision/BackFaceMode.h | 16 - .../Collision/BroadPhase/BroadPhase.cpp | 16 - .../Physics/Collision/BroadPhase/BroadPhase.h | 112 - .../BroadPhase/BroadPhaseBruteForce.cpp | 313 -- .../BroadPhase/BroadPhaseBruteForce.h | 38 - .../Collision/BroadPhase/BroadPhaseLayer.h | 148 - .../BroadPhase/BroadPhaseLayerInterfaceMask.h | 92 - .../BroadPhaseLayerInterfaceTable.h | 64 - .../BroadPhase/BroadPhaseQuadTree.cpp | 609 ---- .../Collision/BroadPhase/BroadPhaseQuadTree.h | 108 - .../Collision/BroadPhase/BroadPhaseQuery.h | 53 - .../ObjectVsBroadPhaseLayerFilterMask.h | 35 - .../ObjectVsBroadPhaseLayerFilterTable.h | 66 - .../Physics/Collision/BroadPhase/QuadTree.cpp | 1684 ---------- .../Physics/Collision/BroadPhase/QuadTree.h | 388 --- .../Collision/CastConvexVsTriangles.cpp | 109 - .../Physics/Collision/CastConvexVsTriangles.h | 46 - .../Jolt/Physics/Collision/CastResult.h | 37 - .../Collision/CastSphereVsTriangles.cpp | 223 -- .../Physics/Collision/CastSphereVsTriangles.h | 49 - .../Jolt/Physics/Collision/CollectFacesMode.h | 16 - .../Collision/CollideConvexVsTriangles.cpp | 150 - .../Collision/CollideConvexVsTriangles.h | 56 - .../Physics/Collision/CollidePointResult.h | 25 - .../Jolt/Physics/Collision/CollideShape.h | 105 - .../CollideSoftBodyVerticesVsTriangles.h | 98 - .../Collision/CollideSphereVsTriangles.cpp | 123 - .../Collision/CollideSphereVsTriangles.h | 50 - .../Physics/Collision/CollisionCollector.h | 102 - .../Collision/CollisionCollectorImpl.h | 134 - .../Physics/Collision/CollisionDispatch.cpp | 107 - .../Physics/Collision/CollisionDispatch.h | 97 - .../Jolt/Physics/Collision/CollisionGroup.cpp | 33 - .../Jolt/Physics/Collision/CollisionGroup.h | 94 - .../Jolt/Physics/Collision/ContactListener.h | 114 - .../Collision/EstimateCollisionResponse.cpp | 213 -- .../Collision/EstimateCollisionResponse.h | 48 - .../Jolt/Physics/Collision/GroupFilter.cpp | 32 - .../Jolt/Physics/Collision/GroupFilter.h | 41 - .../Physics/Collision/GroupFilterTable.cpp | 38 - .../Jolt/Physics/Collision/GroupFilterTable.h | 130 - .../Collision/InternalEdgeRemovingCollector.h | 233 -- .../Collision/ManifoldBetweenTwoFaces.cpp | 237 -- .../Collision/ManifoldBetweenTwoFaces.h | 44 - .../Physics/Collision/NarrowPhaseQuery.cpp | 412 --- .../Jolt/Physics/Collision/NarrowPhaseQuery.h | 74 - .../Physics/Collision/NarrowPhaseStats.cpp | 62 - .../Jolt/Physics/Collision/NarrowPhaseStats.h | 110 - .../Jolt/Physics/Collision/ObjectLayer.h | 111 - .../Collision/ObjectLayerPairFilterMask.h | 52 - .../Collision/ObjectLayerPairFilterTable.h | 78 - .../Physics/Collision/PhysicsMaterial.cpp | 35 - .../Jolt/Physics/Collision/PhysicsMaterial.h | 52 - .../Collision/PhysicsMaterialSimple.cpp | 38 - .../Physics/Collision/PhysicsMaterialSimple.h | 37 - .../Jolt/Physics/Collision/RayCast.h | 81 - .../Jolt/Physics/Collision/Shape/BoxShape.cpp | 318 -- .../Jolt/Physics/Collision/Shape/BoxShape.h | 115 - .../Physics/Collision/Shape/CapsuleShape.cpp | 446 --- .../Physics/Collision/Shape/CapsuleShape.h | 128 - .../Physics/Collision/Shape/CompoundShape.cpp | 398 --- .../Physics/Collision/Shape/CompoundShape.h | 344 -- .../Collision/Shape/CompoundShapeVisitors.h | 460 --- .../Collision/Shape/ConvexHullShape.cpp | 1311 -------- .../Physics/Collision/Shape/ConvexHullShape.h | 202 -- .../Physics/Collision/Shape/ConvexShape.cpp | 559 ---- .../Physics/Collision/Shape/ConvexShape.h | 150 - .../Physics/Collision/Shape/CylinderShape.cpp | 417 --- .../Physics/Collision/Shape/CylinderShape.h | 126 - .../Collision/Shape/DecoratedShape.cpp | 87 - .../Physics/Collision/Shape/DecoratedShape.h | 70 - .../Collision/Shape/GetTrianglesContext.h | 244 -- .../Collision/Shape/HeightFieldShape.cpp | 2624 ---------------- .../Collision/Shape/HeightFieldShape.h | 349 --- .../Physics/Collision/Shape/MeshShape.cpp | 1244 -------- .../Jolt/Physics/Collision/Shape/MeshShape.h | 207 -- .../Collision/Shape/MutableCompoundShape.cpp | 560 ---- .../Collision/Shape/MutableCompoundShape.h | 160 - .../Shape/OffsetCenterOfMassShape.cpp | 217 -- .../Collision/Shape/OffsetCenterOfMassShape.h | 143 - .../PolyhedronSubmergedVolumeCalculator.h | 319 -- .../Shape/RotatedTranslatedShape.cpp | 315 -- .../Collision/Shape/RotatedTranslatedShape.h | 158 - .../Physics/Collision/Shape/ScaleHelpers.h | 68 - .../Physics/Collision/Shape/ScaledShape.cpp | 226 -- .../Physics/Collision/Shape/ScaledShape.h | 140 - .../Jolt/Physics/Collision/Shape/Shape.cpp | 309 -- .../Jolt/Physics/Collision/Shape/Shape.h | 447 --- .../Physics/Collision/Shape/SphereShape.cpp | 352 --- .../Physics/Collision/Shape/SphereShape.h | 125 - .../Collision/Shape/StaticCompoundShape.cpp | 675 ---- .../Collision/Shape/StaticCompoundShape.h | 139 - .../Jolt/Physics/Collision/Shape/SubShapeID.h | 138 - .../Physics/Collision/Shape/SubShapeIDPair.h | 80 - .../Collision/Shape/TaperedCapsuleShape.cpp | 458 --- .../Shape/TaperedCapsuleShape.gliffy | 1 - .../Collision/Shape/TaperedCapsuleShape.h | 125 - .../Physics/Collision/Shape/TriangleShape.cpp | 413 --- .../Physics/Collision/Shape/TriangleShape.h | 138 - .../Jolt/Physics/Collision/ShapeCast.h | 170 - .../Jolt/Physics/Collision/ShapeFilter.h | 72 - .../Physics/Collision/SortReverseAndStore.h | 48 - .../Physics/Collision/TransformedShape.cpp | 180 -- .../Jolt/Physics/Collision/TransformedShape.h | 194 -- .../Constraints/CalculateSolverSteps.h | 66 - .../Physics/Constraints/ConeConstraint.cpp | 246 -- .../Jolt/Physics/Constraints/ConeConstraint.h | 133 - .../Jolt/Physics/Constraints/Constraint.cpp | 73 - .../Jolt/Physics/Constraints/Constraint.h | 238 -- .../Physics/Constraints/ConstraintManager.cpp | 289 -- .../Physics/Constraints/ConstraintManager.h | 99 - .../ConstraintPart/AngleConstraintPart.h | 257 -- .../ConstraintPart/AxisConstraintPart.h | 682 ---- .../ConstraintPart/DualAxisConstraintPart.h | 276 -- .../ConstraintPart/GearConstraintPart.h | 195 -- .../HingeRotationConstraintPart.h | 222 -- .../IndependentAxisConstraintPart.h | 246 -- .../ConstraintPart/PointConstraintPart.h | 239 -- .../RackAndPinionConstraintPart.h | 196 -- .../RotationEulerConstraintPart.h | 270 -- .../RotationQuatConstraintPart.h | 246 -- .../Constraints/ConstraintPart/SpringPart.h | 169 - .../ConstraintPart/SwingTwistConstraintPart.h | 597 ---- .../Constraints/ContactConstraintManager.cpp | 1718 ---------- .../Constraints/ContactConstraintManager.h | 513 --- .../Constraints/DistanceConstraint.cpp | 266 -- .../Physics/Constraints/DistanceConstraint.h | 120 - .../Physics/Constraints/FixedConstraint.cpp | 215 -- .../Physics/Constraints/FixedConstraint.h | 96 - .../Physics/Constraints/GearConstraint.cpp | 188 -- .../Jolt/Physics/Constraints/GearConstraint.h | 116 - .../Physics/Constraints/HingeConstraint.cpp | 424 --- .../Physics/Constraints/HingeConstraint.h | 182 -- .../Physics/Constraints/MotorSettings.cpp | 43 - .../Jolt/Physics/Constraints/MotorSettings.h | 66 - .../Physics/Constraints/PathConstraint.cpp | 458 --- .../Jolt/Physics/Constraints/PathConstraint.h | 186 -- .../Constraints/PathConstraintPath.cpp | 85 - .../Physics/Constraints/PathConstraintPath.h | 71 - .../Constraints/PathConstraintPathHermite.cpp | 308 -- .../Constraints/PathConstraintPathHermite.h | 54 - .../Physics/Constraints/PointConstraint.cpp | 157 - .../Physics/Constraints/PointConstraint.h | 94 - .../Physics/Constraints/PulleyConstraint.cpp | 253 -- .../Physics/Constraints/PulleyConstraint.h | 137 - .../Constraints/RackAndPinionConstraint.cpp | 189 -- .../Constraints/RackAndPinionConstraint.h | 118 - .../Physics/Constraints/SixDOFConstraint.cpp | 900 ------ .../Physics/Constraints/SixDOFConstraint.h | 289 -- .../Physics/Constraints/SliderConstraint.cpp | 501 --- .../Physics/Constraints/SliderConstraint.h | 198 -- .../Physics/Constraints/SpringSettings.cpp | 35 - .../Jolt/Physics/Constraints/SpringSettings.h | 70 - .../Constraints/SwingTwistConstraint.cpp | 524 ---- .../Constraints/SwingTwistConstraint.h | 197 -- .../Physics/Constraints/TwoBodyConstraint.cpp | 56 - .../Physics/Constraints/TwoBodyConstraint.h | 65 - .../Jolt/Physics/DeterminismLog.cpp | 17 - .../JoltPhysics/Jolt/Physics/DeterminismLog.h | 159 - .../JoltPhysics/Jolt/Physics/EActivation.h | 16 - .../Jolt/Physics/EPhysicsUpdateError.h | 37 - .../Jolt/Physics/IslandBuilder.cpp | 484 --- .../JoltPhysics/Jolt/Physics/IslandBuilder.h | 125 - .../Jolt/Physics/LargeIslandSplitter.cpp | 579 ---- .../Jolt/Physics/LargeIslandSplitter.h | 185 -- .../JoltPhysics/Jolt/Physics/PhysicsLock.cpp | 17 - .../JoltPhysics/Jolt/Physics/PhysicsLock.h | 169 - .../JoltPhysics/Jolt/Physics/PhysicsScene.cpp | 261 -- .../JoltPhysics/Jolt/Physics/PhysicsScene.h | 104 - .../Jolt/Physics/PhysicsSettings.h | 119 - .../Jolt/Physics/PhysicsStepListener.h | 27 - .../Jolt/Physics/PhysicsSystem.cpp | 2703 ---------------- .../JoltPhysics/Jolt/Physics/PhysicsSystem.h | 320 -- .../Jolt/Physics/PhysicsUpdateContext.cpp | 23 - .../Jolt/Physics/PhysicsUpdateContext.h | 172 - .../Jolt/Physics/Ragdoll/Ragdoll.cpp | 699 ----- .../Jolt/Physics/Ragdoll/Ragdoll.h | 240 -- .../SoftBody/SoftBodyContactListener.h | 55 - .../SoftBody/SoftBodyCreationSettings.cpp | 122 - .../SoftBody/SoftBodyCreationSettings.h | 73 - .../Jolt/Physics/SoftBody/SoftBodyManifold.h | 59 - .../SoftBody/SoftBodyMotionProperties.cpp | 1089 ------- .../SoftBody/SoftBodyMotionProperties.h | 254 -- .../Jolt/Physics/SoftBody/SoftBodyShape.cpp | 338 -- .../Jolt/Physics/SoftBody/SoftBodyShape.h | 73 - .../SoftBody/SoftBodySharedSettings.cpp | 698 ----- .../Physics/SoftBody/SoftBodySharedSettings.h | 322 -- .../Physics/SoftBody/SoftBodyUpdateContext.h | 67 - .../Jolt/Physics/SoftBody/SoftBodyVertex.h | 28 - .../JoltPhysics/Jolt/Physics/StateRecorder.h | 66 - .../Jolt/Physics/StateRecorderImpl.cpp | 90 - .../Jolt/Physics/StateRecorderImpl.h | 47 - .../Physics/Vehicle/MotorcycleController.cpp | 293 -- .../Physics/Vehicle/MotorcycleController.h | 116 - .../Vehicle/TrackedVehicleController.cpp | 531 ---- .../Vehicle/TrackedVehicleController.h | 166 - .../Physics/Vehicle/VehicleAntiRollBar.cpp | 33 - .../Jolt/Physics/Vehicle/VehicleAntiRollBar.h | 31 - .../Vehicle/VehicleCollisionTester.cpp | 376 --- .../Physics/Vehicle/VehicleCollisionTester.h | 146 - .../Physics/Vehicle/VehicleConstraint.cpp | 681 ---- .../Jolt/Physics/Vehicle/VehicleConstraint.h | 236 -- .../Physics/Vehicle/VehicleController.cpp | 17 - .../Jolt/Physics/Vehicle/VehicleController.h | 80 - .../Physics/Vehicle/VehicleDifferential.cpp | 81 - .../Physics/Vehicle/VehicleDifferential.h | 39 - .../Jolt/Physics/Vehicle/VehicleEngine.cpp | 122 - .../Jolt/Physics/Vehicle/VehicleEngine.h | 93 - .../Jolt/Physics/Vehicle/VehicleTrack.cpp | 52 - .../Jolt/Physics/Vehicle/VehicleTrack.h | 56 - .../Physics/Vehicle/VehicleTransmission.cpp | 159 - .../Physics/Vehicle/VehicleTransmission.h | 87 - .../Jolt/Physics/Vehicle/Wheel.cpp | 93 - .../JoltPhysics/Jolt/Physics/Vehicle/Wheel.h | 148 - .../Vehicle/WheeledVehicleController.cpp | 845 ----- .../Vehicle/WheeledVehicleController.h | 199 -- .../JoltC/JoltPhysics/Jolt/RegisterTypes.cpp | 197 -- .../JoltC/JoltPhysics/Jolt/RegisterTypes.h | 29 - .../Jolt/Renderer/DebugRenderer.cpp | 1071 ------- .../JoltPhysics/Jolt/Renderer/DebugRenderer.h | 347 --- .../Jolt/Renderer/DebugRendererPlayback.cpp | 168 - .../Jolt/Renderer/DebugRendererPlayback.h | 48 - .../Jolt/Renderer/DebugRendererRecorder.cpp | 158 - .../Jolt/Renderer/DebugRendererRecorder.h | 130 - .../Jolt/Renderer/DebugRendererSimple.cpp | 80 - .../Jolt/Renderer/DebugRendererSimple.h | 88 - .../Jolt/Skeleton/SkeletalAnimation.cpp | 110 - .../Jolt/Skeleton/SkeletalAnimation.h | 77 - .../JoltPhysics/Jolt/Skeleton/Skeleton.cpp | 82 - .../JoltPhysics/Jolt/Skeleton/Skeleton.h | 72 - .../Jolt/Skeleton/SkeletonMapper.cpp | 237 -- .../Jolt/Skeleton/SkeletonMapper.h | 145 - .../Jolt/Skeleton/SkeletonPose.cpp | 87 - .../JoltPhysics/Jolt/Skeleton/SkeletonPose.h | 82 - .../Jolt/TriangleGrouper/TriangleGrouper.h | 27 - .../TriangleGrouperClosestCentroid.cpp | 95 - .../TriangleGrouperClosestCentroid.h | 21 - .../TriangleGrouper/TriangleGrouperMorton.cpp | 49 - .../TriangleGrouper/TriangleGrouperMorton.h | 20 - .../TriangleSplitter/TriangleSplitter.cpp | 67 - .../Jolt/TriangleSplitter/TriangleSplitter.h | 84 - .../TriangleSplitterBinning.cpp | 112 - .../TriangleSplitterBinning.h | 52 - .../TriangleSplitterFixedLeafSize.cpp | 170 - .../TriangleSplitterFixedLeafSize.h | 55 - .../TriangleSplitterLongestAxis.cpp | 31 - .../TriangleSplitterLongestAxis.h | 28 - .../TriangleSplitter/TriangleSplitterMean.cpp | 40 - .../TriangleSplitter/TriangleSplitterMean.h | 28 - .../TriangleSplitterMorton.cpp | 63 - .../TriangleSplitter/TriangleSplitterMorton.h | 32 - .../JoltPhysics/JoltViewer/JoltViewer.cmake | 20 - .../JoltPhysics/JoltViewer/JoltViewer.cpp | 154 - .../JoltC/JoltPhysics/JoltViewer/JoltViewer.h | 46 - .../JoltC/JoltPhysics/LICENSE | 7 - .../PerformanceTest/ConvexVsMeshScene.h | 122 - .../JoltPhysics/PerformanceTest/Layers.h | 100 - .../PerformanceTest/PerformanceTest.cmake | 16 - .../PerformanceTest/PerformanceTest.cpp | 464 --- .../PerformanceTest/PerformanceTestScene.h | 25 - .../PerformanceTest/PyramidScene.h | 48 - .../PerformanceTest/RagdollScene.h | 140 - .../JoltC/JoltPhysics/README.md | 159 - .../JoltPhysics/WebIncludes/profile_chart.css | 36 - .../JoltPhysics/WebIncludes/profile_chart.js | 285 -- .../JoltC/JoltPhysics/run_doxygen.bat | 4 - .../JoltPhysics/sonar-project.properties | 14 - crates/joltc-sys-patched/JoltC/LICENSE-APACHE | 201 -- crates/joltc-sys-patched/JoltC/LICENSE-MIT | 21 - crates/joltc-sys-patched/JoltC/README.md | 45 - .../joltc-sys-patched/JoltC/test-all-flags.sh | 16 - crates/joltc-sys-patched/README.md | 10 - crates/joltc-sys-patched/build.rs | 166 - crates/joltc-sys-patched/src/generated.rs | 5 - crates/joltc-sys-patched/src/lib.rs | 40 - .../joltc-sys-patched/tests/framework/mod.rs | 206 -- crates/joltc-sys-patched/tests/smoke_test.rs | 246 -- 563 files changed, 2 insertions(+), 107134 deletions(-) delete mode 100644 crates/joltc-sys-patched/.cargo-ok delete mode 100644 crates/joltc-sys-patched/.cargo_vcs_info.json delete mode 100644 crates/joltc-sys-patched/.gitignore delete mode 100644 crates/joltc-sys-patched/Cargo.toml delete mode 100644 crates/joltc-sys-patched/Cargo.toml.orig delete mode 100644 crates/joltc-sys-patched/JoltC/.editorconfig delete mode 100644 crates/joltc-sys-patched/JoltC/.github/workflows/ci.yml delete mode 100644 crates/joltc-sys-patched/JoltC/.gitignore delete mode 100644 crates/joltc-sys-patched/JoltC/.gitmodules delete mode 100644 crates/joltc-sys-patched/JoltC/CMakeLists.txt delete mode 100644 crates/joltc-sys-patched/JoltC/HelloWorld/README.md delete mode 100644 crates/joltc-sys-patched/JoltC/HelloWorld/main.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltC/Enums.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltC/Functions.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltC/JoltC.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltC/JoltC.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltC/Test.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltC/Vehicle.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltC/Vehicle.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/.clang-format delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/.editorconfig delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/.gitattributes delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/.github/dependabot.yml delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/.github/workflows/build.yml delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/.github/workflows/determinism_check.yml delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/.github/workflows/doxygen.yml delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/.github/workflows/sonar-cloud.yml delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/.gitignore delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Build/.gitignore delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/.gitignore delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/PerformanceTest/build.gradle delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/PerformanceTest/src/main/AndroidManifest.xml delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/PerformanceTest/src/main/cpp/CMakeLists.txt delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/UnitTests/build.gradle delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/UnitTests/src/main/AndroidManifest.xml delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/UnitTests/src/main/cpp/CMakeLists.txt delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/build.gradle delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/gradle.properties delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/gradle/wrapper/gradle-wrapper.jar delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/gradle/wrapper/gradle-wrapper.properties delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/gradlew delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/gradlew.bat delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/settings.gradle delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Build/CMakeLists.txt delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Build/README.md delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_linux_clang_gcc.sh delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_linux_mingw.sh delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2019_cl.bat delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2019_cl_arm.bat delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2019_cl_arm_32bit.bat delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2019_clang.bat delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_cl.bat delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_cl_32bit.bat delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_cl_arm.bat delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_cl_arm_32bit.bat delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_cl_cross_platform_deterministic.bat delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_cl_double.bat delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_clang.bat delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_clang_double.bat delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_uwp.bat delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_uwp_arm.bat delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_windows_mingw.sh delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_xcode_ios.sh delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_xcode_macos.sh delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Build/iOS/UnitTestsInfo.plist delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/ContributorAgreement.md delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/APIChanges.md delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Architecture.md delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Images/ActiveEdge.jpg delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Images/ActiveVsInactiveContactNormal.jpg delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Images/ConvexRadius.jpg delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Images/EllipsoidAABB.png delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Images/GhostCollision.jpg delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Images/LongAndThin.jpg delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Images/MotionQuality.jpg delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Images/MotorDamping.jpg delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Images/MotorFrequency.jpg delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Images/QuadTreeExample.png delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Images/ShapeCenterOfMass.jpg delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Images/SimulationIsland.jpg delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Images/SoftBodySkinnedConstraint.jpg delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Logo.png delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/LogoDark.png delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/LogoSmall.png delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/PerformanceTest.md delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/PhysicsSystemUpdate.drawio delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/PhysicsSystemUpdate.svg delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/ProjectsUsingJolt.md delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/ReleaseNotes.md delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Samples.md delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/SwingTwistConstraint.png delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Doxyfile delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/HelloWorld/HelloWorld.cmake delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/HelloWorld/HelloWorld.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/AABBTree/AABBTreeBuilder.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/AABBTree/AABBTreeBuilder.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/AABBTree/AABBTreeToBuffer.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/AABBTree/NodeCodec/NodeCodecQuadTreeHalfFloat.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/AABBTree/TriangleCodec/TriangleCodecIndexed8BitPackSOA4Flags.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ConfigurationString.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/ARMNeon.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Atomics.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/ByteBuffer.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Color.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Color.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Core.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/FPControlWord.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/FPException.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/FPFlushDenormals.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Factory.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Factory.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/FixedSizeFreeList.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/FixedSizeFreeList.inl delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/HashCombine.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/InsertionSort.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/IssueReporting.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/IssueReporting.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/JobSystem.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/JobSystem.inl delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/JobSystemSingleThreaded.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/JobSystemSingleThreaded.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/JobSystemThreadPool.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/JobSystemThreadPool.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/JobSystemWithBarrier.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/JobSystemWithBarrier.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/LinearCurve.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/LinearCurve.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/LockFreeHashMap.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/LockFreeHashMap.inl delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Memory.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Memory.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Mutex.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/MutexArray.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/NonCopyable.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Profiler.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Profiler.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Profiler.inl delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/QuickSort.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/RTTI.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/RTTI.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Reference.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Result.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/STLAlignedAllocator.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/STLAllocator.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/STLTempAllocator.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Semaphore.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Semaphore.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/StaticArray.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/StreamIn.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/StreamOut.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/StreamUtils.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/StreamWrapper.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/StringTools.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/StringTools.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/TempAllocator.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/TickCounter.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/TickCounter.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/UnorderedMap.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/UnorderedSet.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/AABox.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/AABox4.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/ClipPoly.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/ClosestPoint.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/ConvexHullBuilder.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/ConvexHullBuilder.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/ConvexHullBuilder2D.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/ConvexHullBuilder2D.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/ConvexSupport.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/EPAConvexHullBuilder.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/EPAPenetrationDepth.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/Ellipse.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/GJKClosestPoint.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/IndexedTriangle.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/Indexify.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/Indexify.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/MortonCode.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/OrientedBox.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/OrientedBox.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/Plane.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/RayAABox.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/RayAABox8.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/RayCapsule.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/RayCylinder.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/RaySphere.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/RayTriangle.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/RayTriangle8.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/Sphere.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/Triangle.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Jolt.cmake delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Jolt.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Jolt.natvis delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/DMat44.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/DMat44.inl delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/DVec3.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/DVec3.inl delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Double3.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/DynMatrix.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/EigenValueSymmetric.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/FindRoot.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Float2.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Float3.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Float4.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/GaussianElimination.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/HalfFloat.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Mat44.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Mat44.inl delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Math.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/MathTypes.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Matrix.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Quat.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Quat.inl delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Real.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Swizzle.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Trigonometry.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/UVec4.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/UVec4.inl delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/UVec8.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/UVec8.inl delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Vec3.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Vec3.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Vec3.inl delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Vec4.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Vec4.inl delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Vec8.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Vec8.inl delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Vector.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/GetPrimitiveTypeOfType.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStream.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStream.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamBinaryIn.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamBinaryIn.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamBinaryOut.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamBinaryOut.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamIn.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamIn.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamOut.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamOut.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamTextIn.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamTextIn.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamTextOut.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamTextOut.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamTypes.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/SerializableAttribute.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/SerializableAttributeEnum.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/SerializableAttributeTyped.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/SerializableObject.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/SerializableObject.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/TypeDeclarations.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/TypeDeclarations.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/AllowedDOFs.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/Body.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/Body.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/Body.inl delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyAccess.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyAccess.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyActivationListener.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyCreationSettings.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyCreationSettings.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyFilter.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyID.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyInterface.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyInterface.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyLock.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyLockInterface.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyLockMulti.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyManager.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyManager.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyPair.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyType.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/MassProperties.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/MassProperties.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/MotionProperties.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/MotionProperties.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/MotionProperties.inl delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/MotionQuality.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/MotionType.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Character/Character.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Character/Character.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Character/CharacterBase.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Character/CharacterBase.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Character/CharacterVirtual.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Character/CharacterVirtual.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/AABoxCast.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ActiveEdgeMode.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ActiveEdges.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BackFaceMode.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhase.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhase.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhaseBruteForce.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhaseBruteForce.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhaseLayer.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhaseLayerInterfaceMask.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhaseLayerInterfaceTable.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhaseQuadTree.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhaseQuadTree.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhaseQuery.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/ObjectVsBroadPhaseLayerFilterMask.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/ObjectVsBroadPhaseLayerFilterTable.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/QuadTree.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/QuadTree.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CastConvexVsTriangles.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CastConvexVsTriangles.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CastResult.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CastSphereVsTriangles.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CastSphereVsTriangles.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollectFacesMode.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollideConvexVsTriangles.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollideConvexVsTriangles.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollidePointResult.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollideShape.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollideSoftBodyVerticesVsTriangles.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollideSphereVsTriangles.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollideSphereVsTriangles.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollisionCollector.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollisionCollectorImpl.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollisionDispatch.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollisionDispatch.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollisionGroup.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollisionGroup.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ContactListener.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/EstimateCollisionResponse.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/EstimateCollisionResponse.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/GroupFilter.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/GroupFilter.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/GroupFilterTable.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/GroupFilterTable.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/InternalEdgeRemovingCollector.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ManifoldBetweenTwoFaces.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ManifoldBetweenTwoFaces.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/NarrowPhaseQuery.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/NarrowPhaseQuery.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/NarrowPhaseStats.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/NarrowPhaseStats.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ObjectLayer.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ObjectLayerPairFilterMask.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ObjectLayerPairFilterTable.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/PhysicsMaterial.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/PhysicsMaterial.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/PhysicsMaterialSimple.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/PhysicsMaterialSimple.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/RayCast.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/BoxShape.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/BoxShape.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/CapsuleShape.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/CapsuleShape.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/CompoundShape.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/CompoundShape.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/CompoundShapeVisitors.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/ConvexHullShape.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/ConvexHullShape.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/ConvexShape.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/ConvexShape.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/CylinderShape.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/CylinderShape.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/DecoratedShape.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/DecoratedShape.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/GetTrianglesContext.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/HeightFieldShape.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/HeightFieldShape.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/MeshShape.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/MeshShape.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/MutableCompoundShape.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/MutableCompoundShape.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/OffsetCenterOfMassShape.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/OffsetCenterOfMassShape.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/PolyhedronSubmergedVolumeCalculator.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/RotatedTranslatedShape.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/RotatedTranslatedShape.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/ScaleHelpers.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/ScaledShape.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/ScaledShape.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/Shape.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/Shape.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/SphereShape.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/SphereShape.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/StaticCompoundShape.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/StaticCompoundShape.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/SubShapeID.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/SubShapeIDPair.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/TaperedCapsuleShape.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/TaperedCapsuleShape.gliffy delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/TaperedCapsuleShape.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/TriangleShape.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/TriangleShape.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ShapeCast.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ShapeFilter.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/SortReverseAndStore.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/TransformedShape.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/TransformedShape.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/CalculateSolverSteps.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConeConstraint.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConeConstraint.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/Constraint.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/Constraint.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintManager.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintManager.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/AngleConstraintPart.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/AxisConstraintPart.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/DualAxisConstraintPart.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/GearConstraintPart.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/HingeRotationConstraintPart.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/IndependentAxisConstraintPart.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/PointConstraintPart.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/RackAndPinionConstraintPart.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/RotationEulerConstraintPart.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/RotationQuatConstraintPart.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/SpringPart.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/SwingTwistConstraintPart.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ContactConstraintManager.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ContactConstraintManager.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/DistanceConstraint.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/DistanceConstraint.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/FixedConstraint.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/FixedConstraint.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/GearConstraint.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/GearConstraint.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/HingeConstraint.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/HingeConstraint.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/MotorSettings.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/MotorSettings.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PathConstraint.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PathConstraint.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PathConstraintPath.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PathConstraintPath.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PathConstraintPathHermite.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PathConstraintPathHermite.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PointConstraint.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PointConstraint.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PulleyConstraint.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PulleyConstraint.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/RackAndPinionConstraint.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/RackAndPinionConstraint.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/SixDOFConstraint.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/SixDOFConstraint.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/SliderConstraint.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/SliderConstraint.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/SpringSettings.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/SpringSettings.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/SwingTwistConstraint.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/SwingTwistConstraint.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/TwoBodyConstraint.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/TwoBodyConstraint.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/DeterminismLog.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/DeterminismLog.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/EActivation.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/EPhysicsUpdateError.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/IslandBuilder.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/IslandBuilder.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/LargeIslandSplitter.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/LargeIslandSplitter.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsLock.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsLock.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsScene.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsScene.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsSettings.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsStepListener.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsSystem.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsSystem.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsUpdateContext.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsUpdateContext.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Ragdoll/Ragdoll.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Ragdoll/Ragdoll.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyContactListener.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyCreationSettings.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyCreationSettings.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyManifold.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyMotionProperties.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyMotionProperties.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyShape.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyShape.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodySharedSettings.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodySharedSettings.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyUpdateContext.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyVertex.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/StateRecorder.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/StateRecorderImpl.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/StateRecorderImpl.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/MotorcycleController.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/MotorcycleController.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/TrackedVehicleController.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/TrackedVehicleController.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleAntiRollBar.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleAntiRollBar.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleCollisionTester.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleCollisionTester.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleConstraint.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleConstraint.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleController.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleController.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleDifferential.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleDifferential.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleEngine.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleEngine.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleTrack.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleTrack.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleTransmission.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleTransmission.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/Wheel.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/Wheel.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/WheeledVehicleController.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/WheeledVehicleController.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/RegisterTypes.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/RegisterTypes.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Renderer/DebugRenderer.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Renderer/DebugRenderer.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Renderer/DebugRendererPlayback.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Renderer/DebugRendererPlayback.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Renderer/DebugRendererRecorder.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Renderer/DebugRendererRecorder.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Renderer/DebugRendererSimple.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Renderer/DebugRendererSimple.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Skeleton/SkeletalAnimation.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Skeleton/SkeletalAnimation.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Skeleton/Skeleton.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Skeleton/Skeleton.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Skeleton/SkeletonMapper.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Skeleton/SkeletonMapper.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Skeleton/SkeletonPose.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Skeleton/SkeletonPose.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleGrouper/TriangleGrouper.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleGrouper/TriangleGrouperClosestCentroid.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleGrouper/TriangleGrouperClosestCentroid.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleGrouper/TriangleGrouperMorton.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleGrouper/TriangleGrouperMorton.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitter.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitter.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterBinning.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterBinning.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterFixedLeafSize.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterFixedLeafSize.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterLongestAxis.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterLongestAxis.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterMean.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterMean.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterMorton.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterMorton.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/JoltViewer/JoltViewer.cmake delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/JoltViewer/JoltViewer.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/JoltViewer/JoltViewer.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/LICENSE delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/PerformanceTest/ConvexVsMeshScene.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/PerformanceTest/Layers.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/PerformanceTest/PerformanceTest.cmake delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/PerformanceTest/PerformanceTest.cpp delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/PerformanceTest/PerformanceTestScene.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/PerformanceTest/PyramidScene.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/PerformanceTest/RagdollScene.h delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/README.md delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/WebIncludes/profile_chart.css delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/WebIncludes/profile_chart.js delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/run_doxygen.bat delete mode 100644 crates/joltc-sys-patched/JoltC/JoltPhysics/sonar-project.properties delete mode 100644 crates/joltc-sys-patched/JoltC/LICENSE-APACHE delete mode 100644 crates/joltc-sys-patched/JoltC/LICENSE-MIT delete mode 100644 crates/joltc-sys-patched/JoltC/README.md delete mode 100644 crates/joltc-sys-patched/JoltC/test-all-flags.sh delete mode 100644 crates/joltc-sys-patched/README.md delete mode 100644 crates/joltc-sys-patched/build.rs delete mode 100644 crates/joltc-sys-patched/src/generated.rs delete mode 100644 crates/joltc-sys-patched/src/lib.rs delete mode 100644 crates/joltc-sys-patched/tests/framework/mod.rs delete mode 100644 crates/joltc-sys-patched/tests/smoke_test.rs diff --git a/Cargo.toml b/Cargo.toml index 84b41fa6417..60def7c063e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -351,8 +351,8 @@ jemalloc_pprof = { version = "0.8", features = ["symbolize", "flamegraph"] } zstd-framed = { version = "0.1.1", features = ["tokio"] } # Physics -rolt = "0.3" -joltc-sys = "0.3" +rolt = { git = "https://github.com/pbisogno/jolt-rust", branch = "feature/spacetimedb" } +joltc-sys = { git = "https://github.com/pbisogno/jolt-rust", branch = "feature/spacetimedb" } glam = "0.29" # Vendor the openssl we rely on, rather than depend on a @@ -405,7 +405,3 @@ result_large_err = "allow" # is running against a file that doesn't have a name in it. crane.name = "spacetimedb" -[patch.crates-io] -# Patched to use static CRT (/MT) on Windows when crt-static is enabled, -# fixing RuntimeLibrary mismatch with SpacetimeDB's linker config. -joltc-sys = { path = "crates/joltc-sys-patched" } diff --git a/crates/joltc-sys-patched/.cargo-ok b/crates/joltc-sys-patched/.cargo-ok deleted file mode 100644 index 5f8b795830a..00000000000 --- a/crates/joltc-sys-patched/.cargo-ok +++ /dev/null @@ -1 +0,0 @@ -{"v":1} \ No newline at end of file diff --git a/crates/joltc-sys-patched/.cargo_vcs_info.json b/crates/joltc-sys-patched/.cargo_vcs_info.json deleted file mode 100644 index 4fde47e80d8..00000000000 --- a/crates/joltc-sys-patched/.cargo_vcs_info.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "git": { - "sha1": "dd8c14371c08f3123c49f93b4f37f85896ac6c50" - }, - "path_in_vcs": "crates/joltc-sys" -} \ No newline at end of file diff --git a/crates/joltc-sys-patched/.gitignore b/crates/joltc-sys-patched/.gitignore deleted file mode 100644 index ea8c4bf7f35..00000000000 --- a/crates/joltc-sys-patched/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/target diff --git a/crates/joltc-sys-patched/Cargo.toml b/crates/joltc-sys-patched/Cargo.toml deleted file mode 100644 index dc8d3ede9d4..00000000000 --- a/crates/joltc-sys-patched/Cargo.toml +++ /dev/null @@ -1,51 +0,0 @@ -# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO -# -# When uploading crates to the registry Cargo will automatically -# "normalize" Cargo.toml files for maximal compatibility -# with all versions of Cargo and also rewrite `path` dependencies -# to registry (e.g., crates.io) dependencies. -# -# If you are reading this file be aware that the original Cargo.toml -# will likely look very different (and much more reasonable). -# See Cargo.toml.orig for the original contents. - -[package] -edition = "2021" -name = "joltc-sys" -version = "0.3.1+Jolt-5.0.0" -exclude = [ - "JoltC/JoltPhysics/Assets", - "JoltC/JoltPhysics/TestFramework", - "JoltC/JoltPhysics/Samples", - "JoltC/JoltPhysics/UnitTests", -] -description = "Unsafe bindings to Jolt Physics using JoltC" -readme = "README.md" -license = "MIT OR Apache-2.0" -repository = "https://github.com/SecondHalfGames/jolt-rust" - -[dependencies] - -[dev-dependencies.rand] -version = "0.8.5" - -[build-dependencies.anyhow] -version = "1.0.69" - -[build-dependencies.bindgen] -version = "0.69.1" - -[build-dependencies.cc] -version = "1.0.94" -features = ["parallel"] - -[build-dependencies.cmake] -version = "0.1.50" - -[build-dependencies.walkdir] -version = "2.5.0" - -[features] -default = [] -double-precision = [] -object-layer-u32 = [] diff --git a/crates/joltc-sys-patched/Cargo.toml.orig b/crates/joltc-sys-patched/Cargo.toml.orig deleted file mode 100644 index 9a595d38375..00000000000 --- a/crates/joltc-sys-patched/Cargo.toml.orig +++ /dev/null @@ -1,30 +0,0 @@ -[package] -name = "joltc-sys" -description = "Unsafe bindings to Jolt Physics using JoltC" -version = "0.3.1+Jolt-5.0.0" -license = "MIT OR Apache-2.0" -repository = "https://github.com/SecondHalfGames/jolt-rust" -edition = "2021" - -exclude = ["JoltC/JoltPhysics/Assets", "JoltC/JoltPhysics/TestFramework", "JoltC/JoltPhysics/Samples", "JoltC/JoltPhysics/UnitTests"] - -[features] -default = [] - -# Changes all world coordinates to use f64 instead of 32 coordinates. -double-precision = [] - -# Changes ObjectLayer and related types to be u32 instead of u16. -object-layer-u32 = [] - -[dependencies] - -[build-dependencies] -bindgen = "0.69.1" -cmake = "0.1.50" -cc = { version = "1.0.94", features = ["parallel"] } -walkdir = "2.5.0" -anyhow = "1.0.69" - -[dev-dependencies] -rand = "0.8.5" diff --git a/crates/joltc-sys-patched/JoltC/.editorconfig b/crates/joltc-sys-patched/JoltC/.editorconfig deleted file mode 100644 index 9e1c34d0095..00000000000 --- a/crates/joltc-sys-patched/JoltC/.editorconfig +++ /dev/null @@ -1,6 +0,0 @@ -root = true - -[*.{c,cpp,h}] -charset = utf-8 -indent_style = tab -trim_trailing_whitespace = true \ No newline at end of file diff --git a/crates/joltc-sys-patched/JoltC/.github/workflows/ci.yml b/crates/joltc-sys-patched/JoltC/.github/workflows/ci.yml deleted file mode 100644 index a5180e2e132..00000000000 --- a/crates/joltc-sys-patched/JoltC/.github/workflows/ci.yml +++ /dev/null @@ -1,54 +0,0 @@ -name: Test Suite - -on: - push: - branches: - - main - - pull_request: - branches: - - main - -jobs: - test-suite: - strategy: - fail-fast: false - matrix: - os: [ubuntu-latest, windows-latest] - build_type: [Debug, Release] - build_flags: ['', '-DDOUBLE_PRECISION=ON', '-DOBJECT_LAYER_BITS=32'] - exclude: - - os: ubuntu-latest - build_type: Release - - runs-on: ${{ matrix.os }} - name: Tests - ${{ matrix.os }} ${{ matrix.build_type }} ${{ matrix.build_flags }} - - steps: - - uses: actions/checkout@v4 - with: - submodules: true - - - name: Setup C++ Tooling - uses: aminya/setup-cpp@v1 - with: - compiler: ${{ contains(matrix.os, 'windows') && 'msvc' || 'clang' }} - cmake: true - - - name: Configure CMake - run: cmake -B build ${{ matrix.build_flags }} -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} - - - name: Build - run: cmake --build build --config=${{ matrix.build_type }} - - - name: Run Hello World - shell: bash - run: | - ./build/${{ matrix.build_type }}/HelloWorld - if: ${{ contains(matrix.os, 'windows') }} - - - name: Run Hello World - shell: bash - run: | - ./build/HelloWorld - if: ${{ !contains(matrix.os, 'windows') }} diff --git a/crates/joltc-sys-patched/JoltC/.gitignore b/crates/joltc-sys-patched/JoltC/.gitignore deleted file mode 100644 index 28ed25ce552..00000000000 --- a/crates/joltc-sys-patched/JoltC/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build* \ No newline at end of file diff --git a/crates/joltc-sys-patched/JoltC/.gitmodules b/crates/joltc-sys-patched/JoltC/.gitmodules deleted file mode 100644 index 1024921b1d7..00000000000 --- a/crates/joltc-sys-patched/JoltC/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "JoltPhysics"] - path = JoltPhysics - url = ../../jrouwe/JoltPhysics.git diff --git a/crates/joltc-sys-patched/JoltC/CMakeLists.txt b/crates/joltc-sys-patched/JoltC/CMakeLists.txt deleted file mode 100644 index 82a64066ead..00000000000 --- a/crates/joltc-sys-patched/JoltC/CMakeLists.txt +++ /dev/null @@ -1,76 +0,0 @@ -cmake_minimum_required(VERSION 3.16 FATAL_ERROR) - -# When turning this option on, the library will be compiled using doubles for positions. This allows for much bigger worlds. -option(DOUBLE_PRECISION "Use double precision math" OFF) - -# Number of bits to use in ObjectLayer. Can be 16 or 32. -option(OBJECT_LAYER_BITS "Number of bits in ObjectLayer" 16) - -include(CMakeDependentOption) - -# Ability to toggle between the static and DLL versions of the MSVC runtime library -# Windows Store only supports the DLL version -cmake_dependent_option(USE_STATIC_MSVC_RUNTIME_LIBRARY "Use the static MSVC runtime library" ON "MSVC;NOT WINDOWS_STORE" OFF) - -if (USE_STATIC_MSVC_RUNTIME_LIBRARY) - set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") -endif() - -# Only do this when we're at the top level, see: https://gitlab.kitware.com/cmake/cmake/-/issues/24181 -if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) - set(CMAKE_CONFIGURATION_TYPES "Debug;Release") -endif() - -project(JoltC VERSION 0.1 - DESCRIPTION "C Wrapper for Jolt Physics" - LANGUAGES C CXX) - -add_library(joltc STATIC - JoltC/JoltC.h - JoltC/JoltC.cpp - JoltC/Enums.h - JoltC/Functions.h - JoltC/Vehicle.h - JoltC/Vehicle.cpp - JoltC/Test.cpp -) - -target_compile_features(joltc PUBLIC cxx_std_17) -target_include_directories(joltc PUBLIC . JoltPhysics) -target_link_libraries(joltc PUBLIC Jolt) - -install(TARGETS joltc DESTINATION lib) - -if(MSVC) - target_compile_options(joltc PRIVATE /W4) - - # Ignore 'unreferenced function with internal linkage' - target_compile_options(joltc PRIVATE /wd4505) -else() - target_compile_options(joltc PRIVATE -Wall -Wextra -Wpedantic) -endif() - -if (DOUBLE_PRECISION) - target_compile_definitions(joltc PUBLIC JPC_DOUBLE_PRECISION) -endif() - -if (OBJECT_LAYER_BITS) - target_compile_definitions(joltc PUBLIC JPC_OBJECT_LAYER_BITS=${OBJECT_LAYER_BITS}) -endif() - -if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) - add_executable(HelloWorld - HelloWorld/main.cpp - ) - - # cxx_std_20 gives us designated initializers, bringing us closer to Actual C. - target_compile_features(HelloWorld PRIVATE cxx_std_20) - - # Eventually we should switch back to Actual C: - # set_property(TARGET HelloWorld PROPERTY C_STANDARD 23) - - target_include_directories(HelloWorld PUBLIC .) - target_link_libraries(HelloWorld PUBLIC joltc) -endif() - -add_subdirectory(JoltPhysics/Build) \ No newline at end of file diff --git a/crates/joltc-sys-patched/JoltC/HelloWorld/README.md b/crates/joltc-sys-patched/JoltC/HelloWorld/README.md deleted file mode 100644 index d1b8c9f9c80..00000000000 --- a/crates/joltc-sys-patched/JoltC/HelloWorld/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# HelloWorld in JoltC -A port of the HelloWorld example from Jolt, but using JoltC. \ No newline at end of file diff --git a/crates/joltc-sys-patched/JoltC/HelloWorld/main.cpp b/crates/joltc-sys-patched/JoltC/HelloWorld/main.cpp deleted file mode 100644 index b0218fced5c..00000000000 --- a/crates/joltc-sys-patched/JoltC/HelloWorld/main.cpp +++ /dev/null @@ -1,218 +0,0 @@ -#include -#include -#include - -#include "JoltC/JoltC.h" - -#ifdef _MSC_VER - #define unreachable() __assume(0) -#else - #define unreachable() __builtin_unreachable() -#endif - -typedef enum Hello_ObjectLayers { - HELLO_OL_NON_MOVING, - HELLO_OL_MOVING, - HELLO_OL_COUNT, -} Hello_ObjectLayers; - -typedef enum Hello_BroadPhaseLayers { - HELLO_BPL_NON_MOVING, - HELLO_BPL_MOVING, - HELLO_BPL_COUNT, -} Hello_BroadPhaseLayers; - -unsigned int Hello_BPL_GetNumBroadPhaseLayers(const void *self) { - return HELLO_BPL_COUNT; -} - -JPC_BroadPhaseLayer Hello_BPL_GetBroadPhaseLayer(const void *self, JPC_ObjectLayer inLayer) { - switch (inLayer) { - case HELLO_OL_NON_MOVING: - return HELLO_BPL_NON_MOVING; - - case HELLO_OL_MOVING: - return HELLO_BPL_MOVING; - - default: - unreachable(); - } -} - -static JPC_BroadPhaseLayerInterfaceFns Hello_BPL = { - .GetNumBroadPhaseLayers = Hello_BPL_GetNumBroadPhaseLayers, - .GetBroadPhaseLayer = Hello_BPL_GetBroadPhaseLayer, -}; - -bool Hello_OVB_ShouldCollide(const void *self, JPC_ObjectLayer inLayer1, JPC_BroadPhaseLayer inLayer2) { - switch (inLayer1) { - case HELLO_OL_NON_MOVING: - return inLayer2 == HELLO_BPL_MOVING; - - case HELLO_OL_MOVING: - return true; - - default: - unreachable(); - } -} - -static JPC_ObjectVsBroadPhaseLayerFilterFns Hello_OVB = { - .ShouldCollide = Hello_OVB_ShouldCollide, -}; - -bool Hello_OVO_ShouldCollide(const void *self, JPC_ObjectLayer inLayer1, JPC_ObjectLayer inLayer2) { - switch (inLayer1) - { - case HELLO_OL_NON_MOVING: - return inLayer2 == HELLO_OL_MOVING; // Non moving only collides with moving - - case HELLO_OL_MOVING: - return true; // Moving collides with everything - - default: - unreachable(); - } -} - -static JPC_ObjectLayerPairFilterFns Hello_OVO = { - .ShouldCollide = Hello_OVO_ShouldCollide, -}; - -void Hello_Debug_DrawLine(const void *self, JPC_RVec3 inFrom, JPC_RVec3 inTo, JPC_Color inColor) { - // printf("Draw line from (%f, %f, %f) to (%f, %f, %f) with color (%d, %d, %d)\n", - // inFrom.x, inFrom.y, inFrom.z, inTo.x, inTo.y, inTo.z, inColor.r, inColor.g, inColor.b); -} - -static JPC_DebugRendererSimpleFns Hello_DebugRenderer = { - .DrawLine = Hello_Debug_DrawLine, -}; - -int main() { - JPC_RegisterDefaultAllocator(); - JPC_FactoryInit(); - JPC_RegisterTypes(); - - JPC_TempAllocatorImpl* temp_allocator = JPC_TempAllocatorImpl_new(10 * 1024 * 1024); - - JPC_JobSystemThreadPool* job_system = JPC_JobSystemThreadPool_new2(JPC_MAX_PHYSICS_JOBS, JPC_MAX_PHYSICS_BARRIERS); - - JPC_BroadPhaseLayerInterface* broad_phase_layer_interface = JPC_BroadPhaseLayerInterface_new(nullptr, Hello_BPL); - JPC_ObjectVsBroadPhaseLayerFilter* object_vs_broad_phase_layer_filter = JPC_ObjectVsBroadPhaseLayerFilter_new(nullptr, Hello_OVB); - JPC_ObjectLayerPairFilter* object_vs_object_layer_filter = JPC_ObjectLayerPairFilter_new(nullptr, Hello_OVO); - - const unsigned int cMaxBodies = 1024; - const unsigned int cNumBodyMutexes = 0; - const unsigned int cMaxBodyPairs = 1024; - const unsigned int cMaxContactConstraints = 1024; - - JPC_PhysicsSystem* physics_system = JPC_PhysicsSystem_new(); - JPC_PhysicsSystem_Init( - physics_system, - cMaxBodies, - cNumBodyMutexes, - cMaxBodyPairs, - cMaxContactConstraints, - broad_phase_layer_interface, - object_vs_broad_phase_layer_filter, - object_vs_object_layer_filter); - - // TODO: register body activation listener - // TODO: register contact listener - - JPC_BodyInterface* body_interface = JPC_PhysicsSystem_GetBodyInterface(physics_system); - - JPC_BoxShapeSettings floor_shape_settings; - JPC_BoxShapeSettings_default(&floor_shape_settings); - floor_shape_settings.HalfExtent = JPC_Vec3{100.0f, 1.0f, 100.0f}; - floor_shape_settings.Density = 500.0; - - JPC_Shape* floor_shape; - JPC_String* err; - if (!JPC_BoxShapeSettings_Create(&floor_shape_settings, &floor_shape, &err)) { - printf("fatal error: %s\n", JPC_String_c_str(err)); - - // the world is ending, but I guess we can still free memory - JPC_String_delete(err); - - exit(1); - } - - JPC_BodyCreationSettings floor_settings; - JPC_BodyCreationSettings_default(&floor_settings); - floor_settings.Position = JPC_RVec3{0.0, -1.0, 0.0}; - floor_settings.MotionType = JPC_MOTION_TYPE_STATIC; - floor_settings.ObjectLayer = HELLO_OL_NON_MOVING; - floor_settings.Shape = floor_shape; - - JPC_Body* floor = JPC_BodyInterface_CreateBody(body_interface, &floor_settings); - JPC_BodyInterface_AddBody(body_interface, JPC_Body_GetID(floor), JPC_ACTIVATION_DONT_ACTIVATE); - - JPC_SphereShapeSettings sphere_shape_settings; - JPC_SphereShapeSettings_default(&sphere_shape_settings); - sphere_shape_settings.Radius = 0.5; - - JPC_Shape* sphere_shape; - if (!JPC_SphereShapeSettings_Create(&sphere_shape_settings, &sphere_shape, &err)) { - printf("fatal error: %s\n", JPC_String_c_str(err)); - - // the world is ending, but I guess we can still free memory - JPC_String_delete(err); - - exit(1); - } - - JPC_BodyCreationSettings sphere_settings; - JPC_BodyCreationSettings_default(&sphere_settings); - sphere_settings.Position = JPC_RVec3{0.0, 2.0, 0.0}; - sphere_settings.MotionType = JPC_MOTION_TYPE_DYNAMIC; - sphere_settings.ObjectLayer = HELLO_OL_MOVING; - sphere_settings.Shape = sphere_shape; - - JPC_Body* sphere = JPC_BodyInterface_CreateBody(body_interface, &sphere_settings); - JPC_BodyID sphere_id = JPC_Body_GetID(sphere); - JPC_BodyInterface_AddBody(body_interface, sphere_id, JPC_ACTIVATION_ACTIVATE); - - JPC_BodyInterface_SetLinearVelocity(body_interface, sphere_id, JPC_Vec3{0.0, -5.0, 0.0}); - - JPC_DebugRendererSimple* debug_renderer = JPC_DebugRendererSimple_new(nullptr, Hello_DebugRenderer); - JPC_BodyManager_DrawSettings draw_settings; - JPC_BodyManager_DrawSettings_default(&draw_settings); - JPC_PhysicsSystem_DrawBodies(physics_system, &draw_settings, debug_renderer, nullptr); - - JPC_PhysicsSystem_OptimizeBroadPhase(physics_system); - - const float cDeltaTime = 1.0f / 60.0f; - const int cCollisionSteps = 1; - - int step = 0; - while (JPC_BodyInterface_IsActive(body_interface, sphere_id)) { - ++step; - - JPC_RVec3 position = JPC_BodyInterface_GetCenterOfMassPosition(body_interface, sphere_id); - JPC_Vec3 velocity = JPC_BodyInterface_GetLinearVelocity(body_interface, sphere_id); - - printf("Step %d: Position = (%f, %f, %f), Velocity = (%f, %f, %f)\n", step, position.x, position.y, position.z, velocity.x, velocity.y, velocity.z); - - JPC_PhysicsSystem_Update(physics_system, cDeltaTime, cCollisionSteps, temp_allocator, job_system); - } - - JPC_BodyInterface_RemoveBody(body_interface, sphere_id); - JPC_BodyInterface_DestroyBody(body_interface, sphere_id); - - JPC_BodyInterface_RemoveBody(body_interface, JPC_Body_GetID(floor)); - JPC_BodyInterface_DestroyBody(body_interface, JPC_Body_GetID(floor)); - - JPC_PhysicsSystem_delete(physics_system); - JPC_BroadPhaseLayerInterface_delete(broad_phase_layer_interface); - JPC_ObjectVsBroadPhaseLayerFilter_delete(object_vs_broad_phase_layer_filter); - JPC_ObjectLayerPairFilter_delete(object_vs_object_layer_filter); - - JPC_JobSystemThreadPool_delete(job_system); - JPC_TempAllocatorImpl_delete(temp_allocator); - - JPC_UnregisterTypes(); - JPC_FactoryDelete(); - - printf("Hello, world!\n"); -} \ No newline at end of file diff --git a/crates/joltc-sys-patched/JoltC/JoltC/Enums.h b/crates/joltc-sys-patched/JoltC/JoltC/Enums.h deleted file mode 100644 index 84f17c47f40..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltC/Enums.h +++ /dev/null @@ -1,301 +0,0 @@ -#pragma once - -#include - -const int JPC_MAX_PHYSICS_JOBS = 2048; -const int JPC_MAX_PHYSICS_BARRIERS = 8; - -ENSURE_EQUAL(JPC_MAX_PHYSICS_JOBS, JPH::cMaxPhysicsJobs); -ENSURE_EQUAL(JPC_MAX_PHYSICS_BARRIERS, JPH::cMaxPhysicsBarriers); - -typedef enum JPC_ShapeType: uint8_t { - JPC_SHAPE_TYPE_CONVEX, - JPC_SHAPE_TYPE_COMPOUND, - JPC_SHAPE_TYPE_DECORATED, - JPC_SHAPE_TYPE_MESH, - JPC_SHAPE_TYPE_HEIGHT_FIELD, - JPC_SHAPE_TYPE_SOFTBODY, - JPC_SHAPE_TYPE_USER1, - JPC_SHAPE_TYPE_USER2, - JPC_SHAPE_TYPE_USER3, - JPC_SHAPE_TYPE_USER4, -} JPC_ShapeType; - -ENSURE_ENUM_EQ(JPC_SHAPE_TYPE_CONVEX, JPH::EShapeType::Convex) -ENSURE_ENUM_EQ(JPC_SHAPE_TYPE_COMPOUND, JPH::EShapeType::Compound) -ENSURE_ENUM_EQ(JPC_SHAPE_TYPE_DECORATED, JPH::EShapeType::Decorated) -ENSURE_ENUM_EQ(JPC_SHAPE_TYPE_MESH, JPH::EShapeType::Mesh) -ENSURE_ENUM_EQ(JPC_SHAPE_TYPE_HEIGHT_FIELD, JPH::EShapeType::HeightField) -ENSURE_ENUM_EQ(JPC_SHAPE_TYPE_USER1, JPH::EShapeType::User1) -ENSURE_ENUM_EQ(JPC_SHAPE_TYPE_USER2, JPH::EShapeType::User2) -ENSURE_ENUM_EQ(JPC_SHAPE_TYPE_USER3, JPH::EShapeType::User3) -ENSURE_ENUM_EQ(JPC_SHAPE_TYPE_USER4, JPH::EShapeType::User4) - -typedef enum JPC_ShapeSubType: uint8_t { - JPC_SHAPE_SUB_TYPE_SPHERE, - JPC_SHAPE_SUB_TYPE_BOX, - JPC_SHAPE_SUB_TYPE_TRIANGLE, - JPC_SHAPE_SUB_TYPE_CAPSULE, - JPC_SHAPE_SUB_TYPE_TAPEREDCAPSULE, - JPC_SHAPE_SUB_TYPE_CYLINDER, - JPC_SHAPE_SUB_TYPE_CONVEX_HULL, - JPC_SHAPE_SUB_TYPE_STATIC_COMPOUND, - JPC_SHAPE_SUB_TYPE_MUTABLE_COMPOUND, - JPC_SHAPE_SUB_TYPE_ROTATED_TRANSLATED, - JPC_SHAPE_SUB_TYPE_SCALED, - JPC_SHAPE_SUB_TYPE_OFFSET_CENTER_OF_MASS, - JPC_SHAPE_SUB_TYPE_MESH, - JPC_SHAPE_SUB_TYPE_HEIGHT_FIELD, - JPC_SHAPE_SUB_TYPE_SOFT_BODY, - JPC_SHAPE_SUB_TYPE_USER1, - JPC_SHAPE_SUB_TYPE_USER2, - JPC_SHAPE_SUB_TYPE_USER3, - JPC_SHAPE_SUB_TYPE_USER4, - JPC_SHAPE_SUB_TYPE_USER5, - JPC_SHAPE_SUB_TYPE_USER6, - JPC_SHAPE_SUB_TYPE_USER7, - JPC_SHAPE_SUB_TYPE_USER8, - JPC_SHAPE_SUB_TYPE_USER_CONVEX1, - JPC_SHAPE_SUB_TYPE_USER_CONVEX2, - JPC_SHAPE_SUB_TYPE_USER_CONVEX3, - JPC_SHAPE_SUB_TYPE_USER_CONVEX4, - JPC_SHAPE_SUB_TYPE_USER_CONVEX5, - JPC_SHAPE_SUB_TYPE_USER_CONVEX6, - JPC_SHAPE_SUB_TYPE_USER_CONVEX7, - JPC_SHAPE_SUB_TYPE_USER_CONVEX8, -} JPC_ShapeSubType; - -ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_SPHERE, JPH::EShapeSubType::Sphere) -ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_BOX, JPH::EShapeSubType::Box) -ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_TRIANGLE, JPH::EShapeSubType::Triangle) -ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_CAPSULE, JPH::EShapeSubType::Capsule) -ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_TAPEREDCAPSULE, JPH::EShapeSubType::TaperedCapsule) -ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_CYLINDER, JPH::EShapeSubType::Cylinder) -ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_CONVEX_HULL, JPH::EShapeSubType::ConvexHull) -ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_STATIC_COMPOUND, JPH::EShapeSubType::StaticCompound) -ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_MUTABLE_COMPOUND, JPH::EShapeSubType::MutableCompound) -ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_ROTATED_TRANSLATED, JPH::EShapeSubType::RotatedTranslated) -ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_SCALED, JPH::EShapeSubType::Scaled) -ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_OFFSET_CENTER_OF_MASS, JPH::EShapeSubType::OffsetCenterOfMass) -ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_MESH, JPH::EShapeSubType::Mesh) -ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_HEIGHT_FIELD, JPH::EShapeSubType::HeightField) -ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_SOFT_BODY, JPH::EShapeSubType::SoftBody) -ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_USER1, JPH::EShapeSubType::User1) -ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_USER2, JPH::EShapeSubType::User2) -ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_USER3, JPH::EShapeSubType::User3) -ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_USER4, JPH::EShapeSubType::User4) -ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_USER5, JPH::EShapeSubType::User5) -ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_USER6, JPH::EShapeSubType::User6) -ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_USER7, JPH::EShapeSubType::User7) -ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_USER8, JPH::EShapeSubType::User8) -ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_USER_CONVEX1, JPH::EShapeSubType::UserConvex1) -ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_USER_CONVEX2, JPH::EShapeSubType::UserConvex2) -ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_USER_CONVEX3, JPH::EShapeSubType::UserConvex3) -ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_USER_CONVEX4, JPH::EShapeSubType::UserConvex4) -ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_USER_CONVEX5, JPH::EShapeSubType::UserConvex5) -ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_USER_CONVEX6, JPH::EShapeSubType::UserConvex6) -ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_USER_CONVEX7, JPH::EShapeSubType::UserConvex7) -ENSURE_ENUM_EQ(JPC_SHAPE_SUB_TYPE_USER_CONVEX8, JPH::EShapeSubType::UserConvex8) - -typedef uint32_t JPC_PhysicsUpdateError; -const JPC_PhysicsUpdateError JPC_PHYSICS_UPDATE_ERROR_NONE = 0; -const JPC_PhysicsUpdateError JPC_PHYSICS_UPDATE_ERROR_MANIFOLD_CACHE_FULL = 1 << 0; -const JPC_PhysicsUpdateError JPC_PHYSICS_UPDATE_ERROR_BODY_PAIR_CACHE_FULL = 1 << 1; -const JPC_PhysicsUpdateError JPC_PHYSICS_UPDATE_ERROR_CONTACT_CONSTRAINTS_FULL = 1 << 2; - -ENSURE_ENUM_EQ(JPC_PHYSICS_UPDATE_ERROR_NONE, JPH::EPhysicsUpdateError::None) -ENSURE_ENUM_EQ(JPC_PHYSICS_UPDATE_ERROR_MANIFOLD_CACHE_FULL, JPH::EPhysicsUpdateError::ManifoldCacheFull) -ENSURE_ENUM_EQ(JPC_PHYSICS_UPDATE_ERROR_BODY_PAIR_CACHE_FULL, JPH::EPhysicsUpdateError::BodyPairCacheFull) -ENSURE_ENUM_EQ(JPC_PHYSICS_UPDATE_ERROR_CONTACT_CONSTRAINTS_FULL, JPH::EPhysicsUpdateError::ContactConstraintsFull) - -typedef enum JPC_ConstraintType: uint32_t { - JPC_CONSTRAINT_TYPE_CONSTRAINT, - JPC_CONSTRAINT_TYPE_TWO_BODY_CONSTRAINT, -} JPC_ConstraintType; - -ENSURE_ENUM_EQ(JPC_CONSTRAINT_TYPE_CONSTRAINT, JPH::EConstraintType::Constraint) -ENSURE_ENUM_EQ(JPC_CONSTRAINT_TYPE_TWO_BODY_CONSTRAINT, JPH::EConstraintType::TwoBodyConstraint) - -typedef enum JPC_ConstraintSubType: uint32_t { - JPC_CONSTRAINT_SUB_TYPE_FIXED, - JPC_CONSTRAINT_SUB_TYPE_POINT, - JPC_CONSTRAINT_SUB_TYPE_HINGE, - JPC_CONSTRAINT_SUB_TYPE_SLIDER, - JPC_CONSTRAINT_SUB_TYPE_DISTANCE, - JPC_CONSTRAINT_SUB_TYPE_CONE, - JPC_CONSTRAINT_SUB_TYPE_SWING_TWIST, - JPC_CONSTRAINT_SUB_TYPE_SIX_DOF, - JPC_CONSTRAINT_SUB_TYPE_PATH, - JPC_CONSTRAINT_SUB_TYPE_VEHICLE, - JPC_CONSTRAINT_SUB_TYPE_RACK_AND_PINION, - JPC_CONSTRAINT_SUB_TYPE_GEAR, - JPC_CONSTRAINT_SUB_TYPE_PULLEY, - JPC_CONSTRAINT_SUB_TYPE_USER1, - JPC_CONSTRAINT_SUB_TYPE_USER2, - JPC_CONSTRAINT_SUB_TYPE_USER3, - JPC_CONSTRAINT_SUB_TYPE_USER4, -} JPC_ConstraintSubType; - -ENSURE_ENUM_EQ(JPC_CONSTRAINT_SUB_TYPE_FIXED, JPH::EConstraintSubType::Fixed) -ENSURE_ENUM_EQ(JPC_CONSTRAINT_SUB_TYPE_POINT, JPH::EConstraintSubType::Point) -ENSURE_ENUM_EQ(JPC_CONSTRAINT_SUB_TYPE_HINGE, JPH::EConstraintSubType::Hinge) -ENSURE_ENUM_EQ(JPC_CONSTRAINT_SUB_TYPE_SLIDER, JPH::EConstraintSubType::Slider) -ENSURE_ENUM_EQ(JPC_CONSTRAINT_SUB_TYPE_DISTANCE, JPH::EConstraintSubType::Distance) -ENSURE_ENUM_EQ(JPC_CONSTRAINT_SUB_TYPE_CONE, JPH::EConstraintSubType::Cone) -ENSURE_ENUM_EQ(JPC_CONSTRAINT_SUB_TYPE_SWING_TWIST, JPH::EConstraintSubType::SwingTwist) -ENSURE_ENUM_EQ(JPC_CONSTRAINT_SUB_TYPE_SIX_DOF, JPH::EConstraintSubType::SixDOF) -ENSURE_ENUM_EQ(JPC_CONSTRAINT_SUB_TYPE_PATH, JPH::EConstraintSubType::Path) -ENSURE_ENUM_EQ(JPC_CONSTRAINT_SUB_TYPE_VEHICLE, JPH::EConstraintSubType::Vehicle) -ENSURE_ENUM_EQ(JPC_CONSTRAINT_SUB_TYPE_RACK_AND_PINION, JPH::EConstraintSubType::RackAndPinion) -ENSURE_ENUM_EQ(JPC_CONSTRAINT_SUB_TYPE_GEAR, JPH::EConstraintSubType::Gear) -ENSURE_ENUM_EQ(JPC_CONSTRAINT_SUB_TYPE_PULLEY, JPH::EConstraintSubType::Pulley) -ENSURE_ENUM_EQ(JPC_CONSTRAINT_SUB_TYPE_USER1, JPH::EConstraintSubType::User1) -ENSURE_ENUM_EQ(JPC_CONSTRAINT_SUB_TYPE_USER2, JPH::EConstraintSubType::User2) -ENSURE_ENUM_EQ(JPC_CONSTRAINT_SUB_TYPE_USER3, JPH::EConstraintSubType::User3) -ENSURE_ENUM_EQ(JPC_CONSTRAINT_SUB_TYPE_USER4, JPH::EConstraintSubType::User4) - -typedef enum JPC_ConstraintSpace: uint32_t { - JPC_CONSTRAINT_SPACE_LOCAL_TO_BODY_COM, - JPC_CONSTRAINT_SPACE_WORLD_SPACE, -} JPC_ConstraintSpace; - -ENSURE_ENUM_EQ(JPC_CONSTRAINT_SPACE_LOCAL_TO_BODY_COM, JPH::EConstraintSpace::LocalToBodyCOM) -ENSURE_ENUM_EQ(JPC_CONSTRAINT_SPACE_WORLD_SPACE, JPH::EConstraintSpace::WorldSpace) - -typedef enum JPC_MotionType: uint8_t { - JPC_MOTION_TYPE_STATIC, - JPC_MOTION_TYPE_KINEMATIC, - JPC_MOTION_TYPE_DYNAMIC, -} JPC_MotionType; - -ENSURE_ENUM_EQ(JPC_MOTION_TYPE_STATIC, JPH::EMotionType::Static) -ENSURE_ENUM_EQ(JPC_MOTION_TYPE_KINEMATIC, JPH::EMotionType::Kinematic) -ENSURE_ENUM_EQ(JPC_MOTION_TYPE_DYNAMIC, JPH::EMotionType::Dynamic) - -typedef enum JPC_MotionQuality: uint8_t { - JPC_MOTION_QUALITY_DISCRETE, - JPC_MOTION_QUALITY_LINEAR_CAST, -} JPC_MotionQuality; - -ENSURE_ENUM_EQ(JPC_MOTION_QUALITY_DISCRETE, JPH::EMotionQuality::Discrete) -ENSURE_ENUM_EQ(JPC_MOTION_QUALITY_LINEAR_CAST, JPH::EMotionQuality::LinearCast) - -typedef enum JPC_OverrideMassProperties: uint8_t { - JPC_OVERRIDE_MASS_PROPS_CALC_MASS_INERTIA, - JPC_OVERRIDE_MASS_PROPS_CALC_INERTIA, - JPC_OVERRIDE_MASS_PROPS_MASS_INERTIA_PROVIDED, -} JPC_OverrideMassProperties; - -ENSURE_ENUM_EQ(JPC_OVERRIDE_MASS_PROPS_CALC_MASS_INERTIA, - JPH::EOverrideMassProperties::CalculateMassAndInertia); -ENSURE_ENUM_EQ(JPC_OVERRIDE_MASS_PROPS_CALC_INERTIA, - JPH::EOverrideMassProperties::CalculateInertia); -ENSURE_ENUM_EQ(JPC_OVERRIDE_MASS_PROPS_MASS_INERTIA_PROVIDED, - JPH::EOverrideMassProperties::MassAndInertiaProvided); - -typedef enum JPC_GroundState: uint32_t { - JPC_CHARACTER_GROUND_STATE_ON_GROUND, - JPC_CHARACTER_GROUND_STATE_ON_STEEP_GROUND, - JPC_CHARACTER_GROUND_STATE_NOT_SUPPORTED, - JPC_CHARACTER_GROUND_STATE_IN_AIR, -} JPC_GroundState; - -// ENSURE_ENUM_EQ(JPC_CHARACTER_GROUND_STATE_ON_GROUND, JPH::EGroundState::OnGround) -// ENSURE_ENUM_EQ(JPC_CHARACTER_GROUND_STATE_ON_STEEP_GROUND, JPH::EGroundState::OnSteepGround) -// ENSURE_ENUM_EQ(JPC_CHARACTER_GROUND_STATE_NOT_SUPPORTED, JPH::EGroundState::NotSupported) -// ENSURE_ENUM_EQ(JPC_CHARACTER_GROUND_STATE_IN_AIR, JPH::EGroundState::InAir) - -typedef enum JPC_Activation: uint32_t { - JPC_ACTIVATION_ACTIVATE = 0, - JPC_ACTIVATION_DONT_ACTIVATE = 1, -} JPC_Activation; - -ENSURE_ENUM_EQ(JPC_ACTIVATION_ACTIVATE, JPH::EActivation::Activate) -ENSURE_ENUM_EQ(JPC_ACTIVATION_DONT_ACTIVATE, JPH::EActivation::DontActivate) - -typedef enum JPC_ValidateResult: uint32_t { - JPC_VALIDATE_RESULT_ACCEPT_ALL_CONTACTS, - JPC_VALIDATE_RESULT_ACCEPT_CONTACT, - JPC_VALIDATE_RESULT_REJECT_CONTACT, - JPC_VALIDATE_RESULT_REJECT_ALL_CONTACTS, -} JPC_ValidateResult; - -ENSURE_ENUM_EQ(JPC_VALIDATE_RESULT_ACCEPT_ALL_CONTACTS, - JPH::ValidateResult::AcceptAllContactsForThisBodyPair); -ENSURE_ENUM_EQ(JPC_VALIDATE_RESULT_ACCEPT_CONTACT, - JPH::ValidateResult::AcceptContact); -ENSURE_ENUM_EQ(JPC_VALIDATE_RESULT_REJECT_CONTACT, - JPH::ValidateResult::RejectContact); -ENSURE_ENUM_EQ(JPC_VALIDATE_RESULT_REJECT_ALL_CONTACTS, - JPH::ValidateResult::RejectAllContactsForThisBodyPair); - -typedef enum JPC_BackFaceMode: uint8_t { - JPC_BACK_FACE_IGNORE, - JPC_BACK_FACE_COLLIDE, -} JPC_BackFaceMode; - -ENSURE_ENUM_EQ(JPC_BACK_FACE_IGNORE, JPH::EBackFaceMode::IgnoreBackFaces) -ENSURE_ENUM_EQ(JPC_BACK_FACE_COLLIDE, JPH::EBackFaceMode::CollideWithBackFaces) - -typedef enum JPC_BodyType: uint8_t { - JPC_BODY_TYPE_RIGID_BODY = 0, - JPC_BODY_TYPE_SOFT_BODY = 1, -} JPC_BodyType; - -ENSURE_ENUM_EQ(JPC_BODY_TYPE_RIGID_BODY, JPH::EBodyType::RigidBody) -ENSURE_ENUM_EQ(JPC_BODY_TYPE_SOFT_BODY, JPH::EBodyType::SoftBody) - -typedef enum JPC_AllowedDOFs: uint8_t { - JPC_ALLOWED_DOFS_NONE = 0b000000, - JPC_ALLOWED_DOFS_ALL = 0b111111, - JPC_ALLOWED_DOFS_TRANSLATIONX = 0b000001, - JPC_ALLOWED_DOFS_TRANSLATIONY = 0b000010, - JPC_ALLOWED_DOFS_TRANSLATIONZ = 0b000100, - JPC_ALLOWED_DOFS_ROTATIONX = 0b001000, - JPC_ALLOWED_DOFS_ROTATIONY = 0b010000, - JPC_ALLOWED_DOFS_ROTATIONZ = 0b100000, - JPC_ALLOWED_DOFS_PLANE2D = JPC_ALLOWED_DOFS_TRANSLATIONX | JPC_ALLOWED_DOFS_TRANSLATIONY | JPC_ALLOWED_DOFS_ROTATIONZ, -} JPC_AllowedDOFs; - -ENSURE_ENUM_EQ(JPC_ALLOWED_DOFS_NONE, JPH::EAllowedDOFs::None) -ENSURE_ENUM_EQ(JPC_ALLOWED_DOFS_ALL, JPH::EAllowedDOFs::All) -ENSURE_ENUM_EQ(JPC_ALLOWED_DOFS_TRANSLATIONX, JPH::EAllowedDOFs::TranslationX) -ENSURE_ENUM_EQ(JPC_ALLOWED_DOFS_TRANSLATIONY, JPH::EAllowedDOFs::TranslationY) -ENSURE_ENUM_EQ(JPC_ALLOWED_DOFS_TRANSLATIONZ, JPH::EAllowedDOFs::TranslationZ) -ENSURE_ENUM_EQ(JPC_ALLOWED_DOFS_ROTATIONX, JPH::EAllowedDOFs::RotationX) -ENSURE_ENUM_EQ(JPC_ALLOWED_DOFS_ROTATIONY, JPH::EAllowedDOFs::RotationY) -ENSURE_ENUM_EQ(JPC_ALLOWED_DOFS_ROTATIONZ, JPH::EAllowedDOFs::RotationZ) -ENSURE_ENUM_EQ(JPC_ALLOWED_DOFS_PLANE2D, JPH::EAllowedDOFs::Plane2D) - -typedef enum JPC_Features: uint32_t { - JPC_FEATURE_DOUBLE_PRECISION = (1 << 0), - JPC_FEATURE_NEON = (1 << 1), - JPC_FEATURE_SSE = (1 << 2), - JPC_FEATURE_SSE4_1 = (1 << 3), - JPC_FEATURE_SSE4_2 = (1 << 4), - JPC_FEATURE_AVX = (1 << 5), - JPC_FEATURE_AVX2 = (1 << 6), - JPC_FEATURE_AVX512 = (1 << 7), - JPC_FEATURE_F16C = (1 << 8), - JPC_FEATURE_LZCNT = (1 << 9), - JPC_FEATURE_TZCNT = (1 << 10), - JPC_FEATURE_FMADD = (1 << 11), - JPC_FEATURE_PLATFORM_DETERMINISTIC = (1 << 12), - JPC_FEATURE_FLOATING_POINT_EXCEPTIONS = (1 << 13), - JPC_FEATURE_DEBUG = (1 << 14), -} JPC_Features; - -typedef int JPC_ShapeColor; -const JPC_ShapeColor JPC_SHAPE_COLOR_INSTANCE_COLOR = 0; -const JPC_ShapeColor JPC_SHAPE_COLOR_SHAPE_TYPE_COLOR = 1; -const JPC_ShapeColor JPC_SHAPE_COLOR_MOTION_TYPE_COLOR = 2; -const JPC_ShapeColor JPC_SHAPE_COLOR_SLEEP_COLOR = 3; -const JPC_ShapeColor JPC_SHAPE_COLOR_ISLAND_COLOR = 4; -const JPC_ShapeColor JPC_SHAPE_COLOR_MATERIAL_COLOR = 5; - -ENSURE_ENUM_EQ(JPC_SHAPE_COLOR_INSTANCE_COLOR, JPH::BodyManager::EShapeColor::InstanceColor) -ENSURE_ENUM_EQ(JPC_SHAPE_COLOR_SHAPE_TYPE_COLOR, JPH::BodyManager::EShapeColor::ShapeTypeColor) -ENSURE_ENUM_EQ(JPC_SHAPE_COLOR_MOTION_TYPE_COLOR, JPH::BodyManager::EShapeColor::MotionTypeColor) -ENSURE_ENUM_EQ(JPC_SHAPE_COLOR_SLEEP_COLOR, JPH::BodyManager::EShapeColor::SleepColor) -ENSURE_ENUM_EQ(JPC_SHAPE_COLOR_ISLAND_COLOR, JPH::BodyManager::EShapeColor::IslandColor) -ENSURE_ENUM_EQ(JPC_SHAPE_COLOR_MATERIAL_COLOR, JPH::BodyManager::EShapeColor::MaterialColor) \ No newline at end of file diff --git a/crates/joltc-sys-patched/JoltC/JoltC/Functions.h b/crates/joltc-sys-patched/JoltC/JoltC/Functions.h deleted file mode 100644 index f8dcdee8ad5..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltC/Functions.h +++ /dev/null @@ -1,819 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -#ifdef _MSC_VER - #define JPC_API extern __declspec(dllexport) -#else - #define JPC_API -#endif - -static float JPC_PI = 3.14159265358979323846f; - -// C-compatible typedefs that match Jolt's internal primitive typedefs. -#define uint unsigned int - -#ifdef __cplusplus -extern "C" { -#endif - -JPC_API void JPC_RegisterDefaultAllocator(); -JPC_API void JPC_FactoryInit(); -JPC_API void JPC_FactoryDelete(); -JPC_API void JPC_RegisterTypes(); -JPC_API void JPC_UnregisterTypes(); - -//////////////////////////////////////////////////////////////////////////////// -// Primitive types - -typedef struct JPC_Float3 { - float x; - float y; - float z; -} JPC_Float3; - -ENSURE_SIZE_ALIGN(JPC_Float3, JPH::Float3) - -typedef struct JPC_Vec3 { - alignas(16) float x; - float y; - float z; - float _w; -} JPC_Vec3; - -ENSURE_SIZE_ALIGN(JPC_Vec3, JPH::Vec3) - -typedef struct JPC_Vec4 { - alignas(16) float x; - float y; - float z; - float w; -} JPC_Vec4; - -ENSURE_SIZE_ALIGN(JPC_Vec4, JPH::Vec4) - -typedef struct JPC_DVec3 { - alignas(32) double x; - double y; - double z; - double _w; -} JPC_DVec3; - -ENSURE_SIZE_ALIGN(JPC_DVec3, JPH::DVec3) - -typedef struct JPC_Quat { - alignas(16) float x; - float y; - float z; - float w; -} JPC_Quat; - -ENSURE_SIZE_ALIGN(JPC_Quat, JPH::Quat) - -typedef struct JPC_Mat44 { - alignas(16) JPC_Vec4 matrix[4]; -} JPC_Mat44; - -ENSURE_SIZE_ALIGN(JPC_Mat44, JPH::Mat44) - -typedef struct JPC_DMat44 { - alignas(32) JPC_Vec4 col[3]; - JPC_DVec3 col3; -} JPC_DMat44; - -ENSURE_SIZE_ALIGN(JPC_DMat44, JPH::DMat44) - -typedef struct JPC_Color { - alignas(uint32_t) uint8_t r; - uint8_t g; - uint8_t b; - uint8_t a; -} JPC_Color; - -ENSURE_SIZE_ALIGN(JPC_Color, JPH::Color) - -#ifdef JPC_DOUBLE_PRECISION - typedef JPC_DVec3 JPC_RVec3; - typedef JPC_DMat44 JPC_RMat44; - typedef double Real; -#else - typedef JPC_Vec3 JPC_RVec3; - typedef JPC_Mat44 JPC_RMat44; - typedef float Real; -#endif - -ENSURE_SIZE_ALIGN(JPC_RVec3, JPH::RVec3) - -typedef uint32_t JPC_BodyID; -ENSURE_SIZE_ALIGN(JPC_BodyID, JPH::BodyID) - -typedef uint32_t JPC_SubShapeID; -ENSURE_SIZE_ALIGN(JPC_SubShapeID, JPH::SubShapeID) - -typedef uint8_t JPC_BroadPhaseLayer; -ENSURE_SIZE_ALIGN(JPC_BroadPhaseLayer, JPH::BroadPhaseLayer) - -#ifndef JPC_OBJECT_LAYER_BITS - #define JPC_OBJECT_LAYER_BITS 16 -#endif - -#if JPC_OBJECT_LAYER_BITS == 16 - typedef uint16_t JPC_ObjectLayer; -#elif JPC_OBJECT_LAYER_BITS == 32 - typedef uint32_t JPC_ObjectLayer; -#else - #error "JPC_OBJECT_LAYER_BITS must be 16 or 32" -#endif - -ENSURE_SIZE_ALIGN(JPC_ObjectLayer, JPH::ObjectLayer) - -typedef struct JPC_IndexedTriangleNoMaterial { - uint32_t idx[3]; -} JPC_IndexedTriangleNoMaterial; - -ENSURE_SIZE_ALIGN(JPC_IndexedTriangleNoMaterial, JPH::IndexedTriangleNoMaterial) - -typedef struct JPC_IndexedTriangle { - uint32_t idx[3]; - uint32_t materialIndex; -} JPC_IndexedTriangle; - -ENSURE_SIZE_ALIGN(JPC_IndexedTriangle, JPH::IndexedTriangle) - -typedef struct JPC_RayCast { - JPC_Vec3 Origin; - JPC_Vec3 Direction; -} JPC_RayCast; - -typedef struct JPC_RRayCast { - JPC_RVec3 Origin; - JPC_Vec3 Direction; -} JPC_RRayCast; - -typedef struct JPC_RayCastResult { - JPC_BodyID BodyID; - float Fraction; - JPC_SubShapeID SubShapeID2; -} JPC_RayCastResult; - -typedef struct JPC_Body JPC_Body; - -//////////////////////////////////////////////////////////////////////////////// -// VertexList == Array == std::vector - -typedef struct JPC_VertexList JPC_VertexList; - -JPC_API JPC_VertexList* JPC_VertexList_new(const JPC_Float3* storage, size_t len); -JPC_API void JPC_VertexList_delete(JPC_VertexList* object); - -//////////////////////////////////////////////////////////////////////////////// -// IndexedTriangleList == Array == std::vector - -typedef struct JPC_IndexedTriangleList JPC_IndexedTriangleList; - -JPC_API JPC_IndexedTriangleList* JPC_IndexedTriangleList_new(const JPC_IndexedTriangle* storage, size_t len); -JPC_API void JPC_IndexedTriangleList_delete(JPC_IndexedTriangleList* object); - -//////////////////////////////////////////////////////////////////////////////// -// TempAllocatorImpl - -typedef struct JPC_TempAllocatorImpl JPC_TempAllocatorImpl; - -JPC_API JPC_TempAllocatorImpl* JPC_TempAllocatorImpl_new(uint size); -JPC_API void JPC_TempAllocatorImpl_delete(JPC_TempAllocatorImpl* object); - -//////////////////////////////////////////////////////////////////////////////// -// JobSystemThreadPool - -typedef struct JPC_JobSystemThreadPool JPC_JobSystemThreadPool; - -JPC_API JPC_JobSystemThreadPool* JPC_JobSystemThreadPool_new2( - uint inMaxJobs, - uint inMaxBarriers); -JPC_API JPC_JobSystemThreadPool* JPC_JobSystemThreadPool_new3( - uint inMaxJobs, - uint inMaxBarriers, - int inNumThreads); - -JPC_API void JPC_JobSystemThreadPool_delete(JPC_JobSystemThreadPool* object); - -//////////////////////////////////////////////////////////////////////////////// -// BroadPhaseLayerInterface - -typedef struct JPC_BroadPhaseLayerInterfaceFns { - uint (*GetNumBroadPhaseLayers)(const void *self); - JPC_BroadPhaseLayer (*GetBroadPhaseLayer)(const void *self, JPC_ObjectLayer inLayer); -} JPC_BroadPhaseLayerInterfaceFns; - -typedef struct JPC_BroadPhaseLayerInterface JPC_BroadPhaseLayerInterface; - -JPC_API JPC_BroadPhaseLayerInterface* JPC_BroadPhaseLayerInterface_new( - const void *self, - JPC_BroadPhaseLayerInterfaceFns fns); - -JPC_API void JPC_BroadPhaseLayerInterface_delete(JPC_BroadPhaseLayerInterface* object); - -//////////////////////////////////////////////////////////////////////////////// -// BroadPhaseLayerFilter - -typedef struct JPC_BroadPhaseLayerFilterFns { - bool (*ShouldCollide)(const void *self, JPC_BroadPhaseLayer inLayer); -} JPC_BroadPhaseLayerFilterFns; - -typedef struct JPC_BroadPhaseLayerFilter JPC_BroadPhaseLayerFilter; - -JPC_API JPC_BroadPhaseLayerFilter* JPC_BroadPhaseLayerFilter_new( - const void *self, - JPC_BroadPhaseLayerFilterFns fns); - -JPC_API void JPC_BroadPhaseLayerFilter_delete(JPC_BroadPhaseLayerFilter* object); - -//////////////////////////////////////////////////////////////////////////////// -// ObjectLayerFilter - -typedef struct JPC_ObjectLayerFilterFns { - bool (*ShouldCollide)(const void *self, JPC_ObjectLayer inLayer); -} JPC_ObjectLayerFilterFns; - -typedef struct JPC_ObjectLayerFilter JPC_ObjectLayerFilter; - -JPC_API JPC_ObjectLayerFilter* JPC_ObjectLayerFilter_new( - const void *self, - JPC_ObjectLayerFilterFns fns); - -JPC_API void JPC_ObjectLayerFilter_delete(JPC_ObjectLayerFilter* object); - -//////////////////////////////////////////////////////////////////////////////// -// BodyFilter - -typedef struct JPC_BodyFilterFns { - bool (*ShouldCollide)(const void *self, JPC_BodyID inBodyID); - bool (*ShouldCollideLocked)(const void *self, const JPC_Body *inBodyID); -} JPC_BodyFilterFns; - -typedef struct JPC_BodyFilter JPC_BodyFilter; - -JPC_API JPC_BodyFilter* JPC_BodyFilter_new( - const void *self, - JPC_BodyFilterFns fns); - -JPC_API void JPC_BodyFilter_delete(JPC_BodyFilter* object); - -//////////////////////////////////////////////////////////////////////////////// -// ObjectVsBroadPhaseLayerFilter - -typedef struct JPC_ObjectVsBroadPhaseLayerFilterFns { - bool (*ShouldCollide)(const void *self, JPC_ObjectLayer inLayer1, JPC_BroadPhaseLayer inLayer2); -} JPC_ObjectVsBroadPhaseLayerFilterFns; - -typedef struct JPC_ObjectVsBroadPhaseLayerFilter JPC_ObjectVsBroadPhaseLayerFilter; - -JPC_API JPC_ObjectVsBroadPhaseLayerFilter* JPC_ObjectVsBroadPhaseLayerFilter_new( - const void *self, - JPC_ObjectVsBroadPhaseLayerFilterFns fns); - -JPC_API void JPC_ObjectVsBroadPhaseLayerFilter_delete(JPC_ObjectVsBroadPhaseLayerFilter* object); - -//////////////////////////////////////////////////////////////////////////////// -// ObjectLayerPairFilter - -typedef struct JPC_ObjectLayerPairFilterFns { - bool (*ShouldCollide)(const void *self, JPC_ObjectLayer inLayer1, JPC_ObjectLayer inLayer2); -} JPC_ObjectLayerPairFilterFns; - -typedef struct JPC_ObjectLayerPairFilter JPC_ObjectLayerPairFilter; - -JPC_API JPC_ObjectLayerPairFilter* JPC_ObjectLayerPairFilter_new( - const void *self, - JPC_ObjectLayerPairFilterFns fns); - -JPC_API void JPC_ObjectLayerPairFilter_delete(JPC_ObjectLayerPairFilter* object); - -//////////////////////////////////////////////////////////////////////////////// -// DrawSettings - -typedef struct JPC_BodyManager_DrawSettings { - bool mDrawGetSupportFunction; - bool mDrawSupportDirection; - bool mDrawGetSupportingFace; - bool mDrawShape; - bool mDrawShapeWireframe; - JPC_ShapeColor mDrawShapeColor; - bool mDrawBoundingBox; - bool mDrawCenterOfMassTransform; - bool mDrawWorldTransform; - bool mDrawVelocity; - bool mDrawMassAndInertia; - bool mDrawSleepStats; - bool mDrawSoftBodyVertices; - bool mDrawSoftBodyVertexVelocities; - bool mDrawSoftBodyEdgeConstraints; - bool mDrawSoftBodyBendConstraints; - bool mDrawSoftBodyVolumeConstraints; - bool mDrawSoftBodySkinConstraints; - bool mDrawSoftBodyLRAConstraints; - bool mDrawSoftBodyPredictedBounds; -} JPC_BodyManager_DrawSettings; - -ENSURE_SIZE_ALIGN(JPC_BodyManager_DrawSettings, JPH::BodyManager::DrawSettings) - -JPC_API void JPC_BodyManager_DrawSettings_default(JPC_BodyManager_DrawSettings* object); - -//////////////////////////////////////////////////////////////////////////////// -// DebugRendererSimple - -typedef struct JPC_DebugRendererSimpleFns { - void (*DrawLine)(const void *self, JPC_RVec3 inFrom, JPC_RVec3 inTo, JPC_Color inColor); -} JPC_DebugRendererSimpleFns; - -typedef struct JPC_DebugRendererSimple JPC_DebugRendererSimple; - -JPC_API JPC_DebugRendererSimple* JPC_DebugRendererSimple_new( - const void *self, - JPC_DebugRendererSimpleFns fns); - -JPC_API void JPC_DebugRendererSimple_delete(JPC_DebugRendererSimple* object); - -//////////////////////////////////////////////////////////////////////////////// -// String - -typedef struct JPC_String JPC_String; - -JPC_API void JPC_String_delete(JPC_String* self); -JPC_API const char* JPC_String_c_str(JPC_String* self); - -//////////////////////////////////////////////////////////////////////////////// -// Shape -> RefTarget - -typedef struct JPC_Shape JPC_Shape; - -JPC_API uint32_t JPC_Shape_GetRefCount(const JPC_Shape* self); -JPC_API void JPC_Shape_AddRef(const JPC_Shape* self); -JPC_API void JPC_Shape_Release(const JPC_Shape* self); - -JPC_API JPC_Vec3 JPC_Shape_GetCenterOfMass(const JPC_Shape* self); - -//////////////////////////////////////////////////////////////////////////////// -// TriangleShapeSettings - -typedef struct JPC_TriangleShapeSettings { - // ShapeSettings - uint64_t UserData; - - // ConvexShapeSettings - // TODO: Material - float Density; - - // TriangleShapeSettings - JPC_Vec3 V1; - JPC_Vec3 V2; - JPC_Vec3 V3; - float ConvexRadius; -} JPC_TriangleShapeSettings; - -JPC_API void JPC_TriangleShapeSettings_default(JPC_TriangleShapeSettings* object); -JPC_API bool JPC_TriangleShapeSettings_Create(const JPC_TriangleShapeSettings* self, JPC_Shape** outShape, JPC_String** outError); - -//////////////////////////////////////////////////////////////////////////////// -// BoxShapeSettings -> ConvexShapeSettings -> ShapeSettings - -typedef struct JPC_BoxShapeSettings { - // ShapeSettings - uint64_t UserData; - - // ConvexShapeSettings - // TODO: Material - float Density; - - // BoxShapeSettings - JPC_Vec3 HalfExtent; - float ConvexRadius; -} JPC_BoxShapeSettings; - -JPC_API void JPC_BoxShapeSettings_default(JPC_BoxShapeSettings* object); -JPC_API bool JPC_BoxShapeSettings_Create(const JPC_BoxShapeSettings* self, JPC_Shape** outShape, JPC_String** outError); - -//////////////////////////////////////////////////////////////////////////////// -// SphereShapeSettings -> ConvexShapeSettings -> ShapeSettings - -typedef struct JPC_SphereShapeSettings { - // ShapeSettings - uint64_t UserData; - - // ConvexShapeSettings - // TODO: Material - float Density; - - // SphereShapeSettings - float Radius; -} JPC_SphereShapeSettings; - -JPC_API void JPC_SphereShapeSettings_default(JPC_SphereShapeSettings* object); -JPC_API bool JPC_SphereShapeSettings_Create(const JPC_SphereShapeSettings* self, JPC_Shape** outShape, JPC_String** outError); - -//////////////////////////////////////////////////////////////////////////////// -// CapsuleShapeSettings -> ConvexShapeSettings -> ShapeSettings - -typedef struct JPC_CapsuleShapeSettings { - // ShapeSettings - uint64_t UserData; - - // ConvexShapeSettings - // TODO: Material - float Density; - - // CapsuleShapeSettings - float Radius; - float HalfHeightOfCylinder; -} JPC_CapsuleShapeSettings; - -JPC_API void JPC_CapsuleShapeSettings_default(JPC_CapsuleShapeSettings* object); -JPC_API bool JPC_CapsuleShapeSettings_Create(const JPC_CapsuleShapeSettings* self, JPC_Shape** outShape, JPC_String** outError); - -//////////////////////////////////////////////////////////////////////////////// -// CylinderShapeSettings -> ConvexShapeSettings -> ShapeSettings - -typedef struct JPC_CylinderShapeSettings { - // ShapeSettings - uint64_t UserData; - - // ConvexShapeSettings - // TODO: Material - float Density; - - // CylinderShapeSettings - float HalfHeight; - float Radius; - float ConvexRadius; -} JPC_CylinderShapeSettings; - -JPC_API void JPC_CylinderShapeSettings_default(JPC_CylinderShapeSettings* object); -JPC_API bool JPC_CylinderShapeSettings_Create(const JPC_CylinderShapeSettings* self, JPC_Shape** outShape, JPC_String** outError); - -//////////////////////////////////////////////////////////////////////////////// -// ConvexHullShapeSettings -> ConvexShapeSettings -> ShapeSettings - -typedef struct JPC_ConvexHullShapeSettings { - // ShapeSettings - uint64_t UserData; - - // ConvexShapeSettings - // TODO: Material - float Density; - - // ConvexHullShapeSettings - const JPC_Vec3* Points; - size_t PointsLen; - float MaxConvexRadius; - float MaxErrorConvexRadius; - float HullTolerance; -} JPC_ConvexHullShapeSettings; - -JPC_API void JPC_ConvexHullShapeSettings_default(JPC_ConvexHullShapeSettings* object); -JPC_API bool JPC_ConvexHullShapeSettings_Create(const JPC_ConvexHullShapeSettings* self, JPC_Shape** outShape, JPC_String** outError); - -//////////////////////////////////////////////////////////////////////////////// -// CompoundShape::SubShapeSettings - -typedef struct JPC_SubShapeSettings { - const JPC_Shape* Shape; - JPC_Vec3 Position; - JPC_Quat Rotation; - uint32_t UserData; -} JPC_SubShapeSettings; - -JPC_API void JPC_SubShapeSettings_default(JPC_SubShapeSettings* object); - -//////////////////////////////////////////////////////////////////////////////// -// StaticCompoundShapeSettings -> CompoundShapeSettings -> ShapeSettings - -typedef struct JPC_StaticCompoundShapeSettings { - // ShapeSettings - uint64_t UserData; - - // CompoundShapeSettings - const JPC_SubShapeSettings* SubShapes; - size_t SubShapesLen; - - // StaticCompoundShapeSettings - // (no fields) -} JPC_StaticCompoundShapeSettings; - -JPC_API void JPC_StaticCompoundShapeSettings_default(JPC_StaticCompoundShapeSettings* object); -JPC_API bool JPC_StaticCompoundShapeSettings_Create(const JPC_StaticCompoundShapeSettings* self, JPC_Shape** outShape, JPC_String** outError); - -//////////////////////////////////////////////////////////////////////////////// -// MutableCompoundShape -> CompoundShapeSettings -> ShapeSettings - -typedef struct JPC_MutableCompoundShapeSettings { - // ShapeSettings - uint64_t UserData; - - // CompoundShapeSettings - const JPC_SubShapeSettings* SubShapes; - size_t SubShapesLen; - - // MutableCompoundShape - // (no fields) -} JPC_MutableCompoundShapeSettings; - -JPC_API void JPC_MutableCompoundShapeSettings_default(JPC_MutableCompoundShapeSettings* object); -JPC_API bool JPC_MutableCompoundShapeSettings_Create(const JPC_MutableCompoundShapeSettings* self, JPC_Shape** outShape, JPC_String** outError); - -//////////////////////////////////////////////////////////////////////////////// -// BodyCreationSettings - -typedef struct JPC_BodyCreationSettings { - JPC_RVec3 Position; - JPC_Quat Rotation; - JPC_Vec3 LinearVelocity; - JPC_Vec3 AngularVelocity; - uint64_t UserData; - JPC_ObjectLayer ObjectLayer; - // CollisionGroup CollisionGroup; - JPC_MotionType MotionType; - JPC_AllowedDOFs AllowedDOFs; - bool AllowDynamicOrKinematic; - bool IsSensor; - bool CollideKinematicVsNonDynamic; - bool UseManifoldReduction; - bool ApplyGyroscopicForce; - JPC_MotionQuality MotionQuality; - bool EnhancedInternalEdgeRemoval; - bool AllowSleeping; - float Friction; - float Restitution; - float LinearDamping; - float AngularDamping; - float MaxLinearVelocity; - float MaxAngularVelocity; - float GravityFactor; - uint NumVelocityStepsOverride; - uint NumPositionStepsOverride; - JPC_OverrideMassProperties OverrideMassProperties; - float InertiaMultiplier; - - // MassProperties MassPropertiesOverride; - - const JPC_Shape* Shape; -} JPC_BodyCreationSettings; - -JPC_API void JPC_BodyCreationSettings_default(JPC_BodyCreationSettings* settings); - -typedef struct JPC_BodyCreationSettings JPC_BodyCreationSettings; - -JPC_API JPC_BodyCreationSettings* JPC_BodyCreationSettings_new(); - -//////////////////////////////////////////////////////////////////////////////// -// Body - -JPC_API JPC_BodyID JPC_Body_GetID(const JPC_Body* self); -JPC_API JPC_BodyType JPC_Body_GetBodyType(const JPC_Body* self); -JPC_API bool JPC_Body_IsRigidBody(const JPC_Body* self); -JPC_API bool JPC_Body_IsSoftBody(const JPC_Body* self); -JPC_API bool JPC_Body_IsActive(const JPC_Body* self); -JPC_API bool JPC_Body_IsStatic(const JPC_Body* self); -JPC_API bool JPC_Body_IsKinematic(const JPC_Body* self); -JPC_API bool JPC_Body_IsDynamic(const JPC_Body* self); -JPC_API bool JPC_Body_CanBeKinematicOrDynamic(const JPC_Body* self); -JPC_API void JPC_Body_SetIsSensor(JPC_Body* self, bool inIsSensor); -JPC_API bool JPC_Body_IsSensor(const JPC_Body* self); -JPC_API void JPC_Body_SetCollideKinematicVsNonDynamic(JPC_Body* self, bool inCollide); -JPC_API bool JPC_Body_GetCollideKinematicVsNonDynamic(const JPC_Body* self); -JPC_API void JPC_Body_SetUseManifoldReduction(JPC_Body* self, bool inUseReduction); -JPC_API bool JPC_Body_GetUseManifoldReduction(const JPC_Body* self); -JPC_API bool JPC_Body_GetUseManifoldReductionWithBody(const JPC_Body* self, const JPC_Body* inBody2); -JPC_API void JPC_Body_SetApplyGyroscopicForce(JPC_Body* self, bool inApply); -JPC_API bool JPC_Body_GetApplyGyroscopicForce(const JPC_Body* self); -JPC_API void JPC_Body_SetEnhancedInternalEdgeRemoval(JPC_Body* self, bool inApply); -JPC_API bool JPC_Body_GetEnhancedInternalEdgeRemoval(const JPC_Body* self); -JPC_API bool JPC_Body_GetEnhancedInternalEdgeRemovalWithBody(const JPC_Body* self, const JPC_Body* inBody2); -JPC_API JPC_MotionType JPC_Body_GetMotionType(const JPC_Body* self); -JPC_API void JPC_Body_SetMotionType(JPC_Body* self, JPC_MotionType inMotionType); -JPC_API JPC_BroadPhaseLayer JPC_Body_GetBroadPhaseLayer(const JPC_Body* self); -JPC_API JPC_ObjectLayer JPC_Body_GetObjectLayer(const JPC_Body* self); - -// JPC_API const CollisionGroup & JPC_Body_GetCollisionGroup(const JPC_Body* self); -// JPC_API CollisionGroup & JPC_Body_GetCollisionGroup(JPC_Body* self); -// JPC_API void JPC_Body_SetCollisionGroup(JPC_Body* self, const CollisionGroup &inGroup); - -JPC_API bool JPC_Body_GetAllowSleeping(const JPC_Body* self); -JPC_API void JPC_Body_SetAllowSleeping(JPC_Body* self, bool inAllow); -JPC_API void JPC_Body_ResetSleepTimer(JPC_Body* self); -JPC_API float JPC_Body_GetFriction(const JPC_Body* self); -JPC_API void JPC_Body_SetFriction(JPC_Body* self, float inFriction); -JPC_API float JPC_Body_GetRestitution(const JPC_Body* self); -JPC_API void JPC_Body_SetRestitution(JPC_Body* self, float inRestitution); -JPC_API JPC_Vec3 JPC_Body_GetLinearVelocity(const JPC_Body* self); -JPC_API void JPC_Body_SetLinearVelocity(JPC_Body* self, JPC_Vec3 inLinearVelocity); -JPC_API void JPC_Body_SetLinearVelocityClamped(JPC_Body* self, JPC_Vec3 inLinearVelocity); -JPC_API JPC_Vec3 JPC_Body_GetAngularVelocity(const JPC_Body* self); -JPC_API void JPC_Body_SetAngularVelocity(JPC_Body* self, JPC_Vec3 inAngularVelocity); -JPC_API void JPC_Body_SetAngularVelocityClamped(JPC_Body* self, JPC_Vec3 inAngularVelocity); -JPC_API JPC_Vec3 JPC_Body_GetPointVelocityCOM(const JPC_Body* self, JPC_Vec3 inPointRelativeToCOM); -JPC_API JPC_Vec3 JPC_Body_GetPointVelocity(const JPC_Body* self, JPC_RVec3 inPoint); -JPC_API void JPC_Body_AddForce(JPC_Body* self, JPC_Vec3 inForce); - -// JPC_API void JPC_Body_AddForce(JPC_Body* self, JPC_Vec3 inForce, JPC_RVec3 inPosition); - -JPC_API void JPC_Body_AddTorque(JPC_Body* self, JPC_Vec3 inTorque); -JPC_API JPC_Vec3 JPC_Body_GetAccumulatedForce(const JPC_Body* self); -JPC_API JPC_Vec3 JPC_Body_GetAccumulatedTorque(const JPC_Body* self); -JPC_API void JPC_Body_ResetForce(JPC_Body* self); -JPC_API void JPC_Body_ResetTorque(JPC_Body* self); -JPC_API void JPC_Body_ResetMotion(JPC_Body* self); -JPC_API void JPC_Body_GetInverseInertia(const JPC_Body* self, JPC_Mat44* outMatrix); -JPC_API void JPC_Body_AddImpulse(JPC_Body* self, JPC_Vec3 inImpulse); -JPC_API void JPC_Body_AddImpulse2(JPC_Body* self, JPC_Vec3 inImpulse, JPC_RVec3 inPosition); -JPC_API void JPC_Body_AddAngularImpulse(JPC_Body* self, JPC_Vec3 inAngularImpulse); -JPC_API void JPC_Body_MoveKinematic(JPC_Body* self, JPC_RVec3 inTargetPosition, JPC_Quat inTargetRotation, float inDeltaTime); -JPC_API bool JPC_Body_ApplyBuoyancyImpulse(JPC_Body* self, JPC_RVec3 inSurfacePosition, JPC_Vec3 inSurfaceNormal, float inBuoyancy, float inLinearDrag, float inAngularDrag, JPC_Vec3 inFluidVelocity, JPC_Vec3 inGravity, float inDeltaTime); -JPC_API bool JPC_Body_IsInBroadPhase(const JPC_Body* self); -JPC_API bool JPC_Body_IsCollisionCacheInvalid(const JPC_Body* self); -JPC_API const JPC_Shape* JPC_Body_GetShape(const JPC_Body* self); -JPC_API JPC_RVec3 JPC_Body_GetPosition(const JPC_Body* self); -JPC_API JPC_Quat JPC_Body_GetRotation(const JPC_Body* self); - -// JPC_API RMat44 JPC_Body_GetWorldTransform(const JPC_Body* self); - -JPC_API JPC_RVec3 JPC_Body_GetCenterOfMassPosition(const JPC_Body* self); - -// JPC_API RMat44 JPC_Body_GetCenterOfMassTransform(const JPC_Body* self); -// JPC_API RMat44 JPC_Body_GetInverseCenterOfMassTransform(const JPC_Body* self); -// JPC_API const AABox & JPC_Body_GetWorldSpaceBounds(const JPC_Body* self); -// JPC_API const MotionProperties *JPC_Body_GetMotionProperties(const JPC_Body* self) -// JPC_API MotionProperties * JPC_Body_GetMotionProperties(JPC_Body* self); -// JPC_API const MotionProperties *JPC_Body_GetMotionPropertiesUnchecked(const JPC_Body* self) -// JPC_API MotionProperties * JPC_Body_GetMotionPropertiesUnchecked(JPC_Body* self); - -JPC_API uint64_t JPC_Body_GetUserData(const JPC_Body* self); -JPC_API void JPC_Body_SetUserData(JPC_Body* self, uint64_t inUserData); - -// JPC_API JPC_Vec3 JPC_Body_GetWorldSpaceSurfaceNormal(const JPC_Body* self, const SubShapeID &inSubShapeID, JPC_RVec3 inPosition); -// JPC_API TransformedShape JPC_Body_GetTransformedShape(const JPC_Body* self); -// JPC_API BodyCreationSettings JPC_Body_GetBodyCreationSettings(const JPC_Body* self); -// JPC_API SoftBodyCreationSettings JPC_Body_GetSoftBodyCreationSettings(const JPC_Body* self); - -//////////////////////////////////////////////////////////////////////////////// -// BodyInterface - -typedef struct JPC_BodyInterface JPC_BodyInterface; - -JPC_API JPC_Body* JPC_BodyInterface_CreateBody(JPC_BodyInterface* self, const JPC_BodyCreationSettings* inSettings); -JPC_API JPC_Body* JPC_BodyInterface_CreateBodyWithID(JPC_BodyInterface *self, JPC_BodyID inBodyID, const JPC_BodyCreationSettings* inSettings); -JPC_API JPC_Body* JPC_BodyInterface_CreateBodyWithoutID(const JPC_BodyInterface *self, const JPC_BodyCreationSettings* inSettings); - -// JPC_API JPC_Body* JPC_BodyInterface_CreateSoftBody(JPC_BodyInterface *self, const SoftBodyCreationSettings &inSettings); -// JPC_API JPC_Body* JPC_BodyInterface_CreateSoftBodyWithID(JPC_BodyInterface *self, JPC_BodyID inBodyID, const SoftBodyCreationSettings* inSettings); -// JPC_API JPC_Body* JPC_BodyInterface_CreateSoftBodyWithoutID(const JPC_BodyInterface *self, const SoftBodyCreationSettings* inSettings); - -JPC_API void JPC_BodyInterface_DestroyBodyWithoutID(const JPC_BodyInterface *self, JPC_Body *inBody); -JPC_API bool JPC_BodyInterface_AssignBodyID(JPC_BodyInterface *self, JPC_Body *ioBody); - -// JPC_API bool JPC_BodyInterface_AssignBodyID(JPC_BodyInterface *self, JPC_Body *ioBody, JPC_BodyID inBodyID); - -JPC_API JPC_Body* JPC_BodyInterface_UnassignBodyID(JPC_BodyInterface *self, JPC_BodyID inBodyID); -JPC_API void JPC_BodyInterface_UnassignBodyIDs(JPC_BodyInterface *self, const JPC_BodyID *inBodyIDs, int inNumber, JPC_Body **outBodies); -JPC_API void JPC_BodyInterface_DestroyBody(JPC_BodyInterface *self, JPC_BodyID inBodyID); -JPC_API void JPC_BodyInterface_DestroyBodies(JPC_BodyInterface *self, const JPC_BodyID *inBodyIDs, int inNumber); -JPC_API void JPC_BodyInterface_AddBody(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_Activation inActivationMode); -JPC_API void JPC_BodyInterface_RemoveBody(JPC_BodyInterface *self, JPC_BodyID inBodyID); -JPC_API bool JPC_BodyInterface_IsAdded(const JPC_BodyInterface *self, JPC_BodyID inBodyID); -JPC_API JPC_BodyID JPC_BodyInterface_CreateAndAddBody(JPC_BodyInterface *self, const JPC_BodyCreationSettings* inSettings, JPC_Activation inActivationMode); - -// JPC_API JPC_BodyID JPC_BodyInterface_CreateAndAddSoftBody(JPC_BodyInterface *self, const SoftBodyCreationSettings &inSettings, JPC_Activation inActivationMode); - -JPC_API void* JPC_BodyInterface_AddBodiesPrepare(JPC_BodyInterface *self, JPC_BodyID *ioBodies, int inNumber); -JPC_API void JPC_BodyInterface_AddBodiesFinalize(JPC_BodyInterface *self, JPC_BodyID *ioBodies, int inNumber, void* inAddState, JPC_Activation inActivationMode); -JPC_API void JPC_BodyInterface_AddBodiesAbort(JPC_BodyInterface *self, JPC_BodyID *ioBodies, int inNumber, void* inAddState); -JPC_API void JPC_BodyInterface_RemoveBodies(JPC_BodyInterface *self, JPC_BodyID *ioBodies, int inNumber); -JPC_API void JPC_BodyInterface_ActivateBody(JPC_BodyInterface *self, JPC_BodyID inBodyID); -JPC_API void JPC_BodyInterface_ActivateBodies(JPC_BodyInterface *self, JPC_BodyID *inBodyIDs, int inNumber); - -// JPC_API void JPC_BodyInterface_ActivateBodiesInAABox(JPC_BodyInterface *self, const AABox &inBox, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter); - -JPC_API void JPC_BodyInterface_DeactivateBody(JPC_BodyInterface *self, JPC_BodyID inBodyID); -JPC_API void JPC_BodyInterface_DeactivateBodies(JPC_BodyInterface *self, JPC_BodyID *inBodyIDs, int inNumber); -JPC_API bool JPC_BodyInterface_IsActive(const JPC_BodyInterface *self, JPC_BodyID inBodyID); - -// TwoBodyConstraint * JPC_BodyInterface_CreateConstraint(JPC_BodyInterface *self, const TwoBodyConstraintSettings *inSettings, JPC_BodyID inBodyID1, JPC_BodyID inBodyID2); -// JPC_API void JPC_BodyInterface_ActivateConstraint(JPC_BodyInterface *self, const TwoBodyConstraint *inConstraint); -// RefConst JPC_BodyInterface_GetShape(const JPC_BodyInterface *self, JPC_BodyID inBodyID); - -JPC_API void JPC_BodyInterface_SetShape(const JPC_BodyInterface *self, JPC_BodyID inBodyID, const JPC_Shape *inShape, bool inUpdateMassProperties, JPC_Activation inActivationMode); -JPC_API void JPC_BodyInterface_NotifyShapeChanged(const JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_Vec3 inPreviousCenterOfMass, bool inUpdateMassProperties, JPC_Activation inActivationMode); -JPC_API void JPC_BodyInterface_SetObjectLayer(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_ObjectLayer inLayer); -JPC_API JPC_ObjectLayer JPC_BodyInterface_GetObjectLayer(const JPC_BodyInterface *self, JPC_BodyID inBodyID); -JPC_API void JPC_BodyInterface_SetPositionAndRotation(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_RVec3 inPosition, JPC_Quat inRotation, JPC_Activation inActivationMode); -JPC_API void JPC_BodyInterface_SetPositionAndRotationWhenChanged(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_RVec3 inPosition, JPC_Quat inRotation, JPC_Activation inActivationMode); -JPC_API void JPC_BodyInterface_GetPositionAndRotation(const JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_RVec3 *outPosition, JPC_Quat *outRotation); -JPC_API void JPC_BodyInterface_SetPosition(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_RVec3 inPosition, JPC_Activation inActivationMode); -JPC_API JPC_RVec3 JPC_BodyInterface_GetPosition(const JPC_BodyInterface *self, JPC_BodyID inBodyID); -JPC_API JPC_RVec3 JPC_BodyInterface_GetCenterOfMassPosition(const JPC_BodyInterface *self, JPC_BodyID inBodyID); -JPC_API void JPC_BodyInterface_SetRotation(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_Quat inRotation, JPC_Activation inActivationMode); -JPC_API JPC_Quat JPC_BodyInterface_GetRotation(const JPC_BodyInterface *self, JPC_BodyID inBodyID); - -// RMat44 JPC_BodyInterface_GetWorldTransform(const JPC_BodyInterface *self, JPC_BodyID inBodyID); -// RMat44 JPC_BodyInterface_GetCenterOfMassTransform(const JPC_BodyInterface *self, JPC_BodyID inBodyID); - -JPC_API void JPC_BodyInterface_MoveKinematic(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_RVec3 inTargetPosition, JPC_Quat inTargetRotation, float inDeltaTime); -JPC_API void JPC_BodyInterface_SetLinearAndAngularVelocity(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_Vec3 inLinearVelocity, JPC_Vec3 inAngularVelocity); -JPC_API void JPC_BodyInterface_GetLinearAndAngularVelocity(const JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_Vec3 *outLinearVelocity, JPC_Vec3 *outAngularVelocity); -JPC_API void JPC_BodyInterface_SetLinearVelocity(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_Vec3 inLinearVelocity); -JPC_API JPC_Vec3 JPC_BodyInterface_GetLinearVelocity(const JPC_BodyInterface *self, JPC_BodyID inBodyID); -JPC_API void JPC_BodyInterface_AddLinearVelocity(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_Vec3 inLinearVelocity); -JPC_API void JPC_BodyInterface_AddLinearAndAngularVelocity(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_Vec3 inLinearVelocity, JPC_Vec3 inAngularVelocity); -JPC_API void JPC_BodyInterface_SetAngularVelocity(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_Vec3 inAngularVelocity); -JPC_API JPC_Vec3 JPC_BodyInterface_GetAngularVelocity(const JPC_BodyInterface *self, JPC_BodyID inBodyID); -JPC_API JPC_Vec3 JPC_BodyInterface_GetPointVelocity(const JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_RVec3 inPoint); -JPC_API void JPC_BodyInterface_SetPositionRotationAndVelocity(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_RVec3 inPosition, JPC_Quat inRotation, JPC_Vec3 inLinearVelocity, JPC_Vec3 inAngularVelocity); -JPC_API void JPC_BodyInterface_AddForce(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_Vec3 inForce); - -// JPC_API void JPC_BodyInterface_AddForce(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_Vec3 inForce, JPC_RVec3 inPoint); - -JPC_API void JPC_BodyInterface_AddTorque(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_Vec3 inTorque); -JPC_API void JPC_BodyInterface_AddForceAndTorque(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_Vec3 inForce, JPC_Vec3 inTorque); -JPC_API void JPC_BodyInterface_AddImpulse(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_Vec3 inImpulse); -JPC_API void JPC_BodyInterface_AddImpulse3(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_Vec3 inImpulse, JPC_RVec3 inPoint); -JPC_API void JPC_BodyInterface_AddAngularImpulse(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_Vec3 inAngularImpulse); -JPC_API JPC_BodyType JPC_BodyInterface_GetBodyType(const JPC_BodyInterface *self, JPC_BodyID inBodyID); -JPC_API void JPC_BodyInterface_SetMotionType(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_MotionType inMotionType, JPC_Activation inActivationMode); -JPC_API JPC_MotionType JPC_BodyInterface_GetMotionType(const JPC_BodyInterface *self, JPC_BodyID inBodyID); -JPC_API void JPC_BodyInterface_SetMotionQuality(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_MotionQuality inMotionQuality); -JPC_API JPC_MotionQuality JPC_BodyInterface_GetMotionQuality(const JPC_BodyInterface *self, JPC_BodyID inBodyID); -JPC_API void JPC_BodyInterface_GetInverseInertia(const JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_Mat44 *outMatrix); -JPC_API void JPC_BodyInterface_SetRestitution(JPC_BodyInterface *self, JPC_BodyID inBodyID, float inRestitution); -JPC_API float JPC_BodyInterface_GetRestitution(const JPC_BodyInterface *self, JPC_BodyID inBodyID); -JPC_API void JPC_BodyInterface_SetFriction(JPC_BodyInterface *self, JPC_BodyID inBodyID, float inFriction); -JPC_API float JPC_BodyInterface_GetFriction(const JPC_BodyInterface *self, JPC_BodyID inBodyID); -JPC_API void JPC_BodyInterface_SetGravityFactor(JPC_BodyInterface *self, JPC_BodyID inBodyID, float inGravityFactor); -JPC_API float JPC_BodyInterface_GetGravityFactor(const JPC_BodyInterface *self, JPC_BodyID inBodyID); -JPC_API void JPC_BodyInterface_SetUseManifoldReduction(JPC_BodyInterface *self, JPC_BodyID inBodyID, bool inUseReduction); -JPC_API bool JPC_BodyInterface_GetUseManifoldReduction(const JPC_BodyInterface *self, JPC_BodyID inBodyID); - -// TransformedShape JPC_BodyInterface_GetTransformedShape(const JPC_BodyInterface *self, JPC_BodyID inBodyID); - -JPC_API uint64_t JPC_BodyInterface_GetUserData(const JPC_BodyInterface *self, JPC_BodyID inBodyID); -JPC_API void JPC_BodyInterface_SetUserData(const JPC_BodyInterface *self, JPC_BodyID inBodyID, uint64_t inUserData); - -// const PhysicsMaterial* JPC_BodyInterface_GetMaterial(const JPC_BodyInterface *self, JPC_BodyID inBodyID, const SubShapeID &inSubShapeID); - -JPC_API void JPC_BodyInterface_InvalidateContactCache(JPC_BodyInterface *self, JPC_BodyID inBodyID); - -//////////////////////////////////////////////////////////////////////////////// -// NarrowPhaseQuery - -typedef struct JPC_NarrowPhaseQuery JPC_NarrowPhaseQuery; - -typedef struct JPC_NarrowPhaseQuery_CastRayArgs { - JPC_RRayCast Ray; - JPC_RayCastResult Result; - const JPC_BroadPhaseLayerFilter *BroadPhaseLayerFilter; - const JPC_ObjectLayerFilter *ObjectLayerFilter; - const JPC_BodyFilter *BodyFilter; -} JPC_NarrowPhaseQuery_CastRayArgs; - -JPC_API bool JPC_NarrowPhaseQuery_CastRay(const JPC_NarrowPhaseQuery* self, JPC_NarrowPhaseQuery_CastRayArgs* args); - -//////////////////////////////////////////////////////////////////////////////// -// PhysicsSystem - -typedef struct JPC_PhysicsSystem JPC_PhysicsSystem; - -JPC_API JPC_PhysicsSystem* JPC_PhysicsSystem_new(); -JPC_API void JPC_PhysicsSystem_delete(JPC_PhysicsSystem* object); -JPC_API void JPC_PhysicsSystem_Init( - JPC_PhysicsSystem* self, - uint inMaxBodies, - uint inNumBodyMutexes, - uint inMaxBodyPairs, - uint inMaxContactConstraints, - JPC_BroadPhaseLayerInterface* inBroadPhaseLayerInterface, - JPC_ObjectVsBroadPhaseLayerFilter* inObjectVsBroadPhaseLayerFilter, - JPC_ObjectLayerPairFilter* inObjectLayerPairFilter); - -JPC_API void JPC_PhysicsSystem_OptimizeBroadPhase(JPC_PhysicsSystem* self); - -JPC_API JPC_PhysicsUpdateError JPC_PhysicsSystem_Update( - JPC_PhysicsSystem* self, - float inDeltaTime, - int inCollisionSteps, - JPC_TempAllocatorImpl *inTempAllocator, // FIXME: un-specialize - JPC_JobSystemThreadPool *inJobSystem); // FIXME: un-specialize - -JPC_API JPC_BodyInterface* JPC_PhysicsSystem_GetBodyInterface(JPC_PhysicsSystem* self); - -JPC_API const JPC_NarrowPhaseQuery* JPC_PhysicsSystem_GetNarrowPhaseQuery(const JPC_PhysicsSystem* self); - -JPC_API void JPC_PhysicsSystem_DrawBodies( - JPC_PhysicsSystem* self, - JPC_BodyManager_DrawSettings* inSettings, - JPC_DebugRendererSimple* inRenderer, // FIXME: un-specialize - const void* inBodyFilter); // FIXME: BodyDrawFilter - -#ifdef __cplusplus -} -#endif \ No newline at end of file diff --git a/crates/joltc-sys-patched/JoltC/JoltC/JoltC.cpp b/crates/joltc-sys-patched/JoltC/JoltC/JoltC.cpp deleted file mode 100644 index 925747ea9a7..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltC/JoltC.cpp +++ /dev/null @@ -1,1526 +0,0 @@ -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - -#define OPAQUE_WRAPPER(c_type, cpp_type) \ - static c_type* to_jpc(cpp_type *in) { return reinterpret_cast(in); } \ - static const c_type* to_jpc(const cpp_type *in) { return reinterpret_cast(in); } \ - static cpp_type* to_jph(c_type *in) { return reinterpret_cast(in); } \ - static const cpp_type* to_jph(const c_type *in) { return reinterpret_cast(in); } \ - static cpp_type** to_jph(c_type **in) { return reinterpret_cast(in); } - -#define DESTRUCTOR(c_type) \ - JPC_API void c_type##_delete(c_type* object) { \ - delete to_jph(object); \ - } - -#define ENUM_CONVERSION(c_type, cpp_type) \ - static c_type to_jpc(cpp_type in) { return static_cast(in); } \ - static cpp_type to_jph(c_type in) { return static_cast(in); } - -#define LAYOUT_COMPATIBLE(c_type, cpp_type) \ - static c_type to_jpc(cpp_type in) { \ - c_type out; \ - memcpy(&out, &in, sizeof(c_type)); \ - return out; \ - } \ - static cpp_type to_jph(c_type in) { \ - cpp_type out; \ - memcpy(&out, &in, sizeof(cpp_type)); \ - return out; \ - } \ - static c_type* to_jpc(cpp_type* in) { \ - return reinterpret_cast(in); \ - } \ - static cpp_type* to_jph(c_type* in) { \ - return reinterpret_cast(in); \ - } \ - static const c_type* to_jpc(const cpp_type* in) { \ - return reinterpret_cast(in); \ - } \ - static const cpp_type* to_jph(const c_type* in) { \ - return reinterpret_cast(in); \ - } \ - static_assert(sizeof(c_type) == sizeof(cpp_type), "size of " #c_type " did not match size of " #cpp_type); \ - static_assert(alignof(c_type) == alignof(cpp_type), "align of " #c_type " did not match align of " #cpp_type); \ - static_assert(!std::is_polymorphic_v, #cpp_type " is polymorphic and cannot be made layout compatible"); - -template -constexpr auto to_integral(E e) -> typename std::underlying_type::type -{ - return static_cast::type>(e); -} - -ENUM_CONVERSION(JPC_MotionType, JPH::EMotionType) -ENUM_CONVERSION(JPC_AllowedDOFs, JPH::EAllowedDOFs) -ENUM_CONVERSION(JPC_Activation, JPH::EActivation) -ENUM_CONVERSION(JPC_BodyType, JPH::EBodyType) -ENUM_CONVERSION(JPC_MotionQuality, JPH::EMotionQuality) -ENUM_CONVERSION(JPC_OverrideMassProperties, JPH::EOverrideMassProperties) - -OPAQUE_WRAPPER(JPC_PhysicsSystem, JPH::PhysicsSystem) -DESTRUCTOR(JPC_PhysicsSystem) - -OPAQUE_WRAPPER(JPC_BodyInterface, JPH::BodyInterface) -OPAQUE_WRAPPER(JPC_NarrowPhaseQuery, JPH::NarrowPhaseQuery) - -OPAQUE_WRAPPER(JPC_TempAllocatorImpl, JPH::TempAllocatorImpl) -DESTRUCTOR(JPC_TempAllocatorImpl) - -OPAQUE_WRAPPER(JPC_JobSystemThreadPool, JPH::JobSystemThreadPool) -DESTRUCTOR(JPC_JobSystemThreadPool) - -OPAQUE_WRAPPER(JPC_Shape, JPH::Shape) -OPAQUE_WRAPPER(JPC_Body, JPH::Body) - -OPAQUE_WRAPPER(JPC_VertexList, JPH::VertexList) -DESTRUCTOR(JPC_VertexList) - -OPAQUE_WRAPPER(JPC_IndexedTriangleList, JPH::IndexedTriangleList) -DESTRUCTOR(JPC_IndexedTriangleList) - -OPAQUE_WRAPPER(JPC_String, JPH::String) -DESTRUCTOR(JPC_String) - -LAYOUT_COMPATIBLE(JPC_BodyManager_DrawSettings, JPH::BodyManager::DrawSettings) - -LAYOUT_COMPATIBLE(JPC_BodyID, JPH::BodyID) - -static auto to_jpc(JPH::BroadPhaseLayer in) { return in.GetValue(); } -static auto to_jph(JPC_BroadPhaseLayer in) { return JPH::BroadPhaseLayer(in); } - -static JPC_Vec3 to_jpc(JPH::Vec3 in) { - return JPC_Vec3{in.GetX(), in.GetY(), in.GetZ(), in.GetZ()}; -} -static JPH::Vec3 to_jph(JPC_Vec3 in) { - return JPH::Vec3(in.x, in.y, in.z); -} - -static JPH::Array to_jph(const JPC_Vec3* src, size_t n) { - JPH::Array vec; - vec.resize(n); - - if (src != nullptr) { - memcpy(vec.data(), src, n * sizeof(*src)); - } - - return vec; -} - -static JPC_DVec3 to_jpc(JPH::DVec3 in) { - return JPC_DVec3{in.GetX(), in.GetY(), in.GetZ(), in.GetZ()}; -} -static JPH::DVec3 to_jph(JPC_DVec3 in) { - return JPH::DVec3(in.x, in.y, in.z); -} - -static JPC_Quat to_jpc(JPH::Quat in) { - return JPC_Quat{in.GetX(), in.GetY(), in.GetZ(), in.GetW()}; -} -static JPH::Quat to_jph(JPC_Quat in) { - return JPH::Quat(in.x, in.y, in.z, in.w); -} - -static JPC_Mat44 to_jpc(JPH::Mat44 in) { - JPC_Mat44 out; - in.StoreFloat4x4(reinterpret_cast(&out)); - return out; -} -static JPH::Mat44 to_jph(JPC_Mat44 in) { - return JPH::Mat44::sLoadFloat4x4Aligned(reinterpret_cast(&in)); -} - -static JPC_Color to_jpc(JPH::Color in) { - return JPC_Color{in.r, in.g, in.b, in.a}; -} -static JPH::Color to_jph(JPC_Color in) { - return JPH::Color(in.r, in.g, in.b, in.a); -} - -static JPH::RayCast to_jph(JPC_RayCast in) { - return JPH::RayCast(to_jph(in.Origin), to_jph(in.Direction)); -} - -static JPH::RRayCast to_jph(JPC_RRayCast in) { - return JPH::RRayCast(to_jph(in.Origin), to_jph(in.Direction)); -} - -static JPC_SubShapeID to_jpc(JPH::SubShapeID in) { - return in.GetValue(); -} - -static JPC_RayCastResult to_jpc(JPH::RayCastResult in) { - JPC_RayCastResult out{0}; - out.BodyID = to_jpc(in.mBodyID); - out.Fraction = in.mFraction; - out.SubShapeID2 = to_jpc(in.mSubShapeID2); - - return out; -} - -JPC_API void JPC_RegisterDefaultAllocator() { - JPH::RegisterDefaultAllocator(); -} - -JPC_API void JPC_FactoryInit() { - JPH::Factory::sInstance = new JPH::Factory(); -} - -JPC_API void JPC_FactoryDelete() { - delete JPH::Factory::sInstance; - JPH::Factory::sInstance = nullptr; -} - -JPC_API void JPC_RegisterTypes() { - JPH::RegisterTypes(); -} - -JPC_API void JPC_UnregisterTypes() { - JPH::UnregisterTypes(); -} - -//////////////////////////////////////////////////////////////////////////////// -// VertexList == Array == std::vector - -JPC_API JPC_VertexList* JPC_VertexList_new(const JPC_Float3* storage, size_t len) { - const JPH::Float3* new_storage = (const JPH::Float3*)storage; - return to_jpc(new JPH::VertexList(new_storage, new_storage + len)); -} - -//////////////////////////////////////////////////////////////////////////////// -// IndexedTriangleList == Array == std::vector - -JPC_API JPC_IndexedTriangleList* JPC_IndexedTriangleList_new(const JPC_IndexedTriangle* storage, size_t len) { - const JPH::IndexedTriangle* new_storage = (const JPH::IndexedTriangle*)storage; - return to_jpc(new JPH::IndexedTriangleList(new_storage, new_storage + len)); -} - -//////////////////////////////////////////////////////////////////////////////// -// TempAllocatorImpl - -JPC_API JPC_TempAllocatorImpl* JPC_TempAllocatorImpl_new(uint size) { - return to_jpc(new JPH::TempAllocatorImpl(size)); -} - -//////////////////////////////////////////////////////////////////////////////// -// JobSystemThreadPool - -JPC_API JPC_JobSystemThreadPool* JPC_JobSystemThreadPool_new2( - uint inMaxJobs, - uint inMaxBarriers) -{ - return to_jpc(new JPH::JobSystemThreadPool(inMaxJobs, inMaxBarriers)); -} - -JPC_API JPC_JobSystemThreadPool* JPC_JobSystemThreadPool_new3( - uint inMaxJobs, - uint inMaxBarriers, - int inNumThreads) -{ - return to_jpc(new JPH::JobSystemThreadPool(inMaxJobs, inMaxBarriers, inNumThreads)); -} - -//////////////////////////////////////////////////////////////////////////////// -// BroadPhaseLayerInterface - -class JPC_BroadPhaseLayerInterfaceBridge final : public JPH::BroadPhaseLayerInterface { -public: - explicit JPC_BroadPhaseLayerInterfaceBridge(const void *self, JPC_BroadPhaseLayerInterfaceFns fns) : self(self), fns(fns) {} - - virtual uint GetNumBroadPhaseLayers() const override { - return fns.GetNumBroadPhaseLayers(self); - } - - virtual JPH::BroadPhaseLayer GetBroadPhaseLayer(JPH::ObjectLayer inLayer) const override { - return to_jph(fns.GetBroadPhaseLayer(self, inLayer)); - } - -#if defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED) - virtual const char * GetBroadPhaseLayerName([[maybe_unused]] JPH::BroadPhaseLayer inLayer) const override { - return "FIXME"; - } -#endif - -private: - const void* self; - JPC_BroadPhaseLayerInterfaceFns fns; -}; - -OPAQUE_WRAPPER(JPC_BroadPhaseLayerInterface, JPC_BroadPhaseLayerInterfaceBridge) -DESTRUCTOR(JPC_BroadPhaseLayerInterface) - -JPC_API JPC_BroadPhaseLayerInterface* JPC_BroadPhaseLayerInterface_new( - const void *self, - JPC_BroadPhaseLayerInterfaceFns fns) -{ - return to_jpc(new JPC_BroadPhaseLayerInterfaceBridge(self, fns)); -} - -//////////////////////////////////////////////////////////////////////////////// -// ObjectVsBroadPhaseLayerFilter - -class JPC_ObjectVsBroadPhaseLayerFilterBridge final : public JPH::ObjectVsBroadPhaseLayerFilter { -public: - explicit JPC_ObjectVsBroadPhaseLayerFilterBridge(const void *self, JPC_ObjectVsBroadPhaseLayerFilterFns fns) : self(self), fns(fns) {} - - virtual bool ShouldCollide(JPH::ObjectLayer inLayer1, JPH::BroadPhaseLayer inLayer2) const override { - return fns.ShouldCollide(self, inLayer1, to_jpc(inLayer2)); - } - -private: - const void* self; - JPC_ObjectVsBroadPhaseLayerFilterFns fns; -}; - -OPAQUE_WRAPPER(JPC_ObjectVsBroadPhaseLayerFilter, JPC_ObjectVsBroadPhaseLayerFilterBridge) -DESTRUCTOR(JPC_ObjectVsBroadPhaseLayerFilter) - -JPC_API JPC_ObjectVsBroadPhaseLayerFilter* JPC_ObjectVsBroadPhaseLayerFilter_new( - const void *self, - JPC_ObjectVsBroadPhaseLayerFilterFns fns) -{ - return to_jpc(new JPC_ObjectVsBroadPhaseLayerFilterBridge(self, fns)); -} - -//////////////////////////////////////////////////////////////////////////////// -// BroadPhaseLayerFilter - -class JPC_BroadPhaseLayerFilterBridge final : public JPH::BroadPhaseLayerFilter { -public: - explicit JPC_BroadPhaseLayerFilterBridge(const void *self, JPC_BroadPhaseLayerFilterFns fns) : self(self), fns(fns) {} - - virtual bool ShouldCollide(JPH::BroadPhaseLayer inLayer) const override { - return fns.ShouldCollide(self, to_jpc(inLayer)); - } - -private: - const void* self; - JPC_BroadPhaseLayerFilterFns fns; -}; - -OPAQUE_WRAPPER(JPC_BroadPhaseLayerFilter, JPC_BroadPhaseLayerFilterBridge) -DESTRUCTOR(JPC_BroadPhaseLayerFilter) - -JPC_API JPC_BroadPhaseLayerFilter* JPC_BroadPhaseLayerFilter_new( - const void *self, - JPC_BroadPhaseLayerFilterFns fns) -{ - return to_jpc(new JPC_BroadPhaseLayerFilterBridge(self, fns)); -} - -//////////////////////////////////////////////////////////////////////////////// -// ObjectLayerFilter - -class JPC_ObjectLayerFilterBridge final : public JPH::ObjectLayerFilter { -public: - explicit JPC_ObjectLayerFilterBridge(const void *self, JPC_ObjectLayerFilterFns fns) : self(self), fns(fns) {} - - virtual bool ShouldCollide(JPH::ObjectLayer inLayer) const override { - return fns.ShouldCollide(self, inLayer); - } - -private: - const void* self; - JPC_ObjectLayerFilterFns fns; -}; - -OPAQUE_WRAPPER(JPC_ObjectLayerFilter, JPC_ObjectLayerFilterBridge) -DESTRUCTOR(JPC_ObjectLayerFilter) - -JPC_API JPC_ObjectLayerFilter* JPC_ObjectLayerFilter_new( - const void *self, - JPC_ObjectLayerFilterFns fns) -{ - return to_jpc(new JPC_ObjectLayerFilterBridge(self, fns)); -} - -//////////////////////////////////////////////////////////////////////////////// -// BodyFilter - -class JPC_BodyFilterBridge final : public JPH::BodyFilter { -public: - explicit JPC_BodyFilterBridge(const void *self, JPC_BodyFilterFns fns) : self(self), fns(fns) {} - - virtual bool ShouldCollide(const JPH::BodyID &inBodyID) const override { - return fns.ShouldCollide(self, to_jpc(inBodyID)); - } - - virtual bool ShouldCollideLocked(const JPH::Body &inBody) const override { - return fns.ShouldCollideLocked(self, to_jpc(&inBody)); - } - -private: - const void* self; - JPC_BodyFilterFns fns; -}; - -OPAQUE_WRAPPER(JPC_BodyFilter, JPC_BodyFilterBridge) -DESTRUCTOR(JPC_BodyFilter) - -JPC_API JPC_BodyFilter* JPC_BodyFilter_new( - const void *self, - JPC_BodyFilterFns fns) -{ - return to_jpc(new JPC_BodyFilterBridge(self, fns)); -} - -//////////////////////////////////////////////////////////////////////////////// -// JPC_ObjectLayerPairFilter - -class JPC_ObjectLayerPairFilterBridge final : public JPH::ObjectLayerPairFilter { -public: - explicit JPC_ObjectLayerPairFilterBridge(const void *self, JPC_ObjectLayerPairFilterFns fns) : self(self), fns(fns) {} - - virtual bool ShouldCollide(JPH::ObjectLayer inLayer1, JPH::ObjectLayer inLayer2) const override { - return fns.ShouldCollide(self, inLayer1, inLayer2); - } - -private: - const void* self; - JPC_ObjectLayerPairFilterFns fns; -}; - -OPAQUE_WRAPPER(JPC_ObjectLayerPairFilter, JPC_ObjectLayerPairFilterBridge) -DESTRUCTOR(JPC_ObjectLayerPairFilter) - -JPC_API JPC_ObjectLayerPairFilter* JPC_ObjectLayerPairFilter_new( - const void *self, - JPC_ObjectLayerPairFilterFns fns) -{ - return to_jpc(new JPC_ObjectLayerPairFilterBridge(self, fns)); -} - -//////////////////////////////////////////////////////////////////////////////// -// BodyManager::DrawSettings - -JPC_API void JPC_BodyManager_DrawSettings_default(JPC_BodyManager_DrawSettings* object) { - *object = to_jpc(JPH::BodyManager::DrawSettings()); -} - -//////////////////////////////////////////////////////////////////////////////// -// DebugRendererSimple - -class JPC_DebugRendererSimpleBridge final : public JPH::DebugRendererSimple { -public: - explicit JPC_DebugRendererSimpleBridge(const void *self, JPC_DebugRendererSimpleFns fns) : self(self), fns(fns) {} - - virtual void DrawLine(JPH::RVec3Arg inFrom, JPH::RVec3Arg inTo, JPH::ColorArg inColor) override { - fns.DrawLine(self, to_jpc(inFrom), to_jpc(inTo), to_jpc(inColor)); - } - - virtual void DrawText3D( - [[maybe_unused]] JPH::RVec3Arg inPosition, - [[maybe_unused]] const std::string_view &inString, - [[maybe_unused]] JPH::ColorArg inColor = JPH::Color::sWhite, - [[maybe_unused]] float inHeight = 0.5f) override - { - // TODO - } - -private: - const void* self; - JPC_DebugRendererSimpleFns fns; -}; - -OPAQUE_WRAPPER(JPC_DebugRendererSimple, JPC_DebugRendererSimpleBridge) -DESTRUCTOR(JPC_DebugRendererSimple) - -JPC_API JPC_DebugRendererSimple* JPC_DebugRendererSimple_new( - const void *self, - JPC_DebugRendererSimpleFns fns) -{ - return to_jpc(new JPC_DebugRendererSimpleBridge(self, fns)); -} - -//////////////////////////////////////////////////////////////////////////////// -// String - -JPC_API const char* JPC_String_c_str(JPC_String* self) { - return to_jph(self)->c_str(); -} - -//////////////////////////////////////////////////////////////////////////////// -// Shape - -JPC_API uint32_t JPC_Shape_GetRefCount(const JPC_Shape* self) { - return to_jph(self)->GetRefCount(); -} - -JPC_API void JPC_Shape_AddRef(const JPC_Shape* self) { - to_jph(self)->AddRef(); -} - -JPC_API void JPC_Shape_Release(const JPC_Shape* self) { - to_jph(self)->Release(); -} - -JPC_API JPC_Vec3 JPC_Shape_GetCenterOfMass(const JPC_Shape* self) { - return to_jpc(to_jph(self)->GetCenterOfMass()); -} - -//////////////////////////////////////////////////////////////////////////////// -// ShapeSettings - -// Unpack a ShapeResult into a bool and two pointers to be friendlier to C. -static bool HandleShapeResult(JPH::ShapeSettings::ShapeResult res, JPC_Shape** outShape, JPC_String** outError) { - if (res.HasError()) { - if (outError != nullptr) { - JPH::String* created = new JPH::String(std::move(res.GetError())); - *outError = to_jpc(created); - } - - return false; - } else { - JPH::Ref shape = res.Get(); - shape->AddRef(); - *outShape = to_jpc((JPH::Shape*)shape); - - return true; - } -} - -//////////////////////////////////////////////////////////////////////////////// -// TriangleShapeSettings - -static void to_jph(const JPC_TriangleShapeSettings* input, JPH::TriangleShapeSettings* output) { - output->mUserData = input->UserData; - - // TODO: Material - output->mDensity = input->Density; - - output->mV1 = to_jph(input->V1); - output->mV2 = to_jph(input->V2); - output->mV3 = to_jph(input->V3); - output->mConvexRadius = input->ConvexRadius; -} - -JPC_API void JPC_TriangleShapeSettings_default(JPC_TriangleShapeSettings* object) { - object->UserData = 0; - - // TODO: Material - object->Density = 1000.0; - - object->V1 = {0}; - object->V2 = {0}; - object->V3 = {0}; - object->ConvexRadius = 0.0; -} - -JPC_API bool JPC_TriangleShapeSettings_Create(const JPC_TriangleShapeSettings* self, JPC_Shape** outShape, JPC_String** outError) { - JPH::TriangleShapeSettings settings; - to_jph(self, &settings); - - return HandleShapeResult(settings.Create(), outShape, outError); -} - -//////////////////////////////////////////////////////////////////////////////// -// BoxShapeSettings - -static void to_jph(const JPC_BoxShapeSettings* input, JPH::BoxShapeSettings* output) { - output->mUserData = input->UserData; - - // TODO: Material - output->mDensity = input->Density; - - output->mHalfExtent = to_jph(input->HalfExtent); - output->mConvexRadius = input->ConvexRadius; -} - -JPC_API void JPC_BoxShapeSettings_default(JPC_BoxShapeSettings* object) { - object->UserData = 0; - - // TODO: Material - object->Density = 1000.0; - - object->HalfExtent = JPC_Vec3{0}; - object->ConvexRadius = 0.0; -} - -JPC_API bool JPC_BoxShapeSettings_Create(const JPC_BoxShapeSettings* self, JPC_Shape** outShape, JPC_String** outError) { - JPH::BoxShapeSettings settings; - to_jph(self, &settings); - - return HandleShapeResult(settings.Create(), outShape, outError); -} - -//////////////////////////////////////////////////////////////////////////////// -// SphereShapeSettings - -static void to_jph(const JPC_SphereShapeSettings* input, JPH::SphereShapeSettings* output) { - output->mUserData = input->UserData; - - // TODO: Material - output->mDensity = input->Density; - - output->mRadius = input->Radius; -} - -JPC_API void JPC_SphereShapeSettings_default(JPC_SphereShapeSettings* object) { - object->UserData = 0; - - // TODO: Material - object->Density = 1000.0; - - object->Radius = 0.0; -} - -JPC_API bool JPC_SphereShapeSettings_Create(const JPC_SphereShapeSettings* self, JPC_Shape** outShape, JPC_String** outError) { - JPH::SphereShapeSettings settings; - to_jph(self, &settings); - - return HandleShapeResult(settings.Create(), outShape, outError); -} - -//////////////////////////////////////////////////////////////////////////////// -// CapsuleShapeSettings - -static void to_jph(const JPC_CapsuleShapeSettings* input, JPH::CapsuleShapeSettings* output) { - output->mUserData = input->UserData; - - // TODO: Material - output->mDensity = input->Density; - - output->mRadius = input->Radius; - output->mHalfHeightOfCylinder = input->HalfHeightOfCylinder; -} - -JPC_API void JPC_CapsuleShapeSettings_default(JPC_CapsuleShapeSettings* object) { - object->UserData = 0; - - // TODO: Material - object->Density = 1000.0; - - object->Radius = 0.0; - object->HalfHeightOfCylinder = 0.0; -} - -JPC_API bool JPC_CapsuleShapeSettings_Create(const JPC_CapsuleShapeSettings* self, JPC_Shape** outShape, JPC_String** outError) { - JPH::CapsuleShapeSettings settings; - to_jph(self, &settings); - - return HandleShapeResult(settings.Create(), outShape, outError); -} - -//////////////////////////////////////////////////////////////////////////////// -// CylinderShapeSettings - -static void to_jph(const JPC_CylinderShapeSettings* input, JPH::CylinderShapeSettings* output) { - output->mUserData = input->UserData; - - // TODO: Material - output->mDensity = input->Density; - - output->mHalfHeight = input->HalfHeight; - output->mRadius = input->Radius; - output->mConvexRadius = input->ConvexRadius; -} - -JPC_API void JPC_CylinderShapeSettings_default(JPC_CylinderShapeSettings* object) { - object->UserData = 0; - - // TODO: Material - object->Density = 1000.0; - - object->HalfHeight = 0.0; - object->Radius = 0.0; - object->ConvexRadius = 0.0; -} - -JPC_API bool JPC_CylinderShapeSettings_Create(const JPC_CylinderShapeSettings* self, JPC_Shape** outShape, JPC_String** outError) { - JPH::CylinderShapeSettings settings; - to_jph(self, &settings); - - return HandleShapeResult(settings.Create(), outShape, outError); -} - -//////////////////////////////////////////////////////////////////////////////// -// ConvexHullShapeSettings - -static void to_jph(const JPC_ConvexHullShapeSettings* input, JPH::ConvexHullShapeSettings* output) { - output->mUserData = input->UserData; - - // TODO: Material - output->mDensity = input->Density; - - output->mPoints = to_jph(input->Points, input->PointsLen); - output->mMaxConvexRadius = input->MaxConvexRadius; - output->mMaxErrorConvexRadius = input->MaxErrorConvexRadius; - output->mHullTolerance = input->HullTolerance; -} - -JPC_API void JPC_ConvexHullShapeSettings_default(JPC_ConvexHullShapeSettings* object) { - object->UserData = 0; - - // TODO: Material - object->Density = 1000.0; - - object->Points = nullptr; - object->PointsLen = 0; - object->MaxConvexRadius = 0.0; - object->MaxErrorConvexRadius = 0.05f; - object->HullTolerance = 1.0e-3f; -} - -JPC_API bool JPC_ConvexHullShapeSettings_Create(const JPC_ConvexHullShapeSettings* self, JPC_Shape** outShape, JPC_String** outError) { - JPH::ConvexHullShapeSettings settings; - to_jph(self, &settings); - - return HandleShapeResult(settings.Create(), outShape, outError); -} - -//////////////////////////////////////////////////////////////////////////////// -// CompoundShape::SubShapeSettings - -static JPH::CompoundShapeSettings::SubShapeSettings to_jph(const JPC_SubShapeSettings* input) { - const JPH::Shape* shape = to_jph(input->Shape); - - JPH::CompoundShapeSettings::SubShapeSettings output; - output.mShape = nullptr; - output.mShapePtr = shape; - output.mPosition = to_jph(input->Position); - output.mRotation = to_jph(input->Rotation); - output.mUserData = input->UserData; - return output; -} - -static JPH::Array to_jph(const JPC_SubShapeSettings* src, size_t n) { - JPH::Array vec; - vec.reserve(n); - - for (size_t i = 0; i < n; i++) { - vec.push_back(to_jph(&src[i])); - } - - return vec; -} - -JPC_API void JPC_SubShapeSettings_default(JPC_SubShapeSettings* object) { - object->Shape = nullptr; - object->Position = JPC_Vec3{0}; - object->Rotation = JPC_Quat{0, 0, 0, 1}; - object->UserData = 0; -} - -//////////////////////////////////////////////////////////////////////////////// -// StaticCompoundShapeSettings -> CompoundShapeSettings -> ShapeSettings - -static void to_jph(const JPC_StaticCompoundShapeSettings* input, JPH::StaticCompoundShapeSettings* output) { - output->mUserData = input->UserData; - - output->mSubShapes = to_jph(input->SubShapes, input->SubShapesLen); -} - -JPC_API void JPC_StaticCompoundShapeSettings_default(JPC_StaticCompoundShapeSettings* object) { - object->UserData = 0; - - object->SubShapes = nullptr; - object->SubShapesLen = 0; -} - -JPC_API bool JPC_StaticCompoundShapeSettings_Create(const JPC_StaticCompoundShapeSettings* self, JPC_Shape** outShape, JPC_String** outError) { - JPH::StaticCompoundShapeSettings settings; - to_jph(self, &settings); - - return HandleShapeResult(settings.Create(), outShape, outError); -} - -//////////////////////////////////////////////////////////////////////////////// -// MutableCompoundShapeSettings -> CompoundShapeSettings -> ShapeSettings - -static void to_jph(const JPC_MutableCompoundShapeSettings* input, JPH::MutableCompoundShapeSettings* output) { - output->mUserData = input->UserData; - - output->mSubShapes = to_jph(input->SubShapes, input->SubShapesLen); -} - -JPC_API void JPC_MutableCompoundShapeSettings_default(JPC_MutableCompoundShapeSettings* object) { - object->UserData = 0; - - object->SubShapes = nullptr; - object->SubShapesLen = 0; -} - -JPC_API bool JPC_MutableCompoundShapeSettings_Create(const JPC_MutableCompoundShapeSettings* self, JPC_Shape** outShape, JPC_String** outError) { - JPH::MutableCompoundShapeSettings settings; - to_jph(self, &settings); - - return HandleShapeResult(settings.Create(), outShape, outError); -} - -//////////////////////////////////////////////////////////////////////////////// -// BodyCreationSettings - -static JPH::BodyCreationSettings to_jph(const JPC_BodyCreationSettings* settings) { - JPH::BodyCreationSettings output{}; - - output.mPosition = to_jph(settings->Position); - output.mRotation = to_jph(settings->Rotation); - output.mLinearVelocity = to_jph(settings->LinearVelocity); - output.mAngularVelocity = to_jph(settings->AngularVelocity); - output.mUserData = settings->UserData; - output.mObjectLayer = settings->ObjectLayer; - // CollisionGroup - output.mMotionType = to_jph(settings->MotionType); - output.mAllowedDOFs = to_jph(settings->AllowedDOFs); - output.mAllowDynamicOrKinematic = settings->AllowDynamicOrKinematic; - output.mIsSensor = settings->IsSensor; - output.mCollideKinematicVsNonDynamic = settings->CollideKinematicVsNonDynamic; - output.mUseManifoldReduction = settings->UseManifoldReduction; - output.mApplyGyroscopicForce = settings->ApplyGyroscopicForce; - output.mMotionQuality = to_jph(settings->MotionQuality); - output.mEnhancedInternalEdgeRemoval = settings->EnhancedInternalEdgeRemoval; - output.mAllowSleeping = settings->AllowSleeping; - output.mFriction = settings->Friction; - output.mRestitution = settings->Restitution; - output.mLinearDamping = settings->LinearDamping; - output.mAngularDamping = settings->AngularDamping; - output.mMaxLinearVelocity = settings->MaxLinearVelocity; - output.mMaxAngularVelocity = settings->MaxAngularVelocity; - output.mGravityFactor = settings->GravityFactor; - output.mNumVelocityStepsOverride = settings->NumVelocityStepsOverride; - output.mNumPositionStepsOverride = settings->NumPositionStepsOverride; - output.mOverrideMassProperties = to_jph(settings->OverrideMassProperties); - output.mInertiaMultiplier = settings->InertiaMultiplier; - // output.mMassPropertiesOverride = settings->MassPropertiesOverride; - output.SetShape(to_jph(settings->Shape)); - - return output; -} - -JPC_API void JPC_BodyCreationSettings_default(JPC_BodyCreationSettings* settings) { - settings->Position = JPC_RVec3{0, 0, 0}; - settings->Rotation = JPC_Quat{0, 0, 0, 1}; - settings->LinearVelocity = JPC_Vec3{0, 0, 0}; - settings->AngularVelocity = JPC_Vec3{0, 0, 0}; - settings->UserData = 0; - settings->ObjectLayer = 0; - // CollisionGroup - settings->MotionType = JPC_MOTION_TYPE_DYNAMIC; - settings->AllowedDOFs = JPC_ALLOWED_DOFS_ALL; - settings->AllowDynamicOrKinematic = false; - settings->IsSensor = false; - settings->CollideKinematicVsNonDynamic = false; - settings->UseManifoldReduction = true; - settings->ApplyGyroscopicForce = false; - settings->MotionQuality = JPC_MOTION_QUALITY_DISCRETE; - settings->EnhancedInternalEdgeRemoval = false; - settings->AllowSleeping = true; - settings->Friction = 0.2f; - settings->Restitution = 0.0f; - settings->LinearDamping = 0.05f; - settings->AngularDamping = 0.05f; - settings->MaxLinearVelocity = 500.0f; - settings->MaxAngularVelocity = 0.25f * JPC_PI * 60.0f; - settings->GravityFactor = 1.0f; - settings->NumVelocityStepsOverride = 0; - settings->NumPositionStepsOverride = 0; - settings->OverrideMassProperties = JPC_OVERRIDE_MASS_PROPS_CALC_MASS_INERTIA; - settings->InertiaMultiplier = 1.0f; - // MassPropertiesOverride -} - -//////////////////////////////////////////////////////////////////////////////// -// Body - -JPC_API JPC_BodyID JPC_Body_GetID(const JPC_Body* self) { - return to_jpc(to_jph(self)->GetID()); -} - -JPC_API JPC_BodyType JPC_Body_GetBodyType(const JPC_Body* self) { - return to_jpc(to_jph(self)->GetBodyType()); -} - -JPC_API bool JPC_Body_IsRigidBody(const JPC_Body* self) { - return to_jph(self)->IsRigidBody(); -} - -JPC_API bool JPC_Body_IsSoftBody(const JPC_Body* self) { - return to_jph(self)->IsSoftBody(); -} - -JPC_API bool JPC_Body_IsActive(const JPC_Body* self) { - return to_jph(self)->IsActive(); -} - -JPC_API bool JPC_Body_IsStatic(const JPC_Body* self) { - return to_jph(self)->IsStatic(); -} - -JPC_API bool JPC_Body_IsKinematic(const JPC_Body* self) { - return to_jph(self)->IsKinematic(); -} - -JPC_API bool JPC_Body_IsDynamic(const JPC_Body* self) { - return to_jph(self)->IsDynamic(); -} - -JPC_API bool JPC_Body_CanBeKinematicOrDynamic(const JPC_Body* self) { - return to_jph(self)->CanBeKinematicOrDynamic(); -} - -JPC_API void JPC_Body_SetIsSensor(JPC_Body* self, bool inIsSensor) { - to_jph(self)->SetIsSensor(inIsSensor); -} - -JPC_API bool JPC_Body_IsSensor(const JPC_Body* self) { - return to_jph(self)->IsSensor(); -} - -JPC_API void JPC_Body_SetCollideKinematicVsNonDynamic(JPC_Body* self, bool inCollide) { - to_jph(self)->SetCollideKinematicVsNonDynamic(inCollide); -} - -JPC_API bool JPC_Body_GetCollideKinematicVsNonDynamic(const JPC_Body* self) { - return to_jph(self)->GetCollideKinematicVsNonDynamic(); -} - -JPC_API void JPC_Body_SetUseManifoldReduction(JPC_Body* self, bool inUseReduction) { - to_jph(self)->SetUseManifoldReduction(inUseReduction); -} - -JPC_API bool JPC_Body_GetUseManifoldReduction(const JPC_Body* self) { - return to_jph(self)->GetUseManifoldReduction(); -} - -JPC_API bool JPC_Body_GetUseManifoldReductionWithBody(const JPC_Body* self, const JPC_Body* inBody2) { - return to_jph(self)->GetUseManifoldReductionWithBody(*to_jph(inBody2)); -} - -JPC_API void JPC_Body_SetApplyGyroscopicForce(JPC_Body* self, bool inApply) { - to_jph(self)->SetApplyGyroscopicForce(inApply); -} - -JPC_API bool JPC_Body_GetApplyGyroscopicForce(const JPC_Body* self) { - return to_jph(self)->GetApplyGyroscopicForce(); -} - -JPC_API void JPC_Body_SetEnhancedInternalEdgeRemoval(JPC_Body* self, bool inApply) { - to_jph(self)->SetEnhancedInternalEdgeRemoval(inApply); -} - -JPC_API bool JPC_Body_GetEnhancedInternalEdgeRemoval(const JPC_Body* self) { - return to_jph(self)->GetEnhancedInternalEdgeRemoval(); -} - -JPC_API bool JPC_Body_GetEnhancedInternalEdgeRemovalWithBody(const JPC_Body* self, const JPC_Body* inBody2) { - return to_jph(self)->GetEnhancedInternalEdgeRemovalWithBody(*to_jph(inBody2)); -} - -JPC_API JPC_MotionType JPC_Body_GetMotionType(const JPC_Body* self) { - return to_jpc(to_jph(self)->GetMotionType()); -} - -JPC_API void JPC_Body_SetMotionType(JPC_Body* self, JPC_MotionType inMotionType) { - to_jph(self)->SetMotionType(to_jph(inMotionType)); -} - -JPC_API JPC_BroadPhaseLayer JPC_Body_GetBroadPhaseLayer(const JPC_Body* self) { - return to_jpc(to_jph(self)->GetBroadPhaseLayer()); -} - -JPC_API JPC_ObjectLayer JPC_Body_GetObjectLayer(const JPC_Body* self) { - return to_jph(self)->GetObjectLayer(); -} - -// JPC_API const CollisionGroup & JPC_Body_GetCollisionGroup(const JPC_Body* self); -// JPC_API CollisionGroup & JPC_Body_GetCollisionGroup(JPC_Body* self); -// JPC_API void JPC_Body_SetCollisionGroup(JPC_Body* self, const CollisionGroup &inGroup); - -JPC_API bool JPC_Body_GetAllowSleeping(const JPC_Body* self) { - return to_jph(self)->GetAllowSleeping(); -} - -JPC_API void JPC_Body_SetAllowSleeping(JPC_Body* self, bool inAllow) { - to_jph(self)->SetAllowSleeping(inAllow); -} - -JPC_API void JPC_Body_ResetSleepTimer(JPC_Body* self) { - to_jph(self)->ResetSleepTimer(); -} - -JPC_API float JPC_Body_GetFriction(const JPC_Body* self) { - return to_jph(self)->GetFriction(); -} - -JPC_API void JPC_Body_SetFriction(JPC_Body* self, float inFriction) { - to_jph(self)->SetFriction(inFriction); -} - -JPC_API float JPC_Body_GetRestitution(const JPC_Body* self) { - return to_jph(self)->GetRestitution(); -} - -JPC_API void JPC_Body_SetRestitution(JPC_Body* self, float inRestitution) { - to_jph(self)->SetRestitution(inRestitution); -} - -JPC_API JPC_Vec3 JPC_Body_GetLinearVelocity(const JPC_Body* self) { - return to_jpc(to_jph(self)->GetLinearVelocity()); -} - -JPC_API void JPC_Body_SetLinearVelocity(JPC_Body* self, JPC_Vec3 inLinearVelocity) { - to_jph(self)->SetLinearVelocity(to_jph(inLinearVelocity)); -} - -JPC_API void JPC_Body_SetLinearVelocityClamped(JPC_Body* self, JPC_Vec3 inLinearVelocity) { - to_jph(self)->SetLinearVelocityClamped(to_jph(inLinearVelocity)); -} - -JPC_API JPC_Vec3 JPC_Body_GetAngularVelocity(const JPC_Body* self) { - return to_jpc(to_jph(self)->GetAngularVelocity()); -} - -JPC_API void JPC_Body_SetAngularVelocity(JPC_Body* self, JPC_Vec3 inAngularVelocity) { - to_jph(self)->SetAngularVelocity(to_jph(inAngularVelocity)); -} - -JPC_API void JPC_Body_SetAngularVelocityClamped(JPC_Body* self, JPC_Vec3 inAngularVelocity) { - to_jph(self)->SetAngularVelocityClamped(to_jph(inAngularVelocity)); -} - -JPC_API JPC_Vec3 JPC_Body_GetPointVelocityCOM(const JPC_Body* self, JPC_Vec3 inPointRelativeToCOM) { - return to_jpc(to_jph(self)->GetPointVelocityCOM(to_jph(inPointRelativeToCOM))); -} - -JPC_API JPC_Vec3 JPC_Body_GetPointVelocity(const JPC_Body* self, JPC_RVec3 inPoint) { - return to_jpc(to_jph(self)->GetPointVelocity(to_jph(inPoint))); -} - -// JPC_API void JPC_Body_AddForce(JPC_Body* self, JPC_Vec3 inForce); -// JPC_API void JPC_Body_AddForce(JPC_Body* self, JPC_Vec3 inForce, JPC_RVec3 inPosition); - -JPC_API void JPC_Body_AddTorque(JPC_Body* self, JPC_Vec3 inTorque) { - to_jph(self)->AddTorque(to_jph(inTorque)); -} - -JPC_API JPC_Vec3 JPC_Body_GetAccumulatedForce(const JPC_Body* self) { - return to_jpc(to_jph(self)->GetAccumulatedForce()); -} - -JPC_API JPC_Vec3 JPC_Body_GetAccumulatedTorque(const JPC_Body* self) { - return to_jpc(to_jph(self)->GetAccumulatedTorque()); -} - -JPC_API void JPC_Body_ResetForce(JPC_Body* self) { - to_jph(self)->ResetForce(); -} - -JPC_API void JPC_Body_ResetTorque(JPC_Body* self) { - to_jph(self)->ResetTorque(); -} - -JPC_API void JPC_Body_ResetMotion(JPC_Body* self) { - to_jph(self)->ResetMotion(); -} - -JPC_API void JPC_Body_GetInverseInertia(const JPC_Body* self, JPC_Mat44* outMatrix) { - to_jph(self)->GetInverseInertia().StoreFloat4x4(reinterpret_cast(outMatrix)); -} - -JPC_API void JPC_Body_AddImpulse(JPC_Body* self, JPC_Vec3 inImpulse) { - to_jph(self)->AddImpulse(to_jph(inImpulse)); -} - -JPC_API void JPC_Body_AddImpulse2(JPC_Body* self, JPC_Vec3 inImpulse, JPC_RVec3 inPosition) { - to_jph(self)->AddImpulse(to_jph(inImpulse), to_jph(inPosition)); -} - -JPC_API void JPC_Body_AddAngularImpulse(JPC_Body* self, JPC_Vec3 inAngularImpulse) { - to_jph(self)->AddAngularImpulse(to_jph(inAngularImpulse)); -} - -JPC_API void JPC_Body_MoveKinematic(JPC_Body* self, JPC_RVec3 inTargetPosition, JPC_Quat inTargetRotation, float inDeltaTime) { - to_jph(self)->MoveKinematic(to_jph(inTargetPosition), to_jph(inTargetRotation), inDeltaTime); -} - -JPC_API bool JPC_Body_ApplyBuoyancyImpulse(JPC_Body* self, JPC_RVec3 inSurfacePosition, JPC_Vec3 inSurfaceNormal, float inBuoyancy, float inLinearDrag, float inAngularDrag, JPC_Vec3 inFluidVelocity, JPC_Vec3 inGravity, float inDeltaTime) { - return to_jph(self)->ApplyBuoyancyImpulse(to_jph(inSurfacePosition), to_jph(inSurfaceNormal), inBuoyancy, inLinearDrag, inAngularDrag, to_jph(inFluidVelocity), to_jph(inGravity), inDeltaTime); -} - -JPC_API bool JPC_Body_IsInBroadPhase(const JPC_Body* self) { - return to_jph(self)->IsInBroadPhase(); -} - -JPC_API bool JPC_Body_IsCollisionCacheInvalid(const JPC_Body* self) { - return to_jph(self)->IsCollisionCacheInvalid(); -} - -JPC_API const JPC_Shape* JPC_Body_GetShape(const JPC_Body* self) { - return to_jpc(to_jph(self)->GetShape()); -} - -JPC_API JPC_RVec3 JPC_Body_GetPosition(const JPC_Body* self) { - return to_jpc(to_jph(self)->GetPosition()); -} - -JPC_API JPC_Quat JPC_Body_GetRotation(const JPC_Body* self) { - return to_jpc(to_jph(self)->GetRotation()); -} - -// JPC_API RMat44 JPC_Body_GetWorldTransform(const JPC_Body* self); - -JPC_API JPC_RVec3 JPC_Body_GetCenterOfMassPosition(const JPC_Body* self) { - return to_jpc(to_jph(self)->GetCenterOfMassPosition()); -} - -// JPC_API RMat44 JPC_Body_GetCenterOfMassTransform(const JPC_Body* self); -// JPC_API RMat44 JPC_Body_GetInverseCenterOfMassTransform(const JPC_Body* self); -// JPC_API const AABox & JPC_Body_GetWorldSpaceBounds(const JPC_Body* self); -// JPC_API const MotionProperties *JPC_Body_GetMotionProperties(const JPC_Body* self) -// JPC_API MotionProperties * JPC_Body_GetMotionProperties(JPC_Body* self); -// JPC_API const MotionProperties *JPC_Body_GetMotionPropertiesUnchecked(const JPC_Body* self) -// JPC_API MotionProperties * JPC_Body_GetMotionPropertiesUnchecked(JPC_Body* self); - -JPC_API uint64_t JPC_Body_GetUserData(const JPC_Body* self) { - return to_jph(self)->GetUserData(); -} - -JPC_API void JPC_Body_SetUserData(JPC_Body* self, uint64_t inUserData) { - to_jph(self)->SetUserData(inUserData); -} - -// JPC_API JPC_Vec3 JPC_Body_GetWorldSpaceSurfaceNormal(const JPC_Body* self, const SubShapeID &inSubShapeID, JPC_RVec3 inPosition); -// JPC_API TransformedShape JPC_Body_GetTransformedShape(const JPC_Body* self); -// JPC_API BodyCreationSettings JPC_Body_GetBodyCreationSettings(const JPC_Body* self); -// JPC_API SoftBodyCreationSettings JPC_Body_GetSoftBodyCreationSettings(const JPC_Body* self); - -//////////////////////////////////////////////////////////////////////////////// -// BodyInterface - -JPC_API JPC_Body* JPC_BodyInterface_CreateBody(JPC_BodyInterface* self, const JPC_BodyCreationSettings* inSettings) { - return to_jpc(to_jph(self)->CreateBody(to_jph(inSettings))); -} - -// JPC_API JPC_Body* JPC_BodyInterface_CreateSoftBody(JPC_BodyInterface *self, const SoftBodyCreationSettings &inSettings); - -JPC_API JPC_Body* JPC_BodyInterface_CreateBodyWithID(JPC_BodyInterface *self, JPC_BodyID inBodyID, const JPC_BodyCreationSettings* inSettings) { - return to_jpc(to_jph(self)->CreateBodyWithID(to_jph(inBodyID), to_jph(inSettings))); -} - -// JPC_API JPC_Body* JPC_BodyInterface_CreateSoftBodyWithID(JPC_BodyInterface *self, JPC_BodyID inBodyID, const SoftBodyCreationSettings* inSettings); - -JPC_API JPC_Body* JPC_BodyInterface_CreateBodyWithoutID(const JPC_BodyInterface *self, const JPC_BodyCreationSettings* inSettings) { - return to_jpc(to_jph(self)->CreateBodyWithoutID(to_jph(inSettings))); -} - -// JPC_API JPC_Body* JPC_BodyInterface_CreateSoftBodyWithoutID(const JPC_BodyInterface *self, const SoftBodyCreationSettings* inSettings); - -JPC_API void JPC_BodyInterface_DestroyBodyWithoutID(const JPC_BodyInterface *self, JPC_Body *inBody) { - to_jph(self)->DestroyBodyWithoutID(to_jph(inBody)); -} - -JPC_API bool JPC_BodyInterface_AssignBodyID(JPC_BodyInterface *self, JPC_Body *ioBody) { - return to_jph(self)->AssignBodyID(to_jph(ioBody)); -} - -// JPC_API bool JPC_BodyInterface_AssignBodyID(JPC_BodyInterface *self, JPC_Body *ioBody, JPC_BodyID inBodyID); - -JPC_API JPC_Body* JPC_BodyInterface_UnassignBodyID(JPC_BodyInterface *self, JPC_BodyID inBodyID) { - return to_jpc(to_jph(self)->UnassignBodyID(to_jph(inBodyID))); -} - -// JPC_API void JPC_BodyInterface_UnassignBodyIDs(JPC_BodyInterface *self, const JPC_BodyID *inBodyIDs, int inNumber, JPC_Body **outBodies) { -// return to_jph(self)->UnassignBodyIDs(to_jph(inBodyIDs), inNumber, to_jph(outBodies)); -// } - -JPC_API void JPC_BodyInterface_DestroyBody(JPC_BodyInterface *self, JPC_BodyID inBodyID) { - to_jph(self)->DestroyBody(to_jph(inBodyID)); -} - -// JPC_API void JPC_BodyInterface_DestroyBodies(JPC_BodyInterface *self, const JPC_BodyID *inBodyIDs, int inNumber) { -// return to_jph(self)->DestroyBodies(to_jph(inBodyIDs), int inNumber); -// } - -JPC_API void JPC_BodyInterface_AddBody(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_Activation inActivationMode) { - to_jph(self)->AddBody(to_jph(inBodyID), to_jph(inActivationMode)); -} - -JPC_API void JPC_BodyInterface_RemoveBody(JPC_BodyInterface *self, JPC_BodyID inBodyID) { - to_jph(self)->RemoveBody(to_jph(inBodyID)); -} - -JPC_API bool JPC_BodyInterface_IsAdded(const JPC_BodyInterface *self, JPC_BodyID inBodyID) { - return to_jph(self)->IsAdded(to_jph(inBodyID)); -} - -JPC_API JPC_BodyID JPC_BodyInterface_CreateAndAddBody(JPC_BodyInterface *self, const JPC_BodyCreationSettings* inSettings, JPC_Activation inActivationMode) { - return to_jpc(to_jph(self)->CreateAndAddBody(to_jph(inSettings), to_jph(inActivationMode))); -} - -// JPC_API JPC_BodyID JPC_BodyInterface_CreateAndAddSoftBody(JPC_BodyInterface *self, const SoftBodyCreationSettings &inSettings, JPC_Activation inActivationMode); - -JPC_API void* JPC_BodyInterface_AddBodiesPrepare(JPC_BodyInterface *self, JPC_BodyID *ioBodies, int inNumber) { - return to_jph(self)->AddBodiesPrepare(to_jph(ioBodies), inNumber); -} - -JPC_API void JPC_BodyInterface_AddBodiesFinalize(JPC_BodyInterface *self, JPC_BodyID *ioBodies, int inNumber, void* inAddState, JPC_Activation inActivationMode) { - to_jph(self)->AddBodiesFinalize(to_jph(ioBodies), inNumber, inAddState, to_jph(inActivationMode)); -} - -JPC_API void JPC_BodyInterface_AddBodiesAbort(JPC_BodyInterface *self, JPC_BodyID *ioBodies, int inNumber, void* inAddState) { - to_jph(self)->AddBodiesAbort(to_jph(ioBodies), inNumber, inAddState); -} - -JPC_API void JPC_BodyInterface_RemoveBodies(JPC_BodyInterface *self, JPC_BodyID *ioBodies, int inNumber) { - to_jph(self)->RemoveBodies(to_jph(ioBodies), inNumber); -} - -JPC_API void JPC_BodyInterface_ActivateBody(JPC_BodyInterface *self, JPC_BodyID inBodyID) { - to_jph(self)->ActivateBody(to_jph(inBodyID)); -} - -JPC_API void JPC_BodyInterface_ActivateBodies(JPC_BodyInterface *self, JPC_BodyID *inBodyIDs, int inNumber) { - to_jph(self)->ActivateBodies(to_jph(inBodyIDs), inNumber); -} - -// JPC_API void JPC_BodyInterface_ActivateBodiesInAABox(JPC_BodyInterface *self, const AABox &inBox, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter); - -JPC_API void JPC_BodyInterface_DeactivateBody(JPC_BodyInterface *self, JPC_BodyID inBodyID) { - to_jph(self)->DeactivateBody(to_jph(inBodyID)); -} - -JPC_API void JPC_BodyInterface_DeactivateBodies(JPC_BodyInterface *self, JPC_BodyID *inBodyIDs, int inNumber) { - to_jph(self)->DeactivateBodies(to_jph(inBodyIDs), inNumber); -} - -JPC_API bool JPC_BodyInterface_IsActive(const JPC_BodyInterface *self, JPC_BodyID inBodyID) { - return to_jph(self)->IsActive(to_jph(inBodyID)); -} - -// TwoBodyConstraint * JPC_BodyInterface_CreateConstraint(JPC_BodyInterface *self, const TwoBodyConstraintSettings *inSettings, JPC_BodyID inBodyID1, JPC_BodyID inBodyID2); -// JPC_API void JPC_BodyInterface_ActivateConstraint(JPC_BodyInterface *self, const TwoBodyConstraint *inConstraint); -// RefConst JPC_BodyInterface_GetShape(const JPC_BodyInterface *self, JPC_BodyID inBodyID); - -JPC_API void JPC_BodyInterface_SetShape(const JPC_BodyInterface *self, JPC_BodyID inBodyID, const JPC_Shape *inShape, bool inUpdateMassProperties, JPC_Activation inActivationMode) { - to_jph(self)->SetShape(to_jph(inBodyID), to_jph(inShape), inUpdateMassProperties, to_jph(inActivationMode)); -} - -JPC_API void JPC_BodyInterface_NotifyShapeChanged(const JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_Vec3 inPreviousCenterOfMass, bool inUpdateMassProperties, JPC_Activation inActivationMode) { - to_jph(self)->NotifyShapeChanged(to_jph(inBodyID), to_jph(inPreviousCenterOfMass), inUpdateMassProperties, to_jph(inActivationMode)); -} - -JPC_API void JPC_BodyInterface_SetObjectLayer(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_ObjectLayer inLayer) { - to_jph(self)->SetObjectLayer(to_jph(inBodyID), inLayer); -} - -JPC_API JPC_ObjectLayer JPC_BodyInterface_GetObjectLayer(const JPC_BodyInterface *self, JPC_BodyID inBodyID) { - return to_jph(self)->GetObjectLayer(to_jph(inBodyID)); -} - -JPC_API void JPC_BodyInterface_SetPositionAndRotation(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_RVec3 inPosition, JPC_Quat inRotation, JPC_Activation inActivationMode) { - to_jph(self)->SetPositionAndRotation(to_jph(inBodyID), to_jph(inPosition), to_jph(inRotation), to_jph(inActivationMode)); -} - -JPC_API void JPC_BodyInterface_SetPositionAndRotationWhenChanged(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_RVec3 inPosition, JPC_Quat inRotation, JPC_Activation inActivationMode) { - to_jph(self)->SetPositionAndRotationWhenChanged(to_jph(inBodyID), to_jph(inPosition), to_jph(inRotation), to_jph(inActivationMode)); -} - -JPC_API void JPC_BodyInterface_GetPositionAndRotation(const JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_RVec3 *outPosition, JPC_Quat *outRotation) { - JPH::RVec3 outPos{}; - JPH::Quat outRot{}; - - to_jph(self)->GetPositionAndRotation(to_jph(inBodyID), outPos, outRot); - - *outPosition = to_jpc(outPos); - *outRotation = to_jpc(outRot); -} - -JPC_API void JPC_BodyInterface_SetPosition(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_RVec3 inPosition, JPC_Activation inActivationMode) { - to_jph(self)->SetPosition(to_jph(inBodyID), to_jph(inPosition), to_jph(inActivationMode)); -} - -JPC_API JPC_RVec3 JPC_BodyInterface_GetPosition(const JPC_BodyInterface *self, JPC_BodyID inBodyID) { - return to_jpc(to_jph(self)->GetPosition(to_jph(inBodyID))); -} - -JPC_API JPC_RVec3 JPC_BodyInterface_GetCenterOfMassPosition(const JPC_BodyInterface *self, JPC_BodyID inBodyID) { - return to_jpc(to_jph(self)->GetCenterOfMassPosition(to_jph(inBodyID))); -} - -JPC_API void JPC_BodyInterface_SetRotation(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_Quat inRotation, JPC_Activation inActivationMode) { - to_jph(self)->SetRotation(to_jph(inBodyID), to_jph(inRotation), to_jph(inActivationMode)); -} - -JPC_API JPC_Quat JPC_BodyInterface_GetRotation(const JPC_BodyInterface *self, JPC_BodyID inBodyID) { - return to_jpc(to_jph(self)->GetRotation(to_jph(inBodyID))); -} - -// RMat44 JPC_BodyInterface_GetWorldTransform(const JPC_BodyInterface *self, JPC_BodyID inBodyID); -// RMat44 JPC_BodyInterface_GetCenterOfMassTransform(const JPC_BodyInterface *self, JPC_BodyID inBodyID); - -JPC_API void JPC_BodyInterface_MoveKinematic(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_RVec3 inTargetPosition, JPC_Quat inTargetRotation, float inDeltaTime) { - to_jph(self)->MoveKinematic(to_jph(inBodyID), to_jph(inTargetPosition), to_jph(inTargetRotation), inDeltaTime); -} - -JPC_API void JPC_BodyInterface_SetLinearAndAngularVelocity(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_Vec3 inLinearVelocity, JPC_Vec3 inAngularVelocity) { - to_jph(self)->SetLinearAndAngularVelocity(to_jph(inBodyID), to_jph(inLinearVelocity), to_jph(inAngularVelocity)); -} - -JPC_API void JPC_BodyInterface_GetLinearAndAngularVelocity(const JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_Vec3 *outLinearVelocity, JPC_Vec3 *outAngularVelocity) { - JPH::Vec3 outLinVel; - JPH::Vec3 outAngVel; - - to_jph(self)->GetLinearAndAngularVelocity(to_jph(inBodyID), outLinVel, outAngVel); - - *outLinearVelocity = to_jpc(outLinVel); - *outAngularVelocity = to_jpc(outAngVel); -} - -JPC_API void JPC_BodyInterface_SetLinearVelocity(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_Vec3 inLinearVelocity) { - to_jph(self)->SetLinearVelocity(to_jph(inBodyID), to_jph(inLinearVelocity)); -} - -JPC_API JPC_Vec3 JPC_BodyInterface_GetLinearVelocity(const JPC_BodyInterface *self, JPC_BodyID inBodyID) { - return to_jpc(to_jph(self)->GetLinearVelocity(to_jph(inBodyID))); -} - -JPC_API void JPC_BodyInterface_AddLinearVelocity(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_Vec3 inLinearVelocity) { - to_jph(self)->AddLinearVelocity(to_jph(inBodyID), to_jph(inLinearVelocity)); -} - -JPC_API void JPC_BodyInterface_AddLinearAndAngularVelocity(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_Vec3 inLinearVelocity, JPC_Vec3 inAngularVelocity) { - to_jph(self)->AddLinearAndAngularVelocity(to_jph(inBodyID), to_jph(inLinearVelocity), to_jph(inAngularVelocity)); -} - -JPC_API void JPC_BodyInterface_SetAngularVelocity(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_Vec3 inAngularVelocity) { - to_jph(self)->SetAngularVelocity(to_jph(inBodyID), to_jph(inAngularVelocity)); -} - -JPC_API JPC_Vec3 JPC_BodyInterface_GetAngularVelocity(const JPC_BodyInterface *self, JPC_BodyID inBodyID) { - return to_jpc(to_jph(self)->GetAngularVelocity(to_jph(inBodyID))); -} - -JPC_API JPC_Vec3 JPC_BodyInterface_GetPointVelocity(const JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_RVec3 inPoint) { - return to_jpc(to_jph(self)->GetPointVelocity(to_jph(inBodyID), to_jph(inPoint))); -} - -JPC_API void JPC_BodyInterface_SetPositionRotationAndVelocity(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_RVec3 inPosition, JPC_Quat inRotation, JPC_Vec3 inLinearVelocity, JPC_Vec3 inAngularVelocity) { - to_jph(self)->SetPositionRotationAndVelocity(to_jph(inBodyID), to_jph(inPosition), to_jph(inRotation), to_jph(inLinearVelocity), to_jph(inAngularVelocity)); -} - -JPC_API void JPC_BodyInterface_AddForce(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_Vec3 inForce) { - to_jph(self)->AddForce(to_jph(inBodyID), to_jph(inForce)); -} - -// JPC_API void JPC_BodyInterface_AddForce(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_Vec3 inForce, JPC_RVec3 inPoint); - -JPC_API void JPC_BodyInterface_AddTorque(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_Vec3 inTorque) { - to_jph(self)->AddTorque(to_jph(inBodyID), to_jph(inTorque)); -} - -JPC_API void JPC_BodyInterface_AddForceAndTorque(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_Vec3 inForce, JPC_Vec3 inTorque) { - to_jph(self)->AddForceAndTorque(to_jph(inBodyID), to_jph(inForce), to_jph(inTorque)); -} - -JPC_API void JPC_BodyInterface_AddImpulse(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_Vec3 inImpulse) { - to_jph(self)->AddImpulse(to_jph(inBodyID), to_jph(inImpulse)); -} - -JPC_API void JPC_BodyInterface_AddImpulse3(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_Vec3 inImpulse, JPC_RVec3 inPoint) { - to_jph(self)->AddImpulse(to_jph(inBodyID), to_jph(inImpulse), to_jph(inPoint)); -} - -JPC_API void JPC_BodyInterface_AddAngularImpulse(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_Vec3 inAngularImpulse) { - to_jph(self)->AddAngularImpulse(to_jph(inBodyID), to_jph(inAngularImpulse)); -} - -JPC_API JPC_BodyType JPC_BodyInterface_GetBodyType(const JPC_BodyInterface *self, JPC_BodyID inBodyID) { - return to_jpc(to_jph(self)->GetBodyType(to_jph(inBodyID))); -} - -JPC_API void JPC_BodyInterface_SetMotionType(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_MotionType inMotionType, JPC_Activation inActivationMode) { - to_jph(self)->SetMotionType(to_jph(inBodyID), to_jph(inMotionType), to_jph(inActivationMode)); -} - -JPC_API JPC_MotionType JPC_BodyInterface_GetMotionType(const JPC_BodyInterface *self, JPC_BodyID inBodyID) { - return to_jpc(to_jph(self)->GetMotionType(to_jph(inBodyID))); -} - -JPC_API void JPC_BodyInterface_SetMotionQuality(JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_MotionQuality inMotionQuality) { - to_jph(self)->SetMotionQuality(to_jph(inBodyID), to_jph(inMotionQuality)); -} - -JPC_API JPC_MotionQuality JPC_BodyInterface_GetMotionQuality(const JPC_BodyInterface *self, JPC_BodyID inBodyID) { - return to_jpc(to_jph(self)->GetMotionQuality(to_jph(inBodyID))); -} - -JPC_API void JPC_BodyInterface_GetInverseInertia(const JPC_BodyInterface *self, JPC_BodyID inBodyID, JPC_Mat44 *outMatrix) { - to_jph(self)->GetInverseInertia(to_jph(inBodyID)).StoreFloat4x4(reinterpret_cast(outMatrix)); -} - -JPC_API void JPC_BodyInterface_SetRestitution(JPC_BodyInterface *self, JPC_BodyID inBodyID, float inRestitution) { - to_jph(self)->SetRestitution(to_jph(inBodyID), inRestitution); -} - -JPC_API float JPC_BodyInterface_GetRestitution(const JPC_BodyInterface *self, JPC_BodyID inBodyID) { - return to_jph(self)->GetRestitution(to_jph(inBodyID)); -} - -JPC_API void JPC_BodyInterface_SetFriction(JPC_BodyInterface *self, JPC_BodyID inBodyID, float inFriction) { - to_jph(self)->SetFriction(to_jph(inBodyID), inFriction); -} - -JPC_API float JPC_BodyInterface_GetFriction(const JPC_BodyInterface *self, JPC_BodyID inBodyID) { - return to_jph(self)->GetFriction(to_jph(inBodyID)); -} - -JPC_API void JPC_BodyInterface_SetGravityFactor(JPC_BodyInterface *self, JPC_BodyID inBodyID, float inGravityFactor) { - to_jph(self)->SetGravityFactor(to_jph(inBodyID), inGravityFactor); -} - -JPC_API float JPC_BodyInterface_GetGravityFactor(const JPC_BodyInterface *self, JPC_BodyID inBodyID) { - return to_jph(self)->GetGravityFactor(to_jph(inBodyID)); -} - -JPC_API void JPC_BodyInterface_SetUseManifoldReduction(JPC_BodyInterface *self, JPC_BodyID inBodyID, bool inUseReduction) { - to_jph(self)->SetUseManifoldReduction(to_jph(inBodyID), inUseReduction); -} - -JPC_API bool JPC_BodyInterface_GetUseManifoldReduction(const JPC_BodyInterface *self, JPC_BodyID inBodyID) { - return to_jph(self)->GetUseManifoldReduction(to_jph(inBodyID)); -} - -// TransformedShape JPC_BodyInterface_GetTransformedShape(const JPC_BodyInterface *self, JPC_BodyID inBodyID); - -JPC_API uint64_t JPC_BodyInterface_GetUserData(const JPC_BodyInterface *self, JPC_BodyID inBodyID) { - return to_jph(self)->GetUserData(to_jph(inBodyID)); -} - -JPC_API void JPC_BodyInterface_SetUserData(const JPC_BodyInterface *self, JPC_BodyID inBodyID, uint64_t inUserData) { - to_jph(self)->SetUserData(to_jph(inBodyID), inUserData); -} - -// const PhysicsMaterial* JPC_BodyInterface_GetMaterial(const JPC_BodyInterface *self, JPC_BodyID inBodyID, const SubShapeID &inSubShapeID); - -JPC_API void JPC_BodyInterface_InvalidateContactCache(JPC_BodyInterface *self, JPC_BodyID inBodyID) { - to_jph(self)->InvalidateContactCache(to_jph(inBodyID)); -} - -//////////////////////////////////////////////////////////////////////////////// -// NarrowPhaseQuery - -JPC_API bool JPC_NarrowPhaseQuery_CastRay(const JPC_NarrowPhaseQuery* self, JPC_NarrowPhaseQuery_CastRayArgs* args) { - JPH::RayCastResult result; - - JPH::BroadPhaseLayerFilter defaultBplFilter{}; - const JPH::BroadPhaseLayerFilter* bplFilter = &defaultBplFilter; - if (args->BroadPhaseLayerFilter != nullptr) { - bplFilter = to_jph(args->BroadPhaseLayerFilter); - } - - JPH::ObjectLayerFilter defaultOlFilter{}; - const JPH::ObjectLayerFilter* olFilter = &defaultOlFilter; - if (args->ObjectLayerFilter != nullptr) { - olFilter = to_jph(args->ObjectLayerFilter); - } - - JPH::BodyFilter defaultBodyFilter{}; - const JPH::BodyFilter* bodyFilter = &defaultBodyFilter; - if (args->BodyFilter != nullptr) { - bodyFilter = to_jph(args->BodyFilter); - } - - bool hit = to_jph(self)->CastRay( - to_jph(args->Ray), - result, - *bplFilter, - *olFilter, - *bodyFilter - ); - - if (hit) { - args->Result = to_jpc(result); - } - - return hit; -} - -//////////////////////////////////////////////////////////////////////////////// -// PhysicsSystem - -JPC_API JPC_PhysicsSystem* JPC_PhysicsSystem_new() { - return to_jpc(new JPH::PhysicsSystem()); -} - -JPC_API void JPC_PhysicsSystem_Init( - JPC_PhysicsSystem* self, - uint inMaxBodies, - uint inNumBodyMutexes, - uint inMaxBodyPairs, - uint inMaxContactConstraints, - JPC_BroadPhaseLayerInterface* inBroadPhaseLayerInterface, - JPC_ObjectVsBroadPhaseLayerFilter* inObjectVsBroadPhaseLayerFilter, - JPC_ObjectLayerPairFilter* inObjectLayerPairFilter) -{ - JPC_BroadPhaseLayerInterfaceBridge* impl_inBroadPhaseLayerInterface = to_jph(inBroadPhaseLayerInterface); - JPC_ObjectVsBroadPhaseLayerFilterBridge* impl_inObjectVsBroadPhaseLayerFilter = to_jph(inObjectVsBroadPhaseLayerFilter); - JPC_ObjectLayerPairFilterBridge* impl_inObjectLayerPairFilter = to_jph(inObjectLayerPairFilter); - - to_jph(self)->Init( - inMaxBodies, - inNumBodyMutexes, - inMaxBodyPairs, - inMaxContactConstraints, - *impl_inBroadPhaseLayerInterface, - *impl_inObjectVsBroadPhaseLayerFilter, - *impl_inObjectLayerPairFilter); -} - -JPC_API void JPC_PhysicsSystem_OptimizeBroadPhase(JPC_PhysicsSystem* self) { - to_jph(self)->OptimizeBroadPhase(); -} - -JPC_API JPC_BodyInterface* JPC_PhysicsSystem_GetBodyInterface(JPC_PhysicsSystem* self) { - return to_jpc(&to_jph(self)->GetBodyInterface()); -} - -JPC_API const JPC_NarrowPhaseQuery* JPC_PhysicsSystem_GetNarrowPhaseQuery(const JPC_PhysicsSystem* self) { - return to_jpc(&to_jph(self)->GetNarrowPhaseQuery()); -} - -JPC_API JPC_PhysicsUpdateError JPC_PhysicsSystem_Update( - JPC_PhysicsSystem* self, - float inDeltaTime, - int inCollisionSteps, - JPC_TempAllocatorImpl *inTempAllocator, - JPC_JobSystemThreadPool *inJobSystem) -{ - auto res = to_jph(self)->Update( - inDeltaTime, - inCollisionSteps, - to_jph(inTempAllocator), - to_jph(inJobSystem)); - - return to_integral(res); -} - -JPC_API void JPC_PhysicsSystem_DrawBodies( - JPC_PhysicsSystem* self, - JPC_BodyManager_DrawSettings* inSettings, - JPC_DebugRendererSimple* inRenderer, - [[maybe_unused]] const void* inBodyFilter) -{ - to_jph(self)->DrawBodies(to_jph(*inSettings), to_jph(inRenderer), nullptr); -} \ No newline at end of file diff --git a/crates/joltc-sys-patched/JoltC/JoltC/JoltC.h b/crates/joltc-sys-patched/JoltC/JoltC/JoltC.h deleted file mode 100644 index d7515a6950f..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltC/JoltC.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -#ifndef ENSURE_TESTS - #define ENSURE_EQUAL(a, b) - #define ENSURE_ENUM_EQ(a, b) - #define ENSURE_SIZE_ALIGN(a, b) - #define ENSURE_FIELD(a, b, c, d) -#endif - -#include "JoltC/Enums.h" -#include "JoltC/Functions.h" -#include "JoltC/Vehicle.h" \ No newline at end of file diff --git a/crates/joltc-sys-patched/JoltC/JoltC/Test.cpp b/crates/joltc-sys-patched/JoltC/JoltC/Test.cpp deleted file mode 100644 index 070daae1ef0..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltC/Test.cpp +++ /dev/null @@ -1,50 +0,0 @@ -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -template -constexpr auto to_integral(E e) -> typename std::underlying_type::type -{ - return static_cast::type>(e); -} - -#define ENSURE_TESTS - -#define ENSURE_EQUAL(c_const, cpp_const) \ - static_assert(c_const == cpp_const, #c_const " did not match " #cpp_const); \ - static_assert(std::is_same_v, "type of " #c_const " did not match type of " #cpp_const); - -#define ENSURE_ENUM_EQ(c_const, cpp_enum) \ - static_assert(c_const == to_integral(cpp_enum), #c_const " did not match " #cpp_enum); \ - static_assert(sizeof(c_const) == sizeof(cpp_enum), #c_const " did not have same size as " #cpp_enum); - -// Ensures that a C struct we define is compatible in layout with its -// corresponding C++ type, and that the C++ type is safe to represent in C. -#define ENSURE_SIZE_ALIGN(c_type, cpp_type) \ - static_assert(sizeof(c_type) == sizeof(cpp_type), "size of " #c_type " did not match size of " #cpp_type); \ - static_assert(alignof(c_type) == alignof(cpp_type), "align of " #c_type " did not match align of " #cpp_type); \ - static_assert(!std::is_polymorphic_v, #cpp_type " is polymorphic and cannot be mirrored"); - -#define unsafe_offsetof(st, m) ((size_t) ( (char *)&((st *)(0))->m - (char *)0 )) -#define unsafe_fieldtype(st, m) decltype((st *)(0)->m) - -#define ENSURE_FIELD(type0, field0, type1, field1) \ - static_assert(unsafe_offsetof(type0, field0) == unsafe_offsetof(type1, field1), \ - #type0 "." #field0 " did not have same offset as " #type1 "." #field1); \ - ENSURE_SIZE_ALIGN(unsafe_fieldtype(type0, field0), unsafe_fieldtype(type1, field1)); - -#include "JoltC/JoltC.h" \ No newline at end of file diff --git a/crates/joltc-sys-patched/JoltC/JoltC/Vehicle.cpp b/crates/joltc-sys-patched/JoltC/JoltC/Vehicle.cpp deleted file mode 100644 index e67ec9d5119..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltC/Vehicle.cpp +++ /dev/null @@ -1,375 +0,0 @@ -// Vehicle.cpp — High-level vehicle API wrapping Jolt's WheeledVehicleController. -// -// Internally manages VehicleConstraint lifetimes and maps vehicle IDs to them. -// All vehicle creation complexity (wheels, engine, transmission, differential, -// collision tester, step listener) is handled here behind a flat C API. - -// Jolt C++ headers first (same pattern as JoltC.cpp) -#include -#include -#include -#include -#include -#include -#include -#include - -// JoltC header after Jolt — defines ENSURE_SIZE_ALIGN as empty -#include - -#include -#include -#include - -using namespace JPH; - -// Object layers — must match what physics.rs uses -static constexpr ObjectLayer LAYER_NON_MOVING = 0; -static constexpr ObjectLayer LAYER_MOVING = 1; - -//////////////////////////////////////////////////////////////////////////////// -// Internal vehicle tracking - -struct VehicleData { - VehicleConstraint* constraint; - BodyID body_id; - Ref collision_tester; -}; - -static std::mutex g_vehicle_mutex; -static std::unordered_map g_vehicles; -static uint32_t g_next_vehicle_id = 1; - -// Convert opaque JPC_PhysicsSystem* to JPH::PhysicsSystem* -static PhysicsSystem* to_system(JPC_PhysicsSystem* s) { - return reinterpret_cast(s); -} - -//////////////////////////////////////////////////////////////////////////////// -// Default parameters - -JPC_API void JPC_VehicleParams_default(JPC_VehicleParams* p) { - // Chassis: sedan-like box - p->chassis_half_x = 0.9f; - p->chassis_half_y = 0.3f; - p->chassis_half_z = 2.0f; - p->chassis_mass = 1500.0f; - - // Front wheels - p->front_wheel_x = 0.85f; - p->front_wheel_y = -0.3f; - p->front_wheel_z = 1.3f; - - // Rear wheels - p->rear_wheel_x = 0.85f; - p->rear_wheel_y = -0.3f; - p->rear_wheel_z = -1.3f; - - // Wheel geometry - p->wheel_radius = 0.3f; - p->wheel_width = 0.1f; - - // Suspension - p->suspension_min_length = 0.3f; - p->suspension_max_length = 0.5f; - p->suspension_frequency = 1.5f; - p->suspension_damping = 0.5f; - - // Steering - p->max_steer_angle_deg = 30.0f; - - // Brakes - p->front_brake_torque = 1500.0f; - p->rear_brake_torque = 1000.0f; - p->rear_handbrake_torque = 4000.0f; - - // Engine - p->engine_max_torque = 500.0f; - p->engine_min_rpm = 1000.0f; - p->engine_max_rpm = 6000.0f; - - // Transmission (5-speed) - p->num_gears = 5; - p->gear_ratios[0] = 2.66f; - p->gear_ratios[1] = 1.78f; - p->gear_ratios[2] = 1.30f; - p->gear_ratios[3] = 1.00f; - p->gear_ratios[4] = 0.74f; - p->gear_ratios[5] = 0.0f; - p->reverse_gear_ratio = -2.90f; - p->shift_up_rpm = 4000.0f; - p->shift_down_rpm = 2000.0f; - - // Differential - p->differential_ratio = 3.42f; - p->drive_type = 0; // AWD -} - -//////////////////////////////////////////////////////////////////////////////// -// Create vehicle - -JPC_API uint32_t JPC_PhysicsSystem_CreateVehicle( - JPC_PhysicsSystem* system, - const JPC_VehicleParams* p, - float pos_x, float pos_y, float pos_z, - float rot_x, float rot_y, float rot_z, float rot_w) -{ - PhysicsSystem* ps = to_system(system); - BodyInterface& bi = ps->GetBodyInterface(); - - // --- 1. Create chassis body --- - BoxShapeSettings chassis_shape(Vec3(p->chassis_half_x, p->chassis_half_y, p->chassis_half_z)); - - // Offset center of mass downward for stability - OffsetCenterOfMassShapeSettings com_shape( - Vec3(0, -p->chassis_half_y, 0), - chassis_shape.Create().Get() - ); - - BodyCreationSettings body_settings( - com_shape.Create().Get(), - RVec3(pos_x, pos_y, pos_z), - Quat(rot_x, rot_y, rot_z, rot_w), - EMotionType::Dynamic, - LAYER_MOVING - ); - body_settings.mOverrideMassProperties = EOverrideMassProperties::CalculateInertia; - body_settings.mMassPropertiesOverride.mMass = p->chassis_mass; - - Body* body = bi.CreateBody(body_settings); - if (!body) - return 0; - - bi.AddBody(body->GetID(), EActivation::Activate); - - // --- 2. Configure vehicle constraint --- - VehicleConstraintSettings vehicle_settings; - vehicle_settings.mUp = Vec3::sAxisY(); - vehicle_settings.mForward = Vec3::sAxisZ(); - - // Max pitch/roll angle to prevent flipping - vehicle_settings.mMaxPitchRollAngle = DegreesToRadians(60.0f); - - // --- 3. Create 4 wheels (FL=0, FR=1, RL=2, RR=3) --- - float steer_rad = DegreesToRadians(p->max_steer_angle_deg); - - auto make_wheel = [&](float x, float y, float z, bool is_front) -> Ref { - WheelSettingsWV* w = new WheelSettingsWV; - w->mPosition = Vec3(x, y, z); - w->mSuspensionDirection = Vec3(0, -1, 0); - w->mSteeringAxis = Vec3(0, 1, 0); - w->mWheelUp = Vec3(0, 1, 0); - w->mWheelForward = Vec3(0, 0, 1); - w->mRadius = p->wheel_radius; - w->mWidth = p->wheel_width; - w->mSuspensionMinLength = p->suspension_min_length; - w->mSuspensionMaxLength = p->suspension_max_length; - w->mSuspensionSpring.mFrequency = p->suspension_frequency; - w->mSuspensionSpring.mDamping = p->suspension_damping; - w->mMaxSteerAngle = is_front ? steer_rad : 0.0f; - w->mMaxBrakeTorque = is_front ? p->front_brake_torque : p->rear_brake_torque; - w->mMaxHandBrakeTorque = is_front ? 0.0f : p->rear_handbrake_torque; - return w; - }; - - // FL, FR, RL, RR - vehicle_settings.mWheels.resize(4); - vehicle_settings.mWheels[0] = make_wheel(-p->front_wheel_x, p->front_wheel_y, p->front_wheel_z, true); - vehicle_settings.mWheels[1] = make_wheel( p->front_wheel_x, p->front_wheel_y, p->front_wheel_z, true); - vehicle_settings.mWheels[2] = make_wheel(-p->rear_wheel_x, p->rear_wheel_y, p->rear_wheel_z, false); - vehicle_settings.mWheels[3] = make_wheel( p->rear_wheel_x, p->rear_wheel_y, p->rear_wheel_z, false); - - // --- 4. Controller (engine + transmission + differentials) --- - WheeledVehicleControllerSettings* controller = new WheeledVehicleControllerSettings; - - // Engine - controller->mEngine.mMaxTorque = p->engine_max_torque; - controller->mEngine.mMinRPM = p->engine_min_rpm; - controller->mEngine.mMaxRPM = p->engine_max_rpm; - - // Transmission (auto) - controller->mTransmission.mShiftUpRPM = p->shift_up_rpm; - controller->mTransmission.mShiftDownRPM = p->shift_down_rpm; - controller->mTransmission.mGearRatios.clear(); - for (int i = 0; i < p->num_gears && i < 6; i++) { - if (p->gear_ratios[i] != 0.0f) - controller->mTransmission.mGearRatios.push_back(p->gear_ratios[i]); - } - controller->mTransmission.mReverseGearRatios.clear(); - controller->mTransmission.mReverseGearRatios.push_back(p->reverse_gear_ratio); - - // Differentials based on drive type - controller->mDifferentials.clear(); - if (p->drive_type == 0 || p->drive_type == 1) { - // FWD or AWD: front differential - VehicleDifferentialSettings front_diff; - front_diff.mLeftWheel = 0; - front_diff.mRightWheel = 1; - front_diff.mDifferentialRatio = p->differential_ratio; - front_diff.mEngineTorqueRatio = (p->drive_type == 0) ? 0.5f : 1.0f; - controller->mDifferentials.push_back(front_diff); - } - if (p->drive_type == 0 || p->drive_type == 2) { - // RWD or AWD: rear differential - VehicleDifferentialSettings rear_diff; - rear_diff.mLeftWheel = 2; - rear_diff.mRightWheel = 3; - rear_diff.mDifferentialRatio = p->differential_ratio; - rear_diff.mEngineTorqueRatio = (p->drive_type == 0) ? 0.5f : 1.0f; - controller->mDifferentials.push_back(rear_diff); - } - - vehicle_settings.mController = controller; - - // --- 5. Create constraint and register --- - VehicleConstraint* constraint = new VehicleConstraint(*body, vehicle_settings); - - // Collision tester: use MOVING layer so the broadphase filter allows hitting NON_MOVING ground - // (our filter says MOVING collides with everything, NON_MOVING only collides with MOVING) - Ref tester = new VehicleCollisionTesterRay(LAYER_MOVING); - constraint->SetVehicleCollisionTester(tester); - - // Add constraint to physics system - ps->AddConstraint(constraint); - - // Add as step listener (VehicleConstraint implements PhysicsStepListener) - ps->AddStepListener(constraint); - - // --- 6. Register internally --- - std::lock_guard lock(g_vehicle_mutex); - uint32_t vid = g_next_vehicle_id++; - - g_vehicles[vid] = VehicleData{ - constraint, - body->GetID(), - tester, - }; - - return vid; -} - -//////////////////////////////////////////////////////////////////////////////// -// Set driver input - -JPC_API void JPC_PhysicsSystem_SetVehicleInput( - JPC_PhysicsSystem* system, - uint32_t vehicle_id, - float forward, float right, float brake, float handbrake) -{ - PhysicsSystem* ps = to_system(system); - std::lock_guard lock(g_vehicle_mutex); - auto it = g_vehicles.find(vehicle_id); - if (it == g_vehicles.end()) - return; - - VehicleData& vd = it->second; - - WheeledVehicleController* controller = - static_cast(vd.constraint->GetController()); - controller->SetDriverInput(forward, right, brake, handbrake); - - // Activate the body so Jolt processes the input (sleeping bodies don't move) - if (forward != 0.0f || right != 0.0f || brake != 0.0f || handbrake != 0.0f) { - ps->GetBodyInterface().ActivateBody(vd.body_id); - } -} - -//////////////////////////////////////////////////////////////////////////////// -// Get vehicle state - -JPC_API bool JPC_PhysicsSystem_GetVehicleState( - JPC_PhysicsSystem* system, - uint32_t vehicle_id, - JPC_VehicleState* out) -{ - PhysicsSystem* ps = to_system(system); - std::lock_guard lock(g_vehicle_mutex); - - auto it = g_vehicles.find(vehicle_id); - if (it == g_vehicles.end()) - return false; - - VehicleData& vd = it->second; - BodyInterface& bi = ps->GetBodyInterface(); - - // Body transform - RVec3 pos = bi.GetCenterOfMassPosition(vd.body_id); - Quat rot = bi.GetRotation(vd.body_id); - Vec3 vel = bi.GetLinearVelocity(vd.body_id); - - out->pos_x = (float)pos.GetX(); - out->pos_y = (float)pos.GetY(); - out->pos_z = (float)pos.GetZ(); - out->rot_x = rot.GetX(); - out->rot_y = rot.GetY(); - out->rot_z = rot.GetZ(); - out->rot_w = rot.GetW(); - out->vel_x = vel.GetX(); - out->vel_y = vel.GetY(); - out->vel_z = vel.GetZ(); - - // Speed in km/h (forward component of velocity) - Vec3 forward = rot * Vec3::sAxisZ(); - float forward_speed = vel.Dot(forward); - out->speed_kmh = forward_speed * 3.6f; - - // Engine/transmission - WheeledVehicleController* ctrl = - static_cast(vd.constraint->GetController()); - out->engine_rpm = ctrl->GetEngine().GetCurrentRPM(); - out->current_gear = ctrl->GetTransmission().GetCurrentGear(); - - // Wheel states - Vec3 wheel_right(1, 0, 0); - Vec3 wheel_up(0, 1, 0); - - for (int i = 0; i < 4; i++) { - Wheel* wheel = vd.constraint->GetWheel(i); - RMat44 wt = vd.constraint->GetWheelWorldTransform(i, wheel_right, wheel_up); - - RVec3 wpos = wt.GetTranslation(); - Quat wrot = wt.GetRotation().GetQuaternion(); - - out->wheels[i].pos_x = (float)wpos.GetX(); - out->wheels[i].pos_y = (float)wpos.GetY(); - out->wheels[i].pos_z = (float)wpos.GetZ(); - out->wheels[i].rot_x = wrot.GetX(); - out->wheels[i].rot_y = wrot.GetY(); - out->wheels[i].rot_z = wrot.GetZ(); - out->wheels[i].rot_w = wrot.GetW(); - out->wheels[i].steer_angle = wheel->GetSteerAngle(); - out->wheels[i].rotation_angle = wheel->GetRotationAngle(); - out->wheels[i].angular_velocity = wheel->GetAngularVelocity(); - out->wheels[i].suspension_length = wheel->GetSuspensionLength(); - out->wheels[i].has_contact = wheel->HasContact(); - } - - return true; -} - -//////////////////////////////////////////////////////////////////////////////// -// Destroy vehicle - -JPC_API void JPC_PhysicsSystem_DestroyVehicle( - JPC_PhysicsSystem* system, - uint32_t vehicle_id) -{ - PhysicsSystem* ps = to_system(system); - std::lock_guard lock(g_vehicle_mutex); - - auto it = g_vehicles.find(vehicle_id); - if (it == g_vehicles.end()) - return; - - VehicleData& vd = it->second; - - // Remove step listener first, then constraint, then body - ps->RemoveStepListener(vd.constraint); - ps->RemoveConstraint(vd.constraint); - - BodyInterface& bi = ps->GetBodyInterface(); - bi.RemoveBody(vd.body_id); - bi.DestroyBody(vd.body_id); - - g_vehicles.erase(it); -} diff --git a/crates/joltc-sys-patched/JoltC/JoltC/Vehicle.h b/crates/joltc-sys-patched/JoltC/JoltC/Vehicle.h deleted file mode 100644 index 763236f6f9f..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltC/Vehicle.h +++ /dev/null @@ -1,130 +0,0 @@ -#pragma once - -#include "Functions.h" - -#ifdef __cplusplus -extern "C" { -#endif - -//////////////////////////////////////////////////////////////////////////////// -// Vehicle Parameters — flat struct for creating a 4-wheel vehicle. -// All positions are in local space relative to the vehicle body center. - -typedef struct JPC_VehicleParams { - // Chassis shape (box half-extents in meters) - float chassis_half_x; - float chassis_half_y; - float chassis_half_z; - float chassis_mass; - - // Front wheel attachment positions (mirrored ±x) - float front_wheel_x; // Positive = right side offset - float front_wheel_y; // Height (usually negative, below chassis center) - float front_wheel_z; // Forward offset from center - - // Rear wheel attachment positions (mirrored ±x) - float rear_wheel_x; - float rear_wheel_y; - float rear_wheel_z; - - // Wheel geometry - float wheel_radius; - float wheel_width; - - // Suspension - float suspension_min_length; // Fully compressed (m) - float suspension_max_length; // Fully extended (m) - float suspension_frequency; // Spring frequency (Hz) - float suspension_damping; // Damping ratio (0-1) - - // Steering (front wheels only) - float max_steer_angle_deg; - - // Brakes - float front_brake_torque; - float rear_brake_torque; - float rear_handbrake_torque; - - // Engine - float engine_max_torque; - float engine_min_rpm; - float engine_max_rpm; - - // Transmission (auto mode) - int num_gears; // 1-6 forward gears - float gear_ratios[6]; // Gear ratios (1st through 6th, 0 = unused) - float reverse_gear_ratio; - float shift_up_rpm; - float shift_down_rpm; - - // Differential - float differential_ratio; - int drive_type; // 0 = AWD, 1 = FWD, 2 = RWD -} JPC_VehicleParams; - -// Wheel state for replication -typedef struct JPC_WheelState { - float pos_x, pos_y, pos_z; - float rot_x, rot_y, rot_z, rot_w; - float steer_angle; - float rotation_angle; - float angular_velocity; - float suspension_length; - bool has_contact; -} JPC_WheelState; - -// Complete vehicle state for replication -typedef struct JPC_VehicleState { - // Body transform - float pos_x, pos_y, pos_z; - float rot_x, rot_y, rot_z, rot_w; - float vel_x, vel_y, vel_z; - // Vehicle info - float speed_kmh; - float engine_rpm; - int current_gear; - // Wheel states (FL, FR, RL, RR) - JPC_WheelState wheels[4]; -} JPC_VehicleState; - -//////////////////////////////////////////////////////////////////////////////// -// Vehicle API — high-level functions - -// Fill a VehicleParams struct with sensible defaults for a sedan-like vehicle. -JPC_API void JPC_VehicleParams_default(JPC_VehicleParams* params); - -// Create a 4-wheel vehicle in the physics system. -// Returns a vehicle ID (>0) on success, 0 on failure. -// The vehicle body, constraint, collision tester, and step listener are all -// created and managed internally. -JPC_API uint32_t JPC_PhysicsSystem_CreateVehicle( - JPC_PhysicsSystem* system, - const JPC_VehicleParams* params, - float pos_x, float pos_y, float pos_z, - float rot_x, float rot_y, float rot_z, float rot_w); - -// Set driver input for a vehicle. -// forward: -1 to 1 (backward to forward, auto transmission handles reverse) -// right: -1 to 1 (left to right steering) -// brake: 0 to 1 -// handbrake: 0 to 1 -JPC_API void JPC_PhysicsSystem_SetVehicleInput( - JPC_PhysicsSystem* system, - uint32_t vehicle_id, - float forward, float right, float brake, float handbrake); - -// Get the full vehicle state (body + wheels + engine). -// Returns true on success. -JPC_API bool JPC_PhysicsSystem_GetVehicleState( - JPC_PhysicsSystem* system, - uint32_t vehicle_id, - JPC_VehicleState* out_state); - -// Destroy a vehicle and remove it from the physics system. -JPC_API void JPC_PhysicsSystem_DestroyVehicle( - JPC_PhysicsSystem* system, - uint32_t vehicle_id); - -#ifdef __cplusplus -} -#endif diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/.clang-format b/crates/joltc-sys-patched/JoltC/JoltPhysics/.clang-format deleted file mode 100644 index e3845288a2a..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/.clang-format +++ /dev/null @@ -1 +0,0 @@ -DisableFormat: true diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/.editorconfig b/crates/joltc-sys-patched/JoltC/JoltPhysics/.editorconfig deleted file mode 100644 index 7ea7624a6af..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/.editorconfig +++ /dev/null @@ -1,13 +0,0 @@ -root = true - -[*.{cpp,h,inl,cmake,sh,bat,hlsl}] -indent_style = tab -tab_width = 4 -trim_trailing_whitespace = true -insert_final_newline = true - -[CMakeLists.txt] -indent_style = tab -tab_width = 4 -trim_trailing_whitespace = true -insert_final_newline = true diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/.gitattributes b/crates/joltc-sys-patched/JoltC/JoltPhysics/.gitattributes deleted file mode 100644 index c5482ed7cdb..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/.gitattributes +++ /dev/null @@ -1,18 +0,0 @@ -# Convert LF to CRLF on windows on checkout and convert back before submitting to the repository -* text=auto - -# Explicitly declare text files to always be normalized and converted to native line endings on checkout -*.cpp text -*.inl text -*.h text -*.tof text -*.bat text - -# Force shell files to use LF only -*.sh text eol=lf -gradlew text eol=lf - -# Declare binary file types -*.tga binary -*.bof binary -*.bin binary \ No newline at end of file diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/.github/dependabot.yml b/crates/joltc-sys-patched/JoltC/JoltPhysics/.github/dependabot.yml deleted file mode 100644 index 2c431b0bf04..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/.github/dependabot.yml +++ /dev/null @@ -1,9 +0,0 @@ -# Please see the documentation for all configuration options: -# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates - -version: 2 -updates: - - package-ecosystem: "github-actions" - directory: "/" - schedule: - interval: "weekly" diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/.github/workflows/build.yml b/crates/joltc-sys-patched/JoltC/JoltPhysics/.github/workflows/build.yml deleted file mode 100644 index c7f4433b3ad..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/.github/workflows/build.yml +++ /dev/null @@ -1,322 +0,0 @@ -name: Build - -on: - push: - branches: [ master, 4.x ] - paths-ignore: - - 'Docs/**' - - '**.md' - pull_request: - branches: [ master, 4.x ] - paths-ignore: - - 'Docs/**' - - '**.md' - -jobs: - linux-clang: - runs-on: ubuntu-latest - name: Linux Clang - strategy: - fail-fast: false - matrix: - build_type: [Debug, Release, Distribution, ReleaseASAN, ReleaseUBSAN] - clang_version: [clang++-13, clang++-14] - double_precision: [No, Yes] - - steps: - - name: Checkout Code - uses: actions/checkout@v4 - - name: Configure CMake - run: cmake -B ${{github.workspace}}/Build/Linux_${{matrix.build_type}}_${{matrix.clang_version}} -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DCMAKE_CXX_COMPILER=${{matrix.clang_version}} -DDOUBLE_PRECISION=${{matrix.double_precision}} Build - - name: Build - run: cmake --build ${{github.workspace}}/Build/Linux_${{matrix.build_type}}_${{matrix.clang_version}} -j 2 - - name: Test - working-directory: ${{github.workspace}}/Build/Linux_${{matrix.build_type}}_${{matrix.clang_version}} - run: ctest --output-on-failure --verbose - - linux-clang-so: - runs-on: ubuntu-latest - name: Linux Clang Shared Library - strategy: - fail-fast: false - matrix: - build_type: [Debug, Release, Distribution] - clang_version: [clang++-14] - - steps: - - name: Checkout Code - uses: actions/checkout@v4 - - name: Configure CMake - run: cmake -B ${{github.workspace}}/Build/Linux_${{matrix.build_type}}_${{matrix.clang_version}} -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DCMAKE_CXX_COMPILER=${{matrix.clang_version}} -DBUILD_SHARED_LIBS=Yes Build - - name: Build - run: cmake --build ${{github.workspace}}/Build/Linux_${{matrix.build_type}}_${{matrix.clang_version}} -j 2 - - name: Test - working-directory: ${{github.workspace}}/Build/Linux_${{matrix.build_type}}_${{matrix.clang_version}} - run: ctest --output-on-failure --verbose - - linux-clang-32-bit: - runs-on: ubuntu-latest - name: Linux Clang 32-bit - strategy: - fail-fast: false - matrix: - build_type: [Debug, Release, Distribution] - clang_version: [clang++-14] - - steps: - - name: Checkout Code - uses: actions/checkout@v4 - - name: Update APT - run: sudo apt update - - name: Install G++-Multilib - run: sudo apt -y install g++-multilib - - name: Configure CMake - run: cmake -B ${{github.workspace}}/Build/Linux_${{matrix.build_type}}_${{matrix.clang_version}} -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DCMAKE_CXX_COMPILER=${{matrix.clang_version}} -DCMAKE_CXX_FLAGS=-m32 -DUSE_SSE4_1=OFF -DUSE_SSE4_2=OFF -DUSE_AVX=OFF -DUSE_AVX2=OFF -DUSE_AVX512=OFF -DUSE_LZCNT=OFF -DUSE_TZCNT=OFF -DUSE_F16C=OFF -DUSE_FMADD=OFF Build - - name: Build - run: cmake --build ${{github.workspace}}/Build/Linux_${{matrix.build_type}}_${{matrix.clang_version}} -j 2 - - name: Test - working-directory: ${{github.workspace}}/Build/Linux_${{matrix.build_type}}_${{matrix.clang_version}} - run: ctest --output-on-failure --verbose - - linux-gcc: - runs-on: ubuntu-latest - name: Linux GCC - strategy: - fail-fast: false - matrix: - build_type: [Debug, Release, Distribution] - clang_version: [g++] - - steps: - - name: Checkout Code - uses: actions/checkout@v4 - - name: Configure CMake - run: cmake -B ${{github.workspace}}/Build/Linux_${{matrix.build_type}}_${{matrix.clang_version}} -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DCMAKE_CXX_COMPILER=${{matrix.clang_version}} Build - - name: Build - run: cmake --build ${{github.workspace}}/Build/Linux_${{matrix.build_type}}_${{matrix.clang_version}} -j 2 - - name: Test - working-directory: ${{github.workspace}}/Build/Linux_${{matrix.build_type}}_${{matrix.clang_version}} - run: ctest --output-on-failure --verbose - - linux-gcc-so: - runs-on: ubuntu-latest - name: Linux GCC Shared Library - strategy: - fail-fast: false - matrix: - build_type: [Debug, Release, Distribution] - clang_version: [g++] - - steps: - - name: Checkout Code - uses: actions/checkout@v4 - - name: Configure CMake - run: cmake -B ${{github.workspace}}/Build/Linux_${{matrix.build_type}}_${{matrix.clang_version}} -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DCMAKE_CXX_COMPILER=${{matrix.clang_version}} -DBUILD_SHARED_LIBS=Yes Build - - name: Build - run: cmake --build ${{github.workspace}}/Build/Linux_${{matrix.build_type}}_${{matrix.clang_version}} -j 2 - - name: Test - working-directory: ${{github.workspace}}/Build/Linux_${{matrix.build_type}}_${{matrix.clang_version}} - run: ctest --output-on-failure --verbose - - msys2_mingw_gcc: - runs-on: windows-latest - defaults: - run: - shell: msys2 {0} - name: MSYS2 MinGW GCC - strategy: - fail-fast: false - matrix: - build_type: [Debug, Release] - - steps: - - name: Checkout Code - uses: actions/checkout@v4 - - name: Setup MSYS2 - uses: msys2/setup-msys2@v2 - with: - msystem: mingw64 - install: mingw-w64-x86_64-toolchain mingw-w64-x86_64-cmake - update: true - - name: Configure CMake - run: cmake -B Build/MSYS2_MinGW_GCC -G "MinGW Makefiles" -DCMAKE_BUILD_TYPE=${{matrix.build_type}} Build - - name: Build - run: cmake --build Build/MSYS2_MinGW_GCC -j 2 - - name: Test - working-directory: Build/MSYS2_MinGW_GCC - run: ctest --output-on-failure --verbose - - msvc_cl: - runs-on: windows-latest - name: Visual Studio CL - strategy: - fail-fast: false - matrix: - build_type: [Debug, Release, Distribution] - double_precision: [No, Yes] - - steps: - - name: Checkout Code - uses: actions/checkout@v4 - - name: Add msbuild to PATH - uses: microsoft/setup-msbuild@v2 - - name: Configure CMake - run: cmake -B ${{github.workspace}}/Build/VS2022_CL -G "Visual Studio 17 2022" -A x64 Build -DDOUBLE_PRECISION=${{matrix.double_precision}} - - name: Build - run: msbuild Build\VS2022_CL\JoltPhysics.sln /property:Configuration=${{matrix.build_type}} -m - - name: Test - working-directory: ${{github.workspace}}/Build/VS2022_CL/${{matrix.build_type}} - run: ./UnitTests.exe - - msvc_cl_dll: - runs-on: windows-latest - name: Visual Studio CL Shared Library - strategy: - fail-fast: false - matrix: - build_type: [Debug, Release, Distribution] - - steps: - - name: Checkout Code - uses: actions/checkout@v4 - - name: Add msbuild to PATH - uses: microsoft/setup-msbuild@v2 - - name: Configure CMake - run: cmake -B ${{github.workspace}}/Build/VS2022_CL -G "Visual Studio 17 2022" -A x64 Build -DBUILD_SHARED_LIBS=Yes - - name: Build - run: msbuild Build\VS2022_CL\JoltPhysics.sln /property:Configuration=${{matrix.build_type}} -m - - name: Test - working-directory: ${{github.workspace}}/Build/VS2022_CL/${{matrix.build_type}} - run: ./UnitTests.exe - - msvc_cl_32_bit: - runs-on: windows-latest - name: Visual Studio CL 32-bit - strategy: - fail-fast: false - matrix: - build_type: [Debug, Release] - - steps: - - name: Checkout Code - uses: actions/checkout@v4 - - name: Add msbuild to PATH - uses: microsoft/setup-msbuild@v2 - - name: Configure CMake - run: cmake -B ${{github.workspace}}/Build/VS2022_CL_32_BIT -G "Visual Studio 17 2022" -A Win32 -DUSE_SSE4_1=OFF -DUSE_SSE4_2=OFF -DUSE_AVX=OFF -DUSE_AVX2=OFF -DUSE_AVX512=OFF -DUSE_LZCNT=OFF -DUSE_TZCNT=OFF -DUSE_F16C=OFF -DUSE_FMADD=OFF Build - - name: Build - run: msbuild Build\VS2022_CL_32_BIT\JoltPhysics.sln /property:Configuration=${{matrix.build_type}} -m - - name: Test - working-directory: ${{github.workspace}}/Build/VS2022_CL_32_BIT/${{matrix.build_type}} - run: ./UnitTests.exe - - msvc_cl_arm: - runs-on: windows-latest - name: Visual Studio CL ARM - strategy: - fail-fast: false - matrix: - build_type: [Debug, Release] - - steps: - - name: Checkout Code - uses: actions/checkout@v4 - - name: Add msbuild to PATH - uses: microsoft/setup-msbuild@v2 - - name: Configure CMake - run: cmake -B ${{github.workspace}}/Build/VS2022_CL_ARM -G "Visual Studio 17 2022" -A ARM64 Build - - name: Build - run: msbuild Build\VS2022_CL_ARM\JoltPhysics.sln /property:Configuration=${{matrix.build_type}} -m - - msvc_cl_arm_32_bit: - runs-on: windows-latest - name: Visual Studio CL ARM 32-bit - strategy: - fail-fast: false - matrix: - build_type: [Debug, Release] - - steps: - - name: Checkout Code - uses: actions/checkout@v4 - - name: Add msbuild to PATH - uses: microsoft/setup-msbuild@v2 - - name: Configure CMake - run: cmake -B ${{github.workspace}}/Build/VS2022_CL_ARM_32_BIT -G "Visual Studio 17 2022" -A ARM Build - - name: Build - run: msbuild Build\VS2022_CL_ARM_32_BIT\JoltPhysics.sln /property:Configuration=${{matrix.build_type}} -m - - msvc_clang: - runs-on: windows-latest - name: Visual Studio Clang - strategy: - fail-fast: false - matrix: - build_type: [Debug, Release, Distribution] - double_precision: [No, Yes] - - steps: - - name: Checkout Code - uses: actions/checkout@v4 - - name: Add msbuild to PATH - uses: microsoft/setup-msbuild@v2 - - name: Configure CMake - run: cmake -B ${{github.workspace}}/Build/VS2022_Clang -G "Visual Studio 17 2022" -A x64 -T ClangCL Build -DDOUBLE_PRECISION=${{matrix.double_precision}} - - name: Build - run: msbuild Build\VS2022_Clang\JoltPhysics.sln /property:Configuration=${{matrix.build_type}} -m - - name: Test - working-directory: ${{github.workspace}}/Build/VS2022_Clang/${{matrix.build_type}} - run: ./UnitTests.exe - - macos: - runs-on: macos-latest - name: macOS - strategy: - fail-fast: false - matrix: - build_type: [Debug, Release, Distribution] - clang_version: [clang++] - - steps: - - name: Checkout Code - uses: actions/checkout@v4 - - name: Configure CMake - # github macos-latest runs on a 2013 Ivy Bridge CPU so doesn't have AVX2, LZCNT, TZCNT or FMADD - run: cmake -B ${{github.workspace}}/Build/MacOS_${{matrix.build_type}}_${{matrix.clang_version}} -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DCMAKE_CXX_COMPILER=${{matrix.clang_version}} -DUSE_AVX2=OFF -DUSE_AVX512=OFF -DUSE_LZCNT=OFF -DUSE_TZCNT=OFF -DUSE_FMADD=OFF Build - - name: Build - run: cmake --build ${{github.workspace}}/Build/MacOS_${{matrix.build_type}}_${{matrix.clang_version}} -j 2 - - name: Test - working-directory: ${{github.workspace}}/Build/MacOS_${{matrix.build_type}}_${{matrix.clang_version}} - run: ctest --output-on-failure --verbose - - android: - runs-on: ubuntu-latest - name: Android - strategy: - fail-fast: false - - steps: - - name: Checkout Code - uses: actions/checkout@v4 - - name: Setup Java - uses: actions/setup-java@v4 - with: - distribution: 'temurin' - java-version: '17' - - name: Gradle Build - working-directory: ${{github.workspace}}/Build/Android - run: ./gradlew build --no-daemon - - ios: - runs-on: macos-latest - name: iOS - strategy: - fail-fast: false - - steps: - - name: Checkout Code - uses: actions/checkout@v4 - - name: Configure CMake - run: cmake -B ${{github.workspace}}/Build/XCode_iOS -DTARGET_HELLO_WORLD=OFF -DTARGET_PERFORMANCE_TEST=OFF -DCMAKE_SYSTEM_NAME=iOS -GXcode Build - - name: Build - run: cmake --build ${{github.workspace}}/Build/XCode_iOS -- -sdk iphonesimulator -arch x86_64 diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/.github/workflows/determinism_check.yml b/crates/joltc-sys-patched/JoltC/JoltPhysics/.github/workflows/determinism_check.yml deleted file mode 100644 index 764497b1935..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/.github/workflows/determinism_check.yml +++ /dev/null @@ -1,119 +0,0 @@ -name: Determinism Check - -env: - CONVEX_VS_MESH_HASH: '0x10139effe747511' - RAGDOLL_HASH: '0x777396947c3fff6a' - -on: - push: - branches: [ master, 4.x ] - paths-ignore: - - 'Docs/**' - - '**.md' - pull_request: - branches: [ master, 4.x ] - paths-ignore: - - 'Docs/**' - - '**.md' - -jobs: - linux: - runs-on: ubuntu-latest - name: Linux Determinism Check - - steps: - - name: Checkout Code - uses: actions/checkout@v4 - - name: Configure CMake - run: cmake -B ${{github.workspace}}/Build/Linux_Distribution -DCMAKE_BUILD_TYPE=Distribution -DCMAKE_CXX_COMPILER=clang++ Build -DCROSS_PLATFORM_DETERMINISTIC=ON -DTARGET_VIEWER=OFF -DTARGET_SAMPLES=OFF -DTARGET_HELLO_WORLD=OFF -DTARGET_UNIT_TESTS=OFF - - name: Build - run: cmake --build ${{github.workspace}}/Build/Linux_Distribution - - name: Test ConvexVsMesh - working-directory: ${{github.workspace}}/Build/Linux_Distribution - run: ./PerformanceTest -q=LinearCast -t=2 -s=ConvexVsMesh -validate_hash=${CONVEX_VS_MESH_HASH} - - name: Test Ragdoll - working-directory: ${{github.workspace}}/Build/Linux_Distribution - run: ./PerformanceTest -q=LinearCast -t=2 -s=Ragdoll -validate_hash=${RAGDOLL_HASH} - - msvc_cl: - runs-on: windows-latest - name: Visual Studio CL Determinism Check - - steps: - - name: Checkout Code - uses: actions/checkout@v4 - - name: Add msbuild to PATH - uses: microsoft/setup-msbuild@v2 - - name: Configure CMake - run: cmake -B ${{github.workspace}}/Build/VS2022_CL -G "Visual Studio 17 2022" -A x64 Build -DCROSS_PLATFORM_DETERMINISTIC=ON -DTARGET_VIEWER=OFF -DTARGET_SAMPLES=OFF -DTARGET_HELLO_WORLD=OFF -DTARGET_UNIT_TESTS=OFF - - name: Build - run: msbuild Build\VS2022_CL\JoltPhysics.sln /property:Configuration=Distribution - - name: Test ConvexVsMesh - working-directory: ${{github.workspace}}/Build/VS2022_CL/Distribution - run: ./PerformanceTest -q=LinearCast -t=2 -s=ConvexVsMesh "-validate_hash=$env:CONVEX_VS_MESH_HASH" - - name: Test Ragdoll - working-directory: ${{github.workspace}}/Build/VS2022_CL/Distribution - run: ./PerformanceTest -q=LinearCast -t=2 -s=Ragdoll "-validate_hash=$env:RAGDOLL_HASH" - - msvc_cl_32: - runs-on: windows-latest - name: Visual Studio CL 32-bit Determinism Check - - steps: - - name: Checkout Code - uses: actions/checkout@v4 - - name: Add msbuild to PATH - uses: microsoft/setup-msbuild@v2 - - name: Configure CMake - working-directory: ${{github.workspace}}/Build - run: ./cmake_vs2022_cl_32bit.bat -DCROSS_PLATFORM_DETERMINISTIC=ON -DTARGET_VIEWER=OFF -DTARGET_SAMPLES=OFF -DTARGET_HELLO_WORLD=OFF -DTARGET_UNIT_TESTS=OFF - - name: Build - run: msbuild Build\VS2022_CL_32BIT\JoltPhysics.sln /property:Configuration=Distribution - - name: Test ConvexVsMesh - working-directory: ${{github.workspace}}/Build/VS2022_CL_32BIT/Distribution - run: ./PerformanceTest -q=LinearCast -t=2 -s=ConvexVsMesh "-validate_hash=$env:CONVEX_VS_MESH_HASH" - - name: Test Ragdoll - working-directory: ${{github.workspace}}/Build/VS2022_CL_32BIT/Distribution - run: ./PerformanceTest -q=LinearCast -t=2 -s=Ragdoll "-validate_hash=$env:RAGDOLL_HASH" - - macos: - runs-on: macos-latest - name: macOS Determinism Check - - steps: - - name: Checkout Code - uses: actions/checkout@v4 - - name: Configure CMake - run: cmake -B ${{github.workspace}}/Build/Linux_Distribution -DCMAKE_BUILD_TYPE=Distribution Build -DCROSS_PLATFORM_DETERMINISTIC=ON -DTARGET_VIEWER=OFF -DTARGET_SAMPLES=OFF -DTARGET_HELLO_WORLD=OFF -DTARGET_UNIT_TESTS=OFF -DUSE_AVX2=OFF -DUSE_AVX512=OFF -DUSE_LZCNT=OFF -DUSE_TZCNT=OFF - - name: Build - run: cmake --build ${{github.workspace}}/Build/Linux_Distribution - - name: Test ConvexVsMesh - working-directory: ${{github.workspace}}/Build/Linux_Distribution - run: ./PerformanceTest -q=LinearCast -t=2 -s=ConvexVsMesh -validate_hash=${CONVEX_VS_MESH_HASH} - - name: Test Ragdoll - working-directory: ${{github.workspace}}/Build/Linux_Distribution - run: ./PerformanceTest -q=LinearCast -t=2 -s=Ragdoll -validate_hash=${RAGDOLL_HASH} - - arm: - runs-on: ubuntu-latest - name: ARM Determinism Check - steps: - - name: Checkout Code - uses: actions/checkout@v4 - - name: Update index - run: sudo apt-get update - - name: Install Cross Compiler - run: sudo apt-get install gcc-11-aarch64-linux-gnu gcc-11-multilib g++-11-multilib libstdc++-11-dev-arm64-cross qemu-user -y - - name: Configure CMake - run: cmake -B ${{github.workspace}}/Build/Linux_Distribution -DCMAKE_BUILD_TYPE=Distribution -DCMAKE_CXX_COMPILER=clang++ Build -DCROSS_PLATFORM_DETERMINISTIC=ON -DCROSS_COMPILE_ARM=ON -DTARGET_VIEWER=OFF -DTARGET_SAMPLES=OFF -DTARGET_HELLO_WORLD=OFF -DTARGET_UNIT_TESTS=ON - - name: Build - run: cmake --build ${{github.workspace}}/Build/Linux_Distribution - - name: Test - working-directory: ${{github.workspace}}/Build/Linux_Distribution - run: qemu-aarch64 -L /usr/aarch64-linux-gnu/ ./UnitTests - - name: Test ConvexVsMesh - working-directory: ${{github.workspace}}/Build/Linux_Distribution - run: qemu-aarch64 -L /usr/aarch64-linux-gnu/ ./PerformanceTest -q=LinearCast -t=2 -s=ConvexVsMesh -validate_hash=${CONVEX_VS_MESH_HASH} - - name: Test Ragdoll - working-directory: ${{github.workspace}}/Build/Linux_Distribution - run: qemu-aarch64 -L /usr/aarch64-linux-gnu/ ./PerformanceTest -q=LinearCast -t=2 -s=Ragdoll -validate_hash=${RAGDOLL_HASH} diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/.github/workflows/doxygen.yml b/crates/joltc-sys-patched/JoltC/JoltPhysics/.github/workflows/doxygen.yml deleted file mode 100644 index c1a0f88e7e0..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/.github/workflows/doxygen.yml +++ /dev/null @@ -1,26 +0,0 @@ -name: Doxygen Action - -on: - push: - branches: [ master ] - -# Builds and deploys doxygen documentation -jobs: - build: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v4 - - - name: Doxygen Action - uses: mattnotmitt/doxygen-action@v1.9.8 - with: - doxyfile-path: "./Doxyfile" - working-directory: "." - - - name: Deploy - uses: peaceiris/actions-gh-pages@v3 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: ./Build/Doxygen - force_orphan: true diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/.github/workflows/sonar-cloud.yml b/crates/joltc-sys-patched/JoltC/JoltPhysics/.github/workflows/sonar-cloud.yml deleted file mode 100644 index 0ba40c345fd..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/.github/workflows/sonar-cloud.yml +++ /dev/null @@ -1,59 +0,0 @@ -name: Sonar Cloud - -on: - push: - branches: [ master, 4.x ] - paths-ignore: - - 'Docs/**' - - '**.md' - pull_request: - branches: [ master, 4.x ] - paths-ignore: - - 'Docs/**' - - '**.md' - -jobs: - check-secret: - runs-on: ubuntu-latest - outputs: - sonar-token: ${{ steps.sonar-token.outputs.defined }} - steps: - - id: sonar-token - if: ${{ env.SONAR_TOKEN != '' }} - run: echo "defined=true" >> $GITHUB_OUTPUT - env: - SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} - - build: - name: Build - needs: [check-secret] - if: needs.check-secret.outputs.sonar-token == 'true' - runs-on: ubuntu-latest - env: - BUILD_WRAPPER_OUT_DIR: build_wrapper_output_directory # Directory where build-wrapper output will be placed - CLANG_VERSION: 14 - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis - - name: Install sonar-scanner and build-wrapper - uses: SonarSource/sonarcloud-github-c-cpp@v2 - - name: Configure CMake - run: | - cd ${{github.workspace}}/Build/ - ./cmake_linux_clang_gcc.sh ReleaseCoverage clang++-${{ env.CLANG_VERSION }} - - name: Run build-wrapper - run: | - build-wrapper-linux-x86-64 --out-dir ${{ env.BUILD_WRAPPER_OUT_DIR }} cmake --build ${{github.workspace}}/Build/Linux_ReleaseCoverage - - name: Run unit tests and create coverage report - run: | - cd ${{github.workspace}}/Build/Linux_ReleaseCoverage/ - cmake --build . --target test - llvm-profdata-${{ env.CLANG_VERSION }} merge -sparse default.profraw -o default.profdata - llvm-cov-${{ env.CLANG_VERSION }} show -format=text UnitTests -instr-profile=default.profdata > coverage.txt - - name: Run sonar-scanner - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} - run: | - sonar-scanner --define sonar.cfamily.build-wrapper-output="${{ env.BUILD_WRAPPER_OUT_DIR }}" --define sonar.cfamily.llvm-cov.reportPath="${{github.workspace}}/Build/Linux_ReleaseCoverage/coverage.txt" diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/.gitignore b/crates/joltc-sys-patched/JoltC/JoltPhysics/.gitignore deleted file mode 100644 index 132f40ca483..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -.vs -.vscode -.DS_Store -/profile_chart_*.html -/stats*.html -/snapshot.bin -/*.jor -/detlog.txt diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/.gitignore b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/.gitignore deleted file mode 100644 index a11c7ca5a38..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -/Linux* -/VS20* -/XCode* -/MinGW* -/Doxygen -/CoverageReport diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/.gitignore b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/.gitignore deleted file mode 100644 index be3ca89c736..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -.idea -.gradle -.cxx -build -local.properties diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/PerformanceTest/build.gradle b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/PerformanceTest/build.gradle deleted file mode 100644 index dc9eb4f92f2..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/PerformanceTest/build.gradle +++ /dev/null @@ -1,51 +0,0 @@ -plugins { - id 'com.android.application' -} - -android { - compileSdk 33 - ndkVersion "26.1.10909125" - - defaultConfig { - applicationId "com.joltphysics.performancetest" - minSdk 21 - targetSdk 33 - versionCode 1 - versionName "1.0" - ndk.abiFilters 'arm64-v8a', 'armeabi-v7a', 'x86_64', 'x86' - - externalNativeBuild { - cmake { - cppFlags '-std=c++17 -Wall -Werror -ffp-model=precise -ffp-contract=off -DJPH_PROFILE_ENABLED -DJPH_DEBUG_RENDERER' - arguments '-DANDROID_TOOLCHAIN=clang', '-DANDROID_STL=c++_static', '-DCROSS_PLATFORM_DETERMINISTIC=ON' - } - } - signingConfig signingConfigs.debug - } - - buildTypes { - release { - minifyEnabled false - } - } - - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - - externalNativeBuild { - cmake { - path file('src/main/cpp/CMakeLists.txt') - version '3.22.1' - } - } - - buildFeatures { - viewBinding true - } - namespace 'com.joltphysics.performancetest' -} - -dependencies { -} \ No newline at end of file diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/PerformanceTest/src/main/AndroidManifest.xml b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/PerformanceTest/src/main/AndroidManifest.xml deleted file mode 100644 index fc5682a7476..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/PerformanceTest/src/main/AndroidManifest.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - - - \ No newline at end of file diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/PerformanceTest/src/main/cpp/CMakeLists.txt b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/PerformanceTest/src/main/cpp/CMakeLists.txt deleted file mode 100644 index ab8db81ba63..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/PerformanceTest/src/main/cpp/CMakeLists.txt +++ /dev/null @@ -1,20 +0,0 @@ -cmake_minimum_required(VERSION 3.10.2) - -project("JoltPhysicsPerformanceTest") - -# Make sure we include the app glue sources -set(APP_GLUE_DIR ${ANDROID_NDK}/sources/android/native_app_glue) -include_directories(${APP_GLUE_DIR}) - -# Set repository root -set(PHYSICS_REPO_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../") - -# Make targets -include(${PHYSICS_REPO_ROOT}/Jolt/Jolt.cmake) -include(${PHYSICS_REPO_ROOT}/PerformanceTest/PerformanceTest.cmake) - -# Link shared native library -set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -u ANativeActivity_onCreate") -add_library(PerformanceTest SHARED ${PERFORMANCE_TEST_SRC_FILES} ${APP_GLUE_DIR}/android_native_app_glue.c) -target_include_directories(PerformanceTest PUBLIC Jolt ${JOLT_PHYSICS_ROOT} ${PERFORMANCE_TEST_ROOT}) -target_link_libraries(PerformanceTest Jolt android log) diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/UnitTests/build.gradle b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/UnitTests/build.gradle deleted file mode 100644 index 8f0b3f7e69d..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/UnitTests/build.gradle +++ /dev/null @@ -1,51 +0,0 @@ -plugins { - id 'com.android.application' -} - -android { - compileSdk 33 - ndkVersion "26.1.10909125" - - defaultConfig { - applicationId "com.joltphysics.unittests" - minSdk 21 - targetSdk 33 - versionCode 1 - versionName "1.0" - ndk.abiFilters 'arm64-v8a', 'armeabi-v7a', 'x86_64', 'x86' - - externalNativeBuild { - cmake { - cppFlags '-std=c++17 -Wall -Werror -ffp-contract=off -DJPH_PROFILE_ENABLED -DJPH_DEBUG_RENDERER' - arguments '-DANDROID_TOOLCHAIN=clang', '-DANDROID_STL=c++_static' - } - } - signingConfig signingConfigs.debug - } - - buildTypes { - release { - minifyEnabled false - } - } - - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - - externalNativeBuild { - cmake { - path file('src/main/cpp/CMakeLists.txt') - version '3.22.1' - } - } - - buildFeatures { - viewBinding true - } - namespace 'com.joltphysics.unittests' -} - -dependencies { -} \ No newline at end of file diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/UnitTests/src/main/AndroidManifest.xml b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/UnitTests/src/main/AndroidManifest.xml deleted file mode 100644 index e31e98364ae..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/UnitTests/src/main/AndroidManifest.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - - - \ No newline at end of file diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/UnitTests/src/main/cpp/CMakeLists.txt b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/UnitTests/src/main/cpp/CMakeLists.txt deleted file mode 100644 index de46f4c1e9e..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/UnitTests/src/main/cpp/CMakeLists.txt +++ /dev/null @@ -1,20 +0,0 @@ -cmake_minimum_required(VERSION 3.10.2) - -project("JoltPhysicsUnitTests") - -# Make sure we include the app glue sources -set(APP_GLUE_DIR ${ANDROID_NDK}/sources/android/native_app_glue) -include_directories(${APP_GLUE_DIR}) - -# Set repository root -set(PHYSICS_REPO_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../") - -# Make targets -include(${PHYSICS_REPO_ROOT}/Jolt/Jolt.cmake) -include(${PHYSICS_REPO_ROOT}/UnitTests/UnitTests.cmake) - -# Link shared native library -set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -u ANativeActivity_onCreate") -add_library(UnitTests SHARED ${UNIT_TESTS_SRC_FILES} ${APP_GLUE_DIR}/android_native_app_glue.c) -target_include_directories(UnitTests PUBLIC Jolt ${JOLT_PHYSICS_ROOT} ${UNIT_TESTS_ROOT}) -target_link_libraries(UnitTests Jolt android log) diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/build.gradle b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/build.gradle deleted file mode 100644 index 5b7ac75ff5a..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/build.gradle +++ /dev/null @@ -1,17 +0,0 @@ -// Top-level build file where you can add configuration options common to all sub-projects/modules. -buildscript { - repositories { - google() - mavenCentral() - } - dependencies { - classpath 'com.android.tools.build:gradle:8.1.2' - - // NOTE: Do not place your application dependencies here; they belong - // in the individual module build.gradle files - } -} - -task clean(type: Delete) { - delete rootProject.buildDir -} diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/gradle.properties b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/gradle.properties deleted file mode 100644 index 193a5b26e77..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/gradle.properties +++ /dev/null @@ -1,22 +0,0 @@ -# Project-wide Gradle settings. -# IDE (e.g. Android Studio) users: -# Gradle settings configured through the IDE *will override* -# any settings specified in this file. -# For more details on how to configure your build environment visit -# http://www.gradle.org/docs/current/userguide/build_environment.html -# Specifies the JVM arguments used for the daemon process. -# The setting is particularly useful for tweaking memory settings. -org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 -# When configured, Gradle will run in incubating parallel mode. -# This option should only be used with decoupled projects. More details, visit -# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects -# org.gradle.parallel=true -# AndroidX package structure to make it clearer which packages are bundled with the -# Android operating system, and which are packaged with your app"s APK -# https://developer.android.com/topic/libraries/support-library/androidx-rn -android.useAndroidX=true -# Automatically convert third-party libraries to use AndroidX -android.enableJetifier=true -android.defaults.buildfeatures.buildconfig=true -android.nonTransitiveRClass=false -android.nonFinalResIds=false \ No newline at end of file diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/gradle/wrapper/gradle-wrapper.jar b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index e708b1c023ec8b20f512888fe07c5bd3ff77bb8f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 59203 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
        f+CS3i{$QfT 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

        Q9t)f-@PSL7)=@sPfGB^f5{BcXd3ggxMfG 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 diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Images/QuadTreeExample.png b/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Images/QuadTreeExample.png deleted file mode 100644 index 9e49489cf12e75aab5f26d435a18394305554f54..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 65699 zcmYhib9CHav_3qs8a1|Un~mAnXwuj=8>g{ttFf)dw#|vn-}HO$UGF<<%^Li%KWFc= z@$7w`6aHO65)mF39smF!N=u0;0{{@`003A%EI8<&8`rf{&|4QDT<913%~6RFwnwBz=rJ-hW$l>QVM}sVf}g88SUeB>9avM^4W-Z50Jrnfm@j)5y{-3V^E{8^nu3UiEtBoWa0MOE;C^Se) zmNwk%R6FqElZ|1_@(TdaKpP#}3O~5!nJ$MF-qS_A*3!~+Zz+HT0Cd-Se`$VxjPI?j zq+5D!cfc9%rA7_efBDA8?;OL;_m!83Zy~yTj0Y!xjSGJRp91kbEUZ{^y20EtVn{08 z!wASa?=^TPYn!(@5RJ38;fmyv<6b}9e301!r m{j#+=b)m|jXwy3D6qB^s-LvZK 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%0A*lMC;xAs1sFr`kN;FdsZoGV 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 h-v+_m=E|r|RaeZs*?Rf|O(r6nAiF3e? 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 X_nqAq_YIJFf`XD1l@oyo{qX;PjVpQ$ diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Images/ShapeCenterOfMass.jpg b/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Images/ShapeCenterOfMass.jpg deleted file mode 100644 index 7126964e9892e190c04a170d7028936cbd07faf6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 30218 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 diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Logo.png b/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Logo.png deleted file mode 100644 index e1b80cc97d99062c9d8f1557a8594a1083f17fd5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1937976 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

          !*e_H=^oJliEo9#t8n|C_8(^qwcpt${{)WPD~@ zvgwAmC<=|uTSguh;3)mh;Xj?_9|fcsiU->sT2la)VdN8AWU!htJo#n*{vX{`wF|J( z?BmYq67=ChP>6=~b7)*$Oym zWdZvISJ=5p=%u>!(SR&VlB6wCkcL3xsG=0IGY`jS3V7|ygVRPq?<6!?w%Lh?O1+BR zm&quqrQ`>jt}*1yOBaF1)38R~0)R2hQ{d#5k1FX|OsRrAKZ>m?dQ`T`PJt&(GX(7M zPBFH7l%b=QNQtXGZ?BvXBtbC=|34P1w7n6opdEE`ArIjVt(_;&5PmJb^3GH z7u1?cY|6<)3C+;C9)~aGndDa!%XttuJVP#NOBb##`C(o)_}w}&lA$vh3iz{{&g6UV zT`(W~(Nz=Hbg?{k`Zpd}lljULk9X@`_uZ9GG7owJix=npzeLaL(SI?slI$hVw9SFw zZZ-mGPx{p>FWJ52sPrW%CaiY!oW--$DSnqMJO+F6MFf2sCOyNa;B#FYvUwgqxTmx_ zmWA)r*cgDven?wTMdRDkibmMOwKGDeB1QJ=cek4|1Qh*zlYnpnqQ5XS=daZs@VUYUIq#g(nS_ zufKI55UJ6k49u?yCee!c*@cs?RAOWxq`-QYEX-J?M@Wo^lIQ!7!A&fQt8V>=U*A;) z;Vh87$kvg`uF-o=nIM*(|B*zKN-`AW=fXD@Je=r)oi6Utt1~#P^ zupzKrW|Jt@LhhZel!uj~$%0g72FO-XqUNnIROUxpX-XK+L;wg_I}8F}d2{gwI@?31 zkUhV~AF<1fEu?bJTq%EEJFn_+f3aYjv=fU>=3I(eLytW#+ik`$Co>Owk<*JkOU8tu z*!H8tTmCyed@d71!Gu%wLza@r&*JKk?q5<4$u2rHosm|)l++!=(8#B z7buhrlZ537HNxWIH&hwenKSJrQse*A5t&V;mi`o^w8hfbaSUT8qzwpsrH@OXHR0%@ zcS;F#rl9iI+b@@L4sWR_^j0q861|Cz4rT%NuN07hy?GJsolGw5{9A!Hnw|ClelBKV z5#M`#X9zn1+sq^1c`01U<>W^beiIk z?bXLmS=OT1f@&F$NDZw{C4*LC)8ngRsSeT6=#>&q3;10X66iR|@3j~N7BVpPo;2}4 zrcIJSX+p2k3+P=!N>?%Tc86iVk;hGb)p!}=rW6#hsyPf%1i}K9`R3EHjKv-dJ0S)A z0R`Fm&IdPFeL8~-+u)GYWNE(`I=%1g$CH{RibhpuD=X(F8S~8E@&Y5PIoe|M9%-^b zq@66KUD6u~tjv%s*WmD#IDe1o>jJJ?P-Y%`s-t)Sj_tkRp^Dxblb+rxhE6jHnFpFP z@}I|=j+QiQhLPVb&tuIqXC@;#ujx`;q>HnY+`|fr`|0J%mB-E{C$Jvu%Z3x$i?Fng zw|_?~cl;eqm{&rrVYm^_8+M6uRYo@H8;Pnso8~CK&u%@FVYn1|sYStGp@ct`m(mux zKeH9M%BfuIRG#4Q$J;0Df@L;_|7%mmpeozkSjxb;_CwU-p&rBLc%~C$b5AW^Rwy_s?G3lqH0!bLOVo zE!*%VfvPHvTUiAz`SF!UR=UbU6wf50X$Xh61l<2pc9U$bQBpP%Vhgs3s{gOQ+!)y3 z+CEcEMky5{0~_oon@O1rUZkGol&9Q5j9on5UqVry!qY;Jj$%hp9YPcW0$l}cdwP{c z`0G7YWlQAz)BPjl-9(kS-GgBtjv-S$w{I;U9MZh^;l|Iz7G#nkA8w~d#V~|^dy_-F zfD9itCR-D#c7ElhO{uCDZ;HgTMcIVdcH$=drB$S-+r1pJ)MS4Vq#xVk2m5*FWK-ZV zu3~Hi3va(ZYt2?{`t2vBUD@%wdI3FUzDS*?C-my)pFhGR%9O2FP$02A6@eQ;j8mjO zo6?^~B4-yYDTibRlb%WI(bxh_Sz>&!e@`&k+R0ro6bzBFKRn?)=yZ4_OhztGI=dh_ zPbsq6|D^RU=);>2ct{WN{j-y7N1KL%NmL=h_aws~F@I@;smBD@+B5j~ABLSy;C>bz|DW1H(#!Z>FG_f#3 z4mLc1pM4?Y#W!x|`yhu0glqB4Jz+VcGck}M3j&; zM0c`DRrR)&%^#&@;F|vr8L}iE2IhKoLHYYO3x=tNtz9{f?JPC%*DLdE#qf(uETxq{ zo3M+xRQtZ`Oj@&Po}_^Iq>#?py~FyDlagE9qn7e~c8Jx66_sNqxum?Nx zD_1T&ftAk!zc^3R3yCR%lPHhX)Yixia?-{bisj`0_~vX*`Qi)xdb+qgXvVzt!GTz| zPUFiXhy0UBNlTR#kzHCrPG9|$-=9p+X%7Y-JE43F&(V5c^Bo;_tGq*S6j4 z)9_=fEaJpXeB0)Ge}4O?#3KV6Kgj+U-`&~b+pq3qUW7t^^WNsK$5kzNGNyrT+*C>^ zL*)V%39NC52kbH*garTQhdTqm7t!x7)bi}%*>wsEDZ~Ea3!ADIUtGfou`xDxt zd{Z09$&`VwSJiutoO<`@Gt{0WwFiSmU3etSTqvhJ+N3HC0)f+2Y+nX6E28$pqV5r>vbLGBj!3V$JuJ!(pEVIcTO8e6Wdi>q*>n&PyG$i$2`b`5i zp7x>>rP@J2ply>|NenfjI2Fa=)b)5=H7PKD$(W4A&T5iPP^#cV$n;e4 zqt7paKxOIF;8d%RaM2Ber~5(vNmC(HWvgu&ED2RgZ;GN>Pi8Z@DLQUCsncOc;-n;n zrJN?4WUuUN=~!Ax@2LOEo5%2aR*qk+Q7(-kV^{XLB*VWl$r6)rH=Wu=>PzgYsmvLH zXZLkM9>N%I$wgM7BzX!p%a!}&wi8(Mr$0O>%4EUHm&%fC9)8{@<>R8SOZkR9{JY{3 zHQyjG{EotvDcd7$)rwtqSilems|-CJXEV zubRQ*oEBu1C{t9Jd^x#7XMIY=B+}4vX$0^bZ{zFwl1ZzzYFB)n;M4MCaFYs zX7cqY&&vEmnVGi=1SzxKW6Qeuvwq0D>v?NoB3_@TDEVa$)+9NqS(#*wOjx;Ie+Ik* zUxf1pa4MJT<$gwc*E}6-apj5Q6Y&hJlP% zT1trQKl$(c)AmG( z_l}ml?*{&1JRDtU=YHj-o3$&uo+POktX=wDb-0^={5m1WuX61sietbi_QhZCoY{n! z?H~=0h}tZNqr#b0h6l1;wGybNCs7nl7}yxtt{Rl7PjLFxC#+#6PK#b6rMo1nwh{NL zos^*Dc@wcf_Uw;7{{Am+JVts`Ld?*QL3oNquVycTEr2sK^wJcktE4IvC$>A%qC?J3 zJmPoTi>DlG0|B=1dz8y1)Jms`Q{|oDD)V|kJ28gz)-~dZMtGG4AzFN)A6Ss9W}8+5 zZO|$sr=k#~?K{daE7Q>HtU%n4nCr~8GRUc5Sx`jpwTo-Uq;XGa42)Fg#rcxKjK z`s3#gJZUV^zv-3O%+Nhars^7b>?{Uf)rKKyED3rE_W0)XC8a1;=+624lo;j_@@wfw z`-Dj*UJ*Oz0vk)%_0(cI>mgWlbI0K-;Tnaa#tmslTlM*d02d82)rxNdt$X81fs z$%cB{1neAgHLnw4`~9sE3Lg@j!xkNcu^s7X}a^&7{G zV_EFk7+aaP7`KvX`OM+h#I%L0$f&C1a<-){V_6Y^d$eB2cJ=jmeu_b0*~W+s{<)v* zb_7+R+Bd0|&ADRRVjJVp$DjJzrpg?RjC?+bT4h*lay5%UHuH%cH7T%cqA$I8^HmiA z3ANw>J6VwRVf^#&+*CnFAWcFg4-VHtteXHErx(Dmr}%BC^y<5dv{R2WWf<~M-b8J8 zg!U00@T;CO4ain!6Db>p7**}D{q60e@x|x2SI>bB=>8O>6Pp14Kmfn{lCaYNdu+Q3 z`3cg+7!&jX(W9si{q~%MqO=P#yyT&pr6JIEBEOqtlva}(+X6eC*-AEh%9FEF*-v-Jk=>oSrbj_JTxBOIUC2leRqR9_4ysIUnVmf_DVa-kNEZx4N~$tQEqI2pzR8_d zmE5On4`2^hKPk?BLtKZB95_rn1Z4>ad&-J;rRW~9GNfOAn;=uU^pujGIhnV1Qa_Ac zpHNDc^_(P5mICJqmkveQuFB|V6Q?pC#=Lcj^PYb965qS8;214i$LJ9UmyEOtdu7Mh35ij#hKuToEYLRuxIxnes#zroJR&3 z{;z+ub1IO8RP)H8#4uF#>6`x|$52yA6;Jv>p0ajDS%zV8R-fHJ#fks2pvfEDdM%PU zgKxCNZ`rU+!`oIAB@4*-*FU(aawVn(l@2|baUDf5qeM0|PNmXQX%6Q$N$y$|R$5?iZZ5!#R%}-V`9+=*ryy^e z%~%YF)8wi_mSyiqC5vxg)gtm`jwj24qp)3qZJqI^-^e+PDr;0)dzn>?+=FSGtV-~? z=MOqnYi@@m17#DHT~KB=;4rpdGA{)@-7kjvLg~6Z4zGlh5N9bdux)gE= z)T3(m6r&)2JI`7aucZI$MXx49XEF>)-i#+Ch|)^+Xp$k)SZ2Efe$b}yieRZKV{+M1 zf3e(>;V{x!1h-^Owo`jtGiG`Gd3s*{ITKB3I<@z^Ln9du=1xbrGWo;VN>3Tr4f_~* zJQG-*yEq6gm2>z6s0YPlP5F-`2~v}G-hqFVNk>chJ6$=vsOo#$$1Sq}*RTxnM`l+oQ-lgNk@}lB(5rF#;PR01!8m3vY#qVKT1w%bm}Pn z@|9f`un$7^NibeIyh8{26N?0ss&Z<>_7I`Ni4`X{HLNno|8IYIqha}juijjBh$r!- zH~5*PD;gn>*Ef6QE>85cvIz7k7F6YTzf_$Z56J0x9Gaf}@$PCJr=`gt!-)ou7wEPZ zJj6N!d?xy`GRrhPFp>!T*~&tbJa&Q&!G6h?Oui6MmOzzS+&iT5!^8i-@3u-iWiUdp zA%B@h=~1qVY% zgqr^P^@AClG!mSwz9~-MkD0htO|)idW2wsD$Qb(#p2>eK7P?G}r}c+enUs~4Jq#Z@ zfshoGsu?^OsU)fb-7lo~%PM;@jdw0O(x%10mn#Y?+t4nuai zb+;JB20V1(#p4%Raoe;)8n(rY+yCIhnX?IKxC_sEjMBT2turw@-IN9kQ!4nC&HdVg zp;AZJXMghXAAWsP1-(4=>Q<8%p^`~VdjzXCB)eeY{Y{HQFytv9GuGpDW+=yYC$nLw zbWXn~7Y&B(#M?GU;)KF}G)7gH4u8a%O(d~1^B9`4Gz79xg<@C$*nhU2lg+>20EZ;1 zaz}Z2trdC{uwMSDL;h59+~RQ)*FTmGTegpr*bv3(zt^Oy(uT=@c!0hmQlOj&G6e8+ zKgh2@y$RXlrI|^t1XMgyu#J`hwSaF7`VU+EF-=&tECGj)A!Cw<|b@D4Uw!@9{Zw3&p z6I?m`SXKOI{E;G)P+iK_BdUEyQZ+>y$w7p5P@5mb`oyN|R z-2Q+0{TlVFcXr~>#J4LnRt~F)JX-!gf4zBd`%m|e#>a`lcV8aWgvX0f)T4Jc7Xaeq z3Nn7y_Jfd&s@V&z*4Ri=Qv}SEkPHj-Wtrc}(y00e+v~g%Rq<%2GoRJ5)8b-# z1^G8XH(5~LPh@y4`lgVA@!9Pz?T5E6OFk;hU3X%%^13(3X)leEFxF zviMyU#r78(8TmcHe#j{39wDdwGQPUKk_cNH$4`CASnTw`CS>c>NVLfIQ+^oRcel5i zeq(zFB?6V7`r4-2QR|C`&nMy(H$Ad*8b#fC#?9BPL5ZTeG2(l({X^8bT1mdUHe`RI zE-*5@r$VFBQ39F6b*i~}w%2}EogTIHzxY%Wu_}2kJ7AD7-+bhk(=s933r$+^7jg;@ zG(vAW`%4CuoL5VL20ym`hShB(T(xj>{>4F|B=(jg{JV}?y!(!HGOmR|O5*LOtJBJz4Y1wd1g_?R2 zM^R!h-I+}o#gnA7gbejdOvE%T3Ci&XaxO*Xi8ebXJH4KK!3mu|JqIvQ0OgOy@a-S)_y>a89&*^0oo zF`_iCT8zeaDQ#qx#befDuxa8!`&=|(HPow>z$Fx9uB=qX}>~<&6|H| z;=lllv1RziWU96WGMrlcq#>$k(y-DShpGlZDm?B*;75it4X_8B(#}${aFJ|pvf=UM z0EW=yR#)wG29UvX^^-t$&r_Es+45~tt=rfF|;@^ zl?IJ%mrz1VHF2yp?svFr1j^p(R1@5h%DlpE9(5Gb_9S?+6n+%i(q;acN2}>-`my;$ zJIY+6Rtr+8u6+sQnqCSXTQ6EjR$`D_Q!wq z+UEZd0Hs?61x+fy^V-hV0VcyC7Nmhq)zA^1G+a#^ zYyeF-RVtwe&w5b`WTjQ$AKF=F73_r6XmLj%J5P8xk@JE$tHK%V2RkXf7~2@xnlhnk zc|>4Q=Bo)Kw_7KjC^MD?+bns~5ZFG9MLyUbBTM6|4|tQx7h~@@PYUepC4Z(y6~pV! zV%!97>awhMHiGz7#w^*wO8bjo2vH7nh>d~|T5w>Z>XN6Io%GvY5(_o)djoc79&51e z!6;W)r~XTNSKNM7WnjNT_9T?Vi|MunS(Y#PIP+*cm2xDLFgexJZIxw&_IFBC(eyP< zK|iM$b|FeHkX6||wtJ33o-*v=(`h?&x2DsfhOQ^!YcgLkxw)>J?y2OuTQ9|BuD~i6 znLltmC2=Z84?6_q$Ci22ZCcO6IVZ`Nnl2TRPT5-gTjFQ7pTQ-HZw$j!39%Et_OngN z$Go$pfz7wKqunJ%oH;w`^fyJEL%r~rZCHuzquPFh$Nv15!)C|x%l%E1T9ziM)r8nd zoEE66HT|-wW{KhX&CO&}ga}o?xxMo=cSjC>PRUl#32GiKh<&xrTZd2krqdhGql3Ty z_u`XY**5%)aNvK?ynR&iL#2-JKm7WQGX+9hz*Wvg3mgCTc}WB!P??)6s|ZRjASW_A zb;|wSpWpa}!}VV0H4jBoikF_!7>7JTLubv%B59AiRi+XHy-$mpYG)vQLdYQu^q{`EpFb zJIZ>IR{{7l(XEBd!0aL5XPis1N!Q4OGn;T0e>Vg&4IrI>2SIOL!ckC+HF@S5=dmp9 zC5nKM;aX2PNesgfIm;78ky%`#d>9vhR%Nrex&%!ru*c49U7~CJVif1)oYQBc#G14x zX_mx8|4Q*v?9rTadl=p+fhB7F-6t!%6TZ`;bhkc3huBjySbv+Y9}Z8)T3q=Aa^YJD zAMz_9$0qyip*nDQCV3GKhC2E(Z6*UKO|JHka?yUl)vW?QUr`suqtZ z;=G-3C@xO^`>A84zmIz>>2dCrjBfKiYfx zq(6Bm!5)Qp!qIqM4?#g5>t&Q!K~(xb{li_g#12UTy{ja!#C8vQD1Bk4ES}_AG-h}fEsCr#X-XvXniBHi;Vu83iQ@{plGd@puXLu}QCiQ(B30i&giNn@Z;hSj zFm_09&B3TuNAEmO_v0atZ2vmK>_0a6S&c(H660=@f30O*t-)U23=Xx^9n%LAV>Fr;WuybD2kW*82{V;=_^Zcjw zZZh!fBxcQM{gHk9?mGTfX6~Yw$H=Lx)Bly_$|spSe+HJX$4-1ca~p=49~b!y7nr;< z^Id5uTkpIJ=x9xM{q0rs?-SsJJ+nM(x)V!ZR>;7@DcR&}4Th^jNG0^8T!z0WiyoLw zrBw%MdOWK9!90g5zn@V(C)3K-I5o(ZxARSQCRCNy_rK^I!|cAtKf1Z>JRd@*Scfw( zmzIJ_CCTJCCzy|Pa5p5RFo@mLZ zZj+FGN>!!c&Qolfc%qQ2*}KqQ{-ftMhVa>=kw=R?Hb}yPz}u4txBbOSwIg;hSIBz5 zZvyu@#TQ@LP0O-&U`;B)O^g#yLbZ&Pu3JZS$Hd2eXP5i?EWNiKi1yODSi!qq911o8wI&_v0l?)Xu zQ`uL%Cn$FZ@Yij5yW%5{-;wnbUi$h1$|cmQ7b6)a(3<5*WQc56 zW#pY!Wff9p9G=d^f+3|TtF|#FN>f5?WVLG`tEI_wzdShIWKZHx?Lv1J&%6pl=a5u( zn_neiNm-aQ-5w?H`HLs3y=mz+cs6}Jx5No$Trhe5_0S~kZMILD_lLd~^Oah;%0t=k zt$PhCJG~T3-JR;ca%Ilre~A0iWY*DRI$zBE^mjJHzV6~hb#~6B$-0XSGLw{qc~N@r z3(s&2cFM@Tlj0t@G{G^vM^$1!i|QclHiC zs%)kt`d11$i&BsiTP4|e1Mw@XN2w&g43>?(-IB_n<$wI{O{@6zySqo3xI6rGhbK$6 z9#sr0wRW5Ul)FL%0!m-Hm`;@p(r`5dTa2Y#k4CB9Kfk@MJzDHR zkEb*Vsv45^HB~E>7MrRdRj&$PKd2>T&;4?@zEs)287MuL zKfFfCuMxrfObPb_&Lc1^P!ktG@fL>jgd;D!`m}*E#h~2|=d_or|-6QPOy5tX*@(=Y10?Sfgnv8nsGLYS?_F!-i zzN<-5P8ceWhELMcq%t*KKg>{~ggxbb!w1`6zrHi@m+(17O!fP}*j7#<^XRBqIR<{< znTXZIY8DOEa_}r6O+Q=l))Q-z7>S4ayk>t0O(*qr2Dx5mGaO`o@#})iuGbxHrMuqv z;vTwK&wMZsYN%J96z+TiE5GwSUM~9zxm32>C(~j0tCNvS%dlx#Bx4$3cnUG5`wD_hlw-T7AVVldHwoWum!DI8?)EO`|2HQf0 zJ?TuF@>A{pL1dnw5#{{Que-8<3}0*|I|r5;@rCXGNC<61Z#qAO7!Bd+!MLezcO{F( zfD``dUv5fQF>(|6$|Fjlwa|()e~m^>ggB5UgY69BpL}yuq8GjNGtpjt^F{_fU_|=G zd)i|ACa{f>W3V#wW0X=0fhaf2cE6Nt0uP#`SMk_is$i!_m;%|nm-b;`@IuDJT$>he z*qe3PFJEx}d~kWo8hiRTMj`P&}&omPRl6hyWpv z4Hj1?SgE06Oak%vEw|4;K*>N= zmhflloYGNN)rCHZrR!)SxA8>mwlxy*D?KeQr*e6 z*?#x?-L0TrN)ilgN&<_362s13W%X5Md*Y{5lP|`!2SenZ0^2r7E^ajYq272LvTeeq zWC0uR#S_Om_z~oGGp-gt5arcW$@__`lo-aRDu^LNAX^J5R+-y*NLjTM6D~GK?EVwV z7uN_G*fuuyc5DPfZUl^f^o>m^>=$`ksIVBrO?DdcBo|4B^ zkGUjAQNsK$a+uE{*7fA@funQuRUhrr#f#d(>Zj;(s- z@p9$_*6`pv`-m*R5MG4!gUNfRS8~#tuGv;A6Z#ZoJ@d@8`oti`!+AFgFf^WWDHHzk z4KK+@5knDlc=U{6zC&Z)jbUFpPa3upE{h7!>Ki^DroZF`2=$S!wOC90AzQ5BC{Q^4w{N4P5E zv-`gtFe#SJGDLpoM`NS+i^qmOotMVeJ^5rVRTYQ~rERJp`=d66)D0F_T|%~std=Hs zgi|G+^p}w0!R~p$1|V|?2BbmjkPKO3s43kUJT8~|C0wY)7-Een32E|QReBQt(c#n3 zhb&#|L7+-!z;@Mi52U|ndXq{U!YkM}ot4;>He4p@S<=AV-}4w%yGBex3U0OG2U9N5 zLqu*d{FDFiq0I0JD`lN|;Mr*lODl5pRW@{K*mdLJ|$bvySCo9pWz;h9Ujn@YxXgl}@Fi zezgDhw6bNEIuZ^C8ydZpo05h6ICnF8ks-VI$)kfOtn<7f zS}{CSdu$7GV&@b;i)Y7@j@$!*35C8>Aig&UWt$m!2!c<9?Z*!ztuXflCX3s!I!0J{Oma z37S|8odQi?+Fl`rGn-sOu`RG)hI;%|T(ib1z>Vzi#e&kdL-1>~Ucqm@y898{p6|SN z^8u$Vk5eTJb}4O8(p{0U7`n4ZLmK{K;SK9DtA*aLQB^NWcUm;~-`U=wNfP1KIg+tp zZ{BZXU${xoO9y8O**OnC(4E06;?cq`YM)9V!*E#`Iy<$m7@IT5$ZW1MU^0|EiS-z^ zLB=rF0@0&7nWW1YD*jYrXRE+=RUm+&kz!G*FmxtI1^jYmWV5)s20|XS%kDB2tV}2z z>v9sy47(^AS9>fT`}1=!4ewc1-6XQw4Uvb$sbrocJIaBTvM^~fORIT7j;1}SUtc5H zxy_ar9!8F*yuscPWec3NK`zB`^@rSkcxy7;(MBj~Pwi#ttNWEt8g&9oN75Y~vL}%j zo7^dnYh`n;?W8euF&>rulOx4C&E|3vlNWBRXFgiDmOB|Uich2N%<4EUwvmtJN!Kvfn)I3D|Vm#a{2o&Wqhn{xAr>%9{{ zUmUXw!*zwoF!Y}P-uC(|JIm_uRBWMga%?zUc>G$(hHb;P=$|?Y(elk#b^;)ec3;MV zMwL||u_wF=+E$~bYQ5Ro);pRbaf)UzK=~P|>B~RVff>>Rs zFALbt+A!aKbtCZX0ACQ1r9oe;lnfTz%p;^HQB~nuB>M+n+u8VO@-y|QdR9WHieK2- z4M;K5eD<)Y3TQA`jAb|-0yvk-G=y!Ecraii4;7ql0{k2#BuTQ0@|8Dt1`|wLzWTFG znULq%AMcKY9GlAQ#ScWosZz+WAi*z9Cj9*KyQuH&e{R8#hAIZ6Evymb5rk;TjfsQJfwc_dN|NU3=g6aN(`vP~T<*oM5xwMiQ+lE^q z?LrB(u@o*lt1$G~nMP5TDx_1NQyFfsweCGb@pM5!f~6#G@^^;Z+O!_M%R!pqIKip< z5ppSxcD*D34{R5kcr@ysu zt5ivn^n?lji8BnBv-`jN)18sbyGV2rn{e!HIy&?pBy7j0J@dXq5m05`2U{gu(w-q| z%2S$dBxU}>;8dB@e0}ZmE1mbU6PhJ|!Lp$;^V4rRX%yVYXBp&rB@(bk60rCW5HhX& zx#B$2ibYWFrpC7AZd!96qVO_lLSG5a^5#ai48aC$b|2aB$aZ(K|M53=e({H2-`Vy6 znKM7O@N>R!q09yYDU*GA!IR@Y=IFOYv_D5P{XWrC{Dt9h~* zAlnG`{i>8Kvz2|fvk%vlQj6Y}iT%~~O3DA}dz%tS>ulf*S`Z|13OO-U`d>s5V;IZ6 z_w$`!widr_Y9UICJ>bhLG7tk~`11m%*2UN26*7vl$f)MFwV{Q7dl0#Hq9hT;LpU>c z*oLYXBm3X}aQi0)Tc?|fhU#!U{wyxBM6tmE4^=x83*4GA?DPzAI8&UBcHe3F%EoJ4*i~bLVL02?{wQN)YJSNuEhl$9t{Z;SP9;-_`ZK zn4a+LY|^uw#6&+&wbXj*0Ft}N+3{bHujI15U%B!SxS7Cu_{aDB;fS5#l_`%GPH(u| zYYCq!cN9DPDwf6)bHP&UBuY&z8+&_#JrS>Dm+df5RxR)aRz*CdXne;FTl&}UZmQm} zJ)y5}FGy)|+DFmp8R8zo^QX({x@=cvcse8fBA@iB)nTV>P8Igq&}+%-<2K4jA}r(4 zA$U4@52MaQlxKKwY9q^d|HI86NZ~2vX4#IACBgG8o?x{fGq3BS1e|WlRm3d?KX1{p`ztZaf;Io+cXxa0Q(rrHXmQ3;0!>~N>y=V9 z$C(DW6+e2F+D9*LCwcR~4!S`}2-s;uWG}Y=^$%_WReD<`yw=ckvfqT`)(kbpNiSX! z^}?7%Uij%1aR}wh`wQ#r0ejLZoVrG%U#eb=?P6$UZI>pOOTUE4 zNW0Sol-^=Pdc9#qw>B)5s;1FmocG+g+lOyTjezJ zxQYM`)nW(>RF>p7r5^%ycx=e&`(D5D(#FF@h}<lBXBJ<1y#XuLLhHDeo+_(lgSC$c(x(< zRi=S0-isT5NYz2F9pNY&LU#5puvM~%zxl;rgekyTj85rj2sv2_Wd)0XD)$sK`S^zm z|HgoUogU|Rt4W!${qw^|dGd4xWz+5!!w@46PL=-IES*ExlF$8W{NHEEQXV|z0Z&7m zf!7FjMq%g9WlQP+#^ch0j$2cP?a649f(e8tcv$on)F+`*pSIUdrEI3!!T!_>S9?>^ zn>PA*yEYt7YjBUlv4iO4*CU<_bQU)E$uI$d@+%*2u){jyzM(u>kGQ4V|OCI%rV z%Pl!OLk}z-kH2&KVT%`+PqNnJm!``mW4>oDvWafPV;{hk&UL4B&YZw1o091s-)pcQ ze+z-@9?}+g_jfnHF(&72C@GJM^Iota!>-qlK@4YhI{9#x7vsNg3rpX7pj`AmOwyh5 z;LMLPJT%>WrP%gPli7^6oj^L=bDFZPDTbS}1pY#f0et`UO{u=vYrdMykSCq8z5UCF zG%}qV`%-fUN-5h_k+w)Z`FEa2yQhYGc1cVAY`&9iOaCh+RZb3NmvIUWlIt~uTmOIG z-*}jG5GwQ7*i`XvXOX`C^35Hr;Qs^^9B*E044}J%UiJ%MNQTmWA)9?vnjB)Al0yZX z%M4}bf=4Je4V7fG2V~GH{{E+Tempec5&Run@w?R?k^fn-*(}>5-vS$SN>0GA7@0k4 zWg1jV5#V8t#(rm`l^f!WlK_SeRGeSgKiHmy1CJ?Vknyj!*AV4ZIUpy=g-TwDGV;bi zTCHTkuSHI5dmwF*g3VEM_|8*K?KZM_nLE~Cv!((~83|QWszMq*?g=ra>Rv#b-5veu zPj1BBF!E?ol$MRXe|Jr#?b0NJWf!||^&N=9PD%`HmxYuGSBu|kYP!rGup!bma%%DS z0yLh}gry|KW-+~5(CRFGA?af9r-`9zr>psP$97BP_d*r?c?#_1vUBsgFz$CA!dnX1 zD9G=9TEzLsyP{;ssvq1Y@E0+plgTyl!FDQnxC%@E$A_nEsAO{RnBp8(hk-(VE$o`2 z@rtA$JIjvz7R;52P+#U6@ls5hR;4t5+ zd{-#glZKRhz3ci3<@5>s`|v!KS|@@{>#+ldaG$f4Y*f>Uol^kDRmv~Euqh`vb`ux_ zNPilxLaB0-U}w1d1gG?(2(VRZNSkuIpcGs^;Txl9Qt79ksLT^!C#MA4GO(GMf@imv z(&VVRLF@N^zA164^dQTV%kVQp$6OWSa%}Ma<9;1(h~mrKOTXEqVv6B=`)5=Ml{%3& zc5)H2!Z`!SpWykyFE$1PmSLF;hL;{-BY+KMAc;@-6|Pnmu`wRq+{-D-)IcJ!vSoNe zs@_^`#DC^sxA7$BzLUPt0PV?_0wHj!#!> z83&d9TIe09s^o4vEjUG9H)O*a;nV5TWC5fT4U0(|Pjd4dy1mRwY*)onkJ7L@*fz4-p~P zK8hf1F8t4f1X7dF6ia$kr$qH6utw%1XP%_Dc8`yNp*Wpgum(rv9yt%fZ!-+O+6|ec zy-kltIVzJNHC=ypM=Jv#0%fRA(8#Ym-JE|5;n9P5A}CkI-!pnEKl@%;t<=(bP5i^A zC4Is@iHXm<&dzyDe5t>gJZ#?MDFfpR2hV?58uNTzHs6G{AIZ#32-$?>_sjACqYlpU zeXIW|Rhg9#vZ-R^DP>YTh%xw*O_f`Ubh6>q5j2_4j;>q~*eZ)Y#ovf0Tg&ihwfH+F zkCz6%1mjO$c;n+QZ0GwF#R93sR#|?xC!rQcz0~IN%9n3Jh;5PY9ytIn;>N#UQ?0VT zchV|yI}7}7O|lFBGp3y~w3I++LzX3|sho!c8fLv<20&1%y1A z0mF--%G=bCGD3&j=oCT94nNa&nzC&C`h{^6!q` zU)?-QUUn{GlOa|oo2XI_J>UKL{uapneq><% z#|9o%RWg_i5vsCxXr+YQ%iLZCRU6o8h!lgW8ZiE$vX{boEolQrcVfS` ze`q~eU~-U-5?o6Q`2WGzc2!}S;P6l$potI`kRgp_keX~tW%}7)!f6GzOE(di;YnsN z5<3rpDa%ydZ_lttwfdtiQB^CY(p~d&4z|jpU$|17W&br!^pyhq9=QamDf++JUJr}2 zE2%DZBoAv!lZ`nRgQGC=X9m6uKpM6SsfQ9Z3|#UjMNmS#sfC-eOytR*1|el7#8r$e zm!P$b$|Xj&u0bIsN%C&{RXfGQkj_Nbn|RnoK65rTD^tlHD%U$tf{(e4u_D#d81hTz zN)&bb!Jo>SPUnKNhkahuZMgp)uUxq$pU{W6>yvNz9;(-H`FrL3MQI{^S$*m-=05?VjNy9{4^ACB$bz{UVn$@S*Z=_kY0et|`k8 z{zk2Q_e!xTRq^LP-5J6mmSK0lIIv?zLx+O#Q5{FO*AZmG7a!C&#-gmWfL1e zGcuSze(vTFbr)ZJVP~+2VrY@L4@pZoeWDj^`+-)HP<2U^?5ql%!=O)rmOuRZjgkdF z8jwMh`Rw-Z1yewYjLYua`s|N)|4m7LD6zoL=Gmkrk0P2>(z`u4#h?6m`!wtQu5_!g zUW<0FhLo!yZUm1GI>pe5E;eg{ZK#%^`kEuzWyKK)PM`C(Q{nuSKn4ee{$BSS^z#7Ovj1*w!HHLyq#-Qj09*emW$_oNdGT;$!Zf9y(Bgcwj zFo7#nWyxwnHDBf~ZRCMX*RqSohRaelH-hah+ZeGO09(6*)+K0~2uxSXPUpY+n@uTL zK~Piq4(4%ErZ{bZAdRGmMatZZp*oyjQxmNv1K5cyHGn7m*#5K{;#4Ym#Ff7QxCtyP zt4!5O%fN<_r)&P`xt*O98K|0p*twPRFaQ7X_EOL#N>i`$W6Yf4ub4x+AU$QP!aQUM z0=_@UF5g-+e91nKl5Lo9WiOV!GjwEGbD4*@KYSkAfX{eiNH~Fn`p0-cPM@-7EVjx# zVM{^s#r^$CJp%^I<^x{a zHi46|O?AJgj5=ZhD0y}}S0d!cP^pbsWV9j>Hr$<%)3%>4CZ7A%$N%@w>@o=ael`$~ z{*@Dfql81SfmtoKl?5^^`2GI*>nu6v_4(&_dV!vj%bomCbNn_yf!M6Ft74~owArRVYc^lwET(W&uwriSlHqs?KlJL@PFX$NyvK zkRnTH!kMMXs%&->sG6_e9G;w1^L;6iKnwq@O4Uv;1v}FK8-aUCN`4kXN5A$qFlFD>*6KP~4sAx#ehY8NS3Fr7ftJi*&Ry$(rsi zGM+0}ZYHq$A(fZ5UW)mZL@PNu{W0VoUNiUkvZSwJk4J}51X=yg{^w;_%AbF!vj*~H zwtnwt=}T2SI;|~DU_pLnTbW01{#-B6408gRSqk#xz^xf7(@rj;fu)3_=3E6(%R98$=TLN*buQq?jree@5*MJlb4p(QVhDNVLrLfRq) zrKyb2XXAhL+@{Gh>@3E%oABuU%5SHM^`I*K7Qb2Y7rf)ZxASvs+bEP(rmyTVJt|pj zgFPngQ}MG<=l>?QraSqyf<;-xREski3jB}?`(KOEYWD%g{)MMgDC=pK+K z?;hHfZErddBcHTV@JDqf^qi6NKx#e}7wiD{9?F?A8}0}+X4~)WznU{Wr+w!y>(&S| z{KNg7Tv>u_S7mSva55}(&Y9ogaH=eZ27@7Rx@!9H%LlZT|ge9$cz9RKC{oS@8UP-#<3(+)5#e0H7mu~e9yyX&+f|Y0vImKXjzO0 zFL)CCr@prP@~1^rEgFw(ZEc*wet}1)-$KU~kI>>aK#K)cJS4 zwErj20?tkEEmdLY$-$`l(RTh0rAiyfHcg`sGGrTL|Js+5JSzPFME`_fz4*#Y1E*}i zxe0og&0-I{wLjxcSss%nk2fHsoHbcvC7$#vFsoog$o92b>31MbuLz#<$BUK$c6x-W zx#EI<>B^3(Oa9{71n#e;wZEjI>Ofa>>DbR*SNWTPjS`so zmryH_$g`TR2i(sj1{JmA4r4Zo8!zB&BB0Kp|w&c$hDT1Xu#&&l) zt*U)jrY$5W9#@IYK_-^{@^JflRC;><|ffky=bMH&O&K&$tIP-SG4Z8r!V+7WlRuk zs@osr^(DJJBulpMtSsLMIa|)9kRHbtC6{;c>?AF|g-S1BZ(%LifBw^5tJ!S!y+<0o zFKN9vsI=ti4IXJcfg+n-$;5{j`SHjQGF-yj+sQ;0;Pyj21d^GJyfOAAx60BAWg>tK zEMNWE%|Z3)=XM%8#a8J~g$&O%*iREqVX=_mYp-rO;5fjfLgWmXDtzeO__#CPZ5JZQswtzk4xjBiqd49 z#75fLgk4Cd#ZdIaR+TKBp%aEts(P|GTe~6pPS4hYnpm(`VrA|HP!qp!fRh*7*rQa~ zy|m$~ShCGYg2F^!n&wquPgmmkLG0O6SsBu0c$UKLUkZeL?ax`GDI5(M3MNsfj$ui^ z=AEZ=>S)g(vrRJR7pf&@;p@hH*;Tt!o}i&#IhAMn$^27E|4Bj1(mcg2QRa(m)$+od zybdQ#u;xu|$WmteQ&8*@TdE>8*^0fJ589T$e0$?u`e;deiLSvj{wg1v3mIR(u(LF@ z+Syyp#4b^ZTdk3i-!S@^Df6k%c=F$%?WDx8&BiI;V{8ad(Bx(;w*eZzDBOk1US+BU zv}&?_Z4UcFsKnMfdL-f|FcKEz$vj&AtUW*ex$RS#WT0fB3G4?M@;L3k_)cxL;gep$ zEjj9)!1q7g+yo?pR3dM}64>eKsFg*iuiWz5Q~0@f5E&g|i%)GYWW&j*z%tSQ`De0+iVXA=PeK(v*$8oF1b{P*Aw)T_7%ti1 zTJi=EKT=@$n?EJU_Au<1p~IL^<=0Yh6d_s4|C{$VhLH!cN(Tzr4zvcR{r04RO=-hr zK_|ND_sn=+>4qW~KxttM?XH+Ymm1)>3-drQa{?KMUl{ zki>q)I**;!BS?StmG7Jd|FD`(WJp8M`|z7a!mPT73Ps~;<}ai_^@dn4^vGIknS-Fn zdbO`?XQ?mw8IlG4t*qO8Xu67FJGW{I3V6C-$T{?jj4HctB~Je0_CvXhmR++1u@ixq zwkG0wQ+s>^xbgs#Ky1H>-kA(CWc2V1Dg(VpRnz-@{rNzE=Q67d}9&99R`}!1>7{1?1 zv4khcGyqp!3f57_fHUOhCD|+%Y$$MRkzuIR^7(iWSQW&O%Hr7{GOLu`Z%9F)or@s# z+JIgukgb(PgBUxjT2Kn75_m9lWaSgN?@Gt>NXD+YQB~b}R*Xh%%OC z39_>bJQ(s*rPLbbdw8~DSV{ShM*rJYq6GJAtE5RdLkV}R1>n1Pg*3=A8%{^r0FDj& zQBry*Ch4&)!wZxioe<-d_~!%!7>S)9$KZd26socuqO=eT0Gkd#~R(1vVFe=gTSsT^_}w z0@+mM|Msg}*9zQYF`!qCKIp01nXN-4NX^jZGp;BNelPYUfA!ktT35DzQG!1!lak~{ zylmvIFQ8G$0^Km_A%t%Zg%U`WVGIXjeF;p8Mx_mtwkXv#%tOx3k5W67TfA2W4rTp8 zw*^J@z%?E-%wTAm2>2pbI2tu+ns8Fo*-wiuKLuU6@)Yx!pMf>}*gJ#uZw)8)=(NK( zh2h7cm=C73s=ActD6C5lr;GV8%+dyN_%M$j*eQyjf9gw+pA#zwCamPqRDGz!RCbou zr7>&3p3K@T+g?hn=pLpU@Mr6A3rxNYWvsAMAu9P&&djFI&ncZ#MxMs#djlGwCh`c* zFdb!$knN@vrv`9pE0NYOjh%n5m%;X700O-QcDhU?%O_+3_GV!Fvp%k8!H-8c8$Al1 z3y$Xbn3Qp8s5)?1WsU8mJK0*Wz5RJqnc%hyHkv4BznyZz@2ySgCK-M}%=;hS2>QL5 zf*tCr%G7KE_eZ`AA8BaZFI6Ff2Qv@9FG;UOIK&h2cyLnMP^l=oCLEi$IFEtk4$+eD zfdnF>wHvlk{`%dW2V{Ho|NH&l7t$(EE_f*QLhuc+UIiI03>T~**l!K+ zP9hMyNk)2X7n$(!peFgVNxZjxgTjdbLs*G`s+}qR?)J(`Q#4jC;Z_-GCu=geWU;4{ zD`<_YOHsm2-qDVNufM!Apr?`t=kQ{&f}0$b;_eZuvUUlYzSdS|64|9{LyHe5%d#|m z44n*d5<5%62|bUEo@C<5@1mXlmAWXL;V`QI<4E zW7i~~lP{LrbE!O6t~`A1WCF{lZ4Zb3s$TgzA6{&K5tarZKTE+LpT@vao_>;G(utj= z>9O|M$j^z6zk+~=lp#GFkNrbKi{AH-es>{+Eyne0A~&FO3ewsmWBB7X0+qvWAr2R^ zf3yF0_91R!R>f6qH`y=!rhZ8^`4At|JK~#bK6C~ReX*#Q{FARVSX>occ*S4NDdm&H(P(B?b#NyGi&s z#PBxBlBL+*IHkMPs7Z^~H2pM*w8cibPhz`NG4N>d6Mtzfwvp%G{%~jf+4jH5W%z+& zT6~$FP?ecYCNYIOr7DVF22T7gF>Gs_`|2BY=PYzzw= zf*&kXphl}IZ-W^^)gfld35xyW=XUl+d3d)bvDj3FsyfCj@Qt7Hu*m+G-`}(vS}_2_ zrI)#klEoejd-4^8p^b(ibOh0bkkfvF?Xpw_fo%EjZ69rhAJP^MbgTt7awJJsZ=xRE zOeBE}FT&ZN$ZQl@f}qG`n{>*yIKfkHr4*@5!?wUCGu0wU(r>$5vTA=34_9P~-n8VQ z^bxqk@bk;D1vhoFGH#4v3Ju1T_zvhR|WJ(ec6znm+>Phy^DWiOW+ z%#b@xKIGIMAN~l}kY{jacro1??th!C;o>)wJ(c{e|L(v0@1D6d|MUM~XFMGK^Z#&X znC>w@d9-VN{*7n;cmMg;bq6dBKz-+AVDA3gKtd(V9Rm1q8)ZMYU~I#oyrIY{^QXuC$L;?Z_fsosD0`)9uX z@=p2f*Pi+4WRhACSdz&cP%JaYaPd2@X ze}DS|)G`0R{m*v22npyy%m3o9%zuRopZ@ju0n*<8aC@l)nVE)B-H|wIk z^Y5zTe`nVuJ^NHqT4n7uCH!yy>y7i(-);nS3UsXlMn|;-LL2|T_>Z3XzieO5YW>0g zpS}O=+Uz>-JHZ=;D#Stog-`$rATl!;Axf4kt3{Et+�_*xfDmS~IKP%v#g-7m)fR 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
          g{ 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

          -b>p9TC`sIFHCkJ76-GU++4qqNU3G|Rgr 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

          yFrEqhx+Z7hbpL1^#Y8m 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�dL7k8gzhOj znq{8Lj~Vp*dOvEL&Y>$V<1#Me4GMjxaO19LADp>#?Zjzw=1j7eyECxO|58v@H>|qM`BVmrhmx}LSl=i*c*ss6L)~*lYCD_S&-z$ z-If%g2%k*AGMQ8M$LDo^@sC**N#=J{WSn?(B@9#RJP)yH-E#wrM9D)rK_c0(OrX~! zm3ROQ$`LXWnA>R&^xd`0W_g(EpUsCYOX+O2@DRIii#=e0U*w5l+Y!5LMw73em8YX@ z@JV>AUEVti3{!{=4_hmssWQJrUe4T*&+`zE5a#TOmiCbBAy2Jc%_UzwlD+0>6=KNi zYLWT!s=*OkehO=B)*JqcgFk)B5-^;?L|0mJL@-Kz0EAt`_BP5a>Ig`SJxm%%TkP=p zWn(t9;7Cgx;(EnjDoOx21@xHI(K2^OEUIls^A}f0a>%kxvZ!$r^ zd)A-j!^TIu2)+8XFR98j&=u7-%ohCJJ~bMn7WC5ad=WKAuCFF>dV*s~!9hZcz$ zfT@jBv|s`I1R%cvzTRn7iBQ2fS`;U>=BJzhCp9Ztf2_$q9AZ)M3z;X*866^>txzV7xndDq!ZHSDDPu^w9F z<7^gEb*3(U7zLY!pRvG?b4;sO+mGgJ1TxNGCl@t>NohfxM*o9OG<$ykAz4ttSN!JF zhBIz5cUD5NrAAVYj4ak8^yMm{rTkqLAb!*`)@a;}D)AXl=~&TG1ku~7LPeH2E}*UH z^z)vwMlC99W*LVfTOL4pa-t|8306+e1sLz(un`*^>}v>CsH(DQ!(YXX3pE5nHKrLR_{23rFM!3#M2M7BE77jgceu;z3*BvukA-^!GAgM8_1G-{5lZftej<78O05 z2<(t~r(u(Z1GQxl*`7zGi?L$r@kYJy)sCr$L_g50jU|6~SosZE2z>fBbAeO(u%VQn zPTF8%nNCLWpRCRsf|j}nkZ}LwE9T3>BmbT@@usU6J#_y!``<`kB%GaPzmsK?@=_gW zYs>z{zi8jEVO>j0wIrwv&ck2(wd~OOe^V2Op){Z6Qhh$ioU z&f4)k7fDxKMbjsA%v#<$VFEJ|n@wIm8!|T? z^Vfj`ahM9V7{tc>$RHxV<~~i1Vp0@f4i4Gs!~DtiM?STO{Y|4#Oo)h~i~RDgd{%|i z*=jFWA|s_J4(WmJ4FTxN9Wh`;nZt*#vI$o-Dkf&TJVQX3z_ceGWUzob^FAtUsHh4f zC~E4r)T^9ol2lk0)SEqjAX_`fo;Jc7le-#=Jd)5%?l*c43G-W^!?eX8 zS6tS`Q}*D!P$`Ek5msu1)~db>J)vJy_r;N&k?& zjsu#rmkdxDJ}P!N4(E3f>Luk6>aO&=fnpLce_Dzr2JL~o$P{}4G4+W$fhss`FC4V* zUkBi;;p{=EhkS4ZWt(BMP%CR9EFS-9ka8<$Rxg2rC+$ow=oX;bSa|y0R{JdrGR`If z<5*UE0kIVEkc&laRfd+FBw5HfIYPgsf^&z4{STF7y@h|4-sRWikd0}s%%~?-%p3-m>~c~ z3BZ9cvtFyc0-%;X#<9%I2VcxEa-#2az>?8?0P~TZ*}jwZu5?tWWfGm#mdA!V0v22O zLKXrFPc&6b&!`{(M_LLPYd|gn2;8zL`{O6fw5ESe{20_^&-^V#XawXPYqD%H`${F7 z+RoIA4BLn&s&M|$*Y7^zFZk4g5tg;Z;Q-hnBcZ4;nAKkEwpI`xMZE|N75S2+5M4^` zUB1MFQ;g81q#gTDdZ)QNa^>M+OS}*xy~6@I*$1!m+uXccaEF-Xrd{TOzM>zLVVea{ z~C>9xeZ%P$bopK|-SIj4p#S1deV%!#hTfwJe;{(!cfDM}~&p5DN-4=3i&o;=EJ` z+S;<;`mLtB@4kNGME|bpDA=(h`>CJGUVb?*z7AlXo1JCHef4L281gl&7GJ!J)A_B|-cXC}ubu=_JH#W64x3;%T=wx7RXly=nYx3<(X4C1dv8koe z_SLp#s#@AQrZly6G&G>s;#+HDORMdh+t@d;wX`MMgyvQwN)3&}gVok;OF{$xfqW*o zj2{o2>^||_kG{8i=Z@Be>su#uF6$~WKQ5kP_q=c-+`8DOV;?Cs{V~m{XiF!aIj|{P zP+r8`r!g_lwGL-p9mXNkU>-CJ^KFlM2s1Ov4q!js@0i9NdrgrLS_wiDv)EJ^Y{~(! z0+S-OnwoLPU;A2P!i%iky2vh+=8EOzHue+=1`9~C&Fruz50oS@JEP0YE*FEtCZKF> z&1}2sjILT?KO0{IV%{fa?2|LVR{(gy4x8GDI-GTL{a&YbBTys@GHDk;B?$N!6k(a( zDSO|-(6Cvcf6VDA`}2)j?9*iUFwLFkme9mM$FDTuH!?1nk{1y6ArQkO7KjQOg8jiB z6Hw}_p5dUw0YBQ6CsA;a)X`D8rFPM84Gq>abX^=HK`o7tO?JN5l;n2G`2p%L$m@{L*(n>{eE1qdiq8G)uV_nt64 z8|$x+@T)B(1p=_+b*8&OaHL37>-?YMyuNbRr(sp8&lyH`22rv9mvtmLE!`vp5dOp0q}Wj+3mm(&O| z=y4zK0%-t()M2vMmsfOI0Ka^LbvQPW(OwqV>!PdZ*aHRdcQ4qonl(`YDFRs& zKE6J03qTks9S4Y!u0p`n~}mHU};snj`rK3kb#RSk`eb5`FpW%d$Z zS8D4&X}|LkY1uB%-Td0J-D~0ZTlh2%%bB){ zqf~U#1R99H=ji?&&pa?PJlxP=#Zsw}zbUC|XlS&ebA|`|cRc&OzV4&OZfGhsHXhrv zWuULeOj)EkhKC0GPmTZMpg)F~l+B%VUPm-GwJf;iZMXc>-TgAkxnU12?fFvMhT4X;wVn!qhsFfWcQy^6Y(nUcz|MDw$1PKZ!0>P$p|pVPU|iibqa(X^wlS%dWOxJ-h9H?= zxxrrVB{7OWe_i&cPufb~0biVgBmP?!+Gm{sVGu1{O^2%rkb-hJFrvr-uBjCVLcJ7K zAZpcKab#_9IQ#Avi}RL+wrteVVlNd{R)!1+Q-HCPUZPfn6&@4D z-If}PstSRsk}0!NpsEU}Az2_|Fa@Eas!AiEP!&b+(xGgn-~SwZPxbTKFcrF@fh@%I z2Cbi5@HHh!F;u|5ZhK>bXi1_Ks}lOrlE*OV!oem;XAT);hW_R&xv<1U3z7l<<7r#g zfsOz)(JDMak`W1zOlPaz$rTMzB=98(3Q+|U$@i?X7dMIzb{@;#w#1e~l;^?Jq>d!& za;hf4gp*o}s1yL47Z2KKnlf!u`_zM@4S4<O{TRup#@s-&?YL`D*_9%~{p(na^bZ{lDk*>krQ89lrE) zF3W{oS$1ufotgAqXwZDWvJluuauRh%w5(##UFGpgEJiAhQ=|x816!0WO&Fv zUg^IU;a;Tx$*j~$EuvY%y9ut+=6D(fTq-#7D45@74o+D;$NDRuCxksq9%07 z+`iO45{H=PQy{)v6m*qn1rWnEX+M$39kXyeV<98_r7$#X>VQzwoJ5v2>1z)Y0=|}_ z6p7|^s>q;pbxL+XBr0Sk(61f0S97WrAbLhQlA!cS-q{Bzf)8GKI+K(g1r|U|ocCQ} zm-nz>_x1Sq0aF!OOPK&#fFmQ43;_X-9Uxy#lLZBUupq@k6JUmYXdPmCUo_c1IwW*WGy9XfEkFvRS|}IsHB_%AJixo=+Wd; zerN?Ds90duMk-uGakQ#>e1*(mzacK=)Q_>o)<0DJ4ylWdG}=Ps08$|kW+2hpFPxHnck5|+fhzFBUD=HGtfk4mcoBifH3p}Tk~7PKxi$PoV7Gqt@y}K zXtGX-rkg zuYyEN-UY<~lou+&QVg2%Hy^Tmdfue$``hw%v7g+U{pBX>mM?lpQh9Kj9Zg`3EiD z>OA49G zrcuBY2Io%yBV2S$YC>f(Xh4z4F*TJ6Fu%WeFrP{JWA6q3?41$@siet+#L$f?9>|nz ziqBqRa;GG_cX)O!=e9oFnRYK`oO()wfVpY6Y`u zEpy%qzgwFzQRn@4x7w{Jl_=P$YE3X}$#@jBP_Q!^?t3-6BUvO8!rE&mvR?ntY*iqp zg*;ANCLYE?ILytfXXLdg#>lQwQhX3&arjDyVCY5?=n?BHIB|<_$OkRUV$Pgs_fk=$ z>d~F~y)zWUd-!5@k7cX?VnESKQ3BdGH(Dx2ZHiP9$QsN-(`2M?&?ZiS2Evgwg(u;F z86iJYF5(8+gR~(4ri{n^;aCQUi2#*e!jy^;Dk{pc(E05fKVCd1W{QLj$e|=_X;F5dNgx^h; z{X5^+i?+7xcYbHW2R?99TU)KC@bHW!*DSjBcK+aohSS*8 zGIQB=O)YIydulY&)H1<}Wtfo^Uh*4^F7EMqqk{2D16N((2tXy*%kCy(hF#^J$%p@DHP%U(2eX0zX({mVt! zO{0wo&u3q~-^S8Q4(*d?to@0P-}@{7vaQ3WIvr?Dh*1=D8nD-7sw!H`&kLp|M?h`N zhB4ETggl%)z%70XY2i1Z$z4trkG#k1j0%WLN->Y8k$|rD?9ZPy0!SB-FpTC7$U+DU z;*fVKp^Z5mz*jOuQB_ftDVPv(ThQOUU{~VBS0)30=dtX6xjr|pOhzkN(9{abbL3P| zZR$t{Art`6+K~8I01+w(3(|!a5ghH;&&wZVlTCmTs-lSE-@jzHn`zZ4nlBlOpnov` z(7E~tD4t4CtHr6*K6FGFeEk*1Uj+Q28h@}zQjq|0QjgHlnfx7k@T9$KbyC}D%Myl8 zjffL;fPC@`cN>9I+2=<=TrvXWOQ^H}r(h7kRFLe6N*s`e0Vwjjz!yYnXEd82+CCa; zFTqr`ES#31;pIsfj`;E}V(ujSmX}a4SSW%@&bV>jjZ6JJMtHt3zK~vzhFn5-V4ZvO z&T8THJ{4p?^-@nrprADxLZ)y}Q$W^0ZG$z$Dys?tLnHYu(@_fwV$7HNng%GSQ*3Uu z+EhIq^$PNpc>aJr1RlBttsoAZv|FQt8IzNW;tTfV{8l5VO|MQMrb$AvNsB#rU|aSJ z*Jpq6YNFn1=0o#w*DJ?PWQHS`GC+yHj1}|zR#2-Cw&maIK zbq#aXbi3UQfpEmE&#{9;rWk24Kp;(&fdmw}S_Ib3%|8F2@e#glvAt#$MP1ZVCLLzw zQ8sT`l>K14(FokX#ezg^u)p{I|MJhC`PP5)dqXZAOvti->tC;{7j13Xr#{vA{`cQF zd2;eosQ|OtyWjn~zPSCY=+Cm}{6V~P!Aj0k^5?vCaloJYPx{?h;{$ocUuD^!ox@ie z>xMnVW`Ao$D$~|EwXvxM?wr*(wN0GbweX7hYi>hkWMsIrYhK5+*`uumT*d{4M!7K| zRI2K7hzI*l>G8uA)0>{&{95;+*BTlcXLdF||J*bCUwx|7*tC1oL%qij^>!cW?Y65K zrBcT5IDF~dz15I4UY))F=Ij&AH9u53oDKfZ?4KU7H_%;j;3%yB&~N_EXa4Q(9$N+0 zR3(UJpU!9iTDWzQ-R+_zAw^7l5K@fOfKlLAZpc;icRmP5K;FfX`kc<}eXH~N9ei)A zzfqx_41_Nw70BOnwY`u#)IYI4dvLp5CXB2@EfPU(#IITYzdoM*(mK15XM$h(Q)&U^ zT^@soJV$_u5#(hGP%9ZCQFj5gC4{v~A|{;AJ!o&dzI%DTBBPk$%ap{dmwYfyy@NC! z(b{7njaEf$LI{!!f?AR;QZE3l8}@M>HibNxaxto4Dsq{M)E6u!StXVAJ_HH?Nr<%5 zK$rzPj^*#WNWEi(0BrdtkxVU1c>ZL&7^z~opeRWQI#OpHP<9HUHVjh5k-{Sa>_Hl# zf`O9;5GXJyMjDJt8kmrVS0E~(3;EPaCiWl*(ID&uk)o2xA74$L=2Vpj1-^>vrIb^^ zXx3_=P8Eq&3Xj8MRh)~R+Pamsrv~y-sDSg;mJ5LKe1-7bK?ha7YX00`Wq(!+wFRn1 zH5zI(fqm4T)gS`#4G0mHmbdulJcdW?Q?-I#`z^5tx1D~j5RNv{$jEGLQ3V}@nBYEt zwGc&dj`Z3$BjQ^aLfjgb%bc>;81)aHamJ^gV+ch7c^V=Ym_-YM@m*pJOt`a=+N~j= zPqwAdvXmslxQL-)yCx>=>-OP$n#n)A-~8HFX1uZbfCyFCCzwb4-HrNShitM@1t_Yy zD5`-FTaVfWoSuHW9~ep5WRF>!?aGmhqhl9e1A(({uDP~FnUbwCAi!9RFwyEcYPuae zQ`_?(scLStF4cPnBm*Hy%&LE8PP99;ALn@acWnrREDBv{71bgL8l}UEBDuRw2Fr~P zbLa8wEeq_PCdsH3h#hh}NK#aC*4dg}<1cI>NSNajIFS);_{g&Y2`nd-dt%t7qA#N!l9iBhb`ZOXs>H<&OS^ z9qVfU1|kGiB%{+i?1g1_xd1>0f<{ehGa46@TJ5#&G6nxrZ?%vf^G{`yp|bt5XUy0X z@~(DkTm55>Gz1O|rfQB6iU1DwfBM*7e+J-^Kx3BuB7dIbrTTE&ZP_pV($zC& zobi&N0!IAB?GHbk7cUZMo$jA1pAh50A-@M|#zkGjYVwBkGUDcpv4I}AGRx*<*%N+4 z*14k8(9qm6A-PHQinEKTa1&Pg)2G;#LKrE8bmaQEWt-?{SE4_ASPfUY)(aF+0mkWJ_5?dp2!zc7I;Jq%bnF z_mz!1dnUBZzv0G(O)={+wXp&ADgp>5G9?(CA*683?0`uY0rpIA%xeHKIP5`7qrJBj zpu*())faPv{K&T*wW$y-IhBBD`DL3m(Ps9mW?5cAZlFiu5PM*Yz3CYm3rx_5d+ocx znELw{%?`n^#|0DjwR6l4g>U<3i)ERqnY3q$(*j1=?88bW%jmF7Zt%GWO&(!oN(ft; z>K{Iauncyp-M{mZtId}z82oF1?A1}w7$E~BJklg|=_NM!#EBTjKYg3&lDhCGD`N1K z&nQ$J5j=jCz1W5$x3A1cHX&|Zl>Kn0MUEpB*%Kkchyh7{e7!}BJhcRdP5jbP{QZI# zWwuOgFCNTp^siRD;@J_BfEYv&l8!Y+bXQKde$eweGK(+NuuK?gI80m+fJIeK*8zM^ zDhXf;=Ja+uG+@|lSaz)w0|6i!d8+O#C+7r)7doW7-?BJlsAaF0BO`+V!C(PJNx}sD zee~i1lLXX4C_EzJZxh`JMoUov@U721Ip?S;hqu$fvpy8*I>`V`sgsEWF=#flP-lZ9 zcam3U3Zh|Bt17mFObP{014A;H;)J4V*;|KrNHmiGKT$gcP_<`LW>p#j)Y+xd8qH=3GwV+j@+*4lqWosd5QfQokfPE~a7J?h zWXF8fY=T}rCOfIMp!M9i{os>}4}nS*4DRnXA_9O;#Zri02sKoM=n!Kjx4$C5yaHBD z&#&GvOOZ<<_Q*~PBi2~}GkOZS0O_?O9EmnZNiZU)?P#{U0VSTEfDfy5PZ?U9=LFdz33r|2q38dM97ZJ3SkzI zj6S?0zk?}+$$MhNPvb;@T8c0+D6{OPsQ3X)tdX>(S43qeRTPsrMc@>9apWlMRGoe} z%AfpZo`0uihS@zHU{HhllHe~&N>@LvZ4Ps`?*W=CM>|u`91&q zKiu(wk8L~NtoaM1vz4B?iwP^03539vGd(9v?!6KrYXGrp=h(F$F++0AEW1w}I_6nT zyI}-KCBDu?Ei$hh%wI7n;mB&lDF8lyR53w^489aR#e*m9nptE80H(}_OhCqU3l%^` zH|fN@4>Zw3%N;Z_|M!pVFH#j%r6eIhNVG!v$rF2QMFz-`#E>UTR*=(honm;D57iYo(I=IV$Mir13PvPJA`*d}6?#=j7?Fhl{DKPs zm8$QUt_oSGYNskfG9|#6jJzI*XR6+a7{eV{^m5gexnS0_ZpY8Bs@Q18&kO1?e!c=o z?F3!ms1t$-pm!h7KRapy<+bNj2J{W(_ge-EE>ih|ZXi~*nXtoJ2y-Sw=W+U8Yt`Wh zM6tk6p==<06%h7M_8Wm%K~fy5nD5zRO8 z!*+7O+@a2}t1B3wXCS+IVQyS|0RqKoerFssDjVh-K`KD6!Uyb$oDD@?7y(GyvQb$e z93cj+i!-{>1|UW)8I~c zzOeSt_N=|d7T_4g$YZcWdXDR(emL0cGS{l<*`GaaQ#k#JYjadGG0YO-#FqRr6dp1A zM~_(*GZXf$6&^IM@44Di84OyY=C_g|!yaIkX-xguJz%L7@jq6+`3{@1L-1+>dr~We zIXbKTqldD?o>bWGA@>{wvw(mRz$q5s5IjxObw%|ge7eZyTEFT=?xJR5ZP#(T4BOpr zPf)5WXs>S9f>0y@w6K68ZGlGK4qvZ{VO*Vq{kYqUgzw&~&4QhBo2^z(f%fnbBw!4} z!QdTBvmfj*nlps{u{ixAs$?ia;M82V`K3;8`{q+!AAHBP4?Xm~-s2bjh3;2m+5hHu z_tcAd^RnOn{b^TRaZN+RSzg@!+0SPEm&(h&IBuQ(VL>{4+|SVU`J;%F{pIc#3-+V* z7wsR+8Dqvwe!(1{{MBG*zAkFQq^{2C^CxspgIKceuH`qqcixq^%)k1!>)-d!*S-IL zU48pUSKR#m6>oXp@|)kc`1-qNEV-s@;T5Z{xUw{TbzA4O=C=0cwhp$-Z$^L9fZ?kW z1ttSpRWVSbsuGfvO3B_4-_+RD%y~F_3B!W}ug515=VaIXWcK+L*&PjbFK4|d*(dB8x7n8yE=3Ft z_HTRc;cVK<8`i9}Tif`<)h8xR@tQY>hi%$onr3v+ddMn3CyPLJ%7N8qydT_d4%UrvS<2~ zA|h$ix>u=7V|B5o{`(S2o;iIK6OD|Z$rnVas-_fOOjtpvzJ)DLXr2AG9I znsn6+kI;Kk84-@CMO`hTh)41_J5q#450`JBXM>4B9YiEGxFZ9T&iu-AW|Jy%#b_5B z){wLod3HoWhY4Y95-lpCrKnAZ7$B(xk9Z`bP}!g-w^H=L2Il2%m7tDwJCkxQ6q*F ziUHIJxKI@wh82dn6t$gJ*2W{pm8fkBNT6~bwV=Q(pdc?=ilDMesMLynkS-huCJ#*y z!4V3kgsK8I`cKW88nZ!v8-@&mFfP9orOrO(2olDQY9R_pO;s8cpn+;(Za-?56jHN- zRAeE{0!E}eJ#vat`BHMLmw-G#MHkbk#Rx?qtVu=1)C+v};k_!z)tGd@oxH(R2wxC@ z02o7c!Si@;_Mxk+wtv_@SqK!T+%YZb=~Q`?1;#J35IAIM$b@Cdpe~N06%zu3R1q6F zmS`YJ5KaM#QkQ)qJlK<`&N7(N00KaZqwjDBQ>FkMynmv6YaSE%w>H~rDej|hy;nml zOp(6O15fSAuA6Jc@Cyj_i{*==X;&j-D|ZlwMz%k7-+%e-|K<1r&#W&FX84QSYdr>7 zfL`4G{`cQBVM49h?4rK7y*7HY?5T5moRFWvRarLQf7TZZ{K)qCBGkBVC_CIf<&US$ zADpv#!;HmOlXY}74R;TeW-Pg8V%HqI2vEveJEkVQk z@fij+6ICK)YGditkyrNjy=~FR*PhrrG&pd0*NZ0(>^yN`*X~WdV=C25rE(L@{~xlAr$VKkZz0>pNGJ3R!_>vED)Z zp05r^;h<2|j2X2+w}D7Z0stBNwv6A`4?;LaSPm^svYL@KlNJ(#umcfyU?DFtP1b;r zmUo?21PVhFI{Qd1M5sjpiG^k9f(4W9#j@~IUjScArsOJu8Um2ieQg2hMhr+o1DLZW z`r=(VNEw{fVHZs$Dc2~fR!nI45b)WPAc+ncd6X_tV3GmBgd+kV4?mb76$W8nd-xJa zi>YNvMlpprVLueZ1+~%z5++YmY8|HF1CrRZ`4I5s`hNdJqi&m$HENekEzWOJk=Yad z_Rdp~Hbjmkgum>p-kc*J4M5$hO$P-jY70W?lV_eYHhDwAB{y^aqze#y)}_n_HC-2^yNemRaJRo~id_mp)s+l53jPP_Z5{Avkx z4q{i&%w|l?Qv|9=8LhqE3T*&kEc6Z80fj)FU%_L$ec`Em2eksGhspMy^jB5^$)NJK z#kr(h!?8~Dd%kc-ci|Bxoq5-?tgC!!4kJXkXo@B1n39WmlkG=6r_-KjAOk>#rePTZ zBkE`Nc)=R(lxlMVATzDK#NQ9Ylb0UcbYD-8KQM5iL5Kb;qRUJ5;o~1~xaXdAot@>c zPgeuKj*opT+p{MxUSepka<;?S?{jZ)dn)(7uc}E?&Qbh}qFB z2cIzzRE;4xi>KI)8&zePQyzb<)X><}+}zyLICa9Lu4(gE&RMm7;o5gxar;MC-tvLv zH@$E5+kdKa`n=8=3kLg6wzPK+_MIB3@5S(?th6|L%X_jfEX+1Q=iD)(F>5}S?K+sf zl9w+%^mQNIy6H!2Z@g{Uf|*;6l(Y<_+(0wn4$n^CyV~C8NRfGvB8LNlxr}~kZm>&Y zFxgP#U;!O0WKhw%kM8vl-{y3mc{L{jOd*3=CR_sG#IX6%YuPjHLLh)C=3=VwtC^H4 z2tYc)q>KgC@=wO`LP#_Rvv6KMnBO}_6}m=v_Oh}o+jYVc0A&KI;**jk< ziVZ$C*?^2wMMI$ZQ-TN;p3Z90aypi(C4P?>o2rN$t?z8f8=_-PO9@huL!9&n3$F7& zPvA&BgfSl0$QX&L!=HgLJ4r~=1rfG77DZ@4i!g<-8Ym`!BNwU?C#{8MLq#W{XbEUv zkXLQWogJp)Ik05oJZ7lI@XZR!y|-eMGdXAs=cMk%|STTPpBRg zmbB}PHpFIjjH(i0L?@)@@n}eUtk8QYO;!Bu-zK@C{@?r6s1KYzi8= z`UZ18n?jYA-VI6d``b^?BJee9uIjS8V+5E?w9=9cd(Bv=%Qc)QUbD3@YH1+y@E1?b zJ5~bl71B31TRUkN9iq`tMl_c|-@erBJh06U4Z*N6`w|$ESim(LsUrr=f^!g)1vL{% zicGY$Q~^s@nS{Ca`^J&P>{NeOCGr&UrRkYd1zPI2Ey{m`-`i&IM9-7O-t0zZDSt2c z?h|%hu7B93%$pXN_4DnSN7M)Hjt%P*%ZmJUr&i`TdU^*3zjHf{17E2pl2qXf zLS5I?m?x>S^X?TEUPJlysL$-p_~YX{p7W6$ph#?L%f01}Wlvs674o>)e$-^JOwYry zh};@Qm>REV%4iHKl9T?&lup>%G7i>KQwJKFe;)OZDNVo3h{djjLwN zm{sG|S0k6ii`#31U(^hL3h$5~{PE!^XSdrK!_$}N8~iwD`rTyXc~EnBaOBlT4^HT8 znYCiFe=~3l;2ge;HzF8$;@H`s-v4t~tX;qK%IlB!4o{pqyZ_XQp@HhZnhDvI zcVwTsIr~JbJ#>6dXv$h&%D$NwE=im^cA&j!c-obB&YaXt|1m>plG1Di=y{S!I+;yJ zK^tH{f3oFED$@H*&Ctrkn3S1nEln!VlBgq~!xtRD9vU&GesTl$AQCMiL513z=G%0P zai&*%k>)8#IHV0I${n@x2%sVQhqB{+MlhALkLg>pGcwQZGv*gJS}k_S4X~g@g4igs zr#W;BaZdGzp%9RL03SODlNVWJ-ZLLBzxO~vvZu&~H7qQgY?s!oY5oJ;-)%kFJ80`u zGH5xE=ONH?N7Axril-ChA1u1Gm(&HP$RlQ2eh1w1{*rP8B>%_`YZp-|(}qLUZA&ac zQjaw^*v;k;_OtR@Bx$t{5L`HNq2q#a@=rT(o=JM6PYWYPTaZ*Oo3tn@!mtlyQx>2l zOcNmP^aB)TIY#)!2wg2ns>s+Uw;EFx)JEv8cH1KckOv5(PEU#nfiS)(KDyJcYpO~c zRmigk3QtcYd}*UugO7|3s|fZ0v6dz~Omr+Vss$kv8{kmSnwVWd2H`*eM?kgO>+cs# z>3#Vy;XuUK!+<135Ob;?P{0C2$l#PJwzYG-?o33xM9-X<%Zq6#cg4LCRH?Uhm_Vu$ z2+QoXi4KSkF-++e5OFFcg_I6ds+=P>A3CG|>qHs=r$>#aBWol2-BC% z)koXvamp`Kqsh3T?yJ#>Q7Zvx28$Gx79v6%g_RQZ8y0JkS)*L1Y?37^lpR93V0k%whzOC;IY3);(aae}Y5t zBUhQnuWYi5R5Sfb;EFC=-@za;ykm)_V)PObVxK*h)j2zk*=rX$!N?4r@N{I@8)9wo zw?QGJsBrX`n~V?j)LvhfZ80X0Ji0Xh)PRNhOE=_gH~6a=NC>W+dw40<<>>k7VO*R2igvS7Yi+Z zL)nQe8_!LABO}?i#}5qj4llT7TEm$y4xKm22A|7#lR*PifFWy4K%GI1F|A-nAnWL> zxE)ZE8v4coDW>j3R}COVcq}zEw02BhG<)jorE3^s@e*fcz3aq|OMncel1+2?0xe#N6M)XH`zWpkg;{$XHTzs(DVJv+BFOkcHZ#j3~m zH0a<0oA11b=|iSDOnSR>)zj^3%Sv6S0v5TJoX8(MY(=Qp;NSuo z5<=_~A&w~a`$MjSz9&=t>WiMql+5+9J`*Dh%p$C`O^Sd7LzdZ=`3trXv(Fm(RZ;*4 zf|!XA8aq_6hh}*H#w+>44XsmphBZ#;J>6z);97<92fw~#(z-+Iz-)#vb`LL~aQ zB&j2{Sa|J(5meC$3YGAz6TD}%dKGI}Gywr+*B2EF7?{7EZ@E*n?=!*0k_i#cC zPy`_?%W@$jbVUK2z`78IK-8ixBPj~S6loZ8t)jBds*0&f3h~pbkQ|L*aEQAoM#DH( zuL>CV>J>oLL4v~z+yrhY3=xU?2J_(D|~?F*6x6lzrr4n=;z zDyEX63%UXv%?(jYm&Ujd@&iNpi?{c7+h|p32Vq>t$hFHCxqwW`ng;IFGW&F}gC>~U z7THH|B@<#I0%>ef%Lt__8@R@A3``K33`EBmccTCFQkpseLY6~VMyP6Ow0RUhGbmG$ z%{_i5MTbjLd#PBK;HzmdBgWJY5I`7^MqE8BHwZ`JsR=m3lxBB07G(L3sLNT=YlF|XXY%tlh|;2q9^ z9(!nxrXhLH)%gUr{ixmgB%2h8fhr<|LoNjqc>qB$@R^U7dm9meGveKZ4Dl!@AhW^9 zhpx1xD{H1(V1KwL&xg>>*93ie2w^=5h-F&$-=54qc$F;%(E|+)_O#%nw(Q@1--ZLM z5diH%h3(3j<{AsSs;QnN>w3o$(}h-&q4wfVGYX3Mir`tGgd<+ z0HA5L(ol$q2Uy>pvb8_#%$<~V4>X+Wy0*EgY5yx5`};4_Az5A3k{FX|$oiB^PY( zr0X=JFx;M$bVisWKWQhlF7R=A_$T$1rHya(D2CzzVV8! zVvwhyLXq@rRhNmOV()pFVCzaqN&rQwC?53G$TAau3@}d>=tHd^`P8qdluE%TS{cz| zka7~BOan)AJ3VRfm6}FhM5CUb%1K2ghOJQK=(6j7I>iZp02UK0xDd7Nog@?a&-Z=rOu#aL06Qr7;T2+Bm9>i_jDHBjENY;RY4^-7ESgVvl zKw%dUl2r+`(&s4aL>f%}&3prap5%vCC#o!9UEEHo>Uta@^_LfD0`Zx@dJyhL^ILyB zqe-SeQDMl}Yf_}OqSY0mqc-|S1ZE-P=TwlF%=V*skzq(g0jaQxqC!Mp_%ekon0$Jv z>!=T5$S~c~{~8u$A81HL0BdlVX4uQ%R-cxDVvCTNi6x98fCCZJ6%c_8!2ktDk(l92 z9>5f>Jzi|$_f$g@Elg8%IuGx#-S^beWE_IjEKXqiPulAk7#H}P5fx@!sxeC~lB8J} zN0-ciY509tm@X6NQeSBT6rbH^|04w?IkUFRwLN^Cst7PLd!qgQfJf^bzsyJ*i54uH zVrx}6g+pM}krb}zvH*Gp?dl>hzH(N-y-Nh3r8uJDSSA6b3AZmXrga@v{xKcSvbN^z zfvx6LU57eM1V};9B0MeILA-p>TjjMBfszabhB_+vnmftt=+bLzqkWDLVK{7Rn)Sb* zki-x)kPHn(5t}e|fL=Oij!0x___4m6xoEP{c%eB+#$QL+B)OC+YAxw0zk0)3?Hz(s z2yaT>BK@D(*naCaJ7*y9h>@rLLzB}dHr@5s>kpnB+4jO?LxZpP@(;)LGk)_?y=ZI8 ze*3rE-u15at*y1j>m56?U-*Sf^Tq8o&|=>ivs)y_9rtjS?eRx4>C*D?zR=)THN(7@ z$|K+yacD>Pu4fM~SwHiNc}+Ui&o5pNo0svrVyuUZVNfcmqUe)8q*XOIp((%dB!x_c z!GMafM?iz>rv26Bfdqi6q$0Xxj~h0gYMeS{#*$fEmo57CiuO|s1@-)dM_RKfUD=gn zYA3RtId2qnWYeF^{yHyQs_5@Iwsg*m{cl^#O#DF^yQpNS!J*q>#6CmdHJ@ zrrB9yQOuJNpHeAm@nU-N1Q|w@e#jk?ct;rfH1If+{t;WfLJXS#Uo$#PMH|3T8X+Ks zAV@n`JZr)fAU)9JZZhS^o!H|JrZSm7dn)_QTP^J&PI8PXzWjukOaVNif+K=4b^P|) z>p|HorGaVdaej=fV?E!%_{YmnU!qPG`PCyk^A}?5IBLd`dB+l4jY7a4Lz`gPH7W?( zqYQH`&!OtOTkK{xwBQptT4ejs_{FJ3=vgj&R7mrn(IN)g9%2}0Z)e4kth2?0X)nT- zLHUs)a%z!B49;IYZx0r59CduZzujJl0Ut5;0(~W?iW&Z+yR7%H!{3qsKEdEf@#sKf z4Vo}0qQc)APwh1s1%Su5d*Z5K0J;>}q|O#*QHLf8hY66O8+8b%T0F(}l7S;@6oKdf z%ntufFBa4mG1>Gt^0p({W?w#oLp(&HTLCEQJ}k*oMI?%rO;vgxMFNsUOu{OPFIq^e z3J6IB!(Z|Ddp;lXp)1gzY7Rt*fdOBz3vfc}&%FS4Xae%7H9=LX6(BgERVBqlSjVV< z5GaHV7`ymGsWvLLK*~w!s={wX)W3QKsw`kt_QFN)nsIpNm$}PVtRndOSFZx+tKm=W zv3EZgP#9%^)@xFaX;E|xKxvhJY<+_^5vCku3O_|4TKh0kAYxn)QwRYW)YFR;3qM_| zf*HaxD<~v3#y$oeK@(V|HNiD`HFm>M%-S%>Fl~~0yvyHTc+b^&tpc!xm2z)Og?WVl08E?J%5kcDp#d}; zrYi)Z3W8cDV6{S$>`5{dDq*nKJO`L)IrLNf_4BQbW{Rxnuum`+gI{K3uR9*7#S1_e zd80Y;d|?hda3I*D3sC2@T<4eg;3I}G-t%>(Uc1Ant2}d~qPvuBO7mOR6{K*?BhhSU z?&fA!%(QE&(DXTcB^)#m5D7NfD-7+CQO<`=s&IiXywC&)6tP3Hb>9}d)0_E~4p)|m zi~sN0yp5P5erQxUf)Z0;N(Z4a&el6<$sz`)`W;m_FEEefozOZicPzE9M599XEQ8n2 z&ED$I2I2y1M2kF5l@J5GTL6B?xDGVXefv7HSI;tC91sQ|49r5)WHVdNkaYe>!-{V@I5=?rKL%e0S;KE-S@I>MI^ZvEzy7Q*ZfD|p z_SwAnhC-v?hs96vP|g?+&^_s^NdA*PZy55My!gdx>+=nwC(Fp$^&YO+Mj9FiMoeHl zIB}qN>thF|Ts5Pub1VtVWn6G*%(>K{;ciTzA^Nsg0kWgL8e6r=aI~p5h}aVn(~YVG zaRkO;CXR*2diMYJOG97VH`LF}d3H2pO;fUEle5JQeh2GW7qBK|lecC+=sBmc<#or% z$nfr+I~HGi`?2QP(LR8R5GASQ8Y1UwBN`*ie0TeNd zj8;h?v~Z@`76G_S=vzHKc+xK*DaGCD(ZVDEi{JAQ%(7Q_zi_?%6$7CR>XwD}R&ONn zNYBs;Y$C6>Ujcm45ZS@CyeybvQ4uh9ND|mcW-vm8=;E4MU5LdX8%Efpj<2mp^QO@o zQaXXk7dP5-e3mx*LKX6aL2~uLHserSH_xsU6FD9q+F@GqLXngS zvk0r(r4A6rh_%S?kw+3-JI8W(WDfS&c>+Og09uDqnYs19XWiS5=C}W-Z)y8SuCgMd z!GJ%|&*^~{jiZ@(M__KJEin)PF4(Ar@2$^0(i_;6eQa{{6*hkFd)1z4kiqT8^2p%> zWF*htA}k+k_A0VR3y+eACK!1E`V#{vo{%R_+n3j;#1~LkV1QbJ2~LRFvSv8|MZ%%{ z(#E_q!=MB}#0>CTK!`z9h4@%VmntF{K{V&T>_-(uDoL$q#UI$$ofp;E!s->EmH^mu z-s+JHnPsomNhXlefnZjx1qFdLa(!r`HbygB6{&EcGcOD-iqpCtAw0*hE@Z>v$v6;? z2NzHy9>(RkFAz9DaX>#RfK2>~Brm>(9uQ&>(n?<fMeL8?o&N4~4wF0ddVU99apnQfSFMVXz{R=%seJh;b@ z2h`hG229>^L@gUtsL-6%)AM`E_IBGRX*B&vl3F&*nsLAOQn38e!TeKq$SVe#MscIR z2#IWfNe=JyRb$C-sxQA=U9%CL@Z5f@WdcT>37g5Do}bfc$7nh+=+v7e>fPIIMV~fp*~LW>4?4JGqFKjUab2yDvYz z76!;v(A4?BKJOF%Ex-ZTw6=KC1D}SRUiKoP@i!yDB=-Wg?&h~Nn)mY36=n&^;&gW~ z*RkKWm-BP*dSDeJEGDdQ?0-;g{FIfdh!o9 zS{PDiTRF3N-HkUL>ucQd><@>BUXL{_rs$u~vbX1@`mkX`_8Y%(<;ey2I(wZKI3$Oge5vHSgz!`;63t;DuFEA7ZiTeH%u zS!r{=Wo)l-)|5(HOQjv9(oUCQuWeUW+LM*`Wu*gI>7Z{IKRhyWP7nT_I@Z7aiGwR{ znbp#M`pxv5(s2%7#-)G@b2!F&+{Y1BK_xtS!z5K5>kL<1YS2H2&nx0+Rl1=gv!*JFGEF_1s{_WYLdFfI`f6wuW{jXhh(_Ony zwbM&Xe4wq#lJ@0Ywt7XsDTxB~UO@AjIM5_x`qCdAq{DGn`D;9yA_1m)28?Fz9vm@w zNutiNV+uxKDp}KJs4FW zvWN9NHqG6DAPE3r;)ezj0Hk?JY?Lt2Qj(v0*5ryPv^U!gXkU5G{93N%2_8`tNPt?>Rq?6CV) zTi6T7`z*Jl7Qn|;&_JzF*!*#EY9QxOtA%et!b)*LXyFcE(^}_o0tn;@Rc6_%Rw0F$ zeS5Lzv?)EK03k6fL$e{ThD!^}Fx7k@`vGVPR3ao}^-8a@3SqtAEC63sp%{|Ugi3^r z!UKf}@7t2EX=?R~^HZk6g@}SyZAiGFk`@c5RK;1uxR-KLVtlAy_Z%IiIA3MgaTcp0a2r zLu|dfMluF>6|s*&B-zZ7PlO5*8JP65aNV3-tf|3*lmIYcUtz&Ad$#$L{q}Ni3$^eY z{nmPDT?TyZge|xLm~2eI49$xs+bnq4!`OMu?$Ex|FSQ{sr_-AGomb5u;b|f#QCL0Q zQkDo??Lxkz*#>&G2r;Ef8|K?24UnW;?6~{<-O^}ZIZ>;Tha*7S&Cex!z>rgyxgU-i zu0G)?kSC$Y29uDm07voQ9qj40yRYHsa$?R$w7l%q({FF@qJcY(*==Zx{Tn(02@pS) zXZLxio^l}%VZF-V?|vMaNB5t!xdhi3rwS%%86g41pe#)Sc?{(Xr}Q~|v@Gj#%TG9A z4e{p=;IElw4-wG$G>s#5)W20ki)KzV3z$;Fb!2sBQrkP2uU^p#r56AJB0&IyEB!G{ znr22kP5=RufU(IQYZQq=X9XKMdUI3LvTNSbJu-363y%y9oYP`N1#o+NmVGQgLiJ(Z zyzD>xhXqTPtawwrxV<)LU}u(bTo3t8PvZlJj8lJ?@Avs~)d61=!=^1T`I6QNzgH{W zdi{?|VXe_l(%~njx7L@EVK#q}Y8(GekadDn8Y4wE?%j>O=jOOazI*T_$8&yz8>yR0fn*{QgsH+frZ7v<8sdoue8ia0Ne0NNZ~+KOqadAt4108^`FlS#xf2E)EMNzf zRX*=yoFWD-5?}D5$lek&HGG*pviJ1f{BAXRjEs&v=jnakIwJSmy`fPw%ib0YVnn^M zaEisIXeCJ`_$4HY0Iloi*`MN6!K4VFag9g1aoxP^FP_WRyDnx4j6;bF>?2I5R`mPb zU||6Ul5c1*$#D@ zBt;OI>_{?&1gH|f>Ma@~^hdXXVdH0>)&?ELKqaRts;igZ`>fWDW-8^UAytKH3o4J0o~Z5qcp7C^HLla2T> z?6MaJG5l&WU@r@7F?DG=L=salp`~k^PqbLmWTs7gOrd_P&n|InKW1NGY4od1$NREp z_ve!kb8~mUjqhm5m*xrf6zL^&p{P}0)}oTlcDl|pskI!2N4uiS?se~MHF^9p8NA{r z29jzEV))fu!Z`pGzX+ufGDWQ-q3x)!r%D~A-hhc0B`D}Z{Ot>t)`%l50I|HwR$+FZ z@MwE&(G3|)VIPkqMv^4QQa|AR_N8`(4ZgyoZ}~;g11*jAlp;2PR%fj9eOYRfFOt%k zct7aZl+XpboU3OQb(B$uz%@~=o#RdROs=EF_%wl#5ayT?p<2qhN-}{AwUN%+LE-o7 z4@**oWo!ymv0XXS;-Lx|iu|GxfWY8aHu<$#f2DkqoDb1@Bk; z;V8WOx7dM($=Ri^cLRozd6R5Ojv|}RTRdoSKlZvZa*tHWoI)&_!kC$OmTSE$wh0Y zPoCu`=w-Zlp`q!#fMGDK)o+$bgfX)c8dODK%_beU8O7j0^bOhzR#b(x2LDV4bc7^f zhS^;4@X)}Qw-5a1EhC5PTc<9SI!d#rjjV354>g>Dfvmg77OBsIrmXd#z0+n}UcOW@ zFfcGOGQ4Q*J66q}puPgkcl0U~7JbOZBuFwq6$i+s@A4WGGZ$ho=XLsPC4CK1)2M{m zYw85h*kR^{(8Na-!t4RMvL+*;0A078heWD6*HL?drKSqPXOf} za0tn=ky5r`lD&Uci$R3MTue5CfSe*ms9LX)3#;-p08`3LzPmMFn?#)e@UAb(kUs#k z=Bq#W1=b0@S#zV=A@Qa-*{?*->@eeh?XQBu7ko?|?X|2@W?}x}M!VLCI-wF6VPJ$R zV)#q{CN+}sg3~UY?dA2vI7aFjl0DUuFDZ>d0<-L=w3#kPmL{MG(87f}5Fn2Qb+wG( zPR7|=-}-V7Re_KfjUB{{>3<*~uLj}*Mez|vjE%q1*1K`|2oO1$6}jI-JMzXNBfbUW z=aO}Pd2xq55z4-7hK%Ad0YBU#p4w}l4kw=nh)Etvg-rr4FKns>y3!A=bC_Uorz-YD zu06m==(10X5>A;!h$&U6r5HKv@7kB7gawH)`nCB7zEw;i4k{w}T_~`S3_>#OLBYNN zO>ZkCAUi3Yaw%3#5HP*TnKrp_|VAxa#iHs&?X+7Kn3hL3vGTH0WzAAbkZL94zQ}zOTFJT0{&k z!52n5KdLf?v`V!0F&G6ZjffNN1=3O&?Bgw62TvM@xjb&pqNuJ5M`CJ=Vi4>rCE);h zhso$aFqA#H_l&tjDxx)Nv+X>Vy?8L2KgsyaaM#bZ>0A?u$>>2mZx2}YRw)C2I@0DQ6mas})&=TWOfico2CN3*>qj4B*4;^+9x{DVeN{Nl!(FV|Qj_Y$#as$I)c9tLMZll2cd6WFxh)M+NV2YUP)D}F_B$uzrD zY?@y4In`FLu!9Ua7RSQ+&0F`3Up5q$D>d=8Cu{;##DR!853O09wtz*EKQ_MP59lF- zSrH85B*Cw93c3Fq$D28lB9~p(gZjhBRZC@L+q!o z@r;l^U>fYI8fS&XkVjE=CN$gaX;`L;2C`ARe0sy+jB7im&wq8(WBonj{IuMe=*qI+ z#dBV&18r^Dum5`6?YG}h`^D|F>ti3w_Uy@vZwhc`%=VkGYJFeq&5V((p|P}l!z?>l z&jI>%T*f)#T)MGRmjOz{+E~%vZ0kg$q2NV+&l)+yT_w=6Kh7OYG+MI|M6gdeM0M3H zTQt#f7z1gD0sA{VZoKNr4u1bg|7Ttte(b1CJawSC)IM!w)x>OesoJ~kY#Oicvk8q^ z(<|A1dEpYqspE%!>8IcG(y7_jUA}1yu!-2eaYmUP2Z+PORF%)U7{A(AjDeG(}+ZvZr2ba4^mx##x+fBz2m9gQK*8 za|2=02{8%7Zhe|(868>H*I%|%h^nJ~CWbCHDI#WJcv8{mYomk>2=ymdTaHRINt4KY z=K8Jl@}>6D3grxJwC-_#;}w?C#U=zPD9xb?0VKu2A~wJAie2-&eopQhrwGtpBujUd z-g%+6t`PSw|%| z(PIaiR*NG?01i-ixWg0zd%N>LtW+xtQVCsIgVsIx(CTye+SFYFwa5A_-6D78ua)Qa z+uO&G0i2U+8uz#2cadRhUED|jW`3NtzMm%AkXIlRy~b*y^+R{ z1_M1>cm+Ox;7x8jds`5wdS0doBL%Ta-AYNu4lP=Dm%j(0s`Rn#6x4>16mnEqrDlS( zDriPVezYpGmyv=6LA9h2vEl|t!-^m+SxAFn9LI%LXEg5X;uTdPqoN?QNb2_>v8WA6 z_TdGf^5kA47_#A~fl*{*+7RdstzIF6hiQI;G(j5e^B=Q|g!Ox*RvzV!y{08<<%Ix{ zr(LVMjHCX9hzU#qpo@wKT89_wip*AUm@G*tWZWG2)cEkue0IFkzpqTs>tIPhGI0Lt z`TWU$B8P*ddH_lG%*V`~mg=3dODG3=Oj4gmVPo!}g0QC}7PjX)*u=u;9?bsTU1kll zb_0>)Tbr|c*H|q;m!?gZI=(!fNp1Pc%h3RNGa%Wtq`%nmo+T6H0J3Ywos>vA}tjMNXweJ*{^ze z-bs@YMn+Y*xNVU!^`{F07zYGN5t>w(Gzc0k?5jvfyN-KT_m&@h-E3`P0t2L&){UMJ z;{}`0FcD+_mW6it3l1?-B>}l36SctoHD|gaXKiulqX%q=S%-VAg<7)JKnVRkz=j1` zTQ=Er+aRQo$`4B#jIsVrNWm+HgY-d6g>U%Yfj%5&$oXY-%Q{%Xh`6TYM|G|<;K>!uk? z)-3BP-Mqk-W|+?CXJ*)$6D|3%Wd%Qu942NiO{1E2;ftvw$ZV^VjhPn?wNlJyRZrM- zfrnc7%zDh2Cf1wVQ57@qzV2KdCL5Ng`d3_RQud380tSg^Ba^j9r6TFUwR7!6BC|za zF18=d?p$idZAPAI_xLQFlHEAZ)&)N7R}dwpxj5!=jRe?KY=7_rTc;pxLu~62`+=Z> zuocVMvX)L7J$-^eQ6ZU4f0w)`4R-3%D3UI;U?T9g#dcR0@@fcSu%F&;O(4Tm%1TuT z(8>6ss8>mFnCMco??#|eR0kq#slKln{mB#d8^HoaWR#Nq@(p$emhKHlo_?Fy>UX=# zou{MfoJV$86`@kZT8DqE89w%DCXQepD*x))(?BfTxWHa3fpLo1RD`H#JrN*Dd(}U3 z3NVF1fGIVBP?6O0ZiJ+ltCHHy@{5!bMy9pV?$+m+at>QyzBDGA>N-twR6(rhva6Uh z+4{u8?i6x@$l_*$H@1>AHh*f%oLu@sWsvsYITLXoLR6>4*81c>#XN{@` z&P6wHJ{PdgOF(A@V?+%{Hd|szxn{^Qki6O5I9RcJ;~m`7+b!t=9Xn zKvjX!st%Rk+sAJs9eUJi{UC$M_R69B$$yXr1A%(~knLL#ik(T4r|z?C@uDgg_hg_2#%V7j5f38Q#(hTKjqfW&Mg24TjD9V z1#3FB;DhVt+B*&iN$Cq7*_eKiK_I3^TvKae%Pj?OYP7i)873#S4qpVRSr${oKrNi> z=bFdk{n?y}cC-3-w%GiQ43Q`R*%we@YA0HtgaF+q{FWJLtrLOcy{E4~V5eswo7Is| z`9y+|FKx7xg8h0wPZs*accdw+W@Ha;^9Irb$;u8JG`RwR#x-`-WRryH2_;6*wL|Y2 zo~nS1(8Hg;&C)5v=mF#n*!8wxjYO8xwX?IkS6FlGDx4oOs&I!v_VQvXbcnR3Lz`dI@^Z%<`G?5fhk&nFdM~*Egq+5FCs`?NW&-s z9U#DQSu-o!>93t?Ys^m@Hf$mZL2MisjMGwlL6e%^y4bGkQ-x*3u*RMZG4%i(Y2i^e z|DUIO@r|0Q)5T zm9OEZ7>yMae;N;ePR0>Wk;nIGR^!tkLgKceD!B$^v^8wNXSx^ML>p-Kb>`WROVW$1M7j-?Wt%P5>1Z4|Bsl ze=oS0i>GFL$_b}r;A;~H7o2nuHg@<_0f$-wV?r`!d3|Bi{_LegIc*{N45#9WKKmRp zl1w%+qacGpin7TB0H}&Efb|kNw>wf}@7rRFGGx-54&=%AgeL1r*<|~YKjO!n4HHx7 z!er{j9loC3XDeT*!;}jeLQbY!Rfs zD+c)LF^r%BZO!x-bAErj5g2@WuQ}r9so7UQYt%_TuAh_LFu$DEl<$@MlP8Rzhb>X? z-wPYPBzI(o?_Oy}(89Q0Bu7HF(bG^Wtg)UJLn}x@ghgIXyb#cy$nDCl-z0@1MS9+v z@At89^>c|LS@pgC<}8flP-0s0KbE}nQK!{^`jq`H@Io!RK-Rvv(MABfqFa6ozLISA zxQ59dF{09m!5*9Xl7oyB1M=(@s-l7;U1i>?Wlt|jSgUdb(@-kC-@q4M z8T|itXT7680GXgvYMgA#EXx|RQEshI&{y{Y7HH3=Zp!{HFI>WC89upS-B0wj&Z66y z#mFdh#I?$k{~7Od7Kg(owdBOmJc9s0p7{;|fGIZSP^D5Jk=|Kj4c*krRk2&HjMqD+p4!JeqQ9O~ch*)X7hfC;y@CwB^)bzCL9}JpW=;o2n2xu`rYo?>@_y?D@;UC?F2k!6FP{>_M4;92Q5hsc|kP$(8U+RsZ2m(sv_4-W%LRZ34rL^P$~H3Jml}z zU}|qCjQ3Is^_V2Jsh`Cc#1~RW>SPsDek$DRUqp=}RjJ2RYc$l5iK%LOmUIF1Yv$SoJqA`a*x{)Y&zAPS^uGTD=14`CUJ1 z`x=KqA(E#E|kOmVVkmzv}q;fsh}~Ads*GBC3!NI%L?#Kod+sRT}=NA`-@K zqkYW+I(4McnZ1O?XCH%3Dy(*COr$99l7WB!R-02H;OmA`Xa-`5VUU>^VY}4=)h%j2r z{kfxFTVNs*y`nEgl9JWMWnc$PMQep6>u0RIWwxgE%Xrg(5hnI^ z!bbA4e5HC?3({hts!pKAxK~h&q7B8#a!C#Q{E^n%H!}G3eFMM$((pEaApfi=Wv!(t zT_acU_nlAvXGgz1cV6!TR!7#gIlKRqeTVdtM)!&1bFa90=F+uHTXBg+2L+nBPfX@A z4u|HcnAc))2T%(wMfNMZ{4uC$_K4P<%WOSpPN&U`(b32-=Ym-i&EsAEW-GanFC-Po z#1^~K2?690T9dLeG??t;6EsZC#2_O|2JGW1y_V{<`5%@jetdoQcQ0lylYf?9l7&2W zK7E_n$HL;NHp|;WhFeoMg^@_%1chmog^vV%uLk~##vK^2)Z2?jk3KktXR*^c8TOaRy?8Ka7%6i1PB zTIc&!J*`Ws_bfI)WGE7bnm_0-ekO(1A!DyYgU3Ek*GYtaxGT>mv5&#eJ!lytGML4P zyM=1f*(@h7=XKPo?dlWahl4fRyVozGhc#{H+7orUm&9AKZ9QsPx+Sz_za+W} z7${K`WFhtv)*(YCz7?jh<%>=70KYl}RS`B*7utivzJx~+M-XBPqUjS6QqkY7sThbx z36jj!Jx)PP74=ttKmnu(MfMSGfMU9O1>mIe1#1HQ=(mth5ssp&Qf4%8s?;eQt5=r- z>TjSBT~sGdFJO&T^TMMLUV84goKQ2 zUNhSS07Z!JY;nGOr^v<(p(elP!z<~I{$@jrtb#q(u!#U{GwxS2*nlQHn6d_;Oa=S6 zR{lY8-CUa~sUk=WB8(}DScXFZUF2|lhdzj?vb zRm3t?(gHI&Y%<26s@P+J>}_Pk;(y;2`I;AeLZu04AwX9vFC+%|>;Z&~U>t*YEi*|} z0DOe0GGXsDXz5xfoXS4B*2EMuhgh%EQdEadSwakr+4ronhx;rw^t{B>4_l^7lC*xV zpUn=f+yN|eI&JgiLJl8d^aC~lt<&>*xi!uoe;+(hS5M{7ly=K$0LiP?v+nda{z9<* z9PYL6rMfFdA|A^(UF(i}-r%RcN7 zKdlgEPHCUC`p%h?+qS>-^k83oe~;NK+ke+z-##WVIio#frez=q3 zXy*@n8;bp!P$~nB_XyzBt3Cby`^!UL+nWtAEu0+{llRqB5g(|HrX z*9*&Gl5jF9lm1CkU^diDErS3WBW%Q=Fu;W7thph7KL(QGql*|>1S%ByL$|?a*k8WE zipl|qnM8;pl4KNjEH(b7{ccC!8t%1MH(+zcG<$`pB+()<=o`wO+G7b1Sq-txon#aD z=N`<{2_!9@@RvM5koHV(Hzo!VmJFSSBb(@zIrQc)$zqc(!LU(FSJNr@($tTn?N3>3 z%e(9k4bEmP0Qr4q`6Uni!)8Ih0$>NrgTW5!*ue|1G}7}f_Q=3TMx7&H91-N{QaGrn zh%l~!7Q#%?b6V{1$GH+e=(um_6YEV3EgfHiM1Jd0Q~#4E?EXeTEiP<@uKbdt@Jshf z5t~?3Ed&Bf{3AJP3q{2b;IJ*8V%eb12WoSfQ+7|&KEJ}MBDFZx!9rL^N51e&j}a$) z&_9@4BL)Hn`XQ*On4z2f?H}aToZB5O`Io@}0s=1V&HClPbaazGG>CX;i(E(sNF5p( z!qB0osLqiLFx_&yK=2``QUlp5@?ets?mdwgQ}u~0`5V@QQxK>`%1^aYzW(YrQ{~@v z3iu$H5C!!VxFG%%qe;S7RSkjEk%c#UGYhCHRwL{S4}J?%&N-N|Hs|yL)}_-Cb>h4P z9A|)tuo=w4xmz2W9tx1<%2Tbt(GOlINK#BWHCC@`AhiIUM#Y|HN+6g(d?EO%5<*g_ zT2=Uo>M_YeRSccrl|wf7z|jDT9mLp9^xIXbgFW`DbtDmpTO}znI5OjE?in7**R7x> zDPT$h^2=UtF@#SuBN+C^^y^6J{1K9Dp~7BsC$u(+LB!~fI(s5tU%tF^z`wxwoh^B- zh2#q;*kMAIHR6BV?~11ugD}|xS;H@U2>7bP9UF=OLf5ze9E`w0UNTf^A*s9H>;TQv zUsOYSvv;kqS2IvW^XZ4}M?Ej}M+H|+w`mw7=G(9HUWEb*5+&;-;uU8z#Q z2q^y`|LYmZuA5_5jrj8bV?--!gz=O=S4a_o<=#1T3+mP<-d%QZ%Qu`86&$(+noT`V z47V+^4^DGBaX#$rHY4&&gy<63%ZE%D0;g1cXLEi5kZ382Nyo~II={2Uo&}&S_BM*S z_Gv+_Pl9;wgW7fYd=Y}fnx*B zAG!OcB%l|aw5 zwM^JEH2CI!AJ&}JQ*Qmal?_ckpp?N036}b*bS3gLHd#~9{xU8kqVsE@QYPih_GgX+ zF{8E~>l{SD5WfMx% zr;l9Gl1<7d(YoPxV2$A`vC~_ZAHU&P&yn4O1DEup*WG=CANs)iPc=+bhcov92pxi$ z)F7DSU;<`0W-;b3m>fIRazuuP^P@=-8D>2;k_QBsaP4@-#UcM#3It55u!a~#Wqnu8 zw8fVb{yZH5Y)s{$f^pp#~RG0P+Ke{VGJ z)G}G$zQh)8e&!l8sI23m?dA?6iW3fedIM%#j$I28;QV@`fO+dx@!%7<^_ypr^z&69D+oNCF&D)O-*_WLOXshFnNe zdn}-<`E{Yc#uYL6%!}+bC~F21WZzmh*A^6(`@Jd1JhId7)sk6kDm#JRe$?(%qJV4o zcrn_au*1}sHriFM4fFF_ZE``CdW=aCEtnyo`4anuDq_|#l8h!1B;lBR$-;(aqrD7F zv9cpm$Vi>DT|t^3GD>oK8y}6d0ypVvPvZhqyq?6gizr*~|_Ppv%q~H3WmCk5~g#VL|fL(q1w! zTwsa-uAAf8N%`sxD%R4>RPh!IJwc_Ai1SU>nF7nwR{wdYVGO#F60 z{T!AUOnyYVdGKgod1;o+5D1? znPbLS7qA+Kn(q3Idt2Av(tqki_raaRLv?)*@xo)yw27s)KR>&xli7y=tUy!0IV+|r z0KwG8Gzyp>nd0GSn$rxVS#?onM3@F zkpD>-K3hrv<=fW$a?VDPJ$}Qx)nJK;FXl`7+2Oiprtu4 zl+^2VIwriy7qE2reOcfPq{l<+)1dz2S_8E$;DSLvPQ=1*yJ`TzEYw14i(>2psY>DJ zO)A73pxP;ICdto9zo@EVR6;Vx`s^}G6d4E@(KO@_|X+ilW&RC-R&|%u>Xus}uKc zu^WX#5^$PPYm)8)rd-EIA29W{#rA|Yb1k41q(M|+WX){LV6cFqjA-%((6nO1e7g&s zSqFpwdrkkE^|v3(*CHu$rV%YJ5Yws+Vg!H>ykHq4xQ4bw!Sg(`=^I?saeD6>d&&|c zQURJ-|M&?T!AY>_PwXQJXbWOY#uUkQ

          >R^t2MoO^voNVX=9t3KM$mY(Fmk$}k$Z zuiMfWGP#egHJNQk?MTr&aw%LR`SPLcYcE*Y`RgpvT(%(4$4(rGl!YLD0@qp9t=Q#R~>?!ml&_($X` z{FBC{cVrmklNOB;Q?wj%Q@ny7BCzze~(uvmjKlM-UXfK_5 z;l&q*2P?1SqxaS<`(-D_1RA_bTK)amKm0?sXi?VMnoXW;Q9bgAty}%tuVoKEEa4w3 zjFd_*W+UVN5ab&PH{HAJsypT#=`GoB)`!g$l^mg}@MT8{WHM_4P+y zd-cTOeSVef(n8S`2%~RWtW}6Qg0X}_OdEKYo z<~YQVL0u~FnV>cOF-5BO+S&GcEYX@Y5#w9}S_x9Zz$Sz2>1JIhq6p|m-TMraIszaJ zCVhZSsc3daQTYQLE!xVL@-Bq$@mD}VFrj0O1OvYQY+=5BU|aSse>)Ymn&Oo$V3SPp z(Oo77|1Nzu*(Y?iAMMVb@bnT6Mu_vUuLk0pE(PT62vDVTNZW$R*0IE6{pndQrYvC~ zmn**`44alLk@xBft6LL6EqkRpCkO1SYL@AF&P)JG2z4I~-KZu5ypTkp0zokFZK=+C z1}2-B*nw%C)|*{9GyD3>xn-D=0db&%Pi9stFcwS&}M1qRWXLeCtWS zd<$SIB>1XL#fTi5Edqd$0CjSqB7ZtjC3h*XAf{>ybo3`i(yA1DPgoy{8Daq4f+VQq z0BNyT8=+NXuRo)p#ZRHwBwVNx`Udm+xj-~TD9Bf}z#kX-x?&)09Viq5K}MYH3$;Rl zNl|tx`GHDDRS`1v2n(aBRNmB98})k}E5^2fRcB9KI#9HgU0rB}+NE<#G$TtvrV^6W zAAu=nG*HVgP@{$rt5;G?b;DG8#)GOZ7@A`&7Ag%2p{{pAsmMsDVN_M@t9lVs#a_Qg ztzLyZ0|9@-^TUp?1!z5%3~}&eKJFl_u~_^{V3xo_1~BMrP}cAbP}If{yyK{i^?DN) zn<7N=rGxg=6&wsQuW$&U^(8wX`&ecl@1;8Gvod0UlYuHsX_k_4wC*G+@$U)3S6~1! zB6-~3AE|D^(T-#JOn@l}e*cP1ikc=H{f1b*5qEh6PptWn^XM2z1?rHm-iPAn2ddb z+K&zv;G_PD^`89sv77IYXxPcno_8r3;lOZy4iFwc0SJPzgX_5$MUq$AE-y%Odh2#D ziga)If} zTdz_?3?K$g!{Oh&AbW6|Mf)o^*cppDDzb?|#J<1X4$87FTf?%n)fdaCB@Bci>=2i) zXCkkLb4*FWkta^X^ksmTPE305-S3z(WA4kFp6WeW_zP+|e?Islgku87<~nAdyyRCy*hiMFw0?&>Lb|N5H7X3HXV z9@YuY$&lC2=Q1uNqR+?L^QpXssc|1W#lW6{Yka8l%qfbEWJ8Z1?f=Y+!;kqZg6cqX zX~MLTRTHz>rTkv!apFX2$52-7VZhPhmX`nQybQpOtJHqo9y%>F+13E`VgZC!CrplT6+mFjY3#{QvL)AUYJs`fC{wc zdVq|s9wE8a-wuNvIEWD&DhR_-7J&+LH+B6SV822sVd5lpv6o}|>rfF^8b%v&1o8ZV z?5F&~8*{y)LI7b5k~vu9ezz1E0*RcZAD-g@Ea*uE{GzVz!pM_*jH$Lj0L4b?wa&w) zh&&vc#@>R_@|Lm)H~!Qfd%@@{K7C(1+fGj+K@6L0`HRoX2{v|=8ktw`Vc?`e#{zkL zvBw1)ZJgAa-=ZZ+#AuPsvSGv)(aN_7QAA9R*wZfj!cmW@nMh(eZ@V)g&xRAqFSt-a-3|45UqqO)@$n zsm)5LD%~TmCJQhN>N1#`)pBr(rUnyGB$e_YiXsXY7;REv5JkzeK}1V|m_or<4Iq|2 zu2`v9r=9bNF|A%zo;K$ZV_U$w@I56|ZT!H{=?7@5BGszheX6S5S4Ei8K31fwloSeh)X~NSqgap;>%PF#EKjye`7OBie9X@NcsoqaDZ{ma3ngC zX9J=aPBAg25Nr+%n+z77*mHVP#Jl1|1}Jq1*J#NXGXcdo3((Sn0%CyuL))_tUuClh z%V;1jd&V{i+ z*vOE?5WJkiv4;xF&uLP7tk1HRcK^m5T8N>@US2T%(`)RF67(J_%BZmat<8CIreq7V zZ(D3BGA`62Y9HR2y?7w+f2^U4HS%3(eZf#MC>Tsx+v#C1DvU>V+Ka*%aR$jy zBwJ;#LqOy-4Hp!F1?(3LF{?8TU`qYQp0oEao-_4Z7khg>p2nu;_rC4ANy~43^_3U9 z5Ba@Y2738-v&?Tf8w2dc?O9gk4($H^?6uc^JlCxPj6P4F&$}e-h>pJHh;l<#YRpP4 zS&3Y@)%T*=CYDMZ=-9e^gJ7VaP4e8>babS?2dRYCj^+=1W?kp>)2D$>-FlFU)v0nB z=Mm@L=NxNBp)uKs(}NU6Zw~<4*FJL=OtzbCD}hQV7A&O2l)tobaNieR9sJ|%S$AEJ zhbORbIh%0~f1BMr0K z8dgkXz@N}LZQ7N$uULKc)>mIWc^K>GZ;&}R3=Iy>zv|Z8ZoNfQFcTL2&mZmVIY}6m z*fW1ov`6;J^CQ^;4Nz`?%nJu>Qe%?^n9O(3l1D(3FVj79F1ehH-{JR+LjWCgQ}m(~ zse*44t7ioiWnc1|bIrIbB6ILmwsN}N>$G9MHDQU{)Wq7>Y4;-dw8cx}-nQcE|Xn-}uw0fo7TMI)Vx`nRp_Mbd5QIr;v*py#!pcNs;X*B^uTmQIaAoaM{Y}Q13=tSPv z%*v0Tgu@X zh>)YGiXIDj$v`M_RY@3pcWd5D2%8;WFqKU)*`uY}0+bI2kdY$+qV}Kvk_>sE$OX9u z3IF(Fp3z-Wposv0SPzj-+^gnHy_U|SuZl?(gn z9Y3{EPgHM=M+hsMLEEOEc`tTlYt!OcJEDHbR6U)GK*)$lRj?4h+k!RyY^u5lPO6?F zbyOGym?jI57^XBf=@Gotg1)k$NJ1Ehu7u9cIn0-if>^ZZ|Igl^fZ1_f_r2&oJp`k{ z)8195*6Lk#>gb|?Ws)w}IB{YxATf|Zfta)!EZ6H-{RwJaQ`pE564-Uh-ZO z>!X6OS~g{3W`^r2iA^|A_*p}|&?_1!l?o6F10x8~rId_3d_bnL`k)XP1j0yu;uaG? z9p-oToqO!XGLx4(`tTE}bk&Puv_>oM5b#aXivt8C15;zsuJS7~sxuVip3}*~IRb## z5Eht|ttafqN)XfP6&#rwov_sz2xP zP~=!sK+JA2J1`gcOV$)QyLkNN4HgM?o>Ztxf9qa5QtW=e@Dd6emp?G2TZK$4z7#F- z9OyA7GC+f}sqja=kc4Al_}$kNXD#(|#1SBPtPu%QL6ClM%q+qvuJkVtZuZqHgmpVt z!&g)QCQ;!6dE&<)0-9kg%Z|nGzTgA8*u?@)HOaI__!!xAe38+3QGZ~KElD5g&&^)2 z{H}X{ylHaaXy*=pFYs@KaI+b$0gUoaj&`zl4d;Qsv^Ix)sfR2oAlDFb?YSINx}dPM z6c?D_Eb>;(g5e(IuSbYjiJ3-@~Q&@r>_p4)C%eA@?l zhbGRRIx;pqcrn+?r;A)J_n!BEXx8=by63ta84VCB>51&aAs}*S5}B%5@)7BY3AGZ= z%PJ*7x&Y}6CQvRut)(lkIJ$BPCHa)?XusK1wguRsP&qMR&xwC)w=E2fPg=1`)+R9X>xr0{>4HF> z8Bo!MO&n3J?LOtPd9n1!#_-y4ThXwzuJ_ec=%*YGX2nEuq zA-mOw6_^YVE#hIL(m)=LfG-@$pz1DlVTVzu_*9{V07ab$RA@W=bs5a0U}8Qwc*GGj ze*!=wbK4?&SRMj!vf>3kif|wdey^I5;uH>tU}Dl9&BGcSK{8V8+XQ;e^O@)TzToGabu?@@YIU`Mr;=vTH0^@HJj4(4z^+t$^7LX)! zfQFw06#%wqW%24rH{!T%YOw6U|-;KAz`#M3Lu$@eE8l$GZO6!vVYj$M3UsB9PC1mcAmeFN@S-Q|gpy)%Q6ba}su7E; zSD;F>7AI5w)i^y#R1j`F186?2kZeCZ+{d$GLvSmG2q)5R{ZG&av3uF(@u z&{0P68Uhy%#fdy#bg|=#sh1_u6|X_abG;)u{rqhvu!?#jH5GCiftIonHlqt!LpaVJ zz50V+SKHAE`z{-iu!}sK4Sd)vNM2sJnr}Il$S{ptyR26NZ~%@_fmUUM5fbX@!DLP_ zqG8IKB-y^R&$6|GWY;pHFV7Jngk&&98+DaUi3Alm8U+S%q_8zQ^#W}E`n!xneb6&x zD>z(I*(G6%ziTlaV3n z0HfW4IFxtKblNbfX6SnDm_>*w1h@_!>a|0I!R|pj2k>YkD*P!$wWM={0^+VEW{;S0 z4U?h}0)q%MI*g;pDMX!$H8zeT&>v>FLyVSc&tCiL_Z!h=EO^8|G@FfDHyHg$JzaRl z3U>fWo1)VeFa&(21uep2!gVN-L4e*>b8M;Z9X@-63Sa6Li7!`CUn(%_T?WNeD1Kzv z4P-Z%PB8KRCb14JGB*o07Jk1!RXc4-d}&-(aJJb0pR<&ka6 z$4BlniQ2Hv7gPM`Uc-5?7q^FewSOKb$km5DC*_SHEb-+kty^(8wek1F9RI>*+xgo} ze-4hC?!pfQpGZz||Bh?bSo@wu|MK6g>lr!Edxgq|vsY*1HC%q^x={gDu3mx4rgV+U z$V}#@UsFxf99$`4~Gf(s%E|waXiH z4d;43+Bk3JT@Nn5_1>n*p_51UPL5ai8m~)P83=aiCE=3Pmd*~}++`^hNxCpa;*&bU1O5_KIAnP^ zByRb_!YBO^X<4hw*R0QwzV9tavaBfeSeB$0!D7 z+UTJ95RrK)5BSTRkSt}-wO37P;lpg^?3E)o`YY9uUpdzZm@ zep==eK4O;ly>!6^U|f310Cp^RU)9nNKqCx@0bzM3JU|z;9Rgp(LCgl_1t-Rnszz6A z6??AE_A)qDm|~OwvPw4^uH?1Vuf>1QSJyB?vbD&>#{Bv#H3v(nH{V_AWK! zD;Nki2$|*cv(aCk!0{pgR3`FP?;*fpUFunK|}s!07tkW zT1jGRc1vO$b-HLJqn07yCr^e+d9jw)cjpR~|)p-U@%6qCX6$#c7cglQuOh!#H* zQ4IBiy~)!II%$Er(mDnXwk_?*3%k{!0_U2Xd53g(1A4}rP_0%11;jW(+CrM2hM2Tbpqyl_{HqhyNBe`i zfK_gI;%ofrS;)v|Ca72*WK=*#pn6C#zF#Q~J!Lwj>nVW>M!5D>fV>o60^oNKTDpaQ zdhndUP~mNw5%w1L!V){aqBUKl_`vH@1#}6MM_OnIHVN48tvJC3(t+u63Dm_de5C|k zQh_AdEVcN9NUC_+TM|2LF(Z<(;&oF{JV172pTIPG^m0*N?WK%X7W8dr295xQ0-|(b zQ(*%cAyJVIx#Dj-X>SMDNTW=23J6$_m=Ql0YqTI>DsrG*MK!l2$=~W#7<*3Hi^j2x z9UX`RJ$CF+M2Ct5W^+S$<*;XBU%Eo^-j$)zs{wK|!Dv;;;N=Tqq<^4A6S_#5Scb59 zxZVij;hzX(Frt7-=RlIQ?BW;unANBs^x6WZSt!&xhk9)iuwaSYJ)Rh_jluT&j|1@0 zP;7*Ou3jVoDC3x6K2ZO#(@F&9mhAWox7*Rdj$|-`u7&4|Wpd0J!g8OVPSd`?igtg9 z%xw~_93cP!hX{3!J-6RvbTlns;D0JWv%#dACk)wCTVLOBaNmNro^CDm$5dqyGJu4KYckS{{4xuf46h8(|@CDO5{RwZcayjJ(Wl` z4_>|Z)8Wov2`z=fAcK~<;pAK4?BQhBpTH`CT&`hGST&UEo(!eG*qCc==)Iv`mvn6# zm)GBM<8`xdyf3sZJ#}#V=wM$>AK{CR_uv1)IX8c>ccfm091};jiU*7QMN8PqZgC~Z zzChIjc$8JC!Kg+6)vP zaYBakQj!Xu`L`E!6+*?ANqX>7jNda*7!YilAFE{C9YbRm0fwo%JxT&x&?Ob)_<|j} zoFV%Q-_>v@Mgv8KB)ib`Vpd|DqVhs7G7Mjb6(*bqeUVPRf^?C@HBc*S3{WuZqksswtqFis}lv}+5(SAAO5y4l^a1(1>|;k$ z+TN1r(yE-2wHSlqgah%}O-b+og5J8iFv}ObV3M;{JpguLQko!UyS|h&e`fNOJIr`M zA(T)R6&nHocf?QSf%oe>l5hUAQL>ykYoAS<0bf0N^gh#i<*?;EETe@%j4WtPtf@ZW zA7LPXBX;FW!tk+!N0kM5p$pIpN8^G+sL5>m5((|#m?c-3`sHRa9P;dT`pJMVY$E^o zTQ(3)?_-;+wj%<>c61vDvrA@&{!!yl*{HfADrklA{C?xG5tvr%Y!%oq_Y%e_V+3m0IZvTZpH|Pp`ng$c;sXd^_E>3kP5T@4DATY(TU@T^Bp8fa$T1*;zeZN36KfB59lF`Mq zz+^-)H5=IBG^21t5>s|h&zHA!Adtj32I;zUiB-A^46Q@{MqBt6i37eJaX)0ThJ}|m z_kZ!zuZ@pX^)NyT*%!B8LkfpNZdXE9ja+V}f9a3Ia-}dDa$7>a{CBj9kgLmmLjK_3 z=)d1J@ejwtSXGNVb-9K)`Rm)m%DPZ?aW;W}XK=%>g(dz-Zw2i8$8grO4Cv!3Q3?&Y z8LeSaK1_@Re z{^SSm%jI(4FDytYRI5M=hla4Bwdhlqv!omk8HA-a=P0#-NB{~95}C}x#|Q*WDjB6( zB(eX`?yy567i7b`C>09w=)y!;Ua(nos#8v8*W3U5s#Ou|6;tu8A9i2=WNElF0U=-uOgaR4h0 z-M9{-)hd4D+1wSL@S@eY(1t(&CcOwti;GWdlSL&UZeZAZFNtZ-_{D{Sr%iumDkDAKa$~Q4PBLOB>bjQZi`HdA zI4_36GcQJ$@*6o2qR&%znhhdzfdH`=M81fOJiCM;PP`QA>P8|YjX^7as8<4CdD{|| zyZ}o`1zk+Vg93qZGP)!sLkrZh%SJ}re9V#rS6(FmXcTd+oFw&VR=S+z_?0}MS728g z;PQ$stSP%&ru3h>TRJ=Z?N+lREd{2T0nkdu>VipKj3ccFVF8OaX@zH|kcqRTbW8SC zY}lj~-tM*m0$S9?XOdN0Sd<(N&*d_L06d~)5=4-33BXjm%Ut`54`enTN1Vbzp0usN zfGi*nMcdh9k7UDKx4?2gh#4I_jFerB0$q zTM^*T=TlD6>bO`KKBeSibU8yhW`>>q8WG788N|nLv74@@)ndD5)@MV@kv6WJojlT# zz*-#_pVBxXw1mnox+r2$y31yTcP%%XE(NSFK}buIR-T@*^`=ApB*ztSeVRP zFX0h3B|99#Mhn2i-K?gt{W*iA6>QKr&s0lw@5AE%)Evl2yn&b0#!ZuyDlEwYK}5885dml(u$(h#;z!k zmIMfJapO9Eyu=b;6 zAG}Iq-#5=KVD*<-z#^rzgt-l&MzBw|>UVx#99e_Iph9WMMe<_f@@MeNfAg4%JOtk+EFkZ>$l%iCZuON z&B)dNZLpXFAv2jP@PHq3PWWrj_k_{g>udgr7t=C*sA0; zCZiKJGxuL_VPIb&$xGje4@X6K;eAgCLxa1P+Jpb{jyz78Jp!;f@lbIDK|GK9WB&0` zdYJfy)^PNy3bc^Kn$BJ}dNoWqw2=m~ljd8%e$KF~N?u(6qJK2GF)v~9LENPU7*RnB z(qaIbjWeO!FW1lz5|E^5;unDon@kCa>DT844k#rL0!RA%Zyy&xWk#djjn>H!O^E>Y zsjn+VCL9&~Qv%6!NX6iY6HnS>Im9r)iL>@K9nwd0YLzN2a8V$GbNm}8)mXle0H##D zxI>#ZYFognW=1aQz%KeZk%5fU7JDh@I?XI8fMWd2^bVI;h^;WF$d57^VJ&H=yVAXO zJt`QXx7^1v&5SezTShA_nL(77jM}IxzJk%Um6oe*lo#$INxMv1UNokdM$v3SB#MC~ zmqlIB)AI0fEewv?;<1=fhIC~WfCD~GnMPetf$}AJkyi20ipJM|PnOU`D{yrFZku(% z1RAoYg^i>|i*Y!XR`q2rzdu9Dgto52`vo)>Xp5)t#GJfGV1bPk3m@H?P3Imyk}3OX z+g`I~jy%W(t(HNt$u2Tnf|(QLY&JQiOD#dFdQe4zJir=Vlsm5;v1gAFK+JUAFD=f@ zd*LY_>-Fl2lu!HM8e3;ngt&Wrn@JKsUa(9umUrx!JHM|e3kb7eIl=K7=Oc2 z7O3z(`(HY@KVA5Xs#*}mwHLRS1E2Y6yttje%Y zu6bVmhFM{GZpzi_s%`zq zt0SdO;#aDbBw0_X7D$X1rf8*6Of8*#ZuimqlGKSEy<3x-NA9ry`PY+|y=ut`{<`^L zV9e_xT+g(>y{W+v=CnaqiWvOZ`sDFycE#imPBN(~5S#yDE4K*a3x20lOChQjpeZC^ zs!pKlcxs<9shUu*a>WNlNXSr-Fc4hOWhGV!Z=JB}g)V@X{XNE!01+bd@NK5Wps=%K zmUUqh*EXg8SUtvVPKNCZl9+n^xcNHTZ)K$FJ=EzXc;?}O>qDJ8Vp}!Gf2-OL2d0=G z9UWPw!kqLeTHE21-~}NQq8U5wE~c=F z1w{g%WN=bhJEir6S&(y??`Hl0yG>Mlo16fh@ey!AA^%Xqmubf638fOH)EM|qZ(vP6@x#2kSTTn(!0=c8bO=CZ0kthdGZ=mNn|XS zM3fPc_Bf@kP^H)pG-_MGy1*Hk^04ultSoRj4@oB4jO%z-rTHZXD2}>HK1R65@BRrT zEr7_(^qw5B%b&~^E9#mBFw5DW79bPQ3+O8ONi)8G;Xhi47n(HE0n1Aov5V_(CKv&Z zEYKpuWk@peB{6Ym2gmHeX}y*UdC6#t7UH0!8;2PoNwx7=T&>yw%8?EJlnq*RNnRI6 z85d1PCo6xp@KcE~0FH&+!65+J-eJr1T5qBVAPpXx}(cSXCU*mqoKyVxgxH`XP24q;+Iak>B6 z9lh*g`CGe9olJ=@ z|9z=T+fT2vS33ci3Q1zbV1x21ifR=jY@c~9{M0%#jsP2BV8P-6Ig9^~Ua~brx#I|$ zJ74NzG8ra(sz3vtv$%U!KGDblddRywx-kcmPXwDW0#)}0r==Wf6UTC)><&jAM1b_t{6e@eYy$*tS zhyiQF!?;v@>zfXkB{qAuD^*=FC|Wq|;`&K{h6_ktqqyF08;Y~2Y5cSQ+nXZ;RsDo~ z3OD(0*b((LWH1rJX8X&uCfJMHH~3dFuORr-a7)P7@VrJtbN$DDXTzMO=f73)haEp0 zt5>>WTvFhou%41d1@FaO8BT?;`QtGDpkX35JURB~2gd$*U;cD;?={bb*4(0b`E`w9 zs}opO`Us-3OXm+mG#dS(Pa%aD?^8b!j=DFJP%t&kR#{Ut*R zLrlNb3!&pEMbaOM4`{sGy6pG$?9B#VQ;}`)g(NmldxC>W%P!294kgwQTRPkLhx;u5 z#ya*x{=_;(!sw)}eZXNu{^@<@7h6~(28>YqS(hhS;-SExXs5JLI>Z{r@gf;^5mR{p zZNv9+3tx}kXH0sN=a@W$-?`ZCkD`d>JN@pj)|i+U$_64qml$LX7l^4sV8)06BMgd? zM)BYmU0{y9WTJjpSXgk;ykX7i(xrBzk%OWcrd*T^D)hd4xy|j5tu|rdOIs5cn6-)Z zRWJq)%#qi(*ay%mOAvq-!L-A~7tsnB!JSQ-A90M(g^YGTaZ7Tf)JhnFX}o=r$z0cN zUq*w_5oZImgn^j&K-QQaEUfV_Ckj$Qn5qf+Xn_Ebfde5ijSB(}Eu|nEG=VWL<_cke zn1+e4yyKDH?-b%;mq~_$Saipz*_XU#FLnVW>9T|6C8Yp|4H>3Fmf0u^WmZ9^5qBXn zT>{nheqRVC1`R*jiN{nBGrEYhHX-&FBhyt*Owe&G21Ybww#pWe1m!%6R!A|hv;au& zZarZOm*pf&BFbb`GES#N^^)aKrG*Q3R1=2BEq`6sm$*+kyqd;8jrpocMUoknxSMVT zy0{FaIcb#RgSdJHBvu?JoRS|oI2FEIn~pZpBKUaACRZF?ztW&9UWw#4Zt4}mWkR_@ zh)ZcvAVSwiKoY{T5$DGSQ`b{56&Q$O5Tw$xBi5x}TCm!$^|BDXs0A9PTtwq)({?5% z6Q-We)Mb~vWf{6K70L&IAvE|xNzA6vpk&xpeo7QQV@bvmVIgfm)7I;2gF|*-?u7s3 zU-CAqLe@d?V6QzuF0=4yH~7OcL?R=^NiT=vA-~b2B@zfMU>^ZlqX=W9@KrPbh}T@q zICJeg|MR`9Ijz@x7V@8EN*~%Iz8Zp&x;hJ+aWPI@clu$)0#Nc;&}u8QgvaouYct|M zocyQXKW99N#DfGF)an&r{$(6JY+|25fv$n@Ywz^uj9ix%ni;@?;_M!@Dgj0KcPueJ ziueKuz~{ihWFCMWGB2tSz@t^4en*#G8=aS-$SiEN+0%sUc)^8FS%z_Y_Pg*v8Cn|4 z4ITD$x;_+A!4ww2fgXFSy@J7@8S(>7*$1hXT}&bW=5eFhkY~JfnYBU*h)n#&XnIwu z3W}Dgqsu%LVq=pcy##Mz`!YJPVEkZ_8=4!eKxa;v4gOjH69>P2uM2jw z_8KzaZ^s?>98*o;K-!Dj7bPdnmBD1lyooeT2{FT*K9d z3pqes{l=?!U0wwjHFa?Xt012~*|d6!y5Sg$(!t{F7Va2?P+Vd2DB;+)LQ2C zH7th1E{fE68X8`<_%GapfvCILL=3l{Jp7>nisJ_LMxp{0sQD_q!A z%;IS50!H9t1W;lj$t3)oKVpUoHU~!S!GCCu5jH9LQ07BIp@KZ_2FC1^r3)jN#R5z~ zwCq~N;`cIj)bIrp0o8aa8r6j&#U42Y0d^y-Xz>ELd%#u<@xrv2J0CBS6uNI5wgd{8;fql40dc|E6XyAH8_%&SlUjcGsd;c zL}5fR$6gU|0AlheW|RTY8Al*35K-I}6*Cs_#m2;zv;#A4AZDhxq`iX43_TS$vRpB%6|Bxw$&G<;~y1}z+RJKYN#&}EeCyHbtC3%Fr(Ib{?~*^1ZdRo(t zk$C-8v`o}z^|tuJTQ+T^{=Cn&E?Zk6VMC)1C=oy`?*!ym&$X9;;@9~8R)RTxSBESJ zPzUo{?UADO3+)w72w;#_e!4TJWb7 z(^2@o>ouH*!y!!L#qBGu^o!fU{*XJEkkw(uogEMU+AVeUInJdFiaFPC6+&0wX&kI- zR8fV?3ZdNvk;vUBH~mu(HJuCD$AAN~2o*WW=H#1qd1G89q4RQ@8nDG!Rj_mn-{ zM6OJyat%$XjaGvdlVP$eYXE~lk0%2xGkhd5B7u#|>^2@a*kgs$gKI25(#!e7hVYuX z;nszgKs&s!K^W%E3&XRWN$r76B$aRp1{!s8D`_3c_pUHLl3Klz0DxAU42{}=ky7E1 z;>EEn?6;Ke_C+y-u}Q1>fxsOCX3B4}Vv5aN`-`q#IcyINF+hX~4?766|J;6iuuF~z z2_pz21Hmp#XeNh%i3eJ7QY$`spIKunCt@D~p)DghZsUcr7rZie+PwT&W z*w*hB&oUXzQvRVx0MvMJAW&CDjb&QEKq&2w^m}+-Z)2Rp1X>#G7bLNReS|fW5QKpY z#tEAsIbo<^nQDu{+iw4?KoBI3hyj|R@yV0Zwx;B*;cNiE6ogiic_Al0?oc6~JC~e$ z(G{j}O&4Z4SYjeTu?JJTsNCp}&!bMOqFMm}WF)3@6YIRxx#%h&{wBgJx1sF?xQgtYzi>J<>b$sW{CRqdRuv z6-ojcAlEpq-CWv7qOe%@uI1Jx8iIgH zRLCfI!cppCSC>IdK?B{UBQSZF1%y9&tDXONH(!N&o9y1>4~^SMJ#1ot&<_Y&z+`16 zBxkjPQoF_P#RRQIJjvOBa=5@(7q0g;;=K19+hnLk5Ba0s2R+ zxRd})Tr(&f&1$w{qM_bvQ?GS2@JBW#i)FyZh?Y)xuigM%BNO)3PEJKQC%wec!0E!c zZf5F8F<8n<#B4%_?d~YOaNzGaX#z+d?KgFR7?I00#oSE`Y;-8XQ6%lnwic!WlsXAu zftjbv=Bn^1M(mTf*>E_Wm<`maZncsXzlX0$PynjhIeP(GntWQ+RgX5-*|HbHh!M~B zEqEY zY;XFR-@Rw%oa|SpI5Xo3b`4iK(pQ6G=<+xv${q2_#xszSnogdya9VfGoY4J5_vokJ zp4?vTxDy!X@L&R?nOW$h6?d&B1c91cF6>_&`)-jQ74HC<`s zLQ9zEFON6Wh5Er<=}UC}_{c54wD?rMp0RWBXA?oowxZqMnzVLaXr0mY{`bD)z@Z~2 z4(+`>-z99E(YE}3|9s8b8DB-P_5Zm|i`@g!l{KWbN<$Btc{RSid>4g&{wq z#Qe&3OPz>ePRad+gSG?)Q|no1S-SUkjYB9|(=sBmyl4ri48W8XoPH09A*&dmG-BX@ zKZZNKsv%Aa8@ec6?pSP+HUmp-fQ&`6huPj?vtvW`hF-KFx(BV7q2ZBz*eEfyrB$&n+yhk>xMOcyj2=DjPOZm}DCoM}IQTiDuV>Uczq_Rb|1 z-jZ48e$-65v^e5#kfAZ#2i<7;;F^p-1s+dTyG zjEgRF)Yw9Zv!h4mDAF)nrDH=BNK!H8tgee;C91)r|{g}@=+3K zSDSjPfxey=A+5A7bCofbHeSwAF+Xx>OR5Nbp!DQsMD=5dV%4ieRF<#R+NHpiT)?6R z*4K+Nn8rj`k7DEOPNpKMuzCfgTQb57r`#yYiNz0jB+5yq0jC^4&D1U%!s#yzN(N^7 z5|aU$AdR$Gn#Ks%6a^!GrWY5h{9&1R>6dT>y9gi#(w8T|1v*~WMm1yCQO@!MPCr?K+c05uX$f;(J3usI&fBlBNh=E-!DF2~CYFB2Z zTv*<2@8f*$3KLK!z!4Ux0FZ#dJ3tpN0JHK1N3%fyFrl$Lf2Q3xlRNCtg`*FwwjV20X8g{@tjo;xXb%{sl?Y{Y0YWjz8R)0Brk@T7C^L`Ls|xQm$~g%Jb@l0i`AUnH|nO zUx`y$sVa~#a7Qm)5VB9*W_^A>09*DN4lfCaY?c@WyF`T^0`TF) zXqj6sR(9;XNTP%lt(7dqkhd5(8T_U$b|i^wEV8RJ5JHt4k^^JO=%5v+hipu)hi@J? zI}En2fB21O%KXq(Sxkh00N+3$zsgL`#Ddjp;A8*&5NdpJyFKo4#b4Y$=C8ggJo8Wm zI9EUPuWwy`dq=XL0_UVo^$Uki_-nXQ$V`5@!%_JdmD7rR5u()=Ur>|LO?LfMpTV|e_PVPLzZRBkOgPm*4_)qdLv zg0P`2zxx@2JUN}+&NJabe~e3JmHX&&D`tcvii>Ss){CNCL*R(^(fcf)#$1dqgq1gO zfi-$1qZ>%5W1oNl!6QcXh#k}sfDg?kgQ`Q2x15`^SE2U|TRlK{7)+iH^Egp4+RId2 zXdq_)jJ-4d!8MlsDTh$m*_{M~BDwpp-_XJl!s4qWBNCWeR>K;-w5s%gHVwj>xhQpb*X0Cm0E%J3x_(Oy9 zP`@C=1``2{u+e+8&zN{;DgYe#(Dijz0!fkpm`PefrHklq375=P&>RoY83axibNS z3J*PLm0Wm2?`AAeYSS}dx)7RfL;KZ?}Er>{a6s>2b(wqqG zMkYt?v}alD>x!I~hY%gFp7h3JAxSm}kUmDyh*}9q5;2HY|7^2M0P)OGt6f=$U6BCl z6-U#R-Xzdie~xC8H8!-^lGQE2H7{KFX-by7*F{p=c^&j!^FM;9?&hdV82mh(P zraNawST@T(bTb+fr$A$87Xq!X?MPlsrWGGra;LWM#5E2|zm;s7lXc;*7(eRZZ_6z;zqs8VpuCH}Ba*!<1CHaa5Kbg})!~*0mc8qr ztj%R^gE(~JT@+j_JJ)bgFulW3P76MlCayoaYC(%rEdRch_PuZQA}oPou`rPz|7O?N zzuPg{Ihb%NBNv);?H&1>Tf*EaAH15kK79C3!@@gic<^d$Anf^ic=7kc$u|>DF-Agv zxdkkuiVIlmPloZ)(3k9;kNlajhM!yvK|(yxn>D9$ z_R;&SJb<8E>qH_baytAGVVyHkzCP#Q_wid+O4c~SGRd9)P$$K+tA!!`!H-kwhc9lm zC#GuSwYO!e9~ZD2xI-1aR9zBQ#z% ztxO@0I$m@X0O}BI%n7}C!5Vf<#SLmhfT{x~U~<@OEwaZVmnmi#vnoq40*yS1IJE<$ zimtG5g8;q&V$3ZZ5mA82ms!2XeOp)ZHD|hH7A7?9TU}O#0vy4?EWKEp$XiXT0*p;R1drrw-|B!jhEqz(<7UK#8j zw6i+V_2U3wMigCig04CS~WL$#W=L= z6TpFnab_ORi#9HtBl*D*MhCNXKBt-8BLv zW~{L>Z&KH0s~&~hK6J^21>>{*-!|L3A<1=$K`$uPDvyBKSSSE2Qwd=N3oF{qAhQb8 zi>{5!YaQI(zsBA-iGciK2N$T*n!Fc1dXXdr*{zH0IefHW;*`2-`J7}Ib=t8>FR=Vc zW_JJTb-Q6m-GDXxBBMHqGKHNx7KgS*`?*Pmk1$aI0$|k0V^-A+8%)MRt%Gp|j|=7# z;~11M0-SZ&0V=29u*-%d-sP7V=(7L(C1P4Yw8Q6*{YxImBeX2j669x|Gd&0lnh|As zf2iJM+@osE8#_#9ncwsyTH-{J;?;tmXN!RFaou&+ETGP(2`1BJ9^DR*3t1o@fNM;N z4|ETQ;Nkdy>=@Z}`NsrPrwe}&B9JDmd@n4ADFBQRo(e}e%`+PZkM!@`b=Ixcif(%o zap*{9`9~C#raS$Sv|6#=Ux$BXr>;BXYI`DM?$Xwe{;zj6%`E>rBiC?sgUbS+{c2FX zWExd!Arr-Pfzs~$*5s9cmh4VjNh4ISnLju<`X6>peEoPB^YxplP?u|%lV8&wR@K=O zd?o0?t9$-+XsqRzHpd3SQ@7td6GC&mh)^=5nul&-aeizk6P8 zUK5FfKNRzMyA8%=U*K4f(?#q!dba!7|Fm=0uHmznd;x3L!WHlT*uOeBIFqX5@okpN zNXkUWfo8L;R9I0`%SAAr17s+o<w32A`f5kXUFVKOsHR9c(6s* z*tASG2WBCUf2i;igv3VK0~2R=E)M^&(*rnTf3)L>4SJ@ZDa1gRg4t5|l9vg4V#SWC z2q3d6Y~&G16-88ZoTiEgzI@SIuXC_K0q})4ZPf`adfBC=%SP=9T>&FW(Zwf4HIV48P7Lv+-LS@P_j7vYfoax=;&y}vK?Yy zyknOxb|DlMS`ZEXr4S%T;>RhK3w@uF&4%2P$O8x_gcdCzFH%vfU;u;$uIZIl22oO@ z1G-lQU(*hOS@mkS-tW6ZNH?xtQI`&no#SU9UA2(_8R5uJgNQP#SD@Hb8XUMdA`k_N zB_lL@<&gH05zFwSBt+(jTr4ov`ZDb0!Nmt!F_m1m~U{}u~0nVRQ=P$RXTM)andZk@1MzW7hUHxIg5ull8_?l{V?XBUykfao{ z=Tzt)Ny3vgfPkJg!zOhisY{7L8=x-X124bn>#_n!bCj!~v0xYdxn__Iha@tn(o`K=M_L6P(<=a*RR~;gLRc1CvAWjr=CN8WWu~~`sqLj zkNB4hFsp9_AfZ!>GM9MnSYiYmXw~i(q@Sx|LbHjW1+<(S9N(K4*!lK%+e{L9>NOlO z0~jO>%F6kxX21E&v7y0IUs0JFJ^tj;LVupTGoFvComzt88@*F5WCDOZEy#l)SR)E>* z&yWA@kD{We_@c^#O%?Mm>#@7yt03I>!#v6-razzBonsH05&0vtOCtuiQK0cf3C1mDtN zC881~99o$4D&h7N-lvHQGRl%9`cs2O*q_aXr=?I)y@4=d>{2|C0ioy4v|XU8!#FK= zNOW2-nR8|slOTW2eJRTWEW^)xk%=|BaBV^Qq7pSHnOC=z)J4psU|Arfp>Y#K9y=;C zkU^Ig8YW^y$kfro0T7JnU0!d)QNT!|LbwD>DY(Gj=?-i{3O7yhLYxSrP6r&-PSS-S zu&4aCpT(2<3?T;7N(Gn4CMs-*SyY8@126;s@kM*r6}reEi2!9JGHW}m*igX?%xPhy z^x~9ZV#H>?@J4xnE`${-9yVg^DgYH-NJ7)1g_uGDHfe>59SNDGg`+O;__o3%d$fSb zVVIJ^8Y;2|tz*PSoh(Q4#im_grkp?>pw4^>a!UK1z4jTzoz16nwj4E%1ndMTEXgr; zT6XDTgJ6r{$)?`YleQ);L3g_)%z21pG$KShV-xo6MOjb_Amm+vr4eP~tcoLMNe~!+ zN~8tS_GzK@RD=MBGQ08o^@rj}P7Wk*m?=hN%1Z{toH(d7(#+!CG%EL|fuzRHSfo|G zZU!Y|2Gy@#?LT8zwlw&At)_U&#gIXjVi`!^ z)v}wp0)ja9v?3SHv~b2I9!W(}kqaD|03ai=^ytJ?kW!HaB?;)5RQC0R8|I(;!9+L! znv+DjT=ZdPqOy$cX+V%Ec8Tg}zul-IQ6Z)!Il7>2{yiBZW~sf9Y*3M~p2|^@(^!DU zNEaoS3IZhQLO|}|U`NBTTBq=9gUl671kjQZfDBm^G5Ei5yU_r74*JPKTU#MW`2+7$-=&x(Dq6b@nlC6Xlf_9;E_DfW|n$3&wx`w(z;v{M}V&k}orIcCGd4RbX)% zu?!)D2m_TVaFT+_4MOAMBR85g%?yKzvDr}=wuL0>B!E2Xh<)-_`<0x-y(THX+$F~- zwSJ^{NXZ+trsnol0R!Y=A_HW3cC$?~GK`BVM@)nzMzj!2BNot|)nt!uH8&(2O_~kL zchSOij6_W2QRD=EdY>_odE;2vdLj`<1{X}%@rkDO>sq(I&^2D&^9w~72!RuwN&FFf zDyJue*Srj_3GE@=Y0p($Nl*xH4f(<+ygIz|;p=bv&~-U|F{Yf6*G>Qc|MW>jK~xBw zW5p=RyoRd^TqSBcOm+qK3m{r)-Ewv^OzlSD2d+=vT3scU&9?hh;M8mVjl`e1zo`74_3AsL+Kjt*RXf)3Rwr z3?_z{=fUwX?mzj*?o)g1rlSgvcV1^z7V@}|K~7cbQtTs%I%0Goi7v$wlFW&WL#tx^ zz-r6j3tEjyaYE$OB!t8O6&UmR`f&@DE;#(BIB6LamFGXd%O*#_9UErpLglxfNXAG# z{Np#81;zQ`S{sNg-~y)Ngdk?*un(}a(x1%-YO#hS0-9YEnS=*0ya@+*QVV+vj7)_0T70c z-!+l$yqOkojKP?xkjX*0%#gGPFX)U1R zP8<2MEWc_qDslBHT9dqJ5qiT+Wn|2jL$R2mx)i`yl~IArkkhE%2q;zunbj*$lTA>i ze641M3;haV$uUn47j8-x_#2GB@zSWgdIiv8O9ucESFh5D0uj;`Di0y{rp5TQmsHV} zC=JBeD!ULQ!{s1eICPPMpLPeqRV!X=qPw&}bQhU|GNO7VMr9xwxrmAxWSDW6D_45F z@Y!^!m5hd?ODDj7yii6&*Xn|ny!dQfe5nRn8qS#&9~I5DkWH9y&;qs&;!jOW-jcLm&5=Y!DcM{&ffgV(gk>2> zfHoQd$mH|(A}18hsoz6m2sh5RmJH2@WpP{L2(8aNX9T$%f#>&|mb#*Ad12!Oc*K{t z*c2gE1*vY&Df@n+C1YRj*w$q~mQ0Y0Zf=V`4L31qUq?ra`9bW~g=UR7F-UCKzize} zR8@hZ#o*@>)&RR`QJv_e4AP>Dq7XX_=M%ShEp;Y*$RCwgV2IJXxoOB@uC}e!}CSUbCiy$VMdk2}0`5$k#|It#l}9xv_hDry@+DYHC(l*^+-uR z-3n=1mz@fT!Zod4ab6wh;gdHx_U8x2{`0=cQ`LR?a-lUWnitlkzn51Uxv=8?@cz$) z*(+*$@JcUhAFAaCT`MeL%?>LPs(^+t!@osXYN>8?^5}xLHMh@*N5m4-o-o*R;>~aV z*Khn^zx~GZ|2RJCs|%M4Yu@$Kx4-iPxm@n{MV1*kYNRl-)hBPYRVO7%K&X}t{ZCw& zR4+(!=8(1^l;D2+hS1Vz>RNq}E(uOgmypDzjtbE72VIue+?2c^fCbtf&$CLh01my% zvyx#KT_%tuE)KmCV*`p!F=!!)_M5w{NC528g(OkY?s0=`sL1JVzcZ@HFbh*%$j5YV z77EK}2quqWdF1*sCXxs+6&9x#azKMsY|%blkcd%!$O2(^4~BoS!AdEF*^r^@UCXTn zsGGZCyJyzgbS@apGt~u^GZ-4PVhIkgP<PW&t=7Sz>chK) zHOXugu`t;sPAist{uu%-rJBTccH1xat(Cq__5*n%t+z3j>?G!}rOq9x`-{&EaS z0^ErJnsat-TKvG_QP3_n#YBefzkc8D8M7O|WQPS><`xEL<9GMl%SPY`8cw8v7PDkc zCp@l+Uo)er01T6u<(&wDRDePSib_LJ7(gwEocOEOY7==s;)L0t5_6{Dh>6ax!(BFjfk{V(m&_Pu6pT;mo4b6zGjiJlSaJy#4sQhw%G%H;>0U5 zv;??V>sL$QBZF~F#LJdANYb(?n6z;WY6hep%>wO8J$;=8v|2|{7cE_+nOUA=v_*Mx z23^p~^boQTq1Z@ZT>Y?VkMuaJV7^Nzv2%BD`P8bzua%zJuoHqMil3hMuyG|vCxY?h7!YnneMzLY3UHViN5d#EZ z?3}33$iQcVrg!5VZ5uYu{r(HxqeK2^V$I+$$rH?+FVOrbV8WlhD*g9od2rC~@;BdK zS)9$~YI_``d1k}U{O;Ye=9fOWI)T$D=2QvJ&uQ!a$l~gmwBkt8oHWg~tMY?WB?on8 zFidRj8U6Izldqns^1;W3+>Ckoo7%z>zXn&ry!GLMKMFT}qK2g_pa-vxZ%THn$4KZc z_iFap_7&svK=9+ap|Mc<74^(y-#zxLpqjMZU85rhUwz`Uzx~EHK70D)vHazF@G9b_ z`yRgKPW!z^J?KG5Y$Q`9JF+Lq4v>8WtqMsDz$g86_ShfWgZ>U3uAAy*Q`#Z}ny?oD zmZLqpDXEdsRJkD&6E_<`MXm+v3X&|w0bR0bn->x)kOb@yp#|w(v}mb9z$3eKsZ4^9 zI~gPkQ`>0a$-4ZPg(*ew%PvfS9jhpOA&FjQN$OY@6f>>l=|aXTnYlL7fl+&~+NQ#P zLJ_m{^2fq^mb(Rtu`{+5)!}ar)pB&6 z?Q~{~aV)%?Iep;z@Wh^Q-zqDO7PWeW^X&1lQ-xo&9LWJg^f{^6!jrAW&LqFL=i0MQyCfQcBoK#Jl>SAbwj;{;|PG3jQIF{MbN zd@%?q3e!qnfY7Ee1lf-=X{4P(gR~g4x~A~BA4ofz662G}VnSuDlD$)Ui2|I28KezP zX{Q?0H0p@rM{q6$F2_sUt69CO6;sY>6euZH%#?ght5?xQaWLAAALePJqy~Ql8ogjKrh?agyl0XsZmT>SwI+&ieyTFsQ{uWU0Y)de^HxV zULmAK1`=OdsIv=AOX{VTRh&@(@CW&9UssZ^7!|qg;oFRsFi?v?OcK4r_A@kTf$!|K zG>I4aRSfAtfhezhgQNtYAsXwGr?I4qBpbx$W0ubK3Px=R#4A1tlJsw_Pmc1XsssoV z@GGnw+3b0(0HKlsCgVaEVQj*ug^3r@n2IMPVoVFBXuHlD-_n217Cpc6wiSYM4WGlk zq|It6)dzIQKAXb80xrDAR2UJ^&>p?dIGP*?z*w-uA-;R5tzPMMP6#suxuX(A0L!Su zq;9|k-u1|s?zz>~S-q&S!t@F+eB-ii+nocBJy&3Yklip|+A z_PPNT#|;0K!%3ZF16*ovszQ?K#S~24O+}q9v+swD7B2`86MX8hjm(UH1(Hp-N{AL< zf!Oe+sbi#=(u1PVaM+MhDF%mg22BVcr&N{{lvuNI>Gbo(K5Sad-ZD~~L%+dJRNaFn z#t;;|#aZ}c0AbyoW*RvtsOS&_Tr&ee1`(zg_0d@~R^L5;XwT_W-L*bdB#isJ%-ekX z;ReTQR|xv>)P$Qu@GoFoNlb*?Yau_7>{WwYxc`y$4?MI`zh!<&ajEV9P+}UBW|xrF$p*3(gsNUAV-|bb)-|_}G8hJO1Aeg#M})*>kzvtZ?1DaAQL# zHpoeXUpWo^zKGn5b7J7Z+YMs>lWP15hPp1ER$qX zuLcDYpEydLNWxLBBpE0Kie&QxmL>L!%G~{gc3d?5D zZ{78c*iA%$N46fXOHsiNDtM$SvzQ!o!C{Yjj25$qK`m^xo8qS8?})IOcmBivi-i26 z4Bp;lUF_mrGq&1aI)+VJIusI4zp=~yM!*p=IAS=cFh+_!$zbGeKVD3f<`ZFB=0q_7 z0+1JpiBJ2?zVKsfY{i)Aj@fVdYunqms9|DcAZKV8RZKUTS!Ba8mZ{C zaAp@VwZ)Q=hR_zxg7oSYi2RI^l8B25l~ty+BUIy2s32Coku;{E0;Z=`D=x45if8^>KezEHYE-M@k$5H6`YHgQY^v){_t?rl17@nK5JU(p`xX}X=u$dV{HVhjbuYcX$U*>^C9#P{5`tD&LftNlj<7sWH|RVzm>8EX zMo8P{(Mumig2DMSLq2D{*o3Lr7`tXer+vo^dy}_Z!>1vdGVra-vw%R=w zUrKxDb+*PQVH=U#-?+^07J79Hh#5XWQ85swoe6qG%XKkjUi@$n{OE+0k{ofE>_Q-H zr{A#ElrS-cuHvK%4mM{t+BZPE2Esd*g?F#8HDCCM5j>XdT*{d+1JrO74*^5)If7Q4 z34Fa$(K3+XaeD$0&fQDH@qF{WA6&HUwKM(Qg+p5#CPO&x-;bO69mzk)I1<8+gscJA zT?H?0=Yzg4IFR23WY+FaKMwl?`ci0LV}rQ_ci|zlxh0>`z>n%3@?gfC{_uG@uNiN@SOf>ks80 zDK$xvg|o#7FSp``m$mc4^9PKMW)+WeE;MJLSuVWXod8In)EL^89cT;QpFNLXg+qni zsHJR9g^6+HeR`qSFR;RfEN{c{S4UYz}=XK#E*kxJ&SCHA1b z6-EBII>i#|7}3HLVbG-|FunBPDSz>0;@ZDLh@vW}#`>^&PI5tj36P{(z!Ac>>t!Al z(W#;2#}*asz?iwSG5X|CZebZo=n*e$C?OD&F9?b;O$9W5H!KayW|=U1 z@<7WP_VFbSv|?b>EU6dR(v`o%3rSgHLpZiT_GtwfG0DQ(C2?x%qC8t1uOJx^IRcX1 z{u*fH6&ps7QH0WxM_QoK0R+TRB%%R?Xj)|Gg^-cBdXLs0)!$mQYhJGdB z3gW6*z{*VDv}aS8o_bnE^o7JA#7JnTXH8mKy{bH}beCPMP$3(Uz2YvQSL+23Y+Nz<6zGyFX7nhtR6sX z(~1MxRJz5EzSRs9vm8y*9+IdiwTM~gc^B`RJ#?u6C`WV$ucxr2fHm?o0$8AEAQNDR zQ6r=5$kE|HXwp26fSD@XWP{Ks2oFh&AOrK&!mGg%h7X6Suo>{%N_ZqN_%JPJ>lr0~ z1r>wRU{L`?LTyJ0#V#AdyHDHF4~~$rz?iG0Hm-NCur5lF_GbI_9}Fr0WQdUQQWW9> z5CFXdU`B@R2X~Of3%(#MHjhy1FsZSK6YmJyZ1~JW$TecvLA_@r9PCME;MiGP$GO42 zhhd6?W8rH%>?I4Rs91!+vOsvK%N^{3t?h6rpzaBj#HLRip&&`n^# z5IFQ4ZlI$H5FQw{SL63G8y(3D&H%yS4hsNN*w<;J003cYegI7B^|48tZABtojDKKt z5|R%3;gN=4{)NS_zIv?p#I*0;cX{ipk3WR?!F_Q%IOFebuOawZ^IJalJ9oFVHQGr# zU8GAQ&M}#aLJLG=M&+Lnbo9tn5<>)Kw{J~j}Z`t=%K)?N<%xkDunOI@yER!9{Y=a8EW zW25$~uO%?qJN(Mt-+uZp{la>PDNUy>0NPb^%p-$E01N2G z*}w=xvx}nFX;z~6`%*2Zdve!jLB`?JZqe;nuZWO=RyoIv!DIuE++$w|fJ40`FL-=P zHGta}vFDTp|D=xvj*2VXv5WE7j@hsDn6AH_lYwwts7V+|W4$dsQ_4z3^#fp+l8jNv zMKYhmc5iIwsj%f(63q54>(U%jXmJn)oT7z1glZU|$=5tUBY=GoY=D+s4C?k9l8Q$| zRvEAFOdtkd?&yU^m|hU=CovaJMK&5`EK{Pc$~_C|h2K8>iAK@2H&L1e_zGHEeE--Ff1 zgX*EDaQ)rcZ8<55aZ%GnpIpc(2R~j}$bSsBp)R@bNhRHa^zwX+Cvz#mVE15>DnJxN zk9bHn02$o9ebSyHMu1(q5aY^D4D7NgPM8P)ats$`db#M62$g0q7IpQ6GuVA9dCW>( zQa?G6grs@}MNF|FfY{a(c4Dv#nFS;Kreiy zH@UDd+ycIAmB%YQB3Bv8TxrxDGi_4 zx2l13lBWM=wrLMxN4GHnv=+S7MQy56 zm)<)T+Y2UU%&4E&_{^s7em8_0Tzne9A~p4V`w93TDln*NyttinYvWb$cCa!2h^s01 zTwU%Xzj4Qso6dV_7iW%6r0F5*R%7Hs99_djMCF67-s{@^4eM1SV^EhkA#pd6AOB|8 z*zfO{>@5B6rz~=zIoIBizquvMt@6REg?H5O;8oAwaNwEn;_rvveF^76(4RY8asexs zt1q*F73xFNK<;?5lfmh+XZHNj;KZq9_sU`6;-yP}>{Dm+tsGD#8uA)osL*5_5)+`e zy~PAZ#_hppQsL5B;mg}Ss}>$pVoOx~#LyWcCNwD(Nz!ZlLtXA9hQa<3`)Z*rXLv#K z&<*wm&xLK4UirgyReU~Z$g)4R9DpVO_y94~mAh3oU>`^S^bUJmok1b2vrLi*z(JQ? zN+5~~jU^Ta$kK~J`Tec;S(!?-bf_MHprZTaZQ=D}_N_O~1GdGu3pR#8yL5K=CgtKX6Vueu!td-cF9gY9vPtlIQi>1XX2vO~ zHY`a70+PDL*Us=xFY0!n3rlkBa#$4r?kreeP?JrJNRd&?ASwu8R=^Zpsx~EXcWT8) zK!#p1X%WUgh$^&fi4i}(HtNE66ju0SXcB;rJB%=as^4hO{So0_o06+lyG`P5G7AUIBaRj>gSr(j`FDL1BIkDDR~rNVoHzW0u%Q4dIF}AM0K#&9!DY= zjRoO+-rfanOK+Yz@Q9Ws9Lvhb?C$6`J4AvCyFdXbHa4t5(?v)<)hizc6y3N3`0C0Hs%Gy^c zyL{$3TRlMl`zkB|MlfY(QO;JldPd9?yLjKY%$9Gc4ymY^G30;i346|NZi@+Ehi!Pm z&MrJswu*^y3|f>PEx@38DSUSTJ9rn6hyT`z@bQ~XCV3bN8DBob2BY}ot@b>>)X~K_ zgCeRKO;#%;GvMbZ6(j~Rez(8*OE=Ttn-@P=sshoKUwYA{3m15o{2Uj?6k%-ZGHcLO z+YkC{BLKQ=_+^)kASEA7VfkpkRg;z_Ne zvl@tWUo5h^1!?eKee*H z@w^}X<(w%8P9F6};u?N9Q2A)8H>Q>8A${hs|R$`T>70)`xq4Kiu+*wLN%sa%=eJFNdQqUg(2Y5z|`0Y8=j;nzWVZV&q1{z+qdV zzQS0#>c)qD_Lur6T9lZ`k`bglu7*lgq@jfs~SIbYBr9CkaPMkHfsv?Mo z;WRZQC7JRyEdnYbs1vANF@j$$X(2!_n`$9>B=7I_TRDj>f6?`!2!r)4KSR?{F{R~vK`fCK0q_>D@v+1clxis096;5qS9dd?fVjI zXw!v#v=}ckDkc@YjvzEZfPv78LoePDST)z~S+dLM5P*YatERjbB9gi~yRl6d9Qd+^ zE~XR@G?I}62mmA4gm~?kO)oVS0^$Q1ClX{N4>7;ho-m@z_#Q3R7#aiulX)w6AWjY1 zPctJg`w9scL=MDc612dFHT(hyHaL>801Bs&b~TW63sb_#=zNGq*yK(e5kLn85WhGe z%Fqj8#{Ee~bqVSMj1EFE3(*uZ3ML8*GaAqBLMV6z$*#20f1?3e(3XL^(g85-r#-65 zh*8l(r{hfPiphsM%>)F=r(k$I%Lkk)k;!LYGtYUtd7oYc037NG{ho;ApYp{K0o}#3&b>Sf6{QW?1PYQ(93=*Y!92`tvR?>B zo?TiE)3RD2sc2U=qYE~!dRoN6pzPCyq}GpQU%EZFIt*joFYjbfEW86-$IsEiwng3Uvgg#Ma_d=OolofBozueLkz2=6{+??iy3u>fq^{J09es>$GB4a;*|&e^mw z%Wq3Uul2%_U1%iV@~@_$MKM9E3d0Ju!kQYw4gOXO+~F4^3|xcNz#(i<{yj>qURimq zI@n)Jp_1$6!)BQcgHpi`WuK0e;(>_(HdoHEp9laq%})j^Yd>>yXl*hAXt2AwZP|zu z8|M9MOiO$n&;>K?7knU0z$XeZ!s7_Sp{8V)2#Eo|;Anqn^o3vssL>&YH6VEuk<=s* zBQBla z1aPA5_P1ZviuG5)i`&65fAM?`$kiR382$I7M_xX9VCT`dUpltql@oim(o!wZ71I`}O=356B=0SPPph=0 z9H+DuEx-{UI+{XQUBeT0+Xl!id@b?R`>zHsZnO7HbGkr3dY_#eh$$y3655Zgx4-|l zaP8zGKFi*EfjS>F{`@}HZ>w)Vv))?8Ks`npW%i*721B|vbuOgBPq@(az7>|26&UIe zY$oRQ6)lPkR)2KB0ThkIsoLP|s8x(83Z_!1uZRIY7Ub^64u9Ike)P!>6+rm} zM>3!C*N=`)T0xElBB5o|Ukx@hUSEl$T?(ziF^kaV%DVvN6SLQ$-}<16^FNn31e869 z-R<8qK$3b-@-H0H^0`jzJhgpoZWow#47*7@0uAfV!cx=e(s&o~J(wL39Fh;BaqJpEl z4KiitL+S<^n+?;KkQnmy9cBkYt5-}WglZrp@dY8jSk}+eP}tFhyZcrpVW5Q<1gPYY z#~tznu&pcHx!7a?t(?*#Pc(>O1dblU2jA_qhn}EU&$aKQVRP`@kB@ z#6Sx@L<{J}G68(j-&E>voY+WW!Ok~d zaM)}0b9eVTG zgRg(*z-v!+Zhm6#%U|F5+~fPd|INOWhfeI;G}wExv88oPk3dvH3H@gV%>6 z{q{#Y*5D|Mp#@YnunSagp!m=4wBiu&E9O{^LtdG&E@zfG4@sbKR?bZn&6hv@gav?2 z(k0>Hf_A^J7LuU9yghmCfL;b1S3)x2MYvn=Mt;Cn1n|>5gXs<1VyD_ zS~&Ir@tbh$5+215oLd&z@77_K2$7)xFqL6@3p>W2*b{!P$6b(aFh%JYirB z0e~-Q5r$4hPPW&M6_yL#K9cfC8;xkTfp(dk`K{-i;zBAwGsCWmc0AInG2X>#@9N;7 zFL5d=IAR{|vv0)#xzqzh9f8#fj^*_2Ybt)Pco7*REVgkqXsg3kluw5 z9mV+2X^M&wWg^marYj;c5s0I>bdWn*#lsx+@G03A>I$d|#iSk$7G zSuQt`j!3ISL`5OG6XHd)7-<VgmrFJ?hEr(38TGB*)&(EM*N-PZmx~=GTDpMV0`~0w zaJV;7$1I>l0Q*D;2nJm?_+~$6$AY?0Vzg`|Yy^5ol5gY@n4Ws0rEaseEnoORoffn3 zIj=09A^X7{C349K$jB)hT~q^fQC2cPcP>ukkyO6G!wSOohRDi%q5x8JL(l?RRM7k^S}_dyfXC29dyV|K?PWD5CPpAzQBH?xeOsBiG!9)iFK99(NcPytl=Ox;5__`5KCq(F&Z7N}x%$ZYeq zV9!x85a1o6+9IP>Md`s0za?#b4$IES|SJIK#pS+5r3{`gNj$0}j` z;Gx%F++ORek!Smt2dDS&+C>Cm^O30ai%*{q;RQeX`Fwsd*(N7Phx&(lPxc(&cj(P$ z_HTJ=_e)>h^}?68J@r@HzWrCU{d=B2-Q9Ki(2kbYIg^tUljCFcjZOa0)-_z!&{eB? zNVuiJ-uak8Ia84-ff8cFlVg8=VC+Bd%b%+5HEp@jnp-q4e^b*`*QK^)Vev+L@TzrD z!l?$GPlnBZ6t?_H7&@77E-uO~V9f{}&33gcja;Z33unjeWqc)&4-=i?`DEt`;ens{ z#QpcZPwCzcv1c*ewjtw?m3E&BKX;2IYqAf}0ux<`(URB5Q0TIYI=PILcXrg6x(lVc zLQ5)-`RgfTB149})>v=8;v@fbDFq+xGf8Dbikz6fEjG`R=F7_u01-j%zF-B?8q2wo z(I_ax5SBnROKNWQpG8(mRs8VT#wTq3OFvL!m5h6#_V~MPVRa!RQ=-63rptS&tmF<3 zl?HVPzOY7XT!%OvUV=gsl0`DlUi*!Aq$nVFo-(u8ynBh2TXOWF>rFswQtT2uTDZWk zY}4D`VgeY1gZ;#{J7pIpGE^*3aMZ=FjtIvW87#A_;+8RL4y|$>3UN8jEpGC*SykdAH9b1ZEMSmt7fz1K5>ig&{sn*&(XCmfGJAQI}ri znV%Rfpch31=u)xA#&ppOQ*#9nV_Yac?eE2-Y!h$< zx}oEo3kdMeY6x|C3<%M%&&zWSkXlUGST2e@Hbim!q zEEslCXA~lUR$H+AR+nYRwZ!k=WYi&}JYu3S&W5}mp3+F~SYmeWSe*R8Uu&bSF$qYT zasP&3K5qw$c(93>;)f%f4CGTAEpiO%#rY7-w(htNj+ubwjT##7%pL-aUE@L#Yx7&J zw0hw{G8P2@L5mhye_6YQidpp-W>8NZvp>zkEIS@QU>NbS*ZPPHDdB2(aXZ)_!oh^B7H@>`SQSrfN_>m*FD&K^60*|J2!oE_lsZM^U_zhKmFg|e)eyMd%L?2Y&(5;S95Fo#Q50w=M4;Ht6odr&&nX9D{E<++ST=kOPLwThsiBHBcFbI^3^l8 zWLgCpax>=TZ)yuma+M!%BwPKP@Xp^1Yd&1tcLw)-J-qyxaJKZLP8SE|7O+~Zt|_~K z1#%%bWEbQTm>X8S5gwnkdj?kqxw^WWKky6fEAQYalS%;j%LvcHT>9a&k&<9?4$!iD z^8zE@_Ju-{(SbAetGjSiU7&~ztyNHkGKQ2lJZ_Jlu*+t(U*T>L=?}>c|KU8Tn|vYP zA!9N48^ob;dZ5=IN-8`U_2_-ZC%4+7jIT22$thI}GRVfRsu$Y&I;&FX;>2dNI6YuJ zy#+x?R5GeeD7sdAAuV@eLQ@xMfEJ!FY+2Px9`}1@*f(LH1d5ymttMjv&S%u1^K|&7 zyOMcOU1TBoLseL#UgcO|P5j;wD`V(V9TKxLqeh*5RB!>!h6~Kvr+C<0b97JvbYX#( zE}($zDEnbzW++G~`H0yZ`J6oHW&trv3(39_n{a9{-Pl%%3y{~41CMPzdW)aoswtQYL`|z#fWR&!yz>^Yx(4jLygaz@Ijzz{WwCS@ zlJSEb5!E5$=d&8WbXQuYg1ZW)w0d>SpZ4NHnU*Xb2v@C0D-6kqR#)7W3<6Px z%PW7nbCpyV(>^VPgsiE*^CiZn-L__X zY#0GCS^+?dQ*>9(vB!C^MzmN!Os3>TqgH`M zfr6H9pv6>54x@UIS*`xydgm#-p(ks&h-OvsAb+UWZgat8myJsYEf#3)NB8{Z61|on zeOZK3&!Q@`5Od!ahuT#TW##p)#Ti|$e0LVhrwb{Gp1F%lY>_6;q()?*yWxs zv_5*1-BZF2qgL66Bw;fT<~?WQ(kDUz15FF@z{Ja+`nM60frACQ@S@rg;02p72?Gnn zk1wh{>SMdzH*CL|_|@&^6iHg3dB8HQ#nWNT?OkRZFM#17i~t_7snKaQN};ZvpMPWfcfT|-o_P+UKZKX~0EP1JXQ>D#^@P6+YmU$N4+`}7QY}BO zW?+DKa`{R2t|a<=HLAK*fdr`hyV~DXe;!5#dWL#W4xB!A@QtVUz50*4U;N6Rm%g_B z>CbQb_UE=e^|`J+Th5+3+IwPub8GwR*`qxp4R!U`*0U}?^r5a5)6*&kSyQ{P&ECTp zcL8bc^ojAYKiN0_-}dMGs#|u+!ZOg*Oe-m!_Xl>sa?0hmj|2tv- z6Upu+0$1_=a5&jHAI(!OU@>t27Ga4GbzNxa3rA1Z__n)?h`CGF{`9~2f6bh;fV844 zrc_0)v9w+&oX9>(JxW+_9Jke|5kLNPZSWG#ggqmpjuELKulGQ~Bs+tpvuvS3IhO3mf?^>xN% z6c%>D8XHrAM+lg9d({g$fNLV8oOt-Qq@IMs*+UE9>ls-_ibc6*!_+zCEH)K;y2Hn& zQODT^7^Dcn0yuis0)wWDcoaFUB=IE|2w;|A=u(NRR(wXycK0%S(byb+a~4d@>Ilk_ zCP6X7VIw4~-%@`&K&uQ(;*3Kptg8-;*`r~yL$A)3UaG{x({KPLmXRT(v{<*l1Ky4K z+3t?uYXh_hGs*l3aFc&ISTE^Uc-RDlC;NO55N5-Oh6!YcUVRW~#Sx%iI%Kaw1jutZ z;K*_mgQiO^fcVf{fuZS&t58)N(Z~PZNWmQ_J-cd=(_}QR^F8NWZ z2^G|fyQZAmasrXL>r@yXOD+eT*(5W-^>=pjdAeyMqG=I85kyH1UwjDdvNhJ*1=J*c znZSs`3Rk=mv5{$jQiUu?QYt`VY*7U$9{}xw7)A_0OsqBepT2IQ%=Pj}6t(Fu7R0#k_M1m1il#Pt(B^ow0YUFKl`~f1M zDTS$CiW2bwDoDcIe=o^4U4!$KVzx0hwWK^~c8q<#mIV4&GF1Z3#d@S)-8SR)Wug5a2uGhyp- zBN!}9&CKCGdsm%B?k}Xr6lExqCMG+6Qx}2>=^wGDpEez~T|h8|XUBbpoeAh7xxLF) zuT-ovQ3%-lq(G5}3&n;CAaXvZ)K6*e&}PkT21>%mD(IJUP0jDaXdv?G0 zy{G<1ezN!h98Rg1am$IEFlt7(f1|J?bq%}_gk8y_7U z9XdNXIo{CNlKfre;I!1$;2N$@aDa4SiGyUL}{&u!L?+g8tET*na$IFi-;mzzpD0ae``B6Azmo$ z7%6rv62AsRW6cKcw-u?2P$BJF8Rm%_zAf>1(S;*=F^>8tec?&!FxC6k39CT9zSCaF z04PgTGca#Y?(rXAn`ve-uBk(S*^mH|a0nG&SpL9jdr>_zp!wIKK^_6Tubdm+QTTFz zP4F{$I z7%eP0p$jyD93#a82nnY{W7bL%r%_D709v*k-S(?WqCqqZq8Lcq2WSx$fEl~e0??Yb z=uXUHNg;BuMhs~I4WU>CNez%L#Smr0i35p9%S1?07PKpy+TG95@uf8Q@$cV~h>9TN zC9`|6aM8`iNvH{zXmpncCB8yf>nvP`@hhZ(G5dS90;YHTDkE(Wy4*)G&8!>-y%^yy z=UL}3A(cesUQpQ)y;{8j(DBo&ubaYAFd{N7h+Ps^o7n|X9;6A>nO3?=Z|t(+TBUcNgb+YEDq-=Wfm04X-k6|l7D ziy-V;0KUo-<5zj1tPh{$)YhCV)x&@3kUbX$!AAO*7;3W$zl(=FrHsl5Ks-R%UBRft zF{gUH=#Z_P+H1)DHX1E_WFRzHB)bOeNhrdDmMJr`%-$-6BmSQG;#NyP_H?($Pr(F% z80F5F{rM(rzIr&Z03R?306vo=cbKA=;5(R>Gghj?*8@;QJ~0_Sa-#`AZ$4tCh?ZT( zi+$=#2nWG4q4Ps!h{N49Sf4vAQ3YcEH zmN6xa*TerM;a<^BX3^EDAvZ}D~-pfKzYVc}Kq zYV#>^Y+5gFr*gmPDi|H7#x8&AttLDZ!k1k4k|Uqb51&0_>skBW*z^5w?|t8(; z-u|8czW3#?^>!WX+Os7;IayaC+nqhtU3 z?(r`k4ntM_wm>ddH``YD*Vc!McV+!|2H*Kx;g(+vjpsj8P#K+1hVT3@;pmIW?xhAP zVkGB(B4>~bEn#kxebu@W>O#|CnUD6Yuy}j;hcWxzhAV(O@BPUC{pUaUozCP96Qm>v zWid9=78N7ehjc?mV-ue|$SFrlk#NGl$;B>E=0Y9JZSiFhPm$CE%FEYg)$J>5n5ByuMh5#TMIaESqJwGhf>7haECgnu2@)D>DwAx|yu6WpKmgc5 z1~9$nn1ydbXjj(E{!n=HM0mIVq$9nE(Tf(s^wQq9$_jhL7)bO2{iHwUkFc1;AT4$m z`_D}(1~}@)qrS|c4GY^(Cgrpy0S?}2MW7-;Giv_M)tlF~tdCzAQ446XgRk3tITD~q zFLpGv4CF;EVniD`(I(iR`jvc~Mt%k|QnD9jhR;-BDuaAJ`K3h(7h6m3;_js1PmN4P zE=V_8pp9LCT3W(ve9F>YX(Ip&OlX=@6J$;A$iGr3cTj$aQDOn>Dx3m^RC)CZl#5&! z){BCYb~4P$zUXDsH$^K;3Ym{t2I`I1?lh#ObDFH6OXTsh_T}wp5JCdF=>&nncrle2 zt@!EPG}622XhpyQT*Z_jbZx~c9B6v2Yo<9xo{Mk1*pcZGQ!o;fJYurUE>~Ly9(x() z-TKa2$_r)e+l3nr3I=}MFa=Xy6aX1lULqecpa|6^MIaT7voS!?l2ijE*g#du^s}8}?SXejTRC}*oU9aIp5<9XF z^i@i9iNWjxzfrELWq)t7#;1q5R9z7j+u6dCYqncB5YQ+R8@mhJ{PtJjhYO>9WE85L zB1yT1S(`WimchArmYGL(0UUY(_+r5F1YohJv98^VZD@@h7utK&uWOc-qNsaAD^ zceB4y-a;zW`p_6h1&^xl>5>59QHQPdr`1qLQChSZ+;TK*SQ5VSrOzMV^?fr`8j~Ts zDtU1`=ni39LRO1oA^brIg^S(gL_VKS zUZ>SP)O)h8>)_$HorZ z<@gr`YufWCzuYzU`#UB(t6OBrh2~s)M}9q5z9}BO%7yoSI^6jyp>=T$UmNV%8(#fC z!&`spe=%P!jFN3~rM{(`3k-A0*V5OArlD}Mj{@~nLDB*No(Xz2i4nj=%PW%@SS_=n7Wx8dvk{iRv{8uQXOZI&Caw`cCfJRs; zOxuE1`=%=L>XM`WQF~~bWUA-8i3eQ>lH=^=@c6bQ2I5J_2~EaC_~r%nqdE`yk1C>q zK{71RAMNZi@3eR%_-8t;3tvd0-sxXDBSNep3yLa<{s#%WVQhI9`h7Z6s|<&vM5VRvN0)X{p+3VUNL zAcif+tj3dNx-f441q%bMWXLY5_~B63iK*e}HKv+jj6ma83q8b-7EIAGW+bHopA8v$ z;cMQ^g1^ec_^E34?3nSf01XHgF9c6{gA4tHA52ty1f~VB1~fF(i2-2_;!8%6Lui+A#V}~Oq^TtmQU{^*&DO?M8Q+@M!}hnBuQIkRHP2#1(ORWpQO*;(nPA@Wt{~F6_+5TQV0i z#`k2sIKcK&~<_N zf#A|9(4}ix>|*2EN&*mKY!Gw-baj1KujDoDvIek5m#*N6`WgELH(l|8(iJr7BnDZF zDM7l?3o_E86@dVNulZCMF(k$R^uBYCN&y78?Cw5orGxG`!6)mGuw(E5b=jnRQH~++ z0+__6uoauMXc0q38z3(#`!cS(mzqInnZ;=Y!Z$52UE~2W5VtO}Ly3KMX)(^cAt@Jd zu%NuF$w7>+<^~%t%5HNcLj9)wwDheC|Y745&+bApw*6?M44cpN{ z9!b>MRkZlRAWR~pu=v1gTcKmrkH3|y`=CyE$RLRe+`(i+#k#10d=xYYu**gT3?A^C zz53n(Wherw)HRdr%QAlHg}~i6b|r&V7wVdMHpap*VJ4I8XDQa0A1p7MZO^`;sN#FG z8`RTYh@pZh2qa~YatZ+mglX-2oxa!y;M}!n{7YZ>Lf3vO)(W65+T-7?yUTvPeLCP| z?F#m5@zYbk)R^9j+ZoGM^5S+P-|Sx@tR4vep}+C{>OkIp{cvn}V5qPAOP-u&*q zP2br0!r$$EjE~wPdkM@6i?)WpPj)UN<}6zAu}41Llk2dgqrADy z))AC|*x&$KXbG&FZ|h3xRhN2EBoDHk97qTWDEG0w>8n@bkY_EYdkQAUQO0a1w>T6D zXs)*|V5Wwx%3anT7Pi{YvF-5};$sZ~PEjy1nJnvk+kb|LWctcsd${Q{&w15RSizx1 zfCOqW`&}X+LcV~4m!ynCuU}v{3pr__sU?tyuuu!DDoDbIL&*URpB68#ANPBQekb_v zebEiYIMo0xm}W|kEX}nC^B2stsHh?c9ze@1f1I~AdI0{2`cK@HEQeWHRVY{3ROz8k zU@D(59f-VxK8OSi|WryzHE5d2o%@|AI~l*-;D_fytoIm1|B**;FrLv~UPj!N6ey@YVA9 z0y3EH6`i&wro@`+lOWjz(n=FRblNOIVhw1QfyXW((rK-XfVx0jy~+Rqg<1`wwMc6h zS`%IqqP4Us5Q&Ji-z$PE9>68S#azIuevWFI%ByGU%Y~s&sSsr#N=9DIa3oMp5`Kjb zTUsj;^-*0N`s0P0T?i{SK?)>~zRYdg3;MpRcxUOF)%Y0X6n%b zrpqSKjSCDxOhT0|pjXW0bBxBjIDnXS)Xa6^Ni+;H%z;reqWMGsQ#u?-%H0z?M|XVp zZ%!SW@-@Kx{dvg0VYeZK!prb$1AkH<=a~4f6JAxA_KVx^^5I-@aA@AFJ^shJR9x1l zP-;UyKRGcv*w^2E`0%zD4!ri%?iatZ{h7ah>)U_1@6~Vg9@}^J^s)NJW~8hQ`CRVm z|AOk}L-FcWN$A2-0TPH}x+V*e;8G)6*PL^l@%+ip9U1+D-T7mlHK#-_%*b_g%O74ZW=W&R+>Fsrq3`T#*N_BFa92RrLZ^;Z^P>&=O`H2rmzW((l;1ba>Z0-%-Ee zmm3AANmJgi2bQQUd4z$9+0)#vQ_;92Mut{um$sq9{ymXe8 z&C)dif4)p&?AlCvVS~rR{ux@~KMX@)$d65!aSew}MgXni0g8dKKwwa&028ZG;3DDp z`G%x`Ry2+^JR(3Z91-v*^Y&nSYh$=^zAfR&7mzQ_jD#V8IJBB)B24$9OE3xmzL-EZ z!QDP}KrJp59=piL@oMZZ9ZJ5U7{3-5=LF~j6`^S58ZbFJyJ8q>OColqV;0i8u@^!y z1KNyA8d6cqpK^=*N~ztb#V*JgkxZJ1=qxIldTZp|V@*&sfJYIm(y`n`#ei~_}WQsY@ z?YE>JA$MGeqgh?70koi0R}6FkV$$s&2}kIvi7WA-V^XoS`lS5GCaTJkx294a=06T+q4J>#~j=E#l~k z&n{V?Ngy!h2bzz7gcr9Nfdyz~O3@g3aIF;?1h&RkVc-x9yU{F&K{6cG6R6XYc&ujf zF028vi{+TYtxIGGh9IfWZ_@81YPrpqY)HMTZcxIvKY}B5Y!V@2v{Vo5 z(hE&5lZh!oyDuLO*R@+D9JQl;wsiPw?=&q<3$dY2Y?vi-4gp?l_=UUK(D5h0w8^_2 zJ3q3+<3{B3lY5`|lkVeHzPY^1zh`$F6;Z;d4HLeG)yf~C3FFFxkBc5Is})zti`xOg z?@P#Pu``4}_9ew17>th$jSTdTobBoBI&k={=lrekU)lHlZ=E@^>)6g$bGf>S@v(-c zmRw!#M`1CGe5xODyW-%&QcO(l=pX&P9TU%V+n?H1pgz|;H-E$Iu)Ho*e9c5IZ2XPz z&fl!e&^}oTMGqAAwkPNkP7@*SD=MCVxvncR7bnI?bm+btY|m+C9~}H2oM0RsEAF9 zHwfav0*Weupz(O;5_|U+jt0i8IGOZ2tSYOp%tS$;$UjXZy9g5iy@&ejKv=Y1wnbrF zunPzVFL)PigYwlqK;eNAqC%HxL7ol8Pu!9$B*DZQGB#<2uQ39KgA25@p#solh7|+h zAw!oA!AZ1mLUmWn*`>%dp{#4d$`4RtMr&91GI#bV6fXqd-xn*c3dy06XF* ziat7YMQ~kRHrr@a=!GLiHU$75f|gJ@H`qY{?>G9d4ax}Pr5DHo8<2M(%d$ze+37Qe zj0D6NfV`+)}#r!bDaR`)<{v~{ipVoIFVj~D zD~zP_>eaMS(p@zZT27{zA35n$Gcv336;Ui4cQgEqWQAP_Z2&&kR(_)v5$5vLPt(Zm-u*ngvrMq%d*36oPP*i z-z6hJU*F+N5`|17Ku3=-wTn8}WlK?>4lyOa62O9t&VqiM3}*z(oMF)5}53~XSs1ShdAPj#O}&DcF%;;nFv4SWwDM09wV)#95^__ z6zz;g+trlf2&m3xmlh07jASOCv-nic~=hL(Y#!iK!*>M(ho%D9M} zNE8oU?7n}s8Ij+Q-)zPy#P!>PgoG~qII&u2Ll?-GtVu>^BU%!mT{=6V8^`;e_~M@o z^>y1$RiJcz#g}guB#hcH>1$Zc{`M$>y5cw%!uEu$0ZV)au7-t@f!bf)H5kIb510LU zjf;!?ZVA7QUFT`4xT_gthPd=7XUNPOf+<#7?@O2ncO z00#@G{Orw^%E@j@h|1$ifAeSB!Ur;h)p;iME6>-5yOx+1gwi(~S^#zIGs#q&)I7ML zAX&d4tepp zvoQsY3&LPR=q2c<_gNi?BeFl`83ZFjOTG9Np&CgoOq@2N}dc+;X=^?8F2S$?-p+Xm85Jvmj4TMx65VH!_r&p4P`)vMU z8pkIe;6;&OQyjH+^7u>=t>WP*uzR34ynj`Ax-(IsR|PFJGMJ)mZL**0!>M)=0LTy( zrp%rH*@Au12(Wv+KjCOW4PhxFP*-P*IJiKJ7E?^xC{_1-vyYa%#9jra36T-mVUpn> z22g=uW4yG7dTpgrG?1@oN|I+slXpo&^CtjZ$aAVknFyjqmjegVeA$l%nZtB1(C(4G z=0qDx|6@VgRIp^AE ztomT4?e_vPG5?)5h~JEf(oCv-m@vfqM7% zocqv5cR~y%6GjvkKcfYr82JpkzRvP}8oIyuEqjr>7w1>Kg%K@01%EbOH3AU$^{qtx^{a@kxbDm<7%T>HgSLm z6bW7evY)xuKub#m*`?jzIU2pk#6~ZW>-g+wV$%iKM8qV-t`L1OQTj;~I1-atT1?@N zWJVSwNC*awnAJ&MNCEhS*V$vj%DbAJH&2AgNy{i$fUiW$;R6<-7c2N$J=gxi#wG$v zW5j?XWC%bml&awMo z3;`RX|F+;AOYFHkAPa;_4Ah_mfDz7Tj03#WixExN-FmH9>T7+_cDxF+heypYArT>! zFf%tyw|TbQm=FYB{D!=vxGW8g*cICmk2E?p9f^%uxWH~WRrl=eQIB#j`9Ec z{jljs_``?HvJ|mn!TY^v9W{&G>vdXWaDf=DmYLu%aH??ZAjt+3Yim1_s1Sgu0-CX) zC3gS;bY+cA!Z8La`8_ZFkGG%s!oE%4II-uo z{hOaSy!E-prWq5X!_6~i=lCn+3ZHTp8Lf@>ZRjeY>sUmZE{Spi<@l#w=o$I++mo-J z@nyrQ(U6-lFMm^8SdyD^EjSlG^#7~vjaPc`>gCUb(b5Y?mlc&4uo7x2G}^ZaeIcg^ z^`YssUDq$`GiEp4_wf7P^G@X>G7G6q1REed(M5)`C5%F029${eRF=%L4cZhh zofvFR+#NBiCLsp2#wTsb3`IDYMFxZHB4&z(l2V&|0b76c;h=(;$hJd6qCRp zl8}6DYe8!v3?CXYY!I_sl0Q{&q`B3ZgKPczd4$f8{*k0t!)dIyXVV~v2OtlhcqFXz zjo1dq!@}0YvNj2G@*^Op(U)}NF7`zugDGg#nE{4|t{`<(nMyNWi=)GjJ?NjF9n#WHpTDV9W(Pg~%8OUoDA)NV;%C7G3xmKR;j;V;%Xg>M?w zYX71jt(y^K!S zk`stmfoLjCXcG40N?(|FDzuRMUQL>1Bj5v z$T174UqXyoGm5d>>&q5W(U76h1qw{U0CmNnbf{rUQZ|vN1(FBPcAh&Wl*XhbF|rOS zm?96+r66Hi%w8N&2@xK>l1K3=e>ACk(3pUhzZ7WEg&)Nr*yV5lXiNE&YrfF)h_B{A zP^oP4C+f9WgAv6!yV(ZFE+Ii^0SI{suxqgujx0GIP^k3`+q3atuD?}{F2tyuKJ#2M zs}u@2VnQbJRvxLSLJVQ%5IYawW}_oWahA@qGmY?UHoQ2~%&4jmpL&s2>V%35WKg%` zQYa0PgvK2dc3jh3Q<$#QQ(a#55~Pkb8zW`rY1t?>C`v}g!!O(({&I`GcS8mhIgzlT z%Pti#rL9>QwuL;HsLPkuq%$cm*NO7G z-o4!JYO>4NC-ZjqSQCOgy^0VOn8|_g77o2=9XoM)&oh6~+gs}!ltz6Kgc07D45nuA z$>A@O1=Cf(a7^#T?T!B8c0aUN7<}~ihBAN9md7h0{6+0+c0WiM9~~YWK701mkrTT& zAK$g<$o3a^zVP>Nefx9AcD{OO>vK<rKG-Ep2muIKF9E{puCq0x~6ZHA~ZJPC4ZJ z#>f6--`Jn+5B=3W83d`n&8yl+mDCNR?f47F!D=l5U`1@D>!K;Xo&{ysi?6br2 zgqjMukQ)!fV`Ubs>qF!2@KmyM=~36v`1AkdpWb!Htz==Uwf2_eQ~~7E<+JT=;&j0w z*O2<55n$tTj{r$f^_bEqOm-Y%8|vsvoAbZxJiSSB}c#M1!N6L)FFV?2L6gk zEn`7gG2oR38GQL1qw#_@LL%+L2S}3c$)2*NIt?bmTTl4YE?$qp!4WkEF(8ZuJ*-Xd z?Td_adN6$WMtg2WrU3G2;SMokrZv~tpKA*rE9cngv@WLmd?;!vs9PXxTwVu408Ij$ zF$-&35GW#maSPk4AjXIl)r#yswka7bUYJa}6k)U=y{%&ULQG7l!Cn$*Ss39E>PJ00;WkMMg6mgb~9I0|5}PA2$vvoNxNVAWT#UiIf1r zh7my#iqJU_A;rk&&|lYX1nN;tqYy_-=$riehT>?$HN%vEx`d2JtPz-)+7yG%3M?G% zwZ9JFfao;J7Xzlms3E%t5=9`h(o$?d7U+@+XHk^TAi9n!B7`)ZMih%Kgh=SL1hi=Z zkfuSjkl_m@nU#?lLP==#3QWWPMM3p`Gf$;N#muZui3=a;#aY0re0Zukc$X9k;KFBC z%s8A18NTF& ztEVpRVyc&22-`hlf9ES9!N*;s$#C;hZ>fh*FNZ@p009{UahEOah*plYlpu(pMGP0p zmjFrWN(ExWQv1o9X?h*QQ;fZK~kz;4=)uc+~ zaxFR?JVRFHM82V9VV9QrY4zO14#(o58|*Dq#D>_;ZhKgoXxRXz52j)O=*Y5me{6Ml z?ReCO|Soy*QEgV{+#V?f;!thu7>liR10hnx$Y_vK9F$nPqxN9)U%DB68iQTbe zX2`OWlP1he(xp*TIZ2Ft8I%i{lny$1Eb)60$-p-(v98&i(tB*ph~EBDd#%Hlx7)eL z(2(46%qmojH2SyhslCwspYAaZy|h{Y!WRMH#df?g*WOY?<3&@UGXzPz?>=QrI}=X3 z3x?n&EV~e#D)a)h6bX@Qn3PBV)7=&ZOf1t&{D?6pfMbfJ9`}=LMWQ&NH7(NOj|T;Y z#`lgyp!on_O3Aa zGj3}_R*UWs{zC|7lf54qOpJ|;j|`qZwBz`$@4x-b7vB8VpB;Mh*@JIB)6~*7F+MtD z)(^>I)@Jzoo>{tIGP2?oQ-G5fY}2~@v;1?oZrrjniWF()kB8ooSU^NRd5(bhJ5 z-3R~GO>0+k8szhqzaU5`cP$CO`i^j7z!D80%dnA#=!H-!gy6{Q*t4q>%$N?6on>=4 zGs7!~!u@{BN?8v(Sb(M&Am0IYC2Yu9?X73P;PToG|LBnFYT z9CszAqbZ0_NE}l@Sqn!@wx{!4_7dVplqHaWY6VOA17!Ekyfilw?L^dI>c>In|@Gmo%~- zt5%#I(yLc#TvDecad8%~D#u;id#UF)gNx{k$m6+)*>VG!(j$r>itX>QD}@l3Bme^X zOs4&&Q5*of;>4GWWn`eSS!^vMqgJ{Z3#fCIiQkOlDjzQ^Q85F0#gwUdt%)F~>Iql` z(pOlBBq8}#7y)Q9Tm~|GdeD+huU|~z0RG@#HaigzlUA=G-?1}5AtXf?9xL2-L~YZE9@+s>017-bg8J4u#i%@4X7xgONyMUGH5b0!wUy( zjR{F2xnaHy3b8j&_*#z-6SIkBUyW7G#O`ayjpq7pVY?>;fE=Mn3!ffn$siy?ikc8? zQuDF9pw)gt3**o13qR+@AEVQ|V4245!DN=$rPah@N_TfPt0EM@lm18-o53GBwj0GB z0Ag5xW`l_gRX8vU2)Ignvpr7^jj&2^D>O9&1z&6k5G2+pF(c^33u5umz&d@3_{KB4U;OGo&xwh# z;j^cXHZ(NVH8$lOiO!EO;svA_@blfHWB>c^@h=_@LlX(NB67L9*$kO zk>Ea((3R2oWcbei5{9n6*SqzF{ga{OvuU}S7O)yZ%V0Q>x0?r9qgabJ2g z&zk?fpLt~UbqnJi5tVJUWGHeCDMr~18gRCduyii9G+3FZWXGS@%1tqy0kR$`tfkSO zl2)=-`u>nVSxm*ESBPp?ilFn1z1tCC6%`N&5DdH5ci1lU2Ugo%(y#CE#UszNii#Mp zkASprN0(*lLSi);UqnL;SmEcz1K}Z~R)a;J3kNMl-DjS&$)WfmPGoS33&hy)d$iwH zqyFR-dr>|1t?KZ{qi}S%FDz=cU9hm#mRDff*k{<6QE3T))@qN!olYji>XV^x=VH4D ziG3zPfq~6OEZP%iZEaBxXW76D-vPu996Jy`UF@otRs{w_phT1+_Ta4TumbhF`|ZyM zB+<1aRUl|3hB~|aeS|4=EoMKU+5r4rr%dr<>k}3dL1gOv>y6l9!#)DEC_-S3t_@2QTG7VV=F>+6WE>hI3UdU{ju@dxq$MB& zQ|?T+uwDbK;4H)mfhk6Zx`qZI)Dl~aGpffS^9PsyIEq~WI_^^E%YIZz+W``$EEF>x zl?Vx>K{bBM!mJ(1v+0Y6u`6vy&@8blF-}N60s^~HtR#vT{wf1Uc+t_svUcU7vZE*_ z#k80ZG}jIJ{QOgkPFr7O$v z>A_-1j5afTeY-Gn=+(ytVtD+#_e`a=00QwUSq{;fbY(WSY+Ak2E{GR6A@ZXgfnB+l z5uHU`rdRobh>Soe-^5Fm)FUBeTwqpDoDo=Bo(nX^3I?NJppdXH8S$e>^^)cX$BbZC zX^atLgk7a2sRLB74~@(`UlRII;nnF#Hqx~3EQ)C?dW8W@fTHB_uFbW2c}$QQ(1MSNr^u*mnHf87cuxnqj#%WJ->b+Fg(W%*M@>=Kx?pxHQ-IHjbc z%(bV<{0XBO{_6ohw#r`acGmwS$K&0;a06_*eEt;%x{AsUlRpEmuvMyRyhxg`p7U~! zj>yEMO)|$6^?2wZ)W%vCg2@N`0ZSdJ-|~CU3S0Woc#%hpR80*h`vgN@a)1gqV2O%d zZBa`u*wAgPv-MdWTqcAu>Y*y^=v*RB1_CFk$o_-OC(&eMl>9C+=ix1agK_NV{*-c8>) z_}X^{&Kw&V?5l5R%+=NBL%y#5%DnQ7&nzF_ONS}#m+hU*kAJgk?BDK~?5u9>I~SUB z?H&2`T(GNr@G7Ehss*gEfpBVj*!;)gt-riVPw>8Qpk!oqp?;RFiPwP1Fg6-WKV3F6 z%zG<*HQBkuShRS_st0~8*WC7w>nx>mB9VhgPg;=T0CVP$U&v^5k!#qLiNK0EmYbA= z^kaqa+1OQ{vI@=XNyGq(*ukddSv!?HGx~Q4Q6V|PLUCx!@;gp#4aj3a-olva%Qv11 zUpr<3sum!0BFY-O$dI@ZBbj0u`!E3+bK^2!P4McAM66rO0m?|96ouQl>vGyDAv$I{*b>G04;3Z36c~^Tx?n=s@O>FecBBOpF?>Pq?@4~h52lQmiq}`PXlYtt+PUPjjV^q( zTD<~_L>7=o665Gfk={rMk_2;n>v}{I$W0T2~bU+-nvIb%dnb^{pa`h^s3$5MQrh6mO zTA8qm_nm4jWTJ%9ksKr+9mSbaPhF@Si^mq21yU=UPT#<_rT?3sMT*@MDqY*Cm8P9>JvNY$9a!u~Uf5dc#zBqjl9&6QLHc|lPzb8>3D#Bk!QJxhfo9Qe7y zvY`AT12iJ|*ntURNRs)}?;8_fA1~q~JTh*r#tZY*px^58Wh1R*0Uc8fgpIBPUMpZ0 z7gjX*gY=Xy3tLTvXlS;GY`kHFLdo^bcJUjQ_|WQ%9LD8FmRn@dH{_sD#v1 zZ!KN++6bQ(+f^;K$BFtlGbc_T+HvTOXLh~t z_nqJW=Dt_Hab(*IBLlso1HBC`Gjq9IUBl&jT2Pm-ONZ&%&mS5Z{SP}QzJ5H6R<&GI zmuqOxubE>%w>O=sYvqz#egSK2AUyT!wsh5dS>Iq@8~VfHk_%Y5&@d}_GOil+p=H2+ zJGBI6hPivgvy>{y{-s3g!nOa;U;XEc=guIf08XUCeU^GiG)i`4I+B#K3@y-#RU}Yh zyKS+(1c3TP8A^FAW;Z5CISxq#I-QR}B|$jw$&c(pOA!vh6kY(ekalH9xmZ;MlI+sb zOQNTSK?ZAaB}Kog3@FbiS}Ju@cWr{9}fy7JU7YtP+5oF(}l^+yT zsIT>ZlaN3JB#3JXAKRYIx2v_?% zbAf^v|M*$^!XT#L^!V|@3%f+lD56t#*}&#wUJCkPi6I_SZ9wM$G^4QDEBq}0-zq)- zdjY;CM`DVE^4`o!*Z+P&1$Ag55mUYB;=+!gKl{e6WWKc37LwoqS_(v$50_64J459 zC5WR*DDDL?^MyOvAg*3T*pW*4Dki2xad^eL<-$uNEg(8__`{aNR1Jz9P3bp{YT~>o zxLA%ZRZ1pUglDh%ZodkSeu5ylIKL4Lq$;Zxu^{p`~1n8Ugb^ylBf^^h>95t zikS)LFWC6`6tm>111G~QcZS95U`8NXfUj#7fTPu`C>&+dQ2}T;x`2X=aR6t0(q2y( zca>TYlQ8X~Hk-F-79wNB5{X~9b;(1$!Jhb51(s=&5W-EB_35P`??#UQgKc)1TP$MO;z3Q@5|?iV1Zu5tZelGA^?p# zlZkn{Y%NJ<4TBDyG9NR4PQ8w6v*{*V;yr~HjzI!R2$@8SZrFe%)j zH91%ScHq2N7n4>;Sx;HYEF`TqfCXe|S2i5~^56c=_%y#lmWoJk0fLZyNDs?Y;XF&Q**4GGAEE*%MfWqOCgA zg@%!EW}@`hhjSBQqb@#o=OHzw97EIw4nYtr|7#0+H zkCz!3fn#UB-*ZAzB?o|pgEict4uvEe?a@RF|}x804V}A{h(Xj* zSvD&v81SM>MqQ@m>rfbZzrWQKV4T8Lv%#h?AZ!vmNW5TEhY}7p=|f=E9JlG`fb7!( zl{5lFu$ry#mxAoDi%lJos0fEjM#69iQd5KqV3rt>W(Gb^vC}tVKW!-_lgx3~-B349 zw9w+qJDa>q1;7E=SMb_J{%&7p6(A6}bzwrwI5M03DME?CR2O{g%Qyj3UczUiBvu&m z6=_YlY{~+yXdp2mcBPqeL>ErAdR2@l90>&(A->vkQ=RfMI+6UL5jkndFCS#hVHyf7 zZK@oi@w6Aw+RYfr1d~yT(C)yft+@*fP?MHMm8(|(Yy7bgpNXp;>9Je}TsB9=$Q%Np zss*gZ%m0$xi-9TU`e5OAE2oXjoWAOpo`}rqRW;DZNy~+7*pC;53d2cCDWJZY8P zr|d}5Ggo`kDyOu$^XiS1j9;@n zYy!0F=G)^=aMclm6m%pN`sa^}M*9 zA-wJnk=2CHhwwtl2SBc&I@nhY$A$;`Paf*-+`50ulY3wO`rcRn@$F~7u;bY;9NqEC z+0#eIM+O_3TI%ZSFVAAuiaGX0p^FGE1%Qi&2(=kZ?mRpCZ+A>Q-EChluLAYCrn&jG zv%~Vb3;vacL*EVa)`uDMLVckW>Dd>)`)JsEmA$OJ9Qy1DtP-dT4Q;`1$kc*-m>end z0~B+@^4G(c@|XU@zV@CE-u1p8?;W*AE8;n$wJK$z_=l^VR9;uW5unNPlo_aybGg_f zgOlBFxZnYEqvmU46UM~gd?Y`hM`x3rEF92@P_}w9rAT`b_`%z+39arfG>|Q z?^t3lGbJRtRB4dhF^iDw5S}VJZ2-Sm&@Ub$1MniV{1HyHr~q8R+2kX|zKSKh@TWC2 z1ksuo|K5Nst5taRdU!8*=v5@Ns9?=DKaP)YGbRG4h>tbR+cW!YMT5#@QL9;lNel>d z`U6K0OsVXMA2+PYiL`u@sZl>6_$`R^Ae*AEt5QLZ&;InWT4rki&=mL zU_&dsT0~u(GfNBa+l50bU!o&{WaP-Hb_qi?Ow7`XNvo-l3cvzCI8vcyl4T?!h9o*hMI;)g1_Ee_3P?uKw}_$_4y&{T8Rr1W zVB`a6E9?soJ)BJ~Bmw=cB%}!$6djSKL1s6iq?nw_Y-VPrfoZH>fpP&~e#Gg$L?~&z z!~zzd_N!-7E-Cbjb>#*dyZHQ1xd<&(K8dS2z!?E{GaucOhyu7sUm$=!%#oj#Oq-3g zh^R%Y)ho+heuV|Hre1Agib!)}@abNliBfOu0?>nF$s!Jz@E?Bx@If3}ROu)@5d+8{ zicrSkBcJ)AB`r|ylr=c)lDs7&F}72KmJ8>3u|itqR8fMIicHZ`HxO<7jS>KpPY^M% z;eBhD`FievrDUlylbA{*-DyN-fNBw7BN%&Fy6{Hxr@S(NI8Cot7$#ZxDG=mwK>$R8 z!gaI5tR~~^#+_W#FRr;VJ8MV}T*yn>F%b?M37~%qfwx;BPbGpb_k8Cs* z(kHvLSR>lF)Wv2=x8Rx>5Mz7IpPW~~iUeqW?)K!3djF`!fK3en>tzco6`Ej02he0H-|0Gq!;6V z_3Zv_PyN*eyaOu(j`}N14WALqi5oEaAleE#-@a4^}c0XGGE%ksqyX4=?a z`w-p?;W3}mYq%(|7iW!)3=W(=cJ|cKv!{-9cfPrQ^Ames`X9T$_myKiUhX~KdFJrW zrkVDaP(xGU%NrLP(^$Q->sTsPMWp#91q{QJV}EgA>_6?zpDOv_PZ4rqW^PeOeqB@W z$8s(P_CFa8d?y@!KJ5FZZCn22YJ6FHIh1<>t1--M4gQ1wwV)x)7%1~VWkYB=9=7$C zSueYYc>j-od~*4{*U!tTY$Ka|@>W~cA=li$#tJGWBU&p7JV{ARc`BZ1Fv(@?q7H!= zsfs@=?V%fALQ?vpOd{WrthaZWBzX{^rMyOZMGPh%HdIKUQGhP;CV9|)PuXvSp~40q z*SaSJaA6+=zmH|`g(Px!43)gRZochOIUuYUaEdxA)HXm~&?UcrsPMp%EPvrmTf`xW zqw=lY_LP>!g)Wu}gLyOW#8wD^EHC3q_t1nytztv)0u>`vxp0QY?Zxd77OmHZ^s)hH zHb5^ysv4oZVHYfJv!}(06XTeby2?B}(gDGIB4BT z7+w@1AZB)r3|Gu7ce7TbmAQ!aa3+NI_h>bJ+O>|`)vBrK#depcTs_r2e{_xH za&>)Np7JKis0Q@i#1PB7;e4&;D#H{M7 z+O5?R>KdFOE=2bdBSNhk@WmcTodv9!`Q0P!J5XDkPGmE zpNmB;YGc;!;nV5p>H02mm0AIDf}an()>qZE9kKaiJH)l)L~PyXw_NEoZ9b zs;4U~n;u)!Wt3gnL`#=ZKE^j?uddhSQsAO5VYT+9{i4Uz0ZipK_zIq*)=;vqumQOU z^(PM56Gl2l`W_+zeIw~w{{;=pslGgfHHs}M$_p-3j$miGKXi=@X+G@hbW#AJ#V(5U zuuHKef@G;sp|L{+*5dHC@PcWQ00Bfte(vo)1!TxBgJX%N< zcT^^j0F$@CYCk()O zf)NTJd?}2mw<@fTq$Q!T-sqj^ouD2cW7<9o=@5s2lY7JA&8%D&`#;#g~e`dQ43gVM@*@*V++4JI8|9I{e zu)vsq$Bj{5=k2UfD)p91obgNM^o{99()s*87oROJlNYyxQ~m^5x!9W#Q2FgN#lsp` zFjpv-C&x#I2K$a4-}C&QC;#sC?|) zUz}B~>7ffnEvDiJCZFsb`JY~zeC(|mo_bBCd5g+d&rd5#bNz=i+MiCpS#0ktOtpYD zFD-7f2To@PCUz_hj@!41>!2Yuyq>;R>|7ko>+1R7FaOK6t5+#`0g^Atw5Q#&%qz#! z^~>$NBIzI?LRpCxdF3Oz*d-ZhSJ@L`1n64XWmSZooL*z_2eWRWtzH3gAL*FP4oIvb zNSXk7HUw07$ThoW*&P8?uqIz7>_Z^WzH-9WK@=5CglG{X&thRohkY*(BesY$TuAl~ zr#sf!7vkc*R}@0)7T9xGC^8}x_2_rWi(dTV>;BEg#|Rw!YED#vC~P)d6DJl_%OR%a z9<*%zXxN-S=^PwUawCbBhN%sC{9zovjnrRgV28skXa=?}u{&)xQm=9~U9@<-X@%Km zu(G`9^2X>3S5`do7 z8XU7hg}l0K9{j_CO^cVKFlpm$q&T9bm(jr?2L4F;N6ff{0YTc2`SchKkktG@iPmZn zVzjaeQ*lC5ZT9+U0~3_}*$C`2IyeX@5?T>b=WJ2rIphwgm84pmXMcx%^kNOB!T@3l z6wD_FjdSpniBUr9&FDJ)7zkRD$9PMlKd)E$C>Nn35VI=4$-RiqIe{vscFnb$tyQU& zX9!har3x7t5i~hMZbZY;bX7rOv~{2AnjSM0%MlTVZXWZ@QL80R@Xya|T}*GMZtom= zpV3zcg<7xY5&8@s`v>xIx0>{d&8kU*8wH}{%ZOIrS{IAJ3lr3}z8kr7joAdC?TK`NVw z5HSsnx<^K!6|m8j2i^6e1hdBR@&~4%*DoyM*GBNa`6UaOUaS$`4a;m7na{mwEshYw z%a$J7l|jTb7F;l7eAy`cfp!H$mx&Eov4v|5N`Ya4ij(rfy!cc4wDj&jm3CGZo*vod zvnMZp4vP?w!48q5h+o2{XoesXN+L>OEQC9Pfxr~Ni)5JH7s02_!ju zOa%fswSf#33uHRV2yhgz51(G+X zX>|g~GT7e!<`+KyXMFs&ojJpVuNL+Cm+#K++e4>ICH~Hr%+Y((`%~$uS-v+4%ui{@ z90hRU80XJ!mT&JWr}Xdq39Ku)v?!NH2K!E*c=N#X-`xAumv?>lzrOVKKY8Kr{?i+e z|INt*uMV6%R4Uo)=^C0_N{&M7VuHVY{^Vzk{_)X&_r}=2-vd!_rbnqXKdoAnwlrNX zFO0ifFx3K9Tk392mDh{T4h=(7{0?b%TJvoB^9j3=aIvuNhI`-pu}|#jZ=uF+Z?@z@ zg4?;;(jsTt^~){MfX-ICar=p#MMnNHzn8;#sNAG{q+Re+TdinU7yQ$szS`m^?Gu%k zzSC83Jd0UMwKQ1tY9S06@;F(NOot04XcP7XEdm|nv9E`k;ZT8Wrhu%xaT1Q~qkH2@ zOR`^j&AP0X@PMi7@bYHAsZ;Jk+mQWFd9fEZ2KVy!Zm>w0pZk3w45msZ{n?`qfp;uW zE;3T$x4O=s1>4+H{IZR~YcQ1!aF)22qHyw6xm_ph5Ma%8D^;}B4|?p#OEN;eeXWn( ze?muNhp?qRqV;taq6HMOSQr?yf$MEt?9#q`Jbibc-Cbjs38czJT@x+>Bl<0A>KUTN zseV))j|zr>*;aH~ixEv7wCVamWGu)1z2@-kAo}fRHVhKpe|W-w{ibqmAO>VaDg+?Q zj~z_U95OM&01z++kb754O6i5r;8 zAbgE+8pLc1=ZwEDLTf7ZS{LWx1=MPZ6Z|RxE<}3g^`*i24XQ3?v4Ay)UIXWM9?Usv zW0=+Il~8L$oax6%udZcvW%SE;$Z(mcLR}R&2)ZCn2X5>U2w!#jRHR+uj@+&_2U$>9HvCG#mn$QT_uQi$s)=Y7~K7q zJ$?m0>3hAy(GD<2aHT>y_HGx)dw z5<`WsMZlq#-Os*Yk^`f5Uk+XPF#AW%+D89Yow64y$YUP@oAknGg9(<*x1V>>RH(%i z+40`1Y)&YEq)sV-eY#NW8?hTT5I{FAp(zd+H5(Ra@d5{fXz3QH4Wx2!N}(c2%kU|E z%(Yhl1Wc8H1mw^v6Sk{*2#Tf`a6A z|GwMJDP^zFn*}(Tj`?>9&rdxtno3Wkvd{7?zy)aMCHYmvnSjZB&3}AwR`^;&19x6{x@b}!%)NHF-vs}#5 z>}fkf=Oy*A2JRcHtJXs zCHYk8QZHs=BlVDvI7{rkNWLadhN1*Q7fH*B&V*pY+9!6}RqWyIMJ@)fSC%~Jja@b% zCX!3q)3rU%jAsTLm0i$`}3bDSd5^{UyimdU9I-yI~+<}bpb|XbLA&hM2Ih)1L#69hcMZdQ=ndii4e=g4LSq9>XnR$57YvfK?lSf9xt9($Yo@tu@7obXoUCmoyDuaQ#PJ>frysovG$V`RW_@`IDl&e?MW40rm#RAsF zz*Mm^j5yaX9GvLWnBsD&-`l!Kn0^%WYO8zX^vid3ry~D~T&|1oV*iuQQDwZ!ntH{o zlGH9AhC?P7fEL7wF0Eb(q=5x(X;s%5-4M%LT_0nl(7NPF9zMBEtLYLD5j2Te4=cfW z{D9pMkfH?O>jDIa3v>Xn!~KPw5H@$7IF*yw9i2!o9Wz>2H=sDBrCl!TSZ1V){CW+@ zj+}TfN%QoYH9gMlzdHl1oS}=R-Zjnu2^9foYy!fD2DD<5;HldNMr{{I7{rb?L*-E^ z%rXkg`V7Hyzg#N=ab?G{rb~k&$ZM9`I}y-=Loq}JBb($o63H%~7A#l^+!~hQ zW6Bi$0I6Ww*Jt4&#-ab7|K>4gECmNDasjaa>Ir)qTox1_#u+9?WRS3-H4hlEr_6k+ z85HV=`mJ)+!9|!+Q+55DZ`tGex34vu7(6y;^^j<65|t`5T8w}GC7V)BBTPZ1FgmJ7 z@u&?@pRMs1gkguk_*;AaNYTZ4LSU5LuN*I?PGRE+Npd{2&J8SM7QWv4p&0`Zlw}PK zmh};ahaDz_!E!_ZyBheDZx)YqAttl92236D#|Le&aM-n}_JJ@W%_PFK@F5frF)(C= zZ4&$wf_6P9qY-8o&=Lu|FcBUc9(&?%|8Stc&jsfeeCXg~+e<0kbiOM=qp5U+E9C|J z0$@3no=s&PvsvLrUpu=@a6F|K3Ua16l+yppf3oljE-y?@jE@fXpE|nl&?}GadGe80 zzxBC&Pe1zR%ikO9JvKgkx}mA1)X*?FKE`HGXEjXs0Hgk*=McI|luwS1{co?0|H*+g zIHQLaiLxuLU6?jCrOT#{zML?Yh6biseLn96tWqjXrqNOR-HbYDOI(ypGIl4~}A7M5Y61)oAot5;U}cv)gzw8-TvTy()?u$vd#>W1E~ zK+*CUe~&wS%wh*E=7g>=DabVS_^Q_kUnUc6zCa zC@MAseju;_0iZYaSliZQrYLxDB(pHFrsvcUhK4xeXF9zAYY^hAOo9gNN*B;-x`-Be zaTEYzMtd=gM`%qct-7>fnO$_DtNJ3e%@HWhpTQ>}J6Q59sE0vu?<$PEJZ#x7ME zGJrF1$lu>e7tlx*TX>=Cx@AU-BVT}BHifEP1tYPLj9oTCVQ9C|t-O#Ih#vxc%7_=1 zixZ;|MDO#Lfk3MjAhZR2PSlmj0}g0p=&Ew>h;})i;zb~8+@s-(R;Z6OfbP@lcX+Z5MvXT8)PlA6bX_9hHGUq z>Bfx+sk0#uU#Sizt?WQUd}m*}XG5`zDaphPkiV6;q=hMV^+c|sg+pZnNUMZFZ2?m% zYZlm&9~B|Hs-IMIU`j;*UI3EI=G*hb!{fzQ6ag}92n&r3wu^nx(UR6KC<+vWnRWt0 z5*mXL7&nf;Kor+tZMW&-o$chH$w*i8Mil~WkIna_fAM!#?Xhb`(p5#Nglmi6t2(+c z4&VSd&m1m(xliwpP@VVB?=)R{;egFvNa;WTT8-tKd+pt1H~TLJAde@)wop%6fh%1N z3ea`vv@PVV>GEUbH-A39(-z=twaA~1!aET%KkPsw^WF{iB?ydb(Fbdc3qJA$Oh_29 z3Go#!0;5uF@`tpcPYtK%jues8BFSu6@Rj_E9d;!QQyX2JHXpvm=JSQ4#V)bQ4&(aD z?sV-^E37y0k+G-f!UBZKdI;^p2Rb346}i^Y6bz&2X&e9UU;SVGeU*;_bC07bJ(be- zbe`Xo8BZlX`*rvi`eqA0IlYledyBnUV9Vw7;`R~#%4hi&c7%V=-}rt7mo?(Y4UfM5 z^xmhw{K~gJyYJ~o?GdeS{l%-_`rL`VFN_VJK7HcNmiDgl_XqN+IGc>EU|_GP*gm1m$-B!r~t`ja?-WVf=@ULsBx8z2=b z8&an!JY?Wt6Fa~v2RHlVo9Xu+Funwgsb!a8E3PQpWRnf=8UduBGXds=ie<7~c$6Yw)X+r8D9m7H7B64+$J=2d#)fODP{QF(>uY*H7co=a zX}l=fWO_Y`wcWi|{{SlqO7_4GpB9MFXWs(@zb{cCj6B%$Z z!sgGmYPv2kk~vtwI*)Nqzj}3n-~vHNQ$?k})$@osPW;4Q7s$>!sm89VWjtAsW;@= z#0h?Q1p4dXiXSc{qfPc9)GH1x5t31KT_(FkDv|(*>E+x1}PbTeOhGqdn+8&wf=<(IPC5iW6`w)-Sa9!T07=w%-PVz_#-9aaqx?^w1thwQf7CwAI5>i71V7y_sh55e20J;+@rE#Wb@ znrec4>X5zao?Qf}fFJfR8rYopahC;JfK6%5YqpnsVfG<^1u2dwzu3hBQvnB+)+YN2 zXVdaO;bk4^wzY*FB>Vl%1u>=2Fpm#fF9D!}73PF_1B4CdrDJA?Q-!fm#X=CLN^N>! zQa)m2gMXV9NiOafmw>${&)@Y%s8qL`yu`r*T@2qIi1UgZj|2nX0`R#-9-H>GKi%Ho z2;&O-+NGvKDZoVlvlMwd{2LjXd1MeK09Q2>iu}D)9txw?v7=qtKKx(*{67u$Ueq7- z;>`P=|D4~Ff-yTx`g2$<$v)}Dux5gzsr0xqCEOPzg*tq*Ti$7AvG>Y8@to`2K&D46-} zQA_eCSZ0sL)H0k-OxAzbRcSO09JC+Pm`ijmUHdEl&;N7nRa;46T!_i|BoS!xi1HVy zhw|oeU&^6YS<`JbLszSPI7f^O)l+%(B)e2NkNFFa;LxQ{wn|LOW$aqonrCV5K7W)( zl@hHInTdH8Bf-%E`1|`zo-SxM0WreUn_^%pU9wTVNK$spNad5cGASV29vvvGAqJ=x zNYIg#u`G4kdpFt>>#{?rUwOl3MwO37#4cU53ZAiG$}Cr&_EImqY*c0Oa!eBaN&ns- z@^PP4j%>pHrXwn}2(W3w(UmombU^Sa6tQRg?ixT0`&eTj7H8$>XoyV)ZQ6aXY*a@u z75~5ew%y&t#ZEmw=90?LgJVXAebn_{b^#+D&E|yy_}#0`f~JesjC+xZ9Zk!!`Bvg0 zZ1e2T{-Z80Fxh2#aYPqK%2fQEfe-Y;bw6=xcob-RKIp-Vi8WEu-Xl|Yqc?MfJ8 ze6sj1w7_+i1Yo(xZwg6qddno0m7S&n6kfOp`AZU z3z|?t#7v8N@N?rZ<(j`pJxCnEP*0HOmUI;F^8fcgNWb!~GrG-mn}IrWF1{HbTY|Mh}(5ividf0WYg#olaT=M7_Ho8i*Z=~Vh&D$nd!{{yAYRDSmb zzjbR4abWguVShEHe{to@gFnRRTDGoje&rIJ(()}kmv7p^pGTPspEsbXt$p>jyIR^i z%M;_}@+8cWp;P;x{rcGO>GI?xoJxzl6VADMVrl1R{e?s4LR(tYoi;XJMt>=nFZwM- z)PD|?IqXQwi=Ek{KOHz_?-{Iv)9L6RrjOQrN!IzGR9bb-d+zv!-(GXWd-bL^rIl-! z7O%llq6sMD(B6M`#sM%vo|TO7g&WeD5}EQQh)f595ZI-vq_zyevC`t~bSZZ#3}iuh z8AO5xn|kcSGav?p0m)QaO4brn0E#n@EjJ~PO2qJA^Phfu?*`*jLP&*H!;~omUOJZk z_5Jo76O{&~Y?xI}M_5caf`+iV%YK46N{$Fp@zBZ_P$41j2(ue9La3`(VpT;o1jx;b zDGtab9{O$6A%YTfs!X*fG)(cpdCoX)ZF4<|3UmB)?;vu{1_X^ns zvyD(jq6Je~kW3)1xq&JHkQ=P+1yeCW&L{Eb=|@^sx^bbmw?z3puIN2FkWP=-Zp2)r zTNw0dM+W2^q({J2fUGd186e-|T_DK-}Xdq#BcW+Tg0>s`LPJi}XWmziOvhT~s z3nMBkCC2V=e#v&*o9%}ttv4&0v7GNBui$Ah0`lrM;soqU45;_{Bk4n1Z5M;=il57~ z`(vB!t$eu_Mkqr8Gh@qME~fJt-6)QsLB6X2vMU!Ts@?_AU*2VHZUL}+zu(gq69ItM z(hmDWtHS)2w6J;j|NIv}|H?N%Td*!JT2uP_DSgnFqh=3NTAj+**~16tz-THxmdZ1I z|FlplwUo=Z_;X&D2~MZm#pnvyn1A?Z(`J*K^+?z zw0Fi&TP|)(AGs&}MzJ#m%Bj3CZR$wNiv24%7cSlc)V-XVR;V%gd@42 z1+o@0Kw<*IvH+wan*b(a>XKRUf#{qg1V8M^h`Qp&WX=pbRc#J9Rgf5vDebDL&V|L_ z{Z?`Rn-5CS8;+u_Le2~tqN~lmu~CJ(7OH>@kQnjkiNDJPGl_qCU=T)r^eW8R#z95I~*+j+%)vB1SLr2mmY#_Ps)%TWr$B`Cvz|@*_bf2JM9ldR`r- zbydD}Aery&F9LvrmrfbiuJB;%~+B|IISd(q-?A&{OXI z(kr}R?e`wAcUxTTUyp=^urg_=LKqJ3V>0VSLXwcRe@dr=U5-L`u*2 zBFpvX`yIVgsnq9RMVw|0YciFdOXU;A-V9-D$wald*nRuKTs@W2jK0j3;XkD0Pw`#B zk32>O`udOUAM8Ck+<%g7bm-h~L|T~EU6t-GcBTO7Vl?$Prlw|lGo4=_uV7k?+Ix`d ze}!;f>hWnjPc)>)v2=Poc`~cPWE$I(zFq8`FP3b${{CP2SHJN;{6D^PY%#e5(2`k{ ziKqw2K4hG=-9?s+ECZ7+sgT-~q%2eWR{~K7k=M^3rQ3!axQlqoDkw4Crb1n#zk|szeco z`i=jjYat1f8cYO{Fb|vDf{H!OIeZi$RH$g9yku9C#s*O78!-#oWl*cS3SGJ&kcY_t z`A23|8*BCu=0L-dQ#gbKQTE@Gw)lNQkk^vdQ-@5TxzQ@On^xE}_+}?ty#n%%5jF_I zmJHAUVLtjsx_0TAUL=`AiNOKH!?eS(Aym#ZZ9uaj&t$6Hw|jl z>l&QKMWC)COifyJO=U{_e1_)=x%XPoHT@i32riP@3&dryfE80S!#SG%JB(AEd{Z$S zD}P|;^loQxmc4}9)hj@6U2DNKQ)i#wvdL%8dD$BwX8t#TdVXb*R>WCrB)Y0xh(2)2 zUXrD*5ReOK2)(`z(CX5Ydk4DC<`+0A<}H@ddh=9z<%E6HO^(znh}hF>%Q3-}7P~nI z=;ug4trrOgGY7jI|04UkFtjq9dSS{PBO+@aA1HPKX-`^9I_z4{U@=86M%X6HmasJg z$hSA!GrbrAsw9B&76NSF+-pl?GKdy20IvQJB$bJ>-Bh7VS<^DE=Vm}FVO$T4rQ6n6 z6}7o1-M+5K)&Lb0sS;o*Y_y0<0ceb}!|?S7L6F)ubldVGz-&{a{kC*-gGovlK%oA+ z-!K%h=B-(*;C=jc>npG_S-=0pvW%0c!9tmN3FHVXw)?`T352#$bCUGMVbT@DGHxC zoPJ`9@m0|x&p$>~-W>o?IkPkC}I9;w1hb-%_laAp;Pg`qWFl zps|AtW^oEz{LSt+*>Az04+cx6 zT5~!uY8N}oEWu5gZfW{cBa)AK7qqY3pUkQnASa${%q2g!p{c_x)*M} z`)7aq_x?X?Zn(F9qLsukK51XIQ_>-ukkq(bzFxUaLh7-E;>%m=l^D4Okkl+^Reop% zdF6fOM6?8{U=R+U##P(Mh?sy@TJ0@G%CmIli!$ueDhr<+EUK^Q#W-D-);%NNyuy|z z=q;Deyw*pHJJ_KhJT+=BAHXqMFv-^le0-;^Ni{au8#Y^-jDrBNVGzqe37!AxVxxM| zD?TGIrh-z(UU%g+55i0cRl)TuOn}~fefFb!Yy8J^*i;>sh?cR_g#ZKspM1m~UZa;?6ajO~RDAmJA}~$>acqF@0x|5+ z`_MMivf1!y94}khk2|!E4W>KS*&-CvPM73GfB6lIr>o6svto>1+~S26nmAuLYU@Yh zYZ~POVP?R@{E!_Puup=jUFAjbn7hh{2ng|+UgQa0Oq(~i0}uf4*>w9B-NpX?P&Rd` zMP4)nP6ZheU#@dW3^F02LEZ%vEf7b&SQZh3QkRGvOer5RXaT+P!8T?LrVeg64dGZ_ zQkUFGMu4N(fcPP{*k2h2s#L1LoY(e-JKEab+@NG?X;8HbP^=Ax-73!XK)BZOY%!IE zsrVP@?j45 zNlV{nmOCIjfAqAaRq`u9Y^_hJv}k1<6~zPn{R4#sj4SN{GAn;)sYyn!%=x`8-pqx@I{cuHZ0&}?|bwHzrC%*9fz|@>?^MEv!-Oaq zV-UY|{q8rcNTmy16lr%e7PN6KluC3Tf9qraWY4*mpI{ROPpdUX@r(cVAOClsINq>a8H(IT#iIOZ5}t!#swI!WQQlM+fC*3`_bE4# zpq0C!5tA@2^0bJ_Ans_dSz_;iKkK#Ym7h9+#w>iY8}$P%>g-xJ_u2!yq*s{GQLC_# zj!zDmH4TSqhh8?t%&u9+g+EgCtMBr7{J|@kr3=nicblo@^Nk6is>Wo7?6q7Zag02F zB>noWCQQY`t_ChZ6nVT-GGXVv8|*dq$Wt#dBFa3hVO&`oVVDYrbPwlSd(FP$!H!n7 z5GLu1qeBn0IisU zh>!sY5Yka!1%QTME?c%>H-vNhT>T^GK@*i6Nr1%D)?^PV%f6V=vcYH6(!ve|o9w8U zmM-1JVrWD{tA!P0!~w{JP3WF>yZNxV-2`M-lK7%mX_bnBE;c|fGNSVog4oTz+(UJx zQ1L51lc0J9{&)AA@goCfoB$}cfI>o^E>1!~hDIK`tJTUTXf_1!ixF&Y@Q1)DFQ5^4 z51&`O`XoHeLeML}%mB3-j^N9P>Mc1!orDd|7@_+|?C~)O8=&6`)1<*c9Y?fu!Nd_v zipYm4NBlk^F7U|zjs9quOFLW&>fwD4J16_em{&Iqaoiu8si3w6vINBxwi7inm!wkuI#!DgYf3!a#uCJJ#A% zAi%~jH7&?2@_TxK)&i@n%CV~>j*Mo24a7jJ2 zF|{_I^_O@BXE4nIR+l~Xc|j zYI}2YGl|CXh2JS5Z)$x6rqTyc5{yKn^al_CT6jW|e5AB{=Q?`;nVj{7SIiEn3KfJc zCwkgaUSs$8puHYMG)(N1BFUj7LAfTEjZdb}zhqzid+C^!3gk6;RL(^O7s%5J)OY~? zC+{+0WUz@mxfMbi!5r#0%j8peBDY#>UgWnm+l{xAgJ-&A-_}-!?RFp&h4B79+htS0 zC?dd!$kZlD82+F-#R*<)61-ewW3Wb4@;DpTun&g-z$d&op`J$)BM1WQoFhXn)2rxMh`txfTBpCeaR1pUO+f_(@93>!N z0qE$kK_GB!zyQZLyMN(VA=q^Okz$Hyn1C{eg({G_%In7Kwj1swAuGI05mcH zeza>B6su{$#JC0yFoL?)4ir)q;Owq^?#l&41cz3m10r&cz%KGJqJU&*HHV^Wbz@h( za-GZ6g%qpAa%)wNa@2O!Rjg}4%-XKQr|tGRh%T<$t>^b8fvJJ<<^F(3EiTX1D^NR! zbBN30D}*`fo-|-8{-wk;G1PWxclxlSHa5a^ak=%LF+M*qx>q#P6Zz zU2s5f*d-+XjJa;=YDEi^>yfrxJu*jdL`;;^;eP8P2_cgkm%0ohaO8AhpA4a^Hdjfh zgm3^!3@R$gs1yi%ZIAVaioBqLBjj6}?DzF#Gsc3vJR$?&kSt-!3qzxez>pz3f=~M^ zMP*i6-`s1ram=9Kt-=CXO2*lcfzPJ&1|Q1|cEdvZ0-Dk-OaK9-#b&{K5ultWr&y2} zX8B(=#FK~YHBSIZl_0p-+iUliYV6dFvXxl`3Dd^TDll&xeBu)-1613t$9;yH45< z5h8{WoMMN8>$t4!EG$DSLc|yg2I2gdm(s_sGha-s#wFd)ykL@e>>n+jvpea%FiRy{ zU9#^0zGe0I$9LLPQ{OPFjIOoOfA!h#k0Vd53Cy*xBH5$wtL$5TE4%z%6~2TzFlu&U z)X?Z+oQjs#&VgYY9px{;jtN)JVHMms0%*~TO8*O_I)(DIhn=Gw;J`u-!I86Wj?@si?XchBB=i|!jf_seBW?e7ke|s7gPGnOZW`k75vbmoXT&e zr}w7sEl!)}r=K#q>uW?;zO+j4%N~vkkzLZ@D zU1{Zu>959S_A0%`rk1O2y62aE>yK9K{OD+7=T(cis216WJfh@l>D7~qQV(4S(915V zg!B~IN=XwYVx%evWRQI3uqFC0ykcn)n^H%Tgu8W#$;c)^1v zZ)5RxPE=qqEy$<ttpaA)#R4nvv@K?C%t}R}OpRofhU>|v8 zke4qzNqi3BAWsWNV)WVsx-J6Hgvuw8Z|}3-s~1}lg^D7FzzeNtEZ|PZUArL$;G6x* z2L!bkSDeG+_Q6W=NJTO_=wQTO=?iGJF$7r?IcSD0^L8p!^=elZ;_oL96{2qB(?cq1bKP*1k6Hrpda_)mN*%Dvm3?FO^p?Op zNK%(|jRjLcd>MqJ>o~i*r0(str;w_I6$7orv@4rE?f!l83KMG*vux+C@dBYg!UO&W zRm8|2sD#*L+1}}ZqS9DgK;b|u&eu+)?aS;=$bG(yg#eQKPo+tJ6TEVm(kesP;BPDF zAGK-3<|d}Dt*}E&7v7m73L`x`4M!P>z%WY=`?Ke)s({Akyr!ZTQxrgP*9arXTeNOm zMyz;Bwoas1u%nIu5HUJok8X*h%D^mpS%5mb-Sh0%>i`BdOT>U{y}=PqM@!n$V|>)9 zt|&@={|9!N6Rm;?kh5=H?H2rop3Tg{G&pL5l3(0mmMT`ikHgGhR`M93ctQ&?4G^30 z#XSqv%Q#Xgy=?YiJhS17AG^*JLAh)L!55OWs*mwFG?rdDZX6wAg<%isdtnNV3r6tS z7c5j_{^MuO{@Mk_yBqZVN+dZJ2rx^?P~Krp3#oblkiy!=f=B;afko~U4&?WndkYz? zF%W`h0(DYJrZ`o+YnOjP5?}{nO_#kH4N)Aii=++TS5Ao#3y8rHAlPO2@R8vJW*153 ziY|tr4+$_|J39K(SN~*Wu>Na?FDjba8~>aC>Eq2qZToi5_HE@t*}qHJz;(;NIXKI8 zA5QM__RHzT?cnh2AEerw(*I^3lds?k<_H7n_{-_9Pp4xm()Ol0U(~IGi8L~3-zY4% zq|OWcTHFsA`qKd>r`S0Qr8!x^;s6b$qjleP#eo=3eTUBVedJO@!{W8qzUSxu<Mf%GNK}2@@lxKFB zN?t9VK;a;;V!kbANtfOaZM9O35@qRpOToCtg6U@W(nzBOsfV!Sg|@rR_()Q5k#p&~ zW1amH3mkwJsoCQ9)fsl7(P9_Bm>L`}!o~|KlElllrAA{JkWvYZ@(hp2uvszY723Yu z^nop@cf__J$a}YFSHHBYjtQ^42H1Hl8=a83(uD$;Y$TmSYU&LB9Ja!Fp)Xz zucttT@MM9mZ|q6$@kbtIaD39fKOuE7H4q5Nv!RQm*#*2nA>j3UC7J z6G6y`pbeR+kk13C^1=XxDt=usA^E@qGtm_!R#!gfoJTNI$p>FHbIU%XmlrNsNSECL zR)DL?IgkBpap4B*TBv1CbK=s~H&Xn*gU(%DIHrk>PxVdNH6k~juXKt@D*^37^Ot|l zt3wrX3u*(z%taaG{7?H&U674BD(ledt-9z4I`E*KrTGN1 z9PvvS0wABOaLJPlkQlxB(1ynBL2hUjUdX5yF?4AgD!(10Y9J3!7HGHo&xIocts!8o zt;w$Yh&_GSY;ptH;V@G3zOr~#!6$C9iF$Zjp^IhB3@01AX2Eym8v5vfJ!AFk5j${ueE#8}iwpvY zEoigHqx3))yST&qqi@*EGygXEe*aVVjR}K^qU*CS*t3K-InE($)zHXb1n{@NWwE(H zK=Ve63{?x~=U)Gn(l`69Vn!Yr1ki;^%Y+byCX*ud51ue4Q6Wq*iXBc){m!W!g34Vi z5ol?2aPTfb5I;cz?9jDoQQEw;<*6_K>EXkNUF4FYv8m<3zyC|yer(%;L!(Duum3HZ zbE9-3l^FlAVs{pxpj+oNaVf#)%u##uvi%w(A8WNi_xpoaueiX>fvJDx-^aXyE0{}^ zQ~6kW<)!r3?WwybZJObl)Mz?2l#Vx9hV(1Z6{OUc_LS@Wv_dI0bf(qC&V^wzO)&Gt z&e@os7C)ChU+mOFbKCs)eemIb^1uE;^HukqF1K5T@{9>VHm6lKQ7g`yr;49)CPV3t z$WLEuKRE{nv0vU%Bx~hXKucOjkpzc~)^6B^zqs8p@1l0IOc%iQ*LD|aP9M3+)YQp( z017(*8Vj`K>n~UCbzzYFC_7kIb%8ZFXzBFb*H?r=mM346h!sC|@6ka!YfUUOMY?47 zFJ4R^)0;YdAavn^*j_oFK75Vs;srn;j9)BMSzWW#p3YVogF!;I0>GcN0^63_FY2JE zsYb8Q1XHW8)N0}9rM6gpxH_#a|a<{^WKg2w76;y{A&KO z3BG^8){?A9paS$)zhmF-3a?E6@kuj_Bu?o?QR>7gOq5NLdg=I(d9<04{Z&5gUP*FX ztT0XASKI83yL?bv?>c-b-^SI8;*bb(g;5rHFjDCB{A{Ua;Cu~Jn`9p)ohg# z8a^RyU1I7M(jt4BZ&y|y1_%jZ=A7djPO*XLj8O=c~ovRR9|A=T1T862CmPfS7tQyXVpNMEN zEtI~EnC1xau`L!XE*|QhPqRA7>VmEaeyTsDDlb~S3TEC__bBB8(As8#+{IKQCFa^L zu4`Q2V)aTu4AdIQIblk|aV=vvu5sGsx=+i6RZpzI6rYytwX)M_T@7VKpcg0sz!yha z>XMfD5YY>oy*^-9DTiJqCydL5#GuI*q-{C1-HIIo6Q`%ciZ*tUBp+YDyb#l%B0pi@ zvbaA=28U{C(w{R@wnkp-m6kQNmQg7+3?+Lpr@pqf%J0U=K4Pk1WC6>xaO4qFIHebU z5HRtG3xc5tP$H+7+1ksB`}0XUQh2{5D!_pNlp za{J>w=}&3l6fF&;I59k=J-tOF=F6A5tjq5nBaFHtk+1?o{f@P!g7-Vt*)EcdLUZ-} zk>V#rhsW(lA$xtXPe&XVc6RxlTTZQK4x1taDdKVaaMaq6{p5#$gMGqaj8yHC2i^11 zSN66%{f#fYb?~)9YN__V6EiuWjmrDGZ z?MgIo+mkVi-#Q^-Wh`y@eHU^%F=PN91_ z&}?V{@(x)KBjhYTP9wa-pAJ^S!Y^W^S_Gh}OkiRGShe6Xq`$v>+43X7=#e5+;A0s9 zRPd`L{${^DVT~ztY3T(BprT}LHY<5!%>rA!!Vc*hK3*`wuBL#`Q(+&1*ifVb8J$R< zd%?a6$ER5iu*RqfF^|3m!7g10%feHK(m#HWEi1fdt#!eqUV@K2`{t23^r+|pvVe?) z3C!R{8m%OuQAY+aI>gvzAn#dY-w7}~{yj%ADZmzY*bMA>%WmoQ>dSpzAL0UnF6tQ~ zf#5K~OT8|tzH4m1I7>br{$d{#_Z|*As#F+W14xO>BS(e#qTGT zzD}q~V6bdx!AA@lCf4AvON4|+w9qh5R!SYo2&lBTSd|NtUd_#K2fu;7_HgORf>Ns9a1IAZWn}FW`YZ(hKb587jVkAtvKOOkA4tB_G2Tv7##piNQx)RO#sca|to_2do20VY%cHy%r2V+4_X_bA= z=3DlKxC8w~a{5#MJ|HrZCxarOE(J+f44h)9t=T;4wN`k6j{tF^0-=pv00#j}w3SRx zK`EQR`6VMPt@?9Y1ja5}{r)?T_Rjv2C2w1`sVs^a{B;jpH&k#Cm2jxv9)pL23!5KL zez>-Ys;n8ItA!g3QZ30f=i%O7Tl%rN^_l=sJaK?f!3eFz?_U9kk_-D-Rxi60sm~m- zRi_Z9B@*mwwxKOrU#OB7Mnu&Gfhp=REHkURd22&BXf?NB9KX$Iv*$mRh$)Q}Et|Sw z8v;(*#Xe3UP}3nG0L-FJR}2RM)B!CVjSlEJEWFDdUThKHPgh2vV}~`Wc|dh87f&BH zZ5-`7k?ve?idsjb7p4W{MIi(h7_5#O#&OMY)SB0hmFZji>_II|(FL$TB+#l;AKIS& z=x-iB^z!$t@6w_?IkE2EUt7Im*^`G$ci**Ww5|2PGsh?N<)Dh(Ws3|~a0M3={ps*a>5C(&e?{8fIJ380ji;f3bflCTn$!Fr z@oyCN+wydszx~qOEnqdIrqR?l)fW}v?@ec2-bfhw$C2)8kH%Tu34zEHPk7*>&NtsTfnPK^TMVDx;BYQDHkd zXc-W`vKwMpfG>4Y>ez(c6$5b0^04JcrB@U4J8)z=B0+!*7zGs-2m~ou86OokawXn% z3nyYjjIL{zSiuP&gSZyRysNsb7cmr(RMkO{%DMKG{jeQf&4XOnU@NOXlooIR>I8G` zQj2h>KWI-kDlZ)Mmw9>B1Z>n^S<#C%0Ifmc8m2|A$GyD}vVi@TA-%{*tf|3Lj@_#C z+h%6XEnm0P*6B>xO9v_n4(y~U)Ml6`o&j0La*GgxzArqk4FrO&&FJ#h^hU3 z#VL+Wps7GB|I3$0_^nO0;;F?T@qx+vrx>|_sUT?;8!ddfgE=&wPFK!%v~VP+U-ifL zkTFwUr4uJ3ViTAL1xef3V2@&%Oy%E$Sd!oxy{p@mIJ%9>_IjubXTfS5IKLAq+?G5Eb+A?Hj0pOZv{#xCTFr$_Ca zfdiep25@p~ISr~D<+@dpKpfgCn>p(8C7-udV8}rHT7g>mT2#qkq-q-LA_PL7&y3Cf z2^vhJjyxZTs-A$$aZcA4g1U2f>2djfg|IGWIAt@O-04pa^@dEGUQ-zlf#|I(R)xCO z#LU}iP}Li|LBto20Zj3uJw}~)y144$tH(dzhHhQ&Rl`IEbulrb)hi$YSrDCb0+A11 zJX+lPu+1-{>SFiJelrV4y%2H=pxGo=b=}lg{D2fe2q-0h5WpZZ%2N1M!l0EReC2@U zoo5mofCErx+wJcWSC`Tf$)1+oXAT!X6eyd@VYCvKDJ0o_@tD0D3=X>(5y5tR(0+K) z)>bORBw!*TDIPv;lGiV{e6QO|2z0jTXX0*uPnH=xX^##e#)dkZO@+ToQ@W`1 zjucfLiIZy8CdcbDBK*|jg(H}ZPV)o7OTwkuL>|Is!I1 zmDND)etf41^FRq?m%KY401ho2wA;?Lb#*L3&lU z!J&(pv0y3>%Bc#cg}=7jz9Io%FDAnaS`ZkaR=^s&9H#uIqa_9tLSH?GpoWG?wT)$3 zBx!a38JZZFWGz==!Is|qwyNwC$1{hlOXrC!2sUsZ{!=gQ+4FZ4i~UMIh9FJ#m?F2PHUe@ ze->Y3Y@Rp&{*V0pyMOinyX@LKPfxZh?>y`Yog6`eU?YD42<1KwHeHYGvZOLEo&f@53c&v20t^`J2-2&n7+t89)sT8v4;CD|bfO&7q( zMA>d|lK2P!Expoc?E+hTAs#qnfBQ+kNfSm(el4Do-OqdMfJLSFuU&fP_3D}j98t(o zgXuXkBBT;n*KMz%Bq<*}W%l9NU_El|Tkw^~z=3*~&bROJNyhB>YprAuzes-c+SJgH zR?aVqF{N*GHBtutXU`YUNkOA%j(q+hkY_A(0qPdQW|kQB27e|CU$31=Kee@Zatf6X zZ1F0^SH~D#b3;0C%6{VrJ31oBK-0c_+{zMwQ%tcyul00Kvf3P(Z%NNjAP&O%ZcoFOESLH^`t0)(eoy*h8qaNx5V z+Qq{=YyoSA(Bw{aB!WN9p{o5$V^^GXjgYT&zk3Bg7N(Tq3y+hE}+cSn$OO0*D6eDm_4KSzPq$G72ybpr1KxnF&H0z<#)p zaW)b5q1x|Skk3|jo(VGKpFW&!Uwek53%#6T0cPZwhzA0u)Rh$^&t{3%iz;YoX%h#~ zc6;!IU2xPSHqn@{!`6fJ=P9Z+awK6zK+k+~&Q6qz?FE!`_7FpOwU ze*VVv^*t6LT6okfX?i(zsqT>!Q!ctIM;|*1l9n9^g@Gglb+e7Sx2SXxHk*F*_w-tU zczn=49_VONSmOe*!kSyAtf!^;-r#>PDs7Qj(qUZ~q=mz#j}y{D0GkLy*sfPOrs!sg zpLjGtEC7t43!hz`@Cbt!j;5{i-}>H{KR-G;>VlUJrBdnrKmOrT&lYIH-qcgtv}JzJ z)r(&J!HLnqD-RY;gK|UTr%R>hF8!M075pe;G!5)ZUpbmyTAHqDOA89_w3tj|gX!o* z8g5NprBt~&_M?PTQ!HRLrPhwLqS(1$G^cr|?Q6(&fTQE~;>|scO)Xd7eAjRO*8l!% zzxEHtT6^9aw#+~-AT^M_$rQTpqErH3X@yJy4d^1FsFyr~yfPOXP|Bl?)D~HWU38Ut zq$0wytru+670Sy9M;P+Oh9Y?!K+76g4@XZPGOdXGmet1p;;SYjJ6QPZR}0q`Pvr@2 zoKBWaMp)5hwF)w{#4LgP{M(2qKXu4H!xI~RaRCvJ@Eg1`QOc%^f#4!QkT`+~5K~AR z6kQZN4Gre>x+PYO*@9hVjPzoe$^_k=m3wB`#}QMZr9D7|*x$J-edP_mZ#ryWJuB)i zuUM!Umvwlw^6Lv{?DClc7YqbnF=Sed-@U=^BO-}+x^Rjsy6ooyeP}2$Auti9w9{~~ zDI=OycEc=CFvNh?Y5zTF58s{$lM#epJ)U;^7g4c~JQ5IWHtNa>l<^(dhmWxQ0!CDL zagWTKTmbmga_oixiZ&H~e#$1hgtvCVS+8c-*YPBy4R%oyJ$*={)5rLQ_g^(8w=4HQZ7-At) zgI7V2Y&__`M08@)Q z()ad^@A~#<2Txz}A0jN3y03Zv(yMON9?>#~Z9Vfgylct+XHN|FUFrq3cM3M&w(tZ0 z_XoD!|BG#%i@FxA=vumV_;g>15`J>>%97R+o2Ub5Hjete8*R0P zq=pQ_Rw;SqU?)W7i>8sA1SLECDuLjBy2tgoT>jzchDF&=M5{_9M%~ zq^7!WquEjRmn7S^W%gw>Ksm}TuJKE>xI+<040#nQ@KMJO%!$eL_ir|NW=tW;DWF1t zV`(}SOG3!MbS(YG9Y!!ADlM>6Ox>^r#_%yEN0ctKz{;K>TLMFC$4dJu9F7)y!C-}x zj|DrLK*hiRlw^w}~E$OMl_MF<*B?UiZ zDEzKndL}#|FX|0Ky-XKXG&VV-aHlSys00bqDoDUCw00Q??!q7tK`%z&(`uw}sDAYu zK_LvJo2#=SoXd#Lz0_iwu39KCVdgfg0?27~iMJg zy^0*Q@^gi{!AyhM%2(k6)n|o^xPW!>#&8K{r^*F~r0QHhCu)N{XV@Vy{TV>gjkh4kmY=n{k$tc zss`%S4?9ZWQ2!aTImE<{h}D(nT4f=Zr^>-NnNQ5TMc1-jrBY%3$tC)1FThv&hr=dG zc_hJ;^_hXD%7Q&iO$av0e|RJqWp2zuBS0@`ZnSKz!wqNWYBMXCfai7!7s4hOfTD3+ z2AY(QO)?)X7Q`POPwN(#I!tiV9mzFzM9@nZsIa|xs*pT7VD$k*GXWJD&es|c?yP9= zn{|Rrf!3z<$IrUFe^qU8JiT_(#!D|VKvWPoQvE>-AP5Hjo$GD!h;tZSiZFT9)7pSm zVVEg@*SL1kRTxYi6&U5Ujn|7~q9jzD5{bTcj*R9F`*&}!r6uMB;n4}p1 z?)wo5GK)J*K;u=71yn%8v;xsfUd&Plvs)Y{uUX|?K{Bz@(kM)wYP*yMM(tM=IU7yS z3-mZ8kl(P-%rZcj`gox8P}2zDtnaomOG9QujFZG-tElK$;KFXeR!$^(|IUAIJWSD| zC8k^;OzHOOaeHX==GD!Qf9;D$-`r=SmlCDY`|i4}ZNu$0ob08)tCqL)+_dcQE4`9B_oCHnI+v_jfAjmVzU#rQcm2%f+dsVZu7?+{zPfYK^2O`7jgO9` zRBmc*Yien0Y;K*L7_+;{S8xS$gvm61Fg>{|eRZKdwNSZJF)hkzk`L@7skb?GG^VqE zFXo3D(=1@M&g?0+8KRULho<;3)`g|DxBT|U?)>o24yO5BVA;qcw4@#~5d@cCk^=1_ zuTuD!t)(JhX+`S={&jdkxeOIRo97y?;pE2`kJ%TpFro`PxtP!1l#y1M_*djG0Us4; zO)!ZK8nJMJu<|dxw8TvMQ#{y@^Fx-OiU1sO$- z1%wHM*f0w%0uJgZ>il{14Z8yh6FaI*v|)-C8CvA+QMHoQ-cKE}iDg3ogA$f6c99ft zgf6qeoLIn>${ZjDpfL#j^kI8( zQoGRVLReEI3ojhC(P@`11*w5RhbeVw)Qk>72p6&@h|}mRn*^^Q1xZFL#7`FHh~6Bz zmH;P)KNV_wDcAS+RV!8*pT?%TXzRwKd8k#dYfwzt$pvbKWjXj&t5?&YHptlm_34cq zK5c(T!pOLNb>l*Cfzi$iKRjO{yzs+34Y16`CI0M%X*|w07%kNY&iN;;)hm#PBo#hr z@X?n57PJ|WgUe_w6l(4cjsl3T7|!v*)ZSds5{tif(GZ#_>-F3$NJT(W*HCGZPV}f0 zDp-)OXk}l-n6D$qlmui4nh*10Vi3p6NQ9;rUF9rn15NU>O`VYP(D^#LXvp{q*Y}sax?OiWs6N5yx*AYr3L5qAHk zbYvi{=_<+{j9?i8NdR+SpS|GI;_&&T>r-CdZSY?&LI95xuL2xgH#eGhg4DQ-@K>wS zi__1&VAu3Nea@D^?pSNN2LWcA>g}QJb_B7enL+(wcY#gj91dm)1U~=RTzN4k$(F{n z|CCRZPZ}I-vPqHMb$(V;F;kf8H!%T{5VZD8S*30cfxq)N-9o4+zooLmg_zDgXksIB z_{<4>Kno3+8M$T`3(+MlF%>GtIhqWXfKQD#KmXL@Z@&73DO_4Kue|Yn_r0Hs9%0+* zSNSV$X>Zzm`|`d+gD3X+D}1lO(a==-$^YS|B^$Dulq}KExNZI7v5wU%uetTU4}7ra z#t&_N&xbbM_MvO;{+X@!{LJRtKhm*qMfZx0&8_X@W5ez9=8ca}lqYBVbB9-O1=Sc% zeXpiRPNqG}(>8y!>fD$}ql4+lWEyX=ZxmjHSAbl8n0f(=EH{@6SWT&QARUQ!UFD#N zXu4`s|Hkg(C03pkUa!303W!5%C4s*;fPA6sfS6sAeT@bgHZl~6)rt+hI@7;qh?ds8 z4)v$Mc+u7c7Ws?b$!O^6b)uHt{FUUWponq)fsYnZQ6UVD8AnM9Ysy&)4;H&>VUn^% z*5m@)%v2mr;#CL;`rj7+Z7tJw!ejaCgK6+F_ucY}R< z5t>TICezg!3+>wO^sm2Z+4)0T?YXL^23tmX($^85Kk89=v4=IOVBhk!&n>3jx4|l5 zI7CiMJb(3yl~RBhC|r+|xOHF6*J!@*N-<1mrs5lWiYevL045(WBF!OyKo?+&V7|1= zzQjQNgLj1KqQ=0K6#vuH_JUnal)%Rv`&k};XbVS($ucr~dToiCBMt}^4%RTm04e8m z_XG}sp`}NVn82{6DlU$T=J~t(twdR~p!g{#%B^dc6eC3vFVIq<1^5zyO_=-p3IdO` z0J8uwJlc4N>^Zx}$~)QtVv1IF*yWtTozy`{syxGv#wg^pBqe~lHlTnN3G@vsyxgta zyoV2S+5BR?EFfKanaEy$_m&X+;0UtA@Q22VzvxF*I{GOGLJ^{N^{NUItMVeIHqn8q zU66C2pE+W)Dr?bIr2^>EbWH`ia?8v%L#{O*D@}hIGxW=Zj8G%_xHELiJWHi!O=IcTb`d)8aF zRshl^uj7=gqt~bdU7na6b}>jW7}Q+ZQkuU;fncyt7feD$j20I#6&SmyU?0eWU{QIw zMoZm8HTIOhW!R#SV%b!epOZqq8Du%3Jz(%hHS9hhRMyn|?LOSOk zm2`HR`4Krans%)4$@E&V7>KW_DJnq~C&ps;jWJ*FUhvzAbn*m@bayrk>L7-o}Q8#R-ov2=RKzERlPoVYwypUgi@m`tNny_%|Zt{1SF$;mV}n*22ZH7K7N z|G=*=v+U;e6_C&&O4DFrEDFlfPep+amb9%O3gDtkaivD!fVk!SdO{4Olk@-sv|c zsVgYLky+nuZ;WbdDrQ?CBs_lXI(s=OVQU~*6M=mUf(@0Q*xR`qhqhcc6`Mf6@d?KYNI!fShscj6V3H97 z999O)uBPJJCB}qc7h#<<>}qsuexM9k!<1Sg5ur!YOc9R%6~O$1C#){&@oEzR3^F;$ z10(@n%yOj+8>fyvZG=sAWp$NZAqW7CfW|9mPQ=$>MNgK`$UxvIR z9g;%Gun{VAc%OeX$Ym5p&{bXeu4uK!frp_5pEvi()NKFyVXT7WGYN!7N@m; z4|U~pCUhRH=v+W%LniMERLR$s$$<=4gxZT;Ai~(KhhJRsMD~q(3=?IX%CQDIE3ly1 zW>>+}!bg5@Z+h@LGeWe?A(BA1)+hXKEn`RCs#1>=aL_sTc6d33J6fFpSOdsBwySs7 zw?8v6eyNu*nip?gy7NK)Dg{0c5tSC#h;#A$()G9YEM3&}?DtPF`9I=VarJ_K@LStY zP1JeokdCt204Izlv((VoH`d(R-gVQ}YbLtZt=#(VQpjF-h zPaU!aAg&dp5)}+US71I}3tUF@CMLv4AGB@FCIi7nx`AdxU73sa`sHRBYsmL`Hr%q< zp8CNx<;rTmR|kzQpB_n(7-W|Xby@CivoepWfJopF0|fk!?<~^mk<;eo+xu*_1&*z? zdC@{zqE(CCwkeHL6Z1= ze#?qoHu&URsu65j0p>RX*%e`nRz59MdS+*(FBP?#@J%c1St=W&4~p?3K_mp()M#c= zSIu^0z$a(ep6bF{bECW4T6soEFvYIM7pwj^F9H3WE*dd_H=F?9(y0jsNDII!t zF|I+uBuJ(%K-quIViUt7^5FK<<7+1G91im>NHD=1Q`Khk1xea5*eV7 zL^DR>frBn!t^mT=S;m!azR|$uw({2`@L6eXw~0vrNGnVU=GUL)*G+EF?L!l!LaR-| zYtq;iLLlD-xlA#ZN>{ClW+qf}v77+4L>0~{kXsYY20;wD%2etzfpo+8g`g^=Jovhx z0*@!!>I%=q$W-8L%oXD|0fh55J40Ly`xj*aEB=1!;=;!;R}*njj~1Bf{L8`Rk1Nhh zfMOU_n=cfKqbqNSiO9pKvM?18(}fbTD#_etPOzcEwyLJ4BEgej^tjW=xV_s_LF%dp zQwSrG9`^Ow3)hjsX4S=!ky_HqF0#1*+k&>jIPKOY#ShG>7s+tVM)IHwj(Xt(#h~5D zXW@~St$(zzsa<4b7Tril>e9;{$0x013H9fX+V2oUNQ)LnBrVc5yGnr&txa~52U@pr z02UOIyvTw8haiExz^8rjn7t(dNto1sAG)TnK=62hCaELS)oSwW!k0Rg1X-C)!^HAe zc3ZUshcPM``Un!^5>kDDBo+t;NuU;{N)3a)Pp7f_YM1IWIMgoGJ-EgZLsod0`p7l5 z@fM7Q|DDFPO!uR}rWI=;bw%SPWW+~F4NvwF!UoKjoEK^R{; zI)p)8t5;xf-Nyn1Exj1TyG@8|-LcmGHi?Zc1hhd@9Jf=o`#KjYI%;&8Qpt)%EFi`% zA?aAi>}h|MhS{%35~^-*YV2}Bb&HH03lBsVFbLo?Emr;}ds#cP4RI!H!tk>WU=}T% zU36h&c-&51fI57FxBAup9Auwh+8WcdU-{F~q5604FCMmR+44L8=%FXxEa@T>!vT5= z{_eigwwt>ryW96Xd3lHb!r1S4YXS-HEdqe)H~YL z(Ad<_)ZD&cN!QYK^H$$@)vZ6Z<@TR`-%mWa{N@jDzU?C|?VVlA)-7DUW#P(ABZGZ< zQ0oe=U@8ozldq(|Ih78rNZXr}KiGUOOeD^#Bjr?XNuB<={)Y`Csh6$TIU8+hA)_vK z<_Ha`c{rV%Ok>6V*(kL)KJZ|-(gYU@QiE1};@$u?-ctYR+qe`rHP{d7vdcz70ahLD zvO8fqT3V^mg@x2_kAZsvqW7so=7kG89udI8F2BfAM6F(6OdMg2eDvHA`zb&;^2=pk zFR%R2R!fdBF#>aCmsMNNq}VYe~?k*kHZt)6Gsjl*EQ* zti5Y>dg7o}Ce#ni4{c|wS>Dr|+M6smIP5-lz@FePW~TC3`b0Use7sm>1W4-Hd}Jss z9`oZxITXDFLjc5&3(8T2O)p3A)e}Vkz>eL?;*qXvme^-+2%Ksx7`NR@LCD4~PNl0u z094U&WWT-71lZui+^lli7mon+e|{!y>aksVY2g4Shp{k$OdY}sNdhqSEE0sx7^g(# z9b3?XJn9`LKw1{63rLGyd|^|^+UyM3DYwoSnCL=`nZdQdF2e`(B2O2sKo^^|^55)2 z9wuhRfqvygae_%+U9@W!6pIcpPgUOho14;f(JPMBMWZfGIMS{lv08orj?QOw%{H(=m14en1?Zj0 z5Y8JH!}6v33gLxM4IiR(bmwFuW*84*_$4I-a>eP5)a|NL(MzD~qSkoTZg#2{(DgU| zm?cjerg95nR(a8`w%84@E91eex)kPGq03bmngqaQn#&(ShNRj6VnDj&PHKp)zVyQ` ze#0QZ6!bI~Vn7?o1EmOpX~4Wf+r{sEgKm~@ee`YP>726DP#pzO-ADiHi6fjwmlO?MD+e|`+&9`xJOrF$cja?Y?{MkX9bNa`+5Ppe z9v>V2QG6_@vAN;L{^|7#w)#(jI+!kwDf6RK1>|iG351wXE5;wjhD8mFI~v{`YMVyW4Lo`4xba6gSn#1*C@4BbI2mRvaC$R7lr~PHVLRk}S!BB2XL>ld>4s zA`qE{EC$3x3=WbcCNew1dchVREdZ|^NY>a<3`$aH;VX>GNK{;av`FGpW~7BlDkOm6 zag!%mV-_Ydcrl`K#WSK*`ObkqAjgJrayl)-1f$rnKmw%(gO59<`;LGt_NY*I_Sgj2QCuK3_KxlZu7!fsGaKwm(ndFI z>i2FeBnc0LUw*^p`7Zm^FdN-C0+_NAXkPJR z1j&S!4bzVfGK>P7*x9zs37=II*>r$Hp6!9n#m^Ti60~GTVc-`nI4BZ19AwZXatHv8 zePrmO6%cM~wxekl{QMy*Y-+s7%Lw4Sk{w)tV}pgfK=0wx_D*-5en3%43pykL7T9IO zd&tAo8G^8#KRzNLNqV0=Wb35*R)>VU{U*KQK}KGV4;D*-dwOljQq}~S5|KlSVu}XD z#sCM#&OF-%_{#<1lva^cp}P`{5SDdAIygCU`5avSTAPf(QC$H5e=7B9fc*I>0{(3C zciRK()Xu~7nBMRC1$G_?N9Qqww;h*m0jug@oX6p*eOFvQ^|Q4$LwUsV)hm!I&ekc2 z)o4L<#IFxsqI0ns4iFiMRms=Y6|?HPP_+x4n-WpWse+iaae-#zYAE1BOC(jk5GJqa zPY6DTd+XxD7m{p1gf?ZS+L>WDm~yHxl(gWpt5g(XQh_PU#2`YTo**9}qpo~V&?+DT zbRi=FY$~S`Dv{74j0%}ay|PIQL?}-6A`cd|+b{bN+oNyTH`lO(3^g1DfdWQY7No0O z4p#X+Nie@P&2KK|*538tLn||UfAk3&KBlmYov-dLzEm$$5f2*zG7b%2y#&eRZ17(~ z;gAiD6*6d13Sd@c3cE;Bidn(oYfR`e6)J0JnHj4~yz)SvQVc$r=aIvt>*eFN;zP7_ zA&eL7>ouj4M-mRk=@sx|^={?MdU!zqF#wYjjClU|S^M1|jmW0`pgsKcMSmtAdBgw) z>0;M(5lwby5~hOGl+t^|f1eRPyIM1X$>=DE?YLJ~n$QyeE7Xgttca9-1-Vl%vlvQ0 zwk3V_4HK4YbC18g+fIa zVT>RN6Aoqc%1(R176KT7?;0-VuhHe8{L#}!H#Idq`{WOf?0V89E+OW1F1qHPpSgNP zy9Q1qKtxA#x@V(>%`TNwV^4eMmY%)OoEYw}|9rs@D_T36ANVKRx8A*CQ%_0r10X~P z2k^I`gPVg>C4{F3sVx_a!vR|6b#^XZ-Lv-EtM7b}BKz?c2B`tE*`rU6BjW8)vSxiPk41$cN7hS=Z zfXqS@l|lEed^iL{i=raI3moJTP@YwVBXjs3)t^J!L(YAz}%hYO_FAUYOk_(02mHD z1hD^^7m6ZqkN?i&{1zjA;l{!*E_4G`Kn$Cx;}IG_Xlq2lZK{hfz~8=R1RTvjy?ns2 z3GsyTX1|p&3;j_`sziEK$smkJhJYf3ji|CpBB#jUE$jd!*+m#aM-~CiIo6mr#Um|` z)BvBtEaXwAW&QyKrr2bp@ax6)OULYMrwGJ+QhZWmF6l5+9ABy?t%4zgWz>Om1%=H< zY~mwBFmfjjrh=vyJDh3AA31&IMYl4Q6YvlBo0tu%vi=3IAUm4RAcU|6f>v*HgVw9h zSfE807Z7ZUP*ATBL_qR6a+|ew{G2UTuK*mqou)3rPAzKraANpV#Xmi&RqP*XKgaUkh zk3F&hWGBZofx%ZVZCnIFsKj4ph=p;fs5ii8@(sT551$Anu(vmz94u(sxcucn7*6<= zaruG((4;Oc$wMQrU_wO@Gw5(9YjBh&?B-SF)j5D(3DDBZ#x)K}nG#bnFfFw&G7IE> z4g^P20)Te1Y!6zGPM9vc5F|YO5(dJgf0#;Fav`&8x~&MHOT9|xsL(~~vcbAj6;cS0 z;E|*kBMJb}1+7uYcynX=tCvj?z|m7hn59bwk;D$+$xe5hxz?m-tQIO|A1j@S%t1buJ_%Kr4c% z)^-;`>O%-~c*EwlMb|Gq^wL}XC;U^?4>wAwsik4lt;>G>U)|7o_2N?L%<9!t;7rh^ z21&Jm3iVr;arsd;jIw9vYOA3dB-M_)`|AmOh_+Z)r_cM+$+cp4f=M>xrTXueT6I`tD+ zZ5QVWtWs(iFAXr0#s1kSHKe|qdXx*esB&rL&paLnwgllfQM9IW{j%bb4WLVkx=3v# zCQ?lBE%8;V7i_{&m-xDp;8e>i05OEk`>-WJUF7+sy`sx>b>Pv0z#Sz7>6Mg9wV?$uSWus{vPlDKC?2;iQ#ONiBvTgJ#4lfu%3l2L- zQi(}Ssg}ZnBH_FHP2IeENro7kXyU;LyGVAm+2j1|_m39uCPxL=O2=%}QP@Y4I?WvE zcb{EXHt|?EqTcgSAMCf?rw-ZUZzsJ*Vgt+`T6E#vI9{Kq#S6L!G&b1Nj{pJ2xNN?? zwS&3~0V@A%mKI~djwT8~=x1S&VStK<4HDbMFrqvpB5LVNB#D>N_6*bUw5s1-ig?XKLIb`Wuyv{r&XLm1Z0!8@Ktf4)vMug zTb={zg(m{2l{`#s&|zU3NQ{7KwJV6gOKt>OZ>NC>?>S_xwyUnB4l(kYE;*`|3`k%a zOy7G>^|{44hche8(9R_uqwuyZU|kyI6FRH;tt(u0TGR^6(7s<23gnK0fEkyesch;3 zgd`U@L916lDq%UdRu>YZWs3_CIZ2D(>J1`x0g}4|sIs`d*V6=!wj_9HVBpYLT z1;Ei1pY6n;zXY_DUOHX`j2#jlsmN-JN_vJ#ueqyaUo7YrmU_F}iuDNWBc@|bza+6n z2?aDQ^g=}bg9cc}7rJ`E0ZhgV^C5rt0e0k_tr&|w>MQ+wiAJ;#O6roqFC1{czl93> zm{I`&s1Aul>U7as3AL%v)!^^~Uj~VmInjCE&aMSK#V$xd=CzvwqFKh2)Nj>(!xNUI5BwNRYm#5F%kh2F^tni%Vs0>WgUXI zMKOE6a~6&U2O*O zA`5tjDJ?)er~p)$9A=4K?9=<`>vjTv|B=s(4G);ar9!Epq36aAty{ZV!v_!wQbV&N z;xtS?jXZy<`GKEYzWa^A69@c#s+S3v@hLW2@Ub-?{`0n}p=DlU`@+`Fp0@dHi&{Gt zwXRy-*12;2%4-(gbKjEgYZtG(aq)fkFIjWXn!A2!>-9gjcC@XPvz^1FX$j~I$@wDk zmfrX#=2WP%%%7y*m?qTv4fGB-^o}+*w{>(bUfs2H-GXK7mv7#&?8XmVb<0m~-1z~v z_61AkFIq7=*w4@>#;z=8{m7x5CXb{SUQU18nU*d}>-|sSoG6c^K2mYBy&7mnFVec4 zF={_dcdFPq8y#uIMH^sgs8s*W@skIJn?JJh{;Nw$XB>~AvGlzIXD$~2n(_deB&GDh zU9~C~a>s>9-eBj3dGH>>$SB@J~OHnYr?bDY`4eBZ85^1*kYH~;`H^VxS50z0R6(z;`@mh zmkTNff`R5A?W$eX&Y^oO*=E$oWI6y6c18}23bjK~#-8|DdQyAk37%mH|S1wCG~@ zdH)R`EZYrfRTOWgZA@x_pJvBKT>9V1vKLJIBfL2(fg4xgwT11EcHln@^0WbnWKxP4j>&LDu+%ZO` zT>|Qb0GQ&hgQ$R_4PSx`<{XeSB`-yQaS^#nEJKHwXdqMC)h5g81+t$b=ZMJVbL7li zrAjyq(pHTt_f_jRgo9rNPNC@qgxqpnOy7Hcn8s=F&l%Vp;*vSaQ7T{gai(*DlR%e@ zb0S_YGd*T*5Y!c(m4#`L+o`oVTi1L1Jxvk_F$gfd9j?*jp(^ASa&;L5Rm|8Gtrp9+ z%1}=@Z-HW%PRk(kC9Ucv&95yf!B^7O5}_V5nOovYK$E6&)I1^5aeAKhvuq|t&@wDeXv734z9 z+_jL01afx*#${6mA><{3k?~LYb8`^2y5PMV(!Sn;rdM9VV5ne|a+!Q@F8p_PWc=uW z-O`j703Xn$7{aV(pw_tUR?Oy12_uQGU)`C0-&gu_NxKW`i(g?@0RY7Y#D9b-ibLSv zMugCCOr|m-2+k-D85j3Wi_#I&Tiku0d ziy}<-=wRNl^yEMJ+4t^#`I?6k2W_qlv)~E+nbx)8XL4Y&1a;JhOw6B&L#8ab+t4$ z`^pqOrBY*4$-Y9@(X?=W!?I5MaENeIOJhfOYv+=-){dr9<@@s*LO!VJ5ksB|Aux^P z%-zi?$`j*b6Qjeuhu%1S;^3Yq9vK}z{pO3`9qc_?E>BL3k6z-X(kply!JpKcwDYd? zPrH+UAf6fKr6t{IeZxEXHwydH{!?jxv2!+7q+2f1mnF-oe7N-ZWV!ycv|E4cs=c=? zBQ=oSxF&FMP!0lBSCZ(87CywvuJq=qGkF}qXE%(9M*h{~#fN8TAOUD)Z4wYJl$yoV zt}>=ThO*KnyIUzEf_%xEwCI(EJYCAN;ZyPc%Dc?Fv=mQ!v%OwmUtb}Nm>D5` z>y8&jD#rQj#ijm!SJaR(0?gyt^;Os>PM}GH13`=Y@BhFSj|9!Cdg&4ep#FOg6p>>P zAfsT4Aia(7Osiy(%bkn}^vb>hi(OhE9K^(xxMm^w`^Q+9ND+t=QCVS>q5Qi4;yPcq{ zAq?@kDBr!_enUi(0(@OMs}f|)54}KFb0rlZVWGLv9!J-%9I2Jrx{%qh34uJ2+1xnz z=H7JYI=krlWA>@zskM9`C)%evyZ+D2PGKv&0m?YOH{~ z1NEW~@-X12ZWbgUv$=hV1yk~hIV=#v%LSdl}Zo&`tPj$iGNY- zd;E%rXaxpKoNCW-PNYSp(XT&p^zbXaeMg2CH;kR^FCH8^J(d=CH7;7xI?&qII={K0 z(UzI6>PdV0O3m!GHMO)iH8zzN%q#T`CwjN8Y%F)S4Yss4&uiS&W50)(`w~`l*$ETV z4r*nD;B(d0_hrCT!a{Dg7PY$5yN+HiQ*V255W!4NPVnJl;N+p9Q^zS<`i~!c{ri8_ zd+7BeuRSq7Iy^DKMbQD>T;bCC-F^0LJ5&e~NKygFvNE_@pk9p>8kr3X&D4BZi zFhIy7b7HXg*%^f8PGS-e#F6(rY!G6|NEe8R7EoI2<-F zFf5-fRu_3xwa7hAZ)X}*>1uZxgERCMVl$MVEoNetV;2j%7aJFQytBoR)&kbdC;gm~ zb9nrwG8KFX06o1^tu$4EE7Y0=1udLM`4rnw;Y4qK8>H4w4)rRdK7Ko5*w4fCpyL3TtMcGsl2p738N+l{<%1~s0%t{>~ zDMIR~G&b08*&s}P5FSHP>H;c=QM^Wl7f}I)(b-ZwW);}mXOC{>V*wBv0uZ5=-pNV( zbsh--wB##Vu(vn;@(vSF%uNmEf?WiFyhu`(0}^=Qh`qg@uBcW81t5rN@@2E2x{$Fm zv62Y^m|K@vy&~g)F419`-JmJZ0R|O-XdqKSA0Z+z$%DuK@HfByC;z6{xm39RfnWNy z-~NLLK0tUxtCNTL<5PzaW0?x2lAXzd6lEb=@nh^E6Uci()vmya5ns6D!_1Mhzt<=g z&9iiy8|+cIK-kQUw>G8KU1xS1OZH&{)C~aC<)3r)3N3(Qm3r(>ab92*%LDwnf4x`H1rbf-0UrhmLH-RdXljL?!g7Nm_Wspi$Y zmpx9U{rzvp0@heMeYEs!{YyY8wf*6a($ZEXnlJAvQc3)I>Q9w6uCZ0ugzh$b06b4O z@PTp=yIk=C;z%IRm0=J86xAgQprMprJ5jjH^AvO>Uxh7#HY9U?#4mbNk0k}U&V?t- zwr-^ulr+Q9lW(R6{fl5o!T~TvNDQzmMRh4Wc0mY3$i4&sGD0pHx{?XY5`dsLB*U`6 z0Ch|?F14Woa&?U;m_U3fil&B%U-i-SMlh0*E-LUrl`o)Pwc0JLxvn^ot0b92S|I0# zUl0QM#6k$+6e5p2=N~*}zx;>_bS@e4At^8qI++8K%w=*q|L75tAOz~7Rz@69t4LSO zr=S3)aU_9hV0Viv=a?jG2QEzGB?o2FzTR_-O&{0{eO2L{gS~>c3-6eBU~vvz_;)Ph zE3fASV$*xfy9USXnU@@Ob#ta@wTkME%C~I;u`3zfh?oYH1+~!o-9dPlsVZO5TSc4| zq0?N0*q{kUl7ZOeI{Pkvhf#1K*s3_Ot9MdrfcZQ?h}OHMB_FNiB|u9Gd&1vhI5J_k z!h#?8f%n4UN~&+vLt_M}ed>_SA*0i`-^IiQ!`b041rX4JBU6Eh%7)bzC5P-$s*x#x9OXb%-GVl<*jlBD6UyRZi^EnMbG)z&Hj?(O-xF zh~(3c8KufOT+=9Oj;b&2t3sPO{IMCOWJgb9xg$sZg{*Dg&zzQtbTf%mWOHW}1u zH6e(B!EsxX)~NA_BG#_zkzsQLp_3DX?_O=OG1!Nf zO?7b!Vc%xYU)2E^o%pmIL=ggGAx@kgA8CI5+n*c1w681bShRfU&IdOyZX{Hoz=&C| zL817wnfJdP5&HAjD?Fr97n)M+AfpY?!qI_-=CJBl1pwCI3tX?pCJU8-bmcS%KKTTe z%{LBZpH5vM3k*jpxx99RkF^*MR}T(H935#OMtDpPaK>rZXH>!Gc8e(bgn{9Hrl^7)HbjSkwIgC@tv&b1JC1@ABn zr&F(`zdMoMSe~wHo!MK=C(_tpIy#xg{N?W1L#mfO2GWu7G+6AMjjpt&pe_O$Q_E24 z_++a8Ey9+rrmOB=z@>zXjPeDSQxMk^t}|SIb*X^~;B!%u@TxBBWtWUYilK{)387t7 zq=+!tjb1i@S@MqT%dB321?Dx`SN4>}unZp=Ng^h*iVARbB?fl!qAna^ka(=uUmU6Q z%7!WUc)?DXf*Ri#8!U)yazxP%L6)bIDjCX2x@ z`^;p6zaAb(MQqdmk_ zQV8|JCot7uM9wbIpfsNl0Aaci)1Xk)EXgLj2w;{0hCF!Xguge+t0_2EHdIzg&{B0o zeMmKnE`voEQ^3v#KeZ5}Wuvr{O(dz-%)1{Tb6xq2!!9w4i5OZKkp*>$u(9|&z)`UT zKU^zXz^=Mv2Q3JIF59z*(@iVwaIfpO$Z;24qB&c_xDo&K z%t2S!I()T2jIAo9TJQ<6uG4@lrRz?l|T;VxhhmeE?Vwth4~ZJ z7S{;<(Xw$pCCzYw%P${ct=1IK1uMFSDu0c+Rty}Fu;ElObK`msf_5V?$w#}pvMja5 zzjDYWfRFm>MMb%jT@Y7NxuX|A=z$Alh+kb~N`y%#q+$Kc3ZN^pqYP?_;$p1`pA=PAzkDVMr?7r z**VZ>lGHsiB1ibeGP{Zjm`bI-p+$zsTm0APPzQ+FU=R8_7t`y@RYWMSgoKt6KB&hJ z+KPu%K-n`ZT4G!7-2(2!l_Bap!vD zAizM_;P3Tnpns(JsUQVI%L)Hy&)Y&0M;stS70Uqs@F|N2UFu_k*Kz}ZBfN*tEG3(2 z_)ITQO8_d?A=nf!_SJ?Bz(RWGEHGkfwJ04!Tu%pN8JOxIOgvvnJg=TWZ1R4Sp z2vajG;Q(5UMJn(&Ec7b8a-op0S{*(>ujEA^&EC!wiU2JgCeF7wrUBW^&#oL{M2KDe z-lZ%9xnoeY;Tl4{I+}u*u`7g1&acY^n|jU~b@b+RAegni%&>mwuvTm4Xl!bkw_r)* zf>mp8c<<)hK63S44{pBw!(B_(E?&QF`IeoH%`N4L@k!qbg)4X`7)U+&J zGs88hv2=Pc9c@f4&B@c@I{|%ZcR5WII~gbnIxpq|7QpXRKY&<1I?{LlN{SE?E~y0Q zLK@c-t~30Z)62(BuE`H?OD`R>AJv7adFOhCBH54>gBTeNMU??;GOH`I07tvHu;i#~ zh`%2l{=U9+(+c~_KeSy&d^rh%%Q*J&0-#B1bSd?thznh8kzoVMddQQ+5fF{L#}3+O zdkiXFDtHEEVYmg{F=SdeMQKqfT^@Pe9&3V+0M@W@%~IP{V88c(jU5^-$t-F=lYH?) z+C_|y0}9Dz;4S0ei-KoCYx?|4771BE14rzE%~r}%%#vbu`a9!c#!Lb%vmp=D#^M4% zV*xlNPGkTA6b1|Z-M)w+BZDv*m+AW26MUO|UzH;^_+72O?lzKk_^mkjjSco$z~b~Z zChRlPp>caRt0g9{yU2_>h%u!qXi*1f(aRCh5X^$lB<^qlEk#+N`Z~~WijpK?#U}62 z5OJh93JglS$jCCZHUTCf2@Cw!8|BDmz&{JHu}cf!z?Vuefo3c?cma~pkYx>8K#b$q z?!Q(9pBBk^&BeH&WhyK{ha`Y0BSMIUfH5m;LwwGXt^ge}@&f3UaBcyJk61_w8sIsR z+pi)7XmMz(TnHf$2)c@|-JFx7staL_w-$9RRQWn5BwwYKFDcB*+N^|s2=GJxD})!0 zI3Z?xUUs(DIW?=E)uJo@HmdAHbJ?o<$6p&s)vf?@hLQljwm{Itph~3{b+03}(2ygT zXvN8c3<73xm8|5^5YA_e1VRkr&}+}RR9>RL-%Bs`(iJm_7Ab^Xrk7L!=#>jv9mz>M|#gI&msO_+D;SI$yZeiE~qc-X!?bxY-k8VL`1^XOHA;(>!N zw1}J{Rg*>+7Iv<(C!r`JM<OR zbAF30X?3&|lTZj(7O-%{6cMCWqo6SepTmb1W26fe#DEAw#XG{Z7^K~OeEf;8d~V>a zBPMjIFf=rD+x;KyS-Jw6s1%Go=MX1|@YoR05C8-Ngeh7ZyS=^|7|<$}j36M(vL

        1. oZjgZXUa;4}_|BA-%D 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

          #;dGQM+VpN1b8Q!keMrg-$$~-f(=IjLF?Y}*! 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 r8Y^tK9IQoh{`AyedC6!EZtL#i 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Η*=Q*lym4cF>3nkzam%bUGd2$%Et=&x&AEZF%LU`aD)(rkx_*9O*VHeL z|2}nEO#lIO5oB1CC z^>%;bC|MSKs(N@4ri;iHI#tAWwR18YMB=yGnGkwa`$9#+9!3(_6+l-{$yXPvhj#4; z2arF?gtmo9R8F}^#~&>X@NeHX`b$S0G1Ae!gl~7zg1TP_Czd-Vc7rg7RftY^{!pOP zodfxCPq#K~#ZYYGdnQ977xlBng#EJz-?^yH&h~yE;RpF6?)`fB-kv2hFUuG9LS-r+ z;mhh(I5W{#ug=7KAFunOSV#B~IP~p*$pU9+xjX9ChP4p+2_}Ry5vSd5X9%1wY3}Lc z=1_Q6J7MMp&P=ZG`G@NU+XN;fK^TaW3IvtPjOR8}l#%`Es`2f5VxSnw_;Nd^Oq@zE z$8T6Mb?KTCa4-G%zEPe*ofq?KCfBGhZifJ2_r^_CQXd8ur)JhHOk@s@ZfFK0%mtb+ zkiqOcSTD%Dt?ees0PUj1es6Cg*C@n`H`b<#{gsPKvPT=-+!g}3P$n?B+R@{+CUVpH z_1#LQRFThXaa}Qa7*w~nH?Wxf+{3jjb>w*cLM>@ZsT``tUbBAc&$d*Ko}QPh<#Dc! zA{R-)HAG5U{nNyFchK)^oxF8d+2Kxdsq1iv&6_uMz-hl=>YFC-w%8NM295uo8_F^x((WtP)~C4ApEfNb zkbF)8#7_A-zg|0HDhISf8#SlmXl?AZbrjmmR$F$J7K7TTRt$*GJTzK?>PJD9<8src zI>!te``hbpHo-8J)rVT!YKuoKFsK%P+um9M3`Z{!&dYrQ*2BEuq~SIpq0?{LyL3S@ z5&8rZ&3M(dbIHdn&x{s*WtYh%{Y+_C5Ioge+p{ZF1nKOtPUYW)-(YsIi>X%q}O zkS;>&nJ%D>THWcPe1INy+5{%}B;C-$goTdK6RTITKNJfmAqjg})q&|O8e-o31qbG@ zUU$iLm)`UvSHAXVF1qfut2SObZ^43NhYub*a&Yc%7tY}<#Pqy*Yc9C*ul(o#{j!(+ z^|j0An`-CJ8=dfG3Ztk%rbd6VX`)@;NbIBQ%0L&g!+pwd-`t$X2~oM%z%t+$Bd?y zP1e{+4_vol^tW`-X$~(oioez#D<*8=kzt4N#F?0*7&m>=dF$p%p3fP7c){#kD&vQO z0s~qbS4=%|p!mxcG$SoDq^@0#R5kypN=1m_>$Ki#UFtZp8D<7Xd@5giy#5CC;p0=k zcUN&zW&X+n3Pk~g4{5JWNN>Kp)}+d2t0Km!;8)2>+RRG}lduS5gt`rCifkFFjr#k? zf6;NXh^!mWA@<^CKq7n(?-E68tD0PTh!0cNhVNe-P)RhOvPwvClkjNZs zpUt8O|M-j5IlX}~6(eDGgPj>8oudiiDBVyXVmZK+NkYsV z@Z7ZK@SVo@#{$-A{@~?laQYMOIh^{Z$EhU4R6FxJpB2yHQqei5(VD0_6XRWGQG4+x zk>|rq0gkO+2^nMWk({QKWsu|YBCuAB|$I~n{>avz20rXj12DxmM?7&_oN7q?JG{^X%jr% zSe9=Q>k~|L0SAV`puf4ZvPlH7WC@k=jMJ3f3WyO%7`I$F${3_U{xcJ!U%au>Wdud; z#8&DpQ@m)^$T%+vIF@mUF`hrI@(16jb(~mRv8ZY~)Z*oAznHFvztFEOofUY)n1ep`{0PAwUhf%m#4 zn4Vg_xH^RJ-+Wnpl6=>p625*z9jtqHl)Utp!P08KhGYM__4UgFU9^M8@o7M^I<{FR zhQ>)|pS&CaZZ>}Q`dXn&#>|?OchpPcWL?=ihZnAGx`h}0FX<15fJ7e?@MW%yXb9a= zS~*?s{6D;B>Y8;Wtd@6oAY{O<$nojc!`S9XlmE-tlrPq*)#G$F*3kJzlE}#@f#SOM zVpkrOb5U(+2Z%hyS6(>vk$hCRAwdGk2*h=m4vLTj<|rdnF1TW$_@>KB5#h99aNFKe$#D#vfmDtkIrPm>|L*SX z501EJ0TwS`b-}Cu;*u4s1+Og~cGFA4-SkF?RCNM6(LpN$sj|C#VZCH}8j$Q-ffLD5 z?nop<0uYPa->K|KRmxZ!D&h=X1QF=&uGzHg#Of<9zVS_)U;WdUzwAdhY&`$a!9zz5 z?mcmA?wzbToGnINR^wugxn+Jqs#A%wKezaZY=1szxL^LD= zc%D7j=;Cn7nmE}PkJgzk31>x*a6OL7J0R0-;UFt(=8JKw?5=JfaxKPmr4W5isU)P$ zXlXcWKvMVCZtG9m4I6936C?|9lxX9KH39TrFKQj*6$}_`2s^Bg?<*R32{TW&L{-nX#u;yq zD!Mk2r-7VY9}8H| zVfAWO7)jkr2WNOjp81J;dP%jMm}#7s3Zw|&+jL*V!Bn)>M%jow@C)_NFX&@^Hhse2EH z$aEX0%o2-toVb8+V0XK{2f7h<2+%Q=DttDTJ=M?(1SUed!)y-Rmme!S0#ds4a8HsD z#4uw#&52@MlaY9tNMr`#9B!{rk>~5uHPstQ*Ki0VhOFI02I9poCYd!$>hfUc#*L%q zYlYZkqQCOVl3Ci0@>d_PpCyirk6-Bm1f~PTCY6WwRC^g0DpqPmE3hmH$*u@X)NZ}M zvQGhAu(FzY&yLz9pyAZD(fLW`ZSBM7;$e^gT6i{=i=$8Ot9XFf#6z9yI=Jl(sSn&) z^3?-v@u*U_mJh`pDAgu5lFZg;xtawKLn*dglUNRiAkSS=Oh_x@SIiQ-1V)BUd}0+_ zGr0Hg==DIm%o>5Ezr=+4-u79pyS7(XEMHuX#3TL%^XnGq=WeK7iiFv(P+KCRx2sDB z_g5}wd>;8tvhqu}l;tc@>1r*?tqWv*jdxWzEb}`v#R4z%WklikprpVOxAcCnNl=pj=%v3hm!s?qO#hG0{tz45ZqizGP>hDL%;1a7U# zOvICsC!8)H`+c`-g%fwsO)Xfsc*WZDH(vYtt6uxA3$A<3s&(ftI==VNk>ke>=Sy{S zn8Wt~3zsav`Ze$RJOADPZ^cc2>H5poe(BL^XDnn4M`t&uvBBqGe7TnF!?$H*ivYMH z5K8BNPJ7pd%O_7DGM3s5^%URS-k`zEA`X5AXTZ-x5#z(=we?t$49Qe|>5=+vU}h!m zE^8CVFTAwmf8+KVBpIhDc{9Sqqg#EPqz#v~6%FxZ-mGb2{FEYdagRi)w!Y%j98uN~ zwT#p&VCj}aHC1sGntUeEvc+mxMvHHuAyQilr+=kpKOUkWz!qJboX>CZUQqVAVsnHmqUjBi?5 zG$Qg^hyOtPKx9rQX4MKlcCuShwR{x^Epv#$a1d&HvI>z85l4t@juEY4RFMp^>742c zG&tf~{o!xcesyY~9Ghj{g6Cp{vA*e*AO9ra6PKn>EqL^U99PSRwzC|XqDyG z)W+pp1p0nZ{o-KZme?mADef8=E!SgvyO*}JoXI^@zvOkl5D#=p? zIl&x?Wn9ZR8>F`HB8Kn8^|nwkD{ol-bVDd+KtI)ZN3pM7^%%M%VSs@w7L4yR2h$0a zFo&juq(wr?2Ufz5!2oCd@~2gT>>fQ)@2Z5pye+##%Wh(e<@8mEOt;`ZjqCh)(5R|P z+Ls?Ul~~(YvkP2gH@@^O_3-V^n=V~Mgh3+WrkV<`n8c!6oc5dwVo=#Pd{btSY$HdC zn3&@$8$L-g5kT{ZPYfsa?P@HuNx8c52;f7_&}*jdc1n9}HH4d#Kh=Wh73Xyt{5oJdC7E zZ*F<&du}Mswwz+oo_l?gz5iIJgF{m``PdGgv~5{qBR0WICEHnuZUt;ofXV9G6! z)+sR~qbmBfSiY`VU-nreeC_(`TXsk$N6*1!Jckv{4!EJx zT{$}ISJYD4!+Yu-ff5_H+C7h>Q@ia%Qbh|#JT|73Im-AB*{2_DAG>Y+7Ri1u$3plB zob(SFc%ZG@#&~sRlhmB(+BCtrxA#cZKG;6KOD6VlY}ap``h$DsKlrJC{=}nO%i`IA z`HL1`^M?1XICoQaHOk%N-ovBLOmFNzQr~5RRZHpzM5;7LlMxYv5K0ZFrO(lbE)#kJ z&iT&l0#Un$A?S{}KnUf8U%NS=fn6AKI)++>>FEWFm)?Bc70a)B)8()G z`72)Wj#V2kKfHhUp}kMeeerz`-%U)Ywk2IAg?C?9{7fIjOnIz{3Irq7+_a)T6Rws!zN!i11dM>daI&k`OvhBB3IS;a z+D1i^aQn7mu3gqfwMYRd;&HiEEAn66#j#cxG8eniCA-A|M$z!4isS_~3{ ziZgjyJ6q5)qBM#acVb{aW|Q-;!Wry1>l-35J{@&3nT5Hs+~4&g2h;&m`1Y(Y0iuxl8&E;q>9njK6c9Tf64)9Pq=n zfb|@3s>=+&4_l|ltQKZ+WcEhS37pFNGh)_j$pCaJYeRlU)r_;yS8ak=9=;RTbrJo2 z-5^!UQ#EAB9|{clXep6h8#*I+cDq2QL-gQNrA5k~v;l$4aHcfnjsrD?fX2o?Jhrc1 z#%3YSR|PB#-3=!jS|l?;lF1(>>5(YSZRTBVQ6`5-45^J)=&id(Rne6)g8Vfvpb>j? zZ!L&$7lC+5*bt`n^7Z84W-m!h61(^ff9NZS07pWtgr+@`tu%K$X9F$P(AtvZM%fb= zpj)dUEnOxfS~7?cO*2ZKp}i5@*(rhs1wXm3>HZDcsw z)n26HJ^Yt$s;^MtK+ZZcb`kRO;*6i%l1FHxsCX1`60dkCrd@R?TggZiBX;H5scSb> z7YUeCrh1cKvax;(>0W{z+NgDC z9W_K$LPQJF@Sqbwoe{w7inB@%G30lx06oF!5QDSDB^FG`*qy00G&NziT6AdMf<;Sj z+`MV&Wv_nG8-MPSm%L^1@>NqOPaN8}>%{T)ha~1Ohwm6xZ@m0T0)pBm!YN_vJhr>M<{r#yMGW`8jLgOj;tjkd7fUs*MF(PaT)B&Ue~GuKUh;o;Fc zr?xdq>hqVz=h|BK50UpV};=+0}+F&ar z!@`pG^}stGtG~!0l`{A+LY?ut7+VYs3b+Dm)5K>UZ13;1=|dBkGJ&3)np!YDbzl3* z{5PL0gVZzq5g;5aUs%tUM3tU2ofX>C9dyOtde6*juB;|Eo07n9>==F5)znZkT6>(P z?TjSN5D8AEJT!s}B?13%IK6Xwy|1*n{juP?o~TcS3QSd!1om%gUt9vZX);s~x6DC{ zYy{Fd41$=^)y9nB?M%?y=`Nc9b~bnTOexE0OY>+YvB6JcdymC9byW^`JVLsNOu|zF z;^SMUPLzQH-MA*4qnThL86<{%M@EwPkQ!4lL$D^E9B3-y;WR3cBgh3|0**xt zcFBa@jVHFDJA@qC0ct%P|^`H63RdNf6wjLjht)PmH7x;i1P6!{Aff zWt2utC3l!vy^1_$T;xZEkq;4iW^A^jC+eY%nKd(g1cm_sCU?|LuE{?l+@UHZtpr9T zMn3yc-8n4NAIr}!gYnnmGZ4!nVYhOxWtFUFNhPaS@kLCWWYQb9#K!VvYwD}; zOvzcvi==;BjXYgqgqJL+&w`M25UHot^i|3J>rl3g{NH}9veh*I+V%CRyE@iSR&3k% zSEFLqCe(BqkE7PL%$~ur>R0)_Tm@4<}dxXulsBN@Zy)gb@B35o7dKDS=OFhk50&(oXU(jMT~E-jX#*M z^T5<=E*U*k&hBi*x9@$fLcNBH@%X?%XuL7#z=HWDnLZ33C}QTLTJ^{E4%Ks` zI0aSO_$TelkCDs-rU*F^^Yj!C=b| z)k1n3U9q)D^kH1bFAHkl-#(R<+MF$ij@3^@6X(xdSHc9qNtO6n<|{L-U3MY>h%_NF zq%AB*7ZCAcH(EDeP*X`1F$o}?Hk($|U(~IWs#!?qL@V|q4EU*4g7creB;Q#J%_f`rZ_W74t9f^)Ap?fIzP{+aU#JwY0UOITAyQ9a`72y*P)Nj^c>r?e?XW_i3 zr&0plSAdBVm~=COcTRNU{yd$qL9wH)c3h4~nN zdubdq$7{9P@n5@_f=EDjwM>@_GYV_l_l;E2s3_XC(ZXch%q^czoH0dg%Tu+cfXLiz zQ!9-iU%M@pOv*wA>Z=MZ>#E?Nsu$2?T2LwYc%_ddIx5M}(#8Z6nq+b*9Lbhl8%Zi9 z*|J;mGA*?D9kNT>7rw-){KsFcAd>R7wKbW42>jKIpp^<@f*(i%ZRtm*LUHq?V|%sPeI`)*~w zJ?gLKv=8DYu@fY?OF449T)V?b6PSu58FvNjR5RN%e5hWE<@QGs|7^>sEjT*u8&A}l z8nE!-u2JF$HCD7WLBgMVs7h7K7LGdE^>P^_kCbKhb1MRFJaC|XtgnO?*GCcJ^9c#O5yCliQ~uL^tPWk@A4bc zw?ai|pbec8KdV>a1jM2Ob&|B+OKmUl(_Jx}GcEj_AswBLaCYOm?`Dl@7(>VYqa&Na zOaYu8-b_quauk+qD<3dFs%#t03c~v)U!59BvT8 z<>R8rC4*M3T~_aRQ|Y4_M1aBb+w-|MT~^~Z0i*6U>x*NeG|b8kas|bdD~rtT_DEDh82|+FsLdo z)V_y;zTE_OPmz#kR$*s z2q{UYBd97j12IUL5sq$uwDRdF3Pb<6Z(%c?iO%2G6DnM?dUT}9%lI_Vv3aUY?SXbX zv8KI-#(4fp6klK7HuV>-9W^9p3*p!SG4Pu=0wjnBkJcK~h3z|KB#H5kV3~!47k*9; zyLC_=nfj?0OfPN;tXfjXBADnJ}d;FN{TL>b%Hv_I!3s{DxC2z=@4_F{`|s@^}^-Tri4 zu3{FVb@jTMh0!K`25F_rSWTBx1{qN1M~>pWrOlpE*AKPe!teM+=7a$4KoY<0KaTLp z*F2b6HKR1|%Zebcto~`Ad18w2%P)L7XNU}4{m-n{;V9>|uUt{Asi!@}B5bp}v;Ebu8`V_7_niWoM`|6|MW>jK~(y?H{)cU1t(8-)n+#V^DE;@*2gHOyQcLh5hG%p}{k6?1bbqD$d~r$J_$ev%?E}j-H9B=j`rGLLCZp zddQ5CVR7DamOZ>) zcFQ5Mh3@l>sj*FerEbf7gUBoxVY`6nWhaymJXPy8@g9TF3H&??Q)@Gzv&>7M*kU{c zm1JWvMN)1QJ|vh}PI!6rieoC(3X%~{TEhjJ0H%xtqW+QbS1r`)hh!~wAvqlSqE)q6 zMl8_+rYW4T4e`LPN_76|mue+QkjxUJTY7udwCx5n}klg@7fD7(zEKmg64gzT;-im8ELcw}@_k!%|X2rP;i`Ob(2 zR&Pw#cS$~Y=cxa+tGaIK)Z_c>FC|^FdbCt02F5Ah+HMK_(^vWJp`2I`)KPA~2TU6| zLQvEv5^D)4q&Gm=s;JoI^p{^>yIPI5J=uC5M|ke3AnjGMAd)&9?F>}5_LY|It=?!| zr~lt~OQ}1}*bxu7;i*4c-jZ(uh`XPtN7kFymU_QoQ@-W$dg&~*GB<1>7t~S)d1WM> zNgyt4dHqUYE(&m_J)_g{%yhz1Y9j@9bR{L<5j9urIR5X zB_^>5!%#THYcHw01)XL$mUUEUTVwzcl}q~}kC;agNQ#ay-lzdfXsGehcp*Vho= z)|Nxl3l=Ro=e$eSUUbcQSHA3uSHAPgSO4^d*S~hdYDB4&7(IYhwll?MzgxU`+{9AyBcMHuX=RmQb40;7Sw z2@%K?pz7CdD~@R>Bd{4clDvF*TZw6iOVt$f(C+$}_o4+;pM0QP6UdZG5p7$iA#D!Q zkhnJ5ldNX8&tC?5!1O^EGcnNRRR4%GVP_JdD^%*~sOq2gUYHSnQ~UN}nhC1Xx|&bh zt^q_TH^3pr(PgV^;jD)Z9El1CFh%?0SCyl3J$}_F!Y0sqy|*P8df@oU7EDVg5{8Kw zAzE6gLL_)b(*Hsdh5<&qdf2qkJeF4b3IEG4nEI2}Z`Z6ZMX7Seq7gWux(t zeXRW%ZoXc6ew`|SDKG|^t)3hV^7ym@ev=)gjcPfqzG}%&POuyT7Hot`Dlcz;KYCgF zO&^q__7aoBil!2%C7hNrNG5%v403vOPFAlVk}1127Hm`)M1&KBuOX?Tm@-vCR#v<*Sbg}j7hBXgS0WsG^YzhE8n)C(CWKgx69zfQLhRU z-Mg!!j@k@n2d!bdW`fP`wEH?ePA{oyrogFIuZEJRGCO3>D$v0~Z|sER=@sYjoxt1z z*0Vs)-5m!;U)?)Hoa&l6Lp-DUu@g`Kkb8({bZWN94e)7@+F=Q^hNMr11L#Zvz#Iyk zs%aTmJJRjYXC0h;eeg6!@Y(4hWu7~Ye0Lhf37M|d?ZEc&pJG%Cv<&nn@B9^KAXf72 z(N=6K1HRD7W+H6Y7Jlq^B)#iiz^*Mbady?RmP{q{4M~%mDwq6*c#F z*Gk6ab1X2$_(iMgJ6t$qq>nKONgvNyQh869@|&k~%C_84`3)oOA(8-+X$gQG)>uZX z<%Ix{(P_zwSdi;5BD`AGX|d6=sZv&B$Ju03E}V3W&a_KcOH;;jVjx5PQ(G%_Acl#P z&Oyt(1;ZCbeiN9akdSniWdS2fzI_t)mu@M^h_Qo{(}|-rq&l;`+m(LE5{xF>B^iFa z#|WCD04PFr=J`_>tQsvCflSoh4jKJG_fOkLPY9FbjuB!uRho(Um2D+&H)&<%&C6=( zP%AW+aaC~y1frGYNM_&n@=-%Hv(T2wY#(UpY(mYwP=(j>;~XG-W%`F$ov1A-XV^wX zNg$gl@wg)*kdUYg2K~^UItOsHLOgjUOc_nFoH}q%)(3R$#`61asb<=B?-Xmq5|7R3 zm&=VLJ|ehcZE@`O@QBqt!`jldX@Xi&NnV|t&bNm?|G@|D`RYjj?7;lh7oL0LPj6l~ zKcS}2mDyF=O{;A=1i3K|?1Iz7jZI{NPPjK_Er>v>IuS3>`?gNhrqZj12va0OOo$B? zMVqjJi}TMZdREIg6ICH_dJ}?(^`04Wx-K{$b{(ouWi48|V)gl(H(hn>Ywl-;bC{lCR!J-uX@aRy{a`)sx#7GTU^6|L zK!s-{$FUJOdZO05;YI6f7EqPBAY*u3D7i3D!gimYB zuBrST6;3nz#J=f9$s^;Jt{zQ}Kn(qn`=;L8z9?^^O!qX>OY!lB0@W0x0V z*-EA*W|4QiB7kO1nSgT}MJ%zgrZy{UF#;0WYuAqqDhVeZbgmS-F_MrVClVQVYMa}i zuI@+`3#;3oRFc4JX|L)HNxOvC=RR5lLc~i-Oo>6--pf>_HY%ZYAlHFi{9Zf6tjw=I zHu~A`5JUTMeJYTN$8NdPodcad-fk*$M4*Qq#8k(SK0UhRF!EXPj5eQxQ|R=eW22vx zK3kl*UwCBC(|=_Dxj}|G^BX~P=$Ko;dKSpJ`>a;4;8~sVGci*=^JvGct5;_NQ^nBE z#Ha)Zj#i=o!zy$iC_6udZKe^Jl?eusiB_xp(K(k@1^xHT^dMu5;Ez2AE|ej&RUg(ZeIZeP5ZirFFJcWp{Ub zUj}e!TCyT2_=hUTuWrk%XOr>BhKYFfDI;xtg zC(z2-gsHP{FT^jIf&wcI;q;yk~9}5Czz5EE$YH{S1lPG9p~z< zL)G~t?PAtpZDRZ?L6wbJWtQy-=>)ZeA?^}Sw9+$}#KMWew_Z_gykhMr61DKYTZ)<7 z;@l-uAOG-&p4f7BEnv-Cu;l93|Looq%QOMBOOw&stC?wCdLGcAfdupeB9OsHi@Gfx z65*76x6=~>uxm_ov#N5c1X1boLk#Uh=rD6Qf`i9K^iVkH783sub}w5~=TZmZoEXXu zVlx$on4X?Df5D;~F5fV9?o}JEf9sX6dDrGwzVnJ#zVj6?d+GkE|2%$nbPHJQE(-a*QXYXS!Q%%}mV1{3W@&r@N!OJ!4b0 zEL)buRTL>w5+Q*rxI)CfR{^!}%Y4rL{(cvU-|xMLhhl*&k@!Z|t&``T?cU71_vFp| zPZ9IHEY*K5e$G29vIOv zf|KDF>}HgssLb+Hzo-js?Iwd>eojS&dWLbdX`*z-7ecgHDEn<<<*mGH7geV0%a&Eo zU=pt84_+k4q`PZleGEPpKJh@kKWD#ca7Qj|djHhzw@h%Q8FFv?jS? zY1u*8pof}ni7#Ns0p+MmDvzA1j*6Y&^tsU^=Lg!$><=6mwUNw@bu*6~7#U3e^lxTy zmr`xF79tzEw>lWs28aX?YGGI#`K~oYnECq*iKJsFJfRf~Rb~~K3(f)P0Z(9lQwexT zOauQV#WRh0j4uQWT)p}Zy}an^)r;DHW%X))TtW+2-{HCPeFOb4F-}eN%*E=ZZZ^cU z*{oj4jm{7LG;9~M3)$_=5HA{V1g78lG||p#p#yVT0B!!zY1ukIGosBDn2E|}o}%%EoIyCVdsI|OK2%AMaAFPp zv2WKhM1E8V(Pc!s@PGT0N}fnw*)locA`MByLqt(9;iPAV9FEY+bd*xl!~<$&Ke9=o z;(X}7%5*U+w`j{fwD2nzX_<{GW%~2!qqTl|@=RsSl@*QGtwP{zT2()q2-2CaYA?f& zIL9lO{ZM=NUJ@p3DzRWo*S+u7Dhn-f%ndU!8T{RYzlSZh-@2!S#mp>&%H+E^vdk<+ z*~kM$$n8NMBj40Mc)@QPh|Wj8pE+8cj}tHW0!S0kgo_xTt`fm>Gxa9FVDhq0{bA;o zgk{N+N)lQ3>hIh=^JM$0Kp(hc=3joP`U)Q^!cm(?W~<3OvR?@a*w(kAC4J-~Q^oBb`eFE0>&Jfn${qFGR&#_3 ziCO&0Iy&Q57;+I|YQ9<>qBg=1mGntC9mCx&9NmCHXKY8#4d^bQ3l9~8>`w3}ozYrcxve_yEdI&x9hTN-f+VY|NM{r z%=@>$@t5}g&`;m^_FuU5^>00P>g@7WYmXn?S8wJlTD*ACV%PgC`2Im1)F zTVMY-|Epj6`~UQcxBMsPH|$xmbh*Pl5@}~k&L3OOOdF2t1q?O#nMpBWc}vDf<}?F* zm!y*L``a(aV+|O|LJXqpw+9u3S>xKQfiQy6HM;)!BQ+iOH;9E%!$o_smpbFFkr;^! zDhE%DM%P?K5i>Y*x_8rPsmRPi1&5btv5DXU@y-49D|K}J0mSk8&dmAx0XTWliia?D z*Jk23;}grZ!Xt54y|KmcK8me9yW;wZ!^I(E`o_mpm06&sr#AaD4oVwgPD%1oJ$#~8 zTehufV(nQJU(6D-Ya^^mN2x+bx2js}IEvUK2WEckhS6ayEUGVBruiD8U2Lkg`#?S# z(2i*WLuk7u9jTo1}tRRvLhNjQ1-#g~2(lJZ53?>{#4nTJP5WQZQv zJQ2RA@rC2jx!;rePP{CndoN-uelT$fEnr=&W0|r3<%Xm)Kd!mx5PXk6*T6hu(84ji zoI>~A3I4=y5={dU=wgB02~Nk020UFm3<;gt!fwhThLSNvd6k{pWx-TC)DT_1u4Oc2u7 zo~d6v5sMM7V~21O)5^I|JpXBD9JRo^OjnhSZn| z7$a}nRh{tUk&%&v4d}G=Q2R_Qn!M()Ru-C>nW72U zRc5gdbOjak1A~h_Mz~fwd=Gu;(~sQu*-_o4fzxNsZhG~P@7}W~H?9KiF$2OD^j3f_ z`snyiLq;I!`;Z@Db|B2>B~L7}En2)})%q>#wp?}j?$_S%_FuZ?NB+)rZ~Dns zz3Xqi>Ro^PJwN+P#}= zyz?jj@jv@7Z~7bm+m%51kp^9yuR!TIQ03oF^HSY~k3@8R8-U-`qdCHdUF0 zjc=wQ0=aC>8mb78&zMHcU}cgrbQ{59r~b&s!4c<&t{%PCVVtMJ6eD&SG!c+-dD_J{ zmzl54N8$whS}bVK7=V#Keqi*6FE}+x(PhMF3_>DjR97_`*vCKRTei6FU|B0W%~s7a zGfikg#oE$#Zvd+M#3{78pzX5O;UBIFC*yCO5D5+}O|c39K8EYQ@1-j;ZD z?~R*_i6el#@Jmal#Wk=%GC4*TvtGNszVdFD5i$%OJ=J#G3?eoI(e^bnj~;CO&@zoW zUTwA#8yOQ8gEcU=bcL=aRHIrGNFp)<6DFWa{7SfNX~`t@qNO$~FMuPZU)?@4O7WTv zGq-H52V?0R7W$@^3W6jf+4$8`(WVu8fL0m-`r_-x(1c?VQ(}&?G zL!>L5+#=EWHF1PCL}9>i4P>)+Rj8 zg`t-0aUog@wCrYKI%eqV7ZBkEU?IQqy8sv8Nl33?f%rcB3E|6q-sDuiSgTj_To|g1 zKCqA$z32rttVNrdCghEP8B&LVPUtuPnn68B{K3VhkTo|gL@84f9p@11>!kXP4 zKw1$_M)UNTaaL{=nW2RUcRHB0IpIW?KxVr@e$!XAGUr8RQ+o-;);Zm5C!}}jUHQbC z<)c}r*Fw~tU1^T0d|L|LpMyo7E*g`;gyCLM|3BOQAP#k$5OmWmR4S8es|kz{h)tX? zJTdd-r$?qRdk%q+{Tys*78OhpCYX3nmIGz>xtZ6rzj1@exx}E%`^xp@nxm{$aS>e{ zbdnD?b#mo}N#MrcD`iJ5kmUWAZFO+nxGq~#79#J|k{`S}uAi-J`{2nsg{VteSm2H| zj?$tnt7rDN7k`}U=%c4AFuR}@ksyoH^6BcLo%KBhoFowJsKUQuZGCEli!@*A|FM?l zDw8eos}m{-lqppZ%RbXEI1lI=)+psoGU zy{j!w0RdvwmZj|nhKoj#@au>_)4nm0lPZ!wy0_MO-{9P6t4qYroUgMhxu6@5+^clB z&~)ADv!!c$U;9c(b+xml-Cq$FNgdE*qpJz@-iPihvDM3Jz0a`@^93-$H7L-}N%I=T| z%?_=s(hf0gXT!?6sOLgA8)0R)0KGQoHp3iN1$mot@sg!$Htk%u_3HK8cAej_XV-0a z-+1SHZ@BZlyI%JrH~jF=U-P=VmM&kpap$#{Uvd409eYlnJhs?4wRrI>f9UX%LakXX zb|jar+j`Aax4!kZyZ`3yH~;M4{s;fX_BZ|F=9}L3)Xer{=Pxt1I}Z(g>)TT(*xlY; zb%x+OJ)Hj;#GLI$7-uYD1P=b)ZQz1j9r$4IaptoYMOeSGM#zl$#0;3RL%=QzRE&7& zBd1D5t&7e$oIx*dRAfNQVhttm23|bmAda1MFbY zr>2kb3K8f5%+Iya*!;46Z9Tuac}vxjp+fQanNddxhHl>ZN&xsf*4C?Ac0ct{4gNSn zrA5-fVism9a~}fJ{GPwB!vC{vHdjTf&_b%%lEXYZda0aorEYQY=>hU${%ZU49e{6R zwq#mYEM8oxnj_9#A|a0;#vT0jeU-G}q&B`nD}_P#ZM$dwyT2?&TEa8^sp2A9m?a7A z*%dm0Ep1-}OaC(~LYAFcnb5zrzkcGUmCP!oF^?Q9MHWa(8n14j4|m#_-=Vy>!1|V2 z9RfMT%m!(Dnxl$A(Z&~`ap=m&l4#Ipu(<#1j`2rovQeFD8|6Kwy!_iDf|d$omarRJu(lgX5c&Zg5xz zu@(+>Dz+h~W5Nj6&Kz`hp$l|+$QdF|JBBjzD}Fce9R{$FCVbD^_zGslE8oC+$>^u@ zMY(k6u`pK^zkR&xm{oY5AtwBxo#6;LKW4R=-E}KwcLx)zR}*3ryZMqR&a9>~cH1DS zRdzGfQp`{70(?{Bl9r~$X7`2YT0yT0LMzQaBoX~%iYB&7P1>}LAIUT4p8pfVIMeR( zI0)9XKff}Z>S70xISB!XWKkp?p!}uS-}^?jD6eQ?lBx9Kx_Y_y*<&O1%-INt<(i4x zx4qFgHjpGq!r<8H>PA^b&CMI@YvpMgnB$*1T3>MQ{S2A!6P3r@|V#_LY0-CC5#_7yEQS#!T6?CP=O zGavreC_=k>;Oh1D_Y=?@sil~%aOOvsBlguITniLqP+THp!RbT*6VwV-omxHShE2C< zc-d)hWAU!vgi2CK6p0BnDr9mdoIhVZLsG4HgqK^at2UIWil^PMq0^%t)r304B*^<=zs}#fI{sho-_wKZ8E(%ua+>1@GEZ4-7gVx^NJJ@lJQ5 z@6L)YlhrGOXAKKumEO9P!{DLLqTIQSIYCz;-+OW%MC`!HS_oOX{IYdhui3EU+AX_o z-SdX`?ERsix&Ey`yZ0?Wd(CU#^^UvWvu4j*maSa1@rvuuo<3El`}uRPyruQs$D+lH zSFYK3#Wj02ZM)*tch!p3JKpni|L`CGvs-@T?_PHOU2l2Q9gm;B{JKqxa{|2Q`dTS) ze4Q$HTv;bWU1)7QiGVVLSYB4ma+N_ZW0!NRW{?Ilgh#_&dj*vM3mJUA`gE>^tc-mxy=2tFCkwF%SRadkrGRSIFQY^?}a)#{MP(;jr@rkkwIaq{phHyl{ zDUq1bYV`wx{Q8w8fJ0L2Ds^c`%9MBNOr3jjD#kIB2{XbhYo^+7>>JISqQYd{1~~DR zc+x*CglS8=M!QWnDx&(rlQou#B=O+na}ai8P?@S27aR4{XDf0R^m)vx-FK`K2DDJ) z9hBX8cK5&VFKh7_u#9FrDqX#rGib$w&(#$aItU#vA#lP}cKclgK_)RnXea0AT58c< zLnk_-HFPm6hE;^A%q2X{@0@8bp{G@Xx%e=@;=+->`p)fKFusSbU%_{XR~E1?9N(++ zp5IwrA39l|JRBlobHy1u6F(Rn4z(So#tG(JHepybtF<8fhY!^3+job+8LFd?o*upJ ziJ3tkVG8e@2zM3Gx=pLm~}kuT6yap-RH&d!_<>~ZUY?rOiqrD z`;ID&exZo6_3W)t@x6f={U1lr7@rliH zg0bsZkmsltq0-N7Ju8Q=6F{X}J-#EVgV@&T*l~-6b8q{KN?zbn%Te6`jNkNOiiL`# z-4+tK=doJjOIz@BX9cR_v2KJ~YU`4h?3R&smzdQnz{zHpuv)is;V>o3&+;)k@uaH6 zE}!}AUwrv%pZO2%4a-Xim#w?}hu{4(m#tipMkQc`CiM7iK?r45XR_OS4=84>r%MhI z`w(@zjy_I$7*ZR4c6%hVA_iSAL&W$rNXB3fIO-k4-H9r|aSJqLqK9H)PIH2Z7^V%{ zo#4!hE*1-;r}T>!?cK8M;OXT{mM&kqeC2Dezw+4Hy_@#DZqFU>x&AFbv-|dU?|H*} zF2C~nRU5W$y85O~SKV~ky3Hq#9$K<=*|~FPYL0mY7Y{2|ufP1t8`f;vvF7p}+xOh^ z!|(kE@A#!(z5YG_@cMWD-FMvmPW40xcXSY2}H;OJb8F#&5Gj3aumUCXbt3@ z{J!=Qu9(4;hyQ{0lCnTa+O-+93Fj}mrU5K$UsEs4+I^t?YAJ7c{lpD3zw@=4cCT7L z^XhFipT2HKHG~nRZZw&`<(l%#AoXQSY6AMueI>vY`_-lGnc1`Bze1~uPwMY$-@0NX z!YtU$iVuToBbfC<*hQojQ8eA8 z3NrG?BM{IJx+TIUbhU^8?L1lEChP9B*Fx;~03fv!Mk2tG)2#mzVkj`wvKhcvwp*~Y zde_E^A3s3`pMAK_UZB-GF!Eg{ATU%MVpe95V0vs52y?F5i-ni;m-6 z+#T`?UM771FJS$(I5nr&o-P0{YGK$FW}474m?3}Gz8W&S@I=SX+RvS%R;G2?)Sy;> z7k?PlkRGor6y4GzIabH4z8;+Vo1hyYV=?>hX7p$+s_qk{JKeC3hGv&8L7N^Yz>F;8#dRo%FU~0RxGQ} zvEzuadhgkKA&51)g2xfC77+#13^QJUe1m zE8yy)eiO2)R%hVzkJsaFGC9HCwyWO!($$*dYWnIkGdFB1`)Reb{^NgDHv^EcDXP7r zt@6~(Py2ZwK2y3BaxeD@=f?OUGZ4_U(rxFMfT8FntQ7gd6Lo9%xkpF$`E>iumGZ=y zvP7{}7AHr9<(yQuy3;sO>zIr4+`f4s9|4YVgpr2yDzptV{qvXYo)DiT0gB2E$M65- zhky4{dnsn=vXyVR`+fV*tb5nB)mH*eGt(OojuJC`beDvbZXUF8bU8E*j8B1<4@FET5tK8y>I^M z-LHT54R3qjs`XpfY}&Eusv9?6dHu#K_MSd|`0Sa}iyedLE?c>J*@{)0RxdVO zWC+ULbw-20;f7QhhmEcV5KQtoN++rdN*S@p$C@1GvG!gPsD5a!g*c$ zrr{@BMse4k$~1oSo|$*V`+0j_JE~QQ_Q!1# zFabs~Ya^Il+MWaHNu{ZoDNRuI;rq)DNp$-Bp@Ws<2_sW>-op}`TIzM|+ON@zZasYY z%I6)aEz2kB#e7LD8S0sT8APb16&ZdZ2jbU1RZp-ab>e47L&#_=%XDox`;UzRAm2eg zA-!sAiE%+a3yVwI;vHc|%IUM^BJPMWj-UbwFWb@6wQS*>;W085%y{ts*8W-)V-`3i z;GmPC2(bXs8q)vnYju<(sf-24wG+kamL|+HT0=0MFjY0qEuBlwABb?QDT6d0q7r_m zRng5Zq!a9h6W5VkxpdUJVJi?rUs=5ZobHrO%^8XTI=jaIQSi-Om_vGqaAvLQn68?z zmgY>~PX(#%nAnAh)vIX%n3WmK#SbQGhj!+%IX^B;YaU!cBo`d~4>G?07qI3!CtmK; z_1Dwl9cQL(s6$};Up|d+{Y#m11 z9%o=Tv&(ebJ0u*Lr^8o%|*Ps_lbZCaTEGYf88Gun+j8tmrJ1|t|GCWBFx2+dWrmMtl> z0+7+>VO=tz1Q4g>qaKd%5_Y9lgO^j#{m&yVX+O_V#T~vpDd|+&9y{4WyV)`Ye)^$$ z-zVOM&3+zrMv)Q0q;CaGOsGj;lEbM=88NrVtCkiW*GbMou#o4g2)mLnC0$!wQ%`~j zN?OWzOgv(?>wrFTaMTJd&7VO`R{BtTjOBj`GU;;KvR=9b-nFM<%QtsB;?Bm7n~>^+_T82% z*4M|;ghAa6gQWA}1EY{g>cC(9aH%^3>BebFO~ax1-dEM1R8O{=jV}WI=UPg)ZCUK2 z<>=%m@tmeul=$Z4Ttjba2eTwvXrQW(m<@)A>0VGJc2p{07pEzoPm ze+EHp6#G4A{WzkX9;$#NRfnH`_%A>4Z_l1N)eK)MSho1w>)-wpZ+p#^&J+D0co23v zt#i_@Atz1i=zSYLv7x|(aECtsi5u(tbU-FfX9a#%uZEU85yk9wfgWn;X)PEEKQaE4 z`$8}@GQ`l)G_ra%T9%y>?)3ad=D~?GrD$W}@>PouoLahgiEY`km8)NO+f9s+ILdlu;bc|JFney?X}A{?Owfc z>#>8+%$z^B=!J-*9~87EFJHB8>z>!_y8Z57_~1X?@#bIp@%R2GH~;KE`v)KRyT`ZO zv1RwI*Ic>t%llS5a-_DsTbDVq)%oL)T4rj$8H9{EEML98=IdBs`Pk{1Z|*CB%iAzZfc)LZ{Mp ztZ45VoA?q~O(ktwXH6?IM_^`xKvcK_Zt|G|h~Zb*ZT7^cg-93S|EsUmS`Q1DU|vYq zi7?AUalyW@^#DF6)YD^XSq5}HnQKp*7*tmOn6TPZp3{S5DK-lr63~1H;=lcm-H2$W9+OnMZ+rpMG$lHw(oJ5kY0|Y*h`G5WC%un4| zrZl{|tL01OuAOoZ9~?P~u>ARk_Nnz#=Vm_tMDd->&$bs})2@hxu0^r&MG=lFbeIVs zRmE@Z3KL=^<>J<~NJc=36FRX4Lw^7ENWlKu6_p#LHkKog2uG_^8zjgfC&s&rKmZY& z;0Jm63!ZQ3xFNI3!!&a!Ijt3g9jVRgRqpSGn9#M`4LVN2IiCa0&fz6J4;C(YQE<`S#bBn`37k1M`m?Do>NEo9 z+Oq=m)-|IUbRN8(vH&_aOk@Rjk)D_sce?mzbYJN#rqKq48bY@B)I(9Biq zMn>4oq(uhZJ%)gsCvhD+y%w=zY5lSf2=rQrKHKctD^I`R{#-2>*X0BZ5_8wFcT+80L3Y7;k~^-*$DlHCx?y;i#d-^%xIgN?>aBo7<->ZHFIU39Ro9 z`BbJk@dcH9j~8oV0lE>X=e9LX^%yd^8K7k=WbXOU2CS>M9*PT#- zAu}8pnp(7|&g!A!{FqB}ffh`BroRac1Dmij+?~foPSJ~n7j-%-^W2%!`yRgU^zkF- z&egZH&bKdW?OK2Svw!yIPkjB)H?BPU>@kVq`IAQv9(wGnCr_W%?`Kb+{A-shE?T@~ z$*-dBa)i6UNdU?%;dR&pJVJ&oK5M~gW;Xs7WP?j+~$eA-7>!8?jS`w$Q@Dd=83)8@Vm^_Zf9zi5A}H(i>BL#nbH zkI*BJB>vhJ^(Rn-9rC!Tj2twRUsMwpx(-mtU#}kSvf-)2N{T$wy{> z<@Kc-*8zd_Ps~CH>IQ8?caRRpOw(c7z4;Pd#rcM(ipzABGl&PP?_>+4i}y?mc$;ZV;7D zL{|V?$DIk~ecagKu|xG))qu(;zFjXs4ING6(L;5Hcf{$iA7B_sCuX&Fu`rK)nPIKB zPT}PXX@1KSwX#`h;mZ-pLK^t;b;80i5yPbr(xr(@bO9^tAeTOL-pl&fa)Bodf-3!7@Ga;EO7T&q1lC!SctAgwf%Z}l07YNWjwwgW% zOHrBgy5jLOGfx~E(H$|>WA2VZeC)64kMgWtJ`xLtQ|W}qU}zyogcHU?^dO^-APn6F zOe_BChb!p`!hzJrCb*LFnWyk$7Gn9~gEMd6{Q}Fh9zIZ?Y)CxPxPBZH=N+SAAn&H8z=g__5aG4D9}Yzf^=HuHVvKT?|Nm{I4ok4jnszvszmAcEHIkP5i{fhUg8tCrPQT7t-=6n3?uEt6GhIQD2)<~`v;sP)&`+pAR zL+wR~O9qP;E&AcV^I!ea-~YA5ndYY-I%l{$jjj@;2OxYHR>82lhM3F61bKSohS37t1R4UB>M^Zl0qHI8#n5ALt~sjcXs#sGgVu8;rTNs zjy?0>SI(X}wPx{&&wu*&j~#qw>#Fll9XY3R$)fX5eCrD*j~yIoc8v4MaawtP>7x2e zqc4K8@BJ+wr1w3qXv~lyKlU8&8|0GzG~6N z%Vr)tw0z61Tb8e0|1NX-;zi#+uz1O`72S^}>y}*aAq)|{mnJyrz?!YvfRn0}z}M^B zkJO+uyQyG!9TCtyv5Ei1Cu*$(EdkC=X18DRZT#$48LhWmQ*Rar8Cun{LnQ`#0E8jd zvKs*~4rfLS;wxPdoH{#m{pOlY!s(WK1EYdws?RmCVCYV7fcVi@udmMwsET@YQ-uxz zObxp+^V%baDj2&?U9-$^Lg^NO0AxPx@elwqF@=!eJxINMVLHmM^U@srMi!A_=WFDzPx^G4hEvh-6CWUJ#JgmcC0@E%lFm zyMA{WVY~Ryb-OjYy~o0V?lJw)jfVzDVb8d)u4^y-MRFK+khTXyfy6Txs1V3C(jy-N zb9Y|JANNUj)V*|~S9OFFf&2H@qH1S$;*$?l@;T%mI8i^~oo*xO5EsZ>uC6C@Mu`bi z9ogl#hdnDIrpzH2GCStlo!}2KzlDx@EKJ}VBe>bu49)Oo5{GzzH z*1j+C-CMxQFn4KU;U~&>;gp)+#YG8VXyio=c7bWh3kzm=CQMiu&T11NIUK=*d14#l zrN%jz+3XIPodZLaG-O(S2>i4t6W;Xf3usukvfj0h!K>C+4p*~5LnNaTVd%`zo#?=k z;7c_)?Y$Z!!wGdlF#zAd}8$OmD=9eE?iMHm0?@(WB-QDm3)U5bIc|NGX2g0!ZEl$(0;HG zg9y!k>Szgs1DJ^=9h|wdJ-UZ(Jk5a+n5z2IV~C@U-U)&X(u8z~ut8Otkk|B7CC)nk$2U}c zH<6U?_?Z4?d!-BXYF-fgd#|r{b>l&7DichvO*{}6d<~5yp!uP6+hfx%hKoD3H&ftq<*@!aG0pE&H!57XLj@I#k)2xy7%!C zzW%yv?>o|73T?Q4+lmJcF1>kc{hhv!SsNRq3e={TQVx-G6;jp5tIq$an`)YS`bY`r z;aX>T;V1_#gp8t?5PlzR5FO!UXbqjWCx(XxhQV|@L#@d7H=e=)wN9$n?X21uNrXV% z@IK9h$Uz&_aClS)-@APgPh2mlRv4#Xz(nMG9^)m^7K@7P`6oPtQ=N5C#R zx*V~Y-Dq`SIzWH(HKR$qH|ORp_47l(N$XmYCQu zW2(mqVRo^hBAe#N#}1Wzg4fTpUJE}y|jHbSbV;ZohTp~_inyVlSn z*fr1%U*L%3gcx-Gka=PF&P`49%v`1>_`}_t57QXRd?yjUg6}_k_r8I3DgVNA;jvxZ zR=66c4bJc4I}c!D2$(RaHov9s=)YB-c0KVkW=M;M?dqHeBPP5Lg@=A8Kqj>&^>r)S zgf#v&J1{;BW@r;aWA>(Xfe>e2_@@||A{oZeCfE%}00hz&@`#wSA!OlQGG%Ie8I=Ts zleVN4y?=3gzcc-3 zmvOtY+qffRgTzmmfGRTnsruJnnQtU%7qZ5R zd{K#|7Lx1Z3tp9wo^2nzsID9ToQi9$yCdH;X`eokP$D#3gz_g%v+E51>6>bi!fw5L z@2+5+pi{r5rMdMjkLs_Y0jY{RsW4$$BH7S_5r9qD)ZgE}_)bzJ`EEMDcFRt_4>JCP zQ_Xf6DUaD2L; z@};Hk3KlO}de=|9|0~a~O6zo#Mu}Ogp?4bS)wD$!vGh`JFQoaHh)6DylSH}xiuR3y!a*YSIJ7omNap?#2;HjmHmoD48b=kqw%a$x% ze*Mt`gmCzuj^Z;p^|b{rcxFyZ+s8-1~)-dtueKy~o$?J+W@@ z%B_3L?9S^JT|V-1+s)TLw5YsayJye+M>mMAZwF4+`&zPv;c3|$o32y#lOzWy%=G;5 zco{Quq7tL>jeYfOfQ+G|hSTwRZlpE-ME5<{*JuL{M-&YjXk~`$c!VCI-Hf7){Q0BB z2i%$3OHhKX@B>FI_A>F zHLcWp(2MGi_Yq+Kd9<%_iR3r$snK2=GbzMoHUjtvLpqx^jkYg3;g}0FBt4^~1zey> zI$cYweKFlouSKfxGlSUGijTD4=%Md@qI9z)G17XjM)X)HK?8Msc80YO8gAb?%5w;?mJ~}* zE?P8_1icqN6)^-(s>DVMk=Wu?X*cSfAKehCqJ@ac&hgLvB8DHa)Z#$bRfgWYx}Lht zx_Sk)`W8Fk=WhkLkcYo947#wA4CtJ6-GrAI>|!1G4;`rG>}fpk!cR)26&*S4ix~5V z<%IA8F>B1z7`Ye+(8V~__G#d!)ro2@v)>^YH%u3RG|`&5`HCWqOmCT25Ph5l8GtdGOt+9)!d#oevm3-_WFjA+F_muK zHn+dj!=O9!xML)(K=-tUaJL{C?X~GH0x5f_-9?%c0l-0L>$P&e2M?B`&JlTbyJEL2 zH|mYb0gB8MN9r$Rn|DQ}OT#3INVmje*=$QH5g<%Pd*63UrD*6lmBu}IVCJ#Ir5l4H zqxc8ktR5p!ecLAZ>o(P2Iy>L8I*CHLZV_q-vycdJ>dhcoRQm`kb&|q3b*@&m&a|%~ z^wp~a$LnXF|6=^mmF36-U4i}FtICU*#aT@o|K&twPeAS1RRtO)T9Js*eF7pa5)3}$ z%-SL;tz@c>Z@V0-PuibeqT*h~ZsH-CgN2XDuUaKO0WFO+U_}2&x%m1wU#;)2mu4%3 zmph^a@Eh+gMVqwj>b5Ax6m@p0!`sJhsZkLUFv$DW8*07jqwT}F3%#Av;mj zgxpOf0KF8ms^V8^17iA3>$KtxSC)LT>S#T8_}HKR;s5XWp?0@=DdFttlUKa?7jM~B zpVLcAb%Ijo>_1i?n?P&k>vq-=(bCAI-vEas6~I@!a_P)7M@N#KsmI7riB@c;9Ae0v zE+N*2(8D+rt5*;exqDVHVdzu|6cc%nAUh(aAyMz>MxYZxb|>UJCWJe>mTfM}6Bs(` zVqImZIHWr?YS>a3`km-u!`Ulu4ltEc&gRP1FwL9v!-l;WGA@CDVpb6+$*wY?D zaBZY5BQ1w9nk`veV(6GnpV%;aAO6v|>o2aD+FrM#2GTqZ5%34vpKUd|@~$nW2ENKd zBg$zTE41kJ*>VIKbsdF|-Cy|FFNTHp7YNoPn#Q&W@uTL>dl=Z|tiiO_yH2 za+CwG5aW>Mt92G(#(0it=-}kp(E}}@VoUAL_I@|8Og(&@UV<|Gt`%mlO-WwNE0#X5 zIK-M2bs=}8nZ(%4E*L9@id`VJslv?I)g?nKNygel6>J#%8TT%)?2^G;yPX&sfpo~M zubWMy>n!!n z1$6JOa0o|U+KdB|MMUC{q*_~|>Mm@RDIRMoXe>h<+33n3K~T)ki=X={n;FdhjI`rq6)^WIzPZ$;nT{w$+YwVy$Y;I4+{^e`=(;PuI>Wuv>PoUuTFqG-STRNc!#nhZ+)Oj6FBwxjt1yZeuv zdHOH^?Y<}5ZR@3kv!_q)y!p*bw%saz4Oiao{;Dk_o;*|OskuNyo@^_LnxnLJq?zHQ zJk!>a(6JVk-hqKr%fXHO1j6in9wVJ_w$#xa1JeQObT`;(V{cb~{7@bD$Ya_ZDL01! z0f(t07P1X3gWc|Q$cKsDA%LX*otbrqA-bud5t#6v>vzcPy*eQYoikl^ z_~i3;hGPB9&k2xi0&_6~x4uT{pfg#UI9pT+ASY4>+PSBVsnsD}AVZT~8$M?E6Q^Rd zAd>jX?Cv`@5{9%rbVoS07|BXgI5Co{9$}aejx$XyX8MqY-fG84ZQg0Ut^IXZLtOv+ zl2JDW#OA%@(JwF&)PmP|{E12=IWpviVEM5_b)AaAXm+}NS?tk+Ghb?d4^$Zc=q)u} zss3~VwB;DkVLbrqSFEeQ|7c)Q zO9hj6Gyz)sn~@x1nsmSPR6SjL@<_ecW;gN2m(2-jmmn!o@tX&wXEnCyWP} zj*)W)@Q3n4u|RDD+T)i3!cMSc`DliWsEN9EY_l03rkF&OG8uJI7VPQls z%wVeK5qJd`9e-^Wu=2yPOBpZq0v=*k#cAfOVhg;wI{K*kqKM`BjbmoKh91epZZM=_ zx;7(hm=nSs=J-CUPP@~zFuQu+MI_Fl81&$~Sm-Wn{!=FTz?2XqPR!f`N7DB2fx5v) zfQc|J%p8V7D%pjUc7+k$VZ(8FRGJ+#A19~mT47cO(AEXOZYJpvWF!Pismb~CqsB7W z38rjlup5vJVk(V&>~O8HP?QYG%pPnRBtA!6*aVPx(B>gWN3AZxU$$iCwjC9xs%MYa ziU6B-69z4kJzDa;>d{>3?I_>X{${p>7^-zGZ3Tf*VK8=V?rN$}*xXsknY7h;qURGp z9kurNmE%jv8Xzz$VQOz_-Lz#S0PNuRY?Xy~>~2dW?dJo8lu6BZ14)*LTsx6Bpi z;e%z905PFbeEY77>aO;j-1$S%Zh1LT<;InDrs;(0|F%X}WzM(O1SL}v7knE!PTBd? zLo+{lQ{9f@=lBwqqdS74brY4F8Ts@xM=NqEW|nw%ZKz=0drRFKA>taUPufvPGaouI zK1o^9wLik6IFI|8Uz!&9Z_+c)IoCC(XOU!Hm-iV9_WUuz_(&ZAtS0LW@6h1c7or{?Q_*O!4yf-&pe1gy(8S z#xzkb+S{_4%-6T}k3Q?hY58sM{*d7!AA9ho$maS#Hn$}Q_b&=xC)iI%x7mOUn(Dc;Nitr0h&g^bK>dy%SN|HO*R_+vEYH!|Bd>cAP1YbzSY-@0s z0#3AndB473*qTr$UVB+PjrxCnwsL@1ZLM8rSK77jSlOhd7385*T2f*379L?7z7Cxl zrL}goj&S)MpCtMLQ!OZMEO4RW>_eSGZ7!}ZZKq8(Fl}*r@d0U5tm`w);eK(dN z#$i|nLwbntqu6D-9YAFDDj3Rx%zya4m@-|O-HwUf4t&DBbAnlg!;Iw#foXTM;yYQr zn#;vP{KEWV!`itd=WpWzd*Bs(C%AMMuqF=wCBAwE3p`mDa#|bl7k=t5TyjCK2Qsq? zPaEmG6Z65W>Kzl~BNdzpW=CpOW%X)kzsEByJM4Dhp+L$ZF%*07xq90O$E3@l2`MwE z7)+LkWS^_X&v~N0t7Qg=G$hj)vF&SWYK&Mw1|k!`9if$RhHdd59tbNh7T<|1%_ z1ulXRkyL)<{>rExXzyU-qzZ<#2KJ_b`@_6?jbpoJLzQ+}CXi^WXPQoqmg zoAn8N{)u9${oLROR+VVB)GA2~BJRQ=oX4%%-L;{Kwdbf1KG>B)rH$zHPvuxwR1%y! zTZ-WyI62BqW0SR>pP_{$Hc|Oc-dSmJ(~SVAm8Qu1CD85w?O(pKR#IZ>$W)U|0VntF~KW?K4&RNC%n(vt$s-?1{bks#=L-kbS!Z5*Za1AjhR{ z5nJTENccoWo&fvrzoo=*a=Z|q_;&raPOaheaO3eI{PzAo(R}CSYtPh@+_jr3LfaRgEHQSn3)TE_(z$(YMaY!1D1`peB2^{U z?oshix7DlnTwh}MJ~8Us^X0Q{@-anDOewJ2c9abHY?utD`DE%mY`@$ARk}`Xm&ry; z4OX*Xh?e*>s(OvH$2V7Sc;G6~$gPD^eAej$GsOVA@*ZfmFpH zN!eE`h^<{d z%VL%zm}3o5=>hlED;WBn5K|6oI|#%w7X*6e_p=MSE`9_Ivripqb&c5%Uk!9GJZd3^ zyRl$Phoox9ntFLQl9)zjWv==N!%*JkAiVBwXfwjZjE#5hswIQ0MA%I&Bj?*++pwE9 z)<`vO?*n6lkda4~l0m~49B4mw2Aou}nM(}Nv_5)&?HVM_2*hdxZi`%5^rzLWC1$gj zxk&&CK5^QvyS!eLOfb|D+CovOIb0i%fg?1(d7mgYd(%kr>vR=NPFhn&#DFzZ2^sSL zr+1VBYnVU(s#*dtdMj)49lXy)0Idm_4CYS^+a;!@HpH{-g}8hBZ}(bH65h~W2k!Go z#n;+P-bwX^_SvlJpQILX-Tp90Tq~nxUQ>nEZ=y{n5_!F=pc}gDu(vznOYpB$a5*>plFLGk|cvkp4qe| zQB^Z%>W8THs(9-YsbIBnd>W8PI1ub0Y}7eQK89(ykOW3ot=6SoGL73Dk(QhY$uKC- zjGfr*E!woI#_bpvwv8#9;5!W=X!WMCRwq?E{{b`dIm1Jw#D|K^m$zqM|GIrp;PJ!t?#z)>Bg@JVK%Pvd+pIW165qIsWc+6L z?D=}_oI8`~x)rtjglU@0^CfiRsZ6LPJr&RRXP(UB$is+T*6!%ABx;hC-bFb@qZJF1 zF!|iU6LkWo3R9JADN8wNvXn^H%Ytn0mTgtbmKLc^NivCArCBX0VGutV&yvNpa+d&< zA;=@tR`%HztvI3reCaWbV%!!QPM)pZtJjzA`nKGoKhK=2-x3~b>qN1MhuXUG4~s=<$m`ypZ7npZ9HR>h&u}P%Wma zxck=l(^8s=f91OR>_uXWOyql_oGmv(f?-)yBq0V>;dGhY2-)p2Ias-`3lm{uZPZ2W z4H3PakVroB^-n)?-=DV1OABXDpIH66Us}1QRzNjP*qSWv?TSiOo^PW4!r=$~-H?3flCV(7Y??Hoj|)@~%Qs}-x;=f?9b zf5^oZ#7N{*My2lpgE)~y+W@_;y%&sc#0UqVZ{ zGbCw!>cRR4g5Awg6q=MKPD;T< z7ke4;dy603Z(uFtBz{>>^aN|troY1nbG4SWuTUKB~CX^K-uZ!2i<~9NDlGr zvD$s=$ViuDUg^)oZx%F{69%<5Es8;9UhF~uVN4*LBqK=)@S9o@DnI*3rT8rOfYG0S zVrIwM+9jDJ21E!Y*%n_UXKLe7idGq3``d*=B~y81%k#8|g=C7!S7J8ZVi&C>L*?rA zm5WO^Ma;!WthG8#i1!B$R2A&qR5stad*-+AD{IokbVXbfhLj&{-%`zk)1nsxP$njO6Y($)S@sV=|b)VAHC4ZHgUC z(YO&xkg*dcr_}SxRdLz_#)(&5cyuq6BtCadaq3LI`?{Hx%Sr%5%k?eW>f@~ZvUdIE zT7?8L(8`%FJ7+}O1D1d7&f?&&Ur}d5#otaXA;l4-PjHT$sy~X4=*V3zE!&ZkqYvCL z3+1=*8Kh!&V|z9pzS73BPzkW(tPq$!$x&15OvqUj-0Qk%FD9s>qREKdZ60{w-miS_ z6HV&U!r3#YcD?RLUVrOeP0qE9kmwl0CH zY%VX;Dln_$5V4l7$Od-%@xoC8&aeZa%XbW8>jWmAl>rAFj(%clw?2zBRp_q2jhF^_3*S^TEMbxQ$@yU87VHV&b0bIYS?$xPe3ZTNN zDrWULk0ixFC2xw6%%KmhDv?l+!681MA-%nXMcsf;r3ZYQUix;KieEAxzJF#_`#2_< zk<)fSS+D;X+og?jT}$B}IaFP(mSQbZEULeX`-Uq^fI*Hel50m)!bnK^8Bx96#1LGB!;z>SJvhokpmLAP zM+1=qT75E3!)UP}7_sG&p_13QVp+fem2COWK-p<8>5KWZ57)b&s)Fb`OBqi*yrZ?S zdaTvj@UvUFD;M z7DuQNP&E_|A`fYia@e$CFfgo*WwUY!%?n>?i(+QA@X5IS3*F5cOoeKac9C9d@ zl2|gBUPT2x+`4?l)3StPr;C#?%%(X6=pbUJPtF$yv12Zd};{XkA(j*T(kln9MI{KCL=W+V2^QXnmFLwP|Lh zi7idX)CnJGe*_8`eBZ4_$AaBNOLFuJi1uA7gpHVvQq~<*dnoNbA#Fj^-MguM$-8+8 zhu@MsULUbKex`mpq{r~9lk;%9sdGKLDQdSR0?3Eh2}F|0H}(}rKePWUudfnc0Mtd8 zBgkpMh`=y)k=s0$ahO`OycX%u;iO9eapRUctXXp;sVC`*gdKNHpBukX$ zHt$?}7hvZMtqoP?LYMEtLkmIdhl%d4p-Z}H?7;`KMlxYJ znDz)mX6Omd@zeEw5KL5MSHPjOL7I@OX2PJcw+l=l9A&Q>`a(l?<5cJLTEz19hXN-o z_t;X#pz|R_gtK*<0vsAim6Dcnc+L;e9c3dHFZg|nSdM9q;PCiHhHed;Srux-xS=US zmV4c8YwE3POttA=!Q}m;sAB>?80(%qTI&=yZLLTk3oZ|~)0k^E@84fqM(>QY!Xki^ zVK6F$Yxo-f&WPw|sx1Iv z%J)v4t@xQTW%MbV))$|wk1Ft@madT47a`nzZSBUdVEPl6_|35V_g-IlOC6E%2ccqQ zPkR#cLswUZL%vqWGIz9@=uaP+`NdZcR5sOz(jProzb!STk}NOn*@hm-M%_p%rX$r%eCk;Ya<9vGoJpvf%P*OEBwKG?_NH>O-Z>=Sxr3_4tW)2tM8Z zdP!PWb6h*MBnIgg@@(2oqpEf7uw$fZlM_R!TMR@D@@x0E z?kUMM0WRdui$%#3tzyx=m-1z^6VM5XfAr;SV$KUm`g0oiK@OkzgDk(;qdZ2e_D6Q zS-rf<4EOSnX<>Gt^Jld-WTN|w2og9mA0vj~jBA|${Lm4IsSv3e5}{LeBX*;X7+UVa zx<&cxpQ=e+V0QC@QV?ItY-YG{ASEL`0|2#lGjpSlovx{UMVs(QYH6Ox#TpsHnB68B z5eh>}^qGe%fxu6d$xn_1N86V&(c}>(j7-b~fLR-BII_}w;wq*hpbDWh!Dl|M zU-q?>Ncr*hUK;^6?N-cGnxZNkifZv4O)?Z0cElMl2-$#(cKZSDcw74TT$wOayJ^^QPGK%E$GxU$;fu1PmC z_0*Bk>J@{$RPgOw(tGmtRmLvjsyce^^knVDu* z*-m>|+Ewl2sZ`|dNgSbWx5n!%Oo{UiSCl)@Qq4*jWGIRRi;TM_wXr~eI$A#@a^A&O zr$oC7m~|`8lwsu8?kqb9RcsJ>f#At?dp(WjpK`ltWR$&)Yk#q93|wyj_B zi|_r}$BwNKXMzuU@M(*l=}1+G&;)d^UhVSA7_>o7o1hEKiq4-Vg0725R}Yce3FZ(J zuCp6cLkN4KE9OEsh)DCN}Dvbj2IwX5kvWyO}S+8^M4p5oGu2#-JCqGFIQG)=JgPcq8s$%73Stu zqsLD8sfyV=qyaYV#t4DfQASa=t(t>3(--hx@%mXoRn+6O4LsJ@TZW=1ljH?vxY@Wr(%VE}@F z?z(bay%5)Xrkq|^Nub%stXj)h&(@J*ByG%@LqIg|+*41#ZrWU&gC|GpXq7y)(+QIo z0She0I_)Yu<3k!}ASC?3# z3fK__PA2Z*afxE}=hzw};SVjR70ThC^8*63q9T6)T4@W84bppY$3jfS=8z6S42=LZ zV&$@W-wl(IZgc0l=hcUZg;H-XDZ|KeJ19V%oe^`~@M%+>8F$KVZC(581YLeeLukyd z5_JAh_&Y?mHowh?&F?w_mo2NM_Js#ItJ!eovM?)u;T~s|T%hhHj)ix^Ma1{SDO~sh z)`8yKQnqbRY%?EEenn<+v1B5^U_6Tqyp-G`?(7NQ;jbT;VI zAs~9|B5Z=s@=%1;h97I$O`?EUy}Z5X)GYkLH%BvW2eD)o1mp^_P%AR+)p1Ol*yKPx z0RkLpD^9F^^_hBx8Jd>k_}U$#7U={?Fn3*7Y4`TEm8>V-Ni!clP%`*vVuZEs)@`GP zkhyaG%-8poidxwlzcwBFvGy056XCx02??|a|M{mYsk--xN?Tb_%jVtf?gDa}v9oz~ z{fVD64J5|`Hr;+}e+59D5w&&H+LvNQ@<0ofUrf<3g2@_1n_YZceIm4Fmi$6`HSJCI zi8ykq!V?U{ky7x^-ozu4hYyypU70H{v=B+AdBM8M|O=v7-sJ-`d&IAk=+k+n*P`C~JCH&@A^j9e9`wC-+8U78d5w1`%2 zvsgv)!xL9u+a_3 zpEz;i-jDpt!h`-_tJH?F7~2uhn(AH;AQo)x+kO??=dor1cvlPRWQNHZfACtX+;P}m2MRA zC+rN%zIp{wPr1JRp}npGr_a`lpBX@)S49kZ0FbJ1o*n-(E?YZhIOdMwc@2_Rg# zm=O-A(^OmnVP;I|exnX(qqn(rf9si8ix}7ykJ2u}*oAN?^#me)g{VIUGVVhqbt-8- zYn#SDIkame%sLeMp#!5xp!~Km(3T;Hd|lV=-CnMhOp!Ns?QUNlNMMlfVW~}cK#a98 zaS(=UW_dR);%I^~pG_{r$<)cSHIuSnzPNd7dn(fwahx7{X6L$k!%M9rXuiutW*1>t zX2fI~Q*3QqS#9JlPYx=iHpwz5Y-s}(V|&)!XqBBWSCE=4hOa!;Vs29spb2P5L7?N8 z&eA`6j|&Y+Jh}_thNH219@sJW{rabB-pK?lZ0^WVVU5|$+7$CCM%GLd<*iLy2u76b zgrHvSnyDAWj-DFL<|ogTBC~X3B)T~MOJKC{M`g<{_zfo@ys25!R{>gjC zHZr_a2Q~{J9vjqYCb?Q!hqZG!DtkOBw>LyG4+xXWu=$w8;!CHcN-MgNq(Pz6EjG2uFk%hC$57H7uOTw3 zN+p{lkX+QBbJ}%$$x93De*KyHGy}rTN&js^g8BHjXWn#Gy%;aYO5StL#`@KRs_rYo z(t5l7!2PdZtc(h+R!M+5AjBpxIndtL*t6#=d6L2|mJqZbMJC;;q_%Z0Uv!14MUIi2 zSTO9wl+rbHuqBk3=qbscr40Ij{%*l|Ls z3Z0lEd_#Lfo`pR2XaBK7bpwFo=0R8IP*#H8)^w=zj+50Zesxs(vpT=sJxcn|wa-G8 z$M$h6Ra{f2n^fHC|$f*%O zVqroo2Sa)Sj(!K3L*qBtVn65xhfKi9!42YN!q>FxiQNedYeTzFbDj*npK;#L)V=7g zjddY(zZ2n2U^8pH*4B|2Ng0(1`yjPU2$ z-;@}JC)yh5#t1Vqp%V!j5-Jt^{L$6Dn`RzAG>R~_6pfbRG)x)TbGMw+qdP)_7=X1H z@8eaRViG7~{OfO{oDw2WECVvWQ8%*~N%FC2*M``%YV^$qk`EuKHHcNqN>Xh`WBiQy ziGgKIt`V+8_r3M$!?v_-@`7~Qmen<7Fl#ivadY`)gei*oE5?judLr-}?=E-d7bAA; zxR<_d$LIy63OK*r9?xz{>d%_rH&yR(mya&74^+%M6~^N;pMJ1B+U12iF4C^s+w?a| zIod-wheI%ebZGb`jEO^-E(nYa;Y=}F9btaS<9C&3j#hpZ;qYS|oxCEh<-mXRK-EHZ zX%o2r+4>7G%nCm0!){NUaRe3E`1A0@s{h9eWS}>m_t?%Tl63J|BZI>xY-&V|=P4tC zz$YH4S1f^X9feQ#p#v2pVYJSF_E^2|M%eC^>&ttWG%ChE8nL@A#*t*4I-54TU4~XQ zxgAGU545>EDk17%wOqwk!tKIW1!Quurz(h~4XA5%=#&#+oWMQpOiol0V-2C)-Ck+~ zRqhTFBq(k2SZ7*74Ks>?Doutk&!4ZK@U&a>E7zO8u(!Qkl_%Kj1hB8I1pxbhm%Ji z(4*LqT^1sfs-ZxT)vKW{!sI=aL`R^DA%+v%P#qp=uj+JuHZee=wQXNJX-z7VoFD>n zqkQfPR2k?@jq=4Tdl3U-1iBq%@}cAn0h!YQ*SeCP-yWkLM@H|q;T zLISn;ET`4x-TYNVM(~qtk@@<*Q4>VIu07zYOyxwayex0uwac#z@{X2ytD=ryho;@rptcr%2qC$`SOz^ zJ`0-0V8zpp5f{2t_MgV8x_7)fMnFh{R3%_r?rm%bPSziARK+0gd?_pVcB_Mlw&|a5 z?yqx3*r(6dfyN|UX4{!I1Vu(L-Cp+h1e0{)Ct7U;Rf*h9kxp;|uU=MSfEm{m$s`{A z5aake)-U_>Kloptc%uEa_Dc=t&o5rQ0z$g;Cw5mE$3%$oj)xA zEN^J<96=)Q=uV^9cSH0w)|8Ods|oblp|x4j1%|+!IEd3?1cs3l=)0Xs#@Ui{W};6z zx^Y`~ipUmznBnL23mm3XXR9^=r;0lPxCU`t`)Hhw3|}N^oy#F?F?3(#CmI_%<@y!1 z`V--Zp^aV9(gUAP@ zrzL4Q)J~)aTJvdD543quZH7w183Tj+_m3_rGi-KXo)=Q%{T|2)wS%IK_W<=ET{WOj&qe z`{}*O?%l#`J+Bsi?&kWPJAtQ<)ZM$lO1G`uoH<{MTqY4-OrSr0!)Ug02)KsR=Vt!& z;hLDZ7G8o;QX$ELc=+WD@+1b$xM+E?rQvi{!2{3LW;*-RM=Bn=QHhp~@tR&w)C7%| zZvZdA$V2TCW|Ms~^5oqcMqbFo(T2zN zyRonA85FS{Yl=y%@{m>~aw$+d7fQgf*4T7fPM8D}n!I=_s_{=FJ`k2R~E1qiarD^9Mh{;6GmRtAo=0gI>dwRwd#e0G*9GG@C9Dya~bK(KYpP89__@% zX@;9ss}Il}#JDgT_5l}mv_Gz#ZXDV{+cI=UvDbE@n;K%G6QOT|D^B89zvbEU$u78#D{N`0c6Kj%kFR5o!@|BX8!%xM`3_mO@0&mwtA}Be(RUJR^AEZ6{q*Mk6EY{TfUS*OWv$Bsn>C7k677{ zDZ4yEP8b`HuiY{8zkG3KeLMOU!1((Yl^wRkUFpI(awxtGP=o-S)JZeZCz1yRWORBYE47>Jakod`KD%>a#2x>!UDIy}g_3 z)`b>p=@8BATUx4=m40Ji{oyC{OkP?u*iJ4WVLl zWaG$v8mIW^MT-~z{_p?6GY@~GTwZ!Oarn8*Z+*{euB%@WLV8Rmh#xe^t`Z+GiG`Cs ziHtMB=>a&gL#CIsx;3>nGgPXAco`DCcIDC<;_MEq&~csJY+Tf^FnD(+NNrprCNSi5 z7m$D_RE7dWbbei5r)t;P?$VnFFUGt9K!E(@1hW)9W?~h=sNz;JAspD>RXU;7bZF?z)@7b@WjXzXq`dNV3?}(UaZBq z%PNB{4r`io{(KEqj~*(qjC&;WxAV}qj~C5BZ;(Obn1z{osK!T)Fh%FUqX%dH)@zID&@5S8 zisadO+paSHKzrrcb;`9))`|)yE053Ba!ED6#Un6C$3AeN7ODhaQ)*jL!au*L+GY1` zyGL63lT4iqZB0#8@mrc_4#lSkYnE490;}3gL`#*UTxc`+cjSLsvi)t~m5S#ebATss$_R%Ugou2=rd=KZGaD5FbQ#F5E5)>h$nH=;R?}4wY|7+M zotYGa?haCw*TlL_!Ry#yLbtn^pjWi_t)d=G5X#}5T6BjNhM4~jai}uC9Z=xXITUcs85Xh(iB8Do3bPPFin9EHenc+`58RP z&iTy4qaVk`T{jLo2kiE(?{<)JZ#-nKTUo9(tux7Ba+E`n`OH!Hov+RO{HyA-Q;}TV zR-=MM5=lfd54+NZ^=+}Ka;cUL5RjiqXr!u)B!|OP)sv5uj4+i%r_8p_uVdPR#G#m% zq;S~DdBSG5P;-AfYRV0n^~aCzaHL{M?xTXAqd->|C85dbrxv)Ng(;u2$#n6#uBNq* z<`AgZ+LJ~!8TP4{;_*tGIC3KCK#htPq-pT*_*HpTg89`Cw@_Q^$tEHxFX3v%&h{(C z@x#^nFh^S5ZgMdB6@W6n+(@X^WMPnuR`TF#U^_r*njqym?`ovjGPnVk5^BXzUHxZbP2ZB1CVq$I^~_*DD4nFOe*l>PBpMBx7RJiJuKO<;ld zLnqs>3)_oG>ES{<6^Y?jU$rGmy(CUKy}qh%tGD~QB=kz^)%LMRw**ffuBXthGO>x- zHA}#4XZ3Bn#9Z!5bk^Ds*KH~~0t`B846-k~)j3=XOlO9v)FQjeJh8{2wf|VjK<7evJh}P+_Da z7V@J!h-46Y?TU8J50E}rP6K~vVH!j4J@^R~yK}WG5RVbnuG4^=Lu9}b7bnORX z7J1(_zue`SOujHPaEY^b9ho$F%c{DB&D!o8$=D3<##KZ8@+I|t7WE9#ObLSpBmbAjPoAQ3 z*|L()E`H>3-qub|T4?Ib=G7s!zjkM}LY##4+m5w$71!!Xn{nQEYuVA&<|YvciG^)z z%R602i{Rv$`sTeJ%c2Z*Rgs~Hux(pY!m1c~Z)%Y=%LvTSEZYdkc?dI-j>@2+ z)trgM?x!CrzcC2$3n?*UGsd00Uwmq0$`+Nf)-2$Y?EIl_&}*yf#!SGEWLMM=iJyF$ z4_yE{5oGuA_IC;U?gV;Z2)K1yC2Ye_l*GPm=m>gOgg=ZNCZe*-zOA=<0u#n3cL#R> z^FR#0B18TJCbZ_li`tpB>cad=7+&<WFzu9FGj?514c zSTidO2wfL3zrhLqkWSUCik+FV?{>m?omHF6lwnR|f=@CdIDQ7@q2-D3-N4rcMz{A3 zS-m2eX;o_SrmM!2aeGMEr`w206p|AR(fz_i6;l~=@q6po>_1b29%ugIax74E`+fFY zrALWYi*~N7RhZa;+y-P`&v8t&IB4>~9*=fqSBa{-SlT}QK!$y~ zBn3tW4N+I6j3Vy@?6Q_ee-f;`8X%bKHjOMK;K=9Lfet4xf0D0!Ab-M{VtaQAyn?FtH)}&%=IBzE@!hvLSlqe8`KcRA`26|t88v>ttU9Fi zd@*zCwwuPBX0DFcwBP+*wzS?qM>vZF=4?zq5Z=C~Zbz%D$BS#qtJaTJ)(Gd%1<p_>oT{y37 z=;R!I{GkQ>uRZhp?$E+Kh~%*B=rn!;8wzxl>8c^lo}a9mOEQ?XHq2=|y2atpbzlU8 z2&fWO@8Jop;qDN_Ba#LEzMH$;?8YxTTM%Xq+T)dlUInuXPaNwoH3x=~;5+WF%>W;V zE=P%~qhEd9IN@BU5woi*yU`_Vci*w+V~BlXk&nQ^@jF%~Rzy0yyC&l#2USrK29DjF z|ItnP^wA=0iSYGTj_lCthb%^nCx8xm6@u9(56>)Vzx|2uqVbOqpFDBL-ig+H@)%Lq2lZ^*)^L+y#Mfx8k1?&v#xgaW#o@3VH`VMofCJo4568Vl{}gy z@d=LOPPn}i=YP(7o!MIHeYhr@wTYOna2%^z_5So^pY4#jil z>-A?wdJI&wnjJv%t~0M(SCMR8Qwz8V?%1K?Sd^pIMHT<$l_mb!qodAe2NAQPg7@88 ziV3oRA1a3iq0E^86wSh4Xupr=PD>+6Kmdhtdc%l~&FsRtnR=g^yaX)ru?d)H#(PPT z%%g{D>XBwdARB?`<_v-GL5}q7v7+lM7UR!@!022WFJQ$2M3Jx9A9YSk-eM56F6(g} z1Tdol%EUZ@Fgg1C6P0J8BlA%E`#ew`-sW5&+?#`T0<2m#vJim@OfVzH*RWTIQBBKt z#I;6E$V}r!1!hGLV;Ij%!aQ0px_SjK>hyvGdj*#We$W@NhG+20aW?0*pqGk?NbC-W zCzvPHg}Zy|NZpDDF*vI(W_LIAJ0U!jsbO(2E)OxGWjBhQKaY!f(Cu_O-a~Xd!@)Yl zaDIaHb7s3M)>VmS=KX!j*%<^^Kji^A2u)8m66u-H@+_`1#c4DMj-A;QnQ=OmFBYj?C?Yvdi z;cho|dcdx3&n+=NU;+Wa*LHly!$JJT!>1nj%!f`KAHVog{!y8_ElYnh{ zAnnyf+7g5V>Hj*hTX%&C1PDhR;lgo^8w~)nt7o^D;J5&-&JUrJX?^jD%H6|%0BPf?IczIS>%D0pX$Ls1KRpOkl_y3U`FrH}bvJMu2dXkqoVNXg1BU zrNeVQLX3oIqe3!`#mp%R`295KlIa9xIJ(8DhlqR#Mj$fTjj)Xj`#7o1D1?ZkK4RE% z`9wEH4xOxrAGhtCxngZC$J5mhHTASVvax0Ls9gz?{4LvxiNj+aMI=I{;meq#A;w!q zOe^H$g@7#w`u3gAe|?_{`rh&ET=I)rU$q}Y8SVG9hc|>-Cc}b3FNcilngqwqF|@il zclq~elq0&Pb#~wb?X!k-F*AK5*eChETStB|IRaCp+KQ@u#Om($UM+CP5f#Gv>ZUFA zYe5{ffAY}Grq%VlLd!L=0GP9^zcRp;U^v!PU^rvF_KPSUb(#4gJk%ieC9x;fWM4Jsq zElxkR2vCeloFYOigV5Pk_GaeYt1cFU!B7`+(-GqkgTrzfj{fa~BnL4qHq`Cbu4>OS zriCXkm)~i0%aL~|v)em7Sh&HL8!ssgb40F`Y0TB4Y5q%UyD{Ms>8MKmynuF*DHul}O?!O@y)>`ATYW;<^j4 zh64oXN`{hn$)fB=7~+nMyku`nc@v&m%uy;ZPGwSynbfE6#v^b_5*>AA>_<4rgbj3e zw}eZ`JOv8>fSN9>W{BdA=tz7oyXY_DH_t!jU?IL4Vh+kP}eSgx5zxM*f|)RB|5 zM)vW)s$4TK`KN!BNg@%nq-Y3DcK*bp?T;QPnjK6LM)%*`SN9CP;T$DdVF8Cc2<-p< znYzJv*PilHt23>Qw9?XuF$<%Fdt)7v1VG+VOR_7gmf*r?9nGWodC(~ z(yi9D@25)j;z$){_%@-6QyJm1V7CQ8Qf9BLM&%C}4U!V_fBe8GMb1J}Kez(WG<>?C ziiMxLss6y?()KykBkk!f`HG}v^ZLP2h!H7fK9KLuXZe!)9Y>uZ?WZCH+zdF|IjA;{ z^TOQk=?y8D2y;LIjZh`dyz4;)t$`wR>!P;WH9ma*5LrsY$Kfl`D7x9$x3Q3ZAcIE4NLu8KRm=726-)l&kAC;)bM4FammbcY zJGXlAi8Z(U^x~z<(gUE4nvk+i)V2hWru8aK>qzsJA&3nZYcQulj1ku|%qpO^|Jo}! zb+*3E4-@in9n5M9GyGv~8Zum0>M~Aar!Hb1%d^(@UH1Ta&!uyCiF_J9p%_9{y4pxi zzE=%FM)&42nN=W2RZ7C~*msAR%ShtvTUU2JbPK&|2%}uy`hmrZ>Q|__Ksu&-pbHF3 zNMnP}gwSeTVi^sT6|Qq(W*0y2py5N8cV;HY#1O@t|KaNgWeC=U z5rLrQnkH-*Ni9S@TB!=(H1xSgi#9@n!G~l9x(EaLs%pE-7T4%{^i<8cZf*~sEct$1 z41JFsuH}1@TqRR(`FzQ)>Ky}cH;?s=?U_Kf|;8#bDAI0I5H@PrdozSiw zQ`OyVRW8G?t(LTAYC%eS?ScUxG{M$qz?2C<;z4r%u}UE_p)>n)H`fb9AS(9BbDa)Z zv7|Os2)F3&H43DRSf!8Rz~I6>Cqn!{_@i$7R8*v zYGdt-PZXyP#Q5ZKBANBbpp`j%n1t_rye=-N9OJrr+lYkl$UrR}w$(9O9TcmLjgP1| z-&1kS4cqpjXxD z&N0k#OQ^o za3K7?3*;fXQUrYVGdZcs7GOTumi#0W%a^(@ttw2du!W9svy==8CteVO%&gx9G6SJ; zq%yq<5Cqa#y0!?gz-;U=2+BhzD_tX;d?=km4nH|lVEk#D)a7Qw^7f!XMz2AiY)RFp7lnhTp5*30N^aPGs#_|8& z>+6@Bix<};@#>k4Bdyqv-|C8%_#)OUuU2y;U1LFQ^~`mncS*FORKY&zVlij~k*lg7)~zVvKloPl1HWXT z4y*gXruesCTTRG!+-=&Mzd4ZJCZCVa5>^YjWyOET?&{<$G`PPi@5kFJQd{aef4){N z)pA5ptxZ>pN&iYt6R7a$4vLFW!&@$bsj+o@$2ZliRe!egApv75Kia-HLna(Dj_KL+^+`Cp zd1RWvwyj_B%`g4=Q-AfvVqKDW_UR|y`XfJae)V?IrY&0S#7MX3pWK|J8^aGBet;8Z zCnhiy1~R>#XP&$js_`Rz&O#SB?(!Fp{j&!rJ9C2iuuHYnW9nn$SYyfQHYCF5K7Drv3Ka3I0&B(+C~nN^}#R{vTFN$S0YOicOH1eGAm)tO!yy zab)}I735lr!5G0I+zkSob9G1*qdVaxS~%U^=JvJCSnl@Iv=BLU4Q0DGjy`l1zj+6Z z*n}4O9;z~7bV5~PONYF6M=dD)gE!Yx79E+YG!_y2@n|t_ zVgIqR!!oFS%heTt1Ny#OMo&S~U1*$yEe4W3LKD`LZ7@+qM&e{<(II5SAOds+(9#JB z9xd)3IyiHC%Ug)02Si0ii*j-juHI+?YlZnGK{76kbt~#EGm{JOLQI~KkHR~HsJ7%PsQDc6aIFe7=ppQI+IHzZmzvFv{Si8}NV z5bz&AI7%16G%9jN)aJ}7^_G6%lTWFNFDhyg4qsyHoY%FELW9`U=ET-~G@xm5#}sCK zQH)9uYf5SM(#5qxmkWxiG!v9Df(Syv^!v8inweJIy1gw5w;>|B zkq^9fXRMIw~()knUoMCMjUbf@~0v!a*lzn)8`e^-- zEh^ESaA#LanwcJb^@itbBdiCmYwvetpdU^!VcHF|YYD)p70z$`$;_|3zDW-d$sk5z zp&Rc_=(D~mVuUl}idOcJ+CaYIE~|@$ziN41P!LHIt-#U{3F~9+Bi2#r0VgZM;YAh2 z#f!@_^#>2s`VN^0M-h>4XV_JmepW6U-R!AK+OeW75t_E8v(ZZVs~@g3C8ijaBO;zS zJZd{SsYWs?@^+z^?bRoi<*y~&eoZDLOcjIh_T5#5)}sC;_?*mclCR%XW*JE$&W=bV zPu0EcsVTm+Z$qn!Yh{vGw7g%*e%b3MxHqU&2nNB|ZJxGxcD+n%sW+@!s~A zylwJemLW$f*)TDirb$WDKgw$R_+^2rYrqkPWS9C^o^C5F?RJ7M`~(AN#=%{O#9D8t z(qvwE%wbi9$)Idl@WzciQ4lSU`4t98s#p_PMb#{8yWY@>C)=GB3qO3#%om>=iRDbN zRmWaY{Lei)dVXHb&p9x@^?m=X^<*2LfEK9|QUt2i;}SSc%sB3FG=W%*Anuk==Oez& z0>6(QocZK~rD&Imz*tZlgY3itFdI6$c5R<~td{LK0xJ6OY+JIt{ffosk399~pZvq~ zqrWe8$>HqjQ|DK1_~oCwtL}|kUujg1wFWx*^%A79N`0jkbOL!>y21wOt}YDeqQ4w0 zd_Wft0US9r9f<^DDp5s#Xt}Ecy6>iH!saw`GQiMI$HDejJKcHaH>xn<5e#S(%v#56 zUp&L?RUxv$=2i8eCG-#zrltkv0?Dr1wGg(Phv|nd)@4Ai+WOmYy>=oPnCSl?*TS?g zghLCQHtFpv*NuJwm!lK!6S_S}TWb4pRE3js!wh~^`!j72VYQft+Oi}2IZ@xSd*mR>G)Io#hUsZ-xepCEP-j1QszRJ&A5il_^N6qYWaiR1YlCzwz?KL5Khso zuIBJ@DmLzJ+ET&^co(fOs32&u`J4HE|2QX6l@h1kFKZoG=V6NqLPR|r9=pwsUfbaU zblS){JD8QM-ngYC^Rl^6qeZ=rS2NN%1YAwoMccYe1VSj$!cIJ4b_|6>S0}dit!9#^FVF3c`&S<2-q|;oM3v@guz+uGi7$1 z9!95y-C7L4>PKMcRGQnNirI|*eF}`hStlOIj1@wy6elkY!H`zPpj+4cT^Q3OPI}|6 z_5%DwR}4Ff<+Us70buCxlhDMJvd`!eg4CLK38#$8@zYfs4kE2HNz=lio+gVq!8~@b zw6HQIC)H4u^e)jxk!4_QBE2A%IEmTPQSvIyAyYX}TLtTFzen2LB4mnyVAx6UaimIm zgvjy@L>C{Zgn%Sibi<+Y>4)ku6yY#!u`JH_-CAV_Ux!YWN&+BR8Cy%Kv6uxpR;tM4 zenzAImoJu0!gZ_Ao-ik$)(?qU58N?6B@%i4*r6rXQT*ndK&E7-pPYu}B zIr4b-*t|r{5XUJ#BO-|@a4pNiHTf4`J#+JxIuQK?aC;ETZ063*u1#af08Vcs&|)Xu zrHaZ^M~X>?yhc?d?E}V9C#G8xuqqoFRT^^rrkVfvjjDa%xmv(_+wP*(=0=N*TZl8~ z7vJ--51l%Zd$>yxCyyOo`l_GXy0MnrC^{>T~`%yeCVPCs`J zBBna0W5~&&=}1+yLbI%6p=))5m|NW3pAS{ov^xwBI%h}>1%|-a8@+ZY7A<9>>d5L< z5P2sA7P|N^yOHm^%FshjhzU$H!L}M^PM-WKjacvA1Z;~VaK%M8RS{0Ff-5n#OWGZ2%!xe&I7z!A4$zui#LfWFt2!n|RO!9`T45|Uw_dHsSQpG;WckQXcC&KtT zLCFq|jkPR)pb6Uq;|LP&W-)s$12al<40Yf51_|LPZFkS-}Hl(tw)iSJhXX&NtU$_8(!Em3;9F2Trs< zt+=Sop6wN|93wRO>W4-stBot`E${l$_T{z8nZ8PuPTsk;2;f>3t+LkA00gIG5+tp> zg@mX-gE*cbSS{!32ZW$>qiA z(SBZ<-Uw*Dul*rrQ0fixItTQpVA88BBoaorj=|UNs9LnHz|YGg#PlTZI24sOVEm6C ztoC9`6`E{?T1UCf!pe%g2{RI1U_X3Y;$-}Z!=v@?28LUl?YI{KXnOD=s*ISMZ|v0b0Piu+!*eI{9AoO2{luri;y^(yKBGFZ?na z+Mf#xu|}qk!YR8}2KQhn7R+TlbAzgND;5;MaPRRs*lC4iI5$nQ; zR2g$1>?p5b9T+s#kuJmn-*fE`lzBv*$;@ zNUCmWrxxP=XJ_`dvzge$NU^?*G|nC_yIs8qQ-(ozC+armV|ky!QN zWA#H(VzK#n=^E#EC#aIR`5I=da=WP#x6_A(u@ zw6O!7Lm3Mp(iK7YyEl&Roq{mCN=IiZyM-a}V?3)@V0UN)J#90bApuMcL+VKFG={p+ zulkPOoT1sbTvMkp^mUeQLFDcSU{;Ua+RzJ5=TA30RgoXo?%Gg4KZ(GQ-%ZVmrHiYR z$>{BANp_4#fNwi`s&49HJSr3Nc6H16pcQHryRG0vGK1Cdvqi}{76{n7BC*J4cYph%gz?zM%nWgcsp7Or?G59h{zUt6nT-nj8{11y zB)K5`SbLvBcTrE;VC2M4NOsZYJ0q4XSh_BQ`EF|6Z}KK3)l>*wP_0Aj@eZBjuva4uY(oQ?01i*(7h| zK`lG1J*&gpuSC)O7RJj}iI8Nqt33g_WotbeF#!I`>u3I(PnTsb)QWKBvevE^L%JHB zX3|ZNcJ-~&7C4mhz_aHn7)JPY9n~Rj>uWKNfX_cs+s<{h;N?DZd4+vx$O z&b&FEr#RC4{-I1>v{87&S8s%u+Va?r2!yk~^@%i6NLk*ZBC5182BVl3B~)-Ya!kXq z8yW$G54Ar60Qa`NBGtL7CnN9Sh+o6ZTLBbSz}|@x!&=nq4x~y9EKST2@U+`UmMNaID?68Lx6m zL#%Z#DT6ROKVHJ=1}C5@=i3mjg{O~wtHuZm7j za56B>iq0R7!WVp{O(=GH9)tM9Dw4Af91~*D8#Q4(!!)MJz?^}YtNBAkS@PqDY8TPX zQZz%`?G_AfXlC;ZMD#G)$S7mEYl);!kQv}WX;40Srk+=vIy*`b`=*wW7)Jm)1elD| zvUrOo=lTd8^k!spSG>3 z{GR1lNCvOAjEvhNk=oOqU?Y-q>kxNG_dZei8OeY^bPt}aC!9bxiIEL|w93<%F@i66 zWttE8dL~uLp|hJz>*0fyyxOh&a-1GjhT0Ou{sKJ2{>@wJVcqY1t?sg1r0!U*Xn7JD zx0ky&lnk@g%w|Ct%A}I7-d2)d-&bwXIfqZx;g&HtKo#5gv#E%j63Na%ZQ>_EI89@p zJfJ?CQW~w8PuOu&Kcc<0xc8T@-F^$^0sR-6^RHa*D(_LTsTr1L1QE~LqBu9taIk{+b z^NdatY>4Z(E`RKyzk2v9pJ{@ZBF>*Xdvx*oJ$L+Qb=i2NO%J${)9SfEhH%Q>-XJu- z-sn}aY$)!EL2COhbhFe`c78V%l}@K>D2X{u&xN6IT=dW*Y;aiIG`WH&}2%#uG*f62~e;D)FF%(EX{ zGJVX6DPCwf z)Af)byQWKGi3hDbab`TljK`vkcxpMKWvI^yU>D*v!q2uwoj*SssPDL<9?#^n(}qwM z2=isrSi5;)3bIyPvwxdVShJa`*vW)~nJqn5GL6Y#CK1yU7o0xSzGN!qbOH;8_dk22 zsk9k~qz>eou<+>qKR#1t3FBlkC1%EfAG^NZ+{Z~^1o+MLl=uZ8>`dPRsC2Pe=9iJ` z*w(aYrI(Umgb#6-CcNv~QCE;0{b`ycoNZc{znyA-Z8^s)ZP|T!E&pxF`<`t!Hbnz+1uh{-Ydlj8bJygr*L0cCQc<4aAl7IYk)&BaYMh3+L&a0j6y)adv zw22CbjFMj^;mB-RT`@%6ZdYV82uGtdKLQ=bJ=VG;^jg+(pi?{KfeIDw>5IC6(jM3AEY(3%j2&IG01=t7^g_?5l{Ch3E1(gJfipeZsEW{e0Ui3p@BGD=$}QM0V( zprs&bS`q~#vfa(As&y3e==)g980e-4fCU`LE?UDg~UVy1=3BZ+=Q1aFelGf9u) z`ap}#G?VW&0Gp-T(l25wmXssCq?B>SAPcTRIoY}ufG$dOB7Qqd#y*F$I?H0%KBqT z4ASKyy{ddLoxii5?z%mI58YQ!P&J3I$dH%fv!*$})xL~YO&I^yQri9G;i^q|9+e;EBj~%?>tv|Q?vRcRO&4fCpFNCIPR0xt;qff(Wr8dn6bX_Lx zrail4iwMKLr5u|EXPL^s0W@vB~-s>@7>!8EhG=sf_K(1mMM9IsLK1CcTW#Exykdwv%T zm>F0gWz3SF?(hJS+7(M{Ja?TY1}9ryoavH)4}_zlDhn|QrZXcc5=M*z=uK?|GtcG^ zQ%1gbG(RYPELh#u^x7Ic3GJ@CZQcm`zwV=9JAg zZ#tBOGk^WrBgGlDrA@EI2;FH28z3 z&06MeF_7;Ej*;r{mTNI}+s^h9Vsna5{)%;Vj)hZ!%B1|%k?L?~ z?^~}aCPbJoL|3;P?+4nS#F2G~Jg&*4s>=iffIxNNIC#KEuUlD3MVRN$&wT9LB?dS< z){cH@5nZ4nBoGWDuV+SAslyXbIFQ(Yg{TAsY;@DWE+0Y}BM%;^j)MRB3-!lsf>aH? zByZ{HeCTwSN5t<-#n9srv$~j7axVS^=8;jA{+s~I7to@P;p)|_7=ASQ6?`Ayl?AMe zi3MI85u5+=eo5%^8Cizo%S$yi#I&`JA#+v%ScvQ4v@xrl&piD6h(8pDKGqMlCM=K% zhw(ng#LmR%PaqssJ(83`_s*u7+r0bak(ui^KQG@6##ab@b6ygQNU92Li;M}It^r+% zhRpG3wNW<*%l^OkRMDzn=)%kwBSYi1OxP@Eck7zbmn_hrq;g&R%_G9@nRBCGGNRLU ztJ_z)HMZ6@#+?~=hubelgetjFMkOA_knm2lg|@7M5vbZWk)vfTJ>?<;YGVz5-?3Vh zkuzn(?-L%&T&QJA;*~s@g>SXInV2ohRfx^2XMXqVmGKi!LJ8z!(3aqpm@>I9JlXyx zbc=!TU$#`+mdKOFMGIn6TQGs0gbJ7}*xkHp^kK3vdBF#%FeIKBOqmoJ{l^cEKFM76 zTU%6BLoOe^kOg(PpQ|2lhlVfy${n2*tz&T#-W6+0ossIO@y#B-jaUv@oYqTo5-9%N>#@DqQC(ulJ_V0b8 z1O%y8&h7k600|~y*`k$OTy_W`lyYKcDXj#rD*bbO$>sO_(Z4@-;Hgr(Byr~W!5{je zH!t3D6Iw$ApB4pu*TtobfHq?0v45RKUC}pKwzTD(@;CVKu28krX_$dV*wp^%FcA4^X^FyttdtkdXV{wVr;s0oF@TJ z`Zi)6!`*1X+4dd%1P}pNv0Y4&D8nCBjUzF{ty?jA87<6B<4?b7uU$c|T2^ZfMhRnr zcyvWZjL3J75$>H}O9N9yxi2yn>oGgikScQ``j9q7WnkJoPOHZVMw17s36;`!DwQpKZCidIJMp0 zo@C(I)RxEbm?M%HcOvc8#&#Y)d|>9b9TlOIi{h45^~Mn0%U9KK^oy@9CXdFkEXu?? zU3Q!#?3yj&S&1P!Q#qtN%)yy~u3q2v$ByPQOFk3!Tx07{ICLb*M2qpR9)83kFu}}f z&gr%&1Kk3UNt~UNhD1G_XaNpHQVFS2cKN7-X;>SR;h9$DA3%#FepDbOT}0RIz)5V` z63-~_JTdx$63|T=MBW@vCArJUcw678OtHn^bDdgB*g5mrM@FAA#lfe3)w+_8|n(_P9>G+&(|9w?i}iTYj1ePCV8Dno{U?XQD?TB3X+%z_DR;S4BA@; z@IoZI!--SY(0Qki4A8~=+B4N5PaUZbkuZ*cS8pHfW*gqJu3S2bEk=Ny zZ|$$Qy#z*>eTe*z-n{aUKL5~TU;S(oycBWn+_`6up1JOgKe2b~vZJT!=s)IM9S=ci z1!$~J2O2fzm!^xlWID%qm}{A&(Fu_SjvS6iMymro?YDF4if94AG-m=4fUXV` z2J`+-I3mn&HwtP_U0V2&Jtz%CTF#Xw*?zy^3X9h`tUfa@-n4rCz%S{fTHTU=l2NmKzX7S^w9@9K_!d}M^wIW+)|SCl}ssyfYn#!U9n zkYP<3zfkIrkN)CE8vE5}Mvo9krj5X?NrX&PZg21V7`GoeRF4X-yL{#|57k?-EMO8N zuMK3y-!B=Y^oW`5z;YA*p|eFj7b{{5{hgro#~ z(^F(H%k22?$%eKqZPS@Bs2irtVrq4%bLsweXi9x%=BI19ex%~;Qv&-oP1E_KPB>Ao zv)$9qkep6=R|}1k2D*Ym&TAL*ff044O&6 zWr(kfu2@%9CExx`Gf8}o_~jx|z3rg zf|46_#I-r)p##lt`*Vs3n4NMlUJr$`+0#KFfxwxsFdhF)(9=y7IB>XGaAN*dZUY$A*beqIim@EAHom$VziU~p{w7Z}xsZ)4D;=(aOr z!xBi!1M;K4%=Bwl)NMoN^%zkCzld;DWmlOTg`zS?T&)Zhhs`7$INIAyLFi;8ui4SQ z!_qQxekEA(5*M$_56YTr$hS*bmdewOQ^MS(RAmMQ{3LVuaPJe9EMB>;9vCLMmOxtl%eU1+30oWyfSqe+e)i^ygw6U2)i}2%EL6IHiBlh;(A8~GKw@|E zmYFX-)f~0YU9D+)I|L_dNhGh`R4UBlS8cm|G+apdvi4ZJ!>OA!xMbw5>-tSvvU9gsRDf|b zvHXD1tr{`H0wAE4FgrjdJ&Dg|A^|ZVi1nWXL>&#P_HM2+VifhK+9=vhkPfH|DtEtj zS%U;aKm$qIaw8Y5fBlt3OP8;F=+FM`$&>9sUXnO=;F(>w-+g-3PD9r5)1!uKohEb) zY1d$CGZY|(!Uz_W;gHE;bx?+U=tK~|m=Oy)ks}3a<1U<`@Q}W8eJyc9=LCH>t5<;2 z7we~mV{>RZz!@${bab&m`Hm5|c(2iVUkmkEHj7_`xxd&i4h0sHRz;3U?L?fUJgzsk3 zD7R~4z3v3@sIS!alPd8<5fMd_5Eq!~UR7UwvN*Am4V~R6A_!-opyIy0%VdM~E8A$( z24ON0qx)d{5feL9>}G%>sRO_Ibd6LbvB+1)YwnklJ+5X_zT-oEW>?8}77-<#%%+ zS|MqvcmM&aM9WM0GH)6dS~m((Cuk-x3p%HcY_qABsqia?HU;C?5owce&c+Y|vvFsa z0HFKpQOr^g-_hx-S2*P3!fA#e|IE=!YeiVWj4uZu97hmIGKr*z)D7Cc@^rm_9Zt1$ z9C;=Vhzj5zYJWkJqiza%t&_nd(s$EL7?xSRQk&f*W9Z0tq;^)Ef8i1sL|r_ghsbVc zcE5q1nUIGGnPA8aF;~D7x?ORC9}LYp$){yz)#^y?tN}l7cDCAJe&a7UoGL4)7DHdb zcZgShLO94(Px)xn=?$qN_{* zlaY+1)3xD{84k6FEuQQ>?JYhDwYHi5?>^^z8s^a2cd)Wkr7W!^DQl;cB z*Dz!+wa0U#hB-3GWvmcep#zVwVM{LNbaS| zZso)+-#y>@oZ?6CuUigws2DWpR*WskbSZ-ftpLpYK##$AVf_8KRu!7HIsq2dB(2*M z049&hq`B4h=9K01TMhv)j~=Q&Yt3Len5kc{>q>6$Hn z`I(O%ditRe_tL|elgF1VTfXacKXTpXB?9gnXmy?8?TQX?j5*Fp==?4mVz}$>)F2S( zE1dB4uyY`i0Xn-w<^<@1;}yEA1Vh^W26s;$sl`@!?T-3$+WF;L(4!5V*z)1?AtE!h z98Bv@1OG^SiDnu@?-S_!34ys(CitDk&xtyLq5Lp%cSnxMtX>u`dO_C(BpryH1Jf#i z$zTw(**LZ3Am)Co*Fp~|MCg=2vY(KcWI}XAz)%o$3y^@p?89w;!wbkDm^G;4+kNa{ zy(rX!Id!JapM;%P%*eaysYJl;vD5WoBQg=|1Q?gV;TR4$$_$1!Rrp_iYV>10wRR0_ z4)GgQ`o2k(+m=J`otndg9eqKr-v8Fdt>Ah4^CmwcFe4e$UleVRzN? zkr)BQ(RS3*UDcDVKbhhS6Tz2DSS<@$5rT|iFf%of6xi0)^;AYxw%88Uy*dXGGgHeTKSYET@BhXv(($4|>Z zTOwIUq1j>i*_LA%C%^J^MN3N+Fk3y-6w6dQc9>t^SKr%u>~N(hVMadI{8L8KrloYl ziDdX9QN^9WD3ZxBjbi#!r(sqO(2y$l^h34Y1W&cEmFod5idmq7$)hq(Pn@YAZKzch zbm1VsXCzNRNU0qPbUJ*Jp<{no>oDp6bXxdn-Jd^N4_4XNuJBXh2z7!JoB!Qk)=xEu zP=#MXuBw=w%$*6!Z1W=)df3^W(3*=mfoaaHm=?=Rropf_^f0`Rp~^Jok7WMZ?;&1U zy?QzD$^zE+?2NxCe!G$Bb^2L{hQ2#JX3dIO#X9HcsY>|ZMa_me5f5S0gAsp%88O?m zweVpsM=(^JAVT*tLGSx*m)3)$Vo~_$j=tdH=G7}!6?hVEb2}0W#OqE1a zx@jJTc*jgtWUkuK);k9F3Gdoi=}zXze8H+m3xbpf#AewUr`-=9c-}}#se=SvHj`;p zikTl

          3=sX(Jg^EGy+D6qU@X%Ia0uB4gTi1Nr&3wDi=`_QBDX&HV1yT6)*gdh=7B z!9p@Hn?y|{%a)YWr;n7Rj59eI$V+Z-c~}Cd2wSrFTlZ8VB{kHOLaJ10ReigPfaWO6 zOM8BpY>x^r#NZ20)an?TVmb=AORLjNblzeUOf_U@*>uZsu-z@^t4xUjKNhkWlt-Gm zslIt@aqd;80PWCZb zvtcs$js2sKL9kB?Ge@UV=Z3WXTl;4I>DxvtbfT@qQAM-7V{JWrW=d6#c#J4>fG~w| z?-Mm8Q{*LUg=lvdS{;udJoNi-se7JB+8=+ht2xz;Z4oU3Ze4J4gXezZ@k8~?3yv7! z;@x{|Woq^K`yTs0 z|H|L^+;dw6ENswGdMRBRPJo_R;sJdgv97+fz5EM=!&Ij0BH+Y}anM~smkb|-kKb*stiZuVcm*SQEP}csD9^hxZbu^DgG+U!Ob9EDA*>TqO<=l}g@r9wdK(lktV@)LEf(+aw@bP$@BjuOKk ze5<}&l;>V10>TFB`kH&|$FkEV9c4a8C5#HG)glsDj+(akk7V`m&N|IjEgPjnBymzQ zvfq&%d>oUOpMBpUHP(}d3PI3|uNtkEP*YksO zj%G`f^yjIzMsJt*$mHPC;VuWX50&M#Jxk+@P1y(;Dikrb`d2?(oa)|YN(tZEKeM)d z727FVho;3zok|2!<^o|vl`Hm1mA3o|l9ftZqWK05y6bdLB?J_3kr=+uHTv31Gxz^V2( zXR-}3>{;X=~xPa8UTAlM@ItuaF(Y1n!U5Js6iP`{jTJd6`FGV?lH%u){O@tTB zvwHQiLasjji_wdMm#=&0!S}@JcNwoNU|nR)f6}~sr_{vi6>v0t5-wJ zWj5-=S{zeIr~9mc2!AL5J%9*LRMq@Fo=1T|wl4OCCr66{Lpz9+WwoPcimG7s^7>?W5HZ^ju?eZND?CX6BB2i5DMcc&iz46zQubY0A{pC{1z<#w zY{pvB8idqDHWR0SNY#?@-vq?h5KQxTZ;`4bZ3F;6sRkGEs1_&cIRtje=f_z%Z@hBs zOFVfcr$u#Zdk??1#w?HHg|`3U-ukoCjA&-9HMG7WnHZSWmTF^rB??HEkt;^aRh8Ga zAX!WBn6w~CC-0c#|M{nC<$_s~pj4&9qqTL-%#YnL@|#C(iPI7P!*9*}o!8Z``S{hq zfBcbJmP*5IUwL}wOna|E%IN-KFHmW#(PS`7C-UccHne!6mf7FfdNl+5Cf-gx~hK1boN|jcsif%kG5y;s_F<=_3z z@18i)4#Oph(gdQy*QWd%*Or?XHb6_rXYs3;ZT)Dmy-(W6X{Kf4P zEr=zHN4PGA-1(h3fw}lVU7ti? zcoB6KyT{wNlR772L0tD35U%KSa!h4QRYnrKS&M<8VkBLI6z#?fLaoi6|5HbbNjDsL z_~7%tNTQX1=n=*gvmb3goi^RvytM|XE)4u8HfCKxMmgu9TKq6s%d2g)FjFQ}?PU;= z5e9gn;>hUQ#P^l9*J|;aMkgATWRL8^z}uUoAhB%KLnz^Zcxf16!+pCsM9d0_N z7}wdju2Vw(wx|}x1pnLj)!S_jnd7o+yk=U{eQTy}L6FZo;H=r$QN^7IW4sKGuLAPF z{;8TJHIONh;Mn-ZVL894r*-Ds%&pst&S}81lQxH#al?)>@lgEgGxg{wcEU_21Li1_ zb~MAJVmD3+pjq3oW+c%0|Kgr_1%=V^hVcu z>`>)D?pbokAmzFCHbWN+G4wS=`gzu>X%Nm)8)AO@9n-5<7X`EWebHcM#mlsM1s4#^ zcaDqeRL*wqzi_lklc z6R{Aib(Ni#~e=7rGTA}08MaXAone^oQ|`uENY~Ige%W(_aPDE zJyn1F;LMw^E>oCx0iA~=m#=(2M?pjvgu!5v(KxcpmpNW`yJq>!g9nQ#D}CM0dT0!_ z7|}kgP(9H8CN!GLm!B$rs$z;SXP0D4VkgbQm(CtMS<4&(tNv*@&^z{2R*}zvYZs3G zzoVs|bnNCmCYCGv&&}lcY4C^I-?vWE3`uqwuhe{eOpmqvp1b$A-%!}9YSGM3-dHa{ z*=0wxVUm}yY7{1GvAm;w4kq?RrB(pi{>cYMioZGjcAHvW1R1gFrN?Ien|BxCpZJ;O zf=UTH`Wx$MVQ%me7*i{kmSwwTsx38A`OtlpM%UqMcU(Ama?Y~!i@VqDsGkq8$%|YZ zL8jtt{Q&noTDk`Y99qI7ugTnTq`e5h=Uu0z!_x!Q6E+L!3Nl)RPiEViT0~2iQZJd@ z?6tcfyAcCDLqOy>Ss<_-IKTd}M;>_OYo9BLOBCl1J@?ET-}a;X&TmRX#IId~SL$ue z+`77+!>diBASFZ+QuU7Ab^eFrbY%*C_~htbH2jY2x|GtfcKbGgu1i=v6Bt?^($ho- z&a6!8f(bDp&^ety6zi*36ENnSJoCJmp(Dh$)%DsAs7fzI#+H5#Iv+N)Co6fga#oOZ zr_`3kUVv^Sb}|X(SodSvy>zi5dhG-qbPhsWI&y)4OQRpP<7aAm&14ks!vh*SN>w)2 zA`=(cGAJQ}*KV(;2u=(#PFIHy)JENu?f%?u4YuQ^k$iQ#V;|bt)z)PozT($*;!OQ~ z36bx$cRqqI!f`ej;w(TAhFZk{LR{tdVKOFqKHq%nH@6 zkr^NQThjQ{iRpy={s^5hiXyXmCKeKvQ-~t^J=d2hgQ`JKgg9XIi_>^lCBlae&OF;b zs7lOkJyL3il^A)xGX5)hLKqtjxq1c=yq9tR&D86$QX??{$+RM_5oG}u)v@@>QzI{5 zeR?EQmXFuZ0L8Gb{WZzk+b6xv%jVfcMJCg?82ScERhlE(|M<=N0jAd7_e{MQWxQh0 zuC~-z)V{H$pE0Ye+rp69gcnmbn*D0^_+4fkHlJ--%@18Y${z@u$PS#WZfDAFI=O7# zUomrs7N^b7A^5jmT`Su7>)HXT8Na0s^+Kxy+75&fVCTAW$^xf^tG%tUoZ_U50z|&8 z&Q$CB3d3%+1YC;kRJpSKj3A0!Cr&cxD8@1{l{Z^$i62xweYE~)PmYDETej8D@N;oh z+uP?v^JLNHM0Hwr?>PE73xzRg5L1O;7FwJ!g@XpWquO!I&{b0MtLHJ(>JGIKv0fV& z?4%YD*VMw$3{cZ;H1A+_)ml|GKz`8)pdW8(HD_A^O z1)ml3UuN^`E*uj^`tAfjiOUdDz_sI#h)+i{!6B0Y^#9M^p9lSQop*xY3j!cPfFwu) z1ou_kB#K%{)0S;nwk0*aO!v&VXDm%;QZE4kXQ&1AhG20obT`V@bdS2-y%RWh7k|qn^JFLOizL5TSdRP8zh?>1Eae-`qX> zIa{j=voDZmr^j?4oXIR&Xp4r3;b@1Z5ir$lD9ulC3nR}Ov{Pe zvJvPs4aG z0_jCK4lQu@#<~jJNSr)1Tlb`q*_Tbg2w6+g-~0Y-9a)@LY6l^{0Fn3I^bo1Vu^Z;^ z?k$>ySgRGuyqaN(%7G~cW`F9c@-9M)!h3hua;<7TE>c9YQY$4hE*hf980K=US3)JyT>0miIL(1W48gb{ z6{RZ@3v>IH4F}J<=;5#Yv*X9cYjtNNj=sGA{EK$o^;19d*#6l=SRw~iP$Rf-d=dzx zm}(j^bY~IZ1f?ltwOi2Jc~e9N;-U=(T^geK5pHZdyH0wd4Pu1Ws#`g*U^zs)j;=J> zDV8}9`FuWU0kL!Jcy*z@ODF&3S6_FO08$I|v^vwQzI|Wr@Yet%-R41DK+*smck$j# zFhVXgT6(gBsnoGmAojyasNqcgI{K{PcTH2YfC%76EH)v`fVHU?S1TeLK+*sQl$vw` zbVdmX-4=qn&m6Q%I0nI3@ztkh-i^R1Rg`^jNY#kpXVybt#yCkd^_#~l_Lxa=6vdPj zIyR>uVTh+k8(^(Y+1M_@RFr3zm`eAP${NZX1Y$YWOiRk5YfK(5#~Jp!5yE7XmXrld z^930mUp!J4*i67YmcV#=h+h^ULuk(E&OYJd@MFN!r$Eq`nlVlDs@>`qz<3d0HpF;8Qo$%qY)DcCRX)apH)HL1_Y(&BHi>_!$Sm zaKR}e2jdmw=tb$YQtq_a)fd$BVsxu1=9{EJI=~|@&OTrd34p=#Hdm2TBNB>-j#W{P z#2`rg$lq{Lb%T5i2nP}X3y`S7=LJ*IuJdZ?Sj{^hGzcKX8Le=-MwmHwu@K-}XyvpL zi*UpsmP3Qm&`GD+5UizY$-?&W-bQDEmi`uZQ?xZrXZ^D(7J`wDk*R&KJ`8e?rgTAo zZYsM_G_@bR9@U%(LN5qIIL^y+hgK2p``pyy53RpiOl;mb81s0d4&lXQw`g@{}q zq%DvcG+MJ|(-24_2P7YVsn&=%`5KpcMVPwfl(bhWkYG}GOhwii=>W1VMu3G5gPqVl zRaIJ&(!_J9Qv%j#xEbOrg!Qt-c@v+jHZxun-C<)t>z*{NA}>+$~t#vw)-M{Ih2v34_hpr*35*U7)2#LgFaZ zO#hMco7)W85+2L4KYFB^SM5G>S^XwJn)l$VwK4jw=c=Q!lg?lvOHL_BNyY^uV#uq1 zRa0xtN9w8!EkZyiFipobbw_EPTkHE&r({MCaWEb&M{PV!-e16B_O+AMIGAW0@v~16 zr)~(xK1)oc)4G8~$kNh=v8+ZU<15w(=*j=?traBsHm(R_+!^&Q06}=lX{^eZbrnw$F)@JFdwAdAE@A4?kqE;K zEsYUy$NtUTGkOX#FCUw25ptUF5-pA~7^cAysC9JZrUu zG1*!&s3!P6U-%U9Baq!I$i9H6BI(1*sO z&}r(lm7=LrNIPKii&^w|TI!aa^*JXHtuYx$auHL>BDC;LJL*A|dRS~omi_Ty6l+RB zs5{03N=n60kMq1#vmQDEpvqByXm3S=pI|;cc6Q3|*zqA_v`6#ux$)a6{)vQ5UM|{F z&utM-YiP6=4%c5nZDkBkQT+CKb1adTHVNW7SX*iZ5 zAGoBl$4=B)4lL)CR;9*4%)x1!;*Ku?B>;%cRbhyBy$6Qbyaf{PQ{qh#v1Q?PI06>x zv~8#E7F=)LJgZ=1)!3FftKxMp6k_?AtpH)DIu`U$-p zL$aCa1MAxS!3Qh?rmtaVm^Lz+lT4EdkA zDRq-NP)GMGlZlZ1{CQK@oQk1VMe}8f!@~E93(5`wzD(-1|1wr@F_@t|^HX$*mEw?3 z`x6tA)o0f3N~KEKvCv1ScEo6Xdb}(akGv?wkL94YB<3q5OuDs>95Kj}ByY*J=Wunr z#K4Gk$#oYMAq;Cljpne{q{6jDl4U*Qi>d;!*sazZZ%>ta1M4HF5w$5UO9JjIU?_d=kWOQtFuf#H*bv8{l@$Ofk?6- zb;HKBRBIr_llGuq4L*N70s5rRY>wU~1@`N{cmDDwYsI{{-YTjexf=~a9jI`Tn_EmW zf|Tyqw(-cyc@KW&Uz|8T-WfY1@yg5lZ``%zf}4L~|FIf##R%F_8T5lPv8i(*hPdS+dIvbcij9OZ+V5o%37S@f0VSbe=S6(nuou5|%n# zm352w#*Hm1F4gnSE+#F=197n&B1SIRRvW26CH7sy$?8-w=rGZQfTX}k4HUyvoOY4H zFyPMkV7yWa#vGF`3(lD-_Oe9v&Cv+H%EBvk|No)O>WL&VXWtFTpi6B0O^(OVzx7wu z?2y1{W#>9#dHlKLb;1#w?7h3oJE)OSmVJ?s5rAr{Z*+6538uQxeB-GcjaH2*htilr zOC5A6G7?1bqnDSL2t<(uy6QzTx+K%iB$dX6Y(o0#Q!CFM9?bDKPDO+x@YKQ4!G^Ec z2_F!O(0L}P9*G*40Gp{)dk1L;xd4*-RW;)fZkfak&m5d_>Nk;`i+QtSqAS$-_0qah z0;%%p;PLqm4kJ!n=xOk8ig;fJK09q{8Te_g2q1)QUdua)QJflIu(ObTdsx-T>04f_ z?rY&4iFqx&jomAFS3~+S!+RT8Yr^RKmM)vqAEDO552546MWPK>z2M)JxmKtxvtVIW zW)P;-_;~qP6-{MYrH9?jE{?9k0!)6v(+6LFOqU%~{{7F_i)mA2*~-)`jF)Q8V*y>+ zxp$`aj^8&~MkK9Z@(rwHJ6c$-!n0$R&9vtrAb`yqiy#BCecytUIkKmg7zvFDi8+YD zEDN$;!9lax&pB&~OyNm0yMx=gs6+cj#KLE8uf-za^UtZ*_Y@IGTS1<37l|bi{+;8O z+B%F5&yqc>Nf1)x;{wX#_#5V*Jvq}2X#LK2>UPo6#JbZWgVtlYHy(dmYZoD$=$BtB zJGFAMRvvazQ&r9)gDx?*G(UOOOgCcr+nvC!yRd%9NtXcYe!5zw(&14@vQAASSj+zU z2aArKSNkJqV=?s0H?4g6@glNe^pVRd07w{8*8cEc*TTK%@%<5Xylq!Cl;x}{Fl!wz zWx2eOj&KN#^=gfAt|5GO z9^PBu<)vG@W8)6TL1xqV`6M#6D>|4|w4QT~i53zqv;z^`am$r|_T?`fc>eKO{tU#a zQ;$FT$OSil^4C6gWw&9V7HJkKNR!PGAe;hq#ZtK92mO>tr&iqE}SW3t5eOz z!f6_uovD^0SstO~lZHRlZG<`H8&VF>HyEd-vA>puAiG!4PYNu{2d%Z1z_PCe4mw6| zxTu~j?V`3k4GcQVPL5==dj&ab4W&BsIh$r9$wKMWIO5R!qw+208sXyhOY2I(UkE1X z7@7!TBpi3+E~U`P&>{42qnu#}hY?ja1sJR-vXJ312O2_W zxFd-UlNYJT*=#Y^00&LoapxF;vFJvHcN|BuVlYP-_n_NfpTBTR4TJyLT@`A~f*6dM zX$n*Gh4Vio%wiZ3Mwn~%)4cfV&ToDY8Rv6%RcTc4Lu&zItT}258xxJ0QOW^>nKckp z5Sc!{@MvxF@{3PhwCeKVv1exz!DNjv7ZvuH@~ES$`eTl^j0sI1%y*CNAHK%8mvt06 zA)z(hM}~a}N0mAH?XOk6;)@JjHF{!yg~S?8rfJ>@J3tX(^#A9ZEC1V{t}%)i5!Rp3 zj+E0hCb4c|v^F=jY1;&rBfW?z0ndKYZp5ZF>Sz8&)rb*@*`yD=P;C`v4zyDoT`n|K zMLs{LHQkTDDy2K6;;IQ`LW_`M5X%7qUtsl7`60ta1k(8-HN{E)gtP#Vbl}Gb8AWwA z=f7JW85R_^a9luC9B}i_8UrI->14?bQ^Gi@cf|!$VMZWcVm3thV4{fd6fJ;QsMJCL zYoRT4eU}kC327a4X^8Azf$TKAH^nRC&7RXoS79x@sQ|pG0Gy7U_wXZu_cpK=Vc6Ps z_Uw;Qr;i`z^^k#eMz4D*t|d8DS#>yD)uy|Bqpx+zGTQjm#d2(GF_WfLJf^_R+*YL- zF7ZQTsz5IoSvCv(mN* z9R@m!NT_6b#1GR+u}lFL4jiksNCHqA;W(1DOw65s`~tes^dCM_i~d&U0?oCVQZr&& z7g|_wkw^$I*d84kp<(fH{M5>}@fSIvnxQRKW0^`eQ|ea&x0E`8oT=2yVVy)QOiR+M z^5M^;<8#jQh4pn8)dylgO(PHZ2^*X&iy`-E%wk3BOb60<^vWH6kIO6?qn>QneF+dR6Zl zy;pi*{vlg+2|rvZfE&6*iQlRBSH}ziBkSn2ikeTJMgvJ{m$e zp#%^KTU}VV_QLvb@7B#Lx9q537;*oTkSdI^4laxrDr3#+KVJ}IB=>m4q+~q`pv=N4*q@AvbU6w2vTD6sT8RhsjY=%=Gr?iZh)PZBeTzozS6zEdTkF zB@@>;F^G8PV2v0A&OUqd@eP~5`Q`uVpJqX+ztH_ zCS#0cAMx4oK#LoSm#Pif-Ru|;Wj3R{8?i<6e03bz-r(rQO19DW8 zqi#GD)yw2~^Nz}XW<1Ag#{38OmzE(YouR5QRsv>$%*AGZ@gRtqwqmOOxHUGRlFh?e zpwFK=Ra;13eX2^CjkWqD)5h}XWT{#;@A$fwsWu?_$s5KWEygOs*(WgCth}e$|Mc34Xz5WTK)9XK$YQUhCy3xAzl8T{R&6~<5>;!EzQqexAIROs0Z=T z@!K~iL0m&hpL@BSzHoTf!iv~oGi?QX4%da?o~JlN_wfnt*bFl+QVXMTo=gOnRjQn9P3?rC2UYAPw;wTjiGp@rA zy1uS@gsfxw6Ae=GMwR=YD~|~>Hq$JK(3r{6%I!TuZ237|9up_T4tWA#@o98QNU$4K*I(5SD5giveLkB7sDnEK@=v$ydgu&y9zq zGK7L*(5xMInO^gdl!K9n_SKSuI*A$^FIw5l_&9O;3=T-m$Ty8I)3ab~&uUqgm$EYk zcGgJNJT=Dr1dnF2H>hEg4p*}W3&zVY5)f&tTzK7g*Ts)=W; zF|soyLZu`HtWR2T&8je>B}s-&u1Ws-ZN+f}RO-|W7sRNiI&~6XtS9}yzpv=jE7P1q zHm#9Rb8-nd*O*pcvk7sWUmQh2n&OL()mju^>5C(USB}qq?itEX9h33I>f+^S%^BQff7rED)$#&$Xz7B7F4G<1?}E zkKg@Ybsi|u~<4Z3%=M69VA*N#aLI{2WfCYgmqP~DIrBQFhW}Ai9#pKrw z+LQZhXAL48fn_hU3!Fx`Mq|o3^yBwIjS=g?^2Kp)qi^*%=$MRKRz2+ zr?YznZ?!MJ)oUydh`x;;zn~I3YhkUZ){pjWL^VZ^hNlBF{Q?r()WTbhXC3(zEg0|C zSZNf|-F>zH;k2nZKSG#lhy zJWU<6qX4lGNia3lDjh@oW{Y7}ELd9=tmW%G8{hbYeN)$6$6QuZv{>^yPLL)$wYGrp z7X&Upzdmf7?wY--9F3-7UR>i7E{+Bz{Fwn9Lg4Us8;mb9(OQa3Gr zVApuCciy!^5X;cv_PF^8#;E(P=W2HYr(b6}o#h7KMnq_SI zRCA5Vf_(?RX5{XJyGl&Ul;ZeuR(vT!sCZ0qWSS;G7k$@w9E?YF2&tmb6sfCYd|~pN zX%x67Q=MllGw|~WAuov;ND`B3XX?Vpl7ot{ufA~hqsB2zuT>1mLd8FN6poP4jN~G%+Ra^qYy`L#XM;>6;}ji2XUj>!F=baI z=vs?sI3ylR+tI8~6Su9z6xL|y>;mHFv{`Rael}G1jv_aEtCTZFH{j(v?q&Xw4 zGPTqA@c6v%zcq45{g5*Ux&5!*2T0U!a?lI~~~K|;zN6B=Z#sPYb7waobYr|{6%k9`(M zbsVveAR%%t#KWB+wFbWSbjyd&LRy%ZHmUlKAUTcSr1d=AXf#Gd#ZI;1{D$F<%S-Iw z_>pWxm>`Dwrq~7b^i%1+RL|sdgb%eeA9q=wzpGZF1k{3M;fWUNMrHWIPrXtd(Uyz( z!&VjTh)wW_d=*$Wj-;4`IU=n33>O@*-`c@j3E%4aR>ReL3C77>flsh zIMOYf>j^btO7|YAr{M_nYmdyvf;9GX>z*n*_+l;ItFl9;vKuO`Tbl9Im=mX73|ivX zOUL7<^cC&8{K{|s`5zyCakU?lJ7e(5fjw?-|Knf#g-x3_i8*^TX#rWOlOB$&BSdqW zSQbbT^=2m3;5kPv2FqGg4xp#mU_qsc=%)1e$vQjBy31Jb7~q7TmVv49&}pT$)EBV8 zNw%471Tk%*1+Co8>SoaLQ<=_A)Aa4;#D0#cyS{>QT{IS|iRd=RG0(aLBJf!DO-2z! ztnDwT%U*xc>raH`8f;-`zraP8I;J5x5FK*{AJ!0Y6pvJLgb4(I@ZIxI#XH>pXD9cm z1C`Ypxu_9w2l`buN|@q^F0;&8Y;;q1csLN(;U}|XBMp@Hc8%fWqm(!@8MF( zlEC0>ING(XrdL_*00)lN?W+J%1z;pq5N95mD0=MsAQ)GjTQ4bf%yTxa{K3Pu>tq`J zci&%w{CQ{B!J3(fSrY+zU++VUkd-l=*Z%RmJ1;hpPoNg?7&8s~iM!5)ox#tVxPFo+YOke%$f<|NDOZ0tt@u zD`rup3Q~4}JGzX-z7~*X%>e9FcD{qjjzvrSCK7zQuCfmM(jt7LFjleV>4+utD_|eJF^0wWU|Dmylt zzWsddh;euPM1}FS$v10Z?~&R+x^aZZBfqmsjgMR9EY$C9?=F6|^H_XmiD`utFLdjM zWNb?@5_JR@S%{Oj)VpSTeTE$0AsZP2u_m^M_O0w6zmt9FSZykSQfu_BXV;GwSyn!7 z4La_-)FI1I!eS7Zd(@MsX2xT47M@dZMLqe?okkEFzAG-J*5$WdT1;*7-+$}%Kc@qm zOnv!@$~ti*bC01qXm?^_Wa`5~;8l~XT1^U#rYaE=v`^^t{>gBajp`=dlUsqR<+_Ev2SwNlZr! zrYA=-y~Q!bG#i@yg3JQqB3K|66c@5n#cgNTUj>2%u?BvtAW}gyi+&O2G<~bM?7X_t z8*Ky;I^L0@;j9Y5|c?N?s8=WYAF$5#xqd6IT7c#9W|@ za1hLS}XPc7G%)LyC6(E*zmAd7@dK&*Y|h5A}(_$-vK`J2~DQ;}PCmH^=7j5}-i zYQ&DLHk49{<&F(U5jww$kWf`zU}tZt<%F@q`iEIvT>y-*AWLP}ws98pE(@OVn3#IR zT77_jEVQbL=9fTd@}Q=rsI!f)fXU8kH1E=}gUF995{MCAhy}kr9vc^Mn5kMg6bU0r zar}-78D}cH!-^WV6mf{Bpt8pOynr5k}id9-dcmJ!L+G}$2PYe6-Il;(}t zvOG?E4_anHa*A#cn2N2V(wN3@I`Rwn(&Mw=^8TS=8f12_R&DkL&Z;u+;oZQG^GDo& zboaIxjK@9;P%Ec-Rw;Fp+08ra?`A>FHuML}Yiki+V(PX_SDxBG`%E}k z_K_#>#Qs`^TSCRY`&iMsUaXex8V{5a6SiiF>#WFClZpm`JH`+Er9mjV4V%4oH&&1$ z|D_x2)*U~)S9vk;oJ}j2pI2{{{`_^tq)URAH8B-~iC@a>LwoBZR^-dVq`wg( zqvDp%vV^lVzjS;qTC2ME&?m`=Ia*4~^;j_r6~PFbn0?QtdFfjFD#dpsdIU)aI|s&1 zNZA~gKXt>(qx&lc0T9bKkN@s3m15+9Wkq$a=YK;MwxX1ZXV-bf;fl>2^KbVG563}1 z_k{b#7d^MKX~ zKmVWooxgeZIp?ZNcD+HR)S~MJh}5)^QcQ~ZV@a}U$&@naCOMz=k!`f85ITQaH9)k$ z=8bh7br#S(Ha!&%I@?4(+XPc#SN($Gs&EMht?M4P75izORS~h}G??yQb!qxf=u|WE z5eUE2E?vfnai`rUQ41u_mX9yloUF#Vn`_}dbv-RzhRcd6=#vs)c28(d=%2Z!2CL9v zcCB;3=kKatm938r4TPC_3Y|+bOp546XAwebrwMXe6pvCs5Xeix;Y6S zV-!UzGBK4{-dQ7dw07n0x@zWHbp*q*QpU6IEQ@K%esw(Med<*0;<5H`-&*6jQni!* z#wW_MX)b^8xb>1U@{2drUyNGJ+*dMJ%$Q~|S;K5b?=>w&T@#Q}lU z>^3G5>X8IaDF&%Kvo23`vUOhOFZxjgEt5vbTtMcRZmLxrgMau)Jy|uI?EnD(^hrcP zQ~{^5y!_@pHDf0DOy7^}o9(uUP|;P}>tpa1Ewz<1{#29j&?-(K(~YB;^GzQ#LXh00 z3Ryhhn0&9gpq}~auv_Mv&y`=2vgssy$A<{sKN=k2?EJ#@D_?xHv@$vJqDTd+vh%$a zW)bI~U5n3u_T5=i=z9=JDe#3Li4!dxbQvEL@uu-bRK$t#K&w)zu&Jm>LbX2817)f% z!zP)B_RR+NdmgWyue3~b1tVoMG0;*}jW+Kd2}#!ZjY@O_*?r@JMPQmE`Y8v@>|Q~P zC#9YvS)dCsi|IIYZ1&yNh&jlFa6~2Z5tV7E>ma3_Cg3Q#Ok#f-YcaFnTt>_)nkt5# zxKlx`j_;o z=-n%b9e0Lr>GODNVH#LI0h8y{7Dl3V;>VvBEy#xm-C|9wifQTkejCIL(9ok{cCR2y zlOo1k5=mmpW&40877&##%>;Jocymxu%sQ+U+C`^RB#VFb_BUkTziURA!XXoLYSANm zoVG#2Lc#&^Q3L`JlWrUfi{|%{z#NS{DUFkjt(M^;tr+<@#mPd-dV=3%Q!1-rwY-&I zE#oe#=U;BwSsPMW%@804KY3NHy>N(G+rfRi>yO4v)dzs*I~qH-)|QIZ40W8Oe5K-D z>x1K`8}T*QJx`PoVGxy#FjI8#aikx=cIBUbxBB^^@%vNv&R^tWycU!%*0PlfVRi(0 z$@q8{3sf+1#Qf1K>L=%7ABQqU*0RRMto9jyKx4!LPJP|~WQU77VGTtdNTyzxO#S?I zvkHRShVSmF74R#^%QEk7kSuK}T)d_JMweevsFt+893R&xr_7B`!Vwj&KYDo)ER^C1 z^4UYxwiNkI7+-z5ZXw)$R3nVwiB$#X??~=FGS1F$qzLi+%e57oHG2Kw_r_A^XzSVa zmY;Ka<@Qnl)j%r0XQUo{8*OWyook1@GssBVFx5v8gCa(2kVy>j_<>!s8++=q?z$DJ zRE!)rw({)wC`|Ph_g$kMw11to`TeuHeD1EgCs9b{8WJW4%3-|m-jvU z(w6say8I&>Hf&G}EhAOC+-)M7dHiJE;s)VB(k7%DA-Z`~(5-3sX`hI-S(2a)L$p}} zk;(2=45lb)5SgTn&NlP%`FG7>Dv4$`@@*eNPdPzno6tWO#9}Zu!(UF9(WQ>5ZX?VZ zDo1~wo}G2jrM=6RXk%gODAx4(eA+3%%zHO$`3fKU^vN2o&6PO0-W#z%=KkkicS`Kq z3u~xEq%^6EI@_t?1p1Gm+kns)J504Q;6%RD=5v7-2IBKB((%IkR9-1`B$0=x=%v}h zR?6VN_}+{!nCmaB;j1f%X%;Zl7;ucG9N{Z7jjk^3o!e@u@PGPLZ7*7X zQp}*uyQ8xRW9COLuK;M>|NQJH`Bch5#$51Q_Et4VMWnRI(5gpKCex+Z z6!8VrkB?tQCeNv>rQMz*iNVmod<>5WZGbS!Z7g(c5I zqKfOXFkg!%O9hRP#b-wv0f@ZVA}p8)OS2mu*W?5Irhx#Ehtw;^TgEk;yRg`Y*w@av zWI$1NuY#yMp)SUXZPd%*idbvsCGMoeq8=0a zgtLqYcMF^jre+~+xvKHEjkThE4?li*Zv*RH!8?Bayw%;Sg=@I8Q|6W91IAN+mrlhN zz*I)tDHash@;*h|K^%VV^_S1r!qi9ykSQv=dp!>YAzEzxK{m#dwgteU*qnt36NbC! zeB37xMlCopzYh5XCToT;(Z*U=Aw}q<0itP0Y0wq|HnVwWX3Y%L?&6{yH9wC#Ir8qI z(g-NB)KC$(D8Kjp@!j;X2vY@?v!F>9sFaJb)+}pdd1t+Q=an^KKXqW`BbUv*M_B3m zcGLncf#s2<5*S-riN|s6MfHG&7^*JwPoxJ4@{5nvvh_FbslSM-jsu@O!rJJS@nD6e zTRaB4&MR8j(ppQ6zlFvu$^6MTby1XxR4`+06c?|ZthW^Fp{lV_#W=|ew$$VO#P%Gi zmIO4*)~3=wdtz2E)_BS71YM=>WD+oWrf|^T+gsawy^3XDRCITbAM&FpwX76pwv!lF zqpXYpr%K1p8b?x`&)qd!%gW{hKv_T|+-~6rL)JlH_sr1yCJGR^5|6=Pqs_oQ-GA_VmLE;jE>h=vG&mKyyU~(!7o+hG_2)X3)YAvB)2pS9X!n!5vH(}tR!zR<3z6vbdh&XhVJr6B7R%XSPw=vd2z z*-pdH1rAb&Qj=rQrqXo@=Ky>}>KHmlER4Q3%jPd1CQuWHsSs@}%6nA?Hz|&e^!`ig z?-R1|2ndp{6c#eDaQD>yl~0XNB2pv&0 z5Ka~$j?j_(^)XQ2ePjKV>a(h6N7`Pdw17F`NaO$XPlbx$FCF8 zB7k%DS@l<(p!0A@~UhD}5hm0)yy^|=_ z_?K)etymL}1Chfy5@eP@Q9*1ROPX4Pjq4@B#CzPS8i8E19a4*hNru622!wzZ4xdc; zK}ur*&fQ$W2N^?JIH3x!4Tb|!iq<4SN&_<0A1tIHr=C!VxXj9;cA)@RI6U znd+!R&xk<_18Z$?$`5cd?nkVJP4Jm18*L;wXM8>9f^%jcT1t^HqD462bvAP|w8y5c zZ>Uf0w?ScKkr9InGK@Dr3kWHJ?c+N{DI$h=`rz!z>Xhn?OKP5OVTfh;r{AqLoM5tc%mW%(8nWM$Jz6`+ z)pumaSJ*{@vtw)RVr1!X`S_>-%V?z*hzK#0Ps_(F4m*GT)XJ+T%MS3KAgckY_-c+;tGp&g@*Wb>-Lt^V^7G%>(u##@Js zf9CeOmAYnZVhRR_5fNssY2EqE_+xu*35`p@n)uW2s~dp285s9Y_znb!1gb{kNf3OY zrpk<~MeYL+9jo$^yD?v)3_+y!i<2ZP%_NrvT*;Isi!?-^I{K&^b!jlC-0veNF z{Tu=A26GatzC-fJSr;DIzwem`zH){?g!Rh)JvUr;&529zx_Ijb6;EqGD&kQ0&NeaK zr!ababZk{uA09~2jpGeF7j1sM^h6pT(+o?oZ0 zg@e$!%n3ux!u1!`Z`dE7clO-mN4>pAveR~#Y*dU7QPdmbrF9Vw2+_J=4${+FtBab5 zFD|5xWY0Uhh5|H|6lqWCmQAn!+M_JQuhIx(;v^gTR48FRU8q%=;^5I#` zFC1REef+(r?2PSrF&oMNah)-6zF;6gkt7b?Y+51Ax%G|35m=@nos+Inns>r*(U@Nn z;ZytT?3gJ?{=#)tWknf0%@lO2<;UzM9x6-`5ST0X_DgGL!~jbmp=K@+M$($y=1>0e zRptf~41*Lmj7i5K+6r$&y@0v(JYq`!<}Kru?Xi0K&}Fk<)7D{PM^o2YWwaRKxK6Y# z+wYI9RS*Cbi%Gup3Sb1DJWyI{uyu1;Q?FbvprNc8>MU1o#sp$?V=(bwvaObmfX3kw zk?N3)qlRp?%vgz~iJA|xA!esk`Vt-YllBlc&o18J-maJdJoHZ*Eg`w z)t8|&;gN(k3oIZKfOSlYl49O^NzLe4lwg{$-Hosl8K6Y~Ohu-^AlAyhCF%I)SC0F3 z*Vk>W@Urqadt)sEvML1&;yl|0S+Q|czDC{4+F0e_|NFOo1?Knz~ond$@Mi1S4Qk@lvbLvEceH*EQQ7#RU*&zvBrbVEJx<$jNSUTE=>>gR*L6~nlC5=Y-9;uB6Sq#_}MPZu4gkknK*F+_$P6$voak=COxZMfvJtG@r$-#>b2NS~=V zb@IgX&pvhcul_qvAG@I1aGVa61$9XslVXO5KubQfubN2q$_rL@&HqZTS<`W1k z6jH=;2+XE~R&RlY((1bNHeOXGz?t%|x}dJd!0mxy!M)`L=PY^;*r)4GFV%<0OeL4K z0=+KbEk~u@rXaA;ybJUceNvQ81y&WtOud%dd1}8)-3N9}VJh>BH_RWKo$uqM$V}I} zSLBt3aLLq2J0Cj2q@ssN+bn1eBm7sNnhB)Mh0$@MGa5kGJut@NL<s zMbs-!7gc3XoLu?Vc(*Z%zj||xKd}=RgcpVaVh&kAxa#rMIv#N#*<2kFMi_N*(mt|A zEkjCB+2KwafsFqw8@RNtMur-1=Pnc({RpQoVQO$HqGbe4_}4yAn-_mLUU_@`K42GD5WVlY;YKvJ;7c8lhg6A_PADzACzTXT1lih(OJl8;TGQ0ls1xG8;-c zHGNA(o0f5w&AySOh^cmf1I9qhr+@p_k~HivE*nV}(vl=|MNm%DRdwb)4K3^hALG0$ z%0uXA3%pFGH;r?#bBx3I5w3H$r7S1QEDrA?A?C8B@(=H?ZEJJ%U00S&)N=;oi%gyn z=jb!zr+W~x4y8;*EU~#YdwJ}WCI%puZxl2qT7tm=Be3C@N7l<0Xf{{1HsvZ|;SU(#q zbAC8j4(6{bl7B;R=DtF>YQJ2Di)6aPeLA~W@TR+0jnfR{9YGI*?-ZUr_$CdqR@K(2 z@|!luIs$9)sbrE^6*0J=8MdOEVb@xQgbJE?B75=a43TQ$#Gn(almIMbi`%MGO$hhQ@TT zoZl_FbH4b9KL_^fvPrX?#UzQS2*)y1oPrPEKcTY!ncItRjb7{2(N<() zKUEMw7Ti{@($rBH2*T=ODM<#MRQz;jAI5sImgco?9{XIMyrC4e07)?*1aQladU-SP z2=zDbS=l=lL(bRFUQ@R;|JE(zJdS-j@>Qd;zmwY`^-WRYOy_h|KO;hZJ~ggZ$~d38@yJAx1d5QmP< z2(6(7n(xpi?m{Dc>v)$6T$jp zaH3*d%9a7RC2RF*01H^PD9lw=rt+_ysJnMp=$7$0;Q*oI?b=q)1Q|&h_;hoE)&ha9 zqyEfJ%j1V`M^kGto+1Rq4%%5Grp?pmTYZXVA`A=CK1b~(y6pu-nB6M~Gih8arQ%qQ z9MEkl79vyQ&$#__)b%_30?80s`&-Y|12RF38zo|@&C-=Bit(A)yj1r6m#lnicfGl$ zePqFiq4eab>f2=?KbN7S-f~Itl@d1iutPXi z3v?1ifX_Ahck<5N_?jW`?gHe_Ayfh{9x3~b7`^?+te*dS-(UIgWi{A7_(BcEJjT&& zyH>8uf!R(0)1k$3eq)wjDMcxy)~tPFEa%|($@+^O#ang`*DuX>i@+2}X4Wj@&!0G2 zhS-C9YVNr9!g2)eVyq7}@?geqm5HVK{(FXw0rPBFz{{M*0I9AWMu4;^pxP zEm?#A*6}0JdBX$RatPRDEtjr4yoi6+Nk)(m$<#5NV9JS1OtoV37l0GyT4HNyt%Fuc zn(8-{!ul3g#c4WS6^iQGocGu%*3Z7%c+;@o!w(Z@?grLrc7^Hg)w_Yy?~&{G?9)Lm z*Ty;qPt#X^2J5W`mS5b1R-9Te)2tuWa%uSMgs~PsNa<7^o%v>}*j{?@?b7fwH{=MW z7E+3nPlKhXr;`=~b5EwyFpaaBzi})AAif-lB&Pc0CL7%lBq1uYX4%+DY2>9=FfoEQ z!GC}68^-r6TtpH?<-BuNzBWFsn!i59QHnr#=p6Bq(u83mw^V&>|1205R9tte#nb#R zb$u4f;P?wC+Co7o!eKR5l4F78h$1G*2n!g;Jww_s9FA(KF`jR_alV`arA=#%cqH?+ z@d0nkyB0{*7;(Gu=^I~fH%(#f8<{^IuWhoB#e^`VRkWf0?Eh0&)ssykchn+v?0|gk zt_m;exm6IP+k3Yt)g`7fEfa%4jzUx&&H7k>uj%udlzC#ybmDk)yRdW&A(?nObpH zkrOn`T2B2JKUdEq{NZ>PGQSk5%3_nHB4vW9$mb9HP5}#-o>v3N*_&!{sX=mYKqc+Q zOX-gDt}b!vQ1LH5Ru34?#76IOCz+2oGyb1s;l6u*|H#2J`b$`r_K*MThi>?ZU)g%$uAD@NDyXljA=Jn<+Z7Bk zMRxQcMQKTh9qG~Im8PNgj*nqO@9OEj4VU%;63G|TYx zTsq}6mN|f_VPkCy7P@L^_kQrocut|A(a0Qrt*)8{tP@6n-$gaZOnfagDJ&R~jpeJi z*UnnxWpf=NYDgI}t9FU-&p*3{%9e@WWWx#5;81@f*}8J_!oH!NnIF$^6DA>Ow}ahRHrJilXIckks+Wq z*W`ilY?K?n#Ikc+ak$2j0?CZbENtCe>Bh5GKDeux?6l-Vdy5l~v0oUWpa~!~i!mIX zlO^o>b=?tICNBct-Bb1v_=Nf0eW+6E=buxi@J)+`+@mK(9go)Tm9ti~#?Q1n`e8~P ztjTl1ATO$EYBP`5uM^BCVo@=Ijs+{gYc4E~DSKvlOkqO(%9FM2#f}(a_NjvvgHp6v zX2-UgD@g)fHrZhlC*27cREuh;v3@|0rYd<8R(e=CNRK!`e4mvp!C>@+7(KbDdKR7b{50{nC%M~i78nrnebh(sjSkB zU_eXuius2%(b=m?NMXh}rc1+F5H>rqdB78)G{PrNR(PMhp|U}>%%tlEQ~*;Wr%CC? zS3e}x@x%4=zu;Q>UU5OmXZK2JS_45VijRyD06+Z=rhol{#19E9RSTTrNJ29cSOY)q zrdY583j(R&8+Uuh@b2NgkGQ{Mc*pznItEYEm*EvoQ@Ah7>|Q}DPDjHgmM`OlEM!s%e`*A% zFL2h!R1!@+qiTmSP1|@%r6#b$Sbwx$P`F`F4{sZMT|%@-G1q@m7lwA#!m{7 zkrm08pLoNJo7Mp8@4saBr(>K8w$%F13V@gxFt=~1y9-e2(7fkvT6tl7MJNUn*rn&y z7b_Arq-t6w8wv?Bodn&H#06Lq^_l=8Pr#vwi>%Tsl>J+d#2S{$tpby*sDw|MzYyn@Zu_@f-F`kr!~9s5yw-f5Hv<9Pjvo=y z&pBqtq|VMT6~e)nBR62Lot*uAmB_`$ucg2EbGBQH+ z@`aOb&Cg)GcJ$>98#Y{b$KCsn&E^Bar|BGNIw`tN@o1BPOwL_O!yyC7CJm;FkjpvW zPod>5V);om{G5W&?V^eH+nko~q6IAL2F-UKKYrco(T3lW?R_o#t3rPxKULXvUOiLL zShgQ5G}^Q@^i+6?sia|fx=V42o(vdL`T*Abn82gAZ63i53 z!!)dgBh8a~GYcc^3?_+9>W;mhJsEY$j01OG@%qcBgcVWDC;S+}$JEH~+g(TE$4o5@ zO=V%sFN|w;AX9eckHji+y#A=*cnnIKnUqRKa@K(BT$@HST1xR_HUemtGhgGB{oy4> z5YHW2xq6J@1dfj%PmCSNep!lYULK2(jPPzy6EIX;snOB;q`Fi#!j7AE)cCA4pVh0m zW<1v{5;h5|+QzeLa!g=}5bzUWm}xQim78X#9tZz(`P}5V5C~jGGGz`W z77Glfnu>}!=RY#4FMjH>QYTT6kj62KU3o$I{r=vSgF^-Xbt5CN2%i|UP-(qX8xZE` z%^OP~L9&pU6a;LJyK%>?heMiL$|DQJOwbus&^TsE-VxOTNVLJ$r&wLqmX(q+0%lM02*A!x0 zw%!*TAf}aKAu{IdEt_6R;49BZdOfuR-(dCY7t`{48mYjHtRM=)P-@?_L?%gUM#;j zzrV6@&ky&!c(m?;t$;COP_-mMhfS7<%UM0XSx)&xYtV7%b_PZo$VD4uvv zJS9JW6OjF956!gPb>BEvZ%*r%9xEemPEs%Odk$AOD9iK5mn@?NqY4^?P3!)8ze5Aw zSWWi_4_8#gv{f2JwyHDkrU0E_9O_kfU-PHH*d+GC;kq~A@$RvARwsA9&%!2-`UV78 zyKnr25cSWF=E5QNt?_xOYLjvLl!BCfr4$wY#wV&jXymLc_m1BbyI1oLk8a6Mj8A=w zs_K5_>1W1cL@Iv)8Sw5G4|=ec>KpQ$*s_vvds^2rb1bVVZXoj7zK8bRHmPU09b zrlsN2Qk0VwQy`nRnj%>PMbtk%wt%N9=-l=P;fGKvC6H`mfsR;CeGs#!HR%)61?EC^>hgc1Wn-*6%vIGPmQy;VgQ?%;(%J6D1QBqw@YF&GY1&HB zl&Nt-Lu+*!6;OnR^di~r1L(XxnvS$!KcMTFw)WI4zAhRg&|2q=cVIgUu4}?=Dt7d= z1cRSz+<8aC&hbIX*hgSGs0cM$B%>>mJRbFt%j;)4shA>VbB!oX7`qS6#^?BzDl%%t z9aF&Crzal;Gec!q6M#D$r;l+%Twbw!cwg;bF!haRX0QKeAECWOF$3@Wc9ad$(f;f7 z)=TQa%sQLO#)lom%o<^?9o3j4+H4JIx|%V^G*re%lWse^9=Jwi+N!I5Y`WFy8)J3V zeSRUBIDY3lGXYR~&Zhbkj{k%M~ue#j5yK53WwNl%o+zCnW*N^Ix z*M<-7DS3`GECJQ$rNSE*sn{+ZUlvQVFpEQs`p2&=f%Jc-@TEDA|NDPck_d-W%qhv7 z(}SaJ@spCr7arpxCot2RU)lmgOJg}&5o!c&1a#Z++}yd|4|jd*YO z>Mh~D4Xhs$j8k>jx*AUN2Ov%#YhBX#8S>r|+FH^DBWsNd(-;=wK|7`c^!mtryez*E z*c3Qa@yG5Y@yPddeho3T)7DzX!chQb9f~pKK!AD3 znn_owk9<$Uh)jK&2XFbogs8tSg9FOcvBs z>rwyWVJsk=lDoFm z=v`Hq_gIJO6NqlUlyup7^^N5C`qDGoj-J@~^f&+L`0??(S!XJaA3c2W*0XN;iBCO# zaI^MwW1Lf?cCx9mBOI-9`=!;N8##R%oTLU!%SRp%-DnGFW>mDc1K~)MvK&qWC+8?) zt0M8FbSkrs3W#9=EgYxG#`qM?S;zR)6gt_a9VbpMTApS@Om)xRID2mwS`4Q20v7Cp zY?%7C(R-n(b2_@>Q$%Y)9%MVnsem+nz|~WpvBv>z z)4*kwC7mj|JkvJ-UyY1ifXL%Q5o6&4m((^rA|nG~aLH#68VCK!8|p=7(WcMi8<#R@ z5U6AP+2Ff-%IW9ts!>I!O)ruXFbc6>WBz=<%#q4U(d3QQ?vo)Mo zmI{nOo|PsB;!%q{yBaNgOtp?RPHPsP+gDU($AelKW0?XKGy%nvQJ8 z);bkAnUvzBeGpTFO{W*NHOQ}30%i_;!)f1zwQk$g&c}|I>(nTSBAF0+h+Y9tAFSWW zfvFw37C9slQiyKS*G|sXX|QqZ^ohj~)D{{swJH(@q(MNMkfw;!(?`b%Q{B_Fvmle~ zl$oco)-jc$w;DFIe8#tR@Rp3fjY!_Z4gD&Q7i6?r zH^hS0LUyg|yqN?t7EdLcxr`I1W^0I^Ij$JbLN9w+=1g6=x<*Y%b3g*l zij%3#yNJ}cB^`(6zyHDxbc5hD5rGZ~n9ZZS@$TRv^KP<0UencrY5bKKR1I^+pRYM& zfkE^W;}-_up?$T`$Fx3+@sb}Wf-b%|@vY%vv-X3=UKJTc9(b{OQ%Ysmc}1i(AaVK^ zVPVLgJw8*#yVO$BrT5&+#eaNky+tc~B2);(KCp={J7YJ8PK7l7kmzm%ee?J}x;0!D zqhd}YQ_+UC+;^WazK3c}Or9cW3N#ri%#vY~ucW-VUz&B~@r_taokZ9OF>6JV+s>}) zP$%%4_mnk}<2bb5=CkT&`*G3^FOG^Q_&5T(qLn>(tOU@9U#myfSyqvX{NmbVbgQ6( znu`_{@>*Mw6?`qDo#JZoA@It|FWpqPSPA2^Z^&CbJkH1v6rhhUF zij$Gtb#SDG`4T@1iCkJSj~6_1#&fW#NIPJ1@$`YZ5yEF*_M5xw95KsOBvT7_Us-n& zbCH535+uLE$j#oUS;>%hEz5==7ULirLMdIw`~3@H#1z*QfBJ*he&s8FcJPHKs_YEL z3(r3JYk%XX_H4dBwUBDGG>ghM)D(L6O52i*3VD**BSGrLUxKNe_3XM!%kOffhM_*Dfd+Fd>T5BV=9wu?3g`Mv=DZ( z&HVg$b1GOCgQWVlK_DYl7p2=brjDk%4FXdYS(vKeJawRMv)$dv>KFkn2uB(fVqIU; zhkljPFroWi0!vX2v}T`;e&Ypmx2*rsM~b6VghpvaZrvZ6e_s;QY0Are|A+B-sbTQa z^Ge=T0Emo%BohoGRVeS{X^#4X#C-Q02s_9qYA*JHx zs5qIZSVWmgff0_Z(=?I3!6Pv;!jQ6Hp|X8^w5u>q>a3HDd+dr~{y;Z7AmvVx>9siH z?_~6t(ojiFrrAhhCuj5bzE`Pi)nfGU1fMWSYEtJSw;p9={zR3cYQPag!pJPH`RSp0jIJajU4zvB3U2# z<;wT)j^MowtRE8Ax}M*W>tvn5Xf3qLR5!#zc70@kn;KaN#%v8ZQ|79|p)=jK{IVos zI;u`BG%>}(5PaQ5wR;7abThX(%m)??h=pkb+Qq^yB+G4F8afkvmxjqgrr2q;&?@m5 zfmV?UhEGJ8jS?Gw=|yBBOsQ;`QzO!#S0RBJh*%EH5U0jp=VVh6y3uO5rO7Vdcb>0h zUp3c7uYbNeM`{#x>me&Yux6@t;r#QN#BRT|YL~SXVOq7i8h?|dc=6Wy!5U)I)4`o3 z8V+GglM2#ud@RV!J$nzA?wwbz{FnFCk7`4C%nurlRhQuFwYB}F>we?a<3)h=I^Sdj zU^RCCbK@1Tv51uN$bz1X2J_r!#!iY-DhT{prG^Tdm`7eKBNR8AuEw%Eu;BiCUoT;S z;Zw=_RqDVeiTR=NneaTscXQUhrnoQhwtrOxoKxLoq$;uq+s@r z#UIL@u;sobvY4Y1b-vJms9n6VQ&xY#zoKf|cQ}!$%8pO4IpdHo&QiprRdpHOt<|1; zc~+@txld5)HVGZDFAUk7cOCC1$Nj^_6h!3|>kMS@|JJS5)Eog0A}J#uzqWLn3H31Gjv0q>etFb? zaQb#yJfD7FbzmU{xd(QTRQhMnZoTxvEf0U~4_|wA{%dDvBwjmu)Fecx9W6iet zDc$;s?1N*3ta|ud#K&>B1O#I#p*Lpv0Ux3gfu7Zpx*^XC^Rkdsw$Z|jJhE@LPI40>i%8X!#yAeb#ILkn zyCF#cDx%c1O)^Lo&O3YdZsxA@#*98bL7FG35DAI$M=!5XEm%*SoGs>6CmDWYodeaBj+wz_xwSwf=ii*lNch|dVO08gy&wrmz71*q0^n4|%1!P&SZ(WV0 zCnkdw(*k^RYAwhwE$y5;w-~JE8aq2u+5Gv`!6v^{L@uPVIgGQzh)~gxFf2gRCBCtH zwk8J5rmJNJg=rQ0j~}eGVjFE$4_l3Z-8iLEypu$Ora=GHRpk*a0G6a~Je)PL$^IvI zj@yZ70;<#VB>rd5j(WYuoGg2B$mu3ow4@sf9C<#bnbjRs7E=x_h+y)$8@aQ(%z z4L;U@afBl`3HVx`WFFkZFRihY$ihdjs0cOw{m<7gqmfU~zE6#dG4XFzxd3!r9Ol4;&YGhy!_hpuD#=Z z7jIBIl?_r0Ra3#s&YQjRGsVZQsTLpr`pmw$KpG^7dS{zXnpO>+qId}>O%2Ni1=$>E z0T*?yR@oW{<}>>mx?qR4iz*1hb4~J0v0ev~4^}Q_}^NWK}1f3ev;FZ=%(M5L)2H zHyWJMtAZVU7$Q0C3E_0KQt=}qfPl!fWEZhBJz_yA8e~JeqBBtEFc_*r2LQAP=OEA< zG?3GsY+C@?yfVhl^pUhgg=x@W2Tk(o?Xw4q3=W2}ifVj=Us23LQbjz1PQ_Hbx1=Mb z{&T(#j3-{9=7_N=!$ldNw+18T!M}RC9u7j&C7C}1?A%tLhd0ZEBB7H1+aD+k1~Fr= znVO98*>D$KU^amPu>=tBUwEvh?Ema1>TeG>@2r~c+*-R%)Pc&frB~FSZOjg0yW@%q%qXhs9y>AKf*iYDQcQMO;96@-V_&K|pc~Yr zHqLJvkbNWVUp!TR@6juSTJ1LGIIWZkh@`rIX8h6%zETHCDA(gpXrdB!bj@+rX=ui& zu#jl$Mc0G%yEkMSnQNFeQ>dU`^xzAl&yA+0CEx@=%ehF*Y0`i1wrX|)iHE!cs>QV1 zWCfzj)lXZDbt8`ioJ0c5YAysB2Au*EV(cp-10^sFf{KVC>lyM6Hl+j@aWI}NdFxw* z&hQJ@mtvWnFBRCV&EHq*q3Qe?Af^zYJ;f96;e%{wc49%wET`d2k9rb(JUT5@0)!(U zFj7?*KhjE4`!i0M_c%pZ89E(QIyOb|9t#vB5JX1GY=i*;XiP{^TkAGyBU4~<-62hZ zdBu3MA+g0NEZBKwyrmc>FI|xq+p>9O*I0_LiUoOT?_NzYHQ2-yQws~o?$x`8RpWsU z39;O~!L(lM<21$A!Rh-7-m3h2SPN(F29~>y{yDrKCVr5P^{#fxwL1MW^BoCe)uCY> z%l$yK6OL+s`i}MC95AbmDZNiSPoh@ zynL)^iyoFuz~^o%E$+0WmaUjlDp`L^Qwy}z z>E>Fgx+ju^p(sXL{;N-~Ty?={;qgjcJ}JdDS`6V=l;f5)(K48&n*i8H$T}lgyn?#i zJ)TyhuJoSAYh#W_W-%S9LTW*Vd>uz?9ZV)pIl`n4gC>$(xYyBSz)M9kU>BzjStY^i$VudHS`Bp1Jq;j~$!b51n~9b@IgSN4|OO zM?ZaR%cbJ4HXNxcgiz&$-K#W3?mU4^-MhYV6Cp(qf=b#ah$K!UMGJm|skJFT!VV+M z*kCURte&75XB^_F)ghclXV;NgfYRR4ix`fpd-{Q}Wsq!}dUCQ9Ri}-#K-0@jl$LC$ zJ_}Rb_0j54X+>#{6iplX_{sW6P>TgqqUl(3gh*^sNHyDX#QHj_VQ?^`l2V8rX|DZ% zOe7-{f!4xL^Sbt*Ixv?R!v!%O5s5)pL5Gi%VLTkIBz5hye@IJXIyj|X6&aD>^PeE{M@hHCemt{92f?>8P_j{zRARvyh*`AtoCa zbQ7ee9&Sra%RoY6xq=)OCj;iw)bUPhfcaNe2}iw+yJ>jj%O=y>ZZA2Md;) zo-%{XG2}b2smy|%Aot;rz#0*jUO%5oVZmCwn6{Vs3scUraTAYuoy?b?nC)|+9aCTJ z0-bm9go>Fd&0J-6#EBwcx^K4%)V#f{abzM-NSW!|EHsB@inRXn=~@`UclXTR?1RX} zU_f4{tskf!%s(DmGnuf}_e+o0x0y6f7FAhwWr<@MM?M~74aWkG3$Un4Tg{?+seU&n z=Lkp9$??z%j#4R)kt{D`#USNKRujd0&FdG{`*GnkvsA`@nJ}bu*?)3>36PiKX7|c% z%)feM7J%BR>2H0l2)7xG5P;ZZM7=(9d#Mw{gtQN9v5;*kDK8g|uc+3lajZ&(LB)|h zl(a^i=0>QSk$t_F3Yk2$ho4)QZ0g}V#Kub*AHk&V$%alfMJVi?G6%l86g|2PvuK2S zAw|-=Z>$z@^W)roaMx^I4&)iE8?dpZ12<78Kh7^q_^B0?1e@+ZeeLY~GV zz;gD~&fZkQIQ2=9`9*i$*(DvltteVh^1x#RM(nO?y~ZX+Wj>+aN2 zHB1p=5i=byMOS4%eDhV`|L%i(pLk#v!I_Fzj~smE;NI)*xO>ytThtDuO$2GWv{hQK zvr>(qwVg_6-5`oO^As(x@HgJ*QehAf&526VAVrSey^7*=_bPU#m|B3&whLI0fi6XW zd(kNrO?R&}g(jL}Y8>6A+4BviP5=Ty{l425B57XO1iC+cO?~|>h~!ka@y4btFqLxz z3yRYSLxkgSPYbEkR4kZs(jRbgQFPjT2qBF@MPWhL z(W+_s>zE-bvOQ3PwAJIQQ^Uw>Cn`mQY3moRuRCg4N1jtgux4E1E?xjfZ&wr-8I&A? z;Wyr+0>miB3;Coqg3TMv0b~p*Od&E_MzPOv#K;)j@MPI%!RVLP%bvcX`*s&c7-SgC zkV_!UjLfo|&MFJx7~D)Ks(eg?jJY*K`pJ_u2Bxw(6giCf89$AIT^)ANA|Q`;h=(0a zEu${7i7*3on>AZioTG)!HEdP|Np7eY;bnL%Q))ES%Uxa`%wyW)?^0!dG*N;FV$1@u ze|UfW;Q<&=DF}@EC$F!k`0+ELr!^}0S5`i-t3o|`y!-+?jEgXDHQuiuj-RH z07Q(DY)CmHMzBxcP@@5+s3KX-HBO~a@k6slx9U6MlqH5==W*=mP{$!}L^?SCOCI!D zaA>71n~K;zc6rR&JLW2}nE<5e9s%QJZN5*(EL~<@8gwC+r4B_;k5?24h&CJ+5EP}* z@q`~B@PX@CgEqT$>8DpWwlO2IfBr6O<@Z1DutPzmN2>q%^FjkZG@hkpt)q2 zMZfWG0%C_eFa5k#vQx_l2j=Nx>L{jG-MtDHY7s01ut2PZRZRt}8az#$j?Go^gE;+B zL~fJcZMD}?VlR^7dV)9eAK(^poNnKIWgg5RYfax^eAST_~~^0`OI2)jJa z#_XywEI2yrtTo?5j^wh1jycHa4^QkLkH?P*YFN7pBn&GCMF_yT;)2?v>8Twjju7VWnvdtN zzumlZR!S`Dy!3>9^@Vi{gpVd>1sU2x4Z&142OSV1(Y`o;I}fCU^TRF(2bq5~f0n8? zea4bHMN#2BQ89%H2`Nl%QpSgiCTguVR@-G{{85zT7apyZG(KkifqJ~_r<65W26MLn z6dxY%Zo~+##o{Nft=oXuPpBWdtQP33J@CTH_xF{Tnf|b_W%J6l+smvDe9@NrsO|L^ zmQxm}lkD>oro5ybP#gP?Y_nM^^ssFI8R8_`oAcXNpaPgD_@*S*|Z$dHmq!tFFA_fj|4b z*N(3HaLyTk1J6CW?c&QXyXnpi8|u%#f>+tDk*vO#ng;3^gqa~RrG(Skjx()IkOzkXa!TQ-%(MHx1jz=ZIqM9~Y{_pei z{Dg$*$YA2YOpuW|cj@5m*G4R1X z^@gA+CK2Y^`TSjDsv4sx4*S+TcaIr1TPkG8XWWaVNQ}LNMIT4fY&D6v)3qXvB8E9o>-Z~b##PIp5&s~il1=Ed9WoA}qA0oyhSvDf>ObSbDDnKg@r#G$ zbX=k00cjNZVly0I89|t}FwwL=a`_tsC`JpI)kI{12mn4pHZ`&&PEkr5omIP(=12@t zdG=5}L5T=rIfPnBiKS}IM?^O%Bn6f=EVzO+EdGK3^)&mG6Gv(-)t!!w#aQf2#X=+E zSC7iZoTdKuH*QO{@D$6ODGt9@zxl9?sa9Nft=GXi0?S1@*gEnHb$J_AgEtlUF~bk1 zzGqIWzPEvOrepoSlB;@EnJII9BVGEYcHRa=m|>}P&5Ik~NB;VAahq zIW`o+l*UV|JK|_2JZ2ZPDb2!h+veG18C|d*`u^A&$cm@y2-Is0H(x}wo5UX4 zS33xxNEmKu8U*5+9Z;QGLr+#kwbU5xLx6E7MYC{(DJ5Vfk~1idwq-EEV@hKa+^x~gvNF1auO44`z2}MA zs7lqusYpuedwXi7j;?!^9d+tiu+S_=V;SuL0-$o$;9~)eqZUG9>hb;cFeBq4Id}8Q z7atop6=R`Bo=1J}Z{1Q~z^zc-z+5pZHCmPZxWl(BV+xUzf=`_tSsmc}dy8N*q5Qk= zuQT$;kIuwCcUPrq@tNC;U^bOXQ&-asv!5Cdb3uhMzLiT%$_wZobgjQ{zpR?E8ezER zjw!bp2r~GxDjqP!tfH?yQ|ftDftH@H;+&>}4R!zC-fDTc^UAs#q-%wZW>X|}e>fV1 zt{a$~HP+nnef#;!YJZ%>OqiAmI(WyIm8K|nafmcRcC65>wUJz0m=qaTO(_9!q7z9< zL0Y#Z8&)pfb@@|IKK1;=UoY+%jFTsh?|bUIAOD%3K7QU+vg-2e!5R+sQc`l>*NmCun<|GY{pQt}CMMWC3AR93;7#zZ3${ZmiLj?Ko70(`{dP zwCrRa3E$CEPwmakrz8Tze)FE!b=TGdiMg`yDI>q7cuf{X!jNd#q?=p}4&puS8wAkKR80jD(|UynofJs2>54deysQA~)umzxtvx&DenzGZ z#vPn@cJ;LC2@^y(goDlN=96RIj%+UwerMNuZSeKLUYzY-g(ID+H*j(+VCotcvTus@ zg%?WS)cX&PciiB6c(?K12G(ieG<)_tasj+0BR`0{wH!_PO`m>L3zDH1beFTu3BiaF ze^sn+KQqedfE{z5sU?#~fN3I+1vYa)I3}klGFwomg{XDI_zP7c?AwXoGM7L1a?NVcA;lNM%w#2@zFCf>v{VjNB-R#alI|0}_RQ=v)t8^Y@{2c= zq*c_pn`U7M*b!aXUa~VQ@)-Z!2gV)6*wRweNM)09+AT@Tq<-RL6{RAxI5*68ui~+m zsxQ^<)o1eYBg?Y!C*lL+YwJ|hLN-e)T2vAbn|!GeN-8$M!LQRdK2af4r)DhXxC3+p z-P>N@d~W4u^1JfGhXpy^9zsFYYr5D z;;IT>*A*K{DoRz5I|1WV73OaRmi$;OZa%C27>Jqman)=51e=@oc)=8Z{0dk@2~QRu z$dh^BjE1EIa!Qj`8dJ%B^{IO1EAn)&pMQ6Wz^C6=4H7TXL&xf2iU(d;`Ni?U z>~cM(vl^M_Es198^LJIzZ;c<5O%dYB{q@s$To4NfO?dk`^;kz>fPH*_*`#GG5?e+> z$dm_}+6dgcyXs!_(Usr(e*IAdld#F!U2$PdK!5X=mH+!+l#JL?abunHyJOHsVADq_ zjx!>q71~*-K~h5WK^O(1g_Le{iZ zbLi-7I2x0Y1RtB{Y^)C{q`o-hk%9>a{9nyDe#V!;&tJFl#Yd}xWTehoeZgw%TXDo7 zkYt~i%g&!|yY!-konFR}XTcd1Eh64o;4#YvDdA+P-#wnujxWHcMLq{3#5D4K&&|YC zIrD6Z>WyR5EfH2ED=UBSaE;Zvfoql+tqZoS{C~e$>y1y{Q0l~Ts16&f#VHOu2wi=) zTQuf$Op=UPv$@u+0mw5Or-1lZ50?yde*- z)o@ zIUTLFWTyPkZz+KHa3jpik~k6QxoR!M2qY>)YNq+9Wa>hj?}^Ep1u}P11gR0fE0uk& z7=##kVeA5g4k8Th^DCX$l(Ht9DrEjPhmmo(331Pvs(t+0nMwziFRJLWl((^?iQ*FW8ViDrxk-zPQkq;vuI+{SdN$T$0<krnQ`FneQ#0T?^E9;EY{U5$P^Ui`hgd2BM9ZBXc0@IOrfIJsU z^QV6N+8-R=EwFS{@mMSW@=aBj+yQD3rm`87Ql{b#$WWImBBTBP>#x_ngw{^qAh~sX z-ZfsbZHMN)RMHe%t48T9LK30dgL0jFjQQ=W;7fQk6kwK}ab+Q#7bz842Pjg_`=2Wd zI*;2_0tueLZP#ABb<4Tm`RX5?JTZCW>x{&K=bw1#kmVhaEj8TQ>5K6L+qP7m1aB)S}b%MiSefK9VDCCz>n~h z&^KI<&^D7!5%~r0PI@|FJ!3LgY$Hu9pp8&Z_inAE=WMEBHZm=|ptUN}+QcHc02UVF zPw?opLk_@q-kFNQ1&|MDF{t9S^cmAcqpii`CvT_$t-VVVK!`pa1kk+m``n>=1ni2j zg&LJym2mP@?Ou`R)b*GM31}H~?0}R&S{R+W%-oH+ES9-TU<5*wk#!UP)AJwPoE{kg z$b9~;8d88t#u@y~k~ql<6>atu{Ab6$2|h-EfLDn$H`-93Ms$hJekXdcGp_AV=t`?WnJ1gQg?T zj=(ZW8CvTUY^Z&o45v()zImai@yoK2aQ>qoKYv~6nwVs%qe-S-K%l0Nt<^DhZmm+f zbrFsC+@+7axN`gWP@~bnAdr|@V}!w@5~-`* zfAft>$*88LJM?PJgXWid<8O2oy6VLpQ$|9O19>rI;#2DU?ifp*E6%URFM)ZVPc>af zJLbP@nO7-U7|lF0#TOT0iu~E}AxIuKjq4>x781@B;pnFEiOI73-SHgXzHyC^2TKuk zaduRMK-F}lvJe?6X&)`4{o@NlD0+N6HGgm{1ODa1)d@2)i-)6{wa3**7&Elgg#&la z$D({FAdx#S`JFz9N@8eT*?EkhB37+h7%3Ntl|>$Oa2@U@D<*F~pTj%tCUv%M*(Xr;B#2A6$7n9Kb25&6^6 zT1#fif2-c#!x@4fr46iAd;hzEA6jqz0c&(Rx@*ZSxbEy}hP1xSs-+7|)9N}#%)18b zser&%oh>36=7Pb9wN@j{Rcjh+5m36$JOR@p){Nlc#BxWK0L%g%iAk4mX7{WB;IT;s zp@U!WBAYZIlRxB!CZAGA2N0w#ju7F9!8#IQfrV*>Bv6XlL0|}JmOemW&*9pbhy|9j zM(lX3zaz#h#RSt68$0R?ihvk9sd8?^(k8_64JpP+X2o{B+ljG~5R#NINozGma1tI< zB&}9)@U1fQyVsnm<3G5+zM4dr9X4_BBN-!BV2apO8k-Czt?bxE5)T<(X1#9wHCP?* zyrQ12w>-tHHW^P;5ypw}H0<3s*7J1#=Rd9GDLWJy7m`TI=~#0Mj67!TWQ{j0QWPWB z@wD!HwpMs7Bgjy{?UFLZsZy4?rjpi!)FSxZ2kL$%_7hu5tzx0Wa1+IjiV)NIzzg|b zy}9IRT{zlJsTH1^9}VK>sv2aht}J?_X?aCudGr}gm%Ay1tb>f0xvSGU8uOFakGc<) z#ED=WVdcIQQHo6%H#}uHSkYptC zn3tVj+t&E9v48f)8bD;3dhX?V8cq)}stdrFlX1on?XKSasYRS@(Ru<%?Ho9d@2@zu5`$F8qcbJ3 zcGs0;iP@2Io75a6QhTv;OqJ4%@9>5`r7 zUe!-1jwh7$9P5*Ry|4I}ZYxfT(r)4ex(w09jD0|V`}vyAglCq_1@ik3)h?|t>g>E! zud}^s@pH6(Ub!JlSYV9j)F2qqAegkaj31xDfs|@hL|VRDW9)Y6B-KyG?SB>7WwKlM_uE_87RU?m9O7LRNwl9GcRcE`R%!Y~MIBWi}70BY(bKIj!v}BvF zp%QcRrwB9rmdSxm3$$YR`NjyaEZT$({q2#KCqQu1#(EyGk@zF93|%N*a;ysWx1L>p z_MQr0u4-9?ILk$gVsC7mg>10QnW9Tq6In9eI!_$mjQb~3iN$9EAvQ`h!tHyx2W%KN}T~`&DVx5s1dgyNuW;0q2H>DO9 z(2`SY@lH5%TQF5er>L3ki_D#|7J&tIh}jU)YEJVJ;b%3_G7$*kMMGLc;Ab*xUo9V% zb^+7)!%UD#EoZZ`kL#E{dc5AGh|E+yDg;8rns_KG&Eh6n0UH0t9Ywcm`s5Mu7_&-8 zADLMI{9fOH6^Zo?`9vah5O&mZXG6UyK;BYGYKbYeOkp!dh=FzF)fd*+RjV{hv3aN} zu9RXbNQKOY#;*v0qFQLqANoKf3{gp~@J^nZ{WhW^Ocof-+O)zRUt32IC<|jrZEYc1 zVvvQ4Qo0t7@xls4VOXrEH0$6rnr!5&9r6g(a2iP#EYw&~qpIor3nRIOfFed~m*Thw z0QNGhp3U#@#fh)_muy@4r5o#UGb{L~UOwln z?N>ea^*=cCzvh1O_-l_mc;C6#eeC?5SHAD!4XTqLyh|IVF;x?C4+c1S`UWS}4>Q7P z7PmdU1C=8E_NwhOnpWTr2cp=&y9csCiaHx~HiVRzQ={kP+;ve9BSCJNT1-)EIa0?i zdim9Qk^&fal+3uC5$@Q(@ zd;77kL<|#=zexZ|qk(3OFC8zz*T^}U;8Ig!6s=g?ug(mPPz6B#)e|+~3y+1~$S~A9 zgB@PNHqzqAVxBrxwV)!KBLl31Br#dme&(9`<{gef`^5ZHVtb8gF70`5uO=Gr7}f}R zQ9WUepK)Hw5gDP@gi?;U(w0~Jxk zX0nMt0(w_$COpi;ugaUt=mn zKQhKNrReGI72w29;3CS22&P34%k4T4wjQ-0DVw7at#G8N`+}(evnvaoZQ)h5-fjHQ zoc@rj_Se#wKhk*b6~ecJx6#k0@~5M_>>|5YaGI`9$7A^MwJd|P5JnHs%fciLZJW=k zKM7hVEeI^wk#%crwr;e*g5(tVU6f~@mnFjte_>p1erS9GkP+EhaEcT~zAdytpcP{3 zB{WE<)3AH3KYyw|yOYugJ2G2mGHZSt38q=J!?MOoqB%-U=Zxg+pl`Tnw(?M#-K&I} zngV|Qpo&Oo?1X@zhLY)z-Qb+0%`}nzsap2VL$GDjhLj)?w@r04i@WS}Ozf;36n}irw z%397^$&cR?!~`}YhG4K7cC;1YzUAO0LAs^+)_9AA*heobt$fFdz}@4Wkq2I=pW&0` z4ww=f+&g}*`=ggv(bt|S^*H^^*w7)PR{#r8?KvJ6Q?&0${oNj)iZs;}8x_%7odYj2 zRQ}PoXVzG-L?eJEL-ESX!|FKQ`u;Q#{YRt3j}@Z|oA zDlNu--OkPLLkJMdR!D>$FCMAqS=<}sn=-lzdChl{)O!`6~|uQzwgNh zulevNk8B(rXr=Dcww+y-tsB?TT~)Il<2@-vks)TrgYX%$O`x^Wd@9e~3WQ0 zpcH0CiL~KuyXq@ZV)$R&G5@60ylP*0vWOTVF973@^zVGA#%fG5Wo}H$5-|qu7;w?PC9@yMSw1* zl)+R7{kgl!j==up(UPfbntelU`Q35(Y!k}7lfWFNxmz!(>+8m0ry6iR-$BH#zi{Od zXKMbJ5@MKQChXijGPF*fT6t`LEg_yCzd%Qr1*TNlzs%-Isna@itXf-aX>$!oOYrr( zgh#6nWY-Lwiw|Em`z68KF0HRkne1q#G4GrIOikLKZjNG(+22LC=SazK-Z=hFGaiDI znj_fZ)ZA3{8cQz=75(#}eJi;qsE}T%c};byB7@`Hk;hL%L7f6;WLVBclov%2SrDoXJ46{$5dfhgkSH^hw_kj8I$V!VfsaNb=VDI$jH?BpkpIg-spEQ~1# z#|V$wB&OO?d_kgx$O|&NSCMami?mh5q^VSMfFydu_;rXz0*>$thil?%#VN?tDVtXz zAGfleJo?%jd9VP1 zv;Y?iKYp>`B3UaQD%N@DjF4J=!BJeV8bEWD8XZ!#saVGSaF#8kXo1<77KLCTbQiI& zB27=HtAGjtv#C1=%f7?|A(XZ}I@TaXCr;HpPb@Sd6D>s~aU3|kjF38rMb~l!vn4HS zvY4mMk^0dxe>8 zH8Q+f_C0fG<&v#+>!OH#hzk&vcwQWPI$4pVI?R6P(h73Z_%(5e-vofq4&m3Wjn%Ul ztYflv1O_dL-Af22>fGtE;iqbW00I@Mh28jY6|)Z6#Kt%XjCcHsHs8|HEcuJ{FhZaf zpd0E3hH#$TU$^~#{$v$pVajhlF&-n9<2%M9`qiZK6=&9zWYpz*d+K8r+s~;LIpe7g zF)hQjy8@MBuukQ8+X9n1Zhqeom@3TI`Yh!BP&_1Y9DpW(NvqoSnc4=r|G8??m{s(d z+h^63x-}9>DZW^rhJAE|r=4Y$5{@L}B6R$FBx1+b`uuS2 zNC8JJuDP(H+O(lUBFPsQIH@2R9KK}T0cICZHKk-+2m~Brj<48M#7M%1`*xQFP8B)u z4X1aG>&59-!8^u-c&Uz7dgZmc8H;4La`F_CQg+hXvOj%Iaa8Bx^EMv8@TLd9anGTB zWB2jQ#>;!3IlTY5>puE7Znb+=p^;JA+R6Mj=H$1}y$a!Ip|4#N_e6Kj5G>~Qg zwUbprvqr`QeecVUul&Mw^_UE;d;=)flxA!(G^PK@#+r%M2ru~TZZnc$f$-A@YQKiA zThk0v|M-D2NY{9hQ7ZzC(z>a3O- zn=GLHnfsWt+NId-X3lMI^W7~DAaUdanyBo)T6JTvvu z?CbLX=sWeZhUgNzvG`TlD`rm7pBe8zYFjBUY9S>l>xhhyOtpZ+wPvhF9-pNA2lvk! zq#AjwtP&m$Vezo9h)wfmPK{DBn!*`jp;YpK{Mca9EXPiiZfbY`_!V6;Xt?*;I(LLI z6SLS-B#hWcXh{NP^ObsZtM;JtErl`r^O8lqbM>-A-bRxGaN3OP>M*j@eVpn6k9!|#G(?W z(mo;+CK+k69f>sbw($!~&7T5C`!qjBa+A`Z_i8B0(Y_2Uh}0sSyh1qj)uq|Ja=GQz zOK7o97lM>+U+W+)-Ub%jt&deDQ{Q(AOeLhZ6j;OT*}sXo4%RY$I=ffz;cehYas%t_ zb-}f;jwv(5Lg_o`cU8PS`>S4=%ce4VzNuI+nuJnYn?P6bb@^4@z0*9 zy$_P^%ppq(769QqPB}TP!V^XWAj_i^iNVV3;<0bi3zYn@Gz%0-8sv%Lgn9Ht-6Ui+ z393dhYXXrI$chjLO%b!&XY94rvMlSx`EN_7WfC?{R#cQ{tL?1tMmx*KGS}PBsWq|n z)`4TQ)$RK)S^0ndWi7I~VBz`0wGhm57U;h6TJ21wx9}yHAT7hfcV4JHEK7|)f3mij z5|v19+f@cN5$|9%si>NC+$vC1>R?m$iu3E{2lK{p?il~Z?rJD>c_BH}pEyvJQvIL5 zzFIqVe{y_D3WUA!th$X!>nVyUS-NTYsEbpL)Ug)XM^`>})r@A);_1(ys#14-8Z$xO zJwAq#A6sNF4I34eOKRY<76h&}HL5tSRcoNflC>`28Q3r$71ZbO|;eez6`=`POq~fnx5s z&~Zo7g`he(XmNrj<><1V+s{64=L28;SFawMeAwm;#i2;AHn|ftk8bnNxZy35v*1n=4qb=|nV^?KhS=IfKi_o2gf)598#F z#Pacz^~9o5T|!U5iGWgQvCx;DH+$PI0$m!S6{qTumrUanj0xT406$1+3<3cGC+slE z8zs=X(D`TAQ15yS|A7}~y0HnTPSt0s?jH}-L_nzvpM{^is=j$du|4W$xgydCWWvWu z4>a1l@Rb5<2}x|ytGCxvIa3RmDivY1ko`wvmNA&PK~nt;l?*DS;y~}(Rxg+R^4O;N zh3kq*Q6xDvmKj_?gr(}jPQ(cOFYhcx<2IulNfXag2kJ*-5rhql3>vHd$%8dzADGWU znFR!s>DWZ@@oTGz?woHGK^pc8kJXD>e_%1sh$Xy4#Wlj@b^G`rfLXX^RYJp> zAl>%Yxf{c2qGg{!l{E!m#vKb6oKw4UCJ0UX&%e9!k-SPdkDQoUxN1E=kBa!gR8&TW zQv!Pq&t48Ckd%C6!pT9tn)IUCk*t$4 z9^4DfNQwX9S{4% zg0O6J7+h$L#;VL~wao;0Leudw5Iu zk=(%gq4km&a(Gpu2Yu*%5x&DnKc(AIebzGRMrqQk{6DZ2$8hUDQL94T5 zdY%YjdC`9)Ss0^3PdNd}$M)BZtEg2lGf6iSok=tTErytKLWJL#a*`%HWj3D+ONY$y zSitmFRYSAO-TB8W-9 zIoUP>95g+bKG9nTI>C%_;Tr7e$Okc*O>Y zD;`Ku)ZMM^+_vGIOWya?bNilq=xcSOd}ia+sgrvi`_`%L*X_LSgLmIlAH!1XhDxb& z;D)y8K&iUtWaS7G(k3ZtOs(a!Zk<(>GZmFcwtR>WT~@E1PK#1H1wQ1gB^#mb;QB}mqT+}i#4=MBbFij6bazF<0E$vzfu>tVUTV@18JHu zju6RatT1L~AYzs-E&S})qcP9gP+_2Bsv{5Bj3S#cXvDi{%&^psqI_pw>}h+ zk+KYI_%pcLAHf z{_ZcmJ~a^g*wtk%VX)7FAko*4PwdA|g9QSaT$D1RjyxV4Kh|m{%3Q;a$Y*oYd?J=D z^|&6BNN25MEuS=R1u$kYXY+$^ zo3=&cXvRy%7IRb}Vp2$ieKaDF02))_Myx^7u3EBDucr}D)qbmKbSk_6T6C9@-76rF z7&_Z@{DReZf2n3Fu#C8y3QXyfr(S=4>P_u*>9S<9%ezW?EgYg17mQ5lDc0hzis=Mk=9^}NsZ+EeHF5S#aMku% zX^+thaY8uFuXNlz#!i?aUKe;{8f12FO#ehcY{746v#BZb;IY{!y&#TSSgp;?WHMWH zEv6G{*NZSjI2K|P->l46rul#Vy;_lkCc_l7u}nSzq^Oxm0~51(Ju4omTcglwp}sfo zEZPAH4E@KC);3TKLRD&fB-JgwQ~+X5si;_!ZasVU_&R1sf|(zUrj>;k$=HO5xw$0( ziW0ME(vqG)YG(J)EEyI>?a$)3q^nrt6FYOm!=J;oNQp_!UwwBBJh(H^G?$TQ})mNFO zpqNX*5g>B*8>vDBlBN`Y;oLQW&n!ey+D1A}DZcQ?=Td@{x{%YCB8fTUk0Tv5%pftd z7NFtCtHqCN$BFTK_It+refUO<>>R~=vPG-+QXFHHXblc-|JXE4 zWoIM?9cVh(&QV9&=okx}w)m$_G>;PR8?!F`S zCo2n#yXN0gAnX7F!Z4fDRis8~4G}(bsK&jTC`FYmt+7ES<;5hfmA>-i%#Nyk=(5_0 zAbiIaE8pK&S;hrZv5meYBwl9Ou@+KCE@WNZsV~bBPS#0bq=Foirdsn3`34AD5t|WC zQ6%B)jk5q$L5)}rs>f#CHD9kvK^h`-Ks_O)l<*S=W-I3~A$(3n60ro3QV2hYWYCJy z3SI8T)g&9Oq-m4pCly{Bm!^)1Ih<)W2$OnO3ea60P(iezd&-0k39LL_>C@6>D4hy4 z?R2CO7~HvSu-@1*Q%8+(RxMITOULFf3k2EWnwG95c_!mWG3Fm9{5Wi2y{mm_)k}0O ztR?*CPu7e$#alA_w$U!)Zt8K$Sr{3o=r~(onFwTeADX?4{id~Rfj5mFk-XpaU^Scks9wTRjFi0r^OZdX573UDZ&qh_+ z83$Ol@Ze|^TqEFMhs~I!Rm4~hy7@eJQ*mOS zm@q6%wV0cVhesn{v%TIZB!gz~_RDI+j)1BkI95wlHq#neO3G%_1T!%+o;94bc4vk) zNKF$A&i&7qjMALw3TAW?D&Kjbe$4y!OKbJUqsafSU#`EMW~H@bG}^D+T)me^?(!`a zG;5aKQcL9cy5_=4Il^H_kSwr?<3`1)cguke&A=XslPA_hPrl8 z3-Y?R8;@$<@n~4y<4AadL8k+g1Sg9mj#4*xytu6*rj+i+jpNjedsIgrd~@mpfw`rZ zl4PIcbr;SSl({=CBNxy9(kqLJ!+3~sp_DK>f0M5U9DOh9p)rL7cU^z}!E>&9{2PDx z+Ohd3!OvhEeR=Oc6Er;cn>wbaEqLO5_zO^|I&X^3LdDJMtJZ95f~Vg@Y< zQB;~O#Rg7-nIcA_9c)+0$0z*J zu90rfa1tsb_|hn1LQW+a65km(g`_c=ON@T*9;?ynie2YKmz)}}kpcr|r zCncQl(nS@KpT>Y0ns8)fpE)>tVc)3B4l(0)$&3IpbTEaOJ!~>&1i?T`!L0O?SJhxh z=BKW%PjNv4X5@ykr$btn3V!0R@pk-GMs`-Iv6TR_0uUrc!={n&#P~2%O>koyC_}3v z%>VAb*<))?;!VQ{vqo$(h{L-H*Wb(Mn3Qp##E*Zg#H}p%bvc zF4;Du#&cduaip>=ljKNC;=FpIwxV#JJy@St#}N`Sahk;Oan#!4ft^o}H!)eTzS?u7 zo)9;S5!iR6W_F5V+k94K%{zBpS$}=GbG&OWD#3_>DOnNzH}}?Fc7-&8R7VYk?|AB9 z{qzJ!Rzw>T3Wu@d3UgEdp(^ig{a`P*&sz-3eFuSBrs5f{`+5_{k0>Kai;}vq8?6CP-MyB?3meu*2;P~^n#*n zXa`#DIt&nimAO`$qU$n6>e4oI7M9T=1yfq^`G`vDxD2yc#+e1gU9gt%Rf7puEm}2z zWvpZNokE|l_weJ2_deqO1}yjZ)4u?Yov4|0RrKyv+kaEDnHw5!`%;YJvhI|#j^%05 zsxeHN*}Z~w3P%NQ1e5{`0scSwcD>^eQlRp_9kW}nNK)T>q;5hq3%(SewczgYn39{2|M`b2`_1RZ85u7} zY#AT>Cz%yI!a5uMCy%cD`Ul4wCF8gASWsGljc2c(JXrkS{(7y*8L9p`K1P1U1uL62 z&f-^)7=H4~`cZk=s-4dKOf{@!bU1(X$jblmQ$^ECYi~Qdnu|4Jzy&(GQs@(85r5VE zC&rb!YjOsarj@L@5vo(A^T)xs5EaGhrTqxlhU#yh9j|j%vyVGH6%^{{$B$msF*gh7 zk6m3)7P$Lz8=2o|bB?H3_cMjy?#c`5cD0oaqx+y?^R&q`Xb=+y!P5_o`3Ps3lII}i4*f{^$f$i=TY#z@{_> zq!Apc<`msN4KkY1O-?{S`z)YMMYq%NHCnn*qmAs^Rs(eqtqx!s&vik#`GB8nDguNJ4z64VspNZen6u$)oCsPO zi{hGMj0bU{f{GGqMqorEMNyADBVqo=CrUVzdoFf|Quc8o7G^deGBja^Z}1lk#UsaS zU_l#3WWjh60G~i$znILyqbvW#Cu+!2@eF;K(~$~6 zQd>b)e@rfS-&o(G`j0<4YcBFe!kc!~?Xf1!L{ z2MM*y-)#62g9efP_1j(_#xr`GO;p)GGSTJQq=5d=Wi_)9(ClRXaI8$885^M4of&%N zOQ^5uSECPnIU=pH3dgvs}tXqHt95v?h$k@MHtzxVax9C~&3 zv)VDpS_mn0T4!(s9B8P>;7h;qTk)*lU>D?=cHOOCcgJ^XTBD7}d@2eO+EE0&NUhAMuCC&sB&eRJeJLOCv z{txV$(VA;jWcO;pEUaZSSeA!$zg3HV5WM3C;T<>luHxNjfggLkw}JHr{2(1@y8W>h zmhG?AYZsVrJFrz1In8i+I@e6gtSY(8Z29M2u0Q@jCebN6fMA(uEbLxoVuPe@ww;Es zjHroifTOM~;<7h>?mb!}yk+OePmm&ZVj&Ybw6E!h{cs3m;d05g+C#yy7>cB{Lq-3$ z_twMnfi(x=tUfzVFg=@bWyO}IhDcI`#>~qb^k>CEU6_i9da?RxNPt{Cycm;^a{sov%N6^mUF%pw?bvgDLT-8iTT2ldOiF zf51YP0Hn$}(Dih~SSv?8V!IDjYC>f^AJqaJNc{f?TEKa1 z{MgeK=a*#`reG?2Sfs0Q#x@OtWJ02tl)Lo9`faK4XKnQ7?*xD9Xz0=au( z%^kJ%`p2%GX}O(X-$9FI;W>x_F%3dSMm&9NblIT@#8;n+kN+b{l(qmX^Jx)r}jSa!0~NY?z-W_`;X3MLA6k?uI3b&Io6Un zG+wDz+8`zyl_@6#bwh5fO_?ET<8>kr%QA>Aok~uj-)75cTAKZWVA(~`{H|$pmVM!n z(G%*TTMg+Xu;6Z*O+TDk-aLXgKxJEGiezJ98eSVT4j4fA)rW&A4XxOia{5F|b3oDs zN6n!d`{=1avLOOu+2+e-7+{`t)@+2y1(mrkM+5?v!8~%!cm!;={e5NK9vcvF2#DYD zxx4C&Ty|bP(uz z>T>wi+8+sXdN6+Hm386Vad|0%J8E;H8X4#;FbEKfQ(nwg7DAu6Zsl8J69-@SzGFN= zY%u)$UoR$)X;QbxJQ_KLQB|A&ZGa3MKT#XRd6tk0zSAcL$2UetO@q`RJUI)7;@`Zd z_MyWlL*v!|4+T*+YyyE}m)Ae~ue1|caa8?=|?HPXrGQpt} zA(>R%TnTBPFFaa*mBG<6^^Ls4MX)0b!BC9xcn?wY>`LucJ6A8iy7KSeR%V4-4K)@~ z@)T?EA6-k8G1czL`Omy$D&bUm&irqm)gWWfG(Dj(_V@^kC-XZOEGQ)=m8C^Ywuk}z ze$Tx@fG{R#H^LfN7W_)R1xaoJdyiH)vXCw+X%5R~Fbw~s#)BE*?^(9#^#nX=|N zYb+Ixyf*9M)+Efv4p=K1Q{xn(h1P{42C|rJrbsYZ!G?+0eXv|KaFQx5Y^eTu?S-=r z9KO_9c50yQG`iDSxaL?b;=0nGiSeP&xhGq*p?iev{CtyeG=0$G6$xP3byRUFsGVzDW$R}@j zT|m})D?!G-6uQwUs#LN{b7VDxh>;YTu3j8ZDiQ8b9koE;KBJ!-zf_Vi5Ws$DOkuow zV*I}Hh(UI?G8;4*i^VJ~@?qvM6N7{BP2;ET5W=wbvY3<2LIKFo(pM9Isz8z*X|e<; zszPW*s((?I;A1t3P^wY|YQro}!muv6;(~hYB8`TbMJk$X)@^k;jMW=!A#kp{u=Zu{ z9Dkzxod;@N1y+!PC!-?&NVc0!TVX{S302^*{{uWXe3Y<_GvWQ^8b50Efnda&a25_6rC{#;&%%WmElD zsQ@C`0k=Si&_OQP*ss4#%%91PuH&QQO3x*E{yDP_#wiuGYYyO$NfA>nQU&a%RFOmX z11k8CP|0_f%VT;tiF`Jm%++f|8d;-3O&?>of-oYcSZ3$5x7TZT(CR>}_T9!--Gy69 zg^nOI!e<~7UTVz_F^d7q05V2nLnX^(?zo~xHY#ZLNdjx1AHOXN_;-(afn{_;LL3;o zW(F?071+_TnXN!@Dl^`4CjjxJmC$)C*8qzN`(LRaC?sD$52AL=jrf6B+C=}^vblcX z_1UqEFywdRD2pCI**mt8L%k z{(2Re@vphCUR&dF`{*HRt!*vW6`<{(z*F%OiDXQHX_C4fGaIA|!9(Pu2P z^XTQqv#K2;$?Ts!QB9GP4mb{=h;MON@sF=?#{0Dw&VtDoCmtWCH5@G#n;?MD-+8`N zo*KW{7!JvJk75wxiFt}#XvCsu)ylF^qZqAfilNA6*DGnnx(HH43z76sAFEdlQsmNh z=>;imgzj`&6kiQlV3V%T$^pm4a^7q587AAjVOO^|z!tlYk9wuURt2&Ovc)|D4tUipisiq@*kM$4h02w8ek;V3Ds zfBxON>&O`tkKmu&Un?Fr3}Pcdj4t^!0s^F2**mQ8ia9p6>if!cMsnn}m0!Jiw%(Pm zPhyQ*0#Q|4WxchvoVo+lllL61x;XBCzTUgZD;^Z7m)7`F33pYUvBM}UT0y$`#jjIv zMJ=9UgjO}kpdI2Nz!!C5DRM`YN;x1dVwu9j`-(#`i+(f}MQAmpq9_7z`=z7VNBeN( zHtgI@C9j%jcUg(tEl~Q8^sX!G zvP6aiue5sF{PYE>(ffDS^8$)e5d!$aj*(c4R;s|D;1M@mbLEz;=RJDg7hZd9wcouv z!|>{ngL@vk|H4~;@$B<=rZrUR&iU8DorQEr*S`Bkr9c1&(sa?1hVO_lB7Y}9K#yG@ zb>X|kIx#kAnjbpW8UhFs+q7tXOzo`t6NJz*vD{hxCzDZgobjA}k~+Xz!Gup>84*q| zBDShbBf+?DF|eb%zqLn1=qab8g*=_q*+w{#yk%z%DXCgZDiWkLapLb956Xd53`%nq zEFgU*K~i+9?_xwJOI}eoPM*hX;iURo{Dk% zn;TI9Qw$WVt;YL+IE-ZJ0#vBe;=+*V_R>UG}1S5-w74atJrl?rdXLHs=m%u31?*dZK1Pe7-!2@0wlqi;3UqHNKvw6&5|X@c4DU-OPMNJ>FSESy4LFMs?{sC z+*Mse)~e+=UENh()vHT(Dyd9P;!^Cyvn6XDDN&?2j}So+1OZ~6=l5R!_W6I`;nnv5 z5F8|vGXA`{+-ILXopbL!XW#SPd!L3At6yT;IrF)8teILcuZ~Va62D;j8+nQ~Vz;~k z@wRnS%NG?97vii5di-J_uuOPedz36*zO-@bBiD^g>46vo@*MraleL0OoiL7C>X8vo z8EFefY6&xfNG;d+2t}J<(gVbD_|h~dAx_kTV(zJ2(RGcsIkE&|+}@Y~F#?#`^nc#a zR26HO*+*BEUP7zoR~DB`00)s!(OH5qq(sX;0uZz4Sne@*96Vlq3uL0M6Cf58sh~w@ z4>(~)W{4EKouN!tuY$gM1>Hh?O&E-+>?f)wIAkW87A*-Fq(u-Wh~dKarzGAsez(JQ zWLQ&+nc%zd5OX631?WnIvplj{Yry(Axs>I9}N=raBcJ)=HkbH0n0V~ zBSXJbPB~J%5pcSw@DpT*l1q()#%#K5AoiI`x z$0sn9JY5X2Jj77a&3~^onw$8+?K+ULb>S2>Co;j@nGCvCBwuJRG=+(9ad zL*1yT1=)a3i#+CmFYB#CL1WHgF#A@5()aw%TyNz#>Y zKxDf86xW?UdedG=i7L6shTZjL@o4QmT1l##&E$xVV>O{=fwuI6y)6eHQ<|uNgWwCn zLglaHb-fVzLu0JPtY}GAp0}*M$k3k2&(eVWDUt+s9%J}$`y7pY?7Q$j?vxi+> z(e4n~cl%-gg=eOI?xvD#m8B~KPRB;rUNb+mzGSqHQ@IyHqukykqs?t?OyuT-Q+K?M z9bY!`fC#cOP5bIJ5LLBw@GI!fLsK7bH(H4^k=W>>ZPasn=3lzv|N5Rf zpBYJ)x_L;upnAC7n=$B)E5VB)pJtULMFl-5z9BW|KZ0gs(7(cR2we>h-KR^ z-CDP}Iul*ZX{IXRu^8gW$-1BfpQ1hc8sa1ey&)v_zy18E)>f|D(io0R)Xf-y+%#rS z#yx|x0{zUMdAHqq^QPxEzxd2Uqt{d4*f_Lj`}SuaUjL4tUVPq)Rqab*IozN*Y2D}o zX`5q|k4&Khgr4by4j)!c5W%tbm9X^i1RM)T7StX%HhKyP(%V5-oNJ+ThWvWCY+UQs-RuVAp6gP$b?o@x|U*^ zlc+jB>r-KNFR7~ACnM+hInC7Ono68QVIZJZRqr?G_9NU?qJ{5(X0#kKYcN?lI@7|m zXNP($05ODOx-?9{5eANnZgG3*Y(gcMYP1q+nv*zzeD=c=J4V8E71WbH?Nnp|KeXC( zSM1=co!`EOo;DhM?pRl~HWz~JV{NV6@M4b3a9%wCtJ8A&3CZ4&g@e(kZ%4`cLi}U6|zS zMA}np&DsdmUH;dtdFkE!Y}9q5mSSdM$nD~i_Vr2bGK~Mkl{KRYY?mWDUV{@`Z*?X? zMO)3e?SVlz9VEr@z_!s}K2a2;_LAws%zm!T^}7z2z=HNiLJ=muk6c@|IbJSXig&eN zyl}*Z;5VlMh>&Rqwn0$wI&h~f+jC+ANK7sY9%~D z*PXC%_RzO4tNXGoIy26!p2og9`$yaR;HSjd#Qa(sbvm5A-#6gu)$!^2t>O)WbFUD- zdQ6;!hLS$^o%!rMo74AYWzNRPD*|h0Z?lW#LsvsZbJ&EDUTX}>HrI=ZVaV*OS3}F_ zKIdgWQOojh`U#?yTEbm^0^NM_sdlx;bCQD!jEkptSC%il^fo@RHCn&I= zdrK*Pul+5Bx-Pbi?(Vcb!3)xcPd>LOwD7rLgvy83*Dv0>6cf@9CD5{Aor$w{SuO3k zDZ+eU>(osb)p5zmOfnJj!nIRc?bGcQ2EyT^$!u$1aI=9b;fWdX*$tz=x(I8}U$pMJ z+n>7s>j!tYli&@H1KXcFvVYgrZ~O3p>B<>%$iQCf(%#O@>Qygh{eqYduPeRXS_fUG z(?gC+K>zn*2Wx?IeKu<&fR;C9ol#N$blDk?SJOpKYHZ; zd!Bss!JW@OwDZ|Vc0KpluFa3{+49uYGfzM9)CTJNwmx&=@s0am-1OoLoAz(tbYREk z13R8ycbgGf94RO!y z8$IZx>Ps7Igt_6uso#2}-2L)9OOYLij~#<~3%t4%C)TRiCTkN)1$?v|5jK@{MaDPn z9sN;SX7T&zP5uktuVlkGpS3vzQ((-G1JgCecL6~1a!5?0qVaGwHeUZ zZi-QL`z2H7FRmds@t6o0A(r{^>zi8tm|>Szng^nbDUQfXcns8JbOJF`5}0#Yi!b0y zZ8fbyFcg8)7>E2^mk3RWX@&L@*3$GCy7J7uHAmpkwO`-(gzI(AmP*;TE-acswY;xd zTGK0zuEPx9%r4Oa^7tlJ{7w5t3t`I^*L$NK0w5D`7n;HNN_YlLdqxO=rlq%uRdJ99 zl6l*u?fafBf50J`_Tt#b1+yblEx}VI!`XhY1QL&0TOK(HO-xAI0=xwHgwsHv3;f1H z_Oi8Q?E&ook)BKp2_Od!dSIc|CT18~fLQ3Y@fDz>8xD5~Y2VSx=(+&itX@IaJst7} zW#0Q9It`tTO)li#6F8mm9^nKg_@@&)9bUFYFKh5+YfmX~HgTSA^$K2A_lJZ(E5EM` z-Z-mQFmLwF@YC*_3nndt&YvM%)yB0S27Y*!&k}P32EbaI||`^s$q*Y|$}S ztn;Oza*NPvCxB^MHFJ0M&JPC&bb~?oa7u%i$EHWmRYh_rc5Gn1D|RiI3G4M7CHfO6i##ltuEu=C|z&^&Ko0##tkscw$QCtjz34u?5qaM{S-~9tsJ;;?yYRxE! zN@}lMS$_sQ3rxUH=&mcz+tvP>Aw)47h=CHkU(fevb%srdC`W%G`bd^(LXeacd1$8}6*bi=4Q#Y}2W}86;^YrY@jJx4^ zW{{cLc_*f4PafWL;^g%F`3qL;fBfO6pPM&t{=E4ME?72y^K;KV|L|8Of2)@**mG>* z^oe6HZn)o`z|{PCB{px~RPBqPvh@7b+rR(7%yhfeJ~blS6^q7OxM1G&Y!w$Rn0K-f zm}9X{wEfh)1q(zz@94=9XZhlJhfkDHB-_5kYCmt@#cS8ByXzMgEnPl#S6bIxIRCkQ z^M2~xw{JM%)|PM$g9gL-3l=U~vW(V5KVPwO^jUqFdk9W>P+-VEC5?biBZgHIGDA)u zC_*@?7z>JWiK|RuZ9F8F7IDo&)GP2iY#6Wx_Qnwt(y>F!;iLzc6$#h9MQYuAKf?gx862N3!yiP*$JE7lzo7Ha%cPDN0eh3U?dF>x=rXt0yIbS z=e5s<4!msKQ!`iS3G`kKy0y8I5DU-ktG^GB{Axn1yBPXP+dC#?PPgw+)EGBz`5CxZ zosv#Vhn!$S7>2b$WQMhKZx~OhJ2ZYaID7dnIhPn>$UL+0GuL*UrPy4YE|Q*&5Am{I zUQrmYI_7iuVc^^X)(;E)M1K`${A<*O39YlS^D2DJ#VL)vQp>|8oPFd&t+_a*ovt{y z>cmWYKWI>OX8TYnRQt~51TuNgPa?c(RAGx-kBvO@z!$gpe?|3QqQcSwJUIIiQ z+26RT`-QaVs#W#6M=U3piI~xG2h?NVe(V0JzxDQ)_$){k12@M1;(<~3O3=xxBEuTU zp?~q#sf(74R1(tAY1}0wdu_o%KxDdb41(luef!D-F~UPr-7jVcob+w%eBqhu;n){X z_Y1_xBtoVXht^<7Lxhh_PyJl`mX@|BAN_lutyk@Ep4(fCq`5_*O+&TL6Y~}Q0 zJDc}U{o#|N+nCGFpZaIttJL8YJ_1Tvil}q6I3+oAj++0DJv4De!t}xFH~-N;`|tnWvBTqA z|2H-kEL!px|KtC9{ZId=3m1=`fX^{XkHN5GgY?FbKkUOH(v3snbdo_V!%$$ZkyFyq zomAX4d*b+!HH(iuz2TYNTb_FH>HF~K&0jEo!NQZrk8FPE%g2u#nm2FW{vDeY+d5CJ z1i9Nj<7(8K5N!pk{+l;{^+lI0T6xKQlh!=DYV*<+tFL*-Us$+!nZfRg%P+t7Ew^tu zP~Rq0h543C&(o{@z@@h{`1Ia(Eqr~`i0+s;*8Px85P9^2wOq3}U2!yajpgzCtFcw8{AJ_TS%dH*$4grVQL%>n9|8R&3g93mMyzGi0YH>Vwk z>RaOzAe=Db1?aj@{@Sy(O8XzbqXu~v(zKtvs>a?(_NcmgqB7THCjH9BdW6<+Y8=Z1 z6htMXIzq7z-Q83y^Zt+8XVJTG)YCd*21!g2LR7k`gfa9B2?pMN>C|sOTDtCIKR=!m zRrTPZJK}xo!O?S|rmd%UO)VcxK*XHi4&PN5R1ef?GtNvVrcq{lFqmq4)zzzO-73vY z6M9wX4ejxBxTHNqBLIH?+8y&dbGX(h_Scov~kDjbgTXcjU@SZdeVn2vt#8MkhAYexyFuT((7W5c`FGwlJ#?!`L z+nWqSFJNMogoN4mEcZBrI2|em6N+76f-|=YdT$JCUjk&2j^ZP{9&O#^Z8027aU&X(KW2hH9{=R7Y_WuLz0+t zh39hS;>gwV;{cTo`+Ay!%n8~L}C+ph-_SNL=2*y2=mA{zI~^`-WJ6?@2yJDW2Trd zzGs6sClpQzx7f?Mq2G%<@P89Eh9N7BipsqKmH<5nTcwxTdP`ncdUAoq;Bh@Cc{& z-#%JP4?wKyRIxF+JavPuF*Uu@|*^IxdU`l z=awnQ)W(Y&!jD{AKc17pPgHlecMJu9$u$*3?j;yc^EEuZswjr8Yrm!m=Ny0vriJP< zXGbUJwHKYBm?NHVeyO^D>%3q5z%Ac=a@UI+?w_3<{N1}ZDrRSy4}a^vfB*ma?q`4VzJK>WJo&YMi}}Li-`eun zH=cjwYcD*0&!OGhjvm-`WdE+2>2_y43p2AbGjECm_sTJw1|B)M@8GT%4(!}=VCVDu zcWmCjee<3zPd|JApML*4fB5}x|G{J5{KE&n_D7F@d6l6?5hxxJgVZkc~Z-;?6d8f-J)mf0i_H(W=}Z8O7`3RAA-Q0Fz0-|| zX+*T*!oVwl$F|oy+Mju}UZh&Sc(g$IiQ8&|FtdN+_DTaZvyR>O%SCxEU09G3suZMxwC$nI((u& zJGFR0O?mkDZ!H&Es-y0_v`k$%_!13+#F)}iF;ZVCny#ZPcmc<01m@=(m+dIvIHeOO zMC|(3M%yJTYWv>S`Xx^SaOC9FJKMj zjAHf@B>Qw7-ADpvcO)eHAtD3mtF96wLHx!M=%U!P2TWVKtII(6a}nlnABemMpy9l; z)|(u}3k(%u=yw9C>H;C8ulb{mI%LQXo&6AM6Uph&?Vk>*-Ln7GVng*|B*7_FAiV_U z_uT7217<5Wgo#`&oC3^7q=U=Fa^T0X*p>}!uv)4`1v;EKy{IOWNXHd^)K_>*d$KGiZ0*a|hs8_Yg zKE=%R;JBCxpmVa1q&OoSgnoM0OJ82>U|*G#8evwZD~ajTwkDh2R7DKPpn)W_HN}q8 zhzGXSgFlGKN6a24*wxaS@RNUJhR#0rnaZ3Lu`tV5%WN{it3qQclI(j(B$<-Yz%tt0 zCLs%wSJ|stx~Oug=l9oNGGbHCq>dtkv{o#sFDayfcejth^CIhFO$?YZZcB3MJ_JpE z$D#Us8%`pz>DA*iqbIF6N*EH#zxbAFBLN2GcJkDmW;h8Fjq~m2r{3RQnjiyG$x3`_ zjX#V3 z0D=B--PMUP{+dZ|+P)S>Vwj$px_nj9S}J9CTklSTO2P(vG;88_d}YSk8=Ldn->=j( z_f%pN7$!*}aU_Em;pd+j-GEWSiCLXr{n<`h{5o*W!ei~5)+Ic?BH}BjVrWeAd}#t} zY&zvmOxOKhXXci2Lwje0H8Opl2%)*(Q~i8o>Y*Kr-u;fZ zJ@xp*d$+b9o!{`7K5=}@W8XY*V$scSz5D)c3o{aGnr_z|N8f%3#ay#CV)puzaIXcN zUX|WEGa?Kz6~zgy+ytskRcgCT1-xta+57+W^Z(!fX5Fe?`;Qqf7c5%b4@$=EL&s}VoH$tFklYizOvTzZ&*`jlnav|%#94r zF@snH63Ix>Ak)6|WWvHEPuO6%df7`2L>NWMoSdm)2S<3A0z*^REuu02vW8G(ik*v> zS0=Fj!qHac0oD7vXTvw~J=HC9O2tF@`d9^Tf!Fja= z!_Kb5_4fy=s{JuX2A40Y>3}aGjiUN~tzH2y+YZ%>cnC}`_8%>RjEPNd5ax?<(1TZDw81LH*`fEi(YGj$GJO{48( zOY_4aQ|mh|`Ej^8z2lO490o@fupp%2Dgdo?!iGKd39uX2mM-BhJTrCpWK}8hn4gI9 z8%YAtjqE6DwOq)=3NdBxiugz0tzbwdHuX7<5H0P^6`>)lJ-M@%y+pWrY3<2mtE1LD zQeioJMOvzuC7d9cvac|j)3`vSDz3W#>$k0f7q z^=ddOO&C9euWjuKo!1B!Ue?Gkh6#DJ+RQo~IHr(%A2CI>#!AG*50b&qeuue=*`ejk zK#C#e)=H`}ht;&&rkvD9EOdN5+tNNjXO8SL5g1wy^ZGSKPlPH7%NL0Gib~}fQ=os{?s?V^jeWkx;XXpM|ecPjFw>iySLB#h)EyAW&S?`k&^ zkMF2uHP%!L_{2;JD3W1;eeF_(32v4Y(|aK*s&-p=&FZ?K@P)^t+Ns&m^QtjEv_Ncx zc2B5?FGxW1n>LDpDR;1SEZSphag7N$2#Ol6)zs|?z^8IY)UFO2Qv!$Dp46W_W8*`I z_iopsndy@=Gwlx_p2Mp~y-isQTQet5=+%R}UU>4sd!GB=pKiMUbB}-fk01Hk??3eU zf4Sw6ukPQzdFO%SSFKrm(X!e7(@PH=s|SdJ9ITuefQFnf{W$jXBr++w=(+&RYZv?d zZ7|2lxd#!j;nQ+jBC@c>PR0$jMwm72Z(roZ{$%^Ba5;zb7g1xx7Vq{MYFrC(k%7qI zkbw)$c!oh^S1y^j+tyaESdi%@Pf!%nFW+55BEJa3c)<9s!=o`@Ey?JTHz;wGQ45%= z;M)vkPnd26*=7W;NeaWv^{ZbUwNyhwm@(aTXJ9u$Fe`G3cenW8x~}F6Od!nc!ud5Z zv1wya%*2?|Y8EoEPtR5rj$+MyZIz9Y(PGxTdRoe$%8P*aAFI1!M&c#SB*VVipSZHb zYHNG}e7gPemsyF}KI%Dz=8c@9#f%_UhiptU$kEA}N?mTgsN~gxCIFhEDO;BE(2Er| zd3&|I$4;6F<<7q49+-$j?cuK&)DUq}RJ7f&w(^{hwYUFNvA~q}YC@Mu8?{&WlF@IH ztB>DM!fJC0MV&iZ?4z@!g?M^bEqf)Z2%NXDTAD|tx?e=lp9um)8e)6r6?Muv0h|f; zKs~y|SO!H*5Ewd<&{ZVkYU)3hi^q1+@;cm#QS2)AijF=yKvXi>egYXXc~3F>)Y_O` zT;Cq7MYpsr_F=esl?1h;8`0_`EwDOE^DJ;X|GT>h45RAcQQP%)s=5F|I(kYw9TO_s z57j{jHu>rYYc9UEx&0mO2I{@lo#-})=(T4LO*N;mosM3#VD#s2ClHxzLnulcOUP#MxV;U-T@ zZPE^VMH4wq#FsbLp}b^yDJD&^^Rw!R$ennMUbtx4;^$ zEb{5P3zv=BXrDwUZHyf<5k7Cx)IFQ4wqi*Cw{|Jk<70>FR1s?KVWI+Lu5Xe_j|dCn z#x=DzrXS)e;S>fDz$*2h=S;##VbhB!6PZb$O{O@gRf0=&u=w^vnZ1w;! zIZDegS1y_Q=5uA?>78}bB7~$Ls#}iK5AT2U)Tpr{wC9BpV3Uio-1-(%yAUk9%gw>U zBoEB$%=Ah1?d6rfy7m*8J;@M<7p?uN_uT&Ax4*n^Z#xCw@R*rC@#y{cxFK3~?Ys7$ z7(Eu7V-a*B&_+E_w`Xm+Fb$7P`YN1`p`CQs5S@dF`h)NP`6pq0P@T2ugYd*i2V?>T;ri_(Zw`Y z^eQmC!0sdUuvik6c#lDW2_Rz>4uUT(%zzm{$RKQnl$gR1QY`RA#we6|97vW-n;-E_ z8CR~Xl^Qbw^_r5}A_a9Y$jA5nZO#&8f+5DT0X$t~&yKDzXVp9Y?GMh*jK^mJ@`O7t ztr$x8`kG#;AkrK(kG6D3v_gQ|*#GEtt!n%rb4A!|zMWlcYH2u*V^+S<-3S55 zu}-`2gEHGbXvKK{v%E2dxa9m%-!kYdLi4DK7CHwj@&tNR9hhW>=)&?4#M1V+o1&6^ zB8U71z_cq+^?gtqR9$xdNM(pFFJF>NpAE8l1x&?bY<8lf zuU-Yc_H^<=ubmt1DRJOrl8BW&~=kSKYpX&vzjE1i-j-4Fc{0}Xk z&BbtLfgi@@G&J~1jYs}0wTSijhblw5uU<{GH1z43f6Ce+ovO|;{=#s@DhVP)#=ct{ znho9OClgHn%e6R}=7ZGkK0K1_79bX4MCM29bn`)eAIlv$F`3QfD@WFBA(B#bU{blO zr6UormoB_>%n?;Gk{Cf`dbS5uGJu41;8^V;^C;cS)3ziWp`#lax@cN&zpUP71+|Ly zavHgv6nyWI(Z`a&rN<7C5#&=l%L4l8UA6eaCdCAUpBsK48IQ?X?X#c&Ne5NY5+tn{ zBqN)|)P(h|$@urQpP?>n?^^aYZaY{Rsv?7gtMi)xTDDh~SC4*$w)KX`MNEk~sg*bx z#J6ofRIvf(3kT{07HPO5t@@-_8-*AYWHF^RD=Ss1j%qI+vPp)K|K&UNm&*e&h-zi~ z&;x;Ad4DM)bhSvT2iofo+8$kzq@ylbQD3@6C*U-M0M}&X^!Gg9+S@*YtCmbPq;*sl zS?AgoqZglUPEu%fE`wL~r+-CjQk z$1H5zGj+kmR~%S$>CPvqJA_B^+7!#!X7()aKC!h>J?gD1ZHZ}x0?YVo#j?Kw1k z^OYBD+`nkg(fQY{nU`bz)^+tPs`EtA&0)QV)egEUrxWB9if}F~boK=L^{e=?HfKfB zwP|kzP;?EltSVP)*K!q;0klyIKLbmw0itU$c-5-uiY8&afC+&p9RAs z4(k3knWLc}cBXiZBI2#A>@a~~CxquYB;>k*Ql z-`r#AA2e zYo&S4_B1V3r^DQ;&N=1k)#)M+Kl=zI{#S&T4Yk)YJ`|pas{f59==`C;8xRvsc!NiK z4#AJr0@lPS__Fw6oTHKkn6UiX9kK9ATlcbfMFwA^_voJ4h(y7hdh5lb;t;f=JJ$jZ zoYJhS;RqhdaC#M@p1x_RLy8bkQ~B@e{_1}*W?Nl0^KYbL~0d} zo&14fEc7IxGc%)f0d!m@#f4G8NEG#6(MpXJhRKUq`3gz)R* zS?%dCpo>Ys{gr+urvI6;&mq34{q;!)D3XbX(}aQf(>K(CprE$*Ke60fV(JG(MYX5; zt-f^IZe98b18#xb5+rh#-Sd2=y@P{ePt5(EhOoee+rQk#fUvb)cRjhYO!1YwN*j9n z!IF14SfIjbx(n3a*A~3emWsSZtEKt}Gm)ULUt@%vH-F8I(^Ct#Joe2u%LkZdXQsDr zeDKKrotNMGlQ*?LKQ{sG)wkJq3_11_0-XqI74=kR-~7%0_A9^h>(Ac*rw4XyK6&iOxfQE7EKVFfboAh!ZBKvqk?($e<9Gk~iHGms zzM=jE^iR;D9e=f0HQ(4@}Y2a*8{8)SD)CdqUdspT3Os(d0 zkf%jBe{jtpaO6Z0Bwe<#hDiXNfwrhXh-eW|%z0>_Fo3*xaJ1rpbHmzt=oQC&ECg)rmiH`ALOSw*awStTFtLqRLD$RYE^@+yTbpbCny4pgkM)>h33oltd>N=oKOxSAU!roT% zcypIIWnX*Wjm#+cc0OblzP3FX#KI*j>hIar{JwgW002{QEsSso0O>#$zZB7)L#NgE zn!pivj2Gv!$~5E*={G=BLt1YiZVxGg2L8lVmA6o&#R6Zf#U1%N)@`B9X`pD(x_nhV zji@UWv(dWk;(E571p(J--@;}G62RxaUyWiXM;tSeq(c%RMJEQ26d%}Dz8EJ~F2*S+ zfjkh0lNM<&*AOG=3i}*oGE@Zf!v31tRK*}>2{g0mz&a>{s?afuw#8jGu#XlZb|6~a z%LT0idoA>;FgNe3Y#E%Wk+F|Jpb3v|FGqkm; zmmQvl=mYc+XRz?P^erRZ1;YF^99htpweNtQ(pQ*Wx62@GvQ;T&X9jbNor8!G+i0Q9N*LYTfxSSsq$Lgift}pVD+u=7UH^rmL8_gQ0q=(0#ra zCp(fbNKz6bkO|n#(X~JoF}iu2@Jky@3^Qp#KvINB9geq8DN#>6-`O(y1d^1_&A$vR z{CEV(89Uk~Dn?l7>90I~%{cE=l~0W#)TS*UjNf{=vNtlI7AGwtkQSNj@qhcVN@UMp zGIHnk;GXB}%`f)J3rqudHx(f*UQqeg;`T8q`CMk=PuyPJNH{W;{j;+i50ZnrL8UQA%2h}Hvl zw#TU=%oGcEwokuPgnU7Rd<8#Fwh(%NWY$e|QT>+N5?|Ls){i)iO;;8^3)-@9LG__q zrk}`5i|sKbjU`#_ayZ7zpuj>U&24RnK?H%fUs|_GtCmz1X6@U7a{|PawmiM7?kk*l z55HJd;`vt()tVz=G$Vr1Qi`Bl~vDp8KrTn++$A9oe(xsTZEO=b7*P(bM1f?QI+GKfL*Y zx2~JI^|JFWzF^tDWAj~N?x&&#^r$r~AX zWXiy@W&hN>uc{UHw_jGAtdBpiwP?eF{AYb@nn8?~f%@a^tww{JVutz*G8v2Ll1wDL z;;4-&bc6*jJhk|OsUfjNg=7DbYe&YHEUeixBfQ2U%$wWWvp>K86aM+v|BW9iG8i)%0xrSzA?#Igae(=itU5#PwG<)smALjWzNw?5~G_cp;Nx zYF|z-tXW3nXs{RCKlobFsw$qTdf*yeud(aomg%vXm8{6nvLToi24;oSehmUK#EyMG z*;v@Lw~UyYt23M5fBR^i4Cr@XS)H$=oJ)>)HKFBQWEjx|Ug&C9T8Amc!pB@Vui|6` zpS(_6zNm~)Ay&t+kCUx8`Vm*e%-K$u;`M7r0+928I~{V&0gfqef z!bvTJA4ze-hpw%?V(i2$ry!mnT8h022%}R4syNC*b7=i67ebSi+I``f3InJ(G^|CJ z03vIk2pf(RB8oN{OPr=ET5eTzW_mJRmn!lRn2QKF&>P1_f^<7Ld3x&zM5cS}mcy|j z(%6Zr3FzlAd(|n$&Jtbslory%a5gT60zahHE9hs@E5&Q+rE~bv;M_N`ULQ=H>t{ao z)t+IPd(}8ggNaR6G#9TZs@}$7V~v$(HqxsQhOBO(YjuI4*$E6~qL?L^3Hj4mh*qWw zG$LYX=u=2X?AYRBxD64p7?0#oJ{_Lg&V(UtBCJWV&EjEZmQ8IG!Q32y4db6X!Uy)# zoSIYH^HzYvZ1@rAVjbwzMkT)f@qJ~VuLvZ(N>G}BoGAoT5`zBrqU@hAF#){qno$aM zLHl#{B=J8X(T}_KyJF4#Efph{X)I0&(Bd=+Rstqtqf+umr#^U1tx_fFk$6!j1I^4R zGr(bl4A;vSmzMmJN3}wVU&Wg*svkeAZ`&#rJ}3g?isUspc0jg~RQL~{95IvEx^+1^ zT}~BcqAZ67#h7IWAFbg&RhZPQ8`*ZS zgsZk4Uvz>b@b4d=`qht>ZmQVXb+`mReQyaA8lP)bIT@-;#}Gie)cu}=sK;;!IEW6Y z-gEP_cKOHxCTKo)(b6qfubNuZ*0TVU%*Cz9h;9kLdf2(tR%rvZ!_Do=Q@iTqBtVQW z%q|SFUbmn)_*^Vrv}o~_cOTrfW#^_xW@l$cLT_x$&P?xo_K_popT702@3{Pu3pE6C zP||OR{zk2$-g|8ONMHzeW%*v7&hJK|9#GVQu=~*T;l0~;KKt;FhyLXLFaPd$f9HSr z-tYa-PkiOKwm$x?z1yCtzacpD=IFX}I0H@|KYHx&{#~0M+w{mi-~7{mfB$#Bv2ok} zZ3kv=xoY+P6N~m7t?y*z!U21m@u#17kiBA}zh-s)ptWgV#kpc}Eo_NUKdVaXbi0-!4BeR^-ji>)w5~o_>Vrx7#Xmzg@p~w*+@;HqVzDkVc_J9xm#y#yj^i7vy!ibo0vMi4i zNIhV$uhzG-k!4 z(EgB1pw_($+U2RrXvX>@{TK z4`+3{8|)lJ*X^9Sn9Dq+*bqIiSE}1phGM6*GZ$wm_9Mfq4(5&4kRN0Gm@Hr!oBnLi zmiq!00FqQzOwYy<&alot7iBT+-#c5zk7 zV$0^ddOTjd*6;s!wLISaM)Ai7j-xbo z<nBw86`m>2`*j3-B-+j3Fm#-{G)ba&-23aVv*5_30F?C38;ZlV@ zZ(+TQw|QR~gc57LL>NJaN4AU=)dTI;3*n|qsv1*N5;^8iwf8J+5ElWhz-d5Clk1*y zT+3I!O(SMELM|d^d5Hio_-mGzSlqFUle& z&~9a`A>$J=4auK4ARiDA?OWC@z2wHbpMLz|ecPTHRlMObJ3I5-#-|^-_bXRldBw~H z>*vj{7rsD?a!?E%?o`tQdKDrtgsw;rr64yF7CR}4O_3#k}PkTeUV*NbO#tM>g#~Q`I+g76>?h+Ua z2m;26aL~uvv(k!&P?F{p5DA+fZ9nR0a%^HE;FImw1yNxhXzNETt2L&n;y0YEh%wHT z+TVJ(v>tx3s)&i-P;cI0XZhk<|YFo|0VHrme&R<*uwc0>O;pYLpnnzlb1!?@pNf_FnUP?z? zxoYY+A1dLuw}}-m-DOtgsKgxE_5hl&1)w3_{|u7e;7yXqA^1 z7(_jUs@pE9zZFOreWd+iK55ts`zzlwb?$0|Gu<-PmXnB)>21uGqCLML+H_6`RcHoH zpx0b5l8hY(B0Yay9vK^VCV9o6n_a)UG8R>m$<)R#Qv^!AnGM|$ItH`BuoeAN>zdgS zJ3&++N9WFVz6CKtGNxczyKCshAzrdGZa#G4%orN{`lgyg&x{DqwX6vf9w$Pbs{*HF z&czwbzFrubJ>Bj3YsU{G@>hW$QXjsd@ME%o^?DtrES_g4fv;v1tbFU-D9w9*}vM#fbsQOlJ5)<75=^lW?#< z?D;ekXUGgPzdpLXKAONq1hPe5s+wPq*~~uCCKV$V<3VChsK4I+JdpIO60eqp#JC6* z(~3=di^=Aywx~f~ZSt+;6J&H_awB`zK5|`gqNTQbw1;#QVfJP1*RQE>l*;6Qu&RIX z)k^4tsHd;e$+ulx2`k_{^y1XbYe!j5+$A=g#Kt}gkSer3DdF)-lf08J(Ix;!@Y4hK zKY7*Y(;MU~3vXHwn~LwhrW(R{o=)O4iGI30!kAi$mZR&#KY4m|C&MGx>4$vVK2F(* z7EU_$%Ny&ryANGkuUgb+O~#qD{gLrc?#v2C7_8yg!&}$Yi-EUXR4V5!DrWsc+un>Q z7p+CvJDPhN{mthpB;G&&Ox@rG5zsX8xLYG2r<8H#%yG+9AINCwqo_*01@M7u>SM+U zwQT0q%Vxa)=|-Kfa%njw<3KA?%*qbq_7E?6h7ll{H6!3!ALazJkwH^;FC@KoM+b`O zUR6A@NtbXAq9R&e9t#ZiqlQk4D8@3VwZ(4)B3~y-J9+Y_!O^>q)El6B48-8p&M1f9 zPe?P!w>Q_Hg^J}c69dlSc`Gl!T^p9JU4QHP zi}%)_ldDDr+MacUA#x_zBB^6>^gy&qW#{x&%MLyD*h72o`#1mopZ!nY_|3onyzI(ym$MFqlac^X6i2O9R7U3;dan>ZhG{oumASO`~KwU{$1y-Tzk=)3-+H_u;)mP z3jKV`$Pnc0axU5Dk~fLx98*N70w*UEEG|a& ze6;!9+~*P{{K?zf9NoG|QSX^qK5={5)L2Dx9L}fjE$^7?SJyCpa;9p9p;2TKlHkpE zCR4_>4lz1F6>e1}hU6PzYB*m4c-y*~bl6u*=82uvftkVx_oLv5#0VhB$DkzkfBa@i zifYZWY8pvLB;IvV4(oyA6+b%+rh7}b73qf12_&mOn@5N{Ad{qp7^b3TetlEDM~{o{^|^N8q&E9tZn5t-?<*ol6NvF1FDZ7d2y8u2AGPR6s9h#dr06oqCzWBs zduYt==yZr;*M*l|y@H{;Atp>giaQTiw66kl9nB5+l!(|V<2+sPFyz0`K6Uy^aR%dW zG`v~jd@VTl3gHjnbzHrAT`>15?exUinWEZ0c6UfwhMZHXpBZN_JmDQZbaw_jA~zNf zTMD7W%-`^Od`G=f)MY{pRn8tmU-<=9@N;`_u5pT$V71IInM7og5DW>KS^oMy*k8VK zG$EPE`rMo_a*QI0!>rygm*=;ypxd#L8AhAEnJL1^MS1?Zld2JDJ?|gf7 zbkR~4rif9|;bpqVi?hx>$m{E-J2tyg%Q)?gBuPGYyVEdUjnVh0c9`@1e>JI`=&s0_Y z47;@mt{G2+)voqM z>c}P~6%U)CmoBVV$;jZ?5b2O>R#%IdDrQ?IYh6M8Z(S1fUN!L@+>$*>tjf zEL2+Dz9v&LEw=F4R3y3mP(_j(HdW+jW-Hm<&^jS@F!M(M0h2sb^T+QY3XFQrr2w;^ zRCQ?Dvt}u>5O?gj?a3`x9rv~*L`JRKj5MSh8l`{LvpS>P<_-14Op;&+fys-`z4hef;S0BM0_w z{r*?J{N-=|@4xotfAtT){h9yg!{7Yuj%Ob^et7@vxfQE(n3_7Wf7j-Rzp~+;-+l49 z4TlaM_~|Q-9Q#2GrI4*#^TBDY7jDXWOkz^y8n`E^a1lX}Rw8jfMBT2@n8k*WK z2M`&$n4BVvJS0VKf}n_>Cu-?_V*L5@7$ig97@OGAA(>Nt{^pu$K$U=@|<* ztf@X!%T#N6;q8~zABQ1N{asf~J+05%>->%~3(OLxt9E_+!g}~=DalcgEpw>amN}4j zQstQ@4G9ob_}#p}zATvrE}CC^G%x8cdvt{Wt@WZde(Sl>I$D6~=tz>;PYe;} z=q+nUoQT=J@JyXcfg+G3ke{HUm?}g#Ne}7~t!5!i+p~M7?rlkC(2mZxTq!&v7zvU;)E&m zgaFLtygKmZ>diLSW+FLDUlAS#A0n1Jf3D4omXC;`?pb0&1?>)e;>QrfJ+WaJa~&y~ znQZRxdlfOdyV?&Co!Mua;JLk{s}}nVy@@%qW=}zeAPI8Nwe9Dx2*hR-5!H}3 z-}g^b^(q<0&}K3Zz%=P5A0pf`oFZ_fBM4cYTqFTANQ+F-dq$6TOWDqkzdzxF^F5j*5%gc_ABL=_{vKm^h>?qtR|iJU23!V#P9qOM3cM(|6Eo%a5q z-Z*k%6zxqHP5qmPUrKsN{>ulamM>vc3Bs+(L3s~L0Ug0!$KYY|rnX`tY@*K7l&)LTs+p=7uW4C_2`cuBbl|NM5w!uMW(I zMaEfs`k&b5eWlB6ZUyn(uo2sNWa{oKMl?Pd{pRRI_tuN6yVS;bVt|je2c`&%zoKm? zXMKLFT|s}^-UoEl(h3>Xy!Is>&9N7c?U(N!oe5N`)5Z%f%(fQS6K9O)T^JqyrS@4_ zwTcwEix|a#dcYtt!p@n+3r2sbQ<2J(hc8)u3~96xe!<73}_;H!Ud z{P2Od-*WAd`KyM%AZgvP_UwmG!z7r%{h&yioibZ>?3540NBmyEtfC3+C0d zVzf7usmkPKQ)@5}oh2J8;c#IJxNuopATa4HE-k}Qn>@(IaWxS3bMoARh6jMzb&$P9+~wzQPTJkR^VYs;+J z!9;)`83w5%e)X$mAMJm-xAF)5HgEKXK7pF!+FV~mvs_JR%aoU|s`n_-S?0snicxPj zs!V8mig7llLDbdYagK_W3DQv{Pj&AIxsdCyd?s4YN7hIz-j>XWB; zy`*9iHxZMs#j-YeNq91uvM=~<^TbSjKtNQg1orrj$|_lVc29MSo>avwjnn_ODZe7;2x5Uj5ODahcJcChGcoq5f=!z4FdiuP}zVTc=Sx+mC8lsDJn-ktsG>dL(f8P4cIHfM+7t@Zc zmQDzt4nr|vIPRwmBM0SWF(EVT$ybNhsD2KwFU~Ds{Sa}wtL1gP27Z*66JLFsi_Vi%p<2Z0Q)6T+c8Ct|1E+a@qHHKYTaxudx`U?_<&$Mng#@4vLMew^v(Dw(Ld*!^2; z)J_qGV&}CVC=@e?Aq??m%|sPRnMa@ALSPPEbjZwZx~Nte@NH3#aIy_sHKgS_XpoD@ z9^y`wWWpxe+}QU-2Pe(X;gaGco5UgCqhbdH9IdnnlRNgq$LSL`S{0~GNRhE;R?PC7 zBydsu%?B$KbZNEU&2sGAw~NPZZK~`!V0vg0}_Af|oU9Dltcrqm|O zj3Y5z(>@fl^VjDo;) zAnJIvHKv3lvm{U8;ED2r!|cb};$!_)rsmZB4B-l9L;Z~DDpQ-VlQP;;OeY^YQOkc1 zZmYj>zI;&`jQp}i_53^|`I}aCOLKJeOcpP^Q}5O)n6~QonMYpwBY-y6+!X0jD9-A- zZNn5TMZKZ-6mw`+x6ku4qN5U?s#q2txF^Yv;fdcy9_>zvN9UxgfP8$>%7H^O?#4#s z(Te(K9v$_nEtwxNn<74eoaFZW{_@>*)buTZ>bGX$eOFh6;p51wB9L2C1R3X{kP>qg zJHd-PA2%}y!kVUgW8bD}A6j3xJyBsZAqhsUrXgYq;Z%tjQ?1GKb}YQ|eRtjZ(0$)J zdZ68Sy@@b8d+hN37asrCqxXMf-n@C|uf2TH()MTM+py{WDlNTi@!ki%^@V$W^S}L* zfBtKae*RxP^o4)D@w~Q4I;T+D4*_r7>d$!&4mCxQpHq-Pd?*J zzvLsSmM|!iF?zW9%%W}8uCCi`0!B{LME;T>BX^Czrbos0{QkPrGjXVL+qq#^$-6nv zSD)^EzJ?d`pb_2(W^~F>$+E0Q9`eO!YQVp7ZQW*P__DufdELENRP9$HOfXinU|<)F zh8x6qPnAZJgtTJu!kV@+q*q(UAAD3RPJ~He>B6$SyUlCbsDDyxPeA1Ldff%}qI$V# z_A!AZA}M8cOY@?QCui$nE>&8@&dzp>jL8HvImd`t)NMl2Z_MVmA1;i^Z@sjR*P;bA zS0?zVhyh>sra6j;uWhQ;76F&c_@k2olL|U^Vk7hRcCh-nAq--nxn{?VoLZZaA1zK| z%;K0&FJ3YF=ymeO`UNFDVmbzjveu=Sp+a>j&mW1k~ z`Nmc|GgmAwQ*_DXK+{soMVMsjAhzZ}f+15gTC45ys#1%ere#u26(Us-U3~j&OWLPb zQX5WWU~et zcqlg9_u;-ro_&g;hZxp&D4Jk~$UHg(nS6FJ@2;DBq9TAksYm6~_QBU)?6j;X3s?~Q z_-1r_P>g(cjc?|Y-kGFvG84yCT7Bqv(Zy^NA%ScH>vS>|TacI)9Zl&k#bhV>T`R&7 zh<8xSmtu5>Jwre?ysQ0~Y~n0yZ4E2G;!1szKgs`sJaW&A+MzXj%nG9s(P9dLLs+&X zBaGi}Q2zAbwn_`Qre#k+?)9!KiilR?alGtpHM_SjcgAe&W5U{H^?3`4GW)pNLhHZ% zQl;RlT9&{?vO2Z)ybCG12+-cYc1QiyyyCP>_XF3|8XR{NfBEjROsjV$vp94yb04Bg z)7Y#eZ)s`qMP1C0HYN*aT;%)OdSCTS`w5!-b!(>H*=~>&NtCsIadOUkC(2G+Qi_g|kE0FPFOd~QR19)8E+QNUa$9v0X^Q=!-wnApE#U9G6*P>Lk! zyINXNBwTiKSljQw5Rc-wXR|u0-T$ys2WY&6MA$|+3y6sAhib8m(6^tTnm@l>6lSY7 zr?f3+GG7(v_-!2o4w@14HLL5($~jv;zo8yiEV1_Cn8gdGK73t8&gNw+M;$KIpS-Hp z&hpIEPhMR#FYX82HUQx|1nu%+O4Z%%Q)LXw2;({joyUZvRsdgUpJGS2JoBzZEP#Gs zGiLE^#O%di?Q&%;o44ZP^|Pz5f8nuj96QwBPJB~gc4qp(&MliB_`;s2zHxB>-lgYX zc*~U+>^)X*P-y9neMcX>=ZoL{?f=)a-~0TrL;H>&IWRMQ?nCV7@G9Zt*}3((XTJA^ zec%7~1KVcbcH3JPEL`MTyRd!U$Q7DPEO61`7%eYq4?AbIB11>6K>~DV+b;_B3R3$k z5UNhjOuh4p5>Si^7zDb_=tek~Hl|tCXx4{?jCBUR!n`7kIluD$w%XDJV*Jia+geh4 zh^bbx@<$H$x2GH$TSJInCV>#CL(^^7yQQ)SBNmm&J6t{<)J817g@5ECBn83|1; z^%j4dQv{a5HhW@J9(u8=_%hagy8ZA|(?R`9A~7=&r+$DwW!cA+UgG78&x{`bIdHt%+y5>hv4!(X(KJn$uV|rp6oZXr zMRvGT1?cGJ{DkVy_9@ax3Ru<6?Q>T^c<15DRYG$|m!vJAl49~hwR1rxKeFZ=u!Vr^ zLxk>WHsH{`V%6y3eWB`*0KZ!URdGEG08=L<;0Q;nzg*cN0q9Fq5wnk}E*2(a)pdeD zR5>LG^h0obx^msn5L5O^dxA~`6UC1BeMN}ME7H=9r<3m|(uB+_jp}uTA@OCszwY6^ ziUV;D!MO#jAHa|DOnU9USm>iU46O~{ls+2_bteLwSiO2hVJGLvBLfp!6LwNOd*gk$ zpOBxh+2n(dDHY z&$sVE;t>1tvp+BkX3qj5G$0ZH(95VPSR0#(tHJ=}b8eK$cdc@H~F7mofE6s?b3 zJ9169YTK74q(RazsiA}wdJ59pTMIqopCS`B`;Tm|L|HLGvT(;GRkdo#D1#KCV%#ys zJ~?TnBJaYJygIcAjJBgK!9S+ z-}r9*5rDT|I?A3;&erXg04T;j*J2Jws~^}_=KzDdkJLTY_qJ8H*CA*tP+U_NDqnd? zBsX0UcUb_6fG{vw<|qUXJLT*0dZ6I8OQ!yVTPhtV0D{?{uez)reS(B>He*mNxF4e) zUoj;r1tU^*U|UGSl&{$5wkI#!BIvGLQSav>-hEZw03gU~K3!u7S|lVFw+}Z7@}A9Q zN0&O>oQfg6XNxZ4`Zc5RFmIjcPm+w-$R|$S@{Q-}p#R)k>cEl51X|!mIu9j99nu!X zy+`NGUU2QIOV{st>g$IN9;muE8)i$6XP=TA02_uLhit-JEFOFsY1f{bQ)5!jV= z{AB$#RTtND?XQ&N8bo9eo}I0q4RUq6RPhxdi|p>(iRI#D(14cJtAF_M5@w27wJ~cP zVCTR4si}Ydz=)4ST~%3VKS)qt-KLK|kWvZwC9K$Og26oB^nr}q8Esf2iMX+ioJK`9 z34FADpW9$!2>R8pjz(ac*2OZhJcVkos`j=9W*Ee=)uc0Tw3Y{|IQ{g!)%>hsr6IJy zBx0s3>+M$u%#lD_oi|ig+uJw1NS0U|1Brd=?$*pUAyQPuHICY)i%LCR*QYI@jb_$X zbGlYvU(drL*eOS?TM(t&g4Zn}FhP5%F#D~|wa7^8@bOx?sDQ__E&QYJ){i$7O@)M2 zrMm>cuaM#nM?X8+giSb)nIssX+Zdxh$FEh7dG)s!zwk^2R+4QBa3)Y$vY_73uQi}? zR?NN=n9`k>)rUS6-+ozrxUJeQ+SWfTjM_VxhH7gEDDle=PfS;Bzz*il!=qIg{ImuB z)zKNt13sr(Gz5Wb#z#LGS_Y?Rx&?;DL9tuzNcI)bP1s#MEebk6 zKquNE^UNTef$j{Lh`H0@Pt;BzwPz6MIJ3-|y#!|?b2fNgjJ#6(nK=FNz?*&n>&z#v zAvj;cJ4^g1&!pGxi-pq}>8n@fn`LbQcE4k}xUP@OfZj)FIzHkcVj(3$~cudu-~K_E!`Wxu$7v zGK?N=2>YH%pb2Nv#f*G>rPj$Hh01&x;Y`(tt3R@R>ge>? zLi=lXgyY&C6iI?KF?@-OJWM=gu;uh}P=sye(z?MQMnyjp#_YwYh^i9A_Oe)-%&C4L zkXq0*Ubq%jh~Cv?KCr%>C+&}3i&=-nOZCRlTC1YMw_;_bN2k8CrG18@<-|C=Q_pX_ zz2RzUv7;^GBw5?|plHYwJ1YkApehGiare>6JF}>S|J1G`Y;G;=H?$jr`oP(^52>Iv z6OprBXC_*Fc7EyBDmq5I>lwEn_HKSOlNJ@h96MPHE^d>kuo-ozJE)^2Oht!KAzW4M z*)9gVyrSa{?hVqY^n9LA#1X#(l)klR`OZs64^wFG1?|vNvDZvpN5##KPv9Y6A>T*HFd zndxH(_ilUU;b-ps!zaJ;TTg%EcOLxuAMV`r=*eU48a{_}c>UmH+y2ah-~8-vJ-&JW zop;{3<;b%2t4GfznT`!3t}ho_29O|xtI}YiO3~(e#BmwoxN=>LVTLfop$!qMm%X(7 zWCVyL7&KhtoA#Cwpps#PcY8KpI8X~NaR-T~F8T2iIYqU!VwU3HeaC1b1GQup%&WHo zIePo0qp`8Pw8yN37bByX+gAl+JfqNy2WupMa%WkiWrGCs;cG|VsN#;1yW4ZnCJP*M zR$mR=+a@6Mg<5l8Itoq82u2`bh~JPd0J9k3ng!Zdobz{b6^V(H?!zzE;Ebclh*{=( z1MPW+|{(W}1rOs!kt+cTS9Rg8CW#*3;>2XuiL zerPs(NQm+^4lI@t-EW{3pqOq9=(>ItS%_qpKOK4uRE9w+La%aAq%ny8U0rkn>p+Wk z=zW677T}~sXMoz?v>{@x+dL)0AG(<9@szqVulx`FpDu?N}u5f!NeQwNg)a=J`o5wfOoW2Ul)KJqc^nS*}l7wBYOAjrN35qEef5 z(A|E1XRbV>5$n#c)d$4WyX(uQcU)4g9S({Kl2iFs(VV*1 zBI%Y)*z7pR+y=xHT{3Kyqn3|;{QCMDNcC-7P)oxJY(F%L0V+s`QUv1^S@Uc0<@=+z zEMt&Z2u8Tm&m?X7M3v*Hih35K`D*s+mMr<7IImt^1gGxMva*wVGr|nos4rPq%(q=S zT2V^>*O6)+WlKl#YfrKV;FNo2Z*dYcE&Ci7GIzC80^I&#GUBF!%(Hvz8MKc%Ray)= z8>51VeZuyAb2;Dpd=Wy@OZEcjdpE=Jo+2?syQ7=;mK$%l__C{>xc_S>jt>3~!kZAY zGczZS9X@(+&xxal&?D|SoWtvl=@ZAdJoe2eAGqg3cV4$-_2pCZ=NlEj@@!pSHgL^m zz*PLjTk0jRU58)#yJ_p%Uz0%eF~Q~kckUd$QjPEW1=su6H`ODqMr^dfL~XzJ<0#iY z^<3a!e1Jvc-y~G!l7||L+H8empvy!+OOZkUh|$XMizc8*rPd(YgJbrB{M0*&sY-K< z+ckN$@npr~8ij6HQj$%gs2F5<-_g-G$oaKrhuP|u%z$ks%6x~GNQ6kE zh-yNMwV?O1I*_SHOYdwiQyWwzB$=-Z?Jd0TY?=D#wKZ`|3{%{Ds_0|w{wJN|vko~fGe#t(&Ylw+qWv~K^gl3`z0t70%Q z+i*-YWwY6bN48I0-TvNAQ0HNbQ&$L#qV36@HQmQ1BzTgkRdjR^kaZ}V<_NEj+Bp4- z0GrpWu9Ja_^3`0hP)B^6Dr2fj*n)|eeL$>p96@JYy~H|B9t0L6_BnYFx@B)WQBySp zJ0i)!Qfo^{kqi+!)<7{b*@J7`-BSckOjl+lj6^bQ)R3640KI5)n0iBG-50jp1G5fd zqay}Xde02e+aAm%fNuH?a~Mg-@FA~LA}U=m2Ity=ArpN{nF%CM8%mxs$a6S{v%$Gn z2uFCmuAr1OK5`m-? zXiQsHuVDA#QS(!^aDJ(~C$}Fug;)+=3bLgmBrAsTFIuU}2DDV+D}Mf&kr8uk#oT@f zjTj^bc?l$aiIL3Kirv2b^vvjRL@Qwsqo|l1D;9#Zl!cn(2R-wzAFA(6;rF%6{Dh^w zi^sYC^%qWEv2x^&ix?p$8;dUuk(bE7-?px5H@3VMvl8X@k|6nfNA~3G=+%H&Cc}b0 zNqP$Q0weZUZ!ac0I5s&}Ep@e1G@Y5$p$V3 zK}q3bJIcErR*U#dd+U&W@?6-?ThyF3XPQqY1{u*~zyCxDGxaeBTR(twO#?4qS%>mx zZmiW1w*eH@5Rh-gyh_4l2EhbJ4PwKJ}_t&(pQXowgq;tva|ziX+-|5s%`* zbXVq{O;uKfsK)>P_h6iPf;VVf0te`X~)`!M2-i;D|2cnB2vtF(=n}pq3r`Vz$xJ zm~4ab6QwB-XK^jG1ZTpMQMl%MP z;-v<#wthsIz+2bV82HiaY7pbmkVrDYGa|6j0M#Z_;Sq>sG#N&8h2kCSYCK1fCmdaN z;>h?(42M>Xi==*dm^XTf57Z{r>1J`ntc}0+wfZGP0GSB5P=zK#ARz_mkmc=b_cvcO z_3kU%Q$_7T>Csf!y42p}b=T$fE_nse4pa5p_?5%=wLdjm##;d7xkIGW;I|*E)fqMw zweBz7R0J=4nN7sd4|)VND(Ye=BP~PdvoqoN0>WsA<87DL6w~^q1c0#Gw1O|VVNKDo z{NT3Ab4cp^IL#^*{^8@rCxiLUD{93pz93Is0ge+aVWVR~pQD|1IgXe%{pRp#DULkB zWa=*9*&kfbs$bDs^BoB4iJ3eBdk9fIHb{3P6Ci991GUg=JGxd*Ed)+tfV{avz?N$P zBHw*w|M-qt;f3(Sq&t*ROjn2R6bj-OhU-e<1c_nDIWq{S2^2fg{lfF@qg@@HKS8HY zpc{NGFa#}a2^(->RO~;icaN_H!^qF!&kxQmV2yCfYdCn>Yhx&IHdoOZ44#ep5I=(J zwhv@yhA{f@eaevLhC06w*bQnUGZaHh>?Q)}0*e>cqy7_5r4Ah(JvsX25GDf1Uy7NF z$k@Mdpgs|uFXIHU1|l~bC6xpXH~G}QDQ3dv4kyeInA!JC!o;AM)~#>zJ*Z7a(q)3Y z+JJAH+fg}8*{cl*FcoyO-6kqB^=7v25|3IgByd4ot4bzBKrx%N=|B6)s!BqDDEW3@@x^B~D;?FljotA^j*ei*;>{A%5& z+TTP+u*v?fes%PH-)c@v_o?uxOA~+%onZbWzyJXM^hrcPRKMX^oc7({^s_x9GBzB# zOBPfZ!p@NswTeWhj&J)K?#*i}7-DL} zwAJa+UK)+-e|%s4O@Ktga@L;2peHgHZ=jsYM&QX{CW8NzA$eCj+&;Ak#&(=FhKZ-mhL&@*2){VP0iH4x+g}Mo5<8 z_$vWs_Jnb;eX5*6(}CP9-qS9=%sBQO`5)ctqEM-sF*94*2&32eo(~66fpZa z1UUFQ0aTSt>wLAGrf9F2{!iG>3dO8^MbWf=@roh{h(Sh?PKcK{jW0+Qgp;bJi|TR3 zFrx*oi-RXdy%D~B;*1Pa;dhllF=8+jz|5B7MQwSDKqP}yMQd2p^3W-c_^rD-Iv)_7 zJ{u&eVK7m*&q>u^xd?{FhjhAkXy?^|1tCdUhv=(UuN}Gj8%F-RW7u8i@P@*hW&z6; z{-ZgE=6ZQqzppf~Gt0lCtK|%mXK&}sl6{`&IGy}lW+d$mVP`WAPFRTgaN-F%Eki;} zHHM0zBa`)}U?>9OtZK@<^MbpH#Ly|I?o z5tb1m7zA2Dyq8wX-kdO5yGD#+`&(gl15!u-_Z3tB^m`?&Xn%LRO%f`tcvno%5Js$CT2Gm|MsRHDXM{eFJUX$tv%PD)_LM(< zkNMVhQy;vh9Ep=;g0#U$ud9>%^UsXjsmg)t_q{PLHhp!=#YHFQgtxGpG{)z&tR zqKyUyPggzUqRaYGuztLhJUd(OxETP|xidhb5fU2U9!BsvZ^>N5# ziIZS3aS{p3=!agc$C-_>A8W!|#2NuTX`i-h0%3&2EQ^ew4?l5Rz0&?OHF$|&%SH85ld3p6 zZ(&uT%Vx8j#)2dvx|k3e*fCYzd0GALh)_(GMx~9}=NC#eUpWfBO3YluV}c|J#1N0Q zN0TBMVA|9=Op%E^FuQ2M$haz6Wa`+BPsLnWkyi{e^5Gyhx4*a0%|>9|`PDOM1`(-^ zSjUi#@t_mrv*Fkfy-^VFo$ki%C#-cEdado`O#20N(4w=1Ydo!sWqSuD%^W_}1$s4z z;_)+3bvw-!(J4eq3 z#^_T9kn{TtSK6z<>t*Wf?YvTtuhg>rQ045qYbZ8ByS2^-&Q3Ypb1|1WG15(hfyU_* z4`hC&#S~)m4?cO@LF<~q(?%>Lq|6p64$VR)?>-sBuoj*86HmPNi9C`K$Y?p&nqdk_DHVX(dm7If05*-^TvX&eO-2=E|UDZ|&H~y>1zuvwu3dw0ANed8| z^+%2lkLFRaF>-%<7eH~uY0o;vel=lrtpg?pl1l+!iBR&I@J!6_i2vy^QpD zia&n6G;JhZDQunqYOb}^r7igrKx|U{bo-;unfn>BPmtQejs!G0w5s#~PzSoC#bD2D z;<0BxNggphkXB1-?QMj)U^dZ~qMmH8ImAm0LORn;)E_=o0*BilDAWX}-~C&wmhjzL zdbAeBWP24}YqGSMSt)Yb>n4Qcn)aBO+Y*6Qi(0g~PtbO>q$Ho3s&=)zju;d}W#p}& zt8?1&@h&$NYS=Q zF;nienh_iX7g9I3E^C(8p9$N*_3B!4ltW}8%<1>OtJ@jddRU_NF!v1PLjyQ85 z& zw-IeQukcfwJui%Rl`Z>6UG&I{wGYi#bb)IG=1<))f8+eO{QUcF{qDW@9Nym=d=BSu z4ljq<+1Z_2zQ5^vpa0a)zw7XlOL1IKF0{8^T7=DIXyh?`x!nrKxB_u{nWEh?%I_OM}BiLs1Nh6OPg z7BW<_tf~h8%Zp>o$==XGov?kznH$>6Pz&bO_>a$22AK?!ER@Uf`26&)+AmvNGQaZv zR@;7`$b>@6e8;t|T(l_$ZT6{Q@MvWwj1luc7T7nlL{hFL$1prX7LUwUZ@;Ww3H-$E zHKIysE@hxCkrSmQe`CX%h?4&6Z$nyxV7bf?gFGVXu}2js%uW zhD?I7S9Rs8(IW4SYwI(t0>GzhFGY_{P>jtWVmR62E8e57$T*GwE?QoTP#YT`2nc4_ ztAqg^#Te`yAOkr=!U+*yL8rq%aO?*Xe#~q^w-c?-hZHBQC93YUt7i}LmVU1h-ELt5 zh7r975yIeikmMAJBrh=rCtz5{hEHgc(FwE$nSc zOv1?AR^R9uwFa_N682KDREfCAad*6@+{UaX6 z*SBtrFL7!tv-YLfei=@}?FNQS5gOP!io2sHNRAo;xJ*BE7$0wUT zzS|qOF~rul`y%evw8be#*q765;Dg&L!K(JQ*9LV$-4r#Nab3!0weCm-{1aEyDSiHu zYSi8BkuH4xk4+bc*(+Ao^@8cv>*MWYOZ+z0-ny=yD--|~9B{&GNEe7}0u_H-_Ts%; zz_;f`JgN|!c2j_3i)8vC0vQ3oQLYv>0e(Mr!~6%1UibF5z4eI)zjb7PyVpF2b2x{W z#q8{%JuiOkI}eaTGBmJ+17Tizetn0Iq|1D7d%fDF9pRvUT^L`y z8IYrh{)ww9!H6{%wXp(iW5G~Ber9&`Br??0)0Rc(V%~O1NoIh0bbATgcu~wKmqDAC zMeXZO<_Jal_iU~Qs|jZaB2O3$(J$<;Q7BN$qp=PLO?Tyzx?97nhP2N%`DYeLsG2WM zLvFkni0C$0AY{o`4WS)1wU*V|vk6)>a^~*#E9Znah z$3og}udBr%9$tXKDz-_yVk4O^cD3JK(y=yX8O-c}21TY+wbrzO`h}Wwb~k}{UO9Ry zmMOhKSo~brlV=13@b4d=TD4^AH@;h6zse6YvG%o1)tM9pvtjqte{f6v{sCpAIU*KQ zXy+0lL8gJ|fcM=kw>j9p`yM{OWJn@(c(uJX4O4_UvZ?B!X0Ph#$=Z_-GkFMMG`<M z;qgZh02in9g1P-r%?CpaB}2P&>q|vvgHyVj=;|Szs<|qlYVL@9I%YT_`uI3B-Z_~S zh5{1?P2uL1NDeIo!y-w^L5lY5B-IEEb5Vz(i_YA#?a)iRluK!DuBhqKJNKSN%wM zO?xM?r&h5~r6*Ok!|n3~FCLt_xBUUf*x@$;s7kNel8eMn?c3@Mu!(c^1(jRK|Nd|9 zt65mmqQcztl~=WnW)$k?PM zXQeLMgU9Rcf@P_sB8WL0zt2+m)>W3+mxXzy;y*z)cx>$!kxmp*Jy9xjd;*h213G6sDdTmyy zF^dV+)$R04kiZe3Q)G(qaLU)%0y=$?*vd!?I9s Gy3H!PW1)`<mhaj#v5F7RB!F3|(q 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 EWrMYtPr-)n)^3yTyU>)ynCL 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 Yae`7C`G8MZckvTC~SF~k`|s5 zq!}l>*#6+Ldfp)qwH0QSIG-)>u<_aXj#g30Vhb>5?c@^kM>bc9T&66k3Op`3{KMCc z-f;kD2cxN5Y>74Vag%x(g#Pvw_RB6DEnoGt@Hj}tB2QfIKUQay_=7p6&@|iHFL0%n zss-C7?2`EtCr=fzv!#asE3{d{xvak3h!);B(`tO6Fa>0b(T8`{=Y+UQ56TywKJ!oi zx4(PuCw}c4^s|Gr=geKU=F&I5`-4CFk-z))5B=@8zw5o1-2CHFTjW$$d=+p>03 zo5%FeeeEw#5}9xu7XNy6`nI!vYn+W&osGC#2Eh#T)IjJczx{C8;=JMFNEonXXz=aBO^d)1{@M zo8TeM%B#}M>au92))_rKkQz`GE?G}w(S`@=meHC(ZYA1Z7w){g7W4Ete&d1qwTgO0 zbTS%w=atp&nWNyc74=6c&A~({&bjlJmBF9xZf|eiWgd>_dEvS{fap*UX}&ZQaS3cG z8V9Q2d+H{en@F-yy3x|s!ixEsDb6_3y|DoiUTI$rBaZxU zE#khJ1iG_ET)SjM>4$RBBLRQ7WzW;U?Cz2N@Z?_@6z=RYM)#fu6aI#EHu`C!bA7xikI+;hoJqw|$i!c?49 z)wymt!O#I|w+$`ZUn(3*omE5U88Y{^Zjkhl(wUt*Jw%5@cX+5)V(Z@ec#a<4G2R&G9W0A zT~bLT{bx^&A|dIn9|U>g&XtQREd+^^VFMcaS=3AU(+BFaJ{V{dVv6U4L6m&;xmuYd zh;g$)Ppp=|h$fKTtuwUk5~mw;EyxT ziH0Q{K#w>z1p$Ij?i=wlgp(eUTH!72_sDVL=c>fb>#8eexs^Zgjy!Km3x#^_Q1OJH zlBne#Z&Ac3iH}`EwwWCCSp4Kg4?bsi4}@;3EcV(K5AiB#v&cydil2@b4R|EvGI!CU ziO+2-)3pp}YZ#FG%dNj%w6N`}ENN10FX^@o^{QPxj4-*1<7Z0Y60+>1nU#vm+Yc0` ztpOW7vRKC-Kd)-*#t+<7=Lm+O9Bz+YxWvXH@RRjI+h&q$QAas#&ZL5>y`%Lx5{qs8 zAKqLVs(RZe&+|81JcybVoDHb_3O=1K`Kc0k4wjDTA#%|b&{Emjn*)g14ohu>nZtKX zKC6|II`ju?26bYotXZ`S9~g>PG+6EY&ckI0bM)YGSxQ>l#Rul-l+b&T$S#=E9!R<8 z-~G-n|D6-Z+TY(_Rm_<)XVaC}zVRpi)*F82m+tzZ4?TauRclu)yl=dM8lmtMGf(;MEm?D}_eX5Ws-PtSZy{yPD)CQiThuAf-2aFIs*i5o_P z3(`fQ`v0DK>1l7C&)u87nmpG@d*$q^n2p zi*2X)f#-_3#zOmr#0wXW>~c)!ch}HaY{)Iilg;7m?^pn z+x~7*Gg#Xz+1zR)bck}7U*1tBa5_7bs3xwfitcuUZmy_rEVTPzcuXlht!A9}w0h25 zP3QWhbtiJkJ!cct9c|WPn$HUOuJ-OWPW4=qWXp0~v$BFBRYVV|9S279ZElx&GE0I3 zF|8g@=p4b_tvzZ4&RjZix*h?>PkEji1DBd9X-Jh)D@OXhON&C*!E^OcdRm~x;=lP? zJxHZ`KfR?cp<1(5J8jr#P6vz6h@4Duq?2$ccxF_&0Qo=$zo#eag+WrPB|M0c%^@pN zL6r0ai`xA)tBc~2X(4^yjO(1A{EK=B4&*8k{OOaUC%(H&(mkO{Q=+b+INNZWDw0d- zCG+d)`%h1Pjss*VOGlUOK*7*eIT%&;*{#RI@R_MD80MTilCCgdawqYuP*-wU3A&Qu zua&lvw`*Pg;%(H1>ex|aGtwYFy_YC<%>?WLxgjx$k=6nOd5t+XSLOdXU$DO zFg-|h!KvDYI;GM3bH8qQ{VqOa3q)P+j-8$aPJ!l_)Ku}Y%b+tuj}OK;SF|uLj>Ss< zj(bNg^ZRK1hK;DAL`B2!<@D)MS}x9~_Kng*!63vg*mtkOCK1gd#ZNw7A(3d~iGB5o zQ$E%klAD!RJOHY&AsN zCz6%taZ*JIUL6!xb+rXX#1moXXO%3Oq=}TFg~cx+;upLsASu>!l!tq2TjzHku0k#8i-Hi{u?S-}%zi$}Q(b3r7Ym_Yf+ z=7~RjqSm5RJ$;~_7r?BB@mOPU(uFh+RUQ4<0G(`*g5=o~V6<99XK1C46kBpN@#`-s zhS0Tgx)x4KLX3WASvRh!OXAjb^__dws8CgbZR>5{ z@i|LZUHt$2n?JVj;#;?Fef;2_XGViz2H&nYdHmSA&F|m1eoZxETdW%UkL;>Wh@$&t z#AWcJI3r@ifUalijp-7waarxrOJfM<+9jhed4o#>Her}F4cD%!%_$?oCFLkOm?Qnl zP~CZWG-N)tr5@g)R#t`{>)By(ymn(vMEj1F!p}eT!bqtdgu|n8&DEc}dE&Pos^QNK z`hV>DdPs^+2cFCTxdr_8!!=XMeRTJz)Q;4TTS@*Zqa5`7@N4SNPM@EA|Ced}%KM(H z>+XYxT9izT8aE`5 z42zk5mCkYCSpBgF<=Cl-TiOeKh|1_pIT1rKWl?XGP@bBkhi~W-MDg#O{;8dJdXCMpYPUSftqi4mr=ws-+S5wh7Oyha*qE4O*GrLzacoi|Tfi;6y z8815b!TC>=9WTn(cdy29RzXBzUwy%}-Pgx>a&Hc%@z9;P-^IgoF3vDSRSpQHM;O7v zxi47k3F2<~Ln-H6o(lqzA;Y=&hqeR|iM>MU{Nd?ENNAD>CiIAeKZ&GeojhHiX$QfF z=ria>F^C8w-b1crB{vajG;B|~9zNTf~# z+@eE?JKFjLaPmC1b{wqt@(BK?zgJ{ZEeIc z;M-5!Q0o(3{qjAvuat77Ct!wW+TR#sm)`hKoC8&j?V$;)#Jk&H<0+JjUqQ-5ThGvg zVqvhY{R9|dG#@`zUpyg24`NJj+dJ_q_tkFK_5-!3i>nT~iX+sPrrbJ{OVt*wX+E>H zD3&p-yZpJ^>p`tl0*85}N|!GvXTH*Y0Sa5T)$puofjEif&j zf5kgK@Hc+^KmMQQT>C@UZCu7^>WV)fx~UpH)wptTb&?DO&7_hQM}xweC9`I&yym07 z@S$gpEj_$*%Yps-XST7vZE@G7akk+y$9P58$XBj>per-(- z=As+c6!CQ%tI_q$@1J~%Q^#JK*W%g$N$1yj&@WzA>vIDngH(3{47aZz$ub>eWa(@l z-dr<)@%6_~)POb55SPnhW`LTA+N3A3x4jj(;(`*v!!DwG$I+AZN0A%X)TMz&?a4F6 z%t>s$2&_D{zn*xC>BH?=cupFICD`U132Hz8bbVoc&*bxH*RQEbX48uHoOLU;+W7bb zrQZz3J%Xkequv<#*gf?TdPKv-*~p@Y$mgG`=f{a-u1RC^F98M=6E5GjslIETLgnm0 z=Qg&CM;7}&msW!@a!xlEd#_aY*DnM6| zw_iSbaaC^GnqW=NtqE2wDrOW643*nLpfk0*6owr1bC^Q8F5~tnYcf{JTFuDKWm_r?OBQ_HFKeUpQsgp;AwvXnuA)um9zI_8nsmtQUIp2`3c}gXG{97I zQ-RL&6<`0o`B)!=ic9ae)I14v$NVtLTn*1s_bB)XnXi5C;qs-+((0>mThf& zg=3r~Vg-mOYJc11WkOcgU@0u|Ep0(4ad-QzKnX`Qh`c|yt&}*Vs~}>~SygYTF6VBA zhpu^|6g-h&E})IhKX`27)(sOYFR1U){PQo@a_hvYiS^6smmg`MhgV8x)xohnM!3ny6w< z!_iiA_~a7=^yEP&^;27=?UOO8E|pdtpmh%&!}^>E3O3zaSZ_f{k8WdmA6Ln$Nj=ad zy95o8^1ppGCItbdu$qE1XX;U`MCWp~AQ2|Y+g89T?Bset>B=jEbkD^5+s_dK3ZAsZ z)_t{V)mU4%Y`y3A{@KY_<7K_svu7_|b@30p|3Cb@fA4?%zPo?o=)9HBADvZ&ZKfB^ zt5;oC7$B3=MP4n58$9HE^~Scq4t?$N`Kzvd^L2N=|H5U9pWO23iK7Rbof&-FVfC6d z?|#n@?mISH+vRr;+*vgA)$LAX3~Fu*@AN~R^GiGGq20a5Y6Hm1AA`vR=iE89^^^S+ zB8c_Tz}BDV&aNIJQDeDn&SuMlETY-o0FfYzjVfK}6K#gX`o!M41ee)4+PJ(X6N49p zw;(jaV^lu8s~#ibJk5HnIq;c-#i?`V@ktYfN#nBiLw*cBvb#3v4BPB{fn|36HE32T zxvKSN;(00xF@a4=!SoMYQ|g5pN@sB3#O)WA#S! z{JAyI%&T}ZxGesQd2;V2KLmkZ_1xxpI>BWoSW>0G)wc72_EWq|=8q~?RpX)X5a=uXXyNNWSC>t5IAMALJxY%^6>{I{?aYA2~wQxPJu^n;nnf> zEkz17t^%%azvUNT%LB!f16lkoL4rSrPmU3^Q=3PHj+Md?R~VTc=@8M@9Y%fJnEvaX;s#KeGKrzo`xkG0Ym{Ohcng zuVE@W|CrRdl$g-!GZ8O_sC`MEOoNwKv>AMtVP*sCI~CuCyI0`O4P$f4t2M<$&-rT` za*jz2r@!3o#u$c3)-(*mX*7wkiFJ6HdtNR(v)kLqnRZW2){tj~$Bdd`l>=90y@L~ZCH z#*;uWT7U6w@3pO4S`=|S&0P;Fw=4i(*KzA7_4%CkdO}?;Mfp1f&jMfRx#0lcex(E*^fmgEdNI*H$)(_6gE{h2Z zup8Et$S;1Tyh>qXh!o~DO56|~T8BtGqBGcWuzrOwPc+D~_Opn}QuYtGG|slW!AVu| zxot&p$q;WD{f!4FZdzNrFjivh(tz`l|QZ#0dw`r>MERM&RG$Z-Hpbwtw`<5 z*7bRs$+w{`=GQH$FRE~Z!Uc2cgFfm|NtxK(-ribte8(UE+kbZIL|dW00xVdx{55a+ z@xSrWU;2f=_A`eUY}m55{_M_53I)IWNR^016|2b3pL)EetZKf-=799!YM?fJB#zHn zdd1CeUVh!(r;Z)k_uLbwj!zEb8GPH|z|qsMx%2(=7B1G!ZeCY^;m~k3THCrIuzCo zizi^7xxCF+m@$wgs9K0PGo*4}=LXjX@%Dpt6JObed8`X9E)-g{Db&Qp?Ypn4$u_xh zz`TdSVEouURf-39j-EVKSAc;PvMgegCKy#*Fo(H!MEYHBQ; zh=887a}ef^&%0T)r_mw)>mF6b)^{hSpK)GwoONn`B|f-!x`(~G82U4u{$7TYLsinldn8Py z3{CVKUPr&1CQVkln|OBeiKMB5LxjIGK3bNb2mxavxq`x7hwBBp-uGOJgE5QU1$OnBi5BGpLn)Em zULh)0ZYtb45uUUtxeJDeh~-eNQU+BKB^KT;g$2LgN+CCNYXI?RIO$BB3OB5sShKjE zzM&IEF2_XnWK(eI*Q_f3^0SqlKlg91zsb`^v+5$4fK{$)dHWT$TSqW86QhadFJ4x^ z3$7{h@kqX>MP>Csn%!lPij7AR90zU2C}(*FLqssy)o~aBRcU+8!clos;zm z4H)#Cix!TWj4fGs1mD-JtTeTOECtU3ve3@zffV1Ec++L|$>DON1)w*qDSxVT?U@L& ze9%dL=rtwkl11L)*Iu>#bfmVx-kFWU_S#M!n<9%}op7}@w|;KffFZ5VgiE=6Cit1H z^=t*=+&R_9)-Nq_OB(nG_Y@t^SD&j(IC8QUY&_u)Th;n)dHLXT^_To=(6-@N&a?%& zwX5ny=MSJzZkeQ9(ah&RXdm&Zg-zSbvoslLmS+qVK+X_CrV3z2)t9Uv|@*4<0`D z{4wL4(e#QLEw zVOqecw~t0^8|=n{QGnm%iCLFOuSh3xo7mFcF05g-u--_OWm2k%^hnK+Wb^W@0YbBL zJv%x133<%em@s?jZxr%GmI*T+65#`$&?OY(u|4&th(+_uVVuMPL;0|%#@&xzSLch` zQ$k<{$o@4fTTpK+FP;1|RSIa9NEtIf$4~SVr$*0e!O;_SCxSk&nr(Ft@z9vna10vDu4g6k#z_DD#)yQCGf|uukA4%0)f`J3RcRwtRnLgE-jciO>{)d){kMug&*0LNb2ugPGlhWu zvG&1x^r%X~lZ6w#Yj6x*4Wza2O>0XQ131(9o~z5@Ty{O%+KYaDtEC9HLTUwDBMS`m9 zR@b(jv&DewqKbYEI=6jtLOl@Vi2&MS06z_g&IgW0x|Foc-LdE}Jdz%jrSx;CE(s^M>+=pmM34 zQq`UX`^WdzC0TA!g8u#+YrQdVPSI0oZ@i=u7&ytC_0l_9W1(0~E^nrfotk)kW23|p zu_}L7U2>jIKqt=U^cg%DMs3s{3WNK$*Yx=2OPkxR57TU=D$Na@>M!3`O2C;OkX1{& zqty`ayKYoD>3(rT75(x(bqP3QR3fh((a$ASQN@9_(Wdz}Pksj5p&qPn`;%7D%*vy? zOOFbt5V3orwr^cuf@(g`ebC@+WqV-n?l1n{uO2({GJk?_)#^1r`wM^jlK1`6imTqZ z{=y5aBIsAiY^e;7GnA3d7Ew;FRUSoXJm;pZ-1w@R-&e7t^?QM|`Zp=HzV zSG0$iinypou8c1}T?`t%_aB`WGYz@~NDv?GTeVK^(%nI#7}|Jw_tk9&zwMH^#7sY( zCT(Q5h}4p|V&mZqm`>zvMrNxOfgT0~zQ=v5j>7z_H3;4M;^ zL+=`RrD!1rvQpSk;(#+s{t!DA;q041E+gKp1jBSNl>)I9@g&q<=;6-~3nm?6(tSq> zREW#agJC-84tFIADmTHD6)DQ5@VkgQPhw2O5pxuZp7VPm+IHx?#L&Z3es@Cx zCAp%Uw}m2(av$7TU%Fj6x#GegNYcJ9NFAv^l2dE-0^fht=;@wi z3rFvZ;b9BJ$|;MLSiZaT?;ouewGMe`S1owBE!tkbp#DC;cz(tARkZdX1280_5}cJDnf2h zyE8{i)s1T=ZfPHNE)yHeG2+%OZ`(AACDmA#YGkbtb{s5=ial`*T*dmg!ID)is9@K4q&dg;N%&ZjJjuj0ABM#U=n&+Se`B}ev z)tsIK`}xtsr?E(9AK-UiRWohvF17ie ztij8)w8IGEm{q7FdukYrrsb1w)|2AcruMe`JFckT{iOLLo2zuD;(bT!$uEoNjmv82 zDabFk-@%Z}nKxcCdj65QIB&8gf^ANFOfsG@N@pb|?{0=XW(jp%3Emi?70 z%eO-(YSQ32wZ{IL_8Ql^CGD=!ezNf64-_+RXf{zqi8J4{S8&1HiOZJP`(j5=)LkQW z2>J5^K~pjP|L&2Ahj*4I)e!AI=-|IwgS-4Mex~Y#2~}~h{q0BkkGB<%7I$M+A<{Mb zwc10>78ppdBAr7Tn^xO!MTw;Ah4F>8ujdlo zL7~vaeNb9Q5&+PU=Va9eVs^wbkxHPKSCjLzIXNbXD4jG;xM5BGiA6g5Yrws zPselz!R~F4Xw&7GCx98nS$tyM5sTr;IiX8u3sEsM+;Z8`Db77HG%C?~hRi*!sylX1 zeh&Y9(Am1qsTkY6I!{dFfUIHZrdg?Vo?)q9b#&!3n8EpAW&>*mFCXWc%Z9sGuR6{; z!MSzi8JRH*Dbk{!+-`?B*Q7Z{pZ=@);<6LRCfZ+kuO9mwjrK(Kq*P4x4SlOD=1m{(Dq zm`OT<;bCYP!BBNe`{YD)=A7KJ*kaQJYTbIz1^Sr_CFiO2NfyP*Cq+h27#Fp|%J8U3 z6y`8*SXOl#KN0g+@w6R^Ln(9#M|8R*EJ7ophjrS#Y2Izm48+&l*>wumEgkS zdDqqT-W&>8!|XmXdaKSl1cdvn5X7+WSUm`E>GF}?6s>lz+U~|b|8ntIP+1SGT2#Lu zgor1uN;~d(y8d3xfz;uu#YM49MJW%jsye5{<-P6mH(8FRp?P)ubUj?~nXM&Tbuw#0 zv@cCc= z#Q*%q^^uoUh}?7%B}Jhmt&GSql(aaa)=`tkPS$H0+(-jeJwV&R)K#pY_8)C;47Jai zTy|mmqHuHd@bQ}Qe!u+?WGXWjl~Gk>5Ygsx-~I!C^jp7j;J|@mIzP;sHS6L_FTdj_ z|C=}d^#9|^E3dlis@d5}i-*?IdOlm02O3Ml^3`;tL>BzIM2cK`ChGStpWI)93hS(Q z$>m=8cJb(Suv9pt>HzaYdzZ|=BF(!n&MPKDw39<43!2TfTf^Y&CG zx?#@Mk8G~Xr}h`!yi}i%KU%}k8XLF`^vIRgt1+8i9^O?Z^zD`H;W9&*Le87^@4BK& zic)Al2f-!CQ&9>9>Gp=R8#hqpQlV&IUGXIpM>?ZSE|HW=8SCbR3Qljku1>~~$$nLU*na&mM{X6vdKX-Qhg+PgPn2)!+#=?13Cx71dE|m)> zA{Y31VY??HmfbdoNRu5@hk9qqv0dBj!?A%T`P~ z@uR_d5DZ<1Yha(@m!1S8ER1f9!>O-ityJhIXe; zuc4gvFAV4Md5qY1ue$P~m1p)>+A-C1hpe%C-np1VPnI+fBRG{gOS?`_!nxl*!R@n` zaf}mW_4~Rs$a1hNj7Yj8Y-3Mxixk5@#+-BygT^2(ym;Bj3Wl*(pmW30iYh`+7G^+M zwy-{TCE-v^%_>1pm{Vg4mC#MP*8pb{Y@uTlS$5wd7$u>*#CG6#eg3>JhpIU3H8=eK z77^BU$8%LQ(57grH&x<=*fv& z*HyX5VgtSbLCQIx_|in=TDnj!EsIw>4%VxD|WCMv8q>*+t@j%Pjw*4aMP7aWdEux9q8Xe+(b{Lamuq z^?R-^CoRPh)#N*_sG3Ac2fME|96gH0Kv{2{88ZLyIFAUCEqdGV+Iw`?)t9cl;SF~$+<4ouU5_8yxAXL=nGa)q zYjEQ&cl;+G{?O{B=Dcd4bYQLKoW57xyHPCcv)iuEm^btUQ%60}G^^fy+jY2F->5eY zrk^+)q0w+}nv2A;i#wTt+SQm^@#; z50V>(F%9vm1HG%--S!*}=gvHY&M>6M$u1k@vJ-ojMj2u#BJD6(+~S9b+hh7+D&<`0 zZ095LqCCmP)|+X1{_F;x3qy0~U!obzU@B%du)f>yQfGu0-Mt!9IE{Yv^EaJa!Zg}) z&&!{8f+6$t#q9HJ$GMof(eIb%iLPp>^Q=S|`YF+GkfA62Cr{L$n0iTNb(fOv8X$sQ zkMqz(M=0IOkO46fM$hfh#?aPKBoO^1SOLs`{KUjHt12CBoi35HNc0}7i}q3WF;MlE z%PP^OdOBc8^pE0@n+A2}q4UM%B|$TyL&*5W?G?R2{0uW5U?`U$S{0_} zBef`BeYXC*eBJ8NQi196q-_egJbJPs>?lM})R+pSaP5-XNx7~4Ef-t9aw^Lr=kgY5 zSwuatzrGaCjS6wvuMkGORJF)HKw-`$PSz6-C6U@VcB_3{x}_yvM%x2xq^eF@k!emDAKFIDaMAgrc+B#VRb8oK&lzof=dZ`KlNAL|)a-Q;VPOz7%! z=9CFU#gP!x|M~+>ZCe|oP`LMKJv(-HtIQ9!-}^(y6OaX(6(m-6?mPO)-~30%4(@IF zFM_2Pt$p){{`0@{U;o|N7hQ4DqS@5OAIJ(Hg%5G~WP3Nnf>X!9!`%L()wpSBg5aF? zB94bw8p5O$e*UNUi0*?r$(~jgx%jVMRS&-$JT?EKjn}^QuJLK)pIO zgKsWg^QQMLdd<7`9-Wo(rTb}CJ>jEwRs!*{-PL@sWd7H^04Sl=^@o*57N2bupXP^!quUr;& zs`Mynozp~3PZSzinDgi^X5PlSv)tc&uzrsA;E5W)RSGV(GuaxFxf{0e4oNU-a#Nys z&Y4HrkK7M;lpMeow!5#Y;+Qi{n0dD@dQuf|`-X{M`)WNK<+g~2!rUgvDhnm1&39he zKFBz${-$#IvVJ@Q5;3Re*wRA^a6_%GgCdKe{J z$C28>zxtwjkm}}dZSY=PO=W5P_&Sl`SOdZQ@Q6Rmu%^8hQe$XBEIWz6(kg@ zSW)wFfQSSpg4HgK2QfxvNjpFV(i~4lz41uJ&-yBd=gqAL4^tInBc&<#v`7YA()O!g znt0vDdZvkND6x|B9nAsh>L-)?p2}O3?SP*rbh70#{2iw!zpsQD@~b%5T;>2X>*155 zuVFycCaZsW4MOQz66s?>h`}(8O--f-Iyf2sLmXxefj%@BPa@b6o@90AqjCc|cl8^OoW;YfKzM z8PcW2QnlJ2fDiNx1Bed%iDt%>_sgOG(G!<=*l*7gVxzD{TM&vs0dddaQCx*z>wMzz zQDlQhLafuzC=StEFMBiqeu&*&9PRKiZa$OzMQCNuf+43x{d0H(@7hNg|I4q;8#su$YxgR*%t9=#izQ=uhsiFIx>E_u^%>e~H)0CN<^ybzT}^J! z&7V{C5I2aVo;6LDE{H_3+)BCN;hm!`u=o>tR@5{ri!%{rbWZ6|-g!k!H&->ThRf1R zR@psTUs0LY_K&iArGS=*oTP+)l(09e7HZCpl>69R%$m)SztD4wSCp!5JYsxFWVZev32w`yGuX<}E z1q1qZ7oGTbzwv(_*|)1m=fCH+dKZoFMs4`KeS-cl1F#fx2x14TUlL~IV1GK zdZO@n`xYK=jfJ?%5FBn#L!=TA)iZHf&T)*m3Y0}0MJwRP_R!AiX&CZ0rHcB<=K4)B zf^Cy-)^(RGd+lpCAG`Ffg^QQ(dv@#b!~4s|48B?T>7V)8Wt;Ah&|aD^<48hVJLjD> z>*0J4%e<}YYU1=m=O4SiJ?-1>FQ?AblRgreCv^{P{aX)D{P1-XU)wzzv$~ufE?1YR z(c1PJ^m7OUqW*(H0;J6YEIM`_t|z%Rx0{ImbKqEYAsU?ZXR`Ddo)U;gA|;FFm)n`s zQo3^Q%Gx}7P5WFu^BK{ZxD0tC@q&3}xT~sd)|KXMr1T~;^7cZ?xMES#EXV!6M5J@YG%e&iMfKd+ThKNY~)^%0Y z>KPG5qwHxvSNirVieII&k;@b}5nCfaD%MRX=rtQWM(BVqi|_%VE(%e7n5u3dI2w1;>mbs+=9N2x879@L2+L z&yq;)u0s_kTz28;-T!n~nzy!_PQ1nT$;a!j#OObDbM*v}1<}*B5fAZ>*5UC;R~=4n zv_;A#|>|S+b_bO5l_w;HNK17svyWq0>6B9XyK1@_?w}N-JRFp8=sO-cf+;Q}) zF?3s<4aO2~I&R#6trw^B0E_6uTo@{Z?&P`9a}BO@$tH9hT6rZnt4n9K8<$=QUZMC5 zzGpGBf%V;tZ~u8=`rRwYEIG~m9{O zh~|Eda-b?9CL$~8P~9b_sj3LI{cR1;ruICS1S0-r3#+?g<|jQBh)-^rAyONM1?4oT z4c>x9&eHas6j3RLM5_d;g_wglMw@8Z${}SWqT9>D6K%J(hXs;`Gwt<qk%w)FRDoKgEy6RmqE%7 zD4K&FphPv+BD3u-C6_JCtPsC{d#%c#d}yl?#Jt;>h$;?CsO`6In)r#gJ~+Bz_6 zFj*&07te#w)%U-@`dn2M=w^s`xSiD(6Zo@^y}G@AsAwn(j(KydAKDPX@^wXeW^-HO zbK9!ag#Vy@G>XEY7YFBSR+UAy2a#naHbC>a_^fpAHdbY^K^RBDP0s2dGYlVC|ms&t49zEFbTOmieQvBHURkzX(oczI= z%Vi5reeQSv`JU${%X~J>U%2@D-uLr=`@jABKmG38zWm%A3av5JQfeccW#xeCT2wBf zBnuEBwsHX9luK>%5GS4-3S}i(o75X>MRfm;O5=I`C8M&W2^=XS5J^dA&z^JfhP5|u ze#-@yzj4FT(@$^t+R2mcQ%N)U#^ajr|M2?Du9modO}*J?m}-%a-dTNFKY8Ev)zxecGM5`gsppPK14C)I%gh8R&ll&m|BnyCEY+z29!K($Vz4dO7x06!B?Yo z@*d&n--#my>z0n5wb7AS!Qd``-}dT(W(o5ZD-yDP@iX;nfU@#5rMW0K2%3#YeSX=3 z`p`Hx4jgaa1e|<;3xiRQ)B1l;^B@1(rL~o&q^3B9C7z*8g!1POm!9Pd>M`n{yQ8*a ztlsw>t4DQM$5sVz6GaLO_$AziIEg6ac=k{g>ehAjoaXMs_5D5|B)6(=Q58yBjhRV4 zbaNZg7uN1ot+d-~yqg;X%^a=>9;}K|K)DQbks7TR){PR< zlxvnuJhiVxTw>-%Kt%C5z+pJr1}>tmqEPs`U4P72_yQXZx2~_}^28^Q&VZR|f}9y9 zVm(mRF5M+t{T1fUE}e)tX`qU4?x94gQJSqSZOV!XYVj*z5D%ljl(fsBC&Dl5(iL^f zj%Jst2tAjWxwL9=?Fs(bQI;#k&@d8*X8FqN}Yv`%%Wv-xmV=-0iA!kFNTD(MX8Y3{5z ze{WwN+~3Wj-<#04$NtFi-G`YCtQovGUdAlbMPA7aMK-szX*7)KcWK=Ad)N@Nrv8m( z8Vog$S&$TGKX-aZr?};(24E-!B0{0$kTbyn zLvg9o&su@d)s8tYJ;py(JR-5x^&dQ5-wLVR<_sjt5d|ie>bRqQshaq(OSnG?g`+uf zC-Fna>!x-8_IjQIJt0(#!tgd9g%Ul)l+GWrBph-@sMw@CiryX+qQ^>`$tcVjKXHe( z7K`x~9isM=?S({a7cMM|iJanOV#L^r&$WZolvuZ;rwCQjJ+%C8>nNZ>A#$+oM;I?r3liu|({R~}3d`b~Ts2mb zZIP2trC?6zT+w`c9ElX)6hzQW3a3kK@dPl_QNFc>S+5H9jMH*1-$aiODEX4t(uMV~ zSgmB*rb9)^?XTt7_Ov(qvf-kexn!NsRk5v#Aj}qoY@KW$)pBsa>H-fU4S5`cY_^0z z<=3j9tu2Q|aLIG^%wqK8MESEP%O5`9vcBGPMEU*4M!yId4fxZY9@|~b$AKsI)?<3+ z3Wn6Mr3DS>ebr3~XL4EVx`}#jf^zHX6i{xlwJyzbtLP2w#arM>YQTYqT`My*sv4#sITt&$(#TnswK| z<+k_!^|$}E|M??7`=Q&fxac2#aTc{Ks^%z9wWq2TwluePk>*UQMO7fCbm*iqG|IQD zih_Ubl3LHJBFkNV>O$(gI)7VFrXD`nezy*&C4$Z3{NC;LE-HnJsdxS4EptvSz4Y$8 z-?jX*TXt{Xx_9Sv8*?*wRWSd8B|rF={^ql@HfZZdcD4Str8cjPjyO=?jBa z)~4F7cM=H=`q4Wpp~W?#^UlK)KYLqsn{@b$i1b@$pq3O)Rxo_Ly)lPJdT*PAC4T*Z z_F%}FqF=JS?TfXcN5g_S^_zh82aKb|Pgb8!ciq}7sN95Y#oT6QYSmH-3-ObFMh~4RHu_U)I`QDg5G%&A;0i;V$VY|U zy0_*uw-^C~JAy10>rT}6_?*KWRZKev)Z)KrVNoboDD_a&(d2{GftF$s%CS}h90OAE zgyximlhuXV-xkPK<4(poq4ccKA?`tPgPtasV#Z4JI3f}Ug1B^6 zL@**^5|x7z!7iSc6%+xb>o@M)0Xv4J2;x8|4*eNoj2N~RlOLEL!3J4LvJWL{*}|9=?dKoQE}l)7_|3 zLcFMjwj-Xl+Lz83uLNp9b1fcPVL2ezfrx>4F0KD%=@rt5DHMMGV9RBkiS;UYTL5f7 zFwzWo!M%U_2AC-x6sLgCwKe7`qiiS7SJO~ z)|#v=nLly!x{{?oi=X?p*IO82OI&;Zj!`0Nna*JuLxWY8v;`q0`4x6-=sbVJCGEAW zwn2mb$;XS09`St|>)Au~`F2^DX;v-oymGXkReM0$L1@4Gg)o z@Y~d0^@`8sXR8^Pcw9<2m*A{Yh4|skwV7p&ibB7dd3aaRKX_xwO62#&h}7FC3#zth zc3D+@w0_n-dhlcg#X87BL8oNLf%@#aLJ{8C9(S6WM} zrEK&NN6!m`#WVTscFy#8>%Hy8pL%{RihZZwrwLSnnLBhVGB}%Dn51wQn5P$lNgc5N! z)tjxW@DKzpNz~@i;yI6e?l+&=`n6fJXV05|!P@m3R$ueRANk3@@`FG3U;Neo)BDa6hBTn2#((H$}pGU61WzO+IV%y$& zZfMu!Z`D1yueyqfx4xg%HV*X9yweA|VU{h8aUt5Z^0ZR>~Wt$VAs z%X)J_g`k_gS@{QFsJY_l{iSo=(usfdVDY#exbyCBzrgdvzWR=&T!OM_Ue%s|G=Tu? z=FCiMkUFVE+a;7nF`Y&bQovaJ%+`8}D&O|XPvW|XQ;5Mq)*(SdO|2V7w^#M{=`B^^ zF0Wfte}<`f;Q5+1B_4RLD5?T*GAGMfK|cR<&3klW`=?);_|fYsQL>YFWVWy^TUdK5 z*i1_Q@C%iuM^)r?x!nF7neCx#AQDJun&funX8V=xIbxnus8E46`lBtQ=ewzjl8Cca z{l8J@k#JNcG-k*dlKaHo(Kc3Eq324to3CFw@ug>5KWr_vWI^4?6dCwKL)F!!l@t@A zBy{NQo0k@)dlslMX@Y&@NqOt@odZ>%bbUi@hy%x}S0HwJufw6N3cRWPe0GS=h)g63 zO&l0{7>J7;NKLJlq2^}>KTuBL+9h?*%2RApUG0=1om36RK`lXcuV75WG$5Ye!5qRU zJAV!5!cfDQwy9+tqGt`=m|kQCGdNGoY+!vW@e0SVTln1e`Q(?1^PfJ>%859fXW`p? zKH^({(0B5tP&ilVavK{WoSnG~P8AO@j8)MXx`v*ZicasU&MM+k43~If2=u818M09_ zRMq)=;#@@t(T%^0Ck{w-8;bK#IaSe?C_&axc^YLH2E~UYa7t%wFFf*p1iMbamXHPh zXzL3f^-KU1ZHbLSL;bd`_D5Y(DPVw8 z&<6RjDlVaju0GL1!_sh3ZHs*D3lo3kj!~%tSsbPr1oxLL8(G01A@};#wHB(Ue%g~z zm$&6ye2#l*EQp_o1P`64-M3o0w>1McRt}#S@esd%d%Xvz2pGh&7CZvt*KaHa3!cCB z+WOX5^v72wsKs3A_Sbl4B1~8m3G(vXP->4wz#vh7^lp{>)#vIXT=+Gd2F)M0yUz@< zo7EbryW0jCqO7m&Dh(2pAmZT>J;R zz>`8-b5hP0HhS1f0S_IoTq4no!ceSZ$nF(c>uu$l)3yt##fj&{>CuMN!@Fv6g$+Hb zoUKe$&RZ_4=h3R{vtHO+1PuJaAd7OT&up2vwf#033grQRO1A9^DcZeXyyx_S6|Z~G z&%FO5AAQGP{yUew{pS~7`R2d&&P`w4vvAAadgO|GsUd_t8Cho{lq+BjuC}d^3e;C? zYpQKz;N+F&!>nKZt+y>-yy^9a*1cucvAuhCJaPJ@R++)8k0qDh^0$BC=MJ8l-$!U# zL05#d7y83HtM{gjG$=Mi?U=SR`~y69T{-#%l5pO)ef0bk{kQ$r+(U&eI~TudZ>PlbZP6!W(E{Rj>s2Vc?nwWqcef-FsB_jUYC^DP&zts>GJj* zZmXzAc9nh=a?ASq*;5p1d8Mmo#-N#Rh#>t}Ut2FnuUu4CzWi*}Hnq!J)qQ4Lsd6W} zt9|Mo*iwP_7)0&nf|H>;+NkfKSuRjc2@r`|;06)5q=$CaUfZ_zV}&U6yOn(C=E_Q= z>liA~f|+#*+EUo`2We{E?6`qbi71iCb4h$~Ni*QbbJ>c~B{v*zy}Z(J>{NYW&{Q9{ zZ=L*vKWFp@eGCZhIXv>5(I5$$(WU9c_AB?*Q-Sd?<|0M8d!MegVe&@rkT5SPV1H&R zXD)rZ#SacVZZXH(_c7(V)Fq-JNL{hQ;->awqRXD!L6zW{pWU%-t-R>b>~bT*(Su7bj*I8m5t z>A4+<*f5+mt2>|Ft6Wl;mss!LG5W2co)z@O5b+#_4fHZF^`V=yI)5(2Pvhk!`EWkW zV{W{XGM)>=N*=@1ECF72Eobl@jhPLs?_j*V-77f%>Eo=Nh-38OF6#cYb@8LU}A{8_&vrphM}rFmBva8 zg?R9It&`8Cq?aNIp+*$;$N;*mC8#LS>YZ09cx0iwAs$`Vz8Dp$o=9$0(gWv%ttp3$D{k`_EA z6vip|u>9D4s9F?-IT5@=@LIX8TyZ9esmdvm9 z|J_%O0%F=0&99v%=29UJ3PsPW22T@MDT%qMRb3V~RFzllZ{~72Ml%gcT9{R;^cfUI zV%UXvrvZzG(6*-k4R~Y@=q57Z|6LNPQsvRh+ zk*sG_?|~L%r@gYVLW*6?tje;EQUe?ywYdFG7FCd~w7ru(%2@?E>#h>1-V>*b!d5x9 zXtW|Fe#6@GDz28Q_GPj1Q2i{bvbf|-DAtX%{bBnfd{m)OWXd@!%z^N@ymdnqq~C zAAa}SH*MN<=*%Lr5yy~yC_H7QeTbdq&5Z)s6wN45Z=vwek>-%FAV>dTdw8mz>}XGJ zs`=UzYy(`DdXuJ#Idot#<)xMqxe|274P}wOeM5~$wPbG06cG*d>rTrT)E9qn?mIU7 zvP&{(HBV1{N_`RGZUXPg)Oz$>G)G8m;r<1mUVSmGyLS;mXn{^Ru=JNuG*MYA9w$) zhpSP{hm`brk8%l|N)ack2`;X`B>k(0$}yMtS6oovrMh%QZHTZ^Iwx=37`T@?gby5u zT0FAilJm+%)zIQ7%Kz1a6T8~aIN~9aIgN;Pl)U|l7cRMnli;13s%LWb=*c3|6R@J` zbvB83OB>W*Tqa`%s%~uWdxCU%-Zt{$e^p4+#wenWtqwcF6GWA>_SKZRq&7iavVfCA zpgSOr;o(195(!OXLg$PdF4c*~TF}0V>yqHd?x|_EJCg)GM^2XMp#Nn$T?K=NlZXQm z4Ae}&OsTOgT~N(5WSfd}<&M$2xtzsI9b=sFGv_g!l@m^#u6{U`e;VkY=^??Gxhz=5 z5Xwuka-R4`=p4f|CZ=Yc&+gS(8)ydKR+!nq`d&vyX@39iCE;Ad`$fn6xfq5Drz)R* zHkm&6tPp}Eh#3RQm?yah!!#x$bym(XDVL#4^*NVg#IAOze~77irrw-V*l;=v0z{lW z9&$SeSwpG$b4Hh4s^{)LT({@kuya7pthj3mg4eYdHi6*3f3$1~T)L8+hlzDd%gOFi zrvoV~1eDyu4^Mt!^b_rAk)f(Te`@0R$q_{vIg>~w4eg-HCC{ri+OAf9BM(vTFa*Kr zvSM*7qQ>dcQuc|x6~+-4U0LT?&tQ%l4n_sm61A3Y5wTP<#9oI@h*QQxZXeJGV=u6^B}=s%~3U z;fxy=Bo;Z=FbRUgub;TYqd$cIlI2CDCuNcKTzl87nyGz>CUtB504O=;w0Bh;S-Myn z{n+)TEj(E!Ii%GLhAe?NphW14=huf+#HM-JFl->o|H`wY6(-K|wrz`O7qu5#n=fvA zI@!f&QEUqMsmCi#x$ym__8Mk*R1OUofEyUBW38G#wWat`hy&#~qmx|;x!^Kiy&>p~ z$MUa~xBee-iT7MHT8(o!8t~*59;L>hcU3dB1$kLHGBWqhD{AkIhXw_y+cuQ{Rby>2 zFOBYeX?v)IOVLC5>{+$lx$juDo$9K}pH)w?SOe$Mk#_&mj;d13(ncAc)k|s~r#h<9x{KrSMk5)M2% z59X@m3r4SwX4fDhKY7!rq+xjS@S@kMi=-Q9i$(Kl!$@Q43G%X)py9CTlMCilm(b-<@Vs?XHLe!7 z@~?+Do~|CHO*0*tHZ$VVuYeLf+0}Bc>(+U;wAtRBggGLZEs!o+IC>-7Ha68!2=E~EHaH_)zIT|J+EcyoK{`1c;I6+SmOMngE6 zQ$^A>u{3iS^0_6lbYiH_$+y{C{UB21k`f{)5-Ei=XMU{}lW8V!%1RS_W^3I&O>Vgr z;_@65KkFW*?NXExjIOd5XU!HL_~*X9u=_qa!2P~(?}c+|kSsmgq zeg)xS?1lowFJ4|fs?u%uEn7&D4gUCvy6MEjet`QdEABfNF02x17f4pRUvup^x=v82W$kI`Ko;L}DUq~80y-N=cjnNYhmUu4giSGr;!`m+4M`t6HF~fhJ`d3; z{hVGsL?~mNz|RB6Uwqr;nf))UdDmGNxwsP^v6o4_e7vG^+xjS!O=$qUKCSrPGcBzwWp`nK!uNOZAN66 zJH`enZ0I<}4P)_8M<~N{63{96(ZGHgB%K@Zx+O2%o0+R%bW+t>k=))Q$|bJo%)Mwy zSe;s#i*@jiqRB}&HcoDQrg0h0&m15uaexwM=@e7o z`{njRR$aEA_bx+i!{y66Mj<9Pg(`YB9olC;XyCsUf`quX76}{LQm}p0HfAN*Pr9*K(-KPoQ%oM>P$%)wNk=`+p(5R|Md6jG9SRAR@(Wt4WrpCoSd-& zLAQE;&FXSbS!m-MtXo=p3h%wPh*imZsw|ilpBqO{jygG2j?{k~<}C(CHF{fqOML#R zkq`86+j~Q zb>e9IQTiFYGR&Pf??-<6Z{Bv(YqU%5BlVbJYez#M<60a2!DADD{q?nXv;TN?KQpby z(nk8EGbtU|Edys-(1ZiVHAOm}X&;^b%mdCFmKK||zWw?3+Ufn!v7$X=E0+mbW+xhWc=uJceQf4G-WJ0@aYOy> zCnA*m@e?JIy)fFyFkxarbeTVLC|%x&+;m~hC$}5v8)|g?dixNV!yNLORaK&;lh0_7 zx_?J=xIJOY0oABx>h2t zcfgG+stMF^c8j9iK{ICUa;!xQ)qxilU5!1xf8wECCGMuSw%sytVqiC3!5?m^cwh*c z9UOy$$0pu%SxI$D+)L>k_imrKd__@$J%=l;LVDGufTzyXog#V)t#uS9Y8_z?^2#1c zeE_Z%_Vm~P^xNfa6Vupq&NH*o6D7>CK1A1?1h($5G`LK!VEXdO0N9z@&Y8<-fH36f z2(c&nrE?12nTKC%5d>zVO>UQRj%kaCNcAj8BIzN9JVDY^IU$RV#0lp@U&oE9ogQO; zIxGIL_NKv0GW1;r<8;|eUpUX;Rm98&)^`=YnX^eZG}Wkkc7NRF)pF^4l*HDS_E+~Y z{;|umdgaFOqTXT&KCc)WH8_`ntq=g)2LCtg@;HOIxv>cKJ2P-253 z{HYurkvR*McU)N&g1GZg`-^TiciIk1cCP~B32;?{Qc%S`Jh37z3qxW}m(o=s?af3m zef8R&J*%2tO%ilTL#=$;+bCq;b9E)uOrA+B2PQv?T-Df~m=NWJh+iq3fh{Jul!h7zN(|kiJY=y2m~#FKfA5oL$UBfwAxn*7T8*3_2OD3 zl7gGo);c8C+2D`jv*pZ?8ZJX^L5@LjQg6zhCsRJMx!jPYbU$!)J!RaXqHSyGjgUX-d!r{W%?G$@5>2T@wVtu`0VE5WMUw!TgJ z?knrdFMNw*vY57_uJ&qONS!#-h98Btd)f>h1RI&@AgySKGqR@50622S|1)IqrD0opg%c;i`*+l+oZaS@YL{8HnHX_`R8hu?L&fq3&s#6ASwez8 zq%eq!BoqM!r2qV>s*^Zu(x&Qv{OrU>URM+jGaW%zobk|HmDc!Wag3CSbZz@GHx%>a z?p5HD2xh%VtxBkTkZ@ek{$ApSqI;MCQ3&dvL?WBbdMFc_PTTaJ!s3VQUJ=YCbRS|R z6(Z4h++>AEI=e{Vo_^#+5kb+IixQlUt5#MzBK?Sxz-27%I5_&fS6P$`p|w}W8ic2- zl?LaY=vm3-JZHubX83PDHF|2>5l_@}5T1O;vm^SGm2)op>n5EciC#MBL}$=&@eO5N z>x~^@a1KMHQk-OsX&ys(&L7#m>W-ZYFG+1@PQp3I^oS2H%62ZiXc1nN;aiTG-K%do zW;U?CtMKBJ&Xi4!SB|L)a{~+gn>hz{i{0qCCc%ne&URCo##^fTjq^O`R&4nDUHN>Z za(g~cbTI&%rKU2;tBz1v4I zB4>oK6+X62A3((L#J&nf`Humpr2!AhQ~S`pGKwHfv(xoELOnwxa$p{_olgo0bF^Boh9DGlfuP zZ{KC#wy(~#rPS+|)GhflTPNOl>8KRpzpHJ=J@8yT$%UV|m5fH;+#UhoCo9~xZUmae zSmkQ_TKp~R>y@rl6m6Jy=I6KDYUjCw)wOP1Q?~x!e_E@iJV;fG!}ddm^jpvDX%C%! z^v+tp(NFE`Hn!rlHgXABT&V}{sYY~dDu};uVbRqa2H=B56rB*4vI0DCthQqUD@GA~ zTY8KhHay*)Kff$Z`*`g3b zHo34pwZG~Oq(9i+w?Zd)%eq=HqddBM^e(VExv>2VB4@a*-fmu3udApiD{vxD?yKja z^gt8^p|F;_ZJw!fE36uRu6@i7_{^)$!!fHza~;pwLactWryg?2h{GeROG)P(uo(LM zIn~58=(MVcl57Vk3kGx|bu6BCq}wv@KUf~(Xx;J zz5ngU-f>%cl*ZCVpLndAEQ4LcX&slkYlg;^D@Wb_t}BZ zQGH;jwH=Bfpc86V9g^DW32ppZ^y&Z{2&dUEM(Nj!(!GIlw$d?BT+)_aA$|OGNg1IS z_%H2%Yy_9{7uAm^a7o(GVbN$pT)C(Q_|EpsW{u5@YeZW}#~fLS{DvUq(r=jjA=lVK z;fyq9iS%B?dNTKDc~W0`+FMTUI$YmfTCuP;om2MDKUKTl(gZ)ZZQ|Otfs)H8ab)*O z191_kL-cl$&XCB$Vt(>|Y1Nz9or5d&-{SjdRZ zV3ezv&S5i&X-f1sLltJq`BP7-?wRPW;fA_UV+9C+g;B?1)Y_WH@k zN4J>VubKYTYddrr=1%@HY;@+HdsVw(#UBVKwlpiYl-Eowfc;40#g~{zklcffd2DQz%Y!rq9ed{Xdf(|pM z3XxzjbO}hKxMWM^OMn<)&ey8~-4!r|N9*Tp#I5Zw5Qw`D)tOo&C_SowR4a&}2qX^h zr`P1ZV-;pdk`GeZhKWcViF z60J2clwt$`M&g$N`zuV`Jv9g@2T)7(!@5jMVe5$H`Ii$n6 zDloTCw!&~$o-yF*E{TE00)ipJ=>|N8ZslY5R8Vz3;2u4-;B3F62k}9ayu1Cdm-N)y zh?HEhmoj#@yH`R_kuf){B|w-0;c<3^(<--i1X^U4C&HpCoWU@T%$fD1CFG78bTaZw zI)PCF&FBmyy0+*UhEUS9VMyr&&7%1{1`p`brqmLEt|Ns%mq9eYu6>0RimlZlE8!Oc zrhx<^!X`SymY0%JrJrx>E=XAEkRV%^E!5V*{`{$;Q-1&U3MlF9g_1~x#1@6>3OFxe zan;pg%hyIkZ`{KW=2)lamh~gGvM5w7+#rZ;RHzo1#tJ=(ATnONxHMBJYTR+CmSiHu zT1>WWnh$TDxN&XyM&~uFO8KSB%VLTNG~dDQWc$)thl*;8L%D}eO#Ju_6|mO#o7>}K zIJ1(`>psz*7eIHZEJWtcDe+U{lsClF9ck7=nWsymfX-DV#w$~6g?k-tTI(0uh`i2I*WnDcSifAAh zq!hAs&C1#~`tpw2dnNd5?aNT;OzXE0<;E#~vK*K_ySf%0dP;fQzKJtoMls1VVFuvp z%GQ^vk6l!+#jRdcMBciS0^g!1kAX-;kK@*LWp`2A#??`!5zElgdRwhtmYkUQ#m|)5 z8a~Yd(S`c?z>Vz{^)_!DJ5_?*W(9>yI}VP9m9v?|S&_L!3a2R!Cqwj9ZMTiN>sHsR z=yI{5t5x->T)Q)rj@FmRu3c3_5Cb!_7L4w6NQ1mvfv4v{Bl10CHiDnGx z26%uf>B}#w@75EcM{!j6+9h@Q?yKvJfhQ?c7@|Rza;2_-a)=v{UPsDV;{peyS>)Ii zEmTmq&Mxy8GDq*C1e3)aI$|iLeDZGzKp`L&^W=W|>2}ZM9ubHeiS%~lLy&Dw15$v& z?IWH?k8@7|`iWDOHk*BFOFe2H%@Ct>hSQ~V^Z>Rfan=OgAI0Q@g!%*wnrZui(Sz_n zLn_eO?K@v=fPASl3iHhbiHMA)K@FJULtH*NONYTZyH_AOO7Vy2oGDAvD<nf^Q33D$|&It;WR&dDW*dO1Z>lE6eIAjUa z|MxG}FOCm^Q(3xHG~{pzOoZs1_z8L-m3kP8%j)qKA9_}-NJLd4FhNFQ#~=&|j-{2C z6-VeL)jW{~$|Wr9t-ujOX9YjDNF@|=8H7jlyu5w!RA4GRWsB5Ee~av0CLq47eW^zT zNt+-H&=Y)9B`sG3nM;RDvh>7%m(tjD{$x4J0?z;O6ZPI!EXKnWy!hKnUb!rm4xgyE zPORH4>agJvnrr>F`O9{*RogB{s~i02^|h(Tv`ZQ)_-=0l{rg92vBW$F=(mi)kQIdm z>bCaf=D64Xl?FV!4%d}*ikz}oT%|Hxvme#wLIOQruIw2nG#wuo8^0E7S zkCq0+Vr#0T1D9{UtlpF)Ww}T#3MY*q#f&E`DjJ91bZJpy#X?#|sV>$PcJ$}>Rp#|qVNMZHtav4?ooEYZ6{M{SG|C@st%nYN;)bfF z3{2uYeB0&i!M*m3((Bq-SU9h0BOOm!E|^>UL^M~CC#z0^iQjy1R4$heHl^b{wuoy2 zzDa+ueV|$zQRQS;h!mn8_4%htJW{H(KCRL=q4032m8Sk0|0jB8>)4;&Rtov4-T&RA zC3wm5dT~{5cDq<1$a;o}%h-yE9I8U!;0*nkF@(-8inBBkhSD6DUs%d7U0xsO!a%LX z|AF?6ylYlg(P&_3?)-&UUw7j>?t1rgr&k`_{mik0d(WIc)k-jf=`d&Ryv^_V>F@t* zzxcLmmuguRuKsI-wY9irN}Gc7U02j1Eqz{_rj69UhUh;|r$y39bb~E>+k-r9TDyNo zz1F7%iR1}KhnBs0ZS`s$X5+Ha`=pLM&GBe^dXZhjS%Mz^$Th?=(j9W8fzR~NdE!)U z==Z-!g*ke%*+CB?iyvq|5kSgdbe5HIje#ENyV}c%;YYzx-L+lT&w8}E&d9xNdF>i} z@#*%~-sGdHRq)o&If)H%-_aUDy<*|L*VfC(W(K)<3=OqyYWh_B#Y2LeK_rs8b%vPW zgXQH{o~;LmNl|DLz@YvO7BxWO&cn67{Y1NUKDodC0P~T}<(qlww)Wts8c?oOEQKsG zmmRUP)&3^2|Jdlka7uXMz(YAh7{2^$-7g3pJ6V4_SvC1dGHe#WI!2twjL1srUz*(% zO2W<8or1Tj0^$bd2s)z>MC80iG~dPqA_l}T2le&zc5l_?9y6P@id{0lHtVB-HnJ=- zolAPsxR6WYnOK74X8X*fM-@mHJrjG|{v*qi=#PkWfC4)9Q~Sz)xqv}L{b(CdT?A+Y zm#Md8IddS8j&Gq~-u{NMeey>_&cQGZtV4)QjOF&va-qu| zB1C0?o@((-&z9Y*>HoSv6|baFXT__lZ3Zt7GaFdn41A;e`ZrGh zG@8Hp1GrlpGi`3_zj=374Eg&+CIQ2&F||XCW##^NKFB6cKuPonOmicNGgIxN=LR{| z%2Z}>I}g3*MuW@KZKB_G=mk}>vNP2$EAsn#LqpRTdOCqim$in^-!l_(q352`89=&g zU?}m;srI78kPX6VWtg(((z#!TM_QrsZkII8IfJ;L{xI`ILP!mwx40Kh6_V$G?r5L7 zNZBI=Ifs*<{bvocAHgM%NToUABt3~}*5%qcZ zWSYMK;Wo%f%**^Zhpz4_lP5E(;wjT=U!cF4GUK(5j`{ z6EWpU>t+(1(LJm=pV&WIz9Nde%Sz*sn=Kv{MH^K>^8-6adK@YzB2VnAO0)E}F6B?E z?xuC+Fwe1B5{R^1;%1IEYMrlZ?{4UOmeVTv_a7S-gcPKtz4znIhBq`O61Jv zt5?>7oG7+XFyFSm_HO3Rp7{CKHfJU~e_`^07kxvbej#B)iTL-sk5mtl#n$ZhY+5RW zW94U?B_!_LRLp73Sd4NGN3%;uMjsx%=+WI(V~%x8CtiO^y`YMY6OU>Eg7h$tFZ%f9Dtf*VBvEsA#pVCM}AzXTICp{Xs~t_~rIHgPe(PMC9r!AHB1tFLR3- z{;~E7q08E#ZUb4r!egddy`+|KdF&L0hjMA#f4rWr)Lrv2#7{n6yImZ+b$#u$a^`ow zHhKz`Ah>+P#`59y8_OIYmM^GTy(YuUEtIB8W4xOmcH*Ze; zqN|cBj>zhVUsID3!MLg|%xt?S|B9pRBeb19$Q`8Yw&@51D}0v7m$OJgRa(2zq(3~d zw`we|vUP`PWbzIxfeqq6DMSq7E^~;RvJz3^oGqs?$1&;nKt$6g3tZ~b%+a7wF8|%5 z)!QWie|{p6X*^otvNo&BjVk6qLYp3FoumtzoYyQKvGpSCK3umCpd{}JVo1SLOOXvT z49lJ~=o=|%kg%ClR~1>KCYK7By6T9wVS0ETL(jUrHD&CM@yEKrFGvwOk~@2LwNXdk zy$bN>7?XwRFZWM={r!B<9X@M%NEza+A_2;(=olRou9jIe^g{JuKFiv--;fB;S z2t)rfi1bjTr=4?55B$R-xD+iV4og4)7My~leB~h+Q|oe6kjcO0iU{)~I7Mu#oFg@C zBg8|;N8%1kID*}77?O_Ka0dBedWf+LE))JnNun80#+<|EoZTz@I7MT9`!bOqBB+YQ zg3jzl+!3iX0b)!f9sLj<*m0oFLi36v#k+&3%h)9nbNJ~=<-2r_b?I0Xak@-8bPM?P zOKVrCR_|>Ki7JRlnCaOoR9#rrLPM4c(JUNyCL%<}nL`$+&udoJ-=D>WM8w~8s!SSA zo~|ZS_UgUuO^x!o?Pf}|!abgFNpaYsA5mK9`I9$|tWST11IKDn!4`U^ zH*3i5gH_fg?Y+D(<9Tpr@l*x22dthsQ15i4!qXWF?ej^}q$Ctjnl|(ZrcP9aT&{k& zy3iEMg)>&g zJKLL*KX^^KCl`MIv1&(Zxk_^!#-DAJ(vX^L+cfntj`nH{I=ie3$CZU34VSHGUtMWx z<1?c`PnBixL5+;qMZOedQE%Vxwah7vx z0J8=#~BM_^s;qy;^%H}ov`)xDtr4~RLp>XZ~LW4 z9;P@FSGBz}=}VSX==>*7lx*BC51XDqf-F^E+A;Cqb7kxBiHS#cRaR=FpIHRTwU^SN zm?tpkkG%{B z&03F|w(W9sYNI)S7Q&x#XEu0O+rTAmeuiuiKM9?Oe7JK{y)Y=3 z=O(T^Lc#~7F4-0?O0cDLhCnKzx?=O66wbTa2NNlA5heUre1nvfTgluxqXNn$c;lLy zaEuOPIhEtCLBo|RN577jNtmAunYB__=`)j)>YBy%G5G6O*ISC}Uv8mIELAy^e+D3{ z4$Yi<+Fw@s-xw%Ac)b0}?Uhv_ilB0Y2cO+m+Uk0uK6(B}TPA+$=5mJY%0-okA#>EJ z)3q@~&jUMa8_r#oEnqZl;gVepzx8k}uz9845EW3mTC=%SwcWx7m(0$aQ!O5GvN#QD zBRW2yJl6Pg_l!6mJ};PCy$I0(D&IXf`|@h|eyb{}hvB8w8P_AH!$ zE;nzaGWWWTWlqp#UX88z;NZ{Vw(at7zgFKFOm6VGCu-ZBPQ>shmnWn`hZvQ9?%;@$ zGw2Mmo^GFcq5(a6l8%NPDPu=?q86k(GpM3`xqpc6Ptx%!=`l_SWq9t1;iV(4=5h#^ zG!Hj=x|3a`W9)Ja`reR%_}JIqr^m2>Lp@1PZyKgDoK@td7HWv$>h=7krs9>zz9b9_ zJGJ05_)f&k2G+L>-`%@cFLi=&?>rxzOKpg9&w67!7b+S!8}slJjKNg930V*wSy{nZ zwRPa{xAVxl$%WEY^|Uy$YYSy6A~I6d!YQ#)hymiUlV7)c1!6$~Uw~oZ+|g_Qk#ZIi zb&ENV)vB;b@ll4v*Q~C~E-2B9rk;Djp=X7Y@&r#J={&?P_q%-(o91gt+riE!4v!F5fa}CKLxx7){;Ax5S+iI8nl>H5rwF)Q{j* zsCwjNJ)jg>{G=S>>`zYqkP|1#CR)Yg921m`Ote=yTtdR%t6H3jhsAK#AL>QY?HgWu z4i}MZwjLx%p`ScmZ^QtFs}@cCe)~=ex|KN%4s6sSYT{I2N*xbT{$=~+J8mNu{YWhZ z^M`s>XQgp`@&zeuhfdVr^HavSO8F(riaBc)^eo}5Mih#Od_vits?mL zi=Qc?`sr&VbmFq`NKorQSMA|(DN8)MvLz}v-z&+os%X| zltpb3Ty^>BE%me-=jq3W-x3C3WFcm%%%UA-@3A?TtX{Hh{>^{)umAY|%injyb+38u>8*$N?>VDsXD~4_ zZ^5EB{J>A&_8Z(h)x~Y5HptfMk5 z!Lf9H6UZ;OhjuefVRK}!${j&Mho=pAxcBJ9T>rI4Xm__~%9tjIIbB-msA3L7yj76& zH#|X1$LQ3YPw~y=k6(BC-l}^RD=S9j zN*hUtNiclirlK?ST${CMW*vyTi(Rv-)b47Jn{g84y78rcc*|${%T>9pO_@%Gqx|{< z)!fx~lM@tvM|FnQyFPEWSy=T+TXLmw@@MPbnhjF4WW|BBh1(W3RfxmCdCAxNk(JaSQTcImvmeY`@E-Jm-W^2-&~ z6WXp~Dk`d=bA}kUKtZCP1LnH7Us1%c4bMX#LV@mK&w>NT>n5UFa+&?vF$^ivxuii= zL}%2#BoG8rN@pNQbeBShP+};QA$=;)$2=M1?^#0)J>3&-16l++&&$ABO$1|?FKR_s zv>fF?wGrUKJl+p@wt$Fuhv2=VP}kA2NrS^Rfk_ zpOWb~tJ+TNfD0FXnRYiQ}j0(Tv18Lv-dISac0JhKQ9N2S#)QZAU%6g0b zExlvts*9>mVqlS=#Oc!$ANxY_5KNWE!v~(LZdtyyoh&6qSEUkiIm0M%t0VM!ZLPiU zfFhSjAglP>LWncc{)-_RsO6p_fV#y4wXe~ZUMY@)+SrxWx8HX8Xw75wYh{JanF&kN zY*N!q6*?+pA0*`}3!YZU)m&BoG5~);iqC z@=V44l{-fDfJjMI)K`ip`tcybb!&9x#o<3| zpX+i75=;d7+d${PVNDHl*~*I^uOJND+Q-_lUDdGf^ZwuJc}#rpN@ z|F3`N7hnIukG|#VCGWVho>kFgH>|B{?;Ti8ez?6=CrhWzIj_pfm45HFwGW%oLF6Nw zYn#Iae(|!()pp_B*Sep?8!xG^-PK^fs2FG@rQyAiebd~+oJ&3g=qN4?wEK5dmLnq$D3nDTwM_6zG1c4MZF4hL@EH4mp>`@K zYX58Bv5AxIE9RJEk zut^hfP8x7_hEA!dX7brCkZ`cBOY3{VP%z-?x+SB(1SHp4DN<7C2{R{*i9?s_S*pE$ z%eVCP5? zvDGoW9LqW@h74oOz4Ff!?u;*qKRD5r2`>*X!`zGF8=-Rs-wv4B!20&WH-Dr(d+;pp zc)vnqo8y&WdLINs7qpkif~j-5yX;wK>Tq^?Rx&{n{j%>~ozD#ehCJQ$n9h!AeCtU3 zF{F%%XrFcEJS7B8jg)SJA2( z?pc_{QvJaSmkGo zgjI^kO(GsTs{zYY3&KC#T4`*~S%oU^y=~WqMa(WON_QU~)ejSZpBr-DaYdDvEhe0| z9~eD^-9AJyu49qO-wKYE-7q+)7QTP!5@0#4)lq<1jNh$9 zzVGD1m2(y>Id<^*(1B9-*2`-#h zO^+j^)nSIzb#*ploc?r-w_RRanVQ=GXSaT&qP=DO#iw5|w59a{dYFhs0Kq^$zxw$j z0Nb{A^bWESm)D;+EGvHu4V@o9EA(t!Rxh4sV;Wm}5rwzQ-BW!}XCw9HXNyPg!R9gr zF>k9McR(WZ=hQudLb;BV&ZVL~wx?_v8f1B>uU@mNiiyXWY@EmJzv!2qJEv|dT>bsW zN*tT4=wTW-LtM-z!Iq<*L~B!u!n%xOL}1B+(JzYDC~Nl=?Zv3J(6}H?8$oA1q1M#YV)@&20 zAnj9+7lrcF=gRi;4w+0GnPkY`x1-#4>Fg5l1PWyWj92_jLT>--Baou)(bjvzpfcZb zS+T{RFi7MaU~?16CKUIagP4w$__^uA7xrQhWvR)#+78{22)bx$HNi94(7S%Qd1i+Q zPr9w-CJjm&J<;6t!!rkJjCaj&L;Ja2m5)NIGaESW{zJ~_bQwyg$6Jjah9Nr7xiF@x zqu1E^W2j{4b}x}RMD0lrD`Y73g2eELA`ywt|i{9^msuKG)I zw8^DGqu@-~nz%C|)UlJbOOoK2=bg(x%#_n+Ed5g1CK$onmF5|_7_o|3Ezn3JxB(1!ZkI3gIQ zPw%hSPh?fywv}5}8WP7()wYrJoc1?=K17uTQU*A0xwm>z1)wx+*;~wfV@tItZx)$q zTg_YDMf~HFzap1rx@1{BHLzi6`}1u35xUefq?pl}U({N;s1{P#95mm*y*{?Zb5)u) z#pzO`-@bn0AAF(Q`;7-`fn*JEI3JAr(IfXTd7KelBNZ9{_TTbU*bQ8 zs=BuD8=WUBx%3>VwW85&6?)tH@&rXCsCFjsuU|U)OgMgOiOA*isWau_b*t;yE81k~ z3}oYNee0(+8HTSuH~L!z!6PTj&$n)>N4KIs)DuX5FBYI~_{`Hyvinr~kFp-j1Y5u=JjR@2vk!DRidsg*iZpbRB_H2$SqD_K0C0DI%_tu5AKwYKp z&6@b^)}nKXph~-TWla}+qxqu6vmZRT`YmsL+lJTv=;pV-ckznVS6p@Nf;HF7Ilh1H z+iUw)-HDc`WH zW+AhRZlf2OrVbr1yUPCd%S$tFm4aisBEh34OHX|Pz0K#;8oY%z3hFtUHkKWVPu7eI zRX=SqG{mUF{I<($>)gzMQZsOSh7_GG9hH-OR;av>3+)-t6CeE7~IDmo<*7=THo;clJ>uWD^ zKGK{~a5`yIpHUJiWkIAz!l*Y9r2~Mnu#!I>l%cl$BoTxS1B!E?hp44o(vM#)0p@6{ zifus!V9;-GTwA#!Sh-=#TnbSRp_|X0K`XoMCYK4GQ@E^sSlyvGrlXicXGJ0nEm3>x zE#ZKge_A@1&aqWLE7YWV7Ap{nvp#ci`}hF;A+oDu?r+p<9b&mOGD z9|Q{h4}hUdgQa5v4j3wooj|xxAcze=DQ?#bCe3u5afpvmM3Z6b zwko1(Z->F3K3T!Hbet4_K!w7sa)Mjem!LpXw5@u%lm&)}lQSrSatsNwb&M)VtHw`n zDRGUJKc2@u^pCZl6om4sF<-s1*2BW$1k-U?A`ZW1bwx<|J1uj9b%t;{1D} z#41!<2>pw%R2+=R*5(ZpPaUW<@ho`$^+S`dNVHm#;NDg3rA5vYrw1FNTwq=7{@V{1 zJ!OA#e}&r8%+c)FeYmzr@Ylj`ayf+I{Wnz0N~e}s`Q`RrQUoo7>2#oy$DMjIYAcI~Qa7g9=un9|9Lw^Z?jDwG^s+vWwjE(+KH9ShgmJ57s*?a~IB; zw_w(pQ{U-ZTJshzzWj=7KJelH{LY{G?{0tF`;O0Dw)f~PZB!kgZIP!+)pHU?tAxum zXnHk#pzWxFiVf#At198pNl2MIH(y*G>FIs7g{1P${AtHshpL$fzI9VQJ#^O<)x`L7 z%lP^B{eBeYe)UVWzf)bVjqy{bN6OQhkZ$0g*?=u>I8&l$I_v6k^@Ib*i=Pw*2|nb0 zjY4gFW)b(l{7P+GF!9yrYVLxQr)#hSe~ij>GaB4A%#b>giI<+hos>BYW`jlZ>nHPW zTvOdF_{+D}AH(1e+Lyxq;TOw|KY60YHS1TdEKi27$MQs@@EMdSR?r;=i|Hcu2~~<# zX(&dSf|SDMDh;x3SW{|mT3h!`ZX?Qq3?d97Qjq|n%T%LD%))-{k`Y6Q>z346ZKEun zSE`iGFu!KysLpkRr`r$Cs)zipTx>DKNp+rIfK9bm?`k*qYL`|^C}fosJz!25QOIhM zc$iB%<$?qsIHOUhN=4xevwMfL+p)8QX;z|>Z)7`AZ>XrSV5tOh)#k*wJ@4= z7*aE3Yx1k3pMPp()PXIDq~W2RwX5P%aZtJ*31>H^%bOC~Zd^Oc!W_-4KsZry28nu# zt^K8htEmK?GyGi~TRM!|Ub3M0^PYGVIy-U+q2w~o4=Me!XT`)2ojIJ&TiRQzkXzLd z`X@vrNILq1VQxTY&TEB{KRphg7=841dJJ7XtJ~*^VWq(| zs$Smi)eOFuF|&d7J&jj56;6Yfr+G|jY~r2jc=Yz0Kqr@>a zcZgwb(nE@pI&zPkGa2$=m_N6o1P5U5{7E|!K#ZZY0VS5Hx!(-wkV~5IMtXyoCnzQ!lrl~FoYQd>-V=`S>mMJWI3$l z>CBoW+USv+SXLs7XH_Iiah5G8{xBmFU(jA}OrcZ`=0vn%aE_m|+FxiD8D|tq;FqNi z!`%1I6j{Prde!10(jZVSJ#FQ%tu1@9NOK0VRphpEOB)He1%-%wT@7{UhVn(uY(%l-HA zs;$X6@RrLa{@o*0K(+ehzA}W)D1}5oVpV%A0&_Y8%{rS^eY%aM;Foul6i$aa!IoW^ zvfGO6oZ6OHr0Y{^Dfrm;$@?~dzYz0vf2&q*DC-$BL(P=H1mcbr6?W-~O1dq&liu6q~~(Hi`SU7Y_!P7~(ux&ge+d5L>$s z*NDEOH5OPx=e?)5Y${nA(vCE1J_7-TDy^n0H?J$@%$=T?{gXG(*>+(5u377jEV|@} z?|#$L>)-R%w}1a7um7CAy?V{Td8-$#Saa;a?lWgjPrl^(9e_D==dZr_ ziub(l{a3&DKfCEi{`QZ(_gxc;cf3%*zK(8l8+t#NV zb57H{37RL^Cn!g6M0`@FKJ%pf3X~VOA z+@Ou_Vs4zVm@%TR@L)(EHlyx3Tzgg6IIyq1_Tt`zdG73rj+ABI?9roVU)nL!S$*8ifSV zs~zs!Ue6Yd#M{PY`mNaGvi4hqnZ#r%ivlJWTnZSJj!|sL)zey^(f4xehtYTR+tfrA zwge1`2XuqqcwplCHARV^k&=kijcd!>j1-9elv-NHkd}pKRQ$G?r4)#Kh#pqa!$uV# zMj@9%38%AT@%;M8n%gufxt%#|QxntRKD$?NR{viH z#(X}1RlTZTMLf0t)qme;2H!%=Y+!vCLe!Gq8v72yS!b*BpTEX-uV8xF;mu4sx5HdI zk2%xXhQzZv+%Z*TIFC;w2*cb+g%WKJv1vA{IuOSwK|V&--5C0lbfT*uS?9V(;uIpw za&(c7G1&@Vj*V*dlPw|-BHkg*jU+lKaR~lcNq8i;{A^#9PVC|`yDH&F5g^8^Zo++9 zoeT>IJvUOY{XoTth`oA~#z0&M6#nGK+a9qw+z~le!E}3&M_eoLjWcoH!rS`t#Hp$f zmkE3c3fZj|ut~=mXAGngIy;V?8vWr;`quTunc_(BM6ePz5Dk+>kMhvzG!^{m6C>qJ z%SvqO^5W&CJS8g1(NZWu1y!HgG79lUX(-axC&GG#6(K#*N?j3o(}o{|bZ-J_QacXR z7r42E)1rd&tPl({udJzv#5V^b3IWV2GxY%UTfs(FRC@U2h*>V3WuSp<`P}vgf3f{! zVTcSpvA13feD+ZL)K^=kRea(`3GswaW zGyg5UfZh1HYEi9o@|$AOfC$!pR;P@nARnIG6C_3U?zUZsrylWWo+lUO-B(xbxlDBh zH1iXF_mL_Ko#{E~AHB1zq{q^ejlcBsK-wDb&)dgzlAFR7&(Zq8)~dx5^V@pu$ULP1mm2@_apYr84VzwAT8n3siM# zK;rhcp;@zQYZ3LMZcQwI@q%=xRjRo z_m5VgEZDAY(+hEP;G^w_i8;{0D^6~1L-?InjG_zTM3fz*Wl?+bbUn_4{-tL}zt!6x zW7*W^0mvOHvn$S;H)hq8r3h+i<)Ye7dSv&+@skt(=8<|x>YA0cITeR5YPI*st{2{_ zLwD2vSbj{f{T$(mHaw-{($y;`{?GfWu9PB1f9GrU95i#h1#E`Z>GsYk9!_FoNDGcy zyt(ZxQpzi7ZV*4z-q4p;UVTjB=5;lneEz9oCP+W10q&LaY2SADL3H`{D~bZuh;7{OWkTaJg2s+b{TnE8aW*qCxpezw#!n`LPim1%l(UI`4tBsY8u}eUS|Bn2|%Mjsr zjvJBfmx0ST9Q56*&NF5)3de}hiD%926oYr`-nw555s|(ac|q0S(a4v9JjO-CP>Bgj zON}ABS7-Hn4Cll1s^Dc?`9`=mvwQVjhM5hl??QaX?OwsFGFv5hIv+6CXK=U7G3#e_ zW=JGEymt;UyfF+#&Z=|lE;99|a#jsf_o}7}{{Pwg^Ps=3^FHu=32+k#0wh7KTtRUY zSCKSj*^*^#E!lD6BwnOsl61E2SY2H;RZ}ySY1d5E{4+J~WU8jSYN{)8dPyqX{IUV;?0Si(~m7w10b+0VJ> zp69&h-XDOJq=8hqWV3Vp4N|xkg0>)MNS@LJnRD19_L5#-P&Q>8auy~T1OHn8hKtLH z8m%zJctQthHbPG50gaigATv33nTb?jXklCBq74gfMxA-ykdxp*KC`c0jsuxB1{9(z zCMlCple}%UhIRGELJl*cNV!~CQ-9~I4-YpNcCyW?uRec99I>W2U*B0RivSl4!}cBO z3F~>CtGBvLMzOFqWayRAK&nDXxMTo3%=MdlD^Qj&IxBFd%cZld^mDQ|Tss$|R%2gZ7a;lxPQ4Ywsq=l3wurc-h+e;J)m2$N24vYYj>I?dV^uO}? zN+a>&a*6Rb@2lNdF8Pw=B%e%XUMcDb{NZCuKY#7aFBxn$?@)xR0^1+=rM)~lx7HAe zI()P;D7<`(x;UQY7**h+b+u0ial)7?8XcS0&gelI)?F*^Fo04T%eKb+(ye7`)^Q}; z^{VfgOV1ss&v-FFZFQ1UW`CwW^R${;!H^bF;Iw?qwU;*XHY=rQ2$Vdy1kdfS-AUa1 zn>SW+g_0N7Z7K{c*Y@{TL?DYPSSo?+%`Djv0XcR~{n}kK`xzj54?o|r?IkvCfftz| zW5c+;yC0j;D>y(p&%L`UFJ-FBPycBJQ%@txf%6X@t&JhU& z5Kf?yblrWC->(Fcc^(0j=#d#rURzpq_2$(t9zNr`D=yl0@n!FM=jE$5TzPQQkN(A9 z_?sX5Km6@AKk~Of_*eeU`~S+{e$UVRjh9|qyWzsCH(hq)wzpq*`Bm3m`u3~0U3%rV zOD;eC{L9vFzI5Hji_Sj(g0<&v*}Qr4y7lKRoqpEoXRbZ%jJKV==8RKTpLU9Xp0aw? z>eZ|I-_j|oU`{Eh^P02QuQ~IKqpzu3ed_AfYLPmhdg|)4He7JtdFS2o6F>bEKmM-E zfBygZcmLksyXHUpdw=FsK zgP|^=brc*waY=>6$ocZ&*^)$Uo9d24)XtZxY|Q)zKT_Qz(KBw40Q5Q%atEmu@N_MA zxAdDRP+#hKs$N#rnvBWXR+fq`>5N+E)SxgVykk?HV>vQ#PVXvGPFw4-%4t3}q#IP< z?zieZ6wc~jS-oL%)#{3~F0GZfl|4iO4d-K2k3aR^#@g${DXJJ><6^oc-Mk(t-GiJr!8( z;;!_hOAJ++>ip|_O7FI_%T1OP%fVS!M0t6Cxzc6YF8EO56(W8?7GsZrS-njYgS0OY zF~Nt_7RcBcn;kJbEaRZ%p4}A+y4au=nR8eiz?m*jqH8x5%aK>B&+qPytC66#4DNA^ zCA0*Fgsjm-4xkJD6flCvzF7-B;}Q_AoXeC`ASZZ7R2obso~M&G3UuZt#xz z4`8Fj2x8Ts0Ye(3+$lL2h|(eRleaDX;k!cF< zj6JqRo%0tSuenZ?x)OjXG!?b+iOQ&lm-dH&H$E&1w?hE6>)zdd&n;dmsgG& zu4nev{QK3(_FZ&SX?5GZPdw%GwaH{ ztCarPj>NXi&iSjc5SaJpcj%c%j+Fatr&it-Ga{ip&C=uxRh8XLM5SpoZVEzCE?hqA z22{w<9^F%1!;Huz@PGQ+ERj|wp$zC4216J5^_}T%@gYGWwIBq8HqxPgO^nrj0S{^;~*9WuaMvbVgMw7 zAIohZ?6DjqN&|v-#1RmyNCV6c=yKu@;x(>3vqBL0SY*7;kimc*`)V2G1D92b8{?mR zv4#=meRzI%4|;_W#sMQrcn867Di9WzS8S*aH3MSIID^=L-hh@l9XzDmQrZxKYKK*i z_Wll_b81Re_dc=b_IvA#`oofIHZ481=hctC{m8G~aro7vhu58UiZuN*vZVE@6F_dmGfrQP4$f6;lTopSMeuiCQahRe5{vT8Ls zu`@t5&|k`o`Mo^)Ni2+ZTs2UfSG~??@Sd?UpgOl>h^!3ZeA_*q00&r zA%ZWZ1aa!8udMX~Dgc7C8i=zjq%o$(_`7%27L;M}c0U3(Hi6LV8i~g5#m1XOM5sF!s0&`cZa{%To&%Aa-gR zH?mqrj#kDFrZR_x5e`x~9?d|eoEgZp3Ouu~7UqsgUuE{|QvIh&cEYE2=Sxd>Kl}Q| zjB;R*rMys>!ts!2pE3JH!+_(-Ii`daAoMG9ouJJ079lcQ0Jy*#GUj^@Q%~KM?#=Hq zoM5O=!VeFe+=2ByhSlHoQQ*4?<4-6ngMle)^UDh>!->$mRj`6d%d3KU@z1`IzJhnl zF*OP6!Fude8i4phnbMrac@)N3tr2Ye0r47ye&{k0 zcNsesqR6rFtfEI8N;30@RsdOlb?onn4Ddw91yDwmBbFdDi3~C}-@Ch3A}MmW}FODhZ* z&Km3MJL?smNRxrY>q_ilXBt(a4vpGsxb_{A zqBW;hfjW+Stv3-!Q4mW`Tq;BwKvUbtPMN*-MGh8KJR*>(pZ?SO+RM5#E9t$nzlwqe zm@$9t=?YPZ|H75C2)q2>=Pv=Vtvv0m2l6`7sB?ay$!^)U^zZMVZ4+}Tk}?8ju(rNe z^|s0wrBa*MR@w%n?w6jk^ws&t&arU3H1=1YDVy!5=qg%hCn^NdeoMQ9z1rx4+T`*J z*A^rCCtsW;gx7B_PhmjLrbRcZY@cGP1SSa z!B)o&mv3ErUY!q|v31jyZ5y{KfmA zOjFBFEz2-vVKL?_xYU0{hIaveWps_34(#dklmjSd{Hja1bk`*w&-S~#eD_4!Mmf4nrN zN{{kP5emZVN8dJ-cVRS($ggLJ2`$;jq!n-#&3Q}z>z$R963&<)oyp-EMx&q0x1G9l z)3$0paMk&>Pd3J6RLm>|hGdG|o4a4GO|tG0m*rjGDQc1N8qy(Xy1jqcL7u8?K&c>H zfC`msPAlX;yQ3ahhE`1wBP)y@bG*nDoDty4NOJJ-tjbOFZ|QGyL^X+4Iph#^Zp*e?t^5MMHXxzEEzb7-DuKmF9&FpGtpCwhAk zb)tkN^y8ol*$~K)Y-Q3LJ#dY;fEmOlfN0Fe_PZ;(+_uxctG`dt6Kym<0t-ZE) z!xB9q+5w~jG6cBBgdx}%<;)}EApJVw$$O9tunb=Js_#q8M&J?2@%utlt6wF0(IfbDS4SPcA$mIZ+ z7xsRo0a0X{6+i&<*+=RR_&_xB1=f7qA~Hjkf^*8l0AQA^JT4WcQz-Itz~>B1qeB3a zz@WsH0y2fE%pkcI33y3h!_-+-dk@w28I$<%4hbkFPMMcP2v9|Arh|b9cT9~(@c!+E zQ8wco?5s5(mA8DcU)Bf<75c!vyX%r);d5L?t~SgceqzG4n?A$=fOyig4#I zJYLxaA0=i{2U5GezaqpE0>A!jWhrLN6p5JH%R3QLKqKSbv9D%77)_>0OQ`WWWC*5c zM~+sRVF2ZY>*|##^veJIj!FpCVq|tS?t7)|Ptcy)ThRuUj`j*BWX;jr%?#Z$aG#U;4lf7 zgQ%P%pTPK=KvhEC-{OF#F2K#^Rnl`@k&vbk2&WSnsmHYJq z4I^0+pdC%M`-Vy*vBac{*4J`RZf5|G<_{h%T?hy{cTJ5j?p8o=G4>(_5LFWR@}7%p ztrk)foVvOMQmvWXjPU1J(AxPy`4m7Z}h>mf)pVYRT-lN

          RGXVK+7Kt%Sv~2D;Vh+cr*s-2sqI>5QL+ca@VsP$*03aat|1!*If zjK@n3705)=@S^Vtf~LH2OOIgv7Yp=SL;)`r!_=3aTzdbdrPOl)#&OrPB|rB|`a@mz ziYfyX+!r`OVF?!_73gC83^O-xIcS$-;OCuoNM0@>_~qq541=8FGrL<( zyZ{Te`Fr!=ir57qD+@dk7!nfd|s5Id?AzVKLm8YW=kHT`=_$Vi4ud8!wbF{f0nSQZ|rH)J!h zxO~IxGq-^$%YvL3A9ff8=ghqTq*@rqGy7)0{a0i?M-P`dYlMVL?GT-d}lv-IE$yfxC%r8Ds)|QH% z1*Ubvg;GjTn_`x#k7drCPV(tatusYai0^motSru~G*#PsgGtnxc~cECd99^^PQ7$X zGiQE{HMt89~xq0>U9@zZpMYq?+6T&H`xxWCb6V=-*VVU~@oL|v_bolJ zyZWT`KGG-^9ECEtJ99PP3rp|YULS*p&#kJ9e)Xx6mWFc}i&8*&(faZQ7c*!fkZPF3 zf!k`w**Bh@DH5^7CzlQcy8X6wb!(Fhqlqbv3#3WcwUo3o!3U{wHgZbNDcZ4b z>A(Kb8t_9y0yUzI04AaS{9`o~+_$S93cRC~CH`?lQ)+aKw4+(mtDJnlb7#G2%c<3; z)S^!u+|pl-iJfklLq}>MH9MYyCoO|mXrjnv%7hU9^xjzz6hvEKYiYihV;}*W(_>@2 z0@&N%qPp2F*=t6KU+aO_jleKN`W5FFJ7S}Xp^v}}3uyk*RdrYLks=X8=Kf`jT8437 zId`<$%jaX)o&&ubC3iqgND$9k9~H3 z6%xa8Uq{NWqHoTIN4J18&|Z+di=6=e?q};we-cEgYWo2}NOhaOM_}^xD`hlng8?q1 zG(Ivc$L3@GDg(?Qe(9B^r(Udb6b<~a0f_JjW`GO1)NK?7 zOLXP(=y84+@p!Ou?FqtE!nZH&ErhqkyOZ!k4ks6|PQqIcGAq;89aXA z{(+aM4EpKn)qo)xMELa5;Xw|@%pg@`^=d%0IN!IsrjLPOzu>azwfzfl+Q{+)>V!KNMj!DXY|WBQJ2LpR_R40 zt*s{nC5Ub0!$cP{CSjzGD1{)&DYElwEl)Z(8iE}1Wppn|x;i13;F%6)N#)$$ma<2J z+W8d>v4ogan~cgl2_al9r{G;?y~$-Xmg=|H+lx1BE^}3VvVRVr((-HmtNF@OCbtG5 zyI(GX+ny@d(#pv^(db|Z?%myQDs?XHl0awqd0K=Ga@r$O?tV2g+j-2G;hq1{7nXi< zd##~Vo6i4+&nb!s0TiKkxP+u4rjn+7^~UO}_wTN}7bODGhEL-v%M4{u|IPbm+p;kJ z;iI*OD8dEJeM(eRND(jTZ4u%?)F=A$^PlQS60XrX#ZCp6kblyzgYl?(-Td7vhgx1R zLoG|N2$}**;!gLP%BE!3=dGz>$Ef~v?=g|voN;PpFE?+i%jjZ^av~#c4yutov8TQd zxcTf_y<%;=2G#EU&LdvOu9qsv>71-e(n}^cdGUPr>bRVi)d$MumPRi;dNSd4BxgdP z#0{P$>6X=k?WunXabGlG{>F{9=-`qN_db6SQ{*yX(eB&ME=4zQoo&FZn`PcDF(MP> ze|vAS1WCTKs%tjQ)|cJCUwgWweYC%`W>}`>vU7_|aNHxfzVYl#^_AzB;|kabaU|5) zprkhIK2!jVfbT%8cy#Zsx@~nNNZlJH30jclZo@-Iie|^YlEVwJRZ9p50htP4c)Yrw zf-1(Rz_vtHL65zq*Whx)mL=WC(rOA0#Nv-;&FR&hNDzTv?A~aUfG?W%4w>7{_^gAu z4NK#;UhnHb5r`%9FCVTm8*ovbkwkDTAiqwiOJsCPry^5q-Cs43zWlAtn1np=+{|m2SM<6J5COIk!u_?UcR%Uq zVMs$@apUB-UzLBU??l}-<1Ml@=q#j{*D}+`uJ0hcP>(!=CygXvK>$#uSnL+?rAjpup6P<^I7%zC=oo9+hKM5z{6R1>8IrshQimCIprPO zYK{jBMjp=wpuB(?yNyQG>^%Ql&FjK~K$m$2PXR_Izovf_CiDw5qdf2-%MnnOuaf}C zP^K2~nB2fmg^b5AFa^)dZve`gg7j=@1@N>pT}CtejC!Z{(QZnYkJo}E)gV=x8w;y4 zmtqXe|cBQ3~wfIAwzSxQVXxwR&dXu<`LSd?H)=H#E5xP!CLD9YD* zYHp@Z6iQ_NB~UI+*`tfJUHj|pu>5d1z~Z2y{>s{2^rCeo}t6_=TXCJ8-22pNWyR^M0@yh5OR5KtuKG+j~W2!0wWmbTBlyXG()7BTRuQK@6 z-UlSkPwuVd1|W_9FMp)u5JJ>fJ9$>vjLAdPF0naeF} z>yOxMLqHC^;IJQWHF{?qdM|aLZ0>H4(&09T*WBpS;ei6bp6{jDh&3{#ip>A>dukYQ zNzRjd>Vw~sI+vyElwwiHEx>HqV>BK9k&6fdQI>C}etFXY2!$aK*gKdkHVCpnDJwNh|_n5d}j6tM#xHa`-{x;H&xCb=~W*1JDw?P%hXe9nM1EA?fvrX z+B52{YPVIE_O9DpV_gCG-ZGo%Pq#_{3dBXEMDxtg%Hl3&plfA(WU3qpA+7(sT!RN z*wJ6ot0W3_?!Uy)5?!FijSe74+o>WPak?U89vl^ucHCL5LIjeN9Z8<=pAhx_q>(3`GDI;(me~yiIkV`NG)|=5i#mbUF@QFYOA}x53GEdj`bSQXd zv!h)v&4_^lnv@4`%J3kVfd^AEFfOx@p*)Vf{|E+6#18EPGhpCBxCZY6z%MjM;E6r6 z3F>lKkakS515i#4TMoCAy8y={J09<#KM~&ugtshXg;kv-s+Wkwudx8Je4-(U9Mta!ZLlHr7Gv? zb96ZzPaSYM#f-3I^~yXWvyg@7w6fXlbnn4)f7GJ{P<|GnEjT zAm>y$hig3SVax^~Z{JZtSCu0mi#7|)R8N95l@J=2!{t2nLtw6EHW@rhwy6yl+e{hB z%=|SU^NNFb)tVwln#-KS2(XxOI#c?E$Lk?FCWX(=Cufm^(F_}bZTk&{Pd`#_4q}~I{s~Ve44G^w`3LKJI;Z!n z03Vx12hUV370Avd43^hkf4o>kc!g_rx=&aRDbQN&vKEEtg#0vOgBCA4EH?JpT8 z8v&}W+f-$~u-~O-MFzANUPuu8;l%+sqjw39tTrQbs;TGemhf4yz#D_%4;`&&3oK+J zCIObKvrvH?HfTi6^u;I2k+1avheEpOA3oZV>=*jvX-0`yf*=;^HhCMGsP}I#Y0i5O z)y@~=Kyr0eG~Ty+_C_9q@jmrp&0=BniZY{r8ec{lk2rF3XN=Uva&lZMRMyu^V9Ai{ zc9NkA5p$Y(i%zjSr2)R3$} z|H73e{O)IKw+ni5oXNqZKj<*Ro;Lc(<>fCSD1`31&80}PjRhr}G3G5HD)f!$O$*+< zwN|WC7GhDa+@8Mm$MCpxqza(;w>YAVi3fIe+8_Fq#{b95+ZWjzBu1 zv_7rfm&`*GPr<{_*X{{ov-I<>3(G;gH}~HIVx9t*Vpz4j5Pm`T7Kh8+J_#~A6P(%^+V9*^BxoV#5g~u{M6JQSb4xvv%F~Pk{qVh^ z_s{fQ|bpIK{ivT^F)!u zjX+%_$0 zsW7EEW_i$z%K_8!6mH?s$~^xBZBXbB}oV(}(igu`Qmbg66_dQtzrn`?eaM{nFxlTqhSeWNyB z-FiXw($%Xg5&GPtm7K^TihgP7y_d|QN)&e_du8XmQOc^mJ)NbIS>gKRZL^#K{;Kn9 z|BO!gzK(ZuaQ){j)2hQL_ICsED-d-6j z46<^n2Z!hO&+ZO*Le}=MMu_`SH$x*b5BDMw%UP>Le%cy~qt*g?UxqlxFI6U#?yt)? z)XtZ$JXMj*Ppu^a4j#GnFjNfZAGBfyAw#(PmupyJbA8XQ*Fj{nSrK@s4Fg0WBgZB7 ztnm@zP>Atr4R=9;E6<;OVjMVAL13}>3ZqLPGOQZ5A=8LV+WAr;37oz8*eges@V9H^ zM5aMlM(wUA)+HKkk^ADKm)F{Q)+I^4yyNfIL-xz>oApv<&X&>+uY-iA=2-zzY`_Jn z3+c%)XasQ6S*1GckP0KD7p$8#AF`-0swf!4$zhKnh_fP30$8fYAXe zEC**V0CJ7xE}hG^UI}uU=wpRQ4XZF>gt+(eMOf*Cw z0$mE}tNrzK_uXe6nJwGpc=E+s|4JG!1x+PERl_SVYnSMd@YThl{Kq3TY9% z?&xd#?NJ8M1*yF8tlG$VTfZ_HYeiqdIl4S>4<0Q!Si~?NdEB_B99OGD{@*`c131tM z1Tq69dY2W?_JUszKEHI!)-o)PvkC$C-C3_T4a$TRJ}bP0xGZBjV?4F@QaQMpL2TBw zdJ3cv3G!hq!pLQZvNM{WxTJbr-j3G_#&bYUo;!G}QCj1G&Hed&ks$$;G2NYh_UbZN z$8e{RrcD0LTW3~z$}^TRf_yNl? zATmvrwO}sEuNB+5uB;mdj>$fdX}IITF-@AnF{{$(Q$QBV1$8la1^H7r5wfYZ%js9H zU4WGxUk=A4esil=;Ek?corLdyIJtmz5*FbN4KXJO^S=>(Z{T?2=C|YN%46}g>X>B5 zW3SA=pO>i(nOTKNd@#zU2rFoZR~sQ`o!);AdRu+rd&qP?9&f~sTDnYATrQ|H4;%Z0 zQ|n0|487NH?uunT=CL|3YBSwY7|-TWE5QgvK7vSdkONLzUGIP>$J0P)@_xaf4}>z8 zqmVOQFu7#UV98X^r757?a&3RXYRlTvOE@qqmF;UX_~+XUa?V&?lTN%20W{Z-D_Ok? zjWgdf-9-F*57#gE)mlSO0W&p>8Rmi{$dDz*s?rTexNv2*NW^vqN#D1tJ3 z+iZ5ex__2Cz~zcY|7AOtXQ>D#aY=1fE1YQ|4JCSoqMUDnD8mkxVY0Zy*q%h-IOMo%9?m4sw+7=5y&IEL78?BR$+(1tUD?1VpW~2)z1QtsRK!FJ4!-m9Y-; zBmKRwknKA(`xAPMiK+0@MSPURrj`n|dY94C5TSr9TUQiTg|2UZTn_9YkxaB1!2Y;H z+QB2ePP(SnMr)<7mtgiD?BDFmDon4eQL3QdlI+vU62+v!*}1e58;F*sNo} zZ+i{$+s~<5fo1sv8sB1uvfdv)Ygt> z!~A_Wl4B2nj9j&*p^nW}vxeclFYciHv7GNJEno zd&v0@57ehEk(H6J)!H}v^RY`%G07re_x~6APh22pT2g+zTZH`riwwp=Aa?NnOY1L5 z6|P1`va(0v(O0WA4UeLSHxLIo0(tkdwQh?>Hv?3dIOQ`>Y0YsakUBj?l|xMU7+q>P z1%n1AWK#%xadpIM8qk-(s1kuNK?V<|WpKRmfFNVAvO>=6;rqZ0h*fc63R9W^kchK- zmCI3x*$cuKAT+NFgqXJJCsX1q;;#(Ly*M5abt0CWIIJw~4e2sAz9IUBnCE5o?>?MJ z1D%BL37lNO`tu4#%2A>T>x{7Vb#>aQ80}@@t~PIt~>s4xq|yc^$Jm% z;zQ=FO~H6p!K6HqNyM2p%$p!HbeKGp8x2 zl4+wvHjK|i(A**hKlaU94VgL;`Cz&n1|lcwfF#88K9Ya;p;_i5nK7k9V=89OF<8Qt zdEt-{?}lwI$DbB4n&6Uo&8FF9xFXPGR00sji`IXuO2HA0S-k>d76l&zO2~X~5y+Wd z9%KqQ!yl|K(caweYnZ+#Tp%{+u3z6-e?Om!<-*1_{Z)}e^+{SYZ!EkZ{F;qbHc=>h z`u!18c?}_k0~pNf`*&Arp-dpl$xJ>I*ff8o!ps%BJ4+od)+!@r%xqHIe=RrLbw;W0 znZ@$XEVg#v_Z}!(SS2ww1(CxjBTF8r_SK#u;6?0AZ0w;TnTbMzwqsv?aV8&cx^YVp z7uP(t2m-(VaJ>umj!l&*v1Ay@Qx}NY!wl}z#S&F${`iSXYYCL_ znmid97c%n1Qv}LcJ#qPymoELry=4Xo3@hQiTMd+!UT(XxS852PT|x7KEJT*B`DQu| zEe*bYOT9EV9IuYlEHDvRmQQwX$s1jAP#GB`UD6fzoh3Q>fJN=9=wC?G(8MwoOFqw( zl?4D#mU85icIVFObqNa$$>^!W!c#5-^e*#y6l95QPqA~2C>SjMDd-r=ckU8F`0y1KCJbp!v%hKD6P&x*g%8Bj8=ajS0 zAE?zUNMw8FgCt-fcD|D^))|%c$&>+N(Lm#zxAPNcraFh9glsDUn1oK3c(7NZBM`n~ z+fDCM>uVQP4?I^AU}|}yM=*w+d1`flS6}O8f~I%CM`Ql15?|=b$8mHy9XmM*vHK4% z{oDJBMr`>dlH4Iv({qFtW9;ufJo_LWcW~uwSa##YW>wTVKk;HUw{f^)(l=dt0(E+z z#>(K}5QL)XPgqi)cO_er1#K6*>(!KL52zkYW@nPJt+J>F&y z5zD_exe=MHv(zZm!7FVPTcogVK~J|eUeNP95Js{%pZKZ#9hzU{>>{k6tCxFo&||2 zmdN2tTFL=1!kbnJxFP8B<|R}Ayn9#KMo1R*kWmzv3W*sCv?Ts0flDpoQdfLfB74vNcyJ=Zrv%HNC}4%4P36a`{PxzHfFWmU z&jLhU&MD*pKV=yN16+bBUWz{cY6Rd}p4F>BD05^^JdhygW6nrIoG^wAWF7+|A?i{) zB?I9#1CxV2y|0qGsp??>7&%5@K~A3c(emh?66n!xwyFS5pMRr=RS`0sRRp4U$n;>A zB$&A10%pSp`WL3MRuYKD>o(VG^O-&lc4aeHGJ{E3Ci7z#m1LI;OIV7ya~QU{%o%aM zYa2?KI0By7QzUe_7L)=C+mCmf`i6HKlaG{W2~j%By9Y0#7B77?nvTijka6C}<>*kSr2$M5FLMnU8>^MI_roeS5#$hxo=VOFQ?^ zq79ziU#Uy{lA3BDu47ut+ObU4X5-%TfZLYl4R=WGA?adTNt8b$O?o!-JgsJ zBQ12PN=RX(6J=_@nX3z_Rff@fvwY)H&Iu2wP{kg4KmBmoNz|tPH7)|*bMfr=<`Iab z)~x1efZ~5;9?rG2}n9ccy5=n#z6=sB-ArfA5xBja3E+ z$luDWld^(A>1O+AER4t}`er(9S}lMn54T zVa!LSB9OsqsiQ^Dynb^nDGHSC;TD6W0xw46)ie8Mjzl1u+&C^vTBmUNElb@`*#Vr1 zGUmTyWBrLsgW}5BAf~p$-ai(R&O@>?JQ`)kvoFx+2vM+Q7fG#u3EgOMl!fp?(HT?w=E zrFyT>fVt)D+HrPd{_O&X8~hxDK)(r22x0;=m^%Zv$-YcGO~6y99bw+YjLEClByoP_0Yaslfk zya8BwOnDQ-4VG^WSjS)T2j3z)v2krmbK>+7o+EJL!{`E~%RCfKxHJ0+GBne33WI)t z=bs736x4=t%mA{2Gav+0n99uT6F85DD_F2RFn#ncn4JP77?ywq7eZt18)Sr}fGd|E zmjUJV{Y9$mLU%J@e%7U*A)C4seN! zOtq0_8v4qswH^eY?CXRSycs@nLWIF zQ$q5A%Zi>Z7&=(mb%If?=)u&g(A(FjI5}Aap$@ETbsxU0 zwiJ=@!OJRPM)~8to(mE{re@g)RI44F{n?yqLC(rWt(tX+5J!=)D1#?nEEV!UdOMr@e_c3jO<$>KxSDfGN zdtEU~5jXccp%@84rs}l*behWqlLWQm{M!%CBJ7fF{Qc+^OTT`15&ZbY6;B3;IU($f zJpgg!!#lI;l_S08GGD@jul~@6Vy72U6$A^30vpe&Cc~mFzVLVz44zW-GfpjgLZhlS zU3TZh4q_@#f9w9bR8F9PJnCyZ>OJN9?Xb?n!&p|FQcF$-;WVg&BU&@H@da^|!h})j zx=m%UTJWP6)_c=v#2ouQyQ)q3t}H*_cc`8M%K8#|2O}TsugjC4yW{N}$|J#{!r+h} zc@Q5x;fCIuse*$XGz9T;cGBb*1ea)FVk^(g-0eoi<|;X%1C-mjb7L z;*#Do*zYpCyU`Gy16f(yO)R8@-~wor9khJu$!ah7PxhD8Amg1XN0x`2KmDiGuiO{c z^c!k!t7;B~Z`P`rF#*J763VdtlLlsHi|5yO)=fbmU$V@HjOxGgj%u~UtyZ#Ls7DfZ zW*hMi9jRvJksE=J=l&Uf& zFyrPN3TObBNZW|=)C@% zmf9>x<-703jf?va4+scH9F{aaP)HcXD%wdyz>`#sgBR??<#0IwG2Yrn+JE)Y!(Tj9 z0@1rW;kmXyi>|)ZTuP$ zz#e>rWVJc~qbE4KQ`Q{oH;7b$T$1LWJ<$px&F z@CIOEOnDO{)Cw#Ezp~g9*SNAAQ>E--8GwE~#s-|Ay80`6PvQq)3~#0afgvrnO)VVs z1M+-5Cgp%B-pn#T^))3>&Lj^$-i093m%x;M0T6qBn=hjG7&F(5G=M9Q_6Ur8m?_c$ zg2~4hLKgNwYC)=Mn%FPI1?lWim9m1_&w}wJU<_q2qX1$_6*>V(IdA$X#}^XJCP8Wg zF)YGsM%a@G7zJ4(#v@0YZmtofLrSoxziMH+9A4+ut7fy4srw`lYj*qa3$-=ljQ%o3 zSV9(pU+h^v{OCx?U{-Q*mIX%isx3<;l71fz@cZBM1(FmxZ=+)OsxI{&*1D*tD zm_tXGe)@`~|LJSJS)=nS*KSnUC?-=hvetUy-SDtEBN~l*Y}w7>16_o zkqd2MVLH@ujOL#GN~R7I;Vu{-$K42m&y2FXmx&#^B|w}2ibL$D0y{2=z4$mArDX-{ z&nzKo!EfGIc{PDz`0@T`6Cv-uuwM1rxVF4tb5s+hD#53MKzK-cFF3mr9`^+T2LfaW zZhxj^%I$%)IZGrm_L*NxRV$t`JN76CgP3aybSbdpqII>n`PHXp9=T_EK}%L!2+Sgd z0wj;_+*t%NpWFHaWK^X20QR9mVr&$0iJf6v&MqW@&+IEXtTGBE;a4UHP3#N?O&0RF z=AU!85U`|<-URohC#qaD2)b9N@N9qf9^MejS~UE1`Rz5i>ud4;F+z58(m$(yWQ^ZkH^P=^wo$3yie9JF^vw$3)4fox)&We(m{*S&Hg^+fZe^lgIy|$M zRftt-H8jdE zondq^?GA9cbQT|sMlqLR3Enxv0lk7u^}o8idYaLOz|l$2M1F<3pIpLg{8U!U{ules zLYCkRNO-}hfuEv?B%?M?h#G zr;tnM2n@m0{UAd#Fpfw=7EA~T(GV5YhJnAJ@5%vKhW(BqL1+8oliy-DyI9-r#Rmr2 zg5JQW8puKB45`VgpU+hTk0g)U1sJPWVAM_(y)G;m_9kFK!U?Wk8Fuu~W5SzTy#lZ6 z$o^Lr{{TPW+mFLZ_+fyP3s@&%29g%f=>;%wuP~0RP<6Zk=y)ts`sFlg$CG1#yn4C8 zQ#hWkz{2VkK)+!2i4UXZMhh_IT`nh=Igi>D7Ko>mz?@?|q`|=B+TQquK$&>eo`?x$ z3WMyJN&yU-JilpFmaInFU{MQ3Ip@d;(#%OAy0UteDxe&iFlr8I*g!6Lp`1%+#yBSq zq0ekmc{*!J6&RwN2Qq(zl%M1~FDA`3vz+KvPTpAazxj$!rn3hQ*Cjydn9XJT^0d`U zPrq0mN$r9m$K|izJ>wb(d~;|raFvPAkdiv{sPVYGgp@6hkK>w`IpN_jKEp>1DDmXAW8!A&fcq)g^w>kS!a@I z7YF^y_(=;$uTZOs`|P~6(@q!`3PplUK4X*-w`Y|D)Grvc7bX1M`uv$HWKZ<#A?{gt z9p>ZLR~$&V;P*d9arJ^_}20AgU1cX8sWw9~+P8Q1q2ZLSYpB0#XBptARmlFL zqOs-Tb7sHHXvEOj^2q|CgiGWT{QvhK)zbk~)AuUU&tFr+CfA5cqh!yWD(b4E9;jV) zv;)%kMFn;=2CLfP)qkr1FfSgeQ0tAl`UB6^$~&d$q6xc80!4=L>v|4w@9vrVjF{wposA_5$Ot6gT?v%`yDu(XeSXa`Jg0v3w&H?BABZ%Jsz{){MD-qF zLJ}yKF@q!82z#mSo}iyND?honS~ivkAmhbMS4SDfjTv`_89A#mzGVlHGI9opo^pH_ zZO(FBl{Nz8D60yK+8C2vYzo0I2UREm)d+G1hYhU2xuN%#SPlM7hi*YFnJ zd()2s9utna81$yKV|;wiY?V1P@xljm;snoU_!&N=L1SrBd# z0H(yDM3(fdE_>!wlkfR#X3RDM!I)bpm}HocWlrYU_0o)NI{7G>FVuWdS~xDL2ukgPo|_MWE#hL39nuAtV|}&OKdo-~N7+8Eq{q%-5>MK8x`3T0t#=pLt~VJ4z8kAQp!zmk&K( zbGw)n7i1l@NF>Mvslr7NRD9R$zb}Z2cLtk3+>6hjN^)NM=2*b51f@MGGW>2(H^)OAjvD@JqcCL{zOjwlpj- z*KX= zs-;UJl*nJazIu_`(a`Y@8TZjV@#43tWUX33ts`$ODZFz_zZ&y&$@~jf)^^AckeOr* z^wKM}I1Zzh zQ_{A7eNXkM5VI4z%cu8ti_eD?1xmG1?#G1sr>?dFvAY-U*iq6z*10Gm@Du&%F>P__IdwO|XugkKUr1i;-oNz4Cu*_#oHJ`H zqTvOBkp9B8MSP%tk}t-W8ND`Iw=gs2E2M}KTwy?)uXHm8jXTs7=(eZp*mCyFcz%(e zPfEGbo_w*Ejrg)^RoSEU-Iy6p4<0E>D#)khCoe6eu}FufE)8T{62pT>i&5JN$ltuN z&KMb-HqD^=;mb-+!iZU|o`W|u1gfgY%*TgvWT|}DWRTRsq$!_!xi;P0xufKeNu9_G zJJ&LUy?#sWG&BIle)RBUKE1ErbI)^-wBNXAwt1OM2O7~<1=8U~?COp6F5bqo>JpMb zz~)F6mU4E!wHf}$E-C_}Sv^g93ouXxLPM`J_R%dJFfL!|ZLNeT>p%JFK($nZkU7Z7 z_rd_}JPr~l0Ob%ns}`2Qm~-L#=VD$uG<&xlhDj8~L2$-lCb$#U&DufboQ6nhwtk^lMHl?OCaG znM?zHauO_Z?<2kTwEdi=Pe0P{F7@)n#x=F95UAGaH4c)s?@ilgFNrWos$XxGb;}Ob zi7aX*1|PL>59feN4O4x`|lhSuUP0ZPq^4W3nrElm7H|Jg?>VpP0;`)t`uD)OZ# zYqN>`hb|hkRAH7M3#a3Zfd>1?(n)~|O3M<7cYnNw87?sjCPoCRghB^C zeg2D|#%K0OKji0|`ZYIXpiJ#lhiYYvq6ZWt#?SAsPEICX7{+K*VNCN;!P&6K<`4G= zailA^KU4ddV3JeeRdyz)TBrA|NRof$^L0*hkJ_XC*B8@$sUsDid8BkvA#wV~vn9=C zB*-eWy5!Ch0lhAZCh7LC_WNJqWh4Tl>ouEdK~V6qJHQB+!v4cEGsx5{^jM&+W$cgi zpYB8X{DJy=G7I$zT9}bW5e;64OhBBuBj!JIWpxtev<_paC{a<}XFv|6vC8dybHCAv zSpN3>%QF_|#41YzwD1U$QiizipR{NEhO^2?7iur>o=koMrB)BoE);#e z*OmCi^3Wi_;&Az+7uKhap6g{(py?lep?;1~ReUEZts}6E`R8{meXs{r{%YK-`y2dc zo!;v&y?puAr)GD(i80qf?I^Vsan=9~dy5H4aNoz-H= z>J^M~Fb3zTWPo5`6jF8;RdX{1kAu+U9D*pF@`50zpmt1&vuHA9nbODpW5Q4h2K&Ga zh=YLz!=_csq3!w>7%q+n(+EtoL7Em4Uo)xROcnKm3>^H zHdPDHrv+GGKc+Oum_4n1^2M5$o7o(bB~Xm>nUm0Y2BXZe5C@c*_ekDSHEM<0gfCi- z$ua=hAkKtousC}@j#-o=ylF?ZLt2%;Sue+B^(vSgQ&44c6J3G;zQz7RFERArB!khE zv?EqFdyA3ELjrq3=|CCtoRb5P?{fUPo_PwfP1T0}k)7cYKogcI1Kr(fbPZBUnL|sZ1Mu-5GSW7SavCtx`lr|u%WFtSS)yrX0`Y%0My0Aag>;Pp+ zIHfi&DIms`ANN$2Ieh-{dTA^2$s`|`MziGJ-DLo2@S-Z`mm1-Ml&_j_$D=sNHo%f! z?O%ta_x5Kh{xBs?cHNn<<_MPp3~3Qag@T+)#Mt@YexXuml3{-3^Hr9~vxIq%>?z8! z^OX9tKRJK(9hHB6=8>7e3SDQy3ggVv2c9nr6{0aT5}TJ^DPP#nW=VClL=LOK1!q@! z&mFd5{A9n1LQcJtc%TxhvbklFK%g=_$A9;sIuqzpw$BT9rK}Y@N(0Wvudi@{YMOpa zh|*8oItv;(dCM457MBilNxx0`TzB&L1sOXdBn4HD-+#Ch>^24$vIqLBKqabaqLJlk zK84HMM~AliC-X0_OWqD2t=|Is+c(uB0!G@aZ-297#$QOTVWjBx9fdjntU4q8;AKny z>zy-$LlX%=@W1+0*`WJLXeFp3Ln+JgqI6z+dMy-|q64*-gwahzf=h;}Rd#3~{fc}o zk_C%D?WT9en`IG5W-GV_9_t^n)JBjIt}lg)3g@)lv;6{W;-i*HX+by0rynUY$oN9C z0z6`6ekC&CwfPSh27;7D5LLIvQvEU`h?WaFm8Bw$o8hmz6ai%W8&r+!YkhEX@lj@d?$H)u)P}Q#e z#m-2F7`ju$P}%UJKqi_*iOozk{+xbl70^(z>8v8a_)}L@kJSNOiYJT$30n5IA1wOV z%zfrzR2HZ$Ys)0WWha{6-laCe>2(%iw-x%!`*kzKTrf~;+0#~+G@)dhiIb^_+5}T$ z;eGjVtsuKZmQF=-96WFSR=6@yfM=)3gA!XFbcgIa2!ZPa#O-!V|Imlpe8Gp1yjre6 z-Shn`C>YOMQ=6*vim%T75~Z#E_4g9kdc{G+jJIY|5BK^|xmnUf9FOmreW)Q?@Uou`p?6k=ESC|h zY-ID`hy(`+4KH#T`T>!XGXghA)bTWw`2s1>NfIRxmIYBkE-z0Z$f?F)Xw2onfI&9S zqiQ)!s}{(nGT#J5H5fu*MkrQ*&=2+nSU@h1vYh^lPt;EGzzWh-#f*he zOp#MOSRgigKCA!4m*-;eAuyfF#A~oj&w()JJlqKknm{h`A|984F$a?9=9j7hgGC|d z=MVIctk0`v1m7&it?Oz*BuFYu4V@iaaE6=_x15@_^dga}I4*4%e;_hrQ-qIU=2e-e z=8^=H)O?v@z|aC}vN+#@gfa8KOqYiZ;l+Z0*dZLK1+fjFgi&VjRAWTtJXB}Wuk64@ zwYe#zoTOfG>l8v>blR$7q0}X$Ba_zvEkiXh0cAP!QV2fQI?G|IY94Ct5EVtrvbdBL z2#_pXg@;a1B31pi)^J8b7w1gmKjz~W^>-?eqW(`Pc zwC|O2oDepn_YXaM=>rRpMStx2(#{c3?R!dn^%=ph_Kz_+xDYL>6n5+@;#J*;WnUk-tne5su{l_4 zXCk92DiX$80+_EwlAu=kmi{3;8Bz2yPbX(*n%J4fN@#Vkj>AX#{WZX5jFFS$$-T9Y z>F2H~vQ*)9mSac}$3jCH)2Tb{^de@PJSrN2YDtO)IGD+V1+H126|CjocSaQ)UNmGi8-jrj5 zLrh@)`t|k0mxM=Mi1XSAe?>23)kX(uxkN$7NQw(06B%Mb+jQ1!ZxAvt?A-hpcg`$#CM4}>l+L1_GSv?B@4B$$12<9aFzwJO zymGWeG5g6E%k0lSQVZzeP3tJIoDc<+H*Q(_$A47&k6>aFpckd~N*oj}?@em3yo0RY ztz*W+wOo=X8tti407qY~sJ_r&ql-Oh6kUkR5B8?Tjc3(U53e#VR)MgmEtGNTw921( zqy~qAQ)*EUzW%DwoeeP+@R07WKw)}th!^1R-dtI_yW?w5SCi>x;zKDddU)<=CVl0p z8a#ufch#zL5U8b#*Q?tAJun-=G%*zwr4H}W-q>~NxwSIr_93uVlV7Mu|4&}J^lNvO z%cXQpN&BP6m)?DGsix@Qk(#9>sv=np+E2ac-TO)nQ_uJJAJme3+UioI3eCT{w+2$c zc)}rZF(!o!!T{r;qxB8JtQV^qwF&`cHsA%2sjA=Mo$sPj3rQ)b6w8gCk|*{mux`jPr^y~et?q;SU(70GCCfs z-#(a?dd!~)yzT({hF~gcL9A)n^ttl7dP6W>xp-Ze3S1CA9s?^V9VSg}JEpZ+y;?v5 z8L#;Ph^HWD+NaN%ud3;zI#c0Mo5JjJ@(@bFbV|W7K8_f?dAx~H!2BtbtpUMA zc#c@>7&Ehgw0t^=F&#I386Ri>Yfk%CZGdp|b-gjGSAqB#MPT{-#yLWUs1Z+)?l350 z5(h&-X6CYk9w5k!Ikn*mjY7HEuaB7=i(y(2sErDt4zIE^gW;V;fM3DHi>LS2H-?Y} z!agohf)dzsHR%vwfCe(0kv6!GwV4W$IYRbwgj6I1LALks*0}2Ty zM)O^{mfYB-IJ;z$jPA@+XZflgD_zGjbD?t;cw(6}Qa-msCijGZ550XR3L`SctT}z@ z|NG@)gaLo>ShdkjTPtCcA+rQ;#d*G32VAB{MF?pXx?b5)mEa?A>9}iWEw9XkXowf; zJI8Dj{D+=j+HS0JY{&sL#8Cd|u6N?^PITKpe>w8{op!9h6=_P8%_ z3Gc7>_7-;HC92x7lg6(;RrnlOV2Db@%EFE?6{$Vizd5kJ6UZOAaJFLdmHuu$BT1Hc z&s2BMfnk+|e9PHY<&Hy{wzy%->;p{70Fsu}=7q#))D5zGOZOuR5-<{(<-IXR195gcLrae)j4PY_r78>}t7hch!a^ww=GG-WqdjyL@6#z2k=XnSHhTkXGX| zGGZmPJS7&ufHV`|S@ng->XykukG43cZ#o#YS&nB$xYTBhQeei9URWb=#8NHJvH9^m zrS4zfQ)N90i~ASMM0MX2E}a?6uiaIzhEepc3u`q5BQx}nJNu1wrd)k~jdBUa`ZH=7 z@#byCs38c{bN>4;m6mn+jmvFIKe@eb9~h$wFWheK+)ZbdST{SRM_w&a>GSVy7gS+^ zp@pfJ_BxAO9sx}HVQ5sjp~$(c-#JY*NTWg=^tYWa)uRDE_uXSJmJo$%@qQds=$gwV znQE&|GmgzSYz9&YSq)J0*XNaCN?N<1|2+JBHSu-5m6EB{p^Qlg5WAxVTsH&p#XXW; zv_=@g2NBB*7|l)8v-?Y_LO%Ye7B8_dS|3o2ix~#MJkay@D+!SX^Becp^0y}2t_SwB zHChNk8AWcopT4p#Iptg~bWd{z!mF$mP&%+^6r$=Ajc3*!M%J9>yZ}-ODJP=RL8=o2$y60g)+!dFp>m<6o2Xn+1PWxK&l`W20PH1gHe@Q z=V5tZYQuo6UIhj|o(|9}QZ)!lQU9tYh`I$>pjTK27gGpVfP82FNKjOmx*`6;<24V?ULKdRM5tyZ!--CJ`tfz70|H|iQ`J1`LEB(uZ@X;7KsvMAv$1Z3u9H@nZ zFFje4dvZ+T-Ekc@uK|Y_Z7`uJC|O0wSvqrRfz_@@e4RhInFw*RHS_8&Pu?TAu*sSKK}ND zB^;2$s${%aZr)lhR8=p;#3R1nx~+bde9h@IA=E`+aw=w^j9;oCpL@A3Km7J;T)hR0 z6su??uKhCC=Cg~ki2D+;GC3&uE;}{nduDIp!Pc{Ds{#pG!(xx%ZKNky01=Y;cf^0@i2%toQVSMM7aswnITKZP^BQkm!4O{Df1BAyL;(|&2{5oi3BYA z$u0dcsR#jC(1}Jgmv5*Mo&@E1ktqR|2m$2i2{17urluB>gV^~CNZ<~#Fw)}SY%QVo z<&Zp9uRuhB*a?D(U%4DGRS&Qn;9i6hHF zqX$B8Oi{5R3|+xJ&?#2i4-oC-_dU{)`}e=iub()wcr%$dmC4 zoLs>AVS(cf>&Ju@M$eTY-w-)LIG#No)e_^k5T=TjkIpNEWdzOn1OsSB$oS>Nxr86n zhy@5sj^n|hqcz73!IUiR1+$>nC4a~4B%t6}9*60}+Y@?R4qoS~>fGSZ5I4b81#n64 zcoL7=?Y&>ACXVOT>Q$oQID4{irZCJ_7&9}MvZ)lDNl=?A5SCmbFzZ#w{47Y2iD}9T z_v|XYFCVU{Qf7F8hzesY1c`~+@Q~?j!hLD~u32<3l?u~Mmu!&qp3%RyV{V=Ac_%Yx zd1jmrGr7v^db$}Q0m9$evq^@Tj3TH6?DAfB>)+T;5M~K#PxxG#+$gYFjs##p&0q7; ztHiCF02|4~l7^n-vaNr0D-!l^Hfb`J4BZUIK*zDBQYj=^uTe${<_$ zE;zgXBF&h~&#PrM=~H|J|MKqH$C!XH{=&6&A3fC{p0B9(uhCt%Y1Um?#S->7Kpb(- zhlgHWsuy-r`@TyGgR8>QTfJy6uGgwuh4w;KLB9~ECtTdWMFlTEeBim77LzaX_wSx9 z&Q=q58Ut4M7tkMizG5h)an|CLs;#escfv_KFPt?2u^_xaYrbRS(tqmr^|UVo1Ya$U z&T{6sCfyPQ1;{ipMXHE}L>9+|1@IS{Bh7lSKe?j3|CL%SBOfu>FuGf5RFLFz@L%0s zsXl-yW4ha8j5IWHgnU(mxQ5p>ED?b+HK}iVfu0sLxBxQlBZwE${`X!|>vsgg@7mva z=6uEE!Tu%wSe1y?9WlShv6HEG+u3zPRnLFAmV{J=<($6z*psG5PfONZT#j3ksb$ZB z>TEK@=MQ{qwFQy{xjIjoN)#a)Ix18tKn6%=Ipn)8ocYL3y-`w48hVuEL@D=w?wYa( zCi|XZ1_-ZE>^)dAbwb0}KlqViL7c^xgfTzGKr-HR3}v-fZCv_`SIwdn$Ki?kr+-q6 zFh6!t?G@5k@ynUnSqVxQ5l5OT1wAU#n3V24yZ{Oepyjkxvp3otTA#na{ot%8Ji4a> zP9R2SF>&L&kM4f92q;C9b~*fNy)|~_hFT927NVZpUo#fc)B-NVUnVgR{N$yz0_)P~ zy}nynH!!riBb31}>Cf)*Z`R`1qdhbc8*poXu{Tu02ts-_MYZOVy2+;WAfpg1ZW9C_*j~#G70EUaK?SyNWLyf>vboE;v4Das(Y+5~jQ`?5J7R?((dZ$N zw3pc(1t&)c*rENE~$3um;xbq69k~P zFg>#Ijy<@vGKfr&T7V!05G_0+VIT)(ZYyOh*(0f(BLE-0fYMN=*3df00^<^WBJ8PT zfC>y<10sAu2w09WyUU76v@a(Zkg7y@z@Q(g4GdA){J>Pikmaja znX8=$Of5XY$J_CM1kcd{C&GxuEF3JT9#$ z6S9Dh*t~z2Dn~#eWYe+`W#$ynkunE$tXehO-)=%IJrIygl7`Y^pSOYupRzf6W^&_5 zT9rwp0?5-PLh@%-?2o@Vo8W?+lbIm9r6Th3m(>)E1=gwRJw z{We)Ia=8RGpPnx=R*Uqy0}Kg5S5=)l)|Xj(n#)4O)JW_zb0Xn`mlcv3Uw*RwzRD`9 z;pJ)d6W~2XK1(jqpY2F81DPbjd|jgG@`)Ggl90#u)OR}gh4K@(Rx*&)D^-8t%9(KV zQaEJ-Ir5>-Bxym6EOPY8_gq}%n$vg8aXAxp)%op42@_2%)hy}B9<}`Lyq5i zVSU2|aqNHa`7$iL431R+N*x+H`vG_Dug!N~-&tG-kCanU#2to_hMn0rZ(I7E2bcc( z4P`${gHnB8{6MWjg6lRftvzGbWGs&mA>wrfs-c=yO56#d9N^_wD!GjdxCA7O&UnAp z3pAQCt4;_+IB~ANtxFuNx5hqnS*3q57@*Hfg#c24(mnHAtn$fjcM)T11}HQRNV$4y zB@z@NP(}csu4`t0)3(_iw4;|n>4OiuP8exrY8&uar@=W+-pgSR zKVRzwao-t40ykmM>jsgmDV4Pyh*Ee5E|LWn^0E0!B}`1JsP>X5v2kkC+WHt7Vr0%_ z?tf*r{G<_?TF{$-5TKW)ex`rHQ=^}AX3?{XD8jKA4gUQD)f~z?!=a~l~c~=ttnoQEoB8& z@l-PW;}_MU$^OG7M;|`=YIPf29(=kzX$}s$Um(#Ca?h?Z+vRJomDvo_$7KF)*c-Rh z6U4A4$wz#7$N7$D>XN}b_tndEKiX^e#Nz$f_0?_u+3mAPvJb)ExUm`wFqNGe^yUlx z%rK%L!vFf7Dqzv&&=ZI6a0y8=OhDnX-ax8=>nl&qwxE`reylKz8EHEZGf+~h zmdh9ue`Ly6pI<*X3pj%1S3Y0c6%|f7rNkERc}utNs9`WFT$-H3THd)N8IYz6u`Nx8 zK&^3%F6D~M{OOs3@j*bi$ts{g9XpBTwLw1d%y;q7XPrGxIY$)LqLls&L+>T)tE;5L zXl2O1+~2y24X?af+u}-EZzMDt#|33FS;E>nYl42{(HO}oBU39B?gh6zXK&atTi?-D z&`|ME|6Gi~vH@ec^T;gbW_d8ml#+uOG9F=92ETsy(obAEyL49jukNmm?NPyih|K|+ z1MKd(6JLM<--sGWZQ3Yh@MuQhp56cfCpiK^T97*6nK%}S27$tpdu#1}2pok0Zap{A z6t8^b)p}$b5J0?RfOE<@JBFg;K`4h|tj!;o2ZUe&;_NX2eUv&Il$NvS@|a?SS&m;0 z3#(UP1ujFAdvE~;{qY!=TeuvvuzGb&r@j-gT+4Sy?@9P`2PYS>PQn|2F^;??1Io%m zro-h5lnbNklw}336<`RQvY+7CJ!lq)SKw7ecpi!=r{G9!U=)B!M(C%jSD8tKF_#OF z+Q9S)Z5Fll?D{g-R0S||x@O~_;To`jG07WG&`6g3jxF(7X(A^!4}n-P4Ld_HHTw_x zPpQESjVYLgq`=6jFHr$VOF(EDye=~lbeN~gLlm%Fv7szcCdy?1flJ7UQZ@n2Y>(2= zrwXVY$QKeuv4`K?*$Ii0y7M)sbIOINrMbBE;qR~lm#N`A>n6wKyPCNU#K z7;6*CH|vd<+k5fL7XvS*0`3H%g+FM_`fsP9HT)(+iHi^v= z10UhZ=d}iv|EQvcr%Cu}(Nc2kEGVdzhr_4pgO@G+H@{b0@-Y||BH$OwSgRm_bd?RA z*uXPh=upCDa|(4*>*_g^JL_xx;vOyVpZU|;F7d!~m9!8dYg*g8_BQn1H@P3L3E?I) z0)P0}(&n>f_VAP$9854N{hi6LD~jjCz1>Xfp#R8Rjw7EyDlf9Wb4t|;ns$e070}SK z&1(AYmIjLY0`UvA%%KIdxR4v4*Ul_)7PPFz5Gy`FL%_IXL!yVwWsJ$PHP!d-sy@Ie zTvQ{*7~-5oOn*D4$1!z4k*1BFL{e&d>2IwuFXq+OZ)ckS#XaJ~;$zV!LWX5Wc} z1cZxB+K4;p3{+gVsa{is1fj$tK4p+{f6W~uv2LZnoja-ngZSkM<*`Kqm$M2F+^4!C zv*D?}dgDaqWMD+Vg;o8>q>Lfx(-uZN;bqd5=hqW3VOL9|Zb{&IYw8CgYbfiVl^`n8 z90tr!ysI|7=qLYse>{slx)%A&s2ivI5=cYC1G*9)p%Wt6tTIMoi+8@9bm6*Ldm%P> zqH#n5%2b6#S)3Eqie4*Xt!FVKIila{knmXClDqa-?M++zq1;=aC}rohn`*4^Y<0VR zrhi@&WF*u>qLey7i=ZJsV|6XBr~)LU@Ub z2zX60vTy9F#o6=Qn833y&4@wFi>Mv@W(EVuSOOE*Kv>3Et>ILaat;tm9V7VV0FN=h zEjtK;R z;uA-%0Eh?u>oO8P9-;$sIpzV*vBBBwDg|?^)GomId(<#Hh3VWVNHbT{l<90Eu>1-7 z#QaZcgHj=kQ{I%H?vIx*cS?c*7Lb8~%!Cp!k9NI^+f)ABqxFaAgvBMvvB7L<$V}WKO?4)wPrNwWxa1PON#U@{p%6y3 z5}W;pXR}7rP#BTdj%N9J>9QF6o4C;n50pVB&_hcuLpG{HNf)Ha@mZ%={;6vH8I=+s zpgIR33}D+ApPV_!j2wjQX)R{SC?yZb7*P++URg>DmmaW00gwao z4PBT=_smvph?4LSiv_R3%SUPfF?mjsMnY(((HZf3FD_A>BF6#wss7wAO0j|=M452q zL?GHXubq9J${`LiZItGMU;c(;(uv>6b20-q1>7O@}@G|;^qyTi=BDy3$Xu{`brmey0oUO_1`~G zJD5J)uP#d6@gfFMov1R>eUbDsj*BPxv}Y~mj{fln<4OIvz68D(Wg_G!E}8A`qs~x) z-QD`s-V&7+oKHWz^r6dZse)_h7^Xn=uijQ0oscDZ2~;(WCg>G9Et>-2s<=s!u>AhR zg(+!EwNFh_ov4!YiCb$=1YP3!wo_|sCwoZ7kcC|WbqU&G2oVjG0usG%JFVV{qgMx0 zEw&FlHw#y~TwGwulf9SIO$0+i1*(Jp?u%#lpxPcdXQGh3tG_z0=_x(8mmvV*EUeFv zb(bUL;NdbGUXUXkejH&j{{P~2vyeKnD#58|`V-^SNVDb1o+&?nP5lhQsjKSddiijD zSL?prMM*3((yxBu+L?vqv;1pM*CH(Q48N=T*HpFmzE`SQALFFjdgZW)k;wj;0A>yvl( zN?)|lzMvrHZ{*g-9d37K8c~@(=@tu_OgF`D{NB9FG{3 z6j55sMy;19azvilRDb4?>QSU=DZNfN&@rOOQ`<0}_SK-s6c7-I0Q;|ee)gWJ2*VJf z3f`p=9){)Fg9fse^r8N!8LI$kXaE+#q;&hV2*6UonQvJw`90jZ-4= zB~4ohGJ@Nmsqd)^CU|U5jlFLFzj0$xBGZ{Stu11BVyTWj26O5Vu!j&&aD!fE3(>CX z-re;^Ay7_`fsyT@{`t{t-Vc68(1S2(nvd{m$JVvIN8nrA1;9r%IbiTQYY`m@CPJ`3 z-(O%1jWY?V6hOFAwQc@8hH*zBWoImINyaQdY8@cLfga^~8X*H2FdD30Rb$24Girp- zXc>TvQjmKPjP{+%(Mf_q4+cJ%oTr8jShx&?668EC0}FBpAFUIUat2I&t6*xtfT3ui zd^|vt?s0;k8;{pE-zc1jJtyHLyoGRb0qX}GzRw4g;|+P=rrA^Wl%r@t-L&eMV74@> zj!APo|VUFc{4z%2G4xw{KYWNY`^nrIz`&2% zcWkUQ;PMT1DZIaS*DQZ<=^z<|QMpouHjgx7oioJ`r9xG8ZIH^yGU;F6i%>9e&HPxP z#w;0xv6fEGoQb+KDd2mfHOR=iB(HuY;#E$O33JXt>w`2oA=GWai;8`n$r3kZ|7z= zZLLLt&1Y9W;J#1=YWZnHIhdpoFUfdiFZq@VOoH=~x0i77thI@rzs6?7rsgkn(hks-mA9-vvr_H|l<9?+5SI)9*x804KWCoIRs@_8C(gv~e!12nIi5=fi-}Mu zSHQcWV*~XJKt>2MLYT*fEU(Dl=uP5E1?_0a!5#-Cs1iS-luKIYV{$)=unWj-{Q>Ep zyQXM9^^JNJD=X9>{f$^Lq~dL-RSv^uzy=AtYtsO!u*?i zOBXMMMHTIu4`Tuq_@jPnRc!>idF7A@l*#$PW%c`d2^8cN8!E>`KZ{|nCsOHbpo6Cl zvCF^wj-|hLefh2|?Yv}t5tHmJZ{!izbRjaSE+I>`1T5PR3%?R8iX?;z4?b7H&pP$_ zYij!vQ2;@L{7bi1Ljb(b?5i9j>nsR>R2jr_7CJNa^N-hxzzthUAPI!$W+Yi#D{JW2 z=I`0T?CR~A=?0Q>gCVYjj=k_l7Lx2MZA_ow@F>9HiAw4+q5b|<7(KzgJtAxlet{6w+4cl1N_kn#Q8&hv@o zodUf%lZ+PxUvj#EaM38R2*5kH)~3Q(1&}EdOM;jGqP?BFJ zeY5tr%E?P{b7?i|B8P;h_m%|5s->kbKUI;5Rb*y7F^1?NHNrexgNY!h(!>Owd1}2F zm?#m2SMBDrN;n~Z|Cag+SYG-gpCh!hO1Us?Nfar+b4&dM!N#?RD^*jZAvAhXwM} zIsff_y$i2jFGY-O*Gu(Nh8)!WUOZI3WI>s5dapRY%+8ohNLkzVzz~9qG0rd!LsR{= zX8s$6ckP_*@-(ZZwzN4Fu$FrqW;J5=}V#phJt#)SZ5$Q;azRUmE#PhgG&FkCL5 z-CqI~LdNUBkWc}fFMd^dJ`;$w0~rEig2&MGua#=yL1+TX0_AeLXDUc%hZZzQMvDg) zXG0TdDF>G0r!F>&qGXp&WC^6o7It08K@*fvL)-u(Cw8mvq8_ zl5LgL_2ig9^GIntd{on$GdnXOj9z{bNU0fKAt@@|FP?5slxbdX8Hr{*69rX@cd z0bhZV)1EnVBxx9#^-Z7lf~ebfRMKvywLKq*={w@|Q)d~@Rp-|dRq)AtFuEj-%%8cc z5*x{LmN+reuAuKxjYb(+S=Ej8dkOA%@0Y#A`$ zXhyy>;V^$P|I#_ee3x1Q8Aza9g5U3-HDs!D+K~ihIdFbnGq3oOQ(0f%;Ed7FwN92N zSQM5as z+g~w*lyGXwdv=vBX2fMcj>9EN(s<_%!idF9y>nmp{{(zn?ZZN#E%Y%ockY<|W}JJF zNiag4o+)27T)-Hk9)7<2;p*CKBFBb|QjwvQSSoT!xZ9^{`&R`7LDI6jKMu`8u%&N9 zTsLi5`mOux_J6s*vd=}gOEbQS$mjx9duvm0ymsxs^uGJDwwmrg|&lZdU3+7ie~ zaDd1Mk6j^V#MInG06r~+28OyKn{NtsjxI<97@ZSRK+zQ&>Lx1Ty|XFmvH%^8B#?i{ zj#>mqLmE6JQ^|>HprDrU&pul03XV8JsoRq%!Qm{j*cJlpo6f3cf<&XA5T*a8Pa?oB zMb9Em+V|hwTX**r=hqP)j`6mCtBt>IglL@X}>km<>LW2^dLBTqcVB@nZ0XIG`UYt9`X!Lc|py3cX8Jq?OBk zS1Ob6;PW-;spTesBpF0(ZV(V+a)Kuli*hsqRs6+kP68RqxHAA(SRA8_(gBnM!2>ju zjzuVaLz}8{M^vNFX*7C9`vr*D*jW$g`OpfYeTtbv@Pj6jr)Mzm15AYSz5GDOiFjL8Fi?fglE`!mgAyZ(|;Bsv1?e|$|Lu`r(nM_aN^N&|v zF{Hr&LR{uAPOOTG)Mf=C!z7DS`*QyjH<__5zBmN@$1WzrlDcTM23KQpSX4Q{jzeQ@3n+ouKttTE4jL5+pNE(A!2h%$ zMS4Hu`!1;+aP-PElk;%LkwSLpFq?1!AwhMa1C@R8Ld5baaaJNg^@QFQk-eWRM=vrT z?-w~)$c*bZmpxbX9>z4^V|`z^te`a{6x;`pE{QsLqzVL5M@1s@%kL{o$VW!b+xqiQ z3G7#%EOj61S2(ZVQpZlWCmL_DN|$=IQyraBXdgQtOaZiyK9hx)gS%{=uW=saVmjO8GsP zl&BAGpS9o=EUW!P51;X|Vf zQ$Ztj2r3SJFKq6;W(ooV!yd!0cw%HN7KQES)JnI&(thox8oNj!O4X*bY6%9ug5Qhv zL>c-DRtM=!hSsjyE~3vx3i#eK_Ac|0a4nW`~ROW*6nb(?=@D@^0Dhn z1%mhQiYs@Oq5{6dEl$bdg0P#jz{|m z<#_vsA_!o)aqG9<>D<>p`<=hRLyft1JJ z7f&1zV=-cQ5*?HAx&9TACQ$IJtoJeGT8Yckc;~^>4`-x3aELHKkd23a$VrXyL-@mFF%MClZ7yO&)av#`}i;=ORlAJc!;WAV0A@<0tGs@JGKyT)eVIMO2nE7iVJB5q=kSGx_21X&D z+kj*SP|*F%lcL?^`^Ns!aT3f;lMg7X!b=(#!uXo7VQvJhj+J z3%t~8c0|DHLv1nt7~I;^ z>t!qSQXm;lve_KMyK8@a&iRiXuhot0cQcZt?ZnF&nKK&zY09+76$xadgTWNZGn3D= z>X+YF7%Ua}a|@p?T0!e9*X32`*SB?QJ-1gnpctlr%o+06yXO;9N?TX7m)ZSNUTh#= zRmG}zhyD8pmi{mADU3_a8aHP$dy5T#ad~s^W)cC3Xk1G!hY@J*m-@5cM_*k!WmTn% zIs+|~u0Lbxg7xJB;9#~|z)Py}!rZ*I21&P!n2^tFs#QI@r)uxqQHv}@ZC+dNYTF@B$$MFrf=)yWN1Wy#ZfdFRhm6=u%wDo_GmH&r^86X<+RuVGt~aA97rvKid9vwnS2IXBd4 ztLsgx1dtHKl7E@xbL|~*@Bo3E_Y=340Ynk|7$Zc8bR^>p7w|?e2?*T&fOjMS0YBU?Kjzlnb7)ot6N2JBSR>@PO+I>gCI1!7 z2I|<8l~%_8ldsmVZGsG9$OPe;eKR*6wC-*n?uH9Je^2FXE zaQ|in+#zTss&8#Rx>HoS>)(4xT{7&0{Q<1%jlG>Yz7Q^GvB9C%xo+F}#0E>m=IjVd z-3|wr)}B_4LRZLc+*+3v!yX)z(eUD34{l;2z;|{v0O5Y!Jm7y0!xD9g_nyaWnx(vA%WNvQ0vlBgVKEC zBvYxTk(TN<4Oqe$miQGdkq{);0&yp`Kw&_Hq@dPWS5Zid1fS>)uHb`lhiVrEw;{6u zQw?z!^*Z=q88GCGswvG>OJD^FDGY)I{1o=j|2#ZccJ>GFmji-?G37xs%2TeaUV$Lr z=<3x1EI0cE;mvVP;Z3VP9*mCqMj?MNI|)D7aB>0b`x?G)Z@?7>skeLxd#j|qg@%pi z@$tYEA+=MQ>FO0&V2^oOvK&>Tpw{Cs5bc>FgdpdsgA2|szs#IwfZ9yXfO+S_W?;2g zqH(|({d-s-!kveOgDh2H91=|9WXN>A`daPKkpvE!0mHjQGF8s-4nRUy1WX_A-Br`# z&{9rTf2$EQWNM=7H&AL*6MCQzf)KZ01HecM}j6WjRsS(c(sT?rFCLh2w-xlYvsfFCX1?^FV1^; zlT|$VB*E<*8!K#G2$F9k@yzmpeU3)|A+)QYJ@2#RiiVDFG>Y^-_X* z;FU57!N+@J&PecLUW`fd`DlKeu9w4@L9B*LZjVI|f!x>2VQA#8oFZA=iv3{yIu&B} zC~ys#TD%H46>fWa>7sM0W6%Y#OK!?*Q;lYbfmHPuFA1_?b6R*Ti^6a1mN+e(OK)vK^;%O!j<+?Ae;-ocx}CW z=Mp6{iU?ue&tFp;vPk>=hifr{G@Y4wY#S=Dh^j&wPJE=n+s>{eoCKi%px0RBM=Z?# z{;1g?NCA8NBKgSCx+&1Bg@@G8T2R5r6TcX5OncA8GlC#>ytJ(G;<7U~_Ke)lW|NNcx4fTy@RRi79{W&Wj?(qbQYApZwqqC+jQ@ddv>pgT# z(nV=Oriy&$U%jn9AwLUp-;V(P%znO$5`1n?<=?ocyiPj;^fNi<*20A0h5hMTfEJVj zo;YCP7ioy0nDL>@iXDD+zbj2nny(^|@+lS4YQ7$LLF^<`e8e1qf#U5OX7;F}NHgKB z$hYr|&mRl`WNcueK||-Smy|E4M%2D)*#qoz{KUSr+`ZUtCO@kkODc5 zb!S#*aOv@|0WZ8fn=pYP#M$wcr}~fK z4NuNL10P3*J5vb8nVd1XG1rM$Qw^36_8fyaupl;+Y8kA|&i7vOEy);%kYGkZ9{c#7 z+1qNPY6`i;YdTJjsT&m$6^$TMP3QT1@I z=|pnU336OAl}x~Z{k@j~ym+V*64QFMkr3a_s(0<5;b{rnu(`e-9Z?R%YzzIR=hog3 zg~}7!W@OH5POtS8H1fmKR#(>gkG@c%W;Fd*Xa#n1hfKB!MFREl>#KchjiYUrOB4Qq z%j$&@n&_eemyVYN+tfc9MMe0`S$3D0h<};gD~#pQfoi^_YGF};mrE-I$+ohpef&#T zcP5-=yK`%!Q{D)s0?o7g>zg>Z04*S7ug*IvGImN@2qBO<|KM+brktWT-GlwoR2?`x76;9sxw*6j$AJN-$ zsMh%YbiS)%2J;}7vNiXI6xe!72Rk2)%TzrW37Ai_wC zkh-Bb2)TMyeXbj52`W5go43`Fhzv3-aj_Wz`UI-v9H_OqZR=`; zH!}a^rB%fYzWl+Xvok=iy$bZMCTE0YoS8r1y3d?zCb~o#Lib;m$1P!C(>(ACRmnf7`=ZK#PONMb{ z3u26q^@c$PfTvz8j9QWjjMp$F|C`myo&v@d~y)G}MG^XDJ$8*09T z$(g7S(1^Dp-1}tFS6f_AQ=OZ()$JB(9#2L&DqNy>c2DVSq*c6nR1{=AX9z^&>|nVw zUh+$C_oyI&%haj@p@G2;+{G#904TGP7&5j6PY%6-NCt>UIeWp}GI#DQ?WuAjXfmBG z+M|moc#kBP$mq&EXNUsIJcSHCPK6*I1s?0~pQ-{}i>U!LMtCzTQ0zUhLrmy@~7F)pX@MjaV{umX5b?T>eW2sx&-1>|&o090je;n8j?V@meuoPMlc zsT#AcF-sUg{zCKRa)?S5B)y{?7_~8S$Qd^09LNL<#@UpJNkibEALY92ufQz8kl-A3 zb=j#SV)&e6p{fyZ8f#iSpKh9Uf&866#0k;Xvn#@HJFQ;qinIt&AVC@NyDzFay)qVn zYdXVoDe-Ww7Lob4kwlp>vy=9Sg*5Y40Dts&|E$&J#lpyZtA1y1zDToDcR_zRj5LRN zJoORfQvPtohDyuHlvH28r4q#??c8K10)YPF^<{$tve=bhs812m0+Y9#m=qQ4`Sc@2 zliYytXeTZ2_{baoZ$DY-L1IgYBo-x6%y0vez=gv{i%WcjE7HziQ^YW|5KE&udI+2m zhZ3wlrS=!Nq*Tfz+1TBA*MEAbUT4D#zP__EPonS&RI)7ggZTIIGg23}ZpN=C1q_)lzfxu3 zGOuWfN$EOROJM(x4=laZ-<4BKpfkNL0cqOn6a9OLY=B2Fs+4((-oduJo~@1w5Tgw8 z`pxy;9Fr0*yev0}ApqyRt%BeNPn_#V@s^hU+3o$}+bNZ4zwmOA>2mpzIQ@-YyolGI zQ5$4pHe#KGI~qupm>{Mom#IRwdeyAaYx$!Gf4zk??i*(i#6sEohkJoFKEgW;R(I^E z_Il{~8fTCl(M?+~JM~pfAO+2OnOb6>-dDy`^!`iRfW8?8Cz7+$L~`y&03UbU9od;e zc!CT>=BrN?^3~UtZrNI7`wq>PfN69l8S&drt)&NpKY9d+$Rk$dnE0(Z< zY68_VTfb7)!HS?)TP{7P0^<4NGDDUsT*#CmpWR<_AlIEyJ$Bo=s?zP_NOjtF(Altn z%u$OkSzqg(PrhKzg4_=Xh5z|$wHWPY&o)wIsnQkc)p<%q3oP@Br0my=5(jz#f@19F zCOmf#gWtNpc9n)Q?;Ihh_13wCC|^Fjv~f-M=N@o?kOGtrW_UE8)9Z#oY6a~jM8nRK z-`yq$nM1gz&&K&)Eyi$DIb?+A}lMz8ArPBF_>fQTA$1_4Z_HSmKmWsu83JRrKz2+`g{ zHQTO9{QM*Fk(22o;lr>Lat?$tmno|?^;G!GBPEs)b238Ih5&@dc`AGmIBSUf24ZMT z_)I8)M1q|#z~t=S-Lub8k%lXvkh!nI!6P+oHAy8ltrLxrkdQh6B!~gO^H8-BMrU$P zUsd?uey}H(o#dE0C;c$hmdpLct7f8N12Sn3fh0(K*m>wkW&ZU;+P!*3tjVb9<}-WC zRFg|cRb-ZVS<|_^mIE^9F0u2^3_B7!>eMPrx1?wA`S7+XNr?PVu4_&VPxvcG3Q5%c z&y{WF`Y{!T(t}59$I8d9FYWMOdUCcQD~SZm?K?^ZU9#2)m!+`t$1bkULj~`g5yO9| z_b+ftt_1^sd&eqeIqLRvO0wKI!b@o zt{g?I#aN-6t3V@PRhtlrCP#!+Tlf#7U?Nt9KBDC9^gh^|t&kzE$oC3}3vx&(mfrTI zD+)LQN0E+B-V2nmL>zwaPb(>6-ao&iQZ>%1LN8*XiiXm^bz^O7y6x#2*ilOV$RRSW zp|4b`4ajAjVwG;Gm)3XpdtF9jG3k@Ng{SQ76$i%r{57*ZS-HVuo>FoxU>wk6CAX>= zfVWztW8!l4>JnZudrtwR@ILf>>D|4*vgi=AIh{l~yvh$eSFW*ycD9i$F_Zcdd)3Bz z4HM;0T~R+rXaxB2i^~|oJDf9XKmS+-O$1PGURw#jdz>+!d!$++&1X!wa{itd_{ine zg=hq%i4{3+4P{Wi_p3?97FD@T2uVx`%!<|Fqb1?u_4VN)?jz8RneB$UfBw-cEd`&p za7M;!1zr8G-%x`g;grHx$C&pt5SCL{&32Q0u=nY}Af}@1Iar^o|9dx=Q_P@Ft>9N= zx+kMiCfT@A%Ru{!k>v@L$V3qu1HMfAzx(2>?;*%$A2Fh(T$|rA8Kz|B@9b3`hfAR9 zmaWD6!oljyS+jfCEzain3&^}ip+`k^R!GCGA}o)(Q(2 z9iB!9va8PT_b&Tu*1vUsujZUmo6+*aqYVD~&N6uBRJ$3&vYpj`|DkrWNyHV0Z>O5bZx}`r4uMu$>Y5A+5eT4<@ z{=;>n8H!(cxqLJ}kL^=^~jL8MI8*874Z2Oi~i@XDSs6z0{ws% z0g+j?s`?u|F2Pi;8?&2Z>_5s*;f-g_UJuWbU%>6Gzj>B{>fUYYV|(fkLV)&QRAFal z#k?2(08s!jjp1c{T54Dh+&FqBAz9T`U@C%l^w6XLgHky!rz>;kCoY+-mw}vB4dP$| zVR57|FcqsFZQO&wJ2Q_x#@lMX9@R%x3Ikl$PQ8~(~0c3JVaLY3+~(rWugJ@NP=~rr`|< znOgPTfp16S3XIFEzWc77gzqSvT);XB-w}92!-TQh)B5`dj%oZrKGBOx<8q9N%L{{M z0mfs`gL68hDo>xvU@XUkY@GANT_6Svms1#AV9FSoCM9MbnIr@QrqUpR{Lqv!Gf02{ zKGISiDi(NinLXqo9{6yL`EDdY0_6ocBXkNtSwZ$<#`*cjYvabChp&0`)idDUAqBzA&Y9FEVzDQ+@KIWS((k!`#j5A`CYSOM(rA$`#wtkj zbqU_RyK5FG!^v;$MJzyuo{-01tX*AS>0MngKm>O799phK`)r2U|B)F9jG5!g3^~b7 z^Gp(BY^xA$o4t`lGEr(JQBS^DIg-?ZJ=#@4 zULpfv2oXSb?rX{s+sZ|I+nNAoX(Q2lt>2tV-Y%mf2TQDVY*}01jo!GXI$^b7r%Y;# zv4gXrZ;lR^&=NnH%>sb z9r^FMxZ11Q^gzA7$Kb3(Q6!ya$xs?2eunW(xoeH>J%Y#z8S8 zGl4zp^j=Nw51*0Dmn9^Pk7n$oe&5SpE z>DUZFqXYlj`|7dCX7GCt7klhi80AmgT8r$lKR-A~Y-*{H&EB=M*aJ6gseAV7jk6q* zG3aq#)L+f#!n-e8`rY{t*zu9egfRZcFRF#P42DQUOv}$))!R}!{z8hLJI#9%2JbFp^)!Rzuy8fZYS!c+w*1nKW>4*FETabH@@5BOfwAA~z&s^4; z&-GWdqu2Sd{;n3@TE6dx%Vp=*4irha8w3q?ZhVcn^QDRp$~cJfbC1-ckTLFPH_KJ$ zmq6D3^7|?*VpW~0--a^`Zl7;9M8jqUabr1C^AU7Pf8(CntLxu*wi=XnWn}2Ze9wV0 zmHo^hbMK*wYSGNs*Ag!B6*yaw`DVQ~D4vNGfFv{TEkd59Z}->;*7Rr5xq0K3+4i*h zQ$+su)?eMCi)4c;;mYBHalP-A-u<*xzFgAZJfa1`lY0xP3523XlKz$9bPtTq<32)s z&+f7xGDkrVr0dSCyEq@!x@EEwR8PR3E#zha@L9bA>=9CuAz8bsO%gZkak*5SQjar+ zk)R4D$H895Ib(HA-oi!8|M(|$2i1aR+Xe~v<&uhO$~k3RtG>~riZYBiq{_hthGq3i zLqr-08_%l0;=IsZG+e_lWYwTA)D8a1(VBZiqX6;+&2qpVKo)7~Bvg|Y;qZ4qTYrgx zyr4JSu`|+`r{{o>4QXIu^$MsBfpb2%Ig~DEA23Dc-3Y`%Du9%;dNqaYuZ(c#sr*qG z5SnowmWR@LOahMwhdbcM1kT1^u%LQ+8SQTl-UM&x$CpXI3-G4Bd!uknMOyH?X23~! zYvJSq)=BtYf+=I3&94hJ#rZbIQ!};`e4+AnM|`u=DIgBZcn}_seMp|37apIZ92l1& z%cXfw3ZwQ!o>JU5u?`?!FlGv)b}ClEF&fLDACsI@oAYu@U_g-5$ppLyf6l-h$T4^o zMtR5#GAx6@Bn%_(YHHP+YceAoH0H;eEK|??c~C90k%4$LQ~-tHTB<<(wYhgU$o;ZQ zh$yPDPvaoMbPZopAYsfDk=@uUICuA#CSoT*W}@tjQlj=K^#zx@~Fnx&YZDi zKa>L*dp_~5(gM#&@-?4kB8|b|=53Ygs3j`q8A-U1pusp89H@Zw{jUE@%TMclPgzq- z&;NMu@L~*;5*)<;{{H$-(`O#31rAZIV0L@m(#vz~yzQxqr_Ox!#=58CGB;1}(e*z* zxU_bXlv4EI^CcwxD$*#5%P@jKtTF%N{eyRL^Y7nMRoKzfg_7irYih|)CVcsZs+vIu zUtj&TyUJg_yT{WX_Vf-`hLONoXPG%UyLvYY364lsm2FUr6weq*5eceXlA|`w=Zq^r zLVfzDYtwve(>K`O9wgskKw;!SO=hr!k4yKXFMd@d&%_ zch1{t1@nRDi?KHJbszrRBTHZHmn%Q@jT#06x!-~FA3R!LG$RcplE3$G=~BKVdycwI z*|VcxT?9r0j)>%ayKAK>_5jU<-K7Do3OzWZRO^mbAokzgU$>GLzx3Q%oBJ<+WJY%3 zx_S_30+ulQ;(kBf^O4?Pzq=R-1VkxhI5L>HM37Ne!PE{$l|27HJv8gvF3D8DC=SoQ zRL=!f>Cd0Krv}om&%fW~37C~c(p&OYE3z8I`ndoy`lX7)>GZct*xS=#I7C@J(icdQj8Y)=pJf=Evv${(E)&UwSm|B$czqcJYvUH^P zNrkxo6&c8U23cCf*(FFB4X&uZcX$19C<1dPEgD_2S@b{_Q^JyPeGJ?E+UM`Ht!+I{)@+Hx2!ye>SwceamjzzH%g zhws|z)%P6UbcjyE_Z&_xV4Z~TC1j9XcmOPid}iX?f$8ei%Ic0;HCU#r^#hsn3Xhpk zP8mb2kmvLihFGwC|1+4D1Hnug7eG~BXB#l3T+TS2%?mInN3F?CW>kTNAfV42EAy~{ z40~z=;}1TN=%;c_d^3eg6_^rW8kCTkv6;Ur1d@>=W56t9nNN>Ecq7n3k?EA#9(<~t zA+PTrO$T`~4KOAXS)vd~032qJ@DUQjOaYNmt*i<}dupR15E^*%u*yJsHHyJ0M^4@> zfHzAvij;Jbb!os_B#D0Z>IX)Is|k8pjgYtlBg-|pl!p_jian}uu^|%9KBF)I z@oe3sGbqGme=nnC1&xH*6Lqvy@_De%8bxpz`Ox#VK>v};i#SmgqTMf7`K;5bOq7H? zD&zJ3VX=w=pazNq}+f2|66r7Fk zv$p635y+eoKe@Nk(cieY#!tG)XB8GzSD#;@U=N5>kboRY-QcPaXQ>Oa0@ypMAQkxj zdk@zoyjli);?~luJ5{(c1nI-`l&>WB!dceHaD{+7fNe>bb-!yimIMTe15t$k)D^WA zOQJ~oU%I;91{J-X`>GEhKn#z4?HQG*f2un?`DqA1DKgzsf4jJUZ;_p}hhS&{DFZ>! zB?w7_mXAH~KKoMT;kVD5n5l^mn)hC^^gDf91>i~h`p#PKFot~gk-8D`Npf6S*c$yo z;}Q$=uKDu{+TxK*QkQbkMr0y4&=o$P% zHw`a}w+CUDw5w7$b#)oz_Wy-zOND61UsZq*p4OY@uhb8E{>!^(FQW|!%*e_tv2F)m z!(VlN-Sr8nzH!5rHhykT4#tSK9)%HQ&hSF?c(2FtsIc7`08uVUaFcR#=Sxff_TIMn zK>c>d#p`QZZX}nYeifZw*8b{kC4md^2my9lc#VwMbOV@wc6&WE)zb2x9;yQUPw%U> z(Ke)L6z+enOhtp8Us}g7%bkjYn|l6!y)p^ZA%sVy~Chmx~yK>aB4R|ht(17x5KAkAvn)0A;#n66%puZDfEz;N+xerLjg@w7oD5n53B?vtKa%DaXus|~)`01aR78+tG%9f?5 zbC^X92n|SWT63&XI>S%r1TdK}In30}5wi_y)W)xHIeTnN0EYY1bHpl}%g&mz$zJRq zqB2)DCuR)Hkf}_2%60UNO>J~JEAtN5Fj672M*XC6*H%4PK%F~B4c{6sNwM3wj-Ix_pCHyX7Hk*`3e7DO4nxcKhu z00^XrgvfU2QjnQsuZlDhCY10@BJjp7#ZvZsv*sBDf+QY5o+H}wt1WA1x?(&=*UDn< zoZp)_7k}xMqT%KzZY}yaD~#F{*P)}8HGSx^%I==pTe)0G>#zAHL~NGfheg^+xY2_F zXdV)--kzqG_F~ir+6Dyg+)_$imc|!m!C?tUh$6FUCrykjmS-~sfYCo$1sItYN8o_R zj*G#g3H)=8%~}7Fcso=5k#>P1LI6eg>?$Ezt+ws-x}Jkm$kJ&6OwpgxyDjFgHW1DW zqUr}FOKV*crGqgWiz5St8MUuUs$Lt)HE#74x}5O)yg7#jb|JL+by zw;-RNsYoqGff8E*0 zE4AodI_}+F>5AB5r>V4nqUQE{SqBs_Z>f zp2i-kK~`$``G;o2p^tlPcU>2l`OOT-pbIje zvm`+ToQt^aR|Q_{uctX!9*i^jD8+{t%9N(llBqVUS3(gQNM#*Hl`}w(i%eQz5D4$N zXIAjf@1GpuQ7jz1d3d=b1OdkJ@pzaV@Mo-E`brNY4!%3D?Qipk42-|Cni#w=#*?;( zOcEFnEkpjly1Pwv|IUX60zY+6KO-9Bl98akJJuMGGsxf422z8faRgF!L;_KR5i;e- zpH?kjy#f`|iKqct?V7TWLSR%a;5`-(9Inrk4NJf%Oa%tqT*0V1rbknNJZ9B$%<&eR zrhhVjL-4&_y#gz*UL7xDQ_Ke1+0_s=MOTC2%O*`IEG6TzJ=v5 zu=48FfT8P{O3iV1&p%nU07Lt?cSLm71rm*#NH2ru}ZhbjmruY)*{I6LDq|6^(-0A$b2e>4Y#MV0AnCagoanY|1) zV$()wGSvw#LicqU-*Y#sPC=7OoL33zwlT^UJeFWLK>&C#sM-jCPWgR1i(bRvZfXYuxwdd#B#q6 zbta4e&v3-l8OTB<@XCbCx0j%OS3@-ZfSvnll@Kq<38U^uAhF6UR3#O!W||B&FZ5ZNMm8vyx+ZhUH4<1t zyE6m$Sa$Yju*k%SCIX**sbbo(~HE zGA-iS;r^}s|Ec1t<-dOYmXTRfq4DqCTr0Cz_D?$CfB&WR0V8%Yz({MD6(6AI=N6!a zMnd#z(mb0Ue!etnsV_fORXY6JH_SF$UA(@|B)jF$JgpXmbx3k3x^H*gU$GxSqH)fd zW!pWw>MctK$7>SeX${)F2p*Ykhnu&~{h$sq{uiH9AGw3klxXr9CT;XeZ%YIW zh!^@>UH0cJ%ixwKdK>}DW3L6Q+iBq7zNv6h@uSIz#Qh?wOt$kbAlE);=k9SW+@ z{Kp4Mh=J(QJ^h~V+p2RqSLd1E_2D}6)Y`NdugS;GhMRgJ&vR2806c!1rAt_8K7#^DlFF+SV6*xzyka6 zw5$x%n7la&$Af$k_)Wlx+mNkf(}ldZ)`gSs=NC>cVEuUlGv*WAlezF{`C)*S2enbP zeD&%L!Gf0TyAGH?Rn9!wYBXTwN2A(GavENDXjU77Q3xof(W}rx#-wUNW-g}~PjM4I zv$P-w<8r{eFwaOKGDq!zAgAXsbpRX>ee#?@YD17qkjqTXJ;}nTYN(5F@y|?7dBi+3 z)Fd!44h~8}QX6%({l1q5loiZ}T2);}jzeuQ1DQ#GVBj;MlzF-g$ogUrfTZ2*KLx~f z*%R5!xm`w4Ad04E<{?ua4M8}K`7I=u^3`+uYcFxfk#FZmnu7}NWwI`#b7ltZGs_d| zUXW6LdSCHgx2e>j1kw1y!4jLSAnsHEJ3V;l`TDGqLfS|nEL8mBb)`Mu`XwBTB(}D1 zbSB1NNZ!e!;f1A)?7xZ=f;ev>Nei8|b-X~2F}EILl>fs6)u4$IN{7^L-5I5tNl6#U ze0F~k%PSZ7o@#-sJ0$NEO@cMVvhd)-7~#^ z@^o$1K(B9;K#MH>m7`UlAQ8U3*QW>sY{2#L-u;wxuEv3GuXrKbeDJxl2S()cq7$2A z0Qjx@YvBMUVRuG}h7DN&0xlVlk3GeAHy31VaCzuR1(0eZ^PM}YDu-mgHj?yLP7uHE zlA6U<1NEv9brKI{2Lqf5=fx-cN6n~KhWx$WG$)LFlsSL(saY!nT<|w;tu;I9Gyw@% z+|+XWy6!+xq>P2B^x^^-UFNl>tmXjhS=q!w`yW15tHTHgM>F(Zw|VJ*`dYOw$Yv8V z(Gy6WGtdeZ&!_99(W}m%H9-#10E0|h>(2`DnhIRV$fUO@`*mYy87Uh=*rx1ICh&J2EFV4THk?&!ZDr&$wJ3AthHBRRhbx}Z8!qg5aT4-C{}f;u zHcyOeL9Leyfe>+l>iEkT8pYXIarOB%@|VCgYe%CR>tHRogqTb&=%o+;|JnQVV9$>0 zPV9U@VJ&P0R22&QMgSxLf(sx}3pa_RsaCspEo^tV9rm~_(T<6ji3zuC{W0^`M9A(4 z$3#qoV<>gE-R`#BR#Q?-y{N@qTtyNj2!O<1#9BZV3RM7VLG38a=bZ2Fm%RMFR}Tdc zlql()xOFS<$+KtPym|8Fy}##c*AnAV(nShl+R;-X2)FgO%vHf;DBy*=gKAnZo_l7w z1N3t;-FIAm_tmvVdA#o$O`OJJo-q&$Ayg2e(XZN2WsL5Zy19BFt+lO|MnzORUvOrP zFtv5)U@f3u(=T``7iHhGBAi78Y@i%5G)Bv%jQN>@{U9gAb!t@Pp#1k21 zmM{Q2${24%`td7^>n)pStDoYDfIzVKaCsq~*IZaHSrZsVF;6r$^(`-<&5l3O{y=|# z9~r!{E_WZGE{uT}+ZR?LOYogxsz=SAxmpJj0m!3=G5JP2;EU&=G6LnC$w7lRb~?KY zsw4GifB>9vTK$E?<=Ln8GU$BLf_KOXrOBl3p*A#Se-F#Q`Hi|;<0c4kb=|g*{V{$j zDrXQRa7L4uj9Rx3_~PTUKVrRfq&i^~jk!%2$JyaV&R^FeJZsj3v zxLnb^)gkoLGZ@ptSB2#o2j=yyUcDyp+y_$w7K~YgzqGKnfc57IPX3MJzdU#Nbgkj! z;Z>gjCn8ut=J_`ukL2Zm#zQ^pFH?w?v3dnuLY~aCa7q?AgC&K`f+&Ik_%qYT-l+$=!j)WIP{3Y|Z4!i>q~kdr_KNC1#Aet=6J!D#2R(=qWb}XS%`?kc;w*IaV?DiGmDR2?`%v{RGq9NCU%jGosJ5Q{r*E$`EJ~T7 zaQ>NXTj#WFU?eT_rH}V^s!DhJ1v+Oo|JFT)lx3CJ4z*cY895IclSt!&ykg^gWfMfj z2p~ohoYPFexx&?=L|ewVr~evH6dTB?UeP;!MqVms<35(NPAkbIlA!R*{UdJVGrQoF zr4RJ?xn;I|7oTdle%ayf?KSws8L7UcddqoB@42dG@bQRnq9{d$3nhCL^12q2s6fCx z9r(q=wGbfkm-X*s&cf1ZIT~lY&fEz;{<`<8#piD`ky*Um2d>;um)SK5lhrrIzHQ4a z2#}=RaYg^AS=)vNz8sP~_s{bq*tEVps>Km~xL+t!0F3{bfLu7U;fDSK_w^eqA=h3e z@o5@W3gVO89?f07pd-}q%;rAfv-r$cl-R&cDzvgH(!>y6MZTjH8}=XW&!QQf&Z-Yu z5%t)MwXy&-hpu@3S+#YpL|tDS@#Kdr0r%`EQHPF|is~MH*OTuo2p%ZaCEA9hcF1US zi2!J+80jv%_?+5=jItg_ow z!6nd1+#?LjdPi;(1kd+!%;A^olU^uU2qVq}`VRq0?i*|&>BUP3+i<}!8_gx>)ynl( zdIkTducvU-Fz)M>ENwUI3_ZqXDUAxc;>(A6L5h%^Arl{<_1}3}jfeay`{%o>6nKG> zOc(_>m#1^S8i^owvVY%^+AxK<7OHb7H!}7hS8Qg?auJHDX58(t78&R<;AGU)u z9Re8Bj_l7O5i-bBGcN7Yb@=1fd^xX7vh$Mtdo!Mf|{Xb&KU59Q0_WW&}n74E$h8VM-H#88o?E5vGci zr(WdjVH#Lb!ivaM^d_p&@|qxaPOV*#oXaTzSfDu>Sdb7{zIv70c{wacp1chwlKEO; zMHklKFFC9&V6DNA7M%Q_1p=8aoIGS4bW8`t<%8q&akU&m?|h<%)^dT%SFb>525|~G zUKOTfqc9AcN?5r3)4!rjDHq^GRSWoBa(8_1JaG2ug-osRF3c(zS+lgB zU&g8_NSyxYkxDy(+T{db@BD`*i3$*lap(#xwCsbsW_ISUh^ow_ujr40DLaB3mJ@D{ zThrEV{lqrsP=)~ga8RaXiu~~I*_1SHhBD47haHQ;XZkgn2nRtr!btL|{Vqe0zxs)p z*jjy<{JGe47QiTEau3XUNr4b^8z3 zPjT@AG2y?|yG1s(iYVe88&dmt|76 zgiFr+(K|{q368k0Iy8W&eBr2a;7*y4ZQoaMV3^~AX5>qG-HFpnM=BeXOkzw(ommy{ z0Q$1Cm)G#ujUd;JL~$u<`RhfFltNYw&rXHyIyfr{eo6)ud* zCFHW%C*E0tQ7WY%N>RFP@9aw}%K!4)vn3l1fnA|iXIg?YU=QS$bL(J<;aAnUXO;nf z^3>85{U#S6nLx;yJDr+h)Y}u8*rw@cJE0EM8$fii>aV|f_S<1-L`hq_2g9ye4;`Jc zko@=G(e@~h?Un-DJxw&6-<5T`?wL1TTx(lu*#ntBZ@?RuZoQz2Q~M%{|KD zO^3PT@@j&6o~`mP&f5a$%tg~TAbS1H5E1y?WAz>}xf<8-WRBOI$mh{Ll!T?G&ZuP6ar%-E`14nCLYFr?P z8PfjvRgNE8`s{bhPGyj$M~0DPz^Z@tt)>6=J+yj2WW6=_XBvuOWAe1nnbO2||CO+SMUkugZ%+ ze;ezDtHb9Xn=PXc$V&am07WNqe|1=e>vc$55#A^{Yw#Bd))uhV;D-fcL16(_9}?@u zI{*Ox^hrcPR3cx~>eWetoY|y2FjP-%SZ>b(CVw^@kn(aaAa(RO-#__r-&chpXGMX5 zY0zHvX|X`LaOq(-B&0AsFCYWca$rTwbjGtFAuujekdfxg1)wH*`7G26HHnm6M_^%+ zDP2(o+5WTrDV%9-&N2`aO>-c4urL`iHw6+E_^<@VDjQ6YQW3Lo^T>!r|CvYY&&%dy zIw#?oj80i{hSbfNVeS~)$WfO25t11vf!HJ2EK{PDIjb&eIv&FiOG+PW)uAM*e;g*$lu@pT%#__FX=b+oeLMv)E$8Ju-7>4JxOFAqwxkJL*LSm*X$b z;r*psXATmgR$IGNaFi{*FUI8i>3y>xOJf+OHE-Nl60&5#5>*a`9Br>bLaQ%5xjK*M9Mq`YH`AS|O`Z zh_xg7SPmkS7VOHu_P)vw_a3Um4CW*KX587Q)kl#Izg&r88PJ>HRMAeXn}t|`;^4t! z^}4I3Tz`6Xq$l^x))7RQ96f>Xt2^(7fiD9(bmPa35cpHmuL3V4OLJBdrCB6C)zLX*+U{VY^THl%-ORmGkU zTvwY42{A-K@>-i#=jp`}E{jsju1gx}B44;%N^40LjVOx!?$E#X=CaUb7GhGXOOr{d z@#NeyYU`g%aR4aYq{slum`8QauJs3$Q5~`>w+iu5787JH9a$QnS`ZW{5?gvdI{z_8 zUL>k+XSV}DOqyE9uiR02`vcDvxTSC4hXMhCA(+85 zDt~KxA=4!yjePhZ6S|zsgr?ZG1YL#*(|OR1u3JxcEe0#kfMjnIqDobGihmP zcm`Z~euv^`_gA7H0M7;89v};Q+9=?% z8mNB)FOnUEhsFUPfy&_xm&+9)Zw-@Rwx7?gi@fJZ=rLZop$4}pyC48Vv<^J*Q9;g> z(tOYD%qa#>cYXz?OEXih0SnRwOx@98@SX;nagOh?2jmxWy*DJ1GfmH^( zRiwQpSdcS)>STgD;3PE;npdUIyh@@Kve)6!V3`)K?F=oj;*)*~qiTU*T#o1W^7AN+ zOOP4QRHMRz0m_+f5^iqia%jo(bQKlwJh)JgXAHJsw6}N78_U9eOy5IY!+|ayyypqG%Dc{?CZV{w=pW7dQVX9Uj z8Ag0yxF9|xIE#NiWA9SHRYf>sq@ComO|62u5qy+@C@#GJnsR|{K&OHsB=(RGuyFaI z-L=oIg3wv|&X;O$5k;TcTJq29ybeA$?E2FxAA^TMBaHCG5{|5dyXfD*Lh#JKO20|2 zMWy~Q{I9*Qx)Vs>CMTAF7^WQ&Nbu4V_^5y^1PWh%a_PT*S0O)eUHPu9-+N7o3PF4X zw9^}mT6Q{T(e0`WW*-lV4cmIFnoEZW!w`O9Z=3#?o8Ok;YwDc8;|S zV$&APBPwLh9<|qJXg#HR0%A=}HF^XhqYQJ`Gqoo8@a_`A_;^v>=1?uei4RP)gpsKK z$Gb}x`Hl~FQ(iP*rpy8Xv5`aT&ixf$wOJB%z%z!5tQ)TyfzJgz(`3G#oz?A#MT;0rr~`2%_UloML=Wkf zdE&(?z<@$_uXA98{Ea(jgIiqoy!ibG>IGQBFKB6GKC`#(sc>PZ-6O{Eg=^oD`of^r z(P4B31c?|>&NBi=oHVQd7@0+}Xm=pyFB=|zp`H9#T1SfGQFyEHBg+LQf+1X4sMavu{;T z%a;%UuEFkHnEC3M<`{oC!Omc!p6Gd+h8~8+65xysV<3xnWzZP z0(eZA(7G?&t?qGcq@zxKAE0mNMi{^sG#S}U6}ye1dc zRq?k>bw~PBhr9=*a7K@Qn(~DsmPcEw5GMlztUA2VUW|Mq@dzV`_B> za+ZDN4V@{Z>SW9dgmOx+Jf2Asp8OQf!c$7wWJN|Az+xH%;6pZu$7NJZ@dG(vV4RQi z-lf#Kyrh$VFfcJ`T!srw=QJtELZ3fS7KRL%Xe!iPaE{42e`iw;{;bnxfBwq4&$(yJ zUKLSAJAs*Kx~!>i&!izEis~_Gh#6$!0?IJTp&tMrGI(O+;BeCcTslZbhBudEW|k$t zAW5jKtpDJNGGQ^l4Esy{L1-v5co=ZoWwSp%o1_1ShwDuryo6L{)l?mzMa(JUOJv`KP+-}iPJtN}?LaKYm|TQ3sQNxD5N!E2Rjarx#w?Zb$P~VW&B6_MN2@!H=eFH;9OpHK@C~7 zaIk||G1u!ZJHNF4=#5 z@2Vq;N%ucj+mUnwWnCzg948_1&$}?+?fcQCsNTG}a$1=%2`DkLuF8jMpvWqi4nqrd z%tPY{4Pr=g6v(G8>7qipcuIY+%uToJKt(c;#ZbYycSq5@bxW~FwX-tYh@yS}kuo43 zvZ2L63$hzF%~)gtxsk~%GdfBLuz&f9Y7{pJ3}UU5hNDuM98RIsQjGb9TPhkPz-;bs zw3VDgwWT5}U*FnZce>jX7f^-RK>B<4*KbGuOz(?%zTb{iMaX&c7oy1zMq6;!D3qlf zpjO7a`#>3=9YZMG2&u(lWPaP0QUuTVx{>b&^|hw3|7aOp4b)w(9BkFK z7}V?Xkcr!`^pA2XtG%O#wOZ5Z^{I5K=X(wJj~|O+F{i5KG z<~y=9AA%Sls_1|ImfEo^K5Rfx0qmYY5z7&rwr;jaVdyK~9*WVhaO}qs=MP;st0Lj< z9kXAf12pPdLO~Rp|H(ItchmYB`O!nNMgL@8%$DjFJLP&!D9QueEjK(@i-%`bSo z@9I+bb2pR~LM^yldS2aP`SSqKQbOS_!0V_6?Qw(6&VUM!9xMlsmWA#zp#Y=- z9a!x(y=DhkLPhNz?x6-!r@E$}&ST8JkVa_1~*@hM*M-=pd zRK*einX&@L=yf-V&sh8#5NXKVu$eN&m;uohNdK?3#p5+jjz0~KGZ^#(1Ke5eBbb3y z5Q<^*6o#{d809E~q2h`DZppMN@Z^iLHL?Xc14h-J|8*nz05yg0QxL^=ANo0dBL^T^LV}*8!`H%O33~ zFLubuL+FPAQ)5;!J9q@k!?K)f0kdKxO??J(`RY}GoE0DH%K;^zDCbC480=#rGL*(h z=lb!!dD%oQGoe8im|`;5T!007VA5&!20y9@S?j`pcl!6~M~hy9_eifPL`b+Y(L@uQr$HEEVGmJy0+~>TR`)&zWIHltRX;U%IvI zA%Vd(zWb_5R%g(wSO4PfN^;PzKcikci`nO$SsB;5erEyxzMZo#qyT4z31FtDF00XB zSo-;!OQV7&X7<;&)f$lOn%RyA`$eZL#GG~d(pxrFsMS8+Kf1>ZT|HY!)C!bj4*@ME z_m}hzruSSu6P|<+4RB@!_F+|k%UIG40DFG*6P2!GS5>^HRDtEQs6Jg^gJOTRT`$Ba zlYj+=e3uebRtZ#ILL?Jo7C6u(yr1e%Q^Qx1)@uQ3qlMVN{^q5BeSb6R52sX2Fe-!+ z4Ixqp6ZByxQOfYiemSTuXlG#v^d*wrmBfPY?5x$;Y7yf=F9V^xw%6CF_?`QTrouR1 zyi;8vy}a6&0c0wtnX&?FGYt_V%a<1WcW*5`q_!^JUVTst zDN%Ieh$n$0fOr)44_#NQKgek3V0I?z8`)Na>}^}>6LPZXuRc|Y;?*09IBlovR!ZO7 zpa=)f@V8xBot6zo376#^z5PO8g{uRy^QF2$6w<70RvS62N*oG$IRiUmm;`y|X*2uD zcMGFe;9vsc#*``>bsySQal`xEOYO+|TCTh7SimxD#Q9v3&_9hyx9USPv3J*t%8vNFHq$Ca<9ROAr?x>q3&2OADn+iVJJla zUxqzej6{hiGF_d7a-r`I7hD@>l`9LIY< zFN`M+r7%(I?ASG5lnk*0L^vc!l$eK%I(JznFnWkv2`CT7!H%~g*1X#HQ2&i2lmlXb zgV+R0`N|85Yf3h_oE>P`5cp@`s;$I<&CIYEm){Je5{t~2WXdEF05XG1xEdR6)kJ9 z27e)8Z2@Zy-gwAJdZKrX7al4L@cOD2fQmfb!HJ$dZ!E;NRa}E#UNtadZ7$~_XIzda zB^ZU}u)qiwc$b5!=>f1LdP5q>L>!FTR7JQWwL^kJREOA{*fZE13&v5?!HJ4&5+3TZ z%_IF=Y zf(J|`*6e?8zoDQCI}B6L?TPQ0)3Xy<&1hZslEjghE2Kny=F!eemwPE8me>EK^}eoasR%JqfeI2O~^4gE1!mfYCeParE9X!(MyjLRGp z%fEZ5{;*tLbizs92m1@eAGx(msuig0ap?R_f&1_YGEtB0u8a;>sp|~$qV*m2e)kO? zJ*6<@P@(C6>)w(8a@IpgRvudjvE9(gn}S46ZC(d{`=u3gKn|H{8hU|Ct-t{J=Q}Sg z)v{>GCt#9zp4vYlS3$V2-l}7HQqvE;sd6etN{s8X<-HUY$A9IHQgPr&MFW)ky$s~c zDrZ2NY&knAlFlImQUVz%Vp`b79%leU_~#y-{qaAY$2s<={LwpV`H9$Mj~p!3z0!nC zNV)G)S?2$VUgb&fv&_M^k$Ag_Zs?sjDpW3QAM%2jo+HgUKwG`dul9A!un5{D?&76}fBOiEQb zr*W~sJLoFam(?D$%P*)Lo>g?g8(y%8Gsmlk&sW|lrAYZ>{fml1iYzv0ff(-+zzmOQ zVNwzFAc}=(+3(lFo2jcV^{nd+Yh~Zyu~MP#Xy;N^!t`XWv}z znn1K?!$`F6*k9X!s4%Vq0^ymFT3SR*Jga5<7ZMU2^u#`}t5&bBxM1mb@0-1<>5;)x zln97&M$e?U%r(78d|iNgU)~u z{UJ7=)*tht4#aE7Kum?<5y#^W$en^X^x2X0%)X_cyRmBHBMkF&fZSrY^`99a#~Gjm zh*_0wTrp1}2Vb12fL8!m&g@?_h7qx+`n|T0IcK`7TwFb{I|dk0xfaauF@M#I%>x)5 z2WbgQ$_{a$2w-uU5Jf`B@P^3` z5S%y4hUIcrM6SUatSw-z!Rv>k%RXv2g%hnDoy0eK9|WX2|@zyfaolGy1aBY#11nPWWELwRjq z4&oqlxlo=WMadj1iX^MfFj2a9zH7h%E;+A~Js30Tw{DsJ!Od*vjMHjwKV!z6Opc8> zpKps6XEdDyV_j_*reov8Xl&cI)7Z9~G`4M{vF)DNYHT!VY}=gkelznU_S);Yajk}{ z>v^2K=goJl%xN9vMUn z)nJ7oGu*!K4L$(Xmvwf~jpHR~$yo$>{-r}~+qul+Y6#NNA@yH71oEf$ul|}Jg&Naf zC1r{HPgFY=kisvZ*PrKdP! zDivG^OW13aR-o(B5j@d{aQsVHBWHHQROobW1K(PXcGLx>`LaD9@U&TE4k=Sy;Z|8w z0D2+O`79EQ>xk?V8Z+L(6{6w(om;V>?jSy)6pPSx=+C_;-~exDbtkD-pc#dQNvV0; zwh&*7pVIhe7x^Q?<+WV?<85)dG@+;3@M@s6J;_8?yaby(j`*CB_XZQ)>^McK z$i%_s+yYT4F*P#;zfcI z^PVdF<|)~w`rl9$NNr2hkn0=^RL8)zmvvFg0B89yV04h+CuQ;RiJ;Bu0QX&o=g)c) z&|4*kPk#CH`b|(fQ#6JR612)|#+NgIBg8k3p?oA5sE8TUFdhpkhZq)L%i|@#XEWDP z%J=m3dZ|%H4?M)nSMkEdDzLr}O50wM2H657$%=a|f)e;LM0AX!{BMVK)r%<4TC^N9 zYZFKS$Wot|en6WukWr(X{6~?ov0Hfp10Nsn7qft&(U0B8@M$hq2#uSA6upV6yXl&Z zB=5E=qw}~3q`Spg0F8mF_bP}c_qAPTi`di9UwGQZE~k>^C$|v=WorbJs4VWJZ?oIcx^BI=#jMuaTB5GcA5Rv)mf&rqX5f-Q5m_0#L2i$K{!|Len^6BR@SAE}?UGE2xsIcbKz*uR5QvP_e zB$34000AX*{oou`x`Do1bVZ3vb8&Ecl{-XH`_S8#0&-X(U(b!Fr6hFPWOC!9;gp3Q ziIsH8<9S3fs@A860I z6;0UCXkrrapb^!K0uesO`l1+u^bMRZ*RghBIc)l(kbKNcu%wr_XVCuN6x7hH_Xn_a z#h2ekg|BH4!Kgih+o&^YVVW!2r7IGa4Hb8h^dH>v{(+bh^YX^yL8Ed`UqNAUVjgk_ zRO6%G9*}skG*5ci_67G(?D&{wvst;MqWvZ%J)&I|3H}B9a&+02M-b`Ub=;`mgPD;Kl$~?n6Xf%2YLd&ZA^19k{edK<3 zB0PhmY{~dGIZ0OD0{>gXlkgaHY#M-p{zw3ZCo7#WV~8Vpvdz3Jv;AFtA}DPbTCW?kus|g*2f1HAjSAO6cZ+V>m0_)DC2BSC$M!!?zqNd#?0uD zy?;@yr11)#1?qZ(A`2d5_t=4E_3A`TH|MY3jZw~}8iWzbY*E@)xN|84bZ{gWHye}{ z=4O5><1>`hn4~nm@`=J{t%(}PZV0J==}B!ELn{<>mhznCUOcq|M9Oncxr#VGFMVwu zA+Px#wekq;%+PHDwdLICWack7Rd7G8SY?8=Pu1b~0$v|2bGRw%`1!smfvH4iPsk+B zbV>-vuB0lcdfF;=E55jJGkh#+xMe{ikH^eZ`cHVmN*>G0h3re@kwa{!X}!ixh(AQN zo*%-o!Y_65fyY;(6v5%RSj_Jt+Wo8)GqU8Sl)jcLb;iS&>Hj&>_Oh-khmx%D%+`~g z=J21@k2k%Z5@{-{% zNVaOr-{m-^FHW>h#6)ZXToY5y*Vc@@UVPiHXO4B-1iXe9z=NFRbj?95#D^n0=9~@u zJp9N?QVFa_ka}LE=F^^!t)GbC2{MXSLlC>3PEFqjBdTrPjbbJ94WXUvlz(o7mG1J- zFnwqVYwDt_!W!z&bgK`d!6idU{{YXkjbB%L{hIio_a9Gq;&18o`I=|`(w2qf*lehT znYnwTGA;b1>i-OZJ!PV*Ex@ULFAf9T^2$%&-F@a0qpFaJMkEN5E(lLlZZ=`scuWh2 z!FU$dkOG>o8*!?xPAZ7I>Z;aTE;$v;q7z?}RzDBX>N%HZwp8V{M)tpPYxD)_=F%mg z_k4x?qfa(1y7N4*S}J8eMnyya^!?PG&9C#IB4&MGc(X-|`>+u$y8oLIuMUphM^DVt za*v_4Y@}R;NLT~Si4Fi1rB6w&a^q8&f$h)`l0emH_L;Vjta+(i$}6QOT*AeZh$xBq z`yX9T0JFoj8JGgTf}CMRy*c52#viL(?vOO`HDlC4D;h{-(@4EJjSD7jLA1LrhqOb~w!0qb`^HnJc#tD=NZ0XwGZ}gu z3r`CZnzsy5Es$6YW-mBYpXpe)KqpW%XCT@g!o>)*Jklpao9SvUBwYoJ(+wi+ry9K6 z-jrz!rv3znDW(S4WT9L9U`1Y2A0W}L<6|(hMW2AAH7~P{_8Na@$M!5o`L2UPP)jRh zT5#Gqvl1M}d*8r*C#k_of{sFXk<+};ZY;9t!ZC;vDhLfo0I)Eh8keNT7Ltj5zO<(M zzjn|ejhIj?SgV4u^##hO4q1jfq}%uxCl01|``6Y`j~^O}li+edV7bHoGjEtrv;G5J zS(BKW;A;a@2f{sfavc{x?L93KU(*g8Bx~#zBNQu)VctdG+0_x)Eb%#rEDuD+d3@tf zp7#XHq8m%r)zJaUAMSSlHNX9Ml#dSd)w~hIdU<-lhc&1QE^Nf_<|eV;{dzT!|60hP z=awpXj1rX3cvKo5Hh!ch(WJleY}CU`20+XF6w$ouc__k_vIN7sEj?oFws1+?BPrER zb%u#-mP(X=YyjMTtf;r>yg_qm;!8nW$v-bpVl&2-410)D5hDkF?Jvu91Ck(hjAq&! z9vRvs>T3YyJst;_%jCy0p&#lXt5-dyH%G$h0NF)Zx4l8;krc2Ob?Vcy-D zx8Ad1&%#8{fi0W8NSVFpg{1v>`@rdc z(8Rbky-AaV$jDd8B7?fT{|p9l!?@QLvhZqhve8(m1o2iQ>gZMM)nI;?2TH!Y#@GU? z*SusTpOamzvJYpicW3U)v7qDd`ed>dX>z>JhS__S=bvL784WxhlaaHukJnz`?VG7r zIEuh^vO8kr%}}L<{UsgG=eNA=%ELuQ(AWrfX*ClBjOa;zNSTVuvb|NhG_N=UYla?=8$j>Ql~_wnSyDXGk-+Dre`WnZtym;H^m z{MB78sjELu=iBMU+>cdq=8Wt;hQ;Q^_WRNoVaU*_u))xEy=R4Us#Bc zjR>X7c-!-ZbO-Mgzn^h4My&H)5J<#SodT`#IEuM&EqSaSY!~@Zc_GEx1CbPxXqn3i zKBcC?G7viy5=ZjcwL$@?ybCN$5wo#ncr1(|f)Tce7tt&L66g4u5#6&O@i=9Y!_asAuoEbB|Uwt@n8Gc{*3%5E0fs-j^ zz$6yQ?{;b8EP^4Jc^AmEkT9b0#+nhCzj{?64hm(Kc#IV=gLB3?Z1gfkLK$|Vo6$VI zuj&x`D%eP2&@#u2;EJHz)JdYfv>yL?p+VzyHSKX<*4&GbCW_f-Mi-$`bqQ|}pD2IB zWtbd=!jJQEDtiD2vUG`?4IuOuYt3F;V!L(q`fI5DHT1$epF0-8i%ZtHQ@7A30gB^1 z%26=YUCZ<^MV;C8NaW$V#D5ZaNs9E&plBd~b8Cq3&Lv$a=J35d= zC_qj#ejtY2?&tTksMec@(*O-{&(;M`G4!mspmY?MqpBs2*Is-g|FV*$)3T0a8eiJg z`Tn!OBJ+f`i9}OxZ~Xy2XBP5LlZ^4OYFb+hn=Z%aaK1O7;<%lFscNqcBJO&At=cCD zGRM#R0nmJB_MrxE`HyXLv}EPw_+}LqPNXbls0E5zOl|`4!F{ZUoU(8;CAWT5`IkoR z2CU^d3&`U2V#deiK$u?B1*eppA0Q#@!Y*YoT>Ew`1O&cLcKCb966D~8#CRM(K*{tE zK}4$3yWt?8CZcKg{PuNn`|8kV%DrK0yPcmnmEQqnTCz7}Mx)^;PzkHMofFQP#u02# zd35XtKI7$MaIb{lG#RDd*WRk`wtAcGj`HJBwtx)lCmZAnwY!b9zJYq*`jWq53*$5TG2Kd%x`l;^Shdau%v_O z27tF%N6ZGR8``zmsd!)8f6{wy&HZi=R{!*>{j31XQ=Jo0yJGNAsunTOoS?C={kkR- zSV|03g(f1MAmoPFWn&ecX-HV_aDB8nKzq?IaP=ir2w>MwHL;osPXz_YvDxab9L?Gn zZ<>VC(%`~It)w=!C8VfkA+aJg3W5b8DgwbK_L!F)@k#nTyQ1!l#{m&#d`G@>`$_)T z`u`mFFN9Su;NWw{WV+hV9Es}j@7!T#sZu!*T zCBuMA)x-c$uzM5tSss||eYmR@!h)4O&E@CK$nCj<4q|xoJ;0YV=#PSpAo-9D@eWz)BI++pU|GQMRyq4OJ*h#l z4eRhgfO6gnBp|sP1A5CZdP-9J$!Xkk9-t3Q+nT*lvUuN zw&h45&BmCog>5^Yi!&}*LU@k+wBBpRQ@RF87g(N$dO?)N)5ZCV-#5pqDoDU|;LUQ0 zGyVZ+l_wI75kL;#H2WaGu@G5W2_DAqsUrAi3bVxYXKO#j$A!3p43@2(N$MUR!fckO zNJjKFzY1&+%7{_!C~2-x;3?J_D)H-pI)>hB8~NK=90fYdv?lnbx}DQ`3ICWjr=NVh zJPQT=h@vO$Whax7_tVl6-+0=fYiOhNDD2(9+Pbvb*VmiU^f*mc34@k8mu-8jpl6B} zNMk>f7=c=Fl^Vyzm;`%akbvP$S)BmyU1#PVBhAu9RNW{5K{Zy}wnM8EH+El|yW|Vs zUR!P_L=Ushs!q1NWHne%j(*fY7*jmmp>2)NzM_mvW&dNUmLy1zfq`#~;8F~ngudC&Wa@57Sy!%+39Ba$J zZ*&^Kk=}WF z>jp@@E;9m=3aLrz4*h%XlP5WWrPch&xJ~=FcXDlPZlHiM!#IN|e9qOT5Pb!ixl({b zRmV`_S_oHXJ0GylNbK0peHAcdv8MLC)}mV$^qv35G2G1L^JnkuG5fD)jD*D^m{@re z*jHaeTytR$P@|(;Id=OlVdc!2nV30*FFmy4l$(eKClG^+N=RurkBv3p5gXp0CHk=obG91^b6+L-0S&6Nhwvo{blmk>|xl zWiQ=Q+OW6zRaSQ@+@JdK(>Joc|b0juHzCy^SV)+gUgKjgDQ1 zcZZfD>nMaOTp(cU=K68!^IGq^QA}W=u|*4)i3}x&7bD&-HEn49Z-W?T!nBS~SAcAR z)D4V~D*wm(c*+AL1pbma&!Nt6N=G_OT-$|4%oYBKiDli+1t6DGR+ zjLam0vDoeg$l8*g`iMc9^Mbv5ZzC9YP=(1tqSVSg{~JTR)?$ zyZVOk4ObU=F?RJ~26ov3h}1<=Qk8 zV3?OQ(VCZxI^3{}CN&AEo&o_wmNw{eG6$fK+QXp-*p0wc-(mdNpCJ>ES2kLevxg&6 zpa4Bsvc6u(f${Smp(K0by~*8q2FsEWR82DCE$}t#re>K z5oLoR0pIiLK;#HZBDos59?0I#_D7a=Pu3JfL&DcwMtPpWOiqTG|6T zm~5)(l2iKm=Cx+DTYrO6Z`XQHi$WRu6|nKkuQJAmDov|gQNKq`R9#JQ^xWs6^F5P| z6j!66j7$SQMszuLNaW`a#JK*!FEIs>8_6bkmhiK;!+x zi^{L^8x2f@M1+^8RxN&NM+-8!u?FYs-2rn@a#8ae{gpn=5+NlAo`UJ5J-1@^{rnul zS~v=4x{;63+}9jARzAZJP2?k^o+a|HCHgMgv6m^w$xHIYG!(D%G@Qxw%_jJYS9eY1 zCMupW+^e=CY#i-j!jVYG9TB}w@V%9POqFY{3H1)u4KZVCK6WON6|TvM(>3Z11sDU4 z)572D-)HL(41fj+p|K^wBH&hiHA{vx?u8QvTRwUh@5A4)4bS=YpBbdUG;DNCUdp#> zC^wXT^R_ri=gFD z0iASiEk5aaIk+&Od%NYKs6%A)+K`G>{L>QyE8KT#K^>bK%6{};naGT&*HwJ9 z`;W;)+Giy!gH-1TC|$bw@>FJB(yP^|aC0F-)QlEQ<8O9*&|@s24ZL!>oHR9>%9^D$ zM*+u*SUGW4GcBeMr(5={-D@3hjHLP_$=@Y!HwT-U;fo66lm28_(r!VVN~n47#Mm`Ua$XTKHe0QUa>ioYx^8ii>=CWlczHA+HOBp zPn{-P_9KBF%=F}B$eXz`-0Ch&{iBcB^V!#M=WKj^Y_lBIBAxroDB*JPS3(Ptk z9885=Bvik313_8;i%?dINl@aATV!ROR@lA96I;3yV^=yF&);6GW*GVb>qyRL7li-m zf2Y^Ke%yl0eYaQQ`L^pSQ{lJcr*2%&Tg&%An0$&iPYK5Ir{Dj29H9j!w|_M$-AssT zJl>(=Cv=>HG`&}I7tuKg7e+P({L3c`8F>|zFQ62l9xrKG zn6_w)#yPy|tTPPj2yY*vcY9W7L>T|0hHyf5M8ZRdNVyAW85v{W)erW(V0%h(;;lyi z0!_;(`_k17)+Rdjom0v}ZOWLkTtf@<%&E_riywW6=K6`6X?xv~pbA@(k@JukVmh~D zmGCfmg$U53ssV!r#oiL0k;aU)^r1+Oy7YW!&uP8z&wQRcyrOv-yM&{?i!fD28C;M6 zF^Mu2%g;W&bk}%B@M`rIcsNn`N}UB&sNi28PZ^qZXHOQFmz-)2KhHfaR^H+lFVlp* zszSHPbi}@+Jvu&64+0(c2M>KfJo|b<39Oe(c-=%chZD1YbfKkccHOSg zdVsCc#}sj+J{coA>`fV#M7XvL$24AQ>c(xtlk~bf;qj#)PUZn`JGWZppq@P46e3AV zNk5O;kpf9d^u^fQTUL?v0F?49pc`J7C!ltMehNqONn+dJMRVR72P>A&qt2EHX{YI=51piJ|hsrQF@ zS7#ajCGEw4@P4v9J}cLa-|cHM_CLLHY$Q^#M*N4Yf!d?eUB6&_?BS%qDT91aG?;@i zi+tP{4;NfBtsa->W7$ci282D0TL6$O>4M!)TyYaRki}^Hrr8d@tK%r7wJ zM$c0}PGZw;-z29rxx8fa+#6-m;cq4M*MZ$;jvjn|a^+9_qM!1>C6%d_56P>uzV?tM z4@%?}$?~gsY}EAn42_%bn#<~CERFJN^!s9fbL=ent!{Pp$B!(>Idr|-%)Q*I1<<&Q zAp%K)!D>tp>A)+_7ozKslL1a%p0RM-J+pjD!49zn;#6k^iZMMr-b7%7Rn|&hI0q7U zV8cmR8Yf1L%@ulbsgm52&r_N}Z{$2-%k7>?R^?q4N`G z*r;l$8mqV)p=&v?zCYprW@o-3y%ZmhiBoc=zz(9q{g<|BIP!AS7JAR(-bVw16}sjL z`NJ&Q??Hv^(Iwqr5&#j?W19wk+j_U>gdtmYWZYk2hujc-deSjRREGLSZ~}G^>H6rD zjE1u63dclgf00rj5zt?X#yD9OfQ5r2#zExMn%TM#g(;}c4cXPQA0{zRd$e@MhGnHf zh80RKmARa9uQf<40y2qA>)30fqazi^8|#khRnQEw>cBG!s0zBFQa16&C^E-;U4N^t z-c4(-c@=yD{1)E#)T}F{)ROt{UgG%W8M^kNEX}HAw5H&@ zluuay?C}=YFsa0>c;kZq&%wP-vEk@eA#5f!m_JrP;Zk1(2w4UQE3U+lInJz?1sVmsuQI-gKbuP#UYtz= z7pOtVEkdO#%~mf?-Md;)JIBZ8HgWcOA%Y@^bDq-_nS@S_7_Yz z--<3zH~k@4c1hfLysClWgs^-vcQ8Q!);oLf!n;-vkN1ah336+ojgR z4=>yR>%H`7$=Bd8;M$tgx{-r~-@8lIG*KvAvQJiR8Hypb9A1cxE8u0Ulv&Ww>%@=0 zI-kUN^CDx(c_-j=`n6l$V(0{{cd^${I=-PH=!RhUA zblo0TP*KUirGf}c=IzAof{N`_Z)z1PeTi=D5C%Jp)N~U_dS%5Z!Nw_i8y2N5<$SJ{_*d$^t&Y-AyK(2Q zB3)`uup-y$=#j>~2fa7}KXZ>UlPbN3>&Dj-ZezMJc$|0P*Ht>u;>qWJshq(?hng2ehM_7PUGZtY2SqS8}_xk`lVCed2E}Z5liY1@^;UI|63D@?n$DM|U zFxu1H9Q()lumj(Bmj2ISEZ2Y}Fdcdl0N&ES@}~T;!SP5E9uX@gKDE^iWLeT?Ui`Ss zBa?7|*DM_nt5@mBE?7Quoru`@O2xGvgBs1K~BKzHm<- z4oG@0Ao>e6&Tm-N7-_e4p1(~YnuQ<+N4EeV(CH%ANdh2FtTh=Oa8(93 zvW|dW?7IDi*MBOk`Gx}U7$$7b93jw4W@}AgxR0a?oipq!iOXS(*|$i`vfo9OiI6|= z?0-2n7$s2)M=f5%-;SpR>u|4YAmsJDBWw8oE6u`Yqo+%D_zGG<(7Yg+jiw6byDKQX zOr%oiYN(=4g-s1f&1Q-97)Gb|?*<-hUf^f*4QayW$$+vgsTP`0pV^3g4T8f?6mB^# zMx;%fDpnl?@le@#DLF(Etx{6i;w?ce=bXNg1;=#~=%ox{PSt-nHJS_wb^Ytu=%=UySCH$d z%9&ejdg5yP3r)35L_Z4Km{iVW1cK;?5HY`Vw9bd=O2zrW1WhC+68 z&@u>nSxm)+BXE(YRD}M`0zcmKe{3!pKB;v*c+o9JoBZ0_M3HofI?7ejVw)s_8q$mq zE{(ea`yA4Eh_cS0taV|58bfwAZhv0aArR4VZqTIvVeKBxGUAtU)!t!v*M7e~zDk#Q z!hToU4MqGB@H9r}TOB~$@ZM3Ael6o~>)6fH@&%gOxbZw-gZigp3bh4-Mm?B{%&-;n z#Ma=kq5#AtLttU@(d)qXtLe@HEh+V3ivfBFM$wls96M2cEJ-#Fam9~N)1&TyFG4p> zU|Zi@l|`QZYyroJ8izA(9E4d{_r-iEvyjfJF-6--to@06MEsI;+l*jy&x{7d5w|Yh zYH$4OY#rX=CSlmn!}X~+b827MV5qtuO>^z=w*uPg(Y>Bhff!B(V zd*cG`{$pF)FPnT$Pd%`{C2Xo>KcB)X96?G3U)x>Lvkfs5R>~M&J)RTSxZ)^#55Hi+ z!VsMIS}iVjUGfg|6H`{ixa(&xYh~fUp*wZV0&Wpo7ukr}oQ>y`8Wyu_@7|u+yFWC8 zMkp%g$TZ;qJ~V&|u-)K)abyg9*_n`!fV@tg!;QAe;B+QwEzNpNSE3nAOTZ~nSAf+V zh+BNh2wH)+Y9lN?8Vf$oV5=NgI`p0luhq2D2&&AK5Egej8nlM~-U<6RdYBSrny7;p z|Lg!)Y0P?OV^*3pTm=P<;Aaxj!7|Udr_n8+Gbu1ppjFVJaG1d5Wl-=KEgT}AwglC$ zAVw_7bbl0fAvhvrg__wNG$ zW|eSYYRkGig4K~Kx5FvFz>kC2gJ@AjU@(}VVUWx<>jPm^wDB&GhjkTeNl}Kt0zK_J z^2Za9X_2~uCr;Hj)?Wrzz;M}MZ;V~tW4GSnH@yG}V$;T7NxITR=;=5xa1)UVw!n=( zeV?7(*SUN2?jv2MDPMHr3^i{sUN;vo0o=C+r!YV8ba}n+7}^I3uhgbqwIJB`{srjC zgskoNKzFMoH4PF7We5H$4A5H(!?BLnNkd#}EH z!8a`*$T;ImdZaas1~vfYG~%J*=ArBatRWHUg@XF)wrze18^-mTvXZF9RZ?bEFhUB9@NBsnX znXopC?5>FigcC=`Wq**6;(2%bpn@w+f}g!Q$#?XiT}TgI@31&&{G{iL1nNvobQ&AY zblP+`KojySChrGn5B=Fs!+YRvWw&Ja0X4G#63k4KSs9QSNwQiNv>##H(W>P)ss3-P z_)wXAdlxvq)4bhq6ctMMPhH>;QIr>L%6!x&^2t(`%L8P1G7<;BwTzVM7K^ORc{e%G zZVwfk>lymh;%?=YCN+~x8RI0GHL}0b-BcbnwrrneD=iT0USJzgAo)^k+#e%FdANSwdMu6Gdi7Aae>adY2O+UvbH zDLon(D9~2D42@2yPxIhV^gQsBbYs ztkdpr(Hk>as!?+Tt-oFOy_*_{BE)C;hH=PzFT{eX^JJ%fE0agMU@AebH`K0n6}FO|gePcKK!9jB_1+fV(vVVQ_E6f0jfl{T~Zg z{aJzt?b!^@L-s(9g|DsKCqGmD53hw`!chMc25!#3PVRW_2hfy^is9SgaZ6bX;_7`B z2+U3QO02Y4ABx;mcuhS$wM=POt13$Ftn0o%E(9PJ4>GV<$2^}>f6CG$vSDc%X~#&c z7^wq&w>IS^hd$TdPVJ$v`%YnC?UqdnFa!q(6PJK%Rnh<6-E;$adXp>R#op;XICM5u z&v)js)DpV26Buu#$HWfNszp$|!HSjgPGncuf16(Y(*YNq{^wno&uXqYa~||dgmo;T zqYNQDH`e!!5-3zh-fy=KH>7GVs4QOeer{%gq|m{KZ$&8Ntly-FCjmjh^0^JM-+&wh z0Rb#C$s2$q3GvXM00PeJUA_Zx$WJ2U zB8cEIW1`IY^9y3pUuVHz`{d}aTo$Hs5D9+Fj-^(ydTp)?WA>{Q ztjq=Nv~Cu!t^$>Tc^v$q1{dJQ?AEweq_#nI`%?qF-2&PeIfHldRNs zV1_?| zonJe$Sjh?s)NWj_k`Qt^z>uqHUjKsLZ6~-09A%C32gZT&Vt+N`mhAL0f&8(7%7xcE z+)K-~`Q1I{n&MAIR=XnctUEg!Ky}i^M)nCQ+W@LgB5y=^vrrHFkfL}K2^&|jS@{m3 zcHk!pvnz3TF^Kdfjs@BBcxG&3&T@h7FLla(9lCG%;<%+2m-am-6Wrvbvbw5MZcyEA zZ}PlE{cperVwl9sdeHbLR&@1~DJIQBc0zozEkiB)R)TC4bgFPC3%$}v@xFMgahlOO55gJ*vHqh-dO<#jx4iI5f z=Zp=&lg*u|yy;qo&D;D(EXVJ2(%P%WSltZz!_-z8(_XYUqcAy~Xl0R9&eF8bXl?Px zFtg`8IAt8$4O@MJ9MNuc@3ju^6J`|C6<(F_Ol+8p^!4!a`_m;4J3o*w&iHlPkpvCC z8q$3ie}m#>Lk4-xrc^zg`4_%8qeB|$_Av&~L_KFKV< zb?Za&kqWTMi2q62JHRmwj9J}(pZ1&DMCDA@2_rhTQMzCpq%-xCzLxAqlLxCaX2HiD zmaeB!5cYLZE4T@rS6IS$)tnM^dr$Lj$7Q8>1XXU_t*~~8C@o3WCQ84Dp{b^_l@SVQ zwl>d^c-FIWFqNMt7pkluIDxnGaQ2JDX&&LssxLL z8Os^N`B^>b0WvV|j3;xNanOfHBx z5{x}m^yIHc$)^B{F$+^B9}Sji!|Hz7XR}FF&LF)eB!;Qs~{Q?obpNT^U3<^`|* znO`gt+a=8q5y#a%9w&2t{cVn$x8D>LIVrB$zd3*CrP78>*ZFVN3cELa!Y`)Xr>1;N!W3|naNv#W zc({M&>45|yY=%oJwG3Gt*)wNcd*3v&fBKhB$!3YwT)(`?6P(-;P_SSd`k5$pC62l1Yo^av^HN;Lum%$NksF4NWZh6S zOJs3ac1A>GC`+35<|dH?H6y z;cKyzz~?K5@kFTL8Bh`7%|A^YH~5Nssl&jeUdj}k|?ssdpvKI{kaIvce$Sbckv!ErgNPRqAYG|v``^d|QGIgPUYF0+8YDv3Yu zIhzkBJmMSpPZ8Pf0JH+lt+)p0)c_xaj$XlK(ZpAOKRuna%#xRT0DSm_k2?b>jnOH2 zNc@i~sA6Ke+QHi>pkphisL1Emr#{b*lh#oHq)m>uZeY$ge@G+DR1INc>Re@dwmgMh zoTJwDuh*HiazNcD*Qnvy8;Id6fts=Sd_8l-_-{A#py<)a9%tse)tv{s3TVyg(D6Bo z&<%$s9>e5C2d~>fH@{i*s_Q8D4Is+EiWQ@1kP}8_-9#evwkJyvnLb31k=G}yWVo&R zi#ob97vZxsCc2?y(0(r*&~4s?JZR;8>_P&W2vfZl{a4;{pM@<|+d2aL=js7>K2zMv zA-ivmkZ+f^A$9vq4M_9_8O_vDpiq)USBa+tZ7GN#44AiiJ-l zYUrvdJDM4SWQ;~*5kZ%$I0X9lPjCdh9imun#cD@4Y8|_N8$=n)_L2SWF>D&iiHkW&&vL-U+n%E3X!$1?tBs7-h8Nm~k zzpBCaV2n*13*Ra;nLQw{BWwW`Z7`CNZ37Dtp5=OtbuGzjQ!-b6P2N61Ww*X|!Y@Rx zz7}#Mv|5ZqYv+A_&d?+WwPndRV`cGj9MLxX4unaR{i%o~tNhH3s;SlGEgr|nf_D59 zkK{4PpcM1)HBK^7gK_AOQRS4T6euY{gH^%*+tI3)W%JV|3XO+f=*hZPWgb^14TLaH zjSr$=d}I(Ag()`CiI`+Ii*&f2#=1LPQpp!)>D1dHG(oipWXiA+LTy82D?FXEk%Q4R z*Eckr=Cm!g$b^OQcfv#XaU$pu1Tq1Ie1x};CmsS3x6uP2ogWPu`}#6B6K-z@=!|pp z;eu}$7ne*DhgbFU1m5TxNF>aPt0{;cRCdeOUi#^v3D~laXH$Y2z})GOABwOl7)fReLQ3@+hAIT+ROAfhe;9W-lGz0XJ{ou5ZXKig+QR4nRt<2*_;%Mdc{?{ei>a##A&sA-9 zi3dYBP41GXB7l#7#$`j_ZG%{{7dIX-&`W=Hyn?TEai|elrYe+9|V)7ngCC zY!wvq#`}$-#qctdndWjIPTa6hbNg5o;Vl z^xq6m@Fsd==h&fEj1W~fppzeEl4>yG_~VMV7iye!&p{xUz;D45end6$AoEK z9dpxy%`JjW63#v&9jmIkw5L6<=by>??U(OZ-{$j9jm$&>Rk`Rbi*(&E6adLvza+G|p!Sr!j{F9?O@y z0IyZPCMvXfhyJFGYPDS3G=FhVwSip|;k&Cn;AP+U_!YhL;bl$hcAQpHwub99zgI|2 zqHJQ~Ve+V268Gy+5lB-!0}#{IO~;EQOgvs9bA!KZ>-s#u+0$}m_Ew8~zv?Q1_XbL) z;yxDskl=Q_#Q#$7Z`)@Jj?{3%rg5iQy#5_F*9?_y&Q@hYlq6}Z-q^C`VFhF`=$LTi(VUG2RFmA|*Njyxlf9E;=#OWU)5 zb7GI=`*{y5X2g`!fO=`A^xTsjp%b3E;Sb0U?SCw{FcYq9!gBAfW-)eiHYM@R)unSo zz_)MjV(e^j`HK+^`=FoI&iJ+3P1c1b&*oT8kqp<5kl1|%b~ zRjAe}nVg5>z*)q#YS1-ujg}1l*||O6b$h_9m@zL;$xx{T!xzVKD7J3}pwUVSN=#ahKScm!b1Y_laN-o~dYvFsp@T2hiGmu=-5K;gpbN8MGPM^puE@ z#ej)^fOV?R!pJHv+{mrb_W0G8%0Q#{!?p{j-)=$FR4P@#xMEwMb2B9MfD(AWGk)d~ zwb3099*Coz^lTnFRNn`2W5;G3*y-t}eQdC)&O0sxcxM}EuKrk0ZSwfu9{&Zp?JH#e7etwFC}37Mj7_6 zV)i7({{c}!uD%q^oTkK?L`Hti8s}?F3$_giGBha@Oy+EM1Hq@pKD%Dg_*`DeINNZCZ0ikFVmdpah#91KBC`1)HF z(G|y2n}`ijp&7zsbIp3^KN}P?00J}16u!ExCSHuJZ;^ETIT&LE96^qf)_<2y#~E{6 zXH_Pt#WMdEFCt`RG!vEos^1(djG8D>3eu$d0!$wR&=_C;@fp9#XjHbfnbfuMD)~23b zmVytlsv9@fs#8pYq!)fme>X~jFE?(g^f;YR?e*Qhx*5kyUF$+d?4LYUa&#W}YPrdEQ+sExExUiS23MlmcPbbuxy7y*p>Z&#ewp;5YUQW)R42q@h)69j7~^r^ z86=Fgve_Xelcv6~K-mJPE2esERJ4u-x9`8-pRU z6^kgXpzz457s`)2X>XN&p%!tg9CBUQQft}CDYa@xAc$vVq`F1 z5`@Sh{|k@L%4e*bg(3i%W8dMLNQBsvI)C?yC7N=?JYsVhbtyX^I8s&0413!pv)X`y zL$}F_Kvu7GGsbi0hK+?0v>_9eck`(i2-4Y|8)-DUL)V>B_j_sqWd3LYs*QT$1RmTq z`y=0g6{p{yd9?QPM)>e9=V8gf7(l-ARAG1#fcMB=ePsmALP3`R3GzUKg-bBS7$E^0 z78Y3$nCE2AcrF8;>}~`S+-T?adf$*X6}wz%V2U5BS70V#GPPcS6A7Hct7-(VD&!4^ z6<4oT^z>wWJW1Gpr0&o)_zMqf3s`ILrw1oFJe~X~b~3A1Qy4?!YZ@z;bDcadIC)(Q zWDA$%OlLDIiVbo({zNdPoXU@o1vz6f2eI@Z(2@YcSSmLDOJW@f-_fZZkU zq+93}_VwR&C1cSJLTs{Vwh9@pq!0?9eQdUF0%Sy_NmSl*b=%WZX9es;MVAZ2fBbk& z3eEl#Rg8e5O!pBmJ_`NFd*2`5K9DJ_|u zX@sVZgZ0T|*viA5$qaf<>mKRi-<@czbNJG$a{FS9c{p(;z zgXb4wl(L;WuBd42Kf3h$_m}YX{UR!{`jSF?sYdVhg_&zDzihi`ws|m@2((%}V&q5W zJGm-!eQ*T(u9xaB=l|!oYbO*l=&HE&T^9S#JEMM7?u$<>{a=5g_8UI_VtKK9o>T+! zKX_gJu^nR?=>F&Gg_kS}69Qjft*ZY^au%2rKHM%uPVma7_pf9Emp5*#$^Um?hc%i7Q*( zQvD!c*3`9re63Yo+Gny+A|AtryQ0dgOtkCyRd9 z<<%>`wyjM4)YfwR_6{S^rUv;}Ua1kR9z$mq&5`3JiseXSAv0)XxuIZdFBBqU1|h*$ zr<(ux0L#$}EFpM-g5HjR%=*4N=%?E!9w|BcfGYRPZ+@eE#LL3RUhGwy`8{~S*|oHd z7*{p@yqjraO^@aSZ>n8dcz^23>Jm3}gwc?E>3QWlJ0IRv;~NYiWqh}q$QQU zD|&KoJ>^*f!wV0Zv6l+H+acH5uguJ=eeSPXPKSuk@1nK<*Qd7`bL1j zg1TI~cZdQDRbXI3sY@?>M@jUS1K+_^moi?L4xtE)+9}xx=9Jw)v9K>}0R|<|+lLA; zty%@9>H=wq1z1i7Rz&`gU`0i5p!QW(uRQPSFNJIH7XsE6u-4#50{(PH&oP?6CS8cP z^*-3ux#B`7r!csFKp*i=*;f>`97$=s9@dKLC*!CYGUgpvFa zlT0@qK@PnjG+53*>w7Xo7D~1JFc-|EIRubA8e(&a3`wulvtTB97h%r*=JKE)&HW$rlr_SE=guzugZ8wc0-+0Qt15-3+19GGX*~3EJNGceE zAP&M`w7ypHBx#U)`c;%{e~2YOr~%Au#i8w2tG{J;fYwBrfD_1Ty;15;j&R*{abfbq z`ALfKk|{S+V$z{w<&-SCetdntu;RdVPA^X|m0t24=;6`8pqZ@(cDA5^vfh80_r@f3 z^{uF8Wa_Vfp%$CuCu}H1y=zQWWi@RK;i_CZ5+c{JD)JG$tM{IWLz&cTLvc!(U%zlm z#fPV%xo>BEUOm>jy#KkS{fC!c?p`fu+Uqy(EG1^)S8b>dMG4xy&z3aAG=BWPDlq0> z_2w)XT1fky`)0Rit!$op>a0y! z;tUBWO_@4DM$e1tSM#+kwJGp34TRE=NGx~K%G4o4gDVDP;~PpNKr&&Vwr-zOW~*#m zW6wQ1WeERL`d_?l`a$_5N#T+nus7bm@6BVwqdab_TKewo9u8nF>_F zuoJLc0x$RbC*Ld=TrySvp#@`lU@!viCVFQ6gCR7!aRieK2;75z^DQ%Wbx*ulXHO+$ zTBlY*dljkj{~iZu=OFo#I`$Y64ft6$O1PYnMbstdRRlDO+@4eLx&bp%0EWRgZJcd$ zBh5hzi2dmmM3JdLHC~1}i-Q8y3TlB`Murk_oP%$_bm@2RD`_q%Wdr%fb0ehtor5&u zQftIB!gDQNJlBs`$dA6O?AKC)9An7Q%AbC8#t7-w`;9xhW3*l{EJ=K+_kwokke))L4&M64%PALn;5Tja*3S4%){7R*W=xHyp z{rVn7?srJq5gX^(4{wG5ACFkYed-jB_HPbAs$yygJiNeZTDdnKYHR9l*jVnruWiMvAA`wA-*-Ty(v8&d^UQV z(~B&s0wjy}6W#G%I{LjCBY|!@<;?vQ9MLi$bDp%?0-%gjDT9awM|z)W6^?xmi6&*| z6oPRmWIma4YNxP(Tquvr6?p?EVxMlg8gL>xrbenpi-S-m>?h)aT&S9sGoJ(};yrmd zkpVvnu)1w)t5<)KU~K_w4SpoxPiHhdkrt2Yh1Hn_*fsytP^K~~LI|d-S0{Rw4E&V9 zvufc9_o@#G1k)$nil8fxo~1)IUq2=1rhB(iF;`&J&awnYps^C$`o*7fE$8Sr3^b@A^eZ z+(eTvJt;Wf`E<>b$Um)rXb&>+87_p~ELeffgv78rJ1l4fBy)NNms2SA&cVpUC<5S% zPt>p7xg?FV72Do<0Yd^%0EzjB=3j7+NAk&9iaJy*@4l)Cl;u|xQ!R}*GJNp5s*N1E z6N2;3s828P8ivx;@?CIfl*8bSkT@YF)$lhIn{rYC&ZLDD=fG7ewi;lS%1BkZk4kw7LDU)uB|c&7^NB4 z%VG8w0gbpq%*O|wt5t@C@pta4-|~}tZ)n%V#{}^sw=VsEzEbN2xZJ;Q*}U{uZYcR! zqVBVg&fV$t5uo9OCBekSo%df;XF{?f!zwhSL9)bfjh=VP%!Ab1Khi&s@Y%=85e4`i z`^y)=_fwsFS6V)VM2o_^ude+y@4CFqkg~dQvNH19y<6wO=PE(U%f;xkD8^>?B*RqD zKrrANPcQxbchr(u0)?Hy5(be(;C488tlopGDE0eL;(ytBwXCDPAkgS?vitB zl{VfHg++mUg|}|0uQ*q%o~{pO;riOP`YzJ8y|n?Mwj{SYTpZ_!%cCz>fdsv!!qR)k zjA=$TLXcfN|L8J}gkR9TF~{ZS)$LsAZs+!HjnlFzqZcV4(SSIM02Jnwa;-wsfKuKi zfU1n5w{EEdO9T_w5OUWuvn47R0T&hl*Blac@&PYYjb{f5qV#`$du9)~i5@aPW-2VyX`0f~eMT+f-?+0D z7s4wJMAf;&0^o7oyQ9|Ke(Ba)OS+E9}PCT-1U>j}VP0Tn=Pu zodE61@hA{e(WI(;=`bqjNHN=4t;<9sVu2~jO&6DWddRN%pJ7yy5Hd~k){FZE@CJB2 zI=T<%z=8m%jTr#~)fx|j$qGdPKEUfC#5rP>-`;Oz=dC~o$%+5E`LAt+ z%!EW0`*Z7=N5Sh1&Qo9sc8wKj=8YkU%K=dupjK711YFizkS#x#DGUOj>=;$~nw86_ z&`S+#S!fG>LBjNMAVZe3BhF@1f67y`z$m;9&;wVIGySaCv^M1oV#~P(Osg0?4ezvX zJTuwv_B!BoGyW;S56QM^>#xC&2&^q&t-+src&#JLa{5f)SAkJG9%kTG^`5A9;1!0D z<$s@9T{uASsx&8pOyj1Hw<$=^UhK67`6`%tIxQ^c#S)p$_dZ+OBJi49I0tVoIKB%$ z06r?Fia_K956Ce^Hn-cDFVxJ5Qy8iTy@I4MzdjC=DbApAjJaT@Zb709O=d7(ezF!U z6f%p=++qr$Cj`w@4#*kA!W_EL<1f^EZ2;o}y+fj6j+o0ZYAo7iCP1R~SkE0Z6^v7W zSjbG@xoi$cvN9Fs@!%^S9IzTTN0gQlTs_)uf`y+G!yl5nQ3XxMg-R8d-2%mg| zg~)(}yx#>Z40R^%2_xde$7-vKG7M12K|5XqX4q!`1mbciz)*oEWrvBK@@2%-)}LPM2n=|6o|{QcK`Cv4 z@^GmgQ7%jPq1h+E0WC1(lM{Mpj>zH=Xh*dQWGYYrgE$F4%y{c3k!K_Vz^UJVpjOi! z>Fq6|9XD|i&b(@r-jAkSjIA_h&FU<-{P6Wl_dQpPzwwPCR@ih-;Q@85ios-RM14S5 zYRAe&^(Xq;E_Z0Qh2$H(Rp$>Ltc8i>z#IDpIdHUO<|}E)NRUA4iX2z zFa36}rvS}OxPXUUhuM5iG5*8%lqFf!lIJ64RT>e<`Z&F>Z(gQe*uS`!=HMWW#vd%2 zEN!i^WT7GhG$kP@0mE=50*+Z1%Gz#so4lk$7o>X%0sMD%F8%GBN|Ba*xnIzG^QKZK z1K-l`P!~h5HdZ_KEyj}gt3PWLa$GtmAhCz3AVJ%)uL{`9n66Tu3Y5~H)?X4tpr6BQ z5hi_lYrTCeztsc(-rEX)%f(eyZ?5$q$&#|JEhI{<4#p`r=|}JAol{wd%?i=_qQ8D) z1=(<_4?MfSr2YMO)P9q*PMZa)29sXgRLJd2f&z_E>bR!g-JpW3 zchOEPrOv0GS_3AUqU>)%v7I!gx?~|a`LLAB?fa@NSPsb^E%h_k7X6cZDu@4x-WZht z5*`C;i0BqKu#24U(U~ry-gQMS%aUw-yRU!cQveA^066=2{|SVA2ePC6%ZXLhD^`>- z2|hl?Syki&BTxi}9tJ?F1@H!hn>UvVo<=O1kTE;s9(=W0ucML10QVXUvUK3c4jvB%^79n|dT-ARN8jJ+~<~J(NBI%N05sn7vMeuw_ zA7ps-kjr{kkxOz!6^+7xl+A@M?jOJnyK^A5{Jo)HHV0pLqVxuzv&(?q62{=k8HM0u zMl1>B^2NTv2NHy(_Kvl)s!}^>ro;oLlna;2T8i&u^=kMCTuu$hR;ECZqbh@Qc!%R) z5Jc(f-exWWU>Tn17#MO!R6$c?AuRA3KP0*nBQ+UE|`Zhso1CApAVnBr}4 ziE*$8e)^hnDh8l*goT}P#96I82Ly>%N*S*!8R#`K5aFB8sb8{-`>d*v&T|8KMOXl1 zFvLOv?`na{ksK8+-@JKg!~6>@gsYOhR1$b=sZ)?TvW-|qN?%=O2@Iu-uG*-ZRkpMK z^diFolX*WzTs6^up^`ndVUZpa6&qL{YcZC}{%ftA96`MZBXe8dIc@i9WP}(89_VB; za$ty(n0>NeH%Mh>h=eOQ>b#Hk)Sb;KXvGBIbYOz3{L4&w_ z>1CP#fg9QdZocQLT7)7Q&86p+Nrr;s^HGA#yyTb#lp`-M{qOe_lKosm@WsbxjrAM- zi*Hnrqd(+cLxv__`9;rh!B=o!i?jTv?w8Jj^n9%*0?Dk%+4_9_?p1Cm^3OiKekaZ) zA2V#mLRBu=Mp_KklvE4@$W-h2t+u?{u6r_IjLUEJdt1z=6faZjb6{CLxxD_mz%cYi zIbzdl5AW)hI#lau=}y1@K!x=27iRB;Nv@vWTa+;paEfG)jO-bKjJkvfeBWUZDTRg^C1uYepSgyRUKVr?!cn7iy@GE!J?aw@EJbIX`FQ^73R!gDeg&XJ1{q-p9 zU)@$TbuT0fo%fTB4{0wAD(s#tVToT~e4>_+gboWDRn-vPd%2&*zT8bzqt_6%m#vps z!LS@22*tWnYLS<&_(%>Km#Sbqe+{8&O+YoZN7{R-J#=(!YPGOu{9?o?NV=-6KHlrRUZqkMxauchqJp8aK|zUT656)0a?|cO9rR zPFp&oe=?6EGz?%{qJ$U}%lxg+c&B9(YC_cB+#g5h+WkA1ZrNPBKa}CovkImA;-cPK z6@gR$G~9RZu)&>}z^X!~z>>tAWMpXIH})^OVo`<0!1Zi@2Cjyq7D^_Kam_i}uh}r` z;SL6T_OW^zZaSxWd*ma_tJBKC7^9p?py+!V+sA9QW*5{NuTs#^+Aj2f@no_{Z9tW= zHyeSKNnnY>IKJM$kHN=S?uY@~`x%`Bs11Gr^4rbuVm>R(Cn`Wc2|z|Kr-+(@D%Ls= z`9sJQqdc4f!RRV9geCwf0{Ph7z)wvY5KE$WDj6&Y%%u)5CWA6Ea}4<_F4L$rVH?}b z2Mn$uYQO@1P%Z$xp>M^iDJ++NGVogISAjQt_3A_cSrOhSM?9Jhs%!8U9@ZAH*5D0> z*EC?g4tO2Hf7m9SBn&eao{m!(_#fQnu#fXln$H%H?*R{!)hnPJ0#ox$H8P>k>>*Vv z!gQ7svvbgBg0e6N8Zf38kv#m(84QD+&B#|26)#ce!`U_8aJ)4oE(|S5F{Wl6IQhtgLIAoaU{qr zDMn^!05fnQ)*Lc~0kI^!W5#K24NT(>^^ew}iPGbpw7`_K%Zh90&3sy|$!k)G+b*lF z0$Djp8;so?I1Mq}k#NS;{YQ#`B0xfs^&j?~V*{kb;BUO8s+5y~lslK#V&Ypi*P#lY zzzD&fqzDA*KX^8mkoEP&mY=+`$}w_Ofv8CIq;uz;QFCaRPi>vyvn59X{g!j;bvbDs zufMsybWtrhTKstL1&CpI|NHl~CA~b6O(nNqRy{=ae(I@9&-6QYU0<$!q?f3$*M6cl z>DRwdue$unQ^kt_nas+hS&2Gc+`47fjuo`d2^mQj^CYW~g#6ow%FQI?Va)1Ax!jpH zsJQh#RUNov=!^v!`3z>*x&7;e3Xs|Qfn8WWdPh;Bmx+D6Kj@>CcOO{V*nhxB6CW8y z)mNUZ2z;!6RI#*oZ#;agGvrPPzq+kfh(NfU-*HK~MpS;SQdhiZM`6HUy|H@HW#?74 z`NW>WkboxJb`&=B))KnP{kiW6ktRr$gSA--!R4Uz*zww_lvh-^mhYPLj^xef*76x& z2>H&lwJ@hDtx#5-Qr$qJ_0R9F4P|Vg@x51{0tXM4$P*GrWu@Nwr$WdQkK-&We$nY9c;hF=cJGYiv#k*x4b{j#dEb{{}B zjc1%zL1yYZF0EHS$&rwAjYoK}*bLfp9E?5?r;RUg30iM|e66GR>_w{>yvoo@F*I z41kY7F;~u8!)0f8T8#CsT(1)WDq=|7Y(g z?JzVNY$4eU49>Uo2B^>E*3RwLwb|)LEc2?#dJB(X*(D(`EU8x-yVij>0m&s@(Wp#X z2%JIAkW5W&RDjqpC_!!gn@?#VMZk?ew&cV-USq7C)IB)Bhdnd_!5L-1wu;!qq)`Ba zF$G6im3ytB7FBySc5ub)`*zNp8dbm$2V73=Q4qi&#aS7Uzy+9L7#M{C2#^6)shvs& z<8mO+?(a?vm~u^Z4Hy#4VXnBK3R9MGIV~&ze27m(KyR8A46)1MMED;PtYXQDs#alH z5fFQ7jPH=F0+DP~$;j}7z}o87UlLebz*>Vh7?zK2uXV_qf~WQC<{j{wBzOR?_>fzG zIQ5!F`62dIg@co^0j#KKb?t$aPh>x&syuPcmIj=NUOKeCfA)Jkqyft1yw2uEF(y;M zK*SFD3Yqn8-#1%g3uUp-9Z??YAZu>d?B;f3wgkrP2ZSYL$NFWcNXT3;Rd4Ez^qKR) zj5!v-7442%1L_HD9=E|qK2yy^Lmx1SH+wcqPAz!lmDyU@;2IErBM{8ACu{2WSzHQE zv{%)9QHdpy1_(6wjRZ7ej)2B!$BQMFhs8M(FxHaC^_8^-BbYHJd2I3um>7`qJv-`E zrXi%-uBW_e0hup$iXuxocDz>igf|ip%TNB%&sL5!*yUG`_4igBkTAp9VDyT(aPAqE z{NvK-nc1V^EC_o!eqky7*qyUnI&avi`l+kyPvU$h>fUFoMJ`(3A(~q*2-l21xwlNb zF1aJGld8$kh8kvyNhJ8nt?%BTd&dVytfMG)t{s0zu_GE#Bkt8E?PKccI`6nqE z_dnXdT9}*8%cBgdOPs=x+uGOmx&nngQ zN=nIuzx@2lP6@2vQs@mYxY&t3G8uF2hSH+9pf~G%ywjolt|AB-ztrCUTy4eyglh|2 zn(uGCrP5<^^s(~jtlIIx$WLvpmv`QEc`Y9hQY_b(YCu%hhVmN9MiY0WACBrHVhy{&CX#wW}_u4~LOJK53iR*G7^*d8*cr(93CSC9X67s7QpBC2wRtw5#HS_ngzq(<3kU zDoGD(%ocX$DS(8oHUY`ipDtoz1)O9wX&u6!KTu=}$Y|jaPoI3T`Zk%Y3Z4zLOF{&M zWTII6&3;AHt)y8Axq0&}M~Mn@YM)E5UNM*uWYw4EHysSJeCa9n1M1Ms;?rUk4C(b6 zRIhMVC+S|qfHK?N(Vg@07mJZVwb58){fV)wFRTF}Tay5pSn|4e!W&9J0!!4sZA*<* zx^x9+elwnRdUag{Y!Cr`4>DgVLXg!fpo!J0%5oCe&|8qesi#ysiY?>0f8_;rCyFQS z=yAm!@YahvJjYPT;HoU zr*`}kd@5*Hn}~Jm6?>LZ{NKn?XcO9trD2?8TfhTV*kM{215Gy#ulX;^AUsaFJ#}5` zLO}j${p4kQ{@{TU-ZT1LSCpr?!ak}T&-9n$a|sj(TOWuK~81@;`CO*mgGtYXRXs^yNHB%I9Z6^P{AhHJ0}Cj)B>SZnY@ zhu1OW4LEt;lX-@n3=lHpOl7Y4xSC?Xpjp5NrpP>fSAo@QS5&l$cJo)WjpeX>S963o zXIe9bAf2b5Ey#I&f52&W=oL#*}5@=TS_pvgHAMb=&vNYz8kF1&~6{=6T~X z1vVrlj{R{h^K6hk1!`eZa3~L5S#rb-m|cA!XOwx&GXKb+@ycz03i zHXrWQ6SZW{^3vXolGPKJfMHlbf(djR^Z2LsR(5yBX_XjDO%XfDNgTMTkJYE^I`2}& zfcVagOU_;T){gok^X-?Foe6&2ui!sj8#m;fh?7V|YT4&rDi)&P$6C^^m+B6cSF?@r zYI&6I$Kd+tPcNcDC5-Sgb`rAoN4{qI50BK^E*n@fTZQSRgkO7KVgBx|v$c#0wN`t+ z)=l6Y#8Pp|c}o}fJH_73X&9%ZZ(sl6D{kA5c^x(0gC+$$L&c_TY?(Q8=Fn{Z& z+Qkz{NY!dnje?i~H*K!1H*#s_iN@c#xth3Kn14wfNG!FB?pIHN-j4>*6R2fryrb7` zzs}iXbz`byRazu2gK@7p|LhGlK0NSTWvx%`nZ0laj~JQgeQjHH{`5UTh$SOF^rmpv z_PWyzuq;HG(A64B=fI)oCSYWYA^)LWwGPE9m$&s^9?>W}dq~X3!rUy6zgUF;lh~jO z-P8N^3VL4~JQZ$pLa2iwUxPYVfO;4aF7dhRnHnoda1%i~u()I%Av(ad8|qSHDSYvX zrC;v1-3@*9K;?_AdP#Bd4fv*2@1; zf8FUKdZP_wBAijN?#JFItE_2$>dJ~I3CE5XJp+ojKOKb$6Kiywr6d(7(4fBtS!;8S} zmzN4;X?;kCOQIMiXfBVuTz~oa%y*a0J+s#M)J81O&y_#>Xx(Bk%m5hw&u^DBjwAwx zdY};bssP4-0h9c@d+jM^0K~E3q33G-D^-awz^f{jpbS@Vq0eP7!x*3{EF$d4v@oBU z2RVm5WK;a=FjWc`E=hBwyRIm0fgYSlsfW&K^W`U83z1naX8{o8cNM3wI@jx4y;=a{ zL++y!!Rlte4lvZGeb-HbM*hj$kf-nK zhVa(Q0`pgvz>qm@O=b8MSFgZw8-iJm2a)f{>nE8ctw;$ng8?CkN#lGORB14EY_hCoxU!9|Pd?4hHj;&YGH!`lQdfCjN+%&UL|tX?hY zH~{o9FPF}yfT5TFL<$HbXKdp zbAMGKK;|-=R*Iz)jA1`wG)a&Y;;FNYDDz;*F?)0T({Gl8F2Qk-0RLa!UJXq+d!$nE*;HbC%k60!T5GR0YHE}UMUo9@Dy~DT)!CK~MlK?r&f+LKKC;V@=M$*{e{I)Ii zOL?lmy8gNo8WM<7OTH>*FfZM}*(Ikab?dPlOG4`W>t860#?|HCd1-Bp`G@bRDvaOS zK3iVQ9+`l4H&XFh&%q+kRLfs_BQ~v^6*v7s83BRf|Is_jNMs_w1(Yn`aCWUGTzNq) z(|o=EVzKk3%HqU<*oSx5;Q!sea~Xf&Xazw8jvSx$e1^qB5m3H!TgglS3DHB!^4@;s zP(aMt(@(9Z3{>AWzuY$4^^=}M4j)~zApvnFL^+7|Mc~0?;$g7*|Ncp>P#0tOa8=v)SLbB#Xl{`#y%}aF5Jfa122Z|NVS#kF zKA(l!d2RKd9=P)C2F^Jc3CY8O^eF_i1zm%2;DgDfbJ{VNCAMco zF2g&>rZ6stI#0~APOn=ywmIjMrm+*vh|<);0Wew49+$?Yw7Bwema!cvj-+;hCNMl* zfEYQ21+s`u5LSfXPb7Z|WAzFw=US0{+nyQ+0xJ@{s?yg4D|+EZ_)~`0w|eC!ePg4s z27i8GZ2@Zy3VxJFgp*mlS{RW{H&+~vmk-=0^FVriu)5@F)hd*$8!Qo>){gQ5a(cM{ zk+!<20|rf4G8;P)@0h^^OiCP?L!{v8l}SuQWmi{3IipD-^2eNDIx%vDAms?3+B4Wg znPU(*Uv}P%S8qA@%-TSaNos1tm7iq^R~X>|vLRgAnHMnO8kFB#y_!#4!1ZLpBsSltal>mnNywn-F|rYt8<@^hEvD6hU$fpb#@M*+p#5 zEe?@WpJJIuH8LR)<1*zXe9WF90U_B7>K!%InNdE24^kM)Y zljV{e$q&3av3yBw4w50|T9vx@x5=V~*q^z!(s(o^(Dr`Q+oD7EsK2k z`Xa;Y63bgQ*M6l}dToMUuKn+K*PARDX@BYHOgolxxxcC|BNYc434yQQ{!gybAMmD2 zY6@xMCPCOSf#sYZJEfa!E)Qajx1>>jE1)>=nJvIr;Uet+U-sG2rs^>LbcT5%?b-sU}m9=}9{oFU`B0 zM;)B9_FxIj&XnO7BoOxGuLQ%E>B3YO7rjeAM)@z+GE&&M$ZojOAe0uBm zUiK;5dNt(N-dAF4QQ(wXt5RksUI#NohVjV2o)-MKhD-gl}jwh1X(_Xc}+N~yl zH}n_Hga}eC`h4YDrxG7kWn2GDJ3y~XVBB?}zF6dvcc9GcO%SpC(sO4YkN@gZ)qD5$ z7wbRwSof>`EyeU+9yC1u!XFllY!JEXsT=<63W) z?Gl7pp>K+UQ_AdYlKP$d>icdkKX7dsPXb;nqya3!qz@RWlv9<6edMjh8~FgSGLO*E z^*?^G7{@}%FWyosBZelf)vA-&3i8wC<4;_<^zEIs9pD|8)JNsmcDVOnD3F}^qdaoF zxYB#GZ1M2!+AGKX5bFcKe8 zXJ>y7tZ%D5hs$i%5>7Ii8tbn9I1~a->4dd{-#1r5-xugT3o0h_=T2mB#Hn$E&9NwzI~L{2Eqk~3bk6(C`Ym;18M6xo9F%%kfl)uL=@s6 z%_Y{K@qIRH?qPEvEDk{!j8l`Qn5+NS2O6(ViOT<+|N+=o=2ulhe zNh3@oWJ;H5M3APZWA!I62*8lz95XWO3Vjr%Hs@fR2Qru(W`|M&>|kmGkTe3Ap;{s5 z5Cos9kWG;!n|@98#_>UrBgTNR4=*ySmZCeaJEf8z7?VY_)kp{#jEU)(cbc9ei0aRD z+3XDv3lkNlt!9mq5cCd6cTETaL8cZEPPK!ylrv|gMHypUz!Ga+qLhM~zRNHx2)$DC z+<{W`?;oD!KfiTv?PrmAfBZW|AQcxMfPMebDkNQr&ASfNUZ=eI0#AZ7sMovZf7M5P zpm){H7(3yC59Ree$xfguuUKDSHj&|k9k4+%h5*?m=az&3HzNV?t8JWVB*(dYm)9Mi z{q4ONd&NiL*_Ub&>Wh!p1{_@)S46R2t+O0BrUI$m<1bd{ha_R$sg;6?M!E!mX8>Xv zanT+{W%K&VvVJ?;A3C*`r2XN;Mf^*>QR0bCBXOZj{G&9|KDD)WQpm4{WJ-};etw6# zvq#CT*k*HqV9LzP@opU&D`D$=+u5Zm5CWp$$;^(PG|>JdRTVSRf1Y^pd)+df3IRvN zKYyl<3Nchg2(KyH*LzWQo`{W1o$A&tGs<(%n5_|fxIc=I1@Q6ob+z6afYTX7Mg(r! zTt5!T+SB?+HWMqiRZIeBpI)NM<>%{7MoL>`5!^L@VHUjg;##ZX3$oJNdrR-xF>5cL zYOgqm=CSU9yyL7CQEaIwce-`pFYGl1#^6J7C3L{9p17R7LVXufI*m^5DZ2Dr4~BfC4=^A^b_l-TAHufwIDoTVao8h_!I z!Xscz4C5?Ee*b|gGyAdL^n@~?iVe(TgEJsgkjwZNYHgyvBsK3ai5Ql2Hj#g-Ki7o} zOK!k~NQ>iw)IDzO5BYC>zJ!p;hTH-KqAAO}?-7Vj;bV8!N*S6B`fBf;L;zH2pnJBL zC_|so>74#uQ9~cS3Jww&a87~iYhBN<{NS!ymOb1*=>W)y1R51YGD;l+ zXg(u=BA^zg1Jf+M*r8pFr7XYlg6fkP(W4>lPoAm|RDJrdQPqrSj85j2S8L*{)niCQRK_It} zdt8gq>;6g=mK^A#2n_+kYF_+nvFI%Q?e>Gr}OV=;pxt?;cwExf@EX zK)q#iAtPm~R#KB`s7h)BRORAZds;Tb^W0P8t3Zp!Q_yTes z5P>O7xkh;bpUbOyVRVWIgECbvrVy_&*Q%P71mgmTa?mmFdTWUX1osi!7|uS zBxe=*CM7TTUZDXeZ_J>aYR`EU38Pl}Vaf#-G%n|bSxytot^<`PX95wViAxG&R^hxb z*U6Oevfl7A=$!}6SD&hxD`Xz_qiSFRnIsWz<~p0rAFP)b_V;T$0R>#f1Ll(owV3{)k4Vo*+|6$FO{yahbBSKQbdEQ|AynGjh!2BnhF+B?6Iu<&}zCv^b+D zN6K_31Nv2k%!Y@kbP#2Xq=ER0=8I3%Yc)|FrOIwAv;S>-W=#wrvHbcMDt9WI`y~;8 z3nVPg;y??e+UQO0A=^13f$7(dkSQY|+}V+@u@H+FONF8nc!zS*iJ!i1>4{FgNC-_F z#I7Ju{`St9E_&r%2tL!_Oi?iZM!9`oZS;^z{nowpDHcLdCf}1MP)(F%GAnWD)hd|Q zqm;Ih1GE$#q@S-CdDk zp6IC}NAd=v@cwISQK`IZ8`R3%-@K__Zc=bbyU1YX6fzjca1+Kk_FO;TF~@*3jm-0! zb}BR=*%X-=0UC13vZY!m`9{BM45Jo}i!AYE^(t>v(H<7WrK=?v1WxT&mIT(NDjLOP zOYZ~$F@QAeyAM>}n~+KahCrf}<>xnVu3G7F64z{u`ks*MdzVcw80lshg|Y1JgzzW& z)jtPO?vZt;)ZZS`^vE9GU2XIq-&j96SKpWE?G=f<LvB@VBWMpTZKKq47 zz*7d0ux;5UOpHXN$`gHZ>Aar^#i6jcFLF(c~GyNflq9t(rM z2>P~inUOZb7hTRNM4B#i=x9s3eChwVwOmdowJPB zuP|2TPy5_xEnT=sU8P-W0(Tr0OR3fGcHx( z&83LNn8#nNo76mkK$i}+B!E!vp0fmW1NSvk-SQWlS@hzG086?PvChL71j-SZJAJ^= z65^<+d!!o|96UN(wecWJ?Two%AYjWpAQ2LX5N8HBPvyHcvO|guFZa4CU}|DM%K4L6 zfk7FVmO*#}W99BdFlsei z!u~p7K?U=s)`C@RSlzr6tzLoE8DA4D#PhYnLc+2JYw%iOZ2{{qJG_Cz*&7Jck?~|k zNd$kIj_iDC_V@e+cvTw~d|7yMkMfFg#^X&Pj0@5h9tI~8VivOS(Z?y}p<^{shFP#} zLD%&1#knyHPv5|la{4WKIbQ5lYbt44dD^Mpqns8@^2VjGC!G$MIxM_ls%olH;tCd! zF*TTR<}VntH^;^E_Xq|kkI9NcF2$2C;zs6}_k?VgI>_A7Ufi# zcS;^esMUc68K#aG$pMkS|G5gIS-x|eayCs2GSVEe` zW@pXGWP@#n%WZo~6jK3=)TJiY^S6OWK%W#MGS$Yf@A=!wm~!v{8bRg03Jk*~OSz(d zDpB;F>}G{`cBoCeV>Fk=7$3p6ws%zL-T~7!S<0^dDa@z$&I|wuVtKZHD9G^t!y}c4 zKYy@-0N5`l-*dPEP7xI}5>7ID22@hf*=EV<4I{(?mkwgZYT(1A;2auwIU%uVGSRQ9 zUM&(sK05B`1*TLX<~zNBoN9=sIs3HAAz4BrfQg;NN>DL#g!W_o_LF|%hEhQAjowv6 z4zb`PZ!HM|=%7?EUp!Q6F0_~Zhw29tj~=hAk~DYgOGiqPvf60G_`&Onck}wng=#ax zd}ZOCmsW;mAlmwT?PEfwQ7%2N(jM~Jlbs9Se7ep=As{m?PD1tzL{wsqEf@yy^x6%z z=%XAKPL0o3g@BgwZr)xWxUNUVUJ6v}3@OjguA;y3mbxP#vzH2}O_bE;T}2=!bRnyr zaCs$QMCotO?w={DrI~(x?0@{(ViE8}9>c&ZBef`TvGBNIr$0572&mG6829wejs+P!3C_R% zh4#CBXPZL``sALXgmgzB6Xd+#t@gJ07soX{MT9G#dTQNG=&3LOX7eEJoN|_^r_s}1 z5c9p>zw3x0hI#D88fO&5BoAeA{cGLivyZM1cF zF_QDU_xEeU-4omn(eBXOuk0Vbq++Zo6Y|imy3Cjmr622G+|!N%_{si?pUcOMTK(F! z5W+i#Nq7_LyqTVxE=IXg*WXf-v3zG|ePz!e14-1TbJ~%E)yjn2(7&6B zQaSSL7Xx0X9%~GL$EEeMo)IWno4;O)VZLetrdY4r1>=jt@sebF03gF+osU*Aa;KK-X6=SJ> z2)X+}{oH{jaML9+;;ds6?lS5?TySYz{oVVjz#dgPl-QxYN zO0vTF^KS*xu&uU#G|ZbPb6fOss%V1OHLkEly)-m zS21I4^=b`%Kv-MAT7w@&m=1Z$3*WTbccfnHI7xWbaeO(!tFkPA_N)$rc!6cPgjK{2 zyh6&(uWH5ufpS)_PQ*A?krrxKyo?Zs$7&`nnY9G(Y$5{2bi|};RAm;MOH<5zRiF;Q?Nlq27AQ_XIjG2p^Zvy;L*! ztYd!Z$(qBdGL5dS6Z1tqz|Fy)2`Ee^yU{+XIU6*LaRL=x`NQ)Wpk(QhR z(m73#s$BT~dGMw^# zo?VPNNm0(v6iH6jpI)!MphUx84u0jSszUR7_s=W@Ss;^35RO>KV3MA|2J#*M_Mu)| z>`ff}CF-d?MR{xQ&w?SxnH(}%g}^!T86c?xE)f$VLE6*_EpZ%oW*Eu&3KbSrm1gwH z5j;l-#3<9gy*FwR1(Q$!WNM=~ViOai)`t0R3wQ;11&26jT-VAG#Z3~fxv-M7l6D)%NKdjmsJOAn-ckG2>;bj%(l-EC2hO&=^49x z-dPY_?m1kwB*ZXC^vYRWxU^rLBNl-(k2aoNn-Q{W?6ylw=?xbx{mXkR+kf+>%FR+G zt*x*2r2i*6eXm87gK)hLD9{C;<)kbSI{+hL^ooRqC7LgtKg$r;Y^XN}0etrOVE=IM zs#D;_SDz~7@CS}o(wipEVoFhJ=%^l|5zq2|s0vZLAaMF}BbJel&QVOS{+Yk=$8=bO0061VF?nkD2W64rF*G5UX{~}S|+p}NOj$D{i_HD4HyDl zA{Jqy+(GoBW?_anal=K9GNnusZLVQN+1iTUrK&^~S!BB1h#kD((~s5`B-+hk(D3ep zvu1BcBCB73H9qv(UoC^W*UqX%b_Ss2I17pW`|qd$&@%-AjFF_a%67!Wgs5t%wgH#+ z(nY_%t(I?KRK;eD!~*oX<^Cg8)`At!9^^fpnTqC|)2qWX@7VErz#*W8zY5OUB8H{; z4);&2lCW|8()All7mZYG?=RC2)d*aewvj-xA^q|GHdYbz-bQAJi~;%ss1DXXGUMMr zJfnd@7RuZL8JFDYFfNZDFBSjvoAtLVW~+iEDvb*hlN7e~%do7?;$UK~-{x0Pkdf@1 zcq*XT+l%#SS)ipf1Y)Y=mj->de}XUo<4Cv^07{o6xC~dh+4nvnf}}4K!Opa0pW^yU=)VU zIWtUH2870W2%Nsfydq4^TL5@BT869@VL2}h2vYg{Grza;qNNsKMZF6$4P0)kXZO$k zgf?K1ttce0O4SsI(!?vmLNfKL5UZls&7O(+$-_w+d@`^Q#*>8C$F&B3E?{i|YYl#M zVGKPB1MbPMUV(+dYx!`td~|!&@s5z`<6*hXUJ%R7buKeVm&3v7#AsbVO@kk> zVkm~p0m`{d!NGv+*qTOlxE9A9X7G^aAD8!EiQ&z+$TzpXVPk2@^x82sHDsCjARr+$ zrn3afnksaiaDtrGVnLAk9MxfQU?&h%p|kNlhii+0s%Q_$D8dy`{*5~;u_sVD6Z9eK z_Wn_=GORa244TU5da;0H462fj)@4JKMjFWMkQ|sPjXZ>8qE1^^S+_#6k%IY)JkbuB zGaI(;t?$MD{4Hg9rJa}8MERlJ#e{%`bcJ3a`AN>CDKHy2vl%f;DiktH&Y!zcy5uHI z+7U7x2C?qPWuj*3Viyh^t(UowxqNzGebbjpj1C|X!k*lT3S!9-+x+^wP;7m^8Xd3H z$fb#_GGgr27QOcLot>48DNy>8#X(oO0hKp(;<1L#jTVwVw-P$Q% zr;Y%x|KqQw`S6=6k%q)BZG(?HoW1|K%3!m%ap(Tor;*4}8=JAXWZ0+r1L45sEK1#- zzVUP|?&S4A7!5}StB}!n!I||1Ygzq8{fgYa!xfyDj?^YZ0?XJ_>KjQLyKmsSqkpar z(Dlv!>K2%7?dh;rUpiQ8WZYNai1Tfi)n~#HXxYy`T5rg{bxVC8M^)U37Zf4WSY>=0 zoZ9uL7qPUQx<7cJRF5P`382$c}w|(zL6`4Fc zv&s_*F13!1Erx(2l+vVjQ?EWVEFSSXI|Rjl&^W`017tkPC%Rb05(F~Xr}x&5A))|e zed~Ju+EKj58!w1r7_$jX^SNsj1SGl;S4ihW8Dta{Z+|t+*-de@@5eOpx3-s4@9mf) zPHVE0aJAd_FMaK)rB_a=k3bm@PwhVo(XxGCjT?yFM7d{yE}bPjya-egMF`AY+l%Im zQ%fn;kX$f$rg7cfWAmVK23Okg+t1F%84*xPLdpzq=|aums+*&5iFOfb=+}z{eS~A-mKlSC=@Lu?4h44^7phTus3C#1wd-m=_=qZ?l-bx->$h9Z zse7|t_io#YtYa0Kk&j-%Ik8nHTny91F7w8{12}6Aw(0Z8Ea^X z&_IRn+0K+0s7;k~Y{+Hs&Wu-h{Ds-BqDTXf({r&jr9ueUpfLWX=n*s3H6Xf1)pIY+ zhQtNPD%gs=Fm6?k$f;qfM%mc`69R5dVF($Q3G6B`B~E!k2;WE5fM6V7c)UiN0YQ%P zSiPFUa%odov1+Vx21wxk>T;%#`OJx6m8#dYdIhGUPG77O>XfM;Cq=L(h=8@Tge0920`TLhXWK3vxUu^C-&m_+(%~{)!M9!lzGSg(zCE zVO%PQegQe9Uk-T)4c_dr4=iV#V$6WApKn>8LNrb-AE4^s(V7+y7>~Nl*i7lPg}L1@ zdpr(f;eyzO%W#=>1Ub@%0i>ywuE$68AwWNs^LzK#vp>QeLyL2n(HUtlE(621$aH>U z&ujt#=_rEEB~+Q^%VW)eL1rgm1m^C_05OPpcr$AULiGHBDoCpT&4VRG?Qj!sUa|uf z#!j}!G7}Z#xX7jQPhDB72_zGOK6Yketsv7&yKLKAn^2ORWah2brAV6Do5XKgzjX7( zvuS;0CY@ZVy>>%gG8Gq%M1AJbdI=?4sL-H{3YTc41nW;PqyvU8Z@Z)j2+0|ZgKA03 z=X#NdSonaHL!--!f=o|vj#DH6F(Cndh(Zt0?>k(*`ft3Y)m>PBJJpCU_nUgiu$Y=t zk%p-Bro&qrFR!04`|saZEnewae~3pKOn&X&>bH9Y$GPI)RwWHa0Hg%UjFe7GSs7nO zHyJH*s_26J++(%%2G`csf~vfmFGi4%pIU<%M;cxBOT~lF)tUnDvK z{oLC}@0fK+n0#tVfjaW}NLS1T{36+K@pCuy2iN=dtVs4U)BYpXlzR`=)`b_TrjvKp64W-k*vpLa$z z776JOkZdD11T_>~Uh)4gZ=b~quOOU#S}|@uXVxg8r1Po0wfpJqm(=RjCFj9xj6blzQF)za-NSD?j1w8TjQD!zFtaanuy`yd`T=(|x zz_JHT^_;%t0sBe*2e%g^V?O&>Z6{&dA3s)`BH1JOgvZ(nW5+Pwk>D&)kG)vi=pq~o z%d0o`gA7a7#dzTiW2mn#)Rz&0i$EBm$m0VZ2p7OkCU?vSbc4d3Qa3Nd+|IiX6l3Y? zA6ivQyS`oBwY9Un#T0vmcRmeMhIgjRp^eZd_;EHCf)E>I#HtMLE{7PIIbn2xw6!V+ z8{&d?R12d@Z%o;d5yoXK_uz|`neh4OF)R0DR~cxmGH*U1i9iry5ui61_s%CCsUG6e zZ0f0AR2_g3M99FKRMhHK_a#V!lFqC!w8!9>>WCQzlWDCsVne!X2m;FC3bAfB<(gIv za2c)zFrr>NS+N@CEkJAtOtl22<-l?w12bK{I!RbAZ3+h4>F&1WZfb>v)_GMpdCCP? zG01NqtSEdm%^Ix18whI)SZnZ?6c)zWv3?QoV4f1JI4H09B!CHWMV1r6AfC$fRGSJJ zVy8&-z|%Sp>`av$LryMJ7;vIVz?3VKr(9-_ePB#hmP>#ft5?cXE%)rGDc!i7vMVgu zGo6bBGPxWy!+`MS*^g_?DHr6A^WchkIk@R;_6Awg@ySx!utZgME8vw9jL1X|7?}V4 zp8D=}w4}-zBTDv-m&iiw9AApyh0ZOziW%mY7x#ox>g$Zgnd%!h%|12(@S5*_=(S@0xADO03MnOTzrC$uUyl{^u%XN?MYcdd|{OxgR?n zSYA3(ED8=PA_{xnsz6^~;hj&i)k+C2JFjMPXRe#6gAcgfg!k+HNga7b{Ed(6PF?!9 z56)~RMAe@>RRzEdT8IKH1ib$FTgn*th;mkzTBOyKLHxRV`_c#J@97X29|O3O!IHqR z^=H)gvjrCM=Jh29Fa32S zNRkg8o4t!cjv!zR-sNxHSv^Dn@msglctyU;t=+YTzr?q|s<3IiAZ1pMu0eE$0=Y0N2fxigWbt~X~ z|24JN^9#3>sU*0C@oI<|`SxCZC2DLwa_JV{%bs4g15V_)+|cWQQ2~<{bl~F1uYaNB zXHAN5;PM~*SS9?3|M1~@jgf`Kxh(DTvXqj{vrj9r+`qIxW>1mqI`$xo%gXvco52PB z)?Su-3@Noq@Rd5W>__^I#Sgt{>7RVF+($;j8GW-;lsL%$^(*q75kuytR=}8$iitB` zs^hX+EK>)KJTJd8lOrGzuAt|O+pqNZztR(M{qFrsfB)?*r0?;2cT{k)&_g?!SDs(r zw|eaPTJ^yGe7_c#9VO&2fRNiRUHazsQV|SM^{uG(g4u+??~`d?RO>9Hz%#FQweNZ5IHN}TCT1X(!8RG}_@Cm@D54bfx+CRIgpTiJ-`=v!- z^w9qihyY?YQDmmuYo`02D|IZ{*jui)@2@usSwbKq9mp=~-@C+u47g`?W{w*xj65}# zX3?r!C-)03LIf~w!efWgj=7xyn(Ej+PCIOe(aU{ zxZdvm3l02%qt$zvhl}3m($bEXrKM=f`ARdcqExfXhJ%khx@}M;oML`@VX2HHHn!R7Iw9tPRbmHEfh) z`5S9<1WqRbt3$|gV4O}b08GBWJ7gAe{x`P)!-Xl*gaD*!+D5^sDWIH*Y0mkK3y^H6 z3S|I+%h(y4b-Zi@fRFa%B$+6O#*tcL*_NJ}S^fb2vMQlKKoNqpmjao16{ID*`!~^0 zvY|rSt2RmP5EGL2ijk7K%g?WzVg2c~|4aUi2Bj)-koCn=l39CyuUBeu=?>6KoN95svb6Ls?wMKT66JfYuGK5VcRe#RJ8o7J^b(j@vFhpz%79|&enNgW zu(+f>WVGD9qiE2x8++1P^x4Fh=Ywba?l9Zd1ur*5rGx_EJyYNhxGf-Uh%BRbOdNQ`}DGwwC_G&o5#S8~z^pZvtU7-4Q=lOz$F)qve-H!~A z@WY3a@DE%!OXRNWwFAb)7np<{*A?eiV_mbM)_KwA^)X!$flvbmeC*Etoc~kBUd_~N zE(FFx9{G==bd2|2U1Af2SjgI#M6yBSpM0|xRhddcb&R<^faHXTM)@;FHmIdYnJ6~@ z{LOX#?4xDmXCJH49!tKJ9(6~Kmpf?EcB~>W-Nxm8J7*GgGq+aXZBX_ zioh6u+4=QxAINuIUhI%@CqFzFAcGcWG2hbdhM5DtSNv_&Z>$BJ)9So}`S*q>?$S=ko>;C#%Y8`D$ zD}C-j_p5`o7Fl-o($%Hs^#;toCG{mN3P3zLj?CqcT~U;@AWPFAa~P$sH(YcXFyYM8 zW=qvDw_IEWdV%iEaoVXMQ*G{r5V$OS-*;M;g=d^L+a_rwaXD+&j$z$Ft`c46b*=Z7(qp*k#XnRUXjm2Re&Yw{Pn@XwyK(G{?sOi7M@nWosYmd zA~xp`J3Gi`9(7`ys#;Q<*AkvWs*s^)PYS_g;)Er;fwI;K;4_R6LIttA57c|0hE2pU zfvk@(^~lS$7B;lRhEV2%0}+)!nSl5RVkBLW8OqR%;0zFoTn?Ima_mgdf>|H{w8W%Q z4t`4cCr{Px-PHko%+47kj0=#eL8Fjur>jGV2Ml)S5yqLoG|;@6f?R+Ymf8>umKEVd za(>j{4PU(ie=%0C%!ki-EuX$wgOh=^1*{(kj9g|vYw)^Z4Bvq_WQ@)O{tbc1@$eWk z12BZ=x%C6;TK;5u-7=SR!FV>OFlxtKA+X?G01{a!PecGY)i`w|-$q%OJq&^=m$GBZ z3l=WHz(^!a?!yIQq{&&PM^nHOvqKyS&gg@=|GD}}`#@%#AqXIovDY7g_tj)t)xeMP zn3H|!iTb-b$Q&`EGJ!V5j2T0(LMH1WV0@;zK$Qc;qtH8)<4)Lv42?+-jHlz~-mZmA zN>g) zwtCo8D<>)5{u_0arYx@rfdjyklk6zGXrczP9Ph;!WN?v_zgLq6DODqc5l@+k0jEp^HSbsW)_FJ}8ZdRY;X*|7p>HRw^ z_tu4|tE`~cXaEaIAW+pEd4aLtRAQbp^Pb#ODVcmGrmB?yb6@7Em0hCzjXO&qBsm14 zOgp-)H=E9^r2(la;rTi~X;L5)L3#U!-c+s~?tNb56OwMgytrH@b>EQBE0Z51RY{XR zaBb}oLrLj(I~z|Zp55QX-5cN0ukxj7h$Vr||LQwMh9+;B$xFePo-8v6LG$1Icoq2d z$nK@fJ6B;IMen+z@P?F^`u7k4PsQZH*TzLEL^y7ebpk47i5+Z4Ox+7@JD*1 zTw3b3`PbYMXR6W2>y93;6#A0>T}v}^Tp$si-dREeMXL_$Tc-4q=3ckifzUV)Kzy54TDW}j#o9OYlULOMYn-9+tkX*o%Io@5>-3TaqfywL zvf8h0E6I8wOWY|&Jk6oj)9QU!*OttUXIE$5vA^yVotD>kJyS!(p<_$md3Nb<-CUCY z|Lpx~v|iVFA9!A3CO`rp0RkWi&LAmqkVI0HWZ9BzNueEQsCbZ6PO7VG=tRnw?yO#` zvr1q3t5WIiRcrNHy-Ko^RLY%nEX%QDCypb@8cfY1MN%BZ2_gXy5I{`AJP-YQ_Wyl1 zkKTK60dUad`>cb5eV+Zyd!K#wv+q9Vg}i2YnX1eemOs8@>Tlg%KFWmkOadX}dl9q& zjSE`pmu@I8j2PmkfQ&uWA=@h6FW*$J^k(%+@VWej@7M0ygt7;I=YinWDQMu!i&7_SO+vo@OwKvn5O#%!8mZM>e+oAeN`*VUz{VMOR zqY-dIZfI+-LKjh=exfn9x0M&puZ_vX2AF{d3j(0P(0$Ls#pmukmxf1SK}7uC*&9nUhrHFpP0*X0}_ zAHb+`rab{kz?BiC`vF|y&liL6g7%Z6F(w5Ps0gMUY(9n&^l<}H6}!J6y)gz?j`NsB;jacU#z&U0EHt^cH(*$|}L~kIBS#`=0%HYqo?&OZ}@B(c1c$y9< zyZm7}LSoM>t5?XXxyMb=&l50|rrceCWMQ&~(8Qggkl;tq*oULbE{Wx1k5d9y(XAjy}Cg7agI~d6-jQF zPHU+=hb{y{cHYO&tAmRYGGiDoDz?1#OdYbMFd;-aN6LXtqmT!4W&_Tdq6O6E={{ZN zxghkB2D+BcqL9n0+ozQKB}RHPBk2>wK<5qQGAudF?SQnzBvMW;XCR~lQHXULl%PwS z5F5yk>oFtpLz7EK{8AuaZ6rHJ$i@iNstQ4-zlToL#5#;%SO91g(4RS5S$B}p-YNUU zF4<$Y_Vmd;6@Y(`RX`fTNUNj3l^b)?&pu9DW0I$B~_qPKo|sZcK4+R{nUw6xF}S-k+*S|s1~P)KCrOGz8&hwE3ANk!9k zk(4{_V>b_Os*K`_B~^}g!vFnKwYLam7e#wSC4+_Jl*EHy(RlDgW#b$qL_)Z$XbB%f2`5*7AvUE`yCZ>vB&7ay&*8v1?Z7E;b+~iP{ z2smH4q~eB#B9{)&0qGZL|Lhb0o>aCLiy(PwzlY^pINY@{sHQW#j+h>0;q26$Da>3oiqn*jel}j$Cf;5DuB{0fS57 z!*>@!VyGeh@`cK-mD5GxNqAsGtzN-UckPPNG6xBO3PNaSWVV!kduw5gBgNW>xD42WsQY$L=p;so=H)6)u#tNUEju ztJ~YG%a_zXyT@Oy0z04GJ<9F%Y9`&fdgK=jXRB%2@>7~1czRv(ZtMP%z<_K}ijly% zAAnx94{xi*kq_KltEFiRqW;gnPy}r~2h78Wo`vib1hvoaDec7O<5s}=zq_|2FdHRlJQcl)?|5|0Dp)>ndFo8n z>^L-P{cGDxl-i5-(LREY8{T!J9ALsq6X;{5Kd%QTGwU_YxWP~&kaGzyGQb31ml1dn|OBP{VYB93dkR5-5P+-C2j)DrcuO8tBj2hTkdOCVUS8 z=q&=~wGP%VC!h-nAPXie4yv~wEKHW?Jm&iA$qI2FxKuS=3w)4n(pvv&-|w4%P7_cT z=fFhO5a2~ymqU|FuzQ>uyn6@}nCS%Pf{E3uR{=v^v&7^L z1y464$?OorvU>Oi!5ioPg|A-C!5ai~3s`gTef}2vR<{U zwv(XL>-yif0UJnj?(*SNb<3&}l__tR@9dxjUab@n@T;wuHFW#iGRi-CswiD5%M;{y2-qpD4X;C$bKdqs3=dc$?@F=<`JbL0{fRX( zkx~4`^Yu!ap71wsE4Y}WsX>kVYS4{oAHx~;d*+w|u zrP(qxmLLY`m7m*FOKkazw9uhZ`^jWMxx^ZQLEy7Z% zcU@QaFEXGx0)~WDCrgzXmD|g3+X(gQ&ZR@=&o92Q@Zq+fj>g!d z03$MFvKB$UuWz8%vQeaUmMo}^VCfQ&j)QHt1>qoGJF6uv3y!~d{nVd)ua<#0B^wtX z7GB=QK((8aWkt|nnn*qNo2^_zFr zMxKw|KU#PqoJMEOar#Vg(ZY=M5Uqer2mVfr13XF{_4d_e8~VG}Og*!^-j&uy84}^e zkW!-DL2;jUjLgV6cN~HDwOdjUvS~}%29 zw^h{Eg~;cZP*k^HGGfdU9xbby1b#7+Z6R22N%e>H6Offz_#$X4MAs~@QHWsmmAwj+6Oh$dysH>ON@GrFw$#-Jf*%eac7e|_~%z#A%j4t}yQ zw}3SVf9>F0BO1IBwm;hfS!ExxoNE-$Q(V=F)Aa|Lb3u5sDQqYzg`s5S;pMp?&*{E; z1tx@;Nen+h6)>;G*yl8TPSAP#>J@;T?zO3Z2UR)x zs118~omDxbPZv^7Zg6GG0P)Ig7?5`l-_*VYT!y`cFSL(>g)vT@JU!yg#+fATAKXx1 zM$d{BAV*HSe#I;AQ`9dT?i*!FSuw{&Z!{`1RjzpL@=6*LT)i=^z{q^%giL236@2qm zt)<$^0^5jHIM_Dy!Pi27P`5YTm@No03cL=@^vQ>&{>i(GAgxdV%y)m<&kJ6@sHCxh zRhP`GDrGY9O6DOMs2pCDs;G!z&Nr?cnRN0@ZKF_20`CMWdx+4W*RrGCE^VungcFNP zwOLvC=GM`YP<7tj-=?^ZA22bv+7apl#ImV59Yn6Xv`-l&haRHRLl2Ga7&T?=4 z!Oa^p0_ktOgNZN7BIDLBGmg~ii4I0IBi$+b7=sa%ver@AgYm$zdS#NE{C)Qgb=JgJ zwwLnwHR{hA^u*|WA4cLjtv9DXw->~cHY;$aV%yf2RZ&L*Q88QMdfoEs8@H~mJ0g7m z{m0*1loYY>FP^VU7mO#9F=!}djF^A$mRj*a33JtDqsDrASJD5*o6Gp5C>Rlx@V0EE z`)PvoI+&e@>Kj{I4~*i2gSE^MhkV&*$HCF#^7;0mEA|@@gec7oVs^AR%Euqp>J^uh zbVU)3fBH<_Q;)Sb-!jOXoFf79+O6suV z6r4Z>_Gl5iZOMPr>UwuiMU;yII zCX`(g&Im_x5n2`(o z;Na-5IIkVfrD6`|;MKw00@fV-l)}WI{NsTO(HNTPPmw&lftkvA<@9~a7xKWHpqaQl zFMHPDhCo7pkb~h|B+yl)5c(l#onxm)8<#+zyMVAH4-a?*c1rN27b~?u*7f$Pt|*up z@yce6*`S_F=d4}<6VtpN)p>zSbEb~$RSKCr5gzuwQWKi|cp~7-zOl8#aG}f`OJUif zQ6?6Gt_7r=Jh?j}O(hmFaCT&3=ZMU3nJ|T=>&iKhLguVmA=!d58huPFgm`EtEdZG? znrfRCN}(f1twP8E7J&Zw_NEmxa6w{}o6!154BxjkqnTP zsaMq6Yh*gd5i-MGbJmvm)c9O2bsOnRAJPcX|7&(YrIha9u4p7IEuBjK6AGCB~98Wq2&;D>yK@(gciVKqz;}j za+_L)Yv~-BSQu8dF?IY?ZZh*_3#;d3%}724D;BGCJpCOKe?-IP;y*1e!&ytBhAkGnX_gddNX+WzYtM<>TIpQBpT#U@!amx za@(rPY9L8S3n?JOwd+VN`<^~iW|M%1Yv4n7mQpknF|w*Qw6E+h9ko6rc3KEj%OjLB zRhMZp`ME4~gS_|JTGueM&)%r)l>=zVaR~26?=M}6E%zU|W$MVOS_?=o4JE@L-d3wf zgeVgt9sP%IZ$`FZtNylaEX{WyS!m#EoQy>s@isR$C}Xyj9tTNL_!1~ zyAj~g?Ndiilxs<4Ytz%ZS3Viz=1qv$ATkQOj})fDF&HkG%_J;@qhY|8H%-0!x~V^Z zu0|g8KR8fg`71d`EY9x4m<_@OU=RzkI|3P`<1gDTIk0~D=&sQvoW;ZlaNWwH;du3; z_60W*bg+EmNh@byhs@i2*^8t=zQ%G%6mQse$5kbS4eVLcUM^Emr@gp_1?W*P?kjrF z&HNB;T89A$b{wj+W7Lb5)Xp^rnQoIiuNtky8Sw<1kTeAO3NW z-#ppB-*#nsNa?rmX*=x5y1+0(!jyUAeO_rb0nc7DEFemWawx+CLX0zkNCRa?8}k8V z@L_adbk^A}{n5X40tVM#FOH6uxq&lLOfpz?`C8ee1;jSz2yqs=yVsT%Ozrp%A&w2e zS+Dc>2%I@vV@_v5%uNtcZbs3#zI}ThWEf{~q;)Qka;#06RYY}Da|E(1Je z0$Jo5VusK$YAM<>{)SPPQ{^_!%m`*v2EFNGhtTDK8Eu$=Ax+AjcRSi2afdLxoGx=Z zW(XHz#w;+)>eXwM{L=t4eqLq{epX;^0c#F^O5t@M8DGU{D+S6EaXQQ*YWNI0FU>eX8JIsMcqqBq zJ?;C6I!l1FQm^S7k3nWykq|DEi4>g84%PAkK~}FOJPKvl)pkn)F3C^El1Xh>chC6U z5LR`TF2}=f^ZuHh0~nKFRof4aE(60B$&?)gi5)$R8L)F?hBzuRbyki&=KYyH2cr=B zormkirL6}lgM~y1iN;(!VL=9>8a~2PWy**}Au*J24(Vy>>A~kV);dc*1;r6uK&Lu< zV)R~yW+fRQer|VtKS=iV=#C1Dlr>2OQAbbJ#1RdaFSNZ3HM?#5O3J%7Bai(Ia7O8z z{R<*MP7jOn5ac!>xs9rXOMx8pJY|mtRZAw&Ev4V`3bopoK!w!GNL^-AfUrRNFI=pR zUcwj8AB9dC8GKf+&a_t%L&=_SRi@gSMi3CgRJj?MGvd!YRr#K*gAfU%1C263aQULC zeaGu(3YA%iUV&H!$a5qZd~~Oy=X)9vJEvj^q-6EGJL)bYS@tC_-97#hG_{DqTi4Z~ z$Nqo&c&$EAfw(%uw!+imQhyT-1%u4HudhD9&2Mk3{zDX?j$UU#GD`H{OMcrvdBbK% zG-umd3Dt22lC`qRvb^Q?zuG>iblr;j{PvMkWi1*kQeV0>dK!+uk%Q~89i>8W-_* zcX!JBZmhcl8JpFTjJ>uUHCVo+GVr+U#&pXtm_WzJ?w|UfK34@ovLdN_IKwb$g)VeU z1=eMDSWuz68ek1sS-nzb2H>6BkfLl4p{|FECsORV3+IpCbtR3`rHksb)_FTmpJY6U&B6|eWy1rbs;^RUStQ` zhw$m5h~=MY(I%XcfB8ZQH;gcnYF<=O+By^H}?2QU^wi z*P%6;SD`yr8(TEJ?EM=rl_+HON@H8qBCWbXYjLJ#SXPLIB)n^V^?&Z5C> zSZ=k|87`>~N%*4qbx-LhzjAZi?A(^;5VSTafoKc@birgSWg7w-`*VRh+95ZOKbj5% zGk``X1Uy9(BQIT84~y$p*88>wF5{JEqJyaF3GHti+T~B4uD5X)w&$;InLnA7z;+fB zWHC63G=YBNQo!NeM~np3Q8iX$B$g%YkElA1O|4y4O+XssoncT$O9~F4P17_ksSuES zSWxHJ833`h&O2V-3Z&t1F;Bszf(U%+#j+~3Aqzn+Q*dAerw7X)EKC(}PIH5&cTK%> zeaYckdSd{l+-wGb5C{6|6(HYpBoPhNoBQ$vW&&YA3iE+4dojtU358f;VE2?Qky1!-vWQM!)3_2&A{YRx#pTp2!U#Wa$xu* zgZ#n^m8IOWYBW0!8By0PuX3`KsO}O#CsQjp5+|5k#$}f#)?eFF_DESX*@a9M+cN2= z(Nw!EY^j2LTTa+_K*SzL|Ld#6w0nQcdCcYsiBg~p=|}|BMpuwYNFv0O{=o6tL6cwZ z3r&4-CVz*Q60|KO0A3FEuf9{$Q+WX|LX1rLyDt}wJH6Vp?T1p}jwDc5iFu!TXq23Q z=mI1Vh(IAr1}L2w2`}U2k#wsf*s;mUSeIpS4VmQc75Q;$*MhaqW*-N_D$T_P}9o~StZCQ0cpQ*U4{Cqn=9c5^dg9}JW9aP*VcelRptvf zjh6qHU#PdS?zn0cPk6*J+u>Ht`&nwSQ+?!A3HeN0jS_)7*NlE?@qt^)7)le6lF(KV zvh(HlADjBkJ4P)fP{`hNRlUwxLAat)g_Eb-dx(20fI4UG=4*Wqy&+T&mRH9lH0;U# zJ|pUxvt@i4)4JTh|6Y}!-CgMue)J5JBMA#*9Qt{FIYcR~>J2^1O$jtg6aAv@5?VE9W5!DD_ z2>csu4;p~v^}!Rh=B}BZ+*!m_@RwD>h2<}vD^VL-v~vT-$Ak-#B^8a<>(uJoH?@}v zVKgqM$We9_OY7S}a})cYpDTkuc7KU_W_OKjB;Vi0K$yfC=-3^DE5APg2+UIBW9=9H z-r0UA^Ed7o{h~!;l1`{l2DBh>Us+elE~k>vRxup#4m5akJ7BqEO(Chy-*q&fW@T5k zSJpRM9F!qr*a+W7o?3rt3q8sigoROeLMhaQz(f?ddu_RymDvC;yeJ9LZ+Erdg50#P z9wji<9IekdqjV{ZSve|bKYOZ{invp_wp8tA$rd>*gsXy4_KzrDvq!CHyKw}{rJ=y( z?W^naSe`QZP$S8@>*v}a|J!%gDi76E=y(d`&;^hY9y=Y6Y_E1v2wb{gROQSVQ35W* z^@#_k-ah`FVtrm;^#oxs!_e^h%C=YQfBAZQWjptBYiQ;H1<XXcz z^LsM=5@>zkGQv9^1XDq49#OQTeY>#Zb55K|)`%!?Yd>Ez6lKWDBuy>$4v{bcFf2^f zuvV)@LXaoV)SmQ^1)14h9;v|mq!R;_hSnDc4L2RSJ&PAqpEN2C*}L(4>A>-Dg+L)J zov{M~Fdg3^LLwn#ky($4fo-$EkU)9pejq~90Xi76h8~>@W;bA#s@WkvUKIOHvQ zH@q?~6QPtrm#H8Hy$EnozKh{N>neLE1_)f0? z6e_cA<#0J9gqiMu5L5~=PC^upw-1_RGL0)!&#(h$T+UROAf9O1;A1;R`2hG2PuHK& z5nwb;R0vBJyvo8c<9%cUUo|VV;3wmlL zOSq)g5}4#09MPuAC}SJI8x`wU6pgZ4G982?i|^dIer3(GC3uwwj{ks9rb8{>E&E3A zssNYWcm$Fb@a*o|76B@%DdKN!ErKXwKRIRU(WyWF-qZ(g9*Ip$WsA+zyGKa$C>?;a zfJs2wrb_;QroCXL?BFZ|D){Xu|D;Bgvj3y)lSyFv!AjMXiIRm1s;mzUU5q?-x^|1O z3Zozh1v#jhvG-`L8b}C_oTzmHNLn6fAGbzRZPZq82_avVOZI?nJbRGQ#by{iHm<>N zIc4Ya?vmPml8^4`7+dxik}(mYu$Sbv`Gq6$c6w<{Snh3Ki0{&#+fzR7IZ`>B4ikVk zX-GA}=(uh}C0g%Z- zJ(Jw7NIE(DbBW&B@ps=;E%LoxRZCi4Iov&7I5Ym# zyQ^)Pwc^lNDu9<%@X_Dz_Q}q#Ys-CYedbIVyzkYKGrE~1gM9EGKxs(zf zybjn+-hiPwwfS935@|AdDm6CS1vZg)hn zq{T0(hRb52GL2cTz32r7m-T#7dbETHHZ~--HE{7Gkqbm-qV9aET{r?k2-BGDbRo zr~MdZ>~SVJ4_TC?RfD#$d!TR=jhV(1J8S)2Rre_6YRYSCRW!fHM{n;rdhD&Miw41^ z?b&3`u&eEx8yVJmnsVNu#zH1!R)Lkb?yosUjSp?5l_hU#BlES(M}HoOkQBnJw;w%O zrqXNN_~Hw%Y-n1vpw@*0dY85{B4LRH5Fr5tVX2PYY7d{N?y-4a{S}R!gsV!B)ne>r z?tmHzTQnvB<-h;lD9DbERPJU3Gqj4FV1iTzVPOf4`6<27XYh|qkq4n48l$Sa1~UEY z5Z+!3+`ql7mZ%{;I5YkWD?EaEZGn6LvC+r8KxB4M4rP!NwSit0k8%WhVaAVG4WUcU zx&P%G3?1w+ef4S<+g?YAmK-x`JQut!mRyv^Ir!@da|>8=@G9YFWpIg|eKeeSE?zA3 zF+JdUW7^ao!@cTUgYS&^3C!~Lxyo}%7$TKtq5aAA&kpRf-cm*H=rq>aIl8{vOl)xo+Q)jgRfr5#$d3L-DFEBxjWCD|@ zfE!m@?OG{VQ(wZ)KC(K-*#f082EZ$Kr$Q++iC#41{EZFu z`Wv!*n8Y~WUfju=k;JKX#I)?r6c%OZNhY80IF;QxjE6Mwy=!d=Of6#&^OQg|FdRoF zR+Wynmlk9HryeS##0J28cH`9DYf6{qke0|*^DU}`kOrJ92yLf899)sKvu);bP7p#IEk)cCT2;Zb z1h2S>GaKSps;V`caCkJbf+t_D)eU*wRm-Nfw$-Z;94q-**-1a4 zG<&Hyi#S_RM82S{I3U1<06Z}Mu|<~>PQ7l!s_N(LVF0A`d)MLW66qvvMj17{Vzz zo;_{f3cbpDg|aGK*dav;J!Dlif!zi4X;uUx|MB;ZY#0e?qX)fD@0$8=-&VxNF0Q!^ z-?DaU)BYkb+GOKKsc4IyST^pdhpbCMV*|-}(WK8vxs3^wk^o6jEljk2^4oXT>zset zUQ)iPeeD1A*;?$Acx%cpv@NU5!<)r-N=c4tEL7|BF7>zn`77n4TCGzyUe5IcJx`o?-5+wr?W5b#hKyBjZg2i(Qpy;9x!J=7SOuw!E5b<# zFIY6cBKd*#2u6SssLf@{IU)_D;32a2NcDj&`$zI4wo_uKqMw6Ia|B>gtDvtc;Eh!< z{0%`EgA9EKm|i=CA<$N~-?|B=lyQLQ(28=SLfxMV>g<7t=XhU&I;WG{fB zFJQ{=&^@Bv*iBMDiV(^ z`)V48AXS-uAqHXTKw8Y`Q?!tUK9tUCWjOU-&v;fB#!lb}CV_Q~ChL1gnNP?f(?Otl zb}*?5pvM^cR7KsI%W7_FZq$*T3{dJI(_GoaI%3f{%OW%ia4Bg~-rp`&G0%J&S8A_X zHnn7aiG>k21oG9?29o(maY4e1IFw*_Tl;{IQiX^DCh=yg>sQnlk=gK1-d#vWqRE$o znDJMe>JQgs!aE?P){pKeBTdcaz&EuQXXIDe*hR~-MJ1MsWCe~J+Us4JyJw45zR5+Y zRzO;&Uef+x-kN~GgbwUJGI~d*+pi#=aUuBc?JA5M<4vn8z4`PLwJd^?0SYmG?>Hv` zB!Be&iUXQId3r?59`f0f&MD`tr16saaX0157gYx~cSjQmLP4zD=BAY;xw=#PQN`?k z(IR-AQId1;#MIX|*Bc5!5@IT^Oo&?kVkZsnzkjNNz@G9nOI>Z@!$GWed7yoI?C^=o zehFv5=wmuE0y&>V{EIdq`4G=d8EBKi5>#uR)&?b22?(zAJ(e2=zxd*)KahzLd$xB)I|mabXO{3ZD)JT{JWuhm zkdF$HBc%6VTX&PT%dJrcj~4-zf9d*aVr`-J&)aK#k8Lk$3T~)BdTP|QP%1DrTUV5b ziUA6%msT%z`Aav{4;p&L5iV~}tAVF>R)@KzeRU3CAz@YfZnq%_BmAEBeXGxItUqaB zB%!)=8%5u9L){BZ`plEnuQ-_I#{b}pQ@?d*U1}*}-?Fx(8LY4i1%e3AS+C0{u@<*q z?1H@cih9GewMGGqJFCoVn>DFcW{f!e+O1Q6_Dn?v2sPW@vA!Mzn2(;S&x8SzLr<52 z8$qn<&ok|>8p<$wxNgP^Y5C#><*!9?kuHTeLbeHT6g1%|x1yVW&>F)?8AmUB>a34_7crsD(4C$G`jl z;!8SVoEkyne9O8~0~w!8R|;2@f7+Ixck^s`g9wjWGD(?4ks zqJ>8E^bm6PYz?s0!`rjW{o73fDZ^KbH%9oB6?pW%8|&s;vYCT0}qe_`1N4 zP0<|8!E~5gz?y@fQkZ3A>_hx4kF@i`xp-$0s4863X=dk|5%N2FU`9(`oojiG@xmEH zX|CK^GKUyqKrhcCd;*V>s99zt@Wa|(z-a1x!}wQhf_LtJB|`v|Q`LnGFVTe9JnzWK zQ8`taJeljn7fs;o$X`Ljn0Z_9`QS^h0x@8D`${!BgoL#`?|WI`Nc z9FS7tU$}1SuU?#b)0+D8eg`x;QVxAE5$K#bbnw9h`qxYtgMbZB@2ZRlff;}DC}p#s zE*~Xn&Mbt#uPtyyf&@m^nheUN_$+DPFOTGCcg7f{*b&3zP^$_CBh^^6X7XO;L_@(j zd6aBKs(km_%9mnHzWW1xS)FJp;4{xq1HxLz)n<(`4OQ zLgF$VURp~?S<=`k5-Uh7v02V`W&V<{CtOl{8D!?F@Uk<>9zX!FXRiULD(!4LHL6zT zEbQa=E_=}89lXwY;CRtE3fW%Si=6ze3RVy+J+7`3)kP_0usKx)_iP{3AD&|qAph_6i^niT$kK9$4XU^7Q9Q%K8pcW`O zNZ_yER`13FcAh#@3pZL^472=1VC)yG>)V=9-FoiXb_1#f^-=9s-g&rQy_C!c&m`9J zlt(wTNGfM2CRvrC?Iv}&HBd7Ck?plKrYt^e#-i$2`;Zo~9xiN1 z!~O4%R>Za*tbLJ$lc^&`+O8u-Oac{mtf?0UDNWpXlqGToV9~?>_U&aAE(E`IXDuj* zfBDkJvHDTS@c{KFPrtGfC-_Eqej(U-s6KB;TD)__FWT9kJ*qI=d7$kc1-iHR>tp$B znSs}>O(qL_fjE|gzHDe!RD9yWYLxq0Y^#A90Z<$5+VLAN6%F>nj6cw+ak1CANd|c7 z`lTCMWZFXknI^cIv+XrE#0Z|;Suu<~`GDPZD=Kt^8xoa?#eU-z^{0;f0qn@BdZUm@ zEF=~n_`$*IS_FE&5MCRsmP|dkslF0*sJ)>alPL8lyf+_nJXY(H1kzELM{9i3{gAm7 zfpCQpCNwFig^XSg`VbSP$_?ng)-#o1(4^dXfpa8yIs~cq+V=M?nA)JAXj1&D1#4I$FD{OA`#hY))pgAe^sEC{mG1iB3!QVwxacv_1MY>8tu9?DN4XZUrOzal4; zHT7?bCVb3gFj^o5^inSxqc;$;G}BOZ%8t@97M{5>kQ>DE2oISv%G=v}1m@Wb+n-g# z_{7V#9DoIZ^U_7tVbn$lNW_A1&eXhjyfnekDGBrPCAG5Vk`1_IS$Ts)*&N;N6Wj1A z>~9~Rj{OH(dW^_)R%CDXptm{XKZ!`M3IR=LKUx-A!9IWd!l>K)%C~i z_(d86ID&=2pbOlzuUIM(Z9l0lk-MVh*f5e+wahM=ZO;@?Mcsn=HQi@zh%t%}$q5(Y z`B|6{oVG}TKvkL&5C{*+hVP8OoJ}l{>wa}h5ht*C;loQ2O6R0o5<*Hrmc~AG=jiPx zK^9EO4_yw5uDQHgN)=w0PwlL`9vR^TN{Oj(6hYf0QXy*10od@q8%MPXV{G6fGB$j5 zbETc40h3OsAS}*0C)>!Zm7QZXFp?INswd=C!M3Fcu`~JK-&>oBRxPddsZe&q@_lX~ zdXUV%byXR#uf>1+!DBUM)sX#yrGsGubCOtYh%LE;O zz>l_FVDKo@s~ueC`-Uqk72k7Y>i>98?FV5hInTFsXDo!!#ae?8?bU4>0Y_dwRCzMt zCANK|{efEw5703CJKM|V)ou9(#K<#eram}c2qT#>4A#W?%2i*YB8Tyd#;@}vdbM!b zgeZ@K-@X$sv0t{R7_sygLBocm&4jo#z#6Mlg%lE8Yy2O#;Poq}-gnceQS>ghi9ot5 zvB3lM$`7>PzI5+gw{j%G&BX;}y#-v_ep&TTzE@=8OaiCe9ek|M#9B6U1gOVdvog&X#!P7Ghw_h3qa%BpIBwN=#@*WArNe8 ze|XUUpWjm>7>yiskgv0?Y=iLLBW0Cn<6;Rp?s2yRz3%kSKHt_HTO2%qDx}x8*TdWT zat#`7VVWG;vt?CTEjI-LG5qc$<4tyLl{w8?*0zo(KKl7LUK)Af%#6TSHrEdwrtJhj zi-tTR;r{jYO)iwmIu)fv8SaSs;tS<_8u2&osGe|BTgO+>L+HigS-@+$Fp>}HxPJV_ zOay8PS3Z5FwlFiy;85G~+TeN9%34P|ajJcouLTW0W^2$ygD9;ayvl(6u@(dqGNx)= z4aZ=O!YS~rt>q30=yMAL(a8M{VkZeyhteg(oVl4rI%OJ}O#(^~?as$e)fs_?WCVT|bF&)l@59kIn3Iwyr44ju>7SQo71YVtrS8Ww|El$nBUpJUr zz?y@f8948_Hame@Px={YW<=&o0q0d37!JFc4a^Rw&yH3=I$$S6_H`~8TB4l!M_^*m zH$56|P6or;=}(A`QRuUr>0}^tvJl8)CuknuQJ*+LFnso-$t(wEcpegSP82b@?uYIKL4gkRn zX=0%>jN}2prz*>x%Dvqafv^Man4RODKX3Gw(}dYl&}D7)3Pj_z%NxG^P1{5YnDlSl zTZy{ays{(mSyhF$OaQ4Ci-bpVLQd%#r>sd=K#9$snzqo8u={0 zpO2s`^IGP4e7vuUNqM)0@k9mCOGI7Xf6de{-~7rKzDQWQur@Eew7)I=GB5!FBz>~fl5%@4@as!c%ss_B!1Ft3H-fRS8Dpf_NjQomqsDs#F-i@d8FgX zoat3g((Z7%Y*B6b(Jpkk)RP|?Zy=IDE?-iw6Ke7Q-{a*Pg6g;J{Y_2+QE0^Y#v@*nbLEnDgO8okz1^FKPZm9sbX22Rb^-!!!slCu z92C_WX`7w0-}GTaLAsDL`4|!Cb$vDbZ0m121_HI|rM&*eOI2I_s@*Knh~=_H#m)=2 zq$;|wBO`%d70*UV4x~Zj_SLmw$UI@8L(2kHFYYZ1s|BZODK;N&CyjQsgv2CehZaek zl|dGKVe&UKNR%|X1IYnjXdlIk+3^TXqOBD`gIb3~kTX?)Iyw<7Ha2ixj-vvW2gYs2U~F(c^$!DvD`V990X zc42q!wb7VMlgLEC872V%%Gk|ZT@I0*GBSa}unJ@@7=Yxm`6vh@NYavXSOO6RqcBwJ zi2KetV6ao9-9wz2?ufFzOc81QmC00PrtVzZRhpcTrEF>|QGsEw1LL*JM=YlAJqL(K z`bK8?YNIsMZWv^cRLE(QZK`4+`mb!RR}2tHKc3xFWW>re$dsbwsJwAJr)LbtVPZp? zLm3TrlIsnfmW-9^mcGUewf>cP<2P9ZO=MyxWF$l^h=p4Z6hV&o&J2Y|+HbA{mqD6L zlYk|*NfM$6v2r2|;|7ETC^5=5Imo#V!@G(_}rtQ6=NbyDf1I7Xz|l z5;MO3Qe~Bh@ge|u^W`&76$^HhT4Y`OH8G)}S^B9D<^L8u2z(+W;ceED|4P25sk@;wQ1rq2aUwUzLcPn4n-r6I| zrF8Ch-cqXd{GErZK;ZQ&D>2R|mZQSCBGYUJ$Y{J~{IiU?gA!7P0mPqrs8(vS)^KxM zYj}8D#Yt4jL6$v703NcsecPyol6)9SPn{Vlz4r1^kR|_bSyu!u6J$Xn4NW$hDP*Zg z89^z^-E?f~8(Zq-+y^&L{nj0$&H$K-xFRziH<*n@L6ZFB&e4ik@L4j`3G)`Jj)B*4 zQ(K}{kTz=qG<9RPwGwgWrdl~CUq~SBEo_ZI#?z2GK)6O= zl@8`E({LZTt77%+?t1A`T`f`^tJe$#=YMS-U{%wI2G@t$uMm9V!IJNYwdq>8R82-s_YYJD}{ zJ-Dg72ylv6W~e2N%iq~vk|V9~Kdc{2SUkTlU*9tIKipfb<1j$w-8$f$X8P2_#X^V! zjWdXF?OVt;d;ylcqVWgSfnl~DtXjRAQtT!0p?aJ0Y};!V3@(l3oWEoc=*A(OBN^PV zBXc~xyEau_)m|DU77I&|IfJ@MH;lGH1+r;N=xE2a?ZAlg6}@ehFmgbU%Hg7d8NAqa zc(go`zv`*FY*B5jHX7Gm&|-p7fn@B?0Q-s4wHg%yfF|)llONKBAwL9}rZQCz2_~2G zS|OMEHXxr381sFG)atJKb_KAtCKUprpHPuM=EX-4Q=O58H=qlVklG=*oWKkLJ;`6* zRQ7b}^r_9ILRPN;OjopU@SBez4n6>{V@%A1Lp&4`WXd}Z*56|;!0$u(at?S3&3plR| zld~a>@{BC!wYD=3$vRga$&O(9>Qyd7GXWu%>Gj!0hkj{-GGSP9k|3QY=0iPW$-JaP zC&Lnariq~A70mTBr2`>Q&SfCwE^Sx^7A>eLg<5mXFSZ}LLeS*|mM^Kf(ZTjZ?|@!# z;CRiCGcC?}=ogG~tOb&axB%&hNBJ7-&@hL~E(&`J!6yMAYTxnEQwI_1lV_5NGxZ|s$}wVfk0lI-AAa^sYjFPIARiU0ur^hrcPRIXeiajU#y*(eA} z!+^GsIg>!tEvt$bms%3&ieb{fWY=gCG*!G483hO8aIs$jrP>N?%Ta}Bw2S9hEgv(3 zRX|!A0xG_;xmLCGE{>DNYtqX1d=&g_WqL{!v>tO8yBdT(S@_VOi?S_m z(M5~o^Sw6~F&am$8MS8Ra^xce^De2cIG3H*O#Q1zYE1-j`I5ah$LbEv4O@)q+pWRg zw0g8VN%YuEnyUFyvKD_HCGC&2?9(ZYmyBLx;o1YY)JNi;*;Stj|7Q;tF&24XmNw{O zQY>*MQ1|#>zFx0H2?(gR(Er_5G~2D<}Pm z4H(@}M^DuYfY@V(yj{X3fxqYK!aTFP&eyLfUw9F9Jknii$+QISZgHaqA&a>(U{y z*Ht3(#eHSuP0fCau)~W+HcVBa-LSO4Fd!WRjAV{ki2Blt^)bYX@RhY#oUkDHV0Mp= zA0INSorlYK-RyH4%T(aR04yNtv}Rm@|IAZ`;WaKa8&(w=c4hs4#rW&j2nce=X$6++ z7zh|f6d^}W6jG=4aHQ_|>00=^w+(9^npx9PF0rj61z>JnUGae=P27Z$G#(L>#`Vy5 z$y8Nh0wX4W@m$@r3XmFF-1wy`ZB)Lru~+Z`Q5B+Aj$bfpDScaeqmuy%ASC#`ho^q= zhAL2Pgk(>M3Bl{A0RFIk0u`@b^y&5lL7@NWzzQJ56tNiwC6hoD#X(4>;)QgpdORT( zlUIHNq&<%ivmu4sTI+O(9Oj|Cbydx963jT#^*YCeqHMQ(2s*foVSoXi6w4MBWdi6C zkR_YAK$_9At58Teh2f%A?#k45NL6f0p`QaOP?107hBCyUlTC+-Dwi`7L)NC+vB0SA z#5o6gSs|-eU}E*EPuK!oPUH(_`aodl=5!cV4Og!&7HBaJWMlr14Q6-kwZnzT?@xg@ z6y{d1ekNdU0c#FkD|mzt2b9+`xLoY`_(l(8uU-Cycr-!daoFb>feSQaf^0(UuETY# zoJP~1yPbGKPQUDi4JD^AWKrAa4S~ZaM|pKXRoAOLqy)Jf8s>>bU@n8#*%*Y!aOLS!bNEHd6|i)$I#SflNcuM2;z!bIsS2JGHqBy)K8syG$v1 zcPA3e8n3zh6{`@)?mRMGb}k@Qh-v_j-t0M64ee2ut~(<(v!n=8>XNgBp;L0d2ThdB z(Q!GL-9>eRT!KEiCm9zvgLJTmQUz6rk!dVQ^L3WUiy-qw7*#ox6AF%yh8?|t+;{EN zqdypx({Lv449m=3A@2bxkl=jyL={TcEtSBBi;>IQ2bD<11yJ%@Y81<<1Ln<1nSvq5 zbewI%MVfRayo3{oCT|0sJX4ulAzK9AvA$RmH+e-Qt4+YktS=3lyyKEzb*!qd z7tx~IuyEvTTdFAU+TX)jC?69mWC+MKjaHRtpa;tLHO%*R)xIabEAteYJm+k?1rTV9 z`7NU+jlqBfx@a$TO&VQzlfgPazo+m(V3S2xpjs*(jZ7i|mOu$Pd~7@0_Nv5MXMp|a zsj0WEt6q^@?z=n661DGKU(uGIBbJI&0Z*^X$R~4s+Yr)#LKmILm(usr*@7)UEyl0 zHe8SveU8^{zYjcP7^YSkCOt>QurFFr7z7w&9z_7=)S0Sci833ux7GR6XA6@S+`Ob1>hn|0b8z{C>+Im8W8B8Wj zTCHHb57CoxLtCswrWRSe07copRU~daa_B|h+FDEVs+dhUO644+#ZLW)kA@BgXbTv4 z=AlOxvoY!wWE!S^@%q~L1f!XboUHyFdmMp<^J-hxy34Bd)f$ejTT!NBcZMWWccthL zo~WU?7U1^QP;l?nH3;NQM!qD-qVXqB*GHpR#b&%vn)M&QqpZENeID`9iTWAD+_~?% zzDC_>QMGx0v%ftujCC*VuibErui7?H!0Qsuc@I&+tx9R!CkHP);UK-&$e?SXzjk>I zS%hG@vi(M9Ep+BrdK*0nB0yU8u{Ne53&60!8K6lS4RzHDZCLxo8%iB^qx#=_b0cU! zm6(wnGM_4QvvWb;2v@a8`!U~qVJ93oM_1|1o3q!}F$!-Za75Z;J4SEzEnZN& zw61S|=0S!%LYx6JJRFsSyy=Qs{JN=0bIH7rbx%QvX8|%#oc)MoB${%7uzaP?aGgXBuKj4bn&Xb(XQ=<^tq`3q`iFXHj*XJhBijqFq1Rnr$UfVK$wxi5ADa3X-zj#Ja+G)p%xoULgUkdcl2GBP$eup}n3*>7$w_S(MJRwW#!`!Lw!rUsIO!Qe78{=m&uW&XFn?FRug-grfY z2n}NAE(d!GX=RM1cf6xYTB6`GG^+6ao9|Bj#+yrQXhI+bl?~=2J8xN6`H$SSZiI0@ z4B}cqL*R{@WCBU11X9po9K*Yb3i#|9j3~Up$65`+RGsG&4~`rJkjO$n5&D-8*G_?m zfEV*`Z!0cV$q@E7+caa+azRrFC4tWP2m~a9ILNR-LXiUYYR#6K5*Q4D9q(Wji8%v} zC08^8R6N^n5&S>CT8!wkElWYxDj}p%n)Ube0h#zR5 z&xij=`)m{8Y4opc8ULndv*f0gRnU>HUs1~?X*CD?E3jjmFJ0JZ+Rxs(i6Emw(kw zHZJ{D+y03i@poEl4wtjBtNp+&wbJz9=DIa{t>c~R>*Yr@*`UM#4m$tAR ziP({owZ2R#E&>@(&fROvDtA@}3P?f(wp6z@1u=0JlSD?DeCiZpTR~fI(x4&Gso<|$ zSug2mIE*BJbyMZx@4miPNpvR>&XQSxOsWgn{$>CaVuLDp@H}#-@B)p_BPObDT{YS_^zE&6 z<4||+)%BtG^rEkBt~(3680O1p@D-=lMk01~TLaqYIo0kXO@H`giNaF7v3*78y*JjE zEAMGB(I|pbG0e{+w7z$yeHZ`4=~|-QdY~Q_C)zf;w_V+InJ*ozvT(V-5^#9q{}pYN zi;%>Pims)QSJvj!4edEnv1%)iqUWZ&_VPlqgq_i%tiUR_Mt)JmG+~JB{ep1fP z!5a;83s`gTMuE9b=AGw-^A2hkG62mY_G$&QTwyXwz|k5hgaTSvD%f)UYd?rbi}#qyi*ckiD!}Tvd#G zY)2&z#Hum@m0-ZHTs%q%qMbCe)d)OzyprLVp(?dKo3SyJ>VNoc9#~)%R;Y?@rwA-$)^VC6bP=9Dra;IS+oy|_6Q!66FAk(Vw!Ie77)++yIV}S2 zZ13QZoz}nSIYn0He0)>SH@+&NZdmowGM@$k-1Vz@iw>T5^Z@xSFQv1?HwYsFX z>}g9Xc&qcDttA*3#I%33y{pKoFTGgb20C)G-dAO_%+}Df^7S{-&HT} zftL@Js1v70s~!j###CiVzK-G9O&Udz27&Z>;0zP1G!PXe8|cIqhL9Uq)`D^Z=xhXV zgU@fD?Z@?d57*+_S2kBy1iBOU$9If^xwri;!IL|u?paqMVnz&8<$%$qvB4!FZ@an} zAKhN#Gh#NRU0A3fGJB6y^WD0t_8IA8OBdE#qDU!4Ak*f)KKXxpPdQkUTd!7IZP#uD z0ny*?UOQR_`rYsEk485y^(U67KL25k{lRpFUHL zFdz+q_u3Wp6i8zMGUHCaM6CSM{@PHcfYL!72>^yM6^+KlUowH)Etl7=tY!N5+*p^< zTkNePVG*w1zH`)JVB(A3xM^SQf8+uraKGpzix<@26(s*ccMC=#YaxcaB8wAyeUJA~=NXNPW&Za@6H z>2x?39t3|bf^%_Q((aAaPN$iWX7Cpo6Q0Xjdkx`S@+V*x`!57E%|wHO*CKEZ=3oNm z7O>{vje?6^d3lZSMvsgc&7JeSyr{u_f^s_VjF+Yq`Bue+K$kgP)P=NzhYiZ@ZHIIB zwBx74(1l^4<1>pIk^$xCcGolxq^i%}KY4!xx34Nn<&nm=dL?aknV`{? zoLmaT=Xf2lYB{3ph*-5z!4j9s&LG))s@Pv*TULXe{J-2d+M(uv5v~I#s&#TH(_Yog z;1>*myz6bG3(0?ev%U30GECybK{S;fC+sa5Sa*4KV3Y_%RRHr*UU9}%8FE{!p~V?j z$!X2~^sd@cBmy67s<9x}a}rxhC4v9)kt(MhnTql+o-YZX-&hMhS$O@0>*}4Og0`Xo zLlhV2BCwFHrS4i&RlwQhJJ*j^SA+cSj?t4le#MfSowsVJGS!>*JW=}VUI>Jf79yg!cQ=3ilac&yZ9K0)yZS4kzgfw@oeQf(H znPkp@v}(ai>TXiNB?!NMTP*~zYVWbyca|3UjXPR{wwDcEa!?g|!d(`ALtTF2!7AK; zZ3SWP(R#a1$^uBj*_Ls!O2X+fT0S}6#F}O`wkVfJM{B^`(0;^CNO?hyh~2un)-mJM+GV5d zu3XE;1fvQI1PUcNxxv>jugz{8#4`VqGB|wzjX2SJy|wY!c~{2|}g zHfAP)cGhjnHBdLEvEo?m=F6XFe$cj} z?;Ag=x+U00#BF&|8n5cN?yNtG(0E<+0OcfJ@QKOev zBd{EAk45*$!Q=I;VVp^b_PuS0 z4spDTJptobpI6heY5W^3#vBkzmz_S~QqPPd%Dpy)U>47>`(?|%x>+X3I;hI(6*zIG zS`+2_TNmO*Jetnft%_f9b6Ax_l;UzAHh`S_) zc42M-YYryiHI4#@PQ0dX6aGZtye=ci^PrQRmwtj^h?$)igxG`cyp#bG$37>R5zggc z$2%)u-=6m!)2ABK*;Vxi`4Fa)1=D8$k8B?m;EBy7vdfRge2$?b6+p_pFszD}KEnv8 zBC2zNkOiZh!zIeT#Q?yFXUI%L*IiasJr3)}e{NMq6Z@&oWj|L!GIlMz5ty^7rwJwD zndwR};2kd4URINy7}m`IS-skNpb|xsd&0lExq8Ue?WI3J&f_l^2GB^N zD>=t%slt3cWK;xs)zVR_hKwA3LCVqt>_G+~v-N{I$QXlOxwKzWuY|;-)JBdYprfl1Rnnoj2#nijn z2eZTIOi^b62=Y278jKp08GrG7{aL>3Z)Ti2JxW<)4_Gv>zEi{6h~=R3nq}<+&&^q4 zFIznIp*yQvCKIkEXwd+;Drha$Ph`$c)xH%Tk%fjj5)>H22IW6_x*6G4uZSYy)R~d{ zA8X&eR#3}+g_KdAI6d{&wZ+In{~$5SU@0OQJ4V83k5w3j+U)=y-d0<;s=eA;5^3Zp zICQ++DZDEoT4(RnKYDxN)yf~pe{_5GUcHns1l&|r?b}ujbY|sMm)84G+==7WA&*sN zt}6CW`uU9&fseEo;^4{Q1;D}pwI8^pev09jZyLRwmw@MmUo4+`sK~;5|SMZ+9!T{bh**kh_WlAFo|eiwpny(K0d%A48HgyVd3JoJ!0!fsi^3zY&>YK3$y;^6+|IXbNAGK~7 z0!1cr$UoCYNP&8GcP*1K=CK_m>WP;}{g5I(ndPJjc-BO)eE9BK5OD|>Q6Idy){l0# z#Z5wvo-8s|MBUUH)SXLI8by3aWn>S54doJuEl3TZTvCaQ%vUV^Qsv{q-d>Hy|7S*;l6CbA7QR zyJO8LjM#smvE0<{A#&-0lJ>;T7MAhXuUPB$Jb7m7OE1(-0dx9H*+VH&Op4jgWl8&J zpjz6CrVYr#vT{iYd3I0xd{ras64N2-rwF9r!?KqIf|TJ zing#|&+B+F={X2us`A&i)XrQE>Ki{eRAVM6#+cl?DS^irjlEaEhYMag^eIGUFaUj2=Q6+@fY+$Fbm3_Ail^&W zlzFvBcOt1$I9xvauvewT6WoSBOxI~ht9a7u< z>coIRgHnftH!vgv6PGhWw0FuGU6>BYdTRzcS1-(BSRlyhRl_OV5QfNJo^iKShY5Fj zIbYd()i9%ZKW%Vr9LJ9D*Q&cbVz0LT%J)iw-U`wT-|KV^?MaE-G+I z%pwF*b}buACF3HV4LNPbl^^?^r^(%v>`0q4wz| zwV{tA?94ZG@<(72_;~D8^*5A)Nf$tm7qOCD=Ewb&0DhpL$Nme)h@X1=xk#z_)cW_av3QcSvM2ar+Boj!JSRQFWEt_B5 zJGJ)m(N@7!VRR7a%!LfL7)#)IbqpX@tIF&g62bF(%b3tG2C%2o!^A;A7svt%IYoA~ zzMdw_&D%^c_qR=L zU;PhHm)Nu&e3Sx%32`^i2xv$kUfr383fLai5ch=HHWmSHma{sXk-Q8=; zFjgHuT?X)-OpKp=X!H&jkD|z7%+%I^wIm!pHOlDFJF3GsU*^rLyEQrdrR_^+h!One zomI{U)M_kB$7`*h_GQ5Bt7~(aJKaE`ipI=yloKV*hY=-9D(sE@%C=397GC_?t<}l| z9ywVbfM>S53=2^x(c~Us52VC?W&2rG^xxZAi&JC@RJ_(p$=8z+1JMw%3{ZKUjWmn| zLZWo3N*tbixmLSef;d})n_H}HPJCupRb@|BA;&>b3$H*3$o>9%->IRJsk)F+cU2pc z(W}z2546FD5SI?EMu-qatg4vg(m5}-2jh6IjY7E8Mpy7!jf&tYa%A}is4WR?Y!8cw zC8{3o&+abOQGwSZWB1`Eq>a+50=RN{bBmb*-wKMcs6r3mP>wQ;%RAdP?feu* zgy>71280DZ-();=V$?HXI%I0foq)$#Heo?_Mb07#hIr@hmohRReWxYxU|iT0RGJ@Y4@-3s`ed z@G3?+3B&yMN0^^D^uKa+FD4#NxtuL# zfeD$pRND`LD-gzdQ>H;`qq_H~&~z5%%#RfuA$FO91H>e%a~Vv^=G?(M$jm&jGLtcJ z=9I(Wz`M-UKfH)L{q}>E1K+i_kv2tUgpwZ%`n8gUS-`z2??dr=bRVL8ocea-< z6LdNDNAGX3ZF``S$0B}qPbINs(%$M+?&zbZrfyi#L^WMZ+H+(SK;Wz#d${0|BDw4S zwqyfhGx-3oU{;G?QQ2(X_lY{l9I8nBeET&(j8V!dVhPO3$URrry?OV#_6g@BQ*T*Y zFVfQPVTAsMm8FiT7=z0tY5(cj(Rz)XKc*5zvUcYzGd_HG#Ws-#kca`Z_1NFMW9l0( zm9FxA{M)10_e!war=47~imuyNSB{#H@)zZ8t1BC1LvgiO)%Kaja{P3$U(vo)m4Fof zSRsGy)>_=jodpv--uk>$8^I`zg$jTCy;6!G3#J^2{JLo03_9B!3C39jb{(#>kVbfh zo#=`pgDq}IwGXuYl3&?ei-S53?G@E)sx$Bf5RRB7WmVg#l~!ZFfNy9~rAyWBBkfMi z+U3!DaL@&FS$kFO{`GY~s(N}?1yEnT_v*To#%Dn-@{L#SwRwXl4iyFmcUFnj4ccxL z2K?^B)kgpI-F5Tc*A^xm+0sQ6fh^%Q;xt6>61;W>Fjp?EL|p+=ZL;^Z?Hhl9x;QH) zeECfK*x8nSwGj{IgSSi_J6SYWEv;4QkH5E+vQxwoAE5g&28ee0hD$Wg2snHCOs(0S zY2%n$(ui$VjDY>Y^UF9uPTAR7M43jqG$@*S^|*z|5sy?Mb13Tu*wE)SSiBh`)IPqW z)?f5Vx){9mz|`M=OX1Py8-GPS9>gAPKcq;SM${Xf6STh8w-$oAUD7x2(eJ9fyHI_x0ZvRp={a~BSjJA{QvA)66Ax%%I6HOi0-7jUPDPqxF) zB4ru;kAsv zuP;m=%_lIq%=u!Uc0V?l<*C`LW^rLi(4k+R%j+S5%+sYp|8PK{^JWS)0jef?0{Y>z zbEr4_cVLD{karj=TODDHoCbNyNM z+VwjgYnD~eU_RSEY(${ByYr&?Q}4a0@<8c<$t%eK zrI4!KeyHAxB0-rH?951aRoHQ;81KENE#hdbmZU|ga{`q$7ju0QU?e|`k$M4_=r3=7 z=aiw{y{=Y6u3S>`dnzSipeQRQWFjCF!By?WG3+RjIZL+b)kbVnMFn~($Nz9&(F>GX zLK1%jRc@IW`6ykky}Y7PZ;KqQ)GiAIK(YZt4MuK$^!`eHv9O9XHh{|EudFnCUL(V( zb=W}l#yu6-$^(zp8mQVV1h658^s;tmG+%On&|{C|Z0PiOyD@m#ASM;tYim!$@7&$) zj(bgOA-`H)g(qjO)Qea<|&KG$ulD} zkR^)-32fv;X8HcMRrRXZ`)_VZ_|X~-Zd_SQ4^lg0*R_{d^rW5cJ4pie58qvuiwTV% zxvSo8iw%$@QzWY7(BUDASug=rKrAGoD9XYD>*U(jREx3-Q}$_NKAOe)Fk>=OG;D{Dy8l%U;s*SkcF;ZR?!O{oH|pZmRd*5Q_!m;ZS($mkCi}Tzp?=?7lvOd*RTG~D@_G`W(ZetV{sx>Q_{K{*gkU8GD zzU;3Ev~HkN<*QGsZeLwXGrw|EjTjgsgpV4C0elh981GWwQ{YqpJ%B+V=COR~f-)?B z4btuDWeK&|xs1i-?>`hnI zO(!hMH?`#G>@EN#xW6J0B?%OTWOMW@S7*9YAL2ZqOv;Z;(T`7YS&g^z$ZrN9=#TH=s z;tTb-5tei(S`6133wSO#PM#hKWOF=?sW~D9L{Vyqdf$yDDyvs8fWddHsd8vMaU#cM zTyx}7*t3@^mySR$bRdD)fLa#zAOwQpFZV;BPlA}?!7BLiqJPX`x|V>ucw$eJ2TK2j zR|E!z5}w~P8YC|ehH?Tkikp9*A()w{Mm(z z8taGfQ#-Ox7#0U}o~X*Ds!SJVfgYs^vWe)q%q$H|;9Yji#8d-J*kEpF_SrELTEMw_ z-H=Qy!$nZbQGMf~umTvnYg%z)0w|AnPfq z?JS{*84Awbn9PYmkPLu9k60QkdFU*Xhmg5MU~-I}wPwJGuWy+!d_L|ImJhVw%P+1r zO$WJ*dCq_Qz0qIS5y$sTl<9r*n)>9n**4@iUz+;!=W6a4vA_&WRWwFa&Jm6*c`PJR z4)jTun4t`l^bE0s>fkH2XvWBd&z^1gHern!#5zK39#6QbvopY(+!f@dEiThoIaotR zmoz3H)iPrgv1fLT0urSOKEq@JNfOjzL{DR!b?IQ!Y&ZS5?&aD|ds(g42z zOhJ1?Cjp`tnzbvYHN&38Z|M=98WY5 zz$8iq%|bHSpsXPXL#}`efQ-OqHHh$EW!1SEwuluDh(FB)bL=Y^X&O1Z;~7 z1Rgt8XD%*ZTsb5~X~Z~%CN5mRVrtd0LSm%uS8pAeU5)$0+J0F3p4x)TZ{Jm`0NW4M zy#`n-u!QHMbtD8sPJ%%A`sKAHB*+9A4I_a;VEH(2QHAD;_Co;}moBPLRw2l$9BWzb ztZIMzx?J@ojo8+_NK*)7Tu4&_eUByFO={a{^HR@nm z;X`+pt_AZd<0T5g!uiEWLRW|2?zOdml3Y4?l9QIB)aB~srnanEUbG!9&ee|1DZYca zkg)G>pPWxTnGtso$N<+at3{&P&9|=hU?Pwqr=K&%nGh1NXktyNN?B}4ZvDM{FAG}% zy|NglGw^X)`;u|uqZWG{$G&Rms3DxIiCe$L?m(}-4ESkcnEl@Tzbk0I_^B*^U)<6}|kkElZ0K*cLAiSY;PE;OKz@AivPF~lzIp{Zen>-6-ggd6uulgB!*~Ms z%Z%?uc28%4*>weIOk135^=b|-5X>iMY^gc;8G^Y5tU36Zf&AGgFnz2)@5AP`!;e8l zhbWpwovImYhei$wrsF#%XW{i|%+oQ@3xVnG=kK%~OoaiOP)-no2?@EJKxSem|H<-A z06{mbv*hUb%*aE~&*tu#G-E*tOgK^n6lR=QbOMkB+Yv>*5J>Wnd0s51C@Qj?7fN$z z6OP!>XFQQ3W-!X^C*-m1wU7k5ipsdge=!bZk{6|ri{_71E1PiV@6w|LplKwzru!|? zJ=134E-9rtVjaj#e<6v2zk6K?Pe@gPUO+>vxw~ZSZ?wGw@XqK{2!<3nk3t~T2(S+cM!kF}g)@B?jU z6G}9C(S41$Olo53RV%M>-=RmA)^uj)ZL7-QlHWddv$4_O{rG#UKhO~^OBYoiTh*L0 z8xuDyK-C9ssZ5m(DEA#Nb>G@rI`*saa@Pl(6T{ZuqPYb2;u(UeN*+}o@TQI zOX4QlC9C}%7>in$sw8Ga5t~j66sTTzd2O^I)8+5nT?TADFw&By0fbW#5GYj@u@>;J zzchM3E*o~(S+IrkQSJyVH>`{XM zt4-D1i310<>7PGWzDuDQmh}t3K?Lw@MuuLQG%dmg(xiL|BcwBR3`3^hW&zQm3O8Q8f!3VF6ctCWpWf)8)<<3 zDeOKn3QJ@vJh^k~p7x7}9C2n<(J!seMuOm=XSuenM|r!8btfI>EKTjBY)|hh`2xi- z=YRUyI+xh?R-LkQ8Wj68yK5!obbG)N#ZG!@(Vxlm&_T={gA5k1aNcNPu{T7f2hMp) z^2_wB=wMyQpBvWsAx$O@juYiFR!PqwE}~oE;ti z_{+7Nnh3*e-e1OtH)kwVQ5_-3%rh^ zezGv($lU7H&jHLWV9mi#3!Hc4J#XzB4YQ~)&&%@7dEvazgYyzh!12?yKGNX=<3me- zx<_L_hA=cHg^p2pz9nzdVS+N{J`L)S+D-$InG}M)_qz)jl1-l^DVT`At^Jr$AdFeP z0?LjdAQ%9A@)zJbzOimB+Kmz^0a2AqM$#S?A4++01_3Wg`OT2$AZ? z2AHT55J^HN=&8y9bLqnRI}-d&SB%yllLoLMQ%_(%oj>OybHEcAQPKve!Iv0gZC>B6fC}90qJZR9hbbY^dLbk=);LY!GA?9qB9;h>0{r$)4g&G-}**`|wabXwOsH43;pgvW9($Bjv z!Hd%3_E}#TD%hXcqLjT!y@eTLf}$G<8R+tEG!gi z7+T_vOs^8omUy%3o$IIm@`dtinp^5My~`GtF6AKK(l!L)B#>A+zp;X0pMIhk;~>hM(W|O{JN|an*8R1% ztDtc~G}e1t`^#+XF6nZPvsn|A#A>@utzFbAZ*AWs&do?jJW6YVa%#NZ?M&1s^8)hH zGW*%xg?z`gmCVZnKfkf8Cd5Gk-i7mOe-=?#{{2(c8=G4dPagffH&>M#hY&VX%_JU) zl7Q$)Rc?D5dAedePCm1%ti?`?GbG`6t|@gIw}UFuVlBM5(N&8?ZAqvv`APLRtg4}m zG(D4Qj5Lx)a^B2^0aQC`Xlh>yOWB#UufJ4@y)rK_Ub$q%o;YCV1v88QH?FKIT42Ik zFCluYwB=VURHrZ1K-40ihA>utd+TV~1!Z+YQ94$ZJhZL24jiw>r3Jh5mtU-0NUx4M za_+gh@R0gEz3{c6ueFy@SC{jkiB)Yh05L{cK^5u4_ukInoyL4fxP|mB_?rQM*g9XOz;F1au36R*`n>Xk%?ZKLeRrK1DRv^;C2=M9yLZL7O zdiNd~ndgYWc$C#EWth~)y9&$EYBlF*GysBp#>geIxD&?QH43VngHKh?9eP>8p_7o~ zOiN%mYfVE0+=&LvNXrrU?vBX?yU?UF_ao2?0W$lNK`hTXV4UnzH82!dmt)H5pN<;R zq;Y-8GQz9XYeWI8+4_#M6}qGbBs=13x%ef4TtSnvizkkXx+(nHEIU zs}=}6&@=T4T$HE_^yRg<{Ho!{uW=6EXqa2TnuDJf$oMk*D06{h(Calm&M0R_E=tJWy{V@3x88h{Oa?L>- zvGZ^W6^wJBTY^3l+;x|Ykj^MGPbDx@XApX|QV8XZd+PHvC_yYV({*+vpM(g_R3Ygau=WT}EJRPzVc9F8AAZ59K60phv?D_10!yr5R)QGe-wu zK?(18t|jCqj)a5;OEMEkWR>u=cqZmCBOTJD?Zth?B59OwgA&z}8Ez(4B2!<{T3Tdr zBtFuG=C|9s6@aev^TZP%h=Ty5%edn%BiT%G?>I`%H5C!x=2gL`-;r*H@Qr{ z)w(1N96B-DW2lPbv`7LYnO_P|yj)56(e{lP0bzE+<&eW@acv>-Uf(`>rj?mW!Y3ZA zPYbbR@%-YYOD$=*B#rf&&o~7tjco(PJJy#0;!IlJ17d&yjTgwGJ^3fG2}l}+I+p|j zwxzIYX?-)B7fIdYSFLNPZ9S7TwWBRdt>3<*eb4;PKtn{{_vEQ6!f9uA z?m-8Wko|+VmRM%5%=>z6Aq)!wH<6?+x-_VS5FX9()0LAy&=!dZ6fQslIlN8`=U>ux z9kw2lk3;1nP{LK}HcY*ApakOjy@xCDCRSv=_(F*l1d<(06(T}5?yb!xix$*nb+ZP1 ze{6f@HjrS=Wwml7QNMfbh{kQ=Zu-aXsF;Y)=Qmb@DxM4=Q*-DN3}%uuDc4+HD`;{^ z7?SH=Xz!*0mLq@{F~Uipoe)8OYF8;*)!u{4uWcSVIbsxHGIt%WjP5OKO9DLJ)Cxj* z*BZrH1b*i&O;Kw;QPo%3oM_y+ru4#Nq{umpsJ8rImLjHbePN!=YU?XTtCShDW&_Ol7&_fiSW?VLUzjUrj)HfSg_ zCiF1ZEU!1b#DT#Wt6AG#8$l!a-#tDGg4?S0$gHUSYuMj+V-Z)N+L8xR1}Gz*LMbzLjK?*dC39<=kLmPS_KPVZFIwBAYyj1^V_m9#-c#^xdnDKrlKb6=n6Qz(z z6=LDHw$?jhswyvPpOrCH)=9LBXj8`$TrrQQi5^b8fj@hu@*>12Np_|;d7w)Z`ViAGez96eRi7R)bEn&a+u zwIhsbylAo^j?FMGtK14FPS++l5{RljfGyv4{?MJZm5-fQUsf803-a4rO9g**NS7qj zD^P+)6vo3ROO9+aW7oT{YZ}Mv??9~^fdsLFN55!(ebk(%%U4t*2>01>wD^(XfrRFoL;p&(ztrKAUFJc7LO7of8eMtgz!y^=dwnjG(~H*f#Gh zGbpv+(VNeXc1xD_)`1=D&uu9JX-8#*k8Pj&hi@-3ofDaYC?TefZb5}w~v0u=mPcRyv5(if>!`B6>;c6r~LN# zRqjAo`ne-}InZl|WPypx4tc*aTh#!@jPS$dGtdRb!E;FiswRvKWG(gN1^j5B&Ia`V zJ^}tQz$_k}7p6!1Rlw^a^t<8nu!5- zqCERp+zE2-a|qDIc1#DD>BzwLgFo`;pg@;1;X((Kw#e*aXMBoC3C#RV)G{pwvd2s& zLf>N&m=NL;47H>HpNZEHdQ}QJ_c>X|guryagG)&1O1CQV&3ZHGS=pwN0Mfyo_!6?A z8Ir8bp@%a)M8$m>qhcY{H!~h{1c0)?X}9%5OuNBWq9cfg}tlorggHBmg%{=D~@E zEM?n)%28c@zkRO?8NXzbBv3?@j40re7WnWI0xAM6V{w`ClKG9eP4Oi~p%1Zu3y$#K zJN|xc>@>y9>qW`F_P3AMl)&Z)VQ$)ryi=MfmlUCXohDJ#Hym5wVsy0VXSs^Y% z7+zsnzNA9U_{fh{v@15RvoA_Q5Q~xWtXkXWH6jwb3=D<=H)z$I6tP z%dl%JPnI{vPFy;_OR@+rOWLlkkKJEcncjt9{v{><$(Ku--UTy?-j$UW%Dw_}=?$xj zmw6!G={EUb*Z~;RZ&XL=>;EYD;K)on#(F6 z+(fKP^F>2zdNM!pV2R4hCKaghLMJGbE~$k@kxYXwo?mI($ukutwyCWqSX_%WcdZ#M zhmrY6TSwppW7v$0yp+iiH|_hUPq%Gcv?8nhjwJ#)lu57xP-KV(AHTbby|&z zW2Y)ypKAnq~; zrNsPl@D$mGwxUNG{ED{NiiMqwCWnL7Y1^#|oG}`8pWR)C-PaWH&N;s+S?;&?RjzMf zx21VyT>_0m6$!L3c-f-5)Ia~zvs3@4x0eAZ1=bmjkwopT!`07Kfs~Ssnp7Gyc81E89yRv7Tra=J6ffSIl?MFqhWK)k$Wd>-Zsyneg zj!_EB>9*XHNdkIRS@m!LGE>NT2rrR}R^`G{3# z+|(Z8T?k-vE&~d2&>^tB=`p4W0+3vG#tWZqV!7-`7|D5V_h=4>3coKBLX0nW8CUOqCJkTS96bndqk+b&gB~`_nz^K@Omn z@RYOg7rhRdrvwG4w2mySoGPMmc2w5SUv!OA^-8Yzq zLIU>O)xNgtK#wtE#Sm{i)iupbr|RXym6&0C;^j*J0Cqj(k?qA! zj+7&F=(6FGT5Cg(H*YN=rcrW8U}oEbW4^fUV14KWS1if$io_;nf3Ce&6J8k7ZfvuB zg$UukLqkNuC^F;pY=Nw1{Fi@c1m?0QPmg4(3M0$2k{Czexd7u5m#i4fYquN2kl|%2 zE(FYY;Fd~>i7wo``OKKtchmE<>;>TR6w-An1vbCo5-( zCsp;eskQbc3lf1H#dlmJPKc=@Nd~!WQ=Sa3qkO?xw%mR4%(* zwQOW9ViS_Lt*c$4fAB`!^ZN$)@(Y$=99()}Sa=$1i>_<-4j@KJJ zbN7u*r7p%R7*N<22z}`tk4P{zb~P@xDKPutZ51@U`34r%%Bl+55K&ys_?Tfx1n?+B zpAV?>7r-EPoEd-89>y5r0mlG686|<(Q{}P)Bt{S+&cVb%CP9KLUS-;PoBWL4JVzwZnvvi3ixLetvYubFrTbf3DT5 z^8$fgsX}H~LoKsEkj`aKggArgPxo`dkZfqx1bbGmI=*j}a!FUzU32*e34#QncTRx> zbG6|FC6hLIv${@f9*HKHuGHBByU+Z#x1ZLEYT%qgFFPh?LY)98M@xVf5Q5DAR7r0< z`#V<|*)(QC7R%#i%!sKG#Z7bEhzjIO5H2w$2q+bJRF|y^I6L+=EzuQ$5hWcmdnN(b zlRNA0%xDsmBm-fG%$y!FXVXv;UTm|;-UK1bd|DnG!=z!f!(eenAe}(3N|L|xP>})o zv*a+2Cz+2Nlv0S%8!Ih1u?m-LD#@oK0wGho{a_6gi31DOs&ZEAlKn0TC$QH7^pJn} zWTnBWiUif{$!;>W@5bv06Y>rpTo#xhu-hORCH3_BAfTFS(>{ zgvVbVy&trAe!X*)s5+J`Yz{WPw2*+s4eJt84oP5Q`@0X1bU9qYCQxNn+w_DCFD`D9 z!;IQZ(9%WHe2RwDIr+Mq*Sd! z8s+mFt6TocP4#IoEO`U5!rR`j{NzK0L?5o-f2=b3t1qi9B-vS0evP|2nJxwJdYPoBvC-s z$L^nc&keO{hzkhlDhchTUb>*}xWE6_vghE5dg1Q+6~!W$r7mCAZ}$jrcXNc<2sB(8 zD1A)RfBK2Sl>Nu*6W70bYb}*3zx(>?9>hL6UcJKNzMzqv3Ro0SI;&mWz6QvC;QaKi zTE}6kI56g0TMOwxPeRl&Rp+lUur*UzwV~)qW710pnip-CoMHT3>qmX^t?k$SKD)8X zG)A2&yx_%s6&Y;}h}9xdT1K`ntW%AV5!9(l&(Go!)hH>g7&i87azsyK-p8zaq3t$; z=N&+t?O-FxtwSn*p{>S#sr`WUQ|h7GbFK$(M%Bt$gIb4TOC##ytpZF zq(bz=dVerWezEG(_W4CLE_G%a-_{n<9@*a7x^;$qEe^V?2ie!R6f*C0!>Dyn{n8D! z)pWynb00V3I|6;*|V?I5|Tmyd*~6T>`{V- zC1LNNHb}Ky$nZRbjxiA3*?utqCSN!u2XG|nCIALrZ6GvJ*Xf5YbYf=rRk=XuhpSgz z!gLr4OnEx7<^z!VlL>+e!#dItt0p7_rb`~euyz)*bHRl0iI*f)olE=dR?QAGGMXtR z_!G6SMbRA0!RrNc3s^r7@Uysjb+K?EBPN3Bdt5IHX4Eo)dDR2=jEqB#k`~F*5Oaag ziIAPkc=*{L#$a|uy*7oEBXc^}P*lq1+ICW8{#)@zA!?l1pcl43;w> zmMk$CF{CP)2}*6vN#{21sb&Re$WPu93zJ?Z(Z#rWyxEX^Re}#_k!{M>bO{0@ zL2W{fiwd2Aa7J?3=$BmglMmHiAzJ8i`A1LHy4(Nu$mre4&{I@$+KLaPWLTn@@h5G8 zAin(K_4PK~<%_BtsNyAdEC7q_^yBZX4lhbu_SI5OK1(FEb9ON4ckZtD%MkDaK&E2@ zHV~M0Ah|)#8=Rcd=zdd(jEI;E4QNudc;4lp4C+XssW{SWZi9 z%kle<6)zx!7bI-nSEgbWc?M_M;?JKgvcGw2-2#b{u}*MU%lHNJDrnl*02TT9AQ~Z2 zO@cCO0fMh@F5AkU_THinDfpC{HvHaqMjn0i{^DA?u-4iLNgqq2XwZ7gugO1ftVWNt z9kKwM*_L}jCppx90dT*RySa&M%l=WLESg`NlH|j0Z;N6qeEIOym)cMDkxnMb3Ru#4 z(m(Y;M&|85a>&$Iu}kS=!?(9i{q5W9EwEf7AelQnHBM$HW2(yWCYe~BA(9ug&+X@C zQFUeOb!ik4kg-$!mCd!6C$}xD)RwhBti;l@VEdt3m;>~lI$ImQ6cBG}uck6p1JO$$ zBu0sPy_dbDC?Um`C}e6ur{rd>p`N-P5`sVh11D!&<|Iwj*R!`vp&ULMSrU zF|S@aYhN+PcyGG_QD$)m&~^X%TH@5%e*4b4#qt_doW)i4w~x)EXM;;o3j3E|e5Gds z7!jsDg@kDH52rqK*VG?>uU<(eP)H9QFPgY;rfqZzu!K4xUEls<2e0iAAV_(* zdKEd236sQXcsU*7T>xcvT6H1LLw4l}!$2;FnCYN)!mvO$dv+M|PM0%rIb6L8oJ;<6 z{ES9kI1HB#hwxftf*eCHf&be_&G zKA9*7BEdO^<)E4t^GoTFiE;vIhMHBXKEcA(r~7FpwQ`;=ybDZK6d{PZ$O%4EWV)ml z*$xnh@Rtr$ODV%M9tMkpVG)8o4GAT+VF)8$P+*qlbFRv7-w=6h+4C>wjL_D7MSh$|sQy=94(ovV#Q6kWPw1xXcjv7j5yvVM;eCh_7 z-uQz%T*mU|HT7xpd=UntGh~)*+}b6u9X%%HU0cy`?swwRQ)MtRHos|gy?vH-4MW(#K1{H{fSM5LhN~PkuB2mcP-FL4U`A8*RRVb-rRV^#DI}09?CE_nupLweK z0|KohEHF&f^S}J!)Vr>$^##UfyUL#SejM+pyL4e?VhXrWaN(-U>YG_`^r9D>v;s$@xbIwFa=yK_{stgK@i$#`*Wsdc>Efzo%EebZi`B($N};IQtpktp7Ef;-o=*A-hEIZ!&M z;mA?u!d+{pzPGDd|M=-zp2Nsm5)%0hjdIJrl8oNNXu+vl8H04h?9lUW!MqxpNN^{r z1-lQAmZCafxovgrsX>5?kLu?7#I~aiG zZCBT3$y^RW6@bt48g#!v{Ow#bA_I93^t=!*X9qbcbbJT$IR((f<-m*W{t~4)-q}H3 zG?|by9fkxX+-T?GA1 zJy);5-0Icq3v&xtb8sFQ4nS|{Xc)MVF>yM)Ixg_48He!dB+SlbVx5JgKyu3&S^C=6 z^f?C@V$e)H$fwhHrCDP-7j#h>ktejc><7qXMR~dbA)XN?=sRVf`sC7tQk4nckW!iX zA&8uma|w#2g=->cow64u2wZl_fi4HgUQC!$hD;1IS>JoK_U!bkK+!Y_DFbDN$}W!9 z+#QR{APYYZNLYeT?g7-A_BlYfWC&?SGrw!w&((MKOr7zX3Wo(oo~+}U7MrifY#Ef9 z94=LBmK7#S5%Vas`A~wlw5SibdOeQQPDFDBQD`W z{~LFdJ>b`FEwKRQcemGFgA%>tn=jSIE}@<`uXetOl9)uhL$6a8?H|3rgw!HJ8v(?J z7Coer-u4RTjVnv+TdyAdc0u-QIJiIv-aFU4;y#VoQ`X{f^Wklk@!}OpW<0fPqzf4O z1Q1ug(U!QWW!ppBUfDYX*uWA3Y2>)T8*HMdy2-TGs*6SB-`MRzgLS* zxR@lEh>x7Cst?{=?k9AgexepGRk49&@j=WOwe@F)MOBbImyhF>mpGDO-lE@bEA}+; zp0-jTn1pOURD0qyF{@CzVgKT}sdrpgTGF@c7XaGMCV&R=(d|{hj=o~lR$k6DUC-~S zG?jLYZsdHW?!P=={U1$;L8Rr}flLu|v-9GSACwm{fRWlrcxiNIIl!wx(c!j0gUsP} zuAfS5tG%bsG{?ssHIc#cj%zE=+}q0e)kRu#%h<*RDo`$(SGNiI$TqcA+U*CYzSH)N zl6kx>bh>>a8N>kP3}O!8l0cL-oVImFkmV9iQRuw(vI?5<)8PEx8>@;6coIa+NQ(~T z=6!YdrRu)c3W+6peFL6wx?HMq24zG0gj1>#Msj%PGV6@R*q(-TxkSlkodnH0)>l8j zVP&np-La zau7J^eQAGbjK4vGxZv)1Zcp>BxsQ>A$99xc8B5trAWHPh78N}*m_Rf}ETAIuF^puw zkB;^PiB&H1(RY{**a_!O_DPYicH4m}|4*N*<>16~Ra<{g%LcONwA=6QxV6Wj{fJ5$ z%i-4U>tz92aw$IggMi1oto?ybyyH-Pp28&yMY7`+QtqM@@*xWaN+I1leXeMRkl$Uc z+P3mob`B?R?H=jStLPnu3xo`NFL#(&J|kp;U^;}r$UOb)BGbL-kY&ECUPa5fbX|xI z6VNR&Voba**`bS__&7{f_g+Vc4X?tBz=d#KtmId1%pCkRgt-N*IXDmem`9TfS2z5N z(5p6bM#i6pG1Oyqx*0PX*4MVqYx4y<)i+_CD-Q6Pjm=m)9oaEv29;P27^f4P)0pni zZ-z0{l5u;-w8KnyA=c(+q*LyI z81%15L?eiheq<6EnjAY07O@%O{^RYHllJb3JV!w;A-{4q$4!(_hBpwUW`{Ax95*|d za=_4~mbK0%Mm6cYqNeGp5Rk(js+TSp$v=3!)v-wuL42PJ4CEC7Q4xfT^+@ zq2=~uKD)c}G**%J>$eq#4drQDP2d-ySGJ5)R=@Q?)e5O*&5G&=21!1a+pl_MyTLu{ zDii+v##$YdSFgLQK6teAa2d$}>RvuvctXmrafg4QeKqXN*;->F){}zjBvJ5>ZZE$S z?rJHwTjPVbO#QpZr@sDDNk}t&^#1B>SGLbERj$~gO1Q>C&r|9W8L+y&bQTO^E-#ut z_20a0l>bLyV)C*3E6Y`;9Wk*apsBA`H5Z)uu0Jd2Yzb+ZePw%#ZCynY7f=Q7rq)d5 z-QHTgie$v9(oE4qz{+nKDHMPCLKV=*WCGzdl^)BIg&BAMz*FM2SNv()gL$?5K+*>Wk z4CWyqGhU|`b;(!^QzB1tfFn0uQKJnq1O%e=m@rCcCYOM^sbImp>Zm57svL}muf6BYXy%2*WeSNvC_AzjE)YVT-BLqH z@B=0`6q#2{mwdGC>7KrNHH034a~XEw2AjX>)u6MSYxN3rn&Ijdm|b0;w@-(2#Wprv zpcfb0K(7vd3@YZ}=LY5$u;$=r6($CEa8V=dEX1#641IN@{ES=|f|-EGaWj85e(~C^MW&|u|dzns!Cg=R=NiBTdZ4BmQ z%TYVTye`}{7q|?j@eLImyTiKvl~Yne?TydPkDR$Qh1DUDD>HRCr%h6 z5SVp^xI-=-SC41d5(!*c#XxU%D46n}9~`XRPZIS5$48W53`>36xP1@`Z_bpO>pav< z3Z75H&ewfw-&P?5XxaQ)fyju^J5d9(B zC`g!pNhLxLwA`j*)pmzPl>9-vhn~!X$482o3L~++uPwnO$ngT6gk(YS!lj%_=7!)y zcUHQtip;E70R+rYMh4*3R=PFLF=PwUEml?f(f0U!cSniJQV#7TBzwfj&M&ldP63cm zZno@EZD=G~QKzg*WB?)eUR`Sk=;0k`k|C&KbCQ{u1gb&D3to|+!^m|JK#Idh?y9q- zF*J!oprVwVJTC_RuvRO~?14L%sbzdidXw&kn0EaNB?9rxH*563+g6Q&nK+B^eLOu@ z+fs0S^!|G5M=fd9ZQ8mqel=5~(;#5FcoHweUjHjsT@Lb*ROgP%{YujhbV@{90G~!GT&Jz_==uD{ z8m2z;#MH;%S9rRdPqj7ZXLr|`cx!J~N z2WgIq!}w0&e#fM-3=tq9axbXT{hU2m^G&fB5I}GT29pku34UOxXae$#8$#F83(iJw z!`%c7?NJ^EBoGUSkkzXR#tE+12(v84y=p~6BVS)@Uj-OW&+XzXkknuf{yM=#@UM+wq_*$;tMsYdM$9_HoTT+LwKh@CL_l=e+`=vy51C=hhL8GTrlR8 zJsKhCv!Wq%ITKZgCxTf9H%!+u0l`m92qaPYX~H1$d~(p39YCG9lJk0>tF>eLMwQzFVt5tAnKUtkqy1+g>lSNkR3=e7+iuyiRzlt*;odIH;hPVL%f5fm_C`-_I)%2S_h^qX&n-tB9hg{R03DEZ^{2*`&~R31PSS;AC9)IQNcU( zi0@icizY#8zHCN2bGF`TLXe0tBdXCb{?Y~2%7XBCqoINU$f8B>b=J85YcrJuT=9b0 z{K~S6Tjtl>M}i>J-<1FK>58hXQZ0>1dqjf)RFrpPW4`o4{fQp2Ms&MngHH&Y>rTI> zKIuiZL+9LoyzTQ!hi@$_)PbFcrrvjB`K24*b4@Kkf8gc{AbSE-yx4ZqDKI0D?)R0= zl?k)(!*^FOA>VUD3IF`Y8Vxq>D|*gmNs;^?wEa_58~6V8qf_s{se;B-H!pVO#S6+i zl4(bIynVTh+4{49^Mb(CLZYvLWR0nPeQa+Vqtb{D_?)ZIZeeaBaBM$R@;OCL2>zd6 ztDiXFLWtuLjYhvOy*PSYy5$pOHmgNUuT~B*#p3@->0IZ99;FL`iy}T3 zymko}1;J`YCWr8*FsJmF0Sa5k= zo&gik|BUcjB!~B0kIBGvP1*>|4oS$Y6eaquMZsYr z?i?C3(VDVfTlt$y0;9dA!cB`=#kQF%GTL{HKmL06x~f%<>YSrJypat5 zvHOd%3seBq)ne~)f2jkw^6HT%axrn5X5;6i5hFBwJ_jVD_K zk~gXqG`bi-X5NKC0|)`ijEQ&bOoUO=2+5te&o&0U|K=JlR$*@m4Ebo@x^5I+R|XikuqWH(9rjycol$hJZ0y1ca$YOQpHZ?E&FRhRxJq*Q34AtsSRIxo$h4V`OnW)aHz|@pj=Bl zdgFLt0eZ##>svJ1n%ee*wOGbfhj~0-9^6!`YzP<=3qe{IHhTcmy_u0le4wp~IGCym zkoIf0)|qxKl7UX}Ig&LI(nK5NE80KO6UO6PS>7x(P;+6y{E;!tz^>Ml;`UXez&^Nn zw9Sg;IuaEWtzI_u$o3Y5OKP#(Xh|$e!K4&D(4hbHJta2M_+?aarx>a9#=muY z5tloSUhS7&X!-N_i*Jt@B3k6tq6x6}o~sLEh<@``wO&BNym@7b;4fY<_2o^~j#2uB z7wgVStdK!6G69TO;5Jmq&_{~~3W0Ea=&t%%UZBcz&~y2^73I`BTU#f*3KXHw-&7+1 zuQt^mafTf`sZ1ZD@6OLnc;0KvD(rNkka82GDat#ux>1$d5IXS`P z3_|1F+biYH8%TLXJX3F(pa;1$>FINz;vN4I%@7IVh4Sh4t@L2b7l#nau4u+h+QeqM z3~~mYrJw0?5MX+3(tOSG(ax+8NHygi{co_{RP>3AvqvDLGEhh=9v4EUuw~!KSx}7Q z8NaIh%@dgJ?;B5@Ve)<=mJQ=y;Ep zq+~34YG?gb8OVQN(&A=>H@8=ll(YLFDuOAcEtf`r`b;ImYB9=Mvy37j2y!5!D=!cs zqdJRmkTPnN#7b!Aw=btaiV4a9by;EIFR`3@;FfBv^0aO0!Gb;;Ud-dslje}@si@_r zrOo}el_RS#RAg&FmNV{^QMVKYoIP912V|1({GmIW%~wwS+sEtsEYI$)GIr9|TvpqB zimQFL$(b4cX??s*RTekP+~TN@n$rT5UPSz#!KL@ZYU%nkG9Xr z*G9I+QpUI5;w}S*4nopVH6=6j+yDH0^$IF3TU?I=VEC!eSAlK8Lir@-5|&w-c|*Q1WLm_vujl5 zl;`R1yro*OBJe^r3gDoD7mfHJBU5?rk$Oo}SuagXv2gd{vX%;}*$K%1*4A3xd*6+t zkLaW4?4c8*-75(@IeHzdh{8@j3F0Yw<^HkyJ=5>*sM`%PkIwA}YiCw}HPSR-#dwqe zF@RWw^gSLOZa*N4CVz(`ROQugl2s9fk~+M|AT^fzF&~mA3O)mfGAOd<6fOkhgY?+1ZK-iA zTrLrWmqzkwFH6QhtKyPOcoGmtkpqn?yf8Uj#+?yYGxdg*)ntNCLYdcuKmvC(5LNW4 z0?N2@#)8;GJb%nEyypp94Ro}YI75jhpvtAAL&~)hy`;7fPE5Tzbgu4TF1!3(KJjt| zFrZ2+q*mGOlWrd`h7exB?EBjOyDrl?HU~;(J6>TK5O;j94X7Qm1ST%QkbOG+j1avJ z1-G6%I^}cms>)r)(-~>(L{1P~l=cbvulwp1xF~z( zU=Gd;a|>91P2nd#l$_UqpU%N9RvkOXFYFGV0q1(&Osrmk;bbK&o%dX;SN*wrE-f9l zwXNVCrgNFJnV-4Lxr2m%0wjc)&rP5Dbf-v&@QAv!t+@1oDgTC z*`Z4q5(gB9^PUu7hV&q_UYGf8?_m1$bO@;(su&v6k)Y!{5*O96N0|)>l!dnobl$rE z6&6WU{W#kA&yV365=WYO`49ThFJU;BoLto@bAG~EW%SYhSq#sQTmhzQ~_9Y-Hn0@-?_)ui9NR!}Rj{7v`x z`j+~|F=>*qG_TjKSys8n7hb6SR{6LrWHm$!mx4<&Nx(jeN&AkE%Mr9Uyby>LQG43g zul_~*=n`IO;d6UlAu!ziYWpR`_ulYIeCW;Vy*&M7TOcOnO{=RG7vk^ksO7n6Cv)B9 zRjZ&j!6BNda^1grvA$vs1n}(HDj>tC+sA&=&@Wz4U&5j&K9;lXrvt%zZyaqMg{kN@ z$Mv*7dAi=v6ils_t}E{iqj&=4H?OWYTeaZB+eSNC>PBlcI=oh9uwc6B?r;0la^tA& zzCgZZUA@yGRx!_^3j5z~-|7GS#_|Ott{AKqF^uMZXj^S5a?`1T6aejuE9N<<5L;ek zCzs1VduC)+7D>bS?X883YGyqC^3;7-myyW0_NyD}6Y{D==AVDHlz#lZRfY2MMWd~| z_gr1I3E=AXx|x2C@}>l&z2{i5T{;R8X^i=k@702lGDxnQDkdRTX2aju&~BVd>U~cS z5lVr!R;AUzA3jm*Sy}i<3|G-|MLibZbzSQK?K@&DbieD&*?3q>UB^ay$CyN;xCtQQ z0A%&FHnxb88%n!l=NjAQ&8wv%Y$H>Tj<&P8WW%VvHngy}b}82yW7ajkwxxb5lndws z2yn$aXP2LPsLBy&V0>hI4SK;aCU)|Rma?J!S>oua5?d~}0iQackkJ63iM68L7l3vi zE@?EfR#?`rs1+i?DN5&dYXFqG91MG~xP|Feb>raM`N8xub@3>8|EV%25XDG0brwMY z$&e`n_8&N2x9zjL>)VTo3>t-X?NJDr7kb)b8+9J>_q4}~`Ck7^SOS6|E*mxxXfIEq zG?{L&r)~R+@u3IGxPU35IbJY9I%oMWEFBLRqm@GgLQ_rFWOjfweu^FlWzHQC^q53i z%3b4hhyh^6+93=HQtk$y3wo`BDNtVBP>zGsd0`ycS3V=?OJSej6}=gyfll1p==Fj~ zp04-xg*RIM9L&M%4s#1wbMOin>ofG5gqaWhq~N@dtH6-#0$E-!IM+_sp$!xEOju}| zHSuQL5YFpMCpa(VaP_Lg@S(1uQ+A#)q`i>UD=w?gP87P> zObUl{0!R=t4w?>zawwr+rqD4#f)t#4G@SMNP5>rkc3_NY%8(c(IVS7DbVf*^tuI^j zBgD!>%3ek<)6UJ8xo2;8Owv@~GVoI&xQk=K!8`%&{2ykV(uiOI|JUPJ= z`_;|0Yblfaz@yviB_`>FIl752Vo8)T$&nCtTA~!b$bMKUxAY*Ba%TYf^sdnhag_r! zISyr79cJkMr#*FZC1XPjmOE=V6N^`bt9?6?)42!gwM;)XyIUi`@0*sM5KEJ1yoRrZ!Ko}{d zj?EvvziLbACABD%g4sFSviiB<%F49M!e+oVmybj_wEh+CqxFYQmiFp1EgMw$tvkxF z*pN8Cw7*_C6s{XrR*RsCF%PtTTMn5#bqZ_SR+l9UYt<~r-g;U8VTE0HTJVa0Z7*|Cgv5NwxbYG8rQOmiZoS!_f3^T zr!OHOe^L81Z*ju|YJ>!K&YIW)8!KUK{NBW*U`-nph4V#$KqF?8?M_4T@X*KGW?sg-V9 zEtp>$XlqG;J5TPcZG#$!aP~VR_&>b0s!pA**Y{}u$X(?-MUW!Ks`Bw@EhPPazEw1z zZl4K@*IB+%22{Iq=~Ao8`Gai>k5*7cCUudPFaiyCQ@eK@Fj(BSZ(3aepO5%M;*hND5HnLt^d}+f#daFo*;v*2djV#K8w+`ROkl7BlyfywHO318cap+ zys`Zm2BcTea^E$z$yaS`M#F{w_cv-mt(^K{ed^Azk3K4ZnSjgRxTDsm-nOo4d$W>> zhRqeU7Jpo-(6HHMAd(XzMsC^PY;G%SzkE|gH3o-(aP)*LXh%X^=7_pD?h>G(>$(-A zk;GJ_Hnff(IzmCFn1ipkA240KplGtF7lFs!c+3{I{4aSo$Hg`xlwY|Lmcs=j79;iJ^L*hUvyXqOC z8!!aO^wJ3!68Ex#ZXZwv3Bru9dF<3s)b@k5fS60?Vbu`aZaoY5LVKWevTo{yd{OsY z@`o^c&^la*C~$#fb1)aEpMIEIz?y?spnr4fC%AeAE^yQs4(qRapvfThDu${HXADiq zI{oz3tMiJw5M6ncPJn661mnbIm)!9%UCxj=ltYZN8GPPu10fiix_n7ZJ%`X=?CNJQ zWEpDdNYH6|E$GuhkV2;kq@TdYqfQX$jDcYl2xC77l*u&f?UEG;&p8AsBq;)NQ8CE; zxYtHz#410(r+(9rkW4*;4~uet&lG{FFv(`6svo71&+e4)~2K#1H)mBfr3ipW6#Pso4OewQtVB}e+m-6Ls{lfSpa zb6=ULTDWPgxo^zk%o0=Op5BK5l;rlQ8rmPAMJt_oyci+J zzV`A_sNu0=2^U>}Nks8ZRT6vQE!8bOL$Hf+w* z^R}|Pbw|AK#**Vw*8OxFE2LqP;diXB-F)R#+kh7K<%?^1yzAC1q8=hxyOs}7$mISMoq`}oV%Oc(_g#4rTwSJYZLF(R)vEZk?zquZ;Q z@Tz8paA{dw02!;0>C$`J$RM$&m)FjO#~s927B8rbv3657Ava%9J)b)9%qBjd3h4Q( zmR7fXc-!dX=n05>|DS$#^u8qXAk%h$&9uV<=WPe8#XopUjkM%Y#7=nUKYX&}$0TPR zU}IaiqS~1~$P$xUH)%I}bkQzoD=#gXUvG<{C>;!lOy}Rcqb#r1XahwS+E6l&7G(wy z=rZ;=AWL2Q^@iLI3bl3a?9|oEO9-(H1Gwm7$-CNCP`rd-KX$4vg%O|!?EL$0DZOfu z<wfBopxZ{1mCZW0w2UfkDOTn}#x83C54LdlH(?_=duHf_OR6dWqJOajco6SeBS zaNa9o=@oxBezCNNS9t4oZp+`KeQisT!B8Xuy78A^EMr)bm)dH;detGd81SFpPsVqEcW#^AviO za}>n@@Y4ReCEW;ad_xmt4&dDB1F_$^y82*8SXe?FWIyK+3eX80fjB~Dzf{%F(KWOo zE}LQvSvo-{1IpZYr#lbv6EM9h+EeIShM+cKAHoE27n?#}I~aP}LECj{GdeQd8V`n5 z9S|HG|J*Z}(QI(u)hjR|f2g{{j5bV%*O%;EFr$V4fA;<~*s|-o4?JIv0CGYma!$-3 z24W-u3Zw>#B1IaKJd`b40;{5{qpDn8rlKSIQ$@SV9sQlI?&#`@j_N2<>MED*a@i6s z*^({Eq&SEWMUe!Efh0i8i6Il2$cZ`6iR|Cn|9dx%?tSm!0pI|U?;j^m?z8rq_S*ZL zv)6v-oI4MU^IRq7!JF0j!@)byrA16&aRKWm1@Fvj#M{Bbn=?<_Ltmh(;bT*C2~OJ@ z9?oM()%<-lPgTdnt(3x9@8Vz%nq^9Nbd$vi<Ks2?TcC9#E` zzx3jaMOzALbI6d|%&>8WA(|ClJy9c_!aQLYQj30|y_DCj zq!v=hG%n(RK{C0eeK_Oyo~VbI@^ncA`~J#xqaQ$wU{u))g&d>ow!eh4Yf#yiE^Vs} z%}+dcZmpT0@~V}!j1{Rw2{A}ip)9sq3F>d$J5mycG?0F{eEfrtL4~Njsd3@8N8~WzFA?py#3^SUW(Ka*jP+YeP>_!KvjIRr-8Q$*hpzYYMFLuuPg%F z){d5M9NGjhq5B`dUV`u4R);k2a@pBXuc{eKOIy3pfm%(AHf(r)>!CWVjJFMP`xWDd zt&Adv#W^lugM5|$A3x9^)wueE!kXr9X`&^}^vg0gtwy>_~8kr1wWBa*Lq?y8DPW?rC3q;|#S#goSeA z8pZ#s*VkyI);asz->xzF=?5zg$Ig~o1@RPtoF-ACfjm!l+k+|$dydWg@iV11YqAyp zwnWaHT6-S4A-=c0FdK!v;4V;obg?{1c1pY$+PT9eXO@n0ZDCr0X|??M*!b2!oS#t` zqlk3}9Dl@06&|C`nf3Q>uiNg^?Z+QZx8EtlBPJq@EseT;OBICLnPL-Bz3*Z{!kdpjP zYyAK2{`$KMgpzGEwwrZ4y^u2WLJ$YyA#=(?lqRTJ*}m0?c%*%g55&Rg05MlhXqT+0 zC4$&n+Mzr=jL{*cDIL){to_`xBNH$!75;2$pFU7)rojZWb9wkygc~2(YSEN4g3~Dz zRh@Z)J`D_S$n)0lmeo$TGzfjJ+P8o=tLF_@ui*RT+ai9_u(*KrlY$?oYs8zq950-C z9?hzxKyQfoZDr0n!PAY-Ba3HRa8?Yz`;LDoGE|bM55N>>A!@@j!8t9+M4zh#u{c3@ z2ybXtrew|v(&j9;b^o)crytrJ4yRnhsxH_?(r|Q>8vE^Qix`9_TR;3t`vd!d_N#&+ z#+FH^seD@3tz_=lZDpTY8+v6Ije+2%RaMYZRRl8ujci1^9wLykz3rKUl}^AIJ^-Kb@&nA=xTQs%8aCE}@! zjVfgnaG1R+{Asl^NDuIE#uhOUZBCJ4&$AJBQknR@%re zC3N1hsZPcx57c7WYwZ`=w1O0W?%G;=oke-*WveYK6GZviuKJ+36z<bUj{7q`<=40!?bk-g=Kjz~ic zY2sl}&}(V3LlMAM(Q8XHFCMNHz>(2Y)%N#nt#Yj?yi#l;oR-GDbit#0>g_Q65C&E8 zN~aLVjExBebY=VWtn!^(D-C|-SZRCiP^~Qg>a8>X;h9pX>ct}?J*9B`J{LBv&_+;; zuJ*|3x<0&mqSRso8SF(T{|-PoAqUvNNH~ zVmSG)HVx;Vu;?jcW8>fqP0Rsw&YYRL08rn z%|CF}=;dPORMD%};Nb&vL@2>oL#|yh_P2&_T3ruG{nCwfIa<23th*Qzv0+=fq{hFt zP?)w5RLH4KIi5fheqsE!V6Jlc27grL&a2b8?X3UbzBTi)8)|v3R@mDZi%xrSwx{`n z*VPk7^#!Ze1@drgU{FOTK|QF+LVDM>df)Rtd3D+;*R58Sc9<@Su>Rj#JR`QXi ztJjrdIeoC{4z~84s5l4?*|bp=o%Y~Rxn93*PfR5OoCgef?K(q_DqDJ)9$+O4?!W}8 zh@~{c@^M#AG-ee>%Us_#HiLFkw=FgsE)pjsG z-MNfCpd@Vx>ae}|96_Gs#ZTq53mm#phUiB5!g#jURq-}#7!Yk_Q~RY0_L ze?kF9Gg@Ya&g~)-n4pMZJak5EY+s=349Cyb?`F<}9*Riy)vNAgM^>+f^c4DGJ|+k# z*oJNdh-@tc)7Gc45T0q|?Zt(F{-fiC$Y3sWp-`n;xAi1Z`>u+z4A!(b$|(fHOuOV%amIW|a+vf0 zzZ@eG$gk-+tks#9M_C+n9y*DngX}S=LR>+ATjqm^aPO*?z<~4v|EP^ke-~sl(WvIM z0dLE~oC2S-NwZBBzxaF=(jp8*K0jV1p+r)YSfNx{+Vch9Jy7YTs$aXky1P3NDViNV zb+EEW&Ln9}kR@i%94aXyQKbR>-8$CA2ROsAJGYf@GF!QKy*tzV(LkG4RJdBwOAo=K zk26NK6^gc40P%-oA8FrK=LrVp^uAUkfWL8X^^W|Xl;)?GV~Z6`Tw+DcBbz0U*LphF zUhMjh-d(jw)wK3(l)zRuwRhh>a8*4pLgBR=>ct{$spgMCLg=ZEGvB*Bs_>x)g{2Hc=`)Etm$6afPmI z?@~a7+pN52>!`ojO;`x3igI{lPd#UJv8{eqW7|jN_^GgBSuH3iCvNz)C7~91hxyEZ z+`yw#0zNnvsO#HHTgBgO;d!NfWuJ$L;zl;w6l^aYsmG=WCT%07U4QGATJ_+${mJv? ze7#DxWaf7tFNRB(HwRW#(Aed}P3^;Q3iQMcCa{Tswm8gcl*SU5dmHN7)&0!1f=fUN z&bCxif(G`|_7%C*I)8ykEz&ieE~heZnw6Q3KUZH~FaJ`mE5E$61t;h174@K*jusZ; z^*-R~N?BF)?zSQ3aYBMbO7l!D#_{L2&GnHc=5$Ja1uy5m6HQq|>Msqai%46BN^R|| zkCU!K-`G96K)HSN^!;i;t$Eq(usuCjcHm4+GBb zSO<|fPn(|2&pPKrqGkoL)iL~yyi}z&4G)y_8-<}@bIOFF^Q4W?^$!b7D0fPTz1)H5 z0uqR)PV673g8oOcSz)w0DS`>j(}n_cCN5i1=g0(4x-U>Qtzk$Rwscs?zH_G2Cm#L^ zhEhYIxO2IR&Wbl&z3Quh)DBm#=7;O*gyy+cucjUDn2QecR4rl=KTs?#U@hW@jqlg> z?rrLMv*K^aKhM>_qcbdoc^cCqU61`lZKsE4I3?w38^A9!>v2CWmt=gsyNn zml`5OJiXit{cz{lrJ;wtHiy&Dc2zaqZz5^JkaJir&hg|pbbIA&(CK|v)a~|t3gNUV z^f_OMy~>m;b5>Jxz>v82T#R%3y+Pa)z_(!HH;&O9MTK=D2Y_3h~6xOi43= zHet250aXrxI79R+Kt9Mda{GIu323Z<$))UztO>o^wQWj-nKSIthi;Ds(|QD# zwm%Ugp1m-74vQ64ak%uiuVtaHTt0d_$zf`RBL0NGa*6RXP-?RTkhLmq$lNH2Dz1zg z-ZCrvHVK|%8CO<1;;n=#wkp(Z%p|2p!BELVORXK4wZ7I~i4xePf50g5_g_`}=&{!* z$b%BGqK~QxO-jL5ezw=*XfwYweV00FpqK+Ter8lqh`$=xR(Z5nfo!$6t@zl}20V|6 zM2Q=$$15lYIN4UdHnyxDPM)h(PQ)xUly5C@B^Yz(E>!**9~A!R>Ebk0K)QI(j+P-O z6Kio>t+Xrz#a1^+K;Xl5!9LR$|66x#5Uk88oU=8*A zZmf5g%7GTf|K;I&Kq+U3OCKew(gYM%Ad3hFVH%YVeNM)5w%(n@j7?iA4p-KDxbW)n z`Vg3g5X`!jDpI}UHm<77_P_aT)lz8tjeCoM+d^T4L@n_OW>bFC#`2SsS-t`M=)d09 zTkqUfYd!HKBb-7gP8!=tDb2Xp)Cwv5+fFwod~bAKMZ7xS_R^v(DsYhS7zI<%y{^avc0m_LA=s%oe1CDQ&o@dsdE+s z=P~rFpDKe>w4*1V0*oqMeCk{gMNB-|b+OX3fEgtrvOj*dmhEauu?@(?=aS`he%V~i z!yG-T>e9WV7|y*`vM7eZ_3JAX;iR9rFgxr`cKExG*N5Ti8rv?sxg_!m1G}(hWi904 z)C7AQJ-J!n4<&)ZuMj~ ztz2v&<|Yhv>}fA-c61Z7!Z4T>T`IP^1}B0m-d|dnYxlvSm7#1WPNP>%6edJ+IK6t+ zkzO9c;asa%ZwNm3)xi1vpTHZgi*+nq=OU(&@wSK`CKea47O{x$4<;^NW_5GjgW(OY zbPKsf;mLeuE=0*t&-vGC?$|Mxl@NPXXS1Ky`Gz1OgUzA|bR``Qhm)RRm3c{~&Or+J z)9`+nLiYK*S0|=mi;3Z+2Rd6vm{U-T-v%tEDz!twUI@R#)J9gJGdPUrp+jCpNfH%K z^Ve>7<+94{!zmOXDFT$PL44>mIWCdYqdBg!M8!T+YORObT$u7o=VktDTFq_iH#mWr(Z*W2qFpwNrK+L^72~na%k!g3epn%5H=$AX+;jN zT|X*^?ea@2>B2x^tY^A!OY{(TAFG^DqeLl$+kna5R2H7 z1;q-YR;LVtX1mv6OZe$2A6h1c0cT>8f+~Auh=%x&XiF}t_Vz8K&#Ng*Ig%FAuQo|? zj5;i|CArh*YbhiQs@~v#fmJ54_pO; z*IwT2w%0Z>Z(cR}7E*~U8F?$=zyHok2&HqMf3}%ERc`ave(&+((S*Nvxn5?Z2gUs_NE1c(NKrFyrc{uBw3i?i2M=m{FlB zCrcP^-CXN9)j`Hadn-^(fPRIay}E^QNm=9o9&I<69sZv`P|YD2RX}sa%6|A(ZEBfEiTwFSfNIwd`t3s@ANmA)%XGPwv<<63iH|7i8@cTPE_r zSH2}*QEF!dC+!&m4abavO|2$4wGp*F;mwP6dO=yA8sQf%)}=4cq!d~Vc|{x>4OtM+ zSx?W}m7_tfMU)ifl3GjEJ2!7EyVtF+)hql$?eJn#8H+Bh_-Qsu^Li;dP8~#>Dn68} z?NX@~7*OaLoltF%7|ei&9$O-eXJ`AXK!!j(v`5eB3^;Afty)=yNA}h_l$e8?#foKh zPtl=iczhrp{Th-^1VpEvGJ3huPGoAi8Y0e&F=6>!dpHQva43b%?R(nOL#_{!bKSYSkHaOlZ|{ynktZLsv=@f6=S$lIFILln zeJ5&VzxZ3gflmBDup?Gf#UjKyq(%AEmvWTQrnXPm44yPUBH4G(v+sui9-9b@o_!Zl zh#uwmkS!PC_~sNd!1@#Y!K>{hwv?J39Y_;1HdzV8fY) zh}dv#=!L_hZ>WbSQrY(c!TfI3uJ+OQU>?&jf9(V&INy})G={QoN6%jgyvIO&NTFuqm^B#%4Efsaz7j*wo(PXc0x9V=~+~V+Q;{g ze%%S1!>$Ta?!-(kAqu4AQE~kDrk7<_JXXBqJgT+M`I-nq%K=NI*d3< z*vJy;Gh+$Z?OWQLHEnSRDtm12Ljg`uMI3r7G|-f~^tb#fu4ZP82o9M}Z$`vnbei8w zT;mKu+o^Mvv0S^MvM)J9+J#^8qnV_X0q1KM>j&VZzHv3?Sm8r5rLaLhs)7(f#SQVD`l_J`(<=HEO-)&!Tk7?Te_x5&S_z?kdeOr~&a3&twU9I@P z{ld(@c&yeJGzcZn1aVRu`|1_SU0Z8UR&Ffuriu@*94kFM{KXr}ii=5Nh|`{u6w=ZC zwJfrnm!3({)rt%@dhnNT?SUOZc&(K&VYq5td7IzqV~%xfnxBu~ zh4QZ-Z)?_uUt1f(Af9aMlQE-Hm?W#LRzwPj$ijuc|5{i1lVod$v(IJ`)FV+p%u+&`?Eee6l!C zMfjS_Yr*m0$)aciK~ijm6OnHYUpic^pr0*05_YV$LjRCfB+Vdx&K#y;AvNSiy3LSBDBO!LfAP9I+Y*lE#6p6=4 zfmus8udb`aiuOYpq_EYKT&G44J<-6Z(T>yER3UAeAK6o9kb)#XkLghhZU=EmD_B7! zOT(96m^pv33g{TVvAdqRyIznlt$rzQ+vvUUTJ2fK77DxZv;1$%`qlP!tpKvdKpMX9 zcpcVDwN0C`m-wXl6avRqF0V)SB0?>=5M8IE+f^MM5B^>oX8^@%Vs8vTGx+64qX;Tc z<6FR*m30P15Z&myVZ-Qi{$QU!!#JU`VhC*A)z;m5OZ6$x5TbpckTT*#t>9?5Q-dcx zdeHf)O(;Uq928q9^*5ODeLwWDb*Rn#7gCk|1ZPa6*o+xB=oQOGOSi#LwmaVo9n=z! zxd}*cW?j7kN}_5)TgT8)=bV+U@`;;-iOV1aR7#3F*l1*~@#7FVy}9d+@V$C>$WKR=+G(@b?Es7TM(j+R3<(1qbpIEPz8O)rfSGzIvcOdbOEt@5>D;S zl0=B`omXoTPXlyf&<_>5^Ar9YKl}Qp8PFx|DfE3u_cqXk;b9!;O-^ALs9t5}>gFCn z`xH_e3WNtSi$S1&GiSw+`8QzDq9dp4&(}dHP(BP&U-NIWLPtFAnaanO!+I3Iy={RG zq4xq}CZ>s=$$A!>&}~I*Nk}LTD-CEfZ|0V_NJcPDH=dy739xn&yUQ3J-1_&lvGoXDA<;? zkKia^WR&3vo>=_gcrrN@Bv$kvu!r z#z{6xNhNM+FL8992o(A+-&9K&Eb4kZ3OA0Q-N3AHZwuh+6~)X$+IXVtA8kJo;84)2 zqJ3=T@qIO(>o@7Rw4~l5)Gm=scbt2z5`KNb$_wo+zrT2S=I`HAt&3NnDm@4?F{5b1 zsL>0s@1@M`K?-l3*A<2+);L=9GG8dKccsB9FT+U^Ez%Ku8_nz@V9QMhZmg4q%frI{lsLb z%(aCcjpBLr?5R2hFdHP*((!{uLImj8K5=h}&`HA`TVC%MwaSQi=ob)!fkJdZafmJa zH0YEp<;Z6BTB>R|jh|^ss5MG0JO#25;wa!uIW-X`07yzcUOh?%-CJ}q%t@y?cof!MT7woM z0%3-Dk}{$|#Uf5sC<;75%%Prj<;lZALs<;iSXAo{uJ;M13N$Lt05*YgVyf+KSh_)dmy)c1@ceZJis&;lv zU|Mh!>k(KP~kjqN6xHx;h3ih^RHeld zZ*@bqP7iT-kDnjmhgn5TJ%`gZ1#>KSz0BB#m=HI$a%eN>GacvNE$w%0u#x@qm+DXS z9TSa;s-Z#*rs?(*RzevPCB2yfo<3jGh@s{nGjc>#uyJ*{H-swFf_&pV&Fo4Ph7^0V zD_a^Hx*2dBKv4+dVd46R$kN>y8BFaTs?s!YwF$zQhH zQxfr$;E8kfhz*_mzk5fu`jXagCNNYE*}_6lnJgq(HP}>1Bwjf>T0hdRn)f#om}y{T z>*`AECGDp#)Dj#0Q6)#BLE1*iS6-~Z_U~sAOm4tWWmJ3fetV8#h^+()>Z?6%TqTl_ ze&wFjzdCxIN5=m6zFJcd#%L>F z+aK^vF(pe}q8yzpokXhd+5!qjLH?KDn)%P}F6;GHS9>i^TX=#2C;j;f6^aSJ#`uyAp}>gs3BwqEZwD(2I=L7Rdv2= z@68*lIaKlgYr85S;;_3t9D!NW_^e$?*N4uqCI5Ff5w1J8)>(k^iS}EHQE1C*jzOrs z`LGo0+G{Te@VRG;S*x#Xk8FJP<*H3&zP_uviDnEL4CudcZ|xm&Ni>KUgv!78uKI${ z$@TzIwIz=QwbRF*Ih;~`o?uq%(59z;`KIE*oX4^>liHJK>!DFZqxZ!ljlXSFeQD?D z$uEc8NHpHJy{gKeto5{~IpW3*8|wWj4Uc2h_1~-WLjI4l`s>QN`r%Y-dt^^NBSsax zcYF03{;X!w!1VtuVQkulAV@@FaRT zH?OWv+;w#H{v$dbRaDvj&3Ef-bl6U`*Y1K06-_|D@LKI{>F$*2(Q0gnSG3+fXCniGB5elEc`2`A9tnr-9B#ms10e5+WFAp!2}Vx|0UizK9nM5(+(0Q7LJPu?yr405dpleI4^C~7(u^7)?Tj+Gp z%J;L-G0^M+0uG6z)67H7-!gvZ5x($z4WVGgvYC6fmsgl=K??Q;l3P_4o?Kcx!w`wK zs|-564nd)pA)j#=B2U2exRFhix>WxSzRrNT^K`b3q4l|d zAv44IG1Na%=vF#?0(lf}uG%3M!UMCmYnr&g)hlPy@Kl-r&wDM*71B4mdIfJ@;)jhN z=tF0V_~Br20c#O|l`ye-wUB$*4;pXbCuxUxQ%&eU%hC~Xd$*!nx6G&NO&OYtbAq13 zH`JWzP89|Ziv^huP7twWirmpn*iV1rO55|JNTEQc6j{9*%61?||8Vk=vd!dv$Q+`r ztKwcX^eI=|NG)WJ2#DCSs3ey&TmAA2RldIcY8AGww$IhNp6(B}%>OEL8&AHWb4G(L zg-+kHW;83!6y({1qbFG$QDW=U^vt}YL^xe_&U!OtI9*>qzVyH z&X!;6Porcq2U}#6FJ7z%yW|r1rHeQXstqlFleyXB)Tsab?y@53EhpPU0n%yto#{sp5lc~~;8)yEzKdYQ7S!+P*!QIt6EG7X{AeHpeo~_W8K{lvh zzkO>xm*?#M zPv2AYtXQ2B;SE*RztVF4Xn3WpS5(jHBelt0ue!XJHu|arsKTtAc#=xGh}obu6b*or z=W6Wrg#d+wG|uZ9-7OBu;nfOe6v}u%a{bJc`zrw`i+LEWU6*iPeo3Y7dybb9u}us~ zaS5~qe+8%Y;{z`gb11P(gY(FqtV2A1+LkS7NZ;`}wb3a`2KhI3S9$B2Vi1|0b^OV* zqZetNgF6qGp`31RTd$rdA22fUPamlkDbq|tf|dc3y(*WBKYMY+;5hyZk+Q-!+qd>b ziT;Q8jDqQ0D7GE7oaEw@)iG7twPa~^1aXH6{)eus!i$GTk$mn@4S1Kn%g5uEtP@{f zyAb8!o%oo<*6E*BzJzhCB zI;nMp(L$91D~8S8+slDy7I{V!f3?aNUaOM=Gs@xd@T-B*7Ix!O)wVwyp#S@)E0{F~ z+T|gty?#bx+dd90Rv7NK6*pVMpiDXN9Py?;zSa+9L9}H zm({!53dCP%YuTDW58yXGg@t}O-ru-obV;YgdFp=m;*nZ$!($};om<;i)lSu#rXd<7 z_LKm+@%GT}5*Mqu$|z^z2~KJ;=OaW4-`G`8S?*X@bashoJ6oGti12^m`O3-TDo~YP z;Hu`+36ul6R=oeJVy4-7ar|suLt-6bLa3d~8jHBD1&z15(pai%TDxE5wgt-kAs7uE zDAcOWcu2oTwhP{|wPKZ#!?C=*m~MhFr|{nGwVkjIZI%t_brv!z}zU5hdLXMCc1%owG9pC_-1G^i+E_crZumZaZYoZLzO&8CpYC z3dH+ZQWZ~}^=U}w?3nfhCRVRz<$<~Avl55E@P<6|z@#X1u#Ply!k=l%1l*Tr{nP$% zeQFvLwQr{(f90_7)vGhJ{m|qh-U(P-z*@v#VZ0MAxNm3m3f^WDB5wbNdZrmB?uhf> zAm_otO{Xw4lsVG`)K2K^hh#G+>+`CVC+2fQ@y-d|bf*kCXC<0zQXWB*tt>?;+Z6h2 z1J6(jMS>(3g3#?vywD{=7so%AWG;e7AnM$)A#w7(?_`~2(3PglL7L$!|6(k{DBnCz-@~V@p43t)YD=9g$kYK!%LJGLu$Im7MLiE%o88-l(K! znC(=N8Y z*&aKh3SEJf%0I`yxM$Ba6KdtUGP)W|8){u(>5}>c{xhw+sS6L0IL2+|4_sYM$Qx?a zwDB4=_nta1T90s8p0p+>2zgxxJ%T9=3Aa)P7PtbDFT;Li5q5)B7VRu)&;dwXI_F7eAZ6&)Pn z%(>5s=;@;bnXC9Wtg7$Xvz1Z7C3P941f`34Z&3s?ENO_h)$gY(mEFF!76N|rp_)mH z&&S$RNVKV~mF^Z{TRtuStyim--B>}Q@2) zKXFd}&0A_^0hmUtShbaikGHD?A|A3IytX1^oIJX>oaedyO&jZt;NxfOw*qm-1SP9h zRwM;5-JKIt0mpau*AuvWsKe$49)h&}{B;#K8i2#bp|)qO>Z;32Hqom;NVIYq#Vb%? zk?a?D*2lsVob)NE`pn~_hsF4L?fNqJ?7`}Y=;0oO86~GfvesN_9rDxu^o6oz$Z<8N zRh|hN4V&?0oGJ`@%JtJYi3kRRtwvz`wMU2^QW1}=L++7%Z~M?b*;;LHtpl8tJ5*r+ z|I$rOr1c%~Y=#rH8uF1F%1O0}hCL;W2IWa>FIjXB^nyKC74U>VYaEa}0&VtdE}6Ny z9a548;x{1E-tLb9>^#!x@kCXgyvBCr<+a*=vaO+zbrOXk7Z7k3D{u_a70ex~w?g+7 zL%W&AoSSt<8iqJYr#(aj9&StFX;&B^RWBW`*xs_S{%oW93`I6dx;9&up1TztH1f6u z**^4XP<#89(cikVh4a%7*5!qMZ3*3(o*`ia`W50pcLUNcr%4uzZ4*l#nA2XItP2hd zWkufoF^dJELpKzEatNIv7#=F;(D^bev^sGPrXK+dx=xJ}=bU9QVS0$PF58gp^x%WV zy$Qj@VTS=QL7!N?f-p>r&#JIv*43+dFuid0lYqJUWqvHKUcFF!+bLp*V9*UUINX6$tOf1CE8}bZmO#)2prY8rPSWK&O7|M{p7iKlxIn@rQI55qR zB8M@v=~bDZfy4YYZ>TNh2_?Yjd)g|`M3uuXh^MzvA)Mx6W|l$s!9JU?n858=H=1#t zxlj{y-kMgXY2in#%v#i}eTRD97CYx~?JJd61k)t|Y?;+Jh=MWp1Xw z=#2JUx{F}w5#Vqc9lvx5(biWM$R_#uAO51!OKO!d5V2RrR!vw^UopS4{lZ-u@@u!( z=yfRnjKxcq*J6`HweoO#$|<3rIo1g#jH>9=a1O*JTNWMA@7P*II@NAkHCpab%M-E; z#q-Rpl=HxpDquYh6KBqzPNs>gLLtsBXRO9aG3Li^a>mOr#r zr1~z{vQiE_dC#^I!Ea9lg;W`8OAqj=2g?Vo<|nt+#zWP+k5=9#bi{wVJ&KYb{L@G4 zEf~G<#hsNlV@~tcax!gH{>H9RqY_eVH*KsSA3s~~(>YX2s`g829qLQZmwPtmxmMDD7HEyzE`qg;8K|7mac5u z*Hl9;T&zV6=^3Mp@m8VY+@7zY%Hd6$YTY>u_iV4+EH8Xv(+YHYc#By`^$sH0_in4M z{K`wkz$@M+h8%EcpE59-s`_&IWtELzv8payAGyB#r&hQW)pLCzfkU_M~FMGzxaF=c)N4_J23PsP*~&tiqUcs@P{mg`Xybj z7K~n(px^#>v7yk0hZ5H!C+1a`kKWO~wmps-Z;1nDoUWNppA0bAcYMSU|9g`o0w>Pa zLb+O$RB`N@%PZ!H@k$jsV8~u`QiVt4zp=agjH^M+B`h7W+tmgJ_R%xNY{Q@`VF^$! zTV98xq7%Ytv_xTN?m8h=klHQ_DHxp5Gpur`oWm689P(rj?()jhRrV=#I-V?P&9(eA zL>%blG(o|rGyW&U4ZqjgJHi3BjjL)ujQLz3)wiM1zOM1&_!kd5I?n_qH2ethPFTJA z;fLa#5cx%XAF;TAwTK^2%zvr+zFazKcvE^h=Uh??(PqLrMqeb8P17 z^`#`}nui*cMXArdhQOKTBmoE_-m4&j(ze4*Nf>%z$O#VFG8>k+CPDKo2J%rd^z@Omdt>*if`RO|y4^*bk#2sRe&)+)_*7-R%eRSj6Bkg($=jJ!z2y%pv`8EKfZscb?TN zD;1}K9)}Xz9cwGOmF*{4Wt}Z80-2I2HR6Y^tKso;*S1ErDhjcwveAFw#E6K&Ca@LN zmUYJ&8`wvRX;9FRGM5i*wdJsH?5c+Xcz$X7F~sE9v_%ylD&(O&A_S2br6;OvagVF^ zTDSXXt*`w*zfxbWk>n>+4V&K47qxtoUoLBL1`3%zb-t|BvqG&=Y)MKn>_1r#BBcvz zAl*3g|N8Tp|LUhoT#x@x->51MLl!&!(TA!#e&(8rX9k|y|p5=_w3P?r($e zbbagU-J7bGGonGH_mK!FsoK{POk1a*R?j|luyhi{&#sZ6DiKzAcu%dEgN8e0>Ltqd9L>_D z)plw*bH07_9bG_Z_#3y(ynM8_8{4;sE?lhp%SWyswTs&L&r|%)yANDd3QwP#dFg1; zji{Uj6`a#`1xS;5OM|x9Q?WeSA?-rX z6U5PjL0=g8342VZrGd-Hq4vWF5S}1D08S&2paF*HirTH)h(u>7$`Nr!U(?p?Iwyp4 zR-lIy&YZ75iRnsyPKYQ^98N%}xi@XdA^obRMS@OOi1_E99|fd4*3l_LdM_(XtR(>v zSNrfpJmq;}G8bm$&%`DW1U%izY5)7-MAbCs0uhTpIOKu89|V4&ZZF~|0gDS*i}*3Y ztT%vJFYY)qKbRG7>PE6~i^H4cT>9s^p?0q(7|b(*(C2qvRezj2haD68CzK2i&DHwM zsY5v$vQO2J68>%_cy7E75~Ace-!~mHrz(4x(9qF2$z~co0fODr$vp+WT6!|Aq4Ld2fGsn)>t4zc-Fp*!my#72+j})lP zwJB^8T|9DBsO+c>3`Cm@2WN(#s{UdWV)Ep2udN4Nv1;bC&s2zH)F2lXOh~^2It{6f zeo+E$Bqh6K`6zmye4yH%bjTrxV^PDi1c1mc4R}_!k1Np-C+V-SvmB=@MslD)8N&SPLs!vv{jb?P7$Qgd2eSZg1g-9E66e3UMic#k^{H z?QJN6$vsxEm8;{g4$)RIY_GqO7mB}gYgKW~-uCrfRjz^8REZmYoXVtV)2OtIbwfzI z@M!GKo65?;JSVuM9%VAxIZ(c}btv@u7EfEvk$m3TekA|{aeJz^w*0VvON$k5sBIb> za|;ws&}*6?utadusjByKS!|0_Pamj9ejw4H&=pJ&=*MXTJ@Vz9qcsJp!u*w&Y9S_X zl|nVYog)OL;u~7i+%G8ST?KQznxLGQ*Ib=Q0sVJuojH2CUew~DhNo$-T`Yz?tOOdO zkZ_T|etlht@?gt~WhG*xoSb-`g5fBn+sc&!btU0=^wiMiHMSQsxkm3!%h_V^ytr1qbj`NOB$ z$<@wx0msJqT}MkW7c2UWL+2Ec2VSUG5nbQ72l|4-Y|3Qs+*XT9nnUMkFf=`6<3>Hm z(-v!}t+upI7ZVO>rbclabDU4R)?HFhS`m+u5L0$rcPR__pBN@Glt-I=YX9Qp(QCY+ z#5&|mOo0!2kn)GyPoRMEH+EI?snUI9jel_?qAl7;bM;FL0=qlb)swTFjP<*=&HRsF zuQfG#Sk(3Z;ND_Wb@k;VpH;;W;8CD<^Qv0?bf!>+V$%y@wqYCoT`(;|?|5)`Ej7x?oF8vla3Pp0O7r04xw=X(Vi7+CEG}Rz;>QHDUdI=@QAWqUXHI4^ zG%*Q)?nd?#UUgLZ(QdaxhLq`aC;ro&qnsqQCbfO$1aq|-hF>}xIz!w>>b3S6g8ZT% zLwFR-%xWsuDA50wmC11;sVWv@+9q>L6*wGzbz*4x)Ogi{pberSwO?)D0m&f?mEe#*b@=8;cX2vUrmG+D|Z9q?m5K8g4 z-<6|Z{$NiQ8&BlehD7BpS5&r%=hn?t#epPmOzdjk*`Wm82E9eKGSV^-3D`gUZ0!-r zhEozQnTa;~vjMe${^*|SS(#pV(wm%@VaCaqj*Q-%k^tw2?UkQ7ICE1=(hQpykJKX= zL}-w*+T)3So>Uw*RLYUWOzU_UXY85C(uUx~G;vbOl#>l{!>Xb?pdkgV{=~hlneEvn z>FBp!t?Zf=ASiU=%nAxCMvJ^L-A5rJi;e=2>3hm^IzKaB`r&YOYkL(mI*C*05VT9xvZeK&U=G#t^S}RUtvue` zUX&wFvjLeZ3hBA;#&TG){p5jigSkAUrW^)M5H6QK{lZp)AZ{)-I>Lnu^NDkni(g^D|eMV?c8`aARF4stJbl>6sNvM_&qa=t{WzSjlFf+L+)2 z)O&*C@z0ZAY~S9lPHC&6U)weFU)-0MOtO?P)B~dLSajMFe9yKbe(Z*Nq6eGaEDu}TIT@XCm z^F+xAQLC}E)ktX0RBgK;pr|eJc3s`EwwC5|x*3c2 zKLA9b{bPGavzHuZ*@!BWI(DN!6tXERm_LM)SV0^P!vvzlcHrb_bqqRZ*Aq(iaib%q zvxlKqDFhDn zn{MO6@pfdV5mj$W=l5mx>igS@Mf|v8aRF-)e>LzUxuTgCydk<{)9%gX@C1FjX*ryW z4zoVi8~$u5Piue)qQgGh$YGz~nDgcS+A*ySX0W= z8hVus3NUA%O>Njf!QtA$_c*eJEyxE%au`9i&>1r81(YZRQ%1Brvv730G^VPim~Ev4 z=j)X5-%h*T6e0zbynN*KIeKKX#kXi+F;bbWszOZYWETqb;89jcic)qr)asJ7Cl08h z%|5D*o*6y!6`i|}*2_wH7cIMOG00w0o#xn==dB-YfdU?acWfCs1I(esoB~;U zCNT4P+uC|2LaMKp&U_6A13y3BRs)NF{M~VTY9HD?^D8&a{F6sUPV$*58ln(U?z-oA z@zeS2!OGax;>>Gn-`!uGr8!s;6F2>(ceDuQOFL^+}X zy!UitTSK9$x~xSY4N0gEwQn|Evb^lxv!mY1d|$rt)Vv~9qqJ$rV|TVhTdg=~?|tp{ zL;R$~JZp|wIFQNHsR|^MI_u!crlEc8yvD$e`g+vKbM^Y0wm8d-z-a!@?r!02`KLp~ zL+!P!_g__>)L?FxS)K?TAJXIt7u%vnTaJ14L=n?W;vgX3+BU^DO9XKy@~IMOfdR)teg42f4(AoEEU(?r|()tanNzxaHO4YdR}Tv~s4K#UZk z_9`1%4uM6=AwP`3ijDx@{_)eb)Iu%sG>VmKU^Arr;*okfb?77z=MtigHZjTLb#Yh^ zx`>^*Q0q96+PJEo-ol(QTo;peS<8wcH_&VRG^6>#6hF_l;dg!e8oDZ`vDso8(mFS5 z1)+YhT|z4k?aw4mXZvGkYWy4d8^@uh>YDX6DsVErx2>;5l_~|>XCANcayv7Dvfl4R zr>iXrwApI~KR2}{B*5b~8YNr>bY**1(vPV!Y>YafaQoV-LU$T|@Y*6CJ6pFHCL$gv zWaByhMEjOauU9QwS~sZ^XKS=Og!q|STfH(r_NN$@)W_5e9;dCVLE32R_i@C8v6vgO zdIhm=UomNd!C%rp4k-vG;sj@pJmgt^L|vbU)1#z&6?6@q0#UVQHX`8TWc?Z@!#h`ZDEj9?Kz z-dJ3~TEt%s{2;E&Zw=-^LuVH5*j$%&nS*J^G%$aCSLM&sP&KWsAF47xfLYB=OARU0 zN`{!V38AQxIdqW;&OBG#rx(+DI;GRi4*LuSvDc=M+I5$XLN||N9pUMmrdB4zfhp#2 zwlvhH+#8kISZaqevrudkKI4%ObSn|5s5GmYU7pRU7e zTN*R;M^%8|3_Ek*fRbL0PKTkyNwePWZKo#-y}Esv)>P8O)M4BJ9%9n`Y@SVwor*8O+X zAa0Ag)f|Y3GNY=T zi3+w)K2Y4~i38yLbbAGolRz^;U^mgoJ6U2?n#YejDI3YX$qL3#)@4{%g^eEcwDidX zmGGx^kjG)jVlH#zmJ-au1W1n`c(D>EQmSlvzZ_^^;8ytg_OY$9a%q(@XekOMYWJNe z<-l&#R;TP9Dd#8t`aDXD#=AF{hzL7te)YCGzZgwav#z9U@E|HtMfN@0M(bbHMz$%g zHKcC0J@pT26e9D}G)zux-g zm1Fh1hQ4F#$eG%U3HqNrTY``6t@9Lro*FU1 zwc_O2VgO0wx=ZR>+TByjAC-wP&DX9k>lmIsP=kaaZmR+&j5IBUP(asz;Hr8l%sDG= zOPADhnnJ|%;Zv3KOE{IqoM5`rq0Onsx~{QTr8$~>Py1;?rzhay8&CKEPaUkcz#eM;HC4_CM92K!eyH?l`)Ar)nOcOC4`X8=P zO>Lb{D>D3IG;RayI#d}w!J%`-(wWaaR~Jm)ZeCrhIWFv4m&n*(-rg28ypUHmSr>GdV^*b9?HlR>=;gzFB=T#yA7`RQ(o!hGI4qZ;4I#3ka3o$*hHgzI9+FMh*=}Ev6n)&)FyPwIW(}jXKzTOn8Vm7IHh4= zF{cKEA~@HquX5sVi&RISMxw`0pfkbmF&PRD_rn52FzDh{cD+4Se+vw;uU>Wi7`mPW zp_>baEMKD|l3l`!7&wDfHfy;7#PxCDxru*vE5c9O|&0_vo{Xp@f zvKuU}Uj5`?aRF-)i+FQ*%U9>Q1ZQ2n3T9os3Z}L7J60wQL&_UMj$wK<tdm0$W(j5EfSEg;a@#L0G z^{@s8N;0MX_fORu9BMm-VBl~V3Wt&e4ACa*!1=4U)o<8|LyV>>1&Dac^mlO%r)g!e zy@iqHD}3SkQmYD^gzB;t)oMJ>O0pPI3$^Ye34d2xm20Ur28rUKmaOWaU%Ih!ttSuE z62{kejVvZHv%hD1iP!AErP(&Mlvlf2UTx2=Luze#+Cs*1<#y*sx5WPN(3YTMH{X?r|T#$(LuQ=DWSf~@kNKURApv7#UXfAVal+2Bw@%MdGa zeu5d*VY3mj38Vc3FO(0egedeR)jY>38dtU-)8xR`HI>irJY2P`U%XhH>Xx*{7aH(X zN3_TgG0vIBlSh{DMDt%8zaxe%7RjO*$&c+V%^@1z^)q?xGeFdio)2p820EladVcMW znScFwMTT9}FSg$<{4d{E%bEvHloiZk_@(Eo@X;Gay+i!VFVu@dv4zc^plhRj4(;^< zDQ?@ZTUo0)=!Atr^Z*0XTiOS+5)EV*y{N6WwC7gp++nhH&25{j;VI)urwbI~SKChq z;*S*)HO$7#d58y3jqf$BrN4eh3I3xGRn?~+tamW;8f+)&$-{2B*WUDA87D zO*>!GOd_{ztlt5+;?jBvRAkWq`ps3vnSCef8%#H}gMiUo0PlCFWSC7AVQ*3(@ObXrGHOvsxI~C+5bu%J$PkSx5daK=`_O-_kIRKbzxbI z?RS2!9u;)C`=y&ooU7m6Ke`mt`O=a4PCRjCwTVN9Q}m=QKYjJ+ZxS{PK^?Z!r+HdU z$cwaLpv?x@6znOCW^u;z$ex)?msJGB+=;0WW>qdwt_JBKltig3jAP(14lA&(pmm*y zCqGAsQt#up(Tm~duA4b{q85M8oG&FzJbkeCrKDZ+6;dYa@{TQDp>$yAg*xNLA1JUH z+pjO-wWkQ<6{LguA;i6G9T`!bVz!fK%sY`(wxJX_OoUU$uQ^8h{u8eYPKca3Uk&W5 zDhXXjXO5~ERmc-V&>74moyZJj_IT>t=*hlmi!f~65TQ@k=5Q{lA#r*kOznxYvo9NU zOsi#U+Vh31Ud`p?Lh#)9@#+Z}+jK5a^INw51A@zM9u=GiZzpI&99et_>n8(?3s{Tz zQR4e`!TLeqZThxw@yZ$0il#O|Y&<_+)t zecCz=(@HaGdho-nbDa*gnOyer5TVC%Aa}b_md4fZ;mWj_j z1eo#k*&Y!aL{$!zDG`A<;4rm7b1w&S651y8WKYSE2*gDonr&*607UJCW<(68?D@NQ z+z!#){X^GPAtHz{90LNF?!%DTZJ*;}j;i?9HS;Hl6AGR%=QFWDk8pO89Hva1?D4bp zjpvjR)pB5C%iB?sP|>;Wk|NSml<~7{_|4a)uL|;BMCr8bHcry{JKw1{3gJ`Vs9b{1 z_^g)`WP+L;=SOJSzMRBVOs>M8EJGmTxp8B)an*{7?SEr*LJlhJK8M( z^WVL#+D-#b=04thOGhwNiFa$-`u6>DIM;sN|M#D4L#r)Mq^GiM1ayAp@rq#$v$m9@ zaOsL#3$7k)9jdIo4jFPg_{jAY_Mf|^){oTU5ynK|51(pJ+nugl^zuvUOgz+_+WW4o=hv>^P>Z~q+QW|l z2F)RYpSIrnD8xz8S*qH=Q_D#gw*zeqtJT0P_hZf&0Bp(x?HN+Ib8FoWPPMm9AKP1< zP8{79PefZVDQ86(U2_y5cIgpA1^g_Au3#??YPCWewGNN?T)bEX+rg8ydd^{R#dAH6 zKWuS;pLqlfCsmiUk9?hJFR-J4s)}>##GnRttJU5J0#)3DE~ppOI>ZcJoRze98l|%} zcK?ad3YxNgFO=u){a*~jzd`l_q>#tGfs>?$4B#R^T-=F=;h{ViL$F8eEs7f*sze)H zjUyYQ*`_f;2=rmqten$Cl-SM8^9?bf!N{2v z;hB#4v`aq(yji<%xOxQ(S2(}*#nr2y5G*cWE#gOsT!Vg8m*DyFHeFhH(>lX+VJ^JQ zh`r$-Ll&-_CvMeJxCyKqPh7gInG!+1o{)WzMUC*==9-;Em04_`e1vTx8V=1kJH=rp zRef4uLMCm%V4INbNUcqrQs{Fy=u&2`7&3bf!mJ4;XbH~h>9fGP`H`iYm(ZgepX!bm9i`pbJ%IC+QX-E9fbt8tf2*}1t@Wj4a zR){BQFD3N{?Ix8(|h z-?gnMyiyx0I9IjBN3s;&f9LDU<>aIUL0LMgoM@<3{_qXsNp|}|cSfa2neXQJ8RIZ8 zp{z<~s8$>6nX(M{wg}RBc=gRar7iJ66rw)r97^z>I#9VNA1Kt(nh+}}kMEoLe|AiDD8bL_u(!_1MkQ03}EmNUeO zpeRfJ4a=!|H_=ZS-`a0mDf|P;gljQVvve+G$7| zl=9Q{D3i4OxkEGew%*ZMFCMOEl+*%d9xC(UXRoerd&6(sTZ?Y1+E==n(|m5wkB$F{ zFlcy2NcJKl8rWF5?_DF!h=$+cQ?*8U_Cj3=AKE?h_up5m1K8q&=qXq5jllNg*Pi3G zIFd6W0ZbKW?~yYVD4QtJ&l%#`N?}{&qz#%xv0+w4Vc60Z+o86A=()uMxfIQe%?jpi5y&n-ieB}Dk-4miehVI@`fr@)-Gf_UWY_ci?VEKEny#M ze`7##kuflKtgSj zV^xSV8~`Q~Z4|N&$%gKPIdpUZdFo@dhQ*gF& zDP?0XGp_K9t+;`H5rI0t#zD@0LNlNM;<*de-NRZ{oj>Az@PNZ!{!4$|Y3O>Omt7vO z8UH1Wc<8+lo09!L;zwmS_|aNl#5)9w3s^sf_$&APd%>sf=QpGbH>ZJ&0>iYmU{yr~NDFv#jv z1WDP~Gj}b=Lqythtp2Y1=-#T$lzR9STW_x|hW4GPT&t$_E&aQDd*#x~WF3C^`jIVz zIp!QnzycbWMyD2Ya;i~I(!QRPCL_jfR<8hOHgq~;NSRT4z)UUuw3*6_LuRx+;R&0< zryrczwx&M*LP@ejvZ?*tbM?IyLt^E!S~Sf0I0{q?I1_tMj2;z<8^l2&*8OK6dOApl zikMF99#-%-)EBJFtF~x`_=Wb4TV%22New`uXLWmJ@=!~mqvsEvocVig6(CCR63W2GbhO>C>pk>57ZMV@<7TFx2zhSEHd6L zt1Ek_aM#g#_miu@6SbP@oJGNmfuU39>UYgD*jPz_QF!KjeS}`w@G$ak*;J14L>1O( zoX+uvWfkQ(MlA^KE6D zKxIKq!<9FVziF)_1S>IKR6M4k?^Wwi8FIVT0v zB1|W`ea5GP2GXfamNV2Ud}(LN{?2zs&k#|Fzce)8KHANVy>Vx1XzSb&-i4R}4gDk^kDuIfHh@slv;t8evT{FM+P*oA95Mmwd-I|RJ1%mMw z&;Rs|`iVei$4#4xsMemx&I>u|q9=l3uosgQLQmYB%N>W> z%G&X>qnSpj8h>r76dH}=FG;y2IjL#j9?v-ww$$b}2WlM#X##Z0&_lR*v91Gjy6hOY zT{Z>GkT3=cy$sPaT^qrPzZaUrpu0>t<*_k(uKgZglxPu3M?6PQEw-*%AzP|K34h9v zeK0I{;EZ@jU%i6R1wYXXb4>Jyl@Q}$5YzoIg?<=x_d5FYasty|#u&o6JgsVYI8ikh zrrEmMY5j{>#3J4r78kI74Dn<6{N&rY=r81+F%QpMyX8dW4P_UidGGPpS3M}qy?-u5 zVJ3wgLo3SND}{;KhVt-m8l8XGv|f-2CZ)9IiDL}{XF=%#Czw6i0I5~VLbUpZtb*>@;sOF6YYunyA~VSu4U1(VmzDQ%e5 zUOo6zq(k68m%1wG%u!{}PnmxTUUM!BGQh0%_aw5pn1|C;y za+q0%nm?Q`_JlOnXCo< zPUnf0?|+PCemn#Zf=OvqshS2CEm-DCBan*#hD%S_z)6eEYKEH5;gnwL;D;WB+GRM|)=+OqJ(K zNJ|epyge4l*vE5!$7*)5*92m)0FfdPha6AG`0ep!U@IrjG)^WR`OAn1!ZXF$y>zIbL=;ZLei-o>~8 z{mLq0W8i~5bjWVla-YSZ9m?)vT(3x?bYWEfit*6IR* z81krdOUfyKl}k>h$g7}0M~9nwNfdoyebXVY>(ZsXGHSaFsi&1s;8e!0SIR?zV9@QjtFlo;Z?kG*-|)#u?KM!}_R90$z}sV}k3BeSvs} zn1E+Kwef(z$8Grw31kqY0dC*nwkJ|j$|n>D-GD{q^(|DFL(+cOmf-vIs-O4#F+NzY zw~E!u6h>cPFbnh#*I@fyDfFN&vZ_-$kWC=2{ieIg+;m8M)?M_4^n$jEkkJjz(A!=_ zW`vv;NKv*bz*pg$tJ&r z%!WLGSI{`yY~^R4jZhk5MGRBxhd&`ClJBrpPmsm`a@prt8_+PHEmwbD2G|1VtBR0K z7N7aqk^S8Tt4w~Gh<{d=6Dileim%r=#n8*lPCJ=#)<%Jw|=C-d3%sVTVXc7w{(OP8Jya8DIPyL!$1q9dKntz<ga+E>Nr4OS*$L+F(5r8?`Y zRTJMPFsfNx8p_sR@W~A#;UrN~53G>u*4Lzne`Bll|Y{lABa-9w8}-HMa8Q)?>u{ z;{6z8Luyz&5j0D|qTe6=oxUBAMiAx%Xr@?kwt!RM3IPF0xbgKp!@TB!Vv89={_gqxRrG^9S$*qlUmJy2L{WTL={FO12tyiU&5vr%R~t~id4dfzR0!Wl{?mCwFB9*gMWQOlSiLWf3-@+7uqSyg1&?9;iuYcQKolC!bU?<;bJWr39i_DYjnx z%3rUF`se#5`)^ebVI{T-T4_ve;B(p<-m?O@?(hgFX&(QI-VF?debgNOO+i59#~gz!8aA& zRo~Ouz00Xhy&PA|SfxiQg+Sl$bx%69z8T2r0r;|3o99FE4~#SIKUdnr=dE{yRO$~$9@M0Om zVon8LE?Bs9O-$Hy5f8Ns@IGLY3vorW3ffg%exunLcG_L>y0m3P&sIw>F$~ddBteHa zCRj%Zx{tzTmtMydEQ_nskjUmeh&i*_tKVB-GF=6%rx4cVdd;9;4p&I5DP*C*0UfSs zKlxew!WapS-|#GM``+eWza9ySL4m6vmt|KO5E_CKo zVF!pvKdEsrdobql%_FO-WvJNany!w|U2}(+==YggrP;S^d*ojH*~s`wQJ-xw=gmbO zRJjVuEd2}+ALs0BHr|g{n+OwqX?S~D+d6>-W?8BNZ3C3pT<@wY17|mnS>WxL#md`t z3u&?>(U~89^Cbn(7pJ<80_cUpg0M@b6o(fDbah^~fYXJ5n8#BpT}--w^QrsQ*_YPx zWd!3kHYzjw^q|Jq7>lwf2N!j4)5<{NDJMdRI-L2H_&Eyqf}JEQ5c5c(zn|_yaukJ& z>9eAD#p|&WL9>JL*i+xjt?@0&r`-HsgqGiZZVz+)WJF4z@1rVQkI-NF1$(WJb$1(y)BCxl>SiY`4~E;?pMrqb8+gjW&|Oh zU6H!^SCHBLdZtO6tXq@1y?9Zj413fqwlX+#q@)FbJ4MRG7rKKwM&|{0iJ9_0fCvMp z`(IY>lu}Tw3v(QgVhS7IWIoA%|2xml4{#&CRq_v?WRgv-4%ZP=ApO1NHc2Xg z$uUibiSis-U@T^R9$u$qB9nFf2g$(~^H;ywGj!(uyw7ZmtiJI>bLu-Z?)LBU!ifV; z^nsvY*})^kWF^zSr?&elhZ7E~%=OZBGfE?%(PM&s&$q^taizwH&kuV)Q0Um4wm<0P z*pFP+&;(d(u=4?WWd=3v5Wc=Yw?|7ky_CA#T8`U6%ep+ze!X2K>x>hW_V=h)`$xso z{RKiqW6vILoJkc_BI}>4hcCqdv*1<9xzQBTkQu788UR=nzalvBT;Ej_fKZLc&)e&A zf9uGfTe*hzdteXiZ7KM%z8)Tfg}{(j#UIqgD9f4%h@Ws~x=I7-u{9F%6K8KTfQR|6 zkj~htMPtLUue{*tCc$9&zdyTny1&XEXugfQzw#GD(AhDu8GiOKV~=0!d>h$X&fyxW_si^8nk0#80=aDP8#(@)eFb04VYcTjstsVHOoC$H#O4%lT!7qTHU)%@|hQ6-h= zoQVJpF7Ob~8BVYWUghhNa<#}DiNGt`TAs65Z%rbrbD5zVC%&P08Rvz1i1tP-vY|M6 z-==4&Q~auV^weo1h5iSrf6NRY40&~CTl|EV%1}js`fZI*y8qGZ4z8sP zM{N2RTFr@vT6a#ImmG3ewr$VevCAmseX2|i&jDM9VLWCBJg--B5s}*BiLL2`nUHp? z#@H#DD~!GGxIX2mCTX-;D$Qt&Kv~3Aa=m)LX(qX#2{OV8{kc_6YADVxY7`ldVJAwc zhJkNYtZD%-@uRFbdzSZ`E4cyoQwic##TSndBh9@#(UfR|76EN0?3LsEecrr3pZDfW ze*$zaf-j&3MQh#`H@o&285j$Li75*4uQ1Ft;637ATlwM3gL29>>tui#lz(a5gPVGh z-7Z)-K{Duy#`$EkN`!+!Dc$n~;Tcq$ihNi>Fvg-Wpmvv#`&Hz@{sE>J(y5oPCJfKRR0~a4glbaHjyMY;^@~^ zrAg}465krX?B+7xMG1bOSF`{6h(uY2>@GUXb{&`kg{3{W4<#JlRAQd#U zC4V_w-eN9QZK5%08}bntfrH_v=XdG zBx8L9AwOoN=z#74Ym=d>A%hf4fp_MLzKpU;_k;OZEC44lA8p4~d?ie(H8 zOGaMf@;cIAFRJAjKs#z%E{gv-w7To7kbNADaB851JRNij&dQE-Q3>8^?l&NtE_EGb zEY)}-frJ)2H?R*8avlA}exww!?kQhfNkqk)I3~3A@pvJI?>*LuLon~tjPr>2*j@5{76=lWn8GtGnLP8yCc-$%m*`aabL^LW1=tNHQ z;vY3!Umz8i_kAmW$-HE1l0)&^CRFs0)G4y^;Dj&l@F>M46CT*{;w7C88u&yR31cl% zGW9v(Z854q6Aw;FK|bD&$Fc*M~nWHoRn$|Wx=RtIL~|KTKY8XdrMK?w7gU1k7YL0 z)c125J^xsc+sS2zRG*(M_8FNXu58z3Il17}WU;hBFLbOAl z&Qy5Zc_Bo#e~&#&P`AZ-s#(o{vwRf)jk$Wzvon5XIjF#e(-Z8b0NYm!XhraRGbD;& zSiJlpna%4TWW)LwMpFMd^Ia~z)30!7bu+-bHS0w`pSK!pT>*4-yqyrIeP#gSC{`E9 z-!-rP#G8P6{;AhQhM!}T9Dao1SxzLDT!;g$VV;LN2&K)3+`k-?99lBA=_*RTzwkm> zz2+Le%qBB#&3~=d11T6sXx;WYI-(p|?lk?b`Qen&!2SXMn3}bOxo+0lL(~1wRU3qC z*XkB+6Ptzq9IUhl1Dh&f!iG+s?J(Y;D~gf@7TE;3s0=9Sr_)}wh*-hE=vJ8>SPuHr zMZT8mZtJB51_S74U(<<_z(rom`1=+w?|*Q^?i6&X*O8J^d>S$usUKv`Z=bjLfF9%pBRp8_g8N4BFtOe`(v&jhCs}^E&5N|dXz+hE?=J8Bb3UBF z5Vp??>6MvTi8MI6mBYijKIS+ZC#y0%;nUYGd90XHJjJY9oFNkD#k{JY?Xa6vAH`iz z@iF#Z9&ENq_W}||(!CcizQOI#uX~W#OcK{g+J5I3Hwu~hB5gQ7l&7wFhK{1qXLoFo zhf^4Hx`T)~{`clc?6Nv1+LqR}Hj)~w8^LQq_(az^B7#$72Ikgv;^BJyQ1N=EeM4avH7md*eR}oy zVjub5`l3%-ak@ zXlss?>G*$8l05Y1VI7NM#q{U**?VOSt60LmJw;SOZM15B!%ATaN8E zFhFBg6)d)l@pzriyl11D`X2dd-<{?qs{+L#iiO4OEC!shFvN%_?fS{?F8E23z6LD6 zQdw=jm`15?S0o|0)fYBmt4Y13i%B+70uJ0(kxHR&UT~{Cc1!SkKa4g=Dj-o4Wm6f* z4SHk`8kbfCXk{Rm6aGP3l}lk~z#6XVIQ#izwr0F~OrT0T6yAH?leJLuyZKW?W>|H@kcB$i7ZL{w{r3hvE$Pmkx5M~DonrqkYnaqqMTaee`wrUCU@bav zc~!UI=8TB*AGJ@%Xe{|So7L)}@EodV7~h+4`g+mF`;lbTcRk#hdk+Ep&xf>3wR*p{j6yW^I5;L@kN zpRNu$w?8f~$!!l!_AfXF{0G zwdHYee|G;k9f4D3hwOI|Mzhu6?b|4#-UzRypGGNWWM{o!#DFN@1I2!*YtsKxsUe82 z{s%Kb3@8u=dj9fS<@_;!ECDc9+hn-!@hTK?{9Z2em?9gXbn{{<6NKP18gj>AF^>7X ztS(-#*pVkg+xEvul~&@hQ+Fa;RM*;`;8J9m84Lc*Ew!bSn|eJ(+R^a2^YQ7ZXYQ6L z=vuOs#jh%ZTu3GI*WrNxkq*Ckf}Vhs(wDgMr;vBvSQiH|Ek*#{mVQWz31 zKi#FdwzxahTo&PT3-eh}W!42=g^>RDd7BfPl>M5u64B|;r&d@7v(OQrR;_sMCkT-8 z2{JFKVc_TTe1*pKUY1812Bnz;TYS8ZF7b?JGHvKG>DB5f)$pP2Ob`eJ7D=p#X4B?< z{E?Bn=c_YuN6EHnB5b62lh5)pt5Wpsi?KoHAJF4!JCv+m`DG=Bi>k9=6nhU>LtgmA zcHUr48nE^j<9}?AsX1TB7>T04_jia89`}!ioS2F~hT&`~X<1ELFNCrAi2L)(zSh6XpV451~r#U7zaHykMTYxEMj{ppGfk zL$>}NEF4zOb6^AZLT1yVrE*i3JX}y{tSs^r-}uWSAB(+Q$N)o2S56E3w-9APYy{5F^0<(h?>55O5|j*0XjuyV&3&rhZRIR^%m$0S><9FCt7Ak z?_me_S+L2ul5RC;{*`MjY8e>8I@qf<@};Pc2TV^?@JI z;nj$qqL6(tU&!l6;~3Tf!xwRe-LU%S4Zx)L304QYLE>ph$yVEKQ8*HKVE3_~peC== zX21JcaS+c<-*5;O4)4y0zNGFYNs;8!Vd1dh{(t>a?PEyO<5qssPQ>Mae57>~0@Sws z6X_Y`vQVg>MtQX{^Fgw#H?Wcm0ZyBFS@aC93au($Gzc=;F1ISzg6qW!PP(qy{==_u zU*QR;3d>b;oCw7tp?o|QbHF=@g&%eN#segcRG|rsOYVCsq5MB8)A8^xUP8Fy65g<1 z&?nM2k2QZt!XkglpRCtz*OujnqeOXrnEXTOzX0_?42Oj?K?5}4c0X+!)HFc1Gw9(W zH#7J`YY^Mk0?N4VStTMmx5$FWjXxTE9#t6pFFYwCe6$q6mFKvFXG%Iob%~#cWojC8 z%LxkO0sav!qMg9UlW;Bnec)3~GxP*BJdUxqpBZs>M3~;xt>xqd&R87MxALCK3^2M-vT%?Fv`GvyTAs(!zhbb?Y&Z*OjM%unkMw^D?Fa3AL?YhJFNk3~$GwEdk?e7qQLy*!Y3d2s80M(^ z`sOxU>a|Yal=4^;$3xvJ%?wogktf7`h1w7p(eKEo?GxegvO>9P7%Z*`n4EC(tagrA zJ8=LqE|Zsz4I&~;_jBUq;esdH|J#t;uLES}At=ms&BK4yKci90m(K zpuYUvhWmAn*5k=v!=aeqN38b-KTZiFqfUc?)&H%YgVcQ-(`TqS34>bmVnCFaM=n-f zG&;>}vi(NhO=m;GUI)F>ET1v`1uAvtRL&ww`A0#6#YJ4j(_@{Ucl~Lq7vAV&6jDrz zPvh>Ce|zmo1?OuMtEoQnUZ)1|Sbl5}6!^02Kq;1MrgE|B&5wUHUs`pSbho~Sy~iGy z3k^nL^t&Qxo++3#ZFVG zi1NfyzxuaeYVmB1DffVfvn+B-1o7A zkMw-J?f9RQ1p>Q7FSs?iwH!*S9Ca55eM`qHV+WvQIh!8aojq;ZHo48A?|6`Pdw6e-}Qn5XGV|8LdZ4Y_C$Kb$Y zXDR!5$`#4Dg}X!YxA)x|zhl=O@U|5`Y*j5XF)Zc025G$`Q8nre>dm4G*Liq}0 z`%_;N|7P&j{(BmVM=mV&2$+6})tfY9_sM1oZUfx}dcgHmgs@UvE?1T$x~CX6r-1pT zd_BMmM2AO2s5@A86hKLbhb@WWf9w+f<)9rJF3p3&Cc{`OPaN{*B7$^3?>q5lVqZM3 z!dZhI{%icY11G2Ct5q4T;lV$v8ljN~89uaI+{rlF-I#^&<}l1e+OfT99Vh{^ra<@z zf2F?Q>M9jToVi2OoqIP@Pf)G+XrRt4a2BAWWc9Z<>naQWs$D)IuDNR9}kSE+uWQ zOEyt}KEsqHV9YYoK@2`dN0r()7jV*#Q<>8w^MllY-^f>WR)xZKLZg-_{!`O&=PD0} z^6R{Ah}59NDJhwN z8nRvZ{fZnwHgw!x(65!~;+t8PqYWuv)v-kGqOb&s+fHO5t;8Qk#LGr%Pe@r zH$fSB%cd(Xa>^d6&(8Crdy0ia*U%vTEpZBnG(Bym1>)y5iHG)}2+~v+)&i@3a!5_@ zxSk||M+^tj7iWF0C!teyF$O@11}0sq&J6?}GfI5mv|TK5fh7nvDe6ehrTo#)fr~6Y za-L)JKTOaaSo6?~CE{>K?T(ApvqIm+P;#`Cck;l0$oukd75_nmXYav-W6@$rb#!H{_bEfEXPd0XVO|V3nq@!bwd&RyR-|_$|j}#(JDD- zemhm*(9s6Kt>zPy!5 z2f}*IVYud2+HF2ZSE!hK1ACwRSBX~SChU(Su67;S*b495R|}Nli%GGN>Dqpz<-K&j z$NP<(5VVggB3w=U2T!cClNQ|U!NY{SZT$KE2f1B%a2?Zi5X*T5=X^xe*m!JY*_ zzselBte4@bd7roH2%DfYC@u|I5%|Ii57a4MCSmd4+ES3L)-?;h5Nx}Yo)Ej^3Wf$9 zMHdQb)wrO!@~h>s;cx^ziO=ZPR;7bB8>$7?7L^GG?p}2ln|Jj+=GNxg$_Z>swe~ZlG+ELX(;F(0e!V+@Y($m{UoY>B$YP|q zC0H@Ig|sJ+3H}mY2fey^8n341xkq0j!P#*FMp3^wXgsCZ2$L8LykArp@I23dzhx@C z27V&*KUh6Ku@;_=Jzbort~)IfizLYNny6*?KBN=LUi|Gt%I%jh8Fv+DHkggU<9Pb{ z+^NhEuOOt^?;vnz>h5wb>AvhajWi>CNzA||p} zP1a0$-u7_;*=iJw7THzbxRIuf&)jF!7mv8@avqANV==MJz=5DZ%k8=NBtvY^|XRa-1^~o6x_4S~)x>E};p$ zV(Socce1}&N~hOO8LTgJSZjqH5>)f8)s2|(Ww z3pbWKsb!K<^@@m)VB`t^l}gj^;k#%MrW?OtX(UX}36GbnmT{PYyo4>)=FaUQ;+*qH zw_>zC?PzQLOO8Fm)OR@1mgWWa5i(`{zb%dhj!P_G#}nJd6w%z2)dc8QHo^7gqs`#J zY5+XwJ$IVj&5k*93jWI)HiU2YpD;`qcX)1sfF~QVXTD&xMD6C%znBPBdOp%kW=_}_|eR;jx6a$>gVaMv${WS33F6ukV z!W&!EOOmFy{g2kFj{dPgnycvG=*~)ldj|=9%Pm9aFFFUA?g^SS>q-o@h0gYNtcV1^ ztNVWEs)L|vST;$HDzwHWG_~|I(woV`F}}dU8tLZ+9)2+G z+Vfpy6aL;bz~Yr2u|3K_i;fO$lpcMprDDT+ zKLg&bd=V912+FD!JX9ZRf?AOCH5#v&)C~SDVSch8t-O7`;vXaQ zr*w*Z&R<4X(nx|ztCJV6q+=3beR@6F3ZBoktsS`mRvJHCHaQJ^3!=G>Vhr5uy2s@K z0xQ|}yp{Pdw2ZhZq@JqJkHEG z9?E6scBE#?xJRPl`=cSiSr=}68U4SV>2C=FsHbd3Rkcew-Fj2K^oljCy3KO@*95$% zJG)CJHO%-Cvtfa;4*t(+bZ7){z7zH5^14ZYgh>LEI z7F3dD@eclBZ;&*Z2uvqo_`JY!!nDJ6Am?M`p-unKZ)b{4?~N6db>(XBcP{+Y8Y z#MK`27SKtgSm>QY`+Q<zRXf5+^O%(9mnu5+QlZab z96&O~682*8QaMJZ9(6DXvm|^^dGaX_DMB8w#9OI~oFJ?}%W2e!+KpcJo}w0+csL1TIW35(Xk%bej5PkmnBa)av`l*0~8!Grl_JPJT@~ip#oEO6skWWl3;oO)(iXp z(S3X*J5qm7_lmDqgegDVz67sEjD$)}6g)=XC0|s$-hAh329mtakSU%Xjh2^~gO$^h zxpjgzaLJr}eyYWJ4f&dzYX{tB62?;?~*dqGyze|(}5b^y- zeHmHcJW9o8MImuoxl+FK7D~JpW9E-5^^1%&WVJO$hDzEYM#g$RoKI;l1y8c6o%@mI zDa*+gGLON|E>FpXdt)fBWyEGtmY9}OxGPZe{KmTPnL=Ycd z6twghbW{(19q6Qgp3`0GQ)x4lR{|K`PLszob$c877N#lb){@z${kOTg_|s$Wj%Tkp zAOp)DU*ygTm;|_$J53eWtv1-PbJt2zfB_T9G=p#Q24`h;y9EC5!&q#VN~~TY<24oCsjI=ZhY-c9&B}xVdWNH4UjK# zLgnZML>N`eBopEVg_c?q+u^g2>Bb?w9F4X+k?23_D?lQ(!bm(;U|91oEb-w?dYNAu zr#~!0Szqoht!|_13)3n3(iS`k+_yM!efqdYC+bdV7q;Qc=S z=Xx;F{G0P`d7*x$b}mJX*mf)~hL0WHA);GL=Uh6JU+oih*yi5+;Bcz6t@(0eTjcO$ znv%6&Kv6N?wqaJO)A!mAem_AhkaF?MzrVT&bgCei$qVz18m$oi!q6HHJ1tC(EaSqt zA^EUev@aef;1n4%A9LwHrRe5}Zvm~WsqVFSTKT?>?$Eo?ML7Nt;<$c$)qCHMrSWW( zUn%hfP%8z1KOzMDxHs7mE!prLfIJ3SSE2}U^R85k?R>Xo&86#S<2U#8xsOSX{NvC@ zni4Mbs5x48mHE^L_pjJi3~SF_UvkEwyL{5p9`_%upPj0|ug7-AH~$?@eovg%@W+^>5Gp7$V0)-fovn*ptXjwbrE5dOX2xcz|+Ou1b4ZbUlI;;PgcHi7hgCtc!of5hH1YV`ZgdN z=H>=chgnQce7I5GR7sIoRk`1ZS{rf#m^Ju?ajD)_!ToC(ROF6ub{7|$&O>n~kEuGX zWLr>DmUhDYt||H!k4w7($4Y|fic7@pG|T0YyHj`j(4WLr`;H1IE*6JrI0{IG-i~$d zQUVI9XZV$?qx@0VMxG*3$VcwKeyzaQ4)VV;dJQveanACdVTc7NEjzwaa`4fvG<4GEt{(r1H>o(bs;hdJTkb8a^4dgt;n>2xA79ObE-%Bn z4O@+l4UD4P_G%S<1k}f{HRf4b9)eiARm#j%!`W(kl5aL_ycqbIRxZ2|e4KIipEw)_S{u85AF)^ebJK*`JV? zq}oqr$jr1`zPZ$toZE+{SWQ{9{h@qFn+6~?g;A&Il~Kj=jW0D~rM^eJy=M+rqHr8D*Hr~BAd|)TbvVV585ViV#kn>;u<7e06goLkm4FRiP7!3c^MZco z;#u|8T~@L1Ty(#+{?yD*mNCc21{*18{=Q5^!BB9JE|ZF{0Qd9ylmp@|^UXZ0bB8Hd(Xi2CE%HR1NPA>Dg3=P z>}&7;-|?&Nf@mC|gjEq%;)^3R=?o|)?;*$HSiaY}Rtyn?Nm&+KJw82z>TEd_c-ifI z1CyfYkCVQ)!CvQ_35Lf{j!U{$xb#F+)UX$n)ZXPQS99Za7z1O^%@E)=9oNCLJmg1V zT9ENYomVN#$#BA^MyP&{gv3&z5o(63V9O71;H`9p%)Hyf4i+CbIliWh^!3V<&GQ?S zcWImlfbgQs-k+Q{R30c(TV1f6U?JLt3cE8Z{mJS^Z%>m`Fj*G2goTjd*jZ#>JDh&t7r=xi{z~1gG+{+`tHo_ zf7nnLf8l}1zmzK?`-fb$tFem%-+H>HKwe#ijBUmB*+$`3uOaZzhw~iWJ#r&zYdChc zQ-)ivQSOf@ggX24-sdhbt#e_fM+{jh*j!cCysPr}`|b(e-kIh>NaS)~o`L;P?_D*v zg3>|Mx)pMw*zRW$>PlV#DIW_)*;B2ad>QQuj9>MvUY0U|yKQVk8T;VVZV!DPJf7a( zMVgbL>#jvc;Ze>z;cFRI#-6%{Tke7L09zZ-a~&7R!{NNUNSd4zU#)n6ySVf6Vn(EC z6DFN1GpTE7xF4R%oS*Ee7j$I0bV z&S(~Q4v|3wI$8L6t*JURFQaIz=9(v4EXZbG*BP8j^l}w|j1}X4%d!N>#JYP6^mh@_ zmJw*OC&G|3Xw=Yy(a!j*kksmNJ-|BC=pA|85N*J44&|N;_-Uz$Q90G>0m4cE@bJra zUS`fX1qhMoW;)k;2kt)MD){(MV`eileO34;WY;5J+|_W`nfbose=Jb{)luyNG&O0o zvG`f9f_L9OB@K(E)=1~X`X|Vb|C^pBX?yV^&#^$$92Xl5@MYsG9aCe+g2T#CtT^21 zUPb$jGP?TeeQ^mg?MwUZ=k@bB`7J3fEep)!U1-&p!B3XfNw%O z>US|n2Y5(0XKqgaTCvH}QOmkEolPUHAp8Lz2f_z+aj0z#JnjV-o))ar)0Uiv<|_&> zXg*&G$;N>PH!rhfdgzT4G7m_Gwswgrof9$ujlDa+GgkEk1KM&U=f%TCeSjFo2!OKH zZBk&msV3Zh)GGmM>3eUdXY`)&tt#WCv2zN#a8Rdub`I1vLF zdD0RS)J@`1TBwWqm8SW@RN^$Gll+!k{vVdqYzM;bo*TH0oF;_5N+-jXp#FO}9}p%T z>-&QrRu)rtDn4R+KaUvLY8e4~Av{I&!k7WKCC7>gk-ViV02A1@3iJUs-VuL%=4MF2 zfZ=D)c4jt@%$Ds`oz>j@ua8Ly*b;^nq7wLeXSz{y1Pn(DRX~^%=nav14CVwz+A`<= z9+}|)_OPS>?Wx}lE-e|ub91A(A>(-jlq{DyfdSnpu9;tdO3+_=+k|ZyCRil5h+)|J z)A13Vp$rk2gsLH+qv@qFkx1Mp-xARAjo|OO)*hbiNwgq~MLFG6R5#7%;=eJcj^Cty zNPwQ;jHHqZ!tH?)kw=ky0~>B-Q(^j*`qFJCdAPu+!M=uFMw;(*FU| zE{6@f_}?ySZ{0kDB8XA;`oz$!F)I{GL&tv^Z6;K*W75Ln0Fip-w-g$uk_;4Ig!+bmcUtI2hlyd zw&$Y6N6;FIDII4W9Cgw&_~BmGd@aiCXCFQBa|Z#k1@C8Y>?)?@0W#ylJ)IKImvCHIy7C?f%AYGeI? zTx7D^)@E^4k^}ruF48G~jHmguv*%DR-Lgz*{ohpceO$xlToYsLPvW;nCAxdNeMF;{ z2R|5IW5&&5NA1n;oPCCl*akLDVNqH=ELN5%5-@@PoiA%HI?n7yKh-=oL>%k0-oU`4 zmxbKTVOU`7%oYoP$Z<)P42oIv2&z?+{61YnoJNy)LO;x-)LG4kE$St8g!480_sTbW zY2)i2G??9MfVl6^zzW)40H7%~R2M3x=gW(lP_M%BZ|`&sS;Uz!k_NcG_y?34$B=v9 zyh#s#`Z^IYiljKJx7vFbcvB?_D}D83U?E-}Qw5ga(dN^9~sXFT`^kX6AW z@4k$-^#g{p?g}d$-5`9YGl8oOgMPRq=9$%QRc3O;q^y3M>@q;79l`TQXZ!yE6+!C0 zwk1Cv;%5#;Rds}M)gruoZGAnBA+^{}U#Q3=?|x+V!OkPp{HPoihat`LI1CuzI9UoYr6BAVfTmNakRv zvH|PSljb0zZf-6|)+A>7vffVrMjO$3B26(c37QrqXOdNjg<$8e~E?Yk8O)Z7E zHxyT(GxQFJd7U$z&SHN$JDk(2S8l%bO}fkKNnoOmxItzK!NQ<9jYlaLy_?tEwShZXf1upcVm z>{XI~lROB02t1u_LK~oLT0JUE3x?m8KR0!XeO#TE8Ukm4O}o4J5JPP_45R_*rnW;A za|=t@Ix`4Y_k=bW)&?bhX!Mj5p6C(z&WZTQp3zGIko*BULk=NTUwN_inRbzoQt?<79$QgW-P%C{+I6?t!M~1 z_ppuj?TcW)en&aa3Lo&Efj{i7Aqqi3p?1NQf}70=b}Y162s3WZy`=GlX_PNA9K z*j1nNOWPCeJ;zH=z0lXj2?}qCJ1mA4fDSAS=KS)Oc?Eq=53@d!nxVH8{tAHUJ`d$Gmr6%4rc8 z^Or2IKVN~`zV9L0eYD<8B+G7=hx+Q3Q$A1TsuFq3@h}6sD1)_wn6_8TLvWZ)-@SIR zu0Lr@svsiSlzVNE7C8s=EHj*G&uVc7r+t*95PB#9g@#sEsU{FkbOjhnfp$$$&R(e7 zuCUN~+4x5W`&1;_hWoUuqnG=b>@6CyMK;JZ4EpL7grY-39P$TxIn%spgleDC$Y zHAZ%*VOk*!i8M^`O!Lp9!G0duX~BiSmFKMq!t}s+=U~>pSi~aUCKea47O{xJJL>Z4 zazC8?gIQhvf;THB zv_Yp#SFs`;y}mvEa7W7`z_c5uO&-XuDc5jLMx-AO+a3|A+M|0b_!D7K?o-=nh?4BX z%m=|!uywYS%@>sukW6irBhqz-6BPEfX}zt}^J6^@uUTJdq&z2Y%G6xE@eIv@!&D_1 zko;u!8DJYS_x46JwK5Q4R4{jZ_4vqhA{B)8FivI8gT~sMjb-(UDr`LzQb&_5NfD_B zTkqD)p`^CMg~ z;LM1jE#>g1M*{<$HZ4+?_DIPfal?~ZRTxr8G`{_6rN=0EWV~zJV`6GqL4=jd>KUB$ zFM6^*o+O(xGL!d;O!%Y9cH~Swuz<%#P+MexkrJG)P@Llr5yW(vl#=GdXNupUr18ea z|E*W+OGEgp`RyF}nX9YdP?IG~lmG?^kBq%MX#+qU)YAXV!OF}r^loYIyD1Y_ML$ncrLkE8aA+?l zInlll6b(4(SH;h#4%El6vqDN6`u>w8D~4i=sMR<%AMrcwqun_)u2@kfLbKX?uPXhL zz=V2BdvXV!*jL{t5*!^Q+8NDv;m4!Nx1O46jQ5dJa-W|+t$v~(HPLxYM_mj!)T+b z21)bQ*_W;asc*)Q?KdB6ac)7@cD6+3YcJR9e)~?=>q3I_YqyUSzIvjJ;x~R`B^EDT zQ4fEq`p|WCm}a`u<-2tseOJ9<_Z#U6M?h8Vl@67|j7;>2Cfx{%vHb7V4dGz1Dbu@VR zlh<0A)(||=`Al09B9%BlWhjo9obYH1pIn zwd?DF)9Tjdze`h|o{kdCz*S|nbY>KRs_=llAcSI1m3=5(q;v9+Dg{AP6)FA<-=l=* zS_Ja(0qFFOge%{vR2W%oqupUB3?aVUepfdU?k2L&A_b`&R{2m}oQFY}fa}b}*HrPu zNmGdMQ<%lOcm<&k_d)k3oSlCfLn-Kj6Uc0KRy`WkrSg|i2*vpheQ7f2%qgTifeB)I z7BZV!2xe9L_A!so3qf!DTo{g-A3lDBrWf&JjKu}4Mf{kf-}x4ZH@*|R5yKE~in(J# zWZ@d-;Y?Nc=jfR^*Ni5Vyy2a%uU=6;J!S0s;RGdr!tI#$q)W+dq-WCz!>|C-j2-Jn zkC%5hpfkj7ritBza%bp;u9o$v>Qx=x1ci_WhAk5#q06K^+RXmL(~V|MY%D{lS_Z_+ESG&`{V;TgjbG^{v_VTLs#yeWFD`aeHgy*eB~arB^`yU zF0VW=Ge|rkhB>V;t=!p0LfS65&3XrlVCGlP%SxM|qsTY&-A`opnSJ-*D9#>&no}8FOxkHhZDS?44 zuinsJtiuD?YFVLWupy$5(ylMY=GD;+cSFyKe?=--k$K_1^<{s_x~lcnnBO(o8mdeL(Hv?huevWwH+26(>Eo|gLj z^VK`w*j-Ooty)<_LKQa-pPKnw?`~bwmby-#ua&{~Zm(}_?K?U1`DbT7e0_NW+Qk8Z z)hNLPgV2>Qo#IrYO#`V#0Xna5U*FR&5apC$0PY!DN6yq4K~SUkPfxV1Xy0W+T)nbB z+V%3$(TRYGAPw|DR5=CqpC~1WM1JpyQRp=Di_g{N`iA!UEee-d!CAkhcxf#u8aaq+ zzx+Zqfs=qUja4w5SixhT1!!eef4Z}pA9^;P6BmIx<=ah!tZ*qX`b=9YQUIKBgSJ@A zhyqv0R7Hag&z0@jF;a;3CUyA5;r&;YpB2d_i`gw8mpE{ixk4%%ORfwNADMGsXv02- zcx-NMBH&`%dBVU%Tmq-6H~`HJXC)GvP)@m+G^uPMHwthvtp>bXN{L&s-+ZT{Lu%mUbWx9fnzD0bNJi zSD~2*XUg5EJJG)JAJrhlH$;Ev3>LV0H7mLs^J9Lt1^PS}-cbB)>USPZEEg^W3yjPn z7V*}wxPY~YA9D;ZpbNR4e!uPn(rI(hchu!u^ZN~Zo+qAQ7^u0LFda;XW?i9oTjA-P z(^3-%gDvys{-uct^a;?gsxPHYfQHQML2h$<`>xHjcrbJzA_01ghM^xq_z$)ZJ%kvB z%sv%{p>Sxbn`gnIzl%Wj!LSUmNR`Cqz=_wbm%@RPm@}D1>GZGd8abnk(xVX#kdEtq zDyJ%WMc0Nwl}UUkHk9OSKqQ(=LleaO*V<}@V9I$UdneD0oo{fzXY!t3pS@|Ib zs!-&F(OJ!Ji%s(I_iV2M=A>|J(UYu3Ic`K8L-OQPS- zPtf8XB>ta#pw@keoWD@dLGXtX&f8~`v$6Xxzg24x33V*0q82KE?Jc$sT~{MYnU!*& zy$8oIi~sPm#m~uT20RLfvMw`j9fhwRuQeu|YMl!}WW~~YgRMT#(&)8of zv2WwVd9=S>Ko~OK6cDq}05K6gGzj&^#-=voffOeDqO?)x1jMhi9^F%GBJbT+3~|1G zxpGN;<3ECU?%q)=m|tkWWr^ovTcWMDw1wKM*HvQaY^tTbrc+zuYNE}V$M=`GhJ(`| z^U|fYMx(Kq-7?yloq7`8O{?k&m-n=fxYHTC!jfOu)FR@>s`jcS&L|#s zSwF~CjOGZyk zvZ6L_C>MXz?1E5e%cUqx>IfK8I^toZp#P09oLbbYCUi1NFxyaVFYo@#AGkmt8VZIAgIMY1 zIu}-rey;#MwL^<3WUuz>%U`dW28Ic5T`_jF?LMDAJZU zY|*S3u3kBj``DWwKk_Co;>R0{3s{Tz3BnKOdYU_g6!)EmG=Jg-HtYQiMP4!2!)p;q z)wH(h$=MJSQy={O@K}2&Oxaw`T&_=Kv+st6))xpSGCu~%%v^{weX_-ep_Ohl`{Wxb zXL9-HFTL(FOcc8R_O{_OXq^)}8<^C-FzA8^!stns!6H;PvefqH5wa!Vz4?&C80zK} zOyBc>(t(qsuZKW_5Iu>E94w0|C_LP_>h%d+H$k>2h5(?NOYK)Y!Q}EN)AC5qI zC1#XjbMA&+9p+wxUNB6QmM^S5MUANRd5zVe|+_s%qh*Jx!+S_n)i} zx91D!M#8qW^$C1RpbRakbr4q@RSwA-(nbh#Wc8hqbN`*C_WZ>f;nbodat7i&RD{1* z3$h9csXD4S{7m}|z8g1ImtD4U)RywIeXmMvrS#Zn)}f!v^N($Kl#QU9y?o0$?t0)Q^P<5|1rb?js15| zl`J~7FCMA40-bTp;X&`wR^aE?c9jW-F{E|0UAS0pEM9EC8cDGCKDM-x^89u|JT$2K zSJ<>VCJ4TCc;?sJdx(XmjRp+)ju?L88EM*X{=j(*LKH)wjr22zg4toL8zc^C-q3C< z6@eD*kLDYE;}u>!#&As7*0;~e|9X2r8XF!A5oE>24U~Lr&W0W*!$u*gIF^%0**GMn zToG<=yREzI6RFeZFW+Gu>N{%h+E$^)!#8&y(XQt!FRMrP0Np6=&2fp( zwQpAv#LxqUFFarVq!ppybhUBO&iB+^z7Qi7KeHV3h4#Qy9@vROg6y&tJ+?R_p5X~w zYI|Wwr=gc&di5&EXBKhBR>x301r(<9UA+95%+Onfp~#u`RyYia8kn|s3MgyZ(0Ule zDmS~K2FU8w{u8yZnD_FAwUEE_g-CW*i_9%wGI}pElzG;PhO!gR0nYKRU86L_fHO$&ai9pJrkQaGiReO-l)L9tiIYWL(4v^+c)zC9hR_JW@*fR6B50S{V zQu=Y45%5-RZR2I5&$O?ShaSyfpLT`kj9e%8(SgN=x4UDboi1twrRSwe-It7y; zokjfK%$iGT`Gc&e;>V-E=vR*CpL@3UJs(Rtr~F`h-;P@NusuW0JR<@bPe>6>Q>jHV<3m$l(Qoeuw=*)j~ zYvXJpx`~HrFA*8b@iX zWi%U~Bf&2mRE*@co z+cww5U)MkUN}c{Re0$&M%Wlrl!>30tm*vSX4XL2DQ~R3BXZ{~wnfbf76(=`#wo?_4 z7_xY$9&AkmCy3L*p{D5+{Au{`2LylanxbgSnw2wu^h`bN_4xi7$@tydt1p!C&~J|i z?C}duR(R1hXVuCYz^eEoCS1)0gjXyo>z96MTDh!VjjS-XbMS-Lj+w`uf+r&?5vFA8 znp$HO!#F@2ehg8o5clXI8#<(5&xaS=?`F_%3~X9mU-ZkEoDk6NGZQ} zdyQmg(91{alVrqsWwhh}{PQE{Bei4v%auVC#tl;TKvfnMb8ClLalyr0m&x$%Wff?f!*A5Oq2Fjv(O3*ng`!`@stCR#BMrf0&l zioE@XECdJJk0wpv4V$otMZ9G!E?_NU5#Jw7-^Kgk{8#XWU*fTuHRcZ>F*hMN*WGJ^ zI8FKA9-H}v>qq6FtD48XY5Fv?!{PKLm{sTQ@$b=?#`URc$Iu^%oZMg zccZf@$L&yZsPMw~YDFWTD%U9-GmocDRk9kKx}L%iNkulC{m-zPkBw1PF=0=GTq}j- za%61|F~B9uM?`ztHjlpsCHDw_*r*Mg%tRXeZ=R@JD9=fShv&kT(~!8VsfSl>l9_bG zv*TxKVpKWVV*+@|vezSU;AB1Of=E@@ph_}{k~Jnk$!Mg<}T%*TV;VLW`__~-d3ysxd1Nl`9aUg=!?ks2*sQm<+Th=9VV%)Acs zq|aqDKO=!}OE${lvusIQ@@qe1$17E?@uK5=hledygBHlC2Z|IYFa+tD-Sm|A;KfIc3WFu9kO=Mk7Nq#-D39g0Q*XhYAE zjeMSx^!sxLSQBTFOP_Dg98sb+BK8LPy7t=+d_$3cR#&#uE&j8@f0RAP>mmv0@%+e{ znMYo!%bLRsR8^E|@U0xo1axz+S3T7BkxF|ZgzTvfKW z8I!v5+-vpr98M7DYu48Vxw@mh_vaLLf^(JiIBBEd>o1pPk<0~(byqx4wZ2`H_@Mdb z0M(oA1lOT1V(;5gcYLaF1{l&t==%9nBkHnpfJ=Eb74(`~8#YjIJ#Lp(d%9ncn}ieM;F zQfzHDXjt~DC|Ay2%sX9hQZ_!*iI6W_0S1Uv#yJeDQijZbpi_qEp4dm*G%j0FchXs_ z7LG37EyDad7jAK=a|rag8Z|4X^~|NY!~NZuoFCJF$6dHf-*-%}UcryhpGExGV{rj% z5sP?R==Y(yuGCTp_vv}?rY`Y)!Z9J0xyU@KChnIK3^&M5F)WrXtRtw53f$`jNPFkyLt60o#YKuRpL0 zY?N~kH1p-dGatU8+`xIuruvMO@~_=qJSe=a#f#QVhkTPuQ<&xx2X;f@6;6d@%O}s( zvJOuOirZ)fs+}^mbkY_e(k@lJQYQN^9xFOMC_LeZ0udBSIPmd1>JZPreX^A2nV|U7 zbzZW3=G~h|O7eymdOaf4mZh$cb2e>-@p24KFzj(+%PWnaYA*c}7ZdyIFW<|Ld|7{8H&h`Oy{-ChvY!apPFAa$A z+1^%vE}i*zPgZ`55}y+ghuo-Tpz%VjvRa3FT)}wz&~^3H)vcRHUqi!_z=|6N{scI= z{k|P_B{|R>(EJBpEGGr|-+s4V9=v;7iMx(nY!6|5tX;!x&y6p&jKyMI%Y=5K)Uh|9t604^K4QwY3(Y zb4jd@Zk_;z21XuOTYp)t4C${o{D@!>#=O5c!BgZ(`P5;ej zXFhmcT_uF#{a23`cZNz_pw^DBhjB?2q#w~Qw6}e$cLq}SSt(FZ6*{;?+w_9I&SPAF zfk@VaYysz^hg$K`WMz8Ns8BE{7>#l1?1ef9^$WUDWSo$V!cnx_igTEw88He|Msz8- zt$on5G!Lr4z8BJ*eh4TEHDFqy(^H@%g^sXg3x?1BsO@$;hKIczJ>95@$M7&19_~Fp zzVpt3A@f{H-VWwcg8ruT&mW&zF}*l7Ki+JUzh5DG3oy33=^vpRi}U=!L*fwA^!(}4UK!n};WR=*|o&OP18T6 zBpdnC&e4NZ*zEDwqdM(nrAp*Z*O$?-0S$Pxh@KPe+b;OKdvx~Jl>=?@YuC{VJ0;jO zQz2ow|ITVM%KG+6DdjkWL8^@B>4TN;{p-g^J~O>_O}(9T&HB-*mVD_8&sS3SiF-?2 zF!6lkhI#-;ZV%M*_SE@$AOiE>X>TUxVW?EmY~yDSg?`+&rnG(P!SXPz>yG8O5rGsW zcT!80!Y3c7$G$#%Lv{WA?c>(BY^qhB_q9(@*CI$;&0&tV=ML3-dCHpb(C$)BTPU$e z31Q&JTUV| zPnRHvGT89{+%@$fc4Z=I7ZDM|0Hjc&mWLStppcUtXTFApQs+hMrn1{s#B7^a*Q>H| z(#b9+S$E4d*1!Mir)pT{T}7co>~2@P-Zkn1hYI3De+o>8?e;CT1Qzk?-~1V)eVAcMvUBYtMQ2Ab9h0$CR{eM%3vT|L+pcHsQOd+IcC3ebOVTPOh$i!6tcF z>?bKg(u}Ge6d}E+Laiq~D54@JLLtTILCvaGXgX%54OwlftGXO*(G(46L3m^#VG>A# z%w&3R^jrIXzMTi(ckj)c34jDy_a7%u?z8rCd+mMBx7WVMsJ*U@c??;z5cxH$>UUFw zwU?AC=NdtDuHMu7-C>!|#dDe%yE0_4IES3PZDZ-_lwzL#;!~@P!V_i4Xvyu#dyf{S z-^OJF`_I2HoR4HZ+KV#G#vT z7=PkYY-J11w(n*-qyggR4AhDMolGafUA)8CSUY{qYZ!)eb4f(cjJ6zdwJ!^ropKW8 zj_JZ;nlq1i;;FM0+j)9I;kP5xiNMJV6jP)#yaKgNj8FbEG?vzef$Tr5f zHj~q= zqMqU;TbxN>t0dIM!E59=WwRrF@kVw^olM zMkF19=Tq%oCmQ~3dsz}TJm23{Nvs03XU^B!)`K@z23~wE*NrxwQvx`t%31~yhjQuA zzy3;9DX79s@bJmWrWQ}yh=}2T`$+L{)uv#GYMPDb;)avdt=HCrhp!%<{K2A%|J5E-f**+S0{rWm2WgoYdIqXW9vdjL*TEMe7$TSD2jh_L*KwksRc!qr&fqYTuC0-QdKmhf7Pl2?j=6N4VW$M>`p7Rj@#P%y`8m03&bNx3ja0hs_MS>>Nj+o zChR^^W8)fkMFLf%sa0ehJgsssVdD64^K42Avf^E+{^7P zNDjlJmOmK`e12wcEgNmPa^P2MBNfi9>%|ZZ^maxlXUh*k`Vn0W|M!oU+X+i&1}3O- zRzA7s!t-t@%K4jFJb&qj)%Mh8-@VG(T=x*zCE~DmkO0Q0a=JrpUVqN3VCUgMs$=^P z>xEgaenMe9Lkz{1M(N5#EF+NHGvW|tDB0-`YbS!k5bY1092Tm^4B#+s=LfU{Pd4IX zI-qD>(A$M*{6IuPOa$y>6n(}$myt10#tTC;=xRIWDGozX6(_sJUbPs;+OjeTGbU2* zV=Tz(RevuB^xBBqRESR=c1d{kP)!cs5>-Eac&mcEf*&zf7O+40h2z?k?YS{Or|Grxm?4Tu{>-%mvwg3{d3W-Pg(h3J6zok6 z>s{S*?wRJA4fNG3FhiQxvrL4OROa7fL{%?CXU>$aANImbi)K7Gr8bohrOMNZT{h^e zSDAcc=t{y6p!A6&j2+O58HD-sFANjqu25mz3Xllh);!ImVe8J z;q51C>v^~7U$)n^r*#x6o4TQ%=@{bQ=KT;63@Ei=(Qff@828jBEgUqobp+xe_JBA{ zvXN8}9lv3mPH_QImS_==p5AJA9vjN+#)4`IE2tlk!{QNMa`z2KbxvdWmC zLEqi5e(;bFG)K1gBPfvS3m<7FH`K-%+xbc5x}RzXPYhvDjt^<|+4E(zw6z4-bYGGny`;w>2Mm=h>+3E&GM{Ll*h>txj`HpXRk>}4 zh&|^i1Xk46c$+@^W=~4!H0S5;sD-oKV8RVbw(XnzuReI8;UzM?-A0S*j-<9=R8hLG zh|-}fqwoh?4-O4DLEes8ogDtZ`vz-k?PGSMRB%5NxN<)*<(Rw^XIC>#&WD;P<{%O%@aJjN0VDX|KZ! zHvVV=4a}*cEuEFSZx~v;x@yy|7)=9JiIT|3aK5;`3W)Z^^EM?$@gzPswpA()^MaIBuryf<5BihS&{URJVQHv?G8CUkM18dt26f7P; zQzjG+o}B#KACw7!0!m`;f%b~4I&5o~JP#@|ZNz?mSKaEB*RCg|s#tks>*TLruG^X- z7`3k*p4_#mv>iHGUH08KJ^`0t@`s+fEvuXz?o27g_zFEADvUQ@qq~% z>~m-X%w?|K8>KI{wQj=Dp=@|_2E(=Mh9x$023?s|W;y{e3r0>-?xo2Nv)-xzDEG8y zC(pOfdP45UL{D!r5l(QZeEjs}wIdbq&{=Wfyx9KEheA;Aq#+QClyvJ5wl1E&^B*3E z86nDu*$`d8yU5k;b>j{E28N1jPTZIg zzic;VU|hXhl48!P(? zFwZFI9`;#9RE5Z>Sqp>`VxZhzQqW$4q4b;Vx*kyV(9M;=NGWf=w&vdv!4Qkj?;Gx` z$`cCs5xY)%BJ$xo+Y*&>|2r@wWy-37GG^H?MDV$NLw97xFYWx*duqU#@5X`Lu_Awv zSO4AnYrQP8fkIzT$;@hKY+Ln+hA+HO@BK+tQW{{|p*Txx6=Y~eS87X7%j<{-Y3%lP zbBa_DZn~ycrU)`r%fv0uKh(0jw3nYrUnTtZHaVfRnOzJwwhty&YTY!0n8a!y-j)qOnKwy)kYqd@SN?y40cR=)aj8BKa`0Bn{( zo~c$uL!g$3SUt9*M9|B_w)m14U2%z%4z(gnX_RY-!s=CZ%TijZto!nq4-KSb-S4}( z!X>sSq7BmB|KraO9S!m^M)$WBE=uA*5yap9iz;BCDgjKKt6$o^OR6wae&w1PQOd^o zgEv+oNu~mrpr=uQVsl7gF-%vKP;&O!YTzBOcM>Vb`8*65!L_TZA*%4WzNMv-aN?)h zo6dJ1o!qf;FvL%sP9nX4zxua@@%r_{sIXz69IB=5VYiA*JKd73Qc^>%F+(i!3Q<)h z|LqnV3Tq5EFvqt~K33=E-+k8*ReSCMoeIS554wsTud_+;iPu*J&s3F;wxUJ zO_h(8J1?Uuu`%=~qxe zTha;?pj^=OZ#ALCKb#zkb)opH7fZh~&8%!`%f#uOr~*~-Q^A1gkuNLlt}%z{1w3kF z#q~#{5*9p+I$IqIDLojR%NX)-Uxv-;bHlsX!VvK|Z$QQ$s!%Yi!nU)$zBA{bDo_hV zjVi2D?pjJkJy2oenwli;%ziD3i56-~PxEpr;i!8uAkl4pY^Ua(-X@ZuYiA z)CTEU0YO=i0a6vt8TuIGD2Ya-V1{iTS-pbJndV;64V0z7rSdK<8W&zFG(SFgq&A@EwsLZ70h5|0c!;-SQv|4c^9KE_)~Ei zm+$3^;LMg)16a<_m3jHxugn`TGj|CP=Vt(yf$rhh)%dSaGqI#hcnE8 zVp^GpHO#rkZvhA}h)9>}ldu^*aNmk2!&PgCyVh-ZBqUHNN}H_`q&c+&A@gI1c$$Dngm~PO2Y%(H%DYTA*$NMS z^Lxp;u}zh&YbfO{QPGg}EQ&3?5VlGW+U&fTn|S=G!gM8K_mNT(&Gt#2llY;?Sg>J` zvng;jU$s}o^HfF9UUR4=Udq?knVVwWt5vZ!4` z$1%1j`3LVFdhpr3llR?HB8N{+?r9HCRaDp49WF%l!aseuH0z6WO?-&LM_Q*)@*B-u z5`GM?9x6i$woqzVUQvGD+?MSm!`t=^{aeEfQyYYI5=@R7sW4E^ZDkC^xuLy?y)P7O z6qaKxo_A~<+RIx|(9tv##9%OB{;L-!-)zf;6!s9Q{rn4c8xj5bqFig=XT%^H$_lZl zko%#SaIvDthR5N3ZDrixr<2IRla1J()9h#HUDwrMAi_`nfi29R zc(@jBu-WLrBRE<5QN~P9V&X6bLT=0Et>Ac)SErV)w5V)bZ~+I_UXnTQTl{ajn*tzj@dBQ)9Izq<{7 zp11(Mer&LqwY<&I*`7z+bEE{ZWkDwvZ6CU|RHZ|C$ZfUMo<3Uz;%OI#G&3LTh_~Hs z0YMCBEecP=!;O+F)mU2s!~QH>N)v4xL%m zz;->!ptb+?duyFE464W~fU-i~as`y&2iMgg`l$jZuUi`9% z%%&^rY7=1go8_9Bz*0ygsse|$m=JKFByWX>Xtuq2cyI~g0OXGTl+y}TK$XjWGy?`d zrnxhks_J@}1dgk?ajH?zaUn=IE zUl2>Py%`6ViMQ6oGF!cZ6~M{@)(ZZ1VSIhO)NAFB<)>oa=sYE>S8!1u$YyHWF|X~~ zb;IA2$9OZX8=IIf^x72cXC^Bl&I<;;c4m6lMeH}VL|6WhPxb`nSbgCvoaWzs$_Qhl zxkpTTIfkio7HBwZ9|y)#pq!2J%xjsz*{ed2lx^&G5amHzYArO;xAQS=fS^ zLO<+4neidf@GrR&bSoy5A8U^tX^7#(R$3%+O6ww?7w!fz}8pTNkNlr{e8)k?$My_5{H&9R&hU`hH zvM1<}sv??yPyec)Jz|ovZU`qqQU^}d`V`MUytVEckm_JFM6X;^X-N+e@f+K#vR*w@ z`_|}s)a$?dnPT9o_9|Spw(gdSx!-7SeF~z7R57<^qL6aPC7eto!mMlZ*Ip@e-)Rp| zmD=V^+GyLhzf`4-1ktZqSDQoSY}LlL2y(~9;&F~tB-?w<6Og}txt{V<1+;y8=VZ^( zT30&W*6cVV31{f;wsb;|xZSy_K1andvC3mCpds2Wi~c)zl@vCH1dy#f;Fk7^@J-j& z+wSr3L94@q!Nwn+^uS{Hk53ncs=9%0Ynh-VL9@@=1kD7~*xI)NLpE%nKsi0s8jVlv znf$BA2e<7h2T|gT<`idJ{!>+hygjBG6EsN6X+Re}sn@kv*28qF9%;9sRd+Ot_-UXD zxD9;xz^!!)$?rT`vSs~>dIb1m_t)CoZ$31POiI4j-p&8eO_Po5%C|Viv|9OI_X<&{ zbwQm_ZQ&0w!!L2jh~kejXB2qO!$dfoK#HHL(95e9Ivf6jSxbonIIn0$T>> z@iX$MmXI6%!!BP?)o9O&g+Cq%DIRjgsJ3t6S%aRbGVuv$wZ)wMDnjY#T0v z#2sp5jg~7aXz`Qs)8|SLkir%R>~U(iGVyz_uN5m7E<}2;5r=Z@s>$YShEpJ@m^Tl# zH!*~~ww!F-b`2pSlJS$m^X>06E(Lc_*Hwek*4%RvDN}O8`s(q#E0Ao$$Vy@bfBt;k z6iEq6{7l(iwBvkC*i&dPfPc3w_^RTe0?-ytn;#B_(lpGQ+m%gi_9FAzp(>O!?Q~A7 znGi$Ggsaz9Emba*Y;KwnL(k<&Qmc_8I^$_%GdMwdZs1>-U} z1eDODAxeZLaTxQ%%psgUJKP@)sUTzt3zs=2x$l#*Ddq1!j!J zoB^_#+oh1X+B|4jtWjMM-u3qxHbiIk(0S<3E6>IVy>Dz~uG!TpI81WV zQS+O&93T$~4>$yeDcBoS@?>d-?ES9xB3?R!BrpMsl?k_%DLj3$3(;&J-{OXHk~I8J zzh18vHfEAPC#OoDqn3!hl=xg*C~-(CI?=gWw%ScA8UNv@At!a+<hmsM1T{>el2VB^1fyhOAY zC3nVQONPwEnRAtBIlXD71P|nSu~OTbVA}oIjw-N-!qE43PVTzC{5g5H=sFHvpjeSZ zX5B|Cs@%F>B4>{w4NNU;3kO6*gXWcsV zK)YQ?TkwDGg<1=!oc6vN15mkc>i|SSyS_?^2R#R-YbaWw$>&_-rv3Zmp2`32k1KGN3Q$0sU?JI{W zw!d`O1?4xaFIDj;cB4NUv{6TA4UzJlM=CftRk4By+8z<~Pqde~gIdksvc3)}(S**U zrQd9yZe$l5PwvsnC+dC>wWRblDdGD4Z&jRo4?cWT5%XzI4mh(UmQhPpf`b_+wK(%2 z8z}$5wX1e6rmpMu`o7v?Y?#NE9Of`Ps=!2TLc-&+RKi%uzyv z^7ZY7QyN?4wwR)D+r|=6NH1hi)!5U^?fqQCEoN=GCu~N`x289@Qb_;D+cyoB5y|3I zm3Ap`o2%H=LZpNdF^4Hstd5s08i2w-eXj0Ol+WiU`1t7(q&)iLfV~s;d|UGZ`*a6~ zNxgn_vbWu&NsyJu+Ke%wL(!agWlxY%I_>fJq3H}Zh0rl5tJ;2`_PO%pzS{5ZthHdj zK0+51f9p*fJg}v_1;2p5c;}!kwe-Za-`UOW(~6XH8^lT*Sh;FVh4JdO!w)M4$Pg0b zAbQk(_2n|nTzbJ~KtB2U=*mA{b&K1F22)&;wQd_vPTp8vk8MG+-uj_+-)f zjE?!K=?$b2RIuUX-j0JM<&d@n2TwMfej;_2a;JCqy3s@_2v5iGi!hyIj1dPq_>9d3 zzdx)8Jvc!Zp)Gd1xz5(_VCqI^tegAw!&G(I(8tW+&=$@Z4V3wl+8K1pjOmWW=<`@m zRi1-$$bt9}be@dfF|vBKgx$re7UTah!hl@C3VtH6vVgUMcQh{J`o6^K)mx6G-CO?e zC}1A{ST2Z}sSGS=VlgG1xictaf_tfu9!mzIaBR->E2-vkK)knT#<6)UR@LeAg&zEHhD5hm*&oi=PW}Cy_M>CvIwe3C#b4qM|E;xhejGnw;6_nB8*Y2%-Ff$%f#+H|( ztL;|Cz>tZlH0+_9ixRdx*As@AL*%f1Qbg#}vMA4H%A3EM5^LR1O_yu>~y$eg|n$8g%V$-NNAmC(K zB)+9Rf#r}7+zzu&sg7$)A<7{J!kY%75Il5ha2wLb3^NQFGeXCT@n7%a$^Azysv<@M zHt|<@_~v@Th?8U;(s}lL<#HGjBB6Gu)ygi6iM)b=U8dWD-50jk+L`7t-BztMY+mc!is%1hNL3W?q`d+SNnD9jxY>0+`sUQ@&xQEf=jlU4)%R}YnmpS``x zRom{ydSFX2qlgk`9_LJZ3^^v0#Xs$J$T}0QFlj}G0V^zmvK9ePjGWSWL#?CTo);mt zx9qE0v$`(4sz~8?u|jw0Q&xx`nt6E5x_05czOFW3-%%loa+(jFtfh?)-dcyEN1MVc zhiZA+z}wQ6IVd{UzOE1{<`AjXLGQV--i}0{v7MW?K#TB1KZ*;QP!oUh^}1v01Gm;k z{c;Jd@J{a`PH{nZDXE3+Y&1sY|3{?Rj&b*qLZbBrffudS;V zfBe)&A&{+0N?T0i+w=SD7Z5Qm`s&M*4eJIaAu0=+0)DMcuwvOJSUJg1vh5l}hi=t}?oJY|!ruXtN#@>im4%XCY!PdcI@f>4bb}? zRF+_-a5+D*9hny3H;2w2tm4wllxS(h+(pAUgC+RKRlRJs(aQ>%S&q>~X4K9zW3c7f z2Sa5%Hwv>DN}^mhg=om8o9W*2VT7Emb_%6`Uu|0XmbYI1Iu- zp(aQ*|K!;r)ro#lk}l#w!9eiN>n8ux_a^`Fy@NPNdU2Z9Mi0$0F@d6o1G2$y@0`4M zOD)g@+_1$5I{6m;4*z0XJrk!OAw-YDTzTS8LKIi+Up`Q`2t`9WNG*_xKh&1y7I|q> z?mr3zKW%v<2~aK_lC?&|zLrzn-kg!Us->+XN@Q8D9~;&?9BM2!(DqL}T#GV@oFo{X zfZ>VVbt{=7b0U)RqpA~3XP42 zG~*$dVd+Hv)qCpATz=(m+gR%haA3N&82;D-r&Yw|)zqe&1i`4|{GUBn3rSg_Q)Ofl zdth_%Fh_$(rX$p5FPH>}ZfbAbV@oT<q)V2eu-e!kfUDa_L&+YbfSWflVup@%UdHm6K9#F@3L2U&-q%mBstc=xE6bGZm| z^3n!fHrM%%uD>&k{SQJper9;-VfS`1%xefbons1I%~Oyw%>8giPgeGqz?gq5I4<{= zW}g|_Vh%5X1^Jh5cy3%;%+PWinCBT&W~?tqAuN+ExYR)P5B{uR1wR(7EMTqRA~?}r zG;m4y(X3v_K%r(g6ZN2<_gW@F6%94@UVAT=}7%IXy?I0IV( z-`_R4<=QID2#y_Fuysq+nhJMH3UN3SO6bHHLFmL3>@z!@=gjoIuU>^Gtr#;eC=2LW zw^E2yIKw=4)ixj1-kYjy5Qgw~MAqDQtS}b)A`8gPD7Ez`a^;%gAxA#o&seod`|Yc1TELU= z-+#m8SMF(xGA-F==r6WS{+ss{nzDR6bZiQ2C6fP_zpUt;Ydov3XzPh>l`3rX zEAMZHgoQST=@+qL3tcT$3TMuh234iMt$A^q-GADi{&Q8-U)V@pyRMX^2^os^zx`+} zzy9hy#Q;QXaz)Djf8VXgyJGHv>FrK~{^-v#d-Zzldrs7h41XFg|>ai1}phq9=pJVxC!9C=FAUvm;R4U z2a<#zC)hjRcOEH!D5;KXC&(SwO@9Be$;a;>&Lc#_CgZK^zjAf0?5N^~>&dU(Gx>ji zz4S<~cuR^lO*RzC8Z)lFv=PUz2_H5BTSri9gya8CdsSjiLliNup2~@~wazJL{de1o z?X}|IiOHk47xTK+H8?eP&yiXubxHioZN)#?*Z;5_1`JxwF}<#h&Y)wb-|(r{h?j_K^7ICubU|KDlxHnDW28}@NTLut zae7b^DRD!`P~NUDeh%lsa13PQGoFZ`@Qt3nkOcM)Daql`#`b#=sOo;E$}kpK4B1#u z=osocA+-_2gMm@tHhy~)3i0GC5ZGvlNS-uQuqj}JeBThyU%%W|wA(fI=;=DVzpblk znp(sdHDV2hjw~K^m#ST@%(v>4k@G14Havb_-_?F|BbeG0a*x%(IlV0JIa*^ZbmCls zE;tYL)QU-3I!2`W>Q%IL%%~kRr0TW9H6Hx(cD59@@2}U=`RVECsua50%g2lxsxAX>liD#Z)r1Se(zw)n0#>ksOTx+m)(S3y zOI^Ks2ja4SRLr90KU(*W!e#%eEhadQ)qLC>j%)La9<~`ySUT)ky@D|h5wnd5+QUrk z_%J*(l%Uh?Qy9-+#v&bv;Yn?pwjd{T4?9l|Pn@Y0!%m->HjVj}wKP2Dl-uP?)5&_z zA38JS&a4o@gbAYI02AvqF?4O;X5V_P1@UG(+MV&DlT|N|Z!{J^gwF=m(tXpgT5|5BE<0z|I+ks3P^L z_D-m-P^OWkjrD8m9wHgL6z(5CSS{kAD%s%GYX&_a#Z9{ig6z~)*J1pOi8P;80-V>bt3wR-a^rL7i=kR{MXeFsxPGX@`Tbp$o6-4~&)2dD=i`hjZs)R}R9RKR zBGGz$*W^HZkqRZ}S~kv~v|aXS|HSUAC6 zqg@_)_*CISjkkX=`y}fu;IaJGS zIwJ_bcvoeTz4;%ywbmSgttgQ$UVG)BEy$uOhPzwX!5-V57NWD~YaJw!*JO6dme0y3 zYIvwZ|3LdVBOc8x7^v+XaESc>#KUzDkv2w_k__)e@}V1RK^Z4i#v!7U<(a+3tZ7BCK{%!5_{DW_o&Huwk%5!?a*?Ro+yO&LsQ|Zp@Y6Z$=@buX-@$~Bzv9yI$>2IeY{wMa-KziTJwa!Z?Kh?T;vWUT^ z?e-eNik4~}{Na<6fAV0hAP9)-cGk6@dA;s>d*rrS`1t6(gU>|xRzYrG14!@WAza&6 zUoKgkw{NQU(x#2}st7*6Z~8ZqD~gAEPUkpTQQcoJ8V_Kq9l3(^k?_hPyz8bs_Q3dDg|BV4DhH*-=&$s*o_X9@MjM$AiEA<;OvEN z+JvY7#k%WEZC0Se`OHsdpsK4~5WOme-tew@j2zB9su_%}%+zM} zDp-uCV`*=f!0Gk}kGB=Z#+y%b7Hr_k>eUL~R;(;wt>C90m-ov7Z^!D@W&e`RBTuzl zhPHVhI%P~>j3-c@7a3!Iq5xlScZiHZZRhNJL=&JK_K8LIV>fyM&`o#xYhn&My!Gt{sBH9yt=kI1pBKS$ zEGa=4q$qi7egrdU$04=lkV`$P%I5SPAp-i*Q&Xw-XYiESvSo^&)hkf@n-5nmikL>> zq@iS|f+=Lr=fot3J)g0G+ALM5O?nfBGQD?gt~||Qn!uCP5~+B$+oqxu?6D;mQvUjm zGE|A*^!@Igt#8uHp72oh+3Ct73jHb5&)lK5d=ur8%&d#Wx;&Pp?FTB6yZ?sjqcv?Y zqVnD=stL5wAiH~V&kzTCVlM49-W0m$sg3EtPdkj=sY5)Qr_azCc=G4NCoBDSmOZ_vUM&6KjkPGL zMZ}{M=QnngLP}!&i9L0P7dkh1D9*QSs&}jJI5hdK=}W!=9S>WArpf6ekqwEt z`}EjL2iucet;u|dS|V`lOsx_EJ!)OY;&7CF?uA;-N&mW%?>bU1{HoVHwOVXcz3=8~ zDJeP~%H@=6K(-P1EBDlB#OZ>NHj+iB^8>dQ8_K~GllANBwxEnXo*0doGY+W@@!z-i z&^yFNAq{(u)?ILSLmERd1n6*$)thw`&Mwh8ukUpINQX`}q+Ys&OhtB0pAZM)lj zot)9Gc&On<^1kDf58*j6S#w1-_EcMp5H#n-rgj;}?=Y>dl`q0g&-Lr8U&Ne6R?>Ds zHmY41aeaGdHPE@Qzdmw%QGgU87!vo~P%+%PVOV^8vE5jznHh3m|MB~4k>VqFR1@yF zZn*F7^x3-F^V8<1pDYoAcwBZgLAzj8du^6e8#7>b$j{sdXCsJMVQl8I$Dr-&Ru=<> zH89#spmH+W5>JKi?ka`K5DFeZRHha&CJgNJ?VaeV5(ih3|MX0C0fa6^*9&1L3B)?*vpTd3^2+^?80sB zrIYC445F>OngWKnm-0&o>dk8$FkHiys^GQ5b+yblTa1MKaMP2am`#uOPvT_a6uWn{ za(^*=z)^6nI*88o{J)@R|`hqt%8AAYUOrZxHM%>otuh{q;Vh&Rnm+hb3TS@5* zBGuVU5EpFGcqST{3szd#5_Ra#^a<`5bk4VWY$f}C*bAhxcbE-QYY!>pagx+Rq*9gM z?1IpB#`EylCp(qy{=v5@+O|Yh?vzX*Zb%C^URlJXKpU^Bl#eVP6dLStjz^KhXegJO z6*&^NR}NKTDJhMs-N~$NojqU63W+eUGFHvYMV@u?(pO1fjFOTl?madabg@7N=&$Jg6Y4y$56g^#YT@(Mg+iQiCKdhkZ3;RPS>t$_OA)*so9IFUSUqC)k z8Uti?L&JHcHbgPW!iq#x!hg-Wy6KFPbXi7h^k*dtXOtVyAHTnD{^A&7mR+2@njko& z66Zwbp7x9(f8M>N#3`?~w>7Ld6SlQFt3CZk;V<1)3wa56818Lx;|vkq@!g%JA$^LU z8@yGk$@WCje0I}t8a{G+?TNEqfocQZUadV<1P`95MHW1O!eX|>WcShf_<+L?Z>>Ag za?4K{)g!H63|)KfW&2?!{s z1cZ*=*ABPm6Q^G({pOAmsg7v(Lm4Md^Zji_?D>7QEGtW|&!=vh{M)v)9VH2%f6^8+MCfo za!2in(5Arp>9eJTh;qgfT)C$Dj_i%?MTA@;Vy_GYW!L%<@xb(|m`w!ukXGc%kVm`^ zpDO1QMpYS3DN{mWM3i}=9HZEH6?#nXJ9go&E|k}g)&LVIhIk$xV9p^uQ65h~IXw2t zY<=+7;S>ueijkA^#Xinq{~f7KiE?WZEE9Z5Iq87Y%62Fdiu9g^r*_-kNeIxLr~DsD7`yi=I@so zpOEm5^&~P?HEqK2Gqv`Y!_1l%M3|wk(YP)_*T$mqcrq|XcQsg$Cs4ImZRp+e8FadR zZziCR4+F#_?aPn-a6z5Hg0&~xJ5@pt!-A_$9$1_>DlJ#^!P-XnFKW0(sx&QvTZEdTWyNYv+;S6o?eqUwdV( zjLZ(pRl${A&5BIq~`Zw-l!YY|oK8zj8kme`u?Y zXaR?3_tr^@B8C1>&(vj;iLbv>e#VpVUpKw~R5|{@)34W4lPuy4-Tr_2UfpKp+%)(d zR%5T8Jb2UO(@$1T?vPH6&50175w%4!uV*zRjv-Q*m~;DGTk2OMRx*rJP-S?_Jn=)F zblhuq2tp0%8)|rGIm`*0<)n=Hk59jHc-5MEl@(|p!T3Nwk(;iq7501Edh2KZX7YF6 zRs2zl4bNShYE>&|6IV4V*ClL>g2-d$>Xoa92KMIbyLGE;C8dKxKBN=zxB`%^exKgM zMJ@K+H$+t)_d`^r02vQjN(9|LRn$_*mdm@V7K%9RnZ6AQ!W<>`@g!BDr)kQN;Z)&j zSK95<&z1hcw_2>G=1#Y-yQ*?%%RQ|z1Q?OF)S&hYBGA{rO4&#J9k2$E)qB z{4SXN;Wyqz1ypefFvMG%Q!kY6IYdK8KU7E;hmtl@>+0cGJ5|cYRT#uJz;n2*?<_rm zOp`Syky$3hb_tyq1=%bf&0yxe4ITY3Re^%mb&UDPY{AT7Fm_-;hG2=Rj(Lk6V<%zE z*~=MA3t|b)KYF}#JXyg{8CDjsR`8R7%Uiu#CNAj;JM+`>_F>*DKTB7w9bUJ&Ao}mt zg0dkl@!O^EYEOP`9E? z|NPizmNQB^LL4(Fn9-UGn&}#!V<}Z8)1A5VA3r@jW*9JPBTk9hI2kGFN9LWTo631h zOD6Gohbx|MzFITvU_7sd*c}5U>qH9Uzv4oyrz%pZwRf1v*uUJ~8LVu}>Q(e)LhSIa z>no&jJIO%OGbv3>cU54YcX(sQAlZ;|e0OKs8`oBuEI~cXKTh z)JQu~YY%J zWPAV3Wr8#K(_h!GAI?W~3}^LnTS;MBA$g<2Bc~=GZqL*N-1wR4&1(t{SqCDuF45%gL=32U6(I?W=3A&AY#HpP{yD| zy+dOt@2e6n?l~s4eqF8CJhr0*@vy7PHHV=-2-7ZmhW`7nOg?sR>4X}4(>Lo={?veR0g|{A0kD61YNYn;xmr3QXoI+BdHidq8usyYn!Palbi}OZiMiEtc;4yCS`TM)-4@f9ZXMKvn z`Rmueq<&aFD1fW_zxhE6NPDLni^M7C10w(Lp1wRwFXSC+X%0&EX_Rs{ztp|f*7540 zA+|H<^U1_2QeAo1VCJxPbrld3ieA;(x|CWW?MSWtYlntEnSj%0tUqOfxxKN|oq*%X zUf6P&a|&Xy^8^Auro49e!kY)QRISE#DH>>V5-pAS)hmMi8PfPRj|GV{$fypCjjP}I z;;qFrckDd;hIw7!w@i?iz;YZ5#+<#pg!u9`U$ki}Siw6MD+^dFSi!=OVB}AT?;QB0 zS#b5r4AR`lPtnZuX#C@pf*FJPobt@?Ru0FMd32H7MlpjKg&pYTl$j-u8Rq%t8G3WZ zRSLZ-^j>Q}bL(}`WuecyAaoPfULI@C?5Zp8$cb3^PiDz&*~oe1CT^Jp7NRkt&mBAtjqcCCcHG)7OyoX!I$rLv$J>c(ESk z_GwEOp%A6F3o&JdP}JJ=M`sAs8wbA=k3-Ef~rCRwGfKtZ)>-cDCmOR^CkKGi)|H2Zb(4|^EV$V{hw_w zGlKHAJw!$`iX@YVk|kH~U%0azgY-+v@PS)v$Zc*8u*G6Upaq{578C8Sy;3W2RDJN) zy0GXnMsW&4Ewi?Jc6jJezYyH8uH4{*!*9P<_kKRVuMRN?ZyKnjI+T*E4+2|>$(3t{ z)Blg2p8WisRVatAsP!OMtpn}#Mu;+MbW$I`ueJJkt%gvpXO&$XHp;Zy!XL0mL|gDP zr_3}chse)ITlZ~mw>bUYmuji;fz6XYd%oUEu2D=(+PmJL*;^s94R>d>+piS%9G(2~ zU1dlsP;zY~NdL|w^`h&lI^OP8Y&=@JYE>;uv5vo1Y1;x;#*EPYuRmC_{5*SZcw7Iq zS5BVZTkSpH?v4>m3iaT@la+184HO%@%DM@W&mw8G9BH9T6&`MAHHNHXGUPf_)?I1a z+V0+?)piWPpA<+rYu(%zqYN1j9Wi7a)=F7h&rQtH74RF_kXAdyY!l}+ zjW%$I*_IPiK=6EfXPp;%mSCCyoKfqrR@|m9M59*gwwjg|VUFd7L=2?Tmh2&IaTx#M+V;A}v_&mMwpKUV z%lMrXfMQ#HMFkLp5hc8$9koCf9aQC&=Mk}SqqE)9qUTrjKpPlFn&|Nd9giwyKWYg@ z6r#i>wPS7e&+VK1%H0=Qmx3+#)(KRmi|945-{@^PyX=A(d#)fFaVdTtId#Eew$M`s zeJa{7AE*`)jM0dv01AEOE{BkBFS>d-3}q}U@^K?D%(_=&J^9feW87HGS!PO^pK0t^ zL#8fcwadYnS=iH+mw~b1%<2^^=+fBL`PD1faq!aG@?*g}-@_HGU@@#LV69*U3**hK zUR~ga5mkcrxiAS6Qz)P^B@hEi7OlgP|0^B2@UNgao% zyC;K90fk7QoS9h`&N_oa_EBZa4P`+ubc8497RUP#<6+m?p|($CyY)n=SB1Yft5kw5h70Jp5U&PkZ`&Et36Kq{49!gN8H>#vht{atM5Y=wVTYFCgi-;%RDCNt|>6XHed?xq1ZX9Mbrf zkk^{a6~+HjyRkxFlQQPhC`u6b9xDeh|J`@hqh7m@)Qg-x^@GVT-Z}a8dn@t2j@$Ru z2khQ^IdP_z#*`zDq0-LD@U2gWy6LlRJubEOppfp)A+P*GedLb1 zC+qI(s!`!#YxDYA*}1-5rm&$fO^`(m;Q)O8g?h(4g&0r_kDITZoI6)f_9X)EyQ%iM zJ<2d37-v4y785aj`_*B1Jo9>Krsw5@?FCPxGjYSZq1CQkWNSU*`tszaYsy^0f*+65 zgwFRd&ShH6Dyr>p2)Ut=5o^toErOvWBRY0zcQi*4&Ho{*(YN6?nl zMRtrPZ8JLDlwl&|j?T9+C<|J)0;NE$;xTla^! zfoFHZCfAdCHgLRpK+|e3NZi!3us7v9f@*g0~kx z`U~rV7BAI}@GmX3MC~Qn>R3#w8dCV;uXQ-@zxk`Fj|s|rRsLo>gBc|=BCzqwL18ejc3xx#{cfo-9NK8hG%;nq~MRA9U>MD!S<#wdXgU4hv)FA zdT~rlJIr3~E$yAztV@;omZV74;9h%wO*g9W@1B}`^uFrWy2+wFtHMcvWz_IXUKb7J zURD#Do?AE6YpoUHm@TbVz?>%FiIwlXHh2|(il^mrAGl@sOyMgB>mAYwAVbiDkOWoI zzUy!u@?T2)@Xf`HhfYwS`Q2NF2q*ZmN4C(j90rKQnXtL4uxkVSmElhp2tL&w+zW~_ zT|EhuZ8XrF_S(#?X^1!<5;3ior6b};>9XA1Wz&9@t#Uov<2ckGk`4tsM}gzuNM{>Xf`d>d>i@%4=w}8Uqm-tkj)Op`Z{y zqN)$xTG>9L=0H}jKrlbuZXe_MmtLBzYcHNlM}Opw$$#-cnIMurWhLE|2Qza66t8r_ zXI~iBM!RVmpnTq67rIB<^M;2{)>55w6TKGQrnhmq@)NAJo_2PKTOwAyHeCsYgjzqw zr36hwM8jZ+s|4-o1hT_@H`8TAsx}9%j?@6G&WM@!}ZlK+UT%e zW7keewJH3^r%V5Px3rFHFCeE`6^6%Nt-{gM!~54!n7*eiu2J>$>y!82S|da+@Ih5a z{L~P`l9JfUzD}#@ovMY_Fo&nkUbxLu7`JXHCEBIlP|TkO^b1;jw;Qom@}sDNc3RY;j3}i8#gt#3kdl z+S_igQCo+m!gITsX}L1*jO(fk|}<6&J1M{9)& zX@%MzYMA$Syq|#+1igaLRCVO;xf%4>j)5{yTpB&#GiB&)Q5c(hZJ~z&XE38`nGn)i zqMgG`K$fF9Jc~(Po_i};!A~4k7O+C0)?srMeORrKRR;=YKht{w?WC7)zsT zSkCGdENF4YlO?=bCMfj756e`Azc1o+XY6}956Xo&4^tmlunH)-YRwx~$HLi_!#8%+ zgCQMJ7>v~t876H_cC2BH+%46a#~2?fXB#6rb$tpp&V;9C_ESW8^xL{2%-?))z4JEV56*gsJ9{(}kNLN|?Cz%HiGfNgI-cU{jUXEoIuL3Po7~ z%-V~yjt-JrZef2?$Crv#oLF^%B!|Y6}7dL(}?)d;r31Sx*rVx`tiD< zgF>~K?UP`n38D0YeHs{}V9##eeud$cL#67gFW2$}LsDWXTpq7L_rH`I61R*2g2^rU zw!N*YlrujZ?skc}-}_P#Qx0&-yClhRBi()cO#MM#ddwvXQyY=^oaWn3@~m^ zS#PUXoJ5Z&3UtPStX@Hak1bZB^U%r3sWWw$0J25lt|N7Ojy^(6Uu2~~Zoh7IoenG# zG!8%6Ubx0+{7k2lq9h*5*$jyU!C$#Uwx?-HJGx`}f79(1*cMCwgXqf<;(Em5`b z-h6E>W1#~p=^&kANIUz8U=C5}sQMqiUW>f()*gfAfO3ti_A>acP1y6)n7C~oF)L&_ zk9KT3j|}lYbFQul$4^%;Y;89`Mvo1MNNdGhfj=1%lygioZU5wJKHxCD%$PxVw~LCKJZtcl$qY7 zS#LvcKHDC;sW<^MRelcQ5;g%;0AoO5^EEZ1_!IuDAr^BRx1gEn3x^^Q-wZP%CDoXD z$hUsEP8CWDnrS2)I8keV@7Xel&=Z7*hlUgU;giGQ#9+@ucB9iNlLxD9ywax%oUx}c zJp0=N*R@TX8U7Gs)L|m&5YeFy>VFBNP&rk~=|MjVC<>%mJNU|coB)|q=%&{wY(R2VEqUmDKY#hhHi z)@5L3{9G#1DJxjPJ0B|xSS$EjjkorfV_wzL%1so0G|)_wC;R36w)L1umB()+odHU= zG3GftWlZm7wd09ku%Nl6fq(5AUrHK`5BuuX()xvnOD0}<)l`odO73-!`1f`V_Yusi zn$h166~@y+$RvA4gKYNd>Dx4dFoe_e@zmMMPdY;Hivr5#xLLi5>4*m?NlOsNrc*$X z&nE|vt7N*IDtlo$dA72h{4u{z?vvq2A;Q+Ha+qni0-ZRMx;RWNNHUWXI!uT%BphZo zsbHfg>9JbA-ErOIrS_JPP$Dj#QEvO2cF&r8n9ebqWR)uIiZgr@a0MbgH;4v@2_UJY zrZga?v2qf!Uw&!wj~^V^Y`cyOX(aT9Fo!>f_Nh%3kW~wVR9oh_@tPL@mP~$cXQlLj z69|e}a%m!I3lW^+14mEQ0u3UbQ1JZDbcroK*l$^1eQkSucZuJ8Z4DuVJeuuM(aBzW zWu=!Kt5&r8UleG-ESD36?!O4sU?A2O(ZVK0uW>%Tapqrqd-9v@>8C6!U`X`hmHl6C zE7N3|P7n}nu9lS3qb;hmHw{16KKbuZKdKEsiZXQH1WlQtTJ8E_y-*3m&DRu>l}hGM z)aq53Df!wfwW_67dTGpLO$U#VVu-_#aK#G$d8koqRu?~@6EPI;pYqf|YMyHXC>%Ul zH^NY8bL!=!)2@LqnQNLjA7I z?VEZ!_o})zFPVRa1SpG-7-sPzYKyrA)5d2}x~08y&6O%Gjh~b#q#E7#PU(G+YbH3Hw*%v7O3 zBEaXg2oG@xbND+P=2MBO!b9BPf3z$?T&lw}ZCs}KTN{}btl%=RvVgUMztwmrT!Q@)EI5Z) z5SRC}5fS_480tI=-V!zA0Op;C2a2lsU*a)yN0fB_%7R9MsxrsAq{b$Ni}OqTK@wtm zUb9S9PM(=lR<9szp`@JsE$wcy+;BdlZANok!UHq$K_4?mPgbuWwP7%UJKA33dexfY zFV!7m4T1UJ-R;A0-E<0|F!m=zbLDTew~#nAb@_ z_SW?&7KZe!F2--eW@ao0Qx(T>M*~Q@YTs;Si;0-C-!^^0&8oanxTQ?VaLPpc198{BueMM&at`>rE2^5lhq^0qVS~{g7XU`Rd;CEiDXLiC6@vpry z#x+SpvW=}eNW-)E9!oM$9Gp!N^sNkYh{!tCK_Vnk1U-A7N6N$&$vMX zB8ODS%|QypwKrXSp*bxR{=x2gvp5atAG^0WZK@(tt!VeHa673{mM1vYgF}m9^Jp7F zm^lXcySe1CHrm)#kjEMi0b;sa6>-d@91<)It;?J>F@4VslP|n5yoOoTuiaBkput`Q z-hWHcAy$N|*xKONr^-~FpDvN46&1|mb)j$#!PA$43N36#;^lXScIeoS~r{6F@x#6nX(*wNC0Fx{| zxo23jVEW~Q?d-e0viK4ck-a0IWaGO^(a@xWfxB|=1^G&+ndUzNKh-CAn0;Aof^2WA2`rfuGrz#EXrYU#! zeYmZWQM;j~`f6>xzZ4wSNS?M@IZ6y0l$Q<;%al16K%BLRd-jYH`G5X(E!4(I3bT?6 zszmjDw+xzTz(a{L>^M}vIQShVVydDLp4(S#SGKVTeWXF?9n4i5TNUsP+m~J(7F+3D zzpn1miW`6uyFe%Ll$|eJa?RgyuvV{tNOuXR%`f<)w-+V1m1Tp@run(VIsC%*Vd3od zjkR{nXkV-mjk~X_h5I~AmwV_G4xgOdvZ2~u@oYD0MRN`B7Nv;C7B&}?+zi;Ox+mH) zzbIquX=d*4&K7XY4}rrN0v;MOr_NsBNrC>j+UYn`-gUUD`uw48-&^Q|Jb;)%LRbR<{R1-oAjWU$^zC3-l2F~e>2_;-g3ui zTfV|KX?i*0OY_XsUOjy)JfyIk_=KUejVClg{z7Q7m8sZrbY|uqw(G90yQT6K@L*=z z6gFz3Kj`xn`j`+cP!U;Lzw@^3rL7DR&d$blB(X%02n9$%=DF zL)g-BX>S))NH&!>M?|?zv+TdIqb9z$Z>njZ8P%iH8x1Hi?XC=e{cz(+SNQ2CE9H}A zVSD93B~;4M8ON~M0CWC&44r{9lFScpt>vfCS-HN&A%e-!Y-i5Z3w@5a$basI%A@*w zcIdfnW8LedfD;hoUX}&5u5%^`3K*mipKdPW$4t4?aSZYUqp z*FzJgJt96*(Cr86okud&yEYB}$6~+@lt`_*qHbr%v!#${pr{pa&M=RsuOO!(3Otc_ zIrz}6rLB~=1;lh>nik*eqd{}h#smSRTeY-wRo%qIoLc!OJ!;d%Wb;6is{QSLrC-0N z7S|}*)n4(Q1Q;EILZm*wy%t0SDpfZ050U?Npji6@C;h zaW4(OZ*vhr(1?8HrCO`vClhLoSagNB5nE!B%wJfpTs?jLS-TUex6Ym*eg$3jtyhPK z5D{H|zwpB3w;me0PhbIOMiC146!@H~JXuVC7SW6!5d##VsKh?E1+~={S$$G{z?moh zIPhOQP-8gVEp7(qzT@?FuxdqPrdd@AT5Sj@IfbqN`F zP-1a1OI}{A&x7t|5BH0eW+rpTO|NFo5hOY%oeTl8Fyc$cYoZ+#s_G`jv!8`_Cpf5>MIzf zu5f46$jkg4YT$P%Lj$tAUspMcz@cjz#TpUGcUu;nHPpbcaDR;CAMPgj)6UH@r@+^L za5ry+LRn`@?N#pon(&FQA2)5zSw`=RQcl&t-1!HlJleGNf(r3h^p+2U86gOtjpn*J zjyO*gslJ?|wi#jg6bAbxyNa80ajm@f;pKyr(DZZdv5A+M^j;>H&hube!P|35_LElD zpvBaUhgQ)!-}+_aj`erG@qn(#t6_ya_W$DQWRZJsmaIVtfl3>+8?K>Otr{%^J5$8{ z+*~!gmjx}P@d%$m0@Z3V-T}e&Kf{bWeGvRt_`@LHbmBFSk1U@RNgHMoZT?NpJc&SR zz^PW#RjXF6HLB@!(j?Ohco{vGDS|uSN2xL*EWZ%aF_tHjS&U*ktV#E#35Z^^4PLw+Rfe#lNJt52o(x3W@SO`-jB`62VZ%p&-}IPhkL z+kJ{xg)yPvxvqW9df6toGrlXoD-Ow-nHE=|r=tONuVTx!ma@cU1sJWy_zISo!cSeO zB-g?Sy4(z`G;F}b3JX($3GHM(5|UE1*?@(JB!IKFi(uEJ1Y7^e`$Oow`oW4d6U9n9 z_7^`B-}OlBA_Lg+>m!MhUz<(1V3|h>{g~CquDi$5qm02=krJsV~3ECKodW~ML zCt)M$R|8^Mmf~f<`1ly;UF0HXb0*y3Qunq$j5p_Ybg3=tiezqN-DE=1Dbb{|~!-PeyL&v8umNbhK@@_DnKm_20EHP~%J)wO9PvsBM6fxrSQdfKi}RFnzZ zJaPcpFJyvwNQdS&GN?>j8)(p2zNXW`x;4uR!5T91jo!G)DH(r%X}rh=t`3Fur3Gcp zmnf&j41+_W0D~r!W3uMH@^hIEoJi4=RRTfTnQ(vML`pV~VTa%h3eD#3Yt+tyD_|8=hsim=qbdfO zo|M~Rd^$ZqQZ|vIv#!4(0E;E>wh8!ILSkme7Ogp8^e4#UH^j7HB{yE90&}u5L5y+9 z9{JDLOkmDA$d`S+(ZF}_yhu=vy1dr$h9qD;i}j`2>hPK{M%Pfa!oJ4S)f?nxic#d) zZuu+t(>~|sirE)71kJhx!*_DW>S<`v|AFIh4Z}*Qb+_)AFvVs-b z3$%aFgF#f z5^^cY@%;3q$Dcf2kDgk<$uWo9jgS;z(6ex*Nv~YOofOT0$mBl3biLvo#aZI+fa3ab z%X{-WB3~s*QTnp<^2SW~;fJ;9pK%_TzGqLs`UCx;!CKf6>sC zj`HH_`N7Bj_?xdQ4$MHgThQG3Oqj3_xbXqFMyYE$nMJQmJMg&2S~%($HhJ1paht$` z3{HP1Tb^g;xAO%ZdV#+fZe8eSVx1=U0&l#EyVG30RK0FL6nIj&NcBu-Y==E2nVw|lth|&Ix-om}=L%41albsI)TvV8HR5Y->zLPtZhBBE4uhsRN#HXgs2#}3Co*H(X`so^ zmrICF%)mV4y<8Dt;klm-qcnYpd%9h=T{X|!L0<;4P_98BCxw1JNp#pYZXQcu?@f~P zC%IcQW>D+IQI_ZVDokqcAsTBLxx~4#BKM(IE!Y9?D1DLPVAY=v6_&vd1L4EH;c5Sg zTw)++-zWd3xWWBWK%oqSn-Qxbc;Cigu&Y0Rpf1RdZtEERb-9(ODOT5pxg2;g(5y9! z|D#c&^nf*I(~*Ebk)54}W)aVzt^_|9{4%+3CN+@^L@9#CA*6-1m9erTlDu_FHu zE{2g%Rd%a(=ag* z;|y+?)!#F@Y#Iczee9`swlC#y+=dt5Q6@R-%F zlr@|v8i%*lJmxXKIC+YODg>d}K@?cH=M+K)c7@f<1*7-{fR7BP`QMdsMlEE=;8HGr#nupbctHCo}$5 z(9=j)k-O>8`0Akrplp~Q5F6#oah6wERNMqOwL9g2^H^#O^(1lpKheFcX6K5Z`J*PK zt!%8ng5S0=K=T#jj(C^oOq}H_+y?cie=I{k!8&uDQ-!>v%d4&o=+m+)0SLBhXAqEw81o0^gd5flLOY?~wQIc*n`fgBC=AWR~cR4q!7 zI^5MGw8w_Sb&#l+@gSdaPHY#bC+{{ZuL+B7!mbEudMV+q|MWlSo6b9?=LfkWm2x+Y z3TueC)-So1dBim*Q>KRQRyDPWRXcCo_U)jj=xPz}#SeXhEHb>UVx)$L zTV%`Y=uvtL$%%!y)(aga*1w;~tf|(6PFsL|;}$2}RTI+sdF3>^T+0@YcoW%PdEf>D z3;tmv19GpFX{nwL75s1uHeU~q+6_+B=P(9MFdLVedewbLTS#ikKcNUUP5r9^5UMsV z&yIP)I7{fc%R<*=0D+2hs208b=adyiF1QKNC4)P2O&+}-LJTAf?^?A zM@&O1aMItRj@$JVUWZW|2XX(Lr3m^f7;Dr-j#6AD*29NcV6Fx+;9n@pBI!<0E3>ce zF;93^dgWYgLzj;ciNV~kT7m_!?anz(%REca2WYN{{>owF9)5#>N=~CH?g8w>v?Aus z8&1^t^)FkK?%nW$F7oq@uO6rw{{r->P*RR<8}Bo5A)Ix$rGY_A;adt?Q;PU5__j^< znGm*NAm)@LN$s9rs7gCwHvx)zcBkChN_h}Fbfs$@B&gBYs#Wm4Z91h==JX?npm_Y% zW>Ri2y_b99e)lq|7+m@HLyym(8}DwCwhZiET9A%=ah<_zzhl8fd2%;AA1%XB@QiazHmbb660{3yhj;DHggO2rObNC2L3NVi;kjtg z`iJ0{Au>pLz97LrK=z0t1h?>`AtL!pF%((yy<2prd&1y$XU0GiZy@q7HWZiasrg1>Ntw**s^&R2v{yiVF{ogFwKxwyGE z>?(0pkh_HqiQt?l>Ac^X5_TFFr1{K~n`srBZ_mRiKUSdZNnhwAyqej&1WkJSsV9i_ z37Feq#cc~7ZBh6(1L4WBTk$+_BjnSwoMM8cDV7#xoymVRL0|0&wDt01H3QCju9VJE zwc*InUixG}o%|L@qE!!H5l7gdOp7}^5B}gE*!4uBSo}wCGQL#u*DyWeYUW2|*W_oo z+~&^}%v8!6J8kb@*)5zB*-oA8#29U44$%wMi6oNXUb8#Gim9{Kt_ZUb)`OJ#n>sjnYIUkCJ5Ceka zx^&lfPw!hq2}xmq9QJ#KQIUmtS3^w9HY=7Fht2_g!rh+`%4Oa1LiZ$QLxdSf^@p<# z=stlJH8QBX)gN}%({j5{q{p>7*(Qy=3iN6hh}jBZu$v#ran&>mC?|jaKIU;Tjv(}} z(=OenLsNC|!fpBXYI90SBrrJcilZeN0>g(O2I~$-F;t{T)<;W7>V2as9?27P$as5F zyRK^TDaNzYo;S0Qq|PyExLexw=T8mK%0)`wfCWk{wrq_=wOr)ox2`ynPBrH|B`PB2 zKOzBvKb`vlrH=EiV|5DFCDCbsCbmVqbk;8vT+OY~FS>rTDY3KrGh7ag} zhBesCUFAuHKlm;thO+Oem?1#R7f)o>3{CczJUvtL$y*2qbhe>P#?6zt93T(UkD1I* z&qoOG02ol@5YyQgG=q`F)Z}mkYU`3FX`+hXqJnKmDseVF$IM|g4wsIn_I7u3B803X z?HZ;b&P+TN^6cWGDo2=`*lp`63~bgYy%Tin(uL_khxsrVi@iv?=bqA4rRChbFx*q+ zp-!9(5XFl|RBI!JLl$swvN_4e*!ULZ&X25(@_x@-SH1W1)!`Ab7YsE7Y7XIv0$t$5 zCr`f|OX+_jw{f2=LMCt*5cuRyh#Y-K)`gFo@@?VBetjXDnUntN-6g8?WZ>_i2%Z`f@p0I& zF+&2B*Hg5(BlP---I9`h+(6cHUjDC)wu#@lC&~;f%j4-c=Z+DaX32+}Z`4u(8TLz8 z_}(D7*JAU}&om(a8{`7>=ua629*?NNp>I9!Q`LTGObBqQ{{v+^jK4Fxs9X`tEgWae z9rEJiNg0*=PM(|6uFQk@l-?5>dys(?`@X5UuCH43pOhZDkX}}VSGdX3`E|{-Nv%h` z2mt}2N~kokv29Ms3_~B;X&d7j_ArVVIpza^C75_&EBs|gTl%XVC&o@zm9cOJYWR; zg;R3}EvIlp3$pAfm6f!@JAi8r+v1;$D6m~kWvtR|W+;X|KpBNF;bmhEKa(;5On1|r zDIe!bn&`6btU0oO#1;zMMdf@Tg(Y&CVDJfxj02=3MdII&FB6PR8)^ZYH^nOUi4b#^&jiQS#;h0O0x*;fetz>FbcpC{J$jlXpQXxPz(=izle zj>_LemokGoi&YGn=aguv5Ux9yqz=1pj(2g+NWQSB798aITVCF6YJwzO&du3A*xPVy zn97?4zQ*PQVVTTu+vWLlT-vt;KP0}eOTR$6t+qNYm;2+fra(#KIUa!}+%^gQj1^ro`++PnYs^ zH*4J^i-tMRA$O>T%Q96421dP*a(pQ~@uL@mxnf>`=~64Qta{A(bvZNHopr-lKMY{F zKpC5x{=9s(C&(}-hURT;zN-N6 ziPUXcPw|&Lsqa(@H;5ee&W%Finxr`*o-fy%9w|kL1SM+)HSj_;s4gi=xk&YH@8fc- zmGAd{oh}-ddk}^F+Y}z2EnGN%g`p|Uj(Rg~$0gzK>i>QcD&52hU&kb4j{3It6_<`{ zv$K1$`yNRn2*N#Kt*01pzwb)p!|D8aCCOn(Y4LH<`OJ`H7A6;mnU^Yi)fu!qn73Q< zckrD=npmiT3z?UTpMr`(NgoGIrNQ>wKVCt~TIWYan1@)hnx0152%!ETOA9qFX%+Db zicL)@w)Yci0&cKO(BICdL0!PnS#e~TfaPXOS?C{n;vmQx9pIJ5njb4*6e>izP*nqE zb3!py@a)^*dWo(lKLBsZRaFF4`~$Om)RQzF6LFYfKXBF9^M60;6M5wF%vGbSxl-?g z@YOQQ2o)S|f&N@Ofvxf`9q|7o5T0n0JH_ks8Tgv*iS@rB$PKzW*O=5SHeK zW4-k?O6)uOWBXd_BMdL&onyuIbF-02y`2bPmUm9&IIdwqj$bqyUA(d ztgN&>x%P*eQBb1AvFh#b?X@jro-Tyh+8_Zsu?*tS8A$gr48xK!cbNG}B91 zdhQ0d@K&x@$CU8Sft9O$te<*ZjMm{ztKUvHbY1SR6O(@+xCd+CTZxS*K$==4*8d3& ztLNG;9}XRzzqDwmwVw?C5`YS6In^dSmLRRkBgX>^hsj3^#NR=g`h@uB+1M(JRPULd z|5g#`W-kp+opoz*+~R)){$Xl+5o?OqO^3h8&ECk8`VeohSFyOTZ7H~RJ(n21rSP*3 z&P~VaVL<3)M)Z}jB2v4#GPx*g->J2p_w5u~WdKDm^g+~!$g~HG79m+pY4eI~q@d)k zsn;hJdBim?KK^+pqB`X*+oQP`_du;u1I^vA6JsNHOk{pN3isERubpGO4;88yYB;~v z^(FF-Xyp2+<62)#b$$Me)yf{(?=}6F`0{RDLzr>vgGs3t`a|}DMfj4vNns>>W2m4- z)lzfMpzN#gZ#s>~Er0$+-@PWfM;tUO#kLtA3XO}mdC1<|#T=yLw?k4z`$dLHTQ33+ zi+^2?!sU3I?*4DmJNxOKUl(HiCIjYfRfh3ZT$>xk#YWKzus{gK9~OXOvsTSYFtHmE z)ym{UJ$F|f`<_VcgRD1!i|g_g)%vdRyfgJygur9XngK{P=YG1p_@m)J_oprn#ejpE zlqDliD%QHozl!tKYeK*fK&YTmQqPs{`eGQ-OwM|wYzK5E{}bb{_v5w(UkXUE=_Yf9 z_2i9jWDH19`wJOf7+srggR+^Y@E)ErT|ij3s))`K(mzT$z1+XZzQCf}laaPU%8V5bN^4$>*#l&|IofmjC< z`R18D2Ba=)KRe0jaaxs5f7>jqV)YU=V;(^2DFJ&#kA5c6>21W~xH>^PG`S|M#jHgo zm+{A%>-zn@`NKcc2s}J2W%}OTei<&IEEn>s*Ye}5q3F|GUraIZc?adOi^JRJ9-~-o zqPzLR8RH!$?ul*vu3TOr1n}`1qe)M~lwEUFyG1#~V-mN_;spja@npKgy{<$BuB3sE z#0s~=6g3-{-AI6J)4WzmGvx^}XVz1foIwT1ZbPLj6(zn=1?RA-4!u=`FP+{}l`3WP z&H$T>luo7`xKR2i)R;x(5>ES8`RBC82bM5L<&Q%JG@L3_53|-kFv=*|i_UE%(?m6c zFm?Q@y9VfEWo=O3FNb0U9^#vsgbgp;;Ej(UUPAfg#-d+v(cEusVJZ*jTXJ%GjK{T?x$*Wk~P z^=|_UA}?R9=!&8B5pSOTIjATKAo=hcpjm37g0C)9y`e8yZ>>+nWtxYg+mCPir z>265KZ_ju}Jv096!Mq)qbon?H=ccww92Ts2IS$>E&uSzZe?JCFaL6z5))T^2z+KPy zlsA{s*J7BB-t+Pq+k8i(n4|A}KD9zRX$~KEXMvMF{^&T_-w}P5div+tVQPh~V44AS zUS2x5HvR=>ocUCXKE*G({5kr1o4 zaW({)99aWil3jP&E5NF4%{)Oo)L!U{Mrn!|)=|AjeO|Zvp~WQBYI-QdFQyV(G;<_Q zp|QI4s=+)K;~4hkp{m+aKK+|z9jg&*ugveftmWIPl^oW8x`uH!*gXrt%@uIMzuHQX zB<|z}xRanMO`)AI6Zb`dC`~KH9668~0GPdXul7tKV9w|p>%}|I`N(FK+V@){BH%s> ztbWNeQ4xcup8$7MU1)uYDgd01Xn^GIbBmt6KQpM$8f)Pz7~1W#cmB<{j=+?Vy5htG z2SX6&u@?vOcSh~g44vom<&d#{Y+F|TbVZ66roEw`{x?(|N4;n(ahD{bZBwnm{wN>U z@}-vHu@#~AfwiDkzs{xr_V7h?v?+a#Fa*dAjb7J^H`8VUEVobo-&t}fOdp412V^e( zDnHx6>sFIIYc$I-p%*n4J}%5oTtO5@I+^x;$qFxZS93p@XLyVx$-yKClbL`Ul)z+KpMfI<~OydXd*051hUB^|75fUyZIAM;z`8JzVet z?OTZROy72M+BZwbn{J4`E^ojE`d8)HbkDlI?0n74xx$8bnDed*QK%n%O&*4!1L>2; z93N+Y=GsuOJbyPru^Wp&JPKt@6%Oh9>!7C=Pc*tw#|8I3;V;hHiMAXDMfl%RMc8|t zN*VeRG$df=RQC2%I!Y?zOM9hVovy$9uNwC2pifWUa)@Dd#T%TySV=~H`{3m}rMrS) zgU*0MGztfS@F%xQ1ed%!+t5GwGlmG_Iq9B&q#_HBuNIbuwe$$}E5p`OCF}zytMYl% z6gCEl^f)euE3?ui)_#rtjj>KP_WEj4e3GH2Iq1GVeR)!_U&Now;pa|}_$2?LPF)y( z|1!6ub~UZ&QT084Fl9_;IfI$@b!3RX=f*k4&!UOX$CQj$8qVzyb@@d6_YUTIpo@?y zGy@6TRk|(|M)kXdL%{pk9?*s2?dzY!WA!!wy&+`1aQ=$X+nRs=&5V6cQPPdHfF$Pa zp{-prkxv}`&Fu;NJNKQNi}G=R8YIkBuW~11)j%*CIe&+X7zXCJzZzVK8TjQ}p0i}o z!alkIseFKeek!r#NlsLAI>>NH^-ADY(zuYyM@2yQCv9zs5S0&_U-(gi2`?9!Bz+;Q zXf9}NRsPR#GYfj4O~#2_&3dVbU=86K?@^Om12<&8zno4_uv&hNS}zMC=pe(JJd83B zpEv}`)>o{3fc5li;Gx$S6-(qGNEdWrF_zQM$?C-$*sqjB=2$d3%h@l7?5X4=WPrML z3TZXFyDmnSFT8?1xs8_B(7f2mIl$?+>4h}KWhS0ji0L<#`OMzg3himOS3$ ze0j9RU?;{7`^rkgDr52g|G&Qprbv*V<$(xa$uEx)ycDOuMfl5OHlf(^;j|xe>wy+hmG%ZBkgRX>L|-`C|eKuzLs!B zTrXACvOHq56{_jqfQ?c>r=4&4+~nda-93PeMLVj*`&y6Xd(`bbEuDQa$bn%Q=npEP z*h98-#SbEC#}j<@LHYy(6R#iZ^V$mCZDyB;Q1};ksJc3nmGasGg5WR8=>@XbH!g;n zL9=&$SM!m|r`wXaQ|BO@Pr+cx`aqq})Bx7JR12L8T4Kudv;~u~Vmi%><{hD$c{El# z?PQ+W8>3FU^DLPpX@}8|TOBPLnhI}U_1b>nxQ68>{Dd{^UU}ITuJCyz?Jb`V!(r|Q zI6BY;Q9Uyiozj;H9D7FO=GHn(4+Kv7Q!Jd{=QMIIF@gPr2%xoZbN{+k@yDx3n2#)~ zQJaJs{x?h}2dJBQndg?(GA#-hXyPB!Zv_nBC-1!r?s4^$+PE1NB!4x7qJ->u>j}QM zCq5KA{7w!twHp7UX<3xave_Ta{cfb0p-j5hn<mka|AsqRYh6BVIYD%sinL&(5qhe;OV0AWSX$ z0{hTo6nWzJxIN+s2J@OM#uNYQKSI^OVi<^-gVNxQ9!Wm`LjU4Y68^ympbvgMABhD2 zV4VA*9z^c@Jwjz$Eu!Iz7U8_DQr=}DJ zLDYLj4Ll&tRLX)xr-5-*n)THM!+`e1(8ot#FG7_s4Qyf&1J(@$vZ@n4q>?{DuJTQ= zE=EodacJQu8d_o`XhXN$C4{5RAjyMB9_*=qv8snzH##nHh*3p{gVTR4(@eq z^z=y7Ubmx~@N3_`slxuG#80t`3svAc&&N~B0p*#B0Fy$CeIazETY49OYR$CIjq{8P zx1^?4QrUakCYP~CVqq##V+5&wN`K#-4E@AJ#C%N4<;rTp|<+qHvaALd0dVUXuR<<8Xws8efXpt&+p>%g=Cne;oglTOQ?_@Y< z%x&--oM1ki77(Vi*fRpcc}2S`2AwmArb)924?lpN9j&Xvb@kSU!H$@gldFHP3JE^j zCe>5c3KnQLTrAd@d6~+^DGYcM3)RpbnUPl`wFuG z?||2Rsh*MNyG@DB;bau9p$KQv1&Hn1TgUzII+jSTe<&28?%Vur`JZ4p*Ci_-l#H^x z2`qv?DL3&hdHZ$WG{342r8yi)vs?P97>@S`SNYmqd_M`4uZ3BSf?M$Zt56;F$Ngp4 zg^H~ij$b|7b9@v%2;gKdoR^O(%YNJYceuA#X*fdzqIlawv)EsF+$;1=j>w1@TB>_T z#~(GpYZ9$7-x=6$`aYbKCU_XOP<&*;c;s~n69nC+V=-Z#sk1jQmJf`(>+9I6IyDl- z1abcFyH-mEO*_7Uv&k-wf)t$Z!EeptE(FDTufkyI8Xf)@+(Op-%jZPd`qjE zfuCLOAw#>Y#3;_eA}QHx7#Dq$xjtK-g$DG`v1iWt+ut&YN%5FZ#Z0%+?$Y`e&XZsw zD^zsCb(N+1Y+F1e{wz41z(w>4*7x7hIK(kkGbsVxTQzGs@Pb~~4Isuc#Ae@8%%JJ@ z%Abu4tL1^~X*xVKYR7Jv7;{+!A+ z!X7Ys>a(r_>;U+u$y@3NUOtja9l!&OXgQ)lt4f$KH0uoPXs|iEXs`LXE*SX4C1+P!8Qa3@NK9}8!RA~*$9l=#w$@eF0 zrTQ^QI2zPEJfPpU_z&Hz0v~hr%X{iq)SPo#l|RRat9iSB%JBUG>uC=P|$)+s2_5545T*&(R;qTB|&teYj; zVb&jW@~p{vy~;*^yuJA~{G$ZU{MHxUn#!#irqr@8b?FXt^LQy;tzTc!ba<(jI(>ei zy1&QBwUyUEJf`K2#%Q@ilu{DHlcu5+NT)A}dx^vs(^nEyB<3I|>Hb0G z+liL;pMuQ#X(A2e#YU^czt2S<$6s&RZD)peoZNTMBLEhYE)SeB^RQ3FK})YZp{L8P z;O(p&Zc-RRj#(D8NPZ~Q{d;uEGtcpNOK zwuAsY-9LSDSTATXF_6O6Q$UPI4qW`(DN{NH4_q>(OGVhzX!{dwbSN06t0&zuk0tjn z@Mro8*X-vj;&&Dbxh9988BPv#GipuKoOgXv52+Xn1Q6^BZBCeWZ0f!yji*&(i2X6P z6al`QGG$6Ij-RF@81MP;zm0#c((_Nl@YL_Vy%cFmenPKn;%&K5kwP?!QwZJ5{+8dv zZ<6~|4k0J&BVc%VrED`R1}Tl^vXx;%gx3$pf{2Y$cSdw9n)iUG3~{R;4F{n4G7*~w zDb4W#6@t1{_@Xr|tY@s#CUixg`sc*~A?NRwRjpls>ZQ-u%7A|&YQ9O%;82BnAQ27j zlkmuFX}+0~h2B?r`+-Djr3$RQbF=avz6y6t^`rwDBE*AVsNmI((@q(kTI#ScM3)cw zt1ybbshRqcF$@dX+!Jh8OBWjt<=?JX%d*vfg864|%DIVs`kT)vxDAPnw+kbE>yt1x zb!+lgJX#$mmWVv^$Dv>Z!NwGD`>>H#)kVf*u<0nYmW^HBs z#FEpN66@n^ENo#l3c5iDDw=fBY%kO7dwE2Lw>@}|;jUJH%g65ztwD@rd%&D}VYh1V zG*omwCnSnzo(wj5bz57iz6?xt^W~4!3pk5>g?Gq$=!Xozm_jm`_Wi}%7sC0lGT|`+ zjA``b#PsT1@P|fzuI6=%q2g}>BZ|b3I{nI}ey~_`#zIbH9|iKHPp_69bp**RpUNOb z)9BFDpH*13i=1u3AEnf9mE5f-kapd{I$v}aec5#8c4X+-Tw{z;33o3WYH`fC*wiHP zTtNF?kfTpF8SO*Ebqmj`kJbT!6j#EE~NGx93@Q7+L##U9`FA7iP}OZ-rLizwckiHzI$ZNQ0E&B%+y7(5<$6 zdc9#rdE-N>mFV1qOtx4kKnp%@QX^=Xu}lv0khbi3siIgAOKcqFZ+Axn{+(N43Yc-k zGK*99wb3s01e+x*p1}@_1zZ+-enk-?@Ya_NYy&#n3t~EC-2Nk}h-Mov=iZ0!QLm2N zv@;6&d2?c0m556RtEOmQ47GCs_3>P$m&B@u%g{1cU?6mfN-GP_aQOgy3OgE*E1E{@ zEBH&OI9tc0zV;2qn%XwTx_FHl;A+m}QKjzZ!_#_CEn8x!j^KZLrliC)yoGYgZtL2B> z@$3hSU(GFsqP-BQmlX&MFg&_G!d8{6^?6>~IFG_WedMmi$yz~W9JgVi{h@I(^w7!j zbuI{xNT;z~zW2Z-7T{{~!wz^rs|G(YyBBp8HV?M+V<(LtvaY;%>S5jKIP(8TfE%tn zEJ%A`YCptS;tt%x?+*@kI;n|8a`(5f=KXx+wm{iG;FpG>)SpW9v(`ei(2DEg)#PXi z`Cz4O`D;Iv>sn%Qxik!>^dOZ02eT&xcdfYijUVx7^FU9=Zw;8o<0k^X2Ht%*op66j zbC(Gm`UYG(1hqI3dnLgE+)gGJu#lshVu;>dRNvm62OWS05^Wx%aj6l_sPzcn0aGKH zL0mDHR#Wq6pW;J_#QRZ)i_qw|!KCz9??DDXBSsgLG$dFK>Kust%`OV>wBRlqQHGOn zv!2xCR79NX>s!NVJHo@mgoG}mbN*Z(oH6F`3d*o&Gc_^m>Hehd(bnAK`#4>gC=cjK z$Y$<27$lxWTtUWF(rhkL@P?o`)F@C)ByNPi3v>F{`ipjT$idg8%<*w+w+WJ%+hO+U zDH+%Og$NT5n%I*B1o` zX$L-!@1BXOgfeb=6e;EErrN;2%T$uq7^7w=UY;E`>S#f?33r9=@gpW(IM0)-dLFRd zUuOyHL4HhLw|1p$gEn8Hd_D`6ukxpWz@uxg$13btbz9>g1klE^(eq(r(Ogv3`qmyM z{N&Pn7KXBsnftavRfgu-n)X7UqO%S^=~>X}~SFTFGP)Ez5^iwx$dF2wXE zM!K{o>-nAk)$kXha2OAc1TrAB6K=zB5dg$>O(|N%2ABm^YtL)Ul&^ca)?&v>X(>&< zux#7#FDEwi!Msz)bty#?3re7f$7hV1xrm|Jh1q9Fo=Ai{%QH^?6WPK|iG7%}XU zkh8u$cC(nzae6CU!1RN3f+Lu`tCufi&+XMK-ukV>3bTXujn)uC$?rSEDD;)>u^t$q3zIO*-$oA|4kC?{d4mS zNA{f5CJ-p${8!|ZnNB8`@mQAOeC(0kpCa?=^{BiJb2{SbLX|D+j`SY%(g)G)W*Z!s z8om9XUAL)Me_0!+C9b(zC%I8B8K6Rk({ZyR*LA4iX77Te%*`9l)-S!{fTb!SdQmAp z900*VyEHdo+DnN8w3WWTTfM>5XiojUBHGbB$bVYU+ zTTXGTe`^zx3*pAe+d5hN^BRQjtrhNfG%)%uf&oscB6%mIYvBS7vJe9SNE<7stk-nw zpNaPA)X#Nz)oVYc!<6xQClOQeiv3wA&(cE?ifo$-@iD(>H*y_nf)K9*uZ_7j=Nus; z?a|Qwqvm(K1}C_0^A&PDMTN-}-QJ%*@jDZDwQ(FxD&7pTAV5kGMJJ@GE^|oiOosDf zT1ZOH$Ph(H-ucnTs+*ae6o+Ugr_3fwf6BJ5_FzA%{AEi0EZWc%U7{dc`3vn{IRwb| z(y!ud>ZU{`HN*sOsP> zT;T4COJqIL@!cwe(`3w)lwS0!P&eRxWIzt7EUU~v3csK{|>Dq=1P$A{pndGXyyZ#NPfp_k|h zr#w8*snhn}PKBR0WRl|7=6Uk`~A_n84P&giPJ!G?M2#k>- z{?qC!<~Afvq$Utm^9uC=XvV-!)$PYBJ(6anojJ%F)Y4z!$(q3a;@;wdaK`)J!N8$c zio-nh?sM1J?^#h$Y7D}Bw>NNx^D0W@D?JX1sm)0$RPO`SikG#lmS-wYRDmGe(9&Zk zr340V8T|A%o5pXeBxuGJ%BAXU$eEN5X1AVlSOuu34%!EKAIF9e93o@!A58k`3N|q< zao1#f!!3B-Fw3x^dB@ficBH=l2!;BXx|DP=6)}GbseHH1Tm+*|NhhU6t;TsX@|G&s zQ^9{gNHh}$kGCazw$vn;xGBnLidpGm?SW|-(!U)fT7}QjGJW`{na8(dTggyAy*Uz$ zJt2VS*lFn%44(=7r7)`GZPD(eOgr^&ezUm3q491dueWpz(D!of-RwjW+o zt)si!I1W2dgfTt0nM?lXD5LRTS;i&nspTuAIMzbxA8L(xTJmGjaZ_keS}umi5u|yH-xz zMw5dB|H3lkZo(#w-~oIlgYsFGkYxDuR2Bg3nq`S`&D|oV_zcS2tY2gOyT?-Vrjq{M zi%ChFMw5^h_Qs*NpJ!JxDli49=!L@WjzVhG(5;?4->=@^H7D%4gC}$yl$9Vi1LM|=P&EpRWpJss0?KPN%LSnLK6dsGUI zo9QNrY(|?&7E^Q2oX%W^ENdG6E%A0fk!FJHKAm{mX&woymIB)=uUk@+nEaH$n`^DM z6T4mfXdH{Fd=D`1Ak;ANMg9>P+0u`#h!S&!RiU3RVCmw&n}&TLOP{9xU7`N~L$ zT5H=rDUVe67{0$0+J0Arws@$_*f-wQi40IX{o*oBL;m;K+!vw9B#{NLY#^)vb55+G zue@Q;@5j)m*M|{qs-h-R8>60ve8^WQi%n}K~I{127nLI(CG{D@k!c#?cc=* z?yjjMZ4Y0sCtnOqD;d0ISS0ld@Pg;#N14ptVfQ##%}5}&^0&sQ`T{9!M~%>RX{@mq zrj;%h6YL)NVmwB_B3KU8$XoN0VaqGZ!_#^HZc^;9x3z{UcIIlU3V7wTp^|!71`YU5 z+lLtzn+}P^PeVDZ@9lhz?M$(YUMp?>a4ua0GOf(V8A>Z1Hx@7FN*Vd9fV*K~dNf}V zwOzp8MZ|h#lhA@Rc(uCNi1+L;_&?x5fbf4F(N*^&eO;(jAm@HywNj&J3}Wim6}Bk0 z*#u8(r_n^626qJ==Fnr;w)p66bFse+G#J&a*TuV6#a)gg4OcE@U$6sAG-Nq?6-@TQ z$x%6QJSr2NDZ0Y6c=gyl&BYki{i=@(qG0ls(@04&YmvyxvSfH^{1vh$h0A%zrYS4m z_sjbk6~H6rt!i5yz3mIbcAKwqOE(`M zk+)b6pE|7?*MY+dQTG@x@tgY`!YAw61=qD_#zEHk%gc>M)#Es?96%z z1QBv>cDs0iWv~;%a&!Nr#oM?q;}X*4q<(clqH6~CtQehIxVy~5>?tkaZ~eC+>%hht znomizG*UkqM@$H<6Zf$7o50wrD#bCs*(U+gCzfzsAvh%E*DE<@6S*v5dS*qf6m9+>AqR~SKj-(jPHG5BMg%#Y$Nh(^sDA)=P%^v zt4?-NlwVo%)UD5a{=r;4p))kv2j4&R8h*7B#u$Th2!dfG!>-sde^$;EksctdA!O<9 zc{;G4=xee#+Jv76TL&I4=yr{gto253qpTu2&}~eIdyHCn z#}y{YDv6Buog)3z#z+$B+`NjjTU>ZbMCE=B<@1m7W3f|I@s+-q>L4Y(6m6CN13E#) zzFeXiiqwoOM5;54vGa|u51;2*Oxp~y)}u=Ak877+y&8kmg4Qo%`C|$Gn7F9IFldo0 zZI)b@p3?i(n14^j5?D^0-$E?md9dKvc&qTXHf;reo3XNhwSu=8c_8R-8+IJ5yU;r> z`hzilO1j^6Hv)gBazivQJAh zw+4qMuSq{pI=wTiif_@AeG=C;4=*;6k@;u2v$uL&=qVO#x#mJfl-iPNNof!z-GMFZ zD@BwaCQC_em(>MBTHDG|YyKTh+h4Y)-eS7ia-trxlR0PtL@><(Z1Nu|ag|RDe}|Xk z>pq!kUYZM-6NLCIIp@FAHWWrB26+RQEp7k?f;q(BBY@tup&vb6ANx<07A0Yea^gZE z4-fO-v}FP)!Pc$DC*~Lh#4ITqa=-O(6=?YHzcQq(CigT54lC@cS~Fd+rdA)Rq5Uo$U}#Cl>6RA;P~lc zcy{mPm+vmq@$l)_>s@1boJ7C<=;Yr$RZA!Dzqwv{t|5Mj?6cvCTF&SuXAqHo)vd<1 z;9!dT>4B^C-EAEoI#D$Admk_y z8-LE&mL3Tlx&Yp>>4NnTAvb3AU1uZ&`k$nNO$zZY@t9eKw{XDD*G}0 z^`rHhK3=_ou@ap=J`51k^&MH_aTf47OPNa`aaarsRxPb?9s+pvnm4RBjh~2|*LM0` zUGWw}RW+D2^=jWT9xD-ntJc;ZC*D!G)F7{51@8o`EMTqREyg=!^~$_0O9nrh zyJyF%S8qASlZ^Ran#JY>=5v>Xu4hKu5_67J<&SUq2u|l*Oyv1}mB|Dr&(_>=G0qu` z-B8x2j0Wf^Gm|-#KGPb@205IOf=*AtzSpv*9JOKSRVmLCJ2DhI%yPcIqrUalc}y#b zOD|z87?}00YWIU+>ki}3i)r)1W)KhsTRiErRfjzkh#F#Awtea?h4iO&pb%#C?B8=^ z&2)qCtef7?MVslkyaGtc8BUxYcmE1NBSxJE?Criop_BLynXtT;HD3`quOrA4H=F5%Y(6 z+ZJfD*gbtpLh#TOrL+ zl|B9-Wo+5Uf4;%i4a1|69FAr`SaC1>?2131m3zcA2PFSA4-T}4u#~aUlN*az`N9jW z@1_f9pKNa>Q%G>mw7dCelfZoWrMfkSs_5^w$kOZw1wUpwK@RrmcC!phh!5RV*N~&_ zF*o`J^Se81y#4m0gB}VqsM08VR>&$tq~bhnhK(kxatU%hk287PDSBK1zPD?jrwJNp z|8sXte!tz9MD_=}%Z-2b%hl@t^Dou8eDFl^P@+*8KVZ+OhNmHXfF4pNj-M_HbGL6C z?ziG9=3i|$lqrM8e(2UJ8@-RV<;B7OHn8KNL*=`!D7I$=kdnX;nT28AsqIA@4s=+k*3u-^Hd(F@9U zuo7p&mVM_x+up|yeg#sMA)V7$;gWvOmiYZ!Y<{qW z{!}e?*Uy6Jt5=XmHsVsB(LyL=SC#w3H03KscPaq~VC=)ZY^KFQWJ#rJtO{n>QV1uJ zE;R$3WB&THVW9no`Um+D9QuG7Tu=G|34RxYQ532x^pM zk7$nA*(!@@xureko(z{@OD}Vnn*>Qka9oI25mZJg-Jw zRRyZuXMrJWC<&~pgasw;A%60)da<+&la&|SZ3y!AGv~^gFK(|?HZ4u|n+DPuSCuKe zZ*%=U`SUN-6Kz>UNEk_d`pNcCUh8OSJSVTesy^p`%e4a!J$Y?ERiA5Xc2KKVTL)FF z1ejwnrbD;amQ+F7#T=e+9y~euw?7yTv-YHrG!iF@0&s>b@KAg4i;no<@5&Kvr0mW3 z_q97vbP6`XId*#T%AtWLU67GUvqH2<^7R_1brKytJ^7_OOL?qd8%$@hM$p=FTRbOj zOF-0CN4K>w{2ywoNMv=8LvhRC5%9haRjoKt7QeZpV%uM|9P8j5X9dz;+G6F8pBcWb zN&F+X5BEi)8z(|#h(cgR5ij7AlUHd zsUQ3(d_(;APfgA_Rj;eFfWxW`A#Tuy{|CEkEsR1+?B8?ag++XgQb=30%U%cl+b1SJ zdt2rCl&hl6iTt%!s*hMX3Vwy(NJ}w z-345xo2w~6+L%I?-O-g<45BB9iR^QzDqEny^VAYl=v5F(ht}m|AZ!_vILoWXbNcM$ zySs{x=v>~_K6=<)3SvEbNNqG!tEV5YOs&G$G;|7vtimsW1i|*=4{ND9hcloMLbs3U zSV_5~n_dt&AA5Cp`&hqs8)`7L3s+aAkaBl#jGT9oQq_&7+?nTn%i#``XQ09s7 zO1qjZq4V;vcl0u^WvZQLo1rAsZ_>#(BfB7$SiKrgnBOYAg?+Sw6|5{^tzZQ|*;vj` zLU`<#p4nU;2wtje{E1Zdra|V}Qi4wDI=kBkPE1}qbfF2I0hGswKx$^)E~=DK zZn?JfB!LmDOfi+EB0JuwKm0RucSIs_KC4%;V()M)b@R3L4w_IP`+zy9iT-HOsxv6W zV(yOY9)^eVbg{!;ut7A$A0Sn-?M4-xy%lQ5jT+CxVyh!;p@hf2mVR>A!*olPm~5QB zyuABh#vcbj%r{5wE%8QYOlZPnnmTD^jIZ>dAcgOr9**57w?WuXxV6jmI{V?jZ? z>_b=Hx^ePKdo0m03LifCSX*h@U$=GqtMAu9-F~2|{L45bZZ8Py)>H;cedVqcNzmcUV^a24gng7lp7SE|(fcmkz{tvf{hm4*!xJP>2_R3>(yU%l2n4B( z=GE?yO4|L;Q4zC;Vjp{sRLWw_TZ^+gF-xUwyeb*6y|g32hkyNYtv!LVV194c z#Fqo;ow6CbDa6(XNOfMpdp>4XQMD};eV~bHtVF$p1&}1=seoK zXNq4rJd8S_w1u)&tI9)Fwi9P2@4vO$vZ1X++b|S!i=Ogb*SFy{J*QX^q8Hl;RErH! zA4L$D(9Dbt*xwe&)M^f&frdx&V!<2g;pN=Y$U_X*wkN^CzH4L7IT(7-d5keiGL{@> z^(xM@l|2owAFb{F&2<5}rhSw%Yxims9I`ZUtX81fs#&TawxY*A1DJ^|?X_H~>O2b3 z=I8RuFAWh02A&@d4cEGGvWU_fOUZY4P1o;k7{;LjI{wH)w4p?a+UNHbGYy?t*`#I1 z!GZs!0~c*Z1i8&T&POmj5Mn>n<8<~z2m^#|eE902I;m6cz(B(oU8LjWnSs(* zui(z>>RzNVr%|!BeG+6we2l~j#ysN&_JTPHbMy2uLbRXvc`;;lHSbt!??uxJaB#J8Xp>nfGffIJ^zxCD!#V{gPn|9CcndUeUJhwk zhJlsuwI`T%9I7R=oX>jfLH!iK?~)Vpsimq>As=9doE-|(}t5NAL+ zO2FX!^b-{tsw#%-tM>I{m2Tzl_I5pD%6Ndec#i&q-L*=Q~5JZFYKHB0EEBl&Z5&H0_P2I1 zD%+I3*P7#r>U825a4L{ij;ky>R0exno?*m5r`peKE29L<`SvcSG$h@soV%_l**0An zLBD#<5SG4OzrH>Zc=S}gPzFyo`q8#5f|{>plV@w7s>-I;tbmXO2ybc9&?pRruY%sCSTMQ8LpNKitZ>BaETg zV*SMf)es`CDxeTxpr0qYCN629vl|@h_uDqsCoF;b3=$xv)}EfIYQ~GgdVGeVBO1Mr zYJ#?^I#Gvj*xE8#*eLNkSl=9&8mi{EMFdwoLk02nO{G?3Fq7&o12yurQUK}(r?oI5B%j}X7%c= zQoA&m=!SZs+g*&_G2>)ky@GkQu&d2xLb30Ym~pvd=CDi6PXS^6ZmILr`F&?o7*Adg zovgobb`4?M)82d95sTsJoWa=nF&oU#=bIK{GM0YWdB%smv16TT`{vLV`gotMBSaJ5 zOz~kXn{sDMIq3Aq+8^pW@&GS74Hr#!EWw}zj3{DK$x}b9Wq4uGhEA>q+*e7ox%g?Me$b3+~ zv14-W+A=}I`);Z4qanU_xYACP_<(}J2!Cd8x$0!xu&y*@^@`584Ikb*vvqh=c-kct=A6D#BBu(?0&ngsTl))gEaG3@0tAHK0o>ReM8uV&Yn@Klt0^s ztL^Zqx_yi)8Ms<{AZr}O+@4d*YC@K`%5J-(O8LUlNuzvkXWjVr#GYyCWi zw{5D(V0h_ZUBP-QKoHKgMVTCCg`E;ji*M+hQC47Gd+k}TWuPnC8{FH+?9lh0sIw+- z)58$YH(gZ=Oc>zk>3XquZY5K7;!F|KeVUxsU4PY}0Z-Z}ypOc=;&U(5)wXou-f}O2O%-5EkoO+1F(YWs({t1Nk8J5RhU~E^xYUbq z`U?e*GFdv&9e!q8u|ep$E6|B^TT9^or|%68kVWTD)~)V+{n0YCF+~!Oo!}Ewo6%UC-TZo-M2r-)= z=LK63Hbg_t)^$dZW?;(&3ekW$*Pg7jY2E#eIeH?JF4jh66yo-&Rn~*Q{pfH=>gu(n zq@?yw)~~B4n!_w&TdpZ0XR=5C`i|lZzr%Z*KhYC~uHkuX)8-J!9&E2*i=Koi`%ofo z>(L9bBjtYU;tWv8QUxw4y$q^mR6$PjRE1N)>?r5BO;u`B?yFajC`IAfb9McXNN-4Z zQs|Tp7m6{WKWt-8<=ilps{C96{*CPuD;;C089mxK?|?%WkUkY$aPu@Qm{|r)Qx5@U9k^Oq=xr z^Wd<7S!`B^)52zi_-9xmGhdk*WohnExG$;Dg z7s`m^QE}|_}8Bqfb9sDGdh6Goo7AOES8EMfkz)x~~L7 zvW~a`_{moDrw~owc>y+2p*CO#Vi@6@n6!(`T#uo_@WO*t7zA z(hkCv3W*8-euy+C6n>801acnBywwtncic z+_7<>7~6Z=Z7(21bOLRva4KZ=O1Y1~Ec^*9ian$`AGy7ju0$;2_V{(5COosZEV3eK zPMJP1?z(P>EiiHB+~n!@X+lKe-)V1sdT4rI;mZf>vw)}0P9D6uo^{psEa$O;`TM(S zt(IWiWprFhfAWIo_zE){vmqRv~RYOPe-2>Zs<>qEjOj4`LuSW}s(FMV zJWF%_$Z%1g-$GoTq2;e$Ewe3`-QFK%tXA;12P+F$D|lz&r|B|xNo{$XF7aLGWvpIZ z6wC2>oF5Rl>1&X@PS({Fa#2z?to3I zocm>xEhs=G^X)z$$a`4K%k$V2HdTN#)}Py3nbpnL4wUGLNH2GD(Vwt9bkmS~<5YzR z3Tbaz_rd8x8wx0#J6}n1rwDAyhuh5#;zrx6hw6bQ30N&Iv@|SE_@~cR ziX|bBMfl@qhgUwwjX!(79#uPVqCSL&LO<~x2PRazZ< zIJXY9_<#O|%KZ$3zIYK&P)OwoFPZIEUM}(I%tDNTs_OWe@)PCTuT6gQq58I6#x^*q z|IvdN&IQ0v|6jjSxwNb=ZI4)sHJ$`e&xbM2>Z>B9W= z9aT0YT*LPs8{T<@f|F5E(u9BhSaB*;G^UG#PQBb6qowx3l1q~?VeDbH$AFnXhR2p`Y6a(gH&+#rE$ho4Wjtrj)%)^{0ni0m zeGng284Rb+4*n4N(AEoIr{#H%Ror%%VE`h4hys*!C%NR%DL@;9$d@-O7q8 zn%7T1`JYA^ihxZS10ckId;Tzy^7H$K)04u^5QLbvI4O*!w1q6-tdZY78e6ZxYb*Fb z1R}*~qMCB}gBU_gb2=r~l@0Z$UcYehd8B=l0cRKl3NeCU{Kqa}KzF!pfBoU4|BM2W zjqOQHorRcRc0NQ8O5cL~{R!LFjlZZ$E+IL!F$ksn+Dv?PcqKGrqk|?RqYP2*_~m|q3rj%=0K*!lJ-{))~q)O zkJ?RFm&LqF338uKkSqcZj7_9b_OvOaoC3(bPo8dMtaZk)q_&q+Mk;xji;K6q;_<-|}p6N=o!)gKg6j(f2J zhUJ=7!$*7w#vC3eh0HMT(T{t{bMP?AB6_SdBwx}6s+7O-(&PiTmWispYDjhD{A9Hh ze*NA$cTmWNC`%+8yV0p&Q;4D7q75yrqMX{lda=r!ymdoK8J3dpEY;=XTr%ERlzb}C zbE+*cVNM2!Gy72Jk1c!0_K6a6tl*Ro0=5M3_rFz|l|k*^qvh4;#2XJ7oal)s=nfl-EjV$uUN`>o!Q#{kZ1wQk z=tMIHKC^A|@plc;fWO>U3yy+z-{ztiCuh#pi3WJ$`H3@i(;01dUpK5-7%(Q{ObA8x zJmkzL9B*Xw54+l&Z|kiu9w?ERkQ;Le zu@+wMuZ0OlY5wPr)xB@Z@uA|@{MmJ+7FNVlj-8fjNSSU!J^Y_->oN-Q1YFi~&T56d z-r3N;JjmO}UM+JrR^l`IFWpsd*`tJ%L!c*NQHT{H29?8@pf(z!3Y<8rSH0m9;evpH z9t^n+CwJ>Oq!0sV9-X%0(5V{Uu4xg6uq9A|!&oFMw%^`aWyJh>05V{@(OytXXI6M2 z%v=TLAexO;Y|2Dxl(umqhCS5b7a~J~W}tB1LsjU9Pgbjo*v1K+V4omFNskrS+hYCe zuMDf?58qsaB(mMNa0Zl&4}-2VA}ROPtDOEJs&y)*&^aUSYAi&Lw5v^rDu@q3nET<` zHdY-z^?D6F2*Vg@MLH$d!?6a)5`X;Z`e!IXFU(-xXkRy<*@N-pT9k1Gtkhy(Thx2c zjYYv(^y!KfD5R`PTe9!9OCVz-oV_qc_v+0fs@^JXGk8nXE=S2?0ePF+_0C6DuX2ar z3RV!TEMTo*1@8zfeO-PFf8Z|(3)=0(tX^Hzw|Rz3YD<{yFDey=B_5W<(4Cq0YQcuQ za(`biX5Lw5>gegpjKv(1?atVrJ~zA# znz=nkE>zJhVawspcB@n+x6?%#K#?Y8-66B>2qx$0huNnPHadG66aATOs7k(P3L>pN zcDkB|A#-1~TdoMgKr;_eSZvU?yFMp)Ol!PK;LuP3bkwp^^J;}C9WZ(@4rruJ}F`*NioN$3(Z%n1uwmx5l!;b^cwd%p5>xVya~ z>Y2T@`txV)#aJNS<(O75nkS*Y^IAp5NKO;V|JHoYWZ}b}$Upn#V%8A*9f#^Q@Hpvt zU`s{JIqR_Yuh#v5X;JoxAt(Rzxyi5GU0p^=7If6opq)v@l`-cNezak*eeIQ!0%6pT zU)bLKoG!R%td4eALEc$@W=k!{v;vaYGoi_J_EQxRCznKw4~%BOr~+w(SGd1jOlSz3Jb12xb&Gu&Wnuk^1)$a6V zXND{JhvBhjH|bCp zQ83%<4xLt&1}9N@|w=?BbZx?g8IbO^yTZxzT%{POe@P&AyqxKivua(5l>Y~9s4_m zB*_X-?5@?NM{gfEBdBoTWI0I#4?l88iL*NmJbjE+8nZe`=+|T z34h`saZTsK{WsK-V%ierJS>LBk#&dokAaB~-CD~l@^H-2V@Q+AA=xOA&zHgz!<(?v zs2Kg#doM&LX@45X3Vz|Gga*k!g+NL{%&URZjH{TNMf_2#Huo!$&3OB4TMY~pB%(9} zsfxoW2o5j!KV?I)?L1OzC`q3l*is9itpCTSCqI8@SB- zr9^i0AD$Z8#dBBJNxMx+6*nk!#u3DzEW9^gTYb7}`d;VId7JnMg^?B~S*ItrurZ1l zJ*3b{MevK;>$TZwFC|0aczrDLWB1iEO^B5HFVUbUtJp3D(sH#-xG25<=F;pOK|;h9 zmxx4)OHb^X{NkN;5jb_WnBz~JNm-{KK2@#5AK7(lNZ0Aw4#>HH%?P>inxVb+sok7cGPQ>gZ92l3 zSHOdT)D2e+_YL=)a~<8Wu?u;|vlJeZh3d~+4b z-Fcx@?TNaKzyFp}&ONxg-DjFb%yhbdr{Sf%^^4w&tPLlflv5bQx9o{1qh#YW{u}432vPL_E$7WD%-8d{%`E~f&+uxwXcGa5U zRa|}b3eW-12XAf1ZRsr^6;(x*LXha?ZZcKh+gX3}Q0V$ofL>S{87kpSlY@wV`NhGd zB~O`!>UAW)I18##QnY?Mmz7 z$;{UqJ7;*t=sf7rJZ4V$?)E3;nJ{$0&JSa49d~T1sjUd8k~W-cpHY+2*vBYhSCWFN zuDP#XVMehjLuyl&(xevbg|R!(iM?q=l3&?)g??*GAyQk!wBm<#Qwh*!45VwgEi8`1VkSU>009CZ2+or@NunrPcBJ+onO>f{-RZQ|FS7C`E6esb z=ok3_$?9Htnbne11Fo;2@~gdnc|`a zpOgun4_l$Ax_|IbQ*OKX$>BItATkVq1;a2rf-!gnvUb+xnKF$e_)l*Pr%Yv$(E@Fb zFUN^?bVLdbAOOOsz(>XBeV}13Uex=a{~-iW$1Kxq!ywCv^Gw-QM3xxtFLO)%x*h=^ zJCAJ$4vj-3aBg1KJiGw59f_$FrEeT+%wkYeuO5hvLJzKs6eebg_JjThE+1YW0dSs9 zOoko2Y%OoLM$7;>5QwHDj$W)>05{+u0N)i5s^F$4M}oRoJAFQSgn&5+#^4b^{^Ysn zV(cJ^ybT3%?cn7Dk)=l{x_@0%pT?&McL0IscSjaz+-Ly;gh3HZB7`9Lr^>np^t;D; ztCq&*O5a9A3%iylAYAt(NaQ1!74d;H1|&75}hhmB9#wAT_*#3Yl zox*8I?T(jK%lp>G?jhpAyCLBPvj7eOz(L(os|*bsdoCV9KUwxgqNpT|K&fkm7$gRY z!g9Y6&YTY;hfjrfdx3*W5f}nz&o_%2MglWl7uCNwAmC0qBV=y>=Hccm2XJ zI5DA-BqT${9UK5t-HCI}su?o4c63?U`h-eF2u`1Gywo&1^2lh=uCX1+raS~~XbcwV z1z@VS48KKCWGT@CXnu2D3P~3?_E%v*J~yYL#GdgPS|qIzDoYx#b-Of@8tHCRS;Ow>$iw}XK^akSt7?qZ->ak2B^<{GYE0WA!ybqfP!W~&+)5?c)_xNJ$B zDf5({4Ox-d>~Q(=an!4b`fhW0{6hj0{EYfGgKLC=0koLIBm4+3TD|&t!)O6(gb}U? zbN;k^#F&3DLw`8E^0PiTXnX!owLokWAg?MymBDcEWkjmqvdaj++uRK zy{`YIr-3-%e>Sjdr^SKJY!WF0aa;L1IQL$8w@m=v#Mh>rV1VXcxrn)8%B+(0Uu9Da zLXJ>4Wk5Y!fXJK6yxINkj<_NAE;gqq+A&o(XE*>|#9(p=2qtngY);#B%ArjJYVUw7 zNDD~j#6!!fb0}k)VWa-)f%w}d9SZQmNN_?$gJ-A!Nf!dJ;GQWX`QFa>2%7ZqGtuuD zQTLBuYStapYNpD!#;jG##Q8uq*RpYtgacqQ8r_?~v=csu6=90*lU%@wpHpQ&d1hUl z_+e175ZD}n;S{s*W8-AmO27?01AE8uW)qEGh=P8;R%UUggg4EtS=Zm?hx~F zKLAHD8;#hYpDfdLvY2}PVDE+go0W+F51tMJr}W6Sqw%J5#~TNxpwW7$yx|Km@atvU zkq;C*2q~iOFcUNan&?8fZiOxZ;EojfWO>0qDx9Z;ZJ*L*=cYp#ny@$B6qYF>#nh^0 zy+40Du0LNaJILU})GT;@R|&PO+`&=BFpL3?S;m;_Vu;a1h~bNt2b#q@Mf*;~0?pKP zJSvL-{MQaPOIMWIl^m+C@1IasHkzXUnkNL%{ZDVlhX^nQ(1Jr#&}cr^O5@Y=js)x< zJQ*+ELQG+-*%7ohiVdfKouneDY%PBqz$0e80wDQ+ztQ{Wj|2xk@>ZE{r5)&Nn5ggz z;>*qspNg%o)P;*4f#FUs2vdqRx76tu`$!@NMcKY|7BQbQT4lNH~)RM_D zhU9qV2T0ztHd5T(ynJSxHbB_;rQ@6}t!*|_@PEA0`?dQTS|*a}YFRA-hw9hCh*W6- z`C1)lHfr7P!CH%KZHkLOohzRn6c*XhHCm=Xau{s`?x{}C_IRfJRDKDLmhLr_;OSFcj5 z_UPXd$##9U0(12W$fPvy$&zTP)JVQWE9Ic6B=QDMjsm7S(o20uSO?Q8618N~5Yn}9 zI%OW1-N*mTwP;l=GsCeZp*ft8Fr1Kqu1y)$iY2iF$ngdz!QCfdrpGv`wV>Bjr!=#=rqVDK2pL z`{jjD)`c|pcjVCG+=Rot1r-P!q0Rx2f#dtp?=RbAplM+(*tP&ZzQ`IRO-E`GDb2;X z4@YK9kk-cQnN}~4FX9mooQ}?huu%;JbY(RA;0B|^%@Y56;2iS*6Jkd%e zBsdTc6arlugdp*?qP{9;{{#w}}Ko()^juA|qzYz8HlZ9ld z7krw_ah2{+f%DS7aAf2RA>)Dm$>H8Z<;Ciha?0WtT?2-hzyf{&Hlguiz+t(pC20-C z@9m6uq$$EIDscEu;m>c5?LSZ69(+JYJh3sBtD<%v#+!H6u57e0Vte8)=rVF-8VSJi z+ed!fxISpZI0oOXhR1CAS%wa{`OG_!7H10Ei&4B4&gb{#n!S3u-=<_N2rDYetINU{hlaabVUq#EQ1s<1w_TzKn4Mr#)H>r+tQW# zA|w#jK5VW+*_)iyr~4l$zz#${lo#^l(scTKJPb=#4(X5fMsI$0Q}c3nIHtmwCE#0x zyHp2crOiszt5?L&88g0i7a8VCU}AdIH5mC!?wJg3OVA$IB-&Y!G0C7PTaS zrPO7AXPZx|WSUSVnS>!~vn`bjqs0fw8Mq!)uU=`kYrJc-MqS!kpjnU|pg^31b74+X zmx0=5w-FL8F}If2m`$;}dUbgi659wPd@?Xvz#8Fa0NhVpyKC{B7y8R^?F$;N5%Nyu zN|3i!iMpB#`tjpV4r^Wc`K(?66l-^HGyh!Q&^1z~zNZ`rAJ7wOk;ct*-$JckyKX^8 zT>)o?Fq5BRa-@e?7A=`B(XFXj)4CL*mXsbCK$lZTyYRlc|EwPDHxvc*~lgZbt11ev2qz|S4~R`UnDgB~ldF6PelwpH-}%N=EI$*mzW zMDBzWIJ*O6jS!9j_&R58!iNAB5x{tP4VH@F&BNh>sR;8%rkaQ$&p`mlBZ(LUhbk25 z&U5#Kz@u)zT&^ zXb6g!!#h{k?!ZI_)caOzWKS*LTR$%)JUH~oBtvEt5 z>8KHkNCHN}Uu}y}jo%o2&y)SGDG5E9jGfJP$4*pI16wj(-R68So@==8{w#9sW=uO=-dYRti2pB{UBFc(+u! z5Ph?}Kb}51H`VOU!wz1Uw>x$J$34y8Dr`@ey*LNaP%P0hDqvqzKmpfFf}& zUlJkV&JAVA02=MYWDIq5ajIqHIgtFXA8j_}SrNxSroht0u>p{ALbN!-CZ)fyr8y<3 z>95@vk~H1ag;>SAb4{Fp&u)()y?S}m=Dt+k1_xZ1fX(8|2jOFi6jRFJh1i7cOc5#7 zCk%pNtlD3K%l;Uq`m1mD9mL)fG1@sJ=spxF6*}Ue`Yyo9RDgE)N~xEw0S1xz`inte z5SmghTm&GWo(0+wX%i#X&a4EA3|eRp_;egMZ@80_$gH|3o}Z4+%DRA*=c-i$e*T$6 zq$D6JdWF>A${MsME)p3ws4dh;8B%GQQ4Wn(M=NTZ^FbO0a7e?Yj zEx7Ez#m+P4`B!bBjUZ-I$zOJH8J34*KGD_!^ZZ-#2rabEz7eiBj25s)xZc6Vy8EMh z6>-&nUBIJ{*Y zRhh0N>I|*gNKJROEUoVZymf1>`yhj)#6W0=wB;RehnF|m5Y9IsJZed3{N|~F9Y5Tb zP+0r!w;u@2av*6fGNe?@X^ef13edHZfSKw&WnL=$;m^`~k|P+m*;9yZDle|qI9Z0e zPN8pH+Dzk_3z6m6cOv!%B7;0(Am;8Nff%re6gcgnBEDxG7e`Nr1q3FiV;%yNmS-}S zig2bc#2g7>4Z@)ohJlQBjMma5VCov-hsuY;(gIA35DBxm&y*LUYXqo8t?lu{r(&{4 z4F9;d+1r90N)20ifGLbK{d49w5-OS$=>j867ioX1Gd>v? zZE0j^Pwx2t?)}D&il{)N{+Ugs3TH!%2FEyj>j;n}OUk9pAW;eV@-XZp8)7MES6OBI z{;rTm5n9FDZ;rP?V{>vU)=&40$9}+gs_XtH44iU6Gx&2;%(vc(r?HNe&u$!;=zZbN zhL0HSqh+$hiEgB4V)5eE713xLRyA)*!q+X!Lq!2%CJbUD{vYlMg4rg&K~#Yv8V7ZN zg(v&(jXpRLMRWm7YJ@f4`9=I(ShgJt&i8i4`_ z*q^#B0zj7&*5MI#DNeCKiUm?kZMr$$Pe&1Aa5w;gVGu2{(D|Gr@>bIW6)w@7!tn8B z3+sp?95MNa)((W00p1y+oW(G;i9EQsJQfFxgi>rG!0EGUS*&C+m!^H-D}ZH%qs7)X zeFyz?@s3%9^XCkXQ5k$RY`@nKqbS^!=A{bO#hqU%g8(ovQZ8r!zWoJ$awxPs^$-vc zn2of*M2Lz|2G{UmA_g=CY?DT}qzPya1LIhsto~2edeKHUX0&eob2ObWy9fmY-%r*J*O*qrOnoQ7zQ|wO+lN zt^I%2F@`CI4@Pfaszt~c1{ zFH`p?s^+wc5-t90Vl8x?a!a&z`+z&A;WY(W+KnH`A+?mWdp}yEYr&9eElqitZ3W3K zpf~F>>moolH?IGFx1^@91$H)DbgaLr^7-B_gfFcxt{>S_u_7j&QzChHp917+T>UkK-Q zC}MVpGyJ|CnbJr?0OPR~AnatDW|Z%N{O3HBXo3|h>(HjSgaozN5<5Fz!w(*;D1BH6rvN1Wy7-qO^CRsf&^AFbqsR;jG* zrWSpKj2xgrB(-M$S_B6%IBAd=Cd=0_Y&7I0?q1XUaHOQbBknlwPE5v#qI0cd0s#&# zBl+g?gr+E!z>&ga^Pn+_K`53WYvdfYZZt^QC-qo)!#!C-vKAmElS%yU!)kpJhawFE z+fdL%j=i)A__;hV5$Y+adTo&3Q{_`j3u))r>3B<8q9rCgB>~I~uLX+eUsAiW3k`Nx zuZ$Lgn^;bk7Sx&z6mifF`52>|8pG8y9vpFs%~m^LBtxv#=!(r#@$xXx`0%Ft$Z$Qx zFv8CvMhjRYjBrgbpVcdWez?8xO-xq>7_5Iz2L8e&r!JDNSFb=<(z{_$>x%kAE$KJq z#e9BoQy_^O`fu|ES}+}AsulyLwfCAa*c40t5dRdYmnW%I_|bH z!l?N5{&pvH6$Z#_Bn@&NNyAJ&x1tAP7b07mtXk=UU|{OP@StHj2_XPL_SE@i5@;!WbbSaK4%V1R z6Bx{w_C-iIH3lSU?NjHPEi;4-7#_sD^LX#E+rk<^*kuUh;e7G#2xi%$h=-OrbSj<_ zLS667`kV$ss}3KhfR?m^*wN;He5-eFe`OOHI7iCv3Hz7$ef$$eim;{^e3#MIfT zP+7bvmb#w2J!F_OJ5g8o?ZdraxwqfBWf9H5DDvfc2*}3IC_I*yF&I^d#5Q5f zG*J~9#WEEcReH2Tp=)7MI1uhUvMFFP<|D#lfdizZSvd%tKHuzElmlTZAQ*cnBI#cu zjR(Fmc%*_k_yGa{86aoHlExSHC*_b2AarM^fNe+PQjmQ_kuOu{d{-+aL2XJGnH6Sh z=F+meSxyplmVqju<%<*s5~QwP)kVT=i=Q+^_ihJ3n6)1VqJ9Zi3*9if90Ai~HVi4c zLd+J@72>MfoX=OAMi}8b!Ds<%gb_YA`15lWSFgIrU%27Fp!5HI9U@lm5sO_s{TH&< z$n5YPYQ)pMEfX>a776R3dh1rt`%BTXFvOt9N8Yf*Eh+L{3mgL@H*4yHX`;SYXsH-& zD}HGS)9wu){Cc*-Y(bgf&-3G`ZX9%-dSw=y++Q+n&ebcR3!?%WQ4AO)Oy)e$Sr^Q5 zSj1$B2^(#@bW!NSBvq`YnJ=MYgmIf&UVpB4AOW8snZr1JwGrTRWbKe)$^np}Wup0S zh(`?EK%_g4m}Qj-KZNmrzW=^<08^VZ5nXA$Fon7*hsrxX5Z14oVtiRLwZIxNSoD|g zM0>*La1G)+?}sl-Ip9QnZ;j_f7&%(d3pQ1uBKES2cMwKsJ76jfrgmlK`4{iRN)4sJ z7R$#^bP9HUIJ2(-mCxQD0~GYA%g*3fEZxv z7+dhl$mbG4>3hfGYA`VwQ#fSMvVvSgn2kfz-4T4X7NZQoppHr$WnUHX7sC;cOPpjC zHYH0)ZknT%OBU(a>8K;Mwmq?iq)oc5EIHxSz!1ppLvcr0Swj*IpyFBK=0$`*Y)x;CvUx9 zG}j9GFsTTa0thRpL1W`cJBX1Qm>$V;Y+l`*aUxZcv$|YSH4C)-!C3RmDcOsqqiYLM z@$z${UpSVk3sV^amLrpEDZ)be6r%xY#{A38`=zO~R9Jm#XH?K0oOg#$Mh5}-#$#Yc zl5mwUvA#SGtbnP%voKqVAhWvY3bf$+@>8ZIKbvmWD+6mmX18>pa0>%fvUx9FY}Sb9 zgRU5y*y!Fls?V2v=sCl>XO;+(&)DO|qP967^0Q4S8mI+O^q$o?Gt~OtclaN;(sicENrQFT zMKYDo3C3&ssVS?|2J+!E7(hu1fzIA|;8 zU3iDX5mUteij0cTu3iC4nKrm^0}yjw0g%zGQ>iQp%Vj}C+D9iKn6)xHiqi6k;)ClV z7&t&j@PR=R29r-{<@Kl+<}jpLBtZ&(ushzl1MPCp$^HXe2tWYbEn5=vD|PYfI>=}H zt4{kTdav$pCYHEUQR3zDSearmzq$W(EeCP>6c1Vm5HlWEmxuT$!i(zAa1i5a$ryk` z54y9=Dxkiyd~hIM_Ec8lesZXH^h|_Dt?8q$J`plVdIiumc;!I6YAq^N{!;QAWy=x* z-`*BAVhDvwuXz_3*|F0z!TSy`KsYM%qbArVDot1RIWgM(8o2Zk;b z#=BXxz^U`S|Mkbc&u?j}iMrO(1sB!5C*s_^*o&9wGe}Zhv7JQ48MLw)LU4eqSBg0R zYEUoLkwMq3I_Ib3;aQ*sWjNkG9FM=zS&RGQ47X%zt@??9E5w}9&WS6xaOJqVF%0jZ1)=p~lMzM` zqXn!HM)<^HLBAgU8=(Xt5)Z?=BXzNRF0m+#6e?A@WC0a8-_bK9fuq(R>NrGP*M zzv;S8ErQmy9FQ_9D#HxNo79v^T_y}nz~o>j=O?CcNSz?TvD7C=Ab^`d2+j6?yQ;%M(g#PiE{$NR_-R&n1%#7U_jm_6h1p)`gKvqyjz>=#q!pO`|+mLUc_0s=$JHjzV-lGq^(beZ72Uv?OM>FyZb zFbx$gh_5~|Q)Mh;kSa$}S$kUKiceX1QjGXjJaj6uxZ4V6bF8E&g9YLz20#VLzPvyD zu3s50;1d8^z6Zw*1qj4~PBDv&RUoAb+Jt$b?4bFVkHwZW=8e$6AOX#5Wzix+m`v1B zHvs0|F0bf{PgHdmQs#yZ|l8vI6f5@ z6TfBQ0s;Cfwp^8^Qu@Nt8OSris%SV!+|n_LFJ7T^%8WYe+9_^K)ohw``kbfB>;{Dj#4E76B*O| z9FW<1^(upGeKKg^>XqtO5k_ds&xtu*bLr^H=t8~~+*ejqySVDL`x%0l%`3!JY`xLy z)d&MITEH4%gr5Oi-Cxro)>=i-v)y%F23n>DnoVX#B2m*dYCdV+;SE7IjDdkMH*oKL zqU*iOiH~_zcZAN_63BONE`fGtGydH6=3$UptRq~Tic3czuq1pzP%jZ;fW&rU^_((mqQrWsl@T4;lWz^qwP)u|M7d$94Q zW=8kJ-Lbs#L0P90NapMjI#h0rg%a#!HNWvl)@ECsS zd8Id%LYVnK9Hz}1R>ddwHm-_FA^(|8;ppitQ454QGC~!RL5m8iZV`T!#GP#$y-%}j zi7GI^y;vH72(1M*g+?CXSVh_#Op09kS+OKOV(65{O1aWxJ2VYd7P+z)2HfSINuas`Ji9$sTcTO|L+iD&1?+*cxe0d^A0;ofMDVy0A+^`OtfqVCiX5c z#p}9CHJ{%d>n5Mx7&6Es3?Bh)S{EPq%0Dm>V;h6Ej6KLXxPE0UV==DaGraSGI{_0hwPFX# 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

          rQ?-xuvTNH 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

          wC0Q>kFZqv4R{77= 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�lbI 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ͻ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%0DlO_eq-vj6!0u&1+a0MKY!H;kN@r6N@Q(D`W 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 lS5{ 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∨a!X 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Ⓢ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≻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

          _Y|Q)%Z!^2 zd6l>37`}u9(Q1K7otoB3F&lg|Rz7}T=xp@m6ck)?!LXrUzIJc?N9Bfl7|a`(xtK8e zk?G1A0OiBJ=scm`0q;}LUmUFal)5yHm^VmcT-*?wY}@%1Vsn{34!cB0C-aEUx}I~( zWx;Rg`eFdyvo%8Rre9y^D*p6w1%b4t!-U9fRaW*wn@QmvE9@7s>KHuxL(BnDrk^>r z%kwEX<2RiOn83-sS6STxdNudUN}5Sm`0VAi0?d1OFWW467P%

          oc#<>0u@kXCR|* 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 a|_RAC={=i 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�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

          NS!>qlC@R0iv2~D>TJKZO` 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

          &3Wx?*$d-?sL{cs36 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�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Ξ2_#&f4SwVgz~@#ll_m8PN#+yV-J>uPno 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

          xxf&j;%Ll=QRM4ke?006NC#p4=mIL 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

          s!_6bN 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

          0T>rUXa@x<6}ADGzPrtk)PCr{-hy|t zqSYj4j1P|Qd#*tPy_zMOw!Cs|5}>NN0lkLrYfI>|Tb|tL8|j?)+J*6ng&G1aQk^TV zeh`glV6}~PnQbk{o;++=15~#5IsMe2aH*rE&o5KxL$p+4-PDBowSqdhzZ6m4Oxj@K zAsMKN2||t8HS|qfp66t|_P|LDlX}2$nH60AQmIrY$G~Iw74iDv0{>`}Ri{eLzjGEw zlHE6+`52$Pgw^?=k<4^rFc^H`A!G)e7~3QY#$qd?7|Ik-X2URzO6T=A`w=2lH^wefMwa83z(sCr2=2jLY?dLZLv)Gc(oUsk{xv*Q@apb7)J` zy+0)pXR{mo!v5P0u59wkg?HMMtfOhH(Y>V~J9z>ayZ#>`JAE9IUwsRn32c2Nl7GkJ z(g`ZhmqUVwm^uc{6IFHcLEPp7S6`;c-{phDWUAW&T_XDii5|hxoUS#lxKiU$dL5vN z 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%0A2#FOK<-^G^=g4 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

        2. 31|k$h`^xv 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⁢Cmn%s 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π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 z zpkW=a;LD=k_`hL2HhGZ6Xt9TJJ7>eTJXT%^R#&bwvrLa5WIaR z9!J^5%edpVYYKvPCj%X9b(h&#L$QicHq#V@%CTbzu#-O)iS-26Ja9}?=c_GIj@AlK z7lAW&KNiL^V{j~!3zFz=c(0}eW4j$dHY4cqnIS7XRp0j{xfWD9%g$9xwQW7oCl^r$Evd0AbsV@Nu$2;j!3O2Z3)u!VBSf;v=J1MwRk8jM>Ru$(TpvpcQO{dMC?4+JkA zHwVdr=V?vdaQLIfOnbfV^oTBvYsqBQ2~h&I0U{TqiV3Ie9u?CIOiIxH7eQ3kRLtOql@`JL}T}dny8$T zPQd9KfuKkFsUV731!A_cG*vUvPjgd^kxQil0>N!0BMLP@N5cCO0qY`EEuI?a=9(}1 z(K}>B$K~<$P+hAb&fl3=4fF7e=GV&wN2R6p8QRZ$dwM$cIh+`R#;Qc)SeRmj%(-aw z;;K^3X;lHp9nB@~GE&4soRHLMguQ5uT0|$JE?jk&2%D%_ws~QE6eB~d_?3Seg*g5J zy-F2Qwj92sGrcEFD3Vg|Pn*;&unW~?56X%k;t-DA=BY9+mZPa;6?vbd!H#%lb!&

          ol1AXEM&T}0faz%a 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

          t})PQh@IVcdGJ`0gOUPG=KK9 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

            p~195D}+ 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

            !23RsA|vi%e(i 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

            PM5F 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 z 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%0DNZ1OQQS_HXoWM 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%0A1L)ZH%*eB1VY3un_yoPl@TF4S89Gf7biv=u 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 z z9`YVcZ4fW(hxBfOcT7#snV!Qir} zx}_aGwt;Q@c=yjD_QV6=Zi}md(Z*ms2gvgXHg4$Zj%tO{Z%3UOGXI-=?#< z8IpJxHBOqo#ss&3sKYnrHwZ;L05l1Ftx~L2w?H(-oX%@+W?e&Vr*74V1sCX5ro-o* z2tHQt&l+>;oU~5(fzmTc`#lvkl^~hgnrjG*M3TTzb zN+i24J|aQ-Px18^NTJJ08Pv&G_ynYhSK3X}5ic%ovGn;F02TliJhL#LP?d`zizz53 zhv#GzU`mu5-~%ByY9#u2-3Tq!0v0h-W9RiL=n9%~PrB1`B~BTT==)SN4~p(7b>*H0 z8=9ku3*2zRT|v)y>qWu*t&8vaUnY`OvJeQV)G*JL zqgS0B)>li^b#WqrP!^kT4WQ(xFV?H1_x===)nX#3GB2(`Lab}T-q`Gahhb3oZ~_lM zG?hcVteb)|;QUldSQvc7iNv-w%HDE-YZ15*qDjPok^N&*Tz4BmJNaT>k93mJ%FUJZ zZJ(h|Jf%vAz3#V1o-uf`rQxa)BK|8dqT(^=D?G_nmoU0Sskmp2#6enYCHE9BW%#s| zrcU3kDX@{ZWBzk_J&_(fF_y)(@kGv;mRUN?>A=F{o~B{<8*43sz5;Kg2-4)UjsHH;DH36O)8=?DtbI(+c@?*fFA>Y%x1w^4r>_Dv7zI?e~6I%GiNw zXHgPWiE-%jV*=)cv4}~NVDh>JH7<#vjF`CK)*wP2=*?+dCfCr}4E!`(%_o5gjGS@V zC~HYWCFvZ3bGZ3YO;se9I=m^OG}!z&X2K+aF{a2uE4W%Je*YTwpP@E=@f1s?CHG6@ z(n6b|J-WgJ#51aD%6CQul2a5j(vh$Y(zyCL+t(MHI@38<_EAbs+gNoI2Hpe|Ft4jV zDj1~RC@dqQ90XI@Jy`Km5usvsRRgWPUjg6ZYZKIB{dds|WuuP@OjO;|bnGzoOP#P{ zW}c855pXaNtsO|L8D^7fpRZo9E`#rmdrto zPNL-}#ps#U3nSHsH%SK|Mp$)55yhagoPa~I`meZHpOHt{3T5voa7(---CGv_CeXqc zRL&U6MGrx&{XxA89}uMoG)&%JR&Y50NT7;mr4+{;%;(xGsepjXj7t5TD#V}Te)|F+J|#gxVbIOop_f`7 z_ba--6@_ltUp$dkwZ=-X`EoFCs|ZX`bs@|q-+CJ2UogoWq#~TaTPkRxSynWbeR2Y2 z=M%{OrpdytBJ$;atveb8(#T|c4;!+hskT0Qda8@ko6goY=(H%@oAJn}qg1%@)nrSM z)~%RW+35kK6fi1t^xQA3Ec)`n->S#P)jPNEYvr%UyH-Ub8|+7^{`Td><;e2cp z5zv|)ik>aYTkyj>q9#X2dB_j}E2#_->@RhIK=+TG1tJA##)#vDZCCHsqkkqE1eAhD z!?Eyn%U*}_2LSIiw5HH$A(ss0M&#=MVSYE4|Aci0B%dwqJjgBE!4r!wIB&Ghe5ph> zaL+oVzAsmZ%ZwZ(XZDWV+(+~s@3K^7H07D&r$@lEJ&x<+W1EH!+0-ooVcYcCB(p=9 zRkNOOz%%O{gBEsYc(JzeK2WZJo}(40pG44~nJFHZ%{>nCL$emd)t3+sOy zP{g+^FVa1(WzcKl0p5+Qxu~qkgZySqz8I+m5o1H<-WHsMRf66-vagE^e~Hj~xX5dT zE>IZFK&(zm|D6)^+qY0U=l*xZFhlLFrPATGP%Fs@Z=mcDuYp{oChF&Vk9_dg-!#~r z-zhh~izv+i)mZo#o#4-2h^{^%ZQa2GQcY__dhuakse(#!j|q^IrGh1l1A1T*EYUgQ zYUF5)1$q=9TS30WFp7_Ke)|^kh$CaP63z{j+@VP*RJumCZ(LZJ^l*)x*3>}`zC5J2 z{s4DmSb!7nD9w7CDI50<>5#2YtYq;#=kyeJ1=(VJ{2J9DLGn?kx?ZRarGWTX8V<}z zg?D=7Ql*2cKAx6}x>Phfgg?wM^Lcd)zS!9#5p#@5K}6oG)p@=G6b}RH%Mp@ULgPmy z6FQZ~MdRrhmF^fV5)6l=%Es9+K!<$8sSYN8qpSd)2VRCl7d%^CL=#|WvP_2hJ#UW( zlGr{;6O0f>3L2Qz$h%mF%SeK`R>t)6n)RU1BEPBPbP#o6+QozuC!e;E`(}nI4Z_fO z(^&WN@KZA3{!EvpPb(Z^9ZP}#h?E!(?}<#Dh{1>;?JCm(LQ6Os8oMMF0w0qc9_V~$ z*h4K71DPa{+Vn=goj{^eQ|Bhdn%5WdtkE$R>`zW;sVwN|rgX{wy|<`f@$a`x@l1F@ zm(w|B;|6EM+rLf!L(?lL3&wKGyW*30I~9rnwn0$k+A~4X8f1dwxgX5>d;}GlyCP(J z*Q@E*wO1P0n?I9iMHnR{VFy(>UeS%IVvT~~DGds|8rx<1sIM+Ckhhl1^SllWB+YP* z4QY*Xj3_@K{s66kH1}Si#AW4eg7K4e=SQn)MXY%%Ca%2Yl>#@Dat2{;!UB8(Xnsi1 z8%ajQI37JxceYO@f%Q%G&EmLhurS2X?Nf=Zm2#ZHy~h;{&CiDEr6)*}!4q?PJkW=Z zRiyy(JuK64xVDXTu7yv{{5O*Gqc7|Xq(z&v`p0NNImB}40G)S}KNQhJ+4vdAob?U2 zS4^HEZ7}Cr8yG~woILifGescZG9xz@xT}if6Cx4ASoYdR=>qioAwq6j zJ)6-4w92oQHJDkS7`tL>zF@x~_a_|H9z=_&EO*P-w>S@ zJ8fZ;szf0aEIa+mww5r$pzWlvLRNQxt9a|D;`OBc9xV}AuXS>a8}a!H^mp~5lnWz4 z4>yN+`&7bP+U2)Ae>B>!T*6jB$%3DQuK`-$tj0K40VwmC12ry|q_6Z*2SKc+In%%| zl9K9g0VUJ{xgrv?kv~-r4dk4QfG))LQ^ZvM$n1u=eOas~aGk3`(j{NZ`(q zOG(Qq9#ni%?Wi28;$R2Mn?)i!?0t#*2mowzW*AvZ} z#?r{*Qu$+3Vim32<(S%FPlzmXlipGwZy#1&efIP)>dnLzlMhsbTa~$n8f1L~NT8kn zQoL}ig@*-U666n3$8b|Zi~W?(1q@Q7%yto)=`zSna7%oj|DxJhpadTP$gnMd2ms|2 z>79{bE~t7!b?8(phcoZIP>4Jijay$&UTUWL*oKpnMdDy(4yP7jEF{rVbK)Pl4H|cAQD2` z(@zC;l%%})O{dUVBzdnEJ1Pcjh(3S8-b9fq;pJE*<}N6Cq{$t zpnfsBoRKr;;b!ov@Zu!fP8pPP+6nE6V8q8#=jhd4LwOksV~USZh#~?x;x6Dn|G?EB zMgwgCX4CgpkF!1WWLN7e5{BE~Mp9%_XsjaHpQG+^e~T!%(O~CVaZ+}N1sNx2g5j@+ z4013yMS}E;kX$uXcc#O2%CCA;!GAhpu*dk7T0+`#)ZDDN#TxGVq{!65q7GRh+Q!ih z6wMo3ua~8}O2h$QMMdVbApRKH9U_|8mNrL-^?XMfXfaTj)3MDW>37t78^_ zAkS__*2Qng%@fv(g|1hZ<1>Q;R+VK%rXA=>U%2o&#W5(N@)2$s-EzHe`Ja2CubA!_ zj)soE>V=l>r{P1zn0lT9nP(EenWu_5X00K;9|!7+m~-5a@i!a!NRFR-hk=y5N(_az zxr1P^S(Ab->^@l{re(@MQ@ebmy;1#%5oou`b9Vbx7IjNfojAm|FjXYtDShGM^?CiDqHGHj{oYG|K2W(TzT)w99L_wX0VC879(Q;b8S~T%pz^ zI@CaE&WXHBvcVGLcHnuzQI4w>JR^kD($BhYF0H( zl&;l4df=-lPjB%O$tXu|X*r|ZyJaw&>R3^UP~&qtUz400S=C3fuw&elX4+PlwTFIt z#){{avTlqdu)fho-N9ltv=TqxmJtC@xv8`qj5mIqP60uDTiRNG81Uw=y z*)XqfhpzZke>+c0WR7f?m3ay{CWpFlr3H*)QD%&|VmPBnLMqJU_A`e!x#61LVoTtz z7KE4N@fKIomIp<<01bnYNdniszuEmaW6iG>9aug5mOKQHyi#~$T?N1&mr(5(ptpy3 zshI1Vw|66f?MI=$Lhlm~+HNa3qO?)u;pX;E5167K(Z)+XY%ADO0n2KZF)K>o;`6FYqux2psD*r9)D2Va+uM?>bq#%(w0fDTX#f+^wfAO89mxN{j^DQFMwhF#wO(F-WPUbS0^>+*j$R1sn2g!YQ zU=It*hIRWZGeR=5uRNiZAWPjY@hNwPxEexRX0R{lS6nQq2OM@Q+VD8o3Be6f<)$pD z@hZ?$Kds-jy+YDK&v$feJRuu|aELo!nu-qv68!tX0Id`DCBnu^46_a`OO7#G-i>zC zir}K zZi5d&-b83+IA39fJh$~>a;X^<4)*z>^0)Vkmz{?;=%sS<(2;TyR{V!R&7+rg@Gop3 z7(wO`QsQn7fQT5rMAH}bo-L*-PQ6?Nd6+`fCwS@Lfcl2A)*9fJWZ=+g#E1Mq%{N^= zflWhG|HU;myZhjC51YNf7w%|UL|UB<6${TX5BaKGoS@wAsy%uYUIO20V z+D#>VDNLkA@Hd2{-Qv!LPvL_aOhRhl&6rzKwZr2W8o*K`M%O5x&<~xO>JQ??Ku5ME zx~=+a3S#AhRM)cJoHx%};F$IXRH}VcTDcHeM^>(uoyRsChnL9sAdSw5{oM@oYF&)9 z9{FoED7tc_YOm1T98uV^Y!{Ap76HfQz?AzoYYqL>=e~Dq5$d}cx9RQzxDEjSKoHfM z@jYQ%HvALjTtj`fjP^?mZ_51sU%iJsS>6S0{)E-ow45wYSmtQ|6prl3z&B&dxD;3x zFPW)PT={griows1pUa1x8Sz{z_S(ez4 zIIT%EG5qyOnTl4mA*k6w?93?n_1tPN3sD?DoWm65I(VVV zx#Q9>@A&qacS1Wsy7*?4wie6iG<73?V>$|mM!&8#P9+hXn^HWHy+__RRvLoDeRq`G zN7Wk&yiB2z<9E4vIe5nRa0$W{QnPDS<@Yv5Z$ zc7v0%OcWo2Nl8L@F;GPap?W!L{ce!tr-dYEAvWXV2Ses00B;j6`umD9WIM}Qkhi3k zeZQto{2WwGkO9z%cOTsZYjDCiVYc2>J$P!Q)^Rc{GIi5KI)OJ+M)%_J)vm$tL zKiuj!k)qIH1AGulX46(Tcvz4JNfJw(LMRHLFn7KhY4eEw!7XgHXJJ5<{g3+1={QLvcJ93R93| z4s?rgHNl@$3EaPTwS|!46MUoNf4>ZLwLYM6$4^WIW4j%emjbs7<%|D@{4V`XD4Z&n z5vS|ko1lv6vrlIaLEk-m)NLLs#zU2XrrpDa4a!7G>Cg59hs@*L59?=WV;rcOYHN>( z1gjT8ev*zCnbudXANVb~S&2$X%VH=0+SH9y8h%x&??WoFSxQgQr&)=4ogjH7JU9R? z9x{Dc)@g@{!qbl!Atng4u{;A2F}Fr*b>brN22mONJ#Cxy){uD@D)?51j)&Pa1J+*# zb?yM$d_M|pog+H(W2W#BYE8&^#o0B|pN+WqHN5i=N<;6_(6wpl|2}D+0;O`_0AaGi zAZe@{X6i(GUTidcn6zcqK*jhUAugZW+nEG){s^bk((~>ZQ{uPWMqh8~T1Kk`gaRTIWU~s^6RtUJi zXwn>k=MndDXar+VVbGa?3o&q-qv$Qv9s`W1JN`VzW9%4529q^E2_O}3JkR;|ZC@$h z8N1?Z*xbmN4;HJefDWkrZibSd@lOD=#`cfvZf;YuzUjV04IPU11@nS51I{55`hMfe zV$|dQ5sv)rKgjjdN+;47n8wyDI=%%3TT>wD0d{;a+67;sU;8Qml=6_oR6Z+A%+~V# z+Ob7bk(#TkUnl(j^vqy zvDLbfOF&5lmB=g^r=}>UcY_jf9nJMJ{e2Eol13lhz$|kcBg?fuAhUr(7%)R`)$2(^-oO(wy4N|;jC*6!B#?~r2M^nHu*?lAhK#`7&Mp;aJUk3h3E{vTD`6JB z`}5}^fxL$@pT=~ZVdwGp^p-~bsDXHmL9LN;=NFk2T=hSvh^p_an4mekQkb*6z|o!e zvUd}I&UqdsJ@!d;xZeta@Wq1O3jsG;x>S%RmgEMUicw5y8H5JOX>7Gre6EOrE*u^A zQXkTKlSL84oBGBD|Kk6)HxquZ%asa^S-32L>^2KctdkTd7o4-+mTSk?sB<|gsL@Q<5 z%W^(N1Tux&{ z1ASHSRcx^7GwWB1y0fmHP5-asJE^l>Z{o`xo4372!9LskQns{Nq4~K_r1au>?50Ai zcGnHn3)P|LdR^x0r>gbpGYWoLM|EuVg!4|M!*Coix~!6gwPk{$f!p+(Npc}dy{{Tf zm)tuE8G?v|;pqg@4U`I%_LRPvSwRloz@^t)PM0I=NSw}2E8i9UhNR3%xC=rFE`Rh_ zDl8*zuo&Q~21MCf2Ku|;srqM#jjOhQ}_*#<_sHmS903Zo-4xK=eQuvv+f zudkBS-VWWXw6QS2okHLN4~qQ6xL|0;*te6qO@;G0{0eO-91w6EVLG&9<~VUQd3r@| zRN?9XvXu5xPSxW{uEMJ01(69s6=Ua53znF2gSt6@lGMiv$nEA7mw{@bF%vcZ=dgXA&5faAYcr$f=FQ-Q{k?%rP_kES=eDdAcMhgj!)lZluPQo>8 z24HoQp+R$YOIx4&MM=X02qw5nX#O%a8whBeL5|R9LMWjug7e^qdmSsAN5%_ou4Bao zu+hihx>I?0)yJVjycy=Va{kToAY(-v|@yX2*tX`&mqYnQ|0;kEQZr&a1^Mav;oWO~`Q#-)q02 z${R3|`6K$uPUqN#97(b9w9*B-f7?(WaQR4cPJ{+@Kw6Xdy3kFn&;1u{E+k6gH%`3E z?%WverdD!Ox8k!UWM&0M?SQwZLxvJ?*q7Ut)`r=r2ioVHE5cEUtQQVe1%ALe-((|n zyVrCT{oHo6<<1l#b&t#RZ-#nUes*8mD|7-l3?_nvL`hC7+c-wxN-O0T?b@JS1uP+( z^O}))!KHx6f`J8_{rRJ}peH>K*dL1p*Vj*iVnLJ8-{$q7&wU^=3?U)+RZL3Xwc%&B zHbMCc=~-74exHzJK`)qKr8VW{w%(F0;eO}Bi0HoDCa)Yadq-;;^0R=mKgkY$`n|63{G*m*NDYUgnb;B{@ZRA-1%5~-U>@!0A)q?h=WmKVjGTi*AP+8%jZCL4c~ z_Vc2i24OC52MZBZ|NFOuEG3vvecH=kW^)FgOFmTt!F;)%*j+U%P681j)Q4nNP<<(? z+zW?!4;qlW>2H#Sqmb{Hb&Pcl-?NpOAzxO2^)4-)kF&>#L=_2p@pD&FF8o@_jul2!w>OZ~?dw8-pKuj8zyPs1nVAoA(;Fq4*Qf0*AV48PFO9-Tzbnav()R z-GZj-KJ^Zju0s_!35zN9eXs*K^Cn#ooouM1Gs|hp)ss2zM^L$Pa_Xn`mq>s1*&gOs zB4E&VpaN`%`7v@fe`6cZ_f9H^d)$8l05A_E4>^fdDJ;w@3~G7_vmShCXN!d8x84sbN1 zT?ZK1qal=3Mow=d({g8VvE$l)1k2!<{M4Vdr>L$Ye%jC?7V0hNlbf z?(zIcs)^)B=1vkEEIUa-ty&5tm!!$P66HS_Lcbmi;y{3Px6j_mOI6DVf%cbvpqZF} z)F;pJo+#1 ziZ~CUSXjMoEq(vEUg&>|Noj6gix}iP!kVRZKzC$7-*zC^M8H8M!$a0QN~@yaOK;It zHBhUVeY^U-qP5R#F@*h;tR1$RvGyb0`I3au(ihrXXFQ7{tCv18vm)(>muw6eP9QT? z#NMPLNLsG}MPA5H)vU+o0NxC4<3}X9VsDYP4KejPLq;^i zaTCa}B<*QKumpc(Mn-I1(a{BS(9x3{MPO{=#bzJMC;WDR3z-2C56%~&_UzayRuKFz z1vlpPBKXjGQ_uU9!fx=II0>sMqHY-%Aj9i?%n7bvkATgCpJEqPR?9(UBH)+oI&P z<|ZeMkN5a@uDQ(?{C)=EMQxY4d7dwPvEjVUP(>QRm%+E(Ri0ewoHc**v@-M5>G}(1 z;Yjq6HC8Za20?8N+C7E{5Z)EDT9NHIE$&tWORJ1YtfZW8L9kvJf?CtoFQJSefsEAysC1r?EJb1;zJ-Y>;z84 zre?$$vXP0jU&q`AvCxmqE0%CIoA$+vMwwo`$uvaD&u~P&TVwR*+MWL(;m^eaLhnAJ z=C<1G{izIWX#3Wll!dkFC+Rnw;F2x~qbn;(pb&ZW^E^^2pR(|wa%;032$a;g8H)vI+)I`G?q>-d# z6Vgui^|01*^jIcNnp5kIsfOr>MyuPWVr-7HIg;c_+Rl*?wo?WP_mSn(G=pNjNjezw zI9o=#Dtpy=l0T4ttUmMONB8)=Lt*Cn7BYkw0|7jzsb$TPv+7s#s z68^k*Hdke1JoQG%DI!;Td;L8<6swuRshBB(067U<6) zYxUl28~YR%Y+s%MR^gJ`=R2$b%&HHZ7Jf+&lJFJJ1Gf`^D+f zjRUcPo4i>8N*|#Nf547^@V6#tTZ%Y%FXKmm(^24`d^*%VkA^QgVTR@=sf@3p}*u%(A}R_ z=cDTi!!~R_mQozcA!R<+82OV}aHXlIeZ@)BuoMh5`GIv?{}iv0^iH}I!F6e)OLA$O z%`K?57usvy-0M&T9@3;h{N09qu(vkpJ`hH^4mqWGZTP0I@8jGu1WMzU9Y%H23;<~Tdr9L3}ETelv5M&ri%`)97_96}m^G_5C zh0~x=ZV*thWx~>W>#u#-V=Z9*NRq|S^u-=p^j^RFj?;v-v(V_Fk_EpZR{Aivh@-Ks zuN4Bf0MiP0e6wiN0l+*ggu^>dR5+5xARL4H>JDmcm4C_vGvubAlvq(;#I?O4y3BT< zX!4Ged&r=QfiereEO`KwRUZXnF$y5Vo8FF#_xL9=#OHz4E)bcbn}Q~j^&qMm2@<~_Vh6=7Ak<`R`YvpbMKhqF z;e+J`(cgN8mN%RvOSpu^;nZi~FvB z$p#xlVL`lQieIq5%d&@RDrWAQ*7H4a+h$Hm5E_)CcWoOXWVpszv>8&*u!Mo+9e^W& z`+5iUr1H4G!i14oxJT>&rP<-T@c=3-!V};^4)M6f3U(U>7RBp3H<>`9$~rm8KjiNw zb0P}5bC*~5H=W!%nA#gyiZqIAe)y+~ppEreY<~bh5GF;|**(P4QgR$XM^t?`H2 z(&f>Hs|^xpapr_C8X4(w_u&EnddpHw_Tai0Xw)2v-WisF&@<9f z$Ij6&e1N4FEC0PVcs5#CjU9! zGP@s~(&?3d^YMlCoBq^aS-Kq#WCX9fdEdTc#L4hT@f{kBtU*+nnohI2C3?{EApiZI z$*$mE$1c?1+C@U1Uo8N(VfGrJKGvkSgMa&y`N3jCi z8!9|!T)!`OT~0vrHkHzM3`&);1Dg--Z`PS5=JT|p@O2FW)G)~S`QCV+e1CPcvr}ht zcg2wGl?0KFf9bvs;}Jd$TSbtL#>3u<&T$%m-k&|ZF_-oWMRSq&p@WN7H@4{LD98>- zV@V6rf576ao@^yR$27<*YX7kSr%fyA4;;YOp}$i^>jjIG91QxHy!CZ4nDN6pv$(3z z)R5Q|aZ({Uu)mocmbM|Z#hS3&_y^HQmbW9>opf)5g*sjtJMThz9_r5#y87Coy@A)a zLsK9ooEX26!U&b97F`Yj z|0cUJ1gg@NQ4D(RYj6Jn09XMlcNofSIh^HBdUS z5~jLwmR(?fgrOqfM7 z>socN>4s6ewxFsmKa4@{8?VUN&x}BPHHTNxe|{Wtw|^2Oir~5i(3_k6&d@QW z6=e+R5`RhCHZNFfW^{?XZ#cv0bO>~F&S_Y^!ZCs96*C;|vEPGND^-I>r9#*f0e~3+ zBP)(zL#r}SCludj5I<7nsOtU|`vb?l+T;Fv5bEzr8@58rAxSCDt~SUkoZ|8~a!6Dw zx>6xlVzQe$^*Y?-Ke=SyvMrL7x<8=;dnW(?w+1t`5uTt;XT;KaAV~KLQ*P6FvfL0a^R~wx87697G-rHXXe$ z01x)!hIzm-bBNnlsei@iKVU0|I-{akH|ut1vUS~$cB*%BXdjHp^LFFz1XcJi9X}74 z@2q)*ZYsH-wUdM!+c?*~Fe#9>hnzvw9}JTNHQ|rfb&O%r-UwX`^?xb0w%i?OzUKZ= zadfu%H=Ou=8TSS zr@^NbE`r&P=M`IF?PHTvjveG89mS1G$lW_v;qoZ_5Wo;Y1}cugz<$W;DHDhCOY9UhI`s7Mlvm7tfwb|xj`#rOj3E%8VBr*! z5_T$~1$V{I?;UNR6Vw;H6tt!?h?EMQqwOEOInqPH zN5vWB=VFJ;Y`?wO3n+!H3o&B(ccQk-B z&EpeTT3L@$bZJ4Pxy-G`o_wvVKATv0L z&T}FD$VbAGZ0kI#;hptKwPY%;)xpz-Z5v6eP&frEj}gmKD2z^fc6-7a?ACtJp{xTd zZUkXx)G13c4~wIy{OZXKcG*&LYu0C?!LIbx*zH#4{nR^sm!#?Cy7sG~c7m5LYj~_> z^gQoR0J&ZlVBo1+^R(NTO+> z{cwd4X4xR*q)tbDQ(T@_ra5fWV|d>f>-A2YeY_#@2xjIJ%jSjsLa z=IUtpRAOI0#2BN*D>QVAQ*b4#G3kikEeP|&_Wecyjh!yV$YdG1dCQ!Y#+C^*&5R10 zVCPNhm_1b2%=U?ZA!WAOu*02lGt=V%`${oNxaqMw8ze6`Oey^mMfD~;5j1ZvRye9G z)^@3an-^}v#TS;#lOh=WY#3?5P66T7?Y*0FB8KpwZ6;~4J<5)xDu1x03>{=B;M1Cs z$37&WdeXN@Ct3R(iVjcaAGU+>|B;8Ng?A|Bbsj7x$gforTDgFBx? zXHff<^uZzy>YXWt*ynY9equ6D02Cz8J${>L0Gj0^{(IH4!Ux*>sVMcGNd?X$=;`Ur z{88DbNeu`fQ^iTG)r@Bf;f#<}kbgL;7+LG>K;f|mXtPsgWhDWQKCRD3fn4+cMrC|Z)SNLL<(3wz_i$p!)lie-?tnRJ{{H7AD1%5P zn#lQ~r-wj?cXJUM7&?6`M)m2%;3seW;xWVO8@2pSht zLeBW^dTXvI3aGh{Y%7(*f7-Sb*h{U7H{bWpynn^*96Gt!`gOf+nL%445j;4QaaF@i z@^gz_KPnur2%EyKvV{UHL;IsQ#}RtZS#5e@K;n1ySkcldPp_D9{h7lE(wFChl+T5B z6DrUe3gPhOi%~M0sP3ifTz#qq(?*ck?Wpwz4meP#D=KcmG0skF2@cX$)hoaLk$3RI zG3SrA=d9`4Aj<25>>b^a_gWd4uDu~tDKv?wv7uvFWVb>=x-VlRiEq@jhs#d;#SnHr z6hA;Lrd;5LMQ9Vw9vk5?|CRc4pOD|Bv>DzImc_jrxGyM%JRISjXQQGb#5+`h9i5ZQ z!0xHnYTmsA1&D_0?m_Y)=QXzT)bg#WdKN2xkAR>2b?n!pcd}p z`;BuMAY?c*@l)!P^<&3uay@|`F|VJ9rBbc(QXs^7_bL(44Jpq~SNyYcCG+||xfrHu zcA}$oc*p$KBYP&wFx1T*AjU6BEpVKn_L1QsD)uuU`3Ow)S)!j~G|3-Oj?i5E_9g}~ z-=ZoHz{lt%{8U6mGxn>HD}lOf3j(U#*vpEi>@IWkVKaALC{0oVgcjAa0Img+2NtxT zo=g1d?fRwPVC7QyuvN=pZJspsTjzb)W-77^B9)m7uMkN7wXoX?5RtZHlK4^V>^v)+iB9+Y-8JOY&EuRG&UOBw$qsJ`MvA^e1UtkwmkTtzQLG9vrWm4JWl57Dqw7s zrB?;Ah;dKJ+g&7`MBCoszuu0e+miyviL+yYbCqadHrvsk)aL(9!tcij{xBeAOp@S$&0Ye-R_Cr58MO2y*Eno_&6R#ZE{pB<3vDF1;YN#6t?8>95B^)E^gK7W5PUm z5=9cl6CDSlqa0PlsGSE}9a9_Z5m1l~PM#)OSWpX4n`s6r!oIE<5`x1YuB132A&tOg zQo;{JD0yb{3>vzO4U%Y+34$BzMpBO)J7xnLLBLTJbHE=mR)Sw=pE$a`Y#EJwRfH6E zp<5leJmE-3lQ?titox^(n@)k;!i;q1tA#LRTO7^>$5BcDcURB3@5}R^uj?0x zm5*nHHFIH}8UhVVxDeH_j?S+qY;8ssbUeQiO;{I~Kg)Bf{VyqRi-twiONb8v9TSBzZTtQ8{-i7UnVr96 z068bAHUshZo&4jj%|#m;WI4Lqu-CDiyXvq7E*^qZSR`M-8zYm!4OoUxB4)mF_ZspB z!434GF~+w$LdpWylt3fE_3Dn~=A3lg*1nsIgIZMr68P%eyOKuCtBToEuu@_Arl<4V zmG7#2_8(rmw(-Z@IwcfCO|euyY_u8ez9c zhfzzVOEV|)7;3+#u)CfdI5=w$ge*XLxljuNl#ZE!2OUnhBB-tuVUC25)sf)=BBYO^ zer>&%VSE49*mt4}FsFkS{`RO93a3<6O#r%b&r`bzQ*GoAVYl6w!jEy#_LDodKm0t9 zxEBRD&k+Br?JnXE8u^z2+2$-@@Zxscv%rEdfAHBR7EcU$x(oZ7ub8tZcEB^fTH`$DD-j;?7Rtky7yz*b1e8ECs#wjQ+w7q zTHj%HExj)q0BaSrcon>Ne|y@lU{{uwtUrtgNC}`fKap#P4jEAGjEJue4T0*?mVzA8 zB0kiH`fUTqQg6+a{K>3M03d!~`5P$ACQF<4*b9sURJ_?AK6Wzi*6uiWYR#`knwEa2 zZlR1F+zao%zW1A_VpHiCn?U#5Ed>s?KWS=+^zS^6;JgJf0CcyqMVTWeZPNi}f<3HG zaLIH-s*&787yg#R;e|~6QSW51O<&XS`th8;Wn>(Omqw}F+-RXoQ zA?PCmCwhJEjEs^r`(dWmWW-hWltn-9^|;}7HP!vv>9FNv=Qn4_y4I<-%t)AayWrB4 z{nXgf?((uq*Sje8n~~Kwi>CC#1M+Bq_bzN}h{{e{kmWZXvS3@2a!-2OfrLD2PSR8>q-mN1* zt>W$UrK~fr=e5U~c|L`=Z1kXQMU!GMuaQ>LdnP>?2`nFX=FLYRx@PQ%`f_ot2JD~X zz0Bh?Xy%CCXR4q;J%6RlSvX(#H8W#sb5&4?;CMK@thSE$P%3HmGq<_?9aanPz`oqJ z#lv)1Kt7yDm1CChIdfP<#asT6Zqx=CCZWWn^UMVlJ z%sQpgwb$uY8mJHE64&bM5O>e$40X%|%g}CJQ_G|M3(yQpWp6< zjrU8sqG6xFgi7HmcZK61;-LGrkS}L~@C+PBPamx4!kZ&ElpB`83tTltdCXQNaZ05v zMk{<+%QUN`4Kr9fiD2tGJKG8_W)w+%l(iO}j}c3i9l)nZP-`(F0$praw2f*oaRjR= zQdP~Eg&?fci0XOgcj;k4!anvV`ieB8Rzl-{0+B3-f{TMDV7M05dqnOO6>`BrzZ}6x z+LB7jadD`wjFV1Eg_-=hw6~c$`@Nla+xwzRX_h^4#KHd2$#}-wMfBqIm46kAgZ}Mt zxA8f4F|V=K%G|d}vmb{%9TRS(WP!6kWFWa#p;Z-Mh4hUpEAUN3wpNnbJZQ}FBB;A7 z(rc$VPN7tdt-jip0i{cUAqwmvE~G0CY6u+3QO;&ds{>p(^33rMl`8n39)|N3T_9`+ z@fx3Pg&*vva503XCy_F-{k~-4L|t2Y&DJNR<>=7i`@MiPnCQsRk#Hevkrg|be}GvD$ty9UVRN%>L$>q%n%GQbFgV^qJCc5Tq=8{uH~Q7Bgf&55e^2mbQF8 zdu(P?Zz3S*gKsFN3*uIW@(-26D$&q)UR}*>yCKh*gQ1F@LOeCiDG-Fui0!K7cC_%Y zZM~R3`iqQWZ*Fwl(bA6T6dKBSPPf()pK6oMHTWXeV`8VEqwo5X!}n0+H+I1s{&8bw z_-}oJ%UpJ|sGhg({Fv_(bL|G@D|^qUySFDvCpT_dEAmmU!-|Dfl`!g2qOwgh*VNaD zkTg^GzZy$l&m4CD^>j=uBxeXceX2DPTJU_7&HVEVsiI4NG$9d1Inebv@uSpCKrk_M zKkZl5`)s?ewWgPlMVrJx=z6KHXAS=%ZqD;i_Pl0?fn1Arnt;7xoXFM5adH1jTw)+Q z%@DFcIP5q|NuLn&*EFB4@z+p(BA4<wjE!}Zqbd8yl8Mra|zw31#`~86wQ{|I;bfXfrjSDAEP?m6D zZna(Y?_s0H)RwxKO@#&Cj%CG<${vJF^U#Ur$&#=}$lu6sfvon}kHU{0jMDT>@(gxd zh1~a|_;FP$-WPA{+h3g>39x-z(i}7toGx^NPB0mcx!&l0#WxG<^`oZ)@%AXJ&6p5)!V|gXr3cV1ZgarV9FRyp2x+XmtU3xDGC=8eQ}&V z93X!`2DvDxy5xRr@lr&dbV5@TrGS*H*#X6sn1gjG=oF$9gvJo>1*c@Xl2SGHmtUNs z#p)M^j`>+oDG!JK-{-5PH5(98aEBj(Y$73go}&CX{^NZ6@rEFVN!H% zzcRZe&WyD+yh4gy(g+PF*+n3>3-|J5H~R*$e*4ED`u^(c+`O3de7=J9*C7)5UlQ{y zL{S_kgife^%>RrUG`!&M6NKE9*i|SWQ&UUDDPPv)ir^~IlS{?@td2>$yEnuviA*2& zG0;tVx`)dOzC4r2uGn1V3&*H3S-yj z-k=~diZDz%7I6)tk3`oofwGJQn6oQx6(=^J-XfCFx1~4+!#Jm>3zki4%NL2u+#=^o zv6_dMBB9J+?MQF|uF(%-6{ z7hK_eCj6|26uJPhS3#XG)!#-}d+Mi#6I{~`JQwRriItQGW&)O9b`f?`?ynKp6qq(-=E2<@P=^}re;Gh*_F%S#uI~N^&Ktgmk%_6@q1zw{SybJ*aCuoOY{`Ho=MqFxf7)e`Dl}M3 z*K2B%Pwc7yW+eI5+~5bcFB7NBzY*+@;rZRd=JrB7Ow#z8W(WwI>H~ZfAu)u={;L9g z0K>HZ8(4ffJ$~+{HOKHMyVkV!1dT+v1zbBErkx3$`kZq0rqP98-xxgq9Cs3fyTI@= z|M75${~Ld38#WWM!q_&`&jYQoqrPK6%^f;YdqZ%5UF+Ok+R`FH(PBS->&dT2(`?O1 zc-E4tD8;M zl|FsQz_n1PWgDvx9nhzJ_18p%mOYs<(gG;)aDC6F(1_`zI6Ae?iME}`!JJN;asx)5nxZ@8OY;QMaMwivpYgazCJg?JuiCIf zXpN(kezIlc6kl7wy9nelzKF_Zd4SXo)T~snu>RJPl zMxT}ac^3^@-)(KK$Twm?PbU|#um*OsVtp{rGovT-!Oml8=^Y#b|h^rQ5|i~E)-tS;FhsdgWXZ9g@x4YN&gd+DOJTi;3Q-WOTkOt0miCLh2T+@zZb$mX zMHW2R1o!Pie*K{*Gd5eY`O}Y%)fIEg{>93{3+PR8F^OX4TRIzo5IvlNL9y}i=Jtm} zR#>6bf<76)Inr%@6a91WKOG4*wF?z$OpzP2eUwXdXA+cQDs*ft!5Z4_R3*RIe#2Us zb-+Z5OaB(Qawy3*5A@7jga$E1N*L{PW~II;B_wk%4a(axSfp9-wsc`hSMV(iOHwZl z7lQkxQO5gP5u#i?q&^D2duXS#vMFF9+X+|>q4;$wT}W}?JeMQKG|{6uexoEYRI^vQ zTX&#lj#ypG7mhc~Y@S0FjbSUvc9{6}WP(G69QqkV)>3)Uz|HnCl?Z9J`{UoO{zVKc zuFJK6YG{#p+%UUH=o+^-Y0%8P=c&`o%~>gO+epQTMECFF;@2-O+(p`)oO0R9c6{Y9 z#C}D^&HGsWZL}2f_S*B+Av!n}2PypoPUG)|M2q~o=`n&*3G|AYWkYINUa*vN>Fo|J z$Cn7Pf2iF?x2DO*eVdT55UI5IhU#8)D6(R%o6Z^No1K)fAEyPBj=aK{RmriEroPK# z`ci~{`h9~()Ohm}LQ7!&G-`iOLp4r-aaEF4%}0NyFJ%u<%STT>##V~Tw#n|1QbcC~ zSBFTCE%Z7R5k%Wy+XU|ht2p+2eETWlSs>wy+ZAh_4gYIK%j4DLyH58%eG7@KW?=f9j z*9=-{O8&c1yz1!Y7o0lE@iBVTqK#v}2Z~VAcD1aTSB9yCWowi-$Tc>^TrOT_S_Lit zz94GYNDiL_RkQ{>Hn(u$BMXKrgJ_pSe%wR%YcndCy`j-mV|^Lv(4t~&Ueo3JZL?W^ z=&V`{1=grr&}YdAC0Zbo=D)lS;_5@Z#}z(9{5A5#G4v8xZ^u*>%LEyYnk}5v$qOC1;gmwol<_D)= z&Y5|>0|48In-SLOr-y4;UvRSy+e7)4P)oW374{GScmGWKxOn(Fi@gpI>GeR%{%*e& z;|H@TII31Ing1tFPoK%u_&daiC{(XwhSfl8O*4kg z_@@3cCFgJ*on}t-lm~5w>8MmL@(cxxjZJw~ATZ=-!N=~1`b@oXdanwIByR9{oV+SX zWoP}l|6%C>{|@w%d~#d2vC11(9DMJJMYc(l4k+roqOXf>%ZGDkNU91vuu zj?a%5=VxA*Za?h0;P?wT-q_|{9QUj20?**fG?(@aLYcHYj*zJOO;|PRo0Mnj{mwhP z3s(V{&k0YbVerD$^TSPz?JW{n0JAlqq<9U2b(*EiD`Gc$R zlpQON4w?pb%&c}O%>~YpM>5#*>Pud@Dc!-vo3u5YaZ`slQ`34!sUPyUqm;Z6iktpw zh@rZAM_rt^lP#?w!b5fzrssWfkSCU$YSP0!r<|#N7I$N12Cu1QAr9=}Rtxt6oSq*w z0KQ%8O#ZJ>v)hk^KYq5Czf0FXll(&FeB9G^Zp@zIB=C;-vgCQ%;0|jH{4ZJI@=^=g z4!l`2*3{|h8Wg@}2n~h;C-LhcvH{1ik@&~=(4pPM&5vlaaw}c+P07v@|9m?d>(%t{ zCFJF!WR$^)THDlXxyOm$tNfDn(>K>OZG_&R+jQ*m>LLyySY=i{VPSf<3{f=1pX(jJ zFOrV2>IBX3JH2x>%|Z^7QSc}-DLmtQUVA{gsc}*j+b!K?<9hBXy}RgL2chtJtGywi z7!2e-v1x5+Da*-{mh4E=HYHS>jTOa_8?^^TL&ApaH{R`1=;hL^H|=a=+=YBeTaK!! z#&{SJeS&6;#suD#Iq$pYvI?d>qE@o_J_Z`FM``oKC<-M0G-_Bz5pxpkRO9cr2jY;u zkkt{M;AzFCmokSW>TmZOzws$mT?m3$RIsvC5;L3Y(o3rQbxx|SgOQA3)Z1hq_9Sid zv;t|S=edNgk&&qqGx*lZ24!%l%<^#GoJ6VP?1qiP&slTfX?!(Y1WW3)u($zFY}AI1i!+(9UHBZWv09R z?bAGHQWzm(o*X!2kFc7EoP9%SoA+~dnA)TDd~sRqx8zqC6|QmaZ7G zV`W@pruZ0CbpZmm$cMPoU7}x+;*I$^8yHnz@4CJ68Kj4Kacm#of*LjAI`4X7F7r@m zz&h$duGsE&Y}ar9x{w3tp)%&2*IsE9;K;^`%K2hE#B4OhAw*wa zay~mpAG;CkK(y=7J%}yI7gCKfad1$Zt&q9knujU<{xpmngg&+oPsScLue;Z;?BHu{ zPol=IVSC=JWcK5R>0KYRdebJf{4oXrEa+@*cT@6ZPe9PmAdHJrksoI`ZsGxzxaLD=n)s=I*@74v$H5jZcTG;^6C9#=P| z5UKtxKd1JzFe|x5hvuM-w6ymLmDH^6VE3od#|)ekg0B0fLn4X=qasfNnk*R+m7q@N zulzDEOM_w=ko6$&_&)JDwei}%oC-Fz(OxN%ftM2bU({T&XkF9~Ki@BH zdmpY=45-!~kWBu{j#vE2%owdNqC6l<#Tj{_p3)CmdU=oa5UYZ;;N(nIlF|dyQiXl6 z75WENNkHV=UH!?zT)t@@p|mBb$!`P9MzxL1?p+51B|N{jHMguJ)6xrPNm%-5{mqt6q$8j+CJ(w@y;Cj= zRx#uhrUR+U4q3VXmPU(j6H~il9h6zkF@&Zh%}}n=H>I$oHe;1g*oi#xgpSY>qo-`_ zDwl}ejV;dMZSoUm+&<|T%K@IW_xplzJrCQ=N_mV)qnIQ2eZ?n&5vAqQiFy;Z?_4Ct zJq!`o?+dIy!vvV7BMEY{Vy_?D=B&%+M9WUM)ghPmWX7qgdHH2u*W`)hA1~>CvgwjoNwm# z((rAeAzX)7-C$jc<{uBZu$rn3-<(u1VMwtD(nAWm-Ng1Zw!N9-AN(UIfu!y|P;FKf z#4tbahq(@(-{n?T;_HE!lTVS$pzS+u6g#DQ0nif~ zsu~G5n)qrc*u5@^71}Qf;al3netSsl9s2Qfd%2s$q#5f_KZ44W&^j$$Oan=dqlCXMuLzw5Lvli3xCOot&k9L zAlg*>)KI)bF~ja2gJji2ViC6DhzfBoq4+PckQ+D2%l6o5wJ`pml+ulVoE`U*xV0t7 zfm_1GzWO9$cmVnDAJaW2-0!_j=-(D2s|r<1$_Wbwjo5X<4f-+_l?$Q<9-@%WSI-(x ze}_`FRP2SutwyhCA0cq&XS3mwzQ0QEWXd$iSA$;JV-A$ z%o40tYH7W-p=nS$6=I?FB^gIx8k`K&UTR(6*0E3p-GC`P`BmKP8$rJVGsOg};Q~9= zcL|VaRTf8W^nzAbjeJR`TXS7mryGQA&57WkT5g!8IC&9!u61sn8$(5>xQ83LM-j## z+x-kq2Puhth^4LsFRj0Xkw0&bMbJ!CqRAJ=aQ|0VbEG*CEmkN~zI?4ne_lyIC5ilU z4_4cgx58A~@_yC8d2RQk_;*dtf`(( zPoEG0w3h_6Ehqz{L9AAL!FS%gP8ov$p`|wT8>o5E==n~~=2I(8Y7Q$43eSrRe&w_} zk=f8m!@trooVjQAunaT@KADB-q7a%>89hmN1E7Uduwle9M3D<89KNp?68nL_GSG`wh2A5?Obp@ZLp8* z?|GiM`sBOLEvdhm{`p~VpoM!$Du@1X0a2H6!RQ=g_xt=3VC)x?zf8>T0T7$nTo9w5 zm^S@&29ckO&?~Qf@9DgX3-~yTe~{a_$P9|Hefe_aK&&l^Kko4K$-R5h%%MEfm{*8q zgM5v~Jze_C&zet~CiZa0F|n$VF6EJcO8*y}Qu&g-t)B5uKHFb<(+qgu{0S!MbdYyC zpO|4WMl?2j*;@!UKi{rGV5VO2z0B8fO9zRsqm#wCmmU2v>T~{AOuTT#&0I5bXuz;ISf@FQieyk@X5^Gdc{;+0yWZ!@;LLb<8eeTmmA)2<=A9&)ZIFme5Z$<3 zFy;!x#%`l}%ilI~FVnj*KvW9thE`D2dj}))qRp_ktE{wf<8VH1@MC{l%pnKUmwKAo z^v0mvNB@U80W-bam*wWulvQ`K_5`gceRZ4yuc;2qdF#E5D^L8Cqu1iz56IxgO#TAA zS`$=tU)!)1!zypnIXy+NzZf(>mB+y_%9hMf)-kBkHTEVXXCI!4C-v-mFiIhF21WNPT^1adp z<(O^Ovc|Sc`A*=Kkm--EaJs!vnOQ63jr1)g3;jCimhY9$6(zMrc}5cr*k_# zjohY~x*y_tZL&+Cn zJK~U9+Ik71(bq`%ICk`cl`hb8heLR}qlL{Ac`5{B11!3|zZT^AS%h?#{E($2o^NZe z_c}KZc)HYc5$Xjx=~foLhm*J@MT(XlRHZPGw5gtdIk}dnFO!WpKyAU!ng6ZXWG~c9 z6kqy__`km3g=YV_FA}VzA{DLT8Y!6hFsc9brFMnxm^!;N@PT$GHJIRl_=(dP7!>kD zzgvJ;h*Q$eFlFPsu~?dhHG6%?gn_MJ&P=7@TwD-ckosc;-ELq=A>b z&p=4UZGNjr$5asmF3k2bNd{wuW{BRmltK=rp9;@RK5&*7Ie;LZNbxhUKLlk>21&VH zBE#~f2iF)}QS_V)>p{(gmSYKM-Zw{P+*6W2{lN$zgneAHwuu?RNTZrJUQo%H7h%|0 zz4xwe-(M*4#ze3t$IC;_0%9_UE#Q~^n* zBnSi%X96xFU%UtJCc+-Lo#7p`JDDJW8E_6*?0JArgz^IWFv5Q)XT3(DoQ6uK(?O}K zIpl_>U*Q)SfSCr?bf@{+lVzs8p|3ObkmbmeqpyG2#>YTx2Y3A8BKMC@zy7xN3R~Yp znzi^KfR}+cy*VpO-UsluAoN-hxAppTnfWUrF25r^_SmJt5}5q&Ev4)K=DF@X`?Hol zrVdS}_Bcss9Q-dBcn2#NvWCqje?Rq5w_EO+0p zR3`N_N#|WGZ)YDnIBb^9J&aUK<_1e_=eVD6r-ZJ{gnoD2`VcJOmWqttakw0W_ND}d zuZRd&%3GdeY`~*emntzR_TliCU{RY^GF>uYq;eMiCLF-MgC?hf{-e(R*w4G#fAd$J z+7T247TUmNP-D&0n4nD-SWCV-GKg)j?54Iidz>uqOeGTDj2XXh($5f71%u(#FV+2| zv>i==iQB#Ednl*7vAHV!7kS|!%LU3Z^F#ZRW6jLu1kh#4q4qM7l1R1TA6$G7h$SMO$k^8{1j-F@RJ zB-MH|mrm25m}?t^U$J8C%sKCZt_Gq~)}!K$PEEI-jFIYUVl2)V2Iv2|CE+HI?a=rJ-P7 z28DFd^mfu#O?U4Ky5B$XfQIMJT^Gw2&l^^JeJx>|*8alkhUWjo(`l%cet?trP1X9g zxVsKJF+OuNkz2>s0}Kd58{Ef%9hLS}X_JKHHPm@@f(N^=0rz2D`ec2F2$WwZ?O)ASb}Y=={& z9iA||A{ef@@bW}5BRF-Ge11(!5+MF`dEE|xkpcv&1poP&7YX=GB+9*gNbC@E@JTp* zdPX*?RWPGI%E2oBWYH>q@@brpaMtG*{SgOQf?mYaghdWk?TgbL2|jbRn5LYU5h1Kl z7@Ig!z;=Wy7F^Hm<7$Vxq|!p?V4j9v_?@z^`GM4=D&lW(nOwxcEtb}1n8rEQ1*xru zitu1vsHgi(F>i=DDn@Bd@i~@27(kiShkwAxG}TCcEiV*Dp}5W9pZQN8@ZqObFa&gC z%9n#&l$=M89*P-i3fv*BtVj7vZxv(6Eai6YkzoCSVEkE~EbsP0zeOgha75a0$&@KH<2 z66V8)Wro2MLc*{u-%1NpViq+-%FE-1%`W z_2fu4SU+~v6AZ=pYtB!`5#B}m_E`5o=W@`3|26sA`AgCq%TSm!3>NM1I$=VuSA&8pH4EL*}543n#%eU782jP3TAVINx z0Kus_c!Dw`2Vg{6(9mjUeL)0$NU6mX6&B}`dO4p`AY+d*vK^)msdcVMVVJ^Zu3)EO znVQWm%6XEK6rfZ{bU*dYK5;ZGgr{7G7L!k%-g&O1mBxMl4sAFe!gp2qWB&jzj#;?0 z`HKFf()_&Q!Ur;r)92po?gXxh-YSE=pW%^{*bj{WR*qw%gvsa3;0)=o{iC60k38p9 zN?4(mQrq#xO8EAqX!Lkr0hNB5Fr>$Rt!%2V=dDM_iZ1Q68bj?u>g!+0pLN@}eZPA? zCXnpZ*aAtY)YZQ+H?gmHJkXUAHel^bFsJ3hHxp@(7hktuZ+m3II}I1ZTyuz&3~a<$ zs|_(!Q42-S#4`f!5-wp(L7NDqJ8rLkNZ0(DyvvS2h}@ig*is%B?*hI;BzY>!>GA1gP-U!-`J z^`)?<1-w#^2H^Y6BX89v=xXKXLN`C1pdWou!|~ic_}tJ~Bh`BT|X z+w)^Elsi8UA7-D_))O>)Bxx2;Du!~gQc)y~5T?6CT-yW#wV7}B53YpnLqE6XcL#Ql z4<6WZ3}onCg>J0YLvANrQ%l-$LzeEdppJ{lzdN;Tf+q4&-ID*dlPT+W-F}T1^g2V@ zmE+{0rL*FFqu)fJz9ke$Os3G=@W_+{7^ld+ztD+17#SH|5AcHiV#a3aWIwh(0h^RL z0ALM?bPQoNBm^QPMoWOD_Uz9@zfnCJEEG8QdkZ<=RcS#;N3~%VaKDGcGyC1@Bn?3% z$?^7wcWwfcg4$iX<4MXsaRUh_AvlWZ>54nCh`hdN8Ci7Urv8ymu|n`|~aL+{m&TySmGWxYM@< zQlmxmolT>V&9%zMo1b|Ls4?tecPmf@P!bWWk8gc@JSmEQe81lqM<`gxqIQPHFgmL^ zE1aA^AVzI=#{*AL7Sp3fo7+JdC76)KV=y!M*NVC+vAe2C4maGgi0~}$9n^7zpyonA zeb@L#kATBM5cAU^rGE_=SXtoL$-rZxJ0QtZk$qpTzW)xN*hNGN&Hk_3xY}VZ4o`EF z%BZa6^TjAt$8R?MjWxLWO+vahUJ+(+%Xwf9kuwjO8hwiN1Ha7QE8N-J`f@R&amb0H ziCg9usmc5Ue?7Zrn!Lnb($Vfe*6^RH8b`+vIq`*6OXNtt(jTeA(|Wt;E{$eBx2n6u zh|^5S{RV1M@dX{|HO<@tX~&g#v!#Pp1SH_UB$ch`zJ8#a^OMs^!P8>&QSe4qWcs$z z*lbX?2Yv3=FiFtT&3TA15QHgTPDZVNCQ%!nqz_ammc_+PT3adUz=~BQTbbQvZ%b8{ zb^ATY9@1S2?y8MKghNISxhh?vV-5VYU)}ik=Pn*1xic8e$oH}EKhVEL`hUH_B0SI1 z0d)6|qzBZW8%xm2UkSd))af1K`FKZ71}*l8(hxDHavOONv<10dgp$$Y3Cl9DM6sGx zljmlmEj0oghcs{AttVvd4UK4c-RYHICgMxAQ9#)_LuR?MvZ5o+Av?ddL3V*H%Hahy z!?$;z--r8%@T1gPrNkK?2qc>d;ssH!o`=^@UdHwij8LT3;042Z>B>&VdRndaYI_yN zlMkJ}nb2=KibB84L++b*2d7>f*wmbqfyP8#?qXo0UZE^`JKLTDD{n>zM9^~0IQD)i zw?dj47k06`hB!0>4!Hns{uZIz!Q%lW1-P|IVFvF&$+*VHw5>5ptn25y*~V36d>pkE z&k<9n8FB_t^nCERBq_wNR2h~PULR0pDZS>3>wv}@vMtPgNGPK^J8@6HatJ-kF}LCp z3CkU{x>R%qenqF!I~B7DFw0uCnF2L9z?;lOV}TZ8n}s9*U6|XSpy*&uxz;SVjM!!7 zOQ|~`XNvO%Ls9pHFMO>)q1ybk(q4}1ROKfjOG1QrR{ip1`Vaab!>U9uHG1+G2^w_q zH+pFXUv#EG|7^zEP0fwqh#lY+#M;1{V8fnnf6Z5M9m5(iAZw9{cP}GUPh-AF>izD@ zw-5pmNmLc@(GcJdQrS+=%Zj=-!G!l3Sz0(UBR0ht?Px=eDSc_-y>r@ z7T?4tgW5b9X}%=;>FSe2skFe_)QzKKDRwk-#k z$^Z@{Om}7;rl;Ck05KRMfbaaJHFZ_#&L+vJVTt;u(Mm=<2m-xPL_1XF~XvQHi`!vUW3p zYDv@W`?(jM)*Dgm8#xz>ZDiGc=J(Jt%;X)abIrAjAR`0r&XEc_lB|H}$&sUPr#(GI z;TPH7KEk2F&lpr`J7C7k?syYu(uGY88nw2bG;FT?f$kiP_6!pW_l&4Z-cBZA_$T7< z9RvH5Hz%rxP+4n<+G}SAf;&iG2T3-F8}rvUYbE`cY|fw*Vf`m-?z=A^t4l`XpwqqY=`*y-fX^+jO|L*ec)SgoE&<$JNSpv?m}hm99I9LiJ2|R z77uYw=U&AHePKa8jf)rlTD-TRz77V()Yn$Z_{;OPhhM9(G_qlLgf@$#8cPzh2<#7l zCnMZ8V@=7s7eq@r-b-7CKN+8XM4}bHeB^rnK=6!O)8EP-cnhalDKi~UHx1IOSV1L% zeB}RNM#?5fNfL-H?1Y3kHKN`f@0Z99T1_+yrLWQ_O{#8MQ^4ZGO@1(&m91%MhiJu3 z<>tWooAd|VMMvjs)x^h5JV4Z3ix3-l4w=0OHANPzYSAVyXix&}BjVhovTgH$xjM!#(ANGO?lq zccB4e2B|((u#I@jwt1h+_?}f?a(8h%%2?@hZd@-tn&vmmgY|&40D{{1c9eRh1d)=R zz>^g-Tey0?-kKAVQ-`2azI#A4EAoUq+Nf zkT({TH*t9d<5|$729zy46rqgYZ4K4)pxaU~T5X-K;Cre|$3h1M5z}*e8U< z8RyP?nh1mJ-hO_G@uyl&z-CKyRYLto>Rx-WJ^295ka~hR-XI2vV3|}_J;FXL*?be+hWLxM0}60oZm@`sV3FUb7?Cj+v91w)a!wN1@k0 z_!afGF)3voZFSlUt^;Z_%x{N&c|R1Ua6u2i;WXaDg6nOsT=V;1RC(J~wn#2nnfUIB zYuHjuRU>D5GJkoU_N1O-%^jcexvRXFZp#2#CRo$@^v7OaNjQR%OQbx%2dzqcSzCsk zw&~Juj#_8Hf<9^ZPZe~}7LQ9I(Vsp}S*ORznA5keO_xhn@SweW(EYUZ@yqsGbuR^- zq%z---UNlT0&L$2P{_lRmglP;=5h@1{zM37#C9{kT>7~{pH4$Fc$H@WzVk4&d_-u| z?dExI@K@^{dSX3h?7m1R0eNid)E`BcYA7W|Z7?j7+74{Mt zG{aS$=YJO>hCYBcJPn%{1ef^{*ROGKt$yR4)%`vhKlO>?*(nqG$6j_bc8!!2HQ}sc zWxo%n{QxJ+kV#2xZvSX3|P^YgM(D`Gf1Mpm_@&@Y!-aKyr< z8kq<_8yi(~0}e_;Wh4j(lEnHmsQhr${;})WAN=(u!5L?)bbxRy&)cF}D%gm<$04rtEqi~6-rGF)3oFcq$}*G~fI>F)W|N0u8|>Y+d` z*%R!@xUEtdo{#X<-Ri~~+xS28KQ4&hie%4jOpxB}f2es}U7(OsX5qC7WR|bS-g?)y z`wX@5|Hm`d*X+vnqPOl>oBE|Y{h-9Jbez%Gf)-fbBWjYJ8N};Q#G3j~W} zliQ_*^tFnIT<>3}%u&~vfzq%de8_r*B4RVT)R3z|FRq6?wsPEAwe2hK@CqQZNrRr`hg zX@Zp1#lEPpaEM4Q(T&1FL#J~R`UnD0`uwQM0%iWuKT9QY&$2O^K9}B}FMg?}XYba<4JT9)qdDSegd1!pYzUV+}&M0$pP`tzWz+Xa7<@#I#Jg^-Qc(0v5?^r;V zbO1p*1orl*&Ia$z$0O=@=BY`}+7!iB_}OFr*MqizmVm#`1D&i|AQYVp=4N)sG|L|M zWoRy1oHn2%6!b%glEH?MxL$kr@08qoiU~$Pr=U{jL?xZXMTuChD;nc0bHDf9>_JYoL5)euXI3_NnUamAF8GyA4d)|~zU8sZc~XwLJ4G|2jevlF(jC(1p7&e( zKlMOaE}duQj_YPtez)An`kHtRKA^!7R^jTmkR)*US~b>cf#%jp8S!}VX8U!{l-bMV z{#d&4))7CSZ*vMb8>WjWoLX`{5{D6a`OrDnF_*)h1)8i54nO<#P)=M#Zc z4$4IaaYqf8b^@;`UCd=x$vF8jJ$Qo8gOg6%I|}cVaO_!=`U>J_VRN2Q zX&KKt8Hpl({yk7|Pfn9CfdSvu_x{B9KHASMPT!K%KmUAJIDp(nx~*^E6!bebxv>WL zNp3xkp0sQJN##5(t?^fqtF2vcW#%=G-XQTT3f~T1%5`3Q&s-=2!%;T7pZODg(QOF6VK{`nS&2wM=n9BA@4}|7 z`d`tq=mxP0!oFHQ8X~z+CIMCF*OK?D&Ihz0)F(Xc<5>4SG)C+LnB=zKSMTC?27Z&k z9Fi~Y79NR5U`EDU2VMf6dw1W> zb@PG(xGOF;>Z;@U!){4+e|%6SSyOATv7wKz%#jAgrwH@T7)yHvxj!9#PsLJQ!^Xd# zdWD3-VtT2lFk=cUHKhHl)M=AQ$O?669#Vx!$TwKCAw@R@eI9+WtC!-`{?`ML{J*N$ z_b9i2+ittEDM|^OoJL+h&u~41a#38p;0XKOYm;y-K4C0u{~$EDB-{F;D)7asiT@8= zbB#ow$hRt@$w8DiJowKjrjR@vc@w=3Bg?9_Vh9)j(DJv+UD`CO*L}g1r4gb{y`nY1 z22xa}*=QhzPE6U;Yc5G=>>E)hBk_tM!J3_~ahtq3K>70(!3H-z;~Js95EMgSyq3OV{-{7aFzRtdsUW!sn-NiZsnc4nc(^DJ|u6>(0tq)ig~A|xs9 zFm3m=DF^2wEJ+F1j<9(bl$R-1Z<{tvrmkhnEJR`Vau+qhfNpZM!vNM*G@KL_s8a!- z4XAxmscE`~B3zrFZa6vUzSyPI1b-YmPrg2dQgCAVie%vs6u_&73OV@u{)VBV0WJ&= z6vz5cPVb7mDZE7pAyL7{_2z!$(D|`<>*B)5@C9l&ED?e z1ZQj%$SUD=Z`&>sD?v=x&7wL9+6<3?+fzeJPm-;>*0M$jPcJtIYNQ_Kl%vOVkFB2G z!!Hx~-T^j!g&9=-DMoc<9uB=cf`F4Lbd|tAg}-he7Z;CkTTn5;Jq?XW#>+>aK?tqQ zk^hOXe#%qK2c_YwK!7corQpZEUoPkZXLXiDKrFtg30u39iDijrj_lKIKLy9`4$5*> zHmwjmZS>c%;kg|PV&9NK0vaLzkPFM%?>8l*yT>3=`fI7(Dy5|j+Hcq5$3}$JM{iii z(s#NN79>)1E}E_6E9u9s=t=#PC(C%$&f~N$dph}teGxT9l0qHYWiOHV{^8|bWPG6SjB zCeQj8E<-0AZ8>e~A7T~_6-`x5=LmUWT`J|7<%_YYS(39JPl)ZO4Zbz{wi!H#Kl*F} zPlpQs{=D|jgBjiB|If%?S~!e$@cz26TQig&1q(&ZFM3jQSwQUL0ALuleM$|9aYjqW zcPR-BCvuJJYfStwE_4gKV*;W6#T?6CLzZq7eDT=DW%wu4HL;DpE?Ui_$D{CSc2?8i zCOnx$I+v)7?pW6_gzeU7JfRT$JEc;tK+h_rYA2)n>Wik&jA`SqJn+d?659}uoV=oU z)K~-d{L*)0X@SrMi82Etk#}ivvJ_Ej;d$-l`FSXc+iJu{wLb$2RYr+KKw9SG-}`R+ z?RZ0}W_O)RWjy)tjm6~S1lb^1S>+kjUkqH`RdrOfy-EMv(CuSVb(YtAwXT$2gihE{ z6A>#!rN;2Q!>6X^7gtxi?q9-$_@b0I7abq2H1~F;A29+vi`IsF-MsQ5eabYX#9VHQ zwYrJP5!y({bmdSj2)>=UrbaejKW%JtX2JcV3$zQ{?nL_TL!~m!33Xb!g6d=?(6#6D zaPMHub> z;y?XVBWvpAC4b?fDRJb2cUj@PlM>m#+&k0_S|;zmbt^gsg6l=-gsK=9NNHyGUrLM0 ztf0KuVle5qsjhxMGxoul)HN{(8)Yw@?4^?9?pP*P4yOOJ#1-l|Hpqn*#g( zL3wIRoeZ#o=8+5RFg#(pf@?PVnoXJQ2KV9NIMFgDTeoJ#!)#ZL*255r@QcVR;=Uno z{H02gfQ{kZ)%XzwQyIDpj&n9RUCb|oi9EZwyn6X!MP>I+USkhcqmw|?+_>PsaYiVz zx~cszPwbm;Xd#UBHgmPRtKN#5qCY z0cBO#A0ZZu`e;PT8C8s;VdTLYl8E8MKiExq&2) zIlg{w?~_1%Uk@9S`7(xzKRAb!HoRMdV|B5^O^N1-LLFVH$YQgWbWDt(=kc1FHs0x? zi+QE#IMp63#0@%1_x5chJpH(w&o_oXF-4~!-vKHMa`rVc>ma2rQ50I#5ThD?K|h~A zr1ks?kP#R# z`~;&$KgM6pp*7iI`Zkfs|6P(%cW`g4GujXzJ{bpas`L#<@z~;_n%063?Lv@mC91B2 zn@`lp$v13Z6s#p$EY;qVt;SV!W!w7@Z7celtpZ7TtwK4!t*xf|7lDXUuCxt0Q-a+Nx~)OvYXi&#FdvTwdRY5()Ss(Ssl^ zZzAdKc?0(-y6kncivCYHWs&tlC+KRj@is^rC+~HmKdb9C6nND)LkMUnqE=Q`d3y|# zSA2h2$szK4CTTQbD#voD1}UE+rEKE@tHdb0g>Gp4QChJz=$%TFIQ8?7 z2(c^U%6KPL!tbEyI`?T{CZ()F5eB>NQz$=2pqGP3Xj;_Ys^(S|CYjLL6pWK+@>u1c z5(1M?-`xGlR4PqCjuD=MD<@IdBq@{qyPR#k7&F>Im3;|j8O1z8n3x4C4^hB_fsoA( zB&1bDi(L?hP<1dD(Lu1T+8mBjKMf>&vdpnulw+d_hvRh08tqFoB2W_Y89+G-@v!c44PQ3qPl##)yjlGtWac zA{OWYoowlR4`&k4ZR!3YAqf-bR6 zf)^{nmx+u^T&N>)XjXv`BIFRU%_l0W>tN^DOQZBL=6O`BR)gs%(Q9=^j#J20@T)G} zT4!7+>)B{#Da4NpJzCb1`4Oe$V9QOM8q${|AL3;7W-coeTiSH)3Sy7r7pA>A^&h;% zY}txdusB2ZgWq;!WS&CCL?D_Q+ESnEgU_6)+6-7L-x_uosf>p(o=ZR*hDpYJB?D$@bZ`2qkWOIoG zAry-^#BIWvX68juKh+p4Z0n2ss;=-M`SatM>FvT{XkDZgcLvYCWmD(fr_wD0*velr zvc6WdJjioLkus(SYnlJ(*2Y-I`|ea-M`u@XTVhmfivf})Tz-JMhzJftD#Pf}zGI6P z;Wd11nR&>*Hwn^`Wz$!uNZ!h#@|3z#aM|}A&-=@U;ym!7FvB!)f=8fZ=l$(+G!_pz z$9MAWKt@2oVaXaAY$ZJ{W33t%q_#7aV)G89fRK>j{k93=Oj|MUzt|N$76-zX2Hqhd zqy@GntNT?05HuCc%>^yd{$lvlK}wN0>no2t2r0nc8Bf?l{K$J%(5;3XrE?Ij^TCy- zTQ)PDl46Y@FVXoL!|CE|erU&cX7;>`!}l7A2Z*d5g0%Iv{9>d`woTnTA!1`jl3NuJ zbGmIwF_4|@?Yy16*H@hDNB_y&bE*9*+K^$OOO=P`%HwS%Nz0L{IO8q!%qVBH?jBY9 znRgXb;bahabGzTI`3D<7)Ldta6HJdDUi&H`h5>(?hwPI8y9b?s8)+=Y`y-z6S&kj+ zEP^k}fLHb{*zKCzam#fl8tB$NIJRdxGSr)RvMhGu8%hYQ@y275?1X_1DNBsD>DI|D zUAI0UIjU%(1A=2^nY4DO2MZ@d=ibfNon3ESEf`!Wwc{Qk5`PfaY!Yv0mJQUYQ=A8{3F5p>9qSjd)B^U z0_aJ{2x7Ip=9Bvndws#DYZTG9=f6I_+Ud5(N+RntvR*9Z^gZ`R&ZUdu=nO{@i=fP_ zA}s7vJ_Cj0L5~S^O&L6r0+j{+XX^N(59tX&u69lLPavB9J^GuY1U;xUbH9_)#E6bP zJmuOccB_upz|~8B)XJ)_i-k;5h>tpU!%#(ZK{z@qdoAxdf6TT?YOJC7>U@^FplU=B zP%h}lF&^oLA|hGTQp?2aDdsF348wV@h2tjP^D8%fb<{deH-RPk)R8|!k*X&scmg?l zZfUqC86^1f(NaRzZ|r(TOApQYz2~!}RPPL=op*BhkrfT$;O(@e6Ze+Qhh281h<|cH zM1v_A^e95M7f-p>}_HfYQCP&?~iGhAfNW#wM_#Bcdq1%y)hyb@?>H!g@1&JmDLH|X8e$X8-muW zl`1KlD=XS^USHDles(c4AN<-3TYgP7ni5AP^b2PP%!`w=9O6u9YKYlGT)Smnz|XVv zljQzNAS0i!=%-UayJfU;bDf(vH>LJL7eH8wcB-pD8 z@n3M>$RG^+PX##7IQJ-k>DHN(Pzpt~u5VIm^_3ALfvhS|Kc|d{2>La22?HrlKJ*AX zIL$caZRa}dB;i*uFH5mUFa{n78rv=%WPCSnX6k6bM<#5LOuovFB>FTc0U%(RJQOPa-|NPw6ratr?Cc|$7% zXMq0gXCVCOhG9tf;BB|bpm+NBbJ2D_PfS11cy$Cyau#?WkOv{4o4|zqlGDXIfR2B= zi&BWj6>}L5p=+8pTZ2g3+wLG#!L>b$S27%C0hXqU2&rT*2Xb38DE%7Cv(!`LNo#PJ zdBUTG9D&b>#*YX3EW-Y&QT&X-JAxV6^(wi^mtCWI1N?U3p@jV`?M~Dzx=-aBH+tP_Z7D@0fX62A((vRa% z(rv=hit@1LGGpuLhS0aTR;is!;qyj_L7Hkoh=Bwy**t$rTb_>_cW&6^#3SloEN40O z^-sECDcMokzhC9P|LRxpPbNV+CMXw2%!*S{=Fu@fQ1L(D@prC0I7*N*TUpJVcs4?J{63e#!P=Pc?{UlWBo0((Y=NcrA~w<9G_SfvsA&=e=K_5Xo*jJN`x;=e zwug=HVEcV3U23s-&>Y~mepl3SG8*)5X;hYS$UNNtLk6L{R^akNRQW`UP{2IrKD}}} zFZn;5r;O!_oD{NKJrsE8bSK#>JeXa`ih}|Seq69vz+hJ@lNQ>!Z)eH2J5bY!b57A} zZ&+EY^s0fUgy`1+pZ@S|XugUEtK%UA`I_m6aF-%I_Oa!LK(mrsB@d-Oh02OYo`5!Su?(z!KAfrqQUdDL*uyb^!z_&*N@t7R; z940YAQ~Bf|Qzy6js{;0VMzze?xkD^7)HG)Z0)pv9L9d;dk4zk~)QTm+*KxnhWL!9) z-3S-JzM|7xj?!$UwO`@$7*MXA0PXSjkgm}`3y6t)(kE>SpAR-{uE<`d=~JGlNiqV> zTk*@%oks|q>XdYf)*@HkT+WDHHIAZKg?*^Q7pBmr!9U(xQi%B7dOdCWdCC}b=o^DD zPRtiuNH^PfdsO&BO}~rE!ftNcGI%vg9P`ijI;}4F?vsR-MvHwO@P{Bcp**{`EcR>H z2FpW|kLTB;zd(#LY2OJrb3*pkkZ(is!~<^Z4QQQ{g`;yJHH+rK)OcD_7HRDSi)GYde>vQKBhAL80f z$p|1qJ6o9tK#=lt9sK)RM?pt6J+la7TV}wI*i68h$09(%d@gt+9pL;VMn96we26Yw zcP_f3U7os?b8y*%41E_4u0KMt;hOy6 zy`z9#jgFfP`wo(-r2FZ}p@)EsU|Jee{x^Kk`@FAO?GSNr+sX&a2hgLRL1*p!L7r%H9f(t* ze}vxRliUAo{+!P>9^vQYaiI~QQ}z;i{xY$q^$z(bMOurV(9^2H;d+CT@n6WdjN>T; zkD)33gknQQx+nwB26%i5vw+-ClRa|Rs-xJLtzNb7_gXCOJg(8Nt~JKx0Hi~4U7vc}jsD(NtELUmn;4n{^voJT2y}Z=a^bzP zj$Eck1ttKE$muXPNDdbQF=SxLv12%oX}u-|E3m{x06PGWPU?#cvi+cLxaN$(l1vH! zu3=nu#QPu2(JQumeIGF}LYqs>Tx`&T$qam`&j5dT)~9-D?bJxH5Tj_S29wMS6R^_! zJt*vc_2xYZI-IL|y1(TjBVn<_MvK7o`F=Vm>pLZ3A3=#M49JKmO3*J^0|+h;o0`|5 zoAbB<28JYPlejWmY^@s09YWPBQu3!RpWJ;fvf=pP9@w%!5{vMKSwHR1=%>(RDOs!7 zCBoL&~SbzKDtC*RI?aa~^t@u=MJwL>ck>|* zt)!1Y^;FT3zM7hENO5%;On;fc+~9PGbd{X6$0bm<^S;E&lY>i|?XmZPK}Z!8Y$8*$ zK#`wz5SUQh9Dn~AhfrISN&UsxEk^uHPa8C6)p%G*FYkqLMB#x2>kd!%p=dd&duHWD z=(!jlvAVY7Z?X)L_gkEj4`y{+tb(-~H`ji==Y(kgt6CW-|9=a%Fpgm0oGic{te5;g zloZ`R(@G}rwqMc}EwJn?gt@nEP&hBRq)dV+^?*ACG0G}_A{NL2%}Kg1@MuGZ8_pV^ zjkue98BCSza(}(J6o&;3EVrir{&w-bb)QP@gkjiuhmh*eiwl!}60}};BVTmJRO?`` zeQIEhpsG}>Q8wlL_q~Z1ln++@yK0OU=30H3X%!#$8a2J$2Whwl0lcmn9oetWWgQdwQ6j$NPz{i(eiX}RGuPZH6p1CnF2<1 z<7b-^PAyApZp*2lK(G8g!h)xS)!%&$Og+ZNjDArTF)9E=a}B4+`#erRcp(r9#*O*X zAOh(J#SjpBfOQ9XJD}cj+;EvpBR%*@&$!s>@^iF~N7a5fW-ZD;B9*q%^X-cMMy^kY zYMA*w#|@_&I@)k#NyibInje?G_jRx?eK1~65?uRK*>pYu%Ks%xfHod%Pj%^!>?>YS zb>#y}LEEN|!i8{>fXgENcDItb@O9fVS+Dx&?AYX`-;C4;`W~Xq46f7O?_2Fb#?~O5 zUPIWWouGY4-vQiq3CwS8Iazi{eZbo!{tAfZ&?)?EKeq1rQVf?@w>%>+^BNolVTJ(N zt!IVVE5K%f=QB*s^KsboM3ak&Ugc@pP||u)UryKo z)%k6;T$6i2@{w3Tx&Xv5*=!g`a8(|X#J`d`w|RmJx^AidIPXQUwhr#^9KT8fOn&ZN z1rJzg@E${v(=Q!EZbDdS0z?dDX+xVNawCZ@>Dv*CObXojJo1I?J}@6(|B!NZ%U+e7 z;gW)sK9bT9#8AoY;~fNUNAjxJU$IZ23rag{n@!0P9X)9sm}gbUjAj8Q>|w6aV93tEdbK7fObVGzgAQU4A;NSxf$i0ka3L?1V|S-o&dBLym!Qn3gA0O~{zB)0B1X_0`hr)_Oa3eoyg%aZQ@hf1RKtG4?ObCs-89r;EV!2eoIUF_{(w3n8+T~FY`A?M?B`;_D2ZHID~>~ zYatjnYvU{?+T3gtC2}z5w+ef&f2#(MJeR&BN34xkiM{9N3TfJU3q4JRv?4Cgbp4#W z)s`g=0daIg37G^t1?wp7MfW6C*17m3QKt(r7R)gZrjP4)Y=sGXC~{I72E?bH-VeL) zOqi+3-36~<=?bQ3IjF@ecnL5Ejg76AM2ZG!aBAEIQSHt8Uc{CQY&~F@X8a^Me9Jl( zTiCjx73pGcltYVzojzsodyv6Y(mZcH_< zZYAk~a%4W0rx?_!UEZO6Uh`3ZcgI4pX5J2ybj9;p7jd)jbg+M@`7RL zDU2Q;fAq6pnfZ| zE?s_q5q1JWhO}FRt#ZCyoq^5j9J^(eN^vsGDHL29Tm){!V;_Mg~5S#t|A1Q-ZOKv6400kB*pwL2d%0dliLAn?D0rO;}F+ zD04L&kt?Xxki^7F+8Zzd#Wo7TeO%yqsuE5I;9zKnV%pCiH28yAq`<}QkCG)EuMFaT zF%K84h!mHSEENtE1!STo#0xIR^c8dj|8scIXX4-H9{a8N%ZP0xYwH{Kd{=zE5KLxp z*rAyNb7(=f4gq!b|47}*gZfSbF6E%<&K-!#N11Xn|! z?MeZmt`8CvSM{GGiWZXz?<7fMw>X1doaU?6-r07~_bI z#}`@lfMJXqI{&o0YSn#j31C*hQycu*_!m6}3qAd}XF6sat!!OwDf%RxBR@gjHL#V- zou4=t7QSR4$Jq_KE*8)|p&y3tU?jkYMrQ6``mB+7K)y=k>AlFU0jT)suOjga-9>w5 zp=SI(()qJ^Ef1f0s5$ovqg@he+Y5rVFdF>-h{|w2fuI2 ze+oyZJkM)R^Lp-jzm4~upe!%nix80ok~8i+omYsc1{QI|*bCg8-E2nb+m`pL3V_NP zA`f}e9Mgu8PwDzem~hVCcKUBDIbtmgzq%)NXV8q7U$^LD@mW^Dc3#ziXt}Z@f?>2}No;E~Y?{9MS z!#Y}}Ux%E0oro2_`hC6{4(Tr01)F(KK(0ru@n?u&`kkFFMG$Cd?eQ3p0@0Y1MeM1= zO%*t8yj|4Kn36hc=l~w(DY#t zBlIv-ldHCEsS-a_0?@Q}R(Mp|*gc3xOTvB+Ag}dTgDr@yjk?dVe2Tf~NgwEbN%hL*C zPU|@7$j2jn`A&C2zbg<`ReAEO-9N9Dw1LJ{~5p z%}CMm@K~VukSB51;o@iD_i^*~rO?0C^}!W=*U{F`H_-bAnUj(RrA8x)WjXbYGE6j= zsi~@G;&IRot!t07#T?3J-(QPpR{!Y z`E*JuI^X^C(kQo?w`Nd2v*{CiL1&7*|J!9iolUqv1aHA+{Q>$Rk(xVDRr#_J7z=u`+0*$8{ADcpq=%3->C%AkRGr}l`T$jtpGrBs4Hu)XbN2Cj)qiW=1kpF*US;c(&o2heGX7961o-Njy(5qU06j0uJXf=hav zbwFXSrP;4U-K`*DkV}`h;mr_T3h2&*?rBSzCum99|5Z`HLnfG)^%TtE*=$k(m?0Z+ z^t^6z@6B7 zgQJFaD~1oZc_eleOv@ujZ6o3MVVE3!3W8R(M9e%__CO61m+gEMa-zkzv@i`OX$Px9 za1g`Prh&|vEioE(i#Tj6IzJ$<+W4l#(}SN{)8nuRd2WsgW&!J#p_NbNSET@taBwz+ zg{vZI=%j*15k5=>%t{@;J7X11ZSbX8)Kr@!m`N%mN)+)1H_WlAV0rPx8`N=ud}DMM zxyI&JLNXy$6U|`qtCP*TcA$L+3p2pdf7V$*X9>xAH8LhB*3h$-2xx9qs_=l5Xo5{w ziIfKB8q=mP`tcEhli(ZohCag+*_ZxCZxDBMc=&>waglbck1@n*6sob zlmk#@d2jA{Tcbl|?6ti~IM+8pcui#n4TQ1HIsk9drlEiJ??fgz?BAhX#kVb)h#}>- z)pRM9={tUnVNHg4SDIPVtun;UmrU9^6&GKpNR4d_l%+H90R0MRKgf^&dG<|3!xN8Quc@n0@d08# z|HGd}ER_$n10;hRz%)oI8(3Q0-X)A3l>oEah^QAUcc}D^z|+EM)L=^};2al#E7TF? z$~RB$cX2z`?P=A6b`X_h)GWmJav*m30GdLSQf2g`1MCM#eHu19$YhUe*moOyuEPZi zm-J;6FW0AlNL=G&Lu;mZ`N*ccZ+=_#Mczi5d3-JBd0S2KduM}3jItZg34AX4Cg8kz z3O!pr4U~YM@I2>C%DGvm=YBkO#hR-0Io30u%C&n$G=rZapW>B@b&4 zi@NLir7)%2MtneC>YxuvWG2~P@j=@~jGT~A?r~tw*+{Y$=2S?9m#U9(9dNZHn2l(fsz$$j!k|%PG+_&QA5o$rFAf3){f| zz{n2XgBYh8ua=M=r9)E~@B?x1eS)-#neF$~(lIsf&|{X5x7B5fF{OgMfo7g{nSYbN5N``KHY99J8-+N|@y6!a2}xcxElf8OT1{}aSB z(+JkKL~&ja7V*m1I@W}H6E6xW8B!dX`+EN*A(q20($)P-nnK@2H8(_a1iv8Mp8zba zI)?^kg5caf3<|SLw(9~&#$iuxk}@hlC~)(0i_;~u*WkM+EBOm%nRcrLMp@!^%q#+= zt!u@yurL3$c9lh4x73Pmth?!Ol~HNvhPm;~;Mg=2k@9s2YTQ#uUe1%Q4eVhNz-1(5 zQ`ztnL5#BB$y$oB&TEu2wN5>7K~>5{j6+Zp3F(Ua;S5C#I{sqy33ZqG8#|1TSvYuf zf1fCteuUZzDT?9h=aFjf1B(zAXeFBOKZffFL*{|}-|kWIg@%LPI%!@GUN=0-Fn!*& zfEucS+O)Cps21OH#J6EkZDQ8o2Zb3MgTMj!h{0k4E06po5F+DK_e!~?vd+ckIg-Y^ zvBVnCNfN4-gwdnjr-!fh%6}mmaA(e(8L?K6HO*3f;wf2^yM?biRR{B5%SCe>`DV<8 zaDv>R{6mvv$;)K~hLEo2rF$o#+QJA0BrqN?1D=UNb9BIN$4!6(IufyUOxaJp{m6pA zh$lOzv$U6bW!dL3yN>y(gOX@i#xv&N8cE!+DQYI7f}6w#t8K6n@HJa$cpS-J@5rYv z7QVUTtoYyg?g(cY8nbcfIlPcdnx}=riPd>R&TSxJHc|);Fv|eGKuSdy>~BOxAf36G`^Zop$YxDn*q%LT!5OlWU_l~o6e`Y$9NU8DWzbxI0$ zmDb)?6@C$U)0}f>r>^tcj??=C0lpw#Gp1#)6ghy!9BY{Qn7C~_#2AN1{07`wZzmeq zGbP!Ih2}x?Smo^za@Rlc+`f}6B3|8pcd*x3(EhSAHh1~+s0O!(ND+JffpTNASv+9k6MFhUWZD1>o1`YhP zrDf-pq8UOPv2eOFD6RrG0iLDI zyrX6on;OuK{0iomU^>f;M@|E7z+a>J(ZzVbi0D zKl6*2$1teRf}Jjo*)nGF>QhlLHZ;!C^BZYd!yBaHz1|XM(R-LVqbF(Nw*OAxN8^8{ z;rO^hY(FVulbk?bYGvYdC__n{!RyO=lvd-KtISlS>*RETMl@^(ihA<|%E{~;$##JA zgFc7sPrQ|4Dz69Ro@=gi)FuuvLy<6Z;wkU}CEQ_y#m*LeVIu50TM&lnWX|rA)`AwQ zqUoGXp3R+smoJp0~+gDS;PlvbWrIFWO2zzmM0o6 zW%x67OULZ+Ibn>2Z573z3{##uNoQRq^6XrkeWOPbsymI?2gJ?q(TbH5^`nL2Vst1A zrU8acvqA+?@vm0C#idRMrzQZYbec_U0^`m?ddFBg$t3LyT^(R*)j(X6yU#N+q}48ex$dX%lh4RiEadvdB$@dY z64R#e6UGOsi5I%87!6HY-^}xESha`mKP9b-*8MUN-qJ=M4M-ujACY_DDP0GW5|ZDx z*J}!cm#@_8#v!Xd&l9xPDEi43YFD6l)1Rv8iG_cJc=zGx^MdFR={EQcM?>t~Ec@HV zT{m*9^Zn|zxh~;Z+5K%u(3SSE_TvKV$Y-jS2>G9h-SHzZJ=x~Y-v7C+p^X#25Ud-TA3cNM8hQ`7Ek_O7?taKFBgwW42OWfz=x@7Z z>yJG{+dk?do&T|G0n70iNBa#`xTB@VLQA*Up$EaKH1bb6*Y47%6HXpg*ORGffzad| zz;Y+-e{{OC^xTVBAuWO-FT%u$wZX#d9FYK5IlfT~aFu)_+rN@lP4^cPH%M!tatLav z>o~a=W%1LWq}v_?&C*jwLN#9Pu{1y(mf|WtXZAg0Okj}NenJ*nI=LZzT)rHxn`wG- zuoBm1k$?9^-}69PYhs8IzHV+~wX^>~s*1{qkP9ELa`Ae(f_zO?gP1Fo zsbCF@+~EgZ&8qoNdrM0?#u@REEdgmsTwr{QZqg~h?u?EJ0u%w45lH`x>y)?McLfbr_O&^IA`*$R^XOdx`{qB@_f6)uRME`5S)#P+ zD{+dVGx&At zexih>m=UeE-ks!N7hBglSJ4kmW;wJ$u6nj4182+QS*+wIJd*Bz{n38z|C3H=Xt*7p zvw=3!K)@83H;7>&>NRjx&xi8Hw$V)y%YN4uXDE80nkLF>4)b(Xyeo{m%$m3Hy+`DV zRL1qafEe@?%SnTh{^ZCMgExDW&NG+(dE8L*L%=RE!^biK?sY?36cQE0BB%*<%ZSj& zJ0~!}ckb7E8T4}Mx2n^Ol4CU*wq})1*;l*h?i12I(AoO|LVCb!fYZdthO(RK2b{wI zVhcnnn#)Sp4hG>Ya!j*8aN0&dx8{m(kE2O;FrgDRUN>FT4hSc>BA7|fkggkNTK}O% z-iDQc))IJIxk1bKZ*qluW%W?{yks8t3ncHTTN=y<%N+lt&jr8nMFcT0=2M9%uq>sA zNGNMRu^5QFCqUm0ZZf#}G>Z0UGMvhM{Kak_;Zp>BcLR|c$fS0&jdJ zk9I8%S}Z}I7z)SGNS^Y)BH`{gzb4a@`6VKr)U9NI5vIYHvHHu{O>!bcnA?+`pj7PC ztA39;ZReM_MB5as!rN-pwS&|{T1Ywr`WM|QuDBghdO9hXmhefXzG56xFu6`n^;+I! z@aUmG0a6RhVO*~ZUO6`U*)m`!(~uE|ldvV;r|z<6qpy^oTi5rl`utvB9wN}+*xr_) zqnN87?~}=$3}>ehfDiY?Nq0vs!ek@N#TmFnZB;v}IX6u2y%Idm*TsYAk{Z|+#Y4~Kld_-n2l(pLnM_?nq6DqO&S`-;ad~Ba2 zUS(7XnlNqK*n}(~;$sj|GMsPi`FC_PISw<7-FibB+A}G`6z2g}CpFu-$o(j>^Q9+= zYxozzo&b2S`d7Pc=n)7do^vQ_OoT@Q0i^_cW}KY`-bGFyXf)ltQDbq|xl1OJ%cW+% zE`c=C5GB4TWC>CgT+*(i<<2?hEQ_q0&b9kbv4-01=%~No7~WR@h-%A8vm-H^^1Xia zb3k^&z)VwXE`Ra#yoqS@k+osVvBX`x$4lUz1dBkF$OoJhfUQ zk`LQ8B9IvvcfTM*I$pBSE*2dBe%Dd`;~#;D3CL1~7m@29RKK&ILRUO>(5}ebj^-E{ zdEv!;-znuhSS6IMyziAS1a(7rOv?MMqBIOHX6r?);=Q5fOV>rgF{eus9w^E2ZBtIy zHed#yFog9lP&k|T8&*d`rR*Udq4E}C%niz<-DderoZ(94`=L>ENXY0@(4FDgp*uJ# z^nSlt40Od8L~=Sb=!-gK^JD z)#2Xd6i&p7Rg z+r<@dfp${CIK%YR5BYs9Ihc<|)3l_2F}6#3C#W|dl%Ng=cuZX)`>nBsnwvQT&b0tf z@8Xhq4&B06zg)w`i;>O6*9_)tkaeLKVS%bUBjj9*)N=Ud*P;XGHOs5A&lNDXl z6aF7hXTgY$zH@%V^Nf4% zz1DTjDXRh&a@E5-1sDCDL>@y^SS2BtetL(!Q?T|FNMJ!E4>-7hxMELL_HW*$M~E!6 zV+(kSH#Ihia_}LHXJP+*X{vL4+*(%B$IJQ^^8rw6&J6{8vvp82Y^s`6a$;q5;YWJM zZx>oOEQ(Z;qFNvu0Bm=+KOfsbT8?v`h&#-n$K25uC_;zwZL~=&PGOIx4*<*7+=1Z! zpPG*DK49DDRV9|B>MAyq^n7cZ^Z{)~I##N0B3uIq#lZX%o&2}`k;H+3Jx(`HBLF?%_NzBXV$JZYR(UYQ?N#WCJyw#e zILY%k3BYXB057=lE{=XLw8Jofs5Rw0kVjL%_9`dZ_bq^LFRufD3%`LP%>-xhZ$soc zH?Wq_#_y7(_$@r~`xbSF)Q*Z*u1WapJ$C@_8dVFkHWFP! z+k_y>If-E>oOH+|q!m`>b8@%v&8ksa{(=D(-&e{EYRy?E-VXNU=t<*usGz5@H%Rng z0fFrR`=2!Am(r=NQx92Zb4&J6&mRYmuj2-r(Yo#jphdM4ejf-={%!)Z3n1rp)*T21 zTg2%T;|;-mnw1gKIGET25fYfaTv!ohbSB6|VpyWRHR#jlg2 zCoqrh(e`0JKt_>mVTvO5Grh`cjzBgjWhg<<6sO3{!i}coN4wl~umV zz1vFvRd&LuZv^T!&`TS^7qkb9-YIvk1ZgCv-!LA}@txoF@p=7I-{;jt=OIk%e#YK$ zc)#%{MDbm0T>y=*|JgwSuFxgoUC*jNx4qw0|N{vvDTY&?fD);hRI0XSh#Qgz>S!q;Pm>eE}&Kr(hr@VjSWYyx~ZzWSw2T zoQPy$Q=lEY8L+pp_0k0e`G$K2%TD2upTTikd!+MVVv@+0_yv%DxWGL~E1<3#*e$em zq!fk28qhp@kh(dUacP14=v_O?DNbQ?eGr_zBlk3cY&YBm8h2rS?c(-3Sd*b0GCwgc z!Yi~TyjgwQ1fTFLJbTOBJT!hz5b?_3>(-ODadqz*1SR>SZ`A=fXx_(Yy?r>KM1zPw zNnHT0TKEw8t82czdW0RP@LqrE5=XKI5U+nO5`Ywmd+xf5NJe;U*;CF-761-XISQ|7*=VDyGRykYmSx-O$kS+y882kXp6|UBxo2}c4dH!?p zdiNO4lUjcQz*z%$Zlt0C0bqxi!{d9?;LReN^HJ6(d^gu$WUDl5(WKEtoPfh%4iCxz z6Fpq7Y~CyUB72qR{Gpuc^`G5L6Rq3K*eP)afxlx41Yf{{wRF`F;8w^|)3~&Wm}RAb zB_x8++CS2S(S=9WWLui59Gb`o$_NJXzm{W=d&smk#X}-h|08dfwT0DXW0s>alpltM?*uS ze)o0lNJe212%Y#ve3}tme;xYzbo8SUhjGPw1~-gcF@{o zMAd7se5x~E1Eg9eu$E3~&ujWwOGownl+a+=6_;+d2n^!p*M3tBQsrP0rRd!@ESulQ zBc^$Sp?WZ>b1SoZc7J#k0;W4iF_j$gmU5-nela;MW6u34RX!W$VsG3FNd)AG|D`;C zd>lwHi5pW}hWK~!nf)lL=$XqDiRAo8&6}4Fd%#Nu9FjEl?F$@tqhk0*AHy#WfSR77LN!7=Q+wV zp!`mxw3oQHb9h9L?5`5wb|o2NjtblTSI87hY1SYWF+MkSup)!BY)Wi3xtn1v{MRDN zI6pK~c&(Hv?!$bDdb0lt(G~9g*B(87R6~C+B~-IshEg#m8ceye!_)+f345GU$wtMG z$HV>K(qs6=2-VonsCmlCg4?MGiJ7WcgH$Q*NN|p_MODokxX~kPl3^OngC079cQ2FH z1X{jGN3+f=NO^r0h#3>y>tl+0UNd(YZk0c4d|6jc|AXv^Xy!UOJ>avlr9NI9bLlES z--k+@;`dQNk&Q&MtO&oW^!=Yhb}jY)h9SlOAC)3^%>qvubtrO#mfrDr^jXr-E3_+e z?oZg|&g}v<;hlvsH=MVrXVCa}fh62E3OxslpA1}zVI(46YVfMYr0=GnN@wPZIQ?Mm zi&6V5;3A*b`7C@9&hY$GEE(4L1UvnLkyvugPutqfRn)f0Q}YcJ34_dBCO63H>N>&0 ziEI$I2rBYJv+fM-iNEHSKn$MXx9a)lv?%q5;I1FEffh)V2+&>-h%ONBgk!lL1*D0d z+%wvOydF-bT3%?T=3$aMvu?sZ!;MHnST_R?;`o~zYvM5`?wLPY#JMpuV7IWaawoz^ zs@H_MYmi8#*xT!)hfxi{gU}G6V;TyZ!un8hy&KR*SmVyY%Ezs3oO&Koc`hkziUzVL$ z@@1L{vXBQAO<(h~B!mlY4E7fGB2+w7k1-o`ZYr>WfyVti>4;=dorIfI76LIvfn2{r zY-y6L=?#O39tLDI#?_&Q8~$L< z3XBc5g14n$t|;Zv*j5HmuLRs|f$i)mFN5VaD-_OGWk+trU&F)&wOl#Xes4#8S7hE3(I_00ogcPgKJ@rt-Z`3Bv;W#U96sMVqL^2wXcm3gT7( zPw^-qd4J&XMPc~6cE$-^ghZH7Lw$YpX0KJv4Jjbr6Ia`UpY_OVEhvS(Wd=L-6lO?B zR_wiGx`iOG0|gMSS2`Y7S<5iVBj7X5R+^<;vS+jzIb5?^<-(R>DNloY;)T|9gC9pT za`P|3jjBZgksFY{Ljh7fV zdPfdjxibi^>FNnz(ubWY=RR`=n}i%odU5!x!S*TcMSHDzTD>8Xn$8Ip7x%G7M^+?qJT(VRn!2!rJujHoU~jucUSql>h;rF_{am^`EAsymN;L@Ghn z7A5L<`7pZBw_`Sqx0VYC8May*GE`2EGCaMJ<;BK2R{`1{dUKmo-&Hybm zh4ghqp9+KmMUlH@ExOrMmyA?qZ;K47jJ?gCy@q$oV?fadG7kA)UXDWRFQEnG+-m{} zbgNdJapu@55{YG*3%>kbH)MPBoG6Mzb4pRr*)X5y_Lx{!^P8sQ=Rp-j{|9>O`%39~wzfW^=qIimB!7G3ahv5@_a)Ng$e#ny$kqGG?65T^$Z}*$MY0-$aj9Q6;=J>8+dgw|2>kj&iWk~+J%+xF zQIhvfJJcg|ec+N&Q#v{p3=UxtzKXXs$6@a^#tW=QE_aBW!|ny4K*)32?Q1iBhUE}H zC<`?a*RYC2ih~n&xi1r`L>QRLbZkI6T${NYx1&A?MW4Vb`N=AF3j|-lfE7d5*6awf zPb$kWlR#5AC$l&Vl?LOzGDauDt#bk!$Iy+a&W0iK;X>H$%FDe3i>?oG7@P9cW$daI zHMg2R^;g>q!*fY0~ zo2vu_N5iW68E(*BXLPHJH=r7^P+EoA6EMZcXDDI+8-W|oC7m8MtH*yXBjXz(xXa*7 z3&--Uu37}~j@9>t$VRF3Yt3)O6jG3qlmHUlFaSy+Q8#RY9X#NC>S8ti={ID@R5Y|k zMrmJb?pXKU8OU3F0~~R)MPq|3P*~T7KBNd)(%2|MG}2Wx1!^EV%}wV$9zQ*tyL_we zE;>yh!sr!JFWG#GUCmDnbV^K%hXG8=NA^7s|WsUPThAyfd`os4pUjDhS zq1gZ{&OHeNZ*>V(H9)ppB5VO}@8PiJI@$`puGeRF0lqz%e=xHYTdMxDpz~>fym5LO zQPr6jJEuo|>pyWTOC(Y`1I?y?wbfAB6DxcPyjjG5dpqHR5cN1n-c-Gho9537LcrS~ zS1c|YeDEFaeZsnaq!6G9WPJ&(g_W&3jCedlu{qE#UIeXl&PQ=_yAmw_Lz-%9-p5Q% zhni?X4C-b=s=*0P9P8Xf&`+DY5#j{kmG^UL&_X0yvL_9>g{{ z@s!Q%r?#z7^PuEj?%#7-@sf|x@=d-X5tCOZwT^Ou_n!i=RU}+)gDjFLYYVgMGu<0K zt8X$cwQL+CuLrap8C$u3?`K6mtY6~dAggGPj7Vdn!|VRX>wutkL?`_ZdrH2@rG7n1 z*rKBz!6+F`9gs5zzF8YVZZ4SJ-I9H3qBGff))_NjOOCFypyAZeUj<6#cnt<;Q48mj z^1lm$zb(G;KaZbjyh5KZf|<^+gef`HORf$vWlic-(Jl+id04ERfls=zot&5ZMIigC zXZX0k5<+m64{N3H-|N7QULH3(#d$pNOB*z? z@VM)_J&eBUty+I~3||;V?#D?_`BH}7D>?9Cg3?AVgsH2I8eFP{uTjqm|E$28EYDn81XxL>AslbhBFeWRDSGXXlPCu2oV#bBmn6;FvkECKw-3EOz-W5K#$gS;0{Mj%kF0#7=@0%EiUtS+z~p`M%rnR+HaZxLZCY5w z%>vQ@5!i9jH_LGj_@~{)`7w9D+3?2hRa1b9uCkhd2n=!HBH94yufQa))*qVnqgS4b@82)f=l(abmk14tMs$a2Kf30e~P#M~k%7z=UpU z>XcgXy~k^lk*YzF7UQ``>~4FwFE+DnkLjIekdbU&zW4Sh!HPmEgu#OTWSqz~E$mTg zeQ2;u%Ma~*g2s=LlcHB!gDR?ocVyAClO(Y96*~IsH>D&_o-eQ!Vm!)<=hY{{=J=<; z#?#OpFhS6d=s+yY>b-Cy5Q)M(aV&!*RO=DQzJDE}{3X$M`A$mVe(NkPZ6S^Yj~G}K zIMqc2>uALTl>}c>#DoY=>g<_V_&ySd#?uJ5T-_SXU-g!B<$->EFjwLP2gnzTBQz0e z>wMm!t|78G<<@XyGNuw^E~|q z+^0n#gzGBNeOVru<5JeTM9qfdCwg? zf%(UMZ|usl`RC`PW!J}lDye?{H8tWCz*$F>Uh5V3E7Q}v5W9H1j!Ok*&?Fy^>tc~U zT3=GhqOt6ygwsx0vfjR&6PAK^r#{FTzS*MUa+s~~U0Hm$16liuNo@iAJu>?>pa*nM zg!8FJcd$<5WrF_T?gMsM*dL0+T;XSM6&g#lN8(9FLu>#CWdgEB{pSQUIWi)x5JF&G zgB1jc9NlnxQc)HuPTmaNOnVIuvWh0QIVyn!E(FQ%V_`%XDOT$W&-hi1DGEwqb8;Er zZZlNSCHh}Iu8E5adg8?$b=;Ur6FhB!Yp3K}var}-C4;q;xS^n}oxlsvlJL)+u$ICK zmn?#wB`PuI3~Ebxs|f^P$jhVB3Nd9jd5}rO>pS$T-!y+ zf#N71*qnly-}f-sD)2myMKUZm*66jr2IG=KU~LR5AJ4q3Uv{gF_mL_2M{Z#{ zDTzid5aVU&l6P6olSqc!)kPlQZES7-AQx`6E&q3J?UesNMr;lf3zhFM%|&V) zE)FZVIt5uA?XDw-{__aJxM9OmLwau5Gzf?QilL!YTupBu$=}5>uW%9m3q+HFdn_?# z-=B(u*KX$3kubz?uIJgOzk4FK0Mo`B{&tFBf;X$`z9>lj3j(sNsbci=ef3dv`7y~* z&w(%Zqwc@|YTaCmwD)BOYTkq=qv%jE)teHXgs$4M zAkIqy7_+OJoGecr$%x$U%K$RLkh6w7oU4N!<=@h~G?uILW<`49Dm|-Vs)K-uVJvF_ zc8`ZA08zc~+xz*~_#FpU-48i?CQTwN;IxflPV>H5PeWtc+=yGoQ)J)r0%Af`JIXD> zVWO<|h!22#-e)57RFzX)2Bf{XS--TFJuMzr%5(eg=NPu&0}V;Tgn~u4V7P+|6`o(a z7z%erE^tc>zh1Og{L#{bti-?vV~{vT%=Drf&>nF=+Nr5ntXWzE#$Zh+4*2(dkH3(t z3Ms75XsU~}0(+0?K@0xtdna*fTYqcF$zFXIFrP&74LM_-*76hk3u@%|WU&DoEsHz! zhh@!0Hb8Y;6G=OIdnU#vtOaxEY|66+J}_fWVu8)3lBDbZ_UyL!nUViJvuT~izP)z& zyiLAgb=Eevs1sXd0P&}hYt6~p;&z<0o7!C9t=N@-)G~>-4Nvd`!VvI48=HQFdNH~b z%Xt+`4_sSTXBNaUun>IxB+9|+8KQ~GehYGG+Vbt;3Q$kGD=G8#qAEHktce5`aj+J+ zJ{;6zClDJu+AEB&G*fCy%?Sv7X!GH=YhIS8W$hdX7sEPno#^^MGD!w_S|TZK1pcjZ zb=|$ydIW(dLu8TQkHE03N|dO&A38-Ms?mAhJW29q0XZyKSZ~vqoz>+59)PV1dbK)Z zOMSTPJ-#;F9oc<69P0i_6Wtvlt1Bg(9(kp{!^T|1g)5dtjS-FHyy(7%=h^cM1VeA; zv1En7h~I;6QblBMM(`!l_=6*dKruitt~mSWm7RqH8L8Q@5CgSPke_40JdtL0D%$NO zeF-4c9D@Db3<8wITCjxbFbO+(*DLIr3>^l@kCYB zxx0@z)PHd3;vl}lIWUtz#(3@8$*i*W*sxDihyV$4xM^bvdeyIZt3oJIVO>?hEz(V1 zammM>_tq6=DMzSgnE~3`!wgJTW724r|Aw`UNW5Cg@p0fbv<#B7e$&gRpZ&xj7vKbN z^Se{fsb9X|JfiH_Gx}H0wJF^3-`}b#uB9EAfgDS@P7QH$hMd~-uvry2Ib)yVryUGW z$?Zt3y!zs;BZwTP#nWV~bBk^(_AE(V)rY1e>4Ps)3nO?t;Aev5GRcKr z8q!CBdh^!~{jj}QaY7%_G6!$}tse*N9lOE2gY1TCa{6Vgej|#FO+}*YpFnEvl?{d$ z((Q&aoU=m|Ez?l(&aK0O(BPGbB5)Bh_4vX$4&;(j+iu8DH!{8B+OSI5RJd42XNyh`M? zJ8k`+K!0vd7jh}2603hoG|=^I50TmQ1peS#{ZwrUa-&JQfoNP@=Zgfg$^qrXXLL$d znz9@#kUOmOySV6qzN#-In9TuY4T$(^HEz1^h*j+Kmx$`7e$jr&H(oaHsHtxpVDMVi zC`J-=_)@dqJ$jX3mFX8LHVjL;sR<3MM9#(vSFm9Swd=u=OMb%I;P?u4>^LHEL(v2- zy7W>UKmG&cPzdY7gt4&uXK#OR>n=gwSCevw#*KjFZT7!3%;;_Omf6^y^>u`AsMt?f zUDes5@vx!AW5J#9XD=%yF%uyY?!3SDU&oYXkfV(bNEg9rh(J(JC82~qqY2~Uo|ef6 z9$>dY|@$y7RGV-v{sWN7{xg^GZ-8iT8Ze zd8+DEY`vF9d{`4tkQYFoY(LEMYWxpo9Sf!Ia^ca6igg_n5yxG*?vRd`z#fSwdYCn} ztg=hYva=PHnbvAt+?;k5)vn^;s2LQDEd*FyqlfKB7?rL9zO0N;V$+l!dGsIOVoH{5 zZdVVQ+y7qHu>(}0az1%~o*SxvrxRS-3f}kud+QB2bEcveZu8;KO6Q{1j(S0DDJgD zHi)g%LsL&_A2EhGkKT`MwgeXz%$&~!7UR*d5vgY=TK9lQuB6UXWIU`k>k|oAT-6D3 zw!5xzo}7Xp#1hegF17NBu3)zI?)ANIcNO4+Bw^!q4W5AsE;W-64wwA5wt9doy+ki0 z#Wj_*6gd#Dsq3l&7is&8o41M%_F@~E#zr2GBQJKwY8vdi=ROhyq;|$e?kcX40};?N zQIcXO$&rFdVK{2lW{Jm%uIHnJAw75D+^P*7 zE%L2tT`w{8E+dqB0le&H4czP@gaeo8Q$*Hjp#T3F{bkD#7xoTr+uYa*1j&W}{wG7w z_=8ul6`h#cMPl&>IGc@7pYwom+lgzl#Nv2J!>P6fgh-?)bzfoeKHW5>K^CwT}QYMW$n@We-ehno!+mNZMGE*^0z8{Bp$;UJ4dBA`%E3ss% z7WaaIWowDm*fav>Af!D_;R^2RnxH}Y9{ z(Tjxq`DffAB$kD`Jjb_Q50I>B5Z_+^a>6{w;;~9#wX0t+Lc6knU#N>AtNaiU(ngK1 zB-&1Qq}o-8@7>64tlSQ(o;+OEF6|tU7}u@z+csVjrgH~VC>4Ia={d^z?d&eDj8wkC z9P(a3`8mnC>wPXfwl5)h=bWAUe=Lz#b(Q~(JD8$yQ%{^dCG!DF3gg9OH_fYr+sg%Q zg!IN3(<0#V@kOjTj7xO!igEDZaU%~)Oxd6CLO6@{7#5wi2xg!(Ay(`UHerb^Brb)o2HfsdEjXdC@2Xz^P68u&N4g zrTgwwiLR2=6CSO5M!z7#n$fT0)Ju(uZ;o38Vs za~pf09`1}@vOZZKx1d0JrFa&&XQpWLS%mNAUH#e7u^Wnf8R5*K9JfZP_{ggR9FylQ z65$|Q8>q?&)J=v;+Ga*Rk~Xbsz$tSs-U=e6ND9yOQkL;}t1;IkL;?N-w3t~wTQ@TA z=(j}rKj}!Mygzn6e3y3HS6jlz!)ncSGyN1iLD^d(?R9AC7W=F>RacKv0}un|Ce4;( zzdMPvJ~yjF@CRLfC9W!&2Uqvxej`vd5bk7mL@X6i8M)8|DZPr3PYcDH*18*^VqdyR zkeh${oDn-99dR$c$DHLxCCce!AXQ9Bvzfcjd|#ozYS}Pcj4R@^7Sl9Mh9Jc6P8=Ky zZT-RVEY{mWiF0SVIqUHm2#{0Kh*mT}ALUS%ij^RTsDC?NJLUM^3R`M}8UlD%R{3=P zsFR6hQ2XZ?;?(z{$S8zLWyA``PSV6eVgA>P?_dWi? z5c&avKXk=usV%2g*4RI>U+j9g^i2yYIk1+y(!Y3_e;X{q9zyUM$azWp^BoVPo8g1t z20LDdJ!KHp38*|`AxbZ^IMyKG?4wN~>bfV=$hCF2ey<_28j^`apjqRzOkPC9;X47;m&mmN_MfFXohG`DNF{RbCt zw-jUC9+TUx1FmB*QX2b>MBr$uyY%qh1t^uC`P5gG*B~WkfijB8kA1s!HD5TJ^>Kk_ z{eA@W_U+mD3*VMEMS|yMk|y*Z`+}80jCX#YVl-~u`c84DqNIST7fuItj>NOBy(rrE z6InmR@pmym9Iz{Zy^gU+1MR&^Yaw2@lZlCFwJiC=N?TA5r;n+W;5|P^TKI29?kkOx zIT~27ayz$ED86&6E_Zj-=na=@!GHb1Hb}Q5Fb2$|4($FYy@OVQjn;i!=#q?HtE>Gn zCTS+RUYw7~g|xhX9Ag};Y<-M!%95jXBf&~4fg(63rh*3gGL5RxHD5a;NaP>0PtWyNre^t zvYq#CkHpTUKDb`?(A3InatZ!9ls zjZq#!6&Ptcg)=g6mk6+Ve9Ai1Zm<6s?U=qJ$gzUoCDFHQSaH}_fZ!# zTr$Jub*T%;Nj)Oar`~VTXGgTw;5`46CO3OHL7GF)#?`J%m;8w5DPsBI3L#tpW$N92 z%@AZSQuEcwBI)Bcq|9jv0qAy?pvJ@n0v5Wg9Ig}Gu|=d#xW$J+J`B=h#ua+OmS04( z1#*Sz3Ya(D%JcTB>|$hZd*;+<8KJ7`|HM@l{T$fbYT^kcII7C1srvgQIFU5zwp25f zRg%l8vy5^zOQf;RDP>H~0fSo=rTwd}o1$QbPDL*2T)i04U&`|YcCXFJNC`nDeEiO^ z#8?Fj9E_XTjfGCr^tGZxLhckTbate)VzzZIE|<4Mtm5Zbx1`m2NO6x!}l(_ zg%fH$Rp8tx^_pkNWNt9cL@8@Ak3t|<_7=SuEQP{~EEyNdU4Qw07+MM{W?!Es*-nKiI3g6QwF{iX=V2KzH$!Te0OwWkuIR|$FXlLPL z7PV~c2TTzE^`30X?gS(8Z@HWgcitHqXC|(4aA(19LAF>TsjG2Fto=$+=&OfosRzV; z;a@0c-~XoByHLX7cCQ(#&QX3LR@b>gK)R84@|MrtX-?@ z_^;?0D6)#dHf!(YW?r}EK{#DLEXqDv>qjWcMPZLMCj3T{PpIf>d)GHLvLMGQ=hP2=E z{FXK0<#i|kBkzJbt$Y6Qg5aO=V-`A!m$xahf>zAG%3f&hNtg+b1x*{cO+HIdp^7qA z$G^;7c6XZlnLqb7_r$Sr`3C=<&zw$aLWtqaKott|1rg~P0@F9&`t^DDd#w+I;E6Fa z#ULrNE&1KX$r$DDPna+VwPET*pdj6vx%$!6e~Ny#=6e&XBkyareai=6w`n|_J_I&T zC#{N&Y>Gi?tQ`rHA8`f&YTbSe@V&!@$2wc9tgSicTpXWw+^oDvBs2q|PKJMwrp8`e zT2=c1!G#ir4YB)WS1L-eUg!Ose~;1PQM6=z)Y%l=-af`? zQG*o*p#_0h8cNgkcoYp7SvUmD8y<+>uGXXvTdKdN&h5aUid`#V4uLWAm+&A45E`qe*Uo; zp=MF3W+rK+Rw{E+Wwk!LUf^ctYVKwZThj&+4r}eOg~2~H3_UPyA?ce>^stQzf%36` z8-m^zOY+20Z~do>x>Z<BIs!A<`@HUCEzQdKLyVxS(bC3i=bQ*H)p&o3607u8{|5 zIOv^fdEJ%eWvH1kIAtm}Q^V|W;jFpb(89!f;`oqOcWk|{rbbq7=1$*?v+mX}oVq9} zROaitem1MGlEo@1u+#@11s#8P|A;~!o{9YLBROFd4yK<2?fu8n)iRe}~#nI4tCqhHl7GS0eK?c%fr0Fx%M2goAA*;%L|CP#<<%`KDwtcc=>+ zLo%u{6dyUH%$C%gEO{h_FSjR9w3}D-)Eb$iT^-<9D~UBE2=#?S=GqZ7hYhK$We$2! zxGB4#ZG7&jE09*bh6&DeCn<-aZ=59fm4zNjEm?TF?-bp4*ULfz-P3oFlcon~{1d*y zkzjXF9zb3C&gA-*iNt!ta!EfyOdP3H_&;1<_K9P!jPWBY5}%!wZ&tdBmgGFYk1LUW zlqRWPZMlQz*%*GF|2=P6ilg+iP!1!y7}XxP;PF|=r$*;`EO4u-f(ebt?_{aBC7gj05syz7zi5j<$7uX?@ z?G?4Ox=?t7L5Of$gykaA+5Pijk3_)TMalIO z>nfo?dd!gYr3fwVO-SeGk-U-Ni)+uLu zUn=1hdI&bu7)*(>(R-sqq;u9mw9!qI*kypb_CW-2`DczwmC zGVS&>f7jL=D`>->p)@da#s&Lj&MG<3d)#&}q?%#fv%vHsSorSfgk~AbI>g5Q*T4Zp zDbx@;;YAdiy&}WmC1w&K0e7eSzs9xEB$cAE<(`W_XBXvuUfXa;TDQ&DOKbjg@LxMw zeKAlJE5}IWey=H(wFN<+2n-K=0;{s#xp5S{(8P3S7(<2?KYkX6<#WW`oTfasd838n z>jq+epBI8Rb{-cj6o0#b@9un^GXB+TUSZOOD+M_(ZD+;`#pbqD#nmlA463k3VcTn? z*Hdf;XS|P4`wj%$dl1ro&)?s+UJ2eX;^}@8BJtd|ba6f08gNc=$io`5yX~?04IQOY z3UMQzBN^kG-j3b{Oj$9?me}ll!WFz8CAq@kb0zY@xH}Pg@~}?Q$=)&C9*kA`d*In) zza?z8E$?c{I$eR@6!5YOC)R!e)!Kj#1 zi=#j0uNo?UpOaVc4HlR%2YNLd_yk=cOA!SE8P^0tjdyS3&uiC?*=zlCa%mEm&0Kyd zu~OMT77R;kLUuXWHFW6dQ74_+zKwfrlB*(;J0~c}R+}e&`@VZ`BgletjvxQChp&!A zH@7m@-^D%1IOj2AuF3txrFZK1dXzNq$7pjCDKuTMquDlNRf%sojie{4<&njYIAj;X zJ*}i5i$aWi$;sBbHgA5@rlq)WRe$PS4=9;7ByDn2vhNf73bOhE^S9IQ*0eR~iatdR zv(}#u|0|K_>`*R0v-eH)##5ZZ@6`BBH4VK#f;+J1-Kg8G-(fDe%IePA#vTwh^ccJO zG}1-l@%oJ&((XnCneJs9l9cZC63B>s_)}_#{!fE zOPT@ZE#E7fX-)GxFBx;0D;rR9#25(M#2m`k)yhX!1TrI`XcEn#ZC)CrLkL#|$UeX8 zWR~pD3Wq|ym+~dOd>_gzdlJnR6JwYG(GK1H*=sRltVvBam=_L?5ky+(pe zEH^%6&QMzJ4YylK0&XJuBTEcHaQYr>v3ma^lYsbReoRU1(<|r zrkqG(A1vBo=3P9{j*Fck*v-Pf40n`PArUXrNqi0&{Z zrZb8$(FEfKODa6$81ZR{o{PNlQ5B?JN>B8-VzoJnd?TMBV0VHHTRH@eItrPLbX#p( zqo`+4X(1pCN!z$oe^XU3F6$2&IStG?l8L~oR5ucxmauzG}`NJI< zB;REBWS*Y{_Q7c?O;(1-X2XAVbRhZX>@e;k?fQ9TP8N^(Me@xp%zHguXo|d?+uGt># zfIURdYbG{G4h}{g`#I#VJJQgxYpQ#4aq4p?voWhp4*oim_#kFk+f?ih{t1(DZQ=|%3vxb$taTTJ+6i8gur4$^ zyF{a$)O*oyOcHuNCLyDb-P0&mnO(eyOA%RKEzeXy75GPRw5(Ht%6^in!2V;3zZi7L%@-UUf}dj5&tzr;J2m(u zZOm>r@b_?P15(*P+FqfpJ)fT5@L7zZsqKS;CZElGP4TO9%jU+T)9*Sz(FtU2^zZF2 zk8Q56uWPm3#mwT&ZZ4h(JRI+LJ^%fRa$>C}^T+96rkQ4q)!lJ_V?ghNs&3zpJ+5(a zH34b^%4Ce7eo@c!+pQ3$;-cz9Og0~L!7s}|7-D2LErUM#%%}C1rq^yTT9rocy2TRH z!Rvsh6UX9?-6hJ98F(>`6+1Sp&lpjCoAny?rBWnOq)(^XJoWs)&rCh9$$J&=YbZEv zjb-CM%bVo=TQKuvP(OnEWXOVA?TPPG5p8uc1d8>uJnsl9#mwJhrc$hj*-Henbg`<* zGG1prWG)|wlOug|D$vZTP5s-BcTPCMs6bAn-Eo!r_SdFSq>*oK!RL8DeQZ%Ot0cZ=PmHhk zK1_4YY+qQw4OJ19^bo)OlK0*Ya0lh8Hc7_kSHCc0Ljx1ELHOy5<9D!|a(Cy-shK=5=rc+-<@VM2$zI)GY zww9|bnr?!qPbDSzwE>E&>_AkP#b{6sbPuo-mUi9As z4Bx{K+U%uwdl#n$0h{5#uH^!;4k7tds^bk1~8Me*6zTb5&ThDGHRBbB4gtapJ)lw0bjj}}Ml%Bs5!KQBhdrp%J(#$d7&*Tgh0umwPCR7N!qC4JQ{AJ+!7D9D{*qrRjhsdNK(VO z!~)}t*gz6>Lj;?jONo^9&r*Or)T`W-`x8g~0d?uox49$ahABplW@3I6Vo6?Yt{<{| z^GNDJ(QC(1Bi7pv;^9R{p2@e3xBaV}H=${U z{l$7j5tT-Kr3k(AuFEcG60ZnoTzwtfP2EX_jwcidp?kv3L3W0q;<&)20GIxIQfycCa#=FmS zZ}x)vB|fw?n}&w+k91I?`+k_m+w5S*P2TT@1QXW%^!VkD20F2@$YSyiwx7T*hTv%6WUyf3Kw2O&KMtg?U?Oor1 zbA4R*5gU%-FdMi}o_J+G2 z|3uiOtl0;@6;=rI)-U0DDfm~4==U$|fvLiA`Do`o_2NqfHyU}$q>l@VsAr+Pq0*!u zS@cV*gk_b*)ee4?e#8`o$%O3aK_-j(={;RFzD9(~O{tTh8K==4qL@&JgjdHh)ap1t z<#WXeYyZXIORP+`B(gYUxYUI}A0)IxWX!o;F!$=)li+8}{MNU+QMZ+_uR z^!w-Bs>f*fLJ5D8Ye_sj&nMCzL}SM--g2HkYphL)Q<~)YBV5a?Bb3U9KIK|Mz8P`ZNvyQ$@O~()ZPg zG>-VfpV9E;*<31?phFpb1^U)~?@uySv^VMF$%UjxYsR&%h_jcURcMLST`vL5pqK8@ zNxTs!@ArTOG-t@8zJSkL*xUh;e=h&}=yH|G)i<{x>gVrn{Ca;R0JUYQsVLOg+hw*ZTKYa91N*O1GgumtL}LB1^hyF?9xJN#CVP z*OOmn*DJ+5RF(_HqDT;{=I5Lhsy2}TeRQNXdB~NzmKRld8!%T0;)D$NKTN%2c%EGs zwHuoa8ygLprm^kDw$i|t+4UP_ zgLjVZC7^M+L&k1oGAx5#1dP!{B$iN%A(8vS(Zalu)*@zi(7k&B?j6K-Q~uqe_U8B4EJKSuR6Mz?U;k z?J=9242<)X6oS2+g;Fy?PCcWNz?vXzS1fG19iMEex4ht?=5gI*tlG33P4`6-ujzQF z`pHZ5^AE}sJbS8HD+AqxIN+%xx}x>$>y`X_4sEE{7wnyb8h?PJAUL);*HA^_E5ailDlkYoBh?s`zZ@DI4AfQ zw&LV&*_YK-hWwcbLmZnADD)6GW0ti{bEnhYCE%oeE>4-lNkaYpS9|y#JJ`z2`IPR{ z{B*+>?bq}2>2tfQg^d#o{()MN1;n2SKDEnkY3tm-3Yvb%zFtneI99}(U2ls?%#jn0 zcYzQ$z4zw~A~4LOX4q`y%I%JClL1qnSiItG!}4EowWgMOeptd9)7$G@L>yiR`mMvE zjM#qV30<1uk^(z~-i4gI@=_{OLA2jV6<|u8Xic5I#(P}&#d!!$PZTE1$Zryl09&*kS>^B`3K|aEz zyk$Z7(>&@~2JTTdAwTRXZUggSy&?_B;gZu=*bud-x-v1FUAtn#buJ zoG$HX&Z!t*rdFoZe@eI~2l{f;~j zhFD8aYj9NBGP!VIz$AR*#P%fB^X+@)$H?K3d&L#aSN(?%%5~e@%5E#-_8Mt@+XQ`q zA`{6I^A?W?kWxIE^!ye4@Xxy^kgajBH3YZ{{gxZziW|!hx5SWK3;L=@R*%G0)vIoo za|fS@4x{h?_$ZI6KWomG!;M{c4No4mo{8{CSj2#@i~Z zL-yJ}Kxp%4?nwV;7C$2EYL9c5OW=u1H%&HH{EZ@TC(kq9AQOVg)WLN@q9L13LYdao zfiAv9wdFS^;VHP`SK1~nW4r|ReUv6NC1p`wra3r(FJ#4T z8(b8M!gkNg_wO6-!k>#m2m7G3Aq4d;{FCj3VlEWkfLe)4>y8_kSEFr@>ily)Qo?xxkfCvqV}eFJh>#_LAw4O(XSh1Q%WJib4;alOCtZ4F>Mx*NJu-A^y0nQ(Gw#J z@6poMwF0Q@1Et`iQ$j03mseKGaz=+AXqXsRh_Vaj{N%pnzu#BGe(=2Vq+Mfd{$7>l zA39zbCchx%r+Y13{S6ed-e+f~L8||1Iry%nizdA^>9_i{aP;`*25otHp68+WyJsQI~wPk3i=IvM!74^Unw42<-X%@vv}Ywok9OT$6q$Rsbp5)t2^mc@=LHsUeM9TKzA0 z7_>-1hL)St%ZdZ~V4uEi_>k%e_aa>PQ>({Ks^yik9n@M=r&|liLz9t0q(6q062Kql zy2U$bQk5#fjbT|8(urJRf|10gxwi$`6^eCwLVAdJ+tg$jd{Gy0ONe<)mFr84?|<=8 zOm(veS*wi$uAs&G@>OSFb)sA>&3UREcwTg0sS4TZ$rI<-yi0ky z=dUY*Hc+_OVn1c|28AAw9SQKs@}Txb~kYt!)wgR{pcw*FSi;v2IK z^e*>=rb=(IjoY00>3hmg9=1e8GG%9l1+F2!JG4B={p*nc@wsDMpG5B-VAy^_TNVes z{O13oqK|8p|GN4Y-pia7LmRC?mNgA=i}0++x3n$e>F^Z za;#P1FC{Qw{~8$ZhgiZg4eH$A$08Cp{wfyxm25Q04}%B)6j!(7!ToJ{?wNFx`);#Y z0+UE#RcSs+qb2yz&Xut*x*vaRVk7F2usjLz7@9Jm&;qfv$%LQW*7M%e@)hO*cXBZh zpI>u|>N-C0?P_-!GXSG+QlFVD5RY8F_!|$?tOG@}BXMu*ZwJ@-xutKGLnf6?7{r}1 zdYMtk85GWJbd<{M@9VaErz@hg-{<5)E+->%i@VEOom_>OyCoSM#Hf`Ikel268O*`J zo9XHju^7Jmeg=8&zwZxruZ|RJ`(fW=b^R;le<8knzt$TUkhITY>a`qy+Vse%jiPpsq4j`vzC=9s{+w-96yDXs@|7zi#xK;oL+78SQv80C3MJA*I->O) z!ONebP(E${a7?PY#U<5Q2bYlV&jY$#7=1)Off52N4UM6Y`3TG%gQq=3x3kI&RqO2~ zce^hr7LIU13sgg}a4)^{2MSDn{;Y^&COBS{$ei$lEd63vLUa`d!fx zd(}shm^6RpU=)T9?-de-yI2)w9{6QIBMcpR-XAn&WqJiHQv(nYZvBMZ-03_t7l&Wc zFfc{`_I3faILIsRNN&Hd%c~1+)|2 zYi_=dV!;30i8q`^{c`o=K~v54F&o-T$l!k??@`_%m}-0)F#W>w+DIiVoUCx$8~eOJ zszj5&mbl*|KA^m$^TmUy4x>u^=U__$5maYa=sUwftSe^J_+sZ zDg-N=6MkyPMg&d(VcT*U={V*Otzx=$jHN$ooQNEKj|KKk!f5OJvPUT1IU|3K$Ru*@ z$uF_>T#oAiMsAmrt}`M}fyZ9n9300SMfc;rslU!PZgCKiv4ZDV5vZqSRP-!i&@)!WG7 zO-8ky4lN&^1g)oN#zdT~ygxIwd(Q4yBZni>-USfwW4l9ymQZH$=G;hzLbbM&F8Z4{mq!x)Yo^dBn7$zANyq47s?{@K`zu>E_HcX|m zgO@gi4U51w3MhrSxI&avjHwKd@25!bN{}QP#w#?ms2RIsZGy`<}9CRq@s<7Y_(BbjmVCK_VLqW|eO4-&ILbQrmB0Ig$K zYIQttDZ=jl6Y%1%@iC)rckDLVrU22^gK!Sr3ap13tWF{wDP*0khk~3f)$}I*H^q<0*8E8P2d)@ZUW4EC}7* zW$gC575?{b3=)!ez$^uV;K^M#l|Gl(e>73D*6EJ`O#-*3)T^;Q+9%A6dh-<##2i$D zX@dVz8R^7JS8RQ3W&g@G^f3H3iM_VoNIW2(E%Y?6y|&S`yn)L&&l$1t;&BxGIfP*` z{G*f@R1z-(7(`(wl(Xn##okN|n#{Pra8VldzZ+<+7y>GUt5d@h+?e=2u(j${z-EGC zm6U_$EuCc;RvnCYEBVj3?^@qaD}baVQ|~voiNpIL@xQ;i9&mQDgi8|WrNRNhxgE48 zK5`b-KwVsQ!%>r1WODtx-oK^+#Wprec&7?IMQ|TD;dVE{>?_1PO%84DT)oC-#emnZ zrDiAX8&d{pn!7*0-)Jq91FvqfO{_ZHE_m7}aozX_FuFs1$-U7CNC@2rQU9c|2`*y#Rb6?|m{;qyDy5iOE53%E}K zCITo7lS@*O^pui++_&10i0tKfhaq#c^{+=5Qc${Gth*}{8z|4~o`>;|)PnGXo_2y1 z<$*oeod^O_yhKajk@UIx1)&tF^4|7Ej`twxO&^Tqz|Wf<0nZr2*G2MB%~I?vd|NbK z-Sk2=i=nWk0zng?yuHRy2uy1aAgu=QV zDRy~Rg>cQ>?Y-i^!8IorzDA#V>&H@$v?tFKOfgtjbb9XcXj5oDoKRC+Pyfebd=CC! zkytT4OQdl_$9?S~9`) z>2xG?0{K;{XHuVqdr>G%!867X8-9>ZlxSc4f)%*skz@2(Qjn-HNT}yo5lCP`Ts(sM zoC}&dx_Cm_>4{%(BaE^Rh>O5W%vBYjVMAa9AS}+H>2XDk6y(QLMVn$Tx$j;8G0<1u z#tAQco8g$6)uUcf6LQCQcRM#j$IrUa06C-jqU`Dtcop&AQ5W1;VJ@J4t{tQKe*4|>#o;+|)Jm)M2_*=OvdT=&lcCcbwOq7q2&}{Xr&R*l zDQ+A?WYW+N1b^553rkSJxIUAZM-bp5185EwKn&pEGr1jC3LK!1?lG=-GPiuh7Nej*%O38{UQomi_aN6JTB&*}N?9JJe#HKokg&?$vYmrhg)`>v2D= zeKLG0EUGqg%%ACCbO`5KN*uK4JBa?m9I|4!|DtG#-dz(re9kx@LXXmTt_E#t4G296 z_3-}C#iESG?AkJu@fBewqH$%OH1#n_=L@ru9DlGzveC$W`n#p((*x)IsWF55D`dFR zn_Ip9fZeK&i^@oXzsE?y>Dm^M&pNo~HvlJUa>~YrsO__FK~&|pcM@6%#l*qO49}zL z5OGhBlgD)PMMA51T82g)AFa>x#4Ao?;i@-44+r3%m?c8X0JjMPyA}y@4w;6lk_~(j zgubQ(4QE#&tVSik=_OBdMau>P>Ln1eRyIE4J@AfjYwi)k#du+RVQ|pWaUcn9nABpK z0~)j+cJR#b@JrNsN$R<+0&8GGGuXPY)FQ;gU5i8$f(|s^t1(eC^&jmr>JVpRmx|pakK3WF zMLNTYVQx!j65rSF=;G#pCrFYQESLl>!Kuu&>3K;SU5)!EE0`J(9BdP*4>MX;Y*{Aw z$uEg1JTCOXs|{@(boQTp|LK#gF35FBCVe!63)ByAL+vKXZ1lV ztLTQke$b|gJ2pXTpiswqlWJX*g2(C=UDua`LAWd&s0QSJm~0k*?V$=So+{RSDz zfO|w~Vh0^9aBdDSg|9sB*Fp(eTIoyyn33UfBnde_{;T4~{4yH+FBTM4%g>yasM)uD zWOSoM$q#h=iH~><|4|p!NUX3m|4qxRCz1^=_owj~=KGTQYQ+BMmn6IjUNTEbvZTCf zUI;T@o=ltf-WZNgC+=m=Thv^`_g-hz9wBDss9jIgdw3qs84Eulnwr1ehF3{D|60^B z#;MV}pc=xG?Y+J~1mqftWI`xWwRECKT`EECnqu|1e+7?DKZG;Muf=?pPVf6j@gQfx zl;)LS*=|caR@)JVjt|qUQB*?DNAk0hQ0Pd~iF^X?h8V7yD*TBBs2pz$LSavG=Ad*w zYtl$+%nl?n!89TC7KX?;ASLXr?X;UEvWZ%%-VP$g%>&bR5K^cm`We#y9&42Z&fTB? ziYeH@U`nqXoM0Yg3Euwkwb}mFSW3dW+36TbbF56x#R4nWKwWecMX%s(x%mG2m;0sj z+O)-3zNB0Mp+xj77$=Ya2jVES`@4YQHp>G*h5;^pC}j64JM5cgrobzciDvLrm6-_+ zQH!Ql3%>bTv1w$mhn$!D%O-a_H+TN`f18t6@8NfV+zli8JByq*m?l*#6^7u|khgWg zB8CS^|2qg1B|kw`*b+2+dFkBZ07Yd&QrhHk`TLWBMjEDrjh!c#ttYe}M|X;=hESgM?8NQwp$jWFB zRRBW5&CDH`YXOn>$8YK8tsYo`xNXw&OO(c;;Ot$JA8uH5`n`|BQz&3KLbPLC2zz2t01)MG13^>mTL@{=msslDIM6XtCf>6mSuwTRjPU8BUmDe1B! zkZ79pJs-TK4?p|_8t~CwEnhCR@ZCylQgrJ$d1>CK$9+QDDNIZb#_N!pn!+(_yNpWk zwu0FA3B7Kf45A-5da;o!d-o-RMd8Tq#liVTR8rsgh~Tt6%gt;fjm{wxL4gkz zz#ZST+$)^b_&9@;5Ug5y&g7-3T0`QpTO*mmw!)wCH8VX)Few8^nZ zF-CuIZGcyX`0QK57X>A4sxeL*Uw|jI2u*@VrlarQ#zqt)wfBThaN`Dhq6VK;(@b%G zf?xoqba^0!anLw?I?T$dt1d6RhNhyRODxRC9G7DJ=?F=t*K$4?qdStImsUl?ukwua%qWA&d*38(J@aJ-gEb_=ZDVbJvi zRhj+UC(WyGcUe^q!1I#d{!L4+bKN5{HC1hxQ_}EtMI#x zn%3ainFqY~H~vIAm>#EFoyYTUpJ1s8#oxyTNf_Jx&Xw7Z4=usKGQdQqryxJ<5RCJB zimZN7#uxOYvLN>?uZb3gTO<_vJE?%#3v6HN<8$a~`dp(s$~8bjF{0i3%^r`4y+s|W z7I8Llb!rM&LRRN-F(?ajp^UvW^u0d`ru3ke3W?3_;*w>j%=IJCAWX7jLRXq$kk+g} zs-E>KITkrl>qgVqA*{hP>Iz${lY5zv6v`yai+~e>S^{2whU4u|6ENXsfH66|kU^{| z2U3+2&NsB{h>Bvj10yCWo(UL#=}P*2P^B#DG-3iu$ROHvsw#KAd>WS*L+4APMa*?o zLmbusO7JPp8-QcF!w}%qD$~|Su=yq&=cZ^Ds23bv_dO8fKgcR(lJ|_4y~qVh7ctNU$cH^fW-?BBoG%M}^YO~|83Yt&u$sKs0VVC)y1 zn=4VG@xjE|GaYn4+cfylas6YwkH~z=F<>sOaI#H`4@vTfPcYlukWG; zGf!mo44o0g-cIdH_y8yE)XDUCON<$RckHusDCb-8ta}#Y={JL6Gi{5ofUDs9z#f`F>Ri&hwQB3sw)B$@CjSSgSLfaJovaT9rx$W$V7vH@ zgKa^k*-vIiS8SX5@ICAm#Q)x*AId_<)thkbBm9}%O?Zjt1igRfu!;}@dAFAM&|BOc zr(HR^cR5hrua^;wwOgMo~ z3aO59t3W#7YHiPdKXtdU-XZz`qXq`(gnF(IM(^FjJY5?AP1cmPUUj*$$9 z{S9z5xTATxbvEFfk*YU+uGVcoiP4C+hWt8bWcyaP7GaY^LTxH7lWkb!HMOX?C}viS zE0SJ$V^P90!CIk%TmG!Vw=>=LUG)ffn;--a_)~>90xQRVU?TQqWi>-Qj@sp@07aW6 zK3{r98#Xa#c5<~MobPa35%CF*}`Q5}E0rmIGW+Il|Vn!%89q z+PP-*zgbe*#zkH8;d$92N15+jrtw#Ot@q=c6xV<@H>7NlS)8%k3*E2J6{RW!GeltWz0k?F0z=U7A2o=nC6T_NDJ=g`&Da9u?Suz2 zp$e-YLqvBph``5~CrZkU%muv3s9g9;6xhzqmg$Y|t&xJ33!* z4j|+VaNeOXKw9@kQ3NkuF^io_c|Y!Hp{6K&;7WgB zU=M?6q+CC?``rGy`#&~@gu?H>js}y+Jd%o6XUV`l?1)$M!R+kJUbtaiSdH5+{rANS z@BcYa%?R#9^aPoyd60Vu&fC>*>@?-5&K~>5sC=lIJD@_I=$Fw-*9Fxed&3BmxOzFu`h}~fPq=& zFfoSl8T}ikvt;=;%?GFYz4WTf0cXG8FNioVSQDAoh1dgrq6l#sTi~xg6Tp*e0h60a zi>xpl9NdvzFm*}sPXGX2KixMGAav=ieGb_1%}t3#t!+lim?r)RKF$&!uw@(8{;;1qmW8G;Cgsh`0| zfqD|$@He*>U;*XcJ|DZ~(34p6wo8ppyz`bp`y|au|F@ay|omVUUy0IJoD^&> zl3$pn0|hd1cDw|q5#QL+0>`z3R1fPs8_=W2K;v7hKY=4jrsH2S6UGUj<8|tm%+w}D z1MsmTi8-82TP*kd1fK|5ci6>Z%}ZF0tp>Tz{cHw6N2G+sq~6(<)QJk(s?>=^Lrch^Bd6xUdBJfvA4Ti?~}MCN=N>$b7MwUXP_w>xLG>DqG53a z1P*9dTrE!&+U_A~)nTj)*}rQ3Xq8VovFAkEzZajibVpI`AJY-f27W{x{iYc!jjlH~ z6tGX`Q%oz~`%~iFYv7Km+Q#t!8hO*yIb;>FemVSu+25*A(j&h8`wADU4z!iWqH2&bG>#9y!vdO_xCcs zTBiFglCbdW7+P5iM$@Ah7o1yzpDMCt;ZKbac}o3Iuf`e$K3a*hOz~{F(TEO0Pi|fZ zrD8j`=|lRWLpsVY37*3NaUx`GntEDAI`3b%a$uNs0k_m#fEZ(yrz_v|DM##`6qlF+ z4Bo6t+#@Ps;Pd|V%?P%iOA7@T^Bl8Erd*u|Dl zYlRtxb93}@j;O3%4Z!x$0!@U@(1yz)5r1afrmgCYZ07&!5J1w!fP)|dW&|GL8d2>s zYr-B0*H+d^zTA{=R9aeCLl?$s)u5>CEWaKUveA#Dl@%~oU75i5k=l7=WKTrEz3#$} z&088f&7xNceCb(po3Sb*gjCS%oPZ?jE_IoEt7cM<+hUl1g2KNN^lBE2Tq3);G?Vi} z-ZN+-mUOL9ZRb1CzdBCL<1%`+jyiXR3nS|!$?SSC?%O#NN1X{azU{|A85$>)tIGc5 zfnORdJ*9p-zR9fp-@vPHiJ}h-zQ0}%$U5=-A-Vpw1+3)+SDzPBZ~b#a(=nJl9nS9* zW&?_i3hwiWwhZ#pDW|ShbiBXAU%Crqfih&B^W{n#m6h_pwfU;zxja2^3z`57>;2Nx z!;t_APoDJck~uT8E z|9&hZ^E~Q^7nN@;>F zBZX<~k5vcPlfPOZ=xi{}Wr3>oF_!JGPa}-KMz-f6Ss+kz4iY`Cz|39P%4atF6U3qP zr1PRE0y1?4Z-MN&Vj{k-tiOz|UAC940mJ+TB zW3FX}!#>d0UPrwNEd|z)lU6NPK6JIe)V0!L3{-{RvI2#%`e0ZmfAtZ^qd$Pk(?d}C z0~Nl+s{sN5NE!nGi+3)CQ-Bl3fjpZnZSo^YcUH8;1Igr2&CJU(o|cReh#c;~@00^f zNJJs1K#FwE<*+{9_7iJ;cZ~z&eB!;8I@S6C-bc37{W67yF;yUnkh8P!c9lB?@GTU2JCXBZV2qodlllodFA7l< z`;}!2C!(J5qlQh~sPo2ys#`1HOD<}gLd2JQjwAi9^xyPLen<3CP|D8i^qP79T<451 zU(>4N{=!-gz6H0g*mDvt(uP{e5V6Ov9r{2#HL*pRHo->CMPGz7jSDVx*=KeQwN5m1 zO_^!NvxD+sqY8{3S|e1yzfn&!F2M))-sx$kcb|ni>U*6pINcA5Db{)HOIN(h9cYI} zXC#{x2O>!#O+M%8z)2v(kiQrp$ZEpMVPu6$rov0qQBqih^nJ6%s2u2IXy%GU%r$FF zQE*r$ia_0l!4IS$ld$yi$H(mC_IXT<4 zcLjtb5)*eqdD>fj|K^Xo)H}{V7G-2jvSA+3f_V2%*-lpMEuy!j6=$StfSYFYJUms) zjB|i&!g5ads!&Uhw5OJs_io<12JRCe$EXM|8)afAO~0^Qpw0WhH^VhMgEstHZ7L>b z5dS5b%twn~VG#|Stdnkz$N%2x+YwmnV;EWpDlok6=TGZKrdp%8)J$SUTy#E@o_HNB zSrXgdOL@b;8Q*0+J~;nRQTYEsz3|>|+uAq(=S1~lo;mXFGoRTzHj2=l;avQu`Rh%V zG&S|}d5aC70*RRExHo4w_VN=Pi^Il|ct#QO?mR#EyXyQA^iz{irZU_&b4LthOF@Dj zRE}bg$7%AjU;(x_?`>$XD?V2#G=19PdR}T>@Y)fQK;1uzV=%jr`V=}cU)}r7>&`FA zEDi9d`>3~)%I?654iLQTxc+UbvoqmxNKu}xsGvd9mzEOe(WIU6ebgaEURWuSOtXP0 z=IQXpK|Djb`R|;>I@F3)y9+x{9B8C~zt4>UNIZgMj%hOy;%~I*fM!5C9Xh?AZks$= zfcw*}&jvkiGFV6@fanASV!>-NIpoAr7a(Jb>=Mt7W zxNze#!zsVGw5lXAkuTFnj2zu@%^E%lE(L3S8$!2i$b$C}B58PvaLwG!1^jR2AqaTO z9$HRvLH?MakiMP|lh!uBSv;HR>Th3L1Bl!?<3=U?F_>MpR}#FeL@7 z8Q9A?a^NoKoz5wJq+K}ECnh$WgEgDWu`*O6*C*q_Gd|S4MIRrA@qhfVGgE7K??<%+ zXI$k4{|5E9(ky9BEUOO^v9xu=`fERQe1A$mjz6+z&cn7vJ2y?d-K}I=5I9SAN=L+u z0`05c5pbjpv;a|$CiZb4T9?`V2)T+i*N;83!RA|L0Vnwo0^^;o1MDxv5s!qT0*tq) zkE`NSQ@Ssk+jHGn8EsN$91K+J$?WoQ5m6!DW_WhOZvnFg@eHiLtVaG6uoETM1=Z&2%V*H@C+1+Pz>CtCe z-tg`yIoPH}icM?HJ%T@77i|yL`=`ZW&6z9$RiUSzIp}tcKoI9ht#qwiv``vNA-32@ zd2cTr)(iDY9V3ZTK=sXvp91nL2OCep(IpKyL+oZhrWmTuu&zipw-%PMA3GUDX{g~J zyq0SdW}rq}u_Z7$ZWm7A3*s*&xd1@lUh_Iv0-in}apJ{5SYlqw=xB0nA4Qbyw3dX1 zsyNhxajZb8>hI{vJDc}`yb&yx`*ttJRRZO5z$GSPF_Nc;v3@`d+h*Yft#$3M!Zw-8 z;H+HmYhv#KLabNNT{~21BKQ}l*v}CO^KG`}z`oungIzcu@(c41(hXH>wZ%AxFf93^ zs#GPEdmeh*2Bpb8jD^ypeUC1dUy}s`F#R5!#YjGlj<2yWCoZtHK^(7Rx_!DeOC?aV z)oflB@XZt&7lix%^v=y4QN;7DF82trqn!mfl`p7r^QsKv;@;>`h)R!3?~vvMxTG?7E<3ME)@8_TG2+8*G;~_YVK%QUW;(ImWwBx zSNUcMf~{zOEC%S$hkd|wca^|X(9-43e>6s_`m*>!ag9Y@30o+p5TDb-9B>V{G5pdl zuBXdxS40$MIT?O({`8&78_`>?FU*)nXx)dD55>IJ%ddLAHiSD z1zA`R?u7fY3;ap|mxk;xp(xbErX^y^FF(+)W^VQD(FfZvHnV-n(^)Bmuc!{X+lN%_ zYkHMY?>xS2LPAPq5YYQpidnyX_q-ek(6dF(zOc{lm(ia`hVJMT>)yw}>Ff;TAl=9> zRNFmHF4uf1=V7aoI9a*ZEX5zS{u70cxbT)&X)GT5HjHxr0&_>QKyk>BPO8ez{rTl( z3WI8KcVUXg0J6LUX=_dcwcZ&HBQlIj%>uBQl7u(w!qzF0>hsO=262SIQ(1lG_7{^~0o#s2Tm9maw#_-x>No|^B$(68RF zx8$imd{vv-!V-n3=6^VTPV(ae>hVh98<51Xdo`+QQ%U?vW*!8bHV03inUe@jva&jj zaG315!u1<&yV9R3rk89$Ek0zW7Z6~GK3XH#Y%H|HICxi>X%hJNV1A|0laVDZX8qB` zC!;Fyx3&Mqd~2qP{JT}TO7P1-M1(ElI-&bv`GL^tuHHvlVX2TY;#0fvcA$^31xQ}i2d@kQdg&#S=IJYBqf0SU{rc=;CvThQZ}Iy)sY znlF$qvhNE_hg`Oe`Ve&;#sMOkNEwnN(*!iOhwU8D`~E*UqE`<}OJRM=>uCtbbcOFHX- zo9J#LSMm)zn35w`VQf#<6+*6&h{uQtZ(h?XwIaBD+(zyJuD<|$Zpr{p5q7n|y1`LOWIwkd7@n=kp9~2__P``x!oS?pHfmU*&=3Ad z)%!fmt^Td*#5KtOAo_6%$;^|Qv1ljzyq~91X*rx6@|}#mooA(_%&td|BlcmHI3vC> z8BXKl+X7Yt>x0v;wlBY4f0c#aCN!XNdmxyTSt@5*I#i%W(d2r57>YD7oaNKiW#@N2 zkizo<{LGgkO@vpt)$!}^K^-%)9aY}94n@Jp|Dz1z<^NA4O)=gW9M6Y;;=8?L-NQ6e z4oXAy_m6qllP{+z#K?Y@k)|(!5Y97p6u;XG;z_ z^xp5GmxFvb@{+`}Z1UGBf9@Gwn?rYU3q%-@UHA8q!G7V1sfzbWfL#Lb@twomYsio` zWO`_K@5E&VWyZ4A0ESePfv68?1|{8n&&n>W$d%58ExM%p4iv{ z3^Z3I2GsJ~(b~Whh)+nbBm%j<$L2?dP4`Jl7bSkDSHq1GJZOk_gwfVm93VRrsM#jB z?;BR0bGFL3J-vgt7X#cb_hQ^Ym5rRMBFRsm=7y$-=w4g|>eUc#&mn!L-E{^?>RKBA zbZgo5BBFB`;m_2m?;Bo-gk%<`A1E(l7&iTo;i;4jexo5I=iwsi4PNIKvWb0Hdp#-e z9~eq%V|B{Y5Vj!Ec3)jH5;39p6X}$<#k0}U-yuO#zhRy2WD#;a`x3gV^bupb9N&If zjswqePx-jLKkzSX`3iRyi0pHH4D$Eh+|td>OHyWvXKA(FC9%&&tTC^^yTfa3XkC3IQIu;n&$Se=nuO zt9>XCdB{MnqS>;)X0ZTormlk{p5;T)XI?8IMn!T$&KKyT{oe9+rTM3wc*(q&7Fxr-8KU``kf4HeM^<;NT|0_w*7pF-HhUEzs z@zW#ab5fMEaf2WxKhj4p??(#gu z98T=ho_Ike@=CZsVTfK*s6=3gZ(Pb1 zZ8%*m$&;1u0!}?Vagl<63{RS(x()w=B^XTGhqIh~q};{8Fe8}7dM{h&gdp+t(&_D( zAdRfBK57q4jrdj}9;f;g_HB=&~bXH|WxiUX2L3ig(v@_-Z`(ah{UbBTgbp35;o&JjTxEzI|MH9>9a zx2susL&exvi^))nCvNsNv}t2vBPK;XX^9 z+PMS07|w+gv>`Z7JC7toprw!*V?n17BoXDY2}1xdhqC+w`vHD##^{AO*hI(^2?GSg z?xl$DgW7$pAFtW=h;!TgfR?fS zXq5fwQM!V!4lCK6{|)?YU^4` zj^-Hf+s99+9o%bSFJ!?MCapVze zgZlMRz7THG%vF4;XDgQJjLj|FBp;}lynuZ*GW-h0fWtxLb@CK92fRP4^k1%^Rg2Nb z@#7UWl>94^)L(;;j5O*6ARG(o@uka?Yxq-pZqu#A7SWtztX@f2fe4w{ZT`g zT=4hF!$$-2I)s(coJBn?V*#Jgh%xDbo$!SEQ6P#zZ=k9PNXx17xkJUOAgkCx^Px!) zb;6Zxtj^9(O;f#a*m7Cb)yR^Lj-`jl3I`Ibm>9n1oFxy^p5J6d9oI6IX4CLt@sL2BXTD#pnM$LV zIzMHI2rN9YI8Umq3*u9?`4{_-)n^<}r$n zbCGe+L^v~m)K?0*%KpbsdYn$H(+>2dj;?6FnWY!?_BNrTgDkTEqEh(RwcKiHwkO=` zT7$OM1-Ci#*Yyi)!)48ao;O4}-HMGxZoWGD;j8YFN2nVT0 z$4}Q1LLpy$ug5qk(JuYW%@35p(OE({>(El5N;{jEmdY$h-F|hBV-Co0EBF`mW@?#T z5*42VW6z=p*U^34+*&|^mdvC!EQufc_P`~IBavHff=0#Iu_A8LHLQO0)+BC<|L~wNas-Iei-|MY1>-yD?rK2q@m&?qOirfP%5xIR;qHprglR{0d7u#nQbFd zmXwTkoI=*3ZZI{J$IVW0Pn$jxFVwo%?tvw3cc+S=&dY!d>5pL4X@ZtEMQunVV$VZ% zd%8Jut8%jTCcl-Is2Zwz=n%Ls!XDba?4aHG^GW?-rXO_i>CPtOM12s0Rc1*uM7Z!5 z3>)4UjR}jY*C~K*0yBbMnFV?S+No+Wb%XP?1!JuLAFkdiD$6ZU+orogIz_q>knZk~ z?i7%23F&S@q`SN8r8}g%K{^GbMBYRlNT2WF1b4hRnn3xX?6&ZpgY}JB9EfDW#m!h}cixa0e`eD(oj$KR z0bdyKJ%HAy9_-QR4*S|?X#T*+7h>*MXx^YcTM%B_?srC1SgaS^yNA;Bi}1h1nEM27 zQ~{!7@i)MU`*3ZOiM|zP4~IN}h?O%fdbcL>pbf;qh$)UZrKkidWn*4$zpTdztznS% zV!^;v!lDgq?gE9Mmw@~T12pVY5pSS|RN%b_|4)q6OsXOW;m#bjH8*S!b|=WW>c7dS zUiEZ){Izri{f;K4c=IolV`YWkjkwi|vek}5sd;IFUJo45L{wbs4>&juZ^E5Lo{`Q0 zc>&9>ttb-o{6|O973)ciy6!!eW(cfU{Yk>Gzf|Z+yYGwZ70>Eaw#EwJ$pFa-l5CW` zk<82)Yh9rpcT0goW+v*YE> zJ7vJ#fqnw_T@58?(DHhqnG0Y~HoKk1s(ZW%-34)wV6QTiOkUewemogp<_L-d?PBUq znOe$na0ibFjs2xp`N}(!oZ2p5+6OB=tP+t3xEzYR10nF6l$wy>}jG( zS>RbsP0GC>Qzh}&eAIz*oQ)eOa4ISr1v2#wn|b0E)<#W6imj-RIR)s5Isvgk%_A|X z1@mG8x=ysi38j~caR zST4SQ{>>3`=Cf7RN+g!|u39^K@(L^a7aJ99lHlz0cj0}g8Ln4~NXa6V^@$T_N&Rn! zIm6Pu0B zO_@hXASo1bFL`_=+&hggzfw@Pu^#8#9(zBw=d`yZ$;hOzvS}B#7*+(TM3uqoI@ULA zr274_;1nX%MQz4}(Yw11F8gGh<3ceT(5^DyqHstUgHpb=u@Z=`44_V`*8z7zrQu08 z!S&?OH03Hv+_mV#d6TBm6Uay6zP%qYZ3`HA6DK}@WFt}?HWu3J6+7*n``8457*38Q z#5#z_M;~(nm){@sQLW_0W#ukK-~%`UxVGW;n>wC$`K$WTWXXlbT}YzQ1^GPpeFQOz z;Ze$u$^Ya{QeD0|!kCxQ3~YWG)UQ#CpRwN4jeoRy+ZsA%qr4Q)Sm{Af3gwlfpJJ)O z&cLhGgzCr+J6R(2oFstbELwmhMYR1v@DlUx0ad&0J44Bbb>|}@N>WxhdU%`y)0>WF zUJMu7^qz_cj0#c>goIIQLDXr(_FL~-*HnF;77zmh5%qvj3}d+J$0^^hI|rXVJah9X z7|}e?r2fTChX^d=pAhf#g6iuZX?`rRYMrK>#Ka77Qn*k5qPen&UslIO(U^KoN9%s1MN_ra716yQxOEb zCHzCG3_G%YnIa5hFZ-S9?xGJuo<0!up=qGE4V>}a+9=w1{sZ+9dbo&Kk`b8c!GE?H zwzz@0GBw`HUsuVjeB)tD3>V3?0}{SPl9Z4LVu>CD+oo!OWYk z<-E;77~3Lx7#o55K+q<2Oqi;+y-e4l9A3l^D_o~bKz`oaVej1*ryut%5w^YpttHs{@5AsvWk-Eo}hWDrG z?xQ?kHhk%Bixygk6*sI>FrB`#`n=)~;YEU9&Zw62QF<`0pzQLDqthi_E zEwQ8ejhygnaVh1BXqGtM+`5D<#Pav^-j_SiMsJ8C-F35sz0ku_h@?@6f}fN*tdaMiuhzk9sRmCe3D?htTsufls@fAT(q zl4?6pItf{!Oz7YbbI!w09RKTb8=Ft{<0&{?oci0{T*M z&F36COH-v}iP||;ReaPfYDPKZkcJrfd5_34*(s{Dt500ms) z(#g|F<5vPMq~V^&RX&oCvj_vt&%H>OuwnPcT-JonSjP6X%Q_}6F9a@aZ&69Ki@o|! z#QdwV*u1&l4yU`l970i0;=4KQV26lL9mx~yI=4L9g$mnaO(K}b=FyB?T<3*9j>3wQ z#76I-Co}9GyrjxwP*5&UJ641hVmXF%Dyd35%O>JCtLfyJIVTrL{j^p-=a} zD>3gvmu$sL}dM!{;TvUU?w1hxKt{$J*!o0C>?%vq##hv+?36I&YdHX z24Q~vuO`}AE^*Mxg215KA~`Rtt;T&qm?8}}IerPcpttg^-5m{F4K&lE%>S^CQ3wb1 zC%2lag%ce!EfiwFe3&#JqhymvtcVYZVOp>SMoDx(`f8yLA6<@161Qhj<)}s7a@J>* z1~opkT>9(}X;r1DdGq2_xDT-!or{>8H&FP)=$E*s0%01xJ!Lgd1vl#~X-8@av47m< zh+y?PN<>Z(LW*wz$W`1H8XnL(Ok2Urt`trNC_lip8bVN3#o5C=PmGoU=9H6QCAFw<~Tw@U9v990Ak=hfVLKWg`nvZ^2ZBe_AHh!kI*(K?1E%; zKzQ{wes^EoGC^nF!r+?YSZ3~CN2!vGPaLM5C-ZRLUo7^ctP;*-g0`3(RnfjKy}?cKJq&+2Sei&-$8 z!m(5)^hChNt1nMjkx=Ffzw|&F$?6APQj&H@Vq8dgX%mPrS;vMl_amtIT87NMz+fqQb9gp>ix^-Cg zCCCstR9pWgbT@ri^|S-r#*z9qqXBv^Wsj>aI-KyN+opwu$a@n^+AH*!wb%I?;Kg3A zGCKH!6s*@{rz0@UGTI+hmJwCvND4au zXMJuyMadRM`8QYM6D5z&Ux+>R;U#GwmMROZ%GC={oyE9r*8QF|JZ`06fIuna6P=KU zEZ-hnrEHA%qu>$*Wm_<^`xFoT?_V2T?$5&iwLQKk=e} zeURgF$jrO$^r6J0I8~3b(#)^E;Y}zuQ8*g7#iO(gu!3{CHE_mSJW%V03i@8;?#`aq zL*T}SEnhsO@7==5M?2TY8$x{fTq@3T;&J@~s-O|OX3eOyv)RI$A|Ig!#h!}`J5vD^ zYK+a)wC?fUmlX0WubG9h2MFh!DniMPok5!EA;usw%}b<+fKaKdN4ee)Y>7?c(c{yI zIe>yt*7JlK)=#!pQ$yihV|zo62vg`?Rsnh8(yaKy#ZkJPW&W>~enWJ~}o3xQGie zi|wizq>50cPU$2wi|&Udxq%NF4)aQB0YU=$tnR&h3jMS2hN`JZ+qH-sV<5T)9dH@> zXiir!3X?Bkt30E^&YIDyPwJprtvL3c7&^)TE{d|Ad|tx#W%eHd65Y7PKo?YpyiIH$ zly>hFA_g$xnO2$JXY+2-S39==c1LNY`bdC6uY$muen6UD6+$!g#Ns?5Q+YLBgHwiK zHsO~YPlwlSV`#~?hyVjPk9-cj?m*ub@@k3J+Dk zy5sAF76c`6Fr}aI-^~IB5=uX%?M;B6P4pNjvICo_r1&#@=F1TXA()_b_MU*M=!3pD z{6`4vQAetQ6VpFEi%yGlPJI%o%oVb@e`bcu5$xD(j8;hU7GfexT~{JFBEOJz^3+`( z%|Q)UR&0OfWfw>swb&PgxdQ+NH!C z&f+h>a_IlV0)ReLqGbc~))`YFK9W&ofaPuh!NtqbR@9a^Oe=WNe~>?x3LZr}EITG) zfgP?WvU54$CnR@X#2>oi4;L_Lf?NfPyWoHRCBj8!O<|zlZahh=#)*G8=51w3k~llQ zsY6G5A$%ndNDju?>bn@nw$A8|E9J>z+aWBO<5133VRzu%R;08UG@-XlSv>fiVkw;m zEkqp$AfG=@1*jDa2D{4Og&Ky;+_5r1&t>;bD-rg*-x*EiB1n9OW{61d@cQy)P%8CO zp%lKe1T?P5_`Ea#GMGV_^uvawA`ryF(?R(Fa%B<3Nuwc-1?Sb~Y*zRe@T-pi5R5r1 zdAeODZ;JkA=dSfF|A4z{Oo+oXq*iq*L^I3~hW4mRv*^Pc2d zPE-S8?PuS9W)!^hFAKj0Y8PPFXC7$LMiL^;?TF<(0V9iHYA>c?+}+4Ewi&3rz} z9cXkIX^D8fzWkjxnO;)+&?Us`iC=#3wB#HWzYFW1;^;J*COaVwGsU=6|AxR!MK+0l zM!2YCqCwaL%SStPil>A>Xm8O`ptXx08vV<>OXq{^?`*nRPLb^AFvGll+%x z!}(~@h~{L6_i&U1v9w7RHY=IRMoxg`)7aYV6#uOQJ4RZ=T-4qXAjV{cHGE7w34Tl$ zXk-0FKimoEBedKKcgqp&xZPVT$RN3P`EvPd@#k;qnMmT4RUZOuH`?$^)+dx$ZWI%0 z9NE@Pg z{uln$r(E%ZU58Xs52=|J$+~vD0e$n5o_Hc zXA9zw&)Noi;+^w9MJnc)m>L*q5^sXxyb7X4uAhFBvXjbAZSd$;Ze!Uet1cm-uPpG> zD(|R-(cx8URy=w4`Zz^P_J{}tlv>Jgp@8`g*plcnFk4m_8uU65`)$Rn06|p17Ft~$ z6DKgK7nZa%TEKe`l?!8NYo&79fr>A_18MRWgl;n5*3=Xsre^79+mZO8xL@Ic>ed^L zKe@wqGbKzkVv~X}uax{JE9KSw?9;?n3E~;Kuj>RhS;Ps%T&Rs2bE54JOd>P>PE`Zo z7P|yqf$n5}rpI?ANYTV?BfE`|W+&}6_!Pk%kCh7j6w$Gd+z9}mEx_lF9#+Q{)EdmP zttjSiONjBjCe+(Cms6xhr~F;7=Sz$gY6R1)&=7Q7Bmc{0p7sknw`;+lkw3GnwOb}2 z@&kfEu{3Vc7-;-;@72`o3u6Z%0L;Le1 zeWRCmeU{6F932#TS{F7qFXx0k0?&40N7F62f4X6Ip9o(u7)+krY zd0m^I7v#fgXk9apJZJKkeIMy0c*8`-860Sga9Ow{=>jmQMCAd3^563nG2T9Mx_Vw( z_$xR%Sf=y#sSM27LU;*NLZNXuy8y3dA9Ok#5+h*jVH-mufpG}l00y@9crkfNe@{i+ z{+j=nOX{zBX~>bWVei-A%+(z$!xkM?1~7b(j)07ZQ(7}D3rmKj0bIRY*d~@Ha3PnG zn%!rCg7=H=7A+T_o0^x1q_?6-@Qurl?vL+;Z#4q23Je`g z9m;WrQ+g>SfX-#tqmP0qW{Z;aSQ-_9;eoaTdxSzTMmNAgFM4AtFq7&{0J61_!-xeu z?&YJ?I90A$>-r2RIrEB;N8ztQcE(A%MOQ&k<9 z(`Ah1CGG#-^}su82J}q{J>=V@SH+w09*O@R`8gE-cf)L=ZT}bhb(0!gTfO1^I%b%> z58XrqQNmym_%AXr?W5}Aq168I4{XyPcQAe1JjC^vEkyhs>vnNw=OBF4Dq*`Df8P?G zE1p1-^{;b7w>`$K^_UyRo22u6Xyd&6YzSV@r&a$LqbaSj86hfwnWqUg@|Wx7(+`dJ zA8y=1Fd}Mgq=X(#R*J<90$pL1fh%h1iYV#CT_ZLnKdo|3O)jTw3_EI<5)H$GCM^oH z1YZZI@0D`e{y_zO9^FfNS=!!UWn%-30ANY}uE{h`D>i%yBuao!MTBZ-vtSs84u+0F znM~{)-A^*DKb0vw#DZV%T|Arhi&a$3B5uQaAGin3aJ|?^@R^L^bAveTYn|c(c1Jl3 zW&~iGLI-$qSo(@%lf1cR)ey^&&p5eVWjy-F{CDcA_`hg&;F_OVgoNRg4f({Nd4Ts38nb>s8!nt??0r>zsO3n6k zQT6-z)uXur#C>W~>P!5eDeU*&bH21{#>Qt(v8bflV_@JrzQ7%TpO!yR$w*~dKvBx=f)iK5)LhGAmp6J5Qr$smF zMad+99TPb^ZcV<@7KaUbsxz2~1H2vRBKK?Hv$ccVDuh9FE~Gw57fH;Ork_1fh6Nqk zqYhHa1n0#zsaMkc*O5dtvg(+1_Kst$CLh;aPv#_#?mXP>cd`dB1}17{`EuSS?BGV@ zX#kpmySy1?_=kD-8~C5l6>z9}ed~>J1bIe6oI&rmb6!$}3+dk-wzQOI?E5k_{EEI^ z7P_`qirSg_rimtfVqmN<)tOhVmYUOcW4|{f>SbsDDV$#>`;{0vD25(|hMHI!%{{i~ zeLbN0WMy}fQG7YM!+hcP9TOD$n`w*!JP#CL)2QQU?yNx|YnwVPGZu`8>cBnlDhL=n z7$8z*QLm`sveHLTK7j%*n;g9Ug1f&W&-Z>RH817!>o{|h4vnAbxsFGFnRSK)71+4%0H&$f79luWB;S39BYUDuigHlH)_xAy6c>QC%>bPUXgZZzS&z7VdAU zbY^izeQBFCQr(g@bGn>S^BL0@O%Sdn`5#ab0yJy%CYE^I5I!OcLTx)ce=aCR%yawV z25A|_gpELWnb!~p83_D5^0c4%SQrmEoJVl;D7-Ouq+!rlY_+ZgCpp;;5Qg+7Y^5LPb- z+XGIwHvx@Uun{d&r|TYLB+51sRTa<*Y}vxuV8RehgK)%3x>Ah1pn?}T{(%4Pg7r zM&V|~{VY`6EDo$qzdToU_Sp6Kf>vg-XwgbhB*?-7q+PmZ!$O_YpW#OMih8*Tk2f_E zPJ{{2tywH-Bh=;}w0?%ChVB_O83dr;1Rp^EwI+n%V!~*AhL8+40YCiL#FAG61f*CIcTIV_XxF$pq>6a56#OV_goe*=d7ZHtO_VXvJf=Oxg>htR?8 zv07?@CXg$>=f^0r-HhR9H$zss z-C_F~gnTFzEdnp@#+Kh_cJ`(AOSRmWM$#V_#7%-=64)Lz>|uXocgmT%Wf*o*knl#r zBfI_tdhm{<%He3$PduCkum55JSN9-}#tAEbDapTt|9jNq_dX1(m=)!s9)j|(gw!|s zP#YkJ>1@aCLPgCykB}pYd!R72u}BpRn+q+@w&+iq@s`s=tX*|!WNah)vOsIo{xY|p zBxUDJJ%uxHvCEKqBI#gz%Y>b>Rhc*Mj>>KZ)OwB*U?FRc?%ogK82>FiX!WK)Nxi?! zNd;W=Cl(A5Av;%oznQFaMvO>>o_7RSSVBh)YKoquUmgBVW;jKE02tYRoR{Ay z-?kPnUHuiDX0jUtId>^4L4aOV??GA2x)$k*tOZ&zmyZsQ!i^$ZO%axw*xWfjSEMo!3F z5);>d0(wgu(r9@LZ88H@S2vjp6i*tLvm`l;W?U}u)D*ww@}b~nzzJZ0olG!pcBH0P z9gQoj;}?lDP3Nt>FdpPaq|KS08Hf)|{G-llRaBILN*`CB;Kno3TwK9$GaUoe3RF>` z(lQtQuxM#Qw5MX&RQ6t)0^9_`=-}aJC~3)l{b$LDOa9`+{Q7QfjjosfF;}cw^eid9;D5UF zpOyc2($-%olJ&>Lt+O!ovuQ+JuG?`o@6s_cT+cMrD2or(IF8`Mo^gS|8l`ANS84dy zn?XOgmzgH$cL9hdf_|N}<{|mayrALw2d!hhj%Qb?tRH2 zb{tf}rD)DY%Cbuw)Kga1}F6>MC1dSQK2W2x(5eJW4WT}y@za7O(JtKAde zbci%91o{{2;(u=q`fzO=N%-{{tnxc$pk90L#9nOIO9v7s`mTu18S7^m=cS>^cTg5N z^v`8moc=WFyk2oPB}I>#1)3%e>gnkt^m@by^-EQub9r2kzdHHZyGtlZ=^7_JTk}E@ z@4TmnQ*FuCU(mBXf@AykMP>A_x)qUPoVa?!2e36asWD00*pP=1 z8fZS$W%^Fbooud$=lUcz)^Enai5bfnR+bRK1F+=}KcA1XDB&<`g(I5heJBSIegtxj zArr1R!B+u6Bf}px^kEQb0>@6}oqph<$7#`l#CA@no^5wIAMpbTke=QzZU3?RrnRl??Lxz z1r;^5oGzofYIplz9&gD`u0wUvXpJnm9w{RVUJ4AI z%YN5yQ2yz8BjaX*L}8YGT7EVV1h(x2Gg3Q($dM$^^54VyU$v`ygqjt~`%aa4s3)2J zP>Nh6(;fFnfGW7f?Y=WU&gxb}eZAz;!}N?DC7ylA)$6h3{ipxx(^5<4K|vm=mvMc${?IPsZb)NXe>;^c_uzlY_Oc-lF)3 zJGT%AT@8A#Uvn!W&L*O><8G?65ImqBK9YE}^Pz;i3vmS4v@yzY=oP){e*@!^i2FOe zxDLBQ7M!E>POmFd=J3R!pJ2jQrx}I8n=OPt91`Cdfg_8gRYLH&eO~+kAkhzc`dMs4 zexRr+uU)R}3pf%@XsN?x=VW%ZH#9~52C*_~AnTX{NP9cG(sVw#*bpR$OhWqX9U%N? zAOq*4I;c+4ux1WKCN-CEy!4~zOYHVHobV%*oYUo*|!qDj8NnWX4F<~*8t*ubFYnxdSM z)z9qnP-b~j@`4UV?7 zsKSILEcjkqDy9i{I7&vsAVp_f<&LW$%~SZlqr%X$E!4dk4qSY1^SVReP<3|{-*ZMY z@y1bTp0&h-;9wg6)!M>qOTE2(7~9jITe_z4;FRuvh)s3>XO1tWSd2&<(bxP;f3=&Q75}Jf>sb8&l9GXqw;1%ZedwAg`@d_SI%5UGu`E zm8#)Td9U5%@lF2`aHHY$E_z%M`JUD(B+^6DRAl8i=7y72d<6~dE|**X(%C5%OYE)O zRW9>`YcPB&@9oJRFT{SXxmlP$%9zUjImv;xjZf01O3#=VX~arPJ&=f`v98`3_%ZngR^7J8PR9(d7Jf-c2^hohY13U zX)>d+NsW<4%i7@j)Sc1BlrB# za;Ki>I74pQcvut(=cq0~2W4V}tk2vAmKAS5cY%zr*V$e1&%LoK#Xkl>jV$^(D=m!P zze-hRjJ^_)VMTBO%;6}QJLv*!cznRyyag1gX>NX*J1}^=tV+Q;;CQ8zDr49`^>TOe z?0FSy=FRcmbM1|-L{C(VS^RfbR^p+8rIG;*-Q^-cm;3(jw;GjM z&@a_cwnYg|-tw5PFEAj_v^?ablSwSOhVi4^onuBu zj&y$RL^}oC`P#DiD(*U-p7KBTftfK(@X&7L1ZlIu8=RMTSQBREh_n2|a~6x?vg?XD zynY)?$z4$;Hq8cpLF;V&h{yxATMx^EW(e5{;oIW+@uXIvb2BrcKiJx85-c|A`E;{^ z=K5#Kf>`%+bI6#4Xo5!=!)1$Gi}6}l{FcIUwHHo4l)|!t+aVD(WXLJ1$3QVCzvgS} zt&&$mJ{m3#5frO>kD#5oscL&b8^u{#XfIC1H0S*@eepJvl1nbsA_f3IHaE)|V{a4Q z9@M->$RoWAXN!q)8P~8zXu2fmA8oAEQ^MRh>3~x>3SXCv*-L7O=$As8=x;vbp$UEw z0%EY7S3xRAhP@6^UTtw9GlZ#y-`>@mTLF&E-9B+RYC;) z0k&Qq!Z%pxLBk1>d9NAK$G6gAb@OaVT-4{sY5GV-(dQ%@)*tIZVZ8kOGHi9G=DnLb z!>A|Jop7mKH1%BWyB!@4$zUSEz*2`8BO%3hsV2!)H_@SH6gZ57<@?8C}3o_sc5@H%!8>lOLkUk^-$2ho0LVaD^Od120U<_MLT`6&_T=W8^Q=Wn$$ z01$dx_`l5^VrM$mjt5X{71T z#`oH|tp=msBeM-h}x# z@?w%G{`?eN_<1hL%+1)KVF$V86q+pHV)7si!RsGr=WVUgPs122CBSr^@1t0rJYvW4 zQ#8XA&xCR-h_;?rMTPxJ|M+UeQyP>8=<@MIchl_JZqX(Ou?8?rry5{17J57IG+mQ4 zpi6ig@qV$uMG&JD_Qx!J6Es5|EiZoV2(|uqoQS2ZRR|3)i@Y2ZiElGT7Q;IW`)FMj zmif2ig!>25-WQAMXRLcb>@f2iHt5Bt^iz0^bTPtrnt3Z4DkFZ5xZ`?pf+@JegT;Lb z0ogagO|qz9NI=#D2i82v@lO_&^q=^RI~dIeDRd^}cA#Yqk*ET{jE+&_QYW9&%QSfo z)aBwAAYe}mc##wYW6tO=)%{Q<_S^~7@FhXhm7VOfWfyLB`JI1h%wS$n&Mcz1EF{(h zyF@!Jw9btJ#|TpLfjWFG?S@sE53?c$ov-AN1Gxd4viEra5^oDkQILk=EloiMmocd| z;CrlLWFvdVseS@6Xi8xyC#jftOE%1w z#00_-+6VWm(})Hz(n0X`wOiunv1fPWLqr&ZWtddkHfVa|9Fc&KB(ID*3f9I+k}oof z8~NA&63rSLsgOs%3!DuQ`vi4ltLg6`Wpy}~PPvf&)ARGGC*qpuKOJZe2G`5JngSZ3 z(pZwl8w%IQe0DHo@fQ%t0Jt73HX{|GS`hqR6Uqa1rMNsqJa#v;Yui%Sfj1F5Xc?#Q z!M)2ozA60n7?_!WCGbnIR&R{iUi*UurG)(kJd8{ofXf|54zOq;-q9%Jvf3_I$p0m< zCW*L-!9JTuRv_Ib1fWykOOkT)IA!)l0J3H`=hwvbCN+aOpQxP^_5_um>F2!KnAo{L1q8)T(ja%Rct>EnIal*$4HV?=cTV zXbS}WF_d$~&bw_a6vBG?*PMT3+Tia!N^$EuED2tAYFIzE5# za5gx!Z7duX^UCa5udd#O3OD-OKjD(yNAef%B6gGb7^b98JF%{@S^VHk_=;0&m;Xu9 z2eZH^X4duBJ{S$D=%3@co(F&ZsQ8G2$E>265-CXD<9(23b!*Bl^WTE~X4X~+Xf$jk z@9<~ETYd0&Bzv3?muwt-D&+5|k9TYUD@B(1QJ08b(ikGJhZr?#lHl9D@GHU5;hHCT zW1U_CTjv+{U>lY^P5)Dp72Ms!6EirkiOXwMTye?l6A4H$_B_U+AvUmZc$P&|UbdLVDD_ zowJzNfLWWC@Tjz0yu>*j)lV{OaE=ST0bXrQ;P-WUh>2wQ*UV5bXu%}D!~&KqTqago zubI)(BwJuvYk`96qp?MaO_VcsK)c#c5GElmAM)yP~(C_D}(UcrC#a8p;s4im{p}JZc3}fT7joUo2HDCro z*?I1S+F8ezItP?IDqZicJyl+Z{OA161_45II3l`b)0#v$e6}aV3Nmh2XC4h=6d`@m zU?fOte{3422>f{1t5)oSixSS%n(!`&6K}&7V#UphFYX!(=@{m1mx((ws_t%nqL+y9 zgM+f-eoG>!e*F8O^jyX6?>BR(o7M!gLVplnTTZv0O6EV=9PIHTX-SzmzE}dZUg^g#ddTo$ zkRke%B=moud|5Ww6~h7%*F}XkV`nW~GB;wQ^G(;k;a_2qJHwl?L#=V^F1AeW3O4C7 zz+8{(>Qb!oxDf~UJptsC>3}Uq^k3d4{`G8Gfyyw5h2Yy+*NDYk5FoUd%x=3Ob1dv9 zIhCK<7~rtJ-wv(8TC)BMHUk!JVYCyt*8L)6)aRB^%f1*O4P>iKwm;Q@d2Q>m=jr#r z^9k#p?4RmqU)LjJ%@+3=Sn?=BTRSq|NKZL-e3w2U4{%LiPzHmn_=Y*OhPP-~x#T5D zT$jF%P5N6#M!5%*9$Ye6t%QSG zLu~42(-r=O2awTg3Bfk?vEFXqztzpBmx<;j49pI?uW~)_UH=M*!N_>P{RV+_{^Hi? zH8A?8{u4J7Kt`WPKh2H8A@;P~;!XZfn~C8jSiUSRrjSC!gx$nj@*wf-I@zo(2=lZj zkl0)Z3q#Z{Wie&pa*Ok6gg1JuLQt8b`qWlVqfz|Y?Xk}c#+}T{I_qGBagWuu$B9$W zqUnnHtN7Y&A~pBJEMTsHf6Z?irW)$bQ={5f_~`}8ex2Z|F=Z=~hx~-Ay}q&f7P{}7 z6(-^kQ6+0m_MiP&kGGCs5{6>A3LA!9+2UBg5%v5H5%?xVYjQ3u7SqknShtIz(|iVn zp*ae@sLqfW?`Oy7C%F^-7&R|GXjbrMezSQBI!6iM+81r=Rh(>Cc@05*g6V3*1O5-)> z)~YGQnus@aYa56DK-oRsKTh^bGKn7^v>%Hfmz7HuZ?=2~=N?~WJo|Biv<(qS%UkSgrrb->d$+RW3 z-J^gC7e0ZXA@HsuEx?ttLaKQ&B{UX_UGE-DD>o7=?OU{2#y(!Fb6`KtF-_`A3Dc?~vakRieprAi9?K#>ukrHTC+H zn0wh!xM?kM1~*E>#r}oWz{Xz{mmY3^Oc9@3dXC49huam_$-0hDtyN(DAl41kjTtz; zq@{Pxu%4U={XK39^>NY}W3D>H0gQ=R*B0!YIq-AdnK~~3&+)d;BBjZT+qemQ*S~%{ zXj{6zB@a&nRqlVKFiYU9c#C!U0ZPPv$AoZZYZWow9!yR0(8q|V{Vqw~5GK#j-U_XlR-Ip>Eq za8qS~IBSk0#VM6FpB_bnT)#E!);6A_Bpiwx=~pWda^B2e8UWxlNq$gwr(7wL)9Z!H z|5D+biGBH`Cs;2LRZID z1|C;!i&A?nKnosdFn05PPnPOTG~oMStia}-WEITvuoq?*>)2DLwr6E2G|zX2(X2|C zMe*8~Q@QRld~jW0_I41oWe;)os#AhX)V4t{Gg6XR3g20h+u-wkm>2%kOb%*9yx^de zt*CURmAiLh#4Z?|>42d);(vs}h>XSwc-TCUmQx>)W+>9qMOa0M5Vp&ce#BT?f5|F8 ztdOZCQe+B4jtI=aEvBMdaSd0&z13 zP-*c$UXjrwK@ZBQ|Cv)hp5Ku3GM-VC+pP6&=j?m6X;2RODE)epbd3d)&hz|7k=LWb zK~UvR$aXsYB-J`i-H!M_TH%|r|8IKZxQ6Vw3oom!vp{=#b2DCG8KJZ^M^JKG@loM6 zNhT4kNznEN1?E)vVo~@T6+1U}FuA`u;n6Pk6Bqw|LeJzFKfx_SdBVBZg7$e3+4W3I zR+dsD72<4`Uq~U8RI~(-J$%ch+w;jTa@HL8lK<%s@&q2lZD4 z>}ih?4C3d7f9{Nu3krMgg-D?R{(>kvW7uq9jZ3vVcwB)Tv$>M6_#>K@g_E=S?cQArP7UtjMBE9GM zGHB)rXkT?>U_K7)>RLr_;3lf^IjF&sH!-YLFM#B;J!?*-MB_4jL0G)&>1QUq2SF#!nf&I50GXR5&_}EUHD|o)B{CTUPv@zqtkU@|fAdlWm^vD4F#jO?zBtYcsumuqUav^pF7K{`$2fSgYwP5XFm*DN7@1MZX^N zu&Gb$JX_hj8U3b{CiS0)YUa{qes+9;%f`0lmmylP(XqOZ&h7nuy!ZXDU7RPH^PYeY zt5VQ{RLjDmqH0jwX2e;TG_wkPXnDr`$&FkgfR9d z%uk3I93RqH<{5G(-y!opV?#cKcdgkdYO(#Le|@ne-{DJhdbX((qzs_{&E?y>{ekK z#g!sqlXox(ifWNQs9J!{ z_g9ZK`U&Z7&XHdA04E=|Zd{1O#I39K5AvC>3||?~?T?U2zrH)j5ycN*ySO%mJbw8v ztJt0Lf1ZmmkKcS8*^TL#i0KWAy6l;`M;@`zZjSs-hEN`_;m_e%qd-NsPlm-CP+>U_ z&J-%Gu%t!*(gTY8>@Imd4Fj!x@V-EzShTCoK(wJOti&s(vbHz19`DZ=;c zwmKVn9DcbBZtg<7slq z@y!_vyR^FyU4xaNn8lA+t9hg>)Zt?uMpY)TncsA5qv z{N0g9U~z1q+RSV)MPu8f@iIVZ;nRru1<*`XHJ(O8*#zu69XTFL8AkLs=s5+H3ee{q zeirf~o>$Y;-&|SwgP zpFFFZ;`E&_@D|Y}fmWf~!D4Ljzz$EDjdt0GhSr|W0O%MWN4)n>QaNBfgXYwc`wrmN z4z^eR+hy1n0;BVjs^-45(T0Dfd3^Cr#WqZu&1?f9v=y=CCc)i}7}{F;Gaj8(MS1kB5HP|iUj7^D`2%?k8U~by)jr-wstD1r z1s5^lLY)e5yMsF1Tk}UYDOf#^`Z*584qNMNg%^R$&dSLz*qisM+xwj4m0I{1rMbt| z{u5&7zE*J}9{u<9E{ykF1Jnj3<@u`}9jQh!w`5H34g=>VO%&1dM?3Q5IieBhj~&m^ z2M~pJJOpveI%h6GO#7- zzIGwHVlyE493{ot=dphw&=K0P=to;#G1iZ=7`kPu2iF_8`6}@{rE6`8L)^3)~u>!^_#NJH#V}s%xJ7M zu5drcRQXIo{l>W?kzkW1LxD$Qotk0|%en-BZ@xj6<7NmO5tr60q)MlpGJ*dnIY|{a zL+Guq-yV>xAlyX zkgx3Q-=*&Lr)~+t9uU7WFXMgIo2a!L5_l_2i_N+o7X4YLOX8UJAloCp7x%LiE}QyK zz|3G%=fC`Cmd*IvqB2%-9?(((7bia&6XwWZEsQh8=S#I57dd%s-YoLp90NFE)HDf; z=?e!Bsk2Ld0lFL()sx~vwhBhXmKQ;8bq#t&|M=}8Zi>2Gf8;IMlF~PVqHZNb^tl3L zMZ`gQ?|>wP!RK5Q*pm9-XPiF;C%c#M&=4n4h=%qO3qgM* zKI(O#x)+wi>7Cp{w1Bu<#@LFpSgK#qpE9Y}?eO2!*LtloV&0(hG)D4{r9gpZhhjCi z>^W;^;{^f!WWfZE-Y(js84S*SExYoyV1WCg1-Mr-}mt*7(jH-H6E{yP(DAcKE@F zhfSuQ!C#)UxtTM(>MuB9xZpG(Fh!2sZ$|(rrJ)L~YXCc}(p00%Je?>ah_v2~7a_Pa zB)SUiPRRj{2+Hp(Y#IH#If8g&tc+h{GgRG8e+zLgVC8_t^kry)3tr|YZ2R;(Lp3m) z2NrzXDStw5QZR-kZE{-B8JpP-8vt=-tudlqwxC`5!&1I4o;5x)7w?DSFSVG@Y`HOa z+J5j#2vu_2;Q!!c#@ePq@Q7C`PvO!c-HM6mw;(}T^b(6 zh-fTeSt*boCs-lZ6<)Zk<44eGG90=RHVdl1g69#Ci)<=uFPh9=pZmk=p>bxB9g80Yl8@G_V!a z2waLOMVt50o$o~~e*jCK|L~&XZ_u**%%V&or9r6n+)|eEzpzOU;bYx!^cBy764qL=YuToZ8qy!PvtqbeP9{t^ z7X_06KeQRh_L=4=*&(^Abi!ZnV> z(4j@l>f8tAcpAy_!qPh1BfTnK*JpS<63RdnTQ#u%%G(mPH=UAmvS--2-mqbL6GZ)x z?z)73)?=e&o)1Qu>~2J)=1`Gku0zQL=8DBNpxUyv=d*!d+@?-l)TqHPS#9a@m!Du2 zc6YORCbzug?m`QFnT}zM2y{gzgx15smqL~& z;!?hFgpq9S5)(PRQ0nUhX}Cm4PLbqnu&&w%BvDHA6aF?`=oRa($OyxY-zhDSaz5d` zA1B4OiR@~myC+H-JBB>jmfH&!1Mv-psGalQ{8a>w`*bw2VZ%dE3P>i;SDf2pB{-Ez zVh`xcHojZ4&-MNB0lmftU76sXwo)KTD_6id0sT zTk4a1Ru!5aYflAR{Y`b*lKj2zJHRKuVsytaA?Dnd21QK zVkqP4x!k+cz9s$igq#?PRDHhZsz&SDs$P&wIkp9ylVRv5$ zFxXsxGdz#bBlnj5F4MY+y(vY(i(Ue+$GG81oc-IW$$>$o#k{oFf4Tbx2hP=p06Fvr zk4x|hCy)5p`1fx?e}VO=gi(+hdx?0jWNk_iT_t=u+MKfz{nrKO#>3cR;>b1!L4fBJC9uOu#(j9R*7i@B%lK{`%3X z0Vs|STI9MMC@aG57*qs{$JYQjNK+Uaxs;-BZPv|SdmZ7>xTItG+7EODLii>aa;>sU z8nmw?8J}MU0WM^qA;zloCtOz-N(*c+kXi65plYg4yy61#M6}+D(=satkj_2Hek?%F z!9Jh=rDxPc^3D@pnOXuub_P0#n^jHTqZ+=9Xt%vRI~PR2^3UBL0Op8nf2_D9pq|ln zU{Exp*R_cjN9c6~1UP1*_|b$$cev`SFq8a;D|WI{Jzkihue?(EUynC>MqGaam0}*d zU*-L1A2MHWSL;F9E&ug2?qAqrrU4AvKWbg&CKYG%)#FP_Qa&kV)v!e8#*!uKC*%BO z3`GwDx9LBETr25jG>vR)e2zik`WMIU-K1te09CSaZHtS21o2 z>@Ji7!RfvP&Xcqz#=n93(bOPnLe?LtJ_3ZNR{=bX`TL}q7-&9|K*N!P@T2rx(f+)6 zQUADoY>8RV{_U3iU)Zc7xY~XqAhz~<==X|6Z9yo|{b_};AVsw>lURls34Q(;#Y}=v)kH9Dbxd> zB-K&s(Q`DSwi1UVct63f^CQgA)kZt!4(#31V}3_9Ltp>mY-(gI)fu6fBseeb{;Bt* zJFh(e?rSWX=KS|rxn8k6E;-F9Y-=k2`N=G4Rmx5dkC-O9N&?&=U_V$kfPyglip`Vc zYzOJ&Cdtyr@B#7V8-kce#599h*1{XU-1u}Uurq?K{J#<7f>6`=s-1&^euwYxbApul z-t^^JU&A0!9GgyPvwRMf{hjU3eg8R`5pLzjqcNb=`3F+ZArW~3w)kE}+>@vSxiBhKCBB(5dr4=ZGD zV{Avc7TnVUDNX$Z2D5J)unLsOD8NbsqH2V$LG(Y7wTwaCp|Q0b6dw{3eM>6?67Mcu za?QLYKY2yco@;cNkhpl9WEgvp$MXU(U~j>AOCr`OGv#lVD-Qtx3J4s;oYI{rx91l0 zkmo`{YJ{e7Q+H0jhiNK}q~xa?U*ATO9gxc0MjE7v`(ecEQUxmmy9{q7uXiSFbqi<( zt~Zl!Y!17Uz+Etlzf?99Mq3ZrEQ^VyX;V;H>v*4o@kmj7dCy5} zf_A_so0qKn^>Iw&#)6lYFFv;W=eTh-5d(tkZUqP~N$Sq0k=OQ7z6Co~_vzLNf zEIHu*Adg2}$0%Vx_pfkse^(EdA|{3J!f4(b)*p;nG90X=lVx3^ zl|hZz&gz}?wo+l_=|4=j9(`7+#Nd$Pu@_*Kf@!(v!@ zbJ9sWSEzGE;wPjlR*YdJNT6ZO@wlrU)8qd5!I;C;C0G_#CO*Klf_xg%-x5tCBH3!f z!aQjVA}>+R=HH${^x|sjMU_^MWv9lC?TALMm;-88 zX?R?7MTm_hHQ_?Aj$}J_(-toxabwVbJa4b~DhMJ>wENGmz*MAwxSbNnKazdz)!lbk z1kmM3#cpReyR6zFS^+2{gBE+~mUX6VT2)^H@1&9XFMEH=nJnAuvk;!0XOp!m$%c{h zugs6%L7f5#N;%28xjzY!Zvv}{bOTOnl-}T_WK}T){KV7i-JK;UIa^Z2ZrD&7Yb6SS z4F5g%Z&o^$;hc0BpeY6raRetq0q4lY?^JFyXI}%JvC4_V%D_Xd7P22Lr8F())StI7 zU*T;;G$O-a!O5%$y_i0CanaHQv3w8om%Z2?hN;iPQndE?WST-x)Kto=gX%K>&8`a4 z|8HlcbL5tysN35$m;a?nZxW`+)3)v~R=nD?v*Vnqb>)M7Y0o!vL(r@1!(A?zh6rli zEFOvjSb>|Se{;gbn77MrjV6aw$j^9Z{af6frt8;O*}3j;Jr96~$c0G|Wr;m>fpJj| zg!hTYJQpRpOGkly{mb1tE_OC4WR`_;LW`LXH+eyq>sv&i?Y|W7!rBpTr@DEv)ty_t3WWVxPwo1`l;{T(XV=Lj4Ak#`i=%D=uHe? z*7~1{Cn)7JcfWgsU{!)I@E3s`;rg3JZi(EvovH@|RI zV;_@9ru=7G{(QcBDN$tu*k+ggsgWQS%|I1sz=|zhu&Gk3Y}DW3udsQ;0mO|YU#+V7 zMxf@nffW2`at?Sd$r!-WXykj}ZF?{T03%9H3XmDG^yCN*SKbnmjB}&xaCd``!+QeT z=o+mqeIb5?Bzq#%%3n4wV7U4rt~rwJ`+CfgzKQE&@uj0V6COkfo81mp>vpsp~ zHTyJ-HpBeO@;Ioh3KaV1uk*Lr^Y-<+J)ADaOyVAJ79Bt^Tdx|m4|&5+;6vPBF1PffvCMp-!{E}jfdqhx8y0CoBdZ^hJyY(bhSg5 z5FtFU?i!`=HX)SKe0wH6ne(SCh}~r0dMD2ADN&DN8JvGGSH#Ms5%MK}aj9fegKasy zdk)QIXa>-!%fAm3A8BVa1tjVZ07h8UtZ_z#Z!l^jkCH$xdU0UgD97+^nWt%hS{jLU(Qi zf*q>F4N_FF&xV-Ri<(94$<%)(b{5b|UO9K+(nrLZ?%SXP ze{Y$_bstnh=w-TQQL97vA9LA2xGMNCp))~2b{2*vMkmV{DSB7e1EP6o-Zo0g&WFYv z8EmJa0*w0=Q%vLa(KQY(QY2shHo&rI^`?iU`iXqWqat_4tQ7Konpm z6DD0pjD3+tGTq5HGs3z5^*quI2z5!>0F(sRZO+7I<})`{zlqEOEyF znO`aXH&fxWiV?!TsYEn!yPxnM@Jh`H(;VAUFp#GV4U*-_ZLLJ-;0)tDG-Y$Ylm3%+ z3$jJ9XDDav9qaHSAex2uJ0Wuf8Dw^_O`h)oTW~+K;nDNU^!RKc=8)+An5)#-Ouy%d z>5K7Z6@KPBuW5~3Hr3dGGKY50ct2ccI_mukvAnjvENN7q`n;#t5d(`iKDLH8`?U*e zlUfH|wxshyOHM;ghZ*6E61K7#4og`HmI#s6(MIosD5+=L!}lFw+xqcurWtr^IZe8u z&~%~_zygzH<1Q)`FU~vUhbKhhU()!(`x4CQhPlFNX8>rSUiC=LIFl(G!b>WEcmS~l@v8q#!@?m zPTIMK9}^BJ+CcwF?2vKwF(%U+y6I)WNriLVSFtxhCsti+ ztU?abS4ancB~~POeH|bb{;VIBFS-N5s}v&WSM{GUzoW0$=_n9{k>4NMmkLBY@DPjU(4p5f-YvCLB!BsIz*2Z9cQ2n9XrBGsHAt;~ z=Uvp|9*%J&w<@re!*^)wyjnll(jXDwOVPg29B83uO6HnvD7DhY>!jKS|73tARO^c> zn1kXv<9)IyyP%t3dAirzMhBu6j;}Mg_ew)NrBLC3!x{tKdrPdG%nd@AuiF zHQ>`WIV_pqM~sysjxvhSiP9wLjpliC z)!>GCX>C~3@LLkNw(S*hn1(_pfv-R=l@fo0ZSan7LpKnSnTi+9h>sv!BoqV<@Uehi zh?%jH{^5Q9&cIjmY5W3a7LteL+!nHHZ40d&a|#n7AZlo6C}K=Hqo799Pe&)WbXb>b zm?ll^Jaehm^dX(sVdC^f@bsl)T<~;>w>GtxFpOp?Y`&nBt@Lw!2uitA-}~76f)GIk zKL$c9kWKNBl}YA&mg;#=-`ccs;CzPnMt70%aw)fL7CJlClM}u1`WtHYd>xX6ERls_ zy_7c!L9yd<+NMfyTJTuH*aTz=MpGd@Lh)d09W@Hi&~aPMcClUlwd4^N=jOPz(`aT3 zt#NVG>3sF&tWxI@hO3F@(KJkF|LwVeU1v<>q3cv{OG#tWYYfG&ebiJiz`nxG@f( zz+xd5+-9VV9F0cLQ!LIQ)`6(YNuTT~d z4JFy({)RcB0ZwQ)Cxf{kxP}N%2k6vPY@?0@qT_69)Za*WNJ%PSE_Huan=aj|S z3b!KQKYLzp7g;2Q_*#+qvHNG`)4QRm#{0|>dip(6h7)Vr17Xss?bYR;qsIAh;OtLM z=sn>3{a2_Fx_zCnHJxCT_4;fxMM1$Z9x{)3(b@tgxY|!Z#&BWKUVoyG^n`G0VAx1v zycZz>Q~cD@;`enobwPaFXm;C))BEtD$Y*WDep(TFJB;iMP!stsFL2e@uX5pNY+tnT zgt+B`!8RC3p<;&6XE>6E6Vdg z&^3;5dOD@MLp;tbbRclVR!izYs`J&M&i%e!Z zjpt>pkdb`%%3+1+M90HTT<5chmHWes&=Wf7{Z(j-*KzkJB>N#Mnz{4NwVQ18z~$fQ zjYoz;E@R~RoZtBAI)aZ!-gM{}Jl@xgwDw%lbIBci&9i&r&u}3Hb}#5&&!$y!`k|%q zQutF(<;3SpEiy&x7b8iRz(dvl0o^);b>1GJRi*9?^@ea4aFf*c;3_zHL56zB%O`)I4zj&S9;Ca}p)Q67l z@m>`#jm&bpH~^yGH{Q~`RS5OO8>hq%T;8a%;=p=e@9v|sq?_^4c3So|kuK{kwIYgRa)jM}>{|lMi%TUJ@!fK4Ct8Am#-u17r8>rT_hN=avYJWcVm7u?)ypC&nEwTM7vtM(;dIr{WGoSDFgJ0Gl%JTP(>R4C z=8`K8bT>Udew)t8frM|j{>{t1-M5aPa-{98Eq~cVAK(W{hulV)Zu=K%vL;Y}Jw)O_ zLYNcLs{nFEopiwulVqP-mVSm#@h>}vo0wfzo~p}OjA=){m4}--!aTl-x&CLq z{D!zqoT>)Dfy@4Q43EyXvXTf|3>#L7e4F}jp0K`Toun_u0xVZM=f{+Gz+wmtuhMW{ z3MgswL|O_S9TsZzd5kD9#;s@cdgyDQt$0(pT%{zuHMR?_gcOa&%^K`0QezIeg`*3O zhgX1Tv7m?mWq8?&61B!BG#3G4a-OlI;9P$=*c~}iHATXTON2H1^U-h3O=M!sn-ABOMI%0gV#2?aLX==cqE0-l#(oWi6Y9$24#US zO;$>B3;GAWsmEF0@$V0U9p!;l7WNtsRi?12++Oa>QX>n}j30c4E*ATl&wPD3K?8qR z@2){-PuD{DD+tMqYIIZWg0=AB*&rB~fUh4OMhKGJIgdf5vK2!=#P@OXr?7S=$exgp z@&*3PSQ<{=q}JB$ilNTTn?4wdG+xui&C)+EkmN&7_?X3qe(c4>Y}?)so$O7!-71Wa zAba~9XYaJ2jDJ^}yUBqght((#6^Mm)z+lVUoLVl2CwO>UOgeW29 zkc{urvQRU2EGxX^))x{szPW54uS=xo#6di|6C0TJM@4j9r|;+gl5i*$DCQQ-q;;SAMVTO6AQI-$gkkv z-xmT>m>6^p{|?0vG%}0_C4D~y$VKkY|KSWf@*lnl{~a&lDQOm~-+>+>tuCuxc+=f9 zRi+%mk6O2ImQ=kGA-{!OY<#&oC29CB$$vK4pHuNfMuA{fS;MzZk|uJgi|9V$nsk%* zK-2cwgpGHs(`IFxy)PnM6Xv@#9`tB&ir*^DTB-Zm?C%074w1U=BN^}!JXiQ<+(l`%N@l!_kG?e`&IPJ8R_IV5ICds(H2pbZ(&c?&#p`r@ z_VilAb)bH2Dh9*$r;?A z4r+{&MXRv|U(JCP`GJQ)0PNW-BgO1>eSQ~i%9QSrMn^E=^7TpWY{#D#V>`ENsaLPV z?vB!#_bv^z?$|mLq3gsv+#)9XGR^$aw$(MUg1BJtr|ack{pj6N{jAML{QXog&aF%5 z^C0lx?_4iMo1l5d!X2!8?>z6-#xtjToGb^<+RJ=8CQN?cT@Y%zyjppLYv^#^!YLu2 z;eY8)`+Krm`7YyPlOe+1jP-nUc=}mYYV+VeveaR)Bg$RK>!xxFfjsTJVHa zVwCRlZbr1%6aHg*?c5zh-$&A<*W#KMxW}d~QliapCB{?VStvalwa)Y4pVRKy7@i{R zs~+DSSyBO@3fVoEgA~rePs{x(59h1&>QaZJfdzaGJoO**0Jy{F{Rrw4bsJ*jAWrfA z82mUNz%8>iH9UJl_kPcc4dxAGHp1Xf~@13#8 zWS7FK;sM_0d3t;97DXHHSCI?{m<@up2V>w)A9%~bkfQZx%(Il_IsVD!ZLR=A^_lCL zVwa;3A)}Ef#|6U1hQEp>a_R0bv}ayo)xck5w@~ZEF4}EjtJcJH{q1NMcxe~7E7J$h zbpq{M7+jvl;CYTyT)fkTOyZ4-NPh#NMqH7+LrVBnyr)lfINFD&7>v!^lC%;1H4ZEeUdbPe3&1hfS z&b@l44xwJ3rY!bB)fwsH^J}n;{J9$>T24gIOOXcr0eYs}gK0fs652!({B-Z(vE6>5 zj`wSS5$Zy_!%a|4z!3Q0pIjq)i+4Dkd#m%2<;AA#MHkLmv1t>(HF_D6wGUMe-@acf z9NHu$K2fiZL^|R4r+(UL;^3dC$DoUL+x3glbG*n*k3mj}Q?SdV_bc8F(*}QbFy0m9 zX_N_9>pfT1`$AmegkOENtP6UQoe2|r#iEe=aXIJ&tDBBvhti+Oj?DNVS0plRE>Z|Y z7sHlnqg}b$0}_`k2+5w;g_u7wl=Ym*RU$s zbP)BOSOg_zxV4@O0JNT$`-yspFaujt!eZR)5O$ROj7tz_N3unF;uwbfme_xu9{UW~ zgQg=!qg8_lBsd*&@|M|=a>z@Zk!zIcDHE4(E!^f9CoB+io7yWsH2UU`(K0-;fEDXn zMXJmIqU|};dqw};ApGbqh@z0bIh3hDmE$Jc*N3SA;Gr*^;A{R3&}&${^fWut5l#zPbHuwBkWo!+MpPO%D9W!H~+Yd zNbBlW#jv48=u(;pgI3@6>!{i0hH)Y=r@!}eol8FQJZ58lf#$!dfRG5DDkGuXagt}O zqB*R@OIGU4Dg?_emYJN=^_(9dlxXzR#qg@foB`3`^a51IH6U5R;=H)BsNH_EO|W(# z#A6r5u~NRQYv9&*frW32!^nVB&h5y;_2}E@C%iXDjZ86Fl&j3{qpLMqf@;Cmqz-V$ zYf+)tW3k+YNneLS<0kA;jGgT{M9>a~g{>y3o{8DUYv}W7=2KCp`SA=T{`Twg-0^F* zO}53~VcPnV5V*gd_7^fS{j7cS0OiuI_k_>EUIUQ|9QZ$}Vz)#$fO~=G3MqXni&y)!WY8je%q*F)E9H!2vCacx+rdvo{W+r4x4nA2r-@jy{KP zWOzZ{*C`)pTYB}S2mmiQLwy-D?Xu98(@GTK7*#D|YZ6j!p93js z^fa6-b{OraJ+RV~4ZmfMH9(q>@&Ccq{O^QK5t%oRN`8#X=|bNgE=F9p7s_#@=5(ZZ zCRR>@|DC-Z zbF$0xX?jXU7(row1( zIH9qe3M7yPH(ZJUZ%+WZpJqb<9|$h6(yU9&_>NJ{^)pHDnZi2MH!fn;R`j= zytTnPNRKv+emd<#@bWK`q0vVB>V#(XH@3O=!N0o+ZHhX~=|U|ywvGVDhx4`O+hH(w zMN0xd!C}zKF?i(7!s`m@1$&x`?suzCe-ip*gCzR)svS$u9qG*$REcZ zP`$3xKv)x)H`=YcT+N*hFEm8}e#zH+oYirwvp?9sTc_YST0haFL)W>#c(q3sXnP{i zq1LoOr-Z7mPi!-0lkh4wK4mfCLgHdcbfca29G8J=7QRz4Z8_nH zDdS~xJUpw79lXn6oiRO+9N-qAoAy%z5u(0%jAp zd;|J8$m0P1Bh~eBIZ&ZwXOv|lg(1;mhwgN}9h`6_5g$YY=oZ$f%V$~u5VfZGvmyB% z&BrLO^gfD1(sw^9Pp^OKZxThe$IbH|fMPLbGe0iJ0hp5%(|^09e0TcN+2pzN)5<-e z8UT9XC%B%3XbgFivlp*00>H$O-KF65#?2G@l_W5a8z1Xo&TNUB&E;A{h;DvhRlLI? zJo{wM#>@5TG&n?&P^Zs%@nlEuoq9QW@up{(`~CPB_TgG8cjgB4_LE<%x!wtXHRuhP z=J?+36R+cmmjAfITsV=On3AZ9p0Devo!8YzWA{MxB+q~Vx4n2=OX@T2PxXZ zzA0I?GWLe&FHuRuEu5jYciofk5%+YcNjxv>%=*RN_OybbTR}u zLCJRiS)Qob-sWhfZ0>*+^>iDQHI=MQ!zhVW*zV4PI`*{AUA?ssEM@u!Rd6+*$JQ=C z-4j4J*oLKTv4l((6qM)1EtND6XF)PCKN6$+zjF3F z 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 diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/LogoSmall.png b/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/LogoSmall.png deleted file mode 100644 index a3a2317c78293b59471bac6b289df83f29614b29..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14038 zcmV;{HYv%8P)Px#1ZP1_K>z@;j|==^1poj532;bRa{vG}(f|N8(gEIPp_2drHfu>lK~#8Nt(^&& zrPHu zCYo+qqk0~7^-%MCPMxZ{zqS77J(}=+&-dJKJ+EDBuf5jVYyJ1y``zbMbrMTw2Kqxt zWkNC@;<3=zA2R)6AcTITv0z-xB+$S>=!F?DS#O`|n>=RnL@cEGjDgU>E}1Y_dGXLU zU|l@N+)rM@y&{Z-L_F&jh5%5;0X*KYML|QRsg`0O;UH zDkv*K)L&_W1Nq@hyqM!1$hzsp1D$rwP? z0_g#oVAUwoC^vNADJ98;1&o4&hiEi4tvoX-SJEdQl<=c4h(QTx2t;TZ%__SbKhj~@ zG#wd90iyMvpVODnS|5{wcw+<^qf{2!7AO@21gmWJ9L?2fr`Q{3VCGWtYNmt&ajB{o|js%zpj8P3i$nl^O zLxK*YES0+0iZP)VEc$qZN1`E!sYRMpuz6!-V3CP&?nqg2BA{wW`R+5hJcfb{Zffsm(5qAzzavn zqLZD%C6pl^2wp`ef{_GUWHON)581=iH4Rx`c!s2GLY|J)08(LOsd^5AMKBRTSFcA9 z9qI^)n@ltfkB0ETnI+%&*)a&c8MGzYp^V~Ci6K#vn3D-aJS;dZ7-{8m5#tj%%DLP$ zq!W!%yvf$OepA?L ziN+FkQ@Zfo{_=)FUnS#Hm8T^>u)<&fgD$=H&fo+Jt#O^9Te6mz_!r%Rj$A^S{hY(v3y#1OBAG+rzG-L7j-A^o> zu-o3m0h%Z%N$pKT&|egUOG}rQ=}<7#JQNECAvq^4J(U1DK!7sd%-rW?F%Xix!R@;h zO-+Z6Txk{p)*=B45)sve20c}u;V9grSr{T_&4pY;@G6lwNi2{lFy)(|?O3%u7p2Lg*_D ztXLZmL>V;$-S-+E=t`3zG;vEUtwVj^1n2pyb= zw=klj$>P$(Mjzix<^m7g1qzQwRn}o$eR=$2z3dA}o z5e_MYIl>iAbk#r%Skf2tILA=vXlh0=hnGX-5R$|tmtO^k7am4v(EuHY))(8n6QZUk zz0O__sHD8Ka?sl^KjSK4)0(=9njt&yx|h<%6@UU0gh|5jVpc5-Z8jM{zvP^_aZ8s` z(m+THZytR8F{G#w;s>85@iZ7fjog?~SqNpqMu=OrC?h8%hE&Cnq2r#Jhs5ziD>>$- z=~Eyi!dPpMommjm5Q>fr9<4JmA*aeTJza%ICNMpjfh$fs^4-^-b6LnQD7@~Wg<~dt z%_4S(kUTIY-e?7iogF0QT}H_tA~>AVu!GPfdMiV>B?r9$ajBy~iNT{|Zap7}E({X9 z;TZ+@^R+Q&m$dWgIdGEy|##tcWis*L27sS|kuX;A4o8 zU~r|bWfEyZC?khQL2%HrgQQQ(M<`-IL2H9=?L@Y)CN+rlaUy#E$_dyZgg2xjywEn4 zY{Wi2_NZ!BcqRiZJVJO^db#5aKx+mKzUY>R;&Bfrgoe%Qes$ilJ*kw+o(FF{yzdD} z#1O7H&g?bfZmD8&%WfjiinG}^8gTnI#Dnp^A2H{F$02fp!HNJ;V5hhC7(>_)PD~i- zkOgv5V*?>4HcQ-{+dIwI24aZPgSTXbl&>Fr;^ND$QdSJ>>Y)L}N+ue@ucEZvh{kVK z$cdw7__)Ht!Vlhh(WVNaam$8no7T-Z=$QV27;%^~R=)BUI&u3hrppvegb-4o#K(i2 zQN$$1D~}nOmjI-xoU`HimLX!S@X5(Yo|F~16E;L^8F}^5B12Ot7~>&jop{N+jhP@} zb|y7*x57Me@#R+`qGRE@7r8b=uM5J07)8hIz1;96N3?XOOy9SD!>aWwJ~Y)3Hm+Qn zN~I?5ITwAZ4`RODfk?`}Iq&hqD6NDL^KwL@uOS<753%NM`-+CHCU)J> z#(G8);v*V#&?aFRx;%J9!pai?)Y{o|E zv}~_$-M(qtx{o`WwzM_Wr&C@1nRL3lBh}r})7{LDmC!i9w#@tlM zpBoGTBcugNQh+faqQhR9@}Ofvy*(YuSX+|EH?k#+vXhq)Ash(4J(#5uK#G;`j6|^EpOhux@pUXzKq`s z{|EH*5Kal9jA!D+()sgk$)0$kbH$47A++uN_4+IB%_K3ZhybghY+F@l3 zJBLYKDf9HkAzxUE(g`wb*aK@A2h4;h9X8dK8NamH-j;bCyx}<6- zfR5p}zb5*s$E{@Mk-`^_cb()?`Nsvu#31iSza$RKJ>=M-uPcSH)x zUP`GQEUndn>L|bf0YWjxJAxDxBps}{7MMwYSoq9>1;6|eLxV!~`=x(r>}p!uzw!R? z=nLT=sgSN2F=5QK0|t+sI%MqRno*N#hmE!ye)Ps6COl9A5E7_2*4-3eIecImf#M$p z(tv0NEqQhSbG|<2oO9wYz8F6FgyL%E(DW@?%`)&Z38BLuUQYitbku~W-s9?r2Tmm* zz#B+$c&Te=yl*u8TQ>rZE(~fQfF|ASHsn9BAXe=u29SUPxR4LpI;U(KI+{R+rU0eB z_*~k&W&JO{y<2yueYzeSMlSyO>t#>IHm%w)C{(Qp8-EiX{Y%9=vo5@D^6aB9QQ9y> z!yodoO}@4Mh6Tp**Kubjk(SlN}y(a_O|9B|Ct#+VOpd36X#oYRrWkHPV!Rod3{3 z)#8r!plvk2#q#9JjfXDEmaRJyjwnM9-*~o=f{H^?qBzNEFggrx3@y8aakqubUvFKo z*JXDTL>X&U?!Sz^7JKTcP+xDxJ5U1$b_nIpbH*>N9->eA`2_{!#9RhP zgbOq4H6;lvN51HI_9kRVb%4as1Ro7ZiPn^&%cu5cj^vb}**LUzK>9uc&6gg#$5pa4 zVfzUVwAd#pjz4YMp0GHYf9LuN~&SP&0Hi zElzkqgbIadS^nIa!+K;-E8P;jZu~#X7cw+W;sg5b0+_JDQlIkS8G>QR7S<0!beL7= z7YH(I-8+9be@?s3{$EFY=Tgpr(BP9eQC6Ly>!I*xKA}spdHJG;EsL z-}hdavdufqj_6f6R&BbWPZW%7u}cW>QCMaI5r{fn?lQ?ots`bv$pZdqpF5akfvz! z)kB36F$FtWVOMF`tGoglt@+IP{dYyr2I7zK9`RTKU%5cRur*r60~bd_6Y< zSAlpNvh0=a0O9HoZjUa#<9FSA*4(3!tDtN_JV!p0Yq6Uh=XI;(q z$B$1vaQ}qo?%8wQu{(`A@bEM5UpDtAw-i^XY(n@}s2p6`5Np`BeOp+=F%tq9a9Z&) z%TaAuRQMlYVG(=i&7&s2a@})(yZ-qVV|U%)=l~X{+?Ej5x{oV-S3*NZ?R4&~e+scU z87UE+GeCw!5RfTpk~Qein$YLsg-A=LI+P3(E~V0A0ACDX2`|Wnh_0jB-qf=efd@5& zOBcO)?J4_tY~k?JuAcjYD@Oi)dF85}HHQpo*=tB97DJQxAR0{Y+11tg;S0Z8{MWl1 z?)k{xVIToIoXp&mxRq~mRgQU4b0}Ig=;$R6#gheOolGWJ(Ee0MN8{$UEo;X;b?2!q z+d=A|AuRNVvJ#Ekb)U0ud1~lTs?_!YX>!i8y1KPP#`x8NA)utS@1uRwsa%MQa}7o% zV!z!Xf!uwHrK3J`>46snl$~j-Oi<%W#R#zKTOm%jy2T5xI(fF~Q79MOz2E#Z5TYP` z>1Z*T2!cn*uT+79cvfK9f&)nJ{NBfiRTLA8UH3YN6eQ<7M0i3 zjNE19lv(4ae`Dgl-`wMnla4y?u8SVuaPke$jo)KGRuKb&*V~OnA`2@e7phEDaS*&R zvPm2b7&2A*w8}lZ4SBHFmN6`&w*#3=h`oJF>)4ybKTEUAhzXsG=1of%y=Kw3!R0hON%D@$j#8PLx?LL7$m0Y z%J8%{7p@`tJoJU7$=wl2vOP%{q;n)F`CM(&$HQ*>94z_vO)Hx>ulD%OElA-L54gn} zU7OFzm&)eYwdaRR8kbBClbK&KBPzAZ-fcVee6zf7EMDTt{A58%jC9l@#HujQq9MxNXU}u7(R(jFj!#3JpH!_MkB z%S+8;=Tgd5t34z$MSFGGcdcI_`A5+ncW5bi<`5~E#J|^7A`B5HMNM+-%3KAZrZ=P1 zc<53YNFePAEwD{T%>2g?lSx}BC^6=s1#D`HiWM%9gtTVsSWn$+PH6?ljM3ZA{3g@a z$KntcY9yeNHiryHEpmh~@qy&7Jtw8Mr|hF0SFI02; zj0BV7gz!vCOh-HrEi;m*Ra1nfQa~r)e>?3nTRIF`URqA~gEt5sef-#Zn?PrgkD&y? zYhqd^V+A17j^<-}9!9kwtQ9)w7Zw%G``)>x&1bz$tCsxzVN6j|tjYlo%m?LS&CzZ> zrxY|-j9q=mL7)EWN89R7Yg{$A{f_bJ!;=HWS&h$A&RhZG8De=vVhCG9JTQcc7QrhR zox+%tCJZv<0)bYHA{UPM#9^$f*9ux`K-8_Z38cTD|F|r~E)vx%Mv@{>|6Ic?%ZeZS>C*Kdpe!p+S*%DF{8P;u(>&8 zGAoP9*AJikd`tab+8Z{dyK?x2 zyrM9R1+8#l6r^_i;uOfRMNW<|2q`WtgKcegKq_PFN7`s;$60;x`p8&i!6O70{CeNhPAh6`tn;s_ru(G;;uGL| zA)x%GcmJnUQc*K&{yASi@xqc~`-nq!q%DB>a6?&<6vd+YA`66cK_sU}0m7@w7!omL zh7Os5rE20qq%CH51>N+8njcjth(Qk1Nv*krqN4`2VY|C~{{6HA*MFoBdSmkPZe6h8 zfSYa_v~;Np44{ug_RCO+_uqTTf(OU%*;77vbaBpjrX0XDpQHsnT4wT7gZLd4^29WBm^M+IvN5> z2v|V}9SRM(e({4^Yjfl6=N_?c85i(hU3~FDYu1c^^ik7~sKpDa#10tyNXH8yb>t;` zOg?ZpV+!L-iEuGc^w~_d_H^8`rg(8xN7($waMIRbA0eQ*-@o*zGp}VE+6fi>EcfGy zqY5X$1V#)Y1Wi>I$ch9Xz>zh|0IC^nYTJzd;)DbWU6RK&yA%zR`!Fqj8AZ+ZxgS_e znOz_E2xRb8)ioE~`%3lD+d`Oe-+f;|3Mz2jLDZdn#~y~3R2Gccy@pT}#+MTFLbLu3CGNA!-=zxB+1PgUFAc0hy8Toq+<$JJ{h=m z7ra+Ke(L^GE-tS|jqu2m`m@76SF=0QuC?5 z-n!dE=kNcw+fQHe^qrgQ-saMf9o&Jmu(96$)B_s^&e}YE_)dr4T)Xkru<+{8-E2`5 zwpM+7&!1g43?4mE2M9aBwu^5R3(PMNFpzFrC_wBIco1Mni8GrLRh6-C=-O7}9+X3e zR`4?!U3i`4@LUmW1^idA$Z^4eMGmYF5?YXgK^b$3>$%_F@bujry7NOgJ%l~|JD0T~ ztm6jectLdk9>P!kTjb9uk<1@FVUM8`XAa$Y#;}Pqt4Hh-OC*`7|D(3PUu`PiIIJjE zHR(HH#h*f-+p$sF!us+sW?{JXxo{DZ)2@GF`a$!7gH%C>lA*p>KLG?!%Mj^6Iz}i6 zaXrriJ|7#D78pv z0SKUI$sGxppiB#75JLtQmZXh$61J*~4n=J%EcK5DKmlGtBvxNudOT+CLtN|e&Y`yY zgEwzH)xMY?I&SJw=e_=iD;IMoPv&z4PvjL?JMGY6V|HuVwqbe0hEKQwY+3nkXY*DI z>Z@sFm~l|JbX=HIXutHzQg7($35{i8R8MFgIJ;%&N#n?h4=CUQI-KHk30`Fd1W1%T zpukX5hL;x(&=}(~H5DTp5DC?19=y14 zimj4uKOpzZC$xIlh)rY ze2#d_e=VG_UpTKOOyX6HBpup$N&`2KftJa2BY!(t`Qur4QBt79zy+j$gYY0e2{J7e zXNR&M%_=wjp(qSnl4!94EDAg~ec$3T^Df68lACXbtOXA0Avgxfq9fFHt`B znscPA)|_E>(1alzJQuqJeFzgm6GjRW!b%kD?emYTJDl+fUATzYbV$h7elZC;esw^Z zIzpM8VW1qEXrl1y(|`EgRVN{@8an1@3qD8|SMYuy4ipFx9a{#U+92}<;{)@71(Uk_ zN50ehV)ji<(57OBI7I}5Ci!RHYU;jYOWu~E{7{w`O46Z&{aA5s`OxnTr=W<6>nQrH zB1EN-GZ@i@5i~GVA1M?%vOGcx{{jeeLIXGtCEJ<-L_-jW$Qfb?GlswzAq*~HSSuhS zbGqisKUhEv0fc4ZJ8_@GzB_K$eULYAS^wxYC&mZ-8$s=K`6M{Uf))uhB=Pi=vHe5x zUmc&g!TKHpghMINC?4d6v#YCrH!1%8ME9QCQzvdKx-LHS&*KN5XrBa7AS7O0Fxp3i zs)=aDD{Q>kHjIuM4Ka)IqJz23>`tt*fCevU3lZ{!L5PGd4A8;5k^j+_Zz#-pMQRRh zTOp-ws483e!CSYSzCR}yhI<}y`Y{*WL;Q*iLfOnc5_p)|Mc+7l-$Oq6|PSdyL+86pP`t508Q9zw*>md+6uvg3A}HSd(mjpL^9?0tW| z`{&{<0>8eB_n)C6wrJ{zcp~w7QBfE*vUi`d6O%E!(AsEJk0?VViF-pQ+6OQej3-7I zCU+bS3%>-|EWU-o2pt6pgDXqiHSui+i2tF3xkBoBC_E@}#g+CColNeNF)=)PYE@4< zUrfg_<)LfU;U*LsQ>u#+K?pLY$J;K`57@k>Zp+$_u-)*{TfM37@zb-v3X-PH;?qse zyI|(jeUixs@ZH%N#*J)l*}7?K|1rvw$H-f)A94&;R$2J`M2A5l$h3ayV#=ub(5SaO zz5YSXoVXBknFwwenea1~;ee;-;c5dCz4C;od~{mVLcI7m<5AE)q6=Zw$pIgg9-6v9 zut{-(PNp|~|0TzMyfFLMZqpAxb>3OGB@0V%(x2{H|KY-hb;}B3>uxyb-YJt03*ku) z#JajLWlC7TKAdrO$xAP>Ded=Q1;ypXL-s1Q27OLo0Mh9vl4j!27k+ zAn+F-)dX>|01M)=e-U&_~grW*<)Wz&A(cKvF+&> zB|(P7fm;@h`_{A@-Vt_7EY;Xq&Cb|reDhw=? zEhzszIJiJehL8s}OlgS~1_`d{LU>Cg61yERe;}4v`@!o20|Tkf)=eM3y>-pfzDyd0 zl9F)KO<}?gf4SA&ZM&eb(3~Vo$FAHwrMRpbbTU0%xj(Y!p3%5z<)XhnP+2=-%%tf` zp0EW0*GnP7@&g%!P)Gp*I1&}44Tuo0GG|8O7h@DjjMY6$9WqoPBuTwMREH9#Cv^0( zLkMJ8Aw(ht-A-X>rN&Mqn)Z|lk)(Pvw|{?f!=^8O9-KA1=-+Ruswvx6ke|uV4?O9# z;nmh%v$*-njs0hkog$9)r>TNVP&nCl}(DJX6MX;2N0k&)_PggaAaTdMFmBdJxCWb zP!Jpv;u?VH3Q$lZ0xtAua-0zX%J^UO%G1BU^eEH#JaHsV+W%X-ANqswyH6{OZ_N)| z3u5(ovDW(j?`1;OXR)&2dNpG*kY4oky??p)(sXwR<9EE)R$5kug6diy0C||2LyGWd z=}JL35)YdWeDQ`)bRaSISkLRr8IAe@k9vd`MAh&Uq3TN@a%NG_@Dvm+Je0e*+bJGh z%3ydzCCo}-u`DFMg&975M4y%LxyzN zkAM61&tHAymg4g2+g@r!NN#A-f&zqu2MNW66>-w)l7-M*MJJa)kvY@ps6v^HY=>%U zo-A0fo{#^`!a>e-4Jn5VM3?}%UUrN^loE0XbYM_JjtO#X5rP*4aJ(h%zwpRqZ)D$z zNBr#0*6o|$efHk&w(P&T*^x@ChmG0epdmX=uNgIQ(5Q(OgGVOv3YAspOEqm={^{~} z{`vHM8$Vfu@}!w_FSz^fWW|;@$aHc$78Dfm=!Gcka6o0@2P`B+nY?Hrtl;g5%1YS4 z8-wqAsXrn6u=8(UUPV%g|OveZ=!}NXwrp8O(n)Qr2UVx4_dwW z74&1V_+`&F=M|OrbayU&;lag!`)&PlOZzJ+7RxUzDJZGv&-C{2(fafMqU?kV@0$DV zGs%{Rpn)MClla~gOq^V(F(iN_)K;a7&;UZ@kU<8S0qRm4@U*2$Ztxgj zuQ!Ws4+O*njG_bS;DkWXrtwXu2y8MhW(X%1hkx#YxTohAm07F>B{PmZq!H8UjGh;^{pI(dACB zI)e8kvY|rch=f7>kfWKqTomi;w|B8^DThf)v@S3vyZfToVs4ia8Ks1+PBHB}Afwg@ z3uPq8(MQc34uo6J{Km@nUN$cwT>flxK}jX96chisO4zz;$=by))~|TCaYJ3>rsaM9 z`|W=v;2u0?${yc1{^0MNQ&wKVNyHRDz>b4qwe&AD?cU@yB_>yTrr@9IU~D-ZHT03t z3Q8m-j)Yd28d!vDQew5Jtz|ya0A{qvmIuP+X0M%uL7d=`5iNT!0gX-Hf>3@ZEI_+?@RZfLne9og%yKF z3>`bEq_|Ltv25%$p+J}X*$RdF?!_x z7y}EA|Adr)Lk1j3to5q^q=e~Wrjv^fWrYp_fsT$U;V5%-RA0ye?y+C}+slvKYQ91^ z@Vgfsa{Bc-=P>BtS^goy$PsYRML-&afkO}h2$V@1m{4G-yuiZXP={198p4*Y?ht|! z#&GoC9ugRyfl}xgi1yPM!^G#6Bu*DHsfWu?TJaF}c46}cM8L#I0D=~KBn-jGuWX5i z5bgN%=~+4?5DL5PcZ6|H@BQt;bgBm-o&^NTmYBOWZw@ge&yv$Z=wk)SRDea|5B0*z z5f8}|016Om&+OC_V@|C|ChUf4f7M>9dphs@i<6X&(Hj^6N>~c9)rl&N%o4 zcNTg&+x~dfaco8l2p}9^)M(OOZEa0k+Z*eegl$k*X(YLAWnobC8)u$2@`Z zfAUMRahhgz2d2S5gs!wN5b2`QP|&z`$wZT8>h7nW9Xr|)iWQa4MnSTe6L9*_5? zKKoimR557S$-jJfr|EOp+9XEWYJmng04sOx0th59%x(()m)j^yA3{Os&&Xqbs2(zQ#l+IjD%K2YlSX>Bcy z3oe?1wE)){!tx)8u8OY!cL_J(ZCpd&WN%@sw{X?q6I5jB;*4$ zD3PszKo1EmIh%qrxsu_r%a=ogPIgyv&+4_%IfD_>MS-B({aW7J+xyHN=fCm9o!0q0 zQD{RZOdBz2W<~ADL_FTr(bC>f&xhxxPgkkR;)&#~uXYv}=947%Pp}|Vc-0z+FeySI zC4iV0hAx!D4ikZeQ$~py1>{Vk3o0Z)489y#${(VY61l0X2cl|l1aRzj{h1^@+CX5B zW1;#oWzH`zE+im`EsW65LHyJm7rgq2T_B1pYPwp#4 zWkgz*?^t+cQFP=92{n|>BzgnV6<%U!As93aFYlVr>n4j{4k^e4LPYK&AsMn7@tP;6%{{TG?6W`m_$mUa zZ~o*O1tth^3to@~5M3B%Du zJJHolu0mh}B+O0ipsyOSlgM8VbZuk$Hl&UyG)D2J?WsD|r$~4?zverWx2><+`Jocxu?NoFl)trxSU8VR2zec}4Ar(UWJ6pEhgI(2-h508+L*-~zE8 z`ZM=pUVOf@G>RJ8)99Sa$qA2D$BGm_S6Qe>Xu*j6{F#-*%SH@L!^pEDJ+@x%eTQ7j z1rUOgXgd6V3llkftN-sva#R5mLOh_UkK#fKSIDnCW_(lq+FA3@I_A7Pal$a>gbwH` z5O@N?A)~+{p?+^_#}h-Y00aPpISg_nxMETujn%*P*77=#6X?|h7fNE4H{SGp}s5(Ka6M%Xg|mxDQo>)U{k1z zCOpAF7EIJa+Cb9JVMk-SB;|R1MzYL$cYrh*W;n9NcL;cpNC7zPfQWQ78KIyFW2*;_ z9zDvSX_x7E7%_fVrr5UrlU0jfLk$m>hL*p!ay3tyXa6el<4E8lzNzy9}kGikek zF(E#!ejN@gw`eYd%#R*Z^Tkjh5CX3b^x1TtyV-g$bcKb5!De(gP@~JEh2gxOJ9AgddR4f(z5z>buF7$G;iJ5-nh9h-JM@lQdp40 zmfS#!Dey@SfC8s4YS@niV-kk|HKxk~=NknztkBe|!{|}jqG>$gd2yhhBcjbR(MLm@ z8!4^w5yJz~@)HSiD;E7;TK}@gZx1l6A?N@rr28jUDAU3NA2Tw_kQ9*9CGPDF%~yVF zVn@q%8~6%JB=dKlbMy&6zq7KsmibAZ#jvdW)kfEJe9NnZb>{*v!^j`xmOh|3poED~ zh7hh8MOh#qRx4=Wtwl{gtaX5B1+d6ai(qC*a@3fHF7;wAqFFVWFeMZVNJZ!W4;0wS zkWs?b@`80mW!1csFSO3T(6I5lPTl7aJ~sDd(u-ew^wu9AkWQ!U#pm(h4WGEe2@>AF zc-FIHP0>|UmI&SGZ6FCAu(d3dohRa1W|MV6mTX!RMg#I|69^smq9F+(fkHHPC-k_P zo>A#QsPO=h3QcL?4Shbx2BBmd`Q^5YN=)DNlJ?apn@QUVRh$M3MM6N z5TvUaF+`$Vr-8u)TQqUr(%xBCQk?Fy$s}3(;agAMeQDjhuX+5RQ8KUK z^54EbaoSAgQZ;dh039VytD_2ut%M)vCJ{7#l?NcrjyXxw63 zfu#Y+Iy*2lVgwc>3Hj@QwXNd*dTXjcZWz1eX@cpq-Nv?L7LfZ-@gi0F% z3q~OnkO&|cy5>z?mq4_d2nn-e2P2Xb0YpeJ8u_fUL_7AxngreG@WPS=q()b-Ir-9= zP3Gv3p44cKiR7LBKZdWeLf?}2kN);5@O3s^qPT6;r=ulvus{O7q)nF=#3>>ruUeBK zqkyQWutJU@_#%hUQBLX-6_m{)$_0X9NSY#7kH`_o8WS-SNCH_!_z=l3nRJ*;X+YEz z3}SU5o8CvYhO~^bSqR!nX27nL_OT8VA0=C6gy9T=M@N-Hx2(YYXoVPpM-z7-qKOcS z!8{wME8~Qp@R$HDgcA!4PC$4$BQfXRdS=-K&4>FD{Yz!JJ2Z3BtbscMDZE&qeiA7l zMnTr9!xc=;HyW)Zw2aD78x|oYsog=vAtV_|P}AXs1S=NCyhU3KTjZEq@-Bg4lU~8yJ)prb|Xu=?)MF+}35ewnJ0pYGuBgwJh8~^|S07*qo IM6N<$g4PFbdjJ3c diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/PerformanceTest.md b/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/PerformanceTest.md deleted file mode 100644 index 3997c31e1a4..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/PerformanceTest.md +++ /dev/null @@ -1,36 +0,0 @@ -# Performance Test - -The performance test application contains a couple of simple scenes to test performance of Jolt Physics. It will output the results to the TTY in CSV format. - -## Commandline options - -- -s=[scene]: This allows you to select a scene, [scene] can be; - - Ragdoll: A scene with 16 piles of 10 ragdolls (3680 bodies) with motors active dropping on a level section. - - RagdollSinglePile: A single pile of 160 ragdolls (3680 bodies) with motors active dropping on a level section. - - ConvexVsMesh: A simpler scene of 484 convex shapes (sphere, box, convex hull, capsule) falling on a 2000 triangle mesh. - - Pyramid: A pyramid of 1240 boxes stacked on top of each other to profile large island splitting. -- -i=[iterations]: Number of physics steps before the test finishes. -- -q=[quality]: This limits the motion quality types that the test will run on. By default it will test both. [quality] can be: - - Discrete: Discrete collision detection - - LinearCast: Linear cast continous collision detection -- -t=[num]: This sets the amount of threads the test will run on. By default it will test 1 .. number of virtual processors. Can be 'max' to run on as many thread as the CPU has. -- -no_sleep: Disable sleeping. -- -p: Outputs a profile snapshot every 100 iterations -- -r: Outputs a performance_test_[tag].jor file that contains a recording to be played back with JoltViewer -- -f: Outputs the time taken per frame to per_frame_[tag].csv -- -h: Displays a help text -- -rs: Record the simulation state in state_[tag].bin. -- -vs: Validate the recorded simulation state from state_[tag].bin. This will after every simulation step check that the state is the same as the recorded state and trigger a breakpoint if this is not the case. This is used to validate cross platform determinism. -- -repeat=[num]: Repeats all tests num times. -- -validate_hash=[hash]: Will validate that the hash of the simulation matches the supplied hash. Program terminates with return code 1 if it doesn't. Can be used to automatically validate determinism. - -## Output - -- Motion Quality: Shows the motion quality for the test. -- Thread Count: The amount of threads used for the test. -- Steps / Second: Average amount of physics steps / second over the entire duration of the test. -- Hash: A hash of all positions and rotations of the bodies at the end of the test. Can be used to verify that the test was deterministic. - -## Results - -If you're interested in how Jolt scales with multiple CPUs and compares to other physics engines, take a look at [this document](https://jrouwe.nl/jolt/JoltPhysicsMulticoreScaling.pdf). diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/PhysicsSystemUpdate.drawio b/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/PhysicsSystemUpdate.drawio deleted file mode 100644 index 4d5779bc5df..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/PhysicsSystemUpdate.drawio +++ /dev/null @@ -1,537 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/PhysicsSystemUpdate.svg b/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/PhysicsSystemUpdate.svg deleted file mode 100644 index 475dc579126..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/PhysicsSystemUpdate.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - -
              Apply Gravity
              (in batches)
              Apply Gravity...
              Setup Velocity Constraints
              Setup Velocity C...
              Pre Integrate
              Pre Integrate
              Finalize Islands
              Finalize Islands
              Solve Position Constraints,
              Update Bodies Broadphase
              (per island)
              Solve Position C...
              Set Body Island Idx
              Set Body Island Idx
              P
              P
              V
              V
              P
              P
              V
              V
              Read position
              Read position
              Read/write position
              Read/write position
              Read velocity
              Read velocity
              Read/write velocity
              Read/write velocity
              Broad Phase Update Prepare
              Broad Phase Upda...
              P
              P
              P
              P
              Solve Velocity Constraints
              (per island)
              Solve Velocity C...
              V
              V
              P
              P
              V
              V
              P
              P
              V
              V
              P
              P
              P
              P
              Broad Phase Update Finalize
              Broad Phase Upda...
              Start Next Step
              Start Next Step
              Repeat CollisionStep Times
              Repeat CollisionStep Times
              A
              A
              A
              A
              Reads active bodies
              Reads active bodies
              Deactivates bodies
              Deactivates bodies
              Build Islands from Constraints
              Build Islands fr...
              P
              P
              A
              A
              A
              A
              Find Collisions
              (per batch of active bodies and per pair)
              Find Collisions...
              P
              P
              V
              V
              A
              A
              Multiple concurrent jobs
              Multiple concurrent jobs
              Determine Active Constraints
              (in batches)
              Determine Active...
              A
              A
              Activates bodies
              Activates bodies
              A
              A
              Can spawn
              more jobs
              Can spawn...
              Find CCD Contacts
              (per body)
              Find CCD Contact...
              Resolve CCD Contacts
              Resolve CCD Cont...
              V
              V
              P
              P
              A
              A
              P
              P
              Finalize Contact Cache,
              Contact Removed Callbacks
              Finalize Contact Cache,...
              V
              V
              Not
              Last
              Step
              Not...
              Last Step
              Last Step
              Step Listeners
              (in batches)
              Step Listeners...
              P
              P
              V
              V
              A
              A
              Integrate & Clamp Velocities (in batches)
              Integrate & Clam...
              Post Integrate
              Post Integrate
              If no CCD bodies
              If no CCD bodies
              Starts the final job
              Starts the...
              Soft Body Prepare
              Soft Body Prepare
              Soft Body Collide
              Soft Body Collide
              Soft Body Simulate
              Soft Body Simula...
              Soft Body Finalize
              Soft Body Finali...
              P
              P
              P
              P
              V
              V
              P
              P
              V
              V
              V
              V
              A
              A
              A
              A
              Text is not SVG - cannot display
              \ No newline at end of file diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/ProjectsUsingJolt.md b/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/ProjectsUsingJolt.md deleted file mode 100644 index b0d8b9407f1..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/ProjectsUsingJolt.md +++ /dev/null @@ -1,16 +0,0 @@ -# Projects Using Jolt - -* [Dagor Engine](https://github.com/GaijinEntertainment/DagorEngine) - An open source engine used by [War Thunder](https://warthunder.com/). See [here](https://github.com/GaijinEntertainment/DagorEngine/tree/main/prog/engine/phys/physJolt). -* [ezEngine](https://github.com/ezEngine/ezEngine) - An open source C++ game engine. -* [Godot Jolt](https://github.com/godot-jolt/godot-jolt) - Godot extension that integrates the Jolt physics engine -* [Horizon Forbidden West](https://www.playstation.com/en-us/games/horizon-forbidden-west/) - An open world action RPG adventure. -* [HypeHype](https://www.hypehype.com/) - A mobile app to create, remix and play games. See [this](https://twitter.com/SebAaltonen/status/1726871354228482237) X post. -* [Light Tracer Render](https://lighttracer.org/) - A rendering tool, uses Jolt for object placement. See [this](https://lighttracer.org/blog/light-tracer-render-2-4-0/) announcement. -* [The Mirror](https://themirror.space/) - A game development platform designed to empower developers and artists with real-time, limitless creativity. See [this](https://twitter.com/themirrorgdp/status/1718019599361323023?s=20) X post. -* [NeoAxis Engine](https://www.neoaxis.com/) - A 3D game engine. See [this](https://www.neoaxis.com/news/neoaxis_engine_2023_1_released) announcement. -* [Sceneri](https://www.sceneri.com/) - A mobile app for creating and sharing 3D games and experiences. See [this](https://www.sceneri.com/blog/2023-07-27-jolt-physics-bringing-sceneris-worlds-to-life) blog post. -* [Substrata](https://substrata.info/) - A metaverse platform. -* [VPhysics Jolt](https://github.com/Joshua-Ashton/VPhysics-Jolt), a replacement for Ipion Virtual Physics in the Source Engine. Can be used in e.g. [Garry's Mod](https://store.steampowered.com/app/4000/Garrys_Mod/). -* [X4 Foundations](https://store.steampowered.com/app/392160/X4_Foundations/) - A space simulation game. See [this](https://forum.egosoft.com/viewtopic.php?t=451046) announcement. - -If your project is using Jolt, open a discussion or send a PR to add it to the list. diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/ReleaseNotes.md b/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/ReleaseNotes.md deleted file mode 100644 index 62d9b799169..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/ReleaseNotes.md +++ /dev/null @@ -1,209 +0,0 @@ -# Release Notes - -For breaking API changes see [this document](https://github.com/jrouwe/JoltPhysics/blob/master/Docs/APIChanges.md). - -## v5.0.0 - -### New Functionality - -#### Soft Body - -* Added soft body skinning constraints. This can be used to limit the movement of soft body vertices based on a skinned mesh. See [documentation](https://jrouwe.github.io/JoltPhysics/index.html#skinning-soft-bodies) for more info or watch this [movie](https://www.youtube.com/watch?v=NXw8yMczHJg). -* Added ability to turn on/off skinning constraints and to update the max distance for all constraints with a distance multiplier. -* Added dihedral bend constraints for soft bodies. See [movie](https://www.youtube.com/watch?v=A1iswelnGH4). -* Added long range attachment constraints (also called tethers) for soft bodies. -* Added SoftBodyContactListener which allows you to get callbacks for collisions between soft bodies and rigid bodies. See [movie](https://www.youtube.com/watch?v=DmS_8d2bdOw). -* Added support for a vertex radius for soft bodies. This keeps the vertices a fixed distance away from the surface which can be used to avoid z-fighting while rendering the soft body. -* Added SoftBodySharedSettings::CreateConstraints function that can automatically generate constraints based on the faces of the soft body. -* Added ability to update a soft body outside of the physics simulation step using SoftBodyMotionProperties::CustomUpdate. This is e.g. useful if the soft body is teleported and needs to 'settle'. - -#### Vehicles - -* Added support for less than 1 collision test per simulation step for vehicle wheels. This behavior can be configured differently when the vehicle is active / inactive. This can be used for LODding vehicles. -* Added wheel index to VehicleConstraint::CombineFunction friction callback and calculating longitudinal and lateral friction in the same call so you can have more differentiation between wheels. -* Added ability to override the max tire impulse calculations for wheeled vehicles. See WheeledVehicleController::SetTireMaxImpulseCallback. -* Added ability to disable the lean steering limit for the motorcycle, turning this off makes the motorcycle more unstable, but gives you more control over the final steering angle. - -#### Character - -* CharacterVirtual will now receive an OnContactAdded callback when it collides with a sensor (but will have no further interaction). -* Added user data to CharacterVirtual. - -#### Constraints - -* Swing limits do not need to be symmetrical anymore for SixDOFConstraints. This requires using the new pyramid shaped swing limits (ESwingType::Pyramid). SwingTwistConstraints still requires symmetrical limits but can use the pyramid swing limits too. These are cheaper to evaluate but are less smooth. -* Twist limits no longer need to be centered around zero for SixDOFConstraints and SwingTwistConstraints, any value between -PI and PI is supported now. -* Changed the meaning of Constraint::mNumVelocity/PositionStepsOverride. Before the number of steps would be the maximum of all constraints and the default value, now an overridden value of 0 means that the constraint uses the default value, otherwise it will use the value as specified. This means that if all constraints in an island have a lower value than the default, we will now use the lower value instead of the default. This allows simulating an island at a lower precision than the default. -* Bodies can now also override the default number of solver iterations. This value is used when the body collides with another body and a contact constraint is created (for constraints, the constraint override is always used). -* Added fraction hint to PathConstraintPath::GetClosestPoint. This can be used to speed up the search along the curve and to disambiguate fractions in case a path reaches the same point multiple times (i.e. a figure-8). -* Added Constraint::ResetWarmStart and Ragdoll::ResetWarmStart. Used to notify the system that the configuration of the bodies and/or constraint has changed enough so that the warm start impulses should not be applied the next frame. You can use this function for example when repositioning a ragdoll through Ragdoll::SetPose in such a way that the orientation of the bodies completely changes so that the previous frame impulses are no longer a good approximation of what the impulses will be in the next frame. -* Multithreading the SetupVelocityConstraints job. This was causing a bottleneck in the case that there are a lot of constraints but very few possible collisions. - -#### Collision Detection - -* Created an object layer filter implementation that is similar to Bullet's group & mask filtering, see ObjectLayerPairFilterMask. -* Created implementations of BroadPhaseLayerInterface, ObjectVsBroadPhaseLayerFilter and ObjectLayerPairFilter that use a bit table internally. These make it easier to define ObjectLayers and with which object layers they collide. -* Renamed SensorDetectsStatic to CollideKinematicVsNonDynamic and made it work for non-sensors. This means that kinematic bodies can now get collision callbacks when they collide with other static / kinematic objects. -* Added function to query the bounding box of all bodies in the physics system, see PhysicsSystem::GetBounds. - -#### Simulation - -* Implemented enhanced internal edge removal algorithm. This should help reduce ghost collisions. See BodyCreationSettings::mEnhancedInternalEdgeRemoval and [movie](https://www.youtube.com/watch?v=Wh5MIiiPVDE). -* Added BodyInterface::SetUseManifoldReduction which will clear the contact cache and ensure that you get consistent contact callbacks in case the body that you're changing already has contacts. - -#### Various - -* Ability to enable gyroscopic forces on bodies to create the [Dzhanibekov effect](https://en.wikipedia.org/wiki/Tennis_racket_theorem). -* Supporting SIMD for WASM build. Use -msimd128 -msse4.2 options with emscripten to enable this. -* Allowing WASM build to use a custom memory allocator. -* Added DebugRendererSimple which can be used to simplify the creation of your own DebugRenderer implementation. It only requires a DrawLine, DrawTriangle and DrawText3D function to be implemented (which can be left empty). -* Added ability to update the height field materials after creation. - -### Removed functionality -* Ability to restrict rotational degrees of freedom in local space, instead this is now done in world space. - -### Bug fixes - -* Fixed a bug in cast sphere vs triangle that could return a false positive hit against a degenerate triangle. -* Fixed bug in soft body vs tapered capsule. The calculations were slightly off causing a normal on the top or bottom sphere to be returned while the tapered part was actually closest. -* Fixed bug where colliding a cyclinder against a large triangle could return an incorrect contact point. -* Fixed bug where soft bodies would collide with sensors as if they were normal bodies. -* Sensors will no longer use speculative contacts, so will no longer report contacts before an actual contact is detected. -* Hinge limit constraint forces were clamped wrongly when the hinge was exactly at the minimum limit, making it harder to push the hinge towards the maximum limit. -* Fixed bug when a body with limited DOFs collides with static. If the resulting contact had an infinite effective mass, we would divide by zero and crash. -* Fixed unit tests failing when compiling for 32-bit Linux. The compiler defaults to using x87 instructions in this case which does not work well with the collision detection pipeline. Now defaulting to the SSE instructions. -* Fixed assert and improved interaction between a fast moving rigid body of quality LinearCast and a soft body. -* When creating a MeshShape with triangles that have near identical positions it was possible that the degenerate check decided that a triangle was not degenerate while the triangle in fact would be degenerate after vertex quantization. The simulation would crash when colliding with this triangle. -* A scaled compound shape with a center of mass of non zero would not apply the correct transform to its sub shapes when colliding with a soft body -* A soft body without any edges would hang the solver -* Fixed GCC 11.4 warning in JobSystemThreadPool.cpp: output may be truncated copying 15 bytes from a string of length 63 -* Longitudinal friction impulse for wheeled/tracked vehicles could become much higher than the calculated max because each iteration it was clamped to the max friction impulse which meant the total friction impulse could be PhysicsSettings::mNumVelocitySteps times too high. -* Properly initializing current engine RPM to min RPM for wheeled/tracked vehicles. When min RPM was lower than the default min RPM the engine would not start at min RPM. -* Fixed a possible division by zero in Body::GetBodyCreationSettings when the inverse inertia diagonal had 0's. -* When specifying a -1 for min/max distance of a distance constraint and the calculated distance is incompatible with the other limit, we'll clamp it to that value now instead of ending up with min > max. -* Fixed bug that contact cache was partially uninitialized when colliding two objects with inv mass override of 0. When the contact listener would report a non zero inv mass override the next simulation step this would mean that the simulation would read garbage and potentially crash due to NaNs. - -## v4.0.2 - -### New functionality -* Support for compiling with ninja on Windows. - -### Bug fixes -* Fixed bug in Indexify function that caused it to be really slow when passing 10K identical vertices. Also fixed a problem that could have led to some vertices not being welded. -* Fixed bug in SixDOFConstraint::RestoreState that would cause motors to not properly turn on. -* Fixed a determinism issue in CharacterVirtual. The order of the contacts returned by GetActiveContacts() was not deterministic. -* Fixed issue in sample application that mouse is very sensitive when viewing with Parsec. - -## v4.0.1 - -### New functionality -* Ability to stop overriding CMAKE_CXX_FLAGS_DEBUG/CMAKE_CXX_FLAGS_RELEASE which is important for Android as it uses a lot of extra flags. Set the OVERRIDE_CXX_FLAGS=NO cmake flag to enable this. -* Reduced size of a contact constraint which saves a bit of memory during simulation. -* Can now build a linux shared library using GCC. - -### Bug fixes -* Fixed mass scaling (as provided by the ContactListener) not applied correctly to CCD objects & during solve position constraints. This led to kinematic objects being pushed by dynamic objects. -* Workaround for MSVC 17.8, limits.h doesn't include corecrt.h and triggers an error that \_\_STDC_WANT_SECURE_LIB\_\_ is not defined. -* Fixed bug in MustIncludeC logic in GetClosestPointOnTriangle. -* Removed the need for specifying -Wno-comment when compiling with GCC. - -## v4.0.0 - -### New functionality -* Added support for soft bodies (feature still in development, see [announcement](https://x.com/jrouwe/status/1687051655898955776?s=20)). -* Support for limiting the degrees of freedom of a body to support 2D simulations (see [announcement](https://x.com/jrouwe/status/1676311800797622279?s=20)). -* Support for setting surface velocity of a body (see [announcement](https://x.com/jrouwe/status/1662727355553443844?s=20)). -* Added ability to update a height field after creation (see [announcement](https://x.com/jrouwe/status/1713670512801390829?s=20)). -* Support for non-power of 2 height fields. -* Expose a function to compare the JOLT_VERSION_ID with the version the library was compiled with to detect mismatches between library and client code. -* Added ability to specify extra ragdoll constraints that are not parent/child related. -* Ability to selectively save the state of a physics system to support replicating state over the network. -* Added constraint priority to control the order of evaluation of constraints (and thereby the most influential constraints). -* Sensors can now detect static objects. -* Ability to override mass and inertia of bodies from the ContactListener. -* Ability to specify stiffness/damping for springs instead of frequency/damping. -* Added option to disable the lean spring controller for motorcycles. -* Added vehicle callbacks at the beginning of the step listener and after wheel checks. -* Ability to override the position where the suspension and tire forces are applied. -* Support for building Jolt as a shared library on Windows. -* Optimized Indexify function from O(N^2) to O(N log(N)). - -### Removed functionality -* Removed support for integration sub steps for PhysicsSystem::Update. - -### New supported platforms -* 32-bit versions of Android on ARM and x86. - -### Bug fixes -* Motor frequency/stiffness of 0 should turn the motor off. -* RotatedTranslatedShape::GetPosition returned the wrong value. -* If a body is removed between the broad phase detecting an overlap and the narrow phase locking the body, callbacks could be called on a body that has already been removed. -* Fixed flipped normals in EPA penetration depth algorithm which could cause the normal to point in the wrong direction for collision tests. -* Respecting the IsSensor flag for CCD bodies. -* Fixed double locking issue that could cause a deadlock while updating the AABB of a body during simulation. -* Fixed a crash when fetching a body using an invalid BodyID. -* Windows 32 vs 64-bit versions produce the same deterministic results now. -* Heightfield vs convex was not filled in in collision dispatch table. This caused sensors to assert and not detect collisions with heightfields. -* The friction applied during continuous collision detection could be sqrt(2) times too large. -* The friction was clamped independently on both tangential axis which meant that the total friction could be larger than the amount of friction that should have been allowed. It also meant that an object would slow down quicker on one axis than on another causing a curved trajectory. -* When an object wasn't moving fast enough to trigger restitution for a speculative contact, the contact was enforced at the current position rather than at the distance of the speculative contact. -* Fixed CharacterVirtual jittering issue when moving down on elevator. -* CharacterVirtual was speeding up beyond the requested speed when sliding along a wall. -* CharacterVirtual reported to be on ground for one more frame after jumping against a wall. -* Added missing delta time term in CharacterVirtual::DetermineConstraints. -* CastShape had incorrect early out condition which could cause it to miss the deepest penetration. -* Pitch/roll limit constraint for vehicles didn't work when local vehicle up did not match world up. -* Wheel contact point did not return deepest point in certain cases. -* Fix for engine RPM being much higher than wheel RPM when measured at clutch. Before we were ignoring bake and wheel torques in engine RPM calculation. -* Don't allow the vehicle to sleep when the transmission is switching. -* Fixed bug that caused suspension to be weaker when driving a vehicle over dynamic bodies. - -## v3.0.0 - -* Support for double precision simulation for large worlds (see [announcement](https://twitter.com/jrouwe/status/1599366630273712128)) -* Performance optimization that allows solving large islands on multiple threads (see [announcement](https://twitter.com/jrouwe/status/1633229953775828994)) -* Vehicles now support suspensions that are at an angle with the vehicle body (instead of 90 degrees) -* Supporting cylinder based wheels for vehicles -* Experimental motor cycle physics (see [announcement](https://twitter.com/jrouwe/status/1642479907383959553)) -* CharacterVirtual can now move relative to a moving object (e.g. a space ship) -* Added 2D physics example -* Added functionality to estimate the collision impulse in the contact added callback -* Added a JobSystemWithBarrier class that makes it easier to integrate with your own job system -* Support for 32-bit object layers to allow easier integration with existing collision filtering systems - -## v2.0.1 - -* Adds ARM 32-bit support to support vcpkg-tool - -## v2.0.0 - -### Major new functionality -* Simulation is now deterministic between Windows, Linux and macOS. -* Support for custom memory allocators. -* A new character class that lives outside the main simulation update and is mainly used for player movement (CharacterVirtual). -* Implemented skeleton mapper that can convert an animated skeleton to a ragdoll skeleton and back. -* Rack and pinion, gear and pulley constraints have been added. -* Ability for sensors to detect collisions with sleeping bodies. -* Improved engine model for wheeled vehicles. -* Most constraints can now also be configured in local space. - -### New supported compilers -* MinGW -* GCC - -### New supported platforms -* All intel platforms supporting SSE2 and higher (was SSE4.2) -* 32-bit applications (was 64 bit only) -* Windows on ARM -* Windows UWP -* macOS -* iOS -* WebAssembly - -## v1.1.0 - -* Optimizations. - -## v1.0.0 - -* Initial stable release. diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Samples.md b/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Samples.md deleted file mode 100644 index ce0ab17741b..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Samples.md +++ /dev/null @@ -1,168 +0,0 @@ -# Jolt Physics Samples - -This document describes the demos in the Samples application (currently compiles only under Windows). When you run the samples application the application will initially start paused, press P to unpause it. The menu is accessible through pressing ESC, it has the following options: - -* Select Test - This allows you to select between the different types of physics tests -* Test Settings - Some tests will allow extra configuration, if not this setting will be greyed out -* Restart Test (R) - When selecting this, the test will go back to its initial state -* Run All Tests - This will run every tests for 10 seconds before proceeding to the next. This is a good way of visually inspecting the simulation before commiting a code change. -* Next Test (N) - When running all tests, this option can be used to quickly skip to the next test. -* Physics Settings - This menu contains all physics configuration. -* Drawing Options - This menu shows all the options for drawing the internal state of the physics simulation. -* Mouse Probe - This allows you to switch between various collision detection modes to test the different collision detection algorithms -* Shoot Object - A sample application is not complete without being able to shoot some balls at the simulation (B key). This menu allows additional settings. -* Help - A quick help text. - -## General Controls - -* Use the Mouse and WSAD keys to move around, hold Shift to speed up and Ctrl to slow down -* Hold the Space key to pick up an object in the center of the screen and move it around with the mouse and WSAD. -* P - Pause / unpause simulation. -* O - Single step the simulation. -* , - Step back (only when Physics Settings / Record State for Playback is on). -* . - Step forward (only when Physics Settings / Record State for Playback is on). -* Shift + , - Play reverse (only when Physics Settings / Record State for Playback is on). -* Shift + . - Replay forward (only when Physics Settings / Record State for Playback is on). -* T - Dump frame timing information to profile_*.html (when JPH_PROFILE_ENABLED defined). - -## The Tests - -Note that you can watch all movies below in [a single YouTube playlist](https://www.youtube.com/watch?v=pwyCW0yNKMA&list=PLYXVwtOr1CBxbA50jVg2dKUQvHW_5OOom). - -### Vehicles - -This categories shows vehicles created through the VehicleConstraint. These vehicles use ray- or shape casts to detect collision with the ground and simulate a vehicle with an engine, gearbox, differentials and suspension. - -|[![Vehicle Demo](https://img.youtube.com/vi/A_gvLH4KKDA/hqdefault.jpg)](https://www.youtube.com/watch?v=A_gvLH4KKDA)| -|:-| -|*A wheeled vehicle.*| - -|[![Tank Demo](https://img.youtube.com/vi/QwlPOKbxsqU/hqdefault.jpg)](https://www.youtube.com/watch?v=QwlPOKbxsqU)| -|:-| -|*Demonstrates a tracked vehicle with a turret constrained to the main body with hinge constraints.*| - -|[![Motorcycle Demo](https://img.youtube.com/vi/umI8FF0gVxs/hqdefault.jpg)](https://www.youtube.com/watch?v=umI8FF0gVxs)| -|:-| -|*Demonstrates a motor cycle.*| - -### Rig (Ragdolls) - -This category demonstrates how ragdolls can be made and controlled using keyframing or motors. - -|[![Kinematic Ragdoll](https://img.youtube.com/vi/gvq6qdU3ZTs/hqdefault.jpg)](https://www.youtube.com/watch?v=gvq6qdU3ZTs)| -|:-| -|*A ragdoll set to kinematic mode (infinite mass, simulated using velocities only) interacting with dynamic objects.*| - -|[![Ragdoll Driven to Animated Pose](https://img.youtube.com/vi/lYHhe6HLbs4/hqdefault.jpg)](https://www.youtube.com/watch?v=lYHhe6HLbs4)| -|:-| -|*Demonstrating a humanoid ragdoll driven by motors which are trying to match a sprint animation in local space (green sticks).*| - -|[![Skeleton Mapper](https://img.youtube.com/vi/hrnmgNN-m-U/hqdefault.jpg)](https://www.youtube.com/watch?v=hrnmgNN-m-U)| -|:-| -|*An animation is played back on a high detail skeleton ('Animation') and then mapped onto a low detail ragdoll skeleton ('Reversed Mapped'). This animation is used to drive the motors of the ragdoll. The resulting pose is mapped back to the high detail skeleton ('Mapped'). Note that the skeletons are drawn offset to make them clearer..*| - -|[![160 Ragdolls in a Pile](https://img.youtube.com/vi/pwyCW0yNKMA/hqdefault.jpg)](https://www.youtube.com/watch?v=pwyCW0yNKMA)| -|:-| -|*160 Ragdolls being dropped on a scene from Horizon Zero Dawn.*| - -|[![160 Ragdolls in a Pile (Sleeping Visualization)](https://img.youtube.com/vi/7ZMm7yObpqs/hqdefault.jpg)](https://www.youtube.com/watch?v=7ZMm7yObpqs)| -|:-| -|*160 Ragdolls dropping on a pile, simulated using the Jolt Physics engine. Yellow means the ragdoll is simulated, red means the simulation is sleeping.*| - -|[![160 Ragdolls Driven to Pose](https://img.youtube.com/vi/jhpsIqbsU4I/hqdefault.jpg)](https://www.youtube.com/watch?v=jhpsIqbsU4I)| -|:-| -|*A pile of ragdolls that are driven to a specific animated death pose. This gives the ragdolls 'stiffness'.*| - -### Soft Body - -|[![Soft Body Demo](https://img.youtube.com/vi/vJX_3FNISkw/hqdefault.jpg)](https://www.youtube.com/watch?v=vJX_3FNISkw)| -|:-| -|*Demonstrates Soft Body physics as simulated by Jolt Physics. Soft body physics can be used for things like cloth and soft balls.*| - -|[![Soft Body Contact Listener Demo](https://img.youtube.com/vi/DmS_8d2bdOw/hqdefault.jpg)](https://www.youtube.com/watch?v=DmS_8d2bdOw)| -|:-| -|*Demonstrates the use of soft body contact listeners. You can use these to affect the collision response between a soft body and a rigid body by e.g. artificially making the mass of one of the two higher so that the other is less affected by the collision. Finally you can also turn a contact into a sensor contact which means you get the contact points but there will not be any collision response..*| - -|[![Soft Body Bend Constraints Demo](https://img.youtube.com/vi/A1iswelnGH4/hqdefault.jpg)](https://www.youtube.com/watch?v=A1iswelnGH4)| -|:-| -|*This video shows the effect of bend constraints on a wrinkled cloth. The left most patch has no constraints to preserve the wrinkles, the middle uses distance constrains ('sticks') to preserve the wrinkles and the last one uses dihedral angle constraints to preserve the angle between two triangles on their shared edge.*| - -|[![Soft Body Skin Constraints Demo](https://img.youtube.com/vi/NXw8yMczHJg/hqdefault.jpg)](https://www.youtube.com/watch?v=NXw8yMczHJg)| -|:-| -|*This demo shows a soft body that is connected to a skinned mesh via distance constraints. Each simulated vertex can deviate from its skinned position by a fixed length. The green lines indicate the animated joints of the skinned mesh.*| - -### Character - -This category shows how you can simulate a (humanoid) character using a capsule. - -|[![Character Demo](https://img.youtube.com/vi/YjaJT9of7UE/hqdefault.jpg)](https://www.youtube.com/watch?v=YjaJT9of7UE)| -|:-| -|*A demonstration of a game Character. Demonstrates moving, sliding against the environment, crouching and jumping.*| - -### Water - -This category shows how you can implement a water simulation in your game. - -|[![Water Simulation](https://img.youtube.com/vi/CEr_LtQLGeg/hqdefault.jpg)](https://www.youtube.com/watch?v=CEr_LtQLGeg)| -|:-| -|*Water buoyancy and friction simulation. Demonstrates how various shapes and compound shapes behave in the water. The right most object has a lowered center of mass.*| - -### Constraints - -This category shows the various constraints that are supported. Constraints connect two or more bodies together and limit the relative movement. - -|[![Path Constraint](https://img.youtube.com/vi/6xMKNMjD5pE/hqdefault.jpg)](https://www.youtube.com/watch?v=6xMKNMjD5pE)| -|:-| -|*Showing the path constraint in action.*| - -|[![Swing Twist Constraint](https://img.youtube.com/vi/8aQ9x8SQSuM/hqdefault.jpg)](https://www.youtube.com/watch?v=8aQ9x8SQSuM)| -|:-| -|*Demonstrates a chain of swing-twist constraints (usable for humanoid shoulders). The green cones show the swing limit and the pink pie shows the twist limit.*| - -|[![Gear constraint](https://img.youtube.com/vi/3w5SgElroBw/hqdefault.jpg)](https://www.youtube.com/watch?v=3w5SgElroBw)| -|:-| -|*Demonstrates a gear constraint. Note that the gears can be placed at any relative angle of each other, so you could e.g. create a bevel or worm gear.*| - -|[![Rack and pinion constraint](https://img.youtube.com/vi/e588KG-ZSxc/hqdefault.jpg)](https://www.youtube.com/watch?v=e588KG-ZSxc)| -|:-| -|*Demonstrates a rack and pinion constraint.*| - -|[![Pulley constraint](https://img.youtube.com/vi/9P8OaahtU-4/hqdefault.jpg)](https://www.youtube.com/watch?v=9P8OaahtU-4)| -|:-| -|*Shows two boxes connected through a pulley constraint. In this case the constraint is configured as a block and tackle with and advantage of 2: the right block moves 2x as slow as the left block.*| - -### General - -This category contains general simulation tests. It demonstrates things like friction, restitution, damping, modifying gravity and continous collision detection. Some highlights: - -|[![Stable Box Stacking](https://img.youtube.com/vi/fTtjBLYBxco/hqdefault.jpg)](https://www.youtube.com/watch?v=fTtjBLYBxco)| -|:-| -|*A YouTube video showing stability of a pile of boxes.*| - -|[![Active Edge Detection](https://img.youtube.com/vi/EanFxlkZgcA/hqdefault.jpg)](https://www.youtube.com/watch?v=EanFxlkZgcA)| -|:-| -|*Demonstrates objects sliding along a polygon mesh. Internal mesh edges are ignored and do not cause objects to bounce off.*| - -|[![Funnel Test](https://img.youtube.com/vi/Y-UgylH992A/hqdefault.jpg)](https://www.youtube.com/watch?v=Y-UgylH992A)| -|:-| -|*1000 random shapes in a funnel.*| - -|[![Multithreaded Island Simulation](https://img.youtube.com/vi/_Lv5xlWtCpM/hqdefault.jpg)](https://www.youtube.com/watch?v=_Lv5xlWtCpM)| -|:-| -|*We will automatically split up the simulation in islands of non-interacting bodies and distribute the work across multiple threads. Each island has its own color.*| - -|[![Single vs Double Precision](https://img.youtube.com/vi/KGnlYSW3550/hqdefault.jpg)](https://www.youtube.com/watch?v=KGnlYSW3550)| -|:-| -|*Shows the difference between compiling Jolt Physics in single precision and double precision (define JPH_DOUBLE_PRECISION).*| - -|[![Conveyor belt](https://img.youtube.com/vi/p_H6egZzbZE/hqdefault.jpg)](https://www.youtube.com/watch?v=p_H6egZzbZE)| -|:-| -|*A demo of setting the surface velocity of a body to create a conveyor belt. The boxes have decreasing friction from front to back (last one has zero friction so slowly slides down the ramp).*| - -### Shapes & Scaled Shapes - -These categories show off all of the supported shapes and how they can be scaled at run-time. - -|[![Shape Scaling](https://img.youtube.com/vi/u9cPBGUFurc/hqdefault.jpg)](https://www.youtube.com/watch?v=u9cPBGUFurc)| -|:-| -|*A height field shape using various scales in Jolt Physics: Uniform, Non uniform, Mirrored, Inside out*| diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/SwingTwistConstraint.png b/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/SwingTwistConstraint.png deleted file mode 100644 index 10c16bc2064e15dd39a52a34fb2e32f3a4fff98c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 92708 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�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 zQENode, -# Edge and Graph Attributes specification You need to make sure dot is able -# to find the font, which can be done by putting it in a standard location or by -# setting the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the -# directory containing the font. Default graphviz fontsize is 14. -# The default value is: fontname=Helvetica,fontsize=10. -# This tag requires that the tag HAVE_DOT is set to YES. - -DOT_COMMON_ATTR = "fontname=Helvetica,fontsize=10" - -# DOT_EDGE_ATTR is concatenated with DOT_COMMON_ATTR. For elegant style you can -# add 'arrowhead=open, arrowtail=open, arrowsize=0.5'. Complete documentation about -# arrows shapes. -# The default value is: labelfontname=Helvetica,labelfontsize=10. -# This tag requires that the tag HAVE_DOT is set to YES. - -DOT_EDGE_ATTR = "labelfontname=Helvetica,labelfontsize=10" - -# DOT_NODE_ATTR is concatenated with DOT_COMMON_ATTR. For view without boxes -# around nodes set 'shape=plain' or 'shape=plaintext' Shapes specification -# The default value is: shape=box,height=0.2,width=0.4. -# This tag requires that the tag HAVE_DOT is set to YES. - -DOT_NODE_ATTR = "shape=box,height=0.2,width=0.4" - -# You can set the path where dot can find font specified with fontname in -# DOT_COMMON_ATTR and others dot attributes. -# This tag requires that the tag HAVE_DOT is set to YES. - -DOT_FONTPATH = - -# If the CLASS_GRAPH tag is set to YES (or GRAPH) then doxygen will generate a -# graph for each documented class showing the direct and indirect inheritance -# relations. In case HAVE_DOT is set as well dot will be used to draw the graph, -# otherwise the built-in generator will be used. If the CLASS_GRAPH tag is set -# to TEXT the direct and indirect inheritance relations will be shown as texts / -# links. -# Possible values are: NO, YES, TEXT and GRAPH. -# The default value is: YES. - -CLASS_GRAPH = YES - -# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a -# graph for each documented class showing the direct and indirect implementation -# dependencies (inheritance, containment, and class references variables) of the -# class with other documented classes. -# The default value is: YES. -# This tag requires that the tag HAVE_DOT is set to YES. - -COLLABORATION_GRAPH = YES - -# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for -# groups, showing the direct groups dependencies. See also the chapter Grouping -# in the manual. -# The default value is: YES. -# This tag requires that the tag HAVE_DOT is set to YES. - -GROUP_GRAPHS = YES - -# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and -# collaboration diagrams in a style similar to the OMG's Unified Modeling -# Language. -# The default value is: NO. -# This tag requires that the tag HAVE_DOT is set to YES. - -UML_LOOK = NO - -# If the UML_LOOK tag is enabled, the fields and methods are shown inside the -# class node. If there are many fields or methods and many nodes the graph may -# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the -# number of items for each type to make the size more manageable. Set this to 0 -# for no limit. Note that the threshold may be exceeded by 50% before the limit -# is enforced. So when you set the threshold to 10, up to 15 fields may appear, -# but if the number exceeds 15, the total amount of fields shown is limited to -# 10. -# Minimum value: 0, maximum value: 100, default value: 10. -# This tag requires that the tag UML_LOOK is set to YES. - -UML_LIMIT_NUM_FIELDS = 10 - -# If the DOT_UML_DETAILS tag is set to NO, doxygen will show attributes and -# methods without types and arguments in the UML graphs. If the DOT_UML_DETAILS -# tag is set to YES, doxygen will add type and arguments for attributes and -# methods in the UML graphs. If the DOT_UML_DETAILS tag is set to NONE, doxygen -# will not generate fields with class member information in the UML graphs. The -# class diagrams will look similar to the default class diagrams but using UML -# notation for the relationships. -# Possible values are: NO, YES and NONE. -# The default value is: NO. -# This tag requires that the tag UML_LOOK is set to YES. - -DOT_UML_DETAILS = NO - -# The DOT_WRAP_THRESHOLD tag can be used to set the maximum number of characters -# to display on a single line. If the actual line length exceeds this threshold -# significantly it will wrapped across multiple lines. Some heuristics are apply -# to avoid ugly line breaks. -# Minimum value: 0, maximum value: 1000, default value: 17. -# This tag requires that the tag HAVE_DOT is set to YES. - -DOT_WRAP_THRESHOLD = 17 - -# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and -# collaboration graphs will show the relations between templates and their -# instances. -# The default value is: NO. -# This tag requires that the tag HAVE_DOT is set to YES. - -TEMPLATE_RELATIONS = NO - -# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to -# YES then doxygen will generate a graph for each documented file showing the -# direct and indirect include dependencies of the file with other documented -# files. -# The default value is: YES. -# This tag requires that the tag HAVE_DOT is set to YES. - -INCLUDE_GRAPH = YES - -# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are -# set to YES then doxygen will generate a graph for each documented file showing -# the direct and indirect include dependencies of the file with other documented -# files. -# The default value is: YES. -# This tag requires that the tag HAVE_DOT is set to YES. - -INCLUDED_BY_GRAPH = YES - -# If the CALL_GRAPH tag is set to YES then doxygen will generate a call -# dependency graph for every global function or class method. -# -# Note that enabling this option will significantly increase the time of a run. -# So in most cases it will be better to enable call graphs for selected -# functions only using the \callgraph command. Disabling a call graph can be -# accomplished by means of the command \hidecallgraph. -# The default value is: NO. -# This tag requires that the tag HAVE_DOT is set to YES. - -CALL_GRAPH = NO - -# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller -# dependency graph for every global function or class method. -# -# Note that enabling this option will significantly increase the time of a run. -# So in most cases it will be better to enable caller graphs for selected -# functions only using the \callergraph command. Disabling a caller graph can be -# accomplished by means of the command \hidecallergraph. -# The default value is: NO. -# This tag requires that the tag HAVE_DOT is set to YES. - -CALLER_GRAPH = NO - -# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical -# hierarchy of all classes instead of a textual one. -# The default value is: YES. -# This tag requires that the tag HAVE_DOT is set to YES. - -GRAPHICAL_HIERARCHY = YES - -# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the -# dependencies a directory has on other directories in a graphical way. The -# dependency relations are determined by the #include relations between the -# files in the directories. -# The default value is: YES. -# This tag requires that the tag HAVE_DOT is set to YES. - -DIRECTORY_GRAPH = YES - -# The DIR_GRAPH_MAX_DEPTH tag can be used to limit the maximum number of levels -# of child directories generated in directory dependency graphs by dot. -# Minimum value: 1, maximum value: 25, default value: 1. -# This tag requires that the tag DIRECTORY_GRAPH is set to YES. - -DIR_GRAPH_MAX_DEPTH = 1 - -# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images -# generated by dot. For an explanation of the image formats see the section -# output formats in the documentation of the dot tool (Graphviz (see: -# http://www.graphviz.org/)). -# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order -# to make the SVG files visible in IE 9+ (other browsers do not have this -# requirement). -# Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo, -# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and -# png:gdiplus:gdiplus. -# The default value is: png. -# This tag requires that the tag HAVE_DOT is set to YES. - -DOT_IMAGE_FORMAT = png - -# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to -# enable generation of interactive SVG images that allow zooming and panning. -# -# Note that this requires a modern browser other than Internet Explorer. Tested -# and working are Firefox, Chrome, Safari, and Opera. -# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make -# the SVG files visible. Older versions of IE do not have SVG support. -# The default value is: NO. -# This tag requires that the tag HAVE_DOT is set to YES. - -INTERACTIVE_SVG = NO - -# The DOT_PATH tag can be used to specify the path where the dot tool can be -# found. If left blank, it is assumed the dot tool can be found in the path. -# This tag requires that the tag HAVE_DOT is set to YES. - -DOT_PATH = - -# The DOTFILE_DIRS tag can be used to specify one or more directories that -# contain dot files that are included in the documentation (see the \dotfile -# command). -# This tag requires that the tag HAVE_DOT is set to YES. - -DOTFILE_DIRS = - -# The MSCFILE_DIRS tag can be used to specify one or more directories that -# contain msc files that are included in the documentation (see the \mscfile -# command). - -MSCFILE_DIRS = - -# The DIAFILE_DIRS tag can be used to specify one or more directories that -# contain dia files that are included in the documentation (see the \diafile -# command). - -DIAFILE_DIRS = - -# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the -# path where java can find the plantuml.jar file or to the filename of jar file -# to be used. If left blank, it is assumed PlantUML is not used or called during -# a preprocessing step. Doxygen will generate a warning when it encounters a -# \startuml command in this case and will not generate output for the diagram. - -PLANTUML_JAR_PATH = - -# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a -# configuration file for plantuml. - -PLANTUML_CFG_FILE = - -# When using plantuml, the specified paths are searched for files specified by -# the !include statement in a plantuml block. - -PLANTUML_INCLUDE_PATH = - -# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes -# that will be shown in the graph. If the number of nodes in a graph becomes -# larger than this value, doxygen will truncate the graph, which is visualized -# by representing a node as a red box. Note that doxygen if the number of direct -# children of the root node in a graph is already larger than -# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that -# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. -# Minimum value: 0, maximum value: 10000, default value: 50. -# This tag requires that the tag HAVE_DOT is set to YES. - -DOT_GRAPH_MAX_NODES = 50 - -# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs -# generated by dot. A depth value of 3 means that only nodes reachable from the -# root by following a path via at most 3 edges will be shown. Nodes that lay -# further from the root node will be omitted. Note that setting this option to 1 -# or 2 may greatly reduce the computation time needed for large code bases. Also -# note that the size of a graph can be further restricted by -# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. -# Minimum value: 0, maximum value: 1000, default value: 0. -# This tag requires that the tag HAVE_DOT is set to YES. - -MAX_DOT_GRAPH_DEPTH = 0 - -# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output -# files in one run (i.e. multiple -o and -T options on the command line). This -# makes dot run faster, but since only newer versions of dot (>1.8.10) support -# this, this feature is disabled by default. -# The default value is: NO. -# This tag requires that the tag HAVE_DOT is set to YES. - -DOT_MULTI_TARGETS = NO - -# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page -# explaining the meaning of the various boxes and arrows in the dot generated -# graphs. -# Note: This tag requires that UML_LOOK isn't set, i.e. the doxygen internal -# graphical representation for inheritance and collaboration diagrams is used. -# The default value is: YES. -# This tag requires that the tag HAVE_DOT is set to YES. - -GENERATE_LEGEND = YES - -# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate -# files that are used to generate the various graphs. -# -# Note: This setting is not only used for dot files but also for msc temporary -# files. -# The default value is: YES. - -DOT_CLEANUP = YES diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/HelloWorld/HelloWorld.cmake b/crates/joltc-sys-patched/JoltC/JoltPhysics/HelloWorld/HelloWorld.cmake deleted file mode 100644 index 0580dbbe25f..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/HelloWorld/HelloWorld.cmake +++ /dev/null @@ -1,11 +0,0 @@ -# Root -set(HELLO_WORLD_ROOT ${PHYSICS_REPO_ROOT}/HelloWorld) - -# Source files -set(HELLO_WORLD_SRC_FILES - ${HELLO_WORLD_ROOT}/HelloWorld.cpp - ${HELLO_WORLD_ROOT}/HelloWorld.cmake -) - -# Group source files -source_group(TREE ${HELLO_WORLD_ROOT} FILES ${HELLO_WORLD_SRC_FILES}) diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/HelloWorld/HelloWorld.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/HelloWorld/HelloWorld.cpp deleted file mode 100644 index 72b6b552913..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/HelloWorld/HelloWorld.cpp +++ /dev/null @@ -1,364 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -// The Jolt headers don't include Jolt.h. Always include Jolt.h before including any other Jolt header. -// You can use Jolt.h in your precompiled header to speed up compilation. -#include - -// Jolt includes -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// STL includes -#include -#include -#include - -// Disable common warnings triggered by Jolt, you can use JPH_SUPPRESS_WARNING_PUSH / JPH_SUPPRESS_WARNING_POP to store and restore the warning state -JPH_SUPPRESS_WARNINGS - -// All Jolt symbols are in the JPH namespace -using namespace JPH; - -// If you want your code to compile using single or double precision write 0.0_r to get a Real value that compiles to double or float depending if JPH_DOUBLE_PRECISION is set or not. -using namespace JPH::literals; - -// We're also using STL classes in this example -using namespace std; - -// Callback for traces, connect this to your own trace function if you have one -static void TraceImpl(const char *inFMT, ...) -{ - // Format the message - va_list list; - va_start(list, inFMT); - char buffer[1024]; - vsnprintf(buffer, sizeof(buffer), inFMT, list); - va_end(list); - - // Print to the TTY - cout << buffer << endl; -} - -#ifdef JPH_ENABLE_ASSERTS - -// Callback for asserts, connect this to your own assert handler if you have one -static bool AssertFailedImpl(const char *inExpression, const char *inMessage, const char *inFile, uint inLine) -{ - // Print to the TTY - cout << inFile << ":" << inLine << ": (" << inExpression << ") " << (inMessage != nullptr? inMessage : "") << endl; - - // Breakpoint - return true; -}; - -#endif // JPH_ENABLE_ASSERTS - -// Layer that objects can be in, determines which other objects it can collide with -// Typically you at least want to have 1 layer for moving bodies and 1 layer for static bodies, but you can have more -// layers if you want. E.g. you could have a layer for high detail collision (which is not used by the physics simulation -// but only if you do collision testing). -namespace Layers -{ - static constexpr ObjectLayer NON_MOVING = 0; - static constexpr ObjectLayer MOVING = 1; - static constexpr ObjectLayer NUM_LAYERS = 2; -}; - -/// Class that determines if two object layers can collide -class ObjectLayerPairFilterImpl : public ObjectLayerPairFilter -{ -public: - virtual bool ShouldCollide(ObjectLayer inObject1, ObjectLayer inObject2) const override - { - switch (inObject1) - { - case Layers::NON_MOVING: - return inObject2 == Layers::MOVING; // Non moving only collides with moving - case Layers::MOVING: - return true; // Moving collides with everything - default: - JPH_ASSERT(false); - return false; - } - } -}; - -// Each broadphase layer results in a separate bounding volume tree in the broad phase. You at least want to have -// a layer for non-moving and moving objects to avoid having to update a tree full of static objects every frame. -// You can have a 1-on-1 mapping between object layers and broadphase layers (like in this case) but if you have -// many object layers you'll be creating many broad phase trees, which is not efficient. If you want to fine tune -// your broadphase layers define JPH_TRACK_BROADPHASE_STATS and look at the stats reported on the TTY. -namespace BroadPhaseLayers -{ - static constexpr BroadPhaseLayer NON_MOVING(0); - static constexpr BroadPhaseLayer MOVING(1); - static constexpr uint NUM_LAYERS(2); -}; - -// BroadPhaseLayerInterface implementation -// This defines a mapping between object and broadphase layers. -class BPLayerInterfaceImpl final : public BroadPhaseLayerInterface -{ -public: - BPLayerInterfaceImpl() - { - // Create a mapping table from object to broad phase layer - mObjectToBroadPhase[Layers::NON_MOVING] = BroadPhaseLayers::NON_MOVING; - mObjectToBroadPhase[Layers::MOVING] = BroadPhaseLayers::MOVING; - } - - virtual uint GetNumBroadPhaseLayers() const override - { - return BroadPhaseLayers::NUM_LAYERS; - } - - virtual BroadPhaseLayer GetBroadPhaseLayer(ObjectLayer inLayer) const override - { - JPH_ASSERT(inLayer < Layers::NUM_LAYERS); - return mObjectToBroadPhase[inLayer]; - } - -#if defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED) - virtual const char * GetBroadPhaseLayerName(BroadPhaseLayer inLayer) const override - { - switch ((BroadPhaseLayer::Type)inLayer) - { - case (BroadPhaseLayer::Type)BroadPhaseLayers::NON_MOVING: return "NON_MOVING"; - case (BroadPhaseLayer::Type)BroadPhaseLayers::MOVING: return "MOVING"; - default: JPH_ASSERT(false); return "INVALID"; - } - } -#endif // JPH_EXTERNAL_PROFILE || JPH_PROFILE_ENABLED - -private: - BroadPhaseLayer mObjectToBroadPhase[Layers::NUM_LAYERS]; -}; - -/// Class that determines if an object layer can collide with a broadphase layer -class ObjectVsBroadPhaseLayerFilterImpl : public ObjectVsBroadPhaseLayerFilter -{ -public: - virtual bool ShouldCollide(ObjectLayer inLayer1, BroadPhaseLayer inLayer2) const override - { - switch (inLayer1) - { - case Layers::NON_MOVING: - return inLayer2 == BroadPhaseLayers::MOVING; - case Layers::MOVING: - return true; - default: - JPH_ASSERT(false); - return false; - } - } -}; - -// An example contact listener -class MyContactListener : public ContactListener -{ -public: - // See: ContactListener - virtual ValidateResult OnContactValidate(const Body &inBody1, const Body &inBody2, RVec3Arg inBaseOffset, const CollideShapeResult &inCollisionResult) override - { - cout << "Contact validate callback" << endl; - - // Allows you to ignore a contact before it is created (using layers to not make objects collide is cheaper!) - return ValidateResult::AcceptAllContactsForThisBodyPair; - } - - virtual void OnContactAdded(const Body &inBody1, const Body &inBody2, const ContactManifold &inManifold, ContactSettings &ioSettings) override - { - cout << "A contact was added" << endl; - } - - virtual void OnContactPersisted(const Body &inBody1, const Body &inBody2, const ContactManifold &inManifold, ContactSettings &ioSettings) override - { - cout << "A contact was persisted" << endl; - } - - virtual void OnContactRemoved(const SubShapeIDPair &inSubShapePair) override - { - cout << "A contact was removed" << endl; - } -}; - -// An example activation listener -class MyBodyActivationListener : public BodyActivationListener -{ -public: - virtual void OnBodyActivated(const BodyID &inBodyID, uint64 inBodyUserData) override - { - cout << "A body got activated" << endl; - } - - virtual void OnBodyDeactivated(const BodyID &inBodyID, uint64 inBodyUserData) override - { - cout << "A body went to sleep" << endl; - } -}; - -// Program entry point -int main(int argc, char** argv) -{ - // Register allocation hook. In this example we'll just let Jolt use malloc / free but you can override these if you want (see Memory.h). - // This needs to be done before any other Jolt function is called. - RegisterDefaultAllocator(); - - // Install trace and assert callbacks - Trace = TraceImpl; - JPH_IF_ENABLE_ASSERTS(AssertFailed = AssertFailedImpl;) - - // Create a factory, this class is responsible for creating instances of classes based on their name or hash and is mainly used for deserialization of saved data. - // It is not directly used in this example but still required. - Factory::sInstance = new Factory(); - - // Register all physics types with the factory and install their collision handlers with the CollisionDispatch class. - // If you have your own custom shape types you probably need to register their handlers with the CollisionDispatch before calling this function. - // If you implement your own default material (PhysicsMaterial::sDefault) make sure to initialize it before this function or else this function will create one for you. - RegisterTypes(); - - // We need a temp allocator for temporary allocations during the physics update. We're - // pre-allocating 10 MB to avoid having to do allocations during the physics update. - // B.t.w. 10 MB is way too much for this example but it is a typical value you can use. - // If you don't want to pre-allocate you can also use TempAllocatorMalloc to fall back to - // malloc / free. - TempAllocatorImpl temp_allocator(10 * 1024 * 1024); - - // We need a job system that will execute physics jobs on multiple threads. Typically - // you would implement the JobSystem interface yourself and let Jolt Physics run on top - // of your own job scheduler. JobSystemThreadPool is an example implementation. - JobSystemThreadPool job_system(cMaxPhysicsJobs, cMaxPhysicsBarriers, thread::hardware_concurrency() - 1); - - // This is the max amount of rigid bodies that you can add to the physics system. If you try to add more you'll get an error. - // Note: This value is low because this is a simple test. For a real project use something in the order of 65536. - const uint cMaxBodies = 1024; - - // This determines how many mutexes to allocate to protect rigid bodies from concurrent access. Set it to 0 for the default settings. - const uint cNumBodyMutexes = 0; - - // This is the max amount of body pairs that can be queued at any time (the broad phase will detect overlapping - // body pairs based on their bounding boxes and will insert them into a queue for the narrowphase). If you make this buffer - // too small the queue will fill up and the broad phase jobs will start to do narrow phase work. This is slightly less efficient. - // Note: This value is low because this is a simple test. For a real project use something in the order of 65536. - const uint cMaxBodyPairs = 1024; - - // This is the maximum size of the contact constraint buffer. If more contacts (collisions between bodies) are detected than this - // number then these contacts will be ignored and bodies will start interpenetrating / fall through the world. - // Note: This value is low because this is a simple test. For a real project use something in the order of 10240. - const uint cMaxContactConstraints = 1024; - - // Create mapping table from object layer to broadphase layer - // Note: As this is an interface, PhysicsSystem will take a reference to this so this instance needs to stay alive! - BPLayerInterfaceImpl broad_phase_layer_interface; - - // Create class that filters object vs broadphase layers - // Note: As this is an interface, PhysicsSystem will take a reference to this so this instance needs to stay alive! - ObjectVsBroadPhaseLayerFilterImpl object_vs_broadphase_layer_filter; - - // Create class that filters object vs object layers - // Note: As this is an interface, PhysicsSystem will take a reference to this so this instance needs to stay alive! - ObjectLayerPairFilterImpl object_vs_object_layer_filter; - - // Now we can create the actual physics system. - PhysicsSystem physics_system; - physics_system.Init(cMaxBodies, cNumBodyMutexes, cMaxBodyPairs, cMaxContactConstraints, broad_phase_layer_interface, object_vs_broadphase_layer_filter, object_vs_object_layer_filter); - - // A body activation listener gets notified when bodies activate and go to sleep - // Note that this is called from a job so whatever you do here needs to be thread safe. - // Registering one is entirely optional. - MyBodyActivationListener body_activation_listener; - physics_system.SetBodyActivationListener(&body_activation_listener); - - // A contact listener gets notified when bodies (are about to) collide, and when they separate again. - // Note that this is called from a job so whatever you do here needs to be thread safe. - // Registering one is entirely optional. - MyContactListener contact_listener; - physics_system.SetContactListener(&contact_listener); - - // The main way to interact with the bodies in the physics system is through the body interface. There is a locking and a non-locking - // variant of this. We're going to use the locking version (even though we're not planning to access bodies from multiple threads) - BodyInterface &body_interface = physics_system.GetBodyInterface(); - - // Next we can create a rigid body to serve as the floor, we make a large box - // Create the settings for the collision volume (the shape). - // Note that for simple shapes (like boxes) you can also directly construct a BoxShape. - BoxShapeSettings floor_shape_settings(Vec3(100.0f, 1.0f, 100.0f)); - - // Create the shape - ShapeSettings::ShapeResult floor_shape_result = floor_shape_settings.Create(); - ShapeRefC floor_shape = floor_shape_result.Get(); // We don't expect an error here, but you can check floor_shape_result for HasError() / GetError() - - // Create the settings for the body itself. Note that here you can also set other properties like the restitution / friction. - BodyCreationSettings floor_settings(floor_shape, RVec3(0.0_r, -1.0_r, 0.0_r), Quat::sIdentity(), EMotionType::Static, Layers::NON_MOVING); - - // Create the actual rigid body - Body *floor = body_interface.CreateBody(floor_settings); // Note that if we run out of bodies this can return nullptr - - // Add it to the world - body_interface.AddBody(floor->GetID(), EActivation::DontActivate); - - // Now create a dynamic body to bounce on the floor - // Note that this uses the shorthand version of creating and adding a body to the world - BodyCreationSettings sphere_settings(new SphereShape(0.5f), RVec3(0.0_r, 2.0_r, 0.0_r), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING); - BodyID sphere_id = body_interface.CreateAndAddBody(sphere_settings, EActivation::Activate); - - // Now you can interact with the dynamic body, in this case we're going to give it a velocity. - // (note that if we had used CreateBody then we could have set the velocity straight on the body before adding it to the physics system) - body_interface.SetLinearVelocity(sphere_id, Vec3(0.0f, -5.0f, 0.0f)); - - // We simulate the physics world in discrete time steps. 60 Hz is a good rate to update the physics system. - const float cDeltaTime = 1.0f / 60.0f; - - // Optional step: Before starting the physics simulation you can optimize the broad phase. This improves collision detection performance (it's pointless here because we only have 2 bodies). - // You should definitely not call this every frame or when e.g. streaming in a new level section as it is an expensive operation. - // Instead insert all new objects in batches instead of 1 at a time to keep the broad phase efficient. - physics_system.OptimizeBroadPhase(); - - // Now we're ready to simulate the body, keep simulating until it goes to sleep - uint step = 0; - while (body_interface.IsActive(sphere_id)) - { - // Next step - ++step; - - // Output current position and velocity of the sphere - RVec3 position = body_interface.GetCenterOfMassPosition(sphere_id); - Vec3 velocity = body_interface.GetLinearVelocity(sphere_id); - cout << "Step " << step << ": Position = (" << position.GetX() << ", " << position.GetY() << ", " << position.GetZ() << "), Velocity = (" << velocity.GetX() << ", " << velocity.GetY() << ", " << velocity.GetZ() << ")" << endl; - - // If you take larger steps than 1 / 60th of a second you need to do multiple collision steps in order to keep the simulation stable. Do 1 collision step per 1 / 60th of a second (round up). - const int cCollisionSteps = 1; - - // Step the world - physics_system.Update(cDeltaTime, cCollisionSteps, &temp_allocator, &job_system); - } - - // Remove the sphere from the physics system. Note that the sphere itself keeps all of its state and can be re-added at any time. - body_interface.RemoveBody(sphere_id); - - // Destroy the sphere. After this the sphere ID is no longer valid. - body_interface.DestroyBody(sphere_id); - - // Remove and destroy the floor - body_interface.RemoveBody(floor->GetID()); - body_interface.DestroyBody(floor->GetID()); - - // Unregisters all types with the factory and cleans up the default material - UnregisterTypes(); - - // Destroy the factory - delete Factory::sInstance; - Factory::sInstance = nullptr; - - return 0; -} diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/AABBTree/AABBTreeBuilder.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/AABBTree/AABBTreeBuilder.cpp deleted file mode 100644 index 7e4f0622aa2..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/AABBTree/AABBTreeBuilder.cpp +++ /dev/null @@ -1,225 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include - -JPH_NAMESPACE_BEGIN - -AABBTreeBuilder::Node::Node() -{ - mChild[0] = nullptr; - mChild[1] = nullptr; -} - -AABBTreeBuilder::Node::~Node() -{ - delete mChild[0]; - delete mChild[1]; -} - -uint AABBTreeBuilder::Node::GetMinDepth() const -{ - if (HasChildren()) - { - uint left = mChild[0]->GetMinDepth(); - uint right = mChild[1]->GetMinDepth(); - return min(left, right) + 1; - } - else - return 1; -} - -uint AABBTreeBuilder::Node::GetMaxDepth() const -{ - if (HasChildren()) - { - uint left = mChild[0]->GetMaxDepth(); - uint right = mChild[1]->GetMaxDepth(); - return max(left, right) + 1; - } - else - return 1; -} - -uint AABBTreeBuilder::Node::GetNodeCount() const -{ - if (HasChildren()) - return mChild[0]->GetNodeCount() + mChild[1]->GetNodeCount() + 1; - else - return 1; -} - -uint AABBTreeBuilder::Node::GetLeafNodeCount() const -{ - if (HasChildren()) - return mChild[0]->GetLeafNodeCount() + mChild[1]->GetLeafNodeCount(); - else - return 1; -} - -uint AABBTreeBuilder::Node::GetTriangleCountInTree() const -{ - if (HasChildren()) - return mChild[0]->GetTriangleCountInTree() + mChild[1]->GetTriangleCountInTree(); - else - return GetTriangleCount(); -} - -void AABBTreeBuilder::Node::GetTriangleCountPerNode(float &outAverage, uint &outMin, uint &outMax) const -{ - outMin = INT_MAX; - outMax = 0; - outAverage = 0; - uint avg_divisor = 0; - GetTriangleCountPerNodeInternal(outAverage, avg_divisor, outMin, outMax); - if (avg_divisor > 0) - outAverage /= avg_divisor; -} - -float AABBTreeBuilder::Node::CalculateSAHCost(float inCostTraversal, float inCostLeaf) const -{ - float surface_area = mBounds.GetSurfaceArea(); - return surface_area > 0.0f? CalculateSAHCostInternal(inCostTraversal / surface_area, inCostLeaf / surface_area) : 0.0f; -} - -void AABBTreeBuilder::Node::GetNChildren(uint inN, Array &outChildren) const -{ - JPH_ASSERT(outChildren.empty()); - - // Check if there is anything to expand - if (!HasChildren()) - return; - - // Start with the children of this node - outChildren.push_back(mChild[0]); - outChildren.push_back(mChild[1]); - - size_t next = 0; - bool all_triangles = true; - while (outChildren.size() < inN) - { - // If we have looped over all nodes, start over with the first node again - if (next >= outChildren.size()) - { - // If there only triangle nodes left, we have to terminate - if (all_triangles) - return; - next = 0; - all_triangles = true; - } - - // Try to expand this node into its two children - const Node *to_expand = outChildren[next]; - if (to_expand->HasChildren()) - { - outChildren.erase(outChildren.begin() + next); - outChildren.push_back(to_expand->mChild[0]); - outChildren.push_back(to_expand->mChild[1]); - all_triangles = false; - } - else - { - ++next; - } - } -} - -float AABBTreeBuilder::Node::CalculateSAHCostInternal(float inCostTraversalDivSurfaceArea, float inCostLeafDivSurfaceArea) const -{ - if (HasChildren()) - return inCostTraversalDivSurfaceArea * mBounds.GetSurfaceArea() - + mChild[0]->CalculateSAHCostInternal(inCostTraversalDivSurfaceArea, inCostLeafDivSurfaceArea) - + mChild[1]->CalculateSAHCostInternal(inCostTraversalDivSurfaceArea, inCostLeafDivSurfaceArea); - else - return inCostLeafDivSurfaceArea * mBounds.GetSurfaceArea() * GetTriangleCount(); -} - -void AABBTreeBuilder::Node::GetTriangleCountPerNodeInternal(float &outAverage, uint &outAverageDivisor, uint &outMin, uint &outMax) const -{ - if (HasChildren()) - { - mChild[0]->GetTriangleCountPerNodeInternal(outAverage, outAverageDivisor, outMin, outMax); - mChild[1]->GetTriangleCountPerNodeInternal(outAverage, outAverageDivisor, outMin, outMax); - } - else - { - outAverage += GetTriangleCount(); - outAverageDivisor++; - outMin = min(outMin, GetTriangleCount()); - outMax = max(outMax, GetTriangleCount()); - } -} - -AABBTreeBuilder::AABBTreeBuilder(TriangleSplitter &inSplitter, uint inMaxTrianglesPerLeaf) : - mTriangleSplitter(inSplitter), - mMaxTrianglesPerLeaf(inMaxTrianglesPerLeaf) -{ -} - -AABBTreeBuilder::Node *AABBTreeBuilder::Build(AABBTreeBuilderStats &outStats) -{ - TriangleSplitter::Range initial = mTriangleSplitter.GetInitialRange(); - Node *root = BuildInternal(initial); - - float avg_triangles_per_leaf; - uint min_triangles_per_leaf, max_triangles_per_leaf; - root->GetTriangleCountPerNode(avg_triangles_per_leaf, min_triangles_per_leaf, max_triangles_per_leaf); - - mTriangleSplitter.GetStats(outStats.mSplitterStats); - - outStats.mSAHCost = root->CalculateSAHCost(1.0f, 1.0f); - outStats.mMinDepth = root->GetMinDepth(); - outStats.mMaxDepth = root->GetMaxDepth(); - outStats.mNodeCount = root->GetNodeCount(); - outStats.mLeafNodeCount = root->GetLeafNodeCount(); - outStats.mMaxTrianglesPerLeaf = mMaxTrianglesPerLeaf; - outStats.mTreeMinTrianglesPerLeaf = min_triangles_per_leaf; - outStats.mTreeMaxTrianglesPerLeaf = max_triangles_per_leaf; - outStats.mTreeAvgTrianglesPerLeaf = avg_triangles_per_leaf; - - return root; -} - -AABBTreeBuilder::Node *AABBTreeBuilder::BuildInternal(const TriangleSplitter::Range &inTriangles) -{ - // Check if there are too many triangles left - if (inTriangles.Count() > mMaxTrianglesPerLeaf) - { - // Split triangles in two batches - TriangleSplitter::Range left, right; - if (!mTriangleSplitter.Split(inTriangles, left, right)) - { - JPH_IF_DEBUG(Trace("AABBTreeBuilder: Doing random split for %d triangles (max per node: %u)!", (int)inTriangles.Count(), mMaxTrianglesPerLeaf);) - int half = inTriangles.Count() / 2; - JPH_ASSERT(half > 0); - left = TriangleSplitter::Range(inTriangles.mBegin, inTriangles.mBegin + half); - right = TriangleSplitter::Range(inTriangles.mBegin + half, inTriangles.mEnd); - } - - // Recursively build - Node *node = new Node(); - node->mChild[0] = BuildInternal(left); - node->mChild[1] = BuildInternal(right); - node->mBounds = node->mChild[0]->mBounds; - node->mBounds.Encapsulate(node->mChild[1]->mBounds); - return node; - } - - // Create leaf node - Node *node = new Node(); - node->mTriangles.reserve(inTriangles.Count()); - for (uint i = inTriangles.mBegin; i < inTriangles.mEnd; ++i) - { - const IndexedTriangle &t = mTriangleSplitter.GetTriangle(i); - const VertexList &v = mTriangleSplitter.GetVertices(); - node->mTriangles.push_back(t); - node->mBounds.Encapsulate(v, t); - } - - return node; -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/AABBTree/AABBTreeBuilder.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/AABBTree/AABBTreeBuilder.h deleted file mode 100644 index dacae5e07ca..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/AABBTree/AABBTreeBuilder.h +++ /dev/null @@ -1,110 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -struct AABBTreeBuilderStats -{ - ///@name Splitter stats - TriangleSplitter::Stats mSplitterStats; ///< Stats returned by the triangle splitter algorithm - - ///@name Tree structure - float mSAHCost = 0.0f; ///< Surface Area Heuristic cost of this tree - int mMinDepth = 0; ///< Minimal depth of tree (number of nodes) - int mMaxDepth = 0; ///< Maximum depth of tree (number of nodes) - int mNodeCount = 0; ///< Number of nodes in the tree - int mLeafNodeCount = 0; ///< Number of leaf nodes (that contain triangles) - - ///@name Configured stats - int mMaxTrianglesPerLeaf = 0; ///< Configured max triangles per leaf - - ///@name Actual stats - int mTreeMinTrianglesPerLeaf = 0; ///< Minimal amount of triangles in a leaf - int mTreeMaxTrianglesPerLeaf = 0; ///< Maximal amount of triangles in a leaf - float mTreeAvgTrianglesPerLeaf = 0.0f; ///< Average amount of triangles in leaf nodes -}; - -/// Helper class to build an AABB tree -class JPH_EXPORT AABBTreeBuilder -{ -public: - /// A node in the tree, contains the AABox for the tree and any child nodes or triangles - class Node : public NonCopyable - { - public: - JPH_OVERRIDE_NEW_DELETE - - /// Constructor - Node(); - ~Node(); - - /// Get number of triangles in this node - inline uint GetTriangleCount() const { return uint(mTriangles.size()); } - - /// Check if this node has any children - inline bool HasChildren() const { return mChild[0] != nullptr || mChild[1] != nullptr; } - - /// Min depth of tree - uint GetMinDepth() const; - - /// Max depth of tree - uint GetMaxDepth() const; - - /// Number of nodes in tree - uint GetNodeCount() const; - - /// Number of leaf nodes in tree - uint GetLeafNodeCount() const; - - /// Get triangle count in tree - uint GetTriangleCountInTree() const; - - /// Calculate min and max triangles per node - void GetTriangleCountPerNode(float &outAverage, uint &outMin, uint &outMax) const; - - /// Calculate the total cost of the tree using the surface area heuristic - float CalculateSAHCost(float inCostTraversal, float inCostLeaf) const; - - /// Recursively get children (breadth first) to get in total inN children (or less if there are no more) - void GetNChildren(uint inN, Array &outChildren) const; - - /// Bounding box - AABox mBounds; - - /// Triangles (if no child nodes) - IndexedTriangleList mTriangles; - - /// Child nodes (if no triangles) - Node * mChild[2]; - - private: - friend class AABBTreeBuilder; - - /// Recursive helper function to calculate cost of the tree - float CalculateSAHCostInternal(float inCostTraversalDivSurfaceArea, float inCostLeafDivSurfaceArea) const; - - /// Recursive helper function to calculate min and max triangles per node - void GetTriangleCountPerNodeInternal(float &outAverage, uint &outAverageDivisor, uint &outMin, uint &outMax) const; - }; - - /// Constructor - AABBTreeBuilder(TriangleSplitter &inSplitter, uint inMaxTrianglesPerLeaf = 16); - - /// Recursively build tree, returns the root node of the tree - Node * Build(AABBTreeBuilderStats &outStats); - -private: - Node * BuildInternal(const TriangleSplitter::Range &inTriangles); - - TriangleSplitter & mTriangleSplitter; - const uint mMaxTrianglesPerLeaf; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/AABBTree/AABBTreeToBuffer.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/AABBTree/AABBTreeToBuffer.h deleted file mode 100644 index 1fa9bb7aec0..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/AABBTree/AABBTreeToBuffer.h +++ /dev/null @@ -1,245 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include - -JPH_SUPPRESS_WARNINGS_STD_BEGIN -#include -JPH_SUPPRESS_WARNINGS_STD_END - -JPH_NAMESPACE_BEGIN - -template using Deque = std::deque>; - -/// Conversion algorithm that converts an AABB tree to an optimized binary buffer -template -class AABBTreeToBuffer -{ -public: - /// Header for the tree - using NodeHeader = typename NodeCodec::Header; - - /// Size in bytes of the header of the tree - static const int HeaderSize = NodeCodec::HeaderSize; - - /// Maximum number of children per node in the tree - static const int NumChildrenPerNode = NodeCodec::NumChildrenPerNode; - - /// Header for the triangles - using TriangleHeader = typename TriangleCodec::TriangleHeader; - - /// Size in bytes of the header for the triangles - static const int TriangleHeaderSize = TriangleCodec::TriangleHeaderSize; - - /// Convert AABB tree. Returns false if failed. - bool Convert(const VertexList &inVertices, const AABBTreeBuilder::Node *inRoot, const char *&outError) - { - const typename NodeCodec::EncodingContext node_ctx; - typename TriangleCodec::EncodingContext tri_ctx(inVertices); - - // Estimate the amount of memory required - uint tri_count = inRoot->GetTriangleCountInTree(); - uint node_count = inRoot->GetNodeCount(); - uint nodes_size = node_ctx.GetPessimisticMemoryEstimate(node_count); - uint total_size = HeaderSize + TriangleHeaderSize + nodes_size + tri_ctx.GetPessimisticMemoryEstimate(tri_count); - mTree.reserve(total_size); - - // Reset counters - mNodesSize = 0; - - // Add headers - NodeHeader *header = HeaderSize > 0? mTree.Allocate() : nullptr; - TriangleHeader *triangle_header = TriangleHeaderSize > 0? mTree.Allocate() : nullptr; - - struct NodeData - { - const AABBTreeBuilder::Node * mNode = nullptr; // Node that this entry belongs to - Vec3 mNodeBoundsMin; // Quantized node bounds - Vec3 mNodeBoundsMax; - uint mNodeStart = uint(-1); // Start of node in mTree - uint mTriangleStart = uint(-1); // Start of the triangle data in mTree - uint mNumChildren = 0; // Number of children - uint mChildNodeStart[NumChildrenPerNode]; // Start of the children of the node in mTree - uint mChildTrianglesStart[NumChildrenPerNode]; // Start of the triangle data in mTree - uint * mParentChildNodeStart = nullptr; // Where to store mNodeStart (to patch mChildNodeStart of my parent) - uint * mParentTrianglesStart = nullptr; // Where to store mTriangleStart (to patch mChildTrianglesStart of my parent) - }; - - Deque to_process; - Deque to_process_triangles; - Array node_list; - - node_list.reserve(node_count); // Needed to ensure that array is not reallocated, so we can keep pointers in the array - - NodeData root; - root.mNode = inRoot; - root.mNodeBoundsMin = inRoot->mBounds.mMin; - root.mNodeBoundsMax = inRoot->mBounds.mMax; - node_list.push_back(root); - to_process.push_back(&node_list.back()); - - // Child nodes out of loop so we don't constantly realloc it - Array child_nodes; - child_nodes.reserve(NumChildrenPerNode); - - for (;;) - { - while (!to_process.empty()) - { - // Get the next node to process - NodeData *node_data = to_process.back(); - to_process.pop_back(); - - // Due to quantization box could have become bigger, not smaller - JPH_ASSERT(AABox(node_data->mNodeBoundsMin, node_data->mNodeBoundsMax).Contains(node_data->mNode->mBounds), "AABBTreeToBuffer: Bounding box became smaller!"); - - // Collect the first NumChildrenPerNode sub-nodes in the tree - child_nodes.clear(); // Won't free the memory - node_data->mNode->GetNChildren(NumChildrenPerNode, child_nodes); - node_data->mNumChildren = (uint)child_nodes.size(); - - // Fill in default child bounds - Vec3 child_bounds_min[NumChildrenPerNode], child_bounds_max[NumChildrenPerNode]; - for (size_t i = 0; i < NumChildrenPerNode; ++i) - if (i < child_nodes.size()) - { - child_bounds_min[i] = child_nodes[i]->mBounds.mMin; - child_bounds_max[i] = child_nodes[i]->mBounds.mMax; - } - else - { - child_bounds_min[i] = Vec3::sZero(); - child_bounds_max[i] = Vec3::sZero(); - } - - // Start a new node - uint old_size = (uint)mTree.size(); - node_data->mNodeStart = node_ctx.NodeAllocate(node_data->mNode, node_data->mNodeBoundsMin, node_data->mNodeBoundsMax, child_nodes, child_bounds_min, child_bounds_max, mTree, outError); - if (node_data->mNodeStart == uint(-1)) - return false; - mNodesSize += (uint)mTree.size() - old_size; - - if (node_data->mNode->HasChildren()) - { - // Insert in reverse order so we process left child first when taking nodes from the back - for (int idx = int(child_nodes.size()) - 1; idx >= 0; --idx) - { - // Due to quantization box could have become bigger, not smaller - JPH_ASSERT(AABox(child_bounds_min[idx], child_bounds_max[idx]).Contains(child_nodes[idx]->mBounds), "AABBTreeToBuffer: Bounding box became smaller!"); - - // Add child to list of nodes to be processed - NodeData child; - child.mNode = child_nodes[idx]; - child.mNodeBoundsMin = child_bounds_min[idx]; - child.mNodeBoundsMax = child_bounds_max[idx]; - child.mParentChildNodeStart = &node_data->mChildNodeStart[idx]; - child.mParentTrianglesStart = &node_data->mChildTrianglesStart[idx]; - NodeData *old = &node_list[0]; - node_list.push_back(child); - if (old != &node_list[0]) - { - outError = "Internal Error: Array reallocated, memory corruption!"; - return false; - } - - // Store triangles in separate list so we process them last - if (node_list.back().mNode->HasChildren()) - to_process.push_back(&node_list.back()); - else - to_process_triangles.push_back(&node_list.back()); - } - } - else - { - // Add triangles - node_data->mTriangleStart = tri_ctx.Pack(node_data->mNode->mTriangles, mTree, outError); - if (node_data->mTriangleStart == uint(-1)) - return false; - } - - // Patch offset into parent - if (node_data->mParentChildNodeStart != nullptr) - { - *node_data->mParentChildNodeStart = node_data->mNodeStart; - *node_data->mParentTrianglesStart = node_data->mTriangleStart; - } - } - - // If we've got triangles to process, loop again with just the triangles - if (to_process_triangles.empty()) - break; - else - to_process.swap(to_process_triangles); - } - - // Finalize all nodes - for (NodeData &n : node_list) - if (!node_ctx.NodeFinalize(n.mNode, n.mNodeStart, n.mNumChildren, n.mChildNodeStart, n.mChildTrianglesStart, mTree, outError)) - return false; - - // Finalize the triangles - tri_ctx.Finalize(inVertices, triangle_header, mTree); - - // Validate that we reserved enough memory - if (nodes_size < mNodesSize) - { - outError = "Internal Error: Not enough memory reserved for nodes!"; - return false; - } - if (total_size < (uint)mTree.size()) - { - outError = "Internal Error: Not enough memory reserved for triangles!"; - return false; - } - - // Finalize the nodes - if (!node_ctx.Finalize(header, inRoot, node_list[0].mNodeStart, node_list[0].mTriangleStart, outError)) - return false; - - // Shrink the tree, this will invalidate the header and triangle_header variables - mTree.shrink_to_fit(); - - return true; - } - - /// Get resulting data - inline const ByteBuffer & GetBuffer() const - { - return mTree; - } - - /// Get resulting data - inline ByteBuffer & GetBuffer() - { - return mTree; - } - - /// Get header for tree - inline const NodeHeader * GetNodeHeader() const - { - return mTree.Get(0); - } - - /// Get header for triangles - inline const TriangleHeader * GetTriangleHeader() const - { - return mTree.Get(HeaderSize); - } - - /// Get root of resulting tree - inline const void * GetRoot() const - { - return mTree.Get(HeaderSize + TriangleHeaderSize); - } - -private: - ByteBuffer mTree; ///< Resulting tree structure - uint mNodesSize; ///< Size in bytes of the nodes in the buffer -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/AABBTree/NodeCodec/NodeCodecQuadTreeHalfFloat.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/AABBTree/NodeCodec/NodeCodecQuadTreeHalfFloat.h deleted file mode 100644 index e5376ea82dd..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/AABBTree/NodeCodec/NodeCodecQuadTreeHalfFloat.h +++ /dev/null @@ -1,287 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -template -class NodeCodecQuadTreeHalfFloat -{ -public: - /// Number of child nodes of this node - static constexpr int NumChildrenPerNode = 4; - - /// Header for the tree - struct Header - { - Float3 mRootBoundsMin; - Float3 mRootBoundsMax; - uint32 mRootProperties; - }; - - /// Size of the header (an empty struct is always > 0 bytes so this needs a separate variable) - static constexpr int HeaderSize = sizeof(Header); - - /// Stack size to use during DecodingContext::sWalkTree - static constexpr int StackSize = 128; - - /// Node properties - enum : uint32 - { - TRIANGLE_COUNT_BITS = 4, - TRIANGLE_COUNT_SHIFT = 28, - TRIANGLE_COUNT_MASK = (1 << TRIANGLE_COUNT_BITS) - 1, - OFFSET_BITS = 28, - OFFSET_MASK = (1 << OFFSET_BITS) - 1, - OFFSET_NON_SIGNIFICANT_BITS = 2, - OFFSET_NON_SIGNIFICANT_MASK = (1 << OFFSET_NON_SIGNIFICANT_BITS) - 1, - }; - - /// Node structure - struct Node - { - HalfFloat mBoundsMinX[4]; ///< 4 child bounding boxes - HalfFloat mBoundsMinY[4]; - HalfFloat mBoundsMinZ[4]; - HalfFloat mBoundsMaxX[4]; - HalfFloat mBoundsMaxY[4]; - HalfFloat mBoundsMaxZ[4]; - uint32 mNodeProperties[4]; ///< 4 child node properties - }; - - static_assert(sizeof(Node) == 64, "Node should be 64 bytes"); - - /// This class encodes and compresses quad tree nodes - class EncodingContext - { - public: - /// Get an upper bound on the amount of bytes needed for a node tree with inNodeCount nodes - uint GetPessimisticMemoryEstimate(uint inNodeCount) const - { - return inNodeCount * (sizeof(Node) + Alignment - 1); - } - - /// Allocate a new node for inNode. - /// Algorithm can modify the order of ioChildren to indicate in which order children should be compressed - /// Algorithm can enlarge the bounding boxes of the children during compression and returns these in outChildBoundsMin, outChildBoundsMax - /// inNodeBoundsMin, inNodeBoundsMax is the bounding box if inNode possibly widened by compressing the parent node - /// Returns uint(-1) on error and reports the error in outError - uint NodeAllocate(const AABBTreeBuilder::Node *inNode, Vec3Arg inNodeBoundsMin, Vec3Arg inNodeBoundsMax, Array &ioChildren, Vec3 outChildBoundsMin[NumChildrenPerNode], Vec3 outChildBoundsMax[NumChildrenPerNode], ByteBuffer &ioBuffer, const char *&outError) const - { - // We don't emit nodes for leafs - if (!inNode->HasChildren()) - return (uint)ioBuffer.size(); - - // Align the buffer - ioBuffer.Align(Alignment); - uint node_start = (uint)ioBuffer.size(); - - // Fill in bounds - Node *node = ioBuffer.Allocate(); - - for (size_t i = 0; i < 4; ++i) - { - if (i < ioChildren.size()) - { - const AABBTreeBuilder::Node *this_node = ioChildren[i]; - - // Copy bounding box - node->mBoundsMinX[i] = HalfFloatConversion::FromFloat(this_node->mBounds.mMin.GetX()); - node->mBoundsMinY[i] = HalfFloatConversion::FromFloat(this_node->mBounds.mMin.GetY()); - node->mBoundsMinZ[i] = HalfFloatConversion::FromFloat(this_node->mBounds.mMin.GetZ()); - node->mBoundsMaxX[i] = HalfFloatConversion::FromFloat(this_node->mBounds.mMax.GetX()); - node->mBoundsMaxY[i] = HalfFloatConversion::FromFloat(this_node->mBounds.mMax.GetY()); - node->mBoundsMaxZ[i] = HalfFloatConversion::FromFloat(this_node->mBounds.mMax.GetZ()); - - // Store triangle count - node->mNodeProperties[i] = this_node->GetTriangleCount() << TRIANGLE_COUNT_SHIFT; - if (this_node->GetTriangleCount() >= TRIANGLE_COUNT_MASK) - { - outError = "NodeCodecQuadTreeHalfFloat: Too many triangles"; - return uint(-1); - } - } - else - { - // Make this an invalid triangle node - node->mNodeProperties[i] = uint32(TRIANGLE_COUNT_MASK) << TRIANGLE_COUNT_SHIFT; - - // Make bounding box invalid - node->mBoundsMinX[i] = HALF_FLT_MAX; - node->mBoundsMinY[i] = HALF_FLT_MAX; - node->mBoundsMinZ[i] = HALF_FLT_MAX; - node->mBoundsMaxX[i] = HALF_FLT_MAX; - node->mBoundsMaxY[i] = HALF_FLT_MAX; - node->mBoundsMaxZ[i] = HALF_FLT_MAX; - } - } - - // Since we don't keep track of the bounding box while descending the tree, we keep the root bounds at all levels for triangle compression - for (int i = 0; i < NumChildrenPerNode; ++i) - { - outChildBoundsMin[i] = inNodeBoundsMin; - outChildBoundsMax[i] = inNodeBoundsMax; - } - - return node_start; - } - - /// Once all nodes have been added, this call finalizes all nodes by patching in the offsets of the child nodes (that were added after the node itself was added) - bool NodeFinalize(const AABBTreeBuilder::Node *inNode, uint inNodeStart, uint inNumChildren, const uint *inChildrenNodeStart, const uint *inChildrenTrianglesStart, ByteBuffer &ioBuffer, const char *&outError) const - { - if (!inNode->HasChildren()) - return true; - - Node *node = ioBuffer.Get(inNodeStart); - for (uint i = 0; i < inNumChildren; ++i) - { - // If there are triangles, use the triangle offset otherwise use the node offset - uint offset = node->mNodeProperties[i] != 0? inChildrenTrianglesStart[i] : inChildrenNodeStart[i]; - if (offset & OFFSET_NON_SIGNIFICANT_MASK) - { - outError = "NodeCodecQuadTreeHalfFloat: Internal Error: Offset has non-significant bits set"; - return false; - } - offset >>= OFFSET_NON_SIGNIFICANT_BITS; - if (offset & ~OFFSET_MASK) - { - outError = "NodeCodecQuadTreeHalfFloat: Offset too large. Too much data."; - return false; - } - - // Store offset of next node / triangles - node->mNodeProperties[i] |= offset; - } - - return true; - } - - /// Once all nodes have been finalized, this will finalize the header of the nodes - bool Finalize(Header *outHeader, const AABBTreeBuilder::Node *inRoot, uint inRootNodeStart, uint inRootTrianglesStart, const char *&outError) const - { - uint offset = inRoot->HasChildren()? inRootNodeStart : inRootTrianglesStart; - if (offset & OFFSET_NON_SIGNIFICANT_MASK) - { - outError = "NodeCodecQuadTreeHalfFloat: Internal Error: Offset has non-significant bits set"; - return false; - } - offset >>= OFFSET_NON_SIGNIFICANT_BITS; - if (offset & ~OFFSET_MASK) - { - outError = "NodeCodecQuadTreeHalfFloat: Offset too large. Too much data."; - return false; - } - - inRoot->mBounds.mMin.StoreFloat3(&outHeader->mRootBoundsMin); - inRoot->mBounds.mMax.StoreFloat3(&outHeader->mRootBoundsMax); - outHeader->mRootProperties = offset + (inRoot->GetTriangleCount() << TRIANGLE_COUNT_SHIFT); - if (inRoot->GetTriangleCount() >= TRIANGLE_COUNT_MASK) - { - outError = "NodeCodecQuadTreeHalfFloat: Too many triangles"; - return false; - } - - return true; - } - }; - - /// This class decodes and decompresses quad tree nodes - class DecodingContext - { - public: - /// Get the amount of bits needed to store an ID to a triangle block - inline static uint sTriangleBlockIDBits(const ByteBuffer &inTree) - { - return 32 - CountLeadingZeros((uint32)inTree.size()) - OFFSET_NON_SIGNIFICANT_BITS; - } - - /// Convert a triangle block ID to the start of the triangle buffer - inline static const void * sGetTriangleBlockStart(const uint8 *inBufferStart, uint inTriangleBlockID) - { - return inBufferStart + (inTriangleBlockID << OFFSET_NON_SIGNIFICANT_BITS); - } - - /// Constructor - JPH_INLINE explicit DecodingContext(const Header *inHeader) - { - // Start with the root node on the stack - mNodeStack[0] = inHeader->mRootProperties; - } - - /// Walk the node tree calling the Visitor::VisitNodes for each node encountered and Visitor::VisitTriangles for each triangle encountered - template - JPH_INLINE void WalkTree(const uint8 *inBufferStart, const TriangleContext &inTriangleContext, Visitor &ioVisitor) - { - do - { - // Test if node contains triangles - uint32 node_properties = mNodeStack[mTop]; - uint32 tri_count = node_properties >> TRIANGLE_COUNT_SHIFT; - if (tri_count == 0) - { - const Node *node = reinterpret_cast(inBufferStart + (node_properties << OFFSET_NON_SIGNIFICANT_BITS)); - - // Unpack bounds - UVec4 bounds_minxy = UVec4::sLoadInt4(reinterpret_cast(&node->mBoundsMinX[0])); - Vec4 bounds_minx = HalfFloatConversion::ToFloat(bounds_minxy); - Vec4 bounds_miny = HalfFloatConversion::ToFloat(bounds_minxy.Swizzle()); - - UVec4 bounds_minzmaxx = UVec4::sLoadInt4(reinterpret_cast(&node->mBoundsMinZ[0])); - Vec4 bounds_minz = HalfFloatConversion::ToFloat(bounds_minzmaxx); - Vec4 bounds_maxx = HalfFloatConversion::ToFloat(bounds_minzmaxx.Swizzle()); - - UVec4 bounds_maxyz = UVec4::sLoadInt4(reinterpret_cast(&node->mBoundsMaxY[0])); - Vec4 bounds_maxy = HalfFloatConversion::ToFloat(bounds_maxyz); - Vec4 bounds_maxz = HalfFloatConversion::ToFloat(bounds_maxyz.Swizzle()); - - // Load properties for 4 children - UVec4 properties = UVec4::sLoadInt4(&node->mNodeProperties[0]); - - // Check which sub nodes to visit - int num_results = ioVisitor.VisitNodes(bounds_minx, bounds_miny, bounds_minz, bounds_maxx, bounds_maxy, bounds_maxz, properties, mTop); - - // Push them onto the stack - JPH_ASSERT(mTop + 4 < StackSize); - properties.StoreInt4(&mNodeStack[mTop]); - mTop += num_results; - } - else if (tri_count != TRIANGLE_COUNT_MASK) // TRIANGLE_COUNT_MASK indicates a padding node, normally we shouldn't visit these nodes but when querying with a big enough box you could touch HALF_FLT_MAX (about 65K) - { - // Node contains triangles, do individual tests - uint32 triangle_block_id = node_properties & OFFSET_MASK; - const void *triangles = sGetTriangleBlockStart(inBufferStart, triangle_block_id); - - ioVisitor.VisitTriangles(inTriangleContext, triangles, tri_count, triangle_block_id); - } - - // Check if we're done - if (ioVisitor.ShouldAbort()) - break; - - // Fetch next node until we find one that the visitor wants to see - do - --mTop; - while (mTop >= 0 && !ioVisitor.ShouldVisitNode(mTop)); - } - while (mTop >= 0); - } - - /// This can be used to have the visitor early out (ioVisitor.ShouldAbort() returns true) and later continue again (call WalkTree() again) - bool IsDoneWalking() const - { - return mTop < 0; - } - - private: - uint32 mNodeStack[StackSize]; - int mTop = 0; - }; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/AABBTree/TriangleCodec/TriangleCodecIndexed8BitPackSOA4Flags.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/AABBTree/TriangleCodec/TriangleCodecIndexed8BitPackSOA4Flags.h deleted file mode 100644 index 7d2972187ea..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/AABBTree/TriangleCodec/TriangleCodecIndexed8BitPackSOA4Flags.h +++ /dev/null @@ -1,456 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -/// Store vertices in 64 bits and indices in 8 bits + 8 bit of flags per triangle like this: -/// -/// TriangleBlockHeader, -/// TriangleBlock (4 triangles and their flags in 16 bytes), -/// TriangleBlock... -/// -/// Vertices are stored: -/// -/// VertexData (1 vertex in 64 bits), -/// VertexData... -/// -/// They're compressed relative to the bounding box as provided by the node codec. -class TriangleCodecIndexed8BitPackSOA4Flags -{ -public: - class TriangleHeader - { - public: - Float3 mOffset; ///< Offset of all vertices - Float3 mScale; ///< Scale of all vertices, vertex_position = mOffset + mScale * compressed_vertex_position - }; - - /// Size of the header (an empty struct is always > 0 bytes so this needs a separate variable) - static constexpr int TriangleHeaderSize = sizeof(TriangleHeader); - - /// If this codec could return a different offset than the current buffer size when calling Pack() - static constexpr bool ChangesOffsetOnPack = false; - - /// Amount of bits per component - enum EComponentData : uint32 - { - COMPONENT_BITS = 21, - COMPONENT_MASK = (1 << COMPONENT_BITS) - 1, - }; - - /// Packed X and Y coordinate - enum EVertexXY : uint32 - { - COMPONENT_X = 0, - COMPONENT_Y1 = COMPONENT_BITS, - COMPONENT_Y1_BITS = 32 - COMPONENT_BITS, - }; - - /// Packed Z and Y coordinate - enum EVertexZY : uint32 - { - COMPONENT_Z = 0, - COMPONENT_Y2 = COMPONENT_BITS, - COMPONENT_Y2_BITS = 31 - COMPONENT_BITS, - }; - - /// A single packed vertex - struct VertexData - { - uint32 mVertexXY; - uint32 mVertexZY; - }; - - static_assert(sizeof(VertexData) == 8, "Compiler added padding"); - - /// A block of 4 triangles - struct TriangleBlock - { - uint8 mIndices[3][4]; ///< 8 bit indices to triangle vertices for 4 triangles in the form mIndices[vertex][triangle] where vertex in [0, 2] and triangle in [0, 3] - uint8 mFlags[4]; ///< Triangle flags (could contain material and active edges) - }; - - static_assert(sizeof(TriangleBlock) == 16, "Compiler added padding"); - - /// A triangle header, will be followed by one or more TriangleBlocks - struct TriangleBlockHeader - { - const VertexData * GetVertexData() const { return reinterpret_cast(reinterpret_cast(this) + mOffsetToVertices); } - const TriangleBlock * GetTriangleBlock() const { return reinterpret_cast(reinterpret_cast(this) + sizeof(TriangleBlockHeader)); } - - uint32 mOffsetToVertices; ///< Offset from current block to start of vertices in bytes - }; - - static_assert(sizeof(TriangleBlockHeader) == 4, "Compiler added padding"); - - /// This class is used to validate that the triangle data will not be degenerate after compression - class ValidationContext - { - public: - /// Constructor - ValidationContext(const IndexedTriangleList &inTriangles, const VertexList &inVertices) : - mVertices(inVertices) - { - // Only used the referenced triangles, just like EncodingContext::Finalize does - for (const IndexedTriangle &i : inTriangles) - for (uint32 idx : i.mIdx) - mBounds.Encapsulate(Vec3(inVertices[idx])); - } - - /// Test if a triangle will be degenerate after quantization - bool IsDegenerate(const IndexedTriangle &inTriangle) const - { - // Quantize the triangle in the same way as EncodingContext::Finalize does - UVec4 quantized_vertex[3]; - Vec3 compress_scale = Vec3::sReplicate(COMPONENT_MASK) / Vec3::sMax(mBounds.GetSize(), Vec3::sReplicate(1.0e-20f)); - for (int i = 0; i < 3; ++i) - quantized_vertex[i] = ((Vec3(mVertices[inTriangle.mIdx[i]]) - mBounds.mMin) * compress_scale + Vec3::sReplicate(0.5f)).ToInt(); - return quantized_vertex[0] == quantized_vertex[1] || quantized_vertex[1] == quantized_vertex[2] || quantized_vertex[0] == quantized_vertex[2]; - } - - private: - const VertexList & mVertices; - AABox mBounds; - }; - - /// This class is used to encode and compress triangle data into a byte buffer - class EncodingContext - { - public: - /// Construct the encoding context - explicit EncodingContext(const VertexList &inVertices) : - mVertexMap(inVertices.size(), 0xffffffff) // Fill vertex map with 'not found' - { - // Reserve for worst case to avoid allocating in the inner loop - mVertices.reserve(inVertices.size()); - } - - /// Get an upper bound on the amount of bytes needed to store inTriangleCount triangles - uint GetPessimisticMemoryEstimate(uint inTriangleCount) const - { - // Worst case each triangle is alone in a block, none of the vertices are shared and we need to add 3 bytes to align the vertices - return inTriangleCount * (sizeof(TriangleBlockHeader) + sizeof(TriangleBlock) + 3 * sizeof(VertexData)) + 3; - } - - /// Pack the triangles in inContainer to ioBuffer. This stores the mMaterialIndex of a triangle in the 8 bit flags. - /// Returns uint(-1) on error. - uint Pack(const IndexedTriangleList &inTriangles, ByteBuffer &ioBuffer, const char *&outError) - { - // Determine position of triangles start - uint offset = (uint)ioBuffer.size(); - - // Update stats - uint tri_count = (uint)inTriangles.size(); - mNumTriangles += tri_count; - - // Allocate triangle block header - TriangleBlockHeader *header = ioBuffer.Allocate(); - - // Compute first vertex that this batch will use (ensuring there's enough room if none of the vertices are shared) - uint start_vertex = Clamp((int)mVertices.size() - 256 + (int)tri_count * 3, 0, (int)mVertices.size()); - - // Store the start vertex offset, this will later be patched to give the delta offset relative to the triangle block - mOffsetsToPatch.push_back(uint((uint8 *)&header->mOffsetToVertices - &ioBuffer[0])); - header->mOffsetToVertices = start_vertex * sizeof(VertexData); - - // Pack vertices - uint padded_triangle_count = AlignUp(tri_count, 4); - for (uint t = 0; t < padded_triangle_count; t += 4) - { - TriangleBlock *block = ioBuffer.Allocate(); - for (uint vertex_nr = 0; vertex_nr < 3; ++vertex_nr) - for (uint block_tri_idx = 0; block_tri_idx < 4; ++block_tri_idx) - { - // Fetch vertex index. Create degenerate triangles for padding triangles. - bool triangle_available = t + block_tri_idx < tri_count; - uint32 src_vertex_index = triangle_available? inTriangles[t + block_tri_idx].mIdx[vertex_nr] : inTriangles[tri_count - 1].mIdx[0]; - - // Check if we've seen this vertex before and if it is in the range that we can encode - uint32 &vertex_index = mVertexMap[src_vertex_index]; - if (vertex_index == 0xffffffff || vertex_index < start_vertex) - { - // Add vertex - vertex_index = (uint32)mVertices.size(); - mVertices.push_back(src_vertex_index); - } - - // Store vertex index - uint32 vertex_offset = vertex_index - start_vertex; - if (vertex_offset > 0xff) - { - outError = "TriangleCodecIndexed8BitPackSOA4Flags: Offset doesn't fit in 8 bit"; - return uint(-1); - } - block->mIndices[vertex_nr][block_tri_idx] = (uint8)vertex_offset; - - // Store flags - uint32 flags = triangle_available? inTriangles[t + block_tri_idx].mMaterialIndex : 0; - if (flags > 0xff) - { - outError = "TriangleCodecIndexed8BitPackSOA4Flags: Material index doesn't fit in 8 bit"; - return uint(-1); - } - block->mFlags[block_tri_idx] = (uint8)flags; - } - } - - return offset; - } - - /// After all triangles have been packed, this finalizes the header and triangle buffer - void Finalize(const VertexList &inVertices, TriangleHeader *ioHeader, ByteBuffer &ioBuffer) const - { - // Check if anything to do - if (mVertices.empty()) - return; - - // Align buffer to 4 bytes - uint vertices_idx = (uint)ioBuffer.Align(4); - - // Patch the offsets - for (uint o : mOffsetsToPatch) - *ioBuffer.Get(o) += vertices_idx - o; - - // Calculate bounding box - AABox bounds; - for (uint32 v : mVertices) - bounds.Encapsulate(Vec3(inVertices[v])); - - // Compress vertices - VertexData *vertices = ioBuffer.Allocate(mVertices.size()); - Vec3 compress_scale = Vec3::sReplicate(COMPONENT_MASK) / Vec3::sMax(bounds.GetSize(), Vec3::sReplicate(1.0e-20f)); - for (uint32 v : mVertices) - { - UVec4 c = ((Vec3(inVertices[v]) - bounds.mMin) * compress_scale + Vec3::sReplicate(0.5f)).ToInt(); - JPH_ASSERT(c.GetX() <= COMPONENT_MASK); - JPH_ASSERT(c.GetY() <= COMPONENT_MASK); - JPH_ASSERT(c.GetZ() <= COMPONENT_MASK); - vertices->mVertexXY = c.GetX() + (c.GetY() << COMPONENT_Y1); - vertices->mVertexZY = c.GetZ() + ((c.GetY() >> COMPONENT_Y1_BITS) << COMPONENT_Y2); - ++vertices; - } - - // Store decompression information - bounds.mMin.StoreFloat3(&ioHeader->mOffset); - (bounds.GetSize() / Vec3::sReplicate(COMPONENT_MASK)).StoreFloat3(&ioHeader->mScale); - } - - private: - using VertexMap = Array; - - uint mNumTriangles = 0; - Array mVertices; ///< Output vertices as an index into the original vertex list (inVertices), sorted according to occurrence - VertexMap mVertexMap; ///< Maps from the original mesh vertex index (inVertices) to the index in our output vertices (mVertices) - Array mOffsetsToPatch; ///< Offsets to the vertex buffer that need to be patched in once all nodes have been packed - }; - - /// This class is used to decode and decompress triangle data packed by the EncodingContext - class DecodingContext - { - private: - /// Private helper functions to unpack the 1 vertex of 4 triangles (outX contains the x coordinate of triangle 0 .. 3 etc.) - JPH_INLINE void Unpack(const VertexData *inVertices, UVec4Arg inIndex, Vec4 &outX, Vec4 &outY, Vec4 &outZ) const - { - // Get compressed data - UVec4 c1 = UVec4::sGatherInt4<8>(&inVertices->mVertexXY, inIndex); - UVec4 c2 = UVec4::sGatherInt4<8>(&inVertices->mVertexZY, inIndex); - - // Unpack the x y and z component - UVec4 xc = UVec4::sAnd(c1, UVec4::sReplicate(COMPONENT_MASK)); - UVec4 yc = UVec4::sOr(c1.LogicalShiftRight(), c2.LogicalShiftRight().LogicalShiftLeft()); - UVec4 zc = UVec4::sAnd(c2, UVec4::sReplicate(COMPONENT_MASK)); - - // Convert to float - outX = Vec4::sFusedMultiplyAdd(xc.ToFloat(), mScaleX, mOffsetX); - outY = Vec4::sFusedMultiplyAdd(yc.ToFloat(), mScaleY, mOffsetY); - outZ = Vec4::sFusedMultiplyAdd(zc.ToFloat(), mScaleZ, mOffsetZ); - } - - public: - JPH_INLINE explicit DecodingContext(const TriangleHeader *inHeader) : - mOffsetX(Vec4::sReplicate(inHeader->mOffset.x)), - mOffsetY(Vec4::sReplicate(inHeader->mOffset.y)), - mOffsetZ(Vec4::sReplicate(inHeader->mOffset.z)), - mScaleX(Vec4::sReplicate(inHeader->mScale.x)), - mScaleY(Vec4::sReplicate(inHeader->mScale.y)), - mScaleZ(Vec4::sReplicate(inHeader->mScale.z)) - { - } - - /// Unpacks triangles in the format t1v1,t1v2,t1v3, t2v1,t2v2,t2v3, ... - JPH_INLINE void Unpack(const void *inTriangleStart, uint32 inNumTriangles, Vec3 *outTriangles) const - { - JPH_ASSERT(inNumTriangles > 0); - const TriangleBlockHeader *header = reinterpret_cast(inTriangleStart); - const VertexData *vertices = header->GetVertexData(); - const TriangleBlock *t = header->GetTriangleBlock(); - const TriangleBlock *end = t + ((inNumTriangles + 3) >> 2); - - int triangles_left = inNumTriangles; - - do - { - // Get the indices for the three vertices (reads 4 bytes extra, but these are the flags so that's ok) - UVec4 indices = UVec4::sLoadInt4(reinterpret_cast(&t->mIndices[0])); - UVec4 iv1 = indices.Expand4Byte0(); - UVec4 iv2 = indices.Expand4Byte4(); - UVec4 iv3 = indices.Expand4Byte8(); - - // Decompress the triangle data - Vec4 v1x, v1y, v1z, v2x, v2y, v2z, v3x, v3y, v3z; - Unpack(vertices, iv1, v1x, v1y, v1z); - Unpack(vertices, iv2, v2x, v2y, v2z); - Unpack(vertices, iv3, v3x, v3y, v3z); - - // Transpose it so we get normal vectors - Mat44 v1 = Mat44(v1x, v1y, v1z, Vec4::sZero()).Transposed(); - Mat44 v2 = Mat44(v2x, v2y, v2z, Vec4::sZero()).Transposed(); - Mat44 v3 = Mat44(v3x, v3y, v3z, Vec4::sZero()).Transposed(); - - // Store triangle data - for (int i = 0; i < 4 && triangles_left > 0; ++i, --triangles_left) - { - *outTriangles++ = v1.GetColumn3(i); - *outTriangles++ = v2.GetColumn3(i); - *outTriangles++ = v3.GetColumn3(i); - } - - ++t; - } - while (t < end); - } - - /// Tests a ray against the packed triangles - JPH_INLINE float TestRay(Vec3Arg inRayOrigin, Vec3Arg inRayDirection, const void *inTriangleStart, uint32 inNumTriangles, float inClosest, uint32 &outClosestTriangleIndex) const - { - JPH_ASSERT(inNumTriangles > 0); - const TriangleBlockHeader *header = reinterpret_cast(inTriangleStart); - const VertexData *vertices = header->GetVertexData(); - const TriangleBlock *t = header->GetTriangleBlock(); - const TriangleBlock *end = t + ((inNumTriangles + 3) >> 2); - - Vec4 closest = Vec4::sReplicate(inClosest); - UVec4 closest_triangle_idx = UVec4::sZero(); - - UVec4 start_triangle_idx = UVec4::sZero(); - do - { - // Get the indices for the three vertices (reads 4 bytes extra, but these are the flags so that's ok) - UVec4 indices = UVec4::sLoadInt4(reinterpret_cast(&t->mIndices[0])); - UVec4 iv1 = indices.Expand4Byte0(); - UVec4 iv2 = indices.Expand4Byte4(); - UVec4 iv3 = indices.Expand4Byte8(); - - // Decompress the triangle data - Vec4 v1x, v1y, v1z, v2x, v2y, v2z, v3x, v3y, v3z; - Unpack(vertices, iv1, v1x, v1y, v1z); - Unpack(vertices, iv2, v2x, v2y, v2z); - Unpack(vertices, iv3, v3x, v3y, v3z); - - // Perform ray vs triangle test - Vec4 distance = RayTriangle4(inRayOrigin, inRayDirection, v1x, v1y, v1z, v2x, v2y, v2z, v3x, v3y, v3z); - - // Update closest with the smaller values - UVec4 smaller = Vec4::sLess(distance, closest); - closest = Vec4::sSelect(closest, distance, smaller); - - // Update triangle index with the smallest values - UVec4 triangle_idx = start_triangle_idx + UVec4(0, 1, 2, 3); - closest_triangle_idx = UVec4::sSelect(closest_triangle_idx, triangle_idx, smaller); - - // Next block - ++t; - start_triangle_idx += UVec4::sReplicate(4); - } - while (t < end); - - // Get the smallest component - Vec4::sSort4(closest, closest_triangle_idx); - outClosestTriangleIndex = closest_triangle_idx.GetX(); - return closest.GetX(); - } - - /// Decode a single triangle - inline void GetTriangle(const void *inTriangleStart, uint32 inTriangleIdx, Vec3 &outV1, Vec3 &outV2, Vec3 &outV3) const - { - const TriangleBlockHeader *header = reinterpret_cast(inTriangleStart); - const VertexData *vertices = header->GetVertexData(); - const TriangleBlock *block = header->GetTriangleBlock() + (inTriangleIdx >> 2); - uint32 block_triangle_idx = inTriangleIdx & 0b11; - - // Get the 3 vertices - const VertexData &v1 = vertices[block->mIndices[0][block_triangle_idx]]; - const VertexData &v2 = vertices[block->mIndices[1][block_triangle_idx]]; - const VertexData &v3 = vertices[block->mIndices[2][block_triangle_idx]]; - - // Pack the vertices - UVec4 c1(v1.mVertexXY, v2.mVertexXY, v3.mVertexXY, 0); - UVec4 c2(v1.mVertexZY, v2.mVertexZY, v3.mVertexZY, 0); - - // Unpack the x y and z component - UVec4 xc = UVec4::sAnd(c1, UVec4::sReplicate(COMPONENT_MASK)); - UVec4 yc = UVec4::sOr(c1.LogicalShiftRight(), c2.LogicalShiftRight().LogicalShiftLeft()); - UVec4 zc = UVec4::sAnd(c2, UVec4::sReplicate(COMPONENT_MASK)); - - // Convert to float - Vec4 vx = Vec4::sFusedMultiplyAdd(xc.ToFloat(), mScaleX, mOffsetX); - Vec4 vy = Vec4::sFusedMultiplyAdd(yc.ToFloat(), mScaleY, mOffsetY); - Vec4 vz = Vec4::sFusedMultiplyAdd(zc.ToFloat(), mScaleZ, mOffsetZ); - - // Transpose it so we get normal vectors - Mat44 trans = Mat44(vx, vy, vz, Vec4::sZero()).Transposed(); - outV1 = trans.GetAxisX(); - outV2 = trans.GetAxisY(); - outV3 = trans.GetAxisZ(); - } - - /// Get flags for entire triangle block - JPH_INLINE static void sGetFlags(const void *inTriangleStart, uint32 inNumTriangles, uint8 *outTriangleFlags) - { - JPH_ASSERT(inNumTriangles > 0); - const TriangleBlockHeader *header = reinterpret_cast(inTriangleStart); - const TriangleBlock *t = header->GetTriangleBlock(); - const TriangleBlock *end = t + ((inNumTriangles + 3) >> 2); - - int triangles_left = inNumTriangles; - do - { - for (int i = 0; i < 4 && triangles_left > 0; ++i, --triangles_left) - *outTriangleFlags++ = t->mFlags[i]; - - ++t; - } - while (t < end); - } - - /// Get flags for a particular triangle - JPH_INLINE static uint8 sGetFlags(const void *inTriangleStart, int inTriangleIndex) - { - const TriangleBlockHeader *header = reinterpret_cast(inTriangleStart); - const TriangleBlock *first_block = header->GetTriangleBlock(); - return first_block[inTriangleIndex >> 2].mFlags[inTriangleIndex & 0b11]; - } - - /// Unpacks triangles and flags, convenience function - JPH_INLINE void Unpack(const void *inTriangleStart, uint32 inNumTriangles, Vec3 *outTriangles, uint8 *outTriangleFlags) const - { - Unpack(inTriangleStart, inNumTriangles, outTriangles); - sGetFlags(inTriangleStart, inNumTriangles, outTriangleFlags); - } - - private: - Vec4 mOffsetX; - Vec4 mOffsetY; - Vec4 mOffsetZ; - Vec4 mScaleX; - Vec4 mScaleY; - Vec4 mScaleZ; - }; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ConfigurationString.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ConfigurationString.h deleted file mode 100644 index b6556ce6b5f..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ConfigurationString.h +++ /dev/null @@ -1,71 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2023 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -JPH_NAMESPACE_BEGIN - -/// Construct a string that lists the most important configuration settings -inline const char *GetConfigurationString() -{ - return JPH_IF_SINGLE_PRECISION_ELSE("Single", "Double") " precision " -#if defined(JPH_CPU_X86) - "x86 " -#elif defined(JPH_CPU_ARM) - "ARM " -#elif defined(JPH_PLATFORM_WASM) - "WASM " -#endif -#if JPH_CPU_ADDRESS_BITS == 64 - "64-bit " -#elif JPH_CPU_ADDRESS_BITS == 32 - "32-bit " -#endif - "with instructions: " -#ifdef JPH_USE_NEON - "NEON " -#endif -#ifdef JPH_USE_SSE - "SSE2 " -#endif -#ifdef JPH_USE_SSE4_1 - "SSE4.1 " -#endif -#ifdef JPH_USE_SSE4_2 - "SSE4.2 " -#endif -#ifdef JPH_USE_AVX - "AVX " -#endif -#ifdef JPH_USE_AVX2 - "AVX2 " -#endif -#ifdef JPH_USE_AVX512 - "AVX512 " -#endif -#ifdef JPH_USE_F16C - "F16C " -#endif -#ifdef JPH_USE_LZCNT - "LZCNT " -#endif -#ifdef JPH_USE_TZCNT - "TZCNT " -#endif -#ifdef JPH_USE_FMADD - "FMADD " -#endif -#ifdef JPH_CROSS_PLATFORM_DETERMINISTIC - "(Cross Platform Deterministic) " -#endif -#ifdef JPH_FLOATING_POINT_EXCEPTIONS_ENABLED - "(FP Exceptions) " -#endif -#ifdef _DEBUG - "(Debug) " -#endif - ; -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/ARMNeon.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/ARMNeon.h deleted file mode 100644 index ee4d5527852..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/ARMNeon.h +++ /dev/null @@ -1,88 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2022 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#ifdef JPH_USE_NEON - -#ifdef JPH_COMPILER_MSVC - JPH_NAMESPACE_BEGIN - - // Constructing NEON values - #define JPH_NEON_INT32x4(v1, v2, v3, v4) { int64_t(v1) + (int64_t(v2) << 32), int64_t(v3) + (int64_t(v4) << 32) } - #define JPH_NEON_UINT32x4(v1, v2, v3, v4) { uint64_t(v1) + (uint64_t(v2) << 32), uint64_t(v3) + (uint64_t(v4) << 32) } - #define JPH_NEON_INT8x16(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) { int64_t(v1) + (int64_t(v2) << 8) + (int64_t(v3) << 16) + (int64_t(v4) << 24) + (int64_t(v5) << 32) + (int64_t(v6) << 40) + (int64_t(v7) << 48) + (int64_t(v8) << 56), int64_t(v9) + (int64_t(v10) << 8) + (int64_t(v11) << 16) + (int64_t(v12) << 24) + (int64_t(v13) << 32) + (int64_t(v14) << 40) + (int64_t(v15) << 48) + (int64_t(v16) << 56) } - - // Generic shuffle vector template - template - JPH_INLINE float32x4_t NeonShuffleFloat32x4(float32x4_t inV1, float32x4_t inV2) - { - float32x4_t ret; - ret = vmovq_n_f32(vgetq_lane_f32(I1 >= 4? inV2 : inV1, I1 & 0b11)); - ret = vsetq_lane_f32(vgetq_lane_f32(I2 >= 4? inV2 : inV1, I2 & 0b11), ret, 1); - ret = vsetq_lane_f32(vgetq_lane_f32(I3 >= 4? inV2 : inV1, I3 & 0b11), ret, 2); - ret = vsetq_lane_f32(vgetq_lane_f32(I4 >= 4? inV2 : inV1, I4 & 0b11), ret, 3); - return ret; - } - - // Specializations - template <> - JPH_INLINE float32x4_t NeonShuffleFloat32x4<0, 1, 2, 2>(float32x4_t inV1, float32x4_t inV2) - { - return vcombine_f32(vget_low_f32(inV1), vdup_lane_s32(vget_high_f32(inV1), 0)); - } - - template <> - JPH_INLINE float32x4_t NeonShuffleFloat32x4<0, 1, 3, 3>(float32x4_t inV1, float32x4_t inV2) - { - return vcombine_f32(vget_low_f32(inV1), vdup_lane_s32(vget_high_f32(inV1), 1)); - } - - template <> - JPH_INLINE float32x4_t NeonShuffleFloat32x4<0, 1, 2, 3>(float32x4_t inV1, float32x4_t inV2) - { - return inV1; - } - - template <> - JPH_INLINE float32x4_t NeonShuffleFloat32x4<1, 0, 3, 2>(float32x4_t inV1, float32x4_t inV2) - { - return vcombine_f32(vrev64_f32(vget_low_f32(inV1)), vrev64_f32(vget_high_f32(inV1))); - } - - template <> - JPH_INLINE float32x4_t NeonShuffleFloat32x4<2, 2, 1, 0>(float32x4_t inV1, float32x4_t inV2) - { - return vcombine_f32(vdup_lane_s32(vget_high_f32(inV1), 0), vrev64_f32(vget_low_f32(inV1))); - } - - template <> - JPH_INLINE float32x4_t NeonShuffleFloat32x4<2, 3, 0, 1>(float32x4_t inV1, float32x4_t inV2) - { - return vcombine_f32(vget_high_f32(inV1), vget_low_f32(inV1)); - } - - // Used extensively by cross product - template <> - JPH_INLINE float32x4_t NeonShuffleFloat32x4<1, 2, 0, 0>(float32x4_t inV1, float32x4_t inV2) - { - static int8x16_t table = JPH_NEON_INT8x16(0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03); - return vreinterpretq_f32_u8(vqtbl1q_u8(vreinterpretq_u8_f32(inV1), table)); - } - - // Shuffle a vector - #define JPH_NEON_SHUFFLE_F32x4(vec1, vec2, index1, index2, index3, index4) NeonShuffleFloat32x4(vec1, vec2) - - JPH_NAMESPACE_END -#else - // Constructing NEON values - #define JPH_NEON_INT32x4(v1, v2, v3, v4) { v1, v2, v3, v4 } - #define JPH_NEON_UINT32x4(v1, v2, v3, v4) { v1, v2, v3, v4 } - #define JPH_NEON_INT8x16(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) { v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16 } - - // Shuffle a vector - #define JPH_NEON_SHUFFLE_F32x4(vec1, vec2, index1, index2, index3, index4) __builtin_shufflevector(vec1, vec2, index1, index2, index3, index4) -#endif - -#endif // JPH_USE_NEON diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Atomics.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Atomics.h deleted file mode 100644 index a53faa5c877..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Atomics.h +++ /dev/null @@ -1,44 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -JPH_SUPPRESS_WARNINGS_STD_BEGIN -#include -JPH_SUPPRESS_WARNINGS_STD_END - -JPH_NAMESPACE_BEGIN - -// Things we're using from STL -using std::atomic; -using std::memory_order; -using std::memory_order_relaxed; -using std::memory_order_acquire; -using std::memory_order_release; -using std::memory_order_acq_rel; -using std::memory_order_seq_cst; - -/// Atomically compute the min(ioAtomic, inValue) and store it in ioAtomic, returns true if value was updated -template -bool AtomicMin(atomic &ioAtomic, const T inValue, const memory_order inMemoryOrder = memory_order_seq_cst) -{ - T cur_value = ioAtomic.load(memory_order_relaxed); - while (cur_value > inValue) - if (ioAtomic.compare_exchange_weak(cur_value, inValue, inMemoryOrder)) - return true; - return false; -} - -/// Atomically compute the max(ioAtomic, inValue) and store it in ioAtomic, returns true if value was updated -template -bool AtomicMax(atomic &ioAtomic, const T inValue, const memory_order inMemoryOrder = memory_order_seq_cst) -{ - T cur_value = ioAtomic.load(memory_order_relaxed); - while (cur_value < inValue) - if (ioAtomic.compare_exchange_weak(cur_value, inValue, inMemoryOrder)) - return true; - return false; -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/ByteBuffer.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/ByteBuffer.h deleted file mode 100644 index 32e871a7eda..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/ByteBuffer.h +++ /dev/null @@ -1,74 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -/// Underlying data type for ByteBuffer -using ByteBufferVector = std::vector>; - -/// Simple byte buffer, aligned to a cache line -class ByteBuffer : public ByteBufferVector -{ -public: - /// Align the size to a multiple of inSize, returns the length after alignment - size_t Align(size_t inSize) - { - // Assert power of 2 - JPH_ASSERT(IsPowerOf2(inSize)); - - // Calculate new size and resize buffer - size_t s = AlignUp(size(), inSize); - resize(s); - - return s; - } - - /// Allocate block of data of inSize elements and return the pointer - template - Type * Allocate(size_t inSize = 1) - { - // Reserve space - size_t s = size(); - resize(s + inSize * sizeof(Type)); - - // Get data pointer - Type *data = reinterpret_cast(&at(s)); - - // Construct elements - for (Type *d = data, *d_end = data + inSize; d < d_end; ++d) - ::new (d) Type; - - // Return pointer - return data; - } - - /// Append inData to the buffer - template - void AppendVector(const Array &inData) - { - size_t size = inData.size() * sizeof(Type); - uint8 *data = Allocate(size); - memcpy(data, &inData[0], size); - } - - /// Get object at inPosition (an offset in bytes) - template - const Type * Get(size_t inPosition) const - { - return reinterpret_cast(&at(inPosition)); - } - - /// Get object at inPosition (an offset in bytes) - template - Type * Get(size_t inPosition) - { - return reinterpret_cast(&at(inPosition)); - } -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Color.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Color.cpp deleted file mode 100644 index 93d3cabc75b..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Color.cpp +++ /dev/null @@ -1,38 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include - -JPH_NAMESPACE_BEGIN - -// Predefined colors -const Color Color::sBlack(0, 0, 0); -const Color Color::sDarkRed(128, 0, 0); -const Color Color::sRed(255, 0, 0); -const Color Color::sDarkGreen(0, 128, 0); -const Color Color::sGreen(0, 255, 0); -const Color Color::sDarkBlue(0, 0, 128); -const Color Color::sBlue(0, 0, 255); -const Color Color::sYellow(255, 255, 0); -const Color Color::sPurple(255, 0, 255); -const Color Color::sCyan(0, 255, 255); -const Color Color::sOrange(255, 128, 0); -const Color Color::sDarkOrange(128, 64, 0); -const Color Color::sGrey(128, 128, 128); -const Color Color::sLightGrey(192, 192, 192); -const Color Color::sWhite(255, 255, 255); - -// Generated by: http://phrogz.net/css/distinct-colors.html (this algo: https://en.wikipedia.org/wiki/Color_difference#CMC_l:c_.281984.29) -static constexpr Color sColors[] = { Color(255, 0, 0), Color(204, 143, 102), Color(226, 242, 0), Color(41, 166, 124), Color(0, 170, 255), Color(69, 38, 153), Color(153, 38, 130), Color(229, 57, 80), Color(204, 0, 0), Color(255, 170, 0), Color(85, 128, 0), Color(64, 255, 217), Color(0, 75, 140), Color(161, 115, 230), Color(242, 61, 157), Color(178, 101, 89), Color(140, 94, 0), Color(181, 217, 108), Color(64, 242, 255), Color(77, 117, 153), Color(157, 61, 242), Color(140, 0, 56), Color(127, 57, 32), Color(204, 173, 51), Color(64, 255, 64), Color(38, 145, 153), Color(0, 102, 255), Color(242, 0, 226), Color(153, 77, 107), Color(229, 92, 0), Color(140, 126, 70), Color(0, 179, 71), Color(0, 194, 242), Color(27, 0, 204), Color(230, 115, 222), Color(127, 0, 17) }; - -Color Color::sGetDistinctColor(int inIndex) -{ - JPH_ASSERT(inIndex >= 0); - - return sColors[inIndex % (sizeof(sColors) / sizeof(uint32))]; -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Color.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Color.h deleted file mode 100644 index b979fb4f59e..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Color.h +++ /dev/null @@ -1,81 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -JPH_NAMESPACE_BEGIN - -class Color; - -/// Type to use for passing arguments to a function -using ColorArg = Color; - -/// Class that holds an RGBA color with 8-bits per component -class [[nodiscard]] JPH_EXPORT_GCC_BUG_WORKAROUND Color -{ -public: - /// Constructors - Color() = default; ///< Intentionally not initialized for performance reasons - Color(const Color &inRHS) = default; - Color & operator = (const Color &inRHS) = default; - explicit constexpr Color(uint32 inColor) : mU32(inColor) { } - constexpr Color(uint8 inRed, uint8 inGreen, uint8 inBlue, uint8 inAlpha = 255) : r(inRed), g(inGreen), b(inBlue), a(inAlpha) { } - constexpr Color(ColorArg inRHS, uint8 inAlpha) : r(inRHS.r), g(inRHS.g), b(inRHS.b), a(inAlpha) { } - - /// Comparison - inline bool operator == (ColorArg inRHS) const { return mU32 == inRHS.mU32; } - inline bool operator != (ColorArg inRHS) const { return mU32 != inRHS.mU32; } - - /// Convert to uint32 - uint32 GetUInt32() const { return mU32; } - - /// Element access, 0 = red, 1 = green, 2 = blue, 3 = alpha - inline uint8 operator () (uint inIdx) const { JPH_ASSERT(inIdx < 4); return (&r)[inIdx]; } - inline uint8 & operator () (uint inIdx) { JPH_ASSERT(inIdx < 4); return (&r)[inIdx]; } - - /// Multiply two colors - inline Color operator * (const Color &inRHS) const { return Color(uint8((uint32(r) * inRHS.r) >> 8), uint8((uint32(g) * inRHS.g) >> 8), uint8((uint32(b) * inRHS.b) >> 8), uint8((uint32(a) * inRHS.a) >> 8)); } - - /// Convert to Vec4 with range [0, 1] - inline Vec4 ToVec4() const { return Vec4(r, g, b, a) / 255.0f; } - - /// Get grayscale intensity of color - inline uint8 GetIntensity() const { return uint8((uint32(r) * 54 + g * 183 + b * 19) >> 8); } - - /// Get a visually distinct color - static Color sGetDistinctColor(int inIndex); - - /// Predefined colors - static const Color sBlack; - static const Color sDarkRed; - static const Color sRed; - static const Color sDarkGreen; - static const Color sGreen; - static const Color sDarkBlue; - static const Color sBlue; - static const Color sYellow; - static const Color sPurple; - static const Color sCyan; - static const Color sOrange; - static const Color sDarkOrange; - static const Color sGrey; - static const Color sLightGrey; - static const Color sWhite; - - union - { - uint32 mU32; ///< Combined value for red, green, blue and alpha - struct - { - uint8 r; ///< Red channel - uint8 g; ///< Green channel - uint8 b; ///< Blue channel - uint8 a; ///< Alpha channel - }; - }; -}; - -static_assert(is_trivial(), "Is supposed to be a trivial type!"); - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Core.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Core.h deleted file mode 100644 index bd26ef11e4c..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Core.h +++ /dev/null @@ -1,555 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -// Jolt library version -#define JPH_VERSION_MAJOR 5 -#define JPH_VERSION_MINOR 0 -#define JPH_VERSION_PATCH 0 - -// Determine which features the library was compiled with -#ifdef JPH_DOUBLE_PRECISION - #define JPH_VERSION_FEATURE_BIT_1 1 -#else - #define JPH_VERSION_FEATURE_BIT_1 0 -#endif -#ifdef JPH_CROSS_PLATFORM_DETERMINISTIC - #define JPH_VERSION_FEATURE_BIT_2 1 -#else - #define JPH_VERSION_FEATURE_BIT_2 0 -#endif -#ifdef JPH_FLOATING_POINT_EXCEPTIONS_ENABLED - #define JPH_VERSION_FEATURE_BIT_3 1 -#else - #define JPH_VERSION_FEATURE_BIT_3 0 -#endif -#ifdef JPH_PROFILE_ENABLED - #define JPH_VERSION_FEATURE_BIT_4 1 -#else - #define JPH_VERSION_FEATURE_BIT_4 0 -#endif -#ifdef JPH_EXTERNAL_PROFILE - #define JPH_VERSION_FEATURE_BIT_5 1 -#else - #define JPH_VERSION_FEATURE_BIT_5 0 -#endif -#ifdef JPH_DEBUG_RENDERER - #define JPH_VERSION_FEATURE_BIT_6 1 -#else - #define JPH_VERSION_FEATURE_BIT_6 0 -#endif -#ifdef JPH_DISABLE_TEMP_ALLOCATOR - #define JPH_VERSION_FEATURE_BIT_7 1 -#else - #define JPH_VERSION_FEATURE_BIT_7 0 -#endif -#ifdef JPH_DISABLE_CUSTOM_ALLOCATOR - #define JPH_VERSION_FEATURE_BIT_8 1 -#else - #define JPH_VERSION_FEATURE_BIT_8 0 -#endif -#if defined(JPH_OBJECT_LAYER_BITS) && JPH_OBJECT_LAYER_BITS == 32 - #define JPH_VERSION_FEATURE_BIT_9 1 -#else - #define JPH_VERSION_FEATURE_BIT_9 0 -#endif -#ifdef JPH_ENABLE_ASSERTS - #define JPH_VERSION_FEATURE_BIT_10 1 -#else - #define JPH_VERSION_FEATURE_BIT_10 0 -#endif -#define JPH_VERSION_FEATURES (uint64(JPH_VERSION_FEATURE_BIT_1) | (JPH_VERSION_FEATURE_BIT_2 << 1) | (JPH_VERSION_FEATURE_BIT_3 << 2) | (JPH_VERSION_FEATURE_BIT_4 << 3) | (JPH_VERSION_FEATURE_BIT_5 << 4) | (JPH_VERSION_FEATURE_BIT_6 << 5) | (JPH_VERSION_FEATURE_BIT_7 << 6) | (JPH_VERSION_FEATURE_BIT_8 << 7) | (JPH_VERSION_FEATURE_BIT_9 << 8) | (JPH_VERSION_FEATURE_BIT_10 << 9)) - -// Combine the version and features in a single ID -#define JPH_VERSION_ID ((JPH_VERSION_FEATURES << 24) | (JPH_VERSION_MAJOR << 16) | (JPH_VERSION_MINOR << 8) | JPH_VERSION_PATCH) - -// Determine platform -#if defined(JPH_PLATFORM_BLUE) - // Correct define already defined, this overrides everything else -#elif defined(_WIN32) || defined(_WIN64) - #include - #if WINAPI_FAMILY == WINAPI_FAMILY_APP - #define JPH_PLATFORM_WINDOWS_UWP // Building for Universal Windows Platform - #endif - #define JPH_PLATFORM_WINDOWS -#elif defined(__ANDROID__) // Android is linux too, so that's why we check it first - #define JPH_PLATFORM_ANDROID -#elif defined(__linux__) - #define JPH_PLATFORM_LINUX -#elif defined(__FreeBSD__) - #define JPH_PLATFORM_FREEBSD -#elif defined(__APPLE__) - #include - #if defined(TARGET_OS_IPHONE) && !TARGET_OS_IPHONE - #define JPH_PLATFORM_MACOS - #else - #define JPH_PLATFORM_IOS - #endif -#elif defined(__EMSCRIPTEN__) - #define JPH_PLATFORM_WASM -#endif - -// Platform helper macros -#ifdef JPH_PLATFORM_ANDROID - #define JPH_IF_NOT_ANDROID(x) -#else - #define JPH_IF_NOT_ANDROID(x) x -#endif - -// Determine compiler -#if defined(__clang__) - #define JPH_COMPILER_CLANG -#elif defined(__GNUC__) - #define JPH_COMPILER_GCC -#elif defined(_MSC_VER) - #define JPH_COMPILER_MSVC -#endif - -#if defined(__MINGW64__) || defined (__MINGW32__) - #define JPH_COMPILER_MINGW -#endif - -// Detect CPU architecture -#if defined(__x86_64__) || defined(_M_X64) || defined(__i386__) || defined(_M_IX86) - // X86 CPU architecture - #define JPH_CPU_X86 - #if defined(__x86_64__) || defined(_M_X64) - #define JPH_CPU_ADDRESS_BITS 64 - #else - #define JPH_CPU_ADDRESS_BITS 32 - #endif - #define JPH_USE_SSE - #define JPH_VECTOR_ALIGNMENT 16 - #define JPH_DVECTOR_ALIGNMENT 32 - - // Detect enabled instruction sets - #if defined(__AVX512F__) && defined(__AVX512VL__) && defined(__AVX512DQ__) && !defined(JPH_USE_AVX512) - #define JPH_USE_AVX512 - #endif - #if (defined(__AVX2__) || defined(JPH_USE_AVX512)) && !defined(JPH_USE_AVX2) - #define JPH_USE_AVX2 - #endif - #if (defined(__AVX__) || defined(JPH_USE_AVX2)) && !defined(JPH_USE_AVX) - #define JPH_USE_AVX - #endif - #if (defined(__SSE4_2__) || defined(JPH_USE_AVX)) && !defined(JPH_USE_SSE4_2) - #define JPH_USE_SSE4_2 - #endif - #if (defined(__SSE4_1__) || defined(JPH_USE_SSE4_2)) && !defined(JPH_USE_SSE4_1) - #define JPH_USE_SSE4_1 - #endif - #if (defined(__F16C__) || defined(JPH_USE_AVX2)) && !defined(JPH_USE_F16C) - #define JPH_USE_F16C - #endif - #if (defined(__LZCNT__) || defined(JPH_USE_AVX2)) && !defined(JPH_USE_LZCNT) - #define JPH_USE_LZCNT - #endif - #if (defined(__BMI__) || defined(JPH_USE_AVX2)) && !defined(JPH_USE_TZCNT) - #define JPH_USE_TZCNT - #endif - #ifndef JPH_CROSS_PLATFORM_DETERMINISTIC // FMA is not compatible with cross platform determinism - #if defined(JPH_COMPILER_CLANG) || defined(JPH_COMPILER_GCC) - #if defined(__FMA__) && !defined(JPH_USE_FMADD) - #define JPH_USE_FMADD - #endif - #elif defined(JPH_COMPILER_MSVC) - #if defined(__AVX2__) && !defined(JPH_USE_FMADD) // AVX2 also enables fused multiply add - #define JPH_USE_FMADD - #endif - #else - #error Undefined compiler - #endif - #endif -#elif defined(__aarch64__) || defined(_M_ARM64) || defined(__arm__) || defined(_M_ARM) - // ARM CPU architecture - #define JPH_CPU_ARM - #if defined(__aarch64__) || defined(_M_ARM64) - #define JPH_CPU_ADDRESS_BITS 64 - #define JPH_USE_NEON - #define JPH_VECTOR_ALIGNMENT 16 - #define JPH_DVECTOR_ALIGNMENT 32 - #else - #define JPH_CPU_ADDRESS_BITS 32 - #define JPH_VECTOR_ALIGNMENT 8 // 32-bit ARM does not support aligning on the stack on 16 byte boundaries - #define JPH_DVECTOR_ALIGNMENT 8 - #endif -#elif defined(JPH_PLATFORM_WASM) - // WebAssembly CPU architecture - #define JPH_CPU_WASM - #define JPH_CPU_ADDRESS_BITS 32 - #define JPH_VECTOR_ALIGNMENT 16 - #define JPH_DVECTOR_ALIGNMENT 32 - #ifdef __wasm_simd128__ - #define JPH_USE_SSE - #define JPH_USE_SSE4_1 - #define JPH_USE_SSE4_2 - #endif -#elif defined(__e2k__) - // Elbrus e2k architecture - #define JPH_CPU_E2K - #define JPH_CPU_ADDRESS_BITS 64 - #define JPH_USE_SSE - #define JPH_VECTOR_ALIGNMENT 16 - #define JPH_DVECTOR_ALIGNMENT 32 -#else - #error Unsupported CPU architecture -#endif - -// If this define is set, Jolt is compiled as a shared library -#ifdef JPH_SHARED_LIBRARY - #ifdef JPH_BUILD_SHARED_LIBRARY - // While building the shared library, we must export these symbols - #ifdef JPH_PLATFORM_WINDOWS - #define JPH_EXPORT __declspec(dllexport) - #else - #define JPH_EXPORT __attribute__ ((visibility ("default"))) - #if defined(JPH_COMPILER_GCC) - // Prevents an issue with GCC attribute parsing (see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=69585) - #define JPH_EXPORT_GCC_BUG_WORKAROUND [[gnu::visibility("default")]] - #endif - #endif - #else - // When linking against Jolt, we must import these symbols - #ifdef JPH_PLATFORM_WINDOWS - #define JPH_EXPORT __declspec(dllimport) - #else - #define JPH_EXPORT __attribute__ ((visibility ("default"))) - #if defined(JPH_COMPILER_GCC) - // Prevents an issue with GCC attribute parsing (see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=69585) - #define JPH_EXPORT_GCC_BUG_WORKAROUND [[gnu::visibility("default")]] - #endif - #endif - #endif -#else - // If the define is not set, we use static linking and symbols don't need to be imported or exported - #define JPH_EXPORT -#endif - -#ifndef JPH_EXPORT_GCC_BUG_WORKAROUND - #define JPH_EXPORT_GCC_BUG_WORKAROUND JPH_EXPORT -#endif - -// Macro used by the RTTI macros to not export a function -#define JPH_NO_EXPORT - -// Pragmas to store / restore the warning state and to disable individual warnings -#ifdef JPH_COMPILER_CLANG -#define JPH_PRAGMA(x) _Pragma(#x) -#define JPH_SUPPRESS_WARNING_PUSH JPH_PRAGMA(clang diagnostic push) -#define JPH_SUPPRESS_WARNING_POP JPH_PRAGMA(clang diagnostic pop) -#define JPH_CLANG_SUPPRESS_WARNING(w) JPH_PRAGMA(clang diagnostic ignored w) -#if __clang_major__ >= 13 - #define JPH_CLANG_13_PLUS_SUPPRESS_WARNING(w) JPH_CLANG_SUPPRESS_WARNING(w) -#else - #define JPH_CLANG_13_PLUS_SUPPRESS_WARNING(w) -#endif -#if __clang_major__ >= 16 - #define JPH_CLANG_16_PLUS_SUPPRESS_WARNING(w) JPH_CLANG_SUPPRESS_WARNING(w) -#else - #define JPH_CLANG_16_PLUS_SUPPRESS_WARNING(w) -#endif -#else -#define JPH_CLANG_SUPPRESS_WARNING(w) -#define JPH_CLANG_13_PLUS_SUPPRESS_WARNING(w) -#define JPH_CLANG_16_PLUS_SUPPRESS_WARNING(w) -#endif -#ifdef JPH_COMPILER_GCC -#define JPH_PRAGMA(x) _Pragma(#x) -#define JPH_SUPPRESS_WARNING_PUSH JPH_PRAGMA(GCC diagnostic push) -#define JPH_SUPPRESS_WARNING_POP JPH_PRAGMA(GCC diagnostic pop) -#define JPH_GCC_SUPPRESS_WARNING(w) JPH_PRAGMA(GCC diagnostic ignored w) -#else -#define JPH_GCC_SUPPRESS_WARNING(w) -#endif -#ifdef JPH_COMPILER_MSVC -#define JPH_PRAGMA(x) __pragma(x) -#define JPH_SUPPRESS_WARNING_PUSH JPH_PRAGMA(warning (push)) -#define JPH_SUPPRESS_WARNING_POP JPH_PRAGMA(warning (pop)) -#define JPH_MSVC_SUPPRESS_WARNING(w) JPH_PRAGMA(warning (disable : w)) -#if _MSC_VER >= 1920 && _MSC_VER < 1930 - #define JPH_MSVC2019_SUPPRESS_WARNING(w) JPH_MSVC_SUPPRESS_WARNING(w) -#else - #define JPH_MSVC2019_SUPPRESS_WARNING(w) -#endif -#else -#define JPH_MSVC_SUPPRESS_WARNING(w) -#define JPH_MSVC2019_SUPPRESS_WARNING(w) -#endif - -// Disable common warnings triggered by Jolt when compiling with -Wall -#define JPH_SUPPRESS_WARNINGS \ - JPH_CLANG_SUPPRESS_WARNING("-Wc++98-compat") \ - JPH_CLANG_SUPPRESS_WARNING("-Wc++98-compat-pedantic") \ - JPH_CLANG_SUPPRESS_WARNING("-Wfloat-equal") \ - JPH_CLANG_SUPPRESS_WARNING("-Wsign-conversion") \ - JPH_CLANG_SUPPRESS_WARNING("-Wold-style-cast") \ - JPH_CLANG_SUPPRESS_WARNING("-Wgnu-anonymous-struct") \ - JPH_CLANG_SUPPRESS_WARNING("-Wnested-anon-types") \ - JPH_CLANG_SUPPRESS_WARNING("-Wglobal-constructors") \ - JPH_CLANG_SUPPRESS_WARNING("-Wexit-time-destructors") \ - JPH_CLANG_SUPPRESS_WARNING("-Wnonportable-system-include-path") \ - JPH_CLANG_SUPPRESS_WARNING("-Wlanguage-extension-token") \ - JPH_CLANG_SUPPRESS_WARNING("-Wunused-parameter") \ - JPH_CLANG_SUPPRESS_WARNING("-Wformat-nonliteral") \ - JPH_CLANG_SUPPRESS_WARNING("-Wcovered-switch-default") \ - JPH_CLANG_SUPPRESS_WARNING("-Wcast-align") \ - JPH_CLANG_SUPPRESS_WARNING("-Winvalid-offsetof") \ - JPH_CLANG_SUPPRESS_WARNING("-Wgnu-zero-variadic-macro-arguments") \ - JPH_CLANG_SUPPRESS_WARNING("-Wdocumentation-unknown-command") \ - JPH_CLANG_SUPPRESS_WARNING("-Wctad-maybe-unsupported") \ - JPH_CLANG_13_PLUS_SUPPRESS_WARNING("-Wdeprecated-copy") \ - JPH_CLANG_13_PLUS_SUPPRESS_WARNING("-Wdeprecated-copy-with-dtor") \ - JPH_CLANG_16_PLUS_SUPPRESS_WARNING("-Wunsafe-buffer-usage") \ - JPH_IF_NOT_ANDROID(JPH_CLANG_SUPPRESS_WARNING("-Wimplicit-int-float-conversion")) \ - \ - JPH_GCC_SUPPRESS_WARNING("-Wcomment") \ - JPH_GCC_SUPPRESS_WARNING("-Winvalid-offsetof") \ - JPH_GCC_SUPPRESS_WARNING("-Wclass-memaccess") \ - JPH_GCC_SUPPRESS_WARNING("-Wpedantic") \ - \ - JPH_MSVC_SUPPRESS_WARNING(4619) /* #pragma warning: there is no warning number 'XXXX' */ \ - JPH_MSVC_SUPPRESS_WARNING(4514) /* 'X' : unreferenced inline function has been removed */ \ - JPH_MSVC_SUPPRESS_WARNING(4710) /* 'X' : function not inlined */ \ - JPH_MSVC_SUPPRESS_WARNING(4711) /* function 'X' selected for automatic inline expansion */ \ - JPH_MSVC_SUPPRESS_WARNING(4820) /* 'X': 'Y' bytes padding added after data member 'Z' */ \ - JPH_MSVC_SUPPRESS_WARNING(4100) /* 'X' : unreferenced formal parameter */ \ - JPH_MSVC_SUPPRESS_WARNING(4626) /* 'X' : assignment operator was implicitly defined as deleted because a base class assignment operator is inaccessible or deleted */ \ - JPH_MSVC_SUPPRESS_WARNING(5027) /* 'X' : move assignment operator was implicitly defined as deleted because a base class move assignment operator is inaccessible or deleted */ \ - JPH_MSVC_SUPPRESS_WARNING(4365) /* 'argument' : conversion from 'X' to 'Y', signed / unsigned mismatch */ \ - JPH_MSVC_SUPPRESS_WARNING(4324) /* 'X' : structure was padded due to alignment specifier */ \ - JPH_MSVC_SUPPRESS_WARNING(4625) /* 'X' : copy constructor was implicitly defined as deleted because a base class copy constructor is inaccessible or deleted */ \ - JPH_MSVC_SUPPRESS_WARNING(5026) /* 'X': move constructor was implicitly defined as deleted because a base class move constructor is inaccessible or deleted */ \ - JPH_MSVC_SUPPRESS_WARNING(4623) /* 'X' : default constructor was implicitly defined as deleted */ \ - JPH_MSVC_SUPPRESS_WARNING(4201) /* nonstandard extension used: nameless struct/union */ \ - JPH_MSVC_SUPPRESS_WARNING(4371) /* 'X': layout of class may have changed from a previous version of the compiler due to better packing of member 'Y' */ \ - JPH_MSVC_SUPPRESS_WARNING(5045) /* Compiler will insert Spectre mitigation for memory load if /Qspectre switch specified */ \ - JPH_MSVC_SUPPRESS_WARNING(4583) /* 'X': destructor is not implicitly called */ \ - JPH_MSVC_SUPPRESS_WARNING(4582) /* 'X': constructor is not implicitly called */ \ - JPH_MSVC_SUPPRESS_WARNING(5219) /* implicit conversion from 'X' to 'Y', possible loss of data */ \ - JPH_MSVC_SUPPRESS_WARNING(4826) /* Conversion from 'X *' to 'JPH::uint64' is sign-extended. This may cause unexpected runtime behavior. (32-bit) */ \ - JPH_MSVC_SUPPRESS_WARNING(5264) /* 'X': 'const' variable is not used */ \ - JPH_MSVC_SUPPRESS_WARNING(4251) /* class 'X' needs to have DLL-interface to be used by clients of class 'Y' */ \ - JPH_MSVC_SUPPRESS_WARNING(4738) /* storing 32-bit float result in memory, possible loss of performance */ \ - JPH_MSVC2019_SUPPRESS_WARNING(5246) /* the initialization of a subobject should be wrapped in braces */ - -// OS-specific includes -#if defined(JPH_PLATFORM_WINDOWS) - #define JPH_BREAKPOINT __debugbreak() -#elif defined(JPH_PLATFORM_BLUE) - // Configuration for a popular game console. - // This file is not distributed because it would violate an NDA. - // Creating one should only be a couple of minutes of work if you have the documentation for the platform - // (you only need to define JPH_BREAKPOINT, JPH_PLATFORM_BLUE_GET_TICKS, JPH_PLATFORM_BLUE_MUTEX*, JPH_PLATFORM_BLUE_RWLOCK* and include the right header). - #include -#elif defined(JPH_PLATFORM_LINUX) || defined(JPH_PLATFORM_ANDROID) || defined(JPH_PLATFORM_MACOS) || defined(JPH_PLATFORM_IOS) || defined(JPH_PLATFORM_FREEBSD) - #if defined(JPH_CPU_X86) - #define JPH_BREAKPOINT __asm volatile ("int $0x3") - #elif defined(JPH_CPU_ARM) - #define JPH_BREAKPOINT __builtin_trap() - #elif defined(JPH_CPU_E2K) - #define JPH_BREAKPOINT __builtin_trap() - #endif -#elif defined(JPH_PLATFORM_WASM) - #define JPH_BREAKPOINT do { } while (false) // Not supported -#else - #error Unknown platform -#endif - -// Begin the JPH namespace -#define JPH_NAMESPACE_BEGIN \ - JPH_SUPPRESS_WARNING_PUSH \ - JPH_SUPPRESS_WARNINGS \ - namespace JPH { - -// End the JPH namespace -#define JPH_NAMESPACE_END \ - } \ - JPH_SUPPRESS_WARNING_POP - -// Suppress warnings generated by the standard template library -#define JPH_SUPPRESS_WARNINGS_STD_BEGIN \ - JPH_SUPPRESS_WARNING_PUSH \ - JPH_MSVC_SUPPRESS_WARNING(4365) \ - JPH_MSVC_SUPPRESS_WARNING(4619) \ - JPH_MSVC_SUPPRESS_WARNING(4710) \ - JPH_MSVC_SUPPRESS_WARNING(4711) \ - JPH_MSVC_SUPPRESS_WARNING(4820) \ - JPH_MSVC_SUPPRESS_WARNING(4514) \ - JPH_MSVC_SUPPRESS_WARNING(5262) \ - JPH_MSVC_SUPPRESS_WARNING(5264) \ - JPH_MSVC_SUPPRESS_WARNING(4738) - -#define JPH_SUPPRESS_WARNINGS_STD_END \ - JPH_SUPPRESS_WARNING_POP - -// Standard C++ includes -#include -#include -#include -JPH_SUPPRESS_WARNINGS_STD_BEGIN -#include -#include -#include -#include -#include -#include -#include -JPH_SUPPRESS_WARNINGS_STD_END -#if defined(JPH_USE_SSE) - #include -#elif defined(JPH_USE_NEON) - #ifdef JPH_COMPILER_MSVC - #include - #include - #else - #include - #endif -#endif - -JPH_NAMESPACE_BEGIN - -// Commonly used STL types -using std::pair; -using std::min; -using std::max; -using std::abs; -using std::sqrt; -using std::ceil; -using std::floor; -using std::trunc; -using std::round; -using std::fmod; -using std::swap; -using std::size; -using std::string; -using std::string_view; -using std::function; -using std::numeric_limits; -using std::isfinite; -using std::isnan; -using std::is_trivial; -using std::is_trivially_constructible; -using std::is_trivially_destructible; -using std::ostream; -using std::istream; - -// Standard types -using uint = unsigned int; -using uint8 = std::uint8_t; -using uint16 = std::uint16_t; -using uint32 = std::uint32_t; -using uint64 = std::uint64_t; - -// Assert sizes of types -static_assert(sizeof(uint) >= 4, "Invalid size of uint"); -static_assert(sizeof(uint8) == 1, "Invalid size of uint8"); -static_assert(sizeof(uint16) == 2, "Invalid size of uint16"); -static_assert(sizeof(uint32) == 4, "Invalid size of uint32"); -static_assert(sizeof(uint64) == 8, "Invalid size of uint64"); -static_assert(sizeof(void *) == (JPH_CPU_ADDRESS_BITS == 64? 8 : 4), "Invalid size of pointer" ); - -// Define inline macro -#if defined(JPH_NO_FORCE_INLINE) - #define JPH_INLINE inline -#elif defined(JPH_COMPILER_CLANG) || defined(JPH_COMPILER_GCC) - #define JPH_INLINE __inline__ __attribute__((always_inline)) -#elif defined(JPH_COMPILER_MSVC) - #define JPH_INLINE __forceinline -#else - #error Undefined -#endif - -// Cache line size (used for aligning to cache line) -#ifndef JPH_CACHE_LINE_SIZE - #define JPH_CACHE_LINE_SIZE 64 -#endif - -// Define macro to get current function name -#if defined(JPH_COMPILER_CLANG) || defined(JPH_COMPILER_GCC) - #define JPH_FUNCTION_NAME __PRETTY_FUNCTION__ -#elif defined(JPH_COMPILER_MSVC) - #define JPH_FUNCTION_NAME __FUNCTION__ -#else - #error Undefined -#endif - -// Stack allocation -#define JPH_STACK_ALLOC(n) alloca(n) - -// Shorthand for #ifdef _DEBUG / #endif -#ifdef _DEBUG - #define JPH_IF_DEBUG(...) __VA_ARGS__ - #define JPH_IF_NOT_DEBUG(...) -#else - #define JPH_IF_DEBUG(...) - #define JPH_IF_NOT_DEBUG(...) __VA_ARGS__ -#endif - -// Shorthand for #ifdef JPH_FLOATING_POINT_EXCEPTIONS_ENABLED / #endif -#ifdef JPH_FLOATING_POINT_EXCEPTIONS_ENABLED - #define JPH_IF_FLOATING_POINT_EXCEPTIONS_ENABLED(...) __VA_ARGS__ -#else - #define JPH_IF_FLOATING_POINT_EXCEPTIONS_ENABLED(...) -#endif - -// Helper macros to detect if we're running in single or double precision mode -#ifdef JPH_DOUBLE_PRECISION - #define JPH_IF_SINGLE_PRECISION(...) - #define JPH_IF_SINGLE_PRECISION_ELSE(s, d) d - #define JPH_IF_DOUBLE_PRECISION(...) __VA_ARGS__ -#else - #define JPH_IF_SINGLE_PRECISION(...) __VA_ARGS__ - #define JPH_IF_SINGLE_PRECISION_ELSE(s, d) s - #define JPH_IF_DOUBLE_PRECISION(...) -#endif - -// Helper macro to detect if the debug renderer is active -#ifdef JPH_DEBUG_RENDERER - #define JPH_IF_DEBUG_RENDERER(...) __VA_ARGS__ - #define JPH_IF_NOT_DEBUG_RENDERER(...) -#else - #define JPH_IF_DEBUG_RENDERER(...) - #define JPH_IF_NOT_DEBUG_RENDERER(...) __VA_ARGS__ -#endif - -// Macro to indicate that a parameter / variable is unused -#define JPH_UNUSED(x) (void)x - -// Macro to enable floating point precise mode and to disable fused multiply add instructions -#if defined(JPH_COMPILER_GCC) || defined(JPH_CROSS_PLATFORM_DETERMINISTIC) - // We compile without -ffast-math and -ffp-contract=fast, so we don't need to disable anything - #define JPH_PRECISE_MATH_ON - #define JPH_PRECISE_MATH_OFF -#elif defined(JPH_COMPILER_CLANG) - // We compile without -ffast-math because pragma float_control(precise, on) doesn't seem to actually negate all of the -ffast-math effects and causes the unit tests to fail (even if the pragma is added to all files) - // On clang 14 and later we can turn off float contraction through a pragma (before it was buggy), so if FMA is on we can disable it through this macro - #if (defined(JPH_CPU_ARM) && !defined(JPH_PLATFORM_ANDROID) && __clang_major__ >= 16) || (defined(JPH_CPU_X86) && __clang_major__ >= 14) - #define JPH_PRECISE_MATH_ON \ - _Pragma("float_control(precise, on, push)") \ - _Pragma("clang fp contract(off)") - #define JPH_PRECISE_MATH_OFF \ - _Pragma("float_control(pop)") - #elif __clang_major__ >= 14 && (defined(JPH_USE_FMADD) || defined(FP_FAST_FMA)) - #define JPH_PRECISE_MATH_ON \ - _Pragma("clang fp contract(off)") - #define JPH_PRECISE_MATH_OFF \ - _Pragma("clang fp contract(on)") - #else - #define JPH_PRECISE_MATH_ON - #define JPH_PRECISE_MATH_OFF - #endif -#elif defined(JPH_COMPILER_MSVC) - // Unfortunately there is no way to push the state of fp_contract, so we have to assume it was turned on before JPH_PRECISE_MATH_ON - #define JPH_PRECISE_MATH_ON \ - __pragma(float_control(precise, on, push)) \ - __pragma(fp_contract(off)) - #define JPH_PRECISE_MATH_OFF \ - __pragma(fp_contract(on)) \ - __pragma(float_control(pop)) -#else - #error Undefined -#endif - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/FPControlWord.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/FPControlWord.h deleted file mode 100644 index a046bffe9f3..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/FPControlWord.h +++ /dev/null @@ -1,135 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -#if defined(JPH_CPU_WASM) - -// Not supported - -#elif defined(JPH_USE_SSE) - -/// Helper class that needs to be put on the stack to update the state of the floating point control word. -/// This state is kept per thread. -template -class FPControlWord : public NonCopyable -{ -public: - FPControlWord() - { - mPrevState = _mm_getcsr(); - _mm_setcsr((mPrevState & ~Mask) | Value); - } - - ~FPControlWord() - { - _mm_setcsr((_mm_getcsr() & ~Mask) | (mPrevState & Mask)); - } - -private: - uint mPrevState; -}; - -#elif defined(JPH_CPU_ARM) && defined(JPH_COMPILER_MSVC) - -/// Helper class that needs to be put on the stack to update the state of the floating point control word. -/// This state is kept per thread. -template -class FPControlWord : public NonCopyable -{ -public: - FPControlWord() - { - // Read state before change - _controlfp_s(&mPrevState, 0, 0); - - // Update the state - unsigned int dummy; - _controlfp_s(&dummy, Value, Mask); - } - - ~FPControlWord() - { - // Restore state - unsigned int dummy; - _controlfp_s(&dummy, mPrevState, Mask); - } - -private: - unsigned int mPrevState; -}; - -#elif defined(JPH_CPU_ARM) && defined(JPH_USE_NEON) - -/// Helper class that needs to be put on the stack to update the state of the floating point control word. -/// This state is kept per thread. -template -class FPControlWord : public NonCopyable -{ -public: - FPControlWord() - { - uint64 val; - asm volatile("mrs %0, fpcr" : "=r" (val)); - mPrevState = val; - val &= ~Mask; - val |= Value; - asm volatile("msr fpcr, %0" : /* no output */ : "r" (val)); - } - - ~FPControlWord() - { - uint64 val; - asm volatile("mrs %0, fpcr" : "=r" (val)); - val &= ~Mask; - val |= mPrevState & Mask; - asm volatile("msr fpcr, %0" : /* no output */ : "r" (val)); - } - -private: - uint64 mPrevState; -}; - -#elif defined(JPH_CPU_ARM) - -/// Helper class that needs to be put on the stack to update the state of the floating point control word. -/// This state is kept per thread. -template -class FPControlWord : public NonCopyable -{ -public: - FPControlWord() - { - uint32 val; - asm volatile("vmrs %0, fpscr" : "=r" (val)); - mPrevState = val; - val &= ~Mask; - val |= Value; - asm volatile("vmsr fpscr, %0" : /* no output */ : "r" (val)); - } - - ~FPControlWord() - { - uint32 val; - asm volatile("vmrs %0, fpscr" : "=r" (val)); - val &= ~Mask; - val |= mPrevState & Mask; - asm volatile("vmsr fpscr, %0" : /* no output */ : "r" (val)); - } - -private: - uint32 mPrevState; -}; - -#else - -#error Unsupported CPU architecture - -#endif - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/FPException.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/FPException.h deleted file mode 100644 index 3083f05c050..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/FPException.h +++ /dev/null @@ -1,74 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -#ifdef JPH_FLOATING_POINT_EXCEPTIONS_ENABLED - -#if defined(JPH_CPU_WASM) - -// Not supported -class FPExceptionsEnable { }; -class FPExceptionDisableInvalid { }; -class FPExceptionDisableDivByZero { }; - -#elif defined(JPH_USE_SSE) - -/// Enable floating point divide by zero exception and exceptions on invalid numbers -class FPExceptionsEnable : public FPControlWord<0, _MM_MASK_DIV_ZERO | _MM_MASK_INVALID> { }; - -/// Disable invalid floating point value exceptions -class FPExceptionDisableInvalid : public FPControlWord<_MM_MASK_INVALID, _MM_MASK_INVALID> { }; - -/// Disable division by zero floating point exceptions -class FPExceptionDisableDivByZero : public FPControlWord<_MM_MASK_DIV_ZERO, _MM_MASK_DIV_ZERO> { }; - -#elif defined(JPH_CPU_ARM) && defined(JPH_COMPILER_MSVC) - -/// Enable floating point divide by zero exception and exceptions on invalid numbers -class FPExceptionsEnable : public FPControlWord<0, _EM_INVALID | _EM_ZERODIVIDE> { }; - -/// Disable invalid floating point value exceptions -class FPExceptionDisableInvalid : public FPControlWord<_EM_INVALID, _EM_INVALID> { }; - -/// Disable division by zero floating point exceptions -class FPExceptionDisableDivByZero : public FPControlWord<_EM_ZERODIVIDE, _EM_ZERODIVIDE> { }; - -#elif defined(JPH_CPU_ARM) - -/// Invalid operation exception bit -static constexpr uint64 FP_IOE = 1 << 8; - -/// Enable divide by zero exception bit -static constexpr uint64 FP_DZE = 1 << 9; - -/// Enable floating point divide by zero exception and exceptions on invalid numbers -class FPExceptionsEnable : public FPControlWord { }; - -/// Disable invalid floating point value exceptions -class FPExceptionDisableInvalid : public FPControlWord<0, FP_IOE> { }; - -/// Disable division by zero floating point exceptions -class FPExceptionDisableDivByZero : public FPControlWord<0, FP_DZE> { }; - -#else - -#error Unsupported CPU architecture - -#endif - -#else - -/// Dummy implementations -class FPExceptionsEnable { }; -class FPExceptionDisableInvalid { }; -class FPExceptionDisableDivByZero { }; - -#endif - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/FPFlushDenormals.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/FPFlushDenormals.h deleted file mode 100644 index 672a19dbecf..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/FPFlushDenormals.h +++ /dev/null @@ -1,41 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -#if defined(JPH_CPU_WASM) - -// Not supported -class FPFlushDenormals { }; - -#elif defined(JPH_USE_SSE) - -/// Helper class that needs to be put on the stack to enable flushing denormals to zero -/// This can make floating point operations much faster when working with very small numbers -class FPFlushDenormals : public FPControlWord<_MM_FLUSH_ZERO_ON, _MM_FLUSH_ZERO_MASK> { }; - -#elif defined(JPH_CPU_ARM) && defined(JPH_COMPILER_MSVC) - -class FPFlushDenormals : public FPControlWord<_DN_FLUSH, _MCW_DN> { }; - -#elif defined(JPH_CPU_ARM) - -/// Flush denormals to zero bit -static constexpr uint64 FP_FZ = 1 << 24; - -/// Helper class that needs to be put on the stack to enable flushing denormals to zero -/// This can make floating point operations much faster when working with very small numbers -class FPFlushDenormals : public FPControlWord { }; - -#else - -#error Unsupported CPU architecture - -#endif - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Factory.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Factory.cpp deleted file mode 100644 index 079ffb1600e..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Factory.cpp +++ /dev/null @@ -1,87 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include - -JPH_NAMESPACE_BEGIN - -Factory *Factory::sInstance = nullptr; - -void *Factory::CreateObject(const char *inName) -{ - const RTTI *ci = Find(inName); - return ci != nullptr? ci->CreateObject() : nullptr; -} - -const RTTI *Factory::Find(const char *inName) -{ - ClassNameMap::iterator c = mClassNameMap.find(inName); - return c != mClassNameMap.end()? c->second : nullptr; -} - -const RTTI *Factory::Find(uint32 inHash) -{ - ClassHashMap::iterator c = mClassHashMap.find(inHash); - return c != mClassHashMap.end()? c->second : nullptr; -} - -bool Factory::Register(const RTTI *inRTTI) -{ - // Check if we already know the type - if (Find(inRTTI->GetName()) != nullptr) - return true; - - // Insert this class by name - mClassNameMap.try_emplace(inRTTI->GetName(), inRTTI); - - // Insert this class by hash - if (!mClassHashMap.try_emplace(inRTTI->GetHash(), inRTTI).second) - { - JPH_ASSERT(false, "Hash collision registering type!"); - return false; - } - - // Register base classes - for (int i = 0; i < inRTTI->GetBaseClassCount(); ++i) - if (!Register(inRTTI->GetBaseClass(i))) - return false; - - // Register attribute classes - for (int i = 0; i < inRTTI->GetAttributeCount(); ++i) - { - const RTTI *rtti = inRTTI->GetAttribute(i).GetMemberPrimitiveType(); - if (rtti != nullptr && !Register(rtti)) - return false; - } - - return true; -} - -bool Factory::Register(const RTTI **inRTTIs, uint inNumber) -{ - for (const RTTI **rtti = inRTTIs; rtti < inRTTIs + inNumber; ++rtti) - if (!Register(*rtti)) - return false; - - return true; -} - -void Factory::Clear() -{ - mClassNameMap.clear(); - mClassHashMap.clear(); -} - -Array Factory::GetAllClasses() const -{ - Array all_classes; - all_classes.reserve(mClassNameMap.size()); - for (const ClassNameMap::value_type &c : mClassNameMap) - all_classes.push_back(c.second); - return all_classes; -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Factory.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Factory.h deleted file mode 100644 index 557f38173f6..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Factory.h +++ /dev/null @@ -1,54 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include - -JPH_NAMESPACE_BEGIN - -/// This class is responsible for creating instances of classes based on their name or hash and is mainly used for deserialization of saved data. -class JPH_EXPORT Factory -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Create an object - void * CreateObject(const char *inName); - - /// Find type info for a specific class by name - const RTTI * Find(const char *inName); - - /// Find type info for a specific class by hash - const RTTI * Find(uint32 inHash); - - /// Register an object with the factory. Returns false on failure. - bool Register(const RTTI *inRTTI); - - /// Register a list of objects with the factory. Returns false on failure. - bool Register(const RTTI **inRTTIs, uint inNumber); - - /// Unregisters all types - void Clear(); - - /// Get all registered classes - Array GetAllClasses() const; - - /// Singleton factory instance - static Factory * sInstance; - -private: - using ClassNameMap = UnorderedMap; - - using ClassHashMap = UnorderedMap; - - /// Map of class names to type info - ClassNameMap mClassNameMap; - - // Map of class hash to type info - ClassHashMap mClassHashMap; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/FixedSizeFreeList.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/FixedSizeFreeList.h deleted file mode 100644 index 6f3839b597b..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/FixedSizeFreeList.h +++ /dev/null @@ -1,120 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -/// Class that allows lock free creation / destruction of objects (unless a new page of objects needs to be allocated) -/// It contains a fixed pool of objects and also allows batching up a lot of objects to be destroyed -/// and doing the actual free in a single atomic operation -template -class FixedSizeFreeList : public NonCopyable -{ -private: - /// Storage type for an Object - struct ObjectStorage - { - /// The object we're storing - Object mObject; - - /// When the object is freed (or in the process of being freed as a batch) this will contain the next free object - /// When an object is in use it will contain the object's index in the free list - atomic mNextFreeObject; - }; - - static_assert(alignof(ObjectStorage) == alignof(Object), "Object not properly aligned"); - - /// Access the object storage given the object index - const ObjectStorage & GetStorage(uint32 inObjectIndex) const { return mPages[inObjectIndex >> mPageShift][inObjectIndex & mObjectMask]; } - ObjectStorage & GetStorage(uint32 inObjectIndex) { return mPages[inObjectIndex >> mPageShift][inObjectIndex & mObjectMask]; } - - /// Number of objects that we currently have in the free list / new pages -#ifdef JPH_ENABLE_ASSERTS - atomic mNumFreeObjects; -#endif // JPH_ENABLE_ASSERTS - - /// Simple counter that makes the first free object pointer update with every CAS so that we don't suffer from the ABA problem - atomic mAllocationTag; - - /// Index of first free object, the first 32 bits of an object are used to point to the next free object - atomic mFirstFreeObjectAndTag; - - /// Size (in objects) of a single page - uint32 mPageSize; - - /// Number of bits to shift an object index to the right to get the page number - uint32 mPageShift; - - /// Mask to and an object index with to get the page number - uint32 mObjectMask; - - /// Total number of pages that are usable - uint32 mNumPages; - - /// Total number of objects that have been allocated - uint32 mNumObjectsAllocated; - - /// The first free object to use when the free list is empty (may need to allocate a new page) - atomic mFirstFreeObjectInNewPage; - - /// Array of pages of objects - ObjectStorage ** mPages = nullptr; - - /// Mutex that is used to allocate a new page if the storage runs out - Mutex mPageMutex; - -public: - /// Invalid index - static const uint32 cInvalidObjectIndex = 0xffffffff; - - /// Size of an object + bookkeeping for the freelist - static const int ObjectStorageSize = sizeof(ObjectStorage); - - /// Destructor - inline ~FixedSizeFreeList(); - - /// Initialize the free list, up to inMaxObjects can be allocated - inline void Init(uint inMaxObjects, uint inPageSize); - - /// Lockless construct a new object, inParameters are passed on to the constructor - template - inline uint32 ConstructObject(Parameters &&... inParameters); - - /// Lockless destruct an object and return it to the free pool - inline void DestructObject(uint32 inObjectIndex); - - /// Lockless destruct an object and return it to the free pool - inline void DestructObject(Object *inObject); - - /// A batch of objects that can be destructed - struct Batch - { - uint32 mFirstObjectIndex = cInvalidObjectIndex; - uint32 mLastObjectIndex = cInvalidObjectIndex; - uint32 mNumObjects = 0; - }; - - /// Add a object to an existing batch to be destructed. - /// Adding objects to a batch does not destroy or modify the objects, this will merely link them - /// so that the entire batch can be returned to the free list in a single atomic operation - inline void AddObjectToBatch(Batch &ioBatch, uint32 inObjectIndex); - - /// Lockless destruct batch of objects - inline void DestructObjectBatch(Batch &ioBatch); - - /// Access an object by index. - inline Object & Get(uint32 inObjectIndex) { return GetStorage(inObjectIndex).mObject; } - - /// Access an object by index. - inline const Object & Get(uint32 inObjectIndex) const { return GetStorage(inObjectIndex).mObject; } -}; - -JPH_NAMESPACE_END - -#include "FixedSizeFreeList.inl" diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/FixedSizeFreeList.inl b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/FixedSizeFreeList.inl deleted file mode 100644 index dbaae4377b7..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/FixedSizeFreeList.inl +++ /dev/null @@ -1,211 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -JPH_NAMESPACE_BEGIN - -template -FixedSizeFreeList::~FixedSizeFreeList() -{ - // Check if we got our Init call - if (mPages != nullptr) - { - // Ensure everything is freed before the freelist is destructed - JPH_ASSERT(mNumFreeObjects.load(memory_order_relaxed) == mNumPages * mPageSize); - - // Free memory for pages - uint32 num_pages = mNumObjectsAllocated / mPageSize; - for (uint32 page = 0; page < num_pages; ++page) - AlignedFree(mPages[page]); - Free(mPages); - } -} - -template -void FixedSizeFreeList::Init(uint inMaxObjects, uint inPageSize) -{ - // Check sanity - JPH_ASSERT(inPageSize > 0 && IsPowerOf2(inPageSize)); - JPH_ASSERT(mPages == nullptr); - - // Store configuration parameters - mNumPages = (inMaxObjects + inPageSize - 1) / inPageSize; - mPageSize = inPageSize; - mPageShift = CountTrailingZeros(inPageSize); - mObjectMask = inPageSize - 1; - JPH_IF_ENABLE_ASSERTS(mNumFreeObjects = mNumPages * inPageSize;) - - // Allocate page table - mPages = reinterpret_cast(Allocate(mNumPages * sizeof(ObjectStorage *))); - - // We didn't yet use any objects of any page - mNumObjectsAllocated = 0; - mFirstFreeObjectInNewPage = 0; - - // Start with 1 as the first tag - mAllocationTag = 1; - - // Set first free object (with tag 0) - mFirstFreeObjectAndTag = cInvalidObjectIndex; -} - -template -template -uint32 FixedSizeFreeList::ConstructObject(Parameters &&... inParameters) -{ - for (;;) - { - // Get first object from the linked list - uint64 first_free_object_and_tag = mFirstFreeObjectAndTag.load(memory_order_acquire); - uint32 first_free = uint32(first_free_object_and_tag); - if (first_free == cInvalidObjectIndex) - { - // The free list is empty, we take an object from the page that has never been used before - first_free = mFirstFreeObjectInNewPage.fetch_add(1, memory_order_relaxed); - if (first_free >= mNumObjectsAllocated) - { - // Allocate new page - lock_guard lock(mPageMutex); - while (first_free >= mNumObjectsAllocated) - { - uint32 next_page = mNumObjectsAllocated / mPageSize; - if (next_page == mNumPages) - return cInvalidObjectIndex; // Out of space! - mPages[next_page] = reinterpret_cast(AlignedAllocate(mPageSize * sizeof(ObjectStorage), max(alignof(ObjectStorage), JPH_CACHE_LINE_SIZE))); - mNumObjectsAllocated += mPageSize; - } - } - - // Allocation successful - JPH_IF_ENABLE_ASSERTS(mNumFreeObjects.fetch_sub(1, memory_order_relaxed);) - ObjectStorage &storage = GetStorage(first_free); - ::new (&storage.mObject) Object(std::forward(inParameters)...); - storage.mNextFreeObject.store(first_free, memory_order_release); - return first_free; - } - else - { - // Load next pointer - uint32 new_first_free = GetStorage(first_free).mNextFreeObject.load(memory_order_acquire); - - // Construct a new first free object tag - uint64 new_first_free_object_and_tag = uint64(new_first_free) + (uint64(mAllocationTag.fetch_add(1, memory_order_relaxed)) << 32); - - // Compare and swap - if (mFirstFreeObjectAndTag.compare_exchange_weak(first_free_object_and_tag, new_first_free_object_and_tag, memory_order_release)) - { - // Allocation successful - JPH_IF_ENABLE_ASSERTS(mNumFreeObjects.fetch_sub(1, memory_order_relaxed);) - ObjectStorage &storage = GetStorage(first_free); - ::new (&storage.mObject) Object(std::forward(inParameters)...); - storage.mNextFreeObject.store(first_free, memory_order_release); - return first_free; - } - } - } -} - -template -void FixedSizeFreeList::AddObjectToBatch(Batch &ioBatch, uint32 inObjectIndex) -{ - JPH_ASSERT(GetStorage(inObjectIndex).mNextFreeObject.load(memory_order_relaxed) == inObjectIndex, "Trying to add a object to the batch that is already in a free list"); - JPH_ASSERT(ioBatch.mNumObjects != uint32(-1), "Trying to reuse a batch that has already been freed"); - - // Link object in batch to free - if (ioBatch.mFirstObjectIndex == cInvalidObjectIndex) - ioBatch.mFirstObjectIndex = inObjectIndex; - else - GetStorage(ioBatch.mLastObjectIndex).mNextFreeObject.store(inObjectIndex, memory_order_release); - ioBatch.mLastObjectIndex = inObjectIndex; - ioBatch.mNumObjects++; -} - -template -void FixedSizeFreeList::DestructObjectBatch(Batch &ioBatch) -{ - if (ioBatch.mFirstObjectIndex != cInvalidObjectIndex) - { - // Call destructors - if constexpr (!is_trivially_destructible()) - { - uint32 object_idx = ioBatch.mFirstObjectIndex; - do - { - ObjectStorage &storage = GetStorage(object_idx); - storage.mObject.~Object(); - object_idx = storage.mNextFreeObject.load(memory_order_relaxed); - } - while (object_idx != cInvalidObjectIndex); - } - - // Add to objects free list - ObjectStorage &storage = GetStorage(ioBatch.mLastObjectIndex); - for (;;) - { - // Get first object from the list - uint64 first_free_object_and_tag = mFirstFreeObjectAndTag.load(memory_order_acquire); - uint32 first_free = uint32(first_free_object_and_tag); - - // Make it the next pointer of the last object in the batch that is to be freed - storage.mNextFreeObject.store(first_free, memory_order_release); - - // Construct a new first free object tag - uint64 new_first_free_object_and_tag = uint64(ioBatch.mFirstObjectIndex) + (uint64(mAllocationTag.fetch_add(1, memory_order_relaxed)) << 32); - - // Compare and swap - if (mFirstFreeObjectAndTag.compare_exchange_weak(first_free_object_and_tag, new_first_free_object_and_tag, memory_order_release)) - { - // Free successful - JPH_IF_ENABLE_ASSERTS(mNumFreeObjects.fetch_add(ioBatch.mNumObjects, memory_order_relaxed);) - - // Mark the batch as freed -#ifdef JPH_ENABLE_ASSERTS - ioBatch.mNumObjects = uint32(-1); -#endif - return; - } - } - } -} - -template -void FixedSizeFreeList::DestructObject(uint32 inObjectIndex) -{ - JPH_ASSERT(inObjectIndex != cInvalidObjectIndex); - - // Call destructor - ObjectStorage &storage = GetStorage(inObjectIndex); - storage.mObject.~Object(); - - // Add to object free list - for (;;) - { - // Get first object from the list - uint64 first_free_object_and_tag = mFirstFreeObjectAndTag.load(memory_order_acquire); - uint32 first_free = uint32(first_free_object_and_tag); - - // Make it the next pointer of the last object in the batch that is to be freed - storage.mNextFreeObject.store(first_free, memory_order_release); - - // Construct a new first free object tag - uint64 new_first_free_object_and_tag = uint64(inObjectIndex) + (uint64(mAllocationTag.fetch_add(1, memory_order_relaxed)) << 32); - - // Compare and swap - if (mFirstFreeObjectAndTag.compare_exchange_weak(first_free_object_and_tag, new_first_free_object_and_tag, memory_order_release)) - { - // Free successful - JPH_IF_ENABLE_ASSERTS(mNumFreeObjects.fetch_add(1, memory_order_relaxed);) - return; - } - } -} - -template -inline void FixedSizeFreeList::DestructObject(Object *inObject) -{ - uint32 index = reinterpret_cast(inObject)->mNextFreeObject.load(memory_order_relaxed); - JPH_ASSERT(index < mNumObjectsAllocated); - DestructObject(index); -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/HashCombine.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/HashCombine.h deleted file mode 100644 index 372070be377..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/HashCombine.h +++ /dev/null @@ -1,97 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -JPH_NAMESPACE_BEGIN - -/// Implements the FNV-1a hash algorithm -/// @see https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function -/// @param inData Data block of bytes -/// @param inSize Number of bytes -/// @param inSeed Seed of the hash (can be used to pass in the hash of a previous operation, otherwise leave default) -/// @return Hash -inline uint64 HashBytes(const void *inData, uint inSize, uint64 inSeed = 0xcbf29ce484222325UL) -{ - uint64 hash = inSeed; - for (const uint8 *data = reinterpret_cast(inData); data < reinterpret_cast(inData) + inSize; ++data) - { - hash = hash ^ uint64(*data); - hash = hash * 0x100000001b3UL; - } - return hash; -} - -/// A 64 bit hash function by Thomas Wang, Jan 1997 -/// See: http://web.archive.org/web/20071223173210/http://www.concentric.net/~Ttwang/tech/inthash.htm -/// @param inValue Value to hash -/// @return Hash -inline uint64 Hash64(uint64 inValue) -{ - uint64 hash = inValue; - hash = (~hash) + (hash << 21); // hash = (hash << 21) - hash - 1; - hash = hash ^ (hash >> 24); - hash = (hash + (hash << 3)) + (hash << 8); // hash * 265 - hash = hash ^ (hash >> 14); - hash = (hash + (hash << 2)) + (hash << 4); // hash * 21 - hash = hash ^ (hash >> 28); - hash = hash + (hash << 31); - return hash; -} - -/// @brief Helper function that hashes a single value into ioSeed -/// Taken from: https://stackoverflow.com/questions/2590677/how-do-i-combine-hash-values-in-c0x -template -inline void HashCombineHelper(size_t &ioSeed, const T &inValue) -{ - std::hash hasher; - ioSeed ^= hasher(inValue) + 0x9e3779b9 + (ioSeed << 6) + (ioSeed >> 2); -} - -/// Hash combiner to use a custom struct in an unordered map or set -/// -/// Usage: -/// -/// struct SomeHashKey -/// { -/// std::string key1; -/// std::string key2; -/// bool key3; -/// }; -/// -/// JPH_MAKE_HASHABLE(SomeHashKey, t.key1, t.key2, t.key3) -template -inline void HashCombine(std::size_t &ioSeed, Values... inValues) -{ - // Hash all values together using a fold expression - (HashCombineHelper(ioSeed, inValues), ...); -} - -JPH_NAMESPACE_END - -JPH_SUPPRESS_WARNING_PUSH -JPH_CLANG_SUPPRESS_WARNING("-Wc++98-compat-pedantic") - -#define JPH_MAKE_HASH_STRUCT(type, name, ...) \ - struct [[nodiscard]] name \ - { \ - std::size_t operator()(const type &t) const \ - { \ - std::size_t ret = 0; \ - ::JPH::HashCombine(ret, __VA_ARGS__); \ - return ret; \ - } \ - }; - -#define JPH_MAKE_HASHABLE(type, ...) \ - JPH_SUPPRESS_WARNING_PUSH \ - JPH_SUPPRESS_WARNINGS \ - namespace std \ - { \ - template<> \ - JPH_MAKE_HASH_STRUCT(type, hash, __VA_ARGS__) \ - } \ - JPH_SUPPRESS_WARNING_POP - -JPH_SUPPRESS_WARNING_POP diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/InsertionSort.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/InsertionSort.h deleted file mode 100644 index 8cd8798be20..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/InsertionSort.h +++ /dev/null @@ -1,58 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2022 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -JPH_NAMESPACE_BEGIN - -/// Implementation of the insertion sort algorithm. -template -inline void InsertionSort(Iterator inBegin, Iterator inEnd, Compare inCompare) -{ - // Empty arrays don't need to be sorted - if (inBegin != inEnd) - { - // Start at the second element - for (Iterator i = inBegin + 1; i != inEnd; ++i) - { - // Move this element to a temporary value - auto x = std::move(*i); - - // Check if the element goes before inBegin (we can't decrement the iterator before inBegin so this needs to be a separate branch) - if (inCompare(x, *inBegin)) - { - // Move all elements to the right to make space for x - Iterator prev; - for (Iterator j = i; j != inBegin; j = prev) - { - prev = j - 1; - *j = *prev; - } - - // Move x to the first place - *inBegin = std::move(x); - } - else - { - // Move elements to the right as long as they are bigger than x - Iterator j = i; - for (Iterator prev = j - 1; inCompare(x, *prev); j = prev, --prev) - *j = std::move(*prev); - - // Move x into place - *j = std::move(x); - } - } - } -} - -/// Implementation of insertion sort algorithm without comparator. -template -inline void InsertionSort(Iterator inBegin, Iterator inEnd) -{ - std::less<> compare; - InsertionSort(inBegin, inEnd, compare); -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/IssueReporting.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/IssueReporting.cpp deleted file mode 100644 index e4efe126b4a..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/IssueReporting.cpp +++ /dev/null @@ -1,31 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -JPH_SUPPRESS_WARNINGS_STD_BEGIN -#include -JPH_SUPPRESS_WARNINGS_STD_END - -JPH_NAMESPACE_BEGIN - -static void DummyTrace([[maybe_unused]] const char *inFMT, ...) -{ - JPH_ASSERT(false); -}; - -TraceFunction Trace = DummyTrace; - -#ifdef JPH_ENABLE_ASSERTS - -static bool DummyAssertFailed(const char *inExpression, const char *inMessage, const char *inFile, uint inLine) -{ - return true; // Trigger breakpoint -}; - -AssertFailedFunction AssertFailed = DummyAssertFailed; - -#endif // JPH_ENABLE_ASSERTS - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/IssueReporting.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/IssueReporting.h deleted file mode 100644 index 8a5dc2fa667..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/IssueReporting.h +++ /dev/null @@ -1,38 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -JPH_NAMESPACE_BEGIN - -/// Trace function, needs to be overridden by application. This should output a line of text to the log / TTY. -using TraceFunction = void (*)(const char *inFMT, ...); -JPH_EXPORT extern TraceFunction Trace; - -// Always turn on asserts in Debug mode -#if defined(_DEBUG) && !defined(JPH_ENABLE_ASSERTS) - #define JPH_ENABLE_ASSERTS -#endif - -#ifdef JPH_ENABLE_ASSERTS - /// Function called when an assertion fails. This function should return true if a breakpoint needs to be triggered - using AssertFailedFunction = bool(*)(const char *inExpression, const char *inMessage, const char *inFile, uint inLine); - JPH_EXPORT extern AssertFailedFunction AssertFailed; - - // Helper functions to pass message on to failed function - struct AssertLastParam { }; - inline bool AssertFailedParamHelper(const char *inExpression, const char *inFile, uint inLine, AssertLastParam) { return AssertFailed(inExpression, nullptr, inFile, inLine); } - inline bool AssertFailedParamHelper(const char *inExpression, const char *inFile, uint inLine, const char *inMessage, AssertLastParam) { return AssertFailed(inExpression, inMessage, inFile, inLine); } - - /// Main assert macro, usage: JPH_ASSERT(condition, message) or JPH_ASSERT(condition) - #define JPH_ASSERT(inExpression, ...) do { if (!(inExpression) && AssertFailedParamHelper(#inExpression, __FILE__, JPH::uint(__LINE__), ##__VA_ARGS__, JPH::AssertLastParam())) JPH_BREAKPOINT; } while (false) - - #define JPH_IF_ENABLE_ASSERTS(...) __VA_ARGS__ -#else - #define JPH_ASSERT(...) ((void)0) - - #define JPH_IF_ENABLE_ASSERTS(...) -#endif // JPH_ENABLE_ASSERTS - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/JobSystem.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/JobSystem.h deleted file mode 100644 index f12f53809a2..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/JobSystem.h +++ /dev/null @@ -1,305 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -/// A class that allows units of work (Jobs) to be scheduled across multiple threads. -/// It allows dependencies between the jobs so that the jobs form a graph. -/// -/// The pattern for using this class is: -/// -/// // Create job system -/// JobSystem *job_system = new JobSystemThreadPool(...); -/// -/// // Create some jobs -/// JobHandle second_job = job_system->CreateJob("SecondJob", Color::sRed, []() { ... }, 1); // Create a job with 1 dependency -/// JobHandle first_job = job_system->CreateJob("FirstJob", Color::sGreen, [second_job]() { ....; second_job.RemoveDependency(); }, 0); // Job can start immediately, will start second job when it's done -/// JobHandle third_job = job_system->CreateJob("ThirdJob", Color::sBlue, []() { ... }, 0); // This job can run immediately as well and can run in parallel to job 1 and 2 -/// -/// // Add the jobs to the barrier so that we can execute them while we're waiting -/// Barrier *barrier = job_system->CreateBarrier(); -/// barrier->AddJob(first_job); -/// barrier->AddJob(second_job); -/// barrier->AddJob(third_job); -/// job_system->WaitForJobs(barrier); -/// -/// // Clean up -/// job_system->DestroyBarrier(barrier); -/// delete job_system; -/// -/// Jobs are guaranteed to be started in the order that their dependency counter becomes zero (in case they're scheduled on a background thread) -/// or in the order they're added to the barrier (when dependency count is zero and when executing on the thread that calls WaitForJobs). -/// -/// If you want to implement your own job system, inherit from JobSystem and implement: -/// -/// * JobSystem::GetMaxConcurrency - This should return the maximum number of jobs that can run in parallel. -/// * JobSystem::CreateJob - This should create a Job object and return it to the caller. -/// * JobSystem::FreeJob - This should free the memory associated with the job object. It is called by the Job destructor when it is Release()-ed for the last time. -/// * JobSystem::QueueJob/QueueJobs - These should store the job pointer in an internal queue to run immediately (dependencies are tracked internally, this function is called when the job can run). -/// The Job objects are reference counted and are guaranteed to stay alive during the QueueJob(s) call. If you store the job in your own data structure you need to call AddRef() to take a reference. -/// After the job has been executed you need to call Release() to release the reference. Make sure you no longer dereference the job pointer after calling Release(). -/// -/// JobSystem::Barrier is used to track the completion of a set of jobs. Jobs will be created by other jobs and added to the barrier while it is being waited on. This means that you cannot -/// create a dependency graph beforehand as the graph changes while jobs are running. Implement the following functions: -/// -/// * Barrier::AddJob/AddJobs - Add a job to the barrier, any call to WaitForJobs will now also wait for this job to complete. -/// If you store the job in a data structure in the Barrier you need to call AddRef() on the job to keep it alive and Release() after you're done with it. -/// * Barrier::OnJobFinished - This function is called when a job has finished executing, you can use this to track completion and remove the job from the list of jobs to wait on. -/// -/// The functions on JobSystem that need to be implemented to support barriers are: -/// -/// * JobSystem::CreateBarrier - Create a new barrier. -/// * JobSystem::DestroyBarrier - Destroy a barrier. -/// * JobSystem::WaitForJobs - This is the main function that is used to wait for all jobs that have been added to a Barrier. WaitForJobs can execute jobs that have -/// been added to the barrier while waiting. It is not wise to execute other jobs that touch physics structures as this can cause race conditions and deadlocks. Please keep in mind that the barrier is -/// only intended to wait on the completion of the Jolt jobs added to it, if you scheduled any jobs in your engine's job system to execute the Jolt jobs as part of QueueJob/QueueJobs, you might still need -/// to wait for these in this function after the barrier is finished waiting. -/// -/// An example implementation is JobSystemThreadPool. If you don't want to write the Barrier class you can also inherit from JobSystemWithBarrier. -class JPH_EXPORT JobSystem : public NonCopyable -{ -protected: - class Job; - -public: - JPH_OVERRIDE_NEW_DELETE - - /// A job handle contains a reference to a job. The job will be deleted as soon as there are no JobHandles. - /// referring to the job and when it is not in the job queue / being processed. - class JobHandle : private Ref - { - public: - /// Constructor - inline JobHandle() = default; - inline JobHandle(const JobHandle &inHandle) = default; - inline JobHandle(JobHandle &&inHandle) noexcept : Ref(std::move(inHandle)) { } - - /// Constructor, only to be used by JobSystem - inline explicit JobHandle(Job *inJob) : Ref(inJob) { } - - /// Assignment - inline JobHandle & operator = (const JobHandle &inHandle) = default; - inline JobHandle & operator = (JobHandle &&inHandle) noexcept = default; - - /// Check if this handle contains a job - inline bool IsValid() const { return GetPtr() != nullptr; } - - /// Check if this job has finished executing - inline bool IsDone() const { return GetPtr() != nullptr && GetPtr()->IsDone(); } - - /// Add to the dependency counter. - inline void AddDependency(int inCount = 1) const { GetPtr()->AddDependency(inCount); } - - /// Remove from the dependency counter. Job will start whenever the dependency counter reaches zero - /// and if it does it is no longer valid to call the AddDependency/RemoveDependency functions. - inline void RemoveDependency(int inCount = 1) const { GetPtr()->RemoveDependencyAndQueue(inCount); } - - /// Remove a dependency from a batch of jobs at once, this can be more efficient than removing them one by one as it requires less locking - static inline void sRemoveDependencies(const JobHandle *inHandles, uint inNumHandles, int inCount = 1); - - /// Helper function to remove dependencies on a static array of job handles - template - static inline void sRemoveDependencies(StaticArray &inHandles, int inCount = 1) - { - sRemoveDependencies(inHandles.data(), inHandles.size(), inCount); - } - - /// Inherit the GetPtr function, only to be used by the JobSystem - using Ref::GetPtr; - }; - - /// A job barrier keeps track of a number of jobs and allows waiting until they are all completed. - class Barrier : public NonCopyable - { - public: - JPH_OVERRIDE_NEW_DELETE - - /// Add a job to this barrier - /// Note that jobs can keep being added to the barrier while waiting for the barrier - virtual void AddJob(const JobHandle &inJob) = 0; - - /// Add multiple jobs to this barrier - /// Note that jobs can keep being added to the barrier while waiting for the barrier - virtual void AddJobs(const JobHandle *inHandles, uint inNumHandles) = 0; - - protected: - /// Job needs to be able to call OnJobFinished - friend class Job; - - /// Destructor, you should call JobSystem::DestroyBarrier instead of destructing this object directly - virtual ~Barrier() = default; - - /// Called by a Job to mark that it is finished - virtual void OnJobFinished(Job *inJob) = 0; - }; - - /// Main function of the job - using JobFunction = function; - - /// Destructor - virtual ~JobSystem() = default; - - /// Get maximum number of concurrently executing jobs - virtual int GetMaxConcurrency() const = 0; - - /// Create a new job, the job is started immediately if inNumDependencies == 0 otherwise it starts when - /// RemoveDependency causes the dependency counter to reach 0. - virtual JobHandle CreateJob(const char *inName, ColorArg inColor, const JobFunction &inJobFunction, uint32 inNumDependencies = 0) = 0; - - /// Create a new barrier, used to wait on jobs - virtual Barrier * CreateBarrier() = 0; - - /// Destroy a barrier when it is no longer used. The barrier should be empty at this point. - virtual void DestroyBarrier(Barrier *inBarrier) = 0; - - /// Wait for a set of jobs to be finished, note that only 1 thread can be waiting on a barrier at a time - virtual void WaitForJobs(Barrier *inBarrier) = 0; - -protected: - /// A class that contains information for a single unit of work - class Job - { - public: - JPH_OVERRIDE_NEW_DELETE - - /// Constructor - Job([[maybe_unused]] const char *inJobName, [[maybe_unused]] ColorArg inColor, JobSystem *inJobSystem, const JobFunction &inJobFunction, uint32 inNumDependencies) : - #if defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED) - mJobName(inJobName), - mColor(inColor), - #endif // defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED) - mJobSystem(inJobSystem), - mJobFunction(inJobFunction), - mNumDependencies(inNumDependencies) - { - } - - /// Get the jobs system to which this job belongs - inline JobSystem * GetJobSystem() { return mJobSystem; } - - /// Add or release a reference to this object - inline void AddRef() - { - // Adding a reference can use relaxed memory ordering - mReferenceCount.fetch_add(1, memory_order_relaxed); - } - inline void Release() - { - // Releasing a reference must use release semantics... - if (mReferenceCount.fetch_sub(1, memory_order_release) == 1) - { - // ... so that we can use acquire to ensure that we see any updates from other threads that released a ref before freeing the job - atomic_thread_fence(memory_order_acquire); - mJobSystem->FreeJob(this); - } - } - - /// Add to the dependency counter. - inline void AddDependency(int inCount); - - /// Remove from the dependency counter. Returns true whenever the dependency counter reaches zero - /// and if it does it is no longer valid to call the AddDependency/RemoveDependency functions. - inline bool RemoveDependency(int inCount); - - /// Remove from the dependency counter. Job will be queued whenever the dependency counter reaches zero - /// and if it does it is no longer valid to call the AddDependency/RemoveDependency functions. - inline void RemoveDependencyAndQueue(int inCount); - - /// Set the job barrier that this job belongs to and returns false if this was not possible because the job already finished - inline bool SetBarrier(Barrier *inBarrier) - { - intptr_t barrier = 0; - if (mBarrier.compare_exchange_strong(barrier, reinterpret_cast(inBarrier), memory_order_relaxed)) - return true; - JPH_ASSERT(barrier == cBarrierDoneState, "A job can only belong to 1 barrier"); - return false; - } - - /// Run the job function, returns the number of dependencies that this job still has or cExecutingState or cDoneState - inline uint32 Execute() - { - // Transition job to executing state - uint32 state = 0; // We can only start running with a dependency counter of 0 - if (!mNumDependencies.compare_exchange_strong(state, cExecutingState, memory_order_acquire)) - return state; // state is updated by compare_exchange_strong to the current value - - // Run the job function - { - JPH_PROFILE(mJobName, mColor.GetUInt32()); - mJobFunction(); - } - - // Fetch the barrier pointer and exchange it for the done state, so we're sure that no barrier gets set after we want to call the callback - intptr_t barrier = mBarrier.load(memory_order_relaxed); - for (;;) - { - if (mBarrier.compare_exchange_weak(barrier, cBarrierDoneState, memory_order_relaxed)) - break; - } - JPH_ASSERT(barrier != cBarrierDoneState); - - // Mark job as done - state = cExecutingState; - mNumDependencies.compare_exchange_strong(state, cDoneState, memory_order_relaxed); - JPH_ASSERT(state == cExecutingState); - - // Notify the barrier after we've changed the job to the done state so that any thread reading the state after receiving the callback will see that the job has finished - if (barrier != 0) - reinterpret_cast(barrier)->OnJobFinished(this); - - return cDoneState; - } - - /// Test if the job can be executed - inline bool CanBeExecuted() const { return mNumDependencies.load(memory_order_relaxed) == 0; } - - /// Test if the job finished executing - inline bool IsDone() const { return mNumDependencies.load(memory_order_relaxed) == cDoneState; } - - #if defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED) - /// Get the name of the job - const char * GetName() const { return mJobName; } - #endif // defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED) - - static constexpr uint32 cExecutingState = 0xe0e0e0e0; ///< Value of mNumDependencies when job is executing - static constexpr uint32 cDoneState = 0xd0d0d0d0; ///< Value of mNumDependencies when job is done executing - - static constexpr intptr_t cBarrierDoneState = ~intptr_t(0); ///< Value to use when the barrier has been triggered - -private: - #if defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED) - const char * mJobName; ///< Name of the job - Color mColor; ///< Color of the job in the profiler - #endif // defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED) - JobSystem * mJobSystem; ///< The job system we belong to - atomic mBarrier = 0; ///< Barrier that this job is associated with (is a Barrier pointer) - JobFunction mJobFunction; ///< Main job function - atomic mReferenceCount = 0; ///< Amount of JobHandles pointing to this job - atomic mNumDependencies; ///< Amount of jobs that need to complete before this job can run - }; - - /// Adds a job to the job queue - virtual void QueueJob(Job *inJob) = 0; - - /// Adds a number of jobs at once to the job queue - virtual void QueueJobs(Job **inJobs, uint inNumJobs) = 0; - - /// Frees a job - virtual void FreeJob(Job *inJob) = 0; -}; - -using JobHandle = JobSystem::JobHandle; - -JPH_NAMESPACE_END - -#include "JobSystem.inl" diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/JobSystem.inl b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/JobSystem.inl deleted file mode 100644 index bee4f13b505..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/JobSystem.inl +++ /dev/null @@ -1,56 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -JPH_NAMESPACE_BEGIN - -void JobSystem::Job::AddDependency(int inCount) -{ - JPH_IF_ENABLE_ASSERTS(uint32 old_value =) mNumDependencies.fetch_add(inCount, memory_order_relaxed); - JPH_ASSERT(old_value > 0 && old_value != cExecutingState && old_value != cDoneState, "Job is queued, running or done, it is not allowed to add a dependency to a running job"); -} - -bool JobSystem::Job::RemoveDependency(int inCount) -{ - uint32 old_value = mNumDependencies.fetch_sub(inCount, memory_order_release); - JPH_ASSERT(old_value != cExecutingState && old_value != cDoneState, "Job is running or done, it is not allowed to add a dependency to a running job"); - uint32 new_value = old_value - inCount; - JPH_ASSERT(old_value > new_value, "Test wrap around, this is a logic error"); - return new_value == 0; -} - -void JobSystem::Job::RemoveDependencyAndQueue(int inCount) -{ - if (RemoveDependency(inCount)) - mJobSystem->QueueJob(this); -} - -void JobSystem::JobHandle::sRemoveDependencies(const JobHandle *inHandles, uint inNumHandles, int inCount) -{ - JPH_PROFILE_FUNCTION(); - - JPH_ASSERT(inNumHandles > 0); - - // Get the job system, all jobs should be part of the same job system - JobSystem *job_system = inHandles->GetPtr()->GetJobSystem(); - - // Allocate a buffer to store the jobs that need to be queued - Job **jobs_to_queue = (Job **)JPH_STACK_ALLOC(inNumHandles * sizeof(Job *)); - Job **next_job = jobs_to_queue; - - // Remove the dependencies on all jobs - for (const JobHandle *handle = inHandles, *handle_end = inHandles + inNumHandles; handle < handle_end; ++handle) - { - Job *job = handle->GetPtr(); - JPH_ASSERT(job->GetJobSystem() == job_system); // All jobs should belong to the same job system - if (job->RemoveDependency(inCount)) - *(next_job++) = job; - } - - // If any jobs need to be scheduled, schedule them as a batch - uint num_jobs_to_queue = uint(next_job - jobs_to_queue); - if (num_jobs_to_queue != 0) - job_system->QueueJobs(jobs_to_queue, num_jobs_to_queue); -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/JobSystemSingleThreaded.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/JobSystemSingleThreaded.cpp deleted file mode 100644 index 5f09d1a04ce..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/JobSystemSingleThreaded.cpp +++ /dev/null @@ -1,65 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2023 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include - -JPH_NAMESPACE_BEGIN - -void JobSystemSingleThreaded::Init(uint inMaxJobs) -{ - mJobs.Init(inMaxJobs, inMaxJobs); -} - -JobHandle JobSystemSingleThreaded::CreateJob(const char *inJobName, ColorArg inColor, const JobFunction &inJobFunction, uint32 inNumDependencies) -{ - // Construct an object - uint32 index = mJobs.ConstructObject(inJobName, inColor, this, inJobFunction, inNumDependencies); - JPH_ASSERT(index != AvailableJobs::cInvalidObjectIndex); - Job *job = &mJobs.Get(index); - - // Construct handle to keep a reference, the job is queued below and will immediately complete - JobHandle handle(job); - - // If there are no dependencies, queue the job now - if (inNumDependencies == 0) - QueueJob(job); - - // Return the handle - return handle; -} - -void JobSystemSingleThreaded::FreeJob(Job *inJob) -{ - mJobs.DestructObject(inJob); -} - -void JobSystemSingleThreaded::QueueJob(Job *inJob) -{ - inJob->Execute(); -} - -void JobSystemSingleThreaded::QueueJobs(Job **inJobs, uint inNumJobs) -{ - for (uint i = 0; i < inNumJobs; ++i) - QueueJob(inJobs[i]); -} - -JobSystem::Barrier *JobSystemSingleThreaded::CreateBarrier() -{ - return &mDummyBarrier; -} - -void JobSystemSingleThreaded::DestroyBarrier(Barrier *inBarrier) -{ - // There's nothing to do here, the barrier is just a dummy -} - -void JobSystemSingleThreaded::WaitForJobs(Barrier *inBarrier) -{ - // There's nothing to do here, the barrier is just a dummy, we just execute the jobs immediately -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/JobSystemSingleThreaded.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/JobSystemSingleThreaded.h deleted file mode 100644 index 4db599b3135..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/JobSystemSingleThreaded.h +++ /dev/null @@ -1,62 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2023 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include - -JPH_NAMESPACE_BEGIN - -/// Implementation of a JobSystem without threads, runs jobs as soon as they are added -class JPH_EXPORT JobSystemSingleThreaded final : public JobSystem -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Constructor - JobSystemSingleThreaded() = default; - explicit JobSystemSingleThreaded(uint inMaxJobs) { Init(inMaxJobs); } - - /// Initialize the job system - /// @param inMaxJobs Max number of jobs that can be allocated at any time - void Init(uint inMaxJobs); - - // See JobSystem - virtual int GetMaxConcurrency() const override { return 1; } - virtual JobHandle CreateJob(const char *inName, ColorArg inColor, const JobFunction &inJobFunction, uint32 inNumDependencies = 0) override; - virtual Barrier * CreateBarrier() override; - virtual void DestroyBarrier(Barrier *inBarrier) override; - virtual void WaitForJobs(Barrier *inBarrier) override; - -protected: - // Dummy implementation of Barrier, all jobs are executed immediately - class BarrierImpl : public Barrier - { - public: - JPH_OVERRIDE_NEW_DELETE - - // See Barrier - virtual void AddJob(const JobHandle &inJob) override { /* We don't need to track jobs */ } - virtual void AddJobs(const JobHandle *inHandles, uint inNumHandles) override { /* We don't need to track jobs */ } - - protected: - /// Called by a Job to mark that it is finished - virtual void OnJobFinished(Job *inJob) override { /* We don't need to track jobs */ } - }; - - // See JobSystem - virtual void QueueJob(Job *inJob) override; - virtual void QueueJobs(Job **inJobs, uint inNumJobs) override; - virtual void FreeJob(Job *inJob) override; - - /// Shared barrier since the barrier implementation does nothing - BarrierImpl mDummyBarrier; - - /// Array of jobs (fixed size) - using AvailableJobs = FixedSizeFreeList; - AvailableJobs mJobs; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/JobSystemThreadPool.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/JobSystemThreadPool.cpp deleted file mode 100644 index 64beb5b7f24..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/JobSystemThreadPool.cpp +++ /dev/null @@ -1,354 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include - -#ifdef JPH_PLATFORM_WINDOWS - JPH_SUPPRESS_WARNING_PUSH - JPH_MSVC_SUPPRESS_WARNING(5039) // winbase.h(13179): warning C5039: 'TpSetCallbackCleanupGroup': pointer or reference to potentially throwing function passed to 'extern "C"' function under -EHc. Undefined behavior may occur if this function throws an exception. - #define WIN32_LEAN_AND_MEAN -#ifndef JPH_COMPILER_MINGW - #include -#else - #include -#endif - - JPH_SUPPRESS_WARNING_POP -#endif -#ifdef JPH_PLATFORM_LINUX - #include -#endif - -JPH_NAMESPACE_BEGIN - -void JobSystemThreadPool::Init(uint inMaxJobs, uint inMaxBarriers, int inNumThreads) -{ - JobSystemWithBarrier::Init(inMaxBarriers); - - // Init freelist of jobs - mJobs.Init(inMaxJobs, inMaxJobs); - - // Init queue - for (atomic &j : mQueue) - j = nullptr; - - // Start the worker threads - StartThreads(inNumThreads); -} - -JobSystemThreadPool::JobSystemThreadPool(uint inMaxJobs, uint inMaxBarriers, int inNumThreads) -{ - Init(inMaxJobs, inMaxBarriers, inNumThreads); -} - -void JobSystemThreadPool::StartThreads(int inNumThreads) -{ - // Auto detect number of threads - if (inNumThreads < 0) - inNumThreads = thread::hardware_concurrency() - 1; - - // If no threads are requested we're done - if (inNumThreads == 0) - return; - - // Don't quit the threads - mQuit = false; - - // Allocate heads - mHeads = reinterpret_cast *>(Allocate(sizeof(atomic) * inNumThreads)); - for (int i = 0; i < inNumThreads; ++i) - mHeads[i] = 0; - - // Start running threads - JPH_ASSERT(mThreads.empty()); - mThreads.reserve(inNumThreads); - for (int i = 0; i < inNumThreads; ++i) - mThreads.emplace_back([this, i] { ThreadMain(i); }); -} - -JobSystemThreadPool::~JobSystemThreadPool() -{ - // Stop all worker threads - StopThreads(); -} - -void JobSystemThreadPool::StopThreads() -{ - if (mThreads.empty()) - return; - - // Signal threads that we want to stop and wake them up - mQuit = true; - mSemaphore.Release((uint)mThreads.size()); - - // Wait for all threads to finish - for (thread &t : mThreads) - if (t.joinable()) - t.join(); - - // Delete all threads - mThreads.clear(); - - // Ensure that there are no lingering jobs in the queue - for (uint head = 0; head != mTail; ++head) - { - // Fetch job - Job *job_ptr = mQueue[head & (cQueueLength - 1)].exchange(nullptr); - if (job_ptr != nullptr) - { - // And execute it - job_ptr->Execute(); - job_ptr->Release(); - } - } - - // Destroy heads and reset tail - Free(mHeads); - mHeads = nullptr; - mTail = 0; -} - -JobHandle JobSystemThreadPool::CreateJob(const char *inJobName, ColorArg inColor, const JobFunction &inJobFunction, uint32 inNumDependencies) -{ - JPH_PROFILE_FUNCTION(); - - // Loop until we can get a job from the free list - uint32 index; - for (;;) - { - index = mJobs.ConstructObject(inJobName, inColor, this, inJobFunction, inNumDependencies); - if (index != AvailableJobs::cInvalidObjectIndex) - break; - JPH_ASSERT(false, "No jobs available!"); - std::this_thread::sleep_for(std::chrono::microseconds(100)); - } - Job *job = &mJobs.Get(index); - - // Construct handle to keep a reference, the job is queued below and may immediately complete - JobHandle handle(job); - - // If there are no dependencies, queue the job now - if (inNumDependencies == 0) - QueueJob(job); - - // Return the handle - return handle; -} - -void JobSystemThreadPool::FreeJob(Job *inJob) -{ - mJobs.DestructObject(inJob); -} - -uint JobSystemThreadPool::GetHead() const -{ - // Find the minimal value across all threads - uint head = mTail; - for (size_t i = 0; i < mThreads.size(); ++i) - head = min(head, mHeads[i].load()); - return head; -} - -void JobSystemThreadPool::QueueJobInternal(Job *inJob) -{ - // Add reference to job because we're adding the job to the queue - inJob->AddRef(); - - // Need to read head first because otherwise the tail can already have passed the head - // We read the head outside of the loop since it involves iterating over all threads and we only need to update - // it if there's not enough space in the queue. - uint head = GetHead(); - - for (;;) - { - // Check if there's space in the queue - uint old_value = mTail; - if (old_value - head >= cQueueLength) - { - // We calculated the head outside of the loop, update head (and we also need to update tail to prevent it from passing head) - head = GetHead(); - old_value = mTail; - - // Second check if there's space in the queue - if (old_value - head >= cQueueLength) - { - // Wake up all threads in order to ensure that they can clear any nullptrs they may not have processed yet - mSemaphore.Release((uint)mThreads.size()); - - // Sleep a little (we have to wait for other threads to update their head pointer in order for us to be able to continue) - std::this_thread::sleep_for(std::chrono::microseconds(100)); - continue; - } - } - - // Write the job pointer if the slot is empty - Job *expected_job = nullptr; - bool success = mQueue[old_value & (cQueueLength - 1)].compare_exchange_strong(expected_job, inJob); - - // Regardless of who wrote the slot, we will update the tail (if the successful thread got scheduled out - // after writing the pointer we still want to be able to continue) - mTail.compare_exchange_strong(old_value, old_value + 1); - - // If we successfully added our job we're done - if (success) - break; - } -} - -void JobSystemThreadPool::QueueJob(Job *inJob) -{ - JPH_PROFILE_FUNCTION(); - - // If we have no worker threads, we can't queue the job either. We assume in this case that the job will be added to a barrier and that the barrier will execute the job when it's Wait() function is called. - if (mThreads.empty()) - return; - - // Queue the job - QueueJobInternal(inJob); - - // Wake up thread - mSemaphore.Release(); -} - -void JobSystemThreadPool::QueueJobs(Job **inJobs, uint inNumJobs) -{ - JPH_PROFILE_FUNCTION(); - - JPH_ASSERT(inNumJobs > 0); - - // If we have no worker threads, we can't queue the job either. We assume in this case that the job will be added to a barrier and that the barrier will execute the job when it's Wait() function is called. - if (mThreads.empty()) - return; - - // Queue all jobs - for (Job **job = inJobs, **job_end = inJobs + inNumJobs; job < job_end; ++job) - QueueJobInternal(*job); - - // Wake up threads - mSemaphore.Release(min(inNumJobs, (uint)mThreads.size())); -} - -#if defined(JPH_PLATFORM_WINDOWS) - -#if !defined(JPH_COMPILER_MINGW) // MinGW doesn't support __try/__except) - // Sets the current thread name in MSVC debugger - static void RaiseThreadNameException(const char *inName) - { - #pragma pack(push, 8) - - struct THREADNAME_INFO - { - DWORD dwType; // Must be 0x1000. - LPCSTR szName; // Pointer to name (in user addr space). - DWORD dwThreadID; // Thread ID (-1=caller thread). - DWORD dwFlags; // Reserved for future use, must be zero. - }; - - #pragma pack(pop) - - THREADNAME_INFO info; - info.dwType = 0x1000; - info.szName = inName; - info.dwThreadID = (DWORD)-1; - info.dwFlags = 0; - - __try - { - RaiseException(0x406D1388, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR *)&info); - } - __except(EXCEPTION_EXECUTE_HANDLER) - { - } - } -#endif // !JPH_COMPILER_MINGW - - static void SetThreadName(const char* inName) - { - JPH_SUPPRESS_WARNING_PUSH - - // Suppress casting warning, it's fine here as GetProcAddress doesn't really return a FARPROC - JPH_CLANG_SUPPRESS_WARNING("-Wcast-function-type") // error : cast from 'FARPROC' (aka 'long long (*)()') to 'SetThreadDescriptionFunc' (aka 'long (*)(void *, const wchar_t *)') converts to incompatible function type - JPH_CLANG_SUPPRESS_WARNING("-Wcast-function-type-strict") // error : cast from 'FARPROC' (aka 'long long (*)()') to 'SetThreadDescriptionFunc' (aka 'long (*)(void *, const wchar_t *)') converts to incompatible function type - JPH_MSVC_SUPPRESS_WARNING(4191) // reinterpret_cast' : unsafe conversion from 'FARPROC' to 'SetThreadDescriptionFunc'. Calling this function through the result pointer may cause your program to fail - - using SetThreadDescriptionFunc = HRESULT(WINAPI*)(HANDLE hThread, PCWSTR lpThreadDescription); - static SetThreadDescriptionFunc SetThreadDescription = reinterpret_cast(GetProcAddress(GetModuleHandleW(L"Kernel32.dll"), "SetThreadDescription")); - - JPH_SUPPRESS_WARNING_POP - - if (SetThreadDescription) - { - wchar_t name_buffer[64] = { 0 }; - if (MultiByteToWideChar(CP_UTF8, 0, inName, -1, name_buffer, sizeof(name_buffer) / sizeof(wchar_t) - 1) == 0) - return; - - SetThreadDescription(GetCurrentThread(), name_buffer); - } -#if !defined(JPH_COMPILER_MINGW) - else if (IsDebuggerPresent()) - RaiseThreadNameException(inName); -#endif // !JPH_COMPILER_MINGW - } -#elif defined(JPH_PLATFORM_LINUX) - static void SetThreadName(const char *inName) - { - JPH_ASSERT(strlen(inName) < 16); // String will be truncated if it is longer - prctl(PR_SET_NAME, inName, 0, 0, 0); - } -#endif // JPH_PLATFORM_LINUX - -void JobSystemThreadPool::ThreadMain(int inThreadIndex) -{ - // Name the thread - char name[64]; - snprintf(name, sizeof(name), "Worker %d", int(inThreadIndex + 1)); - -#if defined(JPH_PLATFORM_WINDOWS) || defined(JPH_PLATFORM_LINUX) - SetThreadName(name); -#endif // JPH_PLATFORM_WINDOWS && !JPH_COMPILER_MINGW - - // Enable floating point exceptions - FPExceptionsEnable enable_exceptions; - JPH_UNUSED(enable_exceptions); - - JPH_PROFILE_THREAD_START(name); - - atomic &head = mHeads[inThreadIndex]; - - while (!mQuit) - { - // Wait for jobs - mSemaphore.Acquire(); - - { - JPH_PROFILE("Executing Jobs"); - - // Loop over the queue - while (head != mTail) - { - // Exchange any job pointer we find with a nullptr - atomic &job = mQueue[head & (cQueueLength - 1)]; - if (job.load() != nullptr) - { - Job *job_ptr = job.exchange(nullptr); - if (job_ptr != nullptr) - { - // And execute it - job_ptr->Execute(); - job_ptr->Release(); - } - } - head++; - } - } - } - - JPH_PROFILE_THREAD_END(); -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/JobSystemThreadPool.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/JobSystemThreadPool.h deleted file mode 100644 index 05e5ae9ba51..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/JobSystemThreadPool.h +++ /dev/null @@ -1,92 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include - -JPH_SUPPRESS_WARNINGS_STD_BEGIN -#include -JPH_SUPPRESS_WARNINGS_STD_END - -JPH_NAMESPACE_BEGIN - -// Things we're using from STL -using std::thread; - -/// Implementation of a JobSystem using a thread pool -/// -/// Note that this is considered an example implementation. It is expected that when you integrate -/// the physics engine into your own project that you'll provide your own implementation of the -/// JobSystem built on top of whatever job system your project uses. -class JPH_EXPORT JobSystemThreadPool final : public JobSystemWithBarrier -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Creates a thread pool. - /// @see JobSystemThreadPool::Init - JobSystemThreadPool(uint inMaxJobs, uint inMaxBarriers, int inNumThreads = -1); - JobSystemThreadPool() = default; - virtual ~JobSystemThreadPool() override; - - /// Initialize the thread pool - /// @param inMaxJobs Max number of jobs that can be allocated at any time - /// @param inMaxBarriers Max number of barriers that can be allocated at any time - /// @param inNumThreads Number of threads to start (the number of concurrent jobs is 1 more because the main thread will also run jobs while waiting for a barrier to complete). Use -1 to auto detect the amount of CPU's. - void Init(uint inMaxJobs, uint inMaxBarriers, int inNumThreads = -1); - - // See JobSystem - virtual int GetMaxConcurrency() const override { return int(mThreads.size()) + 1; } - virtual JobHandle CreateJob(const char *inName, ColorArg inColor, const JobFunction &inJobFunction, uint32 inNumDependencies = 0) override; - - /// Change the max concurrency after initialization - void SetNumThreads(int inNumThreads) { StopThreads(); StartThreads(inNumThreads); } - -protected: - // See JobSystem - virtual void QueueJob(Job *inJob) override; - virtual void QueueJobs(Job **inJobs, uint inNumJobs) override; - virtual void FreeJob(Job *inJob) override; - -private: - /// Start/stop the worker threads - void StartThreads(int inNumThreads); - void StopThreads(); - - /// Entry point for a thread - void ThreadMain(int inThreadIndex); - - /// Get the head of the thread that has processed the least amount of jobs - inline uint GetHead() const; - - /// Internal helper function to queue a job - inline void QueueJobInternal(Job *inJob); - - /// Array of jobs (fixed size) - using AvailableJobs = FixedSizeFreeList; - AvailableJobs mJobs; - - /// Threads running jobs - Array mThreads; - - // The job queue - static constexpr uint32 cQueueLength = 1024; - static_assert(IsPowerOf2(cQueueLength)); // We do bit operations and require queue length to be a power of 2 - atomic mQueue[cQueueLength]; - - // Head and tail of the queue, do this value modulo cQueueLength - 1 to get the element in the mQueue array - atomic * mHeads = nullptr; ///< Per executing thread the head of the current queue - alignas(JPH_CACHE_LINE_SIZE) atomic mTail = 0; ///< Tail (write end) of the queue - - // Semaphore used to signal worker threads that there is new work - Semaphore mSemaphore; - - /// Boolean to indicate that we want to stop the job system - atomic mQuit = false; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/JobSystemWithBarrier.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/JobSystemWithBarrier.cpp deleted file mode 100644 index eaaabdb0997..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/JobSystemWithBarrier.cpp +++ /dev/null @@ -1,227 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2023 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include - -JPH_SUPPRESS_WARNINGS_STD_BEGIN -#include -JPH_SUPPRESS_WARNINGS_STD_END - -JPH_NAMESPACE_BEGIN - -JobSystemWithBarrier::BarrierImpl::BarrierImpl() -{ - for (atomic &j : mJobs) - j = nullptr; -} - -JobSystemWithBarrier::BarrierImpl::~BarrierImpl() -{ - JPH_ASSERT(IsEmpty()); -} - -void JobSystemWithBarrier::BarrierImpl::AddJob(const JobHandle &inJob) -{ - JPH_PROFILE_FUNCTION(); - - bool release_semaphore = false; - - // Set the barrier on the job, this returns true if the barrier was successfully set (otherwise the job is already done and we don't need to add it to our list) - Job *job = inJob.GetPtr(); - if (job->SetBarrier(this)) - { - // If the job can be executed we want to release the semaphore an extra time to allow the waiting thread to start executing it - mNumToAcquire++; - if (job->CanBeExecuted()) - { - release_semaphore = true; - mNumToAcquire++; - } - - // Add the job to our job list - job->AddRef(); - uint write_index = mJobWriteIndex++; - while (write_index - mJobReadIndex >= cMaxJobs) - { - JPH_ASSERT(false, "Barrier full, stalling!"); - std::this_thread::sleep_for(std::chrono::microseconds(100)); - } - mJobs[write_index & (cMaxJobs - 1)] = job; - } - - // Notify waiting thread that a new executable job is available - if (release_semaphore) - mSemaphore.Release(); -} - -void JobSystemWithBarrier::BarrierImpl::AddJobs(const JobHandle *inHandles, uint inNumHandles) -{ - JPH_PROFILE_FUNCTION(); - - bool release_semaphore = false; - - for (const JobHandle *handle = inHandles, *handles_end = inHandles + inNumHandles; handle < handles_end; ++handle) - { - // Set the barrier on the job, this returns true if the barrier was successfully set (otherwise the job is already done and we don't need to add it to our list) - Job *job = handle->GetPtr(); - if (job->SetBarrier(this)) - { - // If the job can be executed we want to release the semaphore an extra time to allow the waiting thread to start executing it - mNumToAcquire++; - if (!release_semaphore && job->CanBeExecuted()) - { - release_semaphore = true; - mNumToAcquire++; - } - - // Add the job to our job list - job->AddRef(); - uint write_index = mJobWriteIndex++; - while (write_index - mJobReadIndex >= cMaxJobs) - { - JPH_ASSERT(false, "Barrier full, stalling!"); - std::this_thread::sleep_for(std::chrono::microseconds(100)); - } - mJobs[write_index & (cMaxJobs - 1)] = job; - } - } - - // Notify waiting thread that a new executable job is available - if (release_semaphore) - mSemaphore.Release(); -} - -void JobSystemWithBarrier::BarrierImpl::OnJobFinished(Job *inJob) -{ - JPH_PROFILE_FUNCTION(); - - mSemaphore.Release(); -} - -void JobSystemWithBarrier::BarrierImpl::Wait() -{ - while (mNumToAcquire > 0) - { - { - JPH_PROFILE("Execute Jobs"); - - // Go through all jobs - bool has_executed; - do - { - has_executed = false; - - // Loop through the jobs and erase jobs from the beginning of the list that are done - while (mJobReadIndex < mJobWriteIndex) - { - atomic &job = mJobs[mJobReadIndex & (cMaxJobs - 1)]; - Job *job_ptr = job.load(); - if (job_ptr == nullptr || !job_ptr->IsDone()) - break; - - // Job is finished, release it - job_ptr->Release(); - job = nullptr; - ++mJobReadIndex; - } - - // Loop through the jobs and execute the first executable job - for (uint index = mJobReadIndex; index < mJobWriteIndex; ++index) - { - const atomic &job = mJobs[index & (cMaxJobs - 1)]; - Job *job_ptr = job.load(); - if (job_ptr != nullptr && job_ptr->CanBeExecuted()) - { - // This will only execute the job if it has not already executed - job_ptr->Execute(); - has_executed = true; - break; - } - } - - } while (has_executed); - } - - // Wait for another thread to wake us when either there is more work to do or when all jobs have completed - int num_to_acquire = max(1, mSemaphore.GetValue()); // When there have been multiple releases, we acquire them all at the same time to avoid needlessly spinning on executing jobs - mSemaphore.Acquire(num_to_acquire); - mNumToAcquire -= num_to_acquire; - } - - // All jobs should be done now, release them - while (mJobReadIndex < mJobWriteIndex) - { - atomic &job = mJobs[mJobReadIndex & (cMaxJobs - 1)]; - Job *job_ptr = job.load(); - JPH_ASSERT(job_ptr != nullptr && job_ptr->IsDone()); - job_ptr->Release(); - job = nullptr; - ++mJobReadIndex; - } -} - -void JobSystemWithBarrier::Init(uint inMaxBarriers) -{ - JPH_ASSERT(mBarriers == nullptr); // Already initialized? - - // Init freelist of barriers - mMaxBarriers = inMaxBarriers; - mBarriers = new BarrierImpl [inMaxBarriers]; -} - -JobSystemWithBarrier::JobSystemWithBarrier(uint inMaxBarriers) -{ - Init(inMaxBarriers); -} - -JobSystemWithBarrier::~JobSystemWithBarrier() -{ - // Ensure that none of the barriers are used -#ifdef JPH_ENABLE_ASSERTS - for (const BarrierImpl *b = mBarriers, *b_end = mBarriers + mMaxBarriers; b < b_end; ++b) - JPH_ASSERT(!b->mInUse); -#endif // JPH_ENABLE_ASSERTS - delete [] mBarriers; -} - -JobSystem::Barrier *JobSystemWithBarrier::CreateBarrier() -{ - JPH_PROFILE_FUNCTION(); - - // Find the first unused barrier - for (uint32 index = 0; index < mMaxBarriers; ++index) - { - bool expected = false; - if (mBarriers[index].mInUse.compare_exchange_strong(expected, true)) - return &mBarriers[index]; - } - - return nullptr; -} - -void JobSystemWithBarrier::DestroyBarrier(Barrier *inBarrier) -{ - JPH_PROFILE_FUNCTION(); - - // Check that no jobs are in the barrier - JPH_ASSERT(static_cast(inBarrier)->IsEmpty()); - - // Flag the barrier as unused - bool expected = true; - static_cast(inBarrier)->mInUse.compare_exchange_strong(expected, false); - JPH_ASSERT(expected); -} - -void JobSystemWithBarrier::WaitForJobs(Barrier *inBarrier) -{ - JPH_PROFILE_FUNCTION(); - - // Let our barrier implementation wait for the jobs - static_cast(inBarrier)->Wait(); -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/JobSystemWithBarrier.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/JobSystemWithBarrier.h deleted file mode 100644 index 0428824a4e5..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/JobSystemWithBarrier.h +++ /dev/null @@ -1,85 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2023 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include - -JPH_NAMESPACE_BEGIN - -/// Implementation of the Barrier class for a JobSystem -/// -/// This class can be used to make it easier to create a new JobSystem implementation that integrates with your own job system. -/// It will implement all functionality relating to barriers, so the only functions that are left to be implemented are: -/// -/// * JobSystem::GetMaxConcurrency -/// * JobSystem::CreateJob -/// * JobSystem::FreeJob -/// * JobSystem::QueueJob/QueueJobs -/// -/// See instructions in JobSystem for more information on how to implement these. -class JPH_EXPORT JobSystemWithBarrier : public JobSystem -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Constructs barriers - /// @see JobSystemWithBarrier::Init - explicit JobSystemWithBarrier(uint inMaxBarriers); - JobSystemWithBarrier() = default; - virtual ~JobSystemWithBarrier() override; - - /// Initialize the barriers - /// @param inMaxBarriers Max number of barriers that can be allocated at any time - void Init(uint inMaxBarriers); - - // See JobSystem - virtual Barrier * CreateBarrier() override; - virtual void DestroyBarrier(Barrier *inBarrier) override; - virtual void WaitForJobs(Barrier *inBarrier) override; - -private: - class BarrierImpl : public Barrier - { - public: - JPH_OVERRIDE_NEW_DELETE - - /// Constructor - BarrierImpl(); - virtual ~BarrierImpl() override; - - // See Barrier - virtual void AddJob(const JobHandle &inJob) override; - virtual void AddJobs(const JobHandle *inHandles, uint inNumHandles) override; - - /// Check if there are any jobs in the job barrier - inline bool IsEmpty() const { return mJobReadIndex == mJobWriteIndex; } - - /// Wait for all jobs in this job barrier, while waiting, execute jobs that are part of this barrier on the current thread - void Wait(); - - /// Flag to indicate if a barrier has been handed out - atomic mInUse { false }; - - protected: - /// Called by a Job to mark that it is finished - virtual void OnJobFinished(Job *inJob) override; - - /// Jobs queue for the barrier - static constexpr uint cMaxJobs = 2048; - static_assert(IsPowerOf2(cMaxJobs)); // We do bit operations and require max jobs to be a power of 2 - atomic mJobs[cMaxJobs]; ///< List of jobs that are part of this barrier, nullptrs for empty slots - alignas(JPH_CACHE_LINE_SIZE) atomic mJobReadIndex { 0 }; ///< First job that could be valid (modulo cMaxJobs), can be nullptr if other thread is still working on adding the job - alignas(JPH_CACHE_LINE_SIZE) atomic mJobWriteIndex { 0 }; ///< First job that can be written (modulo cMaxJobs) - atomic mNumToAcquire { 0 }; ///< Number of times the semaphore has been released, the barrier should acquire the semaphore this many times (written at the same time as mJobWriteIndex so ok to put in same cache line) - Semaphore mSemaphore; ///< Semaphore used by finishing jobs to signal the barrier that they're done - }; - - /// Array of barriers (we keep them constructed all the time since constructing a semaphore/mutex is not cheap) - uint mMaxBarriers = 0; ///< Max amount of barriers - BarrierImpl * mBarriers = nullptr; ///< List of the actual barriers -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/LinearCurve.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/LinearCurve.cpp deleted file mode 100644 index a5e21d96c65..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/LinearCurve.cpp +++ /dev/null @@ -1,51 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(LinearCurve::Point) -{ - JPH_ADD_ATTRIBUTE(Point, mX) - JPH_ADD_ATTRIBUTE(Point, mY) -} - -JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(LinearCurve) -{ - JPH_ADD_ATTRIBUTE(LinearCurve, mPoints) -} - -float LinearCurve::GetValue(float inX) const -{ - if (mPoints.empty()) - return 0.0f; - - Points::const_iterator i2 = lower_bound(mPoints.begin(), mPoints.end(), inX, [](const Point &inPoint, float inValue) { return inPoint.mX < inValue; }); - - if (i2 == mPoints.begin()) - return mPoints.front().mY; - else if (i2 == mPoints.end()) - return mPoints.back().mY; - - Points::const_iterator i1 = i2 - 1; - return i1->mY + (inX - i1->mX) * (i2->mY - i1->mY) / (i2->mX - i1->mX); -} - -void LinearCurve::SaveBinaryState(StreamOut &inStream) const -{ - inStream.Write(mPoints); -} - -void LinearCurve::RestoreBinaryState(StreamIn &inStream) -{ - inStream.Read(mPoints); -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/LinearCurve.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/LinearCurve.h deleted file mode 100644 index ed77341a71c..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/LinearCurve.h +++ /dev/null @@ -1,67 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include - -JPH_NAMESPACE_BEGIN - -class StreamOut; -class StreamIn; - -// A set of points (x, y) that form a linear curve -class JPH_EXPORT LinearCurve -{ -public: - JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, LinearCurve) - - /// A point on the curve - class Point - { - public: - JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, Point) - - float mX = 0.0f; - float mY = 0.0f; - }; - - /// Remove all points - void Clear() { mPoints.clear(); } - - /// Reserve memory for inNumPoints points - void Reserve(uint inNumPoints) { mPoints.reserve(inNumPoints); } - - /// Add a point to the curve. Points must be inserted in ascending X or Sort() needs to be called when all points have been added. - /// @param inX X value - /// @param inY Y value - void AddPoint(float inX, float inY) { mPoints.push_back({ inX, inY }); } - - /// Sort the points on X ascending - void Sort() { QuickSort(mPoints.begin(), mPoints.end(), [](const Point &inLHS, const Point &inRHS) { return inLHS.mX < inRHS.mX; }); } - - /// Get the lowest X value - float GetMinX() const { return mPoints.empty()? 0.0f : mPoints.front().mX; } - - /// Get the highest X value - float GetMaxX() const { return mPoints.empty()? 0.0f : mPoints.back().mX; } - - /// Sample value on the curve - /// @param inX X value to sample at - /// @return Interpolated Y value - float GetValue(float inX) const; - - /// Saves the state of this object in binary form to inStream. - void SaveBinaryState(StreamOut &inStream) const; - - /// Restore the state of this object from inStream. - void RestoreBinaryState(StreamIn &inStream); - - /// The points on the curve, should be sorted ascending by x - using Points = Array; - Points mPoints; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/LockFreeHashMap.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/LockFreeHashMap.h deleted file mode 100644 index 9debf9f9c80..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/LockFreeHashMap.h +++ /dev/null @@ -1,182 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include - -JPH_NAMESPACE_BEGIN - -/// Allocator for a lock free hash map -class LFHMAllocator : public NonCopyable -{ -public: - /// Destructor - inline ~LFHMAllocator(); - - /// Initialize the allocator - /// @param inObjectStoreSizeBytes Number of bytes to reserve for all key value pairs - inline void Init(uint inObjectStoreSizeBytes); - - /// Clear all allocations - inline void Clear(); - - /// Allocate a new block of data - /// @param inBlockSize Size of block to allocate (will potentially return a smaller block if memory is full). - /// @param ioBegin Should be the start of the first free byte in current memory block on input, will contain the start of the first free byte in allocated block on return. - /// @param ioEnd Should be the byte beyond the current memory block on input, will contain the byte beyond the allocated block on return. - inline void Allocate(uint32 inBlockSize, uint32 &ioBegin, uint32 &ioEnd); - - /// Convert a pointer to an offset - template - inline uint32 ToOffset(const T *inData) const; - - /// Convert an offset to a pointer - template - inline T * FromOffset(uint32 inOffset) const; - -private: - uint8 * mObjectStore = nullptr; ///< This contains a contiguous list of objects (possibly of varying size) - uint32 mObjectStoreSizeBytes = 0; ///< The size of mObjectStore in bytes - atomic mWriteOffset { 0 }; ///< Next offset to write to in mObjectStore -}; - -/// Allocator context object for a lock free hash map that allocates a larger memory block at once and hands it out in smaller portions. -/// This avoids contention on the atomic LFHMAllocator::mWriteOffset. -class LFHMAllocatorContext : public NonCopyable -{ -public: - /// Construct a new allocator context - inline LFHMAllocatorContext(LFHMAllocator &inAllocator, uint32 inBlockSize); - - /// @brief Allocate data block - /// @param inSize Size of block to allocate. - /// @param inAlignment Alignment of block to allocate. - /// @param outWriteOffset Offset in buffer where block is located - /// @return True if allocation succeeded - inline bool Allocate(uint32 inSize, uint32 inAlignment, uint32 &outWriteOffset); - -private: - LFHMAllocator & mAllocator; - uint32 mBlockSize; - uint32 mBegin = 0; - uint32 mEnd = 0; -}; - -/// Very simple lock free hash map that only allows insertion, retrieval and provides a fixed amount of buckets and fixed storage. -/// Note: This class currently assumes key and value are simple types that need no calls to the destructor. -template -class LockFreeHashMap : public NonCopyable -{ -public: - using MapType = LockFreeHashMap; - - /// Destructor - explicit LockFreeHashMap(LFHMAllocator &inAllocator) : mAllocator(inAllocator) { } - ~LockFreeHashMap(); - - /// Initialization - /// @param inMaxBuckets Max amount of buckets to use in the hashmap. Must be power of 2. - void Init(uint32 inMaxBuckets); - - /// Remove all elements. - /// Note that this cannot happen simultaneously with adding new elements. - void Clear(); - - /// Get the current amount of buckets that the map is using - uint32 GetNumBuckets() const { return mNumBuckets; } - - /// Get the maximum amount of buckets that this map supports - uint32 GetMaxBuckets() const { return mMaxBuckets; } - - /// Update the number of buckets. This must be done after clearing the map and cannot be done concurrently with any other operations on the map. - /// Note that the number of buckets can never become bigger than the specified max buckets during initialization and that it must be a power of 2. - void SetNumBuckets(uint32 inNumBuckets); - - /// A key / value pair that is inserted in the map - class KeyValue - { - public: - const Key & GetKey() const { return mKey; } - Value & GetValue() { return mValue; } - const Value & GetValue() const { return mValue; } - - private: - template friend class LockFreeHashMap; - - Key mKey; ///< Key for this entry - uint32 mNextOffset; ///< Offset in mObjectStore of next KeyValue entry with same hash - Value mValue; ///< Value for this entry + optionally extra bytes - }; - - /// Insert a new element, returns null if map full. - /// Multiple threads can be inserting in the map at the same time. - template - inline KeyValue * Create(LFHMAllocatorContext &ioContext, const Key &inKey, uint64 inKeyHash, int inExtraBytes, Params &&... inConstructorParams); - - /// Find an element, returns null if not found - inline const KeyValue * Find(const Key &inKey, uint64 inKeyHash) const; - - /// Value of an invalid handle - const static uint32 cInvalidHandle = uint32(-1); - - /// Get convert key value pair to uint32 handle - inline uint32 ToHandle(const KeyValue *inKeyValue) const; - - /// Convert uint32 handle back to key and value - inline const KeyValue * FromHandle(uint32 inHandle) const; - -#ifdef JPH_ENABLE_ASSERTS - /// Get the number of key value pairs that this map currently contains. - /// Available only when asserts are enabled because adding elements creates contention on this atomic and negatively affects performance. - inline uint32 GetNumKeyValues() const { return mNumKeyValues; } -#endif // JPH_ENABLE_ASSERTS - - /// Get all key/value pairs - inline void GetAllKeyValues(Array &outAll) const; - - /// Non-const iterator - struct Iterator - { - /// Comparison - bool operator == (const Iterator &inRHS) const { return mMap == inRHS.mMap && mBucket == inRHS.mBucket && mOffset == inRHS.mOffset; } - bool operator != (const Iterator &inRHS) const { return !(*this == inRHS); } - - /// Convert to key value pair - KeyValue & operator * (); - - /// Next item - Iterator & operator ++ (); - - MapType * mMap; - uint32 mBucket; - uint32 mOffset; - }; - - /// Iterate over the map, note that it is not safe to do this in parallel to Clear(). - /// It is safe to do this while adding elements to the map, but newly added elements may or may not be returned by the iterator. - Iterator begin(); - Iterator end(); - -#ifdef _DEBUG - /// Output stats about this map to the log - void TraceStats() const; -#endif - -private: - LFHMAllocator & mAllocator; ///< Allocator used to allocate key value pairs - -#ifdef JPH_ENABLE_ASSERTS - atomic mNumKeyValues = 0; ///< Number of key value pairs in the store -#endif // JPH_ENABLE_ASSERTS - - atomic * mBuckets = nullptr; ///< This contains the offset in mObjectStore of the first object with a particular hash - uint32 mNumBuckets = 0; ///< Current number of buckets - uint32 mMaxBuckets = 0; ///< Maximum number of buckets -}; - -JPH_NAMESPACE_END - -#include "LockFreeHashMap.inl" diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/LockFreeHashMap.inl b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/LockFreeHashMap.inl deleted file mode 100644 index 593b5bd331c..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/LockFreeHashMap.inl +++ /dev/null @@ -1,351 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -JPH_NAMESPACE_BEGIN - -/////////////////////////////////////////////////////////////////////////////////// -// LFHMAllocator -/////////////////////////////////////////////////////////////////////////////////// - -inline LFHMAllocator::~LFHMAllocator() -{ - AlignedFree(mObjectStore); -} - -inline void LFHMAllocator::Init(uint inObjectStoreSizeBytes) -{ - JPH_ASSERT(mObjectStore == nullptr); - - mObjectStoreSizeBytes = inObjectStoreSizeBytes; - mObjectStore = reinterpret_cast(JPH::AlignedAllocate(inObjectStoreSizeBytes, 16)); -} - -inline void LFHMAllocator::Clear() -{ - mWriteOffset = 0; -} - -inline void LFHMAllocator::Allocate(uint32 inBlockSize, uint32 &ioBegin, uint32 &ioEnd) -{ - // If we're already beyond the end of our buffer then don't do an atomic add. - // It's possible that many keys are inserted after the allocator is full, making it possible - // for mWriteOffset (uint32) to wrap around to zero. When this happens, there will be a memory corruption. - // This way, we will be able to progress the write offset beyond the size of the buffer - // worst case by max * inBlockSize. - if (mWriteOffset.load(memory_order_relaxed) >= mObjectStoreSizeBytes) - return; - - // Atomically fetch a block from the pool - uint32 begin = mWriteOffset.fetch_add(inBlockSize, memory_order_relaxed); - uint32 end = min(begin + inBlockSize, mObjectStoreSizeBytes); - - if (ioEnd == begin) - { - // Block is allocated straight after our previous block - begin = ioBegin; - } - else - { - // Block is a new block - begin = min(begin, mObjectStoreSizeBytes); - } - - // Store the begin and end of the resulting block - ioBegin = begin; - ioEnd = end; -} - -template -inline uint32 LFHMAllocator::ToOffset(const T *inData) const -{ - const uint8 *data = reinterpret_cast(inData); - JPH_ASSERT(data >= mObjectStore && data < mObjectStore + mObjectStoreSizeBytes); - return uint32(data - mObjectStore); -} - -template -inline T *LFHMAllocator::FromOffset(uint32 inOffset) const -{ - JPH_ASSERT(inOffset < mObjectStoreSizeBytes); - return reinterpret_cast(mObjectStore + inOffset); -} - -/////////////////////////////////////////////////////////////////////////////////// -// LFHMAllocatorContext -/////////////////////////////////////////////////////////////////////////////////// - -inline LFHMAllocatorContext::LFHMAllocatorContext(LFHMAllocator &inAllocator, uint32 inBlockSize) : - mAllocator(inAllocator), - mBlockSize(inBlockSize) -{ -} - -inline bool LFHMAllocatorContext::Allocate(uint32 inSize, uint32 inAlignment, uint32 &outWriteOffset) -{ - // Calculate needed bytes for alignment - JPH_ASSERT(IsPowerOf2(inAlignment)); - uint32 alignment_mask = inAlignment - 1; - uint32 alignment = (inAlignment - (mBegin & alignment_mask)) & alignment_mask; - - // Check if we have space - if (mEnd - mBegin < inSize + alignment) - { - // Allocate a new block - mAllocator.Allocate(mBlockSize, mBegin, mEnd); - - // Update alignment - alignment = (inAlignment - (mBegin & alignment_mask)) & alignment_mask; - - // Check if we have space again - if (mEnd - mBegin < inSize + alignment) - return false; - } - - // Make the allocation - mBegin += alignment; - outWriteOffset = mBegin; - mBegin += inSize; - return true; -} - -/////////////////////////////////////////////////////////////////////////////////// -// LockFreeHashMap -/////////////////////////////////////////////////////////////////////////////////// - -template -void LockFreeHashMap::Init(uint32 inMaxBuckets) -{ - JPH_ASSERT(inMaxBuckets >= 4 && IsPowerOf2(inMaxBuckets)); - JPH_ASSERT(mBuckets == nullptr); - - mNumBuckets = inMaxBuckets; - mMaxBuckets = inMaxBuckets; - - mBuckets = reinterpret_cast *>(AlignedAllocate(inMaxBuckets * sizeof(atomic), 16)); - - Clear(); -} - -template -LockFreeHashMap::~LockFreeHashMap() -{ - AlignedFree(mBuckets); -} - -template -void LockFreeHashMap::Clear() -{ -#ifdef JPH_ENABLE_ASSERTS - // Reset number of key value pairs - mNumKeyValues = 0; -#endif // JPH_ENABLE_ASSERTS - - // Reset buckets 4 at a time - static_assert(sizeof(atomic) == sizeof(uint32)); - UVec4 invalid_handle = UVec4::sReplicate(cInvalidHandle); - uint32 *start = reinterpret_cast(mBuckets); - const uint32 *end = start + mNumBuckets; - JPH_ASSERT(IsAligned(start, 16)); - while (start < end) - { - invalid_handle.StoreInt4Aligned(start); - start += 4; - } -} - -template -void LockFreeHashMap::SetNumBuckets(uint32 inNumBuckets) -{ - JPH_ASSERT(mNumKeyValues == 0); - JPH_ASSERT(inNumBuckets <= mMaxBuckets); - JPH_ASSERT(inNumBuckets >= 4 && IsPowerOf2(inNumBuckets)); - - mNumBuckets = inNumBuckets; -} - -template -template -inline typename LockFreeHashMap::KeyValue *LockFreeHashMap::Create(LFHMAllocatorContext &ioContext, const Key &inKey, uint64 inKeyHash, int inExtraBytes, Params &&... inConstructorParams) -{ - // This is not a multi map, test the key hasn't been inserted yet - JPH_ASSERT(Find(inKey, inKeyHash) == nullptr); - - // Calculate total size - uint size = sizeof(KeyValue) + inExtraBytes; - - // Get the write offset for this key value pair - uint32 write_offset; - if (!ioContext.Allocate(size, alignof(KeyValue), write_offset)) - return nullptr; - -#ifdef JPH_ENABLE_ASSERTS - // Increment amount of entries in map - mNumKeyValues.fetch_add(1, memory_order_relaxed); -#endif // JPH_ENABLE_ASSERTS - - // Construct the key/value pair - KeyValue *kv = mAllocator.template FromOffset(write_offset); - JPH_ASSERT(intptr_t(kv) % alignof(KeyValue) == 0); -#ifdef _DEBUG - memset(kv, 0xcd, size); -#endif - kv->mKey = inKey; - new (&kv->mValue) Value(std::forward(inConstructorParams)...); - - // Get the offset to the first object from the bucket with corresponding hash - atomic &offset = mBuckets[inKeyHash & (mNumBuckets - 1)]; - - // Add this entry as the first element in the linked list - uint32 old_offset = offset.load(memory_order_relaxed); - for (;;) - { - kv->mNextOffset = old_offset; - if (offset.compare_exchange_weak(old_offset, write_offset, memory_order_release)) - break; - } - - return kv; -} - -template -inline const typename LockFreeHashMap::KeyValue *LockFreeHashMap::Find(const Key &inKey, uint64 inKeyHash) const -{ - // Get the offset to the keyvalue object from the bucket with corresponding hash - uint32 offset = mBuckets[inKeyHash & (mNumBuckets - 1)].load(memory_order_acquire); - while (offset != cInvalidHandle) - { - // Loop through linked list of values until the right one is found - const KeyValue *kv = mAllocator.template FromOffset(offset); - if (kv->mKey == inKey) - return kv; - offset = kv->mNextOffset; - } - - // Not found - return nullptr; -} - -template -inline uint32 LockFreeHashMap::ToHandle(const KeyValue *inKeyValue) const -{ - return mAllocator.ToOffset(inKeyValue); -} - -template -inline const typename LockFreeHashMap::KeyValue *LockFreeHashMap::FromHandle(uint32 inHandle) const -{ - return mAllocator.template FromOffset(inHandle); -} - -template -inline void LockFreeHashMap::GetAllKeyValues(Array &outAll) const -{ - for (const atomic *bucket = mBuckets; bucket < mBuckets + mNumBuckets; ++bucket) - { - uint32 offset = *bucket; - while (offset != cInvalidHandle) - { - const KeyValue *kv = mAllocator.template FromOffset(offset); - outAll.push_back(kv); - offset = kv->mNextOffset; - } - } -} - -template -typename LockFreeHashMap::Iterator LockFreeHashMap::begin() -{ - // Start with the first bucket - Iterator it { this, 0, mBuckets[0] }; - - // If it doesn't contain a valid entry, use the ++ operator to find the first valid entry - if (it.mOffset == cInvalidHandle) - ++it; - - return it; -} - -template -typename LockFreeHashMap::Iterator LockFreeHashMap::end() -{ - return { this, mNumBuckets, cInvalidHandle }; -} - -template -typename LockFreeHashMap::KeyValue &LockFreeHashMap::Iterator::operator* () -{ - JPH_ASSERT(mOffset != cInvalidHandle); - - return *mMap->mAllocator.template FromOffset(mOffset); -} - -template -typename LockFreeHashMap::Iterator &LockFreeHashMap::Iterator::operator++ () -{ - JPH_ASSERT(mBucket < mMap->mNumBuckets); - - // Find the next key value in this bucket - if (mOffset != cInvalidHandle) - { - const KeyValue *kv = mMap->mAllocator.template FromOffset(mOffset); - mOffset = kv->mNextOffset; - if (mOffset != cInvalidHandle) - return *this; - } - - // Loop over next buckets - for (;;) - { - // Next bucket - ++mBucket; - if (mBucket >= mMap->mNumBuckets) - return *this; - - // Fetch the first entry in the bucket - mOffset = mMap->mBuckets[mBucket]; - if (mOffset != cInvalidHandle) - return *this; - } -} - -#ifdef _DEBUG - -template -void LockFreeHashMap::TraceStats() const -{ - const int cMaxPerBucket = 256; - - int max_objects_per_bucket = 0; - int num_objects = 0; - int histogram[cMaxPerBucket]; - for (int i = 0; i < cMaxPerBucket; ++i) - histogram[i] = 0; - - for (atomic *bucket = mBuckets, *bucket_end = mBuckets + mNumBuckets; bucket < bucket_end; ++bucket) - { - int objects_in_bucket = 0; - uint32 offset = *bucket; - while (offset != cInvalidHandle) - { - const KeyValue *kv = mAllocator.template FromOffset(offset); - offset = kv->mNextOffset; - ++objects_in_bucket; - ++num_objects; - } - max_objects_per_bucket = max(objects_in_bucket, max_objects_per_bucket); - histogram[min(objects_in_bucket, cMaxPerBucket - 1)]++; - } - - Trace("max_objects_per_bucket = %d, num_buckets = %u, num_objects = %d", max_objects_per_bucket, mNumBuckets, num_objects); - - for (int i = 0; i < cMaxPerBucket; ++i) - if (histogram[i] != 0) - Trace("%d: %d", i, histogram[i]); -} - -#endif - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Memory.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Memory.cpp deleted file mode 100644 index b259313e367..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Memory.cpp +++ /dev/null @@ -1,74 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -JPH_SUPPRESS_WARNINGS_STD_BEGIN -#include -JPH_SUPPRESS_WARNINGS_STD_END -#include - -JPH_NAMESPACE_BEGIN - -#ifdef JPH_DISABLE_CUSTOM_ALLOCATOR - #define JPH_ALLOC_FN(x) x - #define JPH_ALLOC_SCOPE -#else - #define JPH_ALLOC_FN(x) x##Impl - #define JPH_ALLOC_SCOPE static -#endif - -JPH_ALLOC_SCOPE void *JPH_ALLOC_FN(Allocate)(size_t inSize) -{ - return malloc(inSize); -} - -JPH_ALLOC_SCOPE void JPH_ALLOC_FN(Free)(void *inBlock) -{ - free(inBlock); -} - -JPH_ALLOC_SCOPE void *JPH_ALLOC_FN(AlignedAllocate)(size_t inSize, size_t inAlignment) -{ -#if defined(JPH_PLATFORM_WINDOWS) - // Microsoft doesn't implement posix_memalign - return _aligned_malloc(inSize, inAlignment); -#else - void *block = nullptr; - JPH_SUPPRESS_WARNING_PUSH - JPH_GCC_SUPPRESS_WARNING("-Wunused-result") - JPH_CLANG_SUPPRESS_WARNING("-Wunused-result") - posix_memalign(&block, inAlignment, inSize); - JPH_SUPPRESS_WARNING_POP - return block; -#endif -} - -JPH_ALLOC_SCOPE void JPH_ALLOC_FN(AlignedFree)(void *inBlock) -{ -#if defined(JPH_PLATFORM_WINDOWS) - _aligned_free(inBlock); -#else - free(inBlock); -#endif -} - -#ifndef JPH_DISABLE_CUSTOM_ALLOCATOR - -AllocateFunction Allocate = nullptr; -FreeFunction Free = nullptr; -AlignedAllocateFunction AlignedAllocate = nullptr; -AlignedFreeFunction AlignedFree = nullptr; - -void RegisterDefaultAllocator() -{ - Allocate = AllocateImpl; - Free = FreeImpl; - AlignedAllocate = AlignedAllocateImpl; - AlignedFree = AlignedFreeImpl; -} - -#endif // JPH_DISABLE_CUSTOM_ALLOCATOR - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Memory.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Memory.h deleted file mode 100644 index 72bccaf836a..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Memory.h +++ /dev/null @@ -1,55 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -JPH_NAMESPACE_BEGIN - -#ifndef JPH_DISABLE_CUSTOM_ALLOCATOR - -// Normal memory allocation, must be at least 8 byte aligned on 32 bit platform and 16 byte aligned on 64 bit platform -using AllocateFunction = void *(*)(size_t inSize); -using FreeFunction = void (*)(void *inBlock); - -// Aligned memory allocation -using AlignedAllocateFunction = void *(*)(size_t inSize, size_t inAlignment); -using AlignedFreeFunction = void (*)(void *inBlock); - -// User defined allocation / free functions -JPH_EXPORT extern AllocateFunction Allocate; -JPH_EXPORT extern FreeFunction Free; -JPH_EXPORT extern AlignedAllocateFunction AlignedAllocate; -JPH_EXPORT extern AlignedFreeFunction AlignedFree; - -/// Register platform default allocation / free functions -JPH_EXPORT void RegisterDefaultAllocator(); - -/// Macro to override the new and delete functions -#define JPH_OVERRIDE_NEW_DELETE \ - JPH_INLINE void *operator new (size_t inCount) { return JPH::Allocate(inCount); } \ - JPH_INLINE void operator delete (void *inPointer) noexcept { JPH::Free(inPointer); } \ - JPH_INLINE void *operator new[] (size_t inCount) { return JPH::Allocate(inCount); } \ - JPH_INLINE void operator delete[] (void *inPointer) noexcept { JPH::Free(inPointer); } \ - JPH_INLINE void *operator new (size_t inCount, std::align_val_t inAlignment) { return JPH::AlignedAllocate(inCount, static_cast(inAlignment)); } \ - JPH_INLINE void operator delete (void *inPointer, [[maybe_unused]] std::align_val_t inAlignment) noexcept { JPH::AlignedFree(inPointer); } \ - JPH_INLINE void *operator new[] (size_t inCount, std::align_val_t inAlignment) { return JPH::AlignedAllocate(inCount, static_cast(inAlignment)); } \ - JPH_INLINE void operator delete[] (void *inPointer, [[maybe_unused]] std::align_val_t inAlignment) noexcept { JPH::AlignedFree(inPointer); } - -#else - -// Directly define the allocation functions -JPH_EXPORT void *Allocate(size_t inSize); -JPH_EXPORT void Free(void *inBlock); -JPH_EXPORT void *AlignedAllocate(size_t inSize, size_t inAlignment); -JPH_EXPORT void AlignedFree(void *inBlock); - -// Don't implement allocator registering -inline void RegisterDefaultAllocator() { } - -// Don't override new/delete -#define JPH_OVERRIDE_NEW_DELETE - -#endif // !JPH_DISABLE_CUSTOM_ALLOCATOR - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Mutex.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Mutex.h deleted file mode 100644 index 6969aa45d0a..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Mutex.h +++ /dev/null @@ -1,223 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include - -JPH_SUPPRESS_WARNINGS_STD_BEGIN -#include -#include -#include -JPH_SUPPRESS_WARNINGS_STD_END - -JPH_NAMESPACE_BEGIN - -// Things we're using from STL -using std::mutex; -using std::shared_mutex; -using std::thread; -using std::lock_guard; -using std::shared_lock; -using std::unique_lock; - -#ifdef JPH_PLATFORM_BLUE - -// On Platform Blue the mutex class is not very fast so we implement it using the official APIs -class MutexBase : public NonCopyable -{ -public: - MutexBase() - { - JPH_PLATFORM_BLUE_MUTEX_INIT(mMutex); - } - - ~MutexBase() - { - JPH_PLATFORM_BLUE_MUTEX_DESTROY(mMutex); - } - - inline bool try_lock() - { - return JPH_PLATFORM_BLUE_MUTEX_TRYLOCK(mMutex); - } - - inline void lock() - { - JPH_PLATFORM_BLUE_MUTEX_LOCK(mMutex); - } - - inline void unlock() - { - JPH_PLATFORM_BLUE_MUTEX_UNLOCK(mMutex); - } - -private: - JPH_PLATFORM_BLUE_MUTEX mMutex; -}; - -// On Platform Blue the shared_mutex class is not very fast so we implement it using the official APIs -class SharedMutexBase : public NonCopyable -{ -public: - SharedMutexBase() - { - JPH_PLATFORM_BLUE_RWLOCK_INIT(mRWLock); - } - - ~SharedMutexBase() - { - JPH_PLATFORM_BLUE_RWLOCK_DESTROY(mRWLock); - } - - inline bool try_lock() - { - return JPH_PLATFORM_BLUE_RWLOCK_TRYWLOCK(mRWLock); - } - - inline bool try_lock_shared() - { - return JPH_PLATFORM_BLUE_RWLOCK_TRYRLOCK(mRWLock); - } - - inline void lock() - { - JPH_PLATFORM_BLUE_RWLOCK_WLOCK(mRWLock); - } - - inline void unlock() - { - JPH_PLATFORM_BLUE_RWLOCK_WUNLOCK(mRWLock); - } - - inline void lock_shared() - { - JPH_PLATFORM_BLUE_RWLOCK_RLOCK(mRWLock); - } - - inline void unlock_shared() - { - JPH_PLATFORM_BLUE_RWLOCK_RUNLOCK(mRWLock); - } - -private: - JPH_PLATFORM_BLUE_RWLOCK mRWLock; -}; - -#else - -// On other platforms just use the STL implementation -using MutexBase = mutex; -using SharedMutexBase = shared_mutex; - -#endif // JPH_PLATFORM_BLUE - -#if defined(JPH_ENABLE_ASSERTS) || defined(JPH_PROFILE_ENABLED) || defined(JPH_EXTERNAL_PROFILE) - -/// Very simple wrapper around MutexBase which tracks lock contention in the profiler -/// and asserts that locks/unlocks take place on the same thread -class Mutex : public MutexBase -{ -public: - inline bool try_lock() - { - JPH_ASSERT(mLockedThreadID != std::this_thread::get_id()); - if (MutexBase::try_lock()) - { - JPH_IF_ENABLE_ASSERTS(mLockedThreadID = std::this_thread::get_id();) - return true; - } - return false; - } - - inline void lock() - { - if (!try_lock()) - { - JPH_PROFILE("Lock", 0xff00ffff); - MutexBase::lock(); - JPH_IF_ENABLE_ASSERTS(mLockedThreadID = std::this_thread::get_id();) - } - } - - inline void unlock() - { - JPH_ASSERT(mLockedThreadID == std::this_thread::get_id()); - JPH_IF_ENABLE_ASSERTS(mLockedThreadID = thread::id();) - MutexBase::unlock(); - } - -#ifdef JPH_ENABLE_ASSERTS - inline bool is_locked() - { - return mLockedThreadID != thread::id(); - } -#endif // JPH_ENABLE_ASSERTS - -private: - JPH_IF_ENABLE_ASSERTS(thread::id mLockedThreadID;) -}; - -/// Very simple wrapper around SharedMutexBase which tracks lock contention in the profiler -/// and asserts that locks/unlocks take place on the same thread -class SharedMutex : public SharedMutexBase -{ -public: - inline bool try_lock() - { - JPH_ASSERT(mLockedThreadID != std::this_thread::get_id()); - if (SharedMutexBase::try_lock()) - { - JPH_IF_ENABLE_ASSERTS(mLockedThreadID = std::this_thread::get_id();) - return true; - } - return false; - } - - inline void lock() - { - if (!try_lock()) - { - JPH_PROFILE("WLock", 0xff00ffff); - SharedMutexBase::lock(); - JPH_IF_ENABLE_ASSERTS(mLockedThreadID = std::this_thread::get_id();) - } - } - - inline void unlock() - { - JPH_ASSERT(mLockedThreadID == std::this_thread::get_id()); - JPH_IF_ENABLE_ASSERTS(mLockedThreadID = thread::id();) - SharedMutexBase::unlock(); - } - -#ifdef JPH_ENABLE_ASSERTS - inline bool is_locked() - { - return mLockedThreadID != thread::id(); - } -#endif // JPH_ENABLE_ASSERTS - - inline void lock_shared() - { - if (!try_lock_shared()) - { - JPH_PROFILE("RLock", 0xff00ffff); - SharedMutexBase::lock_shared(); - } - } - -private: - JPH_IF_ENABLE_ASSERTS(thread::id mLockedThreadID;) -}; - -#else - -using Mutex = MutexBase; -using SharedMutex = SharedMutexBase; - -#endif - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/MutexArray.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/MutexArray.h deleted file mode 100644 index f8fdd827913..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/MutexArray.h +++ /dev/null @@ -1,98 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -/// A mutex array protects a number of resources with a limited amount of mutexes. -/// It uses hashing to find the mutex of a particular object. -/// The idea is that if the amount of threads is much smaller than the amount of mutexes -/// that there is a relatively small chance that two different objects map to the same mutex. -template -class MutexArray : public NonCopyable -{ -public: - /// Constructor, constructs an empty mutex array that you need to initialize with Init() - MutexArray() = default; - - /// Constructor, constructs an array with inNumMutexes entries - explicit MutexArray(uint inNumMutexes) { Init(inNumMutexes); } - - /// Destructor - ~MutexArray() { delete [] mMutexStorage; } - - /// Initialization - /// @param inNumMutexes The amount of mutexes to allocate - void Init(uint inNumMutexes) - { - JPH_ASSERT(mMutexStorage == nullptr); - JPH_ASSERT(inNumMutexes > 0 && IsPowerOf2(inNumMutexes)); - - mMutexStorage = new MutexStorage[inNumMutexes]; - mNumMutexes = inNumMutexes; - } - - /// Get the number of mutexes that were allocated - inline uint GetNumMutexes() const - { - return mNumMutexes; - } - - /// Convert an object index to a mutex index - inline uint32 GetMutexIndex(uint32 inObjectIndex) const - { - std::hash hasher; - return hasher(inObjectIndex) & (mNumMutexes - 1); - } - - /// Get the mutex belonging to a certain object by index - inline MutexType & GetMutexByObjectIndex(uint32 inObjectIndex) - { - return mMutexStorage[GetMutexIndex(inObjectIndex)].mMutex; - } - - /// Get a mutex by index in the array - inline MutexType & GetMutexByIndex(uint32 inMutexIndex) - { - return mMutexStorage[inMutexIndex].mMutex; - } - - /// Lock all mutexes - void LockAll() - { - JPH_PROFILE_FUNCTION(); - - MutexStorage *end = mMutexStorage + mNumMutexes; - for (MutexStorage *m = mMutexStorage; m < end; ++m) - m->mMutex.lock(); - } - - /// Unlock all mutexes - void UnlockAll() - { - JPH_PROFILE_FUNCTION(); - - MutexStorage *end = mMutexStorage + mNumMutexes; - for (MutexStorage *m = mMutexStorage; m < end; ++m) - m->mMutex.unlock(); - } - -private: - /// Align the mutex to a cache line to ensure there is no false sharing (this is platform dependent, we do this to be safe) - struct alignas(JPH_CACHE_LINE_SIZE) MutexStorage - { - JPH_OVERRIDE_NEW_DELETE - - MutexType mMutex; - }; - - MutexStorage * mMutexStorage = nullptr; - uint mNumMutexes = 0; -}; - -JPH_NAMESPACE_END - diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/NonCopyable.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/NonCopyable.h deleted file mode 100644 index 18b431e925a..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/NonCopyable.h +++ /dev/null @@ -1,18 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -JPH_NAMESPACE_BEGIN - -/// Class that makes another class non-copyable. Usage: Inherit from NonCopyable. -class JPH_EXPORT NonCopyable -{ -public: - NonCopyable() = default; - NonCopyable(const NonCopyable &) = delete; - void operator = (const NonCopyable &) = delete; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Profiler.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Profiler.cpp deleted file mode 100644 index 5678ba77171..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Profiler.cpp +++ /dev/null @@ -1,346 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include -#include - -JPH_SUPPRESS_WARNINGS_STD_BEGIN -#include -JPH_SUPPRESS_WARNINGS_STD_END - -#ifdef JPH_PROFILE_ENABLED - -JPH_NAMESPACE_BEGIN - -////////////////////////////////////////////////////////////////////////////////////////// -// Profiler -////////////////////////////////////////////////////////////////////////////////////////// - -Profiler *Profiler::sInstance = nullptr; - -#ifdef JPH_SHARED_LIBRARY - static thread_local ProfileThread *sInstance = nullptr; - - ProfileThread *ProfileThread::sGetInstance() - { - return sInstance; - } - - void ProfileThread::sSetInstance(ProfileThread *inInstance) - { - sInstance = inInstance; - } -#else - thread_local ProfileThread *ProfileThread::sInstance = nullptr; -#endif - -bool ProfileMeasurement::sOutOfSamplesReported = false; - -void Profiler::UpdateReferenceTime() -{ - mReferenceTick = GetProcessorTickCount(); - mReferenceTime = std::chrono::high_resolution_clock::now(); -} - -uint64 Profiler::GetProcessorTicksPerSecond() const -{ - uint64 ticks = GetProcessorTickCount(); - std::chrono::high_resolution_clock::time_point time = std::chrono::high_resolution_clock::now(); - - return (ticks - mReferenceTick) * 1000000000ULL / std::chrono::duration_cast(time - mReferenceTime).count(); -} - -void Profiler::NextFrame() -{ - std::lock_guard lock(mLock); - - if (mDump) - { - DumpInternal(); - mDump = false; - } - - for (ProfileThread *t : mThreads) - t->mCurrentSample = 0; - - UpdateReferenceTime(); -} - -void Profiler::Dump(const string_view &inTag) -{ - mDump = true; - mDumpTag = inTag; -} - -void Profiler::AddThread(ProfileThread *inThread) -{ - std::lock_guard lock(mLock); - - mThreads.push_back(inThread); -} - -void Profiler::RemoveThread(ProfileThread *inThread) -{ - std::lock_guard lock(mLock); - - Array::iterator i = find(mThreads.begin(), mThreads.end(), inThread); - JPH_ASSERT(i != mThreads.end()); - mThreads.erase(i); -} - -void Profiler::sAggregate(int inDepth, uint32 inColor, ProfileSample *&ioSample, const ProfileSample *inEnd, Aggregators &ioAggregators, KeyToAggregator &ioKeyToAggregator) -{ - // Store depth - ioSample->mDepth = uint8(min(255, inDepth)); - - // Update color - if (ioSample->mColor == 0) - ioSample->mColor = inColor; - else - inColor = ioSample->mColor; - - // Start accumulating totals - uint64 cycles_this_with_children = ioSample->mEndCycle - ioSample->mStartCycle; - - // Loop over following samples until we find a sample that starts on or after our end - ProfileSample *sample; - for (sample = ioSample + 1; sample < inEnd && sample->mStartCycle < ioSample->mEndCycle; ++sample) - { - JPH_ASSERT(sample[-1].mStartCycle <= sample->mStartCycle); - JPH_ASSERT(sample->mStartCycle >= ioSample->mStartCycle); - JPH_ASSERT(sample->mEndCycle <= ioSample->mEndCycle); - - // Recurse and skip over the children of this child - sAggregate(inDepth + 1, inColor, sample, inEnd, ioAggregators, ioKeyToAggregator); - } - - // Find the aggregator for this name / filename pair - Aggregator *aggregator; - KeyToAggregator::iterator aggregator_idx = ioKeyToAggregator.find(ioSample->mName); - if (aggregator_idx == ioKeyToAggregator.end()) - { - // Not found, add to map and insert in array - ioKeyToAggregator.try_emplace(ioSample->mName, ioAggregators.size()); - ioAggregators.emplace_back(ioSample->mName); - aggregator = &ioAggregators.back(); - } - else - { - // Found - aggregator = &ioAggregators[aggregator_idx->second]; - } - - // Add the measurement to the aggregator - aggregator->AccumulateMeasurement(cycles_this_with_children); - - // Update ioSample to the last child of ioSample - JPH_ASSERT(sample[-1].mStartCycle <= ioSample->mEndCycle); - JPH_ASSERT(sample >= inEnd || sample->mStartCycle >= ioSample->mEndCycle); - ioSample = sample - 1; -} - -void Profiler::DumpInternal() -{ - // Freeze data from threads - // Note that this is not completely thread safe: As a profile sample is added mCurrentSample is incremented - // but the data is not written until the sample finishes. So if we dump the profile information while - // some other thread is running, we may get some garbage information from the previous frame - Threads threads; - for (ProfileThread *t : mThreads) - threads.push_back({ t->mThreadName, t->mSamples, t->mSamples + t->mCurrentSample }); - - // Shift all samples so that the first sample is at zero - uint64 min_cycle = 0xffffffffffffffffUL; - for (const ThreadSamples &t : threads) - if (t.mSamplesBegin < t.mSamplesEnd) - min_cycle = min(min_cycle, t.mSamplesBegin[0].mStartCycle); - for (const ThreadSamples &t : threads) - for (ProfileSample *s = t.mSamplesBegin, *end = t.mSamplesEnd; s < end; ++s) - { - s->mStartCycle -= min_cycle; - s->mEndCycle -= min_cycle; - } - - // Determine tag of this profile - String tag; - if (mDumpTag.empty()) - { - // Next sequence number - static int number = 0; - ++number; - tag = ConvertToString(number); - } - else - { - // Take provided tag - tag = mDumpTag; - mDumpTag.clear(); - } - - // Aggregate data across threads - Aggregators aggregators; - KeyToAggregator key_to_aggregators; - for (const ThreadSamples &t : threads) - for (ProfileSample *s = t.mSamplesBegin, *end = t.mSamplesEnd; s < end; ++s) - sAggregate(0, Color::sGetDistinctColor(0).GetUInt32(), s, end, aggregators, key_to_aggregators); - - // Dump as chart - DumpChart(tag.c_str(), threads, key_to_aggregators, aggregators); -} - -static String sHTMLEncode(const char *inString) -{ - String str(inString); - StringReplace(str, "<", "<"); - StringReplace(str, ">", ">"); - return str; -} - -void Profiler::DumpChart(const char *inTag, const Threads &inThreads, const KeyToAggregator &inKeyToAggregators, const Aggregators &inAggregators) -{ - // Open file - std::ofstream f; - f.open(StringFormat("profile_chart_%s.html", inTag).c_str(), std::ofstream::out | std::ofstream::trunc); - if (!f.is_open()) - return; - - // Write header - f << R"( - - - Profile Chart - - - - - - - -
              - -)"; -} - -JPH_NAMESPACE_END - -#endif // JPH_PROFILE_ENABLED diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Profiler.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Profiler.h deleted file mode 100644 index bbe64182155..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Profiler.h +++ /dev/null @@ -1,284 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -JPH_SUPPRESS_WARNINGS_STD_BEGIN -#include -#include -JPH_SUPPRESS_WARNINGS_STD_END - -#include -#include -#include - -#if defined(JPH_EXTERNAL_PROFILE) - -JPH_NAMESPACE_BEGIN - -/// Create this class on the stack to start sampling timing information of a particular scope. -/// -/// Left unimplemented intentionally. Needs to be implemented by the user of the library. -/// On construction a measurement should start, on destruction it should be stopped. -class alignas(16) ExternalProfileMeasurement : public NonCopyable -{ -public: - /// Constructor - ExternalProfileMeasurement(const char *inName, uint32 inColor = 0); - ~ExternalProfileMeasurement(); - -private: - uint8 mUserData[64]; -}; - -JPH_NAMESPACE_END - -////////////////////////////////////////////////////////////////////////////////////////// -// Macros to do the actual profiling -////////////////////////////////////////////////////////////////////////////////////////// - -JPH_SUPPRESS_WARNING_PUSH -JPH_CLANG_SUPPRESS_WARNING("-Wc++98-compat-pedantic") - -// Dummy implementations -#define JPH_PROFILE_THREAD_START(name) -#define JPH_PROFILE_THREAD_END() -#define JPH_PROFILE_NEXTFRAME() -#define JPH_PROFILE_DUMP(...) - -// Scope profiling measurement -#define JPH_PROFILE_TAG2(line) profile##line -#define JPH_PROFILE_TAG(line) JPH_PROFILE_TAG2(line) - -/// Macro to collect profiling information. -/// -/// Usage: -/// -/// { -/// JPH_PROFILE("Operation"); -/// do operation; -/// } -/// -#define JPH_PROFILE(...) ExternalProfileMeasurement JPH_PROFILE_TAG(__LINE__)(__VA_ARGS__) - -// Scope profiling for function -#define JPH_PROFILE_FUNCTION() JPH_PROFILE(JPH_FUNCTION_NAME) - -JPH_SUPPRESS_WARNING_POP - -#elif defined(JPH_PROFILE_ENABLED) - -JPH_NAMESPACE_BEGIN - -class ProfileSample; -class ProfileThread; - -/// Singleton class for managing profiling information -class JPH_EXPORT Profiler : public NonCopyable -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Constructor - Profiler() { UpdateReferenceTime(); } - - /// Increments the frame counter to provide statistics per frame - void NextFrame(); - - /// Dump profiling statistics at the start of the next frame - /// @param inTag If not empty, this overrides the auto incrementing number in the filename of the dump file - void Dump(const string_view &inTag = string_view()); - - /// Add a thread to be instrumented - void AddThread(ProfileThread *inThread); - - /// Remove a thread from being instrumented - void RemoveThread(ProfileThread *inThread); - - /// Singleton instance - static Profiler * sInstance; - -private: - /// Helper class to freeze ProfileSamples per thread while processing them - struct ThreadSamples - { - String mThreadName; - ProfileSample * mSamplesBegin; - ProfileSample * mSamplesEnd; - }; - - /// Helper class to aggregate ProfileSamples - class Aggregator - { - public: - /// Constructor - Aggregator(const char *inName) : mName(inName) { } - - /// Accumulate results for a measurement - void AccumulateMeasurement(uint64 inCyclesInCallWithChildren) - { - mCallCounter++; - mTotalCyclesInCallWithChildren += inCyclesInCallWithChildren; - mMinCyclesInCallWithChildren = min(inCyclesInCallWithChildren, mMinCyclesInCallWithChildren); - mMaxCyclesInCallWithChildren = max(inCyclesInCallWithChildren, mMaxCyclesInCallWithChildren); - } - - /// Sort descending by total cycles - bool operator < (const Aggregator &inRHS) const - { - return mTotalCyclesInCallWithChildren > inRHS.mTotalCyclesInCallWithChildren; - } - - /// Identification - const char * mName; ///< User defined name of this item - - /// Statistics - uint32 mCallCounter = 0; ///< Number of times AccumulateMeasurement was called - uint64 mTotalCyclesInCallWithChildren = 0; ///< Total amount of cycles spent in this scope - uint64 mMinCyclesInCallWithChildren = 0xffffffffffffffffUL; ///< Minimum amount of cycles spent per call - uint64 mMaxCyclesInCallWithChildren = 0; ///< Maximum amount of cycles spent per call - }; - - using Threads = Array; - using Aggregators = Array; - using KeyToAggregator = UnorderedMap; - - /// Helper function to aggregate profile sample data - static void sAggregate(int inDepth, uint32 inColor, ProfileSample *&ioSample, const ProfileSample *inEnd, Aggregators &ioAggregators, KeyToAggregator &ioKeyToAggregator); - - /// We measure the amount of ticks per second, this function resets the reference time point - void UpdateReferenceTime(); - - /// Get the amount of ticks per second, note that this number will never be fully accurate as the amount of ticks per second may vary with CPU load, so this number is only to be used to give an indication of time for profiling purposes - uint64 GetProcessorTicksPerSecond() const; - - /// Dump profiling statistics - void DumpInternal(); - void DumpChart(const char *inTag, const Threads &inThreads, const KeyToAggregator &inKeyToAggregators, const Aggregators &inAggregators); - - std::mutex mLock; ///< Lock that protects mThreads - uint64 mReferenceTick; ///< Tick count at the start of the frame - std::chrono::high_resolution_clock::time_point mReferenceTime; ///< Time at the start of the frame - Array mThreads; ///< List of all active threads - bool mDump = false; ///< When true, the samples are dumped next frame - String mDumpTag; ///< When not empty, this overrides the auto incrementing number of the dump filename -}; - -// Class that contains the information of a single scoped measurement -class alignas(16) JPH_EXPORT_GCC_BUG_WORKAROUND ProfileSample : public NonCopyable -{ -public: - JPH_OVERRIDE_NEW_DELETE - - const char * mName; ///< User defined name of this item - uint32 mColor; ///< Color to use for this sample - uint8 mDepth; ///< Calculated depth - uint8 mUnused[3]; - uint64 mStartCycle; ///< Cycle counter at start of measurement - uint64 mEndCycle; ///< Cycle counter at end of measurement -}; - -/// Collects all samples of a single thread -class ProfileThread : public NonCopyable -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Constructor - inline ProfileThread(const string_view &inThreadName); - inline ~ProfileThread(); - - static const uint cMaxSamples = 65536; - - String mThreadName; ///< Name of the thread that we're collecting information for - ProfileSample mSamples[cMaxSamples]; ///< Buffer of samples - uint mCurrentSample = 0; ///< Next position to write a sample to - -#ifdef JPH_SHARED_LIBRARY - JPH_EXPORT static void sSetInstance(ProfileThread *inInstance); - JPH_EXPORT static ProfileThread *sGetInstance(); -#else - static inline void sSetInstance(ProfileThread *inInstance) { sInstance = inInstance; } - static inline ProfileThread *sGetInstance() { return sInstance; } - -private: - static thread_local ProfileThread *sInstance; -#endif -}; - -/// Create this class on the stack to start sampling timing information of a particular scope -class JPH_EXPORT ProfileMeasurement : public NonCopyable -{ -public: - /// Constructor - inline ProfileMeasurement(const char *inName, uint32 inColor = 0); - inline ~ProfileMeasurement(); - -private: - ProfileSample * mSample; - ProfileSample mTemp; - - static bool sOutOfSamplesReported; -}; - -JPH_NAMESPACE_END - -#include "Profiler.inl" - -////////////////////////////////////////////////////////////////////////////////////////// -// Macros to do the actual profiling -////////////////////////////////////////////////////////////////////////////////////////// - -JPH_SUPPRESS_WARNING_PUSH -JPH_CLANG_SUPPRESS_WARNING("-Wc++98-compat-pedantic") - -/// Start instrumenting program -#define JPH_PROFILE_START(name) do { Profiler::sInstance = new Profiler; JPH_PROFILE_THREAD_START(name); } while (false) - -/// End instrumenting program -#define JPH_PROFILE_END() do { JPH_PROFILE_THREAD_END(); delete Profiler::sInstance; Profiler::sInstance = nullptr; } while (false) - -/// Start instrumenting a thread -#define JPH_PROFILE_THREAD_START(name) do { if (Profiler::sInstance) ProfileThread::sSetInstance(new ProfileThread(name)); } while (false) - -/// End instrumenting a thread -#define JPH_PROFILE_THREAD_END() do { delete ProfileThread::sGetInstance(); ProfileThread::sSetInstance(nullptr); } while (false) - -/// Scope profiling measurement -#define JPH_PROFILE_TAG2(line) profile##line -#define JPH_PROFILE_TAG(line) JPH_PROFILE_TAG2(line) -#define JPH_PROFILE(...) ProfileMeasurement JPH_PROFILE_TAG(__LINE__)(__VA_ARGS__) - -/// Scope profiling for function -#define JPH_PROFILE_FUNCTION() JPH_PROFILE(JPH_FUNCTION_NAME) - -/// Update frame counter -#define JPH_PROFILE_NEXTFRAME() Profiler::sInstance->NextFrame() - -/// Dump profiling info -#define JPH_PROFILE_DUMP(...) Profiler::sInstance->Dump(__VA_ARGS__) - -JPH_SUPPRESS_WARNING_POP - -#else - -////////////////////////////////////////////////////////////////////////////////////////// -// Dummy profiling instructions -////////////////////////////////////////////////////////////////////////////////////////// - -JPH_SUPPRESS_WARNING_PUSH -JPH_CLANG_SUPPRESS_WARNING("-Wc++98-compat-pedantic") - -#define JPH_PROFILE_START(name) -#define JPH_PROFILE_END() -#define JPH_PROFILE_THREAD_START(name) -#define JPH_PROFILE_THREAD_END() -#define JPH_PROFILE(...) -#define JPH_PROFILE_FUNCTION() -#define JPH_PROFILE_NEXTFRAME() -#define JPH_PROFILE_DUMP(...) - -JPH_SUPPRESS_WARNING_POP - -#endif diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Profiler.inl b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Profiler.inl deleted file mode 100644 index cc429ba51ab..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Profiler.inl +++ /dev/null @@ -1,89 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -JPH_NAMESPACE_BEGIN - -////////////////////////////////////////////////////////////////////////////////////////// -// ProfileThread -////////////////////////////////////////////////////////////////////////////////////////// - -ProfileThread::ProfileThread(const string_view &inThreadName) : - mThreadName(inThreadName) -{ - Profiler::sInstance->AddThread(this); -} - -ProfileThread::~ProfileThread() -{ - Profiler::sInstance->RemoveThread(this); -} - -////////////////////////////////////////////////////////////////////////////////////////// -// ProfileMeasurement -////////////////////////////////////////////////////////////////////////////////////////// - -ProfileMeasurement::ProfileMeasurement(const char *inName, uint32 inColor) -{ - ProfileThread *current_thread = ProfileThread::sGetInstance(); - if (current_thread == nullptr) - { - // Thread not instrumented - mSample = nullptr; - } - else if (current_thread->mCurrentSample < ProfileThread::cMaxSamples) - { - // Get pointer to write data to - mSample = ¤t_thread->mSamples[current_thread->mCurrentSample++]; - - // Start constructing sample (will end up on stack) - mTemp.mName = inName; - mTemp.mColor = inColor; - - // Collect start sample last - mTemp.mStartCycle = GetProcessorTickCount(); - } - else - { - // Out of samples - if (!sOutOfSamplesReported) - { - Trace("ProfileMeasurement: Too many samples, some data will be lost!"); - sOutOfSamplesReported = true; - } - mSample = nullptr; - } -} - -ProfileMeasurement::~ProfileMeasurement() -{ - if (mSample != nullptr) - { - // Finalize sample - mTemp.mEndCycle = GetProcessorTickCount(); - - // Write it to the memory buffer bypassing the cache - static_assert(sizeof(ProfileSample) == 32, "Assume 32 bytes"); - static_assert(alignof(ProfileSample) == 16, "Assume 16 byte alignment"); - #if defined(JPH_USE_SSE) - const __m128i *src = reinterpret_cast(&mTemp); - __m128i *dst = reinterpret_cast<__m128i *>(mSample); - __m128i val = _mm_loadu_si128(src); - _mm_stream_si128(dst, val); - val = _mm_loadu_si128(src + 1); - _mm_stream_si128(dst + 1, val); - #elif defined(JPH_USE_NEON) - const int *src = reinterpret_cast(&mTemp); - int *dst = reinterpret_cast(mSample); - int32x4_t val = vld1q_s32(src); - vst1q_s32(dst, val); - val = vld1q_s32(src + 4); - vst1q_s32(dst + 4, val); - #else - memcpy(mSample, &mTemp, sizeof(ProfileSample)); - #endif - mSample = nullptr; - } -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/QuickSort.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/QuickSort.h deleted file mode 100644 index 0b4a3ff9a9e..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/QuickSort.h +++ /dev/null @@ -1,137 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2022 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -/// Helper function for QuickSort, will move the pivot element to inMiddle. -template -inline void QuickSortMedianOfThree(Iterator inFirst, Iterator inMiddle, Iterator inLast, Compare inCompare) -{ - // This should be guaranteed because we switch over to insertion sort when there's 32 or less elements - JPH_ASSERT(inFirst != inMiddle && inMiddle != inLast); - - if (inCompare(*inMiddle, *inFirst)) - swap(*inFirst, *inMiddle); - - if (inCompare(*inLast, *inFirst)) - swap(*inFirst, *inLast); - - if (inCompare(*inLast, *inMiddle)) - swap(*inMiddle, *inLast); -} - -/// Helper function for QuickSort using the Ninther method, will move the pivot element to inMiddle. -template -inline void QuickSortNinther(Iterator inFirst, Iterator inMiddle, Iterator inLast, Compare inCompare) -{ - // Divide the range in 8 equal parts (this means there are 9 points) - auto diff = (inLast - inFirst) >> 3; - auto two_diff = diff << 1; - - // Median of first 3 points - Iterator mid1 = inFirst + diff; - QuickSortMedianOfThree(inFirst, mid1, inFirst + two_diff, inCompare); - - // Median of second 3 points - QuickSortMedianOfThree(inMiddle - diff, inMiddle, inMiddle + diff, inCompare); - - // Median of third 3 points - Iterator mid3 = inLast - diff; - QuickSortMedianOfThree(inLast - two_diff, mid3, inLast, inCompare); - - // Determine the median of the 3 medians - QuickSortMedianOfThree(mid1, inMiddle, mid3, inCompare); -} - -/// Implementation of the quick sort algorithm. The STL version implementation is not consistent across platforms. -template -inline void QuickSort(Iterator inBegin, Iterator inEnd, Compare inCompare) -{ - // Implementation based on https://en.wikipedia.org/wiki/Quicksort using Hoare's partition scheme - - // Loop so that we only need to do 1 recursive call instead of 2. - for (;;) - { - // If there's less than 2 elements we're done - auto num_elements = inEnd - inBegin; - if (num_elements < 2) - return; - - // Fall back to insertion sort if there are too few elements - if (num_elements <= 32) - { - InsertionSort(inBegin, inEnd, inCompare); - return; - } - - // Determine pivot - Iterator pivot_iterator = inBegin + ((num_elements - 1) >> 1); - QuickSortNinther(inBegin, pivot_iterator, inEnd - 1, inCompare); - auto pivot = *pivot_iterator; - - // Left and right iterators - Iterator i = inBegin; - Iterator j = inEnd; - - for (;;) - { - // Find the first element that is bigger than the pivot - while (inCompare(*i, pivot)) - i++; - - // Find the last element that is smaller than the pivot - do - --j; - while (inCompare(pivot, *j)); - - // If the two iterators crossed, we're done - if (i >= j) - break; - - // Swap the elements - swap(*i, *j); - - // Note that the first while loop in this function should - // have been do i++ while (...) but since we cannot decrement - // the iterator from inBegin we left that out, so we need to do - // it here. - ++i; - } - - // Include the middle element on the left side - j++; - - // Check which partition is smaller - if (j - inBegin < inEnd - j) - { - // Left side is smaller, recurse to left first - QuickSort(inBegin, j, inCompare); - - // Loop again with the right side to avoid a call - inBegin = j; - } - else - { - // Right side is smaller, recurse to right first - QuickSort(j, inEnd, inCompare); - - // Loop again with the left side to avoid a call - inEnd = j; - } - } -} - -/// Implementation of quick sort algorithm without comparator. -template -inline void QuickSort(Iterator inBegin, Iterator inEnd) -{ - std::less<> compare; - QuickSort(inBegin, inEnd, compare); -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/RTTI.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/RTTI.cpp deleted file mode 100644 index 81915feaf19..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/RTTI.cpp +++ /dev/null @@ -1,143 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include - -JPH_NAMESPACE_BEGIN - -////////////////////////////////////////////////////////////////////////////////////////// -// RTTI -////////////////////////////////////////////////////////////////////////////////////////// - -RTTI::RTTI(const char *inName, int inSize, pCreateObjectFunction inCreateObject, pDestructObjectFunction inDestructObject) : - mName(inName), - mSize(inSize), - mCreate(inCreateObject), - mDestruct(inDestructObject) -{ - JPH_ASSERT(inDestructObject != nullptr, "Object cannot be destructed"); -} - -RTTI::RTTI(const char *inName, int inSize, pCreateObjectFunction inCreateObject, pDestructObjectFunction inDestructObject, pCreateRTTIFunction inCreateRTTI) : - mName(inName), - mSize(inSize), - mCreate(inCreateObject), - mDestruct(inDestructObject) -{ - JPH_ASSERT(inDestructObject != nullptr, "Object cannot be destructed"); - - inCreateRTTI(*this); -} - -int RTTI::GetBaseClassCount() const -{ - return (int)mBaseClasses.size(); -} - -const RTTI *RTTI::GetBaseClass(int inIdx) const -{ - return mBaseClasses[inIdx].mRTTI; -} - -uint32 RTTI::GetHash() const -{ - // Perform diffusion step to get from 64 to 32 bits (see https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function) - uint64 hash = HashString(mName); - return (uint32)(hash ^ (hash >> 32)); -} - -void *RTTI::CreateObject() const -{ - return IsAbstract()? nullptr : mCreate(); -} - -void RTTI::DestructObject(void *inObject) const -{ - mDestruct(inObject); -} - -void RTTI::AddBaseClass(const RTTI *inRTTI, int inOffset) -{ - JPH_ASSERT(inOffset >= 0 && inOffset < mSize, "Base class not contained in derived class"); - - // Add base class - BaseClass base; - base.mRTTI = inRTTI; - base.mOffset = inOffset; - mBaseClasses.push_back(base); - - // Add attributes of base class - for (const SerializableAttribute &a : inRTTI->mAttributes) - mAttributes.push_back(SerializableAttribute(a, inOffset)); -} - -bool RTTI::operator == (const RTTI &inRHS) const -{ - // Compare addresses - if (this == &inRHS) - return true; - - // Check that the names differ (if that is the case we probably have two instances - // of the same attribute info across the program, probably the second is in a DLL) - JPH_ASSERT(strcmp(mName, inRHS.mName) != 0); - return false; -} - -bool RTTI::IsKindOf(const RTTI *inRTTI) const -{ - // Check if this is the same type - if (this == inRTTI) - return true; - - // Check all base classes - for (const BaseClass &b : mBaseClasses) - if (b.mRTTI->IsKindOf(inRTTI)) - return true; - - return false; -} - -const void *RTTI::CastTo(const void *inObject, const RTTI *inRTTI) const -{ - JPH_ASSERT(inObject != nullptr); - - // Check if this is the same type - if (this == inRTTI) - return inObject; - - // Check all base classes - for (const BaseClass &b : mBaseClasses) - { - // Cast the pointer to the base class - const void *casted = (const void *)(((const uint8 *)inObject) + b.mOffset); - - // Test base class - const void *rv = b.mRTTI->CastTo(casted, inRTTI); - if (rv != nullptr) - return rv; - } - - // Not possible to cast - return nullptr; -} - -void RTTI::AddAttribute(const SerializableAttribute &inAttribute) -{ - mAttributes.push_back(inAttribute); -} - -int RTTI::GetAttributeCount() const -{ - return (int)mAttributes.size(); -} - -const SerializableAttribute &RTTI::GetAttribute(int inIdx) const -{ - return mAttributes[inIdx]; -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/RTTI.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/RTTI.h deleted file mode 100644 index d9816938fca..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/RTTI.h +++ /dev/null @@ -1,436 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -////////////////////////////////////////////////////////////////////////////////////////// -// RTTI -////////////////////////////////////////////////////////////////////////////////////////// - -/// Light weight runtime type information system. This way we don't need to turn -/// on the default RTTI system of the compiler (introducing a possible overhead for every -/// class) -/// -/// Notes: -/// - An extra virtual member function is added. This adds 8 bytes to the size of -/// an instance of the class (unless you are already using virtual functions). -/// -/// To use RTTI on a specific class use: -/// -/// Header file: -/// -/// class Foo -/// { -/// JPH_DECLARE_RTTI_VIRTUAL_BASE(Foo) -/// } -/// -/// class Bar : public Foo -/// { -/// JPH_DECLARE_RTTI_VIRTUAL(Bar) -/// }; -/// -/// Implementation file: -/// -/// JPH_IMPLEMENT_RTTI_VIRTUAL_BASE(Foo) -/// { -/// } -/// -/// JPH_IMPLEMENT_RTTI_VIRTUAL(Bar) -/// { -/// JPH_ADD_BASE_CLASS(Bar, Foo) // Multiple inheritance is allowed, just do JPH_ADD_BASE_CLASS for every base class -/// } -/// -/// For abstract classes use: -/// -/// Header file: -/// -/// class Foo -/// { -/// JPH_DECLARE_RTTI_ABSTRACT_BASE(Foo) -/// -/// public: -/// virtual void AbstractFunction() = 0; -/// } -/// -/// class Bar : public Foo -/// { -/// JPH_DECLARE_RTTI_VIRTUAL(Bar) -/// -/// public: -/// virtual void AbstractFunction() { } // Function is now implemented so this class is no longer abstract -/// }; -/// -/// Implementation file: -/// -/// JPH_IMPLEMENT_RTTI_ABSTRACT_BASE(Foo) -/// { -/// } -/// -/// JPH_IMPLEMENT_RTTI_VIRTUAL(Bar) -/// { -/// JPH_ADD_BASE_CLASS(Bar, Foo) -/// } -/// -/// Example of usage in a program: -/// -/// Foo *foo_ptr = new Foo; -/// Foo *bar_ptr = new Bar; -/// -/// IsType(foo_ptr, RTTI(Bar)) returns false -/// IsType(bar_ptr, RTTI(Bar)) returns true -/// -/// IsKindOf(foo_ptr, RTTI(Bar)) returns false -/// IsKindOf(bar_ptr, RTTI(Foo)) returns true -/// IsKindOf(bar_ptr, RTTI(Bar)) returns true -/// -/// StaticCast(foo_ptr) asserts and returns foo_ptr casted to pBar -/// StaticCast(bar_ptr) returns bar_ptr casted to pBar -/// -/// DynamicCast(foo_ptr) returns nullptr -/// DynamicCast(bar_ptr) returns bar_ptr casted to pBar -/// -/// Other feature of DynamicCast: -/// -/// class A { int data[5]; }; -/// class B { int data[7]; }; -/// class C : public A, public B { int data[9]; }; -/// -/// C *c = new C; -/// A *a = c; -/// -/// Note that: -/// -/// B *b = (B *)a; -/// -/// generates an invalid pointer, -/// -/// B *b = StaticCast(a); -/// -/// doesn't compile, and -/// -/// B *b = DynamicCast(a); -/// -/// does the correct cast -class JPH_EXPORT RTTI -{ -public: - /// Function to create an object - using pCreateObjectFunction = void *(*)(); - - /// Function to destroy an object - using pDestructObjectFunction = void (*)(void *inObject); - - /// Function to initialize the runtime type info structure - using pCreateRTTIFunction = void (*)(RTTI &inRTTI); - - /// Constructor - RTTI(const char *inName, int inSize, pCreateObjectFunction inCreateObject, pDestructObjectFunction inDestructObject); - RTTI(const char *inName, int inSize, pCreateObjectFunction inCreateObject, pDestructObjectFunction inDestructObject, pCreateRTTIFunction inCreateRTTI); - - // Properties - inline const char * GetName() const { return mName; } - void SetName(const char *inName) { mName = inName; } - inline int GetSize() const { return mSize; } - bool IsAbstract() const { return mCreate == nullptr || mDestruct == nullptr; } - int GetBaseClassCount() const; - const RTTI * GetBaseClass(int inIdx) const; - uint32 GetHash() const; - - /// Create an object of this type (returns nullptr if the object is abstract) - void * CreateObject() const; - - /// Destruct object of this type (does nothing if the object is abstract) - void DestructObject(void *inObject) const; - - /// Add base class - void AddBaseClass(const RTTI *inRTTI, int inOffset); - - /// Equality operators - bool operator == (const RTTI &inRHS) const; - bool operator != (const RTTI &inRHS) const { return !(*this == inRHS); } - - /// Test if this class is derived from class of type inRTTI - bool IsKindOf(const RTTI *inRTTI) const; - - /// Cast inObject of this type to object of type inRTTI, returns nullptr if the cast is unsuccessful - const void * CastTo(const void *inObject, const RTTI *inRTTI) const; - - /// Attribute access - void AddAttribute(const SerializableAttribute &inAttribute); - int GetAttributeCount() const; - const SerializableAttribute & GetAttribute(int inIdx) const; - -protected: - /// Base class information - struct BaseClass - { - const RTTI * mRTTI; - int mOffset; - }; - - const char * mName; ///< Class name - int mSize; ///< Class size - StaticArray mBaseClasses; ///< Names of base classes - pCreateObjectFunction mCreate; ///< Pointer to a function that will create a new instance of this class - pDestructObjectFunction mDestruct; ///< Pointer to a function that will destruct an object of this class - StaticArray mAttributes; ///< All attributes of this class -}; - -////////////////////////////////////////////////////////////////////////////////////////// -// Add run time type info to types that don't have virtual functions -////////////////////////////////////////////////////////////////////////////////////////// - -// JPH_DECLARE_RTTI_NON_VIRTUAL -#define JPH_DECLARE_RTTI_NON_VIRTUAL(linkage, class_name) \ -public: \ - JPH_OVERRIDE_NEW_DELETE \ - friend linkage RTTI * GetRTTIOfType(class_name *); \ - friend inline const RTTI * GetRTTI([[maybe_unused]] const class_name *inObject) { return GetRTTIOfType(static_cast(nullptr)); }\ - static void sCreateRTTI(RTTI &inRTTI); \ - -// JPH_IMPLEMENT_RTTI_NON_VIRTUAL -#define JPH_IMPLEMENT_RTTI_NON_VIRTUAL(class_name) \ - RTTI * GetRTTIOfType(class_name *) \ - { \ - static RTTI rtti(#class_name, sizeof(class_name), []() -> void * { return new class_name; }, [](void *inObject) { delete (class_name *)inObject; }, &class_name::sCreateRTTI); \ - return &rtti; \ - } \ - void class_name::sCreateRTTI(RTTI &inRTTI) \ - -////////////////////////////////////////////////////////////////////////////////////////// -// Same as above, but when you cannot insert the declaration in the class -// itself, for example for templates and third party classes -////////////////////////////////////////////////////////////////////////////////////////// - -// JPH_DECLARE_RTTI_OUTSIDE_CLASS -#define JPH_DECLARE_RTTI_OUTSIDE_CLASS(linkage, class_name) \ - linkage RTTI * GetRTTIOfType(class_name *); \ - inline const RTTI * GetRTTI(const class_name *inObject) { return GetRTTIOfType((class_name *)nullptr); }\ - void CreateRTTI##class_name(RTTI &inRTTI); \ - -// JPH_IMPLEMENT_RTTI_OUTSIDE_CLASS -#define JPH_IMPLEMENT_RTTI_OUTSIDE_CLASS(class_name) \ - RTTI * GetRTTIOfType(class_name *) \ - { \ - static RTTI rtti((const char *)#class_name, sizeof(class_name), []() -> void * { return new class_name; }, [](void *inObject) { delete (class_name *)inObject; }, &CreateRTTI##class_name); \ - return &rtti; \ - } \ - void CreateRTTI##class_name(RTTI &inRTTI) - -////////////////////////////////////////////////////////////////////////////////////////// -// Same as above, but for classes that have virtual functions -////////////////////////////////////////////////////////////////////////////////////////// - -#define JPH_DECLARE_RTTI_HELPER(linkage, class_name, modifier) \ -public: \ - JPH_OVERRIDE_NEW_DELETE \ - friend linkage RTTI * GetRTTIOfType(class_name *); \ - friend inline const RTTI * GetRTTI(const class_name *inObject) { return inObject->GetRTTI(); } \ - virtual const RTTI * GetRTTI() const modifier; \ - virtual const void * CastTo(const RTTI *inRTTI) const modifier; \ - static void sCreateRTTI(RTTI &inRTTI); \ - -// JPH_DECLARE_RTTI_VIRTUAL - for derived classes with RTTI -#define JPH_DECLARE_RTTI_VIRTUAL(linkage, class_name) \ - JPH_DECLARE_RTTI_HELPER(linkage, class_name, override) - -// JPH_IMPLEMENT_RTTI_VIRTUAL -#define JPH_IMPLEMENT_RTTI_VIRTUAL(class_name) \ - RTTI * GetRTTIOfType(class_name *) \ - { \ - static RTTI rtti(#class_name, sizeof(class_name), []() -> void * { return new class_name; }, [](void *inObject) { delete (class_name *)inObject; }, &class_name::sCreateRTTI); \ - return &rtti; \ - } \ - const RTTI * class_name::GetRTTI() const \ - { \ - return JPH_RTTI(class_name); \ - } \ - const void * class_name::CastTo(const RTTI *inRTTI) const \ - { \ - return JPH_RTTI(class_name)->CastTo((const void *)this, inRTTI); \ - } \ - void class_name::sCreateRTTI(RTTI &inRTTI) \ - -// JPH_DECLARE_RTTI_VIRTUAL_BASE - for concrete base class that has RTTI -#define JPH_DECLARE_RTTI_VIRTUAL_BASE(linkage, class_name) \ - JPH_DECLARE_RTTI_HELPER(linkage, class_name, ) - -// JPH_IMPLEMENT_RTTI_VIRTUAL_BASE -#define JPH_IMPLEMENT_RTTI_VIRTUAL_BASE(class_name) \ - JPH_IMPLEMENT_RTTI_VIRTUAL(class_name) - -// JPH_DECLARE_RTTI_ABSTRACT - for derived abstract class that have RTTI -#define JPH_DECLARE_RTTI_ABSTRACT(linkage, class_name) \ - JPH_DECLARE_RTTI_HELPER(linkage, class_name, override) - -// JPH_IMPLEMENT_RTTI_ABSTRACT -#define JPH_IMPLEMENT_RTTI_ABSTRACT(class_name) \ - RTTI * GetRTTIOfType(class_name *) \ - { \ - static RTTI rtti(#class_name, sizeof(class_name), nullptr, [](void *inObject) { delete (class_name *)inObject; }, &class_name::sCreateRTTI); \ - return &rtti; \ - } \ - const RTTI * class_name::GetRTTI() const \ - { \ - return JPH_RTTI(class_name); \ - } \ - const void * class_name::CastTo(const RTTI *inRTTI) const \ - { \ - return JPH_RTTI(class_name)->CastTo((const void *)this, inRTTI); \ - } \ - void class_name::sCreateRTTI(RTTI &inRTTI) \ - -// JPH_DECLARE_RTTI_ABSTRACT_BASE - for abstract base class that has RTTI -#define JPH_DECLARE_RTTI_ABSTRACT_BASE(linkage, class_name) \ - JPH_DECLARE_RTTI_HELPER(linkage, class_name, ) - -// JPH_IMPLEMENT_RTTI_ABSTRACT_BASE -#define JPH_IMPLEMENT_RTTI_ABSTRACT_BASE(class_name) \ - JPH_IMPLEMENT_RTTI_ABSTRACT(class_name) - -////////////////////////////////////////////////////////////////////////////////////////// -// Declare an RTTI class for registering with the factory -////////////////////////////////////////////////////////////////////////////////////////// - -#define JPH_DECLARE_RTTI_FOR_FACTORY(linkage, class_name) \ - linkage RTTI * GetRTTIOfType(class class_name *); - -#define JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(linkage, name_space, class_name) \ - namespace name_space { \ - class class_name; \ - linkage RTTI * GetRTTIOfType(class class_name *); \ - } - -////////////////////////////////////////////////////////////////////////////////////////// -// Find the RTTI of a class -////////////////////////////////////////////////////////////////////////////////////////// - -#define JPH_RTTI(class_name) GetRTTIOfType(static_cast(nullptr)) - -////////////////////////////////////////////////////////////////////////////////////////// -// Macro to rename a class, useful for embedded classes: -// -// class A { class B { }; } -// -// Now use JPH_RENAME_CLASS(B, A::B) to avoid conflicts with other classes named B -////////////////////////////////////////////////////////////////////////////////////////// - -// JPH_RENAME_CLASS -#define JPH_RENAME_CLASS(class_name, new_name) \ - inRTTI.SetName(#new_name); - -////////////////////////////////////////////////////////////////////////////////////////// -// Macro to add base classes -////////////////////////////////////////////////////////////////////////////////////////// - -/// Define very dirty macro to get the offset of a baseclass into a class -#define JPH_BASE_CLASS_OFFSET(inClass, inBaseClass) ((int(uint64((inBaseClass *)((inClass *)0x10000))))-0x10000) - -// JPH_ADD_BASE_CLASS -#define JPH_ADD_BASE_CLASS(class_name, base_class_name) \ - inRTTI.AddBaseClass(JPH_RTTI(base_class_name), JPH_BASE_CLASS_OFFSET(class_name, base_class_name)); - -////////////////////////////////////////////////////////////////////////////////////////// -// Macros and templates to identify a class -////////////////////////////////////////////////////////////////////////////////////////// - -/// Check if inObject is of DstType -template -inline bool IsType(const Type *inObject, const RTTI *inRTTI) -{ - return inObject == nullptr || *inObject->GetRTTI() == *inRTTI; -} - -template -inline bool IsType(const RefConst &inObject, const RTTI *inRTTI) -{ - return inObject == nullptr || *inObject->GetRTTI() == *inRTTI; -} - -template -inline bool IsType(const Ref &inObject, const RTTI *inRTTI) -{ - return inObject == nullptr || *inObject->GetRTTI() == *inRTTI; -} - -/// Check if inObject is or is derived from DstType -template -inline bool IsKindOf(const Type *inObject, const RTTI *inRTTI) -{ - return inObject == nullptr || inObject->GetRTTI()->IsKindOf(inRTTI); -} - -template -inline bool IsKindOf(const RefConst &inObject, const RTTI *inRTTI) -{ - return inObject == nullptr || inObject->GetRTTI()->IsKindOf(inRTTI); -} - -template -inline bool IsKindOf(const Ref &inObject, const RTTI *inRTTI) -{ - return inObject == nullptr || inObject->GetRTTI()->IsKindOf(inRTTI); -} - -/// Cast inObject to DstType, asserts on failure -template -inline const DstType *StaticCast(const SrcType *inObject) -{ - JPH_ASSERT(IsKindOf(inObject, JPH_RTTI(DstType)), "Invalid cast"); - return static_cast(inObject); -} - -template -inline DstType *StaticCast(SrcType *inObject) -{ - JPH_ASSERT(IsKindOf(inObject, JPH_RTTI(DstType)), "Invalid cast"); - return static_cast(inObject); -} - -template -inline RefConst StaticCast(RefConst &inObject) -{ - JPH_ASSERT(IsKindOf(inObject, JPH_RTTI(DstType)), "Invalid cast"); - return static_cast(inObject.GetPtr()); -} - -template -inline Ref StaticCast(Ref &inObject) -{ - JPH_ASSERT(IsKindOf(inObject, JPH_RTTI(DstType)), "Invalid cast"); - return static_cast(inObject.GetPtr()); -} - -/// Cast inObject to DstType, returns nullptr on failure -template -inline const DstType *DynamicCast(const SrcType *inObject) -{ - return inObject != nullptr? reinterpret_cast(inObject->CastTo(JPH_RTTI(DstType))) : nullptr; -} - -template -inline DstType *DynamicCast(SrcType *inObject) -{ - return inObject != nullptr? const_cast(reinterpret_cast(inObject->CastTo(JPH_RTTI(DstType)))) : nullptr; -} - -template -inline RefConst DynamicCast(RefConst &inObject) -{ - return inObject != nullptr? reinterpret_cast(inObject->CastTo(JPH_RTTI(DstType))) : nullptr; -} - -template -inline Ref DynamicCast(Ref &inObject) -{ - return inObject != nullptr? const_cast(reinterpret_cast(inObject->CastTo(JPH_RTTI(DstType)))) : nullptr; -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Reference.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Reference.h deleted file mode 100644 index d6fdf1da512..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Reference.h +++ /dev/null @@ -1,226 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -// Forward declares -template class Ref; -template class RefConst; - -/// Simple class to facilitate reference counting / releasing -/// Derive your class from RefTarget and you can reference it by using Ref or RefConst -/// -/// Reference counting classes keep an integer which indicates how many references -/// to the object are active. Reference counting objects are derived from RefTarget -/// and staT & their life with a reference count of zero. They can then be assigned -/// to equivalents of pointers (Ref) which will increase the reference count immediately. -/// If the destructor of Ref is called or another object is assigned to the reference -/// counting pointer it will decrease the reference count of the object again. If this -/// reference count becomes zero, the object is destroyed. -/// -/// This provides a very powerful mechanism to prevent memory leaks, but also gives -/// some responsibility to the programmer. The most notable point is that you cannot -/// have one object reference another and have the other reference the first one -/// back, because this way the reference count of both objects will never become -/// lower than 1, resulting in a memory leak. By carefully designing your classes -/// (and particularly identifying who owns who in the class hierarchy) you can avoid -/// these problems. -template -class RefTarget -{ -public: - /// Constructor - inline RefTarget() = default; - inline RefTarget(const RefTarget &) { /* Do not copy refcount */ } - inline ~RefTarget() { JPH_IF_ENABLE_ASSERTS(uint32 value = mRefCount.load(memory_order_relaxed);) JPH_ASSERT(value == 0 || value == cEmbedded); } ///< assert no one is referencing us - - /// Mark this class as embedded, this means the type can be used in a compound or constructed on the stack. - /// The Release function will never destruct the object, it is assumed the destructor will be called by whoever allocated - /// the object and at that point in time it is checked that no references are left to the structure. - inline void SetEmbedded() const { JPH_IF_ENABLE_ASSERTS(uint32 old = ) mRefCount.fetch_add(cEmbedded, memory_order_relaxed); JPH_ASSERT(old < cEmbedded); } - - /// Assignment operator - inline RefTarget & operator = (const RefTarget &) { /* Don't copy refcount */ return *this; } - - /// Get current refcount of this object - uint32 GetRefCount() const { return mRefCount.load(memory_order_relaxed); } - - /// Add or release a reference to this object - inline void AddRef() const - { - // Adding a reference can use relaxed memory ordering - mRefCount.fetch_add(1, memory_order_relaxed); - } - - inline void Release() const - { - // Releasing a reference must use release semantics... - if (mRefCount.fetch_sub(1, memory_order_release) == 1) - { - // ... so that we can use acquire to ensure that we see any updates from other threads that released a ref before deleting the object - atomic_thread_fence(memory_order_acquire); - delete static_cast(this); - } - } - - /// INTERNAL HELPER FUNCTION USED BY SERIALIZATION - static int sInternalGetRefCountOffset() { return offsetof(T, mRefCount); } - -protected: - static constexpr uint32 cEmbedded = 0x0ebedded; ///< A large value that gets added to the refcount to mark the object as embedded - - mutable atomic mRefCount = 0; ///< Current reference count -}; - -/// Pure virtual version of RefTarget -class JPH_EXPORT RefTargetVirtual -{ -public: - /// Virtual destructor - virtual ~RefTargetVirtual() = default; - - /// Virtual add reference - virtual void AddRef() = 0; - - /// Virtual release reference - virtual void Release() = 0; -}; - -/// Class for automatic referencing, this is the equivalent of a pointer to type T -/// if you assign a value to this class it will increment the reference count by one -/// of this object, and if you assign something else it will decrease the reference -/// count of the first object again. If it reaches a reference count of zero it will -/// be deleted -template -class Ref -{ -public: - /// Constructor - inline Ref() : mPtr(nullptr) { } - inline Ref(T *inRHS) : mPtr(inRHS) { AddRef(); } - inline Ref(const Ref &inRHS) : mPtr(inRHS.mPtr) { AddRef(); } - inline Ref(Ref &&inRHS) noexcept : mPtr(inRHS.mPtr) { inRHS.mPtr = nullptr; } - inline ~Ref() { Release(); } - - /// Assignment operators - inline Ref & operator = (T *inRHS) { if (mPtr != inRHS) { Release(); mPtr = inRHS; AddRef(); } return *this; } - inline Ref & operator = (const Ref &inRHS) { if (mPtr != inRHS.mPtr) { Release(); mPtr = inRHS.mPtr; AddRef(); } return *this; } - inline Ref & operator = (Ref &&inRHS) noexcept { if (mPtr != inRHS.mPtr) { Release(); mPtr = inRHS.mPtr; inRHS.mPtr = nullptr; } return *this; } - - /// Casting operators - inline operator T *() const { return mPtr; } - - /// Access like a normal pointer - inline T * operator -> () const { return mPtr; } - inline T & operator * () const { return *mPtr; } - - /// Comparison - inline bool operator == (const T * inRHS) const { return mPtr == inRHS; } - inline bool operator == (const Ref &inRHS) const { return mPtr == inRHS.mPtr; } - inline bool operator != (const T * inRHS) const { return mPtr != inRHS; } - inline bool operator != (const Ref &inRHS) const { return mPtr != inRHS.mPtr; } - - /// Get pointer - inline T * GetPtr() const { return mPtr; } - - /// INTERNAL HELPER FUNCTION USED BY SERIALIZATION - void ** InternalGetPointer() { return reinterpret_cast(&mPtr); } - -private: - template friend class RefConst; - - /// Use "variable = nullptr;" to release an object, do not call these functions - inline void AddRef() { if (mPtr != nullptr) mPtr->AddRef(); } - inline void Release() { if (mPtr != nullptr) mPtr->Release(); } - - T * mPtr; ///< Pointer to object that we are reference counting -}; - -/// Class for automatic referencing, this is the equivalent of a CONST pointer to type T -/// if you assign a value to this class it will increment the reference count by one -/// of this object, and if you assign something else it will decrease the reference -/// count of the first object again. If it reaches a reference count of zero it will -/// be deleted -template -class RefConst -{ -public: - /// Constructor - inline RefConst() : mPtr(nullptr) { } - inline RefConst(const T * inRHS) : mPtr(inRHS) { AddRef(); } - inline RefConst(const RefConst &inRHS) : mPtr(inRHS.mPtr) { AddRef(); } - inline RefConst(RefConst &&inRHS) noexcept : mPtr(inRHS.mPtr) { inRHS.mPtr = nullptr; } - inline RefConst(const Ref &inRHS) : mPtr(inRHS.mPtr) { AddRef(); } - inline RefConst(Ref &&inRHS) noexcept : mPtr(inRHS.mPtr) { inRHS.mPtr = nullptr; } - inline ~RefConst() { Release(); } - - /// Assignment operators - inline RefConst & operator = (const T * inRHS) { if (mPtr != inRHS) { Release(); mPtr = inRHS; AddRef(); } return *this; } - inline RefConst & operator = (const RefConst &inRHS) { if (mPtr != inRHS.mPtr) { Release(); mPtr = inRHS.mPtr; AddRef(); } return *this; } - inline RefConst & operator = (RefConst &&inRHS) noexcept { if (mPtr != inRHS.mPtr) { Release(); mPtr = inRHS.mPtr; inRHS.mPtr = nullptr; } return *this; } - inline RefConst & operator = (const Ref &inRHS) { if (mPtr != inRHS.mPtr) { Release(); mPtr = inRHS.mPtr; AddRef(); } return *this; } - inline RefConst & operator = (Ref &&inRHS) noexcept { if (mPtr != inRHS.mPtr) { Release(); mPtr = inRHS.mPtr; inRHS.mPtr = nullptr; } return *this; } - - /// Casting operators - inline operator const T * () const { return mPtr; } - - /// Access like a normal pointer - inline const T * operator -> () const { return mPtr; } - inline const T & operator * () const { return *mPtr; } - - /// Comparison - inline bool operator == (const T * inRHS) const { return mPtr == inRHS; } - inline bool operator == (const RefConst &inRHS) const { return mPtr == inRHS.mPtr; } - inline bool operator == (const Ref &inRHS) const { return mPtr == inRHS.mPtr; } - inline bool operator != (const T * inRHS) const { return mPtr != inRHS; } - inline bool operator != (const RefConst &inRHS) const { return mPtr != inRHS.mPtr; } - inline bool operator != (const Ref &inRHS) const { return mPtr != inRHS.mPtr; } - - /// Get pointer - inline const T * GetPtr() const { return mPtr; } - - /// INTERNAL HELPER FUNCTION USED BY SERIALIZATION - void ** InternalGetPointer() { return const_cast(reinterpret_cast(&mPtr)); } - -private: - /// Use "variable = nullptr;" to release an object, do not call these functions - inline void AddRef() { if (mPtr != nullptr) mPtr->AddRef(); } - inline void Release() { if (mPtr != nullptr) mPtr->Release(); } - - const T * mPtr; ///< Pointer to object that we are reference counting -}; - -JPH_NAMESPACE_END - -JPH_SUPPRESS_WARNING_PUSH -JPH_CLANG_SUPPRESS_WARNING("-Wc++98-compat") - -namespace std -{ - /// Declare std::hash for Ref - template - struct hash> - { - size_t operator () (const JPH::Ref &inRHS) const - { - return hash { }(inRHS.GetPtr()); - } - }; - - /// Declare std::hash for RefConst - template - struct hash> - { - size_t operator () (const JPH::RefConst &inRHS) const - { - return hash { }(inRHS.GetPtr()); - } - }; -} - -JPH_SUPPRESS_WARNING_POP diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Result.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Result.h deleted file mode 100644 index da3b98cb39e..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Result.h +++ /dev/null @@ -1,177 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -JPH_NAMESPACE_BEGIN - -// GCC doesn't properly detect that mState is used to ensure that mResult is initialized -JPH_GCC_SUPPRESS_WARNING("-Wmaybe-uninitialized") - -/// Helper class that either contains a valid result or an error -template -class Result -{ -public: - /// Default constructor - Result() { } - - /// Copy constructor - Result(const Result &inRHS) : - mState(inRHS.mState) - { - switch (inRHS.mState) - { - case EState::Valid: - ::new (&mResult) Type (inRHS.mResult); - break; - - case EState::Error: - ::new (&mError) String(inRHS.mError); - break; - - case EState::Invalid: - break; - } - } - - /// Move constructor - Result(Result &&inRHS) noexcept : - mState(inRHS.mState) - { - switch (inRHS.mState) - { - case EState::Valid: - ::new (&mResult) Type (std::move(inRHS.mResult)); - break; - - case EState::Error: - ::new (&mError) String(std::move(inRHS.mError)); - break; - - case EState::Invalid: - break; - } - - // Don't reset the state of inRHS, the destructors still need to be called after a move operation - } - - /// Destructor - ~Result() { Clear(); } - - /// Copy assignment - Result & operator = (const Result &inRHS) - { - Clear(); - - mState = inRHS.mState; - - switch (inRHS.mState) - { - case EState::Valid: - ::new (&mResult) Type (inRHS.mResult); - break; - - case EState::Error: - ::new (&mError) String(inRHS.mError); - break; - - case EState::Invalid: - break; - } - - return *this; - } - - /// Move assignment - Result & operator = (Result &&inRHS) noexcept - { - Clear(); - - mState = inRHS.mState; - - switch (inRHS.mState) - { - case EState::Valid: - ::new (&mResult) Type (std::move(inRHS.mResult)); - break; - - case EState::Error: - ::new (&mError) String(std::move(inRHS.mError)); - break; - - case EState::Invalid: - break; - } - - // Don't reset the state of inRHS, the destructors still need to be called after a move operation - - return *this; - } - - /// Clear result or error - void Clear() - { - switch (mState) - { - case EState::Valid: - mResult.~Type(); - break; - - case EState::Error: - mError.~String(); - break; - - case EState::Invalid: - break; - } - - mState = EState::Invalid; - } - - /// Checks if the result is still uninitialized - bool IsEmpty() const { return mState == EState::Invalid; } - - /// Checks if the result is valid - bool IsValid() const { return mState == EState::Valid; } - - /// Get the result value - const Type & Get() const { JPH_ASSERT(IsValid()); return mResult; } - - /// Set the result value - void Set(const Type &inResult) { Clear(); ::new (&mResult) Type(inResult); mState = EState::Valid; } - - /// Set the result value (move value) - void Set(Type &&inResult) { Clear(); ::new (&mResult) Type(std::move(inResult)); mState = EState::Valid; } - - /// Check if we had an error - bool HasError() const { return mState == EState::Error; } - - /// Get the error value - const String & GetError() const { JPH_ASSERT(HasError()); return mError; } - - /// Set an error value - void SetError(const char *inError) { Clear(); ::new (&mError) String(inError); mState = EState::Error; } - void SetError(const string_view &inError) { Clear(); ::new (&mError) String(inError); mState = EState::Error; } - void SetError(String &&inError) { Clear(); ::new (&mError) String(std::move(inError)); mState = EState::Error; } - -private: - union - { - Type mResult; ///< The actual result object - String mError; ///< The error description if the result failed - }; - - /// State of the result - enum class EState : uint8 - { - Invalid, - Valid, - Error - }; - - EState mState = EState::Invalid; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/STLAlignedAllocator.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/STLAlignedAllocator.h deleted file mode 100644 index e982ea0238d..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/STLAlignedAllocator.h +++ /dev/null @@ -1,66 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -JPH_NAMESPACE_BEGIN - -/// STL allocator that takes care that memory is aligned to N bytes -template -class STLAlignedAllocator -{ -public: - using value_type = T; - - /// Pointer to type - using pointer = T *; - using const_pointer = const T *; - - /// Reference to type. - /// Can be removed in C++20. - using reference = T &; - using const_reference = const T &; - - using size_type = size_t; - using difference_type = ptrdiff_t; - - /// Constructor - inline STLAlignedAllocator() = default; - - /// Constructor from other allocator - template - inline explicit STLAlignedAllocator(const STLAlignedAllocator &) { } - - /// Allocate memory - inline pointer allocate(size_type inN) - { - return (pointer)AlignedAllocate(inN * sizeof(value_type), N); - } - - /// Free memory - inline void deallocate(pointer inPointer, size_type) - { - AlignedFree(inPointer); - } - - /// Allocators are stateless so assumed to be equal - inline bool operator == (const STLAlignedAllocator &) const - { - return true; - } - - inline bool operator != (const STLAlignedAllocator &) const - { - return false; - } - - /// Converting to allocator for other type - template - struct rebind - { - using other = STLAlignedAllocator; - }; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/STLAllocator.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/STLAllocator.h deleted file mode 100644 index 96417b0cbd1..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/STLAllocator.h +++ /dev/null @@ -1,102 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -JPH_NAMESPACE_BEGIN - -#ifndef JPH_DISABLE_CUSTOM_ALLOCATOR - -/// STL allocator that forwards to our allocation functions -template -class STLAllocator -{ -public: - using value_type = T; - - /// Pointer to type - using pointer = T *; - using const_pointer = const T *; - - /// Reference to type. - /// Can be removed in C++20. - using reference = T &; - using const_reference = const T &; - - using size_type = size_t; - using difference_type = ptrdiff_t; - - /// Constructor - inline STLAllocator() = default; - - /// Constructor from other allocator - template - inline STLAllocator(const STLAllocator &) { } - - /// Allocate memory - inline pointer allocate(size_type inN) - { - if constexpr (alignof(T) > (JPH_CPU_ADDRESS_BITS == 32? 8 : 16)) - return pointer(AlignedAllocate(inN * sizeof(value_type), alignof(T))); - else - return pointer(Allocate(inN * sizeof(value_type))); - } - - /// Free memory - inline void deallocate(pointer inPointer, size_type) - { - if constexpr (alignof(T) > (JPH_CPU_ADDRESS_BITS == 32? 8 : 16)) - AlignedFree(inPointer); - else - Free(inPointer); - } - - /// Allocators are stateless so assumed to be equal - inline bool operator == (const STLAllocator &) const - { - return true; - } - - inline bool operator != (const STLAllocator &) const - { - return false; - } - - /// Converting to allocator for other type - template - struct rebind - { - using other = STLAllocator; - }; -}; - -#else - -template using STLAllocator = std::allocator; - -#endif // !JPH_DISABLE_CUSTOM_ALLOCATOR - -// Declare STL containers that use our allocator -template using Array = std::vector>; -using String = std::basic_string, STLAllocator>; -using IStringStream = std::basic_istringstream, STLAllocator>; - -JPH_NAMESPACE_END - -#if (!defined(JPH_PLATFORM_WINDOWS) || defined(JPH_COMPILER_MINGW)) && !defined(JPH_DISABLE_CUSTOM_ALLOCATOR) - -namespace std -{ - /// Declare std::hash for String, for some reason on Linux based platforms template deduction takes the wrong variant - template <> - struct hash - { - inline size_t operator () (const JPH::String &inRHS) const - { - return hash { } (inRHS); - } - }; -} - -#endif // (!JPH_PLATFORM_WINDOWS || JPH_COMPILER_MINGW) && !JPH_DISABLE_CUSTOM_ALLOCATOR diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/STLTempAllocator.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/STLTempAllocator.h deleted file mode 100644 index 44ed7d6fedf..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/STLTempAllocator.h +++ /dev/null @@ -1,77 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -/// STL allocator that wraps around TempAllocator -template -class STLTempAllocator -{ -public: - using value_type = T; - - /// Pointer to type - using pointer = T *; - using const_pointer = const T *; - - /// Reference to type. - /// Can be removed in C++20. - using reference = T &; - using const_reference = const T &; - - using size_type = size_t; - using difference_type = ptrdiff_t; - - /// Constructor - inline STLTempAllocator(TempAllocator &inAllocator) : mAllocator(inAllocator) { } - - /// Constructor from other allocator - template - inline explicit STLTempAllocator(const STLTempAllocator &inRHS) : mAllocator(inRHS.GetAllocator()) { } - - /// Allocate memory - inline pointer allocate(size_type inN) - { - return pointer(mAllocator.Allocate(uint(inN * sizeof(value_type)))); - } - - /// Free memory - inline void deallocate(pointer inPointer, size_type inN) - { - mAllocator.Free(inPointer, uint(inN * sizeof(value_type))); - } - - /// Allocators are stateless so assumed to be equal - inline bool operator == (const STLTempAllocator &) const - { - return true; - } - - inline bool operator != (const STLTempAllocator &) const - { - return false; - } - - /// Converting to allocator for other type - template - struct rebind - { - using other = STLTempAllocator; - }; - - /// Get our temp allocator - TempAllocator & GetAllocator() const - { - return mAllocator; - } - -private: - TempAllocator & mAllocator; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Semaphore.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Semaphore.cpp deleted file mode 100644 index 294aa755f81..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Semaphore.cpp +++ /dev/null @@ -1,80 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2023 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include - -#ifdef JPH_PLATFORM_WINDOWS - JPH_SUPPRESS_WARNING_PUSH - JPH_MSVC_SUPPRESS_WARNING(5039) // winbase.h(13179): warning C5039: 'TpSetCallbackCleanupGroup': pointer or reference to potentially throwing function passed to 'extern "C"' function under -EHc. Undefined behavior may occur if this function throws an exception. - #define WIN32_LEAN_AND_MEAN -#ifndef JPH_COMPILER_MINGW - #include -#else - #include -#endif - - JPH_SUPPRESS_WARNING_POP -#endif - -JPH_NAMESPACE_BEGIN - -Semaphore::Semaphore() -{ -#ifdef JPH_PLATFORM_WINDOWS - mSemaphore = CreateSemaphore(nullptr, 0, INT_MAX, nullptr); -#endif -} - -Semaphore::~Semaphore() -{ -#ifdef JPH_PLATFORM_WINDOWS - CloseHandle(mSemaphore); -#endif -} - -void Semaphore::Release(uint inNumber) -{ - JPH_ASSERT(inNumber > 0); - -#ifdef JPH_PLATFORM_WINDOWS - int old_value = mCount.fetch_add(inNumber); - if (old_value < 0) - { - int new_value = old_value + (int)inNumber; - int num_to_release = min(new_value, 0) - old_value; - ::ReleaseSemaphore(mSemaphore, num_to_release, nullptr); - } -#else - std::lock_guard lock(mLock); - mCount += (int)inNumber; - if (inNumber > 1) - mWaitVariable.notify_all(); - else - mWaitVariable.notify_one(); -#endif -} - -void Semaphore::Acquire(uint inNumber) -{ - JPH_ASSERT(inNumber > 0); - -#ifdef JPH_PLATFORM_WINDOWS - int old_value = mCount.fetch_sub(inNumber); - int new_value = old_value - (int)inNumber; - if (new_value < 0) - { - int num_to_acquire = min(old_value, 0) - new_value; - for (int i = 0; i < num_to_acquire; ++i) - WaitForSingleObject(mSemaphore, INFINITE); - } -#else - std::unique_lock lock(mLock); - mCount -= (int)inNumber; - mWaitVariable.wait(lock, [this]() { return mCount >= 0; }); -#endif -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Semaphore.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Semaphore.h deleted file mode 100644 index 498b1b8d40b..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/Semaphore.h +++ /dev/null @@ -1,51 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2023 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -JPH_SUPPRESS_WARNINGS_STD_BEGIN -#include -#include -#include -JPH_SUPPRESS_WARNINGS_STD_END - -JPH_NAMESPACE_BEGIN - -// Things we're using from STL -using std::atomic; -using std::mutex; -using std::condition_variable; - -/// Implements a semaphore -/// When we switch to C++20 we can use counting_semaphore to unify this -class JPH_EXPORT Semaphore -{ -public: - /// Constructor - Semaphore(); - ~Semaphore(); - - /// Release the semaphore, signaling the thread waiting on the barrier that there may be work - void Release(uint inNumber = 1); - - /// Acquire the semaphore inNumber times - void Acquire(uint inNumber = 1); - - /// Get the current value of the semaphore - inline int GetValue() const { return mCount; } - -private: -#ifdef JPH_PLATFORM_WINDOWS - // On windows we use a semaphore object since it is more efficient than a lock and a condition variable - alignas(JPH_CACHE_LINE_SIZE) atomic mCount { 0 }; ///< We increment mCount for every release, to acquire we decrement the count. If the count is negative we know that we are waiting on the actual semaphore. - void * mSemaphore; ///< The semaphore is an expensive construct so we only acquire/release it if we know that we need to wait/have waiting threads -#else - // Other platforms: Emulate a semaphore using a mutex, condition variable and count - mutex mLock; - condition_variable mWaitVariable; - int mCount = 0; -#endif -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/StaticArray.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/StaticArray.h deleted file mode 100644 index 70e8cb9a8e8..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/StaticArray.h +++ /dev/null @@ -1,323 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -JPH_NAMESPACE_BEGIN - -/// Simple variable length array backed by a fixed size buffer -template -class [[nodiscard]] StaticArray -{ -public: - using value_type = T; - - using size_type = uint; - - static constexpr uint Capacity = N; - - /// Default constructor - StaticArray() = default; - - /// Constructor from initializer list - explicit StaticArray(std::initializer_list inList) - { - JPH_ASSERT(inList.size() <= N); - for (typename std::initializer_list::iterator i = inList.begin(); i != inList.end(); ++i) - ::new (reinterpret_cast(&mElements[mSize++])) T(*i); - } - - /// Copy constructor - StaticArray(const StaticArray &inRHS) - { - while (mSize < inRHS.mSize) - { - ::new (&mElements[mSize]) T(inRHS[mSize]); - ++mSize; - } - } - - /// Destruct all elements - ~StaticArray() - { - if constexpr (!is_trivially_destructible()) - for (T *e = reinterpret_cast(mElements), *end = e + mSize; e < end; ++e) - e->~T(); - } - - /// Destruct all elements and set length to zero - void clear() - { - if constexpr (!is_trivially_destructible()) - for (T *e = reinterpret_cast(mElements), *end = e + mSize; e < end; ++e) - e->~T(); - mSize = 0; - } - - /// Add element to the back of the array - void push_back(const T &inElement) - { - JPH_ASSERT(mSize < N); - ::new (&mElements[mSize++]) T(inElement); - } - - /// Construct element at the back of the array - template - void emplace_back(A &&... inElement) - { - JPH_ASSERT(mSize < N); - ::new (&mElements[mSize++]) T(std::forward(inElement)...); - } - - /// Remove element from the back of the array - void pop_back() - { - JPH_ASSERT(mSize > 0); - reinterpret_cast(mElements[--mSize]).~T(); - } - - /// Returns true if there are no elements in the array - bool empty() const - { - return mSize == 0; - } - - /// Returns amount of elements in the array - size_type size() const - { - return mSize; - } - - /// Returns maximum amount of elements the array can hold - size_type capacity() const - { - return N; - } - - /// Resize array to new length - void resize(size_type inNewSize) - { - JPH_ASSERT(inNewSize <= N); - if constexpr (!is_trivially_constructible()) - for (T *element = reinterpret_cast(mElements) + mSize, *element_end = reinterpret_cast(mElements) + inNewSize; element < element_end; ++element) - ::new (element) T; - if constexpr (!is_trivially_destructible()) - for (T *element = reinterpret_cast(mElements) + inNewSize, *element_end = reinterpret_cast(mElements) + mSize; element < element_end; ++element) - element->~T(); - mSize = inNewSize; - } - - using const_iterator = const T *; - - /// Iterators - const_iterator begin() const - { - return reinterpret_cast(mElements); - } - - const_iterator end() const - { - return reinterpret_cast(mElements + mSize); - } - - using iterator = T *; - - iterator begin() - { - return reinterpret_cast(mElements); - } - - iterator end() - { - return reinterpret_cast(mElements + mSize); - } - - const T * data() const - { - return reinterpret_cast(mElements); - } - - T * data() - { - return reinterpret_cast(mElements); - } - - /// Access element - T & operator [] (size_type inIdx) - { - JPH_ASSERT(inIdx < mSize); - return reinterpret_cast(mElements[inIdx]); - } - - const T & operator [] (size_type inIdx) const - { - JPH_ASSERT(inIdx < mSize); - return reinterpret_cast(mElements[inIdx]); - } - - /// Access element - T & at(size_type inIdx) - { - JPH_ASSERT(inIdx < mSize); - return reinterpret_cast(mElements[inIdx]); - } - - const T & at(size_type inIdx) const - { - JPH_ASSERT(inIdx < mSize); - return reinterpret_cast(mElements[inIdx]); - } - - /// First element in the array - const T & front() const - { - JPH_ASSERT(mSize > 0); - return reinterpret_cast(mElements[0]); - } - - T & front() - { - JPH_ASSERT(mSize > 0); - return reinterpret_cast(mElements[0]); - } - - /// Last element in the array - const T & back() const - { - JPH_ASSERT(mSize > 0); - return reinterpret_cast(mElements[mSize - 1]); - } - - T & back() - { - JPH_ASSERT(mSize > 0); - return reinterpret_cast(mElements[mSize - 1]); - } - - /// Remove one element from the array - void erase(const_iterator inIter) - { - size_type p = size_type(inIter - begin()); - JPH_ASSERT(p < mSize); - reinterpret_cast(mElements[p]).~T(); - if (p + 1 < mSize) - memmove(mElements + p, mElements + p + 1, (mSize - p - 1) * sizeof(T)); - --mSize; - } - - /// Remove multiple element from the array - void erase(const_iterator inBegin, const_iterator inEnd) - { - size_type p = size_type(inBegin - begin()); - size_type n = size_type(inEnd - inBegin); - JPH_ASSERT(inEnd <= end()); - for (size_type i = 0; i < n; ++i) - reinterpret_cast(mElements[p + i]).~T(); - if (p + n < mSize) - memmove(mElements + p, mElements + p + n, (mSize - p - n) * sizeof(T)); - mSize -= n; - } - - /// Assignment operator - StaticArray & operator = (const StaticArray &inRHS) - { - size_type rhs_size = inRHS.size(); - - if (static_cast(this) != static_cast(&inRHS)) - { - clear(); - - while (mSize < rhs_size) - { - ::new (&mElements[mSize]) T(inRHS[mSize]); - ++mSize; - } - } - - return *this; - } - - /// Assignment operator with static array of different max length - template - StaticArray & operator = (const StaticArray &inRHS) - { - size_type rhs_size = inRHS.size(); - JPH_ASSERT(rhs_size <= N); - - if (static_cast(this) != static_cast(&inRHS)) - { - clear(); - - while (mSize < rhs_size) - { - ::new (&mElements[mSize]) T(inRHS[mSize]); - ++mSize; - } - } - - return *this; - } - - /// Comparing arrays - bool operator == (const StaticArray &inRHS) const - { - if (mSize != inRHS.mSize) - return false; - for (size_type i = 0; i < mSize; ++i) - if (!(reinterpret_cast(mElements[i]) == reinterpret_cast(inRHS.mElements[i]))) - return false; - return true; - } - - bool operator != (const StaticArray &inRHS) const - { - if (mSize != inRHS.mSize) - return true; - for (size_type i = 0; i < mSize; ++i) - if (reinterpret_cast(mElements[i]) != reinterpret_cast(inRHS.mElements[i])) - return true; - return false; - } - -protected: - struct alignas(T) Storage - { - uint8 mData[sizeof(T)]; - }; - - static_assert(sizeof(T) == sizeof(Storage), "Mismatch in size"); - static_assert(alignof(T) == alignof(Storage), "Mismatch in alignment"); - - size_type mSize = 0; - Storage mElements[N]; -}; - -JPH_NAMESPACE_END - -JPH_SUPPRESS_WARNING_PUSH -JPH_CLANG_SUPPRESS_WARNING("-Wc++98-compat") - -namespace std -{ - /// Declare std::hash for StaticArray - template - struct hash> - { - size_t operator () (const JPH::StaticArray &inRHS) const - { - std::size_t ret = 0; - - // Hash length first - JPH::HashCombine(ret, inRHS.size()); - - // Then hash elements - for (const T &t : inRHS) - JPH::HashCombine(ret, t); - - return ret; - } - }; -} - -JPH_SUPPRESS_WARNING_POP diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/StreamIn.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/StreamIn.h deleted file mode 100644 index 0820d0fbf4b..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/StreamIn.h +++ /dev/null @@ -1,110 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -/// Simple binary input stream -class JPH_EXPORT StreamIn : public NonCopyable -{ -public: - /// Virtual destructor - virtual ~StreamIn() = default; - - /// Read a string of bytes from the binary stream - virtual void ReadBytes(void *outData, size_t inNumBytes) = 0; - - /// Returns true when an attempt has been made to read past the end of the file - virtual bool IsEOF() const = 0; - - /// Returns true if there was an IO failure - virtual bool IsFailed() const = 0; - - /// Read a primitive (e.g. float, int, etc.) from the binary stream - template - void Read(T &outT) - { - ReadBytes(&outT, sizeof(outT)); - } - - /// Read a vector of primitives from the binary stream - template - void Read(std::vector &outT) - { - typename Array::size_type len = outT.size(); // Initialize to previous array size, this is used for validation in the StateRecorder class - Read(len); - if (!IsEOF() && !IsFailed()) - { - outT.resize(len); - for (typename Array::size_type i = 0; i < len; ++i) - Read(outT[i]); - } - else - outT.clear(); - } - - /// Read a string from the binary stream (reads the number of characters and then the characters) - template - void Read(std::basic_string &outString) - { - typename std::basic_string::size_type len = 0; - Read(len); - if (!IsEOF() && !IsFailed()) - { - outString.resize(len); - ReadBytes(outString.data(), len * sizeof(Type)); - } - else - outString.clear(); - } - - /// Read a vector of primitives from the binary stream using a custom function to read the elements - template - void Read(std::vector &outT, const F &inReadElement) - { - typename Array::size_type len = outT.size(); // Initialize to previous array size, this is used for validation in the StateRecorder class - Read(len); - if (!IsEOF() && !IsFailed()) - { - outT.resize(len); - for (typename Array::size_type i = 0; i < len; ++i) - inReadElement(*this, outT[i]); - } - else - outT.clear(); - } - - /// Read a Vec3 (don't read W) - void Read(Vec3 &outVec) - { - ReadBytes(&outVec, 3 * sizeof(float)); - outVec = Vec3::sFixW(outVec.mValue); - } - - /// Read a DVec3 (don't read W) - void Read(DVec3 &outVec) - { - ReadBytes(&outVec, 3 * sizeof(double)); - outVec = DVec3::sFixW(outVec.mValue); - } - - /// Read a DMat44 (don't read W component of translation) - void Read(DMat44 &outVec) - { - Vec4 x, y, z; - Read(x); - Read(y); - Read(z); - - DVec3 t; - Read(t); - - outVec = DMat44(x, y, z, t); - } -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/StreamOut.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/StreamOut.h deleted file mode 100644 index ac817ff0828..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/StreamOut.h +++ /dev/null @@ -1,86 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -/// Simple binary output stream -class JPH_EXPORT StreamOut : public NonCopyable -{ -public: - /// Virtual destructor - virtual ~StreamOut() = default; - - /// Write a string of bytes to the binary stream - virtual void WriteBytes(const void *inData, size_t inNumBytes) = 0; - - /// Returns true if there was an IO failure - virtual bool IsFailed() const = 0; - - /// Write a primitive (e.g. float, int, etc.) to the binary stream - template - void Write(const T &inT) - { - WriteBytes(&inT, sizeof(inT)); - } - - /// Write a vector of primitives to the binary stream - template - void Write(const std::vector &inT) - { - typename Array::size_type len = inT.size(); - Write(len); - if (!IsFailed()) - for (typename Array::size_type i = 0; i < len; ++i) - Write(inT[i]); - } - - /// Write a string to the binary stream (writes the number of characters and then the characters) - template - void Write(const std::basic_string &inString) - { - typename std::basic_string::size_type len = inString.size(); - Write(len); - if (!IsFailed()) - WriteBytes(inString.data(), len * sizeof(Type)); - } - - /// Write a vector of primitives to the binary stream using a custom write function - template - void Write(const std::vector &inT, const F &inWriteElement) - { - typename Array::size_type len = inT.size(); - Write(len); - if (!IsFailed()) - for (typename Array::size_type i = 0; i < len; ++i) - inWriteElement(inT[i], *this); - } - - /// Write a Vec3 (don't write W) - void Write(const Vec3 &inVec) - { - WriteBytes(&inVec, 3 * sizeof(float)); - } - - /// Write a DVec3 (don't write W) - void Write(const DVec3 &inVec) - { - WriteBytes(&inVec, 3 * sizeof(double)); - } - - /// Write a DMat44 (don't write W component of translation) - void Write(const DMat44 &inVec) - { - Write(inVec.GetColumn4(0)); - Write(inVec.GetColumn4(1)); - Write(inVec.GetColumn4(2)); - - Write(inVec.GetTranslation()); - } -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/StreamUtils.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/StreamUtils.h deleted file mode 100644 index e374c521062..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/StreamUtils.h +++ /dev/null @@ -1,167 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -namespace StreamUtils { - -template -using ObjectToIDMap = UnorderedMap; - -template -using IDToObjectMap = Array>; - -// Restore a single object by reading the hash of the type, constructing it and then calling the restore function -template -Result> RestoreObject(StreamIn &inStream, void (Type::*inRestoreBinaryStateFunction)(StreamIn &)) -{ - Result> result; - - // Read the hash of the type - uint32 hash; - inStream.Read(hash); - if (inStream.IsEOF() || inStream.IsFailed()) - { - result.SetError("Failed to read type hash"); - return result; - } - - // Get the RTTI for the type - const RTTI *rtti = Factory::sInstance->Find(hash); - if (rtti == nullptr) - { - result.SetError("Failed to create instance of type"); - return result; - } - - // Construct and read the data of the type - Ref object = reinterpret_cast(rtti->CreateObject()); - (object->*inRestoreBinaryStateFunction)(inStream); - if (inStream.IsEOF() || inStream.IsFailed()) - { - result.SetError("Failed to restore object"); - return result; - } - - result.Set(object); - return result; -} - -/// Save an object reference to a stream. Uses a map to map objects to IDs which is also used to prevent writing duplicates. -template -void SaveObjectReference(StreamOut &inStream, const Type *inObject, ObjectToIDMap *ioObjectToIDMap) -{ - if (ioObjectToIDMap == nullptr || inObject == nullptr) - { - // Write null ID - inStream.Write(~uint32(0)); - } - else - { - typename ObjectToIDMap::const_iterator id = ioObjectToIDMap->find(inObject); - if (id != ioObjectToIDMap->end()) - { - // Existing object, write ID - inStream.Write(id->second); - } - else - { - // New object, write the ID - uint32 new_id = uint32(ioObjectToIDMap->size()); - (*ioObjectToIDMap)[inObject] = new_id; - inStream.Write(new_id); - - // Write the object - inObject->SaveBinaryState(inStream); - } - } -} - -/// Restore an object reference from stream. -template -Result> RestoreObjectReference(StreamIn &inStream, IDToObjectMap &ioIDToObjectMap) -{ - Result> result; - - // Read id - uint32 id = ~uint32(0); - inStream.Read(id); - - // Check null - if (id == ~uint32(0)) - { - result.Set(nullptr); - return result; - } - - // Check if it already exists - if (id >= ioIDToObjectMap.size()) - { - // New object, restore it - result = Type::sRestoreFromBinaryState(inStream); - if (result.HasError()) - return result; - JPH_ASSERT(id == ioIDToObjectMap.size()); - ioIDToObjectMap.push_back(result.Get()); - } - else - { - // Existing object filter - result.Set(ioIDToObjectMap[id].GetPtr()); - } - - return result; -} - -// Save an array of objects to a stream. -template -void SaveObjectArray(StreamOut &inStream, const ArrayType &inArray, ObjectToIDMap *ioObjectToIDMap) -{ - inStream.Write(size_t(inArray.size())); - for (const ValueType *value: inArray) - SaveObjectReference(inStream, value, ioObjectToIDMap); -} - -// Restore an array of objects from a stream. -template -Result RestoreObjectArray(StreamIn &inStream, IDToObjectMap &ioIDToObjectMap) -{ - Result result; - - size_t len; - inStream.Read(len); - if (inStream.IsEOF() || inStream.IsFailed()) - { - result.SetError("Failed to read stream"); - return result; - } - - ArrayType values; - values.reserve(len); - for (size_t i = 0; i < len; ++i) - { - Result value = RestoreObjectReference(inStream, ioIDToObjectMap); - if (value.HasError()) - { - result.SetError(value.GetError()); - return result; - } - values.push_back(std::move(value.Get())); - } - - result.Set(values); - return result; -} - -} // StreamUtils - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/StreamWrapper.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/StreamWrapper.h deleted file mode 100644 index 66a36b0571e..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/StreamWrapper.h +++ /dev/null @@ -1,53 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include - -JPH_SUPPRESS_WARNINGS_STD_BEGIN -#include -JPH_SUPPRESS_WARNINGS_STD_END - -JPH_NAMESPACE_BEGIN - -/// Wrapper around std::ostream -class StreamOutWrapper : public StreamOut -{ -public: - /// Constructor - StreamOutWrapper(ostream &ioWrapped) : mWrapped(ioWrapped) { } - - /// Write a string of bytes to the binary stream - virtual void WriteBytes(const void *inData, size_t inNumBytes) override { mWrapped.write((const char *)inData, inNumBytes); } - - /// Returns true if there was an IO failure - virtual bool IsFailed() const override { return mWrapped.fail(); } - -private: - ostream & mWrapped; -}; - -/// Wrapper around std::istream -class StreamInWrapper : public StreamIn -{ -public: - /// Constructor - StreamInWrapper(istream &ioWrapped) : mWrapped(ioWrapped) { } - - /// Write a string of bytes to the binary stream - virtual void ReadBytes(void *outData, size_t inNumBytes) override { mWrapped.read((char *)outData, inNumBytes); } - - /// Returns true when an attempt has been made to read past the end of the file - virtual bool IsEOF() const override { return mWrapped.eof(); } - - /// Returns true if there was an IO failure - virtual bool IsFailed() const override { return mWrapped.fail(); } - -private: - istream & mWrapped; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/StringTools.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/StringTools.cpp deleted file mode 100644 index 6eae982cc6b..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/StringTools.cpp +++ /dev/null @@ -1,101 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include - -JPH_SUPPRESS_WARNINGS_STD_BEGIN -#include -JPH_SUPPRESS_WARNINGS_STD_END - -JPH_NAMESPACE_BEGIN - -String StringFormat(const char *inFMT, ...) -{ - char buffer[1024]; - - // Format the string - va_list list; - va_start(list, inFMT); - vsnprintf(buffer, sizeof(buffer), inFMT, list); - va_end(list); - - return String(buffer); -} - -void StringReplace(String &ioString, const string_view &inSearch, const string_view &inReplace) -{ - size_t index = 0; - for (;;) - { - index = ioString.find(inSearch, index); - if (index == String::npos) - break; - - ioString.replace(index, inSearch.size(), inReplace); - - index += inReplace.size(); - } -} - -void StringToVector(const string_view &inString, Array &outVector, const string_view &inDelimiter, bool inClearVector) -{ - JPH_ASSERT(inDelimiter.size() > 0); - - // Ensure vector empty - if (inClearVector) - outVector.clear(); - - // No string? no elements - if (inString.empty()) - return; - - // Start with initial string - String s(inString); - - // Add to vector while we have a delimiter - size_t i; - while (!s.empty() && (i = s.find(inDelimiter)) != String::npos) - { - outVector.push_back(s.substr(0, i)); - s.erase(0, i + inDelimiter.length()); - } - - // Add final element - outVector.push_back(s); -} - -void VectorToString(const Array &inVector, String &outString, const string_view &inDelimiter) -{ - // Ensure string empty - outString.clear(); - - for (const String &s : inVector) - { - // Add delimiter if not first element - if (!outString.empty()) - outString.append(inDelimiter); - - // Add element - outString.append(s); - } -} - -String ToLower(const string_view &inString) -{ - String out; - out.reserve(inString.length()); - for (char c : inString) - out.push_back((char)tolower(c)); - return out; -} - -const char *NibbleToBinary(uint32 inNibble) -{ - static const char *nibbles[] = { "0000", "0001", "0010", "0011", "0100", "0101", "0110", "0111", "1000", "1001", "1010", "1011", "1100", "1101", "1110", "1111" }; - return nibbles[inNibble & 0xf]; -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/StringTools.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/StringTools.h deleted file mode 100644 index d3def9356b9..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/StringTools.h +++ /dev/null @@ -1,51 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -JPH_NAMESPACE_BEGIN - -/// Create a formatted text string for debugging purposes. -/// Note that this function has an internal buffer of 1024 characters, so long strings will be trimmed. -JPH_EXPORT String StringFormat(const char *inFMT, ...); - -/// Convert type to string -template -String ConvertToString(const T &inValue) -{ - using OStringStream = std::basic_ostringstream, STLAllocator>; - OStringStream oss; - oss << inValue; - return oss.str(); -} - -/// Calculate the FNV-1a hash of inString. -/// @see https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function -constexpr uint64 HashString(const char *inString) -{ - uint64 hash = 14695981039346656037UL; - for (const char *c = inString; *c != 0; ++c) - { - hash ^= *c; - hash = hash * 1099511628211UL; - } - return hash; -} - -/// Replace substring with other string -JPH_EXPORT void StringReplace(String &ioString, const string_view &inSearch, const string_view &inReplace); - -/// Convert a delimited string to an array of strings -JPH_EXPORT void StringToVector(const string_view &inString, Array &outVector, const string_view &inDelimiter = ",", bool inClearVector = true); - -/// Convert an array strings to a delimited string -JPH_EXPORT void VectorToString(const Array &inVector, String &outString, const string_view &inDelimiter = ","); - -/// Convert a string to lower case -JPH_EXPORT String ToLower(const string_view &inString); - -/// Converts the lower 4 bits of inNibble to a string that represents the number in binary format -JPH_EXPORT const char *NibbleToBinary(uint32 inNibble); - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/TempAllocator.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/TempAllocator.h deleted file mode 100644 index 5d228752b86..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/TempAllocator.h +++ /dev/null @@ -1,121 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -/// Allocator for temporary allocations. -/// This allocator works as a stack: The blocks must always be freed in the reverse order as they are allocated. -/// Note that allocations and frees can take place from different threads, but the order is guaranteed though -/// job dependencies, so it is not needed to use any form of locking. -class JPH_EXPORT TempAllocator : public NonCopyable -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Destructor - virtual ~TempAllocator() = default; - - /// Allocates inSize bytes of memory, returned memory address must be JPH_RVECTOR_ALIGNMENT byte aligned - virtual void * Allocate(uint inSize) = 0; - - /// Frees inSize bytes of memory located at inAddress - virtual void Free(void *inAddress, uint inSize) = 0; -}; - -/// Default implementation of the temp allocator that allocates a large block through malloc upfront -class JPH_EXPORT TempAllocatorImpl final : public TempAllocator -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Constructs the allocator with a maximum allocatable size of inSize - explicit TempAllocatorImpl(uint inSize) : - mBase(static_cast(AlignedAllocate(inSize, JPH_RVECTOR_ALIGNMENT))), - mSize(inSize) - { - } - - /// Destructor, frees the block - virtual ~TempAllocatorImpl() override - { - JPH_ASSERT(mTop == 0); - AlignedFree(mBase); - } - - // See: TempAllocator - virtual void * Allocate(uint inSize) override - { - if (inSize == 0) - { - return nullptr; - } - else - { - uint new_top = mTop + AlignUp(inSize, JPH_RVECTOR_ALIGNMENT); - if (new_top > mSize) - { - Trace("TempAllocator: Out of memory"); - std::abort(); - } - void *address = mBase + mTop; - mTop = new_top; - return address; - } - } - - // See: TempAllocator - virtual void Free(void *inAddress, uint inSize) override - { - if (inAddress == nullptr) - { - JPH_ASSERT(inSize == 0); - } - else - { - mTop -= AlignUp(inSize, JPH_RVECTOR_ALIGNMENT); - if (mBase + mTop != inAddress) - { - Trace("TempAllocator: Freeing in the wrong order"); - std::abort(); - } - } - } - - // Check if no allocations have been made - bool IsEmpty() const - { - return mTop == 0; - } - -private: - uint8 * mBase; ///< Base address of the memory block - uint mSize; ///< Size of the memory block - uint mTop = 0; ///< Current top of the stack -}; - -/// Implementation of the TempAllocator that just falls back to malloc/free -/// Note: This can be quite slow when running in the debugger as large memory blocks need to be initialized with 0xcd -class JPH_EXPORT TempAllocatorMalloc final : public TempAllocator -{ -public: - JPH_OVERRIDE_NEW_DELETE - - // See: TempAllocator - virtual void * Allocate(uint inSize) override - { - return AlignedAllocate(inSize, JPH_RVECTOR_ALIGNMENT); - } - - // See: TempAllocator - virtual void Free(void *inAddress, [[maybe_unused]] uint inSize) override - { - AlignedFree(inAddress); - } -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/TickCounter.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/TickCounter.cpp deleted file mode 100644 index ae0a0d00842..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/TickCounter.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include - -#if defined(JPH_PLATFORM_WINDOWS) - JPH_SUPPRESS_WARNING_PUSH - JPH_MSVC_SUPPRESS_WARNING(5039) // winbase.h(13179): warning C5039: 'TpSetCallbackCleanupGroup': pointer or reference to potentially throwing function passed to 'extern "C"' function under -EHc. Undefined behavior may occur if this function throws an exception. - #define WIN32_LEAN_AND_MEAN -#ifndef JPH_COMPILER_MINGW - #include -#else - #include -#endif - JPH_SUPPRESS_WARNING_POP -#endif - -JPH_NAMESPACE_BEGIN - -#if defined(JPH_PLATFORM_WINDOWS_UWP) || (defined(JPH_PLATFORM_WINDOWS) && defined(JPH_CPU_ARM)) - -uint64 GetProcessorTickCount() -{ - LARGE_INTEGER count; - QueryPerformanceCounter(&count); - return uint64(count.QuadPart); -} - -#endif // JPH_PLATFORM_WINDOWS_UWP || (JPH_PLATFORM_WINDOWS && JPH_CPU_ARM) - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/TickCounter.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/TickCounter.h deleted file mode 100644 index 2b5410e3d9f..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/TickCounter.h +++ /dev/null @@ -1,49 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -// Include for __rdtsc -#if defined(JPH_PLATFORM_WINDOWS) - #include -#elif defined(JPH_CPU_X86) && defined(JPH_COMPILER_GCC) - #include -#elif defined(JPH_CPU_E2K) - #include -#endif - -JPH_NAMESPACE_BEGIN - -#if defined(JPH_PLATFORM_WINDOWS_UWP) || (defined(JPH_PLATFORM_WINDOWS) && defined(JPH_CPU_ARM)) - -/// Functionality to get the processors cycle counter -uint64 GetProcessorTickCount(); // Not inline to avoid having to include Windows.h - -#else - -/// Functionality to get the processors cycle counter -JPH_INLINE uint64 GetProcessorTickCount() -{ -#if defined(JPH_PLATFORM_BLUE) - return JPH_PLATFORM_BLUE_GET_TICKS(); -#elif defined(JPH_CPU_X86) - return __rdtsc(); -#elif defined(JPH_CPU_E2K) - return __rdtsc(); -#elif defined(JPH_CPU_ARM) && defined(JPH_USE_NEON) - uint64 val; - asm volatile("mrs %0, cntvct_el0" : "=r" (val)); - return val; -#elif defined(JPH_CPU_ARM) - return 0; // Not supported -#elif defined(JPH_CPU_WASM) - return 0; // Not supported -#else - #error Undefined -#endif -} - -#endif // JPH_PLATFORM_WINDOWS_UWP || (JPH_PLATFORM_WINDOWS && JPH_CPU_ARM) - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/UnorderedMap.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/UnorderedMap.h deleted file mode 100644 index f4ae2ce9642..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/UnorderedMap.h +++ /dev/null @@ -1,15 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -JPH_SUPPRESS_WARNINGS_STD_BEGIN -#include -JPH_SUPPRESS_WARNINGS_STD_END - -JPH_NAMESPACE_BEGIN - -template , class KeyEqual = std::equal_to> using UnorderedMap = std::unordered_map>>; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/UnorderedSet.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/UnorderedSet.h deleted file mode 100644 index bcb31a005e2..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Core/UnorderedSet.h +++ /dev/null @@ -1,15 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -JPH_SUPPRESS_WARNINGS_STD_BEGIN -#include -JPH_SUPPRESS_WARNINGS_STD_END - -JPH_NAMESPACE_BEGIN - -template , class KeyEqual = std::equal_to> using UnorderedSet = std::unordered_set>; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/AABox.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/AABox.h deleted file mode 100644 index f4f02668dc0..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/AABox.h +++ /dev/null @@ -1,304 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -/// Axis aligned box -class [[nodiscard]] AABox -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Constructor - AABox() : mMin(Vec3::sReplicate(FLT_MAX)), mMax(Vec3::sReplicate(-FLT_MAX)) { } - AABox(Vec3Arg inMin, Vec3Arg inMax) : mMin(inMin), mMax(inMax) { } - AABox(DVec3Arg inMin, DVec3Arg inMax) : mMin(inMin.ToVec3RoundDown()), mMax(inMax.ToVec3RoundUp()) { } - AABox(Vec3Arg inCenter, float inRadius) : mMin(inCenter - Vec3::sReplicate(inRadius)), mMax(inCenter + Vec3::sReplicate(inRadius)) { } - - /// Create box from 2 points - static AABox sFromTwoPoints(Vec3Arg inP1, Vec3Arg inP2) { return AABox(Vec3::sMin(inP1, inP2), Vec3::sMax(inP1, inP2)); } - - /// Get bounding box of size 2 * FLT_MAX - static AABox sBiggest() - { - return AABox(Vec3::sReplicate(-FLT_MAX), Vec3::sReplicate(FLT_MAX)); - } - - /// Comparison operators - bool operator == (const AABox &inRHS) const { return mMin == inRHS.mMin && mMax == inRHS.mMax; } - bool operator != (const AABox &inRHS) const { return mMin != inRHS.mMin || mMax != inRHS.mMax; } - - /// Reset the bounding box to an empty bounding box - void SetEmpty() - { - mMin = Vec3::sReplicate(FLT_MAX); - mMax = Vec3::sReplicate(-FLT_MAX); - } - - /// Check if the bounding box is valid (max >= min) - bool IsValid() const - { - return mMin.GetX() <= mMax.GetX() && mMin.GetY() <= mMax.GetY() && mMin.GetZ() <= mMax.GetZ(); - } - - /// Encapsulate point in bounding box - void Encapsulate(Vec3Arg inPos) - { - mMin = Vec3::sMin(mMin, inPos); - mMax = Vec3::sMax(mMax, inPos); - } - - /// Encapsulate bounding box in bounding box - void Encapsulate(const AABox &inRHS) - { - mMin = Vec3::sMin(mMin, inRHS.mMin); - mMax = Vec3::sMax(mMax, inRHS.mMax); - } - - /// Encapsulate triangle in bounding box - void Encapsulate(const Triangle &inRHS) - { - Vec3 v = Vec3::sLoadFloat3Unsafe(inRHS.mV[0]); - Encapsulate(v); - v = Vec3::sLoadFloat3Unsafe(inRHS.mV[1]); - Encapsulate(v); - v = Vec3::sLoadFloat3Unsafe(inRHS.mV[2]); - Encapsulate(v); - } - - /// Encapsulate triangle in bounding box - void Encapsulate(const VertexList &inVertices, const IndexedTriangle &inTriangle) - { - for (uint32 idx : inTriangle.mIdx) - Encapsulate(Vec3(inVertices[idx])); - } - - /// Intersect this bounding box with inOther, returns the intersection - AABox Intersect(const AABox &inOther) const - { - return AABox(Vec3::sMax(mMin, inOther.mMin), Vec3::sMin(mMax, inOther.mMax)); - } - - /// Make sure that each edge of the bounding box has a minimal length - void EnsureMinimalEdgeLength(float inMinEdgeLength) - { - Vec3 min_length = Vec3::sReplicate(inMinEdgeLength); - mMax = Vec3::sSelect(mMax, mMin + min_length, Vec3::sLess(mMax - mMin, min_length)); - } - - /// Widen the box on both sides by inVector - void ExpandBy(Vec3Arg inVector) - { - mMin -= inVector; - mMax += inVector; - } - - /// Get center of bounding box - Vec3 GetCenter() const - { - return 0.5f * (mMin + mMax); - } - - /// Get extent of bounding box (half of the size) - Vec3 GetExtent() const - { - return 0.5f * (mMax - mMin); - } - - /// Get size of bounding box - Vec3 GetSize() const - { - return mMax - mMin; - } - - /// Get surface area of bounding box - float GetSurfaceArea() const - { - Vec3 extent = mMax - mMin; - return 2.0f * (extent.GetX() * extent.GetY() + extent.GetX() * extent.GetZ() + extent.GetY() * extent.GetZ()); - } - - /// Get volume of bounding box - float GetVolume() const - { - Vec3 extent = mMax - mMin; - return extent.GetX() * extent.GetY() * extent.GetZ(); - } - - /// Check if this box contains another box - bool Contains(const AABox &inOther) const - { - return UVec4::sAnd(Vec3::sLessOrEqual(mMin, inOther.mMin), Vec3::sGreaterOrEqual(mMax, inOther.mMax)).TestAllXYZTrue(); - } - - /// Check if this box contains a point - bool Contains(Vec3Arg inOther) const - { - return UVec4::sAnd(Vec3::sLessOrEqual(mMin, inOther), Vec3::sGreaterOrEqual(mMax, inOther)).TestAllXYZTrue(); - } - - /// Check if this box contains a point - bool Contains(DVec3Arg inOther) const - { - return Contains(Vec3(inOther)); - } - - /// Check if this box overlaps with another box - bool Overlaps(const AABox &inOther) const - { - return !UVec4::sOr(Vec3::sGreater(mMin, inOther.mMax), Vec3::sLess(mMax, inOther.mMin)).TestAnyXYZTrue(); - } - - /// Check if this box overlaps with a plane - bool Overlaps(const Plane &inPlane) const - { - Vec3 normal = inPlane.GetNormal(); - float dist_normal = inPlane.SignedDistance(GetSupport(normal)); - float dist_min_normal = inPlane.SignedDistance(GetSupport(-normal)); - return dist_normal * dist_min_normal <= 0.0f; // If both support points are on the same side of the plane we don't overlap - } - - /// Translate bounding box - void Translate(Vec3Arg inTranslation) - { - mMin += inTranslation; - mMax += inTranslation; - } - - /// Translate bounding box - void Translate(DVec3Arg inTranslation) - { - mMin = (DVec3(mMin) + inTranslation).ToVec3RoundDown(); - mMax = (DVec3(mMax) + inTranslation).ToVec3RoundUp(); - } - - /// Transform bounding box - AABox Transformed(Mat44Arg inMatrix) const - { - // Start with the translation of the matrix - Vec3 new_min, new_max; - new_min = new_max = inMatrix.GetTranslation(); - - // Now find the extreme points by considering the product of the min and max with each column of inMatrix - for (int c = 0; c < 3; ++c) - { - Vec3 col = inMatrix.GetColumn3(c); - - Vec3 a = col * mMin[c]; - Vec3 b = col * mMax[c]; - - new_min += Vec3::sMin(a, b); - new_max += Vec3::sMax(a, b); - } - - // Return the new bounding box - return AABox(new_min, new_max); - } - - /// Transform bounding box - AABox Transformed(DMat44Arg inMatrix) const - { - AABox transformed = Transformed(inMatrix.GetRotation()); - transformed.Translate(inMatrix.GetTranslation()); - return transformed; - } - - /// Scale this bounding box, can handle non-uniform and negative scaling - AABox Scaled(Vec3Arg inScale) const - { - return AABox::sFromTwoPoints(mMin * inScale, mMax * inScale); - } - - /// Calculate the support vector for this convex shape. - Vec3 GetSupport(Vec3Arg inDirection) const - { - return Vec3::sSelect(mMax, mMin, Vec3::sLess(inDirection, Vec3::sZero())); - } - - /// Get the vertices of the face that faces inDirection the most - template - void GetSupportingFace(Vec3Arg inDirection, VERTEX_ARRAY &outVertices) const - { - outVertices.resize(4); - - int axis = inDirection.Abs().GetHighestComponentIndex(); - if (inDirection[axis] < 0.0f) - { - switch (axis) - { - case 0: - outVertices[0] = Vec3(mMax.GetX(), mMin.GetY(), mMin.GetZ()); - outVertices[1] = Vec3(mMax.GetX(), mMax.GetY(), mMin.GetZ()); - outVertices[2] = Vec3(mMax.GetX(), mMax.GetY(), mMax.GetZ()); - outVertices[3] = Vec3(mMax.GetX(), mMin.GetY(), mMax.GetZ()); - break; - - case 1: - outVertices[0] = Vec3(mMin.GetX(), mMax.GetY(), mMin.GetZ()); - outVertices[1] = Vec3(mMin.GetX(), mMax.GetY(), mMax.GetZ()); - outVertices[2] = Vec3(mMax.GetX(), mMax.GetY(), mMax.GetZ()); - outVertices[3] = Vec3(mMax.GetX(), mMax.GetY(), mMin.GetZ()); - break; - - case 2: - outVertices[0] = Vec3(mMin.GetX(), mMin.GetY(), mMax.GetZ()); - outVertices[1] = Vec3(mMax.GetX(), mMin.GetY(), mMax.GetZ()); - outVertices[2] = Vec3(mMax.GetX(), mMax.GetY(), mMax.GetZ()); - outVertices[3] = Vec3(mMin.GetX(), mMax.GetY(), mMax.GetZ()); - break; - } - } - else - { - switch (axis) - { - case 0: - outVertices[0] = Vec3(mMin.GetX(), mMin.GetY(), mMin.GetZ()); - outVertices[1] = Vec3(mMin.GetX(), mMin.GetY(), mMax.GetZ()); - outVertices[2] = Vec3(mMin.GetX(), mMax.GetY(), mMax.GetZ()); - outVertices[3] = Vec3(mMin.GetX(), mMax.GetY(), mMin.GetZ()); - break; - - case 1: - outVertices[0] = Vec3(mMin.GetX(), mMin.GetY(), mMin.GetZ()); - outVertices[1] = Vec3(mMax.GetX(), mMin.GetY(), mMin.GetZ()); - outVertices[2] = Vec3(mMax.GetX(), mMin.GetY(), mMax.GetZ()); - outVertices[3] = Vec3(mMin.GetX(), mMin.GetY(), mMax.GetZ()); - break; - - case 2: - outVertices[0] = Vec3(mMin.GetX(), mMin.GetY(), mMin.GetZ()); - outVertices[1] = Vec3(mMin.GetX(), mMax.GetY(), mMin.GetZ()); - outVertices[2] = Vec3(mMax.GetX(), mMax.GetY(), mMin.GetZ()); - outVertices[3] = Vec3(mMax.GetX(), mMin.GetY(), mMin.GetZ()); - break; - } - } - } - - /// Get the closest point on or in this box to inPoint - Vec3 GetClosestPoint(Vec3Arg inPoint) const - { - return Vec3::sMin(Vec3::sMax(inPoint, mMin), mMax); - } - - /// Get the squared distance between inPoint and this box (will be 0 if in Point is inside the box) - inline float GetSqDistanceTo(Vec3Arg inPoint) const - { - return (GetClosestPoint(inPoint) - inPoint).LengthSq(); - } - - /// Bounding box min and max - Vec3 mMin; - Vec3 mMax; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/AABox4.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/AABox4.h deleted file mode 100644 index 4465d4dabe5..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/AABox4.h +++ /dev/null @@ -1,224 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -/// Helper functions that process 4 axis aligned boxes at the same time using SIMD -/// Test if 4 bounding boxes overlap with 1 bounding box, splat 1 box -JPH_INLINE UVec4 AABox4VsBox(const AABox &inBox1, Vec4Arg inBox2MinX, Vec4Arg inBox2MinY, Vec4Arg inBox2MinZ, Vec4Arg inBox2MaxX, Vec4Arg inBox2MaxY, Vec4Arg inBox2MaxZ) -{ - // Splat values of box 1 - Vec4 box1_minx = inBox1.mMin.SplatX(); - Vec4 box1_miny = inBox1.mMin.SplatY(); - Vec4 box1_minz = inBox1.mMin.SplatZ(); - Vec4 box1_maxx = inBox1.mMax.SplatX(); - Vec4 box1_maxy = inBox1.mMax.SplatY(); - Vec4 box1_maxz = inBox1.mMax.SplatZ(); - - // Test separation over each axis - UVec4 nooverlapx = UVec4::sOr(Vec4::sGreater(box1_minx, inBox2MaxX), Vec4::sGreater(inBox2MinX, box1_maxx)); - UVec4 nooverlapy = UVec4::sOr(Vec4::sGreater(box1_miny, inBox2MaxY), Vec4::sGreater(inBox2MinY, box1_maxy)); - UVec4 nooverlapz = UVec4::sOr(Vec4::sGreater(box1_minz, inBox2MaxZ), Vec4::sGreater(inBox2MinZ, box1_maxz)); - - // Return overlap - return UVec4::sNot(UVec4::sOr(UVec4::sOr(nooverlapx, nooverlapy), nooverlapz)); -} - -/// Scale 4 axis aligned boxes -JPH_INLINE void AABox4Scale(Vec3Arg inScale, Vec4Arg inBoxMinX, Vec4Arg inBoxMinY, Vec4Arg inBoxMinZ, Vec4Arg inBoxMaxX, Vec4Arg inBoxMaxY, Vec4Arg inBoxMaxZ, Vec4 &outBoundsMinX, Vec4 &outBoundsMinY, Vec4 &outBoundsMinZ, Vec4 &outBoundsMaxX, Vec4 &outBoundsMaxY, Vec4 &outBoundsMaxZ) -{ - Vec4 scale_x = inScale.SplatX(); - Vec4 scaled_min_x = scale_x * inBoxMinX; - Vec4 scaled_max_x = scale_x * inBoxMaxX; - outBoundsMinX = Vec4::sMin(scaled_min_x, scaled_max_x); // Negative scale can flip min and max - outBoundsMaxX = Vec4::sMax(scaled_min_x, scaled_max_x); - - Vec4 scale_y = inScale.SplatY(); - Vec4 scaled_min_y = scale_y * inBoxMinY; - Vec4 scaled_max_y = scale_y * inBoxMaxY; - outBoundsMinY = Vec4::sMin(scaled_min_y, scaled_max_y); - outBoundsMaxY = Vec4::sMax(scaled_min_y, scaled_max_y); - - Vec4 scale_z = inScale.SplatZ(); - Vec4 scaled_min_z = scale_z * inBoxMinZ; - Vec4 scaled_max_z = scale_z * inBoxMaxZ; - outBoundsMinZ = Vec4::sMin(scaled_min_z, scaled_max_z); - outBoundsMaxZ = Vec4::sMax(scaled_min_z, scaled_max_z); -} - -/// Enlarge 4 bounding boxes with extent (add to both sides) -JPH_INLINE void AABox4EnlargeWithExtent(Vec3Arg inExtent, Vec4 &ioBoundsMinX, Vec4 &ioBoundsMinY, Vec4 &ioBoundsMinZ, Vec4 &ioBoundsMaxX, Vec4 &ioBoundsMaxY, Vec4 &ioBoundsMaxZ) -{ - Vec4 extent_x = inExtent.SplatX(); - ioBoundsMinX -= extent_x; - ioBoundsMaxX += extent_x; - - Vec4 extent_y = inExtent.SplatY(); - ioBoundsMinY -= extent_y; - ioBoundsMaxY += extent_y; - - Vec4 extent_z = inExtent.SplatZ(); - ioBoundsMinZ -= extent_z; - ioBoundsMaxZ += extent_z; -} - -/// Test if 4 bounding boxes overlap with a point -JPH_INLINE UVec4 AABox4VsPoint(Vec3Arg inPoint, Vec4Arg inBoxMinX, Vec4Arg inBoxMinY, Vec4Arg inBoxMinZ, Vec4Arg inBoxMaxX, Vec4Arg inBoxMaxY, Vec4Arg inBoxMaxZ) -{ - // Splat point to 4 component vectors - Vec4 point_x = Vec4(inPoint).SplatX(); - Vec4 point_y = Vec4(inPoint).SplatY(); - Vec4 point_z = Vec4(inPoint).SplatZ(); - - // Test if point overlaps with box - UVec4 overlapx = UVec4::sAnd(Vec4::sGreaterOrEqual(point_x, inBoxMinX), Vec4::sLessOrEqual(point_x, inBoxMaxX)); - UVec4 overlapy = UVec4::sAnd(Vec4::sGreaterOrEqual(point_y, inBoxMinY), Vec4::sLessOrEqual(point_y, inBoxMaxY)); - UVec4 overlapz = UVec4::sAnd(Vec4::sGreaterOrEqual(point_z, inBoxMinZ), Vec4::sLessOrEqual(point_z, inBoxMaxZ)); - - // Test if all are overlapping - return UVec4::sAnd(UVec4::sAnd(overlapx, overlapy), overlapz); -} - -/// Test if 4 bounding boxes overlap with an oriented box -JPH_INLINE UVec4 AABox4VsBox(Mat44Arg inOrientation, Vec3Arg inHalfExtents, Vec4Arg inBoxMinX, Vec4Arg inBoxMinY, Vec4Arg inBoxMinZ, Vec4Arg inBoxMaxX, Vec4Arg inBoxMaxY, Vec4Arg inBoxMaxZ, float inEpsilon = 1.0e-6f) -{ - // Taken from: Real Time Collision Detection - Christer Ericson - // Chapter 4.4.1, page 103-105. - // Note that the code is swapped around: A is the aabox and B is the oriented box (this saves us from having to invert the orientation of the oriented box) - - // Compute translation vector t (the translation of B in the space of A) - Vec4 t[3] { - inOrientation.GetTranslation().SplatX() - 0.5f * (inBoxMinX + inBoxMaxX), - inOrientation.GetTranslation().SplatY() - 0.5f * (inBoxMinY + inBoxMaxY), - inOrientation.GetTranslation().SplatZ() - 0.5f * (inBoxMinZ + inBoxMaxZ) }; - - // Compute common subexpressions. Add in an epsilon term to - // counteract arithmetic errors when two edges are parallel and - // their cross product is (near) null (see text for details) - Vec3 epsilon = Vec3::sReplicate(inEpsilon); - Vec3 abs_r[3] { inOrientation.GetAxisX().Abs() + epsilon, inOrientation.GetAxisY().Abs() + epsilon, inOrientation.GetAxisZ().Abs() + epsilon }; - - // Half extents for a - Vec4 a_half_extents[3] { - 0.5f * (inBoxMaxX - inBoxMinX), - 0.5f * (inBoxMaxY - inBoxMinY), - 0.5f * (inBoxMaxZ - inBoxMinZ) }; - - // Half extents of b - Vec4 b_half_extents_x = inHalfExtents.SplatX(); - Vec4 b_half_extents_y = inHalfExtents.SplatY(); - Vec4 b_half_extents_z = inHalfExtents.SplatZ(); - - // Each component corresponds to 1 overlapping OBB vs ABB - UVec4 overlaps = UVec4(0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff); - - // Test axes L = A0, L = A1, L = A2 - Vec4 ra, rb; - for (int i = 0; i < 3; i++) - { - ra = a_half_extents[i]; - rb = b_half_extents_x * abs_r[0][i] + b_half_extents_y * abs_r[1][i] + b_half_extents_z * abs_r[2][i]; - overlaps = UVec4::sAnd(overlaps, Vec4::sLessOrEqual(t[i].Abs(), ra + rb)); - } - - // Test axes L = B0, L = B1, L = B2 - for (int i = 0; i < 3; i++) - { - ra = a_half_extents[0] * abs_r[i][0] + a_half_extents[1] * abs_r[i][1] + a_half_extents[2] * abs_r[i][2]; - rb = Vec4::sReplicate(inHalfExtents[i]); - overlaps = UVec4::sAnd(overlaps, Vec4::sLessOrEqual((t[0] * inOrientation(0, i) + t[1] * inOrientation(1, i) + t[2] * inOrientation(2, i)).Abs(), ra + rb)); - } - - // Test axis L = A0 x B0 - ra = a_half_extents[1] * abs_r[0][2] + a_half_extents[2] * abs_r[0][1]; - rb = b_half_extents_y * abs_r[2][0] + b_half_extents_z * abs_r[1][0]; - overlaps = UVec4::sAnd(overlaps, Vec4::sLessOrEqual((t[2] * inOrientation(1, 0) - t[1] * inOrientation(2, 0)).Abs(), ra + rb)); - - // Test axis L = A0 x B1 - ra = a_half_extents[1] * abs_r[1][2] + a_half_extents[2] * abs_r[1][1]; - rb = b_half_extents_x * abs_r[2][0] + b_half_extents_z * abs_r[0][0]; - overlaps = UVec4::sAnd(overlaps, Vec4::sLessOrEqual((t[2] * inOrientation(1, 1) - t[1] * inOrientation(2, 1)).Abs(), ra + rb)); - - // Test axis L = A0 x B2 - ra = a_half_extents[1] * abs_r[2][2] + a_half_extents[2] * abs_r[2][1]; - rb = b_half_extents_x * abs_r[1][0] + b_half_extents_y * abs_r[0][0]; - overlaps = UVec4::sAnd(overlaps, Vec4::sLessOrEqual((t[2] * inOrientation(1, 2) - t[1] * inOrientation(2, 2)).Abs(), ra + rb)); - - // Test axis L = A1 x B0 - ra = a_half_extents[0] * abs_r[0][2] + a_half_extents[2] * abs_r[0][0]; - rb = b_half_extents_y * abs_r[2][1] + b_half_extents_z * abs_r[1][1]; - overlaps = UVec4::sAnd(overlaps, Vec4::sLessOrEqual((t[0] * inOrientation(2, 0) - t[2] * inOrientation(0, 0)).Abs(), ra + rb)); - - // Test axis L = A1 x B1 - ra = a_half_extents[0] * abs_r[1][2] + a_half_extents[2] * abs_r[1][0]; - rb = b_half_extents_x * abs_r[2][1] + b_half_extents_z * abs_r[0][1]; - overlaps = UVec4::sAnd(overlaps, Vec4::sLessOrEqual((t[0] * inOrientation(2, 1) - t[2] * inOrientation(0, 1)).Abs(), ra + rb)); - - // Test axis L = A1 x B2 - ra = a_half_extents[0] * abs_r[2][2] + a_half_extents[2] * abs_r[2][0]; - rb = b_half_extents_x * abs_r[1][1] + b_half_extents_y * abs_r[0][1]; - overlaps = UVec4::sAnd(overlaps, Vec4::sLessOrEqual((t[0] * inOrientation(2, 2) - t[2] * inOrientation(0, 2)).Abs(), ra + rb)); - - // Test axis L = A2 x B0 - ra = a_half_extents[0] * abs_r[0][1] + a_half_extents[1] * abs_r[0][0]; - rb = b_half_extents_y * abs_r[2][2] + b_half_extents_z * abs_r[1][2]; - overlaps = UVec4::sAnd(overlaps, Vec4::sLessOrEqual((t[1] * inOrientation(0, 0) - t[0] * inOrientation(1, 0)).Abs(), ra + rb)); - - // Test axis L = A2 x B1 - ra = a_half_extents[0] * abs_r[1][1] + a_half_extents[1] * abs_r[1][0]; - rb = b_half_extents_x * abs_r[2][2] + b_half_extents_z * abs_r[0][2]; - overlaps = UVec4::sAnd(overlaps, Vec4::sLessOrEqual((t[1] * inOrientation(0, 1) - t[0] * inOrientation(1, 1)).Abs(), ra + rb)); - - // Test axis L = A2 x B2 - ra = a_half_extents[0] * abs_r[2][1] + a_half_extents[1] * abs_r[2][0]; - rb = b_half_extents_x * abs_r[1][2] + b_half_extents_y * abs_r[0][2]; - overlaps = UVec4::sAnd(overlaps, Vec4::sLessOrEqual((t[1] * inOrientation(0, 2) - t[0] * inOrientation(1, 2)).Abs(), ra + rb)); - - // Return if the OBB vs AABBs are intersecting - return overlaps; -} - -/// Convenience function that tests 4 AABoxes vs OrientedBox -JPH_INLINE UVec4 AABox4VsBox(const OrientedBox &inBox, Vec4Arg inBoxMinX, Vec4Arg inBoxMinY, Vec4Arg inBoxMinZ, Vec4Arg inBoxMaxX, Vec4Arg inBoxMaxY, Vec4Arg inBoxMaxZ, float inEpsilon = 1.0e-6f) -{ - return AABox4VsBox(inBox.mOrientation, inBox.mHalfExtents, inBoxMinX, inBoxMinY, inBoxMinZ, inBoxMaxX, inBoxMaxY, inBoxMaxZ, inEpsilon); -} - -/// Get the squared distance between 4 AABoxes and a point -JPH_INLINE Vec4 AABox4DistanceSqToPoint(Vec4Arg inPointX, Vec4Arg inPointY, Vec4Arg inPointZ, Vec4Arg inBoxMinX, Vec4Arg inBoxMinY, Vec4Arg inBoxMinZ, Vec4Arg inBoxMaxX, Vec4Arg inBoxMaxY, Vec4Arg inBoxMaxZ) -{ - // Get closest point on box - Vec4 closest_x = Vec4::sMin(Vec4::sMax(inPointX, inBoxMinX), inBoxMaxX); - Vec4 closest_y = Vec4::sMin(Vec4::sMax(inPointY, inBoxMinY), inBoxMaxY); - Vec4 closest_z = Vec4::sMin(Vec4::sMax(inPointZ, inBoxMinZ), inBoxMaxZ); - - // Return the squared distance between the box and point - return Square(closest_x - inPointX) + Square(closest_y - inPointY) + Square(closest_z - inPointZ); -} - -/// Get the squared distance between 4 AABoxes and a point -JPH_INLINE Vec4 AABox4DistanceSqToPoint(Vec3 inPoint, Vec4Arg inBoxMinX, Vec4Arg inBoxMinY, Vec4Arg inBoxMinZ, Vec4Arg inBoxMaxX, Vec4Arg inBoxMaxY, Vec4Arg inBoxMaxZ) -{ - return AABox4DistanceSqToPoint(inPoint.SplatX(), inPoint.SplatY(), inPoint.SplatZ(), inBoxMinX, inBoxMinY, inBoxMinZ, inBoxMaxX, inBoxMaxY, inBoxMaxZ); -} - -/// Test 4 AABoxes vs a sphere -JPH_INLINE UVec4 AABox4VsSphere(Vec4Arg inCenterX, Vec4Arg inCenterY, Vec4Arg inCenterZ, Vec4Arg inRadiusSq, Vec4Arg inBoxMinX, Vec4Arg inBoxMinY, Vec4Arg inBoxMinZ, Vec4Arg inBoxMaxX, Vec4Arg inBoxMaxY, Vec4Arg inBoxMaxZ) -{ - // Test the distance from the center of the sphere to the box is smaller than the radius - Vec4 distance_sq = AABox4DistanceSqToPoint(inCenterX, inCenterY, inCenterZ, inBoxMinX, inBoxMinY, inBoxMinZ, inBoxMaxX, inBoxMaxY, inBoxMaxZ); - return Vec4::sLessOrEqual(distance_sq, inRadiusSq); -} - -/// Test 4 AABoxes vs a sphere -JPH_INLINE UVec4 AABox4VsSphere(Vec3Arg inCenter, float inRadiusSq, Vec4Arg inBoxMinX, Vec4Arg inBoxMinY, Vec4Arg inBoxMinZ, Vec4Arg inBoxMaxX, Vec4Arg inBoxMaxY, Vec4Arg inBoxMaxZ) -{ - return AABox4VsSphere(inCenter.SplatX(), inCenter.SplatY(), inCenter.SplatZ(), Vec4::sReplicate(inRadiusSq), inBoxMinX, inBoxMinY, inBoxMinZ, inBoxMaxX, inBoxMaxY, inBoxMaxZ); -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/ClipPoly.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/ClipPoly.h deleted file mode 100644 index e0ef4f22b3b..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/ClipPoly.h +++ /dev/null @@ -1,200 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -/// Clip inPolygonToClip against the positive halfspace of plane defined by inPlaneOrigin and inPlaneNormal. -/// inPlaneNormal does not need to be normalized. -template -void ClipPolyVsPlane(const VERTEX_ARRAY &inPolygonToClip, Vec3Arg inPlaneOrigin, Vec3Arg inPlaneNormal, VERTEX_ARRAY &outClippedPolygon) -{ - JPH_ASSERT(inPolygonToClip.size() >= 2); - JPH_ASSERT(outClippedPolygon.empty()); - - // Determine state of last point - Vec3 e1 = inPolygonToClip[inPolygonToClip.size() - 1]; - float prev_num = (inPlaneOrigin - e1).Dot(inPlaneNormal); - bool prev_inside = prev_num < 0.0f; - - // Loop through all vertices - for (typename VERTEX_ARRAY::size_type j = 0; j < inPolygonToClip.size(); ++j) - { - // Check if second point is inside - Vec3Arg e2 = inPolygonToClip[j]; - float num = (inPlaneOrigin - e2).Dot(inPlaneNormal); - bool cur_inside = num < 0.0f; - - // In -> Out or Out -> In: Add point on clipping plane - if (cur_inside != prev_inside) - { - // Solve: (X - inPlaneOrigin) . inPlaneNormal = 0 and X = e1 + t * (e2 - e1) for X - Vec3 e12 = e2 - e1; - float denom = e12.Dot(inPlaneNormal); - if (denom != 0.0f) - outClippedPolygon.push_back(e1 + (prev_num / denom) * e12); - else - cur_inside = prev_inside; // Edge is parallel to plane, treat point as if it were on the same side as the last point - } - - // Point inside, add it - if (cur_inside) - outClippedPolygon.push_back(e2); - - // Update previous state - prev_num = num; - prev_inside = cur_inside; - e1 = e2; - } -} - -/// Clip polygon versus polygon. -/// Both polygons are assumed to be in counter clockwise order. -/// @param inClippingPolygonNormal is used to create planes of all edges in inClippingPolygon against which inPolygonToClip is clipped, inClippingPolygonNormal does not need to be normalized -/// @param inClippingPolygon is the polygon which inClippedPolygon is clipped against -/// @param inPolygonToClip is the polygon that is clipped -/// @param outClippedPolygon will contain clipped polygon when function returns -template -void ClipPolyVsPoly(const VERTEX_ARRAY &inPolygonToClip, const VERTEX_ARRAY &inClippingPolygon, Vec3Arg inClippingPolygonNormal, VERTEX_ARRAY &outClippedPolygon) -{ - JPH_ASSERT(inPolygonToClip.size() >= 2); - JPH_ASSERT(inClippingPolygon.size() >= 3); - - VERTEX_ARRAY tmp_vertices[2]; - int tmp_vertices_idx = 0; - - for (typename VERTEX_ARRAY::size_type i = 0; i < inClippingPolygon.size(); ++i) - { - // Get edge to clip against - Vec3 clip_e1 = inClippingPolygon[i]; - Vec3 clip_e2 = inClippingPolygon[(i + 1) % inClippingPolygon.size()]; - Vec3 clip_normal = inClippingPolygonNormal.Cross(clip_e2 - clip_e1); // Pointing inward to the clipping polygon - - // Get source and target polygon - const VERTEX_ARRAY &src_polygon = (i == 0)? inPolygonToClip : tmp_vertices[tmp_vertices_idx]; - tmp_vertices_idx ^= 1; - VERTEX_ARRAY &tgt_polygon = (i == inClippingPolygon.size() - 1)? outClippedPolygon : tmp_vertices[tmp_vertices_idx]; - tgt_polygon.clear(); - - // Clip against the edge - ClipPolyVsPlane(src_polygon, clip_e1, clip_normal, tgt_polygon); - - // Break out if no polygon left - if (tgt_polygon.size() < 3) - { - outClippedPolygon.clear(); - break; - } - } -} - -/// Clip inPolygonToClip against an edge, the edge is projected on inPolygonToClip using inClippingEdgeNormal. -/// The positive half space (the side on the edge in the direction of inClippingEdgeNormal) is cut away. -template -void ClipPolyVsEdge(const VERTEX_ARRAY &inPolygonToClip, Vec3Arg inEdgeVertex1, Vec3Arg inEdgeVertex2, Vec3Arg inClippingEdgeNormal, VERTEX_ARRAY &outClippedPolygon) -{ - JPH_ASSERT(inPolygonToClip.size() >= 3); - JPH_ASSERT(outClippedPolygon.empty()); - - // Get normal that is perpendicular to the edge and the clipping edge normal - Vec3 edge = inEdgeVertex2 - inEdgeVertex1; - Vec3 edge_normal = inClippingEdgeNormal.Cross(edge); - - // Project vertices of edge on inPolygonToClip - Vec3 polygon_normal = (inPolygonToClip[2] - inPolygonToClip[0]).Cross(inPolygonToClip[1] - inPolygonToClip[0]); - float polygon_normal_len_sq = polygon_normal.LengthSq(); - Vec3 v1 = inEdgeVertex1 + polygon_normal.Dot(inPolygonToClip[0] - inEdgeVertex1) * polygon_normal / polygon_normal_len_sq; - Vec3 v2 = inEdgeVertex2 + polygon_normal.Dot(inPolygonToClip[0] - inEdgeVertex2) * polygon_normal / polygon_normal_len_sq; - Vec3 v12 = v2 - v1; - float v12_len_sq = v12.LengthSq(); - - // Determine state of last point - Vec3 e1 = inPolygonToClip[inPolygonToClip.size() - 1]; - float prev_num = (inEdgeVertex1 - e1).Dot(edge_normal); - bool prev_inside = prev_num < 0.0f; - - // Loop through all vertices - for (typename VERTEX_ARRAY::size_type j = 0; j < inPolygonToClip.size(); ++j) - { - // Check if second point is inside - Vec3 e2 = inPolygonToClip[j]; - float num = (inEdgeVertex1 - e2).Dot(edge_normal); - bool cur_inside = num < 0.0f; - - // In -> Out or Out -> In: Add point on clipping plane - if (cur_inside != prev_inside) - { - // Solve: (X - inPlaneOrigin) . inPlaneNormal = 0 and X = e1 + t * (e2 - e1) for X - Vec3 e12 = e2 - e1; - float denom = e12.Dot(edge_normal); - Vec3 clipped_point = e1 + (prev_num / denom) * e12; - - // Project point on line segment v1, v2 so see if it falls outside if the edge - float projection = (clipped_point - v1).Dot(v12); - if (projection < 0.0f) - outClippedPolygon.push_back(v1); - else if (projection > v12_len_sq) - outClippedPolygon.push_back(v2); - else - outClippedPolygon.push_back(clipped_point); - } - - // Update previous state - prev_num = num; - prev_inside = cur_inside; - e1 = e2; - } -} - -/// Clip polygon vs axis aligned box, inPolygonToClip is assume to be in counter clockwise order. -/// Output will be stored in outClippedPolygon. Everything inside inAABox will be kept. -template -void ClipPolyVsAABox(const VERTEX_ARRAY &inPolygonToClip, const AABox &inAABox, VERTEX_ARRAY &outClippedPolygon) -{ - JPH_ASSERT(inPolygonToClip.size() >= 2); - - VERTEX_ARRAY tmp_vertices[2]; - int tmp_vertices_idx = 0; - - for (int coord = 0; coord < 3; ++coord) - for (int side = 0; side < 2; ++side) - { - // Get plane to clip against - Vec3 origin = Vec3::sZero(), normal = Vec3::sZero(); - if (side == 0) - { - normal.SetComponent(coord, 1.0f); - origin.SetComponent(coord, inAABox.mMin[coord]); - } - else - { - normal.SetComponent(coord, -1.0f); - origin.SetComponent(coord, inAABox.mMax[coord]); - } - - // Get source and target polygon - const VERTEX_ARRAY &src_polygon = tmp_vertices_idx == 0? inPolygonToClip : tmp_vertices[tmp_vertices_idx & 1]; - tmp_vertices_idx++; - VERTEX_ARRAY &tgt_polygon = tmp_vertices_idx == 6? outClippedPolygon : tmp_vertices[tmp_vertices_idx & 1]; - tgt_polygon.clear(); - - // Clip against the edge - ClipPolyVsPlane(src_polygon, origin, normal, tgt_polygon); - - // Break out if no polygon left - if (tgt_polygon.size() < 3) - { - outClippedPolygon.clear(); - return; - } - - // Flip normal - normal = -normal; - } -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/ClosestPoint.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/ClosestPoint.h deleted file mode 100644 index a437763f574..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/ClosestPoint.h +++ /dev/null @@ -1,498 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -JPH_NAMESPACE_BEGIN - -// Turn off fused multiply add instruction because it makes the equations of the form a * b - c * d inaccurate below -JPH_PRECISE_MATH_ON - -/// Helper utils to find the closest point to a line segment, triangle or tetrahedron -namespace ClosestPoint -{ - /// Compute barycentric coordinates of closest point to origin for infinite line defined by (inA, inB) - /// Point can then be computed as inA * outU + inB * outV - /// Returns false if the points inA, inB do not form a line (are at the same point) - inline bool GetBaryCentricCoordinates(Vec3Arg inA, Vec3Arg inB, float &outU, float &outV) - { - Vec3 ab = inB - inA; - float denominator = ab.LengthSq(); - if (denominator < Square(FLT_EPSILON)) - { - // Degenerate line segment, fallback to points - if (inA.LengthSq() < inB.LengthSq()) - { - // A closest - outU = 1.0f; - outV = 0.0f; - } - else - { - // B closest - outU = 0.0f; - outV = 1.0f; - } - return false; - } - else - { - outV = -inA.Dot(ab) / denominator; - outU = 1.0f - outV; - } - return true; - } - - /// Compute barycentric coordinates of closest point to origin for plane defined by (inA, inB, inC) - /// Point can then be computed as inA * outU + inB * outV + inC * outW - /// Returns false if the points inA, inB, inC do not form a plane (are on the same line or at the same point) - inline bool GetBaryCentricCoordinates(Vec3Arg inA, Vec3Arg inB, Vec3Arg inC, float &outU, float &outV, float &outW) - { - // Taken from: Real-Time Collision Detection - Christer Ericson (Section: Barycentric Coordinates) - // With p = 0 - // Adjusted to always include the shortest edge of the triangle in the calculation to improve numerical accuracy - - // First calculate the three edges - Vec3 v0 = inB - inA; - Vec3 v1 = inC - inA; - Vec3 v2 = inC - inB; - - // Make sure that the shortest edge is included in the calculation to keep the products a * b - c * d as small as possible to preserve accuracy - float d00 = v0.LengthSq(); - float d11 = v1.LengthSq(); - float d22 = v2.LengthSq(); - if (d00 <= d22) - { - // Use v0 and v1 to calculate barycentric coordinates - float d01 = v0.Dot(v1); - - // Denominator must be positive: - // |v0|^2 * |v1|^2 - (v0 . v1)^2 = |v0|^2 * |v1|^2 * (1 - cos(angle)^2) >= 0 - float denominator = d00 * d11 - d01 * d01; - if (denominator < 1.0e-12f) - { - // Degenerate triangle, return coordinates along longest edge - if (d00 > d11) - { - GetBaryCentricCoordinates(inA, inB, outU, outV); - outW = 0.0f; - } - else - { - GetBaryCentricCoordinates(inA, inC, outU, outW); - outV = 0.0f; - } - return false; - } - else - { - float a0 = inA.Dot(v0); - float a1 = inA.Dot(v1); - outV = (d01 * a1 - d11 * a0) / denominator; - outW = (d01 * a0 - d00 * a1) / denominator; - outU = 1.0f - outV - outW; - } - } - else - { - // Use v1 and v2 to calculate barycentric coordinates - float d12 = v1.Dot(v2); - - float denominator = d11 * d22 - d12 * d12; - if (denominator < 1.0e-12f) - { - // Degenerate triangle, return coordinates along longest edge - if (d11 > d22) - { - GetBaryCentricCoordinates(inA, inC, outU, outW); - outV = 0.0f; - } - else - { - GetBaryCentricCoordinates(inB, inC, outV, outW); - outU = 0.0f; - } - return false; - } - else - { - float c1 = inC.Dot(v1); - float c2 = inC.Dot(v2); - outU = (d22 * c1 - d12 * c2) / denominator; - outV = (d11 * c2 - d12 * c1) / denominator; - outW = 1.0f - outU - outV; - } - } - return true; - } - - /// Get the closest point to the origin of line (inA, inB) - /// outSet describes which features are closest: 1 = a, 2 = b, 3 = line segment ab - inline Vec3 GetClosestPointOnLine(Vec3Arg inA, Vec3Arg inB, uint32 &outSet) - { - float u, v; - GetBaryCentricCoordinates(inA, inB, u, v); - if (v <= 0.0f) - { - // inA is closest point - outSet = 0b0001; - return inA; - } - else if (u <= 0.0f) - { - // inB is closest point - outSet = 0b0010; - return inB; - } - else - { - // Closest point lies on line inA inB - outSet = 0b0011; - return u * inA + v * inB; - } - } - - /// Get the closest point to the origin of triangle (inA, inB, inC) - /// outSet describes which features are closest: 1 = a, 2 = b, 4 = c, 5 = line segment ac, 7 = triangle interior etc. - /// If MustIncludeC is true, the function assumes that C is part of the closest feature (vertex, edge, face) and does less work, if the assumption is not true then a closest point to the other features is returned. - template - inline Vec3 GetClosestPointOnTriangle(Vec3Arg inA, Vec3Arg inB, Vec3Arg inC, uint32 &outSet) - { - // Taken from: Real-Time Collision Detection - Christer Ericson (Section: Closest Point on Triangle to Point) - // With p = 0 - - // The most accurate normal is calculated by using the two shortest edges - // See: https://box2d.org/posts/2014/01/troublesome-triangle/ - // The difference in normals is most pronounced when one edge is much smaller than the others (in which case the other 2 must have roughly the same length). - // Therefore we can suffice by just picking the shortest from 2 edges and use that with the 3rd edge to calculate the normal. - // We first check which of the edges is shorter and if bc is shorter than ac then we swap a with c to a is always on the shortest edge - UVec4 swap_ac; - { - Vec3 ac = inC - inA; - Vec3 bc = inC - inB; - swap_ac = Vec4::sLess(bc.DotV4(bc), ac.DotV4(ac)); - } - Vec3 a = Vec3::sSelect(inA, inC, swap_ac); - Vec3 c = Vec3::sSelect(inC, inA, swap_ac); - - // Calculate normal - Vec3 ab = inB - a; - Vec3 ac = c - a; - Vec3 n = ab.Cross(ac); - float n_len_sq = n.LengthSq(); - - // Check degenerate - if (n_len_sq < 1.0e-10f) // Square(FLT_EPSILON) was too small and caused numerical problems, see test case TestCollideParallelTriangleVsCapsule - { - // Degenerate, fallback to vertices and edges - - // Start with vertex C being the closest - uint32 closest_set = 0b0100; - Vec3 closest_point = inC; - float best_dist_sq = inC.LengthSq(); - - // If the closest point must include C then A or B cannot be closest - // Note that we test vertices first because we want to prefer a closest vertex over a closest edge (this results in an outSet with fewer bits set) - if constexpr (!MustIncludeC) - { - // Try vertex A - float a_len_sq = inA.LengthSq(); - if (a_len_sq < best_dist_sq) - { - closest_set = 0b0001; - closest_point = inA; - best_dist_sq = a_len_sq; - } - - // Try vertex B - float b_len_sq = inB.LengthSq(); - if (b_len_sq < best_dist_sq) - { - closest_set = 0b0010; - closest_point = inB; - best_dist_sq = b_len_sq; - } - } - - // Edge AC - float ac_len_sq = ac.LengthSq(); - if (ac_len_sq > Square(FLT_EPSILON)) - { - float v = Clamp(-a.Dot(ac) / ac_len_sq, 0.0f, 1.0f); - Vec3 q = a + v * ac; - float dist_sq = q.LengthSq(); - if (dist_sq < best_dist_sq) - { - closest_set = 0b0101; - closest_point = q; - best_dist_sq = dist_sq; - } - } - - // Edge BC - Vec3 bc = inC - inB; - float bc_len_sq = bc.LengthSq(); - if (bc_len_sq > Square(FLT_EPSILON)) - { - float v = Clamp(-inB.Dot(bc) / bc_len_sq, 0.0f, 1.0f); - Vec3 q = inB + v * bc; - float dist_sq = q.LengthSq(); - if (dist_sq < best_dist_sq) - { - closest_set = 0b0110; - closest_point = q; - best_dist_sq = dist_sq; - } - } - - // If the closest point must include C then AB cannot be closest - if constexpr (!MustIncludeC) - { - // Edge AB - ab = inB - inA; - float ab_len_sq = ab.LengthSq(); - if (ab_len_sq > Square(FLT_EPSILON)) - { - float v = Clamp(-inA.Dot(ab) / ab_len_sq, 0.0f, 1.0f); - Vec3 q = inA + v * ab; - float dist_sq = q.LengthSq(); - if (dist_sq < best_dist_sq) - { - closest_set = 0b0011; - closest_point = q; - best_dist_sq = dist_sq; - } - } - } - - outSet = closest_set; - return closest_point; - } - - // Check if P in vertex region outside A - Vec3 ap = -a; - float d1 = ab.Dot(ap); - float d2 = ac.Dot(ap); - if (d1 <= 0.0f && d2 <= 0.0f) - { - outSet = swap_ac.GetX()? 0b0100 : 0b0001; - return a; // barycentric coordinates (1,0,0) - } - - // Check if P in vertex region outside B - Vec3 bp = -inB; - float d3 = ab.Dot(bp); - float d4 = ac.Dot(bp); - if (d3 >= 0.0f && d4 <= d3) - { - outSet = 0b0010; - return inB; // barycentric coordinates (0,1,0) - } - - // Check if P in edge region of AB, if so return projection of P onto AB - if (d1 * d4 <= d3 * d2 && d1 >= 0.0f && d3 <= 0.0f) - { - float v = d1 / (d1 - d3); - outSet = swap_ac.GetX()? 0b0110 : 0b0011; - return a + v * ab; // barycentric coordinates (1-v,v,0) - } - - // Check if P in vertex region outside C - Vec3 cp = -c; - float d5 = ab.Dot(cp); - float d6 = ac.Dot(cp); - if (d6 >= 0.0f && d5 <= d6) - { - outSet = swap_ac.GetX()? 0b0001 : 0b0100; - return c; // barycentric coordinates (0,0,1) - } - - // Check if P in edge region of AC, if so return projection of P onto AC - if (d5 * d2 <= d1 * d6 && d2 >= 0.0f && d6 <= 0.0f) - { - float w = d2 / (d2 - d6); - outSet = 0b0101; - return a + w * ac; // barycentric coordinates (1-w,0,w) - } - - // Check if P in edge region of BC, if so return projection of P onto BC - float d4_d3 = d4 - d3; - float d5_d6 = d5 - d6; - if (d3 * d6 <= d5 * d4 && d4_d3 >= 0.0f && d5_d6 >= 0.0f) - { - float w = d4_d3 / (d4_d3 + d5_d6); - outSet = swap_ac.GetX()? 0b0011 : 0b0110; - return inB + w * (c - inB); // barycentric coordinates (0,1-w,w) - } - - // P inside face region. - // Here we deviate from Christer Ericson's article to improve accuracy. - // Determine distance between triangle and origin: distance = (centroid - origin) . normal / |normal| - // Closest point to origin is then: distance . normal / |normal| - // Note that this way of calculating the closest point is much more accurate than first calculating barycentric coordinates - // and then calculating the closest point based on those coordinates. - outSet = 0b0111; - return n * (a + inB + c).Dot(n) / (3.0f * n_len_sq); - } - - /// Check if the origin is outside the plane of triangle (inA, inB, inC). inD specifies the front side of the plane. - inline bool OriginOutsideOfPlane(Vec3Arg inA, Vec3Arg inB, Vec3Arg inC, Vec3Arg inD) - { - // Taken from: Real-Time Collision Detection - Christer Ericson (Section: Closest Point on Tetrahedron to Point) - // With p = 0 - - // Test if point p and d lie on opposite sides of plane through abc - Vec3 n = (inB - inA).Cross(inC - inA); - float signp = inA.Dot(n); // [AP AB AC] - float signd = (inD - inA).Dot(n); // [AD AB AC] - - // Points on opposite sides if expression signs are the same - // Note that we left out the minus sign in signp so we need to check > 0 instead of < 0 as in Christer's book - // We compare against a small negative value to allow for a little bit of slop in the calculations - return signp * signd > -FLT_EPSILON; - } - - /// Returns for each of the planes of the tetrahedron if the origin is inside it - /// Roughly equivalent to: - /// [OriginOutsideOfPlane(inA, inB, inC, inD), - /// OriginOutsideOfPlane(inA, inC, inD, inB), - /// OriginOutsideOfPlane(inA, inD, inB, inC), - /// OriginOutsideOfPlane(inB, inD, inC, inA)] - inline UVec4 OriginOutsideOfTetrahedronPlanes(Vec3Arg inA, Vec3Arg inB, Vec3Arg inC, Vec3Arg inD) - { - Vec3 ab = inB - inA; - Vec3 ac = inC - inA; - Vec3 ad = inD - inA; - Vec3 bd = inD - inB; - Vec3 bc = inC - inB; - - Vec3 ab_cross_ac = ab.Cross(ac); - Vec3 ac_cross_ad = ac.Cross(ad); - Vec3 ad_cross_ab = ad.Cross(ab); - Vec3 bd_cross_bc = bd.Cross(bc); - - // For each plane get the side on which the origin is - float signp0 = inA.Dot(ab_cross_ac); // ABC - float signp1 = inA.Dot(ac_cross_ad); // ACD - float signp2 = inA.Dot(ad_cross_ab); // ADB - float signp3 = inB.Dot(bd_cross_bc); // BDC - Vec4 signp(signp0, signp1, signp2, signp3); - - // For each plane get the side that is outside (determined by the 4th point) - float signd0 = ad.Dot(ab_cross_ac); // D - float signd1 = ab.Dot(ac_cross_ad); // B - float signd2 = ac.Dot(ad_cross_ab); // C - float signd3 = -ab.Dot(bd_cross_bc); // A - Vec4 signd(signd0, signd1, signd2, signd3); - - // The winding of all triangles has been chosen so that signd should have the - // same sign for all components. If this is not the case the tetrahedron - // is degenerate and we return that the origin is in front of all sides - int sign_bits = signd.GetSignBits(); - switch (sign_bits) - { - case 0: - // All positive - return Vec4::sGreaterOrEqual(signp, Vec4::sReplicate(-FLT_EPSILON)); - - case 0xf: - // All negative - return Vec4::sLessOrEqual(signp, Vec4::sReplicate(FLT_EPSILON)); - - default: - // Mixed signs, degenerate tetrahedron - return UVec4::sReplicate(0xffffffff); - } - } - - /// Get the closest point between tetrahedron (inA, inB, inC, inD) to the origin - /// outSet specifies which feature was closest, 1 = a, 2 = b, 4 = c, 8 = d. Edges have 2 bits set, triangles 3 and if the point is in the interior 4 bits are set. - /// If MustIncludeD is true, the function assumes that D is part of the closest feature (vertex, edge, face, tetrahedron) and does less work, if the assumption is not true then a closest point to the other features is returned. - template - inline Vec3 GetClosestPointOnTetrahedron(Vec3Arg inA, Vec3Arg inB, Vec3Arg inC, Vec3Arg inD, uint32 &outSet) - { - // Taken from: Real-Time Collision Detection - Christer Ericson (Section: Closest Point on Tetrahedron to Point) - // With p = 0 - - // Start out assuming point inside all halfspaces, so closest to itself - uint32 closest_set = 0b1111; - Vec3 closest_point = Vec3::sZero(); - float best_dist_sq = FLT_MAX; - - // Determine for each of the faces of the tetrahedron if the origin is in front of the plane - UVec4 origin_out_of_planes = OriginOutsideOfTetrahedronPlanes(inA, inB, inC, inD); - - // If point outside face abc then compute closest point on abc - if (origin_out_of_planes.GetX()) // OriginOutsideOfPlane(inA, inB, inC, inD) - { - if constexpr (MustIncludeD) - { - // If the closest point must include D then ABC cannot be closest but the closest point - // cannot be an interior point either so we return A as closest point - closest_set = 0b0001; - closest_point = inA; - } - else - { - // Test the face normally - closest_point = GetClosestPointOnTriangle(inA, inB, inC, closest_set); - } - best_dist_sq = closest_point.LengthSq(); - } - - // Repeat test for face acd - if (origin_out_of_planes.GetY()) // OriginOutsideOfPlane(inA, inC, inD, inB) - { - uint32 set; - Vec3 q = GetClosestPointOnTriangle(inA, inC, inD, set); - float dist_sq = q.LengthSq(); - if (dist_sq < best_dist_sq) - { - best_dist_sq = dist_sq; - closest_point = q; - closest_set = (set & 0b0001) + ((set & 0b0110) << 1); - } - } - - // Repeat test for face adb - if (origin_out_of_planes.GetZ()) // OriginOutsideOfPlane(inA, inD, inB, inC) - { - // Keep original vertex order, it doesn't matter if the triangle is facing inward or outward - // and it improves consistency for GJK which will always add a new vertex D and keep the closest - // feature from the previous iteration in ABC - uint32 set; - Vec3 q = GetClosestPointOnTriangle(inA, inB, inD, set); - float dist_sq = q.LengthSq(); - if (dist_sq < best_dist_sq) - { - best_dist_sq = dist_sq; - closest_point = q; - closest_set = (set & 0b0011) + ((set & 0b0100) << 1); - } - } - - // Repeat test for face bdc - if (origin_out_of_planes.GetW()) // OriginOutsideOfPlane(inB, inD, inC, inA) - { - // Keep original vertex order, it doesn't matter if the triangle is facing inward or outward - // and it improves consistency for GJK which will always add a new vertex D and keep the closest - // feature from the previous iteration in ABC - uint32 set; - Vec3 q = GetClosestPointOnTriangle(inB, inC, inD, set); - float dist_sq = q.LengthSq(); - if (dist_sq < best_dist_sq) - { - closest_point = q; - closest_set = set << 1; - } - } - - outSet = closest_set; - return closest_point; - } -}; - -JPH_PRECISE_MATH_OFF - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/ConvexHullBuilder.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/ConvexHullBuilder.cpp deleted file mode 100644 index 16ccf89c899..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/ConvexHullBuilder.cpp +++ /dev/null @@ -1,1465 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include -#include -#include - -JPH_SUPPRESS_WARNINGS_STD_BEGIN -#include -JPH_SUPPRESS_WARNINGS_STD_END - -#ifdef JPH_CONVEX_BUILDER_DEBUG - #include -#endif - -JPH_NAMESPACE_BEGIN - -ConvexHullBuilder::Face::~Face() -{ - // Free all edges - Edge *e = mFirstEdge; - if (e != nullptr) - { - do - { - Edge *next = e->mNextEdge; - delete e; - e = next; - } while (e != mFirstEdge); - } -} - -void ConvexHullBuilder::Face::CalculateNormalAndCentroid(const Vec3 *inPositions) -{ - // Get point that we use to construct a triangle fan - Edge *e = mFirstEdge; - Vec3 y0 = inPositions[e->mStartIdx]; - - // Get the 2nd point - e = e->mNextEdge; - Vec3 y1 = inPositions[e->mStartIdx]; - - // Start accumulating the centroid - mCentroid = y0 + y1; - int n = 2; - - // Start accumulating the normal - mNormal = Vec3::sZero(); - - // Loop over remaining edges accumulating normals in a triangle fan fashion - for (e = e->mNextEdge; e != mFirstEdge; e = e->mNextEdge) - { - // Get the 3rd point - Vec3 y2 = inPositions[e->mStartIdx]; - - // Calculate edges (counter clockwise) - Vec3 e0 = y1 - y0; - Vec3 e1 = y2 - y1; - Vec3 e2 = y0 - y2; - - // The best normal is calculated by using the two shortest edges - // See: https://box2d.org/posts/2014/01/troublesome-triangle/ - // The difference in normals is most pronounced when one edge is much smaller than the others (in which case the others must have roughly the same length). - // Therefore we can suffice by just picking the shortest from 2 edges and use that with the 3rd edge to calculate the normal. - // We first check which of the edges is shorter: e1 or e2 - UVec4 e1_shorter_than_e2 = Vec4::sLess(e1.DotV4(e1), e2.DotV4(e2)); - - // We calculate both normals and then select the one that had the shortest edge for our normal (this avoids branching) - Vec3 normal_e01 = e0.Cross(e1); - Vec3 normal_e02 = e2.Cross(e0); - mNormal += Vec3::sSelect(normal_e02, normal_e01, e1_shorter_than_e2); - - // Accumulate centroid - mCentroid += y2; - n++; - - // Update y1 for next triangle - y1 = y2; - } - - // Finalize centroid - mCentroid /= float(n); -} - -void ConvexHullBuilder::Face::Initialize(int inIdx0, int inIdx1, int inIdx2, const Vec3 *inPositions) -{ - JPH_ASSERT(mFirstEdge == nullptr); - JPH_ASSERT(inIdx0 != inIdx1 && inIdx0 != inIdx2 && inIdx1 != inIdx2); - - // Create 3 edges - Edge *e0 = new Edge(this, inIdx0); - Edge *e1 = new Edge(this, inIdx1); - Edge *e2 = new Edge(this, inIdx2); - - // Link edges - e0->mNextEdge = e1; - e1->mNextEdge = e2; - e2->mNextEdge = e0; - mFirstEdge = e0; - - CalculateNormalAndCentroid(inPositions); -} - -ConvexHullBuilder::ConvexHullBuilder(const Positions &inPositions) : - mPositions(inPositions) -{ -#ifdef JPH_CONVEX_BUILDER_DEBUG - mIteration = 0; - - // Center the drawing of the first hull around the origin and calculate the delta offset between states - mOffset = RVec3::sZero(); - if (mPositions.empty()) - { - // No hull will be generated - mDelta = Vec3::sZero(); - } - else - { - Vec3 maxv = Vec3::sReplicate(-FLT_MAX), minv = Vec3::sReplicate(FLT_MAX); - for (Vec3 v : mPositions) - { - minv = Vec3::sMin(minv, v); - maxv = Vec3::sMax(maxv, v); - mOffset -= v; - } - mOffset /= Real(mPositions.size()); - mDelta = Vec3((maxv - minv).GetX() + 0.5f, 0, 0); - mOffset += mDelta; // Don't start at origin, we're already drawing the final hull there - } -#endif -} - -void ConvexHullBuilder::FreeFaces() -{ - for (Face *f : mFaces) - delete f; - mFaces.clear(); -} - -void ConvexHullBuilder::GetFaceForPoint(Vec3Arg inPoint, const Faces &inFaces, Face *&outFace, float &outDistSq) const -{ - outFace = nullptr; - outDistSq = 0.0f; - - for (Face *f : inFaces) - if (!f->mRemoved) - { - // Determine distance to face - float dot = f->mNormal.Dot(inPoint - f->mCentroid); - if (dot > 0.0f) - { - float dist_sq = dot * dot / f->mNormal.LengthSq(); - if (dist_sq > outDistSq) - { - outFace = f; - outDistSq = dist_sq; - } - } - } -} - -float ConvexHullBuilder::GetDistanceToEdgeSq(Vec3Arg inPoint, const Face *inFace) const -{ - bool all_inside = true; - float edge_dist_sq = FLT_MAX; - - // Test if it is inside the edges of the polygon - Edge *edge = inFace->mFirstEdge; - Vec3 p1 = mPositions[edge->GetPreviousEdge()->mStartIdx]; - do - { - Vec3 p2 = mPositions[edge->mStartIdx]; - if ((p2 - p1).Cross(inPoint - p1).Dot(inFace->mNormal) < 0.0f) - { - // It is outside - all_inside = false; - - // Measure distance to this edge - uint32 s; - edge_dist_sq = min(edge_dist_sq, ClosestPoint::GetClosestPointOnLine(p1 - inPoint, p2 - inPoint, s).LengthSq()); - } - p1 = p2; - edge = edge->mNextEdge; - } while (edge != inFace->mFirstEdge); - - return all_inside? 0.0f : edge_dist_sq; -} - -bool ConvexHullBuilder::AssignPointToFace(int inPositionIdx, const Faces &inFaces, float inToleranceSq) -{ - Vec3 point = mPositions[inPositionIdx]; - - // Find the face for which the point is furthest away - Face *best_face; - float best_dist_sq; - GetFaceForPoint(point, inFaces, best_face, best_dist_sq); - - if (best_face != nullptr) - { - // Check if this point is within the tolerance margin to the plane - if (best_dist_sq <= inToleranceSq) - { - // Check distance to edges - float dist_to_edge_sq = GetDistanceToEdgeSq(point, best_face); - if (dist_to_edge_sq > inToleranceSq) - { - // Point is outside of the face and too far away to discard - mCoplanarList.push_back({ inPositionIdx, dist_to_edge_sq }); - } - } - else - { - // This point is in front of the face, add it to the conflict list - if (best_dist_sq > best_face->mFurthestPointDistanceSq) - { - // This point is further away than any others, update the distance and add point as last point - best_face->mFurthestPointDistanceSq = best_dist_sq; - best_face->mConflictList.push_back(inPositionIdx); - } - else - { - // Not the furthest point, add it as the before last point - best_face->mConflictList.push_back(best_face->mConflictList.back()); - best_face->mConflictList[best_face->mConflictList.size() - 2] = inPositionIdx; - } - - return true; - } - } - - return false; -} - -float ConvexHullBuilder::DetermineCoplanarDistance() const -{ - // Formula as per: Implementing Quickhull - Dirk Gregorius. - Vec3 vmax = Vec3::sZero(); - for (Vec3 v : mPositions) - vmax = Vec3::sMax(vmax, v.Abs()); - return 3.0f * FLT_EPSILON * (vmax.GetX() + vmax.GetY() + vmax.GetZ()); -} - -int ConvexHullBuilder::GetNumVerticesUsed() const -{ - UnorderedSet used_verts; - for (Face *f : mFaces) - { - Edge *e = f->mFirstEdge; - do - { - used_verts.insert(e->mStartIdx); - e = e->mNextEdge; - } while (e != f->mFirstEdge); - } - return (int)used_verts.size(); -} - -bool ConvexHullBuilder::ContainsFace(const Array &inIndices) const -{ - for (Face *f : mFaces) - { - Edge *e = f->mFirstEdge; - Array::const_iterator index = find(inIndices.begin(), inIndices.end(), e->mStartIdx); - if (index != inIndices.end()) - { - size_t matches = 0; - - do - { - // Check if index matches - if (*index != e->mStartIdx) - break; - - // Increment number of matches - matches++; - - // Next index in list of inIndices - index++; - if (index == inIndices.end()) - index = inIndices.begin(); - - // Next edge - e = e->mNextEdge; - } while (e != f->mFirstEdge); - - if (matches == inIndices.size()) - return true; - } - } - - return false; -} - -ConvexHullBuilder::EResult ConvexHullBuilder::Initialize(int inMaxVertices, float inTolerance, const char *&outError) -{ - // Free the faces possibly left over from an earlier hull - FreeFaces(); - - // Test that we have at least 3 points - if (mPositions.size() < 3) - { - outError = "Need at least 3 points to make a hull"; - return EResult::TooFewPoints; - } - - // Determine a suitable tolerance for detecting that points are coplanar - float coplanar_tolerance_sq = Square(DetermineCoplanarDistance()); - - // Increase desired tolerance if accuracy doesn't allow it - float tolerance_sq = max(coplanar_tolerance_sq, Square(inTolerance)); - - // Find point furthest from the origin - int idx1 = -1; - float max_dist_sq = -1.0f; - for (int i = 0; i < (int)mPositions.size(); ++i) - { - float dist_sq = mPositions[i].LengthSq(); - if (dist_sq > max_dist_sq) - { - max_dist_sq = dist_sq; - idx1 = i; - } - } - JPH_ASSERT(idx1 >= 0); - - // Find point that is furthest away from this point - int idx2 = -1; - max_dist_sq = -1.0f; - for (int i = 0; i < (int)mPositions.size(); ++i) - if (i != idx1) - { - float dist_sq = (mPositions[i] - mPositions[idx1]).LengthSq(); - if (dist_sq > max_dist_sq) - { - max_dist_sq = dist_sq; - idx2 = i; - } - } - JPH_ASSERT(idx2 >= 0); - - // Find point that forms the biggest triangle - int idx3 = -1; - float best_triangle_area_sq = -1.0f; - for (int i = 0; i < (int)mPositions.size(); ++i) - if (i != idx1 && i != idx2) - { - float triangle_area_sq = (mPositions[idx1] - mPositions[i]).Cross(mPositions[idx2] - mPositions[i]).LengthSq(); - if (triangle_area_sq > best_triangle_area_sq) - { - best_triangle_area_sq = triangle_area_sq; - idx3 = i; - } - } - JPH_ASSERT(idx3 >= 0); - if (best_triangle_area_sq < cMinTriangleAreaSq) - { - outError = "Could not find a suitable initial triangle because its area was too small"; - return EResult::Degenerate; - } - - // Check if we have only 3 vertices - if (mPositions.size() == 3) - { - // Create two triangles (back to back) - Face *t1 = CreateTriangle(idx1, idx2, idx3); - Face *t2 = CreateTriangle(idx1, idx3, idx2); - - // Link faces edges - sLinkFace(t1->mFirstEdge, t2->mFirstEdge->mNextEdge->mNextEdge); - sLinkFace(t1->mFirstEdge->mNextEdge, t2->mFirstEdge->mNextEdge); - sLinkFace(t1->mFirstEdge->mNextEdge->mNextEdge, t2->mFirstEdge); - -#ifdef JPH_CONVEX_BUILDER_DEBUG - // Draw current state - DrawState(); -#endif - - return EResult::Success; - } - - // Find point that forms the biggest tetrahedron - Vec3 initial_plane_normal = (mPositions[idx2] - mPositions[idx1]).Cross(mPositions[idx3] - mPositions[idx1]).Normalized(); - Vec3 initial_plane_centroid = (mPositions[idx1] + mPositions[idx2] + mPositions[idx3]) / 3.0f; - int idx4 = -1; - float max_dist = 0.0f; - for (int i = 0; i < (int)mPositions.size(); ++i) - if (i != idx1 && i != idx2 && i != idx3) - { - float dist = (mPositions[i] - initial_plane_centroid).Dot(initial_plane_normal); - if (abs(dist) > abs(max_dist)) - { - max_dist = dist; - idx4 = i; - } - } - - // Check if the hull is coplanar - if (Square(max_dist) <= 25.0f * coplanar_tolerance_sq) - { - // First project all points in 2D space - Vec3 base1 = initial_plane_normal.GetNormalizedPerpendicular(); - Vec3 base2 = initial_plane_normal.Cross(base1); - Array positions_2d; - positions_2d.reserve(mPositions.size()); - for (Vec3 v : mPositions) - positions_2d.emplace_back(base1.Dot(v), base2.Dot(v), 0.0f); - - // Build hull - Array edges_2d; - ConvexHullBuilder2D builder_2d(positions_2d); - ConvexHullBuilder2D::EResult result = builder_2d.Initialize(idx1, idx2, idx3, inMaxVertices, inTolerance, edges_2d); - - // Create faces (back to back) - Face *f1 = CreateFace(); - Face *f2 = CreateFace(); - - // Create edges for face 1 - Array edges_f1; - edges_f1.reserve(edges_2d.size()); - for (int start_idx : edges_2d) - { - Edge *edge = new Edge(f1, start_idx); - if (edges_f1.empty()) - f1->mFirstEdge = edge; - else - edges_f1.back()->mNextEdge = edge; - edges_f1.push_back(edge); - } - edges_f1.back()->mNextEdge = f1->mFirstEdge; - - // Create edges for face 2 - Array edges_f2; - edges_f2.reserve(edges_2d.size()); - for (int i = (int)edges_2d.size() - 1; i >= 0; --i) - { - Edge *edge = new Edge(f2, edges_2d[i]); - if (edges_f2.empty()) - f2->mFirstEdge = edge; - else - edges_f2.back()->mNextEdge = edge; - edges_f2.push_back(edge); - } - edges_f2.back()->mNextEdge = f2->mFirstEdge; - - // Link edges - for (size_t i = 0; i < edges_2d.size(); ++i) - sLinkFace(edges_f1[i], edges_f2[(2 * edges_2d.size() - 2 - i) % edges_2d.size()]); - - // Calculate the plane for both faces - f1->CalculateNormalAndCentroid(mPositions.data()); - f2->mNormal = -f1->mNormal; - f2->mCentroid = f1->mCentroid; - -#ifdef JPH_CONVEX_BUILDER_DEBUG - // Draw current state - DrawState(); -#endif - - return result == ConvexHullBuilder2D::EResult::MaxVerticesReached? EResult::MaxVerticesReached : EResult::Success; - } - - // Ensure the planes are facing outwards - if (max_dist < 0.0f) - swap(idx2, idx3); - - // Create tetrahedron - Face *t1 = CreateTriangle(idx1, idx2, idx4); - Face *t2 = CreateTriangle(idx2, idx3, idx4); - Face *t3 = CreateTriangle(idx3, idx1, idx4); - Face *t4 = CreateTriangle(idx1, idx3, idx2); - - // Link face edges - sLinkFace(t1->mFirstEdge, t4->mFirstEdge->mNextEdge->mNextEdge); - sLinkFace(t1->mFirstEdge->mNextEdge, t2->mFirstEdge->mNextEdge->mNextEdge); - sLinkFace(t1->mFirstEdge->mNextEdge->mNextEdge, t3->mFirstEdge->mNextEdge); - sLinkFace(t2->mFirstEdge, t4->mFirstEdge->mNextEdge); - sLinkFace(t2->mFirstEdge->mNextEdge, t3->mFirstEdge->mNextEdge->mNextEdge); - sLinkFace(t3->mFirstEdge, t4->mFirstEdge); - - // Build the initial conflict lists - Faces faces { t1, t2, t3, t4 }; - for (int idx = 0; idx < (int)mPositions.size(); ++idx) - if (idx != idx1 && idx != idx2 && idx != idx3 && idx != idx4) - AssignPointToFace(idx, faces, tolerance_sq); - -#ifdef JPH_CONVEX_BUILDER_DEBUG - // Draw current state including conflict list - DrawState(true); - - // Increment iteration counter - ++mIteration; -#endif - - // Overestimate of the actual amount of vertices we use, for limiting the amount of vertices in the hull - int num_vertices_used = 4; - - // Loop through the remainder of the points and add them - for (;;) - { - // Find the face with the furthest point on it - Face *face_with_furthest_point = nullptr; - float furthest_dist_sq = 0.0f; - for (Face *f : mFaces) - if (f->mFurthestPointDistanceSq > furthest_dist_sq) - { - furthest_dist_sq = f->mFurthestPointDistanceSq; - face_with_furthest_point = f; - } - - int furthest_point_idx; - if (face_with_furthest_point != nullptr) - { - // Take the furthest point - furthest_point_idx = face_with_furthest_point->mConflictList.back(); - face_with_furthest_point->mConflictList.pop_back(); - } - else if (!mCoplanarList.empty()) - { - // Try to assign points to faces (this also recalculates the distance to the hull for the coplanar vertices) - CoplanarList coplanar; - mCoplanarList.swap(coplanar); - bool added = false; - for (const Coplanar &c : coplanar) - added |= AssignPointToFace(c.mPositionIdx, mFaces, tolerance_sq); - - // If we were able to assign a point, loop again to pick it up - if (added) - continue; - - // If the coplanar list is empty, there are no points left and we're done - if (mCoplanarList.empty()) - break; - - do - { - // Find the vertex that is furthest from the hull - CoplanarList::size_type best_idx = 0; - float best_dist_sq = mCoplanarList.front().mDistanceSq; - for (CoplanarList::size_type idx = 1; idx < mCoplanarList.size(); ++idx) - { - const Coplanar &c = mCoplanarList[idx]; - if (c.mDistanceSq > best_dist_sq) - { - best_idx = idx; - best_dist_sq = c.mDistanceSq; - } - } - - // Swap it to the end - swap(mCoplanarList[best_idx], mCoplanarList.back()); - - // Remove it - furthest_point_idx = mCoplanarList.back().mPositionIdx; - mCoplanarList.pop_back(); - - // Find the face for which the point is furthest away - GetFaceForPoint(mPositions[furthest_point_idx], mFaces, face_with_furthest_point, best_dist_sq); - } while (!mCoplanarList.empty() && face_with_furthest_point == nullptr); - - if (face_with_furthest_point == nullptr) - break; - } - else - { - // If there are no more vertices, we're done - break; - } - - // Check if we have a limit on the max vertices that we should produce - if (num_vertices_used >= inMaxVertices) - { - // Count the actual amount of used vertices (we did not take the removal of any vertices into account) - num_vertices_used = GetNumVerticesUsed(); - - // Check if there are too many - if (num_vertices_used >= inMaxVertices) - return EResult::MaxVerticesReached; - } - - // We're about to add another vertex - ++num_vertices_used; - - // Add the point to the hull - Faces new_faces; - AddPoint(face_with_furthest_point, furthest_point_idx, coplanar_tolerance_sq, new_faces); - - // Redistribute points on conflict lists belonging to removed faces - for (const Face *face : mFaces) - if (face->mRemoved) - for (int idx : face->mConflictList) - AssignPointToFace(idx, new_faces, tolerance_sq); - - // Permanently delete faces that we removed in AddPoint() - GarbageCollectFaces(); - -#ifdef JPH_CONVEX_BUILDER_DEBUG - // Draw state at the end of this step including conflict list - DrawState(true); - - // Increment iteration counter - ++mIteration; -#endif - } - - // Check if we are left with a hull. It is possible that hull building fails if the points are nearly coplanar. - if (mFaces.size() < 2) - { - outError = "Too few faces in hull"; - return EResult::TooFewFaces; - } - - return EResult::Success; -} - -void ConvexHullBuilder::AddPoint(Face *inFacingFace, int inIdx, float inCoplanarToleranceSq, Faces &outNewFaces) -{ - // Get position - Vec3 pos = mPositions[inIdx]; - -#ifdef JPH_CONVEX_BUILDER_DEBUG - // Draw point to be added - DebugRenderer::sInstance->DrawMarker(cDrawScale * (mOffset + pos), Color::sYellow, 0.1f); - DebugRenderer::sInstance->DrawText3D(cDrawScale * (mOffset + pos), ConvertToString(inIdx), Color::sWhite); -#endif - -#ifdef JPH_ENABLE_ASSERTS - // Check if structure is intact - ValidateFaces(); -#endif - - // Find edge of convex hull of faces that are not facing the new vertex - FullEdges edges; - FindEdge(inFacingFace, pos, edges); - JPH_ASSERT(edges.size() >= 3); - - // Create new faces - outNewFaces.reserve(edges.size()); - for (const FullEdge &e : edges) - { - JPH_ASSERT(e.mStartIdx != e.mEndIdx); - Face *f = CreateTriangle(e.mStartIdx, e.mEndIdx, inIdx); - outNewFaces.push_back(f); - } - - // Link edges - for (Faces::size_type i = 0; i < outNewFaces.size(); ++i) - { - sLinkFace(outNewFaces[i]->mFirstEdge, edges[i].mNeighbourEdge); - sLinkFace(outNewFaces[i]->mFirstEdge->mNextEdge, outNewFaces[(i + 1) % outNewFaces.size()]->mFirstEdge->mNextEdge->mNextEdge); - } - - // Loop on faces that were modified until nothing needs to be checked anymore - Faces affected_faces = outNewFaces; - while (!affected_faces.empty()) - { - // Take the next face - Face *face = affected_faces.back(); - affected_faces.pop_back(); - - if (!face->mRemoved) - { - // Merge with neighbour if this is a degenerate face - MergeDegenerateFace(face, affected_faces); - - // Merge with coplanar neighbours (or when the neighbour forms a concave edge) - if (!face->mRemoved) - MergeCoplanarOrConcaveFaces(face, inCoplanarToleranceSq, affected_faces); - } - } - -#ifdef JPH_ENABLE_ASSERTS - // Check if structure is intact - ValidateFaces(); -#endif -} - -void ConvexHullBuilder::GarbageCollectFaces() -{ - for (int i = (int)mFaces.size() - 1; i >= 0; --i) - { - Face *f = mFaces[i]; - if (f->mRemoved) - { - FreeFace(f); - mFaces.erase(mFaces.begin() + i); - } - } -} - -ConvexHullBuilder::Face *ConvexHullBuilder::CreateFace() -{ - // Call provider to create face - Face *f = new Face(); - -#ifdef JPH_CONVEX_BUILDER_DEBUG - // Remember iteration counter - f->mIteration = mIteration; -#endif - - // Add to list - mFaces.push_back(f); - return f; -} - -ConvexHullBuilder::Face *ConvexHullBuilder::CreateTriangle(int inIdx1, int inIdx2, int inIdx3) -{ - Face *f = CreateFace(); - f->Initialize(inIdx1, inIdx2, inIdx3, mPositions.data()); - return f; -} - -void ConvexHullBuilder::FreeFace(Face *inFace) -{ - JPH_ASSERT(inFace->mRemoved); - -#ifdef JPH_ENABLE_ASSERTS - // Make sure that this face is not connected - Edge *e = inFace->mFirstEdge; - if (e != nullptr) - do - { - JPH_ASSERT(e->mNeighbourEdge == nullptr); - e = e->mNextEdge; - } while (e != inFace->mFirstEdge); -#endif - - // Free the face - delete inFace; -} - -void ConvexHullBuilder::sLinkFace(Edge *inEdge1, Edge *inEdge2) -{ - // Check not connected yet - JPH_ASSERT(inEdge1->mNeighbourEdge == nullptr); - JPH_ASSERT(inEdge2->mNeighbourEdge == nullptr); - JPH_ASSERT(inEdge1->mFace != inEdge2->mFace); - - // Check vertices match - JPH_ASSERT(inEdge1->mStartIdx == inEdge2->mNextEdge->mStartIdx); - JPH_ASSERT(inEdge2->mStartIdx == inEdge1->mNextEdge->mStartIdx); - - // Link up - inEdge1->mNeighbourEdge = inEdge2; - inEdge2->mNeighbourEdge = inEdge1; -} - -void ConvexHullBuilder::sUnlinkFace(Face *inFace) -{ - // Unlink from neighbours - Edge *e = inFace->mFirstEdge; - do - { - if (e->mNeighbourEdge != nullptr) - { - // Validate that neighbour points to us - JPH_ASSERT(e->mNeighbourEdge->mNeighbourEdge == e); - - // Unlink - e->mNeighbourEdge->mNeighbourEdge = nullptr; - e->mNeighbourEdge = nullptr; - } - e = e->mNextEdge; - } while (e != inFace->mFirstEdge); -} - -void ConvexHullBuilder::FindEdge(Face *inFacingFace, Vec3Arg inVertex, FullEdges &outEdges) const -{ - // Assert that we were given an empty array - JPH_ASSERT(outEdges.empty()); - - // Should start with a facing face - JPH_ASSERT(inFacingFace->IsFacing(inVertex)); - - // Flag as removed - inFacingFace->mRemoved = true; - - // Instead of recursing, we build our own stack with the information we need - struct StackEntry - { - Edge * mFirstEdge; - Edge * mCurrentEdge; - }; - constexpr int cMaxEdgeLength = 128; - StackEntry stack[cMaxEdgeLength]; - int cur_stack_pos = 0; - - static_assert(alignof(Edge) >= 2, "Need lowest bit to indicate to tell if we completed the loop"); - - // Start with the face / edge provided - stack[0].mFirstEdge = inFacingFace->mFirstEdge; - stack[0].mCurrentEdge = reinterpret_cast(reinterpret_cast(inFacingFace->mFirstEdge) | 1); // Set lowest bit of pointer to make it different from the first edge - - for (;;) - { - StackEntry &cur_entry = stack[cur_stack_pos]; - - // Next edge - Edge *raw_e = cur_entry.mCurrentEdge; - Edge *e = reinterpret_cast(reinterpret_cast(raw_e) & ~uintptr_t(1)); // Remove the lowest bit which was used to indicate that this is the first edge we're testing - cur_entry.mCurrentEdge = e->mNextEdge; - - // If we're back at the first edge we've completed the face and we're done - if (raw_e == cur_entry.mFirstEdge) - { - // This face needs to be removed, unlink it now, caller will free - sUnlinkFace(e->mFace); - - // Pop from stack - if (--cur_stack_pos < 0) - break; - } - else - { - // Visit neighbour face - Edge *ne = e->mNeighbourEdge; - if (ne != nullptr) - { - Face *n = ne->mFace; - if (!n->mRemoved) - { - // Check if vertex is on the front side of this face - if (n->IsFacing(inVertex)) - { - // Vertex on front, this face needs to be removed - n->mRemoved = true; - - // Add element to the stack of elements to visit - cur_stack_pos++; - JPH_ASSERT(cur_stack_pos < cMaxEdgeLength); - StackEntry &new_entry = stack[cur_stack_pos]; - new_entry.mFirstEdge = ne; - new_entry.mCurrentEdge = ne->mNextEdge; // We don't need to test this edge again since we came from it - } - else - { - // Vertex behind, keep edge - FullEdge full; - full.mNeighbourEdge = ne; - full.mStartIdx = e->mStartIdx; - full.mEndIdx = ne->mStartIdx; - outEdges.push_back(full); - } - } - } - } - } - - // Assert that we have a fully connected loop -#ifdef JPH_ENABLE_ASSERTS - for (int i = 0; i < (int)outEdges.size(); ++i) - JPH_ASSERT(outEdges[i].mEndIdx == outEdges[(i + 1) % outEdges.size()].mStartIdx); -#endif - -#ifdef JPH_CONVEX_BUILDER_DEBUG - // Draw edge of facing faces - for (int i = 0; i < (int)outEdges.size(); ++i) - DebugRenderer::sInstance->DrawArrow(cDrawScale * (mOffset + mPositions[outEdges[i].mStartIdx]), cDrawScale * (mOffset + mPositions[outEdges[i].mEndIdx]), Color::sWhite, 0.01f); - DrawState(); -#endif -} - -void ConvexHullBuilder::MergeFaces(Edge *inEdge) -{ - // Get the face - Face *face = inEdge->mFace; - - // Find the previous and next edge - Edge *next_edge = inEdge->mNextEdge; - Edge *prev_edge = inEdge->GetPreviousEdge(); - - // Get the other face - Edge *other_edge = inEdge->mNeighbourEdge; - Face *other_face = other_edge->mFace; - - // Check if attempting to merge with self - JPH_ASSERT(face != other_face); - -#ifdef JPH_CONVEX_BUILDER_DEBUG - DrawWireFace(face, Color::sGreen); - DrawWireFace(other_face, Color::sRed); - DrawState(); -#endif - - // Loop over the edges of the other face and make them belong to inFace - Edge *edge = other_edge->mNextEdge; - prev_edge->mNextEdge = edge; - for (;;) - { - edge->mFace = face; - if (edge->mNextEdge == other_edge) - { - // Terminate when we are back at other_edge - edge->mNextEdge = next_edge; - break; - } - edge = edge->mNextEdge; - } - - // If the first edge happens to be inEdge we need to fix it because this edge is no longer part of the face. - // Note that we replace it with the first edge of the merged face so that if the MergeFace function is called - // from a loop that loops around the face that it will still terminate after visiting all edges once. - if (face->mFirstEdge == inEdge) - face->mFirstEdge = prev_edge->mNextEdge; - - // Free the edges - delete inEdge; - delete other_edge; - - // Mark the other face as removed - other_face->mFirstEdge = nullptr; - other_face->mRemoved = true; - - // Recalculate plane - face->CalculateNormalAndCentroid(mPositions.data()); - - // Merge conflict lists - if (face->mFurthestPointDistanceSq > other_face->mFurthestPointDistanceSq) - { - // This face has a point that's further away, make sure it remains the last one as we add the other points to this faces list - face->mConflictList.insert(face->mConflictList.end() - 1, other_face->mConflictList.begin(), other_face->mConflictList.end()); - } - else - { - // The other face has a point that's furthest away, add that list at the end. - face->mConflictList.insert(face->mConflictList.end(), other_face->mConflictList.begin(), other_face->mConflictList.end()); - face->mFurthestPointDistanceSq = other_face->mFurthestPointDistanceSq; - } - other_face->mConflictList.clear(); - -#ifdef JPH_CONVEX_BUILDER_DEBUG - DrawWireFace(face, Color::sWhite); - DrawState(); -#endif -} - -void ConvexHullBuilder::MergeDegenerateFace(Face *inFace, Faces &ioAffectedFaces) -{ - // Check area of face - if (inFace->mNormal.LengthSq() < cMinTriangleAreaSq) - { - // Find longest edge, since this face is a sliver this should keep the face convex - float max_length_sq = 0.0f; - Edge *longest_edge = nullptr; - Edge *e = inFace->mFirstEdge; - Vec3 p1 = mPositions[e->mStartIdx]; - do - { - Edge *next = e->mNextEdge; - Vec3 p2 = mPositions[next->mStartIdx]; - float length_sq = (p2 - p1).LengthSq(); - if (length_sq >= max_length_sq) - { - max_length_sq = length_sq; - longest_edge = e; - } - p1 = p2; - e = next; - } while (e != inFace->mFirstEdge); - - // Merge with face on longest edge - MergeFaces(longest_edge); - - // Remove any invalid edges - RemoveInvalidEdges(inFace, ioAffectedFaces); - } -} - -void ConvexHullBuilder::MergeCoplanarOrConcaveFaces(Face *inFace, float inCoplanarToleranceSq, Faces &ioAffectedFaces) -{ - bool merged = false; - - Edge *edge = inFace->mFirstEdge; - do - { - // Store next edge since this edge can be removed - Edge *next_edge = edge->mNextEdge; - - // Test if centroid of one face is above plane of the other face by inCoplanarToleranceSq. - // If so we need to merge other face into inFace. - const Face *other_face = edge->mNeighbourEdge->mFace; - Vec3 delta_centroid = other_face->mCentroid - inFace->mCentroid; - float dist_other_face_centroid = inFace->mNormal.Dot(delta_centroid); - float signed_dist_other_face_centroid_sq = abs(dist_other_face_centroid) * dist_other_face_centroid; - float dist_face_centroid = -other_face->mNormal.Dot(delta_centroid); - float signed_dist_face_centroid_sq = abs(dist_face_centroid) * dist_face_centroid; - float face_normal_len_sq = inFace->mNormal.LengthSq(); - float other_face_normal_len_sq = other_face->mNormal.LengthSq(); - if ((signed_dist_other_face_centroid_sq > -inCoplanarToleranceSq * face_normal_len_sq - || signed_dist_face_centroid_sq > -inCoplanarToleranceSq * other_face_normal_len_sq) - && inFace->mNormal.Dot(other_face->mNormal) > 0.0f) // Never merge faces that are back to back - { - MergeFaces(edge); - merged = true; - } - - edge = next_edge; - } while (edge != inFace->mFirstEdge); - - if (merged) - RemoveInvalidEdges(inFace, ioAffectedFaces); -} - -void ConvexHullBuilder::sMarkAffected(Face *inFace, Faces &ioAffectedFaces) -{ - if (find(ioAffectedFaces.begin(), ioAffectedFaces.end(), inFace) == ioAffectedFaces.end()) - ioAffectedFaces.push_back(inFace); -} - -void ConvexHullBuilder::RemoveInvalidEdges(Face *inFace, Faces &ioAffectedFaces) -{ - // This marks that the plane needs to be recalculated (we delay this until the end of the - // function since we don't use the plane and we want to avoid calculating it multiple times) - bool recalculate_plane = false; - - // We keep going through this loop until no more edges were removed - bool removed; - do - { - removed = false; - - // Loop over all edges in this face - Edge *edge = inFace->mFirstEdge; - Face *neighbour_face = edge->mNeighbourEdge->mFace; - do - { - Edge *next_edge = edge->mNextEdge; - Face *next_neighbour_face = next_edge->mNeighbourEdge->mFace; - - if (neighbour_face == inFace) - { - // We only remove 1 edge at a time, check if this edge's next edge is our neighbour. - // If this check fails, we will continue to scan along the edge until we find an edge where this is the case. - if (edge->mNeighbourEdge == next_edge) - { - // This edge leads back to the starting point, this means the edge is interior and needs to be removed -#ifdef JPH_CONVEX_BUILDER_DEBUG - DrawWireFace(inFace, Color::sBlue); - DrawState(); -#endif - - // Remove edge - Edge *prev_edge = edge->GetPreviousEdge(); - prev_edge->mNextEdge = next_edge->mNextEdge; - if (inFace->mFirstEdge == edge || inFace->mFirstEdge == next_edge) - inFace->mFirstEdge = prev_edge; - delete edge; - delete next_edge; - -#ifdef JPH_CONVEX_BUILDER_DEBUG - DrawWireFace(inFace, Color::sGreen); - DrawState(); -#endif - - // Check if inFace now has only 2 edges left - if (RemoveTwoEdgeFace(inFace, ioAffectedFaces)) - return; // Bail if face no longer exists - - // Restart the loop - recalculate_plane = true; - removed = true; - break; - } - } - else if (neighbour_face == next_neighbour_face) - { - // There are two edges that connect to the same face, we will remove the second one -#ifdef JPH_CONVEX_BUILDER_DEBUG - DrawWireFace(inFace, Color::sYellow); - DrawWireFace(neighbour_face, Color::sRed); - DrawState(); -#endif - - // First merge the neighbours edges - Edge *neighbour_edge = next_edge->mNeighbourEdge; - Edge *next_neighbour_edge = neighbour_edge->mNextEdge; - if (neighbour_face->mFirstEdge == next_neighbour_edge) - neighbour_face->mFirstEdge = neighbour_edge; - neighbour_edge->mNextEdge = next_neighbour_edge->mNextEdge; - neighbour_edge->mNeighbourEdge = edge; - delete next_neighbour_edge; - - // Then merge my own edges - if (inFace->mFirstEdge == next_edge) - inFace->mFirstEdge = edge; - edge->mNextEdge = next_edge->mNextEdge; - edge->mNeighbourEdge = neighbour_edge; - delete next_edge; - -#ifdef JPH_CONVEX_BUILDER_DEBUG - DrawWireFace(inFace, Color::sYellow); - DrawWireFace(neighbour_face, Color::sGreen); - DrawState(); -#endif - - // Check if neighbour has only 2 edges left - if (!RemoveTwoEdgeFace(neighbour_face, ioAffectedFaces)) - { - // No, we need to recalculate its plane - neighbour_face->CalculateNormalAndCentroid(mPositions.data()); - - // Mark neighbour face as affected - sMarkAffected(neighbour_face, ioAffectedFaces); - } - - // Check if inFace now has only 2 edges left - if (RemoveTwoEdgeFace(inFace, ioAffectedFaces)) - return; // Bail if face no longer exists - - // Restart loop - recalculate_plane = true; - removed = true; - break; - } - - // This edge is ok, go to the next edge - edge = next_edge; - neighbour_face = next_neighbour_face; - - } while (edge != inFace->mFirstEdge); - } while (removed); - - // Recalculate plane? - if (recalculate_plane) - inFace->CalculateNormalAndCentroid(mPositions.data()); -} - -bool ConvexHullBuilder::RemoveTwoEdgeFace(Face *inFace, Faces &ioAffectedFaces) const -{ - // Check if this face contains only 2 edges - Edge *edge = inFace->mFirstEdge; - Edge *next_edge = edge->mNextEdge; - JPH_ASSERT(edge != next_edge); // 1 edge faces should not exist - if (next_edge->mNextEdge == edge) - { -#ifdef JPH_CONVEX_BUILDER_DEBUG - DrawWireFace(inFace, Color::sRed); - DrawState(); -#endif - - // Schedule both neighbours for re-checking - Edge *neighbour_edge = edge->mNeighbourEdge; - Face *neighbour_face = neighbour_edge->mFace; - Edge *next_neighbour_edge = next_edge->mNeighbourEdge; - Face *next_neighbour_face = next_neighbour_edge->mFace; - sMarkAffected(neighbour_face, ioAffectedFaces); - sMarkAffected(next_neighbour_face, ioAffectedFaces); - - // Link my neighbours to each other - neighbour_edge->mNeighbourEdge = next_neighbour_edge; - next_neighbour_edge->mNeighbourEdge = neighbour_edge; - - // Unlink my edges - edge->mNeighbourEdge = nullptr; - next_edge->mNeighbourEdge = nullptr; - - // Mark this face as removed - inFace->mRemoved = true; - - return true; - } - - return false; -} - -#ifdef JPH_ENABLE_ASSERTS - -void ConvexHullBuilder::DumpFace(const Face *inFace) const -{ - Trace("f:0x%p", inFace); - - const Edge *e = inFace->mFirstEdge; - do - { - Trace("\te:0x%p { i:%d e:0x%p f:0x%p }", e, e->mStartIdx, e->mNeighbourEdge, e->mNeighbourEdge->mFace); - e = e->mNextEdge; - } while (e != inFace->mFirstEdge); -} - -void ConvexHullBuilder::DumpFaces() const -{ - Trace("Dump Faces:"); - - for (const Face *f : mFaces) - if (!f->mRemoved) - DumpFace(f); -} - -void ConvexHullBuilder::ValidateFace(const Face *inFace) const -{ - if (inFace->mRemoved) - { - const Edge *e = inFace->mFirstEdge; - if (e != nullptr) - do - { - JPH_ASSERT(e->mNeighbourEdge == nullptr); - e = e->mNextEdge; - } while (e != inFace->mFirstEdge); - } - else - { - int edge_count = 0; - - const Edge *e = inFace->mFirstEdge; - do - { - // Count edge - ++edge_count; - - // Validate that adjacent faces are all different - if (mFaces.size() > 2) - for (const Edge *other_edge = e->mNextEdge; other_edge != inFace->mFirstEdge; other_edge = other_edge->mNextEdge) - JPH_ASSERT(e->mNeighbourEdge->mFace != other_edge->mNeighbourEdge->mFace); - - // Assert that the face is correct - JPH_ASSERT(e->mFace == inFace); - - // Assert that we have a neighbour - const Edge *nb_edge = e->mNeighbourEdge; - JPH_ASSERT(nb_edge != nullptr); - if (nb_edge != nullptr) - { - // Assert that our neighbours edge points to us - JPH_ASSERT(nb_edge->mNeighbourEdge == e); - - // Assert that it belongs to a different face - JPH_ASSERT(nb_edge->mFace != inFace); - - // Assert that the next edge of the neighbour points to the same vertex as this edge's vertex - JPH_ASSERT(nb_edge->mNextEdge->mStartIdx == e->mStartIdx); - - // Assert that my next edge points to the same vertex as my neighbours vertex - JPH_ASSERT(e->mNextEdge->mStartIdx == nb_edge->mStartIdx); - } - e = e->mNextEdge; - } while (e != inFace->mFirstEdge); - - // Assert that we have 3 or more edges - JPH_ASSERT(edge_count >= 3); - } -} - -void ConvexHullBuilder::ValidateFaces() const -{ - for (const Face *f : mFaces) - ValidateFace(f); -} - -#endif // JPH_ENABLE_ASSERTS - -void ConvexHullBuilder::GetCenterOfMassAndVolume(Vec3 &outCenterOfMass, float &outVolume) const -{ - // Fourth point is the average of all face centroids - Vec3 v4 = Vec3::sZero(); - for (const Face *f : mFaces) - v4 += f->mCentroid; - v4 /= float(mFaces.size()); - - // Calculate mass and center of mass of this convex hull by summing all tetrahedrons - outVolume = 0.0f; - outCenterOfMass = Vec3::sZero(); - for (const Face *f : mFaces) - { - // Get the first vertex that we'll use to create a triangle fan - Edge *e = f->mFirstEdge; - Vec3 v1 = mPositions[e->mStartIdx]; - - // Get the second vertex - e = e->mNextEdge; - Vec3 v2 = mPositions[e->mStartIdx]; - - for (e = e->mNextEdge; e != f->mFirstEdge; e = e->mNextEdge) - { - // Fetch the last point of the triangle - Vec3 v3 = mPositions[e->mStartIdx]; - - // Calculate center of mass and mass of this tetrahedron, - // see: https://en.wikipedia.org/wiki/Tetrahedron#Volume - float volume_tetrahedron = (v1 - v4).Dot((v2 - v4).Cross(v3 - v4)); // Needs to be divided by 6, postpone this until the end of the loop - Vec3 center_of_mass_tetrahedron = v1 + v2 + v3 + v4; // Needs to be divided by 4, postpone this until the end of the loop - - // Accumulate results - outVolume += volume_tetrahedron; - outCenterOfMass += volume_tetrahedron * center_of_mass_tetrahedron; - - // Update v2 for next triangle - v2 = v3; - } - } - - // Calculate center of mass, fall back to average point in case there is no volume (everything is on a plane in this case) - if (outVolume > FLT_EPSILON) - outCenterOfMass /= 4.0f * outVolume; - else - outCenterOfMass = v4; - - outVolume /= 6.0f; -} - -void ConvexHullBuilder::DetermineMaxError(Face *&outFaceWithMaxError, float &outMaxError, int &outMaxErrorPositionIdx, float &outCoplanarDistance) const -{ - outCoplanarDistance = DetermineCoplanarDistance(); - - // This measures the distance from a polygon to the furthest point outside of the hull - float max_error = 0.0f; - Face *max_error_face = nullptr; - int max_error_point = -1; - - for (int i = 0; i < (int)mPositions.size(); ++i) - { - Vec3 v = mPositions[i]; - - // This measures the closest edge from all faces to point v - // Note that we take the min of all faces since there may be multiple near coplanar faces so if we were to test this per face - // we may find that a point is outside of a polygon and mark it as an error, while it is actually inside a nearly coplanar - // polygon. - float min_edge_dist_sq = FLT_MAX; - Face *min_edge_dist_face = nullptr; - - for (Face *f : mFaces) - { - // Check if point is on or in front of plane - float normal_len = f->mNormal.Length(); - JPH_ASSERT(normal_len > 0.0f); - float plane_dist = f->mNormal.Dot(v - f->mCentroid) / normal_len; - if (plane_dist > -outCoplanarDistance) - { - // Check distance to the edges of this face - float edge_dist_sq = GetDistanceToEdgeSq(v, f); - if (edge_dist_sq < min_edge_dist_sq) - { - min_edge_dist_sq = edge_dist_sq; - min_edge_dist_face = f; - } - - // If the point is inside the polygon and the point is in front of the plane, measure the distance - if (edge_dist_sq == 0.0f && plane_dist > max_error) - { - max_error = plane_dist; - max_error_face = f; - max_error_point = i; - } - } - } - - // If the minimum distance to an edge is further than our current max error, we use that as max error - float min_edge_dist = sqrt(min_edge_dist_sq); - if (min_edge_dist_face != nullptr && min_edge_dist > max_error) - { - max_error = min_edge_dist; - max_error_face = min_edge_dist_face; - max_error_point = i; - } - } - - outFaceWithMaxError = max_error_face; - outMaxError = max_error; - outMaxErrorPositionIdx = max_error_point; -} - -#ifdef JPH_CONVEX_BUILDER_DEBUG - -void ConvexHullBuilder::DrawState(bool inDrawConflictList) const -{ - // Draw origin - DebugRenderer::sInstance->DrawMarker(cDrawScale * mOffset, Color::sRed, 0.2f); - - int face_idx = 0; - - // Draw faces - for (const Face *f : mFaces) - if (!f->mRemoved) - { - Color iteration_color = Color::sGetDistinctColor(f->mIteration); - Color face_color = Color::sGetDistinctColor(face_idx++); - - // First point - const Edge *e = f->mFirstEdge; - RVec3 p1 = cDrawScale * (mOffset + mPositions[e->mStartIdx]); - - // Second point - e = e->mNextEdge; - RVec3 p2 = cDrawScale * (mOffset + mPositions[e->mStartIdx]); - - // First line - DebugRenderer::sInstance->DrawLine(p1, p2, Color::sGrey); - - do - { - // Third point - e = e->mNextEdge; - RVec3 p3 = cDrawScale * (mOffset + mPositions[e->mStartIdx]); - - DebugRenderer::sInstance->DrawTriangle(p1, p2, p3, iteration_color); - - DebugRenderer::sInstance->DrawLine(p2, p3, Color::sGrey); - - p2 = p3; - } - while (e != f->mFirstEdge); - - // Draw normal - RVec3 centroid = cDrawScale * (mOffset + f->mCentroid); - DebugRenderer::sInstance->DrawArrow(centroid, centroid + f->mNormal.NormalizedOr(Vec3::sZero()), face_color, 0.01f); - - // Draw conflict list - if (inDrawConflictList) - for (int idx : f->mConflictList) - DebugRenderer::sInstance->DrawMarker(cDrawScale * (mOffset + mPositions[idx]), face_color, 0.05f); - } - - // Offset to the right - mOffset += mDelta; -} - -void ConvexHullBuilder::DrawWireFace(const Face *inFace, ColorArg inColor) const -{ - const Edge *e = inFace->mFirstEdge; - RVec3 prev = cDrawScale * (mOffset + mPositions[e->mStartIdx]); - do - { - const Edge *next = e->mNextEdge; - RVec3 cur = cDrawScale * (mOffset + mPositions[next->mStartIdx]); - DebugRenderer::sInstance->DrawArrow(prev, cur, inColor, 0.01f); - DebugRenderer::sInstance->DrawText3D(prev, ConvertToString(e->mStartIdx), inColor); - e = next; - prev = cur; - } while (e != inFace->mFirstEdge); -} - -void ConvexHullBuilder::DrawEdge(const Edge *inEdge, ColorArg inColor) const -{ - RVec3 p1 = cDrawScale * (mOffset + mPositions[inEdge->mStartIdx]); - RVec3 p2 = cDrawScale * (mOffset + mPositions[inEdge->mNextEdge->mStartIdx]); - DebugRenderer::sInstance->DrawArrow(p1, p2, inColor, 0.01f); -} - -#endif // JPH_CONVEX_BUILDER_DEBUG - -#ifdef JPH_CONVEX_BUILDER_DUMP_SHAPE - -void ConvexHullBuilder::DumpShape() const -{ - static atomic sShapeNo = 1; - int shape_no = sShapeNo++; - - std::ofstream f; - f.open(StringFormat("dumped_shape%d.cpp", shape_no).c_str(), std::ofstream::out | std::ofstream::trunc); - if (!f.is_open()) - return; - - f << "{\n"; - for (Vec3 v : mPositions) - f << StringFormat("\tVec3(%.9gf, %.9gf, %.9gf),\n", (double)v.GetX(), (double)v.GetY(), (double)v.GetZ()); - f << "},\n"; -} - -#endif // JPH_CONVEX_BUILDER_DUMP_SHAPE - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/ConvexHullBuilder.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/ConvexHullBuilder.h deleted file mode 100644 index 7a6aea8caa5..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/ConvexHullBuilder.h +++ /dev/null @@ -1,276 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -//#define JPH_CONVEX_BUILDER_DEBUG -//#define JPH_CONVEX_BUILDER_DUMP_SHAPE - -#ifdef JPH_CONVEX_BUILDER_DEBUG - #include -#endif - -#include -#include - -JPH_NAMESPACE_BEGIN - -/// A convex hull builder that tries to create hulls as accurately as possible. Used for offline processing. -class JPH_EXPORT ConvexHullBuilder : public NonCopyable -{ -public: - // Forward declare - class Face; - - /// Class that holds the information of an edge - class Edge : public NonCopyable - { - public: - JPH_OVERRIDE_NEW_DELETE - - /// Constructor - Edge(Face *inFace, int inStartIdx) : mFace(inFace), mStartIdx(inStartIdx) { } - - /// Get the previous edge - inline Edge * GetPreviousEdge() - { - Edge *prev_edge = this; - while (prev_edge->mNextEdge != this) - prev_edge = prev_edge->mNextEdge; - return prev_edge; - } - - Face * mFace; ///< Face that this edge belongs to - Edge * mNextEdge = nullptr; ///< Next edge of this face - Edge * mNeighbourEdge = nullptr; ///< Edge that this edge is connected to - int mStartIdx; ///< Vertex index in mPositions that indicates the start vertex of this edge - }; - - using ConflictList = Array; - - /// Class that holds the information of one face - class Face : public NonCopyable - { - public: - JPH_OVERRIDE_NEW_DELETE - - /// Destructor - ~Face(); - - /// Initialize a face with three indices - void Initialize(int inIdx0, int inIdx1, int inIdx2, const Vec3 *inPositions); - - /// Calculates the centroid and normal for this face - void CalculateNormalAndCentroid(const Vec3 *inPositions); - - /// Check if face inFace is facing inPosition - inline bool IsFacing(Vec3Arg inPosition) const - { - JPH_ASSERT(!mRemoved); - return mNormal.Dot(inPosition - mCentroid) > 0.0f; - } - - Vec3 mNormal; ///< Normal of this face, length is 2 times area of face - Vec3 mCentroid; ///< Center of the face - ConflictList mConflictList; ///< Positions associated with this edge (that are closest to this edge). The last position in the list is the point that is furthest away from the face. - Edge * mFirstEdge = nullptr; ///< First edge of this face - float mFurthestPointDistanceSq = 0.0f; ///< Squared distance of furthest point from the conflict list to the face - bool mRemoved = false; ///< Flag that indicates that face has been removed (face will be freed later) -#ifdef JPH_CONVEX_BUILDER_DEBUG - int mIteration; ///< Iteration that this face was created -#endif - }; - - // Typedefs - using Positions = Array; - using Faces = Array; - - /// Constructor - explicit ConvexHullBuilder(const Positions &inPositions); - - /// Destructor - ~ConvexHullBuilder() { FreeFaces(); } - - /// Result enum that indicates how the hull got created - enum class EResult - { - Success, ///< Hull building finished successfully - MaxVerticesReached, ///< Hull building finished successfully, but the desired accuracy was not reached because the max vertices limit was reached - TooFewPoints, ///< Too few points to create a hull - TooFewFaces, ///< Too few faces in the created hull (signifies precision errors during building) - Degenerate, ///< Degenerate hull detected - }; - - /// Takes all positions as provided by the constructor and use them to build a hull - /// Any points that are closer to the hull than inTolerance will be discarded - /// @param inMaxVertices Max vertices to allow in the hull. Specify INT_MAX if there is no limit. - /// @param inTolerance Max distance that a point is allowed to be outside of the hull - /// @param outError Error message when building fails - /// @return Status code that reports if the hull was created or not - EResult Initialize(int inMaxVertices, float inTolerance, const char *&outError); - - /// Returns the amount of vertices that are currently used by the hull - int GetNumVerticesUsed() const; - - /// Returns true if the hull contains a polygon with inIndices (counter clockwise indices in mPositions) - bool ContainsFace(const Array &inIndices) const; - - /// Calculate the center of mass and the volume of the current convex hull - void GetCenterOfMassAndVolume(Vec3 &outCenterOfMass, float &outVolume) const; - - /// Determines the point that is furthest outside of the hull and reports how far it is outside of the hull (which indicates a failure during hull building) - /// @param outFaceWithMaxError The face that caused the error - /// @param outMaxError The maximum distance of a point to the hull - /// @param outMaxErrorPositionIdx The index of the point that had this distance - /// @param outCoplanarDistance Points that are less than this distance from the hull are considered on the hull. This should be used as a lowerbound for the allowed error. - void DetermineMaxError(Face *&outFaceWithMaxError, float &outMaxError, int &outMaxErrorPositionIdx, float &outCoplanarDistance) const; - - /// Access to the created faces. Memory is owned by the convex hull builder. - const Faces & GetFaces() const { return mFaces; } - -private: - /// Minimal square area of a triangle (used for merging and checking if a triangle is degenerate) - static constexpr float cMinTriangleAreaSq = 1.0e-12f; - -#ifdef JPH_CONVEX_BUILDER_DEBUG - /// Factor to scale convex hull when debug drawing the construction process - static constexpr Real cDrawScale = 10; -#endif - - /// Class that holds an edge including start and end index - class FullEdge - { - public: - Edge * mNeighbourEdge; ///< Edge that this edge is connected to - int mStartIdx; ///< Vertex index in mPositions that indicates the start vertex of this edge - int mEndIdx; ///< Vertex index in mPosition that indicates the end vertex of this edge - }; - - // Private typedefs - using FullEdges = Array; - - // Determine a suitable tolerance for detecting that points are coplanar - float DetermineCoplanarDistance() const; - - /// Find the face for which inPoint is furthest to the front - /// @param inPoint Point to test - /// @param inFaces List of faces to test - /// @param outFace Returns the best face - /// @param outDistSq Returns the squared distance how much inPoint is in front of the plane of the face - void GetFaceForPoint(Vec3Arg inPoint, const Faces &inFaces, Face *&outFace, float &outDistSq) const; - - /// @brief Calculates the distance between inPoint and inFace - /// @param inFace Face to test - /// @param inPoint Point to test - /// @return If the projection of the point on the plane is interior to the face 0, otherwise the squared distance to the closest edge - float GetDistanceToEdgeSq(Vec3Arg inPoint, const Face *inFace) const; - - /// Assigns a position to one of the supplied faces based on which face is closest. - /// @param inPositionIdx Index of the position to add - /// @param inFaces List of faces to consider - /// @param inToleranceSq Tolerance of the hull, if the point is closer to the face than this, we ignore it - /// @return True if point was assigned, false if it was discarded or added to the coplanar list - bool AssignPointToFace(int inPositionIdx, const Faces &inFaces, float inToleranceSq); - - /// Add a new point to the convex hull - void AddPoint(Face *inFacingFace, int inIdx, float inToleranceSq, Faces &outNewFaces); - - /// Remove all faces that have been marked 'removed' from mFaces list - void GarbageCollectFaces(); - - /// Create a new face - Face * CreateFace(); - - /// Create a new triangle - Face * CreateTriangle(int inIdx1, int inIdx2, int inIdx3); - - /// Delete a face (checking that it is not connected to any other faces) - void FreeFace(Face *inFace); - - /// Release all faces and edges - void FreeFaces(); - - /// Link face edge to other face edge - static void sLinkFace(Edge *inEdge1, Edge *inEdge2); - - /// Unlink this face from all of its neighbours - static void sUnlinkFace(Face *inFace); - - /// Given one face that faces inVertex, find the edges of the faces that are not facing inVertex. - /// Will flag all those faces for removal. - void FindEdge(Face *inFacingFace, Vec3Arg inVertex, FullEdges &outEdges) const; - - /// Merges the two faces that share inEdge into the face inEdge->mFace - void MergeFaces(Edge *inEdge); - - /// Merges inFace with a neighbour if it is degenerate (a sliver) - void MergeDegenerateFace(Face *inFace, Faces &ioAffectedFaces); - - /// Merges any coplanar as well as neighbours that form a non-convex edge into inFace. - /// Faces are considered coplanar if the distance^2 of the other face's centroid is smaller than inToleranceSq. - void MergeCoplanarOrConcaveFaces(Face *inFace, float inToleranceSq, Faces &ioAffectedFaces); - - /// Mark face as affected if it is not already in the list - static void sMarkAffected(Face *inFace, Faces &ioAffectedFaces); - - /// Removes all invalid edges. - /// 1. Merges inFace with faces that share two edges with it since this means inFace or the other face cannot be convex or the edge is colinear. - /// 2. Removes edges that are interior to inFace (that have inFace on both sides) - /// Any faces that need to be checked for validity will be added to ioAffectedFaces. - void RemoveInvalidEdges(Face *inFace, Faces &ioAffectedFaces); - - /// Removes inFace if it consists of only 2 edges, linking its neighbouring faces together - /// Any faces that need to be checked for validity will be added to ioAffectedFaces. - /// @return True if face was removed. - bool RemoveTwoEdgeFace(Face *inFace, Faces &ioAffectedFaces) const; - -#ifdef JPH_ENABLE_ASSERTS - /// Dumps the text representation of a face to the TTY - void DumpFace(const Face *inFace) const; - - /// Dumps the text representation of all faces to the TTY - void DumpFaces() const; - - /// Check consistency of 1 face - void ValidateFace(const Face *inFace) const; - - /// Check consistency of all faces - void ValidateFaces() const; -#endif - -#ifdef JPH_CONVEX_BUILDER_DEBUG - /// Draw state of algorithm - void DrawState(bool inDrawConflictList = false) const; - - /// Draw a face for debugging purposes - void DrawWireFace(const Face *inFace, ColorArg inColor) const; - - /// Draw an edge for debugging purposes - void DrawEdge(const Edge *inEdge, ColorArg inColor) const; -#endif - -#ifdef JPH_CONVEX_BUILDER_DUMP_SHAPE - void DumpShape() const; -#endif - - const Positions & mPositions; ///< List of positions (some of them are part of the hull) - Faces mFaces; ///< List of faces that are part of the hull (if !mRemoved) - - struct Coplanar - { - int mPositionIdx; ///< Index in mPositions - float mDistanceSq; ///< Distance to the edge of closest face (should be > 0) - }; - using CoplanarList = Array; - - CoplanarList mCoplanarList; ///< List of positions that are coplanar to a face but outside of the face, these are added to the hull at the end - -#ifdef JPH_CONVEX_BUILDER_DEBUG - int mIteration; ///< Number of iterations we've had so far (for debug purposes) - mutable RVec3 mOffset; ///< Offset to use for state drawing - Vec3 mDelta; ///< Delta offset between next states -#endif -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/ConvexHullBuilder2D.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/ConvexHullBuilder2D.cpp deleted file mode 100644 index 968e6b46bb5..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/ConvexHullBuilder2D.cpp +++ /dev/null @@ -1,336 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include - -#ifdef JPH_CONVEX_BUILDER_2D_DEBUG - #include -#endif - -JPH_NAMESPACE_BEGIN - -void ConvexHullBuilder2D::Edge::CalculateNormalAndCenter(const Vec3 *inPositions) -{ - Vec3 p1 = inPositions[mStartIdx]; - Vec3 p2 = inPositions[mNextEdge->mStartIdx]; - - // Center of edge - mCenter = 0.5f * (p1 + p2); - - // Create outward pointing normal. - // We have two choices for the normal (which satisfies normal . edge = 0): - // normal1 = (-edge.y, edge.x, 0) - // normal2 = (edge.y, -edge.x, 0) - // We want (normal x edge).z > 0 so that the normal points out of the polygon. Only normal2 satisfies this condition. - Vec3 edge = p2 - p1; - mNormal = Vec3(edge.GetY(), -edge.GetX(), 0); -} - -ConvexHullBuilder2D::ConvexHullBuilder2D(const Positions &inPositions) : - mPositions(inPositions) -{ -#ifdef JPH_CONVEX_BUILDER_2D_DEBUG - // Center the drawing of the first hull around the origin and calculate the delta offset between states - mOffset = RVec3::sZero(); - if (mPositions.empty()) - { - // No hull will be generated - mDelta = Vec3::sZero(); - } - else - { - Vec3 maxv = Vec3::sReplicate(-FLT_MAX), minv = Vec3::sReplicate(FLT_MAX); - for (Vec3 v : mPositions) - { - minv = Vec3::sMin(minv, v); - maxv = Vec3::sMax(maxv, v); - mOffset -= v; - } - mOffset /= Real(mPositions.size()); - mDelta = Vec3((maxv - minv).GetX() + 0.5f, 0, 0); - mOffset += mDelta; // Don't start at origin, we're already drawing the final hull there - } -#endif -} - -ConvexHullBuilder2D::~ConvexHullBuilder2D() -{ - FreeEdges(); -} - -void ConvexHullBuilder2D::FreeEdges() -{ - if (mFirstEdge == nullptr) - return; - - Edge *edge = mFirstEdge; - do - { - Edge *next = edge->mNextEdge; - delete edge; - edge = next; - } while (edge != mFirstEdge); - - mFirstEdge = nullptr; - mNumEdges = 0; -} - -#ifdef JPH_ENABLE_ASSERTS - -void ConvexHullBuilder2D::ValidateEdges() const -{ - if (mFirstEdge == nullptr) - { - JPH_ASSERT(mNumEdges == 0); - return; - } - - int count = 0; - - Edge *edge = mFirstEdge; - do - { - // Validate connectivity - JPH_ASSERT(edge->mNextEdge->mPrevEdge == edge); - JPH_ASSERT(edge->mPrevEdge->mNextEdge == edge); - - ++count; - edge = edge->mNextEdge; - } while (edge != mFirstEdge); - - // Validate that count matches - JPH_ASSERT(count == mNumEdges); -} - -#endif // JPH_ENABLE_ASSERTS - -void ConvexHullBuilder2D::AssignPointToEdge(int inPositionIdx, const Array &inEdges) const -{ - Vec3 point = mPositions[inPositionIdx]; - - Edge *best_edge = nullptr; - float best_dist_sq = 0.0f; - - // Test against all edges - for (Edge *edge : inEdges) - { - // Determine distance to edge - float dot = edge->mNormal.Dot(point - edge->mCenter); - if (dot > 0.0f) - { - float dist_sq = dot * dot / edge->mNormal.LengthSq(); - if (dist_sq > best_dist_sq) - { - best_edge = edge; - best_dist_sq = dist_sq; - } - } - } - - // If this point is in front of the edge, add it to the conflict list - if (best_edge != nullptr) - { - if (best_dist_sq > best_edge->mFurthestPointDistanceSq) - { - // This point is further away than any others, update the distance and add point as last point - best_edge->mFurthestPointDistanceSq = best_dist_sq; - best_edge->mConflictList.push_back(inPositionIdx); - } - else - { - // Not the furthest point, add it as the before last point - best_edge->mConflictList.push_back(best_edge->mConflictList.back()); - best_edge->mConflictList[best_edge->mConflictList.size() - 2] = inPositionIdx; - } - } -} - -ConvexHullBuilder2D::EResult ConvexHullBuilder2D::Initialize(int inIdx1, int inIdx2, int inIdx3, int inMaxVertices, float inTolerance, Edges &outEdges) -{ - // Clear any leftovers - FreeEdges(); - outEdges.clear(); - - // Reset flag - EResult result = EResult::Success; - - // Determine a suitable tolerance for detecting that points are colinear - // Formula as per: Implementing Quickhull - Dirk Gregorius. - Vec3 vmax = Vec3::sZero(); - for (Vec3 v : mPositions) - vmax = Vec3::sMax(vmax, v.Abs()); - float colinear_tolerance_sq = Square(2.0f * FLT_EPSILON * (vmax.GetX() + vmax.GetY())); - - // Increase desired tolerance if accuracy doesn't allow it - float tolerance_sq = max(colinear_tolerance_sq, Square(inTolerance)); - - // Start with the initial indices in counter clockwise order - float z = (mPositions[inIdx2] - mPositions[inIdx1]).Cross(mPositions[inIdx3] - mPositions[inIdx1]).GetZ(); - if (z < 0.0f) - swap(inIdx1, inIdx2); - - // Create and link edges - Edge *e1 = new Edge(inIdx1); - Edge *e2 = new Edge(inIdx2); - Edge *e3 = new Edge(inIdx3); - e1->mNextEdge = e2; - e1->mPrevEdge = e3; - e2->mNextEdge = e3; - e2->mPrevEdge = e1; - e3->mNextEdge = e1; - e3->mPrevEdge = e2; - mFirstEdge = e1; - mNumEdges = 3; - - // Build the initial conflict lists - Array edges { e1, e2, e3 }; - for (Edge *edge : edges) - edge->CalculateNormalAndCenter(mPositions.data()); - for (int idx = 0; idx < (int)mPositions.size(); ++idx) - if (idx != inIdx1 && idx != inIdx2 && idx != inIdx3) - AssignPointToEdge(idx, edges); - - JPH_IF_ENABLE_ASSERTS(ValidateEdges();) -#ifdef JPH_CONVEX_BUILDER_2D_DEBUG - DrawState(); -#endif - - // Add the remaining points to the hull - for (;;) - { - // Check if we've reached the max amount of vertices that are allowed - if (mNumEdges >= inMaxVertices) - { - result = EResult::MaxVerticesReached; - break; - } - - // Find the edge with the furthest point on it - Edge *edge_with_furthest_point = nullptr; - float furthest_dist_sq = 0.0f; - Edge *edge = mFirstEdge; - do - { - if (edge->mFurthestPointDistanceSq > furthest_dist_sq) - { - furthest_dist_sq = edge->mFurthestPointDistanceSq; - edge_with_furthest_point = edge; - } - edge = edge->mNextEdge; - } while (edge != mFirstEdge); - - // If there is none closer than our tolerance value, we're done - if (edge_with_furthest_point == nullptr || furthest_dist_sq < tolerance_sq) - break; - - // Take the furthest point - int furthest_point_idx = edge_with_furthest_point->mConflictList.back(); - edge_with_furthest_point->mConflictList.pop_back(); - Vec3 furthest_point = mPositions[furthest_point_idx]; - - // Find the horizon of edges that need to be removed - Edge *first_edge = edge_with_furthest_point; - do - { - Edge *prev = first_edge->mPrevEdge; - if (!prev->IsFacing(furthest_point)) - break; - first_edge = prev; - } while (first_edge != edge_with_furthest_point); - - Edge *last_edge = edge_with_furthest_point; - do - { - Edge *next = last_edge->mNextEdge; - if (!next->IsFacing(furthest_point)) - break; - last_edge = next; - } while (last_edge != edge_with_furthest_point); - - // Create new edges - e1 = new Edge(first_edge->mStartIdx); - e2 = new Edge(furthest_point_idx); - e1->mNextEdge = e2; - e1->mPrevEdge = first_edge->mPrevEdge; - e2->mPrevEdge = e1; - e2->mNextEdge = last_edge->mNextEdge; - e1->mPrevEdge->mNextEdge = e1; - e2->mNextEdge->mPrevEdge = e2; - mFirstEdge = e1; // We could delete mFirstEdge so just update it to the newly created edge - mNumEdges += 2; - - // Calculate normals - Array new_edges { e1, e2 }; - for (Edge *new_edge : new_edges) - new_edge->CalculateNormalAndCenter(mPositions.data()); - - // Delete the old edges - for (;;) - { - Edge *next = first_edge->mNextEdge; - - // Redistribute points in conflict list - for (int idx : first_edge->mConflictList) - AssignPointToEdge(idx, new_edges); - - // Delete the old edge - delete first_edge; - --mNumEdges; - - if (first_edge == last_edge) - break; - first_edge = next; - } - - JPH_IF_ENABLE_ASSERTS(ValidateEdges();) - #ifdef JPH_CONVEX_BUILDER_2D_DEBUG - DrawState(); - #endif - } - - // Convert the edge list to a list of indices - outEdges.reserve(mNumEdges); - Edge *edge = mFirstEdge; - do - { - outEdges.push_back(edge->mStartIdx); - edge = edge->mNextEdge; - } while (edge != mFirstEdge); - - return result; -} - -#ifdef JPH_CONVEX_BUILDER_2D_DEBUG - -void ConvexHullBuilder2D::DrawState() -{ - int color_idx = 0; - - const Edge *edge = mFirstEdge; - do - { - const Edge *next = edge->mNextEdge; - - // Get unique color per edge - Color color = Color::sGetDistinctColor(color_idx++); - - // Draw edge and normal - DebugRenderer::sInstance->DrawArrow(cDrawScale * (mOffset + mPositions[edge->mStartIdx]), cDrawScale * (mOffset + mPositions[next->mStartIdx]), color, 0.1f); - DebugRenderer::sInstance->DrawArrow(cDrawScale * (mOffset + edge->mCenter), cDrawScale * (mOffset + edge->mCenter) + edge->mNormal.NormalizedOr(Vec3::sZero()), Color::sGreen, 0.1f); - - // Draw points that belong to this edge in the same color - for (int idx : edge->mConflictList) - DebugRenderer::sInstance->DrawMarker(cDrawScale * (mOffset + mPositions[idx]), color, 0.05f); - - edge = next; - } while (edge != mFirstEdge); - - mOffset += mDelta; -} - -#endif - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/ConvexHullBuilder2D.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/ConvexHullBuilder2D.h deleted file mode 100644 index ff06a3403e0..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/ConvexHullBuilder2D.h +++ /dev/null @@ -1,105 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -//#define JPH_CONVEX_BUILDER_2D_DEBUG - -JPH_NAMESPACE_BEGIN - -/// A convex hull builder that tries to create 2D hulls as accurately as possible. Used for offline processing. -class JPH_EXPORT ConvexHullBuilder2D : public NonCopyable -{ -public: - using Positions = Array; - using Edges = Array; - - /// Constructor - /// @param inPositions Positions used to make the hull. Uses X and Y component of Vec3 only! - explicit ConvexHullBuilder2D(const Positions &inPositions); - - /// Destructor - ~ConvexHullBuilder2D(); - - /// Result enum that indicates how the hull got created - enum class EResult - { - Success, ///< Hull building finished successfully - MaxVerticesReached, ///< Hull building finished successfully, but the desired accuracy was not reached because the max vertices limit was reached - }; - - /// Takes all positions as provided by the constructor and use them to build a hull - /// Any points that are closer to the hull than inTolerance will be discarded - /// @param inIdx1 , inIdx2 , inIdx3 The indices to use as initial hull (in any order) - /// @param inMaxVertices Max vertices to allow in the hull. Specify INT_MAX if there is no limit. - /// @param inTolerance Max distance that a point is allowed to be outside of the hull - /// @param outEdges On success this will contain the list of indices that form the hull (counter clockwise) - /// @return Status code that reports if the hull was created or not - EResult Initialize(int inIdx1, int inIdx2, int inIdx3, int inMaxVertices, float inTolerance, Edges &outEdges); - -private: -#ifdef JPH_CONVEX_BUILDER_2D_DEBUG - /// Factor to scale convex hull when debug drawing the construction process - static constexpr Real cDrawScale = 10; -#endif - - class Edge; - - /// Frees all edges - void FreeEdges(); - - /// Assigns a position to one of the supplied edges based on which edge is closest. - /// @param inPositionIdx Index of the position to add - /// @param inEdges List of edges to consider - void AssignPointToEdge(int inPositionIdx, const Array &inEdges) const; - -#ifdef JPH_CONVEX_BUILDER_2D_DEBUG - /// Draw state of algorithm - void DrawState(); -#endif - -#ifdef JPH_ENABLE_ASSERTS - /// Validate that the edge structure is intact - void ValidateEdges() const; -#endif - - using ConflictList = Array; - - /// Linked list of edges - class Edge - { - public: - JPH_OVERRIDE_NEW_DELETE - - /// Constructor - explicit Edge(int inStartIdx) : mStartIdx(inStartIdx) { } - - /// Calculate the center of the edge and the edge normal - void CalculateNormalAndCenter(const Vec3 *inPositions); - - /// Check if this edge is facing inPosition - inline bool IsFacing(Vec3Arg inPosition) const { return mNormal.Dot(inPosition - mCenter) > 0.0f; } - - Vec3 mNormal; ///< Normal of the edge (not normalized) - Vec3 mCenter; ///< Center of the edge - ConflictList mConflictList; ///< Positions associated with this edge (that are closest to this edge). Last entry is the one furthest away from the edge, remainder is unsorted. - Edge * mPrevEdge = nullptr; ///< Previous edge in circular list - Edge * mNextEdge = nullptr; ///< Next edge in circular list - int mStartIdx; ///< Position index of start of this edge - float mFurthestPointDistanceSq = 0.0f; ///< Squared distance of furthest point from the conflict list to the edge - }; - - const Positions & mPositions; ///< List of positions (some of them are part of the hull) - Edge * mFirstEdge = nullptr; ///< First edge of the hull - int mNumEdges = 0; ///< Number of edges in hull - -#ifdef JPH_CONVEX_BUILDER_2D_DEBUG - RVec3 mOffset; ///< Offset to use for state drawing - Vec3 mDelta; ///< Delta offset between next states -#endif -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/ConvexSupport.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/ConvexSupport.h deleted file mode 100644 index 3ba2c935db2..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/ConvexSupport.h +++ /dev/null @@ -1,188 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -/// Helper functions to get the support point for a convex object -/// Structure that transforms a convex object (supports only uniform scaling) -template -struct TransformedConvexObject -{ - /// Create transformed convex object. - TransformedConvexObject(Mat44Arg inTransform, const ConvexObject &inObject) : - mTransform(inTransform), - mObject(inObject) - { - } - - /// Calculate the support vector for this convex shape. - Vec3 GetSupport(Vec3Arg inDirection) const - { - return mTransform * mObject.GetSupport(mTransform.Multiply3x3Transposed(inDirection)); - } - - /// Get the vertices of the face that faces inDirection the most - template - void GetSupportingFace(Vec3Arg inDirection, VERTEX_ARRAY &outVertices) const - { - mObject.GetSupportingFace(mTransform.Multiply3x3Transposed(inDirection), outVertices); - - for (Vec3 &v : outVertices) - v = mTransform * v; - } - - Mat44 mTransform; - const ConvexObject & mObject; -}; - -/// Structure that adds a convex radius -template -struct AddConvexRadius -{ - AddConvexRadius(const ConvexObject &inObject, float inRadius) : - mObject(inObject), - mRadius(inRadius) - { - } - - /// Calculate the support vector for this convex shape. - Vec3 GetSupport(Vec3Arg inDirection) const - { - float length = inDirection.Length(); - return length > 0.0f ? mObject.GetSupport(inDirection) + (mRadius / length) * inDirection : mObject.GetSupport(inDirection); - } - - const ConvexObject & mObject; - float mRadius; -}; - -/// Structure that performs a Minkowski difference A - B -template -struct MinkowskiDifference -{ - MinkowskiDifference(const ConvexObjectA &inObjectA, const ConvexObjectB &inObjectB) : - mObjectA(inObjectA), - mObjectB(inObjectB) - { - } - - /// Calculate the support vector for this convex shape. - Vec3 GetSupport(Vec3Arg inDirection) const - { - return mObjectA.GetSupport(inDirection) - mObjectB.GetSupport(-inDirection); - } - - const ConvexObjectA & mObjectA; - const ConvexObjectB & mObjectB; -}; - -/// Class that wraps a point so that it can be used with convex collision detection -struct PointConvexSupport -{ - /// Calculate the support vector for this convex shape. - Vec3 GetSupport([[maybe_unused]] Vec3Arg inDirection) const - { - return mPoint; - } - - Vec3 mPoint; -}; - -/// Class that wraps a triangle so that it can used with convex collision detection -struct TriangleConvexSupport -{ - /// Constructor - TriangleConvexSupport(Vec3Arg inV1, Vec3Arg inV2, Vec3Arg inV3) : - mV1(inV1), - mV2(inV2), - mV3(inV3) - { - } - - /// Calculate the support vector for this convex shape. - Vec3 GetSupport(Vec3Arg inDirection) const - { - // Project vertices on inDirection - float d1 = mV1.Dot(inDirection); - float d2 = mV2.Dot(inDirection); - float d3 = mV3.Dot(inDirection); - - // Return vertex with biggest projection - if (d1 > d2) - { - if (d1 > d3) - return mV1; - else - return mV3; - } - else - { - if (d2 > d3) - return mV2; - else - return mV3; - } - } - - /// Get the vertices of the face that faces inDirection the most - template - void GetSupportingFace([[maybe_unused]] Vec3Arg inDirection, VERTEX_ARRAY &outVertices) const - { - outVertices.push_back(mV1); - outVertices.push_back(mV2); - outVertices.push_back(mV3); - } - - /// The three vertices of the triangle - Vec3 mV1; - Vec3 mV2; - Vec3 mV3; -}; - -/// Class that wraps a polygon so that it can used with convex collision detection -template -struct PolygonConvexSupport -{ - /// Constructor - explicit PolygonConvexSupport(const VERTEX_ARRAY &inVertices) : - mVertices(inVertices) - { - } - - /// Calculate the support vector for this convex shape. - Vec3 GetSupport(Vec3Arg inDirection) const - { - Vec3 support_point = mVertices[0]; - float best_dot = mVertices[0].Dot(inDirection); - - for (typename VERTEX_ARRAY::const_iterator v = mVertices.begin() + 1; v < mVertices.end(); ++v) - { - float dot = v->Dot(inDirection); - if (dot > best_dot) - { - best_dot = dot; - support_point = *v; - } - } - - return support_point; - } - - /// Get the vertices of the face that faces inDirection the most - template - void GetSupportingFace([[maybe_unused]] Vec3Arg inDirection, VERTEX_ARRAY_ARG &outVertices) const - { - for (Vec3 v : mVertices) - outVertices.push_back(v); - } - - /// The vertices of the polygon - const VERTEX_ARRAY & mVertices; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/EPAConvexHullBuilder.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/EPAConvexHullBuilder.h deleted file mode 100644 index 9f1c5a8e48c..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/EPAConvexHullBuilder.h +++ /dev/null @@ -1,844 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -// Define to validate the integrity of the hull structure -//#define JPH_EPA_CONVEX_BUILDER_VALIDATE - -// Define to draw the building of the hull for debugging purposes -//#define JPH_EPA_CONVEX_BUILDER_DRAW - -#include - -#ifdef JPH_EPA_CONVEX_BUILDER_DRAW - #include - #include -#endif - -JPH_NAMESPACE_BEGIN - -/// A convex hull builder specifically made for the EPA penetration depth calculation. It trades accuracy for speed and will simply abort of the hull forms defects due to numerical precision problems. -class EPAConvexHullBuilder : public NonCopyable -{ -private: -#ifdef JPH_EPA_CONVEX_BUILDER_DRAW - /// Factor to scale convex hull when debug drawing the construction process - static constexpr Real cDrawScale = 10; -#endif - -public: - // Due to the Euler characteristic (https://en.wikipedia.org/wiki/Euler_characteristic) we know that Vertices - Edges + Faces = 2 - // In our case we only have triangles and they are always fully connected, so each edge is shared exactly between 2 faces: Edges = Faces * 3 / 2 - // Substituting: Vertices = Faces / 2 + 2 which is approximately Faces / 2. - static constexpr int cMaxTriangles = 256; ///< Max triangles in hull - static constexpr int cMaxPoints = cMaxTriangles / 2; ///< Max number of points in hull - - // Constants - static constexpr int cMaxEdgeLength = 128; ///< Max number of edges in FindEdge - static constexpr float cMinTriangleArea = 1.0e-10f; ///< Minimum area of a triangle before, if smaller than this it will not be added to the priority queue - static constexpr float cBarycentricEpsilon = 1.0e-3f; ///< Epsilon value used to determine if a point is in the interior of a triangle - - // Forward declare - class Triangle; - - /// Class that holds the information of an edge - class Edge - { - public: - /// Information about neighbouring triangle - Triangle * mNeighbourTriangle; ///< Triangle that neighbours this triangle - int mNeighbourEdge; ///< Index in mEdge that specifies edge that this Edge is connected to - - int mStartIdx; ///< Vertex index in mPositions that indicates the start vertex of this edge - }; - - using Edges = StaticArray; - using NewTriangles = StaticArray; - - /// Class that holds the information of one triangle - class Triangle : public NonCopyable - { - public: - /// Constructor - inline Triangle(int inIdx0, int inIdx1, int inIdx2, const Vec3 *inPositions); - - /// Check if triangle is facing inPosition - inline bool IsFacing(Vec3Arg inPosition) const - { - JPH_ASSERT(!mRemoved); - return mNormal.Dot(inPosition - mCentroid) > 0.0f; - } - - /// Check if triangle is facing the origin - inline bool IsFacingOrigin() const - { - JPH_ASSERT(!mRemoved); - return mNormal.Dot(mCentroid) < 0.0f; - } - - /// Get the next edge of edge inIndex - inline const Edge & GetNextEdge(int inIndex) const - { - return mEdge[(inIndex + 1) % 3]; - } - - Edge mEdge[3]; ///< 3 edges of this triangle - Vec3 mNormal; ///< Normal of this triangle, length is 2 times area of triangle - Vec3 mCentroid; ///< Center of the triangle - float mClosestLenSq = FLT_MAX; ///< Closest distance^2 from origin to triangle - float mLambda[2]; ///< Barycentric coordinates of closest point to origin on triangle - bool mLambdaRelativeTo0; ///< How to calculate the closest point, true: y0 + l0 * (y1 - y0) + l1 * (y2 - y0), false: y1 + l0 * (y0 - y1) + l1 * (y2 - y1) - bool mClosestPointInterior = false; ///< Flag that indicates that the closest point from this triangle to the origin is an interior point - bool mRemoved = false; ///< Flag that indicates that triangle has been removed - bool mInQueue = false; ///< Flag that indicates that this triangle was placed in the sorted heap (stays true after it is popped because the triangle is freed by the main EPA algorithm loop) -#ifdef JPH_EPA_CONVEX_BUILDER_DRAW - int mIteration; ///< Iteration that this triangle was created -#endif - }; - - /// Factory that creates triangles in a fixed size buffer - class TriangleFactory : public NonCopyable - { - private: - /// Struct that stores both a triangle or a next pointer in case the triangle is unused - union alignas(Triangle) Block - { - uint8 mTriangle[sizeof(Triangle)]; - Block * mNextFree; - }; - - /// Storage for triangle data - Block mTriangles[cMaxTriangles]; ///< Storage for triangles - Block * mNextFree = nullptr; ///< List of free triangles - int mHighWatermark = 0; ///< High water mark for used triangles (if mNextFree == nullptr we can take one from here) - - public: - /// Return all triangles to the free pool - void Clear() - { - mNextFree = nullptr; - mHighWatermark = 0; - } - - /// Allocate a new triangle with 3 indexes - Triangle * CreateTriangle(int inIdx0, int inIdx1, int inIdx2, const Vec3 *inPositions) - { - Triangle *t; - if (mNextFree != nullptr) - { - // Entry available from the free list - t = reinterpret_cast(&mNextFree->mTriangle); - mNextFree = mNextFree->mNextFree; - } - else - { - // Allocate from never used before triangle store - if (mHighWatermark >= cMaxTriangles) - return nullptr; // Buffer full - t = reinterpret_cast(&mTriangles[mHighWatermark].mTriangle); - ++mHighWatermark; - } - - // Call constructor - new (t) Triangle(inIdx0, inIdx1, inIdx2, inPositions); - - return t; - } - - /// Free a triangle - void FreeTriangle(Triangle *inT) - { - // Destruct triangle - inT->~Triangle(); -#ifdef _DEBUG - memset(inT, 0xcd, sizeof(Triangle)); -#endif - - // Add triangle to the free list - Block *tu = reinterpret_cast(inT); - tu->mNextFree = mNextFree; - mNextFree = tu; - } - }; - - // Typedefs - using PointsBase = StaticArray; - using Triangles = StaticArray; - - /// Specialized points list that allows direct access to the size - class Points : public PointsBase - { - public: - size_type & GetSizeRef() - { - return mSize; - } - }; - - /// Specialized triangles list that keeps them sorted on closest distance to origin - class TriangleQueue : public Triangles - { - public: - /// Function to sort triangles on closest distance to origin - static bool sTriangleSorter(const Triangle *inT1, const Triangle *inT2) - { - return inT1->mClosestLenSq > inT2->mClosestLenSq; - } - - /// Add triangle to the list - void push_back(Triangle *inT) - { - // Add to base - Triangles::push_back(inT); - - // Mark in queue - inT->mInQueue = true; - - // Resort heap - std::push_heap(begin(), end(), sTriangleSorter); - } - - /// Peek the next closest triangle without removing it - Triangle * PeekClosest() - { - return front(); - } - - /// Get next closest triangle - Triangle * PopClosest() - { - // Move closest to end - std::pop_heap(begin(), end(), sTriangleSorter); - - // Remove last triangle - Triangle *t = back(); - pop_back(); - return t; - } - }; - - /// Constructor - explicit EPAConvexHullBuilder(const Points &inPositions) : - mPositions(inPositions) - { -#ifdef JPH_EPA_CONVEX_BUILDER_DRAW - mIteration = 0; - mOffset = RVec3::sZero(); -#endif - } - - /// Initialize the hull with 3 points - void Initialize(int inIdx1, int inIdx2, int inIdx3) - { - // Release triangles - mFactory.Clear(); - - // Create triangles (back to back) - Triangle *t1 = CreateTriangle(inIdx1, inIdx2, inIdx3); - Triangle *t2 = CreateTriangle(inIdx1, inIdx3, inIdx2); - - // Link triangles edges - sLinkTriangle(t1, 0, t2, 2); - sLinkTriangle(t1, 1, t2, 1); - sLinkTriangle(t1, 2, t2, 0); - - // Always add both triangles to the priority queue - mTriangleQueue.push_back(t1); - mTriangleQueue.push_back(t2); - -#ifdef JPH_EPA_CONVEX_BUILDER_DRAW - // Draw current state - DrawState(); - - // Increment iteration counter - ++mIteration; -#endif - } - - /// Check if there's another triangle to process from the queue - bool HasNextTriangle() const - { - return !mTriangleQueue.empty(); - } - - /// Access to the next closest triangle to the origin (won't remove it from the queue). - Triangle * PeekClosestTriangleInQueue() - { - return mTriangleQueue.PeekClosest(); - } - - /// Access to the next closest triangle to the origin and remove it from the queue. - Triangle * PopClosestTriangleFromQueue() - { - return mTriangleQueue.PopClosest(); - } - - /// Find the triangle on which inPosition is the furthest to the front - /// Note this function works as long as all points added have been added with AddPoint(..., FLT_MAX). - Triangle * FindFacingTriangle(Vec3Arg inPosition, float &outBestDistSq) - { - Triangle *best = nullptr; - float best_dist_sq = 0.0f; - - for (Triangle *t : mTriangleQueue) - if (!t->mRemoved) - { - float dot = t->mNormal.Dot(inPosition - t->mCentroid); - if (dot > 0.0f) - { - float dist_sq = dot * dot / t->mNormal.LengthSq(); - if (dist_sq > best_dist_sq) - { - best = t; - best_dist_sq = dist_sq; - } - } - } - - outBestDistSq = best_dist_sq; - return best; - } - - /// Add a new point to the convex hull - bool AddPoint(Triangle *inFacingTriangle, int inIdx, float inClosestDistSq, NewTriangles &outTriangles) - { - // Get position - Vec3 pos = mPositions[inIdx]; - -#ifdef JPH_EPA_CONVEX_BUILDER_DRAW - // Draw new support point - DrawMarker(pos, Color::sYellow, 1.0f); -#endif - -#ifdef JPH_EPA_CONVEX_BUILDER_VALIDATE - // Check if structure is intact - ValidateTriangles(); -#endif - - // Find edge of convex hull of triangles that are not facing the new vertex w - Edges edges; - if (!FindEdge(inFacingTriangle, pos, edges)) - return false; - - // Create new triangles - int num_edges = edges.size(); - for (int i = 0; i < num_edges; ++i) - { - // Create new triangle - Triangle *nt = CreateTriangle(edges[i].mStartIdx, edges[(i + 1) % num_edges].mStartIdx, inIdx); - if (nt == nullptr) - return false; - outTriangles.push_back(nt); - - // Check if we need to put this triangle in the priority queue - if ((nt->mClosestPointInterior && nt->mClosestLenSq < inClosestDistSq) // For the main algorithm - || nt->mClosestLenSq < 0.0f) // For when the origin is not inside the hull yet - mTriangleQueue.push_back(nt); - } - - // Link edges - for (int i = 0; i < num_edges; ++i) - { - sLinkTriangle(outTriangles[i], 0, edges[i].mNeighbourTriangle, edges[i].mNeighbourEdge); - sLinkTriangle(outTriangles[i], 1, outTriangles[(i + 1) % num_edges], 2); - } - -#ifdef JPH_EPA_CONVEX_BUILDER_VALIDATE - // Check if structure is intact - ValidateTriangles(); -#endif - -#ifdef JPH_EPA_CONVEX_BUILDER_DRAW - // Draw state of the hull - DrawState(); - - // Increment iteration counter - ++mIteration; -#endif - - return true; - } - - /// Free a triangle - void FreeTriangle(Triangle *inT) - { -#ifdef JPH_ENABLE_ASSERTS - // Make sure that this triangle is not connected - JPH_ASSERT(inT->mRemoved); - for (const Edge &e : inT->mEdge) - JPH_ASSERT(e.mNeighbourTriangle == nullptr); -#endif - -#if defined(JPH_EPA_CONVEX_BUILDER_VALIDATE) || defined(JPH_EPA_CONVEX_BUILDER_DRAW) - // Remove from list of all triangles - Triangles::iterator i = std::find(mTriangles.begin(), mTriangles.end(), inT); - JPH_ASSERT(i != mTriangles.end()); - mTriangles.erase(i); -#endif - - mFactory.FreeTriangle(inT); - } - -private: - /// Create a new triangle - Triangle * CreateTriangle(int inIdx1, int inIdx2, int inIdx3) - { - // Call provider to create triangle - Triangle *t = mFactory.CreateTriangle(inIdx1, inIdx2, inIdx3, mPositions.data()); - if (t == nullptr) - return nullptr; - -#ifdef JPH_EPA_CONVEX_BUILDER_DRAW - // Remember iteration counter - t->mIteration = mIteration; -#endif - -#if defined(JPH_EPA_CONVEX_BUILDER_VALIDATE) || defined(JPH_EPA_CONVEX_BUILDER_DRAW) - // Add to list of triangles for debugging purposes - mTriangles.push_back(t); -#endif - - return t; - } - - /// Link triangle edge to other triangle edge - static void sLinkTriangle(Triangle *inT1, int inEdge1, Triangle *inT2, int inEdge2) - { - JPH_ASSERT(inEdge1 >= 0 && inEdge1 < 3); - JPH_ASSERT(inEdge2 >= 0 && inEdge2 < 3); - Edge &e1 = inT1->mEdge[inEdge1]; - Edge &e2 = inT2->mEdge[inEdge2]; - - // Check not connected yet - JPH_ASSERT(e1.mNeighbourTriangle == nullptr); - JPH_ASSERT(e2.mNeighbourTriangle == nullptr); - - // Check vertices match - JPH_ASSERT(e1.mStartIdx == inT2->GetNextEdge(inEdge2).mStartIdx); - JPH_ASSERT(e2.mStartIdx == inT1->GetNextEdge(inEdge1).mStartIdx); - - // Link up - e1.mNeighbourTriangle = inT2; - e1.mNeighbourEdge = inEdge2; - e2.mNeighbourTriangle = inT1; - e2.mNeighbourEdge = inEdge1; - } - - /// Unlink this triangle - void UnlinkTriangle(Triangle *inT) - { - // Unlink from neighbours - for (int i = 0; i < 3; ++i) - { - Edge &edge = inT->mEdge[i]; - if (edge.mNeighbourTriangle != nullptr) - { - Edge &neighbour_edge = edge.mNeighbourTriangle->mEdge[edge.mNeighbourEdge]; - - // Validate that neighbour points to us - JPH_ASSERT(neighbour_edge.mNeighbourTriangle == inT); - JPH_ASSERT(neighbour_edge.mNeighbourEdge == i); - - // Unlink - neighbour_edge.mNeighbourTriangle = nullptr; - edge.mNeighbourTriangle = nullptr; - } - } - - // If this triangle is not in the priority queue, we can delete it now - if (!inT->mInQueue) - FreeTriangle(inT); - } - - /// Given one triangle that faces inVertex, find the edges of the triangles that are not facing inVertex. - /// Will flag all those triangles for removal. - bool FindEdge(Triangle *inFacingTriangle, Vec3Arg inVertex, Edges &outEdges) - { - // Assert that we were given an empty array - JPH_ASSERT(outEdges.empty()); - - // Should start with a facing triangle - JPH_ASSERT(inFacingTriangle->IsFacing(inVertex)); - - // Flag as removed - inFacingTriangle->mRemoved = true; - - // Instead of recursing, we build our own stack with the information we need - struct StackEntry - { - Triangle * mTriangle; - int mEdge; - int mIter; - }; - StackEntry stack[cMaxEdgeLength]; - int cur_stack_pos = 0; - - // Start with the triangle / edge provided - stack[0].mTriangle = inFacingTriangle; - stack[0].mEdge = 0; - stack[0].mIter = -1; // Start with edge 0 (is incremented below before use) - - // Next index that we expect to find, if we don't then there are 'islands' - int next_expected_start_idx = -1; - - for (;;) - { - StackEntry &cur_entry = stack[cur_stack_pos]; - - // Next iteration - if (++cur_entry.mIter >= 3) - { - // This triangle needs to be removed, unlink it now - UnlinkTriangle(cur_entry.mTriangle); - - // Pop from stack - if (--cur_stack_pos < 0) - break; - } - else - { - // Visit neighbour - Edge &e = cur_entry.mTriangle->mEdge[(cur_entry.mEdge + cur_entry.mIter) % 3]; - Triangle *n = e.mNeighbourTriangle; - if (n != nullptr && !n->mRemoved) - { - // Check if vertex is on the front side of this triangle - if (n->IsFacing(inVertex)) - { - // Vertex on front, this triangle needs to be removed - n->mRemoved = true; - - // Add element to the stack of elements to visit - cur_stack_pos++; - JPH_ASSERT(cur_stack_pos < cMaxEdgeLength); - StackEntry &new_entry = stack[cur_stack_pos]; - new_entry.mTriangle = n; - new_entry.mEdge = e.mNeighbourEdge; - new_entry.mIter = 0; // Is incremented before use, we don't need to test this edge again since we came from it - } - else - { - // Detect if edge doesn't connect to previous edge, if this happens we have found and 'island' which means - // the newly added point is so close to the triangles of the hull that we classified some (nearly) coplanar - // triangles as before and some behind the point. At this point we just abort adding the point because - // we've reached numerical precision. - // Note that we do not need to test if the first and last edge connect, since when there are islands - // there should be at least 2 disconnects. - if (e.mStartIdx != next_expected_start_idx && next_expected_start_idx != -1) - return false; - - // Next expected index is the start index of our neighbour's edge - next_expected_start_idx = n->mEdge[e.mNeighbourEdge].mStartIdx; - - // Vertex behind, keep edge - outEdges.push_back(e); - } - } - } - } - - // Assert that we have a fully connected loop - JPH_ASSERT(outEdges.empty() || outEdges[0].mStartIdx == next_expected_start_idx); - -#ifdef JPH_EPA_CONVEX_BUILDER_DRAW - // Draw edge of facing triangles - for (int i = 0; i < (int)outEdges.size(); ++i) - { - RVec3 edge_start = cDrawScale * (mOffset + mPositions[outEdges[i].mStartIdx]); - DebugRenderer::sInstance->DrawArrow(edge_start, cDrawScale * (mOffset + mPositions[outEdges[(i + 1) % outEdges.size()].mStartIdx]), Color::sYellow, 0.01f); - DebugRenderer::sInstance->DrawText3D(edge_start, ConvertToString(outEdges[i].mStartIdx), Color::sWhite); - } - - // Draw the state with the facing triangles removed - DrawState(); -#endif - - // When we start with two triangles facing away from each other and adding a point that is on the plane, - // sometimes we consider the point in front of both causing both triangles to be removed resulting in an empty edge list. - // In this case we fail to add the point which will result in no collision reported (the shapes are contacting in 1 point so there's 0 penetration) - return outEdges.size() >= 3; - } - -#ifdef JPH_EPA_CONVEX_BUILDER_VALIDATE - /// Check consistency of 1 triangle - void ValidateTriangle(const Triangle *inT) const - { - if (inT->mRemoved) - { - // Validate that removed triangles are not connected to anything - for (const Edge &my_edge : inT->mEdge) - JPH_ASSERT(my_edge.mNeighbourTriangle == nullptr); - } - else - { - for (int i = 0; i < 3; ++i) - { - const Edge &my_edge = inT->mEdge[i]; - - // Assert that we have a neighbour - const Triangle *nb = my_edge.mNeighbourTriangle; - JPH_ASSERT(nb != nullptr); - - if (nb != nullptr) - { - // Assert that our neighbours edge points to us - const Edge &nb_edge = nb->mEdge[my_edge.mNeighbourEdge]; - JPH_ASSERT(nb_edge.mNeighbourTriangle == inT); - JPH_ASSERT(nb_edge.mNeighbourEdge == i); - - // Assert that the next edge of the neighbour points to the same vertex as this edge's vertex - const Edge &nb_next_edge = nb->GetNextEdge(my_edge.mNeighbourEdge); - JPH_ASSERT(nb_next_edge.mStartIdx == my_edge.mStartIdx); - - // Assert that my next edge points to the same vertex as my neighbours vertex - const Edge &my_next_edge = inT->GetNextEdge(i); - JPH_ASSERT(my_next_edge.mStartIdx == nb_edge.mStartIdx); - } - } - } - } - - /// Check consistency of all triangles - void ValidateTriangles() const - { - for (const Triangle *t : mTriangles) - ValidateTriangle(t); - } -#endif - -#ifdef JPH_EPA_CONVEX_BUILDER_DRAW -public: - /// Draw state of algorithm - void DrawState() - { - // Draw origin - DebugRenderer::sInstance->DrawCoordinateSystem(RMat44::sTranslation(cDrawScale * mOffset), 1.0f); - - // Draw triangles - for (const Triangle *t : mTriangles) - if (!t->mRemoved) - { - // Calculate the triangle vertices - RVec3 p1 = cDrawScale * (mOffset + mPositions[t->mEdge[0].mStartIdx]); - RVec3 p2 = cDrawScale * (mOffset + mPositions[t->mEdge[1].mStartIdx]); - RVec3 p3 = cDrawScale * (mOffset + mPositions[t->mEdge[2].mStartIdx]); - - // Draw triangle - DebugRenderer::sInstance->DrawTriangle(p1, p2, p3, Color::sGetDistinctColor(t->mIteration)); - DebugRenderer::sInstance->DrawWireTriangle(p1, p2, p3, Color::sGrey); - - // Draw normal - RVec3 centroid = cDrawScale * (mOffset + t->mCentroid); - float len = t->mNormal.Length(); - if (len > 0.0f) - DebugRenderer::sInstance->DrawArrow(centroid, centroid + t->mNormal / len, Color::sDarkGreen, 0.01f); - } - - // Determine max position - float min_x = FLT_MAX; - float max_x = -FLT_MAX; - for (Vec3 p : mPositions) - { - min_x = min(min_x, p.GetX()); - max_x = max(max_x, p.GetX()); - } - - // Offset to the right - mOffset += Vec3(max_x - min_x + 0.5f, 0.0f, 0.0f); - } - - /// Draw a label to indicate the next stage in the algorithm - void DrawLabel(const string_view &inText) - { - DebugRenderer::sInstance->DrawText3D(cDrawScale * mOffset, inText, Color::sWhite, 0.1f * cDrawScale); - - mOffset += Vec3(5.0f, 0.0f, 0.0f); - } - - /// Draw geometry for debugging purposes - void DrawGeometry(const DebugRenderer::GeometryRef &inGeometry, ColorArg inColor) - { - RMat44 origin = RMat44::sScale(Vec3::sReplicate(cDrawScale)) * RMat44::sTranslation(mOffset); - DebugRenderer::sInstance->DrawGeometry(origin, inGeometry->mBounds.Transformed(origin), inGeometry->mBounds.GetExtent().LengthSq(), inColor, inGeometry); - - mOffset += Vec3(inGeometry->mBounds.GetSize().GetX(), 0, 0); - } - - /// Draw a triangle for debugging purposes - void DrawWireTriangle(const Triangle &inTriangle, ColorArg inColor) - { - RVec3 prev = cDrawScale * (mOffset + mPositions[inTriangle.mEdge[2].mStartIdx]); - for (const Edge &edge : inTriangle.mEdge) - { - RVec3 cur = cDrawScale * (mOffset + mPositions[edge.mStartIdx]); - DebugRenderer::sInstance->DrawArrow(prev, cur, inColor, 0.01f); - prev = cur; - } - } - - /// Draw a marker for debugging purposes - void DrawMarker(Vec3Arg inPosition, ColorArg inColor, float inSize) - { - DebugRenderer::sInstance->DrawMarker(cDrawScale * (mOffset + inPosition), inColor, inSize); - } - - /// Draw an arrow for debugging purposes - void DrawArrow(Vec3Arg inFrom, Vec3Arg inTo, ColorArg inColor, float inArrowSize) - { - DebugRenderer::sInstance->DrawArrow(cDrawScale * (mOffset + inFrom), cDrawScale * (mOffset + inTo), inColor, inArrowSize); - } -#endif - -private: - TriangleFactory mFactory; ///< Factory to create new triangles and remove old ones - const Points & mPositions; ///< List of positions (some of them are part of the hull) - TriangleQueue mTriangleQueue; ///< List of triangles that are part of the hull that still need to be checked (if !mRemoved) - -#if defined(JPH_EPA_CONVEX_BUILDER_VALIDATE) || defined(JPH_EPA_CONVEX_BUILDER_DRAW) - Triangles mTriangles; ///< The list of all triangles in this hull (for debug purposes) -#endif - -#ifdef JPH_EPA_CONVEX_BUILDER_DRAW - int mIteration; ///< Number of iterations we've had so far (for debug purposes) - RVec3 mOffset; ///< Offset to use for state drawing -#endif -}; - -// The determinant that is calculated in the Triangle constructor is really sensitive -// to numerical round off, disable the fmadd instructions to maintain precision. -JPH_PRECISE_MATH_ON - -EPAConvexHullBuilder::Triangle::Triangle(int inIdx0, int inIdx1, int inIdx2, const Vec3 *inPositions) -{ - // Fill in indexes - JPH_ASSERT(inIdx0 != inIdx1 && inIdx0 != inIdx2 && inIdx1 != inIdx2); - mEdge[0].mStartIdx = inIdx0; - mEdge[1].mStartIdx = inIdx1; - mEdge[2].mStartIdx = inIdx2; - - // Clear links - mEdge[0].mNeighbourTriangle = nullptr; - mEdge[1].mNeighbourTriangle = nullptr; - mEdge[2].mNeighbourTriangle = nullptr; - - // Get vertex positions - Vec3 y0 = inPositions[inIdx0]; - Vec3 y1 = inPositions[inIdx1]; - Vec3 y2 = inPositions[inIdx2]; - - // Calculate centroid - mCentroid = (y0 + y1 + y2) / 3.0f; - - // Calculate edges - Vec3 y10 = y1 - y0; - Vec3 y20 = y2 - y0; - Vec3 y21 = y2 - y1; - - // The most accurate normal is calculated by using the two shortest edges - // See: https://box2d.org/posts/2014/01/troublesome-triangle/ - // The difference in normals is most pronounced when one edge is much smaller than the others (in which case the other 2 must have roughly the same length). - // Therefore we can suffice by just picking the shortest from 2 edges and use that with the 3rd edge to calculate the normal. - // We first check which of the edges is shorter. - float y20_dot_y20 = y20.Dot(y20); - float y21_dot_y21 = y21.Dot(y21); - if (y20_dot_y20 < y21_dot_y21) - { - // We select the edges y10 and y20 - mNormal = y10.Cross(y20); - - // Check if triangle is degenerate - float normal_len_sq = mNormal.LengthSq(); - if (normal_len_sq > cMinTriangleArea) - { - // Determine distance between triangle and origin: distance = (centroid - origin) . normal / |normal| - // Note that this way of calculating the closest point is much more accurate than first calculating barycentric coordinates and then calculating the closest - // point based on those coordinates. Note that we preserve the sign of the distance to check on which side the origin is. - float c_dot_n = mCentroid.Dot(mNormal); - mClosestLenSq = abs(c_dot_n) * c_dot_n / normal_len_sq; - - // Calculate closest point to origin using barycentric coordinates: - // - // v = y0 + l0 * (y1 - y0) + l1 * (y2 - y0) - // v . (y1 - y0) = 0 - // v . (y2 - y0) = 0 - // - // Written in matrix form: - // - // | y10.y10 y20.y10 | | l0 | = | -y0.y10 | - // | y10.y20 y20.y20 | | l1 | | -y0.y20 | - // - // (y10 = y1 - y0 etc.) - // - // Cramers rule to invert matrix: - float y10_dot_y10 = y10.LengthSq(); - float y10_dot_y20 = y10.Dot(y20); - float determinant = y10_dot_y10 * y20_dot_y20 - y10_dot_y20 * y10_dot_y20; - if (determinant > 0.0f) // If determinant == 0 then the system is linearly dependent and the triangle is degenerate, since y10.10 * y20.y20 > y10.y20^2 it should also be > 0 - { - float y0_dot_y10 = y0.Dot(y10); - float y0_dot_y20 = y0.Dot(y20); - float l0 = (y10_dot_y20 * y0_dot_y20 - y20_dot_y20 * y0_dot_y10) / determinant; - float l1 = (y10_dot_y20 * y0_dot_y10 - y10_dot_y10 * y0_dot_y20) / determinant; - mLambda[0] = l0; - mLambda[1] = l1; - mLambdaRelativeTo0 = true; - - // Check if closest point is interior to the triangle. For a convex hull which contains the origin each face must contain the origin, but because - // our faces are triangles, we can have multiple coplanar triangles and only 1 will have the origin as an interior point. We want to use this triangle - // to calculate the contact points because it gives the most accurate results, so we will only add these triangles to the priority queue. - if (l0 > -cBarycentricEpsilon && l1 > -cBarycentricEpsilon && l0 + l1 < 1.0f + cBarycentricEpsilon) - mClosestPointInterior = true; - } - } - } - else - { - // We select the edges y10 and y21 - mNormal = y10.Cross(y21); - - // Check if triangle is degenerate - float normal_len_sq = mNormal.LengthSq(); - if (normal_len_sq > cMinTriangleArea) - { - // Again calculate distance between triangle and origin - float c_dot_n = mCentroid.Dot(mNormal); - mClosestLenSq = abs(c_dot_n) * c_dot_n / normal_len_sq; - - // Calculate closest point to origin using barycentric coordinates but this time using y1 as the reference vertex - // - // v = y1 + l0 * (y0 - y1) + l1 * (y2 - y1) - // v . (y0 - y1) = 0 - // v . (y2 - y1) = 0 - // - // Written in matrix form: - // - // | y10.y10 -y21.y10 | | l0 | = | y1.y10 | - // | -y10.y21 y21.y21 | | l1 | | -y1.y21 | - // - // Cramers rule to invert matrix: - float y10_dot_y10 = y10.LengthSq(); - float y10_dot_y21 = y10.Dot(y21); - float determinant = y10_dot_y10 * y21_dot_y21 - y10_dot_y21 * y10_dot_y21; - if (determinant > 0.0f) - { - float y1_dot_y10 = y1.Dot(y10); - float y1_dot_y21 = y1.Dot(y21); - float l0 = (y21_dot_y21 * y1_dot_y10 - y10_dot_y21 * y1_dot_y21) / determinant; - float l1 = (y10_dot_y21 * y1_dot_y10 - y10_dot_y10 * y1_dot_y21) / determinant; - mLambda[0] = l0; - mLambda[1] = l1; - mLambdaRelativeTo0 = false; - - // Again check if the closest point is inside the triangle - if (l0 > -cBarycentricEpsilon && l1 > -cBarycentricEpsilon && l0 + l1 < 1.0f + cBarycentricEpsilon) - mClosestPointInterior = true; - } - } - } -} - -JPH_PRECISE_MATH_OFF - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/EPAPenetrationDepth.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/EPAPenetrationDepth.h deleted file mode 100644 index ba7b18f7e72..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/EPAPenetrationDepth.h +++ /dev/null @@ -1,546 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include -#include - -//#define JPH_EPA_PENETRATION_DEPTH_DEBUG - -JPH_NAMESPACE_BEGIN - -/// Implementation of Expanding Polytope Algorithm as described in: -/// -/// Proximity Queries and Penetration Depth Computation on 3D Game Objects - Gino van den Bergen -/// -/// The implementation of this algorithm does not completely follow the article, instead of splitting -/// triangles at each edge as in fig. 7 in the article, we build a convex hull (removing any triangles that -/// are facing the new point, thereby avoiding the problem of getting really oblong triangles as mentioned in -/// the article). -/// -/// The algorithm roughly works like: -/// -/// - Start with a simplex of the Minkowski sum (difference) of two objects that was calculated by GJK -/// - This simplex should contain the origin (or else GJK would have reported: no collision) -/// - In cases where the simplex consists of 1 - 3 points, find some extra support points (of the Minkowski sum) to get to at least 4 points -/// - Convert this into a convex hull with non-zero volume (which includes the origin) -/// - A: Calculate the closest point to the origin for all triangles of the hull and take the closest one -/// - Calculate a new support point (of the Minkowski sum) in this direction and add this point to the convex hull -/// - This will remove all faces that are facing the new point and will create new triangles to fill up the hole -/// - Loop to A until no closer point found -/// - The closest point indicates the position / direction of least penetration -class EPAPenetrationDepth -{ -private: - // Typedefs - static constexpr int cMaxPoints = EPAConvexHullBuilder::cMaxPoints; - static constexpr int cMaxPointsToIncludeOriginInHull = 32; - static_assert(cMaxPointsToIncludeOriginInHull < cMaxPoints); - - using Triangle = EPAConvexHullBuilder::Triangle; - using Points = EPAConvexHullBuilder::Points; - - /// The GJK algorithm, used to start the EPA algorithm - GJKClosestPoint mGJK; - - /// A list of support points for the EPA algorithm - class SupportPoints - { - public: - /// List of support points - Points mY; - Vec3 mP[cMaxPoints]; - Vec3 mQ[cMaxPoints]; - - /// Calculate and add new support point to the list of points - template - Vec3 Add(const A &inA, const B &inB, Vec3Arg inDirection, int &outIndex) - { - // Get support point of the minkowski sum A - B - Vec3 p = inA.GetSupport(inDirection); - Vec3 q = inB.GetSupport(-inDirection); - Vec3 w = p - q; - - // Store new point - outIndex = mY.size(); - mY.push_back(w); - mP[outIndex] = p; - mQ[outIndex] = q; - - return w; - } - }; - -public: - /// Return code for GetPenetrationDepthStepGJK - enum class EStatus - { - NotColliding, ///< Returned if the objects don't collide, in this case outPointA/outPointB are invalid - Colliding, ///< Returned if the objects penetrate - Indeterminate ///< Returned if the objects penetrate further than the convex radius. In this case you need to call GetPenetrationDepthStepEPA to get the actual penetration depth. - }; - - /// Calculates penetration depth between two objects, first step of two (the GJK step) - /// - /// @param inAExcludingConvexRadius Object A without convex radius. - /// @param inBExcludingConvexRadius Object B without convex radius. - /// @param inConvexRadiusA Convex radius for A. - /// @param inConvexRadiusB Convex radius for B. - /// @param ioV Pass in previously returned value or (1, 0, 0). On return this value is changed to direction to move B out of collision along the shortest path (magnitude is meaningless). - /// @param inTolerance Minimal distance before A and B are considered colliding. - /// @param outPointA Position on A that has the least amount of penetration. - /// @param outPointB Position on B that has the least amount of penetration. - /// Use |outPointB - outPointA| to get the distance of penetration. - template - EStatus GetPenetrationDepthStepGJK(const AE &inAExcludingConvexRadius, float inConvexRadiusA, const BE &inBExcludingConvexRadius, float inConvexRadiusB, float inTolerance, Vec3 &ioV, Vec3 &outPointA, Vec3 &outPointB) - { - JPH_PROFILE_FUNCTION(); - - // Don't supply a zero ioV, we only want to get points on the hull of the Minkowsky sum and not internal points - JPH_ASSERT(!ioV.IsNearZero()); - - // Get closest points - float combined_radius = inConvexRadiusA + inConvexRadiusB; - float combined_radius_sq = combined_radius * combined_radius; - float closest_points_dist_sq = mGJK.GetClosestPoints(inAExcludingConvexRadius, inBExcludingConvexRadius, inTolerance, combined_radius_sq, ioV, outPointA, outPointB); - if (closest_points_dist_sq > combined_radius_sq) - { - // No collision - return EStatus::NotColliding; - } - if (closest_points_dist_sq > 0.0f) - { - // Collision within convex radius, adjust points for convex radius - float v_len = sqrt(closest_points_dist_sq); // GetClosestPoints function returns |ioV|^2 when return value < FLT_MAX - outPointA += ioV * (inConvexRadiusA / v_len); - outPointB -= ioV * (inConvexRadiusB / v_len); - return EStatus::Colliding; - } - - return EStatus::Indeterminate; - } - - /// Calculates penetration depth between two objects, second step (the EPA step) - /// - /// @param inAIncludingConvexRadius Object A with convex radius - /// @param inBIncludingConvexRadius Object B with convex radius - /// @param inTolerance A factor that determines the accuracy of the result. If the change of the squared distance is less than inTolerance * current_penetration_depth^2 the algorithm will terminate. Should be bigger or equal to FLT_EPSILON. - /// @param outV Direction to move B out of collision along the shortest path (magnitude is meaningless) - /// @param outPointA Position on A that has the least amount of penetration - /// @param outPointB Position on B that has the least amount of penetration - /// Use |outPointB - outPointA| to get the distance of penetration - /// - /// @return False if the objects don't collide, in this case outPointA/outPointB are invalid. - /// True if the objects penetrate - template - bool GetPenetrationDepthStepEPA(const AI &inAIncludingConvexRadius, const BI &inBIncludingConvexRadius, float inTolerance, Vec3 &outV, Vec3 &outPointA, Vec3 &outPointB) - { - JPH_PROFILE_FUNCTION(); - - // Check that the tolerance makes sense (smaller value than this will just result in needless iterations) - JPH_ASSERT(inTolerance >= FLT_EPSILON); - - // Fetch the simplex from GJK algorithm - SupportPoints support_points; - mGJK.GetClosestPointsSimplex(support_points.mY.data(), support_points.mP, support_points.mQ, support_points.mY.GetSizeRef()); - - // Fill up the amount of support points to 4 - switch (support_points.mY.size()) - { - case 1: - { - // 1 vertex, which must be at the origin, which is useless for our purpose - JPH_ASSERT(support_points.mY[0].IsNearZero(1.0e-8f)); - support_points.mY.pop_back(); - - // Add support points in 4 directions to form a tetrahedron around the origin - int p1, p2, p3, p4; - (void)support_points.Add(inAIncludingConvexRadius, inBIncludingConvexRadius, Vec3(0, 1, 0), p1); - (void)support_points.Add(inAIncludingConvexRadius, inBIncludingConvexRadius, Vec3(-1, -1, -1), p2); - (void)support_points.Add(inAIncludingConvexRadius, inBIncludingConvexRadius, Vec3(1, -1, -1), p3); - (void)support_points.Add(inAIncludingConvexRadius, inBIncludingConvexRadius, Vec3(0, -1, 1), p4); - JPH_ASSERT(p1 == 0); - JPH_ASSERT(p2 == 1); - JPH_ASSERT(p3 == 2); - JPH_ASSERT(p4 == 3); - break; - } - - case 2: - { - // Two vertices, create 3 extra by taking perpendicular axis and rotating it around in 120 degree increments - Vec3 axis = (support_points.mY[1] - support_points.mY[0]).Normalized(); - Mat44 rotation = Mat44::sRotation(axis, DegreesToRadians(120.0f)); - Vec3 dir1 = axis.GetNormalizedPerpendicular(); - Vec3 dir2 = rotation * dir1; - Vec3 dir3 = rotation * dir2; - int p1, p2, p3; - (void)support_points.Add(inAIncludingConvexRadius, inBIncludingConvexRadius, dir1, p1); - (void)support_points.Add(inAIncludingConvexRadius, inBIncludingConvexRadius, dir2, p2); - (void)support_points.Add(inAIncludingConvexRadius, inBIncludingConvexRadius, dir3, p3); - JPH_ASSERT(p1 == 2); - JPH_ASSERT(p2 == 3); - JPH_ASSERT(p3 == 4); - break; - } - - case 3: - case 4: - // We already have enough points - break; - } - - // Create hull out of the initial points - JPH_ASSERT(support_points.mY.size() >= 3); - EPAConvexHullBuilder hull(support_points.mY); -#ifdef JPH_EPA_CONVEX_BUILDER_DRAW - hull.DrawLabel("Build initial hull"); -#endif -#ifdef JPH_EPA_PENETRATION_DEPTH_DEBUG - Trace("Init: num_points = %u", (uint)support_points.mY.size()); -#endif - hull.Initialize(0, 1, 2); - for (typename Points::size_type i = 3; i < support_points.mY.size(); ++i) - { - float dist_sq; - Triangle *t = hull.FindFacingTriangle(support_points.mY[i], dist_sq); - if (t != nullptr) - { - EPAConvexHullBuilder::NewTriangles new_triangles; - if (!hull.AddPoint(t, i, FLT_MAX, new_triangles)) - { - // We can't recover from a failure to add a point to the hull because the old triangles have been unlinked already. - // Assume no collision. This can happen if the shapes touch in 1 point (or plane) in which case the hull is degenerate. - return false; - } - } - } - -#ifdef JPH_EPA_CONVEX_BUILDER_DRAW - hull.DrawLabel("Complete hull"); - - // Generate the hull of the Minkowski difference for visualization - MinkowskiDifference diff(inAIncludingConvexRadius, inBIncludingConvexRadius); - DebugRenderer::GeometryRef geometry = DebugRenderer::sInstance->CreateTriangleGeometryForConvex([&diff](Vec3Arg inDirection) { return diff.GetSupport(inDirection); }); - hull.DrawGeometry(geometry, Color::sYellow); - - hull.DrawLabel("Ensure origin in hull"); -#endif - - // Loop until we are sure that the origin is inside the hull - for (;;) - { - // Get the next closest triangle - Triangle *t = hull.PeekClosestTriangleInQueue(); - - // Don't process removed triangles, just free them (because they're in a heap we don't remove them earlier since we would have to rebuild the sorted heap) - if (t->mRemoved) - { - hull.PopClosestTriangleFromQueue(); - - // If we run out of triangles, we couldn't include the origin in the hull so there must be very little penetration and we report no collision. - if (!hull.HasNextTriangle()) - return false; - - hull.FreeTriangle(t); - continue; - } - - // If the closest to the triangle is zero or positive, the origin is in the hull and we can proceed to the main algorithm - if (t->mClosestLenSq >= 0.0f) - break; - -#ifdef JPH_EPA_CONVEX_BUILDER_DRAW - hull.DrawLabel("Next iteration"); -#endif -#ifdef JPH_EPA_PENETRATION_DEPTH_DEBUG - Trace("EncapsulateOrigin: verts = (%d, %d, %d), closest_dist_sq = %g, centroid = (%g, %g, %g), normal = (%g, %g, %g)", - t->mEdge[0].mStartIdx, t->mEdge[1].mStartIdx, t->mEdge[2].mStartIdx, - t->mClosestLenSq, - t->mCentroid.GetX(), t->mCentroid.GetY(), t->mCentroid.GetZ(), - t->mNormal.GetX(), t->mNormal.GetY(), t->mNormal.GetZ()); -#endif - - // Remove the triangle from the queue before we start adding new ones (which may result in a new closest triangle at the front of the queue) - hull.PopClosestTriangleFromQueue(); - - // Add a support point to get the origin inside the hull - int new_index; - Vec3 w = support_points.Add(inAIncludingConvexRadius, inBIncludingConvexRadius, t->mNormal, new_index); - -#ifdef JPH_EPA_CONVEX_BUILDER_DRAW - // Draw the point that we're adding - hull.DrawMarker(w, Color::sRed, 1.0f); - hull.DrawWireTriangle(*t, Color::sRed); - hull.DrawState(); -#endif - - // Add the point to the hull, if we fail we terminate and report no collision - EPAConvexHullBuilder::NewTriangles new_triangles; - if (!t->IsFacing(w) || !hull.AddPoint(t, new_index, FLT_MAX, new_triangles)) - return false; - - // The triangle is facing the support point "w" and can now be safely removed - JPH_ASSERT(t->mRemoved); - hull.FreeTriangle(t); - - // If we run out of triangles or points, we couldn't include the origin in the hull so there must be very little penetration and we report no collision. - if (!hull.HasNextTriangle() || support_points.mY.size() >= cMaxPointsToIncludeOriginInHull) - return false; - } - -#ifdef JPH_EPA_CONVEX_BUILDER_DRAW - hull.DrawLabel("Main algorithm"); -#endif - - // Current closest distance to origin - float closest_dist_sq = FLT_MAX; - - // Remember last good triangle - Triangle *last = nullptr; - - // If we want to flip the penetration depth - bool flip_v_sign = false; - - // Loop until closest point found - do - { - // Get closest triangle to the origin - Triangle *t = hull.PopClosestTriangleFromQueue(); - - // Don't process removed triangles, just free them (because they're in a heap we don't remove them earlier since we would have to rebuild the sorted heap) - if (t->mRemoved) - { - hull.FreeTriangle(t); - continue; - } - -#ifdef JPH_EPA_CONVEX_BUILDER_DRAW - hull.DrawLabel("Next iteration"); -#endif -#ifdef JPH_EPA_PENETRATION_DEPTH_DEBUG - Trace("FindClosest: verts = (%d, %d, %d), closest_len_sq = %g, centroid = (%g, %g, %g), normal = (%g, %g, %g)", - t->mEdge[0].mStartIdx, t->mEdge[1].mStartIdx, t->mEdge[2].mStartIdx, - t->mClosestLenSq, - t->mCentroid.GetX(), t->mCentroid.GetY(), t->mCentroid.GetZ(), - t->mNormal.GetX(), t->mNormal.GetY(), t->mNormal.GetZ()); -#endif - // Check if next triangle is further away than closest point, we've found the closest point - if (t->mClosestLenSq >= closest_dist_sq) - break; - - // Replace last good with this triangle - if (last != nullptr) - hull.FreeTriangle(last); - last = t; - - // Add support point in direction of normal of the plane - // Note that the article uses the closest point between the origin and plane, but this always has the exact same direction as the normal (if the origin is behind the plane) - // and this way we do less calculations and lose less precision - int new_index; - Vec3 w = support_points.Add(inAIncludingConvexRadius, inBIncludingConvexRadius, t->mNormal, new_index); - - // Project w onto the triangle normal - float dot = t->mNormal.Dot(w); - - // Check if we just found a separating axis. This can happen if the shape shrunk by convex radius and then expanded by - // convex radius is bigger then the original shape due to inaccuracies in the shrinking process. - if (dot < 0.0f) - return false; - - // Get the distance squared (along normal) to the support point - float dist_sq = Square(dot) / t->mNormal.LengthSq(); - -#ifdef JPH_EPA_PENETRATION_DEPTH_DEBUG - Trace("FindClosest: w = (%g, %g, %g), dot = %g, dist_sq = %g", - w.GetX(), w.GetY(), w.GetZ(), - dot, dist_sq); -#endif -#ifdef JPH_EPA_CONVEX_BUILDER_DRAW - // Draw the point that we're adding - hull.DrawMarker(w, Color::sPurple, 1.0f); - hull.DrawWireTriangle(*t, Color::sPurple); - hull.DrawState(); -#endif - - // If the error became small enough, we've converged - if (dist_sq - t->mClosestLenSq < t->mClosestLenSq * inTolerance) - { -#ifdef JPH_EPA_PENETRATION_DEPTH_DEBUG - Trace("Converged"); -#endif // JPH_EPA_PENETRATION_DEPTH_DEBUG - break; - } - - // Keep track of the minimum distance - closest_dist_sq = min(closest_dist_sq, dist_sq); - - // If the triangle thinks this point is not front facing, we've reached numerical precision and we're done - if (!t->IsFacing(w)) - { -#ifdef JPH_EPA_PENETRATION_DEPTH_DEBUG - Trace("Not facing triangle"); -#endif // JPH_EPA_PENETRATION_DEPTH_DEBUG - break; - } - - // Add point to hull - EPAConvexHullBuilder::NewTriangles new_triangles; - if (!hull.AddPoint(t, new_index, closest_dist_sq, new_triangles)) - { -#ifdef JPH_EPA_PENETRATION_DEPTH_DEBUG - Trace("Could not add point"); -#endif // JPH_EPA_PENETRATION_DEPTH_DEBUG - break; - } - - // If the hull is starting to form defects then we're reaching numerical precision and we have to stop - bool has_defect = false; - for (const Triangle *nt : new_triangles) - if (nt->IsFacingOrigin()) - { - has_defect = true; - break; - } - if (has_defect) - { -#ifdef JPH_EPA_PENETRATION_DEPTH_DEBUG - Trace("Has defect"); -#endif // JPH_EPA_PENETRATION_DEPTH_DEBUG - // When the hull has defects it is possible that the origin has been classified on the wrong side of the triangle - // so we do an additional check to see if the penetration in the -triangle normal direction is smaller than - // the penetration in the triangle normal direction. If so we must flip the sign of the penetration depth. - Vec3 w2 = inAIncludingConvexRadius.GetSupport(-t->mNormal) - inBIncludingConvexRadius.GetSupport(t->mNormal); - float dot2 = -t->mNormal.Dot(w2); - if (dot2 < dot) - flip_v_sign = true; - break; - } - } - while (hull.HasNextTriangle() && support_points.mY.size() < cMaxPoints); - - // Determine closest points, if last == null it means the hull was a plane so there's no penetration - if (last == nullptr) - return false; - -#ifdef JPH_EPA_CONVEX_BUILDER_DRAW - hull.DrawLabel("Closest found"); - hull.DrawWireTriangle(*last, Color::sWhite); - hull.DrawArrow(last->mCentroid, last->mCentroid + last->mNormal.NormalizedOr(Vec3::sZero()), Color::sWhite, 0.1f); - hull.DrawState(); -#endif - - // Calculate penetration by getting the vector from the origin to the closest point on the triangle: - // distance = (centroid - origin) . normal / |normal|, closest = origin + distance * normal / |normal| - outV = (last->mCentroid.Dot(last->mNormal) / last->mNormal.LengthSq()) * last->mNormal; - - // If penetration is near zero, treat this as a non collision since we cannot find a good normal - if (outV.IsNearZero()) - return false; - - // Check if we have to flip the sign of the penetration depth - if (flip_v_sign) - outV = -outV; - - // Use the barycentric coordinates for the closest point to the origin to find the contact points on A and B - Vec3 p0 = support_points.mP[last->mEdge[0].mStartIdx]; - Vec3 p1 = support_points.mP[last->mEdge[1].mStartIdx]; - Vec3 p2 = support_points.mP[last->mEdge[2].mStartIdx]; - - Vec3 q0 = support_points.mQ[last->mEdge[0].mStartIdx]; - Vec3 q1 = support_points.mQ[last->mEdge[1].mStartIdx]; - Vec3 q2 = support_points.mQ[last->mEdge[2].mStartIdx]; - - if (last->mLambdaRelativeTo0) - { - // y0 was the reference vertex - outPointA = p0 + last->mLambda[0] * (p1 - p0) + last->mLambda[1] * (p2 - p0); - outPointB = q0 + last->mLambda[0] * (q1 - q0) + last->mLambda[1] * (q2 - q0); - } - else - { - // y1 was the reference vertex - outPointA = p1 + last->mLambda[0] * (p0 - p1) + last->mLambda[1] * (p2 - p1); - outPointB = q1 + last->mLambda[0] * (q0 - q1) + last->mLambda[1] * (q2 - q1); - } - - return true; - } - - /// This function combines the GJK and EPA steps and is provided as a convenience function. - /// Note: less performant since you're providing all support functions in one go - /// Note 2: You need to initialize ioV, see documentation at GetPenetrationDepthStepGJK! - template - bool GetPenetrationDepth(const AE &inAExcludingConvexRadius, const AI &inAIncludingConvexRadius, float inConvexRadiusA, const BE &inBExcludingConvexRadius, const BI &inBIncludingConvexRadius, float inConvexRadiusB, float inCollisionToleranceSq, float inPenetrationTolerance, Vec3 &ioV, Vec3 &outPointA, Vec3 &outPointB) - { - // Check result of collision detection - switch (GetPenetrationDepthStepGJK(inAExcludingConvexRadius, inConvexRadiusA, inBExcludingConvexRadius, inConvexRadiusB, inCollisionToleranceSq, ioV, outPointA, outPointB)) - { - case EPAPenetrationDepth::EStatus::Colliding: - return true; - - case EPAPenetrationDepth::EStatus::NotColliding: - return false; - - case EPAPenetrationDepth::EStatus::Indeterminate: - return GetPenetrationDepthStepEPA(inAIncludingConvexRadius, inBIncludingConvexRadius, inPenetrationTolerance, ioV, outPointA, outPointB); - } - - JPH_ASSERT(false); - return false; - } - - /// Test if a cast shape inA moving from inStart to lambda * inStart.GetTranslation() + inDirection where lambda e [0, ioLambda> intersects inB - /// - /// @param inStart Start position and orientation of the convex object - /// @param inDirection Direction of the sweep (ioLambda * inDirection determines length) - /// @param inCollisionTolerance The minimal distance between A and B before they are considered colliding - /// @param inPenetrationTolerance A factor that determines the accuracy of the result. If the change of the squared distance is less than inTolerance * current_penetration_depth^2 the algorithm will terminate. Should be bigger or equal to FLT_EPSILON. - /// @param inA The convex object A, must support the GetSupport(Vec3) function. - /// @param inB The convex object B, must support the GetSupport(Vec3) function. - /// @param inConvexRadiusA The convex radius of A, this will be added on all sides to pad A. - /// @param inConvexRadiusB The convex radius of B, this will be added on all sides to pad B. - /// @param inReturnDeepestPoint If the shapes are initially intersecting this determines if the EPA algorithm will run to find the deepest point - /// @param ioLambda The max fraction along the sweep, on output updated with the actual collision fraction. - /// @param outPointA is the contact point on A - /// @param outPointB is the contact point on B - /// @param outContactNormal is either the contact normal when the objects are touching or the penetration axis when the objects are penetrating at the start of the sweep (pointing from A to B, length will not be 1) - /// - /// @return true if the a hit was found, in which case ioLambda, outPointA, outPointB and outSurfaceNormal are updated. - template - bool CastShape(Mat44Arg inStart, Vec3Arg inDirection, float inCollisionTolerance, float inPenetrationTolerance, const A &inA, const B &inB, float inConvexRadiusA, float inConvexRadiusB, bool inReturnDeepestPoint, float &ioLambda, Vec3 &outPointA, Vec3 &outPointB, Vec3 &outContactNormal) - { - // First determine if there's a collision at all - if (!mGJK.CastShape(inStart, inDirection, inCollisionTolerance, inA, inB, inConvexRadiusA, inConvexRadiusB, ioLambda, outPointA, outPointB, outContactNormal)) - return false; - - // When our contact normal is too small, we don't have an accurate result - bool contact_normal_invalid = outContactNormal.IsNearZero(Square(inCollisionTolerance)); - - if (inReturnDeepestPoint - && ioLambda == 0.0f // Only when lambda = 0 we can have the bodies overlap - && (inConvexRadiusA + inConvexRadiusB == 0.0f // When no convex radius was provided we can never trust contact points at lambda = 0 - || contact_normal_invalid)) - { - // If we're initially intersecting, we need to run the EPA algorithm in order to find the deepest contact point - AddConvexRadius add_convex_a(inA, inConvexRadiusA); - AddConvexRadius add_convex_b(inB, inConvexRadiusB); - TransformedConvexObject> transformed_a(inStart, add_convex_a); - if (!GetPenetrationDepthStepEPA(transformed_a, add_convex_b, inPenetrationTolerance, outContactNormal, outPointA, outPointB)) - return false; - } - else if (contact_normal_invalid) - { - // If we weren't able to calculate a contact normal, use the cast direction instead - outContactNormal = inDirection; - } - - return true; - } -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/Ellipse.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/Ellipse.h deleted file mode 100644 index bfa508faae8..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/Ellipse.h +++ /dev/null @@ -1,77 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -/// Ellipse centered around the origin -/// @see https://en.wikipedia.org/wiki/Ellipse -class Ellipse -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Construct ellipse with radius A along the X-axis and B along the Y-axis - Ellipse(float inA, float inB) : mA(inA), mB(inB) { JPH_ASSERT(inA > 0.0f); JPH_ASSERT(inB > 0.0f); } - - /// Check if inPoint is inside the ellipse - bool IsInside(const Float2 &inPoint) const - { - return Square(inPoint.x / mA) + Square(inPoint.y / mB) <= 1.0f; - } - - /// Get the closest point on the ellipse to inPoint - /// Assumes inPoint is outside the ellipse - /// @see Rotation Joint Limits in Quaternion Space by Gino van den Bergen, section 10.1 in Game Engine Gems 3. - Float2 GetClosestPoint(const Float2 &inPoint) const - { - float a_sq = Square(mA); - float b_sq = Square(mB); - - // Equation of ellipse: f(x, y) = (x/a)^2 + (y/b)^2 - 1 = 0 [1] - // Normal on surface: (df/dx, df/dy) = (2 x / a^2, 2 y / b^2) - // Closest point (x', y') on ellipse to point (x, y): (x', y') + t (x / a^2, y / b^2) = (x, y) - // <=> (x', y') = (a^2 x / (t + a^2), b^2 y / (t + b^2)) - // Requiring point to be on ellipse (substituting into [1]): g(t) = (a x / (t + a^2))^2 + (b y / (t + b^2))^2 - 1 = 0 - - // Newton raphson iteration, starting at t = 0 - float t = 0.0f; - for (;;) - { - // Calculate g(t) - float t_plus_a_sq = t + a_sq; - float t_plus_b_sq = t + b_sq; - float gt = Square(mA * inPoint.x / t_plus_a_sq) + Square(mB * inPoint.y / t_plus_b_sq) - 1.0f; - - // Check if g(t) it is close enough to zero - if (abs(gt) < 1.0e-6f) - return Float2(a_sq * inPoint.x / t_plus_a_sq, b_sq * inPoint.y / t_plus_b_sq); - - // Get derivative dg/dt = g'(t) = -2 (b^2 y^2 / (t + b^2)^3 + a^2 x^2 / (t + a^2)^3) - float gt_accent = -2.0f * - (a_sq * Square(inPoint.x) / Cubed(t_plus_a_sq) - + b_sq * Square(inPoint.y) / Cubed(t_plus_b_sq)); - - // Calculate t for next iteration: tn+1 = tn - g(t) / g'(t) - float tn = t - gt / gt_accent; - t = tn; - } - } - - /// Get normal at point inPoint (non-normalized vector) - Float2 GetNormal(const Float2 &inPoint) const - { - // Calculated by [d/dx f(x, y), d/dy f(x, y)], where f(x, y) is the ellipse equation from above - return Float2(inPoint.x / Square(mA), inPoint.y / Square(mB)); - } - -private: - float mA; ///< Radius along X-axis - float mB; ///< Radius along Y-axis -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/GJKClosestPoint.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/GJKClosestPoint.h deleted file mode 100644 index 2be8fec9f75..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/GJKClosestPoint.h +++ /dev/null @@ -1,952 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include -#include - -//#define JPH_GJK_DEBUG -#ifdef JPH_GJK_DEBUG - #include - #include -#endif - -JPH_NAMESPACE_BEGIN - -/// Convex vs convex collision detection -/// Based on: A Fast and Robust GJK Implementation for Collision Detection of Convex Objects - Gino van den Bergen -class GJKClosestPoint : public NonCopyable -{ -private: - /// Get new closest point to origin given simplex mY of mNumPoints points - /// - /// @param inPrevVLenSq Length of |outV|^2 from the previous iteration, used as a maximum value when selecting a new closest point. - /// @param outV Closest point - /// @param outVLenSq |outV|^2 - /// @param outSet Set of points that form the new simplex closest to the origin (bit 1 = mY[0], bit 2 = mY[1], ...) - /// - /// If LastPointPartOfClosestFeature is true then the last point added will be assumed to be part of the closest feature and the function will do less work. - /// - /// @return True if new closest point was found. - /// False if the function failed, in this case the output variables are not modified - template - bool GetClosest(float inPrevVLenSq, Vec3 &outV, float &outVLenSq, uint32 &outSet) const - { -#ifdef JPH_GJK_DEBUG - for (int i = 0; i < mNumPoints; ++i) - Trace("y[%d] = [%s], |y[%d]| = %g", i, ConvertToString(mY[i]).c_str(), i, (double)mY[i].Length()); -#endif - - uint32 set; - Vec3 v; - - switch (mNumPoints) - { - case 1: - // Single point - set = 0b0001; - v = mY[0]; - break; - - case 2: - // Line segment - v = ClosestPoint::GetClosestPointOnLine(mY[0], mY[1], set); - break; - - case 3: - // Triangle - v = ClosestPoint::GetClosestPointOnTriangle(mY[0], mY[1], mY[2], set); - break; - - case 4: - // Tetrahedron - v = ClosestPoint::GetClosestPointOnTetrahedron(mY[0], mY[1], mY[2], mY[3], set); - break; - - default: - JPH_ASSERT(false); - return false; - } - -#ifdef JPH_GJK_DEBUG - Trace("GetClosest: set = 0b%s, v = [%s], |v| = %g", NibbleToBinary(set), ConvertToString(v).c_str(), (double)v.Length()); -#endif - - float v_len_sq = v.LengthSq(); - if (v_len_sq < inPrevVLenSq) // Note, comparison order important: If v_len_sq is NaN then this expression will be false so we will return false - { - // Return closest point - outV = v; - outVLenSq = v_len_sq; - outSet = set; - return true; - } - - // No better match found -#ifdef JPH_GJK_DEBUG - Trace("New closer point is further away, failed to converge"); -#endif - return false; - } - - // Get max(|Y_0|^2 .. |Y_n|^2) - float GetMaxYLengthSq() const - { - float y_len_sq = mY[0].LengthSq(); - for (int i = 1; i < mNumPoints; ++i) - y_len_sq = max(y_len_sq, mY[i].LengthSq()); - return y_len_sq; - } - - // Remove points that are not in the set, only updates mY - void UpdatePointSetY(uint32 inSet) - { - int num_points = 0; - for (int i = 0; i < mNumPoints; ++i) - if ((inSet & (1 << i)) != 0) - { - mY[num_points] = mY[i]; - ++num_points; - } - mNumPoints = num_points; - } - - // GCC 11.3 thinks the assignments to mP, mQ and mY below may use uninitialized variables - JPH_SUPPRESS_WARNING_PUSH - JPH_GCC_SUPPRESS_WARNING("-Wmaybe-uninitialized") - - // Remove points that are not in the set, only updates mP - void UpdatePointSetP(uint32 inSet) - { - int num_points = 0; - for (int i = 0; i < mNumPoints; ++i) - if ((inSet & (1 << i)) != 0) - { - mP[num_points] = mP[i]; - ++num_points; - } - mNumPoints = num_points; - } - - // Remove points that are not in the set, only updates mP and mQ - void UpdatePointSetPQ(uint32 inSet) - { - int num_points = 0; - for (int i = 0; i < mNumPoints; ++i) - if ((inSet & (1 << i)) != 0) - { - mP[num_points] = mP[i]; - mQ[num_points] = mQ[i]; - ++num_points; - } - mNumPoints = num_points; - } - - // Remove points that are not in the set, updates mY, mP and mQ - void UpdatePointSetYPQ(uint32 inSet) - { - int num_points = 0; - for (int i = 0; i < mNumPoints; ++i) - if ((inSet & (1 << i)) != 0) - { - mY[num_points] = mY[i]; - mP[num_points] = mP[i]; - mQ[num_points] = mQ[i]; - ++num_points; - } - mNumPoints = num_points; - } - - JPH_SUPPRESS_WARNING_POP - - // Calculate closest points on A and B - void CalculatePointAAndB(Vec3 &outPointA, Vec3 &outPointB) const - { - switch (mNumPoints) - { - case 1: - outPointA = mP[0]; - outPointB = mQ[0]; - break; - - case 2: - { - float u, v; - ClosestPoint::GetBaryCentricCoordinates(mY[0], mY[1], u, v); - outPointA = u * mP[0] + v * mP[1]; - outPointB = u * mQ[0] + v * mQ[1]; - } - break; - - case 3: - { - float u, v, w; - ClosestPoint::GetBaryCentricCoordinates(mY[0], mY[1], mY[2], u, v, w); - outPointA = u * mP[0] + v * mP[1] + w * mP[2]; - outPointB = u * mQ[0] + v * mQ[1] + w * mQ[2]; - } - break; - - case 4: - #ifdef _DEBUG - memset(&outPointA, 0xcd, sizeof(outPointA)); - memset(&outPointB, 0xcd, sizeof(outPointB)); - #endif - break; - } - } - -public: - /// Test if inA and inB intersect - /// - /// @param inA The convex object A, must support the GetSupport(Vec3) function. - /// @param inB The convex object B, must support the GetSupport(Vec3) function. - /// @param inTolerance Minimal distance between objects when the objects are considered to be colliding - /// @param ioV is used as initial separating axis (provide a zero vector if you don't know yet) - /// - /// @return True if they intersect (in which case ioV = (0, 0, 0)). - /// False if they don't intersect in which case ioV is a separating axis in the direction from A to B (magnitude is meaningless) - template - bool Intersects(const A &inA, const B &inB, float inTolerance, Vec3 &ioV) - { - float tolerance_sq = Square(inTolerance); - - // Reset state - mNumPoints = 0; - -#ifdef JPH_GJK_DEBUG - for (int i = 0; i < 4; ++i) - mY[i] = Vec3::sZero(); -#endif - - // Previous length^2 of v - float prev_v_len_sq = FLT_MAX; - - for (;;) - { -#ifdef JPH_GJK_DEBUG - Trace("v = [%s], num_points = %d", ConvertToString(ioV).c_str(), mNumPoints); -#endif - - // Get support points for shape A and B in the direction of v - Vec3 p = inA.GetSupport(ioV); - Vec3 q = inB.GetSupport(-ioV); - - // Get support point of the minkowski sum A - B of v - Vec3 w = p - q; - - // If the support point sA-B(v) is in the opposite direction as v, then we have found a separating axis and there is no intersection - if (ioV.Dot(w) < 0.0f) - { - // Separating axis found -#ifdef JPH_GJK_DEBUG - Trace("Separating axis"); -#endif - return false; - } - - // Store the point for later use - mY[mNumPoints] = w; - ++mNumPoints; - -#ifdef JPH_GJK_DEBUG - Trace("w = [%s]", ConvertToString(w).c_str()); -#endif - - // Determine the new closest point - float v_len_sq; // Length^2 of v - uint32 set; // Set of points that form the new simplex - if (!GetClosest(prev_v_len_sq, ioV, v_len_sq, set)) - return false; - - // If there are 4 points, the origin is inside the tetrahedron and we're done - if (set == 0xf) - { -#ifdef JPH_GJK_DEBUG - Trace("Full simplex"); -#endif - ioV = Vec3::sZero(); - return true; - } - - // If v is very close to zero, we consider this a collision - if (v_len_sq <= tolerance_sq) - { -#ifdef JPH_GJK_DEBUG - Trace("Distance zero"); -#endif - ioV = Vec3::sZero(); - return true; - } - - // If v is very small compared to the length of y, we also consider this a collision - if (v_len_sq <= FLT_EPSILON * GetMaxYLengthSq()) - { -#ifdef JPH_GJK_DEBUG - Trace("Machine precision reached"); -#endif - ioV = Vec3::sZero(); - return true; - } - - // The next separation axis to test is the negative of the closest point of the Minkowski sum to the origin - // Note: This must be done before terminating as converged since the separating axis is -v - ioV = -ioV; - - // If the squared length of v is not changing enough, we've converged and there is no collision - JPH_ASSERT(prev_v_len_sq >= v_len_sq); - if (prev_v_len_sq - v_len_sq <= FLT_EPSILON * prev_v_len_sq) - { - // v is a separating axis -#ifdef JPH_GJK_DEBUG - Trace("Converged"); -#endif - return false; - } - prev_v_len_sq = v_len_sq; - - // Update the points of the simplex - UpdatePointSetY(set); - } - } - - /// Get closest points between inA and inB - /// - /// @param inA The convex object A, must support the GetSupport(Vec3) function. - /// @param inB The convex object B, must support the GetSupport(Vec3) function. - /// @param inTolerance The minimal distance between A and B before the objects are considered colliding and processing is terminated. - /// @param inMaxDistSq The maximum squared distance between A and B before the objects are considered infinitely far away and processing is terminated. - /// @param ioV Initial guess for the separating axis. Start with any non-zero vector if you don't know. - /// If return value is 0, ioV = (0, 0, 0). - /// If the return value is bigger than 0 but smaller than FLT_MAX, ioV will be the separating axis in the direction from A to B and its length the squared distance between A and B. - /// If the return value is FLT_MAX, ioV will be the separating axis in the direction from A to B and the magnitude of the vector is meaningless. - /// @param outPointA , outPointB - /// If the return value is 0 the points are invalid. - /// If the return value is bigger than 0 but smaller than FLT_MAX these will contain the closest point on A and B. - /// If the return value is FLT_MAX the points are invalid. - /// - /// @return The squared distance between A and B or FLT_MAX when they are further away than inMaxDistSq. - template - float GetClosestPoints(const A &inA, const B &inB, float inTolerance, float inMaxDistSq, Vec3 &ioV, Vec3 &outPointA, Vec3 &outPointB) - { - float tolerance_sq = Square(inTolerance); - - // Reset state - mNumPoints = 0; - -#ifdef JPH_GJK_DEBUG - // Generate the hull of the Minkowski difference for visualization - MinkowskiDifference diff(inA, inB); - mGeometry = DebugRenderer::sInstance->CreateTriangleGeometryForConvex([&diff](Vec3Arg inDirection) { return diff.GetSupport(inDirection); }); - - for (int i = 0; i < 4; ++i) - { - mY[i] = Vec3::sZero(); - mP[i] = Vec3::sZero(); - mQ[i] = Vec3::sZero(); - } -#endif - - // Length^2 of v - float v_len_sq = ioV.LengthSq(); - - // Previous length^2 of v - float prev_v_len_sq = FLT_MAX; - - for (;;) - { -#ifdef JPH_GJK_DEBUG - Trace("v = [%s], num_points = %d", ConvertToString(ioV).c_str(), mNumPoints); -#endif - - // Get support points for shape A and B in the direction of v - Vec3 p = inA.GetSupport(ioV); - Vec3 q = inB.GetSupport(-ioV); - - // Get support point of the minkowski sum A - B of v - Vec3 w = p - q; - - float dot = ioV.Dot(w); - -#ifdef JPH_GJK_DEBUG - // Draw -ioV to show the closest point to the origin from the previous simplex - DebugRenderer::sInstance->DrawArrow(mOffset, mOffset - ioV, Color::sOrange, 0.05f); - - // Draw ioV to show where we're probing next - DebugRenderer::sInstance->DrawArrow(mOffset, mOffset + ioV, Color::sCyan, 0.05f); - - // Draw w, the support point - DebugRenderer::sInstance->DrawArrow(mOffset, mOffset + w, Color::sGreen, 0.05f); - DebugRenderer::sInstance->DrawMarker(mOffset + w, Color::sGreen, 1.0f); - - // Draw the simplex and the Minkowski difference around it - DrawState(); -#endif - - // Test if we have a separation of more than inMaxDistSq, in which case we terminate early - if (dot < 0.0f && dot * dot > v_len_sq * inMaxDistSq) - { -#ifdef JPH_GJK_DEBUG - Trace("Distance bigger than max"); -#endif -#ifdef _DEBUG - memset(&outPointA, 0xcd, sizeof(outPointA)); - memset(&outPointB, 0xcd, sizeof(outPointB)); -#endif - return FLT_MAX; - } - - // Store the point for later use - mY[mNumPoints] = w; - mP[mNumPoints] = p; - mQ[mNumPoints] = q; - ++mNumPoints; - -#ifdef JPH_GJK_DEBUG - Trace("w = [%s]", ConvertToString(w).c_str()); -#endif - - uint32 set; - if (!GetClosest(prev_v_len_sq, ioV, v_len_sq, set)) - { - --mNumPoints; // Undo add last point - break; - } - - // If there are 4 points, the origin is inside the tetrahedron and we're done - if (set == 0xf) - { -#ifdef JPH_GJK_DEBUG - Trace("Full simplex"); -#endif - ioV = Vec3::sZero(); - v_len_sq = 0.0f; - break; - } - - // Update the points of the simplex - UpdatePointSetYPQ(set); - - // If v is very close to zero, we consider this a collision - if (v_len_sq <= tolerance_sq) - { -#ifdef JPH_GJK_DEBUG - Trace("Distance zero"); -#endif - ioV = Vec3::sZero(); - v_len_sq = 0.0f; - break; - } - - // If v is very small compared to the length of y, we also consider this a collision -#ifdef JPH_GJK_DEBUG - Trace("Check v small compared to y: %g <= %g", (double)v_len_sq, (double)(FLT_EPSILON * GetMaxYLengthSq())); -#endif - if (v_len_sq <= FLT_EPSILON * GetMaxYLengthSq()) - { -#ifdef JPH_GJK_DEBUG - Trace("Machine precision reached"); -#endif - ioV = Vec3::sZero(); - v_len_sq = 0.0f; - break; - } - - // The next separation axis to test is the negative of the closest point of the Minkowski sum to the origin - // Note: This must be done before terminating as converged since the separating axis is -v - ioV = -ioV; - - // If the squared length of v is not changing enough, we've converged and there is no collision -#ifdef JPH_GJK_DEBUG - Trace("Check v not changing enough: %g <= %g", (double)(prev_v_len_sq - v_len_sq), (double)(FLT_EPSILON * prev_v_len_sq)); -#endif - JPH_ASSERT(prev_v_len_sq >= v_len_sq); - if (prev_v_len_sq - v_len_sq <= FLT_EPSILON * prev_v_len_sq) - { - // v is a separating axis -#ifdef JPH_GJK_DEBUG - Trace("Converged"); -#endif - break; - } - prev_v_len_sq = v_len_sq; - } - - // Get the closest points - CalculatePointAAndB(outPointA, outPointB); - -#ifdef JPH_GJK_DEBUG - Trace("Return: v = [%s], |v| = %g", ConvertToString(ioV).c_str(), (double)ioV.Length()); - - // Draw -ioV to show the closest point to the origin from the previous simplex - DebugRenderer::sInstance->DrawArrow(mOffset, mOffset - ioV, Color::sOrange, 0.05f); - - // Draw the closest points - DebugRenderer::sInstance->DrawMarker(mOffset + outPointA, Color::sGreen, 1.0f); - DebugRenderer::sInstance->DrawMarker(mOffset + outPointB, Color::sPurple, 1.0f); - - // Draw the simplex and the Minkowski difference around it - DrawState(); -#endif - - JPH_ASSERT(ioV.LengthSq() == v_len_sq); - return v_len_sq; - } - - /// Get the resulting simplex after the GetClosestPoints algorithm finishes. - /// If it returned a squared distance of 0, the origin will be contained in the simplex. - void GetClosestPointsSimplex(Vec3 *outY, Vec3 *outP, Vec3 *outQ, uint &outNumPoints) const - { - uint size = sizeof(Vec3) * mNumPoints; - memcpy(outY, mY, size); - memcpy(outP, mP, size); - memcpy(outQ, mQ, size); - outNumPoints = mNumPoints; - } - - /// Test if a ray inRayOrigin + lambda * inRayDirection for lambda e [0, ioLambda> intersects inA - /// - /// Code based upon: Ray Casting against General Convex Objects with Application to Continuous Collision Detection - Gino van den Bergen - /// - /// @param inRayOrigin Origin of the ray - /// @param inRayDirection Direction of the ray (ioLambda * inDirection determines length) - /// @param inTolerance The minimal distance between the ray and A before it is considered colliding - /// @param inA A convex object that has the GetSupport(Vec3) function - /// @param ioLambda The max fraction along the ray, on output updated with the actual collision fraction. - /// - /// @return true if a hit was found, ioLambda is the solution for lambda. - template - bool CastRay(Vec3Arg inRayOrigin, Vec3Arg inRayDirection, float inTolerance, const A &inA, float &ioLambda) - { - float tolerance_sq = Square(inTolerance); - - // Reset state - mNumPoints = 0; - - float lambda = 0.0f; - Vec3 x = inRayOrigin; - Vec3 v = x - inA.GetSupport(Vec3::sZero()); - float v_len_sq = FLT_MAX; - bool allow_restart = false; - - for (;;) - { -#ifdef JPH_GJK_DEBUG - Trace("v = [%s], num_points = %d", ConvertToString(v).c_str(), mNumPoints); -#endif - - // Get new support point - Vec3 p = inA.GetSupport(v); - Vec3 w = x - p; - -#ifdef JPH_GJK_DEBUG - Trace("w = [%s]", ConvertToString(w).c_str()); -#endif - - float v_dot_w = v.Dot(w); -#ifdef JPH_GJK_DEBUG - Trace("v . w = %g", (double)v_dot_w); -#endif - if (v_dot_w > 0.0f) - { - // If ray and normal are in the same direction, we've passed A and there's no collision - float v_dot_r = v.Dot(inRayDirection); -#ifdef JPH_GJK_DEBUG - Trace("v . r = %g", (double)v_dot_r); -#endif - if (v_dot_r >= 0.0f) - return false; - - // Update the lower bound for lambda - float delta = v_dot_w / v_dot_r; - float old_lambda = lambda; - lambda -= delta; -#ifdef JPH_GJK_DEBUG - Trace("lambda = %g, delta = %g", (double)lambda, (double)delta); -#endif - - // If lambda didn't change, we cannot converge any further and we assume a hit - if (old_lambda == lambda) - break; - - // If lambda is bigger or equal than max, we don't have a hit - if (lambda >= ioLambda) - return false; - - // Update x to new closest point on the ray - x = inRayOrigin + lambda * inRayDirection; - - // We've shifted x, so reset v_len_sq so that it is not used as early out for GetClosest - v_len_sq = FLT_MAX; - - // We allow rebuilding the simplex once after x changes because the simplex was built - // for another x and numerical round off builds up as you keep adding points to an - // existing simplex - allow_restart = true; - } - - // Add p to set P: P = P U {p} - mP[mNumPoints] = p; - ++mNumPoints; - - // Calculate Y = {x} - P - for (int i = 0; i < mNumPoints; ++i) - mY[i] = x - mP[i]; - - // Determine the new closest point from Y to origin - uint32 set; // Set of points that form the new simplex - if (!GetClosest(v_len_sq, v, v_len_sq, set)) - { -#ifdef JPH_GJK_DEBUG - Trace("Failed to converge"); -#endif - - // Only allow 1 restart, if we still can't get a closest point - // we're so close that we return this as a hit - if (!allow_restart) - break; - - // If we fail to converge, we start again with the last point as simplex -#ifdef JPH_GJK_DEBUG - Trace("Restarting"); -#endif - allow_restart = false; - mP[0] = p; - mNumPoints = 1; - v = x - p; - v_len_sq = FLT_MAX; - continue; - } - else if (set == 0xf) - { -#ifdef JPH_GJK_DEBUG - Trace("Full simplex"); -#endif - - // We're inside the tetrahedron, we have a hit (verify that length of v is 0) - JPH_ASSERT(v_len_sq == 0.0f); - break; - } - - // Update the points P to form the new simplex - // Note: We're not updating Y as Y will shift with x so we have to calculate it every iteration - UpdatePointSetP(set); - - // Check if x is close enough to inA - if (v_len_sq <= tolerance_sq) - { -#ifdef JPH_GJK_DEBUG - Trace("Converged"); -#endif - break; - } - } - - // Store hit fraction - ioLambda = lambda; - return true; - } - - /// Test if a cast shape inA moving from inStart to lambda * inStart.GetTranslation() + inDirection where lambda e [0, ioLambda> intersects inB - /// - /// @param inStart Start position and orientation of the convex object - /// @param inDirection Direction of the sweep (ioLambda * inDirection determines length) - /// @param inTolerance The minimal distance between A and B before they are considered colliding - /// @param inA The convex object A, must support the GetSupport(Vec3) function. - /// @param inB The convex object B, must support the GetSupport(Vec3) function. - /// @param ioLambda The max fraction along the sweep, on output updated with the actual collision fraction. - /// - /// @return true if a hit was found, ioLambda is the solution for lambda. - template - bool CastShape(Mat44Arg inStart, Vec3Arg inDirection, float inTolerance, const A &inA, const B &inB, float &ioLambda) - { - // Transform the shape to be cast to the starting position - TransformedConvexObject transformed_a(inStart, inA); - - // Calculate the minkowski difference inB - inA - // inA is moving, so we need to add the back side of inB to the front side of inA - MinkowskiDifference difference(inB, transformed_a); - - // Do a raycast against the Minkowski difference - return CastRay(Vec3::sZero(), inDirection, inTolerance, difference, ioLambda); - } - - /// Test if a cast shape inA moving from inStart to lambda * inStart.GetTranslation() + inDirection where lambda e [0, ioLambda> intersects inB - /// - /// @param inStart Start position and orientation of the convex object - /// @param inDirection Direction of the sweep (ioLambda * inDirection determines length) - /// @param inTolerance The minimal distance between A and B before they are considered colliding - /// @param inA The convex object A, must support the GetSupport(Vec3) function. - /// @param inB The convex object B, must support the GetSupport(Vec3) function. - /// @param inConvexRadiusA The convex radius of A, this will be added on all sides to pad A. - /// @param inConvexRadiusB The convex radius of B, this will be added on all sides to pad B. - /// @param ioLambda The max fraction along the sweep, on output updated with the actual collision fraction. - /// @param outPointA is the contact point on A (if outSeparatingAxis is near zero, this may not be not the deepest point) - /// @param outPointB is the contact point on B (if outSeparatingAxis is near zero, this may not be not the deepest point) - /// @param outSeparatingAxis On return this will contain a vector that points from A to B along the smallest distance of separation. - /// The length of this vector indicates the separation of A and B without their convex radius. - /// If it is near zero, the direction may not be accurate as the bodies may overlap when lambda = 0. - /// - /// @return true if a hit was found, ioLambda is the solution for lambda and outPoint and outSeparatingAxis are valid. - template - bool CastShape(Mat44Arg inStart, Vec3Arg inDirection, float inTolerance, const A &inA, const B &inB, float inConvexRadiusA, float inConvexRadiusB, float &ioLambda, Vec3 &outPointA, Vec3 &outPointB, Vec3 &outSeparatingAxis) - { - float tolerance_sq = Square(inTolerance); - - // Calculate how close A and B (without their convex radius) need to be to each other in order for us to consider this a collision - float sum_convex_radius = inConvexRadiusA + inConvexRadiusB; - - // Transform the shape to be cast to the starting position - TransformedConvexObject transformed_a(inStart, inA); - - // Reset state - mNumPoints = 0; - - float lambda = 0.0f; - Vec3 x = Vec3::sZero(); // Since A is already transformed we can start the cast from zero - Vec3 v = -inB.GetSupport(Vec3::sZero()) + transformed_a.GetSupport(Vec3::sZero()); // See CastRay: v = x - inA.GetSupport(Vec3::sZero()) where inA is the Minkowski difference inB - transformed_a (see CastShape above) and x is zero - float v_len_sq = FLT_MAX; - bool allow_restart = false; - - // Keeps track of separating axis of the previous iteration. - // Initialized at zero as we don't know if our first v is actually a separating axis. - Vec3 prev_v = Vec3::sZero(); - - for (;;) - { -#ifdef JPH_GJK_DEBUG - Trace("v = [%s], num_points = %d", ConvertToString(v).c_str(), mNumPoints); -#endif - - // Calculate the minkowski difference inB - inA - // inA is moving, so we need to add the back side of inB to the front side of inA - // Keep the support points on A and B separate so that in the end we can calculate a contact point - Vec3 p = transformed_a.GetSupport(-v); - Vec3 q = inB.GetSupport(v); - Vec3 w = x - (q - p); - -#ifdef JPH_GJK_DEBUG - Trace("w = [%s]", ConvertToString(w).c_str()); -#endif - - // Difference from article to this code: - // We did not include the convex radius in p and q in order to be able to calculate a good separating axis at the end of the algorithm. - // However when moving forward along inDirection we do need to take this into account so that we keep A and B separated by the sum of their convex radii. - // From p we have to subtract: inConvexRadiusA * v / |v| - // To q we have to add: inConvexRadiusB * v / |v| - // This means that to w we have to add: -(inConvexRadiusA + inConvexRadiusB) * v / |v| - // So to v . w we have to add: v . (-(inConvexRadiusA + inConvexRadiusB) * v / |v|) = -(inConvexRadiusA + inConvexRadiusB) * |v| - float v_dot_w = v.Dot(w) - sum_convex_radius * v.Length(); -#ifdef JPH_GJK_DEBUG - Trace("v . w = %g", (double)v_dot_w); -#endif - if (v_dot_w > 0.0f) - { - // If ray and normal are in the same direction, we've passed A and there's no collision - float v_dot_r = v.Dot(inDirection); -#ifdef JPH_GJK_DEBUG - Trace("v . r = %g", (double)v_dot_r); -#endif - if (v_dot_r >= 0.0f) - return false; - - // Update the lower bound for lambda - float delta = v_dot_w / v_dot_r; - float old_lambda = lambda; - lambda -= delta; -#ifdef JPH_GJK_DEBUG - Trace("lambda = %g, delta = %g", (double)lambda, (double)delta); -#endif - - // If lambda didn't change, we cannot converge any further and we assume a hit - if (old_lambda == lambda) - break; - - // If lambda is bigger or equal than max, we don't have a hit - if (lambda >= ioLambda) - return false; - - // Update x to new closest point on the ray - x = lambda * inDirection; - - // We've shifted x, so reset v_len_sq so that it is not used as early out when GetClosest returns false - v_len_sq = FLT_MAX; - - // Now that we've moved, we know that A and B are not intersecting at lambda = 0, so we can update our tolerance to stop iterating - // as soon as A and B are inConvexRadiusA + inConvexRadiusB apart - tolerance_sq = Square(inTolerance + sum_convex_radius); - - // We allow rebuilding the simplex once after x changes because the simplex was built - // for another x and numerical round off builds up as you keep adding points to an - // existing simplex - allow_restart = true; - } - - // Add p to set P, q to set Q: P = P U {p}, Q = Q U {q} - mP[mNumPoints] = p; - mQ[mNumPoints] = q; - ++mNumPoints; - - // Calculate Y = {x} - (Q - P) - for (int i = 0; i < mNumPoints; ++i) - mY[i] = x - (mQ[i] - mP[i]); - - // Determine the new closest point from Y to origin - uint32 set; // Set of points that form the new simplex - if (!GetClosest(v_len_sq, v, v_len_sq, set)) - { -#ifdef JPH_GJK_DEBUG - Trace("Failed to converge"); -#endif - - // Only allow 1 restart, if we still can't get a closest point - // we're so close that we return this as a hit - if (!allow_restart) - break; - - // If we fail to converge, we start again with the last point as simplex -#ifdef JPH_GJK_DEBUG - Trace("Restarting"); -#endif - allow_restart = false; - mP[0] = p; - mQ[0] = q; - mNumPoints = 1; - v = x - q; - v_len_sq = FLT_MAX; - continue; - } - else if (set == 0xf) - { -#ifdef JPH_GJK_DEBUG - Trace("Full simplex"); -#endif - - // We're inside the tetrahedron, we have a hit (verify that length of v is 0) - JPH_ASSERT(v_len_sq == 0.0f); - break; - } - - // Update the points P and Q to form the new simplex - // Note: We're not updating Y as Y will shift with x so we have to calculate it every iteration - UpdatePointSetPQ(set); - - // Check if A and B are touching according to our tolerance - if (v_len_sq <= tolerance_sq) - { -#ifdef JPH_GJK_DEBUG - Trace("Converged"); -#endif - break; - } - - // Store our v to return as separating axis - prev_v = v; - } - - // Calculate Y = {x} - (Q - P) again so we can calculate the contact points - for (int i = 0; i < mNumPoints; ++i) - mY[i] = x - (mQ[i] - mP[i]); - - // Calculate the offset we need to apply to A and B to correct for the convex radius - Vec3 normalized_v = v.NormalizedOr(Vec3::sZero()); - Vec3 convex_radius_a = inConvexRadiusA * normalized_v; - Vec3 convex_radius_b = inConvexRadiusB * normalized_v; - - // Get the contact point - // Note that A and B will coincide when lambda > 0. In this case we calculate only B as it is more accurate as it contains less terms. - switch (mNumPoints) - { - case 1: - outPointB = mQ[0] + convex_radius_b; - outPointA = lambda > 0.0f? outPointB : mP[0] - convex_radius_a; - break; - - case 2: - { - float bu, bv; - ClosestPoint::GetBaryCentricCoordinates(mY[0], mY[1], bu, bv); - outPointB = bu * mQ[0] + bv * mQ[1] + convex_radius_b; - outPointA = lambda > 0.0f? outPointB : bu * mP[0] + bv * mP[1] - convex_radius_a; - } - break; - - case 3: - case 4: // A full simplex, we can't properly determine a contact point! As contact point we take the closest point of the previous iteration. - { - float bu, bv, bw; - ClosestPoint::GetBaryCentricCoordinates(mY[0], mY[1], mY[2], bu, bv, bw); - outPointB = bu * mQ[0] + bv * mQ[1] + bw * mQ[2] + convex_radius_b; - outPointA = lambda > 0.0f? outPointB : bu * mP[0] + bv * mP[1] + bw * mP[2] - convex_radius_a; - } - break; - } - - // Store separating axis, in case we have a convex radius we can just return v, - // otherwise v will be very small and we resort to returning previous v as an approximation. - outSeparatingAxis = sum_convex_radius > 0.0f? -v : -prev_v; - - // Store hit fraction - ioLambda = lambda; - return true; - } - -private: -#ifdef JPH_GJK_DEBUG - /// Draw state of algorithm - void DrawState() - { - RMat44 origin = RMat44::sTranslation(mOffset); - - // Draw origin - DebugRenderer::sInstance->DrawCoordinateSystem(origin, 1.0f); - - // Draw the hull - DebugRenderer::sInstance->DrawGeometry(origin, mGeometry->mBounds.Transformed(origin), mGeometry->mBounds.GetExtent().LengthSq(), Color::sYellow, mGeometry); - - // Draw Y - for (int i = 0; i < mNumPoints; ++i) - { - // Draw support point - RVec3 y_i = origin * mY[i]; - DebugRenderer::sInstance->DrawMarker(y_i, Color::sRed, 1.0f); - for (int j = i + 1; j < mNumPoints; ++j) - { - // Draw edge - RVec3 y_j = origin * mY[j]; - DebugRenderer::sInstance->DrawLine(y_i, y_j, Color::sRed); - for (int k = j + 1; k < mNumPoints; ++k) - { - // Make sure triangle faces the origin - RVec3 y_k = origin * mY[k]; - RVec3 center = (y_i + y_j + y_k) / Real(3); - RVec3 normal = (y_j - y_i).Cross(y_k - y_i); - if (normal.Dot(center) < Real(0)) - DebugRenderer::sInstance->DrawTriangle(y_i, y_j, y_k, Color::sLightGrey); - else - DebugRenderer::sInstance->DrawTriangle(y_i, y_k, y_j, Color::sLightGrey); - } - } - } - - // Offset to the right - mOffset += Vec3(mGeometry->mBounds.GetSize().GetX() + 2.0f, 0, 0); - } -#endif // JPH_GJK_DEBUG - - Vec3 mY[4]; ///< Support points on A - B - Vec3 mP[4]; ///< Support point on A - Vec3 mQ[4]; ///< Support point on B - int mNumPoints = 0; ///< Number of points in mY, mP and mQ that are valid - -#ifdef JPH_GJK_DEBUG - DebugRenderer::GeometryRef mGeometry; ///< A visualization of the minkowski difference for state drawing - RVec3 mOffset = RVec3::sZero(); ///< Offset to use for state drawing -#endif -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/IndexedTriangle.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/IndexedTriangle.h deleted file mode 100644 index 0275fccade0..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/IndexedTriangle.h +++ /dev/null @@ -1,115 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -/// Triangle with 32-bit indices -class IndexedTriangleNoMaterial -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Constructor - IndexedTriangleNoMaterial() = default; - constexpr IndexedTriangleNoMaterial(uint32 inI1, uint32 inI2, uint32 inI3) : mIdx { inI1, inI2, inI3 } { } - - /// Check if two triangles are identical - bool operator == (const IndexedTriangleNoMaterial &inRHS) const - { - return mIdx[0] == inRHS.mIdx[0] && mIdx[1] == inRHS.mIdx[1] && mIdx[2] == inRHS.mIdx[2]; - } - - /// Check if two triangles are equivalent (using the same vertices) - bool IsEquivalent(const IndexedTriangleNoMaterial &inRHS) const - { - return (mIdx[0] == inRHS.mIdx[0] && mIdx[1] == inRHS.mIdx[1] && mIdx[2] == inRHS.mIdx[2]) - || (mIdx[0] == inRHS.mIdx[1] && mIdx[1] == inRHS.mIdx[2] && mIdx[2] == inRHS.mIdx[0]) - || (mIdx[0] == inRHS.mIdx[2] && mIdx[1] == inRHS.mIdx[0] && mIdx[2] == inRHS.mIdx[1]); - } - - /// Check if two triangles are opposite (using the same vertices but in opposing order) - bool IsOpposite(const IndexedTriangleNoMaterial &inRHS) const - { - return (mIdx[0] == inRHS.mIdx[0] && mIdx[1] == inRHS.mIdx[2] && mIdx[2] == inRHS.mIdx[1]) - || (mIdx[0] == inRHS.mIdx[1] && mIdx[1] == inRHS.mIdx[0] && mIdx[2] == inRHS.mIdx[2]) - || (mIdx[0] == inRHS.mIdx[2] && mIdx[1] == inRHS.mIdx[1] && mIdx[2] == inRHS.mIdx[0]); - } - - /// Check if triangle is degenerate - bool IsDegenerate(const VertexList &inVertices) const - { - Vec3 v0(inVertices[mIdx[0]]); - Vec3 v1(inVertices[mIdx[1]]); - Vec3 v2(inVertices[mIdx[2]]); - - return (v1 - v0).Cross(v2 - v0).IsNearZero(); - } - - /// Rotate the vertices so that the second vertex becomes first etc. This does not change the represented triangle. - void Rotate() - { - uint32 tmp = mIdx[0]; - mIdx[0] = mIdx[1]; - mIdx[1] = mIdx[2]; - mIdx[2] = tmp; - } - - /// Get center of triangle - Vec3 GetCentroid(const VertexList &inVertices) const - { - return (Vec3(inVertices[mIdx[0]]) + Vec3(inVertices[mIdx[1]]) + Vec3(inVertices[mIdx[2]])) / 3.0f; - } - - uint32 mIdx[3]; -}; - -/// Triangle with 32-bit indices and material index -class IndexedTriangle : public IndexedTriangleNoMaterial -{ -public: - using IndexedTriangleNoMaterial::IndexedTriangleNoMaterial; - - /// Constructor - constexpr IndexedTriangle(uint32 inI1, uint32 inI2, uint32 inI3, uint32 inMaterialIndex) : IndexedTriangleNoMaterial(inI1, inI2, inI3), mMaterialIndex(inMaterialIndex) { } - - /// Check if two triangles are identical - bool operator == (const IndexedTriangle &inRHS) const - { - return mMaterialIndex == inRHS.mMaterialIndex && IndexedTriangleNoMaterial::operator==(inRHS); - } - - /// Rotate the vertices so that the lowest vertex becomes the first. This does not change the represented triangle. - IndexedTriangle GetLowestIndexFirst() const - { - if (mIdx[0] < mIdx[1]) - { - if (mIdx[0] < mIdx[2]) - return IndexedTriangle(mIdx[0], mIdx[1], mIdx[2], mMaterialIndex); // 0 is smallest - else - return IndexedTriangle(mIdx[2], mIdx[0], mIdx[1], mMaterialIndex); // 2 is smallest - } - else - { - if (mIdx[1] < mIdx[2]) - return IndexedTriangle(mIdx[1], mIdx[2], mIdx[0], mMaterialIndex); // 1 is smallest - else - return IndexedTriangle(mIdx[2], mIdx[0], mIdx[1], mMaterialIndex); // 2 is smallest - } - } - - uint32 mMaterialIndex = 0; -}; - -using IndexedTriangleNoMaterialList = Array; -using IndexedTriangleList = Array; - -JPH_NAMESPACE_END - -// Create a std::hash for IndexedTriangleNoMaterial and IndexedTriangle -JPH_MAKE_HASHABLE(JPH::IndexedTriangleNoMaterial, t.mIdx[0], t.mIdx[1], t.mIdx[2]) -JPH_MAKE_HASHABLE(JPH::IndexedTriangle, t.mIdx[0], t.mIdx[1], t.mIdx[2], t.mMaterialIndex) diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/Indexify.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/Indexify.cpp deleted file mode 100644 index ac0ff90a580..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/Indexify.cpp +++ /dev/null @@ -1,218 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include - -JPH_NAMESPACE_BEGIN - -static JPH_INLINE const Float3 &sIndexifyGetFloat3(const TriangleList &inTriangles, uint32 inVertexIndex) -{ - return inTriangles[inVertexIndex / 3].mV[inVertexIndex % 3]; -} - -static JPH_INLINE Vec3 sIndexifyGetVec3(const TriangleList &inTriangles, uint32 inVertexIndex) -{ - return Vec3::sLoadFloat3Unsafe(sIndexifyGetFloat3(inTriangles, inVertexIndex)); -} - -static void sIndexifyVerticesBruteForce(const TriangleList &inTriangles, const uint32 *inVertexIndices, const uint32 *inVertexIndicesEnd, Array &ioWeldedVertices, float inVertexWeldDistance) -{ - float weld_dist_sq = Square(inVertexWeldDistance); - - // Compare every vertex - for (const uint32 *v1_idx = inVertexIndices; v1_idx < inVertexIndicesEnd; ++v1_idx) - { - Vec3 v1 = sIndexifyGetVec3(inTriangles, *v1_idx); - - // with every other vertex... - for (const uint32 *v2_idx = v1_idx + 1; v2_idx < inVertexIndicesEnd; ++v2_idx) - { - Vec3 v2 = sIndexifyGetVec3(inTriangles, *v2_idx); - - // If they're weldable - if ((v2 - v1).LengthSq() <= weld_dist_sq) - { - // Find the lowest indices both indices link to - uint32 idx1 = *v1_idx; - for (;;) - { - uint32 new_idx1 = ioWeldedVertices[idx1]; - if (new_idx1 >= idx1) - break; - idx1 = new_idx1; - } - uint32 idx2 = *v2_idx; - for (;;) - { - uint32 new_idx2 = ioWeldedVertices[idx2]; - if (new_idx2 >= idx2) - break; - idx2 = new_idx2; - } - - // Order the vertices - uint32 lowest = min(idx1, idx2); - uint32 highest = max(idx1, idx2); - - // Link highest to lowest - ioWeldedVertices[highest] = lowest; - - // Also update the vertices we started from to avoid creating long chains - ioWeldedVertices[*v1_idx] = lowest; - ioWeldedVertices[*v2_idx] = lowest; - break; - } - } - } -} - -static void sIndexifyVerticesRecursively(const TriangleList &inTriangles, uint32 *ioVertexIndices, uint inNumVertices, uint32 *ioScratch, Array &ioWeldedVertices, float inVertexWeldDistance, uint inMaxRecursion) -{ - // Check if we have few enough vertices to do a brute force search - // Or if we've recursed too deep (this means we chipped off a few vertices each iteration because all points are very close) - if (inNumVertices <= 8 || inMaxRecursion == 0) - { - sIndexifyVerticesBruteForce(inTriangles, ioVertexIndices, ioVertexIndices + inNumVertices, ioWeldedVertices, inVertexWeldDistance); - return; - } - - // Calculate bounds - AABox bounds; - for (const uint32 *v = ioVertexIndices, *v_end = ioVertexIndices + inNumVertices; v < v_end; ++v) - bounds.Encapsulate(sIndexifyGetVec3(inTriangles, *v)); - - // Determine split plane - int split_axis = bounds.GetExtent().GetHighestComponentIndex(); - float split_value = bounds.GetCenter()[split_axis]; - - // Partition vertices - uint32 *v_read = ioVertexIndices, *v_write = ioVertexIndices, *v_end = ioVertexIndices + inNumVertices; - uint32 *scratch = ioScratch; - while (v_read < v_end) - { - // Calculate distance to plane - float distance_to_split_plane = sIndexifyGetFloat3(inTriangles, *v_read)[split_axis] - split_value; - if (distance_to_split_plane < -inVertexWeldDistance) - { - // Vertex is on the right side - *v_write = *v_read; - ++v_read; - ++v_write; - } - else if (distance_to_split_plane > inVertexWeldDistance) - { - // Vertex is on the wrong side, swap with the last vertex - --v_end; - swap(*v_read, *v_end); - } - else - { - // Vertex is too close to the split plane, it goes on both sides - *scratch++ = *v_read++; - } - } - - // Check if we made any progress - uint num_vertices_on_both_sides = (uint)(scratch - ioScratch); - if (num_vertices_on_both_sides == inNumVertices) - { - sIndexifyVerticesBruteForce(inTriangles, ioVertexIndices, ioVertexIndices + inNumVertices, ioWeldedVertices, inVertexWeldDistance); - return; - } - - // Calculate how we classified the vertices - uint num_vertices_left = (uint)(v_write - ioVertexIndices); - uint num_vertices_right = (uint)(ioVertexIndices + inNumVertices - v_end); - JPH_ASSERT(num_vertices_left + num_vertices_right + num_vertices_on_both_sides == inNumVertices); - memcpy(v_write, ioScratch, num_vertices_on_both_sides * sizeof(uint32)); - - // Recurse - uint max_recursion = inMaxRecursion - 1; - sIndexifyVerticesRecursively(inTriangles, ioVertexIndices, num_vertices_left + num_vertices_on_both_sides, ioScratch, ioWeldedVertices, inVertexWeldDistance, max_recursion); - sIndexifyVerticesRecursively(inTriangles, ioVertexIndices + num_vertices_left, num_vertices_right + num_vertices_on_both_sides, ioScratch, ioWeldedVertices, inVertexWeldDistance, max_recursion); -} - -void Indexify(const TriangleList &inTriangles, VertexList &outVertices, IndexedTriangleList &outTriangles, float inVertexWeldDistance) -{ - uint num_triangles = (uint)inTriangles.size(); - uint num_vertices = num_triangles * 3; - - // Create a list of all vertex indices - Array vertex_indices; - vertex_indices.resize(num_vertices); - for (uint i = 0; i < num_vertices; ++i) - vertex_indices[i] = i; - - // Link each vertex to itself - Array welded_vertices; - welded_vertices.resize(num_vertices); - for (uint i = 0; i < num_vertices; ++i) - welded_vertices[i] = i; - - // A scope to free memory used by the scratch array - { - // Some scratch memory, used for the vertices that fall in both partitions - Array scratch; - scratch.resize(num_vertices); - - // Recursively split the vertices - sIndexifyVerticesRecursively(inTriangles, vertex_indices.data(), num_vertices, scratch.data(), welded_vertices, inVertexWeldDistance, 32); - } - - // Do a pass to complete the welding, linking each vertex to the vertex it is welded to - // (and since we're going from 0 to N we can be sure that the vertex we're linking to is already linked to the lowest vertex) - uint num_resulting_vertices = 0; - for (uint i = 0; i < num_vertices; ++i) - { - JPH_ASSERT(welded_vertices[welded_vertices[i]] <= welded_vertices[i]); - welded_vertices[i] = welded_vertices[welded_vertices[i]]; - if (welded_vertices[i] == i) - ++num_resulting_vertices; - } - - // Collect the vertices - outVertices.clear(); - outVertices.reserve(num_resulting_vertices); - for (uint i = 0; i < num_vertices; ++i) - if (welded_vertices[i] == i) - { - // New vertex - welded_vertices[i] = (uint32)outVertices.size(); - outVertices.push_back(sIndexifyGetFloat3(inTriangles, i)); - } - else - { - // Reused vertex, remap index - welded_vertices[i] = welded_vertices[welded_vertices[i]]; - } - - // Create indexed triangles - outTriangles.clear(); - outTriangles.reserve(num_triangles); - for (uint t = 0; t < num_triangles; ++t) - { - IndexedTriangle it; - it.mMaterialIndex = inTriangles[t].mMaterialIndex; - for (int v = 0; v < 3; ++v) - it.mIdx[v] = welded_vertices[t * 3 + v]; - if (!it.IsDegenerate(outVertices)) - outTriangles.push_back(it); - } -} - -void Deindexify(const VertexList &inVertices, const IndexedTriangleList &inTriangles, TriangleList &outTriangles) -{ - outTriangles.resize(inTriangles.size()); - for (size_t t = 0; t < inTriangles.size(); ++t) - { - outTriangles[t].mMaterialIndex = inTriangles[t].mMaterialIndex; - for (int v = 0; v < 3; ++v) - outTriangles[t].mV[v] = inVertices[inTriangles[t].mIdx[v]]; - } -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/Indexify.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/Indexify.h deleted file mode 100644 index 01fb805305d..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/Indexify.h +++ /dev/null @@ -1,19 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include - -JPH_NAMESPACE_BEGIN - -/// Take a list of triangles and get the unique set of vertices and use them to create indexed triangles. -/// Vertices that are less than inVertexWeldDistance apart will be combined to a single vertex. -JPH_EXPORT void Indexify(const TriangleList &inTriangles, VertexList &outVertices, IndexedTriangleList &outTriangles, float inVertexWeldDistance = 1.0e-4f); - -/// Take a list of indexed triangles and unpack them -JPH_EXPORT void Deindexify(const VertexList &inVertices, const IndexedTriangleList &inTriangles, TriangleList &outTriangles); - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/MortonCode.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/MortonCode.h deleted file mode 100644 index e750d7e6d1b..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/MortonCode.h +++ /dev/null @@ -1,40 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -class MortonCode -{ -public: - /// First converts a floating point value in the range [0, 1] to a 10 bit fixed point integer. - /// Then expands a 10-bit integer into 30 bits by inserting 2 zeros after each bit. - static uint32 sExpandBits(float inV) - { - JPH_ASSERT(inV >= 0.0f && inV <= 1.0f); - uint32 v = uint32(inV * 1023.0f + 0.5f); - JPH_ASSERT(v < 1024); - v = (v * 0x00010001u) & 0xFF0000FFu; - v = (v * 0x00000101u) & 0x0F00F00Fu; - v = (v * 0x00000011u) & 0xC30C30C3u; - v = (v * 0x00000005u) & 0x49249249u; - return v; - } - - /// Calculate the morton code for inVector, given that all vectors lie in inVectorBounds - static uint32 sGetMortonCode(Vec3Arg inVector, const AABox &inVectorBounds) - { - // Convert to 10 bit fixed point - Vec3 scaled = (inVector - inVectorBounds.mMin) / inVectorBounds.GetSize(); - uint x = sExpandBits(scaled.GetX()); - uint y = sExpandBits(scaled.GetY()); - uint z = sExpandBits(scaled.GetZ()); - return (x << 2) + (y << 1) + z; - } -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/OrientedBox.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/OrientedBox.cpp deleted file mode 100644 index cbd5ab17f18..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/OrientedBox.cpp +++ /dev/null @@ -1,178 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include - -JPH_NAMESPACE_BEGIN - -bool OrientedBox::Overlaps(const AABox &inBox, float inEpsilon) const -{ - // Taken from: Real Time Collision Detection - Christer Ericson - // Chapter 4.4.1, page 103-105. - // Note that the code is swapped around: A is the aabox and B is the oriented box (this saves us from having to invert the orientation of the oriented box) - - // Convert AABox to center / extent representation - Vec3 a_center = inBox.GetCenter(); - Vec3 a_half_extents = inBox.GetExtent(); - - // Compute rotation matrix expressing b in a's coordinate frame - Mat44 rot(mOrientation.GetColumn4(0), mOrientation.GetColumn4(1), mOrientation.GetColumn4(2), mOrientation.GetColumn4(3) - Vec4(a_center, 0)); - - // Compute common subexpressions. Add in an epsilon term to - // counteract arithmetic errors when two edges are parallel and - // their cross product is (near) null (see text for details) - Vec3 epsilon = Vec3::sReplicate(inEpsilon); - Vec3 abs_r[3] { rot.GetAxisX().Abs() + epsilon, rot.GetAxisY().Abs() + epsilon, rot.GetAxisZ().Abs() + epsilon }; - - // Test axes L = A0, L = A1, L = A2 - float ra, rb; - for (int i = 0; i < 3; i++) - { - ra = a_half_extents[i]; - rb = mHalfExtents[0] * abs_r[0][i] + mHalfExtents[1] * abs_r[1][i] + mHalfExtents[2] * abs_r[2][i]; - if (abs(rot(i, 3)) > ra + rb) return false; - } - - // Test axes L = B0, L = B1, L = B2 - for (int i = 0; i < 3; i++) - { - ra = a_half_extents.Dot(abs_r[i]); - rb = mHalfExtents[i]; - if (abs(rot.GetTranslation().Dot(rot.GetColumn3(i))) > ra + rb) return false; - } - - // Test axis L = A0 x B0 - ra = a_half_extents[1] * abs_r[0][2] + a_half_extents[2] * abs_r[0][1]; - rb = mHalfExtents[1] * abs_r[2][0] + mHalfExtents[2] * abs_r[1][0]; - if (abs(rot(2, 3) * rot(1, 0) - rot(1, 3) * rot(2, 0)) > ra + rb) return false; - - // Test axis L = A0 x B1 - ra = a_half_extents[1] * abs_r[1][2] + a_half_extents[2] * abs_r[1][1]; - rb = mHalfExtents[0] * abs_r[2][0] + mHalfExtents[2] * abs_r[0][0]; - if (abs(rot(2, 3) * rot(1, 1) - rot(1, 3) * rot(2, 1)) > ra + rb) return false; - - // Test axis L = A0 x B2 - ra = a_half_extents[1] * abs_r[2][2] + a_half_extents[2] * abs_r[2][1]; - rb = mHalfExtents[0] * abs_r[1][0] + mHalfExtents[1] * abs_r[0][0]; - if (abs(rot(2, 3) * rot(1, 2) - rot(1, 3) * rot(2, 2)) > ra + rb) return false; - - // Test axis L = A1 x B0 - ra = a_half_extents[0] * abs_r[0][2] + a_half_extents[2] * abs_r[0][0]; - rb = mHalfExtents[1] * abs_r[2][1] + mHalfExtents[2] * abs_r[1][1]; - if (abs(rot(0, 3) * rot(2, 0) - rot(2, 3) * rot(0, 0)) > ra + rb) return false; - - // Test axis L = A1 x B1 - ra = a_half_extents[0] * abs_r[1][2] + a_half_extents[2] * abs_r[1][0]; - rb = mHalfExtents[0] * abs_r[2][1] + mHalfExtents[2] * abs_r[0][1]; - if (abs(rot(0, 3) * rot(2, 1) - rot(2, 3) * rot(0, 1)) > ra + rb) return false; - - // Test axis L = A1 x B2 - ra = a_half_extents[0] * abs_r[2][2] + a_half_extents[2] * abs_r[2][0]; - rb = mHalfExtents[0] * abs_r[1][1] + mHalfExtents[1] * abs_r[0][1]; - if (abs(rot(0, 3) * rot(2, 2) - rot(2, 3) * rot(0, 2)) > ra + rb) return false; - - // Test axis L = A2 x B0 - ra = a_half_extents[0] * abs_r[0][1] + a_half_extents[1] * abs_r[0][0]; - rb = mHalfExtents[1] * abs_r[2][2] + mHalfExtents[2] * abs_r[1][2]; - if (abs(rot(1, 3) * rot(0, 0) - rot(0, 3) * rot(1, 0)) > ra + rb) return false; - - // Test axis L = A2 x B1 - ra = a_half_extents[0] * abs_r[1][1] + a_half_extents[1] * abs_r[1][0]; - rb = mHalfExtents[0] * abs_r[2][2] + mHalfExtents[2] * abs_r[0][2]; - if (abs(rot(1, 3) * rot(0, 1) - rot(0, 3) * rot(1, 1)) > ra + rb) return false; - - // Test axis L = A2 x B2 - ra = a_half_extents[0] * abs_r[2][1] + a_half_extents[1] * abs_r[2][0]; - rb = mHalfExtents[0] * abs_r[1][2] + mHalfExtents[1] * abs_r[0][2]; - if (abs(rot(1, 3) * rot(0, 2) - rot(0, 3) * rot(1, 2)) > ra + rb) return false; - - // Since no separating axis is found, the OBB and AAB must be intersecting - return true; -} - -bool OrientedBox::Overlaps(const OrientedBox &inBox, float inEpsilon) const -{ - // Taken from: Real Time Collision Detection - Christer Ericson - // Chapter 4.4.1, page 103-105. - // Note that A is this, B is inBox - - // Compute rotation matrix expressing b in a's coordinate frame - Mat44 rot = mOrientation.InversedRotationTranslation() * inBox.mOrientation; - - // Compute common subexpressions. Add in an epsilon term to - // counteract arithmetic errors when two edges are parallel and - // their cross product is (near) null (see text for details) - Vec3 epsilon = Vec3::sReplicate(inEpsilon); - Vec3 abs_r[3] { rot.GetAxisX().Abs() + epsilon, rot.GetAxisY().Abs() + epsilon, rot.GetAxisZ().Abs() + epsilon }; - - // Test axes L = A0, L = A1, L = A2 - float ra, rb; - for (int i = 0; i < 3; i++) - { - ra = mHalfExtents[i]; - rb = inBox.mHalfExtents[0] * abs_r[0][i] + inBox.mHalfExtents[1] * abs_r[1][i] + inBox.mHalfExtents[2] * abs_r[2][i]; - if (abs(rot(i, 3)) > ra + rb) return false; - } - - // Test axes L = B0, L = B1, L = B2 - for (int i = 0; i < 3; i++) - { - ra = mHalfExtents.Dot(abs_r[i]); - rb = inBox.mHalfExtents[i]; - if (abs(rot.GetTranslation().Dot(rot.GetColumn3(i))) > ra + rb) return false; - } - - // Test axis L = A0 x B0 - ra = mHalfExtents[1] * abs_r[0][2] + mHalfExtents[2] * abs_r[0][1]; - rb = inBox.mHalfExtents[1] * abs_r[2][0] + inBox.mHalfExtents[2] * abs_r[1][0]; - if (abs(rot(2, 3) * rot(1, 0) - rot(1, 3) * rot(2, 0)) > ra + rb) return false; - - // Test axis L = A0 x B1 - ra = mHalfExtents[1] * abs_r[1][2] + mHalfExtents[2] * abs_r[1][1]; - rb = inBox.mHalfExtents[0] * abs_r[2][0] + inBox.mHalfExtents[2] * abs_r[0][0]; - if (abs(rot(2, 3) * rot(1, 1) - rot(1, 3) * rot(2, 1)) > ra + rb) return false; - - // Test axis L = A0 x B2 - ra = mHalfExtents[1] * abs_r[2][2] + mHalfExtents[2] * abs_r[2][1]; - rb = inBox.mHalfExtents[0] * abs_r[1][0] + inBox.mHalfExtents[1] * abs_r[0][0]; - if (abs(rot(2, 3) * rot(1, 2) - rot(1, 3) * rot(2, 2)) > ra + rb) return false; - - // Test axis L = A1 x B0 - ra = mHalfExtents[0] * abs_r[0][2] + mHalfExtents[2] * abs_r[0][0]; - rb = inBox.mHalfExtents[1] * abs_r[2][1] + inBox.mHalfExtents[2] * abs_r[1][1]; - if (abs(rot(0, 3) * rot(2, 0) - rot(2, 3) * rot(0, 0)) > ra + rb) return false; - - // Test axis L = A1 x B1 - ra = mHalfExtents[0] * abs_r[1][2] + mHalfExtents[2] * abs_r[1][0]; - rb = inBox.mHalfExtents[0] * abs_r[2][1] + inBox.mHalfExtents[2] * abs_r[0][1]; - if (abs(rot(0, 3) * rot(2, 1) - rot(2, 3) * rot(0, 1)) > ra + rb) return false; - - // Test axis L = A1 x B2 - ra = mHalfExtents[0] * abs_r[2][2] + mHalfExtents[2] * abs_r[2][0]; - rb = inBox.mHalfExtents[0] * abs_r[1][1] + inBox.mHalfExtents[1] * abs_r[0][1]; - if (abs(rot(0, 3) * rot(2, 2) - rot(2, 3) * rot(0, 2)) > ra + rb) return false; - - // Test axis L = A2 x B0 - ra = mHalfExtents[0] * abs_r[0][1] + mHalfExtents[1] * abs_r[0][0]; - rb = inBox.mHalfExtents[1] * abs_r[2][2] + inBox.mHalfExtents[2] * abs_r[1][2]; - if (abs(rot(1, 3) * rot(0, 0) - rot(0, 3) * rot(1, 0)) > ra + rb) return false; - - // Test axis L = A2 x B1 - ra = mHalfExtents[0] * abs_r[1][1] + mHalfExtents[1] * abs_r[1][0]; - rb = inBox.mHalfExtents[0] * abs_r[2][2] + inBox.mHalfExtents[2] * abs_r[0][2]; - if (abs(rot(1, 3) * rot(0, 1) - rot(0, 3) * rot(1, 1)) > ra + rb) return false; - - // Test axis L = A2 x B2 - ra = mHalfExtents[0] * abs_r[2][1] + mHalfExtents[1] * abs_r[2][0]; - rb = inBox.mHalfExtents[0] * abs_r[1][2] + inBox.mHalfExtents[1] * abs_r[0][2]; - if (abs(rot(1, 3) * rot(0, 2) - rot(0, 3) * rot(1, 2)) > ra + rb) return false; - - // Since no separating axis is found, the OBBs must be intersecting - return true; -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/OrientedBox.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/OrientedBox.h deleted file mode 100644 index c5c2a0e16c8..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/OrientedBox.h +++ /dev/null @@ -1,39 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -class AABox; - -/// Oriented box -class [[nodiscard]] JPH_EXPORT_GCC_BUG_WORKAROUND OrientedBox -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Constructor - OrientedBox() = default; - OrientedBox(Mat44Arg inOrientation, Vec3Arg inHalfExtents) : mOrientation(inOrientation), mHalfExtents(inHalfExtents) { } - - /// Construct from axis aligned box and transform. Only works for rotation/translation matrix (no scaling / shearing). - OrientedBox(Mat44Arg inOrientation, const AABox &inBox) : OrientedBox(inOrientation.PreTranslated(inBox.GetCenter()), inBox.GetExtent()) { } - - /// Test if oriented box overlaps with axis aligned box each other - bool Overlaps(const AABox &inBox, float inEpsilon = 1.0e-6f) const; - - /// Test if two oriented boxes overlap each other - bool Overlaps(const OrientedBox &inBox, float inEpsilon = 1.0e-6f) const; - - Mat44 mOrientation; ///< Transform that positions and rotates the local space axis aligned box into world space - Vec3 mHalfExtents; ///< Half extents (half the size of the edge) of the local space axis aligned box -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/Plane.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/Plane.h deleted file mode 100644 index c969dbd6f08..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/Plane.h +++ /dev/null @@ -1,86 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -JPH_NAMESPACE_BEGIN - -/// An infinite plane described by the formula X . Normal + Constant = 0. -class [[nodiscard]] Plane -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Constructor - Plane() = default; - explicit Plane(Vec4Arg inNormalAndConstant) : mNormalAndConstant(inNormalAndConstant) { } - Plane(Vec3Arg inNormal, float inConstant) : mNormalAndConstant(inNormal, inConstant) { } - - /// Create from point and normal - static Plane sFromPointAndNormal(Vec3Arg inPoint, Vec3Arg inNormal) { return Plane(Vec4(inNormal, -inNormal.Dot(inPoint))); } - - /// Create from point and normal, double precision version that more accurately calculates the plane constant - static Plane sFromPointAndNormal(DVec3Arg inPoint, Vec3Arg inNormal) { return Plane(Vec4(inNormal, -float(DVec3(inNormal).Dot(inPoint)))); } - - /// Create from 3 counter clockwise points - static Plane sFromPointsCCW(Vec3Arg inV1, Vec3Arg inV2, Vec3Arg inV3) { return sFromPointAndNormal(inV1, (inV2 - inV1).Cross(inV3 - inV1).Normalized()); } - - // Properties - Vec3 GetNormal() const { return Vec3(mNormalAndConstant); } - void SetNormal(Vec3Arg inNormal) { mNormalAndConstant = Vec4(inNormal, mNormalAndConstant.GetW()); } - float GetConstant() const { return mNormalAndConstant.GetW(); } - void SetConstant(float inConstant) { mNormalAndConstant.SetW(inConstant); } - - /// Offset the plane (positive value means move it in the direction of the plane normal) - Plane Offset(float inDistance) const { return Plane(mNormalAndConstant - Vec4(Vec3::sZero(), inDistance)); } - - /// Transform the plane by a matrix - inline Plane GetTransformed(Mat44Arg inTransform) const - { - Vec3 transformed_normal = inTransform.Multiply3x3(GetNormal()); - return Plane(transformed_normal, GetConstant() - inTransform.GetTranslation().Dot(transformed_normal)); - } - - /// Distance point to plane - float SignedDistance(Vec3Arg inPoint) const { return inPoint.Dot(GetNormal()) + GetConstant(); } - - /// Returns intersection point between 3 planes - static bool sIntersectPlanes(const Plane &inP1, const Plane &inP2, const Plane &inP3, Vec3 &outPoint) - { - // We solve the equation: - // |ax, ay, az, aw| | x | | 0 | - // |bx, by, bz, bw| * | y | = | 0 | - // |cx, cy, cz, cw| | z | | 0 | - // | 0, 0, 0, 1| | 1 | | 1 | - // Where normal of plane 1 = (ax, ay, az), plane constant of 1 = aw, normal of plane 2 = (bx, by, bz) etc. - // This involves inverting the matrix and multiplying it with [0, 0, 0, 1] - - // Fetch the normals and plane constants for the three planes - Vec4 a = inP1.mNormalAndConstant; - Vec4 b = inP2.mNormalAndConstant; - Vec4 c = inP3.mNormalAndConstant; - - // Result is a vector that we have to divide by: - float denominator = Vec3(a).Dot(Vec3(b).Cross(Vec3(c))); - if (denominator == 0.0f) - return false; - - // The numerator is: - // [aw*(bz*cy-by*cz)+ay*(bw*cz-bz*cw)+az*(by*cw-bw*cy)] - // [aw*(bx*cz-bz*cx)+ax*(bz*cw-bw*cz)+az*(bw*cx-bx*cw)] - // [aw*(by*cx-bx*cy)+ax*(bw*cy-by*cw)+ay*(bx*cw-bw*cx)] - Vec4 numerator = - a.SplatW() * (b.Swizzle() * c.Swizzle() - b.Swizzle() * c.Swizzle()) - + a.Swizzle() * (b.Swizzle() * c.Swizzle() - b.Swizzle() * c.Swizzle()) - + a.Swizzle() * (b.Swizzle() * c.Swizzle() - b.Swizzle() * c.Swizzle()); - - outPoint = Vec3(numerator) / denominator; - return true; - } - -private: - Vec4 mNormalAndConstant; ///< XYZ = normal, W = constant, plane: x . normal + constant = 0 -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/RayAABox.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/RayAABox.h deleted file mode 100644 index 4506fadbb75..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/RayAABox.h +++ /dev/null @@ -1,241 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -JPH_NAMESPACE_BEGIN - -/// Helper structure holding the reciprocal of a ray for Ray vs AABox testing -class RayInvDirection -{ -public: - /// Constructors - inline RayInvDirection() = default; - inline explicit RayInvDirection(Vec3Arg inDirection) { Set(inDirection); } - - /// Set reciprocal from ray direction - inline void Set(Vec3Arg inDirection) - { - // if (abs(inDirection) <= Epsilon) the ray is nearly parallel to the slab. - mIsParallel = Vec3::sLessOrEqual(inDirection.Abs(), Vec3::sReplicate(1.0e-20f)); - - // Calculate 1 / direction while avoiding division by zero - mInvDirection = Vec3::sSelect(inDirection, Vec3::sReplicate(1.0f), mIsParallel).Reciprocal(); - } - - Vec3 mInvDirection; ///< 1 / ray direction - UVec4 mIsParallel; ///< for each component if it is parallel to the coordinate axis -}; - -/// Intersect AABB with ray, returns minimal distance along ray or FLT_MAX if no hit -/// Note: Can return negative value if ray starts in box -JPH_INLINE float RayAABox(Vec3Arg inOrigin, const RayInvDirection &inInvDirection, Vec3Arg inBoundsMin, Vec3Arg inBoundsMax) -{ - // Constants - Vec3 flt_min = Vec3::sReplicate(-FLT_MAX); - Vec3 flt_max = Vec3::sReplicate(FLT_MAX); - - // Test against all three axii simultaneously. - Vec3 t1 = (inBoundsMin - inOrigin) * inInvDirection.mInvDirection; - Vec3 t2 = (inBoundsMax - inOrigin) * inInvDirection.mInvDirection; - - // Compute the max of min(t1,t2) and the min of max(t1,t2) ensuring we don't - // use the results from any directions parallel to the slab. - Vec3 t_min = Vec3::sSelect(Vec3::sMin(t1, t2), flt_min, inInvDirection.mIsParallel); - Vec3 t_max = Vec3::sSelect(Vec3::sMax(t1, t2), flt_max, inInvDirection.mIsParallel); - - // t_min.xyz = maximum(t_min.x, t_min.y, t_min.z); - t_min = Vec3::sMax(t_min, t_min.Swizzle()); - t_min = Vec3::sMax(t_min, t_min.Swizzle()); - - // t_max.xyz = minimum(t_max.x, t_max.y, t_max.z); - t_max = Vec3::sMin(t_max, t_max.Swizzle()); - t_max = Vec3::sMin(t_max, t_max.Swizzle()); - - // if (t_min > t_max) return FLT_MAX; - UVec4 no_intersection = Vec3::sGreater(t_min, t_max); - - // if (t_max < 0.0f) return FLT_MAX; - no_intersection = UVec4::sOr(no_intersection, Vec3::sLess(t_max, Vec3::sZero())); - - // if (inInvDirection.mIsParallel && !(Min <= inOrigin && inOrigin <= Max)) return FLT_MAX; else return t_min; - UVec4 no_parallel_overlap = UVec4::sOr(Vec3::sLess(inOrigin, inBoundsMin), Vec3::sGreater(inOrigin, inBoundsMax)); - no_intersection = UVec4::sOr(no_intersection, UVec4::sAnd(inInvDirection.mIsParallel, no_parallel_overlap)); - no_intersection = UVec4::sOr(no_intersection, no_intersection.SplatY()); - no_intersection = UVec4::sOr(no_intersection, no_intersection.SplatZ()); - return Vec3::sSelect(t_min, flt_max, no_intersection).GetX(); -} - -/// Intersect 4 AABBs with ray, returns minimal distance along ray or FLT_MAX if no hit -/// Note: Can return negative value if ray starts in box -JPH_INLINE Vec4 RayAABox4(Vec3Arg inOrigin, const RayInvDirection &inInvDirection, Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ) -{ - // Constants - Vec4 flt_min = Vec4::sReplicate(-FLT_MAX); - Vec4 flt_max = Vec4::sReplicate(FLT_MAX); - - // Origin - Vec4 originx = inOrigin.SplatX(); - Vec4 originy = inOrigin.SplatY(); - Vec4 originz = inOrigin.SplatZ(); - - // Parallel - UVec4 parallelx = inInvDirection.mIsParallel.SplatX(); - UVec4 parallely = inInvDirection.mIsParallel.SplatY(); - UVec4 parallelz = inInvDirection.mIsParallel.SplatZ(); - - // Inverse direction - Vec4 invdirx = inInvDirection.mInvDirection.SplatX(); - Vec4 invdiry = inInvDirection.mInvDirection.SplatY(); - Vec4 invdirz = inInvDirection.mInvDirection.SplatZ(); - - // Test against all three axii simultaneously. - Vec4 t1x = (inBoundsMinX - originx) * invdirx; - Vec4 t1y = (inBoundsMinY - originy) * invdiry; - Vec4 t1z = (inBoundsMinZ - originz) * invdirz; - Vec4 t2x = (inBoundsMaxX - originx) * invdirx; - Vec4 t2y = (inBoundsMaxY - originy) * invdiry; - Vec4 t2z = (inBoundsMaxZ - originz) * invdirz; - - // Compute the max of min(t1,t2) and the min of max(t1,t2) ensuring we don't - // use the results from any directions parallel to the slab. - Vec4 t_minx = Vec4::sSelect(Vec4::sMin(t1x, t2x), flt_min, parallelx); - Vec4 t_miny = Vec4::sSelect(Vec4::sMin(t1y, t2y), flt_min, parallely); - Vec4 t_minz = Vec4::sSelect(Vec4::sMin(t1z, t2z), flt_min, parallelz); - Vec4 t_maxx = Vec4::sSelect(Vec4::sMax(t1x, t2x), flt_max, parallelx); - Vec4 t_maxy = Vec4::sSelect(Vec4::sMax(t1y, t2y), flt_max, parallely); - Vec4 t_maxz = Vec4::sSelect(Vec4::sMax(t1z, t2z), flt_max, parallelz); - - // t_min.xyz = maximum(t_min.x, t_min.y, t_min.z); - Vec4 t_min = Vec4::sMax(Vec4::sMax(t_minx, t_miny), t_minz); - - // t_max.xyz = minimum(t_max.x, t_max.y, t_max.z); - Vec4 t_max = Vec4::sMin(Vec4::sMin(t_maxx, t_maxy), t_maxz); - - // if (t_min > t_max) return FLT_MAX; - UVec4 no_intersection = Vec4::sGreater(t_min, t_max); - - // if (t_max < 0.0f) return FLT_MAX; - no_intersection = UVec4::sOr(no_intersection, Vec4::sLess(t_max, Vec4::sZero())); - - // if bounds are invalid return FLOAT_MAX; - UVec4 bounds_invalid = UVec4::sOr(UVec4::sOr(Vec4::sGreater(inBoundsMinX, inBoundsMaxX), Vec4::sGreater(inBoundsMinY, inBoundsMaxY)), Vec4::sGreater(inBoundsMinZ, inBoundsMaxZ)); - no_intersection = UVec4::sOr(no_intersection, bounds_invalid); - - // if (inInvDirection.mIsParallel && !(Min <= inOrigin && inOrigin <= Max)) return FLT_MAX; else return t_min; - UVec4 no_parallel_overlapx = UVec4::sAnd(parallelx, UVec4::sOr(Vec4::sLess(originx, inBoundsMinX), Vec4::sGreater(originx, inBoundsMaxX))); - UVec4 no_parallel_overlapy = UVec4::sAnd(parallely, UVec4::sOr(Vec4::sLess(originy, inBoundsMinY), Vec4::sGreater(originy, inBoundsMaxY))); - UVec4 no_parallel_overlapz = UVec4::sAnd(parallelz, UVec4::sOr(Vec4::sLess(originz, inBoundsMinZ), Vec4::sGreater(originz, inBoundsMaxZ))); - no_intersection = UVec4::sOr(no_intersection, UVec4::sOr(UVec4::sOr(no_parallel_overlapx, no_parallel_overlapy), no_parallel_overlapz)); - return Vec4::sSelect(t_min, flt_max, no_intersection); -} - -/// Intersect AABB with ray, returns minimal and maximal distance along ray or FLT_MAX, -FLT_MAX if no hit -/// Note: Can return negative value for outMin if ray starts in box -JPH_INLINE void RayAABox(Vec3Arg inOrigin, const RayInvDirection &inInvDirection, Vec3Arg inBoundsMin, Vec3Arg inBoundsMax, float &outMin, float &outMax) -{ - // Constants - Vec3 flt_min = Vec3::sReplicate(-FLT_MAX); - Vec3 flt_max = Vec3::sReplicate(FLT_MAX); - - // Test against all three axii simultaneously. - Vec3 t1 = (inBoundsMin - inOrigin) * inInvDirection.mInvDirection; - Vec3 t2 = (inBoundsMax - inOrigin) * inInvDirection.mInvDirection; - - // Compute the max of min(t1,t2) and the min of max(t1,t2) ensuring we don't - // use the results from any directions parallel to the slab. - Vec3 t_min = Vec3::sSelect(Vec3::sMin(t1, t2), flt_min, inInvDirection.mIsParallel); - Vec3 t_max = Vec3::sSelect(Vec3::sMax(t1, t2), flt_max, inInvDirection.mIsParallel); - - // t_min.xyz = maximum(t_min.x, t_min.y, t_min.z); - t_min = Vec3::sMax(t_min, t_min.Swizzle()); - t_min = Vec3::sMax(t_min, t_min.Swizzle()); - - // t_max.xyz = minimum(t_max.x, t_max.y, t_max.z); - t_max = Vec3::sMin(t_max, t_max.Swizzle()); - t_max = Vec3::sMin(t_max, t_max.Swizzle()); - - // if (t_min > t_max) return FLT_MAX; - UVec4 no_intersection = Vec3::sGreater(t_min, t_max); - - // if (t_max < 0.0f) return FLT_MAX; - no_intersection = UVec4::sOr(no_intersection, Vec3::sLess(t_max, Vec3::sZero())); - - // if (inInvDirection.mIsParallel && !(Min <= inOrigin && inOrigin <= Max)) return FLT_MAX; else return t_min; - UVec4 no_parallel_overlap = UVec4::sOr(Vec3::sLess(inOrigin, inBoundsMin), Vec3::sGreater(inOrigin, inBoundsMax)); - no_intersection = UVec4::sOr(no_intersection, UVec4::sAnd(inInvDirection.mIsParallel, no_parallel_overlap)); - no_intersection = UVec4::sOr(no_intersection, no_intersection.SplatY()); - no_intersection = UVec4::sOr(no_intersection, no_intersection.SplatZ()); - outMin = Vec3::sSelect(t_min, flt_max, no_intersection).GetX(); - outMax = Vec3::sSelect(t_max, flt_min, no_intersection).GetX(); -} - -/// Intersect AABB with ray, returns true if there is a hit closer than inClosest -JPH_INLINE bool RayAABoxHits(Vec3Arg inOrigin, const RayInvDirection &inInvDirection, Vec3Arg inBoundsMin, Vec3Arg inBoundsMax, float inClosest) -{ - // Constants - Vec3 flt_min = Vec3::sReplicate(-FLT_MAX); - Vec3 flt_max = Vec3::sReplicate(FLT_MAX); - - // Test against all three axii simultaneously. - Vec3 t1 = (inBoundsMin - inOrigin) * inInvDirection.mInvDirection; - Vec3 t2 = (inBoundsMax - inOrigin) * inInvDirection.mInvDirection; - - // Compute the max of min(t1,t2) and the min of max(t1,t2) ensuring we don't - // use the results from any directions parallel to the slab. - Vec3 t_min = Vec3::sSelect(Vec3::sMin(t1, t2), flt_min, inInvDirection.mIsParallel); - Vec3 t_max = Vec3::sSelect(Vec3::sMax(t1, t2), flt_max, inInvDirection.mIsParallel); - - // t_min.xyz = maximum(t_min.x, t_min.y, t_min.z); - t_min = Vec3::sMax(t_min, t_min.Swizzle()); - t_min = Vec3::sMax(t_min, t_min.Swizzle()); - - // t_max.xyz = minimum(t_max.x, t_max.y, t_max.z); - t_max = Vec3::sMin(t_max, t_max.Swizzle()); - t_max = Vec3::sMin(t_max, t_max.Swizzle()); - - // if (t_min > t_max) return false; - UVec4 no_intersection = Vec3::sGreater(t_min, t_max); - - // if (t_max < 0.0f) return false; - no_intersection = UVec4::sOr(no_intersection, Vec3::sLess(t_max, Vec3::sZero())); - - // if (t_min > inClosest) return false; - no_intersection = UVec4::sOr(no_intersection, Vec3::sGreater(t_min, Vec3::sReplicate(inClosest))); - - // if (inInvDirection.mIsParallel && !(Min <= inOrigin && inOrigin <= Max)) return false; else return true; - UVec4 no_parallel_overlap = UVec4::sOr(Vec3::sLess(inOrigin, inBoundsMin), Vec3::sGreater(inOrigin, inBoundsMax)); - no_intersection = UVec4::sOr(no_intersection, UVec4::sAnd(inInvDirection.mIsParallel, no_parallel_overlap)); - - return !no_intersection.TestAnyXYZTrue(); -} - -/// Intersect AABB with ray without hit fraction, based on separating axis test -/// @see http://www.codercorner.com/RayAABB.cpp -JPH_INLINE bool RayAABoxHits(Vec3Arg inOrigin, Vec3Arg inDirection, Vec3Arg inBoundsMin, Vec3Arg inBoundsMax) -{ - Vec3 extents = inBoundsMax - inBoundsMin; - - Vec3 diff = 2.0f * inOrigin - inBoundsMin - inBoundsMax; - Vec3 abs_diff = diff.Abs(); - - UVec4 no_intersection = UVec4::sAnd(Vec3::sGreater(abs_diff, extents), Vec3::sGreaterOrEqual(diff * inDirection, Vec3::sZero())); - - Vec3 abs_dir = inDirection.Abs(); - Vec3 abs_dir_yzz = abs_dir.Swizzle(); - Vec3 abs_dir_xyx = abs_dir.Swizzle(); - - Vec3 extents_yzz = extents.Swizzle(); - Vec3 extents_xyx = extents.Swizzle(); - - Vec3 diff_yzx = diff.Swizzle(); - - Vec3 dir_yzx = inDirection.Swizzle(); - - no_intersection = UVec4::sOr(no_intersection, Vec3::sGreater((inDirection * diff_yzx - dir_yzx * diff).Abs(), extents_xyx * abs_dir_yzz + extents_yzz * abs_dir_xyx)); - - return !no_intersection.TestAnyXYZTrue(); -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/RayAABox8.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/RayAABox8.h deleted file mode 100644 index 260e3636cd3..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/RayAABox8.h +++ /dev/null @@ -1,76 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include - -JPH_NAMESPACE_BEGIN - -/// Intersect 8 AABBs with ray, returns minimal distance along ray or FLT_MAX if no hit -/// Note: Can return negative value if ray starts in box -JPH_INLINE Vec8 RayAABox8(Vec3Arg inOrigin, const RayInvDirection &inInvDirection, Vec8Arg inBoundsMinX, Vec8Arg inBoundsMinY, Vec8Arg inBoundsMinZ, Vec8Arg inBoundsMaxX, Vec8Arg inBoundsMaxY, Vec8Arg inBoundsMaxZ) -{ - // Constants - Vec8 flt_min = Vec8::sReplicate(-FLT_MAX); - Vec8 flt_max = Vec8::sReplicate(FLT_MAX); - - // Origin - Vec8 originx = Vec8::sSplatX(Vec4(inOrigin)); - Vec8 originy = Vec8::sSplatY(Vec4(inOrigin)); - Vec8 originz = Vec8::sSplatZ(Vec4(inOrigin)); - - // Parallel - UVec8 parallelx = UVec8::sSplatX(inInvDirection.mIsParallel); - UVec8 parallely = UVec8::sSplatY(inInvDirection.mIsParallel); - UVec8 parallelz = UVec8::sSplatZ(inInvDirection.mIsParallel); - - // Inverse direction - Vec8 invdirx = Vec8::sSplatX(Vec4(inInvDirection.mInvDirection)); - Vec8 invdiry = Vec8::sSplatY(Vec4(inInvDirection.mInvDirection)); - Vec8 invdirz = Vec8::sSplatZ(Vec4(inInvDirection.mInvDirection)); - - // Test against all three axii simultaneously. - Vec8 t1x = (inBoundsMinX - originx) * invdirx; - Vec8 t1y = (inBoundsMinY - originy) * invdiry; - Vec8 t1z = (inBoundsMinZ - originz) * invdirz; - Vec8 t2x = (inBoundsMaxX - originx) * invdirx; - Vec8 t2y = (inBoundsMaxY - originy) * invdiry; - Vec8 t2z = (inBoundsMaxZ - originz) * invdirz; - - // Compute the max of min(t1,t2) and the min of max(t1,t2) ensuring we don't - // use the results from any directions parallel to the slab. - Vec8 t_minx = Vec8::sSelect(Vec8::sMin(t1x, t2x), flt_min, parallelx); - Vec8 t_miny = Vec8::sSelect(Vec8::sMin(t1y, t2y), flt_min, parallely); - Vec8 t_minz = Vec8::sSelect(Vec8::sMin(t1z, t2z), flt_min, parallelz); - Vec8 t_maxx = Vec8::sSelect(Vec8::sMax(t1x, t2x), flt_max, parallelx); - Vec8 t_maxy = Vec8::sSelect(Vec8::sMax(t1y, t2y), flt_max, parallely); - Vec8 t_maxz = Vec8::sSelect(Vec8::sMax(t1z, t2z), flt_max, parallelz); - - // t_min.xyz = maximum(t_min.x, t_min.y, t_min.z); - Vec8 t_min = Vec8::sMax(Vec8::sMax(t_minx, t_miny), t_minz); - - // t_max.xyz = minimum(t_max.x, t_max.y, t_max.z); - Vec8 t_max = Vec8::sMin(Vec8::sMin(t_maxx, t_maxy), t_maxz); - - // if (t_min > t_max) return FLT_MAX; - UVec8 no_intersection = Vec8::sGreater(t_min, t_max); - - // if (t_max < 0.0f) return FLT_MAX; - no_intersection = UVec8::sOr(no_intersection, Vec8::sLess(t_max, Vec8::sZero())); - - // if bounds are invalid return FLOAT_MAX; - UVec8 bounds_invalid = UVec8::sOr(UVec8::sOr(Vec8::sGreater(inBoundsMinX, inBoundsMaxX), Vec8::sGreater(inBoundsMinY, inBoundsMaxY)), Vec8::sGreater(inBoundsMinZ, inBoundsMaxZ)); - no_intersection = UVec8::sOr(no_intersection, bounds_invalid); - - // if (inInvDirection.mIsParallel && !(Min <= inOrigin && inOrigin <= Max)) return FLT_MAX; else return t_min; - UVec8 no_parallel_overlapx = UVec8::sAnd(parallelx, UVec8::sOr(Vec8::sLess(originx, inBoundsMinX), Vec8::sGreater(originx, inBoundsMaxX))); - UVec8 no_parallel_overlapy = UVec8::sAnd(parallely, UVec8::sOr(Vec8::sLess(originy, inBoundsMinY), Vec8::sGreater(originy, inBoundsMaxY))); - UVec8 no_parallel_overlapz = UVec8::sAnd(parallelz, UVec8::sOr(Vec8::sLess(originz, inBoundsMinZ), Vec8::sGreater(originz, inBoundsMaxZ))); - no_intersection = UVec8::sOr(no_intersection, UVec8::sOr(UVec8::sOr(no_parallel_overlapx, no_parallel_overlapy), no_parallel_overlapz)); - return Vec8::sSelect(t_min, flt_max, no_intersection); -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/RayCapsule.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/RayCapsule.h deleted file mode 100644 index 4862931b685..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/RayCapsule.h +++ /dev/null @@ -1,37 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include - -JPH_NAMESPACE_BEGIN - -/// Tests a ray starting at inRayOrigin and extending infinitely in inRayDirection -/// against a capsule centered around the origin with its axis along the Y axis and half height specified. -/// @return FLT_MAX if there is no intersection, otherwise the fraction along the ray. -/// @param inRayDirection Ray direction. Does not need to be normalized. -/// @param inRayOrigin Origin of the ray. If the ray starts inside the capsule, the returned fraction will be 0. -/// @param inCapsuleHalfHeight Distance from the origin to the center of the top sphere (or that of the bottom) -/// @param inCapsuleRadius Radius of the top/bottom sphere -JPH_INLINE float RayCapsule(Vec3Arg inRayOrigin, Vec3Arg inRayDirection, float inCapsuleHalfHeight, float inCapsuleRadius) -{ - // Test infinite cylinder - float cylinder = RayCylinder(inRayOrigin, inRayDirection, inCapsuleRadius); - if (cylinder == FLT_MAX) - return FLT_MAX; - - // If this hit is in the finite cylinder we have our fraction - if (abs(inRayOrigin.GetY() + cylinder * inRayDirection.GetY()) <= inCapsuleHalfHeight) - return cylinder; - - // Test upper and lower sphere - Vec3 sphere_center(0, inCapsuleHalfHeight, 0); - float upper = RaySphere(inRayOrigin, inRayDirection, sphere_center, inCapsuleRadius); - float lower = RaySphere(inRayOrigin, inRayDirection, -sphere_center, inCapsuleRadius); - return min(upper, lower); -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/RayCylinder.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/RayCylinder.h deleted file mode 100644 index cabed0680a2..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/RayCylinder.h +++ /dev/null @@ -1,101 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -/// Tests a ray starting at inRayOrigin and extending infinitely in inRayDirection -/// against an infinite cylinder centered along the Y axis -/// @return FLT_MAX if there is no intersection, otherwise the fraction along the ray. -/// @param inRayDirection Direction of the ray. Does not need to be normalized. -/// @param inRayOrigin Origin of the ray. If the ray starts inside the cylinder, the returned fraction will be 0. -/// @param inCylinderRadius Radius of the infinite cylinder -JPH_INLINE float RayCylinder(Vec3Arg inRayOrigin, Vec3Arg inRayDirection, float inCylinderRadius) -{ - // Remove Y component of ray to see of ray intersects with infinite cylinder - UVec4 mask_y = UVec4(0, 0xffffffff, 0, 0); - Vec3 origin_xz = Vec3::sSelect(inRayOrigin, Vec3::sZero(), mask_y); - float origin_xz_len_sq = origin_xz.LengthSq(); - float r_sq = Square(inCylinderRadius); - if (origin_xz_len_sq > r_sq) - { - // Ray starts outside of the infinite cylinder - // Solve: |RayOrigin_xz + fraction * RayDirection_xz|^2 = r^2 to find fraction - Vec3 direction_xz = Vec3::sSelect(inRayDirection, Vec3::sZero(), mask_y); - float a = direction_xz.LengthSq(); - float b = 2.0f * origin_xz.Dot(direction_xz); - float c = origin_xz_len_sq - r_sq; - float fraction1, fraction2; - if (FindRoot(a, b, c, fraction1, fraction2) == 0) - return FLT_MAX; // No intersection with infinite cylinder - - // Get fraction corresponding to the ray entering the circle - float fraction = min(fraction1, fraction2); - if (fraction >= 0.0f) - return fraction; - } - else - { - // Ray starts inside the infinite cylinder - return 0.0f; - } - - // No collision - return FLT_MAX; -} - -/// Test a ray against a cylinder centered around the origin with its axis along the Y axis and half height specified. -/// @return FLT_MAX if there is no intersection, otherwise the fraction along the ray. -/// @param inRayDirection Ray direction. Does not need to be normalized. -/// @param inRayOrigin Origin of the ray. If the ray starts inside the cylinder, the returned fraction will be 0. -/// @param inCylinderRadius Radius of the cylinder -/// @param inCylinderHalfHeight Distance from the origin to the top (or bottom) of the cylinder -JPH_INLINE float RayCylinder(Vec3Arg inRayOrigin, Vec3Arg inRayDirection, float inCylinderHalfHeight, float inCylinderRadius) -{ - // Test infinite cylinder - float fraction = RayCylinder(inRayOrigin, inRayDirection, inCylinderRadius); - if (fraction == FLT_MAX) - return FLT_MAX; - - // If this hit is in the finite cylinder we have our fraction - if (abs(inRayOrigin.GetY() + fraction * inRayDirection.GetY()) <= inCylinderHalfHeight) - return fraction; - - // Check if ray could hit the top or bottom plane of the cylinder - float direction_y = inRayDirection.GetY(); - if (direction_y != 0.0f) - { - // Solving line equation: x = ray_origin + fraction * ray_direction - // and plane equation: plane_normal . x + plane_constant = 0 - // fraction = (-plane_constant - plane_normal . ray_origin) / (plane_normal . ray_direction) - // when the ray_direction.y < 0: - // plane_constant = -cylinder_half_height, plane_normal = (0, 1, 0) - // else - // plane_constant = -cylinder_half_height, plane_normal = (0, -1, 0) - float origin_y = inRayOrigin.GetY(); - float plane_fraction; - if (direction_y < 0.0f) - plane_fraction = (inCylinderHalfHeight - origin_y) / direction_y; - else - plane_fraction = -(inCylinderHalfHeight + origin_y) / direction_y; - - // Check if the hit is in front of the ray - if (plane_fraction >= 0.0f) - { - // Test if this hit is inside the cylinder - Vec3 point = inRayOrigin + plane_fraction * inRayDirection; - float dist_sq = Square(point.GetX()) + Square(point.GetZ()); - if (dist_sq <= Square(inCylinderRadius)) - return plane_fraction; - } - } - - // No collision - return FLT_MAX; -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/RaySphere.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/RaySphere.h deleted file mode 100644 index d5cfb1905a7..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/RaySphere.h +++ /dev/null @@ -1,96 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -/// Tests a ray starting at inRayOrigin and extending infinitely in inRayDirection against a sphere, -/// @return FLT_MAX if there is no intersection, otherwise the fraction along the ray. -/// @param inRayOrigin Ray origin. If the ray starts inside the sphere, the returned fraction will be 0. -/// @param inRayDirection Ray direction. Does not need to be normalized. -/// @param inSphereCenter Position of the center of the sphere -/// @param inSphereRadius Radius of the sphere -JPH_INLINE float RaySphere(Vec3Arg inRayOrigin, Vec3Arg inRayDirection, Vec3Arg inSphereCenter, float inSphereRadius) -{ - // Solve: |RayOrigin + fraction * RayDirection - SphereCenter|^2 = SphereRadius^2 for fraction - Vec3 center_origin = inRayOrigin - inSphereCenter; - float a = inRayDirection.LengthSq(); - float b = 2.0f * inRayDirection.Dot(center_origin); - float c = center_origin.LengthSq() - inSphereRadius * inSphereRadius; - float fraction1, fraction2; - if (FindRoot(a, b, c, fraction1, fraction2) == 0) - return c <= 0.0f? 0.0f : FLT_MAX; // Return if origin is inside the sphere - - // Sort so that the smallest is first - if (fraction1 > fraction2) - swap(fraction1, fraction2); - - // Test solution with lowest fraction, this will be the ray entering the sphere - if (fraction1 >= 0.0f) - return fraction1; // Sphere is before the ray start - - // Test solution with highest fraction, this will be the ray leaving the sphere - if (fraction2 >= 0.0f) - return 0.0f; // We start inside the sphere - - // No solution - return FLT_MAX; -} - -/// Tests a ray starting at inRayOrigin and extending infinitely in inRayDirection against a sphere. -/// Outputs entry and exit points (outMinFraction and outMaxFraction) along the ray (which could be negative if the hit point is before the start of the ray). -/// @param inRayOrigin Ray origin. If the ray starts inside the sphere, the returned fraction will be 0. -/// @param inRayDirection Ray direction. Does not need to be normalized. -/// @param inSphereCenter Position of the center of the sphere. -/// @param inSphereRadius Radius of the sphere. -/// @param outMinFraction Returned lowest intersection fraction -/// @param outMaxFraction Returned highest intersection fraction -/// @return The amount of intersections with the sphere. -/// If 1 intersection is returned outMinFraction will be equal to outMaxFraction -JPH_INLINE int RaySphere(Vec3Arg inRayOrigin, Vec3Arg inRayDirection, Vec3Arg inSphereCenter, float inSphereRadius, float &outMinFraction, float &outMaxFraction) -{ - // Solve: |RayOrigin + fraction * RayDirection - SphereCenter|^2 = SphereRadius^2 for fraction - Vec3 center_origin = inRayOrigin - inSphereCenter; - float a = inRayDirection.LengthSq(); - float b = 2.0f * inRayDirection.Dot(center_origin); - float c = center_origin.LengthSq() - inSphereRadius * inSphereRadius; - float fraction1, fraction2; - switch (FindRoot(a, b, c, fraction1, fraction2)) - { - case 0: - if (c <= 0.0f) - { - // Origin inside sphere - outMinFraction = outMaxFraction = 0.0f; - return 1; - } - else - { - // Origin outside of the sphere - return 0; - } - break; - - case 1: - // Ray is touching the sphere - outMinFraction = outMaxFraction = fraction1; - return 1; - - default: - // Ray enters and exits the sphere - - // Sort so that the smallest is first - if (fraction1 > fraction2) - swap(fraction1, fraction2); - - outMinFraction = fraction1; - outMaxFraction = fraction2; - return 2; - } -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/RayTriangle.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/RayTriangle.h deleted file mode 100644 index dabd0275cc0..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/RayTriangle.h +++ /dev/null @@ -1,158 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -JPH_NAMESPACE_BEGIN - -/// Intersect ray with triangle, returns closest point or FLT_MAX if no hit (branch less version) -/// Adapted from: http://en.wikipedia.org/wiki/M%C3%B6ller%E2%80%93Trumbore_intersection_algorithm -JPH_INLINE float RayTriangle(Vec3Arg inOrigin, Vec3Arg inDirection, Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2) -{ - // Epsilon - Vec3 epsilon = Vec3::sReplicate(1.0e-12f); - - // Zero & one - Vec3 zero = Vec3::sZero(); - Vec3 one = Vec3::sReplicate(1.0f); - - // Find vectors for two edges sharing inV0 - Vec3 e1 = inV1 - inV0; - Vec3 e2 = inV2 - inV0; - - // Begin calculating determinant - also used to calculate u parameter - Vec3 p = inDirection.Cross(e2); - - // if determinant is near zero, ray lies in plane of triangle - Vec3 det = Vec3::sReplicate(e1.Dot(p)); - - // Check if determinant is near zero - UVec4 det_near_zero = Vec3::sLess(det.Abs(), epsilon); - - // When the determinant is near zero, set it to one to avoid dividing by zero - det = Vec3::sSelect(det, Vec3::sReplicate(1.0f), det_near_zero); - - // Calculate distance from inV0 to ray origin - Vec3 s = inOrigin - inV0; - - // Calculate u parameter - Vec3 u = Vec3::sReplicate(s.Dot(p)) / det; - - // Prepare to test v parameter - Vec3 q = s.Cross(e1); - - // Calculate v parameter - Vec3 v = Vec3::sReplicate(inDirection.Dot(q)) / det; - - // Get intersection point - Vec3 t = Vec3::sReplicate(e2.Dot(q)) / det; - - // Check if there is an intersection - UVec4 no_intersection = - UVec4::sOr - ( - UVec4::sOr - ( - UVec4::sOr - ( - det_near_zero, - Vec3::sLess(u, zero) - ), - UVec4::sOr - ( - Vec3::sLess(v, zero), - Vec3::sGreater(u + v, one) - ) - ), - Vec3::sLess(t, zero) - ); - - // Select intersection point or FLT_MAX based on if there is an intersection or not - return Vec3::sSelect(t, Vec3::sReplicate(FLT_MAX), no_intersection).GetX(); -} - -/// Intersect ray with 4 triangles in SOA format, returns 4 vector of closest points or FLT_MAX if no hit (uses bit tricks to do less divisions) -JPH_INLINE Vec4 RayTriangle4(Vec3Arg inOrigin, Vec3Arg inDirection, Vec4Arg inV0X, Vec4Arg inV0Y, Vec4Arg inV0Z, Vec4Arg inV1X, Vec4Arg inV1Y, Vec4Arg inV1Z, Vec4Arg inV2X, Vec4Arg inV2Y, Vec4Arg inV2Z) -{ - // Epsilon - Vec4 epsilon = Vec4::sReplicate(1.0e-12f); - - // Zero - Vec4 zero = Vec4::sZero(); - - // Find vectors for two edges sharing inV0 - Vec4 e1x = inV1X - inV0X; - Vec4 e1y = inV1Y - inV0Y; - Vec4 e1z = inV1Z - inV0Z; - Vec4 e2x = inV2X - inV0X; - Vec4 e2y = inV2Y - inV0Y; - Vec4 e2z = inV2Z - inV0Z; - - // Get direction vector components - Vec4 dx = inDirection.SplatX(); - Vec4 dy = inDirection.SplatY(); - Vec4 dz = inDirection.SplatZ(); - - // Begin calculating determinant - also used to calculate u parameter - Vec4 px = dy * e2z - dz * e2y; - Vec4 py = dz * e2x - dx * e2z; - Vec4 pz = dx * e2y - dy * e2x; - - // if determinant is near zero, ray lies in plane of triangle - Vec4 det = e1x * px + e1y * py + e1z * pz; - - // Get sign bit for determinant and make positive - Vec4 det_sign = Vec4::sAnd(det, UVec4::sReplicate(0x80000000).ReinterpretAsFloat()); - det = Vec4::sXor(det, det_sign); - - // Check which determinants are near zero - UVec4 det_near_zero = Vec4::sLess(det, epsilon); - - // Set components of the determinant to 1 that are near zero to avoid dividing by zero - det = Vec4::sSelect(det, Vec4::sReplicate(1.0f), det_near_zero); - - // Calculate distance from inV0 to ray origin - Vec4 sx = inOrigin.SplatX() - inV0X; - Vec4 sy = inOrigin.SplatY() - inV0Y; - Vec4 sz = inOrigin.SplatZ() - inV0Z; - - // Calculate u parameter and flip sign if determinant was negative - Vec4 u = Vec4::sXor(sx * px + sy * py + sz * pz, det_sign); - - // Prepare to test v parameter - Vec4 qx = sy * e1z - sz * e1y; - Vec4 qy = sz * e1x - sx * e1z; - Vec4 qz = sx * e1y - sy * e1x; - - // Calculate v parameter and flip sign if determinant was negative - Vec4 v = Vec4::sXor(dx * qx + dy * qy + dz * qz, det_sign); - - // Get intersection point and flip sign if determinant was negative - Vec4 t = Vec4::sXor(e2x * qx + e2y * qy + e2z * qz, det_sign); - - // Check if there is an intersection - UVec4 no_intersection = - UVec4::sOr - ( - UVec4::sOr - ( - UVec4::sOr - ( - det_near_zero, - Vec4::sLess(u, zero) - ), - UVec4::sOr - ( - Vec4::sLess(v, zero), - Vec4::sGreater(u + v, det) - ) - ), - Vec4::sLess(t, zero) - ); - - // Select intersection point or FLT_MAX based on if there is an intersection or not - return Vec4::sSelect(t / det, Vec4::sReplicate(FLT_MAX), no_intersection); -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/RayTriangle8.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/RayTriangle8.h deleted file mode 100644 index b97bca3f18d..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/RayTriangle8.h +++ /dev/null @@ -1,91 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -/// Intersect ray with 8 triangles in SOA format, returns 8 vector of closest points or FLT_MAX if no hit -JPH_INLINE Vec8 RayTriangle8(Vec3Arg inOrigin, Vec3Arg inDirection, Vec8Arg inV0X, Vec8Arg inV0Y, Vec8Arg inV0Z, Vec8Arg inV1X, Vec8Arg inV1Y, Vec8Arg inV1Z, Vec8Arg inV2X, Vec8Arg inV2Y, Vec8Arg inV2Z) -{ - // Epsilon - Vec8 epsilon = Vec8::sReplicate(1.0e-12f); - - // Zero & one - Vec8 zero = Vec8::sZero(); - Vec8 one = Vec8::sReplicate(1.0f); - - // Find vectors for two edges sharing inV0 - Vec8 e1x = inV1X - inV0X; - Vec8 e1y = inV1Y - inV0Y; - Vec8 e1z = inV1Z - inV0Z; - Vec8 e2x = inV2X - inV0X; - Vec8 e2y = inV2Y - inV0Y; - Vec8 e2z = inV2Z - inV0Z; - - // Get direction vector components - Vec8 dx = Vec8::sSplatX(Vec4(inDirection)); - Vec8 dy = Vec8::sSplatY(Vec4(inDirection)); - Vec8 dz = Vec8::sSplatZ(Vec4(inDirection)); - - // Begin calculating determinant - also used to calculate u parameter - Vec8 px = dy * e2z - dz * e2y; - Vec8 py = dz * e2x - dx * e2z; - Vec8 pz = dx * e2y - dy * e2x; - - // if determinant is near zero, ray lies in plane of triangle - Vec8 det = e1x * px + e1y * py + e1z * pz; - - // Check which determinants are near zero - UVec8 det_near_zero = Vec8::sLess(det.Abs(), epsilon); - - // Set components of the determinant to 1 that are near zero to avoid dividing by zero - det = Vec8::sSelect(det, Vec8::sReplicate(1.0f), det_near_zero); - - // Calculate distance from inV0 to ray origin - Vec8 sx = Vec8::sSplatX(Vec4(inOrigin)) - inV0X; - Vec8 sy = Vec8::sSplatY(Vec4(inOrigin)) - inV0Y; - Vec8 sz = Vec8::sSplatZ(Vec4(inOrigin)) - inV0Z; - - // Calculate u parameter and flip sign if determinant was negative - Vec8 u = (sx * px + sy * py + sz * pz) / det; - - // Prepare to test v parameter - Vec8 qx = sy * e1z - sz * e1y; - Vec8 qy = sz * e1x - sx * e1z; - Vec8 qz = sx * e1y - sy * e1x; - - // Calculate v parameter and flip sign if determinant was negative - Vec8 v = (dx * qx + dy * qy + dz * qz) / det; - - // Get intersection point and flip sign if determinant was negative - Vec8 t = (e2x * qx + e2y * qy + e2z * qz) / det; - - // Check if there is an intersection - UVec8 no_intersection = - UVec8::sOr - ( - UVec8::sOr - ( - UVec8::sOr - ( - det_near_zero, - Vec8::sLess(u, zero) - ), - UVec8::sOr - ( - Vec8::sLess(v, zero), - Vec8::sGreater(u + v, one) - ) - ), - Vec8::sLess(t, zero) - ); - - // Select intersection point or FLT_MAX based on if there is an intersection or not - return Vec8::sSelect(t, Vec8::sReplicate(FLT_MAX), no_intersection); -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/Sphere.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/Sphere.h deleted file mode 100644 index c9aa0b3e290..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/Sphere.h +++ /dev/null @@ -1,72 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -class [[nodiscard]] Sphere -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Constructor - inline Sphere() = default; - inline Sphere(const Float3 &inCenter, float inRadius) : mCenter(inCenter), mRadius(inRadius) { } - inline Sphere(Vec3Arg inCenter, float inRadius) : mRadius(inRadius) { inCenter.StoreFloat3(&mCenter); } - - /// Calculate the support vector for this convex shape. - inline Vec3 GetSupport(Vec3Arg inDirection) const - { - float length = inDirection.Length(); - return length > 0.0f ? Vec3::sLoadFloat3Unsafe(mCenter) + (mRadius/ length) * inDirection : Vec3::sLoadFloat3Unsafe(mCenter); - } - - // Properties - inline Vec3 GetCenter() const { return Vec3::sLoadFloat3Unsafe(mCenter); } - inline float GetRadius() const { return mRadius; } - - /// Test if two spheres overlap - inline bool Overlaps(const Sphere &inB) const - { - return (Vec3::sLoadFloat3Unsafe(mCenter) - Vec3::sLoadFloat3Unsafe(inB.mCenter)).LengthSq() <= Square(mRadius + inB.mRadius); - } - - /// Check if this sphere overlaps with a box - inline bool Overlaps(const AABox &inOther) const - { - return inOther.GetSqDistanceTo(GetCenter()) <= Square(mRadius); - } - - /// Create the minimal sphere that encapsulates this sphere and inPoint - inline void EncapsulatePoint(Vec3Arg inPoint) - { - // Calculate distance between point and center - Vec3 center = GetCenter(); - Vec3 d_vec = inPoint - center; - float d_sq = d_vec.LengthSq(); - if (d_sq > Square(mRadius)) - { - // It is further away than radius, we need to widen the sphere - // The diameter of the new sphere is radius + d, so the new radius is half of that - float d = sqrt(d_sq); - float radius = 0.5f * (mRadius + d); - - // The center needs to shift by new radius - old radius in the direction of d - center += (radius - mRadius) / d * d_vec; - - // Store new sphere - center.StoreFloat3(&mCenter); - mRadius = radius; - } - } - -private: - Float3 mCenter; - float mRadius; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/Triangle.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/Triangle.h deleted file mode 100644 index a88d08c1ff5..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Geometry/Triangle.h +++ /dev/null @@ -1,34 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -JPH_NAMESPACE_BEGIN - -/// A simple triangle and its material -class Triangle -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Constructor - Triangle() = default; - Triangle(const Float3 &inV1, const Float3 &inV2, const Float3 &inV3) : mV { inV1, inV2, inV3 } { } - Triangle(const Float3 &inV1, const Float3 &inV2, const Float3 &inV3, uint32 inMaterialIndex) : Triangle(inV1, inV2, inV3) { mMaterialIndex = inMaterialIndex; } - Triangle(Vec3Arg inV1, Vec3Arg inV2, Vec3Arg inV3) { inV1.StoreFloat3(&mV[0]); inV2.StoreFloat3(&mV[1]); inV3.StoreFloat3(&mV[2]); } - - /// Get center of triangle - Vec3 GetCentroid() const - { - return (Vec3::sLoadFloat3Unsafe(mV[0]) + Vec3::sLoadFloat3Unsafe(mV[1]) + Vec3::sLoadFloat3Unsafe(mV[2])) * (1.0f / 3.0f); - } - - /// Vertices - Float3 mV[3]; - uint32 mMaterialIndex = 0; ///< Follows mV[3] so that we can read mV as 4 vectors -}; - -using TriangleList = Array; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Jolt.cmake b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Jolt.cmake deleted file mode 100644 index 2b2828428ca..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Jolt.cmake +++ /dev/null @@ -1,634 +0,0 @@ -# Requires C++ 17 -set(CMAKE_CXX_STANDARD 17) -set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_CXX_EXTENSIONS OFF) - -# Root -set(JOLT_PHYSICS_ROOT ${PHYSICS_REPO_ROOT}/Jolt) - -# Source files -set(JOLT_PHYSICS_SRC_FILES - ${JOLT_PHYSICS_ROOT}/AABBTree/AABBTreeBuilder.cpp - ${JOLT_PHYSICS_ROOT}/AABBTree/AABBTreeBuilder.h - ${JOLT_PHYSICS_ROOT}/AABBTree/AABBTreeToBuffer.h - ${JOLT_PHYSICS_ROOT}/AABBTree/NodeCodec/NodeCodecQuadTreeHalfFloat.h - ${JOLT_PHYSICS_ROOT}/AABBTree/TriangleCodec/TriangleCodecIndexed8BitPackSOA4Flags.h - ${JOLT_PHYSICS_ROOT}/Core/ARMNeon.h - ${JOLT_PHYSICS_ROOT}/Core/Atomics.h - ${JOLT_PHYSICS_ROOT}/Core/ByteBuffer.h - ${JOLT_PHYSICS_ROOT}/Core/Color.cpp - ${JOLT_PHYSICS_ROOT}/Core/Color.h - ${JOLT_PHYSICS_ROOT}/Core/Core.h - ${JOLT_PHYSICS_ROOT}/Core/Factory.cpp - ${JOLT_PHYSICS_ROOT}/Core/Factory.h - ${JOLT_PHYSICS_ROOT}/Core/FixedSizeFreeList.h - ${JOLT_PHYSICS_ROOT}/Core/FixedSizeFreeList.inl - ${JOLT_PHYSICS_ROOT}/Core/FPControlWord.h - ${JOLT_PHYSICS_ROOT}/Core/FPException.h - ${JOLT_PHYSICS_ROOT}/Core/FPFlushDenormals.h - ${JOLT_PHYSICS_ROOT}/Core/HashCombine.h - ${JOLT_PHYSICS_ROOT}/Core/InsertionSort.h - ${JOLT_PHYSICS_ROOT}/Core/IssueReporting.cpp - ${JOLT_PHYSICS_ROOT}/Core/IssueReporting.h - ${JOLT_PHYSICS_ROOT}/Core/JobSystem.h - ${JOLT_PHYSICS_ROOT}/Core/JobSystem.inl - ${JOLT_PHYSICS_ROOT}/Core/JobSystemSingleThreaded.cpp - ${JOLT_PHYSICS_ROOT}/Core/JobSystemSingleThreaded.h - ${JOLT_PHYSICS_ROOT}/Core/JobSystemThreadPool.cpp - ${JOLT_PHYSICS_ROOT}/Core/JobSystemThreadPool.h - ${JOLT_PHYSICS_ROOT}/Core/JobSystemWithBarrier.cpp - ${JOLT_PHYSICS_ROOT}/Core/JobSystemWithBarrier.h - ${JOLT_PHYSICS_ROOT}/Core/LinearCurve.cpp - ${JOLT_PHYSICS_ROOT}/Core/LinearCurve.h - ${JOLT_PHYSICS_ROOT}/Core/LockFreeHashMap.h - ${JOLT_PHYSICS_ROOT}/Core/LockFreeHashMap.inl - ${JOLT_PHYSICS_ROOT}/Core/Memory.cpp - ${JOLT_PHYSICS_ROOT}/Core/Memory.h - ${JOLT_PHYSICS_ROOT}/Core/Mutex.h - ${JOLT_PHYSICS_ROOT}/Core/MutexArray.h - ${JOLT_PHYSICS_ROOT}/Core/NonCopyable.h - ${JOLT_PHYSICS_ROOT}/Core/Profiler.cpp - ${JOLT_PHYSICS_ROOT}/Core/Profiler.h - ${JOLT_PHYSICS_ROOT}/Core/Profiler.inl - ${JOLT_PHYSICS_ROOT}/Core/QuickSort.h - ${JOLT_PHYSICS_ROOT}/Core/Reference.h - ${JOLT_PHYSICS_ROOT}/Core/Result.h - ${JOLT_PHYSICS_ROOT}/Core/RTTI.cpp - ${JOLT_PHYSICS_ROOT}/Core/RTTI.h - ${JOLT_PHYSICS_ROOT}/Core/Semaphore.cpp - ${JOLT_PHYSICS_ROOT}/Core/Semaphore.h - ${JOLT_PHYSICS_ROOT}/Core/StaticArray.h - ${JOLT_PHYSICS_ROOT}/Core/StreamIn.h - ${JOLT_PHYSICS_ROOT}/Core/StreamOut.h - ${JOLT_PHYSICS_ROOT}/Core/StreamUtils.h - ${JOLT_PHYSICS_ROOT}/Core/StreamWrapper.h - ${JOLT_PHYSICS_ROOT}/Core/StringTools.cpp - ${JOLT_PHYSICS_ROOT}/Core/StringTools.h - ${JOLT_PHYSICS_ROOT}/Core/STLAlignedAllocator.h - ${JOLT_PHYSICS_ROOT}/Core/STLAllocator.h - ${JOLT_PHYSICS_ROOT}/Core/STLTempAllocator.h - ${JOLT_PHYSICS_ROOT}/Core/TempAllocator.h - ${JOLT_PHYSICS_ROOT}/Core/TickCounter.cpp - ${JOLT_PHYSICS_ROOT}/Core/TickCounter.h - ${JOLT_PHYSICS_ROOT}/Core/UnorderedMap.h - ${JOLT_PHYSICS_ROOT}/Core/UnorderedSet.h - ${JOLT_PHYSICS_ROOT}/Geometry/AABox.h - ${JOLT_PHYSICS_ROOT}/Geometry/AABox4.h - ${JOLT_PHYSICS_ROOT}/Geometry/ClipPoly.h - ${JOLT_PHYSICS_ROOT}/Geometry/ClosestPoint.h - ${JOLT_PHYSICS_ROOT}/Geometry/ConvexHullBuilder.cpp - ${JOLT_PHYSICS_ROOT}/Geometry/ConvexHullBuilder.h - ${JOLT_PHYSICS_ROOT}/Geometry/ConvexHullBuilder2D.cpp - ${JOLT_PHYSICS_ROOT}/Geometry/ConvexHullBuilder2D.h - ${JOLT_PHYSICS_ROOT}/Geometry/ConvexSupport.h - ${JOLT_PHYSICS_ROOT}/Geometry/Ellipse.h - ${JOLT_PHYSICS_ROOT}/Geometry/EPAConvexHullBuilder.h - ${JOLT_PHYSICS_ROOT}/Geometry/EPAPenetrationDepth.h - ${JOLT_PHYSICS_ROOT}/Geometry/GJKClosestPoint.h - ${JOLT_PHYSICS_ROOT}/Geometry/IndexedTriangle.h - ${JOLT_PHYSICS_ROOT}/Geometry/Indexify.cpp - ${JOLT_PHYSICS_ROOT}/Geometry/Indexify.h - ${JOLT_PHYSICS_ROOT}/Geometry/MortonCode.h - ${JOLT_PHYSICS_ROOT}/Geometry/OrientedBox.cpp - ${JOLT_PHYSICS_ROOT}/Geometry/OrientedBox.h - ${JOLT_PHYSICS_ROOT}/Geometry/Plane.h - ${JOLT_PHYSICS_ROOT}/Geometry/RayAABox.h - ${JOLT_PHYSICS_ROOT}/Geometry/RayAABox8.h - ${JOLT_PHYSICS_ROOT}/Geometry/RayCapsule.h - ${JOLT_PHYSICS_ROOT}/Geometry/RayCylinder.h - ${JOLT_PHYSICS_ROOT}/Geometry/RaySphere.h - ${JOLT_PHYSICS_ROOT}/Geometry/RayTriangle.h - ${JOLT_PHYSICS_ROOT}/Geometry/RayTriangle8.h - ${JOLT_PHYSICS_ROOT}/Geometry/Sphere.h - ${JOLT_PHYSICS_ROOT}/Geometry/Triangle.h - ${JOLT_PHYSICS_ROOT}/Jolt.cmake - ${JOLT_PHYSICS_ROOT}/Jolt.h - ${JOLT_PHYSICS_ROOT}/Math/DMat44.h - ${JOLT_PHYSICS_ROOT}/Math/DMat44.inl - ${JOLT_PHYSICS_ROOT}/Math/Double3.h - ${JOLT_PHYSICS_ROOT}/Math/DVec3.h - ${JOLT_PHYSICS_ROOT}/Math/DVec3.inl - ${JOLT_PHYSICS_ROOT}/Math/DynMatrix.h - ${JOLT_PHYSICS_ROOT}/Math/EigenValueSymmetric.h - ${JOLT_PHYSICS_ROOT}/Math/FindRoot.h - ${JOLT_PHYSICS_ROOT}/Math/Float2.h - ${JOLT_PHYSICS_ROOT}/Math/Float3.h - ${JOLT_PHYSICS_ROOT}/Math/Float4.h - ${JOLT_PHYSICS_ROOT}/Math/GaussianElimination.h - ${JOLT_PHYSICS_ROOT}/Math/HalfFloat.h - ${JOLT_PHYSICS_ROOT}/Math/Mat44.h - ${JOLT_PHYSICS_ROOT}/Math/Mat44.inl - ${JOLT_PHYSICS_ROOT}/Math/Math.h - ${JOLT_PHYSICS_ROOT}/Math/MathTypes.h - ${JOLT_PHYSICS_ROOT}/Math/Matrix.h - ${JOLT_PHYSICS_ROOT}/Math/Quat.h - ${JOLT_PHYSICS_ROOT}/Math/Quat.inl - ${JOLT_PHYSICS_ROOT}/Math/Real.h - ${JOLT_PHYSICS_ROOT}/Math/Swizzle.h - ${JOLT_PHYSICS_ROOT}/Math/Trigonometry.h - ${JOLT_PHYSICS_ROOT}/Math/UVec4.h - ${JOLT_PHYSICS_ROOT}/Math/UVec4.inl - ${JOLT_PHYSICS_ROOT}/Math/UVec8.h - ${JOLT_PHYSICS_ROOT}/Math/UVec8.inl - ${JOLT_PHYSICS_ROOT}/Math/Vec3.cpp - ${JOLT_PHYSICS_ROOT}/Math/Vec3.h - ${JOLT_PHYSICS_ROOT}/Math/Vec3.inl - ${JOLT_PHYSICS_ROOT}/Math/Vec4.h - ${JOLT_PHYSICS_ROOT}/Math/Vec4.inl - ${JOLT_PHYSICS_ROOT}/Math/Vec8.h - ${JOLT_PHYSICS_ROOT}/Math/Vec8.inl - ${JOLT_PHYSICS_ROOT}/Math/Vector.h - ${JOLT_PHYSICS_ROOT}/ObjectStream/GetPrimitiveTypeOfType.h - ${JOLT_PHYSICS_ROOT}/ObjectStream/ObjectStream.cpp - ${JOLT_PHYSICS_ROOT}/ObjectStream/ObjectStream.h - ${JOLT_PHYSICS_ROOT}/ObjectStream/ObjectStreamBinaryIn.cpp - ${JOLT_PHYSICS_ROOT}/ObjectStream/ObjectStreamBinaryIn.h - ${JOLT_PHYSICS_ROOT}/ObjectStream/ObjectStreamBinaryOut.cpp - ${JOLT_PHYSICS_ROOT}/ObjectStream/ObjectStreamBinaryOut.h - ${JOLT_PHYSICS_ROOT}/ObjectStream/ObjectStreamIn.cpp - ${JOLT_PHYSICS_ROOT}/ObjectStream/ObjectStreamIn.h - ${JOLT_PHYSICS_ROOT}/ObjectStream/ObjectStreamOut.cpp - ${JOLT_PHYSICS_ROOT}/ObjectStream/ObjectStreamOut.h - ${JOLT_PHYSICS_ROOT}/ObjectStream/ObjectStreamTextIn.cpp - ${JOLT_PHYSICS_ROOT}/ObjectStream/ObjectStreamTextIn.h - ${JOLT_PHYSICS_ROOT}/ObjectStream/ObjectStreamTextOut.cpp - ${JOLT_PHYSICS_ROOT}/ObjectStream/ObjectStreamTextOut.h - ${JOLT_PHYSICS_ROOT}/ObjectStream/ObjectStreamTypes.h - ${JOLT_PHYSICS_ROOT}/ObjectStream/SerializableAttribute.h - ${JOLT_PHYSICS_ROOT}/ObjectStream/SerializableAttributeEnum.h - ${JOLT_PHYSICS_ROOT}/ObjectStream/SerializableAttributeTyped.h - ${JOLT_PHYSICS_ROOT}/ObjectStream/SerializableObject.cpp - ${JOLT_PHYSICS_ROOT}/ObjectStream/SerializableObject.h - ${JOLT_PHYSICS_ROOT}/ObjectStream/TypeDeclarations.cpp - ${JOLT_PHYSICS_ROOT}/ObjectStream/TypeDeclarations.h - ${JOLT_PHYSICS_ROOT}/Physics/Body/AllowedDOFs.h - ${JOLT_PHYSICS_ROOT}/Physics/Body/Body.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Body/Body.h - ${JOLT_PHYSICS_ROOT}/Physics/Body/Body.inl - ${JOLT_PHYSICS_ROOT}/Physics/Body/BodyAccess.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Body/BodyAccess.h - ${JOLT_PHYSICS_ROOT}/Physics/Body/BodyActivationListener.h - ${JOLT_PHYSICS_ROOT}/Physics/Body/BodyCreationSettings.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Body/BodyCreationSettings.h - ${JOLT_PHYSICS_ROOT}/Physics/Body/BodyFilter.h - ${JOLT_PHYSICS_ROOT}/Physics/Body/BodyID.h - ${JOLT_PHYSICS_ROOT}/Physics/Body/BodyInterface.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Body/BodyInterface.h - ${JOLT_PHYSICS_ROOT}/Physics/Body/BodyLock.h - ${JOLT_PHYSICS_ROOT}/Physics/Body/BodyLockInterface.h - ${JOLT_PHYSICS_ROOT}/Physics/Body/BodyLockMulti.h - ${JOLT_PHYSICS_ROOT}/Physics/Body/BodyManager.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Body/BodyManager.h - ${JOLT_PHYSICS_ROOT}/Physics/Body/BodyPair.h - ${JOLT_PHYSICS_ROOT}/Physics/Body/BodyType.h - ${JOLT_PHYSICS_ROOT}/Physics/Body/MassProperties.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Body/MassProperties.h - ${JOLT_PHYSICS_ROOT}/Physics/Body/MotionProperties.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Body/MotionProperties.h - ${JOLT_PHYSICS_ROOT}/Physics/Body/MotionProperties.inl - ${JOLT_PHYSICS_ROOT}/Physics/Body/MotionQuality.h - ${JOLT_PHYSICS_ROOT}/Physics/Body/MotionType.h - ${JOLT_PHYSICS_ROOT}/Physics/Character/Character.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Character/Character.h - ${JOLT_PHYSICS_ROOT}/Physics/Character/CharacterBase.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Character/CharacterBase.h - ${JOLT_PHYSICS_ROOT}/Physics/Character/CharacterVirtual.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Character/CharacterVirtual.h - ${JOLT_PHYSICS_ROOT}/Physics/Collision/AABoxCast.h - ${JOLT_PHYSICS_ROOT}/Physics/Collision/ActiveEdgeMode.h - ${JOLT_PHYSICS_ROOT}/Physics/Collision/ActiveEdges.h - ${JOLT_PHYSICS_ROOT}/Physics/Collision/BackFaceMode.h - ${JOLT_PHYSICS_ROOT}/Physics/Collision/BroadPhase/BroadPhase.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Collision/BroadPhase/BroadPhase.h - ${JOLT_PHYSICS_ROOT}/Physics/Collision/BroadPhase/BroadPhaseBruteForce.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Collision/BroadPhase/BroadPhaseBruteForce.h - ${JOLT_PHYSICS_ROOT}/Physics/Collision/BroadPhase/BroadPhaseLayer.h - ${JOLT_PHYSICS_ROOT}/Physics/Collision/BroadPhase/BroadPhaseLayerInterfaceMask.h - ${JOLT_PHYSICS_ROOT}/Physics/Collision/BroadPhase/BroadPhaseLayerInterfaceTable.h - ${JOLT_PHYSICS_ROOT}/Physics/Collision/BroadPhase/BroadPhaseQuadTree.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Collision/BroadPhase/BroadPhaseQuadTree.h - ${JOLT_PHYSICS_ROOT}/Physics/Collision/BroadPhase/BroadPhaseQuery.h - ${JOLT_PHYSICS_ROOT}/Physics/Collision/BroadPhase/ObjectVsBroadPhaseLayerFilterMask.h - ${JOLT_PHYSICS_ROOT}/Physics/Collision/BroadPhase/ObjectVsBroadPhaseLayerFilterTable.h - ${JOLT_PHYSICS_ROOT}/Physics/Collision/BroadPhase/QuadTree.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Collision/BroadPhase/QuadTree.h - ${JOLT_PHYSICS_ROOT}/Physics/Collision/CastConvexVsTriangles.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Collision/CastConvexVsTriangles.h - ${JOLT_PHYSICS_ROOT}/Physics/Collision/CastSphereVsTriangles.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Collision/CastSphereVsTriangles.h - ${JOLT_PHYSICS_ROOT}/Physics/Collision/CastResult.h - ${JOLT_PHYSICS_ROOT}/Physics/Collision/CollectFacesMode.h - ${JOLT_PHYSICS_ROOT}/Physics/Collision/CollideConvexVsTriangles.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Collision/CollideConvexVsTriangles.h - ${JOLT_PHYSICS_ROOT}/Physics/Collision/CollidePointResult.h - ${JOLT_PHYSICS_ROOT}/Physics/Collision/CollideShape.h - ${JOLT_PHYSICS_ROOT}/Physics/Collision/CollideSoftBodyVerticesVsTriangles.h - ${JOLT_PHYSICS_ROOT}/Physics/Collision/CollideSphereVsTriangles.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Collision/CollideSphereVsTriangles.h - ${JOLT_PHYSICS_ROOT}/Physics/Collision/CollisionCollector.h - ${JOLT_PHYSICS_ROOT}/Physics/Collision/CollisionCollectorImpl.h - ${JOLT_PHYSICS_ROOT}/Physics/Collision/CollisionDispatch.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Collision/CollisionDispatch.h - ${JOLT_PHYSICS_ROOT}/Physics/Collision/CollisionGroup.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Collision/CollisionGroup.h - ${JOLT_PHYSICS_ROOT}/Physics/Collision/ContactListener.h - ${JOLT_PHYSICS_ROOT}/Physics/Collision/EstimateCollisionResponse.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Collision/EstimateCollisionResponse.h - ${JOLT_PHYSICS_ROOT}/Physics/Collision/GroupFilter.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Collision/GroupFilter.h - ${JOLT_PHYSICS_ROOT}/Physics/Collision/GroupFilterTable.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Collision/GroupFilterTable.h - ${JOLT_PHYSICS_ROOT}/Physics/Collision/InternalEdgeRemovingCollector.h - ${JOLT_PHYSICS_ROOT}/Physics/Collision/ManifoldBetweenTwoFaces.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Collision/ManifoldBetweenTwoFaces.h - ${JOLT_PHYSICS_ROOT}/Physics/Collision/NarrowPhaseQuery.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Collision/NarrowPhaseQuery.h - ${JOLT_PHYSICS_ROOT}/Physics/Collision/NarrowPhaseStats.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Collision/NarrowPhaseStats.h - ${JOLT_PHYSICS_ROOT}/Physics/Collision/ObjectLayer.h - ${JOLT_PHYSICS_ROOT}/Physics/Collision/ObjectLayerPairFilterMask.h - ${JOLT_PHYSICS_ROOT}/Physics/Collision/ObjectLayerPairFilterTable.h - ${JOLT_PHYSICS_ROOT}/Physics/Collision/PhysicsMaterial.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Collision/PhysicsMaterial.h - ${JOLT_PHYSICS_ROOT}/Physics/Collision/PhysicsMaterialSimple.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Collision/PhysicsMaterialSimple.h - ${JOLT_PHYSICS_ROOT}/Physics/Collision/RayCast.h - ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/BoxShape.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/BoxShape.h - ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/CapsuleShape.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/CapsuleShape.h - ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/CompoundShape.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/CompoundShape.h - ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/CompoundShapeVisitors.h - ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/ConvexHullShape.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/ConvexHullShape.h - ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/ConvexShape.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/ConvexShape.h - ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/CylinderShape.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/CylinderShape.h - ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/DecoratedShape.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/DecoratedShape.h - ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/GetTrianglesContext.h - ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/HeightFieldShape.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/HeightFieldShape.h - ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/MeshShape.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/MeshShape.h - ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/MutableCompoundShape.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/MutableCompoundShape.h - ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/OffsetCenterOfMassShape.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/OffsetCenterOfMassShape.h - ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/PolyhedronSubmergedVolumeCalculator.h - ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/RotatedTranslatedShape.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/RotatedTranslatedShape.h - ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/ScaledShape.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/ScaledShape.h - ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/ScaleHelpers.h - ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/Shape.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/Shape.h - ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/SphereShape.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/SphereShape.h - ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/StaticCompoundShape.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/StaticCompoundShape.h - ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/SubShapeID.h - ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/SubShapeIDPair.h - ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/TaperedCapsuleShape.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/TaperedCapsuleShape.gliffy - ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/TaperedCapsuleShape.h - ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/TriangleShape.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Collision/Shape/TriangleShape.h - ${JOLT_PHYSICS_ROOT}/Physics/Collision/ShapeCast.h - ${JOLT_PHYSICS_ROOT}/Physics/Collision/ShapeFilter.h - ${JOLT_PHYSICS_ROOT}/Physics/Collision/SortReverseAndStore.h - ${JOLT_PHYSICS_ROOT}/Physics/Collision/TransformedShape.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Collision/TransformedShape.h - ${JOLT_PHYSICS_ROOT}/Physics/Constraints/CalculateSolverSteps.h - ${JOLT_PHYSICS_ROOT}/Physics/Constraints/ConeConstraint.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Constraints/ConeConstraint.h - ${JOLT_PHYSICS_ROOT}/Physics/Constraints/Constraint.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Constraints/Constraint.h - ${JOLT_PHYSICS_ROOT}/Physics/Constraints/ConstraintManager.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Constraints/ConstraintManager.h - ${JOLT_PHYSICS_ROOT}/Physics/Constraints/ConstraintPart/AngleConstraintPart.h - ${JOLT_PHYSICS_ROOT}/Physics/Constraints/ConstraintPart/AxisConstraintPart.h - ${JOLT_PHYSICS_ROOT}/Physics/Constraints/ConstraintPart/DualAxisConstraintPart.h - ${JOLT_PHYSICS_ROOT}/Physics/Constraints/ConstraintPart/GearConstraintPart.h - ${JOLT_PHYSICS_ROOT}/Physics/Constraints/ConstraintPart/HingeRotationConstraintPart.h - ${JOLT_PHYSICS_ROOT}/Physics/Constraints/ConstraintPart/IndependentAxisConstraintPart.h - ${JOLT_PHYSICS_ROOT}/Physics/Constraints/ConstraintPart/PointConstraintPart.h - ${JOLT_PHYSICS_ROOT}/Physics/Constraints/ConstraintPart/RackAndPinionConstraintPart.h - ${JOLT_PHYSICS_ROOT}/Physics/Constraints/ConstraintPart/RotationEulerConstraintPart.h - ${JOLT_PHYSICS_ROOT}/Physics/Constraints/ConstraintPart/RotationQuatConstraintPart.h - ${JOLT_PHYSICS_ROOT}/Physics/Constraints/ConstraintPart/SpringPart.h - ${JOLT_PHYSICS_ROOT}/Physics/Constraints/ConstraintPart/SwingTwistConstraintPart.h - ${JOLT_PHYSICS_ROOT}/Physics/Constraints/ContactConstraintManager.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Constraints/ContactConstraintManager.h - ${JOLT_PHYSICS_ROOT}/Physics/Constraints/DistanceConstraint.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Constraints/DistanceConstraint.h - ${JOLT_PHYSICS_ROOT}/Physics/Constraints/FixedConstraint.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Constraints/FixedConstraint.h - ${JOLT_PHYSICS_ROOT}/Physics/Constraints/GearConstraint.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Constraints/GearConstraint.h - ${JOLT_PHYSICS_ROOT}/Physics/Constraints/HingeConstraint.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Constraints/HingeConstraint.h - ${JOLT_PHYSICS_ROOT}/Physics/Constraints/MotorSettings.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Constraints/MotorSettings.h - ${JOLT_PHYSICS_ROOT}/Physics/Constraints/PathConstraint.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Constraints/PathConstraint.h - ${JOLT_PHYSICS_ROOT}/Physics/Constraints/PathConstraintPath.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Constraints/PathConstraintPath.h - ${JOLT_PHYSICS_ROOT}/Physics/Constraints/PathConstraintPathHermite.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Constraints/PathConstraintPathHermite.h - ${JOLT_PHYSICS_ROOT}/Physics/Constraints/PointConstraint.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Constraints/PointConstraint.h - ${JOLT_PHYSICS_ROOT}/Physics/Constraints/PulleyConstraint.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Constraints/PulleyConstraint.h - ${JOLT_PHYSICS_ROOT}/Physics/Constraints/RackAndPinionConstraint.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Constraints/RackAndPinionConstraint.h - ${JOLT_PHYSICS_ROOT}/Physics/Constraints/SixDOFConstraint.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Constraints/SixDOFConstraint.h - ${JOLT_PHYSICS_ROOT}/Physics/Constraints/SliderConstraint.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Constraints/SliderConstraint.h - ${JOLT_PHYSICS_ROOT}/Physics/Constraints/SpringSettings.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Constraints/SpringSettings.h - ${JOLT_PHYSICS_ROOT}/Physics/Constraints/SwingTwistConstraint.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Constraints/SwingTwistConstraint.h - ${JOLT_PHYSICS_ROOT}/Physics/Constraints/TwoBodyConstraint.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Constraints/TwoBodyConstraint.h - ${JOLT_PHYSICS_ROOT}/Physics/DeterminismLog.cpp - ${JOLT_PHYSICS_ROOT}/Physics/DeterminismLog.h - ${JOLT_PHYSICS_ROOT}/Physics/EActivation.h - ${JOLT_PHYSICS_ROOT}/Physics/EPhysicsUpdateError.h - ${JOLT_PHYSICS_ROOT}/Physics/IslandBuilder.cpp - ${JOLT_PHYSICS_ROOT}/Physics/IslandBuilder.h - ${JOLT_PHYSICS_ROOT}/Physics/LargeIslandSplitter.cpp - ${JOLT_PHYSICS_ROOT}/Physics/LargeIslandSplitter.h - ${JOLT_PHYSICS_ROOT}/Physics/PhysicsLock.cpp - ${JOLT_PHYSICS_ROOT}/Physics/PhysicsLock.h - ${JOLT_PHYSICS_ROOT}/Physics/PhysicsScene.cpp - ${JOLT_PHYSICS_ROOT}/Physics/PhysicsScene.h - ${JOLT_PHYSICS_ROOT}/Physics/PhysicsSettings.h - ${JOLT_PHYSICS_ROOT}/Physics/PhysicsStepListener.h - ${JOLT_PHYSICS_ROOT}/Physics/PhysicsSystem.cpp - ${JOLT_PHYSICS_ROOT}/Physics/PhysicsSystem.h - ${JOLT_PHYSICS_ROOT}/Physics/PhysicsUpdateContext.cpp - ${JOLT_PHYSICS_ROOT}/Physics/PhysicsUpdateContext.h - ${JOLT_PHYSICS_ROOT}/Physics/Ragdoll/Ragdoll.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Ragdoll/Ragdoll.h - ${JOLT_PHYSICS_ROOT}/Physics/SoftBody/SoftBodyContactListener.h - ${JOLT_PHYSICS_ROOT}/Physics/SoftBody/SoftBodyCreationSettings.cpp - ${JOLT_PHYSICS_ROOT}/Physics/SoftBody/SoftBodyCreationSettings.h - ${JOLT_PHYSICS_ROOT}/Physics/SoftBody/SoftBodyManifold.h - ${JOLT_PHYSICS_ROOT}/Physics/SoftBody/SoftBodyMotionProperties.h - ${JOLT_PHYSICS_ROOT}/Physics/SoftBody/SoftBodyMotionProperties.cpp - ${JOLT_PHYSICS_ROOT}/Physics/SoftBody/SoftBodyShape.cpp - ${JOLT_PHYSICS_ROOT}/Physics/SoftBody/SoftBodyShape.h - ${JOLT_PHYSICS_ROOT}/Physics/SoftBody/SoftBodySharedSettings.cpp - ${JOLT_PHYSICS_ROOT}/Physics/SoftBody/SoftBodySharedSettings.h - ${JOLT_PHYSICS_ROOT}/Physics/SoftBody/SoftBodyVertex.h - ${JOLT_PHYSICS_ROOT}/Physics/StateRecorder.h - ${JOLT_PHYSICS_ROOT}/Physics/StateRecorderImpl.cpp - ${JOLT_PHYSICS_ROOT}/Physics/StateRecorderImpl.h - ${JOLT_PHYSICS_ROOT}/Physics/Vehicle/MotorcycleController.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Vehicle/MotorcycleController.h - ${JOLT_PHYSICS_ROOT}/Physics/Vehicle/TrackedVehicleController.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Vehicle/TrackedVehicleController.h - ${JOLT_PHYSICS_ROOT}/Physics/Vehicle/VehicleAntiRollBar.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Vehicle/VehicleAntiRollBar.h - ${JOLT_PHYSICS_ROOT}/Physics/Vehicle/VehicleCollisionTester.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Vehicle/VehicleCollisionTester.h - ${JOLT_PHYSICS_ROOT}/Physics/Vehicle/VehicleConstraint.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Vehicle/VehicleConstraint.h - ${JOLT_PHYSICS_ROOT}/Physics/Vehicle/VehicleController.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Vehicle/VehicleController.h - ${JOLT_PHYSICS_ROOT}/Physics/Vehicle/VehicleDifferential.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Vehicle/VehicleDifferential.h - ${JOLT_PHYSICS_ROOT}/Physics/Vehicle/VehicleEngine.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Vehicle/VehicleEngine.h - ${JOLT_PHYSICS_ROOT}/Physics/Vehicle/VehicleTrack.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Vehicle/VehicleTrack.h - ${JOLT_PHYSICS_ROOT}/Physics/Vehicle/VehicleTransmission.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Vehicle/VehicleTransmission.h - ${JOLT_PHYSICS_ROOT}/Physics/Vehicle/Wheel.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Vehicle/Wheel.h - ${JOLT_PHYSICS_ROOT}/Physics/Vehicle/WheeledVehicleController.cpp - ${JOLT_PHYSICS_ROOT}/Physics/Vehicle/WheeledVehicleController.h - ${JOLT_PHYSICS_ROOT}/RegisterTypes.cpp - ${JOLT_PHYSICS_ROOT}/RegisterTypes.h - ${JOLT_PHYSICS_ROOT}/Renderer/DebugRenderer.cpp - ${JOLT_PHYSICS_ROOT}/Renderer/DebugRenderer.h - ${JOLT_PHYSICS_ROOT}/Renderer/DebugRendererPlayback.cpp - ${JOLT_PHYSICS_ROOT}/Renderer/DebugRendererPlayback.h - ${JOLT_PHYSICS_ROOT}/Renderer/DebugRendererRecorder.cpp - ${JOLT_PHYSICS_ROOT}/Renderer/DebugRendererRecorder.h - ${JOLT_PHYSICS_ROOT}/Renderer/DebugRendererSimple.cpp - ${JOLT_PHYSICS_ROOT}/Renderer/DebugRendererSimple.h - ${JOLT_PHYSICS_ROOT}/Skeleton/SkeletalAnimation.cpp - ${JOLT_PHYSICS_ROOT}/Skeleton/SkeletalAnimation.h - ${JOLT_PHYSICS_ROOT}/Skeleton/Skeleton.cpp - ${JOLT_PHYSICS_ROOT}/Skeleton/Skeleton.h - ${JOLT_PHYSICS_ROOT}/Skeleton/SkeletonMapper.cpp - ${JOLT_PHYSICS_ROOT}/Skeleton/SkeletonMapper.h - ${JOLT_PHYSICS_ROOT}/Skeleton/SkeletonPose.cpp - ${JOLT_PHYSICS_ROOT}/Skeleton/SkeletonPose.h - ${JOLT_PHYSICS_ROOT}/TriangleGrouper/TriangleGrouper.h - ${JOLT_PHYSICS_ROOT}/TriangleGrouper/TriangleGrouperClosestCentroid.cpp - ${JOLT_PHYSICS_ROOT}/TriangleGrouper/TriangleGrouperClosestCentroid.h - ${JOLT_PHYSICS_ROOT}/TriangleGrouper/TriangleGrouperMorton.cpp - ${JOLT_PHYSICS_ROOT}/TriangleGrouper/TriangleGrouperMorton.h - ${JOLT_PHYSICS_ROOT}/TriangleSplitter/TriangleSplitter.cpp - ${JOLT_PHYSICS_ROOT}/TriangleSplitter/TriangleSplitter.h - ${JOLT_PHYSICS_ROOT}/TriangleSplitter/TriangleSplitterBinning.cpp - ${JOLT_PHYSICS_ROOT}/TriangleSplitter/TriangleSplitterBinning.h - ${JOLT_PHYSICS_ROOT}/TriangleSplitter/TriangleSplitterFixedLeafSize.cpp - ${JOLT_PHYSICS_ROOT}/TriangleSplitter/TriangleSplitterFixedLeafSize.h - ${JOLT_PHYSICS_ROOT}/TriangleSplitter/TriangleSplitterLongestAxis.cpp - ${JOLT_PHYSICS_ROOT}/TriangleSplitter/TriangleSplitterLongestAxis.h - ${JOLT_PHYSICS_ROOT}/TriangleSplitter/TriangleSplitterMean.cpp - ${JOLT_PHYSICS_ROOT}/TriangleSplitter/TriangleSplitterMean.h - ${JOLT_PHYSICS_ROOT}/TriangleSplitter/TriangleSplitterMorton.cpp - ${JOLT_PHYSICS_ROOT}/TriangleSplitter/TriangleSplitterMorton.h -) - -if ("${CMAKE_SYSTEM_NAME}" STREQUAL "Windows") - # Add natvis file - set(JOLT_PHYSICS_SRC_FILES ${JOLT_PHYSICS_SRC_FILES} ${JOLT_PHYSICS_ROOT}/Jolt.natvis) -endif() - -# Group source files -source_group(TREE ${JOLT_PHYSICS_ROOT} FILES ${JOLT_PHYSICS_SRC_FILES}) - -# Create Jolt lib -add_library(Jolt ${JOLT_PHYSICS_SRC_FILES}) - -if (BUILD_SHARED_LIBS) - # Set default visibility to hidden - set(CMAKE_CXX_VISIBILITY_PRESET hidden) - - if (GENERATE_DEBUG_SYMBOLS) - if (MSVC) - # MSVC specific option to enable PDB generation - set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} /DEBUG:FASTLINK") - else() - # Clang/GCC option to enable debug symbol generation - set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} -g") - endif() - endif() - - # Set linker flags for other build types to be the same as release - set(CMAKE_SHARED_LINKER_FLAGS_RELEASEASAN "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") - set(CMAKE_SHARED_LINKER_FLAGS_RELEASEUBSAN "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") - set(CMAKE_SHARED_LINKER_FLAGS_RELEASECOVERAGE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") - set(CMAKE_SHARED_LINKER_FLAGS_DISTRIBUTION "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") - - # Public define to instruct user code to import Jolt symbols (rather than use static linking) - target_compile_definitions(Jolt PUBLIC JPH_SHARED_LIBRARY) - - # Private define to instruct the library to export symbols for shared linking - target_compile_definitions(Jolt PRIVATE JPH_BUILD_SHARED_LIBRARY) -endif() - -target_include_directories(Jolt PUBLIC ${PHYSICS_REPO_ROOT}) -target_precompile_headers(Jolt PRIVATE ${JOLT_PHYSICS_ROOT}/Jolt.h) - -# Set the debug/non-debug build flags -target_compile_definitions(Jolt PUBLIC "$<$:_DEBUG>") -target_compile_definitions(Jolt PUBLIC "$<$:NDEBUG>") - -# ASAN should use the default allocators -target_compile_definitions(Jolt PUBLIC "$<$:JPH_DISABLE_TEMP_ALLOCATOR;JPH_DISABLE_CUSTOM_ALLOCATOR>") - -# Setting floating point exceptions -if (FLOATING_POINT_EXCEPTIONS_ENABLED AND "${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") - target_compile_definitions(Jolt PUBLIC "$<$:JPH_FLOATING_POINT_EXCEPTIONS_ENABLED>") -endif() - -# Setting the disable custom allocator flag -if (DISABLE_CUSTOM_ALLOCATOR) - target_compile_definitions(Jolt PUBLIC JPH_DISABLE_CUSTOM_ALLOCATOR) -endif() - -# Setting double precision flag -if (DOUBLE_PRECISION) - target_compile_definitions(Jolt PUBLIC JPH_DOUBLE_PRECISION) -endif() - -# Setting to attempt cross platform determinism -if (CROSS_PLATFORM_DETERMINISTIC) - target_compile_definitions(Jolt PUBLIC JPH_CROSS_PLATFORM_DETERMINISTIC) -endif() - -# Setting to determine number of bits in ObjectLayer -if (OBJECT_LAYER_BITS) - target_compile_definitions(Jolt PUBLIC JPH_OBJECT_LAYER_BITS=${OBJECT_LAYER_BITS}) -endif() - -# Setting to periodically trace broadphase stats to help determine if the broadphase layer configuration is optimal -if (TRACK_BROADPHASE_STATS) - target_compile_definitions(Jolt PUBLIC JPH_TRACK_BROADPHASE_STATS) -endif() - -# Setting to periodically trace narrowphase stats to help determine which collision queries could be optimized -if (TRACK_NARROWPHASE_STATS) - target_compile_definitions(Jolt PUBLIC JPH_TRACK_NARROWPHASE_STATS) -endif() - -# Enable the debug renderer -if (DEBUG_RENDERER_IN_DISTRIBUTION) - target_compile_definitions(Jolt PUBLIC "JPH_DEBUG_RENDERER") -elseif (DEBUG_RENDERER_IN_DEBUG_AND_RELEASE) - target_compile_definitions(Jolt PUBLIC "$<$:JPH_DEBUG_RENDERER>") -endif() - -# Enable the profiler -if (PROFILER_IN_DISTRIBUTION) - target_compile_definitions(Jolt PUBLIC "JPH_PROFILE_ENABLED") -elseif (PROFILER_IN_DEBUG_AND_RELEASE) - target_compile_definitions(Jolt PUBLIC "$<$:JPH_PROFILE_ENABLED>") -endif() - -# Emit the instruction set definitions to ensure that child projects use the same settings even if they override the used instruction sets (a mismatch causes link errors) -function(EMIT_X86_INSTRUCTION_SET_DEFINITIONS) - if (USE_AVX512) - target_compile_definitions(Jolt PUBLIC JPH_USE_AVX512) - endif() - if (USE_AVX2) - target_compile_definitions(Jolt PUBLIC JPH_USE_AVX2) - endif() - if (USE_AVX) - target_compile_definitions(Jolt PUBLIC JPH_USE_AVX) - endif() - if (USE_SSE4_1) - target_compile_definitions(Jolt PUBLIC JPH_USE_SSE4_1) - endif() - if (USE_SSE4_2) - target_compile_definitions(Jolt PUBLIC JPH_USE_SSE4_2) - endif() - if (USE_LZCNT) - target_compile_definitions(Jolt PUBLIC JPH_USE_LZCNT) - endif() - if (USE_TZCNT) - target_compile_definitions(Jolt PUBLIC JPH_USE_TZCNT) - endif() - if (USE_F16C) - target_compile_definitions(Jolt PUBLIC JPH_USE_F16C) - endif() - if (USE_FMADD AND NOT CROSS_PLATFORM_DETERMINISTIC) - target_compile_definitions(Jolt PUBLIC JPH_USE_FMADD) - endif() -endfunction() - -# Add the compiler commandline flags to select the right instruction sets -if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") - if ("${CMAKE_VS_PLATFORM_NAME}" STREQUAL "x86" OR "${CMAKE_VS_PLATFORM_NAME}" STREQUAL "x64") - if (USE_AVX512) - target_compile_options(Jolt PUBLIC /arch:AVX512) - elseif (USE_AVX2) - target_compile_options(Jolt PUBLIC /arch:AVX2) - elseif (USE_AVX) - target_compile_options(Jolt PUBLIC /arch:AVX) - endif() - EMIT_X86_INSTRUCTION_SET_DEFINITIONS() - endif() -else() - if (XCODE) - # XCode builds for multiple architectures, we can't set global flags - elseif (CROSS_COMPILE_ARM OR CMAKE_OSX_ARCHITECTURES MATCHES "arm64" OR "${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "aarch64") - # ARM64 uses no special commandline flags - elseif ("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "x86_64" OR "${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "AMD64") - # x64 - if (USE_AVX512) - target_compile_options(Jolt PUBLIC -mavx512f -mavx512vl -mavx512dq -mavx2 -mbmi -mpopcnt -mlzcnt -mf16c) - elseif (USE_AVX2) - target_compile_options(Jolt PUBLIC -mavx2 -mbmi -mpopcnt -mlzcnt -mf16c) - elseif (USE_AVX) - target_compile_options(Jolt PUBLIC -mavx -mpopcnt) - elseif (USE_SSE4_2) - target_compile_options(Jolt PUBLIC -msse4.2 -mpopcnt) - elseif (USE_SSE4_1) - target_compile_options(Jolt PUBLIC -msse4.1) - else() - target_compile_options(Jolt PUBLIC -msse2) - endif() - if (USE_LZCNT) - target_compile_options(Jolt PUBLIC -mlzcnt) - endif() - if (USE_TZCNT) - target_compile_options(Jolt PUBLIC -mbmi) - endif() - if (USE_F16C) - target_compile_options(Jolt PUBLIC -mf16c) - endif() - if (USE_FMADD AND NOT CROSS_PLATFORM_DETERMINISTIC) - target_compile_options(Jolt PUBLIC -mfma) - endif() - - # On 32-bit builds we need to default to using SSE instructions, the x87 FPU instructions have higher intermediate precision - # which will cause problems in the collision detection code (the effect is similar to leaving FMA on, search for - # JPH_PRECISE_MATH_ON for the locations where this is a problem). - if (NOT MSVC) - target_compile_options(Jolt PUBLIC -mfpmath=sse) - endif() - - EMIT_X86_INSTRUCTION_SET_DEFINITIONS() - endif() -endif() diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Jolt.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Jolt.h deleted file mode 100644 index 10747189599..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Jolt.h +++ /dev/null @@ -1,16 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -// Project includes -#include -#include -#include -#include -#include -#include -#include -#include -#include diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Jolt.natvis b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Jolt.natvis deleted file mode 100644 index 4252263dc02..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Jolt.natvis +++ /dev/null @@ -1,86 +0,0 @@ - - - - r={(int)r}, g={(int)g}, b={(int)b}, a={(int)a} - - - {x}, {y} - - - {x}, {y}, {z} - - - {x}, {y}, {z}, {w} - - - {mF32[0]}, {mF32[1]}, {mF32[2]}, L^2={mF32[0]*mF32[0]+mF32[1]*mF32[1]+mF32[2]*mF32[2]} - - - {mF64[0]}, {mF64[1]}, {mF64[2]}, L^2={mF64[0]*mF64[0]+mF64[1]*mF64[1]+mF64[2]*mF64[2]} - - - {mF32[0]}, {mF32[1]}, {mF32[2]}, {mF32[3]}, L^2={mF32[0]*mF32[0]+mF32[1]*mF32[1]+mF32[2]*mF32[2]+mF32[3]*mF32[3]} - - - {mU32[0]}, {mU32[1]}, {mU32[2]}, {mU32[3]} - - - {mValue} - - - {mCol[0].mF32[0]}, {mCol[1].mF32[0]}, {mCol[2].mF32[0]}, {mCol[3].mF32[0]} | {mCol[0].mF32[1]}, {mCol[1].mF32[1]}, {mCol[2].mF32[1]}, {mCol[3].mF32[1]} | {mCol[0].mF32[2]}, {mCol[1].mF32[2]}, {mCol[2].mF32[2]}, {mCol[3].mF32[2]} - - - {mCol[0].mF32[0]}, {mCol[1].mF32[0]}, {mCol[2].mF32[0]}, {mCol[3].mF32[0]} - - - {mCol[0].mF32[1]}, {mCol[1].mF32[1]}, {mCol[2].mF32[1]}, {mCol[3].mF32[1]} - - - {mCol[0].mF32[2]}, {mCol[1].mF32[2]}, {mCol[2].mF32[2]}, {mCol[3].mF32[2]} - - - {mCol[0].mF32[3]}, {mCol[1].mF32[3]}, {mCol[2].mF32[3]}, {mCol[3].mF32[3]} - - - - - {mCol[0].mF32[0]}, {mCol[1].mF32[0]}, {mCol[2].mF32[0]}, {mCol3.mF64[0]} | {mCol[0].mF32[1]}, {mCol[1].mF32[1]}, {mCol[2].mF32[1]}, {mCol3.mF64[1]} | {mCol[0].mF32[2]}, {mCol[1].mF32[2]}, {mCol[2].mF32[2]}, {mCol3.mF64[2]} - - - {mCol[0].mF32[0]}, {mCol[1].mF32[0]}, {mCol[2].mF32[0]}, {mCol3.mF64[0]} - - - {mCol[0].mF32[1]}, {mCol[1].mF32[1]}, {mCol[2].mF32[1]}, {mCol3.mF64[1]} - - - {mCol[0].mF32[2]}, {mCol[1].mF32[2]}, {mCol[2].mF32[2]}, {mCol3.mF64[2]} - - - {mCol[0].mF32[3]}, {mCol[1].mF32[3]}, {mCol[2].mF32[3]}, 1} - - - - - min=({mMin}), max=({mMax}) - - - {mID} - - - {mDebugName}: p=({mPosition.mF32[0],g}, {mPosition.mF32[1],g}, {mPosition.mF32[2],g}), r=({mRotation.mValue.mF32[0],g}, {mRotation.mValue.mF32[1],g}, {mRotation.mValue.mF32[2],g}, {mRotation.mValue.mF32[3],g}), v=({mLinearVelocity.mF32[0],g}, {mLinearVelocity.mF32[1],g}, {mLinearVelocity.mF32[2],g}), w=({mAngularVelocity.mF32[0],g}, {mAngularVelocity.mF32[1],g}, {mAngularVelocity.mF32[2],g}) - - - bodies={mBodies._Mypair._Myval2._Mylast - mBodies._Mypair._Myval2._Myfirst}, active={mActiveBodies._Mypair._Myval2._Mylast - mActiveBodies._Mypair._Myval2._Myfirst} - - - size={mSize} - - mSize - - mSize - (value_type *)mElements - - - - diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/DMat44.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/DMat44.h deleted file mode 100644 index 65c9687160b..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/DMat44.h +++ /dev/null @@ -1,158 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2022 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -/// Holds a 4x4 matrix of floats with the last column consisting of doubles -class [[nodiscard]] alignas(JPH_DVECTOR_ALIGNMENT) DMat44 -{ -public: - JPH_OVERRIDE_NEW_DELETE - - // Underlying column type - using Type = Vec4::Type; - using DType = DVec3::Type; - using DTypeArg = DVec3::TypeArg; - - // Argument type - using ArgType = DMat44Arg; - - /// Constructor - DMat44() = default; ///< Intentionally not initialized for performance reasons - JPH_INLINE DMat44(Vec4Arg inC1, Vec4Arg inC2, Vec4Arg inC3, DVec3Arg inC4); - DMat44(const DMat44 &inM2) = default; - DMat44 & operator = (const DMat44 &inM2) = default; - JPH_INLINE explicit DMat44(Mat44Arg inM); - JPH_INLINE DMat44(Mat44Arg inRot, DVec3Arg inT); - JPH_INLINE DMat44(Type inC1, Type inC2, Type inC3, DTypeArg inC4); - - /// Zero matrix - static JPH_INLINE DMat44 sZero(); - - /// Identity matrix - static JPH_INLINE DMat44 sIdentity(); - - /// Rotate from quaternion - static JPH_INLINE DMat44 sRotation(QuatArg inQuat) { return DMat44(Mat44::sRotation(inQuat), DVec3::sZero()); } - - /// Get matrix that translates - static JPH_INLINE DMat44 sTranslation(DVec3Arg inV) { return DMat44(Vec4(1, 0, 0, 0), Vec4(0, 1, 0, 0), Vec4(0, 0, 1, 0), inV); } - - /// Get matrix that rotates and translates - static JPH_INLINE DMat44 sRotationTranslation(QuatArg inR, DVec3Arg inT) { return DMat44(Mat44::sRotation(inR), inT); } - - /// Get inverse matrix of sRotationTranslation - static JPH_INLINE DMat44 sInverseRotationTranslation(QuatArg inR, DVec3Arg inT); - - /// Get matrix that scales (produces a matrix with (inV, 1) on its diagonal) - static JPH_INLINE DMat44 sScale(Vec3Arg inV) { return DMat44(Mat44::sScale(inV), DVec3::sZero()); } - - /// Convert to Mat44 rounding to nearest - JPH_INLINE Mat44 ToMat44() const { return Mat44(mCol[0], mCol[1], mCol[2], Vec3(mCol3)); } - - /// Comparison - JPH_INLINE bool operator == (DMat44Arg inM2) const; - JPH_INLINE bool operator != (DMat44Arg inM2) const { return !(*this == inM2); } - - /// Test if two matrices are close - JPH_INLINE bool IsClose(DMat44Arg inM2, float inMaxDistSq = 1.0e-12f) const; - - /// Multiply matrix by matrix - JPH_INLINE DMat44 operator * (Mat44Arg inM) const; - - /// Multiply matrix by matrix - JPH_INLINE DMat44 operator * (DMat44Arg inM) const; - - /// Multiply vector by matrix - JPH_INLINE DVec3 operator * (Vec3Arg inV) const; - - /// Multiply vector by matrix - JPH_INLINE DVec3 operator * (DVec3Arg inV) const; - - /// Multiply vector by only 3x3 part of the matrix - JPH_INLINE Vec3 Multiply3x3(Vec3Arg inV) const { return GetRotation().Multiply3x3(inV); } - - /// Multiply vector by only 3x3 part of the matrix - JPH_INLINE DVec3 Multiply3x3(DVec3Arg inV) const; - - /// Multiply vector by only 3x3 part of the transpose of the matrix (\f$result = this^T \: inV\f$) - JPH_INLINE Vec3 Multiply3x3Transposed(Vec3Arg inV) const { return GetRotation().Multiply3x3Transposed(inV); } - - /// Scale a matrix: result = this * Mat44::sScale(inScale) - JPH_INLINE DMat44 PreScaled(Vec3Arg inScale) const; - - /// Scale a matrix: result = Mat44::sScale(inScale) * this - JPH_INLINE DMat44 PostScaled(Vec3Arg inScale) const; - - /// Pre multiply by translation matrix: result = this * Mat44::sTranslation(inTranslation) - JPH_INLINE DMat44 PreTranslated(Vec3Arg inTranslation) const; - - /// Pre multiply by translation matrix: result = this * Mat44::sTranslation(inTranslation) - JPH_INLINE DMat44 PreTranslated(DVec3Arg inTranslation) const; - - /// Post multiply by translation matrix: result = Mat44::sTranslation(inTranslation) * this (i.e. add inTranslation to the 4-th column) - JPH_INLINE DMat44 PostTranslated(Vec3Arg inTranslation) const; - - /// Post multiply by translation matrix: result = Mat44::sTranslation(inTranslation) * this (i.e. add inTranslation to the 4-th column) - JPH_INLINE DMat44 PostTranslated(DVec3Arg inTranslation) const; - - /// Access to the columns - JPH_INLINE Vec3 GetAxisX() const { return Vec3(mCol[0]); } - JPH_INLINE void SetAxisX(Vec3Arg inV) { mCol[0] = Vec4(inV, 0.0f); } - JPH_INLINE Vec3 GetAxisY() const { return Vec3(mCol[1]); } - JPH_INLINE void SetAxisY(Vec3Arg inV) { mCol[1] = Vec4(inV, 0.0f); } - JPH_INLINE Vec3 GetAxisZ() const { return Vec3(mCol[2]); } - JPH_INLINE void SetAxisZ(Vec3Arg inV) { mCol[2] = Vec4(inV, 0.0f); } - JPH_INLINE DVec3 GetTranslation() const { return mCol3; } - JPH_INLINE void SetTranslation(DVec3Arg inV) { mCol3 = inV; } - JPH_INLINE Vec3 GetColumn3(uint inCol) const { JPH_ASSERT(inCol < 3); return Vec3(mCol[inCol]); } - JPH_INLINE void SetColumn3(uint inCol, Vec3Arg inV) { JPH_ASSERT(inCol < 3); mCol[inCol] = Vec4(inV, 0.0f); } - JPH_INLINE Vec4 GetColumn4(uint inCol) const { JPH_ASSERT(inCol < 3); return mCol[inCol]; } - JPH_INLINE void SetColumn4(uint inCol, Vec4Arg inV) { JPH_ASSERT(inCol < 3); mCol[inCol] = inV; } - - /// Transpose 3x3 subpart of matrix - JPH_INLINE Mat44 Transposed3x3() const { return GetRotation().Transposed3x3(); } - - /// Inverse 4x4 matrix - JPH_INLINE DMat44 Inversed() const; - - /// Inverse 4x4 matrix when it only contains rotation and translation - JPH_INLINE DMat44 InversedRotationTranslation() const; - - /// Get rotation part only (note: retains the first 3 values from the bottom row) - JPH_INLINE Mat44 GetRotation() const { return Mat44(mCol[0], mCol[1], mCol[2], Vec4(0, 0, 0, 1)); } - - /// Updates the rotation part of this matrix (the first 3 columns) - JPH_INLINE void SetRotation(Mat44Arg inRotation); - - /// Convert to quaternion - JPH_INLINE Quat GetQuaternion() const { return GetRotation().GetQuaternion(); } - - /// Get matrix that transforms a direction with the same transform as this matrix (length is not preserved) - JPH_INLINE Mat44 GetDirectionPreservingMatrix() const { return GetRotation().Inversed3x3().Transposed3x3(); } - - /// Works identical to Mat44::Decompose - JPH_INLINE DMat44 Decompose(Vec3 &outScale) const { return DMat44(GetRotation().Decompose(outScale), mCol3); } - - /// To String - friend ostream & operator << (ostream &inStream, DMat44Arg inM) - { - inStream << inM.mCol[0] << ", " << inM.mCol[1] << ", " << inM.mCol[2] << ", " << inM.mCol3; - return inStream; - } - -private: - Vec4 mCol[3]; ///< Rotation columns - DVec3 mCol3; ///< Translation column, 4th element is assumed to be 1 -}; - -static_assert(is_trivial(), "Is supposed to be a trivial type!"); - -JPH_NAMESPACE_END - -#include "DMat44.inl" diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/DMat44.inl b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/DMat44.inl deleted file mode 100644 index 462cf79114c..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/DMat44.inl +++ /dev/null @@ -1,310 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -DMat44::DMat44(Vec4Arg inC1, Vec4Arg inC2, Vec4Arg inC3, DVec3Arg inC4) : - mCol { inC1, inC2, inC3 }, - mCol3(inC4) -{ -} - -DMat44::DMat44(Type inC1, Type inC2, Type inC3, DTypeArg inC4) : - mCol { inC1, inC2, inC3 }, - mCol3(inC4) -{ -} - -DMat44::DMat44(Mat44Arg inM) : - mCol { inM.GetColumn4(0), inM.GetColumn4(1), inM.GetColumn4(2) }, - mCol3(inM.GetTranslation()) -{ -} - -DMat44::DMat44(Mat44Arg inRot, DVec3Arg inT) : - mCol { inRot.GetColumn4(0), inRot.GetColumn4(1), inRot.GetColumn4(2) }, - mCol3(inT) -{ -} - -DMat44 DMat44::sZero() -{ - return DMat44(Vec4::sZero(), Vec4::sZero(), Vec4::sZero(), DVec3::sZero()); -} - -DMat44 DMat44::sIdentity() -{ - return DMat44(Vec4(1, 0, 0, 0), Vec4(0, 1, 0, 0), Vec4(0, 0, 1, 0), DVec3::sZero()); -} - -DMat44 DMat44::sInverseRotationTranslation(QuatArg inR, DVec3Arg inT) -{ - Mat44 m = Mat44::sRotation(inR.Conjugated()); - DMat44 dm(m, DVec3::sZero()); - dm.SetTranslation(-dm.Multiply3x3(inT)); - return dm; -} - -bool DMat44::operator == (DMat44Arg inM2) const -{ - return mCol[0] == inM2.mCol[0] - && mCol[1] == inM2.mCol[1] - && mCol[2] == inM2.mCol[2] - && mCol3 == inM2.mCol3; -} - -bool DMat44::IsClose(DMat44Arg inM2, float inMaxDistSq) const -{ - for (int i = 0; i < 3; ++i) - if (!mCol[i].IsClose(inM2.mCol[i], inMaxDistSq)) - return false; - return mCol3.IsClose(inM2.mCol3, double(inMaxDistSq)); -} - -DVec3 DMat44::operator * (Vec3Arg inV) const -{ -#if defined(JPH_USE_AVX) - __m128 t = _mm_mul_ps(mCol[0].mValue, _mm_shuffle_ps(inV.mValue, inV.mValue, _MM_SHUFFLE(0, 0, 0, 0))); - t = _mm_add_ps(t, _mm_mul_ps(mCol[1].mValue, _mm_shuffle_ps(inV.mValue, inV.mValue, _MM_SHUFFLE(1, 1, 1, 1)))); - t = _mm_add_ps(t, _mm_mul_ps(mCol[2].mValue, _mm_shuffle_ps(inV.mValue, inV.mValue, _MM_SHUFFLE(2, 2, 2, 2)))); - return DVec3::sFixW(_mm256_add_pd(mCol3.mValue, _mm256_cvtps_pd(t))); -#elif defined(JPH_USE_SSE) - __m128 t = _mm_mul_ps(mCol[0].mValue, _mm_shuffle_ps(inV.mValue, inV.mValue, _MM_SHUFFLE(0, 0, 0, 0))); - t = _mm_add_ps(t, _mm_mul_ps(mCol[1].mValue, _mm_shuffle_ps(inV.mValue, inV.mValue, _MM_SHUFFLE(1, 1, 1, 1)))); - t = _mm_add_ps(t, _mm_mul_ps(mCol[2].mValue, _mm_shuffle_ps(inV.mValue, inV.mValue, _MM_SHUFFLE(2, 2, 2, 2)))); - __m128d low = _mm_add_pd(mCol3.mValue.mLow, _mm_cvtps_pd(t)); - __m128d high = _mm_add_pd(mCol3.mValue.mHigh, _mm_cvtps_pd(_mm_shuffle_ps(t, t, _MM_SHUFFLE(2, 2, 2, 2)))); - return DVec3({ low, high }); -#elif defined(JPH_USE_NEON) - float32x4_t t = vmulq_f32(mCol[0].mValue, vdupq_laneq_f32(inV.mValue, 0)); - t = vmlaq_f32(t, mCol[1].mValue, vdupq_laneq_f32(inV.mValue, 1)); - t = vmlaq_f32(t, mCol[2].mValue, vdupq_laneq_f32(inV.mValue, 2)); - float64x2_t low = vaddq_f64(mCol3.mValue.val[0], vcvt_f64_f32(vget_low_f32(t))); - float64x2_t high = vaddq_f64(mCol3.mValue.val[1], vcvt_high_f64_f32(t)); - return DVec3::sFixW({ low, high }); -#else - return DVec3( - mCol3.mF64[0] + double(mCol[0].mF32[0] * inV.mF32[0] + mCol[1].mF32[0] * inV.mF32[1] + mCol[2].mF32[0] * inV.mF32[2]), - mCol3.mF64[1] + double(mCol[0].mF32[1] * inV.mF32[0] + mCol[1].mF32[1] * inV.mF32[1] + mCol[2].mF32[1] * inV.mF32[2]), - mCol3.mF64[2] + double(mCol[0].mF32[2] * inV.mF32[0] + mCol[1].mF32[2] * inV.mF32[1] + mCol[2].mF32[2] * inV.mF32[2])); -#endif -} - -DVec3 DMat44::operator * (DVec3Arg inV) const -{ -#if defined(JPH_USE_AVX) - __m256d t = _mm256_add_pd(mCol3.mValue, _mm256_mul_pd(_mm256_cvtps_pd(mCol[0].mValue), _mm256_set1_pd(inV.mF64[0]))); - t = _mm256_add_pd(t, _mm256_mul_pd(_mm256_cvtps_pd(mCol[1].mValue), _mm256_set1_pd(inV.mF64[1]))); - t = _mm256_add_pd(t, _mm256_mul_pd(_mm256_cvtps_pd(mCol[2].mValue), _mm256_set1_pd(inV.mF64[2]))); - return DVec3::sFixW(t); -#elif defined(JPH_USE_SSE) - __m128d xxxx = _mm_set1_pd(inV.mF64[0]); - __m128d yyyy = _mm_set1_pd(inV.mF64[1]); - __m128d zzzz = _mm_set1_pd(inV.mF64[2]); - __m128 col0 = mCol[0].mValue; - __m128 col1 = mCol[1].mValue; - __m128 col2 = mCol[2].mValue; - __m128d t_low = _mm_add_pd(mCol3.mValue.mLow, _mm_mul_pd(_mm_cvtps_pd(col0), xxxx)); - t_low = _mm_add_pd(t_low, _mm_mul_pd(_mm_cvtps_pd(col1), yyyy)); - t_low = _mm_add_pd(t_low, _mm_mul_pd(_mm_cvtps_pd(col2), zzzz)); - __m128d t_high = _mm_add_pd(mCol3.mValue.mHigh, _mm_mul_pd(_mm_cvtps_pd(_mm_shuffle_ps(col0, col0, _MM_SHUFFLE(2, 2, 2, 2))), xxxx)); - t_high = _mm_add_pd(t_high, _mm_mul_pd(_mm_cvtps_pd(_mm_shuffle_ps(col1, col1, _MM_SHUFFLE(2, 2, 2, 2))), yyyy)); - t_high = _mm_add_pd(t_high, _mm_mul_pd(_mm_cvtps_pd(_mm_shuffle_ps(col2, col2, _MM_SHUFFLE(2, 2, 2, 2))), zzzz)); - return DVec3({ t_low, t_high }); -#elif defined(JPH_USE_NEON) - float64x2_t xxxx = vdupq_laneq_f64(inV.mValue.val[0], 0); - float64x2_t yyyy = vdupq_laneq_f64(inV.mValue.val[0], 1); - float64x2_t zzzz = vdupq_laneq_f64(inV.mValue.val[1], 0); - float32x4_t col0 = mCol[0].mValue; - float32x4_t col1 = mCol[1].mValue; - float32x4_t col2 = mCol[2].mValue; - float64x2_t t_low = vaddq_f64(mCol3.mValue.val[0], vmulq_f64(vcvt_f64_f32(vget_low_f32(col0)), xxxx)); - t_low = vaddq_f64(t_low, vmulq_f64(vcvt_f64_f32(vget_low_f32(col1)), yyyy)); - t_low = vaddq_f64(t_low, vmulq_f64(vcvt_f64_f32(vget_low_f32(col2)), zzzz)); - float64x2_t t_high = vaddq_f64(mCol3.mValue.val[1], vmulq_f64(vcvt_high_f64_f32(col0), xxxx)); - t_high = vaddq_f64(t_high, vmulq_f64(vcvt_high_f64_f32(col1), yyyy)); - t_high = vaddq_f64(t_high, vmulq_f64(vcvt_high_f64_f32(col2), zzzz)); - return DVec3::sFixW({ t_low, t_high }); -#else - return DVec3( - mCol3.mF64[0] + double(mCol[0].mF32[0]) * inV.mF64[0] + double(mCol[1].mF32[0]) * inV.mF64[1] + double(mCol[2].mF32[0]) * inV.mF64[2], - mCol3.mF64[1] + double(mCol[0].mF32[1]) * inV.mF64[0] + double(mCol[1].mF32[1]) * inV.mF64[1] + double(mCol[2].mF32[1]) * inV.mF64[2], - mCol3.mF64[2] + double(mCol[0].mF32[2]) * inV.mF64[0] + double(mCol[1].mF32[2]) * inV.mF64[1] + double(mCol[2].mF32[2]) * inV.mF64[2]); -#endif -} - -DVec3 DMat44::Multiply3x3(DVec3Arg inV) const -{ -#if defined(JPH_USE_AVX) - __m256d t = _mm256_mul_pd(_mm256_cvtps_pd(mCol[0].mValue), _mm256_set1_pd(inV.mF64[0])); - t = _mm256_add_pd(t, _mm256_mul_pd(_mm256_cvtps_pd(mCol[1].mValue), _mm256_set1_pd(inV.mF64[1]))); - t = _mm256_add_pd(t, _mm256_mul_pd(_mm256_cvtps_pd(mCol[2].mValue), _mm256_set1_pd(inV.mF64[2]))); - return DVec3::sFixW(t); -#elif defined(JPH_USE_SSE) - __m128d xxxx = _mm_set1_pd(inV.mF64[0]); - __m128d yyyy = _mm_set1_pd(inV.mF64[1]); - __m128d zzzz = _mm_set1_pd(inV.mF64[2]); - __m128 col0 = mCol[0].mValue; - __m128 col1 = mCol[1].mValue; - __m128 col2 = mCol[2].mValue; - __m128d t_low = _mm_mul_pd(_mm_cvtps_pd(col0), xxxx); - t_low = _mm_add_pd(t_low, _mm_mul_pd(_mm_cvtps_pd(col1), yyyy)); - t_low = _mm_add_pd(t_low, _mm_mul_pd(_mm_cvtps_pd(col2), zzzz)); - __m128d t_high = _mm_mul_pd(_mm_cvtps_pd(_mm_shuffle_ps(col0, col0, _MM_SHUFFLE(2, 2, 2, 2))), xxxx); - t_high = _mm_add_pd(t_high, _mm_mul_pd(_mm_cvtps_pd(_mm_shuffle_ps(col1, col1, _MM_SHUFFLE(2, 2, 2, 2))), yyyy)); - t_high = _mm_add_pd(t_high, _mm_mul_pd(_mm_cvtps_pd(_mm_shuffle_ps(col2, col2, _MM_SHUFFLE(2, 2, 2, 2))), zzzz)); - return DVec3({ t_low, t_high }); -#elif defined(JPH_USE_NEON) - float64x2_t xxxx = vdupq_laneq_f64(inV.mValue.val[0], 0); - float64x2_t yyyy = vdupq_laneq_f64(inV.mValue.val[0], 1); - float64x2_t zzzz = vdupq_laneq_f64(inV.mValue.val[1], 0); - float32x4_t col0 = mCol[0].mValue; - float32x4_t col1 = mCol[1].mValue; - float32x4_t col2 = mCol[2].mValue; - float64x2_t t_low = vmulq_f64(vcvt_f64_f32(vget_low_f32(col0)), xxxx); - t_low = vaddq_f64(t_low, vmulq_f64(vcvt_f64_f32(vget_low_f32(col1)), yyyy)); - t_low = vaddq_f64(t_low, vmulq_f64(vcvt_f64_f32(vget_low_f32(col2)), zzzz)); - float64x2_t t_high = vmulq_f64(vcvt_high_f64_f32(col0), xxxx); - t_high = vaddq_f64(t_high, vmulq_f64(vcvt_high_f64_f32(col1), yyyy)); - t_high = vaddq_f64(t_high, vmulq_f64(vcvt_high_f64_f32(col2), zzzz)); - return DVec3::sFixW({ t_low, t_high }); -#else - return DVec3( - double(mCol[0].mF32[0]) * inV.mF64[0] + double(mCol[1].mF32[0]) * inV.mF64[1] + double(mCol[2].mF32[0]) * inV.mF64[2], - double(mCol[0].mF32[1]) * inV.mF64[0] + double(mCol[1].mF32[1]) * inV.mF64[1] + double(mCol[2].mF32[1]) * inV.mF64[2], - double(mCol[0].mF32[2]) * inV.mF64[0] + double(mCol[1].mF32[2]) * inV.mF64[1] + double(mCol[2].mF32[2]) * inV.mF64[2]); -#endif -} - -DMat44 DMat44::operator * (Mat44Arg inM) const -{ - DMat44 result; - - // Rotation part -#if defined(JPH_USE_SSE) - for (int i = 0; i < 3; ++i) - { - __m128 c = inM.GetColumn4(i).mValue; - __m128 t = _mm_mul_ps(mCol[0].mValue, _mm_shuffle_ps(c, c, _MM_SHUFFLE(0, 0, 0, 0))); - t = _mm_add_ps(t, _mm_mul_ps(mCol[1].mValue, _mm_shuffle_ps(c, c, _MM_SHUFFLE(1, 1, 1, 1)))); - t = _mm_add_ps(t, _mm_mul_ps(mCol[2].mValue, _mm_shuffle_ps(c, c, _MM_SHUFFLE(2, 2, 2, 2)))); - result.mCol[i].mValue = t; - } -#elif defined(JPH_USE_NEON) - for (int i = 0; i < 3; ++i) - { - Type c = inM.GetColumn4(i).mValue; - Type t = vmulq_f32(mCol[0].mValue, vdupq_laneq_f32(c, 0)); - t = vmlaq_f32(t, mCol[1].mValue, vdupq_laneq_f32(c, 1)); - t = vmlaq_f32(t, mCol[2].mValue, vdupq_laneq_f32(c, 2)); - result.mCol[i].mValue = t; - } -#else - for (int i = 0; i < 3; ++i) - { - Vec4 coli = inM.GetColumn4(i); - result.mCol[i] = mCol[0] * coli.mF32[0] + mCol[1] * coli.mF32[1] + mCol[2] * coli.mF32[2]; - } -#endif - - // Translation part - result.mCol3 = *this * inM.GetTranslation(); - - return result; -} - -DMat44 DMat44::operator * (DMat44Arg inM) const -{ - DMat44 result; - - // Rotation part -#if defined(JPH_USE_SSE) - for (int i = 0; i < 3; ++i) - { - __m128 c = inM.mCol[i].mValue; - __m128 t = _mm_mul_ps(mCol[0].mValue, _mm_shuffle_ps(c, c, _MM_SHUFFLE(0, 0, 0, 0))); - t = _mm_add_ps(t, _mm_mul_ps(mCol[1].mValue, _mm_shuffle_ps(c, c, _MM_SHUFFLE(1, 1, 1, 1)))); - t = _mm_add_ps(t, _mm_mul_ps(mCol[2].mValue, _mm_shuffle_ps(c, c, _MM_SHUFFLE(2, 2, 2, 2)))); - result.mCol[i].mValue = t; - } -#elif defined(JPH_USE_NEON) - for (int i = 0; i < 3; ++i) - { - Type c = inM.GetColumn4(i).mValue; - Type t = vmulq_f32(mCol[0].mValue, vdupq_laneq_f32(c, 0)); - t = vmlaq_f32(t, mCol[1].mValue, vdupq_laneq_f32(c, 1)); - t = vmlaq_f32(t, mCol[2].mValue, vdupq_laneq_f32(c, 2)); - result.mCol[i].mValue = t; - } -#else - for (int i = 0; i < 3; ++i) - { - Vec4 coli = inM.mCol[i]; - result.mCol[i] = mCol[0] * coli.mF32[0] + mCol[1] * coli.mF32[1] + mCol[2] * coli.mF32[2]; - } -#endif - - // Translation part - result.mCol3 = *this * inM.GetTranslation(); - - return result; -} - -void DMat44::SetRotation(Mat44Arg inRotation) -{ - mCol[0] = inRotation.GetColumn4(0); - mCol[1] = inRotation.GetColumn4(1); - mCol[2] = inRotation.GetColumn4(2); -} - -DMat44 DMat44::PreScaled(Vec3Arg inScale) const -{ - return DMat44(inScale.GetX() * mCol[0], inScale.GetY() * mCol[1], inScale.GetZ() * mCol[2], mCol3); -} - -DMat44 DMat44::PostScaled(Vec3Arg inScale) const -{ - Vec4 scale(inScale, 1); - return DMat44(scale * mCol[0], scale * mCol[1], scale * mCol[2], DVec3(scale) * mCol3); -} - -DMat44 DMat44::PreTranslated(Vec3Arg inTranslation) const -{ - return DMat44(mCol[0], mCol[1], mCol[2], GetTranslation() + Multiply3x3(inTranslation)); -} - -DMat44 DMat44::PreTranslated(DVec3Arg inTranslation) const -{ - return DMat44(mCol[0], mCol[1], mCol[2], GetTranslation() + Multiply3x3(inTranslation)); -} - -DMat44 DMat44::PostTranslated(Vec3Arg inTranslation) const -{ - return DMat44(mCol[0], mCol[1], mCol[2], GetTranslation() + inTranslation); -} - -DMat44 DMat44::PostTranslated(DVec3Arg inTranslation) const -{ - return DMat44(mCol[0], mCol[1], mCol[2], GetTranslation() + inTranslation); -} - -DMat44 DMat44::Inversed() const -{ - DMat44 m(GetRotation().Inversed3x3()); - m.mCol3 = -m.Multiply3x3(mCol3); - return m; -} - -DMat44 DMat44::InversedRotationTranslation() const -{ - DMat44 m(GetRotation().Transposed3x3()); - m.mCol3 = -m.Multiply3x3(mCol3); - return m; -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/DVec3.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/DVec3.h deleted file mode 100644 index 58e0a06f337..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/DVec3.h +++ /dev/null @@ -1,288 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -/// 3 component vector of doubles (stored as 4 vectors). -/// Note that we keep the 4th component the same as the 3rd component to avoid divisions by zero when JPH_FLOATING_POINT_EXCEPTIONS_ENABLED defined -class [[nodiscard]] alignas(JPH_DVECTOR_ALIGNMENT) DVec3 -{ -public: - JPH_OVERRIDE_NEW_DELETE - - // Underlying vector type -#if defined(JPH_USE_AVX) - using Type = __m256d; - using TypeArg = __m256d; -#elif defined(JPH_USE_SSE) - using Type = struct { __m128d mLow, mHigh; }; - using TypeArg = const Type &; -#elif defined(JPH_USE_NEON) - using Type = float64x2x2_t; - using TypeArg = const Type &; -#else - using Type = struct { double mData[4]; }; - using TypeArg = const Type &; -#endif - - // Argument type - using ArgType = DVec3Arg; - - /// Constructor - DVec3() = default; ///< Intentionally not initialized for performance reasons - DVec3(const DVec3 &inRHS) = default; - DVec3 & operator = (const DVec3 &inRHS) = default; - JPH_INLINE explicit DVec3(Vec3Arg inRHS); - JPH_INLINE explicit DVec3(Vec4Arg inRHS); - JPH_INLINE DVec3(TypeArg inRHS) : mValue(inRHS) { CheckW(); } - - /// Create a vector from 3 components - JPH_INLINE DVec3(double inX, double inY, double inZ); - - /// Load 3 doubles from memory - explicit JPH_INLINE DVec3(const Double3 &inV); - - /// Vector with all zeros - static JPH_INLINE DVec3 sZero(); - - /// Vectors with the principal axis - static JPH_INLINE DVec3 sAxisX() { return DVec3(1, 0, 0); } - static JPH_INLINE DVec3 sAxisY() { return DVec3(0, 1, 0); } - static JPH_INLINE DVec3 sAxisZ() { return DVec3(0, 0, 1); } - - /// Replicate inV across all components - static JPH_INLINE DVec3 sReplicate(double inV); - - /// Vector with all NaN's - static JPH_INLINE DVec3 sNaN(); - - /// Load 3 doubles from memory (reads 64 bits extra which it doesn't use) - static JPH_INLINE DVec3 sLoadDouble3Unsafe(const Double3 &inV); - - /// Store 3 doubles to memory - JPH_INLINE void StoreDouble3(Double3 *outV) const; - - /// Convert to float vector 3 rounding to nearest - JPH_INLINE explicit operator Vec3() const; - - /// Prepare to convert to float vector 3 rounding towards zero (returns DVec3 that can be converted to a Vec3 to get the rounding) - JPH_INLINE DVec3 PrepareRoundToZero() const; - - /// Prepare to convert to float vector 3 rounding towards positive/negative inf (returns DVec3 that can be converted to a Vec3 to get the rounding) - JPH_INLINE DVec3 PrepareRoundToInf() const; - - /// Convert to float vector 3 rounding down - JPH_INLINE Vec3 ToVec3RoundDown() const; - - /// Convert to float vector 3 rounding up - JPH_INLINE Vec3 ToVec3RoundUp() const; - - /// Return the minimum value of each of the components - static JPH_INLINE DVec3 sMin(DVec3Arg inV1, DVec3Arg inV2); - - /// Return the maximum of each of the components - static JPH_INLINE DVec3 sMax(DVec3Arg inV1, DVec3Arg inV2); - - /// Clamp a vector between min and max (component wise) - static JPH_INLINE DVec3 sClamp(DVec3Arg inV, DVec3Arg inMin, DVec3Arg inMax); - - /// Equals (component wise) - static JPH_INLINE DVec3 sEquals(DVec3Arg inV1, DVec3Arg inV2); - - /// Less than (component wise) - static JPH_INLINE DVec3 sLess(DVec3Arg inV1, DVec3Arg inV2); - - /// Less than or equal (component wise) - static JPH_INLINE DVec3 sLessOrEqual(DVec3Arg inV1, DVec3Arg inV2); - - /// Greater than (component wise) - static JPH_INLINE DVec3 sGreater(DVec3Arg inV1, DVec3Arg inV2); - - /// Greater than or equal (component wise) - static JPH_INLINE DVec3 sGreaterOrEqual(DVec3Arg inV1, DVec3Arg inV2); - - /// Calculates inMul1 * inMul2 + inAdd - static JPH_INLINE DVec3 sFusedMultiplyAdd(DVec3Arg inMul1, DVec3Arg inMul2, DVec3Arg inAdd); - - /// Component wise select, returns inV1 when highest bit of inControl = 0 and inV2 when highest bit of inControl = 1 - static JPH_INLINE DVec3 sSelect(DVec3Arg inV1, DVec3Arg inV2, DVec3Arg inControl); - - /// Logical or (component wise) - static JPH_INLINE DVec3 sOr(DVec3Arg inV1, DVec3Arg inV2); - - /// Logical xor (component wise) - static JPH_INLINE DVec3 sXor(DVec3Arg inV1, DVec3Arg inV2); - - /// Logical and (component wise) - static JPH_INLINE DVec3 sAnd(DVec3Arg inV1, DVec3Arg inV2); - - /// Store if X is true in bit 0, Y in bit 1, Z in bit 2 and W in bit 3 (true is when highest bit of component is set) - JPH_INLINE int GetTrues() const; - - /// Test if any of the components are true (true is when highest bit of component is set) - JPH_INLINE bool TestAnyTrue() const; - - /// Test if all components are true (true is when highest bit of component is set) - JPH_INLINE bool TestAllTrue() const; - - /// Get individual components -#if defined(JPH_USE_AVX) - JPH_INLINE double GetX() const { return _mm_cvtsd_f64(_mm256_castpd256_pd128(mValue)); } - JPH_INLINE double GetY() const { return mF64[1]; } - JPH_INLINE double GetZ() const { return mF64[2]; } -#elif defined(JPH_USE_SSE) - JPH_INLINE double GetX() const { return _mm_cvtsd_f64(mValue.mLow); } - JPH_INLINE double GetY() const { return mF64[1]; } - JPH_INLINE double GetZ() const { return _mm_cvtsd_f64(mValue.mHigh); } -#elif defined(JPH_USE_NEON) - JPH_INLINE double GetX() const { return vgetq_lane_f64(mValue.val[0], 0); } - JPH_INLINE double GetY() const { return vgetq_lane_f64(mValue.val[0], 1); } - JPH_INLINE double GetZ() const { return vgetq_lane_f64(mValue.val[1], 0); } -#else - JPH_INLINE double GetX() const { return mF64[0]; } - JPH_INLINE double GetY() const { return mF64[1]; } - JPH_INLINE double GetZ() const { return mF64[2]; } -#endif - - /// Set individual components - JPH_INLINE void SetX(double inX) { mF64[0] = inX; } - JPH_INLINE void SetY(double inY) { mF64[1] = inY; } - JPH_INLINE void SetZ(double inZ) { mF64[2] = mF64[3] = inZ; } // Assure Z and W are the same - - /// Set all components - JPH_INLINE void Set(double inX, double inY, double inZ) { *this = DVec3(inX, inY, inZ); } - - /// Get double component by index - JPH_INLINE double operator [] (uint inCoordinate) const { JPH_ASSERT(inCoordinate < 3); return mF64[inCoordinate]; } - - /// Set double component by index - JPH_INLINE void SetComponent(uint inCoordinate, double inValue) { JPH_ASSERT(inCoordinate < 3); mF64[inCoordinate] = inValue; mValue = sFixW(mValue); } // Assure Z and W are the same - - /// Comparison - JPH_INLINE bool operator == (DVec3Arg inV2) const; - JPH_INLINE bool operator != (DVec3Arg inV2) const { return !(*this == inV2); } - - /// Test if two vectors are close - JPH_INLINE bool IsClose(DVec3Arg inV2, double inMaxDistSq = 1.0e-24) const; - - /// Test if vector is near zero - JPH_INLINE bool IsNearZero(double inMaxDistSq = 1.0e-24) const; - - /// Test if vector is normalized - JPH_INLINE bool IsNormalized(double inTolerance = 1.0e-12) const; - - /// Test if vector contains NaN elements - JPH_INLINE bool IsNaN() const; - - /// Multiply two double vectors (component wise) - JPH_INLINE DVec3 operator * (DVec3Arg inV2) const; - - /// Multiply vector with double - JPH_INLINE DVec3 operator * (double inV2) const; - - /// Multiply vector with double - friend JPH_INLINE DVec3 operator * (double inV1, DVec3Arg inV2); - - /// Divide vector by double - JPH_INLINE DVec3 operator / (double inV2) const; - - /// Multiply vector with double - JPH_INLINE DVec3 & operator *= (double inV2); - - /// Multiply vector with vector - JPH_INLINE DVec3 & operator *= (DVec3Arg inV2); - - /// Divide vector by double - JPH_INLINE DVec3 & operator /= (double inV2); - - /// Add two vectors (component wise) - JPH_INLINE DVec3 operator + (Vec3Arg inV2) const; - - /// Add two double vectors (component wise) - JPH_INLINE DVec3 operator + (DVec3Arg inV2) const; - - /// Add two vectors (component wise) - JPH_INLINE DVec3 & operator += (Vec3Arg inV2); - - /// Add two double vectors (component wise) - JPH_INLINE DVec3 & operator += (DVec3Arg inV2); - - /// Negate - JPH_INLINE DVec3 operator - () const; - - /// Subtract two vectors (component wise) - JPH_INLINE DVec3 operator - (Vec3Arg inV2) const; - - /// Subtract two double vectors (component wise) - JPH_INLINE DVec3 operator - (DVec3Arg inV2) const; - - /// Add two vectors (component wise) - JPH_INLINE DVec3 & operator -= (Vec3Arg inV2); - - /// Add two double vectors (component wise) - JPH_INLINE DVec3 & operator -= (DVec3Arg inV2); - - /// Divide (component wise) - JPH_INLINE DVec3 operator / (DVec3Arg inV2) const; - - /// Return the absolute value of each of the components - JPH_INLINE DVec3 Abs() const; - - /// Reciprocal vector (1 / value) for each of the components - JPH_INLINE DVec3 Reciprocal() const; - - /// Cross product - JPH_INLINE DVec3 Cross(DVec3Arg inV2) const; - - /// Dot product - JPH_INLINE double Dot(DVec3Arg inV2) const; - - /// Squared length of vector - JPH_INLINE double LengthSq() const; - - /// Length of vector - JPH_INLINE double Length() const; - - /// Normalize vector - JPH_INLINE DVec3 Normalized() const; - - /// Component wise square root - JPH_INLINE DVec3 Sqrt() const; - - /// Get vector that contains the sign of each element (returns 1 if positive, -1 if negative) - JPH_INLINE DVec3 GetSign() const; - - /// To String - friend ostream & operator << (ostream &inStream, DVec3Arg inV) - { - inStream << inV.mF64[0] << ", " << inV.mF64[1] << ", " << inV.mF64[2]; - return inStream; - } - - /// Internal helper function that checks that W is equal to Z, so e.g. dividing by it should not generate div by 0 - JPH_INLINE void CheckW() const; - - /// Internal helper function that ensures that the Z component is replicated to the W component to prevent divisions by zero - static JPH_INLINE Type sFixW(TypeArg inValue); - - /// Representations of true and false for boolean operations - inline static const double cTrue = BitCast(~uint64(0)); - inline static const double cFalse = 0.0; - - union - { - Type mValue; - double mF64[4]; - }; -}; - -static_assert(is_trivial(), "Is supposed to be a trivial type!"); - -JPH_NAMESPACE_END - -#include "DVec3.inl" diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/DVec3.inl b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/DVec3.inl deleted file mode 100644 index 08a17f8464a..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/DVec3.inl +++ /dev/null @@ -1,921 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -// Create a std::hash for DVec3 -JPH_MAKE_HASHABLE(JPH::DVec3, t.GetX(), t.GetY(), t.GetZ()) - -JPH_NAMESPACE_BEGIN - -DVec3::DVec3(Vec3Arg inRHS) -{ -#if defined(JPH_USE_AVX) - mValue = _mm256_cvtps_pd(inRHS.mValue); -#elif defined(JPH_USE_SSE) - mValue.mLow = _mm_cvtps_pd(inRHS.mValue); - mValue.mHigh = _mm_cvtps_pd(_mm_shuffle_ps(inRHS.mValue, inRHS.mValue, _MM_SHUFFLE(2, 2, 2, 2))); -#elif defined(JPH_USE_NEON) - mValue.val[0] = vcvt_f64_f32(vget_low_f32(inRHS.mValue)); - mValue.val[1] = vcvt_high_f64_f32(inRHS.mValue); -#else - mF64[0] = (double)inRHS.GetX(); - mF64[1] = (double)inRHS.GetY(); - mF64[2] = (double)inRHS.GetZ(); - #ifdef JPH_FLOATING_POINT_EXCEPTIONS_ENABLED - mF64[3] = mF64[2]; - #endif -#endif -} - -DVec3::DVec3(Vec4Arg inRHS) : - DVec3(Vec3(inRHS)) -{ -} - -DVec3::DVec3(double inX, double inY, double inZ) -{ -#if defined(JPH_USE_AVX) - mValue = _mm256_set_pd(inZ, inZ, inY, inX); // Assure Z and W are the same -#elif defined(JPH_USE_SSE) - mValue.mLow = _mm_set_pd(inY, inX); - mValue.mHigh = _mm_set1_pd(inZ); -#elif defined(JPH_USE_NEON) - mValue.val[0] = vcombine_f64(vcreate_f64(*reinterpret_cast(&inX)), vcreate_f64(*reinterpret_cast(&inY))); - mValue.val[1] = vdupq_n_f64(inZ); -#else - mF64[0] = inX; - mF64[1] = inY; - mF64[2] = inZ; - #ifdef JPH_FLOATING_POINT_EXCEPTIONS_ENABLED - mF64[3] = mF64[2]; - #endif -#endif -} - -DVec3::DVec3(const Double3 &inV) -{ -#if defined(JPH_USE_AVX) - Type x = _mm256_castpd128_pd256(_mm_load_sd(&inV.x)); - Type y = _mm256_castpd128_pd256(_mm_load_sd(&inV.y)); - Type z = _mm256_broadcast_sd(&inV.z); - Type xy = _mm256_unpacklo_pd(x, y); - mValue = _mm256_blend_pd(xy, z, 0b1100); // Assure Z and W are the same -#elif defined(JPH_USE_SSE) - mValue.mLow = _mm_loadu_pd(&inV.x); - mValue.mHigh = _mm_set1_pd(inV.z); -#elif defined(JPH_USE_NEON) - mValue.val[0] = vld1q_f64(&inV.x); - mValue.val[1] = vdupq_n_f64(inV.z); -#else - mF64[0] = inV.x; - mF64[1] = inV.y; - mF64[2] = inV.z; - #ifdef JPH_FLOATING_POINT_EXCEPTIONS_ENABLED - mF64[3] = mF64[2]; - #endif -#endif -} - -void DVec3::CheckW() const -{ -#ifdef JPH_FLOATING_POINT_EXCEPTIONS_ENABLED - // Avoid asserts when both components are NaN - JPH_ASSERT(reinterpret_cast(mF64)[2] == reinterpret_cast(mF64)[3]); -#endif // JPH_FLOATING_POINT_EXCEPTIONS_ENABLED -} - -/// Internal helper function that ensures that the Z component is replicated to the W component to prevent divisions by zero -DVec3::Type DVec3::sFixW(TypeArg inValue) -{ -#ifdef JPH_FLOATING_POINT_EXCEPTIONS_ENABLED - #if defined(JPH_USE_AVX) - return _mm256_shuffle_pd(inValue, inValue, 2); - #elif defined(JPH_USE_SSE) - Type value; - value.mLow = inValue.mLow; - value.mHigh = _mm_shuffle_pd(inValue.mHigh, inValue.mHigh, 0); - return value; - #elif defined(JPH_USE_NEON) - Type value; - value.val[0] = inValue.val[0]; - value.val[1] = vdupq_laneq_f64(inValue.val[1], 0); - return value; - #else - Type value; - value.mData[0] = inValue.mData[0]; - value.mData[1] = inValue.mData[1]; - value.mData[2] = inValue.mData[2]; - value.mData[3] = inValue.mData[2]; - return value; - #endif -#else - return inValue; -#endif // JPH_FLOATING_POINT_EXCEPTIONS_ENABLED -} - -DVec3 DVec3::sZero() -{ -#if defined(JPH_USE_AVX) - return _mm256_setzero_pd(); -#elif defined(JPH_USE_SSE) - __m128d zero = _mm_setzero_pd(); - return DVec3({ zero, zero }); -#elif defined(JPH_USE_NEON) - float64x2_t zero = vdupq_n_f64(0.0); - return DVec3({ zero, zero }); -#else - return DVec3(0, 0, 0); -#endif -} - -DVec3 DVec3::sReplicate(double inV) -{ -#if defined(JPH_USE_AVX) - return _mm256_set1_pd(inV); -#elif defined(JPH_USE_SSE) - __m128d value = _mm_set1_pd(inV); - return DVec3({ value, value }); -#elif defined(JPH_USE_NEON) - float64x2_t value = vdupq_n_f64(inV); - return DVec3({ value, value }); -#else - return DVec3(inV, inV, inV); -#endif -} - -DVec3 DVec3::sNaN() -{ - return sReplicate(numeric_limits::quiet_NaN()); -} - -DVec3 DVec3::sLoadDouble3Unsafe(const Double3 &inV) -{ -#if defined(JPH_USE_AVX) - Type v = _mm256_loadu_pd(&inV.x); -#elif defined(JPH_USE_SSE) - Type v; - v.mLow = _mm_loadu_pd(&inV.x); - v.mHigh = _mm_set1_pd(inV.z); -#elif defined(JPH_USE_NEON) - Type v = vld1q_f64_x2(&inV.x); -#else - Type v = { inV.x, inV.y, inV.z }; -#endif - return sFixW(v); -} - -void DVec3::StoreDouble3(Double3 *outV) const -{ - outV->x = mF64[0]; - outV->y = mF64[1]; - outV->z = mF64[2]; -} - -DVec3::operator Vec3() const -{ -#if defined(JPH_USE_AVX) - return _mm256_cvtpd_ps(mValue); -#elif defined(JPH_USE_SSE) - __m128 low = _mm_cvtpd_ps(mValue.mLow); - __m128 high = _mm_cvtpd_ps(mValue.mHigh); - return _mm_shuffle_ps(low, high, _MM_SHUFFLE(1, 0, 1, 0)); -#elif defined(JPH_USE_NEON) - return vcvt_high_f32_f64(vcvtx_f32_f64(mValue.val[0]), mValue.val[1]); -#else - return Vec3((float)GetX(), (float)GetY(), (float)GetZ()); -#endif -} - -DVec3 DVec3::sMin(DVec3Arg inV1, DVec3Arg inV2) -{ -#if defined(JPH_USE_AVX) - return _mm256_min_pd(inV1.mValue, inV2.mValue); -#elif defined(JPH_USE_SSE) - return DVec3({ _mm_min_pd(inV1.mValue.mLow, inV2.mValue.mLow), _mm_min_pd(inV1.mValue.mHigh, inV2.mValue.mHigh) }); -#elif defined(JPH_USE_NEON) - return DVec3({ vminq_f64(inV1.mValue.val[0], inV2.mValue.val[0]), vminq_f64(inV1.mValue.val[1], inV2.mValue.val[1]) }); -#else - return DVec3(min(inV1.mF64[0], inV2.mF64[0]), - min(inV1.mF64[1], inV2.mF64[1]), - min(inV1.mF64[2], inV2.mF64[2])); -#endif -} - -DVec3 DVec3::sMax(DVec3Arg inV1, DVec3Arg inV2) -{ -#if defined(JPH_USE_AVX) - return _mm256_max_pd(inV1.mValue, inV2.mValue); -#elif defined(JPH_USE_SSE) - return DVec3({ _mm_max_pd(inV1.mValue.mLow, inV2.mValue.mLow), _mm_max_pd(inV1.mValue.mHigh, inV2.mValue.mHigh) }); -#elif defined(JPH_USE_NEON) - return DVec3({ vmaxq_f64(inV1.mValue.val[0], inV2.mValue.val[0]), vmaxq_f64(inV1.mValue.val[1], inV2.mValue.val[1]) }); -#else - return DVec3(max(inV1.mF64[0], inV2.mF64[0]), - max(inV1.mF64[1], inV2.mF64[1]), - max(inV1.mF64[2], inV2.mF64[2])); -#endif -} - -DVec3 DVec3::sClamp(DVec3Arg inV, DVec3Arg inMin, DVec3Arg inMax) -{ - return sMax(sMin(inV, inMax), inMin); -} - -DVec3 DVec3::sEquals(DVec3Arg inV1, DVec3Arg inV2) -{ -#if defined(JPH_USE_AVX) - return _mm256_cmp_pd(inV1.mValue, inV2.mValue, _CMP_EQ_OQ); -#elif defined(JPH_USE_SSE) - return DVec3({ _mm_cmpeq_pd(inV1.mValue.mLow, inV2.mValue.mLow), _mm_cmpeq_pd(inV1.mValue.mHigh, inV2.mValue.mHigh) }); -#elif defined(JPH_USE_NEON) - return DVec3({ vreinterpretq_u64_f64(vceqq_f64(inV1.mValue.val[0], inV2.mValue.val[0])), vreinterpretq_u64_f64(vceqq_f64(inV1.mValue.val[1], inV2.mValue.val[1])) }); -#else - return DVec3(inV1.mF64[0] == inV2.mF64[0]? cTrue : cFalse, - inV1.mF64[1] == inV2.mF64[1]? cTrue : cFalse, - inV1.mF64[2] == inV2.mF64[2]? cTrue : cFalse); -#endif -} - -DVec3 DVec3::sLess(DVec3Arg inV1, DVec3Arg inV2) -{ -#if defined(JPH_USE_AVX) - return _mm256_cmp_pd(inV1.mValue, inV2.mValue, _CMP_LT_OQ); -#elif defined(JPH_USE_SSE) - return DVec3({ _mm_cmplt_pd(inV1.mValue.mLow, inV2.mValue.mLow), _mm_cmplt_pd(inV1.mValue.mHigh, inV2.mValue.mHigh) }); -#elif defined(JPH_USE_NEON) - return DVec3({ vreinterpretq_u64_f64(vcltq_f64(inV1.mValue.val[0], inV2.mValue.val[0])), vreinterpretq_u64_f64(vcltq_f64(inV1.mValue.val[1], inV2.mValue.val[1])) }); -#else - return DVec3(inV1.mF64[0] < inV2.mF64[0]? cTrue : cFalse, - inV1.mF64[1] < inV2.mF64[1]? cTrue : cFalse, - inV1.mF64[2] < inV2.mF64[2]? cTrue : cFalse); -#endif -} - -DVec3 DVec3::sLessOrEqual(DVec3Arg inV1, DVec3Arg inV2) -{ -#if defined(JPH_USE_AVX) - return _mm256_cmp_pd(inV1.mValue, inV2.mValue, _CMP_LE_OQ); -#elif defined(JPH_USE_SSE) - return DVec3({ _mm_cmple_pd(inV1.mValue.mLow, inV2.mValue.mLow), _mm_cmple_pd(inV1.mValue.mHigh, inV2.mValue.mHigh) }); -#elif defined(JPH_USE_NEON) - return DVec3({ vreinterpretq_u64_f64(vcleq_f64(inV1.mValue.val[0], inV2.mValue.val[0])), vreinterpretq_u64_f64(vcleq_f64(inV1.mValue.val[1], inV2.mValue.val[1])) }); -#else - return DVec3(inV1.mF64[0] <= inV2.mF64[0]? cTrue : cFalse, - inV1.mF64[1] <= inV2.mF64[1]? cTrue : cFalse, - inV1.mF64[2] <= inV2.mF64[2]? cTrue : cFalse); -#endif -} - -DVec3 DVec3::sGreater(DVec3Arg inV1, DVec3Arg inV2) -{ -#if defined(JPH_USE_AVX) - return _mm256_cmp_pd(inV1.mValue, inV2.mValue, _CMP_GT_OQ); -#elif defined(JPH_USE_SSE) - return DVec3({ _mm_cmpgt_pd(inV1.mValue.mLow, inV2.mValue.mLow), _mm_cmpgt_pd(inV1.mValue.mHigh, inV2.mValue.mHigh) }); -#elif defined(JPH_USE_NEON) - return DVec3({ vreinterpretq_u64_f64(vcgtq_f64(inV1.mValue.val[0], inV2.mValue.val[0])), vreinterpretq_u64_f64(vcgtq_f64(inV1.mValue.val[1], inV2.mValue.val[1])) }); -#else - return DVec3(inV1.mF64[0] > inV2.mF64[0]? cTrue : cFalse, - inV1.mF64[1] > inV2.mF64[1]? cTrue : cFalse, - inV1.mF64[2] > inV2.mF64[2]? cTrue : cFalse); -#endif -} - -DVec3 DVec3::sGreaterOrEqual(DVec3Arg inV1, DVec3Arg inV2) -{ -#if defined(JPH_USE_AVX) - return _mm256_cmp_pd(inV1.mValue, inV2.mValue, _CMP_GE_OQ); -#elif defined(JPH_USE_SSE) - return DVec3({ _mm_cmpge_pd(inV1.mValue.mLow, inV2.mValue.mLow), _mm_cmpge_pd(inV1.mValue.mHigh, inV2.mValue.mHigh) }); -#elif defined(JPH_USE_NEON) - return DVec3({ vreinterpretq_u64_f64(vcgeq_f64(inV1.mValue.val[0], inV2.mValue.val[0])), vreinterpretq_u64_f64(vcgeq_f64(inV1.mValue.val[1], inV2.mValue.val[1])) }); -#else - return DVec3(inV1.mF64[0] >= inV2.mF64[0]? cTrue : cFalse, - inV1.mF64[1] >= inV2.mF64[1]? cTrue : cFalse, - inV1.mF64[2] >= inV2.mF64[2]? cTrue : cFalse); -#endif -} - -DVec3 DVec3::sFusedMultiplyAdd(DVec3Arg inMul1, DVec3Arg inMul2, DVec3Arg inAdd) -{ -#if defined(JPH_USE_AVX) - #ifdef JPH_USE_FMADD - return _mm256_fmadd_pd(inMul1.mValue, inMul2.mValue, inAdd.mValue); - #else - return _mm256_add_pd(_mm256_mul_pd(inMul1.mValue, inMul2.mValue), inAdd.mValue); - #endif -#elif defined(JPH_USE_NEON) - return DVec3({ vmlaq_f64(inAdd.mValue.val[0], inMul1.mValue.val[0], inMul2.mValue.val[0]), vmlaq_f64(inAdd.mValue.val[1], inMul1.mValue.val[1], inMul2.mValue.val[1]) }); -#else - return inMul1 * inMul2 + inAdd; -#endif -} - -DVec3 DVec3::sSelect(DVec3Arg inV1, DVec3Arg inV2, DVec3Arg inControl) -{ -#if defined(JPH_USE_AVX) - return _mm256_blendv_pd(inV1.mValue, inV2.mValue, inControl.mValue); -#elif defined(JPH_USE_SSE4_1) - Type v = { _mm_blendv_pd(inV1.mValue.mLow, inV2.mValue.mLow, inControl.mValue.mLow), _mm_blendv_pd(inV1.mValue.mHigh, inV2.mValue.mHigh, inControl.mValue.mHigh) }; - return sFixW(v); -#elif defined(JPH_USE_NEON) - Type v = { vbslq_f64(vshrq_n_s64(inControl.mValue.val[0], 63), inV2.mValue.val[0], inV1.mValue.val[0]), vbslq_f64(vshrq_n_s64(inControl.mValue.val[1], 63), inV2.mValue.val[1], inV1.mValue.val[1]) }; - return sFixW(v); -#else - DVec3 result; - for (int i = 0; i < 3; i++) - result.mF64[i] = BitCast(inControl.mF64[i])? inV2.mF64[i] : inV1.mF64[i]; -#ifdef JPH_FLOATING_POINT_EXCEPTIONS_ENABLED - result.mF64[3] = result.mF64[2]; -#endif // JPH_FLOATING_POINT_EXCEPTIONS_ENABLED - return result; -#endif -} - -DVec3 DVec3::sOr(DVec3Arg inV1, DVec3Arg inV2) -{ -#if defined(JPH_USE_AVX) - return _mm256_or_pd(inV1.mValue, inV2.mValue); -#elif defined(JPH_USE_SSE) - return DVec3({ _mm_or_pd(inV1.mValue.mLow, inV2.mValue.mLow), _mm_or_pd(inV1.mValue.mHigh, inV2.mValue.mHigh) }); -#elif defined(JPH_USE_NEON) - return DVec3({ vorrq_s64(inV1.mValue.val[0], inV2.mValue.val[0]), vorrq_s64(inV1.mValue.val[1], inV2.mValue.val[1]) }); -#else - return DVec3(BitCast(BitCast(inV1.mF64[0]) | BitCast(inV2.mF64[0])), - BitCast(BitCast(inV1.mF64[1]) | BitCast(inV2.mF64[1])), - BitCast(BitCast(inV1.mF64[2]) | BitCast(inV2.mF64[2]))); -#endif -} - -DVec3 DVec3::sXor(DVec3Arg inV1, DVec3Arg inV2) -{ -#if defined(JPH_USE_AVX) - return _mm256_xor_pd(inV1.mValue, inV2.mValue); -#elif defined(JPH_USE_SSE) - return DVec3({ _mm_xor_pd(inV1.mValue.mLow, inV2.mValue.mLow), _mm_xor_pd(inV1.mValue.mHigh, inV2.mValue.mHigh) }); -#elif defined(JPH_USE_NEON) - return DVec3({ veorq_s64(inV1.mValue.val[0], inV2.mValue.val[0]), veorq_s64(inV1.mValue.val[1], inV2.mValue.val[1]) }); -#else - return DVec3(BitCast(BitCast(inV1.mF64[0]) ^ BitCast(inV2.mF64[0])), - BitCast(BitCast(inV1.mF64[1]) ^ BitCast(inV2.mF64[1])), - BitCast(BitCast(inV1.mF64[2]) ^ BitCast(inV2.mF64[2]))); -#endif -} - -DVec3 DVec3::sAnd(DVec3Arg inV1, DVec3Arg inV2) -{ -#if defined(JPH_USE_AVX) - return _mm256_and_pd(inV1.mValue, inV2.mValue); -#elif defined(JPH_USE_SSE) - return DVec3({ _mm_and_pd(inV1.mValue.mLow, inV2.mValue.mLow), _mm_and_pd(inV1.mValue.mHigh, inV2.mValue.mHigh) }); -#elif defined(JPH_USE_NEON) - return DVec3({ vandq_s64(inV1.mValue.val[0], inV2.mValue.val[0]), vandq_s64(inV1.mValue.val[1], inV2.mValue.val[1]) }); -#else - return DVec3(BitCast(BitCast(inV1.mF64[0]) & BitCast(inV2.mF64[0])), - BitCast(BitCast(inV1.mF64[1]) & BitCast(inV2.mF64[1])), - BitCast(BitCast(inV1.mF64[2]) & BitCast(inV2.mF64[2]))); -#endif -} - -int DVec3::GetTrues() const -{ -#if defined(JPH_USE_AVX) - return _mm256_movemask_pd(mValue) & 0x7; -#elif defined(JPH_USE_SSE) - return (_mm_movemask_pd(mValue.mLow) + (_mm_movemask_pd(mValue.mHigh) << 2)) & 0x7; -#else - return int((BitCast(mF64[0]) >> 63) | ((BitCast(mF64[1]) >> 63) << 1) | ((BitCast(mF64[2]) >> 63) << 2)); -#endif -} - -bool DVec3::TestAnyTrue() const -{ - return GetTrues() != 0; -} - -bool DVec3::TestAllTrue() const -{ - return GetTrues() == 0x7; -} - -bool DVec3::operator == (DVec3Arg inV2) const -{ - return sEquals(*this, inV2).TestAllTrue(); -} - -bool DVec3::IsClose(DVec3Arg inV2, double inMaxDistSq) const -{ - return (inV2 - *this).LengthSq() <= inMaxDistSq; -} - -bool DVec3::IsNearZero(double inMaxDistSq) const -{ - return LengthSq() <= inMaxDistSq; -} - -DVec3 DVec3::operator * (DVec3Arg inV2) const -{ -#if defined(JPH_USE_AVX) - return _mm256_mul_pd(mValue, inV2.mValue); -#elif defined(JPH_USE_SSE) - return DVec3({ _mm_mul_pd(mValue.mLow, inV2.mValue.mLow), _mm_mul_pd(mValue.mHigh, inV2.mValue.mHigh) }); -#elif defined(JPH_USE_NEON) - return DVec3({ vmulq_f64(mValue.val[0], inV2.mValue.val[0]), vmulq_f64(mValue.val[1], inV2.mValue.val[1]) }); -#else - return DVec3(mF64[0] * inV2.mF64[0], mF64[1] * inV2.mF64[1], mF64[2] * inV2.mF64[2]); -#endif -} - -DVec3 DVec3::operator * (double inV2) const -{ -#if defined(JPH_USE_AVX) - return _mm256_mul_pd(mValue, _mm256_set1_pd(inV2)); -#elif defined(JPH_USE_SSE) - __m128d v = _mm_set1_pd(inV2); - return DVec3({ _mm_mul_pd(mValue.mLow, v), _mm_mul_pd(mValue.mHigh, v) }); -#elif defined(JPH_USE_NEON) - return DVec3({ vmulq_n_f64(mValue.val[0], inV2), vmulq_n_f64(mValue.val[1], inV2) }); -#else - return DVec3(mF64[0] * inV2, mF64[1] * inV2, mF64[2] * inV2); -#endif -} - -DVec3 operator * (double inV1, DVec3Arg inV2) -{ -#if defined(JPH_USE_AVX) - return _mm256_mul_pd(_mm256_set1_pd(inV1), inV2.mValue); -#elif defined(JPH_USE_SSE) - __m128d v = _mm_set1_pd(inV1); - return DVec3({ _mm_mul_pd(v, inV2.mValue.mLow), _mm_mul_pd(v, inV2.mValue.mHigh) }); -#elif defined(JPH_USE_NEON) - return DVec3({ vmulq_n_f64(inV2.mValue.val[0], inV1), vmulq_n_f64(inV2.mValue.val[1], inV1) }); -#else - return DVec3(inV1 * inV2.mF64[0], inV1 * inV2.mF64[1], inV1 * inV2.mF64[2]); -#endif -} - -DVec3 DVec3::operator / (double inV2) const -{ -#if defined(JPH_USE_AVX) - return _mm256_div_pd(mValue, _mm256_set1_pd(inV2)); -#elif defined(JPH_USE_SSE) - __m128d v = _mm_set1_pd(inV2); - return DVec3({ _mm_div_pd(mValue.mLow, v), _mm_div_pd(mValue.mHigh, v) }); -#elif defined(JPH_USE_NEON) - float64x2_t v = vdupq_n_f64(inV2); - return DVec3({ vdivq_f64(mValue.val[0], v), vdivq_f64(mValue.val[1], v) }); -#else - return DVec3(mF64[0] / inV2, mF64[1] / inV2, mF64[2] / inV2); -#endif -} - -DVec3 &DVec3::operator *= (double inV2) -{ -#if defined(JPH_USE_AVX) - mValue = _mm256_mul_pd(mValue, _mm256_set1_pd(inV2)); -#elif defined(JPH_USE_SSE) - __m128d v = _mm_set1_pd(inV2); - mValue.mLow = _mm_mul_pd(mValue.mLow, v); - mValue.mHigh = _mm_mul_pd(mValue.mHigh, v); -#elif defined(JPH_USE_NEON) - mValue.val[0] = vmulq_n_f64(mValue.val[0], inV2); - mValue.val[1] = vmulq_n_f64(mValue.val[1], inV2); -#else - for (int i = 0; i < 3; ++i) - mF64[i] *= inV2; - #ifdef JPH_FLOATING_POINT_EXCEPTIONS_ENABLED - mF64[3] = mF64[2]; - #endif -#endif - return *this; -} - -DVec3 &DVec3::operator *= (DVec3Arg inV2) -{ -#if defined(JPH_USE_AVX) - mValue = _mm256_mul_pd(mValue, inV2.mValue); -#elif defined(JPH_USE_SSE) - mValue.mLow = _mm_mul_pd(mValue.mLow, inV2.mValue.mLow); - mValue.mHigh = _mm_mul_pd(mValue.mHigh, inV2.mValue.mHigh); -#elif defined(JPH_USE_NEON) - mValue.val[0] = vmulq_f64(mValue.val[0], inV2.mValue.val[0]); - mValue.val[1] = vmulq_f64(mValue.val[1], inV2.mValue.val[1]); -#else - for (int i = 0; i < 3; ++i) - mF64[i] *= inV2.mF64[i]; - #ifdef JPH_FLOATING_POINT_EXCEPTIONS_ENABLED - mF64[3] = mF64[2]; - #endif -#endif - return *this; -} - -DVec3 &DVec3::operator /= (double inV2) -{ -#if defined(JPH_USE_AVX) - mValue = _mm256_div_pd(mValue, _mm256_set1_pd(inV2)); -#elif defined(JPH_USE_SSE) - __m128d v = _mm_set1_pd(inV2); - mValue.mLow = _mm_div_pd(mValue.mLow, v); - mValue.mHigh = _mm_div_pd(mValue.mHigh, v); -#elif defined(JPH_USE_NEON) - float64x2_t v = vdupq_n_f64(inV2); - mValue.val[0] = vdivq_f64(mValue.val[0], v); - mValue.val[1] = vdivq_f64(mValue.val[1], v); -#else - for (int i = 0; i < 3; ++i) - mF64[i] /= inV2; - #ifdef JPH_FLOATING_POINT_EXCEPTIONS_ENABLED - mF64[3] = mF64[2]; - #endif -#endif - return *this; -} - -DVec3 DVec3::operator + (Vec3Arg inV2) const -{ -#if defined(JPH_USE_AVX) - return _mm256_add_pd(mValue, _mm256_cvtps_pd(inV2.mValue)); -#elif defined(JPH_USE_SSE) - return DVec3({ _mm_add_pd(mValue.mLow, _mm_cvtps_pd(inV2.mValue)), _mm_add_pd(mValue.mHigh, _mm_cvtps_pd(_mm_shuffle_ps(inV2.mValue, inV2.mValue, _MM_SHUFFLE(2, 2, 2, 2)))) }); -#elif defined(JPH_USE_NEON) - return DVec3({ vaddq_f64(mValue.val[0], vcvt_f64_f32(vget_low_f32(inV2.mValue))), vaddq_f64(mValue.val[1], vcvt_high_f64_f32(inV2.mValue)) }); -#else - return DVec3(mF64[0] + inV2.mF32[0], mF64[1] + inV2.mF32[1], mF64[2] + inV2.mF32[2]); -#endif -} - -DVec3 DVec3::operator + (DVec3Arg inV2) const -{ -#if defined(JPH_USE_AVX) - return _mm256_add_pd(mValue, inV2.mValue); -#elif defined(JPH_USE_SSE) - return DVec3({ _mm_add_pd(mValue.mLow, inV2.mValue.mLow), _mm_add_pd(mValue.mHigh, inV2.mValue.mHigh) }); -#elif defined(JPH_USE_NEON) - return DVec3({ vaddq_f64(mValue.val[0], inV2.mValue.val[0]), vaddq_f64(mValue.val[1], inV2.mValue.val[1]) }); -#else - return DVec3(mF64[0] + inV2.mF64[0], mF64[1] + inV2.mF64[1], mF64[2] + inV2.mF64[2]); -#endif -} - -DVec3 &DVec3::operator += (Vec3Arg inV2) -{ -#if defined(JPH_USE_AVX) - mValue = _mm256_add_pd(mValue, _mm256_cvtps_pd(inV2.mValue)); -#elif defined(JPH_USE_SSE) - mValue.mLow = _mm_add_pd(mValue.mLow, _mm_cvtps_pd(inV2.mValue)); - mValue.mHigh = _mm_add_pd(mValue.mHigh, _mm_cvtps_pd(_mm_shuffle_ps(inV2.mValue, inV2.mValue, _MM_SHUFFLE(2, 2, 2, 2)))); -#elif defined(JPH_USE_NEON) - mValue.val[0] = vaddq_f64(mValue.val[0], vcvt_f64_f32(vget_low_f32(inV2.mValue))); - mValue.val[1] = vaddq_f64(mValue.val[1], vcvt_high_f64_f32(inV2.mValue)); -#else - for (int i = 0; i < 3; ++i) - mF64[i] += inV2.mF32[i]; - #ifdef JPH_FLOATING_POINT_EXCEPTIONS_ENABLED - mF64[3] = mF64[2]; - #endif -#endif - return *this; -} - -DVec3 &DVec3::operator += (DVec3Arg inV2) -{ -#if defined(JPH_USE_AVX) - mValue = _mm256_add_pd(mValue, inV2.mValue); -#elif defined(JPH_USE_SSE) - mValue.mLow = _mm_add_pd(mValue.mLow, inV2.mValue.mLow); - mValue.mHigh = _mm_add_pd(mValue.mHigh, inV2.mValue.mHigh); -#elif defined(JPH_USE_NEON) - mValue.val[0] = vaddq_f64(mValue.val[0], inV2.mValue.val[0]); - mValue.val[1] = vaddq_f64(mValue.val[1], inV2.mValue.val[1]); -#else - for (int i = 0; i < 3; ++i) - mF64[i] += inV2.mF64[i]; - #ifdef JPH_FLOATING_POINT_EXCEPTIONS_ENABLED - mF64[3] = mF64[2]; - #endif -#endif - return *this; -} - -DVec3 DVec3::operator - () const -{ -#if defined(JPH_USE_AVX) - return _mm256_sub_pd(_mm256_setzero_pd(), mValue); -#elif defined(JPH_USE_SSE) - __m128d zero = _mm_setzero_pd(); - return DVec3({ _mm_sub_pd(zero, mValue.mLow), _mm_sub_pd(zero, mValue.mHigh) }); -#elif defined(JPH_USE_NEON) - return DVec3({ vnegq_f64(mValue.val[0]), vnegq_f64(mValue.val[1]) }); -#else - return DVec3(-mF64[0], -mF64[1], -mF64[2]); -#endif -} - -DVec3 DVec3::operator - (Vec3Arg inV2) const -{ -#if defined(JPH_USE_AVX) - return _mm256_sub_pd(mValue, _mm256_cvtps_pd(inV2.mValue)); -#elif defined(JPH_USE_SSE) - return DVec3({ _mm_sub_pd(mValue.mLow, _mm_cvtps_pd(inV2.mValue)), _mm_sub_pd(mValue.mHigh, _mm_cvtps_pd(_mm_shuffle_ps(inV2.mValue, inV2.mValue, _MM_SHUFFLE(2, 2, 2, 2)))) }); -#elif defined(JPH_USE_NEON) - return DVec3({ vsubq_f64(mValue.val[0], vcvt_f64_f32(vget_low_f32(inV2.mValue))), vsubq_f64(mValue.val[1], vcvt_high_f64_f32(inV2.mValue)) }); -#else - return DVec3(mF64[0] - inV2.mF32[0], mF64[1] - inV2.mF32[1], mF64[2] - inV2.mF32[2]); -#endif -} - -DVec3 DVec3::operator - (DVec3Arg inV2) const -{ -#if defined(JPH_USE_AVX) - return _mm256_sub_pd(mValue, inV2.mValue); -#elif defined(JPH_USE_SSE) - return DVec3({ _mm_sub_pd(mValue.mLow, inV2.mValue.mLow), _mm_sub_pd(mValue.mHigh, inV2.mValue.mHigh) }); -#elif defined(JPH_USE_NEON) - return DVec3({ vsubq_f64(mValue.val[0], inV2.mValue.val[0]), vsubq_f64(mValue.val[1], inV2.mValue.val[1]) }); -#else - return DVec3(mF64[0] - inV2.mF64[0], mF64[1] - inV2.mF64[1], mF64[2] - inV2.mF64[2]); -#endif -} - -DVec3 &DVec3::operator -= (Vec3Arg inV2) -{ -#if defined(JPH_USE_AVX) - mValue = _mm256_sub_pd(mValue, _mm256_cvtps_pd(inV2.mValue)); -#elif defined(JPH_USE_SSE) - mValue.mLow = _mm_sub_pd(mValue.mLow, _mm_cvtps_pd(inV2.mValue)); - mValue.mHigh = _mm_sub_pd(mValue.mHigh, _mm_cvtps_pd(_mm_shuffle_ps(inV2.mValue, inV2.mValue, _MM_SHUFFLE(2, 2, 2, 2)))); -#elif defined(JPH_USE_NEON) - mValue.val[0] = vsubq_f64(mValue.val[0], vcvt_f64_f32(vget_low_f32(inV2.mValue))); - mValue.val[1] = vsubq_f64(mValue.val[1], vcvt_high_f64_f32(inV2.mValue)); -#else - for (int i = 0; i < 3; ++i) - mF64[i] -= inV2.mF32[i]; - #ifdef JPH_FLOATING_POINT_EXCEPTIONS_ENABLED - mF64[3] = mF64[2]; - #endif -#endif - return *this; -} - -DVec3 &DVec3::operator -= (DVec3Arg inV2) -{ -#if defined(JPH_USE_AVX) - mValue = _mm256_sub_pd(mValue, inV2.mValue); -#elif defined(JPH_USE_SSE) - mValue.mLow = _mm_sub_pd(mValue.mLow, inV2.mValue.mLow); - mValue.mHigh = _mm_sub_pd(mValue.mHigh, inV2.mValue.mHigh); -#elif defined(JPH_USE_NEON) - mValue.val[0] = vsubq_f64(mValue.val[0], inV2.mValue.val[0]); - mValue.val[1] = vsubq_f64(mValue.val[1], inV2.mValue.val[1]); -#else - for (int i = 0; i < 3; ++i) - mF64[i] -= inV2.mF64[i]; - #ifdef JPH_FLOATING_POINT_EXCEPTIONS_ENABLED - mF64[3] = mF64[2]; - #endif -#endif - return *this; -} - -DVec3 DVec3::operator / (DVec3Arg inV2) const -{ - inV2.CheckW(); -#if defined(JPH_USE_AVX) - return _mm256_div_pd(mValue, inV2.mValue); -#elif defined(JPH_USE_SSE) - return DVec3({ _mm_div_pd(mValue.mLow, inV2.mValue.mLow), _mm_div_pd(mValue.mHigh, inV2.mValue.mHigh) }); -#elif defined(JPH_USE_NEON) - return DVec3({ vdivq_f64(mValue.val[0], inV2.mValue.val[0]), vdivq_f64(mValue.val[1], inV2.mValue.val[1]) }); -#else - return DVec3(mF64[0] / inV2.mF64[0], mF64[1] / inV2.mF64[1], mF64[2] / inV2.mF64[2]); -#endif -} - -DVec3 DVec3::Abs() const -{ -#if defined(JPH_USE_AVX512) - return _mm256_range_pd(mValue, mValue, 0b1000); -#elif defined(JPH_USE_AVX) - return _mm256_max_pd(_mm256_sub_pd(_mm256_setzero_pd(), mValue), mValue); -#elif defined(JPH_USE_SSE) - __m128d zero = _mm_setzero_pd(); - return DVec3({ _mm_max_pd(_mm_sub_pd(zero, mValue.mLow), mValue.mLow), _mm_max_pd(_mm_sub_pd(zero, mValue.mHigh), mValue.mHigh) }); -#elif defined(JPH_USE_NEON) - return DVec3({ vabsq_f64(mValue.val[0]), vabsq_f64(mValue.val[1]) }); -#else - return DVec3(abs(mF64[0]), abs(mF64[1]), abs(mF64[2])); -#endif -} - -DVec3 DVec3::Reciprocal() const -{ - return sReplicate(1.0) / mValue; -} - -DVec3 DVec3::Cross(DVec3Arg inV2) const -{ -#if defined(JPH_USE_AVX2) - __m256d t1 = _mm256_permute4x64_pd(inV2.mValue, _MM_SHUFFLE(0, 0, 2, 1)); // Assure Z and W are the same - t1 = _mm256_mul_pd(t1, mValue); - __m256d t2 = _mm256_permute4x64_pd(mValue, _MM_SHUFFLE(0, 0, 2, 1)); // Assure Z and W are the same - t2 = _mm256_mul_pd(t2, inV2.mValue); - __m256d t3 = _mm256_sub_pd(t1, t2); - return _mm256_permute4x64_pd(t3, _MM_SHUFFLE(0, 0, 2, 1)); // Assure Z and W are the same -#else - return DVec3(mF64[1] * inV2.mF64[2] - mF64[2] * inV2.mF64[1], - mF64[2] * inV2.mF64[0] - mF64[0] * inV2.mF64[2], - mF64[0] * inV2.mF64[1] - mF64[1] * inV2.mF64[0]); -#endif -} - -double DVec3::Dot(DVec3Arg inV2) const -{ -#if defined(JPH_USE_AVX) - __m256d mul = _mm256_mul_pd(mValue, inV2.mValue); - __m128d xy = _mm256_castpd256_pd128(mul); - __m128d yx = _mm_shuffle_pd(xy, xy, 1); - __m128d sum = _mm_add_pd(xy, yx); - __m128d zw = _mm256_extractf128_pd(mul, 1); - sum = _mm_add_pd(sum, zw); - return _mm_cvtsd_f64(sum); -#elif defined(JPH_USE_SSE) - __m128d xy = _mm_mul_pd(mValue.mLow, inV2.mValue.mLow); - __m128d yx = _mm_shuffle_pd(xy, xy, 1); - __m128d sum = _mm_add_pd(xy, yx); - __m128d z = _mm_mul_sd(mValue.mHigh, inV2.mValue.mHigh); - sum = _mm_add_pd(sum, z); - return _mm_cvtsd_f64(sum); -#elif defined(JPH_USE_NEON) - float64x2_t mul_low = vmulq_f64(mValue.val[0], inV2.mValue.val[0]); - float64x2_t mul_high = vmulq_f64(mValue.val[1], inV2.mValue.val[1]); - return vaddvq_f64(mul_low) + vgetq_lane_f64(mul_high, 0); -#else - double dot = 0.0; - for (int i = 0; i < 3; i++) - dot += mF64[i] * inV2.mF64[i]; - return dot; -#endif -} - -double DVec3::LengthSq() const -{ - return Dot(*this); -} - -DVec3 DVec3::Sqrt() const -{ -#if defined(JPH_USE_AVX) - return _mm256_sqrt_pd(mValue); -#elif defined(JPH_USE_SSE) - return DVec3({ _mm_sqrt_pd(mValue.mLow), _mm_sqrt_pd(mValue.mHigh) }); -#elif defined(JPH_USE_NEON) - return DVec3({ vsqrtq_f64(mValue.val[0]), vsqrtq_f64(mValue.val[1]) }); -#else - return DVec3(sqrt(mF64[0]), sqrt(mF64[1]), sqrt(mF64[2])); -#endif -} - -double DVec3::Length() const -{ - return sqrt(Dot(*this)); -} - -DVec3 DVec3::Normalized() const -{ - return *this / Length(); -} - -bool DVec3::IsNormalized(double inTolerance) const -{ - return abs(LengthSq() - 1.0) <= inTolerance; -} - -bool DVec3::IsNaN() const -{ -#if defined(JPH_USE_AVX512) - return (_mm256_fpclass_pd_mask(mValue, 0b10000001) & 0x7) != 0; -#elif defined(JPH_USE_AVX) - return (_mm256_movemask_pd(_mm256_cmp_pd(mValue, mValue, _CMP_UNORD_Q)) & 0x7) != 0; -#elif defined(JPH_USE_SSE) - return ((_mm_movemask_pd(_mm_cmpunord_pd(mValue.mLow, mValue.mLow)) + (_mm_movemask_pd(_mm_cmpunord_pd(mValue.mHigh, mValue.mHigh)) << 2)) & 0x7) != 0; -#else - return isnan(mF64[0]) || isnan(mF64[1]) || isnan(mF64[2]); -#endif -} - -DVec3 DVec3::GetSign() const -{ -#if defined(JPH_USE_AVX512) - return _mm256_fixupimm_pd(mValue, mValue, _mm256_set1_epi32(0xA9A90A00), 0); -#elif defined(JPH_USE_AVX) - __m256d minus_one = _mm256_set1_pd(-1.0); - __m256d one = _mm256_set1_pd(1.0); - return _mm256_or_pd(_mm256_and_pd(mValue, minus_one), one); -#elif defined(JPH_USE_SSE) - __m128d minus_one = _mm_set1_pd(-1.0); - __m128d one = _mm_set1_pd(1.0); - return DVec3({ _mm_or_pd(_mm_and_pd(mValue.mLow, minus_one), one), _mm_or_pd(_mm_and_pd(mValue.mHigh, minus_one), one) }); -#elif defined(JPH_USE_NEON) - float64x2_t minus_one = vdupq_n_f64(-1.0f); - float64x2_t one = vdupq_n_f64(1.0f); - return DVec3({ vorrq_s64(vandq_s64(mValue.val[0], minus_one), one), vorrq_s64(vandq_s64(mValue.val[1], minus_one), one) }); -#else - return DVec3(std::signbit(mF64[0])? -1.0 : 1.0, - std::signbit(mF64[1])? -1.0 : 1.0, - std::signbit(mF64[2])? -1.0 : 1.0); -#endif -} - -DVec3 DVec3::PrepareRoundToZero() const -{ - // Float has 23 bit mantissa, double 52 bit mantissa => we lose 29 bits when converting from double to float - constexpr uint64 cDoubleToFloatMantissaLoss = (1U << 29) - 1; - -#if defined(JPH_USE_AVX) - return _mm256_and_pd(mValue, _mm256_castsi256_pd(_mm256_set1_epi64x(int64_t(~cDoubleToFloatMantissaLoss)))); -#elif defined(JPH_USE_SSE) - __m128d mask = _mm_castsi128_pd(_mm_set1_epi64x(int64_t(~cDoubleToFloatMantissaLoss))); - return DVec3({ _mm_and_pd(mValue.mLow, mask), _mm_and_pd(mValue.mHigh, mask) }); -#elif defined(JPH_USE_NEON) - float64x2_t mask = vreinterpretq_f64_u64(vdupq_n_u64(~cDoubleToFloatMantissaLoss)); - return DVec3({ vandq_s64(mValue.val[0], mask), vandq_s64(mValue.val[1], mask) }); -#else - double x = BitCast(BitCast(mF64[0]) & ~cDoubleToFloatMantissaLoss); - double y = BitCast(BitCast(mF64[1]) & ~cDoubleToFloatMantissaLoss); - double z = BitCast(BitCast(mF64[2]) & ~cDoubleToFloatMantissaLoss); - - return DVec3(x, y, z); -#endif -} - -DVec3 DVec3::PrepareRoundToInf() const -{ - // Float has 23 bit mantissa, double 52 bit mantissa => we lose 29 bits when converting from double to float - constexpr uint64 cDoubleToFloatMantissaLoss = (1U << 29) - 1; - -#if defined(JPH_USE_AVX512) - __m256i mantissa_loss = _mm256_set1_epi64x(cDoubleToFloatMantissaLoss); - __mmask8 is_zero = _mm256_testn_epi64_mask(_mm256_castpd_si256(mValue), mantissa_loss); - __m256d value_or_mantissa_loss = _mm256_or_pd(mValue, _mm256_castsi256_pd(mantissa_loss)); - return _mm256_mask_blend_pd(is_zero, value_or_mantissa_loss, mValue); -#elif defined(JPH_USE_AVX) - __m256i mantissa_loss = _mm256_set1_epi64x(cDoubleToFloatMantissaLoss); - __m256d value_and_mantissa_loss = _mm256_and_pd(mValue, _mm256_castsi256_pd(mantissa_loss)); - __m256d is_zero = _mm256_cmp_pd(value_and_mantissa_loss, _mm256_setzero_pd(), _CMP_EQ_OQ); - __m256d value_or_mantissa_loss = _mm256_or_pd(mValue, _mm256_castsi256_pd(mantissa_loss)); - return _mm256_blendv_pd(value_or_mantissa_loss, mValue, is_zero); -#elif defined(JPH_USE_SSE4_1) - __m128i mantissa_loss = _mm_set1_epi64x(cDoubleToFloatMantissaLoss); - __m128d zero = _mm_setzero_pd(); - __m128d value_and_mantissa_loss_low = _mm_and_pd(mValue.mLow, _mm_castsi128_pd(mantissa_loss)); - __m128d is_zero_low = _mm_cmpeq_pd(value_and_mantissa_loss_low, zero); - __m128d value_or_mantissa_loss_low = _mm_or_pd(mValue.mLow, _mm_castsi128_pd(mantissa_loss)); - __m128d value_and_mantissa_loss_high = _mm_and_pd(mValue.mHigh, _mm_castsi128_pd(mantissa_loss)); - __m128d is_zero_high = _mm_cmpeq_pd(value_and_mantissa_loss_high, zero); - __m128d value_or_mantissa_loss_high = _mm_or_pd(mValue.mHigh, _mm_castsi128_pd(mantissa_loss)); - return DVec3({ _mm_blendv_pd(value_or_mantissa_loss_low, mValue.mLow, is_zero_low), _mm_blendv_pd(value_or_mantissa_loss_high, mValue.mHigh, is_zero_high) }); -#elif defined(JPH_USE_NEON) - float64x2_t mantissa_loss = vreinterpretq_f64_u64(vdupq_n_u64(cDoubleToFloatMantissaLoss)); - float64x2_t zero = vdupq_n_f64(0.0); - float64x2_t value_and_mantissa_loss_low = vandq_s64(mValue.val[0], mantissa_loss); - float64x2_t is_zero_low = vceqq_f64(value_and_mantissa_loss_low, zero); - float64x2_t value_or_mantissa_loss_low = vorrq_s64(mValue.val[0], mantissa_loss); - float64x2_t value_and_mantissa_loss_high = vandq_s64(mValue.val[1], mantissa_loss); - float64x2_t value_low = vbslq_f64(is_zero_low, mValue.val[0], value_or_mantissa_loss_low); - float64x2_t is_zero_high = vceqq_f64(value_and_mantissa_loss_high, zero); - float64x2_t value_or_mantissa_loss_high = vorrq_s64(mValue.val[1], mantissa_loss); - float64x2_t value_high = vbslq_f64(is_zero_high, mValue.val[1], value_or_mantissa_loss_high); - return DVec3({ value_low, value_high }); -#else - uint64 ux = BitCast(mF64[0]); - uint64 uy = BitCast(mF64[1]); - uint64 uz = BitCast(mF64[2]); - - double x = BitCast((ux & cDoubleToFloatMantissaLoss) == 0? ux : (ux | cDoubleToFloatMantissaLoss)); - double y = BitCast((uy & cDoubleToFloatMantissaLoss) == 0? uy : (uy | cDoubleToFloatMantissaLoss)); - double z = BitCast((uz & cDoubleToFloatMantissaLoss) == 0? uz : (uz | cDoubleToFloatMantissaLoss)); - - return DVec3(x, y, z); -#endif -} - -Vec3 DVec3::ToVec3RoundDown() const -{ - DVec3 to_zero = PrepareRoundToZero(); - DVec3 to_inf = PrepareRoundToInf(); - return Vec3(DVec3::sSelect(to_zero, to_inf, DVec3::sLess(*this, DVec3::sZero()))); -} - -Vec3 DVec3::ToVec3RoundUp() const -{ - DVec3 to_zero = PrepareRoundToZero(); - DVec3 to_inf = PrepareRoundToInf(); - return Vec3(DVec3::sSelect(to_inf, to_zero, DVec3::sLess(*this, DVec3::sZero()))); -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Double3.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Double3.h deleted file mode 100644 index 90d3c1642ca..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Double3.h +++ /dev/null @@ -1,48 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -/// Class that holds 3 doubles. Used as a storage class. Convert to DVec3 for calculations. -class [[nodiscard]] Double3 -{ -public: - JPH_OVERRIDE_NEW_DELETE - - Double3() = default; ///< Intentionally not initialized for performance reasons - Double3(const Double3 &inRHS) = default; - Double3 & operator = (const Double3 &inRHS) = default; - Double3(double inX, double inY, double inZ) : x(inX), y(inY), z(inZ) { } - - double operator [] (int inCoordinate) const - { - JPH_ASSERT(inCoordinate < 3); - return *(&x + inCoordinate); - } - - bool operator == (const Double3 &inRHS) const - { - return x == inRHS.x && y == inRHS.y && z == inRHS.z; - } - - bool operator != (const Double3 &inRHS) const - { - return x != inRHS.x || y != inRHS.y || z != inRHS.z; - } - - double x; - double y; - double z; -}; - -static_assert(is_trivial(), "Is supposed to be a trivial type!"); - -JPH_NAMESPACE_END - -// Create a std::hash for Double3 -JPH_MAKE_HASHABLE(JPH::Double3, t.x, t.y, t.z) diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/DynMatrix.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/DynMatrix.h deleted file mode 100644 index 76db294c782..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/DynMatrix.h +++ /dev/null @@ -1,31 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2022 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -JPH_NAMESPACE_BEGIN - -/// Dynamic resizable matrix class -class [[nodiscard]] DynMatrix -{ -public: - /// Constructor - DynMatrix(const DynMatrix &) = default; - DynMatrix(uint inRows, uint inCols) : mRows(inRows), mCols(inCols) { mElements.resize(inRows * inCols); } - - /// Access an element - float operator () (uint inRow, uint inCol) const { JPH_ASSERT(inRow < mRows && inCol < mCols); return mElements[inRow * mCols + inCol]; } - float & operator () (uint inRow, uint inCol) { JPH_ASSERT(inRow < mRows && inCol < mCols); return mElements[inRow * mCols + inCol]; } - - /// Get dimensions - uint GetCols() const { return mCols; } - uint GetRows() const { return mRows; } - -private: - uint mRows; - uint mCols; - Array mElements; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/EigenValueSymmetric.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/EigenValueSymmetric.h deleted file mode 100644 index 43436ee962d..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/EigenValueSymmetric.h +++ /dev/null @@ -1,175 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -/// Function to determine the eigen vectors and values of a N x N real symmetric matrix -/// by Jacobi transformations. This method is most suitable for N < 10. -/// -/// Taken and adapted from Numerical Recipies paragraph 11.1 -/// -/// An eigen vector is a vector v for which \f$A \: v = \lambda \: v\f$ -/// -/// Where: -/// A: A square matrix. -/// \f$\lambda\f$: a non-zero constant value. -/// -/// @see https://en.wikipedia.org/wiki/Eigenvalues_and_eigenvectors -/// -/// Matrix is a matrix type, which has dimensions N x N. -/// @param inMatrix is the matrix of which to return the eigenvalues and vectors -/// @param outEigVec will contain a matrix whose columns contain the normalized eigenvectors (must be identity before call) -/// @param outEigVal will contain the eigenvalues -template -bool EigenValueSymmetric(const Matrix &inMatrix, Matrix &outEigVec, Vector &outEigVal) -{ - // This algorithm works with very small numbers and can trigger invalid float exceptions when not flushing denormals - FPFlushDenormals flush_denormals; - (void)flush_denormals; - - // Maximum number of sweeps to make - const int cMaxSweeps = 50; - - // Get problem dimension - const uint n = inMatrix.GetRows(); - - // Make sure the dimensions are right - JPH_ASSERT(inMatrix.GetRows() == n); - JPH_ASSERT(inMatrix.GetCols() == n); - JPH_ASSERT(outEigVec.GetRows() == n); - JPH_ASSERT(outEigVec.GetCols() == n); - JPH_ASSERT(outEigVal.GetRows() == n); - JPH_ASSERT(outEigVec.IsIdentity()); - - // Get the matrix in a so we can mess with it - Matrix a = inMatrix; - - Vector b, z; - - for (uint ip = 0; ip < n; ++ip) - { - // Initialize b to diagonal of a - b[ip] = a(ip, ip); - - // Initialize output to diagonal of a - outEigVal[ip] = a(ip, ip); - - // Reset z - z[ip] = 0.0f; - } - - for (int sweep = 0; sweep < cMaxSweeps; ++sweep) - { - // Get the sum of the off-diagonal elements of a - float sm = 0.0f; - for (uint ip = 0; ip < n - 1; ++ip) - for (uint iq = ip + 1; iq < n; ++iq) - sm += abs(a(ip, iq)); - - // Normal return, convergence to machine underflow - if (sm == 0.0f) - { - // Sanity checks - #ifdef JPH_ENABLE_ASSERTS - for (uint c = 0; c < n; ++c) - { - // Check if the eigenvector is normalized - JPH_ASSERT(outEigVec.GetColumn(c).IsNormalized()); - - // Check if inMatrix * eigen_vector = eigen_value * eigen_vector - Vector mat_eigvec = inMatrix * outEigVec.GetColumn(c); - Vector eigval_eigvec = outEigVal[c] * outEigVec.GetColumn(c); - JPH_ASSERT(mat_eigvec.IsClose(eigval_eigvec, max(mat_eigvec.LengthSq(), eigval_eigvec.LengthSq()) * 1.0e-6f)); - } - #endif - - // Success - return true; - } - - // On the first three sweeps use a fraction of the sum of the off diagonal elements as threshold - float tresh = sweep < 4? 0.2f * sm / Square(n) : 0.0f; - - for (uint ip = 0; ip < n - 1; ++ip) - for (uint iq = ip + 1; iq < n; ++iq) - { - float g = 100.0f * abs(a(ip, iq)); - - // After four sweeps, skip the rotation if the off-diagonal element is small - if (sweep > 4 - && abs(outEigVal[ip]) + g == abs(outEigVal[ip]) - && abs(outEigVal[iq]) + g == abs(outEigVal[iq])) - { - a(ip, iq) = 0.0f; - } - else if (abs(a(ip, iq)) > tresh) - { - float h = outEigVal[iq] - outEigVal[ip]; - - float t; - if (abs(h) + g == abs(h)) - { - t = a(ip, iq) / h; - } - else - { - float theta = 0.5f * h / a(ip, iq); // Warning: Can become inf if a(ip, iq) too small - t = 1.0f / (abs(theta) + sqrt(1.0f + theta * theta)); // Warning: Squaring large value can make it inf - if (theta < 0.0f) t = -t; - } - - float c = 1.0f / sqrt(1.0f + t * t); - float s = t * c; - float tau = s / (1.0f + c); - h = t * a(ip, iq); - - a(ip, iq) = 0.0f; - - // !Modification from Numerical Recipes! - // h can become infinite due to numerical overflow, this only happens when a(ip, iq) is very small - // so we can safely set a(ip, iq) to zero and skip the rotation, see lines marked with 'Warning' above. - if (!isnan(h)) - { - z[ip] -= h; - z[iq] += h; - - outEigVal[ip] -= h; - outEigVal[iq] += h; - - #define JPH_EVS_ROTATE(a, i, j, k, l) \ - g = a(i, j), \ - h = a(k, l), \ - a(i, j) = g - s * (h + g * tau), \ - a(k, l) = h + s * (g - h * tau) - - uint j; - for (j = 0; j < ip; ++j) JPH_EVS_ROTATE(a, j, ip, j, iq); - for (j = ip + 1; j < iq; ++j) JPH_EVS_ROTATE(a, ip, j, j, iq); - for (j = iq + 1; j < n; ++j) JPH_EVS_ROTATE(a, ip, j, iq, j); - for (j = 0; j < n; ++j) JPH_EVS_ROTATE(outEigVec, j, ip, j, iq); - - #undef JPH_EVS_ROTATE - } - } - } - - // Update eigenvalues with the sum of ta_pq and reinitialize z - for (uint ip = 0; ip < n; ++ip) - { - b[ip] += z[ip]; - outEigVal[ip] = b[ip]; - z[ip] = 0.0f; - } - } - - // Failure - JPH_ASSERT(false, "Too many iterations"); - return false; -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/FindRoot.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/FindRoot.h deleted file mode 100644 index 21fef9f61f0..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/FindRoot.h +++ /dev/null @@ -1,42 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -JPH_NAMESPACE_BEGIN - -/// Find the roots of \f$inA \: x^2 + inB \: x + inC = 0\f$. -/// @return The number of roots, actual roots in outX1 and outX2. -/// If number of roots returned is 1 then outX1 == outX2. -template -inline int FindRoot(const T inA, const T inB, const T inC, T &outX1, T &outX2) -{ - // Check if this is a linear equation - if (inA == T(0)) - { - // Check if this is a constant equation - if (inB == T(0)) - return 0; - - // Linear equation with 1 solution - outX1 = outX2 = -inC / inB; - return 1; - } - - // See Numerical Recipes in C, Chapter 5.6 Quadratic and Cubic Equations - T det = Square(inB) - T(4) * inA * inC; - if (det < T(0)) - return 0; - T q = (inB + Sign(inB) * sqrt(det)) / T(-2); - outX1 = q / inA; - if (q == T(0)) - { - outX2 = outX1; - return 1; - } - outX2 = inC / q; - return 2; -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Float2.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Float2.h deleted file mode 100644 index 9c05d4139cc..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Float2.h +++ /dev/null @@ -1,36 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -JPH_NAMESPACE_BEGIN - -/// Class that holds 2 floats, used as a storage class mainly. -class [[nodiscard]] Float2 -{ -public: - JPH_OVERRIDE_NEW_DELETE - - Float2() = default; ///< Intentionally not initialized for performance reasons - Float2(const Float2 &inRHS) = default; - Float2 & operator = (const Float2 &inRHS) = default; - Float2(float inX, float inY) : x(inX), y(inY) { } - - bool operator == (const Float2 &inRHS) const { return x == inRHS.x && y == inRHS.y; } - bool operator != (const Float2 &inRHS) const { return x != inRHS.x || y != inRHS.y; } - - /// To String - friend ostream & operator << (ostream &inStream, const Float2 &inV) - { - inStream << inV.x << ", " << inV.y; - return inStream; - } - - float x; - float y; -}; - -static_assert(is_trivial(), "Is supposed to be a trivial type!"); - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Float3.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Float3.h deleted file mode 100644 index e288201b971..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Float3.h +++ /dev/null @@ -1,50 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -/// Class that holds 3 floats. Used as a storage class. Convert to Vec3 for calculations. -class [[nodiscard]] Float3 -{ -public: - JPH_OVERRIDE_NEW_DELETE - - Float3() = default; ///< Intentionally not initialized for performance reasons - Float3(const Float3 &inRHS) = default; - Float3 & operator = (const Float3 &inRHS) = default; - constexpr Float3(float inX, float inY, float inZ) : x(inX), y(inY), z(inZ) { } - - float operator [] (int inCoordinate) const - { - JPH_ASSERT(inCoordinate < 3); - return *(&x + inCoordinate); - } - - bool operator == (const Float3 &inRHS) const - { - return x == inRHS.x && y == inRHS.y && z == inRHS.z; - } - - bool operator != (const Float3 &inRHS) const - { - return x != inRHS.x || y != inRHS.y || z != inRHS.z; - } - - float x; - float y; - float z; -}; - -using VertexList = Array; - -static_assert(is_trivial(), "Is supposed to be a trivial type!"); - -JPH_NAMESPACE_END - -// Create a std::hash for Float3 -JPH_MAKE_HASHABLE(JPH::Float3, t.x, t.y, t.z) diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Float4.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Float4.h deleted file mode 100644 index 30845f638a5..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Float4.h +++ /dev/null @@ -1,33 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -JPH_NAMESPACE_BEGIN - -/// Class that holds 4 float values. Convert to Vec4 to perform calculations. -class [[nodiscard]] Float4 -{ -public: - JPH_OVERRIDE_NEW_DELETE - - Float4() = default; ///< Intentionally not initialized for performance reasons - Float4(const Float4 &inRHS) = default; - Float4(float inX, float inY, float inZ, float inW) : x(inX), y(inY), z(inZ), w(inW) { } - - float operator [] (int inCoordinate) const - { - JPH_ASSERT(inCoordinate < 4); - return *(&x + inCoordinate); - } - - float x; - float y; - float z; - float w; -}; - -static_assert(is_trivial(), "Is supposed to be a trivial type!"); - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/GaussianElimination.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/GaussianElimination.h deleted file mode 100644 index a2bfd38cb6e..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/GaussianElimination.h +++ /dev/null @@ -1,102 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -JPH_NAMESPACE_BEGIN - -/// This function performs Gauss-Jordan elimination to solve a matrix equation. -/// A must be an NxN matrix and B must be an NxM matrix forming the equation A * x = B -/// on output B will contain x and A will be destroyed. -/// -/// This code can be used for example to compute the inverse of a matrix. -/// Set A to the matrix to invert, set B to identity and let GaussianElimination solve -/// the equation, on return B will be the inverse of A. And A is destroyed. -/// -/// Taken and adapted from Numerical Recipies in C paragraph 2.1 -template -bool GaussianElimination(MatrixA &ioA, MatrixB &ioB, float inTolerance = 1.0e-16f) -{ - // Get problem dimensions - const uint n = ioA.GetCols(); - const uint m = ioB.GetCols(); - - // Check matrix requirement - JPH_ASSERT(ioA.GetRows() == n); - JPH_ASSERT(ioB.GetRows() == n); - - // Create array for bookkeeping on pivoting - int *ipiv = (int *)JPH_STACK_ALLOC(n * sizeof(int)); - memset(ipiv, 0, n * sizeof(int)); - - for (uint i = 0; i < n; ++i) - { - // Initialize pivot element as the diagonal - uint pivot_row = i, pivot_col = i; - - // Determine pivot element - float largest_element = 0.0f; - for (uint j = 0; j < n; ++j) - if (ipiv[j] != 1) - for (uint k = 0; k < n; ++k) - { - if (ipiv[k] == 0) - { - float element = abs(ioA(j, k)); - if (element >= largest_element) - { - largest_element = element; - pivot_row = j; - pivot_col = k; - } - } - else if (ipiv[k] > 1) - { - return false; - } - } - - // Mark this column as used - ++ipiv[pivot_col]; - - // Exchange rows when needed so that the pivot element is at ioA(pivot_col, pivot_col) instead of at ioA(pivot_row, pivot_col) - if (pivot_row != pivot_col) - { - for (uint j = 0; j < n; ++j) - swap(ioA(pivot_row, j), ioA(pivot_col, j)); - for (uint j = 0; j < m; ++j) - swap(ioB(pivot_row, j), ioB(pivot_col, j)); - } - - // Get diagonal element that we are about to set to 1 - float diagonal_element = ioA(pivot_col, pivot_col); - if (abs(diagonal_element) < inTolerance) - return false; - - // Divide the whole row by the pivot element, making ioA(pivot_col, pivot_col) = 1 - for (uint j = 0; j < n; ++j) - ioA(pivot_col, j) /= diagonal_element; - for (uint j = 0; j < m; ++j) - ioB(pivot_col, j) /= diagonal_element; - ioA(pivot_col, pivot_col) = 1.0f; - - // Next reduce the rows, except for the pivot one, - // after this step the pivot_col column is zero except for the pivot element which is 1 - for (uint j = 0; j < n; ++j) - if (j != pivot_col) - { - float element = ioA(j, pivot_col); - for (uint k = 0; k < n; ++k) - ioA(j, k) -= ioA(pivot_col, k) * element; - for (uint k = 0; k < m; ++k) - ioB(j, k) -= ioB(pivot_col, k) * element; - ioA(j, pivot_col) = 0.0f; - } - } - - // Success - return true; -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/HalfFloat.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/HalfFloat.h deleted file mode 100644 index d28071e3038..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/HalfFloat.h +++ /dev/null @@ -1,204 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -using HalfFloat = uint16; - -// Define half float constant values -static constexpr HalfFloat HALF_FLT_MAX = 0x7bff; -static constexpr HalfFloat HALF_FLT_MAX_NEGATIVE = 0xfbff; -static constexpr HalfFloat HALF_FLT_INF = 0x7c00; -static constexpr HalfFloat HALF_FLT_INF_NEGATIVE = 0xfc00; -static constexpr HalfFloat HALF_FLT_NANQ = 0x7e00; -static constexpr HalfFloat HALF_FLT_NANQ_NEGATIVE = 0xfe00; - -namespace HalfFloatConversion { - -// Layout of a float -static constexpr int FLOAT_SIGN_POS = 31; -static constexpr int FLOAT_EXPONENT_POS = 23; -static constexpr int FLOAT_EXPONENT_BITS = 8; -static constexpr int FLOAT_EXPONENT_MASK = (1 << FLOAT_EXPONENT_BITS) - 1; -static constexpr int FLOAT_EXPONENT_BIAS = 127; -static constexpr int FLOAT_MANTISSA_BITS = 23; -static constexpr int FLOAT_MANTISSA_MASK = (1 << FLOAT_MANTISSA_BITS) - 1; -static constexpr int FLOAT_EXPONENT_AND_MANTISSA_MASK = FLOAT_MANTISSA_MASK + (FLOAT_EXPONENT_MASK << FLOAT_EXPONENT_POS); - -// Layout of half float -static constexpr int HALF_FLT_SIGN_POS = 15; -static constexpr int HALF_FLT_EXPONENT_POS = 10; -static constexpr int HALF_FLT_EXPONENT_BITS = 5; -static constexpr int HALF_FLT_EXPONENT_MASK = (1 << HALF_FLT_EXPONENT_BITS) - 1; -static constexpr int HALF_FLT_EXPONENT_BIAS = 15; -static constexpr int HALF_FLT_MANTISSA_BITS = 10; -static constexpr int HALF_FLT_MANTISSA_MASK = (1 << HALF_FLT_MANTISSA_BITS) - 1; -static constexpr int HALF_FLT_EXPONENT_AND_MANTISSA_MASK = HALF_FLT_MANTISSA_MASK + (HALF_FLT_EXPONENT_MASK << HALF_FLT_EXPONENT_POS); - -/// Define half-float rounding modes -enum ERoundingMode -{ - ROUND_TO_NEG_INF, ///< Round to negative infinity - ROUND_TO_POS_INF, ///< Round to positive infinity - ROUND_TO_NEAREST, ///< Round to nearest value -}; - -/// Convert a float (32-bits) to a half float (16-bits), fallback version when no intrinsics available -template -inline HalfFloat FromFloatFallback(float inV) -{ - // Reinterpret the float as an uint32 - uint32 value = BitCast(inV); - - // Extract exponent - uint32 exponent = (value >> FLOAT_EXPONENT_POS) & FLOAT_EXPONENT_MASK; - - // Extract mantissa - uint32 mantissa = value & FLOAT_MANTISSA_MASK; - - // Extract the sign and move it into the right spot for the half float (so we can just or it in at the end) - HalfFloat hf_sign = HalfFloat(value >> (FLOAT_SIGN_POS - HALF_FLT_SIGN_POS)) & (1 << HALF_FLT_SIGN_POS); - - // Check NaN or INF - if (exponent == FLOAT_EXPONENT_MASK) // NaN or INF - return hf_sign | (mantissa == 0? HALF_FLT_INF : HALF_FLT_NANQ); - - // Rebias the exponent for half floats - int rebiased_exponent = int(exponent) - FLOAT_EXPONENT_BIAS + HALF_FLT_EXPONENT_BIAS; - - // Check overflow to infinity - if (rebiased_exponent >= HALF_FLT_EXPONENT_MASK) - { - bool round_up = RoundingMode == ROUND_TO_NEAREST || (hf_sign == 0) == (RoundingMode == ROUND_TO_POS_INF); - return hf_sign | (round_up? HALF_FLT_INF : HALF_FLT_MAX); - } - - // Check underflow to zero - if (rebiased_exponent < -HALF_FLT_MANTISSA_BITS) - { - bool round_up = RoundingMode != ROUND_TO_NEAREST && (hf_sign == 0) == (RoundingMode == ROUND_TO_POS_INF) && (value & FLOAT_EXPONENT_AND_MANTISSA_MASK) != 0; - return hf_sign | (round_up? 1 : 0); - } - - HalfFloat hf_exponent; - int shift; - if (rebiased_exponent <= 0) - { - // Underflow to denormalized number - hf_exponent = 0; - mantissa |= 1 << FLOAT_MANTISSA_BITS; // Add the implicit 1 bit to the mantissa - shift = FLOAT_MANTISSA_BITS - HALF_FLT_MANTISSA_BITS + 1 - rebiased_exponent; - } - else - { - // Normal half float - hf_exponent = HalfFloat(rebiased_exponent << HALF_FLT_EXPONENT_POS); - shift = FLOAT_MANTISSA_BITS - HALF_FLT_MANTISSA_BITS; - } - - // Compose the half float - HalfFloat hf_mantissa = HalfFloat(mantissa >> shift); - HalfFloat hf = hf_sign | hf_exponent | hf_mantissa; - - // Calculate the remaining bits that we're discarding - uint remainder = mantissa & ((1 << shift) - 1); - - if constexpr (RoundingMode == ROUND_TO_NEAREST) - { - // Round to nearest - uint round_threshold = 1 << (shift - 1); - if (remainder > round_threshold // Above threshold, we must always round - || (remainder == round_threshold && (hf_mantissa & 1))) // When equal, round to nearest even - hf++; // May overflow to infinity - } - else - { - // Round up or down (truncate) depending on the rounding mode - bool round_up = (hf_sign == 0) == (RoundingMode == ROUND_TO_POS_INF) && remainder != 0; - if (round_up) - hf++; // May overflow to infinity - } - - return hf; -} - -/// Convert a float (32-bits) to a half float (16-bits) -template -JPH_INLINE HalfFloat FromFloat(float inV) -{ -#ifdef JPH_USE_F16C - union - { - __m128i u128; - HalfFloat u16[8]; - } hf; - __m128 val = _mm_load_ss(&inV); - switch (RoundingMode) - { - case ROUND_TO_NEG_INF: - hf.u128 = _mm_cvtps_ph(val, _MM_FROUND_TO_NEG_INF); - break; - case ROUND_TO_POS_INF: - hf.u128 = _mm_cvtps_ph(val, _MM_FROUND_TO_POS_INF); - break; - case ROUND_TO_NEAREST: - hf.u128 = _mm_cvtps_ph(val, _MM_FROUND_TO_NEAREST_INT); - break; - } - return hf.u16[0]; -#else - return FromFloatFallback(inV); -#endif -} - -/// Convert 4 half floats (lower 64 bits) to floats, fallback version when no intrinsics available -inline Vec4 ToFloatFallback(UVec4Arg inValue) -{ - // Unpack half floats to 4 uint32's - UVec4 value = inValue.Expand4Uint16Lo(); - - // Normal half float path, extract the exponent and mantissa, shift them into place and update the exponent bias - UVec4 exponent_mantissa = UVec4::sAnd(value, UVec4::sReplicate(HALF_FLT_EXPONENT_AND_MANTISSA_MASK)).LogicalShiftLeft() + UVec4::sReplicate((FLOAT_EXPONENT_BIAS - HALF_FLT_EXPONENT_BIAS) << FLOAT_EXPONENT_POS); - - // Denormalized half float path, renormalize the float - UVec4 exponent_mantissa_denormalized = ((exponent_mantissa + UVec4::sReplicate(1 << FLOAT_EXPONENT_POS)).ReinterpretAsFloat() - UVec4::sReplicate((FLOAT_EXPONENT_BIAS - HALF_FLT_EXPONENT_BIAS + 1) << FLOAT_EXPONENT_POS).ReinterpretAsFloat()).ReinterpretAsInt(); - - // NaN / INF path, set all exponent bits - UVec4 exponent_mantissa_nan_inf = UVec4::sOr(exponent_mantissa, UVec4::sReplicate(FLOAT_EXPONENT_MASK << FLOAT_EXPONENT_POS)); - - // Get the exponent to determine which of the paths we should take - UVec4 exponent_mask = UVec4::sReplicate(HALF_FLT_EXPONENT_MASK << HALF_FLT_EXPONENT_POS); - UVec4 exponent = UVec4::sAnd(value, exponent_mask); - UVec4 is_denormalized = UVec4::sEquals(exponent, UVec4::sZero()); - UVec4 is_nan_inf = UVec4::sEquals(exponent, exponent_mask); - - // Select the correct result - UVec4 result_exponent_mantissa = UVec4::sSelect(UVec4::sSelect(exponent_mantissa, exponent_mantissa_nan_inf, is_nan_inf), exponent_mantissa_denormalized, is_denormalized); - - // Extract the sign bit and shift it to the left - UVec4 sign = UVec4::sAnd(value, UVec4::sReplicate(1 << HALF_FLT_SIGN_POS)).LogicalShiftLeft(); - - // Construct the float - return UVec4::sOr(sign, result_exponent_mantissa).ReinterpretAsFloat(); -} - -/// Convert 4 half floats (lower 64 bits) to floats -JPH_INLINE Vec4 ToFloat(UVec4Arg inValue) -{ -#if defined(JPH_USE_F16C) - return _mm_cvtph_ps(inValue.mValue); -#elif defined(JPH_USE_NEON) - return vcvt_f32_f16(vreinterpret_f16_f32(vget_low_f32(inValue.mValue))); -#else - return ToFloatFallback(inValue); -#endif -} - -} // HalfFloatConversion - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Mat44.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Mat44.h deleted file mode 100644 index 1a1e254c564..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Mat44.h +++ /dev/null @@ -1,243 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -/// Holds a 4x4 matrix of floats, but supports also operations on the 3x3 upper left part of the matrix. -class [[nodiscard]] alignas(JPH_VECTOR_ALIGNMENT) Mat44 -{ -public: - JPH_OVERRIDE_NEW_DELETE - - // Underlying column type - using Type = Vec4::Type; - - // Argument type - using ArgType = Mat44Arg; - - /// Constructor - Mat44() = default; ///< Intentionally not initialized for performance reasons - JPH_INLINE Mat44(Vec4Arg inC1, Vec4Arg inC2, Vec4Arg inC3, Vec4Arg inC4); - JPH_INLINE Mat44(Vec4Arg inC1, Vec4Arg inC2, Vec4Arg inC3, Vec3Arg inC4); - Mat44(const Mat44 &inM2) = default; - Mat44 & operator = (const Mat44 &inM2) = default; - JPH_INLINE Mat44(Type inC1, Type inC2, Type inC3, Type inC4); - - /// Zero matrix - static JPH_INLINE Mat44 sZero(); - - /// Identity matrix - static JPH_INLINE Mat44 sIdentity(); - - /// Matrix filled with NaN's - static JPH_INLINE Mat44 sNaN(); - - /// Load 16 floats from memory - static JPH_INLINE Mat44 sLoadFloat4x4(const Float4 *inV); - - /// Load 16 floats from memory, 16 bytes aligned - static JPH_INLINE Mat44 sLoadFloat4x4Aligned(const Float4 *inV); - - /// Rotate around X, Y or Z axis (angle in radians) - static JPH_INLINE Mat44 sRotationX(float inX); - static JPH_INLINE Mat44 sRotationY(float inY); - static JPH_INLINE Mat44 sRotationZ(float inZ); - - /// Rotate around arbitrary axis - static JPH_INLINE Mat44 sRotation(Vec3Arg inAxis, float inAngle); - - /// Rotate from quaternion - static JPH_INLINE Mat44 sRotation(QuatArg inQuat); - - /// Get matrix that translates - static JPH_INLINE Mat44 sTranslation(Vec3Arg inV); - - /// Get matrix that rotates and translates - static JPH_INLINE Mat44 sRotationTranslation(QuatArg inR, Vec3Arg inT); - - /// Get inverse matrix of sRotationTranslation - static JPH_INLINE Mat44 sInverseRotationTranslation(QuatArg inR, Vec3Arg inT); - - /// Get matrix that scales uniformly - static JPH_INLINE Mat44 sScale(float inScale); - - /// Get matrix that scales (produces a matrix with (inV, 1) on its diagonal) - static JPH_INLINE Mat44 sScale(Vec3Arg inV); - - /// Get outer product of inV and inV2 (equivalent to \f$inV1 \otimes inV2\f$) - static JPH_INLINE Mat44 sOuterProduct(Vec3Arg inV1, Vec3Arg inV2); - - /// Get matrix that represents a cross product \f$A \times B = \text{sCrossProduct}(A) \: B\f$ - static JPH_INLINE Mat44 sCrossProduct(Vec3Arg inV); - - /// Returns matrix ML so that \f$ML(q) \: p = q \: p\f$ (where p and q are quaternions) - static JPH_INLINE Mat44 sQuatLeftMultiply(QuatArg inQ); - - /// Returns matrix MR so that \f$MR(q) \: p = p \: q\f$ (where p and q are quaternions) - static JPH_INLINE Mat44 sQuatRightMultiply(QuatArg inQ); - - /// Returns a look at matrix that transforms from world space to view space - /// @param inPos Position of the camera - /// @param inTarget Target of the camera - /// @param inUp Up vector - static JPH_INLINE Mat44 sLookAt(Vec3Arg inPos, Vec3Arg inTarget, Vec3Arg inUp); - - /// Returns a right-handed perspective projection matrix - static JPH_INLINE Mat44 sPerspective(float inFovY, float inAspect, float inNear, float inFar); - - /// Get float component by element index - JPH_INLINE float operator () (uint inRow, uint inColumn) const { JPH_ASSERT(inRow < 4); JPH_ASSERT(inColumn < 4); return mCol[inColumn].mF32[inRow]; } - JPH_INLINE float & operator () (uint inRow, uint inColumn) { JPH_ASSERT(inRow < 4); JPH_ASSERT(inColumn < 4); return mCol[inColumn].mF32[inRow]; } - - /// Comparison - JPH_INLINE bool operator == (Mat44Arg inM2) const; - JPH_INLINE bool operator != (Mat44Arg inM2) const { return !(*this == inM2); } - - /// Test if two matrices are close - JPH_INLINE bool IsClose(Mat44Arg inM2, float inMaxDistSq = 1.0e-12f) const; - - /// Multiply matrix by matrix - JPH_INLINE Mat44 operator * (Mat44Arg inM) const; - - /// Multiply vector by matrix - JPH_INLINE Vec3 operator * (Vec3Arg inV) const; - JPH_INLINE Vec4 operator * (Vec4Arg inV) const; - - /// Multiply vector by only 3x3 part of the matrix - JPH_INLINE Vec3 Multiply3x3(Vec3Arg inV) const; - - /// Multiply vector by only 3x3 part of the transpose of the matrix (\f$result = this^T \: inV\f$) - JPH_INLINE Vec3 Multiply3x3Transposed(Vec3Arg inV) const; - - /// Multiply 3x3 matrix by 3x3 matrix - JPH_INLINE Mat44 Multiply3x3(Mat44Arg inM) const; - - /// Multiply transpose of 3x3 matrix by 3x3 matrix (\f$result = this^T \: inM\f$) - JPH_INLINE Mat44 Multiply3x3LeftTransposed(Mat44Arg inM) const; - - /// Multiply 3x3 matrix by the transpose of a 3x3 matrix (\f$result = this \: inM^T\f$) - JPH_INLINE Mat44 Multiply3x3RightTransposed(Mat44Arg inM) const; - - /// Multiply matrix with float - JPH_INLINE Mat44 operator * (float inV) const; - friend JPH_INLINE Mat44 operator * (float inV, Mat44Arg inM) { return inM * inV; } - - /// Multiply matrix with float - JPH_INLINE Mat44 & operator *= (float inV); - - /// Per element addition of matrix - JPH_INLINE Mat44 operator + (Mat44Arg inM) const; - - /// Negate - JPH_INLINE Mat44 operator - () const; - - /// Per element subtraction of matrix - JPH_INLINE Mat44 operator - (Mat44Arg inM) const; - - /// Per element addition of matrix - JPH_INLINE Mat44 & operator += (Mat44Arg inM); - - /// Access to the columns - JPH_INLINE Vec3 GetAxisX() const { return Vec3(mCol[0]); } - JPH_INLINE void SetAxisX(Vec3Arg inV) { mCol[0] = Vec4(inV, 0.0f); } - JPH_INLINE Vec3 GetAxisY() const { return Vec3(mCol[1]); } - JPH_INLINE void SetAxisY(Vec3Arg inV) { mCol[1] = Vec4(inV, 0.0f); } - JPH_INLINE Vec3 GetAxisZ() const { return Vec3(mCol[2]); } - JPH_INLINE void SetAxisZ(Vec3Arg inV) { mCol[2] = Vec4(inV, 0.0f); } - JPH_INLINE Vec3 GetTranslation() const { return Vec3(mCol[3]); } - JPH_INLINE void SetTranslation(Vec3Arg inV) { mCol[3] = Vec4(inV, 1.0f); } - JPH_INLINE Vec3 GetDiagonal3() const { return Vec3(mCol[0][0], mCol[1][1], mCol[2][2]); } - JPH_INLINE void SetDiagonal3(Vec3Arg inV) { mCol[0][0] = inV.GetX(); mCol[1][1] = inV.GetY(); mCol[2][2] = inV.GetZ(); } - JPH_INLINE Vec4 GetDiagonal4() const { return Vec4(mCol[0][0], mCol[1][1], mCol[2][2], mCol[3][3]); } - JPH_INLINE void SetDiagonal4(Vec4Arg inV) { mCol[0][0] = inV.GetX(); mCol[1][1] = inV.GetY(); mCol[2][2] = inV.GetZ(); mCol[3][3] = inV.GetW(); } - JPH_INLINE Vec3 GetColumn3(uint inCol) const { JPH_ASSERT(inCol < 4); return Vec3(mCol[inCol]); } - JPH_INLINE void SetColumn3(uint inCol, Vec3Arg inV) { JPH_ASSERT(inCol < 4); mCol[inCol] = Vec4(inV, inCol == 3? 1.0f : 0.0f); } - JPH_INLINE Vec4 GetColumn4(uint inCol) const { JPH_ASSERT(inCol < 4); return mCol[inCol]; } - JPH_INLINE void SetColumn4(uint inCol, Vec4Arg inV) { JPH_ASSERT(inCol < 4); mCol[inCol] = inV; } - - /// Store matrix to memory - JPH_INLINE void StoreFloat4x4(Float4 *outV) const; - - /// Transpose matrix - JPH_INLINE Mat44 Transposed() const; - - /// Transpose 3x3 subpart of matrix - JPH_INLINE Mat44 Transposed3x3() const; - - /// Inverse 4x4 matrix - JPH_INLINE Mat44 Inversed() const; - - /// Inverse 4x4 matrix when it only contains rotation and translation - JPH_INLINE Mat44 InversedRotationTranslation() const; - - /// Get the determinant of a 3x3 matrix - JPH_INLINE float GetDeterminant3x3() const; - - /// Get the adjoint of a 3x3 matrix - JPH_INLINE Mat44 Adjointed3x3() const; - - /// Inverse 3x3 matrix - JPH_INLINE Mat44 Inversed3x3() const; - - /// *this = inM.Inversed3x3(), returns false if the matrix is singular in which case *this is unchanged - JPH_INLINE bool SetInversed3x3(Mat44Arg inM); - - /// Get rotation part only (note: retains the first 3 values from the bottom row) - JPH_INLINE Mat44 GetRotation() const; - - /// Get rotation part only (note: also clears the bottom row) - JPH_INLINE Mat44 GetRotationSafe() const; - - /// Updates the rotation part of this matrix (the first 3 columns) - JPH_INLINE void SetRotation(Mat44Arg inRotation); - - /// Convert to quaternion - JPH_INLINE Quat GetQuaternion() const; - - /// Get matrix that transforms a direction with the same transform as this matrix (length is not preserved) - JPH_INLINE Mat44 GetDirectionPreservingMatrix() const { return GetRotation().Inversed3x3().Transposed3x3(); } - - /// Pre multiply by translation matrix: result = this * Mat44::sTranslation(inTranslation) - JPH_INLINE Mat44 PreTranslated(Vec3Arg inTranslation) const; - - /// Post multiply by translation matrix: result = Mat44::sTranslation(inTranslation) * this (i.e. add inTranslation to the 4-th column) - JPH_INLINE Mat44 PostTranslated(Vec3Arg inTranslation) const; - - /// Scale a matrix: result = this * Mat44::sScale(inScale) - JPH_INLINE Mat44 PreScaled(Vec3Arg inScale) const; - - /// Scale a matrix: result = Mat44::sScale(inScale) * this - JPH_INLINE Mat44 PostScaled(Vec3Arg inScale) const; - - /// Decompose a matrix into a rotation & translation part and into a scale part so that: - /// this = return_value * Mat44::sScale(outScale). - /// This equation only holds when the matrix is orthogonal, if it is not the returned matrix - /// will be made orthogonal using the modified Gram-Schmidt algorithm (see: https://en.wikipedia.org/wiki/Gram%E2%80%93Schmidt_process) - JPH_INLINE Mat44 Decompose(Vec3 &outScale) const; - -#ifndef JPH_DOUBLE_PRECISION - /// In single precision mode just return the matrix itself - JPH_INLINE Mat44 ToMat44() const { return *this; } -#endif // !JPH_DOUBLE_PRECISION - - /// To String - friend ostream & operator << (ostream &inStream, Mat44Arg inM) - { - inStream << inM.mCol[0] << ", " << inM.mCol[1] << ", " << inM.mCol[2] << ", " << inM.mCol[3]; - return inStream; - } - -private: - Vec4 mCol[4]; ///< Column -}; - -static_assert(is_trivial(), "Is supposed to be a trivial type!"); - -JPH_NAMESPACE_END - -#include "Mat44.inl" diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Mat44.inl b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Mat44.inl deleted file mode 100644 index 76577b7153a..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Mat44.inl +++ /dev/null @@ -1,952 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -#define JPH_EL(r, c) mCol[c].mF32[r] - -Mat44::Mat44(Vec4Arg inC1, Vec4Arg inC2, Vec4Arg inC3, Vec4Arg inC4) : - mCol { inC1, inC2, inC3, inC4 } -{ -} - -Mat44::Mat44(Vec4Arg inC1, Vec4Arg inC2, Vec4Arg inC3, Vec3Arg inC4) : - mCol { inC1, inC2, inC3, Vec4(inC4, 1.0f) } -{ -} - -Mat44::Mat44(Type inC1, Type inC2, Type inC3, Type inC4) : - mCol { inC1, inC2, inC3, inC4 } -{ -} - -Mat44 Mat44::sZero() -{ - return Mat44(Vec4::sZero(), Vec4::sZero(), Vec4::sZero(), Vec4::sZero()); -} - -Mat44 Mat44::sIdentity() -{ - return Mat44(Vec4(1, 0, 0, 0), Vec4(0, 1, 0, 0), Vec4(0, 0, 1, 0), Vec4(0, 0, 0, 1)); -} - -Mat44 Mat44::sNaN() -{ - return Mat44(Vec4::sNaN(), Vec4::sNaN(), Vec4::sNaN(), Vec4::sNaN()); -} - -Mat44 Mat44::sLoadFloat4x4(const Float4 *inV) -{ - Mat44 result; - for (int c = 0; c < 4; ++c) - result.mCol[c] = Vec4::sLoadFloat4(inV + c); - return result; -} - -Mat44 Mat44::sLoadFloat4x4Aligned(const Float4 *inV) -{ - Mat44 result; - for (int c = 0; c < 4; ++c) - result.mCol[c] = Vec4::sLoadFloat4Aligned(inV + c); - return result; -} - -Mat44 Mat44::sRotationX(float inX) -{ - Vec4 sv, cv; - Vec4::sReplicate(inX).SinCos(sv, cv); - float s = sv.GetX(), c = cv.GetX(); - return Mat44(Vec4(1, 0, 0, 0), Vec4(0, c, s, 0), Vec4(0, -s, c, 0), Vec4(0, 0, 0, 1)); -} - -Mat44 Mat44::sRotationY(float inY) -{ - Vec4 sv, cv; - Vec4::sReplicate(inY).SinCos(sv, cv); - float s = sv.GetX(), c = cv.GetX(); - return Mat44(Vec4(c, 0, -s, 0), Vec4(0, 1, 0, 0), Vec4(s, 0, c, 0), Vec4(0, 0, 0, 1)); -} - -Mat44 Mat44::sRotationZ(float inZ) -{ - Vec4 sv, cv; - Vec4::sReplicate(inZ).SinCos(sv, cv); - float s = sv.GetX(), c = cv.GetX(); - return Mat44(Vec4(c, s, 0, 0), Vec4(-s, c, 0, 0), Vec4(0, 0, 1, 0), Vec4(0, 0, 0, 1)); -} - -Mat44 Mat44::sRotation(QuatArg inQuat) -{ - JPH_ASSERT(inQuat.IsNormalized()); - - // See: https://en.wikipedia.org/wiki/Quaternions_and_spatial_rotation section 'Quaternion-derived rotation matrix' -#ifdef JPH_USE_SSE4_1 - __m128 xyzw = inQuat.mValue.mValue; - __m128 two_xyzw = _mm_add_ps(xyzw, xyzw); - __m128 yzxw = _mm_shuffle_ps(xyzw, xyzw, _MM_SHUFFLE(3, 0, 2, 1)); - __m128 two_yzxw = _mm_add_ps(yzxw, yzxw); - __m128 zxyw = _mm_shuffle_ps(xyzw, xyzw, _MM_SHUFFLE(3, 1, 0, 2)); - __m128 two_zxyw = _mm_add_ps(zxyw, zxyw); - __m128 wwww = _mm_shuffle_ps(xyzw, xyzw, _MM_SHUFFLE(3, 3, 3, 3)); - __m128 diagonal = _mm_sub_ps(_mm_sub_ps(_mm_set1_ps(1.0f), _mm_mul_ps(two_yzxw, yzxw)), _mm_mul_ps(two_zxyw, zxyw)); // (1 - 2 y^2 - 2 z^2, 1 - 2 x^2 - 2 z^2, 1 - 2 x^2 - 2 y^2, 1 - 4 w^2) - __m128 plus = _mm_add_ps(_mm_mul_ps(two_xyzw, zxyw), _mm_mul_ps(two_yzxw, wwww)); // 2 * (xz + yw, xy + zw, yz + xw, ww) - __m128 minus = _mm_sub_ps(_mm_mul_ps(two_yzxw, xyzw), _mm_mul_ps(two_zxyw, wwww)); // 2 * (xy - zw, yz - xw, xz - yw, 0) - - // Workaround for compiler changing _mm_sub_ps(_mm_mul_ps(...), ...) into a fused multiply sub instruction, resulting in w not being 0 - // There doesn't appear to be a reliable way to turn this off in Clang - minus = _mm_insert_ps(minus, minus, 0b1000); - - __m128 col0 = _mm_blend_ps(_mm_blend_ps(plus, diagonal, 0b0001), minus, 0b1100); // (1 - 2 y^2 - 2 z^2, 2 xy + 2 zw, 2 xz - 2 yw, 0) - __m128 col1 = _mm_blend_ps(_mm_blend_ps(diagonal, minus, 0b1001), plus, 0b0100); // (2 xy - 2 zw, 1 - 2 x^2 - 2 z^2, 2 yz + 2 xw, 0) - __m128 col2 = _mm_blend_ps(_mm_blend_ps(minus, plus, 0b0001), diagonal, 0b0100); // (2 xz + 2 yw, 2 yz - 2 xw, 1 - 2 x^2 - 2 y^2, 0) - __m128 col3 = _mm_set_ps(1, 0, 0, 0); - - return Mat44(col0, col1, col2, col3); -#else - float x = inQuat.GetX(); - float y = inQuat.GetY(); - float z = inQuat.GetZ(); - float w = inQuat.GetW(); - - float tx = x + x; // Note: Using x + x instead of 2.0f * x to force this function to return the same value as the SSE4.1 version across platforms. - float ty = y + y; - float tz = z + z; - - float xx = tx * x; - float yy = ty * y; - float zz = tz * z; - float xy = tx * y; - float xz = tx * z; - float xw = tx * w; - float yz = ty * z; - float yw = ty * w; - float zw = tz * w; - - return Mat44(Vec4((1.0f - yy) - zz, xy + zw, xz - yw, 0.0f), // Note: Added extra brackets to force this function to return the same value as the SSE4.1 version across platforms. - Vec4(xy - zw, (1.0f - zz) - xx, yz + xw, 0.0f), - Vec4(xz + yw, yz - xw, (1.0f - xx) - yy, 0.0f), - Vec4(0.0f, 0.0f, 0.0f, 1.0f)); -#endif -} - -Mat44 Mat44::sRotation(Vec3Arg inAxis, float inAngle) -{ - return sRotation(Quat::sRotation(inAxis, inAngle)); -} - -Mat44 Mat44::sTranslation(Vec3Arg inV) -{ - return Mat44(Vec4(1, 0, 0, 0), Vec4(0, 1, 0, 0), Vec4(0, 0, 1, 0), Vec4(inV, 1)); -} - -Mat44 Mat44::sRotationTranslation(QuatArg inR, Vec3Arg inT) -{ - Mat44 m = sRotation(inR); - m.SetTranslation(inT); - return m; -} - -Mat44 Mat44::sInverseRotationTranslation(QuatArg inR, Vec3Arg inT) -{ - Mat44 m = sRotation(inR.Conjugated()); - m.SetTranslation(-m.Multiply3x3(inT)); - return m; -} - -Mat44 Mat44::sScale(float inScale) -{ - return Mat44(Vec4(inScale, 0, 0, 0), Vec4(0, inScale, 0, 0), Vec4(0, 0, inScale, 0), Vec4(0, 0, 0, 1)); -} - -Mat44 Mat44::sScale(Vec3Arg inV) -{ - return Mat44(Vec4(inV.GetX(), 0, 0, 0), Vec4(0, inV.GetY(), 0, 0), Vec4(0, 0, inV.GetZ(), 0), Vec4(0, 0, 0, 1)); -} - -Mat44 Mat44::sOuterProduct(Vec3Arg inV1, Vec3Arg inV2) -{ - Vec4 v1(inV1, 0); - return Mat44(v1 * inV2.SplatX(), v1 * inV2.SplatY(), v1 * inV2.SplatZ(), Vec4(0, 0, 0, 1)); -} - -Mat44 Mat44::sCrossProduct(Vec3Arg inV) -{ -#ifdef JPH_USE_SSE4_1 - // Zero out the W component - __m128 zero = _mm_setzero_ps(); - __m128 v = _mm_blend_ps(inV.mValue, zero, 0b1000); - - // Negate - __m128 min_v = _mm_sub_ps(zero, v); - - return Mat44( - _mm_shuffle_ps(v, min_v, _MM_SHUFFLE(3, 1, 2, 3)), // [0, z, -y, 0] - _mm_shuffle_ps(min_v, v, _MM_SHUFFLE(3, 0, 3, 2)), // [-z, 0, x, 0] - _mm_blend_ps(_mm_shuffle_ps(v, v, _MM_SHUFFLE(3, 3, 3, 1)), _mm_shuffle_ps(min_v, min_v, _MM_SHUFFLE(3, 3, 0, 3)), 0b0010), // [y, -x, 0, 0] - Vec4(0, 0, 0, 1)); -#else - float x = inV.GetX(); - float y = inV.GetY(); - float z = inV.GetZ(); - - return Mat44( - Vec4(0, z, -y, 0), - Vec4(-z, 0, x, 0), - Vec4(y, -x, 0, 0), - Vec4(0, 0, 0, 1)); -#endif -} - -Mat44 Mat44::sLookAt(Vec3Arg inPos, Vec3Arg inTarget, Vec3Arg inUp) -{ - Vec3 direction = (inTarget - inPos).NormalizedOr(-Vec3::sAxisZ()); - Vec3 right = direction.Cross(inUp).NormalizedOr(Vec3::sAxisX()); - Vec3 up = right.Cross(direction); - - return Mat44(Vec4(right, 0), Vec4(up, 0), Vec4(-direction, 0), Vec4(inPos, 1)).InversedRotationTranslation(); -} - -Mat44 Mat44::sPerspective(float inFovY, float inAspect, float inNear, float inFar) -{ - float height = 1.0f / Tan(0.5f * inFovY); - float width = height / inAspect; - float range = inFar / (inNear - inFar); - - return Mat44(Vec4(width, 0.0f, 0.0f, 0.0f), Vec4(0.0f, height, 0.0f, 0.0f), Vec4(0.0f, 0.0f, range, -1.0f), Vec4(0.0f, 0.0f, range * inNear, 0.0f)); -} - -bool Mat44::operator == (Mat44Arg inM2) const -{ - return UVec4::sAnd( - UVec4::sAnd(Vec4::sEquals(mCol[0], inM2.mCol[0]), Vec4::sEquals(mCol[1], inM2.mCol[1])), - UVec4::sAnd(Vec4::sEquals(mCol[2], inM2.mCol[2]), Vec4::sEquals(mCol[3], inM2.mCol[3])) - ).TestAllTrue(); -} - -bool Mat44::IsClose(Mat44Arg inM2, float inMaxDistSq) const -{ - for (int i = 0; i < 4; ++i) - if (!mCol[i].IsClose(inM2.mCol[i], inMaxDistSq)) - return false; - return true; -} - -Mat44 Mat44::operator * (Mat44Arg inM) const -{ - Mat44 result; -#if defined(JPH_USE_SSE) - for (int i = 0; i < 4; ++i) - { - __m128 c = inM.mCol[i].mValue; - __m128 t = _mm_mul_ps(mCol[0].mValue, _mm_shuffle_ps(c, c, _MM_SHUFFLE(0, 0, 0, 0))); - t = _mm_add_ps(t, _mm_mul_ps(mCol[1].mValue, _mm_shuffle_ps(c, c, _MM_SHUFFLE(1, 1, 1, 1)))); - t = _mm_add_ps(t, _mm_mul_ps(mCol[2].mValue, _mm_shuffle_ps(c, c, _MM_SHUFFLE(2, 2, 2, 2)))); - t = _mm_add_ps(t, _mm_mul_ps(mCol[3].mValue, _mm_shuffle_ps(c, c, _MM_SHUFFLE(3, 3, 3, 3)))); - result.mCol[i].mValue = t; - } -#elif defined(JPH_USE_NEON) - for (int i = 0; i < 4; ++i) - { - Type c = inM.mCol[i].mValue; - Type t = vmulq_f32(mCol[0].mValue, vdupq_laneq_f32(c, 0)); - t = vmlaq_f32(t, mCol[1].mValue, vdupq_laneq_f32(c, 1)); - t = vmlaq_f32(t, mCol[2].mValue, vdupq_laneq_f32(c, 2)); - t = vmlaq_f32(t, mCol[3].mValue, vdupq_laneq_f32(c, 3)); - result.mCol[i].mValue = t; - } -#else - for (int i = 0; i < 4; ++i) - result.mCol[i] = mCol[0] * inM.mCol[i].mF32[0] + mCol[1] * inM.mCol[i].mF32[1] + mCol[2] * inM.mCol[i].mF32[2] + mCol[3] * inM.mCol[i].mF32[3]; -#endif - return result; -} - -Vec3 Mat44::operator * (Vec3Arg inV) const -{ -#if defined(JPH_USE_SSE) - __m128 t = _mm_mul_ps(mCol[0].mValue, _mm_shuffle_ps(inV.mValue, inV.mValue, _MM_SHUFFLE(0, 0, 0, 0))); - t = _mm_add_ps(t, _mm_mul_ps(mCol[1].mValue, _mm_shuffle_ps(inV.mValue, inV.mValue, _MM_SHUFFLE(1, 1, 1, 1)))); - t = _mm_add_ps(t, _mm_mul_ps(mCol[2].mValue, _mm_shuffle_ps(inV.mValue, inV.mValue, _MM_SHUFFLE(2, 2, 2, 2)))); - t = _mm_add_ps(t, mCol[3].mValue); - return Vec3::sFixW(t); -#elif defined(JPH_USE_NEON) - Type t = vmulq_f32(mCol[0].mValue, vdupq_laneq_f32(inV.mValue, 0)); - t = vmlaq_f32(t, mCol[1].mValue, vdupq_laneq_f32(inV.mValue, 1)); - t = vmlaq_f32(t, mCol[2].mValue, vdupq_laneq_f32(inV.mValue, 2)); - t = vaddq_f32(t, mCol[3].mValue); // Don't combine this with the first mul into a fused multiply add, causes precision issues - return Vec3::sFixW(t); -#else - return Vec3( - mCol[0].mF32[0] * inV.mF32[0] + mCol[1].mF32[0] * inV.mF32[1] + mCol[2].mF32[0] * inV.mF32[2] + mCol[3].mF32[0], - mCol[0].mF32[1] * inV.mF32[0] + mCol[1].mF32[1] * inV.mF32[1] + mCol[2].mF32[1] * inV.mF32[2] + mCol[3].mF32[1], - mCol[0].mF32[2] * inV.mF32[0] + mCol[1].mF32[2] * inV.mF32[1] + mCol[2].mF32[2] * inV.mF32[2] + mCol[3].mF32[2]); -#endif -} - -Vec4 Mat44::operator * (Vec4Arg inV) const -{ -#if defined(JPH_USE_SSE) - __m128 t = _mm_mul_ps(mCol[0].mValue, _mm_shuffle_ps(inV.mValue, inV.mValue, _MM_SHUFFLE(0, 0, 0, 0))); - t = _mm_add_ps(t, _mm_mul_ps(mCol[1].mValue, _mm_shuffle_ps(inV.mValue, inV.mValue, _MM_SHUFFLE(1, 1, 1, 1)))); - t = _mm_add_ps(t, _mm_mul_ps(mCol[2].mValue, _mm_shuffle_ps(inV.mValue, inV.mValue, _MM_SHUFFLE(2, 2, 2, 2)))); - t = _mm_add_ps(t, _mm_mul_ps(mCol[3].mValue, _mm_shuffle_ps(inV.mValue, inV.mValue, _MM_SHUFFLE(3, 3, 3, 3)))); - return t; -#elif defined(JPH_USE_NEON) - Type t = vmulq_f32(mCol[0].mValue, vdupq_laneq_f32(inV.mValue, 0)); - t = vmlaq_f32(t, mCol[1].mValue, vdupq_laneq_f32(inV.mValue, 1)); - t = vmlaq_f32(t, mCol[2].mValue, vdupq_laneq_f32(inV.mValue, 2)); - t = vmlaq_f32(t, mCol[3].mValue, vdupq_laneq_f32(inV.mValue, 3)); - return t; -#else - return Vec4( - mCol[0].mF32[0] * inV.mF32[0] + mCol[1].mF32[0] * inV.mF32[1] + mCol[2].mF32[0] * inV.mF32[2] + mCol[3].mF32[0] * inV.mF32[3], - mCol[0].mF32[1] * inV.mF32[0] + mCol[1].mF32[1] * inV.mF32[1] + mCol[2].mF32[1] * inV.mF32[2] + mCol[3].mF32[1] * inV.mF32[3], - mCol[0].mF32[2] * inV.mF32[0] + mCol[1].mF32[2] * inV.mF32[1] + mCol[2].mF32[2] * inV.mF32[2] + mCol[3].mF32[2] * inV.mF32[3], - mCol[0].mF32[3] * inV.mF32[0] + mCol[1].mF32[3] * inV.mF32[1] + mCol[2].mF32[3] * inV.mF32[2] + mCol[3].mF32[3] * inV.mF32[3]); -#endif -} - -Vec3 Mat44::Multiply3x3(Vec3Arg inV) const -{ -#if defined(JPH_USE_SSE) - __m128 t = _mm_mul_ps(mCol[0].mValue, _mm_shuffle_ps(inV.mValue, inV.mValue, _MM_SHUFFLE(0, 0, 0, 0))); - t = _mm_add_ps(t, _mm_mul_ps(mCol[1].mValue, _mm_shuffle_ps(inV.mValue, inV.mValue, _MM_SHUFFLE(1, 1, 1, 1)))); - t = _mm_add_ps(t, _mm_mul_ps(mCol[2].mValue, _mm_shuffle_ps(inV.mValue, inV.mValue, _MM_SHUFFLE(2, 2, 2, 2)))); - return Vec3::sFixW(t); -#elif defined(JPH_USE_NEON) - Type t = vmulq_f32(mCol[0].mValue, vdupq_laneq_f32(inV.mValue, 0)); - t = vmlaq_f32(t, mCol[1].mValue, vdupq_laneq_f32(inV.mValue, 1)); - t = vmlaq_f32(t, mCol[2].mValue, vdupq_laneq_f32(inV.mValue, 2)); - return Vec3::sFixW(t); -#else - return Vec3( - mCol[0].mF32[0] * inV.mF32[0] + mCol[1].mF32[0] * inV.mF32[1] + mCol[2].mF32[0] * inV.mF32[2], - mCol[0].mF32[1] * inV.mF32[0] + mCol[1].mF32[1] * inV.mF32[1] + mCol[2].mF32[1] * inV.mF32[2], - mCol[0].mF32[2] * inV.mF32[0] + mCol[1].mF32[2] * inV.mF32[1] + mCol[2].mF32[2] * inV.mF32[2]); -#endif -} - -Vec3 Mat44::Multiply3x3Transposed(Vec3Arg inV) const -{ -#if defined(JPH_USE_SSE4_1) - __m128 x = _mm_dp_ps(mCol[0].mValue, inV.mValue, 0x7f); - __m128 y = _mm_dp_ps(mCol[1].mValue, inV.mValue, 0x7f); - __m128 xy = _mm_blend_ps(x, y, 0b0010); - __m128 z = _mm_dp_ps(mCol[2].mValue, inV.mValue, 0x7f); - __m128 xyzz = _mm_blend_ps(xy, z, 0b1100); - return xyzz; -#else - return Transposed3x3().Multiply3x3(inV); -#endif -} - -Mat44 Mat44::Multiply3x3(Mat44Arg inM) const -{ - JPH_ASSERT(mCol[0][3] == 0.0f); - JPH_ASSERT(mCol[1][3] == 0.0f); - JPH_ASSERT(mCol[2][3] == 0.0f); - - Mat44 result; -#if defined(JPH_USE_SSE) - for (int i = 0; i < 3; ++i) - { - __m128 c = inM.mCol[i].mValue; - __m128 t = _mm_mul_ps(mCol[0].mValue, _mm_shuffle_ps(c, c, _MM_SHUFFLE(0, 0, 0, 0))); - t = _mm_add_ps(t, _mm_mul_ps(mCol[1].mValue, _mm_shuffle_ps(c, c, _MM_SHUFFLE(1, 1, 1, 1)))); - t = _mm_add_ps(t, _mm_mul_ps(mCol[2].mValue, _mm_shuffle_ps(c, c, _MM_SHUFFLE(2, 2, 2, 2)))); - result.mCol[i].mValue = t; - } -#elif defined(JPH_USE_NEON) - for (int i = 0; i < 3; ++i) - { - Type c = inM.mCol[i].mValue; - Type t = vmulq_f32(mCol[0].mValue, vdupq_laneq_f32(c, 0)); - t = vmlaq_f32(t, mCol[1].mValue, vdupq_laneq_f32(c, 1)); - t = vmlaq_f32(t, mCol[2].mValue, vdupq_laneq_f32(c, 2)); - result.mCol[i].mValue = t; - } -#else - for (int i = 0; i < 3; ++i) - result.mCol[i] = mCol[0] * inM.mCol[i].mF32[0] + mCol[1] * inM.mCol[i].mF32[1] + mCol[2] * inM.mCol[i].mF32[2]; -#endif - result.mCol[3] = Vec4(0, 0, 0, 1); - return result; -} - -Mat44 Mat44::Multiply3x3LeftTransposed(Mat44Arg inM) const -{ - // Transpose left hand side - Mat44 trans = Transposed3x3(); - - // Do 3x3 matrix multiply - Mat44 result; - result.mCol[0] = trans.mCol[0] * inM.mCol[0].SplatX() + trans.mCol[1] * inM.mCol[0].SplatY() + trans.mCol[2] * inM.mCol[0].SplatZ(); - result.mCol[1] = trans.mCol[0] * inM.mCol[1].SplatX() + trans.mCol[1] * inM.mCol[1].SplatY() + trans.mCol[2] * inM.mCol[1].SplatZ(); - result.mCol[2] = trans.mCol[0] * inM.mCol[2].SplatX() + trans.mCol[1] * inM.mCol[2].SplatY() + trans.mCol[2] * inM.mCol[2].SplatZ(); - result.mCol[3] = Vec4(0, 0, 0, 1); - return result; -} - -Mat44 Mat44::Multiply3x3RightTransposed(Mat44Arg inM) const -{ - JPH_ASSERT(mCol[0][3] == 0.0f); - JPH_ASSERT(mCol[1][3] == 0.0f); - JPH_ASSERT(mCol[2][3] == 0.0f); - - Mat44 result; - result.mCol[0] = mCol[0] * inM.mCol[0].SplatX() + mCol[1] * inM.mCol[1].SplatX() + mCol[2] * inM.mCol[2].SplatX(); - result.mCol[1] = mCol[0] * inM.mCol[0].SplatY() + mCol[1] * inM.mCol[1].SplatY() + mCol[2] * inM.mCol[2].SplatY(); - result.mCol[2] = mCol[0] * inM.mCol[0].SplatZ() + mCol[1] * inM.mCol[1].SplatZ() + mCol[2] * inM.mCol[2].SplatZ(); - result.mCol[3] = Vec4(0, 0, 0, 1); - return result; -} - -Mat44 Mat44::operator * (float inV) const -{ - Vec4 multiplier = Vec4::sReplicate(inV); - - Mat44 result; - for (int c = 0; c < 4; ++c) - result.mCol[c] = mCol[c] * multiplier; - return result; -} - -Mat44 &Mat44::operator *= (float inV) -{ - for (int c = 0; c < 4; ++c) - mCol[c] *= inV; - - return *this; -} - -Mat44 Mat44::operator + (Mat44Arg inM) const -{ - Mat44 result; - for (int i = 0; i < 4; ++i) - result.mCol[i] = mCol[i] + inM.mCol[i]; - return result; -} - -Mat44 Mat44::operator - () const -{ - Mat44 result; - for (int i = 0; i < 4; ++i) - result.mCol[i] = -mCol[i]; - return result; -} - -Mat44 Mat44::operator - (Mat44Arg inM) const -{ - Mat44 result; - for (int i = 0; i < 4; ++i) - result.mCol[i] = mCol[i] - inM.mCol[i]; - return result; -} - -Mat44 &Mat44::operator += (Mat44Arg inM) -{ - for (int c = 0; c < 4; ++c) - mCol[c] += inM.mCol[c]; - - return *this; -} - -void Mat44::StoreFloat4x4(Float4 *outV) const -{ - for (int c = 0; c < 4; ++c) - mCol[c].StoreFloat4(outV + c); -} - -Mat44 Mat44::Transposed() const -{ -#if defined(JPH_USE_SSE) - __m128 tmp1 = _mm_shuffle_ps(mCol[0].mValue, mCol[1].mValue, _MM_SHUFFLE(1, 0, 1, 0)); - __m128 tmp3 = _mm_shuffle_ps(mCol[0].mValue, mCol[1].mValue, _MM_SHUFFLE(3, 2, 3, 2)); - __m128 tmp2 = _mm_shuffle_ps(mCol[2].mValue, mCol[3].mValue, _MM_SHUFFLE(1, 0, 1, 0)); - __m128 tmp4 = _mm_shuffle_ps(mCol[2].mValue, mCol[3].mValue, _MM_SHUFFLE(3, 2, 3, 2)); - - Mat44 result; - result.mCol[0].mValue = _mm_shuffle_ps(tmp1, tmp2, _MM_SHUFFLE(2, 0, 2, 0)); - result.mCol[1].mValue = _mm_shuffle_ps(tmp1, tmp2, _MM_SHUFFLE(3, 1, 3, 1)); - result.mCol[2].mValue = _mm_shuffle_ps(tmp3, tmp4, _MM_SHUFFLE(2, 0, 2, 0)); - result.mCol[3].mValue = _mm_shuffle_ps(tmp3, tmp4, _MM_SHUFFLE(3, 1, 3, 1)); - return result; -#elif defined(JPH_USE_NEON) - float32x4x2_t tmp1 = vzipq_f32(mCol[0].mValue, mCol[2].mValue); - float32x4x2_t tmp2 = vzipq_f32(mCol[1].mValue, mCol[3].mValue); - float32x4x2_t tmp3 = vzipq_f32(tmp1.val[0], tmp2.val[0]); - float32x4x2_t tmp4 = vzipq_f32(tmp1.val[1], tmp2.val[1]); - - Mat44 result; - result.mCol[0].mValue = tmp3.val[0]; - result.mCol[1].mValue = tmp3.val[1]; - result.mCol[2].mValue = tmp4.val[0]; - result.mCol[3].mValue = tmp4.val[1]; - return result; -#else - Mat44 result; - for (int c = 0; c < 4; ++c) - for (int r = 0; r < 4; ++r) - result.mCol[r].mF32[c] = mCol[c].mF32[r]; - return result; -#endif -} - -Mat44 Mat44::Transposed3x3() const -{ -#if defined(JPH_USE_SSE) - __m128 zero = _mm_setzero_ps(); - __m128 tmp1 = _mm_shuffle_ps(mCol[0].mValue, mCol[1].mValue, _MM_SHUFFLE(1, 0, 1, 0)); - __m128 tmp3 = _mm_shuffle_ps(mCol[0].mValue, mCol[1].mValue, _MM_SHUFFLE(3, 2, 3, 2)); - __m128 tmp2 = _mm_shuffle_ps(mCol[2].mValue, zero, _MM_SHUFFLE(1, 0, 1, 0)); - __m128 tmp4 = _mm_shuffle_ps(mCol[2].mValue, zero, _MM_SHUFFLE(3, 2, 3, 2)); - - Mat44 result; - result.mCol[0].mValue = _mm_shuffle_ps(tmp1, tmp2, _MM_SHUFFLE(2, 0, 2, 0)); - result.mCol[1].mValue = _mm_shuffle_ps(tmp1, tmp2, _MM_SHUFFLE(3, 1, 3, 1)); - result.mCol[2].mValue = _mm_shuffle_ps(tmp3, tmp4, _MM_SHUFFLE(2, 0, 2, 0)); -#elif defined(JPH_USE_NEON) - float32x4x2_t tmp1 = vzipq_f32(mCol[0].mValue, mCol[2].mValue); - float32x4x2_t tmp2 = vzipq_f32(mCol[1].mValue, vdupq_n_f32(0)); - float32x4x2_t tmp3 = vzipq_f32(tmp1.val[0], tmp2.val[0]); - float32x4x2_t tmp4 = vzipq_f32(tmp1.val[1], tmp2.val[1]); - - Mat44 result; - result.mCol[0].mValue = tmp3.val[0]; - result.mCol[1].mValue = tmp3.val[1]; - result.mCol[2].mValue = tmp4.val[0]; -#else - Mat44 result; - for (int c = 0; c < 3; ++c) - { - for (int r = 0; r < 3; ++r) - result.mCol[c].mF32[r] = mCol[r].mF32[c]; - result.mCol[c].mF32[3] = 0; - } -#endif - result.mCol[3] = Vec4(0, 0, 0, 1); - return result; -} - -Mat44 Mat44::Inversed() const -{ -#if defined(JPH_USE_SSE) - // Algorithm from: http://download.intel.com/design/PentiumIII/sml/24504301.pdf - // Streaming SIMD Extensions - Inverse of 4x4 Matrix - // Adapted to load data using _mm_shuffle_ps instead of loading from memory - // Replaced _mm_rcp_ps with _mm_div_ps for better accuracy - - __m128 tmp1 = _mm_shuffle_ps(mCol[0].mValue, mCol[1].mValue, _MM_SHUFFLE(1, 0, 1, 0)); - __m128 row1 = _mm_shuffle_ps(mCol[2].mValue, mCol[3].mValue, _MM_SHUFFLE(1, 0, 1, 0)); - __m128 row0 = _mm_shuffle_ps(tmp1, row1, _MM_SHUFFLE(2, 0, 2, 0)); - row1 = _mm_shuffle_ps(row1, tmp1, _MM_SHUFFLE(3, 1, 3, 1)); - tmp1 = _mm_shuffle_ps(mCol[0].mValue, mCol[1].mValue, _MM_SHUFFLE(3, 2, 3, 2)); - __m128 row3 = _mm_shuffle_ps(mCol[2].mValue, mCol[3].mValue, _MM_SHUFFLE(3, 2, 3, 2)); - __m128 row2 = _mm_shuffle_ps(tmp1, row3, _MM_SHUFFLE(2, 0, 2, 0)); - row3 = _mm_shuffle_ps(row3, tmp1, _MM_SHUFFLE(3, 1, 3, 1)); - - tmp1 = _mm_mul_ps(row2, row3); - tmp1 = _mm_shuffle_ps(tmp1, tmp1, _MM_SHUFFLE(2, 3, 0, 1)); - __m128 minor0 = _mm_mul_ps(row1, tmp1); - __m128 minor1 = _mm_mul_ps(row0, tmp1); - tmp1 = _mm_shuffle_ps(tmp1, tmp1, _MM_SHUFFLE(1, 0, 3, 2)); - minor0 = _mm_sub_ps(_mm_mul_ps(row1, tmp1), minor0); - minor1 = _mm_sub_ps(_mm_mul_ps(row0, tmp1), minor1); - minor1 = _mm_shuffle_ps(minor1, minor1, _MM_SHUFFLE(1, 0, 3, 2)); - - tmp1 = _mm_mul_ps(row1, row2); - tmp1 = _mm_shuffle_ps(tmp1, tmp1, _MM_SHUFFLE(2, 3, 0, 1)); - minor0 = _mm_add_ps(_mm_mul_ps(row3, tmp1), minor0); - __m128 minor3 = _mm_mul_ps(row0, tmp1); - tmp1 = _mm_shuffle_ps(tmp1, tmp1, _MM_SHUFFLE(1, 0, 3, 2)); - minor0 = _mm_sub_ps(minor0, _mm_mul_ps(row3, tmp1)); - minor3 = _mm_sub_ps(_mm_mul_ps(row0, tmp1), minor3); - minor3 = _mm_shuffle_ps(minor3, minor3, _MM_SHUFFLE(1, 0, 3, 2)); - - tmp1 = _mm_mul_ps(_mm_shuffle_ps(row1, row1, _MM_SHUFFLE(1, 0, 3, 2)), row3); - tmp1 = _mm_shuffle_ps(tmp1, tmp1, _MM_SHUFFLE(2, 3, 0, 1)); - row2 = _mm_shuffle_ps(row2, row2, _MM_SHUFFLE(1, 0, 3, 2)); - minor0 = _mm_add_ps(_mm_mul_ps(row2, tmp1), minor0); - __m128 minor2 = _mm_mul_ps(row0, tmp1); - tmp1 = _mm_shuffle_ps(tmp1, tmp1, _MM_SHUFFLE(1, 0, 3, 2)); - minor0 = _mm_sub_ps(minor0, _mm_mul_ps(row2, tmp1)); - minor2 = _mm_sub_ps(_mm_mul_ps(row0, tmp1), minor2); - minor2 = _mm_shuffle_ps(minor2, minor2, _MM_SHUFFLE(1, 0, 3, 2)); - - tmp1 = _mm_mul_ps(row0, row1); - tmp1 = _mm_shuffle_ps(tmp1, tmp1, _MM_SHUFFLE(2, 3, 0, 1)); - minor2 = _mm_add_ps(_mm_mul_ps(row3, tmp1), minor2); - minor3 = _mm_sub_ps(_mm_mul_ps(row2, tmp1), minor3); - tmp1 = _mm_shuffle_ps(tmp1, tmp1, _MM_SHUFFLE(1, 0, 3, 2)); - minor2 = _mm_sub_ps(_mm_mul_ps(row3, tmp1), minor2); - minor3 = _mm_sub_ps(minor3, _mm_mul_ps(row2, tmp1)); - - tmp1 = _mm_mul_ps(row0, row3); - tmp1 = _mm_shuffle_ps(tmp1, tmp1, _MM_SHUFFLE(2, 3, 0, 1)); - minor1 = _mm_sub_ps(minor1, _mm_mul_ps(row2, tmp1)); - minor2 = _mm_add_ps(_mm_mul_ps(row1, tmp1), minor2); - tmp1 = _mm_shuffle_ps(tmp1, tmp1, _MM_SHUFFLE(1, 0, 3, 2)); - minor1 = _mm_add_ps(_mm_mul_ps(row2, tmp1), minor1); - minor2 = _mm_sub_ps(minor2, _mm_mul_ps(row1, tmp1)); - - tmp1 = _mm_mul_ps(row0, row2); - tmp1 = _mm_shuffle_ps(tmp1, tmp1, _MM_SHUFFLE(2, 3, 0, 1)); - minor1 = _mm_add_ps(_mm_mul_ps(row3, tmp1), minor1); - minor3 = _mm_sub_ps(minor3, _mm_mul_ps(row1, tmp1)); - tmp1 = _mm_shuffle_ps(tmp1, tmp1, _MM_SHUFFLE(1, 0, 3, 2)); - minor1 = _mm_sub_ps(minor1, _mm_mul_ps(row3, tmp1)); - minor3 = _mm_add_ps(_mm_mul_ps(row1, tmp1), minor3); - - __m128 det = _mm_mul_ps(row0, minor0); - det = _mm_add_ps(_mm_shuffle_ps(det, det, _MM_SHUFFLE(2, 3, 0, 1)), det); // Original code did (x + z) + (y + w), changed to (x + y) + (z + w) to match the ARM code below and make the result cross platform deterministic - det = _mm_add_ss(_mm_shuffle_ps(det, det, _MM_SHUFFLE(1, 0, 3, 2)), det); - det = _mm_div_ss(_mm_set_ss(1.0f), det); - det = _mm_shuffle_ps(det, det, _MM_SHUFFLE(0, 0, 0, 0)); - - Mat44 result; - result.mCol[0].mValue = _mm_mul_ps(det, minor0); - result.mCol[1].mValue = _mm_mul_ps(det, minor1); - result.mCol[2].mValue = _mm_mul_ps(det, minor2); - result.mCol[3].mValue = _mm_mul_ps(det, minor3); - return result; -#elif defined(JPH_USE_NEON) - // Adapted from the SSE version, there's surprising few articles about efficient ways of calculating an inverse for ARM on the internet - Type tmp1 = JPH_NEON_SHUFFLE_F32x4(mCol[0].mValue, mCol[1].mValue, 0, 1, 4, 5); - Type row1 = JPH_NEON_SHUFFLE_F32x4(mCol[2].mValue, mCol[3].mValue, 0, 1, 4, 5); - Type row0 = JPH_NEON_SHUFFLE_F32x4(tmp1, row1, 0, 2, 4, 6); - row1 = JPH_NEON_SHUFFLE_F32x4(row1, tmp1, 1, 3, 5, 7); - tmp1 = JPH_NEON_SHUFFLE_F32x4(mCol[0].mValue, mCol[1].mValue, 2, 3, 6, 7); - Type row3 = JPH_NEON_SHUFFLE_F32x4(mCol[2].mValue, mCol[3].mValue, 2, 3, 6, 7); - Type row2 = JPH_NEON_SHUFFLE_F32x4(tmp1, row3, 0, 2, 4, 6); - row3 = JPH_NEON_SHUFFLE_F32x4(row3, tmp1, 1, 3, 5, 7); - - tmp1 = vmulq_f32(row2, row3); - tmp1 = JPH_NEON_SHUFFLE_F32x4(tmp1, tmp1, 1, 0, 3, 2); - Type minor0 = vmulq_f32(row1, tmp1); - Type minor1 = vmulq_f32(row0, tmp1); - tmp1 = JPH_NEON_SHUFFLE_F32x4(tmp1, tmp1, 2, 3, 0, 1); - minor0 = vsubq_f32(vmulq_f32(row1, tmp1), minor0); - minor1 = vsubq_f32(vmulq_f32(row0, tmp1), minor1); - minor1 = JPH_NEON_SHUFFLE_F32x4(minor1, minor1, 2, 3, 0, 1); - - tmp1 = vmulq_f32(row1, row2); - tmp1 = JPH_NEON_SHUFFLE_F32x4(tmp1, tmp1, 1, 0, 3, 2); - minor0 = vaddq_f32(vmulq_f32(row3, tmp1), minor0); - Type minor3 = vmulq_f32(row0, tmp1); - tmp1 = JPH_NEON_SHUFFLE_F32x4(tmp1, tmp1, 2, 3, 0, 1); - minor0 = vsubq_f32(minor0, vmulq_f32(row3, tmp1)); - minor3 = vsubq_f32(vmulq_f32(row0, tmp1), minor3); - minor3 = JPH_NEON_SHUFFLE_F32x4(minor3, minor3, 2, 3, 0, 1); - - tmp1 = JPH_NEON_SHUFFLE_F32x4(row1, row1, 2, 3, 0, 1); - tmp1 = vmulq_f32(tmp1, row3); - tmp1 = JPH_NEON_SHUFFLE_F32x4(tmp1, tmp1, 1, 0, 3, 2); - row2 = JPH_NEON_SHUFFLE_F32x4(row2, row2, 2, 3, 0, 1); - minor0 = vaddq_f32(vmulq_f32(row2, tmp1), minor0); - Type minor2 = vmulq_f32(row0, tmp1); - tmp1 = JPH_NEON_SHUFFLE_F32x4(tmp1, tmp1, 2, 3, 0, 1); - minor0 = vsubq_f32(minor0, vmulq_f32(row2, tmp1)); - minor2 = vsubq_f32(vmulq_f32(row0, tmp1), minor2); - minor2 = JPH_NEON_SHUFFLE_F32x4(minor2, minor2, 2, 3, 0, 1); - - tmp1 = vmulq_f32(row0, row1); - tmp1 = JPH_NEON_SHUFFLE_F32x4(tmp1, tmp1, 1, 0, 3, 2); - minor2 = vaddq_f32(vmulq_f32(row3, tmp1), minor2); - minor3 = vsubq_f32(vmulq_f32(row2, tmp1), minor3); - tmp1 = JPH_NEON_SHUFFLE_F32x4(tmp1, tmp1, 2, 3, 0, 1); - minor2 = vsubq_f32(vmulq_f32(row3, tmp1), minor2); - minor3 = vsubq_f32(minor3, vmulq_f32(row2, tmp1)); - - tmp1 = vmulq_f32(row0, row3); - tmp1 = JPH_NEON_SHUFFLE_F32x4(tmp1, tmp1, 1, 0, 3, 2); - minor1 = vsubq_f32(minor1, vmulq_f32(row2, tmp1)); - minor2 = vaddq_f32(vmulq_f32(row1, tmp1), minor2); - tmp1 = JPH_NEON_SHUFFLE_F32x4(tmp1, tmp1, 2, 3, 0, 1); - minor1 = vaddq_f32(vmulq_f32(row2, tmp1), minor1); - minor2 = vsubq_f32(minor2, vmulq_f32(row1, tmp1)); - - tmp1 = vmulq_f32(row0, row2); - tmp1 = JPH_NEON_SHUFFLE_F32x4(tmp1, tmp1, 1, 0, 3, 2); - minor1 = vaddq_f32(vmulq_f32(row3, tmp1), minor1); - minor3 = vsubq_f32(minor3, vmulq_f32(row1, tmp1)); - tmp1 = JPH_NEON_SHUFFLE_F32x4(tmp1, tmp1, 2, 3, 0, 1); - minor1 = vsubq_f32(minor1, vmulq_f32(row3, tmp1)); - minor3 = vaddq_f32(vmulq_f32(row1, tmp1), minor3); - - Type det = vmulq_f32(row0, minor0); - det = vdupq_n_f32(vaddvq_f32(det)); - det = vdivq_f32(vdupq_n_f32(1.0f), det); - - Mat44 result; - result.mCol[0].mValue = vmulq_f32(det, minor0); - result.mCol[1].mValue = vmulq_f32(det, minor1); - result.mCol[2].mValue = vmulq_f32(det, minor2); - result.mCol[3].mValue = vmulq_f32(det, minor3); - return result; -#else - float m00 = JPH_EL(0, 0), m10 = JPH_EL(1, 0), m20 = JPH_EL(2, 0), m30 = JPH_EL(3, 0); - float m01 = JPH_EL(0, 1), m11 = JPH_EL(1, 1), m21 = JPH_EL(2, 1), m31 = JPH_EL(3, 1); - float m02 = JPH_EL(0, 2), m12 = JPH_EL(1, 2), m22 = JPH_EL(2, 2), m32 = JPH_EL(3, 2); - float m03 = JPH_EL(0, 3), m13 = JPH_EL(1, 3), m23 = JPH_EL(2, 3), m33 = JPH_EL(3, 3); - - float m10211120 = m10 * m21 - m11 * m20; - float m10221220 = m10 * m22 - m12 * m20; - float m10231320 = m10 * m23 - m13 * m20; - float m10311130 = m10 * m31 - m11 * m30; - float m10321230 = m10 * m32 - m12 * m30; - float m10331330 = m10 * m33 - m13 * m30; - float m11221221 = m11 * m22 - m12 * m21; - float m11231321 = m11 * m23 - m13 * m21; - float m11321231 = m11 * m32 - m12 * m31; - float m11331331 = m11 * m33 - m13 * m31; - float m12231322 = m12 * m23 - m13 * m22; - float m12331332 = m12 * m33 - m13 * m32; - float m20312130 = m20 * m31 - m21 * m30; - float m20322230 = m20 * m32 - m22 * m30; - float m20332330 = m20 * m33 - m23 * m30; - float m21322231 = m21 * m32 - m22 * m31; - float m21332331 = m21 * m33 - m23 * m31; - float m22332332 = m22 * m33 - m23 * m32; - - Vec4 col0(m11 * m22332332 - m12 * m21332331 + m13 * m21322231, -m10 * m22332332 + m12 * m20332330 - m13 * m20322230, m10 * m21332331 - m11 * m20332330 + m13 * m20312130, -m10 * m21322231 + m11 * m20322230 - m12 * m20312130); - Vec4 col1(-m01 * m22332332 + m02 * m21332331 - m03 * m21322231, m00 * m22332332 - m02 * m20332330 + m03 * m20322230, -m00 * m21332331 + m01 * m20332330 - m03 * m20312130, m00 * m21322231 - m01 * m20322230 + m02 * m20312130); - Vec4 col2(m01 * m12331332 - m02 * m11331331 + m03 * m11321231, -m00 * m12331332 + m02 * m10331330 - m03 * m10321230, m00 * m11331331 - m01 * m10331330 + m03 * m10311130, -m00 * m11321231 + m01 * m10321230 - m02 * m10311130); - Vec4 col3(-m01 * m12231322 + m02 * m11231321 - m03 * m11221221, m00 * m12231322 - m02 * m10231320 + m03 * m10221220, -m00 * m11231321 + m01 * m10231320 - m03 * m10211120, m00 * m11221221 - m01 * m10221220 + m02 * m10211120); - - float det = m00 * col0.mF32[0] + m01 * col0.mF32[1] + m02 * col0.mF32[2] + m03 * col0.mF32[3]; - - return Mat44(col0 / det, col1 / det, col2 / det, col3 / det); -#endif -} - -Mat44 Mat44::InversedRotationTranslation() const -{ - Mat44 m = Transposed3x3(); - m.SetTranslation(-m.Multiply3x3(GetTranslation())); - return m; -} - -float Mat44::GetDeterminant3x3() const -{ - return GetAxisX().Dot(GetAxisY().Cross(GetAxisZ())); -} - -Mat44 Mat44::Adjointed3x3() const -{ - return Mat44( - Vec4(JPH_EL(1, 1), JPH_EL(1, 2), JPH_EL(1, 0), 0) * Vec4(JPH_EL(2, 2), JPH_EL(2, 0), JPH_EL(2, 1), 0) - - Vec4(JPH_EL(1, 2), JPH_EL(1, 0), JPH_EL(1, 1), 0) * Vec4(JPH_EL(2, 1), JPH_EL(2, 2), JPH_EL(2, 0), 0), - Vec4(JPH_EL(0, 2), JPH_EL(0, 0), JPH_EL(0, 1), 0) * Vec4(JPH_EL(2, 1), JPH_EL(2, 2), JPH_EL(2, 0), 0) - - Vec4(JPH_EL(0, 1), JPH_EL(0, 2), JPH_EL(0, 0), 0) * Vec4(JPH_EL(2, 2), JPH_EL(2, 0), JPH_EL(2, 1), 0), - Vec4(JPH_EL(0, 1), JPH_EL(0, 2), JPH_EL(0, 0), 0) * Vec4(JPH_EL(1, 2), JPH_EL(1, 0), JPH_EL(1, 1), 0) - - Vec4(JPH_EL(0, 2), JPH_EL(0, 0), JPH_EL(0, 1), 0) * Vec4(JPH_EL(1, 1), JPH_EL(1, 2), JPH_EL(1, 0), 0), - Vec4(0, 0, 0, 1)); -} - -Mat44 Mat44::Inversed3x3() const -{ - float det = GetDeterminant3x3(); - - return Mat44( - (Vec4(JPH_EL(1, 1), JPH_EL(1, 2), JPH_EL(1, 0), 0) * Vec4(JPH_EL(2, 2), JPH_EL(2, 0), JPH_EL(2, 1), 0) - - Vec4(JPH_EL(1, 2), JPH_EL(1, 0), JPH_EL(1, 1), 0) * Vec4(JPH_EL(2, 1), JPH_EL(2, 2), JPH_EL(2, 0), 0)) / det, - (Vec4(JPH_EL(0, 2), JPH_EL(0, 0), JPH_EL(0, 1), 0) * Vec4(JPH_EL(2, 1), JPH_EL(2, 2), JPH_EL(2, 0), 0) - - Vec4(JPH_EL(0, 1), JPH_EL(0, 2), JPH_EL(0, 0), 0) * Vec4(JPH_EL(2, 2), JPH_EL(2, 0), JPH_EL(2, 1), 0)) / det, - (Vec4(JPH_EL(0, 1), JPH_EL(0, 2), JPH_EL(0, 0), 0) * Vec4(JPH_EL(1, 2), JPH_EL(1, 0), JPH_EL(1, 1), 0) - - Vec4(JPH_EL(0, 2), JPH_EL(0, 0), JPH_EL(0, 1), 0) * Vec4(JPH_EL(1, 1), JPH_EL(1, 2), JPH_EL(1, 0), 0)) / det, - Vec4(0, 0, 0, 1)); -} - -bool Mat44::SetInversed3x3(Mat44Arg inM) -{ - float det = inM.GetDeterminant3x3(); - - // If the determinant is zero the matrix is singular and we return false - if (det == 0.0f) - return false; - - // Finish calculating the inverse - *this = inM.Adjointed3x3(); - mCol[0] /= det; - mCol[1] /= det; - mCol[2] /= det; - return true; -} - -Quat Mat44::GetQuaternion() const -{ - float tr = mCol[0].mF32[0] + mCol[1].mF32[1] + mCol[2].mF32[2]; - - if (tr >= 0.0f) - { - float s = sqrt(tr + 1.0f); - float is = 0.5f / s; - return Quat( - (mCol[1].mF32[2] - mCol[2].mF32[1]) * is, - (mCol[2].mF32[0] - mCol[0].mF32[2]) * is, - (mCol[0].mF32[1] - mCol[1].mF32[0]) * is, - 0.5f * s); - } - else - { - int i = 0; - if (mCol[1].mF32[1] > mCol[0].mF32[0]) i = 1; - if (mCol[2].mF32[2] > mCol[i].mF32[i]) i = 2; - - if (i == 0) - { - float s = sqrt(mCol[0].mF32[0] - (mCol[1].mF32[1] + mCol[2].mF32[2]) + 1); - float is = 0.5f / s; - return Quat( - 0.5f * s, - (mCol[1].mF32[0] + mCol[0].mF32[1]) * is, - (mCol[0].mF32[2] + mCol[2].mF32[0]) * is, - (mCol[1].mF32[2] - mCol[2].mF32[1]) * is); - } - else if (i == 1) - { - float s = sqrt(mCol[1].mF32[1] - (mCol[2].mF32[2] + mCol[0].mF32[0]) + 1); - float is = 0.5f / s; - return Quat( - (mCol[1].mF32[0] + mCol[0].mF32[1]) * is, - 0.5f * s, - (mCol[2].mF32[1] + mCol[1].mF32[2]) * is, - (mCol[2].mF32[0] - mCol[0].mF32[2]) * is); - } - else - { - JPH_ASSERT(i == 2); - - float s = sqrt(mCol[2].mF32[2] - (mCol[0].mF32[0] + mCol[1].mF32[1]) + 1); - float is = 0.5f / s; - return Quat( - (mCol[0].mF32[2] + mCol[2].mF32[0]) * is, - (mCol[2].mF32[1] + mCol[1].mF32[2]) * is, - 0.5f * s, - (mCol[0].mF32[1] - mCol[1].mF32[0]) * is); - } - } -} - -Mat44 Mat44::sQuatLeftMultiply(QuatArg inQ) -{ - return Mat44( - Vec4(1, 1, -1, -1) * inQ.mValue.Swizzle(), - Vec4(-1, 1, 1, -1) * inQ.mValue.Swizzle(), - Vec4(1, -1, 1, -1) * inQ.mValue.Swizzle(), - inQ.mValue); -} - -Mat44 Mat44::sQuatRightMultiply(QuatArg inQ) -{ - return Mat44( - Vec4(1, -1, 1, -1) * inQ.mValue.Swizzle(), - Vec4(1, 1, -1, -1) * inQ.mValue.Swizzle(), - Vec4(-1, 1, 1, -1) * inQ.mValue.Swizzle(), - inQ.mValue); -} - -Mat44 Mat44::GetRotation() const -{ - JPH_ASSERT(mCol[0][3] == 0.0f); - JPH_ASSERT(mCol[1][3] == 0.0f); - JPH_ASSERT(mCol[2][3] == 0.0f); - - return Mat44(mCol[0], mCol[1], mCol[2], Vec4(0, 0, 0, 1)); -} - -Mat44 Mat44::GetRotationSafe() const -{ -#if defined(JPH_USE_AVX512) - return Mat44(_mm_maskz_mov_ps(0b0111, mCol[0].mValue), - _mm_maskz_mov_ps(0b0111, mCol[1].mValue), - _mm_maskz_mov_ps(0b0111, mCol[2].mValue), - Vec4(0, 0, 0, 1)); -#elif defined(JPH_USE_SSE4_1) - __m128 zero = _mm_setzero_ps(); - return Mat44(_mm_blend_ps(mCol[0].mValue, zero, 8), - _mm_blend_ps(mCol[1].mValue, zero, 8), - _mm_blend_ps(mCol[2].mValue, zero, 8), - Vec4(0, 0, 0, 1)); -#elif defined(JPH_USE_NEON) - return Mat44(vsetq_lane_f32(0, mCol[0].mValue, 3), - vsetq_lane_f32(0, mCol[1].mValue, 3), - vsetq_lane_f32(0, mCol[2].mValue, 3), - Vec4(0, 0, 0, 1)); -#else - return Mat44(Vec4(mCol[0].mF32[0], mCol[0].mF32[1], mCol[0].mF32[2], 0), - Vec4(mCol[1].mF32[0], mCol[1].mF32[1], mCol[1].mF32[2], 0), - Vec4(mCol[2].mF32[0], mCol[2].mF32[1], mCol[2].mF32[2], 0), - Vec4(0, 0, 0, 1)); -#endif -} - -void Mat44::SetRotation(Mat44Arg inRotation) -{ - mCol[0] = inRotation.mCol[0]; - mCol[1] = inRotation.mCol[1]; - mCol[2] = inRotation.mCol[2]; -} - -Mat44 Mat44::PreTranslated(Vec3Arg inTranslation) const -{ - return Mat44(mCol[0], mCol[1], mCol[2], Vec4(GetTranslation() + Multiply3x3(inTranslation), 1)); -} - -Mat44 Mat44::PostTranslated(Vec3Arg inTranslation) const -{ - return Mat44(mCol[0], mCol[1], mCol[2], Vec4(GetTranslation() + inTranslation, 1)); -} - -Mat44 Mat44::PreScaled(Vec3Arg inScale) const -{ - return Mat44(inScale.GetX() * mCol[0], inScale.GetY() * mCol[1], inScale.GetZ() * mCol[2], mCol[3]); -} - -Mat44 Mat44::PostScaled(Vec3Arg inScale) const -{ - Vec4 scale(inScale, 1); - return Mat44(scale * mCol[0], scale * mCol[1], scale * mCol[2], scale * mCol[3]); -} - -Mat44 Mat44::Decompose(Vec3 &outScale) const -{ - // Start the modified Gram-Schmidt algorithm - // X axis will just be normalized - Vec3 x = GetAxisX(); - - // Make Y axis perpendicular to X - Vec3 y = GetAxisY(); - float x_dot_x = x.LengthSq(); - y -= (x.Dot(y) / x_dot_x) * x; - - // Make Z axis perpendicular to X - Vec3 z = GetAxisZ(); - z -= (x.Dot(z) / x_dot_x) * x; - - // Make Z axis perpendicular to Y - float y_dot_y = y.LengthSq(); - z -= (y.Dot(z) / y_dot_y) * y; - - // Determine the scale - float z_dot_z = z.LengthSq(); - outScale = Vec3(x_dot_x, y_dot_y, z_dot_z).Sqrt(); - - // If the resulting x, y and z vectors don't form a right handed matrix, flip the z axis. - if (x.Cross(y).Dot(z) < 0.0f) - outScale.SetZ(-outScale.GetZ()); - - // Determine the rotation and translation - return Mat44(Vec4(x / outScale.GetX(), 0), Vec4(y / outScale.GetY(), 0), Vec4(z / outScale.GetZ(), 0), GetColumn4(3)); -} - -#undef JPH_EL - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Math.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Math.h deleted file mode 100644 index fe787f21086..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Math.h +++ /dev/null @@ -1,203 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -JPH_NAMESPACE_BEGIN - -/// The constant \f$\pi\f$ -static constexpr float JPH_PI = 3.14159265358979323846f; - -/// Convert a value from degrees to radians -constexpr float DegreesToRadians(float inV) -{ - return inV * (JPH_PI / 180.0f); -} - -/// Convert a value from radians to degrees -constexpr float RadiansToDegrees(float inV) -{ - return inV * (180.0f / JPH_PI); -} - -/// Convert angle in radians to the range \f$[-\pi, \pi]\f$ -inline float CenterAngleAroundZero(float inV) -{ - if (inV < -JPH_PI) - { - do - inV += 2.0f * JPH_PI; - while (inV < -JPH_PI); - } - else if (inV > JPH_PI) - { - do - inV -= 2.0f * JPH_PI; - while (inV > JPH_PI); - } - JPH_ASSERT(inV >= -JPH_PI && inV <= JPH_PI); - return inV; -} - -/// Clamp a value between two values -template -constexpr T Clamp(T inV, T inMin, T inMax) -{ - return min(max(inV, inMin), inMax); -} - -/// Square a value -template -constexpr T Square(T inV) -{ - return inV * inV; -} - -/// Returns \f$inV^3\f$. -template -constexpr T Cubed(T inV) -{ - return inV * inV * inV; -} - -/// Get the sign of a value -template -constexpr T Sign(T inV) -{ - return inV < 0? T(-1) : T(1); -} - -/// Check if inV is a power of 2 -template -constexpr bool IsPowerOf2(T inV) -{ - return (inV & (inV - 1)) == 0; -} - -/// Align inV up to the next inAlignment bytes -template -inline T AlignUp(T inV, uint64 inAlignment) -{ - JPH_ASSERT(IsPowerOf2(inAlignment)); - return T((uint64(inV) + inAlignment - 1) & ~(inAlignment - 1)); -} - -/// Check if inV is inAlignment aligned -template -inline bool IsAligned(T inV, uint64 inAlignment) -{ - JPH_ASSERT(IsPowerOf2(inAlignment)); - return (uint64(inV) & (inAlignment - 1)) == 0; -} - -/// Compute number of trailing zero bits (how many low bits are zero) -inline uint CountTrailingZeros(uint32 inValue) -{ -#if defined(JPH_CPU_X86) || defined(JPH_CPU_WASM) - #if defined(JPH_USE_TZCNT) - return _tzcnt_u32(inValue); - #elif defined(JPH_COMPILER_MSVC) - if (inValue == 0) - return 32; - unsigned long result; - _BitScanForward(&result, inValue); - return result; - #else - if (inValue == 0) - return 32; - return __builtin_ctz(inValue); - #endif -#elif defined(JPH_CPU_ARM) - #if defined(JPH_COMPILER_MSVC) - if (inValue == 0) - return 32; - unsigned long result; - _BitScanForward(&result, inValue); - return result; - #else - return __builtin_clz(__builtin_bitreverse32(inValue)); - #endif -#elif defined(JPH_CPU_E2K) - return inValue ? __builtin_ctz(inValue) : 32; -#else - #error Undefined -#endif -} - -/// Compute the number of leading zero bits (how many high bits are zero) -inline uint CountLeadingZeros(uint32 inValue) -{ -#if defined(JPH_CPU_X86) || defined(JPH_CPU_WASM) - #if defined(JPH_USE_LZCNT) - return _lzcnt_u32(inValue); - #elif defined(JPH_COMPILER_MSVC) - if (inValue == 0) - return 32; - unsigned long result; - _BitScanReverse(&result, inValue); - return 31 - result; - #else - if (inValue == 0) - return 32; - return __builtin_clz(inValue); - #endif -#elif defined(JPH_CPU_ARM) - #if defined(JPH_COMPILER_MSVC) - return _CountLeadingZeros(inValue); - #else - return __builtin_clz(inValue); - #endif -#elif defined(JPH_CPU_E2K) - return inValue ? __builtin_clz(inValue) : 32; -#else - #error Undefined -#endif -} - -/// Count the number of 1 bits in a value -inline uint CountBits(uint32 inValue) -{ -#if defined(JPH_COMPILER_CLANG) || defined(JPH_COMPILER_GCC) - return __builtin_popcount(inValue); -#elif defined(JPH_COMPILER_MSVC) - #if defined(JPH_USE_SSE4_2) - return _mm_popcnt_u32(inValue); - #elif defined(JPH_USE_NEON) && (_MSC_VER >= 1930) // _CountOneBits not available on MSVC2019 - return _CountOneBits(inValue); - #else - inValue = inValue - ((inValue >> 1) & 0x55555555); - inValue = (inValue & 0x33333333) + ((inValue >> 2) & 0x33333333); - inValue = (inValue + (inValue >> 4)) & 0x0F0F0F0F; - return (inValue * 0x01010101) >> 24; - #endif -#else - #error Undefined -#endif -} - -/// Get the next higher power of 2 of a value, or the value itself if the value is already a power of 2 -inline uint32 GetNextPowerOf2(uint32 inValue) -{ - return inValue <= 1? uint32(1) : uint32(1) << (32 - CountLeadingZeros(inValue - 1)); -} - -// Simple implementation of C++20 std::bit_cast (unfortunately not constexpr) -template -JPH_INLINE To BitCast(const From &inValue) -{ - static_assert(std::is_trivially_constructible_v); - static_assert(sizeof(From) == sizeof(To)); - - union FromTo - { - To mTo; - From mFrom; - }; - - FromTo convert; - convert.mFrom = inValue; - return convert.mTo; -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/MathTypes.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/MathTypes.h deleted file mode 100644 index 8d019ae427f..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/MathTypes.h +++ /dev/null @@ -1,34 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -JPH_NAMESPACE_BEGIN - -class Vec3; -class DVec3; -class Vec4; -class UVec4; -class Vec8; -class UVec8; -class Quat; -class Mat44; -class DMat44; - -// Types to use for passing arguments to functions -using Vec3Arg = const Vec3; -#ifdef JPH_USE_AVX - using DVec3Arg = const DVec3; -#else - using DVec3Arg = const DVec3 &; -#endif -using Vec4Arg = const Vec4; -using UVec4Arg = const UVec4; -using Vec8Arg = const Vec8; -using UVec8Arg = const UVec8; -using QuatArg = const Quat; -using Mat44Arg = const Mat44 &; -using DMat44Arg = const DMat44 &; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Matrix.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Matrix.h deleted file mode 100644 index 031665bbe71..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Matrix.h +++ /dev/null @@ -1,259 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include - -JPH_NAMESPACE_BEGIN - -/// Templatized matrix class -template -class [[nodiscard]] Matrix -{ -public: - /// Constructor - inline Matrix() = default; - inline Matrix(const Matrix &inM2) { *this = inM2; } - - /// Dimensions - inline uint GetRows() const { return Rows; } - inline uint GetCols() const { return Cols; } - - /// Zero matrix - inline void SetZero() - { - for (uint c = 0; c < Cols; ++c) - mCol[c].SetZero(); - } - - inline static Matrix sZero() { Matrix m; m.SetZero(); return m; } - - /// Check if this matrix consists of all zeros - inline bool IsZero() const - { - for (uint c = 0; c < Cols; ++c) - if (!mCol[c].IsZero()) - return false; - - return true; - } - - /// Identity matrix - inline void SetIdentity() - { - // Clear matrix - SetZero(); - - // Set diagonal to 1 - for (uint rc = 0, min_rc = min(Rows, Cols); rc < min_rc; ++rc) - mCol[rc].mF32[rc] = 1.0f; - } - - inline static Matrix sIdentity() { Matrix m; m.SetIdentity(); return m; } - - /// Check if this matrix is identity - bool IsIdentity() const { return *this == sIdentity(); } - - /// Diagonal matrix - inline void SetDiagonal(const Vector &inV) - { - // Clear matrix - SetZero(); - - // Set diagonal - for (uint rc = 0, min_rc = min(Rows, Cols); rc < min_rc; ++rc) - mCol[rc].mF32[rc] = inV[rc]; - } - - inline static Matrix sDiagonal(const Vector &inV) - { - Matrix m; - m.SetDiagonal(inV); - return m; - } - - /// Copy a (part) of another matrix into this matrix - template - void CopyPart(const OtherMatrix &inM, uint inSourceRow, uint inSourceCol, uint inNumRows, uint inNumCols, uint inDestRow, uint inDestCol) - { - for (uint c = 0; c < inNumCols; ++c) - for (uint r = 0; r < inNumRows; ++r) - mCol[inDestCol + c].mF32[inDestRow + r] = inM(inSourceRow + r, inSourceCol + c); - } - - /// Get float component by element index - inline float operator () (uint inRow, uint inColumn) const - { - JPH_ASSERT(inRow < Rows); - JPH_ASSERT(inColumn < Cols); - return mCol[inColumn].mF32[inRow]; - } - - inline float & operator () (uint inRow, uint inColumn) - { - JPH_ASSERT(inRow < Rows); - JPH_ASSERT(inColumn < Cols); - return mCol[inColumn].mF32[inRow]; - } - - /// Comparison - inline bool operator == (const Matrix &inM2) const - { - for (uint c = 0; c < Cols; ++c) - if (mCol[c] != inM2.mCol[c]) - return false; - return true; - } - - inline bool operator != (const Matrix &inM2) const - { - for (uint c = 0; c < Cols; ++c) - if (mCol[c] != inM2.mCol[c]) - return true; - return false; - } - - /// Assignment - inline Matrix & operator = (const Matrix &inM2) - { - for (uint c = 0; c < Cols; ++c) - mCol[c] = inM2.mCol[c]; - return *this; - } - - /// Multiply matrix by matrix - template - inline Matrix operator * (const Matrix &inM) const - { - Matrix m; - for (uint c = 0; c < OtherCols; ++c) - for (uint r = 0; r < Rows; ++r) - { - float dot = 0.0f; - for (uint i = 0; i < Cols; ++i) - dot += mCol[i].mF32[r] * inM.mCol[c].mF32[i]; - m.mCol[c].mF32[r] = dot; - } - return m; - } - - /// Multiply vector by matrix - inline Vector operator * (const Vector &inV) const - { - Vector v; - for (uint r = 0; r < Rows; ++r) - { - float dot = 0.0f; - for (uint c = 0; c < Cols; ++c) - dot += mCol[c].mF32[r] * inV.mF32[c]; - v.mF32[r] = dot; - } - return v; - } - - /// Multiply matrix with float - inline Matrix operator * (float inV) const - { - Matrix m; - for (uint c = 0; c < Cols; ++c) - m.mCol[c] = mCol[c] * inV; - return m; - } - - inline friend Matrix operator * (float inV, const Matrix &inM) - { - return inM * inV; - } - - /// Per element addition of matrix - inline Matrix operator + (const Matrix &inM) const - { - Matrix m; - for (uint c = 0; c < Cols; ++c) - m.mCol[c] = mCol[c] + inM.mCol[c]; - return m; - } - - /// Per element subtraction of matrix - inline Matrix operator - (const Matrix &inM) const - { - Matrix m; - for (uint c = 0; c < Cols; ++c) - m.mCol[c] = mCol[c] - inM.mCol[c]; - return m; - } - - /// Transpose matrix - inline Matrix Transposed() const - { - Matrix m; - for (uint r = 0; r < Rows; ++r) - for (uint c = 0; c < Cols; ++c) - m.mCol[r].mF32[c] = mCol[c].mF32[r]; - return m; - } - - /// Inverse matrix - bool SetInversed(const Matrix &inM) - { - if constexpr (Rows != Cols) JPH_ASSERT(false); - Matrix copy(inM); - SetIdentity(); - return GaussianElimination(copy, *this); - } - - inline Matrix Inversed() const - { - Matrix m; - m.SetInversed(*this); - return m; - } - - /// To String - friend ostream & operator << (ostream &inStream, const Matrix &inM) - { - for (uint i = 0; i < Cols - 1; ++i) - inStream << inM.mCol[i] << ", "; - inStream << inM.mCol[Cols - 1]; - return inStream; - } - - /// Column access - const Vector & GetColumn(int inIdx) const { return mCol[inIdx]; } - Vector & GetColumn(int inIdx) { return mCol[inIdx]; } - - Vector mCol[Cols]; ///< Column -}; - -// The template specialization doesn't sit well with Doxygen -#ifndef JPH_PLATFORM_DOXYGEN - -/// Specialization of SetInversed for 2x2 matrix -template <> -inline bool Matrix<2, 2>::SetInversed(const Matrix<2, 2> &inM) -{ - // Fetch elements - float a = inM.mCol[0].mF32[0]; - float b = inM.mCol[1].mF32[0]; - float c = inM.mCol[0].mF32[1]; - float d = inM.mCol[1].mF32[1]; - - // Calculate determinant - float det = a * d - b * c; - if (det == 0.0f) - return false; - - // Construct inverse - mCol[0].mF32[0] = d / det; - mCol[1].mF32[0] = -b / det; - mCol[0].mF32[1] = -c / det; - mCol[1].mF32[1] = a / det; - return true; -} - -#endif // !JPH_PLATFORM_DOXYGEN - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Quat.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Quat.h deleted file mode 100644 index 10564cab156..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Quat.h +++ /dev/null @@ -1,255 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include - -JPH_NAMESPACE_BEGIN - -/// Quaternion class, quaternions are 4 dimensional vectors which can describe rotations in 3 dimensional -/// space if their length is 1. -/// -/// They are written as: -/// -/// \f$q = w + x \: i + y \: j + z \: k\f$ -/// -/// or in vector notation: -/// -/// \f$q = [w, v] = [w, x, y, z]\f$ -/// -/// Where: -/// -/// w = the real part -/// v = the imaginary part, (x, y, z) -/// -/// Note that we store the quaternion in a Vec4 as [x, y, z, w] because that makes -/// it easy to extract the rotation axis of the quaternion: -/// -/// q = [cos(angle / 2), sin(angle / 2) * rotation_axis] -class [[nodiscard]] alignas(JPH_VECTOR_ALIGNMENT) Quat -{ -public: - JPH_OVERRIDE_NEW_DELETE - - ///@name Constructors - ///@{ - inline Quat() = default; ///< Intentionally not initialized for performance reasons - Quat(const Quat &inRHS) = default; - Quat & operator = (const Quat &inRHS) = default; - inline Quat(float inX, float inY, float inZ, float inW) : mValue(inX, inY, inZ, inW) { } - inline explicit Quat(Vec4Arg inV) : mValue(inV) { } - ///@} - - ///@name Tests - ///@{ - - /// Check if two quaternions are exactly equal - inline bool operator == (QuatArg inRHS) const { return mValue == inRHS.mValue; } - - /// Check if two quaternions are different - inline bool operator != (QuatArg inRHS) const { return mValue != inRHS.mValue; } - - /// If this quaternion is close to inRHS. Note that q and -q represent the same rotation, this is not checked here. - inline bool IsClose(QuatArg inRHS, float inMaxDistSq = 1.0e-12f) const { return mValue.IsClose(inRHS.mValue, inMaxDistSq); } - - /// If the length of this quaternion is 1 +/- inTolerance - inline bool IsNormalized(float inTolerance = 1.0e-5f) const { return mValue.IsNormalized(inTolerance); } - - /// If any component of this quaternion is a NaN (not a number) - inline bool IsNaN() const { return mValue.IsNaN(); } - - ///@} - ///@name Get components - ///@{ - - /// Get X component (imaginary part i) - JPH_INLINE float GetX() const { return mValue.GetX(); } - - /// Get Y component (imaginary part j) - JPH_INLINE float GetY() const { return mValue.GetY(); } - - /// Get Z component (imaginary part k) - JPH_INLINE float GetZ() const { return mValue.GetZ(); } - - /// Get W component (real part) - JPH_INLINE float GetW() const { return mValue.GetW(); } - - /// Get the imaginary part of the quaternion - JPH_INLINE Vec3 GetXYZ() const { return Vec3(mValue); } - - /// Get the quaternion as a Vec4 - JPH_INLINE Vec4 GetXYZW() const { return mValue; } - - /// Set individual components - JPH_INLINE void SetX(float inX) { mValue.SetX(inX); } - JPH_INLINE void SetY(float inY) { mValue.SetY(inY); } - JPH_INLINE void SetZ(float inZ) { mValue.SetZ(inZ); } - JPH_INLINE void SetW(float inW) { mValue.SetW(inW); } - - /// Set all components - JPH_INLINE void Set(float inX, float inY, float inZ, float inW) { mValue.Set(inX, inY, inZ, inW); } - - ///@} - ///@name Default quaternions - ///@{ - - /// @return [0, 0, 0, 0] - JPH_INLINE static Quat sZero() { return Quat(Vec4::sZero()); } - - /// @return [1, 0, 0, 0] (or in storage format Quat(0, 0, 0, 1)) - JPH_INLINE static Quat sIdentity() { return Quat(0, 0, 0, 1); } - - ///@} - - /// Rotation from axis and angle - JPH_INLINE static Quat sRotation(Vec3Arg inAxis, float inAngle); - - /// Get axis and angle that represents this quaternion, outAngle will always be in the range \f$[0, \pi]\f$ - JPH_INLINE void GetAxisAngle(Vec3 &outAxis, float &outAngle) const; - - /// Create quaternion that rotates a vector from the direction of inFrom to the direction of inTo along the shortest path - /// @see https://www.euclideanspace.com/maths/algebra/vectors/angleBetween/index.htm - JPH_INLINE static Quat sFromTo(Vec3Arg inFrom, Vec3Arg inTo); - - /// Random unit quaternion - template - inline static Quat sRandom(Random &inRandom); - - /// Conversion from Euler angles. Rotation order is X then Y then Z (RotZ * RotY * RotX). Angles in radians. - inline static Quat sEulerAngles(Vec3Arg inAngles); - - /// Conversion to Euler angles. Rotation order is X then Y then Z (RotZ * RotY * RotX). Angles in radians. - inline Vec3 GetEulerAngles() const; - - ///@name Length / normalization operations - ///@{ - - /// Squared length of quaternion. - /// @return Squared length of quaternion (\f$|v|^2\f$) - JPH_INLINE float LengthSq() const { return mValue.LengthSq(); } - - /// Length of quaternion. - /// @return Length of quaternion (\f$|v|\f$) - JPH_INLINE float Length() const { return mValue.Length(); } - - /// Normalize the quaternion (make it length 1) - JPH_INLINE Quat Normalized() const { return Quat(mValue.Normalized()); } - - ///@} - ///@name Additions / multiplications - ///@{ - - JPH_INLINE void operator += (QuatArg inRHS) { mValue += inRHS.mValue; } - JPH_INLINE void operator -= (QuatArg inRHS) { mValue -= inRHS.mValue; } - JPH_INLINE void operator *= (float inValue) { mValue *= inValue; } - JPH_INLINE void operator /= (float inValue) { mValue /= inValue; } - JPH_INLINE Quat operator - () const { return Quat(-mValue); } - JPH_INLINE Quat operator + (QuatArg inRHS) const { return Quat(mValue + inRHS.mValue); } - JPH_INLINE Quat operator - (QuatArg inRHS) const { return Quat(mValue - inRHS.mValue); } - JPH_INLINE Quat operator * (QuatArg inRHS) const; - JPH_INLINE Quat operator * (float inValue) const { return Quat(mValue * inValue); } - inline friend Quat operator * (float inValue, QuatArg inRHS) { return Quat(inRHS.mValue * inValue); } - JPH_INLINE Quat operator / (float inValue) const { return Quat(mValue / inValue); } - - ///@} - - /// Rotate a vector by this quaternion - JPH_INLINE Vec3 operator * (Vec3Arg inValue) const; - - /// Rotate a vector by the inverse of this quaternion - JPH_INLINE Vec3 InverseRotate(Vec3Arg inValue) const; - - /// Rotate a the vector (1, 0, 0) with this quaternion - JPH_INLINE Vec3 RotateAxisX() const; - - /// Rotate a the vector (0, 1, 0) with this quaternion - JPH_INLINE Vec3 RotateAxisY() const; - - /// Rotate a the vector (0, 0, 1) with this quaternion - JPH_INLINE Vec3 RotateAxisZ() const; - - /// Dot product - JPH_INLINE float Dot(QuatArg inRHS) const { return mValue.Dot(inRHS.mValue); } - - /// The conjugate [w, -x, -y, -z] is the same as the inverse for unit quaternions - JPH_INLINE Quat Conjugated() const { return Quat(Vec4::sXor(mValue, UVec4(0x80000000, 0x80000000, 0x80000000, 0).ReinterpretAsFloat())); } - - /// Get inverse quaternion - JPH_INLINE Quat Inversed() const { return Conjugated() / Length(); } - - /// Ensures that the W component is positive by negating the entire quaternion if it is not. This is useful when you want to store a quaternion as a 3 vector by discarding W and reconstructing it as sqrt(1 - x^2 - y^2 - z^2). - JPH_INLINE Quat EnsureWPositive() const { return Quat(Vec4::sXor(mValue, Vec4::sAnd(mValue.SplatW(), UVec4::sReplicate(0x80000000).ReinterpretAsFloat()))); } - - /// Get a quaternion that is perpendicular to this quaternion - JPH_INLINE Quat GetPerpendicular() const { return Quat(Vec4(1, -1, 1, -1) * mValue.Swizzle()); } - - /// Get rotation angle around inAxis (uses Swing Twist Decomposition to get the twist quaternion and uses q(axis, angle) = [cos(angle / 2), axis * sin(angle / 2)]) - JPH_INLINE float GetRotationAngle(Vec3Arg inAxis) const { return GetW() == 0.0f? JPH_PI : 2.0f * ATan(GetXYZ().Dot(inAxis) / GetW()); } - - /// Swing Twist Decomposition: any quaternion can be split up as: - /// - /// \f[q = q_{swing} \: q_{twist}\f] - /// - /// where \f$q_{twist}\f$ rotates only around axis v. - /// - /// \f$q_{twist}\f$ is: - /// - /// \f[q_{twist} = \frac{[q_w, q_{ijk} \cdot v \: v]}{\left|[q_w, q_{ijk} \cdot v \: v]\right|}\f] - /// - /// where q_w is the real part of the quaternion and q_i the imaginary part (a 3 vector). - /// - /// The swing can then be calculated as: - /// - /// \f[q_{swing} = q \: q_{twist}^* \f] - /// - /// Where \f$q_{twist}^*\f$ = complex conjugate of \f$q_{twist}\f$ - JPH_INLINE Quat GetTwist(Vec3Arg inAxis) const; - - /// Decomposes quaternion into swing and twist component: - /// - /// \f$q = q_{swing} \: q_{twist}\f$ - /// - /// where \f$q_{swing} \: \hat{x} = q_{twist} \: \hat{y} = q_{twist} \: \hat{z} = 0\f$ - /// - /// In other words: - /// - /// - \f$q_{twist}\f$ only rotates around the X-axis. - /// - \f$q_{swing}\f$ only rotates around the Y and Z-axis. - /// - /// @see Gino van den Bergen - Rotational Joint Limits in Quaternion Space - GDC 2016 - JPH_INLINE void GetSwingTwist(Quat &outSwing, Quat &outTwist) const; - - /// Linear interpolation between two quaternions (for small steps). - /// @param inFraction is in the range [0, 1] - /// @param inDestination The destination quaternion - /// @return (1 - inFraction) * this + fraction * inDestination - JPH_INLINE Quat LERP(QuatArg inDestination, float inFraction) const; - - /// Spherical linear interpolation between two quaternions. - /// @param inFraction is in the range [0, 1] - /// @param inDestination The destination quaternion - /// @return When fraction is zero this quaternion is returned, when fraction is 1 inDestination is returned. - /// When fraction is between 0 and 1 an interpolation along the shortest path is returned. - JPH_INLINE Quat SLERP(QuatArg inDestination, float inFraction) const; - - /// Load 3 floats from memory (X, Y and Z component and then calculates W) reads 32 bits extra which it doesn't use - static JPH_INLINE Quat sLoadFloat3Unsafe(const Float3 &inV); - - /// Store 3 as floats to memory (X, Y and Z component) - JPH_INLINE void StoreFloat3(Float3 *outV) const; - - /// To String - friend ostream & operator << (ostream &inStream, QuatArg inQ) { inStream << inQ.mValue; return inStream; } - - /// 4 vector that stores [x, y, z, w] parts of the quaternion - Vec4 mValue; -}; - -static_assert(is_trivial(), "Is supposed to be a trivial type!"); - -JPH_NAMESPACE_END - -#include "Quat.inl" diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Quat.inl b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Quat.inl deleted file mode 100644 index 72b78341b70..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Quat.inl +++ /dev/null @@ -1,328 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -JPH_NAMESPACE_BEGIN - -Quat Quat::operator * (QuatArg inRHS) const -{ -#if defined(JPH_USE_SSE4_1) - // Taken from: http://momchil-velikov.blogspot.nl/2013/10/fast-sse-quternion-multiplication.html - __m128 abcd = mValue.mValue; - __m128 xyzw = inRHS.mValue.mValue; - - __m128 t0 = _mm_shuffle_ps(abcd, abcd, _MM_SHUFFLE(3, 3, 3, 3)); - __m128 t1 = _mm_shuffle_ps(xyzw, xyzw, _MM_SHUFFLE(2, 3, 0, 1)); - - __m128 t3 = _mm_shuffle_ps(abcd, abcd, _MM_SHUFFLE(0, 0, 0, 0)); - __m128 t4 = _mm_shuffle_ps(xyzw, xyzw, _MM_SHUFFLE(1, 0, 3, 2)); - - __m128 t5 = _mm_shuffle_ps(abcd, abcd, _MM_SHUFFLE(1, 1, 1, 1)); - __m128 t6 = _mm_shuffle_ps(xyzw, xyzw, _MM_SHUFFLE(2, 0, 3, 1)); - - // [d,d,d,d] * [z,w,x,y] = [dz,dw,dx,dy] - __m128 m0 = _mm_mul_ps(t0, t1); - - // [a,a,a,a] * [y,x,w,z] = [ay,ax,aw,az] - __m128 m1 = _mm_mul_ps(t3, t4); - - // [b,b,b,b] * [z,x,w,y] = [bz,bx,bw,by] - __m128 m2 = _mm_mul_ps(t5, t6); - - // [c,c,c,c] * [w,z,x,y] = [cw,cz,cx,cy] - __m128 t7 = _mm_shuffle_ps(abcd, abcd, _MM_SHUFFLE(2, 2, 2, 2)); - __m128 t8 = _mm_shuffle_ps(xyzw, xyzw, _MM_SHUFFLE(3, 2, 0, 1)); - __m128 m3 = _mm_mul_ps(t7, t8); - - // [dz,dw,dx,dy] + -[ay,ax,aw,az] = [dz+ay,dw-ax,dx+aw,dy-az] - __m128 e = _mm_addsub_ps(m0, m1); - - // [dx+aw,dz+ay,dy-az,dw-ax] - e = _mm_shuffle_ps(e, e, _MM_SHUFFLE(1, 3, 0, 2)); - - // [dx+aw,dz+ay,dy-az,dw-ax] + -[bz,bx,bw,by] = [dx+aw+bz,dz+ay-bx,dy-az+bw,dw-ax-by] - e = _mm_addsub_ps(e, m2); - - // [dz+ay-bx,dw-ax-by,dy-az+bw,dx+aw+bz] - e = _mm_shuffle_ps(e, e, _MM_SHUFFLE(2, 0, 1, 3)); - - // [dz+ay-bx,dw-ax-by,dy-az+bw,dx+aw+bz] + -[cw,cz,cx,cy] = [dz+ay-bx+cw,dw-ax-by-cz,dy-az+bw+cx,dx+aw+bz-cy] - e = _mm_addsub_ps(e, m3); - - // [dw-ax-by-cz,dz+ay-bx+cw,dy-az+bw+cx,dx+aw+bz-cy] - return Quat(Vec4(_mm_shuffle_ps(e, e, _MM_SHUFFLE(2, 3, 1, 0)))); -#else - float lx = mValue.GetX(); - float ly = mValue.GetY(); - float lz = mValue.GetZ(); - float lw = mValue.GetW(); - - float rx = inRHS.mValue.GetX(); - float ry = inRHS.mValue.GetY(); - float rz = inRHS.mValue.GetZ(); - float rw = inRHS.mValue.GetW(); - - float x = lw * rx + lx * rw + ly * rz - lz * ry; - float y = lw * ry - lx * rz + ly * rw + lz * rx; - float z = lw * rz + lx * ry - ly * rx + lz * rw; - float w = lw * rw - lx * rx - ly * ry - lz * rz; - - return Quat(x, y, z, w); -#endif -} - -Quat Quat::sRotation(Vec3Arg inAxis, float inAngle) -{ - // returns [inAxis * sin(0.5f * inAngle), cos(0.5f * inAngle)] - JPH_ASSERT(inAxis.IsNormalized()); - Vec4 s, c; - Vec4::sReplicate(0.5f * inAngle).SinCos(s, c); - return Quat(Vec4::sSelect(Vec4(inAxis) * s, c, UVec4(0, 0, 0, 0xffffffffU))); -} - -void Quat::GetAxisAngle(Vec3 &outAxis, float &outAngle) const -{ - JPH_ASSERT(IsNormalized()); - Quat w_pos = EnsureWPositive(); - float abs_w = w_pos.GetW(); - if (abs_w >= 1.0f) - { - outAxis = Vec3::sZero(); - outAngle = 0.0f; - } - else - { - outAngle = 2.0f * ACos(abs_w); - outAxis = w_pos.GetXYZ().NormalizedOr(Vec3::sZero()); - } -} - -Quat Quat::sFromTo(Vec3Arg inFrom, Vec3Arg inTo) -{ - /* - Uses (inFrom = v1, inTo = v2): - - angle = arcos(v1 . v2 / |v1||v2|) - axis = normalize(v1 x v2) - - Quaternion is then: - - s = sin(angle / 2) - x = axis.x * s - y = axis.y * s - z = axis.z * s - w = cos(angle / 2) - - Using identities: - - sin(2 * a) = 2 * sin(a) * cos(a) - cos(2 * a) = cos(a)^2 - sin(a)^2 - sin(a)^2 + cos(a)^2 = 1 - - This reduces to: - - x = (v1 x v2).x - y = (v1 x v2).y - z = (v1 x v2).z - w = |v1||v2| + v1 . v2 - - which then needs to be normalized because the whole equation was multiplied by 2 cos(angle / 2) - */ - - float len_v1_v2 = sqrt(inFrom.LengthSq() * inTo.LengthSq()); - float w = len_v1_v2 + inFrom.Dot(inTo); - - if (w == 0.0f) - { - if (len_v1_v2 == 0.0f) - { - // If either of the vectors has zero length, there is no rotation and we return identity - return Quat::sIdentity(); - } - else - { - // If vectors are perpendicular, take one of the many 180 degree rotations that exist - return Quat(Vec4(inFrom.GetNormalizedPerpendicular(), 0)); - } - } - - Vec3 v = inFrom.Cross(inTo); - return Quat(Vec4(v, w)).Normalized(); -} - -template -Quat Quat::sRandom(Random &inRandom) -{ - std::uniform_real_distribution zero_to_one(0.0f, 1.0f); - float x0 = zero_to_one(inRandom); - float r1 = sqrt(1.0f - x0), r2 = sqrt(x0); - std::uniform_real_distribution zero_to_two_pi(0.0f, 2.0f * JPH_PI); - Vec4 s, c; - Vec4(zero_to_two_pi(inRandom), zero_to_two_pi(inRandom), 0, 0).SinCos(s, c); - return Quat(s.GetX() * r1, c.GetX() * r1, s.GetY() * r2, c.GetY() * r2); -} - -Quat Quat::sEulerAngles(Vec3Arg inAngles) -{ - Vec4 half(0.5f * inAngles); - Vec4 s, c; - half.SinCos(s, c); - - float cx = c.GetX(); - float sx = s.GetX(); - float cy = c.GetY(); - float sy = s.GetY(); - float cz = c.GetZ(); - float sz = s.GetZ(); - - return Quat( - cz * sx * cy - sz * cx * sy, - cz * cx * sy + sz * sx * cy, - sz * cx * cy - cz * sx * sy, - cz * cx * cy + sz * sx * sy); -} - -Vec3 Quat::GetEulerAngles() const -{ - float y_sq = GetY() * GetY(); - - // X - float t0 = 2.0f * (GetW() * GetX() + GetY() * GetZ()); - float t1 = 1.0f - 2.0f * (GetX() * GetX() + y_sq); - - // Y - float t2 = 2.0f * (GetW() * GetY() - GetZ() * GetX()); - t2 = t2 > 1.0f? 1.0f : t2; - t2 = t2 < -1.0f? -1.0f : t2; - - // Z - float t3 = 2.0f * (GetW() * GetZ() + GetX() * GetY()); - float t4 = 1.0f - 2.0f * (y_sq + GetZ() * GetZ()); - - return Vec3(ATan2(t0, t1), ASin(t2), ATan2(t3, t4)); -} - -Quat Quat::GetTwist(Vec3Arg inAxis) const -{ - Quat twist(Vec4(GetXYZ().Dot(inAxis) * inAxis, GetW())); - float twist_len = twist.LengthSq(); - if (twist_len != 0.0f) - return twist / sqrt(twist_len); - else - return Quat::sIdentity(); -} - -void Quat::GetSwingTwist(Quat &outSwing, Quat &outTwist) const -{ - float x = GetX(), y = GetY(), z = GetZ(), w = GetW(); - float s = sqrt(Square(w) + Square(x)); - if (s != 0.0f) - { - outTwist = Quat(x / s, 0, 0, w / s); - outSwing = Quat(0, (w * y - x * z) / s, (w * z + x * y) / s, s); - } - else - { - // If both x and w are zero, this must be a 180 degree rotation around either y or z - outTwist = Quat::sIdentity(); - outSwing = *this; - } -} - -Quat Quat::LERP(QuatArg inDestination, float inFraction) const -{ - float scale0 = 1.0f - inFraction; - return Quat(Vec4::sReplicate(scale0) * mValue + Vec4::sReplicate(inFraction) * inDestination.mValue); -} - -Quat Quat::SLERP(QuatArg inDestination, float inFraction) const -{ - // Difference at which to LERP instead of SLERP - const float delta = 0.0001f; - - // Calc cosine - float sign_scale1 = 1.0f; - float cos_omega = Dot(inDestination); - - // Adjust signs (if necessary) - if (cos_omega < 0.0f) - { - cos_omega = -cos_omega; - sign_scale1 = -1.0f; - } - - // Calculate coefficients - float scale0, scale1; - if (1.0f - cos_omega > delta) - { - // Standard case (slerp) - float omega = ACos(cos_omega); - float sin_omega = Sin(omega); - scale0 = Sin((1.0f - inFraction) * omega) / sin_omega; - scale1 = sign_scale1 * Sin(inFraction * omega) / sin_omega; - } - else - { - // Quaternions are very close so we can do a linear interpolation - scale0 = 1.0f - inFraction; - scale1 = sign_scale1 * inFraction; - } - - // Interpolate between the two quaternions - return Quat(Vec4::sReplicate(scale0) * mValue + Vec4::sReplicate(scale1) * inDestination.mValue).Normalized(); -} - -Vec3 Quat::operator * (Vec3Arg inValue) const -{ - // Rotating a vector by a quaternion is done by: p' = q * p * q^-1 (q^-1 = conjugated(q) for a unit quaternion) - JPH_ASSERT(IsNormalized()); - return Vec3((*this * Quat(Vec4(inValue, 0)) * Conjugated()).mValue); -} - -Vec3 Quat::InverseRotate(Vec3Arg inValue) const -{ - JPH_ASSERT(IsNormalized()); - return Vec3((Conjugated() * Quat(Vec4(inValue, 0)) * *this).mValue); -} - -Vec3 Quat::RotateAxisX() const -{ - // This is *this * Vec3::sAxisX() written out: - JPH_ASSERT(IsNormalized()); - float x = GetX(), y = GetY(), z = GetZ(), w = GetW(); - float tx = 2.0f * x, tw = 2.0f * w; - return Vec3(tx * x + tw * w - 1.0f, tx * y + z * tw, tx * z - y * tw); -} - -Vec3 Quat::RotateAxisY() const -{ - // This is *this * Vec3::sAxisY() written out: - JPH_ASSERT(IsNormalized()); - float x = GetX(), y = GetY(), z = GetZ(), w = GetW(); - float ty = 2.0f * y, tw = 2.0f * w; - return Vec3(x * ty - z * tw, tw * w + ty * y - 1.0f, x * tw + ty * z); -} - -Vec3 Quat::RotateAxisZ() const -{ - // This is *this * Vec3::sAxisZ() written out: - JPH_ASSERT(IsNormalized()); - float x = GetX(), y = GetY(), z = GetZ(), w = GetW(); - float tz = 2.0f * z, tw = 2.0f * w; - return Vec3(x * tz + y * tw, y * tz - x * tw, tw * w + tz * z - 1.0f); -} - -void Quat::StoreFloat3(Float3 *outV) const -{ - JPH_ASSERT(IsNormalized()); - EnsureWPositive().GetXYZ().StoreFloat3(outV); -} - -Quat Quat::sLoadFloat3Unsafe(const Float3 &inV) -{ - Vec3 v = Vec3::sLoadFloat3Unsafe(inV); - float w = sqrt(max(1.0f - v.LengthSq(), 0.0f)); // It is possible that the length of v is a fraction above 1, and we don't want to introduce NaN's in that case so we clamp to 0 - return Quat(Vec4(v, w)); -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Real.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Real.h deleted file mode 100644 index 7773abf7ef5..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Real.h +++ /dev/null @@ -1,44 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2022 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include - -JPH_NAMESPACE_BEGIN - -#ifdef JPH_DOUBLE_PRECISION - -// Define real to double -using Real = double; -using Real3 = Double3; -using RVec3 = DVec3; -using RVec3Arg = DVec3Arg; -using RMat44 = DMat44; -using RMat44Arg = DMat44Arg; - -#define JPH_RVECTOR_ALIGNMENT JPH_DVECTOR_ALIGNMENT - -#else - -// Define real to float -using Real = float; -using Real3 = Float3; -using RVec3 = Vec3; -using RVec3Arg = Vec3Arg; -using RMat44 = Mat44; -using RMat44Arg = Mat44Arg; - -#define JPH_RVECTOR_ALIGNMENT JPH_VECTOR_ALIGNMENT - -#endif // JPH_DOUBLE_PRECISION - -// Put the 'real' operator in a namespace so that users can opt in to use it: -// using namespace JPH::literals; -namespace literals { - constexpr Real operator ""_r (long double inValue) { return Real(inValue); } -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Swizzle.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Swizzle.h deleted file mode 100644 index ad8dfbc144a..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Swizzle.h +++ /dev/null @@ -1,19 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -JPH_NAMESPACE_BEGIN - -/// Enum indicating which component to use when swizzling -enum -{ - SWIZZLE_X = 0, ///< Use the X component - SWIZZLE_Y = 1, ///< Use the Y component - SWIZZLE_Z = 2, ///< Use the Z component - SWIZZLE_W = 3, ///< Use the W component - SWIZZLE_UNUSED = 2, ///< We always use the Z component when we don't specifically want to initialize a value, this is consistent with what is done in Vec3(x, y, z), Vec3(Float3 &) and Vec3::sLoadFloat3Unsafe -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Trigonometry.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Trigonometry.h deleted file mode 100644 index 0a00564a8a4..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Trigonometry.h +++ /dev/null @@ -1,59 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -JPH_NAMESPACE_BEGIN - -// Note that this file exists because std::sin etc. are not platform independent and will lead to non-deterministic simulation - -/// Sine of x (input in radians) -JPH_INLINE float Sin(float inX) -{ - Vec4 s, c; - Vec4::sReplicate(inX).SinCos(s, c); - return s.GetX(); -} - -/// Cosine of x (input in radians) -JPH_INLINE float Cos(float inX) -{ - Vec4 s, c; - Vec4::sReplicate(inX).SinCos(s, c); - return c.GetX(); -} - -/// Tangent of x (input in radians) -JPH_INLINE float Tan(float inX) -{ - return Vec4::sReplicate(inX).Tan().GetX(); -} - -/// Arc sine of x (returns value in the range [-PI / 2, PI / 2]) -/// Note that all input values will be clamped to the range [-1, 1] and this function will not return NaNs like std::asin -JPH_INLINE float ASin(float inX) -{ - return Vec4::sReplicate(inX).ASin().GetX(); -} - -/// Arc cosine of x (returns value in the range [0, PI]) -/// Note that all input values will be clamped to the range [-1, 1] and this function will not return NaNs like std::acos -JPH_INLINE float ACos(float inX) -{ - return Vec4::sReplicate(inX).ACos().GetX(); -} - -/// Arc tangent of x (returns value in the range [-PI / 2, PI / 2]) -JPH_INLINE float ATan(float inX) -{ - return Vec4::sReplicate(inX).ATan().GetX(); -} - -/// Arc tangent of y / x using the signs of the arguments to determine the correct quadrant (returns value in the range [-PI, PI]) -JPH_INLINE float ATan2(float inY, float inX) -{ - return Vec4::sATan2(Vec4::sReplicate(inY), Vec4::sReplicate(inX)).GetX(); -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/UVec4.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/UVec4.h deleted file mode 100644 index 4855e2296c4..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/UVec4.h +++ /dev/null @@ -1,220 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -class [[nodiscard]] alignas(JPH_VECTOR_ALIGNMENT) UVec4 -{ -public: - JPH_OVERRIDE_NEW_DELETE - - // Underlying vector type -#if defined(JPH_USE_SSE) - using Type = __m128i; -#elif defined(JPH_USE_NEON) - using Type = uint32x4_t; -#else - using Type = struct { uint32 mData[4]; }; -#endif - - /// Constructor - UVec4() = default; ///< Intentionally not initialized for performance reasons - UVec4(const UVec4 &inRHS) = default; - UVec4 & operator = (const UVec4 &inRHS) = default; - JPH_INLINE UVec4(Type inRHS) : mValue(inRHS) { } - - /// Create a vector from 4 integer components - JPH_INLINE UVec4(uint32 inX, uint32 inY, uint32 inZ, uint32 inW); - - /// Comparison - JPH_INLINE bool operator == (UVec4Arg inV2) const; - JPH_INLINE bool operator != (UVec4Arg inV2) const { return !(*this == inV2); } - - /// Swizzle the elements in inV - template - JPH_INLINE UVec4 Swizzle() const; - - /// Vector with all zeros - static JPH_INLINE UVec4 sZero(); - - /// Replicate int inV across all components - static JPH_INLINE UVec4 sReplicate(uint32 inV); - - /// Load 1 int from memory and place it in the X component, zeros Y, Z and W - static JPH_INLINE UVec4 sLoadInt(const uint32 *inV); - - /// Load 4 ints from memory - static JPH_INLINE UVec4 sLoadInt4(const uint32 *inV); - - /// Load 4 ints from memory, aligned to 16 bytes - static JPH_INLINE UVec4 sLoadInt4Aligned(const uint32 *inV); - - /// Gather 4 ints from memory at inBase + inOffsets[i] * Scale - template - static JPH_INLINE UVec4 sGatherInt4(const uint32 *inBase, UVec4Arg inOffsets); - - /// Return the minimum value of each of the components - static JPH_INLINE UVec4 sMin(UVec4Arg inV1, UVec4Arg inV2); - - /// Return the maximum of each of the components - static JPH_INLINE UVec4 sMax(UVec4Arg inV1, UVec4Arg inV2); - - /// Equals (component wise) - static JPH_INLINE UVec4 sEquals(UVec4Arg inV1, UVec4Arg inV2); - - /// Component wise select, returns inV1 when highest bit of inControl = 0 and inV2 when highest bit of inControl = 1 - static JPH_INLINE UVec4 sSelect(UVec4Arg inV1, UVec4Arg inV2, UVec4Arg inControl); - - /// Logical or (component wise) - static JPH_INLINE UVec4 sOr(UVec4Arg inV1, UVec4Arg inV2); - - /// Logical xor (component wise) - static JPH_INLINE UVec4 sXor(UVec4Arg inV1, UVec4Arg inV2); - - /// Logical and (component wise) - static JPH_INLINE UVec4 sAnd(UVec4Arg inV1, UVec4Arg inV2); - - /// Logical not (component wise) - static JPH_INLINE UVec4 sNot(UVec4Arg inV1); - - /// Sorts the elements in inIndex so that the values that correspond to trues in inValue are the first elements. - /// The remaining elements will be set to inValue.w. - /// I.e. if inValue = (true, false, true, false) and inIndex = (1, 2, 3, 4) the function returns (1, 3, 4, 4). - static JPH_INLINE UVec4 sSort4True(UVec4Arg inValue, UVec4Arg inIndex); - - /// Get individual components -#if defined(JPH_USE_SSE) - JPH_INLINE uint32 GetX() const { return uint32(_mm_cvtsi128_si32(mValue)); } - JPH_INLINE uint32 GetY() const { return mU32[1]; } - JPH_INLINE uint32 GetZ() const { return mU32[2]; } - JPH_INLINE uint32 GetW() const { return mU32[3]; } -#elif defined(JPH_USE_NEON) - JPH_INLINE uint32 GetX() const { return vgetq_lane_u32(mValue, 0); } - JPH_INLINE uint32 GetY() const { return vgetq_lane_u32(mValue, 1); } - JPH_INLINE uint32 GetZ() const { return vgetq_lane_u32(mValue, 2); } - JPH_INLINE uint32 GetW() const { return vgetq_lane_u32(mValue, 3); } -#else - JPH_INLINE uint32 GetX() const { return mU32[0]; } - JPH_INLINE uint32 GetY() const { return mU32[1]; } - JPH_INLINE uint32 GetZ() const { return mU32[2]; } - JPH_INLINE uint32 GetW() const { return mU32[3]; } -#endif - - /// Set individual components - JPH_INLINE void SetX(uint32 inX) { mU32[0] = inX; } - JPH_INLINE void SetY(uint32 inY) { mU32[1] = inY; } - JPH_INLINE void SetZ(uint32 inZ) { mU32[2] = inZ; } - JPH_INLINE void SetW(uint32 inW) { mU32[3] = inW; } - - /// Get component by index - JPH_INLINE uint32 operator [] (uint inCoordinate) const { JPH_ASSERT(inCoordinate < 4); return mU32[inCoordinate]; } - JPH_INLINE uint32 & operator [] (uint inCoordinate) { JPH_ASSERT(inCoordinate < 4); return mU32[inCoordinate]; } - - /// Multiplies each of the 4 integer components with an integer (discards any overflow) - JPH_INLINE UVec4 operator * (UVec4Arg inV2) const; - - /// Adds an integer value to all integer components (discards any overflow) - JPH_INLINE UVec4 operator + (UVec4Arg inV2); - - /// Add two integer vectors (component wise) - JPH_INLINE UVec4 & operator += (UVec4Arg inV2); - - /// Replicate the X component to all components - JPH_INLINE UVec4 SplatX() const; - - /// Replicate the Y component to all components - JPH_INLINE UVec4 SplatY() const; - - /// Replicate the Z component to all components - JPH_INLINE UVec4 SplatZ() const; - - /// Replicate the W component to all components - JPH_INLINE UVec4 SplatW() const; - - /// Convert each component from an int to a float - JPH_INLINE Vec4 ToFloat() const; - - /// Reinterpret UVec4 as a Vec4 (doesn't change the bits) - JPH_INLINE Vec4 ReinterpretAsFloat() const; - - /// Store 4 ints to memory - JPH_INLINE void StoreInt4(uint32 *outV) const; - - /// Store 4 ints to memory, aligned to 16 bytes - JPH_INLINE void StoreInt4Aligned(uint32 *outV) const; - - /// Test if any of the components are true (true is when highest bit of component is set) - JPH_INLINE bool TestAnyTrue() const; - - /// Test if any of X, Y or Z components are true (true is when highest bit of component is set) - JPH_INLINE bool TestAnyXYZTrue() const; - - /// Test if all components are true (true is when highest bit of component is set) - JPH_INLINE bool TestAllTrue() const; - - /// Test if X, Y and Z components are true (true is when highest bit of component is set) - JPH_INLINE bool TestAllXYZTrue() const; - - /// Count the number of components that are true (true is when highest bit of component is set) - JPH_INLINE int CountTrues() const; - - /// Store if X is true in bit 0, Y in bit 1, Z in bit 2 and W in bit 3 (true is when highest bit of component is set) - JPH_INLINE int GetTrues() const; - - /// Shift all components by Count bits to the left (filling with zeros from the left) - template - JPH_INLINE UVec4 LogicalShiftLeft() const; - - /// Shift all components by Count bits to the right (filling with zeros from the right) - template - JPH_INLINE UVec4 LogicalShiftRight() const; - - /// Shift all components by Count bits to the right (shifting in the value of the highest bit) - template - JPH_INLINE UVec4 ArithmeticShiftRight() const; - - /// Takes the lower 4 16 bits and expands them to X, Y, Z and W - JPH_INLINE UVec4 Expand4Uint16Lo() const; - - /// Takes the upper 4 16 bits and expands them to X, Y, Z and W - JPH_INLINE UVec4 Expand4Uint16Hi() const; - - /// Takes byte 0 .. 3 and expands them to X, Y, Z and W - JPH_INLINE UVec4 Expand4Byte0() const; - - /// Takes byte 4 .. 7 and expands them to X, Y, Z and W - JPH_INLINE UVec4 Expand4Byte4() const; - - /// Takes byte 8 .. 11 and expands them to X, Y, Z and W - JPH_INLINE UVec4 Expand4Byte8() const; - - /// Takes byte 12 .. 15 and expands them to X, Y, Z and W - JPH_INLINE UVec4 Expand4Byte12() const; - - /// Shift vector components by 4 - Count floats to the left, so if Count = 1 the resulting vector is (W, 0, 0, 0), when Count = 3 the resulting vector is (Y, Z, W, 0) - JPH_INLINE UVec4 ShiftComponents4Minus(int inCount) const; - - /// To String - friend ostream & operator << (ostream &inStream, UVec4Arg inV) - { - inStream << inV.mU32[0] << ", " << inV.mU32[1] << ", " << inV.mU32[2] << ", " << inV.mU32[3]; - return inStream; - } - - union - { - Type mValue; - uint32 mU32[4]; - }; -}; - -static_assert(is_trivial(), "Is supposed to be a trivial type!"); - -JPH_NAMESPACE_END - -#include "UVec4.inl" diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/UVec4.inl b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/UVec4.inl deleted file mode 100644 index e01d66b11cd..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/UVec4.inl +++ /dev/null @@ -1,573 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -JPH_NAMESPACE_BEGIN - -UVec4::UVec4(uint32 inX, uint32 inY, uint32 inZ, uint32 inW) -{ -#if defined(JPH_USE_SSE) - mValue = _mm_set_epi32(int(inW), int(inZ), int(inY), int(inX)); -#elif defined(JPH_USE_NEON) - uint32x2_t xy = vcreate_u32(static_cast(inX) | (static_cast(inY) << 32)); - uint32x2_t zw = vcreate_u32(static_cast(inZ) | (static_cast(inW) << 32)); - mValue = vcombine_u32(xy, zw); -#else - mU32[0] = inX; - mU32[1] = inY; - mU32[2] = inZ; - mU32[3] = inW; -#endif -} - -bool UVec4::operator == (UVec4Arg inV2) const -{ - return sEquals(*this, inV2).TestAllTrue(); -} - -template -UVec4 UVec4::Swizzle() const -{ - static_assert(SwizzleX <= 3, "SwizzleX template parameter out of range"); - static_assert(SwizzleY <= 3, "SwizzleY template parameter out of range"); - static_assert(SwizzleZ <= 3, "SwizzleZ template parameter out of range"); - static_assert(SwizzleW <= 3, "SwizzleW template parameter out of range"); - -#if defined(JPH_USE_SSE) - return _mm_shuffle_epi32(mValue, _MM_SHUFFLE(SwizzleW, SwizzleZ, SwizzleY, SwizzleX)); -#elif defined(JPH_USE_NEON) - return JPH_NEON_SHUFFLE_F32x4(mValue, mValue, SwizzleX, SwizzleY, SwizzleZ, SwizzleW); -#else - return UVec4(mU32[SwizzleX], mU32[SwizzleY], mU32[SwizzleZ], mU32[SwizzleW]); -#endif -} - -UVec4 UVec4::sZero() -{ -#if defined(JPH_USE_SSE) - return _mm_setzero_si128(); -#elif defined(JPH_USE_NEON) - return vdupq_n_u32(0); -#else - return UVec4(0, 0, 0, 0); -#endif -} - -UVec4 UVec4::sReplicate(uint32 inV) -{ -#if defined(JPH_USE_SSE) - return _mm_set1_epi32(int(inV)); -#elif defined(JPH_USE_NEON) - return vdupq_n_u32(inV); -#else - return UVec4(inV, inV, inV, inV); -#endif -} - -UVec4 UVec4::sLoadInt(const uint32 *inV) -{ -#if defined(JPH_USE_SSE) - return _mm_castps_si128(_mm_load_ss(reinterpret_cast(inV))); -#elif defined(JPH_USE_NEON) - return vsetq_lane_u32(*inV, vdupq_n_u32(0), 0); -#else - return UVec4(*inV, 0, 0, 0); -#endif -} - -UVec4 UVec4::sLoadInt4(const uint32 *inV) -{ -#if defined(JPH_USE_SSE) - return _mm_loadu_si128(reinterpret_cast(inV)); -#elif defined(JPH_USE_NEON) - return vld1q_u32(inV); -#else - return UVec4(inV[0], inV[1], inV[2], inV[3]); -#endif -} - -UVec4 UVec4::sLoadInt4Aligned(const uint32 *inV) -{ -#if defined(JPH_USE_SSE) - return _mm_load_si128(reinterpret_cast(inV)); -#elif defined(JPH_USE_NEON) - return vld1q_u32(inV); // ARM doesn't make distinction between aligned or not -#else - return UVec4(inV[0], inV[1], inV[2], inV[3]); -#endif -} - -template -UVec4 UVec4::sGatherInt4(const uint32 *inBase, UVec4Arg inOffsets) -{ -#ifdef JPH_USE_AVX2 - return _mm_i32gather_epi32(reinterpret_cast(inBase), inOffsets.mValue, Scale); -#else - return Vec4::sGatherFloat4(reinterpret_cast(inBase), inOffsets).ReinterpretAsInt(); -#endif -} - -UVec4 UVec4::sMin(UVec4Arg inV1, UVec4Arg inV2) -{ -#if defined(JPH_USE_SSE4_1) - return _mm_min_epu32(inV1.mValue, inV2.mValue); -#elif defined(JPH_USE_NEON) - return vminq_u32(inV1.mValue, inV2.mValue); -#else - UVec4 result; - for (int i = 0; i < 4; i++) - result.mU32[i] = min(inV1.mU32[i], inV2.mU32[i]); - return result; -#endif -} - -UVec4 UVec4::sMax(UVec4Arg inV1, UVec4Arg inV2) -{ -#if defined(JPH_USE_SSE4_1) - return _mm_max_epu32(inV1.mValue, inV2.mValue); -#elif defined(JPH_USE_NEON) - return vmaxq_u32(inV1.mValue, inV2.mValue); -#else - UVec4 result; - for (int i = 0; i < 4; i++) - result.mU32[i] = max(inV1.mU32[i], inV2.mU32[i]); - return result; -#endif -} - -UVec4 UVec4::sEquals(UVec4Arg inV1, UVec4Arg inV2) -{ -#if defined(JPH_USE_SSE) - return _mm_cmpeq_epi32(inV1.mValue, inV2.mValue); -#elif defined(JPH_USE_NEON) - return vceqq_u32(inV1.mValue, inV2.mValue); -#else - return UVec4(inV1.mU32[0] == inV2.mU32[0]? 0xffffffffu : 0, - inV1.mU32[1] == inV2.mU32[1]? 0xffffffffu : 0, - inV1.mU32[2] == inV2.mU32[2]? 0xffffffffu : 0, - inV1.mU32[3] == inV2.mU32[3]? 0xffffffffu : 0); -#endif -} - -UVec4 UVec4::sSelect(UVec4Arg inV1, UVec4Arg inV2, UVec4Arg inControl) -{ -#if defined(JPH_USE_SSE4_1) - return _mm_castps_si128(_mm_blendv_ps(_mm_castsi128_ps(inV1.mValue), _mm_castsi128_ps(inV2.mValue), _mm_castsi128_ps(inControl.mValue))); -#elif defined(JPH_USE_NEON) - return vbslq_u32(vshrq_n_s32(inControl.mValue, 31), inV2.mValue, inV1.mValue); -#else - UVec4 result; - for (int i = 0; i < 4; i++) - result.mU32[i] = inControl.mU32[i] ? inV2.mU32[i] : inV1.mU32[i]; - return result; -#endif -} - -UVec4 UVec4::sOr(UVec4Arg inV1, UVec4Arg inV2) -{ -#if defined(JPH_USE_SSE) - return _mm_or_si128(inV1.mValue, inV2.mValue); -#elif defined(JPH_USE_NEON) - return vorrq_u32(inV1.mValue, inV2.mValue); -#else - return UVec4(inV1.mU32[0] | inV2.mU32[0], - inV1.mU32[1] | inV2.mU32[1], - inV1.mU32[2] | inV2.mU32[2], - inV1.mU32[3] | inV2.mU32[3]); -#endif -} - -UVec4 UVec4::sXor(UVec4Arg inV1, UVec4Arg inV2) -{ -#if defined(JPH_USE_SSE) - return _mm_xor_si128(inV1.mValue, inV2.mValue); -#elif defined(JPH_USE_NEON) - return veorq_u32(inV1.mValue, inV2.mValue); -#else - return UVec4(inV1.mU32[0] ^ inV2.mU32[0], - inV1.mU32[1] ^ inV2.mU32[1], - inV1.mU32[2] ^ inV2.mU32[2], - inV1.mU32[3] ^ inV2.mU32[3]); -#endif -} - -UVec4 UVec4::sAnd(UVec4Arg inV1, UVec4Arg inV2) -{ -#if defined(JPH_USE_SSE) - return _mm_and_si128(inV1.mValue, inV2.mValue); -#elif defined(JPH_USE_NEON) - return vandq_u32(inV1.mValue, inV2.mValue); -#else - return UVec4(inV1.mU32[0] & inV2.mU32[0], - inV1.mU32[1] & inV2.mU32[1], - inV1.mU32[2] & inV2.mU32[2], - inV1.mU32[3] & inV2.mU32[3]); -#endif -} - - -UVec4 UVec4::sNot(UVec4Arg inV1) -{ -#if defined(JPH_USE_AVX512) - return _mm_ternarylogic_epi32(inV1.mValue, inV1.mValue, inV1.mValue, 0b01010101); -#elif defined(JPH_USE_SSE) - return sXor(inV1, sReplicate(0xffffffff)); -#elif defined(JPH_USE_NEON) - return vmvnq_u32(inV1.mValue); -#else - return UVec4(~inV1.mU32[0], ~inV1.mU32[1], ~inV1.mU32[2], ~inV1.mU32[3]); -#endif -} - -UVec4 UVec4::sSort4True(UVec4Arg inValue, UVec4Arg inIndex) -{ - // If inValue.z is false then shift W to Z - UVec4 v = UVec4::sSelect(inIndex.Swizzle(), inIndex, inValue.SplatZ()); - - // If inValue.y is false then shift Z and further to Y and further - v = UVec4::sSelect(v.Swizzle(), v, inValue.SplatY()); - - // If inValue.x is false then shift X and further to Y and further - v = UVec4::sSelect(v.Swizzle(), v, inValue.SplatX()); - - return v; -} - -UVec4 UVec4::operator * (UVec4Arg inV2) const -{ -#if defined(JPH_USE_SSE4_1) - return _mm_mullo_epi32(mValue, inV2.mValue); -#elif defined(JPH_USE_NEON) - return vmulq_u32(mValue, inV2.mValue); -#else - UVec4 result; - for (int i = 0; i < 4; i++) - result.mU32[i] = mU32[i] * inV2.mU32[i]; - return result; -#endif -} - -UVec4 UVec4::operator + (UVec4Arg inV2) -{ -#if defined(JPH_USE_SSE) - return _mm_add_epi32(mValue, inV2.mValue); -#elif defined(JPH_USE_NEON) - return vaddq_u32(mValue, inV2.mValue); -#else - return UVec4(mU32[0] + inV2.mU32[0], - mU32[1] + inV2.mU32[1], - mU32[2] + inV2.mU32[2], - mU32[3] + inV2.mU32[3]); -#endif -} - -UVec4 &UVec4::operator += (UVec4Arg inV2) -{ -#if defined(JPH_USE_SSE) - mValue = _mm_add_epi32(mValue, inV2.mValue); -#elif defined(JPH_USE_NEON) - mValue = vaddq_u32(mValue, inV2.mValue); -#else - for (int i = 0; i < 4; ++i) - mU32[i] += inV2.mU32[i]; -#endif - return *this; -} - -UVec4 UVec4::SplatX() const -{ -#if defined(JPH_USE_SSE) - return _mm_shuffle_epi32(mValue, _MM_SHUFFLE(0, 0, 0, 0)); -#elif defined(JPH_USE_NEON) - return vdupq_laneq_u32(mValue, 0); -#else - return UVec4(mU32[0], mU32[0], mU32[0], mU32[0]); -#endif -} - -UVec4 UVec4::SplatY() const -{ -#if defined(JPH_USE_SSE) - return _mm_shuffle_epi32(mValue, _MM_SHUFFLE(1, 1, 1, 1)); -#elif defined(JPH_USE_NEON) - return vdupq_laneq_u32(mValue, 1); -#else - return UVec4(mU32[1], mU32[1], mU32[1], mU32[1]); -#endif -} - -UVec4 UVec4::SplatZ() const -{ -#if defined(JPH_USE_SSE) - return _mm_shuffle_epi32(mValue, _MM_SHUFFLE(2, 2, 2, 2)); -#elif defined(JPH_USE_NEON) - return vdupq_laneq_u32(mValue, 2); -#else - return UVec4(mU32[2], mU32[2], mU32[2], mU32[2]); -#endif -} - -UVec4 UVec4::SplatW() const -{ -#if defined(JPH_USE_SSE) - return _mm_shuffle_epi32(mValue, _MM_SHUFFLE(3, 3, 3, 3)); -#elif defined(JPH_USE_NEON) - return vdupq_laneq_u32(mValue, 3); -#else - return UVec4(mU32[3], mU32[3], mU32[3], mU32[3]); -#endif -} - -Vec4 UVec4::ToFloat() const -{ -#if defined(JPH_USE_SSE) - return _mm_cvtepi32_ps(mValue); -#elif defined(JPH_USE_NEON) - return vcvtq_f32_s32(mValue); -#else - return Vec4((float)mU32[0], (float)mU32[1], (float)mU32[2], (float)mU32[3]); -#endif -} - -Vec4 UVec4::ReinterpretAsFloat() const -{ -#if defined(JPH_USE_SSE) - return Vec4(_mm_castsi128_ps(mValue)); -#elif defined(JPH_USE_NEON) - return vreinterpretq_f32_s32(mValue); -#else - return *reinterpret_cast(this); -#endif -} - -void UVec4::StoreInt4(uint32 *outV) const -{ -#if defined(JPH_USE_SSE) - _mm_storeu_si128(reinterpret_cast<__m128i *>(outV), mValue); -#elif defined(JPH_USE_NEON) - vst1q_u32(outV, mValue); -#else - for (int i = 0; i < 4; ++i) - outV[i] = mU32[i]; -#endif -} - -void UVec4::StoreInt4Aligned(uint32 *outV) const -{ -#if defined(JPH_USE_SSE) - _mm_store_si128(reinterpret_cast<__m128i *>(outV), mValue); -#elif defined(JPH_USE_NEON) - vst1q_u32(outV, mValue); // ARM doesn't make distinction between aligned or not -#else - for (int i = 0; i < 4; ++i) - outV[i] = mU32[i]; -#endif -} - -int UVec4::CountTrues() const -{ -#if defined(JPH_USE_SSE) - return CountBits(_mm_movemask_ps(_mm_castsi128_ps(mValue))); -#elif defined(JPH_USE_NEON) - return vaddvq_u32(vshrq_n_u32(mValue, 31)); -#else - return (mU32[0] >> 31) + (mU32[1] >> 31) + (mU32[2] >> 31) + (mU32[3] >> 31); -#endif -} - -int UVec4::GetTrues() const -{ -#if defined(JPH_USE_SSE) - return _mm_movemask_ps(_mm_castsi128_ps(mValue)); -#elif defined(JPH_USE_NEON) - int32x4_t shift = JPH_NEON_INT32x4(0, 1, 2, 3); - return vaddvq_u32(vshlq_u32(vshrq_n_u32(mValue, 31), shift)); -#else - return (mU32[0] >> 31) | ((mU32[1] >> 31) << 1) | ((mU32[2] >> 31) << 2) | ((mU32[3] >> 31) << 3); -#endif -} - -bool UVec4::TestAnyTrue() const -{ - return GetTrues() != 0; -} - -bool UVec4::TestAnyXYZTrue() const -{ - return (GetTrues() & 0b111) != 0; -} - -bool UVec4::TestAllTrue() const -{ - return GetTrues() == 0b1111; -} - -bool UVec4::TestAllXYZTrue() const -{ - return (GetTrues() & 0b111) == 0b111; -} - -template -UVec4 UVec4::LogicalShiftLeft() const -{ - static_assert(Count <= 31, "Invalid shift"); - -#if defined(JPH_USE_SSE) - return _mm_slli_epi32(mValue, Count); -#elif defined(JPH_USE_NEON) - return vshlq_n_u32(mValue, Count); -#else - return UVec4(mU32[0] << Count, mU32[1] << Count, mU32[2] << Count, mU32[3] << Count); -#endif -} - -template -UVec4 UVec4::LogicalShiftRight() const -{ - static_assert(Count <= 31, "Invalid shift"); - -#if defined(JPH_USE_SSE) - return _mm_srli_epi32(mValue, Count); -#elif defined(JPH_USE_NEON) - return vshrq_n_u32(mValue, Count); -#else - return UVec4(mU32[0] >> Count, mU32[1] >> Count, mU32[2] >> Count, mU32[3] >> Count); -#endif -} - -template -UVec4 UVec4::ArithmeticShiftRight() const -{ - static_assert(Count <= 31, "Invalid shift"); - -#if defined(JPH_USE_SSE) - return _mm_srai_epi32(mValue, Count); -#elif defined(JPH_USE_NEON) - return vshrq_n_s32(mValue, Count); -#else - return UVec4(uint32(int32_t(mU32[0]) >> Count), - uint32(int32_t(mU32[1]) >> Count), - uint32(int32_t(mU32[2]) >> Count), - uint32(int32_t(mU32[3]) >> Count)); -#endif -} - -UVec4 UVec4::Expand4Uint16Lo() const -{ -#if defined(JPH_USE_SSE) - return _mm_unpacklo_epi16(mValue, _mm_castps_si128(_mm_setzero_ps())); -#elif defined(JPH_USE_NEON) - int16x4_t value = vget_low_s16(mValue); - int16x4_t zero = vdup_n_s16(0); - return vcombine_s16(vzip1_s16(value, zero), vzip2_s16(value, zero)); -#else - return UVec4(mU32[0] & 0xffff, - (mU32[0] >> 16) & 0xffff, - mU32[1] & 0xffff, - (mU32[1] >> 16) & 0xffff); -#endif -} - -UVec4 UVec4::Expand4Uint16Hi() const -{ -#if defined(JPH_USE_SSE) - return _mm_unpackhi_epi16(mValue, _mm_castps_si128(_mm_setzero_ps())); -#elif defined(JPH_USE_NEON) - int16x4_t value = vget_high_s16(mValue); - int16x4_t zero = vdup_n_s16(0); - return vcombine_s16(vzip1_s16(value, zero), vzip2_s16(value, zero)); -#else - return UVec4(mU32[2] & 0xffff, - (mU32[2] >> 16) & 0xffff, - mU32[3] & 0xffff, - (mU32[3] >> 16) & 0xffff); -#endif -} - -UVec4 UVec4::Expand4Byte0() const -{ -#if defined(JPH_USE_SSE4_1) - return _mm_shuffle_epi8(mValue, _mm_set_epi32(int(0xffffff03), int(0xffffff02), int(0xffffff01), int(0xffffff00))); -#elif defined(JPH_USE_NEON) - int8x16_t idx = JPH_NEON_INT8x16(0x00, 0x7f, 0x7f, 0x7f, 0x01, 0x7f, 0x7f, 0x7f, 0x02, 0x7f, 0x7f, 0x7f, 0x03, 0x7f, 0x7f, 0x7f); - return vreinterpretq_u32_s8(vqtbl1q_s8(vreinterpretq_s8_u32(mValue), idx)); -#else - UVec4 result; - for (int i = 0; i < 4; i++) - result.mU32[i] = (mU32[0] >> (i * 8)) & 0xff; - return result; -#endif -} - -UVec4 UVec4::Expand4Byte4() const -{ -#if defined(JPH_USE_SSE4_1) - return _mm_shuffle_epi8(mValue, _mm_set_epi32(int(0xffffff07), int(0xffffff06), int(0xffffff05), int(0xffffff04))); -#elif defined(JPH_USE_NEON) - int8x16_t idx = JPH_NEON_INT8x16(0x04, 0x7f, 0x7f, 0x7f, 0x05, 0x7f, 0x7f, 0x7f, 0x06, 0x7f, 0x7f, 0x7f, 0x07, 0x7f, 0x7f, 0x7f); - return vreinterpretq_u32_s8(vqtbl1q_s8(vreinterpretq_s8_u32(mValue), idx)); -#else - UVec4 result; - for (int i = 0; i < 4; i++) - result.mU32[i] = (mU32[1] >> (i * 8)) & 0xff; - return result; -#endif -} - -UVec4 UVec4::Expand4Byte8() const -{ -#if defined(JPH_USE_SSE4_1) - return _mm_shuffle_epi8(mValue, _mm_set_epi32(int(0xffffff0b), int(0xffffff0a), int(0xffffff09), int(0xffffff08))); -#elif defined(JPH_USE_NEON) - int8x16_t idx = JPH_NEON_INT8x16(0x08, 0x7f, 0x7f, 0x7f, 0x09, 0x7f, 0x7f, 0x7f, 0x0a, 0x7f, 0x7f, 0x7f, 0x0b, 0x7f, 0x7f, 0x7f); - return vreinterpretq_u32_s8(vqtbl1q_s8(vreinterpretq_s8_u32(mValue), idx)); -#else - UVec4 result; - for (int i = 0; i < 4; i++) - result.mU32[i] = (mU32[2] >> (i * 8)) & 0xff; - return result; -#endif -} - -UVec4 UVec4::Expand4Byte12() const -{ -#if defined(JPH_USE_SSE4_1) - return _mm_shuffle_epi8(mValue, _mm_set_epi32(int(0xffffff0f), int(0xffffff0e), int(0xffffff0d), int(0xffffff0c))); -#elif defined(JPH_USE_NEON) - int8x16_t idx = JPH_NEON_INT8x16(0x0c, 0x7f, 0x7f, 0x7f, 0x0d, 0x7f, 0x7f, 0x7f, 0x0e, 0x7f, 0x7f, 0x7f, 0x0f, 0x7f, 0x7f, 0x7f); - return vreinterpretq_u32_s8(vqtbl1q_s8(vreinterpretq_s8_u32(mValue), idx)); -#else - UVec4 result; - for (int i = 0; i < 4; i++) - result.mU32[i] = (mU32[3] >> (i * 8)) & 0xff; - return result; -#endif -} - -UVec4 UVec4::ShiftComponents4Minus(int inCount) const -{ -#if defined(JPH_USE_SSE4_1) || defined(JPH_USE_NEON) - alignas(UVec4) static constexpr uint32 sFourMinusXShuffle[5][4] = - { - { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }, - { 0x0f0e0d0c, 0xffffffff, 0xffffffff, 0xffffffff }, - { 0x0b0a0908, 0x0f0e0d0c, 0xffffffff, 0xffffffff }, - { 0x07060504, 0x0b0a0908, 0x0f0e0d0c, 0xffffffff }, - { 0x03020100, 0x07060504, 0x0b0a0908, 0x0f0e0d0c } - }; -#endif - -#if defined(JPH_USE_SSE4_1) - return _mm_shuffle_epi8(mValue, *reinterpret_cast(sFourMinusXShuffle[inCount])); -#elif defined(JPH_USE_NEON) - uint8x16_t idx = vreinterpretq_u8_u32(*reinterpret_cast(sFourMinusXShuffle[inCount])); - return vreinterpretq_u32_s8(vqtbl1q_s8(vreinterpretq_s8_u32(mValue), idx)); -#else - UVec4 result = UVec4::sZero(); - for (int i = 0; i < inCount; i++) - result.mU32[i] = mU32[i + 4 - inCount]; - return result; -#endif -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/UVec8.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/UVec8.h deleted file mode 100644 index bab31cbeb00..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/UVec8.h +++ /dev/null @@ -1,100 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -class [[nodiscard]] UVec8 -{ -public: - JPH_OVERRIDE_NEW_DELETE - - UVec8() = default; ///< Intentionally not initialized for performance reasons - UVec8(const UVec8 &inRHS) = default; - JPH_INLINE UVec8(__m256i inRHS) : mValue(inRHS) { } - - /// Set 256 bit vector from 2 128 bit vectors - JPH_INLINE UVec8(UVec4Arg inLo, UVec4Arg inHi); - - /// Comparison - JPH_INLINE bool operator == (UVec8Arg inV2) const; - JPH_INLINE bool operator != (UVec8Arg inV2) const { return !(*this == inV2); } - - /// Replicate int across all components - static JPH_INLINE UVec8 sReplicate(uint32 inV); - - /// Replicate the X component of inV to all components - static JPH_INLINE UVec8 sSplatX(UVec4Arg inV); - - /// Replicate the Y component of inV to all components - static JPH_INLINE UVec8 sSplatY(UVec4Arg inV); - - /// Replicate the Z component of inV to all components - static JPH_INLINE UVec8 sSplatZ(UVec4Arg inV); - - /// Equals (component wise) - static JPH_INLINE UVec8 sEquals(UVec8Arg inV1, UVec8Arg inV2); - - /// Component wise select, returns inV1 when highest bit of inControl = 0 and inV2 when highest bit of inControl = 1 - static JPH_INLINE UVec8 sSelect(UVec8Arg inV1, UVec8Arg inV2, UVec8Arg inControl); - - /// Logical or - static JPH_INLINE UVec8 sOr(UVec8Arg inV1, UVec8Arg inV2); - - /// Logical xor - static JPH_INLINE UVec8 sXor(UVec8Arg inV1, UVec8Arg inV2); - - /// Logical and - static JPH_INLINE UVec8 sAnd(UVec8Arg inV1, UVec8Arg inV2); - - /// Get float component by index - JPH_INLINE uint32 operator [] (uint inCoordinate) const { JPH_ASSERT(inCoordinate < 8); return mU32[inCoordinate]; } - JPH_INLINE uint32 & operator [] (uint inCoordinate) { JPH_ASSERT(inCoordinate < 8); return mU32[inCoordinate]; } - - /// 256 bit variant of Vec::Swizzle (no cross 128 bit lane swizzle) - template - JPH_INLINE UVec8 Swizzle() const; - - /// Test if any of the components are true (true is when highest bit of component is set) - JPH_INLINE bool TestAnyTrue() const; - - /// Test if all components are true (true is when highest bit of component is set) - JPH_INLINE bool TestAllTrue() const; - - /// Fetch the lower 128 bit from a 256 bit variable - JPH_INLINE UVec4 LowerVec4() const; - - /// Fetch the higher 128 bit from a 256 bit variable - JPH_INLINE UVec4 UpperVec4() const; - - /// Converts int to float - JPH_INLINE Vec8 ToFloat() const; - - /// Shift all components by Count bits to the left (filling with zeros from the left) - template - JPH_INLINE UVec8 LogicalShiftLeft() const; - - /// Shift all components by Count bits to the right (filling with zeros from the right) - template - JPH_INLINE UVec8 LogicalShiftRight() const; - - /// Shift all components by Count bits to the right (shifting in the value of the highest bit) - template - JPH_INLINE UVec8 ArithmeticShiftRight() const; - - union - { - __m256i mValue; - uint32 mU32[8]; - }; -}; - -static_assert(is_trivial(), "Is supposed to be a trivial type!"); - -JPH_NAMESPACE_END - -#include "UVec8.inl" diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/UVec8.inl b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/UVec8.inl deleted file mode 100644 index 0d7d3117c3d..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/UVec8.inl +++ /dev/null @@ -1,138 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -JPH_NAMESPACE_BEGIN - -UVec8::UVec8(UVec4Arg inLo, UVec4Arg inHi) : - mValue(_mm256_insertf128_si256(_mm256_castsi128_si256(inLo.mValue), inHi.mValue, 1)) -{ -} - -bool UVec8::operator == (UVec8Arg inV2) const -{ - return sEquals(*this, inV2).TestAllTrue(); -} - -UVec8 UVec8::sReplicate(uint32 inV) -{ - return _mm256_set1_epi32(int(inV)); -} - -UVec8 UVec8::sSplatX(UVec4Arg inV) -{ - return _mm256_set1_epi32(inV.GetX()); -} - -UVec8 UVec8::sSplatY(UVec4Arg inV) -{ - return _mm256_set1_epi32(inV.GetY()); -} - -UVec8 UVec8::sSplatZ(UVec4Arg inV) -{ - return _mm256_set1_epi32(inV.GetZ()); -} - -UVec8 UVec8::sEquals(UVec8Arg inV1, UVec8Arg inV2) -{ -#ifdef JPH_USE_AVX2 - return _mm256_cmpeq_epi32(inV1.mValue, inV2.mValue); -#else - return UVec8(UVec4::sEquals(inV1.LowerVec4(), inV2.LowerVec4()), UVec4::sEquals(inV1.UpperVec4(), inV2.UpperVec4())); -#endif -} - -UVec8 UVec8::sSelect(UVec8Arg inV1, UVec8Arg inV2, UVec8Arg inControl) -{ - return _mm256_castps_si256(_mm256_blendv_ps(_mm256_castsi256_ps(inV1.mValue), _mm256_castsi256_ps(inV2.mValue), _mm256_castsi256_ps(inControl.mValue))); -} - -UVec8 UVec8::sOr(UVec8Arg inV1, UVec8Arg inV2) -{ - return _mm256_castps_si256(_mm256_or_ps(_mm256_castsi256_ps(inV1.mValue), _mm256_castsi256_ps(inV2.mValue))); -} - -UVec8 UVec8::sXor(UVec8Arg inV1, UVec8Arg inV2) -{ - return _mm256_castps_si256(_mm256_xor_ps(_mm256_castsi256_ps(inV1.mValue), _mm256_castsi256_ps(inV2.mValue))); -} - -UVec8 UVec8::sAnd(UVec8Arg inV1, UVec8Arg inV2) -{ - return _mm256_castps_si256(_mm256_and_ps(_mm256_castsi256_ps(inV1.mValue), _mm256_castsi256_ps(inV2.mValue))); -} - -template -UVec8 UVec8::Swizzle() const -{ - static_assert(SwizzleX <= 3, "SwizzleX template parameter out of range"); - static_assert(SwizzleY <= 3, "SwizzleY template parameter out of range"); - static_assert(SwizzleZ <= 3, "SwizzleZ template parameter out of range"); - static_assert(SwizzleW <= 3, "SwizzleW template parameter out of range"); - - return _mm256_castps_si256(_mm256_shuffle_ps(_mm256_castsi256_ps(mValue), _mm256_castsi256_ps(mValue), _MM_SHUFFLE(SwizzleW, SwizzleZ, SwizzleY, SwizzleX))); -} - -bool UVec8::TestAnyTrue() const -{ - return _mm256_movemask_ps(_mm256_castsi256_ps(mValue)) != 0; -} - -bool UVec8::TestAllTrue() const -{ - return _mm256_movemask_ps(_mm256_castsi256_ps(mValue)) == 0xff; -} - -UVec4 UVec8::LowerVec4() const -{ - return _mm256_castsi256_si128(mValue); -} - -UVec4 UVec8::UpperVec4() const -{ - return _mm_castps_si128(_mm256_extractf128_ps(_mm256_castsi256_ps(mValue), 1)); -} - -Vec8 UVec8::ToFloat() const -{ - return _mm256_cvtepi32_ps(mValue); -} - -template -UVec8 UVec8::LogicalShiftLeft() const -{ - static_assert(Count <= 31, "Invalid shift"); - -#ifdef JPH_USE_AVX2 - return _mm256_slli_epi32(mValue, Count); -#else - return UVec8(LowerVec4().LogicalShiftLeft(), UpperVec4().LogicalShiftLeft()); -#endif -} - -template -UVec8 UVec8::LogicalShiftRight() const -{ - static_assert(Count <= 31, "Invalid shift"); - -#ifdef JPH_USE_AVX2 - return _mm256_srli_epi32(mValue, Count); -#else - return UVec8(LowerVec4().LogicalShiftRight(), UpperVec4().LogicalShiftRight()); -#endif -} - -template -UVec8 UVec8::ArithmeticShiftRight() const -{ - static_assert(Count <= 31, "Invalid shift"); - -#ifdef JPH_USE_AVX2 - return _mm256_srai_epi32(mValue, Count); -#else - return UVec8(LowerVec4().ArithmeticShiftRight(), UpperVec4().ArithmeticShiftRight()); -#endif -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Vec3.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Vec3.cpp deleted file mode 100644 index 31283d3250d..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Vec3.cpp +++ /dev/null @@ -1,59 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include - -JPH_NAMESPACE_BEGIN - -static void sCreateVertices(std::unordered_set &ioVertices, Vec3Arg inDir1, Vec3Arg inDir2, Vec3Arg inDir3, int inLevel) -{ - Vec3 center1 = (inDir1 + inDir2).Normalized(); - Vec3 center2 = (inDir2 + inDir3).Normalized(); - Vec3 center3 = (inDir3 + inDir1).Normalized(); - - ioVertices.insert(center1); - ioVertices.insert(center2); - ioVertices.insert(center3); - - if (inLevel > 0) - { - int new_level = inLevel - 1; - sCreateVertices(ioVertices, inDir1, center1, center3, new_level); - sCreateVertices(ioVertices, center1, center2, center3, new_level); - sCreateVertices(ioVertices, center1, inDir2, center2, new_level); - sCreateVertices(ioVertices, center3, center2, inDir3, new_level); - } -} - -const std::vector Vec3::sUnitSphere = []() { - - const int level = 3; - - std::unordered_set verts; - - // Add unit axis - verts.insert(Vec3::sAxisX()); - verts.insert(-Vec3::sAxisX()); - verts.insert(Vec3::sAxisY()); - verts.insert(-Vec3::sAxisY()); - verts.insert(Vec3::sAxisZ()); - verts.insert(-Vec3::sAxisZ()); - - // Subdivide - sCreateVertices(verts, Vec3::sAxisX(), Vec3::sAxisY(), Vec3::sAxisZ(), level); - sCreateVertices(verts, -Vec3::sAxisX(), Vec3::sAxisY(), Vec3::sAxisZ(), level); - sCreateVertices(verts, Vec3::sAxisX(), -Vec3::sAxisY(), Vec3::sAxisZ(), level); - sCreateVertices(verts, -Vec3::sAxisX(), -Vec3::sAxisY(), Vec3::sAxisZ(), level); - sCreateVertices(verts, Vec3::sAxisX(), Vec3::sAxisY(), -Vec3::sAxisZ(), level); - sCreateVertices(verts, -Vec3::sAxisX(), Vec3::sAxisY(), -Vec3::sAxisZ(), level); - sCreateVertices(verts, Vec3::sAxisX(), -Vec3::sAxisY(), -Vec3::sAxisZ(), level); - sCreateVertices(verts, -Vec3::sAxisX(), -Vec3::sAxisY(), -Vec3::sAxisZ(), level); - - return std::vector(verts.begin(), verts.end()); -}(); - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Vec3.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Vec3.h deleted file mode 100644 index 5ca71896542..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Vec3.h +++ /dev/null @@ -1,294 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -/// 3 component vector (stored as 4 vectors). -/// Note that we keep the 4th component the same as the 3rd component to avoid divisions by zero when JPH_FLOATING_POINT_EXCEPTIONS_ENABLED defined -class [[nodiscard]] alignas(JPH_VECTOR_ALIGNMENT) Vec3 -{ -public: - JPH_OVERRIDE_NEW_DELETE - - // Underlying vector type -#if defined(JPH_USE_SSE) - using Type = __m128; -#elif defined(JPH_USE_NEON) - using Type = float32x4_t; -#else - using Type = Vec4::Type; -#endif - - // Argument type - using ArgType = Vec3Arg; - - /// Constructor - Vec3() = default; ///< Intentionally not initialized for performance reasons - Vec3(const Vec3 &inRHS) = default; - Vec3 & operator = (const Vec3 &inRHS) = default; - explicit JPH_INLINE Vec3(Vec4Arg inRHS); - JPH_INLINE Vec3(Type inRHS) : mValue(inRHS) { CheckW(); } - - /// Load 3 floats from memory - explicit JPH_INLINE Vec3(const Float3 &inV); - - /// Create a vector from 3 components - JPH_INLINE Vec3(float inX, float inY, float inZ); - - /// Vector with all zeros - static JPH_INLINE Vec3 sZero(); - - /// Vector with all NaN's - static JPH_INLINE Vec3 sNaN(); - - /// Vectors with the principal axis - static JPH_INLINE Vec3 sAxisX() { return Vec3(1, 0, 0); } - static JPH_INLINE Vec3 sAxisY() { return Vec3(0, 1, 0); } - static JPH_INLINE Vec3 sAxisZ() { return Vec3(0, 0, 1); } - - /// Replicate inV across all components - static JPH_INLINE Vec3 sReplicate(float inV); - - /// Load 3 floats from memory (reads 32 bits extra which it doesn't use) - static JPH_INLINE Vec3 sLoadFloat3Unsafe(const Float3 &inV); - - /// Return the minimum value of each of the components - static JPH_INLINE Vec3 sMin(Vec3Arg inV1, Vec3Arg inV2); - - /// Return the maximum of each of the components - static JPH_INLINE Vec3 sMax(Vec3Arg inV1, Vec3Arg inV2); - - /// Clamp a vector between min and max (component wise) - static JPH_INLINE Vec3 sClamp(Vec3Arg inV, Vec3Arg inMin, Vec3Arg inMax); - - /// Equals (component wise) - static JPH_INLINE UVec4 sEquals(Vec3Arg inV1, Vec3Arg inV2); - - /// Less than (component wise) - static JPH_INLINE UVec4 sLess(Vec3Arg inV1, Vec3Arg inV2); - - /// Less than or equal (component wise) - static JPH_INLINE UVec4 sLessOrEqual(Vec3Arg inV1, Vec3Arg inV2); - - /// Greater than (component wise) - static JPH_INLINE UVec4 sGreater(Vec3Arg inV1, Vec3Arg inV2); - - /// Greater than or equal (component wise) - static JPH_INLINE UVec4 sGreaterOrEqual(Vec3Arg inV1, Vec3Arg inV2); - - /// Calculates inMul1 * inMul2 + inAdd - static JPH_INLINE Vec3 sFusedMultiplyAdd(Vec3Arg inMul1, Vec3Arg inMul2, Vec3Arg inAdd); - - /// Component wise select, returns inV1 when highest bit of inControl = 0 and inV2 when highest bit of inControl = 1 - static JPH_INLINE Vec3 sSelect(Vec3Arg inV1, Vec3Arg inV2, UVec4Arg inControl); - - /// Logical or (component wise) - static JPH_INLINE Vec3 sOr(Vec3Arg inV1, Vec3Arg inV2); - - /// Logical xor (component wise) - static JPH_INLINE Vec3 sXor(Vec3Arg inV1, Vec3Arg inV2); - - /// Logical and (component wise) - static JPH_INLINE Vec3 sAnd(Vec3Arg inV1, Vec3Arg inV2); - - /// Get unit vector given spherical coordinates - /// inTheta \f$\in [0, \pi]\f$ is angle between vector and z-axis - /// inPhi \f$\in [0, 2 \pi]\f$ is the angle in the xy-plane starting from the x axis and rotating counter clockwise around the z-axis - static JPH_INLINE Vec3 sUnitSpherical(float inTheta, float inPhi); - - /// A set of vectors uniformly spanning the surface of a unit sphere, usable for debug purposes - JPH_EXPORT static const std::vector sUnitSphere; - - /// Get random unit vector - template - static inline Vec3 sRandom(Random &inRandom); - - /// Get individual components -#if defined(JPH_USE_SSE) - JPH_INLINE float GetX() const { return _mm_cvtss_f32(mValue); } - JPH_INLINE float GetY() const { return mF32[1]; } - JPH_INLINE float GetZ() const { return mF32[2]; } -#elif defined(JPH_USE_NEON) - JPH_INLINE float GetX() const { return vgetq_lane_f32(mValue, 0); } - JPH_INLINE float GetY() const { return vgetq_lane_f32(mValue, 1); } - JPH_INLINE float GetZ() const { return vgetq_lane_f32(mValue, 2); } -#else - JPH_INLINE float GetX() const { return mF32[0]; } - JPH_INLINE float GetY() const { return mF32[1]; } - JPH_INLINE float GetZ() const { return mF32[2]; } -#endif - - /// Set individual components - JPH_INLINE void SetX(float inX) { mF32[0] = inX; } - JPH_INLINE void SetY(float inY) { mF32[1] = inY; } - JPH_INLINE void SetZ(float inZ) { mF32[2] = mF32[3] = inZ; } // Assure Z and W are the same - - /// Set all components - JPH_INLINE void Set(float inX, float inY, float inZ) { *this = Vec3(inX, inY, inZ); } - - /// Get float component by index - JPH_INLINE float operator [] (uint inCoordinate) const { JPH_ASSERT(inCoordinate < 3); return mF32[inCoordinate]; } - - /// Set float component by index - JPH_INLINE void SetComponent(uint inCoordinate, float inValue) { JPH_ASSERT(inCoordinate < 3); mF32[inCoordinate] = inValue; mValue = sFixW(mValue); } // Assure Z and W are the same - - /// Comparison - JPH_INLINE bool operator == (Vec3Arg inV2) const; - JPH_INLINE bool operator != (Vec3Arg inV2) const { return !(*this == inV2); } - - /// Test if two vectors are close - JPH_INLINE bool IsClose(Vec3Arg inV2, float inMaxDistSq = 1.0e-12f) const; - - /// Test if vector is near zero - JPH_INLINE bool IsNearZero(float inMaxDistSq = 1.0e-12f) const; - - /// Test if vector is normalized - JPH_INLINE bool IsNormalized(float inTolerance = 1.0e-6f) const; - - /// Test if vector contains NaN elements - JPH_INLINE bool IsNaN() const; - - /// Multiply two float vectors (component wise) - JPH_INLINE Vec3 operator * (Vec3Arg inV2) const; - - /// Multiply vector with float - JPH_INLINE Vec3 operator * (float inV2) const; - - /// Multiply vector with float - friend JPH_INLINE Vec3 operator * (float inV1, Vec3Arg inV2); - - /// Divide vector by float - JPH_INLINE Vec3 operator / (float inV2) const; - - /// Multiply vector with float - JPH_INLINE Vec3 & operator *= (float inV2); - - /// Multiply vector with vector - JPH_INLINE Vec3 & operator *= (Vec3Arg inV2); - - /// Divide vector by float - JPH_INLINE Vec3 & operator /= (float inV2); - - /// Add two float vectors (component wise) - JPH_INLINE Vec3 operator + (Vec3Arg inV2) const; - - /// Add two float vectors (component wise) - JPH_INLINE Vec3 & operator += (Vec3Arg inV2); - - /// Negate - JPH_INLINE Vec3 operator - () const; - - /// Subtract two float vectors (component wise) - JPH_INLINE Vec3 operator - (Vec3Arg inV2) const; - - /// Add two float vectors (component wise) - JPH_INLINE Vec3 & operator -= (Vec3Arg inV2); - - /// Divide (component wise) - JPH_INLINE Vec3 operator / (Vec3Arg inV2) const; - - /// Swizzle the elements in inV - template - JPH_INLINE Vec3 Swizzle() const; - - /// Replicate the X component to all components - JPH_INLINE Vec4 SplatX() const; - - /// Replicate the Y component to all components - JPH_INLINE Vec4 SplatY() const; - - /// Replicate the Z component to all components - JPH_INLINE Vec4 SplatZ() const; - - /// Get index of component with lowest value - JPH_INLINE int GetLowestComponentIndex() const; - - /// Get index of component with highest value - JPH_INLINE int GetHighestComponentIndex() const; - - /// Return the absolute value of each of the components - JPH_INLINE Vec3 Abs() const; - - /// Reciprocal vector (1 / value) for each of the components - JPH_INLINE Vec3 Reciprocal() const; - - /// Cross product - JPH_INLINE Vec3 Cross(Vec3Arg inV2) const; - - /// Dot product, returns the dot product in X, Y and Z components - JPH_INLINE Vec3 DotV(Vec3Arg inV2) const; - - /// Dot product, returns the dot product in X, Y, Z and W components - JPH_INLINE Vec4 DotV4(Vec3Arg inV2) const; - - /// Dot product - JPH_INLINE float Dot(Vec3Arg inV2) const; - - /// Squared length of vector - JPH_INLINE float LengthSq() const; - - /// Length of vector - JPH_INLINE float Length() const; - - /// Normalize vector - JPH_INLINE Vec3 Normalized() const; - - /// Normalize vector or return inZeroValue if the length of the vector is zero - JPH_INLINE Vec3 NormalizedOr(Vec3Arg inZeroValue) const; - - /// Store 3 floats to memory - JPH_INLINE void StoreFloat3(Float3 *outV) const; - - /// Convert each component from a float to an int - JPH_INLINE UVec4 ToInt() const; - - /// Reinterpret Vec3 as a UVec4 (doesn't change the bits) - JPH_INLINE UVec4 ReinterpretAsInt() const; - - /// Get the minimum of X, Y and Z - JPH_INLINE float ReduceMin() const; - - /// Get the maximum of X, Y and Z - JPH_INLINE float ReduceMax() const; - - /// Component wise square root - JPH_INLINE Vec3 Sqrt() const; - - /// Get normalized vector that is perpendicular to this vector - JPH_INLINE Vec3 GetNormalizedPerpendicular() const; - - /// Get vector that contains the sign of each element (returns 1.0f if positive, -1.0f if negative) - JPH_INLINE Vec3 GetSign() const; - - /// To String - friend ostream & operator << (ostream &inStream, Vec3Arg inV) - { - inStream << inV.mF32[0] << ", " << inV.mF32[1] << ", " << inV.mF32[2]; - return inStream; - } - - /// Internal helper function that checks that W is equal to Z, so e.g. dividing by it should not generate div by 0 - JPH_INLINE void CheckW() const; - - /// Internal helper function that ensures that the Z component is replicated to the W component to prevent divisions by zero - static JPH_INLINE Type sFixW(Type inValue); - - union - { - Type mValue; - float mF32[4]; - }; -}; - -static_assert(is_trivial(), "Is supposed to be a trivial type!"); - -JPH_NAMESPACE_END - -#include "Vec3.inl" diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Vec3.inl b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Vec3.inl deleted file mode 100644 index d53f1828dc7..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Vec3.inl +++ /dev/null @@ -1,845 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include -#include -#include - -JPH_SUPPRESS_WARNINGS_STD_BEGIN -#include -JPH_SUPPRESS_WARNINGS_STD_END - -// Create a std::hash for Vec3 -JPH_MAKE_HASHABLE(JPH::Vec3, t.GetX(), t.GetY(), t.GetZ()) - -JPH_NAMESPACE_BEGIN - -void Vec3::CheckW() const -{ -#ifdef JPH_FLOATING_POINT_EXCEPTIONS_ENABLED - // Avoid asserts when both components are NaN - JPH_ASSERT(reinterpret_cast(mF32)[2] == reinterpret_cast(mF32)[3]); -#endif // JPH_FLOATING_POINT_EXCEPTIONS_ENABLED -} - -JPH_INLINE Vec3::Type Vec3::sFixW(Type inValue) -{ -#ifdef JPH_FLOATING_POINT_EXCEPTIONS_ENABLED - #if defined(JPH_USE_SSE) - return _mm_shuffle_ps(inValue, inValue, _MM_SHUFFLE(2, 2, 1, 0)); - #elif defined(JPH_USE_NEON) - return JPH_NEON_SHUFFLE_F32x4(inValue, inValue, 0, 1, 2, 2); - #else - Type value; - value.mData[0] = inValue.mData[0]; - value.mData[1] = inValue.mData[1]; - value.mData[2] = inValue.mData[2]; - value.mData[3] = inValue.mData[2]; - return value; - #endif -#else - return inValue; -#endif // JPH_FLOATING_POINT_EXCEPTIONS_ENABLED -} - -Vec3::Vec3(Vec4Arg inRHS) : - mValue(sFixW(inRHS.mValue)) -{ -} - -Vec3::Vec3(const Float3 &inV) -{ -#if defined(JPH_USE_SSE) - Type x = _mm_load_ss(&inV.x); - Type y = _mm_load_ss(&inV.y); - Type z = _mm_load_ss(&inV.z); - Type xy = _mm_unpacklo_ps(x, y); - mValue = _mm_shuffle_ps(xy, z, _MM_SHUFFLE(0, 0, 1, 0)); // Assure Z and W are the same -#elif defined(JPH_USE_NEON) - float32x2_t xy = vld1_f32(&inV.x); - float32x2_t zz = vdup_n_f32(inV.z); // Assure Z and W are the same - mValue = vcombine_f32(xy, zz); -#else - mF32[0] = inV[0]; - mF32[1] = inV[1]; - mF32[2] = inV[2]; - #ifdef JPH_FLOATING_POINT_EXCEPTIONS_ENABLED - mF32[3] = inV[2]; - #endif -#endif -} - -Vec3::Vec3(float inX, float inY, float inZ) -{ -#if defined(JPH_USE_SSE) - mValue = _mm_set_ps(inZ, inZ, inY, inX); -#elif defined(JPH_USE_NEON) - uint32x2_t xy = vcreate_f32(static_cast(*reinterpret_cast(&inX)) | (static_cast(*reinterpret_cast(&inY)) << 32)); - uint32x2_t zz = vcreate_f32(static_cast(*reinterpret_cast(&inZ)) | (static_cast(*reinterpret_cast(&inZ)) << 32)); - mValue = vcombine_f32(xy, zz); -#else - mF32[0] = inX; - mF32[1] = inY; - mF32[2] = inZ; - #ifdef JPH_FLOATING_POINT_EXCEPTIONS_ENABLED - mF32[3] = inZ; - #endif -#endif -} - -template -Vec3 Vec3::Swizzle() const -{ - static_assert(SwizzleX <= 3, "SwizzleX template parameter out of range"); - static_assert(SwizzleY <= 3, "SwizzleY template parameter out of range"); - static_assert(SwizzleZ <= 3, "SwizzleZ template parameter out of range"); - -#if defined(JPH_USE_SSE) - return _mm_shuffle_ps(mValue, mValue, _MM_SHUFFLE(SwizzleZ, SwizzleZ, SwizzleY, SwizzleX)); // Assure Z and W are the same -#elif defined(JPH_USE_NEON) - return JPH_NEON_SHUFFLE_F32x4(mValue, mValue, SwizzleX, SwizzleY, SwizzleZ, SwizzleZ); -#else - return Vec3(mF32[SwizzleX], mF32[SwizzleY], mF32[SwizzleZ]); -#endif -} - -Vec3 Vec3::sZero() -{ -#if defined(JPH_USE_SSE) - return _mm_setzero_ps(); -#elif defined(JPH_USE_NEON) - return vdupq_n_f32(0); -#else - return Vec3(0, 0, 0); -#endif -} - -Vec3 Vec3::sReplicate(float inV) -{ -#if defined(JPH_USE_SSE) - return _mm_set1_ps(inV); -#elif defined(JPH_USE_NEON) - return vdupq_n_f32(inV); -#else - return Vec3(inV, inV, inV); -#endif -} - -Vec3 Vec3::sNaN() -{ - return sReplicate(numeric_limits::quiet_NaN()); -} - -Vec3 Vec3::sLoadFloat3Unsafe(const Float3 &inV) -{ -#if defined(JPH_USE_SSE) - Type v = _mm_loadu_ps(&inV.x); -#elif defined(JPH_USE_NEON) - Type v = vld1q_f32(&inV.x); -#else - Type v = { inV.x, inV.y, inV.z }; -#endif - return sFixW(v); -} - -Vec3 Vec3::sMin(Vec3Arg inV1, Vec3Arg inV2) -{ -#if defined(JPH_USE_SSE) - return _mm_min_ps(inV1.mValue, inV2.mValue); -#elif defined(JPH_USE_NEON) - return vminq_f32(inV1.mValue, inV2.mValue); -#else - return Vec3(min(inV1.mF32[0], inV2.mF32[0]), - min(inV1.mF32[1], inV2.mF32[1]), - min(inV1.mF32[2], inV2.mF32[2])); -#endif -} - -Vec3 Vec3::sMax(Vec3Arg inV1, Vec3Arg inV2) -{ -#if defined(JPH_USE_SSE) - return _mm_max_ps(inV1.mValue, inV2.mValue); -#elif defined(JPH_USE_NEON) - return vmaxq_f32(inV1.mValue, inV2.mValue); -#else - return Vec3(max(inV1.mF32[0], inV2.mF32[0]), - max(inV1.mF32[1], inV2.mF32[1]), - max(inV1.mF32[2], inV2.mF32[2])); -#endif -} - -Vec3 Vec3::sClamp(Vec3Arg inV, Vec3Arg inMin, Vec3Arg inMax) -{ - return sMax(sMin(inV, inMax), inMin); -} - -UVec4 Vec3::sEquals(Vec3Arg inV1, Vec3Arg inV2) -{ -#if defined(JPH_USE_SSE) - return _mm_castps_si128(_mm_cmpeq_ps(inV1.mValue, inV2.mValue)); -#elif defined(JPH_USE_NEON) - return vceqq_f32(inV1.mValue, inV2.mValue); -#else - uint32 z = inV1.mF32[2] == inV2.mF32[2]? 0xffffffffu : 0; - return UVec4(inV1.mF32[0] == inV2.mF32[0]? 0xffffffffu : 0, - inV1.mF32[1] == inV2.mF32[1]? 0xffffffffu : 0, - z, - z); -#endif -} - -UVec4 Vec3::sLess(Vec3Arg inV1, Vec3Arg inV2) -{ -#if defined(JPH_USE_SSE) - return _mm_castps_si128(_mm_cmplt_ps(inV1.mValue, inV2.mValue)); -#elif defined(JPH_USE_NEON) - return vcltq_f32(inV1.mValue, inV2.mValue); -#else - uint32 z = inV1.mF32[2] < inV2.mF32[2]? 0xffffffffu : 0; - return UVec4(inV1.mF32[0] < inV2.mF32[0]? 0xffffffffu : 0, - inV1.mF32[1] < inV2.mF32[1]? 0xffffffffu : 0, - z, - z); -#endif -} - -UVec4 Vec3::sLessOrEqual(Vec3Arg inV1, Vec3Arg inV2) -{ -#if defined(JPH_USE_SSE) - return _mm_castps_si128(_mm_cmple_ps(inV1.mValue, inV2.mValue)); -#elif defined(JPH_USE_NEON) - return vcleq_f32(inV1.mValue, inV2.mValue); -#else - uint32 z = inV1.mF32[2] <= inV2.mF32[2]? 0xffffffffu : 0; - return UVec4(inV1.mF32[0] <= inV2.mF32[0]? 0xffffffffu : 0, - inV1.mF32[1] <= inV2.mF32[1]? 0xffffffffu : 0, - z, - z); -#endif -} - -UVec4 Vec3::sGreater(Vec3Arg inV1, Vec3Arg inV2) -{ -#if defined(JPH_USE_SSE) - return _mm_castps_si128(_mm_cmpgt_ps(inV1.mValue, inV2.mValue)); -#elif defined(JPH_USE_NEON) - return vcgtq_f32(inV1.mValue, inV2.mValue); -#else - uint32 z = inV1.mF32[2] > inV2.mF32[2]? 0xffffffffu : 0; - return UVec4(inV1.mF32[0] > inV2.mF32[0]? 0xffffffffu : 0, - inV1.mF32[1] > inV2.mF32[1]? 0xffffffffu : 0, - z, - z); -#endif -} - -UVec4 Vec3::sGreaterOrEqual(Vec3Arg inV1, Vec3Arg inV2) -{ -#if defined(JPH_USE_SSE) - return _mm_castps_si128(_mm_cmpge_ps(inV1.mValue, inV2.mValue)); -#elif defined(JPH_USE_NEON) - return vcgeq_f32(inV1.mValue, inV2.mValue); -#else - uint32 z = inV1.mF32[2] >= inV2.mF32[2]? 0xffffffffu : 0; - return UVec4(inV1.mF32[0] >= inV2.mF32[0]? 0xffffffffu : 0, - inV1.mF32[1] >= inV2.mF32[1]? 0xffffffffu : 0, - z, - z); -#endif -} - -Vec3 Vec3::sFusedMultiplyAdd(Vec3Arg inMul1, Vec3Arg inMul2, Vec3Arg inAdd) -{ -#if defined(JPH_USE_SSE) - #ifdef JPH_USE_FMADD - return _mm_fmadd_ps(inMul1.mValue, inMul2.mValue, inAdd.mValue); - #else - return _mm_add_ps(_mm_mul_ps(inMul1.mValue, inMul2.mValue), inAdd.mValue); - #endif -#elif defined(JPH_USE_NEON) - return vmlaq_f32(inAdd.mValue, inMul1.mValue, inMul2.mValue); -#else - return Vec3(inMul1.mF32[0] * inMul2.mF32[0] + inAdd.mF32[0], - inMul1.mF32[1] * inMul2.mF32[1] + inAdd.mF32[1], - inMul1.mF32[2] * inMul2.mF32[2] + inAdd.mF32[2]); -#endif -} - -Vec3 Vec3::sSelect(Vec3Arg inV1, Vec3Arg inV2, UVec4Arg inControl) -{ -#if defined(JPH_USE_SSE4_1) - Type v = _mm_blendv_ps(inV1.mValue, inV2.mValue, _mm_castsi128_ps(inControl.mValue)); - return sFixW(v); -#elif defined(JPH_USE_NEON) - Type v = vbslq_f32(vshrq_n_s32(inControl.mValue, 31), inV2.mValue, inV1.mValue); - return sFixW(v); -#else - Vec3 result; - for (int i = 0; i < 3; i++) - result.mF32[i] = inControl.mU32[i] ? inV2.mF32[i] : inV1.mF32[i]; -#ifdef JPH_FLOATING_POINT_EXCEPTIONS_ENABLED - result.mF32[3] = result.mF32[2]; -#endif // JPH_FLOATING_POINT_EXCEPTIONS_ENABLED - return result; -#endif -} - -Vec3 Vec3::sOr(Vec3Arg inV1, Vec3Arg inV2) -{ -#if defined(JPH_USE_SSE) - return _mm_or_ps(inV1.mValue, inV2.mValue); -#elif defined(JPH_USE_NEON) - return vorrq_s32(inV1.mValue, inV2.mValue); -#else - return Vec3(UVec4::sOr(inV1.ReinterpretAsInt(), inV2.ReinterpretAsInt()).ReinterpretAsFloat()); -#endif -} - -Vec3 Vec3::sXor(Vec3Arg inV1, Vec3Arg inV2) -{ -#if defined(JPH_USE_SSE) - return _mm_xor_ps(inV1.mValue, inV2.mValue); -#elif defined(JPH_USE_NEON) - return veorq_s32(inV1.mValue, inV2.mValue); -#else - return Vec3(UVec4::sXor(inV1.ReinterpretAsInt(), inV2.ReinterpretAsInt()).ReinterpretAsFloat()); -#endif -} - -Vec3 Vec3::sAnd(Vec3Arg inV1, Vec3Arg inV2) -{ -#if defined(JPH_USE_SSE) - return _mm_and_ps(inV1.mValue, inV2.mValue); -#elif defined(JPH_USE_NEON) - return vandq_s32(inV1.mValue, inV2.mValue); -#else - return Vec3(UVec4::sAnd(inV1.ReinterpretAsInt(), inV2.ReinterpretAsInt()).ReinterpretAsFloat()); -#endif -} - -Vec3 Vec3::sUnitSpherical(float inTheta, float inPhi) -{ - Vec4 s, c; - Vec4(inTheta, inPhi, 0, 0).SinCos(s, c); - return Vec3(s.GetX() * c.GetY(), s.GetX() * s.GetY(), c.GetX()); -} - -template -Vec3 Vec3::sRandom(Random &inRandom) -{ - std::uniform_real_distribution zero_to_one(0.0f, 1.0f); - float theta = JPH_PI * zero_to_one(inRandom); - float phi = 2.0f * JPH_PI * zero_to_one(inRandom); - return sUnitSpherical(theta, phi); -} - -bool Vec3::operator == (Vec3Arg inV2) const -{ - return sEquals(*this, inV2).TestAllXYZTrue(); -} - -bool Vec3::IsClose(Vec3Arg inV2, float inMaxDistSq) const -{ - return (inV2 - *this).LengthSq() <= inMaxDistSq; -} - -bool Vec3::IsNearZero(float inMaxDistSq) const -{ - return LengthSq() <= inMaxDistSq; -} - -Vec3 Vec3::operator * (Vec3Arg inV2) const -{ -#if defined(JPH_USE_SSE) - return _mm_mul_ps(mValue, inV2.mValue); -#elif defined(JPH_USE_NEON) - return vmulq_f32(mValue, inV2.mValue); -#else - return Vec3(mF32[0] * inV2.mF32[0], mF32[1] * inV2.mF32[1], mF32[2] * inV2.mF32[2]); -#endif -} - -Vec3 Vec3::operator * (float inV2) const -{ -#if defined(JPH_USE_SSE) - return _mm_mul_ps(mValue, _mm_set1_ps(inV2)); -#elif defined(JPH_USE_NEON) - return vmulq_n_f32(mValue, inV2); -#else - return Vec3(mF32[0] * inV2, mF32[1] * inV2, mF32[2] * inV2); -#endif -} - -Vec3 operator * (float inV1, Vec3Arg inV2) -{ -#if defined(JPH_USE_SSE) - return _mm_mul_ps(_mm_set1_ps(inV1), inV2.mValue); -#elif defined(JPH_USE_NEON) - return vmulq_n_f32(inV2.mValue, inV1); -#else - return Vec3(inV1 * inV2.mF32[0], inV1 * inV2.mF32[1], inV1 * inV2.mF32[2]); -#endif -} - -Vec3 Vec3::operator / (float inV2) const -{ -#if defined(JPH_USE_SSE) - return _mm_div_ps(mValue, _mm_set1_ps(inV2)); -#elif defined(JPH_USE_NEON) - return vdivq_f32(mValue, vdupq_n_f32(inV2)); -#else - return Vec3(mF32[0] / inV2, mF32[1] / inV2, mF32[2] / inV2); -#endif -} - -Vec3 &Vec3::operator *= (float inV2) -{ -#if defined(JPH_USE_SSE) - mValue = _mm_mul_ps(mValue, _mm_set1_ps(inV2)); -#elif defined(JPH_USE_NEON) - mValue = vmulq_n_f32(mValue, inV2); -#else - for (int i = 0; i < 3; ++i) - mF32[i] *= inV2; - #ifdef JPH_FLOATING_POINT_EXCEPTIONS_ENABLED - mF32[3] = mF32[2]; - #endif -#endif - return *this; -} - -Vec3 &Vec3::operator *= (Vec3Arg inV2) -{ -#if defined(JPH_USE_SSE) - mValue = _mm_mul_ps(mValue, inV2.mValue); -#elif defined(JPH_USE_NEON) - mValue = vmulq_f32(mValue, inV2.mValue); -#else - for (int i = 0; i < 3; ++i) - mF32[i] *= inV2.mF32[i]; - #ifdef JPH_FLOATING_POINT_EXCEPTIONS_ENABLED - mF32[3] = mF32[2]; - #endif -#endif - return *this; -} - -Vec3 &Vec3::operator /= (float inV2) -{ -#if defined(JPH_USE_SSE) - mValue = _mm_div_ps(mValue, _mm_set1_ps(inV2)); -#elif defined(JPH_USE_NEON) - mValue = vdivq_f32(mValue, vdupq_n_f32(inV2)); -#else - for (int i = 0; i < 3; ++i) - mF32[i] /= inV2; - #ifdef JPH_FLOATING_POINT_EXCEPTIONS_ENABLED - mF32[3] = mF32[2]; - #endif -#endif - return *this; -} - -Vec3 Vec3::operator + (Vec3Arg inV2) const -{ -#if defined(JPH_USE_SSE) - return _mm_add_ps(mValue, inV2.mValue); -#elif defined(JPH_USE_NEON) - return vaddq_f32(mValue, inV2.mValue); -#else - return Vec3(mF32[0] + inV2.mF32[0], mF32[1] + inV2.mF32[1], mF32[2] + inV2.mF32[2]); -#endif -} - -Vec3 &Vec3::operator += (Vec3Arg inV2) -{ -#if defined(JPH_USE_SSE) - mValue = _mm_add_ps(mValue, inV2.mValue); -#elif defined(JPH_USE_NEON) - mValue = vaddq_f32(mValue, inV2.mValue); -#else - for (int i = 0; i < 3; ++i) - mF32[i] += inV2.mF32[i]; - #ifdef JPH_FLOATING_POINT_EXCEPTIONS_ENABLED - mF32[3] = mF32[2]; - #endif -#endif - return *this; -} - -Vec3 Vec3::operator - () const -{ -#if defined(JPH_USE_SSE) - return _mm_sub_ps(_mm_setzero_ps(), mValue); -#elif defined(JPH_USE_NEON) - return vnegq_f32(mValue); -#else - return Vec3(-mF32[0], -mF32[1], -mF32[2]); -#endif -} - -Vec3 Vec3::operator - (Vec3Arg inV2) const -{ -#if defined(JPH_USE_SSE) - return _mm_sub_ps(mValue, inV2.mValue); -#elif defined(JPH_USE_NEON) - return vsubq_f32(mValue, inV2.mValue); -#else - return Vec3(mF32[0] - inV2.mF32[0], mF32[1] - inV2.mF32[1], mF32[2] - inV2.mF32[2]); -#endif -} - -Vec3 &Vec3::operator -= (Vec3Arg inV2) -{ -#if defined(JPH_USE_SSE) - mValue = _mm_sub_ps(mValue, inV2.mValue); -#elif defined(JPH_USE_NEON) - mValue = vsubq_f32(mValue, inV2.mValue); -#else - for (int i = 0; i < 3; ++i) - mF32[i] -= inV2.mF32[i]; - #ifdef JPH_FLOATING_POINT_EXCEPTIONS_ENABLED - mF32[3] = mF32[2]; - #endif -#endif - return *this; -} - -Vec3 Vec3::operator / (Vec3Arg inV2) const -{ - inV2.CheckW(); // Check W equals Z to avoid div by zero -#if defined(JPH_USE_SSE) - return _mm_div_ps(mValue, inV2.mValue); -#elif defined(JPH_USE_NEON) - return vdivq_f32(mValue, inV2.mValue); -#else - return Vec3(mF32[0] / inV2.mF32[0], mF32[1] / inV2.mF32[1], mF32[2] / inV2.mF32[2]); -#endif -} - -Vec4 Vec3::SplatX() const -{ -#if defined(JPH_USE_SSE) - return _mm_shuffle_ps(mValue, mValue, _MM_SHUFFLE(0, 0, 0, 0)); -#elif defined(JPH_USE_NEON) - return vdupq_laneq_f32(mValue, 0); -#else - return Vec4(mF32[0], mF32[0], mF32[0], mF32[0]); -#endif -} - -Vec4 Vec3::SplatY() const -{ -#if defined(JPH_USE_SSE) - return _mm_shuffle_ps(mValue, mValue, _MM_SHUFFLE(1, 1, 1, 1)); -#elif defined(JPH_USE_NEON) - return vdupq_laneq_f32(mValue, 1); -#else - return Vec4(mF32[1], mF32[1], mF32[1], mF32[1]); -#endif -} - -Vec4 Vec3::SplatZ() const -{ -#if defined(JPH_USE_SSE) - return _mm_shuffle_ps(mValue, mValue, _MM_SHUFFLE(2, 2, 2, 2)); -#elif defined(JPH_USE_NEON) - return vdupq_laneq_f32(mValue, 2); -#else - return Vec4(mF32[2], mF32[2], mF32[2], mF32[2]); -#endif -} - -int Vec3::GetLowestComponentIndex() const -{ - return GetX() < GetY() ? (GetZ() < GetX() ? 2 : 0) : (GetZ() < GetY() ? 2 : 1); -} - -int Vec3::GetHighestComponentIndex() const -{ - return GetX() > GetY() ? (GetZ() > GetX() ? 2 : 0) : (GetZ() > GetY() ? 2 : 1); -} - -Vec3 Vec3::Abs() const -{ -#if defined(JPH_USE_AVX512) - return _mm_range_ps(mValue, mValue, 0b1000); -#elif defined(JPH_USE_SSE) - return _mm_max_ps(_mm_sub_ps(_mm_setzero_ps(), mValue), mValue); -#elif defined(JPH_USE_NEON) - return vabsq_f32(mValue); -#else - return Vec3(abs(mF32[0]), abs(mF32[1]), abs(mF32[2])); -#endif -} - -Vec3 Vec3::Reciprocal() const -{ - return sReplicate(1.0f) / mValue; -} - -Vec3 Vec3::Cross(Vec3Arg inV2) const -{ -#if defined(JPH_USE_SSE) - Type t1 = _mm_shuffle_ps(inV2.mValue, inV2.mValue, _MM_SHUFFLE(0, 0, 2, 1)); // Assure Z and W are the same - t1 = _mm_mul_ps(t1, mValue); - Type t2 = _mm_shuffle_ps(mValue, mValue, _MM_SHUFFLE(0, 0, 2, 1)); // Assure Z and W are the same - t2 = _mm_mul_ps(t2, inV2.mValue); - Type t3 = _mm_sub_ps(t1, t2); - return _mm_shuffle_ps(t3, t3, _MM_SHUFFLE(0, 0, 2, 1)); // Assure Z and W are the same -#elif defined(JPH_USE_NEON) - Type t1 = JPH_NEON_SHUFFLE_F32x4(inV2.mValue, inV2.mValue, 1, 2, 0, 0); // Assure Z and W are the same - t1 = vmulq_f32(t1, mValue); - Type t2 = JPH_NEON_SHUFFLE_F32x4(mValue, mValue, 1, 2, 0, 0); // Assure Z and W are the same - t2 = vmulq_f32(t2, inV2.mValue); - Type t3 = vsubq_f32(t1, t2); - return JPH_NEON_SHUFFLE_F32x4(t3, t3, 1, 2, 0, 0); // Assure Z and W are the same -#else - return Vec3(mF32[1] * inV2.mF32[2] - mF32[2] * inV2.mF32[1], - mF32[2] * inV2.mF32[0] - mF32[0] * inV2.mF32[2], - mF32[0] * inV2.mF32[1] - mF32[1] * inV2.mF32[0]); -#endif -} - -Vec3 Vec3::DotV(Vec3Arg inV2) const -{ -#if defined(JPH_USE_SSE4_1) - return _mm_dp_ps(mValue, inV2.mValue, 0x7f); -#elif defined(JPH_USE_NEON) - float32x4_t mul = vmulq_f32(mValue, inV2.mValue); - mul = vsetq_lane_f32(0, mul, 3); - return vdupq_n_f32(vaddvq_f32(mul)); -#else - float dot = 0.0f; - for (int i = 0; i < 3; i++) - dot += mF32[i] * inV2.mF32[i]; - return Vec3::sReplicate(dot); -#endif -} - -Vec4 Vec3::DotV4(Vec3Arg inV2) const -{ -#if defined(JPH_USE_SSE4_1) - return _mm_dp_ps(mValue, inV2.mValue, 0x7f); -#elif defined(JPH_USE_NEON) - float32x4_t mul = vmulq_f32(mValue, inV2.mValue); - mul = vsetq_lane_f32(0, mul, 3); - return vdupq_n_f32(vaddvq_f32(mul)); -#else - float dot = 0.0f; - for (int i = 0; i < 3; i++) - dot += mF32[i] * inV2.mF32[i]; - return Vec4::sReplicate(dot); -#endif -} - -float Vec3::Dot(Vec3Arg inV2) const -{ -#if defined(JPH_USE_SSE4_1) - return _mm_cvtss_f32(_mm_dp_ps(mValue, inV2.mValue, 0x7f)); -#elif defined(JPH_USE_NEON) - float32x4_t mul = vmulq_f32(mValue, inV2.mValue); - mul = vsetq_lane_f32(0, mul, 3); - return vaddvq_f32(mul); -#else - float dot = 0.0f; - for (int i = 0; i < 3; i++) - dot += mF32[i] * inV2.mF32[i]; - return dot; -#endif -} - -float Vec3::LengthSq() const -{ -#if defined(JPH_USE_SSE4_1) - return _mm_cvtss_f32(_mm_dp_ps(mValue, mValue, 0x7f)); -#elif defined(JPH_USE_NEON) - float32x4_t mul = vmulq_f32(mValue, mValue); - mul = vsetq_lane_f32(0, mul, 3); - return vaddvq_f32(mul); -#else - float len_sq = 0.0f; - for (int i = 0; i < 3; i++) - len_sq += mF32[i] * mF32[i]; - return len_sq; -#endif -} - -float Vec3::Length() const -{ -#if defined(JPH_USE_SSE4_1) - return _mm_cvtss_f32(_mm_sqrt_ss(_mm_dp_ps(mValue, mValue, 0x7f))); -#elif defined(JPH_USE_NEON) - float32x4_t mul = vmulq_f32(mValue, mValue); - mul = vsetq_lane_f32(0, mul, 3); - float32x2_t sum = vdup_n_f32(vaddvq_f32(mul)); - return vget_lane_f32(vsqrt_f32(sum), 0); -#else - return sqrt(LengthSq()); -#endif -} - -Vec3 Vec3::Sqrt() const -{ -#if defined(JPH_USE_SSE) - return _mm_sqrt_ps(mValue); -#elif defined(JPH_USE_NEON) - return vsqrtq_f32(mValue); -#else - return Vec3(sqrt(mF32[0]), sqrt(mF32[1]), sqrt(mF32[2])); -#endif -} - -Vec3 Vec3::Normalized() const -{ -#if defined(JPH_USE_SSE4_1) - return _mm_div_ps(mValue, _mm_sqrt_ps(_mm_dp_ps(mValue, mValue, 0x7f))); -#elif defined(JPH_USE_NEON) - float32x4_t mul = vmulq_f32(mValue, mValue); - mul = vsetq_lane_f32(0, mul, 3); - float32x4_t sum = vdupq_n_f32(vaddvq_f32(mul)); - return vdivq_f32(mValue, vsqrtq_f32(sum)); -#else - return *this / Length(); -#endif -} - -Vec3 Vec3::NormalizedOr(Vec3Arg inZeroValue) const -{ -#if defined(JPH_USE_SSE4_1) - Type len_sq = _mm_dp_ps(mValue, mValue, 0x7f); - Type is_zero = _mm_cmpeq_ps(len_sq, _mm_setzero_ps()); -#ifdef JPH_FLOATING_POINT_EXCEPTIONS_ENABLED - if (_mm_movemask_ps(is_zero) == 0xf) - return inZeroValue; - else - return _mm_div_ps(mValue, _mm_sqrt_ps(len_sq)); -#else - return _mm_blendv_ps(_mm_div_ps(mValue, _mm_sqrt_ps(len_sq)), inZeroValue.mValue, is_zero); -#endif // JPH_FLOATING_POINT_EXCEPTIONS_ENABLED -#elif defined(JPH_USE_NEON) - float32x4_t mul = vmulq_f32(mValue, mValue); - mul = vsetq_lane_f32(0, mul, 3); - float32x4_t sum = vdupq_n_f32(vaddvq_f32(mul)); - float32x4_t len = vsqrtq_f32(sum); - float32x4_t is_zero = vceqq_f32(len, vdupq_n_f32(0)); - return vbslq_f32(is_zero, inZeroValue.mValue, vdivq_f32(mValue, len)); -#else - float len_sq = LengthSq(); - if (len_sq == 0.0f) - return inZeroValue; - else - return *this / sqrt(len_sq); -#endif -} - -bool Vec3::IsNormalized(float inTolerance) const -{ - return abs(LengthSq() - 1.0f) <= inTolerance; -} - -bool Vec3::IsNaN() const -{ -#if defined(JPH_USE_AVX512) - return (_mm_fpclass_ps_mask(mValue, 0b10000001) & 0x7) != 0; -#elif defined(JPH_USE_SSE) - return (_mm_movemask_ps(_mm_cmpunord_ps(mValue, mValue)) & 0x7) != 0; -#elif defined(JPH_USE_NEON) - uint32x4_t mask = JPH_NEON_UINT32x4(1, 1, 1, 0); - uint32x4_t is_equal = vceqq_f32(mValue, mValue); // If a number is not equal to itself it's a NaN - return vaddvq_u32(vandq_u32(is_equal, mask)) != 3; -#else - return isnan(mF32[0]) || isnan(mF32[1]) || isnan(mF32[2]); -#endif -} - -void Vec3::StoreFloat3(Float3 *outV) const -{ -#if defined(JPH_USE_SSE) - _mm_store_ss(&outV->x, mValue); - Vec3 t = Swizzle(); - _mm_store_ss(&outV->y, t.mValue); - t = t.Swizzle(); - _mm_store_ss(&outV->z, t.mValue); -#elif defined(JPH_USE_NEON) - float32x2_t xy = vget_low_f32(mValue); - vst1_f32(&outV->x, xy); - vst1q_lane_f32(&outV->z, mValue, 2); -#else - outV->x = mF32[0]; - outV->y = mF32[1]; - outV->z = mF32[2]; -#endif -} - -UVec4 Vec3::ToInt() const -{ -#if defined(JPH_USE_SSE) - return _mm_cvttps_epi32(mValue); -#elif defined(JPH_USE_NEON) - return vcvtq_u32_f32(mValue); -#else - return UVec4(uint32(mF32[0]), uint32(mF32[1]), uint32(mF32[2]), uint32(mF32[3])); -#endif -} - -UVec4 Vec3::ReinterpretAsInt() const -{ -#if defined(JPH_USE_SSE) - return UVec4(_mm_castps_si128(mValue)); -#elif defined(JPH_USE_NEON) - return vreinterpretq_u32_f32(mValue); -#else - return *reinterpret_cast(this); -#endif -} - -float Vec3::ReduceMin() const -{ - Vec3 v = sMin(mValue, Swizzle()); - v = sMin(v, v.Swizzle()); - return v.GetX(); -} - -float Vec3::ReduceMax() const -{ - Vec3 v = sMax(mValue, Swizzle()); - v = sMax(v, v.Swizzle()); - return v.GetX(); -} - -Vec3 Vec3::GetNormalizedPerpendicular() const -{ - if (abs(mF32[0]) > abs(mF32[1])) - { - float len = sqrt(mF32[0] * mF32[0] + mF32[2] * mF32[2]); - return Vec3(mF32[2], 0.0f, -mF32[0]) / len; - } - else - { - float len = sqrt(mF32[1] * mF32[1] + mF32[2] * mF32[2]); - return Vec3(0.0f, mF32[2], -mF32[1]) / len; - } -} - -Vec3 Vec3::GetSign() const -{ -#if defined(JPH_USE_AVX512) - return _mm_fixupimm_ps(mValue, mValue, _mm_set1_epi32(0xA9A90A00), 0); -#elif defined(JPH_USE_SSE) - Type minus_one = _mm_set1_ps(-1.0f); - Type one = _mm_set1_ps(1.0f); - return _mm_or_ps(_mm_and_ps(mValue, minus_one), one); -#elif defined(JPH_USE_NEON) - Type minus_one = vdupq_n_f32(-1.0f); - Type one = vdupq_n_f32(1.0f); - return vorrq_s32(vandq_s32(mValue, minus_one), one); -#else - return Vec3(signbit(mF32[0])? -1.0f : 1.0f, - signbit(mF32[1])? -1.0f : 1.0f, - signbit(mF32[2])? -1.0f : 1.0f); -#endif -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Vec4.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Vec4.h deleted file mode 100644 index b369f8e3c61..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Vec4.h +++ /dev/null @@ -1,283 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -class [[nodiscard]] alignas(JPH_VECTOR_ALIGNMENT) Vec4 -{ -public: - JPH_OVERRIDE_NEW_DELETE - - // Underlying vector type -#if defined(JPH_USE_SSE) - using Type = __m128; -#elif defined(JPH_USE_NEON) - using Type = float32x4_t; -#else - using Type = struct { float mData[4]; }; -#endif - - /// Constructor - Vec4() = default; ///< Intentionally not initialized for performance reasons - Vec4(const Vec4 &inRHS) = default; - Vec4 & operator = (const Vec4 &inRHS) = default; - explicit JPH_INLINE Vec4(Vec3Arg inRHS); ///< WARNING: W component undefined! - JPH_INLINE Vec4(Vec3Arg inRHS, float inW); - JPH_INLINE Vec4(Type inRHS) : mValue(inRHS) { } - - /// Create a vector from 4 components - JPH_INLINE Vec4(float inX, float inY, float inZ, float inW); - - /// Vector with all zeros - static JPH_INLINE Vec4 sZero(); - - /// Vector with all NaN's - static JPH_INLINE Vec4 sNaN(); - - /// Replicate inV across all components - static JPH_INLINE Vec4 sReplicate(float inV); - - /// Load 4 floats from memory - static JPH_INLINE Vec4 sLoadFloat4(const Float4 *inV); - - /// Load 4 floats from memory, 16 bytes aligned - static JPH_INLINE Vec4 sLoadFloat4Aligned(const Float4 *inV); - - /// Gather 4 floats from memory at inBase + inOffsets[i] * Scale - template - static JPH_INLINE Vec4 sGatherFloat4(const float *inBase, UVec4Arg inOffsets); - - /// Return the minimum value of each of the components - static JPH_INLINE Vec4 sMin(Vec4Arg inV1, Vec4Arg inV2); - - /// Return the maximum of each of the components - static JPH_INLINE Vec4 sMax(Vec4Arg inV1, Vec4Arg inV2); - - /// Equals (component wise) - static JPH_INLINE UVec4 sEquals(Vec4Arg inV1, Vec4Arg inV2); - - /// Less than (component wise) - static JPH_INLINE UVec4 sLess(Vec4Arg inV1, Vec4Arg inV2); - - /// Less than or equal (component wise) - static JPH_INLINE UVec4 sLessOrEqual(Vec4Arg inV1, Vec4Arg inV2); - - /// Greater than (component wise) - static JPH_INLINE UVec4 sGreater(Vec4Arg inV1, Vec4Arg inV2); - - /// Greater than or equal (component wise) - static JPH_INLINE UVec4 sGreaterOrEqual(Vec4Arg inV1, Vec4Arg inV2); - - /// Calculates inMul1 * inMul2 + inAdd - static JPH_INLINE Vec4 sFusedMultiplyAdd(Vec4Arg inMul1, Vec4Arg inMul2, Vec4Arg inAdd); - - /// Component wise select, returns inV1 when highest bit of inControl = 0 and inV2 when highest bit of inControl = 1 - static JPH_INLINE Vec4 sSelect(Vec4Arg inV1, Vec4Arg inV2, UVec4Arg inControl); - - /// Logical or (component wise) - static JPH_INLINE Vec4 sOr(Vec4Arg inV1, Vec4Arg inV2); - - /// Logical xor (component wise) - static JPH_INLINE Vec4 sXor(Vec4Arg inV1, Vec4Arg inV2); - - /// Logical and (component wise) - static JPH_INLINE Vec4 sAnd(Vec4Arg inV1, Vec4Arg inV2); - - /// Sort the four elements of ioValue and sort ioIndex at the same time. - /// Based on a sorting network: http://en.wikipedia.org/wiki/Sorting_network - static JPH_INLINE void sSort4(Vec4 &ioValue, UVec4 &ioIndex); - - /// Reverse sort the four elements of ioValue (highest first) and sort ioIndex at the same time. - /// Based on a sorting network: http://en.wikipedia.org/wiki/Sorting_network - static JPH_INLINE void sSort4Reverse(Vec4 &ioValue, UVec4 &ioIndex); - - /// Get individual components -#if defined(JPH_USE_SSE) - JPH_INLINE float GetX() const { return _mm_cvtss_f32(mValue); } - JPH_INLINE float GetY() const { return mF32[1]; } - JPH_INLINE float GetZ() const { return mF32[2]; } - JPH_INLINE float GetW() const { return mF32[3]; } -#elif defined(JPH_USE_NEON) - JPH_INLINE float GetX() const { return vgetq_lane_f32(mValue, 0); } - JPH_INLINE float GetY() const { return vgetq_lane_f32(mValue, 1); } - JPH_INLINE float GetZ() const { return vgetq_lane_f32(mValue, 2); } - JPH_INLINE float GetW() const { return vgetq_lane_f32(mValue, 3); } -#else - JPH_INLINE float GetX() const { return mF32[0]; } - JPH_INLINE float GetY() const { return mF32[1]; } - JPH_INLINE float GetZ() const { return mF32[2]; } - JPH_INLINE float GetW() const { return mF32[3]; } -#endif - - /// Set individual components - JPH_INLINE void SetX(float inX) { mF32[0] = inX; } - JPH_INLINE void SetY(float inY) { mF32[1] = inY; } - JPH_INLINE void SetZ(float inZ) { mF32[2] = inZ; } - JPH_INLINE void SetW(float inW) { mF32[3] = inW; } - - /// Set all components - JPH_INLINE void Set(float inX, float inY, float inZ, float inW) { *this = Vec4(inX, inY, inZ, inW); } - - /// Get float component by index - JPH_INLINE float operator [] (uint inCoordinate) const { JPH_ASSERT(inCoordinate < 4); return mF32[inCoordinate]; } - JPH_INLINE float & operator [] (uint inCoordinate) { JPH_ASSERT(inCoordinate < 4); return mF32[inCoordinate]; } - - /// Comparison - JPH_INLINE bool operator == (Vec4Arg inV2) const; - JPH_INLINE bool operator != (Vec4Arg inV2) const { return !(*this == inV2); } - - /// Test if two vectors are close - JPH_INLINE bool IsClose(Vec4Arg inV2, float inMaxDistSq = 1.0e-12f) const; - - /// Test if vector is normalized - JPH_INLINE bool IsNormalized(float inTolerance = 1.0e-6f) const; - - /// Test if vector contains NaN elements - JPH_INLINE bool IsNaN() const; - - /// Multiply two float vectors (component wise) - JPH_INLINE Vec4 operator * (Vec4Arg inV2) const; - - /// Multiply vector with float - JPH_INLINE Vec4 operator * (float inV2) const; - - /// Multiply vector with float - friend JPH_INLINE Vec4 operator * (float inV1, Vec4Arg inV2); - - /// Divide vector by float - JPH_INLINE Vec4 operator / (float inV2) const; - - /// Multiply vector with float - JPH_INLINE Vec4 & operator *= (float inV2); - - /// Multiply vector with vector - JPH_INLINE Vec4 & operator *= (Vec4Arg inV2); - - /// Divide vector by float - JPH_INLINE Vec4 & operator /= (float inV2); - - /// Add two float vectors (component wise) - JPH_INLINE Vec4 operator + (Vec4Arg inV2) const; - - /// Add two float vectors (component wise) - JPH_INLINE Vec4 & operator += (Vec4Arg inV2); - - /// Negate - JPH_INLINE Vec4 operator - () const; - - /// Subtract two float vectors (component wise) - JPH_INLINE Vec4 operator - (Vec4Arg inV2) const; - - /// Add two float vectors (component wise) - JPH_INLINE Vec4 & operator -= (Vec4Arg inV2); - - /// Divide (component wise) - JPH_INLINE Vec4 operator / (Vec4Arg inV2) const; - - /// Swizzle the elements in inV - template - JPH_INLINE Vec4 Swizzle() const; - - /// Replicate the X component to all components - JPH_INLINE Vec4 SplatX() const; - - /// Replicate the Y component to all components - JPH_INLINE Vec4 SplatY() const; - - /// Replicate the Z component to all components - JPH_INLINE Vec4 SplatZ() const; - - /// Replicate the W component to all components - JPH_INLINE Vec4 SplatW() const; - - /// Return the absolute value of each of the components - JPH_INLINE Vec4 Abs() const; - - /// Reciprocal vector (1 / value) for each of the components - JPH_INLINE Vec4 Reciprocal() const; - - /// Dot product, returns the dot product in X, Y and Z components - JPH_INLINE Vec4 DotV(Vec4Arg inV2) const; - - /// Dot product - JPH_INLINE float Dot(Vec4Arg inV2) const; - - /// Squared length of vector - JPH_INLINE float LengthSq() const; - - /// Length of vector - JPH_INLINE float Length() const; - - /// Normalize vector - JPH_INLINE Vec4 Normalized() const; - - /// Store 4 floats to memory - JPH_INLINE void StoreFloat4(Float4 *outV) const; - - /// Convert each component from a float to an int - JPH_INLINE UVec4 ToInt() const; - - /// Reinterpret Vec4 as a UVec4 (doesn't change the bits) - JPH_INLINE UVec4 ReinterpretAsInt() const; - - /// Store if X is negative in bit 0, Y in bit 1, Z in bit 2 and W in bit 3 - JPH_INLINE int GetSignBits() const; - - /// Get the minimum of X, Y, Z and W - JPH_INLINE float ReduceMin() const; - - /// Get the maximum of X, Y, Z and W - JPH_INLINE float ReduceMax() const; - - /// Component wise square root - JPH_INLINE Vec4 Sqrt() const; - - /// Get vector that contains the sign of each element (returns 1.0f if positive, -1.0f if negative) - JPH_INLINE Vec4 GetSign() const; - - /// Calculate the sine and cosine for each element of this vector (input in radians) - inline void SinCos(Vec4 &outSin, Vec4 &outCos) const; - - /// Calculate the tangent for each element of this vector (input in radians) - inline Vec4 Tan() const; - - /// Calculate the arc sine for each element of this vector (returns value in the range [-PI / 2, PI / 2]) - /// Note that all input values will be clamped to the range [-1, 1] and this function will not return NaNs like std::asin - inline Vec4 ASin() const; - - /// Calculate the arc cosine for each element of this vector (returns value in the range [0, PI]) - /// Note that all input values will be clamped to the range [-1, 1] and this function will not return NaNs like std::acos - inline Vec4 ACos() const; - - /// Calculate the arc tangent for each element of this vector (returns value in the range [-PI / 2, PI / 2]) - inline Vec4 ATan() const; - - /// Calculate the arc tangent of y / x using the signs of the arguments to determine the correct quadrant (returns value in the range [-PI, PI]) - inline static Vec4 sATan2(Vec4Arg inY, Vec4Arg inX); - - /// To String - friend ostream & operator << (ostream &inStream, Vec4Arg inV) - { - inStream << inV.mF32[0] << ", " << inV.mF32[1] << ", " << inV.mF32[2] << ", " << inV.mF32[3]; - return inStream; - } - - union - { - Type mValue; - float mF32[4]; - }; -}; - -static_assert(is_trivial(), "Is supposed to be a trivial type!"); - -JPH_NAMESPACE_END - -#include "Vec4.inl" diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Vec4.inl b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Vec4.inl deleted file mode 100644 index 9fb6fc6d001..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Vec4.inl +++ /dev/null @@ -1,970 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -// Constructor -Vec4::Vec4(Vec3Arg inRHS) : - mValue(inRHS.mValue) -{ -} - -Vec4::Vec4(Vec3Arg inRHS, float inW) -{ -#if defined(JPH_USE_SSE4_1) - mValue = _mm_blend_ps(inRHS.mValue, _mm_set1_ps(inW), 8); -#elif defined(JPH_USE_NEON) - mValue = vsetq_lane_f32(inW, inRHS.mValue, 3); -#else - for (int i = 0; i < 3; i++) - mF32[i] = inRHS.mF32[i]; - mF32[3] = inW; -#endif -} - -Vec4::Vec4(float inX, float inY, float inZ, float inW) -{ -#if defined(JPH_USE_SSE) - mValue = _mm_set_ps(inW, inZ, inY, inX); -#elif defined(JPH_USE_NEON) - uint32x2_t xy = vcreate_f32(static_cast(*reinterpret_cast(&inX)) | (static_cast(*reinterpret_cast(&inY)) << 32)); - uint32x2_t zw = vcreate_f32(static_cast(*reinterpret_cast(&inZ)) | (static_cast(*reinterpret_cast(&inW)) << 32)); - mValue = vcombine_f32(xy, zw); -#else - mF32[0] = inX; - mF32[1] = inY; - mF32[2] = inZ; - mF32[3] = inW; -#endif -} - -template -Vec4 Vec4::Swizzle() const -{ - static_assert(SwizzleX <= 3, "SwizzleX template parameter out of range"); - static_assert(SwizzleY <= 3, "SwizzleY template parameter out of range"); - static_assert(SwizzleZ <= 3, "SwizzleZ template parameter out of range"); - static_assert(SwizzleW <= 3, "SwizzleW template parameter out of range"); - -#if defined(JPH_USE_SSE) - return _mm_shuffle_ps(mValue, mValue, _MM_SHUFFLE(SwizzleW, SwizzleZ, SwizzleY, SwizzleX)); -#elif defined(JPH_USE_NEON) - return JPH_NEON_SHUFFLE_F32x4(mValue, mValue, SwizzleX, SwizzleY, SwizzleZ, SwizzleW); -#else - return Vec4(mF32[SwizzleX], mF32[SwizzleY], mF32[SwizzleZ], mF32[SwizzleW]); -#endif -} - -Vec4 Vec4::sZero() -{ -#if defined(JPH_USE_SSE) - return _mm_setzero_ps(); -#elif defined(JPH_USE_NEON) - return vdupq_n_f32(0); -#else - return Vec4(0, 0, 0, 0); -#endif -} - -Vec4 Vec4::sReplicate(float inV) -{ -#if defined(JPH_USE_SSE) - return _mm_set1_ps(inV); -#elif defined(JPH_USE_NEON) - return vdupq_n_f32(inV); -#else - return Vec4(inV, inV, inV, inV); -#endif -} - -Vec4 Vec4::sNaN() -{ - return sReplicate(numeric_limits::quiet_NaN()); -} - -Vec4 Vec4::sLoadFloat4(const Float4 *inV) -{ -#if defined(JPH_USE_SSE) - return _mm_loadu_ps(&inV->x); -#elif defined(JPH_USE_NEON) - return vld1q_f32(&inV->x); -#else - return Vec4(inV->x, inV->y, inV->z, inV->w); -#endif -} - -Vec4 Vec4::sLoadFloat4Aligned(const Float4 *inV) -{ -#if defined(JPH_USE_SSE) - return _mm_load_ps(&inV->x); -#elif defined(JPH_USE_NEON) - return vld1q_f32(&inV->x); -#else - return Vec4(inV->x, inV->y, inV->z, inV->w); -#endif -} - -template -Vec4 Vec4::sGatherFloat4(const float *inBase, UVec4Arg inOffsets) -{ -#if defined(JPH_USE_SSE) - #ifdef JPH_USE_AVX2 - return _mm_i32gather_ps(inBase, inOffsets.mValue, Scale); - #else - const uint8 *base = reinterpret_cast(inBase); - Type x = _mm_load_ss(reinterpret_cast(base + inOffsets.GetX() * Scale)); - Type y = _mm_load_ss(reinterpret_cast(base + inOffsets.GetY() * Scale)); - Type xy = _mm_unpacklo_ps(x, y); - Type z = _mm_load_ss(reinterpret_cast(base + inOffsets.GetZ() * Scale)); - Type w = _mm_load_ss(reinterpret_cast(base + inOffsets.GetW() * Scale)); - Type zw = _mm_unpacklo_ps(z, w); - return _mm_movelh_ps(xy, zw); - #endif -#else - const uint8 *base = reinterpret_cast(inBase); - float x = *reinterpret_cast(base + inOffsets.GetX() * Scale); - float y = *reinterpret_cast(base + inOffsets.GetY() * Scale); - float z = *reinterpret_cast(base + inOffsets.GetZ() * Scale); - float w = *reinterpret_cast(base + inOffsets.GetW() * Scale); - return Vec4(x, y, z, w); -#endif -} - -Vec4 Vec4::sMin(Vec4Arg inV1, Vec4Arg inV2) -{ -#if defined(JPH_USE_SSE) - return _mm_min_ps(inV1.mValue, inV2.mValue); -#elif defined(JPH_USE_NEON) - return vminq_f32(inV1.mValue, inV2.mValue); -#else - return Vec4(min(inV1.mF32[0], inV2.mF32[0]), - min(inV1.mF32[1], inV2.mF32[1]), - min(inV1.mF32[2], inV2.mF32[2]), - min(inV1.mF32[3], inV2.mF32[3])); -#endif -} - -Vec4 Vec4::sMax(Vec4Arg inV1, Vec4Arg inV2) -{ -#if defined(JPH_USE_SSE) - return _mm_max_ps(inV1.mValue, inV2.mValue); -#elif defined(JPH_USE_NEON) - return vmaxq_f32(inV1.mValue, inV2.mValue); -#else - return Vec4(max(inV1.mF32[0], inV2.mF32[0]), - max(inV1.mF32[1], inV2.mF32[1]), - max(inV1.mF32[2], inV2.mF32[2]), - max(inV1.mF32[3], inV2.mF32[3])); -#endif -} - -UVec4 Vec4::sEquals(Vec4Arg inV1, Vec4Arg inV2) -{ -#if defined(JPH_USE_SSE) - return _mm_castps_si128(_mm_cmpeq_ps(inV1.mValue, inV2.mValue)); -#elif defined(JPH_USE_NEON) - return vceqq_f32(inV1.mValue, inV2.mValue); -#else - return UVec4(inV1.mF32[0] == inV2.mF32[0]? 0xffffffffu : 0, - inV1.mF32[1] == inV2.mF32[1]? 0xffffffffu : 0, - inV1.mF32[2] == inV2.mF32[2]? 0xffffffffu : 0, - inV1.mF32[3] == inV2.mF32[3]? 0xffffffffu : 0); -#endif -} - -UVec4 Vec4::sLess(Vec4Arg inV1, Vec4Arg inV2) -{ -#if defined(JPH_USE_SSE) - return _mm_castps_si128(_mm_cmplt_ps(inV1.mValue, inV2.mValue)); -#elif defined(JPH_USE_NEON) - return vcltq_f32(inV1.mValue, inV2.mValue); -#else - return UVec4(inV1.mF32[0] < inV2.mF32[0]? 0xffffffffu : 0, - inV1.mF32[1] < inV2.mF32[1]? 0xffffffffu : 0, - inV1.mF32[2] < inV2.mF32[2]? 0xffffffffu : 0, - inV1.mF32[3] < inV2.mF32[3]? 0xffffffffu : 0); -#endif -} - -UVec4 Vec4::sLessOrEqual(Vec4Arg inV1, Vec4Arg inV2) -{ -#if defined(JPH_USE_SSE) - return _mm_castps_si128(_mm_cmple_ps(inV1.mValue, inV2.mValue)); -#elif defined(JPH_USE_NEON) - return vcleq_f32(inV1.mValue, inV2.mValue); -#else - return UVec4(inV1.mF32[0] <= inV2.mF32[0]? 0xffffffffu : 0, - inV1.mF32[1] <= inV2.mF32[1]? 0xffffffffu : 0, - inV1.mF32[2] <= inV2.mF32[2]? 0xffffffffu : 0, - inV1.mF32[3] <= inV2.mF32[3]? 0xffffffffu : 0); -#endif -} - -UVec4 Vec4::sGreater(Vec4Arg inV1, Vec4Arg inV2) -{ -#if defined(JPH_USE_SSE) - return _mm_castps_si128(_mm_cmpgt_ps(inV1.mValue, inV2.mValue)); -#elif defined(JPH_USE_NEON) - return vcgtq_f32(inV1.mValue, inV2.mValue); -#else - return UVec4(inV1.mF32[0] > inV2.mF32[0]? 0xffffffffu : 0, - inV1.mF32[1] > inV2.mF32[1]? 0xffffffffu : 0, - inV1.mF32[2] > inV2.mF32[2]? 0xffffffffu : 0, - inV1.mF32[3] > inV2.mF32[3]? 0xffffffffu : 0); -#endif -} - -UVec4 Vec4::sGreaterOrEqual(Vec4Arg inV1, Vec4Arg inV2) -{ -#if defined(JPH_USE_SSE) - return _mm_castps_si128(_mm_cmpge_ps(inV1.mValue, inV2.mValue)); -#elif defined(JPH_USE_NEON) - return vcgeq_f32(inV1.mValue, inV2.mValue); -#else - return UVec4(inV1.mF32[0] >= inV2.mF32[0]? 0xffffffffu : 0, - inV1.mF32[1] >= inV2.mF32[1]? 0xffffffffu : 0, - inV1.mF32[2] >= inV2.mF32[2]? 0xffffffffu : 0, - inV1.mF32[3] >= inV2.mF32[3]? 0xffffffffu : 0); -#endif -} - -Vec4 Vec4::sFusedMultiplyAdd(Vec4Arg inMul1, Vec4Arg inMul2, Vec4Arg inAdd) -{ -#if defined(JPH_USE_SSE) - #ifdef JPH_USE_FMADD - return _mm_fmadd_ps(inMul1.mValue, inMul2.mValue, inAdd.mValue); - #else - return _mm_add_ps(_mm_mul_ps(inMul1.mValue, inMul2.mValue), inAdd.mValue); - #endif -#elif defined(JPH_USE_NEON) - return vmlaq_f32(inAdd.mValue, inMul1.mValue, inMul2.mValue); -#else - return Vec4(inMul1.mF32[0] * inMul2.mF32[0] + inAdd.mF32[0], - inMul1.mF32[1] * inMul2.mF32[1] + inAdd.mF32[1], - inMul1.mF32[2] * inMul2.mF32[2] + inAdd.mF32[2], - inMul1.mF32[3] * inMul2.mF32[3] + inAdd.mF32[3]); -#endif -} - -Vec4 Vec4::sSelect(Vec4Arg inV1, Vec4Arg inV2, UVec4Arg inControl) -{ -#if defined(JPH_USE_SSE4_1) - return _mm_blendv_ps(inV1.mValue, inV2.mValue, _mm_castsi128_ps(inControl.mValue)); -#elif defined(JPH_USE_NEON) - return vbslq_f32(vshrq_n_s32(inControl.mValue, 31), inV2.mValue, inV1.mValue); -#else - Vec4 result; - for (int i = 0; i < 4; i++) - result.mF32[i] = inControl.mU32[i] ? inV2.mF32[i] : inV1.mF32[i]; - return result; -#endif -} - -Vec4 Vec4::sOr(Vec4Arg inV1, Vec4Arg inV2) -{ -#if defined(JPH_USE_SSE) - return _mm_or_ps(inV1.mValue, inV2.mValue); -#elif defined(JPH_USE_NEON) - return vorrq_s32(inV1.mValue, inV2.mValue); -#else - return UVec4::sOr(inV1.ReinterpretAsInt(), inV2.ReinterpretAsInt()).ReinterpretAsFloat(); -#endif -} - -Vec4 Vec4::sXor(Vec4Arg inV1, Vec4Arg inV2) -{ -#if defined(JPH_USE_SSE) - return _mm_xor_ps(inV1.mValue, inV2.mValue); -#elif defined(JPH_USE_NEON) - return veorq_s32(inV1.mValue, inV2.mValue); -#else - return UVec4::sXor(inV1.ReinterpretAsInt(), inV2.ReinterpretAsInt()).ReinterpretAsFloat(); -#endif -} - -Vec4 Vec4::sAnd(Vec4Arg inV1, Vec4Arg inV2) -{ -#if defined(JPH_USE_SSE) - return _mm_and_ps(inV1.mValue, inV2.mValue); -#elif defined(JPH_USE_NEON) - return vandq_s32(inV1.mValue, inV2.mValue); -#else - return UVec4::sAnd(inV1.ReinterpretAsInt(), inV2.ReinterpretAsInt()).ReinterpretAsFloat(); -#endif -} - -void Vec4::sSort4(Vec4 &ioValue, UVec4 &ioIndex) -{ - // Pass 1, test 1st vs 3rd, 2nd vs 4th - Vec4 v1 = ioValue.Swizzle(); - UVec4 i1 = ioIndex.Swizzle(); - UVec4 c1 = sLess(ioValue, v1).Swizzle(); - ioValue = sSelect(ioValue, v1, c1); - ioIndex = UVec4::sSelect(ioIndex, i1, c1); - - // Pass 2, test 1st vs 2nd, 3rd vs 4th - Vec4 v2 = ioValue.Swizzle(); - UVec4 i2 = ioIndex.Swizzle(); - UVec4 c2 = sLess(ioValue, v2).Swizzle(); - ioValue = sSelect(ioValue, v2, c2); - ioIndex = UVec4::sSelect(ioIndex, i2, c2); - - // Pass 3, test 2nd vs 3rd component - Vec4 v3 = ioValue.Swizzle(); - UVec4 i3 = ioIndex.Swizzle(); - UVec4 c3 = sLess(ioValue, v3).Swizzle(); - ioValue = sSelect(ioValue, v3, c3); - ioIndex = UVec4::sSelect(ioIndex, i3, c3); -} - -void Vec4::sSort4Reverse(Vec4 &ioValue, UVec4 &ioIndex) -{ - // Pass 1, test 1st vs 3rd, 2nd vs 4th - Vec4 v1 = ioValue.Swizzle(); - UVec4 i1 = ioIndex.Swizzle(); - UVec4 c1 = sGreater(ioValue, v1).Swizzle(); - ioValue = sSelect(ioValue, v1, c1); - ioIndex = UVec4::sSelect(ioIndex, i1, c1); - - // Pass 2, test 1st vs 2nd, 3rd vs 4th - Vec4 v2 = ioValue.Swizzle(); - UVec4 i2 = ioIndex.Swizzle(); - UVec4 c2 = sGreater(ioValue, v2).Swizzle(); - ioValue = sSelect(ioValue, v2, c2); - ioIndex = UVec4::sSelect(ioIndex, i2, c2); - - // Pass 3, test 2nd vs 3rd component - Vec4 v3 = ioValue.Swizzle(); - UVec4 i3 = ioIndex.Swizzle(); - UVec4 c3 = sGreater(ioValue, v3).Swizzle(); - ioValue = sSelect(ioValue, v3, c3); - ioIndex = UVec4::sSelect(ioIndex, i3, c3); -} - -bool Vec4::operator == (Vec4Arg inV2) const -{ - return sEquals(*this, inV2).TestAllTrue(); -} - -bool Vec4::IsClose(Vec4Arg inV2, float inMaxDistSq) const -{ - return (inV2 - *this).LengthSq() <= inMaxDistSq; -} - -bool Vec4::IsNormalized(float inTolerance) const -{ - return abs(LengthSq() - 1.0f) <= inTolerance; -} - -bool Vec4::IsNaN() const -{ -#if defined(JPH_USE_AVX512) - return _mm_fpclass_ps_mask(mValue, 0b10000001) != 0; -#elif defined(JPH_USE_SSE) - return _mm_movemask_ps(_mm_cmpunord_ps(mValue, mValue)) != 0; -#elif defined(JPH_USE_NEON) - uint32x4_t is_equal = vceqq_f32(mValue, mValue); // If a number is not equal to itself it's a NaN - return vaddvq_u32(vshrq_n_u32(is_equal, 31)) != 4; -#else - return isnan(mF32[0]) || isnan(mF32[1]) || isnan(mF32[2]) || isnan(mF32[3]); -#endif -} - -Vec4 Vec4::operator * (Vec4Arg inV2) const -{ -#if defined(JPH_USE_SSE) - return _mm_mul_ps(mValue, inV2.mValue); -#elif defined(JPH_USE_NEON) - return vmulq_f32(mValue, inV2.mValue); -#else - return Vec4(mF32[0] * inV2.mF32[0], - mF32[1] * inV2.mF32[1], - mF32[2] * inV2.mF32[2], - mF32[3] * inV2.mF32[3]); -#endif -} - -Vec4 Vec4::operator * (float inV2) const -{ -#if defined(JPH_USE_SSE) - return _mm_mul_ps(mValue, _mm_set1_ps(inV2)); -#elif defined(JPH_USE_NEON) - return vmulq_n_f32(mValue, inV2); -#else - return Vec4(mF32[0] * inV2, mF32[1] * inV2, mF32[2] * inV2, mF32[3] * inV2); -#endif -} - -/// Multiply vector with float -Vec4 operator * (float inV1, Vec4Arg inV2) -{ -#if defined(JPH_USE_SSE) - return _mm_mul_ps(_mm_set1_ps(inV1), inV2.mValue); -#elif defined(JPH_USE_NEON) - return vmulq_n_f32(inV2.mValue, inV1); -#else - return Vec4(inV1 * inV2.mF32[0], - inV1 * inV2.mF32[1], - inV1 * inV2.mF32[2], - inV1 * inV2.mF32[3]); -#endif -} - -Vec4 Vec4::operator / (float inV2) const -{ -#if defined(JPH_USE_SSE) - return _mm_div_ps(mValue, _mm_set1_ps(inV2)); -#elif defined(JPH_USE_NEON) - return vdivq_f32(mValue, vdupq_n_f32(inV2)); -#else - return Vec4(mF32[0] / inV2, mF32[1] / inV2, mF32[2] / inV2, mF32[3] / inV2); -#endif -} - -Vec4 &Vec4::operator *= (float inV2) -{ -#if defined(JPH_USE_SSE) - mValue = _mm_mul_ps(mValue, _mm_set1_ps(inV2)); -#elif defined(JPH_USE_NEON) - mValue = vmulq_n_f32(mValue, inV2); -#else - for (int i = 0; i < 4; ++i) - mF32[i] *= inV2; -#endif - return *this; -} - -Vec4 &Vec4::operator *= (Vec4Arg inV2) -{ -#if defined(JPH_USE_SSE) - mValue = _mm_mul_ps(mValue, inV2.mValue); -#elif defined(JPH_USE_NEON) - mValue = vmulq_f32(mValue, inV2.mValue); -#else - for (int i = 0; i < 4; ++i) - mF32[i] *= inV2.mF32[i]; -#endif - return *this; -} - -Vec4 &Vec4::operator /= (float inV2) -{ -#if defined(JPH_USE_SSE) - mValue = _mm_div_ps(mValue, _mm_set1_ps(inV2)); -#elif defined(JPH_USE_NEON) - mValue = vdivq_f32(mValue, vdupq_n_f32(inV2)); -#else - for (int i = 0; i < 4; ++i) - mF32[i] /= inV2; -#endif - return *this; -} - -Vec4 Vec4::operator + (Vec4Arg inV2) const -{ -#if defined(JPH_USE_SSE) - return _mm_add_ps(mValue, inV2.mValue); -#elif defined(JPH_USE_NEON) - return vaddq_f32(mValue, inV2.mValue); -#else - return Vec4(mF32[0] + inV2.mF32[0], - mF32[1] + inV2.mF32[1], - mF32[2] + inV2.mF32[2], - mF32[3] + inV2.mF32[3]); -#endif -} - -Vec4 &Vec4::operator += (Vec4Arg inV2) -{ -#if defined(JPH_USE_SSE) - mValue = _mm_add_ps(mValue, inV2.mValue); -#elif defined(JPH_USE_NEON) - mValue = vaddq_f32(mValue, inV2.mValue); -#else - for (int i = 0; i < 4; ++i) - mF32[i] += inV2.mF32[i]; -#endif - return *this; -} - -Vec4 Vec4::operator - () const -{ -#if defined(JPH_USE_SSE) - return _mm_sub_ps(_mm_setzero_ps(), mValue); -#elif defined(JPH_USE_NEON) - return vnegq_f32(mValue); -#else - return Vec4(-mF32[0], -mF32[1], -mF32[2], -mF32[3]); -#endif -} - -Vec4 Vec4::operator - (Vec4Arg inV2) const -{ -#if defined(JPH_USE_SSE) - return _mm_sub_ps(mValue, inV2.mValue); -#elif defined(JPH_USE_NEON) - return vsubq_f32(mValue, inV2.mValue); -#else - return Vec4(mF32[0] - inV2.mF32[0], - mF32[1] - inV2.mF32[1], - mF32[2] - inV2.mF32[2], - mF32[3] - inV2.mF32[3]); -#endif -} - -Vec4 &Vec4::operator -= (Vec4Arg inV2) -{ -#if defined(JPH_USE_SSE) - mValue = _mm_sub_ps(mValue, inV2.mValue); -#elif defined(JPH_USE_NEON) - mValue = vsubq_f32(mValue, inV2.mValue); -#else - for (int i = 0; i < 4; ++i) - mF32[i] -= inV2.mF32[i]; -#endif - return *this; -} - -Vec4 Vec4::operator / (Vec4Arg inV2) const -{ -#if defined(JPH_USE_SSE) - return _mm_div_ps(mValue, inV2.mValue); -#elif defined(JPH_USE_NEON) - return vdivq_f32(mValue, inV2.mValue); -#else - return Vec4(mF32[0] / inV2.mF32[0], - mF32[1] / inV2.mF32[1], - mF32[2] / inV2.mF32[2], - mF32[3] / inV2.mF32[3]); -#endif -} - -Vec4 Vec4::SplatX() const -{ -#if defined(JPH_USE_SSE) - return _mm_shuffle_ps(mValue, mValue, _MM_SHUFFLE(0, 0, 0, 0)); -#elif defined(JPH_USE_NEON) - return vdupq_laneq_f32(mValue, 0); -#else - return Vec4(mF32[0], mF32[0], mF32[0], mF32[0]); -#endif -} - -Vec4 Vec4::SplatY() const -{ -#if defined(JPH_USE_SSE) - return _mm_shuffle_ps(mValue, mValue, _MM_SHUFFLE(1, 1, 1, 1)); -#elif defined(JPH_USE_NEON) - return vdupq_laneq_f32(mValue, 1); -#else - return Vec4(mF32[1], mF32[1], mF32[1], mF32[1]); -#endif -} - -Vec4 Vec4::SplatZ() const -{ -#if defined(JPH_USE_SSE) - return _mm_shuffle_ps(mValue, mValue, _MM_SHUFFLE(2, 2, 2, 2)); -#elif defined(JPH_USE_NEON) - return vdupq_laneq_f32(mValue, 2); -#else - return Vec4(mF32[2], mF32[2], mF32[2], mF32[2]); -#endif -} - -Vec4 Vec4::SplatW() const -{ -#if defined(JPH_USE_SSE) - return _mm_shuffle_ps(mValue, mValue, _MM_SHUFFLE(3, 3, 3, 3)); -#elif defined(JPH_USE_NEON) - return vdupq_laneq_f32(mValue, 3); -#else - return Vec4(mF32[3], mF32[3], mF32[3], mF32[3]); -#endif -} - -Vec4 Vec4::Abs() const -{ -#if defined(JPH_USE_AVX512) - return _mm_range_ps(mValue, mValue, 0b1000); -#elif defined(JPH_USE_SSE) - return _mm_max_ps(_mm_sub_ps(_mm_setzero_ps(), mValue), mValue); -#elif defined(JPH_USE_NEON) - return vabsq_f32(mValue); -#else - return Vec4(abs(mF32[0]), abs(mF32[1]), abs(mF32[2]), abs(mF32[3])); -#endif -} - -Vec4 Vec4::Reciprocal() const -{ - return sReplicate(1.0f) / mValue; -} - -Vec4 Vec4::DotV(Vec4Arg inV2) const -{ -#if defined(JPH_USE_SSE4_1) - return _mm_dp_ps(mValue, inV2.mValue, 0xff); -#elif defined(JPH_USE_NEON) - float32x4_t mul = vmulq_f32(mValue, inV2.mValue); - return vdupq_n_f32(vaddvq_f32(mul)); -#else - // Brackets placed so that the order is consistent with the vectorized version - return Vec4::sReplicate((mF32[0] * inV2.mF32[0] + mF32[1] * inV2.mF32[1]) + (mF32[2] * inV2.mF32[2] + mF32[3] * inV2.mF32[3])); -#endif -} - -float Vec4::Dot(Vec4Arg inV2) const -{ -#if defined(JPH_USE_SSE4_1) - return _mm_cvtss_f32(_mm_dp_ps(mValue, inV2.mValue, 0xff)); -#elif defined(JPH_USE_NEON) - float32x4_t mul = vmulq_f32(mValue, inV2.mValue); - return vaddvq_f32(mul); -#else - // Brackets placed so that the order is consistent with the vectorized version - return (mF32[0] * inV2.mF32[0] + mF32[1] * inV2.mF32[1]) + (mF32[2] * inV2.mF32[2] + mF32[3] * inV2.mF32[3]); -#endif -} - -float Vec4::LengthSq() const -{ -#if defined(JPH_USE_SSE4_1) - return _mm_cvtss_f32(_mm_dp_ps(mValue, mValue, 0xff)); -#elif defined(JPH_USE_NEON) - float32x4_t mul = vmulq_f32(mValue, mValue); - return vaddvq_f32(mul); -#else - // Brackets placed so that the order is consistent with the vectorized version - return (mF32[0] * mF32[0] + mF32[1] * mF32[1]) + (mF32[2] * mF32[2] + mF32[3] * mF32[3]); -#endif -} - -float Vec4::Length() const -{ -#if defined(JPH_USE_SSE4_1) - return _mm_cvtss_f32(_mm_sqrt_ss(_mm_dp_ps(mValue, mValue, 0xff))); -#elif defined(JPH_USE_NEON) - float32x4_t mul = vmulq_f32(mValue, mValue); - float32x2_t sum = vdup_n_f32(vaddvq_f32(mul)); - return vget_lane_f32(vsqrt_f32(sum), 0); -#else - // Brackets placed so that the order is consistent with the vectorized version - return sqrt((mF32[0] * mF32[0] + mF32[1] * mF32[1]) + (mF32[2] * mF32[2] + mF32[3] * mF32[3])); -#endif -} - -Vec4 Vec4::Sqrt() const -{ -#if defined(JPH_USE_SSE) - return _mm_sqrt_ps(mValue); -#elif defined(JPH_USE_NEON) - return vsqrtq_f32(mValue); -#else - return Vec4(sqrt(mF32[0]), sqrt(mF32[1]), sqrt(mF32[2]), sqrt(mF32[3])); -#endif -} - - -Vec4 Vec4::GetSign() const -{ -#if defined(JPH_USE_AVX512) - return _mm_fixupimm_ps(mValue, mValue, _mm_set1_epi32(0xA9A90A00), 0); -#elif defined(JPH_USE_SSE) - Type minus_one = _mm_set1_ps(-1.0f); - Type one = _mm_set1_ps(1.0f); - return _mm_or_ps(_mm_and_ps(mValue, minus_one), one); -#elif defined(JPH_USE_NEON) - Type minus_one = vdupq_n_f32(-1.0f); - Type one = vdupq_n_f32(1.0f); - return vorrq_s32(vandq_s32(mValue, minus_one), one); -#else - return Vec4(signbit(mF32[0])? -1.0f : 1.0f, - signbit(mF32[1])? -1.0f : 1.0f, - signbit(mF32[2])? -1.0f : 1.0f, - signbit(mF32[3])? -1.0f : 1.0f); -#endif -} - -Vec4 Vec4::Normalized() const -{ -#if defined(JPH_USE_SSE4_1) - return _mm_div_ps(mValue, _mm_sqrt_ps(_mm_dp_ps(mValue, mValue, 0xff))); -#elif defined(JPH_USE_NEON) - float32x4_t mul = vmulq_f32(mValue, mValue); - float32x4_t sum = vdupq_n_f32(vaddvq_f32(mul)); - return vdivq_f32(mValue, vsqrtq_f32(sum)); -#else - return *this / Length(); -#endif -} - -void Vec4::StoreFloat4(Float4 *outV) const -{ -#if defined(JPH_USE_SSE) - _mm_storeu_ps(&outV->x, mValue); -#elif defined(JPH_USE_NEON) - vst1q_f32(&outV->x, mValue); -#else - for (int i = 0; i < 4; ++i) - (&outV->x)[i] = mF32[i]; -#endif -} - -UVec4 Vec4::ToInt() const -{ -#if defined(JPH_USE_SSE) - return _mm_cvttps_epi32(mValue); -#elif defined(JPH_USE_NEON) - return vcvtq_u32_f32(mValue); -#else - return UVec4(uint32(mF32[0]), uint32(mF32[1]), uint32(mF32[2]), uint32(mF32[3])); -#endif -} - -UVec4 Vec4::ReinterpretAsInt() const -{ -#if defined(JPH_USE_SSE) - return UVec4(_mm_castps_si128(mValue)); -#elif defined(JPH_USE_NEON) - return vreinterpretq_u32_f32(mValue); -#else - return *reinterpret_cast(this); -#endif -} - -int Vec4::GetSignBits() const -{ -#if defined(JPH_USE_SSE) - return _mm_movemask_ps(mValue); -#elif defined(JPH_USE_NEON) - int32x4_t shift = JPH_NEON_INT32x4(0, 1, 2, 3); - return vaddvq_u32(vshlq_u32(vshrq_n_u32(vreinterpretq_u32_f32(mValue), 31), shift)); -#else - return (signbit(mF32[0])? 1 : 0) | (signbit(mF32[1])? 2 : 0) | (signbit(mF32[2])? 4 : 0) | (signbit(mF32[3])? 8 : 0); -#endif -} - -float Vec4::ReduceMin() const -{ - Vec4 v = sMin(mValue, Swizzle()); - v = sMin(v, v.Swizzle()); - return v.GetX(); -} - -float Vec4::ReduceMax() const -{ - Vec4 v = sMax(mValue, Swizzle()); - v = sMax(v, v.Swizzle()); - return v.GetX(); -} - -void Vec4::SinCos(Vec4 &outSin, Vec4 &outCos) const -{ - // Implementation based on sinf.c from the cephes library, combines sinf and cosf in a single function, changes octants to quadrants and vectorizes it - // Original implementation by Stephen L. Moshier (See: http://www.moshier.net/) - - // Make argument positive and remember sign for sin only since cos is symmetric around x (highest bit of a float is the sign bit) - UVec4 sin_sign = UVec4::sAnd(ReinterpretAsInt(), UVec4::sReplicate(0x80000000U)); - Vec4 x = Vec4::sXor(*this, sin_sign.ReinterpretAsFloat()); - - // x / (PI / 2) rounded to nearest int gives us the quadrant closest to x - UVec4 quadrant = (0.6366197723675814f * x + Vec4::sReplicate(0.5f)).ToInt(); - - // Make x relative to the closest quadrant. - // This does x = x - quadrant * PI / 2 using a two step Cody-Waite argument reduction. - // This improves the accuracy of the result by avoiding loss of significant bits in the subtraction. - // We start with x = x - quadrant * PI / 2, PI / 2 in hexadecimal notation is 0x3fc90fdb, we remove the lowest 16 bits to - // get 0x3fc90000 (= 1.5703125) this means we can now multiply with a number of up to 2^16 without losing any bits. - // This leaves us with: x = (x - quadrant * 1.5703125) - quadrant * (PI / 2 - 1.5703125). - // PI / 2 - 1.5703125 in hexadecimal is 0x39fdaa22, stripping the lowest 12 bits we get 0x39fda000 (= 0.0004837512969970703125) - // This leaves uw with: x = ((x - quadrant * 1.5703125) - quadrant * 0.0004837512969970703125) - quadrant * (PI / 2 - 1.5703125 - 0.0004837512969970703125) - // See: https://stackoverflow.com/questions/42455143/sine-cosine-modular-extended-precision-arithmetic - // After this we have x in the range [-PI / 4, PI / 4]. - Vec4 float_quadrant = quadrant.ToFloat(); - x = ((x - float_quadrant * 1.5703125f) - float_quadrant * 0.0004837512969970703125f) - float_quadrant * 7.549789948768648e-8f; - - // Calculate x2 = x^2 - Vec4 x2 = x * x; - - // Taylor expansion: - // Cos(x) = 1 - x^2/2! + x^4/4! - x^6/6! + x^8/8! + ... = (((x2/8!- 1/6!) * x2 + 1/4!) * x2 - 1/2!) * x2 + 1 - Vec4 taylor_cos = ((2.443315711809948e-5f * x2 - Vec4::sReplicate(1.388731625493765e-3f)) * x2 + Vec4::sReplicate(4.166664568298827e-2f)) * x2 * x2 - 0.5f * x2 + Vec4::sReplicate(1.0f); - // Sin(x) = x - x^3/3! + x^5/5! - x^7/7! + ... = ((-x2/7! + 1/5!) * x2 - 1/3!) * x2 * x + x - Vec4 taylor_sin = ((-1.9515295891e-4f * x2 + Vec4::sReplicate(8.3321608736e-3f)) * x2 - Vec4::sReplicate(1.6666654611e-1f)) * x2 * x + x; - - // The lowest 2 bits of quadrant indicate the quadrant that we are in. - // Let x be the original input value and x' our value that has been mapped to the range [-PI / 4, PI / 4]. - // since cos(x) = sin(x - PI / 2) and since we want to use the Taylor expansion as close as possible to 0, - // we can alternate between using the Taylor expansion for sin and cos according to the following table: - // - // quadrant sin(x) cos(x) - // XXX00b sin(x') cos(x') - // XXX01b cos(x') -sin(x') - // XXX10b -sin(x') -cos(x') - // XXX11b -cos(x') sin(x') - // - // So: sin_sign = bit2, cos_sign = bit1 ^ bit2, bit1 determines if we use sin or cos Taylor expansion - UVec4 bit1 = quadrant.LogicalShiftLeft<31>(); - UVec4 bit2 = UVec4::sAnd(quadrant.LogicalShiftLeft<30>(), UVec4::sReplicate(0x80000000U)); - - // Select which one of the results is sin and which one is cos - Vec4 s = Vec4::sSelect(taylor_sin, taylor_cos, bit1); - Vec4 c = Vec4::sSelect(taylor_cos, taylor_sin, bit1); - - // Update the signs - sin_sign = UVec4::sXor(sin_sign, bit2); - UVec4 cos_sign = UVec4::sXor(bit1, bit2); - - // Correct the signs - outSin = Vec4::sXor(s, sin_sign.ReinterpretAsFloat()); - outCos = Vec4::sXor(c, cos_sign.ReinterpretAsFloat()); -} - -Vec4 Vec4::Tan() const -{ - // Implementation based on tanf.c from the cephes library, see Vec4::SinCos for further details - // Original implementation by Stephen L. Moshier (See: http://www.moshier.net/) - - // Make argument positive - UVec4 tan_sign = UVec4::sAnd(ReinterpretAsInt(), UVec4::sReplicate(0x80000000U)); - Vec4 x = Vec4::sXor(*this, tan_sign.ReinterpretAsFloat()); - - // x / (PI / 2) rounded to nearest int gives us the quadrant closest to x - UVec4 quadrant = (0.6366197723675814f * x + Vec4::sReplicate(0.5f)).ToInt(); - - // Remap x to range [-PI / 4, PI / 4], see Vec4::SinCos - Vec4 float_quadrant = quadrant.ToFloat(); - x = ((x - float_quadrant * 1.5703125f) - float_quadrant * 0.0004837512969970703125f) - float_quadrant * 7.549789948768648e-8f; - - // Calculate x2 = x^2 - Vec4 x2 = x * x; - - // Roughly equivalent to the Taylor expansion: - // Tan(x) = x + x^3/3 + 2*x^5/15 + 17*x^7/315 + 62*x^9/2835 + ... - Vec4 tan = - (((((9.38540185543e-3f * x2 + Vec4::sReplicate(3.11992232697e-3f)) * x2 + Vec4::sReplicate(2.44301354525e-2f)) * x2 - + Vec4::sReplicate(5.34112807005e-2f)) * x2 + Vec4::sReplicate(1.33387994085e-1f)) * x2 + Vec4::sReplicate(3.33331568548e-1f)) * x2 * x + x; - - // For the 2nd and 4th quadrant we need to invert the value - UVec4 bit1 = quadrant.LogicalShiftLeft<31>(); - tan = Vec4::sSelect(tan, Vec4::sReplicate(-1.0f) / (tan JPH_IF_FLOATING_POINT_EXCEPTIONS_ENABLED(+ Vec4::sReplicate(FLT_MIN))), bit1); // Add small epsilon to prevent div by zero, works because tan is always positive - - // Put the sign back - return Vec4::sXor(tan, tan_sign.ReinterpretAsFloat()); -} - -Vec4 Vec4::ASin() const -{ - // Implementation based on asinf.c from the cephes library - // Original implementation by Stephen L. Moshier (See: http://www.moshier.net/) - - // Make argument positive - UVec4 asin_sign = UVec4::sAnd(ReinterpretAsInt(), UVec4::sReplicate(0x80000000U)); - Vec4 a = Vec4::sXor(*this, asin_sign.ReinterpretAsFloat()); - - // ASin is not defined outside the range [-1, 1] but it often happens that a value is slightly above 1 so we just clamp here - a = Vec4::sMin(a, Vec4::sReplicate(1.0f)); - - // When |x| <= 0.5 we use the asin approximation as is - Vec4 z1 = a * a; - Vec4 x1 = a; - - // When |x| > 0.5 we use the identity asin(x) = PI / 2 - 2 * asin(sqrt((1 - x) / 2)) - Vec4 z2 = 0.5f * (Vec4::sReplicate(1.0f) - a); - Vec4 x2 = z2.Sqrt(); - - // Select which of the two situations we have - UVec4 greater = Vec4::sGreater(a, Vec4::sReplicate(0.5f)); - Vec4 z = Vec4::sSelect(z1, z2, greater); - Vec4 x = Vec4::sSelect(x1, x2, greater); - - // Polynomial approximation of asin - z = ((((4.2163199048e-2f * z + Vec4::sReplicate(2.4181311049e-2f)) * z + Vec4::sReplicate(4.5470025998e-2f)) * z + Vec4::sReplicate(7.4953002686e-2f)) * z + Vec4::sReplicate(1.6666752422e-1f)) * z * x + x; - - // If |x| > 0.5 we need to apply the remainder of the identity above - z = Vec4::sSelect(z, Vec4::sReplicate(0.5f * JPH_PI) - (z + z), greater); - - // Put the sign back - return Vec4::sXor(z, asin_sign.ReinterpretAsFloat()); -} - -Vec4 Vec4::ACos() const -{ - // Not the most accurate, but simple - return Vec4::sReplicate(0.5f * JPH_PI) - ASin(); -} - -Vec4 Vec4::ATan() const -{ - // Implementation based on atanf.c from the cephes library - // Original implementation by Stephen L. Moshier (See: http://www.moshier.net/) - - // Make argument positive - UVec4 atan_sign = UVec4::sAnd(ReinterpretAsInt(), UVec4::sReplicate(0x80000000U)); - Vec4 x = Vec4::sXor(*this, atan_sign.ReinterpretAsFloat()); - Vec4 y = Vec4::sZero(); - - // If x > Tan(PI / 8) - UVec4 greater1 = Vec4::sGreater(x, Vec4::sReplicate(0.4142135623730950f)); - Vec4 x1 = (x - Vec4::sReplicate(1.0f)) / (x + Vec4::sReplicate(1.0f)); - - // If x > Tan(3 * PI / 8) - UVec4 greater2 = Vec4::sGreater(x, Vec4::sReplicate(2.414213562373095f)); - Vec4 x2 = Vec4::sReplicate(-1.0f) / (x JPH_IF_FLOATING_POINT_EXCEPTIONS_ENABLED(+ Vec4::sReplicate(FLT_MIN))); // Add small epsilon to prevent div by zero, works because x is always positive - - // Apply first if - x = Vec4::sSelect(x, x1, greater1); - y = Vec4::sSelect(y, Vec4::sReplicate(0.25f * JPH_PI), greater1); - - // Apply second if - x = Vec4::sSelect(x, x2, greater2); - y = Vec4::sSelect(y, Vec4::sReplicate(0.5f * JPH_PI), greater2); - - // Polynomial approximation - Vec4 z = x * x; - y += (((8.05374449538e-2f * z - Vec4::sReplicate(1.38776856032e-1f)) * z + Vec4::sReplicate(1.99777106478e-1f)) * z - Vec4::sReplicate(3.33329491539e-1f)) * z * x + x; - - // Put the sign back - return Vec4::sXor(y, atan_sign.ReinterpretAsFloat()); -} - -Vec4 Vec4::sATan2(Vec4Arg inY, Vec4Arg inX) -{ - UVec4 sign_mask = UVec4::sReplicate(0x80000000U); - - // Determine absolute value and sign of y - UVec4 y_sign = UVec4::sAnd(inY.ReinterpretAsInt(), sign_mask); - Vec4 y_abs = Vec4::sXor(inY, y_sign.ReinterpretAsFloat()); - - // Determine absolute value and sign of x - UVec4 x_sign = UVec4::sAnd(inX.ReinterpretAsInt(), sign_mask); - Vec4 x_abs = Vec4::sXor(inX, x_sign.ReinterpretAsFloat()); - - // Always divide smallest / largest to avoid dividing by zero - UVec4 x_is_numerator = Vec4::sLess(x_abs, y_abs); - Vec4 numerator = Vec4::sSelect(y_abs, x_abs, x_is_numerator); - Vec4 denominator = Vec4::sSelect(x_abs, y_abs, x_is_numerator); - Vec4 atan = (numerator / denominator).ATan(); - - // If we calculated x / y instead of y / x the result is PI / 2 - result (note that this is true because we know the result is positive because the input was positive) - atan = Vec4::sSelect(atan, Vec4::sReplicate(0.5f * JPH_PI) - atan, x_is_numerator); - - // Now we need to map to the correct quadrant - // x_sign y_sign result - // +1 +1 atan - // -1 +1 -atan + PI - // -1 -1 atan - PI - // +1 -1 -atan - // This can be written as: x_sign * y_sign * (atan - (x_sign < 0? PI : 0)) - atan -= Vec4::sAnd(x_sign.ArithmeticShiftRight<31>().ReinterpretAsFloat(), Vec4::sReplicate(JPH_PI)); - atan = Vec4::sXor(atan, UVec4::sXor(x_sign, y_sign).ReinterpretAsFloat()); - return atan; -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Vec8.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Vec8.h deleted file mode 100644 index 639d0415cb3..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Vec8.h +++ /dev/null @@ -1,112 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -class [[nodiscard]] Vec8 -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Constructor - Vec8() = default; ///< Intentionally not initialized for performance reasons - Vec8(const Vec8 &inRHS) = default; - JPH_INLINE Vec8(__m256 inRHS) : mValue(inRHS) { } - - /// Set 256 bit vector from 2 128 bit vectors - JPH_INLINE Vec8(Vec4Arg inLo, Vec4Arg inHi); - - /// Vector with all zeros - static JPH_INLINE Vec8 sZero(); - - /// Replicate across all components - static JPH_INLINE Vec8 sReplicate(float inV); - - /// Replicate the X component of inV to all components - static JPH_INLINE Vec8 sSplatX(Vec4Arg inV); - - /// Replicate the Y component of inV to all components - static JPH_INLINE Vec8 sSplatY(Vec4Arg inV); - - /// Replicate the Z component of inV to all components - static JPH_INLINE Vec8 sSplatZ(Vec4Arg inV); - - /// Calculates inMul1 * inMul2 + inAdd - static JPH_INLINE Vec8 sFusedMultiplyAdd(Vec8Arg inMul1, Vec8Arg inMul2, Vec8Arg inAdd); - - /// Component wise select, returns inV1 when highest bit of inControl = 0 and inV2 when highest bit of inControl = 1 - static JPH_INLINE Vec8 sSelect(Vec8Arg inV1, Vec8Arg inV2, UVec8Arg inControl); - - /// Component wise min - static JPH_INLINE Vec8 sMin(Vec8Arg inV1, Vec8Arg inV2); - - /// Component wise max - static JPH_INLINE Vec8 sMax(Vec8Arg inV1, Vec8Arg inV2); - - /// Less than - static JPH_INLINE UVec8 sLess(Vec8Arg inV1, Vec8Arg inV2); - - /// Greater than - static JPH_INLINE UVec8 sGreater(Vec8Arg inV1, Vec8Arg inV2); - - /// Load from memory - static JPH_INLINE Vec8 sLoadFloat8(const float *inV); - - /// Load 8 floats from memory, 32 bytes aligned - static JPH_INLINE Vec8 sLoadFloat8Aligned(const float *inV); - - /// Get float component by index - JPH_INLINE float operator [] (uint inCoordinate) const { JPH_ASSERT(inCoordinate < 8); return mF32[inCoordinate]; } - JPH_INLINE float & operator [] (uint inCoordinate) { JPH_ASSERT(inCoordinate < 8); return mF32[inCoordinate]; } - - /// Multiply two float vectors - JPH_INLINE Vec8 operator * (Vec8Arg inV2) const; - - /// Multiply vector by float - JPH_INLINE Vec8 operator * (float inV2) const; - - /// Add two float vectors - JPH_INLINE Vec8 operator + (Vec8Arg inV2) const; - - /// Subtract two float vectors - JPH_INLINE Vec8 operator - (Vec8Arg inV2) const; - - /// Divide - JPH_INLINE Vec8 operator / (Vec8Arg inV2) const; - - /// Reciprocal vector - JPH_INLINE Vec8 Reciprocal() const; - - /// 256 bit variant of Vec::Swizzle (no cross 128 bit lane swizzle) - template - JPH_INLINE Vec8 Swizzle() const; - - /// Get absolute value of all components - JPH_INLINE Vec8 Abs() const; - - /// Fetch the lower 128 bit from a 256 bit variable - JPH_INLINE Vec4 LowerVec4() const; - - /// Fetch the higher 128 bit from a 256 bit variable - JPH_INLINE Vec4 UpperVec4() const; - - /// Get the minimum value of the 8 floats - JPH_INLINE float ReduceMin() const; - - union - { - __m256 mValue; - float mF32[8]; - }; -}; - -static_assert(is_trivial(), "Is supposed to be a trivial type!"); - -JPH_NAMESPACE_END - -#include "Vec8.inl" diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Vec8.inl b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Vec8.inl deleted file mode 100644 index f4d462ad051..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Vec8.inl +++ /dev/null @@ -1,148 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -JPH_NAMESPACE_BEGIN - -Vec8::Vec8(Vec4Arg inLo, Vec4Arg inHi) : - mValue(_mm256_insertf128_ps(_mm256_castps128_ps256(inLo.mValue), inHi.mValue, 1)) -{ -} - -Vec8 Vec8::sZero() -{ - return _mm256_setzero_ps(); -} - -Vec8 Vec8::sReplicate(float inV) -{ - return _mm256_set1_ps(inV); -} - -Vec8 Vec8::sSplatX(Vec4Arg inV) -{ - return _mm256_set1_ps(inV.GetX()); -} - -Vec8 Vec8::sSplatY(Vec4Arg inV) -{ - return _mm256_set1_ps(inV.GetY()); -} - -Vec8 Vec8::sSplatZ(Vec4Arg inV) -{ - return _mm256_set1_ps(inV.GetZ()); -} - -Vec8 Vec8::sFusedMultiplyAdd(Vec8Arg inMul1, Vec8Arg inMul2, Vec8Arg inAdd) -{ -#ifdef JPH_USE_FMADD - return _mm256_fmadd_ps(inMul1.mValue, inMul2.mValue, inAdd.mValue); -#else - return _mm256_add_ps(_mm256_mul_ps(inMul1.mValue, inMul2.mValue), inAdd.mValue); -#endif -} - -Vec8 Vec8::sSelect(Vec8Arg inV1, Vec8Arg inV2, UVec8Arg inControl) -{ - return _mm256_blendv_ps(inV1.mValue, inV2.mValue, _mm256_castsi256_ps(inControl.mValue)); -} - -Vec8 Vec8::sMin(Vec8Arg inV1, Vec8Arg inV2) -{ - return _mm256_min_ps(inV1.mValue, inV2.mValue); -} - -Vec8 Vec8::sMax(Vec8Arg inV1, Vec8Arg inV2) -{ - return _mm256_max_ps(inV1.mValue, inV2.mValue); -} - -UVec8 Vec8::sLess(Vec8Arg inV1, Vec8Arg inV2) -{ - return _mm256_castps_si256(_mm256_cmp_ps(inV1.mValue, inV2.mValue, _CMP_LT_OQ)); -} - -UVec8 Vec8::sGreater(Vec8Arg inV1, Vec8Arg inV2) -{ - return _mm256_castps_si256(_mm256_cmp_ps(inV1.mValue, inV2.mValue, _CMP_GT_OQ)); -} - -Vec8 Vec8::sLoadFloat8(const float *inV) -{ - return _mm256_loadu_ps(inV); -} - -Vec8 Vec8::sLoadFloat8Aligned(const float *inV) -{ - return _mm256_load_ps(inV); -} - -Vec8 Vec8::operator * (Vec8Arg inV2) const -{ - return _mm256_mul_ps(mValue, inV2.mValue); -} - -Vec8 Vec8::operator * (float inV2) const -{ - return _mm256_mul_ps(mValue, _mm256_set1_ps(inV2)); -} - -Vec8 Vec8::operator + (Vec8Arg inV2) const -{ - return _mm256_add_ps(mValue, inV2.mValue); -} - -Vec8 Vec8::operator - (Vec8Arg inV2) const -{ - return _mm256_sub_ps(mValue, inV2.mValue); -} - -Vec8 Vec8::operator / (Vec8Arg inV2) const -{ - return _mm256_div_ps(mValue, inV2.mValue); -} - -Vec8 Vec8::Reciprocal() const -{ - return Vec8::sReplicate(1.0f) / mValue; -} - -template -Vec8 Vec8::Swizzle() const -{ - static_assert(SwizzleX <= 3, "SwizzleX template parameter out of range"); - static_assert(SwizzleY <= 3, "SwizzleY template parameter out of range"); - static_assert(SwizzleZ <= 3, "SwizzleZ template parameter out of range"); - static_assert(SwizzleW <= 3, "SwizzleW template parameter out of range"); - - return _mm256_shuffle_ps(mValue, mValue, _MM_SHUFFLE(SwizzleW, SwizzleZ, SwizzleY, SwizzleX)); -} - -Vec8 Vec8::Abs() const -{ -#if defined(JPH_USE_AVX512) - return _mm256_range_ps(mValue, mValue, 0b1000); -#else - return _mm256_max_ps(_mm256_sub_ps(_mm256_setzero_ps(), mValue), mValue); -#endif -} - -Vec4 Vec8::LowerVec4() const -{ - return _mm256_castps256_ps128(mValue); -} - -Vec4 Vec8::UpperVec4() const -{ - return _mm256_extractf128_ps(mValue, 1); -} - -float Vec8::ReduceMin() const -{ - return Vec4::sMin(LowerVec4(), UpperVec4()).ReduceMin(); -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Vector.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Vector.h deleted file mode 100644 index 5880a2c87c8..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Math/Vector.h +++ /dev/null @@ -1,216 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -JPH_NAMESPACE_BEGIN - -/// Templatized vector class -template -class [[nodiscard]] Vector -{ -public: - /// Constructor - inline Vector() = default; - inline Vector(const Vector &inRHS) { *this = inRHS; } - - /// Dimensions - inline uint GetRows() const { return Rows; } - - /// Vector with all zeros - inline void SetZero() - { - for (uint r = 0; r < Rows; ++r) - mF32[r] = 0.0f; - } - - inline static Vector sZero() { Vector v; v.SetZero(); return v; } - - /// Copy a (part) of another vector into this vector - template - void CopyPart(const OtherVector &inV, uint inSourceRow, uint inNumRows, uint inDestRow) - { - for (uint r = 0; r < inNumRows; ++r) - mF32[inDestRow + r] = inV[inSourceRow + r]; - } - - /// Get float component by index - inline float operator [] (uint inCoordinate) const - { - JPH_ASSERT(inCoordinate < Rows); - return mF32[inCoordinate]; - } - - inline float & operator [] (uint inCoordinate) - { - JPH_ASSERT(inCoordinate < Rows); - return mF32[inCoordinate]; - } - - /// Comparison - inline bool operator == (const Vector &inV2) const - { - for (uint r = 0; r < Rows; ++r) - if (mF32[r] != inV2.mF32[r]) - return false; - return true; - } - - inline bool operator != (const Vector &inV2) const - { - for (uint r = 0; r < Rows; ++r) - if (mF32[r] != inV2.mF32[r]) - return true; - return false; - } - - /// Test if vector consists of all zeros - inline bool IsZero() const - { - for (uint r = 0; r < Rows; ++r) - if (mF32[r] != 0.0f) - return false; - return true; - } - - /// Test if two vectors are close to each other - inline bool IsClose(const Vector &inV2, float inMaxDistSq = 1.0e-12f) - { - return (inV2 - *this).LengthSq() <= inMaxDistSq; - } - - /// Assignment - inline Vector & operator = (const Vector &inV2) - { - for (uint r = 0; r < Rows; ++r) - mF32[r] = inV2.mF32[r]; - return *this; - } - - /// Multiply vector with float - inline Vector operator * (const float inV2) const - { - Vector v; - for (uint r = 0; r < Rows; ++r) - v.mF32[r] = mF32[r] * inV2; - return v; - } - - inline Vector & operator *= (const float inV2) - { - for (uint r = 0; r < Rows; ++r) - mF32[r] *= inV2; - return *this; - } - - /// Multiply vector with float - inline friend Vector operator * (const float inV1, const Vector &inV2) - { - return inV2 * inV1; - } - - /// Divide vector by float - inline Vector operator / (float inV2) const - { - Vector v; - for (uint r = 0; r < Rows; ++r) - v.mF32[r] = mF32[r] / inV2; - return v; - } - - inline Vector & operator /= (float inV2) - { - for (uint r = 0; r < Rows; ++r) - mF32[r] /= inV2; - return *this; - } - - /// Add two float vectors (component wise) - inline Vector operator + (const Vector &inV2) const - { - Vector v; - for (uint r = 0; r < Rows; ++r) - v.mF32[r] = mF32[r] + inV2.mF32[r]; - return v; - } - - inline Vector & operator += (const Vector &inV2) - { - for (uint r = 0; r < Rows; ++r) - mF32[r] += inV2.mF32[r]; - return *this; - } - - /// Negate - inline Vector operator - () const - { - Vector v; - for (uint r = 0; r < Rows; ++r) - v.mF32[r] = -mF32[r]; - return v; - } - - /// Subtract two float vectors (component wise) - inline Vector operator - (const Vector &inV2) const - { - Vector v; - for (uint r = 0; r < Rows; ++r) - v.mF32[r] = mF32[r] - inV2.mF32[r]; - return v; - } - - inline Vector & operator -= (const Vector &inV2) - { - for (uint r = 0; r < Rows; ++r) - mF32[r] -= inV2.mF32[r]; - return *this; - } - - /// Dot product - inline float Dot(const Vector &inV2) const - { - float dot = 0.0f; - for (uint r = 0; r < Rows; ++r) - dot += mF32[r] * inV2.mF32[r]; - return dot; - } - - /// Squared length of vector - inline float LengthSq() const - { - return Dot(*this); - } - - /// Length of vector - inline float Length() const - { - return sqrt(LengthSq()); - } - - /// Check if vector is normalized - inline bool IsNormalized(float inToleranceSq = 1.0e-6f) - { - return abs(LengthSq() - 1.0f) <= inToleranceSq; - } - - /// Normalize vector - inline Vector Normalized() const - { - return *this / Length(); - } - - /// To String - friend ostream & operator << (ostream &inStream, const Vector &inV) - { - inStream << "["; - for (uint i = 0; i < Rows - 1; ++i) - inStream << inV.mF32[i] << ", "; - inStream << inV.mF32[Rows - 1] << "]"; - return inStream; - } - - float mF32[Rows]; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/GetPrimitiveTypeOfType.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/GetPrimitiveTypeOfType.h deleted file mode 100644 index 15431c76ea0..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/GetPrimitiveTypeOfType.h +++ /dev/null @@ -1,54 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -/// Helper functions to get the underlying RTTI type of a type (so e.g. Array will return sometype) -template -const RTTI *GetPrimitiveTypeOfType(T *) -{ - return GetRTTIOfType((T *)nullptr); -} - -template -const RTTI *GetPrimitiveTypeOfType(T **) -{ - return GetRTTIOfType((T *)nullptr); -} - -template -const RTTI *GetPrimitiveTypeOfType(Ref *) -{ - return GetRTTIOfType((T *)nullptr); -} - -template -const RTTI *GetPrimitiveTypeOfType(RefConst *) -{ - return GetRTTIOfType((T *)nullptr); -} - -template -const RTTI *GetPrimitiveTypeOfType(Array *) -{ - return GetPrimitiveTypeOfType((T *)nullptr); -} - -template -const RTTI *GetPrimitiveTypeOfType(StaticArray *) -{ - return GetPrimitiveTypeOfType((T *)nullptr); -} - -template -const RTTI *GetPrimitiveTypeOfType(T (*)[N]) -{ - return GetPrimitiveTypeOfType((T *)nullptr); -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStream.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStream.cpp deleted file mode 100644 index 18d952a67dc..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStream.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include - -JPH_NAMESPACE_BEGIN - -// Define macro to declare functions for a specific primitive type -#define JPH_DECLARE_PRIMITIVE(name) \ - bool OSIsType(name *, int inArrayDepth, EOSDataType inDataType, const char *inClassName) \ - { \ - return inArrayDepth == 0 && inDataType == EOSDataType::T_##name; \ - } \ - bool OSReadData(IObjectStreamIn &ioStream, name &outPrimitive) \ - { \ - return ioStream.ReadPrimitiveData(outPrimitive); \ - } \ - void OSWriteDataType(IObjectStreamOut &ioStream, name *) \ - { \ - ioStream.WriteDataType(EOSDataType::T_##name); \ - } \ - void OSWriteData(IObjectStreamOut &ioStream, const name &inPrimitive) \ - { \ - ioStream.HintNextItem(); \ - ioStream.WritePrimitiveData(inPrimitive); \ - } - -// This file uses the JPH_DECLARE_PRIMITIVE macro to define all types -#include - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStream.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStream.h deleted file mode 100644 index e539cce83d8..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStream.h +++ /dev/null @@ -1,329 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -/// Base class for object stream input and output streams. -class JPH_EXPORT ObjectStream : public NonCopyable -{ -public: - /// Stream type - enum class EStreamType - { - Text, - Binary, - }; - -protected: - /// Destructor - virtual ~ObjectStream() = default; - - /// Identifier for objects - using Identifier = uint32; - - static constexpr int sVersion = 1; - static constexpr int sRevision = 0; - static constexpr Identifier sNullIdentifier = 0; -}; - -/// Interface class for reading from an object stream -class JPH_EXPORT IObjectStreamIn : public ObjectStream -{ -public: - ///@name Input type specific operations - virtual bool ReadDataType(EOSDataType &outType) = 0; - virtual bool ReadName(String &outName) = 0; - virtual bool ReadIdentifier(Identifier &outIdentifier) = 0; - virtual bool ReadCount(uint32 &outCount) = 0; - - ///@name Read primitives - virtual bool ReadPrimitiveData(uint8 &outPrimitive) = 0; - virtual bool ReadPrimitiveData(uint16 &outPrimitive) = 0; - virtual bool ReadPrimitiveData(int &outPrimitive) = 0; - virtual bool ReadPrimitiveData(uint32 &outPrimitive) = 0; - virtual bool ReadPrimitiveData(uint64 &outPrimitive) = 0; - virtual bool ReadPrimitiveData(float &outPrimitive) = 0; - virtual bool ReadPrimitiveData(double &outPrimitive) = 0; - virtual bool ReadPrimitiveData(bool &outPrimitive) = 0; - virtual bool ReadPrimitiveData(String &outPrimitive) = 0; - virtual bool ReadPrimitiveData(Float3 &outPrimitive) = 0; - virtual bool ReadPrimitiveData(Double3 &outPrimitive) = 0; - virtual bool ReadPrimitiveData(Vec3 &outPrimitive) = 0; - virtual bool ReadPrimitiveData(DVec3 &outPrimitive) = 0; - virtual bool ReadPrimitiveData(Vec4 &outPrimitive) = 0; - virtual bool ReadPrimitiveData(Quat &outPrimitive) = 0; - virtual bool ReadPrimitiveData(Mat44 &outPrimitive) = 0; - virtual bool ReadPrimitiveData(DMat44 &outPrimitive) = 0; - - ///@name Read compounds - virtual bool ReadClassData(const char *inClassName, void *inInstance) = 0; - virtual bool ReadPointerData(const RTTI *inRTTI, void **inPointer, int inRefCountOffset = -1) = 0; -}; - -/// Interface class for writing to an object stream -class JPH_EXPORT IObjectStreamOut : public ObjectStream -{ -public: - ///@name Output type specific operations - virtual void WriteDataType(EOSDataType inType) = 0; - virtual void WriteName(const char *inName) = 0; - virtual void WriteIdentifier(Identifier inIdentifier) = 0; - virtual void WriteCount(uint32 inCount) = 0; - - ///@name Write primitives - virtual void WritePrimitiveData(const uint8 &inPrimitive) = 0; - virtual void WritePrimitiveData(const uint16 &inPrimitive) = 0; - virtual void WritePrimitiveData(const int &inPrimitive) = 0; - virtual void WritePrimitiveData(const uint32 &inPrimitive) = 0; - virtual void WritePrimitiveData(const uint64 &inPrimitive) = 0; - virtual void WritePrimitiveData(const float &inPrimitive) = 0; - virtual void WritePrimitiveData(const double &inPrimitive) = 0; - virtual void WritePrimitiveData(const bool &inPrimitive) = 0; - virtual void WritePrimitiveData(const String &inPrimitive) = 0; - virtual void WritePrimitiveData(const Float3 &inPrimitive) = 0; - virtual void WritePrimitiveData(const Double3 &inPrimitive) = 0; - virtual void WritePrimitiveData(const Vec3 &inPrimitive) = 0; - virtual void WritePrimitiveData(const DVec3 &inPrimitive) = 0; - virtual void WritePrimitiveData(const Vec4 &inPrimitive) = 0; - virtual void WritePrimitiveData(const Quat &inPrimitive) = 0; - virtual void WritePrimitiveData(const Mat44 &inPrimitive) = 0; - virtual void WritePrimitiveData(const DMat44 &inPrimitive) = 0; - - ///@name Write compounds - virtual void WritePointerData(const RTTI *inRTTI, const void *inPointer) = 0; - virtual void WriteClassData(const RTTI *inRTTI, const void *inInstance) = 0; - - ///@name Layout hints (for text output) - virtual void HintNextItem() { /* Default is do nothing */ } - virtual void HintIndentUp() { /* Default is do nothing */ } - virtual void HintIndentDown() { /* Default is do nothing */ } -}; - -// Define macro to declare functions for a specific primitive type -#define JPH_DECLARE_PRIMITIVE(name) \ - JPH_EXPORT bool OSIsType(name *, int inArrayDepth, EOSDataType inDataType, const char *inClassName); \ - JPH_EXPORT bool OSReadData(IObjectStreamIn &ioStream, name &outPrimitive); \ - JPH_EXPORT void OSWriteDataType(IObjectStreamOut &ioStream, name *); \ - JPH_EXPORT void OSWriteData(IObjectStreamOut &ioStream, const name &inPrimitive); - -// This file uses the JPH_DECLARE_PRIMITIVE macro to define all types -#include - -// Define serialization templates -template -bool OSIsType(Array *, int inArrayDepth, EOSDataType inDataType, const char *inClassName) -{ - return (inArrayDepth > 0 && OSIsType(static_cast(nullptr), inArrayDepth - 1, inDataType, inClassName)); -} - -template -bool OSIsType(StaticArray *, int inArrayDepth, EOSDataType inDataType, const char *inClassName) -{ - return (inArrayDepth > 0 && OSIsType(static_cast(nullptr), inArrayDepth - 1, inDataType, inClassName)); -} - -template -bool OSIsType(T (*)[N], int inArrayDepth, EOSDataType inDataType, const char *inClassName) -{ - return (inArrayDepth > 0 && OSIsType(static_cast(nullptr), inArrayDepth - 1, inDataType, inClassName)); -} - -template -bool OSIsType(Ref *, int inArrayDepth, EOSDataType inDataType, const char *inClassName) -{ - return OSIsType(static_cast(nullptr), inArrayDepth, inDataType, inClassName); -} - -template -bool OSIsType(RefConst *, int inArrayDepth, EOSDataType inDataType, const char *inClassName) -{ - return OSIsType(static_cast(nullptr), inArrayDepth, inDataType, inClassName); -} - -/// Define serialization templates for dynamic arrays -template -bool OSReadData(IObjectStreamIn &ioStream, Array &inArray) -{ - bool continue_reading = true; - - // Read array length - uint32 array_length; - continue_reading = ioStream.ReadCount(array_length); - - // Read array items - if (continue_reading) - { - inArray.clear(); - inArray.resize(array_length); - for (uint32 el = 0; el < array_length && continue_reading; ++el) - continue_reading = OSReadData(ioStream, inArray[el]); - } - - return continue_reading; -} - -/// Define serialization templates for static arrays -template -bool OSReadData(IObjectStreamIn &ioStream, StaticArray &inArray) -{ - bool continue_reading = true; - - // Read array length - uint32 array_length; - continue_reading = ioStream.ReadCount(array_length); - - // Check if we can fit this many elements - if (array_length > N) - return false; - - // Read array items - if (continue_reading) - { - inArray.clear(); - inArray.resize(array_length); - for (uint32 el = 0; el < array_length && continue_reading; ++el) - continue_reading = OSReadData(ioStream, inArray[el]); - } - - return continue_reading; -} - -/// Define serialization templates for C style arrays -template -bool OSReadData(IObjectStreamIn &ioStream, T (&inArray)[N]) -{ - bool continue_reading = true; - - // Read array length - uint32 array_length; - continue_reading = ioStream.ReadCount(array_length); - if (array_length != N) - return false; - - // Read array items - for (uint32 el = 0; el < N && continue_reading; ++el) - continue_reading = OSReadData(ioStream, inArray[el]); - - return continue_reading; -} - -/// Define serialization templates for references -template -bool OSReadData(IObjectStreamIn &ioStream, Ref &inRef) -{ - return ioStream.ReadPointerData(JPH_RTTI(T), inRef.InternalGetPointer(), T::sInternalGetRefCountOffset()); -} - -template -bool OSReadData(IObjectStreamIn &ioStream, RefConst &inRef) -{ - return ioStream.ReadPointerData(JPH_RTTI(T), inRef.InternalGetPointer(), T::sInternalGetRefCountOffset()); -} - -// Define serialization templates for dynamic arrays -template -void OSWriteDataType(IObjectStreamOut &ioStream, Array *) -{ - ioStream.WriteDataType(EOSDataType::Array); - OSWriteDataType(ioStream, static_cast(nullptr)); -} - -template -void OSWriteData(IObjectStreamOut &ioStream, const Array &inArray) -{ - // Write size of array - ioStream.HintNextItem(); - ioStream.WriteCount(static_cast(inArray.size())); - - // Write data in array - ioStream.HintIndentUp(); - for (const T &v : inArray) - OSWriteData(ioStream, v); - ioStream.HintIndentDown(); -} - -/// Define serialization templates for static arrays -template -void OSWriteDataType(IObjectStreamOut &ioStream, StaticArray *) -{ - ioStream.WriteDataType(EOSDataType::Array); - OSWriteDataType(ioStream, static_cast(nullptr)); -} - -template -void OSWriteData(IObjectStreamOut &ioStream, const StaticArray &inArray) -{ - // Write size of array - ioStream.HintNextItem(); - ioStream.WriteCount(inArray.size()); - - // Write data in array - ioStream.HintIndentUp(); - for (const typename StaticArray::value_type &v : inArray) - OSWriteData(ioStream, v); - ioStream.HintIndentDown(); -} - -/// Define serialization templates for C style arrays -template -void OSWriteDataType(IObjectStreamOut &ioStream, T (*)[N]) -{ - ioStream.WriteDataType(EOSDataType::Array); - OSWriteDataType(ioStream, static_cast(nullptr)); -} - -template -void OSWriteData(IObjectStreamOut &ioStream, const T (&inArray)[N]) -{ - // Write size of array - ioStream.HintNextItem(); - ioStream.WriteCount(uint32(N)); - - // Write data in array - ioStream.HintIndentUp(); - for (const T &v : inArray) - OSWriteData(ioStream, v); - ioStream.HintIndentDown(); -} - -/// Define serialization templates for references -template -void OSWriteDataType(IObjectStreamOut &ioStream, Ref *) -{ - OSWriteDataType(ioStream, static_cast(nullptr)); -} - -template -void OSWriteData(IObjectStreamOut &ioStream, const Ref &inRef) -{ - if (inRef != nullptr) - ioStream.WritePointerData(GetRTTI(inRef.GetPtr()), inRef.GetPtr()); - else - ioStream.WritePointerData(nullptr, nullptr); -} - -template -void OSWriteDataType(IObjectStreamOut &ioStream, RefConst *) -{ - OSWriteDataType(ioStream, static_cast(nullptr)); -} - -template -void OSWriteData(IObjectStreamOut &ioStream, const RefConst &inRef) -{ - if (inRef != nullptr) - ioStream.WritePointerData(GetRTTI(inRef.GetPtr()), inRef.GetPtr()); - else - ioStream.WritePointerData(nullptr, nullptr); -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamBinaryIn.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamBinaryIn.cpp deleted file mode 100644 index 3f37e861064..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamBinaryIn.cpp +++ /dev/null @@ -1,230 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include - -JPH_NAMESPACE_BEGIN - -ObjectStreamBinaryIn::ObjectStreamBinaryIn(istream &inStream) : - ObjectStreamIn(inStream) -{ -} - -bool ObjectStreamBinaryIn::ReadDataType(EOSDataType &outType) -{ - uint32 type; - mStream.read((char *)&type, sizeof(type)); - if (mStream.fail()) return false; - outType = (EOSDataType)type; - return true; -} - -bool ObjectStreamBinaryIn::ReadName(String &outName) -{ - return ReadPrimitiveData(outName); -} - -bool ObjectStreamBinaryIn::ReadIdentifier(Identifier &outIdentifier) -{ - Identifier id; - mStream.read((char *)&id, sizeof(id)); - if (mStream.fail()) return false; - outIdentifier = id; - return true; -} - -bool ObjectStreamBinaryIn::ReadCount(uint32 &outCount) -{ - uint32 count; - mStream.read((char *)&count, sizeof(count)); - if (mStream.fail()) return false; - outCount = count; - return true; -} - -bool ObjectStreamBinaryIn::ReadPrimitiveData(uint8 &outPrimitive) -{ - uint8 primitive; - mStream.read((char *)&primitive, sizeof(primitive)); - if (mStream.fail()) return false; - outPrimitive = primitive; - return true; -} - -bool ObjectStreamBinaryIn::ReadPrimitiveData(uint16 &outPrimitive) -{ - uint16 primitive; - mStream.read((char *)&primitive, sizeof(primitive)); - if (mStream.fail()) return false; - outPrimitive = primitive; - return true; -} - -bool ObjectStreamBinaryIn::ReadPrimitiveData(int &outPrimitive) -{ - int primitive; - mStream.read((char *)&primitive, sizeof(primitive)); - if (mStream.fail()) return false; - outPrimitive = primitive; - return true; -} - -bool ObjectStreamBinaryIn::ReadPrimitiveData(uint32 &outPrimitive) -{ - uint32 primitive; - mStream.read((char *)&primitive, sizeof(primitive)); - if (mStream.fail()) return false; - outPrimitive = primitive; - return true; -} - -bool ObjectStreamBinaryIn::ReadPrimitiveData(uint64 &outPrimitive) -{ - uint64 primitive; - mStream.read((char *)&primitive, sizeof(primitive)); - if (mStream.fail()) return false; - outPrimitive = primitive; - return true; -} - -bool ObjectStreamBinaryIn::ReadPrimitiveData(float &outPrimitive) -{ - float primitive; - mStream.read((char *)&primitive, sizeof(primitive)); - if (mStream.fail()) return false; - outPrimitive = primitive; - return true; -} - -bool ObjectStreamBinaryIn::ReadPrimitiveData(double &outPrimitive) -{ - double primitive; - mStream.read((char *)&primitive, sizeof(primitive)); - if (mStream.fail()) return false; - outPrimitive = primitive; - return true; -} - -bool ObjectStreamBinaryIn::ReadPrimitiveData(bool &outPrimitive) -{ - bool primitive; - mStream.read((char *)&primitive, sizeof(primitive)); - if (mStream.fail()) return false; - outPrimitive = primitive; - return true; -} - -bool ObjectStreamBinaryIn::ReadPrimitiveData(String &outPrimitive) -{ - // Read length or ID of string - uint32 len; - if (!ReadPrimitiveData(len)) - return false; - - // Check empty string - if (len == 0) - { - outPrimitive.clear(); - return true; - } - - // Check if it is an ID in the string table - if (len & 0x80000000) - { - StringTable::iterator i = mStringTable.find(len); - if (i == mStringTable.end()) - return false; - outPrimitive = i->second; - return true; - } - - // Read the string - char *data = (char *)JPH_STACK_ALLOC(len + 1); - mStream.read(data, len); - if (mStream.fail()) return false; - data[len] = 0; - outPrimitive = data; - - // Insert string in table - mStringTable.try_emplace(mNextStringID, outPrimitive); - mNextStringID++; - return true; -} - -bool ObjectStreamBinaryIn::ReadPrimitiveData(Float3 &outPrimitive) -{ - Float3 primitive; - mStream.read((char *)&primitive, sizeof(Float3)); - if (mStream.fail()) return false; - outPrimitive = primitive; - return true; -} - -bool ObjectStreamBinaryIn::ReadPrimitiveData(Double3 &outPrimitive) -{ - Double3 primitive; - mStream.read((char *)&primitive, sizeof(Double3)); - if (mStream.fail()) return false; - outPrimitive = primitive; - return true; -} - -bool ObjectStreamBinaryIn::ReadPrimitiveData(Vec3 &outPrimitive) -{ - Float3 primitive; - mStream.read((char *)&primitive, sizeof(Float3)); - if (mStream.fail()) return false; - outPrimitive = Vec3(primitive); // Use Float3 constructor so that we initialize W too - return true; -} - -bool ObjectStreamBinaryIn::ReadPrimitiveData(DVec3 &outPrimitive) -{ - Double3 primitive; - mStream.read((char *)&primitive, sizeof(Double3)); - if (mStream.fail()) return false; - outPrimitive = DVec3(primitive); // Use Float3 constructor so that we initialize W too - return true; -} - -bool ObjectStreamBinaryIn::ReadPrimitiveData(Vec4 &outPrimitive) -{ - Vec4 primitive; - mStream.read((char *)&primitive, sizeof(primitive)); - if (mStream.fail()) return false; - outPrimitive = primitive; - return true; -} - -bool ObjectStreamBinaryIn::ReadPrimitiveData(Quat &outPrimitive) -{ - Quat primitive; - mStream.read((char *)&primitive, sizeof(primitive)); - if (mStream.fail()) return false; - outPrimitive = primitive; - return true; -} - -bool ObjectStreamBinaryIn::ReadPrimitiveData(Mat44 &outPrimitive) -{ - Mat44 primitive; - mStream.read((char *)&primitive, sizeof(primitive)); - if (mStream.fail()) return false; - outPrimitive = primitive; - return true; -} - -bool ObjectStreamBinaryIn::ReadPrimitiveData(DMat44 &outPrimitive) -{ - Vec4 c0, c1, c2; - DVec3 c3; - if (!ReadPrimitiveData(c0) || !ReadPrimitiveData(c1) || !ReadPrimitiveData(c2) || !ReadPrimitiveData(c3)) - return false; - outPrimitive = DMat44(c0, c1, c2, c3); - return true; -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamBinaryIn.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamBinaryIn.h deleted file mode 100644 index 553282e62ef..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamBinaryIn.h +++ /dev/null @@ -1,51 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -/// Implementation of ObjectStream binary input stream. -class JPH_EXPORT ObjectStreamBinaryIn : public ObjectStreamIn -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Constructor - explicit ObjectStreamBinaryIn(istream &inStream); - - ///@name Input type specific operations - virtual bool ReadDataType(EOSDataType &outType) override; - virtual bool ReadName(String &outName) override; - virtual bool ReadIdentifier(Identifier &outIdentifier) override; - virtual bool ReadCount(uint32 &outCount) override; - - virtual bool ReadPrimitiveData(uint8 &outPrimitive) override; - virtual bool ReadPrimitiveData(uint16 &outPrimitive) override; - virtual bool ReadPrimitiveData(int &outPrimitive) override; - virtual bool ReadPrimitiveData(uint32 &outPrimitive) override; - virtual bool ReadPrimitiveData(uint64 &outPrimitive) override; - virtual bool ReadPrimitiveData(float &outPrimitive) override; - virtual bool ReadPrimitiveData(double &outPrimitive) override; - virtual bool ReadPrimitiveData(bool &outPrimitive) override; - virtual bool ReadPrimitiveData(String &outPrimitive) override; - virtual bool ReadPrimitiveData(Float3 &outPrimitive) override; - virtual bool ReadPrimitiveData(Double3 &outPrimitive) override; - virtual bool ReadPrimitiveData(Vec3 &outPrimitive) override; - virtual bool ReadPrimitiveData(DVec3 &outPrimitive) override; - virtual bool ReadPrimitiveData(Vec4 &outPrimitive) override; - virtual bool ReadPrimitiveData(Quat &outPrimitive) override; - virtual bool ReadPrimitiveData(Mat44 &outPrimitive) override; - virtual bool ReadPrimitiveData(DMat44 &outPrimitive) override; - -private: - using StringTable = UnorderedMap; - - StringTable mStringTable; - uint32 mNextStringID = 0x80000000; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamBinaryOut.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamBinaryOut.cpp deleted file mode 100644 index 83f584c1cdc..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamBinaryOut.cpp +++ /dev/null @@ -1,150 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include - -JPH_NAMESPACE_BEGIN - -ObjectStreamBinaryOut::ObjectStreamBinaryOut(ostream &inStream) : - ObjectStreamOut(inStream) -{ - String header; - header = StringFormat("BOS%2d.%02d", ObjectStream::sVersion, ObjectStream::sRevision); - mStream.write(header.c_str(), header.size()); -} - -void ObjectStreamBinaryOut::WriteDataType(EOSDataType inType) -{ - mStream.write((const char *)&inType, sizeof(inType)); -} - -void ObjectStreamBinaryOut::WriteName(const char *inName) -{ - WritePrimitiveData(String(inName)); -} - -void ObjectStreamBinaryOut::WriteIdentifier(Identifier inIdentifier) -{ - mStream.write((const char *)&inIdentifier, sizeof(inIdentifier)); -} - -void ObjectStreamBinaryOut::WriteCount(uint32 inCount) -{ - mStream.write((const char *)&inCount, sizeof(inCount)); -} - -void ObjectStreamBinaryOut::WritePrimitiveData(const uint8 &inPrimitive) -{ - mStream.write((const char *)&inPrimitive, sizeof(inPrimitive)); -} - -void ObjectStreamBinaryOut::WritePrimitiveData(const uint16 &inPrimitive) -{ - mStream.write((const char *)&inPrimitive, sizeof(inPrimitive)); -} - -void ObjectStreamBinaryOut::WritePrimitiveData(const int &inPrimitive) -{ - mStream.write((const char *)&inPrimitive, sizeof(inPrimitive)); -} - -void ObjectStreamBinaryOut::WritePrimitiveData(const uint32 &inPrimitive) -{ - mStream.write((const char *)&inPrimitive, sizeof(inPrimitive)); -} - -void ObjectStreamBinaryOut::WritePrimitiveData(const uint64 &inPrimitive) -{ - mStream.write((const char *)&inPrimitive, sizeof(inPrimitive)); -} - -void ObjectStreamBinaryOut::WritePrimitiveData(const float &inPrimitive) -{ - mStream.write((const char *)&inPrimitive, sizeof(inPrimitive)); -} - -void ObjectStreamBinaryOut::WritePrimitiveData(const double &inPrimitive) -{ - mStream.write((const char *)&inPrimitive, sizeof(inPrimitive)); -} - -void ObjectStreamBinaryOut::WritePrimitiveData(const bool &inPrimitive) -{ - mStream.write((const char *)&inPrimitive, sizeof(inPrimitive)); -} - -void ObjectStreamBinaryOut::WritePrimitiveData(const String &inPrimitive) -{ - // Empty strings are trivial - if (inPrimitive.empty()) - { - WritePrimitiveData((uint32)0); - return; - } - - // Check if we've already written this string - StringTable::iterator i = mStringTable.find(inPrimitive); - if (i != mStringTable.end()) - { - WritePrimitiveData(i->second); - return; - } - - // Insert string in table - mStringTable.try_emplace(inPrimitive, mNextStringID); - mNextStringID++; - - // Write string - uint32 len = min((uint32)inPrimitive.size(), (uint32)0x7fffffff); - WritePrimitiveData(len); - mStream.write(inPrimitive.c_str(), len); -} - -void ObjectStreamBinaryOut::WritePrimitiveData(const Float3 &inPrimitive) -{ - mStream.write((const char *)&inPrimitive, sizeof(Float3)); -} - -void ObjectStreamBinaryOut::WritePrimitiveData(const Double3 &inPrimitive) -{ - mStream.write((const char *)&inPrimitive, sizeof(Double3)); -} - -void ObjectStreamBinaryOut::WritePrimitiveData(const Vec3 &inPrimitive) -{ - mStream.write((const char *)&inPrimitive, 3 * sizeof(float)); -} - -void ObjectStreamBinaryOut::WritePrimitiveData(const DVec3 &inPrimitive) -{ - mStream.write((const char *)&inPrimitive, 3 * sizeof(double)); -} - -void ObjectStreamBinaryOut::WritePrimitiveData(const Vec4 &inPrimitive) -{ - mStream.write((const char *)&inPrimitive, sizeof(inPrimitive)); -} - -void ObjectStreamBinaryOut::WritePrimitiveData(const Quat &inPrimitive) -{ - mStream.write((const char *)&inPrimitive, sizeof(inPrimitive)); -} - -void ObjectStreamBinaryOut::WritePrimitiveData(const Mat44 &inPrimitive) -{ - mStream.write((const char *)&inPrimitive, sizeof(inPrimitive)); -} - -void ObjectStreamBinaryOut::WritePrimitiveData(const DMat44 &inPrimitive) -{ - WritePrimitiveData(inPrimitive.GetColumn4(0)); - WritePrimitiveData(inPrimitive.GetColumn4(1)); - WritePrimitiveData(inPrimitive.GetColumn4(2)); - WritePrimitiveData(inPrimitive.GetTranslation()); -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamBinaryOut.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamBinaryOut.h deleted file mode 100644 index 1c720512009..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamBinaryOut.h +++ /dev/null @@ -1,51 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -/// Implementation of ObjectStream binary output stream. -class JPH_EXPORT ObjectStreamBinaryOut : public ObjectStreamOut -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Constructor and destructor - explicit ObjectStreamBinaryOut(ostream &inStream); - - ///@name Output type specific operations - virtual void WriteDataType(EOSDataType inType) override; - virtual void WriteName(const char *inName) override; - virtual void WriteIdentifier(Identifier inIdentifier) override; - virtual void WriteCount(uint32 inCount) override; - - virtual void WritePrimitiveData(const uint8 &inPrimitive) override; - virtual void WritePrimitiveData(const uint16 &inPrimitive) override; - virtual void WritePrimitiveData(const int &inPrimitive) override; - virtual void WritePrimitiveData(const uint32 &inPrimitive) override; - virtual void WritePrimitiveData(const uint64 &inPrimitive) override; - virtual void WritePrimitiveData(const float &inPrimitive) override; - virtual void WritePrimitiveData(const double &inPrimitive) override; - virtual void WritePrimitiveData(const bool &inPrimitive) override; - virtual void WritePrimitiveData(const String &inPrimitive) override; - virtual void WritePrimitiveData(const Float3 &inPrimitive) override; - virtual void WritePrimitiveData(const Double3 &inPrimitive) override; - virtual void WritePrimitiveData(const Vec3 &inPrimitive) override; - virtual void WritePrimitiveData(const DVec3 &inPrimitive) override; - virtual void WritePrimitiveData(const Vec4 &inPrimitive) override; - virtual void WritePrimitiveData(const Quat &inPrimitive) override; - virtual void WritePrimitiveData(const Mat44 &inPrimitive) override; - virtual void WritePrimitiveData(const DMat44 &inPrimitive) override; - -private: - using StringTable = UnorderedMap; - - StringTable mStringTable; - uint32 mNextStringID = 0x80000000; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamIn.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamIn.cpp deleted file mode 100644 index 45ea2857b2a..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamIn.cpp +++ /dev/null @@ -1,617 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -ObjectStreamIn::ObjectStreamIn(istream &inStream) : - mStream(inStream) -{ -} - -bool ObjectStreamIn::GetInfo(istream &inStream, EStreamType &outType, int &outVersion, int &outRevision) -{ - // Read header and check if it is the correct format, e.g. "TOS 1.00" - char header[9]; - memset(header, 0, 9); - inStream.read(header, 8); - if ((header[0] == 'B' || header[0] == 'T') && header[1] == 'O' && header[2] == 'S' - && (header[3] == ' ' || isdigit(header[3])) && isdigit(header[4]) - && header[5] == '.' && isdigit(header[6]) && isdigit(header[7])) - { - // Check if this is a binary or text objectfile - switch (header[0]) - { - case 'T': outType = ObjectStream::EStreamType::Text; break; - case 'B': outType = ObjectStream::EStreamType::Binary; break; - default: JPH_ASSERT(false); break; - } - - // Extract version and revision - header[5] = '\0'; - outVersion = atoi(&header[3]); - outRevision = atoi(&header[6]); - - return true; - } - - Trace("ObjectStreamIn: Not a valid object stream."); - return false; -} - -ObjectStreamIn *ObjectStreamIn::Open(istream &inStream) -{ - // Check if file is an ObjectStream of the correct version and revision - EStreamType type; - int version; - int revision; - if (GetInfo(inStream, type, version, revision)) - { - if (version == sVersion && revision == sRevision) - { - // Create an input stream of the correct type - switch (type) - { - case EStreamType::Text: return new ObjectStreamTextIn(inStream); - case EStreamType::Binary: return new ObjectStreamBinaryIn(inStream); - default: JPH_ASSERT(false); - } - } - else - { - Trace("ObjectStreamIn: Different version stream (%d.%02d, expected %d.%02d).", version, revision, sVersion, sRevision); - } - } - - return nullptr; -} - -void *ObjectStreamIn::Read(const RTTI *inRTTI) -{ - using ObjectSet = UnorderedSet; - - // Read all information on the stream - void *main_object = nullptr; - bool continue_reading = true; - for (;;) - { - // Get type of next operation - EOSDataType data_type; - if (!ReadDataType(data_type)) - break; - - if (data_type == EOSDataType::Declare) - { - // Read type declaration - if (!ReadRTTI()) - { - Trace("ObjectStreamIn: Fatal error while reading class description for class %s.", inRTTI->GetName()); - continue_reading = false; - break; - } - } - else if (data_type == EOSDataType::Object) - { - const RTTI *rtti; - void *object = ReadObject(rtti); - if (!main_object && object) - { - // This is the first and thus main object of the file. - if (rtti->IsKindOf(inRTTI)) - { - // Object is of correct type - main_object = object; - } - else - { - Trace("ObjectStreamIn: Main object of different type. Expected %s, but found %s.", inRTTI->GetName(), rtti->GetName()); - continue_reading = false; - break; - } - } - } - else - { - // Invalid or out of place token found - Trace("ObjectStreamIn: Invalid or out of place token found."); - continue_reading = false; - break; - } - } - - // Resolve links (pointer, references) - if (continue_reading) - { - // Resolve links - ObjectSet referenced_objects; - for (Link &link : mUnresolvedLinks) - { - IdentifierMap::const_iterator j = mIdentifierMap.find(link.mIdentifier); - if (j != mIdentifierMap.end() && j->second.mRTTI->IsKindOf(link.mRTTI)) - { - const ObjectInfo &obj_info = j->second; - - // Set pointer - *link.mPointer = obj_info.mInstance; - - // Increment refcount if it was a referencing pointer - if (link.mRefCountOffset != -1) - ++(*(uint32 *)(((uint8 *)obj_info.mInstance) + link.mRefCountOffset)); - - // Add referenced object to the list - if (referenced_objects.find(obj_info.mInstance) == referenced_objects.end()) - referenced_objects.insert(obj_info.mInstance); - } - else - { - // Referenced object not found, set pointer to nullptr - Trace("ObjectStreamIn: Setting incorrect pointer to class of type %s to nullptr.", link.mRTTI->GetName()); - *link.mPointer = nullptr; - } - } - - // Release unreferenced objects except the main object - for (const IdentifierMap::value_type &j : mIdentifierMap) - { - const ObjectInfo &obj_info = j.second; - - if (obj_info.mInstance != main_object) - { - ObjectSet::const_iterator k = referenced_objects.find(obj_info.mInstance); - if (k == referenced_objects.end()) - { - Trace("ObjectStreamIn: Releasing unreferenced object of type %s.", obj_info.mRTTI->GetName()); - obj_info.mRTTI->DestructObject(obj_info.mInstance); - } - } - } - - return main_object; - } - else - { - // Release all objects if a fatal error occurred - for (const IdentifierMap::value_type &i : mIdentifierMap) - { - const ObjectInfo &obj_info = i.second; - obj_info.mRTTI->DestructObject(obj_info.mInstance); - } - - return nullptr; - } -} - -void *ObjectStreamIn::ReadObject(const RTTI *& outRTTI) -{ - // Read the object class - void *object = nullptr; - String class_name; - if (ReadName(class_name)) - { - // Get class description - ClassDescriptionMap::iterator i = mClassDescriptionMap.find(class_name); - if (i != mClassDescriptionMap.end()) - { - const ClassDescription &class_desc = i->second; - - // Read object identifier - Identifier identifier; - if (ReadIdentifier(identifier)) - { - // Check if this object can be read or must be skipped - if (identifier != sNullIdentifier - && class_desc.mRTTI - && !class_desc.mRTTI->IsAbstract()) - { - // Create object instance - outRTTI = class_desc.mRTTI; - object = outRTTI->CreateObject(); - - // Read object attributes - if (ReadClassData(class_desc, object)) - { - // Add object to identifier map - mIdentifierMap.try_emplace(identifier, object, outRTTI); - } - else - { - // Fatal error while reading attributes, release object - outRTTI->DestructObject(object); - object = nullptr; - } - } - else - { - // Skip this object - // TODO: This operation can fail, but there is no check yet - Trace("ObjectStreamIn: Found uncreatable object %s.", class_name.c_str()); - ReadClassData(class_desc, nullptr); - } - } - } - else - { - // TODO: This is a fatal error, but this function has no way of indicating this - Trace("ObjectStreamIn: Found object of unknown class %s.", class_name.c_str()); - } - } - - return object; -} - -bool ObjectStreamIn::ReadRTTI() -{ - // Read class name and find it's attribute info - String class_name; - if (!ReadName(class_name)) - return false; - - // Find class - const RTTI *rtti = Factory::sInstance->Find(class_name.c_str()); - if (rtti == nullptr) - Trace("ObjectStreamIn: Unknown class: \"%s\".", class_name.c_str()); - - // Insert class description - ClassDescription &class_desc = mClassDescriptionMap.try_emplace(class_name, rtti).first->second; - - // Read the number of entries in the description - uint32 count; - if (!ReadCount(count)) - return false; - - // Read the entries - for (uint32 i = 0; i < count; ++i) - { - AttributeDescription attribute; - - // Read name - String attribute_name; - if (!ReadName(attribute_name)) - return false; - - // Read type - if (!ReadDataType(attribute.mSourceType)) - return false; - - // Read array depth - while (attribute.mSourceType == EOSDataType::Array) - { - ++attribute.mArrayDepth; - if (!ReadDataType(attribute.mSourceType)) - return false; - } - - // Read instance/pointer class name - if ((attribute.mSourceType == EOSDataType::Instance || attribute.mSourceType == EOSDataType::Pointer) - && !ReadName(attribute.mClassName)) - return false; - - // Find attribute in rtti - if (rtti) - { - // Find attribute index - for (int idx = 0; idx < rtti->GetAttributeCount(); ++idx) - { - const SerializableAttribute &attr = rtti->GetAttribute(idx); - if (strcmp(attr.GetName(), attribute_name.c_str()) == 0) - { - attribute.mIndex = idx; - break; - } - } - - // Check if attribute is of expected type - if (attribute.mIndex >= 0) - { - const SerializableAttribute &attr = rtti->GetAttribute(attribute.mIndex); - if (attr.IsType(attribute.mArrayDepth, attribute.mSourceType, attribute.mClassName.c_str())) - { - // No conversion needed - attribute.mDestinationType = attribute.mSourceType; - } - else if (attribute.mArrayDepth == 0 && attribute.mClassName.empty()) - { - // Try to apply type conversions - if (attribute.mSourceType == EOSDataType::T_Vec3 && attr.IsType(0, EOSDataType::T_DVec3, "")) - attribute.mDestinationType = EOSDataType::T_DVec3; - else if (attribute.mSourceType == EOSDataType::T_DVec3 && attr.IsType(0, EOSDataType::T_Vec3, "")) - attribute.mDestinationType = EOSDataType::T_Vec3; - else - attribute.mIndex = -1; - } - else - { - // No conversion exists - attribute.mIndex = -1; - } - } - } - - // Add attribute to the class description - class_desc.mAttributes.push_back(attribute); - } - - return true; -} - -bool ObjectStreamIn::ReadClassData(const char *inClassName, void *inInstance) -{ - // Find the class description - ClassDescriptionMap::iterator i = mClassDescriptionMap.find(inClassName); - if (i != mClassDescriptionMap.end()) - return ReadClassData(i->second, inInstance); - - return false; -} - -bool ObjectStreamIn::ReadClassData(const ClassDescription &inClassDesc, void *inInstance) -{ - // Read data for this class - bool continue_reading = true; - - for (const AttributeDescription &attr_desc : inClassDesc.mAttributes) - { - // Read or skip the attribute data - if (attr_desc.mIndex >= 0 && inInstance) - { - const SerializableAttribute &attr = inClassDesc.mRTTI->GetAttribute(attr_desc.mIndex); - if (attr_desc.mSourceType == attr_desc.mDestinationType) - { - continue_reading = attr.ReadData(*this, inInstance); - } - else if (attr_desc.mSourceType == EOSDataType::T_Vec3 && attr_desc.mDestinationType == EOSDataType::T_DVec3) - { - // Vec3 to DVec3 - Vec3 tmp; - continue_reading = ReadPrimitiveData(tmp); - if (continue_reading) - *attr.GetMemberPointer(inInstance) = DVec3(tmp); - } - else if (attr_desc.mSourceType == EOSDataType::T_DVec3 && attr_desc.mDestinationType == EOSDataType::T_Vec3) - { - // DVec3 to Vec3 - DVec3 tmp; - continue_reading = ReadPrimitiveData(tmp); - if (continue_reading) - *attr.GetMemberPointer(inInstance) = Vec3(tmp); - } - else - { - JPH_ASSERT(false); // Unknown conversion - continue_reading = SkipAttributeData(attr_desc.mArrayDepth, attr_desc.mSourceType, attr_desc.mClassName.c_str()); - } - } - else - continue_reading = SkipAttributeData(attr_desc.mArrayDepth, attr_desc.mSourceType, attr_desc.mClassName.c_str()); - - if (!continue_reading) - break; - } - - return continue_reading; -} - -bool ObjectStreamIn::ReadPointerData(const RTTI *inRTTI, void **inPointer, int inRefCountOffset) -{ - Identifier identifier; - if (ReadIdentifier(identifier)) - { - if (identifier == sNullIdentifier) - { - // Set nullptr pointer - inPointer = nullptr; - } - else - { - // Put pointer on the list to be resolved later on - Link &link = mUnresolvedLinks.emplace_back(); - link.mPointer = inPointer; - link.mRefCountOffset = inRefCountOffset; - link.mIdentifier = identifier; - link.mRTTI = inRTTI; - } - - return true; - } - - return false; -} - -bool ObjectStreamIn::SkipAttributeData(int inArrayDepth, EOSDataType inDataType, const char *inClassName) -{ - bool continue_reading = true; - - // Get number of items to read - uint32 count = 1; - for (; inArrayDepth > 0; --inArrayDepth) - { - uint32 temporary; - if (ReadCount(temporary)) - { - // Multiply for multi dimensional arrays - count *= temporary; - } - else - { - // Fatal error while reading array size - continue_reading = false; - break; - } - } - - // Read data for all items - if (continue_reading) - { - if (inDataType == EOSDataType::Instance) - { - // Get the class description - ClassDescriptionMap::iterator i = mClassDescriptionMap.find(inClassName); - if (i != mClassDescriptionMap.end()) - { - for (; count > 0 && continue_reading; --count) - continue_reading = ReadClassData(i->second, nullptr); - } - else - { - continue_reading = false; - Trace("ObjectStreamIn: Found instance of unknown class %s.", inClassName); - } - } - else - { - for (; count > 0 && continue_reading; --count) - { - switch (inDataType) - { - case EOSDataType::Pointer: - { - Identifier temporary; - continue_reading = ReadIdentifier(temporary); - break; - } - - case EOSDataType::T_uint8: - { - uint8 temporary; - continue_reading = ReadPrimitiveData(temporary); - break; - } - - case EOSDataType::T_uint16: - { - uint16 temporary; - continue_reading = ReadPrimitiveData(temporary); - break; - } - - case EOSDataType::T_int: - { - int temporary; - continue_reading = ReadPrimitiveData(temporary); - break; - } - - case EOSDataType::T_uint32: - { - uint32 temporary; - continue_reading = ReadPrimitiveData(temporary); - break; - } - - case EOSDataType::T_uint64: - { - uint64 temporary; - continue_reading = ReadPrimitiveData(temporary); - break; - } - - case EOSDataType::T_float: - { - float temporary; - continue_reading = ReadPrimitiveData(temporary); - break; - } - - case EOSDataType::T_double: - { - double temporary; - continue_reading = ReadPrimitiveData(temporary); - break; - } - - case EOSDataType::T_bool: - { - bool temporary; - continue_reading = ReadPrimitiveData(temporary); - break; - } - - case EOSDataType::T_String: - { - String temporary; - continue_reading = ReadPrimitiveData(temporary); - break; - } - - case EOSDataType::T_Float3: - { - Float3 temporary; - continue_reading = ReadPrimitiveData(temporary); - break; - } - - case EOSDataType::T_Double3: - { - Double3 temporary; - continue_reading = ReadPrimitiveData(temporary); - break; - } - - case EOSDataType::T_Vec3: - { - Vec3 temporary; - continue_reading = ReadPrimitiveData(temporary); - break; - } - - case EOSDataType::T_DVec3: - { - DVec3 temporary; - continue_reading = ReadPrimitiveData(temporary); - break; - } - - case EOSDataType::T_Vec4: - { - Vec4 temporary; - continue_reading = ReadPrimitiveData(temporary); - break; - } - - case EOSDataType::T_Quat: - { - Quat temporary; - continue_reading = ReadPrimitiveData(temporary); - break; - } - - case EOSDataType::T_Mat44: - { - Mat44 temporary; - continue_reading = ReadPrimitiveData(temporary); - break; - } - - case EOSDataType::T_DMat44: - { - DMat44 temporary; - continue_reading = ReadPrimitiveData(temporary); - break; - } - - case EOSDataType::Array: - case EOSDataType::Object: - case EOSDataType::Declare: - case EOSDataType::Instance: - case EOSDataType::Invalid: - default: - continue_reading = false; - break; - } - } - } - } - - return continue_reading; -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamIn.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamIn.h deleted file mode 100644 index 542aa71e0f2..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamIn.h +++ /dev/null @@ -1,144 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include -#include - -JPH_SUPPRESS_WARNINGS_STD_BEGIN -#include -JPH_SUPPRESS_WARNINGS_STD_END - -JPH_NAMESPACE_BEGIN - -/// ObjectStreamIn contains all logic for reading an object from disk. It is the base -/// class for the text and binary input streams (ObjectStreamTextIn and ObjectStreamBinaryIn). -class JPH_EXPORT ObjectStreamIn : public IObjectStreamIn -{ -private: - struct ClassDescription; - -public: - /// Main function to read an object from a stream - template - static bool sReadObject(istream &inStream, T *&outObject) - { - // Create the input stream - bool result = false; - ObjectStreamIn *stream = ObjectStreamIn::Open(inStream); - if (stream) - { - // Read the object - outObject = (T *)stream->Read(JPH_RTTI(T)); - result = (outObject != nullptr); - delete stream; - } - return result; - } - - /// Main function to read an object from a stream (reference counting pointer version) - template - static bool sReadObject(istream &inStream, Ref &outObject) - { - T *object = nullptr; - bool result = sReadObject(inStream, object); - outObject = object; - return result; - } - - /// Main function to read an object from a file - template - static bool sReadObject(const char *inFileName, T *&outObject) - { - std::ifstream stream; - stream.open(inFileName, std::ifstream::in | std::ifstream::binary); - if (!stream.is_open()) - return false; - return sReadObject(stream, outObject); - } - - /// Main function to read an object from a file (reference counting pointer version) - template - static bool sReadObject(const char *inFileName, Ref &outObject) - { - T *object = nullptr; - bool result = sReadObject(inFileName, object); - outObject = object; - return result; - } - - ////////////////////////////////////////////////////// - // EVERYTHING BELOW THIS SHOULD NOT DIRECTLY BE CALLED - ////////////////////////////////////////////////////// - - ///@name Serialization operations - void * Read(const RTTI *inRTTI); - void * ReadObject(const RTTI *& outRTTI); - bool ReadRTTI(); - virtual bool ReadClassData(const char *inClassName, void *inInstance) override; - bool ReadClassData(const ClassDescription &inClassDesc, void *inInstance); - virtual bool ReadPointerData(const RTTI *inRTTI, void **inPointer, int inRefCountOffset = -1) override; - bool SkipAttributeData(int inArrayDepth, EOSDataType inDataType, const char *inClassName); - -protected: - /// Constructor - explicit ObjectStreamIn(istream &inStream); - - /// Determine the type and version of an object stream - static bool GetInfo(istream &inStream, EStreamType &outType, int &outVersion, int &outRevision); - - /// Static constructor - static ObjectStreamIn * Open(istream &inStream); - - istream & mStream; - -private: - /// Class descriptions - struct AttributeDescription - { - int mArrayDepth = 0; - EOSDataType mSourceType = EOSDataType::Invalid; - EOSDataType mDestinationType = EOSDataType::Invalid; - String mClassName; - int mIndex = -1; - }; - - struct ClassDescription - { - ClassDescription() = default; - explicit ClassDescription(const RTTI *inRTTI) : mRTTI(inRTTI) { } - - const RTTI * mRTTI = nullptr; - Array mAttributes; - }; - - struct ObjectInfo - { - ObjectInfo() = default; - ObjectInfo(void *inInstance, const RTTI *inRTTI) : mInstance(inInstance), mRTTI(inRTTI) { } - - void * mInstance = nullptr; - const RTTI * mRTTI = nullptr; - }; - - struct Link - { - void ** mPointer; - int mRefCountOffset; - Identifier mIdentifier; - const RTTI * mRTTI; - }; - - using IdentifierMap = UnorderedMap; - using ClassDescriptionMap = UnorderedMap; - - ClassDescriptionMap mClassDescriptionMap; - IdentifierMap mIdentifierMap; ///< Links identifier to an object pointer - Array mUnresolvedLinks; ///< All pointers (links) are resolved after reading the entire file, e.g. when all object exist -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamOut.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamOut.cpp deleted file mode 100644 index a9401acc469..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamOut.cpp +++ /dev/null @@ -1,164 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -ObjectStreamOut::ObjectStreamOut(ostream &inStream) : - mStream(inStream) -{ -// Add all primitives to the class set -#define JPH_DECLARE_PRIMITIVE(name) mClassSet.insert(JPH_RTTI(name)); -#include -} - -ObjectStreamOut *ObjectStreamOut::Open(EStreamType inType, ostream &inStream) -{ - switch (inType) - { - case EStreamType::Text: return new ObjectStreamTextOut(inStream); - case EStreamType::Binary: return new ObjectStreamBinaryOut(inStream); - default: JPH_ASSERT(false); - } - return nullptr; -} - -bool ObjectStreamOut::Write(const void *inObject, const RTTI *inRTTI) -{ - // Assign a new identifier to the object and write it - mIdentifierMap.try_emplace(inObject, mNextIdentifier, inRTTI); - mNextIdentifier++; - WriteObject(inObject); - - // Write all linked objects - while (!mObjectQueue.empty() && !mStream.fail()) - { - const void *linked_object = mObjectQueue.front(); - WriteObject(linked_object); - mObjectQueue.pop(); - } - return !mStream.fail(); -} - -void ObjectStreamOut::WriteObject(const void *inObject) -{ - // Find object identifier - IdentifierMap::iterator i = mIdentifierMap.find(inObject); - JPH_ASSERT(i != mIdentifierMap.end()); - - // Write class description and associated descriptions - QueueRTTI(i->second.mRTTI); - while (!mClassQueue.empty() && !mStream.fail()) - { - WriteRTTI(mClassQueue.front()); - mClassQueue.pop(); - } - - HintNextItem(); - HintNextItem(); - - // Write object header. - WriteDataType(EOSDataType::Object); - WriteName(i->second.mRTTI->GetName()); - WriteIdentifier(i->second.mIdentifier); - - // Write attribute data - WriteClassData(i->second.mRTTI, inObject); -} - -void ObjectStreamOut::QueueRTTI(const RTTI *inRTTI) -{ - ClassSet::const_iterator i = mClassSet.find(inRTTI); - if (i == mClassSet.end()) - { - mClassSet.insert(inRTTI); - mClassQueue.push(inRTTI); - } -} - -void ObjectStreamOut::WriteRTTI(const RTTI *inRTTI) -{ - HintNextItem(); - HintNextItem(); - - // Write class header. E.g. in text mode: "class " - WriteDataType(EOSDataType::Declare); - WriteName(inRTTI->GetName()); - WriteCount(inRTTI->GetAttributeCount()); - - // Write class attribute info - HintIndentUp(); - for (int attr_index = 0; attr_index < inRTTI->GetAttributeCount(); ++attr_index) - { - // Get attribute - const SerializableAttribute &attr = inRTTI->GetAttribute(attr_index); - - // Write definition of attribute class if undefined - const RTTI *rtti = attr.GetMemberPrimitiveType(); - if (rtti != nullptr) - QueueRTTI(rtti); - - HintNextItem(); - - // Write attribute information. - WriteName(attr.GetName()); - attr.WriteDataType(*this); - } - HintIndentDown(); -} - -void ObjectStreamOut::WriteClassData(const RTTI *inRTTI, const void *inInstance) -{ - JPH_ASSERT(inInstance); - - // Write attributes - HintIndentUp(); - for (int attr_index = 0; attr_index < inRTTI->GetAttributeCount(); ++attr_index) - { - // Get attribute - const SerializableAttribute &attr = inRTTI->GetAttribute(attr_index); - attr.WriteData(*this, inInstance); - } - HintIndentDown(); -} - -void ObjectStreamOut::WritePointerData(const RTTI *inRTTI, const void *inPointer) -{ - Identifier identifier; - - if (inPointer) - { - // Check if this object has an identifier - IdentifierMap::iterator i = mIdentifierMap.find(inPointer); - if (i != mIdentifierMap.end()) - { - // Object already has an identifier - identifier = i->second.mIdentifier; - } - else - { - // Assign a new identifier to this object and queue it for serialization - identifier = mNextIdentifier++; - mIdentifierMap.try_emplace(inPointer, identifier, inRTTI); - mObjectQueue.push(inPointer); - } - } - else - { - // Write nullptr pointer - identifier = sNullIdentifier; - } - - // Write the identifier - HintNextItem(); - WriteIdentifier(identifier); -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamOut.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamOut.h deleted file mode 100644 index e85ba5377ae..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamOut.h +++ /dev/null @@ -1,100 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include -#include - -JPH_SUPPRESS_WARNINGS_STD_BEGIN -#include -#include -JPH_SUPPRESS_WARNINGS_STD_END - -JPH_NAMESPACE_BEGIN - -template using Queue = std::queue>>; - -/// ObjectStreamOut contains all logic for writing an object to disk. It is the base -/// class for the text and binary output streams (ObjectStreamTextOut and ObjectStreamBinaryOut). -class JPH_EXPORT ObjectStreamOut : public IObjectStreamOut -{ -private: - struct ObjectInfo; - -public: - /// Main function to write an object to a stream - template - static bool sWriteObject(ostream &inStream, ObjectStream::EStreamType inType, const T &inObject) - { - // Create the output stream - bool result = false; - ObjectStreamOut *stream = ObjectStreamOut::Open(inType, inStream); - if (stream) - { - // Write the object to the stream - result = stream->Write((void *)&inObject, GetRTTI(&inObject)); - delete stream; - } - - return result; - } - - /// Main function to write an object to a file - template - static bool sWriteObject(const char *inFileName, ObjectStream::EStreamType inType, const T &inObject) - { - std::ofstream stream; - stream.open(inFileName, std::ofstream::out | std::ofstream::trunc | std::ofstream::binary); - if (!stream.is_open()) - return false; - return sWriteObject(stream, inType, inObject); - } - - ////////////////////////////////////////////////////// - // EVERYTHING BELOW THIS SHOULD NOT DIRECTLY BE CALLED - ////////////////////////////////////////////////////// - - ///@name Serialization operations - bool Write(const void *inObject, const RTTI *inRTTI); - void WriteObject(const void *inObject); - void QueueRTTI(const RTTI *inRTTI); - void WriteRTTI(const RTTI *inRTTI); - virtual void WriteClassData(const RTTI *inRTTI, const void *inInstance) override; - virtual void WritePointerData(const RTTI *inRTTI, const void *inPointer) override; - -protected: - /// Static constructor - static ObjectStreamOut * Open(EStreamType inType, ostream &inStream); - - /// Constructor - explicit ObjectStreamOut(ostream &inStream); - - ostream & mStream; - -private: - struct ObjectInfo - { - ObjectInfo() : mIdentifier(0), mRTTI(nullptr) { } - ObjectInfo(Identifier inIdentifier, const RTTI *inRTTI) : mIdentifier(inIdentifier), mRTTI(inRTTI) { } - - Identifier mIdentifier; - const RTTI * mRTTI; - }; - - using IdentifierMap = UnorderedMap; - using ClassSet = UnorderedSet; - using ObjectQueue = Queue; - using ClassQueue = Queue; - - Identifier mNextIdentifier = sNullIdentifier + 1; ///< Next free identifier for this stream - IdentifierMap mIdentifierMap; ///< Links object pointer to an identifier - ObjectQueue mObjectQueue; ///< Queue of objects to be written - ClassSet mClassSet; ///< List of classes already written - ClassQueue mClassQueue; ///< List of classes waiting to be written -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamTextIn.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamTextIn.cpp deleted file mode 100644 index 2f9eb383e5b..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamTextIn.cpp +++ /dev/null @@ -1,392 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include - -JPH_NAMESPACE_BEGIN - -ObjectStreamTextIn::ObjectStreamTextIn(istream &inStream) : - ObjectStreamIn(inStream) -{ -} - -bool ObjectStreamTextIn::ReadDataType(EOSDataType &outType) -{ - String token; - if (ReadWord(token)) - { - transform(token.begin(), token.end(), token.begin(), [](char inValue) { return (char)tolower(inValue); }); - if (token == "declare") - outType = EOSDataType::Declare; - else if (token == "object") - outType = EOSDataType::Object; - else if (token == "instance") - outType = EOSDataType::Instance; - else if (token == "pointer") - outType = EOSDataType::Pointer; - else if (token == "array") - outType = EOSDataType::Array; - else if (token == "uint8") - outType = EOSDataType::T_uint8; - else if (token == "uint16") - outType = EOSDataType::T_uint16; - else if (token == "int") - outType = EOSDataType::T_int; - else if (token == "uint32") - outType = EOSDataType::T_uint32; - else if (token == "uint64") - outType = EOSDataType::T_uint64; - else if (token == "float") - outType = EOSDataType::T_float; - else if (token == "double") - outType = EOSDataType::T_double; - else if (token == "bool") - outType = EOSDataType::T_bool; - else if (token == "string") - outType = EOSDataType::T_String; - else if (token == "float3") - outType = EOSDataType::T_Float3; - else if (token == "double3") - outType = EOSDataType::T_Double3; - else if (token == "vec3") - outType = EOSDataType::T_Vec3; - else if (token == "dvec3") - outType = EOSDataType::T_DVec3; - else if (token == "vec4") - outType = EOSDataType::T_Vec4; - else if (token == "quat") - outType = EOSDataType::T_Quat; - else if (token == "mat44") - outType = EOSDataType::T_Mat44; - else if (token == "dmat44") - outType = EOSDataType::T_DMat44; - else - { - Trace("ObjectStreamTextIn: Found unknown data type."); - return false; - } - return true; - } - return false; -} - -bool ObjectStreamTextIn::ReadName(String &outName) -{ - return ReadWord(outName); -} - -bool ObjectStreamTextIn::ReadIdentifier(Identifier &outIdentifier) -{ - String token; - if (!ReadWord(token)) - return false; - outIdentifier = (uint32)std::strtoul(token.c_str(), nullptr, 16); - if (errno == ERANGE) - { - outIdentifier = sNullIdentifier; - return false; - } - return true; -} - -bool ObjectStreamTextIn::ReadCount(uint32 &outCount) -{ - return ReadPrimitiveData(outCount); -} - -bool ObjectStreamTextIn::ReadPrimitiveData(uint8 &outPrimitive) -{ - String token; - if (!ReadWord(token)) - return false; - uint32 temporary; - IStringStream stream(token); - stream >> temporary; - if (!stream.fail()) - { - outPrimitive = (uint8)temporary; - return true; - } - return false; -} - -bool ObjectStreamTextIn::ReadPrimitiveData(uint16 &outPrimitive) -{ - String token; - if (!ReadWord(token)) - return false; - uint32 temporary; - IStringStream stream(token); - stream >> temporary; - if (!stream.fail()) - { - outPrimitive = (uint16)temporary; - return true; - } - return false; -} - -bool ObjectStreamTextIn::ReadPrimitiveData(int &outPrimitive) -{ - String token; - if (!ReadWord(token)) - return false; - IStringStream stream(token); - stream >> outPrimitive; - return !stream.fail(); -} - -bool ObjectStreamTextIn::ReadPrimitiveData(uint32 &outPrimitive) -{ - String token; - if (!ReadWord(token)) - return false; - IStringStream stream(token); - stream >> outPrimitive; - return !stream.fail(); -} - -bool ObjectStreamTextIn::ReadPrimitiveData(uint64 &outPrimitive) -{ - String token; - if (!ReadWord(token)) - return false; - IStringStream stream(token); - stream >> outPrimitive; - return !stream.fail(); -} - -bool ObjectStreamTextIn::ReadPrimitiveData(float &outPrimitive) -{ - String token; - if (!ReadWord(token)) - return false; - IStringStream stream(token); - stream >> outPrimitive; - return !stream.fail(); -} - -bool ObjectStreamTextIn::ReadPrimitiveData(double &outPrimitive) -{ - String token; - if (!ReadWord(token)) - return false; - IStringStream stream(token); - stream >> outPrimitive; - return !stream.fail(); -} - -bool ObjectStreamTextIn::ReadPrimitiveData(bool &outPrimitive) -{ - String token; - if (!ReadWord(token)) - return false; - transform(token.begin(), token.end(), token.begin(), [](char inValue) { return (char)tolower(inValue); }); - outPrimitive = token == "true"; - return outPrimitive || token == "false"; -} - -bool ObjectStreamTextIn::ReadPrimitiveData(String &outPrimitive) -{ - outPrimitive.clear(); - - char c; - - // Skip whitespace - for (;;) - { - if (!ReadChar(c)) - return false; - - if (!isspace(c)) - break; - } - - // Check if it is a opening quote - if (c != '\"') - return false; - - // Read string and interpret special characters - String result; - bool escaped = false; - for (;;) - { - if (!ReadChar(c)) - break; - - switch (c) - { - case '\n': - case '\t': - break; - - case '\\': - if (escaped) - { - result += '\\'; - escaped = false; - } - else - escaped = true; - break; - - case 'n': - if (escaped) - { - result += '\n'; - escaped = false; - } - else - result += 'n'; - break; - - case 't': - if (escaped) - { - result += '\t'; - escaped = false; - } - else - result += 't'; - break; - - case '\"': - if (escaped) - { - result += '\"'; - escaped = false; - } - else - { - // Found closing double quote - outPrimitive = result; - return true; - } - break; - - default: - if (escaped) - escaped = false; - else - result += c; - break; - } - } - - return false; -} - -bool ObjectStreamTextIn::ReadPrimitiveData(Float3 &outPrimitive) -{ - float x, y, z; - if (!ReadPrimitiveData(x) || !ReadPrimitiveData(y) || !ReadPrimitiveData(z)) - return false; - outPrimitive = Float3(x, y, z); - return true; -} - -bool ObjectStreamTextIn::ReadPrimitiveData(Double3 &outPrimitive) -{ - double x, y, z; - if (!ReadPrimitiveData(x) || !ReadPrimitiveData(y) || !ReadPrimitiveData(z)) - return false; - outPrimitive = Double3(x, y, z); - return true; -} - -bool ObjectStreamTextIn::ReadPrimitiveData(Vec3 &outPrimitive) -{ - float x, y, z; - if (!ReadPrimitiveData(x) || !ReadPrimitiveData(y) || !ReadPrimitiveData(z)) - return false; - outPrimitive = Vec3(x, y, z); - return true; -} - -bool ObjectStreamTextIn::ReadPrimitiveData(DVec3 &outPrimitive) -{ - double x, y, z; - if (!ReadPrimitiveData(x) || !ReadPrimitiveData(y) || !ReadPrimitiveData(z)) - return false; - outPrimitive = DVec3(x, y, z); - return true; -} - -bool ObjectStreamTextIn::ReadPrimitiveData(Vec4 &outPrimitive) -{ - float x, y, z, w; - if (!ReadPrimitiveData(x) || !ReadPrimitiveData(y) || !ReadPrimitiveData(z) || !ReadPrimitiveData(w)) - return false; - outPrimitive = Vec4(x, y, z, w); - return true; -} - -bool ObjectStreamTextIn::ReadPrimitiveData(Quat &outPrimitive) -{ - float x, y, z, w; - if (!ReadPrimitiveData(x) || !ReadPrimitiveData(y) || !ReadPrimitiveData(z) || !ReadPrimitiveData(w)) - return false; - outPrimitive = Quat(x, y, z, w); - return true; -} - -bool ObjectStreamTextIn::ReadPrimitiveData(Mat44 &outPrimitive) -{ - Vec4 c0, c1, c2, c3; - if (!ReadPrimitiveData(c0) || !ReadPrimitiveData(c1) || !ReadPrimitiveData(c2) || !ReadPrimitiveData(c3)) - return false; - outPrimitive = Mat44(c0, c1, c2, c3); - return true; -} - -bool ObjectStreamTextIn::ReadPrimitiveData(DMat44 &outPrimitive) -{ - Vec4 c0, c1, c2; - DVec3 c3; - if (!ReadPrimitiveData(c0) || !ReadPrimitiveData(c1) || !ReadPrimitiveData(c2) || !ReadPrimitiveData(c3)) - return false; - outPrimitive = DMat44(c0, c1, c2, c3); - return true; -} - -bool ObjectStreamTextIn::ReadChar(char &outChar) -{ - mStream.get(outChar); - return !mStream.eof(); -} - -bool ObjectStreamTextIn::ReadWord(String &outWord) -{ - outWord.clear(); - - char c; - - // Skip whitespace - for (;;) - { - if (!ReadChar(c)) - return false; - - if (!isspace(c)) - break; - } - - // Read word - for (;;) - { - outWord += c; - - if (!ReadChar(c)) - break; - - if (isspace(c)) - break; - } - - return !outWord.empty(); -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamTextIn.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamTextIn.h deleted file mode 100644 index 5c7e74283a0..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamTextIn.h +++ /dev/null @@ -1,49 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -/// Implementation of ObjectStream text input stream. -class JPH_EXPORT ObjectStreamTextIn : public ObjectStreamIn -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Constructor - explicit ObjectStreamTextIn(istream &inStream); - - ///@name Input type specific operations - virtual bool ReadDataType(EOSDataType &outType) override; - virtual bool ReadName(String &outName) override; - virtual bool ReadIdentifier(Identifier &outIdentifier) override; - virtual bool ReadCount(uint32 &outCount) override; - - virtual bool ReadPrimitiveData(uint8 &outPrimitive) override; - virtual bool ReadPrimitiveData(uint16 &outPrimitive) override; - virtual bool ReadPrimitiveData(int &outPrimitive) override; - virtual bool ReadPrimitiveData(uint32 &outPrimitive) override; - virtual bool ReadPrimitiveData(uint64 &outPrimitive) override; - virtual bool ReadPrimitiveData(float &outPrimitive) override; - virtual bool ReadPrimitiveData(double &outPrimitive) override; - virtual bool ReadPrimitiveData(bool &outPrimitive) override; - virtual bool ReadPrimitiveData(String &outPrimitive) override; - virtual bool ReadPrimitiveData(Float3 &outPrimitive) override; - virtual bool ReadPrimitiveData(Double3 &outPrimitive) override; - virtual bool ReadPrimitiveData(Vec3 &outPrimitive) override; - virtual bool ReadPrimitiveData(DVec3 &outPrimitive) override; - virtual bool ReadPrimitiveData(Vec4 &outPrimitive) override; - virtual bool ReadPrimitiveData(Quat &outPrimitive) override; - virtual bool ReadPrimitiveData(Mat44 &outPrimitive) override; - virtual bool ReadPrimitiveData(DMat44 &outPrimitive) override; - -private: - bool ReadChar(char &outChar); - bool ReadWord(String &outWord); -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamTextOut.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamTextOut.cpp deleted file mode 100644 index 1a2eeaf79e2..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamTextOut.cpp +++ /dev/null @@ -1,227 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include - -JPH_NAMESPACE_BEGIN - -ObjectStreamTextOut::ObjectStreamTextOut(ostream &inStream) : - ObjectStreamOut(inStream) -{ - WriteWord(StringFormat("TOS%2d.%02d", ObjectStream::sVersion, ObjectStream::sRevision)); -} - -void ObjectStreamTextOut::WriteDataType(EOSDataType inType) -{ - switch (inType) - { - case EOSDataType::Declare: WriteWord("declare "); break; - case EOSDataType::Object: WriteWord("object "); break; - case EOSDataType::Instance: WriteWord("instance "); break; - case EOSDataType::Pointer: WriteWord("pointer "); break; - case EOSDataType::Array: WriteWord("array "); break; - case EOSDataType::T_uint8: WriteWord("uint8"); break; - case EOSDataType::T_uint16: WriteWord("uint16"); break; - case EOSDataType::T_int: WriteWord("int"); break; - case EOSDataType::T_uint32: WriteWord("uint32"); break; - case EOSDataType::T_uint64: WriteWord("uint64"); break; - case EOSDataType::T_float: WriteWord("float"); break; - case EOSDataType::T_double: WriteWord("double"); break; - case EOSDataType::T_bool: WriteWord("bool"); break; - case EOSDataType::T_String: WriteWord("string"); break; - case EOSDataType::T_Float3: WriteWord("float3"); break; - case EOSDataType::T_Double3: WriteWord("double3"); break; - case EOSDataType::T_Vec3: WriteWord("vec3"); break; - case EOSDataType::T_DVec3: WriteWord("dvec3"); break; - case EOSDataType::T_Vec4: WriteWord("vec4"); break; - case EOSDataType::T_Quat: WriteWord("quat"); break; - case EOSDataType::T_Mat44: WriteWord("mat44"); break; - case EOSDataType::T_DMat44: WriteWord("dmat44"); break; - case EOSDataType::Invalid: - default: JPH_ASSERT(false); break; - } -} - -void ObjectStreamTextOut::WriteName(const char *inName) -{ - WriteWord(String(inName) + " "); -} - -void ObjectStreamTextOut::WriteIdentifier(Identifier inIdentifier) -{ - WriteWord(StringFormat("%08X", inIdentifier)); -} - -void ObjectStreamTextOut::WriteCount(uint32 inCount) -{ - WriteWord(std::to_string(inCount)); -} - -void ObjectStreamTextOut::WritePrimitiveData(const uint8 &inPrimitive) -{ - WriteWord(std::to_string(inPrimitive)); -} - -void ObjectStreamTextOut::WritePrimitiveData(const uint16 &inPrimitive) -{ - WriteWord(std::to_string(inPrimitive)); -} - -void ObjectStreamTextOut::WritePrimitiveData(const int &inPrimitive) -{ - WriteWord(std::to_string(inPrimitive)); -} - -void ObjectStreamTextOut::WritePrimitiveData(const uint32 &inPrimitive) -{ - WriteWord(std::to_string(inPrimitive)); -} - -void ObjectStreamTextOut::WritePrimitiveData(const uint64 &inPrimitive) -{ - WriteWord(std::to_string(inPrimitive)); -} - -void ObjectStreamTextOut::WritePrimitiveData(const float &inPrimitive) -{ - std::ostringstream stream; - stream.precision(9); - stream << inPrimitive; - WriteWord(stream.str()); -} - -void ObjectStreamTextOut::WritePrimitiveData(const double &inPrimitive) -{ - std::ostringstream stream; - stream.precision(17); - stream << inPrimitive; - WriteWord(stream.str()); -} - -void ObjectStreamTextOut::WritePrimitiveData(const bool &inPrimitive) -{ - WriteWord(inPrimitive? "true" : "false"); -} - -void ObjectStreamTextOut::WritePrimitiveData(const Float3 &inPrimitive) -{ - WritePrimitiveData(inPrimitive.x); - WriteChar(' '); - WritePrimitiveData(inPrimitive.y); - WriteChar(' '); - WritePrimitiveData(inPrimitive.z); -} - -void ObjectStreamTextOut::WritePrimitiveData(const Double3 &inPrimitive) -{ - WritePrimitiveData(inPrimitive.x); - WriteChar(' '); - WritePrimitiveData(inPrimitive.y); - WriteChar(' '); - WritePrimitiveData(inPrimitive.z); -} - -void ObjectStreamTextOut::WritePrimitiveData(const Vec3 &inPrimitive) -{ - WritePrimitiveData(inPrimitive.GetX()); - WriteChar(' '); - WritePrimitiveData(inPrimitive.GetY()); - WriteChar(' '); - WritePrimitiveData(inPrimitive.GetZ()); -} - -void ObjectStreamTextOut::WritePrimitiveData(const DVec3 &inPrimitive) -{ - WritePrimitiveData(inPrimitive.GetX()); - WriteChar(' '); - WritePrimitiveData(inPrimitive.GetY()); - WriteChar(' '); - WritePrimitiveData(inPrimitive.GetZ()); -} - -void ObjectStreamTextOut::WritePrimitiveData(const Vec4 &inPrimitive) -{ - WritePrimitiveData(inPrimitive.GetX()); - WriteChar(' '); - WritePrimitiveData(inPrimitive.GetY()); - WriteChar(' '); - WritePrimitiveData(inPrimitive.GetZ()); - WriteChar(' '); - WritePrimitiveData(inPrimitive.GetW()); -} - -void ObjectStreamTextOut::WritePrimitiveData(const Quat &inPrimitive) -{ - WritePrimitiveData(inPrimitive.GetX()); - WriteChar(' '); - WritePrimitiveData(inPrimitive.GetY()); - WriteChar(' '); - WritePrimitiveData(inPrimitive.GetZ()); - WriteChar(' '); - WritePrimitiveData(inPrimitive.GetW()); -} - -void ObjectStreamTextOut::WritePrimitiveData(const Mat44 &inPrimitive) -{ - WritePrimitiveData(inPrimitive.GetColumn4(0)); - WriteChar(' '); - WritePrimitiveData(inPrimitive.GetColumn4(1)); - WriteChar(' '); - WritePrimitiveData(inPrimitive.GetColumn4(2)); - WriteChar(' '); - WritePrimitiveData(inPrimitive.GetColumn4(3)); -} - -void ObjectStreamTextOut::WritePrimitiveData(const DMat44 &inPrimitive) -{ - WritePrimitiveData(inPrimitive.GetColumn4(0)); - WriteChar(' '); - WritePrimitiveData(inPrimitive.GetColumn4(1)); - WriteChar(' '); - WritePrimitiveData(inPrimitive.GetColumn4(2)); - WriteChar(' '); - WritePrimitiveData(inPrimitive.GetTranslation()); -} - -void ObjectStreamTextOut::WritePrimitiveData(const String &inPrimitive) -{ - String temporary(inPrimitive); - StringReplace(temporary, "\\", "\\\\"); - StringReplace(temporary, "\n", "\\n"); - StringReplace(temporary, "\t", "\\t"); - StringReplace(temporary, "\"", "\\\""); - WriteWord(String("\"") + temporary + String("\"")); -} - -void ObjectStreamTextOut::HintNextItem() -{ - WriteWord("\r\n"); - for (int i = 0; i < mIndentation; ++i) - WriteWord(" "); -} - -void ObjectStreamTextOut::HintIndentUp() -{ - ++mIndentation; -} - -void ObjectStreamTextOut::HintIndentDown() -{ - --mIndentation; -} - -void ObjectStreamTextOut::WriteChar(char inChar) -{ - mStream.put(inChar); -} - -void ObjectStreamTextOut::WriteWord(const string_view &inWord) -{ - mStream << inWord; -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamTextOut.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamTextOut.h deleted file mode 100644 index 30829a9deef..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamTextOut.h +++ /dev/null @@ -1,56 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -/// Implementation of ObjectStream text output stream. -class JPH_EXPORT ObjectStreamTextOut : public ObjectStreamOut -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Constructor and destructor - explicit ObjectStreamTextOut(ostream &inStream); - - ///@name Output type specific operations - virtual void WriteDataType(EOSDataType inType) override; - virtual void WriteName(const char *inName) override; - virtual void WriteIdentifier(Identifier inIdentifier) override; - virtual void WriteCount(uint32 inCount) override; - - virtual void WritePrimitiveData(const uint8 &inPrimitive) override; - virtual void WritePrimitiveData(const uint16 &inPrimitive) override; - virtual void WritePrimitiveData(const int &inPrimitive) override; - virtual void WritePrimitiveData(const uint32 &inPrimitive) override; - virtual void WritePrimitiveData(const uint64 &inPrimitive) override; - virtual void WritePrimitiveData(const float &inPrimitive) override; - virtual void WritePrimitiveData(const double &inPrimitive) override; - virtual void WritePrimitiveData(const bool &inPrimitive) override; - virtual void WritePrimitiveData(const String &inPrimitive) override; - virtual void WritePrimitiveData(const Float3 &inPrimitive) override; - virtual void WritePrimitiveData(const Double3 &inPrimitive) override; - virtual void WritePrimitiveData(const Vec3 &inPrimitive) override; - virtual void WritePrimitiveData(const DVec3 &inPrimitive) override; - virtual void WritePrimitiveData(const Vec4 &inPrimitive) override; - virtual void WritePrimitiveData(const Quat &inPrimitive) override; - virtual void WritePrimitiveData(const Mat44 &inPrimitive) override; - virtual void WritePrimitiveData(const DMat44 &inPrimitive) override; - - ///@name Layout hints (for text output) - virtual void HintNextItem() override; - virtual void HintIndentUp() override; - virtual void HintIndentDown() override; - -private: - void WriteChar(char inChar); - void WriteWord(const string_view &inWord); - - int mIndentation = 0; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamTypes.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamTypes.h deleted file mode 100644 index 6fb08af1710..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/ObjectStreamTypes.h +++ /dev/null @@ -1,24 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -// Note: Order is important, an enum is created and its value is stored in a binary stream! -JPH_DECLARE_PRIMITIVE(uint8) -JPH_DECLARE_PRIMITIVE(uint16) -JPH_DECLARE_PRIMITIVE(int) -JPH_DECLARE_PRIMITIVE(uint32) -JPH_DECLARE_PRIMITIVE(uint64) -JPH_DECLARE_PRIMITIVE(float) -JPH_DECLARE_PRIMITIVE(bool) -JPH_DECLARE_PRIMITIVE(String) -JPH_DECLARE_PRIMITIVE(Float3) -JPH_DECLARE_PRIMITIVE(Vec3) -JPH_DECLARE_PRIMITIVE(Vec4) -JPH_DECLARE_PRIMITIVE(Quat) -JPH_DECLARE_PRIMITIVE(Mat44) -JPH_DECLARE_PRIMITIVE(double) -JPH_DECLARE_PRIMITIVE(DVec3) -JPH_DECLARE_PRIMITIVE(DMat44) -JPH_DECLARE_PRIMITIVE(Double3) - -#undef JPH_DECLARE_PRIMITIVE diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/SerializableAttribute.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/SerializableAttribute.h deleted file mode 100644 index 8701eedff4e..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/SerializableAttribute.h +++ /dev/null @@ -1,107 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -JPH_NAMESPACE_BEGIN - -class RTTI; -class IObjectStreamIn; -class IObjectStreamOut; - -/// Data type -enum class EOSDataType -{ - /// Control codes - Declare, ///< Used to declare the attributes of a new object type - Object, ///< Start of a new object - Instance, ///< Used in attribute declaration, indicates that an object is an instanced attribute (no pointer) - Pointer, ///< Used in attribute declaration, indicates that an object is a pointer attribute - Array, ///< Used in attribute declaration, indicates that this is an array of objects - - // Basic types (primitives) - #define JPH_DECLARE_PRIMITIVE(name) T_##name, - - // This file uses the JPH_DECLARE_PRIMITIVE macro to define all types - #include - - // Error values for read functions - Invalid, ///< Next token on the stream was not a valid data type -}; - -/// Attributes are members of classes that need to be serialized. -class SerializableAttribute -{ -public: - ///@ Serialization functions - using pGetMemberPrimitiveType = const RTTI * (*)(); - using pIsType = bool (*)(int inArrayDepth, EOSDataType inDataType, const char *inClassName); - using pReadData = bool (*)(IObjectStreamIn &ioStream, void *inObject); - using pWriteData = void (*)(IObjectStreamOut &ioStream, const void *inObject); - using pWriteDataType = void (*)(IObjectStreamOut &ioStream); - - /// Constructor - SerializableAttribute(const char *inName, uint inMemberOffset, pGetMemberPrimitiveType inGetMemberPrimitiveType, pIsType inIsType, pReadData inReadData, pWriteData inWriteData, pWriteDataType inWriteDataType) : mName(inName), mMemberOffset(inMemberOffset), mGetMemberPrimitiveType(inGetMemberPrimitiveType), mIsType(inIsType), mReadData(inReadData), mWriteData(inWriteData), mWriteDataType(inWriteDataType) { } - - /// Construct from other attribute with base class offset - SerializableAttribute(const SerializableAttribute &inOther, int inBaseOffset) : mName(inOther.mName), mMemberOffset(inOther.mMemberOffset + inBaseOffset), mGetMemberPrimitiveType(inOther.mGetMemberPrimitiveType), mIsType(inOther.mIsType), mReadData(inOther.mReadData), mWriteData(inOther.mWriteData), mWriteDataType(inOther.mWriteDataType) { } - - /// Name of the attribute - void SetName(const char *inName) { mName = inName; } - const char * GetName() const { return mName; } - - /// Access to the memory location that contains the member - template - inline T * GetMemberPointer(void *inObject) const { return reinterpret_cast(reinterpret_cast(inObject) + mMemberOffset); } - template - inline const T * GetMemberPointer(const void *inObject) const { return reinterpret_cast(reinterpret_cast(inObject) + mMemberOffset); } - - /// In case this attribute contains an RTTI type, return it (note that a Array will return the rtti of sometype) - const RTTI * GetMemberPrimitiveType() const - { - return mGetMemberPrimitiveType(); - } - - /// Check if this attribute is of a specific type - bool IsType(int inArrayDepth, EOSDataType inDataType, const char *inClassName) const - { - return mIsType(inArrayDepth, inDataType, inClassName); - } - - /// Read the data for this attribute into attribute containing class inObject - bool ReadData(IObjectStreamIn &ioStream, void *inObject) const - { - return mReadData(ioStream, GetMemberPointer(inObject)); - } - - /// Write the data for this attribute from attribute containing class inObject - void WriteData(IObjectStreamOut &ioStream, const void *inObject) const - { - mWriteData(ioStream, GetMemberPointer(inObject)); - } - - /// Write the data type of this attribute to a stream - void WriteDataType(IObjectStreamOut &ioStream) const - { - mWriteDataType(ioStream); - } - -private: - // Name of the attribute - const char * mName; - - // Offset of the member relative to the class - uint mMemberOffset; - - // In case this attribute contains an RTTI type, return it (note that a Array will return the rtti of sometype) - pGetMemberPrimitiveType mGetMemberPrimitiveType; - - // Serialization operations - pIsType mIsType; - pReadData mReadData; - pWriteData mWriteData; - pWriteDataType mWriteDataType; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/SerializableAttributeEnum.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/SerializableAttributeEnum.h deleted file mode 100644 index be1ca34bf93..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/SerializableAttributeEnum.h +++ /dev/null @@ -1,58 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include - -JPH_NAMESPACE_BEGIN - -////////////////////////////////////////////////////////////////////////////////////////// -// Macros to add properties to be serialized -////////////////////////////////////////////////////////////////////////////////////////// - -template -inline void AddSerializableAttributeEnum(RTTI &inRTTI, uint inOffset, const char *inName) -{ - inRTTI.AddAttribute(SerializableAttribute(inName, inOffset, - []() -> const RTTI * - { - return nullptr; - }, - [](int inArrayDepth, EOSDataType inDataType, [[maybe_unused]] const char *inClassName) - { - return inArrayDepth == 0 && inDataType == EOSDataType::T_uint32; - }, - [](IObjectStreamIn &ioStream, void *inObject) - { - uint32 temporary; - if (OSReadData(ioStream, temporary)) - { - *reinterpret_cast(inObject) = static_cast(temporary); - return true; - } - return false; - }, - [](IObjectStreamOut &ioStream, const void *inObject) - { - static_assert(sizeof(MemberType) <= sizeof(uint32)); - uint32 temporary = uint32(*reinterpret_cast(inObject)); - OSWriteData(ioStream, temporary); - }, - [](IObjectStreamOut &ioStream) - { - ioStream.WriteDataType(EOSDataType::T_uint32); - })); -} - -// JPH_ADD_ENUM_ATTRIBUTE_WITH_ALIAS -#define JPH_ADD_ENUM_ATTRIBUTE_WITH_ALIAS(class_name, member_name, alias_name) \ - AddSerializableAttributeEnum(inRTTI, offsetof(class_name, member_name), alias_name); - -// JPH_ADD_ENUM_ATTRIBUTE -#define JPH_ADD_ENUM_ATTRIBUTE(class_name, member_name) \ - JPH_ADD_ENUM_ATTRIBUTE_WITH_ALIAS(class_name, member_name, #member_name); - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/SerializableAttributeTyped.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/SerializableAttributeTyped.h deleted file mode 100644 index 23d5dfe7cd2..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/SerializableAttributeTyped.h +++ /dev/null @@ -1,51 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -////////////////////////////////////////////////////////////////////////////////////////// -// Macros to add properties to be serialized -////////////////////////////////////////////////////////////////////////////////////////// - -template -inline void AddSerializableAttributeTyped(RTTI &inRTTI, uint inOffset, const char *inName) -{ - inRTTI.AddAttribute(SerializableAttribute(inName, inOffset, - []() - { - return GetPrimitiveTypeOfType((MemberType *)nullptr); - }, - [](int inArrayDepth, EOSDataType inDataType, const char *inClassName) - { - return OSIsType((MemberType *)nullptr, inArrayDepth, inDataType, inClassName); - }, - [](IObjectStreamIn &ioStream, void *inObject) - { - return OSReadData(ioStream, *reinterpret_cast(inObject)); - }, - [](IObjectStreamOut &ioStream, const void *inObject) - { - OSWriteData(ioStream, *reinterpret_cast(inObject)); - }, - [](IObjectStreamOut &ioStream) - { - OSWriteDataType(ioStream, (MemberType *)nullptr); - })); -} - -// JPH_ADD_ATTRIBUTE -#define JPH_ADD_ATTRIBUTE_WITH_ALIAS(class_name, member_name, alias_name) \ - AddSerializableAttributeTyped(inRTTI, offsetof(class_name, member_name), alias_name); - -// JPH_ADD_ATTRIBUTE -#define JPH_ADD_ATTRIBUTE(class_name, member_name) \ - JPH_ADD_ATTRIBUTE_WITH_ALIAS(class_name, member_name, #member_name) - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/SerializableObject.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/SerializableObject.cpp deleted file mode 100644 index 98d3b3cf8d0..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/SerializableObject.cpp +++ /dev/null @@ -1,15 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include - -JPH_NAMESPACE_BEGIN - -JPH_IMPLEMENT_SERIALIZABLE_ABSTRACT(SerializableObject) -{ -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/SerializableObject.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/SerializableObject.h deleted file mode 100644 index 5ff3f749614..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/SerializableObject.h +++ /dev/null @@ -1,155 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -////////////////////////////////////////////////////////////////////////////////////////// -// Helper macros -////////////////////////////////////////////////////////////////////////////////////////// - -// JPH_DECLARE_SERIALIZATION_FUNCTIONS -#define JPH_DECLARE_SERIALIZATION_FUNCTIONS(linkage, prefix, class_name) \ - linkage prefix bool OSReadData(IObjectStreamIn &ioStream, class_name &inInstance); \ - linkage prefix bool OSReadData(IObjectStreamIn &ioStream, class_name *&inPointer); \ - linkage prefix bool OSIsType(class_name *, int inArrayDepth, EOSDataType inDataType, const char *inClassName); \ - linkage prefix bool OSIsType(class_name **, int inArrayDepth, EOSDataType inDataType, const char *inClassName); \ - linkage prefix void OSWriteData(IObjectStreamOut &ioStream, const class_name &inInstance); \ - linkage prefix void OSWriteData(IObjectStreamOut &ioStream, class_name *const &inPointer); \ - linkage prefix void OSWriteDataType(IObjectStreamOut &ioStream, class_name *); \ - linkage prefix void OSWriteDataType(IObjectStreamOut &ioStream, class_name **); - -// JPH_IMPLEMENT_SERIALIZATION_FUNCTIONS -#define JPH_IMPLEMENT_SERIALIZATION_FUNCTIONS(class_name) \ - bool OSReadData(IObjectStreamIn &ioStream, class_name &inInstance) \ - { \ - return ioStream.ReadClassData(#class_name, (void *)&inInstance); \ - } \ - bool OSReadData(IObjectStreamIn &ioStream, class_name *&inPointer) \ - { \ - return ioStream.ReadPointerData(JPH_RTTI(class_name), (void **)&inPointer); \ - } \ - bool OSIsType(class_name *, int inArrayDepth, EOSDataType inDataType, const char *inClassName) \ - { \ - return inArrayDepth == 0 && inDataType == EOSDataType::Instance && strcmp(inClassName, #class_name) == 0; \ - } \ - bool OSIsType(class_name **, int inArrayDepth, EOSDataType inDataType, const char *inClassName) \ - { \ - return inArrayDepth == 0 && inDataType == EOSDataType::Pointer && strcmp(inClassName, #class_name) == 0; \ - } \ - void OSWriteData(IObjectStreamOut &ioStream, const class_name &inInstance) \ - { \ - ioStream.WriteClassData(JPH_RTTI(class_name), (void *)&inInstance); \ - } \ - void OSWriteData(IObjectStreamOut &ioStream, class_name *const &inPointer) \ - { \ - if (inPointer) \ - ioStream.WritePointerData(GetRTTI(inPointer), (void *)inPointer); \ - else \ - ioStream.WritePointerData(nullptr, nullptr); \ - } \ - void OSWriteDataType(IObjectStreamOut &ioStream, class_name *) \ - { \ - ioStream.WriteDataType(EOSDataType::Instance); \ - ioStream.WriteName(#class_name); \ - } \ - void OSWriteDataType(IObjectStreamOut &ioStream, class_name **) \ - { \ - ioStream.WriteDataType(EOSDataType::Pointer); \ - ioStream.WriteName(#class_name); \ - } - -////////////////////////////////////////////////////////////////////////////////////////// -// Use these macros on non-virtual objects to make them serializable -////////////////////////////////////////////////////////////////////////////////////////// - -// JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL -#define JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(linkage, class_name) \ -public: \ - JPH_DECLARE_RTTI_NON_VIRTUAL(linkage, class_name) \ - JPH_DECLARE_SERIALIZATION_FUNCTIONS(linkage, friend, class_name) \ - -// JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL -#define JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(class_name) \ - JPH_IMPLEMENT_SERIALIZATION_FUNCTIONS(class_name) \ - JPH_IMPLEMENT_RTTI_NON_VIRTUAL(class_name) \ - -////////////////////////////////////////////////////////////////////////////////////////// -// Same as above, but when you cannot insert the declaration in the class itself -////////////////////////////////////////////////////////////////////////////////////////// - -// JPH_DECLARE_SERIALIZABLE_OUTSIDE_CLASS -#define JPH_DECLARE_SERIALIZABLE_OUTSIDE_CLASS(linkage, class_name) \ - JPH_DECLARE_RTTI_OUTSIDE_CLASS(linkage, class_name) \ - JPH_DECLARE_SERIALIZATION_FUNCTIONS(linkage, extern, class_name) \ - -// JPH_IMPLEMENT_SERIALIZABLE_OUTSIDE_CLASS -#define JPH_IMPLEMENT_SERIALIZABLE_OUTSIDE_CLASS(class_name) \ - JPH_IMPLEMENT_SERIALIZATION_FUNCTIONS(class_name) \ - JPH_IMPLEMENT_RTTI_OUTSIDE_CLASS(class_name) \ - -////////////////////////////////////////////////////////////////////////////////////////// -// Same as above, but for classes that have virtual functions -////////////////////////////////////////////////////////////////////////////////////////// - -// JPH_DECLARE_SERIALIZABLE_VIRTUAL - Use for concrete, non-base classes -#define JPH_DECLARE_SERIALIZABLE_VIRTUAL(linkage, class_name) \ -public: \ - JPH_DECLARE_RTTI_VIRTUAL(linkage, class_name) \ - JPH_DECLARE_SERIALIZATION_FUNCTIONS(linkage, friend, class_name) \ - -// JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL -#define JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(class_name) \ - JPH_IMPLEMENT_SERIALIZATION_FUNCTIONS(class_name) \ - JPH_IMPLEMENT_RTTI_VIRTUAL(class_name) \ - -// JPH_DECLARE_SERIALIZABLE_ABSTRACT - Use for abstract, non-base classes -#define JPH_DECLARE_SERIALIZABLE_ABSTRACT(linkage, class_name) \ -public: \ - JPH_DECLARE_RTTI_ABSTRACT(linkage, class_name) \ - JPH_DECLARE_SERIALIZATION_FUNCTIONS(linkage, friend, class_name) \ - -// JPH_IMPLEMENT_SERIALIZABLE_ABSTRACT -#define JPH_IMPLEMENT_SERIALIZABLE_ABSTRACT(class_name) \ - JPH_IMPLEMENT_SERIALIZATION_FUNCTIONS(class_name) \ - JPH_IMPLEMENT_RTTI_ABSTRACT(class_name) \ - -// JPH_DECLARE_SERIALIZABLE_VIRTUAL_BASE - Use for concrete base classes -#define JPH_DECLARE_SERIALIZABLE_VIRTUAL_BASE(linkage, class_name) \ -public: \ - JPH_DECLARE_RTTI_VIRTUAL_BASE(linkage, class_name) \ - JPH_DECLARE_SERIALIZATION_FUNCTIONS(linkage, friend, class_name) \ - -// JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL_BASE -#define JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL_BASE(class_name) \ - JPH_IMPLEMENT_SERIALIZATION_FUNCTIONS(class_name) \ - JPH_IMPLEMENT_RTTI_VIRTUAL_BASE(class_name) \ - -// JPH_DECLARE_SERIALIZABLE_ABSTRACT_BASE - Use for abstract base class -#define JPH_DECLARE_SERIALIZABLE_ABSTRACT_BASE(linkage, class_name) \ -public: \ - JPH_DECLARE_RTTI_ABSTRACT_BASE(linkage, class_name) \ - JPH_DECLARE_SERIALIZATION_FUNCTIONS(linkage, friend, class_name) \ - -// JPH_IMPLEMENT_SERIALIZABLE_ABSTRACT_BASE -#define JPH_IMPLEMENT_SERIALIZABLE_ABSTRACT_BASE(class_name) \ - JPH_IMPLEMENT_SERIALIZATION_FUNCTIONS(class_name) \ - JPH_IMPLEMENT_RTTI_ABSTRACT_BASE(class_name) - -/// Classes must be derived from SerializableObject if you want to be able to save pointers or -/// reference counting pointers to objects of this or derived classes. The type will automatically -/// be determined during serialization and upon deserialization it will be restored correctly. -class JPH_EXPORT SerializableObject : public NonCopyable -{ - JPH_DECLARE_SERIALIZABLE_ABSTRACT_BASE(JPH_EXPORT, SerializableObject) - -public: - /// Constructor - virtual ~SerializableObject() = default; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/TypeDeclarations.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/TypeDeclarations.cpp deleted file mode 100644 index a1d5ac6ac17..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/TypeDeclarations.cpp +++ /dev/null @@ -1,55 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include - -JPH_NAMESPACE_BEGIN - -JPH_IMPLEMENT_RTTI_OUTSIDE_CLASS(uint8) { } -JPH_IMPLEMENT_RTTI_OUTSIDE_CLASS(uint16) { } -JPH_IMPLEMENT_RTTI_OUTSIDE_CLASS(int) { } -JPH_IMPLEMENT_RTTI_OUTSIDE_CLASS(uint32) { } -JPH_IMPLEMENT_RTTI_OUTSIDE_CLASS(uint64) { } -JPH_IMPLEMENT_RTTI_OUTSIDE_CLASS(float) { } -JPH_IMPLEMENT_RTTI_OUTSIDE_CLASS(double) { } -JPH_IMPLEMENT_RTTI_OUTSIDE_CLASS(bool) { } -JPH_IMPLEMENT_RTTI_OUTSIDE_CLASS(String) { } -JPH_IMPLEMENT_RTTI_OUTSIDE_CLASS(Float3) { } -JPH_IMPLEMENT_RTTI_OUTSIDE_CLASS(Double3) { } -JPH_IMPLEMENT_RTTI_OUTSIDE_CLASS(Vec3) { } -JPH_IMPLEMENT_RTTI_OUTSIDE_CLASS(DVec3) { } -JPH_IMPLEMENT_RTTI_OUTSIDE_CLASS(Vec4) { } -JPH_IMPLEMENT_RTTI_OUTSIDE_CLASS(Quat) { } -JPH_IMPLEMENT_RTTI_OUTSIDE_CLASS(Mat44) { } -JPH_IMPLEMENT_RTTI_OUTSIDE_CLASS(DMat44) { } - -JPH_IMPLEMENT_SERIALIZABLE_OUTSIDE_CLASS(Color) -{ - JPH_ADD_ATTRIBUTE(Color, r) - JPH_ADD_ATTRIBUTE(Color, g) - JPH_ADD_ATTRIBUTE(Color, b) - JPH_ADD_ATTRIBUTE(Color, a) -} - -JPH_IMPLEMENT_SERIALIZABLE_OUTSIDE_CLASS(AABox) -{ - JPH_ADD_ATTRIBUTE(AABox, mMin) - JPH_ADD_ATTRIBUTE(AABox, mMax) -} - -JPH_IMPLEMENT_SERIALIZABLE_OUTSIDE_CLASS(Triangle) -{ - JPH_ADD_ATTRIBUTE(Triangle, mV) - JPH_ADD_ATTRIBUTE(Triangle, mMaterialIndex) -} - -JPH_IMPLEMENT_SERIALIZABLE_OUTSIDE_CLASS(IndexedTriangle) -{ - JPH_ADD_ATTRIBUTE(IndexedTriangle, mIdx) - JPH_ADD_ATTRIBUTE(IndexedTriangle, mMaterialIndex) -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/TypeDeclarations.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/TypeDeclarations.h deleted file mode 100644 index 2a61c306be5..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/ObjectStream/TypeDeclarations.h +++ /dev/null @@ -1,41 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -JPH_DECLARE_RTTI_OUTSIDE_CLASS(JPH_EXPORT, uint8); -JPH_DECLARE_RTTI_OUTSIDE_CLASS(JPH_EXPORT, uint16); -JPH_DECLARE_RTTI_OUTSIDE_CLASS(JPH_EXPORT, int); -JPH_DECLARE_RTTI_OUTSIDE_CLASS(JPH_EXPORT, uint32); -JPH_DECLARE_RTTI_OUTSIDE_CLASS(JPH_EXPORT, uint64); -JPH_DECLARE_RTTI_OUTSIDE_CLASS(JPH_EXPORT, float); -JPH_DECLARE_RTTI_OUTSIDE_CLASS(JPH_EXPORT, double); -JPH_DECLARE_RTTI_OUTSIDE_CLASS(JPH_EXPORT, bool); -JPH_DECLARE_RTTI_OUTSIDE_CLASS(JPH_EXPORT, String); -JPH_DECLARE_RTTI_OUTSIDE_CLASS(JPH_EXPORT, Float3); -JPH_DECLARE_RTTI_OUTSIDE_CLASS(JPH_EXPORT, Double3); -JPH_DECLARE_RTTI_OUTSIDE_CLASS(JPH_EXPORT, Vec3); -JPH_DECLARE_RTTI_OUTSIDE_CLASS(JPH_EXPORT, DVec3); -JPH_DECLARE_RTTI_OUTSIDE_CLASS(JPH_EXPORT, Vec4); -JPH_DECLARE_RTTI_OUTSIDE_CLASS(JPH_EXPORT, Quat); -JPH_DECLARE_RTTI_OUTSIDE_CLASS(JPH_EXPORT, Mat44); -JPH_DECLARE_RTTI_OUTSIDE_CLASS(JPH_EXPORT, DMat44); -JPH_DECLARE_SERIALIZABLE_OUTSIDE_CLASS(JPH_EXPORT, Color); -JPH_DECLARE_SERIALIZABLE_OUTSIDE_CLASS(JPH_EXPORT, AABox); -JPH_DECLARE_SERIALIZABLE_OUTSIDE_CLASS(JPH_EXPORT, Triangle); -JPH_DECLARE_SERIALIZABLE_OUTSIDE_CLASS(JPH_EXPORT, IndexedTriangle); - -JPH_NAMESPACE_END - -// These need to be added after all types have been registered or else clang under linux will not find GetRTTIOfType for the type -#include -#include diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/AllowedDOFs.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/AllowedDOFs.h deleted file mode 100644 index 8445cb18633..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/AllowedDOFs.h +++ /dev/null @@ -1,68 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2023 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -JPH_NAMESPACE_BEGIN - -/// Enum used in BodyCreationSettings and MotionProperties to indicate which degrees of freedom a body has -enum class EAllowedDOFs : uint8 -{ - None = 0b000000, ///< No degrees of freedom are allowed. Note that this is not valid and will crash. Use a static body instead. - All = 0b111111, ///< All degrees of freedom are allowed - TranslationX = 0b000001, ///< Body can move in world space X axis - TranslationY = 0b000010, ///< Body can move in world space Y axis - TranslationZ = 0b000100, ///< Body can move in world space Z axis - RotationX = 0b001000, ///< Body can rotate around world space X axis - RotationY = 0b010000, ///< Body can rotate around world space Y axis - RotationZ = 0b100000, ///< Body can rotate around world space Z axis - Plane2D = TranslationX | TranslationY | RotationZ, ///< Body can only move in X and Y axis and rotate around Z axis -}; - -/// Bitwise OR operator for EAllowedDOFs -constexpr EAllowedDOFs operator | (EAllowedDOFs inLHS, EAllowedDOFs inRHS) -{ - return EAllowedDOFs(uint8(inLHS) | uint8(inRHS)); -} - -/// Bitwise AND operator for EAllowedDOFs -constexpr EAllowedDOFs operator & (EAllowedDOFs inLHS, EAllowedDOFs inRHS) -{ - return EAllowedDOFs(uint8(inLHS) & uint8(inRHS)); -} - -/// Bitwise XOR operator for EAllowedDOFs -constexpr EAllowedDOFs operator ^ (EAllowedDOFs inLHS, EAllowedDOFs inRHS) -{ - return EAllowedDOFs(uint8(inLHS) ^ uint8(inRHS)); -} - -/// Bitwise NOT operator for EAllowedDOFs -constexpr EAllowedDOFs operator ~ (EAllowedDOFs inAllowedDOFs) -{ - return EAllowedDOFs(~uint8(inAllowedDOFs)); -} - -/// Bitwise OR assignment operator for EAllowedDOFs -constexpr EAllowedDOFs & operator |= (EAllowedDOFs &ioLHS, EAllowedDOFs inRHS) -{ - ioLHS = ioLHS | inRHS; - return ioLHS; -} - -/// Bitwise AND assignment operator for EAllowedDOFs -constexpr EAllowedDOFs & operator &= (EAllowedDOFs &ioLHS, EAllowedDOFs inRHS) -{ - ioLHS = ioLHS & inRHS; - return ioLHS; -} - -/// Bitwise XOR assignment operator for EAllowedDOFs -constexpr EAllowedDOFs & operator ^= (EAllowedDOFs &ioLHS, EAllowedDOFs inRHS) -{ - ioLHS = ioLHS ^ inRHS; - return ioLHS; -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/Body.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/Body.cpp deleted file mode 100644 index 085ae427c33..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/Body.cpp +++ /dev/null @@ -1,413 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef JPH_DEBUG_RENDERER - #include -#endif // JPH_DEBUG_RENDERER - -JPH_NAMESPACE_BEGIN - -static const SphereShape sFixedToWorldShape(FLT_EPSILON); -Body Body::sFixedToWorld(false); - -Body::Body(bool) : - mPosition(Vec3::sZero()), - mRotation(Quat::sIdentity()), - mShape(&sFixedToWorldShape), // Dummy shape - mFriction(0.0f), - mRestitution(0.0f), - mObjectLayer(cObjectLayerInvalid), - mMotionType(EMotionType::Static) -{ - sFixedToWorldShape.SetEmbedded(); -} - -void Body::SetMotionType(EMotionType inMotionType) -{ - if (mMotionType == inMotionType) - return; - - JPH_ASSERT(inMotionType == EMotionType::Static || mMotionProperties != nullptr, "Body needs to be created with mAllowDynamicOrKinematic set tot true"); - JPH_ASSERT(inMotionType != EMotionType::Static || !IsActive(), "Deactivate body first"); - JPH_ASSERT(inMotionType == EMotionType::Dynamic || !IsSoftBody(), "Soft bodies can only be dynamic, you can make individual vertices kinematic by setting their inverse mass to 0"); - - // Store new motion type - mMotionType = inMotionType; - - if (mMotionProperties != nullptr) - { - // Update cache - JPH_IF_ENABLE_ASSERTS(mMotionProperties->mCachedMotionType = inMotionType;) - - switch (inMotionType) - { - case EMotionType::Static: - // Stop the object - mMotionProperties->mLinearVelocity = Vec3::sZero(); - mMotionProperties->mAngularVelocity = Vec3::sZero(); - [[fallthrough]]; - - case EMotionType::Kinematic: - // Cancel forces - mMotionProperties->ResetForce(); - mMotionProperties->ResetTorque(); - break; - - case EMotionType::Dynamic: - break; - } - } -} - -void Body::SetAllowSleeping(bool inAllow) -{ - mMotionProperties->mAllowSleeping = inAllow; - if (inAllow) - ResetSleepTimer(); -} - -void Body::MoveKinematic(RVec3Arg inTargetPosition, QuatArg inTargetRotation, float inDeltaTime) -{ - JPH_ASSERT(IsRigidBody()); // Only valid for rigid bodies - JPH_ASSERT(!IsStatic()); - JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sPositionAccess, BodyAccess::EAccess::Read)); - - // Calculate center of mass at end situation - RVec3 new_com = inTargetPosition + inTargetRotation * mShape->GetCenterOfMass(); - - // Calculate delta position and rotation - Vec3 delta_pos = Vec3(new_com - mPosition); - Quat delta_rotation = inTargetRotation * mRotation.Conjugated(); - - mMotionProperties->MoveKinematic(delta_pos, delta_rotation, inDeltaTime); -} - -void Body::CalculateWorldSpaceBoundsInternal() -{ - mBounds = mShape->GetWorldSpaceBounds(GetCenterOfMassTransform(), Vec3::sReplicate(1.0f)); -} - -void Body::SetPositionAndRotationInternal(RVec3Arg inPosition, QuatArg inRotation, bool inResetSleepTimer) -{ - JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sPositionAccess, BodyAccess::EAccess::ReadWrite)); - - mPosition = inPosition + inRotation * mShape->GetCenterOfMass(); - mRotation = inRotation; - - // Initialize bounding box - CalculateWorldSpaceBoundsInternal(); - - // Reset sleeping test - if (inResetSleepTimer && mMotionProperties != nullptr) - ResetSleepTimer(); -} - -void Body::UpdateCenterOfMassInternal(Vec3Arg inPreviousCenterOfMass, bool inUpdateMassProperties) -{ - // Update center of mass position so the world position for this body stays the same - mPosition += mRotation * (mShape->GetCenterOfMass() - inPreviousCenterOfMass); - - // Recalculate mass and inertia if requested - if (inUpdateMassProperties && mMotionProperties != nullptr) - mMotionProperties->SetMassProperties(mMotionProperties->GetAllowedDOFs(), mShape->GetMassProperties()); -} - -void Body::SetShapeInternal(const Shape *inShape, bool inUpdateMassProperties) -{ - JPH_ASSERT(IsRigidBody()); // Only valid for rigid bodies - JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sPositionAccess, BodyAccess::EAccess::ReadWrite)); - - // Get the old center of mass - Vec3 old_com = mShape->GetCenterOfMass(); - - // Update the shape - mShape = inShape; - - // Update center of mass - UpdateCenterOfMassInternal(old_com, inUpdateMassProperties); - - // Recalculate bounding box - CalculateWorldSpaceBoundsInternal(); -} - -ECanSleep Body::UpdateSleepStateInternal(float inDeltaTime, float inMaxMovement, float inTimeBeforeSleep) -{ - // Check override & sensors will never go to sleep (they would stop detecting collisions with sleeping bodies) - if (!mMotionProperties->mAllowSleeping || IsSensor()) - return ECanSleep::CannotSleep; - - // Get the points to test - RVec3 points[3]; - GetSleepTestPoints(points); - -#ifdef JPH_DOUBLE_PRECISION - // Get base offset for spheres - DVec3 offset = mMotionProperties->GetSleepTestOffset(); -#endif // JPH_DOUBLE_PRECISION - - for (int i = 0; i < 3; ++i) - { - Sphere &sphere = mMotionProperties->mSleepTestSpheres[i]; - - // Make point relative to base offset -#ifdef JPH_DOUBLE_PRECISION - Vec3 p = Vec3(points[i] - offset); -#else - Vec3 p = points[i]; -#endif // JPH_DOUBLE_PRECISION - - // Encapsulate the point in a sphere - sphere.EncapsulatePoint(p); - - // Test if it exceeded the max movement - if (sphere.GetRadius() > inMaxMovement) - { - // Body is not sleeping, reset test - mMotionProperties->ResetSleepTestSpheres(points); - return ECanSleep::CannotSleep; - } - } - - return mMotionProperties->AccumulateSleepTime(inDeltaTime, inTimeBeforeSleep); -} - -bool Body::ApplyBuoyancyImpulse(RVec3Arg inSurfacePosition, Vec3Arg inSurfaceNormal, float inBuoyancy, float inLinearDrag, float inAngularDrag, Vec3Arg inFluidVelocity, Vec3Arg inGravity, float inDeltaTime) -{ - JPH_PROFILE_FUNCTION(); - - JPH_ASSERT(IsRigidBody()); // Only implemented for rigid bodies currently - - // We follow the approach from 'Game Programming Gems 6' 2.5 Exact Buoyancy for Polyhedra - // All quantities below are in world space - - // For GetSubmergedVolume we transform the surface relative to the body position for increased precision - Mat44 rotation = Mat44::sRotation(mRotation); - Plane surface_relative_to_body = Plane::sFromPointAndNormal(inSurfacePosition - mPosition, inSurfaceNormal); - - // Calculate amount of volume that is submerged and what the center of buoyancy is - float total_volume, submerged_volume; - Vec3 relative_center_of_buoyancy; - mShape->GetSubmergedVolume(rotation, Vec3::sReplicate(1.0f), surface_relative_to_body, total_volume, submerged_volume, relative_center_of_buoyancy JPH_IF_DEBUG_RENDERER(, mPosition)); - - // If we're not submerged, there's no point in doing the rest of the calculations - if (submerged_volume > 0.0f) - { - #ifdef JPH_DEBUG_RENDERER - // Draw submerged volume properties - if (Shape::sDrawSubmergedVolumes) - { - RVec3 center_of_buoyancy = mPosition + relative_center_of_buoyancy; - DebugRenderer::sInstance->DrawMarker(center_of_buoyancy, Color::sWhite, 2.0f); - DebugRenderer::sInstance->DrawText3D(center_of_buoyancy, StringFormat("%.3f / %.3f", (double)submerged_volume, (double)total_volume)); - } - #endif // JPH_DEBUG_RENDERER - - // When buoyancy is 1 we want neutral buoyancy, this means that the density of the liquid is the same as the density of the body at that point. - // Buoyancy > 1 should make the object float, < 1 should make it sink. - float inverse_mass = mMotionProperties->GetInverseMass(); - float fluid_density = inBuoyancy / (total_volume * inverse_mass); - - // Buoyancy force = Density of Fluid * Submerged volume * Magnitude of gravity * Up direction (eq 2.5.1) - // Impulse = Force * Delta time - // We should apply this at the center of buoyancy (= center of mass of submerged volume) - Vec3 buoyancy_impulse = -fluid_density * submerged_volume * mMotionProperties->GetGravityFactor() * inGravity * inDeltaTime; - - // Calculate the velocity of the center of buoyancy relative to the fluid - Vec3 linear_velocity = mMotionProperties->GetLinearVelocity(); - Vec3 angular_velocity = mMotionProperties->GetAngularVelocity(); - Vec3 center_of_buoyancy_velocity = linear_velocity + angular_velocity.Cross(relative_center_of_buoyancy); - Vec3 relative_center_of_buoyancy_velocity = inFluidVelocity - center_of_buoyancy_velocity; - - // Here we deviate from the article, instead of eq 2.5.14 we use a quadratic drag formula: https://en.wikipedia.org/wiki/Drag_%28physics%29 - // Drag force = 0.5 * Fluid Density * (Velocity of fluid - Velocity of center of buoyancy)^2 * Linear Drag * Area Facing the Relative Fluid Velocity - // Again Impulse = Force * Delta Time - // We should apply this at the center of buoyancy (= center of mass for submerged volume with no center of mass offset) - - // Get size of local bounding box - Vec3 size = mShape->GetLocalBounds().GetSize(); - - // Determine area of the local space bounding box in the direction of the relative velocity between the fluid and the center of buoyancy - float area = 0.0f; - float relative_center_of_buoyancy_velocity_len_sq = relative_center_of_buoyancy_velocity.LengthSq(); - if (relative_center_of_buoyancy_velocity_len_sq > 1.0e-12f) - { - Vec3 local_relative_center_of_buoyancy_velocity = GetRotation().Conjugated() * relative_center_of_buoyancy_velocity; - area = local_relative_center_of_buoyancy_velocity.Abs().Dot(size.Swizzle() * size.Swizzle()) / sqrt(relative_center_of_buoyancy_velocity_len_sq); - } - - // Calculate the impulse - Vec3 drag_impulse = (0.5f * fluid_density * inLinearDrag * area * inDeltaTime) * relative_center_of_buoyancy_velocity * relative_center_of_buoyancy_velocity.Length(); - - // Clamp magnitude against current linear velocity to prevent overshoot - float linear_velocity_len_sq = linear_velocity.LengthSq(); - float drag_delta_linear_velocity_len_sq = (drag_impulse * inverse_mass).LengthSq(); - if (drag_delta_linear_velocity_len_sq > linear_velocity_len_sq) - drag_impulse *= sqrt(linear_velocity_len_sq / drag_delta_linear_velocity_len_sq); - - // Calculate the resulting delta linear velocity due to buoyancy and drag - Vec3 delta_linear_velocity = (drag_impulse + buoyancy_impulse) * inverse_mass; - mMotionProperties->AddLinearVelocityStep(delta_linear_velocity); - - // Determine average width of the body (across the three axis) - float l = (size.GetX() + size.GetY() + size.GetZ()) / 3.0f; - - // Drag torque = -Angular Drag * Mass * Submerged volume / Total volume * (Average width of body)^2 * Angular velocity (eq 2.5.15) - Vec3 drag_angular_impulse = (-inAngularDrag * submerged_volume / total_volume * inDeltaTime * Square(l) / inverse_mass) * angular_velocity; - Mat44 inv_inertia = GetInverseInertia(); - Vec3 drag_delta_angular_velocity = inv_inertia * drag_angular_impulse; - - // Clamp magnitude against the current angular velocity to prevent overshoot - float angular_velocity_len_sq = angular_velocity.LengthSq(); - float drag_delta_angular_velocity_len_sq = drag_delta_angular_velocity.LengthSq(); - if (drag_delta_angular_velocity_len_sq > angular_velocity_len_sq) - drag_delta_angular_velocity *= sqrt(angular_velocity_len_sq / drag_delta_angular_velocity_len_sq); - - // Calculate total delta angular velocity due to drag and buoyancy - Vec3 delta_angular_velocity = drag_delta_angular_velocity + inv_inertia * relative_center_of_buoyancy.Cross(buoyancy_impulse + drag_impulse); - mMotionProperties->AddAngularVelocityStep(delta_angular_velocity); - return true; - } - - return false; -} - -void Body::SaveState(StateRecorder &inStream) const -{ - // Only write properties that can change at runtime - inStream.Write(mPosition); - inStream.Write(mRotation); - - if (mMotionProperties != nullptr) - { - if (IsSoftBody()) - static_cast(mMotionProperties)->SaveState(inStream); - else - mMotionProperties->SaveState(inStream); - } -} - -void Body::RestoreState(StateRecorder &inStream) -{ - inStream.Read(mPosition); - inStream.Read(mRotation); - - if (mMotionProperties != nullptr) - { - if (IsSoftBody()) - static_cast(mMotionProperties)->RestoreState(inStream); - else - mMotionProperties->RestoreState(inStream); - - JPH_IF_ENABLE_ASSERTS(mMotionProperties->mCachedMotionType = mMotionType); - } - - // Initialize bounding box - CalculateWorldSpaceBoundsInternal(); -} - -BodyCreationSettings Body::GetBodyCreationSettings() const -{ - JPH_ASSERT(IsRigidBody()); - - BodyCreationSettings result; - - result.mPosition = GetPosition(); - result.mRotation = GetRotation(); - result.mLinearVelocity = mMotionProperties != nullptr? mMotionProperties->GetLinearVelocity() : Vec3::sZero(); - result.mAngularVelocity = mMotionProperties != nullptr? mMotionProperties->GetAngularVelocity() : Vec3::sZero(); - result.mObjectLayer = GetObjectLayer(); - result.mUserData = mUserData; - result.mCollisionGroup = GetCollisionGroup(); - result.mMotionType = GetMotionType(); - result.mAllowedDOFs = mMotionProperties != nullptr? mMotionProperties->GetAllowedDOFs() : EAllowedDOFs::All; - result.mAllowDynamicOrKinematic = mMotionProperties != nullptr; - result.mIsSensor = IsSensor(); - result.mCollideKinematicVsNonDynamic = GetCollideKinematicVsNonDynamic(); - result.mUseManifoldReduction = GetUseManifoldReduction(); - result.mApplyGyroscopicForce = GetApplyGyroscopicForce(); - result.mMotionQuality = mMotionProperties != nullptr? mMotionProperties->GetMotionQuality() : EMotionQuality::Discrete; - result.mEnhancedInternalEdgeRemoval = GetEnhancedInternalEdgeRemoval(); - result.mAllowSleeping = mMotionProperties != nullptr? GetAllowSleeping() : true; - result.mFriction = GetFriction(); - result.mRestitution = GetRestitution(); - result.mLinearDamping = mMotionProperties != nullptr? mMotionProperties->GetLinearDamping() : 0.0f; - result.mAngularDamping = mMotionProperties != nullptr? mMotionProperties->GetAngularDamping() : 0.0f; - result.mMaxLinearVelocity = mMotionProperties != nullptr? mMotionProperties->GetMaxLinearVelocity() : 0.0f; - result.mMaxAngularVelocity = mMotionProperties != nullptr? mMotionProperties->GetMaxAngularVelocity() : 0.0f; - result.mGravityFactor = mMotionProperties != nullptr? mMotionProperties->GetGravityFactor() : 1.0f; - result.mNumVelocityStepsOverride = mMotionProperties != nullptr? mMotionProperties->GetNumVelocityStepsOverride() : 0; - result.mNumPositionStepsOverride = mMotionProperties != nullptr? mMotionProperties->GetNumPositionStepsOverride() : 0; - result.mOverrideMassProperties = EOverrideMassProperties::MassAndInertiaProvided; - - // Invert inertia and mass - if (mMotionProperties != nullptr) - { - float inv_mass = mMotionProperties->GetInverseMassUnchecked(); - Mat44 inv_inertia = mMotionProperties->GetLocalSpaceInverseInertiaUnchecked(); - - // Get mass - result.mMassPropertiesOverride.mMass = inv_mass != 0.0f? 1.0f / inv_mass : FLT_MAX; - - // Get inertia - Mat44 inertia; - if (inertia.SetInversed3x3(inv_inertia)) - { - // Inertia was invertible, we can use it - result.mMassPropertiesOverride.mInertia = inertia; - } - else - { - // Prevent division by zero - Vec3 diagonal = Vec3::sMax(inv_inertia.GetDiagonal3(), Vec3::sReplicate(FLT_MIN)); - result.mMassPropertiesOverride.mInertia = Mat44::sScale(diagonal.Reciprocal()); - } - } - else - { - result.mMassPropertiesOverride.mMass = FLT_MAX; - result.mMassPropertiesOverride.mInertia = Mat44::sScale(Vec3::sReplicate(FLT_MAX)); - } - - result.SetShape(GetShape()); - - return result; -} - -SoftBodyCreationSettings Body::GetSoftBodyCreationSettings() const -{ - JPH_ASSERT(IsSoftBody()); - - SoftBodyCreationSettings result; - - result.mPosition = GetPosition(); - result.mRotation = GetRotation(); - result.mUserData = mUserData; - result.mObjectLayer = GetObjectLayer(); - result.mCollisionGroup = GetCollisionGroup(); - result.mFriction = GetFriction(); - result.mRestitution = GetRestitution(); - const SoftBodyMotionProperties *mp = static_cast(mMotionProperties); - result.mNumIterations = mp->GetNumIterations(); - result.mLinearDamping = mp->GetLinearDamping(); - result.mMaxLinearVelocity = mp->GetMaxLinearVelocity(); - result.mGravityFactor = mp->GetGravityFactor(); - result.mPressure = mp->GetPressure(); - result.mUpdatePosition = mp->GetUpdatePosition(); - result.mSettings = mp->GetSettings(); - - return result; -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/Body.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/Body.h deleted file mode 100644 index 509f55f665c..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/Body.h +++ /dev/null @@ -1,388 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -class StateRecorder; -class BodyCreationSettings; -class SoftBodyCreationSettings; - -/// A rigid body that can be simulated using the physics system -/// -/// Note that internally all properties (position, velocity etc.) are tracked relative to the center of mass of the object to simplify the simulation of the object. -/// -/// The offset between the position of the body and the center of mass position of the body is GetShape()->GetCenterOfMass(). -/// The functions that get/set the position of the body all indicate if they are relative to the center of mass or to the original position in which the shape was created. -/// -/// The linear velocity is also velocity of the center of mass, to correct for this: \f$VelocityCOM = Velocity - AngularVelocity \times ShapeCOM\f$. -class alignas(JPH_RVECTOR_ALIGNMENT) JPH_EXPORT_GCC_BUG_WORKAROUND Body : public NonCopyable -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Default constructor - Body() = default; - - /// Destructor - ~Body() { JPH_ASSERT(mMotionProperties == nullptr); } - - /// Get the id of this body - inline const BodyID & GetID() const { return mID; } - - /// Get the type of body (rigid or soft) - inline EBodyType GetBodyType() const { return mBodyType; } - - /// Check if this body is a rigid body - inline bool IsRigidBody() const { return mBodyType == EBodyType::RigidBody; } - - /// Check if this body is a soft body - inline bool IsSoftBody() const { return mBodyType == EBodyType::SoftBody; } - - /// If this body is currently actively simulating (true) or sleeping (false) - inline bool IsActive() const { return mMotionProperties != nullptr && mMotionProperties->mIndexInActiveBodies != cInactiveIndex; } - - /// Check if this body is static (not movable) - inline bool IsStatic() const { return mMotionType == EMotionType::Static; } - - /// Check if this body is kinematic (keyframed), which means that it will move according to its current velocity, but forces don't affect it - inline bool IsKinematic() const { return mMotionType == EMotionType::Kinematic; } - - /// Check if this body is dynamic, which means that it moves and forces can act on it - inline bool IsDynamic() const { return mMotionType == EMotionType::Dynamic; } - - /// Check if a body could be made kinematic or dynamic (if it was created dynamic or with mAllowDynamicOrKinematic set to true) - inline bool CanBeKinematicOrDynamic() const { return mMotionProperties != nullptr; } - - /// Change the body to a sensor. A sensor will receive collision callbacks, but will not cause any collision responses and can be used as a trigger volume. - /// The cheapest sensor (in terms of CPU usage) is a sensor with motion type Static (they can be moved around using BodyInterface::SetPosition/SetPositionAndRotation). - /// These sensors will only detect collisions with active Dynamic or Kinematic bodies. As soon as a body go to sleep, the contact point with the sensor will be lost. - /// If you make a sensor Dynamic or Kinematic and activate them, the sensor will be able to detect collisions with sleeping bodies too. An active sensor will never go to sleep automatically. - /// When you make a Dynamic or Kinematic sensor, make sure it is in an ObjectLayer that does not collide with Static bodies or other sensors to avoid extra overhead in the broad phase. - inline void SetIsSensor(bool inIsSensor) { JPH_ASSERT(IsRigidBody()); if (inIsSensor) mFlags.fetch_or(uint8(EFlags::IsSensor), memory_order_relaxed); else mFlags.fetch_and(uint8(~uint8(EFlags::IsSensor)), memory_order_relaxed); } - - /// Check if this body is a sensor. - inline bool IsSensor() const { return (mFlags.load(memory_order_relaxed) & uint8(EFlags::IsSensor)) != 0; } - - /// If kinematic objects can generate contact points against other kinematic or static objects. - /// Note that turning this on can be CPU intensive as much more collision detection work will be done without any effect on the simulation (kinematic objects are not affected by other kinematic/static objects). - /// This can be used to make sensors detect static objects. Note that the sensor must be kinematic and active for it to detect static objects. - inline void SetCollideKinematicVsNonDynamic(bool inCollide) { JPH_ASSERT(IsRigidBody()); if (inCollide) mFlags.fetch_or(uint8(EFlags::CollideKinematicVsNonDynamic), memory_order_relaxed); else mFlags.fetch_and(uint8(~uint8(EFlags::CollideKinematicVsNonDynamic)), memory_order_relaxed); } - - /// Check if kinematic objects can generate contact points against other kinematic or static objects. - inline bool GetCollideKinematicVsNonDynamic() const { return (mFlags.load(memory_order_relaxed) & uint8(EFlags::CollideKinematicVsNonDynamic)) != 0; } - - /// If PhysicsSettings::mUseManifoldReduction is true, this allows turning off manifold reduction for this specific body. - /// Manifold reduction by default will combine contacts with similar normals that come from different SubShapeIDs (e.g. different triangles in a mesh shape or different compound shapes). - /// If the application requires tracking exactly which SubShapeIDs are in contact, you can turn off manifold reduction. Note that this comes at a performance cost. - /// Consider using BodyInterface::SetUseManifoldReduction if the body could already be in contact with other bodies to ensure that the contact cache is invalidated and you get the correct contact callbacks. - inline void SetUseManifoldReduction(bool inUseReduction) { JPH_ASSERT(IsRigidBody()); if (inUseReduction) mFlags.fetch_or(uint8(EFlags::UseManifoldReduction), memory_order_relaxed); else mFlags.fetch_and(uint8(~uint8(EFlags::UseManifoldReduction)), memory_order_relaxed); } - - /// Check if this body can use manifold reduction. - inline bool GetUseManifoldReduction() const { return (mFlags.load(memory_order_relaxed) & uint8(EFlags::UseManifoldReduction)) != 0; } - - /// Checks if the combination of this body and inBody2 should use manifold reduction - inline bool GetUseManifoldReductionWithBody(const Body &inBody2) const { return ((mFlags.load(memory_order_relaxed) & inBody2.mFlags.load(memory_order_relaxed)) & uint8(EFlags::UseManifoldReduction)) != 0; } - - /// Set to indicate that the gyroscopic force should be applied to this body (aka Dzhanibekov effect, see https://en.wikipedia.org/wiki/Tennis_racket_theorem) - inline void SetApplyGyroscopicForce(bool inApply) { JPH_ASSERT(IsRigidBody()); if (inApply) mFlags.fetch_or(uint8(EFlags::ApplyGyroscopicForce), memory_order_relaxed); else mFlags.fetch_and(uint8(~uint8(EFlags::ApplyGyroscopicForce)), memory_order_relaxed); } - - /// Check if the gyroscopic force is being applied for this body - inline bool GetApplyGyroscopicForce() const { return (mFlags.load(memory_order_relaxed) & uint8(EFlags::ApplyGyroscopicForce)) != 0; } - - /// Set to indicate that extra effort should be made to try to remove ghost contacts (collisions with internal edges of a mesh). This is more expensive but makes bodies move smoother over a mesh with convex edges. - inline void SetEnhancedInternalEdgeRemoval(bool inApply) { JPH_ASSERT(IsRigidBody()); if (inApply) mFlags.fetch_or(uint8(EFlags::EnhancedInternalEdgeRemoval), memory_order_relaxed); else mFlags.fetch_and(uint8(~uint8(EFlags::EnhancedInternalEdgeRemoval)), memory_order_relaxed); } - - /// Check if enhanced internal edge removal is turned on - inline bool GetEnhancedInternalEdgeRemoval() const { return (mFlags.load(memory_order_relaxed) & uint8(EFlags::EnhancedInternalEdgeRemoval)) != 0; } - - /// Checks if the combination of this body and inBody2 should use enhanced internal edge removal - inline bool GetEnhancedInternalEdgeRemovalWithBody(const Body &inBody2) const { return ((mFlags.load(memory_order_relaxed) | inBody2.mFlags.load(memory_order_relaxed)) & uint8(EFlags::EnhancedInternalEdgeRemoval)) != 0; } - - /// Get the bodies motion type. - inline EMotionType GetMotionType() const { return mMotionType; } - - /// Set the motion type of this body. Consider using BodyInterface::SetMotionType instead of this function if the body may be active or if it needs to be activated. - void SetMotionType(EMotionType inMotionType); - - /// Get broadphase layer, this determines in which broad phase sub-tree the object is placed - inline BroadPhaseLayer GetBroadPhaseLayer() const { return mBroadPhaseLayer; } - - /// Get object layer, this determines which other objects it collides with - inline ObjectLayer GetObjectLayer() const { return mObjectLayer; } - - /// Collision group and sub-group ID, determines which other objects it collides with - const CollisionGroup & GetCollisionGroup() const { return mCollisionGroup; } - CollisionGroup & GetCollisionGroup() { return mCollisionGroup; } - void SetCollisionGroup(const CollisionGroup &inGroup) { mCollisionGroup = inGroup; } - - /// If this body can go to sleep. Note that disabling sleeping on a sleeping object will not wake it up. - bool GetAllowSleeping() const { return mMotionProperties->mAllowSleeping; } - void SetAllowSleeping(bool inAllow); - - /// Resets the sleep timer. This does not wake up the body if it is sleeping, but allows resetting the system that detects when a body is sleeping. - inline void ResetSleepTimer(); - - /// Friction (dimensionless number, usually between 0 and 1, 0 = no friction, 1 = friction force equals force that presses the two bodies together). Note that bodies can have negative friction but the combined friction (see PhysicsSystem::SetCombineFriction) should never go below zero. - inline float GetFriction() const { return mFriction; } - void SetFriction(float inFriction) { mFriction = inFriction; } - - /// Restitution (dimensionless number, usually between 0 and 1, 0 = completely inelastic collision response, 1 = completely elastic collision response). Note that bodies can have negative restitution but the combined restitution (see PhysicsSystem::SetCombineRestitution) should never go below zero. - inline float GetRestitution() const { return mRestitution; } - void SetRestitution(float inRestitution) { mRestitution = inRestitution; } - - /// Get world space linear velocity of the center of mass (unit: m/s) - inline Vec3 GetLinearVelocity() const { return !IsStatic()? mMotionProperties->GetLinearVelocity() : Vec3::sZero(); } - - /// Set world space linear velocity of the center of mass (unit: m/s) - void SetLinearVelocity(Vec3Arg inLinearVelocity) { JPH_ASSERT(!IsStatic()); mMotionProperties->SetLinearVelocity(inLinearVelocity); } - - /// Set world space linear velocity of the center of mass, will make sure the value is clamped against the maximum linear velocity - void SetLinearVelocityClamped(Vec3Arg inLinearVelocity) { JPH_ASSERT(!IsStatic()); mMotionProperties->SetLinearVelocityClamped(inLinearVelocity); } - - /// Get world space angular velocity of the center of mass (unit: rad/s) - inline Vec3 GetAngularVelocity() const { return !IsStatic()? mMotionProperties->GetAngularVelocity() : Vec3::sZero(); } - - /// Set world space angular velocity of the center of mass (unit: rad/s) - void SetAngularVelocity(Vec3Arg inAngularVelocity) { JPH_ASSERT(!IsStatic()); mMotionProperties->SetAngularVelocity(inAngularVelocity); } - - /// Set world space angular velocity of the center of mass, will make sure the value is clamped against the maximum angular velocity - void SetAngularVelocityClamped(Vec3Arg inAngularVelocity) { JPH_ASSERT(!IsStatic()); mMotionProperties->SetAngularVelocityClamped(inAngularVelocity); } - - /// Velocity of point inPoint (in center of mass space, e.g. on the surface of the body) of the body (unit: m/s) - inline Vec3 GetPointVelocityCOM(Vec3Arg inPointRelativeToCOM) const { return !IsStatic()? mMotionProperties->GetPointVelocityCOM(inPointRelativeToCOM) : Vec3::sZero(); } - - /// Velocity of point inPoint (in world space, e.g. on the surface of the body) of the body (unit: m/s) - inline Vec3 GetPointVelocity(RVec3Arg inPoint) const { JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sPositionAccess, BodyAccess::EAccess::Read)); return GetPointVelocityCOM(Vec3(inPoint - mPosition)); } - - /// Add force (unit: N) at center of mass for the next time step, will be reset after the next call to PhysicsSystem::Update - inline void AddForce(Vec3Arg inForce) { JPH_ASSERT(IsDynamic()); (Vec3::sLoadFloat3Unsafe(mMotionProperties->mForce) + inForce).StoreFloat3(&mMotionProperties->mForce); } - - /// Add force (unit: N) at inPosition for the next time step, will be reset after the next call to PhysicsSystem::Update - inline void AddForce(Vec3Arg inForce, RVec3Arg inPosition); - - /// Add torque (unit: N m) for the next time step, will be reset after the next call to PhysicsSystem::Update - inline void AddTorque(Vec3Arg inTorque) { JPH_ASSERT(IsDynamic()); (Vec3::sLoadFloat3Unsafe(mMotionProperties->mTorque) + inTorque).StoreFloat3(&mMotionProperties->mTorque); } - - // Get the total amount of force applied to the center of mass this time step (through AddForce calls). Note that it will reset to zero after PhysicsSystem::Update. - inline Vec3 GetAccumulatedForce() const { JPH_ASSERT(IsDynamic()); return mMotionProperties->GetAccumulatedForce(); } - - // Get the total amount of torque applied to the center of mass this time step (through AddForce/AddTorque calls). Note that it will reset to zero after PhysicsSystem::Update. - inline Vec3 GetAccumulatedTorque() const { JPH_ASSERT(IsDynamic()); return mMotionProperties->GetAccumulatedTorque(); } - - // Reset the total accumulated force, not that this will be done automatically after every time step. - JPH_INLINE void ResetForce() { JPH_ASSERT(IsDynamic()); return mMotionProperties->ResetForce(); } - - // Reset the total accumulated torque, not that this will be done automatically after every time step. - JPH_INLINE void ResetTorque() { JPH_ASSERT(IsDynamic()); return mMotionProperties->ResetTorque(); } - - // Reset the current velocity and accumulated force and torque. - JPH_INLINE void ResetMotion() { JPH_ASSERT(!IsStatic()); return mMotionProperties->ResetMotion(); } - - /// Get inverse inertia tensor in world space - inline Mat44 GetInverseInertia() const; - - /// Add impulse to center of mass (unit: kg m/s) - inline void AddImpulse(Vec3Arg inImpulse); - - /// Add impulse to point in world space (unit: kg m/s) - inline void AddImpulse(Vec3Arg inImpulse, RVec3Arg inPosition); - - /// Add angular impulse in world space (unit: N m s) - inline void AddAngularImpulse(Vec3Arg inAngularImpulse); - - /// Set velocity of body such that it will be positioned at inTargetPosition/Rotation in inDeltaTime seconds. - void MoveKinematic(RVec3Arg inTargetPosition, QuatArg inTargetRotation, float inDeltaTime); - - /// Applies an impulse to the body that simulates fluid buoyancy and drag - /// @param inSurfacePosition Position of the fluid surface in world space - /// @param inSurfaceNormal Normal of the fluid surface (should point up) - /// @param inBuoyancy The buoyancy factor for the body. 1 = neutral body, < 1 sinks, > 1 floats. Note that we don't use the fluid density since it is harder to configure than a simple number between [0, 2] - /// @param inLinearDrag Linear drag factor that slows down the body when in the fluid (approx. 0.5) - /// @param inAngularDrag Angular drag factor that slows down rotation when the body is in the fluid (approx. 0.01) - /// @param inFluidVelocity The average velocity of the fluid (in m/s) in which the body resides - /// @param inGravity The gravity vector (pointing down) - /// @param inDeltaTime Delta time of the next simulation step (in s) - /// @return true if an impulse was applied, false if the body was not in the fluid - bool ApplyBuoyancyImpulse(RVec3Arg inSurfacePosition, Vec3Arg inSurfaceNormal, float inBuoyancy, float inLinearDrag, float inAngularDrag, Vec3Arg inFluidVelocity, Vec3Arg inGravity, float inDeltaTime); - - /// Check if this body has been added to the physics system - inline bool IsInBroadPhase() const { return (mFlags.load(memory_order_relaxed) & uint8(EFlags::IsInBroadPhase)) != 0; } - - /// Check if this body has been changed in such a way that the collision cache should be considered invalid for any body interacting with this body - inline bool IsCollisionCacheInvalid() const { return (mFlags.load(memory_order_relaxed) & uint8(EFlags::InvalidateContactCache)) != 0; } - - /// Get the shape of this body - inline const Shape * GetShape() const { return mShape; } - - /// World space position of the body - inline RVec3 GetPosition() const { JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sPositionAccess, BodyAccess::EAccess::Read)); return mPosition - mRotation * mShape->GetCenterOfMass(); } - - /// World space rotation of the body - inline Quat GetRotation() const { JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sPositionAccess, BodyAccess::EAccess::Read)); return mRotation; } - - /// Calculates the transform of this body - inline RMat44 GetWorldTransform() const; - - /// Gets the world space position of this body's center of mass - inline RVec3 GetCenterOfMassPosition() const { JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sPositionAccess, BodyAccess::EAccess::Read)); return mPosition; } - - /// Calculates the transform for this body's center of mass - inline RMat44 GetCenterOfMassTransform() const; - - /// Calculates the inverse of the transform for this body's center of mass - inline RMat44 GetInverseCenterOfMassTransform() const; - - /// Get world space bounding box - inline const AABox & GetWorldSpaceBounds() const { return mBounds; } - - /// Access to the motion properties - const MotionProperties *GetMotionProperties() const { JPH_ASSERT(!IsStatic()); return mMotionProperties; } - MotionProperties * GetMotionProperties() { JPH_ASSERT(!IsStatic()); return mMotionProperties; } - - /// Access to the motion properties (version that does not check if the object is kinematic or dynamic) - const MotionProperties *GetMotionPropertiesUnchecked() const { return mMotionProperties; } - MotionProperties * GetMotionPropertiesUnchecked() { return mMotionProperties; } - - /// Access to the user data, can be used for anything by the application - uint64 GetUserData() const { return mUserData; } - void SetUserData(uint64 inUserData) { mUserData = inUserData; } - - /// Get surface normal of a particular sub shape and its world space surface position on this body - inline Vec3 GetWorldSpaceSurfaceNormal(const SubShapeID &inSubShapeID, RVec3Arg inPosition) const; - - /// Get the transformed shape of this body, which can be used to do collision detection outside of a body lock - inline TransformedShape GetTransformedShape() const { JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sPositionAccess, BodyAccess::EAccess::Read)); return TransformedShape(mPosition, mRotation, mShape, mID); } - - /// Debug function to convert a body back to a body creation settings object to be able to save/recreate the body later - BodyCreationSettings GetBodyCreationSettings() const; - - /// Debug function to convert a soft body back to a soft body creation settings object to be able to save/recreate the body later - SoftBodyCreationSettings GetSoftBodyCreationSettings() const; - - /// A dummy body that can be used by constraints to attach a constraint to the world instead of another body - static Body sFixedToWorld; - - ///@name THESE FUNCTIONS ARE FOR INTERNAL USE ONLY AND SHOULD NOT BE CALLED BY THE APPLICATION - ///@{ - - /// Helper function for BroadPhase::FindCollidingPairs that returns true when two bodies can collide - /// It assumes that body 1 is dynamic and active and guarantees that it body 1 collides with body 2 that body 2 will not collide with body 1 in order to avoid finding duplicate collision pairs - static inline bool sFindCollidingPairsCanCollide(const Body &inBody1, const Body &inBody2); - - /// Update position using an Euler step (used during position integrate & constraint solving) - inline void AddPositionStep(Vec3Arg inLinearVelocityTimesDeltaTime) { JPH_ASSERT(IsRigidBody()); JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sPositionAccess, BodyAccess::EAccess::ReadWrite)); mPosition += mMotionProperties->LockTranslation(inLinearVelocityTimesDeltaTime); JPH_ASSERT(!mPosition.IsNaN()); } - inline void SubPositionStep(Vec3Arg inLinearVelocityTimesDeltaTime) { JPH_ASSERT(IsRigidBody()); JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sPositionAccess, BodyAccess::EAccess::ReadWrite)); mPosition -= mMotionProperties->LockTranslation(inLinearVelocityTimesDeltaTime); JPH_ASSERT(!mPosition.IsNaN()); } - - /// Update rotation using an Euler step (using during position integrate & constraint solving) - inline void AddRotationStep(Vec3Arg inAngularVelocityTimesDeltaTime); - inline void SubRotationStep(Vec3Arg inAngularVelocityTimesDeltaTime); - - /// Flag if body is in the broadphase (should only be called by the BroadPhase) - inline void SetInBroadPhaseInternal(bool inInBroadPhase) { if (inInBroadPhase) mFlags.fetch_or(uint8(EFlags::IsInBroadPhase), memory_order_relaxed); else mFlags.fetch_and(uint8(~uint8(EFlags::IsInBroadPhase)), memory_order_relaxed); } - - /// Invalidate the contact cache (should only be called by the BodyManager), will be reset the next simulation step. Returns true if the contact cache was still valid. - inline bool InvalidateContactCacheInternal() { return (mFlags.fetch_or(uint8(EFlags::InvalidateContactCache), memory_order_relaxed) & uint8(EFlags::InvalidateContactCache)) == 0; } - - /// Reset the collision cache invalid flag (should only be called by the BodyManager). - inline void ValidateContactCacheInternal() { JPH_IF_ENABLE_ASSERTS(uint8 old_val = ) mFlags.fetch_and(uint8(~uint8(EFlags::InvalidateContactCache)), memory_order_relaxed); JPH_ASSERT((old_val & uint8(EFlags::InvalidateContactCache)) != 0); } - - /// Updates world space bounding box (should only be called by the PhysicsSystem) - void CalculateWorldSpaceBoundsInternal(); - - /// Function to update body's position (should only be called by the BodyInterface since it also requires updating the broadphase) - void SetPositionAndRotationInternal(RVec3Arg inPosition, QuatArg inRotation, bool inResetSleepTimer = true); - - /// Updates the center of mass and optionally mass properties after shifting the center of mass or changes to the shape (should only be called by the BodyInterface since it also requires updating the broadphase) - /// @param inPreviousCenterOfMass Center of mass of the shape before the alterations - /// @param inUpdateMassProperties When true, the mass and inertia tensor is recalculated - void UpdateCenterOfMassInternal(Vec3Arg inPreviousCenterOfMass, bool inUpdateMassProperties); - - /// Function to update a body's shape (should only be called by the BodyInterface since it also requires updating the broadphase) - /// @param inShape The new shape for this body - /// @param inUpdateMassProperties When true, the mass and inertia tensor is recalculated - void SetShapeInternal(const Shape *inShape, bool inUpdateMassProperties); - - /// Access to the index in the BodyManager::mActiveBodies list - uint32 GetIndexInActiveBodiesInternal() const { return mMotionProperties != nullptr? mMotionProperties->mIndexInActiveBodies : cInactiveIndex; } - - /// Update eligibility for sleeping - ECanSleep UpdateSleepStateInternal(float inDeltaTime, float inMaxMovement, float inTimeBeforeSleep); - - /// Saving state for replay - void SaveState(StateRecorder &inStream) const; - - /// Restoring state for replay - void RestoreState(StateRecorder &inStream); - - ///@} - - static constexpr uint32 cInactiveIndex = MotionProperties::cInactiveIndex; ///< Constant indicating that body is not active - -private: - friend class BodyManager; - - explicit Body(bool); ///< Alternative constructor that initializes all members - - inline void GetSleepTestPoints(RVec3 *outPoints) const; ///< Determine points to test for checking if body is sleeping: COM, COM + largest bounding box axis, COM + second largest bounding box axis - - enum class EFlags : uint8 - { - IsSensor = 1 << 0, ///< If this object is a sensor. A sensor will receive collision callbacks, but will not cause any collision responses and can be used as a trigger volume. - CollideKinematicVsNonDynamic = 1 << 1, ///< If kinematic objects can generate contact points against other kinematic or static objects. - IsInBroadPhase = 1 << 2, ///< Set this bit to indicate that the body is in the broadphase - InvalidateContactCache = 1 << 3, ///< Set this bit to indicate that all collision caches for this body are invalid, will be reset the next simulation step. - UseManifoldReduction = 1 << 4, ///< Set this bit to indicate that this body can use manifold reduction (if PhysicsSettings::mUseManifoldReduction is true) - ApplyGyroscopicForce = 1 << 5, ///< Set this bit to indicate that the gyroscopic force should be applied to this body (aka Dzhanibekov effect, see https://en.wikipedia.org/wiki/Tennis_racket_theorem) - EnhancedInternalEdgeRemoval = 1 << 6, ///< Set this bit to indicate that enhanced internal edge removal should be used for this body (see BodyCreationSettings::mEnhancedInternalEdgeRemoval) - }; - - // 16 byte aligned - RVec3 mPosition; ///< World space position of center of mass - Quat mRotation; ///< World space rotation of center of mass - AABox mBounds; ///< World space bounding box of the body - - // 8 byte aligned - RefConst mShape; ///< Shape representing the volume of this body - MotionProperties * mMotionProperties = nullptr; ///< If this is a keyframed or dynamic object, this object holds all information about the movement - uint64 mUserData = 0; ///< User data, can be used for anything by the application - CollisionGroup mCollisionGroup; ///< The collision group this body belongs to (determines if two objects can collide) - - // 4 byte aligned - float mFriction; ///< Friction of the body (dimensionless number, usually between 0 and 1, 0 = no friction, 1 = friction force equals force that presses the two bodies together). Note that bodies can have negative friction but the combined friction (see PhysicsSystem::SetCombineFriction) should never go below zero. - float mRestitution; ///< Restitution of body (dimensionless number, usually between 0 and 1, 0 = completely inelastic collision response, 1 = completely elastic collision response). Note that bodies can have negative restitution but the combined restitution (see PhysicsSystem::SetCombineRestitution) should never go below zero. - BodyID mID; ///< ID of the body (index in the bodies array) - - // 2 or 4 bytes aligned - ObjectLayer mObjectLayer; ///< The collision layer this body belongs to (determines if two objects can collide) - - // 1 byte aligned - EBodyType mBodyType; ///< Type of body (rigid or soft) - BroadPhaseLayer mBroadPhaseLayer; ///< The broad phase layer this body belongs to - EMotionType mMotionType; ///< Type of motion (static, dynamic or kinematic) - atomic mFlags = 0; ///< See EFlags for possible flags - - // 122 bytes up to here (64-bit mode, single precision, 16-bit ObjectLayer) -}; - -static_assert(JPH_CPU_ADDRESS_BITS != 64 || sizeof(Body) == JPH_IF_SINGLE_PRECISION_ELSE(128, 160), "Body size is incorrect"); -static_assert(alignof(Body) == JPH_RVECTOR_ALIGNMENT, "Body should properly align"); - -JPH_NAMESPACE_END - -#include "Body.inl" diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/Body.inl b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/Body.inl deleted file mode 100644 index 51bc64878c3..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/Body.inl +++ /dev/null @@ -1,197 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -JPH_NAMESPACE_BEGIN - -RMat44 Body::GetWorldTransform() const -{ - JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sPositionAccess, BodyAccess::EAccess::Read)); - - return RMat44::sRotationTranslation(mRotation, mPosition).PreTranslated(-mShape->GetCenterOfMass()); -} - -RMat44 Body::GetCenterOfMassTransform() const -{ - JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sPositionAccess, BodyAccess::EAccess::Read)); - - return RMat44::sRotationTranslation(mRotation, mPosition); -} - -RMat44 Body::GetInverseCenterOfMassTransform() const -{ - JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sPositionAccess, BodyAccess::EAccess::Read)); - - return RMat44::sInverseRotationTranslation(mRotation, mPosition); -} - -inline bool Body::sFindCollidingPairsCanCollide(const Body &inBody1, const Body &inBody2) -{ - // First body should never be a soft body - JPH_ASSERT(!inBody1.IsSoftBody()); - - // One of these conditions must be true - // - We always allow detecting collisions between kinematic and non-dynamic bodies - // - One of the bodies must be dynamic to collide - // - A kinematic object can collide with a sensor - if (!inBody1.GetCollideKinematicVsNonDynamic() - && !inBody2.GetCollideKinematicVsNonDynamic() - && (!inBody1.IsDynamic() && !inBody2.IsDynamic()) - && !(inBody1.IsKinematic() && inBody2.IsSensor()) - && !(inBody2.IsKinematic() && inBody1.IsSensor())) - return false; - - // Check that body 1 is active - uint32 body1_index_in_active_bodies = inBody1.GetIndexInActiveBodiesInternal(); - JPH_ASSERT(!inBody1.IsStatic() && body1_index_in_active_bodies != Body::cInactiveIndex, "This function assumes that Body 1 is active"); - - // If the pair A, B collides we need to ensure that the pair B, A does not collide or else we will handle the collision twice. - // If A is the same body as B we don't want to collide (1) - // If A is dynamic / kinematic and B is static we should collide (2) - // If A is dynamic / kinematic and B is dynamic / kinematic we should only collide if - // - A is active and B is not active (3) - // - A is active and B will become active during this simulation step (4) - // - A is active and B is active, we require a condition that makes A, B collide and B, A not (5) - // - // In order to implement this we use the index in the active body list and make use of the fact that - // a body not in the active list has Body.Index = 0xffffffff which is the highest possible value for an uint32. - // - // Because we know that A is active we know that A.Index != 0xffffffff: - // (1) Because A.Index != 0xffffffff, if A.Index = B.Index then A = B, so to collide A.Index != B.Index - // (2) A.Index != 0xffffffff, B.Index = 0xffffffff (because it's static and cannot be in the active list), so to collide A.Index != B.Index - // (3) A.Index != 0xffffffff, B.Index = 0xffffffff (because it's not yet active), so to collide A.Index != B.Index - // (4) A.Index != 0xffffffff, B.Index = 0xffffffff currently. But it can activate during the Broad/NarrowPhase step at which point it - // will be added to the end of the active list which will make B.Index > A.Index (this holds only true when we don't deactivate - // bodies during the Broad/NarrowPhase step), so to collide A.Index < B.Index. - // (5) As tie breaker we can use the same condition A.Index < B.Index to collide, this means that if A, B collides then B, A won't - static_assert(Body::cInactiveIndex == 0xffffffff, "The algorithm below uses this value"); - if (!inBody2.IsSoftBody() && body1_index_in_active_bodies >= inBody2.GetIndexInActiveBodiesInternal()) - return false; - JPH_ASSERT(inBody1.GetID() != inBody2.GetID(), "Read the comment above, A and B are the same body which should not be possible!"); - - // Check collision group filter - if (!inBody1.GetCollisionGroup().CanCollide(inBody2.GetCollisionGroup())) - return false; - - return true; -} - -void Body::AddRotationStep(Vec3Arg inAngularVelocityTimesDeltaTime) -{ - JPH_ASSERT(IsRigidBody()); - JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sPositionAccess, BodyAccess::EAccess::ReadWrite)); - - // This used to use the equation: d/dt R(t) = 1/2 * w(t) * R(t) so that R(t + dt) = R(t) + 1/2 * w(t) * R(t) * dt - // See: Appendix B of An Introduction to Physically Based Modeling: Rigid Body Simulation II-Nonpenetration Constraints - // URL: https://www.cs.cmu.edu/~baraff/sigcourse/notesd2.pdf - // But this is a first order approximation and does not work well for kinematic ragdolls that are driven to a new - // pose if the poses differ enough. So now we split w(t) * dt into an axis and angle part and create a quaternion with it. - // Note that the resulting quaternion is normalized since otherwise numerical drift will eventually make the rotation non-normalized. - float len = inAngularVelocityTimesDeltaTime.Length(); - if (len > 1.0e-6f) - { - mRotation = (Quat::sRotation(inAngularVelocityTimesDeltaTime / len, len) * mRotation).Normalized(); - JPH_ASSERT(!mRotation.IsNaN()); - } -} - -void Body::SubRotationStep(Vec3Arg inAngularVelocityTimesDeltaTime) -{ - JPH_ASSERT(IsRigidBody()); - JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sPositionAccess, BodyAccess::EAccess::ReadWrite)); - - // See comment at Body::AddRotationStep - float len = inAngularVelocityTimesDeltaTime.Length(); - if (len > 1.0e-6f) - { - mRotation = (Quat::sRotation(inAngularVelocityTimesDeltaTime / len, -len) * mRotation).Normalized(); - JPH_ASSERT(!mRotation.IsNaN()); - } -} - -Vec3 Body::GetWorldSpaceSurfaceNormal(const SubShapeID &inSubShapeID, RVec3Arg inPosition) const -{ - RMat44 inv_com = GetInverseCenterOfMassTransform(); - return inv_com.Multiply3x3Transposed(mShape->GetSurfaceNormal(inSubShapeID, Vec3(inv_com * inPosition))).Normalized(); -} - -Mat44 Body::GetInverseInertia() const -{ - JPH_ASSERT(IsDynamic()); - - return GetMotionProperties()->GetInverseInertiaForRotation(Mat44::sRotation(mRotation)); -} - -void Body::AddForce(Vec3Arg inForce, RVec3Arg inPosition) -{ - AddForce(inForce); - AddTorque(Vec3(inPosition - mPosition).Cross(inForce)); -} - -void Body::AddImpulse(Vec3Arg inImpulse) -{ - JPH_ASSERT(IsDynamic()); - - SetLinearVelocityClamped(mMotionProperties->GetLinearVelocity() + inImpulse * mMotionProperties->GetInverseMass()); -} - -void Body::AddImpulse(Vec3Arg inImpulse, RVec3Arg inPosition) -{ - JPH_ASSERT(IsDynamic()); - - SetLinearVelocityClamped(mMotionProperties->GetLinearVelocity() + inImpulse * mMotionProperties->GetInverseMass()); - - SetAngularVelocityClamped(mMotionProperties->GetAngularVelocity() + mMotionProperties->MultiplyWorldSpaceInverseInertiaByVector(mRotation, Vec3(inPosition - mPosition).Cross(inImpulse))); -} - -void Body::AddAngularImpulse(Vec3Arg inAngularImpulse) -{ - JPH_ASSERT(IsDynamic()); - - SetAngularVelocityClamped(mMotionProperties->GetAngularVelocity() + mMotionProperties->MultiplyWorldSpaceInverseInertiaByVector(mRotation, inAngularImpulse)); -} - -void Body::GetSleepTestPoints(RVec3 *outPoints) const -{ - JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sPositionAccess, BodyAccess::EAccess::Read)); - - // Center of mass is the first position - outPoints[0] = mPosition; - - // The second and third position are on the largest axis of the bounding box - Vec3 extent = mShape->GetLocalBounds().GetExtent(); - int lowest_component = extent.GetLowestComponentIndex(); - Mat44 rotation = Mat44::sRotation(mRotation); - switch (lowest_component) - { - case 0: - outPoints[1] = mPosition + extent.GetY() * rotation.GetColumn3(1); - outPoints[2] = mPosition + extent.GetZ() * rotation.GetColumn3(2); - break; - - case 1: - outPoints[1] = mPosition + extent.GetX() * rotation.GetColumn3(0); - outPoints[2] = mPosition + extent.GetZ() * rotation.GetColumn3(2); - break; - - case 2: - outPoints[1] = mPosition + extent.GetX() * rotation.GetColumn3(0); - outPoints[2] = mPosition + extent.GetY() * rotation.GetColumn3(1); - break; - - default: - JPH_ASSERT(false); - break; - } -} - -void Body::ResetSleepTimer() -{ - RVec3 points[3]; - GetSleepTestPoints(points); - mMotionProperties->ResetSleepTestSpheres(points); -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyAccess.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyAccess.cpp deleted file mode 100644 index b343e717c5c..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyAccess.cpp +++ /dev/null @@ -1,18 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include - -#ifdef JPH_ENABLE_ASSERTS - -JPH_NAMESPACE_BEGIN - -thread_local BodyAccess::EAccess BodyAccess::sVelocityAccess = BodyAccess::EAccess::ReadWrite; -thread_local BodyAccess::EAccess BodyAccess::sPositionAccess = BodyAccess::EAccess::ReadWrite; - -JPH_NAMESPACE_END - -#endif diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyAccess.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyAccess.h deleted file mode 100644 index 426460aac5e..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyAccess.h +++ /dev/null @@ -1,55 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#ifdef JPH_ENABLE_ASSERTS - -JPH_NAMESPACE_BEGIN - -class BodyAccess -{ -public: - /// Access rules, used to detect race conditions during simulation - enum class EAccess : uint8 - { - None = 0, - Read = 1, - ReadWrite = 3, - }; - - /// Grant a scope specific access rights on the current thread - class Grant - { - public: - inline Grant(EAccess inVelocity, EAccess inPosition) - { - JPH_ASSERT(sVelocityAccess == EAccess::ReadWrite); - JPH_ASSERT(sPositionAccess == EAccess::ReadWrite); - - sVelocityAccess = inVelocity; - sPositionAccess = inPosition; - } - - inline ~Grant() - { - sVelocityAccess = EAccess::ReadWrite; - sPositionAccess = EAccess::ReadWrite; - } - }; - - /// Check if we have permission - static bool sCheckRights(EAccess inRights, EAccess inDesiredRights) - { - return (uint8(inRights) & uint8(inDesiredRights)) == uint8(inDesiredRights); - } - - // Various permissions that can be granted - static thread_local EAccess sVelocityAccess; - static thread_local EAccess sPositionAccess; -}; - -JPH_NAMESPACE_END - -#endif // JPH_ENABLE_ASSERTS diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyActivationListener.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyActivationListener.h deleted file mode 100644 index 2c8808a1505..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyActivationListener.h +++ /dev/null @@ -1,28 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -JPH_NAMESPACE_BEGIN - -class BodyID; - -/// A listener class that receives events when a body activates or deactivates. -/// It can be registered with the BodyManager (or PhysicsSystem). -class BodyActivationListener -{ -public: - /// Ensure virtual destructor - virtual ~BodyActivationListener() = default; - - /// Called whenever a body activates, note this can be called from any thread so make sure your code is thread safe. - /// At the time of the callback the body inBodyID will be locked and no bodies can be written/activated/deactivated from the callback. - virtual void OnBodyActivated(const BodyID &inBodyID, uint64 inBodyUserData) = 0; - - /// Called whenever a body deactivates, note this can be called from any thread so make sure your code is thread safe. - /// At the time of the callback the body inBodyID will be locked and no bodies can be written/activated/deactivated from the callback. - virtual void OnBodyDeactivated(const BodyID &inBodyID, uint64 inBodyUserData) = 0; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyCreationSettings.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyCreationSettings.cpp deleted file mode 100644 index 9b6d3f92c41..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyCreationSettings.cpp +++ /dev/null @@ -1,234 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(BodyCreationSettings) -{ - JPH_ADD_ATTRIBUTE(BodyCreationSettings, mPosition) - JPH_ADD_ATTRIBUTE(BodyCreationSettings, mRotation) - JPH_ADD_ATTRIBUTE(BodyCreationSettings, mLinearVelocity) - JPH_ADD_ATTRIBUTE(BodyCreationSettings, mAngularVelocity) - JPH_ADD_ATTRIBUTE(BodyCreationSettings, mUserData) - JPH_ADD_ATTRIBUTE(BodyCreationSettings, mShape) - JPH_ADD_ATTRIBUTE(BodyCreationSettings, mCollisionGroup) - JPH_ADD_ENUM_ATTRIBUTE(BodyCreationSettings, mObjectLayer) - JPH_ADD_ENUM_ATTRIBUTE(BodyCreationSettings, mMotionType) - JPH_ADD_ENUM_ATTRIBUTE(BodyCreationSettings, mAllowedDOFs) - JPH_ADD_ATTRIBUTE(BodyCreationSettings, mAllowDynamicOrKinematic) - JPH_ADD_ATTRIBUTE(BodyCreationSettings, mIsSensor) - JPH_ADD_ATTRIBUTE_WITH_ALIAS(BodyCreationSettings, mCollideKinematicVsNonDynamic, "mSensorDetectsStatic") // This is the old name to keep backwards compatibility - JPH_ADD_ATTRIBUTE(BodyCreationSettings, mUseManifoldReduction) - JPH_ADD_ATTRIBUTE(BodyCreationSettings, mApplyGyroscopicForce) - JPH_ADD_ENUM_ATTRIBUTE(BodyCreationSettings, mMotionQuality) - JPH_ADD_ATTRIBUTE(BodyCreationSettings, mEnhancedInternalEdgeRemoval) - JPH_ADD_ATTRIBUTE(BodyCreationSettings, mAllowSleeping) - JPH_ADD_ATTRIBUTE(BodyCreationSettings, mFriction) - JPH_ADD_ATTRIBUTE(BodyCreationSettings, mRestitution) - JPH_ADD_ATTRIBUTE(BodyCreationSettings, mLinearDamping) - JPH_ADD_ATTRIBUTE(BodyCreationSettings, mAngularDamping) - JPH_ADD_ATTRIBUTE(BodyCreationSettings, mMaxLinearVelocity) - JPH_ADD_ATTRIBUTE(BodyCreationSettings, mMaxAngularVelocity) - JPH_ADD_ATTRIBUTE(BodyCreationSettings, mGravityFactor) - JPH_ADD_ATTRIBUTE(BodyCreationSettings, mNumVelocityStepsOverride) - JPH_ADD_ATTRIBUTE(BodyCreationSettings, mNumPositionStepsOverride) - JPH_ADD_ENUM_ATTRIBUTE(BodyCreationSettings, mOverrideMassProperties) - JPH_ADD_ATTRIBUTE(BodyCreationSettings, mInertiaMultiplier) - JPH_ADD_ATTRIBUTE(BodyCreationSettings, mMassPropertiesOverride) -} - -void BodyCreationSettings::SaveBinaryState(StreamOut &inStream) const -{ - inStream.Write(mPosition); - inStream.Write(mRotation); - inStream.Write(mLinearVelocity); - inStream.Write(mAngularVelocity); - mCollisionGroup.SaveBinaryState(inStream); - inStream.Write(mObjectLayer); - inStream.Write(mMotionType); - inStream.Write(mAllowedDOFs); - inStream.Write(mAllowDynamicOrKinematic); - inStream.Write(mIsSensor); - inStream.Write(mCollideKinematicVsNonDynamic); - inStream.Write(mUseManifoldReduction); - inStream.Write(mApplyGyroscopicForce); - inStream.Write(mMotionQuality); - inStream.Write(mEnhancedInternalEdgeRemoval); - inStream.Write(mAllowSleeping); - inStream.Write(mFriction); - inStream.Write(mRestitution); - inStream.Write(mLinearDamping); - inStream.Write(mAngularDamping); - inStream.Write(mMaxLinearVelocity); - inStream.Write(mMaxAngularVelocity); - inStream.Write(mGravityFactor); - inStream.Write(mNumVelocityStepsOverride); - inStream.Write(mNumPositionStepsOverride); - inStream.Write(mOverrideMassProperties); - inStream.Write(mInertiaMultiplier); - mMassPropertiesOverride.SaveBinaryState(inStream); -} - -void BodyCreationSettings::RestoreBinaryState(StreamIn &inStream) -{ - inStream.Read(mPosition); - inStream.Read(mRotation); - inStream.Read(mLinearVelocity); - inStream.Read(mAngularVelocity); - mCollisionGroup.RestoreBinaryState(inStream); - inStream.Read(mObjectLayer); - inStream.Read(mMotionType); - inStream.Read(mAllowedDOFs); - inStream.Read(mAllowDynamicOrKinematic); - inStream.Read(mIsSensor); - inStream.Read(mCollideKinematicVsNonDynamic); - inStream.Read(mUseManifoldReduction); - inStream.Read(mApplyGyroscopicForce); - inStream.Read(mMotionQuality); - inStream.Read(mEnhancedInternalEdgeRemoval); - inStream.Read(mAllowSleeping); - inStream.Read(mFriction); - inStream.Read(mRestitution); - inStream.Read(mLinearDamping); - inStream.Read(mAngularDamping); - inStream.Read(mMaxLinearVelocity); - inStream.Read(mMaxAngularVelocity); - inStream.Read(mGravityFactor); - inStream.Read(mNumVelocityStepsOverride); - inStream.Read(mNumPositionStepsOverride); - inStream.Read(mOverrideMassProperties); - inStream.Read(mInertiaMultiplier); - mMassPropertiesOverride.RestoreBinaryState(inStream); -} - -Shape::ShapeResult BodyCreationSettings::ConvertShapeSettings() -{ - // If we already have a shape, return it - if (mShapePtr != nullptr) - { - mShape = nullptr; - - Shape::ShapeResult result; - result.Set(const_cast(mShapePtr.GetPtr())); - return result; - } - - // Check if we have shape settings - if (mShape == nullptr) - { - Shape::ShapeResult result; - result.SetError("No shape present!"); - return result; - } - - // Create the shape - Shape::ShapeResult result = mShape->Create(); - if (result.IsValid()) - mShapePtr = result.Get(); - mShape = nullptr; - return result; -} - -const Shape *BodyCreationSettings::GetShape() const -{ - // If we already have a shape, return it - if (mShapePtr != nullptr) - return mShapePtr; - - // Check if we have shape settings - if (mShape == nullptr) - return nullptr; - - // Create the shape - Shape::ShapeResult result = mShape->Create(); - if (result.IsValid()) - return result.Get(); - - Trace("Error: %s", result.GetError().c_str()); - JPH_ASSERT(false, "An error occurred during shape creation. Use ConvertShapeSettings() to convert the shape and get the error!"); - return nullptr; -} - -MassProperties BodyCreationSettings::GetMassProperties() const -{ - // Calculate mass properties - MassProperties mass_properties; - switch (mOverrideMassProperties) - { - case EOverrideMassProperties::CalculateMassAndInertia: - mass_properties = GetShape()->GetMassProperties(); - mass_properties.mInertia *= mInertiaMultiplier; - mass_properties.mInertia(3, 3) = 1.0f; - break; - case EOverrideMassProperties::CalculateInertia: - mass_properties = GetShape()->GetMassProperties(); - mass_properties.ScaleToMass(mMassPropertiesOverride.mMass); - mass_properties.mInertia *= mInertiaMultiplier; - mass_properties.mInertia(3, 3) = 1.0f; - break; - case EOverrideMassProperties::MassAndInertiaProvided: - mass_properties = mMassPropertiesOverride; - break; - } - return mass_properties; -} - -void BodyCreationSettings::SaveWithChildren(StreamOut &inStream, ShapeToIDMap *ioShapeMap, MaterialToIDMap *ioMaterialMap, GroupFilterToIDMap *ioGroupFilterMap) const -{ - // Save creation settings - SaveBinaryState(inStream); - - // Save shape - if (ioShapeMap != nullptr && ioMaterialMap != nullptr) - GetShape()->SaveWithChildren(inStream, *ioShapeMap, *ioMaterialMap); - else - inStream.Write(~uint32(0)); - - // Save group filter - StreamUtils::SaveObjectReference(inStream, mCollisionGroup.GetGroupFilter(), ioGroupFilterMap); -} - -BodyCreationSettings::BCSResult BodyCreationSettings::sRestoreWithChildren(StreamIn &inStream, IDToShapeMap &ioShapeMap, IDToMaterialMap &ioMaterialMap, IDToGroupFilterMap &ioGroupFilterMap) -{ - BCSResult result; - - // Read creation settings - BodyCreationSettings settings; - settings.RestoreBinaryState(inStream); - if (inStream.IsEOF() || inStream.IsFailed()) - { - result.SetError("Error reading body creation settings"); - return result; - } - - // Read shape - Shape::ShapeResult shape_result = Shape::sRestoreWithChildren(inStream, ioShapeMap, ioMaterialMap); - if (shape_result.HasError()) - { - result.SetError(shape_result.GetError()); - return result; - } - settings.SetShape(shape_result.Get()); - - // Read group filter - Result gfresult = StreamUtils::RestoreObjectReference(inStream, ioGroupFilterMap); - if (gfresult.HasError()) - { - result.SetError(gfresult.GetError()); - return result; - } - settings.mCollisionGroup.SetGroupFilter(gfresult.Get()); - - result.Set(settings); - return result; -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyCreationSettings.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyCreationSettings.h deleted file mode 100644 index a0395473fe8..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyCreationSettings.h +++ /dev/null @@ -1,124 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -class StreamIn; -class StreamOut; - -/// Enum used in BodyCreationSettings to indicate how mass and inertia should be calculated -enum class EOverrideMassProperties : uint8 -{ - CalculateMassAndInertia, ///< Tells the system to calculate the mass and inertia based on density - CalculateInertia, ///< Tells the system to take the mass from mMassPropertiesOverride and to calculate the inertia based on density of the shapes and to scale it to the provided mass - MassAndInertiaProvided ///< Tells the system to take the mass and inertia from mMassPropertiesOverride -}; - -/// Settings for constructing a rigid body -class JPH_EXPORT BodyCreationSettings -{ -public: - JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, BodyCreationSettings) - - /// Constructor - BodyCreationSettings() = default; - BodyCreationSettings(const ShapeSettings *inShape, RVec3Arg inPosition, QuatArg inRotation, EMotionType inMotionType, ObjectLayer inObjectLayer) : mPosition(inPosition), mRotation(inRotation), mObjectLayer(inObjectLayer), mMotionType(inMotionType), mShape(inShape) { } - BodyCreationSettings(const Shape *inShape, RVec3Arg inPosition, QuatArg inRotation, EMotionType inMotionType, ObjectLayer inObjectLayer) : mPosition(inPosition), mRotation(inRotation), mObjectLayer(inObjectLayer), mMotionType(inMotionType), mShapePtr(inShape) { } - - /// Access to the shape settings object. This contains serializable (non-runtime optimized) information about the Shape. - const ShapeSettings * GetShapeSettings() const { return mShape; } - void SetShapeSettings(const ShapeSettings *inShape) { mShape = inShape; mShapePtr = nullptr; } - - /// Convert ShapeSettings object into a Shape object. This will free the ShapeSettings object and make the object ready for runtime. Serialization is no longer possible after this. - Shape::ShapeResult ConvertShapeSettings(); - - /// Access to the run-time shape object. Will convert from ShapeSettings object if needed. - const Shape * GetShape() const; - void SetShape(const Shape *inShape) { mShapePtr = inShape; mShape = nullptr; } - - /// Check if the mass properties of this body will be calculated (only relevant for kinematic or dynamic objects that need a MotionProperties object) - bool HasMassProperties() const { return mAllowDynamicOrKinematic || mMotionType != EMotionType::Static; } - - /// Calculate (or return when overridden) the mass and inertia for this body - MassProperties GetMassProperties() const; - - /// Saves the state of this object in binary form to inStream. Doesn't store the shape nor the group filter. - void SaveBinaryState(StreamOut &inStream) const; - - /// Restore the state of this object from inStream. Doesn't restore the shape nor the group filter. - void RestoreBinaryState(StreamIn &inStream); - - using GroupFilterToIDMap = StreamUtils::ObjectToIDMap; - using IDToGroupFilterMap = StreamUtils::IDToObjectMap; - using ShapeToIDMap = Shape::ShapeToIDMap; - using IDToShapeMap = Shape::IDToShapeMap; - using MaterialToIDMap = StreamUtils::ObjectToIDMap; - using IDToMaterialMap = StreamUtils::IDToObjectMap; - - /// Save body creation settings, its shape, materials and group filter. Pass in an empty map in ioShapeMap / ioMaterialMap / ioGroupFilterMap or reuse the same map while saving multiple shapes to the same stream in order to avoid writing duplicates. - /// Pass nullptr to ioShapeMap and ioMaterial map to skip saving shapes - /// Pass nullptr to ioGroupFilterMap to skip saving group filters - void SaveWithChildren(StreamOut &inStream, ShapeToIDMap *ioShapeMap, MaterialToIDMap *ioMaterialMap, GroupFilterToIDMap *ioGroupFilterMap) const; - - using BCSResult = Result; - - /// Restore body creation settings, its shape, materials and group filter. Pass in an empty map in ioShapeMap / ioMaterialMap / ioGroupFilterMap or reuse the same map while reading multiple shapes from the same stream in order to restore duplicates. - static BCSResult sRestoreWithChildren(StreamIn &inStream, IDToShapeMap &ioShapeMap, IDToMaterialMap &ioMaterialMap, IDToGroupFilterMap &ioGroupFilterMap); - - RVec3 mPosition = RVec3::sZero(); ///< Position of the body (not of the center of mass) - Quat mRotation = Quat::sIdentity(); ///< Rotation of the body - Vec3 mLinearVelocity = Vec3::sZero(); ///< World space linear velocity of the center of mass (m/s) - Vec3 mAngularVelocity = Vec3::sZero(); ///< World space angular velocity (rad/s) - - /// User data value (can be used by application) - uint64 mUserData = 0; - - ///@name Collision settings - ObjectLayer mObjectLayer = 0; ///< The collision layer this body belongs to (determines if two objects can collide) - CollisionGroup mCollisionGroup; ///< The collision group this body belongs to (determines if two objects can collide) - - ///@name Simulation properties - EMotionType mMotionType = EMotionType::Dynamic; ///< Motion type, determines if the object is static, dynamic or kinematic - EAllowedDOFs mAllowedDOFs = EAllowedDOFs::All; ///< Which degrees of freedom this body has (can be used to limit simulation to 2D) - bool mAllowDynamicOrKinematic = false; ///< When this body is created as static, this setting tells the system to create a MotionProperties object so that the object can be switched to kinematic or dynamic - bool mIsSensor = false; ///< If this body is a sensor. A sensor will receive collision callbacks, but will not cause any collision responses and can be used as a trigger volume. See description at Body::SetIsSensor. - bool mCollideKinematicVsNonDynamic = false; ///< If kinematic objects can generate contact points against other kinematic or static objects. See description at Body::SetCollideKinematicVsNonDynamic. - bool mUseManifoldReduction = true; ///< If this body should use manifold reduction (see description at Body::SetUseManifoldReduction) - bool mApplyGyroscopicForce = false; ///< Set to indicate that the gyroscopic force should be applied to this body (aka Dzhanibekov effect, see https://en.wikipedia.org/wiki/Tennis_racket_theorem) - EMotionQuality mMotionQuality = EMotionQuality::Discrete; ///< Motion quality, or how well it detects collisions when it has a high velocity - bool mEnhancedInternalEdgeRemoval = false; ///< Set to indicate that extra effort should be made to try to remove ghost contacts (collisions with internal edges of a mesh). This is more expensive but makes bodies move smoother over a mesh with convex edges. - bool mAllowSleeping = true; ///< If this body can go to sleep or not - float mFriction = 0.2f; ///< Friction of the body (dimensionless number, usually between 0 and 1, 0 = no friction, 1 = friction force equals force that presses the two bodies together). Note that bodies can have negative friction but the combined friction (see PhysicsSystem::SetCombineFriction) should never go below zero. - float mRestitution = 0.0f; ///< Restitution of body (dimensionless number, usually between 0 and 1, 0 = completely inelastic collision response, 1 = completely elastic collision response). Note that bodies can have negative restitution but the combined restitution (see PhysicsSystem::SetCombineRestitution) should never go below zero. - float mLinearDamping = 0.05f; ///< Linear damping: dv/dt = -c * v. c must be between 0 and 1 but is usually close to 0. - float mAngularDamping = 0.05f; ///< Angular damping: dw/dt = -c * w. c must be between 0 and 1 but is usually close to 0. - float mMaxLinearVelocity = 500.0f; ///< Maximum linear velocity that this body can reach (m/s) - float mMaxAngularVelocity = 0.25f * JPH_PI * 60.0f; ///< Maximum angular velocity that this body can reach (rad/s) - float mGravityFactor = 1.0f; ///< Value to multiply gravity with for this body - uint mNumVelocityStepsOverride = 0; ///< Used only when this body is dynamic and colliding. Override for the number of solver velocity iterations to run, 0 means use the default in PhysicsSettings::mNumVelocitySteps. The number of iterations to use is the max of all contacts and constraints in the island. - uint mNumPositionStepsOverride = 0; ///< Used only when this body is dynamic and colliding. Override for the number of solver position iterations to run, 0 means use the default in PhysicsSettings::mNumPositionSteps. The number of iterations to use is the max of all contacts and constraints in the island. - - ///@name Mass properties of the body (by default calculated by the shape) - EOverrideMassProperties mOverrideMassProperties = EOverrideMassProperties::CalculateMassAndInertia; ///< Determines how mMassPropertiesOverride will be used - float mInertiaMultiplier = 1.0f; ///< When calculating the inertia (not when it is provided) the calculated inertia will be multiplied by this value - MassProperties mMassPropertiesOverride; ///< Contains replacement mass settings which override the automatically calculated values - -private: - /// Collision volume for the body - RefConst mShape; ///< Shape settings, can be serialized. Mutually exclusive with mShapePtr - RefConst mShapePtr; ///< Actual shape, cannot be serialized. Mutually exclusive with mShape -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyFilter.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyFilter.h deleted file mode 100644 index 6f95e031843..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyFilter.h +++ /dev/null @@ -1,102 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include - -JPH_NAMESPACE_BEGIN - -class Body; - -/// Class function to filter out bodies, returns true if test should collide with body -class BodyFilter : public NonCopyable -{ -public: - /// Destructor - virtual ~BodyFilter() = default; - - /// Filter function. Returns true if we should collide with inBodyID - virtual bool ShouldCollide([[maybe_unused]] const BodyID &inBodyID) const - { - return true; - } - - /// Filter function. Returns true if we should collide with inBody (this is called after the body is locked and makes it possible to filter based on body members) - virtual bool ShouldCollideLocked([[maybe_unused]] const Body &inBody) const - { - return true; - } -}; - -/// A simple body filter implementation that ignores a single, specified body -class IgnoreSingleBodyFilter : public BodyFilter -{ -public: - /// Constructor, pass the body you want to ignore - explicit IgnoreSingleBodyFilter(const BodyID &inBodyID) : - mBodyID(inBodyID) - { - } - - /// Filter function. Returns true if we should collide with inBodyID - virtual bool ShouldCollide(const BodyID &inBodyID) const override - { - return mBodyID != inBodyID; - } - -private: - BodyID mBodyID; -}; - -/// A simple body filter implementation that ignores multiple, specified bodies -class IgnoreMultipleBodiesFilter : public BodyFilter -{ -public: - /// Remove all bodies from the filter - void Clear() - { - mBodyIDs.clear(); - } - - /// Reserve space for inSize body ID's - void Reserve(uint inSize) - { - mBodyIDs.reserve(inSize); - } - - /// Add a body to be ignored - void IgnoreBody(const BodyID &inBodyID) - { - mBodyIDs.push_back(inBodyID); - } - - /// Filter function. Returns true if we should collide with inBodyID - virtual bool ShouldCollide(const BodyID &inBodyID) const override - { - return find(mBodyIDs.begin(), mBodyIDs.end(), inBodyID) == mBodyIDs.end(); - } - -private: - Array mBodyIDs; -}; - -#ifdef JPH_DEBUG_RENDERER -/// Class function to filter out bodies for debug rendering, returns true if body should be rendered -class BodyDrawFilter : public NonCopyable -{ -public: - /// Destructor - virtual ~BodyDrawFilter() = default; - - /// Filter function. Returns true if inBody should be rendered - virtual bool ShouldDraw([[maybe_unused]] const Body& inBody) const - { - return true; - } -}; -#endif // JPH_DEBUG_RENDERER - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyID.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyID.h deleted file mode 100644 index f56d96b7b99..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyID.h +++ /dev/null @@ -1,100 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -/// ID of a body. This is a way of reasoning about bodies in a multithreaded simulation while avoiding race conditions. -class BodyID -{ -public: - JPH_OVERRIDE_NEW_DELETE - - static constexpr uint32 cInvalidBodyID = 0xffffffff; ///< The value for an invalid body ID - static constexpr uint32 cBroadPhaseBit = 0x00800000; ///< This bit is used by the broadphase - static constexpr uint32 cMaxBodyIndex = 0x7fffff; ///< Maximum value for body index (also the maximum amount of bodies supported - 1) - static constexpr uint8 cMaxSequenceNumber = 0xff; ///< Maximum value for the sequence number - - /// Construct invalid body ID - BodyID() : - mID(cInvalidBodyID) - { - } - - /// Construct from index and sequence number combined in a single uint32 (use with care!) - explicit BodyID(uint32 inID) : - mID(inID) - { - JPH_ASSERT((inID & cBroadPhaseBit) == 0 || inID == cInvalidBodyID); // Check bit used by broadphase - } - - /// Construct from index and sequence number - explicit BodyID(uint32 inID, uint8 inSequenceNumber) : - mID((uint32(inSequenceNumber) << 24) | inID) - { - JPH_ASSERT(inID < cMaxBodyIndex); // Should not use bit pattern for invalid ID and should not use the broadphase bit - } - - /// Get index in body array - inline uint32 GetIndex() const - { - return mID & cMaxBodyIndex; - } - - /// Get sequence number of body. - /// The sequence number can be used to check if a body ID with the same body index has been reused by another body. - /// It is mainly used in multi threaded situations where a body is removed and its body index is immediately reused by a body created from another thread. - /// Functions querying the broadphase can (after acquiring a body lock) detect that the body has been removed (we assume that this won't happen more than 128 times in a row). - inline uint8 GetSequenceNumber() const - { - return uint8(mID >> 24); - } - - /// Returns the index and sequence number combined in an uint32 - inline uint32 GetIndexAndSequenceNumber() const - { - return mID; - } - - /// Check if the ID is valid - inline bool IsInvalid() const - { - return mID == cInvalidBodyID; - } - - /// Equals check - inline bool operator == (const BodyID &inRHS) const - { - return mID == inRHS.mID; - } - - /// Not equals check - inline bool operator != (const BodyID &inRHS) const - { - return mID != inRHS.mID; - } - - /// Smaller than operator, can be used for sorting bodies - inline bool operator < (const BodyID &inRHS) const - { - return mID < inRHS.mID; - } - - /// Greater than operator, can be used for sorting bodies - inline bool operator > (const BodyID &inRHS) const - { - return mID > inRHS.mID; - } - -private: - uint32 mID; -}; - -JPH_NAMESPACE_END - -// Create a std::hash for BodyID -JPH_MAKE_HASHABLE(JPH::BodyID, t.GetIndexAndSequenceNumber()) diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyInterface.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyInterface.cpp deleted file mode 100644 index 3c98e1fd312..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyInterface.cpp +++ /dev/null @@ -1,1002 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -Body *BodyInterface::CreateBody(const BodyCreationSettings &inSettings) -{ - Body *body = mBodyManager->AllocateBody(inSettings); - if (!mBodyManager->AddBody(body)) - { - mBodyManager->FreeBody(body); - return nullptr; - } - return body; -} - -Body *BodyInterface::CreateSoftBody(const SoftBodyCreationSettings &inSettings) -{ - Body *body = mBodyManager->AllocateSoftBody(inSettings); - if (!mBodyManager->AddBody(body)) - { - mBodyManager->FreeBody(body); - return nullptr; - } - return body; -} - -Body *BodyInterface::CreateBodyWithID(const BodyID &inBodyID, const BodyCreationSettings &inSettings) -{ - Body *body = mBodyManager->AllocateBody(inSettings); - if (!mBodyManager->AddBodyWithCustomID(body, inBodyID)) - { - mBodyManager->FreeBody(body); - return nullptr; - } - return body; -} - -Body *BodyInterface::CreateSoftBodyWithID(const BodyID &inBodyID, const SoftBodyCreationSettings &inSettings) -{ - Body *body = mBodyManager->AllocateSoftBody(inSettings); - if (!mBodyManager->AddBodyWithCustomID(body, inBodyID)) - { - mBodyManager->FreeBody(body); - return nullptr; - } - return body; -} - -Body *BodyInterface::CreateBodyWithoutID(const BodyCreationSettings &inSettings) const -{ - return mBodyManager->AllocateBody(inSettings); -} - -Body *BodyInterface::CreateSoftBodyWithoutID(const SoftBodyCreationSettings &inSettings) const -{ - return mBodyManager->AllocateSoftBody(inSettings); -} - -void BodyInterface::DestroyBodyWithoutID(Body *inBody) const -{ - mBodyManager->FreeBody(inBody); -} - -bool BodyInterface::AssignBodyID(Body *ioBody) -{ - return mBodyManager->AddBody(ioBody); -} - -bool BodyInterface::AssignBodyID(Body *ioBody, const BodyID &inBodyID) -{ - return mBodyManager->AddBodyWithCustomID(ioBody, inBodyID); -} - -Body *BodyInterface::UnassignBodyID(const BodyID &inBodyID) -{ - Body *body = nullptr; - mBodyManager->RemoveBodies(&inBodyID, 1, &body); - return body; -} - -void BodyInterface::UnassignBodyIDs(const BodyID *inBodyIDs, int inNumber, Body **outBodies) -{ - mBodyManager->RemoveBodies(inBodyIDs, inNumber, outBodies); -} - -void BodyInterface::DestroyBody(const BodyID &inBodyID) -{ - mBodyManager->DestroyBodies(&inBodyID, 1); -} - -void BodyInterface::DestroyBodies(const BodyID *inBodyIDs, int inNumber) -{ - mBodyManager->DestroyBodies(inBodyIDs, inNumber); -} - -void BodyInterface::AddBody(const BodyID &inBodyID, EActivation inActivationMode) -{ - BodyLockWrite lock(*mBodyLockInterface, inBodyID); - if (lock.Succeeded()) - { - const Body &body = lock.GetBody(); - - // Add to broadphase - BodyID id = inBodyID; - BroadPhase::AddState add_state = mBroadPhase->AddBodiesPrepare(&id, 1); - mBroadPhase->AddBodiesFinalize(&id, 1, add_state); - - // Optionally activate body - if (inActivationMode == EActivation::Activate && !body.IsStatic()) - mBodyManager->ActivateBodies(&inBodyID, 1); - } -} - -void BodyInterface::RemoveBody(const BodyID &inBodyID) -{ - BodyLockWrite lock(*mBodyLockInterface, inBodyID); - if (lock.Succeeded()) - { - const Body &body = lock.GetBody(); - - // Deactivate body - if (body.IsActive()) - mBodyManager->DeactivateBodies(&inBodyID, 1); - - // Remove from broadphase - BodyID id = inBodyID; - mBroadPhase->RemoveBodies(&id, 1); - } -} - -bool BodyInterface::IsAdded(const BodyID &inBodyID) const -{ - BodyLockRead lock(*mBodyLockInterface, inBodyID); - return lock.SucceededAndIsInBroadPhase(); -} - -BodyID BodyInterface::CreateAndAddBody(const BodyCreationSettings &inSettings, EActivation inActivationMode) -{ - const Body *b = CreateBody(inSettings); - if (b == nullptr) - return BodyID(); // Out of bodies - AddBody(b->GetID(), inActivationMode); - return b->GetID(); -} - -BodyID BodyInterface::CreateAndAddSoftBody(const SoftBodyCreationSettings &inSettings, EActivation inActivationMode) -{ - const Body *b = CreateSoftBody(inSettings); - if (b == nullptr) - return BodyID(); // Out of bodies - AddBody(b->GetID(), inActivationMode); - return b->GetID(); -} - -BodyInterface::AddState BodyInterface::AddBodiesPrepare(BodyID *ioBodies, int inNumber) -{ - return mBroadPhase->AddBodiesPrepare(ioBodies, inNumber); -} - -void BodyInterface::AddBodiesFinalize(BodyID *ioBodies, int inNumber, AddState inAddState, EActivation inActivationMode) -{ - BodyLockMultiWrite lock(*mBodyLockInterface, ioBodies, inNumber); - - // Add to broadphase - mBroadPhase->AddBodiesFinalize(ioBodies, inNumber, inAddState); - - // Optionally activate bodies - if (inActivationMode == EActivation::Activate) - mBodyManager->ActivateBodies(ioBodies, inNumber); -} - -void BodyInterface::AddBodiesAbort(BodyID *ioBodies, int inNumber, AddState inAddState) -{ - mBroadPhase->AddBodiesAbort(ioBodies, inNumber, inAddState); -} - -void BodyInterface::RemoveBodies(BodyID *ioBodies, int inNumber) -{ - BodyLockMultiWrite lock(*mBodyLockInterface, ioBodies, inNumber); - - // Deactivate bodies - mBodyManager->DeactivateBodies(ioBodies, inNumber); - - // Remove from broadphase - mBroadPhase->RemoveBodies(ioBodies, inNumber); -} - -void BodyInterface::ActivateBody(const BodyID &inBodyID) -{ - BodyLockWrite lock(*mBodyLockInterface, inBodyID); - if (lock.Succeeded()) - { - const Body &body = lock.GetBody(); - - if (!body.IsActive()) - mBodyManager->ActivateBodies(&inBodyID, 1); - } -} - -void BodyInterface::ActivateBodies(const BodyID *inBodyIDs, int inNumber) -{ - BodyLockMultiWrite lock(*mBodyLockInterface, inBodyIDs, inNumber); - - mBodyManager->ActivateBodies(inBodyIDs, inNumber); -} - -void BodyInterface::ActivateBodiesInAABox(const AABox &inBox, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) -{ - AllHitCollisionCollector collector; - mBroadPhase->CollideAABox(inBox, collector, inBroadPhaseLayerFilter, inObjectLayerFilter); - ActivateBodies(collector.mHits.data(), (int)collector.mHits.size()); -} - -void BodyInterface::DeactivateBody(const BodyID &inBodyID) -{ - BodyLockWrite lock(*mBodyLockInterface, inBodyID); - if (lock.Succeeded()) - { - const Body &body = lock.GetBody(); - - if (body.IsActive()) - mBodyManager->DeactivateBodies(&inBodyID, 1); - } -} - -void BodyInterface::DeactivateBodies(const BodyID *inBodyIDs, int inNumber) -{ - BodyLockMultiWrite lock(*mBodyLockInterface, inBodyIDs, inNumber); - - mBodyManager->DeactivateBodies(inBodyIDs, inNumber); -} - -bool BodyInterface::IsActive(const BodyID &inBodyID) const -{ - BodyLockRead lock(*mBodyLockInterface, inBodyID); - return lock.Succeeded() && lock.GetBody().IsActive(); -} - -TwoBodyConstraint *BodyInterface::CreateConstraint(const TwoBodyConstraintSettings *inSettings, const BodyID &inBodyID1, const BodyID &inBodyID2) -{ - BodyID constraint_bodies[] = { inBodyID1, inBodyID2 }; - BodyLockMultiWrite lock(*mBodyLockInterface, constraint_bodies, 2); - - Body *body1 = lock.GetBody(0); - Body *body2 = lock.GetBody(1); - - JPH_ASSERT(body1 != body2); - JPH_ASSERT(body1 != nullptr || body2 != nullptr); - - return inSettings->Create(body1 != nullptr? *body1 : Body::sFixedToWorld, body2 != nullptr? *body2 : Body::sFixedToWorld); -} - -void BodyInterface::ActivateConstraint(const TwoBodyConstraint *inConstraint) -{ - BodyID bodies[] = { inConstraint->GetBody1()->GetID(), inConstraint->GetBody2()->GetID() }; - ActivateBodies(bodies, 2); -} - -RefConst BodyInterface::GetShape(const BodyID &inBodyID) const -{ - RefConst shape; - BodyLockRead lock(*mBodyLockInterface, inBodyID); - if (lock.Succeeded()) - shape = lock.GetBody().GetShape(); - return shape; -} - -void BodyInterface::SetShape(const BodyID &inBodyID, const Shape *inShape, bool inUpdateMassProperties, EActivation inActivationMode) const -{ - BodyLockWrite lock(*mBodyLockInterface, inBodyID); - if (lock.Succeeded()) - { - Body &body = lock.GetBody(); - - // Check if shape actually changed - if (body.GetShape() != inShape) - { - // Update the shape - body.SetShapeInternal(inShape, inUpdateMassProperties); - - // Flag collision cache invalid for this body - mBodyManager->InvalidateContactCacheForBody(body); - - // Notify broadphase of change - if (body.IsInBroadPhase()) - { - BodyID id = body.GetID(); - mBroadPhase->NotifyBodiesAABBChanged(&id, 1); - } - - // Optionally activate body - if (inActivationMode == EActivation::Activate && !body.IsStatic()) - mBodyManager->ActivateBodies(&inBodyID, 1); - } - } -} - -void BodyInterface::NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inPreviousCenterOfMass, bool inUpdateMassProperties, EActivation inActivationMode) const -{ - BodyLockWrite lock(*mBodyLockInterface, inBodyID); - if (lock.Succeeded()) - { - Body &body = lock.GetBody(); - - // Update center of mass, mass and inertia - body.UpdateCenterOfMassInternal(inPreviousCenterOfMass, inUpdateMassProperties); - - // Recalculate bounding box - body.CalculateWorldSpaceBoundsInternal(); - - // Flag collision cache invalid for this body - mBodyManager->InvalidateContactCacheForBody(body); - - // Notify broadphase of change - if (body.IsInBroadPhase()) - { - BodyID id = body.GetID(); - mBroadPhase->NotifyBodiesAABBChanged(&id, 1); - } - - // Optionally activate body - if (inActivationMode == EActivation::Activate && !body.IsStatic()) - mBodyManager->ActivateBodies(&inBodyID, 1); - } -} - -void BodyInterface::SetObjectLayer(const BodyID &inBodyID, ObjectLayer inLayer) -{ - BodyLockWrite lock(*mBodyLockInterface, inBodyID); - if (lock.Succeeded()) - { - Body &body = lock.GetBody(); - - // Check if layer actually changed, updating the broadphase is rather expensive - if (body.GetObjectLayer() != inLayer) - { - // Update the layer on the body - mBodyManager->SetBodyObjectLayerInternal(body, inLayer); - - // Notify broadphase of change - if (body.IsInBroadPhase()) - { - BodyID id = body.GetID(); - mBroadPhase->NotifyBodiesLayerChanged(&id, 1); - } - } - } -} - -ObjectLayer BodyInterface::GetObjectLayer(const BodyID &inBodyID) const -{ - BodyLockRead lock(*mBodyLockInterface, inBodyID); - if (lock.Succeeded()) - return lock.GetBody().GetObjectLayer(); - else - return cObjectLayerInvalid; -} - -void BodyInterface::SetPositionAndRotation(const BodyID &inBodyID, RVec3Arg inPosition, QuatArg inRotation, EActivation inActivationMode) -{ - BodyLockWrite lock(*mBodyLockInterface, inBodyID); - if (lock.Succeeded()) - { - Body &body = lock.GetBody(); - - // Update the position - body.SetPositionAndRotationInternal(inPosition, inRotation); - - // Notify broadphase of change - if (body.IsInBroadPhase()) - { - BodyID id = body.GetID(); - mBroadPhase->NotifyBodiesAABBChanged(&id, 1); - } - - // Optionally activate body - if (inActivationMode == EActivation::Activate && !body.IsStatic()) - mBodyManager->ActivateBodies(&inBodyID, 1); - } -} - -void BodyInterface::SetPositionAndRotationWhenChanged(const BodyID &inBodyID, RVec3Arg inPosition, QuatArg inRotation, EActivation inActivationMode) -{ - BodyLockWrite lock(*mBodyLockInterface, inBodyID); - if (lock.Succeeded()) - { - Body &body = lock.GetBody(); - - // Check if there is enough change - if (!body.GetPosition().IsClose(inPosition) - || !body.GetRotation().IsClose(inRotation)) - { - // Update the position - body.SetPositionAndRotationInternal(inPosition, inRotation); - - // Notify broadphase of change - if (body.IsInBroadPhase()) - { - BodyID id = body.GetID(); - mBroadPhase->NotifyBodiesAABBChanged(&id, 1); - } - - // Optionally activate body - if (inActivationMode == EActivation::Activate && !body.IsStatic()) - mBodyManager->ActivateBodies(&inBodyID, 1); - } - } -} - -void BodyInterface::GetPositionAndRotation(const BodyID &inBodyID, RVec3 &outPosition, Quat &outRotation) const -{ - BodyLockRead lock(*mBodyLockInterface, inBodyID); - if (lock.Succeeded()) - { - const Body &body = lock.GetBody(); - outPosition = body.GetPosition(); - outRotation = body.GetRotation(); - } - else - { - outPosition = RVec3::sZero(); - outRotation = Quat::sIdentity(); - } -} - -void BodyInterface::SetPosition(const BodyID &inBodyID, RVec3Arg inPosition, EActivation inActivationMode) -{ - BodyLockWrite lock(*mBodyLockInterface, inBodyID); - if (lock.Succeeded()) - { - Body &body = lock.GetBody(); - - // Update the position - body.SetPositionAndRotationInternal(inPosition, body.GetRotation()); - - // Notify broadphase of change - if (body.IsInBroadPhase()) - { - BodyID id = body.GetID(); - mBroadPhase->NotifyBodiesAABBChanged(&id, 1); - } - - // Optionally activate body - if (inActivationMode == EActivation::Activate && !body.IsStatic()) - mBodyManager->ActivateBodies(&inBodyID, 1); - } -} - -RVec3 BodyInterface::GetPosition(const BodyID &inBodyID) const -{ - BodyLockRead lock(*mBodyLockInterface, inBodyID); - if (lock.Succeeded()) - return lock.GetBody().GetPosition(); - else - return RVec3::sZero(); -} - -RVec3 BodyInterface::GetCenterOfMassPosition(const BodyID &inBodyID) const -{ - BodyLockRead lock(*mBodyLockInterface, inBodyID); - if (lock.Succeeded()) - return lock.GetBody().GetCenterOfMassPosition(); - else - return RVec3::sZero(); -} - -void BodyInterface::SetRotation(const BodyID &inBodyID, QuatArg inRotation, EActivation inActivationMode) -{ - BodyLockWrite lock(*mBodyLockInterface, inBodyID); - if (lock.Succeeded()) - { - Body &body = lock.GetBody(); - - // Update the position - body.SetPositionAndRotationInternal(body.GetPosition(), inRotation); - - // Notify broadphase of change - if (body.IsInBroadPhase()) - { - BodyID id = body.GetID(); - mBroadPhase->NotifyBodiesAABBChanged(&id, 1); - } - - // Optionally activate body - if (inActivationMode == EActivation::Activate && !body.IsStatic()) - mBodyManager->ActivateBodies(&inBodyID, 1); - } -} - -Quat BodyInterface::GetRotation(const BodyID &inBodyID) const -{ - BodyLockRead lock(*mBodyLockInterface, inBodyID); - if (lock.Succeeded()) - return lock.GetBody().GetRotation(); - else - return Quat::sIdentity(); -} - -RMat44 BodyInterface::GetWorldTransform(const BodyID &inBodyID) const -{ - BodyLockRead lock(*mBodyLockInterface, inBodyID); - if (lock.Succeeded()) - return lock.GetBody().GetWorldTransform(); - else - return RMat44::sIdentity(); -} - -RMat44 BodyInterface::GetCenterOfMassTransform(const BodyID &inBodyID) const -{ - BodyLockRead lock(*mBodyLockInterface, inBodyID); - if (lock.Succeeded()) - return lock.GetBody().GetCenterOfMassTransform(); - else - return RMat44::sIdentity(); -} - -void BodyInterface::MoveKinematic(const BodyID &inBodyID, RVec3Arg inTargetPosition, QuatArg inTargetRotation, float inDeltaTime) -{ - BodyLockWrite lock(*mBodyLockInterface, inBodyID); - if (lock.Succeeded()) - { - Body &body = lock.GetBody(); - - body.MoveKinematic(inTargetPosition, inTargetRotation, inDeltaTime); - - if (!body.IsActive() && (!body.GetLinearVelocity().IsNearZero() || !body.GetAngularVelocity().IsNearZero())) - mBodyManager->ActivateBodies(&inBodyID, 1); - } -} - -void BodyInterface::SetLinearAndAngularVelocity(const BodyID &inBodyID, Vec3Arg inLinearVelocity, Vec3Arg inAngularVelocity) -{ - BodyLockWrite lock(*mBodyLockInterface, inBodyID); - if (lock.Succeeded()) - { - Body &body = lock.GetBody(); - if (!body.IsStatic()) - { - body.SetLinearVelocityClamped(inLinearVelocity); - body.SetAngularVelocityClamped(inAngularVelocity); - - if (!body.IsActive() && (!inLinearVelocity.IsNearZero() || !inAngularVelocity.IsNearZero())) - mBodyManager->ActivateBodies(&inBodyID, 1); - } - } -} - -void BodyInterface::GetLinearAndAngularVelocity(const BodyID &inBodyID, Vec3 &outLinearVelocity, Vec3 &outAngularVelocity) const -{ - BodyLockRead lock(*mBodyLockInterface, inBodyID); - if (lock.Succeeded()) - { - const Body &body = lock.GetBody(); - if (!body.IsStatic()) - { - outLinearVelocity = body.GetLinearVelocity(); - outAngularVelocity = body.GetAngularVelocity(); - return; - } - } - - outLinearVelocity = outAngularVelocity = Vec3::sZero(); -} - -void BodyInterface::SetLinearVelocity(const BodyID &inBodyID, Vec3Arg inLinearVelocity) -{ - BodyLockWrite lock(*mBodyLockInterface, inBodyID); - if (lock.Succeeded()) - { - Body &body = lock.GetBody(); - if (!body.IsStatic()) - { - body.SetLinearVelocityClamped(inLinearVelocity); - - if (!body.IsActive() && !inLinearVelocity.IsNearZero()) - mBodyManager->ActivateBodies(&inBodyID, 1); - } - } -} - -Vec3 BodyInterface::GetLinearVelocity(const BodyID &inBodyID) const -{ - BodyLockRead lock(*mBodyLockInterface, inBodyID); - if (lock.Succeeded()) - { - const Body &body = lock.GetBody(); - if (!body.IsStatic()) - return body.GetLinearVelocity(); - } - - return Vec3::sZero(); -} - -void BodyInterface::AddLinearVelocity(const BodyID &inBodyID, Vec3Arg inLinearVelocity) -{ - BodyLockWrite lock(*mBodyLockInterface, inBodyID); - if (lock.Succeeded()) - { - Body &body = lock.GetBody(); - if (!body.IsStatic()) - { - body.SetLinearVelocityClamped(body.GetLinearVelocity() + inLinearVelocity); - - if (!body.IsActive() && !body.GetLinearVelocity().IsNearZero()) - mBodyManager->ActivateBodies(&inBodyID, 1); - } - } -} - -void BodyInterface::AddLinearAndAngularVelocity(const BodyID &inBodyID, Vec3Arg inLinearVelocity, Vec3Arg inAngularVelocity) -{ - BodyLockWrite lock(*mBodyLockInterface, inBodyID); - if (lock.Succeeded()) - { - Body &body = lock.GetBody(); - if (!body.IsStatic()) - { - body.SetLinearVelocityClamped(body.GetLinearVelocity() + inLinearVelocity); - body.SetAngularVelocityClamped(body.GetAngularVelocity() + inAngularVelocity); - - if (!body.IsActive() && (!body.GetLinearVelocity().IsNearZero() || !body.GetAngularVelocity().IsNearZero())) - mBodyManager->ActivateBodies(&inBodyID, 1); - } - } -} - -void BodyInterface::SetAngularVelocity(const BodyID &inBodyID, Vec3Arg inAngularVelocity) -{ - BodyLockWrite lock(*mBodyLockInterface, inBodyID); - if (lock.Succeeded()) - { - Body &body = lock.GetBody(); - if (!body.IsStatic()) - { - body.SetAngularVelocityClamped(inAngularVelocity); - - if (!body.IsActive() && !inAngularVelocity.IsNearZero()) - mBodyManager->ActivateBodies(&inBodyID, 1); - } - } -} - -Vec3 BodyInterface::GetAngularVelocity(const BodyID &inBodyID) const -{ - BodyLockRead lock(*mBodyLockInterface, inBodyID); - if (lock.Succeeded()) - { - const Body &body = lock.GetBody(); - if (!body.IsStatic()) - return body.GetAngularVelocity(); - } - - return Vec3::sZero(); -} - -Vec3 BodyInterface::GetPointVelocity(const BodyID &inBodyID, RVec3Arg inPoint) const -{ - BodyLockRead lock(*mBodyLockInterface, inBodyID); - if (lock.Succeeded()) - { - const Body &body = lock.GetBody(); - if (!body.IsStatic()) - return body.GetPointVelocity(inPoint); - } - - return Vec3::sZero(); -} - -void BodyInterface::AddForce(const BodyID &inBodyID, Vec3Arg inForce) -{ - BodyLockWrite lock(*mBodyLockInterface, inBodyID); - if (lock.Succeeded()) - { - Body &body = lock.GetBody(); - if (body.IsDynamic()) - { - body.AddForce(inForce); - - if (!body.IsActive()) - mBodyManager->ActivateBodies(&inBodyID, 1); - } - } -} - -void BodyInterface::AddForce(const BodyID &inBodyID, Vec3Arg inForce, RVec3Arg inPoint) -{ - BodyLockWrite lock(*mBodyLockInterface, inBodyID); - if (lock.Succeeded()) - { - Body &body = lock.GetBody(); - if (body.IsDynamic()) - { - body.AddForce(inForce, inPoint); - - if (!body.IsActive()) - mBodyManager->ActivateBodies(&inBodyID, 1); - } - } -} - -void BodyInterface::AddTorque(const BodyID &inBodyID, Vec3Arg inTorque) -{ - BodyLockWrite lock(*mBodyLockInterface, inBodyID); - if (lock.Succeeded()) - { - Body &body = lock.GetBody(); - if (body.IsDynamic()) - { - body.AddTorque(inTorque); - - if (!body.IsActive()) - mBodyManager->ActivateBodies(&inBodyID, 1); - } - } -} - -void BodyInterface::AddForceAndTorque(const BodyID &inBodyID, Vec3Arg inForce, Vec3Arg inTorque) -{ - BodyLockWrite lock(*mBodyLockInterface, inBodyID); - if (lock.Succeeded()) - { - Body &body = lock.GetBody(); - if (body.IsDynamic()) - { - body.AddForce(inForce); - body.AddTorque(inTorque); - - if (!body.IsActive()) - mBodyManager->ActivateBodies(&inBodyID, 1); - } - } -} - -void BodyInterface::AddImpulse(const BodyID &inBodyID, Vec3Arg inImpulse) -{ - BodyLockWrite lock(*mBodyLockInterface, inBodyID); - if (lock.Succeeded()) - { - Body &body = lock.GetBody(); - if (body.IsDynamic()) - { - body.AddImpulse(inImpulse); - - if (!body.IsActive()) - mBodyManager->ActivateBodies(&inBodyID, 1); - } - } -} - -void BodyInterface::AddImpulse(const BodyID &inBodyID, Vec3Arg inImpulse, RVec3Arg inPoint) -{ - BodyLockWrite lock(*mBodyLockInterface, inBodyID); - if (lock.Succeeded()) - { - Body &body = lock.GetBody(); - if (body.IsDynamic()) - { - body.AddImpulse(inImpulse, inPoint); - - if (!body.IsActive()) - mBodyManager->ActivateBodies(&inBodyID, 1); - } - } -} - -void BodyInterface::AddAngularImpulse(const BodyID &inBodyID, Vec3Arg inAngularImpulse) -{ - BodyLockWrite lock(*mBodyLockInterface, inBodyID); - if (lock.Succeeded()) - { - Body &body = lock.GetBody(); - if (body.IsDynamic()) - { - body.AddAngularImpulse(inAngularImpulse); - - if (!body.IsActive()) - mBodyManager->ActivateBodies(&inBodyID, 1); - } - } -} - -void BodyInterface::SetPositionRotationAndVelocity(const BodyID &inBodyID, RVec3Arg inPosition, QuatArg inRotation, Vec3Arg inLinearVelocity, Vec3Arg inAngularVelocity) -{ - BodyLockWrite lock(*mBodyLockInterface, inBodyID); - if (lock.Succeeded()) - { - Body &body = lock.GetBody(); - - // Update the position - body.SetPositionAndRotationInternal(inPosition, inRotation); - - // Notify broadphase of change - if (body.IsInBroadPhase()) - { - BodyID id = body.GetID(); - mBroadPhase->NotifyBodiesAABBChanged(&id, 1); - } - - if (!body.IsStatic()) - { - body.SetLinearVelocityClamped(inLinearVelocity); - body.SetAngularVelocityClamped(inAngularVelocity); - - // Optionally activate body - if (!body.IsActive() && (!inLinearVelocity.IsNearZero() || !inAngularVelocity.IsNearZero())) - mBodyManager->ActivateBodies(&inBodyID, 1); - } - } -} - -void BodyInterface::SetMotionType(const BodyID &inBodyID, EMotionType inMotionType, EActivation inActivationMode) -{ - BodyLockWrite lock(*mBodyLockInterface, inBodyID); - if (lock.Succeeded()) - { - Body &body = lock.GetBody(); - - // Deactivate if we're making the body static - if (body.IsActive() && inMotionType == EMotionType::Static) - mBodyManager->DeactivateBodies(&inBodyID, 1); - - body.SetMotionType(inMotionType); - - // Activate body if requested - if (inMotionType != EMotionType::Static && inActivationMode == EActivation::Activate && !body.IsActive()) - mBodyManager->ActivateBodies(&inBodyID, 1); - } -} - -EBodyType BodyInterface::GetBodyType(const BodyID &inBodyID) const -{ - BodyLockRead lock(*mBodyLockInterface, inBodyID); - if (lock.Succeeded()) - return lock.GetBody().GetBodyType(); - else - return EBodyType::RigidBody; -} - -EMotionType BodyInterface::GetMotionType(const BodyID &inBodyID) const -{ - BodyLockRead lock(*mBodyLockInterface, inBodyID); - if (lock.Succeeded()) - return lock.GetBody().GetMotionType(); - else - return EMotionType::Static; -} - -void BodyInterface::SetMotionQuality(const BodyID &inBodyID, EMotionQuality inMotionQuality) -{ - BodyLockWrite lock(*mBodyLockInterface, inBodyID); - if (lock.Succeeded()) - mBodyManager->SetMotionQuality(lock.GetBody(), inMotionQuality); -} - -EMotionQuality BodyInterface::GetMotionQuality(const BodyID &inBodyID) const -{ - BodyLockRead lock(*mBodyLockInterface, inBodyID); - if (lock.Succeeded() && !lock.GetBody().IsStatic()) - return lock.GetBody().GetMotionProperties()->GetMotionQuality(); - else - return EMotionQuality::Discrete; -} - -Mat44 BodyInterface::GetInverseInertia(const BodyID &inBodyID) const -{ - BodyLockRead lock(*mBodyLockInterface, inBodyID); - if (lock.Succeeded()) - return lock.GetBody().GetInverseInertia(); - else - return Mat44::sIdentity(); -} - -void BodyInterface::SetRestitution(const BodyID &inBodyID, float inRestitution) -{ - BodyLockWrite lock(*mBodyLockInterface, inBodyID); - if (lock.Succeeded()) - lock.GetBody().SetRestitution(inRestitution); -} - -float BodyInterface::GetRestitution(const BodyID &inBodyID) const -{ - BodyLockRead lock(*mBodyLockInterface, inBodyID); - if (lock.Succeeded()) - return lock.GetBody().GetRestitution(); - else - return 0.0f; -} - -void BodyInterface::SetFriction(const BodyID &inBodyID, float inFriction) -{ - BodyLockWrite lock(*mBodyLockInterface, inBodyID); - if (lock.Succeeded()) - lock.GetBody().SetFriction(inFriction); -} - -float BodyInterface::GetFriction(const BodyID &inBodyID) const -{ - BodyLockRead lock(*mBodyLockInterface, inBodyID); - if (lock.Succeeded()) - return lock.GetBody().GetFriction(); - else - return 0.0f; -} - -void BodyInterface::SetGravityFactor(const BodyID &inBodyID, float inGravityFactor) -{ - BodyLockWrite lock(*mBodyLockInterface, inBodyID); - if (lock.Succeeded() && lock.GetBody().GetMotionPropertiesUnchecked() != nullptr) - lock.GetBody().GetMotionPropertiesUnchecked()->SetGravityFactor(inGravityFactor); -} - -float BodyInterface::GetGravityFactor(const BodyID &inBodyID) const -{ - BodyLockRead lock(*mBodyLockInterface, inBodyID); - if (lock.Succeeded() && lock.GetBody().GetMotionPropertiesUnchecked() != nullptr) - return lock.GetBody().GetMotionPropertiesUnchecked()->GetGravityFactor(); - else - return 1.0f; -} - -void BodyInterface::SetUseManifoldReduction(const BodyID &inBodyID, bool inUseReduction) -{ - BodyLockWrite lock(*mBodyLockInterface, inBodyID); - if (lock.Succeeded()) - { - Body &body = lock.GetBody(); - if (body.GetUseManifoldReduction() != inUseReduction) - { - body.SetUseManifoldReduction(inUseReduction); - - // Flag collision cache invalid for this body - mBodyManager->InvalidateContactCacheForBody(body); - } - } -} - -bool BodyInterface::GetUseManifoldReduction(const BodyID &inBodyID) const -{ - BodyLockRead lock(*mBodyLockInterface, inBodyID); - if (lock.Succeeded()) - return lock.GetBody().GetUseManifoldReduction(); - else - return true; -} - -TransformedShape BodyInterface::GetTransformedShape(const BodyID &inBodyID) const -{ - BodyLockRead lock(*mBodyLockInterface, inBodyID); - if (lock.Succeeded()) - return lock.GetBody().GetTransformedShape(); - else - return TransformedShape(); -} - -uint64 BodyInterface::GetUserData(const BodyID &inBodyID) const -{ - BodyLockRead lock(*mBodyLockInterface, inBodyID); - if (lock.Succeeded()) - return lock.GetBody().GetUserData(); - else - return 0; -} - -void BodyInterface::SetUserData(const BodyID &inBodyID, uint64 inUserData) const -{ - BodyLockWrite lock(*mBodyLockInterface, inBodyID); - if (lock.Succeeded()) - lock.GetBody().SetUserData(inUserData); -} - -const PhysicsMaterial *BodyInterface::GetMaterial(const BodyID &inBodyID, const SubShapeID &inSubShapeID) const -{ - BodyLockRead lock(*mBodyLockInterface, inBodyID); - if (lock.Succeeded()) - return lock.GetBody().GetShape()->GetMaterial(inSubShapeID); - else - return PhysicsMaterial::sDefault; -} - -void BodyInterface::InvalidateContactCache(const BodyID &inBodyID) -{ - BodyLockWrite lock(*mBodyLockInterface, inBodyID); - if (lock.Succeeded()) - mBodyManager->InvalidateContactCacheForBody(lock.GetBody()); -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyInterface.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyInterface.h deleted file mode 100644 index 3bdee8305ef..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyInterface.h +++ /dev/null @@ -1,278 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -class Body; -class BodyCreationSettings; -class SoftBodyCreationSettings; -class BodyLockInterface; -class BroadPhase; -class BodyManager; -class TransformedShape; -class PhysicsMaterial; -class SubShapeID; -class Shape; -class TwoBodyConstraintSettings; -class TwoBodyConstraint; -class BroadPhaseLayerFilter; -class AABox; - -/// Class that provides operations on bodies using a body ID. Note that if you need to do multiple operations on a single body, it is more efficient to lock the body once and combine the operations. -/// All quantities are in world space unless otherwise specified. -class JPH_EXPORT BodyInterface : public NonCopyable -{ -public: - /// Initialize the interface (should only be called by PhysicsSystem) - void Init(BodyLockInterface &inBodyLockInterface, BodyManager &inBodyManager, BroadPhase &inBroadPhase) { mBodyLockInterface = &inBodyLockInterface; mBodyManager = &inBodyManager; mBroadPhase = &inBroadPhase; } - - /// Create a rigid body - /// @return Created body or null when out of bodies - Body * CreateBody(const BodyCreationSettings &inSettings); - - /// Create a soft body - /// @return Created body or null when out of bodies - Body * CreateSoftBody(const SoftBodyCreationSettings &inSettings); - - /// Create a rigid body with specified ID. This function can be used if a simulation is to run in sync between clients or if a simulation needs to be restored exactly. - /// The ID created on the server can be replicated to the client and used to create a deterministic simulation. - /// @return Created body or null when the body ID is invalid or a body of the same ID already exists. - Body * CreateBodyWithID(const BodyID &inBodyID, const BodyCreationSettings &inSettings); - - /// Create a soft body with specified ID. See comments at CreateBodyWithID. - Body * CreateSoftBodyWithID(const BodyID &inBodyID, const SoftBodyCreationSettings &inSettings); - - /// Advanced use only. Creates a rigid body without specifying an ID. This body cannot be added to the physics system until it has been assigned a body ID. - /// This can be used to decouple allocation from registering the body. A call to CreateBodyWithoutID followed by AssignBodyID is equivalent to calling CreateBodyWithID. - /// @return Created body - Body * CreateBodyWithoutID(const BodyCreationSettings &inSettings) const; - - /// Advanced use only. Creates a body without specifying an ID. See comments at CreateBodyWithoutID. - Body * CreateSoftBodyWithoutID(const SoftBodyCreationSettings &inSettings) const; - - /// Advanced use only. Destroy a body previously created with CreateBodyWithoutID that hasn't gotten an ID yet through the AssignBodyID function, - /// or a body that has had its body ID unassigned through UnassignBodyIDs. Bodies that have an ID should be destroyed through DestroyBody. - void DestroyBodyWithoutID(Body *inBody) const; - - /// Advanced use only. Assigns the next available body ID to a body that was created using CreateBodyWithoutID. After this call, the body can be added to the physics system. - /// @return false if the body already has an ID or out of body ids. - bool AssignBodyID(Body *ioBody); - - /// Advanced use only. Assigns a body ID to a body that was created using CreateBodyWithoutID. After this call, the body can be added to the physics system. - /// @return false if the body already has an ID or if the ID is not valid. - bool AssignBodyID(Body *ioBody, const BodyID &inBodyID); - - /// Advanced use only. See UnassignBodyIDs. Unassigns the ID of a single body. - Body * UnassignBodyID(const BodyID &inBodyID); - - /// Advanced use only. Removes a number of body IDs from their bodies and returns the body pointers. Before calling this, the body should have been removed from the physics system. - /// The body can be destroyed through DestroyBodyWithoutID. This can be used to decouple deallocation. A call to UnassignBodyIDs followed by calls to DestroyBodyWithoutID is equivalent to calling DestroyBodies. - /// @param inBodyIDs A list of body IDs - /// @param inNumber Number of bodies in the list - /// @param outBodies If not null on input, this will contain a list of body pointers corresponding to inBodyIDs that can be destroyed afterwards (caller assumes ownership over these). - void UnassignBodyIDs(const BodyID *inBodyIDs, int inNumber, Body **outBodies); - - /// Destroy a body - void DestroyBody(const BodyID &inBodyID); - - /// Destroy multiple bodies - void DestroyBodies(const BodyID *inBodyIDs, int inNumber); - - /// Add body to the physics system. - /// Note that if you need to add multiple bodies, use the AddBodiesPrepare/AddBodiesFinalize function. - /// Adding many bodies, one at a time, results in a really inefficient broadphase until PhysicsSystem::OptimizeBroadPhase is called or when PhysicsSystem::Update rebuilds the tree! - /// After adding, to get a body by ID use the BodyLockRead or BodyLockWrite interface! - void AddBody(const BodyID &inBodyID, EActivation inActivationMode); - - /// Remove body from the physics system. - void RemoveBody(const BodyID &inBodyID); - - /// Check if a body has been added to the physics system. - bool IsAdded(const BodyID &inBodyID) const; - - /// Combines CreateBody and AddBody - /// @return Created body ID or an invalid ID when out of bodies - BodyID CreateAndAddBody(const BodyCreationSettings &inSettings, EActivation inActivationMode); - - /// Combines CreateSoftBody and AddBody - /// @return Created body ID or an invalid ID when out of bodies - BodyID CreateAndAddSoftBody(const SoftBodyCreationSettings &inSettings, EActivation inActivationMode); - - /// Broadphase add state handle, used to keep track of a batch while adding to the broadphase. - using AddState = void *; - - ///@name Batch adding interface, see Broadphase for further documentation. - /// Note that ioBodies array must be kept constant while the add is in progress. - ///@{ - AddState AddBodiesPrepare(BodyID *ioBodies, int inNumber); - void AddBodiesFinalize(BodyID *ioBodies, int inNumber, AddState inAddState, EActivation inActivationMode); - void AddBodiesAbort(BodyID *ioBodies, int inNumber, AddState inAddState); - void RemoveBodies(BodyID *ioBodies, int inNumber); - ///@} - - ///@name Activate / deactivate a body - ///@{ - void ActivateBody(const BodyID &inBodyID); - void ActivateBodies(const BodyID *inBodyIDs, int inNumber); - void ActivateBodiesInAABox(const AABox &inBox, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter); - void DeactivateBody(const BodyID &inBodyID); - void DeactivateBodies(const BodyID *inBodyIDs, int inNumber); - bool IsActive(const BodyID &inBodyID) const; - ///@} - - /// Create a two body constraint - TwoBodyConstraint * CreateConstraint(const TwoBodyConstraintSettings *inSettings, const BodyID &inBodyID1, const BodyID &inBodyID2); - - /// Activate non-static bodies attached to a constraint - void ActivateConstraint(const TwoBodyConstraint *inConstraint); - - ///@name Access to the shape of a body - ///@{ - - /// Get the current shape - RefConst GetShape(const BodyID &inBodyID) const; - - /// Set a new shape on the body - /// @param inBodyID Body ID of body that had its shape changed - /// @param inShape The new shape - /// @param inUpdateMassProperties When true, the mass and inertia tensor is recalculated - /// @param inActivationMode Weather or not to activate the body - void SetShape(const BodyID &inBodyID, const Shape *inShape, bool inUpdateMassProperties, EActivation inActivationMode) const; - - /// Notify all systems to indicate that a shape has changed (usable for MutableCompoundShapes) - /// @param inBodyID Body ID of body that had its shape changed - /// @param inPreviousCenterOfMass Center of mass of the shape before the alterations - /// @param inUpdateMassProperties When true, the mass and inertia tensor is recalculated - /// @param inActivationMode Weather or not to activate the body - void NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inPreviousCenterOfMass, bool inUpdateMassProperties, EActivation inActivationMode) const; - ///@} - - ///@name Object layer of a body - ///@{ - void SetObjectLayer(const BodyID &inBodyID, ObjectLayer inLayer); - ObjectLayer GetObjectLayer(const BodyID &inBodyID) const; - ///@} - - ///@name Position and rotation of a body - ///@{ - void SetPositionAndRotation(const BodyID &inBodyID, RVec3Arg inPosition, QuatArg inRotation, EActivation inActivationMode); - void SetPositionAndRotationWhenChanged(const BodyID &inBodyID, RVec3Arg inPosition, QuatArg inRotation, EActivation inActivationMode); ///< Will only update the position/rotation and activate the body when the difference is larger than a very small number. This avoids updating the broadphase/waking up a body when the resulting position/orientation doesn't really change. - void GetPositionAndRotation(const BodyID &inBodyID, RVec3 &outPosition, Quat &outRotation) const; - void SetPosition(const BodyID &inBodyID, RVec3Arg inPosition, EActivation inActivationMode); - RVec3 GetPosition(const BodyID &inBodyID) const; - RVec3 GetCenterOfMassPosition(const BodyID &inBodyID) const; - void SetRotation(const BodyID &inBodyID, QuatArg inRotation, EActivation inActivationMode); - Quat GetRotation(const BodyID &inBodyID) const; - RMat44 GetWorldTransform(const BodyID &inBodyID) const; - RMat44 GetCenterOfMassTransform(const BodyID &inBodyID) const; - ///@} - - /// Set velocity of body such that it will be positioned at inTargetPosition/Rotation in inDeltaTime seconds (will activate body if needed) - void MoveKinematic(const BodyID &inBodyID, RVec3Arg inTargetPosition, QuatArg inTargetRotation, float inDeltaTime); - - /// Linear or angular velocity (functions will activate body if needed). - /// Note that the linear velocity is the velocity of the center of mass, which may not coincide with the position of your object, to correct for this: \f$VelocityCOM = Velocity - AngularVelocity \times ShapeCOM\f$ - void SetLinearAndAngularVelocity(const BodyID &inBodyID, Vec3Arg inLinearVelocity, Vec3Arg inAngularVelocity); - void GetLinearAndAngularVelocity(const BodyID &inBodyID, Vec3 &outLinearVelocity, Vec3 &outAngularVelocity) const; - void SetLinearVelocity(const BodyID &inBodyID, Vec3Arg inLinearVelocity); - Vec3 GetLinearVelocity(const BodyID &inBodyID) const; - void AddLinearVelocity(const BodyID &inBodyID, Vec3Arg inLinearVelocity); ///< Add velocity to current velocity - void AddLinearAndAngularVelocity(const BodyID &inBodyID, Vec3Arg inLinearVelocity, Vec3Arg inAngularVelocity); ///< Add linear and angular to current velocities - void SetAngularVelocity(const BodyID &inBodyID, Vec3Arg inAngularVelocity); - Vec3 GetAngularVelocity(const BodyID &inBodyID) const; - Vec3 GetPointVelocity(const BodyID &inBodyID, RVec3Arg inPoint) const; ///< Velocity of point inPoint (in world space, e.g. on the surface of the body) of the body - - /// Set the complete motion state of a body. - /// Note that the linear velocity is the velocity of the center of mass, which may not coincide with the position of your object, to correct for this: \f$VelocityCOM = Velocity - AngularVelocity \times ShapeCOM\f$ - void SetPositionRotationAndVelocity(const BodyID &inBodyID, RVec3Arg inPosition, QuatArg inRotation, Vec3Arg inLinearVelocity, Vec3Arg inAngularVelocity); - - ///@name Add forces to the body - ///@{ - void AddForce(const BodyID &inBodyID, Vec3Arg inForce); ///< See Body::AddForce - void AddForce(const BodyID &inBodyID, Vec3Arg inForce, RVec3Arg inPoint); ///< Applied at inPoint - void AddTorque(const BodyID &inBodyID, Vec3Arg inTorque); ///< See Body::AddTorque - void AddForceAndTorque(const BodyID &inBodyID, Vec3Arg inForce, Vec3Arg inTorque); ///< A combination of Body::AddForce and Body::AddTorque - ///@} - - ///@name Add an impulse to the body - ///@{ - void AddImpulse(const BodyID &inBodyID, Vec3Arg inImpulse); ///< Applied at center of mass - void AddImpulse(const BodyID &inBodyID, Vec3Arg inImpulse, RVec3Arg inPoint); ///< Applied at inPoint - void AddAngularImpulse(const BodyID &inBodyID, Vec3Arg inAngularImpulse); - ///@} - - ///@name Body type - ///@{ - EBodyType GetBodyType(const BodyID &inBodyID) const; - ///@} - - ///@name Body motion type - ///@{ - void SetMotionType(const BodyID &inBodyID, EMotionType inMotionType, EActivation inActivationMode); - EMotionType GetMotionType(const BodyID &inBodyID) const; - ///@} - - ///@name Body motion quality - ///@{ - void SetMotionQuality(const BodyID &inBodyID, EMotionQuality inMotionQuality); - EMotionQuality GetMotionQuality(const BodyID &inBodyID) const; - ///@} - - /// Get inverse inertia tensor in world space - Mat44 GetInverseInertia(const BodyID &inBodyID) const; - - ///@name Restitution - ///@{ - void SetRestitution(const BodyID &inBodyID, float inRestitution); - float GetRestitution(const BodyID &inBodyID) const; - ///@} - - ///@name Friction - ///@{ - void SetFriction(const BodyID &inBodyID, float inFriction); - float GetFriction(const BodyID &inBodyID) const; - ///@} - - ///@name Gravity factor - ///@{ - void SetGravityFactor(const BodyID &inBodyID, float inGravityFactor); - float GetGravityFactor(const BodyID &inBodyID) const; - ///@} - - ///@name Manifold reduction - ///@{ - void SetUseManifoldReduction(const BodyID &inBodyID, bool inUseReduction); - bool GetUseManifoldReduction(const BodyID &inBodyID) const; - ///@} - - /// Get transform and shape for this body, used to perform collision detection - TransformedShape GetTransformedShape(const BodyID &inBodyID) const; - - /// Get the user data for a body - uint64 GetUserData(const BodyID &inBodyID) const; - void SetUserData(const BodyID &inBodyID, uint64 inUserData) const; - - /// Get the material for a particular sub shape - const PhysicsMaterial * GetMaterial(const BodyID &inBodyID, const SubShapeID &inSubShapeID) const; - - /// Set the Body::EFlags::InvalidateContactCache flag for the specified body. This means that the collision cache is invalid for any body pair involving that body until the next physics step. - void InvalidateContactCache(const BodyID &inBodyID); - -private: - BodyLockInterface * mBodyLockInterface = nullptr; - BodyManager * mBodyManager = nullptr; - BroadPhase * mBroadPhase = nullptr; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyLock.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyLock.h deleted file mode 100644 index eccfec4d1e8..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyLock.h +++ /dev/null @@ -1,111 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -/// Base class for locking bodies for the duration of the scope of this class (do not use directly) -template -class BodyLockBase : public NonCopyable -{ -public: - /// Constructor will lock the body - BodyLockBase(const BodyLockInterface &inBodyLockInterface, const BodyID &inBodyID) : - mBodyLockInterface(inBodyLockInterface) - { - if (inBodyID == BodyID()) - { - // Invalid body id - mBodyLockMutex = nullptr; - mBody = nullptr; - } - else - { - // Get mutex - mBodyLockMutex = Write? inBodyLockInterface.LockWrite(inBodyID) : inBodyLockInterface.LockRead(inBodyID); - - // Get a reference to the body or nullptr when it is no longer valid - mBody = inBodyLockInterface.TryGetBody(inBodyID); - } - } - - /// Explicitly release the lock (normally this is done in the destructor) - inline void ReleaseLock() - { - if (mBodyLockMutex != nullptr) - { - if (Write) - mBodyLockInterface.UnlockWrite(mBodyLockMutex); - else - mBodyLockInterface.UnlockRead(mBodyLockMutex); - - mBodyLockMutex = nullptr; - mBody = nullptr; - } - } - - /// Destructor will unlock the body - ~BodyLockBase() - { - ReleaseLock(); - } - - /// Test if the lock was successful (if the body ID was valid) - inline bool Succeeded() const - { - return mBody != nullptr; - } - - /// Test if the lock was successful (if the body ID was valid) and the body is still in the broad phase - inline bool SucceededAndIsInBroadPhase() const - { - return mBody != nullptr && mBody->IsInBroadPhase(); - } - - /// Access the body - inline BodyType & GetBody() const - { - JPH_ASSERT(mBody != nullptr, "Should check Succeeded() first"); - return *mBody; - } - -private: - const BodyLockInterface & mBodyLockInterface; - SharedMutex * mBodyLockMutex; - BodyType * mBody; -}; - -/// A body lock takes a body ID and locks the underlying body so that other threads cannot access its members -/// -/// The common usage pattern is: -/// -/// BodyLockInterface lock_interface = physics_system.GetBodyLockInterface(); // Or non-locking interface if the lock is already taken -/// BodyID body_id = ...; // Obtain ID to body -/// -/// // Scoped lock -/// { -/// BodyLockRead lock(lock_interface, body_id); -/// if (lock.Succeeded()) // body_id may no longer be valid -/// { -/// const Body &body = lock.GetBody(); -/// -/// // Do something with body -/// ... -/// } -/// } -class BodyLockRead : public BodyLockBase -{ - using BodyLockBase::BodyLockBase; -}; - -/// Specialization that locks a body for writing to. @see BodyLockRead for usage patterns. -class BodyLockWrite : public BodyLockBase -{ - using BodyLockBase::BodyLockBase; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyLockInterface.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyLockInterface.h deleted file mode 100644 index b65951fc8cc..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyLockInterface.h +++ /dev/null @@ -1,134 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - - -#pragma once - -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -/// Base class interface for locking a body. Usually you will use BodyLockRead / BodyLockWrite / BodyLockMultiRead / BodyLockMultiWrite instead. -class BodyLockInterface : public NonCopyable -{ -public: - /// Redefine MutexMask - using MutexMask = BodyManager::MutexMask; - - /// Constructor - explicit BodyLockInterface(BodyManager &inBodyManager) : mBodyManager(inBodyManager) { } - virtual ~BodyLockInterface() = default; - - ///@name Locking functions - ///@{ - virtual SharedMutex * LockRead(const BodyID &inBodyID) const = 0; - virtual void UnlockRead(SharedMutex *inMutex) const = 0; - virtual SharedMutex * LockWrite(const BodyID &inBodyID) const = 0; - virtual void UnlockWrite(SharedMutex *inMutex) const = 0; - ///@} - - /// Get the mask needed to lock all bodies - inline MutexMask GetAllBodiesMutexMask() const - { - return mBodyManager.GetAllBodiesMutexMask(); - } - - ///@name Batch locking functions - ///@{ - virtual MutexMask GetMutexMask(const BodyID *inBodies, int inNumber) const = 0; - virtual void LockRead(MutexMask inMutexMask) const = 0; - virtual void UnlockRead(MutexMask inMutexMask) const = 0; - virtual void LockWrite(MutexMask inMutexMask) const = 0; - virtual void UnlockWrite(MutexMask inMutexMask) const = 0; - ///@} - - /// Convert body ID to body - inline Body * TryGetBody(const BodyID &inBodyID) const { return mBodyManager.TryGetBody(inBodyID); } - -protected: - BodyManager & mBodyManager; -}; - -/// Implementation that performs no locking (assumes the lock has already been taken) -class BodyLockInterfaceNoLock final : public BodyLockInterface -{ -public: - using BodyLockInterface::BodyLockInterface; - - ///@name Locking functions - virtual SharedMutex * LockRead([[maybe_unused]] const BodyID &inBodyID) const override { return nullptr; } - virtual void UnlockRead([[maybe_unused]] SharedMutex *inMutex) const override { /* Nothing to do */ } - virtual SharedMutex * LockWrite([[maybe_unused]] const BodyID &inBodyID) const override { return nullptr; } - virtual void UnlockWrite([[maybe_unused]] SharedMutex *inMutex) const override { /* Nothing to do */ } - - ///@name Batch locking functions - virtual MutexMask GetMutexMask([[maybe_unused]] const BodyID *inBodies, [[maybe_unused]] int inNumber) const override { return 0; } - virtual void LockRead([[maybe_unused]] MutexMask inMutexMask) const override { /* Nothing to do */ } - virtual void UnlockRead([[maybe_unused]] MutexMask inMutexMask) const override { /* Nothing to do */ } - virtual void LockWrite([[maybe_unused]] MutexMask inMutexMask) const override { /* Nothing to do */ } - virtual void UnlockWrite([[maybe_unused]] MutexMask inMutexMask) const override { /* Nothing to do */ } -}; - -/// Implementation that uses the body manager to lock the correct mutex for a body -class BodyLockInterfaceLocking final : public BodyLockInterface -{ -public: - using BodyLockInterface::BodyLockInterface; - - ///@name Locking functions - virtual SharedMutex * LockRead(const BodyID &inBodyID) const override - { - SharedMutex &mutex = mBodyManager.GetMutexForBody(inBodyID); - PhysicsLock::sLockShared(mutex JPH_IF_ENABLE_ASSERTS(, &mBodyManager, EPhysicsLockTypes::PerBody)); - return &mutex; - } - - virtual void UnlockRead(SharedMutex *inMutex) const override - { - PhysicsLock::sUnlockShared(*inMutex JPH_IF_ENABLE_ASSERTS(, &mBodyManager, EPhysicsLockTypes::PerBody)); - } - - virtual SharedMutex * LockWrite(const BodyID &inBodyID) const override - { - SharedMutex &mutex = mBodyManager.GetMutexForBody(inBodyID); - PhysicsLock::sLock(mutex JPH_IF_ENABLE_ASSERTS(, &mBodyManager, EPhysicsLockTypes::PerBody)); - return &mutex; - } - - virtual void UnlockWrite(SharedMutex *inMutex) const override - { - PhysicsLock::sUnlock(*inMutex JPH_IF_ENABLE_ASSERTS(, &mBodyManager, EPhysicsLockTypes::PerBody)); - } - - ///@name Batch locking functions - virtual MutexMask GetMutexMask(const BodyID *inBodies, int inNumber) const override - { - return mBodyManager.GetMutexMask(inBodies, inNumber); - } - - virtual void LockRead(MutexMask inMutexMask) const override - { - mBodyManager.LockRead(inMutexMask); - } - - virtual void UnlockRead(MutexMask inMutexMask) const override - { - mBodyManager.UnlockRead(inMutexMask); - } - - virtual void LockWrite(MutexMask inMutexMask) const override - { - mBodyManager.LockWrite(inMutexMask); - } - - virtual void UnlockWrite(MutexMask inMutexMask) const override - { - mBodyManager.UnlockWrite(inMutexMask); - } -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyLockMulti.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyLockMulti.h deleted file mode 100644 index 5872729c029..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyLockMulti.h +++ /dev/null @@ -1,104 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -/// Base class for locking multiple bodies for the duration of the scope of this class (do not use directly) -template -class BodyLockMultiBase : public NonCopyable -{ -public: - /// Redefine MutexMask - using MutexMask = BodyLockInterface::MutexMask; - - /// Constructor will lock the bodies - BodyLockMultiBase(const BodyLockInterface &inBodyLockInterface, const BodyID *inBodyIDs, int inNumber) : - mBodyLockInterface(inBodyLockInterface), - mMutexMask(inBodyLockInterface.GetMutexMask(inBodyIDs, inNumber)), - mBodyIDs(inBodyIDs), - mNumBodyIDs(inNumber) - { - if (mMutexMask != 0) - { - // Get mutex - if (Write) - inBodyLockInterface.LockWrite(mMutexMask); - else - inBodyLockInterface.LockRead(mMutexMask); - } - } - - /// Destructor will unlock the bodies - ~BodyLockMultiBase() - { - if (mMutexMask != 0) - { - if (Write) - mBodyLockInterface.UnlockWrite(mMutexMask); - else - mBodyLockInterface.UnlockRead(mMutexMask); - } - } - - /// Access the body (returns null if body was not properly locked) - inline BodyType * GetBody(int inBodyIndex) const - { - // Range check - JPH_ASSERT(inBodyIndex >= 0 && inBodyIndex < mNumBodyIDs); - - // Get body ID - const BodyID &body_id = mBodyIDs[inBodyIndex]; - if (body_id.IsInvalid()) - return nullptr; - - // Get a reference to the body or nullptr when it is no longer valid - return mBodyLockInterface.TryGetBody(body_id); - } - -private: - const BodyLockInterface & mBodyLockInterface; - MutexMask mMutexMask; - const BodyID * mBodyIDs; - int mNumBodyIDs; -}; - -/// A multi body lock takes a number of body IDs and locks the underlying bodies so that other threads cannot access its members -/// -/// The common usage pattern is: -/// -/// BodyLockInterface lock_interface = physics_system.GetBodyLockInterface(); // Or non-locking interface if the lock is already taken -/// const BodyID *body_id = ...; // Obtain IDs to bodies -/// int num_body_ids = ...; -/// -/// // Scoped lock -/// { -/// BodyLockMultiRead lock(lock_interface, body_ids, num_body_ids); -/// for (int i = 0; i < num_body_ids; ++i) -/// { -/// const Body *body = lock.GetBody(i); -/// if (body != nullptr) -/// { -/// const Body &body = lock.Body(); -/// -/// // Do something with body -/// ... -/// } -/// } -/// } -class BodyLockMultiRead : public BodyLockMultiBase -{ - using BodyLockMultiBase::BodyLockMultiBase; -}; - -/// Specialization that locks multiple bodies for writing to. @see BodyLockMultiRead for usage patterns. -class BodyLockMultiWrite : public BodyLockMultiBase -{ - using BodyLockMultiBase::BodyLockMultiBase; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyManager.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyManager.cpp deleted file mode 100644 index 09995ac7614..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyManager.cpp +++ /dev/null @@ -1,1149 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef JPH_DEBUG_RENDERER - #include - #include -#endif // JPH_DEBUG_RENDERER - -JPH_NAMESPACE_BEGIN - -#ifdef JPH_ENABLE_ASSERTS - static thread_local bool sOverrideAllowActivation = false; - static thread_local bool sOverrideAllowDeactivation = false; - - bool BodyManager::sGetOverrideAllowActivation() - { - return sOverrideAllowActivation; - } - - void BodyManager::sSetOverrideAllowActivation(bool inValue) - { - sOverrideAllowActivation = inValue; - } - - bool BodyManager::sGetOverrideAllowDeactivation() - { - return sOverrideAllowDeactivation; - } - - void BodyManager::sSetOverrideAllowDeactivation(bool inValue) - { - sOverrideAllowDeactivation = inValue; - } -#endif - -// Helper class that combines a body and its motion properties -class BodyWithMotionProperties : public Body -{ -public: - JPH_OVERRIDE_NEW_DELETE - - MotionProperties mMotionProperties; -}; - -// Helper class that combines a soft body its motion properties and shape -class SoftBodyWithMotionPropertiesAndShape : public Body -{ -public: - SoftBodyWithMotionPropertiesAndShape() - { - mShape.SetEmbedded(); - } - - SoftBodyMotionProperties mMotionProperties; - SoftBodyShape mShape; -}; - -inline void BodyManager::sDeleteBody(Body *inBody) -{ - if (inBody->mMotionProperties != nullptr) - { - JPH_IF_ENABLE_ASSERTS(inBody->mMotionProperties = nullptr;) - if (inBody->IsSoftBody()) - { - inBody->mShape = nullptr; // Release the shape to avoid assertion on shape destruction because of embedded object with refcount > 0 - delete static_cast(inBody); - } - else - delete static_cast(inBody); - } - else - delete inBody; -} - -BodyManager::~BodyManager() -{ - UniqueLock lock(mBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::BodiesList)); - - // Destroy any bodies that are still alive - for (Body *b : mBodies) - if (sIsValidBodyPointer(b)) - sDeleteBody(b); - - for (BodyID *active_bodies : mActiveBodies) - delete [] active_bodies; -} - -void BodyManager::Init(uint inMaxBodies, uint inNumBodyMutexes, const BroadPhaseLayerInterface &inLayerInterface) -{ - UniqueLock lock(mBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::BodiesList)); - - // Num body mutexes must be a power of two and not bigger than our MutexMask - uint num_body_mutexes = Clamp(GetNextPowerOf2(inNumBodyMutexes == 0? 2 * thread::hardware_concurrency() : inNumBodyMutexes), 1, sizeof(MutexMask) * 8); - - // Allocate the body mutexes - mBodyMutexes.Init(num_body_mutexes); - - // Allocate space for bodies - mBodies.reserve(inMaxBodies); - - // Allocate space for active bodies - for (BodyID *&active_bodies : mActiveBodies) - { - JPH_ASSERT(active_bodies == nullptr); - active_bodies = new BodyID [inMaxBodies]; - } - - // Allocate space for sequence numbers - mBodySequenceNumbers.resize(inMaxBodies); - - // Keep layer interface - mBroadPhaseLayerInterface = &inLayerInterface; -} - -uint BodyManager::GetNumBodies() const -{ - UniqueLock lock(mBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::BodiesList)); - - return mNumBodies; -} - -BodyManager::BodyStats BodyManager::GetBodyStats() const -{ - UniqueLock lock(mBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::BodiesList)); - - BodyStats stats; - stats.mNumBodies = mNumBodies; - stats.mMaxBodies = uint(mBodies.capacity()); - - for (const Body *body : mBodies) - if (sIsValidBodyPointer(body)) - { - if (body->IsSoftBody()) - { - stats.mNumSoftBodies++; - if (body->IsActive()) - stats.mNumActiveSoftBodies++; - } - else - { - switch (body->GetMotionType()) - { - case EMotionType::Static: - stats.mNumBodiesStatic++; - break; - - case EMotionType::Dynamic: - stats.mNumBodiesDynamic++; - if (body->IsActive()) - stats.mNumActiveBodiesDynamic++; - break; - - case EMotionType::Kinematic: - stats.mNumBodiesKinematic++; - if (body->IsActive()) - stats.mNumActiveBodiesKinematic++; - break; - } - } - } - - return stats; -} - -Body *BodyManager::AllocateBody(const BodyCreationSettings &inBodyCreationSettings) const -{ - // Fill in basic properties - Body *body; - if (inBodyCreationSettings.HasMassProperties()) - { - BodyWithMotionProperties *bmp = new BodyWithMotionProperties; - body = bmp; - body->mMotionProperties = &bmp->mMotionProperties; - } - else - { - body = new Body; - } - body->mBodyType = EBodyType::RigidBody; - body->mShape = inBodyCreationSettings.GetShape(); - body->mUserData = inBodyCreationSettings.mUserData; - body->SetFriction(inBodyCreationSettings.mFriction); - body->SetRestitution(inBodyCreationSettings.mRestitution); - body->mMotionType = inBodyCreationSettings.mMotionType; - if (inBodyCreationSettings.mIsSensor) - body->SetIsSensor(true); - if (inBodyCreationSettings.mCollideKinematicVsNonDynamic) - body->SetCollideKinematicVsNonDynamic(true); - if (inBodyCreationSettings.mUseManifoldReduction) - body->SetUseManifoldReduction(true); - if (inBodyCreationSettings.mApplyGyroscopicForce) - body->SetApplyGyroscopicForce(true); - if (inBodyCreationSettings.mEnhancedInternalEdgeRemoval) - body->SetEnhancedInternalEdgeRemoval(true); - SetBodyObjectLayerInternal(*body, inBodyCreationSettings.mObjectLayer); - body->mObjectLayer = inBodyCreationSettings.mObjectLayer; - body->mCollisionGroup = inBodyCreationSettings.mCollisionGroup; - - if (inBodyCreationSettings.HasMassProperties()) - { - MotionProperties *mp = body->mMotionProperties; - mp->SetLinearDamping(inBodyCreationSettings.mLinearDamping); - mp->SetAngularDamping(inBodyCreationSettings.mAngularDamping); - mp->SetMaxLinearVelocity(inBodyCreationSettings.mMaxLinearVelocity); - mp->SetMaxAngularVelocity(inBodyCreationSettings.mMaxAngularVelocity); - mp->SetMassProperties(inBodyCreationSettings.mAllowedDOFs, inBodyCreationSettings.GetMassProperties()); - mp->SetLinearVelocity(inBodyCreationSettings.mLinearVelocity); // Needs to happen after setting the max linear/angular velocity and setting allowed DOFs - mp->SetAngularVelocity(inBodyCreationSettings.mAngularVelocity); - mp->SetGravityFactor(inBodyCreationSettings.mGravityFactor); - mp->SetNumVelocityStepsOverride(inBodyCreationSettings.mNumVelocityStepsOverride); - mp->SetNumPositionStepsOverride(inBodyCreationSettings.mNumPositionStepsOverride); - mp->mMotionQuality = inBodyCreationSettings.mMotionQuality; - mp->mAllowSleeping = inBodyCreationSettings.mAllowSleeping; - JPH_IF_ENABLE_ASSERTS(mp->mCachedBodyType = body->mBodyType;) - JPH_IF_ENABLE_ASSERTS(mp->mCachedMotionType = body->mMotionType;) - } - - // Position body - body->SetPositionAndRotationInternal(inBodyCreationSettings.mPosition, inBodyCreationSettings.mRotation); - - return body; -} - -/// Create a soft body using creation settings. The returned body will not be part of the body manager yet. -Body *BodyManager::AllocateSoftBody(const SoftBodyCreationSettings &inSoftBodyCreationSettings) const -{ - // Fill in basic properties - SoftBodyWithMotionPropertiesAndShape *bmp = new SoftBodyWithMotionPropertiesAndShape; - SoftBodyMotionProperties *mp = &bmp->mMotionProperties; - SoftBodyShape *shape = &bmp->mShape; - Body *body = bmp; - shape->mSoftBodyMotionProperties = mp; - body->mBodyType = EBodyType::SoftBody; - body->mMotionProperties = mp; - body->mShape = shape; - body->mUserData = inSoftBodyCreationSettings.mUserData; - body->SetFriction(inSoftBodyCreationSettings.mFriction); - body->SetRestitution(inSoftBodyCreationSettings.mRestitution); - body->mMotionType = EMotionType::Dynamic; - SetBodyObjectLayerInternal(*body, inSoftBodyCreationSettings.mObjectLayer); - body->mObjectLayer = inSoftBodyCreationSettings.mObjectLayer; - body->mCollisionGroup = inSoftBodyCreationSettings.mCollisionGroup; - mp->SetLinearDamping(inSoftBodyCreationSettings.mLinearDamping); - mp->SetAngularDamping(0); - mp->SetMaxLinearVelocity(inSoftBodyCreationSettings.mMaxLinearVelocity); - mp->SetMaxAngularVelocity(FLT_MAX); - mp->SetLinearVelocity(Vec3::sZero()); - mp->SetAngularVelocity(Vec3::sZero()); - mp->SetGravityFactor(inSoftBodyCreationSettings.mGravityFactor); - mp->mMotionQuality = EMotionQuality::Discrete; - mp->mAllowSleeping = inSoftBodyCreationSettings.mAllowSleeping; - JPH_IF_ENABLE_ASSERTS(mp->mCachedBodyType = body->mBodyType;) - JPH_IF_ENABLE_ASSERTS(mp->mCachedMotionType = body->mMotionType;) - mp->Initialize(inSoftBodyCreationSettings); - - body->SetPositionAndRotationInternal(inSoftBodyCreationSettings.mPosition, inSoftBodyCreationSettings.mMakeRotationIdentity? Quat::sIdentity() : inSoftBodyCreationSettings.mRotation); - - return body; -} - -void BodyManager::FreeBody(Body *inBody) const -{ - JPH_ASSERT(inBody->GetID().IsInvalid(), "This function should only be called on a body that doesn't have an ID yet, use DestroyBody otherwise"); - - sDeleteBody(inBody); -} - -bool BodyManager::AddBody(Body *ioBody) -{ - // Return error when body was already added - if (!ioBody->GetID().IsInvalid()) - return false; - - // Determine next free index - uint32 idx; - { - UniqueLock lock(mBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::BodiesList)); - - if (mBodyIDFreeListStart != cBodyIDFreeListEnd) - { - // Pop an item from the freelist - JPH_ASSERT(mBodyIDFreeListStart & cIsFreedBody); - idx = uint32(mBodyIDFreeListStart >> cFreedBodyIndexShift); - JPH_ASSERT(!sIsValidBodyPointer(mBodies[idx])); - mBodyIDFreeListStart = uintptr_t(mBodies[idx]); - mBodies[idx] = ioBody; - } - else - { - if (mBodies.size() < mBodies.capacity()) - { - // Allocate a new entry, note that the array should not actually resize since we've reserved it at init time - idx = uint32(mBodies.size()); - mBodies.push_back(ioBody); - } - else - { - // Out of bodies - return false; - } - } - - // Update cached number of bodies - mNumBodies++; - } - - // Get next sequence number and assign the ID - uint8 seq_no = GetNextSequenceNumber(idx); - ioBody->mID = BodyID(idx, seq_no); - return true; -} - -bool BodyManager::AddBodyWithCustomID(Body *ioBody, const BodyID &inBodyID) -{ - // Return error when body was already added - if (!ioBody->GetID().IsInvalid()) - return false; - - { - UniqueLock lock(mBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::BodiesList)); - - // Check if index is beyond the max body ID - uint32 idx = inBodyID.GetIndex(); - if (idx >= mBodies.capacity()) - return false; // Return error - - if (idx < mBodies.size()) - { - // Body array entry has already been allocated, check if there's a free body here - if (sIsValidBodyPointer(mBodies[idx])) - return false; // Return error - - // Remove the entry from the freelist - uintptr_t idx_start = mBodyIDFreeListStart >> cFreedBodyIndexShift; - if (idx == idx_start) - { - // First entry, easy to remove, the start of the list is our next - mBodyIDFreeListStart = uintptr_t(mBodies[idx]); - } - else - { - // Loop over the freelist and find the entry in the freelist pointing to our index - // TODO: This is O(N), see if this becomes a performance problem (don't want to put the freed bodies in a double linked list) - uintptr_t cur, next; - for (cur = idx_start; cur != cBodyIDFreeListEnd >> cFreedBodyIndexShift; cur = next) - { - next = uintptr_t(mBodies[cur]) >> cFreedBodyIndexShift; - if (next == idx) - { - mBodies[cur] = mBodies[idx]; - break; - } - } - JPH_ASSERT(cur != cBodyIDFreeListEnd >> cFreedBodyIndexShift); - } - - // Put the body in the slot - mBodies[idx] = ioBody; - } - else - { - // Ensure that all body IDs up to this body ID have been allocated and added to the free list - while (idx > mBodies.size()) - { - // Push the id onto the freelist - mBodies.push_back((Body *)mBodyIDFreeListStart); - mBodyIDFreeListStart = (uintptr_t(mBodies.size() - 1) << cFreedBodyIndexShift) | cIsFreedBody; - } - - // Add the element to the list - mBodies.push_back(ioBody); - } - - // Update cached number of bodies - mNumBodies++; - } - - // Assign the ID - ioBody->mID = inBodyID; - return true; -} - -Body *BodyManager::RemoveBodyInternal(const BodyID &inBodyID) -{ - // Get body - uint32 idx = inBodyID.GetIndex(); - Body *body = mBodies[idx]; - - // Validate that it can be removed - JPH_ASSERT(body->GetID() == inBodyID); - JPH_ASSERT(!body->IsActive()); - JPH_ASSERT(!body->IsInBroadPhase()); - - // Push the id onto the freelist - mBodies[idx] = (Body *)mBodyIDFreeListStart; - mBodyIDFreeListStart = (uintptr_t(idx) << cFreedBodyIndexShift) | cIsFreedBody; - - return body; -} - -#if defined(_DEBUG) && defined(JPH_ENABLE_ASSERTS) - -void BodyManager::ValidateFreeList() const -{ - // Check that the freelist is correct - size_t num_freed = 0; - for (uintptr_t start = mBodyIDFreeListStart; start != cBodyIDFreeListEnd; start = uintptr_t(mBodies[start >> cFreedBodyIndexShift])) - { - JPH_ASSERT(start & cIsFreedBody); - num_freed++; - } - JPH_ASSERT(mNumBodies == mBodies.size() - num_freed); -} - -#endif // defined(_DEBUG) && _defined(JPH_ENABLE_ASSERTS) - -void BodyManager::RemoveBodies(const BodyID *inBodyIDs, int inNumber, Body **outBodies) -{ - // Don't take lock if no bodies are to be destroyed - if (inNumber <= 0) - return; - - UniqueLock lock(mBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::BodiesList)); - - // Update cached number of bodies - JPH_ASSERT(mNumBodies >= (uint)inNumber); - mNumBodies -= inNumber; - - for (const BodyID *b = inBodyIDs, *b_end = inBodyIDs + inNumber; b < b_end; b++) - { - // Remove body - Body *body = RemoveBodyInternal(*b); - - // Clear the ID - body->mID = BodyID(); - - // Return the body to the caller - if (outBodies != nullptr) - { - *outBodies = body; - ++outBodies; - } - } - -#if defined(_DEBUG) && defined(JPH_ENABLE_ASSERTS) - ValidateFreeList(); -#endif // defined(_DEBUG) && _defined(JPH_ENABLE_ASSERTS) -} - -void BodyManager::DestroyBodies(const BodyID *inBodyIDs, int inNumber) -{ - // Don't take lock if no bodies are to be destroyed - if (inNumber <= 0) - return; - - UniqueLock lock(mBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::BodiesList)); - - // Update cached number of bodies - JPH_ASSERT(mNumBodies >= (uint)inNumber); - mNumBodies -= inNumber; - - for (const BodyID *b = inBodyIDs, *b_end = inBodyIDs + inNumber; b < b_end; b++) - { - // Remove body - Body *body = RemoveBodyInternal(*b); - - // Free the body - sDeleteBody(body); - } - -#if defined(_DEBUG) && defined(JPH_ENABLE_ASSERTS) - ValidateFreeList(); -#endif // defined(_DEBUG) && _defined(JPH_ENABLE_ASSERTS) -} - -void BodyManager::AddBodyToActiveBodies(Body &ioBody) -{ - // Select the correct array to use - int type = (int)ioBody.GetBodyType(); - atomic &num_active_bodies = mNumActiveBodies[type]; - BodyID *active_bodies = mActiveBodies[type]; - - MotionProperties *mp = ioBody.mMotionProperties; - mp->mIndexInActiveBodies = num_active_bodies; - JPH_ASSERT(num_active_bodies < GetMaxBodies()); - active_bodies[num_active_bodies] = ioBody.GetID(); - num_active_bodies++; // Increment atomic after setting the body ID so that PhysicsSystem::JobFindCollisions (which doesn't lock the mActiveBodiesMutex) will only read valid IDs - - // Count CCD bodies - if (mp->GetMotionQuality() == EMotionQuality::LinearCast) - mNumActiveCCDBodies++; -} - -void BodyManager::RemoveBodyFromActiveBodies(Body &ioBody) -{ - // Select the correct array to use - int type = (int)ioBody.GetBodyType(); - atomic &num_active_bodies = mNumActiveBodies[type]; - BodyID *active_bodies = mActiveBodies[type]; - - uint32 last_body_index = num_active_bodies - 1; - MotionProperties *mp = ioBody.mMotionProperties; - if (mp->mIndexInActiveBodies != last_body_index) - { - // This is not the last body, use the last body to fill the hole - BodyID last_body_id = active_bodies[last_body_index]; - active_bodies[mp->mIndexInActiveBodies] = last_body_id; - - // Update that body's index in the active list - Body &last_body = *mBodies[last_body_id.GetIndex()]; - JPH_ASSERT(last_body.mMotionProperties->mIndexInActiveBodies == last_body_index); - last_body.mMotionProperties->mIndexInActiveBodies = mp->mIndexInActiveBodies; - } - - // Mark this body as no longer active - mp->mIndexInActiveBodies = Body::cInactiveIndex; - - // Remove unused element from active bodies list - --num_active_bodies; - - // Count CCD bodies - if (mp->GetMotionQuality() == EMotionQuality::LinearCast) - mNumActiveCCDBodies--; -} - -void BodyManager::ActivateBodies(const BodyID *inBodyIDs, int inNumber) -{ - // Don't take lock if no bodies are to be activated - if (inNumber <= 0) - return; - - UniqueLock lock(mActiveBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::ActiveBodiesList)); - - JPH_ASSERT(!mActiveBodiesLocked || sOverrideAllowActivation); - - for (const BodyID *b = inBodyIDs, *b_end = inBodyIDs + inNumber; b < b_end; b++) - if (!b->IsInvalid()) - { - BodyID body_id = *b; - Body &body = *mBodies[body_id.GetIndex()]; - - JPH_ASSERT(body.GetID() == body_id); - JPH_ASSERT(body.IsInBroadPhase()); - - if (!body.IsStatic() - && body.mMotionProperties->mIndexInActiveBodies == Body::cInactiveIndex) - { - // Reset sleeping - body.ResetSleepTimer(); - - AddBodyToActiveBodies(body); - - // Call activation listener - if (mActivationListener != nullptr) - mActivationListener->OnBodyActivated(body_id, body.GetUserData()); - } - } -} - -void BodyManager::DeactivateBodies(const BodyID *inBodyIDs, int inNumber) -{ - // Don't take lock if no bodies are to be deactivated - if (inNumber <= 0) - return; - - UniqueLock lock(mActiveBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::ActiveBodiesList)); - - JPH_ASSERT(!mActiveBodiesLocked || sOverrideAllowDeactivation); - - for (const BodyID *b = inBodyIDs, *b_end = inBodyIDs + inNumber; b < b_end; b++) - if (!b->IsInvalid()) - { - BodyID body_id = *b; - Body &body = *mBodies[body_id.GetIndex()]; - - JPH_ASSERT(body.GetID() == body_id); - JPH_ASSERT(body.IsInBroadPhase()); - - if (body.mMotionProperties != nullptr - && body.mMotionProperties->mIndexInActiveBodies != Body::cInactiveIndex) - { - // Remove the body from the active bodies list - RemoveBodyFromActiveBodies(body); - - // Mark this body as no longer active - body.mMotionProperties->mIslandIndex = Body::cInactiveIndex; - - // Reset velocity - body.mMotionProperties->mLinearVelocity = Vec3::sZero(); - body.mMotionProperties->mAngularVelocity = Vec3::sZero(); - - // Call activation listener - if (mActivationListener != nullptr) - mActivationListener->OnBodyDeactivated(body_id, body.GetUserData()); - } - } -} - -void BodyManager::SetMotionQuality(Body &ioBody, EMotionQuality inMotionQuality) -{ - MotionProperties *mp = ioBody.GetMotionPropertiesUnchecked(); - if (mp != nullptr && mp->GetMotionQuality() != inMotionQuality) - { - UniqueLock lock(mActiveBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::ActiveBodiesList)); - - JPH_ASSERT(!mActiveBodiesLocked); - - bool is_active = ioBody.IsActive(); - if (is_active && mp->GetMotionQuality() == EMotionQuality::LinearCast) - --mNumActiveCCDBodies; - - mp->mMotionQuality = inMotionQuality; - - if (is_active && mp->GetMotionQuality() == EMotionQuality::LinearCast) - ++mNumActiveCCDBodies; - } -} - -void BodyManager::GetActiveBodies(EBodyType inType, BodyIDVector &outBodyIDs) const -{ - JPH_PROFILE_FUNCTION(); - - UniqueLock lock(mActiveBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::ActiveBodiesList)); - - const BodyID *active_bodies = mActiveBodies[(int)inType]; - outBodyIDs.assign(active_bodies, active_bodies + mNumActiveBodies[(int)inType]); -} - -void BodyManager::GetBodyIDs(BodyIDVector &outBodies) const -{ - JPH_PROFILE_FUNCTION(); - - UniqueLock lock(mBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::BodiesList)); - - // Reserve space for all bodies - outBodies.clear(); - outBodies.reserve(mNumBodies); - - // Iterate the list and find the bodies that are not null - for (const Body *b : mBodies) - if (sIsValidBodyPointer(b)) - outBodies.push_back(b->GetID()); - - // Validate that our reservation was correct - JPH_ASSERT(outBodies.size() == mNumBodies); -} - -void BodyManager::SetBodyActivationListener(BodyActivationListener *inListener) -{ - UniqueLock lock(mActiveBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::ActiveBodiesList)); - - mActivationListener = inListener; -} - -BodyManager::MutexMask BodyManager::GetMutexMask(const BodyID *inBodies, int inNumber) const -{ - JPH_ASSERT(sizeof(MutexMask) * 8 >= mBodyMutexes.GetNumMutexes(), "MutexMask must have enough bits"); - - if (inNumber >= (int)mBodyMutexes.GetNumMutexes()) - { - // Just lock everything if there are too many bodies - return GetAllBodiesMutexMask(); - } - else - { - MutexMask mask = 0; - for (const BodyID *b = inBodies, *b_end = inBodies + inNumber; b < b_end; ++b) - if (!b->IsInvalid()) - { - uint32 index = mBodyMutexes.GetMutexIndex(b->GetIndex()); - mask |= (MutexMask(1) << index); - } - return mask; - } -} - -void BodyManager::LockRead(MutexMask inMutexMask) const -{ - JPH_IF_ENABLE_ASSERTS(PhysicsLock::sCheckLock(this, EPhysicsLockTypes::PerBody)); - - int index = 0; - for (MutexMask mask = inMutexMask; mask != 0; mask >>= 1, index++) - if (mask & 1) - mBodyMutexes.GetMutexByIndex(index).lock_shared(); -} - -void BodyManager::UnlockRead(MutexMask inMutexMask) const -{ - JPH_IF_ENABLE_ASSERTS(PhysicsLock::sCheckUnlock(this, EPhysicsLockTypes::PerBody)); - - int index = 0; - for (MutexMask mask = inMutexMask; mask != 0; mask >>= 1, index++) - if (mask & 1) - mBodyMutexes.GetMutexByIndex(index).unlock_shared(); -} - -void BodyManager::LockWrite(MutexMask inMutexMask) const -{ - JPH_IF_ENABLE_ASSERTS(PhysicsLock::sCheckLock(this, EPhysicsLockTypes::PerBody)); - - int index = 0; - for (MutexMask mask = inMutexMask; mask != 0; mask >>= 1, index++) - if (mask & 1) - mBodyMutexes.GetMutexByIndex(index).lock(); -} - -void BodyManager::UnlockWrite(MutexMask inMutexMask) const -{ - JPH_IF_ENABLE_ASSERTS(PhysicsLock::sCheckUnlock(this, EPhysicsLockTypes::PerBody)); - - int index = 0; - for (MutexMask mask = inMutexMask; mask != 0; mask >>= 1, index++) - if (mask & 1) - mBodyMutexes.GetMutexByIndex(index).unlock(); -} - -void BodyManager::LockAllBodies() const -{ - JPH_IF_ENABLE_ASSERTS(PhysicsLock::sCheckLock(this, EPhysicsLockTypes::PerBody)); - mBodyMutexes.LockAll(); - - PhysicsLock::sLock(mBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::BodiesList)); -} - -void BodyManager::UnlockAllBodies() const -{ - PhysicsLock::sUnlock(mBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::BodiesList)); - - JPH_IF_ENABLE_ASSERTS(PhysicsLock::sCheckUnlock(this, EPhysicsLockTypes::PerBody)); - mBodyMutexes.UnlockAll(); -} - -void BodyManager::SaveState(StateRecorder &inStream, const StateRecorderFilter *inFilter) const -{ - { - LockAllBodies(); - - // Determine which bodies to save - Array bodies; - bodies.reserve(mNumBodies); - for (const Body *b : mBodies) - if (sIsValidBodyPointer(b) && b->IsInBroadPhase() && (inFilter == nullptr || inFilter->ShouldSaveBody(*b))) - bodies.push_back(b); - - // Write state of bodies - uint32 num_bodies = (uint32)bodies.size(); - inStream.Write(num_bodies); - for (const Body *b : bodies) - { - inStream.Write(b->GetID()); - inStream.Write(b->IsActive()); - b->SaveState(inStream); - } - - UnlockAllBodies(); - } -} - -bool BodyManager::RestoreState(StateRecorder &inStream) -{ - BodyIDVector bodies_to_activate, bodies_to_deactivate; - - { - LockAllBodies(); - - if (inStream.IsValidating()) - { - // Read state of bodies, note this reads it in a way to be consistent with validation - uint32 old_num_bodies = 0; - for (const Body *b : mBodies) - if (sIsValidBodyPointer(b) && b->IsInBroadPhase()) - ++old_num_bodies; - uint32 num_bodies = old_num_bodies; // Initialize to current value for validation - inStream.Read(num_bodies); - if (num_bodies != old_num_bodies) - { - JPH_ASSERT(false, "Cannot handle adding/removing bodies"); - UnlockAllBodies(); - return false; - } - - for (Body *b : mBodies) - if (sIsValidBodyPointer(b) && b->IsInBroadPhase()) - { - BodyID body_id = b->GetID(); // Initialize to current value for validation - inStream.Read(body_id); - if (body_id != b->GetID()) - { - JPH_ASSERT(false, "Cannot handle adding/removing bodies"); - UnlockAllBodies(); - return false; - } - bool is_active = b->IsActive(); // Initialize to current value for validation - inStream.Read(is_active); - if (is_active != b->IsActive()) - { - if (is_active) - bodies_to_activate.push_back(body_id); - else - bodies_to_deactivate.push_back(body_id); - } - b->RestoreState(inStream); - } - } - else - { - // Not validating, we can be a bit more loose, read number of bodies - uint32 num_bodies = 0; - inStream.Read(num_bodies); - - // Iterate over the stored bodies and restore their state - for (uint32 idx = 0; idx < num_bodies; ++idx) - { - BodyID body_id; - inStream.Read(body_id); - Body *b = TryGetBody(body_id); - if (b == nullptr) - { - JPH_ASSERT(false, "Restoring state for non-existing body"); - UnlockAllBodies(); - return false; - } - bool is_active; - inStream.Read(is_active); - if (is_active != b->IsActive()) - { - if (is_active) - bodies_to_activate.push_back(body_id); - else - bodies_to_deactivate.push_back(body_id); - } - b->RestoreState(inStream); - } - } - - UnlockAllBodies(); - } - - { - UniqueLock lock(mActiveBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::ActiveBodiesList)); - - for (BodyID body_id : bodies_to_activate) - { - Body *body = TryGetBody(body_id); - AddBodyToActiveBodies(*body); - } - - for (BodyID body_id : bodies_to_deactivate) - { - Body *body = TryGetBody(body_id); - RemoveBodyFromActiveBodies(*body); - } - } - - return true; -} - -void BodyManager::SaveBodyState(const Body &inBody, StateRecorder &inStream) const -{ - inStream.Write(inBody.IsActive()); - - inBody.SaveState(inStream); -} - -void BodyManager::RestoreBodyState(Body &ioBody, StateRecorder &inStream) -{ - bool is_active = ioBody.IsActive(); - inStream.Read(is_active); - - ioBody.RestoreState(inStream); - - if (is_active != ioBody.IsActive()) - { - UniqueLock lock(mActiveBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::ActiveBodiesList)); - - JPH_ASSERT(!mActiveBodiesLocked || sOverrideAllowActivation); - - if (is_active) - AddBodyToActiveBodies(ioBody); - else - RemoveBodyFromActiveBodies(ioBody); - } -} - -#ifdef JPH_DEBUG_RENDERER -void BodyManager::Draw(const DrawSettings &inDrawSettings, const PhysicsSettings &inPhysicsSettings, DebugRenderer *inRenderer, const BodyDrawFilter *inBodyFilter) -{ - JPH_PROFILE_FUNCTION(); - - LockAllBodies(); - - for (const Body *body : mBodies) - if (sIsValidBodyPointer(body) && body->IsInBroadPhase() && (!inBodyFilter || inBodyFilter->ShouldDraw(*body))) - { - JPH_ASSERT(mBodies[body->GetID().GetIndex()] == body); - - bool is_sensor = body->IsSensor(); - - // Determine drawing mode - Color color; - if (is_sensor) - color = Color::sYellow; - else - switch (inDrawSettings.mDrawShapeColor) - { - case EShapeColor::InstanceColor: - // Each instance has own color - color = Color::sGetDistinctColor(body->mID.GetIndex()); - break; - - case EShapeColor::ShapeTypeColor: - color = ShapeFunctions::sGet(body->GetShape()->GetSubType()).mColor; - break; - - case EShapeColor::MotionTypeColor: - // Determine color based on motion type - switch (body->mMotionType) - { - case EMotionType::Static: - color = Color::sGrey; - break; - - case EMotionType::Kinematic: - color = Color::sGreen; - break; - - case EMotionType::Dynamic: - color = Color::sGetDistinctColor(body->mID.GetIndex()); - break; - - default: - JPH_ASSERT(false); - color = Color::sBlack; - break; - } - break; - - case EShapeColor::SleepColor: - // Determine color based on motion type - switch (body->mMotionType) - { - case EMotionType::Static: - color = Color::sGrey; - break; - - case EMotionType::Kinematic: - color = body->IsActive()? Color::sGreen : Color::sRed; - break; - - case EMotionType::Dynamic: - color = body->IsActive()? Color::sYellow : Color::sRed; - break; - - default: - JPH_ASSERT(false); - color = Color::sBlack; - break; - } - break; - - case EShapeColor::IslandColor: - // Determine color based on motion type - switch (body->mMotionType) - { - case EMotionType::Static: - color = Color::sGrey; - break; - - case EMotionType::Kinematic: - case EMotionType::Dynamic: - { - uint32 idx = body->GetMotionProperties()->GetIslandIndexInternal(); - color = idx != Body::cInactiveIndex? Color::sGetDistinctColor(idx) : Color::sLightGrey; - } - break; - - default: - JPH_ASSERT(false); - color = Color::sBlack; - break; - } - break; - - case EShapeColor::MaterialColor: - color = Color::sWhite; - break; - - default: - JPH_ASSERT(false); - color = Color::sBlack; - break; - } - - // Draw the results of GetSupportFunction - if (inDrawSettings.mDrawGetSupportFunction) - body->mShape->DrawGetSupportFunction(inRenderer, body->GetCenterOfMassTransform(), Vec3::sReplicate(1.0f), color, inDrawSettings.mDrawSupportDirection); - - // Draw the results of GetSupportingFace - if (inDrawSettings.mDrawGetSupportingFace) - body->mShape->DrawGetSupportingFace(inRenderer, body->GetCenterOfMassTransform(), Vec3::sReplicate(1.0f)); - - // Draw the shape - if (inDrawSettings.mDrawShape) - body->mShape->Draw(inRenderer, body->GetCenterOfMassTransform(), Vec3::sReplicate(1.0f), color, inDrawSettings.mDrawShapeColor == EShapeColor::MaterialColor, inDrawSettings.mDrawShapeWireframe || is_sensor); - - // Draw bounding box - if (inDrawSettings.mDrawBoundingBox) - inRenderer->DrawWireBox(body->mBounds, color); - - // Draw center of mass transform - if (inDrawSettings.mDrawCenterOfMassTransform) - inRenderer->DrawCoordinateSystem(body->GetCenterOfMassTransform(), 0.2f); - - // Draw world transform - if (inDrawSettings.mDrawWorldTransform) - inRenderer->DrawCoordinateSystem(body->GetWorldTransform(), 0.2f); - - // Draw world space linear and angular velocity - if (inDrawSettings.mDrawVelocity) - { - RVec3 pos = body->GetCenterOfMassPosition(); - inRenderer->DrawArrow(pos, pos + body->GetLinearVelocity(), Color::sGreen, 0.1f); - inRenderer->DrawArrow(pos, pos + body->GetAngularVelocity(), Color::sRed, 0.1f); - } - - if (inDrawSettings.mDrawMassAndInertia && body->IsDynamic()) - { - const MotionProperties *mp = body->GetMotionProperties(); - if (mp->GetInverseMass() > 0.0f - && !Vec3::sEquals(mp->GetInverseInertiaDiagonal(), Vec3::sZero()).TestAnyXYZTrue()) - { - // Invert mass again - float mass = 1.0f / mp->GetInverseMass(); - - // Invert diagonal again - Vec3 diagonal = mp->GetInverseInertiaDiagonal().Reciprocal(); - - // Determine how big of a box has the equivalent inertia - Vec3 box_size = MassProperties::sGetEquivalentSolidBoxSize(mass, diagonal); - - // Draw box with equivalent inertia - inRenderer->DrawWireBox(body->GetCenterOfMassTransform() * Mat44::sRotation(mp->GetInertiaRotation()), AABox(-0.5f * box_size, 0.5f * box_size), Color::sOrange); - - // Draw mass - inRenderer->DrawText3D(body->GetCenterOfMassPosition(), StringFormat("%.2f", (double)mass), Color::sOrange, 0.2f); - } - } - - if (inDrawSettings.mDrawSleepStats && body->IsDynamic() && body->IsActive()) - { - // Draw stats to know which bodies could go to sleep - String text = StringFormat("t: %.1f", (double)body->mMotionProperties->mSleepTestTimer); - uint8 g = uint8(Clamp(255.0f * body->mMotionProperties->mSleepTestTimer / inPhysicsSettings.mTimeBeforeSleep, 0.0f, 255.0f)); - Color sleep_color = Color(0, 255 - g, g); - inRenderer->DrawText3D(body->GetCenterOfMassPosition(), text, sleep_color, 0.2f); - for (int i = 0; i < 3; ++i) - inRenderer->DrawWireSphere(JPH_IF_DOUBLE_PRECISION(body->mMotionProperties->GetSleepTestOffset() +) body->mMotionProperties->mSleepTestSpheres[i].GetCenter(), body->mMotionProperties->mSleepTestSpheres[i].GetRadius(), sleep_color); - } - - if (body->IsSoftBody()) - { - const SoftBodyMotionProperties *mp = static_cast(body->GetMotionProperties()); - RMat44 com = body->GetCenterOfMassTransform(); - - if (inDrawSettings.mDrawSoftBodyVertices) - mp->DrawVertices(inRenderer, com); - - if (inDrawSettings.mDrawSoftBodyVertexVelocities) - mp->DrawVertexVelocities(inRenderer, com); - - if (inDrawSettings.mDrawSoftBodyEdgeConstraints) - mp->DrawEdgeConstraints(inRenderer, com); - - if (inDrawSettings.mDrawSoftBodyBendConstraints) - mp->DrawBendConstraints(inRenderer, com); - - if (inDrawSettings.mDrawSoftBodyVolumeConstraints) - mp->DrawVolumeConstraints(inRenderer, com); - - if (inDrawSettings.mDrawSoftBodySkinConstraints) - mp->DrawSkinConstraints(inRenderer, com); - - if (inDrawSettings.mDrawSoftBodyLRAConstraints) - mp->DrawLRAConstraints(inRenderer, com); - - if (inDrawSettings.mDrawSoftBodyPredictedBounds) - mp->DrawPredictedBounds(inRenderer, com); - } - } - - UnlockAllBodies(); -} -#endif // JPH_DEBUG_RENDERER - -void BodyManager::InvalidateContactCacheForBody(Body &ioBody) -{ - // If this is the first time we flip the collision cache invalid flag, we need to add it to an internal list to ensure we reset the flag at the end of the physics update - if (ioBody.InvalidateContactCacheInternal()) - { - lock_guard lock(mBodiesCacheInvalidMutex); - mBodiesCacheInvalid.push_back(ioBody.GetID()); - } -} - -void BodyManager::ValidateContactCacheForAllBodies() -{ - lock_guard lock(mBodiesCacheInvalidMutex); - - for (const BodyID &b : mBodiesCacheInvalid) - { - // The body may have been removed between the call to InvalidateContactCacheForBody and this call, so check if it still exists - Body *body = TryGetBody(b); - if (body != nullptr) - body->ValidateContactCacheInternal(); - } - mBodiesCacheInvalid.clear(); -} - -#ifdef _DEBUG -void BodyManager::ValidateActiveBodyBounds() -{ - UniqueLock lock(mActiveBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::ActiveBodiesList)); - - for (uint type = 0; type < cBodyTypeCount; ++type) - for (BodyID *id = mActiveBodies[type], *id_end = mActiveBodies[type] + mNumActiveBodies[type]; id < id_end; ++id) - { - const Body *body = mBodies[id->GetIndex()]; - AABox cached = body->GetWorldSpaceBounds(); - AABox calculated = body->GetShape()->GetWorldSpaceBounds(body->GetCenterOfMassTransform(), Vec3::sReplicate(1.0f)); - JPH_ASSERT(cached == calculated); - } -} -#endif // _DEBUG - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyManager.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyManager.h deleted file mode 100644 index a1b859679c9..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyManager.h +++ /dev/null @@ -1,364 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -// Classes -class BodyCreationSettings; -class SoftBodyCreationSettings; -class BodyActivationListener; -class StateRecorderFilter; -struct PhysicsSettings; -#ifdef JPH_DEBUG_RENDERER -class DebugRenderer; -class BodyDrawFilter; -#endif // JPH_DEBUG_RENDERER - -/// Array of bodies -using BodyVector = Array; - -/// Array of body ID's -using BodyIDVector = Array; - -/// Class that contains all bodies -class JPH_EXPORT BodyManager : public NonCopyable -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Destructor - ~BodyManager(); - - /// Initialize the manager - void Init(uint inMaxBodies, uint inNumBodyMutexes, const BroadPhaseLayerInterface &inLayerInterface); - - /// Gets the current amount of bodies that are in the body manager - uint GetNumBodies() const; - - /// Gets the max bodies that we can support - uint GetMaxBodies() const { return uint(mBodies.capacity()); } - - /// Helper struct that counts the number of bodies of each type - struct BodyStats - { - uint mNumBodies = 0; ///< Total number of bodies in the body manager - uint mMaxBodies = 0; ///< Max allowed number of bodies in the body manager (as configured in Init(...)) - - uint mNumBodiesStatic = 0; ///< Number of static bodies - - uint mNumBodiesDynamic = 0; ///< Number of dynamic bodies - uint mNumActiveBodiesDynamic = 0; ///< Number of dynamic bodies that are currently active - - uint mNumBodiesKinematic = 0; ///< Number of kinematic bodies - uint mNumActiveBodiesKinematic = 0; ///< Number of kinematic bodies that are currently active - - uint mNumSoftBodies = 0; ///< Number of soft bodies - uint mNumActiveSoftBodies = 0; ///< Number of soft bodies that are currently active - }; - - /// Get stats about the bodies in the body manager (slow, iterates through all bodies) - BodyStats GetBodyStats() const; - - /// Create a body using creation settings. The returned body will not be part of the body manager yet. - Body * AllocateBody(const BodyCreationSettings &inBodyCreationSettings) const; - - /// Create a soft body using creation settings. The returned body will not be part of the body manager yet. - Body * AllocateSoftBody(const SoftBodyCreationSettings &inSoftBodyCreationSettings) const; - - /// Free a body that has not been added to the body manager yet (if it has, use DestroyBodies). - void FreeBody(Body *inBody) const; - - /// Add a body to the body manager, assigning it the next available ID. Returns false if no more IDs are available. - bool AddBody(Body *ioBody); - - /// Add a body to the body manager, assigning it a custom ID. Returns false if the ID is not valid. - bool AddBodyWithCustomID(Body *ioBody, const BodyID &inBodyID); - - /// Remove a list of bodies from the body manager - void RemoveBodies(const BodyID *inBodyIDs, int inNumber, Body **outBodies); - - /// Remove a set of bodies from the body manager and frees them. - void DestroyBodies(const BodyID *inBodyIDs, int inNumber); - - /// Activate a list of bodies. - /// This function should only be called when an exclusive lock for the bodies are held. - void ActivateBodies(const BodyID *inBodyIDs, int inNumber); - - /// Deactivate a list of bodies. - /// This function should only be called when an exclusive lock for the bodies are held. - void DeactivateBodies(const BodyID *inBodyIDs, int inNumber); - - /// Update the motion quality for a body - void SetMotionQuality(Body &ioBody, EMotionQuality inMotionQuality); - - /// Get copy of the list of active bodies under protection of a lock. - void GetActiveBodies(EBodyType inType, BodyIDVector &outBodyIDs) const; - - /// Get the list of active bodies. Note: Not thread safe. The active bodies list can change at any moment. - const BodyID * GetActiveBodiesUnsafe(EBodyType inType) const { return mActiveBodies[int(inType)]; } - - /// Get the number of active bodies. - uint32 GetNumActiveBodies(EBodyType inType) const { return mNumActiveBodies[int(inType)]; } - - /// Get the number of active bodies that are using continuous collision detection - uint32 GetNumActiveCCDBodies() const { return mNumActiveCCDBodies; } - - /// Listener that is notified whenever a body is activated/deactivated - void SetBodyActivationListener(BodyActivationListener *inListener); - BodyActivationListener * GetBodyActivationListener() const { return mActivationListener; } - - /// Check if this is a valid body pointer. When a body is freed the memory that the pointer occupies is reused to store a freelist. - static inline bool sIsValidBodyPointer(const Body *inBody) { return (uintptr_t(inBody) & cIsFreedBody) == 0; } - - /// Get all bodies. Note that this can contain invalid body pointers, call sIsValidBodyPointer to check. - const BodyVector & GetBodies() const { return mBodies; } - - /// Get all bodies. Note that this can contain invalid body pointers, call sIsValidBodyPointer to check. - BodyVector & GetBodies() { return mBodies; } - - /// Get all body IDs under the protection of a lock - void GetBodyIDs(BodyIDVector &outBodies) const; - - /// Access a body (not protected by lock) - const Body & GetBody(const BodyID &inID) const { return *mBodies[inID.GetIndex()]; } - - /// Access a body (not protected by lock) - Body & GetBody(const BodyID &inID) { return *mBodies[inID.GetIndex()]; } - - /// Access a body, will return a nullptr if the body ID is no longer valid (not protected by lock) - const Body * TryGetBody(const BodyID &inID) const - { - uint32 idx = inID.GetIndex(); - if (idx >= mBodies.size()) - return nullptr; - - const Body *body = mBodies[idx]; - if (sIsValidBodyPointer(body) && body->GetID() == inID) - return body; - - return nullptr; - } - - /// Access a body, will return a nullptr if the body ID is no longer valid (not protected by lock) - Body * TryGetBody(const BodyID &inID) - { - uint32 idx = inID.GetIndex(); - if (idx >= mBodies.size()) - return nullptr; - - Body *body = mBodies[idx]; - if (sIsValidBodyPointer(body) && body->GetID() == inID) - return body; - - return nullptr; - } - - /// Access the mutex for a single body - SharedMutex & GetMutexForBody(const BodyID &inID) const { return mBodyMutexes.GetMutexByObjectIndex(inID.GetIndex()); } - - /// Bodies are protected using an array of mutexes (so a fixed number, not 1 per body). Each bit in this mask indicates a locked mutex. - using MutexMask = uint64; - - ///@name Batch body mutex access (do not use directly) - ///@{ - MutexMask GetAllBodiesMutexMask() const { return mBodyMutexes.GetNumMutexes() == sizeof(MutexMask) * 8? ~MutexMask(0) : (MutexMask(1) << mBodyMutexes.GetNumMutexes()) - 1; } - MutexMask GetMutexMask(const BodyID *inBodies, int inNumber) const; - void LockRead(MutexMask inMutexMask) const; - void UnlockRead(MutexMask inMutexMask) const; - void LockWrite(MutexMask inMutexMask) const; - void UnlockWrite(MutexMask inMutexMask) const; - ///@} - - /// Lock all bodies. This should only be done during PhysicsSystem::Update(). - void LockAllBodies() const; - - /// Unlock all bodies. This should only be done during PhysicsSystem::Update(). - void UnlockAllBodies() const; - - /// Function to update body's layer (should only be called by the BodyInterface since it also requires updating the broadphase) - inline void SetBodyObjectLayerInternal(Body &ioBody, ObjectLayer inLayer) const { ioBody.mObjectLayer = inLayer; ioBody.mBroadPhaseLayer = mBroadPhaseLayerInterface->GetBroadPhaseLayer(inLayer); } - - /// Set the Body::EFlags::InvalidateContactCache flag for the specified body. This means that the collision cache is invalid for any body pair involving that body until the next physics step. - void InvalidateContactCacheForBody(Body &ioBody); - - /// Reset the Body::EFlags::InvalidateContactCache flag for all bodies. All contact pairs in the contact cache will now by valid again. - void ValidateContactCacheForAllBodies(); - - /// Saving state for replay - void SaveState(StateRecorder &inStream, const StateRecorderFilter *inFilter) const; - - /// Restoring state for replay. Returns false if failed. - bool RestoreState(StateRecorder &inStream); - - /// Save the state of a single body for replay - void SaveBodyState(const Body &inBody, StateRecorder &inStream) const; - - /// Save the state of a single body for replay - void RestoreBodyState(Body &inBody, StateRecorder &inStream); - - enum class EShapeColor - { - InstanceColor, ///< Random color per instance - ShapeTypeColor, ///< Convex = green, scaled = yellow, compound = orange, mesh = red - MotionTypeColor, ///< Static = grey, keyframed = green, dynamic = random color per instance - SleepColor, ///< Static = grey, keyframed = green, dynamic = yellow, sleeping = red - IslandColor, ///< Static = grey, active = random color per island, sleeping = light grey - MaterialColor, ///< Color as defined by the PhysicsMaterial of the shape - }; - -#ifdef JPH_DEBUG_RENDERER - /// Draw settings - struct DrawSettings - { - bool mDrawGetSupportFunction = false; ///< Draw the GetSupport() function, used for convex collision detection - bool mDrawSupportDirection = false; ///< When drawing the support function, also draw which direction mapped to a specific support point - bool mDrawGetSupportingFace = false; ///< Draw the faces that were found colliding during collision detection - bool mDrawShape = true; ///< Draw the shapes of all bodies - bool mDrawShapeWireframe = false; ///< When mDrawShape is true and this is true, the shapes will be drawn in wireframe instead of solid. - EShapeColor mDrawShapeColor = EShapeColor::MotionTypeColor; ///< Coloring scheme to use for shapes - bool mDrawBoundingBox = false; ///< Draw a bounding box per body - bool mDrawCenterOfMassTransform = false; ///< Draw the center of mass for each body - bool mDrawWorldTransform = false; ///< Draw the world transform (which can be different than the center of mass) for each body - bool mDrawVelocity = false; ///< Draw the velocity vector for each body - bool mDrawMassAndInertia = false; ///< Draw the mass and inertia (as the box equivalent) for each body - bool mDrawSleepStats = false; ///< Draw stats regarding the sleeping algorithm of each body - bool mDrawSoftBodyVertices = false; ///< Draw the vertices of soft bodies - bool mDrawSoftBodyVertexVelocities = false; ///< Draw the velocities of the vertices of soft bodies - bool mDrawSoftBodyEdgeConstraints = false; ///< Draw the edge constraints of soft bodies - bool mDrawSoftBodyBendConstraints = false; ///< Draw the bend constraints of soft bodies - bool mDrawSoftBodyVolumeConstraints = false; ///< Draw the volume constraints of soft bodies - bool mDrawSoftBodySkinConstraints = false; ///< Draw the skin constraints of soft bodies - bool mDrawSoftBodyLRAConstraints = false; ///< Draw the LRA constraints of soft bodies - bool mDrawSoftBodyPredictedBounds = false; ///< Draw the predicted bounds of soft bodies - }; - - /// Draw the state of the bodies (debugging purposes) - void Draw(const DrawSettings &inSettings, const PhysicsSettings &inPhysicsSettings, DebugRenderer *inRenderer, const BodyDrawFilter *inBodyFilter = nullptr); -#endif // JPH_DEBUG_RENDERER - -#ifdef JPH_ENABLE_ASSERTS - /// Lock the active body list, asserts when Activate/DeactivateBody is called. - void SetActiveBodiesLocked(bool inLocked) { mActiveBodiesLocked = inLocked; } - - /// Per thread override of the locked state, to be used by the PhysicsSystem only! - class GrantActiveBodiesAccess - { - public: - inline GrantActiveBodiesAccess(bool inAllowActivation, bool inAllowDeactivation) - { - JPH_ASSERT(!sGetOverrideAllowActivation()); - sSetOverrideAllowActivation(inAllowActivation); - - JPH_ASSERT(!sGetOverrideAllowDeactivation()); - sSetOverrideAllowDeactivation(inAllowDeactivation); - } - - inline ~GrantActiveBodiesAccess() - { - sSetOverrideAllowActivation(false); - sSetOverrideAllowDeactivation(false); - } - }; -#endif - -#ifdef _DEBUG - /// Validate if the cached bounding boxes are correct for all active bodies - void ValidateActiveBodyBounds(); -#endif // _DEBUG - -private: - /// Increment and get the sequence number of the body -#ifdef JPH_COMPILER_CLANG - __attribute__((no_sanitize("implicit-conversion"))) // We intentionally overflow the uint8 sequence number -#endif - inline uint8 GetNextSequenceNumber(int inBodyIndex) { return ++mBodySequenceNumbers[inBodyIndex]; } - - /// Add a single body to mActiveBodies, note doesn't lock the active body mutex! - inline void AddBodyToActiveBodies(Body &ioBody); - - /// Remove a single body from mActiveBodies, note doesn't lock the active body mutex! - inline void RemoveBodyFromActiveBodies(Body &ioBody); - - /// Helper function to remove a body from the manager - JPH_INLINE Body * RemoveBodyInternal(const BodyID &inBodyID); - - /// Helper function to delete a body (which could actually be a BodyWithMotionProperties) - inline static void sDeleteBody(Body *inBody); - -#if defined(_DEBUG) && defined(JPH_ENABLE_ASSERTS) - /// Function to check that the free list is not corrupted - void ValidateFreeList() const; -#endif // defined(_DEBUG) && _defined(JPH_ENABLE_ASSERTS) - - /// List of pointers to all bodies. Contains invalid pointers for deleted bodies, check with sIsValidBodyPointer. Note that this array is reserved to the max bodies that is passed in the Init function so that adding bodies will not reallocate the array. - BodyVector mBodies; - - /// Current number of allocated bodies - uint mNumBodies = 0; - - /// Indicates that there are no more freed body IDs - static constexpr uintptr_t cBodyIDFreeListEnd = ~uintptr_t(0); - - /// Bit that indicates a pointer in mBodies is actually the index of the next freed body. We use the lowest bit because we know that Bodies need to be 16 byte aligned so addresses can never end in a 1 bit. - static constexpr uintptr_t cIsFreedBody = uintptr_t(1); - - /// Amount of bits to shift to get an index to the next freed body - static constexpr uint cFreedBodyIndexShift = 1; - - /// Index of first entry in mBodies that is unused - uintptr_t mBodyIDFreeListStart = cBodyIDFreeListEnd; - - /// Protects mBodies array (but not the bodies it points to), mNumBodies and mBodyIDFreeListStart - mutable Mutex mBodiesMutex; - - /// An array of mutexes protecting the bodies in the mBodies array - using BodyMutexes = MutexArray; - mutable BodyMutexes mBodyMutexes; - - /// List of next sequence number for a body ID - Array mBodySequenceNumbers; - - /// Mutex that protects the mActiveBodies array - mutable Mutex mActiveBodiesMutex; - - /// List of all active dynamic bodies (size is equal to max amount of bodies) - BodyID * mActiveBodies[cBodyTypeCount] = { }; - - /// How many bodies there are in the list of active bodies - atomic mNumActiveBodies[cBodyTypeCount] = { }; - - /// How many of the active bodies have continuous collision detection enabled - uint32 mNumActiveCCDBodies = 0; - - /// Mutex that protects the mBodiesCacheInvalid array - mutable Mutex mBodiesCacheInvalidMutex; - - /// List of all bodies that should have their cache invalidated - BodyIDVector mBodiesCacheInvalid; - - /// Listener that is notified whenever a body is activated/deactivated - BodyActivationListener * mActivationListener = nullptr; - - /// Cached broadphase layer interface - const BroadPhaseLayerInterface *mBroadPhaseLayerInterface = nullptr; - -#ifdef JPH_ENABLE_ASSERTS - static bool sGetOverrideAllowActivation(); - static void sSetOverrideAllowActivation(bool inValue); - - static bool sGetOverrideAllowDeactivation(); - static void sSetOverrideAllowDeactivation(bool inValue); - - /// Debug system that tries to limit changes to active bodies during the PhysicsSystem::Update() - bool mActiveBodiesLocked = false; -#endif -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyPair.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyPair.h deleted file mode 100644 index 8ac849a49a8..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyPair.h +++ /dev/null @@ -1,36 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include - -JPH_NAMESPACE_BEGIN - -/// Structure that holds a body pair -struct alignas(uint64) BodyPair -{ - JPH_OVERRIDE_NEW_DELETE - - /// Constructor - BodyPair() = default; - BodyPair(BodyID inA, BodyID inB) : mBodyA(inA), mBodyB(inB) { } - - /// Equals operator - bool operator == (const BodyPair &inRHS) const { return *reinterpret_cast(this) == *reinterpret_cast(&inRHS); } - - /// Smaller than operator, used for consistently ordering body pairs - bool operator < (const BodyPair &inRHS) const { return *reinterpret_cast(this) < *reinterpret_cast(&inRHS); } - - /// Get the hash value of this object - uint64 GetHash() const { return Hash64(*reinterpret_cast(this)); } - - BodyID mBodyA; - BodyID mBodyB; -}; - -static_assert(sizeof(BodyPair) == sizeof(uint64), "Mismatch in class size"); - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyType.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyType.h deleted file mode 100644 index 984af06ad23..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/BodyType.h +++ /dev/null @@ -1,19 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2023 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -JPH_NAMESPACE_BEGIN - -/// Type of body -enum class EBodyType : uint8 -{ - RigidBody, ///< Rigid body consisting of a rigid shape - SoftBody, ///< Soft body consisting of a deformable shape -}; - -/// How many types of bodies there are -static constexpr uint cBodyTypeCount = 2; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/MassProperties.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/MassProperties.cpp deleted file mode 100644 index 91df6b0df3a..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/MassProperties.cpp +++ /dev/null @@ -1,185 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(MassProperties) -{ - JPH_ADD_ATTRIBUTE(MassProperties, mMass) - JPH_ADD_ATTRIBUTE(MassProperties, mInertia) -} - -bool MassProperties::DecomposePrincipalMomentsOfInertia(Mat44 &outRotation, Vec3 &outDiagonal) const -{ - // Using eigendecomposition to get the principal components of the inertia tensor - // See: https://en.wikipedia.org/wiki/Eigendecomposition_of_a_matrix - Matrix<3, 3> inertia; - inertia.CopyPart(mInertia, 0, 0, 3, 3, 0, 0); - Matrix<3, 3> eigen_vec = Matrix<3, 3>::sIdentity(); - Vector<3> eigen_val; - if (!EigenValueSymmetric(inertia, eigen_vec, eigen_val)) - return false; - - // Sort so that the biggest value goes first - int indices[] = { 0, 1, 2 }; - InsertionSort(indices, indices + 3, [&eigen_val](int inLeft, int inRight) { return eigen_val[inLeft] > eigen_val[inRight]; }); - - // Convert to a regular Mat44 and Vec3 - outRotation = Mat44::sIdentity(); - for (int i = 0; i < 3; ++i) - { - outRotation.SetColumn3(i, Vec3(reinterpret_cast(eigen_vec.GetColumn(indices[i])))); - outDiagonal.SetComponent(i, eigen_val[indices[i]]); - } - - // Make sure that the rotation matrix is a right handed matrix - if (outRotation.GetAxisX().Cross(outRotation.GetAxisY()).Dot(outRotation.GetAxisZ()) < 0.0f) - outRotation.SetAxisZ(-outRotation.GetAxisZ()); - -#ifdef JPH_ENABLE_ASSERTS - // Validate that the solution is correct, for each axis we want to make sure that the difference in inertia is - // smaller than some fraction of the inertia itself in that axis - Mat44 new_inertia = outRotation * Mat44::sScale(outDiagonal) * outRotation.Inversed(); - for (int i = 0; i < 3; ++i) - JPH_ASSERT(new_inertia.GetColumn3(i).IsClose(mInertia.GetColumn3(i), mInertia.GetColumn3(i).LengthSq() * 1.0e-10f)); -#endif - - return true; -} - -void MassProperties::SetMassAndInertiaOfSolidBox(Vec3Arg inBoxSize, float inDensity) -{ - // Calculate mass - mMass = inBoxSize.GetX() * inBoxSize.GetY() * inBoxSize.GetZ() * inDensity; - - // Calculate inertia - Vec3 size_sq = inBoxSize * inBoxSize; - Vec3 scale = (size_sq.Swizzle() + size_sq.Swizzle()) * (mMass / 12.0f); - mInertia = Mat44::sScale(scale); -} - -void MassProperties::ScaleToMass(float inMass) -{ - if (mMass > 0.0f) - { - // Calculate how much we have to scale the inertia tensor - float mass_scale = inMass / mMass; - - // Update mass - mMass = inMass; - - // Update inertia tensor - for (int i = 0; i < 3; ++i) - mInertia.SetColumn4(i, mInertia.GetColumn4(i) * mass_scale); - } - else - { - // Just set the mass - mMass = inMass; - } -} - -Vec3 MassProperties::sGetEquivalentSolidBoxSize(float inMass, Vec3Arg inInertiaDiagonal) -{ - // Moment of inertia of a solid box has diagonal: - // mass / 12 * [size_y^2 + size_z^2, size_x^2 + size_z^2, size_x^2 + size_y^2] - // Solving for size_x, size_y and size_y (diagonal and mass are known): - Vec3 diagonal = inInertiaDiagonal * (12.0f / inMass); - return Vec3(sqrt(0.5f * (-diagonal[0] + diagonal[1] + diagonal[2])), sqrt(0.5f * (diagonal[0] - diagonal[1] + diagonal[2])), sqrt(0.5f * (diagonal[0] + diagonal[1] - diagonal[2]))); -} - -void MassProperties::Scale(Vec3Arg inScale) -{ - // See: https://en.wikipedia.org/wiki/Moment_of_inertia#Inertia_tensor - // The diagonal of the inertia tensor can be calculated like this: - // Ixx = sum_{k = 1 to n}(m_k * (y_k^2 + z_k^2)) - // Iyy = sum_{k = 1 to n}(m_k * (x_k^2 + z_k^2)) - // Izz = sum_{k = 1 to n}(m_k * (x_k^2 + y_k^2)) - // - // We want to isolate the terms x_k, y_k and z_k: - // d = [0.5, 0.5, 0.5].[Ixx, Iyy, Izz] - // [sum_{k = 1 to n}(m_k * x_k^2), sum_{k = 1 to n}(m_k * y_k^2), sum_{k = 1 to n}(m_k * z_k^2)] = [d, d, d] - [Ixx, Iyy, Izz] - Vec3 diagonal = mInertia.GetDiagonal3(); - Vec3 xyz_sq = Vec3::sReplicate(Vec3::sReplicate(0.5f).Dot(diagonal)) - diagonal; - - // When scaling a shape these terms change like this: - // sum_{k = 1 to n}(m_k * (scale_x * x_k)^2) = scale_x^2 * sum_{k = 1 to n}(m_k * x_k^2) - // Same for y_k and z_k - // Using these terms we can calculate the new diagonal of the inertia tensor: - Vec3 xyz_scaled_sq = inScale * inScale * xyz_sq; - float i_xx = xyz_scaled_sq.GetY() + xyz_scaled_sq.GetZ(); - float i_yy = xyz_scaled_sq.GetX() + xyz_scaled_sq.GetZ(); - float i_zz = xyz_scaled_sq.GetX() + xyz_scaled_sq.GetY(); - - // The off diagonal elements are calculated like: - // Ixy = -sum_{k = 1 to n}(x_k y_k) - // Ixz = -sum_{k = 1 to n}(x_k z_k) - // Iyz = -sum_{k = 1 to n}(y_k z_k) - // Scaling these is simple: - float i_xy = inScale.GetX() * inScale.GetY() * mInertia(0, 1); - float i_xz = inScale.GetX() * inScale.GetZ() * mInertia(0, 2); - float i_yz = inScale.GetY() * inScale.GetZ() * mInertia(1, 2); - - // Update inertia tensor - mInertia(0, 0) = i_xx; - mInertia(0, 1) = i_xy; - mInertia(1, 0) = i_xy; - mInertia(1, 1) = i_yy; - mInertia(0, 2) = i_xz; - mInertia(2, 0) = i_xz; - mInertia(1, 2) = i_yz; - mInertia(2, 1) = i_yz; - mInertia(2, 2) = i_zz; - - // Mass scales linear with volume (note that the scaling can be negative and we don't want the mass to become negative) - float mass_scale = abs(inScale.GetX() * inScale.GetY() * inScale.GetZ()); - mMass *= mass_scale; - - // Inertia scales linear with mass. This updates the m_k terms above. - mInertia *= mass_scale; - - // Ensure that the bottom right element is a 1 again - mInertia(3, 3) = 1.0f; -} - -void MassProperties::Rotate(Mat44Arg inRotation) -{ - mInertia = inRotation.Multiply3x3(mInertia).Multiply3x3RightTransposed(inRotation); -} - -void MassProperties::Translate(Vec3Arg inTranslation) -{ - // Transform the inertia using the parallel axis theorem: I' = I + m * (translation^2 E - translation translation^T) - // Where I is the original body's inertia and E the identity matrix - // See: https://en.wikipedia.org/wiki/Parallel_axis_theorem - mInertia += mMass * (Mat44::sScale(inTranslation.Dot(inTranslation)) - Mat44::sOuterProduct(inTranslation, inTranslation)); - - // Ensure that inertia is a 3x3 matrix, adding inertias causes the bottom right element to change - mInertia.SetColumn4(3, Vec4(0, 0, 0, 1)); -} - -void MassProperties::SaveBinaryState(StreamOut &inStream) const -{ - inStream.Write(mMass); - inStream.Write(mInertia); -} - -void MassProperties::RestoreBinaryState(StreamIn &inStream) -{ - inStream.Read(mMass); - inStream.Read(mInertia); -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/MassProperties.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/MassProperties.h deleted file mode 100644 index a72a901db10..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/MassProperties.h +++ /dev/null @@ -1,58 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -class StreamIn; -class StreamOut; - -/// Describes the mass and inertia properties of a body. Used during body construction only. -class JPH_EXPORT MassProperties -{ -public: - JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, MassProperties) - - /// Using eigendecomposition, decompose the inertia tensor into a diagonal matrix D and a right-handed rotation matrix R so that the inertia tensor is \f$R \: D \: R^{-1}\f$. - /// @see https://en.wikipedia.org/wiki/Moment_of_inertia section 'Principal axes' - /// @param outRotation The rotation matrix R - /// @param outDiagonal The diagonal of the diagonal matrix D - /// @return True if successful, false if failed - bool DecomposePrincipalMomentsOfInertia(Mat44 &outRotation, Vec3 &outDiagonal) const; - - /// Set the mass and inertia of a box with edge size inBoxSize and density inDensity - void SetMassAndInertiaOfSolidBox(Vec3Arg inBoxSize, float inDensity); - - /// Set the mass and scale the inertia tensor to match the mass - void ScaleToMass(float inMass); - - /// Calculates the size of the solid box that has an inertia tensor diagonal inInertiaDiagonal - static Vec3 sGetEquivalentSolidBoxSize(float inMass, Vec3Arg inInertiaDiagonal); - - /// Rotate the inertia by 3x3 matrix inRotation - void Rotate(Mat44Arg inRotation); - - /// Translate the inertia by a vector inTranslation - void Translate(Vec3Arg inTranslation); - - /// Scale the mass and inertia by inScale, note that elements can be < 0 to flip the shape - void Scale(Vec3Arg inScale); - - /// Saves the state of this object in binary form to inStream. - void SaveBinaryState(StreamOut &inStream) const; - - /// Restore the state of this object from inStream. - void RestoreBinaryState(StreamIn &inStream); - - /// Mass of the shape (kg) - float mMass = 0.0f; - - /// Inertia tensor of the shape (kg m^2) - Mat44 mInertia = Mat44::sZero(); -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/MotionProperties.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/MotionProperties.cpp deleted file mode 100644 index 1acc274bb83..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/MotionProperties.cpp +++ /dev/null @@ -1,90 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include - -JPH_NAMESPACE_BEGIN - -void MotionProperties::SetMassProperties(EAllowedDOFs inAllowedDOFs, const MassProperties &inMassProperties) -{ - // Store allowed DOFs - mAllowedDOFs = inAllowedDOFs; - - // Decompose DOFs - uint allowed_translation_axis = uint(inAllowedDOFs) & 0b111; - uint allowed_rotation_axis = (uint(inAllowedDOFs) >> 3) & 0b111; - - // Set inverse mass - if (allowed_translation_axis == 0) - { - // No translation possible - mInvMass = 0.0f; - } - else - { - JPH_ASSERT(inMassProperties.mMass > 0.0f); - mInvMass = 1.0f / inMassProperties.mMass; - } - - if (allowed_rotation_axis == 0) - { - // No rotation possible - mInvInertiaDiagonal = Vec3::sZero(); - mInertiaRotation = Quat::sIdentity(); - } - else - { - // Set inverse inertia - Mat44 rotation; - Vec3 diagonal; - if (inMassProperties.DecomposePrincipalMomentsOfInertia(rotation, diagonal) - && !diagonal.IsNearZero()) - { - mInvInertiaDiagonal = diagonal.Reciprocal(); - mInertiaRotation = rotation.GetQuaternion(); - } - else - { - // Failed! Fall back to inertia tensor of sphere with radius 1. - mInvInertiaDiagonal = Vec3::sReplicate(2.5f * mInvMass); - mInertiaRotation = Quat::sIdentity(); - } - } - - JPH_ASSERT(mInvMass != 0.0f || mInvInertiaDiagonal != Vec3::sZero(), "Can't lock all axes, use a static body for this. This will crash with a division by zero later!"); -} - -void MotionProperties::SaveState(StateRecorder &inStream) const -{ - // Only write properties that can change at runtime - inStream.Write(mLinearVelocity); - inStream.Write(mAngularVelocity); - inStream.Write(mForce); - inStream.Write(mTorque); -#ifdef JPH_DOUBLE_PRECISION - inStream.Write(mSleepTestOffset); -#endif // JPH_DOUBLE_PRECISION - inStream.Write(mSleepTestSpheres); - inStream.Write(mSleepTestTimer); - inStream.Write(mAllowSleeping); -} - -void MotionProperties::RestoreState(StateRecorder &inStream) -{ - inStream.Read(mLinearVelocity); - inStream.Read(mAngularVelocity); - inStream.Read(mForce); - inStream.Read(mTorque); -#ifdef JPH_DOUBLE_PRECISION - inStream.Read(mSleepTestOffset); -#endif // JPH_DOUBLE_PRECISION - inStream.Read(mSleepTestSpheres); - inStream.Read(mSleepTestTimer); - inStream.Read(mAllowSleeping); -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/MotionProperties.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/MotionProperties.h deleted file mode 100644 index 2ed814843e9..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/MotionProperties.h +++ /dev/null @@ -1,278 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -class StateRecorder; - -/// Enum that determines if an object can go to sleep -enum class ECanSleep -{ - CannotSleep = 0, ///< Object cannot go to sleep - CanSleep = 1, ///< Object can go to sleep -}; - -/// The Body class only keeps track of state for static bodies, the MotionProperties class keeps the additional state needed for a moving Body. It has a 1-on-1 relationship with the body. -class JPH_EXPORT MotionProperties -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Motion quality, or how well it detects collisions when it has a high velocity - EMotionQuality GetMotionQuality() const { return mMotionQuality; } - - /// Get the allowed degrees of freedom that this body has (this can be changed by calling SetMassProperties) - inline EAllowedDOFs GetAllowedDOFs() const { return mAllowedDOFs; } - - /// If this body can go to sleep. - inline bool GetAllowSleeping() const { return mAllowSleeping; } - - /// Get world space linear velocity of the center of mass - inline Vec3 GetLinearVelocity() const { JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sVelocityAccess, BodyAccess::EAccess::Read)); return mLinearVelocity; } - - /// Set world space linear velocity of the center of mass - void SetLinearVelocity(Vec3Arg inLinearVelocity) { JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sVelocityAccess, BodyAccess::EAccess::ReadWrite)); JPH_ASSERT(inLinearVelocity.Length() <= mMaxLinearVelocity); mLinearVelocity = LockTranslation(inLinearVelocity); } - - /// Set world space linear velocity of the center of mass, will make sure the value is clamped against the maximum linear velocity - void SetLinearVelocityClamped(Vec3Arg inLinearVelocity) { mLinearVelocity = LockTranslation(inLinearVelocity); ClampLinearVelocity(); } - - /// Get world space angular velocity of the center of mass - inline Vec3 GetAngularVelocity() const { JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sVelocityAccess, BodyAccess::EAccess::Read)); return mAngularVelocity; } - - /// Set world space angular velocity of the center of mass - void SetAngularVelocity(Vec3Arg inAngularVelocity) { JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sVelocityAccess, BodyAccess::EAccess::ReadWrite)); JPH_ASSERT(inAngularVelocity.Length() <= mMaxAngularVelocity); mAngularVelocity = LockAngular(inAngularVelocity); } - - /// Set world space angular velocity of the center of mass, will make sure the value is clamped against the maximum angular velocity - void SetAngularVelocityClamped(Vec3Arg inAngularVelocity) { mAngularVelocity = LockAngular(inAngularVelocity); ClampAngularVelocity(); } - - /// Set velocity of body such that it will be rotate/translate by inDeltaPosition/Rotation in inDeltaTime seconds. - inline void MoveKinematic(Vec3Arg inDeltaPosition, QuatArg inDeltaRotation, float inDeltaTime); - - ///@name Velocity limits - ///@{ - - /// Maximum linear velocity that a body can achieve. Used to prevent the system from exploding. - inline float GetMaxLinearVelocity() const { return mMaxLinearVelocity; } - inline void SetMaxLinearVelocity(float inLinearVelocity) { JPH_ASSERT(inLinearVelocity >= 0.0f); mMaxLinearVelocity = inLinearVelocity; } - - /// Maximum angular velocity that a body can achieve. Used to prevent the system from exploding. - inline float GetMaxAngularVelocity() const { return mMaxAngularVelocity; } - inline void SetMaxAngularVelocity(float inAngularVelocity) { JPH_ASSERT(inAngularVelocity >= 0.0f); mMaxAngularVelocity = inAngularVelocity; } - ///@} - - /// Clamp velocity according to limit - inline void ClampLinearVelocity(); - inline void ClampAngularVelocity(); - - /// Get linear damping: dv/dt = -c * v. c must be between 0 and 1 but is usually close to 0. - inline float GetLinearDamping() const { return mLinearDamping; } - void SetLinearDamping(float inLinearDamping) { JPH_ASSERT(inLinearDamping >= 0.0f); mLinearDamping = inLinearDamping; } - - /// Get angular damping: dw/dt = -c * w. c must be between 0 and 1 but is usually close to 0. - inline float GetAngularDamping() const { return mAngularDamping; } - void SetAngularDamping(float inAngularDamping) { JPH_ASSERT(inAngularDamping >= 0.0f); mAngularDamping = inAngularDamping; } - - /// Get gravity factor (1 = normal gravity, 0 = no gravity) - inline float GetGravityFactor() const { return mGravityFactor; } - void SetGravityFactor(float inGravityFactor) { mGravityFactor = inGravityFactor; } - - /// Set the mass and inertia tensor - void SetMassProperties(EAllowedDOFs inAllowedDOFs, const MassProperties &inMassProperties); - - /// Get inverse mass (1 / mass). Should only be called on a dynamic object (static or kinematic bodies have infinite mass so should be treated as 1 / mass = 0) - inline float GetInverseMass() const { JPH_ASSERT(mCachedMotionType == EMotionType::Dynamic); return mInvMass; } - inline float GetInverseMassUnchecked() const { return mInvMass; } - - /// Set the inverse mass (1 / mass). - /// Note that mass and inertia are linearly related (e.g. inertia of a sphere with mass m and radius r is \f$2/5 \: m \: r^2\f$). - /// If you change mass, inertia should probably change as well. See MassProperties::ScaleToMass. - /// If all your translation degrees of freedom are restricted, make sure this is zero (see EAllowedDOFs). - void SetInverseMass(float inInverseMass) { mInvMass = inInverseMass; } - - /// Diagonal of inverse inertia matrix: D. Should only be called on a dynamic object (static or kinematic bodies have infinite mass so should be treated as D = 0) - inline Vec3 GetInverseInertiaDiagonal() const { JPH_ASSERT(mCachedMotionType == EMotionType::Dynamic); return mInvInertiaDiagonal; } - - /// Rotation (R) that takes inverse inertia diagonal to local space: \f$I_{body}^{-1} = R \: D \: R^{-1}\f$ - inline Quat GetInertiaRotation() const { return mInertiaRotation; } - - /// Set the inverse inertia tensor in local space by setting the diagonal and the rotation: \f$I_{body}^{-1} = R \: D \: R^{-1}\f$. - /// Note that mass and inertia are linearly related (e.g. inertia of a sphere with mass m and radius r is \f$2/5 \: m \: r^2\f$). - /// If you change inertia, mass should probably change as well. See MassProperties::ScaleToMass. - /// If all your rotation degrees of freedom are restricted, make sure this is zero (see EAllowedDOFs). - void SetInverseInertia(Vec3Arg inDiagonal, QuatArg inRot) { mInvInertiaDiagonal = inDiagonal; mInertiaRotation = inRot; } - - /// Get inverse inertia matrix (\f$I_{body}^{-1}\f$). Will be a matrix of zeros for a static or kinematic object. - inline Mat44 GetLocalSpaceInverseInertia() const; - - /// Same as GetLocalSpaceInverseInertia() but doesn't check if the body is dynamic - inline Mat44 GetLocalSpaceInverseInertiaUnchecked() const; - - /// Get inverse inertia matrix (\f$I^{-1}\f$) for a given object rotation (translation will be ignored). Zero if object is static or kinematic. - inline Mat44 GetInverseInertiaForRotation(Mat44Arg inRotation) const; - - /// Multiply a vector with the inverse world space inertia tensor (\f$I_{world}^{-1}\f$). Zero if object is static or kinematic. - JPH_INLINE Vec3 MultiplyWorldSpaceInverseInertiaByVector(QuatArg inBodyRotation, Vec3Arg inV) const; - - /// Velocity of point inPoint (in center of mass space, e.g. on the surface of the body) of the body (unit: m/s) - JPH_INLINE Vec3 GetPointVelocityCOM(Vec3Arg inPointRelativeToCOM) const { return mLinearVelocity + mAngularVelocity.Cross(inPointRelativeToCOM); } - - // Get the total amount of force applied to the center of mass this time step (through Body::AddForce calls). Note that it will reset to zero after PhysicsSystem::Update. - JPH_INLINE Vec3 GetAccumulatedForce() const { return Vec3::sLoadFloat3Unsafe(mForce); } - - // Get the total amount of torque applied to the center of mass this time step (through Body::AddForce/Body::AddTorque calls). Note that it will reset to zero after PhysicsSystem::Update. - JPH_INLINE Vec3 GetAccumulatedTorque() const { return Vec3::sLoadFloat3Unsafe(mTorque); } - - // Reset the total accumulated force, not that this will be done automatically after every time step. - JPH_INLINE void ResetForce() { mForce = Float3(0, 0, 0); } - - // Reset the total accumulated torque, not that this will be done automatically after every time step. - JPH_INLINE void ResetTorque() { mTorque = Float3(0, 0, 0); } - - // Reset the current velocity and accumulated force and torque. - JPH_INLINE void ResetMotion() - { - JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sVelocityAccess, BodyAccess::EAccess::ReadWrite)); - mLinearVelocity = mAngularVelocity = Vec3::sZero(); - mForce = mTorque = Float3(0, 0, 0); - } - - /// Returns a vector where the linear components that are not allowed by mAllowedDOFs are set to 0 and the rest to 0xffffffff - JPH_INLINE UVec4 GetLinearDOFsMask() const - { - UVec4 mask(uint32(EAllowedDOFs::TranslationX), uint32(EAllowedDOFs::TranslationY), uint32(EAllowedDOFs::TranslationZ), 0); - return UVec4::sEquals(UVec4::sAnd(UVec4::sReplicate(uint32(mAllowedDOFs)), mask), mask); - } - - /// Takes a translation vector inV and returns a vector where the components that are not allowed by mAllowedDOFs are set to 0 - JPH_INLINE Vec3 LockTranslation(Vec3Arg inV) const - { - return Vec3::sAnd(inV, Vec3(GetLinearDOFsMask().ReinterpretAsFloat())); - } - - /// Returns a vector where the angular components that are not allowed by mAllowedDOFs are set to 0 and the rest to 0xffffffff - JPH_INLINE UVec4 GetAngularDOFsMask() const - { - UVec4 mask(uint32(EAllowedDOFs::RotationX), uint32(EAllowedDOFs::RotationY), uint32(EAllowedDOFs::RotationZ), 0); - return UVec4::sEquals(UVec4::sAnd(UVec4::sReplicate(uint32(mAllowedDOFs)), mask), mask); - } - - /// Takes an angular velocity / torque vector inV and returns a vector where the components that are not allowed by mAllowedDOFs are set to 0 - JPH_INLINE Vec3 LockAngular(Vec3Arg inV) const - { - return Vec3::sAnd(inV, Vec3(GetAngularDOFsMask().ReinterpretAsFloat())); - } - - /// Used only when this body is dynamic and colliding. Override for the number of solver velocity iterations to run, 0 means use the default in PhysicsSettings::mNumVelocitySteps. The number of iterations to use is the max of all contacts and constraints in the island. - void SetNumVelocityStepsOverride(uint inN) { JPH_ASSERT(inN < 256); mNumVelocityStepsOverride = uint8(inN); } - uint GetNumVelocityStepsOverride() const { return mNumVelocityStepsOverride; } - - /// Used only when this body is dynamic and colliding. Override for the number of solver position iterations to run, 0 means use the default in PhysicsSettings::mNumPositionSteps. The number of iterations to use is the max of all contacts and constraints in the island. - void SetNumPositionStepsOverride(uint inN) { JPH_ASSERT(inN < 256); mNumPositionStepsOverride = uint8(inN); } - uint GetNumPositionStepsOverride() const { return mNumPositionStepsOverride; } - - //////////////////////////////////////////////////////////// - // FUNCTIONS BELOW THIS LINE ARE FOR INTERNAL USE ONLY - //////////////////////////////////////////////////////////// - - ///@name Update linear and angular velocity (used during constraint solving) - ///@{ - inline void AddLinearVelocityStep(Vec3Arg inLinearVelocityChange) { JPH_DET_LOG("AddLinearVelocityStep: " << inLinearVelocityChange); JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sVelocityAccess, BodyAccess::EAccess::ReadWrite)); mLinearVelocity = LockTranslation(mLinearVelocity + inLinearVelocityChange); JPH_ASSERT(!mLinearVelocity.IsNaN()); } - inline void SubLinearVelocityStep(Vec3Arg inLinearVelocityChange) { JPH_DET_LOG("SubLinearVelocityStep: " << inLinearVelocityChange); JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sVelocityAccess, BodyAccess::EAccess::ReadWrite)); mLinearVelocity = LockTranslation(mLinearVelocity - inLinearVelocityChange); JPH_ASSERT(!mLinearVelocity.IsNaN()); } - inline void AddAngularVelocityStep(Vec3Arg inAngularVelocityChange) { JPH_DET_LOG("AddAngularVelocityStep: " << inAngularVelocityChange); JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sVelocityAccess, BodyAccess::EAccess::ReadWrite)); mAngularVelocity += inAngularVelocityChange; JPH_ASSERT(!mAngularVelocity.IsNaN()); } - inline void SubAngularVelocityStep(Vec3Arg inAngularVelocityChange) { JPH_DET_LOG("SubAngularVelocityStep: " << inAngularVelocityChange); JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sVelocityAccess, BodyAccess::EAccess::ReadWrite)); mAngularVelocity -= inAngularVelocityChange; JPH_ASSERT(!mAngularVelocity.IsNaN()); } - ///@} - - /// Apply the gyroscopic force (aka Dzhanibekov effect, see https://en.wikipedia.org/wiki/Tennis_racket_theorem) - inline void ApplyGyroscopicForceInternal(QuatArg inBodyRotation, float inDeltaTime); - - /// Apply all accumulated forces, torques and drag (should only be called by the PhysicsSystem) - inline void ApplyForceTorqueAndDragInternal(QuatArg inBodyRotation, Vec3Arg inGravity, float inDeltaTime); - - /// Access to the island index - uint32 GetIslandIndexInternal() const { return mIslandIndex; } - void SetIslandIndexInternal(uint32 inIndex) { mIslandIndex = inIndex; } - - /// Access to the index in the active bodies array - uint32 GetIndexInActiveBodiesInternal() const { return mIndexInActiveBodies; } - -#ifdef JPH_DOUBLE_PRECISION - inline DVec3 GetSleepTestOffset() const { return DVec3::sLoadDouble3Unsafe(mSleepTestOffset); } -#endif // JPH_DOUBLE_PRECISION - - /// Reset spheres to center around inPoints with radius 0 - inline void ResetSleepTestSpheres(const RVec3 *inPoints); - - /// Reset the sleep test timer without resetting the sleep test spheres - inline void ResetSleepTestTimer() { mSleepTestTimer = 0.0f; } - - /// Accumulate sleep time and return if a body can go to sleep - inline ECanSleep AccumulateSleepTime(float inDeltaTime, float inTimeBeforeSleep); - - /// Saving state for replay - void SaveState(StateRecorder &inStream) const; - - /// Restoring state for replay - void RestoreState(StateRecorder &inStream); - - static constexpr uint32 cInactiveIndex = uint32(-1); ///< Constant indicating that body is not active - -private: - friend class BodyManager; - friend class Body; - - // 1st cache line - // 16 byte aligned - Vec3 mLinearVelocity { Vec3::sZero() }; ///< World space linear velocity of the center of mass (m/s) - Vec3 mAngularVelocity { Vec3::sZero() }; ///< World space angular velocity (rad/s) - Vec3 mInvInertiaDiagonal; ///< Diagonal of inverse inertia matrix: D - Quat mInertiaRotation; ///< Rotation (R) that takes inverse inertia diagonal to local space: Ibody^-1 = R * D * R^-1 - - // 2nd cache line - // 4 byte aligned - Float3 mForce { 0, 0, 0 }; ///< Accumulated world space force (N). Note loaded through intrinsics so ensure that the 4 bytes after this are readable! - Float3 mTorque { 0, 0, 0 }; ///< Accumulated world space torque (N m). Note loaded through intrinsics so ensure that the 4 bytes after this are readable! - float mInvMass; ///< Inverse mass of the object (1/kg) - float mLinearDamping; ///< Linear damping: dv/dt = -c * v. c must be between 0 and 1 but is usually close to 0. - float mAngularDamping; ///< Angular damping: dw/dt = -c * w. c must be between 0 and 1 but is usually close to 0. - float mMaxLinearVelocity; ///< Maximum linear velocity that this body can reach (m/s) - float mMaxAngularVelocity; ///< Maximum angular velocity that this body can reach (rad/s) - float mGravityFactor; ///< Factor to multiply gravity with - uint32 mIndexInActiveBodies = cInactiveIndex; ///< If the body is active, this is the index in the active body list or cInactiveIndex if it is not active (note that there are 2 lists, one for rigid and one for soft bodies) - uint32 mIslandIndex = cInactiveIndex; ///< Index of the island that this body is part of, when the body has not yet been updated or is not active this is cInactiveIndex - - // 1 byte aligned - EMotionQuality mMotionQuality; ///< Motion quality, or how well it detects collisions when it has a high velocity - bool mAllowSleeping; ///< If this body can go to sleep - EAllowedDOFs mAllowedDOFs = EAllowedDOFs::All; ///< Allowed degrees of freedom for this body - uint8 mNumVelocityStepsOverride = 0; ///< Used only when this body is dynamic and colliding. Override for the number of solver velocity iterations to run, 0 means use the default in PhysicsSettings::mNumVelocitySteps. The number of iterations to use is the max of all contacts and constraints in the island. - uint8 mNumPositionStepsOverride = 0; ///< Used only when this body is dynamic and colliding. Override for the number of solver position iterations to run, 0 means use the default in PhysicsSettings::mNumPositionSteps. The number of iterations to use is the max of all contacts and constraints in the island. - - // 3rd cache line (least frequently used) - // 4 byte aligned (or 8 byte if running in double precision) -#ifdef JPH_DOUBLE_PRECISION - Double3 mSleepTestOffset; ///< mSleepTestSpheres are relative to this offset to prevent floating point inaccuracies. Warning: Loaded using sLoadDouble3Unsafe which will read 8 extra bytes. -#endif // JPH_DOUBLE_PRECISION - Sphere mSleepTestSpheres[3]; ///< Measure motion for 3 points on the body to see if it is resting: COM, COM + largest bounding box axis, COM + second largest bounding box axis - float mSleepTestTimer; ///< How long this body has been within the movement tolerance - -#ifdef JPH_ENABLE_ASSERTS - EBodyType mCachedBodyType; ///< Copied from Body::mBodyType and cached for asserting purposes - EMotionType mCachedMotionType; ///< Copied from Body::mMotionType and cached for asserting purposes -#endif -}; - -JPH_NAMESPACE_END - -#include "MotionProperties.inl" diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/MotionProperties.inl b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/MotionProperties.inl deleted file mode 100644 index 1fb044ef48e..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/MotionProperties.inl +++ /dev/null @@ -1,168 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -JPH_NAMESPACE_BEGIN - -void MotionProperties::MoveKinematic(Vec3Arg inDeltaPosition, QuatArg inDeltaRotation, float inDeltaTime) -{ - JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sVelocityAccess, BodyAccess::EAccess::ReadWrite)); - JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sPositionAccess, BodyAccess::EAccess::Read)); - JPH_ASSERT(mCachedBodyType == EBodyType::RigidBody); - JPH_ASSERT(mCachedMotionType != EMotionType::Static); - - // Calculate required linear velocity - mLinearVelocity = LockTranslation(inDeltaPosition / inDeltaTime); - - // Calculate required angular velocity - Vec3 axis; - float angle; - inDeltaRotation.GetAxisAngle(axis, angle); - mAngularVelocity = LockAngular(axis * (angle / inDeltaTime)); -} - -void MotionProperties::ClampLinearVelocity() -{ - JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sVelocityAccess, BodyAccess::EAccess::ReadWrite)); - - float len_sq = mLinearVelocity.LengthSq(); - JPH_ASSERT(isfinite(len_sq)); - if (len_sq > Square(mMaxLinearVelocity)) - mLinearVelocity *= mMaxLinearVelocity / sqrt(len_sq); -} - -void MotionProperties::ClampAngularVelocity() -{ - JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sVelocityAccess, BodyAccess::EAccess::ReadWrite)); - - float len_sq = mAngularVelocity.LengthSq(); - JPH_ASSERT(isfinite(len_sq)); - if (len_sq > Square(mMaxAngularVelocity)) - mAngularVelocity *= mMaxAngularVelocity / sqrt(len_sq); -} - -inline Mat44 MotionProperties::GetLocalSpaceInverseInertiaUnchecked() const -{ - Mat44 rotation = Mat44::sRotation(mInertiaRotation); - Mat44 rotation_mul_scale_transposed(mInvInertiaDiagonal.SplatX() * rotation.GetColumn4(0), mInvInertiaDiagonal.SplatY() * rotation.GetColumn4(1), mInvInertiaDiagonal.SplatZ() * rotation.GetColumn4(2), Vec4(0, 0, 0, 1)); - return rotation.Multiply3x3RightTransposed(rotation_mul_scale_transposed); -} - -inline Mat44 MotionProperties::GetLocalSpaceInverseInertia() const -{ - JPH_ASSERT(mCachedMotionType == EMotionType::Dynamic); - return GetLocalSpaceInverseInertiaUnchecked(); -} - -Mat44 MotionProperties::GetInverseInertiaForRotation(Mat44Arg inRotation) const -{ - JPH_ASSERT(mCachedMotionType == EMotionType::Dynamic); - - Mat44 rotation = inRotation.Multiply3x3(Mat44::sRotation(mInertiaRotation)); - Mat44 rotation_mul_scale_transposed(mInvInertiaDiagonal.SplatX() * rotation.GetColumn4(0), mInvInertiaDiagonal.SplatY() * rotation.GetColumn4(1), mInvInertiaDiagonal.SplatZ() * rotation.GetColumn4(2), Vec4(0, 0, 0, 1)); - Mat44 inverse_inertia = rotation.Multiply3x3RightTransposed(rotation_mul_scale_transposed); - - // We need to mask out both the rows and columns of DOFs that are not allowed - Vec4 angular_dofs_mask = GetAngularDOFsMask().ReinterpretAsFloat(); - inverse_inertia.SetColumn4(0, Vec4::sAnd(inverse_inertia.GetColumn4(0), Vec4::sAnd(angular_dofs_mask, angular_dofs_mask.SplatX()))); - inverse_inertia.SetColumn4(1, Vec4::sAnd(inverse_inertia.GetColumn4(1), Vec4::sAnd(angular_dofs_mask, angular_dofs_mask.SplatY()))); - inverse_inertia.SetColumn4(2, Vec4::sAnd(inverse_inertia.GetColumn4(2), Vec4::sAnd(angular_dofs_mask, angular_dofs_mask.SplatZ()))); - - return inverse_inertia; -} - -Vec3 MotionProperties::MultiplyWorldSpaceInverseInertiaByVector(QuatArg inBodyRotation, Vec3Arg inV) const -{ - JPH_ASSERT(mCachedMotionType == EMotionType::Dynamic); - - // Mask out columns of DOFs that are not allowed - Vec3 angular_dofs_mask = Vec3(GetAngularDOFsMask().ReinterpretAsFloat()); - Vec3 v = Vec3::sAnd(inV, angular_dofs_mask); - - // Multiply vector by inverse inertia - Mat44 rotation = Mat44::sRotation(inBodyRotation * mInertiaRotation); - Vec3 result = rotation.Multiply3x3(mInvInertiaDiagonal * rotation.Multiply3x3Transposed(v)); - - // Mask out rows of DOFs that are not allowed - return Vec3::sAnd(result, angular_dofs_mask); -} - -void MotionProperties::ApplyGyroscopicForceInternal(QuatArg inBodyRotation, float inDeltaTime) -{ - JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sVelocityAccess, BodyAccess::EAccess::ReadWrite)); - JPH_ASSERT(mCachedBodyType == EBodyType::RigidBody); - JPH_ASSERT(mCachedMotionType == EMotionType::Dynamic); - - // Calculate local space inertia tensor (a diagonal in local space) - UVec4 is_zero = Vec3::sEquals(mInvInertiaDiagonal, Vec3::sZero()); - Vec3 denominator = Vec3::sSelect(mInvInertiaDiagonal, Vec3::sReplicate(1.0f), is_zero); - Vec3 nominator = Vec3::sSelect(Vec3::sReplicate(1.0f), Vec3::sZero(), is_zero); - Vec3 local_inertia = nominator / denominator; // Avoid dividing by zero, inertia in this axis will be zero - - // Calculate local space angular momentum - Quat inertia_space_to_world_space = inBodyRotation * mInertiaRotation; - Vec3 local_angular_velocity = inertia_space_to_world_space.Conjugated() * mAngularVelocity; - Vec3 local_momentum = local_inertia * local_angular_velocity; - - // The gyroscopic force applies a torque: T = -w x I w where w is angular velocity and I the inertia tensor - // Calculate the new angular momentum by applying the gyroscopic force and make sure the new magnitude is the same as the old one - // to avoid introducing energy into the system due to the Euler step - Vec3 new_local_momentum = local_momentum - inDeltaTime * local_angular_velocity.Cross(local_momentum); - float new_local_momentum_len_sq = new_local_momentum.LengthSq(); - new_local_momentum = new_local_momentum_len_sq > 0.0f? new_local_momentum * sqrt(local_momentum.LengthSq() / new_local_momentum_len_sq) : Vec3::sZero(); - - // Convert back to world space angular velocity - mAngularVelocity = inertia_space_to_world_space * (mInvInertiaDiagonal * new_local_momentum); -} - -void MotionProperties::ApplyForceTorqueAndDragInternal(QuatArg inBodyRotation, Vec3Arg inGravity, float inDeltaTime) -{ - JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sVelocityAccess, BodyAccess::EAccess::ReadWrite)); - JPH_ASSERT(mCachedBodyType == EBodyType::RigidBody); - JPH_ASSERT(mCachedMotionType == EMotionType::Dynamic); - - // Update linear velocity - mLinearVelocity = LockTranslation(mLinearVelocity + inDeltaTime * (mGravityFactor * inGravity + mInvMass * GetAccumulatedForce())); - - // Update angular velocity - mAngularVelocity += inDeltaTime * MultiplyWorldSpaceInverseInertiaByVector(inBodyRotation, GetAccumulatedTorque()); - - // Linear damping: dv/dt = -c * v - // Solution: v(t) = v(0) * e^(-c * t) or v2 = v1 * e^(-c * dt) - // Taylor expansion of e^(-c * dt) = 1 - c * dt + ... - // Since dt is usually in the order of 1/60 and c is a low number too this approximation is good enough - mLinearVelocity *= max(0.0f, 1.0f - mLinearDamping * inDeltaTime); - mAngularVelocity *= max(0.0f, 1.0f - mAngularDamping * inDeltaTime); - - // Clamp velocities - ClampLinearVelocity(); - ClampAngularVelocity(); -} - -void MotionProperties::ResetSleepTestSpheres(const RVec3 *inPoints) -{ -#ifdef JPH_DOUBLE_PRECISION - // Make spheres relative to the first point and initialize them to zero radius - DVec3 offset = inPoints[0]; - offset.StoreDouble3(&mSleepTestOffset); - mSleepTestSpheres[0] = Sphere(Vec3::sZero(), 0.0f); - for (int i = 1; i < 3; ++i) - mSleepTestSpheres[i] = Sphere(Vec3(inPoints[i] - offset), 0.0f); -#else - // Initialize the spheres to zero radius around the supplied points - for (int i = 0; i < 3; ++i) - mSleepTestSpheres[i] = Sphere(inPoints[i], 0.0f); -#endif - - mSleepTestTimer = 0.0f; -} - -ECanSleep MotionProperties::AccumulateSleepTime(float inDeltaTime, float inTimeBeforeSleep) -{ - mSleepTestTimer += inDeltaTime; - return mSleepTestTimer >= inTimeBeforeSleep? ECanSleep::CanSleep : ECanSleep::CannotSleep; -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/MotionQuality.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/MotionQuality.h deleted file mode 100644 index b1ba343c06a..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/MotionQuality.h +++ /dev/null @@ -1,31 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -JPH_NAMESPACE_BEGIN - -/// Motion quality, or how well it detects collisions when it has a high velocity -enum class EMotionQuality : uint8 -{ - /// Update the body in discrete steps. Body will tunnel throuh thin objects if its velocity is high enough. - /// This is the cheapest way of simulating a body. - Discrete, - - /// Update the body using linear casting. When stepping the body, its collision shape is cast from - /// start to destination using the starting rotation. The body will not be able to tunnel through thin - /// objects at high velocity, but tunneling is still possible if the body is long and thin and has high - /// angular velocity. Time is stolen from the object (which means it will move up to the first collision - /// and will not bounce off the surface until the next integration step). This will make the body appear - /// to go slower when it collides with high velocity. In order to not get stuck, the body is always - /// allowed to move by a fraction of it's inner radius, which may eventually lead it to pass through geometry. - /// - /// Note that if you're using a collision listener, you can receive contact added/persisted notifications of contacts - /// that may in the end not happen. This happens between bodies that are using casting: If bodies A and B collide at t1 - /// and B and C collide at t2 where t2 < t1 and A and C don't collide. In this case you may receive an incorrect contact - /// point added callback between A and B (which will be removed the next frame). - LinearCast, -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/MotionType.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/MotionType.h deleted file mode 100644 index 6de0d8c8ec7..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Body/MotionType.h +++ /dev/null @@ -1,17 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -JPH_NAMESPACE_BEGIN - -/// Motion type of a physics body -enum class EMotionType : uint8 -{ - Static, ///< Non movable - Kinematic, ///< Movable using velocities only, does not respond to forces - Dynamic, ///< Responds to forces as a normal physics object -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Character/Character.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Character/Character.cpp deleted file mode 100644 index 6bb8611e909..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Character/Character.cpp +++ /dev/null @@ -1,317 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -static inline const BodyLockInterface &sGetBodyLockInterface(const PhysicsSystem *inSystem, bool inLockBodies) -{ - return inLockBodies? static_cast(inSystem->GetBodyLockInterface()) : static_cast(inSystem->GetBodyLockInterfaceNoLock()); -} - -static inline BodyInterface &sGetBodyInterface(PhysicsSystem *inSystem, bool inLockBodies) -{ - return inLockBodies? inSystem->GetBodyInterface() : inSystem->GetBodyInterfaceNoLock(); -} - -static inline const NarrowPhaseQuery &sGetNarrowPhaseQuery(const PhysicsSystem *inSystem, bool inLockBodies) -{ - return inLockBodies? inSystem->GetNarrowPhaseQuery() : inSystem->GetNarrowPhaseQueryNoLock(); -} - -Character::Character(const CharacterSettings *inSettings, RVec3Arg inPosition, QuatArg inRotation, uint64 inUserData, PhysicsSystem *inSystem) : - CharacterBase(inSettings, inSystem), - mLayer(inSettings->mLayer) -{ - // Construct rigid body - BodyCreationSettings settings(mShape, inPosition, inRotation, EMotionType::Dynamic, mLayer); - settings.mAllowedDOFs = EAllowedDOFs::TranslationX | EAllowedDOFs::TranslationY | EAllowedDOFs::TranslationZ; - settings.mOverrideMassProperties = EOverrideMassProperties::MassAndInertiaProvided; - settings.mMassPropertiesOverride.mMass = inSettings->mMass; - settings.mFriction = inSettings->mFriction; - settings.mGravityFactor = inSettings->mGravityFactor; - settings.mUserData = inUserData; - const Body *body = mSystem->GetBodyInterface().CreateBody(settings); - if (body != nullptr) - mBodyID = body->GetID(); -} - -Character::~Character() -{ - // Destroy the body - mSystem->GetBodyInterface().DestroyBody(mBodyID); -} - -void Character::AddToPhysicsSystem(EActivation inActivationMode, bool inLockBodies) -{ - sGetBodyInterface(mSystem, inLockBodies).AddBody(mBodyID, inActivationMode); -} - -void Character::RemoveFromPhysicsSystem(bool inLockBodies) -{ - sGetBodyInterface(mSystem, inLockBodies).RemoveBody(mBodyID); -} - -void Character::Activate(bool inLockBodies) -{ - sGetBodyInterface(mSystem, inLockBodies).ActivateBody(mBodyID); -} - -void Character::CheckCollision(RMat44Arg inCenterOfMassTransform, Vec3Arg inMovementDirection, float inMaxSeparationDistance, const Shape *inShape, RVec3Arg inBaseOffset, CollideShapeCollector &ioCollector, bool inLockBodies) const -{ - // Create query broadphase layer filter - DefaultBroadPhaseLayerFilter broadphase_layer_filter = mSystem->GetDefaultBroadPhaseLayerFilter(mLayer); - - // Create query object layer filter - DefaultObjectLayerFilter object_layer_filter = mSystem->GetDefaultLayerFilter(mLayer); - - // Ignore my own body - IgnoreSingleBodyFilter body_filter(mBodyID); - - // Settings for collide shape - CollideShapeSettings settings; - settings.mMaxSeparationDistance = inMaxSeparationDistance; - settings.mActiveEdgeMode = EActiveEdgeMode::CollideOnlyWithActive; - settings.mActiveEdgeMovementDirection = inMovementDirection; - settings.mBackFaceMode = EBackFaceMode::IgnoreBackFaces; - - sGetNarrowPhaseQuery(mSystem, inLockBodies).CollideShape(inShape, Vec3::sReplicate(1.0f), inCenterOfMassTransform, settings, inBaseOffset, ioCollector, broadphase_layer_filter, object_layer_filter, body_filter); -} - -void Character::CheckCollision(RVec3Arg inPosition, QuatArg inRotation, Vec3Arg inMovementDirection, float inMaxSeparationDistance, const Shape *inShape, RVec3Arg inBaseOffset, CollideShapeCollector &ioCollector, bool inLockBodies) const -{ - // Calculate center of mass transform - RMat44 center_of_mass = RMat44::sRotationTranslation(inRotation, inPosition).PreTranslated(inShape->GetCenterOfMass()); - - CheckCollision(center_of_mass, inMovementDirection, inMaxSeparationDistance, inShape, inBaseOffset, ioCollector, inLockBodies); -} - -void Character::CheckCollision(const Shape *inShape, float inMaxSeparationDistance, RVec3Arg inBaseOffset, CollideShapeCollector &ioCollector, bool inLockBodies) const -{ - // Determine position and velocity of body - RMat44 query_transform; - Vec3 velocity; - { - BodyLockRead lock(sGetBodyLockInterface(mSystem, inLockBodies), mBodyID); - if (!lock.Succeeded()) - return; - - const Body &body = lock.GetBody(); - - // Correct the center of mass transform for the difference between the old and new center of mass shape - query_transform = body.GetCenterOfMassTransform().PreTranslated(inShape->GetCenterOfMass() - mShape->GetCenterOfMass()); - velocity = body.GetLinearVelocity(); - } - - CheckCollision(query_transform, velocity, inMaxSeparationDistance, inShape, inBaseOffset, ioCollector, inLockBodies); -} - -void Character::PostSimulation(float inMaxSeparationDistance, bool inLockBodies) -{ - // Get character position, rotation and velocity - RVec3 char_pos; - Quat char_rot; - Vec3 char_vel; - { - BodyLockRead lock(sGetBodyLockInterface(mSystem, inLockBodies), mBodyID); - if (!lock.Succeeded()) - return; - const Body &body = lock.GetBody(); - char_pos = body.GetPosition(); - char_rot = body.GetRotation(); - char_vel = body.GetLinearVelocity(); - } - - // Collector that finds the hit with the normal that is the most 'up' - class MyCollector : public CollideShapeCollector - { - public: - // Constructor - explicit MyCollector(Vec3Arg inUp, RVec3 inBaseOffset) : mBaseOffset(inBaseOffset), mUp(inUp) { } - - // See: CollectorType::AddHit - virtual void AddHit(const CollideShapeResult &inResult) override - { - Vec3 normal = -inResult.mPenetrationAxis.Normalized(); - float dot = normal.Dot(mUp); - if (dot > mBestDot) // Find the hit that is most aligned with the up vector - { - mGroundBodyID = inResult.mBodyID2; - mGroundBodySubShapeID = inResult.mSubShapeID2; - mGroundPosition = mBaseOffset + inResult.mContactPointOn2; - mGroundNormal = normal; - mBestDot = dot; - } - } - - BodyID mGroundBodyID; - SubShapeID mGroundBodySubShapeID; - RVec3 mGroundPosition = RVec3::sZero(); - Vec3 mGroundNormal = Vec3::sZero(); - - private: - RVec3 mBaseOffset; - Vec3 mUp; - float mBestDot = -FLT_MAX; - }; - - // Collide shape - MyCollector collector(mUp, char_pos); - CheckCollision(char_pos, char_rot, char_vel, inMaxSeparationDistance, mShape, char_pos, collector, inLockBodies); - - // Copy results - mGroundBodyID = collector.mGroundBodyID; - mGroundBodySubShapeID = collector.mGroundBodySubShapeID; - mGroundPosition = collector.mGroundPosition; - mGroundNormal = collector.mGroundNormal; - - // Get additional data from body - BodyLockRead lock(sGetBodyLockInterface(mSystem, inLockBodies), mGroundBodyID); - if (lock.Succeeded()) - { - const Body &body = lock.GetBody(); - - // Update ground state - RMat44 inv_transform = RMat44::sInverseRotationTranslation(char_rot, char_pos); - if (mSupportingVolume.SignedDistance(Vec3(inv_transform * mGroundPosition)) > 0.0f) - mGroundState = EGroundState::NotSupported; - else if (IsSlopeTooSteep(mGroundNormal)) - mGroundState = EGroundState::OnSteepGround; - else - mGroundState = EGroundState::OnGround; - - // Copy other body properties - mGroundMaterial = body.GetShape()->GetMaterial(mGroundBodySubShapeID); - mGroundVelocity = body.GetPointVelocity(mGroundPosition); - mGroundUserData = body.GetUserData(); - } - else - { - mGroundState = EGroundState::InAir; - mGroundMaterial = PhysicsMaterial::sDefault; - mGroundVelocity = Vec3::sZero(); - mGroundUserData = 0; - } -} - -void Character::SetLinearAndAngularVelocity(Vec3Arg inLinearVelocity, Vec3Arg inAngularVelocity, bool inLockBodies) -{ - sGetBodyInterface(mSystem, inLockBodies).SetLinearAndAngularVelocity(mBodyID, inLinearVelocity, inAngularVelocity); -} - -Vec3 Character::GetLinearVelocity(bool inLockBodies) const -{ - return sGetBodyInterface(mSystem, inLockBodies).GetLinearVelocity(mBodyID); -} - -void Character::SetLinearVelocity(Vec3Arg inLinearVelocity, bool inLockBodies) -{ - sGetBodyInterface(mSystem, inLockBodies).SetLinearVelocity(mBodyID, inLinearVelocity); -} - -void Character::AddLinearVelocity(Vec3Arg inLinearVelocity, bool inLockBodies) -{ - sGetBodyInterface(mSystem, inLockBodies).AddLinearVelocity(mBodyID, inLinearVelocity); -} - -void Character::AddImpulse(Vec3Arg inImpulse, bool inLockBodies) -{ - sGetBodyInterface(mSystem, inLockBodies).AddImpulse(mBodyID, inImpulse); -} - -void Character::GetPositionAndRotation(RVec3 &outPosition, Quat &outRotation, bool inLockBodies) const -{ - sGetBodyInterface(mSystem, inLockBodies).GetPositionAndRotation(mBodyID, outPosition, outRotation); -} - -void Character::SetPositionAndRotation(RVec3Arg inPosition, QuatArg inRotation, EActivation inActivationMode, bool inLockBodies) const -{ - sGetBodyInterface(mSystem, inLockBodies).SetPositionAndRotation(mBodyID, inPosition, inRotation, inActivationMode); -} - -RVec3 Character::GetPosition(bool inLockBodies) const -{ - return sGetBodyInterface(mSystem, inLockBodies).GetPosition(mBodyID); -} - -void Character::SetPosition(RVec3Arg inPosition, EActivation inActivationMode, bool inLockBodies) -{ - sGetBodyInterface(mSystem, inLockBodies).SetPosition(mBodyID, inPosition, inActivationMode); -} - -Quat Character::GetRotation(bool inLockBodies) const -{ - return sGetBodyInterface(mSystem, inLockBodies).GetRotation(mBodyID); -} - -void Character::SetRotation(QuatArg inRotation, EActivation inActivationMode, bool inLockBodies) -{ - sGetBodyInterface(mSystem, inLockBodies).SetRotation(mBodyID, inRotation, inActivationMode); -} - -RVec3 Character::GetCenterOfMassPosition(bool inLockBodies) const -{ - return sGetBodyInterface(mSystem, inLockBodies).GetCenterOfMassPosition(mBodyID); -} - -RMat44 Character::GetWorldTransform(bool inLockBodies) const -{ - return sGetBodyInterface(mSystem, inLockBodies).GetWorldTransform(mBodyID); -} - -void Character::SetLayer(ObjectLayer inLayer, bool inLockBodies) -{ - mLayer = inLayer; - - sGetBodyInterface(mSystem, inLockBodies).SetObjectLayer(mBodyID, inLayer); -} - -bool Character::SetShape(const Shape *inShape, float inMaxPenetrationDepth, bool inLockBodies) -{ - if (inMaxPenetrationDepth < FLT_MAX) - { - // Collector that checks if there is anything in the way while switching to inShape - class MyCollector : public CollideShapeCollector - { - public: - // Constructor - explicit MyCollector(float inMaxPenetrationDepth) : mMaxPenetrationDepth(inMaxPenetrationDepth) { } - - // See: CollectorType::AddHit - virtual void AddHit(const CollideShapeResult &inResult) override - { - if (inResult.mPenetrationDepth > mMaxPenetrationDepth) - { - mHadCollision = true; - ForceEarlyOut(); - } - } - - float mMaxPenetrationDepth; - bool mHadCollision = false; - }; - - // Test if anything is in the way of switching - RVec3 char_pos = GetPosition(inLockBodies); - MyCollector collector(inMaxPenetrationDepth); - CheckCollision(inShape, 0.0f, char_pos, collector, inLockBodies); - if (collector.mHadCollision) - return false; - } - - // Switch the shape - mShape = inShape; - sGetBodyInterface(mSystem, inLockBodies).SetShape(mBodyID, mShape, false, EActivation::Activate); - return true; -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Character/Character.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Character/Character.h deleted file mode 100644 index a47d75581b0..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Character/Character.h +++ /dev/null @@ -1,139 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include - -JPH_NAMESPACE_BEGIN - -/// Contains the configuration of a character -class JPH_EXPORT CharacterSettings : public CharacterBaseSettings -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Layer that this character will be added to - ObjectLayer mLayer = 0; - - /// Mass of the character - float mMass = 80.0f; - - /// Friction for the character - float mFriction = 0.2f; - - /// Value to multiply gravity with for this character - float mGravityFactor = 1.0f; -}; - -/// Runtime character object. -/// This object usually represents the player or a humanoid AI. It uses a single rigid body, -/// usually with a capsule shape to simulate movement and collision for the character. -/// The character is a keyframed object, the application controls it by setting the velocity. -class JPH_EXPORT Character : public CharacterBase -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Constructor - /// @param inSettings The settings for the character - /// @param inPosition Initial position for the character - /// @param inRotation Initial rotation for the character (usually only around Y) - /// @param inUserData Application specific value - /// @param inSystem Physics system that this character will be added to later - Character(const CharacterSettings *inSettings, RVec3Arg inPosition, QuatArg inRotation, uint64 inUserData, PhysicsSystem *inSystem); - - /// Destructor - virtual ~Character() override; - - /// Add bodies and constraints to the system and optionally activate the bodies - void AddToPhysicsSystem(EActivation inActivationMode = EActivation::Activate, bool inLockBodies = true); - - /// Remove bodies and constraints from the system - void RemoveFromPhysicsSystem(bool inLockBodies = true); - - /// Wake up the character - void Activate(bool inLockBodies = true); - - /// Needs to be called after every PhysicsSystem::Update - /// @param inMaxSeparationDistance Max distance between the floor and the character to still consider the character standing on the floor - /// @param inLockBodies If the collision query should use the locking body interface (true) or the non locking body interface (false) - void PostSimulation(float inMaxSeparationDistance, bool inLockBodies = true); - - /// Control the velocity of the character - void SetLinearAndAngularVelocity(Vec3Arg inLinearVelocity, Vec3Arg inAngularVelocity, bool inLockBodies = true); - - /// Get the linear velocity of the character (m / s) - Vec3 GetLinearVelocity(bool inLockBodies = true) const; - - /// Set the linear velocity of the character (m / s) - void SetLinearVelocity(Vec3Arg inLinearVelocity, bool inLockBodies = true); - - /// Add world space linear velocity to current velocity (m / s) - void AddLinearVelocity(Vec3Arg inLinearVelocity, bool inLockBodies = true); - - /// Add impulse to the center of mass of the character - void AddImpulse(Vec3Arg inImpulse, bool inLockBodies = true); - - /// Get the body associated with this character - BodyID GetBodyID() const { return mBodyID; } - - /// Get position / rotation of the body - void GetPositionAndRotation(RVec3 &outPosition, Quat &outRotation, bool inLockBodies = true) const; - - /// Set the position / rotation of the body, optionally activating it. - void SetPositionAndRotation(RVec3Arg inPosition, QuatArg inRotation, EActivation inActivationMode = EActivation::Activate, bool inLockBodies = true) const; - - /// Get the position of the character - RVec3 GetPosition(bool inLockBodies = true) const; - - /// Set the position of the character, optionally activating it. - void SetPosition(RVec3Arg inPostion, EActivation inActivationMode = EActivation::Activate, bool inLockBodies = true); - - /// Get the rotation of the character - Quat GetRotation(bool inLockBodies = true) const; - - /// Set the rotation of the character, optionally activating it. - void SetRotation(QuatArg inRotation, EActivation inActivationMode = EActivation::Activate, bool inLockBodies = true); - - /// Position of the center of mass of the underlying rigid body - RVec3 GetCenterOfMassPosition(bool inLockBodies = true) const; - - /// Calculate the world transform of the character - RMat44 GetWorldTransform(bool inLockBodies = true) const; - - /// Update the layer of the character - void SetLayer(ObjectLayer inLayer, bool inLockBodies = true); - - /// Switch the shape of the character (e.g. for stance). When inMaxPenetrationDepth is not FLT_MAX, it checks - /// if the new shape collides before switching shape. Returns true if the switch succeeded. - bool SetShape(const Shape *inShape, float inMaxPenetrationDepth, bool inLockBodies = true); - - /// @brief Get all contacts for the character at a particular location - /// @param inPosition Position to test. - /// @param inRotation Rotation at which to test the shape. - /// @param inMovementDirection A hint in which direction the character is moving, will be used to calculate a proper normal. - /// @param inMaxSeparationDistance How much distance around the character you want to report contacts in (can be 0 to match the character exactly). - /// @param inShape Shape to test collision with. - /// @param inBaseOffset All hit results will be returned relative to this offset, can be zero to get results in world position, but when you're testing far from the origin you get better precision by picking a position that's closer e.g. GetPosition() since floats are most accurate near the origin - /// @param ioCollector Collision collector that receives the collision results. - /// @param inLockBodies If the collision query should use the locking body interface (true) or the non locking body interface (false) - void CheckCollision(RVec3Arg inPosition, QuatArg inRotation, Vec3Arg inMovementDirection, float inMaxSeparationDistance, const Shape *inShape, RVec3Arg inBaseOffset, CollideShapeCollector &ioCollector, bool inLockBodies = true) const; - -private: - /// Check collisions between inShape and the world using the center of mass transform - void CheckCollision(RMat44Arg inCenterOfMassTransform, Vec3Arg inMovementDirection, float inMaxSeparationDistance, const Shape *inShape, RVec3Arg inBaseOffset, CollideShapeCollector &ioCollector, bool inLockBodies) const; - - /// Check collisions between inShape and the world using the current position / rotation of the character - void CheckCollision(const Shape *inShape, float inMaxSeparationDistance, RVec3Arg inBaseOffset, CollideShapeCollector &ioCollector, bool inLockBodies) const; - - /// The body of this character - BodyID mBodyID; - - /// The layer the body is in - ObjectLayer mLayer; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Character/CharacterBase.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Character/CharacterBase.cpp deleted file mode 100644 index d3142506509..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Character/CharacterBase.cpp +++ /dev/null @@ -1,59 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include - -JPH_NAMESPACE_BEGIN - -CharacterBase::CharacterBase(const CharacterBaseSettings *inSettings, PhysicsSystem *inSystem) : - mSystem(inSystem), - mShape(inSettings->mShape), - mUp(inSettings->mUp), - mSupportingVolume(inSettings->mSupportingVolume) -{ - // Initialize max slope angle - SetMaxSlopeAngle(inSettings->mMaxSlopeAngle); -} - -const char *CharacterBase::sToString(EGroundState inState) -{ - switch (inState) - { - case EGroundState::OnGround: return "OnGround"; - case EGroundState::OnSteepGround: return "OnSteepGround"; - case EGroundState::NotSupported: return "NotSupported"; - case EGroundState::InAir: return "InAir"; - } - - JPH_ASSERT(false); - return "Unknown"; -} - -void CharacterBase::SaveState(StateRecorder &inStream) const -{ - inStream.Write(mGroundState); - inStream.Write(mGroundBodyID); - inStream.Write(mGroundBodySubShapeID); - inStream.Write(mGroundPosition); - inStream.Write(mGroundNormal); - inStream.Write(mGroundVelocity); - // Can't save user data (may be a pointer) and material -} - -void CharacterBase::RestoreState(StateRecorder &inStream) -{ - inStream.Read(mGroundState); - inStream.Read(mGroundBodyID); - inStream.Read(mGroundBodySubShapeID); - inStream.Read(mGroundPosition); - inStream.Read(mGroundNormal); - inStream.Read(mGroundVelocity); - mGroundUserData = 0; // Cannot restore user data - mGroundMaterial = PhysicsMaterial::sDefault; // Cannot restore material -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Character/CharacterBase.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Character/CharacterBase.h deleted file mode 100644 index 2603352d4cf..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Character/CharacterBase.h +++ /dev/null @@ -1,154 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -class PhysicsSystem; -class StateRecorder; - -/// Base class for configuration of a character -class JPH_EXPORT CharacterBaseSettings : public RefTarget -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Constructor - CharacterBaseSettings() = default; - CharacterBaseSettings(const CharacterBaseSettings &inSettings) = default; - CharacterBaseSettings & operator = (const CharacterBaseSettings &inSettings) = default; - - /// Virtual destructor - virtual ~CharacterBaseSettings() = default; - - /// Vector indicating the up direction of the character - Vec3 mUp = Vec3::sAxisY(); - - /// Plane, defined in local space relative to the character. Every contact behind this plane can support the - /// character, every contact in front of this plane is treated as only colliding with the player. - /// Default: Accept any contact. - Plane mSupportingVolume { Vec3::sAxisY(), -1.0e10f }; - - /// Maximum angle of slope that character can still walk on (radians). - float mMaxSlopeAngle = DegreesToRadians(50.0f); - - /// Initial shape that represents the character's volume. - /// Usually this is a capsule, make sure the shape is made so that the bottom of the shape is at (0, 0, 0). - RefConst mShape; -}; - -/// Base class for character class -class JPH_EXPORT CharacterBase : public RefTarget, public NonCopyable -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Constructor - CharacterBase(const CharacterBaseSettings *inSettings, PhysicsSystem *inSystem); - - /// Destructor - virtual ~CharacterBase() = default; - - /// Set the maximum angle of slope that character can still walk on (radians) - void SetMaxSlopeAngle(float inMaxSlopeAngle) { mCosMaxSlopeAngle = Cos(inMaxSlopeAngle); } - float GetCosMaxSlopeAngle() const { return mCosMaxSlopeAngle; } - - /// Set the up vector for the character - void SetUp(Vec3Arg inUp) { mUp = inUp; } - Vec3 GetUp() const { return mUp; } - - /// Check if the normal of the ground surface is too steep to walk on - bool IsSlopeTooSteep(Vec3Arg inNormal) const - { - // If cos max slope angle is close to one the system is turned off, - // otherwise check the angle between the up and normal vector - return mCosMaxSlopeAngle < cNoMaxSlopeAngle && inNormal.Dot(mUp) < mCosMaxSlopeAngle; - } - - /// Get the current shape that the character is using. - const Shape * GetShape() const { return mShape; } - - enum class EGroundState - { - OnGround, ///< Character is on the ground and can move freely. - OnSteepGround, ///< Character is on a slope that is too steep and can't climb up any further. The caller should start applying downward velocity if sliding from the slope is desired. - NotSupported, ///< Character is touching an object, but is not supported by it and should fall. The GetGroundXXX functions will return information about the touched object. - InAir, ///< Character is in the air and is not touching anything. - }; - - /// Debug function to convert enum values to string - static const char * sToString(EGroundState inState); - - ///@name Properties of the ground this character is standing on - - /// Current ground state - EGroundState GetGroundState() const { return mGroundState; } - - /// Returns true if the player is supported by normal or steep ground - bool IsSupported() const { return mGroundState == EGroundState::OnGround || mGroundState == EGroundState::OnSteepGround; } - - /// Get the contact point with the ground - RVec3 GetGroundPosition() const { return mGroundPosition; } - - /// Get the contact normal with the ground - Vec3 GetGroundNormal() const { return mGroundNormal; } - - /// Velocity in world space of ground - Vec3 GetGroundVelocity() const { return mGroundVelocity; } - - /// Material that the character is standing on - const PhysicsMaterial * GetGroundMaterial() const { return mGroundMaterial; } - - /// BodyID of the object the character is standing on. Note may have been removed! - BodyID GetGroundBodyID() const { return mGroundBodyID; } - - /// Sub part of the body that we're standing on. - SubShapeID GetGroundSubShapeID() const { return mGroundBodySubShapeID; } - - /// User data value of the body that we're standing on - uint64 GetGroundUserData() const { return mGroundUserData; } - - // Saving / restoring state for replay - virtual void SaveState(StateRecorder &inStream) const; - virtual void RestoreState(StateRecorder &inStream); - -protected: - // Cached physics system - PhysicsSystem * mSystem; - - // The shape that the body currently has - RefConst mShape; - - // The character's world space up axis - Vec3 mUp; - - // Every contact behind this plane can support the character - Plane mSupportingVolume; - - // Beyond this value there is no max slope - static constexpr float cNoMaxSlopeAngle = 0.9999f; - - // Cosine of the maximum angle of slope that character can still walk on - float mCosMaxSlopeAngle; - - // Ground properties - EGroundState mGroundState = EGroundState::InAir; - BodyID mGroundBodyID; - SubShapeID mGroundBodySubShapeID; - RVec3 mGroundPosition = RVec3::sZero(); - Vec3 mGroundNormal = Vec3::sZero(); - Vec3 mGroundVelocity = Vec3::sZero(); - RefConst mGroundMaterial = PhysicsMaterial::sDefault; - uint64 mGroundUserData = 0; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Character/CharacterVirtual.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Character/CharacterVirtual.cpp deleted file mode 100644 index b4b12ecfd29..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Character/CharacterVirtual.cpp +++ /dev/null @@ -1,1512 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef JPH_DEBUG_RENDERER - #include -#endif // JPH_DEBUG_RENDERER - -JPH_NAMESPACE_BEGIN - -CharacterVirtual::CharacterVirtual(const CharacterVirtualSettings *inSettings, RVec3Arg inPosition, QuatArg inRotation, uint64 inUserData, PhysicsSystem *inSystem) : - CharacterBase(inSettings, inSystem), - mBackFaceMode(inSettings->mBackFaceMode), - mPredictiveContactDistance(inSettings->mPredictiveContactDistance), - mMaxCollisionIterations(inSettings->mMaxCollisionIterations), - mMaxConstraintIterations(inSettings->mMaxConstraintIterations), - mMinTimeRemaining(inSettings->mMinTimeRemaining), - mCollisionTolerance(inSettings->mCollisionTolerance), - mCharacterPadding(inSettings->mCharacterPadding), - mMaxNumHits(inSettings->mMaxNumHits), - mHitReductionCosMaxAngle(inSettings->mHitReductionCosMaxAngle), - mPenetrationRecoverySpeed(inSettings->mPenetrationRecoverySpeed), - mShapeOffset(inSettings->mShapeOffset), - mPosition(inPosition), - mRotation(inRotation), - mUserData(inUserData) -{ - // Copy settings - SetMaxStrength(inSettings->mMaxStrength); - SetMass(inSettings->mMass); -} - -void CharacterVirtual::GetAdjustedBodyVelocity(const Body& inBody, Vec3 &outLinearVelocity, Vec3 &outAngularVelocity) const -{ - // Get real velocity of body - if (!inBody.IsStatic()) - { - const MotionProperties *mp = inBody.GetMotionPropertiesUnchecked(); - outLinearVelocity = mp->GetLinearVelocity(); - outAngularVelocity = mp->GetAngularVelocity(); - } - else - { - outLinearVelocity = outAngularVelocity = Vec3::sZero(); - } - - // Allow application to override - if (mListener != nullptr) - mListener->OnAdjustBodyVelocity(this, inBody, outLinearVelocity, outAngularVelocity); -} - -Vec3 CharacterVirtual::CalculateCharacterGroundVelocity(RVec3Arg inCenterOfMass, Vec3Arg inLinearVelocity, Vec3Arg inAngularVelocity, float inDeltaTime) const -{ - // Get angular velocity - float angular_velocity_len_sq = inAngularVelocity.LengthSq(); - if (angular_velocity_len_sq < 1.0e-12f) - return inLinearVelocity; - float angular_velocity_len = sqrt(angular_velocity_len_sq); - - // Calculate the rotation that the object will make in the time step - Quat rotation = Quat::sRotation(inAngularVelocity / angular_velocity_len, angular_velocity_len * inDeltaTime); - - // Calculate where the new character position will be - RVec3 new_position = inCenterOfMass + rotation * Vec3(mPosition - inCenterOfMass); - - // Calculate the velocity - return inLinearVelocity + Vec3(new_position - mPosition) / inDeltaTime; -} - -template -void CharacterVirtual::sFillContactProperties(const CharacterVirtual *inCharacter, Contact &outContact, const Body &inBody, Vec3Arg inUp, RVec3Arg inBaseOffset, const taCollector &inCollector, const CollideShapeResult &inResult) -{ - // Get adjusted body velocity - Vec3 linear_velocity, angular_velocity; - inCharacter->GetAdjustedBodyVelocity(inBody, linear_velocity, angular_velocity); - - outContact.mPosition = inBaseOffset + inResult.mContactPointOn2; - outContact.mLinearVelocity = linear_velocity + angular_velocity.Cross(Vec3(outContact.mPosition - inBody.GetCenterOfMassPosition())); // Calculate point velocity - outContact.mContactNormal = -inResult.mPenetrationAxis.NormalizedOr(Vec3::sZero()); - outContact.mSurfaceNormal = inCollector.GetContext()->GetWorldSpaceSurfaceNormal(inResult.mSubShapeID2, outContact.mPosition); - if (outContact.mContactNormal.Dot(outContact.mSurfaceNormal) < 0.0f) - outContact.mSurfaceNormal = -outContact.mSurfaceNormal; // Flip surface normal if we're hitting a back face - if (outContact.mContactNormal.Dot(inUp) > outContact.mSurfaceNormal.Dot(inUp)) - outContact.mSurfaceNormal = outContact.mContactNormal; // Replace surface normal with contact normal if the contact normal is pointing more upwards - outContact.mDistance = -inResult.mPenetrationDepth; - outContact.mBodyB = inResult.mBodyID2; - outContact.mSubShapeIDB = inResult.mSubShapeID2; - outContact.mMotionTypeB = inBody.GetMotionType(); - outContact.mIsSensorB = inBody.IsSensor(); - outContact.mUserData = inBody.GetUserData(); - outContact.mMaterial = inCollector.GetContext()->GetMaterial(inResult.mSubShapeID2); -} - -void CharacterVirtual::ContactCollector::AddHit(const CollideShapeResult &inResult) -{ - // If we exceed our contact limit, try to clean up near-duplicate contacts - if (mContacts.size() == mMaxHits) - { - // Flag that we hit this code path - mMaxHitsExceeded = true; - - // Check if we can do reduction - if (mHitReductionCosMaxAngle > -1.0f) - { - // Loop all contacts and find similar contacts - for (int i = (int)mContacts.size() - 1; i >= 0; --i) - { - Contact &contact_i = mContacts[i]; - for (int j = i - 1; j >= 0; --j) - { - Contact &contact_j = mContacts[j]; - if (contact_i.mBodyB == contact_j.mBodyB // Same body - && contact_i.mContactNormal.Dot(contact_j.mContactNormal) > mHitReductionCosMaxAngle) // Very similar contact normals - { - // Remove the contact with the biggest distance - bool i_is_last = i == (int)mContacts.size() - 1; - if (contact_i.mDistance > contact_j.mDistance) - { - // Remove i - if (!i_is_last) - contact_i = mContacts.back(); - mContacts.pop_back(); - - // Break out of the loop, i is now an element that we already processed - break; - } - else - { - // Remove j - contact_j = mContacts.back(); - mContacts.pop_back(); - - // If i was the last element, we just moved it into position j. Break out of the loop, we'll see it again later. - if (i_is_last) - break; - } - } - } - } - } - - if (mContacts.size() == mMaxHits) - { - // There are still too many hits, give up! - ForceEarlyOut(); - return; - } - } - - BodyLockRead lock(mSystem->GetBodyLockInterface(), inResult.mBodyID2); - if (lock.SucceededAndIsInBroadPhase()) - { - mContacts.emplace_back(); - Contact &contact = mContacts.back(); - sFillContactProperties(mCharacter, contact, lock.GetBody(), mUp, mBaseOffset, *this, inResult); - contact.mFraction = 0.0f; - } -} - -void CharacterVirtual::ContactCastCollector::AddHit(const ShapeCastResult &inResult) -{ - // Should not have gotten here without a lower fraction - JPH_ASSERT(inResult.mFraction < mContact.mFraction); - - if (inResult.mFraction > 0.0f // Ignore collisions at fraction = 0 - && inResult.mPenetrationAxis.Dot(mDisplacement) > 0.0f) // Ignore penetrations that we're moving away from - { - // Test if this contact should be ignored - for (const IgnoredContact &c : mIgnoredContacts) - if (c.mBodyID == inResult.mBodyID2 && c.mSubShapeID == inResult.mSubShapeID2) - return; - - Contact contact; - - // Lock body only while we fetch contact properties - { - BodyLockRead lock(mSystem->GetBodyLockInterface(), inResult.mBodyID2); - if (!lock.SucceededAndIsInBroadPhase()) - return; - - // Sweeps don't result in OnContactAdded callbacks so we can ignore sensors here - const Body &body = lock.GetBody(); - if (body.IsSensor()) - return; - - // Convert the hit result into a contact - sFillContactProperties(mCharacter, contact, body, mUp, mBaseOffset, *this, inResult); - } - - contact.mFraction = inResult.mFraction; - - // Check if the contact that will make us penetrate more than the allowed tolerance - if (contact.mDistance + contact.mContactNormal.Dot(mDisplacement) < -mCharacter->mCollisionTolerance - && mCharacter->ValidateContact(contact)) - { - mContact = contact; - UpdateEarlyOutFraction(contact.mFraction); - } - } -} - -void CharacterVirtual::CheckCollision(RVec3Arg inPosition, QuatArg inRotation, Vec3Arg inMovementDirection, float inMaxSeparationDistance, const Shape *inShape, RVec3Arg inBaseOffset, CollideShapeCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter) const -{ - // Query shape transform - RMat44 transform = GetCenterOfMassTransform(inPosition, inRotation, inShape); - - // Settings for collide shape - CollideShapeSettings settings; - settings.mActiveEdgeMode = EActiveEdgeMode::CollideOnlyWithActive; - settings.mBackFaceMode = mBackFaceMode; - settings.mActiveEdgeMovementDirection = inMovementDirection; - settings.mMaxSeparationDistance = mCharacterPadding + inMaxSeparationDistance; - - // Collide shape - mSystem->GetNarrowPhaseQuery().CollideShape(inShape, Vec3::sReplicate(1.0f), transform, settings, inBaseOffset, ioCollector, inBroadPhaseLayerFilter, inObjectLayerFilter, inBodyFilter, inShapeFilter); -} - -void CharacterVirtual::GetContactsAtPosition(RVec3Arg inPosition, Vec3Arg inMovementDirection, const Shape *inShape, TempContactList &outContacts, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter) const -{ - // Remove previous results - outContacts.clear(); - - // Collide shape - ContactCollector collector(mSystem, this, mMaxNumHits, mHitReductionCosMaxAngle, mUp, mPosition, outContacts); - CheckCollision(inPosition, mRotation, inMovementDirection, mPredictiveContactDistance, inShape, mPosition, collector, inBroadPhaseLayerFilter, inObjectLayerFilter, inBodyFilter, inShapeFilter); - - // The broadphase bounding boxes will not be deterministic, which means that the order in which the contacts are received by the collector is not deterministic. - // Therefore we need to sort the contacts to preserve determinism. Note that currently this will fail if we exceed mMaxNumHits hits. - QuickSort(outContacts.begin(), outContacts.end(), ContactOrderingPredicate()); - - // Flag if we exceeded the max number of hits - mMaxHitsExceeded = collector.mMaxHitsExceeded; - - // Reduce distance to contact by padding to ensure we stay away from the object by a little margin - // (this will make collision detection cheaper - especially for sweep tests as they won't hit the surface if we're properly sliding) - for (Contact &c : outContacts) - c.mDistance -= mCharacterPadding; -} - -void CharacterVirtual::RemoveConflictingContacts(TempContactList &ioContacts, IgnoredContactList &outIgnoredContacts) const -{ - // Only use this algorithm if we're penetrating further than this (due to numerical precision issues we can always penetrate a little bit and we don't want to discard contacts if they just have a tiny penetration) - // We do need to account for padding (see GetContactsAtPosition) that is removed from the contact distances, to compensate we add it to the cMinRequiredPenetration - const float cMinRequiredPenetration = 1.25f * mCharacterPadding; - - // Discard conflicting penetrating contacts - for (size_t c1 = 0; c1 < ioContacts.size(); c1++) - { - Contact &contact1 = ioContacts[c1]; - if (contact1.mDistance <= -cMinRequiredPenetration) // Only for penetrations - for (size_t c2 = c1 + 1; c2 < ioContacts.size(); c2++) - { - Contact &contact2 = ioContacts[c2]; - if (contact1.mBodyB == contact2.mBodyB // Only same body - && contact2.mDistance <= -cMinRequiredPenetration // Only for penetrations - && contact1.mContactNormal.Dot(contact2.mContactNormal) < 0.0f) // Only opposing normals - { - // Discard contacts with the least amount of penetration - if (contact1.mDistance < contact2.mDistance) - { - // Discard the 2nd contact - outIgnoredContacts.emplace_back(contact2.mBodyB, contact2.mSubShapeIDB); - ioContacts.erase(ioContacts.begin() + c2); - c2--; - } - else - { - // Discard the first contact - outIgnoredContacts.emplace_back(contact1.mBodyB, contact1.mSubShapeIDB); - ioContacts.erase(ioContacts.begin() + c1); - c1--; - break; - } - } - } - } -} - -bool CharacterVirtual::ValidateContact(const Contact &inContact) const -{ - if (mListener == nullptr) - return true; - - return mListener->OnContactValidate(this, inContact.mBodyB, inContact.mSubShapeIDB); -} - -template -inline static bool sCorrectFractionForCharacterPadding(const Shape *inShape, Mat44Arg inStart, Vec3Arg inDisplacement, const T &inPolygon, float &ioFraction) -{ - if (inShape->GetType() == EShapeType::Convex) - { - // Get the support function for the shape we're casting - const ConvexShape *convex_shape = static_cast(inShape); - ConvexShape::SupportBuffer buffer; - const ConvexShape::Support *support = convex_shape->GetSupportFunction(ConvexShape::ESupportMode::IncludeConvexRadius, buffer, Vec3::sReplicate(1.0f)); - - // Cast the shape against the polygon - GJKClosestPoint gjk; - return gjk.CastShape(inStart, inDisplacement, cDefaultCollisionTolerance, *support, inPolygon, ioFraction); - } - else if (inShape->GetSubType() == EShapeSubType::RotatedTranslated) - { - const RotatedTranslatedShape *rt_shape = static_cast(inShape); - return sCorrectFractionForCharacterPadding(rt_shape->GetInnerShape(), inStart * Mat44::sRotation(rt_shape->GetRotation()), inDisplacement, inPolygon, ioFraction); - } - else - { - JPH_ASSERT(false, "Not supported yet!"); - return false; - } -} - -bool CharacterVirtual::GetFirstContactForSweep(RVec3Arg inPosition, Vec3Arg inDisplacement, Contact &outContact, const IgnoredContactList &inIgnoredContacts, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter) const -{ - // Too small distance -> skip checking - float displacement_len_sq = inDisplacement.LengthSq(); - if (displacement_len_sq < 1.0e-8f) - return false; - - // Calculate start transform - RMat44 start = GetCenterOfMassTransform(inPosition, mRotation, mShape); - - // Settings for the cast - ShapeCastSettings settings; - settings.mBackFaceModeTriangles = mBackFaceMode; - settings.mBackFaceModeConvex = EBackFaceMode::IgnoreBackFaces; - settings.mActiveEdgeMode = EActiveEdgeMode::CollideOnlyWithActive; - settings.mUseShrunkenShapeAndConvexRadius = true; - settings.mReturnDeepestPoint = false; - - // Calculate how much extra fraction we need to add to the cast to account for the character padding - float character_padding_fraction = mCharacterPadding / sqrt(displacement_len_sq); - - // Cast shape - Contact contact; - contact.mFraction = 1.0f + character_padding_fraction; - ContactCastCollector collector(mSystem, this, inDisplacement, mUp, inIgnoredContacts, start.GetTranslation(), contact); - collector.ResetEarlyOutFraction(contact.mFraction); - RShapeCast shape_cast(mShape, Vec3::sReplicate(1.0f), start, inDisplacement); - mSystem->GetNarrowPhaseQuery().CastShape(shape_cast, settings, start.GetTranslation(), collector, inBroadPhaseLayerFilter, inObjectLayerFilter, inBodyFilter, inShapeFilter); - if (contact.mBodyB.IsInvalid()) - return false; - - // Store contact - outContact = contact; - - // Fetch the face we're colliding with - TransformedShape ts = mSystem->GetBodyInterface().GetTransformedShape(outContact.mBodyB); - Shape::SupportingFace face; - ts.GetSupportingFace(outContact.mSubShapeIDB, -outContact.mContactNormal, start.GetTranslation(), face); - - bool corrected = false; - if (face.size() >= 2) - { - // Inflate the colliding face by the character padding - PolygonConvexSupport polygon(face); - AddConvexRadius add_cvx(polygon, mCharacterPadding); - - // Correct fraction to hit this inflated face instead of the inner shape - corrected = sCorrectFractionForCharacterPadding(mShape, start.GetRotation(), inDisplacement, add_cvx, outContact.mFraction); - } - if (!corrected) - { - // When there's only a single contact point or when we were unable to correct the fraction, - // we can just move the fraction back so that the character and its padding don't hit the contact point anymore - outContact.mFraction = max(0.0f, outContact.mFraction - character_padding_fraction); - } - - // Ensure that we never return a fraction that's bigger than 1 (which could happen due to float precision issues). - outContact.mFraction = min(outContact.mFraction, 1.0f); - - return true; -} - -void CharacterVirtual::DetermineConstraints(TempContactList &inContacts, float inDeltaTime, ConstraintList &outConstraints) const -{ - for (Contact &c : inContacts) - { - Vec3 contact_velocity = c.mLinearVelocity; - - // Penetrating contact: Add a contact velocity that pushes the character out at the desired speed - if (c.mDistance < 0.0f) - contact_velocity -= c.mContactNormal * c.mDistance * mPenetrationRecoverySpeed / inDeltaTime; - - // Convert to a constraint - outConstraints.emplace_back(); - Constraint &constraint = outConstraints.back(); - constraint.mContact = &c; - constraint.mLinearVelocity = contact_velocity; - constraint.mPlane = Plane(c.mContactNormal, c.mDistance); - - // Next check if the angle is too steep and if it is add an additional constraint that holds the character back - if (IsSlopeTooSteep(c.mSurfaceNormal)) - { - // Only take planes that point up. - // Note that we use the contact normal to allow for better sliding as the surface normal may be in the opposite direction of movement. - float dot = c.mContactNormal.Dot(mUp); - if (dot > 1.0e-3f) // Add a little slack, if the normal is perfectly horizontal we already have our vertical plane. - { - // Mark the slope constraint as steep - constraint.mIsSteepSlope = true; - - // Make horizontal normal - Vec3 normal = (c.mContactNormal - dot * mUp).Normalized(); - - // Create a secondary constraint that blocks horizontal movement - outConstraints.emplace_back(); - Constraint &vertical_constraint = outConstraints.back(); - vertical_constraint.mContact = &c; - vertical_constraint.mLinearVelocity = contact_velocity.Dot(normal) * normal; // Project the contact velocity on the new normal so that both planes push at an equal rate - vertical_constraint.mPlane = Plane(normal, c.mDistance / normal.Dot(c.mContactNormal)); // Calculate the distance we have to travel horizontally to hit the contact plane - } - } - } -} - -bool CharacterVirtual::HandleContact(Vec3Arg inVelocity, Constraint &ioConstraint, float inDeltaTime) const -{ - Contact &contact = *ioConstraint.mContact; - - // Validate the contact point - if (!ValidateContact(contact)) - return false; - - // Send contact added event - CharacterContactSettings settings; - if (mListener != nullptr) - mListener->OnContactAdded(this, contact.mBodyB, contact.mSubShapeIDB, contact.mPosition, -contact.mContactNormal, settings); - contact.mCanPushCharacter = settings.mCanPushCharacter; - - // We don't have any further interaction with sensors beyond an OnContactAdded notification - if (contact.mIsSensorB) - return false; - - // If body B cannot receive an impulse, we're done - if (!settings.mCanReceiveImpulses || contact.mMotionTypeB != EMotionType::Dynamic) - return true; - - // Lock the body we're colliding with - BodyLockWrite lock(mSystem->GetBodyLockInterface(), contact.mBodyB); - if (!lock.SucceededAndIsInBroadPhase()) - return false; // Body has been removed, we should not collide with it anymore - const Body &body = lock.GetBody(); - - // Calculate the velocity that we want to apply at B so that it will start moving at the character's speed at the contact point - constexpr float cDamping = 0.9f; - constexpr float cPenetrationResolution = 0.4f; - Vec3 relative_velocity = inVelocity - contact.mLinearVelocity; - float projected_velocity = relative_velocity.Dot(contact.mContactNormal); - float delta_velocity = -projected_velocity * cDamping - min(contact.mDistance, 0.0f) * cPenetrationResolution / inDeltaTime; - - // Don't apply impulses if we're separating - if (delta_velocity < 0.0f) - return true; - - // Determine mass properties of the body we're colliding with - const MotionProperties *motion_properties = body.GetMotionProperties(); - RVec3 center_of_mass = body.GetCenterOfMassPosition(); - Mat44 inverse_inertia = body.GetInverseInertia(); - float inverse_mass = motion_properties->GetInverseMass(); - - // Calculate the inverse of the mass of body B as seen at the contact point in the direction of the contact normal - Vec3 jacobian = Vec3(contact.mPosition - center_of_mass).Cross(contact.mContactNormal); - float inv_effective_mass = inverse_inertia.Multiply3x3(jacobian).Dot(jacobian) + inverse_mass; - - // Impulse P = M dv - float impulse = delta_velocity / inv_effective_mass; - - // Clamp the impulse according to the character strength, character strength is a force in newtons, P = F dt - float max_impulse = mMaxStrength * inDeltaTime; - impulse = min(impulse, max_impulse); - - // Calculate the world space impulse to apply - Vec3 world_impulse = -impulse * contact.mContactNormal; - - // Cancel impulse in down direction (we apply gravity later) - float impulse_dot_up = world_impulse.Dot(mUp); - if (impulse_dot_up < 0.0f) - world_impulse -= impulse_dot_up * mUp; - - // Now apply the impulse (body is already locked so we use the no-lock interface) - mSystem->GetBodyInterfaceNoLock().AddImpulse(contact.mBodyB, world_impulse, contact.mPosition); - return true; -} - -void CharacterVirtual::SolveConstraints(Vec3Arg inVelocity, float inDeltaTime, float inTimeRemaining, ConstraintList &ioConstraints, IgnoredContactList &ioIgnoredContacts, float &outTimeSimulated, Vec3 &outDisplacement, TempAllocator &inAllocator -#ifdef JPH_DEBUG_RENDERER - , bool inDrawConstraints -#endif // JPH_DEBUG_RENDERER - ) const -{ - // If there are no constraints we can immediately move to our target - if (ioConstraints.empty()) - { - outDisplacement = inVelocity * inTimeRemaining; - outTimeSimulated = inTimeRemaining; - return; - } - - // Create array that holds the constraints in order of time of impact (sort will happen later) - std::vector> sorted_constraints(inAllocator); - sorted_constraints.resize(ioConstraints.size()); - for (size_t index = 0; index < sorted_constraints.size(); index++) - sorted_constraints[index] = &ioConstraints[index]; - - // This is the velocity we use for the displacement, if we hit something it will be shortened - Vec3 velocity = inVelocity; - - // Keep track of the last velocity that was applied to the character so that we can detect when the velocity reverses - Vec3 last_velocity = inVelocity; - - // Start with no displacement - outDisplacement = Vec3::sZero(); - outTimeSimulated = 0.0f; - - // These are the contacts that we hit previously without moving a significant distance - std::vector> previous_contacts(inAllocator); - previous_contacts.resize(mMaxConstraintIterations); - int num_previous_contacts = 0; - - // Loop for a max amount of iterations - for (uint iteration = 0; iteration < mMaxConstraintIterations; iteration++) - { - // Calculate time of impact for all constraints - for (Constraint &c : ioConstraints) - { - // Project velocity on plane direction - c.mProjectedVelocity = c.mPlane.GetNormal().Dot(c.mLinearVelocity - velocity); - if (c.mProjectedVelocity < 1.0e-6f) - { - c.mTOI = FLT_MAX; - } - else - { - // Distance to plane - float dist = c.mPlane.SignedDistance(outDisplacement); - - if (dist - c.mProjectedVelocity * inTimeRemaining > -1.0e-4f) - { - // Too little penetration, accept the movement - c.mTOI = FLT_MAX; - } - else - { - // Calculate time of impact - c.mTOI = max(0.0f, dist / c.mProjectedVelocity); - } - } - } - - // Sort constraints on proximity - QuickSort(sorted_constraints.begin(), sorted_constraints.end(), [](const Constraint *inLHS, const Constraint *inRHS) { - // If both constraints hit at t = 0 then order the one that will push the character furthest first - // Note that because we add velocity to penetrating contacts, this will also resolve contacts that penetrate the most - if (inLHS->mTOI <= 0.0f && inRHS->mTOI <= 0.0f) - return inLHS->mProjectedVelocity > inRHS->mProjectedVelocity; - - // Then sort on time of impact - if (inLHS->mTOI != inRHS->mTOI) - return inLHS->mTOI < inRHS->mTOI; - - // As a tie breaker sort static first so it has the most influence - return inLHS->mContact->mMotionTypeB > inRHS->mContact->mMotionTypeB; - }); - - // Find the first valid constraint - Constraint *constraint = nullptr; - for (Constraint *c : sorted_constraints) - { - // Take the first contact and see if we can reach it - if (c->mTOI >= inTimeRemaining) - { - // We can reach our goal! - outDisplacement += velocity * inTimeRemaining; - outTimeSimulated += inTimeRemaining; - return; - } - - // Test if this contact was discarded by the contact callback before - if (c->mContact->mWasDiscarded) - continue; - - // Check if we made contact with this before - if (!c->mContact->mHadCollision) - { - // Handle the contact - if (!HandleContact(velocity, *c, inDeltaTime)) - { - // Constraint should be ignored, remove it from the list - c->mContact->mWasDiscarded = true; - - // Mark it as ignored for GetFirstContactForSweep - ioIgnoredContacts.emplace_back(c->mContact->mBodyB, c->mContact->mSubShapeIDB); - continue; - } - - c->mContact->mHadCollision = true; - } - - // Cancel velocity of constraint if it cannot push the character - if (!c->mContact->mCanPushCharacter) - c->mLinearVelocity = Vec3::sZero(); - - // We found the first constraint that we want to collide with - constraint = c; - break; - } - - if (constraint == nullptr) - { - // All constraints were discarded, we can reach our goal! - outDisplacement += velocity * inTimeRemaining; - outTimeSimulated += inTimeRemaining; - return; - } - - // Move to the contact - outDisplacement += velocity * constraint->mTOI; - inTimeRemaining -= constraint->mTOI; - outTimeSimulated += constraint->mTOI; - - // If there's not enough time left to be simulated, bail - if (inTimeRemaining < mMinTimeRemaining) - return; - - // If we've moved significantly, clear all previous contacts - if (constraint->mTOI > 1.0e-4f) - num_previous_contacts = 0; - - // Get the normal of the plane we're hitting - Vec3 plane_normal = constraint->mPlane.GetNormal(); - - // If we're hitting a steep slope we cancel the velocity towards the slope first so that we don't end up sliding up the slope - // (we may hit the slope before the vertical wall constraint we added which will result in a small movement up causing jitter in the character movement) - if (constraint->mIsSteepSlope) - { - // We're hitting a steep slope, create a vertical plane that blocks any further movement up the slope (note: not normalized) - Vec3 vertical_plane_normal = plane_normal - plane_normal.Dot(mUp) * mUp; - - // Get the relative velocity between the character and the constraint - Vec3 relative_velocity = velocity - constraint->mLinearVelocity; - - // Remove velocity towards the slope - velocity = velocity - min(0.0f, relative_velocity.Dot(vertical_plane_normal)) * vertical_plane_normal / vertical_plane_normal.LengthSq(); - } - - // Get the relative velocity between the character and the constraint - Vec3 relative_velocity = velocity - constraint->mLinearVelocity; - - // Calculate new velocity if we cancel the relative velocity in the normal direction - Vec3 new_velocity = velocity - relative_velocity.Dot(plane_normal) * plane_normal; - - // Find the normal of the previous contact that we will violate the most if we move in this new direction - float highest_penetration = 0.0f; - Constraint *other_constraint = nullptr; - for (Constraint **c = previous_contacts.data(); c < previous_contacts.data() + num_previous_contacts; ++c) - if (*c != constraint) - { - // Calculate how much we will penetrate if we move in this direction - Vec3 other_normal = (*c)->mPlane.GetNormal(); - float penetration = ((*c)->mLinearVelocity - new_velocity).Dot(other_normal); - if (penetration > highest_penetration) - { - // We don't want parallel or anti-parallel normals as that will cause our cross product below to become zero. Slack is approx 10 degrees. - float dot = other_normal.Dot(plane_normal); - if (dot < 0.984f && dot > -0.984f) - { - highest_penetration = penetration; - other_constraint = *c; - } - } - } - - // Check if we found a 2nd constraint - if (other_constraint != nullptr) - { - // Calculate the sliding direction and project the new velocity onto that sliding direction - Vec3 other_normal = other_constraint->mPlane.GetNormal(); - Vec3 slide_dir = plane_normal.Cross(other_normal).Normalized(); - Vec3 velocity_in_slide_dir = new_velocity.Dot(slide_dir) * slide_dir; - - // Cancel the constraint velocity in the other constraint plane's direction so that we won't try to apply it again and keep ping ponging between planes - constraint->mLinearVelocity -= min(0.0f, constraint->mLinearVelocity.Dot(other_normal)) * other_normal; - - // Cancel the other constraints velocity in this constraint plane's direction so that we won't try to apply it again and keep ping ponging between planes - other_constraint->mLinearVelocity -= min(0.0f, other_constraint->mLinearVelocity.Dot(plane_normal)) * plane_normal; - - // Calculate the velocity of this constraint perpendicular to the slide direction - Vec3 perpendicular_velocity = constraint->mLinearVelocity - constraint->mLinearVelocity.Dot(slide_dir) * slide_dir; - - // Calculate the velocity of the other constraint perpendicular to the slide direction - Vec3 other_perpendicular_velocity = other_constraint->mLinearVelocity - other_constraint->mLinearVelocity.Dot(slide_dir) * slide_dir; - - // Add all components together - new_velocity = velocity_in_slide_dir + perpendicular_velocity + other_perpendicular_velocity; - } - - // Allow application to modify calculated velocity - if (mListener != nullptr) - mListener->OnContactSolve(this, constraint->mContact->mBodyB, constraint->mContact->mSubShapeIDB, constraint->mContact->mPosition, constraint->mContact->mContactNormal, constraint->mContact->mLinearVelocity, constraint->mContact->mMaterial, velocity, new_velocity); - -#ifdef JPH_DEBUG_RENDERER - if (inDrawConstraints) - { - // Calculate where to draw - RVec3 offset = mPosition + Vec3(0, 0, 2.5f * (iteration + 1)); - - // Draw constraint plane - DebugRenderer::sInstance->DrawPlane(offset, constraint->mPlane.GetNormal(), Color::sCyan, 1.0f); - - // Draw 2nd constraint plane - if (other_constraint != nullptr) - DebugRenderer::sInstance->DrawPlane(offset, other_constraint->mPlane.GetNormal(), Color::sBlue, 1.0f); - - // Draw starting velocity - DebugRenderer::sInstance->DrawArrow(offset, offset + velocity, Color::sGreen, 0.05f); - - // Draw resulting velocity - DebugRenderer::sInstance->DrawArrow(offset, offset + new_velocity, Color::sRed, 0.05f); - } -#endif // JPH_DEBUG_RENDERER - - // Update the velocity - velocity = new_velocity; - - // Add the contact to the list so that next iteration we can avoid violating it again - previous_contacts[num_previous_contacts] = constraint; - num_previous_contacts++; - - // Check early out - if (constraint->mProjectedVelocity < 1.0e-8f // Constraint should not be pushing, otherwise there may be other constraints that are pushing us - && velocity.LengthSq() < 1.0e-8f) // There's not enough velocity left - return; - - // If the constraint has velocity we accept the new velocity, otherwise check that we didn't reverse velocity - if (!constraint->mLinearVelocity.IsNearZero(1.0e-8f)) - last_velocity = constraint->mLinearVelocity; - else if (velocity.Dot(last_velocity) < 0.0f) - return; - } -} - -void CharacterVirtual::UpdateSupportingContact(bool inSkipContactVelocityCheck, TempAllocator &inAllocator) -{ - // Flag contacts as having a collision if they're close enough but ignore contacts we're moving away from. - // Note that if we did MoveShape before we want to preserve any contacts that it marked as colliding - for (Contact &c : mActiveContacts) - if (!c.mWasDiscarded - && !c.mHadCollision - && c.mDistance < mCollisionTolerance - && (inSkipContactVelocityCheck || c.mSurfaceNormal.Dot(mLinearVelocity - c.mLinearVelocity) <= 1.0e-4f)) - { - if (ValidateContact(c) && !c.mIsSensorB) - c.mHadCollision = true; - else - c.mWasDiscarded = true; - } - - // Calculate transform that takes us to character local space - RMat44 inv_transform = RMat44::sInverseRotationTranslation(mRotation, mPosition); - - // Determine if we're supported or not - int num_supported = 0; - int num_sliding = 0; - int num_avg_normal = 0; - Vec3 avg_normal = Vec3::sZero(); - Vec3 avg_velocity = Vec3::sZero(); - const Contact *supporting_contact = nullptr; - float max_cos_angle = -FLT_MAX; - const Contact *deepest_contact = nullptr; - float smallest_distance = FLT_MAX; - for (const Contact &c : mActiveContacts) - if (c.mHadCollision) - { - // Calculate the angle between the plane normal and the up direction - float cos_angle = c.mSurfaceNormal.Dot(mUp); - - // Find the deepest contact - if (c.mDistance < smallest_distance) - { - deepest_contact = &c; - smallest_distance = c.mDistance; - } - - // If this contact is in front of our plane, we cannot be supported by it - if (mSupportingVolume.SignedDistance(Vec3(inv_transform * c.mPosition)) > 0.0f) - continue; - - // Find the contact with the normal that is pointing most upwards and store it - if (max_cos_angle < cos_angle) - { - supporting_contact = &c; - max_cos_angle = cos_angle; - } - - // Check if this is a sliding or supported contact - bool is_supported = mCosMaxSlopeAngle > cNoMaxSlopeAngle || cos_angle >= mCosMaxSlopeAngle; - if (is_supported) - num_supported++; - else - num_sliding++; - - // If the angle between the two is less than 85 degrees we also use it to calculate the average normal - if (cos_angle >= 0.08f) - { - avg_normal += c.mSurfaceNormal; - num_avg_normal++; - - // For static or dynamic objects or for contacts that don't support us just take the contact velocity - if (c.mMotionTypeB != EMotionType::Kinematic || !is_supported) - avg_velocity += c.mLinearVelocity; - else - { - // For keyframed objects that support us calculate the velocity at our position rather than at the contact position so that we properly follow the object - BodyLockRead lock(mSystem->GetBodyLockInterface(), c.mBodyB); - if (lock.SucceededAndIsInBroadPhase()) - { - const Body &body = lock.GetBody(); - - // Get adjusted body velocity - Vec3 linear_velocity, angular_velocity; - GetAdjustedBodyVelocity(body, linear_velocity, angular_velocity); - - // Calculate the ground velocity - avg_velocity += CalculateCharacterGroundVelocity(body.GetCenterOfMassPosition(), linear_velocity, angular_velocity, mLastDeltaTime); - } - else - { - // Fall back to contact velocity - avg_velocity += c.mLinearVelocity; - } - } - } - } - - // Take either the most supporting contact or the deepest contact - const Contact *best_contact = supporting_contact != nullptr? supporting_contact : deepest_contact; - - // Calculate average normal and velocity - if (num_avg_normal >= 1) - { - mGroundNormal = avg_normal.Normalized(); - mGroundVelocity = avg_velocity / float(num_avg_normal); - } - else if (best_contact != nullptr) - { - mGroundNormal = best_contact->mSurfaceNormal; - mGroundVelocity = best_contact->mLinearVelocity; - } - else - { - mGroundNormal = Vec3::sZero(); - mGroundVelocity = Vec3::sZero(); - } - - // Copy contact properties - if (best_contact != nullptr) - { - mGroundBodyID = best_contact->mBodyB; - mGroundBodySubShapeID = best_contact->mSubShapeIDB; - mGroundPosition = best_contact->mPosition; - mGroundMaterial = best_contact->mMaterial; - mGroundUserData = best_contact->mUserData; - } - else - { - mGroundBodyID = BodyID(); - mGroundBodySubShapeID = SubShapeID(); - mGroundPosition = RVec3::sZero(); - mGroundMaterial = PhysicsMaterial::sDefault; - mGroundUserData = 0; - } - - // Determine ground state - if (num_supported > 0) - { - // We made contact with something that supports us - mGroundState = EGroundState::OnGround; - } - else if (num_sliding > 0) - { - if ((mLinearVelocity - deepest_contact->mLinearVelocity).Dot(mUp) > 1.0e-4f) - { - // We cannot be on ground if we're moving upwards relative to the ground - mGroundState = EGroundState::OnSteepGround; - } - else - { - // If we're sliding down, we may actually be standing on multiple sliding contacts in such a way that we can't slide off, in this case we're also supported - - // Convert the contacts into constraints - TempContactList contacts(mActiveContacts.begin(), mActiveContacts.end(), inAllocator); - ConstraintList constraints(inAllocator); - constraints.reserve(contacts.size() * 2); - DetermineConstraints(contacts, mLastDeltaTime, constraints); - - // Solve the displacement using these constraints, this is used to check if we didn't move at all because we are supported - Vec3 displacement; - float time_simulated; - IgnoredContactList ignored_contacts(inAllocator); - ignored_contacts.reserve(contacts.size()); - SolveConstraints(-mUp, 1.0f, 1.0f, constraints, ignored_contacts, time_simulated, displacement, inAllocator); - - // If we're blocked then we're supported, otherwise we're sliding - float min_required_displacement_sq = Square(0.6f * mLastDeltaTime); - if (time_simulated < 0.001f || displacement.LengthSq() < min_required_displacement_sq) - mGroundState = EGroundState::OnGround; - else - mGroundState = EGroundState::OnSteepGround; - } - } - else - { - // Not supported by anything - mGroundState = best_contact != nullptr? EGroundState::NotSupported : EGroundState::InAir; - } -} - -void CharacterVirtual::StoreActiveContacts(const TempContactList &inContacts, TempAllocator &inAllocator) -{ - mActiveContacts.assign(inContacts.begin(), inContacts.end()); - - UpdateSupportingContact(true, inAllocator); -} - -void CharacterVirtual::MoveShape(RVec3 &ioPosition, Vec3Arg inVelocity, float inDeltaTime, ContactList *outActiveContacts, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter, TempAllocator &inAllocator -#ifdef JPH_DEBUG_RENDERER - , bool inDrawConstraints -#endif // JPH_DEBUG_RENDERER - ) const -{ - JPH_DET_LOG("CharacterVirtual::MoveShape: pos: " << ioPosition << " vel: " << inVelocity << " dt: " << inDeltaTime); - - Vec3 movement_direction = inVelocity.NormalizedOr(Vec3::sZero()); - - float time_remaining = inDeltaTime; - for (uint iteration = 0; iteration < mMaxCollisionIterations && time_remaining >= mMinTimeRemaining; iteration++) - { - JPH_DET_LOG("iter: " << iteration << " time: " << time_remaining); - - // Determine contacts in the neighborhood - TempContactList contacts(inAllocator); - contacts.reserve(mMaxNumHits); - GetContactsAtPosition(ioPosition, movement_direction, mShape, contacts, inBroadPhaseLayerFilter, inObjectLayerFilter, inBodyFilter, inShapeFilter); - -#ifdef JPH_ENABLE_DETERMINISM_LOG - for (const Contact &c : contacts) - JPH_DET_LOG("contact: " << c.mPosition << " vel: " << c.mLinearVelocity << " cnormal: " << c.mContactNormal << " snormal: " << c.mSurfaceNormal << " dist: " << c.mDistance << " fraction: " << c.mFraction << " body: " << c.mBodyB << " subshape: " << c.mSubShapeIDB); -#endif // JPH_ENABLE_DETERMINISM_LOG - - // Remove contacts with the same body that have conflicting normals - IgnoredContactList ignored_contacts(inAllocator); - ignored_contacts.reserve(contacts.size()); - RemoveConflictingContacts(contacts, ignored_contacts); - - // Convert contacts into constraints - ConstraintList constraints(inAllocator); - constraints.reserve(contacts.size() * 2); - DetermineConstraints(contacts, inDeltaTime, constraints); - -#ifdef JPH_DEBUG_RENDERER - bool draw_constraints = inDrawConstraints && iteration == 0; - if (draw_constraints) - { - for (const Constraint &c : constraints) - { - // Draw contact point - DebugRenderer::sInstance->DrawMarker(c.mContact->mPosition, Color::sYellow, 0.05f); - Vec3 dist_to_plane = -c.mPlane.GetConstant() * c.mPlane.GetNormal(); - - // Draw arrow towards surface that we're hitting - DebugRenderer::sInstance->DrawArrow(c.mContact->mPosition, c.mContact->mPosition - dist_to_plane, Color::sYellow, 0.05f); - - // Draw plane around the player position indicating the space that we can move - DebugRenderer::sInstance->DrawPlane(mPosition + dist_to_plane, c.mPlane.GetNormal(), Color::sCyan, 1.0f); - DebugRenderer::sInstance->DrawArrow(mPosition + dist_to_plane, mPosition + dist_to_plane + c.mContact->mSurfaceNormal, Color::sRed, 0.05f); - } - } -#endif // JPH_DEBUG_RENDERER - - // Solve the displacement using these constraints - Vec3 displacement; - float time_simulated; - SolveConstraints(inVelocity, inDeltaTime, time_remaining, constraints, ignored_contacts, time_simulated, displacement, inAllocator - #ifdef JPH_DEBUG_RENDERER - , draw_constraints - #endif // JPH_DEBUG_RENDERER - ); - - // Store the contacts now that the colliding ones have been marked - if (outActiveContacts != nullptr) - outActiveContacts->assign(contacts.begin(), contacts.end()); - - // Do a sweep to test if the path is really unobstructed - Contact cast_contact; - if (GetFirstContactForSweep(ioPosition, displacement, cast_contact, ignored_contacts, inBroadPhaseLayerFilter, inObjectLayerFilter, inBodyFilter, inShapeFilter)) - { - displacement *= cast_contact.mFraction; - time_simulated *= cast_contact.mFraction; - } - - // Update the position - ioPosition += displacement; - time_remaining -= time_simulated; - - // If the displacement during this iteration was too small we assume we cannot further progress this update - if (displacement.LengthSq() < 1.0e-8f) - break; - } -} - -Vec3 CharacterVirtual::CancelVelocityTowardsSteepSlopes(Vec3Arg inDesiredVelocity) const -{ - // If we're not pushing against a steep slope, return the desired velocity - // Note: This is important as WalkStairs overrides the ground state to OnGround when its first check fails but the second succeeds - if (mGroundState == CharacterVirtual::EGroundState::OnGround - || mGroundState == CharacterVirtual::EGroundState::InAir) - return inDesiredVelocity; - - Vec3 desired_velocity = inDesiredVelocity; - for (const Contact &c : mActiveContacts) - if (c.mHadCollision - && IsSlopeTooSteep(c.mSurfaceNormal)) - { - // Note that we use the contact normal to allow for better sliding as the surface normal may be in the opposite direction of movement. - Vec3 normal = c.mContactNormal; - - // Remove normal vertical component - normal -= normal.Dot(mUp) * mUp; - - // Cancel horizontal movement in opposite direction - float dot = normal.Dot(desired_velocity); - if (dot < 0.0f) - desired_velocity -= (dot * normal) / normal.LengthSq(); - } - return desired_velocity; -} - -void CharacterVirtual::Update(float inDeltaTime, Vec3Arg inGravity, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter, TempAllocator &inAllocator) -{ - // If there's no delta time, we don't need to do anything - if (inDeltaTime <= 0.0f) - return; - - // Remember delta time for checking if we're supported by the ground - mLastDeltaTime = inDeltaTime; - - // Slide the shape through the world - MoveShape(mPosition, mLinearVelocity, inDeltaTime, &mActiveContacts, inBroadPhaseLayerFilter, inObjectLayerFilter, inBodyFilter, inShapeFilter, inAllocator - #ifdef JPH_DEBUG_RENDERER - , sDrawConstraints - #endif // JPH_DEBUG_RENDERER - ); - - // Determine the object that we're standing on - UpdateSupportingContact(false, inAllocator); - - // If we're on the ground - if (!mGroundBodyID.IsInvalid() && mMass > 0.0f) - { - // Add the impulse to the ground due to gravity: P = F dt = M g dt - float normal_dot_gravity = mGroundNormal.Dot(inGravity); - if (normal_dot_gravity < 0.0f) - { - Vec3 world_impulse = -(mMass * normal_dot_gravity / inGravity.Length() * inDeltaTime) * inGravity; - mSystem->GetBodyInterface().AddImpulse(mGroundBodyID, world_impulse, mGroundPosition); - } - } -} - -void CharacterVirtual::RefreshContacts(const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter, TempAllocator &inAllocator) -{ - // Determine the contacts - TempContactList contacts(inAllocator); - contacts.reserve(mMaxNumHits); - GetContactsAtPosition(mPosition, mLinearVelocity.NormalizedOr(Vec3::sZero()), mShape, contacts, inBroadPhaseLayerFilter, inObjectLayerFilter, inBodyFilter, inShapeFilter); - - StoreActiveContacts(contacts, inAllocator); -} - -void CharacterVirtual::UpdateGroundVelocity() -{ - BodyLockRead lock(mSystem->GetBodyLockInterface(), mGroundBodyID); - if (lock.SucceededAndIsInBroadPhase()) - { - const Body &body = lock.GetBody(); - - // Get adjusted body velocity - Vec3 linear_velocity, angular_velocity; - GetAdjustedBodyVelocity(body, linear_velocity, angular_velocity); - - // Calculate the ground velocity - mGroundVelocity = CalculateCharacterGroundVelocity(body.GetCenterOfMassPosition(), linear_velocity, angular_velocity, mLastDeltaTime); - } -} - -void CharacterVirtual::MoveToContact(RVec3Arg inPosition, const Contact &inContact, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter, TempAllocator &inAllocator) -{ - // Set the new position - SetPosition(inPosition); - - // Determine the contacts - TempContactList contacts(inAllocator); - contacts.reserve(mMaxNumHits + 1); // +1 because we can add one extra below - GetContactsAtPosition(mPosition, mLinearVelocity.NormalizedOr(Vec3::sZero()), mShape, contacts, inBroadPhaseLayerFilter, inObjectLayerFilter, inBodyFilter, inShapeFilter); - - // Ensure that we mark inContact as colliding - bool found_contact = false; - for (Contact &c : contacts) - if (c.mBodyB == inContact.mBodyB - && c.mSubShapeIDB == inContact.mSubShapeIDB) - { - c.mHadCollision = true; - found_contact = true; - } - if (!found_contact) - { - contacts.push_back(inContact); - - Contact © = contacts.back(); - copy.mHadCollision = true; - } - - StoreActiveContacts(contacts, inAllocator); - JPH_ASSERT(mGroundState != EGroundState::InAir); -} - -bool CharacterVirtual::SetShape(const Shape *inShape, float inMaxPenetrationDepth, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter, TempAllocator &inAllocator) -{ - if (mShape == nullptr || mSystem == nullptr) - { - // It hasn't been initialized yet - mShape = inShape; - return true; - } - - if (inShape != mShape && inShape != nullptr) - { - if (inMaxPenetrationDepth < FLT_MAX) - { - // Check collision around the new shape - TempContactList contacts(inAllocator); - contacts.reserve(mMaxNumHits); - GetContactsAtPosition(mPosition, mLinearVelocity.NormalizedOr(Vec3::sZero()), inShape, contacts, inBroadPhaseLayerFilter, inObjectLayerFilter, inBodyFilter, inShapeFilter); - - // Test if this results in penetration, if so cancel the transition - for (const Contact &c : contacts) - if (c.mDistance < -inMaxPenetrationDepth) - return false; - - StoreActiveContacts(contacts, inAllocator); - } - - // Set new shape - mShape = inShape; - } - - return mShape == inShape; -} - -bool CharacterVirtual::CanWalkStairs(Vec3Arg inLinearVelocity) const -{ - // We can only walk stairs if we're supported - if (!IsSupported()) - return false; - - // Check if there's enough horizontal velocity to trigger a stair walk - Vec3 horizontal_velocity = inLinearVelocity - inLinearVelocity.Dot(mUp) * mUp; - if (horizontal_velocity.IsNearZero(1.0e-6f)) - return false; - - // Check contacts for steep slopes - for (const Contact &c : mActiveContacts) - if (c.mHadCollision - && c.mSurfaceNormal.Dot(horizontal_velocity - c.mLinearVelocity) < 0.0f // Pushing into the contact - && IsSlopeTooSteep(c.mSurfaceNormal)) // Slope too steep - return true; - - return false; -} - -bool CharacterVirtual::WalkStairs(float inDeltaTime, Vec3Arg inStepUp, Vec3Arg inStepForward, Vec3Arg inStepForwardTest, Vec3Arg inStepDownExtra, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter, TempAllocator &inAllocator) -{ - // Move up - Vec3 up = inStepUp; - Contact contact; - IgnoredContactList dummy_ignored_contacts(inAllocator); - if (GetFirstContactForSweep(mPosition, up, contact, dummy_ignored_contacts, inBroadPhaseLayerFilter, inObjectLayerFilter, inBodyFilter, inShapeFilter)) - { - if (contact.mFraction < 1.0e-6f) - return false; // No movement, cancel - - // Limit up movement to the first contact point - up *= contact.mFraction; - } - RVec3 up_position = mPosition + up; - -#ifdef JPH_DEBUG_RENDERER - // Draw sweep up - if (sDrawWalkStairs) - DebugRenderer::sInstance->DrawArrow(mPosition, up_position, Color::sWhite, 0.01f); -#endif // JPH_DEBUG_RENDERER - - // Collect normals of steep slopes that we would like to walk stairs on. - // We need to do this before calling MoveShape because it will update mActiveContacts. - Vec3 character_velocity = inStepForward / inDeltaTime; - Vec3 horizontal_velocity = character_velocity - character_velocity.Dot(mUp) * mUp; - std::vector> steep_slope_normals(inAllocator); - steep_slope_normals.reserve(mActiveContacts.size()); - for (const Contact &c : mActiveContacts) - if (c.mHadCollision - && c.mSurfaceNormal.Dot(horizontal_velocity - c.mLinearVelocity) < 0.0f // Pushing into the contact - && IsSlopeTooSteep(c.mSurfaceNormal)) // Slope too steep - steep_slope_normals.push_back(c.mSurfaceNormal); - if (steep_slope_normals.empty()) - return false; // No steep slopes, cancel - - // Horizontal movement - RVec3 new_position = up_position; - MoveShape(new_position, character_velocity, inDeltaTime, nullptr, inBroadPhaseLayerFilter, inObjectLayerFilter, inBodyFilter, inShapeFilter, inAllocator); - Vec3 horizontal_movement = Vec3(new_position - up_position); - float horizontal_movement_sq = horizontal_movement.LengthSq(); - if (horizontal_movement_sq < 1.0e-8f) - return false; // No movement, cancel - - // Check if we made any progress towards any of the steep slopes, if not we just slid along the slope - // so we need to cancel the stair walk or else we will move faster than we should as we've done - // normal movement first and then stair walk. - bool made_progress = false; - float max_dot = -0.05f * inStepForward.Length(); - for (const Vec3 &normal : steep_slope_normals) - if (normal.Dot(horizontal_movement) < max_dot) - { - // We moved more than 5% of the forward step against a steep slope, accept this as progress - made_progress = true; - break; - } - if (!made_progress) - return false; - -#ifdef JPH_DEBUG_RENDERER - // Draw horizontal sweep - if (sDrawWalkStairs) - DebugRenderer::sInstance->DrawArrow(up_position, new_position, Color::sWhite, 0.01f); -#endif // JPH_DEBUG_RENDERER - - // Move down towards the floor. - // Note that we travel the same amount down as we traveled up with the specified extra - Vec3 down = -up + inStepDownExtra; - if (!GetFirstContactForSweep(new_position, down, contact, dummy_ignored_contacts, inBroadPhaseLayerFilter, inObjectLayerFilter, inBodyFilter, inShapeFilter)) - return false; // No floor found, we're in mid air, cancel stair walk - -#ifdef JPH_DEBUG_RENDERER - // Draw sweep down - if (sDrawWalkStairs) - { - RVec3 debug_pos = new_position + contact.mFraction * down; - DebugRenderer::sInstance->DrawArrow(new_position, debug_pos, Color::sWhite, 0.01f); - DebugRenderer::sInstance->DrawArrow(contact.mPosition, contact.mPosition + contact.mSurfaceNormal, Color::sWhite, 0.01f); - mShape->Draw(DebugRenderer::sInstance, GetCenterOfMassTransform(debug_pos, mRotation, mShape), Vec3::sReplicate(1.0f), Color::sWhite, false, true); - } -#endif // JPH_DEBUG_RENDERER - - // Test for floor that will support the character - if (IsSlopeTooSteep(contact.mSurfaceNormal)) - { - // If no test position was provided, we cancel the stair walk - if (inStepForwardTest.IsNearZero()) - return false; - - // Delta time may be very small, so it may be that we hit the edge of a step and the normal is too horizontal. - // In order to judge if the floor is flat further along the sweep, we test again for a floor at inStepForwardTest - // and check if the normal is valid there. - RVec3 test_position = up_position; - MoveShape(test_position, inStepForwardTest / inDeltaTime, inDeltaTime, nullptr, inBroadPhaseLayerFilter, inObjectLayerFilter, inBodyFilter, inShapeFilter, inAllocator); - float test_horizontal_movement_sq = Vec3(test_position - up_position).LengthSq(); - if (test_horizontal_movement_sq <= horizontal_movement_sq + 1.0e-8f) - return false; // We didn't move any further than in the previous test - - #ifdef JPH_DEBUG_RENDERER - // Draw 2nd sweep horizontal - if (sDrawWalkStairs) - DebugRenderer::sInstance->DrawArrow(up_position, test_position, Color::sCyan, 0.01f); - #endif // JPH_DEBUG_RENDERER - - // Then sweep down - Contact test_contact; - if (!GetFirstContactForSweep(test_position, down, test_contact, dummy_ignored_contacts, inBroadPhaseLayerFilter, inObjectLayerFilter, inBodyFilter, inShapeFilter)) - return false; - - #ifdef JPH_DEBUG_RENDERER - // Draw 2nd sweep down - if (sDrawWalkStairs) - { - RVec3 debug_pos = test_position + test_contact.mFraction * down; - DebugRenderer::sInstance->DrawArrow(test_position, debug_pos, Color::sCyan, 0.01f); - DebugRenderer::sInstance->DrawArrow(test_contact.mPosition, test_contact.mPosition + test_contact.mSurfaceNormal, Color::sCyan, 0.01f); - mShape->Draw(DebugRenderer::sInstance, GetCenterOfMassTransform(debug_pos, mRotation, mShape), Vec3::sReplicate(1.0f), Color::sCyan, false, true); - } - #endif // JPH_DEBUG_RENDERER - - if (IsSlopeTooSteep(test_contact.mSurfaceNormal)) - return false; - } - - // Calculate new down position - down *= contact.mFraction; - new_position += down; - - // Move the character to the new location - MoveToContact(new_position, contact, inBroadPhaseLayerFilter, inObjectLayerFilter, inBodyFilter, inShapeFilter, inAllocator); - - // Override ground state to 'on ground', it is possible that the contact normal is too steep, but in this case the inStepForwardTest has found a contact normal that is not too steep - mGroundState = EGroundState::OnGround; - - return true; -} - -bool CharacterVirtual::StickToFloor(Vec3Arg inStepDown, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter, TempAllocator &inAllocator) -{ - // Try to find the floor - Contact contact; - IgnoredContactList dummy_ignored_contacts(inAllocator); - if (!GetFirstContactForSweep(mPosition, inStepDown, contact, dummy_ignored_contacts, inBroadPhaseLayerFilter, inObjectLayerFilter, inBodyFilter, inShapeFilter)) - return false; // If no floor found, don't update our position - - // Calculate new position - RVec3 new_position = mPosition + contact.mFraction * inStepDown; - -#ifdef JPH_DEBUG_RENDERER - // Draw sweep down - if (sDrawStickToFloor) - { - DebugRenderer::sInstance->DrawArrow(mPosition, new_position, Color::sOrange, 0.01f); - mShape->Draw(DebugRenderer::sInstance, GetCenterOfMassTransform(new_position, mRotation, mShape), Vec3::sReplicate(1.0f), Color::sOrange, false, true); - } -#endif // JPH_DEBUG_RENDERER - - // Move the character to the new location - MoveToContact(new_position, contact, inBroadPhaseLayerFilter, inObjectLayerFilter, inBodyFilter, inShapeFilter, inAllocator); - return true; -} - -void CharacterVirtual::ExtendedUpdate(float inDeltaTime, Vec3Arg inGravity, const ExtendedUpdateSettings &inSettings, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter, TempAllocator &inAllocator) -{ - // Update the velocity - Vec3 desired_velocity = mLinearVelocity; - mLinearVelocity = CancelVelocityTowardsSteepSlopes(desired_velocity); - - // Remember old position - RVec3 old_position = mPosition; - - // Track if on ground before the update - bool ground_to_air = IsSupported(); - - // Update the character position (instant, do not have to wait for physics update) - Update(inDeltaTime, inGravity, inBroadPhaseLayerFilter, inObjectLayerFilter, inBodyFilter, inShapeFilter, inAllocator); - - // ... and that we got into air after - if (IsSupported()) - ground_to_air = false; - - // If stick to floor enabled and we're going from supported to not supported - if (ground_to_air && !inSettings.mStickToFloorStepDown.IsNearZero()) - { - // If we're not moving up, stick to the floor - float velocity = Vec3(mPosition - old_position).Dot(mUp) / inDeltaTime; - if (velocity <= 1.0e-6f) - StickToFloor(inSettings.mStickToFloorStepDown, inBroadPhaseLayerFilter, inObjectLayerFilter, inBodyFilter, inShapeFilter, inAllocator); - } - - // If walk stairs enabled - if (!inSettings.mWalkStairsStepUp.IsNearZero()) - { - // Calculate how much we wanted to move horizontally - Vec3 desired_horizontal_step = desired_velocity * inDeltaTime; - desired_horizontal_step -= desired_horizontal_step.Dot(mUp) * mUp; - float desired_horizontal_step_len = desired_horizontal_step.Length(); - if (desired_horizontal_step_len > 0.0f) - { - // Calculate how much we moved horizontally - Vec3 achieved_horizontal_step = Vec3(mPosition - old_position); - achieved_horizontal_step -= achieved_horizontal_step.Dot(mUp) * mUp; - - // Only count movement in the direction of the desired movement - // (otherwise we find it ok if we're sliding downhill while we're trying to climb uphill) - Vec3 step_forward_normalized = desired_horizontal_step / desired_horizontal_step_len; - achieved_horizontal_step = max(0.0f, achieved_horizontal_step.Dot(step_forward_normalized)) * step_forward_normalized; - float achieved_horizontal_step_len = achieved_horizontal_step.Length(); - - // If we didn't move as far as we wanted and we're against a slope that's too steep - if (achieved_horizontal_step_len + 1.0e-4f < desired_horizontal_step_len - && CanWalkStairs(desired_velocity)) - { - // Calculate how much we should step forward - // Note that we clamp the step forward to a minimum distance. This is done because at very high frame rates the delta time - // may be very small, causing a very small step forward. If the step becomes small enough, we may not move far enough - // horizontally to actually end up at the top of the step. - Vec3 step_forward = step_forward_normalized * max(inSettings.mWalkStairsMinStepForward, desired_horizontal_step_len - achieved_horizontal_step_len); - - // Calculate how far to scan ahead for a floor. This is only used in case the floor normal at step_forward is too steep. - // In that case an additional check will be performed at this distance to check if that normal is not too steep. - // Start with the ground normal in the horizontal plane and normalizing it - Vec3 step_forward_test = -mGroundNormal; - step_forward_test -= step_forward_test.Dot(mUp) * mUp; - step_forward_test = step_forward_test.NormalizedOr(step_forward_normalized); - - // If this normalized vector and the character forward vector is bigger than a preset angle, we use the character forward vector instead of the ground normal - // to do our forward test - if (step_forward_test.Dot(step_forward_normalized) < inSettings.mWalkStairsCosAngleForwardContact) - step_forward_test = step_forward_normalized; - - // Calculate the correct magnitude for the test vector - step_forward_test *= inSettings.mWalkStairsStepForwardTest; - - WalkStairs(inDeltaTime, inSettings.mWalkStairsStepUp, step_forward, step_forward_test, inSettings.mWalkStairsStepDownExtra, inBroadPhaseLayerFilter, inObjectLayerFilter, inBodyFilter, inShapeFilter, inAllocator); - } - } - } -} - -void CharacterVirtual::Contact::SaveState(StateRecorder &inStream) const -{ - inStream.Write(mPosition); - inStream.Write(mLinearVelocity); - inStream.Write(mContactNormal); - inStream.Write(mSurfaceNormal); - inStream.Write(mDistance); - inStream.Write(mFraction); - inStream.Write(mBodyB); - inStream.Write(mSubShapeIDB); - inStream.Write(mMotionTypeB); - inStream.Write(mHadCollision); - inStream.Write(mWasDiscarded); - inStream.Write(mCanPushCharacter); - // Cannot store user data (may be a pointer) and material -} - -void CharacterVirtual::Contact::RestoreState(StateRecorder &inStream) -{ - inStream.Read(mPosition); - inStream.Read(mLinearVelocity); - inStream.Read(mContactNormal); - inStream.Read(mSurfaceNormal); - inStream.Read(mDistance); - inStream.Read(mFraction); - inStream.Read(mBodyB); - inStream.Read(mSubShapeIDB); - inStream.Read(mMotionTypeB); - inStream.Read(mHadCollision); - inStream.Read(mWasDiscarded); - inStream.Read(mCanPushCharacter); - mUserData = 0; // Cannot restore user data - mMaterial = PhysicsMaterial::sDefault; // Cannot restore material -} - -void CharacterVirtual::SaveState(StateRecorder &inStream) const -{ - CharacterBase::SaveState(inStream); - - inStream.Write(mPosition); - inStream.Write(mRotation); - inStream.Write(mLinearVelocity); - inStream.Write(mLastDeltaTime); - inStream.Write(mMaxHitsExceeded); - - // Store contacts that had collision, we're using it at the beginning of the step in CancelVelocityTowardsSteepSlopes - uint32 num_contacts = 0; - for (const Contact &c : mActiveContacts) - if (c.mHadCollision) - ++num_contacts; - inStream.Write(num_contacts); - for (const Contact &c : mActiveContacts) - if (c.mHadCollision) - c.SaveState(inStream); -} - -void CharacterVirtual::RestoreState(StateRecorder &inStream) -{ - CharacterBase::RestoreState(inStream); - - inStream.Read(mPosition); - inStream.Read(mRotation); - inStream.Read(mLinearVelocity); - inStream.Read(mLastDeltaTime); - inStream.Read(mMaxHitsExceeded); - - // When validating remove contacts that don't have collision since we didn't save them - if (inStream.IsValidating()) - for (int i = (int)mActiveContacts.size() - 1; i >= 0; --i) - if (!mActiveContacts[i].mHadCollision) - mActiveContacts.erase(mActiveContacts.begin() + i); - - uint32 num_contacts = (uint32)mActiveContacts.size(); - inStream.Read(num_contacts); - mActiveContacts.resize(num_contacts); - for (Contact &c : mActiveContacts) - c.RestoreState(inStream); -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Character/CharacterVirtual.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Character/CharacterVirtual.h deleted file mode 100644 index a0a126eed6d..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Character/CharacterVirtual.h +++ /dev/null @@ -1,502 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -class CharacterVirtual; - -/// Contains the configuration of a character -class JPH_EXPORT CharacterVirtualSettings : public CharacterBaseSettings -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Character mass (kg). Used to push down objects with gravity when the character is standing on top. - float mMass = 70.0f; - - /// Maximum force with which the character can push other bodies (N). - float mMaxStrength = 100.0f; - - /// An extra offset applied to the shape in local space. This allows applying an extra offset to the shape in local space. - Vec3 mShapeOffset = Vec3::sZero(); - - ///@name Movement settings - EBackFaceMode mBackFaceMode = EBackFaceMode::CollideWithBackFaces; ///< When colliding with back faces, the character will not be able to move through back facing triangles. Use this if you have triangles that need to collide on both sides. - float mPredictiveContactDistance = 0.1f; ///< How far to scan outside of the shape for predictive contacts. A value of 0 will most likely cause the character to get stuck as it cannot properly calculate a sliding direction anymore. A value that's too high will cause ghost collisions. - uint mMaxCollisionIterations = 5; ///< Max amount of collision loops - uint mMaxConstraintIterations = 15; ///< How often to try stepping in the constraint solving - float mMinTimeRemaining = 1.0e-4f; ///< Early out condition: If this much time is left to simulate we are done - float mCollisionTolerance = 1.0e-3f; ///< How far we're willing to penetrate geometry - float mCharacterPadding = 0.02f; ///< How far we try to stay away from the geometry, this ensures that the sweep will hit as little as possible lowering the collision cost and reducing the risk of getting stuck - uint mMaxNumHits = 256; ///< Max num hits to collect in order to avoid excess of contact points collection - float mHitReductionCosMaxAngle = 0.999f; ///< Cos(angle) where angle is the maximum angle between two hits contact normals that are allowed to be merged during hit reduction. Default is around 2.5 degrees. Set to -1 to turn off. - float mPenetrationRecoverySpeed = 1.0f; ///< This value governs how fast a penetration will be resolved, 0 = nothing is resolved, 1 = everything in one update -}; - -/// This class contains settings that allow you to override the behavior of a character's collision response -class CharacterContactSettings -{ -public: - bool mCanPushCharacter = true; ///< True when the object can push the virtual character - bool mCanReceiveImpulses = true; ///< True when the virtual character can apply impulses (push) the body -}; - -/// This class receives callbacks when a virtual character hits something. -class JPH_EXPORT CharacterContactListener -{ -public: - /// Destructor - virtual ~CharacterContactListener() = default; - - /// Callback to adjust the velocity of a body as seen by the character. Can be adjusted to e.g. implement a conveyor belt or an inertial dampener system of a sci-fi space ship. - /// Note that inBody2 is locked during the callback so you can read its properties freely. - virtual void OnAdjustBodyVelocity(const CharacterVirtual *inCharacter, const Body &inBody2, Vec3 &ioLinearVelocity, Vec3 &ioAngularVelocity) { /* Do nothing, the linear and angular velocity are already filled in */ } - - /// Checks if a character can collide with specified body. Return true if the contact is valid. - virtual bool OnContactValidate(const CharacterVirtual *inCharacter, const BodyID &inBodyID2, const SubShapeID &inSubShapeID2) { return true; } - - /// Called whenever the character collides with a body. - /// @param inCharacter Character that is being solved - /// @param inBodyID2 Body ID of body that is being hit - /// @param inSubShapeID2 Sub shape ID of shape that is being hit - /// @param inContactPosition World space contact position - /// @param inContactNormal World space contact normal - /// @param ioSettings Settings returned by the contact callback to indicate how the character should behave - virtual void OnContactAdded(const CharacterVirtual *inCharacter, const BodyID &inBodyID2, const SubShapeID &inSubShapeID2, RVec3Arg inContactPosition, Vec3Arg inContactNormal, CharacterContactSettings &ioSettings) { /* Default do nothing */ } - - /// Called whenever a contact is being used by the solver. Allows the listener to override the resulting character velocity (e.g. by preventing sliding along certain surfaces). - /// @param inCharacter Character that is being solved - /// @param inBodyID2 Body ID of body that is being hit - /// @param inSubShapeID2 Sub shape ID of shape that is being hit - /// @param inContactPosition World space contact position - /// @param inContactNormal World space contact normal - /// @param inContactVelocity World space velocity of contact point (e.g. for a moving platform) - /// @param inContactMaterial Material of contact point - /// @param inCharacterVelocity World space velocity of the character prior to hitting this contact - /// @param ioNewCharacterVelocity Contains the calculated world space velocity of the character after hitting this contact, this velocity slides along the surface of the contact. Can be modified by the listener to provide an alternative velocity. - virtual void OnContactSolve(const CharacterVirtual *inCharacter, const BodyID &inBodyID2, const SubShapeID &inSubShapeID2, RVec3Arg inContactPosition, Vec3Arg inContactNormal, Vec3Arg inContactVelocity, const PhysicsMaterial *inContactMaterial, Vec3Arg inCharacterVelocity, Vec3 &ioNewCharacterVelocity) { /* Default do nothing */ } -}; - -/// Runtime character object. -/// This object usually represents the player. Contrary to the Character class it doesn't use a rigid body but moves doing collision checks only (hence the name virtual). -/// The advantage of this is that you can determine when the character moves in the frame (usually this has to happen at a very particular point in the frame) -/// but the downside is that other objects don't see this virtual character. In order to make this work it is recommended to pair a CharacterVirtual with a Character that -/// moves along. This Character should be keyframed (or at least have no gravity) and move along with the CharacterVirtual so that other rigid bodies can collide with it. -class JPH_EXPORT CharacterVirtual : public CharacterBase -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Constructor - /// @param inSettings The settings for the character - /// @param inPosition Initial position for the character - /// @param inRotation Initial rotation for the character (usually only around the up-axis) - /// @param inUserData Application specific value - /// @param inSystem Physics system that this character will be added to later - CharacterVirtual(const CharacterVirtualSettings *inSettings, RVec3Arg inPosition, QuatArg inRotation, uint64 inUserData, PhysicsSystem *inSystem); - - /// Constructor without user data - CharacterVirtual(const CharacterVirtualSettings *inSettings, RVec3Arg inPosition, QuatArg inRotation, PhysicsSystem *inSystem) : CharacterVirtual(inSettings, inPosition, inRotation, 0, inSystem) { } - - /// Set the contact listener - void SetListener(CharacterContactListener *inListener) { mListener = inListener; } - - /// Get the current contact listener - CharacterContactListener * GetListener() const { return mListener; } - - /// Get the linear velocity of the character (m / s) - Vec3 GetLinearVelocity() const { return mLinearVelocity; } - - /// Set the linear velocity of the character (m / s) - void SetLinearVelocity(Vec3Arg inLinearVelocity) { mLinearVelocity = inLinearVelocity; } - - /// Get the position of the character - RVec3 GetPosition() const { return mPosition; } - - /// Set the position of the character - void SetPosition(RVec3Arg inPosition) { mPosition = inPosition; } - - /// Get the rotation of the character - Quat GetRotation() const { return mRotation; } - - /// Set the rotation of the character - void SetRotation(QuatArg inRotation) { mRotation = inRotation; } - - /// Calculate the world transform of the character - RMat44 GetWorldTransform() const { return RMat44::sRotationTranslation(mRotation, mPosition); } - - /// Calculates the transform for this character's center of mass - RMat44 GetCenterOfMassTransform() const { return GetCenterOfMassTransform(mPosition, mRotation, mShape); } - - /// Character mass (kg) - float GetMass() const { return mMass; } - void SetMass(float inMass) { mMass = inMass; } - - /// Maximum force with which the character can push other bodies (N) - float GetMaxStrength() const { return mMaxStrength; } - void SetMaxStrength(float inMaxStrength) { mMaxStrength = inMaxStrength; } - - /// This value governs how fast a penetration will be resolved, 0 = nothing is resolved, 1 = everything in one update - float GetPenetrationRecoverySpeed() const { return mPenetrationRecoverySpeed; } - void SetPenetrationRecoverySpeed(float inSpeed) { mPenetrationRecoverySpeed = inSpeed; } - - /// Character padding - float GetCharacterPadding() const { return mCharacterPadding; } - - /// Max num hits to collect in order to avoid excess of contact points collection - uint GetMaxNumHits() const { return mMaxNumHits; } - void SetMaxNumHits(uint inMaxHits) { mMaxNumHits = inMaxHits; } - - /// Cos(angle) where angle is the maximum angle between two hits contact normals that are allowed to be merged during hit reduction. Default is around 2.5 degrees. Set to -1 to turn off. - float GetHitReductionCosMaxAngle() const { return mHitReductionCosMaxAngle; } - void SetHitReductionCosMaxAngle(float inCosMaxAngle) { mHitReductionCosMaxAngle = inCosMaxAngle; } - - /// Returns if we exceeded the maximum number of hits during the last collision check and had to discard hits based on distance. - /// This can be used to find areas that have too complex geometry for the character to navigate properly. - /// To solve you can either increase the max number of hits or simplify the geometry. Note that the character simulation will - /// try to do its best to select the most relevant contacts to avoid the character from getting stuck. - bool GetMaxHitsExceeded() const { return mMaxHitsExceeded; } - - /// An extra offset applied to the shape in local space. This allows applying an extra offset to the shape in local space. Note that setting it on the fly can cause the shape to teleport into collision. - Vec3 GetShapeOffset() const { return mShapeOffset; } - void SetShapeOffset(Vec3Arg inShapeOffset) { mShapeOffset = inShapeOffset; } - - /// Access to the user data, can be used for anything by the application - uint64 GetUserData() const { return mUserData; } - void SetUserData(uint64 inUserData) { mUserData = inUserData; } - - /// This function can be called prior to calling Update() to convert a desired velocity into a velocity that won't make the character move further onto steep slopes. - /// This velocity can then be set on the character using SetLinearVelocity() - /// @param inDesiredVelocity Velocity to clamp against steep walls - /// @return A new velocity vector that won't make the character move up steep slopes - Vec3 CancelVelocityTowardsSteepSlopes(Vec3Arg inDesiredVelocity) const; - - /// This is the main update function. It moves the character according to its current velocity (the character is similar to a kinematic body in the sense - /// that you set the velocity and the character will follow unless collision is blocking the way). Note it's your own responsibility to apply gravity to the character velocity! - /// Different surface materials (like ice) can be emulated by getting the ground material and adjusting the velocity and/or the max slope angle accordingly every frame. - /// @param inDeltaTime Time step to simulate. - /// @param inGravity Gravity vector (m/s^2). This gravity vector is only used when the character is standing on top of another object to apply downward force. - /// @param inBroadPhaseLayerFilter Filter that is used to check if the character collides with something in the broadphase. - /// @param inObjectLayerFilter Filter that is used to check if a character collides with a layer. - /// @param inBodyFilter Filter that is used to check if a character collides with a body. - /// @param inShapeFilter Filter that is used to check if a character collides with a subshape. - /// @param inAllocator An allocator for temporary allocations. All memory will be freed by the time this function returns. - void Update(float inDeltaTime, Vec3Arg inGravity, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter, TempAllocator &inAllocator); - - /// This function will return true if the character has moved into a slope that is too steep (e.g. a vertical wall). - /// You would call WalkStairs to attempt to step up stairs. - /// @param inLinearVelocity The linear velocity that the player desired. This is used to determine if we're pushing into a step. - bool CanWalkStairs(Vec3Arg inLinearVelocity) const; - - /// When stair walking is needed, you can call the WalkStairs function to cast up, forward and down again to try to find a valid position - /// @param inDeltaTime Time step to simulate. - /// @param inStepUp The direction and distance to step up (this corresponds to the max step height) - /// @param inStepForward The direction and distance to step forward after the step up - /// @param inStepForwardTest When running at a high frequency, inStepForward can be very small and it's likely that you hit the side of the stairs on the way down. This could produce a normal that violates the max slope angle. If this happens, we test again using this distance from the up position to see if we find a valid slope. - /// @param inStepDownExtra An additional translation that is added when stepping down at the end. Allows you to step further down than up. Set to zero if you don't want this. Should be in the opposite direction of up. - /// @param inBroadPhaseLayerFilter Filter that is used to check if the character collides with something in the broadphase. - /// @param inObjectLayerFilter Filter that is used to check if a character collides with a layer. - /// @param inBodyFilter Filter that is used to check if a character collides with a body. - /// @param inShapeFilter Filter that is used to check if a character collides with a subshape. - /// @param inAllocator An allocator for temporary allocations. All memory will be freed by the time this function returns. - /// @return true if the stair walk was successful - bool WalkStairs(float inDeltaTime, Vec3Arg inStepUp, Vec3Arg inStepForward, Vec3Arg inStepForwardTest, Vec3Arg inStepDownExtra, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter, TempAllocator &inAllocator); - - /// This function can be used to artificially keep the character to the floor. Normally when a character is on a small step and starts moving horizontally, the character will - /// lose contact with the floor because the initial vertical velocity is zero while the horizontal velocity is quite high. To prevent the character from losing contact with the floor, - /// we do an additional collision check downwards and if we find the floor within a certain distance, we project the character onto the floor. - /// @param inStepDown Max amount to project the character downwards (if no floor is found within this distance, the function will return false) - /// @param inBroadPhaseLayerFilter Filter that is used to check if the character collides with something in the broadphase. - /// @param inObjectLayerFilter Filter that is used to check if a character collides with a layer. - /// @param inBodyFilter Filter that is used to check if a character collides with a body. - /// @param inShapeFilter Filter that is used to check if a character collides with a subshape. - /// @param inAllocator An allocator for temporary allocations. All memory will be freed by the time this function returns. - /// @return True if the character was successfully projected onto the floor. - bool StickToFloor(Vec3Arg inStepDown, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter, TempAllocator &inAllocator); - - /// Settings struct with settings for ExtendedUpdate - struct ExtendedUpdateSettings - { - Vec3 mStickToFloorStepDown { 0, -0.5f, 0 }; ///< See StickToFloor inStepDown parameter. Can be zero to turn off. - Vec3 mWalkStairsStepUp { 0, 0.4f, 0 }; ///< See WalkStairs inStepUp parameter. Can be zero to turn off. - float mWalkStairsMinStepForward { 0.02f }; ///< See WalkStairs inStepForward parameter. Note that the parameter only indicates a magnitude, direction is taken from current velocity. - float mWalkStairsStepForwardTest { 0.15f }; ///< See WalkStairs inStepForwardTest parameter. Note that the parameter only indicates a magnitude, direction is taken from current velocity. - float mWalkStairsCosAngleForwardContact { Cos(DegreesToRadians(75.0f)) }; ///< Cos(angle) where angle is the maximum angle between the ground normal in the horizontal plane and the character forward vector where we're willing to adjust the step forward test towards the contact normal. - Vec3 mWalkStairsStepDownExtra { Vec3::sZero() }; ///< See WalkStairs inStepDownExtra - }; - - /// This function combines Update, StickToFloor and WalkStairs. This function serves as an example of how these functions could be combined. - /// Before calling, call SetLinearVelocity to update the horizontal/vertical speed of the character, typically this is: - /// - When on OnGround and not moving away from ground: velocity = GetGroundVelocity() + horizontal speed as input by player + optional vertical jump velocity + delta time * gravity - /// - Else: velocity = current vertical velocity + horizontal speed as input by player + delta time * gravity - /// @param inDeltaTime Time step to simulate. - /// @param inGravity Gravity vector (m/s^2). This gravity vector is only used when the character is standing on top of another object to apply downward force. - /// @param inSettings A structure containing settings for the algorithm. - /// @param inBroadPhaseLayerFilter Filter that is used to check if the character collides with something in the broadphase. - /// @param inObjectLayerFilter Filter that is used to check if a character collides with a layer. - /// @param inBodyFilter Filter that is used to check if a character collides with a body. - /// @param inShapeFilter Filter that is used to check if a character collides with a subshape. - /// @param inAllocator An allocator for temporary allocations. All memory will be freed by the time this function returns. - void ExtendedUpdate(float inDeltaTime, Vec3Arg inGravity, const ExtendedUpdateSettings &inSettings, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter, TempAllocator &inAllocator); - - /// This function can be used after a character has teleported to determine the new contacts with the world. - void RefreshContacts(const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter, TempAllocator &inAllocator); - - /// Use the ground body ID to get an updated estimate of the ground velocity. This function can be used if the ground body has moved / changed velocity and you want a new estimate of the ground velocity. - /// It will not perform collision detection, so is less accurate than RefreshContacts but a lot faster. - void UpdateGroundVelocity(); - - /// Switch the shape of the character (e.g. for stance). - /// @param inShape The shape to switch to. - /// @param inMaxPenetrationDepth When inMaxPenetrationDepth is not FLT_MAX, it checks if the new shape collides before switching shape. This is the max penetration we're willing to accept after the switch. - /// @param inBroadPhaseLayerFilter Filter that is used to check if the character collides with something in the broadphase. - /// @param inObjectLayerFilter Filter that is used to check if a character collides with a layer. - /// @param inBodyFilter Filter that is used to check if a character collides with a body. - /// @param inShapeFilter Filter that is used to check if a character collides with a subshape. - /// @param inAllocator An allocator for temporary allocations. All memory will be freed by the time this function returns. - /// @return Returns true if the switch succeeded. - bool SetShape(const Shape *inShape, float inMaxPenetrationDepth, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter, TempAllocator &inAllocator); - - /// @brief Get all contacts for the character at a particular location - /// @param inPosition Position to test, note that this position will be corrected for the character padding. - /// @param inRotation Rotation at which to test the shape. - /// @param inMovementDirection A hint in which direction the character is moving, will be used to calculate a proper normal. - /// @param inMaxSeparationDistance How much distance around the character you want to report contacts in (can be 0 to match the character exactly). - /// @param inShape Shape to test collision with. - /// @param inBaseOffset All hit results will be returned relative to this offset, can be zero to get results in world position, but when you're testing far from the origin you get better precision by picking a position that's closer e.g. GetPosition() since floats are most accurate near the origin - /// @param ioCollector Collision collector that receives the collision results. - /// @param inBroadPhaseLayerFilter Filter that is used to check if the character collides with something in the broadphase. - /// @param inObjectLayerFilter Filter that is used to check if a character collides with a layer. - /// @param inBodyFilter Filter that is used to check if a character collides with a body. - /// @param inShapeFilter Filter that is used to check if a character collides with a subshape. - void CheckCollision(RVec3Arg inPosition, QuatArg inRotation, Vec3Arg inMovementDirection, float inMaxSeparationDistance, const Shape *inShape, RVec3Arg inBaseOffset, CollideShapeCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter) const; - - // Saving / restoring state for replay - virtual void SaveState(StateRecorder &inStream) const override; - virtual void RestoreState(StateRecorder &inStream) override; - -#ifdef JPH_DEBUG_RENDERER - static inline bool sDrawConstraints = false; ///< Draw the current state of the constraints for iteration 0 when creating them - static inline bool sDrawWalkStairs = false; ///< Draw the state of the walk stairs algorithm - static inline bool sDrawStickToFloor = false; ///< Draw the state of the stick to floor algorithm -#endif - - // Encapsulates a collision contact - struct Contact - { - // Saving / restoring state for replay - void SaveState(StateRecorder &inStream) const; - void RestoreState(StateRecorder &inStream); - - RVec3 mPosition; ///< Position where the character makes contact - Vec3 mLinearVelocity; ///< Velocity of the contact point - Vec3 mContactNormal; ///< Contact normal, pointing towards the character - Vec3 mSurfaceNormal; ///< Surface normal of the contact - float mDistance; ///< Distance to the contact <= 0 means that it is an actual contact, > 0 means predictive - float mFraction; ///< Fraction along the path where this contact takes place - BodyID mBodyB; ///< ID of body we're colliding with - SubShapeID mSubShapeIDB; ///< Sub shape ID of body we're colliding with - EMotionType mMotionTypeB; ///< Motion type of B, used to determine the priority of the contact - bool mIsSensorB; ///< If B is a sensor - uint64 mUserData; ///< User data of B - const PhysicsMaterial * mMaterial; ///< Material of B - bool mHadCollision = false; ///< If the character actually collided with the contact (can be false if a predictive contact never becomes a real one) - bool mWasDiscarded = false; ///< If the contact validate callback chose to discard this contact - bool mCanPushCharacter = true; ///< When true, the velocity of the contact point can push the character - }; - - using TempContactList = std::vector>; - using ContactList = Array; - - /// Access to the internal list of contacts that the character has found. - const ContactList & GetActiveContacts() const { return mActiveContacts; } - -private: - // Sorting predicate for making contact order deterministic - struct ContactOrderingPredicate - { - inline bool operator () (const Contact &inLHS, const Contact &inRHS) const - { - if (inLHS.mBodyB != inRHS.mBodyB) - return inLHS.mBodyB < inRHS.mBodyB; - - return inLHS.mSubShapeIDB.GetValue() < inRHS.mSubShapeIDB.GetValue(); - } - }; - - // A contact that needs to be ignored - struct IgnoredContact - { - IgnoredContact() = default; - IgnoredContact(const BodyID &inBodyID, const SubShapeID &inSubShapeID) : mBodyID(inBodyID), mSubShapeID(inSubShapeID) { } - - BodyID mBodyID; ///< ID of body we're colliding with - SubShapeID mSubShapeID; ///< Sub shape of body we're colliding with - }; - - using IgnoredContactList = std::vector>; - - // A constraint that limits the movement of the character - struct Constraint - { - Contact * mContact; ///< Contact that this constraint was generated from - float mTOI; ///< Calculated time of impact (can be negative if penetrating) - float mProjectedVelocity; ///< Velocity of the contact projected on the contact normal (negative if separating) - Vec3 mLinearVelocity; ///< Velocity of the contact (can contain a corrective velocity to resolve penetration) - Plane mPlane; ///< Plane around the origin that describes how far we can displace (from the origin) - bool mIsSteepSlope = false; ///< If this constraint belongs to a steep slope - }; - - using ConstraintList = std::vector>; - - // Collision collector that collects hits for CollideShape - class ContactCollector : public CollideShapeCollector - { - public: - ContactCollector(PhysicsSystem *inSystem, const CharacterVirtual *inCharacter, uint inMaxHits, float inHitReductionCosMaxAngle, Vec3Arg inUp, RVec3Arg inBaseOffset, TempContactList &outContacts) : mBaseOffset(inBaseOffset), mUp(inUp), mSystem(inSystem), mCharacter(inCharacter), mContacts(outContacts), mMaxHits(inMaxHits), mHitReductionCosMaxAngle(inHitReductionCosMaxAngle) { } - - virtual void AddHit(const CollideShapeResult &inResult) override; - - RVec3 mBaseOffset; - Vec3 mUp; - PhysicsSystem * mSystem; - const CharacterVirtual * mCharacter; - TempContactList & mContacts; - uint mMaxHits; - float mHitReductionCosMaxAngle; - bool mMaxHitsExceeded = false; - }; - - // A collision collector that collects hits for CastShape - class ContactCastCollector : public CastShapeCollector - { - public: - ContactCastCollector(PhysicsSystem *inSystem, const CharacterVirtual *inCharacter, Vec3Arg inDisplacement, Vec3Arg inUp, const IgnoredContactList &inIgnoredContacts, RVec3Arg inBaseOffset, Contact &outContact) : mBaseOffset(inBaseOffset), mDisplacement(inDisplacement), mUp(inUp), mSystem(inSystem), mCharacter(inCharacter), mIgnoredContacts(inIgnoredContacts), mContact(outContact) { } - - virtual void AddHit(const ShapeCastResult &inResult) override; - - RVec3 mBaseOffset; - Vec3 mDisplacement; - Vec3 mUp; - PhysicsSystem * mSystem; - const CharacterVirtual * mCharacter; - const IgnoredContactList & mIgnoredContacts; - Contact & mContact; - }; - - // Helper function to convert a Jolt collision result into a contact - template - inline static void sFillContactProperties(const CharacterVirtual *inCharacter, Contact &outContact, const Body &inBody, Vec3Arg inUp, RVec3Arg inBaseOffset, const taCollector &inCollector, const CollideShapeResult &inResult); - - // Move the shape from ioPosition and try to displace it by inVelocity * inDeltaTime, this will try to slide the shape along the world geometry - void MoveShape(RVec3 &ioPosition, Vec3Arg inVelocity, float inDeltaTime, ContactList *outActiveContacts, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter, TempAllocator &inAllocator - #ifdef JPH_DEBUG_RENDERER - , bool inDrawConstraints = false - #endif // JPH_DEBUG_RENDERER - ) const; - - // Ask the callback if inContact is a valid contact point - bool ValidateContact(const Contact &inContact) const; - - // Tests the shape for collision around inPosition - void GetContactsAtPosition(RVec3Arg inPosition, Vec3Arg inMovementDirection, const Shape *inShape, TempContactList &outContacts, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter) const; - - // Remove penetrating contacts with the same body that have conflicting normals, leaving these will make the character mover get stuck - void RemoveConflictingContacts(TempContactList &ioContacts, IgnoredContactList &outIgnoredContacts) const; - - // Convert contacts into constraints. The character is assumed to start at the origin and the constraints are planes around the origin that confine the movement of the character. - void DetermineConstraints(TempContactList &inContacts, float inDeltaTime, ConstraintList &outConstraints) const; - - // Use the constraints to solve the displacement of the character. This will slide the character on the planes around the origin for as far as possible. - void SolveConstraints(Vec3Arg inVelocity, float inDeltaTime, float inTimeRemaining, ConstraintList &ioConstraints, IgnoredContactList &ioIgnoredContacts, float &outTimeSimulated, Vec3 &outDisplacement, TempAllocator &inAllocator - #ifdef JPH_DEBUG_RENDERER - , bool inDrawConstraints = false - #endif // JPH_DEBUG_RENDERER - ) const; - - // Get the velocity of a body adjusted by the contact listener - void GetAdjustedBodyVelocity(const Body& inBody, Vec3 &outLinearVelocity, Vec3 &outAngularVelocity) const; - - // Calculate the ground velocity of the character assuming it's standing on an object with specified linear and angular velocity and with specified center of mass. - // Note that we don't just take the point velocity because a point on an object with angular velocity traces an arc, - // so if you just take point velocity * delta time you get an error that accumulates over time - Vec3 CalculateCharacterGroundVelocity(RVec3Arg inCenterOfMass, Vec3Arg inLinearVelocity, Vec3Arg inAngularVelocity, float inDeltaTime) const; - - // Handle contact with physics object that we're colliding against - bool HandleContact(Vec3Arg inVelocity, Constraint &ioConstraint, float inDeltaTime) const; - - // Does a swept test of the shape from inPosition with displacement inDisplacement, returns true if there was a collision - bool GetFirstContactForSweep(RVec3Arg inPosition, Vec3Arg inDisplacement, Contact &outContact, const IgnoredContactList &inIgnoredContacts, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter) const; - - // Store contacts so that we have proper ground information - void StoreActiveContacts(const TempContactList &inContacts, TempAllocator &inAllocator); - - // This function will determine which contacts are touching the character and will calculate the one that is supporting us - void UpdateSupportingContact(bool inSkipContactVelocityCheck, TempAllocator &inAllocator); - - /// This function can be called after moving the character to a new colliding position - void MoveToContact(RVec3Arg inPosition, const Contact &inContact, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter, TempAllocator &inAllocator); - - // This function returns the actual center of mass of the shape, not corrected for the character padding - inline RMat44 GetCenterOfMassTransform(RVec3Arg inPosition, QuatArg inRotation, const Shape *inShape) const - { - return RMat44::sRotationTranslation(inRotation, inPosition).PreTranslated(mShapeOffset + inShape->GetCenterOfMass()).PostTranslated(mCharacterPadding * mUp); - } - - // Our main listener for contacts - CharacterContactListener * mListener = nullptr; - - // Movement settings - EBackFaceMode mBackFaceMode; // When colliding with back faces, the character will not be able to move through back facing triangles. Use this if you have triangles that need to collide on both sides. - float mPredictiveContactDistance; // How far to scan outside of the shape for predictive contacts. A value of 0 will most likely cause the character to get stuck as it cannot properly calculate a sliding direction anymore. A value that's too high will cause ghost collisions. - uint mMaxCollisionIterations; // Max amount of collision loops - uint mMaxConstraintIterations; // How often to try stepping in the constraint solving - float mMinTimeRemaining; // Early out condition: If this much time is left to simulate we are done - float mCollisionTolerance; // How far we're willing to penetrate geometry - float mCharacterPadding; // How far we try to stay away from the geometry, this ensures that the sweep will hit as little as possible lowering the collision cost and reducing the risk of getting stuck - uint mMaxNumHits; // Max num hits to collect in order to avoid excess of contact points collection - float mHitReductionCosMaxAngle; // Cos(angle) where angle is the maximum angle between two hits contact normals that are allowed to be merged during hit reduction. Default is around 2.5 degrees. Set to -1 to turn off. - float mPenetrationRecoverySpeed; // This value governs how fast a penetration will be resolved, 0 = nothing is resolved, 1 = everything in one update - - // Character mass (kg) - float mMass; - - // Maximum force with which the character can push other bodies (N) - float mMaxStrength; - - // An extra offset applied to the shape in local space. This allows applying an extra offset to the shape in local space. - Vec3 mShapeOffset = Vec3::sZero(); - - // Current position (of the base, not the center of mass) - RVec3 mPosition = RVec3::sZero(); - - // Current rotation (of the base, not of the center of mass) - Quat mRotation = Quat::sIdentity(); - - // Current linear velocity - Vec3 mLinearVelocity = Vec3::sZero(); - - // List of contacts that were active in the last frame - ContactList mActiveContacts; - - // Remembers the delta time of the last update - float mLastDeltaTime = 1.0f / 60.0f; - - // Remember if we exceeded the maximum number of hits and had to remove similar contacts - mutable bool mMaxHitsExceeded = false; - - // User data, can be used for anything by the application - uint64 mUserData = 0; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/AABoxCast.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/AABoxCast.h deleted file mode 100644 index a1cedf1ad92..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/AABoxCast.h +++ /dev/null @@ -1,20 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -/// Structure that holds AABox moving linearly through 3d space -struct AABoxCast -{ - JPH_OVERRIDE_NEW_DELETE - - AABox mBox; ///< Axis aligned box at starting location - Vec3 mDirection; ///< Direction and length of the cast (anything beyond this length will not be reported as a hit) -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ActiveEdgeMode.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ActiveEdgeMode.h deleted file mode 100644 index 30f96aeb123..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ActiveEdgeMode.h +++ /dev/null @@ -1,17 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -JPH_NAMESPACE_BEGIN - -/// How to treat active/inactive edges. -/// An active edge is an edge that either has no neighbouring edge or if the angle between the two connecting faces is too large, see: ActiveEdges -enum class EActiveEdgeMode : uint8 -{ - CollideOnlyWithActive, ///< Do not collide with inactive edges. For physics simulation, this gives less ghost collisions. - CollideWithAll, ///< Collide with all edges. Use this when you're interested in all collisions. -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ActiveEdges.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ActiveEdges.h deleted file mode 100644 index 7e51d2a63b7..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ActiveEdges.h +++ /dev/null @@ -1,114 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -/// An active edge is an edge that either has no neighbouring edge or if the angle between the two connecting faces is too large. -namespace ActiveEdges -{ - /// Helper function to check if an edge is active or not - /// @param inNormal1 Triangle normal of triangle on the left side of the edge (when looking along the edge from the top) - /// @param inNormal2 Triangle normal of triangle on the right side of the edge - /// @param inEdgeDirection Vector that points along the edge - /// @param inCosThresholdAngle Cosine of the threshold angle (if the angle between the two triangles is bigger than this, the edge is active, note that a concave edge is always inactive) - inline static bool IsEdgeActive(Vec3Arg inNormal1, Vec3Arg inNormal2, Vec3Arg inEdgeDirection, float inCosThresholdAngle) - { - // If normals are opposite the edges are active (the triangles are back to back) - float cos_angle_normals = inNormal1.Dot(inNormal2); - if (cos_angle_normals < -0.999848f) // cos(179 degrees) - return true; - - // Check if concave edge, if so we are not active - if (inNormal1.Cross(inNormal2).Dot(inEdgeDirection) < 0.0f) - return false; - - // Convex edge, active when angle bigger than threshold - return cos_angle_normals < inCosThresholdAngle; - } - - /// Replace normal by triangle normal if a hit is hitting an inactive edge - /// @param inV0 , inV1 , inV2 form the triangle - /// @param inTriangleNormal is the normal of the provided triangle (does not need to be normalized) - /// @param inActiveEdges bit 0 = edge v0..v1 is active, bit 1 = edge v1..v2 is active, bit 2 = edge v2..v0 is active - /// @param inPoint Collision point on the triangle - /// @param inNormal Collision normal on the triangle (does not need to be normalized) - /// @param inMovementDirection Can be zero. This gives an indication of in which direction the motion is to determine if when we hit an inactive edge/triangle we should return the triangle normal. - /// @return Returns inNormal if an active edge was hit, otherwise returns inTriangleNormal - inline static Vec3 FixNormal(Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2, Vec3Arg inTriangleNormal, uint8 inActiveEdges, Vec3Arg inPoint, Vec3Arg inNormal, Vec3Arg inMovementDirection) - { - // Check: All of the edges are active, we have the correct normal already. No need to call this function! - JPH_ASSERT(inActiveEdges != 0b111); - - // If inNormal would affect movement less than inTriangleNormal use inNormal - // This is done since it is really hard to make a distinction between sliding over a horizontal triangulated grid and hitting an edge (in this case you want to use the triangle normal) - // and sliding over a triangulated grid and grazing a vertical triangle with an inactive edge (in this case using the triangle normal will cause the object to bounce back so we want to use the calculated normal). - // To solve this we take a movement hint to give an indication of what direction our object is moving. If the edge normal results in less motion difference than the triangle normal we use the edge normal. - float normal_length = inNormal.Length(); - float triangle_normal_length = inTriangleNormal.Length(); - if (inMovementDirection.Dot(inNormal) * triangle_normal_length < inMovementDirection.Dot(inTriangleNormal) * normal_length) - return inNormal; - - // Check: None of the edges are active, we need to use the triangle normal - if (inActiveEdges == 0) - return inTriangleNormal; - - // Some edges are active. - // If normal is parallel to the triangle normal we don't need to check the active edges. - if (inTriangleNormal.Dot(inNormal) > 0.999848f * normal_length * triangle_normal_length) // cos(1 degree) - return inNormal; - - const float cEpsilon = 1.0e-4f; - const float cOneMinusEpsilon = 1.0f - cEpsilon; - - uint colliding_edge; - - // Test where the contact point is in the triangle - float u, v, w; - ClosestPoint::GetBaryCentricCoordinates(inV0 - inPoint, inV1 - inPoint, inV2 - inPoint, u, v, w); - if (u > cOneMinusEpsilon) - { - // Colliding with v0, edge 0 or 2 needs to be active - colliding_edge = 0b101; - } - else if (v > cOneMinusEpsilon) - { - // Colliding with v1, edge 0 or 1 needs to be active - colliding_edge = 0b011; - } - else if (w > cOneMinusEpsilon) - { - // Colliding with v2, edge 1 or 2 needs to be active - colliding_edge = 0b110; - } - else if (u < cEpsilon) - { - // Colliding with edge v1, v2, edge 1 needs to be active - colliding_edge = 0b010; - } - else if (v < cEpsilon) - { - // Colliding with edge v0, v2, edge 2 needs to be active - colliding_edge = 0b100; - } - else if (w < cEpsilon) - { - // Colliding with edge v0, v1, edge 0 needs to be active - colliding_edge = 0b001; - } - else - { - // Interior hit - return inTriangleNormal; - } - - // If this edge is active, use the provided normal instead of the triangle normal - return (inActiveEdges & colliding_edge) != 0? inNormal : inTriangleNormal; - } -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BackFaceMode.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BackFaceMode.h deleted file mode 100644 index 441dcd89a5f..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BackFaceMode.h +++ /dev/null @@ -1,16 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -JPH_NAMESPACE_BEGIN - -/// How collision detection functions will treat back facing triangles -enum class EBackFaceMode : uint8 -{ - IgnoreBackFaces, ///< Ignore collision with back facing surfaces/triangles - CollideWithBackFaces, ///< Collide with back facing surfaces/triangles -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhase.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhase.cpp deleted file mode 100644 index 1317d13393c..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhase.cpp +++ /dev/null @@ -1,16 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include - -JPH_NAMESPACE_BEGIN - -void BroadPhase::Init(BodyManager *inBodyManager, const BroadPhaseLayerInterface &inLayerInterface) -{ - mBodyManager = inBodyManager; -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhase.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhase.h deleted file mode 100644 index 8b6506e901d..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhase.h +++ /dev/null @@ -1,112 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include - -JPH_NAMESPACE_BEGIN - -// Shorthand function to ifdef out code if broadphase stats tracking is off -#ifdef JPH_TRACK_BROADPHASE_STATS - #define JPH_IF_TRACK_BROADPHASE_STATS(...) __VA_ARGS__ -#else - #define JPH_IF_TRACK_BROADPHASE_STATS(...) -#endif // JPH_TRACK_BROADPHASE_STATS - -class BodyManager; -struct BodyPair; - -using BodyPairCollector = CollisionCollector; - -/// Used to do coarse collision detection operations to quickly prune out bodies that will not collide. -class JPH_EXPORT BroadPhase : public BroadPhaseQuery -{ -public: - /// Initialize the broadphase. - /// @param inBodyManager The body manager singleton - /// @param inLayerInterface Interface that maps object layers to broadphase layers. - /// Note that the broadphase takes a pointer to the data inside inObjectToBroadPhaseLayer so this object should remain static. - virtual void Init(BodyManager *inBodyManager, const BroadPhaseLayerInterface &inLayerInterface); - - /// Should be called after many objects have been inserted to make the broadphase more efficient, usually done on startup only - virtual void Optimize() { /* Optionally overridden by implementation */ } - - /// Must be called just before updating the broadphase when none of the body mutexes are locked - virtual void FrameSync() { /* Optionally overridden by implementation */ } - - /// Must be called before UpdatePrepare to prevent modifications from being made to the tree - virtual void LockModifications() { /* Optionally overridden by implementation */ } - - /// Context used during broadphase update - struct UpdateState { void *mData[4]; }; - - /// Update the broadphase, needs to be called frequently to update the internal state when bodies have been modified. - /// The UpdatePrepare() function can run in a background thread without influencing the broadphase - virtual UpdateState UpdatePrepare() { return UpdateState(); } - - /// Finalizing the update will quickly apply the changes - virtual void UpdateFinalize([[maybe_unused]] const UpdateState &inUpdateState) { /* Optionally overridden by implementation */ } - - /// Must be called after UpdateFinalize to allow modifications to the broadphase - virtual void UnlockModifications() { /* Optionally overridden by implementation */ } - - /// Handle used during adding bodies to the broadphase - using AddState = void *; - - /// Prepare adding inNumber bodies at ioBodies to the broadphase, returns a handle that should be used in AddBodiesFinalize/Abort. - /// This can be done on a background thread without influencing the broadphase. - /// ioBodies may be shuffled around by this function and should be kept that way until AddBodiesFinalize/Abort is called. - virtual AddState AddBodiesPrepare([[maybe_unused]] BodyID *ioBodies, [[maybe_unused]] int inNumber) { return nullptr; } // By default the broadphase doesn't support this - - /// Finalize adding bodies to the broadphase, supply the return value of AddBodiesPrepare in inAddState. - /// Please ensure that the ioBodies array passed to AddBodiesPrepare is unmodified and passed again to this function. - virtual void AddBodiesFinalize(BodyID *ioBodies, int inNumber, AddState inAddState) = 0; - - /// Abort adding bodies to the broadphase, supply the return value of AddBodiesPrepare in inAddState. - /// This can be done on a background thread without influencing the broadphase. - /// Please ensure that the ioBodies array passed to AddBodiesPrepare is unmodified and passed again to this function. - virtual void AddBodiesAbort([[maybe_unused]] BodyID *ioBodies, [[maybe_unused]] int inNumber, [[maybe_unused]] AddState inAddState) { /* By default nothing needs to be done */ } - - /// Remove inNumber bodies in ioBodies from the broadphase. - /// ioBodies may be shuffled around by this function. - virtual void RemoveBodies(BodyID *ioBodies, int inNumber) = 0; - - /// Call whenever the aabb of a body changes (can change order of ioBodies array) - /// inTakeLock should be false if we're between LockModifications/UnlockModificiations in which case care needs to be taken to not call this between UpdatePrepare/UpdateFinalize - virtual void NotifyBodiesAABBChanged(BodyID *ioBodies, int inNumber, bool inTakeLock = true) = 0; - - /// Call whenever the layer (and optionally the aabb as well) of a body changes (can change order of ioBodies array) - virtual void NotifyBodiesLayerChanged(BodyID *ioBodies, int inNumber) = 0; - - /// Find all colliding pairs between dynamic bodies - /// Note that this function is very specifically tailored for the PhysicsSystem::Update function, hence it is not part of the BroadPhaseQuery interface. - /// One of the assumptions it can make is that no locking is needed during the query as it will only be called during a very particular part of the update. - /// @param ioActiveBodies is a list of bodies for which we need to find colliding pairs (this function can change the order of the ioActiveBodies array). This can be a subset of the set of active bodies in the system. - /// @param inNumActiveBodies is the size of the ioActiveBodies array. - /// @param inSpeculativeContactDistance Distance at which speculative contact points will be created. - /// @param inObjectVsBroadPhaseLayerFilter is the filter that determines if an object can collide with a broadphase layer. - /// @param inObjectLayerPairFilter is the filter that determines if two objects can collide. - /// @param ioPairCollector receives callbacks for every body pair found. - virtual void FindCollidingPairs(BodyID *ioActiveBodies, int inNumActiveBodies, float inSpeculativeContactDistance, const ObjectVsBroadPhaseLayerFilter &inObjectVsBroadPhaseLayerFilter, const ObjectLayerPairFilter &inObjectLayerPairFilter, BodyPairCollector &ioPairCollector) const = 0; - - /// Same as BroadPhaseQuery::CastAABox but can be implemented in a way to take no broad phase locks. - virtual void CastAABoxNoLock(const AABoxCast &inBox, CastShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const = 0; - - /// Get the bounding box of all objects in the broadphase - virtual AABox GetBounds() const = 0; - -#ifdef JPH_TRACK_BROADPHASE_STATS - /// Trace the collected broadphase stats in CSV form. - /// This report can be used to judge and tweak the efficiency of the broadphase. - virtual void ReportStats() { /* Can be implemented by derived classes */ } -#endif // JPH_TRACK_BROADPHASE_STATS - -protected: - /// Link to the body manager that manages the bodies in this broadphase - BodyManager * mBodyManager = nullptr; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhaseBruteForce.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhaseBruteForce.cpp deleted file mode 100644 index 10cf30a20c8..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhaseBruteForce.cpp +++ /dev/null @@ -1,313 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -void BroadPhaseBruteForce::AddBodiesFinalize(BodyID *ioBodies, int inNumber, AddState inAddState) -{ - lock_guard lock(mMutex); - - BodyVector &bodies = mBodyManager->GetBodies(); - - // Allocate space - uint32 idx = (uint32)mBodyIDs.size(); - mBodyIDs.resize(idx + inNumber); - - // Add bodies - for (const BodyID *b = ioBodies, *b_end = ioBodies + inNumber; b < b_end; ++b) - { - Body &body = *bodies[b->GetIndex()]; - - // Validate that body ID is consistent with array index - JPH_ASSERT(body.GetID() == *b); - JPH_ASSERT(!body.IsInBroadPhase()); - - // Add it to the list - mBodyIDs[idx] = body.GetID(); - ++idx; - - // Indicate body is in the broadphase - body.SetInBroadPhaseInternal(true); - } - - // Resort - QuickSort(mBodyIDs.begin(), mBodyIDs.end()); -} - -void BroadPhaseBruteForce::RemoveBodies(BodyID *ioBodies, int inNumber) -{ - lock_guard lock(mMutex); - - BodyVector &bodies = mBodyManager->GetBodies(); - - JPH_ASSERT((int)mBodyIDs.size() >= inNumber); - - // Remove bodies - for (const BodyID *b = ioBodies, *b_end = ioBodies + inNumber; b < b_end; ++b) - { - Body &body = *bodies[b->GetIndex()]; - - // Validate that body ID is consistent with array index - JPH_ASSERT(body.GetID() == *b); - JPH_ASSERT(body.IsInBroadPhase()); - - // Find body id - Array::iterator it = lower_bound(mBodyIDs.begin(), mBodyIDs.end(), body.GetID()); - JPH_ASSERT(it != mBodyIDs.end()); - - // Remove element - mBodyIDs.erase(it); - - // Indicate body is no longer in the broadphase - body.SetInBroadPhaseInternal(false); - } -} - -void BroadPhaseBruteForce::NotifyBodiesAABBChanged(BodyID *ioBodies, int inNumber, bool inTakeLock) -{ - // Do nothing, we directly reference the body -} - -void BroadPhaseBruteForce::NotifyBodiesLayerChanged(BodyID * ioBodies, int inNumber) -{ - // Do nothing, we directly reference the body -} - -void BroadPhaseBruteForce::CastRay(const RayCast &inRay, RayCastBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const -{ - shared_lock lock(mMutex); - - // Load ray - Vec3 origin(inRay.mOrigin); - RayInvDirection inv_direction(inRay.mDirection); - - // For all bodies - float early_out_fraction = ioCollector.GetEarlyOutFraction(); - for (BodyID b : mBodyIDs) - { - const Body &body = mBodyManager->GetBody(b); - - // Test layer - if (inObjectLayerFilter.ShouldCollide(body.GetObjectLayer())) - { - // Test intersection with ray - const AABox &bounds = body.GetWorldSpaceBounds(); - float fraction = RayAABox(origin, inv_direction, bounds.mMin, bounds.mMax); - if (fraction < early_out_fraction) - { - // Store hit - BroadPhaseCastResult result { b, fraction }; - ioCollector.AddHit(result); - if (ioCollector.ShouldEarlyOut()) - break; - early_out_fraction = ioCollector.GetEarlyOutFraction(); - } - } - } -} - -void BroadPhaseBruteForce::CollideAABox(const AABox &inBox, CollideShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const -{ - shared_lock lock(mMutex); - - // For all bodies - for (BodyID b : mBodyIDs) - { - const Body &body = mBodyManager->GetBody(b); - - // Test layer - if (inObjectLayerFilter.ShouldCollide(body.GetObjectLayer())) - { - // Test intersection with box - const AABox &bounds = body.GetWorldSpaceBounds(); - if (bounds.Overlaps(inBox)) - { - // Store hit - ioCollector.AddHit(b); - if (ioCollector.ShouldEarlyOut()) - break; - } - } - } -} - -void BroadPhaseBruteForce::CollideSphere(Vec3Arg inCenter, float inRadius, CollideShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const -{ - shared_lock lock(mMutex); - - float radius_sq = Square(inRadius); - - // For all bodies - for (BodyID b : mBodyIDs) - { - const Body &body = mBodyManager->GetBody(b); - - // Test layer - if (inObjectLayerFilter.ShouldCollide(body.GetObjectLayer())) - { - // Test intersection with box - const AABox &bounds = body.GetWorldSpaceBounds(); - if (bounds.GetSqDistanceTo(inCenter) <= radius_sq) - { - // Store hit - ioCollector.AddHit(b); - if (ioCollector.ShouldEarlyOut()) - break; - } - } - } -} - -void BroadPhaseBruteForce::CollidePoint(Vec3Arg inPoint, CollideShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const -{ - shared_lock lock(mMutex); - - // For all bodies - for (BodyID b : mBodyIDs) - { - const Body &body = mBodyManager->GetBody(b); - - // Test layer - if (inObjectLayerFilter.ShouldCollide(body.GetObjectLayer())) - { - // Test intersection with box - const AABox &bounds = body.GetWorldSpaceBounds(); - if (bounds.Contains(inPoint)) - { - // Store hit - ioCollector.AddHit(b); - if (ioCollector.ShouldEarlyOut()) - break; - } - } - } -} - -void BroadPhaseBruteForce::CollideOrientedBox(const OrientedBox &inBox, CollideShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const -{ - shared_lock lock(mMutex); - - // For all bodies - for (BodyID b : mBodyIDs) - { - const Body &body = mBodyManager->GetBody(b); - - // Test layer - if (inObjectLayerFilter.ShouldCollide(body.GetObjectLayer())) - { - // Test intersection with box - const AABox &bounds = body.GetWorldSpaceBounds(); - if (inBox.Overlaps(bounds)) - { - // Store hit - ioCollector.AddHit(b); - if (ioCollector.ShouldEarlyOut()) - break; - } - } - } -} - -void BroadPhaseBruteForce::CastAABoxNoLock(const AABoxCast &inBox, CastShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const -{ - shared_lock lock(mMutex); - - // Load box - Vec3 origin(inBox.mBox.GetCenter()); - Vec3 extent(inBox.mBox.GetExtent()); - RayInvDirection inv_direction(inBox.mDirection); - - // For all bodies - float early_out_fraction = ioCollector.GetPositiveEarlyOutFraction(); - for (BodyID b : mBodyIDs) - { - const Body &body = mBodyManager->GetBody(b); - - // Test layer - if (inObjectLayerFilter.ShouldCollide(body.GetObjectLayer())) - { - // Test intersection with ray - const AABox &bounds = body.GetWorldSpaceBounds(); - float fraction = RayAABox(origin, inv_direction, bounds.mMin - extent, bounds.mMax + extent); - if (fraction < early_out_fraction) - { - // Store hit - BroadPhaseCastResult result { b, fraction }; - ioCollector.AddHit(result); - if (ioCollector.ShouldEarlyOut()) - break; - early_out_fraction = ioCollector.GetPositiveEarlyOutFraction(); - } - } - } -} - -void BroadPhaseBruteForce::CastAABox(const AABoxCast &inBox, CastShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const -{ - CastAABoxNoLock(inBox, ioCollector, inBroadPhaseLayerFilter, inObjectLayerFilter); -} - -void BroadPhaseBruteForce::FindCollidingPairs(BodyID *ioActiveBodies, int inNumActiveBodies, float inSpeculativeContactDistance, const ObjectVsBroadPhaseLayerFilter &inObjectVsBroadPhaseLayerFilter, const ObjectLayerPairFilter &inObjectLayerPairFilter, BodyPairCollector &ioPairCollector) const -{ - shared_lock lock(mMutex); - - // Loop through all active bodies - size_t num_bodies = mBodyIDs.size(); - for (int b1 = 0; b1 < inNumActiveBodies; ++b1) - { - BodyID b1_id = ioActiveBodies[b1]; - const Body &body1 = mBodyManager->GetBody(b1_id); - const ObjectLayer layer1 = body1.GetObjectLayer(); - - // Expand the bounding box by the speculative contact distance - AABox bounds1 = body1.GetWorldSpaceBounds(); - bounds1.ExpandBy(Vec3::sReplicate(inSpeculativeContactDistance)); - - // For all other bodies - for (size_t b2 = 0; b2 < num_bodies; ++b2) - { - // Check if bodies can collide - BodyID b2_id = mBodyIDs[b2]; - const Body &body2 = mBodyManager->GetBody(b2_id); - if (!Body::sFindCollidingPairsCanCollide(body1, body2)) - continue; - - // Check if layers can collide - const ObjectLayer layer2 = body2.GetObjectLayer(); - if (!inObjectLayerPairFilter.ShouldCollide(layer1, layer2)) - continue; - - // Check if bounds overlap - const AABox &bounds2 = body2.GetWorldSpaceBounds(); - if (!bounds1.Overlaps(bounds2)) - continue; - - // Store overlapping pair - ioPairCollector.AddHit({ b1_id, b2_id }); - } - } -} - -AABox BroadPhaseBruteForce::GetBounds() const -{ - shared_lock lock(mMutex); - - AABox bounds; - for (BodyID b : mBodyIDs) - bounds.Encapsulate(mBodyManager->GetBody(b).GetWorldSpaceBounds()); - return bounds; -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhaseBruteForce.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhaseBruteForce.h deleted file mode 100644 index c3e20f5c8b8..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhaseBruteForce.h +++ /dev/null @@ -1,38 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include - -JPH_NAMESPACE_BEGIN - -/// Test BroadPhase implementation that does not do anything to speed up the operations. Can be used as a reference implementation. -class JPH_EXPORT BroadPhaseBruteForce final : public BroadPhase -{ -public: - JPH_OVERRIDE_NEW_DELETE - - // Implementing interface of BroadPhase (see BroadPhase for documentation) - virtual void AddBodiesFinalize(BodyID *ioBodies, int inNumber, AddState inAddState) override; - virtual void RemoveBodies(BodyID *ioBodies, int inNumber) override; - virtual void NotifyBodiesAABBChanged(BodyID *ioBodies, int inNumber, bool inTakeLock) override; - virtual void NotifyBodiesLayerChanged(BodyID *ioBodies, int inNumber) override; - virtual void CastRay(const RayCast &inRay, RayCastBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const override; - virtual void CollideAABox(const AABox &inBox, CollideShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const override; - virtual void CollideSphere(Vec3Arg inCenter, float inRadius, CollideShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const override; - virtual void CollidePoint(Vec3Arg inPoint, CollideShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const override; - virtual void CollideOrientedBox(const OrientedBox &inBox, CollideShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const override; - virtual void CastAABoxNoLock(const AABoxCast &inBox, CastShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const override; - virtual void CastAABox(const AABoxCast &inBox, CastShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const override; - virtual void FindCollidingPairs(BodyID *ioActiveBodies, int inNumActiveBodies, float inSpeculativeContactDistance, const ObjectVsBroadPhaseLayerFilter &inObjectVsBroadPhaseLayerFilter, const ObjectLayerPairFilter &inObjectLayerPairFilter, BodyPairCollector &ioPairCollector) const override; - virtual AABox GetBounds() const override; - -private: - Array mBodyIDs; - mutable SharedMutex mMutex; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhaseLayer.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhaseLayer.h deleted file mode 100644 index bd22cc3a5a6..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhaseLayer.h +++ /dev/null @@ -1,148 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include - -JPH_NAMESPACE_BEGIN - -/// An object layer can be mapped to a broadphase layer. Objects with the same broadphase layer will end up in the same sub structure (usually a tree) of the broadphase. -/// When there are many layers, this reduces the total amount of sub structures the broad phase needs to manage. Usually you want objects that don't collide with each other -/// in different broad phase layers, but there could be exceptions if objects layers only contain a minor amount of objects so it is not beneficial to give each layer its -/// own sub structure in the broadphase. -/// Note: This class requires explicit casting from and to Type to avoid confusion with ObjectLayer -class BroadPhaseLayer -{ -public: - using Type = uint8; - - JPH_INLINE BroadPhaseLayer() = default; - JPH_INLINE explicit constexpr BroadPhaseLayer(Type inValue) : mValue(inValue) { } - JPH_INLINE constexpr BroadPhaseLayer(const BroadPhaseLayer &) = default; - JPH_INLINE BroadPhaseLayer & operator = (const BroadPhaseLayer &) = default; - - JPH_INLINE constexpr bool operator == (const BroadPhaseLayer &inRHS) const - { - return mValue == inRHS.mValue; - } - - JPH_INLINE constexpr bool operator != (const BroadPhaseLayer &inRHS) const - { - return mValue != inRHS.mValue; - } - - JPH_INLINE constexpr bool operator < (const BroadPhaseLayer &inRHS) const - { - return mValue < inRHS.mValue; - } - - JPH_INLINE explicit constexpr operator Type() const - { - return mValue; - } - - JPH_INLINE Type GetValue() const - { - return mValue; - } - -private: - Type mValue; -}; - -/// Constant value used to indicate an invalid broad phase layer -static constexpr BroadPhaseLayer cBroadPhaseLayerInvalid(0xff); - -/// Interface that the application should implement to allow mapping object layers to broadphase layers -class BroadPhaseLayerInterface : public NonCopyable -{ -public: - /// Destructor - virtual ~BroadPhaseLayerInterface() = default; - - /// Return the number of broadphase layers there are - virtual uint GetNumBroadPhaseLayers() const = 0; - - /// Convert an object layer to the corresponding broadphase layer - virtual BroadPhaseLayer GetBroadPhaseLayer(ObjectLayer inLayer) const = 0; - -#if defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED) - /// Get the user readable name of a broadphase layer (debugging purposes) - virtual const char * GetBroadPhaseLayerName(BroadPhaseLayer inLayer) const = 0; -#endif // JPH_EXTERNAL_PROFILE || JPH_PROFILE_ENABLED -}; - -/// Class to test if an object can collide with a broadphase layer. Used while finding collision pairs. -class ObjectVsBroadPhaseLayerFilter : public NonCopyable -{ -public: - /// Destructor - virtual ~ObjectVsBroadPhaseLayerFilter() = default; - - /// Returns true if an object layer should collide with a broadphase layer - virtual bool ShouldCollide([[maybe_unused]] ObjectLayer inLayer1, [[maybe_unused]] BroadPhaseLayer inLayer2) const - { - return true; - } -}; - -/// Filter class for broadphase layers -class BroadPhaseLayerFilter : public NonCopyable -{ -public: - /// Destructor - virtual ~BroadPhaseLayerFilter() = default; - - /// Function to filter out broadphase layers when doing collision query test (return true to allow testing against objects with this layer) - virtual bool ShouldCollide([[maybe_unused]] BroadPhaseLayer inLayer) const - { - return true; - } -}; - -/// Default filter class that uses the pair filter in combination with a specified layer to filter layers -class DefaultBroadPhaseLayerFilter : public BroadPhaseLayerFilter -{ -public: - /// Constructor - DefaultBroadPhaseLayerFilter(const ObjectVsBroadPhaseLayerFilter &inObjectVsBroadPhaseLayerFilter, ObjectLayer inLayer) : - mObjectVsBroadPhaseLayerFilter(inObjectVsBroadPhaseLayerFilter), - mLayer(inLayer) - { - } - - // See BroadPhaseLayerFilter::ShouldCollide - virtual bool ShouldCollide(BroadPhaseLayer inLayer) const override - { - return mObjectVsBroadPhaseLayerFilter.ShouldCollide(mLayer, inLayer); - } - -private: - const ObjectVsBroadPhaseLayerFilter &mObjectVsBroadPhaseLayerFilter; - ObjectLayer mLayer; -}; - -/// Allows objects from a specific broad phase layer only -class SpecifiedBroadPhaseLayerFilter : public BroadPhaseLayerFilter -{ -public: - /// Constructor - explicit SpecifiedBroadPhaseLayerFilter(BroadPhaseLayer inLayer) : - mLayer(inLayer) - { - } - - // See BroadPhaseLayerFilter::ShouldCollide - virtual bool ShouldCollide(BroadPhaseLayer inLayer) const override - { - return mLayer == inLayer; - } - -private: - BroadPhaseLayer mLayer; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhaseLayerInterfaceMask.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhaseLayerInterfaceMask.h deleted file mode 100644 index 15a894bb12c..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhaseLayerInterfaceMask.h +++ /dev/null @@ -1,92 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2023 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include - -JPH_NAMESPACE_BEGIN - -/// BroadPhaseLayerInterface implementation. -/// This defines a mapping between object and broadphase layers. -/// This implementation works together with ObjectLayerPairFilterMask and ObjectVsBroadPhaseLayerFilterMask. -/// A broadphase layer is suitable for an object if its group & inGroupsToInclude is not zero and its group & inGroupsToExclude is zero. -/// The broadphase layers are iterated from lowest to highest value and the first one that matches is taken. If none match then it takes the last layer. -class BroadPhaseLayerInterfaceMask : public BroadPhaseLayerInterface -{ -public: - JPH_OVERRIDE_NEW_DELETE - - explicit BroadPhaseLayerInterfaceMask(uint inNumBroadPhaseLayers) - { - JPH_ASSERT(inNumBroadPhaseLayers > 0); - mMapping.resize(inNumBroadPhaseLayers); - -#if defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED) - mBroadPhaseLayerNames.resize(inNumBroadPhaseLayers, "Undefined"); -#endif // JPH_EXTERNAL_PROFILE || JPH_PROFILE_ENABLED - } - - // Configures a broadphase layer. - void ConfigureLayer(BroadPhaseLayer inBroadPhaseLayer, uint32 inGroupsToInclude, uint32 inGroupsToExclude) - { - JPH_ASSERT((BroadPhaseLayer::Type)inBroadPhaseLayer < (uint)mMapping.size()); - Mapping &m = mMapping[(BroadPhaseLayer::Type)inBroadPhaseLayer]; - m.mGroupsToInclude = inGroupsToInclude; - m.mGroupsToExclude = inGroupsToExclude; - } - - virtual uint GetNumBroadPhaseLayers() const override - { - return (uint)mMapping.size(); - } - - virtual BroadPhaseLayer GetBroadPhaseLayer(ObjectLayer inLayer) const override - { - // Try to find the first broadphase layer that matches - uint32 group = ObjectLayerPairFilterMask::sGetGroup(inLayer); - for (const Mapping &m : mMapping) - if ((group & m.mGroupsToInclude) != 0 && (group & m.mGroupsToExclude) == 0) - return BroadPhaseLayer(BroadPhaseLayer::Type(&m - mMapping.data())); - - // Fall back to the last broadphase layer - return BroadPhaseLayer(BroadPhaseLayer::Type(mMapping.size() - 1)); - } - - /// Returns true if an object layer should collide with a broadphase layer, this function is being called from ObjectVsBroadPhaseLayerFilterMask - inline bool ShouldCollide(ObjectLayer inLayer1, BroadPhaseLayer inLayer2) const - { - uint32 mask = ObjectLayerPairFilterMask::sGetMask(inLayer1); - const Mapping &m = mMapping[(BroadPhaseLayer::Type)inLayer2]; - return &m == &mMapping.back() // Last layer may collide with anything - || (m.mGroupsToInclude & mask) != 0; // Mask allows it to collide with objects that could reside in this layer - } - -#if defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED) - void SetBroadPhaseLayerName(BroadPhaseLayer inLayer, const char *inName) - { - mBroadPhaseLayerNames[(BroadPhaseLayer::Type)inLayer] = inName; - } - - virtual const char * GetBroadPhaseLayerName(BroadPhaseLayer inLayer) const override - { - return mBroadPhaseLayerNames[(BroadPhaseLayer::Type)inLayer]; - } -#endif // JPH_EXTERNAL_PROFILE || JPH_PROFILE_ENABLED - -private: - struct Mapping - { - uint32 mGroupsToInclude = 0; - uint32 mGroupsToExclude = ~uint32(0); - }; - Array mMapping; - -#if defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED) - Array mBroadPhaseLayerNames; -#endif // JPH_EXTERNAL_PROFILE || JPH_PROFILE_ENABLED -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhaseLayerInterfaceTable.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhaseLayerInterfaceTable.h deleted file mode 100644 index e777a08589b..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhaseLayerInterfaceTable.h +++ /dev/null @@ -1,64 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2023 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -/// BroadPhaseLayerInterface implementation. -/// This defines a mapping between object and broadphase layers. -/// This implementation uses a simple table -class BroadPhaseLayerInterfaceTable : public BroadPhaseLayerInterface -{ -public: - JPH_OVERRIDE_NEW_DELETE - - BroadPhaseLayerInterfaceTable(uint inNumObjectLayers, uint inNumBroadPhaseLayers) : - mNumBroadPhaseLayers(inNumBroadPhaseLayers) - { - mObjectToBroadPhase.resize(inNumObjectLayers, BroadPhaseLayer(0)); -#if defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED) - mBroadPhaseLayerNames.resize(inNumBroadPhaseLayers, "Undefined"); -#endif // JPH_EXTERNAL_PROFILE || JPH_PROFILE_ENABLED - } - - void MapObjectToBroadPhaseLayer(ObjectLayer inObjectLayer, BroadPhaseLayer inBroadPhaseLayer) - { - JPH_ASSERT((BroadPhaseLayer::Type)inBroadPhaseLayer < mNumBroadPhaseLayers); - mObjectToBroadPhase[inObjectLayer] = inBroadPhaseLayer; - } - - virtual uint GetNumBroadPhaseLayers() const override - { - return mNumBroadPhaseLayers; - } - - virtual BroadPhaseLayer GetBroadPhaseLayer(ObjectLayer inLayer) const override - { - return mObjectToBroadPhase[inLayer]; - } - -#if defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED) - void SetBroadPhaseLayerName(BroadPhaseLayer inLayer, const char *inName) - { - mBroadPhaseLayerNames[(BroadPhaseLayer::Type)inLayer] = inName; - } - - virtual const char * GetBroadPhaseLayerName(BroadPhaseLayer inLayer) const override - { - return mBroadPhaseLayerNames[(BroadPhaseLayer::Type)inLayer]; - } -#endif // JPH_EXTERNAL_PROFILE || JPH_PROFILE_ENABLED - -private: - uint mNumBroadPhaseLayers; - Array mObjectToBroadPhase; -#if defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED) - Array mBroadPhaseLayerNames; -#endif // JPH_EXTERNAL_PROFILE || JPH_PROFILE_ENABLED -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhaseQuadTree.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhaseQuadTree.cpp deleted file mode 100644 index 0c3d480ba30..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhaseQuadTree.cpp +++ /dev/null @@ -1,609 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include -#include -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -BroadPhaseQuadTree::~BroadPhaseQuadTree() -{ - delete [] mLayers; -} - -void BroadPhaseQuadTree::Init(BodyManager *inBodyManager, const BroadPhaseLayerInterface &inLayerInterface) -{ - BroadPhase::Init(inBodyManager, inLayerInterface); - - // Store input parameters - mBroadPhaseLayerInterface = &inLayerInterface; - mNumLayers = inLayerInterface.GetNumBroadPhaseLayers(); - JPH_ASSERT(mNumLayers < (BroadPhaseLayer::Type)cBroadPhaseLayerInvalid); - -#ifdef JPH_ENABLE_ASSERTS - // Store lock context - mLockContext = inBodyManager; -#endif // JPH_ENABLE_ASSERTS - - // Store max bodies - mMaxBodies = inBodyManager->GetMaxBodies(); - - // Initialize tracking data - mTracking.resize(mMaxBodies); - - // Init allocator - // Estimate the amount of nodes we're going to need - uint32 num_leaves = (uint32)(mMaxBodies + 1) / 2; // Assume 50% fill - uint32 num_leaves_plus_internal_nodes = num_leaves + (num_leaves + 2) / 3; // = Sum(num_leaves * 4^-i) with i = [0, Inf]. - mAllocator.Init(2 * num_leaves_plus_internal_nodes, 256); // We use double the amount of nodes while rebuilding the tree during Update() - - // Init sub trees - mLayers = new QuadTree [mNumLayers]; - for (uint l = 0; l < mNumLayers; ++l) - { - mLayers[l].Init(mAllocator); - -#if defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED) - // Set the name of the layer - mLayers[l].SetName(inLayerInterface.GetBroadPhaseLayerName(BroadPhaseLayer(BroadPhaseLayer::Type(l)))); -#endif // JPH_EXTERNAL_PROFILE || JPH_PROFILE_ENABLED - } -} - -void BroadPhaseQuadTree::FrameSync() -{ - JPH_PROFILE_FUNCTION(); - - // Take a unique lock on the old query lock so that we know no one is using the old nodes anymore. - // Note that nothing should be locked at this point to avoid risking a lock inversion deadlock. - // Note that in other places where we lock this mutex we don't use SharedLock to detect lock inversions. As long as - // nothing else is locked this is safe. This is why BroadPhaseQuery should be the highest priority lock. - UniqueLock root_lock(mQueryLocks[mQueryLockIdx ^ 1] JPH_IF_ENABLE_ASSERTS(, mLockContext, EPhysicsLockTypes::BroadPhaseQuery)); - - for (BroadPhaseLayer::Type l = 0; l < mNumLayers; ++l) - mLayers[l].DiscardOldTree(); -} - -void BroadPhaseQuadTree::Optimize() -{ - JPH_PROFILE_FUNCTION(); - - FrameSync(); - - LockModifications(); - - for (uint l = 0; l < mNumLayers; ++l) - { - QuadTree &tree = mLayers[l]; - if (tree.HasBodies()) - { - QuadTree::UpdateState update_state; - tree.UpdatePrepare(mBodyManager->GetBodies(), mTracking, update_state, true); - tree.UpdateFinalize(mBodyManager->GetBodies(), mTracking, update_state); - } - } - - UnlockModifications(); - - mNextLayerToUpdate = 0; -} - -void BroadPhaseQuadTree::LockModifications() -{ - // From this point on we prevent modifications to the tree - PhysicsLock::sLock(mUpdateMutex JPH_IF_ENABLE_ASSERTS(, mLockContext, EPhysicsLockTypes::BroadPhaseUpdate)); -} - -BroadPhase::UpdateState BroadPhaseQuadTree::UpdatePrepare() -{ - // LockModifications should have been called - JPH_ASSERT(mUpdateMutex.is_locked()); - - // Create update state - UpdateState update_state; - UpdateStateImpl *update_state_impl = reinterpret_cast(&update_state); - - // Loop until we've seen all layers - for (uint iteration = 0; iteration < mNumLayers; ++iteration) - { - // Get the layer - QuadTree &tree = mLayers[mNextLayerToUpdate]; - mNextLayerToUpdate = (mNextLayerToUpdate + 1) % mNumLayers; - - // If it is dirty we update this one - if (tree.HasBodies() && tree.IsDirty() && tree.CanBeUpdated()) - { - update_state_impl->mTree = &tree; - tree.UpdatePrepare(mBodyManager->GetBodies(), mTracking, update_state_impl->mUpdateState, false); - return update_state; - } - } - - // Nothing to update - update_state_impl->mTree = nullptr; - return update_state; -} - -void BroadPhaseQuadTree::UpdateFinalize(const UpdateState &inUpdateState) -{ - // LockModifications should have been called - JPH_ASSERT(mUpdateMutex.is_locked()); - - // Test if a tree was updated - const UpdateStateImpl *update_state_impl = reinterpret_cast(&inUpdateState); - if (update_state_impl->mTree == nullptr) - return; - - update_state_impl->mTree->UpdateFinalize(mBodyManager->GetBodies(), mTracking, update_state_impl->mUpdateState); - - // Make all queries from now on use the new lock - mQueryLockIdx = mQueryLockIdx ^ 1; -} - -void BroadPhaseQuadTree::UnlockModifications() -{ - // From this point on we allow modifications to the tree again - PhysicsLock::sUnlock(mUpdateMutex JPH_IF_ENABLE_ASSERTS(, mLockContext, EPhysicsLockTypes::BroadPhaseUpdate)); -} - -BroadPhase::AddState BroadPhaseQuadTree::AddBodiesPrepare(BodyID *ioBodies, int inNumber) -{ - JPH_PROFILE_FUNCTION(); - - JPH_ASSERT(inNumber > 0); - - const BodyVector &bodies = mBodyManager->GetBodies(); - JPH_ASSERT(mMaxBodies == mBodyManager->GetMaxBodies()); - - LayerState *state = new LayerState [mNumLayers]; - - // Sort bodies on layer - Body * const * const bodies_ptr = bodies.data(); // C pointer or else sort is incredibly slow in debug mode - QuickSort(ioBodies, ioBodies + inNumber, [bodies_ptr](BodyID inLHS, BodyID inRHS) { return bodies_ptr[inLHS.GetIndex()]->GetBroadPhaseLayer() < bodies_ptr[inRHS.GetIndex()]->GetBroadPhaseLayer(); }); - - BodyID *b_start = ioBodies, *b_end = ioBodies + inNumber; - while (b_start < b_end) - { - // Get broadphase layer - BroadPhaseLayer::Type broadphase_layer = (BroadPhaseLayer::Type)bodies[b_start->GetIndex()]->GetBroadPhaseLayer(); - JPH_ASSERT(broadphase_layer < mNumLayers); - - // Find first body with different layer - BodyID *b_mid = std::upper_bound(b_start, b_end, broadphase_layer, [bodies_ptr](BroadPhaseLayer::Type inLayer, BodyID inBodyID) { return inLayer < (BroadPhaseLayer::Type)bodies_ptr[inBodyID.GetIndex()]->GetBroadPhaseLayer(); }); - - // Keep track of state for this layer - LayerState &layer_state = state[broadphase_layer]; - layer_state.mBodyStart = b_start; - layer_state.mBodyEnd = b_mid; - - // Insert all bodies of the same layer - mLayers[broadphase_layer].AddBodiesPrepare(bodies, mTracking, b_start, int(b_mid - b_start), layer_state.mAddState); - - // Keep track in which tree we placed the object - for (const BodyID *b = b_start; b < b_mid; ++b) - { - uint32 index = b->GetIndex(); - JPH_ASSERT(bodies[index]->GetID() == *b, "Provided BodyID doesn't match BodyID in body manager"); - JPH_ASSERT(!bodies[index]->IsInBroadPhase()); - Tracking &t = mTracking[index]; - JPH_ASSERT(t.mBroadPhaseLayer == (BroadPhaseLayer::Type)cBroadPhaseLayerInvalid); - t.mBroadPhaseLayer = broadphase_layer; - JPH_ASSERT(t.mObjectLayer == cObjectLayerInvalid); - t.mObjectLayer = bodies[index]->GetObjectLayer(); - } - - // Repeat - b_start = b_mid; - } - - return state; -} - -void BroadPhaseQuadTree::AddBodiesFinalize(BodyID *ioBodies, int inNumber, AddState inAddState) -{ - JPH_PROFILE_FUNCTION(); - - // This cannot run concurrently with UpdatePrepare()/UpdateFinalize() - SharedLock lock(mUpdateMutex JPH_IF_ENABLE_ASSERTS(, mLockContext, EPhysicsLockTypes::BroadPhaseUpdate)); - - BodyVector &bodies = mBodyManager->GetBodies(); - JPH_ASSERT(mMaxBodies == mBodyManager->GetMaxBodies()); - - LayerState *state = (LayerState *)inAddState; - - for (BroadPhaseLayer::Type broadphase_layer = 0; broadphase_layer < mNumLayers; broadphase_layer++) - { - const LayerState &l = state[broadphase_layer]; - if (l.mBodyStart != nullptr) - { - // Insert all bodies of the same layer - mLayers[broadphase_layer].AddBodiesFinalize(mTracking, int(l.mBodyEnd - l.mBodyStart), l.mAddState); - - // Mark added to broadphase - for (const BodyID *b = l.mBodyStart; b < l.mBodyEnd; ++b) - { - uint32 index = b->GetIndex(); - JPH_ASSERT(bodies[index]->GetID() == *b, "Provided BodyID doesn't match BodyID in body manager"); - JPH_ASSERT(mTracking[index].mBroadPhaseLayer == broadphase_layer); - JPH_ASSERT(mTracking[index].mObjectLayer == bodies[index]->GetObjectLayer()); - JPH_ASSERT(!bodies[index]->IsInBroadPhase()); - bodies[index]->SetInBroadPhaseInternal(true); - } - } - } - - delete [] state; -} - -void BroadPhaseQuadTree::AddBodiesAbort(BodyID *ioBodies, int inNumber, AddState inAddState) -{ - JPH_PROFILE_FUNCTION(); - - JPH_IF_ENABLE_ASSERTS(const BodyVector &bodies = mBodyManager->GetBodies();) - JPH_ASSERT(mMaxBodies == mBodyManager->GetMaxBodies()); - - LayerState *state = (LayerState *)inAddState; - - for (BroadPhaseLayer::Type broadphase_layer = 0; broadphase_layer < mNumLayers; broadphase_layer++) - { - const LayerState &l = state[broadphase_layer]; - if (l.mBodyStart != nullptr) - { - // Insert all bodies of the same layer - mLayers[broadphase_layer].AddBodiesAbort(mTracking, l.mAddState); - - // Reset bookkeeping - for (const BodyID *b = l.mBodyStart; b < l.mBodyEnd; ++b) - { - uint32 index = b->GetIndex(); - JPH_ASSERT(bodies[index]->GetID() == *b, "Provided BodyID doesn't match BodyID in body manager"); - JPH_ASSERT(!bodies[index]->IsInBroadPhase()); - Tracking &t = mTracking[index]; - JPH_ASSERT(t.mBroadPhaseLayer == broadphase_layer); - t.mBroadPhaseLayer = (BroadPhaseLayer::Type)cBroadPhaseLayerInvalid; - t.mObjectLayer = cObjectLayerInvalid; - } - } - } - - delete [] state; -} - -void BroadPhaseQuadTree::RemoveBodies(BodyID *ioBodies, int inNumber) -{ - JPH_PROFILE_FUNCTION(); - - // This cannot run concurrently with UpdatePrepare()/UpdateFinalize() - SharedLock lock(mUpdateMutex JPH_IF_ENABLE_ASSERTS(, mLockContext, EPhysicsLockTypes::BroadPhaseUpdate)); - - JPH_ASSERT(inNumber > 0); - - BodyVector &bodies = mBodyManager->GetBodies(); - JPH_ASSERT(mMaxBodies == mBodyManager->GetMaxBodies()); - - // Sort bodies on layer - Tracking *tracking = mTracking.data(); // C pointer or else sort is incredibly slow in debug mode - QuickSort(ioBodies, ioBodies + inNumber, [tracking](BodyID inLHS, BodyID inRHS) { return tracking[inLHS.GetIndex()].mBroadPhaseLayer < tracking[inRHS.GetIndex()].mBroadPhaseLayer; }); - - BodyID *b_start = ioBodies, *b_end = ioBodies + inNumber; - while (b_start < b_end) - { - // Get broad phase layer - BroadPhaseLayer::Type broadphase_layer = mTracking[b_start->GetIndex()].mBroadPhaseLayer; - JPH_ASSERT(broadphase_layer != (BroadPhaseLayer::Type)cBroadPhaseLayerInvalid); - - // Find first body with different layer - BodyID *b_mid = std::upper_bound(b_start, b_end, broadphase_layer, [tracking](BroadPhaseLayer::Type inLayer, BodyID inBodyID) { return inLayer < tracking[inBodyID.GetIndex()].mBroadPhaseLayer; }); - - // Remove all bodies of the same layer - mLayers[broadphase_layer].RemoveBodies(bodies, mTracking, b_start, int(b_mid - b_start)); - - for (const BodyID *b = b_start; b < b_mid; ++b) - { - // Reset bookkeeping - uint32 index = b->GetIndex(); - Tracking &t = tracking[index]; - t.mBroadPhaseLayer = (BroadPhaseLayer::Type)cBroadPhaseLayerInvalid; - t.mObjectLayer = cObjectLayerInvalid; - - // Mark removed from broadphase - JPH_ASSERT(bodies[index]->IsInBroadPhase()); - bodies[index]->SetInBroadPhaseInternal(false); - } - - // Repeat - b_start = b_mid; - } -} - -void BroadPhaseQuadTree::NotifyBodiesAABBChanged(BodyID *ioBodies, int inNumber, bool inTakeLock) -{ - JPH_PROFILE_FUNCTION(); - - JPH_ASSERT(inNumber > 0); - - // This cannot run concurrently with UpdatePrepare()/UpdateFinalize() - if (inTakeLock) - PhysicsLock::sLockShared(mUpdateMutex JPH_IF_ENABLE_ASSERTS(, mLockContext, EPhysicsLockTypes::BroadPhaseUpdate)); - else - JPH_ASSERT(mUpdateMutex.is_locked()); - - const BodyVector &bodies = mBodyManager->GetBodies(); - JPH_ASSERT(mMaxBodies == mBodyManager->GetMaxBodies()); - - // Sort bodies on layer - const Tracking *tracking = mTracking.data(); // C pointer or else sort is incredibly slow in debug mode - QuickSort(ioBodies, ioBodies + inNumber, [tracking](BodyID inLHS, BodyID inRHS) { return tracking[inLHS.GetIndex()].mBroadPhaseLayer < tracking[inRHS.GetIndex()].mBroadPhaseLayer; }); - - BodyID *b_start = ioBodies, *b_end = ioBodies + inNumber; - while (b_start < b_end) - { - // Get broadphase layer - BroadPhaseLayer::Type broadphase_layer = tracking[b_start->GetIndex()].mBroadPhaseLayer; - JPH_ASSERT(broadphase_layer != (BroadPhaseLayer::Type)cBroadPhaseLayerInvalid); - - // Find first body with different layer - BodyID *b_mid = std::upper_bound(b_start, b_end, broadphase_layer, [tracking](BroadPhaseLayer::Type inLayer, BodyID inBodyID) { return inLayer < tracking[inBodyID.GetIndex()].mBroadPhaseLayer; }); - - // Nodify all bodies of the same layer changed - mLayers[broadphase_layer].NotifyBodiesAABBChanged(bodies, mTracking, b_start, int(b_mid - b_start)); - - // Repeat - b_start = b_mid; - } - - if (inTakeLock) - PhysicsLock::sUnlockShared(mUpdateMutex JPH_IF_ENABLE_ASSERTS(, mLockContext, EPhysicsLockTypes::BroadPhaseUpdate)); -} - -void BroadPhaseQuadTree::NotifyBodiesLayerChanged(BodyID *ioBodies, int inNumber) -{ - JPH_PROFILE_FUNCTION(); - - JPH_ASSERT(inNumber > 0); - - // First sort the bodies that actually changed layer to beginning of the array - const BodyVector &bodies = mBodyManager->GetBodies(); - JPH_ASSERT(mMaxBodies == mBodyManager->GetMaxBodies()); - for (BodyID *body_id = ioBodies + inNumber - 1; body_id >= ioBodies; --body_id) - { - uint32 index = body_id->GetIndex(); - JPH_ASSERT(bodies[index]->GetID() == *body_id, "Provided BodyID doesn't match BodyID in body manager"); - const Body *body = bodies[index]; - BroadPhaseLayer::Type broadphase_layer = (BroadPhaseLayer::Type)body->GetBroadPhaseLayer(); - JPH_ASSERT(broadphase_layer < mNumLayers); - if (mTracking[index].mBroadPhaseLayer == broadphase_layer) - { - // Update tracking information - mTracking[index].mObjectLayer = body->GetObjectLayer(); - - // Move the body to the end, layer didn't change - swap(*body_id, ioBodies[inNumber - 1]); - --inNumber; - } - } - - if (inNumber > 0) - { - // Changing layer requires us to remove from one tree and add to another, so this is equivalent to removing all bodies first and then adding them again - RemoveBodies(ioBodies, inNumber); - AddState add_state = AddBodiesPrepare(ioBodies, inNumber); - AddBodiesFinalize(ioBodies, inNumber, add_state); - } -} - -void BroadPhaseQuadTree::CastRay(const RayCast &inRay, RayCastBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const -{ - JPH_PROFILE_FUNCTION(); - - JPH_ASSERT(mMaxBodies == mBodyManager->GetMaxBodies()); - - // Prevent this from running in parallel with node deletion in FrameSync(), see notes there - shared_lock lock(mQueryLocks[mQueryLockIdx]); - - // Loop over all layers and test the ones that could hit - for (BroadPhaseLayer::Type l = 0; l < mNumLayers; ++l) - { - const QuadTree &tree = mLayers[l]; - if (tree.HasBodies() && inBroadPhaseLayerFilter.ShouldCollide(BroadPhaseLayer(l))) - { - JPH_PROFILE(tree.GetName()); - tree.CastRay(inRay, ioCollector, inObjectLayerFilter, mTracking); - if (ioCollector.ShouldEarlyOut()) - break; - } - } -} - -void BroadPhaseQuadTree::CollideAABox(const AABox &inBox, CollideShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const -{ - JPH_PROFILE_FUNCTION(); - - JPH_ASSERT(mMaxBodies == mBodyManager->GetMaxBodies()); - - // Prevent this from running in parallel with node deletion in FrameSync(), see notes there - shared_lock lock(mQueryLocks[mQueryLockIdx]); - - // Loop over all layers and test the ones that could hit - for (BroadPhaseLayer::Type l = 0; l < mNumLayers; ++l) - { - const QuadTree &tree = mLayers[l]; - if (tree.HasBodies() && inBroadPhaseLayerFilter.ShouldCollide(BroadPhaseLayer(l))) - { - JPH_PROFILE(tree.GetName()); - tree.CollideAABox(inBox, ioCollector, inObjectLayerFilter, mTracking); - if (ioCollector.ShouldEarlyOut()) - break; - } - } -} - -void BroadPhaseQuadTree::CollideSphere(Vec3Arg inCenter, float inRadius, CollideShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const -{ - JPH_PROFILE_FUNCTION(); - - JPH_ASSERT(mMaxBodies == mBodyManager->GetMaxBodies()); - - // Prevent this from running in parallel with node deletion in FrameSync(), see notes there - shared_lock lock(mQueryLocks[mQueryLockIdx]); - - // Loop over all layers and test the ones that could hit - for (BroadPhaseLayer::Type l = 0; l < mNumLayers; ++l) - { - const QuadTree &tree = mLayers[l]; - if (tree.HasBodies() && inBroadPhaseLayerFilter.ShouldCollide(BroadPhaseLayer(l))) - { - JPH_PROFILE(tree.GetName()); - tree.CollideSphere(inCenter, inRadius, ioCollector, inObjectLayerFilter, mTracking); - if (ioCollector.ShouldEarlyOut()) - break; - } - } -} - -void BroadPhaseQuadTree::CollidePoint(Vec3Arg inPoint, CollideShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const -{ - JPH_PROFILE_FUNCTION(); - - JPH_ASSERT(mMaxBodies == mBodyManager->GetMaxBodies()); - - // Prevent this from running in parallel with node deletion in FrameSync(), see notes there - shared_lock lock(mQueryLocks[mQueryLockIdx]); - - // Loop over all layers and test the ones that could hit - for (BroadPhaseLayer::Type l = 0; l < mNumLayers; ++l) - { - const QuadTree &tree = mLayers[l]; - if (tree.HasBodies() && inBroadPhaseLayerFilter.ShouldCollide(BroadPhaseLayer(l))) - { - JPH_PROFILE(tree.GetName()); - tree.CollidePoint(inPoint, ioCollector, inObjectLayerFilter, mTracking); - if (ioCollector.ShouldEarlyOut()) - break; - } - } -} - -void BroadPhaseQuadTree::CollideOrientedBox(const OrientedBox &inBox, CollideShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const -{ - JPH_PROFILE_FUNCTION(); - - JPH_ASSERT(mMaxBodies == mBodyManager->GetMaxBodies()); - - // Prevent this from running in parallel with node deletion in FrameSync(), see notes there - shared_lock lock(mQueryLocks[mQueryLockIdx]); - - // Loop over all layers and test the ones that could hit - for (BroadPhaseLayer::Type l = 0; l < mNumLayers; ++l) - { - const QuadTree &tree = mLayers[l]; - if (tree.HasBodies() && inBroadPhaseLayerFilter.ShouldCollide(BroadPhaseLayer(l))) - { - JPH_PROFILE(tree.GetName()); - tree.CollideOrientedBox(inBox, ioCollector, inObjectLayerFilter, mTracking); - if (ioCollector.ShouldEarlyOut()) - break; - } - } -} - -void BroadPhaseQuadTree::CastAABoxNoLock(const AABoxCast &inBox, CastShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const -{ - JPH_PROFILE_FUNCTION(); - - JPH_ASSERT(mMaxBodies == mBodyManager->GetMaxBodies()); - - // Loop over all layers and test the ones that could hit - for (BroadPhaseLayer::Type l = 0; l < mNumLayers; ++l) - { - const QuadTree &tree = mLayers[l]; - if (tree.HasBodies() && inBroadPhaseLayerFilter.ShouldCollide(BroadPhaseLayer(l))) - { - JPH_PROFILE(tree.GetName()); - tree.CastAABox(inBox, ioCollector, inObjectLayerFilter, mTracking); - if (ioCollector.ShouldEarlyOut()) - break; - } - } -} - -void BroadPhaseQuadTree::CastAABox(const AABoxCast &inBox, CastShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const -{ - // Prevent this from running in parallel with node deletion in FrameSync(), see notes there - shared_lock lock(mQueryLocks[mQueryLockIdx]); - - CastAABoxNoLock(inBox, ioCollector, inBroadPhaseLayerFilter, inObjectLayerFilter); -} - -void BroadPhaseQuadTree::FindCollidingPairs(BodyID *ioActiveBodies, int inNumActiveBodies, float inSpeculativeContactDistance, const ObjectVsBroadPhaseLayerFilter &inObjectVsBroadPhaseLayerFilter, const ObjectLayerPairFilter &inObjectLayerPairFilter, BodyPairCollector &ioPairCollector) const -{ - JPH_PROFILE_FUNCTION(); - - const BodyVector &bodies = mBodyManager->GetBodies(); - JPH_ASSERT(mMaxBodies == mBodyManager->GetMaxBodies()); - - // Note that we don't take any locks at this point. We know that the tree is not going to be swapped or deleted while finding collision pairs due to the way the jobs are scheduled in the PhysicsSystem::Update. - - // Sort bodies on layer - const Tracking *tracking = mTracking.data(); // C pointer or else sort is incredibly slow in debug mode - QuickSort(ioActiveBodies, ioActiveBodies + inNumActiveBodies, [tracking](BodyID inLHS, BodyID inRHS) { return tracking[inLHS.GetIndex()].mObjectLayer < tracking[inRHS.GetIndex()].mObjectLayer; }); - - BodyID *b_start = ioActiveBodies, *b_end = ioActiveBodies + inNumActiveBodies; - while (b_start < b_end) - { - // Get broadphase layer - ObjectLayer object_layer = tracking[b_start->GetIndex()].mObjectLayer; - JPH_ASSERT(object_layer != cObjectLayerInvalid); - - // Find first body with different layer - BodyID *b_mid = std::upper_bound(b_start, b_end, object_layer, [tracking](ObjectLayer inLayer, BodyID inBodyID) { return inLayer < tracking[inBodyID.GetIndex()].mObjectLayer; }); - - // Loop over all layers and test the ones that could hit - for (BroadPhaseLayer::Type l = 0; l < mNumLayers; ++l) - { - const QuadTree &tree = mLayers[l]; - if (tree.HasBodies() && inObjectVsBroadPhaseLayerFilter.ShouldCollide(object_layer, BroadPhaseLayer(l))) - { - JPH_PROFILE(tree.GetName()); - tree.FindCollidingPairs(bodies, b_start, int(b_mid - b_start), inSpeculativeContactDistance, ioPairCollector, inObjectLayerPairFilter); - } - } - - // Repeat - b_start = b_mid; - } -} - -AABox BroadPhaseQuadTree::GetBounds() const -{ - // Prevent this from running in parallel with node deletion in FrameSync(), see notes there - shared_lock lock(mQueryLocks[mQueryLockIdx]); - - AABox bounds; - for (BroadPhaseLayer::Type l = 0; l < mNumLayers; ++l) - bounds.Encapsulate(mLayers[l].GetBounds()); - return bounds; -} - -#ifdef JPH_TRACK_BROADPHASE_STATS - -void BroadPhaseQuadTree::ReportStats() -{ - Trace("Query Type, Filter Description, Tree Name, Num Queries, Total Time (%%), Total Time Excl. Collector (%%), Nodes Visited, Bodies Visited, Hits Reported, Hits Reported vs Bodies Visited (%%), Hits Reported vs Nodes Visited"); - - uint64 total_ticks = 0; - for (BroadPhaseLayer::Type l = 0; l < mNumLayers; ++l) - total_ticks += mLayers[l].GetTicks100Pct(); - - for (BroadPhaseLayer::Type l = 0; l < mNumLayers; ++l) - mLayers[l].ReportStats(total_ticks); -} - -#endif // JPH_TRACK_BROADPHASE_STATS - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhaseQuadTree.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhaseQuadTree.h deleted file mode 100644 index ae97c10f0e3..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhaseQuadTree.h +++ /dev/null @@ -1,108 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -/// Fast SIMD based quad tree BroadPhase that is multithreading aware and tries to do a minimal amount of locking. -class JPH_EXPORT BroadPhaseQuadTree final : public BroadPhase -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Destructor - virtual ~BroadPhaseQuadTree() override; - - // Implementing interface of BroadPhase (see BroadPhase for documentation) - virtual void Init(BodyManager *inBodyManager, const BroadPhaseLayerInterface &inLayerInterface) override; - virtual void Optimize() override; - virtual void FrameSync() override; - virtual void LockModifications() override; - virtual UpdateState UpdatePrepare() override; - virtual void UpdateFinalize(const UpdateState &inUpdateState) override; - virtual void UnlockModifications() override; - virtual AddState AddBodiesPrepare(BodyID *ioBodies, int inNumber) override; - virtual void AddBodiesFinalize(BodyID *ioBodies, int inNumber, AddState inAddState) override; - virtual void AddBodiesAbort(BodyID *ioBodies, int inNumber, AddState inAddState) override; - virtual void RemoveBodies(BodyID *ioBodies, int inNumber) override; - virtual void NotifyBodiesAABBChanged(BodyID *ioBodies, int inNumber, bool inTakeLock) override; - virtual void NotifyBodiesLayerChanged(BodyID *ioBodies, int inNumber) override; - virtual void CastRay(const RayCast &inRay, RayCastBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const override; - virtual void CollideAABox(const AABox &inBox, CollideShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const override; - virtual void CollideSphere(Vec3Arg inCenter, float inRadius, CollideShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const override; - virtual void CollidePoint(Vec3Arg inPoint, CollideShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const override; - virtual void CollideOrientedBox(const OrientedBox &inBox, CollideShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const override; - virtual void CastAABoxNoLock(const AABoxCast &inBox, CastShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const override; - virtual void CastAABox(const AABoxCast &inBox, CastShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const override; - virtual void FindCollidingPairs(BodyID *ioActiveBodies, int inNumActiveBodies, float inSpeculativeContactDistance, const ObjectVsBroadPhaseLayerFilter &inObjectVsBroadPhaseLayerFilter, const ObjectLayerPairFilter &inObjectLayerPairFilter, BodyPairCollector &ioPairCollector) const override; - virtual AABox GetBounds() const override; -#ifdef JPH_TRACK_BROADPHASE_STATS - virtual void ReportStats() override; -#endif // JPH_TRACK_BROADPHASE_STATS - -private: - /// Helper struct for AddBodies handle - struct LayerState - { - JPH_OVERRIDE_NEW_DELETE - - BodyID * mBodyStart = nullptr; - BodyID * mBodyEnd; - QuadTree::AddState mAddState; - }; - - using Tracking = QuadTree::Tracking; - using TrackingVector = QuadTree::TrackingVector; - -#ifdef JPH_ENABLE_ASSERTS - /// Context used to lock a physics lock - PhysicsLockContext mLockContext = nullptr; -#endif // JPH_ENABLE_ASSERTS - - /// Max amount of bodies we support - size_t mMaxBodies = 0; - - /// Array that for each BodyID keeps track of where it is located in which tree - TrackingVector mTracking; - - /// Node allocator for all trees - QuadTree::Allocator mAllocator; - - /// Information about broad phase layers - const BroadPhaseLayerInterface *mBroadPhaseLayerInterface = nullptr; - - /// One tree per object layer - QuadTree * mLayers; - uint mNumLayers; - - /// UpdateState implementation for this tree used during UpdatePrepare/Finalize() - struct UpdateStateImpl - { - QuadTree * mTree; - QuadTree::UpdateState mUpdateState; - }; - - static_assert(sizeof(UpdateStateImpl) <= sizeof(UpdateState)); - static_assert(alignof(UpdateStateImpl) <= alignof(UpdateState)); - - /// Mutex that prevents object modification during UpdatePrepare/Finalize() - SharedMutex mUpdateMutex; - - /// We double buffer all trees so that we can query while building the next one and we destroy the old tree the next physics update. - /// This structure ensures that we wait for queries that are still using the old tree. - mutable SharedMutex mQueryLocks[2]; - - /// This index indicates which lock is currently active, it alternates between 0 and 1 - atomic mQueryLockIdx { 0 }; - - /// This is the next tree to update in UpdatePrepare() - uint32 mNextLayerToUpdate = 0; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhaseQuery.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhaseQuery.h deleted file mode 100644 index 10085e66fe8..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/BroadPhaseQuery.h +++ /dev/null @@ -1,53 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -struct RayCast; -class BroadPhaseCastResult; -class AABox; -class OrientedBox; -struct AABoxCast; - -// Various collector configurations -using RayCastBodyCollector = CollisionCollector; -using CastShapeBodyCollector = CollisionCollector; -using CollideShapeBodyCollector = CollisionCollector; - -/// Interface to the broadphase that can perform collision queries. These queries will only test the bounding box of the body to quickly determine a potential set of colliding bodies. -/// The shapes of the bodies are not tested, if you want this then you should use the NarrowPhaseQuery interface. -class JPH_EXPORT BroadPhaseQuery : public NonCopyable -{ -public: - /// Virtual destructor - virtual ~BroadPhaseQuery() = default; - - /// Cast a ray and add any hits to ioCollector - virtual void CastRay(const RayCast &inRay, RayCastBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter = { }, const ObjectLayerFilter &inObjectLayerFilter = { }) const = 0; - - /// Get bodies intersecting with inBox and any hits to ioCollector - virtual void CollideAABox(const AABox &inBox, CollideShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter = { }, const ObjectLayerFilter &inObjectLayerFilter = { }) const = 0; - - /// Get bodies intersecting with a sphere and any hits to ioCollector - virtual void CollideSphere(Vec3Arg inCenter, float inRadius, CollideShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter = { }, const ObjectLayerFilter &inObjectLayerFilter = { }) const = 0; - - /// Get bodies intersecting with a point and any hits to ioCollector - virtual void CollidePoint(Vec3Arg inPoint, CollideShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter = { }, const ObjectLayerFilter &inObjectLayerFilter = { }) const = 0; - - /// Get bodies intersecting with an oriented box and any hits to ioCollector - virtual void CollideOrientedBox(const OrientedBox &inBox, CollideShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter = { }, const ObjectLayerFilter &inObjectLayerFilter = { }) const = 0; - - /// Cast a box and add any hits to ioCollector - virtual void CastAABox(const AABoxCast &inBox, CastShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter = { }, const ObjectLayerFilter &inObjectLayerFilter = { }) const = 0; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/ObjectVsBroadPhaseLayerFilterMask.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/ObjectVsBroadPhaseLayerFilterMask.h deleted file mode 100644 index 20305a5f0ca..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/ObjectVsBroadPhaseLayerFilterMask.h +++ /dev/null @@ -1,35 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2023 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -/// Class that determines if an object layer can collide with a broadphase layer. -/// This implementation works together with BroadPhaseLayerInterfaceMask and ObjectLayerPairFilterMask -class ObjectVsBroadPhaseLayerFilterMask : public ObjectVsBroadPhaseLayerFilter -{ -public: - JPH_OVERRIDE_NEW_DELETE - -/// Constructor - ObjectVsBroadPhaseLayerFilterMask(const BroadPhaseLayerInterfaceMask &inBroadPhaseLayerInterface) : - mBroadPhaseLayerInterface(inBroadPhaseLayerInterface) - { - } - - /// Returns true if an object layer should collide with a broadphase layer - virtual bool ShouldCollide(ObjectLayer inLayer1, BroadPhaseLayer inLayer2) const override - { - // Just defer to BroadPhaseLayerInterface - return mBroadPhaseLayerInterface.ShouldCollide(inLayer1, inLayer2); - } - -private: - const BroadPhaseLayerInterfaceMask &mBroadPhaseLayerInterface; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/ObjectVsBroadPhaseLayerFilterTable.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/ObjectVsBroadPhaseLayerFilterTable.h deleted file mode 100644 index 532ce6da0ed..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/ObjectVsBroadPhaseLayerFilterTable.h +++ /dev/null @@ -1,66 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2023 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -/// Class that determines if an object layer can collide with a broadphase layer. -/// This implementation uses a table and constructs itself from an ObjectLayerPairFilter and a BroadPhaseLayerInterface. -class ObjectVsBroadPhaseLayerFilterTable : public ObjectVsBroadPhaseLayerFilter -{ -private: - /// Get which bit corresponds to the pair (inLayer1, inLayer2) - uint GetBit(ObjectLayer inLayer1, BroadPhaseLayer inLayer2) const - { - // Calculate at which bit the entry for this pair resides - return inLayer1 * mNumBroadPhaseLayers + (BroadPhaseLayer::Type)inLayer2; - } - -public: - JPH_OVERRIDE_NEW_DELETE - - /// Construct the table - /// @param inBroadPhaseLayerInterface The broad phase layer interface that maps object layers to broad phase layers - /// @param inNumBroadPhaseLayers Number of broad phase layers - /// @param inObjectLayerPairFilter The object layer pair filter that determines which object layers can collide - /// @param inNumObjectLayers Number of object layers - ObjectVsBroadPhaseLayerFilterTable(const BroadPhaseLayerInterface &inBroadPhaseLayerInterface, uint inNumBroadPhaseLayers, const ObjectLayerPairFilter &inObjectLayerPairFilter, uint inNumObjectLayers) : - mNumBroadPhaseLayers(inNumBroadPhaseLayers) - { - // Resize table and set all entries to false - mTable.resize((inNumBroadPhaseLayers * inNumObjectLayers + 7) / 8, 0); - - // Loop over all object layer pairs - for (ObjectLayer o1 = 0; o1 < inNumObjectLayers; ++o1) - for (ObjectLayer o2 = 0; o2 < inNumObjectLayers; ++o2) - { - // Get the broad phase layer for the second object layer - BroadPhaseLayer b2 = inBroadPhaseLayerInterface.GetBroadPhaseLayer(o2); - JPH_ASSERT((BroadPhaseLayer::Type)b2 < inNumBroadPhaseLayers); - - // If the object layers collide then so should the object and broadphase layer - if (inObjectLayerPairFilter.ShouldCollide(o1, o2)) - { - uint bit = GetBit(o1, b2); - mTable[bit >> 3] |= 1 << (bit & 0b111); - } - } - } - - /// Returns true if an object layer should collide with a broadphase layer - virtual bool ShouldCollide(ObjectLayer inLayer1, BroadPhaseLayer inLayer2) const override - { - uint bit = GetBit(inLayer1, inLayer2); - return (mTable[bit >> 3] & (1 << (bit & 0b111))) != 0; - } - -private: - uint mNumBroadPhaseLayers; ///< The total number of broadphase layers - Array mTable; ///< The table of bits that indicates which layers collide -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/QuadTree.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/QuadTree.cpp deleted file mode 100644 index 4c1e740c7f2..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/QuadTree.cpp +++ /dev/null @@ -1,1684 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef JPH_DUMP_BROADPHASE_TREE -JPH_SUPPRESS_WARNINGS_STD_BEGIN -#include -JPH_SUPPRESS_WARNINGS_STD_END -#endif // JPH_DUMP_BROADPHASE_TREE - -JPH_NAMESPACE_BEGIN - -//////////////////////////////////////////////////////////////////////////////////////////////////////// -// QuadTree::Node -//////////////////////////////////////////////////////////////////////////////////////////////////////// - -QuadTree::Node::Node(bool inIsChanged) : - mIsChanged(inIsChanged) -{ - // First reset bounds - Vec4 val = Vec4::sReplicate(cLargeFloat); - val.StoreFloat4((Float4 *)&mBoundsMinX); - val.StoreFloat4((Float4 *)&mBoundsMinY); - val.StoreFloat4((Float4 *)&mBoundsMinZ); - val = Vec4::sReplicate(-cLargeFloat); - val.StoreFloat4((Float4 *)&mBoundsMaxX); - val.StoreFloat4((Float4 *)&mBoundsMaxY); - val.StoreFloat4((Float4 *)&mBoundsMaxZ); - - // Reset child node ids - mChildNodeID[0] = NodeID::sInvalid(); - mChildNodeID[1] = NodeID::sInvalid(); - mChildNodeID[2] = NodeID::sInvalid(); - mChildNodeID[3] = NodeID::sInvalid(); -} - -void QuadTree::Node::GetChildBounds(int inChildIndex, AABox &outBounds) const -{ - // Read bounding box in order min -> max - outBounds.mMin = Vec3(mBoundsMinX[inChildIndex], mBoundsMinY[inChildIndex], mBoundsMinZ[inChildIndex]); - outBounds.mMax = Vec3(mBoundsMaxX[inChildIndex], mBoundsMaxY[inChildIndex], mBoundsMaxZ[inChildIndex]); -} - -void QuadTree::Node::SetChildBounds(int inChildIndex, const AABox &inBounds) -{ - // Set max first (this keeps the bounding box invalid for reading threads) - mBoundsMaxZ[inChildIndex] = inBounds.mMax.GetZ(); - mBoundsMaxY[inChildIndex] = inBounds.mMax.GetY(); - mBoundsMaxX[inChildIndex] = inBounds.mMax.GetX(); - - // Then set min (and make box valid) - mBoundsMinZ[inChildIndex] = inBounds.mMin.GetZ(); - mBoundsMinY[inChildIndex] = inBounds.mMin.GetY(); - mBoundsMinX[inChildIndex] = inBounds.mMin.GetX(); // Min X becomes valid last -} - -void QuadTree::Node::InvalidateChildBounds(int inChildIndex) -{ - // First we make the box invalid by setting the min to cLargeFloat - mBoundsMinX[inChildIndex] = cLargeFloat; // Min X becomes invalid first - mBoundsMinY[inChildIndex] = cLargeFloat; - mBoundsMinZ[inChildIndex] = cLargeFloat; - - // Then we reset the max values too - mBoundsMaxX[inChildIndex] = -cLargeFloat; - mBoundsMaxY[inChildIndex] = -cLargeFloat; - mBoundsMaxZ[inChildIndex] = -cLargeFloat; -} - -void QuadTree::Node::GetNodeBounds(AABox &outBounds) const -{ - // Get first child bounds - GetChildBounds(0, outBounds); - - // Encapsulate other child bounds - for (int child_idx = 1; child_idx < 4; ++child_idx) - { - AABox tmp; - GetChildBounds(child_idx, tmp); - outBounds.Encapsulate(tmp); - } -} - -bool QuadTree::Node::EncapsulateChildBounds(int inChildIndex, const AABox &inBounds) -{ - bool changed = AtomicMin(mBoundsMinX[inChildIndex], inBounds.mMin.GetX()); - changed |= AtomicMin(mBoundsMinY[inChildIndex], inBounds.mMin.GetY()); - changed |= AtomicMin(mBoundsMinZ[inChildIndex], inBounds.mMin.GetZ()); - changed |= AtomicMax(mBoundsMaxX[inChildIndex], inBounds.mMax.GetX()); - changed |= AtomicMax(mBoundsMaxY[inChildIndex], inBounds.mMax.GetY()); - changed |= AtomicMax(mBoundsMaxZ[inChildIndex], inBounds.mMax.GetZ()); - return changed; -} - -//////////////////////////////////////////////////////////////////////////////////////////////////////// -// QuadTree -//////////////////////////////////////////////////////////////////////////////////////////////////////// - -const float QuadTree::cLargeFloat = 1.0e30f; -const AABox QuadTree::cInvalidBounds(Vec3::sReplicate(cLargeFloat), Vec3::sReplicate(-cLargeFloat)); - -void QuadTree::GetBodyLocation(const TrackingVector &inTracking, BodyID inBodyID, uint32 &outNodeIdx, uint32 &outChildIdx) const -{ - uint32 body_location = inTracking[inBodyID.GetIndex()].mBodyLocation; - JPH_ASSERT(body_location != Tracking::cInvalidBodyLocation); - outNodeIdx = body_location & 0x3fffffff; - outChildIdx = body_location >> 30; - JPH_ASSERT(mAllocator->Get(outNodeIdx).mChildNodeID[outChildIdx] == inBodyID, "Make sure that the body is in the node where it should be"); -} - -void QuadTree::SetBodyLocation(TrackingVector &ioTracking, BodyID inBodyID, uint32 inNodeIdx, uint32 inChildIdx) const -{ - JPH_ASSERT(inNodeIdx <= 0x3fffffff); - JPH_ASSERT(inChildIdx < 4); - JPH_ASSERT(mAllocator->Get(inNodeIdx).mChildNodeID[inChildIdx] == inBodyID, "Make sure that the body is in the node where it should be"); - ioTracking[inBodyID.GetIndex()].mBodyLocation = inNodeIdx + (inChildIdx << 30); - -#ifdef JPH_ENABLE_ASSERTS - uint32 v1, v2; - GetBodyLocation(ioTracking, inBodyID, v1, v2); - JPH_ASSERT(v1 == inNodeIdx); - JPH_ASSERT(v2 == inChildIdx); -#endif -} - -void QuadTree::sInvalidateBodyLocation(TrackingVector &ioTracking, BodyID inBodyID) -{ - ioTracking[inBodyID.GetIndex()].mBodyLocation = Tracking::cInvalidBodyLocation; -} - -QuadTree::~QuadTree() -{ - // Get rid of any nodes that are still to be freed - DiscardOldTree(); - - // Get the current root node - const RootNode &root_node = GetCurrentRoot(); - - // Collect all bodies - Allocator::Batch free_batch; - NodeID node_stack[cStackSize]; - node_stack[0] = root_node.GetNodeID(); - JPH_ASSERT(node_stack[0].IsValid()); - if (node_stack[0].IsNode()) - { - int top = 0; - do - { - // Process node - NodeID node_id = node_stack[top]; - JPH_ASSERT(!node_id.IsBody()); - uint32 node_idx = node_id.GetNodeIndex(); - const Node &node = mAllocator->Get(node_idx); - - // Recurse and get all child nodes - for (NodeID child_node_id : node.mChildNodeID) - if (child_node_id.IsValid() && child_node_id.IsNode()) - { - JPH_ASSERT(top < cStackSize); - node_stack[top] = child_node_id; - top++; - } - - // Mark node to be freed - mAllocator->AddObjectToBatch(free_batch, node_idx); - --top; - } - while (top >= 0); - } - - // Now free all nodes - mAllocator->DestructObjectBatch(free_batch); -} - -uint32 QuadTree::AllocateNode(bool inIsChanged) -{ - uint32 index = mAllocator->ConstructObject(inIsChanged); - if (index == Allocator::cInvalidObjectIndex) - { - Trace("QuadTree: Out of nodes!"); - std::abort(); - } - return index; -} - -void QuadTree::Init(Allocator &inAllocator) -{ - // Store allocator - mAllocator = &inAllocator; - - // Allocate root node - mRootNode[mRootNodeIndex].mIndex = AllocateNode(false); -} - -void QuadTree::DiscardOldTree() -{ - // Check if there is an old tree - RootNode &old_root_node = mRootNode[mRootNodeIndex ^ 1]; - if (old_root_node.mIndex != cInvalidNodeIndex) - { - // Clear the root - old_root_node.mIndex = cInvalidNodeIndex; - - // Now free all old nodes - mAllocator->DestructObjectBatch(mFreeNodeBatch); - - // Clear the batch - mFreeNodeBatch = Allocator::Batch(); - } -} - -AABox QuadTree::GetBounds() const -{ - uint32 node_idx = GetCurrentRoot().mIndex; - JPH_ASSERT(node_idx != cInvalidNodeIndex); - const Node &node = mAllocator->Get(node_idx); - - AABox bounds; - node.GetNodeBounds(bounds); - return bounds; -} - -void QuadTree::UpdatePrepare(const BodyVector &inBodies, TrackingVector &ioTracking, UpdateState &outUpdateState, bool inFullRebuild) -{ -#ifdef JPH_ENABLE_ASSERTS - // We only read positions - BodyAccess::Grant grant(BodyAccess::EAccess::None, BodyAccess::EAccess::Read); -#endif - - // Assert we have no nodes pending deletion, this means DiscardOldTree wasn't called yet - JPH_ASSERT(mFreeNodeBatch.mNumObjects == 0); - - // Mark tree non-dirty - mIsDirty = false; - - // Get the current root node - const RootNode &root_node = GetCurrentRoot(); - -#ifdef JPH_DUMP_BROADPHASE_TREE - DumpTree(root_node.GetNodeID(), StringFormat("%s_PRE", mName).c_str()); -#endif - - // Assert sane data -#ifdef _DEBUG - ValidateTree(inBodies, ioTracking, root_node.mIndex, mNumBodies); -#endif - - // Create space for all body ID's - NodeID *node_ids = new NodeID [mNumBodies]; - NodeID *cur_node_id = node_ids; - - // Collect all bodies - NodeID node_stack[cStackSize]; - node_stack[0] = root_node.GetNodeID(); - JPH_ASSERT(node_stack[0].IsValid()); - int top = 0; - do - { - // Check if node is a body - NodeID node_id = node_stack[top]; - if (node_id.IsBody()) - { - // Validate that we're still in the right layer - #ifdef JPH_ENABLE_ASSERTS - uint32 body_index = node_id.GetBodyID().GetIndex(); - JPH_ASSERT(ioTracking[body_index].mObjectLayer == inBodies[body_index]->GetObjectLayer()); - #endif - - // Store body - *cur_node_id = node_id; - ++cur_node_id; - } - else - { - // Process normal node - uint32 node_idx = node_id.GetNodeIndex(); - const Node &node = mAllocator->Get(node_idx); - - if (!node.mIsChanged && !inFullRebuild) - { - // Node is unchanged, treat it as a whole - *cur_node_id = node_id; - ++cur_node_id; - } - else - { - // Node is changed, recurse and get all children - for (NodeID child_node_id : node.mChildNodeID) - if (child_node_id.IsValid()) - { - if (top < cStackSize) - { - node_stack[top] = child_node_id; - top++; - } - else - { - JPH_ASSERT(false); // Out of stack space, this must be a very deep tree. Are you batch adding bodies to the broadphase? - - // Falling back to adding the node as a whole - *cur_node_id = child_node_id; - ++cur_node_id; - } - } - - // Mark node to be freed - mAllocator->AddObjectToBatch(mFreeNodeBatch, node_idx); - } - } - --top; - } - while (top >= 0); - - // Check that our book keeping matches - uint32 num_node_ids = uint32(cur_node_id - node_ids); - JPH_ASSERT(inFullRebuild? num_node_ids == mNumBodies : num_node_ids <= mNumBodies); - - // This will be the new root node id - NodeID root_node_id; - - if (num_node_ids > 0) - { - // We mark the first 5 levels (max 1024 nodes) of the newly built tree as 'changed' so that - // those nodes get recreated every time when we rebuild the tree. This balances the amount of - // time we spend on rebuilding the tree ('unchanged' nodes will be put in the new tree as a whole) - // vs the quality of the built tree. - constexpr uint cMaxDepthMarkChanged = 5; - - // Build new tree - AABox root_bounds; - root_node_id = BuildTree(inBodies, ioTracking, node_ids, num_node_ids, cMaxDepthMarkChanged, root_bounds); - - if (root_node_id.IsBody()) - { - // For a single body we need to allocate a new root node - uint32 root_idx = AllocateNode(false); - Node &root = mAllocator->Get(root_idx); - root.SetChildBounds(0, root_bounds); - root.mChildNodeID[0] = root_node_id; - SetBodyLocation(ioTracking, root_node_id.GetBodyID(), root_idx, 0); - root_node_id = NodeID::sFromNodeIndex(root_idx); - } - } - else - { - // Empty tree, create root node - uint32 root_idx = AllocateNode(false); - root_node_id = NodeID::sFromNodeIndex(root_idx); - } - - // Delete temporary data - delete [] node_ids; - - outUpdateState.mRootNodeID = root_node_id; -} - -void QuadTree::UpdateFinalize([[maybe_unused]] const BodyVector &inBodies, [[maybe_unused]] const TrackingVector &inTracking, const UpdateState &inUpdateState) -{ - // Tree building is complete, now we switch the old with the new tree - uint32 new_root_idx = mRootNodeIndex ^ 1; - RootNode &new_root_node = mRootNode[new_root_idx]; - { - // Note: We don't need to lock here as the old tree stays available so any queries - // that use it can continue using it until DiscardOldTree is called. This slot - // should be empty and unused at this moment. - JPH_ASSERT(new_root_node.mIndex == cInvalidNodeIndex); - new_root_node.mIndex = inUpdateState.mRootNodeID.GetNodeIndex(); - } - - // All queries that start from now on will use this new tree - mRootNodeIndex = new_root_idx; - -#ifdef JPH_DUMP_BROADPHASE_TREE - DumpTree(new_root_node.GetNodeID(), StringFormat("%s_POST", mName).c_str()); -#endif - -#ifdef _DEBUG - ValidateTree(inBodies, inTracking, new_root_node.mIndex, mNumBodies); -#endif -} - -void QuadTree::sPartition(NodeID *ioNodeIDs, Vec3 *ioNodeCenters, int inNumber, int &outMidPoint) -{ - // Handle trivial case - if (inNumber <= 4) - { - outMidPoint = inNumber / 2; - return; - } - - // Calculate bounding box of box centers - Vec3 center_min = Vec3::sReplicate(cLargeFloat); - Vec3 center_max = Vec3::sReplicate(-cLargeFloat); - for (const Vec3 *c = ioNodeCenters, *c_end = ioNodeCenters + inNumber; c < c_end; ++c) - { - Vec3 center = *c; - center_min = Vec3::sMin(center_min, center); - center_max = Vec3::sMax(center_max, center); - } - - // Calculate split plane - int dimension = (center_max - center_min).GetHighestComponentIndex(); - float split = 0.5f * (center_min + center_max)[dimension]; - - // Divide bodies - int start = 0, end = inNumber; - while (start < end) - { - // Search for first element that is on the right hand side of the split plane - while (start < end && ioNodeCenters[start][dimension] < split) - ++start; - - // Search for the first element that is on the left hand side of the split plane - while (start < end && ioNodeCenters[end - 1][dimension] >= split) - --end; - - if (start < end) - { - // Swap the two elements - swap(ioNodeIDs[start], ioNodeIDs[end - 1]); - swap(ioNodeCenters[start], ioNodeCenters[end - 1]); - ++start; - --end; - } - } - JPH_ASSERT(start == end); - - if (start > 0 && start < inNumber) - { - // Success! - outMidPoint = start; - } - else - { - // Failed to divide bodies - outMidPoint = inNumber / 2; - } -} - -void QuadTree::sPartition4(NodeID *ioNodeIDs, Vec3 *ioNodeCenters, int inBegin, int inEnd, int *outSplit) -{ - NodeID *node_ids = ioNodeIDs + inBegin; - Vec3 *node_centers = ioNodeCenters + inBegin; - int number = inEnd - inBegin; - - // Partition entire range - sPartition(node_ids, node_centers, number, outSplit[2]); - - // Partition lower half - sPartition(node_ids, node_centers, outSplit[2], outSplit[1]); - - // Partition upper half - sPartition(node_ids + outSplit[2], node_centers + outSplit[2], number - outSplit[2], outSplit[3]); - - // Convert to proper range - outSplit[0] = inBegin; - outSplit[1] += inBegin; - outSplit[2] += inBegin; - outSplit[3] += outSplit[2]; - outSplit[4] = inEnd; -} - -AABox QuadTree::GetNodeOrBodyBounds(const BodyVector &inBodies, NodeID inNodeID) const -{ - if (inNodeID.IsNode()) - { - // It is a node - uint32 node_idx = inNodeID.GetNodeIndex(); - const Node &node = mAllocator->Get(node_idx); - - AABox bounds; - node.GetNodeBounds(bounds); - return bounds; - } - else - { - // It is a body - return inBodies[inNodeID.GetBodyID().GetIndex()]->GetWorldSpaceBounds(); - } -} - -QuadTree::NodeID QuadTree::BuildTree(const BodyVector &inBodies, TrackingVector &ioTracking, NodeID *ioNodeIDs, int inNumber, uint inMaxDepthMarkChanged, AABox &outBounds) -{ - // Trivial case: No bodies in tree - if (inNumber == 0) - { - outBounds = cInvalidBounds; - return NodeID::sInvalid(); - } - - // Trivial case: When we have 1 body or node, return it - if (inNumber == 1) - { - if (ioNodeIDs->IsNode()) - { - // When returning an existing node as root, ensure that no parent has been set - Node &node = mAllocator->Get(ioNodeIDs->GetNodeIndex()); - node.mParentNodeIndex = cInvalidNodeIndex; - } - outBounds = GetNodeOrBodyBounds(inBodies, *ioNodeIDs); - return *ioNodeIDs; - } - - // Calculate centers of all bodies that are to be inserted - Vec3 *centers = new Vec3 [inNumber]; - JPH_ASSERT(IsAligned(centers, JPH_VECTOR_ALIGNMENT)); - Vec3 *c = centers; - for (const NodeID *n = ioNodeIDs, *n_end = ioNodeIDs + inNumber; n < n_end; ++n, ++c) - *c = GetNodeOrBodyBounds(inBodies, *n).GetCenter(); - - // The algorithm is a recursive tree build, but to avoid the call overhead we keep track of a stack here - struct StackEntry - { - uint32 mNodeIdx; // Node index of node that is generated - int mChildIdx; // Index of child that we're currently processing - int mSplit[5]; // Indices where the node ID's have been split to form 4 partitions - uint32 mDepth; // Depth of this node in the tree - Vec3 mNodeBoundsMin; // Bounding box of this node, accumulated while iterating over children - Vec3 mNodeBoundsMax; - }; - static_assert(sizeof(StackEntry) == 64); - StackEntry stack[cStackSize / 4]; // We don't process 4 at a time in this loop but 1, so the stack can be 4x as small - int top = 0; - - // Create root node - stack[0].mNodeIdx = AllocateNode(inMaxDepthMarkChanged > 0); - stack[0].mChildIdx = -1; - stack[0].mDepth = 0; - stack[0].mNodeBoundsMin = Vec3::sReplicate(cLargeFloat); - stack[0].mNodeBoundsMax = Vec3::sReplicate(-cLargeFloat); - sPartition4(ioNodeIDs, centers, 0, inNumber, stack[0].mSplit); - - for (;;) - { - StackEntry &cur_stack = stack[top]; - - // Next child - cur_stack.mChildIdx++; - - // Check if all children processed - if (cur_stack.mChildIdx >= 4) - { - // Terminate if there's nothing left to pop - if (top <= 0) - break; - - // Add our bounds to our parents bounds - StackEntry &prev_stack = stack[top - 1]; - prev_stack.mNodeBoundsMin = Vec3::sMin(prev_stack.mNodeBoundsMin, cur_stack.mNodeBoundsMin); - prev_stack.mNodeBoundsMax = Vec3::sMax(prev_stack.mNodeBoundsMax, cur_stack.mNodeBoundsMax); - - // Store parent node - Node &node = mAllocator->Get(cur_stack.mNodeIdx); - node.mParentNodeIndex = prev_stack.mNodeIdx; - - // Store this node's properties in the parent node - Node &parent_node = mAllocator->Get(prev_stack.mNodeIdx); - parent_node.mChildNodeID[prev_stack.mChildIdx] = NodeID::sFromNodeIndex(cur_stack.mNodeIdx); - parent_node.SetChildBounds(prev_stack.mChildIdx, AABox(cur_stack.mNodeBoundsMin, cur_stack.mNodeBoundsMax)); - - // Pop entry from stack - --top; - } - else - { - // Get low and high index to bodies to process - int low = cur_stack.mSplit[cur_stack.mChildIdx]; - int high = cur_stack.mSplit[cur_stack.mChildIdx + 1]; - int num_bodies = high - low; - - if (num_bodies == 1) - { - // Get body info - NodeID child_node_id = ioNodeIDs[low]; - AABox bounds = GetNodeOrBodyBounds(inBodies, child_node_id); - - // Update node - Node &node = mAllocator->Get(cur_stack.mNodeIdx); - node.mChildNodeID[cur_stack.mChildIdx] = child_node_id; - node.SetChildBounds(cur_stack.mChildIdx, bounds); - - if (child_node_id.IsNode()) - { - // Update parent for this node - Node &child_node = mAllocator->Get(child_node_id.GetNodeIndex()); - child_node.mParentNodeIndex = cur_stack.mNodeIdx; - } - else - { - // Set location in tracking - SetBodyLocation(ioTracking, child_node_id.GetBodyID(), cur_stack.mNodeIdx, cur_stack.mChildIdx); - } - - // Encapsulate bounding box in parent - cur_stack.mNodeBoundsMin = Vec3::sMin(cur_stack.mNodeBoundsMin, bounds.mMin); - cur_stack.mNodeBoundsMax = Vec3::sMax(cur_stack.mNodeBoundsMax, bounds.mMax); - } - else if (num_bodies > 1) - { - // Allocate new node - StackEntry &new_stack = stack[++top]; - JPH_ASSERT(top < cStackSize / 4); - uint32 next_depth = cur_stack.mDepth + 1; - new_stack.mNodeIdx = AllocateNode(inMaxDepthMarkChanged > next_depth); - new_stack.mChildIdx = -1; - new_stack.mDepth = next_depth; - new_stack.mNodeBoundsMin = Vec3::sReplicate(cLargeFloat); - new_stack.mNodeBoundsMax = Vec3::sReplicate(-cLargeFloat); - sPartition4(ioNodeIDs, centers, low, high, new_stack.mSplit); - } - } - } - - // Delete temporary data - delete [] centers; - - // Store bounding box of root - outBounds.mMin = stack[0].mNodeBoundsMin; - outBounds.mMax = stack[0].mNodeBoundsMax; - - // Return root - return NodeID::sFromNodeIndex(stack[0].mNodeIdx); -} - -void QuadTree::MarkNodeAndParentsChanged(uint32 inNodeIndex) -{ - uint32 node_idx = inNodeIndex; - - do - { - // If node has changed, parent will be too - Node &node = mAllocator->Get(node_idx); - if (node.mIsChanged) - break; - - // Mark node as changed - node.mIsChanged = true; - - // Get our parent - node_idx = node.mParentNodeIndex; - } - while (node_idx != cInvalidNodeIndex); -} - -void QuadTree::WidenAndMarkNodeAndParentsChanged(uint32 inNodeIndex, const AABox &inNewBounds) -{ - uint32 node_idx = inNodeIndex; - - for (;;) - { - // Mark node as changed - Node &node = mAllocator->Get(node_idx); - node.mIsChanged = true; - - // Get our parent - uint32 parent_idx = node.mParentNodeIndex; - if (parent_idx == cInvalidNodeIndex) - break; - - // Find which child of the parent we're in - Node &parent_node = mAllocator->Get(parent_idx); - NodeID node_id = NodeID::sFromNodeIndex(node_idx); - int child_idx = -1; - for (int i = 0; i < 4; ++i) - if (parent_node.mChildNodeID[i] == node_id) - { - // Found one, set the node index and child index and update the bounding box too - child_idx = i; - break; - } - JPH_ASSERT(child_idx != -1, "Nodes don't get removed from the tree, we must have found it"); - - // To avoid any race conditions with other threads we only enlarge bounding boxes - if (!parent_node.EncapsulateChildBounds(child_idx, inNewBounds)) - { - // No changes to bounding box, only marking as changed remains to be done - if (!parent_node.mIsChanged) - MarkNodeAndParentsChanged(parent_idx); - break; - } - - // Update node index - node_idx = parent_idx; - } -} - -bool QuadTree::TryInsertLeaf(TrackingVector &ioTracking, int inNodeIndex, NodeID inLeafID, const AABox &inLeafBounds, int inLeafNumBodies) -{ - // Tentively assign the node as parent - bool leaf_is_node = inLeafID.IsNode(); - if (leaf_is_node) - { - uint32 leaf_idx = inLeafID.GetNodeIndex(); - mAllocator->Get(leaf_idx).mParentNodeIndex = inNodeIndex; - } - - // Fetch node that we're adding to - Node &node = mAllocator->Get(inNodeIndex); - - // Find an empty child - for (uint32 child_idx = 0; child_idx < 4; ++child_idx) - if (node.mChildNodeID[child_idx].CompareExchange(NodeID::sInvalid(), inLeafID)) // Check if we can claim it - { - // We managed to add it to the node - - // If leaf was a body, we need to update its bookkeeping - if (!leaf_is_node) - SetBodyLocation(ioTracking, inLeafID.GetBodyID(), inNodeIndex, child_idx); - - // Now set the bounding box making the child valid for queries - node.SetChildBounds(child_idx, inLeafBounds); - - // Widen the bounds for our parents too - WidenAndMarkNodeAndParentsChanged(inNodeIndex, inLeafBounds); - - // Update body counter - mNumBodies += inLeafNumBodies; - - // And we're done - return true; - } - - return false; -} - -bool QuadTree::TryCreateNewRoot(TrackingVector &ioTracking, atomic &ioRootNodeIndex, NodeID inLeafID, const AABox &inLeafBounds, int inLeafNumBodies) -{ - // Fetch old root - uint32 root_idx = ioRootNodeIndex; - Node &root = mAllocator->Get(root_idx); - - // Create new root, mark this new root as changed as we're not creating a very efficient tree at this point - uint32 new_root_idx = AllocateNode(true); - Node &new_root = mAllocator->Get(new_root_idx); - - // First child is current root, note that since the tree may be modified concurrently we cannot assume that the bounds of our child will be correct so we set a very large bounding box - new_root.mChildNodeID[0] = NodeID::sFromNodeIndex(root_idx); - new_root.SetChildBounds(0, AABox(Vec3::sReplicate(-cLargeFloat), Vec3::sReplicate(cLargeFloat))); - - // Second child is new leaf - new_root.mChildNodeID[1] = inLeafID; - new_root.SetChildBounds(1, inLeafBounds); - - // Tentatively assign new root as parent - bool leaf_is_node = inLeafID.IsNode(); - if (leaf_is_node) - { - uint32 leaf_idx = inLeafID.GetNodeIndex(); - mAllocator->Get(leaf_idx).mParentNodeIndex = new_root_idx; - } - - // Try to swap it - if (ioRootNodeIndex.compare_exchange_strong(root_idx, new_root_idx)) - { - // We managed to set the new root - - // If leaf was a body, we need to update its bookkeeping - if (!leaf_is_node) - SetBodyLocation(ioTracking, inLeafID.GetBodyID(), new_root_idx, 1); - - // Store parent node for old root - root.mParentNodeIndex = new_root_idx; - - // Update body counter - mNumBodies += inLeafNumBodies; - - // And we're done - return true; - } - - // Failed to swap, someone else must have created a new root, try again - mAllocator->DestructObject(new_root_idx); - return false; -} - -void QuadTree::AddBodiesPrepare(const BodyVector &inBodies, TrackingVector &ioTracking, BodyID *ioBodyIDs, int inNumber, AddState &outState) -{ - // Assert sane input - JPH_ASSERT(ioBodyIDs != nullptr); - JPH_ASSERT(inNumber > 0); - -#ifdef JPH_ENABLE_ASSERTS - // Below we just cast the body ID's to node ID's, check here that that is valid - for (const BodyID *b = ioBodyIDs, *b_end = ioBodyIDs + inNumber; b < b_end; ++b) - NodeID::sFromBodyID(*b); -#endif - - // Build subtree for the new bodies, note that we mark all nodes as 'not changed' - // so they will stay together as a batch and will make the tree rebuild cheaper - outState.mLeafID = BuildTree(inBodies, ioTracking, (NodeID *)ioBodyIDs, inNumber, 0, outState.mLeafBounds); - -#ifdef _DEBUG - if (outState.mLeafID.IsNode()) - ValidateTree(inBodies, ioTracking, outState.mLeafID.GetNodeIndex(), inNumber); -#endif -} - -void QuadTree::AddBodiesFinalize(TrackingVector &ioTracking, int inNumberBodies, const AddState &inState) -{ - // Assert sane input - JPH_ASSERT(inNumberBodies > 0); - - // Mark tree dirty - mIsDirty = true; - - // Get the current root node - RootNode &root_node = GetCurrentRoot(); - - for (;;) - { - // Check if we can insert the body in the root - if (TryInsertLeaf(ioTracking, root_node.mIndex, inState.mLeafID, inState.mLeafBounds, inNumberBodies)) - return; - - // Check if we can create a new root - if (TryCreateNewRoot(ioTracking, root_node.mIndex, inState.mLeafID, inState.mLeafBounds, inNumberBodies)) - return; - } -} - -void QuadTree::AddBodiesAbort(TrackingVector &ioTracking, const AddState &inState) -{ - // Collect all bodies - Allocator::Batch free_batch; - NodeID node_stack[cStackSize]; - node_stack[0] = inState.mLeafID; - JPH_ASSERT(node_stack[0].IsValid()); - int top = 0; - do - { - // Check if node is a body - NodeID child_node_id = node_stack[top]; - if (child_node_id.IsBody()) - { - // Reset location of body - sInvalidateBodyLocation(ioTracking, child_node_id.GetBodyID()); - } - else - { - // Process normal node - uint32 node_idx = child_node_id.GetNodeIndex(); - const Node &node = mAllocator->Get(node_idx); - for (NodeID sub_child_node_id : node.mChildNodeID) - if (sub_child_node_id.IsValid()) - { - JPH_ASSERT(top < cStackSize); - node_stack[top] = sub_child_node_id; - top++; - } - - // Mark it to be freed - mAllocator->AddObjectToBatch(free_batch, node_idx); - } - --top; - } - while (top >= 0); - - // Now free all nodes as a single batch - mAllocator->DestructObjectBatch(free_batch); -} - -void QuadTree::RemoveBodies([[maybe_unused]] const BodyVector &inBodies, TrackingVector &ioTracking, const BodyID *ioBodyIDs, int inNumber) -{ - // Assert sane input - JPH_ASSERT(ioBodyIDs != nullptr); - JPH_ASSERT(inNumber > 0); - - // Mark tree dirty - mIsDirty = true; - - for (const BodyID *cur = ioBodyIDs, *end = ioBodyIDs + inNumber; cur < end; ++cur) - { - // Check if BodyID is correct - JPH_ASSERT(inBodies[cur->GetIndex()]->GetID() == *cur, "Provided BodyID doesn't match BodyID in body manager"); - - // Get location of body - uint32 node_idx, child_idx; - GetBodyLocation(ioTracking, *cur, node_idx, child_idx); - - // First we reset our internal bookkeeping - sInvalidateBodyLocation(ioTracking, *cur); - - // Then we make the bounding box invalid, no queries can find this node anymore - Node &node = mAllocator->Get(node_idx); - node.InvalidateChildBounds(child_idx); - - // Finally we reset the child id, this makes the node available for adds again - node.mChildNodeID[child_idx] = NodeID::sInvalid(); - - // We don't need to bubble up our bounding box changes to our parents since we never make volumes smaller, only bigger - // But we do need to mark the nodes as changed so that the tree can be rebuilt - MarkNodeAndParentsChanged(node_idx); - } - - mNumBodies -= inNumber; -} - -void QuadTree::NotifyBodiesAABBChanged(const BodyVector &inBodies, const TrackingVector &inTracking, const BodyID *ioBodyIDs, int inNumber) -{ - // Assert sane input - JPH_ASSERT(ioBodyIDs != nullptr); - JPH_ASSERT(inNumber > 0); - - for (const BodyID *cur = ioBodyIDs, *end = ioBodyIDs + inNumber; cur < end; ++cur) - { - // Check if BodyID is correct - const Body *body = inBodies[cur->GetIndex()]; - JPH_ASSERT(body->GetID() == *cur, "Provided BodyID doesn't match BodyID in body manager"); - - // Get the new bounding box - const AABox &new_bounds = body->GetWorldSpaceBounds(); - - // Get location of body - uint32 node_idx, child_idx; - GetBodyLocation(inTracking, *cur, node_idx, child_idx); - - // Widen bounds for node - Node &node = mAllocator->Get(node_idx); - if (node.EncapsulateChildBounds(child_idx, new_bounds)) - { - // Mark tree dirty - mIsDirty = true; - - // If bounds changed, widen the bounds for our parents too - WidenAndMarkNodeAndParentsChanged(node_idx, new_bounds); - } - } -} - -template -JPH_INLINE void QuadTree::WalkTree(const ObjectLayerFilter &inObjectLayerFilter, const TrackingVector &inTracking, Visitor &ioVisitor JPH_IF_TRACK_BROADPHASE_STATS(, LayerToStats &ioStats)) const -{ - // Get the root - const RootNode &root_node = GetCurrentRoot(); - -#ifdef JPH_TRACK_BROADPHASE_STATS - // Start tracking stats - int bodies_visited = 0; - int hits_collected = 0; - int nodes_visited = 0; - uint64 collector_ticks = 0; - - uint64 start = GetProcessorTickCount(); -#endif // JPH_TRACK_BROADPHASE_STATS - - NodeID node_stack[cStackSize]; - node_stack[0] = root_node.GetNodeID(); - int top = 0; - do - { - // Check if node is a body - NodeID child_node_id = node_stack[top]; - if (child_node_id.IsBody()) - { - // Track amount of bodies visited - JPH_IF_TRACK_BROADPHASE_STATS(++bodies_visited;) - - BodyID body_id = child_node_id.GetBodyID(); - ObjectLayer object_layer = inTracking[body_id.GetIndex()].mObjectLayer; // We're not taking a lock on the body, so it may be in the process of being removed so check if the object layer is invalid - if (object_layer != cObjectLayerInvalid && inObjectLayerFilter.ShouldCollide(object_layer)) - { - JPH_PROFILE("VisitBody"); - - // Track amount of hits - JPH_IF_TRACK_BROADPHASE_STATS(++hits_collected;) - - // Start track time the collector takes - JPH_IF_TRACK_BROADPHASE_STATS(uint64 collector_start = GetProcessorTickCount();) - - // We found a body we collide with, call our visitor - ioVisitor.VisitBody(body_id, top); - - // End track time the collector takes - JPH_IF_TRACK_BROADPHASE_STATS(collector_ticks += GetProcessorTickCount() - collector_start;) - - // Check if we're done - if (ioVisitor.ShouldAbort()) - break; - } - } - else if (child_node_id.IsValid()) - { - JPH_IF_TRACK_BROADPHASE_STATS(++nodes_visited;) - - // Check if stack can hold more nodes - if (top + 4 < cStackSize) - { - // Process normal node - const Node &node = mAllocator->Get(child_node_id.GetNodeIndex()); - JPH_ASSERT(IsAligned(&node, JPH_CACHE_LINE_SIZE)); - - // Load bounds of 4 children - Vec4 bounds_minx = Vec4::sLoadFloat4Aligned((const Float4 *)&node.mBoundsMinX); - Vec4 bounds_miny = Vec4::sLoadFloat4Aligned((const Float4 *)&node.mBoundsMinY); - Vec4 bounds_minz = Vec4::sLoadFloat4Aligned((const Float4 *)&node.mBoundsMinZ); - Vec4 bounds_maxx = Vec4::sLoadFloat4Aligned((const Float4 *)&node.mBoundsMaxX); - Vec4 bounds_maxy = Vec4::sLoadFloat4Aligned((const Float4 *)&node.mBoundsMaxY); - Vec4 bounds_maxz = Vec4::sLoadFloat4Aligned((const Float4 *)&node.mBoundsMaxZ); - - // Load ids for 4 children - UVec4 child_ids = UVec4::sLoadInt4Aligned((const uint32 *)&node.mChildNodeID[0]); - - // Check which sub nodes to visit - int num_results = ioVisitor.VisitNodes(bounds_minx, bounds_miny, bounds_minz, bounds_maxx, bounds_maxy, bounds_maxz, child_ids, top); - child_ids.StoreInt4((uint32 *)&node_stack[top]); - top += num_results; - } - else - JPH_ASSERT(false, "Stack full!"); - } - - // Fetch next node until we find one that the visitor wants to see - do - --top; - while (top >= 0 && !ioVisitor.ShouldVisitNode(top)); - } - while (top >= 0); - -#ifdef JPH_TRACK_BROADPHASE_STATS - // Calculate total time the broadphase walk took - uint64 total_ticks = GetProcessorTickCount() - start; - - // Update stats under lock protection (slow!) - { - unique_lock lock(mStatsMutex); - Stat &s = ioStats[inObjectLayerFilter.GetDescription()]; - s.mNumQueries++; - s.mNodesVisited += nodes_visited; - s.mBodiesVisited += bodies_visited; - s.mHitsReported += hits_collected; - s.mTotalTicks += total_ticks; - s.mCollectorTicks += collector_ticks; - } -#endif // JPH_TRACK_BROADPHASE_STATS -} - -void QuadTree::CastRay(const RayCast &inRay, RayCastBodyCollector &ioCollector, const ObjectLayerFilter &inObjectLayerFilter, const TrackingVector &inTracking) const -{ - class Visitor - { - public: - /// Constructor - JPH_INLINE Visitor(const RayCast &inRay, RayCastBodyCollector &ioCollector) : - mOrigin(inRay.mOrigin), - mInvDirection(inRay.mDirection), - mCollector(ioCollector) - { - mFractionStack[0] = -1; - } - - /// Returns true if further processing of the tree should be aborted - JPH_INLINE bool ShouldAbort() const - { - return mCollector.ShouldEarlyOut(); - } - - /// Returns true if this node / body should be visited, false if no hit can be generated - JPH_INLINE bool ShouldVisitNode(int inStackTop) const - { - return mFractionStack[inStackTop] < mCollector.GetEarlyOutFraction(); - } - - /// Visit nodes, returns number of hits found and sorts ioChildNodeIDs so that they are at the beginning of the vector. - JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioChildNodeIDs, int inStackTop) - { - // Test the ray against 4 bounding boxes - Vec4 fraction = RayAABox4(mOrigin, mInvDirection, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ); - - // Sort so that highest values are first (we want to first process closer hits and we process stack top to bottom) - return SortReverseAndStore(fraction, mCollector.GetEarlyOutFraction(), ioChildNodeIDs, &mFractionStack[inStackTop]); - } - - /// Visit a body, returns false if the algorithm should terminate because no hits can be generated anymore - JPH_INLINE void VisitBody(const BodyID &inBodyID, int inStackTop) - { - // Store potential hit with body - BroadPhaseCastResult result { inBodyID, mFractionStack[inStackTop] }; - mCollector.AddHit(result); - } - - private: - Vec3 mOrigin; - RayInvDirection mInvDirection; - RayCastBodyCollector & mCollector; - float mFractionStack[cStackSize]; - }; - - Visitor visitor(inRay, ioCollector); - WalkTree(inObjectLayerFilter, inTracking, visitor JPH_IF_TRACK_BROADPHASE_STATS(, mCastRayStats)); -} - -void QuadTree::CollideAABox(const AABox &inBox, CollideShapeBodyCollector &ioCollector, const ObjectLayerFilter &inObjectLayerFilter, const TrackingVector &inTracking) const -{ - class Visitor - { - public: - /// Constructor - JPH_INLINE Visitor(const AABox &inBox, CollideShapeBodyCollector &ioCollector) : - mBox(inBox), - mCollector(ioCollector) - { - } - - /// Returns true if further processing of the tree should be aborted - JPH_INLINE bool ShouldAbort() const - { - return mCollector.ShouldEarlyOut(); - } - - /// Returns true if this node / body should be visited, false if no hit can be generated - JPH_INLINE bool ShouldVisitNode(int inStackTop) const - { - return true; - } - - /// Visit nodes, returns number of hits found and sorts ioChildNodeIDs so that they are at the beginning of the vector. - JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioChildNodeIDs, int inStackTop) const - { - // Test the box vs 4 boxes - UVec4 hitting = AABox4VsBox(mBox, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ); - return CountAndSortTrues(hitting, ioChildNodeIDs); - } - - /// Visit a body, returns false if the algorithm should terminate because no hits can be generated anymore - JPH_INLINE void VisitBody(const BodyID &inBodyID, int inStackTop) - { - // Store potential hit with body - mCollector.AddHit(inBodyID); - } - - private: - const AABox & mBox; - CollideShapeBodyCollector & mCollector; - }; - - Visitor visitor(inBox, ioCollector); - WalkTree(inObjectLayerFilter, inTracking, visitor JPH_IF_TRACK_BROADPHASE_STATS(, mCollideAABoxStats)); -} - -void QuadTree::CollideSphere(Vec3Arg inCenter, float inRadius, CollideShapeBodyCollector &ioCollector, const ObjectLayerFilter &inObjectLayerFilter, const TrackingVector &inTracking) const -{ - class Visitor - { - public: - /// Constructor - JPH_INLINE Visitor(Vec3Arg inCenter, float inRadius, CollideShapeBodyCollector &ioCollector) : - mCenterX(inCenter.SplatX()), - mCenterY(inCenter.SplatY()), - mCenterZ(inCenter.SplatZ()), - mRadiusSq(Vec4::sReplicate(Square(inRadius))), - mCollector(ioCollector) - { - } - - /// Returns true if further processing of the tree should be aborted - JPH_INLINE bool ShouldAbort() const - { - return mCollector.ShouldEarlyOut(); - } - - /// Returns true if this node / body should be visited, false if no hit can be generated - JPH_INLINE bool ShouldVisitNode(int inStackTop) const - { - return true; - } - - /// Visit nodes, returns number of hits found and sorts ioChildNodeIDs so that they are at the beginning of the vector. - JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioChildNodeIDs, int inStackTop) const - { - // Test 4 boxes vs sphere - UVec4 hitting = AABox4VsSphere(mCenterX, mCenterY, mCenterZ, mRadiusSq, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ); - return CountAndSortTrues(hitting, ioChildNodeIDs); - } - - /// Visit a body, returns false if the algorithm should terminate because no hits can be generated anymore - JPH_INLINE void VisitBody(const BodyID &inBodyID, int inStackTop) - { - // Store potential hit with body - mCollector.AddHit(inBodyID); - } - - private: - Vec4 mCenterX; - Vec4 mCenterY; - Vec4 mCenterZ; - Vec4 mRadiusSq; - CollideShapeBodyCollector & mCollector; - }; - - Visitor visitor(inCenter, inRadius, ioCollector); - WalkTree(inObjectLayerFilter, inTracking, visitor JPH_IF_TRACK_BROADPHASE_STATS(, mCollideSphereStats)); -} - -void QuadTree::CollidePoint(Vec3Arg inPoint, CollideShapeBodyCollector &ioCollector, const ObjectLayerFilter &inObjectLayerFilter, const TrackingVector &inTracking) const -{ - class Visitor - { - public: - /// Constructor - JPH_INLINE Visitor(Vec3Arg inPoint, CollideShapeBodyCollector &ioCollector) : - mPoint(inPoint), - mCollector(ioCollector) - { - } - - /// Returns true if further processing of the tree should be aborted - JPH_INLINE bool ShouldAbort() const - { - return mCollector.ShouldEarlyOut(); - } - - /// Returns true if this node / body should be visited, false if no hit can be generated - JPH_INLINE bool ShouldVisitNode(int inStackTop) const - { - return true; - } - - /// Visit nodes, returns number of hits found and sorts ioChildNodeIDs so that they are at the beginning of the vector. - JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioChildNodeIDs, int inStackTop) const - { - // Test if point overlaps with box - UVec4 hitting = AABox4VsPoint(mPoint, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ); - return CountAndSortTrues(hitting, ioChildNodeIDs); - } - - /// Visit a body, returns false if the algorithm should terminate because no hits can be generated anymore - JPH_INLINE void VisitBody(const BodyID &inBodyID, int inStackTop) - { - // Store potential hit with body - mCollector.AddHit(inBodyID); - } - - private: - Vec3 mPoint; - CollideShapeBodyCollector & mCollector; - }; - - Visitor visitor(inPoint, ioCollector); - WalkTree(inObjectLayerFilter, inTracking, visitor JPH_IF_TRACK_BROADPHASE_STATS(, mCollidePointStats)); -} - -void QuadTree::CollideOrientedBox(const OrientedBox &inBox, CollideShapeBodyCollector &ioCollector, const ObjectLayerFilter &inObjectLayerFilter, const TrackingVector &inTracking) const -{ - class Visitor - { - public: - /// Constructor - JPH_INLINE Visitor(const OrientedBox &inBox, CollideShapeBodyCollector &ioCollector) : - mBox(inBox), - mCollector(ioCollector) - { - } - - /// Returns true if further processing of the tree should be aborted - JPH_INLINE bool ShouldAbort() const - { - return mCollector.ShouldEarlyOut(); - } - - /// Returns true if this node / body should be visited, false if no hit can be generated - JPH_INLINE bool ShouldVisitNode(int inStackTop) const - { - return true; - } - - /// Visit nodes, returns number of hits found and sorts ioChildNodeIDs so that they are at the beginning of the vector. - JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioChildNodeIDs, int inStackTop) const - { - // Test if point overlaps with box - UVec4 hitting = AABox4VsBox(mBox, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ); - return CountAndSortTrues(hitting, ioChildNodeIDs); - } - - /// Visit a body, returns false if the algorithm should terminate because no hits can be generated anymore - JPH_INLINE void VisitBody(const BodyID &inBodyID, int inStackTop) - { - // Store potential hit with body - mCollector.AddHit(inBodyID); - } - - private: - OrientedBox mBox; - CollideShapeBodyCollector & mCollector; - }; - - Visitor visitor(inBox, ioCollector); - WalkTree(inObjectLayerFilter, inTracking, visitor JPH_IF_TRACK_BROADPHASE_STATS(, mCollideOrientedBoxStats)); -} - -void QuadTree::CastAABox(const AABoxCast &inBox, CastShapeBodyCollector &ioCollector, const ObjectLayerFilter &inObjectLayerFilter, const TrackingVector &inTracking) const -{ - class Visitor - { - public: - /// Constructor - JPH_INLINE Visitor(const AABoxCast &inBox, CastShapeBodyCollector &ioCollector) : - mOrigin(inBox.mBox.GetCenter()), - mExtent(inBox.mBox.GetExtent()), - mInvDirection(inBox.mDirection), - mCollector(ioCollector) - { - mFractionStack[0] = -1; - } - - /// Returns true if further processing of the tree should be aborted - JPH_INLINE bool ShouldAbort() const - { - return mCollector.ShouldEarlyOut(); - } - - /// Returns true if this node / body should be visited, false if no hit can be generated - JPH_INLINE bool ShouldVisitNode(int inStackTop) const - { - return mFractionStack[inStackTop] < mCollector.GetPositiveEarlyOutFraction(); - } - - /// Visit nodes, returns number of hits found and sorts ioChildNodeIDs so that they are at the beginning of the vector. - JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioChildNodeIDs, int inStackTop) - { - // Enlarge them by the casted aabox extents - Vec4 bounds_min_x = inBoundsMinX, bounds_min_y = inBoundsMinY, bounds_min_z = inBoundsMinZ, bounds_max_x = inBoundsMaxX, bounds_max_y = inBoundsMaxY, bounds_max_z = inBoundsMaxZ; - AABox4EnlargeWithExtent(mExtent, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z); - - // Test 4 children - Vec4 fraction = RayAABox4(mOrigin, mInvDirection, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z); - - // Sort so that highest values are first (we want to first process closer hits and we process stack top to bottom) - return SortReverseAndStore(fraction, mCollector.GetPositiveEarlyOutFraction(), ioChildNodeIDs, &mFractionStack[inStackTop]); - } - - /// Visit a body, returns false if the algorithm should terminate because no hits can be generated anymore - JPH_INLINE void VisitBody(const BodyID &inBodyID, int inStackTop) - { - // Store potential hit with body - BroadPhaseCastResult result { inBodyID, mFractionStack[inStackTop] }; - mCollector.AddHit(result); - } - - private: - Vec3 mOrigin; - Vec3 mExtent; - RayInvDirection mInvDirection; - CastShapeBodyCollector & mCollector; - float mFractionStack[cStackSize]; - }; - - Visitor visitor(inBox, ioCollector); - WalkTree(inObjectLayerFilter, inTracking, visitor JPH_IF_TRACK_BROADPHASE_STATS(, mCastAABoxStats)); -} - -void QuadTree::FindCollidingPairs(const BodyVector &inBodies, const BodyID *inActiveBodies, int inNumActiveBodies, float inSpeculativeContactDistance, BodyPairCollector &ioPairCollector, const ObjectLayerPairFilter &inObjectLayerPairFilter) const -{ - // Note that we don't lock the tree at this point. We know that the tree is not going to be swapped or deleted while finding collision pairs due to the way the jobs are scheduled in the PhysicsSystem::Update. - // We double check this at the end of the function. - const RootNode &root_node = GetCurrentRoot(); - JPH_ASSERT(root_node.mIndex != cInvalidNodeIndex); - - // Assert sane input - JPH_ASSERT(inActiveBodies != nullptr); - JPH_ASSERT(inNumActiveBodies > 0); - - NodeID node_stack[cStackSize]; - - // Loop over all active bodies - for (int b1 = 0; b1 < inNumActiveBodies; ++b1) - { - BodyID b1_id = inActiveBodies[b1]; - const Body &body1 = *inBodies[b1_id.GetIndex()]; - JPH_ASSERT(!body1.IsStatic()); - - // Expand the bounding box by the speculative contact distance - AABox bounds1 = body1.GetWorldSpaceBounds(); - bounds1.ExpandBy(Vec3::sReplicate(inSpeculativeContactDistance)); - - // Test each body with the tree - node_stack[0] = root_node.GetNodeID(); - int top = 0; - do - { - // Check if node is a body - NodeID child_node_id = node_stack[top]; - if (child_node_id.IsBody()) - { - // Don't collide with self - BodyID b2_id = child_node_id.GetBodyID(); - if (b1_id != b2_id) - { - // Collision between dynamic pairs need to be picked up only once - const Body &body2 = *inBodies[b2_id.GetIndex()]; - if (inObjectLayerPairFilter.ShouldCollide(body1.GetObjectLayer(), body2.GetObjectLayer()) - && Body::sFindCollidingPairsCanCollide(body1, body2) - && bounds1.Overlaps(body2.GetWorldSpaceBounds())) // In the broadphase we widen the bounding box when a body moves, do a final check to see if the bounding boxes actually overlap - { - // Store potential hit between bodies - ioPairCollector.AddHit({ b1_id, b2_id }); - } - } - } - else if (child_node_id.IsValid()) - { - // Process normal node - const Node &node = mAllocator->Get(child_node_id.GetNodeIndex()); - JPH_ASSERT(IsAligned(&node, JPH_CACHE_LINE_SIZE)); - - // Get bounds of 4 children - Vec4 bounds_minx = Vec4::sLoadFloat4Aligned((const Float4 *)&node.mBoundsMinX); - Vec4 bounds_miny = Vec4::sLoadFloat4Aligned((const Float4 *)&node.mBoundsMinY); - Vec4 bounds_minz = Vec4::sLoadFloat4Aligned((const Float4 *)&node.mBoundsMinZ); - Vec4 bounds_maxx = Vec4::sLoadFloat4Aligned((const Float4 *)&node.mBoundsMaxX); - Vec4 bounds_maxy = Vec4::sLoadFloat4Aligned((const Float4 *)&node.mBoundsMaxY); - Vec4 bounds_maxz = Vec4::sLoadFloat4Aligned((const Float4 *)&node.mBoundsMaxZ); - - // Test overlap - UVec4 overlap = AABox4VsBox(bounds1, bounds_minx, bounds_miny, bounds_minz, bounds_maxx, bounds_maxy, bounds_maxz); - int num_results = overlap.CountTrues(); - if (num_results > 0) - { - // Load ids for 4 children - UVec4 child_ids = UVec4::sLoadInt4Aligned((const uint32 *)&node.mChildNodeID[0]); - - // Sort so that overlaps are first - child_ids = UVec4::sSort4True(overlap, child_ids); - - // Push them onto the stack - if (top + 4 < cStackSize) - { - child_ids.StoreInt4((uint32 *)&node_stack[top]); - top += num_results; - } - else - JPH_ASSERT(false, "Stack full!"); - } - } - --top; - } - while (top >= 0); - } - - // Test that the root node was not swapped while finding collision pairs. - // This would mean that UpdateFinalize/DiscardOldTree ran during collision detection which should not be possible due to the way the jobs are scheduled. - JPH_ASSERT(root_node.mIndex != cInvalidNodeIndex); - JPH_ASSERT(&root_node == &GetCurrentRoot()); -} - -#ifdef _DEBUG - -void QuadTree::ValidateTree(const BodyVector &inBodies, const TrackingVector &inTracking, uint32 inNodeIndex, uint32 inNumExpectedBodies) const -{ - JPH_PROFILE_FUNCTION(); - - // Root should be valid - JPH_ASSERT(inNodeIndex != cInvalidNodeIndex); - - // To avoid call overhead, create a stack in place - struct StackEntry - { - uint32 mNodeIndex; - uint32 mParentNodeIndex; - }; - StackEntry stack[cStackSize]; - stack[0].mNodeIndex = inNodeIndex; - stack[0].mParentNodeIndex = cInvalidNodeIndex; - int top = 0; - - uint32 num_bodies = 0; - - do - { - // Copy entry from the stack - StackEntry cur_stack = stack[top]; - - // Validate parent - const Node &node = mAllocator->Get(cur_stack.mNodeIndex); - JPH_ASSERT(node.mParentNodeIndex == cur_stack.mParentNodeIndex); - - // Validate that when a parent is not-changed that all of its children are also - JPH_ASSERT(cur_stack.mParentNodeIndex == cInvalidNodeIndex || mAllocator->Get(cur_stack.mParentNodeIndex).mIsChanged || !node.mIsChanged); - - // Loop children - for (uint32 i = 0; i < 4; ++i) - { - NodeID child_node_id = node.mChildNodeID[i]; - if (child_node_id.IsValid()) - { - if (child_node_id.IsNode()) - { - // Child is a node, recurse - uint32 child_idx = child_node_id.GetNodeIndex(); - JPH_ASSERT(top < cStackSize); - StackEntry &new_entry = stack[top++]; - new_entry.mNodeIndex = child_idx; - new_entry.mParentNodeIndex = cur_stack.mNodeIndex; - - // Validate that the bounding box is bigger or equal to the bounds in the tree - // Bounding box could also be invalid if all children of our child were removed - AABox child_bounds; - node.GetChildBounds(i, child_bounds); - AABox real_child_bounds; - mAllocator->Get(child_idx).GetNodeBounds(real_child_bounds); - JPH_ASSERT(child_bounds.Contains(real_child_bounds) || !real_child_bounds.IsValid()); - } - else - { - // Increment number of bodies found - ++num_bodies; - - // Check if tracker matches position of body - uint32 node_idx, child_idx; - GetBodyLocation(inTracking, child_node_id.GetBodyID(), node_idx, child_idx); - JPH_ASSERT(node_idx == cur_stack.mNodeIndex); - JPH_ASSERT(child_idx == i); - - // Validate that the body bounds are bigger or equal to the bounds in the tree - AABox body_bounds; - node.GetChildBounds(i, body_bounds); - const Body *body = inBodies[child_node_id.GetBodyID().GetIndex()]; - AABox cached_body_bounds = body->GetWorldSpaceBounds(); - AABox real_body_bounds = body->GetShape()->GetWorldSpaceBounds(body->GetCenterOfMassTransform(), Vec3::sReplicate(1.0f)); - JPH_ASSERT(cached_body_bounds == real_body_bounds); // Check that cached body bounds are up to date - JPH_ASSERT(body_bounds.Contains(real_body_bounds)); - } - } - } - --top; - } - while (top >= 0); - - // Check that the amount of bodies in the tree matches our counter - JPH_ASSERT(num_bodies == inNumExpectedBodies); -} - -#endif - -#ifdef JPH_DUMP_BROADPHASE_TREE - -void QuadTree::DumpTree(const NodeID &inRoot, const char *inFileNamePrefix) const -{ - // Open DOT file - std::ofstream f; - f.open(StringFormat("%s.dot", inFileNamePrefix).c_str(), std::ofstream::out | std::ofstream::trunc); - if (!f.is_open()) - return; - - // Write header - f << "digraph {\n"; - - // Iterate the entire tree - NodeID node_stack[cStackSize]; - node_stack[0] = inRoot; - JPH_ASSERT(node_stack[0].IsValid()); - int top = 0; - do - { - // Check if node is a body - NodeID node_id = node_stack[top]; - if (node_id.IsBody()) - { - // Output body - String body_id = ConvertToString(node_id.GetBodyID().GetIndex()); - f << "body" << body_id << "[label = \"Body " << body_id << "\"]\n"; - } - else - { - // Process normal node - uint32 node_idx = node_id.GetNodeIndex(); - const Node &node = mAllocator->Get(node_idx); - - // Get bounding box - AABox bounds; - node.GetNodeBounds(bounds); - - // Output node - String node_str = ConvertToString(node_idx); - f << "node" << node_str << "[label = \"Node " << node_str << "\nVolume: " << ConvertToString(bounds.GetVolume()) << "\" color=" << (node.mIsChanged? "red" : "black") << "]\n"; - - // Recurse and get all children - for (NodeID child_node_id : node.mChildNodeID) - if (child_node_id.IsValid()) - { - JPH_ASSERT(top < cStackSize); - node_stack[top] = child_node_id; - top++; - - // Output link - f << "node" << node_str << " -> "; - if (child_node_id.IsBody()) - f << "body" << ConvertToString(child_node_id.GetBodyID().GetIndex()); - else - f << "node" << ConvertToString(child_node_id.GetNodeIndex()); - f << "\n"; - } - } - --top; - } - while (top >= 0); - - // Finish DOT file - f << "}\n"; - f.close(); - - // Convert to svg file - String cmd = StringFormat("dot %s.dot -Tsvg -o %s.svg", inFileNamePrefix, inFileNamePrefix); - system(cmd.c_str()); -} - -#endif // JPH_DUMP_BROADPHASE_TREE - -#ifdef JPH_TRACK_BROADPHASE_STATS - -uint64 QuadTree::GetTicks100Pct(const LayerToStats &inLayer) const -{ - uint64 total_ticks = 0; - for (const LayerToStats::value_type &kv : inLayer) - total_ticks += kv.second.mTotalTicks; - return total_ticks; -} - -void QuadTree::ReportStats(const char *inName, const LayerToStats &inLayer, uint64 inTicks100Pct) const -{ - for (const LayerToStats::value_type &kv : inLayer) - { - double total_pct = 100.0 * double(kv.second.mTotalTicks) / double(inTicks100Pct); - double total_pct_excl_collector = 100.0 * double(kv.second.mTotalTicks - kv.second.mCollectorTicks) / double(inTicks100Pct); - double hits_reported_vs_bodies_visited = kv.second.mBodiesVisited > 0? 100.0 * double(kv.second.mHitsReported) / double(kv.second.mBodiesVisited) : 100.0; - double hits_reported_vs_nodes_visited = kv.second.mNodesVisited > 0? double(kv.second.mHitsReported) / double(kv.second.mNodesVisited) : -1.0; - - std::stringstream str; - str << inName << ", " << kv.first << ", " << mName << ", " << kv.second.mNumQueries << ", " << total_pct << ", " << total_pct_excl_collector << ", " << kv.second.mNodesVisited << ", " << kv.second.mBodiesVisited << ", " << kv.second.mHitsReported << ", " << hits_reported_vs_bodies_visited << ", " << hits_reported_vs_nodes_visited; - Trace(str.str().c_str()); - } -} - -uint64 QuadTree::GetTicks100Pct() const -{ - uint64 total_ticks = 0; - total_ticks += GetTicks100Pct(mCastRayStats); - total_ticks += GetTicks100Pct(mCollideAABoxStats); - total_ticks += GetTicks100Pct(mCollideSphereStats); - total_ticks += GetTicks100Pct(mCollidePointStats); - total_ticks += GetTicks100Pct(mCollideOrientedBoxStats); - total_ticks += GetTicks100Pct(mCastAABoxStats); - return total_ticks; -} - -void QuadTree::ReportStats(uint64 inTicks100Pct) const -{ - unique_lock lock(mStatsMutex); - ReportStats("RayCast", mCastRayStats, inTicks100Pct); - ReportStats("CollideAABox", mCollideAABoxStats, inTicks100Pct); - ReportStats("CollideSphere", mCollideSphereStats, inTicks100Pct); - ReportStats("CollidePoint", mCollidePointStats, inTicks100Pct); - ReportStats("CollideOrientedBox", mCollideOrientedBoxStats, inTicks100Pct); - ReportStats("CastAABox", mCastAABoxStats, inTicks100Pct); -} - -#endif // JPH_TRACK_BROADPHASE_STATS - -uint QuadTree::GetMaxTreeDepth(const NodeID &inNodeID) const -{ - // Reached a leaf? - if (!inNodeID.IsValid() || inNodeID.IsBody()) - return 0; - - // Recurse to children - uint max_depth = 0; - const Node &node = mAllocator->Get(inNodeID.GetNodeIndex()); - for (NodeID child_node_id : node.mChildNodeID) - max_depth = max(max_depth, GetMaxTreeDepth(child_node_id)); - return max_depth + 1; -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/QuadTree.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/QuadTree.h deleted file mode 100644 index 053ea4f7a39..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/BroadPhase/QuadTree.h +++ /dev/null @@ -1,388 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include -#include -#include - -//#define JPH_DUMP_BROADPHASE_TREE - -JPH_NAMESPACE_BEGIN - -/// Internal tree structure in broadphase, is essentially a quad AABB tree. -/// Tree is lockless (except for UpdatePrepare/Finalize() function), modifying objects in the tree will widen the aabbs of parent nodes to make the node fit. -/// During the UpdatePrepare/Finalize() call the tree is rebuilt to achieve a tight fit again. -class JPH_EXPORT QuadTree : public NonCopyable -{ -public: - JPH_OVERRIDE_NEW_DELETE - -private: - // Forward declare - class AtomicNodeID; - - /// Class that points to either a body or a node in the tree - class NodeID - { - public: - JPH_OVERRIDE_NEW_DELETE - - /// Default constructor does not initialize - inline NodeID() = default; - - /// Construct a node ID - static inline NodeID sInvalid() { return NodeID(cInvalidNodeIndex); } - static inline NodeID sFromBodyID(BodyID inID) { NodeID node_id(inID.GetIndexAndSequenceNumber()); JPH_ASSERT(node_id.IsBody()); return node_id; } - static inline NodeID sFromNodeIndex(uint32 inIdx) { NodeID node_id(inIdx | cIsNode); JPH_ASSERT(node_id.IsNode()); return node_id; } - - /// Check what type of ID it is - inline bool IsValid() const { return mID != cInvalidNodeIndex; } - inline bool IsBody() const { return (mID & cIsNode) == 0; } - inline bool IsNode() const { return (mID & cIsNode) != 0; } - - /// Get body or node index - inline BodyID GetBodyID() const { JPH_ASSERT(IsBody()); return BodyID(mID); } - inline uint32 GetNodeIndex() const { JPH_ASSERT(IsNode()); return mID & ~cIsNode; } - - /// Comparison - inline bool operator == (const BodyID &inRHS) const { return mID == inRHS.GetIndexAndSequenceNumber(); } - inline bool operator == (const NodeID &inRHS) const { return mID == inRHS.mID; } - - private: - friend class AtomicNodeID; - - inline explicit NodeID(uint32 inID) : mID(inID) { } - - static const uint32 cIsNode = BodyID::cBroadPhaseBit; ///< If this bit is set it means that the ID refers to a node, otherwise it refers to a body - - uint32 mID; - }; - - static_assert(sizeof(NodeID) == sizeof(BodyID), "Body id's should have the same size as NodeIDs"); - - /// A NodeID that uses atomics to store the value - class AtomicNodeID - { - public: - /// Constructor - AtomicNodeID() = default; - explicit AtomicNodeID(const NodeID &inRHS) : mID(inRHS.mID) { } - - /// Assignment - inline void operator = (const NodeID &inRHS) { mID = inRHS.mID; } - - /// Getting the value - inline operator NodeID () const { return NodeID(mID); } - - /// Check if the ID is valid - inline bool IsValid() const { return mID != cInvalidNodeIndex; } - - /// Comparison - inline bool operator == (const BodyID &inRHS) const { return mID == inRHS.GetIndexAndSequenceNumber(); } - inline bool operator == (const NodeID &inRHS) const { return mID == inRHS.mID; } - - /// Atomically compare and swap value. Expects inOld value, replaces with inNew value or returns false - inline bool CompareExchange(NodeID inOld, NodeID inNew) { return mID.compare_exchange_strong(inOld.mID, inNew.mID); } - - private: - atomic mID; - }; - - /// Class that represents a node in the tree - class Node - { - public: - /// Construct node - explicit Node(bool inIsChanged); - - /// Get bounding box encapsulating all children - void GetNodeBounds(AABox &outBounds) const; - - /// Get bounding box in a consistent way with the functions below (check outBounds.IsValid() before using the box) - void GetChildBounds(int inChildIndex, AABox &outBounds) const; - - /// Set the bounds in such a way that other threads will either see a fully correct bounding box or a bounding box with no volume - void SetChildBounds(int inChildIndex, const AABox &inBounds); - - /// Invalidate bounding box in such a way that other threads will not temporarily see a very large bounding box - void InvalidateChildBounds(int inChildIndex); - - /// Encapsulate inBounds in node bounds, returns true if there were changes - bool EncapsulateChildBounds(int inChildIndex, const AABox &inBounds); - - /// Bounding box for child nodes or bodies (all initially set to invalid so no collision test will ever traverse to the leaf) - atomic mBoundsMinX[4]; - atomic mBoundsMinY[4]; - atomic mBoundsMinZ[4]; - atomic mBoundsMaxX[4]; - atomic mBoundsMaxY[4]; - atomic mBoundsMaxZ[4]; - - /// Index of child node or body ID. - AtomicNodeID mChildNodeID[4]; - - /// Index of the parent node. - /// Note: This value is unreliable during the UpdatePrepare/Finalize() function as a node may be relinked to the newly built tree. - atomic mParentNodeIndex = cInvalidNodeIndex; - - /// If this part of the tree has changed, if not, we will treat this sub tree as a single body during the UpdatePrepare/Finalize(). - /// If any changes are made to an object inside this sub tree then the direct path from the body to the top of the tree will become changed. - atomic mIsChanged; - - // Padding to align to 124 bytes - uint32 mPadding = 0; - }; - - // Maximum size of the stack during tree walk - static constexpr int cStackSize = 128; - - static_assert(sizeof(atomic) == 4, "Assuming that an atomic doesn't add any additional storage"); - static_assert(sizeof(atomic) == 4, "Assuming that an atomic doesn't add any additional storage"); - static_assert(is_trivially_destructible(), "Assuming that we don't have a destructor"); - -public: - /// Class that allocates tree nodes, can be shared between multiple trees - using Allocator = FixedSizeFreeList; - - static_assert(Allocator::ObjectStorageSize == 128, "Node should be 128 bytes"); - - /// Data to track location of a Body in the tree - struct Tracking - { - /// Constructor to satisfy the vector class - Tracking() = default; - Tracking(const Tracking &inRHS) : mBroadPhaseLayer(inRHS.mBroadPhaseLayer.load()), mObjectLayer(inRHS.mObjectLayer.load()), mBodyLocation(inRHS.mBodyLocation.load()) { } - - /// Invalid body location identifier - static const uint32 cInvalidBodyLocation = 0xffffffff; - - atomic mBroadPhaseLayer = (BroadPhaseLayer::Type)cBroadPhaseLayerInvalid; - atomic mObjectLayer = cObjectLayerInvalid; - atomic mBodyLocation { cInvalidBodyLocation }; - }; - - using TrackingVector = Array; - - /// Destructor - ~QuadTree(); - -#if defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED) - /// Name of the tree for debugging purposes - void SetName(const char *inName) { mName = inName; } - inline const char * GetName() const { return mName; } -#endif // JPH_EXTERNAL_PROFILE || JPH_PROFILE_ENABLED - - /// Check if there is anything in the tree - inline bool HasBodies() const { return mNumBodies != 0; } - - /// Check if the tree needs an UpdatePrepare/Finalize() - inline bool IsDirty() const { return mIsDirty; } - - /// Check if this tree can get an UpdatePrepare/Finalize() or if it needs a DiscardOldTree() first - inline bool CanBeUpdated() const { return mFreeNodeBatch.mNumObjects == 0; } - - /// Initialization - void Init(Allocator &inAllocator); - - struct UpdateState - { - NodeID mRootNodeID; ///< This will be the new root node id - }; - - /// Will throw away the previous frame's nodes so that we can start building a new tree in the background - void DiscardOldTree(); - - /// Get the bounding box for this tree - AABox GetBounds() const; - - /// Update the broadphase, needs to be called regularly to achieve a tight fit of the tree when bodies have been modified. - /// UpdatePrepare() will build the tree, UpdateFinalize() will lock the root of the tree shortly and swap the trees and afterwards clean up temporary data structures. - void UpdatePrepare(const BodyVector &inBodies, TrackingVector &ioTracking, UpdateState &outUpdateState, bool inFullRebuild); - void UpdateFinalize(const BodyVector &inBodies, const TrackingVector &inTracking, const UpdateState &inUpdateState); - - /// Temporary data structure to pass information between AddBodiesPrepare and AddBodiesFinalize/Abort - struct AddState - { - NodeID mLeafID = NodeID::sInvalid(); - AABox mLeafBounds; - }; - - /// Prepare adding inNumber bodies at ioBodyIDs to the quad tree, returns the state in outState that should be used in AddBodiesFinalize. - /// This can be done on a background thread without influencing the broadphase. - /// ioBodyIDs may be shuffled around by this function. - void AddBodiesPrepare(const BodyVector &inBodies, TrackingVector &ioTracking, BodyID *ioBodyIDs, int inNumber, AddState &outState); - - /// Finalize adding bodies to the quadtree, supply the same number of bodies as in AddBodiesPrepare. - void AddBodiesFinalize(TrackingVector &ioTracking, int inNumberBodies, const AddState &inState); - - /// Abort adding bodies to the quadtree, supply the same bodies and state as in AddBodiesPrepare. - /// This can be done on a background thread without influencing the broadphase. - void AddBodiesAbort(TrackingVector &ioTracking, const AddState &inState); - - /// Remove inNumber bodies in ioBodyIDs from the quadtree. - void RemoveBodies(const BodyVector &inBodies, TrackingVector &ioTracking, const BodyID *ioBodyIDs, int inNumber); - - /// Call whenever the aabb of a body changes. - void NotifyBodiesAABBChanged(const BodyVector &inBodies, const TrackingVector &inTracking, const BodyID *ioBodyIDs, int inNumber); - - /// Cast a ray and get the intersecting bodies in ioCollector. - void CastRay(const RayCast &inRay, RayCastBodyCollector &ioCollector, const ObjectLayerFilter &inObjectLayerFilter, const TrackingVector &inTracking) const; - - /// Get bodies intersecting with inBox in ioCollector - void CollideAABox(const AABox &inBox, CollideShapeBodyCollector &ioCollector, const ObjectLayerFilter &inObjectLayerFilter, const TrackingVector &inTracking) const; - - /// Get bodies intersecting with a sphere in ioCollector - void CollideSphere(Vec3Arg inCenter, float inRadius, CollideShapeBodyCollector &ioCollector, const ObjectLayerFilter &inObjectLayerFilter, const TrackingVector &inTracking) const; - - /// Get bodies intersecting with a point and any hits to ioCollector - void CollidePoint(Vec3Arg inPoint, CollideShapeBodyCollector &ioCollector, const ObjectLayerFilter &inObjectLayerFilter, const TrackingVector &inTracking) const; - - /// Get bodies intersecting with an oriented box and any hits to ioCollector - void CollideOrientedBox(const OrientedBox &inBox, CollideShapeBodyCollector &ioCollector, const ObjectLayerFilter &inObjectLayerFilter, const TrackingVector &inTracking) const; - - /// Cast a box and get intersecting bodies in ioCollector - void CastAABox(const AABoxCast &inBox, CastShapeBodyCollector &ioCollector, const ObjectLayerFilter &inObjectLayerFilter, const TrackingVector &inTracking) const; - - /// Find all colliding pairs between dynamic bodies, calls ioPairCollector for every pair found - void FindCollidingPairs(const BodyVector &inBodies, const BodyID *inActiveBodies, int inNumActiveBodies, float inSpeculativeContactDistance, BodyPairCollector &ioPairCollector, const ObjectLayerPairFilter &inObjectLayerPairFilter) const; - -#ifdef JPH_TRACK_BROADPHASE_STATS - /// Sum up all the ticks spent in the various layers - uint64 GetTicks100Pct() const; - - /// Trace the stats of this tree to the TTY - void ReportStats(uint64 inTicks100Pct) const; -#endif // JPH_TRACK_BROADPHASE_STATS - -private: - /// Constants - static const uint32 cInvalidNodeIndex = 0xffffffff; ///< Value used to indicate node index is invalid - static const float cLargeFloat; ///< A large floating point number that is small enough to not cause any overflows - static const AABox cInvalidBounds; ///< Invalid bounding box using cLargeFloat - - /// We alternate between two trees in order to let collision queries complete in parallel to adding/removing objects to the tree - struct RootNode - { - /// Get the ID of the root node - inline NodeID GetNodeID() const { return NodeID::sFromNodeIndex(mIndex); } - - /// Index of the root node of the tree (this is always a node, never a body id) - atomic mIndex { cInvalidNodeIndex }; - }; - - /// Caches location of body inBodyID in the tracker, body can be found in mNodes[inNodeIdx].mChildNodeID[inChildIdx] - void GetBodyLocation(const TrackingVector &inTracking, BodyID inBodyID, uint32 &outNodeIdx, uint32 &outChildIdx) const; - void SetBodyLocation(TrackingVector &ioTracking, BodyID inBodyID, uint32 inNodeIdx, uint32 inChildIdx) const; - static void sInvalidateBodyLocation(TrackingVector &ioTracking, BodyID inBodyID); - - /// Get the current root of the tree - JPH_INLINE const RootNode & GetCurrentRoot() const { return mRootNode[mRootNodeIndex]; } - JPH_INLINE RootNode & GetCurrentRoot() { return mRootNode[mRootNodeIndex]; } - - /// Depending on if inNodeID is a body or tree node return the bounding box - inline AABox GetNodeOrBodyBounds(const BodyVector &inBodies, NodeID inNodeID) const; - - /// Mark node and all of its parents as changed - inline void MarkNodeAndParentsChanged(uint32 inNodeIndex); - - /// Widen parent bounds of node inNodeIndex to encapsulate inNewBounds, also mark node and all of its parents as changed - inline void WidenAndMarkNodeAndParentsChanged(uint32 inNodeIndex, const AABox &inNewBounds); - - /// Allocate a new node - inline uint32 AllocateNode(bool inIsChanged); - - /// Try to insert a new leaf to the tree at inNodeIndex - inline bool TryInsertLeaf(TrackingVector &ioTracking, int inNodeIndex, NodeID inLeafID, const AABox &inLeafBounds, int inLeafNumBodies); - - /// Try to replace the existing root with a new root that contains both the existing root and the new leaf - inline bool TryCreateNewRoot(TrackingVector &ioTracking, atomic &ioRootNodeIndex, NodeID inLeafID, const AABox &inLeafBounds, int inLeafNumBodies); - - /// Build a tree for ioBodyIDs, returns the NodeID of the root (which will be the ID of a single body if inNumber = 1). All tree levels up to inMaxDepthMarkChanged will be marked as 'changed'. - NodeID BuildTree(const BodyVector &inBodies, TrackingVector &ioTracking, NodeID *ioNodeIDs, int inNumber, uint inMaxDepthMarkChanged, AABox &outBounds); - - /// Sorts ioNodeIDs spatially into 2 groups. Second groups starts at ioNodeIDs + outMidPoint. - /// After the function returns ioNodeIDs and ioNodeCenters will be shuffled - static void sPartition(NodeID *ioNodeIDs, Vec3 *ioNodeCenters, int inNumber, int &outMidPoint); - - /// Sorts ioNodeIDs from inBegin to (but excluding) inEnd spatially into 4 groups. - /// outSplit needs to be 5 ints long, when the function returns each group runs from outSplit[i] to (but excluding) outSplit[i + 1] - /// After the function returns ioNodeIDs and ioNodeCenters will be shuffled - static void sPartition4(NodeID *ioNodeIDs, Vec3 *ioNodeCenters, int inBegin, int inEnd, int *outSplit); - -#ifdef _DEBUG - /// Validate that the tree is consistent. - /// Note: This function only works if the tree is not modified while we're traversing it. - void ValidateTree(const BodyVector &inBodies, const TrackingVector &inTracking, uint32 inNodeIndex, uint32 inNumExpectedBodies) const; -#endif - -#ifdef JPH_DUMP_BROADPHASE_TREE - /// Dump the tree in DOT format (see: https://graphviz.org/) - void DumpTree(const NodeID &inRoot, const char *inFileNamePrefix) const; -#endif - -#ifdef JPH_TRACK_BROADPHASE_STATS - /// Mutex protecting the various LayerToStats members - mutable Mutex mStatsMutex; - - struct Stat - { - uint64 mNumQueries = 0; - uint64 mNodesVisited = 0; - uint64 mBodiesVisited = 0; - uint64 mHitsReported = 0; - uint64 mTotalTicks = 0; - uint64 mCollectorTicks = 0; - }; - - using LayerToStats = UnorderedMap; - - /// Sum up all the ticks in a layer - uint64 GetTicks100Pct(const LayerToStats &inLayer) const; - - /// Trace the stats of a single query type to the TTY - void ReportStats(const char *inName, const LayerToStats &inLayer, uint64 inTicks100Pct) const; - - mutable LayerToStats mCastRayStats; - mutable LayerToStats mCollideAABoxStats; - mutable LayerToStats mCollideSphereStats; - mutable LayerToStats mCollidePointStats; - mutable LayerToStats mCollideOrientedBoxStats; - mutable LayerToStats mCastAABoxStats; -#endif // JPH_TRACK_BROADPHASE_STATS - - /// Debug function to get the depth of the tree from node inNodeID - uint GetMaxTreeDepth(const NodeID &inNodeID) const; - - /// Walk the node tree calling the Visitor::VisitNodes for each node encountered and Visitor::VisitBody for each body encountered - template - JPH_INLINE void WalkTree(const ObjectLayerFilter &inObjectLayerFilter, const TrackingVector &inTracking, Visitor &ioVisitor JPH_IF_TRACK_BROADPHASE_STATS(, LayerToStats &ioStats)) const; - -#if defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED) - /// Name of this tree for debugging purposes - const char * mName = "Layer"; -#endif // JPH_EXTERNAL_PROFILE || JPH_PROFILE_ENABLED - - /// Number of bodies currently in the tree - atomic mNumBodies { 0 }; - - /// We alternate between two tree root nodes. When updating, we activate the new tree and we keep the old tree alive. - /// for queries that are in progress until the next time DiscardOldTree() is called. - RootNode mRootNode[2]; - atomic mRootNodeIndex { 0 }; - - /// Allocator that controls adding / freeing nodes - Allocator * mAllocator = nullptr; - - /// This is a list of nodes that must be deleted after the trees are swapped and the old tree is no longer in use - Allocator::Batch mFreeNodeBatch; - - /// Flag to keep track of changes to the broadphase, if false, we don't need to UpdatePrepare/Finalize() - atomic mIsDirty = false; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CastConvexVsTriangles.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CastConvexVsTriangles.cpp deleted file mode 100644 index a69208a04dd..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CastConvexVsTriangles.cpp +++ /dev/null @@ -1,109 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -CastConvexVsTriangles::CastConvexVsTriangles(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, Vec3Arg inScale, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, CastShapeCollector &ioCollector) : - mShapeCast(inShapeCast), - mShapeCastSettings(inShapeCastSettings), - mCenterOfMassTransform2(inCenterOfMassTransform2), - mScale(inScale), - mSubShapeIDCreator1(inSubShapeIDCreator1), - mCollector(ioCollector) -{ - JPH_ASSERT(inShapeCast.mShape->GetType() == EShapeType::Convex); - - // Determine if shape is inside out or not - mScaleSign = ScaleHelpers::IsInsideOut(inScale)? -1.0f : 1.0f; -} - -void CastConvexVsTriangles::Cast(Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2, uint8 inActiveEdges, const SubShapeID &inSubShapeID2) -{ - JPH_PROFILE_FUNCTION(); - - // Scale triangle - Vec3 v0 = mScale * inV0; - Vec3 v1 = mScale * inV1; - Vec3 v2 = mScale * inV2; - - // Calculate triangle normal - Vec3 triangle_normal = mScaleSign * (v1 - v0).Cross(v2 - v0); - - // Backface check - bool back_facing = triangle_normal.Dot(mShapeCast.mDirection) > 0.0f; - if (mShapeCastSettings.mBackFaceModeTriangles == EBackFaceMode::IgnoreBackFaces && back_facing) - return; - - // Create triangle support function - TriangleConvexSupport triangle { v0, v1, v2 }; - - // Check if we already created the cast shape support function - if (mSupport == nullptr) - { - // Determine if we want to use the actual shape or a shrunken shape with convex radius - ConvexShape::ESupportMode support_mode = mShapeCastSettings.mUseShrunkenShapeAndConvexRadius? ConvexShape::ESupportMode::ExcludeConvexRadius : ConvexShape::ESupportMode::Default; - - // Create support function - mSupport = static_cast(mShapeCast.mShape)->GetSupportFunction(support_mode, mSupportBuffer, mShapeCast.mScale); - } - - EPAPenetrationDepth epa; - float fraction = mCollector.GetEarlyOutFraction(); - Vec3 contact_point_a, contact_point_b, contact_normal; - if (epa.CastShape(mShapeCast.mCenterOfMassStart, mShapeCast.mDirection, mShapeCastSettings.mCollisionTolerance, mShapeCastSettings.mPenetrationTolerance, *mSupport, triangle, mSupport->GetConvexRadius(), 0.0f, mShapeCastSettings.mReturnDeepestPoint, fraction, contact_point_a, contact_point_b, contact_normal)) - { - // Check if we have enabled active edge detection - if (mShapeCastSettings.mActiveEdgeMode == EActiveEdgeMode::CollideOnlyWithActive && inActiveEdges != 0b111) - { - // Convert the active edge velocity hint to local space - Vec3 active_edge_movement_direction = mCenterOfMassTransform2.Multiply3x3Transposed(mShapeCastSettings.mActiveEdgeMovementDirection); - - // Update the contact normal to account for active edges - // Note that we flip the triangle normal as the penetration axis is pointing towards the triangle instead of away - contact_normal = ActiveEdges::FixNormal(v0, v1, v2, back_facing? triangle_normal : -triangle_normal, inActiveEdges, contact_point_b, contact_normal, active_edge_movement_direction); - } - - // Convert to world space - contact_point_a = mCenterOfMassTransform2 * contact_point_a; - contact_point_b = mCenterOfMassTransform2 * contact_point_b; - Vec3 contact_normal_world = mCenterOfMassTransform2.Multiply3x3(contact_normal); - - // Its a hit, store the sub shape id's - ShapeCastResult result(fraction, contact_point_a, contact_point_b, contact_normal_world, back_facing, mSubShapeIDCreator1.GetID(), inSubShapeID2, TransformedShape::sGetBodyID(mCollector.GetContext())); - - // Early out if this hit is deeper than the collector's early out value - if (fraction == 0.0f && -result.mPenetrationDepth >= mCollector.GetEarlyOutFraction()) - return; - - // Gather faces - if (mShapeCastSettings.mCollectFacesMode == ECollectFacesMode::CollectFaces) - { - // Get supporting face of shape 1 - Mat44 transform_1_to_2 = mShapeCast.mCenterOfMassStart; - transform_1_to_2.SetTranslation(transform_1_to_2.GetTranslation() + fraction * mShapeCast.mDirection); - static_cast(mShapeCast.mShape)->GetSupportingFace(SubShapeID(), transform_1_to_2.Multiply3x3Transposed(-contact_normal), mShapeCast.mScale, mCenterOfMassTransform2 * transform_1_to_2, result.mShape1Face); - - // Get face of the triangle - triangle.GetSupportingFace(contact_normal, result.mShape2Face); - - // Convert to world space - for (Vec3 &p : result.mShape2Face) - p = mCenterOfMassTransform2 * p; - } - - JPH_IF_TRACK_NARROWPHASE_STATS(TrackNarrowPhaseCollector track;) - mCollector.AddHit(result); - } -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CastConvexVsTriangles.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CastConvexVsTriangles.h deleted file mode 100644 index 24e3a99b4e8..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CastConvexVsTriangles.h +++ /dev/null @@ -1,46 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include - -JPH_NAMESPACE_BEGIN - -/// Collision detection helper that casts a convex object vs one or more triangles -class JPH_EXPORT CastConvexVsTriangles -{ -public: - /// Constructor - /// @param inShapeCast The shape to cast against the triangles and its start and direction - /// @param inShapeCastSettings Settings for performing the cast - /// @param inScale Local space scale for the shape to cast against. - /// @param inCenterOfMassTransform2 Is the center of mass transform of shape 2 (excluding scale), this is used to provide a transform to the shape cast result so that local quantities can be transformed into world space. - /// @param inSubShapeIDCreator1 Class that tracks the current sub shape ID for the casting shape - /// @param ioCollector The collector that receives the results. - CastConvexVsTriangles(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, Vec3Arg inScale, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, CastShapeCollector &ioCollector); - - /// Cast convex object with a single triangle - /// @param inV0 , inV1 , inV2: CCW triangle vertices - /// @param inActiveEdges bit 0 = edge v0..v1 is active, bit 1 = edge v1..v2 is active, bit 2 = edge v2..v0 is active - /// An active edge is an edge that is not connected to another triangle in such a way that it is impossible to collide with the edge - /// @param inSubShapeID2 The sub shape ID for the triangle - void Cast(Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2, uint8 inActiveEdges, const SubShapeID &inSubShapeID2); - -protected: - const ShapeCast & mShapeCast; - const ShapeCastSettings & mShapeCastSettings; - const Mat44 & mCenterOfMassTransform2; - Vec3 mScale; - SubShapeIDCreator mSubShapeIDCreator1; - CastShapeCollector & mCollector; - -private: - ConvexShape::SupportBuffer mSupportBuffer; ///< Buffer that holds the support function of the cast shape - const ConvexShape::Support * mSupport = nullptr; ///< Support function of the cast shape - float mScaleSign; ///< Sign of the scale, -1 if object is inside out, 1 if not -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CastResult.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CastResult.h deleted file mode 100644 index 6bb49feb7c8..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CastResult.h +++ /dev/null @@ -1,37 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include - -JPH_NAMESPACE_BEGIN - -/// Structure that holds a ray cast or other object cast hit -class BroadPhaseCastResult -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Function required by the CollisionCollector. A smaller fraction is considered to be a 'better hit'. For rays/cast shapes we can just use the collision fraction. - inline float GetEarlyOutFraction() const { return mFraction; } - - /// Reset this result so it can be reused for a new cast. - inline void Reset() { mBodyID = BodyID(); mFraction = 1.0f + FLT_EPSILON; } - - BodyID mBodyID; ///< Body that was hit - float mFraction = 1.0f + FLT_EPSILON; ///< Hit fraction of the ray/object [0, 1], HitPoint = Start + mFraction * (End - Start) -}; - -/// Specialization of cast result against a shape -class RayCastResult : public BroadPhaseCastResult -{ -public: - JPH_OVERRIDE_NEW_DELETE - - SubShapeID mSubShapeID2; ///< Sub shape ID of shape that we collided against -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CastSphereVsTriangles.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CastSphereVsTriangles.cpp deleted file mode 100644 index 166883be8da..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CastSphereVsTriangles.cpp +++ /dev/null @@ -1,223 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -CastSphereVsTriangles::CastSphereVsTriangles(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, Vec3Arg inScale, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, CastShapeCollector &ioCollector) : - mStart(inShapeCast.mCenterOfMassStart.GetTranslation()), - mDirection(inShapeCast.mDirection), - mShapeCastSettings(inShapeCastSettings), - mCenterOfMassTransform2(inCenterOfMassTransform2), - mScale(inScale), - mSubShapeIDCreator1(inSubShapeIDCreator1), - mCollector(ioCollector) -{ - // Cast to sphere shape - JPH_ASSERT(inShapeCast.mShape->GetSubType() == EShapeSubType::Sphere); - const SphereShape *sphere = static_cast(inShapeCast.mShape); - - // Scale the radius - mRadius = sphere->GetRadius() * abs(inShapeCast.mScale.GetX()); - - // Determine if shape is inside out or not - mScaleSign = ScaleHelpers::IsInsideOut(inScale)? -1.0f : 1.0f; -} - -void CastSphereVsTriangles::AddHit(bool inBackFacing, const SubShapeID &inSubShapeID2, float inFraction, Vec3Arg inContactPointA, Vec3Arg inContactPointB, Vec3Arg inContactNormal) -{ - // Convert to world space - Vec3 contact_point_a = mCenterOfMassTransform2 * (mStart + inContactPointA); - Vec3 contact_point_b = mCenterOfMassTransform2 * (mStart + inContactPointB); - Vec3 contact_normal_world = mCenterOfMassTransform2.Multiply3x3(inContactNormal); - - // Its a hit, store the sub shape id's - ShapeCastResult result(inFraction, contact_point_a, contact_point_b, contact_normal_world, inBackFacing, mSubShapeIDCreator1.GetID(), inSubShapeID2, TransformedShape::sGetBodyID(mCollector.GetContext())); - - // Note: We don't gather faces here because that's only useful if both shapes have a face. Since the sphere always has only 1 contact point, the manifold is always a point. - - JPH_IF_TRACK_NARROWPHASE_STATS(TrackNarrowPhaseCollector track;) - mCollector.AddHit(result); -} - -void CastSphereVsTriangles::AddHitWithActiveEdgeDetection(Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2, bool inBackFacing, Vec3Arg inTriangleNormal, uint8 inActiveEdges, const SubShapeID &inSubShapeID2, float inFraction, Vec3Arg inContactPointA, Vec3Arg inContactPointB, Vec3Arg inContactNormal) -{ - // Check if we have enabled active edge detection - Vec3 contact_normal = inContactNormal; - if (mShapeCastSettings.mActiveEdgeMode == EActiveEdgeMode::CollideOnlyWithActive && inActiveEdges != 0b111) - { - // Convert the active edge velocity hint to local space - Vec3 active_edge_movement_direction = mCenterOfMassTransform2.Multiply3x3Transposed(mShapeCastSettings.mActiveEdgeMovementDirection); - - // Update the contact normal to account for active edges - // Note that we flip the triangle normal as the penetration axis is pointing towards the triangle instead of away - contact_normal = ActiveEdges::FixNormal(inV0, inV1, inV2, inBackFacing? inTriangleNormal : -inTriangleNormal, inActiveEdges, inContactPointB, inContactNormal, active_edge_movement_direction); - } - - AddHit(inBackFacing, inSubShapeID2, inFraction, inContactPointA, inContactPointB, contact_normal); -} - -// This is a simplified version of the ray cylinder test from: Real Time Collision Detection - Christer Ericson -// Chapter 5.3.7, page 194-197. Some conditions have been removed as we're not interested in hitting the caps of the cylinder. -// Note that the ray origin is assumed to be the origin here. -float CastSphereVsTriangles::RayCylinder(Vec3Arg inRayDirection, Vec3Arg inCylinderA, Vec3Arg inCylinderB, float inRadius) const -{ - // Calculate cylinder axis - Vec3 axis = inCylinderB - inCylinderA; - - // Make ray start relative to cylinder side A (moving cylinder A to the origin) - Vec3 start = -inCylinderA; - - // Test if segment is fully on the A side of the cylinder - float start_dot_axis = start.Dot(axis); - float direction_dot_axis = inRayDirection.Dot(axis); - float end_dot_axis = start_dot_axis + direction_dot_axis; - if (start_dot_axis < 0.0f && end_dot_axis < 0.0f) - return FLT_MAX; - - // Test if segment is fully on the B side of the cylinder - float axis_len_sq = axis.LengthSq(); - if (start_dot_axis > axis_len_sq && end_dot_axis > axis_len_sq) - return FLT_MAX; - - // Calculate a, b and c, the factors for quadratic equation - // We're basically solving the ray: x = start + direction * t - // The closest point to x on the segment A B is: w = (x . axis) * axis / (axis . axis) - // The distance between x and w should be radius: (x - w) . (x - w) = radius^2 - // Solving this gives the following: - float a = axis_len_sq * inRayDirection.LengthSq() - Square(direction_dot_axis); - if (abs(a) < 1.0e-6f) - return FLT_MAX; // Segment runs parallel to cylinder axis, stop processing, we will either hit at fraction = 0 or we'll hit a vertex - float b = axis_len_sq * start.Dot(inRayDirection) - direction_dot_axis * start_dot_axis; // should be multiplied by 2, instead we'll divide a and c by 2 when we solve the quadratic equation - float c = axis_len_sq * (start.LengthSq() - Square(inRadius)) - Square(start_dot_axis); - float det = Square(b) - a * c; // normally 4 * a * c but since both a and c need to be divided by 2 we lose the 4 - if (det < 0.0f) - return FLT_MAX; // No solution to quadractic equation - - // Solve fraction t where the ray hits the cylinder - float t = -(b + sqrt(det)) / a; // normally divided by 2 * a but since a should be divided by 2 we lose the 2 - if (t < 0.0f || t > 1.0f) - return FLT_MAX; // Intersection lies outside segment - if (start_dot_axis + t * direction_dot_axis < 0.0f || start_dot_axis + t * direction_dot_axis > axis_len_sq) - return FLT_MAX; // Intersection outside the end point of the cylinder, stop processing, we will possibly hit a vertex - return t; -} - -void CastSphereVsTriangles::Cast(Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2, uint8 inActiveEdges, const SubShapeID &inSubShapeID2) -{ - JPH_PROFILE_FUNCTION(); - - // Scale triangle and make it relative to the start of the cast - Vec3 v0 = mScale * inV0 - mStart; - Vec3 v1 = mScale * inV1 - mStart; - Vec3 v2 = mScale * inV2 - mStart; - - // Calculate triangle normal - Vec3 triangle_normal = mScaleSign * (v1 - v0).Cross(v2 - v0); - float triangle_normal_len = triangle_normal.Length(); - if (triangle_normal_len == 0.0f) - return; // Degenerate triangle - triangle_normal /= triangle_normal_len; - - // Backface check - float normal_dot_direction = triangle_normal.Dot(mDirection); - bool back_facing = normal_dot_direction > 0.0f; - if (mShapeCastSettings.mBackFaceModeTriangles == EBackFaceMode::IgnoreBackFaces && back_facing) - return; - - // Test if distance between the sphere and plane of triangle is smaller or equal than the radius - if (abs(v0.Dot(triangle_normal)) <= mRadius) - { - // Check if the sphere intersects at the start of the cast - uint32 closest_feature; - Vec3 q = ClosestPoint::GetClosestPointOnTriangle(v0, v1, v2, closest_feature); - float q_len_sq = q.LengthSq(); - if (q_len_sq <= Square(mRadius)) - { - // Early out if this hit is deeper than the collector's early out value - float q_len = sqrt(q_len_sq); - float penetration_depth = mRadius - q_len; - if (-penetration_depth >= mCollector.GetEarlyOutFraction()) - return; - - // Generate contact point - Vec3 contact_normal = q_len > 0.0f? q / q_len : Vec3::sAxisY(); - Vec3 contact_point_a = q + contact_normal * penetration_depth; - Vec3 contact_point_b = q; - AddHitWithActiveEdgeDetection(v0, v1, v2, back_facing, triangle_normal, inActiveEdges, inSubShapeID2, 0.0f, contact_point_a, contact_point_b, contact_normal); - return; - } - } - else - { - // Check if cast is not parallel to the plane of the triangle - float abs_normal_dot_direction = abs(normal_dot_direction); - if (abs_normal_dot_direction > 1.0e-6f) - { - // Calculate the point on the sphere that will hit the triangle's plane first and calculate a fraction where it will do so - Vec3 d = Sign(normal_dot_direction) * mRadius * triangle_normal; - float plane_intersection = (v0 - d).Dot(triangle_normal) / normal_dot_direction; - - // Check if sphere will hit in the interval that we're interested in - if (plane_intersection * abs_normal_dot_direction < -mRadius // Sphere hits the plane before the sweep, cannot intersect - || plane_intersection >= mCollector.GetEarlyOutFraction()) // Sphere hits the plane after the sweep / early out fraction, cannot intersect - return; - - // We can only report an interior hit if we're hitting the plane during our sweep and not before - if (plane_intersection >= 0.0f) - { - // Calculate the point of contact on the plane - Vec3 p = d + plane_intersection * mDirection; - - // Check if this is an interior point - float u, v, w; - if (ClosestPoint::GetBaryCentricCoordinates(v0 - p, v1 - p, v2 - p, u, v, w) - && u >= 0.0f && v >= 0.0f && w >= 0.0f) - { - // Interior point, we found the collision point. We don't need to check active edges. - AddHit(back_facing, inSubShapeID2, plane_intersection, p, p, back_facing? triangle_normal : -triangle_normal); - return; - } - } - } - } - - // Test 3 edges - float fraction = RayCylinder(mDirection, v0, v1, mRadius); - fraction = min(fraction, RayCylinder(mDirection, v1, v2, mRadius)); - fraction = min(fraction, RayCylinder(mDirection, v2, v0, mRadius)); - - // Test 3 vertices - fraction = min(fraction, RaySphere(Vec3::sZero(), mDirection, v0, mRadius)); - fraction = min(fraction, RaySphere(Vec3::sZero(), mDirection, v1, mRadius)); - fraction = min(fraction, RaySphere(Vec3::sZero(), mDirection, v2, mRadius)); - - // Check if we have a collision - JPH_ASSERT(fraction >= 0.0f); - if (fraction < mCollector.GetEarlyOutFraction()) - { - // Calculate the center of the sphere at the point of contact - Vec3 p = fraction * mDirection; - - // Get contact point and normal - uint32 closest_feature; - Vec3 q = ClosestPoint::GetClosestPointOnTriangle(v0 - p, v1 - p, v2 - p, closest_feature); - Vec3 contact_normal = q.Normalized(); - Vec3 contact_point_ab = p + q; - AddHitWithActiveEdgeDetection(v0, v1, v2, back_facing, triangle_normal, inActiveEdges, inSubShapeID2, fraction, contact_point_ab, contact_point_ab, contact_normal); - } -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CastSphereVsTriangles.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CastSphereVsTriangles.h deleted file mode 100644 index 624e44e5132..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CastSphereVsTriangles.h +++ /dev/null @@ -1,49 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -/// Collision detection helper that casts a sphere vs one or more triangles -class JPH_EXPORT CastSphereVsTriangles -{ -public: - /// Constructor - /// @param inShapeCast The sphere to cast against the triangles and its start and direction - /// @param inShapeCastSettings Settings for performing the cast - /// @param inScale Local space scale for the shape to cast against. - /// @param inCenterOfMassTransform2 Is the center of mass transform of shape 2 (excluding scale), this is used to provide a transform to the shape cast result so that local quantities can be transformed into world space. - /// @param inSubShapeIDCreator1 Class that tracks the current sub shape ID for the casting shape - /// @param ioCollector The collector that receives the results. - CastSphereVsTriangles(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, Vec3Arg inScale, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, CastShapeCollector &ioCollector); - - /// Cast sphere with a single triangle - /// @param inV0 , inV1 , inV2: CCW triangle vertices - /// @param inActiveEdges bit 0 = edge v0..v1 is active, bit 1 = edge v1..v2 is active, bit 2 = edge v2..v0 is active - /// An active edge is an edge that is not connected to another triangle in such a way that it is impossible to collide with the edge - /// @param inSubShapeID2 The sub shape ID for the triangle - void Cast(Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2, uint8 inActiveEdges, const SubShapeID &inSubShapeID2); - -protected: - Vec3 mStart; ///< Starting location of the sphere - Vec3 mDirection; ///< Direction and length of movement of sphere - float mRadius; ///< Scaled radius of sphere - const ShapeCastSettings & mShapeCastSettings; - const Mat44 & mCenterOfMassTransform2; - Vec3 mScale; - SubShapeIDCreator mSubShapeIDCreator1; - CastShapeCollector & mCollector; - -private: - void AddHit(bool inBackFacing, const SubShapeID &inSubShapeID2, float inFraction, Vec3Arg inContactPointA, Vec3Arg inContactPointB, Vec3Arg inContactNormal); - void AddHitWithActiveEdgeDetection(Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2, bool inBackFacing, Vec3Arg inTriangleNormal, uint8 inActiveEdges, const SubShapeID &inSubShapeID2, float inFraction, Vec3Arg inContactPointA, Vec3Arg inContactPointB, Vec3Arg inContactNormal); - float RayCylinder(Vec3Arg inRayDirection, Vec3Arg inCylinderA, Vec3Arg inCylinderB, float inRadius) const; - - float mScaleSign; ///< Sign of the scale, -1 if object is inside out, 1 if not -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollectFacesMode.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollectFacesMode.h deleted file mode 100644 index 4cac6256db1..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollectFacesMode.h +++ /dev/null @@ -1,16 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -JPH_NAMESPACE_BEGIN - -/// Whether or not to collect faces, used by CastShape and CollideShape -enum class ECollectFacesMode : uint8 -{ - CollectFaces, ///< mShape1/2Face is desired - NoFaces ///< mShape1/2Face is not desired -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollideConvexVsTriangles.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollideConvexVsTriangles.cpp deleted file mode 100644 index f00b0023aa9..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollideConvexVsTriangles.cpp +++ /dev/null @@ -1,150 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -CollideConvexVsTriangles::CollideConvexVsTriangles(const ConvexShape *inShape1, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeID &inSubShapeID1, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector) : - mCollideShapeSettings(inCollideShapeSettings), - mCollector(ioCollector), - mShape1(inShape1), - mScale1(inScale1), - mScale2(inScale2), - mTransform1(inCenterOfMassTransform1), - mSubShapeID1(inSubShapeID1) -{ - // Get transforms - Mat44 inverse_transform2 = inCenterOfMassTransform2.InversedRotationTranslation(); - Mat44 transform1_to_2 = inverse_transform2 * inCenterOfMassTransform1; - mTransform2To1 = transform1_to_2.InversedRotationTranslation(); - - // Calculate bounds - mBoundsOf1 = inShape1->GetLocalBounds().Scaled(inScale1); - mBoundsOf1.ExpandBy(Vec3::sReplicate(inCollideShapeSettings.mMaxSeparationDistance)); - mBoundsOf1InSpaceOf2 = mBoundsOf1.Transformed(transform1_to_2); // Convert bounding box of 1 into space of 2 - - // Determine if shape 2 is inside out or not - mScaleSign2 = ScaleHelpers::IsInsideOut(inScale2)? -1.0f : 1.0f; -} - -void CollideConvexVsTriangles::Collide(Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2, uint8 inActiveEdges, const SubShapeID &inSubShapeID2) -{ - JPH_PROFILE_FUNCTION(); - - // Scale triangle and transform it to the space of 1 - Vec3 v0 = mTransform2To1 * (mScale2 * inV0); - Vec3 v1 = mTransform2To1 * (mScale2 * inV1); - Vec3 v2 = mTransform2To1 * (mScale2 * inV2); - - // Calculate triangle normal - Vec3 triangle_normal = mScaleSign2 * (v1 - v0).Cross(v2 - v0); - - // Backface check - bool back_facing = triangle_normal.Dot(v0) > 0.0f; - if (mCollideShapeSettings.mBackFaceMode == EBackFaceMode::IgnoreBackFaces && back_facing) - return; - - // Get bounding box for triangle - AABox triangle_bbox = AABox::sFromTwoPoints(v0, v1); - triangle_bbox.Encapsulate(v2); - - // Get intersection between triangle and shape box, if there is none, we're done - if (!triangle_bbox.Overlaps(mBoundsOf1)) - return; - - // Create triangle support function - TriangleConvexSupport triangle(v0, v1, v2); - - // Perform collision detection - // Note: As we don't remember the penetration axis from the last iteration, and it is likely that the shape (A) we're colliding the triangle (B) against is in front of the triangle, - // and the penetration axis is the shortest distance along to push B out of collision, we use the inverse of the triangle normal as an initial penetration axis. This has been seen - // to improve performance by approx. 5% over using a fixed axis like (1, 0, 0). - Vec3 penetration_axis = -triangle_normal, point1, point2; - EPAPenetrationDepth pen_depth; - EPAPenetrationDepth::EStatus status; - - // Get the support function - if (mShape1ExCvxRadius == nullptr) - mShape1ExCvxRadius = mShape1->GetSupportFunction(ConvexShape::ESupportMode::ExcludeConvexRadius, mBufferExCvxRadius, mScale1); - - // Perform GJK step - status = pen_depth.GetPenetrationDepthStepGJK(*mShape1ExCvxRadius, mShape1ExCvxRadius->GetConvexRadius() + mCollideShapeSettings.mMaxSeparationDistance, triangle, 0.0f, mCollideShapeSettings.mCollisionTolerance, penetration_axis, point1, point2); - - // Check result of collision detection - if (status == EPAPenetrationDepth::EStatus::NotColliding) - return; - else if (status == EPAPenetrationDepth::EStatus::Indeterminate) - { - // Need to run expensive EPA algorithm - - // Get the support function - if (mShape1IncCvxRadius == nullptr) - mShape1IncCvxRadius = mShape1->GetSupportFunction(ConvexShape::ESupportMode::IncludeConvexRadius, mBufferIncCvxRadius, mScale1); - - // Add convex radius - AddConvexRadius shape1_add_max_separation_distance(*mShape1IncCvxRadius, mCollideShapeSettings.mMaxSeparationDistance); - - // Perform EPA step - if (!pen_depth.GetPenetrationDepthStepEPA(shape1_add_max_separation_distance, triangle, mCollideShapeSettings.mPenetrationTolerance, penetration_axis, point1, point2)) - return; - } - - // Check if the penetration is bigger than the early out fraction - float penetration_depth = (point2 - point1).Length() - mCollideShapeSettings.mMaxSeparationDistance; - if (-penetration_depth >= mCollector.GetEarlyOutFraction()) - return; - - // Correct point1 for the added separation distance - float penetration_axis_len = penetration_axis.Length(); - if (penetration_axis_len > 0.0f) - point1 -= penetration_axis * (mCollideShapeSettings.mMaxSeparationDistance / penetration_axis_len); - - // Check if we have enabled active edge detection - if (mCollideShapeSettings.mActiveEdgeMode == EActiveEdgeMode::CollideOnlyWithActive && inActiveEdges != 0b111) - { - // Convert the active edge velocity hint to local space - Vec3 active_edge_movement_direction = mTransform1.Multiply3x3Transposed(mCollideShapeSettings.mActiveEdgeMovementDirection); - - // Update the penetration axis to account for active edges - // Note that we flip the triangle normal as the penetration axis is pointing towards the triangle instead of away - penetration_axis = ActiveEdges::FixNormal(v0, v1, v2, back_facing? triangle_normal : -triangle_normal, inActiveEdges, point2, penetration_axis, active_edge_movement_direction); - } - - // Convert to world space - point1 = mTransform1 * point1; - point2 = mTransform1 * point2; - Vec3 penetration_axis_world = mTransform1.Multiply3x3(penetration_axis); - - // Create collision result - CollideShapeResult result(point1, point2, penetration_axis_world, penetration_depth, mSubShapeID1, inSubShapeID2, TransformedShape::sGetBodyID(mCollector.GetContext())); - - // Gather faces - if (mCollideShapeSettings.mCollectFacesMode == ECollectFacesMode::CollectFaces) - { - // Get supporting face of shape 1 - mShape1->GetSupportingFace(SubShapeID(), -penetration_axis, mScale1, mTransform1, result.mShape1Face); - - // Get face of the triangle - result.mShape2Face.resize(3); - result.mShape2Face[0] = mTransform1 * v0; - result.mShape2Face[1] = mTransform1 * v1; - result.mShape2Face[2] = mTransform1 * v2; - } - - // Notify the collector - JPH_IF_TRACK_NARROWPHASE_STATS(TrackNarrowPhaseCollector track;) - mCollector.AddHit(result); -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollideConvexVsTriangles.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollideConvexVsTriangles.h deleted file mode 100644 index 65cff73931f..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollideConvexVsTriangles.h +++ /dev/null @@ -1,56 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -class CollideShapeSettings; - -/// Collision detection helper that collides a convex object vs one or more triangles -class JPH_EXPORT CollideConvexVsTriangles -{ -public: - /// Constructor - /// @param inShape1 The convex shape to collide against triangles - /// @param inScale1 Local space scale for the convex object - /// @param inScale2 Local space scale for the triangles - /// @param inCenterOfMassTransform1 Transform that takes the center of mass of 1 into world space - /// @param inCenterOfMassTransform2 Transform that takes the center of mass of 2 into world space - /// @param inSubShapeID1 Sub shape ID of the convex object - /// @param inCollideShapeSettings Settings for the collide shape query - /// @param ioCollector The collector that will receive the results - CollideConvexVsTriangles(const ConvexShape *inShape1, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeID &inSubShapeID1, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector); - - /// Collide convex object with a single triangle - /// @param inV0 , inV1 , inV2: CCW triangle vertices - /// @param inActiveEdges bit 0 = edge v0..v1 is active, bit 1 = edge v1..v2 is active, bit 2 = edge v2..v0 is active - /// An active edge is an edge that is not connected to another triangle in such a way that it is impossible to collide with the edge - /// @param inSubShapeID2 The sub shape ID for the triangle - void Collide(Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2, uint8 inActiveEdges, const SubShapeID &inSubShapeID2); - -protected: - const CollideShapeSettings & mCollideShapeSettings; ///< Settings for this collision operation - CollideShapeCollector & mCollector; ///< The collector that will receive the results - const ConvexShape * mShape1; ///< The shape that we're colliding with - Vec3 mScale1; ///< The scale of the shape (in shape local space) of the shape we're colliding with - Vec3 mScale2; ///< The scale of the shape (in shape local space) of the shape we're colliding against - Mat44 mTransform1; ///< Transform of the shape we're colliding with - Mat44 mTransform2To1; ///< Transform that takes a point in space of the colliding shape to the shape we're colliding with - AABox mBoundsOf1; ///< Bounds of the colliding shape in local space - AABox mBoundsOf1InSpaceOf2; ///< Bounds of the colliding shape in space of shape we're colliding with - SubShapeID mSubShapeID1; ///< Sub shape ID of colliding shape - float mScaleSign2; ///< Sign of the scale of object 2, -1 if object is inside out, 1 if not - ConvexShape::SupportBuffer mBufferExCvxRadius; ///< Buffer that holds the support function data excluding convex radius - ConvexShape::SupportBuffer mBufferIncCvxRadius; ///< Buffer that holds the support function data including convex radius - const ConvexShape::Support * mShape1ExCvxRadius = nullptr; ///< Actual support function object excluding convex radius - const ConvexShape::Support * mShape1IncCvxRadius = nullptr; ///< Actual support function object including convex radius -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollidePointResult.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollidePointResult.h deleted file mode 100644 index 8601b3c40a0..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollidePointResult.h +++ /dev/null @@ -1,25 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include - -JPH_NAMESPACE_BEGIN - -/// Structure that holds the result of colliding a point against a shape -class CollidePointResult -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Function required by the CollisionCollector. A smaller fraction is considered to be a 'better hit'. For point queries there is no sensible return value. - inline float GetEarlyOutFraction() const { return 0.0f; } - - BodyID mBodyID; ///< Body that was hit - SubShapeID mSubShapeID2; ///< Sub shape ID of shape that we collided against -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollideShape.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollideShape.h deleted file mode 100644 index 2c5f8f217ff..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollideShape.h +++ /dev/null @@ -1,105 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -/// Class that contains all information of two colliding shapes -class CollideShapeResult -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Default constructor - CollideShapeResult() = default; - - /// Constructor - CollideShapeResult(Vec3Arg inContactPointOn1, Vec3Arg inContactPointOn2, Vec3Arg inPenetrationAxis, float inPenetrationDepth, const SubShapeID &inSubShapeID1, const SubShapeID &inSubShapeID2, const BodyID &inBodyID2) : - mContactPointOn1(inContactPointOn1), - mContactPointOn2(inContactPointOn2), - mPenetrationAxis(inPenetrationAxis), - mPenetrationDepth(inPenetrationDepth), - mSubShapeID1(inSubShapeID1), - mSubShapeID2(inSubShapeID2), - mBodyID2(inBodyID2) - { - } - - /// Function required by the CollisionCollector. A smaller fraction is considered to be a 'better hit'. We use -penetration depth to get the hit with the biggest penetration depth - inline float GetEarlyOutFraction() const { return -mPenetrationDepth; } - - /// Reverses the hit result, swapping contact point 1 with contact point 2 etc. - inline CollideShapeResult Reversed() const - { - CollideShapeResult result; - result.mContactPointOn2 = mContactPointOn1; - result.mContactPointOn1 = mContactPointOn2; - result.mPenetrationAxis = -mPenetrationAxis; - result.mPenetrationDepth = mPenetrationDepth; - result.mSubShapeID2 = mSubShapeID1; - result.mSubShapeID1 = mSubShapeID2; - result.mBodyID2 = mBodyID2; - result.mShape2Face = mShape1Face; - result.mShape1Face = mShape2Face; - return result; - } - - using Face = StaticArray; - - Vec3 mContactPointOn1; ///< Contact point on the surface of shape 1 (in world space or relative to base offset) - Vec3 mContactPointOn2; ///< Contact point on the surface of shape 2 (in world space or relative to base offset). If the penetration depth is 0, this will be the same as mContactPointOn1. - Vec3 mPenetrationAxis; ///< Direction to move shape 2 out of collision along the shortest path (magnitude is meaningless, in world space). You can use -mPenetrationAxis.Normalized() as contact normal. - float mPenetrationDepth; ///< Penetration depth (move shape 2 by this distance to resolve the collision) - SubShapeID mSubShapeID1; ///< Sub shape ID that identifies the face on shape 1 - SubShapeID mSubShapeID2; ///< Sub shape ID that identifies the face on shape 2 - BodyID mBodyID2; ///< BodyID to which shape 2 belongs to - Face mShape1Face; ///< Colliding face on shape 1 (optional result, in world space or relative to base offset) - Face mShape2Face; ///< Colliding face on shape 2 (optional result, in world space or relative to base offset) -}; - -/// Settings to be passed with a collision query -class CollideSettingsBase -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// How active edges (edges that a moving object should bump into) are handled - EActiveEdgeMode mActiveEdgeMode = EActiveEdgeMode::CollideOnlyWithActive; - - /// If colliding faces should be collected or only the collision point - ECollectFacesMode mCollectFacesMode = ECollectFacesMode::NoFaces; - - /// If objects are closer than this distance, they are considered to be colliding (used for GJK) (unit: meter) - float mCollisionTolerance = cDefaultCollisionTolerance; - - /// A factor that determines the accuracy of the penetration depth calculation. If the change of the squared distance is less than tolerance * current_penetration_depth^2 the algorithm will terminate. (unit: dimensionless) - float mPenetrationTolerance = cDefaultPenetrationTolerance; - - /// When mActiveEdgeMode is CollideOnlyWithActive a movement direction can be provided. When hitting an inactive edge, the system will select the triangle normal as penetration depth only if it impedes the movement less than with the calculated penetration depth. - Vec3 mActiveEdgeMovementDirection = Vec3::sZero(); -}; - -/// Settings to be passed with a collision query -class CollideShapeSettings : public CollideSettingsBase -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// When > 0 contacts in the vicinity of the query shape can be found. All nearest contacts that are not further away than this distance will be found (unit: meter) - float mMaxSeparationDistance = 0.0f; - - /// How backfacing triangles should be treated - EBackFaceMode mBackFaceMode = EBackFaceMode::IgnoreBackFaces; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollideSoftBodyVerticesVsTriangles.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollideSoftBodyVerticesVsTriangles.h deleted file mode 100644 index 4385e15cddf..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollideSoftBodyVerticesVsTriangles.h +++ /dev/null @@ -1,98 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2024 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include - -JPH_NAMESPACE_BEGIN - -/// Collision detection helper that collides soft body vertices vs triangles -class JPH_EXPORT CollideSoftBodyVerticesVsTriangles -{ -public: - CollideSoftBodyVerticesVsTriangles(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale) : - mTransform(inCenterOfMassTransform * Mat44::sScale(inScale)), - mInvTransform(mTransform.Inversed()), - mNormalSign(ScaleHelpers::IsInsideOut(inScale)? -1.0f : 1.0f) - { - } - - JPH_INLINE void StartVertex(const SoftBodyVertex &inVertex) - { - mLocalPosition = mInvTransform * inVertex.mPosition; - mClosestDistanceSq = FLT_MAX; - } - - JPH_INLINE void ProcessTriangle(Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2) - { - // Get the closest point from the vertex to the triangle - uint32 set; - Vec3 closest_point = ClosestPoint::GetClosestPointOnTriangle(inV0 - mLocalPosition, inV1 - mLocalPosition, inV2 - mLocalPosition, set); - float dist_sq = closest_point.LengthSq(); - if (dist_sq < mClosestDistanceSq) - { - mV0 = inV0; - mV1 = inV1; - mV2 = inV2; - mClosestPoint = closest_point; - mClosestDistanceSq = dist_sq; - mSet = set; - } - } - - JPH_INLINE void FinishVertex(SoftBodyVertex &ioVertex, int inCollidingShapeIndex) const - { - if (mClosestDistanceSq < FLT_MAX) - { - // Convert triangle to world space - Vec3 v0 = mTransform * mV0; - Vec3 v1 = mTransform * mV1; - Vec3 v2 = mTransform * mV2; - Vec3 triangle_normal = mNormalSign * (v1 - v0).Cross(v2 - v0).NormalizedOr(Vec3::sAxisY()); - - if (mSet == 0b111) - { - // Closest is interior to the triangle, use plane as collision plane but don't allow more than 0.1 m penetration - // because otherwise a triangle half a level a way will have a huge penetration if it is back facing - float penetration = min(triangle_normal.Dot(v0 - ioVertex.mPosition), 0.1f); - if (penetration > ioVertex.mLargestPenetration) - { - ioVertex.mLargestPenetration = penetration; - ioVertex.mCollisionPlane = Plane::sFromPointAndNormal(v0, triangle_normal); - ioVertex.mCollidingShapeIndex = inCollidingShapeIndex; - } - } - else - { - // Closest point is on an edge or vertex, use closest point as collision plane - Vec3 closest_point = mTransform * (mLocalPosition + mClosestPoint); - Vec3 normal = ioVertex.mPosition - closest_point; - if (normal.Dot(triangle_normal) > 0.0f) // Ignore back facing edges - { - float normal_length = normal.Length(); - float penetration = -normal_length; - if (penetration > ioVertex.mLargestPenetration) - { - ioVertex.mLargestPenetration = penetration; - ioVertex.mCollisionPlane = Plane::sFromPointAndNormal(closest_point, normal_length > 0.0f? normal / normal_length : triangle_normal); - ioVertex.mCollidingShapeIndex = inCollidingShapeIndex; - } - } - } - } - } - - Mat44 mTransform; - Mat44 mInvTransform; - Vec3 mLocalPosition; - Vec3 mV0, mV1, mV2; - Vec3 mClosestPoint; - float mNormalSign; - float mClosestDistanceSq; - uint32 mSet; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollideSphereVsTriangles.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollideSphereVsTriangles.cpp deleted file mode 100644 index 92ed28a7b49..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollideSphereVsTriangles.cpp +++ /dev/null @@ -1,123 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -static constexpr uint8 sClosestFeatureToActiveEdgesMask[] = { - 0b000, // 0b000: Invalid, guarded by an assert - 0b101, // 0b001: Vertex 1 -> edge 1 or 3 - 0b011, // 0b010: Vertex 2 -> edge 1 or 2 - 0b001, // 0b011: Vertex 1 & 2 -> edge 1 - 0b110, // 0b100: Vertex 3 -> edge 2 or 3 - 0b100, // 0b101: Vertex 1 & 3 -> edge 3 - 0b010, // 0b110: Vertex 2 & 3 -> edge 2 - // 0b111: Vertex 1, 2 & 3 -> interior, guarded by an if -}; - -CollideSphereVsTriangles::CollideSphereVsTriangles(const SphereShape *inShape1, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeID &inSubShapeID1, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector) : - mCollideShapeSettings(inCollideShapeSettings), - mCollector(ioCollector), - mShape1(inShape1), - mScale2(inScale2), - mTransform2(inCenterOfMassTransform2), - mSubShapeID1(inSubShapeID1) -{ - // Calculate the center of the sphere in the space of 2 - mSphereCenterIn2 = inCenterOfMassTransform2.Multiply3x3Transposed(inCenterOfMassTransform1.GetTranslation() - inCenterOfMassTransform2.GetTranslation()); - - // Determine if shape 2 is inside out or not - mScaleSign2 = ScaleHelpers::IsInsideOut(inScale2)? -1.0f : 1.0f; - - // Check that the sphere is uniformly scaled - JPH_ASSERT(ScaleHelpers::IsUniformScale(inScale1.Abs())); - mRadius = abs(inScale1.GetX()) * inShape1->GetRadius(); - mRadiusPlusMaxSeparationSq = Square(mRadius + inCollideShapeSettings.mMaxSeparationDistance); -} - -void CollideSphereVsTriangles::Collide(Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2, uint8 inActiveEdges, const SubShapeID &inSubShapeID2) -{ - JPH_PROFILE_FUNCTION(); - - // Scale triangle and make it relative to the center of the sphere - Vec3 v0 = mScale2 * inV0 - mSphereCenterIn2; - Vec3 v1 = mScale2 * inV1 - mSphereCenterIn2; - Vec3 v2 = mScale2 * inV2 - mSphereCenterIn2; - - // Calculate triangle normal - Vec3 triangle_normal = mScaleSign2 * (v1 - v0).Cross(v2 - v0); - - // Backface check - bool back_facing = triangle_normal.Dot(v0) > 0.0f; - if (mCollideShapeSettings.mBackFaceMode == EBackFaceMode::IgnoreBackFaces && back_facing) - return; - - // Check if we collide with the sphere - uint32 closest_feature; - Vec3 point2 = ClosestPoint::GetClosestPointOnTriangle(v0, v1, v2, closest_feature); - float point2_len_sq = point2.LengthSq(); - if (point2_len_sq > mRadiusPlusMaxSeparationSq) - return; - - // Calculate penetration depth - float penetration_depth = mRadius - sqrt(point2_len_sq); - if (-penetration_depth >= mCollector.GetEarlyOutFraction()) - return; - - // Calculate penetration axis, direction along which to push 2 to move it out of collision (this is always away from the sphere center) - Vec3 penetration_axis = point2.NormalizedOr(Vec3::sAxisY()); - - // Calculate the point on the sphere - Vec3 point1 = mRadius * penetration_axis; - - // Check if we have enabled active edge detection - JPH_ASSERT(closest_feature != 0); - if (mCollideShapeSettings.mActiveEdgeMode == EActiveEdgeMode::CollideOnlyWithActive - && closest_feature != 0b111 // For an interior hit we should already have the right normal - && (inActiveEdges & sClosestFeatureToActiveEdgesMask[closest_feature]) == 0) // If we didn't hit an active edge we should take the triangle normal - { - // Convert the active edge velocity hint to local space - Vec3 active_edge_movement_direction = mTransform2.Multiply3x3Transposed(mCollideShapeSettings.mActiveEdgeMovementDirection); - - // See ActiveEdges::FixNormal. If penetration_axis affects the movement less than the triangle normal we keep penetration_axis. - Vec3 new_penetration_axis = back_facing? triangle_normal : -triangle_normal; - if (active_edge_movement_direction.Dot(penetration_axis) * new_penetration_axis.Length() >= active_edge_movement_direction.Dot(new_penetration_axis)) - penetration_axis = new_penetration_axis; - } - - // Convert to world space - point1 = mTransform2 * (mSphereCenterIn2 + point1); - point2 = mTransform2 * (mSphereCenterIn2 + point2); - Vec3 penetration_axis_world = mTransform2.Multiply3x3(penetration_axis); - - // Create collision result - CollideShapeResult result(point1, point2, penetration_axis_world, penetration_depth, mSubShapeID1, inSubShapeID2, TransformedShape::sGetBodyID(mCollector.GetContext())); - - // Gather faces - if (mCollideShapeSettings.mCollectFacesMode == ECollectFacesMode::CollectFaces) - { - // The sphere doesn't have a supporting face - - // Get face of triangle 2 - result.mShape2Face.resize(3); - result.mShape2Face[0] = mTransform2 * (mSphereCenterIn2 + v0); - result.mShape2Face[1] = mTransform2 * (mSphereCenterIn2 + v1); - result.mShape2Face[2] = mTransform2 * (mSphereCenterIn2 + v2); - } - - // Notify the collector - JPH_IF_TRACK_NARROWPHASE_STATS(TrackNarrowPhaseCollector track;) - mCollector.AddHit(result); -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollideSphereVsTriangles.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollideSphereVsTriangles.h deleted file mode 100644 index 13175f4b31c..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollideSphereVsTriangles.h +++ /dev/null @@ -1,50 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -class CollideShapeSettings; - -/// Collision detection helper that collides a sphere vs one or more triangles -class JPH_EXPORT CollideSphereVsTriangles -{ -public: - /// Constructor - /// @param inShape1 The sphere to collide against triangles - /// @param inScale1 Local space scale for the sphere - /// @param inScale2 Local space scale for the triangles - /// @param inCenterOfMassTransform1 Transform that takes the center of mass of 1 into world space - /// @param inCenterOfMassTransform2 Transform that takes the center of mass of 2 into world space - /// @param inSubShapeID1 Sub shape ID of the convex object - /// @param inCollideShapeSettings Settings for the collide shape query - /// @param ioCollector The collector that will receive the results - CollideSphereVsTriangles(const SphereShape *inShape1, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeID &inSubShapeID1, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector); - - /// Collide sphere with a single triangle - /// @param inV0 , inV1 , inV2: CCW triangle vertices - /// @param inActiveEdges bit 0 = edge v0..v1 is active, bit 1 = edge v1..v2 is active, bit 2 = edge v2..v0 is active - /// An active edge is an edge that is not connected to another triangle in such a way that it is impossible to collide with the edge - /// @param inSubShapeID2 The sub shape ID for the triangle - void Collide(Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2, uint8 inActiveEdges, const SubShapeID &inSubShapeID2); - -protected: - const CollideShapeSettings & mCollideShapeSettings; ///< Settings for this collision operation - CollideShapeCollector & mCollector; ///< The collector that will receive the results - const SphereShape * mShape1; ///< The shape that we're colliding with - Vec3 mScale2; ///< The scale of the shape (in shape local space) of the shape we're colliding against - Mat44 mTransform2; ///< Transform of the shape we're colliding against - Vec3 mSphereCenterIn2; ///< The center of the sphere in the space of 2 - SubShapeID mSubShapeID1; ///< Sub shape ID of colliding shape - float mScaleSign2; ///< Sign of the scale of object 2, -1 if object is inside out, 1 if not - float mRadius; ///< Radius of the sphere - float mRadiusPlusMaxSeparationSq; ///< (Radius + Max SeparationDistance)^2 -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollisionCollector.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollisionCollector.h deleted file mode 100644 index 274e8f03284..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollisionCollector.h +++ /dev/null @@ -1,102 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -JPH_NAMESPACE_BEGIN - -class Body; -class TransformedShape; - -/// Traits to use for CastRay -class CollisionCollectorTraitsCastRay -{ -public: - /// For rays the early out fraction is the fraction along the line to order hits. - static constexpr float InitialEarlyOutFraction = 1.0f + FLT_EPSILON; ///< Furthest hit: Fraction is 1 + epsilon - static constexpr float ShouldEarlyOutFraction = 0.0f; ///< Closest hit: Fraction is 0 -}; - -/// Traits to use for CastShape -class CollisionCollectorTraitsCastShape -{ -public: - /// For rays the early out fraction is the fraction along the line to order hits. - static constexpr float InitialEarlyOutFraction = 1.0f + FLT_EPSILON; ///< Furthest hit: Fraction is 1 + epsilon - static constexpr float ShouldEarlyOutFraction = -FLT_MAX; ///< Deepest hit: Penetration is infinite -}; - -/// Traits to use for CollideShape -class CollisionCollectorTraitsCollideShape -{ -public: - /// For shape collisions we use -penetration depth to order hits. - static constexpr float InitialEarlyOutFraction = FLT_MAX; ///< Most shallow hit: Separation is infinite - static constexpr float ShouldEarlyOutFraction = -FLT_MAX; ///< Deepest hit: Penetration is infinite -}; - -/// Traits to use for CollidePoint -using CollisionCollectorTraitsCollidePoint = CollisionCollectorTraitsCollideShape; - -/// Virtual interface that allows collecting multiple collision results -template -class CollisionCollector -{ -public: - /// Declare ResultType so that derived classes can use it - using ResultType = ResultTypeArg; - - /// Default constructor - CollisionCollector() = default; - - /// Constructor to initialize from another collector - template - explicit CollisionCollector(const CollisionCollector &inRHS) : mEarlyOutFraction(inRHS.GetEarlyOutFraction()), mContext(inRHS.GetContext()) { } - CollisionCollector(const CollisionCollector &inRHS) = default; - - /// Destructor - virtual ~CollisionCollector() = default; - - /// If you want to reuse this collector, call Reset() - virtual void Reset() { mEarlyOutFraction = TraitsType::InitialEarlyOutFraction; } - - /// When running a query through the NarrowPhaseQuery class, this will be called for every body that is potentially colliding. - /// It allows collecting additional information needed by the collision collector implementation from the body under lock protection - /// before AddHit is called (e.g. the user data pointer or the velocity of the body). - virtual void OnBody([[maybe_unused]] const Body &inBody) { /* Collects nothing by default */ } - - /// Set by the collision detection functions to the current TransformedShape that we're colliding against before calling the AddHit function - void SetContext(const TransformedShape *inContext) { mContext = inContext; } - const TransformedShape *GetContext() const { return mContext; } - - /// This function will be called for every hit found, it's up to the application to decide how to store the hit - virtual void AddHit(const ResultType &inResult) = 0; - - /// Update the early out fraction (should be lower than before) - inline void UpdateEarlyOutFraction(float inFraction) { JPH_ASSERT(inFraction <= mEarlyOutFraction); mEarlyOutFraction = inFraction; } - - /// Reset the early out fraction to a specific value - inline void ResetEarlyOutFraction(float inFraction = TraitsType::InitialEarlyOutFraction) { mEarlyOutFraction = inFraction; } - - /// Force the collision detection algorithm to terminate as soon as possible. Call this from the AddHit function when a satisfying hit is found. - inline void ForceEarlyOut() { mEarlyOutFraction = TraitsType::ShouldEarlyOutFraction; } - - /// When true, the collector will no longer accept any additional hits and the collision detection routine should early out as soon as possible - inline bool ShouldEarlyOut() const { return mEarlyOutFraction <= TraitsType::ShouldEarlyOutFraction; } - - /// Get the current early out value - inline float GetEarlyOutFraction() const { return mEarlyOutFraction; } - - /// Get the current early out value but make sure it's bigger than zero, this is used for shape casting as negative values are used for penetration - inline float GetPositiveEarlyOutFraction() const { return max(FLT_MIN, mEarlyOutFraction); } - -private: - /// The early out fraction determines the fraction below which the collector is still accepting a hit (can be used to reduce the amount of work) - float mEarlyOutFraction = TraitsType::InitialEarlyOutFraction; - - /// Set by the collision detection functions to the current TransformedShape of the body that we're colliding against before calling the AddHit function - const TransformedShape *mContext = nullptr; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollisionCollectorImpl.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollisionCollectorImpl.h deleted file mode 100644 index 7bdd5da2797..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollisionCollectorImpl.h +++ /dev/null @@ -1,134 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include - -JPH_NAMESPACE_BEGIN - -/// Simple implementation that collects all hits and optionally sorts them on distance -template -class AllHitCollisionCollector : public CollectorType -{ -public: - /// Redeclare ResultType - using ResultType = typename CollectorType::ResultType; - - // See: CollectorType::Reset - virtual void Reset() override - { - CollectorType::Reset(); - - mHits.clear(); - } - - // See: CollectorType::AddHit - virtual void AddHit(const ResultType &inResult) override - { - mHits.push_back(inResult); - } - - /// Order hits on closest first - void Sort() - { - QuickSort(mHits.begin(), mHits.end(), [](const ResultType &inLHS, const ResultType &inRHS) { return inLHS.GetEarlyOutFraction() < inRHS.GetEarlyOutFraction(); }); - } - - /// Check if any hits were collected - inline bool HadHit() const - { - return !mHits.empty(); - } - - Array mHits; -}; - -/// Simple implementation that collects the closest / deepest hit -template -class ClosestHitCollisionCollector : public CollectorType -{ -public: - /// Redeclare ResultType - using ResultType = typename CollectorType::ResultType; - - // See: CollectorType::Reset - virtual void Reset() override - { - CollectorType::Reset(); - - mHadHit = false; - } - - // See: CollectorType::AddHit - virtual void AddHit(const ResultType &inResult) override - { - float early_out = inResult.GetEarlyOutFraction(); - if (!mHadHit || early_out < mHit.GetEarlyOutFraction()) - { - // Update early out fraction - CollectorType::UpdateEarlyOutFraction(early_out); - - // Store hit - mHit = inResult; - mHadHit = true; - } - } - - /// Check if this collector has had a hit - inline bool HadHit() const - { - return mHadHit; - } - - ResultType mHit; - -private: - bool mHadHit = false; -}; - -/// Simple implementation that collects any hit -template -class AnyHitCollisionCollector : public CollectorType -{ -public: - /// Redeclare ResultType - using ResultType = typename CollectorType::ResultType; - - // See: CollectorType::Reset - virtual void Reset() override - { - CollectorType::Reset(); - - mHadHit = false; - } - - // See: CollectorType::AddHit - virtual void AddHit(const ResultType &inResult) override - { - // Test that the collector is not collecting more hits after forcing an early out - JPH_ASSERT(!mHadHit); - - // Abort any further testing - CollectorType::ForceEarlyOut(); - - // Store hit - mHit = inResult; - mHadHit = true; - } - - /// Check if this collector has had a hit - inline bool HadHit() const - { - return mHadHit; - } - - ResultType mHit; - -private: - bool mHadHit = false; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollisionDispatch.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollisionDispatch.cpp deleted file mode 100644 index 259a9526e8b..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollisionDispatch.cpp +++ /dev/null @@ -1,107 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include - -JPH_NAMESPACE_BEGIN - -CollisionDispatch::CollideShape CollisionDispatch::sCollideShape[NumSubShapeTypes][NumSubShapeTypes]; -CollisionDispatch::CastShape CollisionDispatch::sCastShape[NumSubShapeTypes][NumSubShapeTypes]; - -void CollisionDispatch::sInit() -{ - for (uint i = 0; i < NumSubShapeTypes; ++i) - for (uint j = 0; j < NumSubShapeTypes; ++j) - { - if (sCollideShape[i][j] == nullptr) - sCollideShape[i][j] = [](const Shape *, const Shape *, Vec3Arg, Vec3Arg, Mat44Arg, Mat44Arg, const SubShapeIDCreator &, const SubShapeIDCreator &, const CollideShapeSettings &, CollideShapeCollector &, const ShapeFilter &) - { - JPH_ASSERT(false, "Unsupported shape pair"); - }; - - if (sCastShape[i][j] == nullptr) - sCastShape[i][j] = [](const ShapeCast &, const ShapeCastSettings &, const Shape *, Vec3Arg, const ShapeFilter &, Mat44Arg, const SubShapeIDCreator &, const SubShapeIDCreator &, CastShapeCollector &) - { - JPH_ASSERT(false, "Unsupported shape pair"); - }; - } -} - -void CollisionDispatch::sReversedCollideShape(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter) -{ - // A collision collector that flips the collision results - class ReversedCollector : public CollideShapeCollector - { - public: - explicit ReversedCollector(CollideShapeCollector &ioCollector) : - CollideShapeCollector(ioCollector), - mCollector(ioCollector) - { - } - - virtual void AddHit(const CollideShapeResult &inResult) override - { - // Add the reversed hit - mCollector.AddHit(inResult.Reversed()); - - // If our chained collector updated its early out fraction, we need to follow - UpdateEarlyOutFraction(mCollector.GetEarlyOutFraction()); - } - - private: - CollideShapeCollector & mCollector; - }; - - ReversedShapeFilter shape_filter(inShapeFilter); - ReversedCollector collector(ioCollector); - sCollideShapeVsShape(inShape2, inShape1, inScale2, inScale1, inCenterOfMassTransform2, inCenterOfMassTransform1, inSubShapeIDCreator2, inSubShapeIDCreator1, inCollideShapeSettings, collector, shape_filter); -} - -void CollisionDispatch::sReversedCastShape(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector) -{ - // A collision collector that flips the collision results - class ReversedCollector : public CastShapeCollector - { - public: - explicit ReversedCollector(CastShapeCollector &ioCollector, Vec3Arg inWorldDirection) : - CastShapeCollector(ioCollector), - mCollector(ioCollector), - mWorldDirection(inWorldDirection) - { - } - - virtual void AddHit(const ShapeCastResult &inResult) override - { - // Add the reversed hit - mCollector.AddHit(inResult.Reversed(mWorldDirection)); - - // If our chained collector updated its early out fraction, we need to follow - UpdateEarlyOutFraction(mCollector.GetEarlyOutFraction()); - } - - private: - CastShapeCollector & mCollector; - Vec3 mWorldDirection; - }; - - // Reverse the shape cast (shape cast is in local space to shape 2) - Mat44 com_start_inv = inShapeCast.mCenterOfMassStart.InversedRotationTranslation(); - ShapeCast local_shape_cast(inShape, inScale, com_start_inv, -com_start_inv.Multiply3x3(inShapeCast.mDirection)); - - // Calculate the center of mass of shape 1 at start of sweep - Mat44 shape1_com = inCenterOfMassTransform2 * inShapeCast.mCenterOfMassStart; - - // Calculate the world space direction vector of the shape cast - Vec3 world_direction = -inCenterOfMassTransform2.Multiply3x3(inShapeCast.mDirection); - - // Forward the cast - ReversedShapeFilter shape_filter(inShapeFilter); - ReversedCollector collector(ioCollector, world_direction); - sCastShapeVsShapeLocalSpace(local_shape_cast, inShapeCastSettings, inShapeCast.mShape, inShapeCast.mScale, shape_filter, shape1_com, inSubShapeIDCreator2, inSubShapeIDCreator1, collector); -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollisionDispatch.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollisionDispatch.h deleted file mode 100644 index 2b8f5ad457e..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollisionDispatch.h +++ /dev/null @@ -1,97 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -class CollideShapeSettings; - -/// Dispatch function, main function to handle collisions between shapes -class JPH_EXPORT CollisionDispatch -{ -public: - /// Collide 2 shapes and pass any collision on to ioCollector - /// @param inShape1 The first shape - /// @param inShape2 The second shape - /// @param inScale1 Local space scale of shape 1 - /// @param inScale2 Local space scale of shape 2 - /// @param inCenterOfMassTransform1 Transform to transform center of mass of shape 1 into world space - /// @param inCenterOfMassTransform2 Transform to transform center of mass of shape 2 into world space - /// @param inSubShapeIDCreator1 Class that tracks the current sub shape ID for shape 1 - /// @param inSubShapeIDCreator2 Class that tracks the current sub shape ID for shape 2 - /// @param inCollideShapeSettings Options for the CollideShape test - /// @param ioCollector The collector that receives the results. - /// @param inShapeFilter allows selectively disabling collisions between pairs of (sub) shapes. - static inline void sCollideShapeVsShape(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter = { }) - { - JPH_IF_TRACK_NARROWPHASE_STATS(TrackNarrowPhaseStat track(NarrowPhaseStat::sCollideShape[(int)inShape1->GetSubType()][(int)inShape2->GetSubType()]);) - - // Only test shape if it passes the shape filter - if (inShapeFilter.ShouldCollide(inShape1, inSubShapeIDCreator1.GetID(), inShape2, inSubShapeIDCreator2.GetID())) - sCollideShape[(int)inShape1->GetSubType()][(int)inShape2->GetSubType()](inShape1, inShape2, inScale1, inScale2, inCenterOfMassTransform1, inCenterOfMassTransform2, inSubShapeIDCreator1, inSubShapeIDCreator2, inCollideShapeSettings, ioCollector, inShapeFilter); - } - - /// Cast a shape against this shape, passes any hits found to ioCollector. - /// Note: This version takes the shape cast in local space relative to the center of mass of inShape, take a look at sCastShapeVsShapeWorldSpace if you have a shape cast in world space. - /// @param inShapeCastLocal The shape to cast against the other shape and its start and direction. - /// @param inShapeCastSettings Settings for performing the cast - /// @param inShape The shape to cast against. - /// @param inScale Local space scale for the shape to cast against. - /// @param inShapeFilter allows selectively disabling collisions between pairs of (sub) shapes. - /// @param inCenterOfMassTransform2 Is the center of mass transform of shape 2 (excluding scale), this is used to provide a transform to the shape cast result so that local hit result quantities can be transformed into world space. - /// @param inSubShapeIDCreator1 Class that tracks the current sub shape ID for the casting shape - /// @param inSubShapeIDCreator2 Class that tracks the current sub shape ID for the shape we're casting against - /// @param ioCollector The collector that receives the results. - static inline void sCastShapeVsShapeLocalSpace(const ShapeCast &inShapeCastLocal, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector) - { - JPH_IF_TRACK_NARROWPHASE_STATS(TrackNarrowPhaseStat track(NarrowPhaseStat::sCastShape[(int)inShapeCastLocal.mShape->GetSubType()][(int)inShape->GetSubType()]);) - - // Only test shape if it passes the shape filter - if (inShapeFilter.ShouldCollide(inShapeCastLocal.mShape, inSubShapeIDCreator1.GetID(), inShape, inSubShapeIDCreator2.GetID())) - sCastShape[(int)inShapeCastLocal.mShape->GetSubType()][(int)inShape->GetSubType()](inShapeCastLocal, inShapeCastSettings, inShape, inScale, inShapeFilter, inCenterOfMassTransform2, inSubShapeIDCreator1, inSubShapeIDCreator2, ioCollector); - } - - /// See: sCastShapeVsShapeLocalSpace. - /// The only difference is that the shape cast (inShapeCastWorld) is provided in world space. - /// Note: A shape cast contains the center of mass start of the shape, if you have the world transform of the shape you probably want to construct it using ShapeCast::sFromWorldTransform. - static inline void sCastShapeVsShapeWorldSpace(const ShapeCast &inShapeCastWorld, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector) - { - ShapeCast local_shape_cast = inShapeCastWorld.PostTransformed(inCenterOfMassTransform2.InversedRotationTranslation()); - sCastShapeVsShapeLocalSpace(local_shape_cast, inShapeCastSettings, inShape, inScale, inShapeFilter, inCenterOfMassTransform2, inSubShapeIDCreator1, inSubShapeIDCreator2, ioCollector); - } - - /// Function that collides 2 shapes (see sCollideShapeVsShape) - using CollideShape = void (*)(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter); - - /// Function that casts a shape vs another shape (see sCastShapeVsShapeLocalSpace) - using CastShape = void (*)(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector); - - /// Initialize all collision functions with a function that asserts and returns no collision - static void sInit(); - - /// Register a collide shape function in the collision table - static void sRegisterCollideShape(EShapeSubType inType1, EShapeSubType inType2, CollideShape inFunction) { sCollideShape[(int)inType1][(int)inType2] = inFunction; } - - /// Register a cast shape function in the collision table - static void sRegisterCastShape(EShapeSubType inType1, EShapeSubType inType2, CastShape inFunction) { sCastShape[(int)inType1][(int)inType2] = inFunction; } - - /// An implementation of CollideShape that swaps inShape1 and inShape2 and swaps the result back, can be registered if the collision function only exists the other way around - static void sReversedCollideShape(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter); - - /// An implementation of CastShape that swaps inShape1 and inShape2 and swaps the result back, can be registered if the collision function only exists the other way around - static void sReversedCastShape(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector); - -private: - static CollideShape sCollideShape[NumSubShapeTypes][NumSubShapeTypes]; - static CastShape sCastShape[NumSubShapeTypes][NumSubShapeTypes]; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollisionGroup.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollisionGroup.cpp deleted file mode 100644 index a8c98b6dc4c..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollisionGroup.cpp +++ /dev/null @@ -1,33 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(CollisionGroup) -{ - JPH_ADD_ATTRIBUTE(CollisionGroup, mGroupFilter) - JPH_ADD_ATTRIBUTE(CollisionGroup, mGroupID) - JPH_ADD_ATTRIBUTE(CollisionGroup, mSubGroupID) -} - -void CollisionGroup::SaveBinaryState(StreamOut &inStream) const -{ - inStream.Write(mGroupID); - inStream.Write(mSubGroupID); -} - -void CollisionGroup::RestoreBinaryState(StreamIn &inStream) -{ - inStream.Read(mGroupID); - inStream.Read(mSubGroupID); -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollisionGroup.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollisionGroup.h deleted file mode 100644 index 266242ff052..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/CollisionGroup.h +++ /dev/null @@ -1,94 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include - -JPH_NAMESPACE_BEGIN - -class StreamIn; -class StreamOut; - -/// Two objects collide with each other if: -/// - Both don't have a group filter -/// - The first group filter says that the objects can collide -/// - Or if there's no filter for the first object, the second group filter says the objects can collide -class JPH_EXPORT CollisionGroup -{ -public: - JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, CollisionGroup) - - using GroupID = uint32; - using SubGroupID = uint32; - - static const GroupID cInvalidGroup = ~GroupID(0); - static const SubGroupID cInvalidSubGroup = ~SubGroupID(0); - - /// Default constructor - CollisionGroup() = default; - - /// Construct with all properties - CollisionGroup(const GroupFilter *inFilter, GroupID inGroupID, SubGroupID inSubGroupID) : mGroupFilter(inFilter), mGroupID(inGroupID), mSubGroupID(inSubGroupID) { } - - /// Set the collision group filter - inline void SetGroupFilter(const GroupFilter *inFilter) - { - mGroupFilter = inFilter; - } - - /// Get the collision group filter - inline const GroupFilter *GetGroupFilter() const - { - return mGroupFilter; - } - - /// Set the main group id for this object - inline void SetGroupID(GroupID inID) - { - mGroupID = inID; - } - - inline GroupID GetGroupID() const - { - return mGroupID; - } - - /// Add this object to a sub group - inline void SetSubGroupID(SubGroupID inID) - { - mSubGroupID = inID; - } - - inline SubGroupID GetSubGroupID() const - { - return mSubGroupID; - } - - /// Check if this object collides with another object - bool CanCollide(const CollisionGroup &inOther) const - { - // Call the CanCollide function of the first group filter that's not null - if (mGroupFilter != nullptr) - return mGroupFilter->CanCollide(*this, inOther); - else if (inOther.mGroupFilter != nullptr) - return inOther.mGroupFilter->CanCollide(inOther, *this); - else - return true; - } - - /// Saves the state of this object in binary form to inStream. Does not save group filter. - void SaveBinaryState(StreamOut &inStream) const; - - /// Restore the state of this object from inStream. Does not save group filter. - void RestoreBinaryState(StreamIn &inStream); - -private: - RefConst mGroupFilter; - GroupID mGroupID = cInvalidGroup; - SubGroupID mSubGroupID = cInvalidSubGroup; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ContactListener.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ContactListener.h deleted file mode 100644 index d347dc47571..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ContactListener.h +++ /dev/null @@ -1,114 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include - -JPH_NAMESPACE_BEGIN - -class Body; -class CollideShapeResult; - -/// Array of contact points -using ContactPoints = StaticArray; - -/// Manifold class, describes the contact surface between two bodies -class ContactManifold -{ -public: - /// Swaps shape 1 and 2 - ContactManifold SwapShapes() const { return { mBaseOffset, -mWorldSpaceNormal, mPenetrationDepth, mSubShapeID2, mSubShapeID1, mRelativeContactPointsOn2, mRelativeContactPointsOn1 }; } - - /// Access to the world space contact positions - inline RVec3 GetWorldSpaceContactPointOn1(uint inIndex) const { return mBaseOffset + mRelativeContactPointsOn1[inIndex]; } - inline RVec3 GetWorldSpaceContactPointOn2(uint inIndex) const { return mBaseOffset + mRelativeContactPointsOn2[inIndex]; } - - RVec3 mBaseOffset; ///< Offset to which all the contact points are relative - Vec3 mWorldSpaceNormal; ///< Normal for this manifold, direction along which to move body 2 out of collision along the shortest path - float mPenetrationDepth; ///< Penetration depth (move shape 2 by this distance to resolve the collision). If this value is negative, this is a speculative contact point and may not actually result in a velocity change as during solving the bodies may not actually collide. - SubShapeID mSubShapeID1; ///< Sub shapes that formed this manifold (note that when multiple manifolds are combined because they're coplanar, we lose some information here because we only keep track of one sub shape pair that we encounter, see description at Body::SetUseManifoldReduction) - SubShapeID mSubShapeID2; - ContactPoints mRelativeContactPointsOn1; ///< Contact points on the surface of shape 1 relative to mBaseOffset. - ContactPoints mRelativeContactPointsOn2; ///< Contact points on the surface of shape 2 relative to mBaseOffset. If there's no penetration, this will be the same as mRelativeContactPointsOn1. If there is penetration they will be different. -}; - -/// When a contact point is added or persisted, the callback gets a chance to override certain properties of the contact constraint. -/// The values are filled in with their defaults by the system so the callback doesn't need to modify anything, but it can if it wants to. -class ContactSettings -{ -public: - float mCombinedFriction; ///< Combined friction for the body pair (see: PhysicsSystem::SetCombineFriction) - float mCombinedRestitution; ///< Combined restitution for the body pair (see: PhysicsSystem::SetCombineRestitution) - float mInvMassScale1 = 1.0f; ///< Scale factor for the inverse mass of body 1 (0 = infinite mass, 1 = use original mass, 2 = body has half the mass). For the same contact pair, you should strive to keep the value the same over time. - float mInvInertiaScale1 = 1.0f; ///< Scale factor for the inverse inertia of body 1 (usually same as mInvMassScale1) - float mInvMassScale2 = 1.0f; ///< Scale factor for the inverse mass of body 2 (0 = infinite mass, 1 = use original mass, 2 = body has half the mass). For the same contact pair, you should strive to keep the value the same over time. - float mInvInertiaScale2 = 1.0f; ///< Scale factor for the inverse inertia of body 2 (usually same as mInvMassScale2) - bool mIsSensor; ///< If the contact should be treated as a sensor vs body contact (no collision response) - Vec3 mRelativeLinearSurfaceVelocity = Vec3::sZero(); ///< Relative linear surface velocity between the bodies (world space surface velocity of body 2 - world space surface velocity of body 1), can be used to create a conveyor belt effect - Vec3 mRelativeAngularSurfaceVelocity = Vec3::sZero(); ///< Relative angular surface velocity between the bodies (world space angular surface velocity of body 2 - world space angular surface velocity of body 1). Note that this angular velocity is relative to the center of mass of body 1, so if you want it relative to body 2's center of mass you need to add body 2 angular velocity x (body 1 world space center of mass - body 2 world space center of mass) to mRelativeLinearSurfaceVelocity. -}; - -/// Return value for the OnContactValidate callback. Determines if the contact is being processed or not. -/// Results are ordered so that the strongest accept has the lowest number and the strongest reject the highest number (which allows for easy combining of results) -enum class ValidateResult -{ - AcceptAllContactsForThisBodyPair, ///< Accept this and any further contact points for this body pair - AcceptContact, ///< Accept this contact only (and continue calling this callback for every contact manifold for the same body pair) - RejectContact, ///< Reject this contact only (but process any other contact manifolds for the same body pair) - RejectAllContactsForThisBodyPair ///< Rejects this and any further contact points for this body pair -}; - -/// A listener class that receives collision contact events. -/// It can be registered with the ContactConstraintManager (or PhysicsSystem). -/// Note that contact listener callbacks are called from multiple threads at the same time when all bodies are locked, you're only allowed to read from the bodies and you can't change physics state. -class ContactListener -{ -public: - /// Ensure virtual destructor - virtual ~ContactListener() = default; - - /// Called after detecting a collision between a body pair, but before calling OnContactAdded and before adding the contact constraint. - /// If the function rejects the contact, the contact will not be added and any other contacts between this body pair will not be processed. - /// This function will only be called once per PhysicsSystem::Update per body pair and may not be called again the next update - /// if a contact persists and no new contact pairs between sub shapes are found. - /// This is a rather expensive time to reject a contact point since a lot of the collision detection has happened already, make sure you - /// filter out the majority of undesired body pairs through the ObjectLayerPairFilter that is registered on the PhysicsSystem. - /// Note that this callback is called when all bodies are locked, so don't use any locking functions! - /// Body 1 will have a motion type that is larger or equal than body 2's motion type (order from large to small: dynamic -> kinematic -> static). When motion types are equal, they are ordered by BodyID. - /// The collision result (inCollisionResult) is reported relative to inBaseOffset. - virtual ValidateResult OnContactValidate([[maybe_unused]] const Body &inBody1, [[maybe_unused]] const Body &inBody2, [[maybe_unused]] RVec3Arg inBaseOffset, [[maybe_unused]] const CollideShapeResult &inCollisionResult) { return ValidateResult::AcceptAllContactsForThisBodyPair; } - - /// Called whenever a new contact point is detected. - /// Note that this callback is called when all bodies are locked, so don't use any locking functions! - /// Body 1 and 2 will be sorted such that body 1 ID < body 2 ID, so body 1 may not be dynamic. - /// Note that only active bodies will report contacts, as soon as a body goes to sleep the contacts between that body and all other - /// bodies will receive an OnContactRemoved callback, if this is the case then Body::IsActive() will return false during the callback. - /// When contacts are added, the constraint solver has not run yet, so the collision impulse is unknown at that point. - /// The velocities of inBody1 and inBody2 are the velocities before the contact has been resolved, so you can use this to - /// estimate the collision impulse to e.g. determine the volume of the impact sound to play (see: EstimateCollisionResponse). - virtual void OnContactAdded([[maybe_unused]] const Body &inBody1, [[maybe_unused]] const Body &inBody2, [[maybe_unused]] const ContactManifold &inManifold, [[maybe_unused]] ContactSettings &ioSettings) { /* Do nothing */ } - - /// Called whenever a contact is detected that was also detected last update. - /// Note that this callback is called when all bodies are locked, so don't use any locking functions! - /// Body 1 and 2 will be sorted such that body 1 ID < body 2 ID, so body 1 may not be dynamic. - /// If the structure of the shape of a body changes between simulation steps (e.g. by adding/removing a child shape of a compound shape), - /// it is possible that the same sub shape ID used to identify the removed child shape is now reused for a different child shape. The physics - /// system cannot detect this, so may send a 'contact persisted' callback even though the contact is now on a different child shape. You can - /// detect this by keeping the old shape (before adding/removing a part) around until the next PhysicsSystem::Update (when the OnContactPersisted - /// callbacks are triggered) and resolving the sub shape ID against both the old and new shape to see if they still refer to the same child shape. - virtual void OnContactPersisted([[maybe_unused]] const Body &inBody1, [[maybe_unused]] const Body &inBody2, [[maybe_unused]] const ContactManifold &inManifold, [[maybe_unused]] ContactSettings &ioSettings) { /* Do nothing */ } - - /// Called whenever a contact was detected last update but is not detected anymore. - /// Note that this callback is called when all bodies are locked, so don't use any locking functions! - /// Note that we're using BodyID's since the bodies may have been removed at the time of callback. - /// Body 1 and 2 will be sorted such that body 1 ID < body 2 ID, so body 1 may not be dynamic. - /// The sub shape ID were created in the previous simulation step too, so if the structure of a shape changes (e.g. by adding/removing a child shape of a compound shape), - /// the sub shape ID may not be valid / may not point to the same sub shape anymore. - /// If you want to know if this is the last contact between the two bodies, use PhysicsSystem::WereBodiesInContact. - virtual void OnContactRemoved([[maybe_unused]] const SubShapeIDPair &inSubShapePair) { /* Do nothing */ } -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/EstimateCollisionResponse.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/EstimateCollisionResponse.cpp deleted file mode 100644 index 53cf12b5d41..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/EstimateCollisionResponse.cpp +++ /dev/null @@ -1,213 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include - -JPH_NAMESPACE_BEGIN - -void EstimateCollisionResponse(const Body &inBody1, const Body &inBody2, const ContactManifold &inManifold, CollisionEstimationResult &outResult, float inCombinedFriction, float inCombinedRestitution, float inMinVelocityForRestitution, uint inNumIterations) -{ - // Note this code is based on AxisConstraintPart, see that class for more comments on the math - - ContactPoints::size_type num_points = inManifold.mRelativeContactPointsOn1.size(); - JPH_ASSERT(num_points == inManifold.mRelativeContactPointsOn2.size()); - - // Start with zero impulses - outResult.mImpulses.resize(num_points); - memset(outResult.mImpulses.data(), 0, num_points * sizeof(CollisionEstimationResult::Impulse)); - - // Calculate friction directions - outResult.mTangent1 = inManifold.mWorldSpaceNormal.GetNormalizedPerpendicular(); - outResult.mTangent2 = inManifold.mWorldSpaceNormal.Cross(outResult.mTangent1); - - // Get body velocities - EMotionType motion_type1 = inBody1.GetMotionType(); - const MotionProperties *motion_properties1 = inBody1.GetMotionPropertiesUnchecked(); - if (motion_type1 != EMotionType::Static) - { - outResult.mLinearVelocity1 = motion_properties1->GetLinearVelocity(); - outResult.mAngularVelocity1 = motion_properties1->GetAngularVelocity(); - } - else - outResult.mLinearVelocity1 = outResult.mAngularVelocity1 = Vec3::sZero(); - - EMotionType motion_type2 = inBody2.GetMotionType(); - const MotionProperties *motion_properties2 = inBody2.GetMotionPropertiesUnchecked(); - if (motion_type2 != EMotionType::Static) - { - outResult.mLinearVelocity2 = motion_properties2->GetLinearVelocity(); - outResult.mAngularVelocity2 = motion_properties2->GetAngularVelocity(); - } - else - outResult.mLinearVelocity2 = outResult.mAngularVelocity2 = Vec3::sZero(); - - // Get inverse mass and inertia - float inv_m1, inv_m2; - Mat44 inv_i1, inv_i2; - if (motion_type1 == EMotionType::Dynamic) - { - inv_m1 = motion_properties1->GetInverseMass(); - inv_i1 = inBody1.GetInverseInertia(); - } - else - { - inv_m1 = 0.0f; - inv_i1 = Mat44::sZero(); - } - - if (motion_type2 == EMotionType::Dynamic) - { - inv_m2 = motion_properties2->GetInverseMass(); - inv_i2 = inBody2.GetInverseInertia(); - } - else - { - inv_m2 = 0.0f; - inv_i2 = Mat44::sZero(); - } - - // Get center of masses relative to the base offset - Vec3 com1 = Vec3(inBody1.GetCenterOfMassPosition() - inManifold.mBaseOffset); - Vec3 com2 = Vec3(inBody2.GetCenterOfMassPosition() - inManifold.mBaseOffset); - - struct AxisConstraint - { - inline void Initialize(Vec3Arg inR1, Vec3Arg inR2, Vec3Arg inWorldSpaceNormal, float inInvM1, float inInvM2, Mat44Arg inInvI1, Mat44Arg inInvI2) - { - // Calculate effective mass: K^-1 = (J M^-1 J^T)^-1 - mR1PlusUxAxis = inR1.Cross(inWorldSpaceNormal); - mR2xAxis = inR2.Cross(inWorldSpaceNormal); - mInvI1_R1PlusUxAxis = inInvI1.Multiply3x3(mR1PlusUxAxis); - mInvI2_R2xAxis = inInvI2.Multiply3x3(mR2xAxis); - mEffectiveMass = 1.0f / (inInvM1 + mInvI1_R1PlusUxAxis.Dot(mR1PlusUxAxis) + inInvM2 + mInvI2_R2xAxis.Dot(mR2xAxis)); - mBias = 0.0f; - } - - inline float SolveGetLambda(Vec3Arg inWorldSpaceNormal, const CollisionEstimationResult &inResult) const - { - // Calculate jacobian multiplied by linear/angular velocity - float jv = inWorldSpaceNormal.Dot(inResult.mLinearVelocity1 - inResult.mLinearVelocity2) + mR1PlusUxAxis.Dot(inResult.mAngularVelocity1) - mR2xAxis.Dot(inResult.mAngularVelocity2); - - // Lagrange multiplier is: - // - // lambda = -K^-1 (J v + b) - return mEffectiveMass * (jv - mBias); - } - - inline void SolveApplyLambda(Vec3Arg inWorldSpaceNormal, float inInvM1, float inInvM2, float inLambda, CollisionEstimationResult &ioResult) const - { - // Apply impulse to body velocities - ioResult.mLinearVelocity1 -= (inLambda * inInvM1) * inWorldSpaceNormal; - ioResult.mAngularVelocity1 -= inLambda * mInvI1_R1PlusUxAxis; - ioResult.mLinearVelocity2 += (inLambda * inInvM2) * inWorldSpaceNormal; - ioResult.mAngularVelocity2 += inLambda * mInvI2_R2xAxis; - } - - inline void Solve(Vec3Arg inWorldSpaceNormal, float inInvM1, float inInvM2, float inMinLambda, float inMaxLambda, float &ioTotalLambda, CollisionEstimationResult &ioResult) const - { - // Calculate new total lambda - float total_lambda = ioTotalLambda + SolveGetLambda(inWorldSpaceNormal, ioResult); - - // Clamp impulse - total_lambda = Clamp(total_lambda, inMinLambda, inMaxLambda); - - SolveApplyLambda(inWorldSpaceNormal, inInvM1, inInvM2, total_lambda - ioTotalLambda, ioResult); - - ioTotalLambda = total_lambda; - } - - Vec3 mR1PlusUxAxis; - Vec3 mR2xAxis; - Vec3 mInvI1_R1PlusUxAxis; - Vec3 mInvI2_R2xAxis; - float mEffectiveMass; - float mBias; - }; - - struct Constraint - { - AxisConstraint mContact; - AxisConstraint mFriction1; - AxisConstraint mFriction2; - }; - - // Initialize the constraint properties - Constraint constraints[ContactPoints::Capacity]; - for (uint c = 0; c < num_points; ++c) - { - Constraint &constraint = constraints[c]; - - // Calculate contact points relative to body 1 and 2 - Vec3 p = 0.5f * (inManifold.mRelativeContactPointsOn1[c] + inManifold.mRelativeContactPointsOn2[c]); - Vec3 r1 = p - com1; - Vec3 r2 = p - com2; - - // Initialize contact constraint - constraint.mContact.Initialize(r1, r2, inManifold.mWorldSpaceNormal, inv_m1, inv_m2, inv_i1, inv_i2); - - // Handle elastic collisions - if (inCombinedRestitution > 0.0f) - { - // Calculate velocity of contact point - Vec3 relative_velocity = outResult.mLinearVelocity2 + outResult.mAngularVelocity2.Cross(r2) - outResult.mLinearVelocity1 - outResult.mAngularVelocity1.Cross(r1); - float normal_velocity = relative_velocity.Dot(inManifold.mWorldSpaceNormal); - - // If it is big enough, apply restitution - if (normal_velocity < -inMinVelocityForRestitution) - constraint.mContact.mBias = inCombinedRestitution * normal_velocity; - } - - if (inCombinedFriction > 0.0f) - { - // Initialize friction constraints - constraint.mFriction1.Initialize(r1, r2, outResult.mTangent1, inv_m1, inv_m2, inv_i1, inv_i2); - constraint.mFriction2.Initialize(r1, r2, outResult.mTangent2, inv_m1, inv_m2, inv_i1, inv_i2); - } - } - - // If there's only 1 contact point, we only need 1 iteration - int num_iterations = inCombinedFriction <= 0.0f && num_points == 1? 1 : inNumIterations; - - // Solve iteratively - for (int iteration = 0; iteration < num_iterations; ++iteration) - { - // Solve friction constraints first - if (inCombinedFriction > 0.0f && iteration > 0) // For first iteration the contact impulse is zero so there's no point in applying friction - for (uint c = 0; c < num_points; ++c) - { - const Constraint &constraint = constraints[c]; - CollisionEstimationResult::Impulse &impulse = outResult.mImpulses[c]; - - float lambda1 = impulse.mFrictionImpulse1 + constraint.mFriction1.SolveGetLambda(outResult.mTangent1, outResult); - float lambda2 = impulse.mFrictionImpulse2 + constraint.mFriction2.SolveGetLambda(outResult.mTangent2, outResult); - - // Calculate max impulse based on contact impulse - float max_impulse = inCombinedFriction * impulse.mContactImpulse; - - // If the total lambda that we will apply is too large, scale it back - float total_lambda_sq = Square(lambda1) + Square(lambda2); - if (total_lambda_sq > Square(max_impulse)) - { - float scale = max_impulse / sqrt(total_lambda_sq); - lambda1 *= scale; - lambda2 *= scale; - } - - constraint.mFriction1.SolveApplyLambda(outResult.mTangent1, inv_m1, inv_m2, lambda1 - impulse.mFrictionImpulse1, outResult); - constraint.mFriction2.SolveApplyLambda(outResult.mTangent2, inv_m1, inv_m2, lambda2 - impulse.mFrictionImpulse2, outResult); - - impulse.mFrictionImpulse1 = lambda1; - impulse.mFrictionImpulse2 = lambda2; - } - - // Solve contact constraints last - for (uint c = 0; c < num_points; ++c) - constraints[c].mContact.Solve(inManifold.mWorldSpaceNormal, inv_m1, inv_m2, 0.0f, FLT_MAX, outResult.mImpulses[c].mContactImpulse, outResult); - } -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/EstimateCollisionResponse.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/EstimateCollisionResponse.h deleted file mode 100644 index 45098d1477e..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/EstimateCollisionResponse.h +++ /dev/null @@ -1,48 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2023 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -/// A structure that contains the estimated contact and friction impulses and the resulting body velocities -struct CollisionEstimationResult -{ - Vec3 mLinearVelocity1; ///< The estimated linear velocity of body 1 after collision - Vec3 mAngularVelocity1; ///< The estimated angular velocity of body 1 after collision - Vec3 mLinearVelocity2; ///< The estimated linear velocity of body 2 after collision - Vec3 mAngularVelocity2; ///< The estimated angular velocity of body 2 after collision - - Vec3 mTangent1; ///< Normalized tangent of contact normal - Vec3 mTangent2; ///< Second normalized tangent of contact normal (forms a basis with mTangent1 and mWorldSpaceNormal) - - struct Impulse - { - float mContactImpulse; ///< Estimated contact impulses (kg m / s) - float mFrictionImpulse1; ///< Estimated friction impulses in the direction of tangent 1 (kg m / s) - float mFrictionImpulse2; ///< Estimated friction impulses in the direction of tangent 2 (kg m / s) - }; - - using Impulses = StaticArray; - - Impulses mImpulses; -}; - -/// This function estimates the contact impulses and body velocity changes as a result of a collision. -/// It can be used in the ContactListener::OnContactAdded to determine the strength of the collision to e.g. play a sound or trigger a particle system. -/// This function is accurate when two bodies collide but will not be accurate when more than 2 bodies collide at the same time as it does not know about these other collisions. -/// -/// @param inBody1 Colliding body 1 -/// @param inBody2 Colliding body 2 -/// @param inManifold The collision manifold -/// @param outResult A structure that contains the estimated contact and friction impulses and the resulting body velocities -/// @param inCombinedFriction The combined friction of body 1 and body 2 (see ContactSettings::mCombinedFriction) -/// @param inCombinedRestitution The combined restitution of body 1 and body 2 (see ContactSettings::mCombinedRestitution) -/// @param inMinVelocityForRestitution Minimal velocity required for restitution to be applied (see PhysicsSettings::mMinVelocityForRestitution) -/// @param inNumIterations Number of iterations to use for the impulse estimation (see PhysicsSettings::mNumVelocitySteps, note you can probably use a lower number for a decent estimate). If you set the number of iterations to 1 then no friction will be calculated. -JPH_EXPORT void EstimateCollisionResponse(const Body &inBody1, const Body &inBody2, const ContactManifold &inManifold, CollisionEstimationResult &outResult, float inCombinedFriction, float inCombinedRestitution, float inMinVelocityForRestitution = 1.0f, uint inNumIterations = 10); - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/GroupFilter.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/GroupFilter.cpp deleted file mode 100644 index 80c7620fdad..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/GroupFilter.cpp +++ /dev/null @@ -1,32 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include - -JPH_NAMESPACE_BEGIN - -JPH_IMPLEMENT_SERIALIZABLE_ABSTRACT_BASE(GroupFilter) -{ - JPH_ADD_BASE_CLASS(GroupFilter, SerializableObject) -} - -void GroupFilter::SaveBinaryState(StreamOut &inStream) const -{ - inStream.Write(GetRTTI()->GetHash()); -} - -void GroupFilter::RestoreBinaryState(StreamIn &inStream) -{ - // RTTI hash is read in sRestoreFromBinaryState -} - -GroupFilter::GroupFilterResult GroupFilter::sRestoreFromBinaryState(StreamIn &inStream) -{ - return StreamUtils::RestoreObject(inStream, &GroupFilter::RestoreBinaryState); -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/GroupFilter.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/GroupFilter.h deleted file mode 100644 index f0ad835e008..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/GroupFilter.h +++ /dev/null @@ -1,41 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include - -JPH_NAMESPACE_BEGIN - -class CollisionGroup; -class StreamIn; -class StreamOut; - -/// Abstract class that checks if two CollisionGroups collide -class JPH_EXPORT GroupFilter : public SerializableObject, public RefTarget -{ -public: - JPH_DECLARE_SERIALIZABLE_ABSTRACT(JPH_EXPORT, GroupFilter) - - /// Virtual destructor - virtual ~GroupFilter() override = default; - - /// Check if two groups collide - virtual bool CanCollide(const CollisionGroup &inGroup1, const CollisionGroup &inGroup2) const = 0; - - /// Saves the contents of the group filter in binary form to inStream. - virtual void SaveBinaryState(StreamOut &inStream) const; - - using GroupFilterResult = Result>; - - /// Creates a GroupFilter of the correct type and restores its contents from the binary stream inStream. - static GroupFilterResult sRestoreFromBinaryState(StreamIn &inStream); - -protected: - /// This function should not be called directly, it is used by sRestoreFromBinaryState. - virtual void RestoreBinaryState(StreamIn &inStream); -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/GroupFilterTable.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/GroupFilterTable.cpp deleted file mode 100644 index dd69d27c852..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/GroupFilterTable.cpp +++ /dev/null @@ -1,38 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(GroupFilterTable) -{ - JPH_ADD_BASE_CLASS(GroupFilterTable, GroupFilter) - - JPH_ADD_ATTRIBUTE(GroupFilterTable, mNumSubGroups) - JPH_ADD_ATTRIBUTE(GroupFilterTable, mTable) -} - -void GroupFilterTable::SaveBinaryState(StreamOut &inStream) const -{ - GroupFilter::SaveBinaryState(inStream); - - inStream.Write(mNumSubGroups); - inStream.Write(mTable); -} - -void GroupFilterTable::RestoreBinaryState(StreamIn &inStream) -{ - GroupFilter::RestoreBinaryState(inStream); - - inStream.Read(mNumSubGroups); - inStream.Read(mTable); -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/GroupFilterTable.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/GroupFilterTable.h deleted file mode 100644 index 62ce34ce55f..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/GroupFilterTable.h +++ /dev/null @@ -1,130 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include - -JPH_NAMESPACE_BEGIN - -/// Implementation of GroupFilter that stores a bit table with one bit per sub shape ID pair to determine if they collide or not -/// -/// The collision rules: -/// - If one of the objects is in the cInvalidGroup the objects will collide. -/// - If the objects are in different groups they will collide. -/// - If they're in the same group but their collision filter is different they will not collide. -/// - If they're in the same group and their collision filters match, we'll use the SubGroupID and the table below. -/// -/// For N = 6 sub groups the table will look like: -/// -/// sub group 1 ---> -/// sub group 2 x..... -/// | ox.... -/// | oox... -/// V ooox.. -/// oooox. -/// ooooox -/// -/// * 'x' means sub group 1 == sub group 2 and we define this to never collide. -/// * 'o' is a bit that we have to store that defines if the sub groups collide or not. -/// * '.' is a bit we don't need to store because the table is symmetric, we take care that group 2 > group 1 by swapping sub group 1 and sub group 2 if needed. -/// -/// The total number of bits we need to store is (N * (N - 1)) / 2 -class JPH_EXPORT GroupFilterTable final : public GroupFilter -{ - JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, GroupFilterTable) - -private: - using GroupID = CollisionGroup::GroupID; - using SubGroupID = CollisionGroup::SubGroupID; - - /// Get which bit corresponds to the pair (inSubGroup1, inSubGroup2) - int GetBit(SubGroupID inSubGroup1, SubGroupID inSubGroup2) const - { - JPH_ASSERT(inSubGroup1 != inSubGroup2); - - // We store the lower left half only, so swap the inputs when trying to access the top right half - if (inSubGroup1 > inSubGroup2) - swap(inSubGroup1, inSubGroup2); - - JPH_ASSERT(inSubGroup2 < mNumSubGroups); - - // Calculate at which bit the entry for this pair resides - // We use the fact that a row always starts at inSubGroup2 * (inSubGroup2 - 1) / 2 - // (this is the amount of bits needed to store a table of inSubGroup2 entries) - return (inSubGroup2 * (inSubGroup2 - 1)) / 2 + inSubGroup1; - } - -public: - /// Constructs the table with inNumSubGroups subgroups, initially all collision pairs are enabled except when the sub group ID is the same - explicit GroupFilterTable(uint inNumSubGroups = 0) : - mNumSubGroups(inNumSubGroups) - { - // By default everything collides - int table_size = ((inNumSubGroups * (inNumSubGroups - 1)) / 2 + 7) / 8; - mTable.resize(table_size, 0xff); - } - - /// Copy constructor - GroupFilterTable(const GroupFilterTable &inRHS) : mNumSubGroups(inRHS.mNumSubGroups), mTable(inRHS.mTable) { } - - /// Disable collision between two sub groups - void DisableCollision(SubGroupID inSubGroup1, SubGroupID inSubGroup2) - { - int bit = GetBit(inSubGroup1, inSubGroup2); - mTable[bit >> 3] &= (0xff ^ (1 << (bit & 0b111))); - } - - /// Enable collision between two sub groups - void EnableCollision(SubGroupID inSubGroup1, SubGroupID inSubGroup2) - { - int bit = GetBit(inSubGroup1, inSubGroup2); - mTable[bit >> 3] |= 1 << (bit & 0b111); - } - - /// Check if the collision between two subgroups is enabled - inline bool IsCollisionEnabled(SubGroupID inSubGroup1, SubGroupID inSubGroup2) const - { - // Test if the bit is set for this group pair - int bit = GetBit(inSubGroup1, inSubGroup2); - return (mTable[bit >> 3] & (1 << (bit & 0b111))) != 0; - } - - /// Checks if two CollisionGroups collide - virtual bool CanCollide(const CollisionGroup &inGroup1, const CollisionGroup &inGroup2) const override - { - // If one of the groups is cInvalidGroup the objects will collide (note that the if following this if will ensure that group2 is not cInvalidGroup) - if (inGroup1.GetGroupID() == CollisionGroup::cInvalidGroup) - return true; - - // If the objects are in different groups, they collide - if (inGroup1.GetGroupID() != inGroup2.GetGroupID()) - return true; - - // If the collision filters do not match, but they're in the same group we ignore the collision - if (inGroup1.GetGroupFilter() != inGroup2.GetGroupFilter()) - return false; - - // If they are in the same sub group, they don't collide - if (inGroup1.GetSubGroupID() == inGroup2.GetSubGroupID()) - return false; - - // Check the bit table - return IsCollisionEnabled(inGroup1.GetSubGroupID(), inGroup2.GetSubGroupID()); - } - - // See: GroupFilter::SaveBinaryState - virtual void SaveBinaryState(StreamOut &inStream) const override; - -protected: - // See: GroupFilter::RestoreBinaryState - virtual void RestoreBinaryState(StreamIn &inStream) override; - -private: - uint mNumSubGroups; ///< The number of subgroups that this group filter supports - Array mTable; ///< The table of bits that indicates which pairs collide -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/InternalEdgeRemovingCollector.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/InternalEdgeRemovingCollector.h deleted file mode 100644 index 46e37534c72..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/InternalEdgeRemovingCollector.h +++ /dev/null @@ -1,233 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2024 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include - -//#define JPH_INTERNAL_EDGE_REMOVING_COLLECTOR_DEBUG - -#ifdef JPH_INTERNAL_EDGE_REMOVING_COLLECTOR_DEBUG -#include -#endif // JPH_INTERNAL_EDGE_REMOVING_COLLECTOR_DEBUG - -JPH_NAMESPACE_BEGIN - -/// Removes internal edges from collision results. Can be used to filter out 'ghost collisions'. -/// Based on: Contact generation for meshes - Pierre Terdiman (https://www.codercorner.com/MeshContacts.pdf) -class InternalEdgeRemovingCollector : public CollideShapeCollector -{ - static constexpr uint cMaxDelayedResults = 16; - static constexpr uint cMaxVoidedFeatures = 128; - - /// Check if a vertex is voided - inline bool IsVoided(Vec3 inV) const - { - for (const Float3 &vf : mVoidedFeatures) - if (inV.IsClose(Vec3::sLoadFloat3Unsafe(vf), 1.0e-8f)) - return true; - return false; - } - - /// Add all vertices of a face to the voided features - inline void VoidFeatures(const CollideShapeResult &inResult) - { - for (const Vec3 &v : inResult.mShape2Face) - if (!IsVoided(v)) - { - if (mVoidedFeatures.size() == cMaxVoidedFeatures) - break; - Float3 f; - v.StoreFloat3(&f); - mVoidedFeatures.push_back(f); - } - } - - /// Call the chained collector - inline void Chain(const CollideShapeResult &inResult) - { - // Make sure the chained collector has the same context as we do - mChainedCollector.SetContext(GetContext()); - - // Forward the hit - mChainedCollector.AddHit(inResult); - - // If our chained collector updated its early out fraction, we need to follow - UpdateEarlyOutFraction(mChainedCollector.GetEarlyOutFraction()); - } - - /// Call the chained collector and void all features of inResult - inline void ChainAndVoid(const CollideShapeResult &inResult) - { - Chain(inResult); - VoidFeatures(inResult); - - #ifdef JPH_INTERNAL_EDGE_REMOVING_COLLECTOR_DEBUG - DebugRenderer::sInstance->DrawWirePolygon(RMat44::sIdentity(), inResult.mShape2Face, Color::sGreen); - DebugRenderer::sInstance->DrawArrow(RVec3(inResult.mContactPointOn2), RVec3(inResult.mContactPointOn2) + inResult.mPenetrationAxis.NormalizedOr(Vec3::sZero()), Color::sGreen, 0.1f); - #endif // JPH_INTERNAL_EDGE_REMOVING_COLLECTOR_DEBUG - } - -public: - /// Constructor, configures a collector to be called with all the results that do not hit internal edges - explicit InternalEdgeRemovingCollector(CollideShapeCollector &inChainedCollector) : - mChainedCollector(inChainedCollector) - { - } - - // See: CollideShapeCollector::Reset - virtual void Reset() override - { - CollideShapeCollector::Reset(); - - mChainedCollector.Reset(); - - mVoidedFeatures.clear(); - mDelayedResults.clear(); - } - - // See: CollideShapeCollector::OnBody - virtual void OnBody(const Body &inBody) override - { - // Just forward the call to our chained collector - mChainedCollector.OnBody(inBody); - } - - // See: CollideShapeCollector::AddHit - virtual void AddHit(const CollideShapeResult &inResult) override - { - // We only support welding when the shape is a triangle or has more vertices so that we can calculate a normal - if (inResult.mShape2Face.size() < 3) - return ChainAndVoid(inResult); - - // Get the triangle normal of shape 2 face - Vec3 triangle_normal = (inResult.mShape2Face[1] - inResult.mShape2Face[0]).Cross(inResult.mShape2Face[2] - inResult.mShape2Face[0]); - float triangle_normal_len = triangle_normal.Length(); - if (triangle_normal_len < 1e-6f) - return ChainAndVoid(inResult); - - // If the triangle normal matches the contact normal within 1 degree, we can process the contact immediately - // We make the assumption here that if the contact normal and the triangle normal align that the we're dealing with a 'face contact' - Vec3 contact_normal = -inResult.mPenetrationAxis; - float contact_normal_len = inResult.mPenetrationAxis.Length(); - if (triangle_normal.Dot(contact_normal) > 0.999848f * contact_normal_len * triangle_normal_len) // cos(1 degree) - return ChainAndVoid(inResult); - - // Delayed processing - if (mDelayedResults.size() == cMaxDelayedResults) - return ChainAndVoid(inResult); - mDelayedResults.push_back(inResult); - } - - /// After all hits have been added, call this function to process the delayed results - void Flush() - { - // Sort on biggest penetration depth first - uint sorted_indices[cMaxDelayedResults]; - for (uint i = 0; i < uint(mDelayedResults.size()); ++i) - sorted_indices[i] = i; - QuickSort(sorted_indices, sorted_indices + mDelayedResults.size(), [this](uint inLHS, uint inRHS) { return mDelayedResults[inLHS].mPenetrationDepth > mDelayedResults[inRHS].mPenetrationDepth; }); - - // Loop over all results - for (uint i = 0; i < uint(mDelayedResults.size()); ++i) - { - const CollideShapeResult &r = mDelayedResults[sorted_indices[i]]; - - // Determine which vertex or which edge is the closest to the contact point - float best_dist_sq = FLT_MAX; - uint best_v1_idx = 0; - uint best_v2_idx = 0; - uint num_v = uint(r.mShape2Face.size()); - uint v1_idx = num_v - 1; - Vec3 v1 = r.mShape2Face[v1_idx] - r.mContactPointOn2; - for (uint v2_idx = 0; v2_idx < num_v; ++v2_idx) - { - Vec3 v2 = r.mShape2Face[v2_idx] - r.mContactPointOn2; - Vec3 v1_v2 = v2 - v1; - float denominator = v1_v2.LengthSq(); - if (denominator < Square(FLT_EPSILON)) - { - // Degenerate, assume v1 is closest, v2 will be tested in a later iteration - float v1_len_sq = v1.LengthSq(); - if (v1_len_sq < best_dist_sq) - { - best_dist_sq = v1_len_sq; - best_v1_idx = v1_idx; - best_v2_idx = v1_idx; - } - } - else - { - // Taken from ClosestPoint::GetBaryCentricCoordinates - float fraction = -v1.Dot(v1_v2) / denominator; - if (fraction < 1.0e-6f) - { - // Closest lies on v1 - float v1_len_sq = v1.LengthSq(); - if (v1_len_sq < best_dist_sq) - { - best_dist_sq = v1_len_sq; - best_v1_idx = v1_idx; - best_v2_idx = v1_idx; - } - } - else if (fraction < 1.0f - 1.0e-6f) - { - // Closest lies on the line segment v1, v2 - Vec3 closest = v1 + fraction * v1_v2; - float closest_len_sq = closest.LengthSq(); - if (closest_len_sq < best_dist_sq) - { - best_dist_sq = closest_len_sq; - best_v1_idx = v1_idx; - best_v2_idx = v2_idx; - } - } - // else closest is v2, but v2 will be tested in a later iteration - } - - v1_idx = v2_idx; - v1 = v2; - } - - // Check if this vertex/edge is voided - bool voided = IsVoided(r.mShape2Face[best_v1_idx]) - && (best_v1_idx == best_v2_idx || IsVoided(r.mShape2Face[best_v2_idx])); - - #ifdef JPH_INTERNAL_EDGE_REMOVING_COLLECTOR_DEBUG - Color color = voided? Color::sRed : Color::sYellow; - DebugRenderer::sInstance->DrawText3D(RVec3(r.mContactPointOn2), StringFormat("%d: %g", i, r.mPenetrationDepth), color, 0.1f); - DebugRenderer::sInstance->DrawWirePolygon(RMat44::sIdentity(), r.mShape2Face, color); - DebugRenderer::sInstance->DrawArrow(RVec3(r.mContactPointOn2), RVec3(r.mContactPointOn2) + r.mPenetrationAxis.NormalizedOr(Vec3::sZero()), color, 0.1f); - DebugRenderer::sInstance->DrawMarker(RVec3(r.mShape2Face[best_v1_idx]), IsVoided(r.mShape2Face[best_v1_idx])? Color::sRed : Color::sYellow, 0.1f); - DebugRenderer::sInstance->DrawMarker(RVec3(r.mShape2Face[best_v2_idx]), IsVoided(r.mShape2Face[best_v2_idx])? Color::sRed : Color::sYellow, 0.1f); - #endif // JPH_INTERNAL_EDGE_REMOVING_COLLECTOR_DEBUG - - // No voided features, accept the contact - if (!voided) - Chain(r); - - // Void the features of this face - VoidFeatures(r); - } - } - - /// Version of CollisionDispatch::sCollideShapeVsShape that removes internal edges - static void sCollideShapeVsShape(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter = { }) - { - JPH_ASSERT(inCollideShapeSettings.mCollectFacesMode == ECollectFacesMode::CollectFaces); // Won't work without collecting faces - - InternalEdgeRemovingCollector wrapper(ioCollector); - CollisionDispatch::sCollideShapeVsShape(inShape1, inShape2, inScale1, inScale2, inCenterOfMassTransform1, inCenterOfMassTransform2, inSubShapeIDCreator1, inSubShapeIDCreator2, inCollideShapeSettings, wrapper, inShapeFilter); - wrapper.Flush(); - } - -private: - CollideShapeCollector & mChainedCollector; - StaticArray mVoidedFeatures; // Read with Vec3::sLoadFloat3Unsafe so must not be the last member - StaticArray mDelayedResults; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ManifoldBetweenTwoFaces.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ManifoldBetweenTwoFaces.cpp deleted file mode 100644 index 3331f5d7a03..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ManifoldBetweenTwoFaces.cpp +++ /dev/null @@ -1,237 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include -#ifdef JPH_DEBUG_RENDERER - #include -#endif // JPH_DEBUG_RENDERER - -JPH_NAMESPACE_BEGIN - -void PruneContactPoints(Vec3Arg inPenetrationAxis, ContactPoints &ioContactPointsOn1, ContactPoints &ioContactPointsOn2 JPH_IF_DEBUG_RENDERER(, RVec3Arg inCenterOfMass)) -{ - // Makes no sense to call this with 4 or less points - JPH_ASSERT(ioContactPointsOn1.size() > 4); - - // Both arrays should have the same size - JPH_ASSERT(ioContactPointsOn1.size() == ioContactPointsOn2.size()); - - // Penetration axis must be normalized - JPH_ASSERT(inPenetrationAxis.IsNormalized()); - - // We use a heuristic of (distance to center of mass) * (penetration depth) to find the contact point that we should keep - // Neither of those two terms should ever become zero, so we clamp against this minimum value - constexpr float cMinDistanceSq = 1.0e-6f; // 1 mm - - ContactPoints projected; - StaticArray penetration_depth_sq; - for (ContactPoints::size_type i = 0; i < ioContactPointsOn1.size(); ++i) - { - // Project contact points on the plane through inCenterOfMass with normal inPenetrationAxis and center around the center of mass of body 1 - // (note that since all points are relative to inCenterOfMass we can project onto the plane through the origin) - Vec3 v1 = ioContactPointsOn1[i]; - projected.push_back(v1 - v1.Dot(inPenetrationAxis) * inPenetrationAxis); - - // Calculate penetration depth^2 of each point and clamp against the minimal distance - Vec3 v2 = ioContactPointsOn2[i]; - penetration_depth_sq.push_back(max(cMinDistanceSq, (v2 - v1).LengthSq())); - } - - // Find the point that is furthest away from the center of mass (its torque will have the biggest influence) - // and the point that has the deepest penetration depth. Use the heuristic (distance to center of mass) * (penetration depth) for this. - uint point1 = 0; - float val = max(cMinDistanceSq, projected[0].LengthSq()) * penetration_depth_sq[0]; - for (uint i = 0; i < projected.size(); ++i) - { - float v = max(cMinDistanceSq, projected[i].LengthSq()) * penetration_depth_sq[i]; - if (v > val) - { - val = v; - point1 = i; - } - } - Vec3 point1v = projected[point1]; - - // Find point furthest from the first point forming a line segment with point1. Again combine this with the heuristic - // for deepest point as per above. - uint point2 = uint(-1); - val = -FLT_MAX; - for (uint i = 0; i < projected.size(); ++i) - if (i != point1) - { - float v = max(cMinDistanceSq, (projected[i] - point1v).LengthSq()) * penetration_depth_sq[i]; - if (v > val) - { - val = v; - point2 = i; - } - } - JPH_ASSERT(point2 != uint(-1)); - Vec3 point2v = projected[point2]; - - // Find furthest points on both sides of the line segment in order to maximize the area - uint point3 = uint(-1); - uint point4 = uint(-1); - float min_val = 0.0f; - float max_val = 0.0f; - Vec3 perp = (point2v - point1v).Cross(inPenetrationAxis); - for (uint i = 0; i < projected.size(); ++i) - if (i != point1 && i != point2) - { - float v = perp.Dot(projected[i] - point1v); - if (v < min_val) - { - min_val = v; - point3 = i; - } - else if (v > max_val) - { - max_val = v; - point4 = i; - } - } - - // Add points to array (in order so they form a polygon) - StaticArray points_to_keep_on_1, points_to_keep_on_2; - points_to_keep_on_1.push_back(ioContactPointsOn1[point1]); - points_to_keep_on_2.push_back(ioContactPointsOn2[point1]); - if (point3 != uint(-1)) - { - points_to_keep_on_1.push_back(ioContactPointsOn1[point3]); - points_to_keep_on_2.push_back(ioContactPointsOn2[point3]); - } - points_to_keep_on_1.push_back(ioContactPointsOn1[point2]); - points_to_keep_on_2.push_back(ioContactPointsOn2[point2]); - if (point4 != uint(-1)) - { - JPH_ASSERT(point3 != point4); - points_to_keep_on_1.push_back(ioContactPointsOn1[point4]); - points_to_keep_on_2.push_back(ioContactPointsOn2[point4]); - } - -#ifdef JPH_DEBUG_RENDERER - if (ContactConstraintManager::sDrawContactPointReduction) - { - // Draw input polygon - DebugRenderer::sInstance->DrawWirePolygon(RMat44::sTranslation(inCenterOfMass), ioContactPointsOn1, Color::sOrange, 0.05f); - - // Draw primary axis - DebugRenderer::sInstance->DrawArrow(inCenterOfMass + ioContactPointsOn1[point1], inCenterOfMass + ioContactPointsOn1[point2], Color::sRed, 0.05f); - - // Draw contact points we kept - for (Vec3 p : points_to_keep_on_1) - DebugRenderer::sInstance->DrawMarker(inCenterOfMass + p, Color::sGreen, 0.1f); - } -#endif // JPH_DEBUG_RENDERER - - // Copy the points back to the input buffer - ioContactPointsOn1 = points_to_keep_on_1; - ioContactPointsOn2 = points_to_keep_on_2; -} - -void ManifoldBetweenTwoFaces(Vec3Arg inContactPoint1, Vec3Arg inContactPoint2, Vec3Arg inPenetrationAxis, float inMaxContactDistanceSq , const ConvexShape::SupportingFace &inShape1Face, const ConvexShape::SupportingFace &inShape2Face, ContactPoints &outContactPoints1, ContactPoints &outContactPoints2 JPH_IF_DEBUG_RENDERER(, RVec3Arg inCenterOfMass)) -{ -#ifdef JPH_DEBUG_RENDERER - if (ContactConstraintManager::sDrawContactPoint) - { - RVec3 cp1 = inCenterOfMass + inContactPoint1; - RVec3 cp2 = inCenterOfMass + inContactPoint2; - - // Draw contact points - DebugRenderer::sInstance->DrawMarker(cp1, Color::sRed, 0.1f); - DebugRenderer::sInstance->DrawMarker(cp2, Color::sGreen, 0.1f); - - // Draw contact normal - DebugRenderer::sInstance->DrawArrow(cp1, cp1 + inPenetrationAxis.Normalized(), Color::sRed, 0.05f); - } -#endif // JPH_DEBUG_RENDERER - - // Remember size before adding new points, to check at the end if we added some - ContactPoints::size_type old_size = outContactPoints1.size(); - - // Check if both shapes have polygon faces - if (inShape1Face.size() >= 2 // The dynamic shape needs to have at least 2 points or else there can never be more than 1 contact point - && inShape2Face.size() >= 3) // The dynamic/static shape needs to have at least 3 points (in the case that it has 2 points only if the edges match exactly you can have 2 contact points, but this situation is unstable anyhow) - { - // Clip the polygon of face 2 against that of 1 - ConvexShape::SupportingFace clipped_face; - if (inShape1Face.size() >= 3) - ClipPolyVsPoly(inShape2Face, inShape1Face, inPenetrationAxis, clipped_face); - else if (inShape1Face.size() == 2) - ClipPolyVsEdge(inShape2Face, inShape1Face[0], inShape1Face[1], inPenetrationAxis, clipped_face); - - // Project the points back onto the plane of shape 1 face and only keep those that are behind the plane - Vec3 plane_origin = inShape1Face[0]; - Vec3 plane_normal; - Vec3 first_edge = inShape1Face[1] - plane_origin; - if (inShape1Face.size() >= 3) - { - // Three vertices, can just calculate the normal - plane_normal = first_edge.Cross(inShape1Face[2] - plane_origin); - } - else - { - // Two vertices, first find a perpendicular to the edge and penetration axis and then use the perpendicular together with the edge to form a normal - plane_normal = first_edge.Cross(inPenetrationAxis).Cross(first_edge); - } - - // Check if the plane normal has any length, if not the clipped shape is so small that we'll just use the contact points - float plane_normal_len_sq = plane_normal.LengthSq(); - if (plane_normal_len_sq > 0.0f) - { - // Discard points of faces that are too far away to collide - for (Vec3 p2 : clipped_face) - { - float distance = (p2 - plane_origin).Dot(plane_normal); // Note should divide by length of plane_normal (unnormalized here) - if (distance <= 0.0f || Square(distance) < inMaxContactDistanceSq * plane_normal_len_sq) // Must be close enough to plane, note we correct for not dividing by plane normal length here - { - // Project point back on shape 1 using the normal, note we correct for not dividing by plane normal length here: - // p1 = p2 - (distance / sqrt(plane_normal_len_sq)) * (plane_normal / sqrt(plane_normal_len_sq)); - Vec3 p1 = p2 - (distance / plane_normal_len_sq) * plane_normal; - - outContactPoints1.push_back(p1); - outContactPoints2.push_back(p2); - } - } - } - - #ifdef JPH_DEBUG_RENDERER - if (ContactConstraintManager::sDrawSupportingFaces) - { - RMat44 com = RMat44::sTranslation(inCenterOfMass); - - // Draw clipped poly - DebugRenderer::sInstance->DrawWirePolygon(com, clipped_face, Color::sOrange); - - // Draw supporting faces - DebugRenderer::sInstance->DrawWirePolygon(com, inShape1Face, Color::sRed, 0.05f); - DebugRenderer::sInstance->DrawWirePolygon(com, inShape2Face, Color::sGreen, 0.05f); - - // Draw normal - if (plane_normal_len_sq > 0.0f) - { - RVec3 plane_origin_ws = inCenterOfMass + plane_origin; - DebugRenderer::sInstance->DrawArrow(plane_origin_ws, plane_origin_ws + plane_normal / sqrt(plane_normal_len_sq), Color::sYellow, 0.05f); - } - - // Draw contact points that remain after distance check - for (ContactPoints::size_type p = old_size; p < outContactPoints1.size(); ++p) - DebugRenderer::sInstance->DrawMarker(inCenterOfMass + outContactPoints1[p], Color::sYellow, 0.1f); - } - #endif // JPH_DEBUG_RENDERER - } - - // If the clipping result is empty, use the contact point itself - if (outContactPoints1.size() == old_size) - { - outContactPoints1.push_back(inContactPoint1); - outContactPoints2.push_back(inContactPoint2); - } -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ManifoldBetweenTwoFaces.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ManifoldBetweenTwoFaces.h deleted file mode 100644 index 72a5b8f0c14..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ManifoldBetweenTwoFaces.h +++ /dev/null @@ -1,44 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include - -JPH_NAMESPACE_BEGIN - -/// Remove contact points if there are > 4 (no more than 4 are needed for a stable solution) -/// @param inPenetrationAxis is the world space penetration axis (must be normalized) -/// @param ioContactPointsOn1 The contact points on shape 1 relative to inCenterOfMass -/// @param ioContactPointsOn2 The contact points on shape 2 relative to inCenterOfMass -/// On output ioContactPointsOn1/2 are reduced to 4 or less points -#ifdef JPH_DEBUG_RENDERER -/// @param inCenterOfMass Center of mass position of body 1 -#endif -JPH_EXPORT void PruneContactPoints(Vec3Arg inPenetrationAxis, ContactPoints &ioContactPointsOn1, ContactPoints &ioContactPointsOn2 -#ifdef JPH_DEBUG_RENDERER - , RVec3Arg inCenterOfMass -#endif - ); - -/// Determine contact points between 2 faces of 2 shapes and return them in outContactPoints 1 & 2 -/// @param inContactPoint1 The contact point on shape 1 relative to inCenterOfMass -/// @param inContactPoint2 The contact point on shape 2 relative to inCenterOfMass -/// @param inPenetrationAxis The local space penetration axis in world space -/// @param inMaxContactDistanceSq After face 2 is clipped against face 1, each remaining point on face 2 is tested against the plane of face 1. If the distance^2 on the positive side of the plane is larger than this distance, the point will be discarded as a contact point. -/// @param inShape1Face The supporting faces on shape 1 relative to inCenterOfMass -/// @param inShape2Face The supporting faces on shape 2 relative to inCenterOfMass -/// @param outContactPoints1 Returns the contact points between the two shapes for shape 1 relative to inCenterOfMass (any existing points in the output array are left as is) -/// @param outContactPoints2 Returns the contact points between the two shapes for shape 2 relative to inCenterOfMass (any existing points in the output array are left as is) -#ifdef JPH_DEBUG_RENDERER -/// @param inCenterOfMass Center of mass position of body 1 -#endif -JPH_EXPORT void ManifoldBetweenTwoFaces(Vec3Arg inContactPoint1, Vec3Arg inContactPoint2, Vec3Arg inPenetrationAxis, float inMaxContactDistanceSq, const ConvexShape::SupportingFace &inShape1Face, const ConvexShape::SupportingFace &inShape2Face, ContactPoints &outContactPoints1, ContactPoints &outContactPoints2 -#ifdef JPH_DEBUG_RENDERER - , RVec3Arg inCenterOfMass -#endif - ); - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/NarrowPhaseQuery.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/NarrowPhaseQuery.cpp deleted file mode 100644 index d2b9663e3a4..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/NarrowPhaseQuery.cpp +++ /dev/null @@ -1,412 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -bool NarrowPhaseQuery::CastRay(const RRayCast &inRay, RayCastResult &ioHit, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter) const -{ - JPH_PROFILE_FUNCTION(); - - class MyCollector : public RayCastBodyCollector - { - public: - MyCollector(const RRayCast &inRay, RayCastResult &ioHit, const BodyLockInterface &inBodyLockInterface, const BodyFilter &inBodyFilter) : - mRay(inRay), - mHit(ioHit), - mBodyLockInterface(inBodyLockInterface), - mBodyFilter(inBodyFilter) - { - UpdateEarlyOutFraction(ioHit.mFraction); - } - - virtual void AddHit(const ResultType &inResult) override - { - JPH_ASSERT(inResult.mFraction < mHit.mFraction, "This hit should not have been passed on to the collector"); - - // Only test shape if it passes the body filter - if (mBodyFilter.ShouldCollide(inResult.mBodyID)) - { - // Lock the body - BodyLockRead lock(mBodyLockInterface, inResult.mBodyID); - if (lock.SucceededAndIsInBroadPhase()) // Race condition: body could have been removed since it has been found in the broadphase, ensures body is in the broadphase while we call the callbacks - { - const Body &body = lock.GetBody(); - - // Check body filter again now that we've locked the body - if (mBodyFilter.ShouldCollideLocked(body)) - { - // Collect the transformed shape - TransformedShape ts = body.GetTransformedShape(); - - // Release the lock now, we have all the info we need in the transformed shape - lock.ReleaseLock(); - - // Do narrow phase collision check - if (ts.CastRay(mRay, mHit)) - { - // Test that we didn't find a further hit by accident - JPH_ASSERT(mHit.mFraction >= 0.0f && mHit.mFraction < GetEarlyOutFraction()); - - // Update early out fraction based on narrow phase collector - UpdateEarlyOutFraction(mHit.mFraction); - } - } - } - } - } - - RRayCast mRay; - RayCastResult & mHit; - const BodyLockInterface & mBodyLockInterface; - const BodyFilter & mBodyFilter; - }; - - // Do broadphase test, note that the broadphase uses floats so we drop precision here - MyCollector collector(inRay, ioHit, *mBodyLockInterface, inBodyFilter); - mBroadPhaseQuery->CastRay(RayCast(inRay), collector, inBroadPhaseLayerFilter, inObjectLayerFilter); - return ioHit.mFraction <= 1.0f; -} - -void NarrowPhaseQuery::CastRay(const RRayCast &inRay, const RayCastSettings &inRayCastSettings, CastRayCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter) const -{ - JPH_PROFILE_FUNCTION(); - - class MyCollector : public RayCastBodyCollector - { - public: - MyCollector(const RRayCast &inRay, const RayCastSettings &inRayCastSettings, CastRayCollector &ioCollector, const BodyLockInterface &inBodyLockInterface, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter) : - RayCastBodyCollector(ioCollector), - mRay(inRay), - mRayCastSettings(inRayCastSettings), - mCollector(ioCollector), - mBodyLockInterface(inBodyLockInterface), - mBodyFilter(inBodyFilter), - mShapeFilter(inShapeFilter) - { - } - - virtual void AddHit(const ResultType &inResult) override - { - JPH_ASSERT(inResult.mFraction < mCollector.GetEarlyOutFraction(), "This hit should not have been passed on to the collector"); - - // Only test shape if it passes the body filter - if (mBodyFilter.ShouldCollide(inResult.mBodyID)) - { - // Lock the body - BodyLockRead lock(mBodyLockInterface, inResult.mBodyID); - if (lock.SucceededAndIsInBroadPhase()) // Race condition: body could have been removed since it has been found in the broadphase, ensures body is in the broadphase while we call the callbacks - { - const Body &body = lock.GetBody(); - - // Check body filter again now that we've locked the body - if (mBodyFilter.ShouldCollideLocked(body)) - { - // Collect the transformed shape - TransformedShape ts = body.GetTransformedShape(); - - // Notify collector of new body - mCollector.OnBody(body); - - // Release the lock now, we have all the info we need in the transformed shape - lock.ReleaseLock(); - - // Do narrow phase collision check - ts.CastRay(mRay, mRayCastSettings, mCollector, mShapeFilter); - - // Update early out fraction based on narrow phase collector - UpdateEarlyOutFraction(mCollector.GetEarlyOutFraction()); - } - } - } - } - - RRayCast mRay; - RayCastSettings mRayCastSettings; - CastRayCollector & mCollector; - const BodyLockInterface & mBodyLockInterface; - const BodyFilter & mBodyFilter; - const ShapeFilter & mShapeFilter; - }; - - // Do broadphase test, note that the broadphase uses floats so we drop precision here - MyCollector collector(inRay, inRayCastSettings, ioCollector, *mBodyLockInterface, inBodyFilter, inShapeFilter); - mBroadPhaseQuery->CastRay(RayCast(inRay), collector, inBroadPhaseLayerFilter, inObjectLayerFilter); -} - -void NarrowPhaseQuery::CollidePoint(RVec3Arg inPoint, CollidePointCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter) const -{ - JPH_PROFILE_FUNCTION(); - - class MyCollector : public CollideShapeBodyCollector - { - public: - MyCollector(RVec3Arg inPoint, CollidePointCollector &ioCollector, const BodyLockInterface &inBodyLockInterface, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter) : - CollideShapeBodyCollector(ioCollector), - mPoint(inPoint), - mCollector(ioCollector), - mBodyLockInterface(inBodyLockInterface), - mBodyFilter(inBodyFilter), - mShapeFilter(inShapeFilter) - { - } - - virtual void AddHit(const ResultType &inResult) override - { - // Only test shape if it passes the body filter - if (mBodyFilter.ShouldCollide(inResult)) - { - // Lock the body - BodyLockRead lock(mBodyLockInterface, inResult); - if (lock.SucceededAndIsInBroadPhase()) // Race condition: body could have been removed since it has been found in the broadphase, ensures body is in the broadphase while we call the callbacks - { - const Body &body = lock.GetBody(); - - // Check body filter again now that we've locked the body - if (mBodyFilter.ShouldCollideLocked(body)) - { - // Collect the transformed shape - TransformedShape ts = body.GetTransformedShape(); - - // Notify collector of new body - mCollector.OnBody(body); - - // Release the lock now, we have all the info we need in the transformed shape - lock.ReleaseLock(); - - // Do narrow phase collision check - ts.CollidePoint(mPoint, mCollector, mShapeFilter); - - // Update early out fraction based on narrow phase collector - UpdateEarlyOutFraction(mCollector.GetEarlyOutFraction()); - } - } - } - } - - RVec3 mPoint; - CollidePointCollector & mCollector; - const BodyLockInterface & mBodyLockInterface; - const BodyFilter & mBodyFilter; - const ShapeFilter & mShapeFilter; - }; - - // Do broadphase test (note: truncates double to single precision since the broadphase uses single precision) - MyCollector collector(inPoint, ioCollector, *mBodyLockInterface, inBodyFilter, inShapeFilter); - mBroadPhaseQuery->CollidePoint(Vec3(inPoint), collector, inBroadPhaseLayerFilter, inObjectLayerFilter); -} - -void NarrowPhaseQuery::CollideShape(const Shape *inShape, Vec3Arg inShapeScale, RMat44Arg inCenterOfMassTransform, const CollideShapeSettings &inCollideShapeSettings, RVec3Arg inBaseOffset, CollideShapeCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter) const -{ - JPH_PROFILE_FUNCTION(); - - class MyCollector : public CollideShapeBodyCollector - { - public: - MyCollector(const Shape *inShape, Vec3Arg inShapeScale, RMat44Arg inCenterOfMassTransform, const CollideShapeSettings &inCollideShapeSettings, RVec3Arg inBaseOffset, CollideShapeCollector &ioCollector, const BodyLockInterface &inBodyLockInterface, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter) : - CollideShapeBodyCollector(ioCollector), - mShape(inShape), - mShapeScale(inShapeScale), - mCenterOfMassTransform(inCenterOfMassTransform), - mCollideShapeSettings(inCollideShapeSettings), - mBaseOffset(inBaseOffset), - mCollector(ioCollector), - mBodyLockInterface(inBodyLockInterface), - mBodyFilter(inBodyFilter), - mShapeFilter(inShapeFilter) - { - } - - virtual void AddHit(const ResultType &inResult) override - { - // Only test shape if it passes the body filter - if (mBodyFilter.ShouldCollide(inResult)) - { - // Lock the body - BodyLockRead lock(mBodyLockInterface, inResult); - if (lock.SucceededAndIsInBroadPhase()) // Race condition: body could have been removed since it has been found in the broadphase, ensures body is in the broadphase while we call the callbacks - { - const Body &body = lock.GetBody(); - - // Check body filter again now that we've locked the body - if (mBodyFilter.ShouldCollideLocked(body)) - { - // Collect the transformed shape - TransformedShape ts = body.GetTransformedShape(); - - // Notify collector of new body - mCollector.OnBody(body); - - // Release the lock now, we have all the info we need in the transformed shape - lock.ReleaseLock(); - - // Do narrow phase collision check - ts.CollideShape(mShape, mShapeScale, mCenterOfMassTransform, mCollideShapeSettings, mBaseOffset, mCollector, mShapeFilter); - - // Update early out fraction based on narrow phase collector - UpdateEarlyOutFraction(mCollector.GetEarlyOutFraction()); - } - } - } - } - - const Shape * mShape; - Vec3 mShapeScale; - RMat44 mCenterOfMassTransform; - const CollideShapeSettings & mCollideShapeSettings; - RVec3 mBaseOffset; - CollideShapeCollector & mCollector; - const BodyLockInterface & mBodyLockInterface; - const BodyFilter & mBodyFilter; - const ShapeFilter & mShapeFilter; - }; - - // Calculate bounds for shape and expand by max separation distance - AABox bounds = inShape->GetWorldSpaceBounds(inCenterOfMassTransform, inShapeScale); - bounds.ExpandBy(Vec3::sReplicate(inCollideShapeSettings.mMaxSeparationDistance)); - - // Do broadphase test - MyCollector collector(inShape, inShapeScale, inCenterOfMassTransform, inCollideShapeSettings, inBaseOffset, ioCollector, *mBodyLockInterface, inBodyFilter, inShapeFilter); - mBroadPhaseQuery->CollideAABox(bounds, collector, inBroadPhaseLayerFilter, inObjectLayerFilter); -} - -void NarrowPhaseQuery::CastShape(const RShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, RVec3Arg inBaseOffset, CastShapeCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter) const -{ - JPH_PROFILE_FUNCTION(); - - class MyCollector : public CastShapeBodyCollector - { - public: - MyCollector(const RShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, RVec3Arg inBaseOffset, CastShapeCollector &ioCollector, const BodyLockInterface &inBodyLockInterface, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter) : - CastShapeBodyCollector(ioCollector), - mShapeCast(inShapeCast), - mShapeCastSettings(inShapeCastSettings), - mBaseOffset(inBaseOffset), - mCollector(ioCollector), - mBodyLockInterface(inBodyLockInterface), - mBodyFilter(inBodyFilter), - mShapeFilter(inShapeFilter) - { - } - - virtual void AddHit(const ResultType &inResult) override - { - JPH_ASSERT(inResult.mFraction <= max(0.0f, mCollector.GetEarlyOutFraction()), "This hit should not have been passed on to the collector"); - - // Only test shape if it passes the body filter - if (mBodyFilter.ShouldCollide(inResult.mBodyID)) - { - // Lock the body - BodyLockRead lock(mBodyLockInterface, inResult.mBodyID); - if (lock.SucceededAndIsInBroadPhase()) // Race condition: body could have been removed since it has been found in the broadphase, ensures body is in the broadphase while we call the callbacks - { - const Body &body = lock.GetBody(); - - // Check body filter again now that we've locked the body - if (mBodyFilter.ShouldCollideLocked(body)) - { - // Collect the transformed shape - TransformedShape ts = body.GetTransformedShape(); - - // Notify collector of new body - mCollector.OnBody(body); - - // Release the lock now, we have all the info we need in the transformed shape - lock.ReleaseLock(); - - // Do narrow phase collision check - ts.CastShape(mShapeCast, mShapeCastSettings, mBaseOffset, mCollector, mShapeFilter); - - // Update early out fraction based on narrow phase collector - UpdateEarlyOutFraction(mCollector.GetEarlyOutFraction()); - } - } - } - } - - RShapeCast mShapeCast; - const ShapeCastSettings & mShapeCastSettings; - RVec3 mBaseOffset; - CastShapeCollector & mCollector; - const BodyLockInterface & mBodyLockInterface; - const BodyFilter & mBodyFilter; - const ShapeFilter & mShapeFilter; - }; - - // Do broadphase test - MyCollector collector(inShapeCast, inShapeCastSettings, inBaseOffset, ioCollector, *mBodyLockInterface, inBodyFilter, inShapeFilter); - mBroadPhaseQuery->CastAABox({ inShapeCast.mShapeWorldBounds, inShapeCast.mDirection }, collector, inBroadPhaseLayerFilter, inObjectLayerFilter); -} - -void NarrowPhaseQuery::CollectTransformedShapes(const AABox &inBox, TransformedShapeCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter) const -{ - class MyCollector : public CollideShapeBodyCollector - { - public: - MyCollector(const AABox &inBox, TransformedShapeCollector &ioCollector, const BodyLockInterface &inBodyLockInterface, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter) : - CollideShapeBodyCollector(ioCollector), - mBox(inBox), - mCollector(ioCollector), - mBodyLockInterface(inBodyLockInterface), - mBodyFilter(inBodyFilter), - mShapeFilter(inShapeFilter) - { - } - - virtual void AddHit(const ResultType &inResult) override - { - // Only test shape if it passes the body filter - if (mBodyFilter.ShouldCollide(inResult)) - { - // Lock the body - BodyLockRead lock(mBodyLockInterface, inResult); - if (lock.SucceededAndIsInBroadPhase()) // Race condition: body could have been removed since it has been found in the broadphase, ensures body is in the broadphase while we call the callbacks - { - const Body &body = lock.GetBody(); - - // Check body filter again now that we've locked the body - if (mBodyFilter.ShouldCollideLocked(body)) - { - // Collect the transformed shape - TransformedShape ts = body.GetTransformedShape(); - - // Notify collector of new body - mCollector.OnBody(body); - - // Release the lock now, we have all the info we need in the transformed shape - lock.ReleaseLock(); - - // Do narrow phase collision check - ts.CollectTransformedShapes(mBox, mCollector, mShapeFilter); - - // Update early out fraction based on narrow phase collector - UpdateEarlyOutFraction(mCollector.GetEarlyOutFraction()); - } - } - } - } - - const AABox & mBox; - TransformedShapeCollector & mCollector; - const BodyLockInterface & mBodyLockInterface; - const BodyFilter & mBodyFilter; - const ShapeFilter & mShapeFilter; - }; - - // Do broadphase test - MyCollector collector(inBox, ioCollector, *mBodyLockInterface, inBodyFilter, inShapeFilter); - mBroadPhaseQuery->CollideAABox(inBox, collector, inBroadPhaseLayerFilter, inObjectLayerFilter); -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/NarrowPhaseQuery.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/NarrowPhaseQuery.h deleted file mode 100644 index 0967b21a1b1..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/NarrowPhaseQuery.h +++ /dev/null @@ -1,74 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -class Shape; -class CollideShapeSettings; -class RayCastResult; - -/// Class that provides an interface for doing precise collision detection against the broad and then the narrow phase. -/// Unlike a BroadPhaseQuery, the NarrowPhaseQuery will test against shapes and will return collision information against triangles, spheres etc. -class JPH_EXPORT NarrowPhaseQuery : public NonCopyable -{ -public: - /// Initialize the interface (should only be called by PhysicsSystem) - void Init(BodyLockInterface &inBodyLockInterface, BroadPhaseQuery &inBroadPhaseQuery) { mBodyLockInterface = &inBodyLockInterface; mBroadPhaseQuery = &inBroadPhaseQuery; } - - /// Cast a ray and find the closest hit. Returns true if it finds a hit. Hits further than ioHit.mFraction will not be considered and in this case ioHit will remain unmodified (and the function will return false). - /// Convex objects will be treated as solid (meaning if the ray starts inside, you'll get a hit fraction of 0) and back face hits against triangles are returned. - /// If you want the surface normal of the hit use Body::GetWorldSpaceSurfaceNormal(ioHit.mSubShapeID2, inRay.GetPointOnRay(ioHit.mFraction)) on body with ID ioHit.mBodyID. - bool CastRay(const RRayCast &inRay, RayCastResult &ioHit, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter = { }, const ObjectLayerFilter &inObjectLayerFilter = { }, const BodyFilter &inBodyFilter = { }) const; - - /// Cast a ray, allows collecting multiple hits. Note that this version is more flexible but also slightly slower than the CastRay function that returns only a single hit. - /// If you want the surface normal of the hit use Body::GetWorldSpaceSurfaceNormal(collected sub shape ID, inRay.GetPointOnRay(collected fraction)) on body with collected body ID. - void CastRay(const RRayCast &inRay, const RayCastSettings &inRayCastSettings, CastRayCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter = { }, const ObjectLayerFilter &inObjectLayerFilter = { }, const BodyFilter &inBodyFilter = { }, const ShapeFilter &inShapeFilter = { }) const; - - /// Check if inPoint is inside any shapes. For this tests all shapes are treated as if they were solid. - /// For a mesh shape, this test will only provide sensible information if the mesh is a closed manifold. - /// For each shape that collides, ioCollector will receive a hit - void CollidePoint(RVec3Arg inPoint, CollidePointCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter = { }, const ObjectLayerFilter &inObjectLayerFilter = { }, const BodyFilter &inBodyFilter = { }, const ShapeFilter &inShapeFilter = { }) const; - - /// Collide a shape with the system - /// @param inShape Shape to test - /// @param inShapeScale Scale in local space of shape - /// @param inCenterOfMassTransform Center of mass transform for the shape - /// @param inCollideShapeSettings Settings - /// @param inBaseOffset All hit results will be returned relative to this offset, can be zero to get results in world position, but when you're testing far from the origin you get better precision by picking a position that's closer e.g. inCenterOfMassTransform.GetTranslation() since floats are most accurate near the origin - /// @param ioCollector Collector that receives the hits - /// @param inBroadPhaseLayerFilter Filter that filters at broadphase level - /// @param inObjectLayerFilter Filter that filters at layer level - /// @param inBodyFilter Filter that filters at body level - /// @param inShapeFilter Filter that filters at shape level - void CollideShape(const Shape *inShape, Vec3Arg inShapeScale, RMat44Arg inCenterOfMassTransform, const CollideShapeSettings &inCollideShapeSettings, RVec3Arg inBaseOffset, CollideShapeCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter = { }, const ObjectLayerFilter &inObjectLayerFilter = { }, const BodyFilter &inBodyFilter = { }, const ShapeFilter &inShapeFilter = { }) const; - - /// Cast a shape and report any hits to ioCollector - /// @param inShapeCast The shape cast and its position and direction - /// @param inShapeCastSettings Settings for the shape cast - /// @param inBaseOffset All hit results will be returned relative to this offset, can be zero to get results in world position, but when you're testing far from the origin you get better precision by picking a position that's closer e.g. inShapeCast.mCenterOfMassStart.GetTranslation() since floats are most accurate near the origin - /// @param ioCollector Collector that receives the hits - /// @param inBroadPhaseLayerFilter Filter that filters at broadphase level - /// @param inObjectLayerFilter Filter that filters at layer level - /// @param inBodyFilter Filter that filters at body level - /// @param inShapeFilter Filter that filters at shape level - void CastShape(const RShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, RVec3Arg inBaseOffset, CastShapeCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter = { }, const ObjectLayerFilter &inObjectLayerFilter = { }, const BodyFilter &inBodyFilter = { }, const ShapeFilter &inShapeFilter = { }) const; - - /// Collect all leaf transformed shapes that fall inside world space box inBox - void CollectTransformedShapes(const AABox &inBox, TransformedShapeCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter = { }, const ObjectLayerFilter &inObjectLayerFilter = { }, const BodyFilter &inBodyFilter = { }, const ShapeFilter &inShapeFilter = { }) const; - -private: - BodyLockInterface * mBodyLockInterface = nullptr; - BroadPhaseQuery * mBroadPhaseQuery = nullptr; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/NarrowPhaseStats.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/NarrowPhaseStats.cpp deleted file mode 100644 index 69c61137253..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/NarrowPhaseStats.cpp +++ /dev/null @@ -1,62 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include - -#ifdef JPH_TRACK_NARROWPHASE_STATS - -JPH_NAMESPACE_BEGIN - -NarrowPhaseStat NarrowPhaseStat::sCollideShape[NumSubShapeTypes][NumSubShapeTypes]; -NarrowPhaseStat NarrowPhaseStat::sCastShape[NumSubShapeTypes][NumSubShapeTypes]; - -thread_local TrackNarrowPhaseStat *TrackNarrowPhaseStat::sRoot = nullptr; - -void NarrowPhaseStat::ReportStats(const char *inName, EShapeSubType inType1, EShapeSubType inType2, uint64 inTicks100Pct) const -{ - double total_pct = 100.0 * double(mTotalTicks) / double(inTicks100Pct); - double total_pct_excl_children = 100.0 * double(mTotalTicks - mChildTicks) / double(inTicks100Pct); - - std::stringstream str; - str << inName << ", " << sSubShapeTypeNames[(int)inType1] << ", " << sSubShapeTypeNames[(int)inType2] << ", " << mNumQueries << ", " << total_pct << ", " << total_pct_excl_children << ", " << total_pct_excl_children / mNumQueries << ", " << mHitsReported; - Trace(str.str().c_str()); -} - -void NarrowPhaseStat::sReportStats() -{ - Trace("Query Type, Shape Type 1, Shape Type 2, Num Queries, Total Time (%%), Total Time Excl Children (%%), Total Time Excl. Children / Query (%%), Hits Reported"); - - uint64 total_ticks = 0; - for (EShapeSubType t1 : sAllSubShapeTypes) - for (EShapeSubType t2 : sAllSubShapeTypes) - { - const NarrowPhaseStat &collide_stat = sCollideShape[(int)t1][(int)t2]; - total_ticks += collide_stat.mTotalTicks - collide_stat.mChildTicks; - - const NarrowPhaseStat &cast_stat = sCastShape[(int)t1][(int)t2]; - total_ticks += cast_stat.mTotalTicks - cast_stat.mChildTicks; - } - - for (EShapeSubType t1 : sAllSubShapeTypes) - for (EShapeSubType t2 : sAllSubShapeTypes) - { - const NarrowPhaseStat &stat = sCollideShape[(int)t1][(int)t2]; - if (stat.mNumQueries > 0) - stat.ReportStats("CollideShape", t1, t2, total_ticks); - } - - for (EShapeSubType t1 : sAllSubShapeTypes) - for (EShapeSubType t2 : sAllSubShapeTypes) - { - const NarrowPhaseStat &stat = sCastShape[(int)t1][(int)t2]; - if (stat.mNumQueries > 0) - stat.ReportStats("CastShape", t1, t2, total_ticks); - } -} - -JPH_NAMESPACE_END - -#endif // JPH_TRACK_NARROWPHASE_STATS diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/NarrowPhaseStats.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/NarrowPhaseStats.h deleted file mode 100644 index 813933bb754..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/NarrowPhaseStats.h +++ /dev/null @@ -1,110 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include - -JPH_SUPPRESS_WARNING_PUSH -JPH_CLANG_SUPPRESS_WARNING("-Wc++98-compat-pedantic") - -// Shorthand function to ifdef out code if narrow phase stats tracking is off -#ifdef JPH_TRACK_NARROWPHASE_STATS - #define JPH_IF_TRACK_NARROWPHASE_STATS(...) __VA_ARGS__ -#else - #define JPH_IF_TRACK_NARROWPHASE_STATS(...) -#endif // JPH_TRACK_NARROWPHASE_STATS - -JPH_SUPPRESS_WARNING_POP - -#ifdef JPH_TRACK_NARROWPHASE_STATS - -JPH_NAMESPACE_BEGIN - -/// Structure that tracks narrow phase timing information for a particular combination of shapes -class NarrowPhaseStat -{ -public: - /// Trace an individual stat in CSV form. - void ReportStats(const char *inName, EShapeSubType inType1, EShapeSubType inType2, uint64 inTicks100Pct) const; - - /// Trace the collected broadphase stats in CSV form. - /// This report can be used to judge and tweak the efficiency of the broadphase. - static void sReportStats(); - - atomic mNumQueries = 0; - atomic mHitsReported = 0; - atomic mTotalTicks = 0; - atomic mChildTicks = 0; - - static NarrowPhaseStat sCollideShape[NumSubShapeTypes][NumSubShapeTypes]; - static NarrowPhaseStat sCastShape[NumSubShapeTypes][NumSubShapeTypes]; -}; - -/// Object that tracks the start and end of a narrow phase operation -class TrackNarrowPhaseStat -{ -public: - TrackNarrowPhaseStat(NarrowPhaseStat &inStat) : - mStat(inStat), - mParent(sRoot), - mStart(GetProcessorTickCount()) - { - // Make this the new root of the chain - sRoot = this; - } - - ~TrackNarrowPhaseStat() - { - uint64 delta_ticks = GetProcessorTickCount() - mStart; - - // Notify parent of time spent in child - if (mParent != nullptr) - mParent->mStat.mChildTicks += delta_ticks; - - // Increment stats at this level - mStat.mNumQueries++; - mStat.mTotalTicks += delta_ticks; - - // Restore root pointer - JPH_ASSERT(sRoot == this); - sRoot = mParent; - } - - NarrowPhaseStat & mStat; - TrackNarrowPhaseStat * mParent; - uint64 mStart; - - static thread_local TrackNarrowPhaseStat *sRoot; -}; - -/// Object that tracks the start and end of a hit being processed by a collision collector -class TrackNarrowPhaseCollector -{ -public: - TrackNarrowPhaseCollector() : - mStart(GetProcessorTickCount()) - { - } - - ~TrackNarrowPhaseCollector() - { - // Mark time spent in collector as 'child' time for the parent - uint64 delta_ticks = GetProcessorTickCount() - mStart; - if (TrackNarrowPhaseStat::sRoot != nullptr) - TrackNarrowPhaseStat::sRoot->mStat.mChildTicks += delta_ticks; - - // Notify all parents of a hit - for (TrackNarrowPhaseStat *track = TrackNarrowPhaseStat::sRoot; track != nullptr; track = track->mParent) - track->mStat.mHitsReported++; - } - -private: - uint64 mStart; -}; - -JPH_NAMESPACE_END - -#endif // JPH_TRACK_NARROWPHASE_STATS diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ObjectLayer.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ObjectLayer.h deleted file mode 100644 index f684e09e637..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ObjectLayer.h +++ /dev/null @@ -1,111 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -/// Layer that objects can be in, determines which other objects it can collide with -#ifndef JPH_OBJECT_LAYER_BITS - #define JPH_OBJECT_LAYER_BITS 16 -#endif // JPH_OBJECT_LAYER_BITS -#if JPH_OBJECT_LAYER_BITS == 16 - using ObjectLayer = uint16; -#elif JPH_OBJECT_LAYER_BITS == 32 - using ObjectLayer = uint32; -#else - #error "JPH_OBJECT_LAYER_BITS must be 16 or 32" -#endif - -/// Constant value used to indicate an invalid object layer -static constexpr ObjectLayer cObjectLayerInvalid = ObjectLayer(~ObjectLayer(0U)); - -/// Filter class for object layers -class ObjectLayerFilter : public NonCopyable -{ -public: - /// Destructor - virtual ~ObjectLayerFilter() = default; - - /// Function to filter out object layers when doing collision query test (return true to allow testing against objects with this layer) - virtual bool ShouldCollide([[maybe_unused]] ObjectLayer inLayer) const - { - return true; - } - -#ifdef JPH_TRACK_BROADPHASE_STATS - /// Get a string that describes this filter for stat tracking purposes - virtual String GetDescription() const - { - return "No Description"; - } -#endif // JPH_TRACK_BROADPHASE_STATS -}; - -/// Filter class to test if two objects can collide based on their object layer. Used while finding collision pairs. -class ObjectLayerPairFilter : public NonCopyable -{ -public: - /// Destructor - virtual ~ObjectLayerPairFilter() = default; - - /// Returns true if two layers can collide - virtual bool ShouldCollide([[maybe_unused]] ObjectLayer inLayer1, [[maybe_unused]] ObjectLayer inLayer2) const - { - return true; - } -}; - -/// Default filter class that uses the pair filter in combination with a specified layer to filter layers -class DefaultObjectLayerFilter : public ObjectLayerFilter -{ -public: - /// Constructor - DefaultObjectLayerFilter(const ObjectLayerPairFilter &inObjectLayerPairFilter, ObjectLayer inLayer) : - mObjectLayerPairFilter(inObjectLayerPairFilter), - mLayer(inLayer) - { - } - - /// Copy constructor - DefaultObjectLayerFilter(const DefaultObjectLayerFilter &inRHS) : - mObjectLayerPairFilter(inRHS.mObjectLayerPairFilter), - mLayer(inRHS.mLayer) - { - } - - // See ObjectLayerFilter::ShouldCollide - virtual bool ShouldCollide(ObjectLayer inLayer) const override - { - return mObjectLayerPairFilter.ShouldCollide(mLayer, inLayer); - } - -private: - const ObjectLayerPairFilter & mObjectLayerPairFilter; - ObjectLayer mLayer; -}; - -/// Allows objects from a specific layer only -class SpecifiedObjectLayerFilter : public ObjectLayerFilter -{ -public: - /// Constructor - explicit SpecifiedObjectLayerFilter(ObjectLayer inLayer) : - mLayer(inLayer) - { - } - - // See ObjectLayerFilter::ShouldCollide - virtual bool ShouldCollide(ObjectLayer inLayer) const override - { - return mLayer == inLayer; - } - -private: - ObjectLayer mLayer; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ObjectLayerPairFilterMask.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ObjectLayerPairFilterMask.h deleted file mode 100644 index dc3494c2ee3..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ObjectLayerPairFilterMask.h +++ /dev/null @@ -1,52 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2023 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -/// Filter class to test if two objects can collide based on their object layer. Used while finding collision pairs. -/// Uses group bits and mask bits. Two layers can collide if Object1.Group & Object2.Mask is non-zero and Object2.Group & Object1.Mask is non-zero. -/// The behavior is similar to that in e.g. Bullet. -/// This implementation works together with BroadPhaseLayerInterfaceMask and ObjectVsBroadPhaseLayerFilterMask -class ObjectLayerPairFilterMask : public ObjectLayerPairFilter -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Number of bits for the group and mask bits - static constexpr uint32 cNumBits = JPH_OBJECT_LAYER_BITS / 2; - static constexpr uint32 cMask = (1 << cNumBits) - 1; - - /// Construct an ObjectLayer from a group and mask bits - static ObjectLayer sGetObjectLayer(uint32 inGroup, uint32 inMask = cMask) - { - JPH_ASSERT((inGroup & ~cMask) == 0); - JPH_ASSERT((inMask & ~cMask) == 0); - return ObjectLayer((inGroup & cMask) | (inMask << cNumBits)); - } - - /// Get the group bits from an ObjectLayer - static inline uint32 sGetGroup(ObjectLayer inObjectLayer) - { - return uint32(inObjectLayer) & cMask; - } - - /// Get the mask bits from an ObjectLayer - static inline uint32 sGetMask(ObjectLayer inObjectLayer) - { - return uint32(inObjectLayer) >> cNumBits; - } - - /// Returns true if two layers can collide - virtual bool ShouldCollide(ObjectLayer inObject1, ObjectLayer inObject2) const override - { - return (sGetGroup(inObject1) & sGetMask(inObject2)) != 0 - && (sGetGroup(inObject2) & sGetMask(inObject1)) != 0; - } -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ObjectLayerPairFilterTable.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ObjectLayerPairFilterTable.h deleted file mode 100644 index 1d62178af7e..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ObjectLayerPairFilterTable.h +++ /dev/null @@ -1,78 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2023 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -/// Filter class to test if two objects can collide based on their object layer. Used while finding collision pairs. -/// This implementation uses a table to determine if two layers can collide. -class ObjectLayerPairFilterTable : public ObjectLayerPairFilter -{ -private: - /// Get which bit corresponds to the pair (inLayer1, inLayer2) - uint GetBit(ObjectLayer inLayer1, ObjectLayer inLayer2) const - { - // We store the lower left half only, so swap the inputs when trying to access the top right half - if (inLayer1 > inLayer2) - swap(inLayer1, inLayer2); - - JPH_ASSERT(inLayer2 < mNumObjectLayers); - - // Calculate at which bit the entry for this pair resides - // We use the fact that a row always starts at inLayer2 * (inLayer2 + 1) / 2 - // (this is the amount of bits needed to store a table of inLayer2 entries) - return (inLayer2 * (inLayer2 + 1)) / 2 + inLayer1; - } - -public: - JPH_OVERRIDE_NEW_DELETE - - /// Constructs the table with inNumObjectLayers Layers, initially all layer pairs are disabled - explicit ObjectLayerPairFilterTable(uint inNumObjectLayers) : - mNumObjectLayers(inNumObjectLayers) - { - // By default nothing collides - // For the first layer we only need to store 1 bit, for the second 2 bits, for the third 3 bits, etc. - // We use the formula Sum_i=1^N i = N * (N + 1) / 2 to calculate the size of the table - int table_size = (inNumObjectLayers * (inNumObjectLayers + 1) / 2 + 7) / 8; - mTable.resize(table_size, 0); - } - - /// Get the number of object layers - uint GetNumObjectLayers() const - { - return mNumObjectLayers; - } - - /// Disable collision between two object layers - void DisableCollision(ObjectLayer inLayer1, ObjectLayer inLayer2) - { - uint bit = GetBit(inLayer1, inLayer2); - mTable[bit >> 3] &= (0xff ^ (1 << (bit & 0b111))); - } - - /// Enable collision between two object layers - void EnableCollision(ObjectLayer inLayer1, ObjectLayer inLayer2) - { - uint bit = GetBit(inLayer1, inLayer2); - mTable[bit >> 3] |= 1 << (bit & 0b111); - } - - /// Returns true if two layers can collide - virtual bool ShouldCollide(ObjectLayer inObject1, ObjectLayer inObject2) const override - { - // Test if the bit is set for this group pair - uint bit = GetBit(inObject1, inObject2); - return (mTable[bit >> 3] & (1 << (bit & 0b111))) != 0; - } - -private: - uint mNumObjectLayers; ///< The number of layers that this table supports - Array mTable; ///< The table of bits that indicates which layers collide -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/PhysicsMaterial.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/PhysicsMaterial.cpp deleted file mode 100644 index 17a982e7499..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/PhysicsMaterial.cpp +++ /dev/null @@ -1,35 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -RefConst PhysicsMaterial::sDefault; - -JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(PhysicsMaterial) -{ - JPH_ADD_BASE_CLASS(PhysicsMaterial, SerializableObject) -} - -void PhysicsMaterial::SaveBinaryState(StreamOut &inStream) const -{ - inStream.Write(GetRTTI()->GetHash()); -} - -void PhysicsMaterial::RestoreBinaryState(StreamIn &inStream) -{ - // RTTI hash is read in sRestoreFromBinaryState -} - -PhysicsMaterial::PhysicsMaterialResult PhysicsMaterial::sRestoreFromBinaryState(StreamIn &inStream) -{ - return StreamUtils::RestoreObject(inStream, &PhysicsMaterial::RestoreBinaryState); -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/PhysicsMaterial.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/PhysicsMaterial.h deleted file mode 100644 index d24e344d1a6..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/PhysicsMaterial.h +++ /dev/null @@ -1,52 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -class StreamIn; -class StreamOut; - -/// This structure describes the surface of (part of) a shape. You should inherit from it to define additional -/// information that is interesting for the simulation. The 2 materials involved in a contact could be used -/// to decide which sound or particle effects to play. -/// -/// If you inherit from this material, don't forget to create a suitable default material in sDefault -class JPH_EXPORT PhysicsMaterial : public SerializableObject, public RefTarget -{ -public: - JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, PhysicsMaterial) - - /// Virtual destructor - virtual ~PhysicsMaterial() override = default; - - /// Default material that is used when a shape has no materials defined - static RefConst sDefault; - - // Properties - virtual const char * GetDebugName() const { return "Unknown"; } - virtual Color GetDebugColor() const { return Color::sGrey; } - - /// Saves the contents of the material in binary form to inStream. - virtual void SaveBinaryState(StreamOut &inStream) const; - - using PhysicsMaterialResult = Result>; - - /// Creates a PhysicsMaterial of the correct type and restores its contents from the binary stream inStream. - static PhysicsMaterialResult sRestoreFromBinaryState(StreamIn &inStream); - -protected: - /// This function should not be called directly, it is used by sRestoreFromBinaryState. - virtual void RestoreBinaryState(StreamIn &inStream); -}; - -using PhysicsMaterialList = Array>; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/PhysicsMaterialSimple.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/PhysicsMaterialSimple.cpp deleted file mode 100644 index 02a569822c6..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/PhysicsMaterialSimple.cpp +++ /dev/null @@ -1,38 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(PhysicsMaterialSimple) -{ - JPH_ADD_BASE_CLASS(PhysicsMaterialSimple, PhysicsMaterial) - - JPH_ADD_ATTRIBUTE(PhysicsMaterialSimple, mDebugName) - JPH_ADD_ATTRIBUTE(PhysicsMaterialSimple, mDebugColor) -} - -void PhysicsMaterialSimple::SaveBinaryState(StreamOut &inStream) const -{ - PhysicsMaterial::SaveBinaryState(inStream); - - inStream.Write(mDebugName); - inStream.Write(mDebugColor); -} - -void PhysicsMaterialSimple::RestoreBinaryState(StreamIn &inStream) -{ - PhysicsMaterial::RestoreBinaryState(inStream); - - inStream.Read(mDebugName); - inStream.Read(mDebugColor); -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/PhysicsMaterialSimple.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/PhysicsMaterialSimple.h deleted file mode 100644 index 21c1e634a56..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/PhysicsMaterialSimple.h +++ /dev/null @@ -1,37 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -/// Sample implementation of PhysicsMaterial that just holds the needed properties directly -class JPH_EXPORT PhysicsMaterialSimple : public PhysicsMaterial -{ -public: - JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, PhysicsMaterialSimple) - - /// Constructor - PhysicsMaterialSimple() = default; - PhysicsMaterialSimple(const string_view &inName, ColorArg inColor) : mDebugName(inName), mDebugColor(inColor) { } - - // Properties - virtual const char * GetDebugName() const override { return mDebugName.c_str(); } - virtual Color GetDebugColor() const override { return mDebugColor; } - - // See: PhysicsMaterial::SaveBinaryState - virtual void SaveBinaryState(StreamOut &inStream) const override; - -protected: - // See: PhysicsMaterial::RestoreBinaryState - virtual void RestoreBinaryState(StreamIn &inStream) override; - -private: - String mDebugName; ///< Name of the material, used for debugging purposes - Color mDebugColor = Color::sGrey; ///< Color of the material, used to render the shapes -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/RayCast.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/RayCast.h deleted file mode 100644 index 052c815296a..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/RayCast.h +++ /dev/null @@ -1,81 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -/// Structure that holds a single ray cast -template -struct RayCastT -{ - JPH_OVERRIDE_NEW_DELETE - - /// Constructors - RayCastT() = default; // Allow raycast to be created uninitialized - RayCastT(typename Vec::ArgType inOrigin, Vec3Arg inDirection) : mOrigin(inOrigin), mDirection(inDirection) { } - RayCastT(const RayCastT &) = default; - - /// Transform this ray using inTransform - RayCastType Transformed(typename Mat::ArgType inTransform) const - { - Vec ray_origin = inTransform * mOrigin; - Vec3 ray_direction(inTransform * (mOrigin + mDirection) - ray_origin); - return { ray_origin, ray_direction }; - } - - /// Translate ray using inTranslation - RayCastType Translated(typename Vec::ArgType inTranslation) const - { - return { inTranslation + mOrigin, mDirection }; - } - - /// Get point with fraction inFraction on ray (0 = start of ray, 1 = end of ray) - inline Vec GetPointOnRay(float inFraction) const - { - return mOrigin + inFraction * mDirection; - } - - Vec mOrigin; ///< Origin of the ray - Vec3 mDirection; ///< Direction and length of the ray (anything beyond this length will not be reported as a hit) -}; - -struct RayCast : public RayCastT -{ - using RayCastT::RayCastT; -}; - -struct RRayCast : public RayCastT -{ - using RayCastT::RayCastT; - - /// Convert from RayCast, converts single to double precision - explicit RRayCast(const RayCast &inRay) : - RRayCast(RVec3(inRay.mOrigin), inRay.mDirection) - { - } - - /// Convert to RayCast, which implies casting from double precision to single precision - explicit operator RayCast() const - { - return RayCast(Vec3(mOrigin), mDirection); - } -}; - -/// Settings to be passed with a ray cast -class RayCastSettings -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// How backfacing triangles should be treated - EBackFaceMode mBackFaceMode = EBackFaceMode::IgnoreBackFaces; - - /// If convex shapes should be treated as solid. When true, a ray starting inside a convex shape will generate a hit at fraction 0. - bool mTreatConvexAsSolid = true; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/BoxShape.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/BoxShape.cpp deleted file mode 100644 index da6b08c4ff5..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/BoxShape.cpp +++ /dev/null @@ -1,318 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef JPH_DEBUG_RENDERER - #include -#endif // JPH_DEBUG_RENDERER - -JPH_NAMESPACE_BEGIN - -JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(BoxShapeSettings) -{ - JPH_ADD_BASE_CLASS(BoxShapeSettings, ConvexShapeSettings) - - JPH_ADD_ATTRIBUTE(BoxShapeSettings, mHalfExtent) - JPH_ADD_ATTRIBUTE(BoxShapeSettings, mConvexRadius) -} - -static const Vec3 sUnitBoxTriangles[] = { - Vec3(-1, 1, -1), Vec3(-1, 1, 1), Vec3(1, 1, 1), - Vec3(-1, 1, -1), Vec3(1, 1, 1), Vec3(1, 1, -1), - Vec3(-1, -1, -1), Vec3(1, -1, -1), Vec3(1, -1, 1), - Vec3(-1, -1, -1), Vec3(1, -1, 1), Vec3(-1, -1, 1), - Vec3(-1, 1, -1), Vec3(-1, -1, -1), Vec3(-1, -1, 1), - Vec3(-1, 1, -1), Vec3(-1, -1, 1), Vec3(-1, 1, 1), - Vec3(1, 1, 1), Vec3(1, -1, 1), Vec3(1, -1, -1), - Vec3(1, 1, 1), Vec3(1, -1, -1), Vec3(1, 1, -1), - Vec3(-1, 1, 1), Vec3(-1, -1, 1), Vec3(1, -1, 1), - Vec3(-1, 1, 1), Vec3(1, -1, 1), Vec3(1, 1, 1), - Vec3(-1, 1, -1), Vec3(1, 1, -1), Vec3(1, -1, -1), - Vec3(-1, 1, -1), Vec3(1, -1, -1), Vec3(-1, -1, -1) -}; - -ShapeSettings::ShapeResult BoxShapeSettings::Create() const -{ - if (mCachedResult.IsEmpty()) - Ref shape = new BoxShape(*this, mCachedResult); - return mCachedResult; -} - -BoxShape::BoxShape(const BoxShapeSettings &inSettings, ShapeResult &outResult) : - ConvexShape(EShapeSubType::Box, inSettings, outResult), - mHalfExtent(inSettings.mHalfExtent), - mConvexRadius(inSettings.mConvexRadius) -{ - // Check convex radius - if (inSettings.mConvexRadius < 0.0f - || inSettings.mHalfExtent.ReduceMin() <= inSettings.mConvexRadius) - { - outResult.SetError("Invalid convex radius"); - return; - } - - // Result is valid - outResult.Set(this); -} - -class BoxShape::Box final : public Support -{ -public: - Box(const AABox &inBox, float inConvexRadius) : - mBox(inBox), - mConvexRadius(inConvexRadius) - { - static_assert(sizeof(Box) <= sizeof(SupportBuffer), "Buffer size too small"); - JPH_ASSERT(IsAligned(this, alignof(Box))); - } - - virtual Vec3 GetSupport(Vec3Arg inDirection) const override - { - return mBox.GetSupport(inDirection); - } - - virtual float GetConvexRadius() const override - { - return mConvexRadius; - } - -private: - AABox mBox; - float mConvexRadius; -}; - -const ConvexShape::Support *BoxShape::GetSupportFunction(ESupportMode inMode, SupportBuffer &inBuffer, Vec3Arg inScale) const -{ - // Scale our half extents - Vec3 scaled_half_extent = inScale.Abs() * mHalfExtent; - - switch (inMode) - { - case ESupportMode::IncludeConvexRadius: - case ESupportMode::Default: - { - // Make box out of our half extents - AABox box = AABox(-scaled_half_extent, scaled_half_extent); - JPH_ASSERT(box.IsValid()); - return new (&inBuffer) Box(box, 0.0f); - } - - case ESupportMode::ExcludeConvexRadius: - { - // Reduce the box by our convex radius - float convex_radius = ScaleHelpers::ScaleConvexRadius(mConvexRadius, inScale); - Vec3 convex_radius3 = Vec3::sReplicate(convex_radius); - Vec3 reduced_half_extent = scaled_half_extent - convex_radius3; - AABox box = AABox(-reduced_half_extent, reduced_half_extent); - JPH_ASSERT(box.IsValid()); - return new (&inBuffer) Box(box, convex_radius); - } - } - - JPH_ASSERT(false); - return nullptr; -} - -void BoxShape::GetSupportingFace(const SubShapeID &inSubShapeID, Vec3Arg inDirection, Vec3Arg inScale, Mat44Arg inCenterOfMassTransform, SupportingFace &outVertices) const -{ - JPH_ASSERT(inSubShapeID.IsEmpty(), "Invalid subshape ID"); - - Vec3 scaled_half_extent = inScale.Abs() * mHalfExtent; - AABox box(-scaled_half_extent, scaled_half_extent); - box.GetSupportingFace(inDirection, outVertices); - - // Transform to world space - for (Vec3 &v : outVertices) - v = inCenterOfMassTransform * v; -} - -MassProperties BoxShape::GetMassProperties() const -{ - MassProperties p; - p.SetMassAndInertiaOfSolidBox(2.0f * mHalfExtent, GetDensity()); - return p; -} - -Vec3 BoxShape::GetSurfaceNormal(const SubShapeID &inSubShapeID, Vec3Arg inLocalSurfacePosition) const -{ - JPH_ASSERT(inSubShapeID.IsEmpty(), "Invalid subshape ID"); - - // Get component that is closest to the surface of the box - int index = (inLocalSurfacePosition.Abs() - mHalfExtent).Abs().GetLowestComponentIndex(); - - // Calculate normal - Vec3 normal = Vec3::sZero(); - normal.SetComponent(index, inLocalSurfacePosition[index] > 0.0f? 1.0f : -1.0f); - return normal; -} - -#ifdef JPH_DEBUG_RENDERER -void BoxShape::Draw(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inUseMaterialColors, bool inDrawWireframe) const -{ - DebugRenderer::EDrawMode draw_mode = inDrawWireframe? DebugRenderer::EDrawMode::Wireframe : DebugRenderer::EDrawMode::Solid; - inRenderer->DrawBox(inCenterOfMassTransform * Mat44::sScale(inScale.Abs()), GetLocalBounds(), inUseMaterialColors? GetMaterial()->GetDebugColor() : inColor, DebugRenderer::ECastShadow::On, draw_mode); -} -#endif // JPH_DEBUG_RENDERER - -bool BoxShape::CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const -{ - // Test hit against box - float fraction = max(RayAABox(inRay.mOrigin, RayInvDirection(inRay.mDirection), -mHalfExtent, mHalfExtent), 0.0f); - if (fraction < ioHit.mFraction) - { - ioHit.mFraction = fraction; - ioHit.mSubShapeID2 = inSubShapeIDCreator.GetID(); - return true; - } - return false; -} - -void BoxShape::CastRay(const RayCast &inRay, const RayCastSettings &inRayCastSettings, const SubShapeIDCreator &inSubShapeIDCreator, CastRayCollector &ioCollector, const ShapeFilter &inShapeFilter) const -{ - // Test shape filter - if (!inShapeFilter.ShouldCollide(this, inSubShapeIDCreator.GetID())) - return; - - float min_fraction, max_fraction; - RayAABox(inRay.mOrigin, RayInvDirection(inRay.mDirection), -mHalfExtent, mHalfExtent, min_fraction, max_fraction); - if (min_fraction <= max_fraction // Ray should intersect - && max_fraction >= 0.0f // End of ray should be inside box - && min_fraction < ioCollector.GetEarlyOutFraction()) // Start of ray should be before early out fraction - { - // Better hit than the current hit - RayCastResult hit; - hit.mBodyID = TransformedShape::sGetBodyID(ioCollector.GetContext()); - hit.mSubShapeID2 = inSubShapeIDCreator.GetID(); - - // Check front side - if (inRayCastSettings.mTreatConvexAsSolid || min_fraction > 0.0f) - { - hit.mFraction = max(0.0f, min_fraction); - ioCollector.AddHit(hit); - } - - // Check back side hit - if (inRayCastSettings.mBackFaceMode == EBackFaceMode::CollideWithBackFaces - && max_fraction < ioCollector.GetEarlyOutFraction()) - { - hit.mFraction = max_fraction; - ioCollector.AddHit(hit); - } - } -} - -void BoxShape::CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter) const -{ - // Test shape filter - if (!inShapeFilter.ShouldCollide(this, inSubShapeIDCreator.GetID())) - return; - - if (Vec3::sLessOrEqual(inPoint.Abs(), mHalfExtent).TestAllXYZTrue()) - ioCollector.AddHit({ TransformedShape::sGetBodyID(ioCollector.GetContext()), inSubShapeIDCreator.GetID() }); -} - -void BoxShape::CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, SoftBodyVertex *ioVertices, uint inNumVertices, [[maybe_unused]] float inDeltaTime, [[maybe_unused]] Vec3Arg inDisplacementDueToGravity, int inCollidingShapeIndex) const -{ - Mat44 inverse_transform = inCenterOfMassTransform.InversedRotationTranslation(); - Vec3 half_extent = inScale.Abs() * mHalfExtent; - - for (SoftBodyVertex *v = ioVertices, *sbv_end = ioVertices + inNumVertices; v < sbv_end; ++v) - if (v->mInvMass > 0.0f) - { - // Convert to local space - Vec3 local_pos = inverse_transform * v->mPosition; - - // Clamp point to inside box - Vec3 clamped_point = Vec3::sMax(Vec3::sMin(local_pos, half_extent), -half_extent); - - // Test if point was inside - if (clamped_point == local_pos) - { - // Calculate closest distance to surface - Vec3 delta = half_extent - local_pos.Abs(); - int index = delta.GetLowestComponentIndex(); - float penetration = delta[index]; - if (penetration > v->mLargestPenetration) - { - v->mLargestPenetration = penetration; - - // Calculate contact point and normal - Vec3 possible_normals[] = { Vec3::sAxisX(), Vec3::sAxisY(), Vec3::sAxisZ() }; - Vec3 normal = local_pos.GetSign() * possible_normals[index]; - Vec3 point = normal * half_extent; - - // Store collision - v->mCollisionPlane = Plane::sFromPointAndNormal(point, normal).GetTransformed(inCenterOfMassTransform); - v->mCollidingShapeIndex = inCollidingShapeIndex; - } - } - else - { - // Calculate normal - Vec3 normal = local_pos - clamped_point; - float normal_length = normal.Length(); - - // Penetration will be negative since we're not penetrating - float penetration = -normal_length; - if (penetration > v->mLargestPenetration) - { - normal /= normal_length; - - v->mLargestPenetration = penetration; - - // Store collision - v->mCollisionPlane = Plane::sFromPointAndNormal(clamped_point, normal).GetTransformed(inCenterOfMassTransform); - v->mCollidingShapeIndex = inCollidingShapeIndex; - } - } - } -} - -void BoxShape::GetTrianglesStart(GetTrianglesContext &ioContext, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) const -{ - new (&ioContext) GetTrianglesContextVertexList(inPositionCOM, inRotation, inScale, Mat44::sScale(mHalfExtent), sUnitBoxTriangles, sizeof(sUnitBoxTriangles) / sizeof(Vec3), GetMaterial()); -} - -int BoxShape::GetTrianglesNext(GetTrianglesContext &ioContext, int inMaxTrianglesRequested, Float3 *outTriangleVertices, const PhysicsMaterial **outMaterials) const -{ - return ((GetTrianglesContextVertexList &)ioContext).GetTrianglesNext(inMaxTrianglesRequested, outTriangleVertices, outMaterials); -} - -void BoxShape::SaveBinaryState(StreamOut &inStream) const -{ - ConvexShape::SaveBinaryState(inStream); - - inStream.Write(mHalfExtent); - inStream.Write(mConvexRadius); -} - -void BoxShape::RestoreBinaryState(StreamIn &inStream) -{ - ConvexShape::RestoreBinaryState(inStream); - - inStream.Read(mHalfExtent); - inStream.Read(mConvexRadius); -} - -void BoxShape::sRegister() -{ - ShapeFunctions &f = ShapeFunctions::sGet(EShapeSubType::Box); - f.mConstruct = []() -> Shape * { return new BoxShape; }; - f.mColor = Color::sGreen; -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/BoxShape.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/BoxShape.h deleted file mode 100644 index dc53772d987..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/BoxShape.h +++ /dev/null @@ -1,115 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include - -JPH_NAMESPACE_BEGIN - -/// Class that constructs a BoxShape -class JPH_EXPORT BoxShapeSettings final : public ConvexShapeSettings -{ -public: - JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, BoxShapeSettings) - - /// Default constructor for deserialization - BoxShapeSettings() = default; - - /// Create a box with half edge length inHalfExtent and convex radius inConvexRadius. - /// (internally the convex radius will be subtracted from the half extent so the total box will not grow with the convex radius). - BoxShapeSettings(Vec3Arg inHalfExtent, float inConvexRadius = cDefaultConvexRadius, const PhysicsMaterial *inMaterial = nullptr) : ConvexShapeSettings(inMaterial), mHalfExtent(inHalfExtent), mConvexRadius(inConvexRadius) { } - - // See: ShapeSettings - virtual ShapeResult Create() const override; - - Vec3 mHalfExtent = Vec3::sZero(); ///< Half the size of the box (including convex radius) - float mConvexRadius = 0.0f; -}; - -/// A box, centered around the origin -class JPH_EXPORT BoxShape final : public ConvexShape -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Constructor - BoxShape() : ConvexShape(EShapeSubType::Box) { } - BoxShape(const BoxShapeSettings &inSettings, ShapeResult &outResult); - - /// Create a box with half edge length inHalfExtent and convex radius inConvexRadius. - /// (internally the convex radius will be subtracted from the half extent so the total box will not grow with the convex radius). - BoxShape(Vec3Arg inHalfExtent, float inConvexRadius = cDefaultConvexRadius, const PhysicsMaterial *inMaterial = nullptr) : ConvexShape(EShapeSubType::Box, inMaterial), mHalfExtent(inHalfExtent), mConvexRadius(inConvexRadius) { JPH_ASSERT(inConvexRadius >= 0.0f); JPH_ASSERT(inHalfExtent.ReduceMin() >= inConvexRadius); } - - /// Get half extent of box - Vec3 GetHalfExtent() const { return mHalfExtent; } - - // See Shape::GetLocalBounds - virtual AABox GetLocalBounds() const override { return AABox(-mHalfExtent, mHalfExtent); } - - // See Shape::GetInnerRadius - virtual float GetInnerRadius() const override { return mHalfExtent.ReduceMin(); } - - // See Shape::GetMassProperties - virtual MassProperties GetMassProperties() const override; - - // See Shape::GetSurfaceNormal - virtual Vec3 GetSurfaceNormal(const SubShapeID &inSubShapeID, Vec3Arg inLocalSurfacePosition) const override; - - // See Shape::GetSupportingFace - virtual void GetSupportingFace(const SubShapeID &inSubShapeID, Vec3Arg inDirection, Vec3Arg inScale, Mat44Arg inCenterOfMassTransform, SupportingFace &outVertices) const override; - - // See ConvexShape::GetSupportFunction - virtual const Support * GetSupportFunction(ESupportMode inMode, SupportBuffer &inBuffer, Vec3Arg inScale) const override; - -#ifdef JPH_DEBUG_RENDERER - // See Shape::Draw - virtual void Draw(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inUseMaterialColors, bool inDrawWireframe) const override; -#endif // JPH_DEBUG_RENDERER - - // See Shape::CastRay - virtual bool CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const override; - virtual void CastRay(const RayCast &inRay, const RayCastSettings &inRayCastSettings, const SubShapeIDCreator &inSubShapeIDCreator, CastRayCollector &ioCollector, const ShapeFilter &inShapeFilter = { }) const override; - - // See: Shape::CollidePoint - virtual void CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter = { }) const override; - - // See: Shape::CollideSoftBodyVertices - virtual void CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, SoftBodyVertex *ioVertices, uint inNumVertices, float inDeltaTime, Vec3Arg inDisplacementDueToGravity, int inCollidingShapeIndex) const override; - - // See Shape::GetTrianglesStart - virtual void GetTrianglesStart(GetTrianglesContext &ioContext, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) const override; - - // See Shape::GetTrianglesNext - virtual int GetTrianglesNext(GetTrianglesContext &ioContext, int inMaxTrianglesRequested, Float3 *outTriangleVertices, const PhysicsMaterial **outMaterials = nullptr) const override; - - // See Shape - virtual void SaveBinaryState(StreamOut &inStream) const override; - - // See Shape::GetStats - virtual Stats GetStats() const override { return Stats(sizeof(*this), 12); } - - // See Shape::GetVolume - virtual float GetVolume() const override { return GetLocalBounds().GetVolume(); } - - /// Get the convex radius of this box - float GetConvexRadius() const { return mConvexRadius; } - - // Register shape functions with the registry - static void sRegister(); - -protected: - // See: Shape::RestoreBinaryState - virtual void RestoreBinaryState(StreamIn &inStream) override; - -private: - // Class for GetSupportFunction - class Box; - - Vec3 mHalfExtent = Vec3::sZero(); ///< Half the size of the box (including convex radius) - float mConvexRadius = 0.0f; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/CapsuleShape.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/CapsuleShape.cpp deleted file mode 100644 index 5f050ea63a5..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/CapsuleShape.cpp +++ /dev/null @@ -1,446 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef JPH_DEBUG_RENDERER - #include -#endif // JPH_DEBUG_RENDERER - -JPH_NAMESPACE_BEGIN - -JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(CapsuleShapeSettings) -{ - JPH_ADD_BASE_CLASS(CapsuleShapeSettings, ConvexShapeSettings) - - JPH_ADD_ATTRIBUTE(CapsuleShapeSettings, mRadius) - JPH_ADD_ATTRIBUTE(CapsuleShapeSettings, mHalfHeightOfCylinder) -} - -static const int cCapsuleDetailLevel = 2; - -static const std::vector sCapsuleTopTriangles = []() { - std::vector verts; - GetTrianglesContextVertexList::sCreateHalfUnitSphereTop(verts, cCapsuleDetailLevel); - return verts; -}(); - -static const std::vector sCapsuleMiddleTriangles = []() { - std::vector verts; - GetTrianglesContextVertexList::sCreateUnitOpenCylinder(verts, cCapsuleDetailLevel); - return verts; -}(); - -static const std::vector sCapsuleBottomTriangles = []() { - std::vector verts; - GetTrianglesContextVertexList::sCreateHalfUnitSphereBottom(verts, cCapsuleDetailLevel); - return verts; -}(); - -ShapeSettings::ShapeResult CapsuleShapeSettings::Create() const -{ - if (mCachedResult.IsEmpty()) - { - Ref shape; - if (IsValid() && IsSphere()) - { - // If the capsule has no height, use a sphere instead - shape = new SphereShape(mRadius, mMaterial); - mCachedResult.Set(shape); - } - else - shape = new CapsuleShape(*this, mCachedResult); - } - return mCachedResult; -} - -CapsuleShape::CapsuleShape(const CapsuleShapeSettings &inSettings, ShapeResult &outResult) : - ConvexShape(EShapeSubType::Capsule, inSettings, outResult), - mRadius(inSettings.mRadius), - mHalfHeightOfCylinder(inSettings.mHalfHeightOfCylinder) -{ - if (inSettings.mHalfHeightOfCylinder <= 0.0f) - { - outResult.SetError("Invalid height"); - return; - } - - if (inSettings.mRadius <= 0.0f) - { - outResult.SetError("Invalid radius"); - return; - } - - outResult.Set(this); -} - -class CapsuleShape::CapsuleNoConvex final : public Support -{ -public: - CapsuleNoConvex(Vec3Arg inHalfHeightOfCylinder, float inConvexRadius) : - mHalfHeightOfCylinder(inHalfHeightOfCylinder), - mConvexRadius(inConvexRadius) - { - static_assert(sizeof(CapsuleNoConvex) <= sizeof(SupportBuffer), "Buffer size too small"); - JPH_ASSERT(IsAligned(this, alignof(CapsuleNoConvex))); - } - - virtual Vec3 GetSupport(Vec3Arg inDirection) const override - { - if (inDirection.GetY() > 0) - return mHalfHeightOfCylinder; - else - return -mHalfHeightOfCylinder; - } - - virtual float GetConvexRadius() const override - { - return mConvexRadius; - } - -private: - Vec3 mHalfHeightOfCylinder; - float mConvexRadius; -}; - -class CapsuleShape::CapsuleWithConvex final : public Support -{ -public: - CapsuleWithConvex(Vec3Arg inHalfHeightOfCylinder, float inRadius) : - mHalfHeightOfCylinder(inHalfHeightOfCylinder), - mRadius(inRadius) - { - static_assert(sizeof(CapsuleWithConvex) <= sizeof(SupportBuffer), "Buffer size too small"); - JPH_ASSERT(IsAligned(this, alignof(CapsuleWithConvex))); - } - - virtual Vec3 GetSupport(Vec3Arg inDirection) const override - { - float len = inDirection.Length(); - Vec3 radius = len > 0.0f? inDirection * (mRadius / len) : Vec3::sZero(); - - if (inDirection.GetY() > 0) - return radius + mHalfHeightOfCylinder; - else - return radius - mHalfHeightOfCylinder; - } - - virtual float GetConvexRadius() const override - { - return 0.0f; - } - -private: - Vec3 mHalfHeightOfCylinder; - float mRadius; -}; - -const ConvexShape::Support *CapsuleShape::GetSupportFunction(ESupportMode inMode, SupportBuffer &inBuffer, Vec3Arg inScale) const -{ - JPH_ASSERT(IsValidScale(inScale)); - - // Get scaled capsule - Vec3 abs_scale = inScale.Abs(); - float scale = abs_scale.GetX(); - Vec3 scaled_half_height_of_cylinder = Vec3(0, scale * mHalfHeightOfCylinder, 0); - float scaled_radius = scale * mRadius; - - switch (inMode) - { - case ESupportMode::IncludeConvexRadius: - return new (&inBuffer) CapsuleWithConvex(scaled_half_height_of_cylinder, scaled_radius); - - case ESupportMode::ExcludeConvexRadius: - case ESupportMode::Default: - return new (&inBuffer) CapsuleNoConvex(scaled_half_height_of_cylinder, scaled_radius); - } - - JPH_ASSERT(false); - return nullptr; -} - -void CapsuleShape::GetSupportingFace(const SubShapeID &inSubShapeID, Vec3Arg inDirection, Vec3Arg inScale, Mat44Arg inCenterOfMassTransform, SupportingFace &outVertices) const -{ - JPH_ASSERT(inSubShapeID.IsEmpty(), "Invalid subshape ID"); - JPH_ASSERT(IsValidScale(inScale)); - - // Get direction in horizontal plane - Vec3 direction = inDirection; - direction.SetComponent(1, 0.0f); - - // Check zero vector, in this case we're hitting from top/bottom so there's no supporting face - float len = direction.Length(); - if (len == 0.0f) - return; - - // Get scaled capsule - Vec3 abs_scale = inScale.Abs(); - float scale = abs_scale.GetX(); - Vec3 scaled_half_height_of_cylinder = Vec3(0, scale * mHalfHeightOfCylinder, 0); - float scaled_radius = scale * mRadius; - - // Get support point for top and bottom sphere in the opposite of 'direction' (including convex radius) - Vec3 support = (scaled_radius / len) * direction; - Vec3 support_top = scaled_half_height_of_cylinder - support; - Vec3 support_bottom = -scaled_half_height_of_cylinder - support; - - // Get projection on inDirection - // Note that inDirection is not normalized, so we need to divide by inDirection.Length() to get the actual projection - // We've multiplied both sides of the if below with inDirection.Length() - float proj_top = support_top.Dot(inDirection); - float proj_bottom = support_bottom.Dot(inDirection); - - // If projection is roughly equal then return line, otherwise we return nothing as there's only 1 point - if (abs(proj_top - proj_bottom) < cCapsuleProjectionSlop * inDirection.Length()) - { - outVertices.push_back(inCenterOfMassTransform * support_top); - outVertices.push_back(inCenterOfMassTransform * support_bottom); - } -} - -MassProperties CapsuleShape::GetMassProperties() const -{ - MassProperties p; - - float density = GetDensity(); - - // Calculate inertia and mass according to: - // https://www.gamedev.net/resources/_/technical/math-and-physics/capsule-inertia-tensor-r3856 - // Note that there is an error in eq 14, H^2/2 should be H^2/4 in Ixx and Izz, eq 12 does contain the correct value - float radius_sq = Square(mRadius); - float height = 2.0f * mHalfHeightOfCylinder; - float cylinder_mass = JPH_PI * height * radius_sq * density; - float hemisphere_mass = (2.0f * JPH_PI / 3.0f) * radius_sq * mRadius * density; - - // From cylinder - float height_sq = Square(height); - float inertia_y = radius_sq * cylinder_mass * 0.5f; - float inertia_xz = inertia_y * 0.5f + cylinder_mass * height_sq / 12.0f; - - // From hemispheres - float temp = hemisphere_mass * 4.0f * radius_sq / 5.0f; - inertia_y += temp; - inertia_xz += temp + hemisphere_mass * (0.5f * height_sq + (3.0f / 4.0f) * height * mRadius); - - // Mass is cylinder + hemispheres - p.mMass = cylinder_mass + hemisphere_mass * 2.0f; - - // Set inertia - p.mInertia = Mat44::sScale(Vec3(inertia_xz, inertia_y, inertia_xz)); - - return p; -} - -Vec3 CapsuleShape::GetSurfaceNormal(const SubShapeID &inSubShapeID, Vec3Arg inLocalSurfacePosition) const -{ - JPH_ASSERT(inSubShapeID.IsEmpty(), "Invalid subshape ID"); - - if (inLocalSurfacePosition.GetY() > mHalfHeightOfCylinder) - return (inLocalSurfacePosition - Vec3(0, mHalfHeightOfCylinder, 0)).Normalized(); - else if (inLocalSurfacePosition.GetY() < -mHalfHeightOfCylinder) - return (inLocalSurfacePosition - Vec3(0, -mHalfHeightOfCylinder, 0)).Normalized(); - else - return Vec3(inLocalSurfacePosition.GetX(), 0, inLocalSurfacePosition.GetZ()).NormalizedOr(Vec3::sAxisX()); -} - -AABox CapsuleShape::GetLocalBounds() const -{ - Vec3 extent = Vec3::sReplicate(mRadius) + Vec3(0, mHalfHeightOfCylinder, 0); - return AABox(-extent, extent); -} - -AABox CapsuleShape::GetWorldSpaceBounds(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale) const -{ - JPH_ASSERT(IsValidScale(inScale)); - - Vec3 abs_scale = inScale.Abs(); - float scale = abs_scale.GetX(); - Vec3 extent = Vec3::sReplicate(scale * mRadius); - Vec3 height = Vec3(0, scale * mHalfHeightOfCylinder, 0); - Vec3 p1 = inCenterOfMassTransform * -height; - Vec3 p2 = inCenterOfMassTransform * height; - return AABox(Vec3::sMin(p1, p2) - extent, Vec3::sMax(p1, p2) + extent); -} - -#ifdef JPH_DEBUG_RENDERER -void CapsuleShape::Draw(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inUseMaterialColors, bool inDrawWireframe) const -{ - DebugRenderer::EDrawMode draw_mode = inDrawWireframe? DebugRenderer::EDrawMode::Wireframe : DebugRenderer::EDrawMode::Solid; - inRenderer->DrawCapsule(inCenterOfMassTransform * Mat44::sScale(inScale.Abs().GetX()), mHalfHeightOfCylinder, mRadius, inUseMaterialColors? GetMaterial()->GetDebugColor() : inColor, DebugRenderer::ECastShadow::On, draw_mode); -} -#endif // JPH_DEBUG_RENDERER - -bool CapsuleShape::CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const -{ - // Test ray against capsule - float fraction = RayCapsule(inRay.mOrigin, inRay.mDirection, mHalfHeightOfCylinder, mRadius); - if (fraction < ioHit.mFraction) - { - ioHit.mFraction = fraction; - ioHit.mSubShapeID2 = inSubShapeIDCreator.GetID(); - return true; - } - return false; -} - -void CapsuleShape::CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter) const -{ - // Test shape filter - if (!inShapeFilter.ShouldCollide(this, inSubShapeIDCreator.GetID())) - return; - - float radius_sq = Square(mRadius); - - // Get vertical distance to the top/bottom sphere centers - float delta_y = abs(inPoint.GetY()) - mHalfHeightOfCylinder; - - // Get distance in horizontal plane - float xz_sq = Square(inPoint.GetX()) + Square(inPoint.GetZ()); - - // Check if the point is in one of the two spheres - bool in_sphere = xz_sq + Square(delta_y) <= radius_sq; - - // Check if the point is in the cylinder in the middle - bool in_cylinder = delta_y <= 0.0f && xz_sq <= radius_sq; - - if (in_sphere || in_cylinder) - ioCollector.AddHit({ TransformedShape::sGetBodyID(ioCollector.GetContext()), inSubShapeIDCreator.GetID() }); -} - -void CapsuleShape::CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, SoftBodyVertex *ioVertices, uint inNumVertices, [[maybe_unused]] float inDeltaTime, [[maybe_unused]] Vec3Arg inDisplacementDueToGravity, int inCollidingShapeIndex) const -{ - JPH_ASSERT(IsValidScale(inScale)); - - Mat44 inverse_transform = inCenterOfMassTransform.InversedRotationTranslation(); - - // Get scaled capsule - float scale = abs(inScale.GetX()); - float half_height_of_cylinder = scale * mHalfHeightOfCylinder; - float radius = scale * mRadius; - - for (SoftBodyVertex *v = ioVertices, *sbv_end = ioVertices + inNumVertices; v < sbv_end; ++v) - if (v->mInvMass > 0.0f) - { - // Calculate penetration - Vec3 local_pos = inverse_transform * v->mPosition; - if (abs(local_pos.GetY()) <= half_height_of_cylinder) - { - // Near cylinder - Vec3 normal = local_pos; - normal.SetY(0.0f); - float normal_length = normal.Length(); - float penetration = radius - normal_length; - if (penetration > v->mLargestPenetration) - { - v->mLargestPenetration = penetration; - - // Calculate contact point and normal - normal = normal_length > 0.0f? normal / normal_length : Vec3::sAxisX(); - Vec3 point = radius * normal; - - // Store collision - v->mCollisionPlane = Plane::sFromPointAndNormal(point, normal).GetTransformed(inCenterOfMassTransform); - v->mCollidingShapeIndex = inCollidingShapeIndex; - } - } - else - { - // Near cap - Vec3 center = Vec3(0, Sign(local_pos.GetY()) * half_height_of_cylinder, 0); - Vec3 delta = local_pos - center; - float distance = delta.Length(); - float penetration = radius - distance; - if (penetration > v->mLargestPenetration) - { - v->mLargestPenetration = penetration; - - // Calculate contact point and normal - Vec3 normal = delta / distance; - Vec3 point = center + radius * normal; - - // Store collision - v->mCollisionPlane = Plane::sFromPointAndNormal(point, normal).GetTransformed(inCenterOfMassTransform); - v->mCollidingShapeIndex = inCollidingShapeIndex; - } - } - } -} - -void CapsuleShape::TransformShape(Mat44Arg inCenterOfMassTransform, TransformedShapeCollector &ioCollector) const -{ - Vec3 scale; - Mat44 transform = inCenterOfMassTransform.Decompose(scale); - TransformedShape ts(RVec3(transform.GetTranslation()), transform.GetQuaternion(), this, BodyID(), SubShapeIDCreator()); - ts.SetShapeScale(ScaleHelpers::MakeUniformScale(scale.Abs())); - ioCollector.AddHit(ts); -} - -void CapsuleShape::GetTrianglesStart(GetTrianglesContext &ioContext, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) const -{ - JPH_ASSERT(IsValidScale(inScale)); - - Vec3 abs_scale = inScale.Abs(); - float scale = abs_scale.GetX(); - - GetTrianglesContextMultiVertexList *context = new (&ioContext) GetTrianglesContextMultiVertexList(false, GetMaterial()); - - Mat44 world_matrix = Mat44::sRotationTranslation(inRotation, inPositionCOM) * Mat44::sScale(scale); - - Mat44 top_matrix = world_matrix * Mat44(Vec4(mRadius, 0, 0, 0), Vec4(0, mRadius, 0, 0), Vec4(0, 0, mRadius, 0), Vec4(0, mHalfHeightOfCylinder, 0, 1)); - context->AddPart(top_matrix, sCapsuleTopTriangles.data(), sCapsuleTopTriangles.size()); - - Mat44 middle_matrix = world_matrix * Mat44::sScale(Vec3(mRadius, mHalfHeightOfCylinder, mRadius)); - context->AddPart(middle_matrix, sCapsuleMiddleTriangles.data(), sCapsuleMiddleTriangles.size()); - - Mat44 bottom_matrix = world_matrix * Mat44(Vec4(mRadius, 0, 0, 0), Vec4(0, mRadius, 0, 0), Vec4(0, 0, mRadius, 0), Vec4(0, -mHalfHeightOfCylinder, 0, 1)); - context->AddPart(bottom_matrix, sCapsuleBottomTriangles.data(), sCapsuleBottomTriangles.size()); -} - -int CapsuleShape::GetTrianglesNext(GetTrianglesContext &ioContext, int inMaxTrianglesRequested, Float3 *outTriangleVertices, const PhysicsMaterial **outMaterials) const -{ - return ((GetTrianglesContextMultiVertexList &)ioContext).GetTrianglesNext(inMaxTrianglesRequested, outTriangleVertices, outMaterials); -} - -void CapsuleShape::SaveBinaryState(StreamOut &inStream) const -{ - ConvexShape::SaveBinaryState(inStream); - - inStream.Write(mRadius); - inStream.Write(mHalfHeightOfCylinder); -} - -void CapsuleShape::RestoreBinaryState(StreamIn &inStream) -{ - ConvexShape::RestoreBinaryState(inStream); - - inStream.Read(mRadius); - inStream.Read(mHalfHeightOfCylinder); -} - -bool CapsuleShape::IsValidScale(Vec3Arg inScale) const -{ - return ConvexShape::IsValidScale(inScale) && ScaleHelpers::IsUniformScale(inScale.Abs()); -} - -void CapsuleShape::sRegister() -{ - ShapeFunctions &f = ShapeFunctions::sGet(EShapeSubType::Capsule); - f.mConstruct = []() -> Shape * { return new CapsuleShape; }; - f.mColor = Color::sGreen; -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/CapsuleShape.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/CapsuleShape.h deleted file mode 100644 index 73281d0febb..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/CapsuleShape.h +++ /dev/null @@ -1,128 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -/// Class that constructs a CapsuleShape -class JPH_EXPORT CapsuleShapeSettings final : public ConvexShapeSettings -{ - JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, CapsuleShapeSettings) - - /// Default constructor for deserialization - CapsuleShapeSettings() = default; - - /// Create a capsule centered around the origin with one sphere cap at (0, -inHalfHeightOfCylinder, 0) and the other at (0, inHalfHeightOfCylinder, 0) - CapsuleShapeSettings(float inHalfHeightOfCylinder, float inRadius, const PhysicsMaterial *inMaterial = nullptr) : ConvexShapeSettings(inMaterial), mRadius(inRadius), mHalfHeightOfCylinder(inHalfHeightOfCylinder) { } - - /// Check if this is a valid capsule shape - bool IsValid() const { return mRadius > 0.0f && mHalfHeightOfCylinder >= 0.0f; } - - /// Checks if the settings of this capsule make this shape a sphere - bool IsSphere() const { return mHalfHeightOfCylinder == 0.0f; } - - // See: ShapeSettings - virtual ShapeResult Create() const override; - - float mRadius = 0.0f; - float mHalfHeightOfCylinder = 0.0f; -}; - -/// A capsule, implemented as a line segment with convex radius -class JPH_EXPORT CapsuleShape final : public ConvexShape -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Constructor - CapsuleShape() : ConvexShape(EShapeSubType::Capsule) { } - CapsuleShape(const CapsuleShapeSettings &inSettings, ShapeResult &outResult); - - /// Create a capsule centered around the origin with one sphere cap at (0, -inHalfHeightOfCylinder, 0) and the other at (0, inHalfHeightOfCylinder, 0) - CapsuleShape(float inHalfHeightOfCylinder, float inRadius, const PhysicsMaterial *inMaterial = nullptr) : ConvexShape(EShapeSubType::Capsule, inMaterial), mRadius(inRadius), mHalfHeightOfCylinder(inHalfHeightOfCylinder) { JPH_ASSERT(inHalfHeightOfCylinder > 0.0f); JPH_ASSERT(inRadius > 0.0f); } - - /// Radius of the cylinder - float GetRadius() const { return mRadius; } - - /// Get half of the height of the cylinder - float GetHalfHeightOfCylinder() const { return mHalfHeightOfCylinder; } - - // See Shape::GetLocalBounds - virtual AABox GetLocalBounds() const override; - - // See Shape::GetWorldSpaceBounds - virtual AABox GetWorldSpaceBounds(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale) const override; - using Shape::GetWorldSpaceBounds; - - // See Shape::GetInnerRadius - virtual float GetInnerRadius() const override { return mRadius; } - - // See Shape::GetMassProperties - virtual MassProperties GetMassProperties() const override; - - // See Shape::GetSurfaceNormal - virtual Vec3 GetSurfaceNormal(const SubShapeID &inSubShapeID, Vec3Arg inLocalSurfacePosition) const override; - - // See Shape::GetSupportingFace - virtual void GetSupportingFace(const SubShapeID &inSubShapeID, Vec3Arg inDirection, Vec3Arg inScale, Mat44Arg inCenterOfMassTransform, SupportingFace &outVertices) const override; - - // See ConvexShape::GetSupportFunction - virtual const Support * GetSupportFunction(ESupportMode inMode, SupportBuffer &inBuffer, Vec3Arg inScale) const override; - -#ifdef JPH_DEBUG_RENDERER - // See Shape::Draw - virtual void Draw(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inUseMaterialColors, bool inDrawWireframe) const override; -#endif // JPH_DEBUG_RENDERER - - // See Shape::CastRay - using ConvexShape::CastRay; - virtual bool CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const override; - - // See: Shape::CollidePoint - virtual void CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter = { }) const override; - - // See: Shape::CollideSoftBodyVertices - virtual void CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, SoftBodyVertex *ioVertices, uint inNumVertices, float inDeltaTime, Vec3Arg inDisplacementDueToGravity, int inCollidingShapeIndex) const override; - - // See Shape::TransformShape - virtual void TransformShape(Mat44Arg inCenterOfMassTransform, TransformedShapeCollector &ioCollector) const override; - - // See Shape::GetTrianglesStart - virtual void GetTrianglesStart(GetTrianglesContext &ioContext, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) const override; - - // See Shape::GetTrianglesNext - virtual int GetTrianglesNext(GetTrianglesContext &ioContext, int inMaxTrianglesRequested, Float3 *outTriangleVertices, const PhysicsMaterial **outMaterials = nullptr) const override; - - // See Shape - virtual void SaveBinaryState(StreamOut &inStream) const override; - - // See Shape::GetStats - virtual Stats GetStats() const override { return Stats(sizeof(*this), 0); } - - // See Shape::GetVolume - virtual float GetVolume() const override { return 4.0f / 3.0f * JPH_PI * Cubed(mRadius) + 2.0f * JPH_PI * mHalfHeightOfCylinder * Square(mRadius); } - - // See Shape::IsValidScale - virtual bool IsValidScale(Vec3Arg inScale) const override; - - // Register shape functions with the registry - static void sRegister(); - -protected: - // See: Shape::RestoreBinaryState - virtual void RestoreBinaryState(StreamIn &inStream) override; - -private: - // Classes for GetSupportFunction - class CapsuleNoConvex; - class CapsuleWithConvex; - - float mRadius = 0.0f; - float mHalfHeightOfCylinder = 0.0f; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/CompoundShape.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/CompoundShape.cpp deleted file mode 100644 index 20a61f28a52..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/CompoundShape.cpp +++ /dev/null @@ -1,398 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef JPH_DEBUG_RENDERER - #include -#endif // JPH_DEBUG_RENDERER - -JPH_NAMESPACE_BEGIN - -JPH_IMPLEMENT_SERIALIZABLE_ABSTRACT(CompoundShapeSettings) -{ - JPH_ADD_BASE_CLASS(CompoundShapeSettings, ShapeSettings) - - JPH_ADD_ATTRIBUTE(CompoundShapeSettings, mSubShapes) -} - -JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(CompoundShapeSettings::SubShapeSettings) -{ - JPH_ADD_ATTRIBUTE(CompoundShapeSettings::SubShapeSettings, mShape) - JPH_ADD_ATTRIBUTE(CompoundShapeSettings::SubShapeSettings, mPosition) - JPH_ADD_ATTRIBUTE(CompoundShapeSettings::SubShapeSettings, mRotation) - JPH_ADD_ATTRIBUTE(CompoundShapeSettings::SubShapeSettings, mUserData) -} - -void CompoundShapeSettings::AddShape(Vec3Arg inPosition, QuatArg inRotation, const ShapeSettings *inShape, uint32 inUserData) -{ - // Add shape - SubShapeSettings shape; - shape.mPosition = inPosition; - shape.mRotation = inRotation; - shape.mShape = inShape; - shape.mUserData = inUserData; - mSubShapes.push_back(shape); -} - -void CompoundShapeSettings::AddShape(Vec3Arg inPosition, QuatArg inRotation, const Shape *inShape, uint32 inUserData) -{ - // Add shape - SubShapeSettings shape; - shape.mPosition = inPosition; - shape.mRotation = inRotation; - shape.mShapePtr = inShape; - shape.mUserData = inUserData; - mSubShapes.push_back(shape); -} - -bool CompoundShape::MustBeStatic() const -{ - for (const SubShape &shape : mSubShapes) - if (shape.mShape->MustBeStatic()) - return true; - - return false; -} - -MassProperties CompoundShape::GetMassProperties() const -{ - MassProperties p; - - // Calculate mass and inertia - p.mMass = 0.0f; - p.mInertia = Mat44::sZero(); - for (const SubShape &shape : mSubShapes) - { - // Rotate and translate inertia of child into place - MassProperties child = shape.mShape->GetMassProperties(); - child.Rotate(Mat44::sRotation(shape.GetRotation())); - child.Translate(shape.GetPositionCOM()); - - // Accumulate mass and inertia - p.mMass += child.mMass; - p.mInertia += child.mInertia; - } - - // Ensure that inertia is a 3x3 matrix, adding inertias causes the bottom right element to change - p.mInertia.SetColumn4(3, Vec4(0, 0, 0, 1)); - - return p; -} - -AABox CompoundShape::GetWorldSpaceBounds(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale) const -{ - if (mSubShapes.size() <= 10) - { - AABox bounds; - for (const SubShape &shape : mSubShapes) - { - Mat44 transform = inCenterOfMassTransform * shape.GetLocalTransformNoScale(inScale); - bounds.Encapsulate(shape.mShape->GetWorldSpaceBounds(transform, shape.TransformScale(inScale))); - } - return bounds; - } - else - { - // If there are too many shapes, use the base class function (this will result in a slightly wider bounding box) - return Shape::GetWorldSpaceBounds(inCenterOfMassTransform, inScale); - } -} - -uint CompoundShape::GetSubShapeIDBitsRecursive() const -{ - // Add max of child bits to our bits - uint child_bits = 0; - for (const SubShape &shape : mSubShapes) - child_bits = max(child_bits, shape.mShape->GetSubShapeIDBitsRecursive()); - return child_bits + GetSubShapeIDBits(); -} - -const PhysicsMaterial *CompoundShape::GetMaterial(const SubShapeID &inSubShapeID) const -{ - // Decode sub shape index - SubShapeID remainder; - uint32 index = GetSubShapeIndexFromID(inSubShapeID, remainder); - - // Pass call on - return mSubShapes[index].mShape->GetMaterial(remainder); -} - -uint64 CompoundShape::GetSubShapeUserData(const SubShapeID &inSubShapeID) const -{ - // Decode sub shape index - SubShapeID remainder; - uint32 index = GetSubShapeIndexFromID(inSubShapeID, remainder); - if (index >= mSubShapes.size()) - return 0; // No longer valid index - - // Pass call on - return mSubShapes[index].mShape->GetSubShapeUserData(remainder); -} - -TransformedShape CompoundShape::GetSubShapeTransformedShape(const SubShapeID &inSubShapeID, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale, SubShapeID &outRemainder) const -{ - // Get the sub shape - const SubShape &sub_shape = mSubShapes[GetSubShapeIndexFromID(inSubShapeID, outRemainder)]; - - // Calculate transform for sub shape - Vec3 position = inPositionCOM + inRotation * (inScale * sub_shape.GetPositionCOM()); - Quat rotation = inRotation * sub_shape.GetRotation(); - Vec3 scale = sub_shape.TransformScale(inScale); - - // Return transformed shape - TransformedShape ts(RVec3(position), rotation, sub_shape.mShape, BodyID()); - ts.SetShapeScale(scale); - return ts; -} - -Vec3 CompoundShape::GetSurfaceNormal(const SubShapeID &inSubShapeID, Vec3Arg inLocalSurfacePosition) const -{ - // Decode sub shape index - SubShapeID remainder; - uint32 index = GetSubShapeIndexFromID(inSubShapeID, remainder); - - // Transform surface position to local space and pass call on - const SubShape &shape = mSubShapes[index]; - Mat44 transform = Mat44::sInverseRotationTranslation(shape.GetRotation(), shape.GetPositionCOM()); - Vec3 normal = shape.mShape->GetSurfaceNormal(remainder, transform * inLocalSurfacePosition); - - // Transform normal to this shape's space - return transform.Multiply3x3Transposed(normal); -} - -void CompoundShape::GetSupportingFace(const SubShapeID &inSubShapeID, Vec3Arg inDirection, Vec3Arg inScale, Mat44Arg inCenterOfMassTransform, SupportingFace &outVertices) const -{ - // Decode sub shape index - SubShapeID remainder; - uint32 index = GetSubShapeIndexFromID(inSubShapeID, remainder); - - // Apply transform and pass on to sub shape - const SubShape &shape = mSubShapes[index]; - Mat44 transform = shape.GetLocalTransformNoScale(inScale); - shape.mShape->GetSupportingFace(remainder, transform.Multiply3x3Transposed(inDirection), shape.TransformScale(inScale), inCenterOfMassTransform * transform, outVertices); -} - -void CompoundShape::GetSubmergedVolume(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, const Plane &inSurface, float &outTotalVolume, float &outSubmergedVolume, Vec3 &outCenterOfBuoyancy JPH_IF_DEBUG_RENDERER(, RVec3Arg inBaseOffset)) const -{ - outTotalVolume = 0.0f; - outSubmergedVolume = 0.0f; - outCenterOfBuoyancy = Vec3::sZero(); - - for (const SubShape &shape : mSubShapes) - { - // Get center of mass transform of child - Mat44 transform = inCenterOfMassTransform * shape.GetLocalTransformNoScale(inScale); - - // Recurse to child - float total_volume, submerged_volume; - Vec3 center_of_buoyancy; - shape.mShape->GetSubmergedVolume(transform, shape.TransformScale(inScale), inSurface, total_volume, submerged_volume, center_of_buoyancy JPH_IF_DEBUG_RENDERER(, inBaseOffset)); - - // Accumulate volumes - outTotalVolume += total_volume; - outSubmergedVolume += submerged_volume; - - // The center of buoyancy is the weighted average of the center of buoyancy of our child shapes - outCenterOfBuoyancy += submerged_volume * center_of_buoyancy; - } - - if (outSubmergedVolume > 0.0f) - outCenterOfBuoyancy /= outSubmergedVolume; - -#ifdef JPH_DEBUG_RENDERER - // Draw center of buoyancy - if (sDrawSubmergedVolumes) - DebugRenderer::sInstance->DrawWireSphere(inBaseOffset + outCenterOfBuoyancy, 0.05f, Color::sRed, 1); -#endif // JPH_DEBUG_RENDERER -} - -#ifdef JPH_DEBUG_RENDERER -void CompoundShape::Draw(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inUseMaterialColors, bool inDrawWireframe) const -{ - for (const SubShape &shape : mSubShapes) - { - Mat44 transform = shape.GetLocalTransformNoScale(inScale); - shape.mShape->Draw(inRenderer, inCenterOfMassTransform * transform, shape.TransformScale(inScale), inColor, inUseMaterialColors, inDrawWireframe); - } -} - -void CompoundShape::DrawGetSupportFunction(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inDrawSupportDirection) const -{ - for (const SubShape &shape : mSubShapes) - { - Mat44 transform = shape.GetLocalTransformNoScale(inScale); - shape.mShape->DrawGetSupportFunction(inRenderer, inCenterOfMassTransform * transform, shape.TransformScale(inScale), inColor, inDrawSupportDirection); - } -} - -void CompoundShape::DrawGetSupportingFace(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale) const -{ - for (const SubShape &shape : mSubShapes) - { - Mat44 transform = shape.GetLocalTransformNoScale(inScale); - shape.mShape->DrawGetSupportingFace(inRenderer, inCenterOfMassTransform * transform, shape.TransformScale(inScale)); - } -} -#endif // JPH_DEBUG_RENDERER - -void CompoundShape::CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, SoftBodyVertex *ioVertices, uint inNumVertices, float inDeltaTime, Vec3Arg inDisplacementDueToGravity, int inCollidingShapeIndex) const -{ - for (const SubShape &shape : mSubShapes) - { - Mat44 transform = shape.GetLocalTransformNoScale(inScale); - shape.mShape->CollideSoftBodyVertices(inCenterOfMassTransform * transform, shape.TransformScale(inScale), ioVertices, inNumVertices, inDeltaTime, inDisplacementDueToGravity, inCollidingShapeIndex); - } -} - -void CompoundShape::TransformShape(Mat44Arg inCenterOfMassTransform, TransformedShapeCollector &ioCollector) const -{ - for (const SubShape &shape : mSubShapes) - shape.mShape->TransformShape(inCenterOfMassTransform * Mat44::sRotationTranslation(shape.GetRotation(), shape.GetPositionCOM()), ioCollector); -} - -void CompoundShape::sCastCompoundVsShape(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector) -{ - JPH_PROFILE_FUNCTION(); - - // Fetch compound shape from cast shape - JPH_ASSERT(inShapeCast.mShape->GetType() == EShapeType::Compound); - const CompoundShape *compound = static_cast(inShapeCast.mShape); - - // Number of sub shapes - int n = (int)compound->mSubShapes.size(); - - // Determine amount of bits for sub shape - uint sub_shape_bits = compound->GetSubShapeIDBits(); - - // Recurse to sub shapes - for (int i = 0; i < n; ++i) - { - const SubShape &shape = compound->mSubShapes[i]; - - // Create ID for sub shape - SubShapeIDCreator shape1_sub_shape_id = inSubShapeIDCreator1.PushID(i, sub_shape_bits); - - // Transform the shape cast and update the shape - Mat44 transform = inShapeCast.mCenterOfMassStart * shape.GetLocalTransformNoScale(inShapeCast.mScale); - Vec3 scale = shape.TransformScale(inShapeCast.mScale); - ShapeCast shape_cast(shape.mShape, scale, transform, inShapeCast.mDirection); - - CollisionDispatch::sCastShapeVsShapeLocalSpace(shape_cast, inShapeCastSettings, inShape, inScale, inShapeFilter, inCenterOfMassTransform2, shape1_sub_shape_id, inSubShapeIDCreator2, ioCollector); - - if (ioCollector.ShouldEarlyOut()) - break; - } -} - -void CompoundShape::SaveBinaryState(StreamOut &inStream) const -{ - Shape::SaveBinaryState(inStream); - - inStream.Write(mCenterOfMass); - inStream.Write(mLocalBounds.mMin); - inStream.Write(mLocalBounds.mMax); - inStream.Write(mInnerRadius); - - // Write sub shapes - inStream.Write(mSubShapes, [](const SubShape &inElement, StreamOut &inS) { - inS.Write(inElement.mUserData); - inS.Write(inElement.mPositionCOM); - inS.Write(inElement.mRotation); - }); -} - -void CompoundShape::RestoreBinaryState(StreamIn &inStream) -{ - Shape::RestoreBinaryState(inStream); - - inStream.Read(mCenterOfMass); - inStream.Read(mLocalBounds.mMin); - inStream.Read(mLocalBounds.mMax); - inStream.Read(mInnerRadius); - - // Read sub shapes - inStream.Read(mSubShapes, [](StreamIn &inS, SubShape &outElement) { - inS.Read(outElement.mUserData); - inS.Read(outElement.mPositionCOM); - inS.Read(outElement.mRotation); - outElement.mIsRotationIdentity = outElement.mRotation == Float3(0, 0, 0); - }); -} - -void CompoundShape::SaveSubShapeState(ShapeList &outSubShapes) const -{ - outSubShapes.clear(); - outSubShapes.reserve(mSubShapes.size()); - for (const SubShape &shape : mSubShapes) - outSubShapes.push_back(shape.mShape); -} - -void CompoundShape::RestoreSubShapeState(const ShapeRefC *inSubShapes, uint inNumShapes) -{ - JPH_ASSERT(mSubShapes.size() == inNumShapes); - for (uint i = 0; i < inNumShapes; ++i) - mSubShapes[i].mShape = inSubShapes[i]; -} - -Shape::Stats CompoundShape::GetStatsRecursive(VisitedShapes &ioVisitedShapes) const -{ - // Get own stats - Stats stats = Shape::GetStatsRecursive(ioVisitedShapes); - - // Add child stats - for (const SubShape &shape : mSubShapes) - { - Stats child_stats = shape.mShape->GetStatsRecursive(ioVisitedShapes); - stats.mSizeBytes += child_stats.mSizeBytes; - stats.mNumTriangles += child_stats.mNumTriangles; - } - - return stats; -} - -float CompoundShape::GetVolume() const -{ - float volume = 0.0f; - for (const SubShape &shape : mSubShapes) - volume += shape.mShape->GetVolume(); - return volume; -} - -bool CompoundShape::IsValidScale(Vec3Arg inScale) const -{ - if (!Shape::IsValidScale(inScale)) - return false; - - for (const SubShape &shape : mSubShapes) - { - // Test if the scale is non-uniform and the shape is rotated - if (!shape.IsValidScale(inScale)) - return false; - - // Test the child shape - if (!shape.mShape->IsValidScale(shape.TransformScale(inScale))) - return false; - } - - return true; -} - -void CompoundShape::sRegister() -{ - for (EShapeSubType s1 : sCompoundSubShapeTypes) - for (EShapeSubType s2 : sAllSubShapeTypes) - CollisionDispatch::sRegisterCastShape(s1, s2, sCastCompoundVsShape); -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/CompoundShape.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/CompoundShape.h deleted file mode 100644 index 7694bf4e000..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/CompoundShape.h +++ /dev/null @@ -1,344 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -class CollideShapeSettings; -class OrientedBox; - -/// Base class settings to construct a compound shape -class JPH_EXPORT CompoundShapeSettings : public ShapeSettings -{ -public: - JPH_DECLARE_SERIALIZABLE_ABSTRACT(JPH_EXPORT, CompoundShapeSettings) - - /// Constructor. Use AddShape to add the parts. - CompoundShapeSettings() = default; - - /// Add a shape to the compound. - void AddShape(Vec3Arg inPosition, QuatArg inRotation, const ShapeSettings *inShape, uint32 inUserData = 0); - - /// Add a shape to the compound. Variant that uses a concrete shape, which means this object cannot be serialized. - void AddShape(Vec3Arg inPosition, QuatArg inRotation, const Shape *inShape, uint32 inUserData = 0); - - struct SubShapeSettings - { - JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, SubShapeSettings) - - RefConst mShape; ///< Sub shape (either this or mShapePtr needs to be filled up) - RefConst mShapePtr; ///< Sub shape (either this or mShape needs to be filled up) - Vec3 mPosition; ///< Position of the sub shape - Quat mRotation; ///< Rotation of the sub shape - uint32 mUserData = 0; ///< User data value (can be used by the application for any purpose) - }; - - using SubShapes = Array; - - SubShapes mSubShapes; -}; - -/// Base class for a compound shape -class JPH_EXPORT CompoundShape : public Shape -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Constructor - explicit CompoundShape(EShapeSubType inSubType) : Shape(EShapeType::Compound, inSubType) { } - CompoundShape(EShapeSubType inSubType, const ShapeSettings &inSettings, ShapeResult &outResult) : Shape(EShapeType::Compound, inSubType, inSettings, outResult) { } - - // See Shape::GetCenterOfMass - virtual Vec3 GetCenterOfMass() const override { return mCenterOfMass; } - - // See Shape::MustBeStatic - virtual bool MustBeStatic() const override; - - // See Shape::GetLocalBounds - virtual AABox GetLocalBounds() const override { return mLocalBounds; } - - // See Shape::GetSubShapeIDBitsRecursive - virtual uint GetSubShapeIDBitsRecursive() const override; - - // See Shape::GetWorldSpaceBounds - virtual AABox GetWorldSpaceBounds(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale) const override; - using Shape::GetWorldSpaceBounds; - - // See Shape::GetInnerRadius - virtual float GetInnerRadius() const override { return mInnerRadius; } - - // See Shape::GetMassProperties - virtual MassProperties GetMassProperties() const override; - - // See Shape::GetMaterial - virtual const PhysicsMaterial * GetMaterial(const SubShapeID &inSubShapeID) const override; - - // See Shape::GetSubShapeUserData - virtual uint64 GetSubShapeUserData(const SubShapeID &inSubShapeID) const override; - - // See Shape::GetSubShapeTransformedShape - virtual TransformedShape GetSubShapeTransformedShape(const SubShapeID &inSubShapeID, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale, SubShapeID &outRemainder) const override; - - // See Shape::GetSurfaceNormal - virtual Vec3 GetSurfaceNormal(const SubShapeID &inSubShapeID, Vec3Arg inLocalSurfacePosition) const override; - - // See Shape::GetSupportingFace - virtual void GetSupportingFace(const SubShapeID &inSubShapeID, Vec3Arg inDirection, Vec3Arg inScale, Mat44Arg inCenterOfMassTransform, SupportingFace &outVertices) const override; - - // See Shape::GetSubmergedVolume - virtual void GetSubmergedVolume(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, const Plane &inSurface, float &outTotalVolume, float &outSubmergedVolume, Vec3 &outCenterOfBuoyancy JPH_IF_DEBUG_RENDERER(, RVec3Arg inBaseOffset)) const override; - -#ifdef JPH_DEBUG_RENDERER - // See Shape::Draw - virtual void Draw(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inUseMaterialColors, bool inDrawWireframe) const override; - - // See Shape::DrawGetSupportFunction - virtual void DrawGetSupportFunction(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inDrawSupportDirection) const override; - - // See Shape::DrawGetSupportingFace - virtual void DrawGetSupportingFace(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale) const override; -#endif // JPH_DEBUG_RENDERER - - // See: Shape::CollideSoftBodyVertices - virtual void CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, SoftBodyVertex *ioVertices, uint inNumVertices, float inDeltaTime, Vec3Arg inDisplacementDueToGravity, int inCollidingShapeIndex) const override; - - // See Shape::TransformShape - virtual void TransformShape(Mat44Arg inCenterOfMassTransform, TransformedShapeCollector &ioCollector) const override; - - // See Shape::GetTrianglesStart - virtual void GetTrianglesStart(GetTrianglesContext &ioContext, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) const override { JPH_ASSERT(false, "Cannot call on non-leaf shapes, use CollectTransformedShapes to collect the leaves first!"); } - - // See Shape::GetTrianglesNext - virtual int GetTrianglesNext(GetTrianglesContext &ioContext, int inMaxTrianglesRequested, Float3 *outTriangleVertices, const PhysicsMaterial **outMaterials = nullptr) const override { JPH_ASSERT(false, "Cannot call on non-leaf shapes, use CollectTransformedShapes to collect the leaves first!"); return 0; } - - /// Get which sub shape's bounding boxes overlap with an axis aligned box - /// @param inBox The axis aligned box to test against (relative to the center of mass of this shape) - /// @param outSubShapeIndices Buffer where to place the indices of the sub shapes that intersect - /// @param inMaxSubShapeIndices How many indices will fit in the buffer (normally you'd provide a buffer of GetNumSubShapes() indices) - /// @return How many indices were placed in outSubShapeIndices - virtual int GetIntersectingSubShapes(const AABox &inBox, uint *outSubShapeIndices, int inMaxSubShapeIndices) const = 0; - - /// Get which sub shape's bounding boxes overlap with an axis aligned box - /// @param inBox The axis aligned box to test against (relative to the center of mass of this shape) - /// @param outSubShapeIndices Buffer where to place the indices of the sub shapes that intersect - /// @param inMaxSubShapeIndices How many indices will fit in the buffer (normally you'd provide a buffer of GetNumSubShapes() indices) - /// @return How many indices were placed in outSubShapeIndices - virtual int GetIntersectingSubShapes(const OrientedBox &inBox, uint *outSubShapeIndices, int inMaxSubShapeIndices) const = 0; - - struct SubShape - { - /// Initialize sub shape from sub shape settings - /// @param inSettings Settings object - /// @param outResult Result object, only used in case of error - /// @return True on success, false on failure - bool FromSettings(const CompoundShapeSettings::SubShapeSettings &inSettings, ShapeResult &outResult) - { - if (inSettings.mShapePtr != nullptr) - { - // Use provided shape - mShape = inSettings.mShapePtr; - } - else - { - // Create child shape - ShapeResult child_result = inSettings.mShape->Create(); - if (!child_result.IsValid()) - { - outResult = child_result; - return false; - } - mShape = child_result.Get(); - } - - // Copy user data - mUserData = inSettings.mUserData; - - SetTransform(inSettings.mPosition, inSettings.mRotation, Vec3::sZero() /* Center of mass not yet calculated */); - return true; - } - - /// Update the transform of this sub shape - /// @param inPosition New position - /// @param inRotation New orientation - /// @param inCenterOfMass The center of mass of the compound shape - JPH_INLINE void SetTransform(Vec3Arg inPosition, QuatArg inRotation, Vec3Arg inCenterOfMass) - { - SetPositionCOM(inPosition - inCenterOfMass + inRotation * mShape->GetCenterOfMass()); - - mIsRotationIdentity = inRotation.IsClose(Quat::sIdentity()) || inRotation.IsClose(-Quat::sIdentity()); - SetRotation(mIsRotationIdentity? Quat::sIdentity() : inRotation); - } - - /// Get the local transform for this shape given the scale of the child shape - /// The total transform of the child shape will be GetLocalTransformNoScale(inScale) * Mat44::sScaling(TransformScale(inScale)) - /// @param inScale The scale of the child shape (in local space of this shape) - JPH_INLINE Mat44 GetLocalTransformNoScale(Vec3Arg inScale) const - { - JPH_ASSERT(IsValidScale(inScale)); - return Mat44::sRotationTranslation(GetRotation(), inScale * GetPositionCOM()); - } - - /// Test if inScale is valid for this sub shape - inline bool IsValidScale(Vec3Arg inScale) const - { - // We can always handle uniform scale or identity rotations - if (mIsRotationIdentity || ScaleHelpers::IsUniformScale(inScale)) - return true; - - return ScaleHelpers::CanScaleBeRotated(GetRotation(), inScale); - } - - /// Transform the scale to the local space of the child shape - inline Vec3 TransformScale(Vec3Arg inScale) const - { - // We don't need to transform uniform scale or if the rotation is identity - if (mIsRotationIdentity || ScaleHelpers::IsUniformScale(inScale)) - return inScale; - - return ScaleHelpers::RotateScale(GetRotation(), inScale); - } - - /// Compress the center of mass position - JPH_INLINE void SetPositionCOM(Vec3Arg inPositionCOM) - { - inPositionCOM.StoreFloat3(&mPositionCOM); - } - - /// Uncompress the center of mass position - JPH_INLINE Vec3 GetPositionCOM() const - { - return Vec3::sLoadFloat3Unsafe(mPositionCOM); - } - - /// Compress the rotation - JPH_INLINE void SetRotation(QuatArg inRotation) - { - inRotation.StoreFloat3(&mRotation); - } - - /// Uncompress the rotation - JPH_INLINE Quat GetRotation() const - { - return mIsRotationIdentity? Quat::sIdentity() : Quat::sLoadFloat3Unsafe(mRotation); - } - - RefConst mShape; - Float3 mPositionCOM; ///< Note: Position of center of mass of sub shape! - Float3 mRotation; ///< Note: X, Y, Z of rotation quaternion - note we read 4 bytes beyond this so make sure there's something there - uint32 mUserData; ///< User data value (put here because it falls in padding bytes) - bool mIsRotationIdentity; ///< If mRotation is close to identity (put here because it falls in padding bytes) - // 3 padding bytes left - }; - - static_assert(sizeof(SubShape) == (JPH_CPU_ADDRESS_BITS == 64? 40 : 36), "Compiler added unexpected padding"); - - using SubShapes = Array; - - /// Access to the sub shapes of this compound - const SubShapes & GetSubShapes() const { return mSubShapes; } - - /// Get the total number of sub shapes - uint GetNumSubShapes() const { return uint(mSubShapes.size()); } - - /// Access to a particular sub shape - const SubShape & GetSubShape(uint inIdx) const { return mSubShapes[inIdx]; } - - /// Get the user data associated with a shape in this compound - uint32 GetCompoundUserData(uint inIdx) const { return mSubShapes[inIdx].mUserData; } - - /// Set the user data associated with a shape in this compound - void SetCompoundUserData(uint inIdx, uint32 inUserData) { mSubShapes[inIdx].mUserData = inUserData; } - - /// Check if a sub shape ID is still valid for this shape - /// @param inSubShapeID Sub shape id that indicates the leaf shape relative to this shape - /// @return True if the ID is valid, false if not - inline bool IsSubShapeIDValid(SubShapeID inSubShapeID) const - { - SubShapeID remainder; - return inSubShapeID.PopID(GetSubShapeIDBits(), remainder) < mSubShapes.size(); - } - - /// Convert SubShapeID to sub shape index - /// @param inSubShapeID Sub shape id that indicates the leaf shape relative to this shape - /// @param outRemainder This is the sub shape ID for the sub shape of the compound after popping off the index - /// @return The index of the sub shape of this compound - inline uint32 GetSubShapeIndexFromID(SubShapeID inSubShapeID, SubShapeID &outRemainder) const - { - uint32 idx = inSubShapeID.PopID(GetSubShapeIDBits(), outRemainder); - JPH_ASSERT(idx < mSubShapes.size(), "Invalid SubShapeID"); - return idx; - } - - /// @brief Convert a sub shape index to a sub shape ID - /// @param inIdx Index of the sub shape of this compound - /// @param inParentSubShapeID Parent SubShapeID (describing the path to the compound shape) - /// @return A sub shape ID creator that contains the full path to the sub shape with index inIdx - inline SubShapeIDCreator GetSubShapeIDFromIndex(int inIdx, const SubShapeIDCreator &inParentSubShapeID) const - { - return inParentSubShapeID.PushID(inIdx, GetSubShapeIDBits()); - } - - // See Shape - virtual void SaveBinaryState(StreamOut &inStream) const override; - virtual void SaveSubShapeState(ShapeList &outSubShapes) const override; - virtual void RestoreSubShapeState(const ShapeRefC *inSubShapes, uint inNumShapes) override; - - // See Shape::GetStatsRecursive - virtual Stats GetStatsRecursive(VisitedShapes &ioVisitedShapes) const override; - - // See Shape::GetVolume - virtual float GetVolume() const override; - - // See Shape::IsValidScale - virtual bool IsValidScale(Vec3Arg inScale) const override; - - // Register shape functions with the registry - static void sRegister(); - -protected: - // See: Shape::RestoreBinaryState - virtual void RestoreBinaryState(StreamIn &inStream) override; - - // Visitors for collision detection - struct CastRayVisitor; - struct CastRayVisitorCollector; - struct CollidePointVisitor; - struct CastShapeVisitor; - struct CollectTransformedShapesVisitor; - struct CollideCompoundVsShapeVisitor; - struct CollideShapeVsCompoundVisitor; - template struct GetIntersectingSubShapesVisitor; - - /// Determine amount of bits needed to encode sub shape id - inline uint GetSubShapeIDBits() const - { - // Ensure we have enough bits to encode our shape [0, n - 1] - uint32 n = uint32(mSubShapes.size()) - 1; - return 32 - CountLeadingZeros(n); - } - - /// Determine the inner radius of this shape - inline void CalculateInnerRadius() - { - mInnerRadius = FLT_MAX; - for (const SubShape &s : mSubShapes) - mInnerRadius = min(mInnerRadius, s.mShape->GetInnerRadius()); - } - - Vec3 mCenterOfMass { Vec3::sZero() }; ///< Center of mass of the compound - AABox mLocalBounds; - SubShapes mSubShapes; - float mInnerRadius = FLT_MAX; ///< Smallest radius of GetInnerRadius() of child shapes - -private: - // Helper functions called by CollisionDispatch - static void sCastCompoundVsShape(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector); -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/CompoundShapeVisitors.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/CompoundShapeVisitors.h deleted file mode 100644 index ecccb658a4f..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/CompoundShapeVisitors.h +++ /dev/null @@ -1,460 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -struct CompoundShape::CastRayVisitor -{ - JPH_INLINE CastRayVisitor(const RayCast &inRay, const CompoundShape *inShape, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) : - mRay(inRay), - mHit(ioHit), - mSubShapeIDCreator(inSubShapeIDCreator), - mSubShapeBits(inShape->GetSubShapeIDBits()) - { - // Determine ray properties of cast - mInvDirection.Set(inRay.mDirection); - } - - /// Returns true when collision detection should abort because it's not possible to find a better hit - JPH_INLINE bool ShouldAbort() const - { - return mHit.mFraction <= 0.0f; - } - - /// Test ray against 4 bounding boxes and returns the distance where the ray enters the bounding box - JPH_INLINE Vec4 TestBounds(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ) const - { - return RayAABox4(mRay.mOrigin, mInvDirection, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ); - } - - /// Test the ray against a single subshape - JPH_INLINE void VisitShape(const SubShape &inSubShape, uint32 inSubShapeIndex) - { - // Create ID for sub shape - SubShapeIDCreator shape2_sub_shape_id = mSubShapeIDCreator.PushID(inSubShapeIndex, mSubShapeBits); - - // Transform the ray - Mat44 transform = Mat44::sInverseRotationTranslation(inSubShape.GetRotation(), inSubShape.GetPositionCOM()); - RayCast ray = mRay.Transformed(transform); - if (inSubShape.mShape->CastRay(ray, shape2_sub_shape_id, mHit)) - mReturnValue = true; - } - - RayInvDirection mInvDirection; - const RayCast & mRay; - RayCastResult & mHit; - SubShapeIDCreator mSubShapeIDCreator; - uint mSubShapeBits; - bool mReturnValue = false; -}; - -struct CompoundShape::CastRayVisitorCollector -{ - JPH_INLINE CastRayVisitorCollector(const RayCast &inRay, const RayCastSettings &inRayCastSettings, const CompoundShape *inShape, const SubShapeIDCreator &inSubShapeIDCreator, CastRayCollector &ioCollector, const ShapeFilter &inShapeFilter) : - mRay(inRay), - mCollector(ioCollector), - mSubShapeIDCreator(inSubShapeIDCreator), - mSubShapeBits(inShape->GetSubShapeIDBits()), - mRayCastSettings(inRayCastSettings), - mShapeFilter(inShapeFilter) - { - // Determine ray properties of cast - mInvDirection.Set(inRay.mDirection); - } - - /// Returns true when collision detection should abort because it's not possible to find a better hit - JPH_INLINE bool ShouldAbort() const - { - return mCollector.ShouldEarlyOut(); - } - - /// Test ray against 4 bounding boxes and returns the distance where the ray enters the bounding box - JPH_INLINE Vec4 TestBounds(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ) const - { - return RayAABox4(mRay.mOrigin, mInvDirection, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ); - } - - /// Test the ray against a single subshape - JPH_INLINE void VisitShape(const SubShape &inSubShape, uint32 inSubShapeIndex) - { - // Create ID for sub shape - SubShapeIDCreator shape2_sub_shape_id = mSubShapeIDCreator.PushID(inSubShapeIndex, mSubShapeBits); - - // Transform the ray - Mat44 transform = Mat44::sInverseRotationTranslation(inSubShape.GetRotation(), inSubShape.GetPositionCOM()); - RayCast ray = mRay.Transformed(transform); - inSubShape.mShape->CastRay(ray, mRayCastSettings, shape2_sub_shape_id, mCollector, mShapeFilter); - } - - RayInvDirection mInvDirection; - const RayCast & mRay; - CastRayCollector & mCollector; - SubShapeIDCreator mSubShapeIDCreator; - uint mSubShapeBits; - RayCastSettings mRayCastSettings; - const ShapeFilter & mShapeFilter; -}; - -struct CompoundShape::CollidePointVisitor -{ - JPH_INLINE CollidePointVisitor(Vec3Arg inPoint, const CompoundShape *inShape, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter) : - mPoint(inPoint), - mSubShapeIDCreator(inSubShapeIDCreator), - mCollector(ioCollector), - mSubShapeBits(inShape->GetSubShapeIDBits()), - mShapeFilter(inShapeFilter) - { - } - - /// Returns true when collision detection should abort because it's not possible to find a better hit - JPH_INLINE bool ShouldAbort() const - { - return mCollector.ShouldEarlyOut(); - } - - /// Test if point overlaps with 4 boxes, returns true for the ones that do - JPH_INLINE UVec4 TestBounds(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ) const - { - return AABox4VsPoint(mPoint, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ); - } - - /// Test the point against a single subshape - JPH_INLINE void VisitShape(const SubShape &inSubShape, uint32 inSubShapeIndex) - { - // Create ID for sub shape - SubShapeIDCreator shape2_sub_shape_id = mSubShapeIDCreator.PushID(inSubShapeIndex, mSubShapeBits); - - // Transform the point - Mat44 transform = Mat44::sInverseRotationTranslation(inSubShape.GetRotation(), inSubShape.GetPositionCOM()); - inSubShape.mShape->CollidePoint(transform * mPoint, shape2_sub_shape_id, mCollector, mShapeFilter); - } - - Vec3 mPoint; - SubShapeIDCreator mSubShapeIDCreator; - CollidePointCollector & mCollector; - uint mSubShapeBits; - const ShapeFilter & mShapeFilter; -}; - -struct CompoundShape::CastShapeVisitor -{ - JPH_INLINE CastShapeVisitor(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const CompoundShape *inShape, Vec3Arg inScale, const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector) : - mBoxCenter(inShapeCast.mShapeWorldBounds.GetCenter()), - mBoxExtent(inShapeCast.mShapeWorldBounds.GetExtent()), - mScale(inScale), - mShapeCast(inShapeCast), - mShapeCastSettings(inShapeCastSettings), - mShapeFilter(inShapeFilter), - mCollector(ioCollector), - mCenterOfMassTransform2(inCenterOfMassTransform2), - mSubShapeIDCreator1(inSubShapeIDCreator1), - mSubShapeIDCreator2(inSubShapeIDCreator2), - mSubShapeBits(inShape->GetSubShapeIDBits()) - { - // Determine ray properties of cast - mInvDirection.Set(inShapeCast.mDirection); - } - - /// Returns true when collision detection should abort because it's not possible to find a better hit - JPH_INLINE bool ShouldAbort() const - { - return mCollector.ShouldEarlyOut(); - } - - /// Tests the shape cast against 4 bounding boxes, returns the distance along the shape cast where the shape first enters the bounding box - JPH_INLINE Vec4 TestBounds(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ) const - { - // Scale the bounding boxes - Vec4 bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z; - AABox4Scale(mScale, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z); - - // Enlarge them by the casted shape's box extents - AABox4EnlargeWithExtent(mBoxExtent, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z); - - // Test ray against the bounding boxes - return RayAABox4(mBoxCenter, mInvDirection, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z); - } - - /// Test the cast shape against a single subshape - JPH_INLINE void VisitShape(const SubShape &inSubShape, uint32 inSubShapeIndex) - { - JPH_ASSERT(inSubShape.IsValidScale(mScale)); - - // Create ID for sub shape - SubShapeIDCreator shape2_sub_shape_id = mSubShapeIDCreator2.PushID(inSubShapeIndex, mSubShapeBits); - - // Calculate the local transform for this sub shape - Mat44 local_transform = Mat44::sRotationTranslation(inSubShape.GetRotation(), mScale * inSubShape.GetPositionCOM()); - - // Transform the center of mass of 2 - Mat44 center_of_mass_transform2 = mCenterOfMassTransform2 * local_transform; - - // Transform the shape cast - ShapeCast shape_cast = mShapeCast.PostTransformed(local_transform.InversedRotationTranslation()); - - CollisionDispatch::sCastShapeVsShapeLocalSpace(shape_cast, mShapeCastSettings, inSubShape.mShape, inSubShape.TransformScale(mScale), mShapeFilter, center_of_mass_transform2, mSubShapeIDCreator1, shape2_sub_shape_id, mCollector); - } - - RayInvDirection mInvDirection; - Vec3 mBoxCenter; - Vec3 mBoxExtent; - Vec3 mScale; - const ShapeCast & mShapeCast; - const ShapeCastSettings & mShapeCastSettings; - const ShapeFilter & mShapeFilter; - CastShapeCollector & mCollector; - Mat44 mCenterOfMassTransform2; - SubShapeIDCreator mSubShapeIDCreator1; - SubShapeIDCreator mSubShapeIDCreator2; - uint mSubShapeBits; -}; - -struct CompoundShape::CollectTransformedShapesVisitor -{ - JPH_INLINE CollectTransformedShapesVisitor(const AABox &inBox, const CompoundShape *inShape, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale, const SubShapeIDCreator &inSubShapeIDCreator, TransformedShapeCollector &ioCollector, const ShapeFilter &inShapeFilter) : - mBox(inBox), - mLocalBox(Mat44::sInverseRotationTranslation(inRotation, inPositionCOM), inBox), - mPositionCOM(inPositionCOM), - mRotation(inRotation), - mScale(inScale), - mSubShapeIDCreator(inSubShapeIDCreator), - mCollector(ioCollector), - mSubShapeBits(inShape->GetSubShapeIDBits()), - mShapeFilter(inShapeFilter) - { - } - - /// Returns true when collision detection should abort because it's not possible to find a better hit - JPH_INLINE bool ShouldAbort() const - { - return mCollector.ShouldEarlyOut(); - } - - /// Tests 4 bounding boxes against the query box, returns true for the ones that collide - JPH_INLINE UVec4 TestBounds(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ) const - { - // Scale the bounding boxes of this node - Vec4 bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z; - AABox4Scale(mScale, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z); - - // Test which nodes collide - return AABox4VsBox(mLocalBox, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z); - } - - /// Collect the transformed sub shapes for a single subshape - JPH_INLINE void VisitShape(const SubShape &inSubShape, uint32 inSubShapeIndex) - { - JPH_ASSERT(inSubShape.IsValidScale(mScale)); - - // Create ID for sub shape - SubShapeIDCreator sub_shape_id = mSubShapeIDCreator.PushID(inSubShapeIndex, mSubShapeBits); - - // Calculate world transform for sub shape - Vec3 position = mPositionCOM + mRotation * (mScale * inSubShape.GetPositionCOM()); - Quat rotation = mRotation * inSubShape.GetRotation(); - - // Recurse to sub shape - inSubShape.mShape->CollectTransformedShapes(mBox, position, rotation, inSubShape.TransformScale(mScale), sub_shape_id, mCollector, mShapeFilter); - } - - AABox mBox; - OrientedBox mLocalBox; - Vec3 mPositionCOM; - Quat mRotation; - Vec3 mScale; - SubShapeIDCreator mSubShapeIDCreator; - TransformedShapeCollector & mCollector; - uint mSubShapeBits; - const ShapeFilter & mShapeFilter; -}; - -struct CompoundShape::CollideCompoundVsShapeVisitor -{ - JPH_INLINE CollideCompoundVsShapeVisitor(const CompoundShape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter) : - mCollideShapeSettings(inCollideShapeSettings), - mCollector(ioCollector), - mShape2(inShape2), - mScale1(inScale1), - mScale2(inScale2), - mTransform1(inCenterOfMassTransform1), - mTransform2(inCenterOfMassTransform2), - mSubShapeIDCreator1(inSubShapeIDCreator1), - mSubShapeIDCreator2(inSubShapeIDCreator2), - mSubShapeBits(inShape1->GetSubShapeIDBits()), - mShapeFilter(inShapeFilter) - { - // Get transform from shape 2 to shape 1 - Mat44 transform2_to_1 = inCenterOfMassTransform1.InversedRotationTranslation() * inCenterOfMassTransform2; - - // Convert bounding box of 2 into space of 1 - mBoundsOf2InSpaceOf1 = inShape2->GetLocalBounds().Scaled(inScale2).Transformed(transform2_to_1); - } - - /// Returns true when collision detection should abort because it's not possible to find a better hit - JPH_INLINE bool ShouldAbort() const - { - return mCollector.ShouldEarlyOut(); - } - - /// Tests the bounds of shape 2 vs 4 bounding boxes, returns true for the ones that intersect - JPH_INLINE UVec4 TestBounds(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ) const - { - // Scale the bounding boxes - Vec4 bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z; - AABox4Scale(mScale1, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z); - - // Test which boxes collide - return AABox4VsBox(mBoundsOf2InSpaceOf1, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z); - } - - /// Test the shape against a single subshape - JPH_INLINE void VisitShape(const SubShape &inSubShape, uint32 inSubShapeIndex) - { - // Get world transform of 1 - Mat44 transform1 = mTransform1 * inSubShape.GetLocalTransformNoScale(mScale1); - - // Create ID for sub shape - SubShapeIDCreator shape1_sub_shape_id = mSubShapeIDCreator1.PushID(inSubShapeIndex, mSubShapeBits); - - CollisionDispatch::sCollideShapeVsShape(inSubShape.mShape, mShape2, inSubShape.TransformScale(mScale1), mScale2, transform1, mTransform2, shape1_sub_shape_id, mSubShapeIDCreator2, mCollideShapeSettings, mCollector, mShapeFilter); - } - - const CollideShapeSettings & mCollideShapeSettings; - CollideShapeCollector & mCollector; - const Shape * mShape2; - Vec3 mScale1; - Vec3 mScale2; - Mat44 mTransform1; - Mat44 mTransform2; - AABox mBoundsOf2InSpaceOf1; - SubShapeIDCreator mSubShapeIDCreator1; - SubShapeIDCreator mSubShapeIDCreator2; - uint mSubShapeBits; - const ShapeFilter & mShapeFilter; -}; - -struct CompoundShape::CollideShapeVsCompoundVisitor -{ - JPH_INLINE CollideShapeVsCompoundVisitor(const Shape *inShape1, const CompoundShape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter) : - mCollideShapeSettings(inCollideShapeSettings), - mCollector(ioCollector), - mShape1(inShape1), - mScale1(inScale1), - mScale2(inScale2), - mTransform1(inCenterOfMassTransform1), - mTransform2(inCenterOfMassTransform2), - mSubShapeIDCreator1(inSubShapeIDCreator1), - mSubShapeIDCreator2(inSubShapeIDCreator2), - mSubShapeBits(inShape2->GetSubShapeIDBits()), - mShapeFilter(inShapeFilter) - { - // Get transform from shape 1 to shape 2 - Mat44 transform1_to_2 = inCenterOfMassTransform2.InversedRotationTranslation() * inCenterOfMassTransform1; - - // Convert bounding box of 1 into space of 2 - mBoundsOf1InSpaceOf2 = inShape1->GetLocalBounds().Scaled(inScale1).Transformed(transform1_to_2); - mBoundsOf1InSpaceOf2.ExpandBy(Vec3::sReplicate(inCollideShapeSettings.mMaxSeparationDistance)); - } - - /// Returns true when collision detection should abort because it's not possible to find a better hit - JPH_INLINE bool ShouldAbort() const - { - return mCollector.ShouldEarlyOut(); - } - - /// Tests the bounds of shape 1 vs 4 bounding boxes, returns true for the ones that intersect - JPH_INLINE UVec4 TestBounds(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ) const - { - // Scale the bounding boxes - Vec4 bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z; - AABox4Scale(mScale2, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z); - - // Test which bounding boxes collide - return AABox4VsBox(mBoundsOf1InSpaceOf2, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z); - } - - /// Test the shape against a single subshape - JPH_INLINE void VisitShape(const SubShape &inSubShape, uint32 inSubShapeIndex) - { - // Create ID for sub shape - SubShapeIDCreator shape2_sub_shape_id = mSubShapeIDCreator2.PushID(inSubShapeIndex, mSubShapeBits); - - // Get world transform of 2 - Mat44 transform2 = mTransform2 * inSubShape.GetLocalTransformNoScale(mScale2); - - CollisionDispatch::sCollideShapeVsShape(mShape1, inSubShape.mShape, mScale1, inSubShape.TransformScale(mScale2), mTransform1, transform2, mSubShapeIDCreator1, shape2_sub_shape_id, mCollideShapeSettings, mCollector, mShapeFilter); - } - - const CollideShapeSettings & mCollideShapeSettings; - CollideShapeCollector & mCollector; - const Shape * mShape1; - Vec3 mScale1; - Vec3 mScale2; - Mat44 mTransform1; - Mat44 mTransform2; - AABox mBoundsOf1InSpaceOf2; - SubShapeIDCreator mSubShapeIDCreator1; - SubShapeIDCreator mSubShapeIDCreator2; - uint mSubShapeBits; - const ShapeFilter & mShapeFilter; -}; - -template -struct CompoundShape::GetIntersectingSubShapesVisitor -{ - JPH_INLINE GetIntersectingSubShapesVisitor(const BoxType &inBox, uint *outSubShapeIndices, int inMaxSubShapeIndices) : - mBox(inBox), - mSubShapeIndices(outSubShapeIndices), - mMaxSubShapeIndices(inMaxSubShapeIndices) - { - } - - /// Returns true when collision detection should abort because the buffer is full - JPH_INLINE bool ShouldAbort() const - { - return mNumResults >= mMaxSubShapeIndices; - } - - /// Tests the box vs 4 bounding boxes, returns true for the ones that intersect - JPH_INLINE UVec4 TestBounds(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ) const - { - // Test which bounding boxes collide - return AABox4VsBox(mBox, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ); - } - - /// Records a hit - JPH_INLINE void VisitShape([[maybe_unused]] const SubShape &inSubShape, uint32 inSubShapeIndex) - { - JPH_ASSERT(mNumResults < mMaxSubShapeIndices); - *mSubShapeIndices++ = inSubShapeIndex; - mNumResults++; - } - - /// Get the number of indices that were found - JPH_INLINE int GetNumResults() const - { - return mNumResults; - } - -private: - BoxType mBox; - uint * mSubShapeIndices; - int mMaxSubShapeIndices; - int mNumResults = 0; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/ConvexHullShape.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/ConvexHullShape.cpp deleted file mode 100644 index d1de637173f..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/ConvexHullShape.cpp +++ /dev/null @@ -1,1311 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(ConvexHullShapeSettings) -{ - JPH_ADD_BASE_CLASS(ConvexHullShapeSettings, ConvexShapeSettings) - - JPH_ADD_ATTRIBUTE(ConvexHullShapeSettings, mPoints) - JPH_ADD_ATTRIBUTE(ConvexHullShapeSettings, mMaxConvexRadius) - JPH_ADD_ATTRIBUTE(ConvexHullShapeSettings, mMaxErrorConvexRadius) - JPH_ADD_ATTRIBUTE(ConvexHullShapeSettings, mHullTolerance) -} - -ShapeSettings::ShapeResult ConvexHullShapeSettings::Create() const -{ - if (mCachedResult.IsEmpty()) - Ref shape = new ConvexHullShape(*this, mCachedResult); - return mCachedResult; -} - -ConvexHullShape::ConvexHullShape(const ConvexHullShapeSettings &inSettings, ShapeResult &outResult) : - ConvexShape(EShapeSubType::ConvexHull, inSettings, outResult), - mConvexRadius(inSettings.mMaxConvexRadius) -{ - using BuilderFace = ConvexHullBuilder::Face; - using Edge = ConvexHullBuilder::Edge; - using Faces = Array; - - // Check convex radius - if (mConvexRadius < 0.0f) - { - outResult.SetError("Invalid convex radius"); - return; - } - - // Build convex hull - const char *error = nullptr; - ConvexHullBuilder builder(inSettings.mPoints); - ConvexHullBuilder::EResult result = builder.Initialize(cMaxPointsInHull, inSettings.mHullTolerance, error); - if (result != ConvexHullBuilder::EResult::Success && result != ConvexHullBuilder::EResult::MaxVerticesReached) - { - outResult.SetError(error); - return; - } - const Faces &builder_faces = builder.GetFaces(); - - // Check the consistency of the resulting hull if we fully built it - if (result == ConvexHullBuilder::EResult::Success) - { - ConvexHullBuilder::Face *max_error_face; - float max_error_distance, coplanar_distance; - int max_error_idx; - builder.DetermineMaxError(max_error_face, max_error_distance, max_error_idx, coplanar_distance); - if (max_error_distance > 4.0f * max(coplanar_distance, inSettings.mHullTolerance)) // Coplanar distance could be bigger than the allowed tolerance if the points are far apart - { - outResult.SetError(StringFormat("Hull building failed, point %d had an error of %g (relative to tolerance: %g)", max_error_idx, (double)max_error_distance, double(max_error_distance / inSettings.mHullTolerance))); - return; - } - } - - // Calculate center of mass and volume - builder.GetCenterOfMassAndVolume(mCenterOfMass, mVolume); - - // Calculate covariance matrix - // See: - // - Why the inertia tensor is the inertia tensor - Jonathan Blow (http://number-none.com/blow/inertia/deriving_i.html) - // - How to find the inertia tensor (or other mass properties) of a 3D solid body represented by a triangle mesh (Draft) - Jonathan Blow, Atman J Binstock (http://number-none.com/blow/inertia/bb_inertia.doc) - Mat44 covariance_canonical(Vec4(1.0f / 60.0f, 1.0f / 120.0f, 1.0f / 120.0f, 0), Vec4(1.0f / 120.0f, 1.0f / 60.0f, 1.0f / 120.0f, 0), Vec4(1.0f / 120.0f, 1.0f / 120.0f, 1.0f / 60.0f, 0), Vec4(0, 0, 0, 1)); - Mat44 covariance_matrix = Mat44::sZero(); - for (BuilderFace *f : builder_faces) - { - // Fourth point of the tetrahedron is at the center of mass, we subtract it from the other points so we get a tetrahedron with one vertex at zero - // The first point on the face will be used to form a triangle fan - Edge *e = f->mFirstEdge; - Vec3 v1 = inSettings.mPoints[e->mStartIdx] - mCenterOfMass; - - // Get the 2nd point - e = e->mNextEdge; - Vec3 v2 = inSettings.mPoints[e->mStartIdx] - mCenterOfMass; - - // Loop over the triangle fan - for (e = e->mNextEdge; e != f->mFirstEdge; e = e->mNextEdge) - { - Vec3 v3 = inSettings.mPoints[e->mStartIdx] - mCenterOfMass; - - // Affine transform that transforms a unit tetrahedon (with vertices (0, 0, 0), (1, 0, 0), (0, 1, 0) and (0, 0, 1) to this tetrahedron - Mat44 a(Vec4(v1, 0), Vec4(v2, 0), Vec4(v3, 0), Vec4(0, 0, 0, 1)); - - // Calculate covariance matrix for this tetrahedron - float det_a = a.GetDeterminant3x3(); - Mat44 c = det_a * (a * covariance_canonical * a.Transposed()); - - // Add it - covariance_matrix += c; - - // Prepare for next triangle - v2 = v3; - } - } - - // Calculate inertia matrix assuming density is 1, note that element (3, 3) is garbage - mInertia = Mat44::sIdentity() * (covariance_matrix(0, 0) + covariance_matrix(1, 1) + covariance_matrix(2, 2)) - covariance_matrix; - - // Convert polygons from the builder to our internal representation - using VtxMap = UnorderedMap; - VtxMap vertex_map; - for (BuilderFace *builder_face : builder_faces) - { - // Determine where the vertices go - JPH_ASSERT(mVertexIdx.size() <= 0xFFFF); - uint16 first_vertex = (uint16)mVertexIdx.size(); - uint16 num_vertices = 0; - - // Loop over vertices in face - Edge *edge = builder_face->mFirstEdge; - do - { - // Remap to new index, not all points in the original input set are required to form the hull - uint8 new_idx; - int original_idx = edge->mStartIdx; - VtxMap::iterator m = vertex_map.find(original_idx); - if (m != vertex_map.end()) - { - // Found, reuse - new_idx = m->second; - } - else - { - // This is a new point - // Make relative to center of mass - Vec3 p = inSettings.mPoints[original_idx] - mCenterOfMass; - - // Update local bounds - mLocalBounds.Encapsulate(p); - - // Add to point list - JPH_ASSERT(mPoints.size() <= 0xff); - new_idx = (uint8)mPoints.size(); - mPoints.push_back({ p }); - vertex_map[original_idx] = new_idx; - } - - // Append to vertex list - JPH_ASSERT(mVertexIdx.size() < 0xffff); - mVertexIdx.push_back(new_idx); - num_vertices++; - - edge = edge->mNextEdge; - } while (edge != builder_face->mFirstEdge); - - // Add face - mFaces.push_back({ first_vertex, num_vertices }); - - // Add plane - Plane plane = Plane::sFromPointAndNormal(builder_face->mCentroid - mCenterOfMass, builder_face->mNormal.Normalized()); - mPlanes.push_back(plane); - } - - // Test if GetSupportFunction can support this many points - if (mPoints.size() > cMaxPointsInHull) - { - outResult.SetError(StringFormat("Internal error: Too many points in hull (%u), max allowed %d", (uint)mPoints.size(), cMaxPointsInHull)); - return; - } - - for (int p = 0; p < (int)mPoints.size(); ++p) - { - // For each point, find faces that use the point - Array faces; - for (int f = 0; f < (int)mFaces.size(); ++f) - { - const Face &face = mFaces[f]; - for (int v = 0; v < face.mNumVertices; ++v) - if (mVertexIdx[face.mFirstVertex + v] == p) - { - faces.push_back(f); - break; - } - } - - if (faces.size() < 2) - { - outResult.SetError("A point must be connected to 2 or more faces!"); - return; - } - - // Find the 3 normals that form the largest tetrahedron - // The largest tetrahedron we can get is ((1, 0, 0) x (0, 1, 0)) . (0, 0, 1) = 1, if the volume is only 5% of that, - // the three vectors are too coplanar and we fall back to using only 2 plane normals - float biggest_volume = 0.05f; - int best3[3] = { -1, -1, -1 }; - - // When using 2 normals, we get the two with the biggest angle between them with a minimal difference of 1 degree - // otherwise we fall back to just using 1 plane normal - float smallest_dot = Cos(DegreesToRadians(1.0f)); - int best2[2] = { -1, -1 }; - - for (int face1 = 0; face1 < (int)faces.size(); ++face1) - { - Vec3 normal1 = mPlanes[faces[face1]].GetNormal(); - for (int face2 = face1 + 1; face2 < (int)faces.size(); ++face2) - { - Vec3 normal2 = mPlanes[faces[face2]].GetNormal(); - Vec3 cross = normal1.Cross(normal2); - - // Determine the 2 face normals that are most apart - float dot = normal1.Dot(normal2); - if (dot < smallest_dot) - { - smallest_dot = dot; - best2[0] = faces[face1]; - best2[1] = faces[face2]; - } - - // Determine the 3 face normals that form the largest tetrahedron - for (int face3 = face2 + 1; face3 < (int)faces.size(); ++face3) - { - Vec3 normal3 = mPlanes[faces[face3]].GetNormal(); - float volume = abs(cross.Dot(normal3)); - if (volume > biggest_volume) - { - biggest_volume = volume; - best3[0] = faces[face1]; - best3[1] = faces[face2]; - best3[2] = faces[face3]; - } - } - } - } - - // If we didn't find 3 planes, use 2, if we didn't find 2 use 1 - if (best3[0] != -1) - faces = { best3[0], best3[1], best3[2] }; - else if (best2[0] != -1) - faces = { best2[0], best2[1] }; - else - faces = { faces[0] }; - - // Copy the faces to the points buffer - Point &point = mPoints[p]; - point.mNumFaces = (int)faces.size(); - for (int i = 0; i < (int)faces.size(); ++i) - point.mFaces[i] = faces[i]; - } - - // If the convex radius is already zero, there's no point in further reducing it - if (mConvexRadius > 0.0f) - { - // Find out how thin the hull is by walking over all planes and checking the thickness of the hull in that direction - float min_size = FLT_MAX; - for (const Plane &plane : mPlanes) - { - // Take the point that is furthest away from the plane as thickness of this hull - float max_dist = 0.0f; - for (const Point &point : mPoints) - { - float dist = -plane.SignedDistance(point.mPosition); // Point is always behind plane, so we need to negate - if (dist > max_dist) - max_dist = dist; - } - min_size = min(min_size, max_dist); - } - - // We need to fit in 2x the convex radius in min_size, so reduce the convex radius if it's bigger than that - mConvexRadius = min(mConvexRadius, 0.5f * min_size); - } - - // Now walk over all points and see if we have to further reduce the convex radius because of sharp edges - if (mConvexRadius > 0.0f) - { - for (const Point &point : mPoints) - if (point.mNumFaces != 1) // If we have a single face, shifting back is easy and we don't need to reduce the convex radius - { - // Get first two planes - Plane p1 = mPlanes[point.mFaces[0]]; - Plane p2 = mPlanes[point.mFaces[1]]; - Plane p3; - Vec3 offset_mask; - - if (point.mNumFaces == 3) - { - // Get third plane - p3 = mPlanes[point.mFaces[2]]; - - // All 3 planes will be offset by the convex radius - offset_mask = Vec3::sReplicate(1); - } - else - { - // Third plane has normal perpendicular to the other two planes and goes through the vertex position - JPH_ASSERT(point.mNumFaces == 2); - p3 = Plane::sFromPointAndNormal(point.mPosition, p1.GetNormal().Cross(p2.GetNormal())); - - // Only the first and 2nd plane will be offset, the 3rd plane is only there to guide the intersection point - offset_mask = Vec3(1, 1, 0); - } - - // Plane equation: point . normal + constant = 0 - // Offsetting the plane backwards with convex radius r: point . normal + constant + r = 0 - // To find the intersection 'point' of 3 planes we solve: - // |n1x n1y n1z| |x| | r + c1 | - // |n2x n2y n2z| |y| = - | r + c2 | <=> n point = -r (1, 1, 1) - (c1, c2, c3) - // |n3x n3y n3z| |z| | r + c3 | - // Where point = (x, y, z), n1x is the x component of the first plane, c1 = plane constant of plane 1, etc. - // The relation between how much the intersection point shifts as a function of r is: -r * n^-1 (1, 1, 1) = r * offset - // Where offset = -n^-1 (1, 1, 1) or -n^-1 (1, 1, 0) in case only the first 2 planes are offset - // The error that is introduced by a convex radius r is: error = r * |offset| - r - // So the max convex radius given error is: r = error / (|offset| - 1) - Mat44 n = Mat44(Vec4(p1.GetNormal(), 0), Vec4(p2.GetNormal(), 0), Vec4(p3.GetNormal(), 0), Vec4(0, 0, 0, 1)).Transposed(); - float det_n = n.GetDeterminant3x3(); - if (det_n == 0.0f) - { - // If the determinant is zero, the matrix is not invertible so no solution exists to move the point backwards and we have to choose a convex radius of zero - mConvexRadius = 0.0f; - break; - } - Mat44 adj_n = n.Adjointed3x3(); - float offset = ((adj_n * offset_mask) / det_n).Length(); - JPH_ASSERT(offset > 1.0f); - float max_convex_radius = inSettings.mMaxErrorConvexRadius / (offset - 1.0f); - mConvexRadius = min(mConvexRadius, max_convex_radius); - } - } - - // Calculate the inner radius by getting the minimum distance from the origin to the planes of the hull - mInnerRadius = FLT_MAX; - for (const Plane &p : mPlanes) - mInnerRadius = min(mInnerRadius, -p.GetConstant()); - mInnerRadius = max(0.0f, mInnerRadius); // Clamp against zero, this should do nothing as the shape is centered around the center of mass but for flat convex hulls there may be numerical round off issues - - outResult.Set(this); -} - -MassProperties ConvexHullShape::GetMassProperties() const -{ - MassProperties p; - - float density = GetDensity(); - - // Calculate mass - p.mMass = density * mVolume; - - // Calculate inertia matrix - p.mInertia = density * mInertia; - p.mInertia(3, 3) = 1.0f; - - return p; -} - -Vec3 ConvexHullShape::GetSurfaceNormal(const SubShapeID &inSubShapeID, Vec3Arg inLocalSurfacePosition) const -{ - JPH_ASSERT(inSubShapeID.IsEmpty(), "Invalid subshape ID"); - - const Plane &first_plane = mPlanes[0]; - Vec3 best_normal = first_plane.GetNormal(); - float best_dist = abs(first_plane.SignedDistance(inLocalSurfacePosition)); - - // Find the face that has the shortest distance to the surface point - for (Array::size_type i = 1; i < mFaces.size(); ++i) - { - const Plane &plane = mPlanes[i]; - Vec3 plane_normal = plane.GetNormal(); - float dist = abs(plane.SignedDistance(inLocalSurfacePosition)); - if (dist < best_dist) - { - best_dist = dist; - best_normal = plane_normal; - } - } - - return best_normal; -} - -class ConvexHullShape::HullNoConvex final : public Support -{ -public: - explicit HullNoConvex(float inConvexRadius) : - mConvexRadius(inConvexRadius) - { - static_assert(sizeof(HullNoConvex) <= sizeof(SupportBuffer), "Buffer size too small"); - JPH_ASSERT(IsAligned(this, alignof(HullNoConvex))); - } - - virtual Vec3 GetSupport(Vec3Arg inDirection) const override - { - // Find the point with the highest projection on inDirection - float best_dot = -FLT_MAX; - Vec3 best_point = Vec3::sZero(); - - for (Vec3 point : mPoints) - { - // Check if its support is bigger than the current max - float dot = point.Dot(inDirection); - if (dot > best_dot) - { - best_dot = dot; - best_point = point; - } - } - - return best_point; - } - - virtual float GetConvexRadius() const override - { - return mConvexRadius; - } - - using PointsArray = StaticArray; - - inline PointsArray & GetPoints() - { - return mPoints; - } - - const PointsArray & GetPoints() const - { - return mPoints; - } - -private: - float mConvexRadius; - PointsArray mPoints; -}; - -class ConvexHullShape::HullWithConvex final : public Support -{ -public: - explicit HullWithConvex(const ConvexHullShape *inShape) : - mShape(inShape) - { - static_assert(sizeof(HullWithConvex) <= sizeof(SupportBuffer), "Buffer size too small"); - JPH_ASSERT(IsAligned(this, alignof(HullWithConvex))); - } - - virtual Vec3 GetSupport(Vec3Arg inDirection) const override - { - // Find the point with the highest projection on inDirection - float best_dot = -FLT_MAX; - Vec3 best_point = Vec3::sZero(); - - for (const Point &point : mShape->mPoints) - { - // Check if its support is bigger than the current max - float dot = point.mPosition.Dot(inDirection); - if (dot > best_dot) - { - best_dot = dot; - best_point = point.mPosition; - } - } - - return best_point; - } - - virtual float GetConvexRadius() const override - { - return 0.0f; - } - -private: - const ConvexHullShape * mShape; -}; - -class ConvexHullShape::HullWithConvexScaled final : public Support -{ -public: - HullWithConvexScaled(const ConvexHullShape *inShape, Vec3Arg inScale) : - mShape(inShape), - mScale(inScale) - { - static_assert(sizeof(HullWithConvexScaled) <= sizeof(SupportBuffer), "Buffer size too small"); - JPH_ASSERT(IsAligned(this, alignof(HullWithConvexScaled))); - } - - virtual Vec3 GetSupport(Vec3Arg inDirection) const override - { - // Find the point with the highest projection on inDirection - float best_dot = -FLT_MAX; - Vec3 best_point = Vec3::sZero(); - - for (const Point &point : mShape->mPoints) - { - // Calculate scaled position - Vec3 pos = mScale * point.mPosition; - - // Check if its support is bigger than the current max - float dot = pos.Dot(inDirection); - if (dot > best_dot) - { - best_dot = dot; - best_point = pos; - } - } - - return best_point; - } - - virtual float GetConvexRadius() const override - { - return 0.0f; - } - -private: - const ConvexHullShape * mShape; - Vec3 mScale; -}; - -const ConvexShape::Support *ConvexHullShape::GetSupportFunction(ESupportMode inMode, SupportBuffer &inBuffer, Vec3Arg inScale) const -{ - // If there's no convex radius, we don't need to shrink the hull - if (mConvexRadius == 0.0f) - { - if (ScaleHelpers::IsNotScaled(inScale)) - return new (&inBuffer) HullWithConvex(this); - else - return new (&inBuffer) HullWithConvexScaled(this, inScale); - } - - switch (inMode) - { - case ESupportMode::IncludeConvexRadius: - case ESupportMode::Default: - if (ScaleHelpers::IsNotScaled(inScale)) - return new (&inBuffer) HullWithConvex(this); - else - return new (&inBuffer) HullWithConvexScaled(this, inScale); - - case ESupportMode::ExcludeConvexRadius: - if (ScaleHelpers::IsNotScaled(inScale)) - { - // Create support function - HullNoConvex *hull = new (&inBuffer) HullNoConvex(mConvexRadius); - HullNoConvex::PointsArray &transformed_points = hull->GetPoints(); - JPH_ASSERT(mPoints.size() <= cMaxPointsInHull, "Not enough space, this should have been caught during shape creation!"); - - for (const Point &point : mPoints) - { - Vec3 new_point; - - if (point.mNumFaces == 1) - { - // Simply shift back by the convex radius using our 1 plane - new_point = point.mPosition - mPlanes[point.mFaces[0]].GetNormal() * mConvexRadius; - } - else - { - // Get first two planes and offset inwards by convex radius - Plane p1 = mPlanes[point.mFaces[0]].Offset(-mConvexRadius); - Plane p2 = mPlanes[point.mFaces[1]].Offset(-mConvexRadius); - Plane p3; - - if (point.mNumFaces == 3) - { - // Get third plane and offset inwards by convex radius - p3 = mPlanes[point.mFaces[2]].Offset(-mConvexRadius); - } - else - { - // Third plane has normal perpendicular to the other two planes and goes through the vertex position - JPH_ASSERT(point.mNumFaces == 2); - p3 = Plane::sFromPointAndNormal(point.mPosition, p1.GetNormal().Cross(p2.GetNormal())); - } - - // Find intersection point between the three planes - if (!Plane::sIntersectPlanes(p1, p2, p3, new_point)) - { - // Fallback: Just push point back using the first plane - new_point = point.mPosition - p1.GetNormal() * mConvexRadius; - } - } - - // Add point - transformed_points.push_back(new_point); - } - - return hull; - } - else - { - // Calculate scaled convex radius - float convex_radius = ScaleHelpers::ScaleConvexRadius(mConvexRadius, inScale); - - // Create new support function - HullNoConvex *hull = new (&inBuffer) HullNoConvex(convex_radius); - HullNoConvex::PointsArray &transformed_points = hull->GetPoints(); - JPH_ASSERT(mPoints.size() <= cMaxPointsInHull, "Not enough space, this should have been caught during shape creation!"); - - // Precalculate inverse scale - Vec3 inv_scale = inScale.Reciprocal(); - - for (const Point &point : mPoints) - { - // Calculate scaled position - Vec3 pos = inScale * point.mPosition; - - // Transform normals for plane 1 with scale - Vec3 n1 = (inv_scale * mPlanes[point.mFaces[0]].GetNormal()).Normalized(); - - Vec3 new_point; - - if (point.mNumFaces == 1) - { - // Simply shift back by the convex radius using our 1 plane - new_point = pos - n1 * convex_radius; - } - else - { - // Transform normals for plane 2 with scale - Vec3 n2 = (inv_scale * mPlanes[point.mFaces[1]].GetNormal()).Normalized(); - - // Get first two planes and offset inwards by convex radius - Plane p1 = Plane::sFromPointAndNormal(pos, n1).Offset(-convex_radius); - Plane p2 = Plane::sFromPointAndNormal(pos, n2).Offset(-convex_radius); - Plane p3; - - if (point.mNumFaces == 3) - { - // Transform last normal with scale - Vec3 n3 = (inv_scale * mPlanes[point.mFaces[2]].GetNormal()).Normalized(); - - // Get third plane and offset inwards by convex radius - p3 = Plane::sFromPointAndNormal(pos, n3).Offset(-convex_radius); - } - else - { - // Third plane has normal perpendicular to the other two planes and goes through the vertex position - JPH_ASSERT(point.mNumFaces == 2); - p3 = Plane::sFromPointAndNormal(pos, n1.Cross(n2)); - } - - // Find intersection point between the three planes - if (!Plane::sIntersectPlanes(p1, p2, p3, new_point)) - { - // Fallback: Just push point back using the first plane - new_point = pos - n1 * convex_radius; - } - } - - // Add point - transformed_points.push_back(new_point); - } - - return hull; - } - } - - JPH_ASSERT(false); - return nullptr; -} - -void ConvexHullShape::GetSupportingFace(const SubShapeID &inSubShapeID, Vec3Arg inDirection, Vec3Arg inScale, Mat44Arg inCenterOfMassTransform, SupportingFace &outVertices) const -{ - JPH_ASSERT(inSubShapeID.IsEmpty(), "Invalid subshape ID"); - - Vec3 inv_scale = inScale.Reciprocal(); - - // Need to transform the plane normals using inScale - // Transforming a direction with matrix M is done through multiplying by (M^-1)^T - // In this case M is a diagonal matrix with the scale vector, so we need to multiply our normal by 1 / scale and renormalize afterwards - Vec3 plane0_normal = inv_scale * mPlanes[0].GetNormal(); - float best_dot = plane0_normal.Dot(inDirection) / plane0_normal.Length(); - int best_face_idx = 0; - - for (Array::size_type i = 1; i < mPlanes.size(); ++i) - { - Vec3 plane_normal = inv_scale * mPlanes[i].GetNormal(); - float dot = plane_normal.Dot(inDirection) / plane_normal.Length(); - if (dot < best_dot) - { - best_dot = dot; - best_face_idx = (int)i; - } - } - - // Get vertices - const Face &best_face = mFaces[best_face_idx]; - const uint8 *first_vtx = mVertexIdx.data() + best_face.mFirstVertex; - const uint8 *end_vtx = first_vtx + best_face.mNumVertices; - - // If we have more than 1/2 the capacity of outVertices worth of vertices, we start skipping vertices (note we can't fill the buffer completely since extra edges will be generated by clipping). - // TODO: This really needs a better algorithm to determine which vertices are important! - int max_vertices_to_return = outVertices.capacity() / 2; - int delta_vtx = (int(best_face.mNumVertices) + max_vertices_to_return) / max_vertices_to_return; - - // Calculate transform with scale - Mat44 transform = inCenterOfMassTransform.PreScaled(inScale); - - if (ScaleHelpers::IsInsideOut(inScale)) - { - // Flip winding of supporting face - for (const uint8 *v = end_vtx - 1; v >= first_vtx; v -= delta_vtx) - outVertices.push_back(transform * mPoints[*v].mPosition); - } - else - { - // Normal winding of supporting face - for (const uint8 *v = first_vtx; v < end_vtx; v += delta_vtx) - outVertices.push_back(transform * mPoints[*v].mPosition); - } -} - -void ConvexHullShape::GetSubmergedVolume(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, const Plane &inSurface, float &outTotalVolume, float &outSubmergedVolume, Vec3 &outCenterOfBuoyancy JPH_IF_DEBUG_RENDERER(, RVec3Arg inBaseOffset)) const -{ - // Trivially calculate total volume - Vec3 abs_scale = inScale.Abs(); - outTotalVolume = mVolume * abs_scale.GetX() * abs_scale.GetY() * abs_scale.GetZ(); - - // Check if shape has been scaled inside out - bool is_inside_out = ScaleHelpers::IsInsideOut(inScale); - - // Convert the points to world space and determine the distance to the surface - int num_points = int(mPoints.size()); - PolyhedronSubmergedVolumeCalculator::Point *buffer = (PolyhedronSubmergedVolumeCalculator::Point *)JPH_STACK_ALLOC(num_points * sizeof(PolyhedronSubmergedVolumeCalculator::Point)); - PolyhedronSubmergedVolumeCalculator submerged_vol_calc(inCenterOfMassTransform * Mat44::sScale(inScale), &mPoints[0].mPosition, sizeof(Point), num_points, inSurface, buffer JPH_IF_DEBUG_RENDERER(, inBaseOffset)); - - if (submerged_vol_calc.AreAllAbove()) - { - // We're above the water - outSubmergedVolume = 0.0f; - outCenterOfBuoyancy = Vec3::sZero(); - } - else if (submerged_vol_calc.AreAllBelow()) - { - // We're fully submerged - outSubmergedVolume = outTotalVolume; - outCenterOfBuoyancy = inCenterOfMassTransform.GetTranslation(); - } - else - { - // Calculate submerged volume - int reference_point_idx = submerged_vol_calc.GetReferencePointIdx(); - for (const Face &f : mFaces) - { - const uint8 *first_vtx = mVertexIdx.data() + f.mFirstVertex; - const uint8 *end_vtx = first_vtx + f.mNumVertices; - - // If any of the vertices of this face are the reference point, the volume will be zero so we can skip this face - bool degenerate = false; - for (const uint8 *v = first_vtx; v < end_vtx; ++v) - if (*v == reference_point_idx) - { - degenerate = true; - break; - } - if (degenerate) - continue; - - // Triangulate the face - int i1 = *first_vtx; - if (is_inside_out) - { - // Reverse winding - for (const uint8 *v = first_vtx + 2; v < end_vtx; ++v) - { - int i2 = *(v - 1); - int i3 = *v; - submerged_vol_calc.AddFace(i1, i3, i2); - } - } - else - { - // Normal winding - for (const uint8 *v = first_vtx + 2; v < end_vtx; ++v) - { - int i2 = *(v - 1); - int i3 = *v; - submerged_vol_calc.AddFace(i1, i2, i3); - } - } - } - - // Get the results - submerged_vol_calc.GetResult(outSubmergedVolume, outCenterOfBuoyancy); - } - -#ifdef JPH_DEBUG_RENDERER - // Draw center of buoyancy - if (sDrawSubmergedVolumes) - DebugRenderer::sInstance->DrawWireSphere(inBaseOffset + outCenterOfBuoyancy, 0.05f, Color::sRed, 1); -#endif // JPH_DEBUG_RENDERER -} - -#ifdef JPH_DEBUG_RENDERER -void ConvexHullShape::Draw(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inUseMaterialColors, bool inDrawWireframe) const -{ - if (mGeometry == nullptr) - { - Array triangles; - for (const Face &f : mFaces) - { - const uint8 *first_vtx = mVertexIdx.data() + f.mFirstVertex; - const uint8 *end_vtx = first_vtx + f.mNumVertices; - - // Draw first triangle of polygon - Vec3 v0 = mPoints[first_vtx[0]].mPosition; - Vec3 v1 = mPoints[first_vtx[1]].mPosition; - Vec3 v2 = mPoints[first_vtx[2]].mPosition; - Vec3 uv_direction = (v1 - v0).Normalized(); - triangles.push_back({ v0, v1, v2, Color::sWhite, v0, uv_direction }); - - // Draw any other triangles in this polygon - for (const uint8 *v = first_vtx + 3; v < end_vtx; ++v) - triangles.push_back({ v0, mPoints[*(v - 1)].mPosition, mPoints[*v].mPosition, Color::sWhite, v0, uv_direction }); - } - mGeometry = new DebugRenderer::Geometry(inRenderer->CreateTriangleBatch(triangles), GetLocalBounds()); - } - - // Test if the shape is scaled inside out - DebugRenderer::ECullMode cull_mode = ScaleHelpers::IsInsideOut(inScale)? DebugRenderer::ECullMode::CullFrontFace : DebugRenderer::ECullMode::CullBackFace; - - // Determine the draw mode - DebugRenderer::EDrawMode draw_mode = inDrawWireframe? DebugRenderer::EDrawMode::Wireframe : DebugRenderer::EDrawMode::Solid; - - // Draw the geometry - Color color = inUseMaterialColors? GetMaterial()->GetDebugColor() : inColor; - RMat44 transform = inCenterOfMassTransform.PreScaled(inScale); - inRenderer->DrawGeometry(transform, color, mGeometry, cull_mode, DebugRenderer::ECastShadow::On, draw_mode); - - // Draw the outline if requested - if (sDrawFaceOutlines) - for (const Face &f : mFaces) - { - const uint8 *first_vtx = mVertexIdx.data() + f.mFirstVertex; - const uint8 *end_vtx = first_vtx + f.mNumVertices; - - // Draw edges of face - inRenderer->DrawLine(transform * mPoints[*(end_vtx - 1)].mPosition, transform * mPoints[*first_vtx].mPosition, Color::sGrey); - for (const uint8 *v = first_vtx + 1; v < end_vtx; ++v) - inRenderer->DrawLine(transform * mPoints[*(v - 1)].mPosition, transform * mPoints[*v].mPosition, Color::sGrey); - } -} - -void ConvexHullShape::DrawShrunkShape(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale) const -{ - // Get the shrunk points - SupportBuffer buffer; - const HullNoConvex *support = mConvexRadius > 0.0f? static_cast(GetSupportFunction(ESupportMode::ExcludeConvexRadius, buffer, inScale)) : nullptr; - - RMat44 transform = inCenterOfMassTransform * Mat44::sScale(inScale); - - for (int p = 0; p < (int)mPoints.size(); ++p) - { - const Point &point = mPoints[p]; - RVec3 position = transform * point.mPosition; - RVec3 shrunk_point = support != nullptr? transform * support->GetPoints()[p] : position; - - // Draw difference between shrunk position and position - inRenderer->DrawLine(position, shrunk_point, Color::sGreen); - - // Draw face normals that are contributing - for (int i = 0; i < point.mNumFaces; ++i) - inRenderer->DrawLine(position, position + 0.1f * mPlanes[point.mFaces[i]].GetNormal(), Color::sYellow); - - // Draw point index - inRenderer->DrawText3D(position, ConvertToString(p), Color::sWhite, 0.1f); - } -} -#endif // JPH_DEBUG_RENDERER - -bool ConvexHullShape::CastRayHelper(const RayCast &inRay, float &outMinFraction, float &outMaxFraction) const -{ - if (mFaces.size() == 2) - { - // If we have only 2 faces, we're a flat convex hull and we need to test edges instead of planes - - // Check if plane is parallel to ray - const Plane &p = mPlanes.front(); - Vec3 plane_normal = p.GetNormal(); - float direction_projection = inRay.mDirection.Dot(plane_normal); - if (abs(direction_projection) >= 1.0e-12f) - { - // Calculate intersection point - float distance_to_plane = inRay.mOrigin.Dot(plane_normal) + p.GetConstant(); - float fraction = -distance_to_plane / direction_projection; - if (fraction < 0.0f || fraction > 1.0f) - { - // Does not hit plane, no hit - outMinFraction = 0.0f; - outMaxFraction = 1.0f + FLT_EPSILON; - return false; - } - Vec3 intersection_point = inRay.mOrigin + fraction * inRay.mDirection; - - // Test all edges to see if point is inside polygon - const Face &f = mFaces.front(); - const uint8 *first_vtx = mVertexIdx.data() + f.mFirstVertex; - const uint8 *end_vtx = first_vtx + f.mNumVertices; - Vec3 p1 = mPoints[*end_vtx].mPosition; - for (const uint8 *v = first_vtx; v < end_vtx; ++v) - { - Vec3 p2 = mPoints[*v].mPosition; - if ((p2 - p1).Cross(intersection_point - p1).Dot(plane_normal) < 0.0f) - { - // Outside polygon, no hit - outMinFraction = 0.0f; - outMaxFraction = 1.0f + FLT_EPSILON; - return false; - } - p1 = p2; - } - - // Inside polygon, a hit - outMinFraction = fraction; - outMaxFraction = fraction; - return true; - } - else - { - // Parallel ray doesn't hit - outMinFraction = 0.0f; - outMaxFraction = 1.0f + FLT_EPSILON; - return false; - } - } - else - { - // Clip ray against all planes - int fractions_set = 0; - bool all_inside = true; - float min_fraction = 0.0f, max_fraction = 1.0f + FLT_EPSILON; - for (const Plane &p : mPlanes) - { - // Check if the ray origin is behind this plane - Vec3 plane_normal = p.GetNormal(); - float distance_to_plane = inRay.mOrigin.Dot(plane_normal) + p.GetConstant(); - bool is_outside = distance_to_plane > 0.0f; - all_inside &= !is_outside; - - // Check if plane is parallel to ray - float direction_projection = inRay.mDirection.Dot(plane_normal); - if (abs(direction_projection) >= 1.0e-12f) - { - // Get intersection fraction between ray and plane - float fraction = -distance_to_plane / direction_projection; - - // Update interval of ray that is inside the hull - if (direction_projection < 0.0f) - { - min_fraction = max(fraction, min_fraction); - fractions_set |= 1; - } - else - { - max_fraction = min(fraction, max_fraction); - fractions_set |= 2; - } - } - else if (is_outside) - return false; // Outside the plane and parallel, no hit! - } - - // Test if both min and max have been set - if (fractions_set == 3) - { - // Output fractions - outMinFraction = min_fraction; - outMaxFraction = max_fraction; - - // Test if the infinite ray intersects with the hull (the length will be checked later) - return min_fraction <= max_fraction && max_fraction >= 0.0f; - } - else - { - // Degenerate case, either the ray is parallel to all planes or the ray has zero length - outMinFraction = 0.0f; - outMaxFraction = 1.0f + FLT_EPSILON; - - // Return if the origin is inside the hull - return all_inside; - } - } -} - -bool ConvexHullShape::CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const -{ - // Determine if ray hits the shape - float min_fraction, max_fraction; - if (CastRayHelper(inRay, min_fraction, max_fraction) - && min_fraction < ioHit.mFraction) // Check if this is a closer hit - { - // Better hit than the current hit - ioHit.mFraction = min_fraction; - ioHit.mSubShapeID2 = inSubShapeIDCreator.GetID(); - return true; - } - return false; -} - -void ConvexHullShape::CastRay(const RayCast &inRay, const RayCastSettings &inRayCastSettings, const SubShapeIDCreator &inSubShapeIDCreator, CastRayCollector &ioCollector, const ShapeFilter &inShapeFilter) const -{ - // Test shape filter - if (!inShapeFilter.ShouldCollide(this, inSubShapeIDCreator.GetID())) - return; - - // Determine if ray hits the shape - float min_fraction, max_fraction; - if (CastRayHelper(inRay, min_fraction, max_fraction) - && min_fraction < ioCollector.GetEarlyOutFraction()) // Check if this is closer than the early out fraction - { - // Better hit than the current hit - RayCastResult hit; - hit.mBodyID = TransformedShape::sGetBodyID(ioCollector.GetContext()); - hit.mSubShapeID2 = inSubShapeIDCreator.GetID(); - - // Check front side hit - if (inRayCastSettings.mTreatConvexAsSolid || min_fraction > 0.0f) - { - hit.mFraction = min_fraction; - ioCollector.AddHit(hit); - } - - // Check back side hit - if (inRayCastSettings.mBackFaceMode == EBackFaceMode::CollideWithBackFaces - && max_fraction < ioCollector.GetEarlyOutFraction()) - { - hit.mFraction = max_fraction; - ioCollector.AddHit(hit); - } - } -} - -void ConvexHullShape::CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter) const -{ - // Test shape filter - if (!inShapeFilter.ShouldCollide(this, inSubShapeIDCreator.GetID())) - return; - - // Check if point is behind all planes - for (const Plane &p : mPlanes) - if (p.SignedDistance(inPoint) > 0.0f) - return; - - // Point is inside - ioCollector.AddHit({ TransformedShape::sGetBodyID(ioCollector.GetContext()), inSubShapeIDCreator.GetID() }); -} - -void ConvexHullShape::CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, SoftBodyVertex *ioVertices, uint inNumVertices, [[maybe_unused]] float inDeltaTime, [[maybe_unused]] Vec3Arg inDisplacementDueToGravity, int inCollidingShapeIndex) const -{ - Mat44 inverse_transform = inCenterOfMassTransform.InversedRotationTranslation(); - - Vec3 inv_scale = inScale.Reciprocal(); - bool is_not_scaled = ScaleHelpers::IsNotScaled(inScale); - float scale_flip = ScaleHelpers::IsInsideOut(inScale)? -1.0f : 1.0f; - - for (SoftBodyVertex *v = ioVertices, *sbv_end = ioVertices + inNumVertices; v < sbv_end; ++v) - if (v->mInvMass > 0.0f) - { - Vec3 local_pos = inverse_transform * v->mPosition; - - // Find most facing plane - float max_distance = -FLT_MAX; - Vec3 max_plane_normal = Vec3::sZero(); - uint max_plane_idx = 0; - if (is_not_scaled) - { - // Without scale, it is trivial to calculate the distance to the hull - for (const Plane &p : mPlanes) - { - float distance = p.SignedDistance(local_pos); - if (distance > max_distance) - { - max_distance = distance; - max_plane_normal = p.GetNormal(); - max_plane_idx = uint(&p - mPlanes.data()); - } - } - } - else - { - // When there's scale we need to calculate the planes first - for (uint i = 0; i < (uint)mPlanes.size(); ++i) - { - // Calculate plane normal and point by scaling the original plane - Vec3 plane_normal = (inv_scale * mPlanes[i].GetNormal()).Normalized(); - Vec3 plane_point = inScale * mPoints[mVertexIdx[mFaces[i].mFirstVertex]].mPosition; - - float distance = plane_normal.Dot(local_pos - plane_point); - if (distance > max_distance) - { - max_distance = distance; - max_plane_normal = plane_normal; - max_plane_idx = i; - } - } - } - bool is_outside = max_distance > 0.0f; - - // Project point onto that plane - Vec3 closest_point = local_pos - max_distance * max_plane_normal; - - // Check edges if we're outside the hull (when inside we know the closest face is also the closest point to the surface) - if (is_outside) - { - // Loop over edges - float closest_point_dist_sq = FLT_MAX; - const Face &face = mFaces[max_plane_idx]; - for (const uint8 *v_start = &mVertexIdx[face.mFirstVertex], *v1 = v_start, *v_end = v_start + face.mNumVertices; v1 < v_end; ++v1) - { - // Find second point - const uint8 *v2 = v1 + 1; - if (v2 == v_end) - v2 = v_start; - - // Get edge points - Vec3 p1 = inScale * mPoints[*v1].mPosition; - Vec3 p2 = inScale * mPoints[*v2].mPosition; - - // Check if the position is outside the edge (if not, the face will be closer) - Vec3 edge_normal = (p2 - p1).Cross(max_plane_normal); - if (scale_flip * edge_normal.Dot(local_pos - p1) > 0.0f) - { - // Get closest point on edge - uint32 set; - Vec3 closest = ClosestPoint::GetClosestPointOnLine(p1 - local_pos, p2 - local_pos, set); - float distance_sq = closest.LengthSq(); - if (distance_sq < closest_point_dist_sq) - closest_point = local_pos + closest; - } - } - } - - // Check if this is the largest penetration - Vec3 normal = local_pos - closest_point; - float normal_length = normal.Length(); - float penetration = normal_length; - if (is_outside) - penetration = -penetration; - else - normal = -normal; - if (penetration > v->mLargestPenetration) - { - v->mLargestPenetration = penetration; - - // Calculate contact plane - normal = normal_length > 0.0f? normal / normal_length : max_plane_normal; - Plane plane = Plane::sFromPointAndNormal(closest_point, normal); - - // Store collision - v->mCollisionPlane = plane.GetTransformed(inCenterOfMassTransform); - v->mCollidingShapeIndex = inCollidingShapeIndex; - } - } -} - -class ConvexHullShape::CHSGetTrianglesContext -{ -public: - CHSGetTrianglesContext(Mat44Arg inTransform, bool inIsInsideOut) : mTransform(inTransform), mIsInsideOut(inIsInsideOut) { } - - Mat44 mTransform; - bool mIsInsideOut; - size_t mCurrentFace = 0; -}; - -void ConvexHullShape::GetTrianglesStart(GetTrianglesContext &ioContext, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) const -{ - static_assert(sizeof(CHSGetTrianglesContext) <= sizeof(GetTrianglesContext), "GetTrianglesContext too small"); - JPH_ASSERT(IsAligned(&ioContext, alignof(CHSGetTrianglesContext))); - - new (&ioContext) CHSGetTrianglesContext(Mat44::sRotationTranslation(inRotation, inPositionCOM) * Mat44::sScale(inScale), ScaleHelpers::IsInsideOut(inScale)); -} - -int ConvexHullShape::GetTrianglesNext(GetTrianglesContext &ioContext, int inMaxTrianglesRequested, Float3 *outTriangleVertices, const PhysicsMaterial **outMaterials) const -{ - static_assert(cGetTrianglesMinTrianglesRequested >= 12, "cGetTrianglesMinTrianglesRequested is too small"); - JPH_ASSERT(inMaxTrianglesRequested >= cGetTrianglesMinTrianglesRequested); - - CHSGetTrianglesContext &context = (CHSGetTrianglesContext &)ioContext; - - int total_num_triangles = 0; - for (; context.mCurrentFace < mFaces.size(); ++context.mCurrentFace) - { - const Face &f = mFaces[context.mCurrentFace]; - - const uint8 *first_vtx = mVertexIdx.data() + f.mFirstVertex; - const uint8 *end_vtx = first_vtx + f.mNumVertices; - - // Check if there is still room in the output buffer for this face - int num_triangles = f.mNumVertices - 2; - inMaxTrianglesRequested -= num_triangles; - if (inMaxTrianglesRequested < 0) - break; - total_num_triangles += num_triangles; - - // Get first triangle of polygon - Vec3 v0 = context.mTransform * mPoints[first_vtx[0]].mPosition; - Vec3 v1 = context.mTransform * mPoints[first_vtx[1]].mPosition; - Vec3 v2 = context.mTransform * mPoints[first_vtx[2]].mPosition; - v0.StoreFloat3(outTriangleVertices++); - if (context.mIsInsideOut) - { - // Store first triangle in this polygon flipped - v2.StoreFloat3(outTriangleVertices++); - v1.StoreFloat3(outTriangleVertices++); - - // Store other triangles in this polygon flipped - for (const uint8 *v = first_vtx + 3; v < end_vtx; ++v) - { - v0.StoreFloat3(outTriangleVertices++); - (context.mTransform * mPoints[*v].mPosition).StoreFloat3(outTriangleVertices++); - (context.mTransform * mPoints[*(v - 1)].mPosition).StoreFloat3(outTriangleVertices++); - } - } - else - { - // Store first triangle in this polygon - v1.StoreFloat3(outTriangleVertices++); - v2.StoreFloat3(outTriangleVertices++); - - // Store other triangles in this polygon - for (const uint8 *v = first_vtx + 3; v < end_vtx; ++v) - { - v0.StoreFloat3(outTriangleVertices++); - (context.mTransform * mPoints[*(v - 1)].mPosition).StoreFloat3(outTriangleVertices++); - (context.mTransform * mPoints[*v].mPosition).StoreFloat3(outTriangleVertices++); - } - } - } - - // Store materials - if (outMaterials != nullptr) - { - const PhysicsMaterial *material = GetMaterial(); - for (const PhysicsMaterial **m = outMaterials, **m_end = outMaterials + total_num_triangles; m < m_end; ++m) - *m = material; - } - - return total_num_triangles; -} - -void ConvexHullShape::SaveBinaryState(StreamOut &inStream) const -{ - ConvexShape::SaveBinaryState(inStream); - - inStream.Write(mCenterOfMass); - inStream.Write(mInertia); - inStream.Write(mLocalBounds.mMin); - inStream.Write(mLocalBounds.mMax); - inStream.Write(mPoints); - inStream.Write(mFaces); - inStream.Write(mPlanes); - inStream.Write(mVertexIdx); - inStream.Write(mConvexRadius); - inStream.Write(mVolume); - inStream.Write(mInnerRadius); -} - -void ConvexHullShape::RestoreBinaryState(StreamIn &inStream) -{ - ConvexShape::RestoreBinaryState(inStream); - - inStream.Read(mCenterOfMass); - inStream.Read(mInertia); - inStream.Read(mLocalBounds.mMin); - inStream.Read(mLocalBounds.mMax); - inStream.Read(mPoints); - inStream.Read(mFaces); - inStream.Read(mPlanes); - inStream.Read(mVertexIdx); - inStream.Read(mConvexRadius); - inStream.Read(mVolume); - inStream.Read(mInnerRadius); -} - -Shape::Stats ConvexHullShape::GetStats() const -{ - // Count number of triangles - uint triangle_count = 0; - for (const Face &f : mFaces) - triangle_count += f.mNumVertices - 2; - - return Stats( - sizeof(*this) - + mPoints.size() * sizeof(Point) - + mFaces.size() * sizeof(Face) - + mPlanes.size() * sizeof(Plane) - + mVertexIdx.size() * sizeof(uint8), - triangle_count); -} - -void ConvexHullShape::sRegister() -{ - ShapeFunctions &f = ShapeFunctions::sGet(EShapeSubType::ConvexHull); - f.mConstruct = []() -> Shape * { return new ConvexHullShape; }; - f.mColor = Color::sGreen; -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/ConvexHullShape.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/ConvexHullShape.h deleted file mode 100644 index 8837a82d85f..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/ConvexHullShape.h +++ /dev/null @@ -1,202 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include -#ifdef JPH_DEBUG_RENDERER - #include -#endif // JPH_DEBUG_RENDERER - -JPH_NAMESPACE_BEGIN - -/// Class that constructs a ConvexHullShape -class JPH_EXPORT ConvexHullShapeSettings final : public ConvexShapeSettings -{ -public: - JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, ConvexHullShapeSettings) - - /// Default constructor for deserialization - ConvexHullShapeSettings() = default; - - /// Create a convex hull from inPoints and maximum convex radius inMaxConvexRadius, the radius is automatically lowered if the hull requires it. - /// (internally this will be subtracted so the total size will not grow with the convex radius). - ConvexHullShapeSettings(const Vec3 *inPoints, int inNumPoints, float inMaxConvexRadius = cDefaultConvexRadius, const PhysicsMaterial *inMaterial = nullptr) : ConvexShapeSettings(inMaterial), mPoints(inPoints, inPoints + inNumPoints), mMaxConvexRadius(inMaxConvexRadius) { } - ConvexHullShapeSettings(const Array &inPoints, float inConvexRadius = cDefaultConvexRadius, const PhysicsMaterial *inMaterial = nullptr) : ConvexShapeSettings(inMaterial), mPoints(inPoints), mMaxConvexRadius(inConvexRadius) { } - - // See: ShapeSettings - virtual ShapeResult Create() const override; - - Array mPoints; ///< Points to create the hull from - float mMaxConvexRadius = 0.0f; ///< Convex radius as supplied by the constructor. Note that during hull creation the convex radius can be made smaller if the value is too big for the hull. - float mMaxErrorConvexRadius = 0.05f; ///< Maximum distance between the shrunk hull + convex radius and the actual hull. - float mHullTolerance = 1.0e-3f; ///< Points are allowed this far outside of the hull (increasing this yields a hull with less vertices). Note that the actual used value can be larger if the points of the hull are far apart. -}; - -/// A convex hull -class JPH_EXPORT ConvexHullShape final : public ConvexShape -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Maximum amount of points supported in a convex hull. Note that while constructing a hull, interior points are discarded so you can provide more points. - /// The ConvexHullShapeSettings::Create function will return an error when too many points are provided. - static constexpr int cMaxPointsInHull = 256; - - /// Constructor - ConvexHullShape() : ConvexShape(EShapeSubType::ConvexHull) { } - ConvexHullShape(const ConvexHullShapeSettings &inSettings, ShapeResult &outResult); - - // See Shape::GetCenterOfMass - virtual Vec3 GetCenterOfMass() const override { return mCenterOfMass; } - - // See Shape::GetLocalBounds - virtual AABox GetLocalBounds() const override { return mLocalBounds; } - - // See Shape::GetInnerRadius - virtual float GetInnerRadius() const override { return mInnerRadius; } - - // See Shape::GetMassProperties - virtual MassProperties GetMassProperties() const override; - - // See Shape::GetSurfaceNormal - virtual Vec3 GetSurfaceNormal(const SubShapeID &inSubShapeID, Vec3Arg inLocalSurfacePosition) const override; - - // See Shape::GetSupportingFace - virtual void GetSupportingFace(const SubShapeID &inSubShapeID, Vec3Arg inDirection, Vec3Arg inScale, Mat44Arg inCenterOfMassTransform, SupportingFace &outVertices) const override; - - // See ConvexShape::GetSupportFunction - virtual const Support * GetSupportFunction(ESupportMode inMode, SupportBuffer &inBuffer, Vec3Arg inScale) const override; - - // See Shape::GetSubmergedVolume - virtual void GetSubmergedVolume(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, const Plane &inSurface, float &outTotalVolume, float &outSubmergedVolume, Vec3 &outCenterOfBuoyancy JPH_IF_DEBUG_RENDERER(, RVec3Arg inBaseOffset)) const override; - -#ifdef JPH_DEBUG_RENDERER - // See Shape::Draw - virtual void Draw(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inUseMaterialColors, bool inDrawWireframe) const override; - - /// Debugging helper draw function that draws how all points are moved when a shape is shrunk by the convex radius - void DrawShrunkShape(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale) const; -#endif // JPH_DEBUG_RENDERER - - // See Shape::CastRay - virtual bool CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const override; - virtual void CastRay(const RayCast &inRay, const RayCastSettings &inRayCastSettings, const SubShapeIDCreator &inSubShapeIDCreator, CastRayCollector &ioCollector, const ShapeFilter &inShapeFilter = { }) const override; - - // See: Shape::CollidePoint - virtual void CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter = { }) const override; - - // See: Shape::CollideSoftBodyVertices - virtual void CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, SoftBodyVertex *ioVertices, uint inNumVertices, float inDeltaTime, Vec3Arg inDisplacementDueToGravity, int inCollidingShapeIndex) const override; - - // See Shape::GetTrianglesStart - virtual void GetTrianglesStart(GetTrianglesContext &ioContext, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) const override; - - // See Shape::GetTrianglesNext - virtual int GetTrianglesNext(GetTrianglesContext &ioContext, int inMaxTrianglesRequested, Float3 *outTriangleVertices, const PhysicsMaterial **outMaterials = nullptr) const override; - - // See Shape - virtual void SaveBinaryState(StreamOut &inStream) const override; - - // See Shape::GetStats - virtual Stats GetStats() const override; - - // See Shape::GetVolume - virtual float GetVolume() const override { return mVolume; } - - /// Get the convex radius of this convex hull - float GetConvexRadius() const { return mConvexRadius; } - - /// Get the planes of this convex hull - const Array & GetPlanes() const { return mPlanes; } - - /// Get the number of vertices in this convex hull - inline uint GetNumPoints() const { return uint(mPoints.size()); } - - /// Get a vertex of this convex hull relative to the center of mass - inline Vec3 GetPoint(uint inIndex) const { return mPoints[inIndex].mPosition; } - - /// Get the number of faces in this convex hull - inline uint GetNumFaces() const { return uint(mFaces.size()); } - - /// Get the number of vertices in a face - inline uint GetNumVerticesInFace(uint inFaceIndex) const { return mFaces[inFaceIndex].mNumVertices; } - - /// Get the vertices indices of a face - /// @param inFaceIndex Index of the face. - /// @param inMaxVertices Maximum number of vertices to return. - /// @param outVertices Array of vertices indices, must be at least inMaxVertices in size, the vertices are returned in counter clockwise order and the positions can be obtained using GetPoint(index). - /// @return Number of vertices in face, if this is bigger than inMaxVertices, not all vertices were retrieved. - inline uint GetFaceVertices(uint inFaceIndex, uint inMaxVertices, uint *outVertices) const - { - const Face &face = mFaces[inFaceIndex]; - const uint8 *first_vertex = mVertexIdx.data() + face.mFirstVertex; - uint num_vertices = min(face.mNumVertices, inMaxVertices); - for (uint i = 0; i < num_vertices; ++i) - outVertices[i] = first_vertex[i]; - return face.mNumVertices; - } - - // Register shape functions with the registry - static void sRegister(); - -#ifdef JPH_DEBUG_RENDERER - /// Draw the outlines of the faces of the convex hull when drawing the shape - inline static bool sDrawFaceOutlines = false; -#endif // JPH_DEBUG_RENDERER - -protected: - // See: Shape::RestoreBinaryState - virtual void RestoreBinaryState(StreamIn &inStream) override; - -private: - /// Helper function that returns the min and max fraction along the ray that hits the convex hull. Returns false if there is no hit. - bool CastRayHelper(const RayCast &inRay, float &outMinFraction, float &outMaxFraction) const; - - /// Class for GetTrianglesStart/Next - class CHSGetTrianglesContext; - - /// Classes for GetSupportFunction - class HullNoConvex; - class HullWithConvex; - class HullWithConvexScaled; - - struct Face - { - uint16 mFirstVertex; ///< First index in mVertexIdx to use - uint16 mNumVertices = 0; ///< Number of vertices in the mVertexIdx to use - }; - - static_assert(sizeof(Face) == 4, "Unexpected size"); - static_assert(alignof(Face) == 2, "Unexpected alignment"); - - struct Point - { - Vec3 mPosition; ///< Position of vertex - int mNumFaces = 0; ///< Number of faces in the face array below - int mFaces[3] = { -1, -1, -1 }; ///< Indices of 3 neighboring faces with the biggest difference in normal (used to shift vertices for convex radius) - }; - - static_assert(sizeof(Point) == 32, "Unexpected size"); - static_assert(alignof(Point) == JPH_VECTOR_ALIGNMENT, "Unexpected alignment"); - - Vec3 mCenterOfMass; ///< Center of mass of this convex hull - Mat44 mInertia; ///< Inertia matrix assuming density is 1 (needs to be multiplied by density) - AABox mLocalBounds; ///< Local bounding box for the convex hull - Array mPoints; ///< Points on the convex hull surface - Array mFaces; ///< Faces of the convex hull surface - Array mPlanes; ///< Planes for the faces (1-on-1 with mFaces array, separate because they need to be 16 byte aligned) - Array mVertexIdx; ///< A list of vertex indices (indexing in mPoints) for each of the faces - float mConvexRadius = 0.0f; ///< Convex radius - float mVolume; ///< Total volume of the convex hull - float mInnerRadius = FLT_MAX; ///< Radius of the biggest sphere that fits entirely in the convex hull - -#ifdef JPH_DEBUG_RENDERER - mutable DebugRenderer::GeometryRef mGeometry; -#endif // JPH_DEBUG_RENDERER -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/ConvexShape.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/ConvexShape.cpp deleted file mode 100644 index 550723589f3..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/ConvexShape.cpp +++ /dev/null @@ -1,559 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -JPH_IMPLEMENT_SERIALIZABLE_ABSTRACT(ConvexShapeSettings) -{ - JPH_ADD_BASE_CLASS(ConvexShapeSettings, ShapeSettings) - - JPH_ADD_ATTRIBUTE(ConvexShapeSettings, mDensity) - JPH_ADD_ATTRIBUTE(ConvexShapeSettings, mMaterial) -} - -const std::vector ConvexShape::sUnitSphereTriangles = []() { - const int level = 2; - - std::vector verts; - GetTrianglesContextVertexList::sCreateHalfUnitSphereTop(verts, level); - GetTrianglesContextVertexList::sCreateHalfUnitSphereBottom(verts, level); - return verts; -}(); - -void ConvexShape::sCollideConvexVsConvex(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, [[maybe_unused]] const ShapeFilter &inShapeFilter) -{ - JPH_PROFILE_FUNCTION(); - - // Get the shapes - JPH_ASSERT(inShape1->GetType() == EShapeType::Convex); - JPH_ASSERT(inShape2->GetType() == EShapeType::Convex); - const ConvexShape *shape1 = static_cast(inShape1); - const ConvexShape *shape2 = static_cast(inShape2); - - // Get transforms - Mat44 inverse_transform1 = inCenterOfMassTransform1.InversedRotationTranslation(); - Mat44 transform_2_to_1 = inverse_transform1 * inCenterOfMassTransform2; - - // Get bounding boxes - AABox shape1_bbox = shape1->GetLocalBounds().Scaled(inScale1); - shape1_bbox.ExpandBy(Vec3::sReplicate(inCollideShapeSettings.mMaxSeparationDistance)); - AABox shape2_bbox = shape2->GetLocalBounds().Scaled(inScale2); - - // Check if they overlap - if (!OrientedBox(transform_2_to_1, shape2_bbox).Overlaps(shape1_bbox)) - return; - - // Note: As we don't remember the penetration axis from the last iteration, and it is likely that shape2 is pushed out of - // collision relative to shape1 by comparing their COM's, we use that as an initial penetration axis: shape2.com - shape1.com - // This has been seen to improve performance by approx. 1% over using a fixed axis like (1, 0, 0). - Vec3 penetration_axis = transform_2_to_1.GetTranslation(); - - // Ensure that we do not pass in a near zero penetration axis - if (penetration_axis.IsNearZero()) - penetration_axis = Vec3::sAxisX(); - - Vec3 point1, point2; - EPAPenetrationDepth pen_depth; - EPAPenetrationDepth::EStatus status; - - // Scope to limit lifetime of SupportBuffer - { - // Create support function - SupportBuffer buffer1_excl_cvx_radius, buffer2_excl_cvx_radius; - const Support *shape1_excl_cvx_radius = shape1->GetSupportFunction(ConvexShape::ESupportMode::ExcludeConvexRadius, buffer1_excl_cvx_radius, inScale1); - const Support *shape2_excl_cvx_radius = shape2->GetSupportFunction(ConvexShape::ESupportMode::ExcludeConvexRadius, buffer2_excl_cvx_radius, inScale2); - - // Transform shape 2 in the space of shape 1 - TransformedConvexObject transformed2_excl_cvx_radius(transform_2_to_1, *shape2_excl_cvx_radius); - - // Perform GJK step - status = pen_depth.GetPenetrationDepthStepGJK(*shape1_excl_cvx_radius, shape1_excl_cvx_radius->GetConvexRadius() + inCollideShapeSettings.mMaxSeparationDistance, transformed2_excl_cvx_radius, shape2_excl_cvx_radius->GetConvexRadius(), inCollideShapeSettings.mCollisionTolerance, penetration_axis, point1, point2); - } - - // Check result of collision detection - switch (status) - { - case EPAPenetrationDepth::EStatus::Colliding: - break; - - case EPAPenetrationDepth::EStatus::NotColliding: - return; - - case EPAPenetrationDepth::EStatus::Indeterminate: - { - // Need to run expensive EPA algorithm - - // Create support function - SupportBuffer buffer1_incl_cvx_radius, buffer2_incl_cvx_radius; - const Support *shape1_incl_cvx_radius = shape1->GetSupportFunction(ConvexShape::ESupportMode::IncludeConvexRadius, buffer1_incl_cvx_radius, inScale1); - const Support *shape2_incl_cvx_radius = shape2->GetSupportFunction(ConvexShape::ESupportMode::IncludeConvexRadius, buffer2_incl_cvx_radius, inScale2); - - // Add separation distance - AddConvexRadius shape1_add_max_separation_distance(*shape1_incl_cvx_radius, inCollideShapeSettings.mMaxSeparationDistance); - - // Transform shape 2 in the space of shape 1 - TransformedConvexObject transformed2_incl_cvx_radius(transform_2_to_1, *shape2_incl_cvx_radius); - - // Perform EPA step - if (!pen_depth.GetPenetrationDepthStepEPA(shape1_add_max_separation_distance, transformed2_incl_cvx_radius, inCollideShapeSettings.mPenetrationTolerance, penetration_axis, point1, point2)) - return; - break; - } - } - - // Check if the penetration is bigger than the early out fraction - float penetration_depth = (point2 - point1).Length() - inCollideShapeSettings.mMaxSeparationDistance; - if (-penetration_depth >= ioCollector.GetEarlyOutFraction()) - return; - - // Correct point1 for the added separation distance - float penetration_axis_len = penetration_axis.Length(); - if (penetration_axis_len > 0.0f) - point1 -= penetration_axis * (inCollideShapeSettings.mMaxSeparationDistance / penetration_axis_len); - - // Convert to world space - point1 = inCenterOfMassTransform1 * point1; - point2 = inCenterOfMassTransform1 * point2; - Vec3 penetration_axis_world = inCenterOfMassTransform1.Multiply3x3(penetration_axis); - - // Create collision result - CollideShapeResult result(point1, point2, penetration_axis_world, penetration_depth, inSubShapeIDCreator1.GetID(), inSubShapeIDCreator2.GetID(), TransformedShape::sGetBodyID(ioCollector.GetContext())); - - // Gather faces - if (inCollideShapeSettings.mCollectFacesMode == ECollectFacesMode::CollectFaces) - { - // Get supporting face of shape 1 - shape1->GetSupportingFace(SubShapeID(), -penetration_axis, inScale1, inCenterOfMassTransform1, result.mShape1Face); - - // Get supporting face of shape 2 - shape2->GetSupportingFace(SubShapeID(), transform_2_to_1.Multiply3x3Transposed(penetration_axis), inScale2, inCenterOfMassTransform2, result.mShape2Face); - } - - // Notify the collector - JPH_IF_TRACK_NARROWPHASE_STATS(TrackNarrowPhaseCollector track;) - ioCollector.AddHit(result); -} - -bool ConvexShape::CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const -{ - // Note: This is a fallback routine, most convex shapes should implement a more performant version! - - JPH_PROFILE_FUNCTION(); - - // Create support function - SupportBuffer buffer; - const Support *support = GetSupportFunction(ConvexShape::ESupportMode::IncludeConvexRadius, buffer, Vec3::sReplicate(1.0f)); - - // Cast ray - GJKClosestPoint gjk; - if (gjk.CastRay(inRay.mOrigin, inRay.mDirection, cDefaultCollisionTolerance, *support, ioHit.mFraction)) - { - ioHit.mSubShapeID2 = inSubShapeIDCreator.GetID(); - return true; - } - - return false; -} - -void ConvexShape::CastRay(const RayCast &inRay, const RayCastSettings &inRayCastSettings, const SubShapeIDCreator &inSubShapeIDCreator, CastRayCollector &ioCollector, const ShapeFilter &inShapeFilter) const -{ - // Note: This is a fallback routine, most convex shapes should implement a more performant version! - - // Test shape filter - if (!inShapeFilter.ShouldCollide(this, inSubShapeIDCreator.GetID())) - return; - - // First do a normal raycast, limited to the early out fraction - RayCastResult hit; - hit.mFraction = ioCollector.GetEarlyOutFraction(); - if (CastRay(inRay, inSubShapeIDCreator, hit)) - { - // Check front side - if (inRayCastSettings.mTreatConvexAsSolid || hit.mFraction > 0.0f) - { - hit.mBodyID = TransformedShape::sGetBodyID(ioCollector.GetContext()); - ioCollector.AddHit(hit); - } - - // Check if we want back facing hits and the collector still accepts additional hits - if (inRayCastSettings.mBackFaceMode == EBackFaceMode::CollideWithBackFaces && !ioCollector.ShouldEarlyOut()) - { - // Invert the ray, going from the early out fraction back to the fraction where we found our forward hit - float start_fraction = min(1.0f, ioCollector.GetEarlyOutFraction()); - float delta_fraction = hit.mFraction - start_fraction; - if (delta_fraction < 0.0f) - { - RayCast inverted_ray { inRay.mOrigin + start_fraction * inRay.mDirection, delta_fraction * inRay.mDirection }; - - // Cast another ray - RayCastResult inverted_hit; - inverted_hit.mFraction = 1.0f; - if (CastRay(inverted_ray, inSubShapeIDCreator, inverted_hit) - && inverted_hit.mFraction > 0.0f) // Ignore hits with fraction 0, this means the ray ends inside the object and we don't want to report it as a back facing hit - { - // Invert fraction and rescale it to the fraction of the original ray - inverted_hit.mFraction = hit.mFraction + (inverted_hit.mFraction - 1.0f) * delta_fraction; - inverted_hit.mBodyID = TransformedShape::sGetBodyID(ioCollector.GetContext()); - ioCollector.AddHit(inverted_hit); - } - } - } - } -} - -void ConvexShape::CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter) const -{ - // Test shape filter - if (!inShapeFilter.ShouldCollide(this, inSubShapeIDCreator.GetID())) - return; - - // First test bounding box - if (GetLocalBounds().Contains(inPoint)) - { - // Create support function - SupportBuffer buffer; - const Support *support = GetSupportFunction(ConvexShape::ESupportMode::IncludeConvexRadius, buffer, Vec3::sReplicate(1.0f)); - - // Create support function for point - PointConvexSupport point { inPoint }; - - // Test intersection - GJKClosestPoint gjk; - Vec3 v = inPoint; - if (gjk.Intersects(*support, point, cDefaultCollisionTolerance, v)) - ioCollector.AddHit({ TransformedShape::sGetBodyID(ioCollector.GetContext()), inSubShapeIDCreator.GetID() }); - } -} - -void ConvexShape::sCastConvexVsConvex(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, [[maybe_unused]] const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector) -{ - JPH_PROFILE_FUNCTION(); - - // Only supported for convex shapes - JPH_ASSERT(inShapeCast.mShape->GetType() == EShapeType::Convex); - const ConvexShape *cast_shape = static_cast(inShapeCast.mShape); - - JPH_ASSERT(inShape->GetType() == EShapeType::Convex); - const ConvexShape *shape = static_cast(inShape); - - // Determine if we want to use the actual shape or a shrunken shape with convex radius - ConvexShape::ESupportMode support_mode = inShapeCastSettings.mUseShrunkenShapeAndConvexRadius? ConvexShape::ESupportMode::ExcludeConvexRadius : ConvexShape::ESupportMode::Default; - - // Create support function for shape to cast - SupportBuffer cast_buffer; - const Support *cast_support = cast_shape->GetSupportFunction(support_mode, cast_buffer, inShapeCast.mScale); - - // Create support function for target shape - SupportBuffer target_buffer; - const Support *target_support = shape->GetSupportFunction(support_mode, target_buffer, inScale); - - // Do a raycast against the result - EPAPenetrationDepth epa; - float fraction = ioCollector.GetEarlyOutFraction(); - Vec3 contact_point_a, contact_point_b, contact_normal; - if (epa.CastShape(inShapeCast.mCenterOfMassStart, inShapeCast.mDirection, inShapeCastSettings.mCollisionTolerance, inShapeCastSettings.mPenetrationTolerance, *cast_support, *target_support, cast_support->GetConvexRadius(), target_support->GetConvexRadius(), inShapeCastSettings.mReturnDeepestPoint, fraction, contact_point_a, contact_point_b, contact_normal) - && (inShapeCastSettings.mBackFaceModeConvex == EBackFaceMode::CollideWithBackFaces - || contact_normal.Dot(inShapeCast.mDirection) > 0.0f)) // Test if backfacing - { - // Convert to world space - contact_point_a = inCenterOfMassTransform2 * contact_point_a; - contact_point_b = inCenterOfMassTransform2 * contact_point_b; - Vec3 contact_normal_world = inCenterOfMassTransform2.Multiply3x3(contact_normal); - - ShapeCastResult result(fraction, contact_point_a, contact_point_b, contact_normal_world, false, inSubShapeIDCreator1.GetID(), inSubShapeIDCreator2.GetID(), TransformedShape::sGetBodyID(ioCollector.GetContext())); - - // Early out if this hit is deeper than the collector's early out value - if (fraction == 0.0f && -result.mPenetrationDepth >= ioCollector.GetEarlyOutFraction()) - return; - - // Gather faces - if (inShapeCastSettings.mCollectFacesMode == ECollectFacesMode::CollectFaces) - { - // Get supporting face of shape 1 - Mat44 transform_1_to_2 = inShapeCast.mCenterOfMassStart; - transform_1_to_2.SetTranslation(transform_1_to_2.GetTranslation() + fraction * inShapeCast.mDirection); - cast_shape->GetSupportingFace(SubShapeID(), transform_1_to_2.Multiply3x3Transposed(-contact_normal), inShapeCast.mScale, inCenterOfMassTransform2 * transform_1_to_2, result.mShape1Face); - - // Get supporting face of shape 2 - shape->GetSupportingFace(SubShapeID(), contact_normal, inScale, inCenterOfMassTransform2, result.mShape2Face); - } - - JPH_IF_TRACK_NARROWPHASE_STATS(TrackNarrowPhaseCollector track;) - ioCollector.AddHit(result); - } -} - -class ConvexShape::CSGetTrianglesContext -{ -public: - CSGetTrianglesContext(const ConvexShape *inShape, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) : - mLocalToWorld(Mat44::sRotationTranslation(inRotation, inPositionCOM) * Mat44::sScale(inScale)), - mIsInsideOut(ScaleHelpers::IsInsideOut(inScale)) - { - mSupport = inShape->GetSupportFunction(ESupportMode::IncludeConvexRadius, mSupportBuffer, Vec3::sReplicate(1.0f)); - } - - SupportBuffer mSupportBuffer; - const Support * mSupport; - Mat44 mLocalToWorld; - bool mIsInsideOut; - size_t mCurrentVertex = 0; -}; - -void ConvexShape::GetTrianglesStart(GetTrianglesContext &ioContext, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) const -{ - static_assert(sizeof(CSGetTrianglesContext) <= sizeof(GetTrianglesContext), "GetTrianglesContext too small"); - JPH_ASSERT(IsAligned(&ioContext, alignof(CSGetTrianglesContext))); - - new (&ioContext) CSGetTrianglesContext(this, inPositionCOM, inRotation, inScale); -} - -int ConvexShape::GetTrianglesNext(GetTrianglesContext &ioContext, int inMaxTrianglesRequested, Float3 *outTriangleVertices, const PhysicsMaterial **outMaterials) const -{ - JPH_ASSERT(inMaxTrianglesRequested >= cGetTrianglesMinTrianglesRequested); - - CSGetTrianglesContext &context = (CSGetTrianglesContext &)ioContext; - - int total_num_vertices = min(inMaxTrianglesRequested * 3, int(sUnitSphereTriangles.size() - context.mCurrentVertex)); - - if (context.mIsInsideOut) - { - // Store triangles flipped - for (const Vec3 *v = sUnitSphereTriangles.data() + context.mCurrentVertex, *v_end = v + total_num_vertices; v < v_end; v += 3) - { - (context.mLocalToWorld * context.mSupport->GetSupport(v[0])).StoreFloat3(outTriangleVertices++); - (context.mLocalToWorld * context.mSupport->GetSupport(v[2])).StoreFloat3(outTriangleVertices++); - (context.mLocalToWorld * context.mSupport->GetSupport(v[1])).StoreFloat3(outTriangleVertices++); - } - } - else - { - // Store triangles - for (const Vec3 *v = sUnitSphereTriangles.data() + context.mCurrentVertex, *v_end = v + total_num_vertices; v < v_end; v += 3) - { - (context.mLocalToWorld * context.mSupport->GetSupport(v[0])).StoreFloat3(outTriangleVertices++); - (context.mLocalToWorld * context.mSupport->GetSupport(v[1])).StoreFloat3(outTriangleVertices++); - (context.mLocalToWorld * context.mSupport->GetSupport(v[2])).StoreFloat3(outTriangleVertices++); - } - } - - context.mCurrentVertex += total_num_vertices; - int total_num_triangles = total_num_vertices / 3; - - // Store materials - if (outMaterials != nullptr) - { - const PhysicsMaterial *material = GetMaterial(); - for (const PhysicsMaterial **m = outMaterials, **m_end = outMaterials + total_num_triangles; m < m_end; ++m) - *m = material; - } - - return total_num_triangles; -} - -void ConvexShape::GetSubmergedVolume(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, const Plane &inSurface, float &outTotalVolume, float &outSubmergedVolume, Vec3 &outCenterOfBuoyancy JPH_IF_DEBUG_RENDERER(, RVec3Arg inBaseOffset)) const -{ - // Calculate total volume - Vec3 abs_scale = inScale.Abs(); - Vec3 extent = GetLocalBounds().GetExtent() * abs_scale; - outTotalVolume = 8.0f * extent.GetX() * extent.GetY() * extent.GetZ(); - - // Points of the bounding box - Vec3 points[] = - { - Vec3(-1, -1, -1), - Vec3( 1, -1, -1), - Vec3(-1, 1, -1), - Vec3( 1, 1, -1), - Vec3(-1, -1, 1), - Vec3( 1, -1, 1), - Vec3(-1, 1, 1), - Vec3( 1, 1, 1), - }; - - // Faces of the bounding box - using Face = int[5]; - #define MAKE_FACE(a, b, c, d) { a, b, c, d, ((1 << a) | (1 << b) | (1 << c) | (1 << d)) } // Last int is a bit mask that indicates which indices are used - Face faces[] = - { - MAKE_FACE(0, 2, 3, 1), - MAKE_FACE(4, 6, 2, 0), - MAKE_FACE(4, 5, 7, 6), - MAKE_FACE(1, 3, 7, 5), - MAKE_FACE(2, 6, 7, 3), - MAKE_FACE(0, 1, 5, 4), - }; - - PolyhedronSubmergedVolumeCalculator::Point *buffer = (PolyhedronSubmergedVolumeCalculator::Point *)JPH_STACK_ALLOC(8 * sizeof(PolyhedronSubmergedVolumeCalculator::Point)); - PolyhedronSubmergedVolumeCalculator submerged_vol_calc(inCenterOfMassTransform * Mat44::sScale(extent), points, sizeof(Vec3), 8, inSurface, buffer JPH_IF_DEBUG_RENDERER(, inBaseOffset)); - - if (submerged_vol_calc.AreAllAbove()) - { - // We're above the water - outSubmergedVolume = 0.0f; - outCenterOfBuoyancy = Vec3::sZero(); - } - else if (submerged_vol_calc.AreAllBelow()) - { - // We're fully submerged - outSubmergedVolume = outTotalVolume; - outCenterOfBuoyancy = inCenterOfMassTransform.GetTranslation(); - } - else - { - // Calculate submerged volume - int reference_point_bit = 1 << submerged_vol_calc.GetReferencePointIdx(); - for (const Face &f : faces) - { - // Test if this face includes the reference point - if ((f[4] & reference_point_bit) == 0) - { - // Triangulate the face (a quad) - submerged_vol_calc.AddFace(f[0], f[1], f[2]); - submerged_vol_calc.AddFace(f[0], f[2], f[3]); - } - } - - submerged_vol_calc.GetResult(outSubmergedVolume, outCenterOfBuoyancy); - } -} - -#ifdef JPH_DEBUG_RENDERER -void ConvexShape::DrawGetSupportFunction(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inDrawSupportDirection) const -{ - // Get the support function with convex radius - SupportBuffer buffer; - const Support *support = GetSupportFunction(ESupportMode::ExcludeConvexRadius, buffer, inScale); - AddConvexRadius add_convex(*support, support->GetConvexRadius()); - - // Draw the shape - DebugRenderer::GeometryRef geometry = inRenderer->CreateTriangleGeometryForConvex([&add_convex](Vec3Arg inDirection) { return add_convex.GetSupport(inDirection); }); - AABox bounds = geometry->mBounds.Transformed(inCenterOfMassTransform); - float lod_scale_sq = geometry->mBounds.GetExtent().LengthSq(); - inRenderer->DrawGeometry(inCenterOfMassTransform, bounds, lod_scale_sq, inColor, geometry); - - if (inDrawSupportDirection) - { - // Iterate on all directions and draw the support point and an arrow in the direction that was sampled to test if the support points make sense - for (Vec3 v : Vec3::sUnitSphere) - { - Vec3 direction = 0.05f * v; - Vec3 pos = add_convex.GetSupport(direction); - RVec3 from = inCenterOfMassTransform * pos; - RVec3 to = inCenterOfMassTransform * (pos + direction); - inRenderer->DrawMarker(from, Color::sWhite, 0.001f); - inRenderer->DrawArrow(from, to, Color::sWhite, 0.001f); - } - } -} - -void ConvexShape::DrawGetSupportingFace(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale) const -{ - // Sample directions and map which faces belong to which directions - using FaceToDirection = UnorderedMap>; - FaceToDirection faces; - for (Vec3 v : Vec3::sUnitSphere) - { - Vec3 direction = 0.05f * v; - - SupportingFace face; - GetSupportingFace(SubShapeID(), direction, inScale, Mat44::sIdentity(), face); - - if (!face.empty()) - { - JPH_ASSERT(face.size() >= 2, "The GetSupportingFace function should either return nothing or at least an edge"); - faces[face].push_back(direction); - } - } - - // Draw each face in a unique color and draw corresponding directions - int color_it = 0; - for (FaceToDirection::value_type &ftd : faces) - { - Color color = Color::sGetDistinctColor(color_it++); - - // Create copy of face (key in map is read only) - SupportingFace face = ftd.first; - - // Displace the face a little bit forward so it is easier to see - Vec3 normal = face.size() >= 3? (face[2] - face[1]).Cross(face[0] - face[1]).Normalized() : Vec3::sZero(); - Vec3 displacement = 0.001f * normal; - - // Transform face to world space and calculate center of mass - Vec3 com_ls = Vec3::sZero(); - for (Vec3 &v : face) - { - v = inCenterOfMassTransform.Multiply3x3(v + displacement); - com_ls += v; - } - RVec3 com = inCenterOfMassTransform.GetTranslation() + com_ls / (float)face.size(); - - // Draw the polygon and directions - inRenderer->DrawWirePolygon(RMat44::sTranslation(inCenterOfMassTransform.GetTranslation()), face, color, face.size() >= 3? 0.001f : 0.0f); - if (face.size() >= 3) - inRenderer->DrawArrow(com, com + inCenterOfMassTransform.Multiply3x3(normal), color, 0.01f); - for (Vec3 &v : ftd.second) - inRenderer->DrawArrow(com, com + inCenterOfMassTransform.Multiply3x3(-v), color, 0.001f); - } -} -#endif // JPH_DEBUG_RENDERER - -void ConvexShape::SaveBinaryState(StreamOut &inStream) const -{ - Shape::SaveBinaryState(inStream); - - inStream.Write(mDensity); -} - -void ConvexShape::RestoreBinaryState(StreamIn &inStream) -{ - Shape::RestoreBinaryState(inStream); - - inStream.Read(mDensity); -} - -void ConvexShape::SaveMaterialState(PhysicsMaterialList &outMaterials) const -{ - outMaterials.clear(); - outMaterials.push_back(mMaterial); -} - -void ConvexShape::RestoreMaterialState(const PhysicsMaterialRefC *inMaterials, uint inNumMaterials) -{ - JPH_ASSERT(inNumMaterials == 1); - mMaterial = inMaterials[0]; -} - -void ConvexShape::sRegister() -{ - for (EShapeSubType s1 : sConvexSubShapeTypes) - for (EShapeSubType s2 : sConvexSubShapeTypes) - { - CollisionDispatch::sRegisterCollideShape(s1, s2, sCollideConvexVsConvex); - CollisionDispatch::sRegisterCastShape(s1, s2, sCastConvexVsConvex); - } -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/ConvexShape.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/ConvexShape.h deleted file mode 100644 index 562403b52a8..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/ConvexShape.h +++ /dev/null @@ -1,150 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -class CollideShapeSettings; - -/// Class that constructs a ConvexShape (abstract) -class JPH_EXPORT ConvexShapeSettings : public ShapeSettings -{ -public: - JPH_DECLARE_SERIALIZABLE_ABSTRACT(JPH_EXPORT, ConvexShapeSettings) - - /// Constructor - ConvexShapeSettings() = default; - explicit ConvexShapeSettings(const PhysicsMaterial *inMaterial) : mMaterial(inMaterial) { } - - /// Set the density of the object in kg / m^3 - void SetDensity(float inDensity) { mDensity = inDensity; } - - // Properties - RefConst mMaterial; ///< Material assigned to this shape - float mDensity = 1000.0f; ///< Uniform density of the interior of the convex object (kg / m^3) -}; - -/// Base class for all convex shapes. Defines a virtual interface. -class JPH_EXPORT ConvexShape : public Shape -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Constructor - explicit ConvexShape(EShapeSubType inSubType) : Shape(EShapeType::Convex, inSubType) { } - ConvexShape(EShapeSubType inSubType, const ConvexShapeSettings &inSettings, ShapeResult &outResult) : Shape(EShapeType::Convex, inSubType, inSettings, outResult), mMaterial(inSettings.mMaterial), mDensity(inSettings.mDensity) { } - ConvexShape(EShapeSubType inSubType, const PhysicsMaterial *inMaterial) : Shape(EShapeType::Convex, inSubType), mMaterial(inMaterial) { } - - // See Shape::GetSubShapeIDBitsRecursive - virtual uint GetSubShapeIDBitsRecursive() const override { return 0; } // Convex shapes don't have sub shapes - - // See Shape::GetMaterial - virtual const PhysicsMaterial * GetMaterial(const SubShapeID &inSubShapeID) const override { JPH_ASSERT(inSubShapeID.IsEmpty(), "Invalid subshape ID"); return GetMaterial(); } - - // See Shape::CastRay - virtual bool CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const override; - virtual void CastRay(const RayCast &inRay, const RayCastSettings &inRayCastSettings, const SubShapeIDCreator &inSubShapeIDCreator, CastRayCollector &ioCollector, const ShapeFilter &inShapeFilter = { }) const override; - - // See: Shape::CollidePoint - virtual void CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter = { }) const override; - - // See Shape::GetTrianglesStart - virtual void GetTrianglesStart(GetTrianglesContext &ioContext, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) const override; - - // See Shape::GetTrianglesNext - virtual int GetTrianglesNext(GetTrianglesContext &ioContext, int inMaxTrianglesRequested, Float3 *outTriangleVertices, const PhysicsMaterial **outMaterials = nullptr) const override; - - // See Shape::GetSubmergedVolume - virtual void GetSubmergedVolume(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, const Plane &inSurface, float &outTotalVolume, float &outSubmergedVolume, Vec3 &outCenterOfBuoyancy JPH_IF_DEBUG_RENDERER(, RVec3Arg inBaseOffset)) const override; - - /// Function that provides an interface for GJK - class Support - { - public: - /// Warning: Virtual destructor will not be called on this object! - virtual ~Support() = default; - - /// Calculate the support vector for this convex shape (includes / excludes the convex radius depending on how this was obtained). - /// Support vector is relative to the center of mass of the shape. - virtual Vec3 GetSupport(Vec3Arg inDirection) const = 0; - - /// Convex radius of shape. Collision detection on penetrating shapes is much more expensive, - /// so you can add a radius around objects to increase the shape. This makes it far less likely that they will actually penetrate. - virtual float GetConvexRadius() const = 0; - }; - - /// Buffer to hold a Support object, used to avoid dynamic memory allocations - class alignas(16) SupportBuffer - { - public: - uint8 mData[4160]; - }; - - /// How the GetSupport function should behave - enum class ESupportMode - { - ExcludeConvexRadius, ///< Return the shape excluding the convex radius, Support::GetConvexRadius will return the convex radius if there is one, but adding this radius may not result in the most accurate/efficient representation of shapes with sharp edges - IncludeConvexRadius, ///< Return the shape including the convex radius, Support::GetSupport includes the convex radius if there is one, Support::GetConvexRadius will return 0 - Default, ///< Use both Support::GetSupport add Support::GetConvexRadius to get a support point that matches the original shape as accurately/efficiently as possible - }; - - /// Returns an object that provides the GetSupport function for this shape. - /// inMode determines if this support function includes or excludes the convex radius. - /// of the values returned by the GetSupport function. This improves numerical accuracy of the results. - /// inScale scales this shape in local space. - virtual const Support * GetSupportFunction(ESupportMode inMode, SupportBuffer &inBuffer, Vec3Arg inScale) const = 0; - - /// Material of the shape - void SetMaterial(const PhysicsMaterial *inMaterial) { mMaterial = inMaterial; } - const PhysicsMaterial * GetMaterial() const { return mMaterial != nullptr? mMaterial : PhysicsMaterial::sDefault; } - - /// Set density of the shape (kg / m^3) - void SetDensity(float inDensity) { mDensity = inDensity; } - - /// Get density of the shape (kg / m^3) - float GetDensity() const { return mDensity; } - -#ifdef JPH_DEBUG_RENDERER - // See Shape::DrawGetSupportFunction - virtual void DrawGetSupportFunction(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inDrawSupportDirection) const override; - - // See Shape::DrawGetSupportingFace - virtual void DrawGetSupportingFace(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale) const override; -#endif // JPH_DEBUG_RENDERER - - // See Shape - virtual void SaveBinaryState(StreamOut &inStream) const override; - virtual void SaveMaterialState(PhysicsMaterialList &outMaterials) const override; - virtual void RestoreMaterialState(const PhysicsMaterialRefC *inMaterials, uint inNumMaterials) override; - - // Register shape functions with the registry - static void sRegister(); - -protected: - // See: Shape::RestoreBinaryState - virtual void RestoreBinaryState(StreamIn &inStream) override; - - /// Vertex list that forms a unit sphere - static const std::vector sUnitSphereTriangles; - -private: - // Class for GetTrianglesStart/Next - class CSGetTrianglesContext; - - // Helper functions called by CollisionDispatch - static void sCollideConvexVsConvex(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter); - static void sCastConvexVsConvex(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector); - - // Properties - RefConst mMaterial; ///< Material assigned to this shape - float mDensity = 1000.0f; ///< Uniform density of the interior of the convex object (kg / m^3) -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/CylinderShape.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/CylinderShape.cpp deleted file mode 100644 index 51a9f3c54bd..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/CylinderShape.cpp +++ /dev/null @@ -1,417 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef JPH_DEBUG_RENDERER - #include -#endif // JPH_DEBUG_RENDERER - -JPH_NAMESPACE_BEGIN - -JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(CylinderShapeSettings) -{ - JPH_ADD_BASE_CLASS(CylinderShapeSettings, ConvexShapeSettings) - - JPH_ADD_ATTRIBUTE(CylinderShapeSettings, mHalfHeight) - JPH_ADD_ATTRIBUTE(CylinderShapeSettings, mRadius) - JPH_ADD_ATTRIBUTE(CylinderShapeSettings, mConvexRadius) -} - -// Approximation of top face with 8 vertices -static const float cSin45 = 0.70710678118654752440084436210485f; -static const Vec3 cTopFace[] = -{ - Vec3(0.0f, 1.0f, 1.0f), - Vec3(cSin45, 1.0f, cSin45), - Vec3(1.0f, 1.0f, 0.0f), - Vec3(cSin45, 1.0f, -cSin45), - Vec3(-0.0f, 1.0f, -1.0f), - Vec3(-cSin45, 1.0f, -cSin45), - Vec3(-1.0f, 1.0f, 0.0f), - Vec3(-cSin45, 1.0f, cSin45) -}; - -static const std::vector sUnitCylinderTriangles = []() { - std::vector verts; - - const Vec3 bottom_offset(0.0f, -2.0f, 0.0f); - - int num_verts = sizeof(cTopFace) / sizeof(Vec3); - for (int i = 0; i < num_verts; ++i) - { - Vec3 t1 = cTopFace[i]; - Vec3 t2 = cTopFace[(i + 1) % num_verts]; - Vec3 b1 = cTopFace[i] + bottom_offset; - Vec3 b2 = cTopFace[(i + 1) % num_verts] + bottom_offset; - - // Top - verts.emplace_back(0.0f, 1.0f, 0.0f); - verts.push_back(t1); - verts.push_back(t2); - - // Bottom - verts.emplace_back(0.0f, -1.0f, 0.0f); - verts.push_back(b2); - verts.push_back(b1); - - // Side - verts.push_back(t1); - verts.push_back(b1); - verts.push_back(t2); - - verts.push_back(t2); - verts.push_back(b1); - verts.push_back(b2); - } - - return verts; -}(); - -ShapeSettings::ShapeResult CylinderShapeSettings::Create() const -{ - if (mCachedResult.IsEmpty()) - Ref shape = new CylinderShape(*this, mCachedResult); - return mCachedResult; -} - -CylinderShape::CylinderShape(const CylinderShapeSettings &inSettings, ShapeResult &outResult) : - ConvexShape(EShapeSubType::Cylinder, inSettings, outResult), - mHalfHeight(inSettings.mHalfHeight), - mRadius(inSettings.mRadius), - mConvexRadius(inSettings.mConvexRadius) -{ - if (inSettings.mHalfHeight < inSettings.mConvexRadius) - { - outResult.SetError("Invalid height"); - return; - } - - if (inSettings.mRadius < inSettings.mConvexRadius) - { - outResult.SetError("Invalid radius"); - return; - } - - if (inSettings.mConvexRadius < 0.0f) - { - outResult.SetError("Invalid convex radius"); - return; - } - - outResult.Set(this); -} - -CylinderShape::CylinderShape(float inHalfHeight, float inRadius, float inConvexRadius, const PhysicsMaterial *inMaterial) : - ConvexShape(EShapeSubType::Cylinder, inMaterial), - mHalfHeight(inHalfHeight), - mRadius(inRadius), - mConvexRadius(inConvexRadius) -{ - JPH_ASSERT(inHalfHeight >= inConvexRadius); - JPH_ASSERT(inRadius >= inConvexRadius); - JPH_ASSERT(inConvexRadius >= 0.0f); -} - -class CylinderShape::Cylinder final : public Support -{ -public: - Cylinder(float inHalfHeight, float inRadius, float inConvexRadius) : - mHalfHeight(inHalfHeight), - mRadius(inRadius), - mConvexRadius(inConvexRadius) - { - static_assert(sizeof(Cylinder) <= sizeof(SupportBuffer), "Buffer size too small"); - JPH_ASSERT(IsAligned(this, alignof(Cylinder))); - } - - virtual Vec3 GetSupport(Vec3Arg inDirection) const override - { - // Support mapping, taken from: - // A Fast and Robust GJK Implementation for Collision Detection of Convex Objects - Gino van den Bergen - // page 8 - float x = inDirection.GetX(), y = inDirection.GetY(), z = inDirection.GetZ(); - float o = sqrt(Square(x) + Square(z)); - if (o > 0.0f) - return Vec3((mRadius * x) / o, Sign(y) * mHalfHeight, (mRadius * z) / o); - else - return Vec3(0, Sign(y) * mHalfHeight, 0); - } - - virtual float GetConvexRadius() const override - { - return mConvexRadius; - } - -private: - float mHalfHeight; - float mRadius; - float mConvexRadius; -}; - -const ConvexShape::Support *CylinderShape::GetSupportFunction(ESupportMode inMode, SupportBuffer &inBuffer, Vec3Arg inScale) const -{ - JPH_ASSERT(IsValidScale(inScale)); - - // Get scaled cylinder - Vec3 abs_scale = inScale.Abs(); - float scale_xz = abs_scale.GetX(); - float scale_y = abs_scale.GetY(); - float scaled_half_height = scale_y * mHalfHeight; - float scaled_radius = scale_xz * mRadius; - float scaled_convex_radius = ScaleHelpers::ScaleConvexRadius(mConvexRadius, inScale); - - switch (inMode) - { - case ESupportMode::IncludeConvexRadius: - case ESupportMode::Default: - return new (&inBuffer) Cylinder(scaled_half_height, scaled_radius, 0.0f); - - case ESupportMode::ExcludeConvexRadius: - return new (&inBuffer) Cylinder(scaled_half_height - scaled_convex_radius, scaled_radius - scaled_convex_radius, scaled_convex_radius); - } - - JPH_ASSERT(false); - return nullptr; -} - -void CylinderShape::GetSupportingFace(const SubShapeID &inSubShapeID, Vec3Arg inDirection, Vec3Arg inScale, Mat44Arg inCenterOfMassTransform, SupportingFace &outVertices) const -{ - JPH_ASSERT(inSubShapeID.IsEmpty(), "Invalid subshape ID"); - JPH_ASSERT(IsValidScale(inScale)); - - // Get scaled cylinder - Vec3 abs_scale = inScale.Abs(); - float scale_xz = abs_scale.GetX(); - float scale_y = abs_scale.GetY(); - float scaled_half_height = scale_y * mHalfHeight; - float scaled_radius = scale_xz * mRadius; - - float x = inDirection.GetX(), y = inDirection.GetY(), z = inDirection.GetZ(); - float o = sqrt(Square(x) + Square(z)); - - // If o / |y| > scaled_radius / scaled_half_height, we're hitting the side - if (o * scaled_half_height > scaled_radius * abs(y)) - { - // Hitting side - float f = -scaled_radius / o; - float vx = x * f; - float vz = z * f; - outVertices.push_back(inCenterOfMassTransform * Vec3(vx, scaled_half_height, vz)); - outVertices.push_back(inCenterOfMassTransform * Vec3(vx, -scaled_half_height, vz)); - } - else - { - // Hitting top or bottom - Vec3 multiplier = y < 0.0f? Vec3(scaled_radius, scaled_half_height, scaled_radius) : Vec3(-scaled_radius, -scaled_half_height, scaled_radius); - Mat44 transform = inCenterOfMassTransform.PreScaled(multiplier); - for (const Vec3 &v : cTopFace) - outVertices.push_back(transform * v); - } -} - -MassProperties CylinderShape::GetMassProperties() const -{ - MassProperties p; - - // Mass is surface of circle * height - float radius_sq = Square(mRadius); - float height = 2.0f * mHalfHeight; - p.mMass = JPH_PI * radius_sq * height * GetDensity(); - - // Inertia according to https://en.wikipedia.org/wiki/List_of_moments_of_inertia: - float inertia_y = radius_sq * p.mMass * 0.5f; - float inertia_x = inertia_y * 0.5f + p.mMass * height * height / 12.0f; - float inertia_z = inertia_x; - - // Set inertia - p.mInertia = Mat44::sScale(Vec3(inertia_x, inertia_y, inertia_z)); - - return p; -} - -Vec3 CylinderShape::GetSurfaceNormal(const SubShapeID &inSubShapeID, Vec3Arg inLocalSurfacePosition) const -{ - JPH_ASSERT(inSubShapeID.IsEmpty(), "Invalid subshape ID"); - - // Calculate distance to infinite cylinder surface - Vec3 local_surface_position_xz(inLocalSurfacePosition.GetX(), 0, inLocalSurfacePosition.GetZ()); - float local_surface_position_xz_len = local_surface_position_xz.Length(); - float distance_to_curved_surface = abs(local_surface_position_xz_len - mRadius); - - // Calculate distance to top or bottom plane - float distance_to_top_or_bottom = abs(abs(inLocalSurfacePosition.GetY()) - mHalfHeight); - - // Return normal according to closest surface - if (distance_to_curved_surface < distance_to_top_or_bottom) - return local_surface_position_xz / local_surface_position_xz_len; - else - return inLocalSurfacePosition.GetY() > 0.0f? Vec3::sAxisY() : -Vec3::sAxisY(); -} - -AABox CylinderShape::GetLocalBounds() const -{ - Vec3 extent = Vec3(mRadius, mHalfHeight, mRadius); - return AABox(-extent, extent); -} - -#ifdef JPH_DEBUG_RENDERER -void CylinderShape::Draw(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inUseMaterialColors, bool inDrawWireframe) const -{ - DebugRenderer::EDrawMode draw_mode = inDrawWireframe? DebugRenderer::EDrawMode::Wireframe : DebugRenderer::EDrawMode::Solid; - inRenderer->DrawCylinder(inCenterOfMassTransform * Mat44::sScale(inScale.Abs()), mHalfHeight, mRadius, inUseMaterialColors? GetMaterial()->GetDebugColor() : inColor, DebugRenderer::ECastShadow::On, draw_mode); -} -#endif // JPH_DEBUG_RENDERER - -bool CylinderShape::CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const -{ - // Test ray against capsule - float fraction = RayCylinder(inRay.mOrigin, inRay.mDirection, mHalfHeight, mRadius); - if (fraction < ioHit.mFraction) - { - ioHit.mFraction = fraction; - ioHit.mSubShapeID2 = inSubShapeIDCreator.GetID(); - return true; - } - return false; -} - -void CylinderShape::CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter) const -{ - // Test shape filter - if (!inShapeFilter.ShouldCollide(this, inSubShapeIDCreator.GetID())) - return; - - // Check if the point is in the cylinder - if (abs(inPoint.GetY()) <= mHalfHeight // Within the height - && Square(inPoint.GetX()) + Square(inPoint.GetZ()) <= Square(mRadius)) // Within the radius - ioCollector.AddHit({ TransformedShape::sGetBodyID(ioCollector.GetContext()), inSubShapeIDCreator.GetID() }); -} - -void CylinderShape::CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, SoftBodyVertex *ioVertices, uint inNumVertices, [[maybe_unused]] float inDeltaTime, [[maybe_unused]] Vec3Arg inDisplacementDueToGravity, int inCollidingShapeIndex) const -{ - JPH_ASSERT(IsValidScale(inScale)); - - Mat44 inverse_transform = inCenterOfMassTransform.InversedRotationTranslation(); - - // Get scaled cylinder - Vec3 abs_scale = inScale.Abs(); - float half_height = abs_scale.GetY() * mHalfHeight; - float radius = abs_scale.GetX() * mRadius; - - for (SoftBodyVertex *v = ioVertices, *sbv_end = ioVertices + inNumVertices; v < sbv_end; ++v) - if (v->mInvMass > 0.0f) - { - Vec3 local_pos = inverse_transform * v->mPosition; - - // Calculate penetration into side surface - Vec3 side_normal = local_pos; - side_normal.SetY(0.0f); - float side_normal_length = side_normal.Length(); - float side_penetration = radius - side_normal_length; - - // Calculate penetration into top or bottom plane - float top_penetration = half_height - abs(local_pos.GetY()); - - Vec3 point, normal; - if (side_penetration < 0.0f && top_penetration < 0.0f) - { - // We're outside the cylinder height and radius - point = side_normal * (radius / side_normal_length) + Vec3(0, half_height * Sign(local_pos.GetY()), 0); - normal = (local_pos - point).NormalizedOr(Vec3::sAxisY()); - } - else if (side_penetration < top_penetration) - { - // Side surface is closest - normal = side_normal_length > 0.0f? side_normal / side_normal_length : Vec3::sAxisX(); - point = radius * normal; - } - else - { - // Top or bottom plane is closest - normal = Vec3(0, Sign(local_pos.GetY()), 0); - point = half_height * normal; - } - - // Calculate penetration - Plane plane = Plane::sFromPointAndNormal(point, normal); - float penetration = -plane.SignedDistance(local_pos); - if (penetration > v->mLargestPenetration) - { - v->mLargestPenetration = penetration; - - // Store collision - v->mCollisionPlane = plane.GetTransformed(inCenterOfMassTransform); - v->mCollidingShapeIndex = inCollidingShapeIndex; - } - } -} - -void CylinderShape::TransformShape(Mat44Arg inCenterOfMassTransform, TransformedShapeCollector &ioCollector) const -{ - Vec3 scale; - Mat44 transform = inCenterOfMassTransform.Decompose(scale); - TransformedShape ts(RVec3(transform.GetTranslation()), transform.GetQuaternion(), this, BodyID(), SubShapeIDCreator()); - Vec3 abs_scale = scale.Abs(); - float xz = 0.5f * (abs_scale.GetX() + abs_scale.GetZ()); - ts.SetShapeScale(Vec3(xz, abs_scale.GetY(), xz)); - ioCollector.AddHit(ts); -} - -void CylinderShape::GetTrianglesStart(GetTrianglesContext &ioContext, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) const -{ - Mat44 unit_cylinder_transform(Vec4(mRadius, 0, 0, 0), Vec4(0, mHalfHeight, 0, 0), Vec4(0, 0, mRadius, 0), Vec4(0, 0, 0, 1)); - new (&ioContext) GetTrianglesContextVertexList(inPositionCOM, inRotation, inScale, unit_cylinder_transform, sUnitCylinderTriangles.data(), sUnitCylinderTriangles.size(), GetMaterial()); -} - -int CylinderShape::GetTrianglesNext(GetTrianglesContext &ioContext, int inMaxTrianglesRequested, Float3 *outTriangleVertices, const PhysicsMaterial **outMaterials) const -{ - return ((GetTrianglesContextVertexList &)ioContext).GetTrianglesNext(inMaxTrianglesRequested, outTriangleVertices, outMaterials); -} - -void CylinderShape::SaveBinaryState(StreamOut &inStream) const -{ - ConvexShape::SaveBinaryState(inStream); - - inStream.Write(mHalfHeight); - inStream.Write(mRadius); - inStream.Write(mConvexRadius); -} - -void CylinderShape::RestoreBinaryState(StreamIn &inStream) -{ - ConvexShape::RestoreBinaryState(inStream); - - inStream.Read(mHalfHeight); - inStream.Read(mRadius); - inStream.Read(mConvexRadius); -} - -bool CylinderShape::IsValidScale(Vec3Arg inScale) const -{ - // X and Z need same scale - Vec3 abs_scale = inScale.Abs(); - return ConvexShape::IsValidScale(inScale) && abs_scale.Swizzle().IsClose(abs_scale, ScaleHelpers::cScaleToleranceSq); -} - -void CylinderShape::sRegister() -{ - ShapeFunctions &f = ShapeFunctions::sGet(EShapeSubType::Cylinder); - f.mConstruct = []() -> Shape * { return new CylinderShape; }; - f.mColor = Color::sGreen; -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/CylinderShape.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/CylinderShape.h deleted file mode 100644 index 52108b13e8a..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/CylinderShape.h +++ /dev/null @@ -1,126 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include - -JPH_NAMESPACE_BEGIN - -/// Class that constructs a CylinderShape -class JPH_EXPORT CylinderShapeSettings final : public ConvexShapeSettings -{ -public: - JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, CylinderShapeSettings) - - /// Default constructor for deserialization - CylinderShapeSettings() = default; - - /// Create a shape centered around the origin with one top at (0, -inHalfHeight, 0) and the other at (0, inHalfHeight, 0) and radius inRadius. - /// (internally the convex radius will be subtracted from the cylinder the total cylinder will not grow with the convex radius, but the edges of the cylinder will be rounded a bit). - CylinderShapeSettings(float inHalfHeight, float inRadius, float inConvexRadius = cDefaultConvexRadius, const PhysicsMaterial *inMaterial = nullptr) : ConvexShapeSettings(inMaterial), mHalfHeight(inHalfHeight), mRadius(inRadius), mConvexRadius(inConvexRadius) { } - - // See: ShapeSettings - virtual ShapeResult Create() const override; - - float mHalfHeight = 0.0f; - float mRadius = 0.0f; - float mConvexRadius = 0.0f; -}; - -/// A cylinder -class JPH_EXPORT CylinderShape final : public ConvexShape -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Constructor - CylinderShape() : ConvexShape(EShapeSubType::Cylinder) { } - CylinderShape(const CylinderShapeSettings &inSettings, ShapeResult &outResult); - - /// Create a shape centered around the origin with one top at (0, -inHalfHeight, 0) and the other at (0, inHalfHeight, 0) and radius inRadius. - /// (internally the convex radius will be subtracted from the cylinder the total cylinder will not grow with the convex radius, but the edges of the cylinder will be rounded a bit). - CylinderShape(float inHalfHeight, float inRadius, float inConvexRadius = cDefaultConvexRadius, const PhysicsMaterial *inMaterial = nullptr); - - /// Get half height of cylinder - float GetHalfHeight() const { return mHalfHeight; } - - /// Get radius of cylinder - float GetRadius() const { return mRadius; } - - // See Shape::GetLocalBounds - virtual AABox GetLocalBounds() const override; - - // See Shape::GetInnerRadius - virtual float GetInnerRadius() const override { return min(mHalfHeight, mRadius); } - - // See Shape::GetMassProperties - virtual MassProperties GetMassProperties() const override; - - // See Shape::GetSurfaceNormal - virtual Vec3 GetSurfaceNormal(const SubShapeID &inSubShapeID, Vec3Arg inLocalSurfacePosition) const override; - - // See Shape::GetSupportingFace - virtual void GetSupportingFace(const SubShapeID &inSubShapeID, Vec3Arg inDirection, Vec3Arg inScale, Mat44Arg inCenterOfMassTransform, SupportingFace &outVertices) const override; - - // See ConvexShape::GetSupportFunction - virtual const Support * GetSupportFunction(ESupportMode inMode, SupportBuffer &inBuffer, Vec3Arg inScale) const override; - -#ifdef JPH_DEBUG_RENDERER - // See Shape::Draw - virtual void Draw(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inUseMaterialColors, bool inDrawWireframe) const override; -#endif // JPH_DEBUG_RENDERER - - // See Shape::CastRay - using ConvexShape::CastRay; - virtual bool CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const override; - - // See: Shape::CollidePoint - virtual void CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter = { }) const override; - - // See: Shape::CollideSoftBodyVertices - virtual void CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, SoftBodyVertex *ioVertices, uint inNumVertices, float inDeltaTime, Vec3Arg inDisplacementDueToGravity, int inCollidingShapeIndex) const override; - - // See Shape::TransformShape - virtual void TransformShape(Mat44Arg inCenterOfMassTransform, TransformedShapeCollector &ioCollector) const override; - - // See Shape::GetTrianglesStart - virtual void GetTrianglesStart(GetTrianglesContext &ioContext, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) const override; - - // See Shape::GetTrianglesNext - virtual int GetTrianglesNext(GetTrianglesContext &ioContext, int inMaxTrianglesRequested, Float3 *outTriangleVertices, const PhysicsMaterial **outMaterials = nullptr) const override; - - // See Shape - virtual void SaveBinaryState(StreamOut &inStream) const override; - - // See Shape::GetStats - virtual Stats GetStats() const override { return Stats(sizeof(*this), 0); } - - // See Shape::GetVolume - virtual float GetVolume() const override { return 2.0f * JPH_PI * mHalfHeight * Square(mRadius); } - - /// Get the convex radius of this cylinder - float GetConvexRadius() const { return mConvexRadius; } - - // See Shape::IsValidScale - virtual bool IsValidScale(Vec3Arg inScale) const override; - - // Register shape functions with the registry - static void sRegister(); - -protected: - // See: Shape::RestoreBinaryState - virtual void RestoreBinaryState(StreamIn &inStream) override; - -private: - // Class for GetSupportFunction - class Cylinder; - - float mHalfHeight = 0.0f; - float mRadius = 0.0f; - float mConvexRadius = 0.0f; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/DecoratedShape.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/DecoratedShape.cpp deleted file mode 100644 index 339f7836376..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/DecoratedShape.cpp +++ /dev/null @@ -1,87 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include - -JPH_NAMESPACE_BEGIN - -JPH_IMPLEMENT_SERIALIZABLE_ABSTRACT(DecoratedShapeSettings) -{ - JPH_ADD_BASE_CLASS(DecoratedShapeSettings, ShapeSettings) - - JPH_ADD_ATTRIBUTE(DecoratedShapeSettings, mInnerShape) -} - -DecoratedShape::DecoratedShape(EShapeSubType inSubType, const DecoratedShapeSettings &inSettings, ShapeResult &outResult) : - Shape(EShapeType::Decorated, inSubType, inSettings, outResult) -{ - // Check that there's a shape - if (inSettings.mInnerShape == nullptr && inSettings.mInnerShapePtr == nullptr) - { - outResult.SetError("Inner shape is null!"); - return; - } - - if (inSettings.mInnerShapePtr != nullptr) - { - // Use provided shape - mInnerShape = inSettings.mInnerShapePtr; - } - else - { - // Create child shape - ShapeResult child_result = inSettings.mInnerShape->Create(); - if (!child_result.IsValid()) - { - outResult = child_result; - return; - } - mInnerShape = child_result.Get(); - } -} - -const PhysicsMaterial *DecoratedShape::GetMaterial(const SubShapeID &inSubShapeID) const -{ - return mInnerShape->GetMaterial(inSubShapeID); -} - -void DecoratedShape::GetSupportingFace(const SubShapeID &inSubShapeID, Vec3Arg inDirection, Vec3Arg inScale, Mat44Arg inCenterOfMassTransform, SupportingFace &outVertices) const -{ - mInnerShape->GetSupportingFace(inSubShapeID, inDirection, inScale, inCenterOfMassTransform, outVertices); -} - -uint64 DecoratedShape::GetSubShapeUserData(const SubShapeID &inSubShapeID) const -{ - return mInnerShape->GetSubShapeUserData(inSubShapeID); -} - -void DecoratedShape::SaveSubShapeState(ShapeList &outSubShapes) const -{ - outSubShapes.clear(); - outSubShapes.push_back(mInnerShape); -} - -void DecoratedShape::RestoreSubShapeState(const ShapeRefC *inSubShapes, uint inNumShapes) -{ - JPH_ASSERT(inNumShapes == 1); - mInnerShape = inSubShapes[0]; -} - -Shape::Stats DecoratedShape::GetStatsRecursive(VisitedShapes &ioVisitedShapes) const -{ - // Get own stats - Stats stats = Shape::GetStatsRecursive(ioVisitedShapes); - - // Add child stats - Stats child_stats = mInnerShape->GetStatsRecursive(ioVisitedShapes); - stats.mSizeBytes += child_stats.mSizeBytes; - stats.mNumTriangles += child_stats.mNumTriangles; - - return stats; -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/DecoratedShape.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/DecoratedShape.h deleted file mode 100644 index 5c97dcebc9d..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/DecoratedShape.h +++ /dev/null @@ -1,70 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -/// Class that constructs a DecoratedShape -class JPH_EXPORT DecoratedShapeSettings : public ShapeSettings -{ - JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, DecoratedShapeSettings) - - /// Default constructor for deserialization - DecoratedShapeSettings() = default; - - /// Constructor that decorates another shape - explicit DecoratedShapeSettings(const ShapeSettings *inShape) : mInnerShape(inShape) { } - explicit DecoratedShapeSettings(const Shape *inShape) : mInnerShapePtr(inShape) { } - - RefConst mInnerShape; ///< Sub shape (either this or mShapePtr needs to be filled up) - RefConst mInnerShapePtr; ///< Sub shape (either this or mShape needs to be filled up) -}; - -/// Base class for shapes that decorate another shape with extra functionality (e.g. scale, translation etc.) -class JPH_EXPORT DecoratedShape : public Shape -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Constructor - explicit DecoratedShape(EShapeSubType inSubType) : Shape(EShapeType::Decorated, inSubType) { } - DecoratedShape(EShapeSubType inSubType, const Shape *inInnerShape) : Shape(EShapeType::Decorated, inSubType), mInnerShape(inInnerShape) { } - DecoratedShape(EShapeSubType inSubType, const DecoratedShapeSettings &inSettings, ShapeResult &outResult); - - /// Access to the decorated inner shape - const Shape * GetInnerShape() const { return mInnerShape; } - - // See Shape::MustBeStatic - virtual bool MustBeStatic() const override { return mInnerShape->MustBeStatic(); } - - // See Shape::GetCenterOfMass - virtual Vec3 GetCenterOfMass() const override { return mInnerShape->GetCenterOfMass(); } - - // See Shape::GetSubShapeIDBitsRecursive - virtual uint GetSubShapeIDBitsRecursive() const override { return mInnerShape->GetSubShapeIDBitsRecursive(); } - - // See Shape::GetMaterial - virtual const PhysicsMaterial * GetMaterial(const SubShapeID &inSubShapeID) const override; - - // See Shape::GetSupportingFace - virtual void GetSupportingFace(const SubShapeID &inSubShapeID, Vec3Arg inDirection, Vec3Arg inScale, Mat44Arg inCenterOfMassTransform, SupportingFace &outVertices) const override; - - // See Shape::GetSubShapeUserData - virtual uint64 GetSubShapeUserData(const SubShapeID &inSubShapeID) const override; - - // See Shape - virtual void SaveSubShapeState(ShapeList &outSubShapes) const override; - virtual void RestoreSubShapeState(const ShapeRefC *inSubShapes, uint inNumShapes) override; - - // See Shape::GetStatsRecursive - virtual Stats GetStatsRecursive(VisitedShapes &ioVisitedShapes) const override; - -protected: - RefConst mInnerShape; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/GetTrianglesContext.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/GetTrianglesContext.h deleted file mode 100644 index c594ece6486..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/GetTrianglesContext.h +++ /dev/null @@ -1,244 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -class PhysicsMaterial; - -/// Implementation of GetTrianglesStart/Next that uses a fixed list of vertices for the triangles. These are transformed into world space when getting the triangles. -class GetTrianglesContextVertexList -{ -public: - /// Constructor, to be called in GetTrianglesStart - GetTrianglesContextVertexList(Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale, Mat44Arg inLocalTransform, const Vec3 *inTriangleVertices, size_t inNumTriangleVertices, const PhysicsMaterial *inMaterial) : - mLocalToWorld(Mat44::sRotationTranslation(inRotation, inPositionCOM) * Mat44::sScale(inScale) * inLocalTransform), - mTriangleVertices(inTriangleVertices), - mNumTriangleVertices(inNumTriangleVertices), - mMaterial(inMaterial), - mIsInsideOut(ScaleHelpers::IsInsideOut(inScale)) - { - static_assert(sizeof(GetTrianglesContextVertexList) <= sizeof(Shape::GetTrianglesContext), "GetTrianglesContext too small"); - JPH_ASSERT(IsAligned(this, alignof(GetTrianglesContextVertexList))); - JPH_ASSERT(inNumTriangleVertices % 3 == 0); - } - - /// @see Shape::GetTrianglesNext - int GetTrianglesNext(int inMaxTrianglesRequested, Float3 *outTriangleVertices, const PhysicsMaterial **outMaterials) - { - JPH_ASSERT(inMaxTrianglesRequested >= Shape::cGetTrianglesMinTrianglesRequested); - - int total_num_vertices = min(inMaxTrianglesRequested * 3, int(mNumTriangleVertices - mCurrentVertex)); - - if (mIsInsideOut) - { - // Store triangles flipped - for (const Vec3 *v = mTriangleVertices + mCurrentVertex, *v_end = v + total_num_vertices; v < v_end; v += 3) - { - (mLocalToWorld * v[0]).StoreFloat3(outTriangleVertices++); - (mLocalToWorld * v[2]).StoreFloat3(outTriangleVertices++); - (mLocalToWorld * v[1]).StoreFloat3(outTriangleVertices++); - } - } - else - { - // Store triangles - for (const Vec3 *v = mTriangleVertices + mCurrentVertex, *v_end = v + total_num_vertices; v < v_end; v += 3) - { - (mLocalToWorld * v[0]).StoreFloat3(outTriangleVertices++); - (mLocalToWorld * v[1]).StoreFloat3(outTriangleVertices++); - (mLocalToWorld * v[2]).StoreFloat3(outTriangleVertices++); - } - } - - // Update the current vertex to point to the next vertex to get - mCurrentVertex += total_num_vertices; - int total_num_triangles = total_num_vertices / 3; - - // Store materials - if (outMaterials != nullptr) - for (const PhysicsMaterial **m = outMaterials, **m_end = outMaterials + total_num_triangles; m < m_end; ++m) - *m = mMaterial; - - return total_num_triangles; - } - - /// Helper function that creates a vertex list of a half unit sphere (top part) - static void sCreateHalfUnitSphereTop(std::vector &ioVertices, int inDetailLevel) - { - sCreateUnitSphereHelper(ioVertices, Vec3::sAxisX(), Vec3::sAxisY(), Vec3::sAxisZ(), inDetailLevel); - sCreateUnitSphereHelper(ioVertices, Vec3::sAxisY(), -Vec3::sAxisX(), Vec3::sAxisZ(), inDetailLevel); - sCreateUnitSphereHelper(ioVertices, Vec3::sAxisY(), Vec3::sAxisX(), -Vec3::sAxisZ(), inDetailLevel); - sCreateUnitSphereHelper(ioVertices, -Vec3::sAxisX(), Vec3::sAxisY(), -Vec3::sAxisZ(), inDetailLevel); - } - - /// Helper function that creates a vertex list of a half unit sphere (bottom part) - static void sCreateHalfUnitSphereBottom(std::vector &ioVertices, int inDetailLevel) - { - sCreateUnitSphereHelper(ioVertices, -Vec3::sAxisX(), -Vec3::sAxisY(), Vec3::sAxisZ(), inDetailLevel); - sCreateUnitSphereHelper(ioVertices, -Vec3::sAxisY(), Vec3::sAxisX(), Vec3::sAxisZ(), inDetailLevel); - sCreateUnitSphereHelper(ioVertices, Vec3::sAxisX(), -Vec3::sAxisY(), -Vec3::sAxisZ(), inDetailLevel); - sCreateUnitSphereHelper(ioVertices, -Vec3::sAxisY(), -Vec3::sAxisX(), -Vec3::sAxisZ(), inDetailLevel); - } - - /// Helper function that creates an open cylinder of half height 1 and radius 1 - static void sCreateUnitOpenCylinder(std::vector &ioVertices, int inDetailLevel) - { - const Vec3 bottom_offset(0.0f, -2.0f, 0.0f); - int num_verts = 4 * (1 << inDetailLevel); - for (int i = 0; i < num_verts; ++i) - { - float angle1 = 2.0f * JPH_PI * (float(i) / num_verts); - float angle2 = 2.0f * JPH_PI * (float(i + 1) / num_verts); - - Vec3 t1(Sin(angle1), 1.0f, Cos(angle1)); - Vec3 t2(Sin(angle2), 1.0f, Cos(angle2)); - Vec3 b1 = t1 + bottom_offset; - Vec3 b2 = t2 + bottom_offset; - - ioVertices.push_back(t1); - ioVertices.push_back(b1); - ioVertices.push_back(t2); - - ioVertices.push_back(t2); - ioVertices.push_back(b1); - ioVertices.push_back(b2); - } - } - -private: - /// Recursive helper function for creating a sphere - static void sCreateUnitSphereHelper(std::vector &ioVertices, Vec3Arg inV1, Vec3Arg inV2, Vec3Arg inV3, int inLevel) - { - Vec3 center1 = (inV1 + inV2).Normalized(); - Vec3 center2 = (inV2 + inV3).Normalized(); - Vec3 center3 = (inV3 + inV1).Normalized(); - - if (inLevel > 0) - { - int new_level = inLevel - 1; - sCreateUnitSphereHelper(ioVertices, inV1, center1, center3, new_level); - sCreateUnitSphereHelper(ioVertices, center1, center2, center3, new_level); - sCreateUnitSphereHelper(ioVertices, center1, inV2, center2, new_level); - sCreateUnitSphereHelper(ioVertices, center3, center2, inV3, new_level); - } - else - { - ioVertices.push_back(inV1); - ioVertices.push_back(inV2); - ioVertices.push_back(inV3); - } - } - - Mat44 mLocalToWorld; - const Vec3 * mTriangleVertices; - size_t mNumTriangleVertices; - size_t mCurrentVertex = 0; - const PhysicsMaterial * mMaterial; - bool mIsInsideOut; -}; - -/// Implementation of GetTrianglesStart/Next that uses a multiple fixed lists of vertices for the triangles. These are transformed into world space when getting the triangles. -class GetTrianglesContextMultiVertexList -{ -public: - /// Constructor, to be called in GetTrianglesStart - GetTrianglesContextMultiVertexList(bool inIsInsideOut, const PhysicsMaterial *inMaterial) : - mMaterial(inMaterial), - mIsInsideOut(inIsInsideOut) - { - static_assert(sizeof(GetTrianglesContextMultiVertexList) <= sizeof(Shape::GetTrianglesContext), "GetTrianglesContext too small"); - JPH_ASSERT(IsAligned(this, alignof(GetTrianglesContextMultiVertexList))); - } - - /// Add a mesh part and its transform - void AddPart(Mat44Arg inLocalToWorld, const Vec3 *inTriangleVertices, size_t inNumTriangleVertices) - { - JPH_ASSERT(inNumTriangleVertices % 3 == 0); - - mParts.push_back({ inLocalToWorld, inTriangleVertices, inNumTriangleVertices }); - } - - /// @see Shape::GetTrianglesNext - int GetTrianglesNext(int inMaxTrianglesRequested, Float3 *outTriangleVertices, const PhysicsMaterial **outMaterials) - { - JPH_ASSERT(inMaxTrianglesRequested >= Shape::cGetTrianglesMinTrianglesRequested); - - int total_num_vertices = 0; - int max_vertices_requested = inMaxTrianglesRequested * 3; - - // Loop over parts - for (; mCurrentPart < mParts.size(); ++mCurrentPart) - { - const Part &part = mParts[mCurrentPart]; - - // Calculate how many vertices to take from this part - int part_num_vertices = min(max_vertices_requested, int(part.mNumTriangleVertices - mCurrentVertex)); - if (part_num_vertices == 0) - break; - - max_vertices_requested -= part_num_vertices; - total_num_vertices += part_num_vertices; - - if (mIsInsideOut) - { - // Store triangles flipped - for (const Vec3 *v = part.mTriangleVertices + mCurrentVertex, *v_end = v + part_num_vertices; v < v_end; v += 3) - { - (part.mLocalToWorld * v[0]).StoreFloat3(outTriangleVertices++); - (part.mLocalToWorld * v[2]).StoreFloat3(outTriangleVertices++); - (part.mLocalToWorld * v[1]).StoreFloat3(outTriangleVertices++); - } - } - else - { - // Store triangles - for (const Vec3 *v = part.mTriangleVertices + mCurrentVertex, *v_end = v + part_num_vertices; v < v_end; v += 3) - { - (part.mLocalToWorld * v[0]).StoreFloat3(outTriangleVertices++); - (part.mLocalToWorld * v[1]).StoreFloat3(outTriangleVertices++); - (part.mLocalToWorld * v[2]).StoreFloat3(outTriangleVertices++); - } - } - - // Update the current vertex to point to the next vertex to get - mCurrentVertex += part_num_vertices; - - // Check if we completed this part - if (mCurrentVertex < part.mNumTriangleVertices) - break; - - // Reset current vertex for the next part - mCurrentVertex = 0; - } - - int total_num_triangles = total_num_vertices / 3; - - // Store materials - if (outMaterials != nullptr) - for (const PhysicsMaterial **m = outMaterials, **m_end = outMaterials + total_num_triangles; m < m_end; ++m) - *m = mMaterial; - - return total_num_triangles; - } - -private: - struct Part - { - Mat44 mLocalToWorld; - const Vec3 * mTriangleVertices; - size_t mNumTriangleVertices; - }; - - StaticArray mParts; - uint mCurrentPart = 0; - size_t mCurrentVertex = 0; - const PhysicsMaterial * mMaterial; - bool mIsInsideOut; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/HeightFieldShape.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/HeightFieldShape.cpp deleted file mode 100644 index 7a0352f60be..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/HeightFieldShape.cpp +++ /dev/null @@ -1,2624 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -//#define JPH_DEBUG_HEIGHT_FIELD - -JPH_NAMESPACE_BEGIN - -#ifdef JPH_DEBUG_RENDERER -bool HeightFieldShape::sDrawTriangleOutlines = false; -#endif // JPH_DEBUG_RENDERER - -using namespace HeightFieldShapeConstants; - -JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(HeightFieldShapeSettings) -{ - JPH_ADD_BASE_CLASS(HeightFieldShapeSettings, ShapeSettings) - - JPH_ADD_ATTRIBUTE(HeightFieldShapeSettings, mHeightSamples) - JPH_ADD_ATTRIBUTE(HeightFieldShapeSettings, mOffset) - JPH_ADD_ATTRIBUTE(HeightFieldShapeSettings, mScale) - JPH_ADD_ATTRIBUTE(HeightFieldShapeSettings, mMinHeightValue) - JPH_ADD_ATTRIBUTE(HeightFieldShapeSettings, mMaxHeightValue) - JPH_ADD_ATTRIBUTE(HeightFieldShapeSettings, mSampleCount) - JPH_ADD_ATTRIBUTE(HeightFieldShapeSettings, mBlockSize) - JPH_ADD_ATTRIBUTE(HeightFieldShapeSettings, mBitsPerSample) - JPH_ADD_ATTRIBUTE(HeightFieldShapeSettings, mMaterialIndices) - JPH_ADD_ATTRIBUTE(HeightFieldShapeSettings, mMaterials) - JPH_ADD_ATTRIBUTE(HeightFieldShapeSettings, mActiveEdgeCosThresholdAngle) -} - -const uint HeightFieldShape::sGridOffsets[] = -{ - 0, // level: 0, max x/y: 0, offset: 0 - 1, // level: 1, max x/y: 1, offset: 1 - 5, // level: 2, max x/y: 3, offset: 1 + 4 - 21, // level: 3, max x/y: 7, offset: 1 + 4 + 16 - 85, // level: 4, max x/y: 15, offset: 1 + 4 + 16 + 64 - 341, // level: 5, max x/y: 31, offset: 1 + 4 + 16 + 64 + 256 - 1365, // level: 6, max x/y: 63, offset: 1 + 4 + 16 + 64 + 256 + 1024 - 5461, // level: 7, max x/y: 127, offset: 1 + 4 + 16 + 64 + 256 + 1024 + 4096 - 21845, // level: 8, max x/y: 255, offset: 1 + 4 + 16 + 64 + 256 + 1024 + 4096 + ... - 87381, // level: 9, max x/y: 511, offset: 1 + 4 + 16 + 64 + 256 + 1024 + 4096 + ... - 349525, // level: 10, max x/y: 1023, offset: 1 + 4 + 16 + 64 + 256 + 1024 + 4096 + ... - 1398101, // level: 11, max x/y: 2047, offset: 1 + 4 + 16 + 64 + 256 + 1024 + 4096 + ... - 5592405, // level: 12, max x/y: 4095, offset: 1 + 4 + 16 + 64 + 256 + 1024 + 4096 + ... - 22369621, // level: 13, max x/y: 8191, offset: 1 + 4 + 16 + 64 + 256 + 1024 + 4096 + ... - 89478485, // level: 14, max x/y: 16383, offset: 1 + 4 + 16 + 64 + 256 + 1024 + 4096 + ... -}; - -HeightFieldShapeSettings::HeightFieldShapeSettings(const float *inSamples, Vec3Arg inOffset, Vec3Arg inScale, uint32 inSampleCount, const uint8 *inMaterialIndices, const PhysicsMaterialList &inMaterialList) : - mOffset(inOffset), - mScale(inScale), - mSampleCount(inSampleCount) -{ - mHeightSamples.resize(inSampleCount * inSampleCount); - memcpy(&mHeightSamples[0], inSamples, inSampleCount * inSampleCount * sizeof(float)); - - if (!inMaterialList.empty() && inMaterialIndices != nullptr) - { - mMaterialIndices.resize(Square(inSampleCount - 1)); - memcpy(&mMaterialIndices[0], inMaterialIndices, Square(inSampleCount - 1) * sizeof(uint8)); - mMaterials = inMaterialList; - } - else - { - JPH_ASSERT(inMaterialList.empty()); - JPH_ASSERT(inMaterialIndices == nullptr); - } -} - -ShapeSettings::ShapeResult HeightFieldShapeSettings::Create() const -{ - if (mCachedResult.IsEmpty()) - Ref shape = new HeightFieldShape(*this, mCachedResult); - return mCachedResult; -} - -void HeightFieldShapeSettings::DetermineMinAndMaxSample(float &outMinValue, float &outMaxValue, float &outQuantizationScale) const -{ - // Determine min and max value - outMinValue = mMinHeightValue; - outMaxValue = mMaxHeightValue; - for (float h : mHeightSamples) - if (h != cNoCollisionValue) - { - outMinValue = min(outMinValue, h); - outMaxValue = max(outMaxValue, h); - } - - // Prevent dividing by zero by setting a minimal height difference - float height_diff = max(outMaxValue - outMinValue, 1.0e-6f); - - // Calculate the scale factor to quantize to 16 bits - outQuantizationScale = float(cMaxHeightValue16) / height_diff; -} - -uint32 HeightFieldShapeSettings::CalculateBitsPerSampleForError(float inMaxError) const -{ - // Start with 1 bit per sample - uint32 bits_per_sample = 1; - - // Determine total range - float min_value, max_value, scale; - DetermineMinAndMaxSample(min_value, max_value, scale); - if (min_value < max_value) - { - // Loop over all blocks - for (uint y = 0; y < mSampleCount; y += mBlockSize) - for (uint x = 0; x < mSampleCount; x += mBlockSize) - { - // Determine min and max block value + take 1 sample border just like we do while building the hierarchical grids - float block_min_value = FLT_MAX, block_max_value = -FLT_MAX; - for (uint bx = x; bx < min(x + mBlockSize + 1, mSampleCount); ++bx) - for (uint by = y; by < min(y + mBlockSize + 1, mSampleCount); ++by) - { - float h = mHeightSamples[by * mSampleCount + bx]; - if (h != cNoCollisionValue) - { - block_min_value = min(block_min_value, h); - block_max_value = max(block_max_value, h); - } - } - - if (block_min_value < block_max_value) - { - // Quantize then dequantize block min/max value - block_min_value = min_value + floor((block_min_value - min_value) * scale) / scale; - block_max_value = min_value + ceil((block_max_value - min_value) * scale) / scale; - float block_height = block_max_value - block_min_value; - - // Loop over the block again - for (uint bx = x; bx < x + mBlockSize; ++bx) - for (uint by = y; by < y + mBlockSize; ++by) - { - // Get the height - float height = mHeightSamples[by * mSampleCount + bx]; - if (height != cNoCollisionValue) - { - for (;;) - { - // Determine bitmask for sample - uint32 sample_mask = (1 << bits_per_sample) - 1; - - // Quantize - float quantized_height = floor((height - block_min_value) * float(sample_mask) / block_height); - quantized_height = Clamp(quantized_height, 0.0f, float(sample_mask - 1)); - - // Dequantize and check error - float dequantized_height = block_min_value + (quantized_height + 0.5f) * block_height / float(sample_mask); - if (abs(dequantized_height - height) <= inMaxError) - break; - - // Not accurate enough, increase bits per sample - bits_per_sample++; - - // Don't go above 8 bits per sample - if (bits_per_sample == 8) - return bits_per_sample; - } - } - } - } - } - - } - - return bits_per_sample; -} - -void HeightFieldShape::CalculateActiveEdges(uint inX, uint inY, uint inSizeX, uint inSizeY, const float *inHeights, uint inHeightsStartX, uint inHeightsStartY, uint inHeightsStride, float inHeightsScale, float inActiveEdgeCosThresholdAngle, TempAllocator &inAllocator) -{ - // Allocate temporary buffer for normals - uint normals_size = 2 * inSizeX * inSizeY * sizeof(Vec3); - Vec3 *normals = (Vec3 *)inAllocator.Allocate(normals_size); - - // Calculate triangle normals and make normals zero for triangles that are missing - Vec3 *out_normal = normals; - for (uint y = 0; y < inSizeY; ++y) - for (uint x = 0; x < inSizeX; ++x) - { - // Get height on diagonal - const float *height_samples = inHeights + (inY - inHeightsStartY + y) * inHeightsStride + (inX - inHeightsStartX + x); - float x1y1_h = height_samples[0]; - float x2y2_h = height_samples[inHeightsStride + 1]; - if (x1y1_h != cNoCollisionValue && x2y2_h != cNoCollisionValue) - { - // Calculate normal for lower left triangle (e.g. T1A) - float x1y2_h = height_samples[inHeightsStride]; - if (x1y2_h != cNoCollisionValue) - { - Vec3 x2y2_minus_x1y2(mScale.GetX(), inHeightsScale * (x2y2_h - x1y2_h), 0); - Vec3 x1y1_minus_x1y2(0, inHeightsScale * (x1y1_h - x1y2_h), -mScale.GetZ()); - out_normal[0] = x2y2_minus_x1y2.Cross(x1y1_minus_x1y2).Normalized(); - } - else - out_normal[0] = Vec3::sZero(); - - // Calculate normal for upper right triangle (e.g. T1B) - float x2y1_h = height_samples[1]; - if (x2y1_h != cNoCollisionValue) - { - Vec3 x1y1_minus_x2y1(-mScale.GetX(), inHeightsScale * (x1y1_h - x2y1_h), 0); - Vec3 x2y2_minus_x2y1(0, inHeightsScale * (x2y2_h - x2y1_h), mScale.GetZ()); - out_normal[1] = x1y1_minus_x2y1.Cross(x2y2_minus_x2y1).Normalized(); - } - else - out_normal[1] = Vec3::sZero(); - } - else - { - out_normal[0] = Vec3::sZero(); - out_normal[1] = Vec3::sZero(); - } - - out_normal += 2; - } - - // Calculate active edges - const Vec3 *in_normal = normals; - uint global_bit_pos = 3 * (inY * (mSampleCount - 1) + inX); - for (uint y = 0; y < inSizeY; ++y) - { - for (uint x = 0; x < inSizeX; ++x) - { - // Get vertex heights - const float *height_samples = inHeights + (inY - inHeightsStartY + y) * inHeightsStride + (inX - inHeightsStartX + x); - float x1y1_h = height_samples[0]; - float x1y2_h = height_samples[inHeightsStride]; - float x2y2_h = height_samples[inHeightsStride + 1]; - bool x1y1_valid = x1y1_h != cNoCollisionValue; - bool x1y2_valid = x1y2_h != cNoCollisionValue; - bool x2y2_valid = x2y2_h != cNoCollisionValue; - - // Calculate the edge flags (3 bits) - // See diagram in the next function for the edge numbering - uint16 edge_mask = 0b111; - uint16 edge_flags = 0; - - // Edge 0 - if (x == 0) - edge_mask &= 0b110; // We need normal x - 1 which we didn't calculate, don't update this edge - else if (x1y1_valid && x1y2_valid) - { - Vec3 edge0_direction(0, inHeightsScale * (x1y2_h - x1y1_h), mScale.GetZ()); - if (ActiveEdges::IsEdgeActive(in_normal[0], in_normal[-1], edge0_direction, inActiveEdgeCosThresholdAngle)) - edge_flags |= 0b001; - } - - // Edge 1 - if (y == inSizeY - 1) - edge_mask &= 0b101; // We need normal y + 1 which we didn't calculate, don't update this edge - else if (x1y2_valid && x2y2_valid) - { - Vec3 edge1_direction(mScale.GetX(), inHeightsScale * (x2y2_h - x1y2_h), 0); - if (ActiveEdges::IsEdgeActive(in_normal[0], in_normal[2 * inSizeX + 1], edge1_direction, inActiveEdgeCosThresholdAngle)) - edge_flags |= 0b010; - } - - // Edge 2 - if (x1y1_valid && x2y2_valid) - { - Vec3 edge2_direction(-mScale.GetX(), inHeightsScale * (x1y1_h - x2y2_h), -mScale.GetZ()); - if (ActiveEdges::IsEdgeActive(in_normal[0], in_normal[1], edge2_direction, inActiveEdgeCosThresholdAngle)) - edge_flags |= 0b100; - } - - // Store the edge flags in the array - uint byte_pos = global_bit_pos >> 3; - uint bit_pos = global_bit_pos & 0b111; - uint8 *edge_flags_ptr = &mActiveEdges[byte_pos]; - uint16 combined_edge_flags = uint16(edge_flags_ptr[0]) | uint16(uint16(edge_flags_ptr[1]) << 8); - combined_edge_flags &= ~(edge_mask << bit_pos); - combined_edge_flags |= edge_flags << bit_pos; - edge_flags_ptr[0] = uint8(combined_edge_flags); - edge_flags_ptr[1] = uint8(combined_edge_flags >> 8); - - in_normal += 2; - global_bit_pos += 3; - } - - global_bit_pos += 3 * (mSampleCount - 1 - inSizeX); - } - - // Free temporary buffer for normals - inAllocator.Free(normals, normals_size); -} - -void HeightFieldShape::CalculateActiveEdges(const HeightFieldShapeSettings &inSettings) -{ - /* - Store active edges. The triangles are organized like this: - x ---> - - y + + - | \ T1B | \ T2B - | e0 e2 | \ - | | T1A \ | T2A \ - V +--e1---+-------+ - | \ T3B | \ T4B - | \ | \ - | T3A \ | T4A \ - +-------+-------+ - We store active edges e0 .. e2 as bits 0 .. 2. - We store triangles horizontally then vertically (order T1A, T2A, T3A and T4A). - The top edge and right edge of the heightfield are always active so we do not need to store them, - therefore we only need to store (mSampleCount - 1)^2 * 3-bit - The triangles T1B, T2B, T3B and T4B do not need to be stored, their active edges can be constructed from adjacent triangles. - Add 1 byte padding so we can always read 1 uint16 to get the bits that cross an 8 bit boundary - */ - mActiveEdges.resize((Square(mSampleCount - 1) * 3 + 7) / 8 + 1); - - // Make all edges active (if mSampleCount is bigger than inSettings.mSampleCount we need to fill up the padding, - // also edges at x = 0 and y = inSettings.mSampleCount - 1 are not updated) - memset(mActiveEdges.data(), 0xff, mActiveEdges.size()); - - // Now clear the edges that are not active - TempAllocatorMalloc allocator; - CalculateActiveEdges(0, 0, inSettings.mSampleCount - 1, inSettings.mSampleCount - 1, inSettings.mHeightSamples.data(), 0, 0, inSettings.mSampleCount, inSettings.mScale.GetY(), inSettings.mActiveEdgeCosThresholdAngle, allocator); -} - -void HeightFieldShape::StoreMaterialIndices(const HeightFieldShapeSettings &inSettings) -{ - // We need to account for any rounding of the sample count to the nearest block size - uint in_count_min_1 = inSettings.mSampleCount - 1; - uint out_count_min_1 = mSampleCount - 1; - - mNumBitsPerMaterialIndex = 32 - CountLeadingZeros((uint32)mMaterials.size() - 1); - mMaterialIndices.resize(((Square(out_count_min_1) * mNumBitsPerMaterialIndex + 7) >> 3) + 1); // Add 1 byte so we don't read out of bounds when reading an uint16 - - for (uint y = 0; y < out_count_min_1; ++y) - for (uint x = 0; x < out_count_min_1; ++x) - { - // Read material - uint16 material_index = x < in_count_min_1 && y < in_count_min_1? uint16(inSettings.mMaterialIndices[x + y * in_count_min_1]) : 0; - - // Calculate byte and bit position where the material index needs to go - uint sample_pos = x + y * out_count_min_1; - uint bit_pos = sample_pos * mNumBitsPerMaterialIndex; - uint byte_pos = bit_pos >> 3; - bit_pos &= 0b111; - - // Write the material index - material_index <<= bit_pos; - JPH_ASSERT(byte_pos + 1 < mMaterialIndices.size()); - mMaterialIndices[byte_pos] |= uint8(material_index); - mMaterialIndices[byte_pos + 1] |= uint8(material_index >> 8); - } -} - -void HeightFieldShape::CacheValues() -{ - mSampleMask = uint8((uint32(1) << mBitsPerSample) - 1); -} - -HeightFieldShape::HeightFieldShape(const HeightFieldShapeSettings &inSettings, ShapeResult &outResult) : - Shape(EShapeType::HeightField, EShapeSubType::HeightField, inSettings, outResult), - mOffset(inSettings.mOffset), - mScale(inSettings.mScale), - mSampleCount(((inSettings.mSampleCount + inSettings.mBlockSize - 1) / inSettings.mBlockSize) * inSettings.mBlockSize), // Round sample count to nearest block size - mBlockSize(inSettings.mBlockSize), - mBitsPerSample(uint8(inSettings.mBitsPerSample)), - mMaterials(inSettings.mMaterials) -{ - CacheValues(); - - // Check block size - if (mBlockSize < 2 || mBlockSize > 8) - { - outResult.SetError("HeightFieldShape: Block size must be in the range [2, 8]!"); - return; - } - - // Check bits per sample - if (inSettings.mBitsPerSample < 1 || inSettings.mBitsPerSample > 8) - { - outResult.SetError("HeightFieldShape: Bits per sample must be in the range [1, 8]!"); - return; - } - - // We stop at mBlockSize x mBlockSize height sample blocks - uint num_blocks = GetNumBlocks(); - - // We want at least 1 grid layer - if (num_blocks < 2) - { - outResult.SetError("HeightFieldShape: Sample count too low!"); - return; - } - - // Check that we don't overflow our 32 bit 'properties' - if (num_blocks > (1 << cNumBitsXY)) - { - outResult.SetError("HeightFieldShape: Sample count too high!"); - return; - } - - // Check if we're not exceeding the amount of sub shape id bits - if (GetSubShapeIDBitsRecursive() > SubShapeID::MaxBits) - { - outResult.SetError("HeightFieldShape: Size exceeds the amount of available sub shape ID bits!"); - return; - } - - if (!mMaterials.empty()) - { - // Validate materials - if (mMaterials.size() > 256) - { - outResult.SetError("Supporting max 256 materials per height field"); - return; - } - for (uint8 s : inSettings.mMaterialIndices) - if (s >= mMaterials.size()) - { - outResult.SetError(StringFormat("Material %u is beyond material list (size: %u)", s, (uint)mMaterials.size())); - return; - } - } - else - { - // No materials assigned, validate that no materials have been specified - if (!inSettings.mMaterialIndices.empty()) - { - outResult.SetError("No materials present, mMaterialIndices should be empty"); - return; - } - } - - // Determine range - float min_value, max_value, scale; - inSettings.DetermineMinAndMaxSample(min_value, max_value, scale); - if (min_value > max_value) - { - // If there is no collision with this heightmap, leave everything empty - mMaterials.clear(); - outResult.Set(this); - return; - } - - // Quantize to uint16 - Array quantized_samples; - quantized_samples.reserve(mSampleCount * mSampleCount); - for (uint y = 0; y < inSettings.mSampleCount; ++y) - { - for (uint x = 0; x < inSettings.mSampleCount; ++x) - { - float h = inSettings.mHeightSamples[x + y * inSettings.mSampleCount]; - if (h == cNoCollisionValue) - { - quantized_samples.push_back(cNoCollisionValue16); - } - else - { - // Floor the quantized height to get a lower bound for the quantized value - int quantized_height = (int)floor(scale * (h - min_value)); - - // Ensure that the height says below the max height value so we can safely add 1 to get the upper bound for the quantized value - quantized_height = Clamp(quantized_height, 0, int(cMaxHeightValue16 - 1)); - - quantized_samples.push_back(uint16(quantized_height)); - } - } - // Pad remaining columns with no collision - for (uint x = inSettings.mSampleCount; x < mSampleCount; ++x) - quantized_samples.push_back(cNoCollisionValue16); - } - // Pad remaining rows with no collision - for (uint y = inSettings.mSampleCount; y < mSampleCount; ++y) - for (uint x = 0; x < mSampleCount; ++x) - quantized_samples.push_back(cNoCollisionValue16); - - // Update offset and scale to account for the compression to uint16 - if (min_value <= max_value) // Only when there was collision - { - // In GetPosition we always add 0.5 to the quantized sample in order to reduce the average error. - // We want to be able to exactly quantize min_value (this is important in case the heightfield is entirely flat) so we subtract that value from min_value. - min_value -= 0.5f / (scale * mSampleMask); - - mOffset.SetY(mOffset.GetY() + mScale.GetY() * min_value); - } - mScale.SetY(mScale.GetY() / scale); - - // Calculate amount of grids - uint max_level = sGetMaxLevel(num_blocks); - - // Temporary data structure used during creating of a hierarchy of grids - struct Range - { - uint16 mMin; - uint16 mMax; - }; - - // Reserve size for temporary range data + reserve 1 extra for a 1x1 grid that we won't store but use for calculating the bounding box - Array> ranges; - ranges.resize(max_level + 1); - - // Calculate highest detail grid by combining mBlockSize x mBlockSize height samples - Array *cur_range_vector = &ranges.back(); - uint num_blocks_pow2 = GetNextPowerOf2(num_blocks); // We calculate the range blocks as if the heightfield was a power of 2, when we save the range blocks we'll ignore the extra samples (this makes downsampling easier) - cur_range_vector->resize(num_blocks_pow2 * num_blocks_pow2); - Range *range_dst = &cur_range_vector->front(); - for (uint y = 0; y < num_blocks_pow2; ++y) - for (uint x = 0; x < num_blocks_pow2; ++x) - { - range_dst->mMin = 0xffff; - range_dst->mMax = 0; - uint max_bx = x == num_blocks_pow2 - 1? mBlockSize : mBlockSize + 1; // for interior blocks take 1 more because the triangles connect to the next block so we must include their height too - uint max_by = y == num_blocks_pow2 - 1? mBlockSize : mBlockSize + 1; - for (uint by = 0; by < max_by; ++by) - for (uint bx = 0; bx < max_bx; ++bx) - { - uint sx = x * mBlockSize + bx; - uint sy = y * mBlockSize + by; - if (sx < mSampleCount && sy < mSampleCount) - { - uint16 h = quantized_samples[sy * mSampleCount + sx]; - if (h != cNoCollisionValue16) - { - range_dst->mMin = min(range_dst->mMin, h); - range_dst->mMax = max(range_dst->mMax, uint16(h + 1)); // Add 1 to the max so we know the real value is between mMin and mMax - } - } - } - ++range_dst; - } - - // Calculate remaining grids - for (uint n = num_blocks_pow2 >> 1; n >= 1; n >>= 1) - { - // Get source buffer - const Range *range_src = &cur_range_vector->front(); - - // Previous array element - --cur_range_vector; - - // Make space for this grid - cur_range_vector->resize(n * n); - - // Get target buffer - range_dst = &cur_range_vector->front(); - - // Combine the results of 2x2 ranges - for (uint y = 0; y < n; ++y) - for (uint x = 0; x < n; ++x) - { - range_dst->mMin = 0xffff; - range_dst->mMax = 0; - for (uint by = 0; by < 2; ++by) - for (uint bx = 0; bx < 2; ++bx) - { - const Range &r = range_src[(y * 2 + by) * n * 2 + x * 2 + bx]; - range_dst->mMin = min(range_dst->mMin, r.mMin); - range_dst->mMax = max(range_dst->mMax, r.mMax); - } - ++range_dst; - } - } - JPH_ASSERT(cur_range_vector == &ranges.front()); - - // Store global range for bounding box calculation - mMinSample = ranges[0][0].mMin; - mMaxSample = ranges[0][0].mMax; - -#ifdef JPH_ENABLE_ASSERTS - // Validate that we did not lose range along the way - uint16 minv = 0xffff, maxv = 0; - for (uint16 v : quantized_samples) - if (v != cNoCollisionValue16) - { - minv = min(minv, v); - maxv = max(maxv, uint16(v + 1)); - } - JPH_ASSERT(mMinSample == minv && mMaxSample == maxv); -#endif - - // Now erase the first element, we need a 2x2 grid to start with - ranges.erase(ranges.begin()); - - // Create blocks - uint max_stride = (num_blocks + 1) >> 1; - mRangeBlocks.reserve(sGridOffsets[ranges.size()]); - for (uint level = 0; level < ranges.size(); ++level) - { - JPH_ASSERT(mRangeBlocks.size() == sGridOffsets[level]); - - uint in_n = 1 << level; - uint out_n = min(in_n, max_stride); // At the most detailed level we store a non-power of 2 number of blocks - - for (uint y = 0; y < out_n; ++y) - for (uint x = 0; x < out_n; ++x) - { - // Convert from 2x2 Range structure to 1 RangeBlock structure - RangeBlock rb; - for (uint by = 0; by < 2; ++by) - for (uint bx = 0; bx < 2; ++bx) - { - uint src_pos = (y * 2 + by) * 2 * in_n + (x * 2 + bx); - uint dst_pos = by * 2 + bx; - rb.mMin[dst_pos] = ranges[level][src_pos].mMin; - rb.mMax[dst_pos] = ranges[level][src_pos].mMax; - } - - // Add this block - mRangeBlocks.push_back(rb); - } - } - JPH_ASSERT(mRangeBlocks.size() == sGridOffsets[ranges.size() - 1] + Square(max_stride)); - - // Quantize height samples - mHeightSamples.resize((mSampleCount * mSampleCount * inSettings.mBitsPerSample + 7) / 8 + 1); - int sample = 0; - for (uint y = 0; y < mSampleCount; ++y) - for (uint x = 0; x < mSampleCount; ++x) - { - uint32 output_value; - - float h = x < inSettings.mSampleCount && y < inSettings.mSampleCount? inSettings.mHeightSamples[x + y * inSettings.mSampleCount] : cNoCollisionValue; - if (h == cNoCollisionValue) - { - // No collision - output_value = mSampleMask; - } - else - { - // Get range of block so we know what range to compress to - uint bx = x / mBlockSize; - uint by = y / mBlockSize; - const Range &range = ranges.back()[by * num_blocks_pow2 + bx]; - JPH_ASSERT(range.mMin < range.mMax); - - // Quantize to mBitsPerSample bits, note that mSampleMask is reserved for indicating that there's no collision. - // We divide the range into mSampleMask segments and use the mid points of these segments as the quantized values. - // This results in a lower error than if we had quantized our data using the lowest point of all these segments. - float h_min = min_value + range.mMin / scale; - float h_delta = float(range.mMax - range.mMin) / scale; - float quantized_height = floor((h - h_min) * float(mSampleMask) / h_delta); - output_value = uint32(Clamp((int)quantized_height, 0, int(mSampleMask) - 1)); // mSampleMask is reserved as 'no collision value' - } - - // Store the sample - uint byte_pos = sample >> 3; - uint bit_pos = sample & 0b111; - output_value <<= bit_pos; - mHeightSamples[byte_pos] |= uint8(output_value); - mHeightSamples[byte_pos + 1] |= uint8(output_value >> 8); - sample += inSettings.mBitsPerSample; - } - - // Calculate the active edges - CalculateActiveEdges(inSettings); - - // Compress material indices - if (mMaterials.size() > 1) - StoreMaterialIndices(inSettings); - - outResult.Set(this); -} - -inline void HeightFieldShape::sGetRangeBlockOffsetAndStride(uint inNumBlocks, uint inMaxLevel, uint &outRangeBlockOffset, uint &outRangeBlockStride) -{ - outRangeBlockOffset = sGridOffsets[inMaxLevel - 1]; - outRangeBlockStride = (inNumBlocks + 1) >> 1; -} - -inline void HeightFieldShape::GetRangeBlock(uint inBlockX, uint inBlockY, uint inRangeBlockOffset, uint inRangeBlockStride, RangeBlock *&outBlock, uint &outIndexInBlock) -{ - JPH_ASSERT(inBlockX < GetNumBlocks() && inBlockY < GetNumBlocks()); - - // Convert to location of range block - uint rbx = inBlockX >> 1; - uint rby = inBlockY >> 1; - outIndexInBlock = ((inBlockY & 1) << 1) + (inBlockX & 1); - - outBlock = &mRangeBlocks[inRangeBlockOffset + rby * inRangeBlockStride + rbx]; -} - -inline void HeightFieldShape::GetBlockOffsetAndScale(uint inBlockX, uint inBlockY, uint inRangeBlockOffset, uint inRangeBlockStride, float &outBlockOffset, float &outBlockScale) const -{ - JPH_ASSERT(inBlockX < GetNumBlocks() && inBlockY < GetNumBlocks()); - - // Convert to location of range block - uint rbx = inBlockX >> 1; - uint rby = inBlockY >> 1; - uint n = ((inBlockY & 1) << 1) + (inBlockX & 1); - - // Calculate offset and scale - const RangeBlock &block = mRangeBlocks[inRangeBlockOffset + rby * inRangeBlockStride + rbx]; - outBlockOffset = float(block.mMin[n]); - outBlockScale = float(block.mMax[n] - block.mMin[n]) / float(mSampleMask); -} - -inline uint8 HeightFieldShape::GetHeightSample(uint inX, uint inY) const -{ - JPH_ASSERT(inX < mSampleCount); - JPH_ASSERT(inY < mSampleCount); - - // Determine bit position of sample - uint sample = (inY * mSampleCount + inX) * uint(mBitsPerSample); - uint byte_pos = sample >> 3; - uint bit_pos = sample & 0b111; - - // Fetch the height sample value - JPH_ASSERT(byte_pos + 1 < mHeightSamples.size()); - const uint8 *height_samples = mHeightSamples.data() + byte_pos; - uint16 height_sample = uint16(height_samples[0]) | uint16(uint16(height_samples[1]) << 8); - return uint8(height_sample >> bit_pos) & mSampleMask; -} - -inline Vec3 HeightFieldShape::GetPosition(uint inX, uint inY, float inBlockOffset, float inBlockScale, bool &outNoCollision) const -{ - // Get quantized value - uint8 height_sample = GetHeightSample(inX, inY); - outNoCollision = height_sample == mSampleMask; - - // Add 0.5 to the quantized value to minimize the error (see constructor) - return mOffset + mScale * Vec3(float(inX), inBlockOffset + (0.5f + height_sample) * inBlockScale, float(inY)); -} - -Vec3 HeightFieldShape::GetPosition(uint inX, uint inY) const -{ - // Test if there are any samples - if (mHeightSamples.empty()) - return mOffset + mScale * Vec3(float(inX), 0.0f, float(inY)); - - // Get block location - uint bx = inX / mBlockSize; - uint by = inY / mBlockSize; - - // Calculate offset and stride - uint num_blocks = GetNumBlocks(); - uint range_block_offset, range_block_stride; - sGetRangeBlockOffsetAndStride(num_blocks, sGetMaxLevel(num_blocks), range_block_offset, range_block_stride); - - float offset, scale; - GetBlockOffsetAndScale(bx, by, range_block_offset, range_block_stride, offset, scale); - - bool no_collision; - return GetPosition(inX, inY, offset, scale, no_collision); -} - -bool HeightFieldShape::IsNoCollision(uint inX, uint inY) const -{ - return mHeightSamples.empty() || GetHeightSample(inX, inY) == mSampleMask; -} - -bool HeightFieldShape::ProjectOntoSurface(Vec3Arg inLocalPosition, Vec3 &outSurfacePosition, SubShapeID &outSubShapeID) const -{ - // Check if we have collision - if (mHeightSamples.empty()) - return false; - - // Convert coordinate to integer space - Vec3 integer_space = (inLocalPosition - mOffset) / mScale; - - // Get x coordinate and fraction - float x_frac = integer_space.GetX(); - if (x_frac < 0.0f || x_frac >= mSampleCount - 1) - return false; - uint x = (uint)floor(x_frac); - x_frac -= x; - - // Get y coordinate and fraction - float y_frac = integer_space.GetZ(); - if (y_frac < 0.0f || y_frac >= mSampleCount - 1) - return false; - uint y = (uint)floor(y_frac); - y_frac -= y; - - // If one of the diagonal points doesn't have collision, we don't have a height at this location - if (IsNoCollision(x, y) || IsNoCollision(x + 1, y + 1)) - return false; - - if (y_frac >= x_frac) - { - // Left bottom triangle, test the 3rd point - if (IsNoCollision(x, y + 1)) - return false; - - // Interpolate height value - Vec3 v1 = GetPosition(x, y); - Vec3 v2 = GetPosition(x, y + 1); - Vec3 v3 = GetPosition(x + 1, y + 1); - outSurfacePosition = v1 + y_frac * (v2 - v1) + x_frac * (v3 - v2); - SubShapeIDCreator creator; - outSubShapeID = EncodeSubShapeID(creator, x, y, 0); - return true; - } - else - { - // Right top triangle, test the third point - if (IsNoCollision(x + 1, y)) - return false; - - // Interpolate height value - Vec3 v1 = GetPosition(x, y); - Vec3 v2 = GetPosition(x + 1, y + 1); - Vec3 v3 = GetPosition(x + 1, y); - outSurfacePosition = v1 + y_frac * (v2 - v3) + x_frac * (v3 - v1); - SubShapeIDCreator creator; - outSubShapeID = EncodeSubShapeID(creator, x, y, 1); - return true; - } -} - -void HeightFieldShape::GetHeights(uint inX, uint inY, uint inSizeX, uint inSizeY, float *outHeights, uint inHeightsStride) const -{ - if (inSizeX == 0 || inSizeY == 0) - return; - - JPH_ASSERT(inX % mBlockSize == 0 && inY % mBlockSize == 0); - JPH_ASSERT(inX < mSampleCount && inY < mSampleCount); - JPH_ASSERT(inX + inSizeX <= mSampleCount && inY + inSizeY <= mSampleCount); - - // Test if there are any samples - if (mHeightSamples.empty()) - { - // No samples, return the offset - float offset = mOffset.GetY(); - for (uint y = 0; y < inSizeY; ++y, outHeights += inHeightsStride) - for (uint x = 0; x < inSizeX; ++x) - outHeights[x] = offset; - } - else - { - // Calculate offset and stride - uint num_blocks = GetNumBlocks(); - uint range_block_offset, range_block_stride; - sGetRangeBlockOffsetAndStride(num_blocks, sGetMaxLevel(num_blocks), range_block_offset, range_block_stride); - - // Loop over blocks - uint block_start_x = inX / mBlockSize; - uint block_start_y = inY / mBlockSize; - uint num_blocks_x = inSizeX / mBlockSize; - uint num_blocks_y = inSizeY / mBlockSize; - for (uint block_y = 0; block_y < num_blocks_y; ++block_y) - for (uint block_x = 0; block_x < num_blocks_x; ++block_x) - { - // Get offset and scale for block - float offset, scale; - GetBlockOffsetAndScale(block_start_x + block_x, block_start_y + block_y, range_block_offset, range_block_stride, offset, scale); - - // Adjust by global offset and scale - // Note: This is the math applied in GetPosition() written out to reduce calculations in the inner loop - scale *= mScale.GetY(); - offset = mOffset.GetY() + mScale.GetY() * offset + 0.5f * scale; - - // Loop over samples in block - for (uint sample_y = 0; sample_y < mBlockSize; ++sample_y) - for (uint sample_x = 0; sample_x < mBlockSize; ++sample_x) - { - // Calculate output coordinate - uint output_x = block_x * mBlockSize + sample_x; - uint output_y = block_y * mBlockSize + sample_y; - - // Get quantized value - uint8 height_sample = GetHeightSample(inX + output_x, inY + output_y); - - // Dequantize - float h = height_sample != mSampleMask? offset + height_sample * scale : cNoCollisionValue; - outHeights[output_y * inHeightsStride + output_x] = h; - } - } - } -} - -void HeightFieldShape::SetHeights(uint inX, uint inY, uint inSizeX, uint inSizeY, const float *inHeights, uint inHeightsStride, TempAllocator &inAllocator, float inActiveEdgeCosThresholdAngle) -{ - if (inSizeX == 0 || inSizeY == 0) - return; - - JPH_ASSERT(!mHeightSamples.empty()); - JPH_ASSERT(inX % mBlockSize == 0 && inY % mBlockSize == 0); - JPH_ASSERT(inX < mSampleCount && inY < mSampleCount); - JPH_ASSERT(inX + inSizeX <= mSampleCount && inY + inSizeY <= mSampleCount); - - // If we have a block in negative x/y direction, we will affect its range so we need to take it into account - bool need_temp_heights = false; - uint affected_x = inX; - uint affected_y = inY; - uint affected_size_x = inSizeX; - uint affected_size_y = inSizeY; - if (inX > 0) { affected_x -= mBlockSize; affected_size_x += mBlockSize; need_temp_heights = true; } - if (inY > 0) { affected_y -= mBlockSize; affected_size_y += mBlockSize; need_temp_heights = true; } - - // If we have a block in positive x/y direction, our ranges are affected by it so we need to take it into account - uint heights_size_x = affected_size_x; - uint heights_size_y = affected_size_y; - if (inX + inSizeX < mSampleCount) { heights_size_x += mBlockSize; need_temp_heights = true; } - if (inY + inSizeY < mSampleCount) { heights_size_y += mBlockSize; need_temp_heights = true; } - - // Get heights for affected area - const float *heights; - float *temp_heights; - if (need_temp_heights) - { - // Fetch the surrounding height data (note we're forced to recompress this data with a potentially different range so there will be some precision loss here) - temp_heights = (float *)inAllocator.Allocate(heights_size_x * heights_size_y * sizeof(float)); - heights = temp_heights; - - // We need to fill in the following areas: - // - // +-----------------+ - // | 2 | - // |---+---------+---| - // | | | | - // | 3 | 1 | 4 | - // | | | | - // |---+---------+---| - // | 5 | - // +-----------------+ - // - // 1. The area that is affected by the new heights (we just copy these) - // 2-5. These areas are either needed to calculate the range of the affected blocks or they need to be recompressed with a different range - uint offset_x = inX - affected_x; - uint offset_y = inY - affected_y; - - // Area 2 - GetHeights(affected_x, affected_y, heights_size_x, offset_y, temp_heights, heights_size_x); - float *area3_start = temp_heights + offset_y * heights_size_x; - - // Area 3 - GetHeights(affected_x, inY, offset_x, inSizeY, area3_start, heights_size_x); - - // Area 1 - float *area1_start = area3_start + offset_x; - for (uint y = 0; y < inSizeY; ++y, area1_start += heights_size_x, inHeights += inHeightsStride) - memcpy(area1_start, inHeights, inSizeX * sizeof(float)); - - // Area 4 - uint area4_x = inX + inSizeX; - GetHeights(area4_x, inY, affected_x + heights_size_x - area4_x, inSizeY, area3_start + area4_x - affected_x, heights_size_x); - - // Area 5 - uint area5_y = inY + inSizeY; - float *area5_start = temp_heights + (area5_y - affected_y) * heights_size_x; - GetHeights(affected_x, area5_y, heights_size_x, affected_y + heights_size_y - area5_y, area5_start, heights_size_x); - } - else - { - // We can directly use the input buffer because there are no extra edges to take into account - heights = inHeights; - heights_size_x = inHeightsStride; - temp_heights = nullptr; - } - - // Calculate offset and stride - uint num_blocks = GetNumBlocks(); - uint range_block_offset, range_block_stride; - uint max_level = sGetMaxLevel(num_blocks); - sGetRangeBlockOffsetAndStride(num_blocks, max_level, range_block_offset, range_block_stride); - - // Loop over blocks - uint block_start_x = affected_x / mBlockSize; - uint block_start_y = affected_y / mBlockSize; - uint num_blocks_x = affected_size_x / mBlockSize; - uint num_blocks_y = affected_size_y / mBlockSize; - for (uint block_y = 0, sample_start_y = 0; block_y < num_blocks_y; ++block_y, sample_start_y += mBlockSize) - for (uint block_x = 0, sample_start_x = 0; block_x < num_blocks_x; ++block_x, sample_start_x += mBlockSize) - { - // Determine quantized min and max value for block - // Note that we need to include 1 extra row in the positive x/y direction to account for connecting triangles - int min_value = 0xffff; - int max_value = 0; - uint sample_x_end = min(sample_start_x + mBlockSize + 1, mSampleCount - affected_x); - uint sample_y_end = min(sample_start_y + mBlockSize + 1, mSampleCount - affected_y); - for (uint sample_y = sample_start_y; sample_y < sample_y_end; ++sample_y) - for (uint sample_x = sample_start_x; sample_x < sample_x_end; ++sample_x) - { - float h = heights[sample_y * heights_size_x + sample_x]; - if (h != cNoCollisionValue) - { - int quantized_height = Clamp((int)floor((h - mOffset.GetY()) / mScale.GetY()), 0, int(cMaxHeightValue16 - 1)); - min_value = min(min_value, quantized_height); - max_value = max(max_value, quantized_height + 1); - } - } - if (min_value > max_value) - min_value = max_value = cNoCollisionValue16; - - // Update range for block - RangeBlock *range_block; - uint index_in_block; - GetRangeBlock(block_start_x + block_x, block_start_y + block_y, range_block_offset, range_block_stride, range_block, index_in_block); - range_block->mMin[index_in_block] = uint16(min_value); - range_block->mMax[index_in_block] = uint16(max_value); - - // Get offset and scale for block - float offset_block = float(min_value); - float scale_block = float(max_value - min_value) / float(mSampleMask); - - // Calculate scale and offset using the formula used in GetPosition() solved for the quantized height (excluding 0.5 because we round down while quantizing) - float scale = scale_block * mScale.GetY(); - float offset = mOffset.GetY() + offset_block * mScale.GetY(); - - // Loop over samples in block - sample_x_end = sample_start_x + mBlockSize; - sample_y_end = sample_start_y + mBlockSize; - for (uint sample_y = sample_start_y; sample_y < sample_y_end; ++sample_y) - for (uint sample_x = sample_start_x; sample_x < sample_x_end; ++sample_x) - { - // Quantize height - float h = heights[sample_y * heights_size_x + sample_x]; - uint8 quantized_height = h != cNoCollisionValue? uint8(Clamp((int)floor((h - offset) / scale), 0, int(mSampleMask) - 1)) : mSampleMask; - - // Determine bit position of sample - uint sample = ((affected_y + sample_y) * mSampleCount + affected_x + sample_x) * uint(mBitsPerSample); - uint byte_pos = sample >> 3; - uint bit_pos = sample & 0b111; - - // Update the height value sample - JPH_ASSERT(byte_pos + 1 < mHeightSamples.size()); - uint8 *height_samples = mHeightSamples.data() + byte_pos; - uint16 height_sample = uint16(height_samples[0]) | uint16(uint16(height_samples[1]) << 8); - height_sample &= ~(uint16(mSampleMask) << bit_pos); - height_sample |= uint16(quantized_height) << bit_pos; - height_samples[0] = uint8(height_sample); - height_samples[1] = uint8(height_sample >> 8); - } - } - - // Update active edges - // Note that we must take an extra row on all sides to account for connecting triangles - uint ae_x = inX > 1? inX - 2 : 0; - uint ae_y = inY > 1? inY - 2 : 0; - uint ae_sx = min(inX + inSizeX + 1, mSampleCount - 1) - ae_x; - uint ae_sy = min(inY + inSizeY + 1, mSampleCount - 1) - ae_y; - CalculateActiveEdges(ae_x, ae_y, ae_sx, ae_sy, heights, affected_x, affected_y, heights_size_x, 1.0f, inActiveEdgeCosThresholdAngle, inAllocator); - - // Free temporary buffer - if (temp_heights != nullptr) - inAllocator.Free(temp_heights, heights_size_x * heights_size_y * sizeof(float)); - - // Update hierarchy of range blocks - while (max_level > 1) - { - // Get offset and stride for destination blocks - uint dst_range_block_offset, dst_range_block_stride; - sGetRangeBlockOffsetAndStride(num_blocks >> 1, max_level - 1, dst_range_block_offset, dst_range_block_stride); - - // If we're starting halfway through a 2x2 block, we need to process one extra block since we take steps of 2 blocks below - uint block_x_end = (block_start_x & 1) && block_start_x + num_blocks_x < num_blocks? num_blocks_x + 1 : num_blocks_x; - uint block_y_end = (block_start_y & 1) && block_start_y + num_blocks_y < num_blocks? num_blocks_y + 1 : num_blocks_y; - - // Loop over all affected blocks - for (uint block_y = 0; block_y < block_y_end; block_y += 2) - for (uint block_x = 0; block_x < block_x_end; block_x += 2) - { - // Get source range block - RangeBlock *src_range_block; - uint index_in_src_block; - GetRangeBlock(block_start_x + block_x, block_start_y + block_y, range_block_offset, range_block_stride, src_range_block, index_in_src_block); - - // Determine quantized min and max value for the entire 2x2 block - uint16 min_value = 0xffff; - uint16 max_value = 0; - for (uint i = 0; i < 4; ++i) - if (src_range_block->mMin[i] != cNoCollisionValue16) - { - min_value = min(min_value, src_range_block->mMin[i]); - max_value = max(max_value, src_range_block->mMax[i]); - } - - // Write to destination block - RangeBlock *dst_range_block; - uint index_in_dst_block; - GetRangeBlock((block_start_x + block_x) >> 1, (block_start_y + block_y) >> 1, dst_range_block_offset, dst_range_block_stride, dst_range_block, index_in_dst_block); - dst_range_block->mMin[index_in_dst_block] = uint16(min_value); - dst_range_block->mMax[index_in_dst_block] = uint16(max_value); - } - - // Go up one level - --max_level; - num_blocks >>= 1; - block_start_x >>= 1; - block_start_y >>= 1; - num_blocks_x = min((num_blocks_x + 1) >> 1, num_blocks); - num_blocks_y = min((num_blocks_y + 1) >> 1, num_blocks); - - // Update stride and offset for source to old destination - range_block_offset = dst_range_block_offset; - range_block_stride = dst_range_block_stride; - } - - // Calculate new min and max sample for the entire height field - mMinSample = 0xffff; - mMaxSample = 0; - for (uint i = 0; i < 4; ++i) - if (mRangeBlocks[0].mMin[i] != cNoCollisionValue16) - { - mMinSample = min(mMinSample, mRangeBlocks[0].mMin[i]); - mMaxSample = max(mMaxSample, mRangeBlocks[0].mMax[i]); - } - -#ifdef JPH_DEBUG_RENDERER - // Invalidate temporary rendering data - mGeometry.clear(); -#endif -} - -void HeightFieldShape::GetMaterials(uint inX, uint inY, uint inSizeX, uint inSizeY, uint8 *outMaterials, uint inMaterialsStride) const -{ - if (inSizeX == 0 || inSizeY == 0) - return; - - if (mMaterialIndices.empty()) - { - // Return all 0's - for (uint y = 0; y < inSizeY; ++y) - { - uint8 *out_indices = outMaterials + y * inMaterialsStride; - for (uint x = 0; x < inSizeX; ++x) - *out_indices++ = 0; - } - return; - } - - JPH_ASSERT(inX < mSampleCount && inY < mSampleCount); - JPH_ASSERT(inX + inSizeX < mSampleCount && inY + inSizeY < mSampleCount); - - uint count_min_1 = mSampleCount - 1; - uint16 material_index_mask = uint16((1 << mNumBitsPerMaterialIndex) - 1); - - for (uint y = 0; y < inSizeY; ++y) - { - // Calculate input position - uint bit_pos = (inX + (inY + y) * count_min_1) * mNumBitsPerMaterialIndex; - const uint8 *in_indices = mMaterialIndices.data() + (bit_pos >> 3); - bit_pos &= 0b111; - - // Calculate output position - uint8 *out_indices = outMaterials + y * inMaterialsStride; - - for (uint x = 0; x < inSizeX; ++x) - { - // Get material index - uint16 material_index = uint16(in_indices[0]) + uint16(uint16(in_indices[1]) << 8); - material_index >>= bit_pos; - material_index &= material_index_mask; - *out_indices = uint8(material_index); - - // Go to the next index - bit_pos += mNumBitsPerMaterialIndex; - in_indices += bit_pos >> 3; - bit_pos &= 0b111; - ++out_indices; - } - } -} - -bool HeightFieldShape::SetMaterials(uint inX, uint inY, uint inSizeX, uint inSizeY, const uint8 *inMaterials, uint inMaterialsStride, const PhysicsMaterialList *inMaterialList, TempAllocator &inAllocator) -{ - if (inSizeX == 0 || inSizeY == 0) - return true; - - JPH_ASSERT(inX < mSampleCount && inY < mSampleCount); - JPH_ASSERT(inX + inSizeX < mSampleCount && inY + inSizeY < mSampleCount); - - // Remap materials - uint material_remap_table_size = uint(inMaterialList != nullptr? inMaterialList->size() : mMaterials.size()); - uint8 *material_remap_table = (uint8 *)inAllocator.Allocate(material_remap_table_size); - if (inMaterialList != nullptr) - { - // Conservatively reserve more space if the incoming material list is bigger - if (inMaterialList->size() > mMaterials.size()) - mMaterials.reserve(inMaterialList->size()); - - // Create a remap table - uint8 *remap_entry = material_remap_table; - for (const PhysicsMaterial *material : *inMaterialList) - { - // Try to find it in the existing list - PhysicsMaterialList::const_iterator it = std::find(mMaterials.begin(), mMaterials.end(), material); - if (it != mMaterials.end()) - { - // Found it, calculate index - *remap_entry = uint8(it - mMaterials.begin()); - } - else - { - // Not found, add it - if (mMaterials.size() >= 256) - { - // We can't have more than 256 materials since we use uint8 as indices - inAllocator.Free(material_remap_table, material_remap_table_size); - return false; - } - *remap_entry = uint8(mMaterials.size()); - mMaterials.push_back(material); - } - ++remap_entry; - } - } - else - { - // No remapping - for (uint i = 0; i < material_remap_table_size; ++i) - material_remap_table[i] = uint8(i); - } - - if (mMaterials.size() == 1) - { - // Only 1 material, we don't need to store the material indices - return true; - } - - // Check if we need to resize the material indices array - uint count_min_1 = mSampleCount - 1; - uint32 new_bits_per_material_index = 32 - CountLeadingZeros((uint32)mMaterials.size() - 1); - JPH_ASSERT(mNumBitsPerMaterialIndex <= 8 && new_bits_per_material_index <= 8); - if (new_bits_per_material_index != mNumBitsPerMaterialIndex) - { - // Resize the material indices array - mMaterialIndices.resize(((Square(count_min_1) * new_bits_per_material_index + 7) >> 3) + 1); // Add 1 byte so we don't read out of bounds when reading an uint16 - - // Calculate old and new mask - uint16 old_material_index_mask = uint16((1 << mNumBitsPerMaterialIndex) - 1); - uint16 new_material_index_mask = uint16((1 << new_bits_per_material_index) - 1); - - // Loop through the array backwards to avoid overwriting data - int in_bit_pos = (count_min_1 * count_min_1 - 1) * mNumBitsPerMaterialIndex; - const uint8 *in_indices = mMaterialIndices.data() + (in_bit_pos >> 3); - in_bit_pos &= 0b111; - int out_bit_pos = (count_min_1 * count_min_1 - 1) * new_bits_per_material_index; - uint8 *out_indices = mMaterialIndices.data() + (out_bit_pos >> 3); - out_bit_pos &= 0b111; - - while (out_indices >= mMaterialIndices.data()) - { - // Read the material index - uint16 material_index = uint16(in_indices[0]) + uint16(uint16(in_indices[1]) << 8); - material_index >>= in_bit_pos; - material_index &= old_material_index_mask; - - // Write the material index - uint16 output_data = uint16(out_indices[0]) + uint16(uint16(out_indices[1]) << 8); - output_data &= ~(new_material_index_mask << out_bit_pos); - output_data |= material_index << out_bit_pos; - out_indices[0] = uint8(output_data); - out_indices[1] = uint8(output_data >> 8); - - // Go to the previous index - in_bit_pos -= int(mNumBitsPerMaterialIndex); - in_indices += in_bit_pos >> 3; - in_bit_pos &= 0b111; - out_bit_pos -= int(new_bits_per_material_index); - out_indices += out_bit_pos >> 3; - out_bit_pos &= 0b111; - } - - // Accept the new bits per material index - mNumBitsPerMaterialIndex = new_bits_per_material_index; - } - - uint16 material_index_mask = uint16((1 << mNumBitsPerMaterialIndex) - 1); - for (uint y = 0; y < inSizeY; ++y) - { - // Calculate input position - const uint8 *in_indices = inMaterials + y * inMaterialsStride; - - // Calculate output position - uint bit_pos = (inX + (inY + y) * count_min_1) * mNumBitsPerMaterialIndex; - uint8 *out_indices = mMaterialIndices.data() + (bit_pos >> 3); - bit_pos &= 0b111; - - for (uint x = 0; x < inSizeX; ++x) - { - // Update material - uint16 output_data = uint16(out_indices[0]) + uint16(uint16(out_indices[1]) << 8); - output_data &= ~(material_index_mask << bit_pos); - output_data |= material_remap_table[*in_indices] << bit_pos; - out_indices[0] = uint8(output_data); - out_indices[1] = uint8(output_data >> 8); - - // Go to the next index - in_indices++; - bit_pos += mNumBitsPerMaterialIndex; - out_indices += bit_pos >> 3; - bit_pos &= 0b111; - } - } - - // Free the remapping table - inAllocator.Free(material_remap_table, material_remap_table_size); - return true; -} - -MassProperties HeightFieldShape::GetMassProperties() const -{ - // Object should always be static, return default mass properties - return MassProperties(); -} - -const PhysicsMaterial *HeightFieldShape::GetMaterial(uint inX, uint inY) const -{ - if (mMaterials.empty()) - return PhysicsMaterial::sDefault; - if (mMaterials.size() == 1) - return mMaterials[0]; - - uint count_min_1 = mSampleCount - 1; - JPH_ASSERT(inX < count_min_1); - JPH_ASSERT(inY < count_min_1); - - // Calculate at which bit the material index starts - uint bit_pos = (inX + inY * count_min_1) * mNumBitsPerMaterialIndex; - uint byte_pos = bit_pos >> 3; - bit_pos &= 0b111; - - // Read the material index - JPH_ASSERT(byte_pos + 1 < mMaterialIndices.size()); - const uint8 *material_indices = mMaterialIndices.data() + byte_pos; - uint16 material_index = uint16(material_indices[0]) + uint16(uint16(material_indices[1]) << 8); - material_index >>= bit_pos; - material_index &= (1 << mNumBitsPerMaterialIndex) - 1; - - // Return the material - return mMaterials[material_index]; -} - -uint HeightFieldShape::GetSubShapeIDBits() const -{ - // Need to store X, Y and 1 extra bit to specify the triangle number in the quad - return 2 * (32 - CountLeadingZeros(mSampleCount - 1)) + 1; -} - -SubShapeID HeightFieldShape::EncodeSubShapeID(const SubShapeIDCreator &inCreator, uint inX, uint inY, uint inTriangle) const -{ - return inCreator.PushID((inX + inY * mSampleCount) * 2 + inTriangle, GetSubShapeIDBits()).GetID(); -} - -void HeightFieldShape::DecodeSubShapeID(const SubShapeID &inSubShapeID, uint &outX, uint &outY, uint &outTriangle) const -{ - // Decode sub shape id - SubShapeID remainder; - uint32 id = inSubShapeID.PopID(GetSubShapeIDBits(), remainder); - JPH_ASSERT(remainder.IsEmpty(), "Invalid subshape ID"); - - // Get triangle index - outTriangle = id & 1; - id >>= 1; - - // Fetch the x and y coordinate - outX = id % mSampleCount; - outY = id / mSampleCount; -} - -const PhysicsMaterial *HeightFieldShape::GetMaterial(const SubShapeID &inSubShapeID) const -{ - // Decode ID - uint x, y, triangle; - DecodeSubShapeID(inSubShapeID, x, y, triangle); - - // Fetch the material - return GetMaterial(x, y); -} - -Vec3 HeightFieldShape::GetSurfaceNormal(const SubShapeID &inSubShapeID, Vec3Arg inLocalSurfacePosition) const -{ - // Decode ID - uint x, y, triangle; - DecodeSubShapeID(inSubShapeID, x, y, triangle); - - // Fetch vertices that both triangles share - Vec3 x1y1 = GetPosition(x, y); - Vec3 x2y2 = GetPosition(x + 1, y + 1); - - // Get normal depending on which triangle was selected - Vec3 normal; - if (triangle == 0) - { - Vec3 x1y2 = GetPosition(x, y + 1); - normal = (x2y2 - x1y2).Cross(x1y1 - x1y2); - } - else - { - Vec3 x2y1 = GetPosition(x + 1, y); - normal = (x1y1 - x2y1).Cross(x2y2 - x2y1); - } - - return normal.Normalized(); -} - -void HeightFieldShape::GetSupportingFace(const SubShapeID &inSubShapeID, Vec3Arg inDirection, Vec3Arg inScale, Mat44Arg inCenterOfMassTransform, SupportingFace &outVertices) const -{ - // Decode ID - uint x, y, triangle; - DecodeSubShapeID(inSubShapeID, x, y, triangle); - - // Fetch the triangle - outVertices.resize(3); - outVertices[0] = GetPosition(x, y); - Vec3 v2 = GetPosition(x + 1, y + 1); - if (triangle == 0) - { - outVertices[1] = GetPosition(x, y + 1); - outVertices[2] = v2; - } - else - { - outVertices[1] = v2; - outVertices[2] = GetPosition(x + 1, y); - } - - // Flip triangle if scaled inside out - if (ScaleHelpers::IsInsideOut(inScale)) - swap(outVertices[1], outVertices[2]); - - // Transform to world space - Mat44 transform = inCenterOfMassTransform.PreScaled(inScale); - for (Vec3 &v : outVertices) - v = transform * v; -} - -inline uint8 HeightFieldShape::GetEdgeFlags(uint inX, uint inY, uint inTriangle) const -{ - JPH_ASSERT(inX < mSampleCount - 1 && inY < mSampleCount - 1); - - if (inTriangle == 0) - { - // The edge flags for this triangle are directly stored, find the right 3 bits - uint bit_pos = 3 * (inX + inY * (mSampleCount - 1)); - uint byte_pos = bit_pos >> 3; - bit_pos &= 0b111; - JPH_ASSERT(byte_pos + 1 < mActiveEdges.size()); - const uint8 *active_edges = mActiveEdges.data() + byte_pos; - uint16 edge_flags = uint16(active_edges[0]) + uint16(uint16(active_edges[1]) << 8); - return uint8(edge_flags >> bit_pos) & 0b111; - } - else - { - // We don't store this triangle directly, we need to look at our three neighbours to construct the edge flags - uint8 edge0 = (GetEdgeFlags(inX, inY, 0) & 0b100) != 0? 0b001 : 0; // Diagonal edge - uint8 edge1 = inX == mSampleCount - 2 || (GetEdgeFlags(inX + 1, inY, 0) & 0b001) != 0? 0b010 : 0; // Vertical edge - uint8 edge2 = inY == 0 || (GetEdgeFlags(inX, inY - 1, 0) & 0b010) != 0? 0b100 : 0; // Horizontal edge - return edge0 | edge1 | edge2; - } -} - -AABox HeightFieldShape::GetLocalBounds() const -{ - if (mMinSample == cNoCollisionValue16) - { - // This whole height field shape doesn't have any collision, return the center point - Vec3 center = mOffset + 0.5f * mScale * Vec3(float(mSampleCount - 1), 0.0f, float(mSampleCount - 1)); - return AABox(center, center); - } - else - { - // Bounding box based on min and max sample height - Vec3 bmin = mOffset + mScale * Vec3(0.0f, float(mMinSample), 0.0f); - Vec3 bmax = mOffset + mScale * Vec3(float(mSampleCount - 1), float(mMaxSample), float(mSampleCount - 1)); - return AABox(bmin, bmax); - } -} - -#ifdef JPH_DEBUG_RENDERER -void HeightFieldShape::Draw(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inUseMaterialColors, bool inDrawWireframe) const -{ - // Don't draw anything if we don't have any collision - if (mHeightSamples.empty()) - return; - - // Reset the batch if we switch coloring mode - if (mCachedUseMaterialColors != inUseMaterialColors) - { - mGeometry.clear(); - mCachedUseMaterialColors = inUseMaterialColors; - } - - if (mGeometry.empty()) - { - // Divide terrain in triangle batches of max 64x64x2 triangles to allow better culling of the terrain - uint32 block_size = min(mSampleCount, 64); - for (uint32 by = 0; by < mSampleCount; by += block_size) - for (uint32 bx = 0; bx < mSampleCount; bx += block_size) - { - // Create vertices for a block - Array triangles; - triangles.resize(block_size * block_size * 2); - DebugRenderer::Triangle *out_tri = &triangles[0]; - for (uint32 y = by, max_y = min(by + block_size, mSampleCount - 1); y < max_y; ++y) - for (uint32 x = bx, max_x = min(bx + block_size, mSampleCount - 1); x < max_x; ++x) - if (!IsNoCollision(x, y) && !IsNoCollision(x + 1, y + 1)) - { - Vec3 x1y1 = GetPosition(x, y); - Vec3 x2y2 = GetPosition(x + 1, y + 1); - Color color = inUseMaterialColors? GetMaterial(x, y)->GetDebugColor() : Color::sWhite; - - if (!IsNoCollision(x, y + 1)) - { - Vec3 x1y2 = GetPosition(x, y + 1); - - x1y1.StoreFloat3(&out_tri->mV[0].mPosition); - x1y2.StoreFloat3(&out_tri->mV[1].mPosition); - x2y2.StoreFloat3(&out_tri->mV[2].mPosition); - - Vec3 normal = (x2y2 - x1y2).Cross(x1y1 - x1y2).Normalized(); - for (DebugRenderer::Vertex &v : out_tri->mV) - { - v.mColor = color; - v.mUV = Float2(0, 0); - normal.StoreFloat3(&v.mNormal); - } - - ++out_tri; - } - - if (!IsNoCollision(x + 1, y)) - { - Vec3 x2y1 = GetPosition(x + 1, y); - - x1y1.StoreFloat3(&out_tri->mV[0].mPosition); - x2y2.StoreFloat3(&out_tri->mV[1].mPosition); - x2y1.StoreFloat3(&out_tri->mV[2].mPosition); - - Vec3 normal = (x1y1 - x2y1).Cross(x2y2 - x2y1).Normalized(); - for (DebugRenderer::Vertex &v : out_tri->mV) - { - v.mColor = color; - v.mUV = Float2(0, 0); - normal.StoreFloat3(&v.mNormal); - } - - ++out_tri; - } - } - - // Resize triangles array to actual amount of triangles written - size_t num_triangles = out_tri - &triangles[0]; - triangles.resize(num_triangles); - - // Create batch - if (num_triangles > 0) - mGeometry.push_back(new DebugRenderer::Geometry(inRenderer->CreateTriangleBatch(triangles), DebugRenderer::sCalculateBounds(&triangles[0].mV[0], int(3 * num_triangles)))); - } - } - - // Get transform including scale - RMat44 transform = inCenterOfMassTransform.PreScaled(inScale); - - // Test if the shape is scaled inside out - DebugRenderer::ECullMode cull_mode = ScaleHelpers::IsInsideOut(inScale)? DebugRenderer::ECullMode::CullFrontFace : DebugRenderer::ECullMode::CullBackFace; - - // Determine the draw mode - DebugRenderer::EDrawMode draw_mode = inDrawWireframe? DebugRenderer::EDrawMode::Wireframe : DebugRenderer::EDrawMode::Solid; - - // Draw the geometry - for (const DebugRenderer::GeometryRef &b : mGeometry) - inRenderer->DrawGeometry(transform, inColor, b, cull_mode, DebugRenderer::ECastShadow::On, draw_mode); - - if (sDrawTriangleOutlines) - { - struct Visitor - { - JPH_INLINE explicit Visitor(const HeightFieldShape *inShape, DebugRenderer *inRenderer, RMat44Arg inTransform) : - mShape(inShape), - mRenderer(inRenderer), - mTransform(inTransform) - { - } - - JPH_INLINE bool ShouldAbort() const - { - return false; - } - - JPH_INLINE bool ShouldVisitRangeBlock([[maybe_unused]] int inStackTop) const - { - return true; - } - - JPH_INLINE int VisitRangeBlock(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, [[maybe_unused]] int inStackTop) const - { - UVec4 valid = Vec4::sLessOrEqual(inBoundsMinY, inBoundsMaxY); - return CountAndSortTrues(valid, ioProperties); - } - - JPH_INLINE void VisitTriangle(uint inX, uint inY, uint inTriangle, Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2) const - { - // Determine active edges - uint8 active_edges = mShape->GetEdgeFlags(inX, inY, inTriangle); - - // Loop through edges - Vec3 v[] = { inV0, inV1, inV2 }; - for (uint edge_idx = 0; edge_idx < 3; ++edge_idx) - { - RVec3 v1 = mTransform * v[edge_idx]; - RVec3 v2 = mTransform * v[(edge_idx + 1) % 3]; - - // Draw active edge as a green arrow, other edges as grey - if (active_edges & (1 << edge_idx)) - mRenderer->DrawArrow(v1, v2, Color::sGreen, 0.01f); - else - mRenderer->DrawLine(v1, v2, Color::sGrey); - } - } - - const HeightFieldShape *mShape; - DebugRenderer * mRenderer; - RMat44 mTransform; - }; - - Visitor visitor(this, inRenderer, inCenterOfMassTransform.PreScaled(inScale)); - WalkHeightField(visitor); - } -} -#endif // JPH_DEBUG_RENDERER - -class HeightFieldShape::DecodingContext -{ -public: - JPH_INLINE explicit DecodingContext(const HeightFieldShape *inShape) : - mShape(inShape) - { - static_assert(sizeof(sGridOffsets) / sizeof(uint) == cNumBitsXY + 1, "Offsets array is not long enough"); - - // Construct root stack entry - mPropertiesStack[0] = 0; // level: 0, x: 0, y: 0 - } - - template - JPH_INLINE void WalkHeightField(Visitor &ioVisitor) - { - // Early out if there's no collision - if (mShape->mHeightSamples.empty()) - return; - - // Precalculate values relating to sample count - uint32 sample_count = mShape->mSampleCount; - UVec4 sample_count_min_1 = UVec4::sReplicate(sample_count - 1); - - // Precalculate values relating to block size - uint32 block_size = mShape->mBlockSize; - uint32 block_size_plus_1 = block_size + 1; - uint num_blocks = mShape->GetNumBlocks(); - uint num_blocks_min_1 = num_blocks - 1; - uint max_level = HeightFieldShape::sGetMaxLevel(num_blocks); - uint32 max_stride = (num_blocks + 1) >> 1; - - // Precalculate range block offset and stride for GetBlockOffsetAndScale - uint range_block_offset, range_block_stride; - sGetRangeBlockOffsetAndStride(num_blocks, max_level, range_block_offset, range_block_stride); - - // Allocate space for vertices and 'no collision' flags - int array_size = Square(block_size_plus_1); - Vec3 *vertices = reinterpret_cast(JPH_STACK_ALLOC(array_size * sizeof(Vec3))); - bool *no_collision = reinterpret_cast(JPH_STACK_ALLOC(array_size * sizeof(bool))); - - // Splat offsets - Vec4 ox = mShape->mOffset.SplatX(); - Vec4 oy = mShape->mOffset.SplatY(); - Vec4 oz = mShape->mOffset.SplatZ(); - - // Splat scales - Vec4 sx = mShape->mScale.SplatX(); - Vec4 sy = mShape->mScale.SplatY(); - Vec4 sz = mShape->mScale.SplatZ(); - - do - { - // Decode properties - uint32 properties_top = mPropertiesStack[mTop]; - uint32 x = properties_top & cMaskBitsXY; - uint32 y = (properties_top >> cNumBitsXY) & cMaskBitsXY; - uint32 level = properties_top >> cLevelShift; - - if (level >= max_level) - { - // Determine actual range of samples (minus one because we eventually want to iterate over the triangles, not the samples) - uint32 min_x = x * block_size; - uint32 max_x = min_x + block_size; - uint32 min_y = y * block_size; - uint32 max_y = min_y + block_size; - - // Decompress vertices of block at (x, y) - Vec3 *dst_vertex = vertices; - bool *dst_no_collision = no_collision; - float block_offset, block_scale; - mShape->GetBlockOffsetAndScale(x, y, range_block_offset, range_block_stride, block_offset, block_scale); - for (uint32 v_y = min_y; v_y < max_y; ++v_y) - { - for (uint32 v_x = min_x; v_x < max_x; ++v_x) - { - *dst_vertex = mShape->GetPosition(v_x, v_y, block_offset, block_scale, *dst_no_collision); - ++dst_vertex; - ++dst_no_collision; - } - - // Skip last column, these values come from a different block - ++dst_vertex; - ++dst_no_collision; - } - - // Decompress block (x + 1, y) - uint32 max_x_decrement = 0; - if (x < num_blocks_min_1) - { - dst_vertex = vertices + block_size; - dst_no_collision = no_collision + block_size; - mShape->GetBlockOffsetAndScale(x + 1, y, range_block_offset, range_block_stride, block_offset, block_scale); - for (uint32 v_y = min_y; v_y < max_y; ++v_y) - { - *dst_vertex = mShape->GetPosition(max_x, v_y, block_offset, block_scale, *dst_no_collision); - dst_vertex += block_size_plus_1; - dst_no_collision += block_size_plus_1; - } - } - else - max_x_decrement = 1; // We don't have a next block, one less triangle to test - - // Decompress block (x, y + 1) - if (y < num_blocks_min_1) - { - uint start = block_size * block_size_plus_1; - dst_vertex = vertices + start; - dst_no_collision = no_collision + start; - mShape->GetBlockOffsetAndScale(x, y + 1, range_block_offset, range_block_stride, block_offset, block_scale); - for (uint32 v_x = min_x; v_x < max_x; ++v_x) - { - *dst_vertex = mShape->GetPosition(v_x, max_y, block_offset, block_scale, *dst_no_collision); - ++dst_vertex; - ++dst_no_collision; - } - - // Decompress single sample of block at (x + 1, y + 1) - if (x < num_blocks_min_1) - { - mShape->GetBlockOffsetAndScale(x + 1, y + 1, range_block_offset, range_block_stride, block_offset, block_scale); - *dst_vertex = mShape->GetPosition(max_x, max_y, block_offset, block_scale, *dst_no_collision); - } - } - else - --max_y; // We don't have a next block, one less triangle to test - - // Update max_x (we've been using it so we couldn't update it earlier) - max_x -= max_x_decrement; - - // We're going to divide the vertices in 4 blocks to do one more runtime sub-division, calculate the ranges of those blocks - struct Range - { - uint32 mMinX, mMinY, mNumTrianglesX, mNumTrianglesY; - }; - uint32 half_block_size = block_size >> 1; - uint32 block_size_x = max_x - min_x - half_block_size; - uint32 block_size_y = max_y - min_y - half_block_size; - Range ranges[] = - { - { 0, 0, half_block_size, half_block_size }, - { half_block_size, 0, block_size_x, half_block_size }, - { 0, half_block_size, half_block_size, block_size_y }, - { half_block_size, half_block_size, block_size_x, block_size_y }, - }; - - // Calculate the min and max of each of the blocks - Mat44 block_min, block_max; - for (int block = 0; block < 4; ++block) - { - // Get the range for this block - const Range &range = ranges[block]; - uint32 start = range.mMinX + range.mMinY * block_size_plus_1; - uint32 size_x_plus_1 = range.mNumTrianglesX + 1; - uint32 size_y_plus_1 = range.mNumTrianglesY + 1; - - // Calculate where to start reading - const Vec3 *src_vertex = vertices + start; - const bool *src_no_collision = no_collision + start; - uint32 stride = block_size_plus_1 - size_x_plus_1; - - // Start range with a very large inside-out box - Vec3 value_min = Vec3::sReplicate(1.0e30f); - Vec3 value_max = Vec3::sReplicate(-1.0e30f); - - // Loop over the samples to determine the min and max of this block - for (uint32 block_y = 0; block_y < size_y_plus_1; ++block_y) - { - for (uint32 block_x = 0; block_x < size_x_plus_1; ++block_x) - { - if (!*src_no_collision) - { - value_min = Vec3::sMin(value_min, *src_vertex); - value_max = Vec3::sMax(value_max, *src_vertex); - } - ++src_vertex; - ++src_no_collision; - } - src_vertex += stride; - src_no_collision += stride; - } - block_min.SetColumn4(block, Vec4(value_min)); - block_max.SetColumn4(block, Vec4(value_max)); - } - - #ifdef JPH_DEBUG_HEIGHT_FIELD - // Draw the bounding boxes of the sub-nodes - for (int block = 0; block < 4; ++block) - { - AABox bounds(block_min.GetColumn3(block), block_max.GetColumn3(block)); - if (bounds.IsValid()) - DebugRenderer::sInstance->DrawWireBox(bounds, Color::sYellow); - } - #endif // JPH_DEBUG_HEIGHT_FIELD - - // Transpose so we have the mins and maxes of each of the blocks in rows instead of columns - Mat44 transposed_min = block_min.Transposed(); - Mat44 transposed_max = block_max.Transposed(); - - // Check which blocks collide - // Note: At this point we don't use our own stack but we do allow the visitor to use its own stack - // to store collision distances so that we can still early out when no closer hits have been found. - UVec4 colliding_blocks(0, 1, 2, 3); - int num_results = ioVisitor.VisitRangeBlock(transposed_min.GetColumn4(0), transposed_min.GetColumn4(1), transposed_min.GetColumn4(2), transposed_max.GetColumn4(0), transposed_max.GetColumn4(1), transposed_max.GetColumn4(2), colliding_blocks, mTop); - - // Loop through the results backwards (closest first) - int result = num_results - 1; - while (result >= 0) - { - // Calculate the min and max of this block - uint32 block = colliding_blocks[result]; - const Range &range = ranges[block]; - uint32 block_min_x = min_x + range.mMinX; - uint32 block_max_x = block_min_x + range.mNumTrianglesX; - uint32 block_min_y = min_y + range.mMinY; - uint32 block_max_y = block_min_y + range.mNumTrianglesY; - - // Loop triangles - for (uint32 v_y = block_min_y; v_y < block_max_y; ++v_y) - for (uint32 v_x = block_min_x; v_x < block_max_x; ++v_x) - { - // Get first vertex - const int offset = (v_y - min_y) * block_size_plus_1 + (v_x - min_x); - const Vec3 *start_vertex = vertices + offset; - const bool *start_no_collision = no_collision + offset; - - // Check if vertices shared by both triangles have collision - if (!start_no_collision[0] && !start_no_collision[block_size_plus_1 + 1]) - { - // Loop 2 triangles - for (uint t = 0; t < 2; ++t) - { - // Determine triangle vertices - Vec3 v0, v1, v2; - if (t == 0) - { - // Check third vertex - if (start_no_collision[block_size_plus_1]) - continue; - - // Get vertices for triangle - v0 = start_vertex[0]; - v1 = start_vertex[block_size_plus_1]; - v2 = start_vertex[block_size_plus_1 + 1]; - } - else - { - // Check third vertex - if (start_no_collision[1]) - continue; - - // Get vertices for triangle - v0 = start_vertex[0]; - v1 = start_vertex[block_size_plus_1 + 1]; - v2 = start_vertex[1]; - } - - #ifdef JPH_DEBUG_HEIGHT_FIELD - DebugRenderer::sInstance->DrawWireTriangle(RVec3(v0), RVec3(v1), RVec3(v2), Color::sWhite); - #endif - - // Call visitor - ioVisitor.VisitTriangle(v_x, v_y, t, v0, v1, v2); - - // Check if we're done - if (ioVisitor.ShouldAbort()) - return; - } - } - } - - // Fetch next block until we find one that the visitor wants to see - do - --result; - while (result >= 0 && !ioVisitor.ShouldVisitRangeBlock(mTop + result)); - } - } - else - { - // Visit child grid - uint32 stride = min(1U << level, max_stride); // At the most detailed level we store a non-power of 2 number of blocks - uint32 offset = sGridOffsets[level] + stride * y + x; - - // Decode min/max height - UVec4 block = UVec4::sLoadInt4Aligned(reinterpret_cast(&mShape->mRangeBlocks[offset])); - Vec4 bounds_miny = oy + sy * block.Expand4Uint16Lo().ToFloat(); - Vec4 bounds_maxy = oy + sy * block.Expand4Uint16Hi().ToFloat(); - - // Calculate size of one cell at this grid level - UVec4 internal_cell_size = UVec4::sReplicate(block_size << (max_level - level - 1)); // subtract 1 from level because we have an internal grid of 2x2 - - // Calculate min/max x and z - UVec4 two_x = UVec4::sReplicate(2 * x); // multiply by two because we have an internal grid of 2x2 - Vec4 bounds_minx = ox + sx * (internal_cell_size * (two_x + UVec4(0, 1, 0, 1))).ToFloat(); - Vec4 bounds_maxx = ox + sx * UVec4::sMin(internal_cell_size * (two_x + UVec4(1, 2, 1, 2)), sample_count_min_1).ToFloat(); - - UVec4 two_y = UVec4::sReplicate(2 * y); - Vec4 bounds_minz = oz + sz * (internal_cell_size * (two_y + UVec4(0, 0, 1, 1))).ToFloat(); - Vec4 bounds_maxz = oz + sz * UVec4::sMin(internal_cell_size * (two_y + UVec4(1, 1, 2, 2)), sample_count_min_1).ToFloat(); - - // Calculate properties of child blocks - UVec4 properties = UVec4::sReplicate(((level + 1) << cLevelShift) + (y << (cNumBitsXY + 1)) + (x << 1)) + UVec4(0, 1, 1 << cNumBitsXY, (1 << cNumBitsXY) + 1); - - #ifdef JPH_DEBUG_HEIGHT_FIELD - // Draw boxes - for (int i = 0; i < 4; ++i) - { - AABox b(Vec3(bounds_minx[i], bounds_miny[i], bounds_minz[i]), Vec3(bounds_maxx[i], bounds_maxy[i], bounds_maxz[i])); - if (b.IsValid()) - DebugRenderer::sInstance->DrawWireBox(b, Color::sGreen); - } - #endif - - // Check which sub nodes to visit - int num_results = ioVisitor.VisitRangeBlock(bounds_minx, bounds_miny, bounds_minz, bounds_maxx, bounds_maxy, bounds_maxz, properties, mTop); - - // Push them onto the stack - JPH_ASSERT(mTop + 4 < cStackSize); - properties.StoreInt4(&mPropertiesStack[mTop]); - mTop += num_results; - } - - // Check if we're done - if (ioVisitor.ShouldAbort()) - return; - - // Fetch next node until we find one that the visitor wants to see - do - --mTop; - while (mTop >= 0 && !ioVisitor.ShouldVisitRangeBlock(mTop)); - } - while (mTop >= 0); - } - - // This can be used to have the visitor early out (ioVisitor.ShouldAbort() returns true) and later continue again (call WalkHeightField() again) - JPH_INLINE bool IsDoneWalking() const - { - return mTop < 0; - } - -private: - const HeightFieldShape * mShape; - int mTop = 0; - uint32 mPropertiesStack[cStackSize]; -}; - -template -void HeightFieldShape::WalkHeightField(Visitor &ioVisitor) const -{ - DecodingContext ctx(this); - ctx.WalkHeightField(ioVisitor); -} - -bool HeightFieldShape::CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const -{ - JPH_PROFILE_FUNCTION(); - - struct Visitor - { - JPH_INLINE explicit Visitor(const HeightFieldShape *inShape, const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) : - mHit(ioHit), - mRayOrigin(inRay.mOrigin), - mRayDirection(inRay.mDirection), - mRayInvDirection(inRay.mDirection), - mShape(inShape), - mSubShapeIDCreator(inSubShapeIDCreator) - { - } - - JPH_INLINE bool ShouldAbort() const - { - return mHit.mFraction <= 0.0f; - } - - JPH_INLINE bool ShouldVisitRangeBlock(int inStackTop) const - { - return mDistanceStack[inStackTop] < mHit.mFraction; - } - - JPH_INLINE int VisitRangeBlock(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, int inStackTop) - { - // Test bounds of 4 children - Vec4 distance = RayAABox4(mRayOrigin, mRayInvDirection, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ); - - // Sort so that highest values are first (we want to first process closer hits and we process stack top to bottom) - return SortReverseAndStore(distance, mHit.mFraction, ioProperties, &mDistanceStack[inStackTop]); - } - - JPH_INLINE void VisitTriangle(uint inX, uint inY, uint inTriangle, Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2) - { - float fraction = RayTriangle(mRayOrigin, mRayDirection, inV0, inV1, inV2); - if (fraction < mHit.mFraction) - { - // It's a closer hit - mHit.mFraction = fraction; - mHit.mSubShapeID2 = mShape->EncodeSubShapeID(mSubShapeIDCreator, inX, inY, inTriangle); - mReturnValue = true; - } - } - - RayCastResult & mHit; - Vec3 mRayOrigin; - Vec3 mRayDirection; - RayInvDirection mRayInvDirection; - const HeightFieldShape *mShape; - SubShapeIDCreator mSubShapeIDCreator; - bool mReturnValue = false; - float mDistanceStack[cStackSize]; - }; - - Visitor visitor(this, inRay, inSubShapeIDCreator, ioHit); - WalkHeightField(visitor); - - return visitor.mReturnValue; -} - -void HeightFieldShape::CastRay(const RayCast &inRay, const RayCastSettings &inRayCastSettings, const SubShapeIDCreator &inSubShapeIDCreator, CastRayCollector &ioCollector, const ShapeFilter &inShapeFilter) const -{ - JPH_PROFILE_FUNCTION(); - - // Test shape filter - if (!inShapeFilter.ShouldCollide(this, inSubShapeIDCreator.GetID())) - return; - - struct Visitor - { - JPH_INLINE explicit Visitor(const HeightFieldShape *inShape, const RayCast &inRay, const RayCastSettings &inRayCastSettings, const SubShapeIDCreator &inSubShapeIDCreator, CastRayCollector &ioCollector) : - mCollector(ioCollector), - mRayOrigin(inRay.mOrigin), - mRayDirection(inRay.mDirection), - mRayInvDirection(inRay.mDirection), - mBackFaceMode(inRayCastSettings.mBackFaceMode), - mShape(inShape), - mSubShapeIDCreator(inSubShapeIDCreator) - { - } - - JPH_INLINE bool ShouldAbort() const - { - return mCollector.ShouldEarlyOut(); - } - - JPH_INLINE bool ShouldVisitRangeBlock(int inStackTop) const - { - return mDistanceStack[inStackTop] < mCollector.GetEarlyOutFraction(); - } - - JPH_INLINE int VisitRangeBlock(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, int inStackTop) - { - // Test bounds of 4 children - Vec4 distance = RayAABox4(mRayOrigin, mRayInvDirection, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ); - - // Sort so that highest values are first (we want to first process closer hits and we process stack top to bottom) - return SortReverseAndStore(distance, mCollector.GetEarlyOutFraction(), ioProperties, &mDistanceStack[inStackTop]); - } - - JPH_INLINE void VisitTriangle(uint inX, uint inY, uint inTriangle, Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2) const - { - // Back facing check - if (mBackFaceMode == EBackFaceMode::IgnoreBackFaces && (inV2 - inV0).Cross(inV1 - inV0).Dot(mRayDirection) < 0) - return; - - // Check the triangle - float fraction = RayTriangle(mRayOrigin, mRayDirection, inV0, inV1, inV2); - if (fraction < mCollector.GetEarlyOutFraction()) - { - RayCastResult hit; - hit.mBodyID = TransformedShape::sGetBodyID(mCollector.GetContext()); - hit.mFraction = fraction; - hit.mSubShapeID2 = mShape->EncodeSubShapeID(mSubShapeIDCreator, inX, inY, inTriangle); - mCollector.AddHit(hit); - } - } - - CastRayCollector & mCollector; - Vec3 mRayOrigin; - Vec3 mRayDirection; - RayInvDirection mRayInvDirection; - EBackFaceMode mBackFaceMode; - const HeightFieldShape *mShape; - SubShapeIDCreator mSubShapeIDCreator; - float mDistanceStack[cStackSize]; - }; - - Visitor visitor(this, inRay, inRayCastSettings, inSubShapeIDCreator, ioCollector); - WalkHeightField(visitor); -} - -void HeightFieldShape::CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter) const -{ - // A height field doesn't have volume, so we can't test insideness -} - -void HeightFieldShape::CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, SoftBodyVertex *ioVertices, uint inNumVertices, [[maybe_unused]] float inDeltaTime, [[maybe_unused]] Vec3Arg inDisplacementDueToGravity, int inCollidingShapeIndex) const -{ - JPH_PROFILE_FUNCTION(); - - struct Visitor : public CollideSoftBodyVerticesVsTriangles - { - using CollideSoftBodyVerticesVsTriangles::CollideSoftBodyVerticesVsTriangles; - - JPH_INLINE bool ShouldAbort() const - { - return false; - } - - JPH_INLINE bool ShouldVisitRangeBlock([[maybe_unused]] int inStackTop) const - { - return mDistanceStack[inStackTop] < mClosestDistanceSq; - } - - JPH_INLINE int VisitRangeBlock(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, int inStackTop) - { - // Get distance to vertex - Vec4 dist_sq = AABox4DistanceSqToPoint(mLocalPosition, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ); - - // Sort so that highest values are first (we want to first process closer hits and we process stack top to bottom) - return SortReverseAndStore(dist_sq, mClosestDistanceSq, ioProperties, &mDistanceStack[inStackTop]); - } - - JPH_INLINE void VisitTriangle([[maybe_unused]] uint inX, [[maybe_unused]] uint inY, [[maybe_unused]] uint inTriangle, Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2) - { - ProcessTriangle(inV0, inV1, inV2); - } - - float mDistanceStack[cStackSize]; - }; - - Visitor visitor(inCenterOfMassTransform, inScale); - - for (SoftBodyVertex *v = ioVertices, *sbv_end = ioVertices + inNumVertices; v < sbv_end; ++v) - if (v->mInvMass > 0.0f) - { - visitor.StartVertex(*v); - WalkHeightField(visitor); - visitor.FinishVertex(*v, inCollidingShapeIndex); - } -} - -void HeightFieldShape::sCastConvexVsHeightField(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, [[maybe_unused]] const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector) -{ - JPH_PROFILE_FUNCTION(); - - struct Visitor : public CastConvexVsTriangles - { - using CastConvexVsTriangles::CastConvexVsTriangles; - - JPH_INLINE bool ShouldAbort() const - { - return mCollector.ShouldEarlyOut(); - } - - JPH_INLINE bool ShouldVisitRangeBlock(int inStackTop) const - { - return mDistanceStack[inStackTop] < mCollector.GetPositiveEarlyOutFraction(); - } - - JPH_INLINE int VisitRangeBlock(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, int inStackTop) - { - // Scale the bounding boxes of this node - Vec4 bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z; - AABox4Scale(mScale, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z); - - // Enlarge them by the casted shape's box extents - AABox4EnlargeWithExtent(mBoxExtent, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z); - - // Test bounds of 4 children - Vec4 distance = RayAABox4(mBoxCenter, mInvDirection, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z); - - // Clear distance for invalid bounds - distance = Vec4::sSelect(Vec4::sReplicate(FLT_MAX), distance, Vec4::sLessOrEqual(inBoundsMinY, inBoundsMaxY)); - - // Sort so that highest values are first (we want to first process closer hits and we process stack top to bottom) - return SortReverseAndStore(distance, mCollector.GetPositiveEarlyOutFraction(), ioProperties, &mDistanceStack[inStackTop]); - } - - JPH_INLINE void VisitTriangle(uint inX, uint inY, uint inTriangle, Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2) - { - // Create sub shape id for this part - SubShapeID triangle_sub_shape_id = mShape2->EncodeSubShapeID(mSubShapeIDCreator2, inX, inY, inTriangle); - - // Determine active edges - uint8 active_edges = mShape2->GetEdgeFlags(inX, inY, inTriangle); - - Cast(inV0, inV1, inV2, active_edges, triangle_sub_shape_id); - } - - const HeightFieldShape * mShape2; - RayInvDirection mInvDirection; - Vec3 mBoxCenter; - Vec3 mBoxExtent; - SubShapeIDCreator mSubShapeIDCreator2; - float mDistanceStack[cStackSize]; - }; - - JPH_ASSERT(inShape->GetSubType() == EShapeSubType::HeightField); - const HeightFieldShape *shape = static_cast(inShape); - - Visitor visitor(inShapeCast, inShapeCastSettings, inScale, inCenterOfMassTransform2, inSubShapeIDCreator1, ioCollector); - visitor.mShape2 = shape; - visitor.mInvDirection.Set(inShapeCast.mDirection); - visitor.mBoxCenter = inShapeCast.mShapeWorldBounds.GetCenter(); - visitor.mBoxExtent = inShapeCast.mShapeWorldBounds.GetExtent(); - visitor.mSubShapeIDCreator2 = inSubShapeIDCreator2; - shape->WalkHeightField(visitor); -} - -void HeightFieldShape::sCastSphereVsHeightField(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, [[maybe_unused]] const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector) -{ - JPH_PROFILE_FUNCTION(); - - struct Visitor : public CastSphereVsTriangles - { - using CastSphereVsTriangles::CastSphereVsTriangles; - - JPH_INLINE bool ShouldAbort() const - { - return mCollector.ShouldEarlyOut(); - } - - JPH_INLINE bool ShouldVisitRangeBlock(int inStackTop) const - { - return mDistanceStack[inStackTop] < mCollector.GetPositiveEarlyOutFraction(); - } - - JPH_INLINE int VisitRangeBlock(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, int inStackTop) - { - // Scale the bounding boxes of this node - Vec4 bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z; - AABox4Scale(mScale, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z); - - // Enlarge them by the radius of the sphere - AABox4EnlargeWithExtent(Vec3::sReplicate(mRadius), bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z); - - // Test bounds of 4 children - Vec4 distance = RayAABox4(mStart, mInvDirection, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z); - - // Clear distance for invalid bounds - distance = Vec4::sSelect(Vec4::sReplicate(FLT_MAX), distance, Vec4::sLessOrEqual(inBoundsMinY, inBoundsMaxY)); - - // Sort so that highest values are first (we want to first process closer hits and we process stack top to bottom) - return SortReverseAndStore(distance, mCollector.GetPositiveEarlyOutFraction(), ioProperties, &mDistanceStack[inStackTop]); - } - - JPH_INLINE void VisitTriangle(uint inX, uint inY, uint inTriangle, Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2) - { - // Create sub shape id for this part - SubShapeID triangle_sub_shape_id = mShape2->EncodeSubShapeID(mSubShapeIDCreator2, inX, inY, inTriangle); - - // Determine active edges - uint8 active_edges = mShape2->GetEdgeFlags(inX, inY, inTriangle); - - Cast(inV0, inV1, inV2, active_edges, triangle_sub_shape_id); - } - - const HeightFieldShape * mShape2; - RayInvDirection mInvDirection; - SubShapeIDCreator mSubShapeIDCreator2; - float mDistanceStack[cStackSize]; - }; - - JPH_ASSERT(inShape->GetSubType() == EShapeSubType::HeightField); - const HeightFieldShape *shape = static_cast(inShape); - - Visitor visitor(inShapeCast, inShapeCastSettings, inScale, inCenterOfMassTransform2, inSubShapeIDCreator1, ioCollector); - visitor.mShape2 = shape; - visitor.mInvDirection.Set(inShapeCast.mDirection); - visitor.mSubShapeIDCreator2 = inSubShapeIDCreator2; - shape->WalkHeightField(visitor); -} - -struct HeightFieldShape::HSGetTrianglesContext -{ - HSGetTrianglesContext(const HeightFieldShape *inShape, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) : - mDecodeCtx(inShape), - mShape(inShape), - mLocalBox(Mat44::sInverseRotationTranslation(inRotation, inPositionCOM), inBox), - mHeightFieldScale(inScale), - mLocalToWorld(Mat44::sRotationTranslation(inRotation, inPositionCOM) * Mat44::sScale(inScale)), - mIsInsideOut(ScaleHelpers::IsInsideOut(inScale)) - { - } - - bool ShouldAbort() const - { - return mShouldAbort; - } - - bool ShouldVisitRangeBlock([[maybe_unused]] int inStackTop) const - { - return true; - } - - int VisitRangeBlock(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, [[maybe_unused]] int inStackTop) const - { - // Scale the bounding boxes of this node - Vec4 bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z; - AABox4Scale(mHeightFieldScale, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z); - - // Test which nodes collide - UVec4 collides = AABox4VsBox(mLocalBox, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z); - - // Filter out invalid bounding boxes - collides = UVec4::sAnd(collides, Vec4::sLessOrEqual(inBoundsMinY, inBoundsMaxY)); - - return CountAndSortTrues(collides, ioProperties); - } - - void VisitTriangle(uint inX, uint inY, [[maybe_unused]] uint inTriangle, Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2) - { - // When the buffer is full and we cannot process the triangles, abort the height field walk. The next time GetTrianglesNext is called we will continue here. - if (mNumTrianglesFound + 1 > mMaxTrianglesRequested) - { - mShouldAbort = true; - return; - } - - // Store vertices as Float3 - if (mIsInsideOut) - { - // Reverse vertices - (mLocalToWorld * inV0).StoreFloat3(mTriangleVertices++); - (mLocalToWorld * inV2).StoreFloat3(mTriangleVertices++); - (mLocalToWorld * inV1).StoreFloat3(mTriangleVertices++); - } - else - { - // Normal scale - (mLocalToWorld * inV0).StoreFloat3(mTriangleVertices++); - (mLocalToWorld * inV1).StoreFloat3(mTriangleVertices++); - (mLocalToWorld * inV2).StoreFloat3(mTriangleVertices++); - } - - // Decode material - if (mMaterials != nullptr) - *mMaterials++ = mShape->GetMaterial(inX, inY); - - // Accumulate triangles found - mNumTrianglesFound++; - } - - DecodingContext mDecodeCtx; - const HeightFieldShape * mShape; - OrientedBox mLocalBox; - Vec3 mHeightFieldScale; - Mat44 mLocalToWorld; - int mMaxTrianglesRequested; - Float3 * mTriangleVertices; - int mNumTrianglesFound; - const PhysicsMaterial ** mMaterials; - bool mShouldAbort; - bool mIsInsideOut; -}; - -void HeightFieldShape::GetTrianglesStart(GetTrianglesContext &ioContext, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) const -{ - static_assert(sizeof(HSGetTrianglesContext) <= sizeof(GetTrianglesContext), "GetTrianglesContext too small"); - JPH_ASSERT(IsAligned(&ioContext, alignof(HSGetTrianglesContext))); - - new (&ioContext) HSGetTrianglesContext(this, inBox, inPositionCOM, inRotation, inScale); -} - -int HeightFieldShape::GetTrianglesNext(GetTrianglesContext &ioContext, int inMaxTrianglesRequested, Float3 *outTriangleVertices, const PhysicsMaterial **outMaterials) const -{ - static_assert(cGetTrianglesMinTrianglesRequested >= 1, "cGetTrianglesMinTrianglesRequested is too small"); - JPH_ASSERT(inMaxTrianglesRequested >= cGetTrianglesMinTrianglesRequested); - - // Check if we're done - HSGetTrianglesContext &context = (HSGetTrianglesContext &)ioContext; - if (context.mDecodeCtx.IsDoneWalking()) - return 0; - - // Store parameters on context - context.mMaxTrianglesRequested = inMaxTrianglesRequested; - context.mTriangleVertices = outTriangleVertices; - context.mMaterials = outMaterials; - context.mShouldAbort = false; // Reset the abort flag - context.mNumTrianglesFound = 0; - - // Continue (or start) walking the height field - context.mDecodeCtx.WalkHeightField(context); - return context.mNumTrianglesFound; -} - -void HeightFieldShape::sCollideConvexVsHeightField(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, [[maybe_unused]] const ShapeFilter &inShapeFilter) -{ - JPH_PROFILE_FUNCTION(); - - // Get the shapes - JPH_ASSERT(inShape1->GetType() == EShapeType::Convex); - JPH_ASSERT(inShape2->GetType() == EShapeType::HeightField); - const ConvexShape *shape1 = static_cast(inShape1); - const HeightFieldShape *shape2 = static_cast(inShape2); - - struct Visitor : public CollideConvexVsTriangles - { - using CollideConvexVsTriangles::CollideConvexVsTriangles; - - JPH_INLINE bool ShouldAbort() const - { - return mCollector.ShouldEarlyOut(); - } - - JPH_INLINE bool ShouldVisitRangeBlock([[maybe_unused]] int inStackTop) const - { - return true; - } - - JPH_INLINE int VisitRangeBlock(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, [[maybe_unused]] int inStackTop) const - { - // Scale the bounding boxes of this node - Vec4 bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z; - AABox4Scale(mScale2, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z); - - // Test which nodes collide - UVec4 collides = AABox4VsBox(mBoundsOf1InSpaceOf2, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z); - - // Filter out invalid bounding boxes - collides = UVec4::sAnd(collides, Vec4::sLessOrEqual(inBoundsMinY, inBoundsMaxY)); - - return CountAndSortTrues(collides, ioProperties); - } - - JPH_INLINE void VisitTriangle(uint inX, uint inY, uint inTriangle, Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2) - { - // Create ID for triangle - SubShapeID triangle_sub_shape_id = mShape2->EncodeSubShapeID(mSubShapeIDCreator2, inX, inY, inTriangle); - - // Determine active edges - uint8 active_edges = mShape2->GetEdgeFlags(inX, inY, inTriangle); - - Collide(inV0, inV1, inV2, active_edges, triangle_sub_shape_id); - } - - const HeightFieldShape * mShape2; - SubShapeIDCreator mSubShapeIDCreator2; - }; - - Visitor visitor(shape1, inScale1, inScale2, inCenterOfMassTransform1, inCenterOfMassTransform2, inSubShapeIDCreator1.GetID(), inCollideShapeSettings, ioCollector); - visitor.mShape2 = shape2; - visitor.mSubShapeIDCreator2 = inSubShapeIDCreator2; - shape2->WalkHeightField(visitor); -} - -void HeightFieldShape::sCollideSphereVsHeightField(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, [[maybe_unused]] const ShapeFilter &inShapeFilter) -{ - JPH_PROFILE_FUNCTION(); - - // Get the shapes - JPH_ASSERT(inShape1->GetSubType() == EShapeSubType::Sphere); - JPH_ASSERT(inShape2->GetType() == EShapeType::HeightField); - const SphereShape *shape1 = static_cast(inShape1); - const HeightFieldShape *shape2 = static_cast(inShape2); - - struct Visitor : public CollideSphereVsTriangles - { - using CollideSphereVsTriangles::CollideSphereVsTriangles; - - JPH_INLINE bool ShouldAbort() const - { - return mCollector.ShouldEarlyOut(); - } - - JPH_INLINE bool ShouldVisitRangeBlock([[maybe_unused]] int inStackTop) const - { - return true; - } - - JPH_INLINE int VisitRangeBlock(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, [[maybe_unused]] int inStackTop) const - { - // Scale the bounding boxes of this node - Vec4 bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z; - AABox4Scale(mScale2, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z); - - // Test which nodes collide - UVec4 collides = AABox4VsSphere(mSphereCenterIn2, mRadiusPlusMaxSeparationSq, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z); - - // Filter out invalid bounding boxes - collides = UVec4::sAnd(collides, Vec4::sLessOrEqual(inBoundsMinY, inBoundsMaxY)); - - return CountAndSortTrues(collides, ioProperties); - } - - JPH_INLINE void VisitTriangle(uint inX, uint inY, uint inTriangle, Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2) - { - // Create ID for triangle - SubShapeID triangle_sub_shape_id = mShape2->EncodeSubShapeID(mSubShapeIDCreator2, inX, inY, inTriangle); - - // Determine active edges - uint8 active_edges = mShape2->GetEdgeFlags(inX, inY, inTriangle); - - Collide(inV0, inV1, inV2, active_edges, triangle_sub_shape_id); - } - - const HeightFieldShape * mShape2; - SubShapeIDCreator mSubShapeIDCreator2; - }; - - Visitor visitor(shape1, inScale1, inScale2, inCenterOfMassTransform1, inCenterOfMassTransform2, inSubShapeIDCreator1.GetID(), inCollideShapeSettings, ioCollector); - visitor.mShape2 = shape2; - visitor.mSubShapeIDCreator2 = inSubShapeIDCreator2; - shape2->WalkHeightField(visitor); -} - -void HeightFieldShape::SaveBinaryState(StreamOut &inStream) const -{ - Shape::SaveBinaryState(inStream); - - inStream.Write(mOffset); - inStream.Write(mScale); - inStream.Write(mSampleCount); - inStream.Write(mBlockSize); - inStream.Write(mBitsPerSample); - inStream.Write(mMinSample); - inStream.Write(mMaxSample); - inStream.Write(mRangeBlocks); - inStream.Write(mHeightSamples); - inStream.Write(mActiveEdges); - inStream.Write(mMaterialIndices); - inStream.Write(mNumBitsPerMaterialIndex); -} - -void HeightFieldShape::RestoreBinaryState(StreamIn &inStream) -{ - Shape::RestoreBinaryState(inStream); - - inStream.Read(mOffset); - inStream.Read(mScale); - inStream.Read(mSampleCount); - inStream.Read(mBlockSize); - inStream.Read(mBitsPerSample); - inStream.Read(mMinSample); - inStream.Read(mMaxSample); - inStream.Read(mRangeBlocks); - inStream.Read(mHeightSamples); - inStream.Read(mActiveEdges); - inStream.Read(mMaterialIndices); - inStream.Read(mNumBitsPerMaterialIndex); - - CacheValues(); -} - -void HeightFieldShape::SaveMaterialState(PhysicsMaterialList &outMaterials) const -{ - outMaterials = mMaterials; -} - -void HeightFieldShape::RestoreMaterialState(const PhysicsMaterialRefC *inMaterials, uint inNumMaterials) -{ - mMaterials.assign(inMaterials, inMaterials + inNumMaterials); -} - -Shape::Stats HeightFieldShape::GetStats() const -{ - return Stats( - sizeof(*this) - + mMaterials.size() * sizeof(Ref) - + mRangeBlocks.size() * sizeof(RangeBlock) - + mHeightSamples.size() * sizeof(uint8) - + mActiveEdges.size() * sizeof(uint8) - + mMaterialIndices.size() * sizeof(uint8), - mHeightSamples.empty()? 0 : Square(mSampleCount - 1) * 2); -} - -void HeightFieldShape::sRegister() -{ - ShapeFunctions &f = ShapeFunctions::sGet(EShapeSubType::HeightField); - f.mConstruct = []() -> Shape * { return new HeightFieldShape; }; - f.mColor = Color::sPurple; - - for (EShapeSubType s : sConvexSubShapeTypes) - { - CollisionDispatch::sRegisterCollideShape(s, EShapeSubType::HeightField, sCollideConvexVsHeightField); - CollisionDispatch::sRegisterCastShape(s, EShapeSubType::HeightField, sCastConvexVsHeightField); - - CollisionDispatch::sRegisterCastShape(EShapeSubType::HeightField, s, CollisionDispatch::sReversedCastShape); - CollisionDispatch::sRegisterCollideShape(EShapeSubType::HeightField, s, CollisionDispatch::sReversedCollideShape); - } - - // Specialized collision functions - CollisionDispatch::sRegisterCollideShape(EShapeSubType::Sphere, EShapeSubType::HeightField, sCollideSphereVsHeightField); - CollisionDispatch::sRegisterCastShape(EShapeSubType::Sphere, EShapeSubType::HeightField, sCastSphereVsHeightField); -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/HeightFieldShape.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/HeightFieldShape.h deleted file mode 100644 index e3a855ed12f..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/HeightFieldShape.h +++ /dev/null @@ -1,349 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#ifdef JPH_DEBUG_RENDERER - #include -#endif // JPH_DEBUG_RENDERER - -JPH_NAMESPACE_BEGIN - -class ConvexShape; -class CollideShapeSettings; -class TempAllocator; - -/// Constants for HeightFieldShape, this was moved out of the HeightFieldShape because of a linker bug -namespace HeightFieldShapeConstants -{ - /// Value used to create gaps in the height field - constexpr float cNoCollisionValue = FLT_MAX; - - /// Stack size to use during WalkHeightField - constexpr int cStackSize = 128; - - /// A position in the hierarchical grid is defined by a level (which grid), x and y position. We encode this in a single uint32 as: level << 28 | y << 14 | x - constexpr uint cNumBitsXY = 14; - constexpr uint cMaskBitsXY = (1 << cNumBitsXY) - 1; - constexpr uint cLevelShift = 2 * cNumBitsXY; - - /// When height samples are converted to 16 bit: - constexpr uint16 cNoCollisionValue16 = 0xffff; ///< This is the magic value for 'no collision' - constexpr uint16 cMaxHeightValue16 = 0xfffe; ///< This is the maximum allowed height value -}; - -/// Class that constructs a HeightFieldShape -class JPH_EXPORT HeightFieldShapeSettings final : public ShapeSettings -{ -public: - JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, HeightFieldShapeSettings) - - /// Default constructor for deserialization - HeightFieldShapeSettings() = default; - - /// Create a height field shape of inSampleCount * inSampleCount vertices. - /// The height field is a surface defined by: inOffset + inScale * (x, inSamples[y * inSampleCount + x], y). - /// where x and y are integers in the range x and y e [0, inSampleCount - 1]. - /// inSampleCount: inSampleCount / mBlockSize must be minimally 2 and a power of 2 is the most efficient in terms of performance and storage. - /// inSamples: inSampleCount^2 vertices. - /// inMaterialIndices: (inSampleCount - 1)^2 indices that index into inMaterialList. - HeightFieldShapeSettings(const float *inSamples, Vec3Arg inOffset, Vec3Arg inScale, uint32 inSampleCount, const uint8 *inMaterialIndices = nullptr, const PhysicsMaterialList &inMaterialList = PhysicsMaterialList()); - - // See: ShapeSettings - virtual ShapeResult Create() const override; - - /// Determine the minimal and maximal value of mHeightSamples (will ignore cNoCollisionValue) - /// @param outMinValue The minimal value of mHeightSamples or FLT_MAX if no samples have collision - /// @param outMaxValue The maximal value of mHeightSamples or -FLT_MAX if no samples have collision - /// @param outQuantizationScale (value - outMinValue) * outQuantizationScale quantizes a height sample to 16 bits - void DetermineMinAndMaxSample(float &outMinValue, float &outMaxValue, float &outQuantizationScale) const; - - /// Given mBlockSize, mSampleCount and mHeightSamples, calculate the amount of bits needed to stay below absolute error inMaxError - /// @param inMaxError Maximum allowed error in mHeightSamples after compression (note that this does not take mScale.Y into account) - /// @return Needed bits per sample in the range [1, 8]. - uint32 CalculateBitsPerSampleForError(float inMaxError) const; - - /// The height field is a surface defined by: mOffset + mScale * (x, mHeightSamples[y * mSampleCount + x], y). - /// where x and y are integers in the range x and y e [0, mSampleCount - 1]. - Vec3 mOffset = Vec3::sZero(); - Vec3 mScale = Vec3::sReplicate(1.0f); - uint32 mSampleCount = 0; - - /// Artificial minimal value of mHeightSamples, used for compression and can be used to update the terrain after creating with lower height values. If there are any lower values in mHeightSamples, this value will be ignored. - float mMinHeightValue = FLT_MAX; - - /// Artificial maximum value of mHeightSamples, used for compression and can be used to update the terrain after creating with higher height values. If there are any higher values in mHeightSamples, this value will be ignored. - float mMaxHeightValue = -FLT_MAX; - - /// The heightfield is divided in blocks of mBlockSize * mBlockSize * 2 triangles and the acceleration structure culls blocks only, - /// bigger block sizes reduce memory consumption but also reduce query performance. Sensible values are [2, 8], does not need to be - /// a power of 2. Note that at run-time we'll perform one more grid subdivision, so the effective block size is half of what is provided here. - uint32 mBlockSize = 2; - - /// How many bits per sample to use to compress the height field. Can be in the range [1, 8]. - /// Note that each sample is compressed relative to the min/max value of its block of mBlockSize * mBlockSize pixels so the effective precision is higher. - /// Also note that increasing mBlockSize saves more memory than reducing the amount of bits per sample. - uint32 mBitsPerSample = 8; - - /// An array of mSampleCount^2 height samples. Samples are stored in row major order, so the sample at (x, y) is at index y * mSampleCount + x. - Array mHeightSamples; - - /// An array of (mSampleCount - 1)^2 material indices. - Array mMaterialIndices; - - /// The materials of square at (x, y) is: mMaterials[mMaterialIndices[x + y * (mSampleCount - 1)]] - PhysicsMaterialList mMaterials; - - /// Cosine of the threshold angle (if the angle between the two triangles is bigger than this, the edge is active, note that a concave edge is always inactive). - /// Setting this value too small can cause ghost collisions with edges, setting it too big can cause depenetration artifacts (objects not depenetrating quickly). - /// Valid ranges are between cos(0 degrees) and cos(90 degrees). The default value is cos(5 degrees). - float mActiveEdgeCosThresholdAngle = 0.996195f; // cos(5 degrees) -}; - -/// A height field shape. Cannot be used as a dynamic object. -class JPH_EXPORT HeightFieldShape final : public Shape -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Constructor - HeightFieldShape() : Shape(EShapeType::HeightField, EShapeSubType::HeightField) { } - HeightFieldShape(const HeightFieldShapeSettings &inSettings, ShapeResult &outResult); - - // See Shape::MustBeStatic - virtual bool MustBeStatic() const override { return true; } - - /// Get the size of the height field. Note that this will always be rounded up to the nearest multiple of GetBlockSize(). - inline uint GetSampleCount() const { return mSampleCount; } - - /// Get the size of a block - inline uint GetBlockSize() const { return mBlockSize; } - - // See Shape::GetLocalBounds - virtual AABox GetLocalBounds() const override; - - // See Shape::GetSubShapeIDBitsRecursive - virtual uint GetSubShapeIDBitsRecursive() const override { return GetSubShapeIDBits(); } - - // See Shape::GetInnerRadius - virtual float GetInnerRadius() const override { return 0.0f; } - - // See Shape::GetMassProperties - virtual MassProperties GetMassProperties() const override; - - // See Shape::GetMaterial - virtual const PhysicsMaterial * GetMaterial(const SubShapeID &inSubShapeID) const override; - - /// Overload to get the material at a particular location - const PhysicsMaterial * GetMaterial(uint inX, uint inY) const; - - // See Shape::GetSurfaceNormal - virtual Vec3 GetSurfaceNormal(const SubShapeID &inSubShapeID, Vec3Arg inLocalSurfacePosition) const override; - - // See Shape::GetSupportingFace - virtual void GetSupportingFace(const SubShapeID &inSubShapeID, Vec3Arg inDirection, Vec3Arg inScale, Mat44Arg inCenterOfMassTransform, SupportingFace &outVertices) const override; - - // See Shape::GetSubmergedVolume - virtual void GetSubmergedVolume(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, const Plane &inSurface, float &outTotalVolume, float &outSubmergedVolume, Vec3 &outCenterOfBuoyancy JPH_IF_DEBUG_RENDERER(, RVec3Arg inBaseOffset)) const override { JPH_ASSERT(false, "Not supported"); } - -#ifdef JPH_DEBUG_RENDERER - // See Shape::Draw - virtual void Draw(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inUseMaterialColors, bool inDrawWireframe) const override; -#endif // JPH_DEBUG_RENDERER - - // See Shape::CastRay - virtual bool CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const override; - virtual void CastRay(const RayCast &inRay, const RayCastSettings &inRayCastSettings, const SubShapeIDCreator &inSubShapeIDCreator, CastRayCollector &ioCollector, const ShapeFilter &inShapeFilter = { }) const override; - - // See: Shape::CollidePoint - virtual void CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter = { }) const override; - - // See: Shape::CollideSoftBodyVertices - virtual void CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, SoftBodyVertex *ioVertices, uint inNumVertices, float inDeltaTime, Vec3Arg inDisplacementDueToGravity, int inCollidingShapeIndex) const override; - - // See Shape::GetTrianglesStart - virtual void GetTrianglesStart(GetTrianglesContext &ioContext, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) const override; - - // See Shape::GetTrianglesNext - virtual int GetTrianglesNext(GetTrianglesContext &ioContext, int inMaxTrianglesRequested, Float3 *outTriangleVertices, const PhysicsMaterial **outMaterials = nullptr) const override; - - /// Get height field position at sampled location (inX, inY). - /// where inX and inY are integers in the range inX e [0, mSampleCount - 1] and inY e [0, mSampleCount - 1]. - Vec3 GetPosition(uint inX, uint inY) const; - - /// Check if height field at sampled location (inX, inY) has collision (has a hole or not) - bool IsNoCollision(uint inX, uint inY) const; - - /// Projects inLocalPosition (a point in the space of the shape) along the Y axis onto the surface and returns it in outSurfacePosition. - /// When there is no surface position (because of a hole or because the point is outside the heightfield) the function will return false. - bool ProjectOntoSurface(Vec3Arg inLocalPosition, Vec3 &outSurfacePosition, SubShapeID &outSubShapeID) const; - - /// Get the height values of a block of data. - /// Note that the height values are decompressed so will be slightly different from what the shape was originally created with. - /// @param inX Start X position, must be a multiple of mBlockSize and in the range [0, mSampleCount - 1] - /// @param inY Start Y position, must be a multiple of mBlockSize and in the range [0, mSampleCount - 1] - /// @param inSizeX Number of samples in X direction, must be a multiple of mBlockSize and in the range [0, mSampleCount - inX] - /// @param inSizeY Number of samples in Y direction, must be a multiple of mBlockSize and in the range [0, mSampleCount - inX] - /// @param outHeights Returned height values, must be at least inSizeX * inSizeY floats. Values are returned in x-major order and can be cNoCollisionValue. - /// @param inHeightsStride Stride in floats between two consecutive rows of outHeights. - void GetHeights(uint inX, uint inY, uint inSizeX, uint inSizeY, float *outHeights, uint inHeightsStride) const; - - /// Set the height values of a block of data. - /// Note that this requires decompressing and recompressing a border of size mBlockSize in the negative x/y direction so will cause some precision loss. - /// @param inX Start X position, must be a multiple of mBlockSize and in the range [0, mSampleCount - 1] - /// @param inY Start Y position, must be a multiple of mBlockSize and in the range [0, mSampleCount - 1] - /// @param inSizeX Number of samples in X direction, must be a multiple of mBlockSize and in the range [0, mSampleCount - inX] - /// @param inSizeY Number of samples in Y direction, must be a multiple of mBlockSize and in the range [0, mSampleCount - inX] - /// @param inHeights The new height values to set, must be an array of inSizeX * inSizeY floats, can be cNoCollisionValue. - /// @param inHeightsStride Stride in floats between two consecutive rows of outHeights. - /// @param inAllocator Allocator to use for temporary memory - /// @param inActiveEdgeCosThresholdAngle Cosine of the threshold angle (if the angle between the two triangles is bigger than this, the edge is active, note that a concave edge is always inactive). - void SetHeights(uint inX, uint inY, uint inSizeX, uint inSizeY, const float *inHeights, uint inHeightsStride, TempAllocator &inAllocator, float inActiveEdgeCosThresholdAngle = 0.996195f); - - /// Get the current list of materials, the indices returned by GetMaterials() will index into this list. - const PhysicsMaterialList & GetMaterialList() const { return mMaterials; } - - /// Get the material indices of a block of data. - /// @param inX Start X position, must in the range [0, mSampleCount - 1] - /// @param inY Start Y position, must in the range [0, mSampleCount - 1] - /// @param inSizeX Number of samples in X direction - /// @param inSizeY Number of samples in Y direction - /// @param outMaterials Returned material indices, must be at least inSizeX * inSizeY uint8s. Values are returned in x-major order. - /// @param inMaterialsStride Stride in uint8s between two consecutive rows of outMaterials. - void GetMaterials(uint inX, uint inY, uint inSizeX, uint inSizeY, uint8 *outMaterials, uint inMaterialsStride) const; - - /// Set the material indices of a block of data. - /// @param inX Start X position, must in the range [0, mSampleCount - 1] - /// @param inY Start Y position, must in the range [0, mSampleCount - 1] - /// @param inSizeX Number of samples in X direction - /// @param inSizeY Number of samples in Y direction - /// @param inMaterials The new material indices, must be at least inSizeX * inSizeY uint8s. Values are returned in x-major order. - /// @param inMaterialsStride Stride in uint8s between two consecutive rows of inMaterials. - /// @param inMaterialList The material list to use for the new material indices or nullptr if the material list should not be updated - /// @param inAllocator Allocator to use for temporary memory - /// @return True if the material indices were set, false if the total number of materials exceeded 256 - bool SetMaterials(uint inX, uint inY, uint inSizeX, uint inSizeY, const uint8 *inMaterials, uint inMaterialsStride, const PhysicsMaterialList *inMaterialList, TempAllocator &inAllocator); - - // See Shape - virtual void SaveBinaryState(StreamOut &inStream) const override; - virtual void SaveMaterialState(PhysicsMaterialList &outMaterials) const override; - virtual void RestoreMaterialState(const PhysicsMaterialRefC *inMaterials, uint inNumMaterials) override; - - // See Shape::GetStats - virtual Stats GetStats() const override; - - // See Shape::GetVolume - virtual float GetVolume() const override { return 0; } - -#ifdef JPH_DEBUG_RENDERER - // Settings - static bool sDrawTriangleOutlines; -#endif // JPH_DEBUG_RENDERER - - // Register shape functions with the registry - static void sRegister(); - -protected: - // See: Shape::RestoreBinaryState - virtual void RestoreBinaryState(StreamIn &inStream) override; - -private: - class DecodingContext; ///< Context class for walking through all nodes of a heightfield - struct HSGetTrianglesContext; ///< Context class for GetTrianglesStart/Next - - /// Calculate commonly used values and store them in the shape - void CacheValues(); - - /// Calculate bit mask for all active edges in the heightfield for a specific region - void CalculateActiveEdges(uint inX, uint inY, uint inSizeX, uint inSizeY, const float *inHeights, uint inHeightsStartX, uint inHeightsStartY, uint inHeightsStride, float inHeightsScale, float inActiveEdgeCosThresholdAngle, TempAllocator &inAllocator); - - /// Calculate bit mask for all active edges in the heightfield - void CalculateActiveEdges(const HeightFieldShapeSettings &inSettings); - - /// Store material indices in the least amount of bits per index possible - void StoreMaterialIndices(const HeightFieldShapeSettings &inSettings); - - /// Get the amount of horizontal/vertical blocks - inline uint GetNumBlocks() const { return mSampleCount / mBlockSize; } - - /// Get the maximum level (amount of grids) of the tree - static inline uint sGetMaxLevel(uint inNumBlocks) { return 32 - CountLeadingZeros(inNumBlocks - 1); } - - /// Get the range block offset and stride for GetBlockOffsetAndScale - static inline void sGetRangeBlockOffsetAndStride(uint inNumBlocks, uint inMaxLevel, uint &outRangeBlockOffset, uint &outRangeBlockStride); - - /// For block (inBlockX, inBlockY) get the offset and scale needed to decode a uint8 height sample to a uint16 - inline void GetBlockOffsetAndScale(uint inBlockX, uint inBlockY, uint inRangeBlockOffset, uint inRangeBlockStride, float &outBlockOffset, float &outBlockScale) const; - - /// Get the height sample at position (inX, inY) - inline uint8 GetHeightSample(uint inX, uint inY) const; - - /// Faster version of GetPosition when block offset and scale are already known - inline Vec3 GetPosition(uint inX, uint inY, float inBlockOffset, float inBlockScale, bool &outNoCollision) const; - - /// Determine amount of bits needed to encode sub shape id - uint GetSubShapeIDBits() const; - - /// En/decode a sub shape ID. inX and inY specify the coordinate of the triangle. inTriangle == 0 is the lower triangle, inTriangle == 1 is the upper triangle. - inline SubShapeID EncodeSubShapeID(const SubShapeIDCreator &inCreator, uint inX, uint inY, uint inTriangle) const; - inline void DecodeSubShapeID(const SubShapeID &inSubShapeID, uint &outX, uint &outY, uint &outTriangle) const; - - /// Get the edge flags for a triangle - inline uint8 GetEdgeFlags(uint inX, uint inY, uint inTriangle) const; - - // Helper functions called by CollisionDispatch - static void sCollideConvexVsHeightField(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter); - static void sCollideSphereVsHeightField(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter); - static void sCastConvexVsHeightField(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector); - static void sCastSphereVsHeightField(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector); - - /// Visit the entire height field using a visitor pattern - /// Note: Used to be inlined but this triggers a bug in MSVC where it will not free the memory allocated by alloca which causes a stack overflow when WalkHeightField is called in a loop (clang does it correct) - template - void WalkHeightField(Visitor &ioVisitor) const; - - /// A block of 2x2 ranges used to form a hierarchical grid, ordered left top, right top, left bottom, right bottom - struct alignas(16) RangeBlock - { - uint16 mMin[4]; - uint16 mMax[4]; - }; - - /// For block (inBlockX, inBlockY) get the range block and the entry in the range block - inline void GetRangeBlock(uint inBlockX, uint inBlockY, uint inRangeBlockOffset, uint inRangeBlockStride, RangeBlock *&outBlock, uint &outIndexInBlock); - - /// Offset of first RangedBlock in grid per level - static const uint sGridOffsets[]; - - /// The height field is a surface defined by: mOffset + mScale * (x, mHeightSamples[y * mSampleCount + x], y). - /// where x and y are integers in the range x and y e [0, mSampleCount - 1]. - Vec3 mOffset = Vec3::sZero(); - Vec3 mScale = Vec3::sReplicate(1.0f); - - /// Height data - uint32 mSampleCount = 0; ///< See HeightFieldShapeSettings::mSampleCount - uint32 mBlockSize = 2; ///< See HeightFieldShapeSettings::mBlockSize - uint8 mBitsPerSample = 8; ///< See HeightFieldShapeSettings::mBitsPerSample - uint8 mSampleMask = 0xff; ///< All bits set for a sample: (1 << mBitsPerSample) - 1, used to indicate that there's no collision - uint16 mMinSample = HeightFieldShapeConstants::cNoCollisionValue16; ///< Min and max value in mHeightSamples quantized to 16 bit, for calculating bounding box - uint16 mMaxSample = HeightFieldShapeConstants::cNoCollisionValue16; - Array mRangeBlocks; ///< Hierarchical grid of range data describing the height variations within 1 block. The grid for level starts at offset sGridOffsets[] - Array mHeightSamples; ///< mBitsPerSample-bit height samples. Value [0, mMaxHeightValue] maps to highest detail grid in mRangeBlocks [mMin, mMax]. mNoCollisionValue is reserved to indicate no collision. - Array mActiveEdges; ///< (mSampleCount - 1)^2 * 3-bit active edge flags. - - /// Materials - PhysicsMaterialList mMaterials; ///< The materials of square at (x, y) is: mMaterials[mMaterialIndices[x + y * (mSampleCount - 1)]] - Array mMaterialIndices; ///< Compressed to the minimum amount of bits per material index (mSampleCount - 1) * (mSampleCount - 1) * mNumBitsPerMaterialIndex bits of data - uint32 mNumBitsPerMaterialIndex = 0; ///< Number of bits per material index - -#ifdef JPH_DEBUG_RENDERER - /// Temporary rendering data - mutable Array mGeometry; - mutable bool mCachedUseMaterialColors = false; ///< This is used to regenerate the triangle batch if the drawing settings change -#endif // JPH_DEBUG_RENDERER -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/MeshShape.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/MeshShape.cpp deleted file mode 100644 index cf5e864031e..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/MeshShape.cpp +++ /dev/null @@ -1,1244 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -#ifdef JPH_DEBUG_RENDERER -bool MeshShape::sDrawTriangleGroups = false; -bool MeshShape::sDrawTriangleOutlines = false; -#endif // JPH_DEBUG_RENDERER - -JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(MeshShapeSettings) -{ - JPH_ADD_BASE_CLASS(MeshShapeSettings, ShapeSettings) - - JPH_ADD_ATTRIBUTE(MeshShapeSettings, mTriangleVertices) - JPH_ADD_ATTRIBUTE(MeshShapeSettings, mIndexedTriangles) - JPH_ADD_ATTRIBUTE(MeshShapeSettings, mMaterials) - JPH_ADD_ATTRIBUTE(MeshShapeSettings, mMaxTrianglesPerLeaf) - JPH_ADD_ATTRIBUTE(MeshShapeSettings, mActiveEdgeCosThresholdAngle) -} - -// Codecs this mesh shape is using -using TriangleCodec = TriangleCodecIndexed8BitPackSOA4Flags; -using NodeCodec = NodeCodecQuadTreeHalfFloat<1>; - -// Get header for tree -static JPH_INLINE const NodeCodec::Header *sGetNodeHeader(const ByteBuffer &inTree) -{ - return inTree.Get(0); -} - -// Get header for triangles -static JPH_INLINE const TriangleCodec::TriangleHeader *sGetTriangleHeader(const ByteBuffer &inTree) -{ - return inTree.Get(NodeCodec::HeaderSize); -} - -MeshShapeSettings::MeshShapeSettings(const TriangleList &inTriangles, PhysicsMaterialList inMaterials) : - mMaterials(std::move(inMaterials)) -{ - Indexify(inTriangles, mTriangleVertices, mIndexedTriangles); - - Sanitize(); -} - -MeshShapeSettings::MeshShapeSettings(VertexList inVertices, IndexedTriangleList inTriangles, PhysicsMaterialList inMaterials) : - mTriangleVertices(std::move(inVertices)), - mIndexedTriangles(std::move(inTriangles)), - mMaterials(std::move(inMaterials)) -{ - Sanitize(); -} - -void MeshShapeSettings::Sanitize() -{ - // Remove degenerate and duplicate triangles - UnorderedSet triangles; - triangles.reserve(mIndexedTriangles.size()); - TriangleCodec::ValidationContext validation_ctx(mIndexedTriangles, mTriangleVertices); - for (int t = (int)mIndexedTriangles.size() - 1; t >= 0; --t) - { - const IndexedTriangle &tri = mIndexedTriangles[t]; - - if (tri.IsDegenerate(mTriangleVertices) // Degenerate triangle - || validation_ctx.IsDegenerate(tri) // Triangle is degenerate in the quantized space - || !triangles.insert(tri.GetLowestIndexFirst()).second) // Duplicate triangle - { - // The order of triangles doesn't matter (gets reordered while building the tree), so we can just swap the last triangle into this slot - mIndexedTriangles[t] = mIndexedTriangles.back(); - mIndexedTriangles.pop_back(); - } - } -} - -ShapeSettings::ShapeResult MeshShapeSettings::Create() const -{ - if (mCachedResult.IsEmpty()) - Ref shape = new MeshShape(*this, mCachedResult); - return mCachedResult; -} - -MeshShape::MeshShape(const MeshShapeSettings &inSettings, ShapeResult &outResult) : - Shape(EShapeType::Mesh, EShapeSubType::Mesh, inSettings, outResult) -{ - // Check if there are any triangles - if (inSettings.mIndexedTriangles.empty()) - { - outResult.SetError("Need triangles to create a mesh shape!"); - return; - } - - // Check triangles - TriangleCodec::ValidationContext validation_ctx(inSettings.mIndexedTriangles, inSettings.mTriangleVertices); - for (int t = (int)inSettings.mIndexedTriangles.size() - 1; t >= 0; --t) - { - const IndexedTriangle &triangle = inSettings.mIndexedTriangles[t]; - if (triangle.IsDegenerate(inSettings.mTriangleVertices) - || validation_ctx.IsDegenerate(triangle)) - { - outResult.SetError(StringFormat("Triangle %d is degenerate!", t)); - return; - } - else - { - // Check vertex indices - for (uint32 idx : triangle.mIdx) - if (idx >= inSettings.mTriangleVertices.size()) - { - outResult.SetError(StringFormat("Vertex index %u is beyond vertex list (size: %u)", idx, (uint)inSettings.mTriangleVertices.size())); - return; - } - } - } - - // Copy materials - mMaterials = inSettings.mMaterials; - if (!mMaterials.empty()) - { - // Validate materials - if (mMaterials.size() > (1 << FLAGS_MATERIAL_BITS)) - { - outResult.SetError(StringFormat("Supporting max %d materials per mesh", 1 << FLAGS_MATERIAL_BITS)); - return; - } - for (const IndexedTriangle &t : inSettings.mIndexedTriangles) - if (t.mMaterialIndex >= mMaterials.size()) - { - outResult.SetError(StringFormat("Triangle material %u is beyond material list (size: %u)", t.mMaterialIndex, (uint)mMaterials.size())); - return; - } - } - else - { - // No materials assigned, validate that all triangles use material index 0 - for (const IndexedTriangle &t : inSettings.mIndexedTriangles) - if (t.mMaterialIndex != 0) - { - outResult.SetError("No materials present, all triangles should have material index 0"); - return; - } - } - - // Check max triangles - if (inSettings.mMaxTrianglesPerLeaf < 1 || inSettings.mMaxTrianglesPerLeaf > MaxTrianglesPerLeaf) - { - outResult.SetError("Invalid max triangles per leaf"); - return; - } - - // Fill in active edge bits - IndexedTriangleList indexed_triangles = inSettings.mIndexedTriangles; // Copy indices since we're adding the 'active edge' flag - sFindActiveEdges(inSettings, indexed_triangles); - - // Create triangle splitter - TriangleSplitterBinning splitter(inSettings.mTriangleVertices, indexed_triangles); - - // Build tree - AABBTreeBuilder builder(splitter, inSettings.mMaxTrianglesPerLeaf); - AABBTreeBuilderStats builder_stats; - AABBTreeBuilder::Node *root = builder.Build(builder_stats); - - // Convert to buffer - AABBTreeToBuffer buffer; - const char *error = nullptr; - if (!buffer.Convert(inSettings.mTriangleVertices, root, error)) - { - outResult.SetError(error); - delete root; - return; - } - - // Kill tree - delete root; - - // Move data to this class - mTree.swap(buffer.GetBuffer()); - - // Check if we're not exceeding the amount of sub shape id bits - if (GetSubShapeIDBitsRecursive() > SubShapeID::MaxBits) - { - outResult.SetError("Mesh is too big and exceeds the amount of available sub shape ID bits"); - return; - } - - outResult.Set(this); -} - -void MeshShape::sFindActiveEdges(const MeshShapeSettings &inSettings, IndexedTriangleList &ioIndices) -{ - // A struct to hold the two vertex indices of an edge - struct Edge - { - Edge(int inIdx1, int inIdx2) : mIdx1(min(inIdx1, inIdx2)), mIdx2(max(inIdx1, inIdx2)) { } - - uint GetIndexInTriangle(const IndexedTriangle &inTriangle) const - { - for (uint edge_idx = 0; edge_idx < 3; ++edge_idx) - { - Edge edge(inTriangle.mIdx[edge_idx], inTriangle.mIdx[(edge_idx + 1) % 3]); - if (*this == edge) - return edge_idx; - } - - JPH_ASSERT(false); - return ~uint(0); - } - - bool operator == (const Edge &inRHS) const - { - return mIdx1 == inRHS.mIdx1 && mIdx2 == inRHS.mIdx2; - } - - int mIdx1; - int mIdx2; - }; - - JPH_MAKE_HASH_STRUCT(Edge, EdgeHash, t.mIdx1, t.mIdx2) - - // A struct to hold the triangles that are connected to an edge - struct TriangleIndices - { - uint mNumTriangles = 0; - uint mTriangleIndices[2]; - }; - - // Build a list of edge to triangles - using EdgeToTriangle = UnorderedMap; - EdgeToTriangle edge_to_triangle; - edge_to_triangle.reserve(ioIndices.size() * 3); - for (uint triangle_idx = 0; triangle_idx < ioIndices.size(); ++triangle_idx) - { - IndexedTriangle &triangle = ioIndices[triangle_idx]; - for (uint edge_idx = 0; edge_idx < 3; ++edge_idx) - { - Edge edge(triangle.mIdx[edge_idx], triangle.mIdx[(edge_idx + 1) % 3]); - TriangleIndices &indices = edge_to_triangle[edge]; - if (indices.mNumTriangles < 2) - { - // Store index of triangle that connects to this edge - indices.mTriangleIndices[indices.mNumTriangles] = triangle_idx; - indices.mNumTriangles++; - } - else - { - // 3 or more triangles share an edge, mark this edge as active - uint32 mask = 1 << (edge_idx + FLAGS_ACTIVE_EGDE_SHIFT); - JPH_ASSERT((triangle.mMaterialIndex & mask) == 0); - triangle.mMaterialIndex |= mask; - } - } - } - - // Walk over all edges and determine which ones are active - for (const EdgeToTriangle::value_type &edge : edge_to_triangle) - { - uint num_active = 0; - if (edge.second.mNumTriangles == 1) - { - // Edge is not shared, it is an active edge - num_active = 1; - } - else if (edge.second.mNumTriangles == 2) - { - // Simple shared edge, determine if edge is active based on the two adjacent triangles - const IndexedTriangle &triangle1 = ioIndices[edge.second.mTriangleIndices[0]]; - const IndexedTriangle &triangle2 = ioIndices[edge.second.mTriangleIndices[1]]; - - // Find which edge this is for both triangles - uint edge_idx1 = edge.first.GetIndexInTriangle(triangle1); - uint edge_idx2 = edge.first.GetIndexInTriangle(triangle2); - - // Construct a plane for triangle 1 (e1 = edge vertex 1, e2 = edge vertex 2, op = opposing vertex) - Vec3 triangle1_e1 = Vec3(inSettings.mTriangleVertices[triangle1.mIdx[edge_idx1]]); - Vec3 triangle1_e2 = Vec3(inSettings.mTriangleVertices[triangle1.mIdx[(edge_idx1 + 1) % 3]]); - Vec3 triangle1_op = Vec3(inSettings.mTriangleVertices[triangle1.mIdx[(edge_idx1 + 2) % 3]]); - Plane triangle1_plane = Plane::sFromPointsCCW(triangle1_e1, triangle1_e2, triangle1_op); - - // Construct a plane for triangle 2 - Vec3 triangle2_e1 = Vec3(inSettings.mTriangleVertices[triangle2.mIdx[edge_idx2]]); - Vec3 triangle2_e2 = Vec3(inSettings.mTriangleVertices[triangle2.mIdx[(edge_idx2 + 1) % 3]]); - Vec3 triangle2_op = Vec3(inSettings.mTriangleVertices[triangle2.mIdx[(edge_idx2 + 2) % 3]]); - Plane triangle2_plane = Plane::sFromPointsCCW(triangle2_e1, triangle2_e2, triangle2_op); - - // Determine if the edge is active - num_active = ActiveEdges::IsEdgeActive(triangle1_plane.GetNormal(), triangle2_plane.GetNormal(), triangle1_e2 - triangle1_e1, inSettings.mActiveEdgeCosThresholdAngle)? 2 : 0; - } - else - { - // More edges incoming, we've already marked all edges beyond the 2nd as active - num_active = 2; - } - - // Mark edges of all original triangles active - for (uint i = 0; i < num_active; ++i) - { - uint triangle_idx = edge.second.mTriangleIndices[i]; - IndexedTriangle &triangle = ioIndices[triangle_idx]; - uint edge_idx = edge.first.GetIndexInTriangle(triangle); - uint32 mask = 1 << (edge_idx + FLAGS_ACTIVE_EGDE_SHIFT); - JPH_ASSERT((triangle.mMaterialIndex & mask) == 0); - triangle.mMaterialIndex |= mask; - } - } -} - -MassProperties MeshShape::GetMassProperties() const -{ - // Object should always be static, return default mass properties - return MassProperties(); -} - -void MeshShape::DecodeSubShapeID(const SubShapeID &inSubShapeID, const void *&outTriangleBlock, uint32 &outTriangleIndex) const -{ - // Get block - SubShapeID triangle_idx_subshape_id; - uint32 block_id = inSubShapeID.PopID(NodeCodec::DecodingContext::sTriangleBlockIDBits(mTree), triangle_idx_subshape_id); - outTriangleBlock = NodeCodec::DecodingContext::sGetTriangleBlockStart(&mTree[0], block_id); - - // Fetch the triangle index - SubShapeID remainder; - outTriangleIndex = triangle_idx_subshape_id.PopID(NumTriangleBits, remainder); - JPH_ASSERT(remainder.IsEmpty(), "Invalid subshape ID"); -} - -uint MeshShape::GetMaterialIndex(const SubShapeID &inSubShapeID) const -{ - // Decode ID - const void *block_start; - uint32 triangle_idx; - DecodeSubShapeID(inSubShapeID, block_start, triangle_idx); - - // Fetch the flags - uint8 flags = TriangleCodec::DecodingContext::sGetFlags(block_start, triangle_idx); - return flags & FLAGS_MATERIAL_MASK; -} - -const PhysicsMaterial *MeshShape::GetMaterial(const SubShapeID &inSubShapeID) const -{ - // Return the default material if there are no materials on this shape - if (mMaterials.empty()) - return PhysicsMaterial::sDefault; - - return mMaterials[GetMaterialIndex(inSubShapeID)]; -} - -Vec3 MeshShape::GetSurfaceNormal(const SubShapeID &inSubShapeID, Vec3Arg inLocalSurfacePosition) const -{ - // Decode ID - const void *block_start; - uint32 triangle_idx; - DecodeSubShapeID(inSubShapeID, block_start, triangle_idx); - - // Decode triangle - Vec3 v1, v2, v3; - const TriangleCodec::DecodingContext triangle_ctx(sGetTriangleHeader(mTree)); - triangle_ctx.GetTriangle(block_start, triangle_idx, v1, v2, v3); - - // Calculate normal - return (v3 - v2).Cross(v1 - v2).Normalized(); -} - -void MeshShape::GetSupportingFace(const SubShapeID &inSubShapeID, Vec3Arg inDirection, Vec3Arg inScale, Mat44Arg inCenterOfMassTransform, SupportingFace &outVertices) const -{ - // Decode ID - const void *block_start; - uint32 triangle_idx; - DecodeSubShapeID(inSubShapeID, block_start, triangle_idx); - - // Decode triangle - const TriangleCodec::DecodingContext triangle_ctx(sGetTriangleHeader(mTree)); - outVertices.resize(3); - triangle_ctx.GetTriangle(block_start, triangle_idx, outVertices[0], outVertices[1], outVertices[2]); - - // Flip triangle if scaled inside out - if (ScaleHelpers::IsInsideOut(inScale)) - swap(outVertices[1], outVertices[2]); - - // Calculate transform with scale - Mat44 transform = inCenterOfMassTransform.PreScaled(inScale); - - // Transform to world space - for (Vec3 &v : outVertices) - v = transform * v; -} - -AABox MeshShape::GetLocalBounds() const -{ - const NodeCodec::Header *header = sGetNodeHeader(mTree); - return AABox(Vec3::sLoadFloat3Unsafe(header->mRootBoundsMin), Vec3::sLoadFloat3Unsafe(header->mRootBoundsMax)); -} - -uint MeshShape::GetSubShapeIDBitsRecursive() const -{ - return NodeCodec::DecodingContext::sTriangleBlockIDBits(mTree) + NumTriangleBits; -} - -template -JPH_INLINE void MeshShape::WalkTree(Visitor &ioVisitor) const -{ - const NodeCodec::Header *header = sGetNodeHeader(mTree); - NodeCodec::DecodingContext node_ctx(header); - - const TriangleCodec::DecodingContext triangle_ctx(sGetTriangleHeader(mTree)); - const uint8 *buffer_start = &mTree[0]; - node_ctx.WalkTree(buffer_start, triangle_ctx, ioVisitor); -} - -template -JPH_INLINE void MeshShape::WalkTreePerTriangle(const SubShapeIDCreator &inSubShapeIDCreator2, Visitor &ioVisitor) const -{ - struct ChainedVisitor - { - JPH_INLINE ChainedVisitor(Visitor &ioVisitor, const SubShapeIDCreator &inSubShapeIDCreator2, uint inTriangleBlockIDBits) : - mVisitor(ioVisitor), - mSubShapeIDCreator2(inSubShapeIDCreator2), - mTriangleBlockIDBits(inTriangleBlockIDBits) - { - } - - JPH_INLINE bool ShouldAbort() const - { - return mVisitor.ShouldAbort(); - } - - JPH_INLINE bool ShouldVisitNode(int inStackTop) const - { - return mVisitor.ShouldVisitNode(inStackTop); - } - - JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, int inStackTop) - { - return mVisitor.VisitNodes(inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ, ioProperties, inStackTop); - } - - JPH_INLINE void VisitTriangles(const TriangleCodec::DecodingContext &ioContext, const void *inTriangles, int inNumTriangles, uint32 inTriangleBlockID) - { - // Create ID for triangle block - SubShapeIDCreator block_sub_shape_id = mSubShapeIDCreator2.PushID(inTriangleBlockID, mTriangleBlockIDBits); - - // Decode vertices and flags - JPH_ASSERT(inNumTriangles <= MaxTrianglesPerLeaf); - Vec3 vertices[MaxTrianglesPerLeaf * 3]; - uint8 flags[MaxTrianglesPerLeaf]; - ioContext.Unpack(inTriangles, inNumTriangles, vertices, flags); - - int triangle_idx = 0; - for (const Vec3 *v = vertices, *v_end = vertices + inNumTriangles * 3; v < v_end; v += 3, triangle_idx++) - { - // Determine active edges - uint8 active_edges = (flags[triangle_idx] >> FLAGS_ACTIVE_EGDE_SHIFT) & FLAGS_ACTIVE_EDGE_MASK; - - // Create ID for triangle - SubShapeIDCreator triangle_sub_shape_id = block_sub_shape_id.PushID(triangle_idx, NumTriangleBits); - - mVisitor.VisitTriangle(v[0], v[1], v[2], active_edges, triangle_sub_shape_id.GetID()); - - // Check if we should early out now - if (mVisitor.ShouldAbort()) - break; - } - } - - Visitor & mVisitor; - SubShapeIDCreator mSubShapeIDCreator2; - uint mTriangleBlockIDBits; - }; - - ChainedVisitor visitor(ioVisitor, inSubShapeIDCreator2, NodeCodec::DecodingContext::sTriangleBlockIDBits(mTree)); - WalkTree(visitor); -} - -#ifdef JPH_DEBUG_RENDERER -void MeshShape::Draw(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inUseMaterialColors, bool inDrawWireframe) const -{ - // Reset the batch if we switch coloring mode - if (mCachedTrianglesColoredPerGroup != sDrawTriangleGroups || mCachedUseMaterialColors != inUseMaterialColors) - { - mGeometry = nullptr; - mCachedTrianglesColoredPerGroup = sDrawTriangleGroups; - mCachedUseMaterialColors = inUseMaterialColors; - } - - if (mGeometry == nullptr) - { - struct Visitor - { - JPH_INLINE bool ShouldAbort() const - { - return false; - } - - JPH_INLINE bool ShouldVisitNode(int inStackTop) const - { - return true; - } - - JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, int inStackTop) - { - UVec4 valid = UVec4::sOr(UVec4::sOr(Vec4::sLess(inBoundsMinX, inBoundsMaxX), Vec4::sLess(inBoundsMinY, inBoundsMaxY)), Vec4::sLess(inBoundsMinZ, inBoundsMaxZ)); - return CountAndSortTrues(valid, ioProperties); - } - - JPH_INLINE void VisitTriangles(const TriangleCodec::DecodingContext &ioContext, const void *inTriangles, int inNumTriangles, [[maybe_unused]] uint32 inTriangleBlockID) - { - JPH_ASSERT(inNumTriangles <= MaxTrianglesPerLeaf); - Vec3 vertices[MaxTrianglesPerLeaf * 3]; - ioContext.Unpack(inTriangles, inNumTriangles, vertices); - - if (mDrawTriangleGroups || !mUseMaterialColors || mMaterials.empty()) - { - // Single color for mesh - Color color = mDrawTriangleGroups? Color::sGetDistinctColor(mColorIdx++) : (mUseMaterialColors? PhysicsMaterial::sDefault->GetDebugColor() : Color::sWhite); - for (const Vec3 *v = vertices, *v_end = vertices + inNumTriangles * 3; v < v_end; v += 3) - mTriangles.push_back({ v[0], v[1], v[2], color }); - } - else - { - // Per triangle color - uint8 flags[MaxTrianglesPerLeaf]; - TriangleCodec::DecodingContext::sGetFlags(inTriangles, inNumTriangles, flags); - - const uint8 *f = flags; - for (const Vec3 *v = vertices, *v_end = vertices + inNumTriangles * 3; v < v_end; v += 3, f++) - mTriangles.push_back({ v[0], v[1], v[2], mMaterials[*f & FLAGS_MATERIAL_MASK]->GetDebugColor() }); - } - } - - Array & mTriangles; - const PhysicsMaterialList & mMaterials; - bool mUseMaterialColors; - bool mDrawTriangleGroups; - int mColorIdx = 0; - }; - - Array triangles; - Visitor visitor { triangles, mMaterials, mCachedUseMaterialColors, mCachedTrianglesColoredPerGroup }; - WalkTree(visitor); - mGeometry = new DebugRenderer::Geometry(inRenderer->CreateTriangleBatch(triangles), GetLocalBounds()); - } - - // Test if the shape is scaled inside out - DebugRenderer::ECullMode cull_mode = ScaleHelpers::IsInsideOut(inScale)? DebugRenderer::ECullMode::CullFrontFace : DebugRenderer::ECullMode::CullBackFace; - - // Determine the draw mode - DebugRenderer::EDrawMode draw_mode = inDrawWireframe? DebugRenderer::EDrawMode::Wireframe : DebugRenderer::EDrawMode::Solid; - - // Draw the geometry - inRenderer->DrawGeometry(inCenterOfMassTransform * Mat44::sScale(inScale), inColor, mGeometry, cull_mode, DebugRenderer::ECastShadow::On, draw_mode); - - if (sDrawTriangleOutlines) - { - struct Visitor - { - JPH_INLINE Visitor(DebugRenderer *inRenderer, RMat44Arg inTransform) : - mRenderer(inRenderer), - mTransform(inTransform) - { - } - - JPH_INLINE bool ShouldAbort() const - { - return false; - } - - JPH_INLINE bool ShouldVisitNode(int inStackTop) const - { - return true; - } - - JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, int inStackTop) - { - UVec4 valid = UVec4::sOr(UVec4::sOr(Vec4::sLess(inBoundsMinX, inBoundsMaxX), Vec4::sLess(inBoundsMinY, inBoundsMaxY)), Vec4::sLess(inBoundsMinZ, inBoundsMaxZ)); - return CountAndSortTrues(valid, ioProperties); - } - - JPH_INLINE void VisitTriangles(const TriangleCodec::DecodingContext &ioContext, const void *inTriangles, int inNumTriangles, uint32 inTriangleBlockID) - { - // Decode vertices and flags - JPH_ASSERT(inNumTriangles <= MaxTrianglesPerLeaf); - Vec3 vertices[MaxTrianglesPerLeaf * 3]; - uint8 flags[MaxTrianglesPerLeaf]; - ioContext.Unpack(inTriangles, inNumTriangles, vertices, flags); - - // Loop through triangles - const uint8 *f = flags; - for (Vec3 *v = vertices, *v_end = vertices + inNumTriangles * 3; v < v_end; v += 3, ++f) - { - // Loop through edges - for (uint edge_idx = 0; edge_idx < 3; ++edge_idx) - { - RVec3 v1 = mTransform * v[edge_idx]; - RVec3 v2 = mTransform * v[(edge_idx + 1) % 3]; - - // Draw active edge as a green arrow, other edges as grey - if (*f & (1 << (edge_idx + FLAGS_ACTIVE_EGDE_SHIFT))) - mRenderer->DrawArrow(v1, v2, Color::sGreen, 0.01f); - else - mRenderer->DrawLine(v1, v2, Color::sGrey); - } - } - } - - DebugRenderer * mRenderer; - RMat44 mTransform; - }; - - Visitor visitor { inRenderer, inCenterOfMassTransform.PreScaled(inScale) }; - WalkTree(visitor); - } -} -#endif // JPH_DEBUG_RENDERER - -bool MeshShape::CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const -{ - JPH_PROFILE_FUNCTION(); - - struct Visitor - { - JPH_INLINE explicit Visitor(RayCastResult &ioHit) : - mHit(ioHit) - { - } - - JPH_INLINE bool ShouldAbort() const - { - return mHit.mFraction <= 0.0f; - } - - JPH_INLINE bool ShouldVisitNode(int inStackTop) const - { - return mDistanceStack[inStackTop] < mHit.mFraction; - } - - JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, int inStackTop) - { - // Test bounds of 4 children - Vec4 distance = RayAABox4(mRayOrigin, mRayInvDirection, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ); - - // Sort so that highest values are first (we want to first process closer hits and we process stack top to bottom) - return SortReverseAndStore(distance, mHit.mFraction, ioProperties, &mDistanceStack[inStackTop]); - } - - JPH_INLINE void VisitTriangles(const TriangleCodec::DecodingContext &ioContext, const void *inTriangles, int inNumTriangles, uint32 inTriangleBlockID) - { - // Test against triangles - uint32 triangle_idx; - float fraction = ioContext.TestRay(mRayOrigin, mRayDirection, inTriangles, inNumTriangles, mHit.mFraction, triangle_idx); - if (fraction < mHit.mFraction) - { - mHit.mFraction = fraction; - mHit.mSubShapeID2 = mSubShapeIDCreator.PushID(inTriangleBlockID, mTriangleBlockIDBits).PushID(triangle_idx, NumTriangleBits).GetID(); - mReturnValue = true; - } - } - - RayCastResult & mHit; - Vec3 mRayOrigin; - Vec3 mRayDirection; - RayInvDirection mRayInvDirection; - uint mTriangleBlockIDBits; - SubShapeIDCreator mSubShapeIDCreator; - bool mReturnValue = false; - float mDistanceStack[NodeCodec::StackSize]; - }; - - Visitor visitor(ioHit); - visitor.mRayOrigin = inRay.mOrigin; - visitor.mRayDirection = inRay.mDirection; - visitor.mRayInvDirection.Set(inRay.mDirection); - visitor.mTriangleBlockIDBits = NodeCodec::DecodingContext::sTriangleBlockIDBits(mTree); - visitor.mSubShapeIDCreator = inSubShapeIDCreator; - WalkTree(visitor); - - return visitor.mReturnValue; -} - -void MeshShape::CastRay(const RayCast &inRay, const RayCastSettings &inRayCastSettings, const SubShapeIDCreator &inSubShapeIDCreator, CastRayCollector &ioCollector, const ShapeFilter &inShapeFilter) const -{ - JPH_PROFILE_FUNCTION(); - - // Test shape filter - if (!inShapeFilter.ShouldCollide(this, inSubShapeIDCreator.GetID())) - return; - - struct Visitor - { - JPH_INLINE explicit Visitor(CastRayCollector &ioCollector) : - mCollector(ioCollector) - { - } - - JPH_INLINE bool ShouldAbort() const - { - return mCollector.ShouldEarlyOut(); - } - - JPH_INLINE bool ShouldVisitNode(int inStackTop) const - { - return mDistanceStack[inStackTop] < mCollector.GetEarlyOutFraction(); - } - - JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, int inStackTop) - { - // Test bounds of 4 children - Vec4 distance = RayAABox4(mRayOrigin, mRayInvDirection, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ); - - // Sort so that highest values are first (we want to first process closer hits and we process stack top to bottom) - return SortReverseAndStore(distance, mCollector.GetEarlyOutFraction(), ioProperties, &mDistanceStack[inStackTop]); - } - - JPH_INLINE void VisitTriangle(Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2, [[maybe_unused]] uint8 inActiveEdges, SubShapeID inSubShapeID2) - { - // Back facing check - if (mBackFaceMode == EBackFaceMode::IgnoreBackFaces && (inV2 - inV0).Cross(inV1 - inV0).Dot(mRayDirection) < 0) - return; - - // Check the triangle - float fraction = RayTriangle(mRayOrigin, mRayDirection, inV0, inV1, inV2); - if (fraction < mCollector.GetEarlyOutFraction()) - { - RayCastResult hit; - hit.mBodyID = TransformedShape::sGetBodyID(mCollector.GetContext()); - hit.mFraction = fraction; - hit.mSubShapeID2 = inSubShapeID2; - mCollector.AddHit(hit); - } - } - - CastRayCollector & mCollector; - Vec3 mRayOrigin; - Vec3 mRayDirection; - RayInvDirection mRayInvDirection; - EBackFaceMode mBackFaceMode; - float mDistanceStack[NodeCodec::StackSize]; - }; - - Visitor visitor(ioCollector); - visitor.mBackFaceMode = inRayCastSettings.mBackFaceMode; - visitor.mRayOrigin = inRay.mOrigin; - visitor.mRayDirection = inRay.mDirection; - visitor.mRayInvDirection.Set(inRay.mDirection); - WalkTreePerTriangle(inSubShapeIDCreator, visitor); -} - -void MeshShape::CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter) const -{ - sCollidePointUsingRayCast(*this, inPoint, inSubShapeIDCreator, ioCollector, inShapeFilter); -} - -void MeshShape::CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, SoftBodyVertex *ioVertices, uint inNumVertices, [[maybe_unused]] float inDeltaTime, [[maybe_unused]] Vec3Arg inDisplacementDueToGravity, int inCollidingShapeIndex) const -{ - JPH_PROFILE_FUNCTION(); - - struct Visitor : public CollideSoftBodyVerticesVsTriangles - { - using CollideSoftBodyVerticesVsTriangles::CollideSoftBodyVerticesVsTriangles; - - JPH_INLINE bool ShouldAbort() const - { - return false; - } - - JPH_INLINE bool ShouldVisitNode([[maybe_unused]] int inStackTop) const - { - return mDistanceStack[inStackTop] < mClosestDistanceSq; - } - - JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, int inStackTop) - { - // Get distance to vertex - Vec4 dist_sq = AABox4DistanceSqToPoint(mLocalPosition, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ); - - // Sort so that highest values are first (we want to first process closer hits and we process stack top to bottom) - return SortReverseAndStore(dist_sq, mClosestDistanceSq, ioProperties, &mDistanceStack[inStackTop]); - } - - JPH_INLINE void VisitTriangle(Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2, [[maybe_unused]] uint8 inActiveEdges, [[maybe_unused]] SubShapeID inSubShapeID2) - { - ProcessTriangle(inV0, inV1, inV2); - } - - float mDistanceStack[NodeCodec::StackSize]; - }; - - Visitor visitor(inCenterOfMassTransform, inScale); - - for (SoftBodyVertex *v = ioVertices, *sbv_end = ioVertices + inNumVertices; v < sbv_end; ++v) - if (v->mInvMass > 0.0f) - { - visitor.StartVertex(*v); - WalkTreePerTriangle(SubShapeIDCreator(), visitor); - visitor.FinishVertex(*v, inCollidingShapeIndex); - } -} - -void MeshShape::sCastConvexVsMesh(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, [[maybe_unused]] const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector) -{ - JPH_PROFILE_FUNCTION(); - - struct Visitor : public CastConvexVsTriangles - { - using CastConvexVsTriangles::CastConvexVsTriangles; - - JPH_INLINE bool ShouldAbort() const - { - return mCollector.ShouldEarlyOut(); - } - - JPH_INLINE bool ShouldVisitNode(int inStackTop) const - { - return mDistanceStack[inStackTop] < mCollector.GetPositiveEarlyOutFraction(); - } - - JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, int inStackTop) - { - // Scale the bounding boxes of this node - Vec4 bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z; - AABox4Scale(mScale, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z); - - // Enlarge them by the casted shape's box extents - AABox4EnlargeWithExtent(mBoxExtent, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z); - - // Test bounds of 4 children - Vec4 distance = RayAABox4(mBoxCenter, mInvDirection, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z); - - // Sort so that highest values are first (we want to first process closer hits and we process stack top to bottom) - return SortReverseAndStore(distance, mCollector.GetPositiveEarlyOutFraction(), ioProperties, &mDistanceStack[inStackTop]); - } - - JPH_INLINE void VisitTriangle(Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2, uint8 inActiveEdges, SubShapeID inSubShapeID2) - { - Cast(inV0, inV1, inV2, inActiveEdges, inSubShapeID2); - } - - RayInvDirection mInvDirection; - Vec3 mBoxCenter; - Vec3 mBoxExtent; - float mDistanceStack[NodeCodec::StackSize]; - }; - - JPH_ASSERT(inShape->GetSubType() == EShapeSubType::Mesh); - const MeshShape *shape = static_cast(inShape); - - Visitor visitor(inShapeCast, inShapeCastSettings, inScale, inCenterOfMassTransform2, inSubShapeIDCreator1, ioCollector); - visitor.mInvDirection.Set(inShapeCast.mDirection); - visitor.mBoxCenter = inShapeCast.mShapeWorldBounds.GetCenter(); - visitor.mBoxExtent = inShapeCast.mShapeWorldBounds.GetExtent(); - shape->WalkTreePerTriangle(inSubShapeIDCreator2, visitor); -} - -void MeshShape::sCastSphereVsMesh(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, [[maybe_unused]] const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector) -{ - JPH_PROFILE_FUNCTION(); - - struct Visitor : public CastSphereVsTriangles - { - using CastSphereVsTriangles::CastSphereVsTriangles; - - JPH_INLINE bool ShouldAbort() const - { - return mCollector.ShouldEarlyOut(); - } - - JPH_INLINE bool ShouldVisitNode(int inStackTop) const - { - return mDistanceStack[inStackTop] < mCollector.GetPositiveEarlyOutFraction(); - } - - JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, int inStackTop) - { - // Scale the bounding boxes of this node - Vec4 bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z; - AABox4Scale(mScale, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z); - - // Enlarge them by the radius of the sphere - AABox4EnlargeWithExtent(Vec3::sReplicate(mRadius), bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z); - - // Test bounds of 4 children - Vec4 distance = RayAABox4(mStart, mInvDirection, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z); - - // Sort so that highest values are first (we want to first process closer hits and we process stack top to bottom) - return SortReverseAndStore(distance, mCollector.GetPositiveEarlyOutFraction(), ioProperties, &mDistanceStack[inStackTop]); - } - - JPH_INLINE void VisitTriangle(Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2, uint8 inActiveEdges, SubShapeID inSubShapeID2) - { - Cast(inV0, inV1, inV2, inActiveEdges, inSubShapeID2); - } - - RayInvDirection mInvDirection; - float mDistanceStack[NodeCodec::StackSize]; - }; - - JPH_ASSERT(inShape->GetSubType() == EShapeSubType::Mesh); - const MeshShape *shape = static_cast(inShape); - - Visitor visitor(inShapeCast, inShapeCastSettings, inScale, inCenterOfMassTransform2, inSubShapeIDCreator1, ioCollector); - visitor.mInvDirection.Set(inShapeCast.mDirection); - shape->WalkTreePerTriangle(inSubShapeIDCreator2, visitor); -} - -struct MeshShape::MSGetTrianglesContext -{ - JPH_INLINE MSGetTrianglesContext(const MeshShape *inShape, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) : - mDecodeCtx(sGetNodeHeader(inShape->mTree)), - mShape(inShape), - mLocalBox(Mat44::sInverseRotationTranslation(inRotation, inPositionCOM), inBox), - mMeshScale(inScale), - mLocalToWorld(Mat44::sRotationTranslation(inRotation, inPositionCOM) * Mat44::sScale(inScale)), - mIsInsideOut(ScaleHelpers::IsInsideOut(inScale)) - { - } - - JPH_INLINE bool ShouldAbort() const - { - return mShouldAbort; - } - - JPH_INLINE bool ShouldVisitNode([[maybe_unused]] int inStackTop) const - { - return true; - } - - JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, [[maybe_unused]] int inStackTop) const - { - // Scale the bounding boxes of this node - Vec4 bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z; - AABox4Scale(mMeshScale, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z); - - // Test which nodes collide - UVec4 collides = AABox4VsBox(mLocalBox, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z); - return CountAndSortTrues(collides, ioProperties); - } - - JPH_INLINE void VisitTriangles(const TriangleCodec::DecodingContext &ioContext, const void *inTriangles, int inNumTriangles, [[maybe_unused]] uint32 inTriangleBlockID) - { - // When the buffer is full and we cannot process the triangles, abort the tree walk. The next time GetTrianglesNext is called we will continue here. - if (mNumTrianglesFound + inNumTriangles > mMaxTrianglesRequested) - { - mShouldAbort = true; - return; - } - - // Decode vertices - JPH_ASSERT(inNumTriangles <= MaxTrianglesPerLeaf); - Vec3 vertices[MaxTrianglesPerLeaf * 3]; - ioContext.Unpack(inTriangles, inNumTriangles, vertices); - - // Store vertices as Float3 - if (mIsInsideOut) - { - // Scaled inside out, flip the triangles - for (const Vec3 *v = vertices, *v_end = v + 3 * inNumTriangles; v < v_end; v += 3) - { - (mLocalToWorld * v[0]).StoreFloat3(mTriangleVertices++); - (mLocalToWorld * v[2]).StoreFloat3(mTriangleVertices++); - (mLocalToWorld * v[1]).StoreFloat3(mTriangleVertices++); - } - } - else - { - // Normal scale - for (const Vec3 *v = vertices, *v_end = v + 3 * inNumTriangles; v < v_end; ++v) - (mLocalToWorld * *v).StoreFloat3(mTriangleVertices++); - } - - if (mMaterials != nullptr) - { - if (mShape->mMaterials.empty()) - { - // No materials, output default - const PhysicsMaterial *default_material = PhysicsMaterial::sDefault; - for (int m = 0; m < inNumTriangles; ++m) - *mMaterials++ = default_material; - } - else - { - // Decode triangle flags - uint8 flags[MaxTrianglesPerLeaf]; - TriangleCodec::DecodingContext::sGetFlags(inTriangles, inNumTriangles, flags); - - // Store materials - for (const uint8 *f = flags, *f_end = f + inNumTriangles; f < f_end; ++f) - *mMaterials++ = mShape->mMaterials[*f & FLAGS_MATERIAL_MASK].GetPtr(); - } - } - - // Accumulate triangles found - mNumTrianglesFound += inNumTriangles; - } - - NodeCodec::DecodingContext mDecodeCtx; - const MeshShape * mShape; - OrientedBox mLocalBox; - Vec3 mMeshScale; - Mat44 mLocalToWorld; - int mMaxTrianglesRequested; - Float3 * mTriangleVertices; - int mNumTrianglesFound; - const PhysicsMaterial ** mMaterials; - bool mShouldAbort; - bool mIsInsideOut; -}; - -void MeshShape::GetTrianglesStart(GetTrianglesContext &ioContext, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) const -{ - static_assert(sizeof(MSGetTrianglesContext) <= sizeof(GetTrianglesContext), "GetTrianglesContext too small"); - JPH_ASSERT(IsAligned(&ioContext, alignof(MSGetTrianglesContext))); - - new (&ioContext) MSGetTrianglesContext(this, inBox, inPositionCOM, inRotation, inScale); -} - -int MeshShape::GetTrianglesNext(GetTrianglesContext &ioContext, int inMaxTrianglesRequested, Float3 *outTriangleVertices, const PhysicsMaterial **outMaterials) const -{ - static_assert(cGetTrianglesMinTrianglesRequested >= MaxTrianglesPerLeaf, "cGetTrianglesMinTrianglesRequested is too small"); - JPH_ASSERT(inMaxTrianglesRequested >= cGetTrianglesMinTrianglesRequested); - - // Check if we're done - MSGetTrianglesContext &context = (MSGetTrianglesContext &)ioContext; - if (context.mDecodeCtx.IsDoneWalking()) - return 0; - - // Store parameters on context - context.mMaxTrianglesRequested = inMaxTrianglesRequested; - context.mTriangleVertices = outTriangleVertices; - context.mMaterials = outMaterials; - context.mShouldAbort = false; // Reset the abort flag - context.mNumTrianglesFound = 0; - - // Continue (or start) walking the tree - const TriangleCodec::DecodingContext triangle_ctx(sGetTriangleHeader(mTree)); - const uint8 *buffer_start = &mTree[0]; - context.mDecodeCtx.WalkTree(buffer_start, triangle_ctx, context); - return context.mNumTrianglesFound; -} - -void MeshShape::sCollideConvexVsMesh(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, [[maybe_unused]] const ShapeFilter &inShapeFilter) -{ - JPH_PROFILE_FUNCTION(); - - // Get the shapes - JPH_ASSERT(inShape1->GetType() == EShapeType::Convex); - JPH_ASSERT(inShape2->GetType() == EShapeType::Mesh); - const ConvexShape *shape1 = static_cast(inShape1); - const MeshShape *shape2 = static_cast(inShape2); - - struct Visitor : public CollideConvexVsTriangles - { - using CollideConvexVsTriangles::CollideConvexVsTriangles; - - JPH_INLINE bool ShouldAbort() const - { - return mCollector.ShouldEarlyOut(); - } - - JPH_INLINE bool ShouldVisitNode([[maybe_unused]] int inStackTop) const - { - return true; - } - - JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, [[maybe_unused]] int inStackTop) const - { - // Scale the bounding boxes of this node - Vec4 bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z; - AABox4Scale(mScale2, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z); - - // Test which nodes collide - UVec4 collides = AABox4VsBox(mBoundsOf1InSpaceOf2, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z); - return CountAndSortTrues(collides, ioProperties); - } - - JPH_INLINE void VisitTriangle(Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2, uint8 inActiveEdges, SubShapeID inSubShapeID2) - { - Collide(inV0, inV1, inV2, inActiveEdges, inSubShapeID2); - } - }; - - Visitor visitor(shape1, inScale1, inScale2, inCenterOfMassTransform1, inCenterOfMassTransform2, inSubShapeIDCreator1.GetID(), inCollideShapeSettings, ioCollector); - shape2->WalkTreePerTriangle(inSubShapeIDCreator2, visitor); -} - -void MeshShape::sCollideSphereVsMesh(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, [[maybe_unused]] const ShapeFilter &inShapeFilter) -{ - JPH_PROFILE_FUNCTION(); - - // Get the shapes - JPH_ASSERT(inShape1->GetSubType() == EShapeSubType::Sphere); - JPH_ASSERT(inShape2->GetType() == EShapeType::Mesh); - const SphereShape *shape1 = static_cast(inShape1); - const MeshShape *shape2 = static_cast(inShape2); - - struct Visitor : public CollideSphereVsTriangles - { - using CollideSphereVsTriangles::CollideSphereVsTriangles; - - JPH_INLINE bool ShouldAbort() const - { - return mCollector.ShouldEarlyOut(); - } - - JPH_INLINE bool ShouldVisitNode([[maybe_unused]] int inStackTop) const - { - return true; - } - - JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, [[maybe_unused]] int inStackTop) const - { - // Scale the bounding boxes of this node - Vec4 bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z; - AABox4Scale(mScale2, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z); - - // Test which nodes collide - UVec4 collides = AABox4VsSphere(mSphereCenterIn2, mRadiusPlusMaxSeparationSq, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z); - return CountAndSortTrues(collides, ioProperties); - } - - JPH_INLINE void VisitTriangle(Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2, uint8 inActiveEdges, SubShapeID inSubShapeID2) - { - Collide(inV0, inV1, inV2, inActiveEdges, inSubShapeID2); - } - }; - - Visitor visitor(shape1, inScale1, inScale2, inCenterOfMassTransform1, inCenterOfMassTransform2, inSubShapeIDCreator1.GetID(), inCollideShapeSettings, ioCollector); - shape2->WalkTreePerTriangle(inSubShapeIDCreator2, visitor); -} - -void MeshShape::SaveBinaryState(StreamOut &inStream) const -{ - Shape::SaveBinaryState(inStream); - - inStream.Write(static_cast(mTree)); // Make sure we use the Array<> overload -} - -void MeshShape::RestoreBinaryState(StreamIn &inStream) -{ - Shape::RestoreBinaryState(inStream); - - inStream.Read(static_cast(mTree)); // Make sure we use the Array<> overload -} - -void MeshShape::SaveMaterialState(PhysicsMaterialList &outMaterials) const -{ - outMaterials = mMaterials; -} - -void MeshShape::RestoreMaterialState(const PhysicsMaterialRefC *inMaterials, uint inNumMaterials) -{ - mMaterials.assign(inMaterials, inMaterials + inNumMaterials); -} - -Shape::Stats MeshShape::GetStats() const -{ - // Walk the tree to count the triangles - struct Visitor - { - JPH_INLINE bool ShouldAbort() const - { - return false; - } - - JPH_INLINE bool ShouldVisitNode([[maybe_unused]] int inStackTop) const - { - return true; - } - - JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, [[maybe_unused]] int inStackTop) const - { - // Visit all valid children - UVec4 valid = UVec4::sOr(UVec4::sOr(Vec4::sLess(inBoundsMinX, inBoundsMaxX), Vec4::sLess(inBoundsMinY, inBoundsMaxY)), Vec4::sLess(inBoundsMinZ, inBoundsMaxZ)); - return CountAndSortTrues(valid, ioProperties); - } - - JPH_INLINE void VisitTriangles([[maybe_unused]] const TriangleCodec::DecodingContext &ioContext, [[maybe_unused]] const void *inTriangles, int inNumTriangles, [[maybe_unused]] uint32 inTriangleBlockID) - { - mNumTriangles += inNumTriangles; - } - - uint mNumTriangles = 0; - }; - - Visitor visitor; - WalkTree(visitor); - - return Stats(sizeof(*this) + mMaterials.size() * sizeof(Ref) + mTree.size() * sizeof(uint8), visitor.mNumTriangles); -} - -void MeshShape::sRegister() -{ - ShapeFunctions &f = ShapeFunctions::sGet(EShapeSubType::Mesh); - f.mConstruct = []() -> Shape * { return new MeshShape; }; - f.mColor = Color::sRed; - - for (EShapeSubType s : sConvexSubShapeTypes) - { - CollisionDispatch::sRegisterCollideShape(s, EShapeSubType::Mesh, sCollideConvexVsMesh); - CollisionDispatch::sRegisterCastShape(s, EShapeSubType::Mesh, sCastConvexVsMesh); - - CollisionDispatch::sRegisterCastShape(EShapeSubType::Mesh, s, CollisionDispatch::sReversedCastShape); - CollisionDispatch::sRegisterCollideShape(EShapeSubType::Mesh, s, CollisionDispatch::sReversedCollideShape); - } - - // Specialized collision functions - CollisionDispatch::sRegisterCollideShape(EShapeSubType::Sphere, EShapeSubType::Mesh, sCollideSphereVsMesh); - CollisionDispatch::sRegisterCastShape(EShapeSubType::Sphere, EShapeSubType::Mesh, sCastSphereVsMesh); -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/MeshShape.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/MeshShape.h deleted file mode 100644 index 0483252345e..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/MeshShape.h +++ /dev/null @@ -1,207 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include -#include -#include -#ifdef JPH_DEBUG_RENDERER - #include -#endif // JPH_DEBUG_RENDERER - -JPH_NAMESPACE_BEGIN - -class ConvexShape; -class CollideShapeSettings; - -/// Class that constructs a MeshShape -class JPH_EXPORT MeshShapeSettings final : public ShapeSettings -{ -public: - JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, MeshShapeSettings) - - /// Default constructor for deserialization - MeshShapeSettings() = default; - - /// Create a mesh shape. - MeshShapeSettings(const TriangleList &inTriangles, PhysicsMaterialList inMaterials = PhysicsMaterialList()); - MeshShapeSettings(VertexList inVertices, IndexedTriangleList inTriangles, PhysicsMaterialList inMaterials = PhysicsMaterialList()); - - /// Sanitize the mesh data. Remove duplicate and degenerate triangles. This is called automatically when constructing the MeshShapeSettings with a list of (indexed-) triangles. - void Sanitize(); - - // See: ShapeSettings - virtual ShapeResult Create() const override; - - /// Vertices belonging to mIndexedTriangles - VertexList mTriangleVertices; - - /// Original list of indexed triangles (triangles will be reordered internally in the mesh shape). - /// Triangles must be provided in counter clockwise order. - /// For simulation, the triangles are considered to be single sided. - /// For ray casts you can choose to make triangles double sided by setting RayCastSettings::mBackFaceMode to EBackFaceMode::CollideWithBackFaces. - /// For collide shape tests you can use CollideShapeSettings::mBackFaceMode and for shape casts you can use ShapeCastSettings::mBackFaceModeTriangles. - IndexedTriangleList mIndexedTriangles; - - /// Materials assigned to the triangles. Each triangle specifies which material it uses through its mMaterialIndex - PhysicsMaterialList mMaterials; - - /// Maximum number of triangles in each leaf of the axis aligned box tree. This is a balance between memory and performance. Can be in the range [1, MeshShape::MaxTrianglesPerLeaf]. - /// Sensible values are between 4 (for better performance) and 8 (for less memory usage). - uint mMaxTrianglesPerLeaf = 8; - - /// Cosine of the threshold angle (if the angle between the two triangles is bigger than this, the edge is active, note that a concave edge is always inactive). - /// Setting this value too small can cause ghost collisions with edges, setting it too big can cause depenetration artifacts (objects not depenetrating quickly). - /// Valid ranges are between cos(0 degrees) and cos(90 degrees). The default value is cos(5 degrees). - float mActiveEdgeCosThresholdAngle = 0.996195f; // cos(5 degrees) -}; - -/// A mesh shape, consisting of triangles. Mesh shapes are mostly used for static geometry. -/// They can be used by dynamic or kinematic objects but only if they don't collide with other mesh or heightfield shapes as those collisions are currently not supported. -/// Note that if you make a mesh shape a dynamic or kinematic object, you need to provide a mass yourself as mesh shapes don't need to form a closed hull so don't have a well defined volume from which the mass can be calculated. -class JPH_EXPORT MeshShape final : public Shape -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Constructor - MeshShape() : Shape(EShapeType::Mesh, EShapeSubType::Mesh) { } - MeshShape(const MeshShapeSettings &inSettings, ShapeResult &outResult); - - // See Shape::MustBeStatic - virtual bool MustBeStatic() const override { return true; } - - // See Shape::GetLocalBounds - virtual AABox GetLocalBounds() const override; - - // See Shape::GetSubShapeIDBitsRecursive - virtual uint GetSubShapeIDBitsRecursive() const override; - - // See Shape::GetInnerRadius - virtual float GetInnerRadius() const override { return 0.0f; } - - // See Shape::GetMassProperties - virtual MassProperties GetMassProperties() const override; - - // See Shape::GetMaterial - virtual const PhysicsMaterial * GetMaterial(const SubShapeID &inSubShapeID) const override; - - /// Get the list of all materials - const PhysicsMaterialList & GetMaterialList() const { return mMaterials; } - - /// Determine which material index a particular sub shape uses (note that if there are no materials this function will return 0 so check the array size) - /// Note: This could for example be used to create a decorator shape around a mesh shape that overrides the GetMaterial call to replace a material with another material. - uint GetMaterialIndex(const SubShapeID &inSubShapeID) const; - - // See Shape::GetSurfaceNormal - virtual Vec3 GetSurfaceNormal(const SubShapeID &inSubShapeID, Vec3Arg inLocalSurfacePosition) const override; - - // See Shape::GetSupportingFace - virtual void GetSupportingFace(const SubShapeID &inSubShapeID, Vec3Arg inDirection, Vec3Arg inScale, Mat44Arg inCenterOfMassTransform, SupportingFace &outVertices) const override; - -#ifdef JPH_DEBUG_RENDERER - // See Shape::Draw - virtual void Draw(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inUseMaterialColors, bool inDrawWireframe) const override; -#endif // JPH_DEBUG_RENDERER - - // See Shape::CastRay - virtual bool CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const override; - virtual void CastRay(const RayCast &inRay, const RayCastSettings &inRayCastSettings, const SubShapeIDCreator &inSubShapeIDCreator, CastRayCollector &ioCollector, const ShapeFilter &inShapeFilter = { }) const override; - - /// See: Shape::CollidePoint - /// Note that for CollidePoint to work for a mesh shape, the mesh needs to be closed (a manifold) or multiple non-intersecting manifolds. Triangles may be facing the interior of the manifold. - /// Insideness is tested by counting the amount of triangles encountered when casting an infinite ray from inPoint. If the number of hits is odd we're inside, if it's even we're outside. - virtual void CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter = { }) const override; - - // See: Shape::CollideSoftBodyVertices - virtual void CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, SoftBodyVertex *ioVertices, uint inNumVertices, float inDeltaTime, Vec3Arg inDisplacementDueToGravity, int inCollidingShapeIndex) const override; - - // See Shape::GetTrianglesStart - virtual void GetTrianglesStart(GetTrianglesContext &ioContext, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) const override; - - // See Shape::GetTrianglesNext - virtual int GetTrianglesNext(GetTrianglesContext &ioContext, int inMaxTrianglesRequested, Float3 *outTriangleVertices, const PhysicsMaterial **outMaterials = nullptr) const override; - - // See Shape::GetSubmergedVolume - virtual void GetSubmergedVolume(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, const Plane &inSurface, float &outTotalVolume, float &outSubmergedVolume, Vec3 &outCenterOfBuoyancy JPH_IF_DEBUG_RENDERER(, RVec3Arg inBaseOffset)) const override { JPH_ASSERT(false, "Not supported"); } - - // See Shape - virtual void SaveBinaryState(StreamOut &inStream) const override; - virtual void SaveMaterialState(PhysicsMaterialList &outMaterials) const override; - virtual void RestoreMaterialState(const PhysicsMaterialRefC *inMaterials, uint inNumMaterials) override; - - // See Shape::GetStats - virtual Stats GetStats() const override; - - // See Shape::GetVolume - virtual float GetVolume() const override { return 0; } - -#ifdef JPH_DEBUG_RENDERER - // Settings - static bool sDrawTriangleGroups; - static bool sDrawTriangleOutlines; -#endif // JPH_DEBUG_RENDERER - - // Register shape functions with the registry - static void sRegister(); - -protected: - // See: Shape::RestoreBinaryState - virtual void RestoreBinaryState(StreamIn &inStream) override; - -private: - struct MSGetTrianglesContext; ///< Context class for GetTrianglesStart/Next - - static constexpr int NumTriangleBits = 3; ///< How many bits to reserve to encode the triangle index - static constexpr int MaxTrianglesPerLeaf = 1 << NumTriangleBits; ///< Number of triangles that are stored max per leaf aabb node - - /// Find and flag active edges - static void sFindActiveEdges(const MeshShapeSettings &inSettings, IndexedTriangleList &ioIndices); - - /// Visit the entire tree using a visitor pattern - template - void WalkTree(Visitor &ioVisitor) const; - - /// Same as above but with a callback per triangle instead of per block of triangles - template - void WalkTreePerTriangle(const SubShapeIDCreator &inSubShapeIDCreator2, Visitor &ioVisitor) const; - - /// Decode a sub shape ID - inline void DecodeSubShapeID(const SubShapeID &inSubShapeID, const void *&outTriangleBlock, uint32 &outTriangleIndex) const; - - // Helper functions called by CollisionDispatch - static void sCollideConvexVsMesh(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter); - static void sCollideSphereVsMesh(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter); - static void sCastConvexVsMesh(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector); - static void sCastSphereVsMesh(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector); - - /// Materials assigned to the triangles. Each triangle specifies which material it uses through its mMaterialIndex - PhysicsMaterialList mMaterials; - - ByteBuffer mTree; ///< Resulting packed data structure - - /// 8 bit flags stored per triangle - enum ETriangleFlags - { - /// Material index - FLAGS_MATERIAL_BITS = 5, - FLAGS_MATERIAL_MASK = (1 << FLAGS_MATERIAL_BITS) - 1, - - /// Active edge bits - FLAGS_ACTIVE_EGDE_SHIFT = FLAGS_MATERIAL_BITS, - FLAGS_ACTIVE_EDGE_BITS = 3, - FLAGS_ACTIVE_EDGE_MASK = (1 << FLAGS_ACTIVE_EDGE_BITS) - 1 - }; - -#ifdef JPH_DEBUG_RENDERER - mutable DebugRenderer::GeometryRef mGeometry; ///< Debug rendering data - mutable bool mCachedTrianglesColoredPerGroup = false; ///< This is used to regenerate the triangle batch if the drawing settings change - mutable bool mCachedUseMaterialColors = false; ///< This is used to regenerate the triangle batch if the drawing settings change -#endif // JPH_DEBUG_RENDERER -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/MutableCompoundShape.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/MutableCompoundShape.cpp deleted file mode 100644 index 61802e783dc..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/MutableCompoundShape.cpp +++ /dev/null @@ -1,560 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(MutableCompoundShapeSettings) -{ - JPH_ADD_BASE_CLASS(MutableCompoundShapeSettings, CompoundShapeSettings) -} - -ShapeSettings::ShapeResult MutableCompoundShapeSettings::Create() const -{ - // Build a mutable compound shape - if (mCachedResult.IsEmpty()) - Ref shape = new MutableCompoundShape(*this, mCachedResult); - - return mCachedResult; -} - -MutableCompoundShape::MutableCompoundShape(const MutableCompoundShapeSettings &inSettings, ShapeResult &outResult) : - CompoundShape(EShapeSubType::MutableCompound, inSettings, outResult) -{ - mSubShapes.reserve(inSettings.mSubShapes.size()); - for (const CompoundShapeSettings::SubShapeSettings &shape : inSettings.mSubShapes) - { - // Start constructing the runtime sub shape - SubShape out_shape; - if (!out_shape.FromSettings(shape, outResult)) - return; - - mSubShapes.push_back(out_shape); - } - - AdjustCenterOfMass(); - - CalculateSubShapeBounds(0, (uint)mSubShapes.size()); - - // Check if we're not exceeding the amount of sub shape id bits - if (GetSubShapeIDBitsRecursive() > SubShapeID::MaxBits) - { - outResult.SetError("Compound hierarchy is too deep and exceeds the amount of available sub shape ID bits"); - return; - } - - outResult.Set(this); -} - -void MutableCompoundShape::AdjustCenterOfMass() -{ - // First calculate the delta of the center of mass - float mass = 0.0f; - Vec3 center_of_mass = Vec3::sZero(); - for (const CompoundShape::SubShape &sub_shape : mSubShapes) - { - MassProperties child = sub_shape.mShape->GetMassProperties(); - mass += child.mMass; - center_of_mass += sub_shape.GetPositionCOM() * child.mMass; - } - if (mass > 0.0f) - center_of_mass /= mass; - - // Now adjust all shapes to recenter around center of mass - for (CompoundShape::SubShape &sub_shape : mSubShapes) - sub_shape.SetPositionCOM(sub_shape.GetPositionCOM() - center_of_mass); - - // And adjust the center of mass for this shape in the opposite direction - mCenterOfMass += center_of_mass; -} - -void MutableCompoundShape::CalculateLocalBounds() -{ - uint num_blocks = GetNumBlocks(); - if (num_blocks > 0) - { - // Initialize min/max for first block - const Bounds *bounds = mSubShapeBounds.data(); - Vec4 min_x = bounds->mMinX; - Vec4 min_y = bounds->mMinY; - Vec4 min_z = bounds->mMinZ; - Vec4 max_x = bounds->mMaxX; - Vec4 max_y = bounds->mMaxY; - Vec4 max_z = bounds->mMaxZ; - - // Accumulate other blocks - const Bounds *bounds_end = bounds + num_blocks; - for (++bounds; bounds < bounds_end; ++bounds) - { - min_x = Vec4::sMin(min_x, bounds->mMinX); - min_y = Vec4::sMin(min_y, bounds->mMinY); - min_z = Vec4::sMin(min_z, bounds->mMinZ); - max_x = Vec4::sMax(max_x, bounds->mMaxX); - max_y = Vec4::sMax(max_y, bounds->mMaxY); - max_z = Vec4::sMax(max_z, bounds->mMaxZ); - } - - // Calculate resulting bounding box - mLocalBounds.mMin.SetX(min_x.ReduceMin()); - mLocalBounds.mMin.SetY(min_y.ReduceMin()); - mLocalBounds.mMin.SetZ(min_z.ReduceMin()); - mLocalBounds.mMax.SetX(max_x.ReduceMax()); - mLocalBounds.mMax.SetY(max_y.ReduceMax()); - mLocalBounds.mMax.SetZ(max_z.ReduceMax()); - } - else - { - // There are no subshapes, set the bounding box to invalid - mLocalBounds.SetEmpty(); - } - - // Cache the inner radius as it can take a while to recursively iterate over all sub shapes - CalculateInnerRadius(); -} - -void MutableCompoundShape::EnsureSubShapeBoundsCapacity() -{ - // Check if we have enough space - uint new_capacity = ((uint)mSubShapes.size() + 3) >> 2; - if (mSubShapeBounds.size() < new_capacity) - mSubShapeBounds.resize(new_capacity); -} - -void MutableCompoundShape::CalculateSubShapeBounds(uint inStartIdx, uint inNumber) -{ - // Ensure that we have allocated the required space for mSubShapeBounds - EnsureSubShapeBoundsCapacity(); - - // Loop over blocks of 4 sub shapes - for (uint sub_shape_idx_start = inStartIdx & ~uint(3), sub_shape_idx_end = inStartIdx + inNumber; sub_shape_idx_start < sub_shape_idx_end; sub_shape_idx_start += 4) - { - Mat44 bounds_min; - Mat44 bounds_max; - - AABox sub_shape_bounds; - for (uint col = 0; col < 4; ++col) - { - uint sub_shape_idx = sub_shape_idx_start + col; - if (sub_shape_idx < mSubShapes.size()) // else reuse sub_shape_bounds from previous iteration - { - const SubShape &sub_shape = mSubShapes[sub_shape_idx]; - - // Tranform the shape's bounds into our local space - Mat44 transform = Mat44::sRotationTranslation(sub_shape.GetRotation(), sub_shape.GetPositionCOM()); - - // Get the bounding box - sub_shape_bounds = sub_shape.mShape->GetWorldSpaceBounds(transform, Vec3::sReplicate(1.0f)); - } - - // Put the bounds as columns in a matrix - bounds_min.SetColumn3(col, sub_shape_bounds.mMin); - bounds_max.SetColumn3(col, sub_shape_bounds.mMax); - } - - // Transpose to go to strucucture of arrays format - Mat44 bounds_min_t = bounds_min.Transposed(); - Mat44 bounds_max_t = bounds_max.Transposed(); - - // Store in our bounds array - Bounds &bounds = mSubShapeBounds[sub_shape_idx_start >> 2]; - bounds.mMinX = bounds_min_t.GetColumn4(0); - bounds.mMinY = bounds_min_t.GetColumn4(1); - bounds.mMinZ = bounds_min_t.GetColumn4(2); - bounds.mMaxX = bounds_max_t.GetColumn4(0); - bounds.mMaxY = bounds_max_t.GetColumn4(1); - bounds.mMaxZ = bounds_max_t.GetColumn4(2); - } - - CalculateLocalBounds(); -} - -uint MutableCompoundShape::AddShape(Vec3Arg inPosition, QuatArg inRotation, const Shape *inShape, uint32 inUserData) -{ - SubShape sub_shape; - sub_shape.mShape = inShape; - sub_shape.mUserData = inUserData; - sub_shape.SetTransform(inPosition, inRotation, mCenterOfMass); - mSubShapes.push_back(sub_shape); - uint shape_idx = (uint)mSubShapes.size() - 1; - - CalculateSubShapeBounds(shape_idx, 1); - - return shape_idx; -} - -void MutableCompoundShape::RemoveShape(uint inIndex) -{ - mSubShapes.erase(mSubShapes.begin() + inIndex); - - uint num_bounds = (uint)mSubShapes.size() - inIndex; - if (num_bounds > 0) - CalculateSubShapeBounds(inIndex, num_bounds); - else - CalculateLocalBounds(); -} - -void MutableCompoundShape::ModifyShape(uint inIndex, Vec3Arg inPosition, QuatArg inRotation) -{ - SubShape &sub_shape = mSubShapes[inIndex]; - sub_shape.SetTransform(inPosition, inRotation, mCenterOfMass); - - CalculateSubShapeBounds(inIndex, 1); -} - -void MutableCompoundShape::ModifyShape(uint inIndex, Vec3Arg inPosition, QuatArg inRotation, const Shape *inShape) -{ - SubShape &sub_shape = mSubShapes[inIndex]; - sub_shape.mShape = inShape; - sub_shape.SetTransform(inPosition, inRotation, mCenterOfMass); - - CalculateSubShapeBounds(inIndex, 1); -} - -void MutableCompoundShape::ModifyShapes(uint inStartIndex, uint inNumber, const Vec3 *inPositions, const Quat *inRotations, uint inPositionStride, uint inRotationStride) -{ - JPH_ASSERT(inStartIndex + inNumber <= mSubShapes.size()); - - const Vec3 *pos = inPositions; - const Quat *rot = inRotations; - for (SubShape *dest = &mSubShapes[inStartIndex], *dest_end = dest + inNumber; dest < dest_end; ++dest) - { - // Update transform - dest->SetTransform(*pos, *rot, mCenterOfMass); - - // Advance pointer in position / rotation buffer - pos = reinterpret_cast(reinterpret_cast(pos) + inPositionStride); - rot = reinterpret_cast(reinterpret_cast(rot) + inRotationStride); - } - - CalculateSubShapeBounds(inStartIndex, inNumber); -} - -template -inline void MutableCompoundShape::WalkSubShapes(Visitor &ioVisitor) const -{ - // Loop over all blocks of 4 bounding boxes - for (uint block = 0, num_blocks = GetNumBlocks(); block < num_blocks; ++block) - { - // Test the bounding boxes - const Bounds &bounds = mSubShapeBounds[block]; - typename Visitor::Result result = ioVisitor.TestBlock(bounds.mMinX, bounds.mMinY, bounds.mMinZ, bounds.mMaxX, bounds.mMaxY, bounds.mMaxZ); - - // Check if any of the bounding boxes collided - if (ioVisitor.ShouldVisitBlock(result)) - { - // Go through the individual boxes - uint sub_shape_start_idx = block << 2; - for (uint col = 0, max_col = min(4, (uint)mSubShapes.size() - sub_shape_start_idx); col < max_col; ++col) // Don't read beyond the end of the subshapes array - if (ioVisitor.ShouldVisitSubShape(result, col)) // Because the early out fraction can change, we need to retest every shape - { - // Test sub shape - uint sub_shape_idx = sub_shape_start_idx + col; - const SubShape &sub_shape = mSubShapes[sub_shape_idx]; - ioVisitor.VisitShape(sub_shape, sub_shape_idx); - - // If no better collision is available abort - if (ioVisitor.ShouldAbort()) - break; - } - } - } -} - -bool MutableCompoundShape::CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const -{ - JPH_PROFILE_FUNCTION(); - - struct Visitor : public CastRayVisitor - { - using CastRayVisitor::CastRayVisitor; - - using Result = Vec4; - - JPH_INLINE Result TestBlock(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ) const - { - return TestBounds(inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ); - } - - JPH_INLINE bool ShouldVisitBlock(Vec4Arg inResult) const - { - UVec4 closer = Vec4::sLess(inResult, Vec4::sReplicate(mHit.mFraction)); - return closer.TestAnyTrue(); - } - - JPH_INLINE bool ShouldVisitSubShape(Vec4Arg inResult, uint inIndexInBlock) const - { - return inResult[inIndexInBlock] < mHit.mFraction; - } - }; - - Visitor visitor(inRay, this, inSubShapeIDCreator, ioHit); - WalkSubShapes(visitor); - return visitor.mReturnValue; -} - -void MutableCompoundShape::CastRay(const RayCast &inRay, const RayCastSettings &inRayCastSettings, const SubShapeIDCreator &inSubShapeIDCreator, CastRayCollector &ioCollector, const ShapeFilter &inShapeFilter) const -{ - JPH_PROFILE_FUNCTION(); - - // Test shape filter - if (!inShapeFilter.ShouldCollide(this, inSubShapeIDCreator.GetID())) - return; - - struct Visitor : public CastRayVisitorCollector - { - using CastRayVisitorCollector::CastRayVisitorCollector; - - using Result = Vec4; - - JPH_INLINE Result TestBlock(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ) const - { - return TestBounds(inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ); - } - - JPH_INLINE bool ShouldVisitBlock(Vec4Arg inResult) const - { - UVec4 closer = Vec4::sLess(inResult, Vec4::sReplicate(mCollector.GetEarlyOutFraction())); - return closer.TestAnyTrue(); - } - - JPH_INLINE bool ShouldVisitSubShape(Vec4Arg inResult, uint inIndexInBlock) const - { - return inResult[inIndexInBlock] < mCollector.GetEarlyOutFraction(); - } - }; - - Visitor visitor(inRay, inRayCastSettings, this, inSubShapeIDCreator, ioCollector, inShapeFilter); - WalkSubShapes(visitor); -} - -void MutableCompoundShape::CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter) const -{ - JPH_PROFILE_FUNCTION(); - - // Test shape filter - if (!inShapeFilter.ShouldCollide(this, inSubShapeIDCreator.GetID())) - return; - - struct Visitor : public CollidePointVisitor - { - using CollidePointVisitor::CollidePointVisitor; - - using Result = UVec4; - - JPH_INLINE Result TestBlock(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ) const - { - return TestBounds(inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ); - } - - JPH_INLINE bool ShouldVisitBlock(UVec4Arg inResult) const - { - return inResult.TestAnyTrue(); - } - - JPH_INLINE bool ShouldVisitSubShape(UVec4Arg inResult, uint inIndexInBlock) const - { - return inResult[inIndexInBlock] != 0; - } - }; - - Visitor visitor(inPoint, this, inSubShapeIDCreator, ioCollector, inShapeFilter); - WalkSubShapes(visitor); -} - -void MutableCompoundShape::sCastShapeVsCompound(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector) -{ - JPH_PROFILE_FUNCTION(); - - struct Visitor : public CastShapeVisitor - { - using CastShapeVisitor::CastShapeVisitor; - - using Result = Vec4; - - JPH_INLINE Result TestBlock(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ) const - { - return TestBounds(inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ); - } - - JPH_INLINE bool ShouldVisitBlock(Vec4Arg inResult) const - { - UVec4 closer = Vec4::sLess(inResult, Vec4::sReplicate(mCollector.GetPositiveEarlyOutFraction())); - return closer.TestAnyTrue(); - } - - JPH_INLINE bool ShouldVisitSubShape(Vec4Arg inResult, uint inIndexInBlock) const - { - return inResult[inIndexInBlock] < mCollector.GetPositiveEarlyOutFraction(); - } - }; - - JPH_ASSERT(inShape->GetSubType() == EShapeSubType::MutableCompound); - const MutableCompoundShape *shape = static_cast(inShape); - - Visitor visitor(inShapeCast, inShapeCastSettings, shape, inScale, inShapeFilter, inCenterOfMassTransform2, inSubShapeIDCreator1, inSubShapeIDCreator2, ioCollector); - shape->WalkSubShapes(visitor); -} - -void MutableCompoundShape::CollectTransformedShapes(const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale, const SubShapeIDCreator &inSubShapeIDCreator, TransformedShapeCollector &ioCollector, const ShapeFilter &inShapeFilter) const -{ - JPH_PROFILE_FUNCTION(); - - // Test shape filter - if (!inShapeFilter.ShouldCollide(this, inSubShapeIDCreator.GetID())) - return; - - struct Visitor : public CollectTransformedShapesVisitor - { - using CollectTransformedShapesVisitor::CollectTransformedShapesVisitor; - - using Result = UVec4; - - JPH_INLINE Result TestBlock(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ) const - { - return TestBounds(inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ); - } - - JPH_INLINE bool ShouldVisitBlock(UVec4Arg inResult) const - { - return inResult.TestAnyTrue(); - } - - JPH_INLINE bool ShouldVisitSubShape(UVec4Arg inResult, uint inIndexInBlock) const - { - return inResult[inIndexInBlock] != 0; - } - }; - - Visitor visitor(inBox, this, inPositionCOM, inRotation, inScale, inSubShapeIDCreator, ioCollector, inShapeFilter); - WalkSubShapes(visitor); -} - -int MutableCompoundShape::GetIntersectingSubShapes(const AABox &inBox, uint *outSubShapeIndices, int inMaxSubShapeIndices) const -{ - JPH_PROFILE_FUNCTION(); - - GetIntersectingSubShapesVisitorMC visitor(inBox, outSubShapeIndices, inMaxSubShapeIndices); - WalkSubShapes(visitor); - return visitor.GetNumResults(); -} - -int MutableCompoundShape::GetIntersectingSubShapes(const OrientedBox &inBox, uint *outSubShapeIndices, int inMaxSubShapeIndices) const -{ - JPH_PROFILE_FUNCTION(); - - GetIntersectingSubShapesVisitorMC visitor(inBox, outSubShapeIndices, inMaxSubShapeIndices); - WalkSubShapes(visitor); - return visitor.GetNumResults(); -} - -void MutableCompoundShape::sCollideCompoundVsShape(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter) -{ - JPH_PROFILE_FUNCTION(); - - JPH_ASSERT(inShape1->GetSubType() == EShapeSubType::MutableCompound); - const MutableCompoundShape *shape1 = static_cast(inShape1); - - struct Visitor : public CollideCompoundVsShapeVisitor - { - using CollideCompoundVsShapeVisitor::CollideCompoundVsShapeVisitor; - - using Result = UVec4; - - JPH_INLINE Result TestBlock(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ) const - { - return TestBounds(inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ); - } - - JPH_INLINE bool ShouldVisitBlock(UVec4Arg inResult) const - { - return inResult.TestAnyTrue(); - } - - JPH_INLINE bool ShouldVisitSubShape(UVec4Arg inResult, uint inIndexInBlock) const - { - return inResult[inIndexInBlock] != 0; - } - }; - - Visitor visitor(shape1, inShape2, inScale1, inScale2, inCenterOfMassTransform1, inCenterOfMassTransform2, inSubShapeIDCreator1, inSubShapeIDCreator2, inCollideShapeSettings, ioCollector, inShapeFilter); - shape1->WalkSubShapes(visitor); -} - -void MutableCompoundShape::sCollideShapeVsCompound(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter) -{ - JPH_PROFILE_FUNCTION(); - - JPH_ASSERT(inShape2->GetSubType() == EShapeSubType::MutableCompound); - const MutableCompoundShape *shape2 = static_cast(inShape2); - - struct Visitor : public CollideShapeVsCompoundVisitor - { - using CollideShapeVsCompoundVisitor::CollideShapeVsCompoundVisitor; - - using Result = UVec4; - - JPH_INLINE Result TestBlock(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ) const - { - return TestBounds(inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ); - } - - JPH_INLINE bool ShouldVisitBlock(UVec4Arg inResult) const - { - return inResult.TestAnyTrue(); - } - - JPH_INLINE bool ShouldVisitSubShape(UVec4Arg inResult, uint inIndexInBlock) const - { - return inResult[inIndexInBlock] != 0; - } - }; - - Visitor visitor(inShape1, shape2, inScale1, inScale2, inCenterOfMassTransform1, inCenterOfMassTransform2, inSubShapeIDCreator1, inSubShapeIDCreator2, inCollideShapeSettings, ioCollector, inShapeFilter); - shape2->WalkSubShapes(visitor); -} - -void MutableCompoundShape::SaveBinaryState(StreamOut &inStream) const -{ - CompoundShape::SaveBinaryState(inStream); - - // Write bounds - uint bounds_size = (((uint)mSubShapes.size() + 3) >> 2) * sizeof(Bounds); - inStream.WriteBytes(mSubShapeBounds.data(), bounds_size); -} - -void MutableCompoundShape::RestoreBinaryState(StreamIn &inStream) -{ - CompoundShape::RestoreBinaryState(inStream); - - // Ensure that we have allocated the required space for mSubShapeBounds - EnsureSubShapeBoundsCapacity(); - - // Read bounds - uint bounds_size = (((uint)mSubShapes.size() + 3) >> 2) * sizeof(Bounds); - inStream.ReadBytes(mSubShapeBounds.data(), bounds_size); -} - -void MutableCompoundShape::sRegister() -{ - ShapeFunctions &f = ShapeFunctions::sGet(EShapeSubType::MutableCompound); - f.mConstruct = []() -> Shape * { return new MutableCompoundShape; }; - f.mColor = Color::sDarkOrange; - - for (EShapeSubType s : sAllSubShapeTypes) - { - CollisionDispatch::sRegisterCollideShape(EShapeSubType::MutableCompound, s, sCollideCompoundVsShape); - CollisionDispatch::sRegisterCollideShape(s, EShapeSubType::MutableCompound, sCollideShapeVsCompound); - CollisionDispatch::sRegisterCastShape(s, EShapeSubType::MutableCompound, sCastShapeVsCompound); - } -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/MutableCompoundShape.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/MutableCompoundShape.h deleted file mode 100644 index eeba481b616..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/MutableCompoundShape.h +++ /dev/null @@ -1,160 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -class CollideShapeSettings; - -/// Class that constructs a MutableCompoundShape. -class JPH_EXPORT MutableCompoundShapeSettings final : public CompoundShapeSettings -{ -public: - JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, MutableCompoundShapeSettings) - - // See: ShapeSettings - virtual ShapeResult Create() const override; -}; - -/// A compound shape, sub shapes can be rotated and translated. -/// This shape is optimized for adding / removing and changing the rotation / translation of sub shapes but is less efficient in querying. -/// Shifts all child objects so that they're centered around the center of mass (which needs to be kept up to date by calling AdjustCenterOfMass). -/// -/// Note: If you're using MutableCompoundShapes and are querying data while modifying the shape you'll have a race condition. -/// In this case it is best to create a new MutableCompoundShape and set the new shape on the body using BodyInterface::SetShape. If a -/// query is still working on the old shape, it will have taken a reference and keep the old shape alive until the query finishes. -class JPH_EXPORT MutableCompoundShape final : public CompoundShape -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Constructor - MutableCompoundShape() : CompoundShape(EShapeSubType::MutableCompound) { } - MutableCompoundShape(const MutableCompoundShapeSettings &inSettings, ShapeResult &outResult); - - // See Shape::CastRay - virtual bool CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const override; - virtual void CastRay(const RayCast &inRay, const RayCastSettings &inRayCastSettings, const SubShapeIDCreator &inSubShapeIDCreator, CastRayCollector &ioCollector, const ShapeFilter &inShapeFilter = { }) const override; - - // See: Shape::CollidePoint - virtual void CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter = { }) const override; - - // See Shape::CollectTransformedShapes - virtual void CollectTransformedShapes(const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale, const SubShapeIDCreator &inSubShapeIDCreator, TransformedShapeCollector &ioCollector, const ShapeFilter &inShapeFilter) const override; - - // See: CompoundShape::GetIntersectingSubShapes - virtual int GetIntersectingSubShapes(const AABox &inBox, uint *outSubShapeIndices, int inMaxSubShapeIndices) const override; - - // See: CompoundShape::GetIntersectingSubShapes - virtual int GetIntersectingSubShapes(const OrientedBox &inBox, uint *outSubShapeIndices, int inMaxSubShapeIndices) const override; - - // See Shape - virtual void SaveBinaryState(StreamOut &inStream) const override; - - // See Shape::GetStats - virtual Stats GetStats() const override { return Stats(sizeof(*this) + mSubShapes.size() * sizeof(SubShape) + mSubShapeBounds.size() * sizeof(Bounds), 0); } - - ///@{ - /// @name Mutating shapes. Note that this is not thread safe, so you need to ensure that any bodies that use this shape are locked at the time of modification using BodyLockWrite. After modification you need to call BodyInterface::NotifyShapeChanged to update the broadphase and collision caches. - - /// Adding a new shape - /// @return The index of the newly added shape - uint AddShape(Vec3Arg inPosition, QuatArg inRotation, const Shape *inShape, uint32 inUserData = 0); - - /// Remove a shape by index - void RemoveShape(uint inIndex); - - /// Modify the position / orientation of a shape - void ModifyShape(uint inIndex, Vec3Arg inPosition, QuatArg inRotation); - - /// Modify the position / orientation and shape at the same time - void ModifyShape(uint inIndex, Vec3Arg inPosition, QuatArg inRotation, const Shape *inShape); - - /// @brief Batch set positions / orientations, this avoids duplicate work due to bounding box calculation. - /// @param inStartIndex Index of first shape to update - /// @param inNumber Number of shapes to update - /// @param inPositions A list of positions with arbitrary stride - /// @param inRotations A list of orientations with arbitrary stride - /// @param inPositionStride The position stride (the number of bytes between the first and second element) - /// @param inRotationStride The orientation stride (the number of bytes between the first and second element) - void ModifyShapes(uint inStartIndex, uint inNumber, const Vec3 *inPositions, const Quat *inRotations, uint inPositionStride = sizeof(Vec3), uint inRotationStride = sizeof(Quat)); - - /// Recalculate the center of mass and shift all objects so they're centered around it - /// (this needs to be done of dynamic bodies and if the center of mass changes significantly due to adding / removing / repositioning sub shapes or else the simulation will look unnatural) - /// Note that after adjusting the center of mass of an object you need to call BodyInterface::NotifyShapeChanged and Constraint::NotifyShapeChanged on the relevant bodies / constraints. - void AdjustCenterOfMass(); - - ///@} - - // Register shape functions with the registry - static void sRegister(); - -protected: - // See: Shape::RestoreBinaryState - virtual void RestoreBinaryState(StreamIn &inStream) override; - -private: - // Visitor for GetIntersectingSubShapes - template - struct GetIntersectingSubShapesVisitorMC : public GetIntersectingSubShapesVisitor - { - using GetIntersectingSubShapesVisitor::GetIntersectingSubShapesVisitor; - - using Result = UVec4; - - JPH_INLINE Result TestBlock(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ) const - { - return GetIntersectingSubShapesVisitor::TestBounds(inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ); - } - - JPH_INLINE bool ShouldVisitBlock(UVec4Arg inResult) const - { - return inResult.TestAnyTrue(); - } - - JPH_INLINE bool ShouldVisitSubShape(UVec4Arg inResult, uint inIndexInBlock) const - { - return inResult[inIndexInBlock] != 0; - } - }; - - /// Get the number of blocks of 4 bounding boxes - inline uint GetNumBlocks() const { return ((uint)mSubShapes.size() + 3) >> 2; } - - /// Ensure that the mSubShapeBounds has enough space to store bounding boxes equivalent to the number of shapes in mSubShapes - void EnsureSubShapeBoundsCapacity(); - - /// Update mSubShapeBounds - /// @param inStartIdx First sub shape to update - /// @param inNumber Number of shapes to update - void CalculateSubShapeBounds(uint inStartIdx, uint inNumber); - - /// Calculate mLocalBounds from mSubShapeBounds - void CalculateLocalBounds(); - - template - JPH_INLINE void WalkSubShapes(Visitor &ioVisitor) const; ///< Walk the sub shapes and call Visitor::VisitShape for each sub shape encountered - - // Helper functions called by CollisionDispatch - static void sCollideCompoundVsShape(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter); - static void sCollideShapeVsCompound(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter); - static void sCastShapeVsCompound(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector); - - struct Bounds - { - Vec4 mMinX; - Vec4 mMinY; - Vec4 mMinZ; - Vec4 mMaxX; - Vec4 mMaxY; - Vec4 mMaxZ; - }; - - Array mSubShapeBounds; ///< Bounding boxes of all sub shapes in SOA format (in blocks of 4 boxes), MinX 0..3, MinY 0..3, MinZ 0..3, MaxX 0..3, MaxY 0..3, MaxZ 0..3, MinX 4..7, MinY 4..7, ... -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/OffsetCenterOfMassShape.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/OffsetCenterOfMassShape.cpp deleted file mode 100644 index 942eed0d91e..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/OffsetCenterOfMassShape.cpp +++ /dev/null @@ -1,217 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(OffsetCenterOfMassShapeSettings) -{ - JPH_ADD_BASE_CLASS(OffsetCenterOfMassShapeSettings, DecoratedShapeSettings) - - JPH_ADD_ATTRIBUTE(OffsetCenterOfMassShapeSettings, mOffset) -} - -ShapeSettings::ShapeResult OffsetCenterOfMassShapeSettings::Create() const -{ - if (mCachedResult.IsEmpty()) - Ref shape = new OffsetCenterOfMassShape(*this, mCachedResult); - return mCachedResult; -} - -OffsetCenterOfMassShape::OffsetCenterOfMassShape(const OffsetCenterOfMassShapeSettings &inSettings, ShapeResult &outResult) : - DecoratedShape(EShapeSubType::OffsetCenterOfMass, inSettings, outResult), - mOffset(inSettings.mOffset) -{ - if (outResult.HasError()) - return; - - outResult.Set(this); -} - -AABox OffsetCenterOfMassShape::GetLocalBounds() const -{ - AABox bounds = mInnerShape->GetLocalBounds(); - bounds.mMin -= mOffset; - bounds.mMax -= mOffset; - return bounds; -} - -AABox OffsetCenterOfMassShape::GetWorldSpaceBounds(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale) const -{ - return mInnerShape->GetWorldSpaceBounds(inCenterOfMassTransform.PreTranslated(-inScale * mOffset), inScale); -} - -TransformedShape OffsetCenterOfMassShape::GetSubShapeTransformedShape(const SubShapeID &inSubShapeID, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale, SubShapeID &outRemainder) const -{ - // We don't use any bits in the sub shape ID - outRemainder = inSubShapeID; - - TransformedShape ts(RVec3(inPositionCOM - inRotation * (inScale * mOffset)), inRotation, mInnerShape, BodyID()); - ts.SetShapeScale(inScale); - return ts; -} - -Vec3 OffsetCenterOfMassShape::GetSurfaceNormal(const SubShapeID &inSubShapeID, Vec3Arg inLocalSurfacePosition) const -{ - // Transform surface position to local space and pass call on - return mInnerShape->GetSurfaceNormal(inSubShapeID, inLocalSurfacePosition + mOffset); -} - -void OffsetCenterOfMassShape::GetSupportingFace(const SubShapeID &inSubShapeID, Vec3Arg inDirection, Vec3Arg inScale, Mat44Arg inCenterOfMassTransform, SupportingFace &outVertices) const -{ - mInnerShape->GetSupportingFace(inSubShapeID, inDirection, inScale, inCenterOfMassTransform.PreTranslated(-inScale * mOffset), outVertices); -} - -void OffsetCenterOfMassShape::GetSubmergedVolume(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, const Plane &inSurface, float &outTotalVolume, float &outSubmergedVolume, Vec3 &outCenterOfBuoyancy JPH_IF_DEBUG_RENDERER(, RVec3Arg inBaseOffset)) const -{ - mInnerShape->GetSubmergedVolume(inCenterOfMassTransform.PreTranslated(-inScale * mOffset), inScale, inSurface, outTotalVolume, outSubmergedVolume, outCenterOfBuoyancy JPH_IF_DEBUG_RENDERER(, inBaseOffset)); -} - -#ifdef JPH_DEBUG_RENDERER -void OffsetCenterOfMassShape::Draw(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inUseMaterialColors, bool inDrawWireframe) const -{ - mInnerShape->Draw(inRenderer, inCenterOfMassTransform.PreTranslated(-inScale * mOffset), inScale, inColor, inUseMaterialColors, inDrawWireframe); -} - -void OffsetCenterOfMassShape::DrawGetSupportFunction(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inDrawSupportDirection) const -{ - mInnerShape->DrawGetSupportFunction(inRenderer, inCenterOfMassTransform.PreTranslated(-inScale * mOffset), inScale, inColor, inDrawSupportDirection); -} - -void OffsetCenterOfMassShape::DrawGetSupportingFace(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale) const -{ - mInnerShape->DrawGetSupportingFace(inRenderer, inCenterOfMassTransform.PreTranslated(-inScale * mOffset), inScale); -} -#endif // JPH_DEBUG_RENDERER - -bool OffsetCenterOfMassShape::CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const -{ - // Transform the ray to local space - RayCast ray = inRay; - ray.mOrigin += mOffset; - - return mInnerShape->CastRay(ray, inSubShapeIDCreator, ioHit); -} - -void OffsetCenterOfMassShape::CastRay(const RayCast &inRay, const RayCastSettings &inRayCastSettings, const SubShapeIDCreator &inSubShapeIDCreator, CastRayCollector &ioCollector, const ShapeFilter &inShapeFilter) const -{ - // Test shape filter - if (!inShapeFilter.ShouldCollide(this, inSubShapeIDCreator.GetID())) - return; - - // Transform the ray to local space - RayCast ray = inRay; - ray.mOrigin += mOffset; - - return mInnerShape->CastRay(ray, inRayCastSettings, inSubShapeIDCreator, ioCollector, inShapeFilter); -} - -void OffsetCenterOfMassShape::CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter) const -{ - // Test shape filter - if (!inShapeFilter.ShouldCollide(this, inSubShapeIDCreator.GetID())) - return; - - // Pass the point on to the inner shape in local space - mInnerShape->CollidePoint(inPoint + mOffset, inSubShapeIDCreator, ioCollector, inShapeFilter); -} - -void OffsetCenterOfMassShape::CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, SoftBodyVertex *ioVertices, uint inNumVertices, float inDeltaTime, Vec3Arg inDisplacementDueToGravity, int inCollidingShapeIndex) const -{ - mInnerShape->CollideSoftBodyVertices(inCenterOfMassTransform.PreTranslated(-inScale * mOffset), inScale, ioVertices, inNumVertices, inDeltaTime, inDisplacementDueToGravity, inCollidingShapeIndex); -} - -void OffsetCenterOfMassShape::CollectTransformedShapes(const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale, const SubShapeIDCreator &inSubShapeIDCreator, TransformedShapeCollector &ioCollector, const ShapeFilter &inShapeFilter) const -{ - // Test shape filter - if (!inShapeFilter.ShouldCollide(this, inSubShapeIDCreator.GetID())) - return; - - mInnerShape->CollectTransformedShapes(inBox, inPositionCOM - inRotation * (inScale * mOffset), inRotation, inScale, inSubShapeIDCreator, ioCollector, inShapeFilter); -} - -void OffsetCenterOfMassShape::TransformShape(Mat44Arg inCenterOfMassTransform, TransformedShapeCollector &ioCollector) const -{ - mInnerShape->TransformShape(inCenterOfMassTransform.PreTranslated(-mOffset), ioCollector); -} - -void OffsetCenterOfMassShape::sCollideOffsetCenterOfMassVsShape(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter) -{ - JPH_ASSERT(inShape1->GetSubType() == EShapeSubType::OffsetCenterOfMass); - const OffsetCenterOfMassShape *shape1 = static_cast(inShape1); - - CollisionDispatch::sCollideShapeVsShape(shape1->mInnerShape, inShape2, inScale1, inScale2, inCenterOfMassTransform1.PreTranslated(-inScale1 * shape1->mOffset), inCenterOfMassTransform2, inSubShapeIDCreator1, inSubShapeIDCreator2, inCollideShapeSettings, ioCollector, inShapeFilter); -} - -void OffsetCenterOfMassShape::sCollideShapeVsOffsetCenterOfMass(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter) -{ - JPH_ASSERT(inShape2->GetSubType() == EShapeSubType::OffsetCenterOfMass); - const OffsetCenterOfMassShape *shape2 = static_cast(inShape2); - - CollisionDispatch::sCollideShapeVsShape(inShape1, shape2->mInnerShape, inScale1, inScale2, inCenterOfMassTransform1, inCenterOfMassTransform2.PreTranslated(-inScale2 * shape2->mOffset), inSubShapeIDCreator1, inSubShapeIDCreator2, inCollideShapeSettings, ioCollector, inShapeFilter); -} - -void OffsetCenterOfMassShape::sCastOffsetCenterOfMassVsShape(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector) -{ - // Fetch offset center of mass shape from cast shape - JPH_ASSERT(inShapeCast.mShape->GetSubType() == EShapeSubType::OffsetCenterOfMass); - const OffsetCenterOfMassShape *shape1 = static_cast(inShapeCast.mShape); - - // Transform the shape cast and update the shape - ShapeCast shape_cast(shape1->mInnerShape, inShapeCast.mScale, inShapeCast.mCenterOfMassStart.PreTranslated(-inShapeCast.mScale * shape1->mOffset), inShapeCast.mDirection); - - CollisionDispatch::sCastShapeVsShapeLocalSpace(shape_cast, inShapeCastSettings, inShape, inScale, inShapeFilter, inCenterOfMassTransform2, inSubShapeIDCreator1, inSubShapeIDCreator2, ioCollector); -} - -void OffsetCenterOfMassShape::sCastShapeVsOffsetCenterOfMass(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector) -{ - JPH_ASSERT(inShape->GetSubType() == EShapeSubType::OffsetCenterOfMass); - const OffsetCenterOfMassShape *shape = static_cast(inShape); - - // Transform the shape cast - ShapeCast shape_cast = inShapeCast.PostTransformed(Mat44::sTranslation(inScale * shape->mOffset)); - - CollisionDispatch::sCastShapeVsShapeLocalSpace(shape_cast, inShapeCastSettings, shape->mInnerShape, inScale, inShapeFilter, inCenterOfMassTransform2.PreTranslated(-inScale * shape->mOffset), inSubShapeIDCreator1, inSubShapeIDCreator2, ioCollector); -} - -void OffsetCenterOfMassShape::SaveBinaryState(StreamOut &inStream) const -{ - DecoratedShape::SaveBinaryState(inStream); - - inStream.Write(mOffset); -} - -void OffsetCenterOfMassShape::RestoreBinaryState(StreamIn &inStream) -{ - DecoratedShape::RestoreBinaryState(inStream); - - inStream.Read(mOffset); -} - -void OffsetCenterOfMassShape::sRegister() -{ - ShapeFunctions &f = ShapeFunctions::sGet(EShapeSubType::OffsetCenterOfMass); - f.mConstruct = []() -> Shape * { return new OffsetCenterOfMassShape; }; - f.mColor = Color::sCyan; - - for (EShapeSubType s : sAllSubShapeTypes) - { - CollisionDispatch::sRegisterCollideShape(EShapeSubType::OffsetCenterOfMass, s, sCollideOffsetCenterOfMassVsShape); - CollisionDispatch::sRegisterCollideShape(s, EShapeSubType::OffsetCenterOfMass, sCollideShapeVsOffsetCenterOfMass); - CollisionDispatch::sRegisterCastShape(EShapeSubType::OffsetCenterOfMass, s, sCastOffsetCenterOfMassVsShape); - CollisionDispatch::sRegisterCastShape(s, EShapeSubType::OffsetCenterOfMass, sCastShapeVsOffsetCenterOfMass); - } -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/OffsetCenterOfMassShape.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/OffsetCenterOfMassShape.h deleted file mode 100644 index 3e828181cb5..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/OffsetCenterOfMassShape.h +++ /dev/null @@ -1,143 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -class CollideShapeSettings; - -/// Class that constructs an OffsetCenterOfMassShape -class JPH_EXPORT OffsetCenterOfMassShapeSettings final : public DecoratedShapeSettings -{ -public: - JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, OffsetCenterOfMassShapeSettings) - - /// Constructor - OffsetCenterOfMassShapeSettings() = default; - - /// Construct with shape settings, can be serialized. - OffsetCenterOfMassShapeSettings(Vec3Arg inOffset, const ShapeSettings *inShape) : DecoratedShapeSettings(inShape), mOffset(inOffset) { } - - /// Variant that uses a concrete shape, which means this object cannot be serialized. - OffsetCenterOfMassShapeSettings(Vec3Arg inOffset, const Shape *inShape): DecoratedShapeSettings(inShape), mOffset(inOffset) { } - - // See: ShapeSettings - virtual ShapeResult Create() const override; - - Vec3 mOffset; ///< Offset to be applied to the center of mass of the child shape -}; - -/// This shape will shift the center of mass of a child shape, it can e.g. be used to lower the center of mass of an unstable object like a boat to make it stable -class JPH_EXPORT OffsetCenterOfMassShape final : public DecoratedShape -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Constructor - OffsetCenterOfMassShape() : DecoratedShape(EShapeSubType::OffsetCenterOfMass) { } - OffsetCenterOfMassShape(const OffsetCenterOfMassShapeSettings &inSettings, ShapeResult &outResult); - OffsetCenterOfMassShape(const Shape *inShape, Vec3Arg inOffset) : DecoratedShape(EShapeSubType::OffsetCenterOfMass, inShape), mOffset(inOffset) { } - - /// Access the offset that is applied to the center of mass - Vec3 GetOffset() const { return mOffset; } - - // See Shape::GetCenterOfMass - virtual Vec3 GetCenterOfMass() const override { return mInnerShape->GetCenterOfMass() + mOffset; } - - // See Shape::GetLocalBounds - virtual AABox GetLocalBounds() const override; - - // See Shape::GetWorldSpaceBounds - virtual AABox GetWorldSpaceBounds(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale) const override; - using Shape::GetWorldSpaceBounds; - - // See Shape::GetInnerRadius - virtual float GetInnerRadius() const override { return mInnerShape->GetInnerRadius(); } - - // See Shape::GetMassProperties - virtual MassProperties GetMassProperties() const override - { - MassProperties mp = mInnerShape->GetMassProperties(); - mp.Translate(mOffset); - return mp; - } - - // See Shape::GetSubShapeTransformedShape - virtual TransformedShape GetSubShapeTransformedShape(const SubShapeID &inSubShapeID, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale, SubShapeID &outRemainder) const override; - - // See Shape::GetSurfaceNormal - virtual Vec3 GetSurfaceNormal(const SubShapeID &inSubShapeID, Vec3Arg inLocalSurfacePosition) const override; - - // See Shape::GetSupportingFace - virtual void GetSupportingFace(const SubShapeID &inSubShapeID, Vec3Arg inDirection, Vec3Arg inScale, Mat44Arg inCenterOfMassTransform, SupportingFace &outVertices) const override; - - // See Shape::GetSubmergedVolume - virtual void GetSubmergedVolume(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, const Plane &inSurface, float &outTotalVolume, float &outSubmergedVolume, Vec3 &outCenterOfBuoyancy JPH_IF_DEBUG_RENDERER(, RVec3Arg inBaseOffset)) const override; - -#ifdef JPH_DEBUG_RENDERER - // See Shape::Draw - virtual void Draw(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inUseMaterialColors, bool inDrawWireframe) const override; - - // See Shape::DrawGetSupportFunction - virtual void DrawGetSupportFunction(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inDrawSupportDirection) const override; - - // See Shape::DrawGetSupportingFace - virtual void DrawGetSupportingFace(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale) const override; -#endif // JPH_DEBUG_RENDERER - - // See Shape::CastRay - virtual bool CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const override; - virtual void CastRay(const RayCast &inRay, const RayCastSettings &inRayCastSettings, const SubShapeIDCreator &inSubShapeIDCreator, CastRayCollector &ioCollector, const ShapeFilter &inShapeFilter = { }) const override; - - // See: Shape::CollidePoint - virtual void CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter = { }) const override; - - // See: Shape::CollideSoftBodyVertices - virtual void CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, SoftBodyVertex *ioVertices, uint inNumVertices, float inDeltaTime, Vec3Arg inDisplacementDueToGravity, int inCollidingShapeIndex) const override; - - // See Shape::CollectTransformedShapes - virtual void CollectTransformedShapes(const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale, const SubShapeIDCreator &inSubShapeIDCreator, TransformedShapeCollector &ioCollector, const ShapeFilter &inShapeFilter) const override; - - // See Shape::TransformShape - virtual void TransformShape(Mat44Arg inCenterOfMassTransform, TransformedShapeCollector &ioCollector) const override; - - // See Shape::GetTrianglesStart - virtual void GetTrianglesStart(GetTrianglesContext &ioContext, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) const override { JPH_ASSERT(false, "Cannot call on non-leaf shapes, use CollectTransformedShapes to collect the leaves first!"); } - - // See Shape::GetTrianglesNext - virtual int GetTrianglesNext(GetTrianglesContext &ioContext, int inMaxTrianglesRequested, Float3 *outTriangleVertices, const PhysicsMaterial **outMaterials = nullptr) const override { JPH_ASSERT(false, "Cannot call on non-leaf shapes, use CollectTransformedShapes to collect the leaves first!"); return 0; } - - // See Shape - virtual void SaveBinaryState(StreamOut &inStream) const override; - - // See Shape::GetStats - virtual Stats GetStats() const override { return Stats(sizeof(*this), 0); } - - // See Shape::GetVolume - virtual float GetVolume() const override { return mInnerShape->GetVolume(); } - - // See Shape::IsValidScale - virtual bool IsValidScale(Vec3Arg inScale) const override { return mInnerShape->IsValidScale(inScale); } - - // Register shape functions with the registry - static void sRegister(); - -protected: - // See: Shape::RestoreBinaryState - virtual void RestoreBinaryState(StreamIn &inStream) override; - -private: - // Helper functions called by CollisionDispatch - static void sCollideOffsetCenterOfMassVsShape(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter); - static void sCollideShapeVsOffsetCenterOfMass(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter); - static void sCastOffsetCenterOfMassVsShape(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector); - static void sCastShapeVsOffsetCenterOfMass(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector); - - Vec3 mOffset; ///< Offset of the center of mass -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/PolyhedronSubmergedVolumeCalculator.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/PolyhedronSubmergedVolumeCalculator.h deleted file mode 100644 index 96e69f0b19b..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/PolyhedronSubmergedVolumeCalculator.h +++ /dev/null @@ -1,319 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#ifdef JPH_DEBUG_RENDERER - #include -#endif // JPH_DEBUG_RENDERER - -JPH_NAMESPACE_BEGIN - -/// This class calculates the intersection between a fluid surface and a polyhedron and returns the submerged volume and its center of buoyancy -/// Construct this class and then one by one add all faces of the polyhedron using the AddFace function. After all faces have been added the result -/// can be gotten through GetResult. -class PolyhedronSubmergedVolumeCalculator -{ -private: - // Calculate submerged volume * 6 and center of mass * 4 for a tetrahedron with 4 vertices submerged - // inV1 .. inV4 are submerged - inline static void sTetrahedronVolume4(Vec3Arg inV1, Vec3Arg inV2, Vec3Arg inV3, Vec3Arg inV4, float &outVolumeTimes6, Vec3 &outCenterTimes4) - { - // Calculate center of mass and mass of this tetrahedron, - // see: https://en.wikipedia.org/wiki/Tetrahedron#Volume - outVolumeTimes6 = max((inV1 - inV4).Dot((inV2 - inV4).Cross(inV3 - inV4)), 0.0f); // All contributions should be positive because we use a reference point that is on the surface of the hull - outCenterTimes4 = inV1 + inV2 + inV3 + inV4; - } - - // Get the intersection point with a plane. - // inV1 is inD1 distance away from the plane, inV2 is inD2 distance away from the plane - inline static Vec3 sGetPlaneIntersection(Vec3Arg inV1, float inD1, Vec3Arg inV2, float inD2) - { - JPH_ASSERT(Sign(inD1) != Sign(inD2), "Assuming both points are on opposite ends of the plane"); - float delta = inD1 - inD2; - if (abs(delta) < 1.0e-6f) - return inV1; // Parallel to plane, just pick a point - else - return inV1 + inD1 * (inV2 - inV1) / delta; - } - - // Calculate submerged volume * 6 and center of mass * 4 for a tetrahedron with 1 vertex submerged - // inV1 is submerged, inV2 .. inV4 are not - // inD1 .. inD4 are the distances from the points to the plane - inline JPH_IF_NOT_DEBUG_RENDERER(static) void sTetrahedronVolume1(Vec3Arg inV1, float inD1, Vec3Arg inV2, float inD2, Vec3Arg inV3, float inD3, Vec3Arg inV4, float inD4, float &outVolumeTimes6, Vec3 &outCenterTimes4) - { - // A tetrahedron with 1 point submerged is cut along 3 edges forming a new tetrahedron - Vec3 v2 = sGetPlaneIntersection(inV1, inD1, inV2, inD2); - Vec3 v3 = sGetPlaneIntersection(inV1, inD1, inV3, inD3); - Vec3 v4 = sGetPlaneIntersection(inV1, inD1, inV4, inD4); - - #ifdef JPH_DEBUG_RENDERER - // Draw intersection between tetrahedron and surface - if (Shape::sDrawSubmergedVolumes) - { - RVec3 v2w = mBaseOffset + v2; - RVec3 v3w = mBaseOffset + v3; - RVec3 v4w = mBaseOffset + v4; - - DebugRenderer::sInstance->DrawTriangle(v4w, v3w, v2w, Color::sGreen); - DebugRenderer::sInstance->DrawWireTriangle(v4w, v3w, v2w, Color::sWhite); - } - #endif // JPH_DEBUG_RENDERER - - sTetrahedronVolume4(inV1, v2, v3, v4, outVolumeTimes6, outCenterTimes4); - } - - // Calculate submerged volume * 6 and center of mass * 4 for a tetrahedron with 2 vertices submerged - // inV1, inV2 are submerged, inV3, inV4 are not - // inD1 .. inD4 are the distances from the points to the plane - inline JPH_IF_NOT_DEBUG_RENDERER(static) void sTetrahedronVolume2(Vec3Arg inV1, float inD1, Vec3Arg inV2, float inD2, Vec3Arg inV3, float inD3, Vec3Arg inV4, float inD4, float &outVolumeTimes6, Vec3 &outCenterTimes4) - { - // A tetrahedron with 2 points submerged is cut along 4 edges forming a quad - Vec3 c = sGetPlaneIntersection(inV1, inD1, inV3, inD3); - Vec3 d = sGetPlaneIntersection(inV1, inD1, inV4, inD4); - Vec3 e = sGetPlaneIntersection(inV2, inD2, inV4, inD4); - Vec3 f = sGetPlaneIntersection(inV2, inD2, inV3, inD3); - - #ifdef JPH_DEBUG_RENDERER - // Draw intersection between tetrahedron and surface - if (Shape::sDrawSubmergedVolumes) - { - RVec3 cw = mBaseOffset + c; - RVec3 dw = mBaseOffset + d; - RVec3 ew = mBaseOffset + e; - RVec3 fw = mBaseOffset + f; - - DebugRenderer::sInstance->DrawTriangle(cw, ew, dw, Color::sGreen); - DebugRenderer::sInstance->DrawTriangle(cw, fw, ew, Color::sGreen); - DebugRenderer::sInstance->DrawWireTriangle(cw, ew, dw, Color::sWhite); - DebugRenderer::sInstance->DrawWireTriangle(cw, fw, ew, Color::sWhite); - } - #endif // JPH_DEBUG_RENDERER - - // We pick point c as reference (which is on the cut off surface) - // This leaves us with three tetrahedrons to sum up (any faces that are in the same plane as c will have zero volume) - Vec3 center1, center2, center3; - float volume1, volume2, volume3; - sTetrahedronVolume4(e, f, inV2, c, volume1, center1); - sTetrahedronVolume4(e, inV1, d, c, volume2, center2); - sTetrahedronVolume4(e, inV2, inV1, c, volume3, center3); - - // Tally up the totals - outVolumeTimes6 = volume1 + volume2 + volume3; - outCenterTimes4 = outVolumeTimes6 > 0.0f? (volume1 * center1 + volume2 * center2 + volume3 * center3) / outVolumeTimes6 : Vec3::sZero(); - } - - // Calculate submerged volume * 6 and center of mass * 4 for a tetrahedron with 3 vertices submerged - // inV1, inV2, inV3 are submerged, inV4 is not - // inD1 .. inD4 are the distances from the points to the plane - inline JPH_IF_NOT_DEBUG_RENDERER(static) void sTetrahedronVolume3(Vec3Arg inV1, float inD1, Vec3Arg inV2, float inD2, Vec3Arg inV3, float inD3, Vec3Arg inV4, float inD4, float &outVolumeTimes6, Vec3 &outCenterTimes4) - { - // A tetrahedron with 1 point above the surface is cut along 3 edges forming a new tetrahedron - Vec3 v1 = sGetPlaneIntersection(inV1, inD1, inV4, inD4); - Vec3 v2 = sGetPlaneIntersection(inV2, inD2, inV4, inD4); - Vec3 v3 = sGetPlaneIntersection(inV3, inD3, inV4, inD4); - - #ifdef JPH_DEBUG_RENDERER - // Draw intersection between tetrahedron and surface - if (Shape::sDrawSubmergedVolumes) - { - RVec3 v1w = mBaseOffset + v1; - RVec3 v2w = mBaseOffset + v2; - RVec3 v3w = mBaseOffset + v3; - - DebugRenderer::sInstance->DrawTriangle(v3w, v2w, v1w, Color::sGreen); - DebugRenderer::sInstance->DrawWireTriangle(v3w, v2w, v1w, Color::sWhite); - } - #endif // JPH_DEBUG_RENDERER - - Vec3 dry_center, total_center; - float dry_volume, total_volume; - - // We first calculate the part that is above the surface - sTetrahedronVolume4(v1, v2, v3, inV4, dry_volume, dry_center); - - // Calculate the total volume - sTetrahedronVolume4(inV1, inV2, inV3, inV4, total_volume, total_center); - - // From this we can calculate the center and volume of the submerged part - outVolumeTimes6 = max(total_volume - dry_volume, 0.0f); - outCenterTimes4 = outVolumeTimes6 > 0.0f? (total_center * total_volume - dry_center * dry_volume) / outVolumeTimes6 : Vec3::sZero(); - } - -public: - /// A helper class that contains cached information about a polyhedron vertex - class Point - { - public: - Vec3 mPosition; ///< World space position of vertex - float mDistanceToSurface; ///< Signed distance to the surface (> 0 is above, < 0 is below) - bool mAboveSurface; ///< If the point is above the surface (mDistanceToSurface > 0) - }; - - /// Constructor - /// @param inTransform Transform to transform all incoming points with - /// @param inPoints Array of points that are part of the polyhedron - /// @param inPointStride Amount of bytes between each point (should usually be sizeof(Vec3)) - /// @param inNumPoints The amount of points - /// @param inSurface The plane that forms the fluid surface (normal should point up) - /// @param ioBuffer A temporary buffer of Point's that should have inNumPoints entries and should stay alive while this class is alive -#ifdef JPH_DEBUG_RENDERER - /// @param inBaseOffset The offset to transform inTransform to world space (in double precision mode this can be used to shift the whole operation closer to the origin). Only used for debug drawing. -#endif // JPH_DEBUG_RENDERER - PolyhedronSubmergedVolumeCalculator(const Mat44 &inTransform, const Vec3 *inPoints, int inPointStride, int inNumPoints, const Plane &inSurface, Point *ioBuffer -#ifdef JPH_DEBUG_RENDERER // Not using JPH_IF_DEBUG_RENDERER for Doxygen - , RVec3 inBaseOffset -#endif // JPH_DEBUG_RENDERER - ) : - mPoints(ioBuffer) -#ifdef JPH_DEBUG_RENDERER - , mBaseOffset(inBaseOffset) -#endif // JPH_DEBUG_RENDERER - { - // Convert the points to world space and determine the distance to the surface - float reference_dist = FLT_MAX; - for (int p = 0; p < inNumPoints; ++p) - { - // Calculate values - Vec3 transformed_point = inTransform * *reinterpret_cast(reinterpret_cast(inPoints) + p * inPointStride); - float dist = inSurface.SignedDistance(transformed_point); - bool above = dist >= 0.0f; - - // Keep track if all are above or below - mAllAbove &= above; - mAllBelow &= !above; - - // Calculate lowest point, we use this to create tetrahedrons out of all faces - if (reference_dist > dist) - { - mReferencePointIdx = p; - reference_dist = dist; - } - - // Store values - ioBuffer->mPosition = transformed_point; - ioBuffer->mDistanceToSurface = dist; - ioBuffer->mAboveSurface = above; - ++ioBuffer; - } - } - - /// Check if all points are above the surface. Should be used as early out. - inline bool AreAllAbove() const - { - return mAllAbove; - } - - /// Check if all points are below the surface. Should be used as early out. - inline bool AreAllBelow() const - { - return mAllBelow; - } - - /// Get the lowest point of the polyhedron. Used to form the 4th vertex to make a tetrahedron out of a polyhedron face. - inline int GetReferencePointIdx() const - { - return mReferencePointIdx; - } - - /// Add a polyhedron face. Supply the indices of the points that form the face (in counter clockwise order). - void AddFace(int inIdx1, int inIdx2, int inIdx3) - { - JPH_ASSERT(inIdx1 != mReferencePointIdx && inIdx2 != mReferencePointIdx && inIdx3 != mReferencePointIdx, "A face using the reference point will not contribute to the volume"); - - // Find the points - const Point &ref = mPoints[mReferencePointIdx]; - const Point &p1 = mPoints[inIdx1]; - const Point &p2 = mPoints[inIdx2]; - const Point &p3 = mPoints[inIdx3]; - - // Determine which vertices are submerged - uint code = (p1.mAboveSurface? 0 : 0b001) | (p2.mAboveSurface? 0 : 0b010) | (p3.mAboveSurface? 0 : 0b100); - - float volume; - Vec3 center; - switch (code) - { - case 0b000: - // One point submerged - sTetrahedronVolume1(ref.mPosition, ref.mDistanceToSurface, p3.mPosition, p3.mDistanceToSurface, p2.mPosition, p2.mDistanceToSurface, p1.mPosition, p1.mDistanceToSurface, volume, center); - break; - - case 0b001: - // Two points submerged - sTetrahedronVolume2(ref.mPosition, ref.mDistanceToSurface, p1.mPosition, p1.mDistanceToSurface, p3.mPosition, p3.mDistanceToSurface, p2.mPosition, p2.mDistanceToSurface, volume, center); - break; - - case 0b010: - // Two points submerged - sTetrahedronVolume2(ref.mPosition, ref.mDistanceToSurface, p2.mPosition, p2.mDistanceToSurface, p1.mPosition, p1.mDistanceToSurface, p3.mPosition, p3.mDistanceToSurface, volume, center); - break; - - case 0b100: - // Two points submerged - sTetrahedronVolume2(ref.mPosition, ref.mDistanceToSurface, p3.mPosition, p3.mDistanceToSurface, p2.mPosition, p2.mDistanceToSurface, p1.mPosition, p1.mDistanceToSurface, volume, center); - break; - - case 0b011: - // Three points submerged - sTetrahedronVolume3(ref.mPosition, ref.mDistanceToSurface, p2.mPosition, p2.mDistanceToSurface, p1.mPosition, p1.mDistanceToSurface, p3.mPosition, p3.mDistanceToSurface, volume, center); - break; - - case 0b101: - // Three points submerged - sTetrahedronVolume3(ref.mPosition, ref.mDistanceToSurface, p1.mPosition, p1.mDistanceToSurface, p3.mPosition, p3.mDistanceToSurface, p2.mPosition, p2.mDistanceToSurface, volume, center); - break; - - case 0b110: - // Three points submerged - sTetrahedronVolume3(ref.mPosition, ref.mDistanceToSurface, p3.mPosition, p3.mDistanceToSurface, p2.mPosition, p2.mDistanceToSurface, p1.mPosition, p1.mDistanceToSurface, volume, center); - break; - - case 0b111: - // Four points submerged - sTetrahedronVolume4(ref.mPosition, p3.mPosition, p2.mPosition, p1.mPosition, volume, center); - break; - - default: - // Should not be possible - JPH_ASSERT(false); - volume = 0.0f; - center = Vec3::sZero(); - break; - } - - mSubmergedVolume += volume; - mCenterOfBuoyancy += volume * center; - } - - /// Call after all faces have been added. Returns the submerged volume and the center of buoyancy for the submerged volume. - void GetResult(float &outSubmergedVolume, Vec3 &outCenterOfBuoyancy) const - { - outCenterOfBuoyancy = mSubmergedVolume > 0.0f? mCenterOfBuoyancy / (4.0f * mSubmergedVolume) : Vec3::sZero(); // Do this before dividing submerged volume by 6 to get correct weight factor - outSubmergedVolume = mSubmergedVolume / 6.0f; - } - -private: - // The precalculated points for this polyhedron - const Point * mPoints; - - // If all points are above/below the surface - bool mAllBelow = true; - bool mAllAbove = true; - - // The lowest point - int mReferencePointIdx = 0; - - // Aggregator for submerged volume and center of buoyancy - float mSubmergedVolume = 0.0f; - Vec3 mCenterOfBuoyancy = Vec3::sZero(); - -#ifdef JPH_DEBUG_RENDERER - // Base offset used for drawing - RVec3 mBaseOffset; -#endif -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/RotatedTranslatedShape.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/RotatedTranslatedShape.cpp deleted file mode 100644 index 7d6e06a019f..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/RotatedTranslatedShape.cpp +++ /dev/null @@ -1,315 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(RotatedTranslatedShapeSettings) -{ - JPH_ADD_BASE_CLASS(RotatedTranslatedShapeSettings, DecoratedShapeSettings) - - JPH_ADD_ATTRIBUTE(RotatedTranslatedShapeSettings, mPosition) - JPH_ADD_ATTRIBUTE(RotatedTranslatedShapeSettings, mRotation) -} - -ShapeSettings::ShapeResult RotatedTranslatedShapeSettings::Create() const -{ - if (mCachedResult.IsEmpty()) - Ref shape = new RotatedTranslatedShape(*this, mCachedResult); - return mCachedResult; -} - -RotatedTranslatedShape::RotatedTranslatedShape(const RotatedTranslatedShapeSettings &inSettings, ShapeResult &outResult) : - DecoratedShape(EShapeSubType::RotatedTranslated, inSettings, outResult) -{ - if (outResult.HasError()) - return; - - // Calculate center of mass position - mCenterOfMass = inSettings.mPosition + inSettings.mRotation * mInnerShape->GetCenterOfMass(); - - // Store rotation (position is always zero because we center around the center of mass) - mRotation = inSettings.mRotation; - mIsRotationIdentity = mRotation.IsClose(Quat::sIdentity()); - - outResult.Set(this); -} - -RotatedTranslatedShape::RotatedTranslatedShape(Vec3Arg inPosition, QuatArg inRotation, const Shape *inShape) : - DecoratedShape(EShapeSubType::RotatedTranslated, inShape) -{ - // Calculate center of mass position - mCenterOfMass = inPosition + inRotation * mInnerShape->GetCenterOfMass(); - - // Store rotation (position is always zero because we center around the center of mass) - mRotation = inRotation; - mIsRotationIdentity = mRotation.IsClose(Quat::sIdentity()); -} - -MassProperties RotatedTranslatedShape::GetMassProperties() const -{ - // Rotate inertia of child into place - MassProperties p = mInnerShape->GetMassProperties(); - p.Rotate(Mat44::sRotation(mRotation)); - return p; -} - -AABox RotatedTranslatedShape::GetLocalBounds() const -{ - return mInnerShape->GetLocalBounds().Transformed(Mat44::sRotation(mRotation)); -} - -AABox RotatedTranslatedShape::GetWorldSpaceBounds(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale) const -{ - Mat44 transform = inCenterOfMassTransform * Mat44::sRotation(mRotation); - return mInnerShape->GetWorldSpaceBounds(transform, TransformScale(inScale)); -} - -TransformedShape RotatedTranslatedShape::GetSubShapeTransformedShape(const SubShapeID &inSubShapeID, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale, SubShapeID &outRemainder) const -{ - // We don't use any bits in the sub shape ID - outRemainder = inSubShapeID; - - TransformedShape ts(RVec3(inPositionCOM), inRotation * mRotation, mInnerShape, BodyID()); - ts.SetShapeScale(TransformScale(inScale)); - return ts; -} - -Vec3 RotatedTranslatedShape::GetSurfaceNormal(const SubShapeID &inSubShapeID, Vec3Arg inLocalSurfacePosition) const -{ - // Transform surface position to local space and pass call on - Mat44 transform = Mat44::sRotation(mRotation.Conjugated()); - Vec3 normal = mInnerShape->GetSurfaceNormal(inSubShapeID, transform * inLocalSurfacePosition); - - // Transform normal to this shape's space - return transform.Multiply3x3Transposed(normal); -} - -void RotatedTranslatedShape::GetSupportingFace(const SubShapeID &inSubShapeID, Vec3Arg inDirection, Vec3Arg inScale, Mat44Arg inCenterOfMassTransform, SupportingFace &outVertices) const -{ - Mat44 transform = Mat44::sRotation(mRotation); - mInnerShape->GetSupportingFace(inSubShapeID, transform.Multiply3x3Transposed(inDirection), TransformScale(inScale), inCenterOfMassTransform * transform, outVertices); -} - -void RotatedTranslatedShape::GetSubmergedVolume(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, const Plane &inSurface, float &outTotalVolume, float &outSubmergedVolume, Vec3 &outCenterOfBuoyancy JPH_IF_DEBUG_RENDERER(, RVec3Arg inBaseOffset)) const -{ - // Get center of mass transform of child - Mat44 transform = inCenterOfMassTransform * Mat44::sRotation(mRotation); - - // Recurse to child - mInnerShape->GetSubmergedVolume(transform, TransformScale(inScale), inSurface, outTotalVolume, outSubmergedVolume, outCenterOfBuoyancy JPH_IF_DEBUG_RENDERER(, inBaseOffset)); -} - -#ifdef JPH_DEBUG_RENDERER -void RotatedTranslatedShape::Draw(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inUseMaterialColors, bool inDrawWireframe) const -{ - mInnerShape->Draw(inRenderer, inCenterOfMassTransform * Mat44::sRotation(mRotation), TransformScale(inScale), inColor, inUseMaterialColors, inDrawWireframe); -} - -void RotatedTranslatedShape::DrawGetSupportFunction(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inDrawSupportDirection) const -{ - mInnerShape->DrawGetSupportFunction(inRenderer, inCenterOfMassTransform * Mat44::sRotation(mRotation), TransformScale(inScale), inColor, inDrawSupportDirection); -} - -void RotatedTranslatedShape::DrawGetSupportingFace(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale) const -{ - mInnerShape->DrawGetSupportingFace(inRenderer, inCenterOfMassTransform * Mat44::sRotation(mRotation), TransformScale(inScale)); -} -#endif // JPH_DEBUG_RENDERER - -bool RotatedTranslatedShape::CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const -{ - // Transform the ray - Mat44 transform = Mat44::sRotation(mRotation.Conjugated()); - RayCast ray = inRay.Transformed(transform); - - return mInnerShape->CastRay(ray, inSubShapeIDCreator, ioHit); -} - -void RotatedTranslatedShape::CastRay(const RayCast &inRay, const RayCastSettings &inRayCastSettings, const SubShapeIDCreator &inSubShapeIDCreator, CastRayCollector &ioCollector, const ShapeFilter &inShapeFilter) const -{ - // Test shape filter - if (!inShapeFilter.ShouldCollide(this, inSubShapeIDCreator.GetID())) - return; - - // Transform the ray - Mat44 transform = Mat44::sRotation(mRotation.Conjugated()); - RayCast ray = inRay.Transformed(transform); - - return mInnerShape->CastRay(ray, inRayCastSettings, inSubShapeIDCreator, ioCollector, inShapeFilter); -} - -void RotatedTranslatedShape::CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter) const -{ - // Test shape filter - if (!inShapeFilter.ShouldCollide(this, inSubShapeIDCreator.GetID())) - return; - - // Transform the point - Mat44 transform = Mat44::sRotation(mRotation.Conjugated()); - mInnerShape->CollidePoint(transform * inPoint, inSubShapeIDCreator, ioCollector, inShapeFilter); -} - -void RotatedTranslatedShape::CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, SoftBodyVertex *ioVertices, uint inNumVertices, float inDeltaTime, Vec3Arg inDisplacementDueToGravity, int inCollidingShapeIndex) const -{ - mInnerShape->CollideSoftBodyVertices(inCenterOfMassTransform * Mat44::sRotation(mRotation), inScale, ioVertices, inNumVertices, inDeltaTime, inDisplacementDueToGravity, inCollidingShapeIndex); -} - -void RotatedTranslatedShape::CollectTransformedShapes(const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale, const SubShapeIDCreator &inSubShapeIDCreator, TransformedShapeCollector &ioCollector, const ShapeFilter &inShapeFilter) const -{ - // Test shape filter - if (!inShapeFilter.ShouldCollide(this, inSubShapeIDCreator.GetID())) - return; - - mInnerShape->CollectTransformedShapes(inBox, inPositionCOM, inRotation * mRotation, TransformScale(inScale), inSubShapeIDCreator, ioCollector, inShapeFilter); -} - -void RotatedTranslatedShape::TransformShape(Mat44Arg inCenterOfMassTransform, TransformedShapeCollector &ioCollector) const -{ - mInnerShape->TransformShape(inCenterOfMassTransform * Mat44::sRotation(mRotation), ioCollector); -} - -void RotatedTranslatedShape::sCollideRotatedTranslatedVsShape(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter) -{ - JPH_ASSERT(inShape1->GetSubType() == EShapeSubType::RotatedTranslated); - const RotatedTranslatedShape *shape1 = static_cast(inShape1); - - // Get world transform of 1 - Mat44 transform1 = inCenterOfMassTransform1 * Mat44::sRotation(shape1->mRotation); - - CollisionDispatch::sCollideShapeVsShape(shape1->mInnerShape, inShape2, shape1->TransformScale(inScale1), inScale2, transform1, inCenterOfMassTransform2, inSubShapeIDCreator1, inSubShapeIDCreator2, inCollideShapeSettings, ioCollector, inShapeFilter); -} - -void RotatedTranslatedShape::sCollideShapeVsRotatedTranslated(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter) -{ - JPH_ASSERT(inShape2->GetSubType() == EShapeSubType::RotatedTranslated); - const RotatedTranslatedShape *shape2 = static_cast(inShape2); - - // Get world transform of 2 - Mat44 transform2 = inCenterOfMassTransform2 * Mat44::sRotation(shape2->mRotation); - - CollisionDispatch::sCollideShapeVsShape(inShape1, shape2->mInnerShape, inScale1, shape2->TransformScale(inScale2), inCenterOfMassTransform1, transform2, inSubShapeIDCreator1, inSubShapeIDCreator2, inCollideShapeSettings, ioCollector, inShapeFilter); -} - -void RotatedTranslatedShape::sCollideRotatedTranslatedVsRotatedTranslated(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter) -{ - JPH_ASSERT(inShape1->GetSubType() == EShapeSubType::RotatedTranslated); - const RotatedTranslatedShape *shape1 = static_cast(inShape1); - JPH_ASSERT(inShape2->GetSubType() == EShapeSubType::RotatedTranslated); - const RotatedTranslatedShape *shape2 = static_cast(inShape2); - - // Get world transform of 1 and 2 - Mat44 transform1 = inCenterOfMassTransform1 * Mat44::sRotation(shape1->mRotation); - Mat44 transform2 = inCenterOfMassTransform2 * Mat44::sRotation(shape2->mRotation); - - CollisionDispatch::sCollideShapeVsShape(shape1->mInnerShape, shape2->mInnerShape, shape1->TransformScale(inScale1), shape2->TransformScale(inScale2), transform1, transform2, inSubShapeIDCreator1, inSubShapeIDCreator2, inCollideShapeSettings, ioCollector, inShapeFilter); -} - -void RotatedTranslatedShape::sCastRotatedTranslatedVsShape(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector) -{ - // Fetch rotated translated shape from cast shape - JPH_ASSERT(inShapeCast.mShape->GetSubType() == EShapeSubType::RotatedTranslated); - const RotatedTranslatedShape *shape1 = static_cast(inShapeCast.mShape); - - // Transform the shape cast and update the shape - Mat44 transform = inShapeCast.mCenterOfMassStart * Mat44::sRotation(shape1->mRotation); - Vec3 scale = shape1->TransformScale(inShapeCast.mScale); - ShapeCast shape_cast(shape1->mInnerShape, scale, transform, inShapeCast.mDirection); - - CollisionDispatch::sCastShapeVsShapeLocalSpace(shape_cast, inShapeCastSettings, inShape, inScale, inShapeFilter, inCenterOfMassTransform2, inSubShapeIDCreator1, inSubShapeIDCreator2, ioCollector); -} - -void RotatedTranslatedShape::sCastShapeVsRotatedTranslated(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector) -{ - JPH_ASSERT(inShape->GetSubType() == EShapeSubType::RotatedTranslated); - const RotatedTranslatedShape *shape = static_cast(inShape); - - // Determine the local transform - Mat44 local_transform = Mat44::sRotation(shape->mRotation); - - // Transform the shape cast - ShapeCast shape_cast = inShapeCast.PostTransformed(local_transform.Transposed3x3()); - - CollisionDispatch::sCastShapeVsShapeLocalSpace(shape_cast, inShapeCastSettings, shape->mInnerShape, shape->TransformScale(inScale), inShapeFilter, inCenterOfMassTransform2 * local_transform, inSubShapeIDCreator1, inSubShapeIDCreator2, ioCollector); -} - -void RotatedTranslatedShape::sCastRotatedTranslatedVsRotatedTranslated(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector) -{ - JPH_ASSERT(inShapeCast.mShape->GetSubType() == EShapeSubType::RotatedTranslated); - const RotatedTranslatedShape *shape1 = static_cast(inShapeCast.mShape); - JPH_ASSERT(inShape->GetSubType() == EShapeSubType::RotatedTranslated); - const RotatedTranslatedShape *shape2 = static_cast(inShape); - - // Determine the local transform of shape 2 - Mat44 local_transform2 = Mat44::sRotation(shape2->mRotation); - Mat44 local_transform2_transposed = local_transform2.Transposed3x3(); - - // Transform the shape cast and update the shape - Mat44 transform = (local_transform2_transposed * inShapeCast.mCenterOfMassStart) * Mat44::sRotation(shape1->mRotation); - Vec3 scale = shape1->TransformScale(inShapeCast.mScale); - ShapeCast shape_cast(shape1->mInnerShape, scale, transform, local_transform2_transposed.Multiply3x3(inShapeCast.mDirection)); - - CollisionDispatch::sCastShapeVsShapeLocalSpace(shape_cast, inShapeCastSettings, shape2->mInnerShape, shape2->TransformScale(inScale), inShapeFilter, inCenterOfMassTransform2 * local_transform2, inSubShapeIDCreator1, inSubShapeIDCreator2, ioCollector); -} - -void RotatedTranslatedShape::SaveBinaryState(StreamOut &inStream) const -{ - DecoratedShape::SaveBinaryState(inStream); - - inStream.Write(mCenterOfMass); - inStream.Write(mRotation); -} - -void RotatedTranslatedShape::RestoreBinaryState(StreamIn &inStream) -{ - DecoratedShape::RestoreBinaryState(inStream); - - inStream.Read(mCenterOfMass); - inStream.Read(mRotation); - mIsRotationIdentity = mRotation.IsClose(Quat::sIdentity()); -} - -bool RotatedTranslatedShape::IsValidScale(Vec3Arg inScale) const -{ - if (!DecoratedShape::IsValidScale(inScale)) - return false; - - if (mIsRotationIdentity || ScaleHelpers::IsUniformScale(inScale)) - return mInnerShape->IsValidScale(inScale); - - if (!ScaleHelpers::CanScaleBeRotated(mRotation, inScale)) - return false; - - return mInnerShape->IsValidScale(ScaleHelpers::RotateScale(mRotation, inScale)); -} - -void RotatedTranslatedShape::sRegister() -{ - ShapeFunctions &f = ShapeFunctions::sGet(EShapeSubType::RotatedTranslated); - f.mConstruct = []() -> Shape * { return new RotatedTranslatedShape; }; - f.mColor = Color::sBlue; - - for (EShapeSubType s : sAllSubShapeTypes) - { - CollisionDispatch::sRegisterCollideShape(EShapeSubType::RotatedTranslated, s, sCollideRotatedTranslatedVsShape); - CollisionDispatch::sRegisterCollideShape(s, EShapeSubType::RotatedTranslated, sCollideShapeVsRotatedTranslated); - CollisionDispatch::sRegisterCastShape(EShapeSubType::RotatedTranslated, s, sCastRotatedTranslatedVsShape); - CollisionDispatch::sRegisterCastShape(s, EShapeSubType::RotatedTranslated, sCastShapeVsRotatedTranslated); - } - - CollisionDispatch::sRegisterCollideShape(EShapeSubType::RotatedTranslated, EShapeSubType::RotatedTranslated, sCollideRotatedTranslatedVsRotatedTranslated); - CollisionDispatch::sRegisterCastShape(EShapeSubType::RotatedTranslated, EShapeSubType::RotatedTranslated, sCastRotatedTranslatedVsRotatedTranslated); -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/RotatedTranslatedShape.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/RotatedTranslatedShape.h deleted file mode 100644 index c37dbca82c8..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/RotatedTranslatedShape.h +++ /dev/null @@ -1,158 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include - -JPH_NAMESPACE_BEGIN - -class CollideShapeSettings; - -/// Class that constructs a RotatedTranslatedShape -class JPH_EXPORT RotatedTranslatedShapeSettings final : public DecoratedShapeSettings -{ -public: - JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, RotatedTranslatedShapeSettings) - - /// Constructor - RotatedTranslatedShapeSettings() = default; - - /// Construct with shape settings, can be serialized. - RotatedTranslatedShapeSettings(Vec3Arg inPosition, QuatArg inRotation, const ShapeSettings *inShape) : DecoratedShapeSettings(inShape), mPosition(inPosition), mRotation(inRotation) { } - - /// Variant that uses a concrete shape, which means this object cannot be serialized. - RotatedTranslatedShapeSettings(Vec3Arg inPosition, QuatArg inRotation, const Shape *inShape): DecoratedShapeSettings(inShape), mPosition(inPosition), mRotation(inRotation) { } - - // See: ShapeSettings - virtual ShapeResult Create() const override; - - Vec3 mPosition; ///< Position of the sub shape - Quat mRotation; ///< Rotation of the sub shape -}; - -/// A rotated translated shape will rotate and translate a child shape. -/// Shifts the child object so that it is centered around the center of mass. -class JPH_EXPORT RotatedTranslatedShape final : public DecoratedShape -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Constructor - RotatedTranslatedShape() : DecoratedShape(EShapeSubType::RotatedTranslated) { } - RotatedTranslatedShape(const RotatedTranslatedShapeSettings &inSettings, ShapeResult &outResult); - RotatedTranslatedShape(Vec3Arg inPosition, QuatArg inRotation, const Shape *inShape); - - /// Access the rotation that is applied to the inner shape - Quat GetRotation() const { return mRotation; } - - /// Access the translation that has been applied to the inner shape - Vec3 GetPosition() const { return mCenterOfMass - mRotation * mInnerShape->GetCenterOfMass(); } - - // See Shape::GetCenterOfMass - virtual Vec3 GetCenterOfMass() const override { return mCenterOfMass; } - - // See Shape::GetLocalBounds - virtual AABox GetLocalBounds() const override; - - // See Shape::GetWorldSpaceBounds - virtual AABox GetWorldSpaceBounds(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale) const override; - using Shape::GetWorldSpaceBounds; - - // See Shape::GetInnerRadius - virtual float GetInnerRadius() const override { return mInnerShape->GetInnerRadius(); } - - // See Shape::GetMassProperties - virtual MassProperties GetMassProperties() const override; - - // See Shape::GetSubShapeTransformedShape - virtual TransformedShape GetSubShapeTransformedShape(const SubShapeID &inSubShapeID, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale, SubShapeID &outRemainder) const override; - - // See Shape::GetSurfaceNormal - virtual Vec3 GetSurfaceNormal(const SubShapeID &inSubShapeID, Vec3Arg inLocalSurfacePosition) const override; - - // See Shape::GetSupportingFace - virtual void GetSupportingFace(const SubShapeID &inSubShapeID, Vec3Arg inDirection, Vec3Arg inScale, Mat44Arg inCenterOfMassTransform, SupportingFace &outVertices) const override; - - // See Shape::GetSubmergedVolume - virtual void GetSubmergedVolume(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, const Plane &inSurface, float &outTotalVolume, float &outSubmergedVolume, Vec3 &outCenterOfBuoyancy JPH_IF_DEBUG_RENDERER(, RVec3Arg inBaseOffset)) const override; - -#ifdef JPH_DEBUG_RENDERER - // See Shape::Draw - virtual void Draw(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inUseMaterialColors, bool inDrawWireframe) const override; - - // See Shape::DrawGetSupportFunction - virtual void DrawGetSupportFunction(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inDrawSupportDirection) const override; - - // See Shape::DrawGetSupportingFace - virtual void DrawGetSupportingFace(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale) const override; -#endif // JPH_DEBUG_RENDERER - - // See Shape::CastRay - virtual bool CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const override; - virtual void CastRay(const RayCast &inRay, const RayCastSettings &inRayCastSettings, const SubShapeIDCreator &inSubShapeIDCreator, CastRayCollector &ioCollector, const ShapeFilter &inShapeFilter = { }) const override; - - // See: Shape::CollidePoint - virtual void CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter = { }) const override; - - // See: Shape::CollideSoftBodyVertices - virtual void CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, SoftBodyVertex *ioVertices, uint inNumVertices, float inDeltaTime, Vec3Arg inDisplacementDueToGravity, int inCollidingShapeIndex) const override; - - // See Shape::CollectTransformedShapes - virtual void CollectTransformedShapes(const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale, const SubShapeIDCreator &inSubShapeIDCreator, TransformedShapeCollector &ioCollector, const ShapeFilter &inShapeFilter) const override; - - // See Shape::TransformShape - virtual void TransformShape(Mat44Arg inCenterOfMassTransform, TransformedShapeCollector &ioCollector) const override; - - // See Shape::GetTrianglesStart - virtual void GetTrianglesStart(GetTrianglesContext &ioContext, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) const override { JPH_ASSERT(false, "Cannot call on non-leaf shapes, use CollectTransformedShapes to collect the leaves first!"); } - - // See Shape::GetTrianglesNext - virtual int GetTrianglesNext(GetTrianglesContext &ioContext, int inMaxTrianglesRequested, Float3 *outTriangleVertices, const PhysicsMaterial **outMaterials = nullptr) const override { JPH_ASSERT(false, "Cannot call on non-leaf shapes, use CollectTransformedShapes to collect the leaves first!"); return 0; } - - // See Shape - virtual void SaveBinaryState(StreamOut &inStream) const override; - - // See Shape::GetStats - virtual Stats GetStats() const override { return Stats(sizeof(*this), 0); } - - // See Shape::GetVolume - virtual float GetVolume() const override { return mInnerShape->GetVolume(); } - - // See Shape::IsValidScale - virtual bool IsValidScale(Vec3Arg inScale) const override; - - // Register shape functions with the registry - static void sRegister(); - -protected: - // See: Shape::RestoreBinaryState - virtual void RestoreBinaryState(StreamIn &inStream) override; - -private: - // Helper functions called by CollisionDispatch - static void sCollideRotatedTranslatedVsShape(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter); - static void sCollideShapeVsRotatedTranslated(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter); - static void sCollideRotatedTranslatedVsRotatedTranslated(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter); - static void sCastRotatedTranslatedVsShape(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector); - static void sCastShapeVsRotatedTranslated(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector); - static void sCastRotatedTranslatedVsRotatedTranslated(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector); - - /// Transform the scale to the local space of the child shape - inline Vec3 TransformScale(Vec3Arg inScale) const - { - // We don't need to transform uniform scale or if the rotation is identity - if (mIsRotationIdentity || ScaleHelpers::IsUniformScale(inScale)) - return inScale; - - return ScaleHelpers::RotateScale(mRotation, inScale); - } - - bool mIsRotationIdentity; ///< If mRotation is close to identity (put here because it falls in padding bytes) - Vec3 mCenterOfMass; ///< Position of the center of mass - Quat mRotation; ///< Rotation of the child shape -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/ScaleHelpers.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/ScaleHelpers.h deleted file mode 100644 index ba20c251965..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/ScaleHelpers.h +++ /dev/null @@ -1,68 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -/// Helper functions to get properties of a scaling vector -namespace ScaleHelpers -{ - /// The tolerance used to check if components of the scale vector are the same - static constexpr float cScaleToleranceSq = 1.0e-8f; - - /// Test if a scale is identity - inline bool IsNotScaled(Vec3Arg inScale) { return inScale.IsClose(Vec3::sReplicate(1.0f), cScaleToleranceSq); } - - /// Test if a scale is uniform - inline bool IsUniformScale(Vec3Arg inScale) { return inScale.Swizzle().IsClose(inScale, cScaleToleranceSq); } - - /// Scale the convex radius of an object - inline float ScaleConvexRadius(float inConvexRadius, Vec3Arg inScale) { return min(inConvexRadius * inScale.Abs().ReduceMin(), cDefaultConvexRadius); } - - /// Test if a scale flips an object inside out (which requires flipping all normals and polygon windings) - inline bool IsInsideOut(Vec3Arg inScale) { return (CountBits(Vec3::sLess(inScale, Vec3::sZero()).GetTrues() & 0x7) & 1) != 0; } - - /// Get the average scale if inScale, used to make the scale uniform when a shape doesn't support non-uniform scale - inline Vec3 MakeUniformScale(Vec3Arg inScale) { return Vec3::sReplicate((inScale.GetX() + inScale.GetY() + inScale.GetZ()) / 3.0f); } - - /// Checks in scale can be rotated to child shape - /// @param inRotation Rotation of child shape - /// @param inScale Scale in local space of parent shape - /// @return True if the scale is valid (no shearing introduced) - inline bool CanScaleBeRotated(QuatArg inRotation, Vec3Arg inScale) - { - // inScale is a scale in local space of the shape, so the transform for the shape (ignoring translation) is: T = Mat44::sScale(inScale) * mRotation. - // when we pass the scale to the child it needs to be local to the child, so we want T = mRotation * Mat44::sScale(ChildScale). - // Solving for ChildScale: ChildScale = mRotation^-1 * Mat44::sScale(inScale) * mRotation = mRotation^T * Mat44::sScale(inScale) * mRotation - // If any of the off diagonal elements are non-zero, it means the scale / rotation is not compatible. - Mat44 r = Mat44::sRotation(inRotation); - Mat44 child_scale = r.Multiply3x3LeftTransposed(r.PostScaled(inScale)); - - // Get the columns, but zero the diagonal - Vec4 zero = Vec4::sZero(); - Vec4 c0 = Vec4::sSelect(child_scale.GetColumn4(0), zero, UVec4(0xffffffff, 0, 0, 0)).Abs(); - Vec4 c1 = Vec4::sSelect(child_scale.GetColumn4(1), zero, UVec4(0, 0xffffffff, 0, 0)).Abs(); - Vec4 c2 = Vec4::sSelect(child_scale.GetColumn4(2), zero, UVec4(0, 0, 0xffffffff, 0)).Abs(); - - // Check if all elements are less than epsilon - Vec4 epsilon = Vec4::sReplicate(1.0e-6f); - return UVec4::sAnd(UVec4::sAnd(Vec4::sLess(c0, epsilon), Vec4::sLess(c1, epsilon)), Vec4::sLess(c2, epsilon)).TestAllTrue(); - } - - /// Adjust scale for rotated child shape - /// @param inRotation Rotation of child shape - /// @param inScale Scale in local space of parent shape - /// @return Rotated scale - inline Vec3 RotateScale(QuatArg inRotation, Vec3Arg inScale) - { - // Get the diagonal of mRotation^T * Mat44::sScale(inScale) * mRotation (see comment at CanScaleBeRotated) - Mat44 r = Mat44::sRotation(inRotation); - return r.Multiply3x3LeftTransposed(r.PostScaled(inScale)).GetDiagonal3(); - } -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/ScaledShape.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/ScaledShape.cpp deleted file mode 100644 index 10b1c83f3a5..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/ScaledShape.cpp +++ /dev/null @@ -1,226 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(ScaledShapeSettings) -{ - JPH_ADD_BASE_CLASS(ScaledShapeSettings, DecoratedShapeSettings) - - JPH_ADD_ATTRIBUTE(ScaledShapeSettings, mScale) -} - -ShapeSettings::ShapeResult ScaledShapeSettings::Create() const -{ - if (mCachedResult.IsEmpty()) - Ref shape = new ScaledShape(*this, mCachedResult); - return mCachedResult; -} - -ScaledShape::ScaledShape(const ScaledShapeSettings &inSettings, ShapeResult &outResult) : - DecoratedShape(EShapeSubType::Scaled, inSettings, outResult), - mScale(inSettings.mScale) -{ - if (outResult.HasError()) - return; - - outResult.Set(this); -} - -MassProperties ScaledShape::GetMassProperties() const -{ - MassProperties p = mInnerShape->GetMassProperties(); - p.Scale(mScale); - return p; -} - -AABox ScaledShape::GetLocalBounds() const -{ - return mInnerShape->GetLocalBounds().Scaled(mScale); -} - -AABox ScaledShape::GetWorldSpaceBounds(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale) const -{ - return mInnerShape->GetWorldSpaceBounds(inCenterOfMassTransform, inScale * mScale); -} - -TransformedShape ScaledShape::GetSubShapeTransformedShape(const SubShapeID &inSubShapeID, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale, SubShapeID &outRemainder) const -{ - // We don't use any bits in the sub shape ID - outRemainder = inSubShapeID; - - TransformedShape ts(RVec3(inPositionCOM), inRotation, mInnerShape, BodyID()); - ts.SetShapeScale(inScale * mScale); - return ts; -} - -Vec3 ScaledShape::GetSurfaceNormal(const SubShapeID &inSubShapeID, Vec3Arg inLocalSurfacePosition) const -{ - // Transform the surface point to local space and pass the query on - Vec3 normal = mInnerShape->GetSurfaceNormal(inSubShapeID, inLocalSurfacePosition / mScale); - - // Need to transform the plane normals using inScale - // Transforming a direction with matrix M is done through multiplying by (M^-1)^T - // In this case M is a diagonal matrix with the scale vector, so we need to multiply our normal by 1 / scale and renormalize afterwards - return (normal / mScale).Normalized(); -} - -void ScaledShape::GetSupportingFace(const SubShapeID &inSubShapeID, Vec3Arg inDirection, Vec3Arg inScale, Mat44Arg inCenterOfMassTransform, SupportingFace &outVertices) const -{ - mInnerShape->GetSupportingFace(inSubShapeID, inDirection, inScale * mScale, inCenterOfMassTransform, outVertices); -} - -void ScaledShape::GetSubmergedVolume(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, const Plane &inSurface, float &outTotalVolume, float &outSubmergedVolume, Vec3 &outCenterOfBuoyancy JPH_IF_DEBUG_RENDERER(, RVec3Arg inBaseOffset)) const -{ - mInnerShape->GetSubmergedVolume(inCenterOfMassTransform, inScale * mScale, inSurface, outTotalVolume, outSubmergedVolume, outCenterOfBuoyancy JPH_IF_DEBUG_RENDERER(, inBaseOffset)); -} - -#ifdef JPH_DEBUG_RENDERER -void ScaledShape::Draw(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inUseMaterialColors, bool inDrawWireframe) const -{ - mInnerShape->Draw(inRenderer, inCenterOfMassTransform, inScale * mScale, inColor, inUseMaterialColors, inDrawWireframe); -} - -void ScaledShape::DrawGetSupportFunction(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inDrawSupportDirection) const -{ - mInnerShape->DrawGetSupportFunction(inRenderer, inCenterOfMassTransform, inScale * mScale, inColor, inDrawSupportDirection); -} - -void ScaledShape::DrawGetSupportingFace(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale) const -{ - mInnerShape->DrawGetSupportingFace(inRenderer, inCenterOfMassTransform, inScale * mScale); -} -#endif // JPH_DEBUG_RENDERER - -bool ScaledShape::CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const -{ - Vec3 inv_scale = mScale.Reciprocal(); - RayCast scaled_ray { inv_scale * inRay.mOrigin, inv_scale * inRay.mDirection }; - return mInnerShape->CastRay(scaled_ray, inSubShapeIDCreator, ioHit); -} - -void ScaledShape::CastRay(const RayCast &inRay, const RayCastSettings &inRayCastSettings, const SubShapeIDCreator &inSubShapeIDCreator, CastRayCollector &ioCollector, const ShapeFilter &inShapeFilter) const -{ - // Test shape filter - if (!inShapeFilter.ShouldCollide(this, inSubShapeIDCreator.GetID())) - return; - - Vec3 inv_scale = mScale.Reciprocal(); - RayCast scaled_ray { inv_scale * inRay.mOrigin, inv_scale * inRay.mDirection }; - return mInnerShape->CastRay(scaled_ray, inRayCastSettings, inSubShapeIDCreator, ioCollector, inShapeFilter); -} - -void ScaledShape::CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter) const -{ - // Test shape filter - if (!inShapeFilter.ShouldCollide(this, inSubShapeIDCreator.GetID())) - return; - - Vec3 inv_scale = mScale.Reciprocal(); - mInnerShape->CollidePoint(inv_scale * inPoint, inSubShapeIDCreator, ioCollector, inShapeFilter); -} - -void ScaledShape::CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, SoftBodyVertex *ioVertices, uint inNumVertices, float inDeltaTime, Vec3Arg inDisplacementDueToGravity, int inCollidingShapeIndex) const -{ - mInnerShape->CollideSoftBodyVertices(inCenterOfMassTransform, inScale * mScale, ioVertices, inNumVertices, inDeltaTime, inDisplacementDueToGravity, inCollidingShapeIndex); -} - -void ScaledShape::CollectTransformedShapes(const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale, const SubShapeIDCreator &inSubShapeIDCreator, TransformedShapeCollector &ioCollector, const ShapeFilter &inShapeFilter) const -{ - // Test shape filter - if (!inShapeFilter.ShouldCollide(this, inSubShapeIDCreator.GetID())) - return; - - mInnerShape->CollectTransformedShapes(inBox, inPositionCOM, inRotation, inScale * mScale, inSubShapeIDCreator, ioCollector, inShapeFilter); -} - -void ScaledShape::TransformShape(Mat44Arg inCenterOfMassTransform, TransformedShapeCollector &ioCollector) const -{ - mInnerShape->TransformShape(inCenterOfMassTransform * Mat44::sScale(mScale), ioCollector); -} - -void ScaledShape::SaveBinaryState(StreamOut &inStream) const -{ - DecoratedShape::SaveBinaryState(inStream); - - inStream.Write(mScale); -} - -void ScaledShape::RestoreBinaryState(StreamIn &inStream) -{ - DecoratedShape::RestoreBinaryState(inStream); - - inStream.Read(mScale); -} - -float ScaledShape::GetVolume() const -{ - return abs(mScale.GetX() * mScale.GetY() * mScale.GetZ()) * mInnerShape->GetVolume(); -} - -bool ScaledShape::IsValidScale(Vec3Arg inScale) const -{ - return mInnerShape->IsValidScale(inScale * mScale); -} - -void ScaledShape::sCollideScaledVsShape(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter) -{ - JPH_ASSERT(inShape1->GetSubType() == EShapeSubType::Scaled); - const ScaledShape *shape1 = static_cast(inShape1); - - CollisionDispatch::sCollideShapeVsShape(shape1->GetInnerShape(), inShape2, inScale1 * shape1->GetScale(), inScale2, inCenterOfMassTransform1, inCenterOfMassTransform2, inSubShapeIDCreator1, inSubShapeIDCreator2, inCollideShapeSettings, ioCollector, inShapeFilter); -} - -void ScaledShape::sCollideShapeVsScaled(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter) -{ - JPH_ASSERT(inShape2->GetSubType() == EShapeSubType::Scaled); - const ScaledShape *shape2 = static_cast(inShape2); - - CollisionDispatch::sCollideShapeVsShape(inShape1, shape2->GetInnerShape(), inScale1, inScale2 * shape2->GetScale(), inCenterOfMassTransform1, inCenterOfMassTransform2, inSubShapeIDCreator1, inSubShapeIDCreator2, inCollideShapeSettings, ioCollector, inShapeFilter); -} - -void ScaledShape::sCastScaledVsShape(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector) -{ - JPH_ASSERT(inShapeCast.mShape->GetSubType() == EShapeSubType::Scaled); - const ScaledShape *shape = static_cast(inShapeCast.mShape); - - ShapeCast scaled_cast(shape->GetInnerShape(), inShapeCast.mScale * shape->GetScale(), inShapeCast.mCenterOfMassStart, inShapeCast.mDirection); - CollisionDispatch::sCastShapeVsShapeLocalSpace(scaled_cast, inShapeCastSettings, inShape, inScale, inShapeFilter, inCenterOfMassTransform2, inSubShapeIDCreator1, inSubShapeIDCreator2, ioCollector); -} - -void ScaledShape::sCastShapeVsScaled(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector) -{ - JPH_ASSERT(inShape->GetSubType() == EShapeSubType::Scaled); - const ScaledShape *shape = static_cast(inShape); - - CollisionDispatch::sCastShapeVsShapeLocalSpace(inShapeCast, inShapeCastSettings, shape->mInnerShape, inScale * shape->mScale, inShapeFilter, inCenterOfMassTransform2, inSubShapeIDCreator1, inSubShapeIDCreator2, ioCollector); -} - -void ScaledShape::sRegister() -{ - ShapeFunctions &f = ShapeFunctions::sGet(EShapeSubType::Scaled); - f.mConstruct = []() -> Shape * { return new ScaledShape; }; - f.mColor = Color::sYellow; - - for (EShapeSubType s : sAllSubShapeTypes) - { - CollisionDispatch::sRegisterCollideShape(EShapeSubType::Scaled, s, sCollideScaledVsShape); - CollisionDispatch::sRegisterCollideShape(s, EShapeSubType::Scaled, sCollideShapeVsScaled); - CollisionDispatch::sRegisterCastShape(EShapeSubType::Scaled, s, sCastScaledVsShape); - CollisionDispatch::sRegisterCastShape(s, EShapeSubType::Scaled, sCastShapeVsScaled); - } -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/ScaledShape.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/ScaledShape.h deleted file mode 100644 index ea9b3435175..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/ScaledShape.h +++ /dev/null @@ -1,140 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -class SubShapeIDCreator; -class CollideShapeSettings; - -/// Class that constructs a ScaledShape -class JPH_EXPORT ScaledShapeSettings final : public DecoratedShapeSettings -{ - JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, ScaledShapeSettings) - - /// Default constructor for deserialization - ScaledShapeSettings() = default; - - /// Constructor that decorates another shape with a scale - ScaledShapeSettings(const ShapeSettings *inShape, Vec3Arg inScale) : DecoratedShapeSettings(inShape), mScale(inScale) { } - - /// Variant that uses a concrete shape, which means this object cannot be serialized. - ScaledShapeSettings(const Shape *inShape, Vec3Arg inScale) : DecoratedShapeSettings(inShape), mScale(inScale) { } - - // See: ShapeSettings - virtual ShapeResult Create() const override; - - Vec3 mScale = Vec3(1, 1, 1); -}; - -/// A shape that scales a child shape in local space of that shape. The scale can be non-uniform and can even turn it inside out when one or three components of the scale are negative. -class JPH_EXPORT ScaledShape final : public DecoratedShape -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Constructor - ScaledShape() : DecoratedShape(EShapeSubType::Scaled) { } - ScaledShape(const ScaledShapeSettings &inSettings, ShapeResult &outResult); - - /// Constructor that decorates another shape with a scale - ScaledShape(const Shape *inShape, Vec3Arg inScale) : DecoratedShape(EShapeSubType::Scaled, inShape), mScale(inScale) { } - - /// Get the scale - Vec3 GetScale() const { return mScale; } - - // See Shape::GetCenterOfMass - virtual Vec3 GetCenterOfMass() const override { return mScale * mInnerShape->GetCenterOfMass(); } - - // See Shape::GetLocalBounds - virtual AABox GetLocalBounds() const override; - - // See Shape::GetWorldSpaceBounds - virtual AABox GetWorldSpaceBounds(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale) const override; - using Shape::GetWorldSpaceBounds; - - // See Shape::GetInnerRadius - virtual float GetInnerRadius() const override { return mScale.ReduceMin() * mInnerShape->GetInnerRadius(); } - - // See Shape::GetMassProperties - virtual MassProperties GetMassProperties() const override; - - // See Shape::GetSubShapeTransformedShape - virtual TransformedShape GetSubShapeTransformedShape(const SubShapeID &inSubShapeID, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale, SubShapeID &outRemainder) const override; - - // See Shape::GetSurfaceNormal - virtual Vec3 GetSurfaceNormal(const SubShapeID &inSubShapeID, Vec3Arg inLocalSurfacePosition) const override; - - // See Shape::GetSupportingFace - virtual void GetSupportingFace(const SubShapeID &inSubShapeID, Vec3Arg inDirection, Vec3Arg inScale, Mat44Arg inCenterOfMassTransform, SupportingFace &outVertices) const override; - - // See Shape::GetSubmergedVolume - virtual void GetSubmergedVolume(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, const Plane &inSurface, float &outTotalVolume, float &outSubmergedVolume, Vec3 &outCenterOfBuoyancy JPH_IF_DEBUG_RENDERER(, RVec3Arg inBaseOffset)) const override; - -#ifdef JPH_DEBUG_RENDERER - // See Shape::Draw - virtual void Draw(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inUseMaterialColors, bool inDrawWireframe) const override; - - // See Shape::DrawGetSupportFunction - virtual void DrawGetSupportFunction(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inDrawSupportDirection) const override; - - // See Shape::DrawGetSupportingFace - virtual void DrawGetSupportingFace(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale) const override; -#endif // JPH_DEBUG_RENDERER - - // See Shape::CastRay - virtual bool CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const override; - virtual void CastRay(const RayCast &inRay, const RayCastSettings &inRayCastSettings, const SubShapeIDCreator &inSubShapeIDCreator, CastRayCollector &ioCollector, const ShapeFilter &inShapeFilter = { }) const override; - - // See: Shape::CollidePoint - virtual void CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter = { }) const override; - - // See: Shape::CollideSoftBodyVertices - virtual void CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, SoftBodyVertex *ioVertices, uint inNumVertices, float inDeltaTime, Vec3Arg inDisplacementDueToGravity, int inCollidingShapeIndex) const override; - - // See Shape::CollectTransformedShapes - virtual void CollectTransformedShapes(const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale, const SubShapeIDCreator &inSubShapeIDCreator, TransformedShapeCollector &ioCollector, const ShapeFilter &inShapeFilter) const override; - - // See Shape::TransformShape - virtual void TransformShape(Mat44Arg inCenterOfMassTransform, TransformedShapeCollector &ioCollector) const override; - - // See Shape::GetTrianglesStart - virtual void GetTrianglesStart(GetTrianglesContext &ioContext, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) const override { JPH_ASSERT(false, "Cannot call on non-leaf shapes, use CollectTransformedShapes to collect the leaves first!"); } - - // See Shape::GetTrianglesNext - virtual int GetTrianglesNext(GetTrianglesContext &ioContext, int inMaxTrianglesRequested, Float3 *outTriangleVertices, const PhysicsMaterial **outMaterials = nullptr) const override { JPH_ASSERT(false, "Cannot call on non-leaf shapes, use CollectTransformedShapes to collect the leaves first!"); return 0; } - - // See Shape - virtual void SaveBinaryState(StreamOut &inStream) const override; - - // See Shape::GetStats - virtual Stats GetStats() const override { return Stats(sizeof(*this), 0); } - - // See Shape::GetVolume - virtual float GetVolume() const override; - - // See Shape::IsValidScale - virtual bool IsValidScale(Vec3Arg inScale) const override; - - // Register shape functions with the registry - static void sRegister(); - -protected: - // See: Shape::RestoreBinaryState - virtual void RestoreBinaryState(StreamIn &inStream) override; - -private: - // Helper functions called by CollisionDispatch - static void sCollideScaledVsShape(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter); - static void sCollideShapeVsScaled(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter); - static void sCastScaledVsShape(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector); - static void sCastShapeVsScaled(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector); - - Vec3 mScale = Vec3(1, 1, 1); -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/Shape.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/Shape.cpp deleted file mode 100644 index 8412156f7b6..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/Shape.cpp +++ /dev/null @@ -1,309 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -JPH_IMPLEMENT_SERIALIZABLE_ABSTRACT_BASE(ShapeSettings) -{ - JPH_ADD_BASE_CLASS(ShapeSettings, SerializableObject) - - JPH_ADD_ATTRIBUTE(ShapeSettings, mUserData) -} - -#ifdef JPH_DEBUG_RENDERER -bool Shape::sDrawSubmergedVolumes = false; -#endif // JPH_DEBUG_RENDERER - -ShapeFunctions ShapeFunctions::sRegistry[NumSubShapeTypes]; - -TransformedShape Shape::GetSubShapeTransformedShape(const SubShapeID &inSubShapeID, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale, SubShapeID &outRemainder) const -{ - // We have reached the leaf shape so there is no remainder - outRemainder = SubShapeID(); - - // Just return the transformed shape for this shape - TransformedShape ts(RVec3(inPositionCOM), inRotation, this, BodyID()); - ts.SetShapeScale(inScale); - return ts; -} - -void Shape::CollectTransformedShapes(const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale, const SubShapeIDCreator &inSubShapeIDCreator, TransformedShapeCollector &ioCollector, const ShapeFilter &inShapeFilter) const -{ - // Test shape filter - if (!inShapeFilter.ShouldCollide(this, inSubShapeIDCreator.GetID())) - return; - - TransformedShape ts(RVec3(inPositionCOM), inRotation, this, TransformedShape::sGetBodyID(ioCollector.GetContext()), inSubShapeIDCreator); - ts.SetShapeScale(inScale); - ioCollector.AddHit(ts); -} - -void Shape::TransformShape(Mat44Arg inCenterOfMassTransform, TransformedShapeCollector &ioCollector) const -{ - Vec3 scale; - Mat44 transform = inCenterOfMassTransform.Decompose(scale); - TransformedShape ts(RVec3(transform.GetTranslation()), transform.GetQuaternion(), this, BodyID(), SubShapeIDCreator()); - ts.SetShapeScale(scale); - ioCollector.AddHit(ts); -} - -void Shape::SaveBinaryState(StreamOut &inStream) const -{ - inStream.Write(mShapeSubType); - inStream.Write(mUserData); -} - -void Shape::RestoreBinaryState(StreamIn &inStream) -{ - // Type hash read by sRestoreFromBinaryState - inStream.Read(mUserData); -} - -Shape::ShapeResult Shape::sRestoreFromBinaryState(StreamIn &inStream) -{ - ShapeResult result; - - // Read the type of the shape - EShapeSubType shape_sub_type; - inStream.Read(shape_sub_type); - if (inStream.IsEOF() || inStream.IsFailed()) - { - result.SetError("Failed to read type id"); - return result; - } - - // Construct and read the data of the shape - Ref shape = ShapeFunctions::sGet(shape_sub_type).mConstruct(); - shape->RestoreBinaryState(inStream); - if (inStream.IsEOF() || inStream.IsFailed()) - { - result.SetError("Failed to restore shape"); - return result; - } - - result.Set(shape); - return result; -} - -void Shape::SaveWithChildren(StreamOut &inStream, ShapeToIDMap &ioShapeMap, MaterialToIDMap &ioMaterialMap) const -{ - ShapeToIDMap::const_iterator shape_id_iter = ioShapeMap.find(this); - if (shape_id_iter == ioShapeMap.end()) - { - // Write shape ID of this shape - uint32 shape_id = (uint32)ioShapeMap.size(); - ioShapeMap[this] = shape_id; - inStream.Write(shape_id); - - // Write the shape itself - SaveBinaryState(inStream); - - // Write the ID's of all sub shapes - ShapeList sub_shapes; - SaveSubShapeState(sub_shapes); - inStream.Write(sub_shapes.size()); - for (const Shape *shape : sub_shapes) - { - if (shape == nullptr) - inStream.Write(~uint32(0)); - else - shape->SaveWithChildren(inStream, ioShapeMap, ioMaterialMap); - } - - // Write the materials - PhysicsMaterialList materials; - SaveMaterialState(materials); - StreamUtils::SaveObjectArray(inStream, materials, &ioMaterialMap); - } - else - { - // Known shape, just write the ID - inStream.Write(shape_id_iter->second); - } -} - -Shape::ShapeResult Shape::sRestoreWithChildren(StreamIn &inStream, IDToShapeMap &ioShapeMap, IDToMaterialMap &ioMaterialMap) -{ - ShapeResult result; - - // Read ID of this shape - uint32 shape_id; - inStream.Read(shape_id); - if (inStream.IsEOF() || inStream.IsFailed()) - { - result.SetError("Failed to read shape id"); - return result; - } - - // Check nullptr shape - if (shape_id == ~uint32(0)) - { - result.Set(nullptr); - return result; - } - - // Check if we already read this shape - if (shape_id < ioShapeMap.size()) - { - result.Set(ioShapeMap[shape_id]); - return result; - } - - // Read the shape - result = sRestoreFromBinaryState(inStream); - if (result.HasError()) - return result; - JPH_ASSERT(ioShapeMap.size() == shape_id); // Assert that this is the next ID in the map - ioShapeMap.push_back(result.Get()); - - // Read the sub shapes - size_t len; - inStream.Read(len); - if (inStream.IsEOF() || inStream.IsFailed()) - { - result.SetError("Failed to read stream"); - return result; - } - ShapeList sub_shapes; - sub_shapes.reserve(len); - for (size_t i = 0; i < len; ++i) - { - ShapeResult sub_shape_result = sRestoreWithChildren(inStream, ioShapeMap, ioMaterialMap); - if (sub_shape_result.HasError()) - return sub_shape_result; - sub_shapes.push_back(sub_shape_result.Get()); - } - result.Get()->RestoreSubShapeState(sub_shapes.data(), (uint)sub_shapes.size()); - - // Read the materials - Result mlresult = StreamUtils::RestoreObjectArray(inStream, ioMaterialMap); - if (mlresult.HasError()) - { - result.SetError(mlresult.GetError()); - return result; - } - const PhysicsMaterialList &materials = mlresult.Get(); - result.Get()->RestoreMaterialState(materials.data(), (uint)materials.size()); - - return result; -} - -Shape::Stats Shape::GetStatsRecursive(VisitedShapes &ioVisitedShapes) const -{ - Stats stats = GetStats(); - - // If shape is already visited, don't count its size again - if (!ioVisitedShapes.insert(this).second) - stats.mSizeBytes = 0; - - return stats; -} - -Shape::ShapeResult Shape::ScaleShape(Vec3Arg inScale) const -{ - const Vec3 unit_scale = Vec3::sReplicate(1.0f); - - if (inScale.IsNearZero()) - { - ShapeResult result; - result.SetError("Can't use zero scale!"); - return result; - } - - // First test if we can just wrap this shape in a scaled shape - if (IsValidScale(inScale)) - { - // Test if the scale is near unit - ShapeResult result; - if (inScale.IsClose(unit_scale)) - result.Set(const_cast(this)); - else - result.Set(new ScaledShape(this, inScale)); - return result; - } - - // Collect the leaf shapes and their transforms - struct Collector : TransformedShapeCollector - { - virtual void AddHit(const ResultType &inResult) override - { - mShapes.push_back(inResult); - } - - Array mShapes; - }; - Collector collector; - TransformShape(Mat44::sScale(inScale) * Mat44::sTranslation(GetCenterOfMass()), collector); - - // Construct a compound shape - StaticCompoundShapeSettings compound; - compound.mSubShapes.reserve(collector.mShapes.size()); - for (const TransformedShape &ts : collector.mShapes) - { - const Shape *shape = ts.mShape; - - // Construct a scaled shape if scale is not unit - Vec3 scale = ts.GetShapeScale(); - if (!scale.IsClose(unit_scale)) - shape = new ScaledShape(shape, scale); - - // Add the shape - compound.AddShape(Vec3(ts.mShapePositionCOM) - ts.mShapeRotation * shape->GetCenterOfMass(), ts.mShapeRotation, shape); - } - - return compound.Create(); -} - -void Shape::sCollidePointUsingRayCast(const Shape &inShape, Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter) -{ - // First test if we're inside our bounding box - AABox bounds = inShape.GetLocalBounds(); - if (bounds.Contains(inPoint)) - { - // A collector that just counts the number of hits - class HitCountCollector : public CastRayCollector - { - public: - virtual void AddHit(const RayCastResult &inResult) override - { - // Store the last sub shape ID so that we can provide something to our outer hit collector - mSubShapeID = inResult.mSubShapeID2; - - ++mHitCount; - } - - int mHitCount = 0; - SubShapeID mSubShapeID; - }; - HitCountCollector collector; - - // Configure the raycast - RayCastSettings settings; - settings.mBackFaceMode = EBackFaceMode::CollideWithBackFaces; - - // Cast a ray that's 10% longer than the height of our bounding box - inShape.CastRay(RayCast { inPoint, 1.1f * bounds.GetSize().GetY() * Vec3::sAxisY() }, settings, inSubShapeIDCreator, collector, inShapeFilter); - - // Odd amount of hits means inside - if ((collector.mHitCount & 1) == 1) - ioCollector.AddHit({ TransformedShape::sGetBodyID(ioCollector.GetContext()), collector.mSubShapeID }); - } -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/Shape.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/Shape.h deleted file mode 100644 index f7abc1e68c1..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/Shape.h +++ /dev/null @@ -1,447 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -struct RayCast; -class RayCastSettings; -struct ShapeCast; -class ShapeCastSettings; -class RayCastResult; -class ShapeCastResult; -class CollidePointResult; -class CollideShapeResult; -class SubShapeIDCreator; -class SubShapeID; -class PhysicsMaterial; -class TransformedShape; -class Plane; -class SoftBodyVertex; -class Shape; -class StreamOut; -class StreamIn; -#ifdef JPH_DEBUG_RENDERER -class DebugRenderer; -#endif // JPH_DEBUG_RENDERER - -using CastRayCollector = CollisionCollector; -using CastShapeCollector = CollisionCollector; -using CollidePointCollector = CollisionCollector; -using CollideShapeCollector = CollisionCollector; -using TransformedShapeCollector = CollisionCollector; - -using ShapeRefC = RefConst; -using ShapeList = Array; -using PhysicsMaterialRefC = RefConst; -using PhysicsMaterialList = Array; - -/// Shapes are categorized in groups, each shape can return which group it belongs to through its Shape::GetType function. -enum class EShapeType : uint8 -{ - Convex, ///< Used by ConvexShape, all shapes that use the generic convex vs convex collision detection system (box, sphere, capsule, tapered capsule, cylinder, triangle) - Compound, ///< Used by CompoundShape - Decorated, ///< Used by DecoratedShape - Mesh, ///< Used by MeshShape - HeightField, ///< Used by HeightFieldShape - SoftBody, ///< Used by SoftBodyShape - - // User defined shapes - User1, - User2, - User3, - User4, -}; - -/// This enumerates all shape types, each shape can return its type through Shape::GetSubType -enum class EShapeSubType : uint8 -{ - // Convex shapes - Sphere, - Box, - Triangle, - Capsule, - TaperedCapsule, - Cylinder, - ConvexHull, - - // Compound shapes - StaticCompound, - MutableCompound, - - // Decorated shapes - RotatedTranslated, - Scaled, - OffsetCenterOfMass, - - // Other shapes - Mesh, - HeightField, - SoftBody, - - // User defined shapes - User1, - User2, - User3, - User4, - User5, - User6, - User7, - User8, - - // User defined convex shapes - UserConvex1, - UserConvex2, - UserConvex3, - UserConvex4, - UserConvex5, - UserConvex6, - UserConvex7, - UserConvex8, -}; - -// Sets of shape sub types -static constexpr EShapeSubType sAllSubShapeTypes[] = { EShapeSubType::Sphere, EShapeSubType::Box, EShapeSubType::Triangle, EShapeSubType::Capsule, EShapeSubType::TaperedCapsule, EShapeSubType::Cylinder, EShapeSubType::ConvexHull, EShapeSubType::StaticCompound, EShapeSubType::MutableCompound, EShapeSubType::RotatedTranslated, EShapeSubType::Scaled, EShapeSubType::OffsetCenterOfMass, EShapeSubType::Mesh, EShapeSubType::HeightField, EShapeSubType::SoftBody, EShapeSubType::User1, EShapeSubType::User2, EShapeSubType::User3, EShapeSubType::User4, EShapeSubType::User5, EShapeSubType::User6, EShapeSubType::User7, EShapeSubType::User8, EShapeSubType::UserConvex1, EShapeSubType::UserConvex2, EShapeSubType::UserConvex3, EShapeSubType::UserConvex4, EShapeSubType::UserConvex5, EShapeSubType::UserConvex6, EShapeSubType::UserConvex7, EShapeSubType::UserConvex8 }; -static constexpr EShapeSubType sConvexSubShapeTypes[] = { EShapeSubType::Sphere, EShapeSubType::Box, EShapeSubType::Triangle, EShapeSubType::Capsule, EShapeSubType::TaperedCapsule, EShapeSubType::Cylinder, EShapeSubType::ConvexHull, EShapeSubType::UserConvex1, EShapeSubType::UserConvex2, EShapeSubType::UserConvex3, EShapeSubType::UserConvex4, EShapeSubType::UserConvex5, EShapeSubType::UserConvex6, EShapeSubType::UserConvex7, EShapeSubType::UserConvex8 }; -static constexpr EShapeSubType sCompoundSubShapeTypes[] = { EShapeSubType::StaticCompound, EShapeSubType::MutableCompound }; -static constexpr EShapeSubType sDecoratorSubShapeTypes[] = { EShapeSubType::RotatedTranslated, EShapeSubType::Scaled, EShapeSubType::OffsetCenterOfMass }; - -/// How many shape types we support -static constexpr uint NumSubShapeTypes = uint(size(sAllSubShapeTypes)); - -/// Names of sub shape types -static constexpr const char *sSubShapeTypeNames[] = { "Sphere", "Box", "Triangle", "Capsule", "TaperedCapsule", "Cylinder", "ConvexHull", "StaticCompound", "MutableCompound", "RotatedTranslated", "Scaled", "OffsetCenterOfMass", "Mesh", "HeightField", "SoftBody", "User1", "User2", "User3", "User4", "User5", "User6", "User7", "User8", "UserConvex1", "UserConvex2", "UserConvex3", "UserConvex4", "UserConvex5", "UserConvex6", "UserConvex7", "UserConvex8" }; -static_assert(size(sSubShapeTypeNames) == NumSubShapeTypes); - -/// Class that can construct shapes and that is serializable using the ObjectStream system. -/// Can be used to store shape data in 'uncooked' form (i.e. in a form that is still human readable and authorable). -/// Once the shape has been created using the Create() function, the data will be moved into the Shape class -/// in a form that is optimized for collision detection. After this, the ShapeSettings object is no longer needed -/// and can be destroyed. Each shape class has a derived class of the ShapeSettings object to store shape specific -/// data. -class JPH_EXPORT ShapeSettings : public SerializableObject, public RefTarget -{ -public: - JPH_DECLARE_SERIALIZABLE_ABSTRACT(JPH_EXPORT, ShapeSettings) - - using ShapeResult = Result>; - - /// Create a shape according to the settings specified by this object. - virtual ShapeResult Create() const = 0; - - /// When creating a shape, the result is cached so that calling Create() again will return the same shape. - /// If you make changes to the ShapeSettings you need to call this function to clear the cached result to allow Create() to build a new shape. - void ClearCachedResult() { mCachedResult.Clear(); } - - /// User data (to be used freely by the application) - uint64 mUserData = 0; - -protected: - mutable ShapeResult mCachedResult; -}; - -/// Function table for functions on shapes -class JPH_EXPORT ShapeFunctions -{ -public: - /// Construct a shape - Shape * (*mConstruct)() = nullptr; - - /// Color of the shape when drawing - Color mColor = Color::sBlack; - - /// Get an entry in the registry for a particular sub type - static inline ShapeFunctions & sGet(EShapeSubType inSubType) { return sRegistry[int(inSubType)]; } - -private: - static ShapeFunctions sRegistry[NumSubShapeTypes]; -}; - -/// Base class for all shapes (collision volume of a body). Defines a virtual interface for collision detection. -class JPH_EXPORT Shape : public RefTarget, public NonCopyable -{ -public: - JPH_OVERRIDE_NEW_DELETE - - using ShapeResult = ShapeSettings::ShapeResult; - - /// Constructor - Shape(EShapeType inType, EShapeSubType inSubType) : mShapeType(inType), mShapeSubType(inSubType) { } - Shape(EShapeType inType, EShapeSubType inSubType, const ShapeSettings &inSettings, [[maybe_unused]] ShapeResult &outResult) : mUserData(inSettings.mUserData), mShapeType(inType), mShapeSubType(inSubType) { } - - /// Destructor - virtual ~Shape() = default; - - /// Get type - inline EShapeType GetType() const { return mShapeType; } - inline EShapeSubType GetSubType() const { return mShapeSubType; } - - /// User data (to be used freely by the application) - uint64 GetUserData() const { return mUserData; } - void SetUserData(uint64 inUserData) { mUserData = inUserData; } - - /// Check if this shape can only be used to create a static body or if it can also be dynamic/kinematic - virtual bool MustBeStatic() const { return false; } - - /// All shapes are centered around their center of mass. This function returns the center of mass position that needs to be applied to transform the shape to where it was created. - virtual Vec3 GetCenterOfMass() const { return Vec3::sZero(); } - - /// Get local bounding box including convex radius, this box is centered around the center of mass rather than the world transform - virtual AABox GetLocalBounds() const = 0; - - /// Get the max number of sub shape ID bits that are needed to be able to address any leaf shape in this shape. Used mainly for checking that it is smaller or equal than SubShapeID::MaxBits. - virtual uint GetSubShapeIDBitsRecursive() const = 0; - - /// Get world space bounds including convex radius. - /// This shape is scaled by inScale in local space first. - /// This function can be overridden to return a closer fitting world space bounding box, by default it will just transform what GetLocalBounds() returns. - virtual AABox GetWorldSpaceBounds(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale) const { return GetLocalBounds().Scaled(inScale).Transformed(inCenterOfMassTransform); } - - /// Get world space bounds including convex radius. - AABox GetWorldSpaceBounds(DMat44Arg inCenterOfMassTransform, Vec3Arg inScale) const - { - // Use single precision version using the rotation only - AABox bounds = GetWorldSpaceBounds(inCenterOfMassTransform.GetRotation(), inScale); - - // Apply translation - bounds.Translate(inCenterOfMassTransform.GetTranslation()); - - return bounds; - } - - /// Returns the radius of the biggest sphere that fits entirely in the shape. In case this shape consists of multiple sub shapes, it returns the smallest sphere of the parts. - /// This can be used as a measure of how far the shape can be moved without risking going through geometry. - virtual float GetInnerRadius() const = 0; - - /// Calculate the mass and inertia of this shape - virtual MassProperties GetMassProperties() const = 0; - - /// Get the material assigned to a particular sub shape ID - virtual const PhysicsMaterial * GetMaterial(const SubShapeID &inSubShapeID) const = 0; - - /// Get the surface normal of a particular sub shape ID and point on surface (all vectors are relative to center of mass for this shape). - /// Note: When you have a CollideShapeResult or ShapeCastResult you should use -mPenetrationAxis.Normalized() as contact normal as GetSurfaceNormal will only return face normals (and not vertex or edge normals). - virtual Vec3 GetSurfaceNormal(const SubShapeID &inSubShapeID, Vec3Arg inLocalSurfacePosition) const = 0; - - /// Type definition for a supporting face - using SupportingFace = StaticArray; - - /// Get the vertices of the face that faces inDirection the most (includes any convex radius). Note that this function can only return faces of - /// convex shapes or triangles, which is why a sub shape ID to get to that leaf must be provided. - /// @param inSubShapeID Sub shape ID of target shape - /// @param inDirection Direction that the face should be facing (in local space to this shape) - /// @param inCenterOfMassTransform Transform to transform outVertices with - /// @param inScale Scale of this shape - /// @param outVertices Resulting face. The returned face can be empty if the shape doesn't have polygons to return (e.g. because it's a sphere). The face will be returned in world space. - virtual void GetSupportingFace([[maybe_unused]] const SubShapeID &inSubShapeID, [[maybe_unused]] Vec3Arg inDirection, [[maybe_unused]] Vec3Arg inScale, [[maybe_unused]] Mat44Arg inCenterOfMassTransform, [[maybe_unused]] SupportingFace &outVertices) const { /* Nothing */ } - - /// Get the user data of a particular sub shape ID - virtual uint64 GetSubShapeUserData([[maybe_unused]] const SubShapeID &inSubShapeID) const { return mUserData; } - - /// Get the direct child sub shape and its transform for a sub shape ID. - /// @param inSubShapeID Sub shape ID that indicates the path to the leaf shape - /// @param inPositionCOM The position of the center of mass of this shape - /// @param inRotation The orientation of this shape - /// @param inScale Scale of this shape - /// @param outRemainder The remainder of the sub shape ID after removing the sub shape - /// @return Direct child sub shape and its transform, note that the body ID and sub shape ID will be invalid - virtual TransformedShape GetSubShapeTransformedShape(const SubShapeID &inSubShapeID, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale, SubShapeID &outRemainder) const; - - /// Gets the properties needed to do buoyancy calculations for a body using this shape - /// @param inCenterOfMassTransform Transform that takes this shape (centered around center of mass) to world space (or a desired other space) - /// @param inScale Scale in local space of the shape - /// @param inSurface The surface plane of the liquid relative to inCenterOfMassTransform - /// @param outTotalVolume On return this contains the total volume of the shape - /// @param outSubmergedVolume On return this contains the submerged volume of the shape - /// @param outCenterOfBuoyancy On return this contains the world space center of mass of the submerged volume -#ifdef JPH_DEBUG_RENDERER - /// @param inBaseOffset The offset to transform inCenterOfMassTransform to world space (in double precision mode this can be used to shift the whole operation closer to the origin). Only used for debug drawing. -#endif - virtual void GetSubmergedVolume(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, const Plane &inSurface, float &outTotalVolume, float &outSubmergedVolume, Vec3 &outCenterOfBuoyancy -#ifdef JPH_DEBUG_RENDERER // Not using JPH_IF_DEBUG_RENDERER for Doxygen - , RVec3Arg inBaseOffset -#endif - ) const = 0; - -#ifdef JPH_DEBUG_RENDERER - /// Draw the shape at a particular location with a particular color (debugging purposes) - virtual void Draw(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inUseMaterialColors, bool inDrawWireframe) const = 0; - - /// Draw the results of the GetSupportFunction with the convex radius added back on to show any errors introduced by this process (only relevant for convex shapes) - virtual void DrawGetSupportFunction([[maybe_unused]] DebugRenderer *inRenderer, [[maybe_unused]] RMat44Arg inCenterOfMassTransform, [[maybe_unused]] Vec3Arg inScale, [[maybe_unused]] ColorArg inColor, [[maybe_unused]] bool inDrawSupportDirection) const { /* Only implemented for convex shapes */ } - - /// Draw the results of the GetSupportingFace function to show any errors introduced by this process (only relevant for convex shapes) - virtual void DrawGetSupportingFace([[maybe_unused]] DebugRenderer *inRenderer, [[maybe_unused]] RMat44Arg inCenterOfMassTransform, [[maybe_unused]] Vec3Arg inScale) const { /* Only implemented for convex shapes */ } -#endif // JPH_DEBUG_RENDERER - - /// Cast a ray against this shape, returns true if it finds a hit closer than ioHit.mFraction and updates that fraction. Otherwise ioHit is left untouched and the function returns false. - /// Note that the ray should be relative to the center of mass of this shape (i.e. subtract Shape::GetCenterOfMass() from RayCast::mOrigin if you want to cast against the shape in the space it was created). - /// Convex objects will be treated as solid (meaning if the ray starts inside, you'll get a hit fraction of 0) and back face hits against triangles are returned. - /// If you want the surface normal of the hit use GetSurfaceNormal(ioHit.mSubShapeID2, inRay.GetPointOnRay(ioHit.mFraction)). - virtual bool CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const = 0; - - /// Cast a ray against this shape. Allows returning multiple hits through ioCollector. Note that this version is more flexible but also slightly slower than the CastRay function that returns only a single hit. - /// If you want the surface normal of the hit use GetSurfaceNormal(collected sub shape ID, inRay.GetPointOnRay(collected faction)). - virtual void CastRay(const RayCast &inRay, const RayCastSettings &inRayCastSettings, const SubShapeIDCreator &inSubShapeIDCreator, CastRayCollector &ioCollector, const ShapeFilter &inShapeFilter = { }) const = 0; - - /// Check if inPoint is inside this shape. For this tests all shapes are treated as if they were solid. - /// Note that inPoint should be relative to the center of mass of this shape (i.e. subtract Shape::GetCenterOfMass() from inPoint if you want to test against the shape in the space it was created). - /// For a mesh shape, this test will only provide sensible information if the mesh is a closed manifold. - /// For each shape that collides, ioCollector will receive a hit. - virtual void CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter = { }) const = 0; - - /// Collides all vertices of a soft body with this shape and updates SoftBodyVertex::mCollisionPlane, SoftBodyVertex::mCollidingShapeIndex and SoftBodyVertex::mLargestPenetration if a collision with more penetration was found. - /// @param inCenterOfMassTransform Center of mass transform for this shape relative to the vertices. - /// @param inScale The scale to use for this shape - /// @param ioVertices The vertices of the soft body - /// @param inNumVertices The number of vertices in ioVertices - /// @param inDeltaTime Delta time of this time step (can be used to extrapolate the position using the velocity of the particle) - /// @param inDisplacementDueToGravity Displacement due to gravity during this time step - /// @param inCollidingShapeIndex Value to store in SoftBodyVertex::mCollidingShapeIndex when a collision was found - virtual void CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, SoftBodyVertex *ioVertices, uint inNumVertices, float inDeltaTime, Vec3Arg inDisplacementDueToGravity, int inCollidingShapeIndex) const = 0; - - /// Collect the leaf transformed shapes of all leaf shapes of this shape. - /// inBox is the world space axis aligned box which leaf shapes should collide with. - /// inPositionCOM/inRotation/inScale describes the transform of this shape. - /// inSubShapeIDCeator represents the current sub shape ID of this shape. - virtual void CollectTransformedShapes(const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale, const SubShapeIDCreator &inSubShapeIDCreator, TransformedShapeCollector &ioCollector, const ShapeFilter &inShapeFilter) const; - - /// Transforms this shape and all of its children with inTransform, resulting shape(s) are passed to ioCollector. - /// Note that not all shapes support all transforms (especially true for scaling), the resulting shape will try to match the transform as accurately as possible. - /// @param inCenterOfMassTransform The transform (rotation, translation, scale) that the center of mass of the shape should get - /// @param ioCollector The transformed shapes will be passed to this collector - virtual void TransformShape(Mat44Arg inCenterOfMassTransform, TransformedShapeCollector &ioCollector) const; - - /// Scale this shape. Note that not all shapes support all scales, this will return a shape that matches the scale as accurately as possible. See Shape::IsValidScale for more information. - /// @param inScale The scale to use for this shape (note: this scale is applied to the entire shape in the space it was created, most other functions apply the scale in the space of the leaf shapes and from the center of mass!) - ShapeResult ScaleShape(Vec3Arg inScale) const; - - /// An opaque buffer that holds shape specific information during GetTrianglesStart/Next. - struct alignas(16) GetTrianglesContext { uint8 mData[4288]; }; - - /// This is the minimum amount of triangles that should be requested through GetTrianglesNext. - static constexpr int cGetTrianglesMinTrianglesRequested = 32; - - /// To start iterating over triangles, call this function first. - /// ioContext is a temporary buffer and should remain untouched until the last call to GetTrianglesNext. - /// inBox is the world space bounding in which you want to get the triangles. - /// inPositionCOM/inRotation/inScale describes the transform of this shape. - /// To get the actual triangles call GetTrianglesNext. - virtual void GetTrianglesStart(GetTrianglesContext &ioContext, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) const = 0; - - /// Call this repeatedly to get all triangles in the box. - /// outTriangleVertices should be large enough to hold 3 * inMaxTriangleRequested entries. - /// outMaterials (if it is not null) should contain inMaxTrianglesRequested entries. - /// The function returns the amount of triangles that it found (which will be <= inMaxTrianglesRequested), or 0 if there are no more triangles. - /// Note that the function can return a value < inMaxTrianglesRequested and still have more triangles to process (triangles can be returned in blocks). - /// Note that the function may return triangles outside of the requested box, only coarse culling is performed on the returned triangles. - virtual int GetTrianglesNext(GetTrianglesContext &ioContext, int inMaxTrianglesRequested, Float3 *outTriangleVertices, const PhysicsMaterial **outMaterials = nullptr) const = 0; - - ///@name Binary serialization of the shape. Note that this saves the 'cooked' shape in a format which will not be backwards compatible for newer library versions. - /// In this case you need to recreate the shape from the ShapeSettings object and save it again. The user is expected to call SaveBinaryState followed by SaveMaterialState and SaveSubShapeState. - /// The stream should be stored as is and the material and shape list should be saved using the applications own serialization system (e.g. by assigning an ID to each pointer). - /// When restoring data, call sRestoreFromBinaryState to get the shape and then call RestoreMaterialState and RestoreSubShapeState to restore the pointers to the external objects. - /// Alternatively you can use SaveWithChildren and sRestoreWithChildren to save and restore the shape and all its child shapes and materials in a single stream. - ///@{ - - /// Saves the contents of the shape in binary form to inStream. - virtual void SaveBinaryState(StreamOut &inStream) const; - - /// Creates a Shape of the correct type and restores its contents from the binary stream inStream. - static ShapeResult sRestoreFromBinaryState(StreamIn &inStream); - - /// Outputs the material references that this shape has to outMaterials. - virtual void SaveMaterialState([[maybe_unused]] PhysicsMaterialList &outMaterials) const { /* By default do nothing */ } - - /// Restore the material references after calling sRestoreFromBinaryState. Note that the exact same materials need to be provided in the same order as returned by SaveMaterialState. - virtual void RestoreMaterialState([[maybe_unused]] const PhysicsMaterialRefC *inMaterials, uint inNumMaterials) { JPH_ASSERT(inNumMaterials == 0); } - - /// Outputs the shape references that this shape has to outSubShapes. - virtual void SaveSubShapeState([[maybe_unused]] ShapeList &outSubShapes) const { /* By default do nothing */ } - - /// Restore the shape references after calling sRestoreFromBinaryState. Note that the exact same shapes need to be provided in the same order as returned by SaveSubShapeState. - virtual void RestoreSubShapeState([[maybe_unused]] const ShapeRefC *inSubShapes, uint inNumShapes) { JPH_ASSERT(inNumShapes == 0); } - - using ShapeToIDMap = StreamUtils::ObjectToIDMap; - using IDToShapeMap = StreamUtils::IDToObjectMap; - using MaterialToIDMap = StreamUtils::ObjectToIDMap; - using IDToMaterialMap = StreamUtils::IDToObjectMap; - - /// Save this shape, all its children and its materials. Pass in an empty map in ioShapeMap / ioMaterialMap or reuse the same map while saving multiple shapes to the same stream in order to avoid writing duplicates. - void SaveWithChildren(StreamOut &inStream, ShapeToIDMap &ioShapeMap, MaterialToIDMap &ioMaterialMap) const; - - /// Restore a shape, all its children and materials. Pass in an empty map in ioShapeMap / ioMaterialMap or reuse the same map while reading multiple shapes from the same stream in order to restore duplicates. - static ShapeResult sRestoreWithChildren(StreamIn &inStream, IDToShapeMap &ioShapeMap, IDToMaterialMap &ioMaterialMap); - - ///@} - - /// Class that holds information about the shape that can be used for logging / data collection purposes - struct Stats - { - Stats(size_t inSizeBytes, uint inNumTriangles) : mSizeBytes(inSizeBytes), mNumTriangles(inNumTriangles) { } - - size_t mSizeBytes; ///< Amount of memory used by this shape (size in bytes) - uint mNumTriangles; ///< Number of triangles in this shape (when applicable) - }; - - /// Get stats of this shape. Use for logging / data collection purposes only. Does not add values from child shapes, use GetStatsRecursive for this. - virtual Stats GetStats() const = 0; - - using VisitedShapes = UnorderedSet; - - /// Get the combined stats of this shape and its children. - /// @param ioVisitedShapes is used to track which shapes have already been visited, to avoid calculating the wrong memory size. - virtual Stats GetStatsRecursive(VisitedShapes &ioVisitedShapes) const; - - ///< Volume of this shape (m^3). Note that for compound shapes the volume may be incorrect since child shapes can overlap which is not accounted for. - virtual float GetVolume() const = 0; - - /// Test if inScale is a valid scale for this shape. Some shapes can only be scaled uniformly, compound shapes cannot handle shapes - /// being rotated and scaled (this would cause shearing), scale can never be zero. When the scale is invalid, the function will return false. - /// - /// Here's a list of supported scales: - /// * SphereShape: Scale must be uniform (signs of scale are ignored). - /// * BoxShape: Any scale supported (signs of scale are ignored). - /// * TriangleShape: Any scale supported when convex radius is zero, otherwise only uniform scale supported. - /// * CapsuleShape: Scale must be uniform (signs of scale are ignored). - /// * TaperedCapsuleShape: Scale must be uniform (sign of Y scale can be used to flip the capsule). - /// * CylinderShape: Scale must be uniform in XZ plane, Y can scale independently (signs of scale are ignored). - /// * RotatedTranslatedShape: Scale must not cause shear in the child shape. - /// * CompoundShape: Scale must not cause shear in any of the child shapes. - virtual bool IsValidScale(Vec3Arg inScale) const { return !inScale.IsNearZero(); } - -#ifdef JPH_DEBUG_RENDERER - /// Debug helper which draws the intersection between water and the shapes, the center of buoyancy and the submerged volume - static bool sDrawSubmergedVolumes; -#endif // JPH_DEBUG_RENDERER - -protected: - /// This function should not be called directly, it is used by sRestoreFromBinaryState. - virtual void RestoreBinaryState(StreamIn &inStream); - - /// A fallback version of CollidePoint that uses a ray cast and counts the number of hits to determine if the point is inside the shape. Odd number of hits means inside, even number of hits means outside. - static void sCollidePointUsingRayCast(const Shape &inShape, Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter); - -private: - uint64 mUserData = 0; - EShapeType mShapeType; - EShapeSubType mShapeSubType; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/SphereShape.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/SphereShape.cpp deleted file mode 100644 index 28577f7ec67..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/SphereShape.cpp +++ /dev/null @@ -1,352 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef JPH_DEBUG_RENDERER - #include -#endif // JPH_DEBUG_RENDERER - -JPH_NAMESPACE_BEGIN - -JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(SphereShapeSettings) -{ - JPH_ADD_BASE_CLASS(SphereShapeSettings, ConvexShapeSettings) - - JPH_ADD_ATTRIBUTE(SphereShapeSettings, mRadius) -} - -ShapeSettings::ShapeResult SphereShapeSettings::Create() const -{ - if (mCachedResult.IsEmpty()) - Ref shape = new SphereShape(*this, mCachedResult); - return mCachedResult; -} - -SphereShape::SphereShape(const SphereShapeSettings &inSettings, ShapeResult &outResult) : - ConvexShape(EShapeSubType::Sphere, inSettings, outResult), - mRadius(inSettings.mRadius) -{ - if (inSettings.mRadius <= 0.0f) - { - outResult.SetError("Invalid radius"); - return; - } - - outResult.Set(this); -} - -float SphereShape::GetScaledRadius(Vec3Arg inScale) const -{ - JPH_ASSERT(IsValidScale(inScale)); - - Vec3 abs_scale = inScale.Abs(); - return abs_scale.GetX() * mRadius; -} - -AABox SphereShape::GetLocalBounds() const -{ - Vec3 half_extent = Vec3::sReplicate(mRadius); - return AABox(-half_extent, half_extent); -} - -AABox SphereShape::GetWorldSpaceBounds(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale) const -{ - float scaled_radius = GetScaledRadius(inScale); - Vec3 half_extent = Vec3::sReplicate(scaled_radius); - AABox bounds(-half_extent, half_extent); - bounds.Translate(inCenterOfMassTransform.GetTranslation()); - return bounds; -} - -class SphereShape::SphereNoConvex final : public Support -{ -public: - explicit SphereNoConvex(float inRadius) : - mRadius(inRadius) - { - static_assert(sizeof(SphereNoConvex) <= sizeof(SupportBuffer), "Buffer size too small"); - JPH_ASSERT(IsAligned(this, alignof(SphereNoConvex))); - } - - virtual Vec3 GetSupport(Vec3Arg inDirection) const override - { - return Vec3::sZero(); - } - - virtual float GetConvexRadius() const override - { - return mRadius; - } - -private: - float mRadius; -}; - -class SphereShape::SphereWithConvex final : public Support -{ -public: - explicit SphereWithConvex(float inRadius) : - mRadius(inRadius) - { - static_assert(sizeof(SphereWithConvex) <= sizeof(SupportBuffer), "Buffer size too small"); - JPH_ASSERT(IsAligned(this, alignof(SphereWithConvex))); - } - - virtual Vec3 GetSupport(Vec3Arg inDirection) const override - { - float len = inDirection.Length(); - return len > 0.0f? (mRadius / len) * inDirection : Vec3::sZero(); - } - - virtual float GetConvexRadius() const override - { - return 0.0f; - } - -private: - float mRadius; -}; - -const ConvexShape::Support *SphereShape::GetSupportFunction(ESupportMode inMode, SupportBuffer &inBuffer, Vec3Arg inScale) const -{ - float scaled_radius = GetScaledRadius(inScale); - - switch (inMode) - { - case ESupportMode::IncludeConvexRadius: - return new (&inBuffer) SphereWithConvex(scaled_radius); - - case ESupportMode::ExcludeConvexRadius: - case ESupportMode::Default: - return new (&inBuffer) SphereNoConvex(scaled_radius); - } - - JPH_ASSERT(false); - return nullptr; -} - -MassProperties SphereShape::GetMassProperties() const -{ - MassProperties p; - - // Calculate mass - float r2 = mRadius * mRadius; - p.mMass = (4.0f / 3.0f * JPH_PI) * mRadius * r2 * GetDensity(); - - // Calculate inertia - float inertia = (2.0f / 5.0f) * p.mMass * r2; - p.mInertia = Mat44::sScale(inertia); - - return p; -} - -Vec3 SphereShape::GetSurfaceNormal(const SubShapeID &inSubShapeID, Vec3Arg inLocalSurfacePosition) const -{ - JPH_ASSERT(inSubShapeID.IsEmpty(), "Invalid subshape ID"); - - float len = inLocalSurfacePosition.Length(); - return len != 0.0f? inLocalSurfacePosition / len : Vec3::sAxisY(); -} - -void SphereShape::GetSubmergedVolume(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, const Plane &inSurface, float &outTotalVolume, float &outSubmergedVolume, Vec3 &outCenterOfBuoyancy JPH_IF_DEBUG_RENDERER(, RVec3Arg inBaseOffset)) const -{ - float scaled_radius = GetScaledRadius(inScale); - outTotalVolume = (4.0f / 3.0f * JPH_PI) * Cubed(scaled_radius); - - float distance_to_surface = inSurface.SignedDistance(inCenterOfMassTransform.GetTranslation()); - if (distance_to_surface >= scaled_radius) - { - // Above surface - outSubmergedVolume = 0.0f; - outCenterOfBuoyancy = Vec3::sZero(); - } - else if (distance_to_surface <= -scaled_radius) - { - // Under surface - outSubmergedVolume = outTotalVolume; - outCenterOfBuoyancy = inCenterOfMassTransform.GetTranslation(); - } - else - { - // Intersecting surface - - // Calculate submerged volume, see: https://en.wikipedia.org/wiki/Spherical_cap - float h = scaled_radius - distance_to_surface; - outSubmergedVolume = (JPH_PI / 3.0f) * Square(h) * (3.0f * scaled_radius - h); - - // Calculate center of buoyancy, see: http://mathworld.wolfram.com/SphericalCap.html (eq 10) - float z = (3.0f / 4.0f) * Square(2.0f * scaled_radius - h) / (3.0f * scaled_radius - h); - outCenterOfBuoyancy = inCenterOfMassTransform.GetTranslation() - z * inSurface.GetNormal(); // Negative normal since we want the portion under the water - - #ifdef JPH_DEBUG_RENDERER - // Draw intersection between sphere and water plane - if (sDrawSubmergedVolumes) - { - Vec3 circle_center = inCenterOfMassTransform.GetTranslation() - distance_to_surface * inSurface.GetNormal(); - float circle_radius = sqrt(Square(scaled_radius) - Square(distance_to_surface)); - DebugRenderer::sInstance->DrawPie(inBaseOffset + circle_center, circle_radius, inSurface.GetNormal(), inSurface.GetNormal().GetNormalizedPerpendicular(), -JPH_PI, JPH_PI, Color::sGreen, DebugRenderer::ECastShadow::Off); - } - #endif // JPH_DEBUG_RENDERER - } - -#ifdef JPH_DEBUG_RENDERER - // Draw center of buoyancy - if (sDrawSubmergedVolumes) - DebugRenderer::sInstance->DrawWireSphere(inBaseOffset + outCenterOfBuoyancy, 0.05f, Color::sRed, 1); -#endif // JPH_DEBUG_RENDERER -} - -#ifdef JPH_DEBUG_RENDERER -void SphereShape::Draw(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inUseMaterialColors, bool inDrawWireframe) const -{ - DebugRenderer::EDrawMode draw_mode = inDrawWireframe? DebugRenderer::EDrawMode::Wireframe : DebugRenderer::EDrawMode::Solid; - inRenderer->DrawUnitSphere(inCenterOfMassTransform * Mat44::sScale(mRadius * inScale.Abs().GetX()), inUseMaterialColors? GetMaterial()->GetDebugColor() : inColor, DebugRenderer::ECastShadow::On, draw_mode); -} -#endif // JPH_DEBUG_RENDERER - -bool SphereShape::CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const -{ - float fraction = RaySphere(inRay.mOrigin, inRay.mDirection, Vec3::sZero(), mRadius); - if (fraction < ioHit.mFraction) - { - ioHit.mFraction = fraction; - ioHit.mSubShapeID2 = inSubShapeIDCreator.GetID(); - return true; - } - return false; -} - -void SphereShape::CastRay(const RayCast &inRay, const RayCastSettings &inRayCastSettings, const SubShapeIDCreator &inSubShapeIDCreator, CastRayCollector &ioCollector, const ShapeFilter &inShapeFilter) const -{ - // Test shape filter - if (!inShapeFilter.ShouldCollide(this, inSubShapeIDCreator.GetID())) - return; - - float min_fraction, max_fraction; - int num_results = RaySphere(inRay.mOrigin, inRay.mDirection, Vec3::sZero(), mRadius, min_fraction, max_fraction); - if (num_results > 0 // Ray should intersect - && max_fraction >= 0.0f // End of ray should be inside sphere - && min_fraction < ioCollector.GetEarlyOutFraction()) // Start of ray should be before early out fraction - { - // Better hit than the current hit - RayCastResult hit; - hit.mBodyID = TransformedShape::sGetBodyID(ioCollector.GetContext()); - hit.mSubShapeID2 = inSubShapeIDCreator.GetID(); - - // Check front side hit - if (inRayCastSettings.mTreatConvexAsSolid || min_fraction > 0.0f) - { - hit.mFraction = max(0.0f, min_fraction); - ioCollector.AddHit(hit); - } - - // Check back side hit - if (inRayCastSettings.mBackFaceMode == EBackFaceMode::CollideWithBackFaces - && num_results > 1 // Ray should have 2 intersections - && max_fraction < ioCollector.GetEarlyOutFraction()) // End of ray should be before early out fraction - { - hit.mFraction = max_fraction; - ioCollector.AddHit(hit); - } - } -} - -void SphereShape::CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter) const -{ - // Test shape filter - if (!inShapeFilter.ShouldCollide(this, inSubShapeIDCreator.GetID())) - return; - - if (inPoint.LengthSq() <= Square(mRadius)) - ioCollector.AddHit({ TransformedShape::sGetBodyID(ioCollector.GetContext()), inSubShapeIDCreator.GetID() }); -} - -void SphereShape::CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, SoftBodyVertex *ioVertices, uint inNumVertices, [[maybe_unused]] float inDeltaTime, [[maybe_unused]] Vec3Arg inDisplacementDueToGravity, int inCollidingShapeIndex) const -{ - Vec3 center = inCenterOfMassTransform.GetTranslation(); - float radius = GetScaledRadius(inScale); - - for (SoftBodyVertex *v = ioVertices, *sbv_end = ioVertices + inNumVertices; v < sbv_end; ++v) - if (v->mInvMass > 0.0f) - { - // Calculate penetration - Vec3 delta = v->mPosition - center; - float distance = delta.Length(); - float penetration = radius - distance; - if (penetration > v->mLargestPenetration) - { - v->mLargestPenetration = penetration; - - // Calculate contact point and normal - Vec3 normal = distance > 0.0f? delta / distance : Vec3::sAxisY(); - Vec3 point = center + radius * normal; - - // Store collision - v->mCollisionPlane = Plane::sFromPointAndNormal(point, normal); - v->mCollidingShapeIndex = inCollidingShapeIndex; - } - } -} - -void SphereShape::TransformShape(Mat44Arg inCenterOfMassTransform, TransformedShapeCollector &ioCollector) const -{ - Vec3 scale; - Mat44 transform = inCenterOfMassTransform.Decompose(scale); - TransformedShape ts(RVec3(transform.GetTranslation()), transform.GetQuaternion(), this, BodyID(), SubShapeIDCreator()); - ts.SetShapeScale(ScaleHelpers::MakeUniformScale(scale.Abs())); - ioCollector.AddHit(ts); -} - -void SphereShape::GetTrianglesStart(GetTrianglesContext &ioContext, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) const -{ - float scaled_radius = GetScaledRadius(inScale); - new (&ioContext) GetTrianglesContextVertexList(inPositionCOM, inRotation, Vec3::sReplicate(1.0f), Mat44::sScale(scaled_radius), sUnitSphereTriangles.data(), sUnitSphereTriangles.size(), GetMaterial()); -} - -int SphereShape::GetTrianglesNext(GetTrianglesContext &ioContext, int inMaxTrianglesRequested, Float3 *outTriangleVertices, const PhysicsMaterial **outMaterials) const -{ - return ((GetTrianglesContextVertexList &)ioContext).GetTrianglesNext(inMaxTrianglesRequested, outTriangleVertices, outMaterials); -} - -void SphereShape::SaveBinaryState(StreamOut &inStream) const -{ - ConvexShape::SaveBinaryState(inStream); - - inStream.Write(mRadius); -} - -void SphereShape::RestoreBinaryState(StreamIn &inStream) -{ - ConvexShape::RestoreBinaryState(inStream); - - inStream.Read(mRadius); -} - -bool SphereShape::IsValidScale(Vec3Arg inScale) const -{ - return ConvexShape::IsValidScale(inScale) && ScaleHelpers::IsUniformScale(inScale.Abs()); -} - -void SphereShape::sRegister() -{ - ShapeFunctions &f = ShapeFunctions::sGet(EShapeSubType::Sphere); - f.mConstruct = []() -> Shape * { return new SphereShape; }; - f.mColor = Color::sGreen; -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/SphereShape.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/SphereShape.h deleted file mode 100644 index 9d976948a2d..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/SphereShape.h +++ /dev/null @@ -1,125 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -/// Class that constructs a SphereShape -class JPH_EXPORT SphereShapeSettings final : public ConvexShapeSettings -{ -public: - JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, SphereShapeSettings) - - /// Default constructor for deserialization - SphereShapeSettings() = default; - - /// Create a sphere with radius inRadius - SphereShapeSettings(float inRadius, const PhysicsMaterial *inMaterial = nullptr) : ConvexShapeSettings(inMaterial), mRadius(inRadius) { } - - // See: ShapeSettings - virtual ShapeResult Create() const override; - - float mRadius = 0.0f; -}; - -/// A sphere, centered around the origin. -/// Note that it is implemented as a point with convex radius. -class JPH_EXPORT SphereShape final : public ConvexShape -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Constructor - SphereShape() : ConvexShape(EShapeSubType::Sphere) { } - SphereShape(const SphereShapeSettings &inSettings, ShapeResult &outResult); - - /// Create a sphere with radius inRadius - SphereShape(float inRadius, const PhysicsMaterial *inMaterial = nullptr) : ConvexShape(EShapeSubType::Sphere, inMaterial), mRadius(inRadius) { JPH_ASSERT(inRadius > 0.0f); } - - /// Radius of the sphere - float GetRadius() const { return mRadius; } - - // See Shape::GetLocalBounds - virtual AABox GetLocalBounds() const override; - - // See Shape::GetWorldSpaceBounds - virtual AABox GetWorldSpaceBounds(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale) const override; - using Shape::GetWorldSpaceBounds; - - // See Shape::GetInnerRadius - virtual float GetInnerRadius() const override { return mRadius; } - - // See Shape::GetMassProperties - virtual MassProperties GetMassProperties() const override; - - // See Shape::GetSurfaceNormal - virtual Vec3 GetSurfaceNormal(const SubShapeID &inSubShapeID, Vec3Arg inLocalSurfacePosition) const override; - - // See Shape::GetSupportingFace - virtual void GetSupportingFace([[maybe_unused]] const SubShapeID &inSubShapeID, [[maybe_unused]] Vec3Arg inDirection, [[maybe_unused]] Vec3Arg inScale, [[maybe_unused]] Mat44Arg inCenterOfMassTransform, [[maybe_unused]] SupportingFace &outVertices) const override { /* Hit is always a single point, no point in returning anything */ } - - // See ConvexShape::GetSupportFunction - virtual const Support * GetSupportFunction(ESupportMode inMode, SupportBuffer &inBuffer, Vec3Arg inScale) const override; - - // See Shape::GetSubmergedVolume - virtual void GetSubmergedVolume(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, const Plane &inSurface, float &outTotalVolume, float &outSubmergedVolume, Vec3 &outCenterOfBuoyancy JPH_IF_DEBUG_RENDERER(, RVec3Arg inBaseOffset)) const override; - -#ifdef JPH_DEBUG_RENDERER - // See Shape::Draw - virtual void Draw(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inUseMaterialColors, bool inDrawWireframe) const override; -#endif // JPH_DEBUG_RENDERER - - // See Shape::CastRay - virtual bool CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const override; - virtual void CastRay(const RayCast &inRay, const RayCastSettings &inRayCastSettings, const SubShapeIDCreator &inSubShapeIDCreator, CastRayCollector &ioCollector, const ShapeFilter &inShapeFilter = { }) const override; - - // See: Shape::CollidePoint - virtual void CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter = { }) const override; - - // See: Shape::CollideSoftBodyVertices - virtual void CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, SoftBodyVertex *ioVertices, uint inNumVertices, float inDeltaTime, Vec3Arg inDisplacementDueToGravity, int inCollidingShapeIndex) const override; - - // See Shape::TransformShape - virtual void TransformShape(Mat44Arg inCenterOfMassTransform, TransformedShapeCollector &ioCollector) const override; - - // See Shape::GetTrianglesStart - virtual void GetTrianglesStart(GetTrianglesContext &ioContext, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) const override; - - // See Shape::GetTrianglesNext - virtual int GetTrianglesNext(GetTrianglesContext &ioContext, int inMaxTrianglesRequested, Float3 *outTriangleVertices, const PhysicsMaterial **outMaterials = nullptr) const override; - - // See Shape - virtual void SaveBinaryState(StreamOut &inStream) const override; - - // See Shape::GetStats - virtual Stats GetStats() const override { return Stats(sizeof(*this), 0); } - - // See Shape::GetVolume - virtual float GetVolume() const override { return 4.0f / 3.0f * JPH_PI * Cubed(mRadius); } - - // See Shape::IsValidScale - virtual bool IsValidScale(Vec3Arg inScale) const override; - - // Register shape functions with the registry - static void sRegister(); - -protected: - // See: Shape::RestoreBinaryState - virtual void RestoreBinaryState(StreamIn &inStream) override; - -private: - // Get the radius of this sphere scaled by inScale - inline float GetScaledRadius(Vec3Arg inScale) const; - - // Classes for GetSupportFunction - class SphereNoConvex; - class SphereWithConvex; - - float mRadius = 0.0f; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/StaticCompoundShape.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/StaticCompoundShape.cpp deleted file mode 100644 index 01e369bab32..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/StaticCompoundShape.cpp +++ /dev/null @@ -1,675 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(StaticCompoundShapeSettings) -{ - JPH_ADD_BASE_CLASS(StaticCompoundShapeSettings, CompoundShapeSettings) -} - -ShapeSettings::ShapeResult StaticCompoundShapeSettings::Create(TempAllocator &inTempAllocator) const -{ - if (mCachedResult.IsEmpty()) - { - if (mSubShapes.size() == 0) - { - // It's an error to create a compound with no subshapes (the compound cannot encode this) - mCachedResult.SetError("Compound needs a sub shape!"); - } - else if (mSubShapes.size() == 1) - { - // If there's only 1 part we don't need a StaticCompoundShape - const SubShapeSettings &s = mSubShapes[0]; - if (s.mPosition == Vec3::sZero() - && s.mRotation == Quat::sIdentity()) - { - // No rotation or translation, we can use the shape directly - if (s.mShapePtr != nullptr) - mCachedResult.Set(const_cast(s.mShapePtr.GetPtr())); - else if (s.mShape != nullptr) - mCachedResult = s.mShape->Create(); - else - mCachedResult.SetError("Sub shape is null!"); - } - else - { - // We can use a RotatedTranslatedShape instead - RotatedTranslatedShapeSettings settings; - settings.mPosition = s.mPosition; - settings.mRotation = s.mRotation; - settings.mInnerShape = s.mShape; - settings.mInnerShapePtr = s.mShapePtr; - Ref shape = new RotatedTranslatedShape(settings, mCachedResult); - } - } - else - { - // Build a regular compound shape - Ref shape = new StaticCompoundShape(*this, inTempAllocator, mCachedResult); - } - } - return mCachedResult; -} - -ShapeSettings::ShapeResult StaticCompoundShapeSettings::Create() const -{ - TempAllocatorMalloc allocator; - return Create(allocator); -} - -void StaticCompoundShape::Node::SetChildInvalid(uint inIndex) -{ - // Make this an invalid node - mNodeProperties[inIndex] = INVALID_NODE; - - // Make bounding box invalid - mBoundsMinX[inIndex] = HALF_FLT_MAX; - mBoundsMinY[inIndex] = HALF_FLT_MAX; - mBoundsMinZ[inIndex] = HALF_FLT_MAX; - mBoundsMaxX[inIndex] = HALF_FLT_MAX; - mBoundsMaxY[inIndex] = HALF_FLT_MAX; - mBoundsMaxZ[inIndex] = HALF_FLT_MAX; -} - -void StaticCompoundShape::Node::SetChildBounds(uint inIndex, const AABox &inBounds) -{ - mBoundsMinX[inIndex] = HalfFloatConversion::FromFloat(inBounds.mMin.GetX()); - mBoundsMinY[inIndex] = HalfFloatConversion::FromFloat(inBounds.mMin.GetY()); - mBoundsMinZ[inIndex] = HalfFloatConversion::FromFloat(inBounds.mMin.GetZ()); - mBoundsMaxX[inIndex] = HalfFloatConversion::FromFloat(inBounds.mMax.GetX()); - mBoundsMaxY[inIndex] = HalfFloatConversion::FromFloat(inBounds.mMax.GetY()); - mBoundsMaxZ[inIndex] = HalfFloatConversion::FromFloat(inBounds.mMax.GetZ()); -} - -void StaticCompoundShape::sPartition(uint *ioBodyIdx, AABox *ioBounds, int inNumber, int &outMidPoint) -{ - // Handle trivial case - if (inNumber <= 4) - { - outMidPoint = inNumber / 2; - return; - } - - // Calculate bounding box of box centers - Vec3 center_min = Vec3::sReplicate(FLT_MAX); - Vec3 center_max = Vec3::sReplicate(-FLT_MAX); - for (const AABox *b = ioBounds, *b_end = ioBounds + inNumber; b < b_end; ++b) - { - Vec3 center = b->GetCenter(); - center_min = Vec3::sMin(center_min, center); - center_max = Vec3::sMax(center_max, center); - } - - // Calculate split plane - int dimension = (center_max - center_min).GetHighestComponentIndex(); - float split = 0.5f * (center_min + center_max)[dimension]; - - // Divide bodies - int start = 0, end = inNumber; - while (start < end) - { - // Search for first element that is on the right hand side of the split plane - while (start < end && ioBounds[start].GetCenter()[dimension] < split) - ++start; - - // Search for the first element that is on the left hand side of the split plane - while (start < end && ioBounds[end - 1].GetCenter()[dimension] >= split) - --end; - - if (start < end) - { - // Swap the two elements - swap(ioBodyIdx[start], ioBodyIdx[end - 1]); - swap(ioBounds[start], ioBounds[end - 1]); - ++start; - --end; - } - } - JPH_ASSERT(start == end); - - if (start > 0 && start < inNumber) - { - // Success! - outMidPoint = start; - } - else - { - // Failed to divide bodies - outMidPoint = inNumber / 2; - } -} - -void StaticCompoundShape::sPartition4(uint *ioBodyIdx, AABox *ioBounds, int inBegin, int inEnd, int *outSplit) -{ - uint *body_idx = ioBodyIdx + inBegin; - AABox *node_bounds = ioBounds + inBegin; - int number = inEnd - inBegin; - - // Partition entire range - sPartition(body_idx, node_bounds, number, outSplit[2]); - - // Partition lower half - sPartition(body_idx, node_bounds, outSplit[2], outSplit[1]); - - // Partition upper half - sPartition(body_idx + outSplit[2], node_bounds + outSplit[2], number - outSplit[2], outSplit[3]); - - // Convert to proper range - outSplit[0] = inBegin; - outSplit[1] += inBegin; - outSplit[2] += inBegin; - outSplit[3] += outSplit[2]; - outSplit[4] = inEnd; -} - -StaticCompoundShape::StaticCompoundShape(const StaticCompoundShapeSettings &inSettings, TempAllocator &inTempAllocator, ShapeResult &outResult) : - CompoundShape(EShapeSubType::StaticCompound, inSettings, outResult) -{ - // Check that there's at least 1 shape - uint num_subshapes = (uint)inSettings.mSubShapes.size(); - if (num_subshapes < 2) - { - outResult.SetError("Compound needs at least 2 sub shapes, otherwise you should use a RotatedTranslatedShape!"); - return; - } - - // Keep track of total mass to calculate center of mass - float mass = 0.0f; - - mSubShapes.resize(num_subshapes); - for (uint i = 0; i < num_subshapes; ++i) - { - const CompoundShapeSettings::SubShapeSettings &shape = inSettings.mSubShapes[i]; - - // Start constructing the runtime sub shape - SubShape &out_shape = mSubShapes[i]; - if (!out_shape.FromSettings(shape, outResult)) - return; - - // Calculate mass properties of child - MassProperties child = out_shape.mShape->GetMassProperties(); - - // Accumulate center of mass - mass += child.mMass; - mCenterOfMass += out_shape.GetPositionCOM() * child.mMass; - } - - if (mass > 0.0f) - mCenterOfMass /= mass; - - // Cache the inner radius as it can take a while to recursively iterate over all sub shapes - CalculateInnerRadius(); - - // Temporary storage for the bounding boxes of all shapes - uint bounds_size = num_subshapes * sizeof(AABox); - AABox *bounds = (AABox *)inTempAllocator.Allocate(bounds_size); - - // Temporary storage for body indexes (we're shuffling them) - uint body_idx_size = num_subshapes * sizeof(uint); - uint *body_idx = (uint *)inTempAllocator.Allocate(body_idx_size); - - // Shift all shapes so that the center of mass is now at the origin and calculate bounds - for (uint i = 0; i < num_subshapes; ++i) - { - SubShape &shape = mSubShapes[i]; - - // Shift the shape so it's centered around our center of mass - shape.SetPositionCOM(shape.GetPositionCOM() - mCenterOfMass); - - // Transform the shape's bounds into our local space - Mat44 transform = Mat44::sRotationTranslation(shape.GetRotation(), shape.GetPositionCOM()); - AABox shape_bounds = shape.mShape->GetWorldSpaceBounds(transform, Vec3::sReplicate(1.0f)); - - // Store bounds and body index for tree construction - bounds[i] = shape_bounds; - body_idx[i] = i; - - // Update our local bounds - mLocalBounds.Encapsulate(shape_bounds); - } - - // The algorithm is a recursive tree build, but to avoid the call overhead we keep track of a stack here - struct StackEntry - { - uint32 mNodeIdx; // Node index of node that is generated - int mChildIdx; // Index of child that we're currently processing - int mSplit[5]; // Indices where the node ID's have been split to form 4 partitions - AABox mBounds; // Bounding box of this node - }; - uint stack_size = num_subshapes * sizeof(StackEntry); - StackEntry *stack = (StackEntry *)inTempAllocator.Allocate(stack_size); - int top = 0; - - // Reserve enough space so that every sub shape gets its own leaf node - uint next_node_idx = 0; - mNodes.resize(num_subshapes + (num_subshapes + 2) / 3); // = Sum(num_subshapes * 4^-i) with i = [0, Inf]. - - // Create root node - stack[0].mNodeIdx = next_node_idx++; - stack[0].mChildIdx = -1; - stack[0].mBounds = AABox(); - sPartition4(body_idx, bounds, 0, num_subshapes, stack[0].mSplit); - - for (;;) - { - StackEntry &cur_stack = stack[top]; - - // Next child - cur_stack.mChildIdx++; - - // Check if all children processed - if (cur_stack.mChildIdx >= 4) - { - // Terminate if there's nothing left to pop - if (top <= 0) - break; - - // Add our bounds to our parents bounds - StackEntry &prev_stack = stack[top - 1]; - prev_stack.mBounds.Encapsulate(cur_stack.mBounds); - - // Store this node's properties in the parent node - Node &parent_node = mNodes[prev_stack.mNodeIdx]; - parent_node.mNodeProperties[prev_stack.mChildIdx] = cur_stack.mNodeIdx; - parent_node.SetChildBounds(prev_stack.mChildIdx, cur_stack.mBounds); - - // Pop entry from stack - --top; - } - else - { - // Get low and high index to bodies to process - int low = cur_stack.mSplit[cur_stack.mChildIdx]; - int high = cur_stack.mSplit[cur_stack.mChildIdx + 1]; - int num_bodies = high - low; - - if (num_bodies == 0) - { - // Mark invalid - Node &node = mNodes[cur_stack.mNodeIdx]; - node.SetChildInvalid(cur_stack.mChildIdx); - } - else if (num_bodies == 1) - { - // Get body info - uint child_node_idx = body_idx[low]; - const AABox &child_bounds = bounds[low]; - - // Update node - Node &node = mNodes[cur_stack.mNodeIdx]; - node.mNodeProperties[cur_stack.mChildIdx] = child_node_idx | IS_SUBSHAPE; - node.SetChildBounds(cur_stack.mChildIdx, child_bounds); - - // Encapsulate bounding box in parent - cur_stack.mBounds.Encapsulate(child_bounds); - } - else - { - // Allocate new node - StackEntry &new_stack = stack[++top]; - JPH_ASSERT(top < (int)num_subshapes); - new_stack.mNodeIdx = next_node_idx++; - new_stack.mChildIdx = -1; - new_stack.mBounds = AABox(); - sPartition4(body_idx, bounds, low, high, new_stack.mSplit); - } - } - } - - // Resize nodes to actual size - JPH_ASSERT(next_node_idx <= mNodes.size()); - mNodes.resize(next_node_idx); - mNodes.shrink_to_fit(); - - // Free temporary memory - inTempAllocator.Free(stack, stack_size); - inTempAllocator.Free(body_idx, body_idx_size); - inTempAllocator.Free(bounds, bounds_size); - - // Check if we ran out of bits for addressing a node - if (next_node_idx > IS_SUBSHAPE) - { - outResult.SetError("Compound hierarchy has too many nodes"); - return; - } - - // Check if we're not exceeding the amount of sub shape id bits - if (GetSubShapeIDBitsRecursive() > SubShapeID::MaxBits) - { - outResult.SetError("Compound hierarchy is too deep and exceeds the amount of available sub shape ID bits"); - return; - } - - outResult.Set(this); -} - -template -inline void StaticCompoundShape::WalkTree(Visitor &ioVisitor) const -{ - uint32 node_stack[cStackSize]; - node_stack[0] = 0; - int top = 0; - do - { - // Test if the node is valid, the node should rarely be invalid but it is possible when testing - // a really large box against the tree that the invalid nodes will intersect with the box - uint32 node_properties = node_stack[top]; - if (node_properties != INVALID_NODE) - { - // Test if node contains triangles - bool is_node = (node_properties & IS_SUBSHAPE) == 0; - if (is_node) - { - const Node &node = mNodes[node_properties]; - - // Unpack bounds - UVec4 bounds_minxy = UVec4::sLoadInt4(reinterpret_cast(&node.mBoundsMinX[0])); - Vec4 bounds_minx = HalfFloatConversion::ToFloat(bounds_minxy); - Vec4 bounds_miny = HalfFloatConversion::ToFloat(bounds_minxy.Swizzle()); - - UVec4 bounds_minzmaxx = UVec4::sLoadInt4(reinterpret_cast(&node.mBoundsMinZ[0])); - Vec4 bounds_minz = HalfFloatConversion::ToFloat(bounds_minzmaxx); - Vec4 bounds_maxx = HalfFloatConversion::ToFloat(bounds_minzmaxx.Swizzle()); - - UVec4 bounds_maxyz = UVec4::sLoadInt4(reinterpret_cast(&node.mBoundsMaxY[0])); - Vec4 bounds_maxy = HalfFloatConversion::ToFloat(bounds_maxyz); - Vec4 bounds_maxz = HalfFloatConversion::ToFloat(bounds_maxyz.Swizzle()); - - // Load properties for 4 children - UVec4 properties = UVec4::sLoadInt4(&node.mNodeProperties[0]); - - // Check which sub nodes to visit - int num_results = ioVisitor.VisitNodes(bounds_minx, bounds_miny, bounds_minz, bounds_maxx, bounds_maxy, bounds_maxz, properties, top); - - // Push them onto the stack - JPH_ASSERT(top + 4 < cStackSize); - properties.StoreInt4(&node_stack[top]); - top += num_results; - } - else - { - // Points to a sub shape - uint32 sub_shape_idx = node_properties ^ IS_SUBSHAPE; - const SubShape &sub_shape = mSubShapes[sub_shape_idx]; - - ioVisitor.VisitShape(sub_shape, sub_shape_idx); - } - - // Check if we're done - if (ioVisitor.ShouldAbort()) - break; - } - - // Fetch next node until we find one that the visitor wants to see - do - --top; - while (top >= 0 && !ioVisitor.ShouldVisitNode(top)); - } - while (top >= 0); -} - -bool StaticCompoundShape::CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const -{ - JPH_PROFILE_FUNCTION(); - - struct Visitor : public CastRayVisitor - { - using CastRayVisitor::CastRayVisitor; - - JPH_INLINE bool ShouldVisitNode(int inStackTop) const - { - return mDistanceStack[inStackTop] < mHit.mFraction; - } - - JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, int inStackTop) - { - // Test bounds of 4 children - Vec4 distance = TestBounds(inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ); - - // Sort so that highest values are first (we want to first process closer hits and we process stack top to bottom) - return SortReverseAndStore(distance, mHit.mFraction, ioProperties, &mDistanceStack[inStackTop]); - } - - float mDistanceStack[cStackSize]; - }; - - Visitor visitor(inRay, this, inSubShapeIDCreator, ioHit); - WalkTree(visitor); - return visitor.mReturnValue; -} - -void StaticCompoundShape::CastRay(const RayCast &inRay, const RayCastSettings &inRayCastSettings, const SubShapeIDCreator &inSubShapeIDCreator, CastRayCollector &ioCollector, const ShapeFilter &inShapeFilter) const -{ - // Test shape filter - if (!inShapeFilter.ShouldCollide(this, inSubShapeIDCreator.GetID())) - return; - - JPH_PROFILE_FUNCTION(); - - struct Visitor : public CastRayVisitorCollector - { - using CastRayVisitorCollector::CastRayVisitorCollector; - - JPH_INLINE bool ShouldVisitNode(int inStackTop) const - { - return mDistanceStack[inStackTop] < mCollector.GetEarlyOutFraction(); - } - - JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, int inStackTop) - { - // Test bounds of 4 children - Vec4 distance = TestBounds(inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ); - - // Sort so that highest values are first (we want to first process closer hits and we process stack top to bottom) - return SortReverseAndStore(distance, mCollector.GetEarlyOutFraction(), ioProperties, &mDistanceStack[inStackTop]); - } - - float mDistanceStack[cStackSize]; - }; - - Visitor visitor(inRay, inRayCastSettings, this, inSubShapeIDCreator, ioCollector, inShapeFilter); - WalkTree(visitor); -} - -void StaticCompoundShape::CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter) const -{ - JPH_PROFILE_FUNCTION(); - - struct Visitor : public CollidePointVisitor - { - using CollidePointVisitor::CollidePointVisitor; - - JPH_INLINE bool ShouldVisitNode([[maybe_unused]] int inStackTop) const - { - return true; - } - - JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, [[maybe_unused]] int inStackTop) const - { - // Test if point overlaps with box - UVec4 collides = TestBounds(inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ); - return CountAndSortTrues(collides, ioProperties); - } - }; - - Visitor visitor(inPoint, this, inSubShapeIDCreator, ioCollector, inShapeFilter); - WalkTree(visitor); -} - -void StaticCompoundShape::sCastShapeVsCompound(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector) -{ - JPH_PROFILE_FUNCTION(); - - struct Visitor : public CastShapeVisitor - { - using CastShapeVisitor::CastShapeVisitor; - - JPH_INLINE bool ShouldVisitNode(int inStackTop) const - { - return mDistanceStack[inStackTop] < mCollector.GetPositiveEarlyOutFraction(); - } - - JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, int inStackTop) - { - // Test bounds of 4 children - Vec4 distance = TestBounds(inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ); - - // Sort so that highest values are first (we want to first process closer hits and we process stack top to bottom) - return SortReverseAndStore(distance, mCollector.GetPositiveEarlyOutFraction(), ioProperties, &mDistanceStack[inStackTop]); - } - - float mDistanceStack[cStackSize]; - }; - - JPH_ASSERT(inShape->GetSubType() == EShapeSubType::StaticCompound); - const StaticCompoundShape *shape = static_cast(inShape); - - Visitor visitor(inShapeCast, inShapeCastSettings, shape, inScale, inShapeFilter, inCenterOfMassTransform2, inSubShapeIDCreator1, inSubShapeIDCreator2, ioCollector); - shape->WalkTree(visitor); -} - -void StaticCompoundShape::CollectTransformedShapes(const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale, const SubShapeIDCreator &inSubShapeIDCreator, TransformedShapeCollector &ioCollector, const ShapeFilter &inShapeFilter) const -{ - JPH_PROFILE_FUNCTION(); - - // Test shape filter - if (!inShapeFilter.ShouldCollide(this, inSubShapeIDCreator.GetID())) - return; - - struct Visitor : public CollectTransformedShapesVisitor - { - using CollectTransformedShapesVisitor::CollectTransformedShapesVisitor; - - JPH_INLINE bool ShouldVisitNode([[maybe_unused]] int inStackTop) const - { - return true; - } - - JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, [[maybe_unused]] int inStackTop) const - { - // Test which nodes collide - UVec4 collides = TestBounds(inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ); - return CountAndSortTrues(collides, ioProperties); - } - }; - - Visitor visitor(inBox, this, inPositionCOM, inRotation, inScale, inSubShapeIDCreator, ioCollector, inShapeFilter); - WalkTree(visitor); -} - -int StaticCompoundShape::GetIntersectingSubShapes(const AABox &inBox, uint *outSubShapeIndices, int inMaxSubShapeIndices) const -{ - JPH_PROFILE_FUNCTION(); - - GetIntersectingSubShapesVisitorSC visitor(inBox, outSubShapeIndices, inMaxSubShapeIndices); - WalkTree(visitor); - return visitor.GetNumResults(); -} - -int StaticCompoundShape::GetIntersectingSubShapes(const OrientedBox &inBox, uint *outSubShapeIndices, int inMaxSubShapeIndices) const -{ - JPH_PROFILE_FUNCTION(); - - GetIntersectingSubShapesVisitorSC visitor(inBox, outSubShapeIndices, inMaxSubShapeIndices); - WalkTree(visitor); - return visitor.GetNumResults(); -} - -void StaticCompoundShape::sCollideCompoundVsShape(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter) -{ - JPH_PROFILE_FUNCTION(); - - JPH_ASSERT(inShape1->GetSubType() == EShapeSubType::StaticCompound); - const StaticCompoundShape *shape1 = static_cast(inShape1); - - struct Visitor : public CollideCompoundVsShapeVisitor - { - using CollideCompoundVsShapeVisitor::CollideCompoundVsShapeVisitor; - - JPH_INLINE bool ShouldVisitNode([[maybe_unused]] int inStackTop) const - { - return true; - } - - JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, [[maybe_unused]] int inStackTop) const - { - // Test which nodes collide - UVec4 collides = TestBounds(inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ); - return CountAndSortTrues(collides, ioProperties); - } - }; - - Visitor visitor(shape1, inShape2, inScale1, inScale2, inCenterOfMassTransform1, inCenterOfMassTransform2, inSubShapeIDCreator1, inSubShapeIDCreator2, inCollideShapeSettings, ioCollector, inShapeFilter); - shape1->WalkTree(visitor); -} - -void StaticCompoundShape::sCollideShapeVsCompound(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter) -{ - JPH_PROFILE_FUNCTION(); - - struct Visitor : public CollideShapeVsCompoundVisitor - { - using CollideShapeVsCompoundVisitor::CollideShapeVsCompoundVisitor; - - JPH_INLINE bool ShouldVisitNode([[maybe_unused]] int inStackTop) const - { - return true; - } - - JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, [[maybe_unused]] int inStackTop) const - { - // Test which nodes collide - UVec4 collides = TestBounds(inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ); - return CountAndSortTrues(collides, ioProperties); - } - }; - - JPH_ASSERT(inShape2->GetSubType() == EShapeSubType::StaticCompound); - const StaticCompoundShape *shape2 = static_cast(inShape2); - - Visitor visitor(inShape1, shape2, inScale1, inScale2, inCenterOfMassTransform1, inCenterOfMassTransform2, inSubShapeIDCreator1, inSubShapeIDCreator2, inCollideShapeSettings, ioCollector, inShapeFilter); - shape2->WalkTree(visitor); -} - -void StaticCompoundShape::SaveBinaryState(StreamOut &inStream) const -{ - CompoundShape::SaveBinaryState(inStream); - - inStream.Write(mNodes); -} - -void StaticCompoundShape::RestoreBinaryState(StreamIn &inStream) -{ - CompoundShape::RestoreBinaryState(inStream); - - inStream.Read(mNodes); -} - -void StaticCompoundShape::sRegister() -{ - ShapeFunctions &f = ShapeFunctions::sGet(EShapeSubType::StaticCompound); - f.mConstruct = []() -> Shape * { return new StaticCompoundShape; }; - f.mColor = Color::sOrange; - - for (EShapeSubType s : sAllSubShapeTypes) - { - CollisionDispatch::sRegisterCollideShape(EShapeSubType::StaticCompound, s, sCollideCompoundVsShape); - CollisionDispatch::sRegisterCollideShape(s, EShapeSubType::StaticCompound, sCollideShapeVsCompound); - CollisionDispatch::sRegisterCastShape(s, EShapeSubType::StaticCompound, sCastShapeVsCompound); - } -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/StaticCompoundShape.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/StaticCompoundShape.h deleted file mode 100644 index c3b7b7e76e2..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/StaticCompoundShape.h +++ /dev/null @@ -1,139 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -class CollideShapeSettings; -class TempAllocator; - -/// Class that constructs a StaticCompoundShape. Note that if you only want a compound of 1 shape, use a RotatedTranslatedShape instead. -class JPH_EXPORT StaticCompoundShapeSettings final : public CompoundShapeSettings -{ -public: - JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, StaticCompoundShapeSettings) - - // See: ShapeSettings - virtual ShapeResult Create() const override; - - /// Specialization of Create() function that allows specifying a temp allocator to avoid temporary memory allocations on the heap - ShapeResult Create(TempAllocator &inTempAllocator) const; -}; - -/// A compound shape, sub shapes can be rotated and translated. -/// Sub shapes cannot be modified once the shape is constructed. -/// Shifts all child objects so that they're centered around the center of mass. -class JPH_EXPORT StaticCompoundShape final : public CompoundShape -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Constructor - StaticCompoundShape() : CompoundShape(EShapeSubType::StaticCompound) { } - StaticCompoundShape(const StaticCompoundShapeSettings &inSettings, TempAllocator &inTempAllocator, ShapeResult &outResult); - - // See Shape::CastRay - virtual bool CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const override; - virtual void CastRay(const RayCast &inRay, const RayCastSettings &inRayCastSettings, const SubShapeIDCreator &inSubShapeIDCreator, CastRayCollector &ioCollector, const ShapeFilter &inShapeFilter = { }) const override; - - // See: Shape::CollidePoint - virtual void CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter = { }) const override; - - // See Shape::CollectTransformedShapes - virtual void CollectTransformedShapes(const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale, const SubShapeIDCreator &inSubShapeIDCreator, TransformedShapeCollector &ioCollector, const ShapeFilter &inShapeFilter) const override; - - // See: CompoundShape::GetIntersectingSubShapes - virtual int GetIntersectingSubShapes(const AABox &inBox, uint *outSubShapeIndices, int inMaxSubShapeIndices) const override; - - // See: CompoundShape::GetIntersectingSubShapes - virtual int GetIntersectingSubShapes(const OrientedBox &inBox, uint *outSubShapeIndices, int inMaxSubShapeIndices) const override; - - // See Shape - virtual void SaveBinaryState(StreamOut &inStream) const override; - - // See Shape::GetStats - virtual Stats GetStats() const override { return Stats(sizeof(*this) + mSubShapes.size() * sizeof(SubShape) + mNodes.size() * sizeof(Node), 0); } - - // Register shape functions with the registry - static void sRegister(); - -protected: - // See: Shape::RestoreBinaryState - virtual void RestoreBinaryState(StreamIn &inStream) override; - -private: - // Visitor for GetIntersectingSubShapes - template - struct GetIntersectingSubShapesVisitorSC : public GetIntersectingSubShapesVisitor - { - using GetIntersectingSubShapesVisitor::GetIntersectingSubShapesVisitor; - - JPH_INLINE bool ShouldVisitNode(int inStackTop) const - { - return true; - } - - JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, int inStackTop) - { - // Test if point overlaps with box - UVec4 collides = GetIntersectingSubShapesVisitor::TestBounds(inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ); - return CountAndSortTrues(collides, ioProperties); - } - }; - - /// Sorts ioBodyIdx spatially into 2 groups. Second groups starts at ioBodyIdx + outMidPoint. - /// After the function returns ioBodyIdx and ioBounds will be shuffled - static void sPartition(uint *ioBodyIdx, AABox *ioBounds, int inNumber, int &outMidPoint); - - /// Sorts ioBodyIdx from inBegin to (but excluding) inEnd spatially into 4 groups. - /// outSplit needs to be 5 ints long, when the function returns each group runs from outSplit[i] to (but excluding) outSplit[i + 1] - /// After the function returns ioBodyIdx and ioBounds will be shuffled - static void sPartition4(uint *ioBodyIdx, AABox *ioBounds, int inBegin, int inEnd, int *outSplit); - - // Helper functions called by CollisionDispatch - static void sCollideCompoundVsShape(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter); - static void sCollideShapeVsCompound(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter); - static void sCastShapeVsCompound(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector); - - // Maximum size of the stack during tree walk - static constexpr int cStackSize = 128; - - template - JPH_INLINE void WalkTree(Visitor &ioVisitor) const; ///< Walk the node tree calling the Visitor::VisitNodes for each node encountered and Visitor::VisitShape for each sub shape encountered - - /// Bits used in Node::mNodeProperties - enum : uint32 - { - IS_SUBSHAPE = 0x80000000, ///< If this bit is set, the other bits index in mSubShape, otherwise in mNodes - INVALID_NODE = 0x7fffffff, ///< Signifies an invalid node - }; - - /// Node structure - struct Node - { - void SetChildBounds(uint inIndex, const AABox &inBounds); ///< Set bounding box for child inIndex to inBounds - void SetChildInvalid(uint inIndex); ///< Mark the child inIndex as invalid and set its bounding box to invalid - - HalfFloat mBoundsMinX[4]; ///< 4 child bounding boxes - HalfFloat mBoundsMinY[4]; - HalfFloat mBoundsMinZ[4]; - HalfFloat mBoundsMaxX[4]; - HalfFloat mBoundsMaxY[4]; - HalfFloat mBoundsMaxZ[4]; - uint32 mNodeProperties[4]; ///< 4 child node properties - }; - - static_assert(sizeof(Node) == 64, "Node should be 64 bytes"); - - using Nodes = Array; - - Nodes mNodes; ///< Quad tree node structure -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/SubShapeID.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/SubShapeID.h deleted file mode 100644 index f1e8d3713bc..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/SubShapeID.h +++ /dev/null @@ -1,138 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -JPH_NAMESPACE_BEGIN - -/// @brief A sub shape id contains a path to an element (usually a triangle or other primitive type) of a compound shape -/// -/// Each sub shape knows how many bits it needs to encode its ID, so knows how many bits to take from the sub shape ID. -/// -/// For example: -/// * We have a CompoundShape A with 5 child shapes (identify sub shape using 3 bits AAA) -/// * One of its child shapes is CompoundShape B which has 3 child shapes (identify sub shape using 2 bits BB) -/// * One of its child shapes is MeshShape C which contains enough triangles to need 7 bits to identify a triangle (identify sub shape using 7 bits CCCCCCC, note that MeshShape is block based and sorts triangles spatially, you can't assume that the first triangle will have bit pattern 0000000). -/// -/// The bit pattern of the sub shape ID to identify a triangle in MeshShape C will then be CCCCCCCBBAAA. -/// -/// A sub shape ID will become invalid when the structure of the shape changes. For example, if a child shape is removed from a compound shape, the sub shape ID will no longer be valid. -/// This can be a problem when caching sub shape IDs from one frame to the next. See comments at ContactListener::OnContactPersisted / OnContactRemoved. -class SubShapeID -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Underlying storage type - using Type = uint32; - - /// Type that is bigger than the underlying storage type for operations that would otherwise overflow - using BiggerType = uint64; - - static_assert(sizeof(BiggerType) > sizeof(Type), "The calculation below assumes BiggerType is a bigger type than Type"); - - /// How many bits we can store in this ID - static constexpr uint MaxBits = 8 * sizeof(Type); - - /// Constructor - SubShapeID() = default; - - /// Get the next id in the chain of ids (pops parents before children) - Type PopID(uint inBits, SubShapeID &outRemainder) const - { - Type mask_bits = Type((BiggerType(1) << inBits) - 1); - Type fill_bits = Type(BiggerType(cEmpty) << (MaxBits - inBits)); // Fill left side bits with 1 so that if there's no remainder all bits will be set, note that we do this using a BiggerType since on intel 0xffffffff << 32 == 0xffffffff - Type v = mValue & mask_bits; - outRemainder = SubShapeID(Type(BiggerType(mValue) >> inBits) | fill_bits); - return v; - } - - /// Get the value of the path to the sub shape ID - inline Type GetValue() const - { - return mValue; - } - - /// Set the value of the sub shape ID (use with care!) - inline void SetValue(Type inValue) - { - mValue = inValue; - } - - /// Check if there is any bits of subshape ID left. - /// Note that this is not a 100% guarantee as the subshape ID could consist of all 1 bits. Use for asserts only. - inline bool IsEmpty() const - { - return mValue == cEmpty; - } - - /// Check equal - inline bool operator == (const SubShapeID &inRHS) const - { - return mValue == inRHS.mValue; - } - - /// Check not-equal - inline bool operator != (const SubShapeID &inRHS) const - { - return mValue != inRHS.mValue; - } - -private: - friend class SubShapeIDCreator; - - /// An empty SubShapeID has all bits set - static constexpr Type cEmpty = ~Type(0); - - /// Constructor - explicit SubShapeID(const Type &inValue) : mValue(inValue) { } - - /// Adds an id at a particular position in the chain - /// (this should really only be called by the SubShapeIDCreator) - void PushID(Type inValue, uint inFirstBit, uint inBits) - { - // First clear the bits - mValue &= ~(Type((BiggerType(1) << inBits) - 1) << inFirstBit); - - // Then set them to the new value - mValue |= inValue << inFirstBit; - } - - Type mValue = cEmpty; -}; - -/// A sub shape id creator can be used to create a new sub shape id by recursing through the shape -/// hierarchy and pushing new ID's onto the chain -class SubShapeIDCreator -{ -public: - /// Add a new id to the chain of id's and return it - SubShapeIDCreator PushID(uint inValue, uint inBits) const - { - JPH_ASSERT(inValue < (SubShapeID::BiggerType(1) << inBits)); - SubShapeIDCreator copy = *this; - copy.mID.PushID(inValue, mCurrentBit, inBits); - copy.mCurrentBit += inBits; - JPH_ASSERT(copy.mCurrentBit <= SubShapeID::MaxBits); - return copy; - } - - // Get the resulting sub shape ID - const SubShapeID & GetID() const - { - return mID; - } - - /// Get the number of bits that have been written to the sub shape ID so far - inline uint GetNumBitsWritten() const - { - return mCurrentBit; - } - -private: - SubShapeID mID; - uint mCurrentBit = 0; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/SubShapeIDPair.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/SubShapeIDPair.h deleted file mode 100644 index ccad840b6ad..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/SubShapeIDPair.h +++ /dev/null @@ -1,80 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -/// A pair of bodies and their sub shape ID's. Can be used as a key in a map to find a contact point. -class SubShapeIDPair -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Constructor - SubShapeIDPair() = default; - SubShapeIDPair(const BodyID &inBody1ID, const SubShapeID &inSubShapeID1, const BodyID &inBody2ID, const SubShapeID &inSubShapeID2) : mBody1ID(inBody1ID), mSubShapeID1(inSubShapeID1), mBody2ID(inBody2ID), mSubShapeID2(inSubShapeID2) { } - SubShapeIDPair & operator = (const SubShapeIDPair &) = default; - SubShapeIDPair(const SubShapeIDPair &) = default; - - /// Equality operator - inline bool operator == (const SubShapeIDPair &inRHS) const - { - return UVec4::sLoadInt4(reinterpret_cast(this)) == UVec4::sLoadInt4(reinterpret_cast(&inRHS)); - } - - /// Less than operator, used to consistently order contact points for a deterministic simulation - inline bool operator < (const SubShapeIDPair &inRHS) const - { - if (mBody1ID != inRHS.mBody1ID) - return mBody1ID < inRHS.mBody1ID; - - if (mSubShapeID1.GetValue() != inRHS.mSubShapeID1.GetValue()) - return mSubShapeID1.GetValue() < inRHS.mSubShapeID1.GetValue(); - - if (mBody2ID != inRHS.mBody2ID) - return mBody2ID < inRHS.mBody2ID; - - return mSubShapeID2.GetValue() < inRHS.mSubShapeID2.GetValue(); - } - - const BodyID & GetBody1ID() const { return mBody1ID; } - const SubShapeID & GetSubShapeID1() const { return mSubShapeID1; } - const BodyID & GetBody2ID() const { return mBody2ID; } - const SubShapeID & GetSubShapeID2() const { return mSubShapeID2; } - - uint64 GetHash() const { return HashBytes(this, sizeof(SubShapeIDPair)); } - -private: - BodyID mBody1ID; - SubShapeID mSubShapeID1; - BodyID mBody2ID; - SubShapeID mSubShapeID2; -}; - -static_assert(sizeof(SubShapeIDPair) == 16, "Unexpected size"); -static_assert(alignof(SubShapeIDPair) == 4, "Assuming 4 byte aligned"); - -JPH_NAMESPACE_END - -JPH_SUPPRESS_WARNINGS_STD_BEGIN - -namespace std -{ - /// Declare std::hash for SubShapeIDPair, note that std::hash is platform dependent and we need this one to be consistent because we sort on it in the ContactConstraintManager - template <> - struct hash - { - inline size_t operator () (const JPH::SubShapeIDPair &inRHS) const - { - return static_cast(inRHS.GetHash()); - } - }; -} - -JPH_SUPPRESS_WARNINGS_STD_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/TaperedCapsuleShape.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/TaperedCapsuleShape.cpp deleted file mode 100644 index 98386418520..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/TaperedCapsuleShape.cpp +++ /dev/null @@ -1,458 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef JPH_DEBUG_RENDERER - #include -#endif // JPH_DEBUG_RENDERER - -JPH_NAMESPACE_BEGIN - -JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(TaperedCapsuleShapeSettings) -{ - JPH_ADD_BASE_CLASS(TaperedCapsuleShapeSettings, ConvexShapeSettings) - - JPH_ADD_ATTRIBUTE(TaperedCapsuleShapeSettings, mHalfHeightOfTaperedCylinder) - JPH_ADD_ATTRIBUTE(TaperedCapsuleShapeSettings, mTopRadius) - JPH_ADD_ATTRIBUTE(TaperedCapsuleShapeSettings, mBottomRadius) -} - -bool TaperedCapsuleShapeSettings::IsSphere() const -{ - return max(mTopRadius, mBottomRadius) >= 2.0f * mHalfHeightOfTaperedCylinder + min(mTopRadius, mBottomRadius); -} - -ShapeSettings::ShapeResult TaperedCapsuleShapeSettings::Create() const -{ - if (mCachedResult.IsEmpty()) - { - Ref shape; - if (IsValid() && IsSphere()) - { - // Determine sphere center and radius - float radius, center; - if (mTopRadius > mBottomRadius) - { - radius = mTopRadius; - center = mHalfHeightOfTaperedCylinder; - } - else - { - radius = mBottomRadius; - center = -mHalfHeightOfTaperedCylinder; - } - - // Create sphere - shape = new SphereShape(radius, mMaterial); - - // Offset sphere if needed - if (abs(center) > 1.0e-6f) - { - RotatedTranslatedShapeSettings rot_trans(Vec3(0, center, 0), Quat::sIdentity(), shape); - mCachedResult = rot_trans.Create(); - } - else - mCachedResult.Set(shape); - } - else - { - // Normal tapered capsule shape - shape = new TaperedCapsuleShape(*this, mCachedResult); - } - } - return mCachedResult; -} - -TaperedCapsuleShapeSettings::TaperedCapsuleShapeSettings(float inHalfHeightOfTaperedCylinder, float inTopRadius, float inBottomRadius, const PhysicsMaterial *inMaterial) : - ConvexShapeSettings(inMaterial), - mHalfHeightOfTaperedCylinder(inHalfHeightOfTaperedCylinder), - mTopRadius(inTopRadius), - mBottomRadius(inBottomRadius) -{ -} - -TaperedCapsuleShape::TaperedCapsuleShape(const TaperedCapsuleShapeSettings &inSettings, ShapeResult &outResult) : - ConvexShape(EShapeSubType::TaperedCapsule, inSettings, outResult), - mTopRadius(inSettings.mTopRadius), - mBottomRadius(inSettings.mBottomRadius) -{ - if (mTopRadius <= 0.0f) - { - outResult.SetError("Invalid top radius"); - return; - } - - if (mBottomRadius <= 0.0f) - { - outResult.SetError("Invalid bottom radius"); - return; - } - - if (inSettings.mHalfHeightOfTaperedCylinder <= 0.0f) - { - outResult.SetError("Invalid height"); - return; - } - - // If this goes off one of the sphere ends falls totally inside the other and you should use a sphere instead - if (inSettings.IsSphere()) - { - outResult.SetError("One sphere embedded in other sphere, please use sphere shape instead"); - return; - } - - // Approximation: The center of mass is exactly half way between the top and bottom cap of the tapered capsule - mTopCenter = inSettings.mHalfHeightOfTaperedCylinder + 0.5f * (mBottomRadius - mTopRadius); - mBottomCenter = -inSettings.mHalfHeightOfTaperedCylinder + 0.5f * (mBottomRadius - mTopRadius); - - // Calculate center of mass - mCenterOfMass = Vec3(0, inSettings.mHalfHeightOfTaperedCylinder - mTopCenter, 0); - - // Calculate convex radius - mConvexRadius = min(mTopRadius, mBottomRadius); - JPH_ASSERT(mConvexRadius > 0.0f); - - // Calculate the sin and tan of the angle that the cone surface makes with the Y axis - // See: TaperedCapsuleShape.gliffy - mSinAlpha = (mBottomRadius - mTopRadius) / (mTopCenter - mBottomCenter); - JPH_ASSERT(mSinAlpha >= -1.0f && mSinAlpha <= 1.0f); - mTanAlpha = Tan(ASin(mSinAlpha)); - - outResult.Set(this); -} - -class TaperedCapsuleShape::TaperedCapsule final : public Support -{ -public: - TaperedCapsule(Vec3Arg inTopCenter, Vec3Arg inBottomCenter, float inTopRadius, float inBottomRadius, float inConvexRadius) : - mTopCenter(inTopCenter), - mBottomCenter(inBottomCenter), - mTopRadius(inTopRadius), - mBottomRadius(inBottomRadius), - mConvexRadius(inConvexRadius) - { - static_assert(sizeof(TaperedCapsule) <= sizeof(SupportBuffer), "Buffer size too small"); - JPH_ASSERT(IsAligned(this, alignof(TaperedCapsule))); - } - - virtual Vec3 GetSupport(Vec3Arg inDirection) const override - { - // Check zero vector - float len = inDirection.Length(); - if (len == 0.0f) - return mTopCenter + Vec3(0, mTopRadius, 0); // Return top - - // Check if the support of the top sphere or bottom sphere is bigger - Vec3 support_top = mTopCenter + (mTopRadius / len) * inDirection; - Vec3 support_bottom = mBottomCenter + (mBottomRadius / len) * inDirection; - if (support_top.Dot(inDirection) > support_bottom.Dot(inDirection)) - return support_top; - else - return support_bottom; - } - - virtual float GetConvexRadius() const override - { - return mConvexRadius; - } - -private: - Vec3 mTopCenter; - Vec3 mBottomCenter; - float mTopRadius; - float mBottomRadius; - float mConvexRadius; -}; - -const ConvexShape::Support *TaperedCapsuleShape::GetSupportFunction(ESupportMode inMode, SupportBuffer &inBuffer, Vec3Arg inScale) const -{ - JPH_ASSERT(IsValidScale(inScale)); - - // Get scaled tapered capsule - Vec3 abs_scale = inScale.Abs(); - float scale_xz = abs_scale.GetX(); - float scale_y = inScale.GetY(); // The sign of y is important as it flips the tapered capsule - Vec3 scaled_top_center = Vec3(0, scale_y * mTopCenter, 0); - Vec3 scaled_bottom_center = Vec3(0, scale_y * mBottomCenter, 0); - float scaled_top_radius = scale_xz * mTopRadius; - float scaled_bottom_radius = scale_xz * mBottomRadius; - float scaled_convex_radius = scale_xz * mConvexRadius; - - switch (inMode) - { - case ESupportMode::IncludeConvexRadius: - return new (&inBuffer) TaperedCapsule(scaled_top_center, scaled_bottom_center, scaled_top_radius, scaled_bottom_radius, 0.0f); - - case ESupportMode::ExcludeConvexRadius: - case ESupportMode::Default: - { - // Get radii reduced by convex radius - float tr = scaled_top_radius - scaled_convex_radius; - float br = scaled_bottom_radius - scaled_convex_radius; - JPH_ASSERT(tr >= 0.0f && br >= 0.0f); - JPH_ASSERT(tr == 0.0f || br == 0.0f, "Convex radius should be that of the smallest sphere"); - return new (&inBuffer) TaperedCapsule(scaled_top_center, scaled_bottom_center, tr, br, scaled_convex_radius); - } - } - - JPH_ASSERT(false); - return nullptr; -} - -void TaperedCapsuleShape::GetSupportingFace(const SubShapeID &inSubShapeID, Vec3Arg inDirection, Vec3Arg inScale, Mat44Arg inCenterOfMassTransform, SupportingFace &outVertices) const -{ - JPH_ASSERT(inSubShapeID.IsEmpty(), "Invalid subshape ID"); - JPH_ASSERT(IsValidScale(inScale)); - - // Check zero vector - float len = inDirection.Length(); - if (len == 0.0f) - return; - - // Get scaled tapered capsule - Vec3 abs_scale = inScale.Abs(); - float scale_xz = abs_scale.GetX(); - float scale_y = inScale.GetY(); // The sign of y is important as it flips the tapered capsule - Vec3 scaled_top_center = Vec3(0, scale_y * mTopCenter, 0); - Vec3 scaled_bottom_center = Vec3(0, scale_y * mBottomCenter, 0); - float scaled_top_radius = scale_xz * mTopRadius; - float scaled_bottom_radius = scale_xz * mBottomRadius; - - // Get support point for top and bottom sphere in the opposite of inDirection (including convex radius) - Vec3 support_top = scaled_top_center - (scaled_top_radius / len) * inDirection; - Vec3 support_bottom = scaled_bottom_center - (scaled_bottom_radius / len) * inDirection; - - // Get projection on inDirection - float proj_top = support_top.Dot(inDirection); - float proj_bottom = support_bottom.Dot(inDirection); - - // If projection is roughly equal then return line, otherwise we return nothing as there's only 1 point - if (abs(proj_top - proj_bottom) < cCapsuleProjectionSlop * len) - { - outVertices.push_back(inCenterOfMassTransform * support_top); - outVertices.push_back(inCenterOfMassTransform * support_bottom); - } -} - -MassProperties TaperedCapsuleShape::GetMassProperties() const -{ - AABox box = GetInertiaApproximation(); - - MassProperties p; - p.SetMassAndInertiaOfSolidBox(box.GetSize(), GetDensity()); - return p; -} - -Vec3 TaperedCapsuleShape::GetSurfaceNormal(const SubShapeID &inSubShapeID, Vec3Arg inLocalSurfacePosition) const -{ - JPH_ASSERT(inSubShapeID.IsEmpty(), "Invalid subshape ID"); - - // See: TaperedCapsuleShape.gliffy - // We need to calculate ty and by in order to see if the position is on the top or bottom sphere - // sin(alpha) = by / br = ty / tr - // => by = sin(alpha) * br, ty = sin(alpha) * tr - - if (inLocalSurfacePosition.GetY() > mTopCenter + mSinAlpha * mTopRadius) - return (inLocalSurfacePosition - Vec3(0, mTopCenter, 0)).Normalized(); - else if (inLocalSurfacePosition.GetY() < mBottomCenter + mSinAlpha * mBottomRadius) - return (inLocalSurfacePosition - Vec3(0, mBottomCenter, 0)).Normalized(); - else - { - // Get perpendicular vector to the surface in the xz plane - Vec3 perpendicular = Vec3(inLocalSurfacePosition.GetX(), 0, inLocalSurfacePosition.GetZ()).NormalizedOr(Vec3::sAxisX()); - - // We know that the perpendicular has length 1 and that it needs a y component where tan(alpha) = y / 1 in order to align it to the surface - perpendicular.SetY(mTanAlpha); - return perpendicular.Normalized(); - } -} - -AABox TaperedCapsuleShape::GetLocalBounds() const -{ - float max_radius = max(mTopRadius, mBottomRadius); - return AABox(Vec3(-max_radius, mBottomCenter - mBottomRadius, -max_radius), Vec3(max_radius, mTopCenter + mTopRadius, max_radius)); -} - -AABox TaperedCapsuleShape::GetWorldSpaceBounds(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale) const -{ - JPH_ASSERT(IsValidScale(inScale)); - - Vec3 abs_scale = inScale.Abs(); - float scale_xz = abs_scale.GetX(); - float scale_y = inScale.GetY(); // The sign of y is important as it flips the tapered capsule - Vec3 bottom_extent = Vec3::sReplicate(scale_xz * mBottomRadius); - Vec3 bottom_center = inCenterOfMassTransform * Vec3(0, scale_y * mBottomCenter, 0); - Vec3 top_extent = Vec3::sReplicate(scale_xz * mTopRadius); - Vec3 top_center = inCenterOfMassTransform * Vec3(0, scale_y * mTopCenter, 0); - Vec3 p1 = Vec3::sMin(top_center - top_extent, bottom_center - bottom_extent); - Vec3 p2 = Vec3::sMax(top_center + top_extent, bottom_center + bottom_extent); - return AABox(p1, p2); -} - -void TaperedCapsuleShape::CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, SoftBodyVertex *ioVertices, uint inNumVertices, [[maybe_unused]] float inDeltaTime, [[maybe_unused]] Vec3Arg inDisplacementDueToGravity, int inCollidingShapeIndex) const -{ - JPH_ASSERT(IsValidScale(inScale)); - - Mat44 inverse_transform = inCenterOfMassTransform.InversedRotationTranslation(); - - // Get scaled tapered capsule - Vec3 abs_scale = inScale.Abs(); - float scale_y = abs_scale.GetY(); - float scale_xz = abs_scale.GetX(); - Vec3 scale_y_flip(1, Sign(inScale.GetY()), 1); - Vec3 scaled_top_center(0, scale_y * mTopCenter, 0); - Vec3 scaled_bottom_center(0, scale_y * mBottomCenter, 0); - float scaled_top_radius = scale_xz * mTopRadius; - float scaled_bottom_radius = scale_xz * mBottomRadius; - - for (SoftBodyVertex *v = ioVertices, *sbv_end = ioVertices + inNumVertices; v < sbv_end; ++v) - if (v->mInvMass > 0.0f) - { - Vec3 local_pos = scale_y_flip * (inverse_transform * v->mPosition); - - Vec3 position, normal; - - // If the vertex is inside the cone starting at the top center pointing along the y-axis with angle PI/2 - alpha then the closest point is on the top sphere - // This corresponds to: Dot(y-axis, (local_pos - top_center) / |local_pos - top_center|) >= cos(PI/2 - alpha) - // <=> (local_pos - top_center).y >= sin(alpha) * |local_pos - top_center| - Vec3 top_center_to_local_pos = local_pos - scaled_top_center; - float top_center_to_local_pos_len = top_center_to_local_pos.Length(); - if (top_center_to_local_pos.GetY() >= mSinAlpha * top_center_to_local_pos_len) - { - // Top sphere - normal = top_center_to_local_pos_len != 0.0f? top_center_to_local_pos / top_center_to_local_pos_len : Vec3::sAxisY(); - position = scaled_top_center + scaled_top_radius * normal; - } - else - { - // If the vertex is outside the cone starting at the bottom center pointing along the y-axis with angle PI/2 - alpha then the closest point is on the bottom sphere - // This corresponds to: Dot(y-axis, (local_pos - bottom_center) / |local_pos - bottom_center|) <= cos(PI/2 - alpha) - // <=> (local_pos - bottom_center).y <= sin(alpha) * |local_pos - bottom_center| - Vec3 bottom_center_to_local_pos = local_pos - scaled_bottom_center; - float bottom_center_to_local_pos_len = bottom_center_to_local_pos.Length(); - if (bottom_center_to_local_pos.GetY() <= mSinAlpha * bottom_center_to_local_pos_len) - { - // Bottom sphere - normal = bottom_center_to_local_pos_len != 0.0f? bottom_center_to_local_pos / bottom_center_to_local_pos_len : -Vec3::sAxisY(); - } - else - { - // Tapered cylinder - normal = Vec3(local_pos.GetX(), 0, local_pos.GetZ()).NormalizedOr(Vec3::sAxisX()); - normal.SetY(mTanAlpha); - normal = normal.NormalizedOr(Vec3::sAxisX()); - } - position = scaled_bottom_center + scaled_bottom_radius * normal; - } - - Plane plane = Plane::sFromPointAndNormal(position, normal); - float penetration = -plane.SignedDistance(local_pos); - if (penetration > v->mLargestPenetration) - { - v->mLargestPenetration = penetration; - - // Need to flip the normal's y if capsule is flipped (this corresponds to flipping both the point and the normal around y) - plane.SetNormal(scale_y_flip * plane.GetNormal()); - - // Store collision - v->mCollisionPlane = plane.GetTransformed(inCenterOfMassTransform); - v->mCollidingShapeIndex = inCollidingShapeIndex; - } - } -} - -#ifdef JPH_DEBUG_RENDERER -void TaperedCapsuleShape::Draw(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inUseMaterialColors, bool inDrawWireframe) const -{ - if (mGeometry == nullptr) - { - SupportBuffer buffer; - const Support *support = GetSupportFunction(ESupportMode::IncludeConvexRadius, buffer, Vec3::sReplicate(1.0f)); - mGeometry = inRenderer->CreateTriangleGeometryForConvex([support](Vec3Arg inDirection) { return support->GetSupport(inDirection); }); - } - - // Preserve flip along y axis but make sure we're not inside out - Vec3 scale = ScaleHelpers::IsInsideOut(inScale)? Vec3(-1, 1, 1) * inScale : inScale; - RMat44 world_transform = inCenterOfMassTransform * Mat44::sScale(scale); - - AABox bounds = Shape::GetWorldSpaceBounds(inCenterOfMassTransform, inScale); - - float lod_scale_sq = Square(max(mTopRadius, mBottomRadius)); - - Color color = inUseMaterialColors? GetMaterial()->GetDebugColor() : inColor; - - DebugRenderer::EDrawMode draw_mode = inDrawWireframe? DebugRenderer::EDrawMode::Wireframe : DebugRenderer::EDrawMode::Solid; - - inRenderer->DrawGeometry(world_transform, bounds, lod_scale_sq, color, mGeometry, DebugRenderer::ECullMode::CullBackFace, DebugRenderer::ECastShadow::On, draw_mode); -} -#endif // JPH_DEBUG_RENDERER - -AABox TaperedCapsuleShape::GetInertiaApproximation() const -{ - // TODO: For now the mass and inertia is that of a box - float avg_radius = 0.5f * (mTopRadius + mBottomRadius); - return AABox(Vec3(-avg_radius, mBottomCenter - mBottomRadius, -avg_radius), Vec3(avg_radius, mTopCenter + mTopRadius, avg_radius)); -} - -void TaperedCapsuleShape::TransformShape(Mat44Arg inCenterOfMassTransform, TransformedShapeCollector &ioCollector) const -{ - Vec3 scale; - Mat44 transform = inCenterOfMassTransform.Decompose(scale); - TransformedShape ts(RVec3(transform.GetTranslation()), transform.GetQuaternion(), this, BodyID(), SubShapeIDCreator()); - ts.SetShapeScale(scale.GetSign() * ScaleHelpers::MakeUniformScale(scale.Abs())); - ioCollector.AddHit(ts); -} - -void TaperedCapsuleShape::SaveBinaryState(StreamOut &inStream) const -{ - ConvexShape::SaveBinaryState(inStream); - - inStream.Write(mCenterOfMass); - inStream.Write(mTopRadius); - inStream.Write(mBottomRadius); - inStream.Write(mTopCenter); - inStream.Write(mBottomCenter); - inStream.Write(mConvexRadius); - inStream.Write(mSinAlpha); - inStream.Write(mTanAlpha); -} - -void TaperedCapsuleShape::RestoreBinaryState(StreamIn &inStream) -{ - ConvexShape::RestoreBinaryState(inStream); - - inStream.Read(mCenterOfMass); - inStream.Read(mTopRadius); - inStream.Read(mBottomRadius); - inStream.Read(mTopCenter); - inStream.Read(mBottomCenter); - inStream.Read(mConvexRadius); - inStream.Read(mSinAlpha); - inStream.Read(mTanAlpha); -} - -bool TaperedCapsuleShape::IsValidScale(Vec3Arg inScale) const -{ - return ConvexShape::IsValidScale(inScale) && ScaleHelpers::IsUniformScale(inScale.Abs()); -} - -void TaperedCapsuleShape::sRegister() -{ - ShapeFunctions &f = ShapeFunctions::sGet(EShapeSubType::TaperedCapsule); - f.mConstruct = []() -> Shape * { return new TaperedCapsuleShape; }; - f.mColor = Color::sGreen; -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/TaperedCapsuleShape.gliffy b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/TaperedCapsuleShape.gliffy deleted file mode 100644 index 3e5221b8655..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/TaperedCapsuleShape.gliffy +++ /dev/null @@ -1 +0,0 @@ -{"contentType":"application/gliffy+json","version":"1.1","metadata":{"title":"untitled","revision":0,"exportBorder":false},"embeddedResources":{"index":0,"resources":[]},"stage":{"objects":[{"x":870,"y":406,"rotation":0,"id":62,"uid":"com.gliffy.shape.basic.basic_v1.default.line","width":100,"height":100,"lockAspectRatio":false,"lockShape":false,"order":62,"graphic":{"type":"Line","Line":{"strokeWidth":2,"strokeColor":"#000000","fillColor":"none","dashStyle":"1.0,1.0","startArrow":0,"endArrow":0,"startArrowRotation":"auto","endArrowRotation":"auto","ortho":false,"interpolationType":"linear","cornerRadius":null,"controlPath":[[1.5,5.5],[1.5,-46.196228102251325]],"lockSegments":{}}},"children":null,"constraints":{"constraints":[],"startConstraint":{"type":"StartPositionConstraint","StartPositionConstraint":{"nodeId":4,"px":0.5,"py":0.5}}},"linkMap":[]},{"x":410,"y":406,"rotation":0,"id":60,"uid":"com.gliffy.shape.basic.basic_v1.default.line","width":100,"height":100,"lockAspectRatio":false,"lockShape":false,"order":60,"graphic":{"type":"Line","Line":{"strokeWidth":2,"strokeColor":"#000000","fillColor":"none","dashStyle":"1.0,1.0","startArrow":0,"endArrow":0,"startArrowRotation":"auto","endArrowRotation":"auto","ortho":false,"interpolationType":"linear","cornerRadius":null,"controlPath":[[0,5.5],[0,-49.03668490108288]],"lockSegments":{}}},"children":null,"constraints":{"constraints":[],"startConstraint":{"type":"StartPositionConstraint","StartPositionConstraint":{"nodeId":0,"px":0.5,"py":0.5}}},"linkMap":[]},{"x":614,"y":385,"rotation":0,"id":58,"uid":"com.gliffy.shape.basic.basic_v1.default.line","width":100,"height":100,"lockAspectRatio":false,"lockShape":false,"order":58,"graphic":{"type":"Line","Line":{"strokeWidth":2,"strokeColor":"#000000","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":2,"startArrowRotation":"auto","endArrowRotation":"auto","ortho":false,"interpolationType":"linear","cornerRadius":null,"controlPath":[[0,0],[-204.06126531020038,0]],"lockSegments":{}}},"children":null,"linkMap":[]},{"x":626,"y":385,"rotation":0,"id":57,"uid":"com.gliffy.shape.basic.basic_v1.default.line","width":100,"height":100,"lockAspectRatio":false,"lockShape":false,"order":57,"graphic":{"type":"Line","Line":{"strokeWidth":2,"strokeColor":"#000000","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":2,"startArrowRotation":"auto","endArrowRotation":"auto","ortho":false,"interpolationType":"linear","cornerRadius":null,"controlPath":[[0,0],[248.0020161208372,0]],"lockSegments":{}}},"children":null,"linkMap":[]},{"x":818,"y":520,"rotation":0,"id":55,"uid":"com.gliffy.shape.basic.basic_v1.default.line","width":100,"height":100,"lockAspectRatio":false,"lockShape":false,"order":55,"graphic":{"type":"Line","Line":{"strokeWidth":2,"strokeColor":"#000000","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":2,"startArrowRotation":"auto","endArrowRotation":"auto","ortho":false,"interpolationType":"linear","cornerRadius":null,"controlPath":[[0,0],[-48,76]],"lockSegments":{}}},"children":null,"linkMap":[]},{"x":830,"y":502,"rotation":0,"id":54,"uid":"com.gliffy.shape.basic.basic_v1.default.line","width":100,"height":100,"lockAspectRatio":false,"lockShape":false,"order":54,"graphic":{"type":"Line","Line":{"strokeWidth":2,"strokeColor":"#000000","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":2,"startArrowRotation":"auto","endArrowRotation":"auto","ortho":false,"interpolationType":"linear","cornerRadius":null,"controlPath":[[0,0],[48,-82]],"lockSegments":{}}},"children":null,"linkMap":[]},{"x":373,"y":410,"rotation":0,"id":50,"uid":"com.gliffy.shape.basic.basic_v1.default.text","width":28,"height":23,"lockAspectRatio":false,"lockShape":false,"order":19,"graphic":{"type":"Text","Text":{"tid":null,"valign":"middle","overflow":"none","vposition":"none","hposition":"none","html":"

              tx

              ","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null,"linkMap":[]},{"x":387,"y":392,"rotation":0,"id":49,"uid":"com.gliffy.shape.basic.basic_v1.default.text","width":26,"height":23,"lockAspectRatio":false,"lockShape":false,"order":18,"graphic":{"type":"Text","Text":{"tid":null,"valign":"middle","overflow":"none","vposition":"none","hposition":"none","html":"

              ty

              ","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null,"linkMap":[]},{"x":722,"y":488,"rotation":0,"id":45,"uid":"com.gliffy.shape.basic.basic_v1.default.text","width":20,"height":20,"lockAspectRatio":false,"lockShape":false,"order":16,"graphic":{"type":"Text","Text":{"tid":null,"valign":"middle","overflow":"none","vposition":"none","hposition":"none","html":"

              bx

              ","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null,"linkMap":[]},{"x":440,"y":411.5,"rotation":0,"id":40,"uid":"com.gliffy.shape.basic.basic_v1.default.text","width":29,"height":28.500000000000004,"lockAspectRatio":false,"lockShape":false,"order":13,"graphic":{"type":"Text","Text":{"tid":null,"valign":"middle","overflow":"none","vposition":"none","hposition":"none","html":"

              α

              ","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null,"linkMap":[]},{"x":411,"y":414,"rotation":0,"id":34,"uid":"com.gliffy.shape.basic.basic_v1.default.line","width":100,"height":100,"lockAspectRatio":false,"lockShape":false,"order":12,"graphic":{"type":"Line","Line":{"strokeWidth":2,"strokeColor":"#000000","fillColor":"none","dashStyle":"1.0,1.0","startArrow":0,"endArrow":0,"startArrowRotation":"auto","endArrowRotation":"auto","ortho":false,"interpolationType":"linear","cornerRadius":null,"controlPath":[[-1,-2.5],[349,180]],"lockSegments":{}}},"children":null,"constraints":{"constraints":[],"startConstraint":{"type":"StartPositionConstraint","StartPositionConstraint":{"nodeId":0,"px":0.5,"py":0.5}}},"linkMap":[]},{"x":744,"y":613,"rotation":0,"id":32,"uid":"com.gliffy.shape.basic.basic_v1.default.line","width":100,"height":100,"lockAspectRatio":false,"lockShape":false,"order":11,"graphic":{"type":"Line","Line":{"strokeWidth":2,"strokeColor":"#000000","fillColor":"none","dashStyle":"1.0,1.0","startArrow":0,"endArrow":0,"startArrowRotation":"auto","endArrowRotation":"auto","ortho":false,"interpolationType":"linear","cornerRadius":null,"controlPath":[[0,0],[-1.1368683772161603e-13,-201.00995000248122]],"lockSegments":{}}},"children":null,"linkMap":[]},{"x":394,"y":436,"rotation":0,"id":30,"uid":"com.gliffy.shape.basic.basic_v1.default.line","width":100,"height":100,"lockAspectRatio":false,"lockShape":false,"order":10,"graphic":{"type":"Line","Line":{"strokeWidth":2,"strokeColor":"#000000","fillColor":"none","dashStyle":"1.0,1.0","startArrow":0,"endArrow":0,"startArrowRotation":"auto","endArrowRotation":"auto","ortho":false,"interpolationType":"linear","cornerRadius":null,"controlPath":[[0,0],[0,-26.076809620810593]],"lockSegments":{}}},"children":null,"linkMap":[]},{"x":607,"y":368.5,"rotation":0,"id":26,"uid":"com.gliffy.shape.basic.basic_v1.default.text","width":20,"height":30,"lockAspectRatio":false,"lockShape":false,"order":9,"graphic":{"type":"Text","Text":{"tid":null,"valign":"middle","overflow":"none","vposition":"none","hposition":"none","html":"

              h

              ","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null,"linkMap":[]},{"x":401,"y":412.5,"rotation":0,"id":25,"uid":"com.gliffy.shape.basic.basic_v1.default.text","width":20,"height":30,"lockAspectRatio":false,"lockShape":false,"order":8,"graphic":{"type":"Text","Text":{"tid":null,"valign":"middle","overflow":"none","vposition":"none","hposition":"none","html":"

              tr

              ","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null,"linkMap":[]},{"x":808.5,"y":500,"rotation":0,"id":23,"uid":"com.gliffy.shape.basic.basic_v1.default.text","width":49,"height":27,"lockAspectRatio":false,"lockShape":false,"order":7,"graphic":{"type":"Text","Text":{"tid":null,"valign":"middle","overflow":"none","vposition":"none","hposition":"none","html":"

              br - tr

              ","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null,"linkMap":[]},{"x":402,"y":416,"rotation":0,"id":21,"uid":"com.gliffy.shape.basic.basic_v1.default.line","width":100,"height":100,"lockAspectRatio":false,"lockShape":false,"order":6,"graphic":{"type":"Line","Line":{"strokeWidth":2,"strokeColor":"#000000","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":0,"startArrowRotation":"auto","endArrowRotation":"auto","ortho":false,"interpolationType":"linear","cornerRadius":null,"controlPath":[[8,-4.5],[-7,21]],"lockSegments":{}}},"children":null,"constraints":{"constraints":[],"startConstraint":{"type":"StartPositionConstraint","StartPositionConstraint":{"nodeId":0,"px":0.5,"py":0.5}}},"linkMap":[]},{"x":347,"y":412,"rotation":0,"id":16,"uid":"com.gliffy.shape.basic.basic_v1.default.line","width":100,"height":100,"lockAspectRatio":false,"lockShape":false,"order":5,"graphic":{"type":"Line","Line":{"strokeWidth":2,"strokeColor":"#000000","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":0,"startArrowRotation":"auto","endArrowRotation":"auto","ortho":false,"interpolationType":"linear","cornerRadius":null,"controlPath":[[0,0],[523.5,2.5]],"lockSegments":{}}},"children":null,"linkMap":[]},{"x":854,"y":433,"rotation":0,"id":14,"uid":"com.gliffy.shape.basic.basic_v1.default.line","width":100,"height":100,"lockAspectRatio":false,"lockShape":false,"order":4,"graphic":{"type":"Line","Line":{"strokeWidth":2,"strokeColor":"#000000","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":0,"startArrowRotation":"auto","endArrowRotation":"auto","ortho":false,"interpolationType":"linear","cornerRadius":null,"controlPath":[[17.5,-21.5],[-106,184]],"lockSegments":{}}},"children":null,"constraints":{"constraints":[],"startConstraint":{"type":"StartPositionConstraint","StartPositionConstraint":{"nodeId":4,"px":0.5,"py":0.5}}},"linkMap":[]},{"x":395,"y":437,"rotation":0,"id":9,"uid":"com.gliffy.shape.basic.basic_v1.default.line","width":100,"height":100,"lockAspectRatio":false,"lockShape":false,"order":3,"graphic":{"type":"Line","Line":{"strokeWidth":2,"strokeColor":"#000000","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":0,"startArrowRotation":"auto","endArrowRotation":"auto","ortho":false,"interpolationType":"linear","cornerRadius":null,"controlPath":[[-52,-25],[695,360]],"lockSegments":{}}},"children":null,"linkMap":[]},{"x":395,"y":384,"rotation":0,"id":7,"uid":"com.gliffy.shape.basic.basic_v1.default.line","width":100,"height":100,"lockAspectRatio":false,"lockShape":false,"order":2,"graphic":{"type":"Line","Line":{"strokeWidth":2,"strokeColor":"#000000","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":0,"startArrowRotation":"auto","endArrowRotation":"auto","ortho":false,"interpolationType":"linear","cornerRadius":null,"controlPath":[[-49,26],[703,-359]],"lockSegments":{}}},"children":null,"linkMap":[]},{"x":630,"y":169.99999999999997,"rotation":0,"id":4,"uid":"com.gliffy.shape.basic.basic_v1.default.circle","width":483.00000000000006,"height":483.00000000000006,"lockAspectRatio":true,"lockShape":false,"order":1,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.ellipse.basic_v1","strokeWidth":2,"strokeColor":"#333333","fillColor":"#FFFFFF","gradient":false,"dropShadow":false,"state":0,"shadowX":0,"shadowY":0,"opacity":1}},"children":[],"linkMap":[]},{"x":380,"y":381.5,"rotation":0,"id":0,"uid":"com.gliffy.shape.basic.basic_v1.default.circle","width":60,"height":60,"lockAspectRatio":true,"lockShape":false,"order":0,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.ellipse.basic_v1","strokeWidth":2,"strokeColor":"#333333","fillColor":"#FFFFFF","gradient":false,"dropShadow":false,"state":0,"shadowX":0,"shadowY":0,"opacity":1}},"children":[],"linkMap":[]},{"x":728,"y":612,"rotation":0,"id":41,"uid":"com.gliffy.shape.basic.basic_v1.default.text","width":29,"height":28.500000000000004,"lockAspectRatio":false,"lockShape":false,"order":14,"graphic":{"type":"Text","Text":{"tid":null,"valign":"middle","overflow":"none","vposition":"none","hposition":"none","html":"

              α

              ","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null,"linkMap":[]},{"x":375,"y":432.5,"rotation":0,"id":42,"uid":"com.gliffy.shape.basic.basic_v1.default.text","width":29,"height":28.500000000000004,"lockAspectRatio":false,"lockShape":false,"order":15,"graphic":{"type":"Text","Text":{"tid":null,"valign":"middle","overflow":"none","vposition":"none","hposition":"none","html":"

              α

              ","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null,"linkMap":[]},{"x":753,"y":595.5,"rotation":0,"id":53,"uid":"com.gliffy.shape.basic.basic_v1.default.text","width":20,"height":30,"lockAspectRatio":false,"lockShape":false,"order":20,"graphic":{"type":"Text","Text":{"tid":null,"valign":"middle","overflow":"none","vposition":"none","hposition":"none","html":"

              tr

              ","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null,"linkMap":[]},{"x":794,"y":400,"rotation":0,"id":65,"uid":"com.gliffy.shape.basic.basic_v1.default.line","width":100,"height":100,"lockAspectRatio":false,"lockShape":false,"order":65,"graphic":{"type":"Line","Line":{"strokeWidth":2,"strokeColor":"#000000","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":2,"startArrowRotation":"auto","endArrowRotation":"auto","ortho":false,"interpolationType":"linear","cornerRadius":null,"controlPath":[[0,0],[-48.01041553663117,0]],"lockSegments":{}}},"children":null,"linkMap":[]},{"x":790,"y":393.25,"rotation":0,"id":46,"uid":"com.gliffy.shape.basic.basic_v1.default.text","width":29,"height":14,"lockAspectRatio":false,"lockShape":false,"order":17,"graphic":{"type":"Text","Text":{"tid":null,"valign":"middle","overflow":"none","vposition":"none","hposition":"none","html":"

              by

              ","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null,"linkMap":[]},{"x":818,"y":400,"rotation":0,"id":66,"uid":"com.gliffy.shape.basic.basic_v1.default.line","width":100,"height":100,"lockAspectRatio":false,"lockShape":false,"order":66,"graphic":{"type":"Line","Line":{"strokeWidth":2,"strokeColor":"#000000","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":2,"startArrowRotation":"auto","endArrowRotation":"auto","ortho":false,"interpolationType":"linear","cornerRadius":null,"controlPath":[[0,0],[54.230987451824944,0]],"lockSegments":{}}},"children":null,"linkMap":[]}],"background":"#FFFFFF","width":1113,"height":802,"maxWidth":5000,"maxHeight":5000,"nodeIndex":69,"autoFit":true,"exportBorder":false,"gridOn":true,"snapToGrid":false,"drawingGuidesOn":false,"pageBreaksOn":false,"printGridOn":false,"printPaper":"LETTER","printShrinkToFit":false,"printPortrait":true,"shapeStyles":{"com.gliffy.shape.basic.basic_v1.default":{"fill":"#FFFFFF","stroke":"#333333","strokeWidth":2}},"lineStyles":{"global":{"dashStyle":null,"endArrow":2,"startArrow":0}},"textStyles":{},"themeData":null}} \ No newline at end of file diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/TaperedCapsuleShape.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/TaperedCapsuleShape.h deleted file mode 100644 index 6dad3c2e9b1..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/TaperedCapsuleShape.h +++ /dev/null @@ -1,125 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#ifdef JPH_DEBUG_RENDERER - #include -#endif // JPH_DEBUG_RENDERER - -JPH_NAMESPACE_BEGIN - -/// Class that constructs a TaperedCapsuleShape -class JPH_EXPORT TaperedCapsuleShapeSettings final : public ConvexShapeSettings -{ - JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, TaperedCapsuleShapeSettings) - - /// Default constructor for deserialization - TaperedCapsuleShapeSettings() = default; - - /// Create a tapered capsule centered around the origin with one sphere cap at (0, -inHalfHeightOfTaperedCylinder, 0) with radius inBottomRadius and the other at (0, inHalfHeightOfTaperedCylinder, 0) with radius inTopRadius - TaperedCapsuleShapeSettings(float inHalfHeightOfTaperedCylinder, float inTopRadius, float inBottomRadius, const PhysicsMaterial *inMaterial = nullptr); - - /// Check if the settings are valid - bool IsValid() const { return mTopRadius > 0.0f && mBottomRadius > 0.0f && mHalfHeightOfTaperedCylinder >= 0.0f; } - - /// Checks if the settings of this tapered capsule make this shape a sphere - bool IsSphere() const; - - // See: ShapeSettings - virtual ShapeResult Create() const override; - - float mHalfHeightOfTaperedCylinder = 0.0f; - float mTopRadius = 0.0f; - float mBottomRadius = 0.0f; -}; - -/// A capsule with different top and bottom radii -class JPH_EXPORT TaperedCapsuleShape final : public ConvexShape -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Constructor - TaperedCapsuleShape() : ConvexShape(EShapeSubType::TaperedCapsule) { } - TaperedCapsuleShape(const TaperedCapsuleShapeSettings &inSettings, ShapeResult &outResult); - - // See Shape::GetCenterOfMass - virtual Vec3 GetCenterOfMass() const override { return mCenterOfMass; } - - // See Shape::GetLocalBounds - virtual AABox GetLocalBounds() const override; - - // See Shape::GetWorldSpaceBounds - virtual AABox GetWorldSpaceBounds(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale) const override; - using Shape::GetWorldSpaceBounds; - - // See Shape::GetInnerRadius - virtual float GetInnerRadius() const override { return min(mTopRadius, mBottomRadius); } - - // See Shape::GetMassProperties - virtual MassProperties GetMassProperties() const override; - - // See Shape::GetSurfaceNormal - virtual Vec3 GetSurfaceNormal(const SubShapeID &inSubShapeID, Vec3Arg inLocalSurfacePosition) const override; - - // See Shape::GetSupportingFace - virtual void GetSupportingFace(const SubShapeID &inSubShapeID, Vec3Arg inDirection, Vec3Arg inScale, Mat44Arg inCenterOfMassTransform, SupportingFace &outVertices) const override; - - // See ConvexShape::GetSupportFunction - virtual const Support * GetSupportFunction(ESupportMode inMode, SupportBuffer &inBuffer, Vec3Arg inScale) const override; - - // See: Shape::CollideSoftBodyVertices - virtual void CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, SoftBodyVertex *ioVertices, uint inNumVertices, float inDeltaTime, Vec3Arg inDisplacementDueToGravity, int inCollidingShapeIndex) const override; - -#ifdef JPH_DEBUG_RENDERER - // See Shape::Draw - virtual void Draw(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inUseMaterialColors, bool inDrawWireframe) const override; -#endif // JPH_DEBUG_RENDERER - - // See Shape::TransformShape - virtual void TransformShape(Mat44Arg inCenterOfMassTransform, TransformedShapeCollector &ioCollector) const override; - - // See Shape - virtual void SaveBinaryState(StreamOut &inStream) const override; - - // See Shape::GetStats - virtual Stats GetStats() const override { return Stats(sizeof(*this), 0); } - - // See Shape::GetVolume - virtual float GetVolume() const override { return GetLocalBounds().GetVolume(); } // Volume is approximate! - - // See Shape::IsValidScale - virtual bool IsValidScale(Vec3Arg inScale) const override; - - // Register shape functions with the registry - static void sRegister(); - -protected: - // See: Shape::RestoreBinaryState - virtual void RestoreBinaryState(StreamIn &inStream) override; - -private: - // Class for GetSupportFunction - class TaperedCapsule; - - /// Returns box that approximates the inertia - AABox GetInertiaApproximation() const; - - Vec3 mCenterOfMass = Vec3::sZero(); - float mTopRadius = 0.0f; - float mBottomRadius = 0.0f; - float mTopCenter = 0.0f; - float mBottomCenter = 0.0f; - float mConvexRadius = 0.0f; - float mSinAlpha = 0.0f; - float mTanAlpha = 0.0f; - -#ifdef JPH_DEBUG_RENDERER - mutable DebugRenderer::GeometryRef mGeometry; -#endif // JPH_DEBUG_RENDERER -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/TriangleShape.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/TriangleShape.cpp deleted file mode 100644 index b70179647d1..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/TriangleShape.cpp +++ /dev/null @@ -1,413 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef JPH_DEBUG_RENDERER - #include -#endif // JPH_DEBUG_RENDERER - -JPH_NAMESPACE_BEGIN - -JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(TriangleShapeSettings) -{ - JPH_ADD_BASE_CLASS(TriangleShapeSettings, ConvexShapeSettings) - - JPH_ADD_ATTRIBUTE(TriangleShapeSettings, mV1) - JPH_ADD_ATTRIBUTE(TriangleShapeSettings, mV2) - JPH_ADD_ATTRIBUTE(TriangleShapeSettings, mV3) - JPH_ADD_ATTRIBUTE(TriangleShapeSettings, mConvexRadius) -} - -ShapeSettings::ShapeResult TriangleShapeSettings::Create() const -{ - if (mCachedResult.IsEmpty()) - Ref shape = new TriangleShape(*this, mCachedResult); - return mCachedResult; -} - -TriangleShape::TriangleShape(const TriangleShapeSettings &inSettings, ShapeResult &outResult) : - ConvexShape(EShapeSubType::Triangle, inSettings, outResult), - mV1(inSettings.mV1), - mV2(inSettings.mV2), - mV3(inSettings.mV3), - mConvexRadius(inSettings.mConvexRadius) -{ - if (inSettings.mConvexRadius < 0.0f) - { - outResult.SetError("Invalid convex radius"); - return; - } - - outResult.Set(this); -} - -AABox TriangleShape::GetLocalBounds() const -{ - AABox bounds(mV1, mV1); - bounds.Encapsulate(mV2); - bounds.Encapsulate(mV3); - bounds.ExpandBy(Vec3::sReplicate(mConvexRadius)); - return bounds; -} - -AABox TriangleShape::GetWorldSpaceBounds(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale) const -{ - JPH_ASSERT(IsValidScale(inScale)); - - Vec3 v1 = inCenterOfMassTransform * (inScale * mV1); - Vec3 v2 = inCenterOfMassTransform * (inScale * mV2); - Vec3 v3 = inCenterOfMassTransform * (inScale * mV3); - - AABox bounds(v1, v1); - bounds.Encapsulate(v2); - bounds.Encapsulate(v3); - bounds.ExpandBy(inScale * mConvexRadius); - return bounds; -} - -class TriangleShape::TriangleNoConvex final : public Support -{ -public: - TriangleNoConvex(Vec3Arg inV1, Vec3Arg inV2, Vec3Arg inV3) : - mTriangleSuport(inV1, inV2, inV3) - { - static_assert(sizeof(TriangleNoConvex) <= sizeof(SupportBuffer), "Buffer size too small"); - JPH_ASSERT(IsAligned(this, alignof(TriangleNoConvex))); - } - - virtual Vec3 GetSupport(Vec3Arg inDirection) const override - { - return mTriangleSuport.GetSupport(inDirection); - } - - virtual float GetConvexRadius() const override - { - return 0.0f; - } - -private: - TriangleConvexSupport mTriangleSuport; -}; - -class TriangleShape::TriangleWithConvex final : public Support -{ -public: - TriangleWithConvex(Vec3Arg inV1, Vec3Arg inV2, Vec3Arg inV3, float inConvexRadius) : - mConvexRadius(inConvexRadius), - mTriangleSuport(inV1, inV2, inV3) - { - static_assert(sizeof(TriangleWithConvex) <= sizeof(SupportBuffer), "Buffer size too small"); - JPH_ASSERT(IsAligned(this, alignof(TriangleWithConvex))); - } - - virtual Vec3 GetSupport(Vec3Arg inDirection) const override - { - Vec3 support = mTriangleSuport.GetSupport(inDirection); - float len = inDirection.Length(); - if (len > 0.0f) - support += (mConvexRadius / len) * inDirection; - return support; - } - - virtual float GetConvexRadius() const override - { - return mConvexRadius; - } - -private: - float mConvexRadius; - TriangleConvexSupport mTriangleSuport; -}; - -const ConvexShape::Support *TriangleShape::GetSupportFunction(ESupportMode inMode, SupportBuffer &inBuffer, Vec3Arg inScale) const -{ - switch (inMode) - { - case ESupportMode::IncludeConvexRadius: - case ESupportMode::Default: - if (mConvexRadius > 0.0f) - return new (&inBuffer) TriangleWithConvex(inScale * mV1, inScale * mV2, inScale * mV3, mConvexRadius); - [[fallthrough]]; - - case ESupportMode::ExcludeConvexRadius: - return new (&inBuffer) TriangleNoConvex(inScale * mV1, inScale * mV2, inScale * mV3); - } - - JPH_ASSERT(false); - return nullptr; -} - -void TriangleShape::GetSupportingFace(const SubShapeID &inSubShapeID, Vec3Arg inDirection, Vec3Arg inScale, Mat44Arg inCenterOfMassTransform, SupportingFace &outVertices) const -{ - JPH_ASSERT(inSubShapeID.IsEmpty(), "Invalid subshape ID"); - - // Calculate transform with scale - Mat44 transform = inCenterOfMassTransform.PreScaled(inScale); - - // Flip triangle if scaled inside out - if (ScaleHelpers::IsInsideOut(inScale)) - { - outVertices.push_back(transform * mV1); - outVertices.push_back(transform * mV3); - outVertices.push_back(transform * mV2); - } - else - { - outVertices.push_back(transform * mV1); - outVertices.push_back(transform * mV2); - outVertices.push_back(transform * mV3); - } -} - -MassProperties TriangleShape::GetMassProperties() const -{ - // Object should always be static, return default mass properties - return MassProperties(); -} - -Vec3 TriangleShape::GetSurfaceNormal(const SubShapeID &inSubShapeID, Vec3Arg inLocalSurfacePosition) const -{ - JPH_ASSERT(inSubShapeID.IsEmpty(), "Invalid subshape ID"); - - Vec3 cross = (mV2 - mV1).Cross(mV3 - mV1); - float len = cross.Length(); - return len != 0.0f? cross / len : Vec3::sAxisY(); -} - -void TriangleShape::GetSubmergedVolume(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, const Plane &inSurface, float &outTotalVolume, float &outSubmergedVolume, Vec3 &outCenterOfBuoyancy JPH_IF_DEBUG_RENDERER(, RVec3Arg inBaseOffset)) const -{ - // A triangle has no volume - outTotalVolume = outSubmergedVolume = 0.0f; - outCenterOfBuoyancy = Vec3::sZero(); -} - -#ifdef JPH_DEBUG_RENDERER -void TriangleShape::Draw(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inUseMaterialColors, bool inDrawWireframe) const -{ - RVec3 v1 = inCenterOfMassTransform * (inScale * mV1); - RVec3 v2 = inCenterOfMassTransform * (inScale * mV2); - RVec3 v3 = inCenterOfMassTransform * (inScale * mV3); - - if (ScaleHelpers::IsInsideOut(inScale)) - swap(v1, v2); - - if (inDrawWireframe) - inRenderer->DrawWireTriangle(v1, v2, v3, inUseMaterialColors? GetMaterial()->GetDebugColor() : inColor); - else - inRenderer->DrawTriangle(v1, v2, v3, inUseMaterialColors? GetMaterial()->GetDebugColor() : inColor); -} -#endif // JPH_DEBUG_RENDERER - -bool TriangleShape::CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const -{ - float fraction = RayTriangle(inRay.mOrigin, inRay.mDirection, mV1, mV2, mV3); - if (fraction < ioHit.mFraction) - { - ioHit.mFraction = fraction; - ioHit.mSubShapeID2 = inSubShapeIDCreator.GetID(); - return true; - } - return false; -} - -void TriangleShape::CastRay(const RayCast &inRay, const RayCastSettings &inRayCastSettings, const SubShapeIDCreator &inSubShapeIDCreator, CastRayCollector &ioCollector, const ShapeFilter &inShapeFilter) const -{ - // Test shape filter - if (!inShapeFilter.ShouldCollide(this, inSubShapeIDCreator.GetID())) - return; - - // Back facing check - if (inRayCastSettings.mBackFaceMode == EBackFaceMode::IgnoreBackFaces && (mV2 - mV1).Cross(mV3 - mV1).Dot(inRay.mDirection) > 0.0f) - return; - - // Test ray against triangle - float fraction = RayTriangle(inRay.mOrigin, inRay.mDirection, mV1, mV2, mV3); - if (fraction < ioCollector.GetEarlyOutFraction()) - { - // Better hit than the current hit - RayCastResult hit; - hit.mBodyID = TransformedShape::sGetBodyID(ioCollector.GetContext()); - hit.mFraction = fraction; - hit.mSubShapeID2 = inSubShapeIDCreator.GetID(); - ioCollector.AddHit(hit); - } -} - -void TriangleShape::CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter) const -{ - // Can't be inside a triangle -} - -void TriangleShape::CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, SoftBodyVertex *ioVertices, uint inNumVertices, [[maybe_unused]] float inDeltaTime, [[maybe_unused]] Vec3Arg inDisplacementDueToGravity, int inCollidingShapeIndex) const -{ - CollideSoftBodyVerticesVsTriangles collider(inCenterOfMassTransform, inScale); - - for (SoftBodyVertex *v = ioVertices, *sbv_end = ioVertices + inNumVertices; v < sbv_end; ++v) - if (v->mInvMass > 0.0f) - { - collider.StartVertex(*v); - collider.ProcessTriangle(mV1, mV2, mV3); - collider.FinishVertex(*v, inCollidingShapeIndex); - } -} - -void TriangleShape::sCollideConvexVsTriangle(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, [[maybe_unused]] const ShapeFilter &inShapeFilter) -{ - JPH_ASSERT(inShape1->GetType() == EShapeType::Convex); - const ConvexShape *shape1 = static_cast(inShape1); - JPH_ASSERT(inShape2->GetSubType() == EShapeSubType::Triangle); - const TriangleShape *shape2 = static_cast(inShape2); - - CollideConvexVsTriangles collider(shape1, inScale1, inScale2, inCenterOfMassTransform1, inCenterOfMassTransform2, inSubShapeIDCreator1.GetID(), inCollideShapeSettings, ioCollector); - collider.Collide(shape2->mV1, shape2->mV2, shape2->mV3, 0b111, inSubShapeIDCreator2.GetID()); -} - -void TriangleShape::sCollideSphereVsTriangle(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, [[maybe_unused]] const ShapeFilter &inShapeFilter) -{ - JPH_ASSERT(inShape1->GetSubType() == EShapeSubType::Sphere); - const SphereShape *shape1 = static_cast(inShape1); - JPH_ASSERT(inShape2->GetSubType() == EShapeSubType::Triangle); - const TriangleShape *shape2 = static_cast(inShape2); - - CollideSphereVsTriangles collider(shape1, inScale1, inScale2, inCenterOfMassTransform1, inCenterOfMassTransform2, inSubShapeIDCreator1.GetID(), inCollideShapeSettings, ioCollector); - collider.Collide(shape2->mV1, shape2->mV2, shape2->mV3, 0b111, inSubShapeIDCreator2.GetID()); -} - -void TriangleShape::sCastConvexVsTriangle(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, [[maybe_unused]] const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector) -{ - JPH_ASSERT(inShape->GetSubType() == EShapeSubType::Triangle); - const TriangleShape *shape = static_cast(inShape); - - CastConvexVsTriangles caster(inShapeCast, inShapeCastSettings, inScale, inCenterOfMassTransform2, inSubShapeIDCreator1, ioCollector); - caster.Cast(shape->mV1, shape->mV2, shape->mV3, 0b111, inSubShapeIDCreator2.GetID()); -} - -void TriangleShape::sCastSphereVsTriangle(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, [[maybe_unused]] const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector) -{ - JPH_ASSERT(inShape->GetSubType() == EShapeSubType::Triangle); - const TriangleShape *shape = static_cast(inShape); - - CastSphereVsTriangles caster(inShapeCast, inShapeCastSettings, inScale, inCenterOfMassTransform2, inSubShapeIDCreator1, ioCollector); - caster.Cast(shape->mV1, shape->mV2, shape->mV3, 0b111, inSubShapeIDCreator2.GetID()); -} - -void TriangleShape::TransformShape(Mat44Arg inCenterOfMassTransform, TransformedShapeCollector &ioCollector) const -{ - Vec3 scale; - Mat44 transform = inCenterOfMassTransform.Decompose(scale); - TransformedShape ts(RVec3(transform.GetTranslation()), transform.GetQuaternion(), this, BodyID(), SubShapeIDCreator()); - ts.SetShapeScale(mConvexRadius == 0.0f? scale : scale.GetSign() * ScaleHelpers::MakeUniformScale(scale.Abs())); - ioCollector.AddHit(ts); -} - -class TriangleShape::TSGetTrianglesContext -{ -public: - TSGetTrianglesContext(Vec3Arg inV1, Vec3Arg inV2, Vec3Arg inV3) : mV1(inV1), mV2(inV2), mV3(inV3) { } - - Vec3 mV1; - Vec3 mV2; - Vec3 mV3; - - bool mIsDone = false; -}; - -void TriangleShape::GetTrianglesStart(GetTrianglesContext &ioContext, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) const -{ - static_assert(sizeof(TSGetTrianglesContext) <= sizeof(GetTrianglesContext), "GetTrianglesContext too small"); - JPH_ASSERT(IsAligned(&ioContext, alignof(TSGetTrianglesContext))); - - Mat44 m = Mat44::sRotationTranslation(inRotation, inPositionCOM) * Mat44::sScale(inScale); - - new (&ioContext) TSGetTrianglesContext(m * mV1, m * mV2, m * mV3); -} - -int TriangleShape::GetTrianglesNext(GetTrianglesContext &ioContext, int inMaxTrianglesRequested, Float3 *outTriangleVertices, const PhysicsMaterial **outMaterials) const -{ - static_assert(cGetTrianglesMinTrianglesRequested >= 3, "cGetTrianglesMinTrianglesRequested is too small"); - JPH_ASSERT(inMaxTrianglesRequested >= cGetTrianglesMinTrianglesRequested); - - TSGetTrianglesContext &context = (TSGetTrianglesContext &)ioContext; - - // Only return the triangle the 1st time - if (context.mIsDone) - return 0; - context.mIsDone = true; - - // Store triangle - context.mV1.StoreFloat3(outTriangleVertices); - context.mV2.StoreFloat3(outTriangleVertices + 1); - context.mV3.StoreFloat3(outTriangleVertices + 2); - - // Store material - if (outMaterials != nullptr) - *outMaterials = GetMaterial(); - - return 1; -} - -void TriangleShape::SaveBinaryState(StreamOut &inStream) const -{ - ConvexShape::SaveBinaryState(inStream); - - inStream.Write(mV1); - inStream.Write(mV2); - inStream.Write(mV3); - inStream.Write(mConvexRadius); -} - -void TriangleShape::RestoreBinaryState(StreamIn &inStream) -{ - ConvexShape::RestoreBinaryState(inStream); - - inStream.Read(mV1); - inStream.Read(mV2); - inStream.Read(mV3); - inStream.Read(mConvexRadius); -} - -bool TriangleShape::IsValidScale(Vec3Arg inScale) const -{ - return ConvexShape::IsValidScale(inScale) && (mConvexRadius == 0.0f || ScaleHelpers::IsUniformScale(inScale.Abs())); -} - -void TriangleShape::sRegister() -{ - ShapeFunctions &f = ShapeFunctions::sGet(EShapeSubType::Triangle); - f.mConstruct = []() -> Shape * { return new TriangleShape; }; - f.mColor = Color::sGreen; - - for (EShapeSubType s : sConvexSubShapeTypes) - { - CollisionDispatch::sRegisterCollideShape(s, EShapeSubType::Triangle, sCollideConvexVsTriangle); - CollisionDispatch::sRegisterCastShape(s, EShapeSubType::Triangle, sCastConvexVsTriangle); - } - - // Specialized collision functions - CollisionDispatch::sRegisterCollideShape(EShapeSubType::Sphere, EShapeSubType::Triangle, sCollideSphereVsTriangle); - CollisionDispatch::sRegisterCastShape(EShapeSubType::Sphere, EShapeSubType::Triangle, sCastSphereVsTriangle); -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/TriangleShape.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/TriangleShape.h deleted file mode 100644 index 990e6e051b8..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/Shape/TriangleShape.h +++ /dev/null @@ -1,138 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -/// Class that constructs a TriangleShape -class JPH_EXPORT TriangleShapeSettings final : public ConvexShapeSettings -{ -public: - JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, TriangleShapeSettings) - - /// Default constructor for deserialization - TriangleShapeSettings() = default; - - /// Create a triangle with points (inV1, inV2, inV3) (counter clockwise) and convex radius inConvexRadius. - /// Note that the convex radius is currently only used for shape vs shape collision, for all other purposes the triangle is infinitely thin. - TriangleShapeSettings(Vec3Arg inV1, Vec3Arg inV2, Vec3Arg inV3, float inConvexRadius = 0.0f, const PhysicsMaterial *inMaterial = nullptr) : ConvexShapeSettings(inMaterial), mV1(inV1), mV2(inV2), mV3(inV3), mConvexRadius(inConvexRadius) { } - - // See: ShapeSettings - virtual ShapeResult Create() const override; - - Vec3 mV1; - Vec3 mV2; - Vec3 mV3; - float mConvexRadius = 0.0f; -}; - -/// A single triangle, not the most efficient way of creating a world filled with triangles but can be used as a query shape for example. -class JPH_EXPORT TriangleShape final : public ConvexShape -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Constructor - TriangleShape() : ConvexShape(EShapeSubType::Triangle) { } - TriangleShape(const TriangleShapeSettings &inSettings, ShapeResult &outResult); - - /// Create a triangle with points (inV1, inV2, inV3) (counter clockwise) and convex radius inConvexRadius. - /// Note that the convex radius is currently only used for shape vs shape collision, for all other purposes the triangle is infinitely thin. - TriangleShape(Vec3Arg inV1, Vec3Arg inV2, Vec3Arg inV3, float inConvexRadius = 0.0f, const PhysicsMaterial *inMaterial = nullptr) : ConvexShape(EShapeSubType::Triangle, inMaterial), mV1(inV1), mV2(inV2), mV3(inV3), mConvexRadius(inConvexRadius) { JPH_ASSERT(inConvexRadius >= 0.0f); } - - /// Convex radius - float GetConvexRadius() const { return mConvexRadius; } - - // See Shape::GetLocalBounds - virtual AABox GetLocalBounds() const override; - - // See Shape::GetWorldSpaceBounds - virtual AABox GetWorldSpaceBounds(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale) const override; - using Shape::GetWorldSpaceBounds; - - // See Shape::GetInnerRadius - virtual float GetInnerRadius() const override { return mConvexRadius; } - - // See Shape::GetMassProperties - virtual MassProperties GetMassProperties() const override; - - // See Shape::GetSurfaceNormal - virtual Vec3 GetSurfaceNormal(const SubShapeID &inSubShapeID, Vec3Arg inLocalSurfacePosition) const override; - - // See Shape::GetSupportingFace - virtual void GetSupportingFace(const SubShapeID &inSubShapeID, Vec3Arg inDirection, Vec3Arg inScale, Mat44Arg inCenterOfMassTransform, SupportingFace &outVertices) const override; - - // See ConvexShape::GetSupportFunction - virtual const Support * GetSupportFunction(ESupportMode inMode, SupportBuffer &inBuffer, Vec3Arg inScale) const override; - - // See Shape::GetSubmergedVolume - virtual void GetSubmergedVolume(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, const Plane &inSurface, float &outTotalVolume, float &outSubmergedVolume, Vec3 &outCenterOfBuoyancy JPH_IF_DEBUG_RENDERER(, RVec3Arg inBaseOffset)) const override; - -#ifdef JPH_DEBUG_RENDERER - // See Shape::Draw - virtual void Draw(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inUseMaterialColors, bool inDrawWireframe) const override; -#endif // JPH_DEBUG_RENDERER - - // See Shape::CastRay - virtual bool CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const override; - virtual void CastRay(const RayCast &inRay, const RayCastSettings &inRayCastSettings, const SubShapeIDCreator &inSubShapeIDCreator, CastRayCollector &ioCollector, const ShapeFilter &inShapeFilter = { }) const override; - - // See: Shape::CollidePoint - virtual void CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter = { }) const override; - - // See: Shape::CollideSoftBodyVertices - virtual void CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, SoftBodyVertex *ioVertices, uint inNumVertices, float inDeltaTime, Vec3Arg inDisplacementDueToGravity, int inCollidingShapeIndex) const override; - - // See Shape::TransformShape - virtual void TransformShape(Mat44Arg inCenterOfMassTransform, TransformedShapeCollector &ioCollector) const override; - - // See Shape::GetTrianglesStart - virtual void GetTrianglesStart(GetTrianglesContext &ioContext, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) const override; - - // See Shape::GetTrianglesNext - virtual int GetTrianglesNext(GetTrianglesContext &ioContext, int inMaxTrianglesRequested, Float3 *outTriangleVertices, const PhysicsMaterial **outMaterials = nullptr) const override; - - // See Shape - virtual void SaveBinaryState(StreamOut &inStream) const override; - - // See Shape::GetStats - virtual Stats GetStats() const override { return Stats(sizeof(*this), 1); } - - // See Shape::GetVolume - virtual float GetVolume() const override { return 0; } - - // See Shape::IsValidScale - virtual bool IsValidScale(Vec3Arg inScale) const override; - - // Register shape functions with the registry - static void sRegister(); - -protected: - // See: Shape::RestoreBinaryState - virtual void RestoreBinaryState(StreamIn &inStream) override; - -private: - // Helper functions called by CollisionDispatch - static void sCollideConvexVsTriangle(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter); - static void sCollideSphereVsTriangle(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter); - static void sCastConvexVsTriangle(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector); - static void sCastSphereVsTriangle(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector); - - // Context for GetTrianglesStart/Next - class TSGetTrianglesContext; - - // Classes for GetSupportFunction - class TriangleNoConvex; - class TriangleWithConvex; - - Vec3 mV1; - Vec3 mV2; - Vec3 mV3; - float mConvexRadius = 0.0f; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ShapeCast.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ShapeCast.h deleted file mode 100644 index e7741b84d82..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ShapeCast.h +++ /dev/null @@ -1,170 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -/// Structure that holds a single shape cast (a shape moving along a linear path in 3d space with no rotation) -template -struct ShapeCastT -{ - JPH_OVERRIDE_NEW_DELETE - - /// Constructor - ShapeCastT(const Shape *inShape, Vec3Arg inScale, typename Mat::ArgType inCenterOfMassStart, Vec3Arg inDirection, const AABox &inWorldSpaceBounds) : - mShape(inShape), - mScale(inScale), - mCenterOfMassStart(inCenterOfMassStart), - mDirection(inDirection), - mShapeWorldBounds(inWorldSpaceBounds) - { - } - - /// Constructor - ShapeCastT(const Shape *inShape, Vec3Arg inScale, typename Mat::ArgType inCenterOfMassStart, Vec3Arg inDirection) : - ShapeCastT(inShape, inScale, inCenterOfMassStart, inDirection, inShape->GetWorldSpaceBounds(inCenterOfMassStart, inScale)) - { - } - - /// Construct a shape cast using a world transform for a shape instead of a center of mass transform - static inline ShapeCastType sFromWorldTransform(const Shape *inShape, Vec3Arg inScale, typename Mat::ArgType inWorldTransform, Vec3Arg inDirection) - { - return ShapeCastType(inShape, inScale, inWorldTransform.PreTranslated(inShape->GetCenterOfMass()), inDirection); - } - - /// Transform this shape cast using inTransform. Multiply transform on the left left hand side. - ShapeCastType PostTransformed(typename Mat::ArgType inTransform) const - { - Mat44 start = inTransform * mCenterOfMassStart; - Vec3 direction = inTransform.Multiply3x3(mDirection); - return { mShape, mScale, start, direction }; - } - - /// Translate this shape cast by inTranslation. - ShapeCastType PostTranslated(typename Vec::ArgType inTranslation) const - { - return { mShape, mScale, mCenterOfMassStart.PostTranslated(inTranslation), mDirection }; - } - - /// Get point with fraction inFraction on ray from mCenterOfMassStart to mCenterOfMassStart + mDirection (0 = start of ray, 1 = end of ray) - inline Vec GetPointOnRay(float inFraction) const - { - return mCenterOfMassStart.GetTranslation() + inFraction * mDirection; - } - - const Shape * mShape; ///< Shape that's being cast (cannot be mesh shape). Note that this structure does not assume ownership over the shape for performance reasons. - const Vec3 mScale; ///< Scale in local space of the shape being cast - const Mat mCenterOfMassStart; ///< Start position and orientation of the center of mass of the shape (construct using sFromWorldTransform if you have a world transform for your shape) - const Vec3 mDirection; ///< Direction and length of the cast (anything beyond this length will not be reported as a hit) - const AABox mShapeWorldBounds; ///< Cached shape's world bounds, calculated in constructor -}; - -struct ShapeCast : public ShapeCastT -{ - using ShapeCastT::ShapeCastT; -}; - -struct RShapeCast : public ShapeCastT -{ - using ShapeCastT::ShapeCastT; - - /// Convert from ShapeCast, converts single to double precision - explicit RShapeCast(const ShapeCast &inCast) : - RShapeCast(inCast.mShape, inCast.mScale, RMat44(inCast.mCenterOfMassStart), inCast.mDirection, inCast.mShapeWorldBounds) - { - } - - /// Convert to ShapeCast, which implies casting from double precision to single precision - explicit operator ShapeCast() const - { - return ShapeCast(mShape, mScale, mCenterOfMassStart.ToMat44(), mDirection, mShapeWorldBounds); - } -}; - -/// Settings to be passed with a shape cast -class ShapeCastSettings : public CollideSettingsBase -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// How backfacing triangles should be treated (should we report moving out of a triangle?) - EBackFaceMode mBackFaceModeTriangles = EBackFaceMode::IgnoreBackFaces; - - /// How backfacing convex objects should be treated (should we report starting inside an object and moving out?) - EBackFaceMode mBackFaceModeConvex = EBackFaceMode::IgnoreBackFaces; - - /// Indicates if we want to shrink the shape by the convex radius and then expand it again. This speeds up collision detection and gives a more accurate normal at the cost of a more 'rounded' shape. - bool mUseShrunkenShapeAndConvexRadius = false; - - /// When true, and the shape is intersecting at the beginning of the cast (fraction = 0) then this will calculate the deepest penetration point (costing additional CPU time) - bool mReturnDeepestPoint = false; -}; - -/// Result of a shape cast test -class ShapeCastResult : public CollideShapeResult -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Default constructor - ShapeCastResult() = default; - - /// Constructor - /// @param inFraction Fraction at which the cast hit - /// @param inContactPoint1 Contact point on shape 1 - /// @param inContactPoint2 Contact point on shape 2 - /// @param inContactNormalOrPenetrationDepth Contact normal pointing from shape 1 to 2 or penetration depth vector when the objects are penetrating (also from 1 to 2) - /// @param inBackFaceHit If this hit was a back face hit - /// @param inSubShapeID1 Sub shape id for shape 1 - /// @param inSubShapeID2 Sub shape id for shape 2 - /// @param inBodyID2 BodyID that was hit - ShapeCastResult(float inFraction, Vec3Arg inContactPoint1, Vec3Arg inContactPoint2, Vec3Arg inContactNormalOrPenetrationDepth, bool inBackFaceHit, const SubShapeID &inSubShapeID1, const SubShapeID &inSubShapeID2, const BodyID &inBodyID2) : - CollideShapeResult(inContactPoint1, inContactPoint2, inContactNormalOrPenetrationDepth, (inContactPoint2 - inContactPoint1).Length(), inSubShapeID1, inSubShapeID2, inBodyID2), - mFraction(inFraction), - mIsBackFaceHit(inBackFaceHit) - { - } - - /// Function required by the CollisionCollector. A smaller fraction is considered to be a 'better hit'. For rays/cast shapes we can just use the collision fraction. The fraction and penetration depth are combined in such a way that deeper hits at fraction 0 go first. - inline float GetEarlyOutFraction() const { return mFraction > 0.0f? mFraction : -mPenetrationDepth; } - - /// Reverses the hit result, swapping contact point 1 with contact point 2 etc. - /// @param inWorldSpaceCastDirection Direction of the shape cast in world space - ShapeCastResult Reversed(Vec3Arg inWorldSpaceCastDirection) const - { - // Calculate by how much to shift the contact points - Vec3 delta = mFraction * inWorldSpaceCastDirection; - - ShapeCastResult result; - result.mContactPointOn2 = mContactPointOn1 - delta; - result.mContactPointOn1 = mContactPointOn2 - delta; - result.mPenetrationAxis = -mPenetrationAxis; - result.mPenetrationDepth = mPenetrationDepth; - result.mSubShapeID2 = mSubShapeID1; - result.mSubShapeID1 = mSubShapeID2; - result.mBodyID2 = mBodyID2; - result.mFraction = mFraction; - result.mIsBackFaceHit = mIsBackFaceHit; - - result.mShape2Face.resize(mShape1Face.size()); - for (Face::size_type i = 0; i < mShape1Face.size(); ++i) - result.mShape2Face[i] = mShape1Face[i] - delta; - - result.mShape1Face.resize(mShape2Face.size()); - for (Face::size_type i = 0; i < mShape2Face.size(); ++i) - result.mShape1Face[i] = mShape2Face[i] - delta; - - return result; - } - - float mFraction; ///< This is the fraction where the shape hit the other shape: CenterOfMassOnHit = Start + value * (End - Start) - bool mIsBackFaceHit; ///< True if the shape was hit from the back side -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ShapeFilter.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ShapeFilter.h deleted file mode 100644 index 0b5ebbefd3d..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/ShapeFilter.h +++ /dev/null @@ -1,72 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include - -JPH_NAMESPACE_BEGIN - -class Shape; -class SubShapeID; - -/// Filter class -class ShapeFilter : public NonCopyable -{ -public: - /// Destructor - virtual ~ShapeFilter() = default; - - /// Filter function to determine if we should collide with a shape. Returns true if the filter passes. - /// This overload is called when the query doesn't have a source shape (e.g. ray cast / collide point) - /// @param inShape2 Shape we're colliding against - /// @param inSubShapeIDOfShape2 The sub shape ID that will lead from the root shape to inShape2 (i.e. the shape of mBodyID2) - virtual bool ShouldCollide([[maybe_unused]] const Shape *inShape2, [[maybe_unused]] const SubShapeID &inSubShapeIDOfShape2) const - { - return true; - } - - /// Filter function to determine if two shapes should collide. Returns true if the filter passes. - /// This overload is called when querying a shape vs a shape (e.g. collide object / cast object). - /// It is called at each level of the shape hierarchy, so if you have a compound shape with a box, this function will be called twice. - /// It will not be called on triangles that are part of another shape, i.e a mesh shape will not trigger a callback per triangle. You can filter out individual triangles in the CollisionCollector::AddHit function by their sub shape ID. - /// @param inShape1 1st shape that is colliding - /// @param inSubShapeIDOfShape1 The sub shape ID that will lead from the root shape to inShape1 (i.e. the shape that is used to collide or cast against shape 2) - /// @param inShape2 2nd shape that is colliding - /// @param inSubShapeIDOfShape2 The sub shape ID that will lead from the root shape to inShape2 (i.e. the shape of mBodyID2) - virtual bool ShouldCollide([[maybe_unused]] const Shape *inShape1, [[maybe_unused]] const SubShapeID &inSubShapeIDOfShape1, [[maybe_unused]] const Shape *inShape2, [[maybe_unused]] const SubShapeID &inSubShapeIDOfShape2) const - { - return true; - } - - /// Set by the collision detection functions to the body ID of the body that we're colliding against before calling the ShouldCollide function - mutable BodyID mBodyID2; -}; - -/// Helper class to reverse the order of the shapes in the ShouldCollide function -class ReversedShapeFilter : public ShapeFilter -{ -public: - /// Constructor - explicit ReversedShapeFilter(const ShapeFilter &inFilter) : mFilter(inFilter) - { - mBodyID2 = inFilter.mBodyID2; - } - - virtual bool ShouldCollide(const Shape *inShape2, const SubShapeID &inSubShapeIDOfShape2) const override - { - return mFilter.ShouldCollide(inShape2, inSubShapeIDOfShape2); - } - - virtual bool ShouldCollide(const Shape *inShape1, const SubShapeID &inSubShapeIDOfShape1, const Shape *inShape2, const SubShapeID &inSubShapeIDOfShape2) const override - { - return mFilter.ShouldCollide(inShape2, inSubShapeIDOfShape2, inShape1, inSubShapeIDOfShape1); - } - -private: - const ShapeFilter & mFilter; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/SortReverseAndStore.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/SortReverseAndStore.h deleted file mode 100644 index 4a073096c26..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/SortReverseAndStore.h +++ /dev/null @@ -1,48 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -JPH_NAMESPACE_BEGIN - -/// This function will sort values from high to low and only keep the ones that are less than inMaxValue -/// @param inValues Values to be sorted -/// @param inMaxValue Values need to be less than this to keep them -/// @param ioIdentifiers 4 identifiers that will be sorted in the same way as the values -/// @param outValues The values are stored here from high to low -/// @return The number of values that were kept -JPH_INLINE int SortReverseAndStore(Vec4Arg inValues, float inMaxValue, UVec4 &ioIdentifiers, float *outValues) -{ - // Sort so that highest values are first (we want to first process closer hits and we process stack top to bottom) - Vec4 values = inValues; - Vec4::sSort4Reverse(values, ioIdentifiers); - - // Count how many results are less than the max value - UVec4 closer = Vec4::sLess(values, Vec4::sReplicate(inMaxValue)); - int num_results = closer.CountTrues(); - - // Shift the values so that only the ones that are less than max are kept - values = values.ReinterpretAsInt().ShiftComponents4Minus(num_results).ReinterpretAsFloat(); - ioIdentifiers = ioIdentifiers.ShiftComponents4Minus(num_results); - - // Store the values - values.StoreFloat4(reinterpret_cast(outValues)); - - return num_results; -} - -/// Shift the elements so that the identifiers that correspond with the trues in inValue come first -/// @param inValue Values to test for true or false -/// @param ioIdentifiers the identifiers that are shifted, on return they are shifted -/// @return The number of trues -JPH_INLINE int CountAndSortTrues(UVec4Arg inValue, UVec4 &ioIdentifiers) -{ - // Sort the hits - ioIdentifiers = UVec4::sSort4True(inValue, ioIdentifiers); - - // Return the amount of hits - return inValue.CountTrues(); -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/TransformedShape.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/TransformedShape.cpp deleted file mode 100644 index 470387b52a6..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/TransformedShape.cpp +++ /dev/null @@ -1,180 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -bool TransformedShape::CastRay(const RRayCast &inRay, RayCastResult &ioHit) const -{ - if (mShape != nullptr) - { - // Transform the ray to local space, note that this drops precision which is possible because we're in local space now - RayCast ray(inRay.Transformed(GetInverseCenterOfMassTransform())); - - // Scale the ray - Vec3 inv_scale = GetShapeScale().Reciprocal(); - ray.mOrigin *= inv_scale; - ray.mDirection *= inv_scale; - - // Cast the ray on the shape - SubShapeIDCreator sub_shape_id(mSubShapeIDCreator); - if (mShape->CastRay(ray, sub_shape_id, ioHit)) - { - // Set body ID on the hit result - ioHit.mBodyID = mBodyID; - - return true; - } - } - - return false; -} - -void TransformedShape::CastRay(const RRayCast &inRay, const RayCastSettings &inRayCastSettings, CastRayCollector &ioCollector, const ShapeFilter &inShapeFilter) const -{ - if (mShape != nullptr) - { - // Set the context on the collector and filter - ioCollector.SetContext(this); - inShapeFilter.mBodyID2 = mBodyID; - - // Transform the ray to local space, note that this drops precision which is possible because we're in local space now - RayCast ray(inRay.Transformed(GetInverseCenterOfMassTransform())); - - // Scale the ray - Vec3 inv_scale = GetShapeScale().Reciprocal(); - ray.mOrigin *= inv_scale; - ray.mDirection *= inv_scale; - - // Cast the ray on the shape - SubShapeIDCreator sub_shape_id(mSubShapeIDCreator); - mShape->CastRay(ray, inRayCastSettings, sub_shape_id, ioCollector, inShapeFilter); - } -} - -void TransformedShape::CollidePoint(RVec3Arg inPoint, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter) const -{ - if (mShape != nullptr) - { - // Set the context on the collector and filter - ioCollector.SetContext(this); - inShapeFilter.mBodyID2 = mBodyID; - - // Transform and scale the point to local space - Vec3 point = Vec3(GetInverseCenterOfMassTransform() * inPoint) / GetShapeScale(); - - // Do point collide on the shape - SubShapeIDCreator sub_shape_id(mSubShapeIDCreator); - mShape->CollidePoint(point, sub_shape_id, ioCollector, inShapeFilter); - } -} - -void TransformedShape::CollideShape(const Shape *inShape, Vec3Arg inShapeScale, RMat44Arg inCenterOfMassTransform, const CollideShapeSettings &inCollideShapeSettings, RVec3Arg inBaseOffset, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter) const -{ - if (mShape != nullptr) - { - // Set the context on the collector and filter - ioCollector.SetContext(this); - inShapeFilter.mBodyID2 = mBodyID; - - SubShapeIDCreator sub_shape_id1, sub_shape_id2(mSubShapeIDCreator); - Mat44 transform1 = inCenterOfMassTransform.PostTranslated(-inBaseOffset).ToMat44(); - Mat44 transform2 = GetCenterOfMassTransform().PostTranslated(-inBaseOffset).ToMat44(); - CollisionDispatch::sCollideShapeVsShape(inShape, mShape, inShapeScale, GetShapeScale(), transform1, transform2, sub_shape_id1, sub_shape_id2, inCollideShapeSettings, ioCollector, inShapeFilter); - } -} - -void TransformedShape::CastShape(const RShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, RVec3Arg inBaseOffset, CastShapeCollector &ioCollector, const ShapeFilter &inShapeFilter) const -{ - if (mShape != nullptr) - { - // Set the context on the collector and filter - ioCollector.SetContext(this); - inShapeFilter.mBodyID2 = mBodyID; - - // Get the shape cast relative to the base offset and convert it to floats - ShapeCast shape_cast(inShapeCast.PostTranslated(-inBaseOffset)); - - // Get center of mass of object we're casting against relative to the base offset and convert it to floats - Mat44 center_of_mass_transform2 = GetCenterOfMassTransform().PostTranslated(-inBaseOffset).ToMat44(); - - SubShapeIDCreator sub_shape_id1, sub_shape_id2(mSubShapeIDCreator); - CollisionDispatch::sCastShapeVsShapeWorldSpace(shape_cast, inShapeCastSettings, mShape, GetShapeScale(), inShapeFilter, center_of_mass_transform2, sub_shape_id1, sub_shape_id2, ioCollector); - } -} - -void TransformedShape::CollectTransformedShapes(const AABox &inBox, TransformedShapeCollector &ioCollector, const ShapeFilter &inShapeFilter) const -{ - if (mShape != nullptr) - { - struct MyCollector : public TransformedShapeCollector - { - MyCollector(TransformedShapeCollector &ioCollector, RVec3 inShapePositionCOM) : - TransformedShapeCollector(ioCollector), - mCollector(ioCollector), - mShapePositionCOM(inShapePositionCOM) - { - } - - virtual void AddHit(const TransformedShape &inResult) override - { - // Apply the center of mass offset - TransformedShape ts = inResult; - ts.mShapePositionCOM += mShapePositionCOM; - - // Pass hit on to child collector - mCollector.AddHit(ts); - - // Update early out fraction based on child collector - UpdateEarlyOutFraction(mCollector.GetEarlyOutFraction()); - } - - TransformedShapeCollector & mCollector; - RVec3 mShapePositionCOM; - }; - - // Set the context on the collector - ioCollector.SetContext(this); - - // Wrap the collector so we can add the center of mass precision, we do this to avoid losing precision because CollectTransformedShapes uses single precision floats - MyCollector collector(ioCollector, mShapePositionCOM); - - // Take box to local space for the shape - AABox box = inBox; - box.Translate(-mShapePositionCOM); - - mShape->CollectTransformedShapes(box, Vec3::sZero(), mShapeRotation, GetShapeScale(), mSubShapeIDCreator, collector, inShapeFilter); - } -} - -void TransformedShape::GetTrianglesStart(GetTrianglesContext &ioContext, const AABox &inBox, RVec3Arg inBaseOffset) const -{ - if (mShape != nullptr) - { - // Take box to local space for the shape - AABox box = inBox; - box.Translate(-inBaseOffset); - - mShape->GetTrianglesStart(ioContext, box, Vec3(mShapePositionCOM - inBaseOffset), mShapeRotation, GetShapeScale()); - } -} - -int TransformedShape::GetTrianglesNext(GetTrianglesContext &ioContext, int inMaxTrianglesRequested, Float3 *outTriangleVertices, const PhysicsMaterial **outMaterials) const -{ - if (mShape != nullptr) - return mShape->GetTrianglesNext(ioContext, inMaxTrianglesRequested, outTriangleVertices, outMaterials); - else - return 0; -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/TransformedShape.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/TransformedShape.h deleted file mode 100644 index 887a8ee4411..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Collision/TransformedShape.h +++ /dev/null @@ -1,194 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -struct RRayCast; -struct RShapeCast; -class CollideShapeSettings; -class RayCastResult; - -/// Temporary data structure that contains a shape and a transform. -/// This structure can be obtained from a body (e.g. after a broad phase query) under lock protection. -/// The lock can then be released and collision detection operations can be safely performed since -/// the class takes a reference on the shape and does not use anything from the body anymore. -class JPH_EXPORT TransformedShape -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Constructor - TransformedShape() = default; - TransformedShape(RVec3Arg inPositionCOM, QuatArg inRotation, const Shape *inShape, const BodyID &inBodyID, const SubShapeIDCreator &inSubShapeIDCreator = SubShapeIDCreator()) : mShapePositionCOM(inPositionCOM), mShapeRotation(inRotation), mShape(inShape), mBodyID(inBodyID), mSubShapeIDCreator(inSubShapeIDCreator) { } - - /// Cast a ray and find the closest hit. Returns true if it finds a hit. Hits further than ioHit.mFraction will not be considered and in this case ioHit will remain unmodified (and the function will return false). - /// Convex objects will be treated as solid (meaning if the ray starts inside, you'll get a hit fraction of 0) and back face hits are returned. - /// If you want the surface normal of the hit use GetWorldSpaceSurfaceNormal(ioHit.mSubShapeID2, inRay.GetPointOnRay(ioHit.mFraction)) on this object. - bool CastRay(const RRayCast &inRay, RayCastResult &ioHit) const; - - /// Cast a ray, allows collecting multiple hits. Note that this version is more flexible but also slightly slower than the CastRay function that returns only a single hit. - /// If you want the surface normal of the hit use GetWorldSpaceSurfaceNormal(collected sub shape ID, inRay.GetPointOnRay(collected fraction)) on this object. - void CastRay(const RRayCast &inRay, const RayCastSettings &inRayCastSettings, CastRayCollector &ioCollector, const ShapeFilter &inShapeFilter = { }) const; - - /// Check if inPoint is inside any shapes. For this tests all shapes are treated as if they were solid. - /// For a mesh shape, this test will only provide sensible information if the mesh is a closed manifold. - /// For each shape that collides, ioCollector will receive a hit - void CollidePoint(RVec3Arg inPoint, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter = { }) const; - - /// Collide a shape and report any hits to ioCollector - /// @param inShape Shape to test - /// @param inShapeScale Scale in local space of shape - /// @param inCenterOfMassTransform Center of mass transform for the shape - /// @param inCollideShapeSettings Settings - /// @param inBaseOffset All hit results will be returned relative to this offset, can be zero to get results in world position, but when you're testing far from the origin you get better precision by picking a position that's closer e.g. mShapePositionCOM since floats are most accurate near the origin - /// @param ioCollector Collector that receives the hits - /// @param inShapeFilter Filter that allows you to reject collisions - void CollideShape(const Shape *inShape, Vec3Arg inShapeScale, RMat44Arg inCenterOfMassTransform, const CollideShapeSettings &inCollideShapeSettings, RVec3Arg inBaseOffset, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter = { }) const; - - /// Cast a shape and report any hits to ioCollector - /// @param inShapeCast The shape cast and its position and direction - /// @param inShapeCastSettings Settings for the shape cast - /// @param inBaseOffset All hit results will be returned relative to this offset, can be zero to get results in world position, but when you're testing far from the origin you get better precision by picking a position that's closer e.g. mShapePositionCOM or inShapeCast.mCenterOfMassStart.GetTranslation() since floats are most accurate near the origin - /// @param ioCollector Collector that receives the hits - /// @param inShapeFilter Filter that allows you to reject collisions - void CastShape(const RShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, RVec3Arg inBaseOffset, CastShapeCollector &ioCollector, const ShapeFilter &inShapeFilter = { }) const; - - /// Collect the leaf transformed shapes of all leaf shapes of this shape - /// inBox is the world space axis aligned box which leaf shapes should collide with - void CollectTransformedShapes(const AABox &inBox, TransformedShapeCollector &ioCollector, const ShapeFilter &inShapeFilter = { }) const; - - /// Use the context from Shape - using GetTrianglesContext = Shape::GetTrianglesContext; - - /// To start iterating over triangles, call this function first. - /// To get the actual triangles call GetTrianglesNext. - /// @param ioContext A temporary buffer and should remain untouched until the last call to GetTrianglesNext. - /// @param inBox The world space bounding in which you want to get the triangles. - /// @param inBaseOffset All hit results will be returned relative to this offset, can be zero to get results in world position, but when you're testing far from the origin you get better precision by picking a position that's closer e.g. inBox.GetCenter() since floats are most accurate near the origin - void GetTrianglesStart(GetTrianglesContext &ioContext, const AABox &inBox, RVec3Arg inBaseOffset) const; - - /// Call this repeatedly to get all triangles in the box. - /// outTriangleVertices should be large enough to hold 3 * inMaxTriangleRequested entries - /// outMaterials (if it is not null) should contain inMaxTrianglesRequested entries - /// The function returns the amount of triangles that it found (which will be <= inMaxTrianglesRequested), or 0 if there are no more triangles. - /// Note that the function can return a value < inMaxTrianglesRequested and still have more triangles to process (triangles can be returned in blocks) - /// Note that the function may return triangles outside of the requested box, only coarse culling is performed on the returned triangles - int GetTrianglesNext(GetTrianglesContext &ioContext, int inMaxTrianglesRequested, Float3 *outTriangleVertices, const PhysicsMaterial **outMaterials = nullptr) const; - - /// Get/set the scale of the shape as a Vec3 - inline Vec3 GetShapeScale() const { return Vec3::sLoadFloat3Unsafe(mShapeScale); } - inline void SetShapeScale(Vec3Arg inScale) { inScale.StoreFloat3(&mShapeScale); } - - /// Calculates the transform for this shapes's center of mass (excluding scale) - inline RMat44 GetCenterOfMassTransform() const { return RMat44::sRotationTranslation(mShapeRotation, mShapePositionCOM); } - - /// Calculates the inverse of the transform for this shape's center of mass (excluding scale) - inline RMat44 GetInverseCenterOfMassTransform() const { return RMat44::sInverseRotationTranslation(mShapeRotation, mShapePositionCOM); } - - /// Sets the world transform (including scale) of this transformed shape (not from the center of mass but in the space the shape was created) - inline void SetWorldTransform(RVec3Arg inPosition, QuatArg inRotation, Vec3Arg inScale) - { - mShapePositionCOM = inPosition + inRotation * (inScale * mShape->GetCenterOfMass()); - mShapeRotation = inRotation; - SetShapeScale(inScale); - } - - /// Sets the world transform (including scale) of this transformed shape (not from the center of mass but in the space the shape was created) - inline void SetWorldTransform(RMat44Arg inTransform) - { - Vec3 scale; - RMat44 rot_trans = inTransform.Decompose(scale); - SetWorldTransform(rot_trans.GetTranslation(), rot_trans.GetQuaternion(), scale); - } - - /// Calculates the world transform including scale of this shape (not from the center of mass but in the space the shape was created) - inline RMat44 GetWorldTransform() const - { - RMat44 transform = RMat44::sRotation(mShapeRotation).PreScaled(GetShapeScale()); - transform.SetTranslation(mShapePositionCOM - transform.Multiply3x3(mShape->GetCenterOfMass())); - return transform; - } - - /// Get the world space bounding box for this transformed shape - AABox GetWorldSpaceBounds() const { return mShape != nullptr? mShape->GetWorldSpaceBounds(GetCenterOfMassTransform(), GetShapeScale()) : AABox(); } - - /// Make inSubShapeID relative to mShape. When mSubShapeIDCreator is not empty, this is needed in order to get the correct path to the sub shape. - inline SubShapeID MakeSubShapeIDRelativeToShape(const SubShapeID &inSubShapeID) const - { - // Take off the sub shape ID part that comes from mSubShapeIDCreator and validate that it is the same - SubShapeID sub_shape_id; - uint num_bits_written = mSubShapeIDCreator.GetNumBitsWritten(); - JPH_IF_ENABLE_ASSERTS(uint32 root_id =) inSubShapeID.PopID(num_bits_written, sub_shape_id); - JPH_ASSERT(root_id == (mSubShapeIDCreator.GetID().GetValue() & ((1 << num_bits_written) - 1))); - return sub_shape_id; - } - - /// Get surface normal of a particular sub shape and its world space surface position on this body. - /// Note: When you have a CollideShapeResult or ShapeCastResult you should use -mPenetrationAxis.Normalized() as contact normal as GetWorldSpaceSurfaceNormal will only return face normals (and not vertex or edge normals). - inline Vec3 GetWorldSpaceSurfaceNormal(const SubShapeID &inSubShapeID, RVec3Arg inPosition) const - { - RMat44 inv_com = GetInverseCenterOfMassTransform(); - Vec3 scale = GetShapeScale(); // See comment at ScaledShape::GetSurfaceNormal for the math behind the scaling of the normal - return inv_com.Multiply3x3Transposed(mShape->GetSurfaceNormal(MakeSubShapeIDRelativeToShape(inSubShapeID), Vec3(inv_com * inPosition) / scale) / scale).Normalized(); - } - - /// Get the vertices of the face that faces inDirection the most (includes any convex radius). Note that this function can only return faces of - /// convex shapes or triangles, which is why a sub shape ID to get to that leaf must be provided. - /// @param inSubShapeID Sub shape ID of target shape - /// @param inDirection Direction that the face should be facing (in world space) - /// @param inBaseOffset The vertices will be returned relative to this offset, can be zero to get results in world position, but when you're testing far from the origin you get better precision by picking a position that's closer e.g. mShapePositionCOM since floats are most accurate near the origin - /// @param outVertices Resulting face. Note the returned face can have a single point if the shape doesn't have polygons to return (e.g. because it's a sphere). The face will be returned in world space. - void GetSupportingFace(const SubShapeID &inSubShapeID, Vec3Arg inDirection, RVec3Arg inBaseOffset, Shape::SupportingFace &outVertices) const - { - Mat44 com = GetCenterOfMassTransform().PostTranslated(-inBaseOffset).ToMat44(); - mShape->GetSupportingFace(MakeSubShapeIDRelativeToShape(inSubShapeID), com.Multiply3x3Transposed(inDirection), GetShapeScale(), com, outVertices); - } - - /// Get material of a particular sub shape - inline const PhysicsMaterial *GetMaterial(const SubShapeID &inSubShapeID) const - { - return mShape->GetMaterial(MakeSubShapeIDRelativeToShape(inSubShapeID)); - } - - /// Get the user data of a particular sub shape - inline uint64 GetSubShapeUserData(const SubShapeID &inSubShapeID) const - { - return mShape->GetSubShapeUserData(MakeSubShapeIDRelativeToShape(inSubShapeID)); - } - - /// Get the direct child sub shape and its transform for a sub shape ID. - /// @param inSubShapeID Sub shape ID that indicates the path to the leaf shape - /// @param outRemainder The remainder of the sub shape ID after removing the sub shape - /// @return Direct child sub shape and its transform, note that the body ID and sub shape ID will be invalid - TransformedShape GetSubShapeTransformedShape(const SubShapeID &inSubShapeID, SubShapeID &outRemainder) const - { - TransformedShape ts = mShape->GetSubShapeTransformedShape(inSubShapeID, Vec3::sZero(), mShapeRotation, GetShapeScale(), outRemainder); - ts.mShapePositionCOM += mShapePositionCOM; - return ts; - } - - /// Helper function to return the body id from a transformed shape. If the transformed shape is null an invalid body ID will be returned. - inline static BodyID sGetBodyID(const TransformedShape *inTS) { return inTS != nullptr? inTS->mBodyID : BodyID(); } - - RVec3 mShapePositionCOM; ///< Center of mass world position of the shape - Quat mShapeRotation; ///< Rotation of the shape - RefConst mShape; ///< The shape itself - Float3 mShapeScale { 1, 1, 1 }; ///< Not stored as Vec3 to get a nicely packed structure - BodyID mBodyID; ///< Optional body ID from which this shape comes - SubShapeIDCreator mSubShapeIDCreator; ///< Optional sub shape ID creator for the shape (can be used when expanding compound shapes into multiple transformed shapes) -}; - -static_assert(JPH_CPU_ADDRESS_BITS != 64 || sizeof(TransformedShape) == JPH_IF_SINGLE_PRECISION_ELSE(64, 96), "Not properly packed"); -static_assert(alignof(TransformedShape) == JPH_RVECTOR_ALIGNMENT, "Not properly aligned"); - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/CalculateSolverSteps.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/CalculateSolverSteps.h deleted file mode 100644 index 857eddfb8e1..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/CalculateSolverSteps.h +++ /dev/null @@ -1,66 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2023 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -/// Class used to calculate the total number of velocity and position steps -class CalculateSolverSteps -{ -public: - /// Constructor - JPH_INLINE explicit CalculateSolverSteps(const PhysicsSettings &inSettings) : mSettings(inSettings) { } - - /// Combine the number of velocity and position steps for this body/constraint with the current values - template - JPH_INLINE void operator () (const Type *inObject) - { - uint num_velocity_steps = inObject->GetNumVelocityStepsOverride(); - mNumVelocitySteps = max(mNumVelocitySteps, num_velocity_steps); - mApplyDefaultVelocity |= num_velocity_steps == 0; - - uint num_position_steps = inObject->GetNumPositionStepsOverride(); - mNumPositionSteps = max(mNumPositionSteps, num_position_steps); - mApplyDefaultPosition |= num_position_steps == 0; - } - - /// Must be called after all bodies/constraints have been processed - JPH_INLINE void Finalize() - { - // If we have a default velocity/position step count, take the max of the default and the overrides - if (mApplyDefaultVelocity) - mNumVelocitySteps = max(mNumVelocitySteps, mSettings.mNumVelocitySteps); - if (mApplyDefaultPosition) - mNumPositionSteps = max(mNumPositionSteps, mSettings.mNumPositionSteps); - } - - /// Get the results of the calculation - JPH_INLINE uint GetNumPositionSteps() const { return mNumPositionSteps; } - JPH_INLINE uint GetNumVelocitySteps() const { return mNumVelocitySteps; } - -private: - const PhysicsSettings & mSettings; - - uint mNumVelocitySteps = 0; - uint mNumPositionSteps = 0; - - bool mApplyDefaultVelocity = false; - bool mApplyDefaultPosition = false; -}; - -/// Dummy class to replace the steps calculator when we don't need the result -class DummyCalculateSolverSteps -{ -public: - template - JPH_INLINE void operator () (const Type *) const - { - /* Nothing to do */ - } -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConeConstraint.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConeConstraint.cpp deleted file mode 100644 index 9889fa643de..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConeConstraint.cpp +++ /dev/null @@ -1,246 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include -#include -#include -#ifdef JPH_DEBUG_RENDERER - #include -#endif // JPH_DEBUG_RENDERER - -JPH_NAMESPACE_BEGIN - -JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(ConeConstraintSettings) -{ - JPH_ADD_BASE_CLASS(ConeConstraintSettings, TwoBodyConstraintSettings) - - JPH_ADD_ENUM_ATTRIBUTE(ConeConstraintSettings, mSpace) - JPH_ADD_ATTRIBUTE(ConeConstraintSettings, mPoint1) - JPH_ADD_ATTRIBUTE(ConeConstraintSettings, mTwistAxis1) - JPH_ADD_ATTRIBUTE(ConeConstraintSettings, mPoint2) - JPH_ADD_ATTRIBUTE(ConeConstraintSettings, mTwistAxis2) - JPH_ADD_ATTRIBUTE(ConeConstraintSettings, mHalfConeAngle) -} - -void ConeConstraintSettings::SaveBinaryState(StreamOut &inStream) const -{ - ConstraintSettings::SaveBinaryState(inStream); - - inStream.Write(mSpace); - inStream.Write(mPoint1); - inStream.Write(mTwistAxis1); - inStream.Write(mPoint2); - inStream.Write(mTwistAxis2); - inStream.Write(mHalfConeAngle); -} - -void ConeConstraintSettings::RestoreBinaryState(StreamIn &inStream) -{ - ConstraintSettings::RestoreBinaryState(inStream); - - inStream.Read(mSpace); - inStream.Read(mPoint1); - inStream.Read(mTwistAxis1); - inStream.Read(mPoint2); - inStream.Read(mTwistAxis2); - inStream.Read(mHalfConeAngle); -} - -TwoBodyConstraint *ConeConstraintSettings::Create(Body &inBody1, Body &inBody2) const -{ - return new ConeConstraint(inBody1, inBody2, *this); -} - -ConeConstraint::ConeConstraint(Body &inBody1, Body &inBody2, const ConeConstraintSettings &inSettings) : - TwoBodyConstraint(inBody1, inBody2, inSettings) -{ - // Store limits - SetHalfConeAngle(inSettings.mHalfConeAngle); - - // Initialize rotation axis to perpendicular of twist axis in case the angle between the twist axis is 0 in the first frame - mWorldSpaceRotationAxis = inSettings.mTwistAxis1.GetNormalizedPerpendicular(); - - if (inSettings.mSpace == EConstraintSpace::WorldSpace) - { - // If all properties were specified in world space, take them to local space now - RMat44 inv_transform1 = inBody1.GetInverseCenterOfMassTransform(); - mLocalSpacePosition1 = Vec3(inv_transform1 * inSettings.mPoint1); - mLocalSpaceTwistAxis1 = inv_transform1.Multiply3x3(inSettings.mTwistAxis1); - - RMat44 inv_transform2 = inBody2.GetInverseCenterOfMassTransform(); - mLocalSpacePosition2 = Vec3(inv_transform2 * inSettings.mPoint2); - mLocalSpaceTwistAxis2 = inv_transform2.Multiply3x3(inSettings.mTwistAxis2); - } - else - { - // Properties already in local space - mLocalSpacePosition1 = Vec3(inSettings.mPoint1); - mLocalSpacePosition2 = Vec3(inSettings.mPoint2); - mLocalSpaceTwistAxis1 = inSettings.mTwistAxis1; - mLocalSpaceTwistAxis2 = inSettings.mTwistAxis2; - - // If they were in local space, we need to take the initial rotation axis to world space - mWorldSpaceRotationAxis = inBody1.GetRotation() * mWorldSpaceRotationAxis; - } -} - -void ConeConstraint::NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM) -{ - if (mBody1->GetID() == inBodyID) - mLocalSpacePosition1 -= inDeltaCOM; - else if (mBody2->GetID() == inBodyID) - mLocalSpacePosition2 -= inDeltaCOM; -} - -void ConeConstraint::CalculateRotationConstraintProperties(Mat44Arg inRotation1, Mat44Arg inRotation2) -{ - // Rotation is along the cross product of both twist axis - Vec3 twist1 = inRotation1.Multiply3x3(mLocalSpaceTwistAxis1); - Vec3 twist2 = inRotation2.Multiply3x3(mLocalSpaceTwistAxis2); - - // Calculate dot product between twist axis, if it's smaller than the cone angle we need to correct - mCosTheta = twist1.Dot(twist2); - if (mCosTheta < mCosHalfConeAngle) - { - // Rotation axis is defined by the two twist axis - Vec3 rot_axis = twist2.Cross(twist1); - - // If we can't find a rotation axis because the twist is too small, we'll use last frame's rotation axis - float len = rot_axis.Length(); - if (len > 0.0f) - mWorldSpaceRotationAxis = rot_axis / len; - - mAngleConstraintPart.CalculateConstraintProperties(*mBody1, *mBody2, mWorldSpaceRotationAxis); - } - else - mAngleConstraintPart.Deactivate(); -} - -void ConeConstraint::SetupVelocityConstraint(float inDeltaTime) -{ - Mat44 rotation1 = Mat44::sRotation(mBody1->GetRotation()); - Mat44 rotation2 = Mat44::sRotation(mBody2->GetRotation()); - mPointConstraintPart.CalculateConstraintProperties(*mBody1, rotation1, mLocalSpacePosition1, *mBody2, rotation2, mLocalSpacePosition2); - CalculateRotationConstraintProperties(rotation1, rotation2); -} - -void ConeConstraint::ResetWarmStart() -{ - mPointConstraintPart.Deactivate(); - mAngleConstraintPart.Deactivate(); -} - -void ConeConstraint::WarmStartVelocityConstraint(float inWarmStartImpulseRatio) -{ - // Warm starting: Apply previous frame impulse - mPointConstraintPart.WarmStart(*mBody1, *mBody2, inWarmStartImpulseRatio); - mAngleConstraintPart.WarmStart(*mBody1, *mBody2, inWarmStartImpulseRatio); -} - -bool ConeConstraint::SolveVelocityConstraint(float inDeltaTime) -{ - bool pos = mPointConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2); - - bool rot = false; - if (mAngleConstraintPart.IsActive()) - rot = mAngleConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2, mWorldSpaceRotationAxis, 0, FLT_MAX); - - return pos || rot; -} - -bool ConeConstraint::SolvePositionConstraint(float inDeltaTime, float inBaumgarte) -{ - mPointConstraintPart.CalculateConstraintProperties(*mBody1, Mat44::sRotation(mBody1->GetRotation()), mLocalSpacePosition1, *mBody2, Mat44::sRotation(mBody2->GetRotation()), mLocalSpacePosition2); - bool pos = mPointConstraintPart.SolvePositionConstraint(*mBody1, *mBody2, inBaumgarte); - - bool rot = false; - CalculateRotationConstraintProperties(Mat44::sRotation(mBody1->GetRotation()), Mat44::sRotation(mBody2->GetRotation())); - if (mAngleConstraintPart.IsActive()) - rot = mAngleConstraintPart.SolvePositionConstraint(*mBody1, *mBody2, mCosTheta - mCosHalfConeAngle, inBaumgarte); - - return pos || rot; -} - -#ifdef JPH_DEBUG_RENDERER -void ConeConstraint::DrawConstraint(DebugRenderer *inRenderer) const -{ - RMat44 transform1 = mBody1->GetCenterOfMassTransform(); - RMat44 transform2 = mBody2->GetCenterOfMassTransform(); - - RVec3 p1 = transform1 * mLocalSpacePosition1; - RVec3 p2 = transform2 * mLocalSpacePosition2; - - // Draw constraint - inRenderer->DrawMarker(p1, Color::sRed, 0.1f); - inRenderer->DrawMarker(p2, Color::sGreen, 0.1f); - - // Draw twist axis - inRenderer->DrawLine(p1, p1 + mDrawConstraintSize * transform1.Multiply3x3(mLocalSpaceTwistAxis1), Color::sRed); - inRenderer->DrawLine(p2, p2 + mDrawConstraintSize * transform2.Multiply3x3(mLocalSpaceTwistAxis2), Color::sGreen); -} - -void ConeConstraint::DrawConstraintLimits(DebugRenderer *inRenderer) const -{ - // Get constraint properties in world space - RMat44 transform1 = mBody1->GetCenterOfMassTransform(); - RVec3 position1 = transform1 * mLocalSpacePosition1; - Vec3 twist_axis1 = transform1.Multiply3x3(mLocalSpaceTwistAxis1); - Vec3 normal_axis1 = transform1.Multiply3x3(mLocalSpaceTwistAxis1.GetNormalizedPerpendicular()); - - inRenderer->DrawOpenCone(position1, twist_axis1, normal_axis1, ACos(mCosHalfConeAngle), mDrawConstraintSize * mCosHalfConeAngle, Color::sPurple, DebugRenderer::ECastShadow::Off); -} -#endif // JPH_DEBUG_RENDERER - -void ConeConstraint::SaveState(StateRecorder &inStream) const -{ - TwoBodyConstraint::SaveState(inStream); - - mPointConstraintPart.SaveState(inStream); - mAngleConstraintPart.SaveState(inStream); - inStream.Write(mWorldSpaceRotationAxis); // When twist is too small, the rotation is used from last frame so we need to store it -} - -void ConeConstraint::RestoreState(StateRecorder &inStream) -{ - TwoBodyConstraint::RestoreState(inStream); - - mPointConstraintPart.RestoreState(inStream); - mAngleConstraintPart.RestoreState(inStream); - inStream.Read(mWorldSpaceRotationAxis); -} - -Ref ConeConstraint::GetConstraintSettings() const -{ - ConeConstraintSettings *settings = new ConeConstraintSettings; - ToConstraintSettings(*settings); - settings->mSpace = EConstraintSpace::LocalToBodyCOM; - settings->mPoint1 = RVec3(mLocalSpacePosition1); - settings->mTwistAxis1 = mLocalSpaceTwistAxis1; - settings->mPoint2 = RVec3(mLocalSpacePosition2); - settings->mTwistAxis2 = mLocalSpaceTwistAxis2; - settings->mHalfConeAngle = ACos(mCosHalfConeAngle); - return settings; -} - -Mat44 ConeConstraint::GetConstraintToBody1Matrix() const -{ - Vec3 perp = mLocalSpaceTwistAxis1.GetNormalizedPerpendicular(); - Vec3 perp2 = mLocalSpaceTwistAxis1.Cross(perp); - return Mat44(Vec4(mLocalSpaceTwistAxis1, 0), Vec4(perp, 0), Vec4(perp2, 0), Vec4(mLocalSpacePosition1, 1)); -} - -Mat44 ConeConstraint::GetConstraintToBody2Matrix() const -{ - // Note: Incorrect in rotation around the twist axis (the perpendicular does not match that of body 1), - // this should not matter as we're not limiting rotation around the twist axis. - Vec3 perp = mLocalSpaceTwistAxis2.GetNormalizedPerpendicular(); - Vec3 perp2 = mLocalSpaceTwistAxis2.Cross(perp); - return Mat44(Vec4(mLocalSpaceTwistAxis2, 0), Vec4(perp, 0), Vec4(perp2, 0), Vec4(mLocalSpacePosition2, 1)); -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConeConstraint.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConeConstraint.h deleted file mode 100644 index 630d089dd1f..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConeConstraint.h +++ /dev/null @@ -1,133 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -/// Cone constraint settings, used to create a cone constraint -class JPH_EXPORT ConeConstraintSettings final : public TwoBodyConstraintSettings -{ -public: - JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, ConeConstraintSettings) - - // See: ConstraintSettings::SaveBinaryState - virtual void SaveBinaryState(StreamOut &inStream) const override; - - /// Create an instance of this constraint - virtual TwoBodyConstraint * Create(Body &inBody1, Body &inBody2) const override; - - /// This determines in which space the constraint is setup, all properties below should be in the specified space - EConstraintSpace mSpace = EConstraintSpace::WorldSpace; - - /// Body 1 constraint reference frame (space determined by mSpace) - RVec3 mPoint1 = RVec3::sZero(); - Vec3 mTwistAxis1 = Vec3::sAxisX(); - - /// Body 2 constraint reference frame (space determined by mSpace) - RVec3 mPoint2 = RVec3::sZero(); - Vec3 mTwistAxis2 = Vec3::sAxisX(); - - /// Half of maximum angle between twist axis of body 1 and 2 - float mHalfConeAngle = 0.0f; - -protected: - // See: ConstraintSettings::RestoreBinaryState - virtual void RestoreBinaryState(StreamIn &inStream) override; -}; - -/// A cone constraint constraints 2 bodies to a single point and limits the swing between the twist axis within a cone: -/// -/// t1 . t2 <= cos(theta) -/// -/// Where: -/// -/// t1 = twist axis of body 1. -/// t2 = twist axis of body 2. -/// theta = half cone angle (angle from the principal axis of the cone to the edge). -/// -/// Calculating the Jacobian: -/// -/// Constraint equation: -/// -/// C = t1 . t2 - cos(theta) -/// -/// Derivative: -/// -/// d/dt C = d/dt (t1 . t2) = (d/dt t1) . t2 + t1 . (d/dt t2) = (w1 x t1) . t2 + t1 . (w2 x t2) = (t1 x t2) . w1 + (t2 x t1) . w2 -/// -/// d/dt C = J v = [0, -t2 x t1, 0, t2 x t1] [v1, w1, v2, w2] -/// -/// Where J is the Jacobian. -/// -/// Note that this is the exact same equation as used in AngleConstraintPart if we use t2 x t1 as the world space axis -class JPH_EXPORT ConeConstraint final : public TwoBodyConstraint -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Construct cone constraint - ConeConstraint(Body &inBody1, Body &inBody2, const ConeConstraintSettings &inSettings); - - // Generic interface of a constraint - virtual EConstraintSubType GetSubType() const override { return EConstraintSubType::Cone; } - virtual void NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM) override; - virtual void SetupVelocityConstraint(float inDeltaTime) override; - virtual void ResetWarmStart() override; - virtual void WarmStartVelocityConstraint(float inWarmStartImpulseRatio) override; - virtual bool SolveVelocityConstraint(float inDeltaTime) override; - virtual bool SolvePositionConstraint(float inDeltaTime, float inBaumgarte) override; -#ifdef JPH_DEBUG_RENDERER - virtual void DrawConstraint(DebugRenderer *inRenderer) const override; - virtual void DrawConstraintLimits(DebugRenderer *inRenderer) const override; -#endif // JPH_DEBUG_RENDERER - virtual void SaveState(StateRecorder &inStream) const override; - virtual void RestoreState(StateRecorder &inStream) override; - virtual Ref GetConstraintSettings() const override; - - // See: TwoBodyConstraint - virtual Mat44 GetConstraintToBody1Matrix() const override; - virtual Mat44 GetConstraintToBody2Matrix() const override; - - /// Update maximum angle between body 1 and 2 (see ConeConstraintSettings) - void SetHalfConeAngle(float inHalfConeAngle) { JPH_ASSERT(inHalfConeAngle >= 0.0f && inHalfConeAngle <= JPH_PI); mCosHalfConeAngle = Cos(inHalfConeAngle); } - float GetCosHalfConeAngle() const { return mCosHalfConeAngle; } - - ///@name Get Lagrange multiplier from last physics update (the linear/angular impulse applied to satisfy the constraint) - inline Vec3 GetTotalLambdaPosition() const { return mPointConstraintPart.GetTotalLambda(); } - inline float GetTotalLambdaRotation() const { return mAngleConstraintPart.GetTotalLambda(); } - -private: - // Internal helper function to calculate the values below - void CalculateRotationConstraintProperties(Mat44Arg inRotation1, Mat44Arg inRotation2); - - // CONFIGURATION PROPERTIES FOLLOW - - // Local space constraint positions - Vec3 mLocalSpacePosition1; - Vec3 mLocalSpacePosition2; - - // Local space constraint axis - Vec3 mLocalSpaceTwistAxis1; - Vec3 mLocalSpaceTwistAxis2; - - // Angular limits - float mCosHalfConeAngle; - - // RUN TIME PROPERTIES FOLLOW - - // Axis and angle of rotation between the two bodies - Vec3 mWorldSpaceRotationAxis; - float mCosTheta; - - // The constraint parts - PointConstraintPart mPointConstraintPart; - AngleConstraintPart mAngleConstraintPart; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/Constraint.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/Constraint.cpp deleted file mode 100644 index b81a8d81a71..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/Constraint.cpp +++ /dev/null @@ -1,73 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(ConstraintSettings) -{ - JPH_ADD_BASE_CLASS(ConstraintSettings, SerializableObject) - - JPH_ADD_ATTRIBUTE(ConstraintSettings, mEnabled) - JPH_ADD_ATTRIBUTE(ConstraintSettings, mDrawConstraintSize) - JPH_ADD_ATTRIBUTE(ConstraintSettings, mConstraintPriority) - JPH_ADD_ATTRIBUTE(ConstraintSettings, mNumVelocityStepsOverride) - JPH_ADD_ATTRIBUTE(ConstraintSettings, mNumPositionStepsOverride) - JPH_ADD_ATTRIBUTE(ConstraintSettings, mUserData) -} - -void ConstraintSettings::SaveBinaryState(StreamOut &inStream) const -{ - inStream.Write(GetRTTI()->GetHash()); - inStream.Write(mEnabled); - inStream.Write(mDrawConstraintSize); - inStream.Write(mConstraintPriority); - inStream.Write(mNumVelocityStepsOverride); - inStream.Write(mNumPositionStepsOverride); -} - -void ConstraintSettings::RestoreBinaryState(StreamIn &inStream) -{ - // Type hash read by sRestoreFromBinaryState - inStream.Read(mEnabled); - inStream.Read(mDrawConstraintSize); - inStream.Read(mConstraintPriority); - inStream.Read(mNumVelocityStepsOverride); - inStream.Read(mNumPositionStepsOverride); -} - -ConstraintSettings::ConstraintResult ConstraintSettings::sRestoreFromBinaryState(StreamIn &inStream) -{ - return StreamUtils::RestoreObject(inStream, &ConstraintSettings::RestoreBinaryState); -} - -void Constraint::SaveState(StateRecorder &inStream) const -{ - inStream.Write(mEnabled); -} - -void Constraint::RestoreState(StateRecorder &inStream) -{ - inStream.Read(mEnabled); -} - -void Constraint::ToConstraintSettings(ConstraintSettings &outSettings) const -{ - outSettings.mEnabled = mEnabled; - outSettings.mConstraintPriority = mConstraintPriority; - outSettings.mNumVelocityStepsOverride = mNumVelocityStepsOverride; - outSettings.mNumPositionStepsOverride = mNumPositionStepsOverride; - outSettings.mUserData = mUserData; -#ifdef JPH_DEBUG_RENDERER - outSettings.mDrawConstraintSize = mDrawConstraintSize; -#endif // JPH_DEBUG_RENDERER -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/Constraint.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/Constraint.h deleted file mode 100644 index f8827cf68d7..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/Constraint.h +++ /dev/null @@ -1,238 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -class BodyID; -class IslandBuilder; -class LargeIslandSplitter; -class BodyManager; -class StateRecorder; -class StreamIn; -class StreamOut; -#ifdef JPH_DEBUG_RENDERER -class DebugRenderer; -#endif // JPH_DEBUG_RENDERER - -/// Enum to identify constraint type -enum class EConstraintType -{ - Constraint, - TwoBodyConstraint, -}; - -/// Enum to identify constraint sub type -enum class EConstraintSubType -{ - Fixed, - Point, - Hinge, - Slider, - Distance, - Cone, - SwingTwist, - SixDOF, - Path, - Vehicle, - RackAndPinion, - Gear, - Pulley, - - /// User defined constraint types start here - User1, - User2, - User3, - User4 -}; - -/// Certain constraints support setting them up in local or world space. This governs what is used. -enum class EConstraintSpace -{ - LocalToBodyCOM, ///< All constraint properties are specified in local space to center of mass of the bodies that are being constrained (so e.g. 'constraint position 1' will be local to body 1 COM, 'constraint position 2' will be local to body 2 COM). Note that this means you need to subtract Shape::GetCenterOfMass() from positions! - WorldSpace, ///< All constraint properties are specified in world space -}; - -/// Class used to store the configuration of a constraint. Allows run-time creation of constraints. -class JPH_EXPORT ConstraintSettings : public SerializableObject, public RefTarget -{ -public: - JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, ConstraintSettings) - - using ConstraintResult = Result>; - - /// Saves the contents of the constraint settings in binary form to inStream. - virtual void SaveBinaryState(StreamOut &inStream) const; - - /// Creates a constraint of the correct type and restores its contents from the binary stream inStream. - static ConstraintResult sRestoreFromBinaryState(StreamIn &inStream); - - /// If this constraint is enabled initially. Use Constraint::SetEnabled to toggle after creation. - bool mEnabled = true; - - /// Priority of the constraint when solving. Higher numbers have are more likely to be solved correctly. - /// Note that if you want a deterministic simulation and you cannot guarantee the order in which constraints are added/removed, you can make the priority for all constraints unique to get a deterministic ordering. - uint32 mConstraintPriority = 0; - - /// Used only when the constraint is active. Override for the number of solver velocity iterations to run, 0 means use the default in PhysicsSettings::mNumVelocitySteps. The number of iterations to use is the max of all contacts and constraints in the island. - uint mNumVelocityStepsOverride = 0; - - /// Used only when the constraint is active. Override for the number of solver position iterations to run, 0 means use the default in PhysicsSettings::mNumPositionSteps. The number of iterations to use is the max of all contacts and constraints in the island. - uint mNumPositionStepsOverride = 0; - - /// Size of constraint when drawing it through the debug renderer - float mDrawConstraintSize = 1.0f; - - /// User data value (can be used by application) - uint64 mUserData = 0; - -protected: - /// This function should not be called directly, it is used by sRestoreFromBinaryState. - virtual void RestoreBinaryState(StreamIn &inStream); -}; - -/// Base class for all physics constraints. A constraint removes one or more degrees of freedom for a rigid body. -class JPH_EXPORT Constraint : public RefTarget, public NonCopyable -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Constructor - explicit Constraint(const ConstraintSettings &inSettings) : -#ifdef JPH_DEBUG_RENDERER - mDrawConstraintSize(inSettings.mDrawConstraintSize), -#endif // JPH_DEBUG_RENDERER - mConstraintPriority(inSettings.mConstraintPriority), - mNumVelocityStepsOverride(uint8(inSettings.mNumVelocityStepsOverride)), - mNumPositionStepsOverride(uint8(inSettings.mNumPositionStepsOverride)), - mEnabled(inSettings.mEnabled), - mUserData(inSettings.mUserData) - { - JPH_ASSERT(inSettings.mNumVelocityStepsOverride < 256); - JPH_ASSERT(inSettings.mNumPositionStepsOverride < 256); - } - - /// Virtual destructor - virtual ~Constraint() = default; - - /// Get the type of a constraint - virtual EConstraintType GetType() const { return EConstraintType::Constraint; } - - /// Get the sub type of a constraint - virtual EConstraintSubType GetSubType() const = 0; - - /// Priority of the constraint when solving. Higher numbers have are more likely to be solved correctly. - /// Note that if you want a deterministic simulation and you cannot guarantee the order in which constraints are added/removed, you can make the priority for all constraints unique to get a deterministic ordering. - uint32 GetConstraintPriority() const { return mConstraintPriority; } - void SetConstraintPriority(uint32 inPriority) { mConstraintPriority = inPriority; } - - /// Used only when the constraint is active. Override for the number of solver velocity iterations to run, 0 means use the default in PhysicsSettings::mNumVelocitySteps. The number of iterations to use is the max of all contacts and constraints in the island. - void SetNumVelocityStepsOverride(uint inN) { JPH_ASSERT(inN < 256); mNumVelocityStepsOverride = uint8(inN); } - uint GetNumVelocityStepsOverride() const { return mNumVelocityStepsOverride; } - - /// Used only when the constraint is active. Override for the number of solver position iterations to run, 0 means use the default in PhysicsSettings::mNumPositionSteps. The number of iterations to use is the max of all contacts and constraints in the island. - void SetNumPositionStepsOverride(uint inN) { JPH_ASSERT(inN < 256); mNumPositionStepsOverride = uint8(inN); } - uint GetNumPositionStepsOverride() const { return mNumPositionStepsOverride; } - - /// Enable / disable this constraint. This can e.g. be used to implement a breakable constraint by detecting that the constraint impulse - /// (see e.g. PointConstraint::GetTotalLambdaPosition) went over a certain limit and then disabling the constraint. - /// Note that although a disabled constraint will not affect the simulation in any way anymore, it does incur some processing overhead. - /// Alternatively you can remove a constraint from the constraint manager (which may be more costly if you want to disable the constraint for a short while). - void SetEnabled(bool inEnabled) { mEnabled = inEnabled; } - - /// Test if a constraint is enabled. - bool GetEnabled() const { return mEnabled; } - - /// Access to the user data, can be used for anything by the application - uint64 GetUserData() const { return mUserData; } - void SetUserData(uint64 inUserData) { mUserData = inUserData; } - - /// Notify the constraint that the shape of a body has changed and that its center of mass has moved by inDeltaCOM. - /// Bodies don't know which constraints are connected to them so the user is responsible for notifying the relevant constraints when a body changes. - /// @param inBodyID ID of the body that has changed - /// @param inDeltaCOM The delta of the center of mass of the body (shape->GetCenterOfMass() - shape_before_change->GetCenterOfMass()) - virtual void NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM) = 0; - - /// Notify the system that the configuration of the bodies and/or constraint has changed enough so that the warm start impulses should not be applied the next frame. - /// You can use this function for example when repositioning a ragdoll through Ragdoll::SetPose in such a way that the orientation of the bodies completely changes so that - /// the previous frame impulses are no longer a good approximation of what the impulses will be in the next frame. Calling this function when there are no big changes - /// will result in the constraints being much 'softer' than usual so they are more easily violated (e.g. a long chain of bodies might sag a bit if you call this every frame). - virtual void ResetWarmStart() = 0; - - ///@name Solver interface - ///@{ - virtual bool IsActive() const { return mEnabled; } - virtual void SetupVelocityConstraint(float inDeltaTime) = 0; - virtual void WarmStartVelocityConstraint(float inWarmStartImpulseRatio) = 0; - virtual bool SolveVelocityConstraint(float inDeltaTime) = 0; - virtual bool SolvePositionConstraint(float inDeltaTime, float inBaumgarte) = 0; - ///@} - - /// Link bodies that are connected by this constraint in the island builder - virtual void BuildIslands(uint32 inConstraintIndex, IslandBuilder &ioBuilder, BodyManager &inBodyManager) = 0; - - /// Link bodies that are connected by this constraint in the same split. Returns the split index. - virtual uint BuildIslandSplits(LargeIslandSplitter &ioSplitter) const = 0; - -#ifdef JPH_DEBUG_RENDERER - // Drawing interface - virtual void DrawConstraint(DebugRenderer *inRenderer) const = 0; - virtual void DrawConstraintLimits([[maybe_unused]] DebugRenderer *inRenderer) const { } - virtual void DrawConstraintReferenceFrame([[maybe_unused]] DebugRenderer *inRenderer) const { } - - /// Size of constraint when drawing it through the debug renderer - float GetDrawConstraintSize() const { return mDrawConstraintSize; } - void SetDrawConstraintSize(float inSize) { mDrawConstraintSize = inSize; } -#endif // JPH_DEBUG_RENDERER - - /// Saving state for replay - virtual void SaveState(StateRecorder &inStream) const; - - /// Restoring state for replay - virtual void RestoreState(StateRecorder &inStream); - - /// Debug function to convert a constraint to its settings, note that this will not save to which bodies the constraint is connected to - virtual Ref GetConstraintSettings() const = 0; - -protected: - /// Helper function to copy settings back to constraint settings for this base class - void ToConstraintSettings(ConstraintSettings &outSettings) const; - -#ifdef JPH_DEBUG_RENDERER - /// Size of constraint when drawing it through the debug renderer - float mDrawConstraintSize; -#endif // JPH_DEBUG_RENDERER - -private: - friend class ConstraintManager; - - /// Index that indicates this constraint is not in the constraint manager - static constexpr uint32 cInvalidConstraintIndex = 0xffffffff; - - /// Index in the mConstraints list of the ConstraintManager for easy finding - uint32 mConstraintIndex = cInvalidConstraintIndex; - - /// Priority of the constraint when solving. Higher numbers have are more likely to be solved correctly. - uint32 mConstraintPriority = 0; - - /// Used only when the constraint is active. Override for the number of solver velocity iterations to run, 0 means use the default in PhysicsSettings::mNumVelocitySteps. The number of iterations to use is the max of all contacts and constraints in the island. - uint8 mNumVelocityStepsOverride = 0; - - /// Used only when the constraint is active. Override for the number of solver position iterations to run, 0 means use the default in PhysicsSettings::mNumPositionSteps. The number of iterations to use is the max of all contacts and constraints in the island. - uint8 mNumPositionStepsOverride = 0; - - /// If this constraint is currently enabled - bool mEnabled = true; - - /// User data value (can be used by application) - uint64 mUserData; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintManager.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintManager.cpp deleted file mode 100644 index 509bddbd972..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintManager.cpp +++ /dev/null @@ -1,289 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -void ConstraintManager::Add(Constraint **inConstraints, int inNumber) -{ - UniqueLock lock(mConstraintsMutex JPH_IF_ENABLE_ASSERTS(, mLockContext, EPhysicsLockTypes::ConstraintsList)); - - mConstraints.reserve(mConstraints.size() + inNumber); - - for (Constraint **c = inConstraints, **c_end = inConstraints + inNumber; c < c_end; ++c) - { - Constraint *constraint = *c; - - // Assume this constraint has not been added yet - JPH_ASSERT(constraint->mConstraintIndex == Constraint::cInvalidConstraintIndex); - - // Add to the list - constraint->mConstraintIndex = uint32(mConstraints.size()); - mConstraints.push_back(constraint); - } -} - -void ConstraintManager::Remove(Constraint **inConstraints, int inNumber) -{ - UniqueLock lock(mConstraintsMutex JPH_IF_ENABLE_ASSERTS(, mLockContext, EPhysicsLockTypes::ConstraintsList)); - - for (Constraint **c = inConstraints, **c_end = inConstraints + inNumber; c < c_end; ++c) - { - Constraint *constraint = *c; - - // Reset constraint index for this constraint - uint32 this_constraint_idx = constraint->mConstraintIndex; - constraint->mConstraintIndex = Constraint::cInvalidConstraintIndex; - JPH_ASSERT(this_constraint_idx != Constraint::cInvalidConstraintIndex); - - // Check if this constraint is somewhere in the middle of the constraints, in this case we need to move the last constraint to this position - uint32 last_constraint_idx = uint32(mConstraints.size() - 1); - if (this_constraint_idx < last_constraint_idx) - { - Constraint *last_constraint = mConstraints[last_constraint_idx]; - last_constraint->mConstraintIndex = this_constraint_idx; - mConstraints[this_constraint_idx] = last_constraint; - } - - // Pop last constraint - mConstraints.pop_back(); - } -} - -Constraints ConstraintManager::GetConstraints() const -{ - UniqueLock lock(mConstraintsMutex JPH_IF_ENABLE_ASSERTS(, mLockContext, EPhysicsLockTypes::ConstraintsList)); - - Constraints copy = mConstraints; - return copy; -} - -void ConstraintManager::GetActiveConstraints(uint32 inStartConstraintIdx, uint32 inEndConstraintIdx, Constraint **outActiveConstraints, uint32 &outNumActiveConstraints) const -{ - JPH_PROFILE_FUNCTION(); - - JPH_ASSERT(inEndConstraintIdx <= mConstraints.size()); - - uint32 num_active_constraints = 0; - for (uint32 constraint_idx = inStartConstraintIdx; constraint_idx < inEndConstraintIdx; ++constraint_idx) - { - Constraint *c = mConstraints[constraint_idx]; - JPH_ASSERT(c->mConstraintIndex == constraint_idx); - if (c->IsActive()) - { - *(outActiveConstraints++) = c; - num_active_constraints++; - } - } - - outNumActiveConstraints = num_active_constraints; -} - -void ConstraintManager::sBuildIslands(Constraint **inActiveConstraints, uint32 inNumActiveConstraints, IslandBuilder &ioBuilder, BodyManager &inBodyManager) -{ - JPH_PROFILE_FUNCTION(); - - for (uint32 constraint_idx = 0; constraint_idx < inNumActiveConstraints; ++constraint_idx) - { - Constraint *c = inActiveConstraints[constraint_idx]; - c->BuildIslands(constraint_idx, ioBuilder, inBodyManager); - } -} - -void ConstraintManager::sSortConstraints(Constraint **inActiveConstraints, uint32 *inConstraintIdxBegin, uint32 *inConstraintIdxEnd) -{ - JPH_PROFILE_FUNCTION(); - - QuickSort(inConstraintIdxBegin, inConstraintIdxEnd, [inActiveConstraints](uint32 inLHS, uint32 inRHS) { - const Constraint *lhs = inActiveConstraints[inLHS]; - const Constraint *rhs = inActiveConstraints[inRHS]; - - if (lhs->GetConstraintPriority() != rhs->GetConstraintPriority()) - return lhs->GetConstraintPriority() < rhs->GetConstraintPriority(); - - return lhs->mConstraintIndex < rhs->mConstraintIndex; - }); -} - -void ConstraintManager::sSetupVelocityConstraints(Constraint **inActiveConstraints, uint32 inNumActiveConstraints, float inDeltaTime) -{ - JPH_PROFILE_FUNCTION(); - - for (Constraint **c = inActiveConstraints, **c_end = inActiveConstraints + inNumActiveConstraints; c < c_end; ++c) - (*c)->SetupVelocityConstraint(inDeltaTime); -} - -template -void ConstraintManager::sWarmStartVelocityConstraints(Constraint **inActiveConstraints, const uint32 *inConstraintIdxBegin, const uint32 *inConstraintIdxEnd, float inWarmStartImpulseRatio, ConstraintCallback &ioCallback) -{ - JPH_PROFILE_FUNCTION(); - - for (const uint32 *constraint_idx = inConstraintIdxBegin; constraint_idx < inConstraintIdxEnd; ++constraint_idx) - { - Constraint *c = inActiveConstraints[*constraint_idx]; - ioCallback(c); - c->WarmStartVelocityConstraint(inWarmStartImpulseRatio); - } -} - -// Specialize for the two constraint callback types -template void ConstraintManager::sWarmStartVelocityConstraints(Constraint **inActiveConstraints, const uint32 *inConstraintIdxBegin, const uint32 *inConstraintIdxEnd, float inWarmStartImpulseRatio, CalculateSolverSteps &ioCallback); -template void ConstraintManager::sWarmStartVelocityConstraints(Constraint **inActiveConstraints, const uint32 *inConstraintIdxBegin, const uint32 *inConstraintIdxEnd, float inWarmStartImpulseRatio, DummyCalculateSolverSteps &ioCallback); - -bool ConstraintManager::sSolveVelocityConstraints(Constraint **inActiveConstraints, const uint32 *inConstraintIdxBegin, const uint32 *inConstraintIdxEnd, float inDeltaTime) -{ - JPH_PROFILE_FUNCTION(); - - bool any_impulse_applied = false; - - for (const uint32 *constraint_idx = inConstraintIdxBegin; constraint_idx < inConstraintIdxEnd; ++constraint_idx) - { - Constraint *c = inActiveConstraints[*constraint_idx]; - any_impulse_applied |= c->SolveVelocityConstraint(inDeltaTime); - } - - return any_impulse_applied; -} - -bool ConstraintManager::sSolvePositionConstraints(Constraint **inActiveConstraints, const uint32 *inConstraintIdxBegin, const uint32 *inConstraintIdxEnd, float inDeltaTime, float inBaumgarte) -{ - JPH_PROFILE_FUNCTION(); - - bool any_impulse_applied = false; - - for (const uint32 *constraint_idx = inConstraintIdxBegin; constraint_idx < inConstraintIdxEnd; ++constraint_idx) - { - Constraint *c = inActiveConstraints[*constraint_idx]; - any_impulse_applied |= c->SolvePositionConstraint(inDeltaTime, inBaumgarte); - } - - return any_impulse_applied; -} - -#ifdef JPH_DEBUG_RENDERER -void ConstraintManager::DrawConstraints(DebugRenderer *inRenderer) const -{ - JPH_PROFILE_FUNCTION(); - - UniqueLock lock(mConstraintsMutex JPH_IF_ENABLE_ASSERTS(, mLockContext, EPhysicsLockTypes::ConstraintsList)); - - for (const Ref &c : mConstraints) - c->DrawConstraint(inRenderer); -} - -void ConstraintManager::DrawConstraintLimits(DebugRenderer *inRenderer) const -{ - JPH_PROFILE_FUNCTION(); - - UniqueLock lock(mConstraintsMutex JPH_IF_ENABLE_ASSERTS(, mLockContext, EPhysicsLockTypes::ConstraintsList)); - - for (const Ref &c : mConstraints) - c->DrawConstraintLimits(inRenderer); -} - -void ConstraintManager::DrawConstraintReferenceFrame(DebugRenderer *inRenderer) const -{ - JPH_PROFILE_FUNCTION(); - - UniqueLock lock(mConstraintsMutex JPH_IF_ENABLE_ASSERTS(, mLockContext, EPhysicsLockTypes::ConstraintsList)); - - for (const Ref &c : mConstraints) - c->DrawConstraintReferenceFrame(inRenderer); -} -#endif // JPH_DEBUG_RENDERER - -void ConstraintManager::SaveState(StateRecorder &inStream, const StateRecorderFilter *inFilter) const -{ - UniqueLock lock(mConstraintsMutex JPH_IF_ENABLE_ASSERTS(, mLockContext, EPhysicsLockTypes::ConstraintsList)); - - // Write state of constraints - if (inFilter != nullptr) - { - // Determine which constraints to save - Array constraints; - constraints.reserve(mConstraints.size()); - for (const Ref &c : mConstraints) - if (inFilter->ShouldSaveConstraint(*c)) - constraints.push_back(c); - - // Save them - uint32 num_constraints = (uint32)constraints.size(); - inStream.Write(num_constraints); - for (const Constraint *c : constraints) - { - inStream.Write(c->mConstraintIndex); - c->SaveState(inStream); - } - } - else - { - // Save all constraints - uint32 num_constraints = (uint32)mConstraints.size(); - inStream.Write(num_constraints); - for (const Ref &c : mConstraints) - { - inStream.Write(c->mConstraintIndex); - c->SaveState(inStream); - } - } -} - -bool ConstraintManager::RestoreState(StateRecorder &inStream) -{ - UniqueLock lock(mConstraintsMutex JPH_IF_ENABLE_ASSERTS(, mLockContext, EPhysicsLockTypes::ConstraintsList)); - - if (inStream.IsValidating()) - { - // Read state of constraints - uint32 num_constraints = (uint32)mConstraints.size(); // Initialize to current value for validation - inStream.Read(num_constraints); - if (num_constraints != mConstraints.size()) - { - JPH_ASSERT(false, "Cannot handle adding/removing constraints"); - return false; - } - for (const Ref &c : mConstraints) - { - uint32 constraint_index = c->mConstraintIndex; - inStream.Read(constraint_index); - if (constraint_index != c->mConstraintIndex) - { - JPH_ASSERT(false, "Unexpected constraint index"); - return false; - } - c->RestoreState(inStream); - } - } - else - { - // Not validating, use more flexible reading, read number of constraints - uint32 num_constraints = 0; - inStream.Read(num_constraints); - - for (uint32 idx = 0; idx < num_constraints; ++idx) - { - uint32 constraint_index; - inStream.Read(constraint_index); - if (mConstraints.size() <= constraint_index) - { - JPH_ASSERT(false, "Restoring state for non-existing constraint"); - return false; - } - mConstraints[constraint_index]->RestoreState(inStream); - } - } - - return true; -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintManager.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintManager.h deleted file mode 100644 index 7be8b06926c..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintManager.h +++ /dev/null @@ -1,99 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -class IslandBuilder; -class BodyManager; -class StateRecorderFilter; -#ifdef JPH_DEBUG_RENDERER -class DebugRenderer; -#endif // JPH_DEBUG_RENDERER - -/// A list of constraints -using Constraints = Array>; - -/// A constraint manager manages all constraints of the same type -class JPH_EXPORT ConstraintManager : public NonCopyable -{ -public: - JPH_OVERRIDE_NEW_DELETE - -#ifdef JPH_ENABLE_ASSERTS - /// Constructor - ConstraintManager(PhysicsLockContext inContext) : mLockContext(inContext) { } -#endif // JPH_ENABLE_ASSERTS - - /// Add a new constraint. This is thread safe. - /// Note that the inConstraints array is allowed to have nullptrs, these will be ignored. - void Add(Constraint **inConstraints, int inNumber); - - /// Remove a constraint. This is thread safe. - /// Note that the inConstraints array is allowed to have nullptrs, these will be ignored. - void Remove(Constraint **inConstraint, int inNumber); - - /// Get a list of all constraints - Constraints GetConstraints() const; - - /// Get total number of constraints - inline uint32 GetNumConstraints() const { return uint32(mConstraints.size()); } - - /// Determine the active constraints of a subset of the constraints - void GetActiveConstraints(uint32 inStartConstraintIdx, uint32 inEndConstraintIdx, Constraint **outActiveConstraints, uint32 &outNumActiveConstraints) const; - - /// Link bodies to form islands - static void sBuildIslands(Constraint **inActiveConstraints, uint32 inNumActiveConstraints, IslandBuilder &ioBuilder, BodyManager &inBodyManager); - - /// In order to have a deterministic simulation, we need to sort the constraints of an island before solving them - static void sSortConstraints(Constraint **inActiveConstraints, uint32 *inConstraintIdxBegin, uint32 *inConstraintIdxEnd); - - /// Prior to solving the velocity constraints, you must call SetupVelocityConstraints once to precalculate values that are independent of velocity - static void sSetupVelocityConstraints(Constraint **inActiveConstraints, uint32 inNumActiveConstraints, float inDeltaTime); - - /// Apply last frame's impulses, must be called prior to SolveVelocityConstraints - template - static void sWarmStartVelocityConstraints(Constraint **inActiveConstraints, const uint32 *inConstraintIdxBegin, const uint32 *inConstraintIdxEnd, float inWarmStartImpulseRatio, ConstraintCallback &ioCallback); - - /// This function is called multiple times to iteratively come to a solution that meets all velocity constraints - static bool sSolveVelocityConstraints(Constraint **inActiveConstraints, const uint32 *inConstraintIdxBegin, const uint32 *inConstraintIdxEnd, float inDeltaTime); - - /// This function is called multiple times to iteratively come to a solution that meets all position constraints - static bool sSolvePositionConstraints(Constraint **inActiveConstraints, const uint32 *inConstraintIdxBegin, const uint32 *inConstraintIdxEnd, float inDeltaTime, float inBaumgarte); - -#ifdef JPH_DEBUG_RENDERER - /// Draw all constraints - void DrawConstraints(DebugRenderer *inRenderer) const; - - /// Draw all constraint limits - void DrawConstraintLimits(DebugRenderer *inRenderer) const; - - /// Draw all constraint reference frames - void DrawConstraintReferenceFrame(DebugRenderer *inRenderer) const; -#endif // JPH_DEBUG_RENDERER - - /// Save state of constraints - void SaveState(StateRecorder &inStream, const StateRecorderFilter *inFilter) const; - - /// Restore the state of constraints. Returns false if failed. - bool RestoreState(StateRecorder &inStream); - - /// Lock all constraints. This should only be done during PhysicsSystem::Update(). - void LockAllConstraints() { PhysicsLock::sLock(mConstraintsMutex JPH_IF_ENABLE_ASSERTS(, mLockContext, EPhysicsLockTypes::ConstraintsList)); } - void UnlockAllConstraints() { PhysicsLock::sUnlock(mConstraintsMutex JPH_IF_ENABLE_ASSERTS(, mLockContext, EPhysicsLockTypes::ConstraintsList)); } - -private: -#ifdef JPH_ENABLE_ASSERTS - PhysicsLockContext mLockContext; -#endif // JPH_ENABLE_ASSERTS - Constraints mConstraints; - mutable Mutex mConstraintsMutex; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/AngleConstraintPart.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/AngleConstraintPart.h deleted file mode 100644 index f7493102e2a..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/AngleConstraintPart.h +++ /dev/null @@ -1,257 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -/// Constraint that constrains rotation along 1 axis -/// -/// Based on: "Constraints Derivation for Rigid Body Simulation in 3D" - Daniel Chappuis, see section 2.4.5 -/// -/// Constraint equation (eq 108): -/// -/// \f[C = \theta(t) - \theta_{min}\f] -/// -/// Jacobian (eq 109): -/// -/// \f[J = \begin{bmatrix}0 & -a^T & 0 & a^T\end{bmatrix}\f] -/// -/// Used terms (here and below, everything in world space):\n -/// a = axis around which rotation is constrained (normalized).\n -/// x1, x2 = center of mass for the bodies.\n -/// v = [v1, w1, v2, w2].\n -/// v1, v2 = linear velocity of body 1 and 2.\n -/// w1, w2 = angular velocity of body 1 and 2.\n -/// M = mass matrix, a diagonal matrix of the mass and inertia with diagonal [m1, I1, m2, I2].\n -/// \f$K^{-1} = \left( J M^{-1} J^T \right)^{-1}\f$ = effective mass.\n -/// b = velocity bias.\n -/// \f$\beta\f$ = baumgarte constant. -class AngleConstraintPart -{ - /// Internal helper function to update velocities of bodies after Lagrange multiplier is calculated - JPH_INLINE bool ApplyVelocityStep(Body &ioBody1, Body &ioBody2, float inLambda) const - { - // Apply impulse if delta is not zero - if (inLambda != 0.0f) - { - // Calculate velocity change due to constraint - // - // Impulse: - // P = J^T lambda - // - // Euler velocity integration: - // v' = v + M^-1 P - if (ioBody1.IsDynamic()) - ioBody1.GetMotionProperties()->SubAngularVelocityStep(inLambda * mInvI1_Axis); - if (ioBody2.IsDynamic()) - ioBody2.GetMotionProperties()->AddAngularVelocityStep(inLambda * mInvI2_Axis); - return true; - } - - return false; - } - - /// Internal helper function to calculate the inverse effective mass - JPH_INLINE float CalculateInverseEffectiveMass(const Body &inBody1, const Body &inBody2, Vec3Arg inWorldSpaceAxis) - { - JPH_ASSERT(inWorldSpaceAxis.IsNormalized(1.0e-4f)); - - // Calculate properties used below - mInvI1_Axis = inBody1.IsDynamic()? inBody1.GetMotionProperties()->MultiplyWorldSpaceInverseInertiaByVector(inBody1.GetRotation(), inWorldSpaceAxis) : Vec3::sZero(); - mInvI2_Axis = inBody2.IsDynamic()? inBody2.GetMotionProperties()->MultiplyWorldSpaceInverseInertiaByVector(inBody2.GetRotation(), inWorldSpaceAxis) : Vec3::sZero(); - - // Calculate inverse effective mass: K = J M^-1 J^T - return inWorldSpaceAxis.Dot(mInvI1_Axis + mInvI2_Axis); - } - -public: - /// Calculate properties used during the functions below - /// @param inBody1 The first body that this constraint is attached to - /// @param inBody2 The second body that this constraint is attached to - /// @param inWorldSpaceAxis The axis of rotation along which the constraint acts (normalized) - /// Set the following terms to zero if you don't want to drive the constraint to zero with a spring: - /// @param inBias Bias term (b) for the constraint impulse: lambda = J v + b - inline void CalculateConstraintProperties(const Body &inBody1, const Body &inBody2, Vec3Arg inWorldSpaceAxis, float inBias = 0.0f) - { - float inv_effective_mass = CalculateInverseEffectiveMass(inBody1, inBody2, inWorldSpaceAxis); - - if (inv_effective_mass == 0.0f) - Deactivate(); - else - { - mEffectiveMass = 1.0f / inv_effective_mass; - mSpringPart.CalculateSpringPropertiesWithBias(inBias); - } - } - - /// Calculate properties used during the functions below - /// @param inDeltaTime Time step - /// @param inBody1 The first body that this constraint is attached to - /// @param inBody2 The second body that this constraint is attached to - /// @param inWorldSpaceAxis The axis of rotation along which the constraint acts (normalized) - /// Set the following terms to zero if you don't want to drive the constraint to zero with a spring: - /// @param inBias Bias term (b) for the constraint impulse: lambda = J v + b - /// @param inC Value of the constraint equation (C) - /// @param inFrequency Oscillation frequency (Hz) - /// @param inDamping Damping factor (0 = no damping, 1 = critical damping) - inline void CalculateConstraintPropertiesWithFrequencyAndDamping(float inDeltaTime, const Body &inBody1, const Body &inBody2, Vec3Arg inWorldSpaceAxis, float inBias, float inC, float inFrequency, float inDamping) - { - float inv_effective_mass = CalculateInverseEffectiveMass(inBody1, inBody2, inWorldSpaceAxis); - - if (inv_effective_mass == 0.0f) - Deactivate(); - else - mSpringPart.CalculateSpringPropertiesWithFrequencyAndDamping(inDeltaTime, inv_effective_mass, inBias, inC, inFrequency, inDamping, mEffectiveMass); - } - - /// Calculate properties used during the functions below - /// @param inDeltaTime Time step - /// @param inBody1 The first body that this constraint is attached to - /// @param inBody2 The second body that this constraint is attached to - /// @param inWorldSpaceAxis The axis of rotation along which the constraint acts (normalized) - /// Set the following terms to zero if you don't want to drive the constraint to zero with a spring: - /// @param inBias Bias term (b) for the constraint impulse: lambda = J v + b - /// @param inC Value of the constraint equation (C) - /// @param inStiffness Spring stiffness k. - /// @param inDamping Spring damping coefficient c. - inline void CalculateConstraintPropertiesWithStiffnessAndDamping(float inDeltaTime, const Body &inBody1, const Body &inBody2, Vec3Arg inWorldSpaceAxis, float inBias, float inC, float inStiffness, float inDamping) - { - float inv_effective_mass = CalculateInverseEffectiveMass(inBody1, inBody2, inWorldSpaceAxis); - - if (inv_effective_mass == 0.0f) - Deactivate(); - else - mSpringPart.CalculateSpringPropertiesWithStiffnessAndDamping(inDeltaTime, inv_effective_mass, inBias, inC, inStiffness, inDamping, mEffectiveMass); - } - - /// Selects one of the above functions based on the spring settings - inline void CalculateConstraintPropertiesWithSettings(float inDeltaTime, const Body &inBody1, const Body &inBody2, Vec3Arg inWorldSpaceAxis, float inBias, float inC, const SpringSettings &inSpringSettings) - { - float inv_effective_mass = CalculateInverseEffectiveMass(inBody1, inBody2, inWorldSpaceAxis); - - if (inv_effective_mass == 0.0f) - Deactivate(); - else if (inSpringSettings.mMode == ESpringMode::FrequencyAndDamping) - mSpringPart.CalculateSpringPropertiesWithFrequencyAndDamping(inDeltaTime, inv_effective_mass, inBias, inC, inSpringSettings.mFrequency, inSpringSettings.mDamping, mEffectiveMass); - else - mSpringPart.CalculateSpringPropertiesWithStiffnessAndDamping(inDeltaTime, inv_effective_mass, inBias, inC, inSpringSettings.mStiffness, inSpringSettings.mDamping, mEffectiveMass); - } - - /// Deactivate this constraint - inline void Deactivate() - { - mEffectiveMass = 0.0f; - mTotalLambda = 0.0f; - } - - /// Check if constraint is active - inline bool IsActive() const - { - return mEffectiveMass != 0.0f; - } - - /// Must be called from the WarmStartVelocityConstraint call to apply the previous frame's impulses - /// @param ioBody1 The first body that this constraint is attached to - /// @param ioBody2 The second body that this constraint is attached to - /// @param inWarmStartImpulseRatio Ratio of new step to old time step (dt_new / dt_old) for scaling the lagrange multiplier of the previous frame - inline void WarmStart(Body &ioBody1, Body &ioBody2, float inWarmStartImpulseRatio) - { - mTotalLambda *= inWarmStartImpulseRatio; - ApplyVelocityStep(ioBody1, ioBody2, mTotalLambda); - } - - /// Iteratively update the velocity constraint. Makes sure d/dt C(...) = 0, where C is the constraint equation. - /// @param ioBody1 The first body that this constraint is attached to - /// @param ioBody2 The second body that this constraint is attached to - /// @param inWorldSpaceAxis The axis of rotation along which the constraint acts (normalized) - /// @param inMinLambda Minimum angular impulse to apply (N m s) - /// @param inMaxLambda Maximum angular impulse to apply (N m s) - inline bool SolveVelocityConstraint(Body &ioBody1, Body &ioBody2, Vec3Arg inWorldSpaceAxis, float inMinLambda, float inMaxLambda) - { - // Lagrange multiplier is: - // - // lambda = -K^-1 (J v + b) - float lambda = mEffectiveMass * (inWorldSpaceAxis.Dot(ioBody1.GetAngularVelocity() - ioBody2.GetAngularVelocity()) - mSpringPart.GetBias(mTotalLambda)); - float new_lambda = Clamp(mTotalLambda + lambda, inMinLambda, inMaxLambda); // Clamp impulse - lambda = new_lambda - mTotalLambda; // Lambda potentially got clamped, calculate the new impulse to apply - mTotalLambda = new_lambda; // Store accumulated impulse - - return ApplyVelocityStep(ioBody1, ioBody2, lambda); - } - - /// Return lagrange multiplier - float GetTotalLambda() const - { - return mTotalLambda; - } - - /// Iteratively update the position constraint. Makes sure C(...) == 0. - /// @param ioBody1 The first body that this constraint is attached to - /// @param ioBody2 The second body that this constraint is attached to - /// @param inC Value of the constraint equation (C) - /// @param inBaumgarte Baumgarte constant (fraction of the error to correct) - inline bool SolvePositionConstraint(Body &ioBody1, Body &ioBody2, float inC, float inBaumgarte) const - { - // Only apply position constraint when the constraint is hard, otherwise the velocity bias will fix the constraint - if (inC != 0.0f && !mSpringPart.IsActive()) - { - // Calculate lagrange multiplier (lambda) for Baumgarte stabilization: - // - // lambda = -K^-1 * beta / dt * C - // - // We should divide by inDeltaTime, but we should multiply by inDeltaTime in the Euler step below so they're cancelled out - float lambda = -mEffectiveMass * inBaumgarte * inC; - - // Directly integrate velocity change for one time step - // - // Euler velocity integration: - // dv = M^-1 P - // - // Impulse: - // P = J^T lambda - // - // Euler position integration: - // x' = x + dv * dt - // - // Note we don't accumulate velocities for the stabilization. This is using the approach described in 'Modeling and - // Solving Constraints' by Erin Catto presented at GDC 2007. On slide 78 it is suggested to split up the Baumgarte - // stabilization for positional drift so that it does not actually add to the momentum. We combine an Euler velocity - // integrate + a position integrate and then discard the velocity change. - if (ioBody1.IsDynamic()) - ioBody1.SubRotationStep(lambda * mInvI1_Axis); - if (ioBody2.IsDynamic()) - ioBody2.AddRotationStep(lambda * mInvI2_Axis); - return true; - } - - return false; - } - - /// Save state of this constraint part - void SaveState(StateRecorder &inStream) const - { - inStream.Write(mTotalLambda); - } - - /// Restore state of this constraint part - void RestoreState(StateRecorder &inStream) - { - inStream.Read(mTotalLambda); - } - -private: - Vec3 mInvI1_Axis; - Vec3 mInvI2_Axis; - float mEffectiveMass = 0.0f; - SpringPart mSpringPart; - float mTotalLambda = 0.0f; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/AxisConstraintPart.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/AxisConstraintPart.h deleted file mode 100644 index 541050b91b9..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/AxisConstraintPart.h +++ /dev/null @@ -1,682 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -/// Constraint that constrains motion along 1 axis -/// -/// @see "Constraints Derivation for Rigid Body Simulation in 3D" - Daniel Chappuis, section 2.1.1 -/// (we're not using the approximation of eq 27 but instead add the U term as in eq 55) -/// -/// Constraint equation (eq 25): -/// -/// \f[C = (p_2 - p_1) \cdot n\f] -/// -/// Jacobian (eq 28): -/// -/// \f[J = \begin{bmatrix} -n^T & (-(r_1 + u) \times n)^T & n^T & (r_2 \times n)^T \end{bmatrix}\f] -/// -/// Used terms (here and below, everything in world space):\n -/// n = constraint axis (normalized).\n -/// p1, p2 = constraint points.\n -/// r1 = p1 - x1.\n -/// r2 = p2 - x2.\n -/// u = x2 + r2 - x1 - r1 = p2 - p1.\n -/// x1, x2 = center of mass for the bodies.\n -/// v = [v1, w1, v2, w2].\n -/// v1, v2 = linear velocity of body 1 and 2.\n -/// w1, w2 = angular velocity of body 1 and 2.\n -/// M = mass matrix, a diagonal matrix of the mass and inertia with diagonal [m1, I1, m2, I2].\n -/// \f$K^{-1} = \left( J M^{-1} J^T \right)^{-1}\f$ = effective mass.\n -/// b = velocity bias.\n -/// \f$\beta\f$ = baumgarte constant. -class AxisConstraintPart -{ - /// Internal helper function to update velocities of bodies after Lagrange multiplier is calculated - template - JPH_INLINE bool ApplyVelocityStep(MotionProperties *ioMotionProperties1, float inInvMass1, MotionProperties *ioMotionProperties2, float inInvMass2, Vec3Arg inWorldSpaceAxis, float inLambda) const - { - // Apply impulse if delta is not zero - if (inLambda != 0.0f) - { - // Calculate velocity change due to constraint - // - // Impulse: - // P = J^T lambda - // - // Euler velocity integration: - // v' = v + M^-1 P - if constexpr (Type1 == EMotionType::Dynamic) - { - ioMotionProperties1->SubLinearVelocityStep((inLambda * inInvMass1) * inWorldSpaceAxis); - ioMotionProperties1->SubAngularVelocityStep(inLambda * Vec3::sLoadFloat3Unsafe(mInvI1_R1PlusUxAxis)); - } - if constexpr (Type2 == EMotionType::Dynamic) - { - ioMotionProperties2->AddLinearVelocityStep((inLambda * inInvMass2) * inWorldSpaceAxis); - ioMotionProperties2->AddAngularVelocityStep(inLambda * Vec3::sLoadFloat3Unsafe(mInvI2_R2xAxis)); - } - return true; - } - - return false; - } - - /// Internal helper function to calculate the inverse effective mass - template - JPH_INLINE float TemplatedCalculateInverseEffectiveMass(float inInvMass1, Mat44Arg inInvI1, Vec3Arg inR1PlusU, float inInvMass2, Mat44Arg inInvI2, Vec3Arg inR2, Vec3Arg inWorldSpaceAxis) - { - JPH_ASSERT(inWorldSpaceAxis.IsNormalized(1.0e-5f)); - - // Calculate properties used below - Vec3 r1_plus_u_x_axis; - if constexpr (Type1 != EMotionType::Static) - { - r1_plus_u_x_axis = inR1PlusU.Cross(inWorldSpaceAxis); - r1_plus_u_x_axis.StoreFloat3(&mR1PlusUxAxis); - } - else - { - #ifdef _DEBUG - Vec3::sNaN().StoreFloat3(&mR1PlusUxAxis); - #endif - } - - Vec3 r2_x_axis; - if constexpr (Type2 != EMotionType::Static) - { - r2_x_axis = inR2.Cross(inWorldSpaceAxis); - r2_x_axis.StoreFloat3(&mR2xAxis); - } - else - { - #ifdef _DEBUG - Vec3::sNaN().StoreFloat3(&mR2xAxis); - #endif - } - - // Calculate inverse effective mass: K = J M^-1 J^T - float inv_effective_mass; - - if constexpr (Type1 == EMotionType::Dynamic) - { - Vec3 invi1_r1_plus_u_x_axis = inInvI1.Multiply3x3(r1_plus_u_x_axis); - invi1_r1_plus_u_x_axis.StoreFloat3(&mInvI1_R1PlusUxAxis); - inv_effective_mass = inInvMass1 + invi1_r1_plus_u_x_axis.Dot(r1_plus_u_x_axis); - } - else - { - (void)r1_plus_u_x_axis; // Fix compiler warning: Not using this (it's not calculated either) - JPH_IF_DEBUG(Vec3::sNaN().StoreFloat3(&mInvI1_R1PlusUxAxis);) - inv_effective_mass = 0.0f; - } - - if constexpr (Type2 == EMotionType::Dynamic) - { - Vec3 invi2_r2_x_axis = inInvI2.Multiply3x3(r2_x_axis); - invi2_r2_x_axis.StoreFloat3(&mInvI2_R2xAxis); - inv_effective_mass += inInvMass2 + invi2_r2_x_axis.Dot(r2_x_axis); - } - else - { - (void)r2_x_axis; // Fix compiler warning: Not using this (it's not calculated either) - JPH_IF_DEBUG(Vec3::sNaN().StoreFloat3(&mInvI2_R2xAxis);) - } - - return inv_effective_mass; - } - - /// Internal helper function to calculate the inverse effective mass - JPH_INLINE float CalculateInverseEffectiveMass(const Body &inBody1, Vec3Arg inR1PlusU, const Body &inBody2, Vec3Arg inR2, Vec3Arg inWorldSpaceAxis) - { - // Dispatch to the correct templated form - switch (inBody1.GetMotionType()) - { - case EMotionType::Dynamic: - { - const MotionProperties *mp1 = inBody1.GetMotionPropertiesUnchecked(); - float inv_m1 = mp1->GetInverseMass(); - Mat44 inv_i1 = inBody1.GetInverseInertia(); - switch (inBody2.GetMotionType()) - { - case EMotionType::Dynamic: - return TemplatedCalculateInverseEffectiveMass(inv_m1, inv_i1, inR1PlusU, inBody2.GetMotionPropertiesUnchecked()->GetInverseMass(), inBody2.GetInverseInertia(), inR2, inWorldSpaceAxis); - - case EMotionType::Kinematic: - return TemplatedCalculateInverseEffectiveMass(inv_m1, inv_i1, inR1PlusU, 0 /* Will not be used */, Mat44() /* Will not be used */, inR2, inWorldSpaceAxis); - - case EMotionType::Static: - return TemplatedCalculateInverseEffectiveMass(inv_m1, inv_i1, inR1PlusU, 0 /* Will not be used */, Mat44() /* Will not be used */, inR2, inWorldSpaceAxis); - - default: - break; - } - break; - } - - case EMotionType::Kinematic: - JPH_ASSERT(inBody2.IsDynamic()); - return TemplatedCalculateInverseEffectiveMass(0 /* Will not be used */, Mat44() /* Will not be used */, inR1PlusU, inBody2.GetMotionPropertiesUnchecked()->GetInverseMass(), inBody2.GetInverseInertia(), inR2, inWorldSpaceAxis); - - case EMotionType::Static: - JPH_ASSERT(inBody2.IsDynamic()); - return TemplatedCalculateInverseEffectiveMass(0 /* Will not be used */, Mat44() /* Will not be used */, inR1PlusU, inBody2.GetMotionPropertiesUnchecked()->GetInverseMass(), inBody2.GetInverseInertia(), inR2, inWorldSpaceAxis); - - default: - break; - } - - JPH_ASSERT(false); - return 0.0f; - } - - /// Internal helper function to calculate the inverse effective mass, version that supports mass scaling - JPH_INLINE float CalculateInverseEffectiveMassWithMassOverride(const Body &inBody1, float inInvMass1, float inInvInertiaScale1, Vec3Arg inR1PlusU, const Body &inBody2, float inInvMass2, float inInvInertiaScale2, Vec3Arg inR2, Vec3Arg inWorldSpaceAxis) - { - // Dispatch to the correct templated form - switch (inBody1.GetMotionType()) - { - case EMotionType::Dynamic: - { - Mat44 inv_i1 = inInvInertiaScale1 * inBody1.GetInverseInertia(); - switch (inBody2.GetMotionType()) - { - case EMotionType::Dynamic: - return TemplatedCalculateInverseEffectiveMass(inInvMass1, inv_i1, inR1PlusU, inInvMass2, inInvInertiaScale2 * inBody2.GetInverseInertia(), inR2, inWorldSpaceAxis); - - case EMotionType::Kinematic: - return TemplatedCalculateInverseEffectiveMass(inInvMass1, inv_i1, inR1PlusU, 0 /* Will not be used */, Mat44() /* Will not be used */, inR2, inWorldSpaceAxis); - - case EMotionType::Static: - return TemplatedCalculateInverseEffectiveMass(inInvMass1, inv_i1, inR1PlusU, 0 /* Will not be used */, Mat44() /* Will not be used */, inR2, inWorldSpaceAxis); - - default: - break; - } - break; - } - - case EMotionType::Kinematic: - JPH_ASSERT(inBody2.IsDynamic()); - return TemplatedCalculateInverseEffectiveMass(0 /* Will not be used */, Mat44() /* Will not be used */, inR1PlusU, inInvMass2, inInvInertiaScale2 * inBody2.GetInverseInertia(), inR2, inWorldSpaceAxis); - - case EMotionType::Static: - JPH_ASSERT(inBody2.IsDynamic()); - return TemplatedCalculateInverseEffectiveMass(0 /* Will not be used */, Mat44() /* Will not be used */, inR1PlusU, inInvMass2, inInvInertiaScale2 * inBody2.GetInverseInertia(), inR2, inWorldSpaceAxis); - - default: - break; - } - - JPH_ASSERT(false); - return 0.0f; - } - -public: - /// Templated form of CalculateConstraintProperties with the motion types baked in - template - JPH_INLINE void TemplatedCalculateConstraintProperties(float inInvMass1, Mat44Arg inInvI1, Vec3Arg inR1PlusU, float inInvMass2, Mat44Arg inInvI2, Vec3Arg inR2, Vec3Arg inWorldSpaceAxis, float inBias = 0.0f) - { - float inv_effective_mass = TemplatedCalculateInverseEffectiveMass(inInvMass1, inInvI1, inR1PlusU, inInvMass2, inInvI2, inR2, inWorldSpaceAxis); - - if (inv_effective_mass == 0.0f) - Deactivate(); - else - { - mEffectiveMass = 1.0f / inv_effective_mass; - mSpringPart.CalculateSpringPropertiesWithBias(inBias); - } - - JPH_DET_LOG("TemplatedCalculateConstraintProperties: invM1: " << inInvMass1 << " invI1: " << inInvI1 << " r1PlusU: " << inR1PlusU << " invM2: " << inInvMass2 << " invI2: " << inInvI2 << " r2: " << inR2 << " bias: " << inBias << " r1PlusUxAxis: " << mR1PlusUxAxis << " r2xAxis: " << mR2xAxis << " invI1_R1PlusUxAxis: " << mInvI1_R1PlusUxAxis << " invI2_R2xAxis: " << mInvI2_R2xAxis << " effectiveMass: " << mEffectiveMass << " totalLambda: " << mTotalLambda); - } - - /// Calculate properties used during the functions below - /// @param inBody1 The first body that this constraint is attached to - /// @param inBody2 The second body that this constraint is attached to - /// @param inR1PlusU See equations above (r1 + u) - /// @param inR2 See equations above (r2) - /// @param inWorldSpaceAxis Axis along which the constraint acts (normalized, pointing from body 1 to 2) - /// @param inBias Bias term (b) for the constraint impulse: lambda = J v + b - inline void CalculateConstraintProperties(const Body &inBody1, Vec3Arg inR1PlusU, const Body &inBody2, Vec3Arg inR2, Vec3Arg inWorldSpaceAxis, float inBias = 0.0f) - { - float inv_effective_mass = CalculateInverseEffectiveMass(inBody1, inR1PlusU, inBody2, inR2, inWorldSpaceAxis); - - if (inv_effective_mass == 0.0f) - Deactivate(); - else - { - mEffectiveMass = 1.0f / inv_effective_mass; - mSpringPart.CalculateSpringPropertiesWithBias(inBias); - } - } - - /// Calculate properties used during the functions below, version that supports mass scaling - /// @param inBody1 The first body that this constraint is attached to - /// @param inBody2 The second body that this constraint is attached to - /// @param inInvMass1 The inverse mass of body 1 (only used when body 1 is dynamic) - /// @param inInvMass2 The inverse mass of body 2 (only used when body 2 is dynamic) - /// @param inInvInertiaScale1 Scale factor for the inverse inertia of body 1 - /// @param inInvInertiaScale2 Scale factor for the inverse inertia of body 2 - /// @param inR1PlusU See equations above (r1 + u) - /// @param inR2 See equations above (r2) - /// @param inWorldSpaceAxis Axis along which the constraint acts (normalized, pointing from body 1 to 2) - /// @param inBias Bias term (b) for the constraint impulse: lambda = J v + b - inline void CalculateConstraintPropertiesWithMassOverride(const Body &inBody1, float inInvMass1, float inInvInertiaScale1, Vec3Arg inR1PlusU, const Body &inBody2, float inInvMass2, float inInvInertiaScale2, Vec3Arg inR2, Vec3Arg inWorldSpaceAxis, float inBias = 0.0f) - { - float inv_effective_mass = CalculateInverseEffectiveMassWithMassOverride(inBody1, inInvMass1, inInvInertiaScale1, inR1PlusU, inBody2, inInvMass2, inInvInertiaScale2, inR2, inWorldSpaceAxis); - - if (inv_effective_mass == 0.0f) - Deactivate(); - else - { - mEffectiveMass = 1.0f / inv_effective_mass; - mSpringPart.CalculateSpringPropertiesWithBias(inBias); - } - } - - /// Calculate properties used during the functions below - /// @param inDeltaTime Time step - /// @param inBody1 The first body that this constraint is attached to - /// @param inBody2 The second body that this constraint is attached to - /// @param inR1PlusU See equations above (r1 + u) - /// @param inR2 See equations above (r2) - /// @param inWorldSpaceAxis Axis along which the constraint acts (normalized, pointing from body 1 to 2) - /// @param inBias Bias term (b) for the constraint impulse: lambda = J v + b - /// @param inC Value of the constraint equation (C). - /// @param inFrequency Oscillation frequency (Hz). - /// @param inDamping Damping factor (0 = no damping, 1 = critical damping). - inline void CalculateConstraintPropertiesWithFrequencyAndDamping(float inDeltaTime, const Body &inBody1, Vec3Arg inR1PlusU, const Body &inBody2, Vec3Arg inR2, Vec3Arg inWorldSpaceAxis, float inBias, float inC, float inFrequency, float inDamping) - { - float inv_effective_mass = CalculateInverseEffectiveMass(inBody1, inR1PlusU, inBody2, inR2, inWorldSpaceAxis); - - if (inv_effective_mass == 0.0f) - Deactivate(); - else - mSpringPart.CalculateSpringPropertiesWithFrequencyAndDamping(inDeltaTime, inv_effective_mass, inBias, inC, inFrequency, inDamping, mEffectiveMass); - } - - /// Calculate properties used during the functions below - /// @param inDeltaTime Time step - /// @param inBody1 The first body that this constraint is attached to - /// @param inBody2 The second body that this constraint is attached to - /// @param inR1PlusU See equations above (r1 + u) - /// @param inR2 See equations above (r2) - /// @param inWorldSpaceAxis Axis along which the constraint acts (normalized, pointing from body 1 to 2) - /// @param inBias Bias term (b) for the constraint impulse: lambda = J v + b - /// @param inC Value of the constraint equation (C). - /// @param inStiffness Spring stiffness k. - /// @param inDamping Spring damping coefficient c. - inline void CalculateConstraintPropertiesWithStiffnessAndDamping(float inDeltaTime, const Body &inBody1, Vec3Arg inR1PlusU, const Body &inBody2, Vec3Arg inR2, Vec3Arg inWorldSpaceAxis, float inBias, float inC, float inStiffness, float inDamping) - { - float inv_effective_mass = CalculateInverseEffectiveMass(inBody1, inR1PlusU, inBody2, inR2, inWorldSpaceAxis); - - if (inv_effective_mass == 0.0f) - Deactivate(); - else - mSpringPart.CalculateSpringPropertiesWithStiffnessAndDamping(inDeltaTime, inv_effective_mass, inBias, inC, inStiffness, inDamping, mEffectiveMass); - } - - /// Selects one of the above functions based on the spring settings - inline void CalculateConstraintPropertiesWithSettings(float inDeltaTime, const Body &inBody1, Vec3Arg inR1PlusU, const Body &inBody2, Vec3Arg inR2, Vec3Arg inWorldSpaceAxis, float inBias, float inC, const SpringSettings &inSpringSettings) - { - float inv_effective_mass = CalculateInverseEffectiveMass(inBody1, inR1PlusU, inBody2, inR2, inWorldSpaceAxis); - - if (inv_effective_mass == 0.0f) - Deactivate(); - else if (inSpringSettings.mMode == ESpringMode::FrequencyAndDamping) - mSpringPart.CalculateSpringPropertiesWithFrequencyAndDamping(inDeltaTime, inv_effective_mass, inBias, inC, inSpringSettings.mFrequency, inSpringSettings.mDamping, mEffectiveMass); - else - mSpringPart.CalculateSpringPropertiesWithStiffnessAndDamping(inDeltaTime, inv_effective_mass, inBias, inC, inSpringSettings.mStiffness, inSpringSettings.mDamping, mEffectiveMass); - } - - /// Deactivate this constraint - inline void Deactivate() - { - mEffectiveMass = 0.0f; - mTotalLambda = 0.0f; - } - - /// Check if constraint is active - inline bool IsActive() const - { - return mEffectiveMass != 0.0f; - } - - /// Templated form of WarmStart with the motion types baked in - template - inline void TemplatedWarmStart(MotionProperties *ioMotionProperties1, float inInvMass1, MotionProperties *ioMotionProperties2, float inInvMass2, Vec3Arg inWorldSpaceAxis, float inWarmStartImpulseRatio) - { - mTotalLambda *= inWarmStartImpulseRatio; - - ApplyVelocityStep(ioMotionProperties1, inInvMass1, ioMotionProperties2, inInvMass2, inWorldSpaceAxis, mTotalLambda); - } - - /// Must be called from the WarmStartVelocityConstraint call to apply the previous frame's impulses - /// @param ioBody1 The first body that this constraint is attached to - /// @param ioBody2 The second body that this constraint is attached to - /// @param inWorldSpaceAxis Axis along which the constraint acts (normalized) - /// @param inWarmStartImpulseRatio Ratio of new step to old time step (dt_new / dt_old) for scaling the lagrange multiplier of the previous frame - inline void WarmStart(Body &ioBody1, Body &ioBody2, Vec3Arg inWorldSpaceAxis, float inWarmStartImpulseRatio) - { - EMotionType motion_type1 = ioBody1.GetMotionType(); - MotionProperties *motion_properties1 = ioBody1.GetMotionPropertiesUnchecked(); - - EMotionType motion_type2 = ioBody2.GetMotionType(); - MotionProperties *motion_properties2 = ioBody2.GetMotionPropertiesUnchecked(); - - // Dispatch to the correct templated form - // Note: Warm starting doesn't differentiate between kinematic/static bodies so we handle both as static bodies - if (motion_type1 == EMotionType::Dynamic) - { - if (motion_type2 == EMotionType::Dynamic) - TemplatedWarmStart(motion_properties1, motion_properties1->GetInverseMass(), motion_properties2, motion_properties2->GetInverseMass(), inWorldSpaceAxis, inWarmStartImpulseRatio); - else - TemplatedWarmStart(motion_properties1, motion_properties1->GetInverseMass(), motion_properties2, 0.0f /* Unused */, inWorldSpaceAxis, inWarmStartImpulseRatio); - } - else - { - JPH_ASSERT(motion_type2 == EMotionType::Dynamic); - TemplatedWarmStart(motion_properties1, 0.0f /* Unused */, motion_properties2, motion_properties2->GetInverseMass(), inWorldSpaceAxis, inWarmStartImpulseRatio); - } - } - - /// Templated form of SolveVelocityConstraint with the motion types baked in, part 1: get the total lambda - template - JPH_INLINE float TemplatedSolveVelocityConstraintGetTotalLambda(const MotionProperties *ioMotionProperties1, const MotionProperties *ioMotionProperties2, Vec3Arg inWorldSpaceAxis) const - { - // Calculate jacobian multiplied by linear velocity - float jv; - if constexpr (Type1 != EMotionType::Static && Type2 != EMotionType::Static) - jv = inWorldSpaceAxis.Dot(ioMotionProperties1->GetLinearVelocity() - ioMotionProperties2->GetLinearVelocity()); - else if constexpr (Type1 != EMotionType::Static) - jv = inWorldSpaceAxis.Dot(ioMotionProperties1->GetLinearVelocity()); - else if constexpr (Type2 != EMotionType::Static) - jv = inWorldSpaceAxis.Dot(-ioMotionProperties2->GetLinearVelocity()); - else - JPH_ASSERT(false); // Static vs static is nonsensical! - - // Calculate jacobian multiplied by angular velocity - if constexpr (Type1 != EMotionType::Static) - jv += Vec3::sLoadFloat3Unsafe(mR1PlusUxAxis).Dot(ioMotionProperties1->GetAngularVelocity()); - if constexpr (Type2 != EMotionType::Static) - jv -= Vec3::sLoadFloat3Unsafe(mR2xAxis).Dot(ioMotionProperties2->GetAngularVelocity()); - - // Lagrange multiplier is: - // - // lambda = -K^-1 (J v + b) - float lambda = mEffectiveMass * (jv - mSpringPart.GetBias(mTotalLambda)); - - // Return the total accumulated lambda - return mTotalLambda + lambda; - } - - /// Templated form of SolveVelocityConstraint with the motion types baked in, part 2: apply new lambda - template - JPH_INLINE bool TemplatedSolveVelocityConstraintApplyLambda(MotionProperties *ioMotionProperties1, float inInvMass1, MotionProperties *ioMotionProperties2, float inInvMass2, Vec3Arg inWorldSpaceAxis, float inTotalLambda) - { - float delta_lambda = inTotalLambda - mTotalLambda; // Calculate change in lambda - mTotalLambda = inTotalLambda; // Store accumulated impulse - - return ApplyVelocityStep(ioMotionProperties1, inInvMass1, ioMotionProperties2, inInvMass2, inWorldSpaceAxis, delta_lambda); - } - - /// Templated form of SolveVelocityConstraint with the motion types baked in - template - inline bool TemplatedSolveVelocityConstraint(MotionProperties *ioMotionProperties1, float inInvMass1, MotionProperties *ioMotionProperties2, float inInvMass2, Vec3Arg inWorldSpaceAxis, float inMinLambda, float inMaxLambda) - { - float total_lambda = TemplatedSolveVelocityConstraintGetTotalLambda(ioMotionProperties1, ioMotionProperties2, inWorldSpaceAxis); - - // Clamp impulse to specified range - total_lambda = Clamp(total_lambda, inMinLambda, inMaxLambda); - - return TemplatedSolveVelocityConstraintApplyLambda(ioMotionProperties1, inInvMass1, ioMotionProperties2, inInvMass2, inWorldSpaceAxis, total_lambda); - } - - /// Iteratively update the velocity constraint. Makes sure d/dt C(...) = 0, where C is the constraint equation. - /// @param ioBody1 The first body that this constraint is attached to - /// @param ioBody2 The second body that this constraint is attached to - /// @param inWorldSpaceAxis Axis along which the constraint acts (normalized) - /// @param inMinLambda Minimum value of constraint impulse to apply (N s) - /// @param inMaxLambda Maximum value of constraint impulse to apply (N s) - inline bool SolveVelocityConstraint(Body &ioBody1, Body &ioBody2, Vec3Arg inWorldSpaceAxis, float inMinLambda, float inMaxLambda) - { - EMotionType motion_type1 = ioBody1.GetMotionType(); - MotionProperties *motion_properties1 = ioBody1.GetMotionPropertiesUnchecked(); - - EMotionType motion_type2 = ioBody2.GetMotionType(); - MotionProperties *motion_properties2 = ioBody2.GetMotionPropertiesUnchecked(); - - // Dispatch to the correct templated form - switch (motion_type1) - { - case EMotionType::Dynamic: - switch (motion_type2) - { - case EMotionType::Dynamic: - return TemplatedSolveVelocityConstraint(motion_properties1, motion_properties1->GetInverseMass(), motion_properties2, motion_properties2->GetInverseMass(), inWorldSpaceAxis, inMinLambda, inMaxLambda); - - case EMotionType::Kinematic: - return TemplatedSolveVelocityConstraint(motion_properties1, motion_properties1->GetInverseMass(), motion_properties2, 0.0f /* Unused */, inWorldSpaceAxis, inMinLambda, inMaxLambda); - - case EMotionType::Static: - return TemplatedSolveVelocityConstraint(motion_properties1, motion_properties1->GetInverseMass(), motion_properties2, 0.0f /* Unused */, inWorldSpaceAxis, inMinLambda, inMaxLambda); - - default: - JPH_ASSERT(false); - break; - } - break; - - case EMotionType::Kinematic: - JPH_ASSERT(motion_type2 == EMotionType::Dynamic); - return TemplatedSolveVelocityConstraint(motion_properties1, 0.0f /* Unused */, motion_properties2, motion_properties2->GetInverseMass(), inWorldSpaceAxis, inMinLambda, inMaxLambda); - - case EMotionType::Static: - JPH_ASSERT(motion_type2 == EMotionType::Dynamic); - return TemplatedSolveVelocityConstraint(motion_properties1, 0.0f /* Unused */, motion_properties2, motion_properties2->GetInverseMass(), inWorldSpaceAxis, inMinLambda, inMaxLambda); - - default: - JPH_ASSERT(false); - break; - } - - return false; - } - - /// Iteratively update the velocity constraint. Makes sure d/dt C(...) = 0, where C is the constraint equation. - /// @param ioBody1 The first body that this constraint is attached to - /// @param ioBody2 The second body that this constraint is attached to - /// @param inInvMass1 The inverse mass of body 1 (only used when body 1 is dynamic) - /// @param inInvMass2 The inverse mass of body 2 (only used when body 2 is dynamic) - /// @param inWorldSpaceAxis Axis along which the constraint acts (normalized) - /// @param inMinLambda Minimum value of constraint impulse to apply (N s) - /// @param inMaxLambda Maximum value of constraint impulse to apply (N s) - inline bool SolveVelocityConstraintWithMassOverride(Body &ioBody1, float inInvMass1, Body &ioBody2, float inInvMass2, Vec3Arg inWorldSpaceAxis, float inMinLambda, float inMaxLambda) - { - EMotionType motion_type1 = ioBody1.GetMotionType(); - MotionProperties *motion_properties1 = ioBody1.GetMotionPropertiesUnchecked(); - - EMotionType motion_type2 = ioBody2.GetMotionType(); - MotionProperties *motion_properties2 = ioBody2.GetMotionPropertiesUnchecked(); - - // Dispatch to the correct templated form - switch (motion_type1) - { - case EMotionType::Dynamic: - switch (motion_type2) - { - case EMotionType::Dynamic: - return TemplatedSolveVelocityConstraint(motion_properties1, inInvMass1, motion_properties2, inInvMass2, inWorldSpaceAxis, inMinLambda, inMaxLambda); - - case EMotionType::Kinematic: - return TemplatedSolveVelocityConstraint(motion_properties1, inInvMass1, motion_properties2, 0.0f /* Unused */, inWorldSpaceAxis, inMinLambda, inMaxLambda); - - case EMotionType::Static: - return TemplatedSolveVelocityConstraint(motion_properties1, inInvMass1, motion_properties2, 0.0f /* Unused */, inWorldSpaceAxis, inMinLambda, inMaxLambda); - - default: - JPH_ASSERT(false); - break; - } - break; - - case EMotionType::Kinematic: - JPH_ASSERT(motion_type2 == EMotionType::Dynamic); - return TemplatedSolveVelocityConstraint(motion_properties1, 0.0f /* Unused */, motion_properties2, inInvMass2, inWorldSpaceAxis, inMinLambda, inMaxLambda); - - case EMotionType::Static: - JPH_ASSERT(motion_type2 == EMotionType::Dynamic); - return TemplatedSolveVelocityConstraint(motion_properties1, 0.0f /* Unused */, motion_properties2, inInvMass2, inWorldSpaceAxis, inMinLambda, inMaxLambda); - - default: - JPH_ASSERT(false); - break; - } - - return false; - } - - /// Iteratively update the position constraint. Makes sure C(...) = 0. - /// @param ioBody1 The first body that this constraint is attached to - /// @param ioBody2 The second body that this constraint is attached to - /// @param inWorldSpaceAxis Axis along which the constraint acts (normalized) - /// @param inC Value of the constraint equation (C) - /// @param inBaumgarte Baumgarte constant (fraction of the error to correct) - inline bool SolvePositionConstraint(Body &ioBody1, Body &ioBody2, Vec3Arg inWorldSpaceAxis, float inC, float inBaumgarte) const - { - // Only apply position constraint when the constraint is hard, otherwise the velocity bias will fix the constraint - if (inC != 0.0f && !mSpringPart.IsActive()) - { - // Calculate lagrange multiplier (lambda) for Baumgarte stabilization: - // - // lambda = -K^-1 * beta / dt * C - // - // We should divide by inDeltaTime, but we should multiply by inDeltaTime in the Euler step below so they're cancelled out - float lambda = -mEffectiveMass * inBaumgarte * inC; - - // Directly integrate velocity change for one time step - // - // Euler velocity integration: - // dv = M^-1 P - // - // Impulse: - // P = J^T lambda - // - // Euler position integration: - // x' = x + dv * dt - // - // Note we don't accumulate velocities for the stabilization. This is using the approach described in 'Modeling and - // Solving Constraints' by Erin Catto presented at GDC 2007. On slide 78 it is suggested to split up the Baumgarte - // stabilization for positional drift so that it does not actually add to the momentum. We combine an Euler velocity - // integrate + a position integrate and then discard the velocity change. - if (ioBody1.IsDynamic()) - { - ioBody1.SubPositionStep((lambda * ioBody1.GetMotionProperties()->GetInverseMass()) * inWorldSpaceAxis); - ioBody1.SubRotationStep(lambda * Vec3::sLoadFloat3Unsafe(mInvI1_R1PlusUxAxis)); - } - if (ioBody2.IsDynamic()) - { - ioBody2.AddPositionStep((lambda * ioBody2.GetMotionProperties()->GetInverseMass()) * inWorldSpaceAxis); - ioBody2.AddRotationStep(lambda * Vec3::sLoadFloat3Unsafe(mInvI2_R2xAxis)); - } - return true; - } - - return false; - } - - /// Iteratively update the position constraint. Makes sure C(...) = 0. - /// @param ioBody1 The first body that this constraint is attached to - /// @param ioBody2 The second body that this constraint is attached to - /// @param inInvMass1 The inverse mass of body 1 (only used when body 1 is dynamic) - /// @param inInvMass2 The inverse mass of body 2 (only used when body 2 is dynamic) - /// @param inWorldSpaceAxis Axis along which the constraint acts (normalized) - /// @param inC Value of the constraint equation (C) - /// @param inBaumgarte Baumgarte constant (fraction of the error to correct) - inline bool SolvePositionConstraintWithMassOverride(Body &ioBody1, float inInvMass1, Body &ioBody2, float inInvMass2, Vec3Arg inWorldSpaceAxis, float inC, float inBaumgarte) const - { - // Only apply position constraint when the constraint is hard, otherwise the velocity bias will fix the constraint - if (inC != 0.0f && !mSpringPart.IsActive()) - { - // Calculate lagrange multiplier (lambda) for Baumgarte stabilization: - // - // lambda = -K^-1 * beta / dt * C - // - // We should divide by inDeltaTime, but we should multiply by inDeltaTime in the Euler step below so they're cancelled out - float lambda = -mEffectiveMass * inBaumgarte * inC; - - // Directly integrate velocity change for one time step - // - // Euler velocity integration: - // dv = M^-1 P - // - // Impulse: - // P = J^T lambda - // - // Euler position integration: - // x' = x + dv * dt - // - // Note we don't accumulate velocities for the stabilization. This is using the approach described in 'Modeling and - // Solving Constraints' by Erin Catto presented at GDC 2007. On slide 78 it is suggested to split up the Baumgarte - // stabilization for positional drift so that it does not actually add to the momentum. We combine an Euler velocity - // integrate + a position integrate and then discard the velocity change. - if (ioBody1.IsDynamic()) - { - ioBody1.SubPositionStep((lambda * inInvMass1) * inWorldSpaceAxis); - ioBody1.SubRotationStep(lambda * Vec3::sLoadFloat3Unsafe(mInvI1_R1PlusUxAxis)); - } - if (ioBody2.IsDynamic()) - { - ioBody2.AddPositionStep((lambda * inInvMass2) * inWorldSpaceAxis); - ioBody2.AddRotationStep(lambda * Vec3::sLoadFloat3Unsafe(mInvI2_R2xAxis)); - } - return true; - } - - return false; - } - - /// Override total lagrange multiplier, can be used to set the initial value for warm starting - inline void SetTotalLambda(float inLambda) - { - mTotalLambda = inLambda; - } - - /// Return lagrange multiplier - inline float GetTotalLambda() const - { - return mTotalLambda; - } - - /// Save state of this constraint part - void SaveState(StateRecorder &inStream) const - { - inStream.Write(mTotalLambda); - } - - /// Restore state of this constraint part - void RestoreState(StateRecorder &inStream) - { - inStream.Read(mTotalLambda); - } - -private: - Float3 mR1PlusUxAxis; - Float3 mR2xAxis; - Float3 mInvI1_R1PlusUxAxis; - Float3 mInvI2_R2xAxis; - float mEffectiveMass = 0.0f; - SpringPart mSpringPart; - float mTotalLambda = 0.0f; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/DualAxisConstraintPart.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/DualAxisConstraintPart.h deleted file mode 100644 index c0ebe5ac42e..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/DualAxisConstraintPart.h +++ /dev/null @@ -1,276 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -/** - Constrains movement on 2 axis - - @see "Constraints Derivation for Rigid Body Simulation in 3D" - Daniel Chappuis, section 2.3.1 - - Constraint equation (eq 51): - - \f[C = \begin{bmatrix} (p_2 - p_1) \cdot n_1 \\ (p_2 - p_1) \cdot n_2\end{bmatrix}\f] - - Jacobian (transposed) (eq 55): - - \f[J^T = \begin{bmatrix} - -n_1 & -n_2 \\ - -(r_1 + u) \times n_1 & -(r_1 + u) \times n_2 \\ - n_1 & n_2 \\ - r_2 \times n_1 & r_2 \times n_2 - \end{bmatrix}\f] - - Used terms (here and below, everything in world space):\n - n1, n2 = constraint axis (normalized).\n - p1, p2 = constraint points.\n - r1 = p1 - x1.\n - r2 = p2 - x2.\n - u = x2 + r2 - x1 - r1 = p2 - p1.\n - x1, x2 = center of mass for the bodies.\n - v = [v1, w1, v2, w2].\n - v1, v2 = linear velocity of body 1 and 2.\n - w1, w2 = angular velocity of body 1 and 2.\n - M = mass matrix, a diagonal matrix of the mass and inertia with diagonal [m1, I1, m2, I2].\n - \f$K^{-1} = \left( J M^{-1} J^T \right)^{-1}\f$ = effective mass.\n - b = velocity bias.\n - \f$\beta\f$ = baumgarte constant. -**/ -class DualAxisConstraintPart -{ -public: - using Vec2 = Vector<2>; - using Mat22 = Matrix<2, 2>; - -private: - /// Internal helper function to update velocities of bodies after Lagrange multiplier is calculated - JPH_INLINE bool ApplyVelocityStep(Body &ioBody1, Body &ioBody2, Vec3Arg inN1, Vec3Arg inN2, const Vec2 &inLambda) const - { - // Apply impulse if delta is not zero - if (!inLambda.IsZero()) - { - // Calculate velocity change due to constraint - // - // Impulse: - // P = J^T lambda - // - // Euler velocity integration: - // v' = v + M^-1 P - Vec3 impulse = inN1 * inLambda[0] + inN2 * inLambda[1]; - if (ioBody1.IsDynamic()) - { - MotionProperties *mp1 = ioBody1.GetMotionProperties(); - mp1->SubLinearVelocityStep(mp1->GetInverseMass() * impulse); - mp1->SubAngularVelocityStep(mInvI1_R1PlusUxN1 * inLambda[0] + mInvI1_R1PlusUxN2 * inLambda[1]); - } - if (ioBody2.IsDynamic()) - { - MotionProperties *mp2 = ioBody2.GetMotionProperties(); - mp2->AddLinearVelocityStep(mp2->GetInverseMass() * impulse); - mp2->AddAngularVelocityStep(mInvI2_R2xN1 * inLambda[0] + mInvI2_R2xN2 * inLambda[1]); - } - return true; - } - - return false; - } - - /// Internal helper function to calculate the lagrange multiplier - inline void CalculateLagrangeMultiplier(const Body &inBody1, const Body &inBody2, Vec3Arg inN1, Vec3Arg inN2, Vec2 &outLambda) const - { - // Calculate lagrange multiplier: - // - // lambda = -K^-1 (J v + b) - Vec3 delta_lin = inBody1.GetLinearVelocity() - inBody2.GetLinearVelocity(); - Vec2 jv; - jv[0] = inN1.Dot(delta_lin) + mR1PlusUxN1.Dot(inBody1.GetAngularVelocity()) - mR2xN1.Dot(inBody2.GetAngularVelocity()); - jv[1] = inN2.Dot(delta_lin) + mR1PlusUxN2.Dot(inBody1.GetAngularVelocity()) - mR2xN2.Dot(inBody2.GetAngularVelocity()); - outLambda = mEffectiveMass * jv; - } - -public: - /// Calculate properties used during the functions below - /// All input vectors are in world space - inline void CalculateConstraintProperties(const Body &inBody1, Mat44Arg inRotation1, Vec3Arg inR1PlusU, const Body &inBody2, Mat44Arg inRotation2, Vec3Arg inR2, Vec3Arg inN1, Vec3Arg inN2) - { - JPH_ASSERT(inN1.IsNormalized(1.0e-5f)); - JPH_ASSERT(inN2.IsNormalized(1.0e-5f)); - - // Calculate properties used during constraint solving - mR1PlusUxN1 = inR1PlusU.Cross(inN1); - mR1PlusUxN2 = inR1PlusU.Cross(inN2); - mR2xN1 = inR2.Cross(inN1); - mR2xN2 = inR2.Cross(inN2); - - // Calculate effective mass: K^-1 = (J M^-1 J^T)^-1, eq 59 - Mat22 inv_effective_mass; - if (inBody1.IsDynamic()) - { - const MotionProperties *mp1 = inBody1.GetMotionProperties(); - Mat44 inv_i1 = mp1->GetInverseInertiaForRotation(inRotation1); - mInvI1_R1PlusUxN1 = inv_i1.Multiply3x3(mR1PlusUxN1); - mInvI1_R1PlusUxN2 = inv_i1.Multiply3x3(mR1PlusUxN2); - - inv_effective_mass(0, 0) = mp1->GetInverseMass() + mR1PlusUxN1.Dot(mInvI1_R1PlusUxN1); - inv_effective_mass(0, 1) = mR1PlusUxN1.Dot(mInvI1_R1PlusUxN2); - inv_effective_mass(1, 0) = mR1PlusUxN2.Dot(mInvI1_R1PlusUxN1); - inv_effective_mass(1, 1) = mp1->GetInverseMass() + mR1PlusUxN2.Dot(mInvI1_R1PlusUxN2); - } - else - { - JPH_IF_DEBUG(mInvI1_R1PlusUxN1 = Vec3::sNaN();) - JPH_IF_DEBUG(mInvI1_R1PlusUxN2 = Vec3::sNaN();) - - inv_effective_mass = Mat22::sZero(); - } - - if (inBody2.IsDynamic()) - { - const MotionProperties *mp2 = inBody2.GetMotionProperties(); - Mat44 inv_i2 = mp2->GetInverseInertiaForRotation(inRotation2); - mInvI2_R2xN1 = inv_i2.Multiply3x3(mR2xN1); - mInvI2_R2xN2 = inv_i2.Multiply3x3(mR2xN2); - - inv_effective_mass(0, 0) += mp2->GetInverseMass() + mR2xN1.Dot(mInvI2_R2xN1); - inv_effective_mass(0, 1) += mR2xN1.Dot(mInvI2_R2xN2); - inv_effective_mass(1, 0) += mR2xN2.Dot(mInvI2_R2xN1); - inv_effective_mass(1, 1) += mp2->GetInverseMass() + mR2xN2.Dot(mInvI2_R2xN2); - } - else - { - JPH_IF_DEBUG(mInvI2_R2xN1 = Vec3::sNaN();) - JPH_IF_DEBUG(mInvI2_R2xN2 = Vec3::sNaN();) - } - - if (!mEffectiveMass.SetInversed(inv_effective_mass)) - Deactivate(); - } - - /// Deactivate this constraint - inline void Deactivate() - { - mEffectiveMass.SetZero(); - mTotalLambda.SetZero(); - } - - /// Check if constraint is active - inline bool IsActive() const - { - return !mEffectiveMass.IsZero(); - } - - /// Must be called from the WarmStartVelocityConstraint call to apply the previous frame's impulses - /// All input vectors are in world space - inline void WarmStart(Body &ioBody1, Body &ioBody2, Vec3Arg inN1, Vec3Arg inN2, float inWarmStartImpulseRatio) - { - mTotalLambda *= inWarmStartImpulseRatio; - ApplyVelocityStep(ioBody1, ioBody2, inN1, inN2, mTotalLambda); - } - - /// Iteratively update the velocity constraint. Makes sure d/dt C(...) = 0, where C is the constraint equation. - /// All input vectors are in world space - inline bool SolveVelocityConstraint(Body &ioBody1, Body &ioBody2, Vec3Arg inN1, Vec3Arg inN2) - { - Vec2 lambda; - CalculateLagrangeMultiplier(ioBody1, ioBody2, inN1, inN2, lambda); - - // Store accumulated lambda - mTotalLambda += lambda; - - return ApplyVelocityStep(ioBody1, ioBody2, inN1, inN2, lambda); - } - - /// Iteratively update the position constraint. Makes sure C(...) = 0. - /// All input vectors are in world space - inline bool SolvePositionConstraint(Body &ioBody1, Body &ioBody2, Vec3Arg inU, Vec3Arg inN1, Vec3Arg inN2, float inBaumgarte) const - { - Vec2 c; - c[0] = inU.Dot(inN1); - c[1] = inU.Dot(inN2); - if (!c.IsZero()) - { - // Calculate lagrange multiplier (lambda) for Baumgarte stabilization: - // - // lambda = -K^-1 * beta / dt * C - // - // We should divide by inDeltaTime, but we should multiply by inDeltaTime in the Euler step below so they're cancelled out - Vec2 lambda = -inBaumgarte * (mEffectiveMass * c); - - // Directly integrate velocity change for one time step - // - // Euler velocity integration: - // dv = M^-1 P - // - // Impulse: - // P = J^T lambda - // - // Euler position integration: - // x' = x + dv * dt - // - // Note we don't accumulate velocities for the stabilization. This is using the approach described in 'Modeling and - // Solving Constraints' by Erin Catto presented at GDC 2007. On slide 78 it is suggested to split up the Baumgarte - // stabilization for positional drift so that it does not actually add to the momentum. We combine an Euler velocity - // integrate + a position integrate and then discard the velocity change. - Vec3 impulse = inN1 * lambda[0] + inN2 * lambda[1]; - if (ioBody1.IsDynamic()) - { - ioBody1.SubPositionStep(ioBody1.GetMotionProperties()->GetInverseMass() * impulse); - ioBody1.SubRotationStep(mInvI1_R1PlusUxN1 * lambda[0] + mInvI1_R1PlusUxN2 * lambda[1]); - } - if (ioBody2.IsDynamic()) - { - ioBody2.AddPositionStep(ioBody2.GetMotionProperties()->GetInverseMass() * impulse); - ioBody2.AddRotationStep(mInvI2_R2xN1 * lambda[0] + mInvI2_R2xN2 * lambda[1]); - } - return true; - } - - return false; - } - - /// Override total lagrange multiplier, can be used to set the initial value for warm starting - inline void SetTotalLambda(const Vec2 &inLambda) - { - mTotalLambda = inLambda; - } - - /// Return lagrange multiplier - inline const Vec2 & GetTotalLambda() const - { - return mTotalLambda; - } - - /// Save state of this constraint part - void SaveState(StateRecorder &inStream) const - { - inStream.Write(mTotalLambda); - } - - /// Restore state of this constraint part - void RestoreState(StateRecorder &inStream) - { - inStream.Read(mTotalLambda); - } - -private: - Vec3 mR1PlusUxN1; - Vec3 mR1PlusUxN2; - Vec3 mR2xN1; - Vec3 mR2xN2; - Vec3 mInvI1_R1PlusUxN1; - Vec3 mInvI1_R1PlusUxN2; - Vec3 mInvI2_R2xN1; - Vec3 mInvI2_R2xN2; - Mat22 mEffectiveMass; - Vec2 mTotalLambda { Vec2::sZero() }; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/GearConstraintPart.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/GearConstraintPart.h deleted file mode 100644 index 6e277a64b56..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/GearConstraintPart.h +++ /dev/null @@ -1,195 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include - -JPH_NAMESPACE_BEGIN - -/// Constraint that constrains two rotations using a gear (rotating in opposite direction) -/// -/// Constraint equation: -/// -/// C = Rotation1(t) + r Rotation2(t) -/// -/// Derivative: -/// -/// d/dt C = 0 -/// <=> w1 . a + r w2 . b = 0 -/// -/// Jacobian: -/// -/// \f[J = \begin{bmatrix}0 & a^T & 0 & r b^T\end{bmatrix}\f] -/// -/// Used terms (here and below, everything in world space):\n -/// a = axis around which body 1 rotates (normalized).\n -/// b = axis along which body 2 slides (normalized).\n -/// Rotation1(t) = rotation around a of body 1.\n -/// Rotation2(t) = rotation around b of body 2.\n -/// r = ratio between rotation for body 1 and 2.\n -/// v = [v1, w1, v2, w2].\n -/// v1, v2 = linear velocity of body 1 and 2.\n -/// w1, w2 = angular velocity of body 1 and 2.\n -/// M = mass matrix, a diagonal matrix of the mass and inertia with diagonal [m1, I1, m2, I2].\n -/// \f$K^{-1} = \left( J M^{-1} J^T \right)^{-1}\f$ = effective mass.\n -/// \f$\beta\f$ = baumgarte constant. -class GearConstraintPart -{ - /// Internal helper function to update velocities of bodies after Lagrange multiplier is calculated - JPH_INLINE bool ApplyVelocityStep(Body &ioBody1, Body &ioBody2, float inLambda) const - { - // Apply impulse if delta is not zero - if (inLambda != 0.0f) - { - // Calculate velocity change due to constraint - // - // Impulse: - // P = J^T lambda - // - // Euler velocity integration: - // v' = v + M^-1 P - ioBody1.GetMotionProperties()->AddAngularVelocityStep(inLambda * mInvI1_A); - ioBody2.GetMotionProperties()->AddAngularVelocityStep(inLambda * mInvI2_B); - return true; - } - - return false; - } - -public: - /// Calculate properties used during the functions below - /// @param inBody1 The first body that this constraint is attached to - /// @param inBody2 The second body that this constraint is attached to - /// @param inWorldSpaceHingeAxis1 The axis around which body 1 rotates - /// @param inWorldSpaceHingeAxis2 The axis around which body 2 rotates - /// @param inRatio The ratio between rotation and translation - inline void CalculateConstraintProperties(const Body &inBody1, Vec3Arg inWorldSpaceHingeAxis1, const Body &inBody2, Vec3Arg inWorldSpaceHingeAxis2, float inRatio) - { - JPH_ASSERT(inWorldSpaceHingeAxis1.IsNormalized(1.0e-4f)); - JPH_ASSERT(inWorldSpaceHingeAxis2.IsNormalized(1.0e-4f)); - - // Calculate: I1^-1 a - mInvI1_A = inBody1.GetMotionProperties()->MultiplyWorldSpaceInverseInertiaByVector(inBody1.GetRotation(), inWorldSpaceHingeAxis1); - - // Calculate: I2^-1 b - mInvI2_B = inBody2.GetMotionProperties()->MultiplyWorldSpaceInverseInertiaByVector(inBody2.GetRotation(), inWorldSpaceHingeAxis2); - - // K^-1 = 1 / (J M^-1 J^T) = 1 / (a^T I1^-1 a + r^2 * b^T I2^-1 b) - float inv_effective_mass = (inWorldSpaceHingeAxis1.Dot(mInvI1_A) + inWorldSpaceHingeAxis2.Dot(mInvI2_B) * Square(inRatio)); - if (inv_effective_mass == 0.0f) - Deactivate(); - else - mEffectiveMass = 1.0f / inv_effective_mass; - } - - /// Deactivate this constraint - inline void Deactivate() - { - mEffectiveMass = 0.0f; - mTotalLambda = 0.0f; - } - - /// Check if constraint is active - inline bool IsActive() const - { - return mEffectiveMass != 0.0f; - } - - /// Must be called from the WarmStartVelocityConstraint call to apply the previous frame's impulses - /// @param ioBody1 The first body that this constraint is attached to - /// @param ioBody2 The second body that this constraint is attached to - /// @param inWarmStartImpulseRatio Ratio of new step to old time step (dt_new / dt_old) for scaling the lagrange multiplier of the previous frame - inline void WarmStart(Body &ioBody1, Body &ioBody2, float inWarmStartImpulseRatio) - { - mTotalLambda *= inWarmStartImpulseRatio; - ApplyVelocityStep(ioBody1, ioBody2, mTotalLambda); - } - - /// Iteratively update the velocity constraint. Makes sure d/dt C(...) = 0, where C is the constraint equation. - /// @param ioBody1 The first body that this constraint is attached to - /// @param ioBody2 The second body that this constraint is attached to - /// @param inWorldSpaceHingeAxis1 The axis around which body 1 rotates - /// @param inWorldSpaceHingeAxis2 The axis around which body 2 rotates - /// @param inRatio The ratio between rotation and translation - inline bool SolveVelocityConstraint(Body &ioBody1, Vec3Arg inWorldSpaceHingeAxis1, Body &ioBody2, Vec3Arg inWorldSpaceHingeAxis2, float inRatio) - { - // Lagrange multiplier is: - // - // lambda = -K^-1 (J v + b) - float lambda = -mEffectiveMass * (inWorldSpaceHingeAxis1.Dot(ioBody1.GetAngularVelocity()) + inRatio * inWorldSpaceHingeAxis2.Dot(ioBody2.GetAngularVelocity())); - mTotalLambda += lambda; // Store accumulated impulse - - return ApplyVelocityStep(ioBody1, ioBody2, lambda); - } - - /// Return lagrange multiplier - float GetTotalLambda() const - { - return mTotalLambda; - } - - /// Iteratively update the position constraint. Makes sure C(...) == 0. - /// @param ioBody1 The first body that this constraint is attached to - /// @param ioBody2 The second body that this constraint is attached to - /// @param inC Value of the constraint equation (C) - /// @param inBaumgarte Baumgarte constant (fraction of the error to correct) - inline bool SolvePositionConstraint(Body &ioBody1, Body &ioBody2, float inC, float inBaumgarte) const - { - // Only apply position constraint when the constraint is hard, otherwise the velocity bias will fix the constraint - if (inC != 0.0f) - { - // Calculate lagrange multiplier (lambda) for Baumgarte stabilization: - // - // lambda = -K^-1 * beta / dt * C - // - // We should divide by inDeltaTime, but we should multiply by inDeltaTime in the Euler step below so they're cancelled out - float lambda = -mEffectiveMass * inBaumgarte * inC; - - // Directly integrate velocity change for one time step - // - // Euler velocity integration: - // dv = M^-1 P - // - // Impulse: - // P = J^T lambda - // - // Euler position integration: - // x' = x + dv * dt - // - // Note we don't accumulate velocities for the stabilization. This is using the approach described in 'Modeling and - // Solving Constraints' by Erin Catto presented at GDC 2007. On slide 78 it is suggested to split up the Baumgarte - // stabilization for positional drift so that it does not actually add to the momentum. We combine an Euler velocity - // integrate + a position integrate and then discard the velocity change. - if (ioBody1.IsDynamic()) - ioBody1.AddRotationStep(lambda * mInvI1_A); - if (ioBody2.IsDynamic()) - ioBody2.AddRotationStep(lambda * mInvI2_B); - return true; - } - - return false; - } - - /// Save state of this constraint part - void SaveState(StateRecorder &inStream) const - { - inStream.Write(mTotalLambda); - } - - /// Restore state of this constraint part - void RestoreState(StateRecorder &inStream) - { - inStream.Read(mTotalLambda); - } - -private: - Vec3 mInvI1_A; - Vec3 mInvI2_B; - float mEffectiveMass = 0.0f; - float mTotalLambda = 0.0f; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/HingeRotationConstraintPart.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/HingeRotationConstraintPart.h deleted file mode 100644 index 5f566f70179..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/HingeRotationConstraintPart.h +++ /dev/null @@ -1,222 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -/** - Constrains rotation around 2 axis so that it only allows rotation around 1 axis - - Based on: "Constraints Derivation for Rigid Body Simulation in 3D" - Daniel Chappuis, section 2.4.1 - - Constraint equation (eq 87): - - \f[C = \begin{bmatrix}a_1 \cdot b_2 \\ a_1 \cdot c_2\end{bmatrix}\f] - - Jacobian (eq 90): - - \f[J = \begin{bmatrix} - 0 & -b_2 \times a_1 & 0 & b_2 \times a_1 \\ - 0 & -c_2 \times a_1 & 0 & c2 \times a_1 - \end{bmatrix}\f] - - Used terms (here and below, everything in world space):\n - a1 = hinge axis on body 1.\n - b2, c2 = axis perpendicular to hinge axis on body 2.\n - x1, x2 = center of mass for the bodies.\n - v = [v1, w1, v2, w2].\n - v1, v2 = linear velocity of body 1 and 2.\n - w1, w2 = angular velocity of body 1 and 2.\n - M = mass matrix, a diagonal matrix of the mass and inertia with diagonal [m1, I1, m2, I2].\n - \f$K^{-1} = \left( J M^{-1} J^T \right)^{-1}\f$ = effective mass.\n - b = velocity bias.\n - \f$\beta\f$ = baumgarte constant.\n - E = identity matrix. -**/ -class HingeRotationConstraintPart -{ -public: - using Vec2 = Vector<2>; - using Mat22 = Matrix<2, 2>; - -private: - /// Internal helper function to update velocities of bodies after Lagrange multiplier is calculated - JPH_INLINE bool ApplyVelocityStep(Body &ioBody1, Body &ioBody2, const Vec2 &inLambda) const - { - // Apply impulse if delta is not zero - if (!inLambda.IsZero()) - { - // Calculate velocity change due to constraint - // - // Impulse: - // P = J^T lambda - // - // Euler velocity integration: - // v' = v + M^-1 P - Vec3 impulse = mB2xA1 * inLambda[0] + mC2xA1 * inLambda[1]; - if (ioBody1.IsDynamic()) - ioBody1.GetMotionProperties()->SubAngularVelocityStep(mInvI1.Multiply3x3(impulse)); - if (ioBody2.IsDynamic()) - ioBody2.GetMotionProperties()->AddAngularVelocityStep(mInvI2.Multiply3x3(impulse)); - return true; - } - - return false; - } - -public: - /// Calculate properties used during the functions below - inline void CalculateConstraintProperties(const Body &inBody1, Mat44Arg inRotation1, Vec3Arg inWorldSpaceHingeAxis1, const Body &inBody2, Mat44Arg inRotation2, Vec3Arg inWorldSpaceHingeAxis2) - { - JPH_ASSERT(inWorldSpaceHingeAxis1.IsNormalized(1.0e-5f)); - JPH_ASSERT(inWorldSpaceHingeAxis2.IsNormalized(1.0e-5f)); - - // Calculate hinge axis in world space - mA1 = inWorldSpaceHingeAxis1; - Vec3 a2 = inWorldSpaceHingeAxis2; - float dot = mA1.Dot(a2); - if (dot <= 1.0e-3f) - { - // World space axes are more than 90 degrees apart, get a perpendicular vector in the plane formed by mA1 and a2 as hinge axis until the rotation is less than 90 degrees - Vec3 perp = a2 - dot * mA1; - if (perp.LengthSq() < 1.0e-6f) - { - // mA1 ~ -a2, take random perpendicular - perp = mA1.GetNormalizedPerpendicular(); - } - - // Blend in a little bit from mA1 so we're less than 90 degrees apart - a2 = (0.99f * perp.Normalized() + 0.01f * mA1).Normalized(); - } - mB2 = a2.GetNormalizedPerpendicular(); - mC2 = a2.Cross(mB2); - - // Calculate properties used during constraint solving - mInvI1 = inBody1.IsDynamic()? inBody1.GetMotionProperties()->GetInverseInertiaForRotation(inRotation1) : Mat44::sZero(); - mInvI2 = inBody2.IsDynamic()? inBody2.GetMotionProperties()->GetInverseInertiaForRotation(inRotation2) : Mat44::sZero(); - mB2xA1 = mB2.Cross(mA1); - mC2xA1 = mC2.Cross(mA1); - - // Calculate effective mass: K^-1 = (J M^-1 J^T)^-1 - Mat44 summed_inv_inertia = mInvI1 + mInvI2; - Mat22 inv_effective_mass; - inv_effective_mass(0, 0) = mB2xA1.Dot(summed_inv_inertia.Multiply3x3(mB2xA1)); - inv_effective_mass(0, 1) = mB2xA1.Dot(summed_inv_inertia.Multiply3x3(mC2xA1)); - inv_effective_mass(1, 0) = mC2xA1.Dot(summed_inv_inertia.Multiply3x3(mB2xA1)); - inv_effective_mass(1, 1) = mC2xA1.Dot(summed_inv_inertia.Multiply3x3(mC2xA1)); - if (!mEffectiveMass.SetInversed(inv_effective_mass)) - Deactivate(); - } - - /// Deactivate this constraint - inline void Deactivate() - { - mEffectiveMass.SetZero(); - mTotalLambda.SetZero(); - } - - /// Must be called from the WarmStartVelocityConstraint call to apply the previous frame's impulses - inline void WarmStart(Body &ioBody1, Body &ioBody2, float inWarmStartImpulseRatio) - { - mTotalLambda *= inWarmStartImpulseRatio; - ApplyVelocityStep(ioBody1, ioBody2, mTotalLambda); - } - - /// Iteratively update the velocity constraint. Makes sure d/dt C(...) = 0, where C is the constraint equation. - inline bool SolveVelocityConstraint(Body &ioBody1, Body &ioBody2) - { - // Calculate lagrange multiplier: - // - // lambda = -K^-1 (J v + b) - Vec3 delta_ang = ioBody1.GetAngularVelocity() - ioBody2.GetAngularVelocity(); - Vec2 jv; - jv[0] = mB2xA1.Dot(delta_ang); - jv[1] = mC2xA1.Dot(delta_ang); - Vec2 lambda = mEffectiveMass * jv; - - // Store accumulated lambda - mTotalLambda += lambda; - - return ApplyVelocityStep(ioBody1, ioBody2, lambda); - } - - /// Iteratively update the position constraint. Makes sure C(...) = 0. - inline bool SolvePositionConstraint(Body &ioBody1, Body &ioBody2, float inBaumgarte) const - { - // Constraint needs Axis of body 1 perpendicular to both B and C from body 2 (which are both perpendicular to the Axis of body 2) - Vec2 c; - c[0] = mA1.Dot(mB2); - c[1] = mA1.Dot(mC2); - if (!c.IsZero()) - { - // Calculate lagrange multiplier (lambda) for Baumgarte stabilization: - // - // lambda = -K^-1 * beta / dt * C - // - // We should divide by inDeltaTime, but we should multiply by inDeltaTime in the Euler step below so they're cancelled out - Vec2 lambda = -inBaumgarte * (mEffectiveMass * c); - - // Directly integrate velocity change for one time step - // - // Euler velocity integration: - // dv = M^-1 P - // - // Impulse: - // P = J^T lambda - // - // Euler position integration: - // x' = x + dv * dt - // - // Note we don't accumulate velocities for the stabilization. This is using the approach described in 'Modeling and - // Solving Constraints' by Erin Catto presented at GDC 2007. On slide 78 it is suggested to split up the Baumgarte - // stabilization for positional drift so that it does not actually add to the momentum. We combine an Euler velocity - // integrate + a position integrate and then discard the velocity change. - Vec3 impulse = mB2xA1 * lambda[0] + mC2xA1 * lambda[1]; - if (ioBody1.IsDynamic()) - ioBody1.SubRotationStep(mInvI1.Multiply3x3(impulse)); - if (ioBody2.IsDynamic()) - ioBody2.AddRotationStep(mInvI2.Multiply3x3(impulse)); - return true; - } - - return false; - } - - /// Return lagrange multiplier - const Vec2 & GetTotalLambda() const - { - return mTotalLambda; - } - - /// Save state of this constraint part - void SaveState(StateRecorder &inStream) const - { - inStream.Write(mTotalLambda); - } - - /// Restore state of this constraint part - void RestoreState(StateRecorder &inStream) - { - inStream.Read(mTotalLambda); - } - -private: - Vec3 mA1; ///< World space hinge axis for body 1 - Vec3 mB2; ///< World space perpendiculars of hinge axis for body 2 - Vec3 mC2; - Mat44 mInvI1; - Mat44 mInvI2; - Vec3 mB2xA1; - Vec3 mC2xA1; - Mat22 mEffectiveMass; - Vec2 mTotalLambda { Vec2::sZero() }; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/IndependentAxisConstraintPart.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/IndependentAxisConstraintPart.h deleted file mode 100644 index 5b752c6c693..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/IndependentAxisConstraintPart.h +++ /dev/null @@ -1,246 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2022 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include - -JPH_NAMESPACE_BEGIN - -/// Constraint part to an AxisConstraintPart but both bodies have an independent axis on which the force is applied. -/// -/// Constraint equation: -/// -/// \f[C = (x_1 + r_1 - f_1) . n_1 + r (x_2 + r_2 - f_2) \cdot n_2\f] -/// -/// Calculating the Jacobian: -/// -/// \f[dC/dt = (v_1 + w_1 \times r_1) \cdot n_1 + (x_1 + r_1 - f_1) \cdot d n_1/dt + r (v_2 + w_2 \times r_2) \cdot n_2 + r (x_2 + r_2 - f_2) \cdot d n_2/dt\f] -/// -/// Assuming that d n1/dt and d n2/dt are small this becomes: -/// -/// \f[(v_1 + w_1 \times r_1) \cdot n_1 + r (v_2 + w_2 \times r_2) \cdot n_2\f] -/// \f[= v_1 \cdot n_1 + r_1 \times n_1 \cdot w_1 + r v_2 \cdot n_2 + r r_2 \times n_2 \cdot w_2\f] -/// -/// Jacobian: -/// -/// \f[J = \begin{bmatrix}n_1 & r_1 \times n_1 & r n_2 & r r_2 \times n_2\end{bmatrix}\f] -/// -/// Effective mass: -/// -/// \f[K = m_1^{-1} + r_1 \times n_1 I_1^{-1} r_1 \times n_1 + r^2 m_2^{-1} + r^2 r_2 \times n_2 I_2^{-1} r_2 \times n_2\f] -/// -/// Used terms (here and below, everything in world space):\n -/// n1 = (x1 + r1 - f1) / |x1 + r1 - f1|, axis along which the force is applied for body 1\n -/// n2 = (x2 + r2 - f2) / |x2 + r2 - f2|, axis along which the force is applied for body 2\n -/// r = ratio how forces are applied between bodies.\n -/// x1, x2 = center of mass for the bodies.\n -/// v = [v1, w1, v2, w2].\n -/// v1, v2 = linear velocity of body 1 and 2.\n -/// w1, w2 = angular velocity of body 1 and 2.\n -/// M = mass matrix, a diagonal matrix of the mass and inertia with diagonal [m1, I1, m2, I2].\n -/// \f$K^{-1} = \left( J M^{-1} J^T \right)^{-1}\f$ = effective mass.\n -/// b = velocity bias.\n -/// \f$\beta\f$ = baumgarte constant. -class IndependentAxisConstraintPart -{ - /// Internal helper function to update velocities of bodies after Lagrange multiplier is calculated - JPH_INLINE bool ApplyVelocityStep(Body &ioBody1, Body &ioBody2, Vec3Arg inN1, Vec3Arg inN2, float inRatio, float inLambda) const - { - // Apply impulse if delta is not zero - if (inLambda != 0.0f) - { - // Calculate velocity change due to constraint - // - // Impulse: - // P = J^T lambda - // - // Euler velocity integration: - // v' = v + M^-1 P - if (ioBody1.IsDynamic()) - { - MotionProperties *mp1 = ioBody1.GetMotionProperties(); - mp1->AddLinearVelocityStep((mp1->GetInverseMass() * inLambda) * inN1); - mp1->AddAngularVelocityStep(mInvI1_R1xN1 * inLambda); - } - if (ioBody2.IsDynamic()) - { - MotionProperties *mp2 = ioBody2.GetMotionProperties(); - mp2->AddLinearVelocityStep((inRatio * mp2->GetInverseMass() * inLambda) * inN2); - mp2->AddAngularVelocityStep(mInvI2_RatioR2xN2 * inLambda); - } - return true; - } - - return false; - } - -public: - /// Calculate properties used during the functions below - /// @param inBody1 The first body that this constraint is attached to - /// @param inBody2 The second body that this constraint is attached to - /// @param inR1 The position on which the constraint operates on body 1 relative to COM - /// @param inN1 The world space normal in which the constraint operates for body 1 - /// @param inR2 The position on which the constraint operates on body 1 relative to COM - /// @param inN2 The world space normal in which the constraint operates for body 2 - /// @param inRatio The ratio how forces are applied between bodies - inline void CalculateConstraintProperties(const Body &inBody1, const Body &inBody2, Vec3Arg inR1, Vec3Arg inN1, Vec3Arg inR2, Vec3Arg inN2, float inRatio) - { - JPH_ASSERT(inN1.IsNormalized(1.0e-4f) && inN2.IsNormalized(1.0e-4f)); - - float inv_effective_mass = 0.0f; - - if (!inBody1.IsStatic()) - { - const MotionProperties *mp1 = inBody1.GetMotionProperties(); - - mR1xN1 = inR1.Cross(inN1); - mInvI1_R1xN1 = mp1->MultiplyWorldSpaceInverseInertiaByVector(inBody1.GetRotation(), mR1xN1); - - inv_effective_mass += mp1->GetInverseMass() + mInvI1_R1xN1.Dot(mR1xN1); - } - - if (!inBody2.IsStatic()) - { - const MotionProperties *mp2 = inBody2.GetMotionProperties(); - - mRatioR2xN2 = inRatio * inR2.Cross(inN2); - mInvI2_RatioR2xN2 = mp2->MultiplyWorldSpaceInverseInertiaByVector(inBody2.GetRotation(), mRatioR2xN2); - - inv_effective_mass += Square(inRatio) * mp2->GetInverseMass() + mInvI2_RatioR2xN2.Dot(mRatioR2xN2); - } - - // Calculate inverse effective mass: K = J M^-1 J^T - if (inv_effective_mass == 0.0f) - Deactivate(); - else - mEffectiveMass = 1.0f / inv_effective_mass; - } - - /// Deactivate this constraint - inline void Deactivate() - { - mEffectiveMass = 0.0f; - mTotalLambda = 0.0f; - } - - /// Check if constraint is active - inline bool IsActive() const - { - return mEffectiveMass != 0.0f; - } - - /// Must be called from the WarmStartVelocityConstraint call to apply the previous frame's impulses - /// @param ioBody1 The first body that this constraint is attached to - /// @param ioBody2 The second body that this constraint is attached to - /// @param inN1 The world space normal in which the constraint operates for body 1 - /// @param inN2 The world space normal in which the constraint operates for body 2 - /// @param inRatio The ratio how forces are applied between bodies - /// @param inWarmStartImpulseRatio Ratio of new step to old time step (dt_new / dt_old) for scaling the lagrange multiplier of the previous frame - inline void WarmStart(Body &ioBody1, Body &ioBody2, Vec3Arg inN1, Vec3Arg inN2, float inRatio, float inWarmStartImpulseRatio) - { - mTotalLambda *= inWarmStartImpulseRatio; - ApplyVelocityStep(ioBody1, ioBody2, inN1, inN2, inRatio, mTotalLambda); - } - - /// Iteratively update the velocity constraint. Makes sure d/dt C(...) = 0, where C is the constraint equation. - /// @param ioBody1 The first body that this constraint is attached to - /// @param ioBody2 The second body that this constraint is attached to - /// @param inN1 The world space normal in which the constraint operates for body 1 - /// @param inN2 The world space normal in which the constraint operates for body 2 - /// @param inRatio The ratio how forces are applied between bodies - /// @param inMinLambda Minimum angular impulse to apply (N m s) - /// @param inMaxLambda Maximum angular impulse to apply (N m s) - inline bool SolveVelocityConstraint(Body &ioBody1, Body &ioBody2, Vec3Arg inN1, Vec3Arg inN2, float inRatio, float inMinLambda, float inMaxLambda) - { - // Lagrange multiplier is: - // - // lambda = -K^-1 (J v + b) - float lambda = -mEffectiveMass * (inN1.Dot(ioBody1.GetLinearVelocity()) + mR1xN1.Dot(ioBody1.GetAngularVelocity()) + inRatio * inN2.Dot(ioBody2.GetLinearVelocity()) + mRatioR2xN2.Dot(ioBody2.GetAngularVelocity())); - float new_lambda = Clamp(mTotalLambda + lambda, inMinLambda, inMaxLambda); // Clamp impulse - lambda = new_lambda - mTotalLambda; // Lambda potentially got clamped, calculate the new impulse to apply - mTotalLambda = new_lambda; // Store accumulated impulse - - return ApplyVelocityStep(ioBody1, ioBody2, inN1, inN2, inRatio, lambda); - } - - /// Return lagrange multiplier - float GetTotalLambda() const - { - return mTotalLambda; - } - - /// Iteratively update the position constraint. Makes sure C(...) == 0. - /// @param ioBody1 The first body that this constraint is attached to - /// @param ioBody2 The second body that this constraint is attached to - /// @param inN1 The world space normal in which the constraint operates for body 1 - /// @param inN2 The world space normal in which the constraint operates for body 2 - /// @param inRatio The ratio how forces are applied between bodies - /// @param inC Value of the constraint equation (C) - /// @param inBaumgarte Baumgarte constant (fraction of the error to correct) - inline bool SolvePositionConstraint(Body &ioBody1, Body &ioBody2, Vec3Arg inN1, Vec3Arg inN2, float inRatio, float inC, float inBaumgarte) const - { - if (inC != 0.0f) - { - // Calculate lagrange multiplier (lambda) for Baumgarte stabilization: - // - // lambda = -K^-1 * beta / dt * C - // - // We should divide by inDeltaTime, but we should multiply by inDeltaTime in the Euler step below so they're cancelled out - float lambda = -mEffectiveMass * inBaumgarte * inC; - - // Directly integrate velocity change for one time step - // - // Euler velocity integration: - // dv = M^-1 P - // - // Impulse: - // P = J^T lambda - // - // Euler position integration: - // x' = x + dv * dt - // - // Note we don't accumulate velocities for the stabilization. This is using the approach described in 'Modeling and - // Solving Constraints' by Erin Catto presented at GDC 2007. On slide 78 it is suggested to split up the Baumgarte - // stabilization for positional drift so that it does not actually add to the momentum. We combine an Euler velocity - // integrate + a position integrate and then discard the velocity change. - if (ioBody1.IsDynamic()) - { - ioBody1.AddPositionStep((lambda * ioBody1.GetMotionPropertiesUnchecked()->GetInverseMass()) * inN1); - ioBody1.AddRotationStep(lambda * mInvI1_R1xN1); - } - if (ioBody2.IsDynamic()) - { - ioBody2.AddPositionStep((lambda * inRatio * ioBody2.GetMotionPropertiesUnchecked()->GetInverseMass()) * inN2); - ioBody2.AddRotationStep(lambda * mInvI2_RatioR2xN2); - } - return true; - } - - return false; - } - - /// Save state of this constraint part - void SaveState(StateRecorder &inStream) const - { - inStream.Write(mTotalLambda); - } - - /// Restore state of this constraint part - void RestoreState(StateRecorder &inStream) - { - inStream.Read(mTotalLambda); - } - -private: - Vec3 mR1xN1; - Vec3 mInvI1_R1xN1; - Vec3 mRatioR2xN2; - Vec3 mInvI2_RatioR2xN2; - float mEffectiveMass = 0.0f; - float mTotalLambda = 0.0f; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/PointConstraintPart.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/PointConstraintPart.h deleted file mode 100644 index 62f663f9e5a..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/PointConstraintPart.h +++ /dev/null @@ -1,239 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include - -JPH_NAMESPACE_BEGIN - -/// Constrains movement along 3 axis -/// -/// @see "Constraints Derivation for Rigid Body Simulation in 3D" - Daniel Chappuis, section 2.2.1 -/// -/// Constraint equation (eq 45): -/// -/// \f[C = p_2 - p_1\f] -/// -/// Jacobian (transposed) (eq 47): -/// -/// \f[J^T = \begin{bmatrix}-E & r1x & E & -r2x^T\end{bmatrix} -/// = \begin{bmatrix}-E^T \\ r1x^T \\ E^T \\ -r2x^T\end{bmatrix} -/// = \begin{bmatrix}-E \\ -r1x \\ E \\ r2x\end{bmatrix}\f] -/// -/// Used terms (here and below, everything in world space):\n -/// p1, p2 = constraint points.\n -/// r1 = p1 - x1.\n -/// r2 = p2 - x2.\n -/// r1x = 3x3 matrix for which r1x v = r1 x v (cross product).\n -/// x1, x2 = center of mass for the bodies.\n -/// v = [v1, w1, v2, w2].\n -/// v1, v2 = linear velocity of body 1 and 2.\n -/// w1, w2 = angular velocity of body 1 and 2.\n -/// M = mass matrix, a diagonal matrix of the mass and inertia with diagonal [m1, I1, m2, I2].\n -/// \f$K^{-1} = \left( J M^{-1} J^T \right)^{-1}\f$ = effective mass.\n -/// b = velocity bias.\n -/// \f$\beta\f$ = baumgarte constant.\n -/// E = identity matrix. -class PointConstraintPart -{ - JPH_INLINE bool ApplyVelocityStep(Body &ioBody1, Body &ioBody2, Vec3Arg inLambda) const - { - // Apply impulse if delta is not zero - if (inLambda != Vec3::sZero()) - { - // Calculate velocity change due to constraint - // - // Impulse: - // P = J^T lambda - // - // Euler velocity integration: - // v' = v + M^-1 P - if (ioBody1.IsDynamic()) - { - MotionProperties *mp1 = ioBody1.GetMotionProperties(); - mp1->SubLinearVelocityStep(mp1->GetInverseMass() * inLambda); - mp1->SubAngularVelocityStep(mInvI1_R1X * inLambda); - } - if (ioBody2.IsDynamic()) - { - MotionProperties *mp2 = ioBody2.GetMotionProperties(); - mp2->AddLinearVelocityStep(mp2->GetInverseMass() * inLambda); - mp2->AddAngularVelocityStep(mInvI2_R2X * inLambda); - } - return true; - } - - return false; - } - -public: - /// Calculate properties used during the functions below - /// @param inBody1 The first body that this constraint is attached to - /// @param inBody2 The second body that this constraint is attached to - /// @param inRotation1 The 3x3 rotation matrix for body 1 (translation part is ignored) - /// @param inRotation2 The 3x3 rotation matrix for body 2 (translation part is ignored) - /// @param inR1 Local space vector from center of mass to constraint point for body 1 - /// @param inR2 Local space vector from center of mass to constraint point for body 2 - inline void CalculateConstraintProperties(const Body &inBody1, Mat44Arg inRotation1, Vec3Arg inR1, const Body &inBody2, Mat44Arg inRotation2, Vec3Arg inR2) - { - // Positions where the point constraint acts on (middle point between center of masses) in world space - mR1 = inRotation1.Multiply3x3(inR1); - mR2 = inRotation2.Multiply3x3(inR2); - - // Calculate effective mass: K^-1 = (J M^-1 J^T)^-1 - // Using: I^-1 = R * Ibody^-1 * R^T - float summed_inv_mass; - Mat44 inv_effective_mass; - if (inBody1.IsDynamic()) - { - const MotionProperties *mp1 = inBody1.GetMotionProperties(); - Mat44 inv_i1 = mp1->GetInverseInertiaForRotation(inRotation1); - summed_inv_mass = mp1->GetInverseMass(); - - Mat44 r1x = Mat44::sCrossProduct(mR1); - mInvI1_R1X = inv_i1.Multiply3x3(r1x); - inv_effective_mass = r1x.Multiply3x3(inv_i1).Multiply3x3RightTransposed(r1x); - } - else - { - JPH_IF_DEBUG(mInvI1_R1X = Mat44::sNaN();) - - summed_inv_mass = 0.0f; - inv_effective_mass = Mat44::sZero(); - } - - if (inBody2.IsDynamic()) - { - const MotionProperties *mp2 = inBody2.GetMotionProperties(); - Mat44 inv_i2 = mp2->GetInverseInertiaForRotation(inRotation2); - summed_inv_mass += mp2->GetInverseMass(); - - Mat44 r2x = Mat44::sCrossProduct(mR2); - mInvI2_R2X = inv_i2.Multiply3x3(r2x); - inv_effective_mass += r2x.Multiply3x3(inv_i2).Multiply3x3RightTransposed(r2x); - } - else - { - JPH_IF_DEBUG(mInvI2_R2X = Mat44::sNaN();) - } - - inv_effective_mass += Mat44::sScale(summed_inv_mass); - if (!mEffectiveMass.SetInversed3x3(inv_effective_mass)) - Deactivate(); - } - - /// Deactivate this constraint - inline void Deactivate() - { - mEffectiveMass = Mat44::sZero(); - mTotalLambda = Vec3::sZero(); - } - - /// Check if constraint is active - inline bool IsActive() const - { - return mEffectiveMass(3, 3) != 0.0f; - } - - /// Must be called from the WarmStartVelocityConstraint call to apply the previous frame's impulses - /// @param ioBody1 The first body that this constraint is attached to - /// @param ioBody2 The second body that this constraint is attached to - /// @param inWarmStartImpulseRatio Ratio of new step to old time step (dt_new / dt_old) for scaling the lagrange multiplier of the previous frame - inline void WarmStart(Body &ioBody1, Body &ioBody2, float inWarmStartImpulseRatio) - { - mTotalLambda *= inWarmStartImpulseRatio; - ApplyVelocityStep(ioBody1, ioBody2, mTotalLambda); - } - - /// Iteratively update the velocity constraint. Makes sure d/dt C(...) = 0, where C is the constraint equation. - /// @param ioBody1 The first body that this constraint is attached to - /// @param ioBody2 The second body that this constraint is attached to - inline bool SolveVelocityConstraint(Body &ioBody1, Body &ioBody2) - { - // Calculate lagrange multiplier: - // - // lambda = -K^-1 (J v + b) - Vec3 lambda = mEffectiveMass * (ioBody1.GetLinearVelocity() - mR1.Cross(ioBody1.GetAngularVelocity()) - ioBody2.GetLinearVelocity() + mR2.Cross(ioBody2.GetAngularVelocity())); - mTotalLambda += lambda; // Store accumulated lambda - return ApplyVelocityStep(ioBody1, ioBody2, lambda); - } - - /// Iteratively update the position constraint. Makes sure C(...) = 0. - /// @param ioBody1 The first body that this constraint is attached to - /// @param ioBody2 The second body that this constraint is attached to - /// @param inBaumgarte Baumgarte constant (fraction of the error to correct) - inline bool SolvePositionConstraint(Body &ioBody1, Body &ioBody2, float inBaumgarte) const - { - Vec3 separation = (Vec3(ioBody2.GetCenterOfMassPosition() - ioBody1.GetCenterOfMassPosition()) + mR2 - mR1); - if (separation != Vec3::sZero()) - { - // Calculate lagrange multiplier (lambda) for Baumgarte stabilization: - // - // lambda = -K^-1 * beta / dt * C - // - // We should divide by inDeltaTime, but we should multiply by inDeltaTime in the Euler step below so they're cancelled out - Vec3 lambda = mEffectiveMass * -inBaumgarte * separation; - - // Directly integrate velocity change for one time step - // - // Euler velocity integration: - // dv = M^-1 P - // - // Impulse: - // P = J^T lambda - // - // Euler position integration: - // x' = x + dv * dt - // - // Note we don't accumulate velocities for the stabilization. This is using the approach described in 'Modeling and - // Solving Constraints' by Erin Catto presented at GDC 2007. On slide 78 it is suggested to split up the Baumgarte - // stabilization for positional drift so that it does not actually add to the momentum. We combine an Euler velocity - // integrate + a position integrate and then discard the velocity change. - if (ioBody1.IsDynamic()) - { - ioBody1.SubPositionStep(ioBody1.GetMotionProperties()->GetInverseMass() * lambda); - ioBody1.SubRotationStep(mInvI1_R1X * lambda); - } - if (ioBody2.IsDynamic()) - { - ioBody2.AddPositionStep(ioBody2.GetMotionProperties()->GetInverseMass() * lambda); - ioBody2.AddRotationStep(mInvI2_R2X * lambda); - } - - return true; - } - - return false; - } - - /// Return lagrange multiplier - Vec3 GetTotalLambda() const - { - return mTotalLambda; - } - - /// Save state of this constraint part - void SaveState(StateRecorder &inStream) const - { - inStream.Write(mTotalLambda); - } - - /// Restore state of this constraint part - void RestoreState(StateRecorder &inStream) - { - inStream.Read(mTotalLambda); - } - -private: - Vec3 mR1; - Vec3 mR2; - Mat44 mInvI1_R1X; - Mat44 mInvI2_R2X; - Mat44 mEffectiveMass; - Vec3 mTotalLambda { Vec3::sZero() }; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/RackAndPinionConstraintPart.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/RackAndPinionConstraintPart.h deleted file mode 100644 index 641f4867a11..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/RackAndPinionConstraintPart.h +++ /dev/null @@ -1,196 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include - -JPH_NAMESPACE_BEGIN - -/// Constraint that constrains a rotation to a translation -/// -/// Constraint equation: -/// -/// C = Theta(t) - r d(t) -/// -/// Derivative: -/// -/// d/dt C = 0 -/// <=> w1 . a - r v2 . b = 0 -/// -/// Jacobian: -/// -/// \f[J = \begin{bmatrix}0 & a^T & -r b^T & 0\end{bmatrix}\f] -/// -/// Used terms (here and below, everything in world space):\n -/// a = axis around which body 1 rotates (normalized).\n -/// b = axis along which body 2 slides (normalized).\n -/// Theta(t) = rotation around a of body 1.\n -/// d(t) = distance body 2 slides.\n -/// r = ratio between rotation and translation.\n -/// v = [v1, w1, v2, w2].\n -/// v1, v2 = linear velocity of body 1 and 2.\n -/// w1, w2 = angular velocity of body 1 and 2.\n -/// M = mass matrix, a diagonal matrix of the mass and inertia with diagonal [m1, I1, m2, I2].\n -/// \f$K^{-1} = \left( J M^{-1} J^T \right)^{-1}\f$ = effective mass.\n -/// \f$\beta\f$ = baumgarte constant. -class RackAndPinionConstraintPart -{ - /// Internal helper function to update velocities of bodies after Lagrange multiplier is calculated - JPH_INLINE bool ApplyVelocityStep(Body &ioBody1, Body &ioBody2, float inLambda) const - { - // Apply impulse if delta is not zero - if (inLambda != 0.0f) - { - // Calculate velocity change due to constraint - // - // Impulse: - // P = J^T lambda - // - // Euler velocity integration: - // v' = v + M^-1 P - ioBody1.GetMotionProperties()->AddAngularVelocityStep(inLambda * mInvI1_A); - ioBody2.GetMotionProperties()->SubLinearVelocityStep(inLambda * mRatio_InvM2_B); - return true; - } - - return false; - } - -public: - /// Calculate properties used during the functions below - /// @param inBody1 The first body that this constraint is attached to - /// @param inBody2 The second body that this constraint is attached to - /// @param inWorldSpaceHingeAxis The axis around which body 1 rotates - /// @param inWorldSpaceSliderAxis The axis along which body 2 slides - /// @param inRatio The ratio between rotation and translation - inline void CalculateConstraintProperties(const Body &inBody1, Vec3Arg inWorldSpaceHingeAxis, const Body &inBody2, Vec3Arg inWorldSpaceSliderAxis, float inRatio) - { - JPH_ASSERT(inWorldSpaceHingeAxis.IsNormalized(1.0e-4f)); - JPH_ASSERT(inWorldSpaceSliderAxis.IsNormalized(1.0e-4f)); - - // Calculate: I1^-1 a - mInvI1_A = inBody1.GetMotionProperties()->MultiplyWorldSpaceInverseInertiaByVector(inBody1.GetRotation(), inWorldSpaceHingeAxis); - - // Calculate: r/m2 b - float inv_m2 = inBody2.GetMotionProperties()->GetInverseMass(); - mRatio_InvM2_B = inRatio * inv_m2 * inWorldSpaceSliderAxis; - - // K^-1 = 1 / (J M^-1 J^T) = 1 / (a^T I1^-1 a + 1/m2 * r^2 * b . b) - float inv_effective_mass = (inWorldSpaceHingeAxis.Dot(mInvI1_A) + inv_m2 * Square(inRatio)); - if (inv_effective_mass == 0.0f) - Deactivate(); - else - mEffectiveMass = 1.0f / inv_effective_mass; - } - - /// Deactivate this constraint - inline void Deactivate() - { - mEffectiveMass = 0.0f; - mTotalLambda = 0.0f; - } - - /// Check if constraint is active - inline bool IsActive() const - { - return mEffectiveMass != 0.0f; - } - - /// Must be called from the WarmStartVelocityConstraint call to apply the previous frame's impulses - /// @param ioBody1 The first body that this constraint is attached to - /// @param ioBody2 The second body that this constraint is attached to - /// @param inWarmStartImpulseRatio Ratio of new step to old time step (dt_new / dt_old) for scaling the lagrange multiplier of the previous frame - inline void WarmStart(Body &ioBody1, Body &ioBody2, float inWarmStartImpulseRatio) - { - mTotalLambda *= inWarmStartImpulseRatio; - ApplyVelocityStep(ioBody1, ioBody2, mTotalLambda); - } - - /// Iteratively update the velocity constraint. Makes sure d/dt C(...) = 0, where C is the constraint equation. - /// @param ioBody1 The first body that this constraint is attached to - /// @param ioBody2 The second body that this constraint is attached to - /// @param inWorldSpaceHingeAxis The axis around which body 1 rotates - /// @param inWorldSpaceSliderAxis The axis along which body 2 slides - /// @param inRatio The ratio between rotation and translation - inline bool SolveVelocityConstraint(Body &ioBody1, Vec3Arg inWorldSpaceHingeAxis, Body &ioBody2, Vec3Arg inWorldSpaceSliderAxis, float inRatio) - { - // Lagrange multiplier is: - // - // lambda = -K^-1 (J v + b) - float lambda = mEffectiveMass * (inRatio * inWorldSpaceSliderAxis.Dot(ioBody2.GetLinearVelocity()) - inWorldSpaceHingeAxis.Dot(ioBody1.GetAngularVelocity())); - mTotalLambda += lambda; // Store accumulated impulse - - return ApplyVelocityStep(ioBody1, ioBody2, lambda); - } - - /// Return lagrange multiplier - float GetTotalLambda() const - { - return mTotalLambda; - } - - /// Iteratively update the position constraint. Makes sure C(...) == 0. - /// @param ioBody1 The first body that this constraint is attached to - /// @param ioBody2 The second body that this constraint is attached to - /// @param inC Value of the constraint equation (C) - /// @param inBaumgarte Baumgarte constant (fraction of the error to correct) - inline bool SolvePositionConstraint(Body &ioBody1, Body &ioBody2, float inC, float inBaumgarte) const - { - // Only apply position constraint when the constraint is hard, otherwise the velocity bias will fix the constraint - if (inC != 0.0f) - { - // Calculate lagrange multiplier (lambda) for Baumgarte stabilization: - // - // lambda = -K^-1 * beta / dt * C - // - // We should divide by inDeltaTime, but we should multiply by inDeltaTime in the Euler step below so they're cancelled out - float lambda = -mEffectiveMass * inBaumgarte * inC; - - // Directly integrate velocity change for one time step - // - // Euler velocity integration: - // dv = M^-1 P - // - // Impulse: - // P = J^T lambda - // - // Euler position integration: - // x' = x + dv * dt - // - // Note we don't accumulate velocities for the stabilization. This is using the approach described in 'Modeling and - // Solving Constraints' by Erin Catto presented at GDC 2007. On slide 78 it is suggested to split up the Baumgarte - // stabilization for positional drift so that it does not actually add to the momentum. We combine an Euler velocity - // integrate + a position integrate and then discard the velocity change. - if (ioBody1.IsDynamic()) - ioBody1.AddRotationStep(lambda * mInvI1_A); - if (ioBody2.IsDynamic()) - ioBody2.SubPositionStep(lambda * mRatio_InvM2_B); - return true; - } - - return false; - } - - /// Save state of this constraint part - void SaveState(StateRecorder &inStream) const - { - inStream.Write(mTotalLambda); - } - - /// Restore state of this constraint part - void RestoreState(StateRecorder &inStream) - { - inStream.Read(mTotalLambda); - } - -private: - Vec3 mInvI1_A; - Vec3 mRatio_InvM2_B; - float mEffectiveMass = 0.0f; - float mTotalLambda = 0.0f; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/RotationEulerConstraintPart.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/RotationEulerConstraintPart.h deleted file mode 100644 index 34319e980f3..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/RotationEulerConstraintPart.h +++ /dev/null @@ -1,270 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include - -JPH_NAMESPACE_BEGIN - -/// Constrains rotation around all axis so that only translation is allowed -/// -/// Based on: "Constraints Derivation for Rigid Body Simulation in 3D" - Daniel Chappuis, section 2.5.1 -/// -/// Constraint equation (eq 129): -/// -/// \f[C = \begin{bmatrix}\Delta\theta_x, \Delta\theta_y, \Delta\theta_z\end{bmatrix}\f] -/// -/// Jacobian (eq 131): -/// -/// \f[J = \begin{bmatrix}0 & -E & 0 & E\end{bmatrix}\f] -/// -/// Used terms (here and below, everything in world space):\n -/// delta_theta_* = difference in rotation between initial rotation of bodies 1 and 2.\n -/// x1, x2 = center of mass for the bodies.\n -/// v = [v1, w1, v2, w2].\n -/// v1, v2 = linear velocity of body 1 and 2.\n -/// w1, w2 = angular velocity of body 1 and 2.\n -/// M = mass matrix, a diagonal matrix of the mass and inertia with diagonal [m1, I1, m2, I2].\n -/// \f$K^{-1} = \left( J M^{-1} J^T \right)^{-1}\f$ = effective mass.\n -/// b = velocity bias.\n -/// \f$\beta\f$ = baumgarte constant.\n -/// E = identity matrix.\n -class RotationEulerConstraintPart -{ -private: - /// Internal helper function to update velocities of bodies after Lagrange multiplier is calculated - JPH_INLINE bool ApplyVelocityStep(Body &ioBody1, Body &ioBody2, Vec3Arg inLambda) const - { - // Apply impulse if delta is not zero - if (inLambda != Vec3::sZero()) - { - // Calculate velocity change due to constraint - // - // Impulse: - // P = J^T lambda - // - // Euler velocity integration: - // v' = v + M^-1 P - if (ioBody1.IsDynamic()) - ioBody1.GetMotionProperties()->SubAngularVelocityStep(mInvI1.Multiply3x3(inLambda)); - if (ioBody2.IsDynamic()) - ioBody2.GetMotionProperties()->AddAngularVelocityStep(mInvI2.Multiply3x3(inLambda)); - return true; - } - - return false; - } - -public: - /// Return inverse of initial rotation from body 1 to body 2 in body 1 space - static Quat sGetInvInitialOrientation(const Body &inBody1, const Body &inBody2) - { - // q20 = q10 r0 - // <=> r0 = q10^-1 q20 - // <=> r0^-1 = q20^-1 q10 - // - // where: - // - // q20 = initial orientation of body 2 - // q10 = initial orientation of body 1 - // r0 = initial rotation from body 1 to body 2 - return inBody2.GetRotation().Conjugated() * inBody1.GetRotation(); - } - - /// @brief Return inverse of initial rotation from body 1 to body 2 in body 1 space - /// @param inAxisX1 Reference axis X for body 1 - /// @param inAxisY1 Reference axis Y for body 1 - /// @param inAxisX2 Reference axis X for body 2 - /// @param inAxisY2 Reference axis Y for body 2 - static Quat sGetInvInitialOrientationXY(Vec3Arg inAxisX1, Vec3Arg inAxisY1, Vec3Arg inAxisX2, Vec3Arg inAxisY2) - { - // Store inverse of initial rotation from body 1 to body 2 in body 1 space: - // - // q20 = q10 r0 - // <=> r0 = q10^-1 q20 - // <=> r0^-1 = q20^-1 q10 - // - // where: - // - // q10, q20 = world space initial orientation of body 1 and 2 - // r0 = initial rotation from body 1 to body 2 in local space of body 1 - // - // We can also write this in terms of the constraint matrices: - // - // q20 c2 = q10 c1 - // <=> q20 = q10 c1 c2^-1 - // => r0 = c1 c2^-1 - // <=> r0^-1 = c2 c1^-1 - // - // where: - // - // c1, c2 = matrix that takes us from body 1 and 2 COM to constraint space 1 and 2 - if (inAxisX1 == inAxisX2 && inAxisY1 == inAxisY2) - { - // Axis are the same -> identity transform - return Quat::sIdentity(); - } - else - { - Mat44 constraint1(Vec4(inAxisX1, 0), Vec4(inAxisY1, 0), Vec4(inAxisX1.Cross(inAxisY1), 0), Vec4(0, 0, 0, 1)); - Mat44 constraint2(Vec4(inAxisX2, 0), Vec4(inAxisY2, 0), Vec4(inAxisX2.Cross(inAxisY2), 0), Vec4(0, 0, 0, 1)); - return constraint2.GetQuaternion() * constraint1.GetQuaternion().Conjugated(); - } - } - - /// @brief Return inverse of initial rotation from body 1 to body 2 in body 1 space - /// @param inAxisX1 Reference axis X for body 1 - /// @param inAxisZ1 Reference axis Z for body 1 - /// @param inAxisX2 Reference axis X for body 2 - /// @param inAxisZ2 Reference axis Z for body 2 - static Quat sGetInvInitialOrientationXZ(Vec3Arg inAxisX1, Vec3Arg inAxisZ1, Vec3Arg inAxisX2, Vec3Arg inAxisZ2) - { - // See comment at sGetInvInitialOrientationXY - if (inAxisX1 == inAxisX2 && inAxisZ1 == inAxisZ2) - { - return Quat::sIdentity(); - } - else - { - Mat44 constraint1(Vec4(inAxisX1, 0), Vec4(inAxisZ1.Cross(inAxisX1), 0), Vec4(inAxisZ1, 0), Vec4(0, 0, 0, 1)); - Mat44 constraint2(Vec4(inAxisX2, 0), Vec4(inAxisZ2.Cross(inAxisX2), 0), Vec4(inAxisZ2, 0), Vec4(0, 0, 0, 1)); - return constraint2.GetQuaternion() * constraint1.GetQuaternion().Conjugated(); - } - } - - /// Calculate properties used during the functions below - inline void CalculateConstraintProperties(const Body &inBody1, Mat44Arg inRotation1, const Body &inBody2, Mat44Arg inRotation2) - { - // Calculate properties used during constraint solving - mInvI1 = inBody1.IsDynamic()? inBody1.GetMotionProperties()->GetInverseInertiaForRotation(inRotation1) : Mat44::sZero(); - mInvI2 = inBody2.IsDynamic()? inBody2.GetMotionProperties()->GetInverseInertiaForRotation(inRotation2) : Mat44::sZero(); - - // Calculate effective mass: K^-1 = (J M^-1 J^T)^-1 - if (!mEffectiveMass.SetInversed3x3(mInvI1 + mInvI2)) - Deactivate(); - } - - /// Deactivate this constraint - inline void Deactivate() - { - mEffectiveMass = Mat44::sZero(); - mTotalLambda = Vec3::sZero(); - } - - /// Check if constraint is active - inline bool IsActive() const - { - return mEffectiveMass(3, 3) != 0.0f; - } - - /// Must be called from the WarmStartVelocityConstraint call to apply the previous frame's impulses - inline void WarmStart(Body &ioBody1, Body &ioBody2, float inWarmStartImpulseRatio) - { - mTotalLambda *= inWarmStartImpulseRatio; - ApplyVelocityStep(ioBody1, ioBody2, mTotalLambda); - } - - /// Iteratively update the velocity constraint. Makes sure d/dt C(...) = 0, where C is the constraint equation. - inline bool SolveVelocityConstraint(Body &ioBody1, Body &ioBody2) - { - // Calculate lagrange multiplier: - // - // lambda = -K^-1 (J v + b) - Vec3 lambda = mEffectiveMass.Multiply3x3(ioBody1.GetAngularVelocity() - ioBody2.GetAngularVelocity()); - mTotalLambda += lambda; - return ApplyVelocityStep(ioBody1, ioBody2, lambda); - } - - /// Iteratively update the position constraint. Makes sure C(...) = 0. - inline bool SolvePositionConstraint(Body &ioBody1, Body &ioBody2, QuatArg inInvInitialOrientation, float inBaumgarte) const - { - // Calculate difference in rotation - // - // The rotation should be: - // - // q2 = q1 r0 - // - // But because of drift the actual rotation is - // - // q2 = diff q1 r0 - // <=> diff = q2 r0^-1 q1^-1 - // - // Where: - // q1 = current rotation of body 1 - // q2 = current rotation of body 2 - // diff = error that needs to be reduced to zero - Quat diff = ioBody2.GetRotation() * inInvInitialOrientation * ioBody1.GetRotation().Conjugated(); - - // A quaternion can be seen as: - // - // q = [sin(theta / 2) * v, cos(theta/2)] - // - // Where: - // v = rotation vector - // theta = rotation angle - // - // If we assume theta is small (error is small) then sin(x) = x so an approximation of the error angles is: - Vec3 error = 2.0f * diff.EnsureWPositive().GetXYZ(); - if (error != Vec3::sZero()) - { - // Calculate lagrange multiplier (lambda) for Baumgarte stabilization: - // - // lambda = -K^-1 * beta / dt * C - // - // We should divide by inDeltaTime, but we should multiply by inDeltaTime in the Euler step below so they're cancelled out - Vec3 lambda = -inBaumgarte * mEffectiveMass * error; - - // Directly integrate velocity change for one time step - // - // Euler velocity integration: - // dv = M^-1 P - // - // Impulse: - // P = J^T lambda - // - // Euler position integration: - // x' = x + dv * dt - // - // Note we don't accumulate velocities for the stabilization. This is using the approach described in 'Modeling and - // Solving Constraints' by Erin Catto presented at GDC 2007. On slide 78 it is suggested to split up the Baumgarte - // stabilization for positional drift so that it does not actually add to the momentum. We combine an Euler velocity - // integrate + a position integrate and then discard the velocity change. - if (ioBody1.IsDynamic()) - ioBody1.SubRotationStep(mInvI1.Multiply3x3(lambda)); - if (ioBody2.IsDynamic()) - ioBody2.AddRotationStep(mInvI2.Multiply3x3(lambda)); - return true; - } - - return false; - } - - /// Return lagrange multiplier - Vec3 GetTotalLambda() const - { - return mTotalLambda; - } - - /// Save state of this constraint part - void SaveState(StateRecorder &inStream) const - { - inStream.Write(mTotalLambda); - } - - /// Restore state of this constraint part - void RestoreState(StateRecorder &inStream) - { - inStream.Read(mTotalLambda); - } - -private: - Mat44 mInvI1; - Mat44 mInvI2; - Mat44 mEffectiveMass; - Vec3 mTotalLambda { Vec3::sZero() }; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/RotationQuatConstraintPart.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/RotationQuatConstraintPart.h deleted file mode 100644 index e7fb9672d82..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/RotationQuatConstraintPart.h +++ /dev/null @@ -1,246 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include - -JPH_NAMESPACE_BEGIN - -/// Quaternion based constraint that constrains rotation around all axis so that only translation is allowed. -/// -/// NOTE: This constraint part is more expensive than the RotationEulerConstraintPart and slightly more correct since -/// RotationEulerConstraintPart::SolvePositionConstraint contains an approximation. In practice the difference -/// is small, so the RotationEulerConstraintPart is probably the better choice. -/// -/// Rotation is fixed between bodies like this: -/// -/// q2 = q1 r0 -/// -/// Where: -/// q1, q2 = world space quaternions representing rotation of body 1 and 2. -/// r0 = initial rotation between bodies in local space of body 1, this can be calculated by: -/// -/// q20 = q10 r0 -/// <=> r0 = q10^* q20 -/// -/// Where: -/// q10, q20 = initial world space rotations of body 1 and 2. -/// q10^* = conjugate of quaternion q10 (which is the same as the inverse for a unit quaternion) -/// -/// We exclusively use the conjugate below: -/// -/// r0^* = q20^* q10 -/// -/// The error in the rotation is (in local space of body 1): -/// -/// q2 = q1 error r0 -/// <=> error = q1^* q2 r0^* -/// -/// The imaginary part of the quaternion represents the rotation axis * sin(angle / 2). The real part of the quaternion -/// does not add any additional information (we know the quaternion in normalized) and we're removing 3 degrees of freedom -/// so we want 3 parameters. Therefore we define the constraint equation like: -/// -/// C = A q1^* q2 r0^* = 0 -/// -/// Where (if you write a quaternion as [real-part, i-part, j-part, k-part]): -/// -/// [0, 1, 0, 0] -/// A = [0, 0, 1, 0] -/// [0, 0, 0, 1] -/// -/// or in our case since we store a quaternion like [i-part, j-part, k-part, real-part]: -/// -/// [1, 0, 0, 0] -/// A = [0, 1, 0, 0] -/// [0, 0, 1, 0] -/// -/// Time derivative: -/// -/// d/dt C = A (q1^* d/dt(q2) + d/dt(q1^*) q2) r0^* -/// = A (q1^* (1/2 W2 q2) + (1/2 W1 q1)^* q2) r0^* -/// = 1/2 A (q1^* W2 q2 + q1^* W1^* q2) r0^* -/// = 1/2 A (q1^* W2 q2 - q1^* W1 * q2) r0^* -/// = 1/2 A ML(q1^*) MR(q2 r0^*) (W2 - W1) -/// = 1/2 A ML(q1^*) MR(q2 r0^*) A^T (w2 - w1) -/// -/// Where: -/// W1 = [0, w1], W2 = [0, w2] (converting angular velocity to imaginary part of quaternion). -/// w1, w2 = angular velocity of body 1 and 2. -/// d/dt(q) = 1/2 W q (time derivative of a quaternion). -/// W^* = -W (conjugate negates angular velocity as quaternion). -/// ML(q): 4x4 matrix so that q * p = ML(q) * p, where q and p are quaternions. -/// MR(p): 4x4 matrix so that q * p = MR(p) * q, where q and p are quaternions. -/// A^T: Transpose of A. -/// -/// Jacobian: -/// -/// J = [0, -1/2 A ML(q1^*) MR(q2 r0^*) A^T, 0, 1/2 A ML(q1^*) MR(q2 r0^*) A^T] -/// = [0, -JP, 0, JP] -/// -/// Suggested reading: -/// - 3D Constraint Derivations for Impulse Solvers - Marijn Tamis -/// - Game Physics Pearls - Section 9 - Quaternion Based Constraints - Claude Lacoursiere -class RotationQuatConstraintPart -{ -private: - /// Internal helper function to update velocities of bodies after Lagrange multiplier is calculated - JPH_INLINE bool ApplyVelocityStep(Body &ioBody1, Body &ioBody2, Vec3Arg inLambda) const - { - // Apply impulse if delta is not zero - if (inLambda != Vec3::sZero()) - { - // Calculate velocity change due to constraint - // - // Impulse: - // P = J^T lambda - // - // Euler velocity integration: - // v' = v + M^-1 P - if (ioBody1.IsDynamic()) - ioBody1.GetMotionProperties()->SubAngularVelocityStep(mInvI1_JPT.Multiply3x3(inLambda)); - if (ioBody2.IsDynamic()) - ioBody2.GetMotionProperties()->AddAngularVelocityStep(mInvI2_JPT.Multiply3x3(inLambda)); - return true; - } - - return false; - } - -public: - /// Return inverse of initial rotation from body 1 to body 2 in body 1 space - static Quat sGetInvInitialOrientation(const Body &inBody1, const Body &inBody2) - { - // q20 = q10 r0 - // <=> r0 = q10^-1 q20 - // <=> r0^-1 = q20^-1 q10 - // - // where: - // - // q20 = initial orientation of body 2 - // q10 = initial orientation of body 1 - // r0 = initial rotation from body 1 to body 2 - return inBody2.GetRotation().Conjugated() * inBody1.GetRotation(); - } - - /// Calculate properties used during the functions below - inline void CalculateConstraintProperties(const Body &inBody1, Mat44Arg inRotation1, const Body &inBody2, Mat44Arg inRotation2, QuatArg inInvInitialOrientation) - { - // Calculate: JP = 1/2 A ML(q1^*) MR(q2 r0^*) A^T - Mat44 jp = (Mat44::sQuatLeftMultiply(0.5f * inBody1.GetRotation().Conjugated()) * Mat44::sQuatRightMultiply(inBody2.GetRotation() * inInvInitialOrientation)).GetRotationSafe(); - - // Calculate properties used during constraint solving - Mat44 inv_i1 = inBody1.IsDynamic()? inBody1.GetMotionProperties()->GetInverseInertiaForRotation(inRotation1) : Mat44::sZero(); - Mat44 inv_i2 = inBody2.IsDynamic()? inBody2.GetMotionProperties()->GetInverseInertiaForRotation(inRotation2) : Mat44::sZero(); - mInvI1_JPT = inv_i1.Multiply3x3RightTransposed(jp); - mInvI2_JPT = inv_i2.Multiply3x3RightTransposed(jp); - - // Calculate effective mass: K^-1 = (J M^-1 J^T)^-1 - // = (JP * I1^-1 * JP^T + JP * I2^-1 * JP^T)^-1 - // = (JP * (I1^-1 + I2^-1) * JP^T)^-1 - if (!mEffectiveMass.SetInversed3x3(jp.Multiply3x3(inv_i1 + inv_i2).Multiply3x3RightTransposed(jp))) - Deactivate(); - else - mEffectiveMass_JP = mEffectiveMass.Multiply3x3(jp); - } - - /// Deactivate this constraint - inline void Deactivate() - { - mEffectiveMass = Mat44::sZero(); - mEffectiveMass_JP = Mat44::sZero(); - mTotalLambda = Vec3::sZero(); - } - - /// Check if constraint is active - inline bool IsActive() const - { - return mEffectiveMass(3, 3) != 0.0f; - } - - /// Must be called from the WarmStartVelocityConstraint call to apply the previous frame's impulses - inline void WarmStart(Body &ioBody1, Body &ioBody2, float inWarmStartImpulseRatio) - { - mTotalLambda *= inWarmStartImpulseRatio; - ApplyVelocityStep(ioBody1, ioBody2, mTotalLambda); - } - - /// Iteratively update the velocity constraint. Makes sure d/dt C(...) = 0, where C is the constraint equation. - inline bool SolveVelocityConstraint(Body &ioBody1, Body &ioBody2) - { - // Calculate lagrange multiplier: - // - // lambda = -K^-1 (J v + b) - Vec3 lambda = mEffectiveMass_JP.Multiply3x3(ioBody1.GetAngularVelocity() - ioBody2.GetAngularVelocity()); - mTotalLambda += lambda; - return ApplyVelocityStep(ioBody1, ioBody2, lambda); - } - - /// Iteratively update the position constraint. Makes sure C(...) = 0. - inline bool SolvePositionConstraint(Body &ioBody1, Body &ioBody2, QuatArg inInvInitialOrientation, float inBaumgarte) const - { - // Calculate constraint equation - Vec3 c = (ioBody1.GetRotation().Conjugated() * ioBody2.GetRotation() * inInvInitialOrientation).GetXYZ(); - if (c != Vec3::sZero()) - { - // Calculate lagrange multiplier (lambda) for Baumgarte stabilization: - // - // lambda = -K^-1 * beta / dt * C - // - // We should divide by inDeltaTime, but we should multiply by inDeltaTime in the Euler step below so they're cancelled out - Vec3 lambda = -inBaumgarte * mEffectiveMass * c; - - // Directly integrate velocity change for one time step - // - // Euler velocity integration: - // dv = M^-1 P - // - // Impulse: - // P = J^T lambda - // - // Euler position integration: - // x' = x + dv * dt - // - // Note we don't accumulate velocities for the stabilization. This is using the approach described in 'Modeling and - // Solving Constraints' by Erin Catto presented at GDC 2007. On slide 78 it is suggested to split up the Baumgarte - // stabilization for positional drift so that it does not actually add to the momentum. We combine an Euler velocity - // integrate + a position integrate and then discard the velocity change. - if (ioBody1.IsDynamic()) - ioBody1.SubRotationStep(mInvI1_JPT.Multiply3x3(lambda)); - if (ioBody2.IsDynamic()) - ioBody2.AddRotationStep(mInvI2_JPT.Multiply3x3(lambda)); - return true; - } - - return false; - } - - /// Return lagrange multiplier - Vec3 GetTotalLambda() const - { - return mTotalLambda; - } - - /// Save state of this constraint part - void SaveState(StateRecorder &inStream) const - { - inStream.Write(mTotalLambda); - } - - /// Restore state of this constraint part - void RestoreState(StateRecorder &inStream) - { - inStream.Read(mTotalLambda); - } - -private: - Mat44 mInvI1_JPT; - Mat44 mInvI2_JPT; - Mat44 mEffectiveMass; - Mat44 mEffectiveMass_JP; - Vec3 mTotalLambda { Vec3::sZero() }; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/SpringPart.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/SpringPart.h deleted file mode 100644 index 0a8a4a9730c..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/SpringPart.h +++ /dev/null @@ -1,169 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -JPH_NAMESPACE_BEGIN -#ifndef JPH_PLATFORM_DOXYGEN // Somehow Doxygen gets confused and thinks the parameters to CalculateSpringProperties belong to this macro -JPH_MSVC_SUPPRESS_WARNING(4723) // potential divide by 0 - caused by line: outEffectiveMass = 1.0f / inInvEffectiveMass, note that JPH_NAMESPACE_BEGIN already pushes the warning state -#endif // !JPH_PLATFORM_DOXYGEN - -/// Class used in other constraint parts to calculate the required bias factor in the lagrange multiplier for creating springs -class SpringPart -{ -private: - JPH_INLINE void CalculateSpringPropertiesHelper(float inDeltaTime, float inInvEffectiveMass, float inBias, float inC, float inStiffness, float inDamping, float &outEffectiveMass) - { - // Soft constraints as per: Soft Constraints: Reinventing The Spring - Erin Catto - GDC 2011 - - // Note that the calculation of beta and gamma below are based on the solution of an implicit Euler integration scheme - // This scheme is unconditionally stable but has built in damping, so even when you set the damping ratio to 0 there will still - // be damping. See page 16 and 32. - - // Calculate softness (gamma in the slides) - // See page 34 and note that the gamma needs to be divided by delta time since we're working with impulses rather than forces: - // softness = 1 / (dt * (c + dt * k)) - // Note that the spring stiffness is k and the spring damping is c - mSoftness = 1.0f / (inDeltaTime * (inDamping + inDeltaTime * inStiffness)); - - // Calculate bias factor (baumgarte stabilization): - // beta = dt * k / (c + dt * k) = dt * k^2 * softness - // b = beta / dt * C = dt * k * softness * C - mBias = inBias + inDeltaTime * inStiffness * mSoftness * inC; - - // Update the effective mass, see post by Erin Catto: http://www.bulletphysics.org/Bullet/phpBB3/viewtopic.php?f=4&t=1354 - // - // Newton's Law: - // M * (v2 - v1) = J^T * lambda - // - // Velocity constraint with softness and Baumgarte: - // J * v2 + softness * lambda + b = 0 - // - // where b = beta * C / dt - // - // We know everything except v2 and lambda. - // - // First solve Newton's law for v2 in terms of lambda: - // - // v2 = v1 + M^-1 * J^T * lambda - // - // Substitute this expression into the velocity constraint: - // - // J * (v1 + M^-1 * J^T * lambda) + softness * lambda + b = 0 - // - // Now collect coefficients of lambda: - // - // (J * M^-1 * J^T + softness) * lambda = - J * v1 - b - // - // Now we define: - // - // K = J * M^-1 * J^T + softness - // - // So our new effective mass is K^-1 - outEffectiveMass = 1.0f / (inInvEffectiveMass + mSoftness); - } - -public: - /// Turn off the spring and set a bias only - /// - /// @param inBias Bias term (b) for the constraint impulse: lambda = J v + b - inline void CalculateSpringPropertiesWithBias(float inBias) - { - mSoftness = 0.0f; - mBias = inBias; - } - - /// Calculate spring properties based on frequency and damping ratio - /// - /// @param inDeltaTime Time step - /// @param inInvEffectiveMass Inverse effective mass K - /// @param inBias Bias term (b) for the constraint impulse: lambda = J v + b - /// @param inC Value of the constraint equation (C). Set to zero if you don't want to drive the constraint to zero with a spring. - /// @param inFrequency Oscillation frequency (Hz). Set to zero if you don't want to drive the constraint to zero with a spring. - /// @param inDamping Damping factor (0 = no damping, 1 = critical damping). Set to zero if you don't want to drive the constraint to zero with a spring. - /// @param outEffectiveMass On return, this contains the new effective mass K^-1 - inline void CalculateSpringPropertiesWithFrequencyAndDamping(float inDeltaTime, float inInvEffectiveMass, float inBias, float inC, float inFrequency, float inDamping, float &outEffectiveMass) - { - outEffectiveMass = 1.0f / inInvEffectiveMass; - - if (inFrequency > 0.0f) - { - // Calculate angular frequency - float omega = 2.0f * JPH_PI * inFrequency; - - // Calculate spring stiffness k and damping constant c (page 45) - float k = outEffectiveMass * Square(omega); - float c = 2.0f * outEffectiveMass * inDamping * omega; - - CalculateSpringPropertiesHelper(inDeltaTime, inInvEffectiveMass, inBias, inC, k, c, outEffectiveMass); - } - else - { - CalculateSpringPropertiesWithBias(inBias); - } - } - - /// Calculate spring properties with spring Stiffness (k) and damping (c), this is based on the spring equation: F = -k * x - c * v - /// - /// @param inDeltaTime Time step - /// @param inInvEffectiveMass Inverse effective mass K - /// @param inBias Bias term (b) for the constraint impulse: lambda = J v + b - /// @param inC Value of the constraint equation (C). Set to zero if you don't want to drive the constraint to zero with a spring. - /// @param inStiffness Spring stiffness k. Set to zero if you don't want to drive the constraint to zero with a spring. - /// @param inDamping Spring damping coefficient c. Set to zero if you don't want to drive the constraint to zero with a spring. - /// @param outEffectiveMass On return, this contains the new effective mass K^-1 - inline void CalculateSpringPropertiesWithStiffnessAndDamping(float inDeltaTime, float inInvEffectiveMass, float inBias, float inC, float inStiffness, float inDamping, float &outEffectiveMass) - { - if (inStiffness > 0.0f) - { - CalculateSpringPropertiesHelper(inDeltaTime, inInvEffectiveMass, inBias, inC, inStiffness, inDamping, outEffectiveMass); - } - else - { - outEffectiveMass = 1.0f / inInvEffectiveMass; - - CalculateSpringPropertiesWithBias(inBias); - } - } - - /// Returns if this spring is active - inline bool IsActive() const - { - return mSoftness != 0.0f; - } - - /// Get total bias b, including supplied bias and bias for spring: lambda = J v + b - inline float GetBias(float inTotalLambda) const - { - // Remainder of post by Erin Catto: http://www.bulletphysics.org/Bullet/phpBB3/viewtopic.php?f=4&t=1354 - // - // Each iteration we are not computing the whole impulse, we are computing an increment to the impulse and we are updating the velocity. - // Also, as we solve each constraint we get a perfect v2, but then some other constraint will come along and mess it up. - // So we want to patch up the constraint while acknowledging the accumulated impulse and the damaged velocity. - // To help with that we use P for the accumulated impulse and lambda as the update. Mathematically we have: - // - // M * (v2new - v2damaged) = J^T * lambda - // J * v2new + softness * (total_lambda + lambda) + b = 0 - // - // If we solve this we get: - // - // v2new = v2damaged + M^-1 * J^T * lambda - // J * (v2damaged + M^-1 * J^T * lambda) + softness * total_lambda + softness * lambda + b = 0 - // - // (J * M^-1 * J^T + softness) * lambda = -(J * v2damaged + softness * total_lambda + b) - // - // So our lagrange multiplier becomes: - // - // lambda = -K^-1 (J v + softness * total_lambda + b) - // - // So we return the bias: softness * total_lambda + b - return mSoftness * inTotalLambda + mBias; - } - -private: - float mBias = 0.0f; - float mSoftness = 0.0f; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/SwingTwistConstraintPart.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/SwingTwistConstraintPart.h deleted file mode 100644 index c2a47547f5e..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ConstraintPart/SwingTwistConstraintPart.h +++ /dev/null @@ -1,597 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -/// How the swing limit behaves -enum class ESwingType : uint8 -{ - Cone, ///< Swing is limited by a cone shape, note that this cone starts to deform for larger swing angles. Cone limits only support limits that are symmetric around 0. - Pyramid, ///< Swing is limited by a pyramid shape, note that this pyramid starts to deform for larger swing angles. -}; - -/// Quaternion based constraint that decomposes the rotation in constraint space in swing and twist: q = q_swing * q_twist -/// where q_swing.x = 0 and where q_twist.y = q_twist.z = 0 -/// -/// - Rotation around the twist (x-axis) is within [inTwistMinAngle, inTwistMaxAngle]. -/// - Rotation around the swing axis (y and z axis) are limited to an ellipsoid in quaternion space formed by the equation: -/// -/// (q_swing.y / sin(inSwingYHalfAngle / 2))^2 + (q_swing.z / sin(inSwingZHalfAngle / 2))^2 <= 1 -/// -/// Which roughly corresponds to an elliptic cone shape with major axis (inSwingYHalfAngle, inSwingZHalfAngle). -/// -/// In case inSwingYHalfAngle = 0, the rotation around Y will be constrained to 0 and the rotation around Z -/// will be constrained between [-inSwingZHalfAngle, inSwingZHalfAngle]. Vice versa if inSwingZHalfAngle = 0. -class SwingTwistConstraintPart -{ -public: - /// Override the swing type - void SetSwingType(ESwingType inSwingType) - { - mSwingType = inSwingType; - } - - /// Get the swing type for this part - ESwingType GetSwingType() const - { - return mSwingType; - } - - /// Set limits for this constraint (see description above for parameters) - void SetLimits(float inTwistMinAngle, float inTwistMaxAngle, float inSwingYMinAngle, float inSwingYMaxAngle, float inSwingZMinAngle, float inSwingZMaxAngle) - { - constexpr float cLockedAngle = DegreesToRadians(0.5f); - constexpr float cFreeAngle = DegreesToRadians(179.5f); - - // Assume sane input - JPH_ASSERT(inTwistMinAngle <= inTwistMaxAngle); - JPH_ASSERT(inSwingYMinAngle <= inSwingYMaxAngle); - JPH_ASSERT(inSwingZMinAngle <= inSwingZMaxAngle); - JPH_ASSERT(inSwingYMinAngle >= -JPH_PI && inSwingYMaxAngle <= JPH_PI); - JPH_ASSERT(inSwingZMinAngle >= -JPH_PI && inSwingZMaxAngle <= JPH_PI); - - // Calculate the sine and cosine of the half angles - Vec4 half_twist = 0.5f * Vec4(inTwistMinAngle, inTwistMaxAngle, 0, 0); - Vec4 twist_s, twist_c; - half_twist.SinCos(twist_s, twist_c); - Vec4 half_swing = 0.5f * Vec4(inSwingYMinAngle, inSwingYMaxAngle, inSwingZMinAngle, inSwingZMaxAngle); - Vec4 swing_s, swing_c; - half_swing.SinCos(swing_s, swing_c); - - // Store half angles for pyramid limit - mSwingYHalfMinAngle = half_swing.GetX(); - mSwingYHalfMaxAngle = half_swing.GetY(); - mSwingZHalfMinAngle = half_swing.GetZ(); - mSwingZHalfMaxAngle = half_swing.GetW(); - - // Store axis flags which are used at runtime to quickly decided which constraints to apply - mRotationFlags = 0; - if (inTwistMinAngle > -cLockedAngle && inTwistMaxAngle < cLockedAngle) - { - mRotationFlags |= TwistXLocked; - mSinTwistHalfMinAngle = 0.0f; - mSinTwistHalfMaxAngle = 0.0f; - mCosTwistHalfMinAngle = 1.0f; - mCosTwistHalfMaxAngle = 1.0f; - } - else if (inTwistMinAngle < -cFreeAngle && inTwistMaxAngle > cFreeAngle) - { - mRotationFlags |= TwistXFree; - mSinTwistHalfMinAngle = -1.0f; - mSinTwistHalfMaxAngle = 1.0f; - mCosTwistHalfMinAngle = 0.0f; - mCosTwistHalfMaxAngle = 0.0f; - } - else - { - mSinTwistHalfMinAngle = twist_s.GetX(); - mSinTwistHalfMaxAngle = twist_s.GetY(); - mCosTwistHalfMinAngle = twist_c.GetX(); - mCosTwistHalfMaxAngle = twist_c.GetY(); - } - - if (inSwingYMinAngle > -cLockedAngle && inSwingYMaxAngle < cLockedAngle) - { - mRotationFlags |= SwingYLocked; - mSinSwingYHalfMinAngle = 0.0f; - mSinSwingYHalfMaxAngle = 0.0f; - mCosSwingYHalfMinAngle = 1.0f; - mCosSwingYHalfMaxAngle = 1.0f; - } - else if (inSwingYMinAngle < -cFreeAngle && inSwingYMaxAngle > cFreeAngle) - { - mRotationFlags |= SwingYFree; - mSinSwingYHalfMinAngle = -1.0f; - mSinSwingYHalfMaxAngle = 1.0f; - mCosSwingYHalfMinAngle = 0.0f; - mCosSwingYHalfMaxAngle = 0.0f; - } - else - { - mSinSwingYHalfMinAngle = swing_s.GetX(); - mSinSwingYHalfMaxAngle = swing_s.GetY(); - mCosSwingYHalfMinAngle = swing_c.GetX(); - mCosSwingYHalfMaxAngle = swing_c.GetY(); - JPH_ASSERT(mSinSwingYHalfMinAngle <= mSinSwingYHalfMaxAngle); - } - - if (inSwingZMinAngle > -cLockedAngle && inSwingZMaxAngle < cLockedAngle) - { - mRotationFlags |= SwingZLocked; - mSinSwingZHalfMinAngle = 0.0f; - mSinSwingZHalfMaxAngle = 0.0f; - mCosSwingZHalfMinAngle = 1.0f; - mCosSwingZHalfMaxAngle = 1.0f; - } - else if (inSwingZMinAngle < -cFreeAngle && inSwingZMaxAngle > cFreeAngle) - { - mRotationFlags |= SwingZFree; - mSinSwingZHalfMinAngle = -1.0f; - mSinSwingZHalfMaxAngle = 1.0f; - mCosSwingZHalfMinAngle = 0.0f; - mCosSwingZHalfMaxAngle = 0.0f; - } - else - { - mSinSwingZHalfMinAngle = swing_s.GetZ(); - mSinSwingZHalfMaxAngle = swing_s.GetW(); - mCosSwingZHalfMinAngle = swing_c.GetZ(); - mCosSwingZHalfMaxAngle = swing_c.GetW(); - JPH_ASSERT(mSinSwingZHalfMinAngle <= mSinSwingZHalfMaxAngle); - } - } - - /// Flags to indicate which axis got clamped by ClampSwingTwist - static constexpr uint cClampedTwistMin = 1 << 0; - static constexpr uint cClampedTwistMax = 1 << 1; - static constexpr uint cClampedSwingYMin = 1 << 2; - static constexpr uint cClampedSwingYMax = 1 << 3; - static constexpr uint cClampedSwingZMin = 1 << 4; - static constexpr uint cClampedSwingZMax = 1 << 5; - - /// Helper function to determine if we're clamped against the min or max limit - static JPH_INLINE bool sDistanceToMinShorter(float inDeltaMin, float inDeltaMax) - { - // We're outside of the limits, get actual delta to min/max range - // Note that a swing/twist of -1 and 1 represent the same angle, so if the difference is bigger than 1, the shortest angle is the other way around (2 - difference) - // We should actually be working with angles rather than sin(angle / 2). When the difference is small the approximation is accurate, but - // when working with extreme values the calculation is off and e.g. when the limit is between 0 and 180 a value of approx -60 will clamp - // to 180 rather than 0 (you'd expect anything > -90 to go to 0). - inDeltaMin = abs(inDeltaMin); - if (inDeltaMin > 1.0f) inDeltaMin = 2.0f - inDeltaMin; - inDeltaMax = abs(inDeltaMax); - if (inDeltaMax > 1.0f) inDeltaMax = 2.0f - inDeltaMax; - return inDeltaMin < inDeltaMax; - } - - /// Clamp twist and swing against the constraint limits, returns which parts were clamped (everything assumed in constraint space) - inline void ClampSwingTwist(Quat &ioSwing, Quat &ioTwist, uint &outClampedAxis) const - { - // Start with not clamped - outClampedAxis = 0; - - // Check that swing and twist quaternions don't contain rotations around the wrong axis - JPH_ASSERT(ioSwing.GetX() == 0.0f); - JPH_ASSERT(ioTwist.GetY() == 0.0f); - JPH_ASSERT(ioTwist.GetZ() == 0.0f); - - // Ensure quaternions have w > 0 - bool negate_swing = ioSwing.GetW() < 0.0f; - if (negate_swing) - ioSwing = -ioSwing; - bool negate_twist = ioTwist.GetW() < 0.0f; - if (negate_twist) - ioTwist = -ioTwist; - - if (mRotationFlags & TwistXLocked) - { - // Twist axis is locked, clamp whenever twist is not identity - outClampedAxis |= ioTwist.GetX() != 0.0f? (cClampedTwistMin | cClampedTwistMax) : 0; - ioTwist = Quat::sIdentity(); - } - else if ((mRotationFlags & TwistXFree) == 0) - { - // Twist axis has limit, clamp whenever out of range - float delta_min = mSinTwistHalfMinAngle - ioTwist.GetX(); - float delta_max = ioTwist.GetX() - mSinTwistHalfMaxAngle; - if (delta_min > 0.0f || delta_max > 0.0f) - { - // Pick the twist that corresponds to the smallest delta - if (sDistanceToMinShorter(delta_min, delta_max)) - { - ioTwist = Quat(mSinTwistHalfMinAngle, 0, 0, mCosTwistHalfMinAngle); - outClampedAxis |= cClampedTwistMin; - } - else - { - ioTwist = Quat(mSinTwistHalfMaxAngle, 0, 0, mCosTwistHalfMaxAngle); - outClampedAxis |= cClampedTwistMax; - } - } - } - - // Clamp swing - if (mRotationFlags & SwingYLocked) - { - if (mRotationFlags & SwingZLocked) - { - // Both swing Y and Z are disabled, no degrees of freedom in swing - outClampedAxis |= ioSwing.GetY() != 0.0f? (cClampedSwingYMin | cClampedSwingYMax) : 0; - outClampedAxis |= ioSwing.GetZ() != 0.0f? (cClampedSwingZMin | cClampedSwingZMax) : 0; - ioSwing = Quat::sIdentity(); - } - else - { - // Swing Y angle disabled, only 1 degree of freedom in swing - outClampedAxis |= ioSwing.GetY() != 0.0f? (cClampedSwingYMin | cClampedSwingYMax) : 0; - float delta_min = mSinSwingZHalfMinAngle - ioSwing.GetZ(); - float delta_max = ioSwing.GetZ() - mSinSwingZHalfMaxAngle; - if (delta_min > 0.0f || delta_max > 0.0f) - { - // Pick the swing that corresponds to the smallest delta - if (sDistanceToMinShorter(delta_min, delta_max)) - { - ioSwing = Quat(0, 0, mSinSwingZHalfMinAngle, mCosSwingZHalfMinAngle); - outClampedAxis |= cClampedSwingZMin; - } - else - { - ioSwing = Quat(0, 0, mSinSwingZHalfMaxAngle, mCosSwingZHalfMaxAngle); - outClampedAxis |= cClampedSwingZMax; - } - } - else if ((outClampedAxis & cClampedSwingYMin) != 0) - { - float z = ioSwing.GetZ(); - ioSwing = Quat(0, 0, z, sqrt(1.0f - Square(z))); - } - } - } - else if (mRotationFlags & SwingZLocked) - { - // Swing Z angle disabled, only 1 degree of freedom in swing - outClampedAxis |= ioSwing.GetZ() != 0.0f? (cClampedSwingZMin | cClampedSwingZMax) : 0; - float delta_min = mSinSwingYHalfMinAngle - ioSwing.GetY(); - float delta_max = ioSwing.GetY() - mSinSwingYHalfMaxAngle; - if (delta_min > 0.0f || delta_max > 0.0f) - { - // Pick the swing that corresponds to the smallest delta - if (sDistanceToMinShorter(delta_min, delta_max)) - { - ioSwing = Quat(0, mSinSwingYHalfMinAngle, 0, mCosSwingYHalfMinAngle); - outClampedAxis |= cClampedSwingYMin; - } - else - { - ioSwing = Quat(0, mSinSwingYHalfMaxAngle, 0, mCosSwingYHalfMaxAngle); - outClampedAxis |= cClampedSwingYMax; - } - } - else if ((outClampedAxis & cClampedSwingZMin) != 0) - { - float y = ioSwing.GetY(); - ioSwing = Quat(0, y, 0, sqrt(1.0f - Square(y))); - } - } - else - { - // Two degrees of freedom - if (mSwingType == ESwingType::Cone) - { - // Use ellipse to solve limits - Ellipse ellipse(mSinSwingYHalfMaxAngle, mSinSwingZHalfMaxAngle); - Float2 point(ioSwing.GetY(), ioSwing.GetZ()); - if (!ellipse.IsInside(point)) - { - Float2 closest = ellipse.GetClosestPoint(point); - ioSwing = Quat(0, closest.x, closest.y, sqrt(max(0.0f, 1.0f - Square(closest.x) - Square(closest.y)))); - outClampedAxis |= cClampedSwingYMin | cClampedSwingYMax | cClampedSwingZMin | cClampedSwingZMax; // We're not using the flags on which side we got clamped here - } - } - else - { - // Use pyramid to solve limits - // The quaternion rotating by angle y around the Y axis then rotating by angle z around the Z axis is: - // q = Quat::sRotation(Vec3::sAxisZ(), z) * Quat::sRotation(Vec3::sAxisY(), y) - // [q.x, q.y, q.z, q.w] = [-sin(y / 2) * sin(z / 2), sin(y / 2) * cos(z / 2), cos(y / 2) * sin(z / 2), cos(y / 2) * cos(z / 2)] - // So we can calculate y / 2 = atan2(q.y, q.w) and z / 2 = atan2(q.z, q.w) - Vec4 half_angle = Vec4::sATan2(ioSwing.GetXYZW().Swizzle(), ioSwing.GetXYZW().SplatW()); - Vec4 min_half_angle(mSwingYHalfMinAngle, mSwingYHalfMinAngle, mSwingZHalfMinAngle, mSwingZHalfMinAngle); - Vec4 max_half_angle(mSwingYHalfMaxAngle, mSwingYHalfMaxAngle, mSwingZHalfMaxAngle, mSwingZHalfMaxAngle); - Vec4 clamped_half_angle = Vec4::sMin(Vec4::sMax(half_angle, min_half_angle), max_half_angle); - UVec4 unclamped = Vec4::sEquals(half_angle, clamped_half_angle); - if (!unclamped.TestAllTrue()) - { - // We now calculate the quaternion again using the formula for q above, - // but we leave out the x component in order to not introduce twist - Vec4 s, c; - clamped_half_angle.SinCos(s, c); - ioSwing = Quat(0, s.GetY() * c.GetZ(), c.GetY() * s.GetZ(), c.GetY() * c.GetZ()).Normalized(); - outClampedAxis |= cClampedSwingYMin | cClampedSwingYMax | cClampedSwingZMin | cClampedSwingZMax; // We're not using the flags on which side we got clamped here - } - } - } - - // Flip sign back - if (negate_swing) - ioSwing = -ioSwing; - if (negate_twist) - ioTwist = -ioTwist; - - JPH_ASSERT(ioSwing.IsNormalized()); - JPH_ASSERT(ioTwist.IsNormalized()); - } - - /// Calculate properties used during the functions below - /// @param inBody1 The first body that this constraint is attached to - /// @param inBody2 The second body that this constraint is attached to - /// @param inConstraintRotation The current rotation of the constraint in constraint space - /// @param inConstraintToWorld Rotates from constraint space into world space - inline void CalculateConstraintProperties(const Body &inBody1, const Body &inBody2, QuatArg inConstraintRotation, QuatArg inConstraintToWorld) - { - // Decompose into swing and twist - Quat q_swing, q_twist; - inConstraintRotation.GetSwingTwist(q_swing, q_twist); - - // Clamp against joint limits - Quat q_clamped_swing = q_swing, q_clamped_twist = q_twist; - uint clamped_axis; - ClampSwingTwist(q_clamped_swing, q_clamped_twist, clamped_axis); - - if (mRotationFlags & SwingYLocked) - { - Quat twist_to_world = inConstraintToWorld * q_swing; - mWorldSpaceSwingLimitYRotationAxis = twist_to_world.RotateAxisY(); - mWorldSpaceSwingLimitZRotationAxis = twist_to_world.RotateAxisZ(); - - if (mRotationFlags & SwingZLocked) - { - // Swing fully locked - mSwingLimitYConstraintPart.CalculateConstraintProperties(inBody1, inBody2, mWorldSpaceSwingLimitYRotationAxis); - mSwingLimitZConstraintPart.CalculateConstraintProperties(inBody1, inBody2, mWorldSpaceSwingLimitZRotationAxis); - } - else - { - // Swing only locked around Y - mSwingLimitYConstraintPart.CalculateConstraintProperties(inBody1, inBody2, mWorldSpaceSwingLimitYRotationAxis); - if ((clamped_axis & (cClampedSwingZMin | cClampedSwingZMax)) != 0) - { - if ((clamped_axis & cClampedSwingZMin) != 0) - mWorldSpaceSwingLimitZRotationAxis = -mWorldSpaceSwingLimitZRotationAxis; // Flip axis if hitting min limit because the impulse limit is going to be between [-FLT_MAX, 0] - mSwingLimitZConstraintPart.CalculateConstraintProperties(inBody1, inBody2, mWorldSpaceSwingLimitZRotationAxis); - } - else - mSwingLimitZConstraintPart.Deactivate(); - } - } - else if (mRotationFlags & SwingZLocked) - { - // Swing only locked around Z - Quat twist_to_world = inConstraintToWorld * q_swing; - mWorldSpaceSwingLimitYRotationAxis = twist_to_world.RotateAxisY(); - mWorldSpaceSwingLimitZRotationAxis = twist_to_world.RotateAxisZ(); - - if ((clamped_axis & (cClampedSwingYMin | cClampedSwingYMax)) != 0) - { - if ((clamped_axis & cClampedSwingYMin) != 0) - mWorldSpaceSwingLimitYRotationAxis = -mWorldSpaceSwingLimitYRotationAxis; // Flip axis if hitting min limit because the impulse limit is going to be between [-FLT_MAX, 0] - mSwingLimitYConstraintPart.CalculateConstraintProperties(inBody1, inBody2, mWorldSpaceSwingLimitYRotationAxis); - } - else - mSwingLimitYConstraintPart.Deactivate(); - mSwingLimitZConstraintPart.CalculateConstraintProperties(inBody1, inBody2, mWorldSpaceSwingLimitZRotationAxis); - } - else if ((mRotationFlags & SwingYZFree) != SwingYZFree) - { - // Swing has limits around Y and Z - if ((clamped_axis & (cClampedSwingYMin | cClampedSwingYMax | cClampedSwingZMin | cClampedSwingZMax)) != 0) - { - // Calculate axis of rotation from clamped swing to swing - Vec3 current = (inConstraintToWorld * q_swing).RotateAxisX(); - Vec3 desired = (inConstraintToWorld * q_clamped_swing).RotateAxisX(); - mWorldSpaceSwingLimitYRotationAxis = desired.Cross(current); - float len = mWorldSpaceSwingLimitYRotationAxis.Length(); - if (len != 0.0f) - { - mWorldSpaceSwingLimitYRotationAxis /= len; - mSwingLimitYConstraintPart.CalculateConstraintProperties(inBody1, inBody2, mWorldSpaceSwingLimitYRotationAxis); - } - else - mSwingLimitYConstraintPart.Deactivate(); - } - else - mSwingLimitYConstraintPart.Deactivate(); - mSwingLimitZConstraintPart.Deactivate(); - } - else - { - // No swing limits - mSwingLimitYConstraintPart.Deactivate(); - mSwingLimitZConstraintPart.Deactivate(); - } - - if (mRotationFlags & TwistXLocked) - { - // Twist locked, always activate constraint - mWorldSpaceTwistLimitRotationAxis = (inConstraintToWorld * q_swing).RotateAxisX(); - mTwistLimitConstraintPart.CalculateConstraintProperties(inBody1, inBody2, mWorldSpaceTwistLimitRotationAxis); - } - else if ((mRotationFlags & TwistXFree) == 0) - { - // Twist has limits - if ((clamped_axis & (cClampedTwistMin | cClampedTwistMax)) != 0) - { - mWorldSpaceTwistLimitRotationAxis = (inConstraintToWorld * q_swing).RotateAxisX(); - if ((clamped_axis & cClampedTwistMin) != 0) - mWorldSpaceTwistLimitRotationAxis = -mWorldSpaceTwistLimitRotationAxis; // Flip axis if hitting min limit because the impulse limit is going to be between [-FLT_MAX, 0] - mTwistLimitConstraintPart.CalculateConstraintProperties(inBody1, inBody2, mWorldSpaceTwistLimitRotationAxis); - } - else - mTwistLimitConstraintPart.Deactivate(); - } - else - { - // No twist limits - mTwistLimitConstraintPart.Deactivate(); - } - } - - /// Deactivate this constraint - void Deactivate() - { - mSwingLimitYConstraintPart.Deactivate(); - mSwingLimitZConstraintPart.Deactivate(); - mTwistLimitConstraintPart.Deactivate(); - } - - /// Check if constraint is active - inline bool IsActive() const - { - return mSwingLimitYConstraintPart.IsActive() || mSwingLimitZConstraintPart.IsActive() || mTwistLimitConstraintPart.IsActive(); - } - - /// Must be called from the WarmStartVelocityConstraint call to apply the previous frame's impulses - inline void WarmStart(Body &ioBody1, Body &ioBody2, float inWarmStartImpulseRatio) - { - mSwingLimitYConstraintPart.WarmStart(ioBody1, ioBody2, inWarmStartImpulseRatio); - mSwingLimitZConstraintPart.WarmStart(ioBody1, ioBody2, inWarmStartImpulseRatio); - mTwistLimitConstraintPart.WarmStart(ioBody1, ioBody2, inWarmStartImpulseRatio); - } - - /// Iteratively update the velocity constraint. Makes sure d/dt C(...) = 0, where C is the constraint equation. - inline bool SolveVelocityConstraint(Body &ioBody1, Body &ioBody2) - { - bool impulse = false; - - // Solve swing constraint - if (mSwingLimitYConstraintPart.IsActive()) - impulse |= mSwingLimitYConstraintPart.SolveVelocityConstraint(ioBody1, ioBody2, mWorldSpaceSwingLimitYRotationAxis, -FLT_MAX, mSinSwingYHalfMinAngle == mSinSwingYHalfMaxAngle? FLT_MAX : 0.0f); - - if (mSwingLimitZConstraintPart.IsActive()) - impulse |= mSwingLimitZConstraintPart.SolveVelocityConstraint(ioBody1, ioBody2, mWorldSpaceSwingLimitZRotationAxis, -FLT_MAX, mSinSwingZHalfMinAngle == mSinSwingZHalfMaxAngle? FLT_MAX : 0.0f); - - // Solve twist constraint - if (mTwistLimitConstraintPart.IsActive()) - impulse |= mTwistLimitConstraintPart.SolveVelocityConstraint(ioBody1, ioBody2, mWorldSpaceTwistLimitRotationAxis, -FLT_MAX, mSinTwistHalfMinAngle == mSinTwistHalfMaxAngle? FLT_MAX : 0.0f); - - return impulse; - } - - /// Iteratively update the position constraint. Makes sure C(...) = 0. - /// @param ioBody1 The first body that this constraint is attached to - /// @param ioBody2 The second body that this constraint is attached to - /// @param inConstraintRotation The current rotation of the constraint in constraint space - /// @param inConstraintToBody1 , inConstraintToBody2 Rotates from constraint space to body 1/2 space - /// @param inBaumgarte Baumgarte constant (fraction of the error to correct) - inline bool SolvePositionConstraint(Body &ioBody1, Body &ioBody2, QuatArg inConstraintRotation, QuatArg inConstraintToBody1, QuatArg inConstraintToBody2, float inBaumgarte) const - { - Quat q_swing, q_twist; - inConstraintRotation.GetSwingTwist(q_swing, q_twist); - - uint clamped_axis; - ClampSwingTwist(q_swing, q_twist, clamped_axis); - - // Solve rotation violations - if (clamped_axis != 0) - { - RotationEulerConstraintPart part; - Quat inv_initial_orientation = inConstraintToBody2 * (inConstraintToBody1 * q_swing * q_twist).Conjugated(); - part.CalculateConstraintProperties(ioBody1, Mat44::sRotation(ioBody1.GetRotation()), ioBody2, Mat44::sRotation(ioBody2.GetRotation())); - return part.SolvePositionConstraint(ioBody1, ioBody2, inv_initial_orientation, inBaumgarte); - } - - return false; - } - - /// Return lagrange multiplier for swing - inline float GetTotalSwingYLambda() const - { - return mSwingLimitYConstraintPart.GetTotalLambda(); - } - - inline float GetTotalSwingZLambda() const - { - return mSwingLimitZConstraintPart.GetTotalLambda(); - } - - /// Return lagrange multiplier for twist - inline float GetTotalTwistLambda() const - { - return mTwistLimitConstraintPart.GetTotalLambda(); - } - - /// Save state of this constraint part - void SaveState(StateRecorder &inStream) const - { - mSwingLimitYConstraintPart.SaveState(inStream); - mSwingLimitZConstraintPart.SaveState(inStream); - mTwistLimitConstraintPart.SaveState(inStream); - } - - /// Restore state of this constraint part - void RestoreState(StateRecorder &inStream) - { - mSwingLimitYConstraintPart.RestoreState(inStream); - mSwingLimitZConstraintPart.RestoreState(inStream); - mTwistLimitConstraintPart.RestoreState(inStream); - } - -private: - // CONFIGURATION PROPERTIES FOLLOW - - enum ERotationFlags - { - /// Indicates that axis is completely locked (cannot rotate around this axis) - TwistXLocked = 1 << 0, - SwingYLocked = 1 << 1, - SwingZLocked = 1 << 2, - - /// Indicates that axis is completely free (can rotate around without limits) - TwistXFree = 1 << 3, - SwingYFree = 1 << 4, - SwingZFree = 1 << 5, - SwingYZFree = SwingYFree | SwingZFree - }; - - uint8 mRotationFlags; - - // Constants - ESwingType mSwingType = ESwingType::Cone; - float mSinTwistHalfMinAngle; - float mSinTwistHalfMaxAngle; - float mCosTwistHalfMinAngle; - float mCosTwistHalfMaxAngle; - float mSwingYHalfMinAngle; - float mSwingYHalfMaxAngle; - float mSwingZHalfMinAngle; - float mSwingZHalfMaxAngle; - float mSinSwingYHalfMinAngle; - float mSinSwingYHalfMaxAngle; - float mSinSwingZHalfMinAngle; - float mSinSwingZHalfMaxAngle; - float mCosSwingYHalfMinAngle; - float mCosSwingYHalfMaxAngle; - float mCosSwingZHalfMinAngle; - float mCosSwingZHalfMaxAngle; - - // RUN TIME PROPERTIES FOLLOW - - /// Rotation axis for the angle constraint parts - Vec3 mWorldSpaceSwingLimitYRotationAxis; - Vec3 mWorldSpaceSwingLimitZRotationAxis; - Vec3 mWorldSpaceTwistLimitRotationAxis; - - /// The constraint parts - AngleConstraintPart mSwingLimitYConstraintPart; - AngleConstraintPart mSwingLimitZConstraintPart; - AngleConstraintPart mTwistLimitConstraintPart; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ContactConstraintManager.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ContactConstraintManager.cpp deleted file mode 100644 index f3f96888347..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ContactConstraintManager.cpp +++ /dev/null @@ -1,1718 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef JPH_DEBUG_RENDERER - #include -#endif // JPH_DEBUG_RENDERER - -JPH_NAMESPACE_BEGIN - -using namespace literals; - -#ifdef JPH_DEBUG_RENDERER -bool ContactConstraintManager::sDrawContactPoint = false; -bool ContactConstraintManager::sDrawSupportingFaces = false; -bool ContactConstraintManager::sDrawContactPointReduction = false; -bool ContactConstraintManager::sDrawContactManifolds = false; -#endif // JPH_DEBUG_RENDERER - -//#define JPH_MANIFOLD_CACHE_DEBUG - -//////////////////////////////////////////////////////////////////////////////////////////////////////// -// ContactConstraintManager::WorldContactPoint -//////////////////////////////////////////////////////////////////////////////////////////////////////// - -void ContactConstraintManager::WorldContactPoint::CalculateNonPenetrationConstraintProperties(const Body &inBody1, float inInvMass1, float inInvInertiaScale1, const Body &inBody2, float inInvMass2, float inInvInertiaScale2, RVec3Arg inWorldSpacePosition1, RVec3Arg inWorldSpacePosition2, Vec3Arg inWorldSpaceNormal) -{ - // Calculate collision points relative to body - RVec3 p = 0.5_r * (inWorldSpacePosition1 + inWorldSpacePosition2); - Vec3 r1 = Vec3(p - inBody1.GetCenterOfMassPosition()); - Vec3 r2 = Vec3(p - inBody2.GetCenterOfMassPosition()); - - mNonPenetrationConstraint.CalculateConstraintPropertiesWithMassOverride(inBody1, inInvMass1, inInvInertiaScale1, r1, inBody2, inInvMass2, inInvInertiaScale2, r2, inWorldSpaceNormal); -} - -template -JPH_INLINE void ContactConstraintManager::WorldContactPoint::TemplatedCalculateFrictionAndNonPenetrationConstraintProperties(float inDeltaTime, const Body &inBody1, const Body &inBody2, float inInvM1, float inInvM2, Mat44Arg inInvI1, Mat44Arg inInvI2, RVec3Arg inWorldSpacePosition1, RVec3Arg inWorldSpacePosition2, Vec3Arg inWorldSpaceNormal, Vec3Arg inWorldSpaceTangent1, Vec3Arg inWorldSpaceTangent2, const ContactSettings &inSettings, float inMinVelocityForRestitution) -{ - JPH_DET_LOG("TemplatedCalculateFrictionAndNonPenetrationConstraintProperties: p1: " << inWorldSpacePosition1 << " p2: " << inWorldSpacePosition2 - << " normal: " << inWorldSpaceNormal << " tangent1: " << inWorldSpaceTangent1 << " tangent2: " << inWorldSpaceTangent2 - << " restitution: " << inSettings.mCombinedRestitution << " friction: " << inSettings.mCombinedFriction << " minv: " << inMinVelocityForRestitution - << " surface_vel: " << inSettings.mRelativeLinearSurfaceVelocity << " surface_ang: " << inSettings.mRelativeAngularSurfaceVelocity); - - // Calculate collision points relative to body - RVec3 p = 0.5_r * (inWorldSpacePosition1 + inWorldSpacePosition2); - Vec3 r1 = Vec3(p - inBody1.GetCenterOfMassPosition()); - Vec3 r2 = Vec3(p - inBody2.GetCenterOfMassPosition()); - - // Calculate velocity of collision points - Vec3 relative_velocity; - if constexpr (Type1 != EMotionType::Static && Type2 != EMotionType::Static) - relative_velocity = inBody2.GetMotionPropertiesUnchecked()->GetPointVelocityCOM(r2) - inBody1.GetMotionPropertiesUnchecked()->GetPointVelocityCOM(r1); - else if constexpr (Type1 != EMotionType::Static) - relative_velocity = -inBody1.GetMotionPropertiesUnchecked()->GetPointVelocityCOM(r1); - else if constexpr (Type2 != EMotionType::Static) - relative_velocity = inBody2.GetMotionPropertiesUnchecked()->GetPointVelocityCOM(r2); - else - { - JPH_ASSERT(false); // Static vs static makes no sense - relative_velocity = Vec3::sZero(); - } - float normal_velocity = relative_velocity.Dot(inWorldSpaceNormal); - - // How much the shapes are penetrating (> 0 if penetrating, < 0 if separated) - float penetration = Vec3(inWorldSpacePosition1 - inWorldSpacePosition2).Dot(inWorldSpaceNormal); - - // If there is no penetration, this is a speculative contact and we will apply a bias to the contact constraint - // so that the constraint becomes relative_velocity . contact normal > -penetration / delta_time - // instead of relative_velocity . contact normal > 0 - // See: GDC 2013: "Physics for Game Programmers; Continuous Collision" - Erin Catto - float speculative_contact_velocity_bias = max(0.0f, -penetration / inDeltaTime); - - // Determine if the velocity is big enough for restitution - float normal_velocity_bias; - if (inSettings.mCombinedRestitution > 0.0f && normal_velocity < -inMinVelocityForRestitution) - { - // We have a velocity that is big enough for restitution. This is where speculative contacts don't work - // great as we have to decide now if we're going to apply the restitution or not. If the relative - // velocity is big enough for a hit, we apply the restitution (in the end, due to other constraints, - // the objects may actually not collide and we will have applied restitution incorrectly). Another - // artifact that occurs because of this approximation is that the object will bounce from its current - // position rather than from a position where it is touching the other object. This causes the object - // to appear to move faster for 1 frame (the opposite of time stealing). - if (normal_velocity < -speculative_contact_velocity_bias) - normal_velocity_bias = inSettings.mCombinedRestitution * normal_velocity; - else - // In this case we have predicted that we don't hit the other object, but if we do (due to other constraints changing velocities) - // the speculative contact will prevent penetration but will not apply restitution leading to another artifact. - normal_velocity_bias = speculative_contact_velocity_bias; - } - else - { - // No restitution. We can safely apply our contact velocity bias. - normal_velocity_bias = speculative_contact_velocity_bias; - } - - mNonPenetrationConstraint.TemplatedCalculateConstraintProperties(inInvM1, inInvI1, r1, inInvM2, inInvI2, r2, inWorldSpaceNormal, normal_velocity_bias); - - // Calculate friction part - if (inSettings.mCombinedFriction > 0.0f) - { - // Get surface velocity relative to tangents - Vec3 ws_surface_velocity = inSettings.mRelativeLinearSurfaceVelocity + inSettings.mRelativeAngularSurfaceVelocity.Cross(r1); - float surface_velocity1 = inWorldSpaceTangent1.Dot(ws_surface_velocity); - float surface_velocity2 = inWorldSpaceTangent2.Dot(ws_surface_velocity); - - // Implement friction as 2 AxisContraintParts - mFrictionConstraint1.TemplatedCalculateConstraintProperties(inInvM1, inInvI1, r1, inInvM2, inInvI2, r2, inWorldSpaceTangent1, surface_velocity1); - mFrictionConstraint2.TemplatedCalculateConstraintProperties(inInvM1, inInvI1, r1, inInvM2, inInvI2, r2, inWorldSpaceTangent2, surface_velocity2); - } - else - { - // Turn off friction constraint - mFrictionConstraint1.Deactivate(); - mFrictionConstraint2.Deactivate(); - } -} - -//////////////////////////////////////////////////////////////////////////////////////////////////////// -// ContactConstraintManager::ContactConstraint -//////////////////////////////////////////////////////////////////////////////////////////////////////// - -#ifdef JPH_DEBUG_RENDERER -void ContactConstraintManager::ContactConstraint::Draw(DebugRenderer *inRenderer, ColorArg inManifoldColor) const -{ - if (mContactPoints.empty()) - return; - - // Get body transforms - RMat44 transform_body1 = mBody1->GetCenterOfMassTransform(); - RMat44 transform_body2 = mBody2->GetCenterOfMassTransform(); - - RVec3 prev_point = transform_body1 * Vec3::sLoadFloat3Unsafe(mContactPoints.back().mContactPoint->mPosition1); - for (const WorldContactPoint &wcp : mContactPoints) - { - // Test if any lambda from the previous frame was transferred - float radius = wcp.mNonPenetrationConstraint.GetTotalLambda() == 0.0f - && wcp.mFrictionConstraint1.GetTotalLambda() == 0.0f - && wcp.mFrictionConstraint2.GetTotalLambda() == 0.0f? 0.1f : 0.2f; - - RVec3 next_point = transform_body1 * Vec3::sLoadFloat3Unsafe(wcp.mContactPoint->mPosition1); - inRenderer->DrawMarker(next_point, Color::sCyan, radius); - inRenderer->DrawMarker(transform_body2 * Vec3::sLoadFloat3Unsafe(wcp.mContactPoint->mPosition2), Color::sPurple, radius); - - // Draw edge - inRenderer->DrawArrow(prev_point, next_point, inManifoldColor, 0.05f); - prev_point = next_point; - } - - // Draw normal - RVec3 wp = transform_body1 * Vec3::sLoadFloat3Unsafe(mContactPoints[0].mContactPoint->mPosition1); - inRenderer->DrawArrow(wp, wp + GetWorldSpaceNormal(), Color::sRed, 0.05f); - - // Get tangents - Vec3 t1, t2; - GetTangents(t1, t2); - - // Draw tangents - inRenderer->DrawLine(wp, wp + t1, Color::sGreen); - inRenderer->DrawLine(wp, wp + t2, Color::sBlue); -} -#endif // JPH_DEBUG_RENDERER - -//////////////////////////////////////////////////////////////////////////////////////////////////////// -// ContactConstraintManager::CachedContactPoint -//////////////////////////////////////////////////////////////////////////////////////////////////////// - -void ContactConstraintManager::CachedContactPoint::SaveState(StateRecorder &inStream) const -{ - inStream.Write(mPosition1); - inStream.Write(mPosition2); - inStream.Write(mNonPenetrationLambda); - inStream.Write(mFrictionLambda); -} - -void ContactConstraintManager::CachedContactPoint::RestoreState(StateRecorder &inStream) -{ - inStream.Read(mPosition1); - inStream.Read(mPosition2); - inStream.Read(mNonPenetrationLambda); - inStream.Read(mFrictionLambda); -} - -//////////////////////////////////////////////////////////////////////////////////////////////////////// -// ContactConstraintManager::CachedManifold -//////////////////////////////////////////////////////////////////////////////////////////////////////// - -void ContactConstraintManager::CachedManifold::SaveState(StateRecorder &inStream) const -{ - inStream.Write(mContactNormal); -} - -void ContactConstraintManager::CachedManifold::RestoreState(StateRecorder &inStream) -{ - inStream.Read(mContactNormal); -} - -//////////////////////////////////////////////////////////////////////////////////////////////////////// -// ContactConstraintManager::CachedBodyPair -//////////////////////////////////////////////////////////////////////////////////////////////////////// - -void ContactConstraintManager::CachedBodyPair::SaveState(StateRecorder &inStream) const -{ - inStream.Write(mDeltaPosition); - inStream.Write(mDeltaRotation); -} - -void ContactConstraintManager::CachedBodyPair::RestoreState(StateRecorder &inStream) -{ - inStream.Read(mDeltaPosition); - inStream.Read(mDeltaRotation); -} - -//////////////////////////////////////////////////////////////////////////////////////////////////////// -// ContactConstraintManager::ManifoldCache -//////////////////////////////////////////////////////////////////////////////////////////////////////// - -void ContactConstraintManager::ManifoldCache::Init(uint inMaxBodyPairs, uint inMaxContactConstraints, uint inCachedManifoldsSize) -{ - mAllocator.Init(inMaxBodyPairs * sizeof(BodyPairMap::KeyValue) + inCachedManifoldsSize); - mCachedManifolds.Init(GetNextPowerOf2(inMaxContactConstraints)); - mCachedBodyPairs.Init(GetNextPowerOf2(inMaxBodyPairs)); -} - -void ContactConstraintManager::ManifoldCache::Clear() -{ - JPH_PROFILE_FUNCTION(); - - mCachedManifolds.Clear(); - mCachedBodyPairs.Clear(); - mAllocator.Clear(); - -#ifdef JPH_ENABLE_ASSERTS - // Mark as incomplete - mIsFinalized = false; -#endif -} - -void ContactConstraintManager::ManifoldCache::Prepare(uint inExpectedNumBodyPairs, uint inExpectedNumManifolds) -{ - // Minimum amount of buckets to use in the hash map - constexpr uint32 cMinBuckets = 1024; - - // Use the next higher power of 2 of amount of objects in the cache from last frame to determine the amount of buckets in this frame - mCachedManifolds.SetNumBuckets(min(max(cMinBuckets, GetNextPowerOf2(inExpectedNumManifolds)), mCachedManifolds.GetMaxBuckets())); - mCachedBodyPairs.SetNumBuckets(min(max(cMinBuckets, GetNextPowerOf2(inExpectedNumBodyPairs)), mCachedBodyPairs.GetMaxBuckets())); -} - -const ContactConstraintManager::MKeyValue *ContactConstraintManager::ManifoldCache::Find(const SubShapeIDPair &inKey, uint64 inKeyHash) const -{ - JPH_ASSERT(mIsFinalized); - return mCachedManifolds.Find(inKey, inKeyHash); -} - -ContactConstraintManager::MKeyValue *ContactConstraintManager::ManifoldCache::Create(ContactAllocator &ioContactAllocator, const SubShapeIDPair &inKey, uint64 inKeyHash, int inNumContactPoints) -{ - JPH_ASSERT(!mIsFinalized); - MKeyValue *kv = mCachedManifolds.Create(ioContactAllocator, inKey, inKeyHash, CachedManifold::sGetRequiredExtraSize(inNumContactPoints)); - if (kv == nullptr) - { - ioContactAllocator.mErrors |= EPhysicsUpdateError::ManifoldCacheFull; - return nullptr; - } - kv->GetValue().mNumContactPoints = uint16(inNumContactPoints); - ++ioContactAllocator.mNumManifolds; - return kv; -} - -ContactConstraintManager::MKVAndCreated ContactConstraintManager::ManifoldCache::FindOrCreate(ContactAllocator &ioContactAllocator, const SubShapeIDPair &inKey, uint64 inKeyHash, int inNumContactPoints) -{ - MKeyValue *kv = const_cast(mCachedManifolds.Find(inKey, inKeyHash)); - if (kv != nullptr) - return { kv, false }; - - return { Create(ioContactAllocator, inKey, inKeyHash, inNumContactPoints), true }; -} - -uint32 ContactConstraintManager::ManifoldCache::ToHandle(const MKeyValue *inKeyValue) const -{ - JPH_ASSERT(!mIsFinalized); - return mCachedManifolds.ToHandle(inKeyValue); -} - -const ContactConstraintManager::MKeyValue *ContactConstraintManager::ManifoldCache::FromHandle(uint32 inHandle) const -{ - JPH_ASSERT(mIsFinalized); - return mCachedManifolds.FromHandle(inHandle); -} - -const ContactConstraintManager::BPKeyValue *ContactConstraintManager::ManifoldCache::Find(const BodyPair &inKey, uint64 inKeyHash) const -{ - JPH_ASSERT(mIsFinalized); - return mCachedBodyPairs.Find(inKey, inKeyHash); -} - -ContactConstraintManager::BPKeyValue *ContactConstraintManager::ManifoldCache::Create(ContactAllocator &ioContactAllocator, const BodyPair &inKey, uint64 inKeyHash) -{ - JPH_ASSERT(!mIsFinalized); - BPKeyValue *kv = mCachedBodyPairs.Create(ioContactAllocator, inKey, inKeyHash, 0); - if (kv == nullptr) - { - ioContactAllocator.mErrors |= EPhysicsUpdateError::BodyPairCacheFull; - return nullptr; - } - ++ioContactAllocator.mNumBodyPairs; - return kv; -} - -void ContactConstraintManager::ManifoldCache::GetAllBodyPairsSorted(Array &outAll) const -{ - JPH_ASSERT(mIsFinalized); - mCachedBodyPairs.GetAllKeyValues(outAll); - - // Sort by key - QuickSort(outAll.begin(), outAll.end(), [](const BPKeyValue *inLHS, const BPKeyValue *inRHS) { - return inLHS->GetKey() < inRHS->GetKey(); - }); -} - -void ContactConstraintManager::ManifoldCache::GetAllManifoldsSorted(const CachedBodyPair &inBodyPair, Array &outAll) const -{ - JPH_ASSERT(mIsFinalized); - - // Iterate through the attached manifolds - for (uint32 handle = inBodyPair.mFirstCachedManifold; handle != ManifoldMap::cInvalidHandle; handle = FromHandle(handle)->GetValue().mNextWithSameBodyPair) - { - const MKeyValue *kv = mCachedManifolds.FromHandle(handle); - outAll.push_back(kv); - } - - // Sort by key - QuickSort(outAll.begin(), outAll.end(), [](const MKeyValue *inLHS, const MKeyValue *inRHS) { - return inLHS->GetKey() < inRHS->GetKey(); - }); -} - -void ContactConstraintManager::ManifoldCache::GetAllCCDManifoldsSorted(Array &outAll) const -{ - mCachedManifolds.GetAllKeyValues(outAll); - - for (int i = (int)outAll.size() - 1; i >= 0; --i) - if ((outAll[i]->GetValue().mFlags & (uint16)CachedManifold::EFlags::CCDContact) == 0) - { - outAll[i] = outAll.back(); - outAll.pop_back(); - } - - // Sort by key - QuickSort(outAll.begin(), outAll.end(), [](const MKeyValue *inLHS, const MKeyValue *inRHS) { - return inLHS->GetKey() < inRHS->GetKey(); - }); -} - -void ContactConstraintManager::ManifoldCache::ContactPointRemovedCallbacks(ContactListener *inListener) -{ - JPH_PROFILE_FUNCTION(); - - for (MKeyValue &kv : mCachedManifolds) - if ((kv.GetValue().mFlags & uint16(CachedManifold::EFlags::ContactPersisted)) == 0) - inListener->OnContactRemoved(kv.GetKey()); -} - -#ifdef JPH_ENABLE_ASSERTS - -void ContactConstraintManager::ManifoldCache::Finalize() -{ - mIsFinalized = true; - -#ifdef JPH_MANIFOLD_CACHE_DEBUG - Trace("ManifoldMap:"); - mCachedManifolds.TraceStats(); - Trace("BodyPairMap:"); - mCachedBodyPairs.TraceStats(); -#endif // JPH_MANIFOLD_CACHE_DEBUG -} - -#endif - -void ContactConstraintManager::ManifoldCache::SaveState(StateRecorder &inStream, const StateRecorderFilter *inFilter) const -{ - JPH_ASSERT(mIsFinalized); - - // Get contents of cache - Array all_bp; - GetAllBodyPairsSorted(all_bp); - - // Determine which ones to save - Array selected_bp; - if (inFilter == nullptr) - selected_bp = std::move(all_bp); - else - { - selected_bp.reserve(all_bp.size()); - for (const BPKeyValue *bp_kv : all_bp) - if (inFilter->ShouldSaveContact(bp_kv->GetKey().mBodyA, bp_kv->GetKey().mBodyB)) - selected_bp.push_back(bp_kv); - } - - // Write body pairs - size_t num_body_pairs = selected_bp.size(); - inStream.Write(num_body_pairs); - for (const BPKeyValue *bp_kv : selected_bp) - { - // Write body pair key - inStream.Write(bp_kv->GetKey()); - - // Write body pair - const CachedBodyPair &bp = bp_kv->GetValue(); - bp.SaveState(inStream); - - // Get attached manifolds - Array all_m; - GetAllManifoldsSorted(bp, all_m); - - // Write num manifolds - size_t num_manifolds = all_m.size(); - inStream.Write(num_manifolds); - - // Write all manifolds - for (const MKeyValue *m_kv : all_m) - { - // Write key - inStream.Write(m_kv->GetKey()); - const CachedManifold &cm = m_kv->GetValue(); - JPH_ASSERT((cm.mFlags & (uint16)CachedManifold::EFlags::CCDContact) == 0); - - // Write amount of contacts - inStream.Write(cm.mNumContactPoints); - - // Write manifold - cm.SaveState(inStream); - - // Write contact points - for (uint32 i = 0; i < cm.mNumContactPoints; ++i) - cm.mContactPoints[i].SaveState(inStream); - } - } - - // Get CCD manifolds - Array all_m; - GetAllCCDManifoldsSorted(all_m); - - // Determine which ones to save - Array selected_m; - if (inFilter == nullptr) - selected_m = std::move(all_m); - else - { - selected_m.reserve(all_m.size()); - for (const MKeyValue *m_kv : all_m) - if (inFilter->ShouldSaveContact(m_kv->GetKey().GetBody1ID(), m_kv->GetKey().GetBody2ID())) - selected_m.push_back(m_kv); - } - - // Write all CCD manifold keys - size_t num_manifolds = selected_m.size(); - inStream.Write(num_manifolds); - for (const MKeyValue *m_kv : selected_m) - inStream.Write(m_kv->GetKey()); -} - -bool ContactConstraintManager::ManifoldCache::RestoreState(const ManifoldCache &inReadCache, StateRecorder &inStream) -{ - JPH_ASSERT(!mIsFinalized); - - bool success = true; - - // Create a contact allocator for restoring the contact cache - ContactAllocator contact_allocator(GetContactAllocator()); - - // When validating, get all existing body pairs - Array all_bp; - if (inStream.IsValidating()) - inReadCache.GetAllBodyPairsSorted(all_bp); - - // Read amount of body pairs - size_t num_body_pairs; - if (inStream.IsValidating()) - num_body_pairs = all_bp.size(); - inStream.Read(num_body_pairs); - - // Read entire cache - for (size_t i = 0; i < num_body_pairs; ++i) - { - // Read key - BodyPair body_pair_key; - if (inStream.IsValidating() && i < all_bp.size()) - body_pair_key = all_bp[i]->GetKey(); - inStream.Read(body_pair_key); - - // Create new entry for this body pair - uint64 body_pair_hash = body_pair_key.GetHash(); - BPKeyValue *bp_kv = Create(contact_allocator, body_pair_key, body_pair_hash); - if (bp_kv == nullptr) - { - // Out of cache space - success = false; - break; - } - CachedBodyPair &bp = bp_kv->GetValue(); - - // Read body pair - if (inStream.IsValidating() && i < all_bp.size()) - memcpy(&bp, &all_bp[i]->GetValue(), sizeof(CachedBodyPair)); - bp.RestoreState(inStream); - - // When validating, get all existing manifolds - Array all_m; - if (inStream.IsValidating()) - inReadCache.GetAllManifoldsSorted(all_bp[i]->GetValue(), all_m); - - // Read amount of manifolds - size_t num_manifolds; - if (inStream.IsValidating()) - num_manifolds = all_m.size(); - inStream.Read(num_manifolds); - - uint32 handle = ManifoldMap::cInvalidHandle; - for (size_t j = 0; j < num_manifolds; ++j) - { - // Read key - SubShapeIDPair sub_shape_key; - if (inStream.IsValidating() && j < all_m.size()) - sub_shape_key = all_m[j]->GetKey(); - inStream.Read(sub_shape_key); - uint64 sub_shape_key_hash = sub_shape_key.GetHash(); - - // Read amount of contact points - uint16 num_contact_points; - if (inStream.IsValidating() && j < all_m.size()) - num_contact_points = all_m[j]->GetValue().mNumContactPoints; - inStream.Read(num_contact_points); - - // Read manifold - MKeyValue *m_kv = Create(contact_allocator, sub_shape_key, sub_shape_key_hash, num_contact_points); - if (m_kv == nullptr) - { - // Out of cache space - success = false; - break; - } - CachedManifold &cm = m_kv->GetValue(); - if (inStream.IsValidating() && j < all_m.size()) - { - memcpy(&cm, &all_m[j]->GetValue(), CachedManifold::sGetRequiredTotalSize(num_contact_points)); - cm.mNumContactPoints = uint16(num_contact_points); // Restore num contact points - } - cm.RestoreState(inStream); - cm.mNextWithSameBodyPair = handle; - handle = ToHandle(m_kv); - - // Read contact points - for (uint32 k = 0; k < num_contact_points; ++k) - cm.mContactPoints[k].RestoreState(inStream); - } - bp.mFirstCachedManifold = handle; - } - - // When validating, get all existing CCD manifolds - Array all_m; - if (inStream.IsValidating()) - inReadCache.GetAllCCDManifoldsSorted(all_m); - - // Read amount of CCD manifolds - size_t num_manifolds; - if (inStream.IsValidating()) - num_manifolds = all_m.size(); - inStream.Read(num_manifolds); - - for (size_t j = 0; j < num_manifolds; ++j) - { - // Read key - SubShapeIDPair sub_shape_key; - if (inStream.IsValidating() && j < all_m.size()) - sub_shape_key = all_m[j]->GetKey(); - inStream.Read(sub_shape_key); - uint64 sub_shape_key_hash = sub_shape_key.GetHash(); - - // Create CCD manifold - MKeyValue *m_kv = Create(contact_allocator, sub_shape_key, sub_shape_key_hash, 0); - if (m_kv == nullptr) - { - // Out of cache space - success = false; - break; - } - CachedManifold &cm = m_kv->GetValue(); - cm.mFlags |= (uint16)CachedManifold::EFlags::CCDContact; - } - -#ifdef JPH_ENABLE_ASSERTS - mIsFinalized = true; -#endif - - return success; -} - -//////////////////////////////////////////////////////////////////////////////////////////////////////// -// ContactConstraintManager -//////////////////////////////////////////////////////////////////////////////////////////////////////// - -ContactConstraintManager::ContactConstraintManager(const PhysicsSettings &inPhysicsSettings) : - mPhysicsSettings(inPhysicsSettings) -{ -#ifdef JPH_ENABLE_ASSERTS - // For the first frame mark this empty buffer as finalized - mCache[mCacheWriteIdx ^ 1].Finalize(); -#endif -} - -ContactConstraintManager::~ContactConstraintManager() -{ - JPH_ASSERT(mConstraints == nullptr); -} - -void ContactConstraintManager::Init(uint inMaxBodyPairs, uint inMaxContactConstraints) -{ - mMaxConstraints = inMaxContactConstraints; - - // Calculate worst case cache usage - uint cached_manifolds_size = inMaxContactConstraints * (sizeof(CachedManifold) + (MaxContactPoints - 1) * sizeof(CachedContactPoint)); - - // Init the caches - mCache[0].Init(inMaxBodyPairs, inMaxContactConstraints, cached_manifolds_size); - mCache[1].Init(inMaxBodyPairs, inMaxContactConstraints, cached_manifolds_size); -} - -void ContactConstraintManager::PrepareConstraintBuffer(PhysicsUpdateContext *inContext) -{ - // Store context - mUpdateContext = inContext; - - // Allocate temporary constraint buffer - JPH_ASSERT(mConstraints == nullptr); - mConstraints = (ContactConstraint *)inContext->mTempAllocator->Allocate(mMaxConstraints * sizeof(ContactConstraint)); -} - -template -JPH_INLINE void ContactConstraintManager::TemplatedCalculateFrictionAndNonPenetrationConstraintProperties(ContactConstraint &ioConstraint, const ContactSettings &inSettings, float inDeltaTime, RMat44Arg inTransformBody1, RMat44Arg inTransformBody2, const Body &inBody1, const Body &inBody2) -{ - // Calculate scaled mass and inertia - Mat44 inv_i1; - if constexpr (Type1 == EMotionType::Dynamic) - { - const MotionProperties *mp1 = inBody1.GetMotionPropertiesUnchecked(); - inv_i1 = inSettings.mInvInertiaScale1 * mp1->GetInverseInertiaForRotation(inTransformBody1.GetRotation()); - } - else - { - inv_i1 = Mat44::sZero(); - } - - Mat44 inv_i2; - if constexpr (Type2 == EMotionType::Dynamic) - { - const MotionProperties *mp2 = inBody2.GetMotionPropertiesUnchecked(); - inv_i2 = inSettings.mInvInertiaScale2 * mp2->GetInverseInertiaForRotation(inTransformBody2.GetRotation()); - } - else - { - inv_i2 = Mat44::sZero(); - } - - // Calculate tangents - Vec3 t1, t2; - ioConstraint.GetTangents(t1, t2); - - Vec3 ws_normal = ioConstraint.GetWorldSpaceNormal(); - - // Setup velocity constraint properties - float min_velocity_for_restitution = mPhysicsSettings.mMinVelocityForRestitution; - for (WorldContactPoint &wcp : ioConstraint.mContactPoints) - { - RVec3 p1 = inTransformBody1 * Vec3::sLoadFloat3Unsafe(wcp.mContactPoint->mPosition1); - RVec3 p2 = inTransformBody2 * Vec3::sLoadFloat3Unsafe(wcp.mContactPoint->mPosition2); - wcp.TemplatedCalculateFrictionAndNonPenetrationConstraintProperties(inDeltaTime, inBody1, inBody2, ioConstraint.mInvMass1, ioConstraint.mInvMass2, inv_i1, inv_i2, p1, p2, ws_normal, t1, t2, inSettings, min_velocity_for_restitution); - } -} - -inline void ContactConstraintManager::CalculateFrictionAndNonPenetrationConstraintProperties(ContactConstraint &ioConstraint, const ContactSettings &inSettings, float inDeltaTime, RMat44Arg inTransformBody1, RMat44Arg inTransformBody2, const Body &inBody1, const Body &inBody2) -{ - // Dispatch to the correct templated form - switch (inBody1.GetMotionType()) - { - case EMotionType::Dynamic: - switch (inBody2.GetMotionType()) - { - case EMotionType::Dynamic: - TemplatedCalculateFrictionAndNonPenetrationConstraintProperties(ioConstraint, inSettings, inDeltaTime, inTransformBody1, inTransformBody2, inBody1, inBody2); - break; - - case EMotionType::Kinematic: - TemplatedCalculateFrictionAndNonPenetrationConstraintProperties(ioConstraint, inSettings, inDeltaTime, inTransformBody1, inTransformBody2, inBody1, inBody2); - break; - - case EMotionType::Static: - TemplatedCalculateFrictionAndNonPenetrationConstraintProperties(ioConstraint, inSettings, inDeltaTime, inTransformBody1, inTransformBody2, inBody1, inBody2); - break; - - default: - JPH_ASSERT(false); - break; - } - break; - - case EMotionType::Kinematic: - JPH_ASSERT(inBody2.IsDynamic()); - TemplatedCalculateFrictionAndNonPenetrationConstraintProperties(ioConstraint, inSettings, inDeltaTime, inTransformBody1, inTransformBody2, inBody1, inBody2); - break; - - case EMotionType::Static: - JPH_ASSERT(inBody2.IsDynamic()); - TemplatedCalculateFrictionAndNonPenetrationConstraintProperties(ioConstraint, inSettings, inDeltaTime, inTransformBody1, inTransformBody2, inBody1, inBody2); - break; - - default: - JPH_ASSERT(false); - break; - } -} - -void ContactConstraintManager::GetContactsFromCache(ContactAllocator &ioContactAllocator, Body &inBody1, Body &inBody2, bool &outPairHandled, bool &outConstraintCreated) -{ - JPH_PROFILE_FUNCTION(); - - // Start with nothing found and not handled - outConstraintCreated = false; - outPairHandled = false; - - // Swap bodies so that body 1 id < body 2 id - Body *body1, *body2; - if (inBody1.GetID() < inBody2.GetID()) - { - body1 = &inBody1; - body2 = &inBody2; - } - else - { - body1 = &inBody2; - body2 = &inBody1; - } - - // Find the cached body pair - BodyPair body_pair_key(body1->GetID(), body2->GetID()); - uint64 body_pair_hash = body_pair_key.GetHash(); - const ManifoldCache &read_cache = mCache[mCacheWriteIdx ^ 1]; - const BPKeyValue *kv = read_cache.Find(body_pair_key, body_pair_hash); - if (kv == nullptr) - return; - const CachedBodyPair &input_cbp = kv->GetValue(); - - // Get relative translation - Quat inv_r1 = body1->GetRotation().Conjugated(); - Vec3 delta_position = inv_r1 * Vec3(body2->GetCenterOfMassPosition() - body1->GetCenterOfMassPosition()); - - // Get old position delta - Vec3 old_delta_position = Vec3::sLoadFloat3Unsafe(input_cbp.mDeltaPosition); - - // Check if bodies are still roughly in the same relative position - if ((delta_position - old_delta_position).LengthSq() > mPhysicsSettings.mBodyPairCacheMaxDeltaPositionSq) - return; - - // Determine relative orientation - Quat delta_rotation = inv_r1 * body2->GetRotation(); - - // Reconstruct old quaternion delta - Quat old_delta_rotation = Quat::sLoadFloat3Unsafe(input_cbp.mDeltaRotation); - - // Check if bodies are still roughly in the same relative orientation - // The delta between 2 quaternions p and q is: p q^* = [rotation_axis * sin(angle / 2), cos(angle / 2)] - // From the W component we can extract the angle: cos(angle / 2) = px * qx + py * qy + pz * qz + pw * qw = p . q - // Since we want to abort if the rotation is smaller than -angle or bigger than angle, we can write the comparison as |p . q| < cos(angle / 2) - if (abs(delta_rotation.Dot(old_delta_rotation)) < mPhysicsSettings.mBodyPairCacheCosMaxDeltaRotationDiv2) - return; - - // The cache is valid, return that we've handled this body pair - outPairHandled = true; - - // Copy the cached body pair to this frame - ManifoldCache &write_cache = mCache[mCacheWriteIdx]; - BPKeyValue *output_bp_kv = write_cache.Create(ioContactAllocator, body_pair_key, body_pair_hash); - if (output_bp_kv == nullptr) - return; // Out of cache space - CachedBodyPair *output_cbp = &output_bp_kv->GetValue(); - memcpy(output_cbp, &input_cbp, sizeof(CachedBodyPair)); - - // If there were no contacts, we have handled the contact - if (input_cbp.mFirstCachedManifold == ManifoldMap::cInvalidHandle) - return; - - // Get body transforms - RMat44 transform_body1 = body1->GetCenterOfMassTransform(); - RMat44 transform_body2 = body2->GetCenterOfMassTransform(); - - // Get time step - float delta_time = mUpdateContext->mStepDeltaTime; - - // Copy manifolds - uint32 output_handle = ManifoldMap::cInvalidHandle; - uint32 input_handle = input_cbp.mFirstCachedManifold; - do - { - JPH_PROFILE("Add Constraint From Cached Manifold"); - - // Find the existing manifold - const MKeyValue *input_kv = read_cache.FromHandle(input_handle); - const SubShapeIDPair &input_key = input_kv->GetKey(); - const CachedManifold &input_cm = input_kv->GetValue(); - JPH_ASSERT(input_cm.mNumContactPoints > 0); // There should be contact points in this manifold! - - // Create room for manifold in write buffer and copy data - uint64 input_hash = input_key.GetHash(); - MKeyValue *output_kv = write_cache.Create(ioContactAllocator, input_key, input_hash, input_cm.mNumContactPoints); - if (output_kv == nullptr) - break; // Out of cache space - CachedManifold *output_cm = &output_kv->GetValue(); - memcpy(output_cm, &input_cm, CachedManifold::sGetRequiredTotalSize(input_cm.mNumContactPoints)); - - // Link the object under the body pairs - output_cm->mNextWithSameBodyPair = output_handle; - output_handle = write_cache.ToHandle(output_kv); - - // Calculate default contact settings - ContactSettings settings; - settings.mCombinedFriction = mCombineFriction(*body1, input_key.GetSubShapeID1(), *body2, input_key.GetSubShapeID2()); - settings.mCombinedRestitution = mCombineRestitution(*body1, input_key.GetSubShapeID1(), *body2, input_key.GetSubShapeID2()); - settings.mIsSensor = body1->IsSensor() || body2->IsSensor(); - - // Calculate world space contact normal - Vec3 world_space_normal = transform_body2.Multiply3x3(Vec3::sLoadFloat3Unsafe(output_cm->mContactNormal)).Normalized(); - - // Call contact listener to update settings - if (mContactListener != nullptr) - { - // Convert constraint to manifold structure for callback - ContactManifold manifold; - manifold.mWorldSpaceNormal = world_space_normal; - manifold.mSubShapeID1 = input_key.GetSubShapeID1(); - manifold.mSubShapeID2 = input_key.GetSubShapeID2(); - manifold.mBaseOffset = transform_body1.GetTranslation(); - manifold.mRelativeContactPointsOn1.resize(output_cm->mNumContactPoints); - manifold.mRelativeContactPointsOn2.resize(output_cm->mNumContactPoints); - Mat44 local_transform_body2 = transform_body2.PostTranslated(-manifold.mBaseOffset).ToMat44(); - float penetration_depth = -FLT_MAX; - for (uint32 i = 0; i < output_cm->mNumContactPoints; ++i) - { - const CachedContactPoint &ccp = output_cm->mContactPoints[i]; - manifold.mRelativeContactPointsOn1[i] = transform_body1.Multiply3x3(Vec3::sLoadFloat3Unsafe(ccp.mPosition1)); - manifold.mRelativeContactPointsOn2[i] = local_transform_body2 * Vec3::sLoadFloat3Unsafe(ccp.mPosition2); - penetration_depth = max(penetration_depth, (manifold.mRelativeContactPointsOn1[0] - manifold.mRelativeContactPointsOn2[0]).Dot(world_space_normal)); - } - manifold.mPenetrationDepth = penetration_depth; // We don't have the penetration depth anymore, estimate it - - // Notify callback - mContactListener->OnContactPersisted(*body1, *body2, manifold, settings); - } - - JPH_ASSERT(settings.mIsSensor || !(body1->IsSensor() || body2->IsSensor()), "Sensors cannot be converted into regular bodies by a contact callback!"); - if (!settings.mIsSensor // If one of the bodies is a sensor, don't actually create the constraint - && ((body1->IsDynamic() && settings.mInvMassScale1 != 0.0f) // One of the bodies must have mass to be able to create a contact constraint - || (body2->IsDynamic() && settings.mInvMassScale2 != 0.0f))) - { - // Add contact constraint in world space for the solver - uint32 constraint_idx = mNumConstraints++; - if (constraint_idx >= mMaxConstraints) - { - ioContactAllocator.mErrors |= EPhysicsUpdateError::ContactConstraintsFull; - break; - } - - // A constraint will be created - outConstraintCreated = true; - - ContactConstraint &constraint = mConstraints[constraint_idx]; - new (&constraint) ContactConstraint(); - constraint.mBody1 = body1; - constraint.mBody2 = body2; - constraint.mSortKey = input_hash; - world_space_normal.StoreFloat3(&constraint.mWorldSpaceNormal); - constraint.mCombinedFriction = settings.mCombinedFriction; - constraint.mInvMass1 = body1->GetMotionPropertiesUnchecked() != nullptr? settings.mInvMassScale1 * body1->GetMotionPropertiesUnchecked()->GetInverseMassUnchecked() : 0.0f; - constraint.mInvInertiaScale1 = settings.mInvInertiaScale1; - constraint.mInvMass2 = body2->GetMotionPropertiesUnchecked() != nullptr? settings.mInvMassScale2 * body2->GetMotionPropertiesUnchecked()->GetInverseMassUnchecked() : 0.0f; - constraint.mInvInertiaScale2 = settings.mInvInertiaScale2; - constraint.mContactPoints.resize(output_cm->mNumContactPoints); - for (uint32 i = 0; i < output_cm->mNumContactPoints; ++i) - { - CachedContactPoint &ccp = output_cm->mContactPoints[i]; - WorldContactPoint &wcp = constraint.mContactPoints[i]; - wcp.mNonPenetrationConstraint.SetTotalLambda(ccp.mNonPenetrationLambda); - wcp.mFrictionConstraint1.SetTotalLambda(ccp.mFrictionLambda[0]); - wcp.mFrictionConstraint2.SetTotalLambda(ccp.mFrictionLambda[1]); - wcp.mContactPoint = &ccp; - } - - JPH_DET_LOG("GetContactsFromCache: id1: " << constraint.mBody1->GetID() << " id2: " << constraint.mBody2->GetID() << " key: " << constraint.mSortKey); - - // Calculate friction and non-penetration constraint properties for all contact points - CalculateFrictionAndNonPenetrationConstraintProperties(constraint, settings, delta_time, transform_body1, transform_body2, *body1, *body2); - - // Notify island builder - mUpdateContext->mIslandBuilder->LinkContact(constraint_idx, body1->GetIndexInActiveBodiesInternal(), body2->GetIndexInActiveBodiesInternal()); - - #ifdef JPH_DEBUG_RENDERER - // Draw the manifold - if (sDrawContactManifolds) - constraint.Draw(DebugRenderer::sInstance, Color::sYellow); - #endif // JPH_DEBUG_RENDERER - } - - // Mark contact as persisted so that we won't fire OnContactRemoved callbacks - input_cm.mFlags |= (uint16)CachedManifold::EFlags::ContactPersisted; - - // Fetch the next manifold - input_handle = input_cm.mNextWithSameBodyPair; - } - while (input_handle != ManifoldMap::cInvalidHandle); - output_cbp->mFirstCachedManifold = output_handle; -} - -ContactConstraintManager::BodyPairHandle ContactConstraintManager::AddBodyPair(ContactAllocator &ioContactAllocator, const Body &inBody1, const Body &inBody2) -{ - JPH_PROFILE_FUNCTION(); - - // Swap bodies so that body 1 id < body 2 id - const Body *body1, *body2; - if (inBody1.GetID() < inBody2.GetID()) - { - body1 = &inBody1; - body2 = &inBody2; - } - else - { - body1 = &inBody2; - body2 = &inBody1; - } - - // Add an entry - BodyPair body_pair_key(body1->GetID(), body2->GetID()); - uint64 body_pair_hash = body_pair_key.GetHash(); - BPKeyValue *body_pair_kv = mCache[mCacheWriteIdx].Create(ioContactAllocator, body_pair_key, body_pair_hash); - if (body_pair_kv == nullptr) - return nullptr; // Out of cache space - CachedBodyPair *cbp = &body_pair_kv->GetValue(); - cbp->mFirstCachedManifold = ManifoldMap::cInvalidHandle; - - // Get relative translation - Quat inv_r1 = body1->GetRotation().Conjugated(); - Vec3 delta_position = inv_r1 * Vec3(body2->GetCenterOfMassPosition() - body1->GetCenterOfMassPosition()); - - // Store it - delta_position.StoreFloat3(&cbp->mDeltaPosition); - - // Determine relative orientation - Quat delta_rotation = inv_r1 * body2->GetRotation(); - - // Store it - delta_rotation.StoreFloat3(&cbp->mDeltaRotation); - - return cbp; -} - -template -bool ContactConstraintManager::TemplatedAddContactConstraint(ContactAllocator &ioContactAllocator, BodyPairHandle inBodyPairHandle, Body &inBody1, Body &inBody2, const ContactManifold &inManifold) -{ - // Calculate hash - SubShapeIDPair key { inBody1.GetID(), inManifold.mSubShapeID1, inBody2.GetID(), inManifold.mSubShapeID2 }; - uint64 key_hash = key.GetHash(); - - // Determine number of contact points - int num_contact_points = (int)inManifold.mRelativeContactPointsOn1.size(); - JPH_ASSERT(num_contact_points <= MaxContactPoints); - JPH_ASSERT(num_contact_points == (int)inManifold.mRelativeContactPointsOn2.size()); - - // Reserve space for new contact cache entry - // Note that for dynamic vs dynamic we always require the first body to have a lower body id to get a consistent key - // under which to look up the contact - ManifoldCache &write_cache = mCache[mCacheWriteIdx]; - MKeyValue *new_manifold_kv = write_cache.Create(ioContactAllocator, key, key_hash, num_contact_points); - if (new_manifold_kv == nullptr) - return false; // Out of cache space - CachedManifold *new_manifold = &new_manifold_kv->GetValue(); - - // Transform the world space normal to the space of body 2 (this is usually the static body) - RMat44 inverse_transform_body2 = inBody2.GetInverseCenterOfMassTransform(); - inverse_transform_body2.Multiply3x3(inManifold.mWorldSpaceNormal).Normalized().StoreFloat3(&new_manifold->mContactNormal); - - // Settings object that gets passed to the callback - ContactSettings settings; - settings.mCombinedFriction = mCombineFriction(inBody1, inManifold.mSubShapeID1, inBody2, inManifold.mSubShapeID2); - settings.mCombinedRestitution = mCombineRestitution(inBody1, inManifold.mSubShapeID1, inBody2, inManifold.mSubShapeID2); - settings.mIsSensor = inBody1.IsSensor() || inBody2.IsSensor(); - - // Get the contact points for the old cache entry - const ManifoldCache &read_cache = mCache[mCacheWriteIdx ^ 1]; - const MKeyValue *old_manifold_kv = read_cache.Find(key, key_hash); - const CachedContactPoint *ccp_start; - const CachedContactPoint *ccp_end; - if (old_manifold_kv != nullptr) - { - // Call point persisted listener - if (mContactListener != nullptr) - mContactListener->OnContactPersisted(inBody1, inBody2, inManifold, settings); - - // Fetch the contact points from the old manifold - const CachedManifold *old_manifold = &old_manifold_kv->GetValue(); - ccp_start = old_manifold->mContactPoints; - ccp_end = ccp_start + old_manifold->mNumContactPoints; - - // Mark contact as persisted so that we won't fire OnContactRemoved callbacks - old_manifold->mFlags |= (uint16)CachedManifold::EFlags::ContactPersisted; - } - else - { - // Call point added listener - if (mContactListener != nullptr) - mContactListener->OnContactAdded(inBody1, inBody2, inManifold, settings); - - // No contact points available from old manifold - ccp_start = nullptr; - ccp_end = nullptr; - } - - // Get inverse transform for body 1 - RMat44 inverse_transform_body1 = inBody1.GetInverseCenterOfMassTransform(); - - bool contact_constraint_created = false; - - // If one of the bodies is a sensor, don't actually create the constraint - JPH_ASSERT(settings.mIsSensor || !(inBody1.IsSensor() || inBody2.IsSensor()), "Sensors cannot be converted into regular bodies by a contact callback!"); - if (!settings.mIsSensor - && ((inBody1.IsDynamic() && settings.mInvMassScale1 != 0.0f) // One of the bodies must have mass to be able to create a contact constraint - || (inBody2.IsDynamic() && settings.mInvMassScale2 != 0.0f))) - { - // Add contact constraint - uint32 constraint_idx = mNumConstraints++; - if (constraint_idx >= mMaxConstraints) - { - ioContactAllocator.mErrors |= EPhysicsUpdateError::ContactConstraintsFull; - - // Manifold has been created already, we're not filling it in, so we need to reset the contact number of points. - // Note that we don't hook it up to the body pair cache so that it won't be used as a cache during the next simulation. - new_manifold->mNumContactPoints = 0; - return false; - } - - // We will create a contact constraint - contact_constraint_created = true; - - ContactConstraint &constraint = mConstraints[constraint_idx]; - new (&constraint) ContactConstraint(); - constraint.mBody1 = &inBody1; - constraint.mBody2 = &inBody2; - constraint.mSortKey = key_hash; - inManifold.mWorldSpaceNormal.StoreFloat3(&constraint.mWorldSpaceNormal); - constraint.mCombinedFriction = settings.mCombinedFriction; - constraint.mInvMass1 = inBody1.GetMotionPropertiesUnchecked() != nullptr? settings.mInvMassScale1 * inBody1.GetMotionPropertiesUnchecked()->GetInverseMassUnchecked() : 0.0f; - constraint.mInvInertiaScale1 = settings.mInvInertiaScale1; - constraint.mInvMass2 = inBody2.GetMotionPropertiesUnchecked() != nullptr? settings.mInvMassScale2 * inBody2.GetMotionPropertiesUnchecked()->GetInverseMassUnchecked() : 0.0f; - constraint.mInvInertiaScale2 = settings.mInvInertiaScale2; - - JPH_DET_LOG("TemplatedAddContactConstraint: id1: " << constraint.mBody1->GetID() << " id2: " << constraint.mBody2->GetID() << " key: " << constraint.mSortKey); - - // Notify island builder - mUpdateContext->mIslandBuilder->LinkContact(constraint_idx, inBody1.GetIndexInActiveBodiesInternal(), inBody2.GetIndexInActiveBodiesInternal()); - - // Get time step - float delta_time = mUpdateContext->mStepDeltaTime; - - // Calculate scaled mass and inertia - float inv_m1; - Mat44 inv_i1; - if constexpr (Type1 == EMotionType::Dynamic) - { - const MotionProperties *mp1 = inBody1.GetMotionPropertiesUnchecked(); - inv_m1 = settings.mInvMassScale1 * mp1->GetInverseMass(); - inv_i1 = settings.mInvInertiaScale1 * mp1->GetInverseInertiaForRotation(inverse_transform_body1.Transposed3x3()); - } - else - { - inv_m1 = 0.0f; - inv_i1 = Mat44::sZero(); - } - - float inv_m2; - Mat44 inv_i2; - if constexpr (Type2 == EMotionType::Dynamic) - { - const MotionProperties *mp2 = inBody2.GetMotionPropertiesUnchecked(); - inv_m2 = settings.mInvMassScale2 * mp2->GetInverseMass(); - inv_i2 = settings.mInvInertiaScale2 * mp2->GetInverseInertiaForRotation(inverse_transform_body2.Transposed3x3()); - } - else - { - inv_m2 = 0.0f; - inv_i2 = Mat44::sZero(); - } - - // Calculate tangents - Vec3 t1, t2; - constraint.GetTangents(t1, t2); - - constraint.mContactPoints.resize(num_contact_points); - for (int i = 0; i < num_contact_points; ++i) - { - // Convert to world space and set positions - WorldContactPoint &wcp = constraint.mContactPoints[i]; - RVec3 p1_ws = inManifold.mBaseOffset + inManifold.mRelativeContactPointsOn1[i]; - RVec3 p2_ws = inManifold.mBaseOffset + inManifold.mRelativeContactPointsOn2[i]; - - // Convert to local space to the body - Vec3 p1_ls = Vec3(inverse_transform_body1 * p1_ws); - Vec3 p2_ls = Vec3(inverse_transform_body2 * p2_ws); - - // Check if we have a close contact point from last update - bool lambda_set = false; - for (const CachedContactPoint *ccp = ccp_start; ccp < ccp_end; ccp++) - if (Vec3::sLoadFloat3Unsafe(ccp->mPosition1).IsClose(p1_ls, mPhysicsSettings.mContactPointPreserveLambdaMaxDistSq) - && Vec3::sLoadFloat3Unsafe(ccp->mPosition2).IsClose(p2_ls, mPhysicsSettings.mContactPointPreserveLambdaMaxDistSq)) - { - // Get lambdas from previous frame - wcp.mNonPenetrationConstraint.SetTotalLambda(ccp->mNonPenetrationLambda); - wcp.mFrictionConstraint1.SetTotalLambda(ccp->mFrictionLambda[0]); - wcp.mFrictionConstraint2.SetTotalLambda(ccp->mFrictionLambda[1]); - lambda_set = true; - break; - } - if (!lambda_set) - { - wcp.mNonPenetrationConstraint.SetTotalLambda(0.0f); - wcp.mFrictionConstraint1.SetTotalLambda(0.0f); - wcp.mFrictionConstraint2.SetTotalLambda(0.0f); - } - - // Create new contact point - CachedContactPoint &cp = new_manifold->mContactPoints[i]; - p1_ls.StoreFloat3(&cp.mPosition1); - p2_ls.StoreFloat3(&cp.mPosition2); - wcp.mContactPoint = &cp; - - // Setup velocity constraint - wcp.TemplatedCalculateFrictionAndNonPenetrationConstraintProperties(delta_time, inBody1, inBody2, inv_m1, inv_m2, inv_i1, inv_i2, p1_ws, p2_ws, inManifold.mWorldSpaceNormal, t1, t2, settings, mPhysicsSettings.mMinVelocityForRestitution); - } - - #ifdef JPH_DEBUG_RENDERER - // Draw the manifold - if (sDrawContactManifolds) - constraint.Draw(DebugRenderer::sInstance, Color::sOrange); - #endif // JPH_DEBUG_RENDERER - } - else - { - // Store the contact manifold in the cache - for (int i = 0; i < num_contact_points; ++i) - { - // Convert to local space to the body - Vec3 p1 = Vec3(inverse_transform_body1 * (inManifold.mBaseOffset + inManifold.mRelativeContactPointsOn1[i])); - Vec3 p2 = Vec3(inverse_transform_body2 * (inManifold.mBaseOffset + inManifold.mRelativeContactPointsOn2[i])); - - // Create new contact point - CachedContactPoint &cp = new_manifold->mContactPoints[i]; - p1.StoreFloat3(&cp.mPosition1); - p2.StoreFloat3(&cp.mPosition2); - - // Reset contact impulses, we haven't applied any - cp.mNonPenetrationLambda = 0.0f; - cp.mFrictionLambda[0] = 0.0f; - cp.mFrictionLambda[1] = 0.0f; - } - } - - // Store cached contact point in body pair cache - CachedBodyPair *cbp = reinterpret_cast(inBodyPairHandle); - new_manifold->mNextWithSameBodyPair = cbp->mFirstCachedManifold; - cbp->mFirstCachedManifold = write_cache.ToHandle(new_manifold_kv); - - // A contact constraint was added - return contact_constraint_created; -} - -bool ContactConstraintManager::AddContactConstraint(ContactAllocator &ioContactAllocator, BodyPairHandle inBodyPairHandle, Body &inBody1, Body &inBody2, const ContactManifold &inManifold) -{ - JPH_PROFILE_FUNCTION(); - - JPH_DET_LOG("AddContactConstraint: id1: " << inBody1.GetID() << " id2: " << inBody2.GetID() - << " subshape1: " << inManifold.mSubShapeID1 << " subshape2: " << inManifold.mSubShapeID2 - << " normal: " << inManifold.mWorldSpaceNormal << " pendepth: " << inManifold.mPenetrationDepth); - - JPH_ASSERT(inManifold.mWorldSpaceNormal.IsNormalized()); - - // Swap bodies so that body 1 id < body 2 id - const ContactManifold *manifold; - Body *body1, *body2; - ContactManifold temp; - if (inBody2.GetID() < inBody1.GetID()) - { - body1 = &inBody2; - body2 = &inBody1; - temp = inManifold.SwapShapes(); - manifold = &temp; - } - else - { - body1 = &inBody1; - body2 = &inBody2; - manifold = &inManifold; - } - - // Dispatch to the correct templated form - // Note: Non-dynamic vs non-dynamic can happen in this case due to one body being a sensor, so we need to have an extended switch case here - switch (body1->GetMotionType()) - { - case EMotionType::Dynamic: - { - switch (body2->GetMotionType()) - { - case EMotionType::Dynamic: - return TemplatedAddContactConstraint(ioContactAllocator, inBodyPairHandle, *body1, *body2, *manifold); - - case EMotionType::Kinematic: - return TemplatedAddContactConstraint(ioContactAllocator, inBodyPairHandle, *body1, *body2, *manifold); - - case EMotionType::Static: - return TemplatedAddContactConstraint(ioContactAllocator, inBodyPairHandle, *body1, *body2, *manifold); - - default: - JPH_ASSERT(false); - break; - } - break; - } - - case EMotionType::Kinematic: - switch (body2->GetMotionType()) - { - case EMotionType::Dynamic: - return TemplatedAddContactConstraint(ioContactAllocator, inBodyPairHandle, *body1, *body2, *manifold); - - case EMotionType::Kinematic: - return TemplatedAddContactConstraint(ioContactAllocator, inBodyPairHandle, *body1, *body2, *manifold); - - case EMotionType::Static: - return TemplatedAddContactConstraint(ioContactAllocator, inBodyPairHandle, *body1, *body2, *manifold); - - default: - JPH_ASSERT(false); - break; - } - break; - - case EMotionType::Static: - switch (body2->GetMotionType()) - { - case EMotionType::Dynamic: - return TemplatedAddContactConstraint(ioContactAllocator, inBodyPairHandle, *body1, *body2, *manifold); - - case EMotionType::Kinematic: - return TemplatedAddContactConstraint(ioContactAllocator, inBodyPairHandle, *body1, *body2, *manifold); - - case EMotionType::Static: // Static vs static not possible - default: - JPH_ASSERT(false); - break; - } - break; - - default: - JPH_ASSERT(false); - break; - } - - return false; -} - -void ContactConstraintManager::OnCCDContactAdded(ContactAllocator &ioContactAllocator, const Body &inBody1, const Body &inBody2, const ContactManifold &inManifold, ContactSettings &outSettings) -{ - JPH_ASSERT(inManifold.mWorldSpaceNormal.IsNormalized()); - - // Calculate contact settings - outSettings.mCombinedFriction = mCombineFriction(inBody1, inManifold.mSubShapeID1, inBody2, inManifold.mSubShapeID2); - outSettings.mCombinedRestitution = mCombineRestitution(inBody1, inManifold.mSubShapeID1, inBody2, inManifold.mSubShapeID2); - outSettings.mIsSensor = false; // For now, no sensors are supported during CCD - - // The remainder of this function only deals with calling contact callbacks, if there's no contact callback we also don't need to do this work - if (mContactListener != nullptr) - { - // Swap bodies so that body 1 id < body 2 id - const ContactManifold *manifold; - const Body *body1, *body2; - ContactManifold temp; - if (inBody2.GetID() < inBody1.GetID()) - { - body1 = &inBody2; - body2 = &inBody1; - temp = inManifold.SwapShapes(); - manifold = &temp; - } - else - { - body1 = &inBody1; - body2 = &inBody2; - manifold = &inManifold; - } - - // Calculate hash - SubShapeIDPair key { body1->GetID(), manifold->mSubShapeID1, body2->GetID(), manifold->mSubShapeID2 }; - uint64 key_hash = key.GetHash(); - - // Check if we already created this contact this physics update - ManifoldCache &write_cache = mCache[mCacheWriteIdx]; - MKVAndCreated new_manifold_kv = write_cache.FindOrCreate(ioContactAllocator, key, key_hash, 0); - if (new_manifold_kv.second) - { - // This contact is new for this physics update, check if previous update we already had this contact. - const ManifoldCache &read_cache = mCache[mCacheWriteIdx ^ 1]; - const MKeyValue *old_manifold_kv = read_cache.Find(key, key_hash); - if (old_manifold_kv == nullptr) - { - // New contact - mContactListener->OnContactAdded(*body1, *body2, *manifold, outSettings); - } - else - { - // Existing contact - mContactListener->OnContactPersisted(*body1, *body2, *manifold, outSettings); - - // Mark contact as persisted so that we won't fire OnContactRemoved callbacks - old_manifold_kv->GetValue().mFlags |= (uint16)CachedManifold::EFlags::ContactPersisted; - } - - // Check if the cache is full - if (new_manifold_kv.first != nullptr) - { - // We don't store any contact points in this manifold as it is not for caching impulses, we only need to know that the contact was created - CachedManifold &new_manifold = new_manifold_kv.first->GetValue(); - new_manifold.mContactNormal = { 0, 0, 0 }; - new_manifold.mFlags |= (uint16)CachedManifold::EFlags::CCDContact; - } - } - else - { - // Already found this contact this physics update. - // Note that we can trigger OnContactPersisted multiple times per physics update, but otherwise we have no way of obtaining the settings - mContactListener->OnContactPersisted(*body1, *body2, *manifold, outSettings); - } - - // If we swapped body1 and body2 we need to swap the mass scales back - if (manifold == &temp) - { - swap(outSettings.mInvMassScale1, outSettings.mInvMassScale2); - swap(outSettings.mInvInertiaScale1, outSettings.mInvInertiaScale2); - // Note we do not need to negate the relative surface velocity as it is not applied by the CCD collision constraint - } - } - - JPH_ASSERT(outSettings.mIsSensor || !(inBody1.IsSensor() || inBody2.IsSensor()), "Sensors cannot be converted into regular bodies by a contact callback!"); -} - -void ContactConstraintManager::SortContacts(uint32 *inConstraintIdxBegin, uint32 *inConstraintIdxEnd) const -{ - JPH_PROFILE_FUNCTION(); - - QuickSort(inConstraintIdxBegin, inConstraintIdxEnd, [this](uint32 inLHS, uint32 inRHS) { - const ContactConstraint &lhs = mConstraints[inLHS]; - const ContactConstraint &rhs = mConstraints[inRHS]; - - // Most of the time the sort key will be different so we sort on that - if (lhs.mSortKey != rhs.mSortKey) - return lhs.mSortKey < rhs.mSortKey; - - // If they're equal we use the IDs of body 1 to order - if (lhs.mBody1 != rhs.mBody1) - return lhs.mBody1->GetID() < rhs.mBody1->GetID(); - - // If they're still equal we use the IDs of body 2 to order - if (lhs.mBody2 != rhs.mBody2) - return lhs.mBody2->GetID() < rhs.mBody2->GetID(); - - JPH_ASSERT(inLHS == inRHS, "Hash collision, ordering will be inconsistent"); - return false; - }); -} - -void ContactConstraintManager::FinalizeContactCacheAndCallContactPointRemovedCallbacks(uint inExpectedNumBodyPairs, uint inExpectedNumManifolds) -{ - JPH_PROFILE_FUNCTION(); - -#ifdef JPH_ENABLE_ASSERTS - // Mark cache as finalized - ManifoldCache &old_write_cache = mCache[mCacheWriteIdx]; - old_write_cache.Finalize(); - - // Check that the count of body pairs and manifolds that we tracked outside of the cache (to avoid contention on an atomic) is correct - JPH_ASSERT(old_write_cache.GetNumBodyPairs() == inExpectedNumBodyPairs); - JPH_ASSERT(old_write_cache.GetNumManifolds() == inExpectedNumManifolds); -#endif - - // Buffers are now complete, make write buffer the read buffer - mCacheWriteIdx ^= 1; - - // Get the old read cache / new write cache - ManifoldCache &old_read_cache = mCache[mCacheWriteIdx]; - - // Call the contact point removal callbacks - if (mContactListener != nullptr) - old_read_cache.ContactPointRemovedCallbacks(mContactListener); - - // We're done with the old read cache now - old_read_cache.Clear(); - - // Use the amount of contacts from the last iteration to determine the amount of buckets to use in the hash map for the next iteration - old_read_cache.Prepare(inExpectedNumBodyPairs, inExpectedNumManifolds); -} - -bool ContactConstraintManager::WereBodiesInContact(const BodyID &inBody1ID, const BodyID &inBody2ID) const -{ - // The body pair needs to be in the cache and it needs to have a manifold (otherwise it's just a record indicating that there are no collisions) - const ManifoldCache &read_cache = mCache[mCacheWriteIdx ^ 1]; - BodyPair key; - if (inBody1ID < inBody2ID) - key = BodyPair(inBody1ID, inBody2ID); - else - key = BodyPair(inBody2ID, inBody1ID); - uint64 key_hash = key.GetHash(); - const BPKeyValue *kv = read_cache.Find(key, key_hash); - return kv != nullptr && kv->GetValue().mFirstCachedManifold != ManifoldMap::cInvalidHandle; -} - -template -JPH_INLINE void ContactConstraintManager::sWarmStartConstraint(ContactConstraint &ioConstraint, MotionProperties *ioMotionProperties1, MotionProperties *ioMotionProperties2, float inWarmStartImpulseRatio) -{ - // Calculate tangents - Vec3 t1, t2; - ioConstraint.GetTangents(t1, t2); - - Vec3 ws_normal = ioConstraint.GetWorldSpaceNormal(); - - for (WorldContactPoint &wcp : ioConstraint.mContactPoints) - { - // Warm starting: Apply impulse from last frame - if (wcp.mFrictionConstraint1.IsActive() || wcp.mFrictionConstraint2.IsActive()) - { - wcp.mFrictionConstraint1.TemplatedWarmStart(ioMotionProperties1, ioConstraint.mInvMass1, ioMotionProperties2, ioConstraint.mInvMass2, t1, inWarmStartImpulseRatio); - wcp.mFrictionConstraint2.TemplatedWarmStart(ioMotionProperties1, ioConstraint.mInvMass1, ioMotionProperties2, ioConstraint.mInvMass2, t2, inWarmStartImpulseRatio); - } - wcp.mNonPenetrationConstraint.TemplatedWarmStart(ioMotionProperties1, ioConstraint.mInvMass1, ioMotionProperties2, ioConstraint.mInvMass2, ws_normal, inWarmStartImpulseRatio); - } -} - -template -void ContactConstraintManager::WarmStartVelocityConstraints(const uint32 *inConstraintIdxBegin, const uint32 *inConstraintIdxEnd, float inWarmStartImpulseRatio, MotionPropertiesCallback &ioCallback) -{ - JPH_PROFILE_FUNCTION(); - - for (const uint32 *constraint_idx = inConstraintIdxBegin; constraint_idx < inConstraintIdxEnd; ++constraint_idx) - { - ContactConstraint &constraint = mConstraints[*constraint_idx]; - - // Fetch bodies - Body &body1 = *constraint.mBody1; - EMotionType motion_type1 = body1.GetMotionType(); - MotionProperties *motion_properties1 = body1.GetMotionPropertiesUnchecked(); - - Body &body2 = *constraint.mBody2; - EMotionType motion_type2 = body2.GetMotionType(); - MotionProperties *motion_properties2 = body2.GetMotionPropertiesUnchecked(); - - // Dispatch to the correct templated form - // Note: Warm starting doesn't differentiate between kinematic/static bodies so we handle both as static bodies - if (motion_type1 == EMotionType::Dynamic) - { - if (motion_type2 == EMotionType::Dynamic) - { - sWarmStartConstraint(constraint, motion_properties1, motion_properties2, inWarmStartImpulseRatio); - - ioCallback(motion_properties2); - } - else - sWarmStartConstraint(constraint, motion_properties1, motion_properties2, inWarmStartImpulseRatio); - - ioCallback(motion_properties1); - } - else - { - JPH_ASSERT(motion_type2 == EMotionType::Dynamic); - - sWarmStartConstraint(constraint, motion_properties1, motion_properties2, inWarmStartImpulseRatio); - - ioCallback(motion_properties2); - } - } -} - -// Specialize for the two body callback types -template void ContactConstraintManager::WarmStartVelocityConstraints(const uint32 *inConstraintIdxBegin, const uint32 *inConstraintIdxEnd, float inWarmStartImpulseRatio, CalculateSolverSteps &ioCallback); -template void ContactConstraintManager::WarmStartVelocityConstraints(const uint32 *inConstraintIdxBegin, const uint32 *inConstraintIdxEnd, float inWarmStartImpulseRatio, DummyCalculateSolverSteps &ioCallback); - -template -JPH_INLINE bool ContactConstraintManager::sSolveVelocityConstraint(ContactConstraint &ioConstraint, MotionProperties *ioMotionProperties1, MotionProperties *ioMotionProperties2) -{ - bool any_impulse_applied = false; - - // Calculate tangents - Vec3 t1, t2; - ioConstraint.GetTangents(t1, t2); - - // First apply all friction constraints (non-penetration is more important than friction) - for (WorldContactPoint &wcp : ioConstraint.mContactPoints) - { - // Check if friction is enabled - if (wcp.mFrictionConstraint1.IsActive() || wcp.mFrictionConstraint2.IsActive()) - { - // Calculate impulse to stop motion in tangential direction - float lambda1 = wcp.mFrictionConstraint1.TemplatedSolveVelocityConstraintGetTotalLambda(ioMotionProperties1, ioMotionProperties2, t1); - float lambda2 = wcp.mFrictionConstraint2.TemplatedSolveVelocityConstraintGetTotalLambda(ioMotionProperties1, ioMotionProperties2, t2); - float total_lambda_sq = Square(lambda1) + Square(lambda2); - - // Calculate max impulse that can be applied. Note that we're using the non-penetration impulse from the previous iteration here. - // We do this because non-penetration is more important so is solved last (the last things that are solved in an iterative solver - // contribute the most). - float max_lambda_f = ioConstraint.mCombinedFriction * wcp.mNonPenetrationConstraint.GetTotalLambda(); - - // If the total lambda that we will apply is too large, scale it back - if (total_lambda_sq > Square(max_lambda_f)) - { - float scale = max_lambda_f / sqrt(total_lambda_sq); - lambda1 *= scale; - lambda2 *= scale; - } - - // Apply the friction impulse - if (wcp.mFrictionConstraint1.TemplatedSolveVelocityConstraintApplyLambda(ioMotionProperties1, ioConstraint.mInvMass1, ioMotionProperties2, ioConstraint.mInvMass2, t1, lambda1)) - any_impulse_applied = true; - if (wcp.mFrictionConstraint2.TemplatedSolveVelocityConstraintApplyLambda(ioMotionProperties1, ioConstraint.mInvMass1, ioMotionProperties2, ioConstraint.mInvMass2, t2, lambda2)) - any_impulse_applied = true; - } - } - - Vec3 ws_normal = ioConstraint.GetWorldSpaceNormal(); - - // Then apply all non-penetration constraints - for (WorldContactPoint &wcp : ioConstraint.mContactPoints) - { - // Solve non penetration velocities - if (wcp.mNonPenetrationConstraint.TemplatedSolveVelocityConstraint(ioMotionProperties1, ioConstraint.mInvMass1, ioMotionProperties2, ioConstraint.mInvMass2, ws_normal, 0.0f, FLT_MAX)) - any_impulse_applied = true; - } - - return any_impulse_applied; -} - -bool ContactConstraintManager::SolveVelocityConstraints(const uint32 *inConstraintIdxBegin, const uint32 *inConstraintIdxEnd) -{ - JPH_PROFILE_FUNCTION(); - - bool any_impulse_applied = false; - - for (const uint32 *constraint_idx = inConstraintIdxBegin; constraint_idx < inConstraintIdxEnd; ++constraint_idx) - { - ContactConstraint &constraint = mConstraints[*constraint_idx]; - - // Fetch bodies - Body &body1 = *constraint.mBody1; - EMotionType motion_type1 = body1.GetMotionType(); - MotionProperties *motion_properties1 = body1.GetMotionPropertiesUnchecked(); - - Body &body2 = *constraint.mBody2; - EMotionType motion_type2 = body2.GetMotionType(); - MotionProperties *motion_properties2 = body2.GetMotionPropertiesUnchecked(); - - // Dispatch to the correct templated form - switch (motion_type1) - { - case EMotionType::Dynamic: - switch (motion_type2) - { - case EMotionType::Dynamic: - any_impulse_applied |= sSolveVelocityConstraint(constraint, motion_properties1, motion_properties2); - break; - - case EMotionType::Kinematic: - any_impulse_applied |= sSolveVelocityConstraint(constraint, motion_properties1, motion_properties2); - break; - - case EMotionType::Static: - any_impulse_applied |= sSolveVelocityConstraint(constraint, motion_properties1, motion_properties2); - break; - - default: - JPH_ASSERT(false); - break; - } - break; - - case EMotionType::Kinematic: - JPH_ASSERT(motion_type2 == EMotionType::Dynamic); - any_impulse_applied |= sSolveVelocityConstraint(constraint, motion_properties1, motion_properties2); - break; - - case EMotionType::Static: - JPH_ASSERT(motion_type2 == EMotionType::Dynamic); - any_impulse_applied |= sSolveVelocityConstraint(constraint, motion_properties1, motion_properties2); - break; - - default: - JPH_ASSERT(false); - break; - } - } - - return any_impulse_applied; -} - -void ContactConstraintManager::StoreAppliedImpulses(const uint32 *inConstraintIdxBegin, const uint32 *inConstraintIdxEnd) const -{ - // Copy back total applied impulse to cache for the next frame - for (const uint32 *constraint_idx = inConstraintIdxBegin; constraint_idx < inConstraintIdxEnd; ++constraint_idx) - { - const ContactConstraint &constraint = mConstraints[*constraint_idx]; - - for (const WorldContactPoint &wcp : constraint.mContactPoints) - { - wcp.mContactPoint->mNonPenetrationLambda = wcp.mNonPenetrationConstraint.GetTotalLambda(); - wcp.mContactPoint->mFrictionLambda[0] = wcp.mFrictionConstraint1.GetTotalLambda(); - wcp.mContactPoint->mFrictionLambda[1] = wcp.mFrictionConstraint2.GetTotalLambda(); - } - } -} - -bool ContactConstraintManager::SolvePositionConstraints(const uint32 *inConstraintIdxBegin, const uint32 *inConstraintIdxEnd) -{ - JPH_PROFILE_FUNCTION(); - - bool any_impulse_applied = false; - - for (const uint32 *constraint_idx = inConstraintIdxBegin; constraint_idx < inConstraintIdxEnd; ++constraint_idx) - { - ContactConstraint &constraint = mConstraints[*constraint_idx]; - - // Fetch bodies - Body &body1 = *constraint.mBody1; - Body &body2 = *constraint.mBody2; - - // Get transforms - RMat44 transform1 = body1.GetCenterOfMassTransform(); - RMat44 transform2 = body2.GetCenterOfMassTransform(); - - Vec3 ws_normal = constraint.GetWorldSpaceNormal(); - - for (WorldContactPoint &wcp : constraint.mContactPoints) - { - // Calculate new contact point positions in world space (the bodies may have moved) - RVec3 p1 = transform1 * Vec3::sLoadFloat3Unsafe(wcp.mContactPoint->mPosition1); - RVec3 p2 = transform2 * Vec3::sLoadFloat3Unsafe(wcp.mContactPoint->mPosition2); - - // Calculate separation along the normal (negative if interpenetrating) - // Allow a little penetration by default (PhysicsSettings::mPenetrationSlop) to avoid jittering between contact/no-contact which wipes out the contact cache and warm start impulses - // Clamp penetration to a max PhysicsSettings::mMaxPenetrationDistance so that we don't apply a huge impulse if we're penetrating a lot - float separation = max(Vec3(p2 - p1).Dot(ws_normal) + mPhysicsSettings.mPenetrationSlop, -mPhysicsSettings.mMaxPenetrationDistance); - - // Only enforce constraint when separation < 0 (otherwise we're apart) - if (separation < 0.0f) - { - // Update constraint properties (bodies may have moved) - wcp.CalculateNonPenetrationConstraintProperties(body1, constraint.mInvMass1, constraint.mInvInertiaScale1, body2, constraint.mInvMass2, constraint.mInvInertiaScale2, p1, p2, ws_normal); - - // Solve position errors - if (wcp.mNonPenetrationConstraint.SolvePositionConstraintWithMassOverride(body1, constraint.mInvMass1, body2, constraint.mInvMass2, ws_normal, separation, mPhysicsSettings.mBaumgarte)) - any_impulse_applied = true; - } - } - } - - return any_impulse_applied; -} - -void ContactConstraintManager::RecycleConstraintBuffer() -{ - // Reset constraint array - mNumConstraints = 0; -} - -void ContactConstraintManager::FinishConstraintBuffer() -{ - // Free constraints buffer - mUpdateContext->mTempAllocator->Free(mConstraints, mMaxConstraints * sizeof(ContactConstraint)); - mConstraints = nullptr; - mNumConstraints = 0; - - // Reset update context - mUpdateContext = nullptr; -} - -void ContactConstraintManager::SaveState(StateRecorder &inStream, const StateRecorderFilter *inFilter) const -{ - mCache[mCacheWriteIdx ^ 1].SaveState(inStream, inFilter); -} - -bool ContactConstraintManager::RestoreState(StateRecorder &inStream) -{ - bool success = mCache[mCacheWriteIdx].RestoreState(mCache[mCacheWriteIdx ^ 1], inStream); - mCacheWriteIdx ^= 1; - mCache[mCacheWriteIdx].Clear(); - return success; -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ContactConstraintManager.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ContactConstraintManager.h deleted file mode 100644 index cb9d7d80f5c..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/ContactConstraintManager.h +++ /dev/null @@ -1,513 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -JPH_SUPPRESS_WARNINGS_STD_BEGIN -#include -JPH_SUPPRESS_WARNINGS_STD_END - -JPH_NAMESPACE_BEGIN - -struct PhysicsSettings; -class PhysicsUpdateContext; - -class JPH_EXPORT ContactConstraintManager : public NonCopyable -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Constructor - explicit ContactConstraintManager(const PhysicsSettings &inPhysicsSettings); - ~ContactConstraintManager(); - - /// Initialize the system. - /// @param inMaxBodyPairs Maximum amount of body pairs to process (anything else will fall through the world), this number should generally be much higher than the max amount of contact points as there will be lots of bodies close that are not actually touching - /// @param inMaxContactConstraints Maximum amount of contact constraints to process (anything else will fall through the world) - void Init(uint inMaxBodyPairs, uint inMaxContactConstraints); - - /// Listener that is notified whenever a contact point between two bodies is added/updated/removed - void SetContactListener(ContactListener *inListener) { mContactListener = inListener; } - ContactListener * GetContactListener() const { return mContactListener; } - - /// Callback function to combine the restitution or friction of two bodies - /// Note that when merging manifolds (when PhysicsSettings::mUseManifoldReduction is true) you will only get a callback for the merged manifold. - /// It is not possible in that case to get all sub shape ID pairs that were colliding, you'll get the first encountered pair. - using CombineFunction = float (*)(const Body &inBody1, const SubShapeID &inSubShapeID1, const Body &inBody2, const SubShapeID &inSubShapeID2); - - /// Set the function that combines the friction of two bodies and returns it - /// Default method is the geometric mean: sqrt(friction1 * friction2). - void SetCombineFriction(CombineFunction inCombineFriction) { mCombineFriction = inCombineFriction; } - CombineFunction GetCombineFriction() const { return mCombineFriction; } - - /// Set the function that combines the restitution of two bodies and returns it - /// Default method is max(restitution1, restitution1) - void SetCombineRestitution(CombineFunction inCombineRestitution) { mCombineRestitution = inCombineRestitution; } - CombineFunction GetCombineRestitution() const { return mCombineRestitution; } - - /// Get the max number of contact constraints that are allowed - uint32 GetMaxConstraints() const { return mMaxConstraints; } - - /// Check with the listener if inBody1 and inBody2 could collide, returns false if not - inline ValidateResult ValidateContactPoint(const Body &inBody1, const Body &inBody2, RVec3Arg inBaseOffset, const CollideShapeResult &inCollisionResult) const - { - if (mContactListener == nullptr) - return ValidateResult::AcceptAllContactsForThisBodyPair; - - return mContactListener->OnContactValidate(inBody1, inBody2, inBaseOffset, inCollisionResult); - } - - /// Sets up the constraint buffer. Should be called before starting collision detection. - void PrepareConstraintBuffer(PhysicsUpdateContext *inContext); - - /// Max 4 contact points are needed for a stable manifold - static const int MaxContactPoints = 4; - - /// Contacts are allocated in a lock free hash map - class ContactAllocator : public LFHMAllocatorContext - { - public: - using LFHMAllocatorContext::LFHMAllocatorContext; - - uint mNumBodyPairs = 0; ///< Total number of body pairs added using this allocator - uint mNumManifolds = 0; ///< Total number of manifolds added using this allocator - EPhysicsUpdateError mErrors = EPhysicsUpdateError::None; ///< Errors reported on this allocator - }; - - /// Get a new allocator context for storing contacts. Note that you should call this once and then add multiple contacts using the context. - ContactAllocator GetContactAllocator() { return mCache[mCacheWriteIdx].GetContactAllocator(); } - - /// Check if the contact points from the previous frame are reusable and if so copy them. - /// When the cache was usable and the pair has been handled: outPairHandled = true. - /// When a contact constraint was produced: outConstraintCreated = true. - void GetContactsFromCache(ContactAllocator &ioContactAllocator, Body &inBody1, Body &inBody2, bool &outPairHandled, bool &outConstraintCreated); - - /// Handle used to keep track of the current body pair - using BodyPairHandle = void *; - - /// Create a handle for a colliding body pair so that contact constraints can be added between them. - /// Needs to be called once per body pair per frame before calling AddContactConstraint. - BodyPairHandle AddBodyPair(ContactAllocator &ioContactAllocator, const Body &inBody1, const Body &inBody2); - - /// Add a contact constraint for this frame. - /// - /// @param ioContactAllocator The allocator that reserves memory for the contacts - /// @param inBodyPair The handle for the contact cache for this body pair - /// @param inBody1 The first body that is colliding - /// @param inBody2 The second body that is colliding - /// @param inManifold The manifold that describes the collision - /// @return true if a contact constraint was created (can be false in the case of a sensor) - /// - /// This is using the approach described in 'Modeling and Solving Constraints' by Erin Catto presented at GDC 2009 (and later years with slight modifications). - /// We're using the formulas from slide 50 - 53 combined. - /// - /// Euler velocity integration: - /// - /// v1' = v1 + M^-1 P - /// - /// Impulse: - /// - /// P = J^T lambda - /// - /// Constraint force: - /// - /// lambda = -K^-1 J v1 - /// - /// Inverse effective mass: - /// - /// K = J M^-1 J^T - /// - /// Constraint equation (limits movement in 1 axis): - /// - /// C = (p2 - p1) . n - /// - /// Jacobian (for position constraint) - /// - /// J = [-n, -r1 x n, n, r2 x n] - /// - /// n = contact normal (pointing away from body 1). - /// p1, p2 = positions of collision on body 1 and 2. - /// r1, r2 = contact point relative to center of mass of body 1 and body 2 (r1 = p1 - x1, r2 = p2 - x2). - /// v1, v2 = (linear velocity, angular velocity): 6 vectors containing linear and angular velocity for body 1 and 2. - /// M = mass matrix, a diagonal matrix of the mass and inertia with diagonal [m1, I1, m2, I2]. - bool AddContactConstraint(ContactAllocator &ioContactAllocator, BodyPairHandle inBodyPair, Body &inBody1, Body &inBody2, const ContactManifold &inManifold); - - /// Finalizes the contact cache, the contact cache that was generated during the calls to AddContactConstraint in this update - /// will be used from now on to read from. After finalizing the contact cache, the contact removed callbacks will be called. - /// inExpectedNumBodyPairs / inExpectedNumManifolds are the amount of body pairs / manifolds found in the previous step and is - /// used to determine the amount of buckets the contact cache hash map will use in the next update. - void FinalizeContactCacheAndCallContactPointRemovedCallbacks(uint inExpectedNumBodyPairs, uint inExpectedNumManifolds); - - /// Check if 2 bodies were in contact during the last simulation step. Since contacts are only detected between active bodies, at least one of the bodies must be active. - /// Uses the read collision cache to determine if 2 bodies are in contact. - bool WereBodiesInContact(const BodyID &inBody1ID, const BodyID &inBody2ID) const; - - /// Get the number of contact constraints that were found - uint32 GetNumConstraints() const { return min(mNumConstraints, mMaxConstraints); } - - /// Sort contact constraints deterministically - void SortContacts(uint32 *inConstraintIdxBegin, uint32 *inConstraintIdxEnd) const; - - /// Get the affected bodies for a given constraint - inline void GetAffectedBodies(uint32 inConstraintIdx, const Body *&outBody1, const Body *&outBody2) const - { - const ContactConstraint &constraint = mConstraints[inConstraintIdx]; - outBody1 = constraint.mBody1; - outBody2 = constraint.mBody2; - } - - /// Apply last frame's impulses as an initial guess for this frame's impulses - template - void WarmStartVelocityConstraints(const uint32 *inConstraintIdxBegin, const uint32 *inConstraintIdxEnd, float inWarmStartImpulseRatio, MotionPropertiesCallback &ioCallback); - - /// Solve velocity constraints, when almost nothing changes this should only apply very small impulses - /// since we're warm starting with the total impulse applied in the last frame above. - /// - /// Friction wise we're using the Coulomb friction model which says that: - /// - /// |F_T| <= mu |F_N| - /// - /// Where F_T is the tangential force, F_N is the normal force and mu is the friction coefficient - /// - /// In impulse terms this becomes: - /// - /// |lambda_T| <= mu |lambda_N| - /// - /// And the constraint that needs to be applied is exactly the same as a non penetration constraint - /// except that we use a tangent instead of a normal. The tangent should point in the direction of the - /// tangential velocity of the point: - /// - /// J = [-T, -r1 x T, T, r2 x T] - /// - /// Where T is the tangent. - /// - /// See slide 42 and 43. - /// - /// Restitution is implemented as a velocity bias (see slide 41): - /// - /// b = e v_n^- - /// - /// e = the restitution coefficient, v_n^- is the normal velocity prior to the collision - /// - /// Restitution is only applied when v_n^- is large enough and the points are moving towards collision - bool SolveVelocityConstraints(const uint32 *inConstraintIdxBegin, const uint32 *inConstraintIdxEnd); - - /// Save back the lambdas to the contact cache for the next warm start - void StoreAppliedImpulses(const uint32 *inConstraintIdxBegin, const uint32 *inConstraintIdxEnd) const; - - /// Solve position constraints. - /// This is using the approach described in 'Modeling and Solving Constraints' by Erin Catto presented at GDC 2007. - /// On slide 78 it is suggested to split up the Baumgarte stabilization for positional drift so that it does not - /// actually add to the momentum. We combine an Euler velocity integrate + a position integrate and then discard the velocity - /// change. - /// - /// Constraint force: - /// - /// lambda = -K^-1 b - /// - /// Baumgarte stabilization: - /// - /// b = beta / dt C - /// - /// beta = baumgarte stabilization factor. - /// dt = delta time. - bool SolvePositionConstraints(const uint32 *inConstraintIdxBegin, const uint32 *inConstraintIdxEnd); - - /// Recycle the constraint buffer. Should be called between collision simulation steps. - void RecycleConstraintBuffer(); - - /// Terminate the constraint buffer. Should be called after simulation ends. - void FinishConstraintBuffer(); - - /// Called by continuous collision detection to notify the contact listener that a contact was added - /// @param ioContactAllocator The allocator that reserves memory for the contacts - /// @param inBody1 The first body that is colliding - /// @param inBody2 The second body that is colliding - /// @param inManifold The manifold that describes the collision - /// @param outSettings The calculated contact settings (may be overridden by the contact listener) - void OnCCDContactAdded(ContactAllocator &ioContactAllocator, const Body &inBody1, const Body &inBody2, const ContactManifold &inManifold, ContactSettings &outSettings); - -#ifdef JPH_DEBUG_RENDERER - // Drawing properties - static bool sDrawContactPoint; - static bool sDrawSupportingFaces; - static bool sDrawContactPointReduction; - static bool sDrawContactManifolds; -#endif // JPH_DEBUG_RENDERER - - /// Saving state for replay - void SaveState(StateRecorder &inStream, const StateRecorderFilter *inFilter) const; - - /// Restoring state for replay. Returns false when failed. - bool RestoreState(StateRecorder &inStream); - -private: - /// Local space contact point, used for caching impulses - class CachedContactPoint - { - public: - /// Saving / restoring state for replay - void SaveState(StateRecorder &inStream) const; - void RestoreState(StateRecorder &inStream); - - /// Local space positions on body 1 and 2. - /// Note: these values are read through sLoadFloat3Unsafe. - Float3 mPosition1; - Float3 mPosition2; - - /// Total applied impulse during the last update that it was used - float mNonPenetrationLambda; - Vector<2> mFrictionLambda; - }; - - static_assert(sizeof(CachedContactPoint) == 36, "Unexpected size"); - static_assert(alignof(CachedContactPoint) == 4, "Assuming 4 byte aligned"); - - /// A single cached manifold - class CachedManifold - { - public: - /// Calculate size in bytes needed beyond the size of the class to store inNumContactPoints - static int sGetRequiredExtraSize(int inNumContactPoints) { return max(0, inNumContactPoints - 1) * sizeof(CachedContactPoint); } - - /// Calculate total class size needed for storing inNumContactPoints - static int sGetRequiredTotalSize(int inNumContactPoints) { return sizeof(CachedManifold) + sGetRequiredExtraSize(inNumContactPoints); } - - /// Saving / restoring state for replay - void SaveState(StateRecorder &inStream) const; - void RestoreState(StateRecorder &inStream); - - /// Handle to next cached contact points in ManifoldCache::mCachedManifolds for the same body pair - uint32 mNextWithSameBodyPair; - - /// Contact normal in the space of 2. - /// Note: this value is read through sLoadFloat3Unsafe. - Float3 mContactNormal; - - /// Flags for this cached manifold - enum class EFlags : uint16 - { - ContactPersisted = 1, ///< If this cache entry was reused in the next simulation update - CCDContact = 2 ///< This is a cached manifold reported by continuous collision detection and was only used to create a contact callback - }; - - /// @see EFlags - mutable atomic mFlags { 0 }; - - /// Number of contact points in the array below - uint16 mNumContactPoints; - - /// Contact points that this manifold consists of - CachedContactPoint mContactPoints[1]; - }; - - static_assert(sizeof(CachedManifold) == 56, "This structure is expect to not contain any waste due to alignment"); - static_assert(alignof(CachedManifold) == 4, "Assuming 4 byte aligned"); - - /// Define a map that maps SubShapeIDPair -> manifold - using ManifoldMap = LockFreeHashMap; - using MKeyValue = ManifoldMap::KeyValue; - using MKVAndCreated = pair; - - /// Start of list of contact points for a particular pair of bodies - class CachedBodyPair - { - public: - /// Saving / restoring state for replay - void SaveState(StateRecorder &inStream) const; - void RestoreState(StateRecorder &inStream); - - /// Local space position difference from Body A to Body B. - /// Note: this value is read through sLoadFloat3Unsafe - Float3 mDeltaPosition; - - /// Local space rotation difference from Body A to Body B, fourth component of quaternion is not stored but is guaranteed >= 0. - /// Note: this value is read through sLoadFloat3Unsafe - Float3 mDeltaRotation; - - /// Handle to first manifold in ManifoldCache::mCachedManifolds - uint32 mFirstCachedManifold; - }; - - static_assert(sizeof(CachedBodyPair) == 28, "Unexpected size"); - static_assert(alignof(CachedBodyPair) == 4, "Assuming 4 byte aligned"); - - /// Define a map that maps BodyPair -> CachedBodyPair - using BodyPairMap = LockFreeHashMap; - using BPKeyValue = BodyPairMap::KeyValue; - - /// Holds all caches that are needed to quickly find cached body pairs / manifolds - class ManifoldCache - { - public: - /// Initialize the cache - void Init(uint inMaxBodyPairs, uint inMaxContactConstraints, uint inCachedManifoldsSize); - - /// Reset all entries from the cache - void Clear(); - - /// Prepare cache before creating new contacts. - /// inExpectedNumBodyPairs / inExpectedNumManifolds are the amount of body pairs / manifolds found in the previous step and is used to determine the amount of buckets the contact cache hash map will use. - void Prepare(uint inExpectedNumBodyPairs, uint inExpectedNumManifolds); - - /// Get a new allocator context for storing contacts. Note that you should call this once and then add multiple contacts using the context. - ContactAllocator GetContactAllocator() { return ContactAllocator(mAllocator, cAllocatorBlockSize); } - - /// Find / create cached entry for SubShapeIDPair -> CachedManifold - const MKeyValue * Find(const SubShapeIDPair &inKey, uint64 inKeyHash) const; - MKeyValue * Create(ContactAllocator &ioContactAllocator, const SubShapeIDPair &inKey, uint64 inKeyHash, int inNumContactPoints); - MKVAndCreated FindOrCreate(ContactAllocator &ioContactAllocator, const SubShapeIDPair &inKey, uint64 inKeyHash, int inNumContactPoints); - uint32 ToHandle(const MKeyValue *inKeyValue) const; - const MKeyValue * FromHandle(uint32 inHandle) const; - - /// Find / create entry for BodyPair -> CachedBodyPair - const BPKeyValue * Find(const BodyPair &inKey, uint64 inKeyHash) const; - BPKeyValue * Create(ContactAllocator &ioContactAllocator, const BodyPair &inKey, uint64 inKeyHash); - void GetAllBodyPairsSorted(Array &outAll) const; - void GetAllManifoldsSorted(const CachedBodyPair &inBodyPair, Array &outAll) const; - void GetAllCCDManifoldsSorted(Array &outAll) const; - void ContactPointRemovedCallbacks(ContactListener *inListener); - -#ifdef JPH_ENABLE_ASSERTS - /// Get the amount of manifolds in the cache - uint GetNumManifolds() const { return mCachedManifolds.GetNumKeyValues(); } - - /// Get the amount of body pairs in the cache - uint GetNumBodyPairs() const { return mCachedBodyPairs.GetNumKeyValues(); } - - /// Before a cache is finalized you can only do Create(), after only Find() or Clear() - void Finalize(); -#endif - - /// Saving / restoring state for replay - void SaveState(StateRecorder &inStream, const StateRecorderFilter *inFilter) const; - bool RestoreState(const ManifoldCache &inReadCache, StateRecorder &inStream); - - private: - /// Block size used when allocating new blocks in the contact cache - static constexpr uint32 cAllocatorBlockSize = 4096; - - /// Allocator used by both mCachedManifolds and mCachedBodyPairs, this makes it more likely that a body pair and its manifolds are close in memory - LFHMAllocator mAllocator; - - /// Simple hash map for SubShapeIDPair -> CachedManifold - ManifoldMap mCachedManifolds { mAllocator }; - - /// Simple hash map for BodyPair -> CachedBodyPair - BodyPairMap mCachedBodyPairs { mAllocator }; - -#ifdef JPH_ENABLE_ASSERTS - bool mIsFinalized = false; ///< Marks if this buffer is complete -#endif - }; - - ManifoldCache mCache[2]; ///< We have one cache to read from and one to write to - int mCacheWriteIdx = 0; ///< Which cache we're currently writing to - - /// World space contact point, used for solving penetrations - class WorldContactPoint - { - public: - /// Calculate constraint properties below - void CalculateNonPenetrationConstraintProperties(const Body &inBody1, float inInvMass1, float inInvInertiaScale1, const Body &inBody2, float inInvMass2, float inInvInertiaScale2, RVec3Arg inWorldSpacePosition1, RVec3Arg inWorldSpacePosition2, Vec3Arg inWorldSpaceNormal); - - template - JPH_INLINE void TemplatedCalculateFrictionAndNonPenetrationConstraintProperties(float inDeltaTime, const Body &inBody1, const Body &inBody2, float inInvM1, float inInvM2, Mat44Arg inInvI1, Mat44Arg inInvI2, RVec3Arg inWorldSpacePosition1, RVec3Arg inWorldSpacePosition2, Vec3Arg inWorldSpaceNormal, Vec3Arg inWorldSpaceTangent1, Vec3Arg inWorldSpaceTangent2, const ContactSettings &inSettings, float inMinVelocityForRestitution); - - /// The constraint parts - AxisConstraintPart mNonPenetrationConstraint; - AxisConstraintPart mFrictionConstraint1; - AxisConstraintPart mFrictionConstraint2; - - /// Contact cache - CachedContactPoint * mContactPoint; - }; - - using WorldContactPoints = StaticArray; - - /// Contact constraint class, used for solving penetrations - class ContactConstraint - { - public: - #ifdef JPH_DEBUG_RENDERER - /// Draw the state of the contact constraint - void Draw(DebugRenderer *inRenderer, ColorArg inManifoldColor) const; - #endif // JPH_DEBUG_RENDERER - - /// Convert the world space normal to a Vec3 - JPH_INLINE Vec3 GetWorldSpaceNormal() const - { - return Vec3::sLoadFloat3Unsafe(mWorldSpaceNormal); - } - - /// Get the tangents for this contact constraint - JPH_INLINE void GetTangents(Vec3 &outTangent1, Vec3 &outTangent2) const - { - Vec3 ws_normal = GetWorldSpaceNormal(); - outTangent1 = ws_normal.GetNormalizedPerpendicular(); - outTangent2 = ws_normal.Cross(outTangent1); - } - - Body * mBody1; - Body * mBody2; - uint64 mSortKey; - Float3 mWorldSpaceNormal; - float mCombinedFriction; - float mInvMass1; - float mInvInertiaScale1; - float mInvMass2; - float mInvInertiaScale2; - WorldContactPoints mContactPoints; - }; - - /// Internal helper function to calculate the friction and non-penetration constraint properties. Templated to the motion type to reduce the amount of branches and calculations. - template - JPH_INLINE void TemplatedCalculateFrictionAndNonPenetrationConstraintProperties(ContactConstraint &ioConstraint, const ContactSettings &inSettings, float inDeltaTime, RMat44Arg inTransformBody1, RMat44Arg inTransformBody2, const Body &inBody1, const Body &inBody2); - - /// Internal helper function to calculate the friction and non-penetration constraint properties. - inline void CalculateFrictionAndNonPenetrationConstraintProperties(ContactConstraint &ioConstraint, const ContactSettings &inSettings, float inDeltaTime, RMat44Arg inTransformBody1, RMat44Arg inTransformBody2, const Body &inBody1, const Body &inBody2); - - /// Internal helper function to add a contact constraint. Templated to the motion type to reduce the amount of branches and calculations. - template - bool TemplatedAddContactConstraint(ContactAllocator &ioContactAllocator, BodyPairHandle inBodyPairHandle, Body &inBody1, Body &inBody2, const ContactManifold &inManifold); - - /// Internal helper function to warm start contact constraint. Templated to the motion type to reduce the amount of branches and calculations. - template - JPH_INLINE static void sWarmStartConstraint(ContactConstraint &ioConstraint, MotionProperties *ioMotionProperties1, MotionProperties *ioMotionProperties2, float inWarmStartImpulseRatio); - - /// Internal helper function to solve a single contact constraint. Templated to the motion type to reduce the amount of branches and calculations. - template - JPH_INLINE static bool sSolveVelocityConstraint(ContactConstraint &ioConstraint, MotionProperties *ioMotionProperties1, MotionProperties *ioMotionProperties2); - - /// The main physics settings instance - const PhysicsSettings & mPhysicsSettings; - - /// Listener that is notified whenever a contact point between two bodies is added/updated/removed - ContactListener * mContactListener = nullptr; - - /// Functions that are used to combine friction and restitution of 2 bodies - CombineFunction mCombineFriction = [](const Body &inBody1, const SubShapeID &, const Body &inBody2, const SubShapeID &) { return sqrt(inBody1.GetFriction() * inBody2.GetFriction()); }; - CombineFunction mCombineRestitution = [](const Body &inBody1, const SubShapeID &, const Body &inBody2, const SubShapeID &) { return max(inBody1.GetRestitution(), inBody2.GetRestitution()); }; - - /// The constraints that were added this frame - ContactConstraint * mConstraints = nullptr; - uint32 mMaxConstraints = 0; - atomic mNumConstraints { 0 }; - - /// Context used for this physics update - PhysicsUpdateContext * mUpdateContext; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/DistanceConstraint.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/DistanceConstraint.cpp deleted file mode 100644 index e70bfb24ced..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/DistanceConstraint.cpp +++ /dev/null @@ -1,266 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include -#include -#include -#ifdef JPH_DEBUG_RENDERER - #include -#endif // JPH_DEBUG_RENDERER - -JPH_NAMESPACE_BEGIN - -using namespace literals; - -JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(DistanceConstraintSettings) -{ - JPH_ADD_BASE_CLASS(DistanceConstraintSettings, TwoBodyConstraintSettings) - - JPH_ADD_ENUM_ATTRIBUTE(DistanceConstraintSettings, mSpace) - JPH_ADD_ATTRIBUTE(DistanceConstraintSettings, mPoint1) - JPH_ADD_ATTRIBUTE(DistanceConstraintSettings, mPoint2) - JPH_ADD_ATTRIBUTE(DistanceConstraintSettings, mMinDistance) - JPH_ADD_ATTRIBUTE(DistanceConstraintSettings, mMaxDistance) - JPH_ADD_ENUM_ATTRIBUTE_WITH_ALIAS(DistanceConstraintSettings, mLimitsSpringSettings.mMode, "mSpringMode") - JPH_ADD_ATTRIBUTE_WITH_ALIAS(DistanceConstraintSettings, mLimitsSpringSettings.mFrequency, "mFrequency") // Renaming attributes to stay compatible with old versions of the library - JPH_ADD_ATTRIBUTE_WITH_ALIAS(DistanceConstraintSettings, mLimitsSpringSettings.mDamping, "mDamping") -} - -void DistanceConstraintSettings::SaveBinaryState(StreamOut &inStream) const -{ - ConstraintSettings::SaveBinaryState(inStream); - - inStream.Write(mSpace); - inStream.Write(mPoint1); - inStream.Write(mPoint2); - inStream.Write(mMinDistance); - inStream.Write(mMaxDistance); - mLimitsSpringSettings.SaveBinaryState(inStream); -} - -void DistanceConstraintSettings::RestoreBinaryState(StreamIn &inStream) -{ - ConstraintSettings::RestoreBinaryState(inStream); - - inStream.Read(mSpace); - inStream.Read(mPoint1); - inStream.Read(mPoint2); - inStream.Read(mMinDistance); - inStream.Read(mMaxDistance); - mLimitsSpringSettings.RestoreBinaryState(inStream); -} - -TwoBodyConstraint *DistanceConstraintSettings::Create(Body &inBody1, Body &inBody2) const -{ - return new DistanceConstraint(inBody1, inBody2, *this); -} - -DistanceConstraint::DistanceConstraint(Body &inBody1, Body &inBody2, const DistanceConstraintSettings &inSettings) : - TwoBodyConstraint(inBody1, inBody2, inSettings), - mMinDistance(inSettings.mMinDistance), - mMaxDistance(inSettings.mMaxDistance) -{ - if (inSettings.mSpace == EConstraintSpace::WorldSpace) - { - // If all properties were specified in world space, take them to local space now - mLocalSpacePosition1 = Vec3(inBody1.GetInverseCenterOfMassTransform() * inSettings.mPoint1); - mLocalSpacePosition2 = Vec3(inBody2.GetInverseCenterOfMassTransform() * inSettings.mPoint2); - mWorldSpacePosition1 = inSettings.mPoint1; - mWorldSpacePosition2 = inSettings.mPoint2; - } - else - { - // If properties were specified in local space, we need to calculate world space positions - mLocalSpacePosition1 = Vec3(inSettings.mPoint1); - mLocalSpacePosition2 = Vec3(inSettings.mPoint2); - mWorldSpacePosition1 = inBody1.GetCenterOfMassTransform() * inSettings.mPoint1; - mWorldSpacePosition2 = inBody2.GetCenterOfMassTransform() * inSettings.mPoint2; - } - - // Store distance we want to keep between the world space points - float distance = Vec3(mWorldSpacePosition2 - mWorldSpacePosition1).Length(); - float min_distance, max_distance; - if (mMinDistance < 0.0f && mMaxDistance < 0.0f) - { - min_distance = max_distance = distance; - } - else - { - min_distance = mMinDistance < 0.0f? min(distance, mMaxDistance) : mMinDistance; - max_distance = mMaxDistance < 0.0f? max(distance, mMinDistance) : mMaxDistance; - } - SetDistance(min_distance, max_distance); - - // Most likely gravity is going to tear us apart (this is only used when the distance between the points = 0) - mWorldSpaceNormal = Vec3::sAxisY(); - - // Store spring settings - SetLimitsSpringSettings(inSettings.mLimitsSpringSettings); -} - -void DistanceConstraint::NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM) -{ - if (mBody1->GetID() == inBodyID) - mLocalSpacePosition1 -= inDeltaCOM; - else if (mBody2->GetID() == inBodyID) - mLocalSpacePosition2 -= inDeltaCOM; -} - -void DistanceConstraint::CalculateConstraintProperties(float inDeltaTime) -{ - // Update world space positions (the bodies may have moved) - mWorldSpacePosition1 = mBody1->GetCenterOfMassTransform() * mLocalSpacePosition1; - mWorldSpacePosition2 = mBody2->GetCenterOfMassTransform() * mLocalSpacePosition2; - - // Calculate world space normal - Vec3 delta = Vec3(mWorldSpacePosition2 - mWorldSpacePosition1); - float delta_len = delta.Length(); - if (delta_len > 0.0f) - mWorldSpaceNormal = delta / delta_len; - - // Calculate points relative to body - // r1 + u = (p1 - x1) + (p2 - p1) = p2 - x1 - Vec3 r1_plus_u = Vec3(mWorldSpacePosition2 - mBody1->GetCenterOfMassPosition()); - Vec3 r2 = Vec3(mWorldSpacePosition2 - mBody2->GetCenterOfMassPosition()); - - if (mMinDistance == mMaxDistance) - { - mAxisConstraint.CalculateConstraintPropertiesWithSettings(inDeltaTime, *mBody1, r1_plus_u, *mBody2, r2, mWorldSpaceNormal, 0.0f, delta_len - mMinDistance, mLimitsSpringSettings); - - // Single distance, allow constraint forces in both directions - mMinLambda = -FLT_MAX; - mMaxLambda = FLT_MAX; - } - else if (delta_len <= mMinDistance) - { - mAxisConstraint.CalculateConstraintPropertiesWithSettings(inDeltaTime, *mBody1, r1_plus_u, *mBody2, r2, mWorldSpaceNormal, 0.0f, delta_len - mMinDistance, mLimitsSpringSettings); - - // Allow constraint forces to make distance bigger only - mMinLambda = 0; - mMaxLambda = FLT_MAX; - } - else if (delta_len >= mMaxDistance) - { - mAxisConstraint.CalculateConstraintPropertiesWithSettings(inDeltaTime, *mBody1, r1_plus_u, *mBody2, r2, mWorldSpaceNormal, 0.0f, delta_len - mMaxDistance, mLimitsSpringSettings); - - // Allow constraint forces to make distance smaller only - mMinLambda = -FLT_MAX; - mMaxLambda = 0; - } - else - mAxisConstraint.Deactivate(); -} - -void DistanceConstraint::SetupVelocityConstraint(float inDeltaTime) -{ - CalculateConstraintProperties(inDeltaTime); -} - -void DistanceConstraint::ResetWarmStart() -{ - mAxisConstraint.Deactivate(); -} - -void DistanceConstraint::WarmStartVelocityConstraint(float inWarmStartImpulseRatio) -{ - mAxisConstraint.WarmStart(*mBody1, *mBody2, mWorldSpaceNormal, inWarmStartImpulseRatio); -} - -bool DistanceConstraint::SolveVelocityConstraint(float inDeltaTime) -{ - if (mAxisConstraint.IsActive()) - return mAxisConstraint.SolveVelocityConstraint(*mBody1, *mBody2, mWorldSpaceNormal, mMinLambda, mMaxLambda); - else - return false; -} - -bool DistanceConstraint::SolvePositionConstraint(float inDeltaTime, float inBaumgarte) -{ - if (mLimitsSpringSettings.mFrequency <= 0.0f) // When the spring is active, we don't need to solve the position constraint - { - float distance = Vec3(mWorldSpacePosition2 - mWorldSpacePosition1).Dot(mWorldSpaceNormal); - - // Calculate position error - float position_error = 0.0f; - if (distance < mMinDistance) - position_error = distance - mMinDistance; - else if (distance > mMaxDistance) - position_error = distance - mMaxDistance; - - if (position_error != 0.0f) - { - // Update constraint properties (bodies may have moved) - CalculateConstraintProperties(inDeltaTime); - - return mAxisConstraint.SolvePositionConstraint(*mBody1, *mBody2, mWorldSpaceNormal, position_error, inBaumgarte); - } - } - - return false; -} - -#ifdef JPH_DEBUG_RENDERER -void DistanceConstraint::DrawConstraint(DebugRenderer *inRenderer) const -{ - // Draw constraint - Vec3 delta = Vec3(mWorldSpacePosition2 - mWorldSpacePosition1); - float len = delta.Length(); - if (len < mMinDistance) - { - RVec3 real_end_pos = mWorldSpacePosition1 + (len > 0.0f? delta * mMinDistance / len : Vec3(0, len, 0)); - inRenderer->DrawLine(mWorldSpacePosition1, mWorldSpacePosition2, Color::sGreen); - inRenderer->DrawLine(mWorldSpacePosition2, real_end_pos, Color::sYellow); - } - else if (len > mMaxDistance) - { - RVec3 real_end_pos = mWorldSpacePosition1 + (len > 0.0f? delta * mMaxDistance / len : Vec3(0, len, 0)); - inRenderer->DrawLine(mWorldSpacePosition1, real_end_pos, Color::sGreen); - inRenderer->DrawLine(real_end_pos, mWorldSpacePosition2, Color::sRed); - } - else - inRenderer->DrawLine(mWorldSpacePosition1, mWorldSpacePosition2, Color::sGreen); - - // Draw constraint end points - inRenderer->DrawMarker(mWorldSpacePosition1, Color::sWhite, 0.1f); - inRenderer->DrawMarker(mWorldSpacePosition2, Color::sWhite, 0.1f); - - // Draw current length - inRenderer->DrawText3D(0.5_r * (mWorldSpacePosition1 + mWorldSpacePosition2), StringFormat("%.2f", (double)len)); -} -#endif // JPH_DEBUG_RENDERER - -void DistanceConstraint::SaveState(StateRecorder &inStream) const -{ - TwoBodyConstraint::SaveState(inStream); - - mAxisConstraint.SaveState(inStream); - inStream.Write(mWorldSpaceNormal); // When distance = 0, the normal is used from last frame so we need to store it -} - -void DistanceConstraint::RestoreState(StateRecorder &inStream) -{ - TwoBodyConstraint::RestoreState(inStream); - - mAxisConstraint.RestoreState(inStream); - inStream.Read(mWorldSpaceNormal); -} - -Ref DistanceConstraint::GetConstraintSettings() const -{ - DistanceConstraintSettings *settings = new DistanceConstraintSettings; - ToConstraintSettings(*settings); - settings->mSpace = EConstraintSpace::LocalToBodyCOM; - settings->mPoint1 = RVec3(mLocalSpacePosition1); - settings->mPoint2 = RVec3(mLocalSpacePosition2); - settings->mMinDistance = mMinDistance; - settings->mMaxDistance = mMaxDistance; - settings->mLimitsSpringSettings = mLimitsSpringSettings; - return settings; -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/DistanceConstraint.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/DistanceConstraint.h deleted file mode 100644 index fc9d3526714..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/DistanceConstraint.h +++ /dev/null @@ -1,120 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include - -JPH_NAMESPACE_BEGIN - -/// Distance constraint settings, used to create a distance constraint -class JPH_EXPORT DistanceConstraintSettings final : public TwoBodyConstraintSettings -{ -public: - JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, DistanceConstraintSettings) - - // See: ConstraintSettings::SaveBinaryState - virtual void SaveBinaryState(StreamOut &inStream) const override; - - /// Create an instance of this constraint - virtual TwoBodyConstraint * Create(Body &inBody1, Body &inBody2) const override; - - /// This determines in which space the constraint is setup, all properties below should be in the specified space - EConstraintSpace mSpace = EConstraintSpace::WorldSpace; - - /// Body 1 constraint reference frame (space determined by mSpace). - /// Constraint will keep mPoint1 (a point on body 1) and mPoint2 (a point on body 2) at the same distance. - /// Note that this constraint can be used as a cheap PointConstraint by setting mPoint1 = mPoint2 (but this removes only 1 degree of freedom instead of 3). - RVec3 mPoint1 = RVec3::sZero(); - - /// Body 2 constraint reference frame (space determined by mSpace) - RVec3 mPoint2 = RVec3::sZero(); - - /// Ability to override the distance range at which the two points are kept apart. If the value is negative, it will be replaced by the distance between mPoint1 and mPoint2 (works only if mSpace is world space). - float mMinDistance = -1.0f; - float mMaxDistance = -1.0f; - - /// When enabled, this makes the limits soft. When the constraint exceeds the limits, a spring force will pull it back. - SpringSettings mLimitsSpringSettings; - -protected: - // See: ConstraintSettings::RestoreBinaryState - virtual void RestoreBinaryState(StreamIn &inStream) override; -}; - -/// This constraint is a stiff spring that holds 2 points at a fixed distance from each other -class JPH_EXPORT DistanceConstraint final : public TwoBodyConstraint -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Construct distance constraint - DistanceConstraint(Body &inBody1, Body &inBody2, const DistanceConstraintSettings &inSettings); - - // Generic interface of a constraint - virtual EConstraintSubType GetSubType() const override { return EConstraintSubType::Distance; } - virtual void NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM) override; - virtual void SetupVelocityConstraint(float inDeltaTime) override; - virtual void ResetWarmStart() override; - virtual void WarmStartVelocityConstraint(float inWarmStartImpulseRatio) override; - virtual bool SolveVelocityConstraint(float inDeltaTime) override; - virtual bool SolvePositionConstraint(float inDeltaTime, float inBaumgarte) override; -#ifdef JPH_DEBUG_RENDERER - virtual void DrawConstraint(DebugRenderer *inRenderer) const override; -#endif // JPH_DEBUG_RENDERER - virtual void SaveState(StateRecorder &inStream) const override; - virtual void RestoreState(StateRecorder &inStream) override; - virtual Ref GetConstraintSettings() const override; - - // See: TwoBodyConstraint - virtual Mat44 GetConstraintToBody1Matrix() const override { return Mat44::sTranslation(mLocalSpacePosition1); } - virtual Mat44 GetConstraintToBody2Matrix() const override { return Mat44::sTranslation(mLocalSpacePosition2); } // Note: Incorrect rotation as we don't track the original rotation difference, should not matter though as the constraint is not limiting rotation. - - /// Update the minimum and maximum distance for the constraint - void SetDistance(float inMinDistance, float inMaxDistance) { JPH_ASSERT(inMinDistance <= inMaxDistance); mMinDistance = inMinDistance; mMaxDistance = inMaxDistance; } - float GetMinDistance() const { return mMinDistance; } - float GetMaxDistance() const { return mMaxDistance; } - - /// Update the limits spring settings - const SpringSettings & GetLimitsSpringSettings() const { return mLimitsSpringSettings; } - SpringSettings & GetLimitsSpringSettings() { return mLimitsSpringSettings; } - void SetLimitsSpringSettings(const SpringSettings &inLimitsSpringSettings) { mLimitsSpringSettings = inLimitsSpringSettings; } - - ///@name Get Lagrange multiplier from last physics update (the linear impulse applied to satisfy the constraint) - inline float GetTotalLambdaPosition() const { return mAxisConstraint.GetTotalLambda(); } - -private: - // Internal helper function to calculate the values below - void CalculateConstraintProperties(float inDeltaTime); - - // CONFIGURATION PROPERTIES FOLLOW - - // Local space constraint positions - Vec3 mLocalSpacePosition1; - Vec3 mLocalSpacePosition2; - - // Min/max distance that must be kept between the world space points - float mMinDistance; - float mMaxDistance; - - // Soft constraint limits - SpringSettings mLimitsSpringSettings; - - // RUN TIME PROPERTIES FOLLOW - - // World space positions and normal - RVec3 mWorldSpacePosition1; - RVec3 mWorldSpacePosition2; - Vec3 mWorldSpaceNormal; - - // Depending on if the distance < min or distance > max we can apply forces to prevent further violations - float mMinLambda; - float mMaxLambda; - - // The constraint part - AxisConstraintPart mAxisConstraint; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/FixedConstraint.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/FixedConstraint.cpp deleted file mode 100644 index b0639851672..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/FixedConstraint.cpp +++ /dev/null @@ -1,215 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include -#ifdef JPH_DEBUG_RENDERER - #include -#endif // JPH_DEBUG_RENDERER - -JPH_NAMESPACE_BEGIN - -using namespace literals; - -JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(FixedConstraintSettings) -{ - JPH_ADD_BASE_CLASS(FixedConstraintSettings, TwoBodyConstraintSettings) - - JPH_ADD_ENUM_ATTRIBUTE(FixedConstraintSettings, mSpace) - JPH_ADD_ATTRIBUTE(FixedConstraintSettings, mAutoDetectPoint) - JPH_ADD_ATTRIBUTE(FixedConstraintSettings, mPoint1) - JPH_ADD_ATTRIBUTE(FixedConstraintSettings, mAxisX1) - JPH_ADD_ATTRIBUTE(FixedConstraintSettings, mAxisY1) - JPH_ADD_ATTRIBUTE(FixedConstraintSettings, mPoint2) - JPH_ADD_ATTRIBUTE(FixedConstraintSettings, mAxisX2) - JPH_ADD_ATTRIBUTE(FixedConstraintSettings, mAxisY2) -} - -void FixedConstraintSettings::SaveBinaryState(StreamOut &inStream) const -{ - ConstraintSettings::SaveBinaryState(inStream); - - inStream.Write(mSpace); - inStream.Write(mAutoDetectPoint); - inStream.Write(mPoint1); - inStream.Write(mAxisX1); - inStream.Write(mAxisY1); - inStream.Write(mPoint2); - inStream.Write(mAxisX2); - inStream.Write(mAxisY2); -} - -void FixedConstraintSettings::RestoreBinaryState(StreamIn &inStream) -{ - ConstraintSettings::RestoreBinaryState(inStream); - - inStream.Read(mSpace); - inStream.Read(mAutoDetectPoint); - inStream.Read(mPoint1); - inStream.Read(mAxisX1); - inStream.Read(mAxisY1); - inStream.Read(mPoint2); - inStream.Read(mAxisX2); - inStream.Read(mAxisY2); -} - -TwoBodyConstraint *FixedConstraintSettings::Create(Body &inBody1, Body &inBody2) const -{ - return new FixedConstraint(inBody1, inBody2, *this); -} - -FixedConstraint::FixedConstraint(Body &inBody1, Body &inBody2, const FixedConstraintSettings &inSettings) : - TwoBodyConstraint(inBody1, inBody2, inSettings) -{ - // Store inverse of initial rotation from body 1 to body 2 in body 1 space - mInvInitialOrientation = RotationEulerConstraintPart::sGetInvInitialOrientationXY(inSettings.mAxisX1, inSettings.mAxisY1, inSettings.mAxisX2, inSettings.mAxisY2); - - if (inSettings.mSpace == EConstraintSpace::WorldSpace) - { - if (inSettings.mAutoDetectPoint) - { - // Determine anchor point: If any of the bodies can never be dynamic use the other body as anchor point - RVec3 anchor; - if (!inBody1.CanBeKinematicOrDynamic()) - anchor = inBody2.GetCenterOfMassPosition(); - else if (!inBody2.CanBeKinematicOrDynamic()) - anchor = inBody1.GetCenterOfMassPosition(); - else - { - // Otherwise use weighted anchor point towards the lightest body - Real inv_m1 = Real(inBody1.GetMotionPropertiesUnchecked()->GetInverseMassUnchecked()); - Real inv_m2 = Real(inBody2.GetMotionPropertiesUnchecked()->GetInverseMassUnchecked()); - Real total_inv_mass = inv_m1 + inv_m2; - if (total_inv_mass != 0.0_r) - anchor = (inv_m1 * inBody1.GetCenterOfMassPosition() + inv_m2 * inBody2.GetCenterOfMassPosition()) / (inv_m1 + inv_m2); - else - anchor = inBody1.GetCenterOfMassPosition(); - } - - // Store local positions - mLocalSpacePosition1 = Vec3(inBody1.GetInverseCenterOfMassTransform() * anchor); - mLocalSpacePosition2 = Vec3(inBody2.GetInverseCenterOfMassTransform() * anchor); - } - else - { - // Store local positions - mLocalSpacePosition1 = Vec3(inBody1.GetInverseCenterOfMassTransform() * inSettings.mPoint1); - mLocalSpacePosition2 = Vec3(inBody2.GetInverseCenterOfMassTransform() * inSettings.mPoint2); - } - - // Constraints were specified in world space, so we should have replaced c1 with q10^-1 c1 and c2 with q20^-1 c2 - // => r0^-1 = (q20^-1 c2) (q10^-1 c1)^1 = q20^-1 (c2 c1^-1) q10 - mInvInitialOrientation = inBody2.GetRotation().Conjugated() * mInvInitialOrientation * inBody1.GetRotation(); - } - else - { - // Store local positions - mLocalSpacePosition1 = Vec3(inSettings.mPoint1); - mLocalSpacePosition2 = Vec3(inSettings.mPoint2); - } -} - -void FixedConstraint::NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM) -{ - if (mBody1->GetID() == inBodyID) - mLocalSpacePosition1 -= inDeltaCOM; - else if (mBody2->GetID() == inBodyID) - mLocalSpacePosition2 -= inDeltaCOM; -} - -void FixedConstraint::SetupVelocityConstraint(float inDeltaTime) -{ - // Calculate constraint values that don't change when the bodies don't change position - Mat44 rotation1 = Mat44::sRotation(mBody1->GetRotation()); - Mat44 rotation2 = Mat44::sRotation(mBody2->GetRotation()); - mRotationConstraintPart.CalculateConstraintProperties(*mBody1, rotation1, *mBody2, rotation2); - mPointConstraintPart.CalculateConstraintProperties(*mBody1, rotation1, mLocalSpacePosition1, *mBody2, rotation2, mLocalSpacePosition2); -} - -void FixedConstraint::ResetWarmStart() -{ - mRotationConstraintPart.Deactivate(); - mPointConstraintPart.Deactivate(); -} - -void FixedConstraint::WarmStartVelocityConstraint(float inWarmStartImpulseRatio) -{ - // Warm starting: Apply previous frame impulse - mRotationConstraintPart.WarmStart(*mBody1, *mBody2, inWarmStartImpulseRatio); - mPointConstraintPart.WarmStart(*mBody1, *mBody2, inWarmStartImpulseRatio); -} - -bool FixedConstraint::SolveVelocityConstraint(float inDeltaTime) -{ - // Solve rotation constraint - bool rot = mRotationConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2); - - // Solve position constraint - bool pos = mPointConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2); - - return rot || pos; -} - -bool FixedConstraint::SolvePositionConstraint(float inDeltaTime, float inBaumgarte) -{ - // Solve rotation constraint - mRotationConstraintPart.CalculateConstraintProperties(*mBody1, Mat44::sRotation(mBody1->GetRotation()), *mBody2, Mat44::sRotation(mBody2->GetRotation())); - bool rot = mRotationConstraintPart.SolvePositionConstraint(*mBody1, *mBody2, mInvInitialOrientation, inBaumgarte); - - // Solve position constraint - mPointConstraintPart.CalculateConstraintProperties(*mBody1, Mat44::sRotation(mBody1->GetRotation()), mLocalSpacePosition1, *mBody2, Mat44::sRotation(mBody2->GetRotation()), mLocalSpacePosition2); - bool pos = mPointConstraintPart.SolvePositionConstraint(*mBody1, *mBody2, inBaumgarte); - - return rot || pos; -} - -#ifdef JPH_DEBUG_RENDERER -void FixedConstraint::DrawConstraint(DebugRenderer *inRenderer) const -{ - RMat44 com1 = mBody1->GetCenterOfMassTransform(); - RMat44 com2 = mBody2->GetCenterOfMassTransform(); - - RVec3 anchor1 = com1 * mLocalSpacePosition1; - RVec3 anchor2 = com2 * mLocalSpacePosition2; - - // Draw constraint - inRenderer->DrawLine(com1.GetTranslation(), anchor1, Color::sGreen); - inRenderer->DrawLine(com2.GetTranslation(), anchor2, Color::sBlue); -} -#endif // JPH_DEBUG_RENDERER - -void FixedConstraint::SaveState(StateRecorder &inStream) const -{ - TwoBodyConstraint::SaveState(inStream); - - mRotationConstraintPart.SaveState(inStream); - mPointConstraintPart.SaveState(inStream); -} - -void FixedConstraint::RestoreState(StateRecorder &inStream) -{ - TwoBodyConstraint::RestoreState(inStream); - - mRotationConstraintPart.RestoreState(inStream); - mPointConstraintPart.RestoreState(inStream); -} - -Ref FixedConstraint::GetConstraintSettings() const -{ - FixedConstraintSettings *settings = new FixedConstraintSettings; - ToConstraintSettings(*settings); - settings->mSpace = EConstraintSpace::LocalToBodyCOM; - settings->mPoint1 = RVec3(mLocalSpacePosition1); - settings->mAxisX1 = Vec3::sAxisX(); - settings->mAxisY1 = Vec3::sAxisY(); - settings->mPoint2 = RVec3(mLocalSpacePosition2); - settings->mAxisX2 = mInvInitialOrientation.RotateAxisX(); - settings->mAxisY2 = mInvInitialOrientation.RotateAxisY(); - return settings; -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/FixedConstraint.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/FixedConstraint.h deleted file mode 100644 index 16d54d3c6da..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/FixedConstraint.h +++ /dev/null @@ -1,96 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -/// Fixed constraint settings, used to create a fixed constraint -class JPH_EXPORT FixedConstraintSettings final : public TwoBodyConstraintSettings -{ -public: - JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, FixedConstraintSettings) - - // See: ConstraintSettings::SaveBinaryState - virtual void SaveBinaryState(StreamOut &inStream) const override; - - /// Create an instance of this constraint - virtual TwoBodyConstraint * Create(Body &inBody1, Body &inBody2) const override; - - /// This determines in which space the constraint is setup, all properties below should be in the specified space - EConstraintSpace mSpace = EConstraintSpace::WorldSpace; - - /// When mSpace is WorldSpace mPoint1 and mPoint2 can be automatically calculated based on the positions of the bodies when the constraint is created (they will be fixated in their current relative position/orientation). Set this to false if you want to supply the attachment points yourself. - bool mAutoDetectPoint = false; - - /// Body 1 constraint reference frame (space determined by mSpace) - RVec3 mPoint1 = RVec3::sZero(); - Vec3 mAxisX1 = Vec3::sAxisX(); - Vec3 mAxisY1 = Vec3::sAxisY(); - - /// Body 2 constraint reference frame (space determined by mSpace) - RVec3 mPoint2 = RVec3::sZero(); - Vec3 mAxisX2 = Vec3::sAxisX(); - Vec3 mAxisY2 = Vec3::sAxisY(); - -protected: - // See: ConstraintSettings::RestoreBinaryState - virtual void RestoreBinaryState(StreamIn &inStream) override; -}; - -/// A fixed constraint welds two bodies together removing all degrees of freedom between them. -/// This variant uses Euler angles for the rotation constraint. -class JPH_EXPORT FixedConstraint final : public TwoBodyConstraint -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Constructor - FixedConstraint(Body &inBody1, Body &inBody2, const FixedConstraintSettings &inSettings); - - // Generic interface of a constraint - virtual EConstraintSubType GetSubType() const override { return EConstraintSubType::Fixed; } - virtual void NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM) override; - virtual void SetupVelocityConstraint(float inDeltaTime) override; - virtual void ResetWarmStart() override; - virtual void WarmStartVelocityConstraint(float inWarmStartImpulseRatio) override; - virtual bool SolveVelocityConstraint(float inDeltaTime) override; - virtual bool SolvePositionConstraint(float inDeltaTime, float inBaumgarte) override; -#ifdef JPH_DEBUG_RENDERER - virtual void DrawConstraint(DebugRenderer *inRenderer) const override; -#endif // JPH_DEBUG_RENDERER - virtual void SaveState(StateRecorder &inStream) const override; - virtual void RestoreState(StateRecorder &inStream) override; - virtual Ref GetConstraintSettings() const override; - - // See: TwoBodyConstraint - virtual Mat44 GetConstraintToBody1Matrix() const override { return Mat44::sTranslation(mLocalSpacePosition1); } - virtual Mat44 GetConstraintToBody2Matrix() const override { return Mat44::sRotationTranslation(mInvInitialOrientation, mLocalSpacePosition2); } - - ///@name Get Lagrange multiplier from last physics update (the linear/angular impulse applied to satisfy the constraint) - inline Vec3 GetTotalLambdaPosition() const { return mPointConstraintPart.GetTotalLambda(); } - inline Vec3 GetTotalLambdaRotation() const { return mRotationConstraintPart.GetTotalLambda(); } - -private: - // CONFIGURATION PROPERTIES FOLLOW - - // Local space constraint positions - Vec3 mLocalSpacePosition1; - Vec3 mLocalSpacePosition2; - - // Inverse of initial rotation from body 1 to body 2 in body 1 space - Quat mInvInitialOrientation; - - // RUN TIME PROPERTIES FOLLOW - - // The constraint parts - RotationEulerConstraintPart mRotationConstraintPart; - PointConstraintPart mPointConstraintPart; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/GearConstraint.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/GearConstraint.cpp deleted file mode 100644 index 676bee65c64..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/GearConstraint.cpp +++ /dev/null @@ -1,188 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include -#include -#include -#include -#ifdef JPH_DEBUG_RENDERER - #include -#endif // JPH_DEBUG_RENDERER - -JPH_NAMESPACE_BEGIN - -JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(GearConstraintSettings) -{ - JPH_ADD_BASE_CLASS(GearConstraintSettings, TwoBodyConstraintSettings) - - JPH_ADD_ENUM_ATTRIBUTE(GearConstraintSettings, mSpace) - JPH_ADD_ATTRIBUTE(GearConstraintSettings, mHingeAxis1) - JPH_ADD_ATTRIBUTE(GearConstraintSettings, mHingeAxis2) - JPH_ADD_ATTRIBUTE(GearConstraintSettings, mRatio) -} - -void GearConstraintSettings::SaveBinaryState(StreamOut &inStream) const -{ - ConstraintSettings::SaveBinaryState(inStream); - - inStream.Write(mSpace); - inStream.Write(mHingeAxis1); - inStream.Write(mHingeAxis2); - inStream.Write(mRatio); -} - -void GearConstraintSettings::RestoreBinaryState(StreamIn &inStream) -{ - ConstraintSettings::RestoreBinaryState(inStream); - - inStream.Read(mSpace); - inStream.Read(mHingeAxis1); - inStream.Read(mHingeAxis2); - inStream.Read(mRatio); -} - -TwoBodyConstraint *GearConstraintSettings::Create(Body &inBody1, Body &inBody2) const -{ - return new GearConstraint(inBody1, inBody2, *this); -} - -GearConstraint::GearConstraint(Body &inBody1, Body &inBody2, const GearConstraintSettings &inSettings) : - TwoBodyConstraint(inBody1, inBody2, inSettings), - mLocalSpaceHingeAxis1(inSettings.mHingeAxis1), - mLocalSpaceHingeAxis2(inSettings.mHingeAxis2), - mRatio(inSettings.mRatio) -{ - if (inSettings.mSpace == EConstraintSpace::WorldSpace) - { - // If all properties were specified in world space, take them to local space now - mLocalSpaceHingeAxis1 = inBody1.GetInverseCenterOfMassTransform().Multiply3x3(mLocalSpaceHingeAxis1).Normalized(); - mLocalSpaceHingeAxis2 = inBody2.GetInverseCenterOfMassTransform().Multiply3x3(mLocalSpaceHingeAxis2).Normalized(); - } -} - -void GearConstraint::CalculateConstraintProperties(Mat44Arg inRotation1, Mat44Arg inRotation2) -{ - // Calculate world space normals - mWorldSpaceHingeAxis1 = inRotation1 * mLocalSpaceHingeAxis1; - mWorldSpaceHingeAxis2 = inRotation2 * mLocalSpaceHingeAxis2; - - mGearConstraintPart.CalculateConstraintProperties(*mBody1, mWorldSpaceHingeAxis1, *mBody2, mWorldSpaceHingeAxis2, mRatio); -} - -void GearConstraint::SetupVelocityConstraint(float inDeltaTime) -{ - // Calculate constraint properties that are constant while bodies don't move - Mat44 rotation1 = Mat44::sRotation(mBody1->GetRotation()); - Mat44 rotation2 = Mat44::sRotation(mBody2->GetRotation()); - CalculateConstraintProperties(rotation1, rotation2); -} - -void GearConstraint::ResetWarmStart() -{ - mGearConstraintPart.Deactivate(); -} - -void GearConstraint::WarmStartVelocityConstraint(float inWarmStartImpulseRatio) -{ - // Warm starting: Apply previous frame impulse - mGearConstraintPart.WarmStart(*mBody1, *mBody2, inWarmStartImpulseRatio); -} - -bool GearConstraint::SolveVelocityConstraint(float inDeltaTime) -{ - return mGearConstraintPart.SolveVelocityConstraint(*mBody1, mWorldSpaceHingeAxis1, *mBody2, mWorldSpaceHingeAxis2, mRatio); -} - -bool GearConstraint::SolvePositionConstraint(float inDeltaTime, float inBaumgarte) -{ - if (mGear1Constraint == nullptr || mGear2Constraint == nullptr) - return false; - - float gear1rot; - if (mGear1Constraint->GetSubType() == EConstraintSubType::Hinge) - { - gear1rot = static_cast(mGear1Constraint.GetPtr())->GetCurrentAngle(); - } - else - { - JPH_ASSERT(false, "Unsupported"); - return false; - } - - float gear2rot; - if (mGear2Constraint->GetSubType() == EConstraintSubType::Hinge) - { - gear2rot = static_cast(mGear2Constraint.GetPtr())->GetCurrentAngle(); - } - else - { - JPH_ASSERT(false, "Unsupported"); - return false; - } - - float error = CenterAngleAroundZero(fmod(gear1rot + mRatio * gear2rot, 2.0f * JPH_PI)); - if (error == 0.0f) - return false; - - Mat44 rotation1 = Mat44::sRotation(mBody1->GetRotation()); - Mat44 rotation2 = Mat44::sRotation(mBody2->GetRotation()); - CalculateConstraintProperties(rotation1, rotation2); - return mGearConstraintPart.SolvePositionConstraint(*mBody1, *mBody2, error, inBaumgarte); -} - -#ifdef JPH_DEBUG_RENDERER -void GearConstraint::DrawConstraint(DebugRenderer *inRenderer) const -{ - RMat44 transform1 = mBody1->GetCenterOfMassTransform(); - RMat44 transform2 = mBody2->GetCenterOfMassTransform(); - - // Draw constraint axis - inRenderer->DrawArrow(transform1.GetTranslation(), transform1 * mLocalSpaceHingeAxis1, Color::sGreen, 0.01f); - inRenderer->DrawArrow(transform2.GetTranslation(), transform2 * mLocalSpaceHingeAxis2, Color::sBlue, 0.01f); -} - -#endif // JPH_DEBUG_RENDERER - -void GearConstraint::SaveState(StateRecorder &inStream) const -{ - TwoBodyConstraint::SaveState(inStream); - - mGearConstraintPart.SaveState(inStream); -} - -void GearConstraint::RestoreState(StateRecorder &inStream) -{ - TwoBodyConstraint::RestoreState(inStream); - - mGearConstraintPart.RestoreState(inStream); -} - -Ref GearConstraint::GetConstraintSettings() const -{ - GearConstraintSettings *settings = new GearConstraintSettings; - ToConstraintSettings(*settings); - settings->mSpace = EConstraintSpace::LocalToBodyCOM; - settings->mHingeAxis1 = mLocalSpaceHingeAxis1; - settings->mHingeAxis2 = mLocalSpaceHingeAxis2; - settings->mRatio = mRatio; - return settings; -} - -Mat44 GearConstraint::GetConstraintToBody1Matrix() const -{ - Vec3 perp = mLocalSpaceHingeAxis1.GetNormalizedPerpendicular(); - return Mat44(Vec4(mLocalSpaceHingeAxis1, 0), Vec4(perp, 0), Vec4(mLocalSpaceHingeAxis1.Cross(perp), 0), Vec4(0, 0, 0, 1)); -} - -Mat44 GearConstraint::GetConstraintToBody2Matrix() const -{ - Vec3 perp = mLocalSpaceHingeAxis2.GetNormalizedPerpendicular(); - return Mat44(Vec4(mLocalSpaceHingeAxis2, 0), Vec4(perp, 0), Vec4(mLocalSpaceHingeAxis2.Cross(perp), 0), Vec4(0, 0, 0, 1)); -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/GearConstraint.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/GearConstraint.h deleted file mode 100644 index 8b6233b1aaf..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/GearConstraint.h +++ /dev/null @@ -1,116 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include - -JPH_NAMESPACE_BEGIN - -/// Gear constraint settings -class JPH_EXPORT GearConstraintSettings final : public TwoBodyConstraintSettings -{ -public: - JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, GearConstraintSettings) - - // See: ConstraintSettings::SaveBinaryState - virtual void SaveBinaryState(StreamOut &inStream) const override; - - /// Create an instance of this constraint. - virtual TwoBodyConstraint * Create(Body &inBody1, Body &inBody2) const override; - - /// Defines the ratio between the rotation of both gears - /// The ratio is defined as: Gear1Rotation(t) = -ratio * Gear2Rotation(t) - /// @param inNumTeethGear1 Number of teeth that body 1 has - /// @param inNumTeethGear2 Number of teeth that body 2 has - void SetRatio(int inNumTeethGear1, int inNumTeethGear2) - { - mRatio = float(inNumTeethGear2) / float(inNumTeethGear1); - } - - /// This determines in which space the constraint is setup, all properties below should be in the specified space - EConstraintSpace mSpace = EConstraintSpace::WorldSpace; - - /// Body 1 constraint reference frame (space determined by mSpace). - Vec3 mHingeAxis1 = Vec3::sAxisX(); - - /// Body 2 constraint reference frame (space determined by mSpace) - Vec3 mHingeAxis2 = Vec3::sAxisX(); - - /// Ratio between both gears, see SetRatio. - float mRatio = 1.0f; - -protected: - // See: ConstraintSettings::RestoreBinaryState - virtual void RestoreBinaryState(StreamIn &inStream) override; -}; - -/// A gear constraint constrains the rotation of body1 to the rotation of body 2 using a gear. -/// Note that this constraint needs to be used in conjunction with a two hinge constraints. -class JPH_EXPORT GearConstraint final : public TwoBodyConstraint -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Construct gear constraint - GearConstraint(Body &inBody1, Body &inBody2, const GearConstraintSettings &inSettings); - - // Generic interface of a constraint - virtual EConstraintSubType GetSubType() const override { return EConstraintSubType::Gear; } - virtual void NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM) override { /* Do nothing */ } - virtual void SetupVelocityConstraint(float inDeltaTime) override; - virtual void ResetWarmStart() override; - virtual void WarmStartVelocityConstraint(float inWarmStartImpulseRatio) override; - virtual bool SolveVelocityConstraint(float inDeltaTime) override; - virtual bool SolvePositionConstraint(float inDeltaTime, float inBaumgarte) override; -#ifdef JPH_DEBUG_RENDERER - virtual void DrawConstraint(DebugRenderer *inRenderer) const override; -#endif // JPH_DEBUG_RENDERER - virtual void SaveState(StateRecorder &inStream) const override; - virtual void RestoreState(StateRecorder &inStream) override; - virtual Ref GetConstraintSettings() const override; - - // See: TwoBodyConstraint - virtual Mat44 GetConstraintToBody1Matrix() const override; - virtual Mat44 GetConstraintToBody2Matrix() const override; - - /// The constraints that constrain both gears (2 hinges), optional and used to calculate the rotation error and fix numerical drift. - void SetConstraints(const Constraint *inGear1, const Constraint *inGear2) { mGear1Constraint = inGear1; mGear2Constraint = inGear2; } - - ///@name Get Lagrange multiplier from last physics update (the angular impulse applied to satisfy the constraint) - inline float GetTotalLambda() const { return mGearConstraintPart.GetTotalLambda(); } - -private: - // Internal helper function to calculate the values below - void CalculateConstraintProperties(Mat44Arg inRotation1, Mat44Arg inRotation2); - - // CONFIGURATION PROPERTIES FOLLOW - - // Local space hinge axis for body 1 - Vec3 mLocalSpaceHingeAxis1; - - // Local space hinge axis for body 2 - Vec3 mLocalSpaceHingeAxis2; - - // Ratio between gear 1 and 2 - float mRatio; - - // The constraints that constrain both gears (2 hinges), optional and used to calculate the rotation error and fix numerical drift. - RefConst mGear1Constraint; - RefConst mGear2Constraint; - - // RUN TIME PROPERTIES FOLLOW - - // World space hinge axis for body 1 - Vec3 mWorldSpaceHingeAxis1; - - // World space hinge axis for body 2 - Vec3 mWorldSpaceHingeAxis2; - - // The constraint parts - GearConstraintPart mGearConstraintPart; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/HingeConstraint.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/HingeConstraint.cpp deleted file mode 100644 index 82465736295..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/HingeConstraint.cpp +++ /dev/null @@ -1,424 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include -#include -#include -#include -#ifdef JPH_DEBUG_RENDERER - #include -#endif // JPH_DEBUG_RENDERER - -JPH_NAMESPACE_BEGIN - -JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(HingeConstraintSettings) -{ - JPH_ADD_BASE_CLASS(HingeConstraintSettings, TwoBodyConstraintSettings) - - JPH_ADD_ENUM_ATTRIBUTE(HingeConstraintSettings, mSpace) - JPH_ADD_ATTRIBUTE(HingeConstraintSettings, mPoint1) - JPH_ADD_ATTRIBUTE(HingeConstraintSettings, mHingeAxis1) - JPH_ADD_ATTRIBUTE(HingeConstraintSettings, mNormalAxis1) - JPH_ADD_ATTRIBUTE(HingeConstraintSettings, mPoint2) - JPH_ADD_ATTRIBUTE(HingeConstraintSettings, mHingeAxis2) - JPH_ADD_ATTRIBUTE(HingeConstraintSettings, mNormalAxis2) - JPH_ADD_ATTRIBUTE(HingeConstraintSettings, mLimitsMin) - JPH_ADD_ATTRIBUTE(HingeConstraintSettings, mLimitsMax) - JPH_ADD_ATTRIBUTE(HingeConstraintSettings, mLimitsSpringSettings) - JPH_ADD_ATTRIBUTE(HingeConstraintSettings, mMaxFrictionTorque) - JPH_ADD_ATTRIBUTE(HingeConstraintSettings, mMotorSettings) -} - -void HingeConstraintSettings::SaveBinaryState(StreamOut &inStream) const -{ - ConstraintSettings::SaveBinaryState(inStream); - - inStream.Write(mSpace); - inStream.Write(mPoint1); - inStream.Write(mHingeAxis1); - inStream.Write(mNormalAxis1); - inStream.Write(mPoint2); - inStream.Write(mHingeAxis2); - inStream.Write(mNormalAxis2); - inStream.Write(mLimitsMin); - inStream.Write(mLimitsMax); - inStream.Write(mMaxFrictionTorque); - mLimitsSpringSettings.SaveBinaryState(inStream); - mMotorSettings.SaveBinaryState(inStream); -} - -void HingeConstraintSettings::RestoreBinaryState(StreamIn &inStream) -{ - ConstraintSettings::RestoreBinaryState(inStream); - - inStream.Read(mSpace); - inStream.Read(mPoint1); - inStream.Read(mHingeAxis1); - inStream.Read(mNormalAxis1); - inStream.Read(mPoint2); - inStream.Read(mHingeAxis2); - inStream.Read(mNormalAxis2); - inStream.Read(mLimitsMin); - inStream.Read(mLimitsMax); - inStream.Read(mMaxFrictionTorque); - mLimitsSpringSettings.RestoreBinaryState(inStream); - mMotorSettings.RestoreBinaryState(inStream);} - -TwoBodyConstraint *HingeConstraintSettings::Create(Body &inBody1, Body &inBody2) const -{ - return new HingeConstraint(inBody1, inBody2, *this); -} - -HingeConstraint::HingeConstraint(Body &inBody1, Body &inBody2, const HingeConstraintSettings &inSettings) : - TwoBodyConstraint(inBody1, inBody2, inSettings), - mMaxFrictionTorque(inSettings.mMaxFrictionTorque), - mMotorSettings(inSettings.mMotorSettings) -{ - // Store limits - JPH_ASSERT(inSettings.mLimitsMin != inSettings.mLimitsMax || inSettings.mLimitsSpringSettings.mFrequency > 0.0f, "Better use a fixed constraint in this case"); - SetLimits(inSettings.mLimitsMin, inSettings.mLimitsMax); - - // Store inverse of initial rotation from body 1 to body 2 in body 1 space - mInvInitialOrientation = RotationEulerConstraintPart::sGetInvInitialOrientationXZ(inSettings.mNormalAxis1, inSettings.mHingeAxis1, inSettings.mNormalAxis2, inSettings.mHingeAxis2); - - if (inSettings.mSpace == EConstraintSpace::WorldSpace) - { - // If all properties were specified in world space, take them to local space now - RMat44 inv_transform1 = inBody1.GetInverseCenterOfMassTransform(); - mLocalSpacePosition1 = Vec3(inv_transform1 * inSettings.mPoint1); - mLocalSpaceHingeAxis1 = inv_transform1.Multiply3x3(inSettings.mHingeAxis1).Normalized(); - mLocalSpaceNormalAxis1 = inv_transform1.Multiply3x3(inSettings.mNormalAxis1).Normalized(); - - RMat44 inv_transform2 = inBody2.GetInverseCenterOfMassTransform(); - mLocalSpacePosition2 = Vec3(inv_transform2 * inSettings.mPoint2); - mLocalSpaceHingeAxis2 = inv_transform2.Multiply3x3(inSettings.mHingeAxis2).Normalized(); - mLocalSpaceNormalAxis2 = inv_transform2.Multiply3x3(inSettings.mNormalAxis2).Normalized(); - - // Constraints were specified in world space, so we should have replaced c1 with q10^-1 c1 and c2 with q20^-1 c2 - // => r0^-1 = (q20^-1 c2) (q10^-1 c1)^1 = q20^-1 (c2 c1^-1) q10 - mInvInitialOrientation = inBody2.GetRotation().Conjugated() * mInvInitialOrientation * inBody1.GetRotation(); - } - else - { - mLocalSpacePosition1 = Vec3(inSettings.mPoint1); - mLocalSpaceHingeAxis1 = inSettings.mHingeAxis1; - mLocalSpaceNormalAxis1 = inSettings.mNormalAxis1; - - mLocalSpacePosition2 = Vec3(inSettings.mPoint2); - mLocalSpaceHingeAxis2 = inSettings.mHingeAxis2; - mLocalSpaceNormalAxis2 = inSettings.mNormalAxis2; - } - - // Store spring settings - SetLimitsSpringSettings(inSettings.mLimitsSpringSettings); -} - -void HingeConstraint::NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM) -{ - if (mBody1->GetID() == inBodyID) - mLocalSpacePosition1 -= inDeltaCOM; - else if (mBody2->GetID() == inBodyID) - mLocalSpacePosition2 -= inDeltaCOM; -} - -float HingeConstraint::GetCurrentAngle() const -{ - // See: CalculateA1AndTheta - Quat rotation1 = mBody1->GetRotation(); - Quat diff = mBody2->GetRotation() * mInvInitialOrientation * rotation1.Conjugated(); - return diff.GetRotationAngle(rotation1 * mLocalSpaceHingeAxis1); -} - -void HingeConstraint::SetLimits(float inLimitsMin, float inLimitsMax) -{ - JPH_ASSERT(inLimitsMin <= 0.0f && inLimitsMin >= -JPH_PI); - JPH_ASSERT(inLimitsMax >= 0.0f && inLimitsMax <= JPH_PI); - mLimitsMin = inLimitsMin; - mLimitsMax = inLimitsMax; - mHasLimits = mLimitsMin > -JPH_PI && mLimitsMax < JPH_PI; -} - -void HingeConstraint::CalculateA1AndTheta() -{ - if (mHasLimits || mMotorState != EMotorState::Off || mMaxFrictionTorque > 0.0f) - { - Quat rotation1 = mBody1->GetRotation(); - - // Calculate relative rotation in world space - // - // The rest rotation is: - // - // q2 = q1 r0 - // - // But the actual rotation is - // - // q2 = diff q1 r0 - // <=> diff = q2 r0^-1 q1^-1 - // - // Where: - // q1 = current rotation of body 1 - // q2 = current rotation of body 2 - // diff = relative rotation in world space - Quat diff = mBody2->GetRotation() * mInvInitialOrientation * rotation1.Conjugated(); - - // Calculate hinge axis in world space - mA1 = rotation1 * mLocalSpaceHingeAxis1; - - // Get rotation angle around the hinge axis - mTheta = diff.GetRotationAngle(mA1); - } -} - -void HingeConstraint::CalculateRotationLimitsConstraintProperties(float inDeltaTime) -{ - // Apply constraint if outside of limits - if (mHasLimits && (mTheta <= mLimitsMin || mTheta >= mLimitsMax)) - mRotationLimitsConstraintPart.CalculateConstraintPropertiesWithSettings(inDeltaTime, *mBody1, *mBody2, mA1, 0.0f, GetSmallestAngleToLimit(), mLimitsSpringSettings); - else - mRotationLimitsConstraintPart.Deactivate(); -} - -void HingeConstraint::CalculateMotorConstraintProperties(float inDeltaTime) -{ - switch (mMotorState) - { - case EMotorState::Off: - if (mMaxFrictionTorque > 0.0f) - mMotorConstraintPart.CalculateConstraintProperties(*mBody1, *mBody2, mA1); - else - mMotorConstraintPart.Deactivate(); - break; - - case EMotorState::Velocity: - mMotorConstraintPart.CalculateConstraintProperties(*mBody1, *mBody2, mA1, -mTargetAngularVelocity); - break; - - case EMotorState::Position: - if (mMotorSettings.mSpringSettings.HasStiffness()) - mMotorConstraintPart.CalculateConstraintPropertiesWithSettings(inDeltaTime, *mBody1, *mBody2, mA1, 0.0f, CenterAngleAroundZero(mTheta - mTargetAngle), mMotorSettings.mSpringSettings); - else - mMotorConstraintPart.Deactivate(); - break; - } -} - -void HingeConstraint::SetupVelocityConstraint(float inDeltaTime) -{ - // Cache constraint values that are valid until the bodies move - Mat44 rotation1 = Mat44::sRotation(mBody1->GetRotation()); - Mat44 rotation2 = Mat44::sRotation(mBody2->GetRotation()); - mPointConstraintPart.CalculateConstraintProperties(*mBody1, rotation1, mLocalSpacePosition1, *mBody2, rotation2, mLocalSpacePosition2); - mRotationConstraintPart.CalculateConstraintProperties(*mBody1, rotation1, rotation1.Multiply3x3(mLocalSpaceHingeAxis1), *mBody2, rotation2, rotation2.Multiply3x3(mLocalSpaceHingeAxis2)); - CalculateA1AndTheta(); - CalculateRotationLimitsConstraintProperties(inDeltaTime); - CalculateMotorConstraintProperties(inDeltaTime); -} - -void HingeConstraint::ResetWarmStart() -{ - mMotorConstraintPart.Deactivate(); - mPointConstraintPart.Deactivate(); - mRotationConstraintPart.Deactivate(); - mRotationLimitsConstraintPart.Deactivate(); -} - -void HingeConstraint::WarmStartVelocityConstraint(float inWarmStartImpulseRatio) -{ - // Warm starting: Apply previous frame impulse - mMotorConstraintPart.WarmStart(*mBody1, *mBody2, inWarmStartImpulseRatio); - mPointConstraintPart.WarmStart(*mBody1, *mBody2, inWarmStartImpulseRatio); - mRotationConstraintPart.WarmStart(*mBody1, *mBody2, inWarmStartImpulseRatio); - mRotationLimitsConstraintPart.WarmStart(*mBody1, *mBody2, inWarmStartImpulseRatio); -} - -float HingeConstraint::GetSmallestAngleToLimit() const -{ - float dist_to_min = CenterAngleAroundZero(mTheta - mLimitsMin); - float dist_to_max = CenterAngleAroundZero(mTheta - mLimitsMax); - return abs(dist_to_min) < abs(dist_to_max)? dist_to_min : dist_to_max; -} - -bool HingeConstraint::IsMinLimitClosest() const -{ - float dist_to_min = CenterAngleAroundZero(mTheta - mLimitsMin); - float dist_to_max = CenterAngleAroundZero(mTheta - mLimitsMax); - return abs(dist_to_min) < abs(dist_to_max); -} - -bool HingeConstraint::SolveVelocityConstraint(float inDeltaTime) -{ - // Solve motor - bool motor = false; - if (mMotorConstraintPart.IsActive()) - { - switch (mMotorState) - { - case EMotorState::Off: - { - float max_lambda = mMaxFrictionTorque * inDeltaTime; - motor = mMotorConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2, mA1, -max_lambda, max_lambda); - break; - } - - case EMotorState::Velocity: - case EMotorState::Position: - motor = mMotorConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2, mA1, inDeltaTime * mMotorSettings.mMinTorqueLimit, inDeltaTime * mMotorSettings.mMaxTorqueLimit); - break; - } - } - - // Solve point constraint - bool pos = mPointConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2); - - // Solve rotation constraint - bool rot = mRotationConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2); - - // Solve rotation limits - bool limit = false; - if (mRotationLimitsConstraintPart.IsActive()) - { - float min_lambda, max_lambda; - if (mLimitsMin == mLimitsMax) - { - min_lambda = -FLT_MAX; - max_lambda = FLT_MAX; - } - else if (IsMinLimitClosest()) - { - min_lambda = 0.0f; - max_lambda = FLT_MAX; - } - else - { - min_lambda = -FLT_MAX; - max_lambda = 0.0f; - } - limit = mRotationLimitsConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2, mA1, min_lambda, max_lambda); - } - - return motor || pos || rot || limit; -} - -bool HingeConstraint::SolvePositionConstraint(float inDeltaTime, float inBaumgarte) -{ - // Motor operates on velocities only, don't call SolvePositionConstraint - - // Solve point constraint - mPointConstraintPart.CalculateConstraintProperties(*mBody1, Mat44::sRotation(mBody1->GetRotation()), mLocalSpacePosition1, *mBody2, Mat44::sRotation(mBody2->GetRotation()), mLocalSpacePosition2); - bool pos = mPointConstraintPart.SolvePositionConstraint(*mBody1, *mBody2, inBaumgarte); - - // Solve rotation constraint - Mat44 rotation1 = Mat44::sRotation(mBody1->GetRotation()); // Note that previous call to GetRotation() is out of date since the rotation has changed - Mat44 rotation2 = Mat44::sRotation(mBody2->GetRotation()); - mRotationConstraintPart.CalculateConstraintProperties(*mBody1, rotation1, rotation1.Multiply3x3(mLocalSpaceHingeAxis1), *mBody2, rotation2, rotation2.Multiply3x3(mLocalSpaceHingeAxis2)); - bool rot = mRotationConstraintPart.SolvePositionConstraint(*mBody1, *mBody2, inBaumgarte); - - // Solve rotation limits - bool limit = false; - if (mHasLimits && mLimitsSpringSettings.mFrequency <= 0.0f) - { - CalculateA1AndTheta(); - CalculateRotationLimitsConstraintProperties(inDeltaTime); - if (mRotationLimitsConstraintPart.IsActive()) - limit = mRotationLimitsConstraintPart.SolvePositionConstraint(*mBody1, *mBody2, GetSmallestAngleToLimit(), inBaumgarte); - } - - return pos || rot || limit; -} - -#ifdef JPH_DEBUG_RENDERER -void HingeConstraint::DrawConstraint(DebugRenderer *inRenderer) const -{ - RMat44 transform1 = mBody1->GetCenterOfMassTransform(); - RMat44 transform2 = mBody2->GetCenterOfMassTransform(); - - // Draw constraint - RVec3 constraint_pos1 = transform1 * mLocalSpacePosition1; - inRenderer->DrawMarker(constraint_pos1, Color::sRed, 0.1f); - inRenderer->DrawLine(constraint_pos1, transform1 * (mLocalSpacePosition1 + mDrawConstraintSize * mLocalSpaceHingeAxis1), Color::sRed); - - RVec3 constraint_pos2 = transform2 * mLocalSpacePosition2; - inRenderer->DrawMarker(constraint_pos2, Color::sGreen, 0.1f); - inRenderer->DrawLine(constraint_pos2, transform2 * (mLocalSpacePosition2 + mDrawConstraintSize * mLocalSpaceHingeAxis2), Color::sGreen); - inRenderer->DrawLine(constraint_pos2, transform2 * (mLocalSpacePosition2 + mDrawConstraintSize * mLocalSpaceNormalAxis2), Color::sWhite); -} - -void HingeConstraint::DrawConstraintLimits(DebugRenderer *inRenderer) const -{ - if (mHasLimits && mLimitsMax > mLimitsMin) - { - // Get constraint properties in world space - RMat44 transform1 = mBody1->GetCenterOfMassTransform(); - RVec3 position1 = transform1 * mLocalSpacePosition1; - Vec3 hinge_axis1 = transform1.Multiply3x3(mLocalSpaceHingeAxis1); - Vec3 normal_axis1 = transform1.Multiply3x3(mLocalSpaceNormalAxis1); - - inRenderer->DrawPie(position1, mDrawConstraintSize, hinge_axis1, normal_axis1, mLimitsMin, mLimitsMax, Color::sPurple, DebugRenderer::ECastShadow::Off); - } -} -#endif // JPH_DEBUG_RENDERER - -void HingeConstraint::SaveState(StateRecorder &inStream) const -{ - TwoBodyConstraint::SaveState(inStream); - - mMotorConstraintPart.SaveState(inStream); - mRotationConstraintPart.SaveState(inStream); - mPointConstraintPart.SaveState(inStream); - mRotationLimitsConstraintPart.SaveState(inStream); - - inStream.Write(mMotorState); - inStream.Write(mTargetAngularVelocity); - inStream.Write(mTargetAngle); -} - -void HingeConstraint::RestoreState(StateRecorder &inStream) -{ - TwoBodyConstraint::RestoreState(inStream); - - mMotorConstraintPart.RestoreState(inStream); - mRotationConstraintPart.RestoreState(inStream); - mPointConstraintPart.RestoreState(inStream); - mRotationLimitsConstraintPart.RestoreState(inStream); - - inStream.Read(mMotorState); - inStream.Read(mTargetAngularVelocity); - inStream.Read(mTargetAngle); -} - - -Ref HingeConstraint::GetConstraintSettings() const -{ - HingeConstraintSettings *settings = new HingeConstraintSettings; - ToConstraintSettings(*settings); - settings->mSpace = EConstraintSpace::LocalToBodyCOM; - settings->mPoint1 = RVec3(mLocalSpacePosition1); - settings->mHingeAxis1 = mLocalSpaceHingeAxis1; - settings->mNormalAxis1 = mLocalSpaceNormalAxis1; - settings->mPoint2 = RVec3(mLocalSpacePosition2); - settings->mHingeAxis2 = mLocalSpaceHingeAxis2; - settings->mNormalAxis2 = mLocalSpaceNormalAxis2; - settings->mLimitsMin = mLimitsMin; - settings->mLimitsMax = mLimitsMax; - settings->mLimitsSpringSettings = mLimitsSpringSettings; - settings->mMaxFrictionTorque = mMaxFrictionTorque; - settings->mMotorSettings = mMotorSettings; - return settings; -} - -Mat44 HingeConstraint::GetConstraintToBody1Matrix() const -{ - return Mat44(Vec4(mLocalSpaceHingeAxis1, 0), Vec4(mLocalSpaceNormalAxis1, 0), Vec4(mLocalSpaceHingeAxis1.Cross(mLocalSpaceNormalAxis1), 0), Vec4(mLocalSpacePosition1, 1)); -} - -Mat44 HingeConstraint::GetConstraintToBody2Matrix() const -{ - return Mat44(Vec4(mLocalSpaceHingeAxis2, 0), Vec4(mLocalSpaceNormalAxis2, 0), Vec4(mLocalSpaceHingeAxis2.Cross(mLocalSpaceNormalAxis2), 0), Vec4(mLocalSpacePosition2, 1)); -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/HingeConstraint.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/HingeConstraint.h deleted file mode 100644 index edf48d0e02f..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/HingeConstraint.h +++ /dev/null @@ -1,182 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -/// Hinge constraint settings, used to create a hinge constraint -class JPH_EXPORT HingeConstraintSettings final : public TwoBodyConstraintSettings -{ -public: - JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, HingeConstraintSettings) - - // See: ConstraintSettings::SaveBinaryState - virtual void SaveBinaryState(StreamOut &inStream) const override; - - /// Create an instance of this constraint - virtual TwoBodyConstraint * Create(Body &inBody1, Body &inBody2) const override; - - /// This determines in which space the constraint is setup, all properties below should be in the specified space - EConstraintSpace mSpace = EConstraintSpace::WorldSpace; - - /// Body 1 constraint reference frame (space determined by mSpace). - /// Hinge axis is the axis where rotation is allowed, normal axis defines the 0 angle of the hinge. - RVec3 mPoint1 = RVec3::sZero(); - Vec3 mHingeAxis1 = Vec3::sAxisY(); - Vec3 mNormalAxis1 = Vec3::sAxisX(); - - /// Body 2 constraint reference frame (space determined by mSpace) - RVec3 mPoint2 = RVec3::sZero(); - Vec3 mHingeAxis2 = Vec3::sAxisY(); - Vec3 mNormalAxis2 = Vec3::sAxisX(); - - /// Bodies are assumed to be placed so that the hinge angle = 0, movement will be limited between [mLimitsMin, mLimitsMax] where mLimitsMin e [-pi, 0] and mLimitsMax e [0, pi]. - /// Both angles are in radians. - float mLimitsMin = -JPH_PI; - float mLimitsMax = JPH_PI; - - /// When enabled, this makes the limits soft. When the constraint exceeds the limits, a spring force will pull it back. - SpringSettings mLimitsSpringSettings; - - /// Maximum amount of torque (N m) to apply as friction when the constraint is not powered by a motor - float mMaxFrictionTorque = 0.0f; - - /// In case the constraint is powered, this determines the motor settings around the hinge axis - MotorSettings mMotorSettings; - -protected: - // See: ConstraintSettings::RestoreBinaryState - virtual void RestoreBinaryState(StreamIn &inStream) override; -}; - -/// A hinge constraint constrains 2 bodies on a single point and allows only a single axis of rotation -class JPH_EXPORT HingeConstraint final : public TwoBodyConstraint -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Construct hinge constraint - HingeConstraint(Body &inBody1, Body &inBody2, const HingeConstraintSettings &inSettings); - - // Generic interface of a constraint - virtual EConstraintSubType GetSubType() const override { return EConstraintSubType::Hinge; } - virtual void NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM) override; - virtual void SetupVelocityConstraint(float inDeltaTime) override; - virtual void ResetWarmStart() override; - virtual void WarmStartVelocityConstraint(float inWarmStartImpulseRatio) override; - virtual bool SolveVelocityConstraint(float inDeltaTime) override; - virtual bool SolvePositionConstraint(float inDeltaTime, float inBaumgarte) override; -#ifdef JPH_DEBUG_RENDERER - virtual void DrawConstraint(DebugRenderer *inRenderer) const override; - virtual void DrawConstraintLimits(DebugRenderer *inRenderer) const override; -#endif // JPH_DEBUG_RENDERER - virtual void SaveState(StateRecorder &inStream) const override; - virtual void RestoreState(StateRecorder &inStream) override; - virtual Ref GetConstraintSettings() const override; - - // See: TwoBodyConstraint - virtual Mat44 GetConstraintToBody1Matrix() const override; - virtual Mat44 GetConstraintToBody2Matrix() const override; - - /// Get the current rotation angle from the rest position - float GetCurrentAngle() const; - - // Friction control - void SetMaxFrictionTorque(float inFrictionTorque) { mMaxFrictionTorque = inFrictionTorque; } - float GetMaxFrictionTorque() const { return mMaxFrictionTorque; } - - // Motor settings - MotorSettings & GetMotorSettings() { return mMotorSettings; } - const MotorSettings & GetMotorSettings() const { return mMotorSettings; } - - // Motor controls - void SetMotorState(EMotorState inState) { JPH_ASSERT(inState == EMotorState::Off || mMotorSettings.IsValid()); mMotorState = inState; } - EMotorState GetMotorState() const { return mMotorState; } - void SetTargetAngularVelocity(float inAngularVelocity) { mTargetAngularVelocity = inAngularVelocity; } ///< rad/s - float GetTargetAngularVelocity() const { return mTargetAngularVelocity; } - void SetTargetAngle(float inAngle) { mTargetAngle = mHasLimits? Clamp(inAngle, mLimitsMin, mLimitsMax) : inAngle; } ///< rad - float GetTargetAngle() const { return mTargetAngle; } - - /// Update the rotation limits of the hinge, value in radians (see HingeConstraintSettings) - void SetLimits(float inLimitsMin, float inLimitsMax); - float GetLimitsMin() const { return mLimitsMin; } - float GetLimitsMax() const { return mLimitsMax; } - bool HasLimits() const { return mHasLimits; } - - /// Update the limits spring settings - const SpringSettings & GetLimitsSpringSettings() const { return mLimitsSpringSettings; } - SpringSettings & GetLimitsSpringSettings() { return mLimitsSpringSettings; } - void SetLimitsSpringSettings(const SpringSettings &inLimitsSpringSettings) { mLimitsSpringSettings = inLimitsSpringSettings; } - - ///@name Get Lagrange multiplier from last physics update (the linear/angular impulse applied to satisfy the constraint) - inline Vec3 GetTotalLambdaPosition() const { return mPointConstraintPart.GetTotalLambda(); } - inline Vector<2> GetTotalLambdaRotation() const { return mRotationConstraintPart.GetTotalLambda(); } - inline float GetTotalLambdaRotationLimits() const { return mRotationLimitsConstraintPart.GetTotalLambda(); } - inline float GetTotalLambdaMotor() const { return mMotorConstraintPart.GetTotalLambda(); } - -private: - // Internal helper function to calculate the values below - void CalculateA1AndTheta(); - void CalculateRotationLimitsConstraintProperties(float inDeltaTime); - void CalculateMotorConstraintProperties(float inDeltaTime); - inline float GetSmallestAngleToLimit() const; - inline bool IsMinLimitClosest() const; - - // CONFIGURATION PROPERTIES FOLLOW - - // Local space constraint positions - Vec3 mLocalSpacePosition1; - Vec3 mLocalSpacePosition2; - - // Local space hinge directions - Vec3 mLocalSpaceHingeAxis1; - Vec3 mLocalSpaceHingeAxis2; - - // Local space normal direction (direction relative to which to draw constraint limits) - Vec3 mLocalSpaceNormalAxis1; - Vec3 mLocalSpaceNormalAxis2; - - // Inverse of initial relative orientation between bodies (which defines hinge angle = 0) - Quat mInvInitialOrientation; - - // Hinge limits - bool mHasLimits; - float mLimitsMin; - float mLimitsMax; - - // Soft constraint limits - SpringSettings mLimitsSpringSettings; - - // Friction - float mMaxFrictionTorque; - - // Motor controls - MotorSettings mMotorSettings; - EMotorState mMotorState = EMotorState::Off; - float mTargetAngularVelocity = 0.0f; - float mTargetAngle = 0.0f; - - // RUN TIME PROPERTIES FOLLOW - - // Current rotation around the hinge axis - float mTheta = 0.0f; - - // World space hinge axis for body 1 - Vec3 mA1; - - // The constraint parts - PointConstraintPart mPointConstraintPart; - HingeRotationConstraintPart mRotationConstraintPart; - AngleConstraintPart mRotationLimitsConstraintPart; - AngleConstraintPart mMotorConstraintPart; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/MotorSettings.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/MotorSettings.cpp deleted file mode 100644 index e4daecdd7d5..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/MotorSettings.cpp +++ /dev/null @@ -1,43 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(MotorSettings) -{ - JPH_ADD_ENUM_ATTRIBUTE_WITH_ALIAS(MotorSettings, mSpringSettings.mMode, "mSpringMode") - JPH_ADD_ATTRIBUTE_WITH_ALIAS(MotorSettings, mSpringSettings.mFrequency, "mFrequency") // Renaming attributes to stay compatible with old versions of the library - JPH_ADD_ATTRIBUTE_WITH_ALIAS(MotorSettings, mSpringSettings.mDamping, "mDamping") - JPH_ADD_ATTRIBUTE(MotorSettings, mMinForceLimit) - JPH_ADD_ATTRIBUTE(MotorSettings, mMaxForceLimit) - JPH_ADD_ATTRIBUTE(MotorSettings, mMinTorqueLimit) - JPH_ADD_ATTRIBUTE(MotorSettings, mMaxTorqueLimit) -} - -void MotorSettings::SaveBinaryState(StreamOut &inStream) const -{ - mSpringSettings.SaveBinaryState(inStream); - inStream.Write(mMinForceLimit); - inStream.Write(mMaxForceLimit); - inStream.Write(mMinTorqueLimit); - inStream.Write(mMaxTorqueLimit); -} - -void MotorSettings::RestoreBinaryState(StreamIn &inStream) -{ - mSpringSettings.RestoreBinaryState(inStream); - inStream.Read(mMinForceLimit); - inStream.Read(mMaxForceLimit); - inStream.Read(mMinTorqueLimit); - inStream.Read(mMaxTorqueLimit); -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/MotorSettings.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/MotorSettings.h deleted file mode 100644 index 99484b7d005..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/MotorSettings.h +++ /dev/null @@ -1,66 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -class StreamIn; -class StreamOut; - -enum class EMotorState -{ - Off, ///< Motor is off - Velocity, ///< Motor will drive to target velocity - Position ///< Motor will drive to target position -}; - -/// Class that contains the settings for a constraint motor. -/// See the main page of the API documentation for more information on how to configure a motor. -class JPH_EXPORT MotorSettings -{ -public: - JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, MotorSettings) - - /// Constructor - MotorSettings() = default; - MotorSettings(const MotorSettings &) = default; - MotorSettings & operator = (const MotorSettings &) = default; - MotorSettings(float inFrequency, float inDamping) : mSpringSettings(ESpringMode::FrequencyAndDamping, inFrequency, inDamping) { JPH_ASSERT(IsValid()); } - MotorSettings(float inFrequency, float inDamping, float inForceLimit, float inTorqueLimit) : mSpringSettings(ESpringMode::FrequencyAndDamping, inFrequency, inDamping), mMinForceLimit(-inForceLimit), mMaxForceLimit(inForceLimit), mMinTorqueLimit(-inTorqueLimit), mMaxTorqueLimit(inTorqueLimit) { JPH_ASSERT(IsValid()); } - - /// Set asymmetric force limits - void SetForceLimits(float inMin, float inMax) { JPH_ASSERT(inMin <= inMax); mMinForceLimit = inMin; mMaxForceLimit = inMax; } - - /// Set asymmetric torque limits - void SetTorqueLimits(float inMin, float inMax) { JPH_ASSERT(inMin <= inMax); mMinTorqueLimit = inMin; mMaxTorqueLimit = inMax; } - - /// Set symmetric force limits - void SetForceLimit(float inLimit) { mMinForceLimit = -inLimit; mMaxForceLimit = inLimit; } - - /// Set symmetric torque limits - void SetTorqueLimit(float inLimit) { mMinTorqueLimit = -inLimit; mMaxTorqueLimit = inLimit; } - - /// Check if settings are valid - bool IsValid() const { return mSpringSettings.mFrequency >= 0.0f && mSpringSettings.mDamping >= 0.0f && mMinForceLimit <= mMaxForceLimit && mMinTorqueLimit <= mMaxTorqueLimit; } - - /// Saves the contents of the motor settings in binary form to inStream. - void SaveBinaryState(StreamOut &inStream) const; - - /// Restores contents from the binary stream inStream. - void RestoreBinaryState(StreamIn &inStream); - - // Settings - SpringSettings mSpringSettings { ESpringMode::FrequencyAndDamping, 2.0f, 1.0f }; ///< Settings for the spring that is used to drive to the position target (not used when motor is a velocity motor). - float mMinForceLimit = -FLT_MAX; ///< Minimum force to apply in case of a linear constraint (N). Usually this is -mMaxForceLimit unless you want a motor that can e.g. push but not pull. Not used when motor is an angular motor. - float mMaxForceLimit = FLT_MAX; ///< Maximum force to apply in case of a linear constraint (N). Not used when motor is an angular motor. - float mMinTorqueLimit = -FLT_MAX; ///< Minimum torque to apply in case of a angular constraint (N m). Usually this is -mMaxTorqueLimit unless you want a motor that can e.g. push but not pull. Not used when motor is a position motor. - float mMaxTorqueLimit = FLT_MAX; ///< Maximum torque to apply in case of a angular constraint (N m). Not used when motor is a position motor. -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PathConstraint.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PathConstraint.cpp deleted file mode 100644 index e5b6eb7da03..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PathConstraint.cpp +++ /dev/null @@ -1,458 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include -#include -#include -#include -#ifdef JPH_DEBUG_RENDERER - #include -#endif // JPH_DEBUG_RENDERER - -JPH_NAMESPACE_BEGIN - -JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(PathConstraintSettings) -{ - JPH_ADD_BASE_CLASS(PathConstraintSettings, TwoBodyConstraintSettings) - - JPH_ADD_ATTRIBUTE(PathConstraintSettings, mPath) - JPH_ADD_ATTRIBUTE(PathConstraintSettings, mPathPosition) - JPH_ADD_ATTRIBUTE(PathConstraintSettings, mPathRotation) - JPH_ADD_ATTRIBUTE(PathConstraintSettings, mPathFraction) - JPH_ADD_ATTRIBUTE(PathConstraintSettings, mMaxFrictionForce) - JPH_ADD_ATTRIBUTE(PathConstraintSettings, mPositionMotorSettings) - JPH_ADD_ENUM_ATTRIBUTE(PathConstraintSettings, mRotationConstraintType) -} - -void PathConstraintSettings::SaveBinaryState(StreamOut &inStream) const -{ - ConstraintSettings::SaveBinaryState(inStream); - - mPath->SaveBinaryState(inStream); - inStream.Write(mPathPosition); - inStream.Write(mPathRotation); - inStream.Write(mPathFraction); - inStream.Write(mMaxFrictionForce); - inStream.Write(mRotationConstraintType); - mPositionMotorSettings.SaveBinaryState(inStream); -} - -void PathConstraintSettings::RestoreBinaryState(StreamIn &inStream) -{ - ConstraintSettings::RestoreBinaryState(inStream); - - PathConstraintPath::PathResult result = PathConstraintPath::sRestoreFromBinaryState(inStream); - if (!result.HasError()) - mPath = result.Get(); - inStream.Read(mPathPosition); - inStream.Read(mPathRotation); - inStream.Read(mPathFraction); - inStream.Read(mMaxFrictionForce); - inStream.Read(mRotationConstraintType); - mPositionMotorSettings.RestoreBinaryState(inStream); -} - -TwoBodyConstraint *PathConstraintSettings::Create(Body &inBody1, Body &inBody2) const -{ - return new PathConstraint(inBody1, inBody2, *this); -} - -PathConstraint::PathConstraint(Body &inBody1, Body &inBody2, const PathConstraintSettings &inSettings) : - TwoBodyConstraint(inBody1, inBody2, inSettings), - mRotationConstraintType(inSettings.mRotationConstraintType), - mMaxFrictionForce(inSettings.mMaxFrictionForce), - mPositionMotorSettings(inSettings.mPositionMotorSettings) -{ - // Calculate transform that takes us from the path start to center of mass space of body 1 - mPathToBody1 = Mat44::sRotationTranslation(inSettings.mPathRotation, inSettings.mPathPosition - inBody1.GetShape()->GetCenterOfMass()); - - SetPath(inSettings.mPath, inSettings.mPathFraction); -} - -void PathConstraint::NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM) -{ - if (mBody1->GetID() == inBodyID) - mPathToBody1.SetTranslation(mPathToBody1.GetTranslation() - inDeltaCOM); - else if (mBody2->GetID() == inBodyID) - mPathToBody2.SetTranslation(mPathToBody2.GetTranslation() - inDeltaCOM); -} - -void PathConstraint::SetPath(const PathConstraintPath *inPath, float inPathFraction) -{ - mPath = inPath; - mPathFraction = inPathFraction; - - if (mPath != nullptr) - { - // Get the point on the path for this fraction - Vec3 path_point, path_tangent, path_normal, path_binormal; - mPath->GetPointOnPath(mPathFraction, path_point, path_tangent, path_normal, path_binormal); - - // Construct the matrix that takes us from the closest point on the path to body 2 center of mass space - Mat44 closest_point_to_path(Vec4(path_tangent, 0), Vec4(path_binormal, 0), Vec4(path_normal, 0), Vec4(path_point, 1)); - Mat44 cp_to_body1 = mPathToBody1 * closest_point_to_path; - mPathToBody2 = (mBody2->GetInverseCenterOfMassTransform() * mBody1->GetCenterOfMassTransform()).ToMat44() * cp_to_body1; - - // Calculate initial orientation - if (mRotationConstraintType == EPathRotationConstraintType::FullyConstrained) - mInvInitialOrientation = RotationEulerConstraintPart::sGetInvInitialOrientation(*mBody1, *mBody2); - } -} - -void PathConstraint::CalculateConstraintProperties(float inDeltaTime) -{ - // Get transforms of body 1 and 2 - RMat44 transform1 = mBody1->GetCenterOfMassTransform(); - RMat44 transform2 = mBody2->GetCenterOfMassTransform(); - - // Get the transform of the path transform as seen from body 1 in world space - RMat44 path_to_world_1 = transform1 * mPathToBody1; - - // Get the transform of from the point on path that body 2 is attached to in world space - RMat44 path_to_world_2 = transform2 * mPathToBody2; - - // Calculate new closest point on path - RVec3 position2 = path_to_world_2.GetTranslation(); - Vec3 position2_local_to_path = Vec3(path_to_world_1.InversedRotationTranslation() * position2); - mPathFraction = mPath->GetClosestPoint(position2_local_to_path, mPathFraction); - - // Get the point on the path for this fraction - Vec3 path_point, path_tangent, path_normal, path_binormal; - mPath->GetPointOnPath(mPathFraction, path_point, path_tangent, path_normal, path_binormal); - - // Calculate R1 and R2 - RVec3 path_point_ws = path_to_world_1 * path_point; - mR1 = Vec3(path_point_ws - mBody1->GetCenterOfMassPosition()); - mR2 = Vec3(position2 - mBody2->GetCenterOfMassPosition()); - - // Calculate U = X2 + R2 - X1 - R1 - mU = Vec3(position2 - path_point_ws); - - // Calculate world space normals - mPathNormal = path_to_world_1.Multiply3x3(path_normal); - mPathBinormal = path_to_world_1.Multiply3x3(path_binormal); - - // Calculate slide axis - mPathTangent = path_to_world_1.Multiply3x3(path_tangent); - - // Prepare constraint part for position constraint to slide along the path - mPositionConstraintPart.CalculateConstraintProperties(*mBody1, transform1.GetRotation(), mR1 + mU, *mBody2, transform2.GetRotation(), mR2, mPathNormal, mPathBinormal); - - // Check if closest point is on the boundary of the path and if so apply limit - if (!mPath->IsLooping() && (mPathFraction <= 0.0f || mPathFraction >= mPath->GetPathMaxFraction())) - mPositionLimitsConstraintPart.CalculateConstraintProperties(*mBody1, mR1 + mU, *mBody2, mR2, mPathTangent); - else - mPositionLimitsConstraintPart.Deactivate(); - - // Prepare rotation constraint part - switch (mRotationConstraintType) - { - case EPathRotationConstraintType::Free: - // No rotational limits - break; - - case EPathRotationConstraintType::ConstrainAroundTangent: - mHingeConstraintPart.CalculateConstraintProperties(*mBody1, transform1.GetRotation(), mPathTangent, *mBody2, transform2.GetRotation(), path_to_world_2.GetAxisX()); - break; - - case EPathRotationConstraintType::ConstrainAroundNormal: - mHingeConstraintPart.CalculateConstraintProperties(*mBody1, transform1.GetRotation(), mPathNormal, *mBody2, transform2.GetRotation(), path_to_world_2.GetAxisZ()); - break; - - case EPathRotationConstraintType::ConstrainAroundBinormal: - mHingeConstraintPart.CalculateConstraintProperties(*mBody1, transform1.GetRotation(), mPathBinormal, *mBody2, transform2.GetRotation(), path_to_world_2.GetAxisY()); - break; - - case EPathRotationConstraintType::ConstrainToPath: - // We need to calculate the inverse of the rotation from body 1 to body 2 for the current path position (see: RotationEulerConstraintPart::sGetInvInitialOrientation) - // RotationBody2 = RotationBody1 * InitialOrientation <=> InitialOrientation^-1 = RotationBody2^-1 * RotationBody1 - // We can express RotationBody2 in terms of RotationBody1: RotationBody2 = RotationBody1 * PathToBody1 * RotationClosestPointOnPath * PathToBody2^-1 - // Combining these two: InitialOrientation^-1 = PathToBody2 * (PathToBody1 * RotationClosestPointOnPath)^-1 - mInvInitialOrientation = mPathToBody2.Multiply3x3RightTransposed(mPathToBody1.Multiply3x3(Mat44(Vec4(path_tangent, 0), Vec4(path_binormal, 0), Vec4(path_normal, 0), Vec4::sZero()))).GetQuaternion(); - [[fallthrough]]; - - case EPathRotationConstraintType::FullyConstrained: - mRotationConstraintPart.CalculateConstraintProperties(*mBody1, transform1.GetRotation(), *mBody2, transform2.GetRotation()); - break; - } - - // Motor properties - switch (mPositionMotorState) - { - case EMotorState::Off: - if (mMaxFrictionForce > 0.0f) - mPositionMotorConstraintPart.CalculateConstraintProperties(*mBody1, mR1 + mU, *mBody2, mR2, mPathTangent); - else - mPositionMotorConstraintPart.Deactivate(); - break; - - case EMotorState::Velocity: - mPositionMotorConstraintPart.CalculateConstraintProperties(*mBody1, mR1 + mU, *mBody2, mR2, mPathTangent, -mTargetVelocity); - break; - - case EMotorState::Position: - if (mPositionMotorSettings.mSpringSettings.HasStiffness()) - { - // Calculate constraint value to drive to - float c; - if (mPath->IsLooping()) - { - float max_fraction = mPath->GetPathMaxFraction(); - c = fmod(mPathFraction - mTargetPathFraction, max_fraction); - float half_max_fraction = 0.5f * max_fraction; - if (c > half_max_fraction) - c -= max_fraction; - else if (c < -half_max_fraction) - c += max_fraction; - } - else - c = mPathFraction - mTargetPathFraction; - mPositionMotorConstraintPart.CalculateConstraintPropertiesWithSettings(inDeltaTime, *mBody1, mR1 + mU, *mBody2, mR2, mPathTangent, 0.0f, c, mPositionMotorSettings.mSpringSettings); - } - else - mPositionMotorConstraintPart.Deactivate(); - break; - } -} - -void PathConstraint::SetupVelocityConstraint(float inDeltaTime) -{ - CalculateConstraintProperties(inDeltaTime); -} - -void PathConstraint::ResetWarmStart() -{ - mPositionMotorConstraintPart.Deactivate(); - mPositionConstraintPart.Deactivate(); - mPositionLimitsConstraintPart.Deactivate(); - mHingeConstraintPart.Deactivate(); - mRotationConstraintPart.Deactivate(); -} - -void PathConstraint::WarmStartVelocityConstraint(float inWarmStartImpulseRatio) -{ - // Warm starting: Apply previous frame impulse - mPositionMotorConstraintPart.WarmStart(*mBody1, *mBody2, mPathTangent, inWarmStartImpulseRatio); - mPositionConstraintPart.WarmStart(*mBody1, *mBody2, mPathNormal, mPathBinormal, inWarmStartImpulseRatio); - mPositionLimitsConstraintPart.WarmStart(*mBody1, *mBody2, mPathTangent, inWarmStartImpulseRatio); - - switch (mRotationConstraintType) - { - case EPathRotationConstraintType::Free: - // No rotational limits - break; - - case EPathRotationConstraintType::ConstrainAroundTangent: - case EPathRotationConstraintType::ConstrainAroundNormal: - case EPathRotationConstraintType::ConstrainAroundBinormal: - mHingeConstraintPart.WarmStart(*mBody1, *mBody2, inWarmStartImpulseRatio); - break; - - case EPathRotationConstraintType::ConstrainToPath: - case EPathRotationConstraintType::FullyConstrained: - mRotationConstraintPart.WarmStart(*mBody1, *mBody2, inWarmStartImpulseRatio); - break; - } -} - -bool PathConstraint::SolveVelocityConstraint(float inDeltaTime) -{ - // Solve motor - bool motor = false; - if (mPositionMotorConstraintPart.IsActive()) - { - switch (mPositionMotorState) - { - case EMotorState::Off: - { - float max_lambda = mMaxFrictionForce * inDeltaTime; - motor = mPositionMotorConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2, mPathTangent, -max_lambda, max_lambda); - break; - } - - case EMotorState::Velocity: - case EMotorState::Position: - motor = mPositionMotorConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2, mPathTangent, inDeltaTime * mPositionMotorSettings.mMinForceLimit, inDeltaTime * mPositionMotorSettings.mMaxForceLimit); - break; - } - } - - // Solve position constraint along 2 axis - bool pos = mPositionConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2, mPathNormal, mPathBinormal); - - // Solve limits along path axis - bool limit = false; - if (mPositionLimitsConstraintPart.IsActive()) - { - if (mPathFraction <= 0.0f) - limit = mPositionLimitsConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2, mPathTangent, 0, FLT_MAX); - else - { - JPH_ASSERT(mPathFraction >= mPath->GetPathMaxFraction()); - limit = mPositionLimitsConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2, mPathTangent, -FLT_MAX, 0); - } - } - - // Solve rotational constraint - // Note, this is not entirely correct, we should apply a velocity constraint so that the body will actually follow the path - // by looking at the derivative of the tangent, normal or binormal but we don't. This means the position constraint solver - // will need to correct the orientation error that builds up, which in turn means that the simulation is not physically correct. - bool rot = false; - switch (mRotationConstraintType) - { - case EPathRotationConstraintType::Free: - // No rotational limits - break; - - case EPathRotationConstraintType::ConstrainAroundTangent: - case EPathRotationConstraintType::ConstrainAroundNormal: - case EPathRotationConstraintType::ConstrainAroundBinormal: - rot = mHingeConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2); - break; - - case EPathRotationConstraintType::ConstrainToPath: - case EPathRotationConstraintType::FullyConstrained: - rot = mRotationConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2); - break; - } - - return motor || pos || limit || rot; -} - -bool PathConstraint::SolvePositionConstraint(float inDeltaTime, float inBaumgarte) -{ - // Update constraint properties (bodies may have moved) - CalculateConstraintProperties(inDeltaTime); - - // Solve position constraint along 2 axis - bool pos = mPositionConstraintPart.SolvePositionConstraint(*mBody1, *mBody2, mU, mPathNormal, mPathBinormal, inBaumgarte); - - // Solve limits along path axis - bool limit = false; - if (mPositionLimitsConstraintPart.IsActive()) - { - if (mPathFraction <= 0.0f) - limit = mPositionLimitsConstraintPart.SolvePositionConstraint(*mBody1, *mBody2, mPathTangent, mU.Dot(mPathTangent), inBaumgarte); - else - { - JPH_ASSERT(mPathFraction >= mPath->GetPathMaxFraction()); - limit = mPositionLimitsConstraintPart.SolvePositionConstraint(*mBody1, *mBody2, mPathTangent, mU.Dot(mPathTangent), inBaumgarte); - } - } - - // Solve rotational constraint - bool rot = false; - switch (mRotationConstraintType) - { - case EPathRotationConstraintType::Free: - // No rotational limits - break; - - case EPathRotationConstraintType::ConstrainAroundTangent: - case EPathRotationConstraintType::ConstrainAroundNormal: - case EPathRotationConstraintType::ConstrainAroundBinormal: - rot = mHingeConstraintPart.SolvePositionConstraint(*mBody1, *mBody2, inBaumgarte); - break; - - case EPathRotationConstraintType::ConstrainToPath: - case EPathRotationConstraintType::FullyConstrained: - rot = mRotationConstraintPart.SolvePositionConstraint(*mBody1, *mBody2, mInvInitialOrientation, inBaumgarte); - break; - } - - return pos || limit || rot; -} - -#ifdef JPH_DEBUG_RENDERER -void PathConstraint::DrawConstraint(DebugRenderer *inRenderer) const -{ - if (mPath != nullptr) - { - // Draw the path in world space - RMat44 path_to_world = mBody1->GetCenterOfMassTransform() * mPathToBody1; - mPath->DrawPath(inRenderer, path_to_world); - - // Draw anchor point of both bodies in world space - RVec3 x1 = mBody1->GetCenterOfMassPosition() + mR1; - RVec3 x2 = mBody2->GetCenterOfMassPosition() + mR2; - inRenderer->DrawMarker(x1, Color::sYellow, 0.1f); - inRenderer->DrawMarker(x2, Color::sYellow, 0.1f); - inRenderer->DrawArrow(x1, x1 + mPathTangent, Color::sBlue, 0.1f); - inRenderer->DrawArrow(x1, x1 + mPathNormal, Color::sRed, 0.1f); - inRenderer->DrawArrow(x1, x1 + mPathBinormal, Color::sGreen, 0.1f); - inRenderer->DrawText3D(x1, StringFormat("%.1f", (double)mPathFraction)); - - // Draw motor - switch (mPositionMotorState) - { - case EMotorState::Position: - { - // Draw target marker - Vec3 position, tangent, normal, binormal; - mPath->GetPointOnPath(mTargetPathFraction, position, tangent, normal, binormal); - inRenderer->DrawMarker(path_to_world * position, Color::sYellow, 1.0f); - break; - } - - case EMotorState::Velocity: - { - RVec3 position = mBody2->GetCenterOfMassPosition() + mR2; - inRenderer->DrawArrow(position, position + mPathTangent * mTargetVelocity, Color::sRed, 0.1f); - break; - } - - case EMotorState::Off: - break; - } - } -} -#endif // JPH_DEBUG_RENDERER - -void PathConstraint::SaveState(StateRecorder &inStream) const -{ - TwoBodyConstraint::SaveState(inStream); - - mPositionConstraintPart.SaveState(inStream); - mPositionLimitsConstraintPart.SaveState(inStream); - mPositionMotorConstraintPart.SaveState(inStream); - mHingeConstraintPart.SaveState(inStream); - mRotationConstraintPart.SaveState(inStream); - - inStream.Write(mMaxFrictionForce); - inStream.Write(mPositionMotorSettings); - inStream.Write(mPositionMotorState); - inStream.Write(mTargetVelocity); - inStream.Write(mTargetPathFraction); - inStream.Write(mPathFraction); -} - -void PathConstraint::RestoreState(StateRecorder &inStream) -{ - TwoBodyConstraint::RestoreState(inStream); - - mPositionConstraintPart.RestoreState(inStream); - mPositionLimitsConstraintPart.RestoreState(inStream); - mPositionMotorConstraintPart.RestoreState(inStream); - mHingeConstraintPart.RestoreState(inStream); - mRotationConstraintPart.RestoreState(inStream); - - inStream.Read(mMaxFrictionForce); - inStream.Read(mPositionMotorSettings); - inStream.Read(mPositionMotorState); - inStream.Read(mTargetVelocity); - inStream.Read(mTargetPathFraction); - inStream.Read(mPathFraction); -} - -Ref PathConstraint::GetConstraintSettings() const -{ - JPH_ASSERT(false); // Not implemented yet - return nullptr; -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PathConstraint.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PathConstraint.h deleted file mode 100644 index 6301b852330..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PathConstraint.h +++ /dev/null @@ -1,186 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -/// How to constrain the rotation of the body to a PathConstraint -enum class EPathRotationConstraintType -{ - Free, ///< Do not constrain the rotation of the body at all - ConstrainAroundTangent, ///< Only allow rotation around the tangent vector (following the path) - ConstrainAroundNormal, ///< Only allow rotation around the normal vector (perpendicular to the path) - ConstrainAroundBinormal, ///< Only allow rotation around the binormal vector (perpendicular to the path) - ConstrainToPath, ///< Fully constrain the rotation of body 2 to the path (following the tangent and normal of the path) - FullyConstrained, ///< Fully constrain the rotation of the body 2 to the rotation of body 1 -}; - -/// Path constraint settings, used to constrain the degrees of freedom between two bodies to a path -/// -/// The requirements of the path are that: -/// * Tangent, normal and bi-normal form an orthonormal basis with: tangent cross bi-normal = normal -/// * The path points along the tangent vector -/// * The path is continuous so doesn't contain any sharp corners -/// -/// The reason for all this is that the constraint acts like a slider constraint with the sliding axis being the tangent vector (the assumption here is that delta time will be small enough so that the path is linear for that delta time). -class JPH_EXPORT PathConstraintSettings final : public TwoBodyConstraintSettings -{ -public: - JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, PathConstraintSettings) - - // See: ConstraintSettings::SaveBinaryState - virtual void SaveBinaryState(StreamOut &inStream) const override; - - /// Create an instance of this constraint - virtual TwoBodyConstraint * Create(Body &inBody1, Body &inBody2) const override; - - /// The path that constrains the two bodies - RefConst mPath; - - /// The position of the path start relative to world transform of body 1 - Vec3 mPathPosition = Vec3::sZero(); - - /// The rotation of the path start relative to world transform of body 1 - Quat mPathRotation = Quat::sIdentity(); - - /// The fraction along the path that corresponds to the initial position of body 2. Usually this is 0, the beginning of the path. But if you want to start an object halfway the path you can calculate this with mPath->GetClosestPoint(point on path to attach body to). - float mPathFraction = 0.0f; - - /// Maximum amount of friction force to apply (N) when not driven by a motor. - float mMaxFrictionForce = 0.0f; - - /// In case the constraint is powered, this determines the motor settings along the path - MotorSettings mPositionMotorSettings; - - /// How to constrain the rotation of the body to the path - EPathRotationConstraintType mRotationConstraintType = EPathRotationConstraintType::Free; - -protected: - // See: ConstraintSettings::RestoreBinaryState - virtual void RestoreBinaryState(StreamIn &inStream) override; -}; - -/// Path constraint, used to constrain the degrees of freedom between two bodies to a path -class JPH_EXPORT PathConstraint final : public TwoBodyConstraint -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Construct point constraint - PathConstraint(Body &inBody1, Body &inBody2, const PathConstraintSettings &inSettings); - - // Generic interface of a constraint - virtual EConstraintSubType GetSubType() const override { return EConstraintSubType::Path; } - virtual void NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM) override; - virtual void SetupVelocityConstraint(float inDeltaTime) override; - virtual void ResetWarmStart() override; - virtual void WarmStartVelocityConstraint(float inWarmStartImpulseRatio) override; - virtual bool SolveVelocityConstraint(float inDeltaTime) override; - virtual bool SolvePositionConstraint(float inDeltaTime, float inBaumgarte) override; -#ifdef JPH_DEBUG_RENDERER - virtual void DrawConstraint(DebugRenderer *inRenderer) const override; -#endif // JPH_DEBUG_RENDERER - virtual void SaveState(StateRecorder &inStream) const override; - virtual void RestoreState(StateRecorder &inStream) override; - virtual bool IsActive() const override { return TwoBodyConstraint::IsActive() && mPath != nullptr; } - virtual Ref GetConstraintSettings() const override; - - // See: TwoBodyConstraint - virtual Mat44 GetConstraintToBody1Matrix() const override { return mPathToBody1; } - virtual Mat44 GetConstraintToBody2Matrix() const override { return mPathToBody2; } - - /// Update the path for this constraint - void SetPath(const PathConstraintPath *inPath, float inPathFraction); - - /// Access to the current path - const PathConstraintPath * GetPath() const { return mPath; } - - /// Access to the current fraction along the path e [0, GetPath()->GetMaxPathFraction()] - float GetPathFraction() const { return mPathFraction; } - - /// Friction control - void SetMaxFrictionForce(float inFrictionForce) { mMaxFrictionForce = inFrictionForce; } - float GetMaxFrictionForce() const { return mMaxFrictionForce; } - - /// Position motor settings - MotorSettings & GetPositionMotorSettings() { return mPositionMotorSettings; } - const MotorSettings & GetPositionMotorSettings() const { return mPositionMotorSettings; } - - // Position motor controls (drives body 2 along the path) - void SetPositionMotorState(EMotorState inState) { JPH_ASSERT(inState == EMotorState::Off || mPositionMotorSettings.IsValid()); mPositionMotorState = inState; } - EMotorState GetPositionMotorState() const { return mPositionMotorState; } - void SetTargetVelocity(float inVelocity) { mTargetVelocity = inVelocity; } - float GetTargetVelocity() const { return mTargetVelocity; } - void SetTargetPathFraction(float inFraction) { JPH_ASSERT(mPath->IsLooping() || (inFraction >= 0.0f && inFraction <= mPath->GetPathMaxFraction())); mTargetPathFraction = inFraction; } - float GetTargetPathFraction() const { return mTargetPathFraction; } - - ///@name Get Lagrange multiplier from last physics update (the linear/angular impulse applied to satisfy the constraint) - inline Vector<2> GetTotalLambdaPosition() const { return mPositionConstraintPart.GetTotalLambda(); } - inline float GetTotalLambdaPositionLimits() const { return mPositionLimitsConstraintPart.GetTotalLambda(); } - inline float GetTotalLambdaMotor() const { return mPositionMotorConstraintPart.GetTotalLambda(); } - inline Vector<2> GetTotalLambdaRotationHinge() const { return mHingeConstraintPart.GetTotalLambda(); } - inline Vec3 GetTotalLambdaRotation() const { return mRotationConstraintPart.GetTotalLambda(); } - -private: - // Internal helper function to calculate the values below - void CalculateConstraintProperties(float inDeltaTime); - - // CONFIGURATION PROPERTIES FOLLOW - - RefConst mPath; ///< The path that attaches the two bodies - Mat44 mPathToBody1; ///< Transform that takes a quantity from path space to body 1 center of mass space - Mat44 mPathToBody2; ///< Transform that takes a quantity from path space to body 2 center of mass space - EPathRotationConstraintType mRotationConstraintType; ///< How to constrain the rotation of the path - - // Friction - float mMaxFrictionForce; - - // Motor controls - MotorSettings mPositionMotorSettings; - EMotorState mPositionMotorState = EMotorState::Off; - float mTargetVelocity = 0.0f; - float mTargetPathFraction = 0.0f; - - // RUN TIME PROPERTIES FOLLOW - - // Positions where the point constraint acts on in world space - Vec3 mR1; - Vec3 mR2; - - // X2 + R2 - X1 - R1 - Vec3 mU; - - // World space path tangent - Vec3 mPathTangent; - - // Normals to the path tangent - Vec3 mPathNormal; - Vec3 mPathBinormal; - - // Inverse of initial rotation from body 1 to body 2 in body 1 space (only used when rotation constraint type is FullyConstrained) - Quat mInvInitialOrientation; - - // Current fraction along the path where body 2 is attached - float mPathFraction = 0.0f; - - // Translation constraint parts - DualAxisConstraintPart mPositionConstraintPart; ///< Constraint part that keeps the movement along the tangent of the path - AxisConstraintPart mPositionLimitsConstraintPart; ///< Constraint part that prevents movement beyond the beginning and end of the path - AxisConstraintPart mPositionMotorConstraintPart; ///< Constraint to drive the object along the path or to apply friction - - // Rotation constraint parts - HingeRotationConstraintPart mHingeConstraintPart; ///< Constraint part that removes 2 degrees of rotation freedom - RotationEulerConstraintPart mRotationConstraintPart; ///< Constraint part that removes all rotational freedom -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PathConstraintPath.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PathConstraintPath.cpp deleted file mode 100644 index 69c0a82f3bd..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PathConstraintPath.cpp +++ /dev/null @@ -1,85 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#ifdef JPH_DEBUG_RENDERER - #include -#endif // JPH_DEBUG_RENDERER - -JPH_NAMESPACE_BEGIN - -JPH_IMPLEMENT_SERIALIZABLE_ABSTRACT(PathConstraintPath) -{ - JPH_ADD_BASE_CLASS(PathConstraintPath, SerializableObject) -} - -#ifdef JPH_DEBUG_RENDERER -// Helper function to transform the results of GetPointOnPath to world space -static inline void sTransformPathPoint(RMat44Arg inTransform, Vec3Arg inPosition, RVec3 &outPosition, Vec3 &ioNormal, Vec3 &ioBinormal) -{ - outPosition = inTransform * inPosition; - ioNormal = inTransform.Multiply3x3(ioNormal); - ioBinormal = inTransform.Multiply3x3(ioBinormal); -} - -// Helper function to draw a path segment -static inline void sDrawPathSegment(DebugRenderer *inRenderer, RVec3Arg inPrevPosition, RVec3Arg inPosition, Vec3Arg inNormal, Vec3Arg inBinormal) -{ - inRenderer->DrawLine(inPrevPosition, inPosition, Color::sWhite); - inRenderer->DrawArrow(inPosition, inPosition + 0.1f * inNormal, Color::sRed, 0.02f); - inRenderer->DrawArrow(inPosition, inPosition + 0.1f * inBinormal, Color::sGreen, 0.02f); -} - -void PathConstraintPath::DrawPath(DebugRenderer *inRenderer, RMat44Arg inBaseTransform) const -{ - // Calculate first point - Vec3 lfirst_pos, first_tangent, first_normal, first_binormal; - GetPointOnPath(0.0f, lfirst_pos, first_tangent, first_normal, first_binormal); - RVec3 first_pos; - sTransformPathPoint(inBaseTransform, lfirst_pos, first_pos, first_normal, first_binormal); - - float t_max = GetPathMaxFraction(); - - // Draw the segments - RVec3 prev_pos = first_pos; - for (float t = 0.1f; t < t_max; t += 0.1f) - { - Vec3 lpos, tangent, normal, binormal; - GetPointOnPath(t, lpos, tangent, normal, binormal); - RVec3 pos; - sTransformPathPoint(inBaseTransform, lpos, pos, normal, binormal); - sDrawPathSegment(inRenderer, prev_pos, pos, normal, binormal); - prev_pos = pos; - } - - // Draw last point - Vec3 lpos, tangent, normal, binormal; - GetPointOnPath(t_max, lpos, tangent, normal, binormal); - RVec3 pos; - sTransformPathPoint(inBaseTransform, lpos, pos, normal, binormal); - sDrawPathSegment(inRenderer, prev_pos, pos, normal, binormal); -} -#endif // JPH_DEBUG_RENDERER - -void PathConstraintPath::SaveBinaryState(StreamOut &inStream) const -{ - inStream.Write(GetRTTI()->GetHash()); - inStream.Write(mIsLooping); -} - -void PathConstraintPath::RestoreBinaryState(StreamIn &inStream) -{ - // Type hash read by sRestoreFromBinaryState - inStream.Read(mIsLooping); -} - -PathConstraintPath::PathResult PathConstraintPath::sRestoreFromBinaryState(StreamIn &inStream) -{ - return StreamUtils::RestoreObject(inStream, &PathConstraintPath::RestoreBinaryState); -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PathConstraintPath.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PathConstraintPath.h deleted file mode 100644 index 06f10408a09..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PathConstraintPath.h +++ /dev/null @@ -1,71 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -class StreamIn; -class StreamOut; -#ifdef JPH_DEBUG_RENDERER -class DebugRenderer; -#endif // JPH_DEBUG_RENDERER - -/// The path for a path constraint. It allows attaching two bodies to each other while giving the second body the freedom to move along a path relative to the first. -class JPH_EXPORT PathConstraintPath : public SerializableObject, public RefTarget -{ -public: - JPH_DECLARE_SERIALIZABLE_ABSTRACT(JPH_EXPORT, PathConstraintPath) - - using PathResult = Result>; - - /// Virtual destructor to ensure that derived types get their destructors called - virtual ~PathConstraintPath() override = default; - - /// Gets the max fraction along the path. I.e. sort of the length of the path. - virtual float GetPathMaxFraction() const = 0; - - /// Get the globally closest point on the curve (Could be slow!) - /// @param inPosition Position to find closest point for - /// @param inFractionHint Last known fraction along the path (can be used to speed up the search) - /// @return Fraction of closest point along the path - virtual float GetClosestPoint(Vec3Arg inPosition, float inFractionHint) const = 0; - - /// Given the fraction along the path, get the point, tangent and normal. - /// @param inFraction Fraction along the path [0, GetPathMaxFraction()]. - /// @param outPathPosition Returns the closest position to inSearchPosition on the path. - /// @param outPathTangent Returns the tangent to the path at outPathPosition (the vector that follows the direction of the path) - /// @param outPathNormal Return the normal to the path at outPathPosition (a vector that's perpendicular to outPathTangent) - /// @param outPathBinormal Returns the binormal to the path at outPathPosition (a vector so that normal cross tangent = binormal) - virtual void GetPointOnPath(float inFraction, Vec3 &outPathPosition, Vec3 &outPathTangent, Vec3 &outPathNormal, Vec3 &outPathBinormal) const = 0; - - /// If the path is looping or not. If a path is looping, the first and last point are automatically connected to each other. They should not be the same points. - void SetIsLooping(bool inIsLooping) { mIsLooping = inIsLooping; } - bool IsLooping() const { return mIsLooping; } - -#ifdef JPH_DEBUG_RENDERER - /// Draw the path relative to inBaseTransform. Used for debug purposes. - void DrawPath(DebugRenderer *inRenderer, RMat44Arg inBaseTransform) const; -#endif // JPH_DEBUG_RENDERER - - /// Saves the contents of the path in binary form to inStream. - virtual void SaveBinaryState(StreamOut &inStream) const; - - /// Creates a Shape of the correct type and restores its contents from the binary stream inStream. - static PathResult sRestoreFromBinaryState(StreamIn &inStream); - -protected: - /// This function should not be called directly, it is used by sRestoreFromBinaryState. - virtual void RestoreBinaryState(StreamIn &inStream); - -private: - /// If the path is looping or not. If a path is looping, the first and last point are automatically connected to each other. They should not be the same points. - bool mIsLooping = false; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PathConstraintPathHermite.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PathConstraintPathHermite.cpp deleted file mode 100644 index 55c1f33ad82..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PathConstraintPathHermite.cpp +++ /dev/null @@ -1,308 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(PathConstraintPathHermite::Point) -{ - JPH_ADD_ATTRIBUTE(PathConstraintPathHermite::Point, mPosition) - JPH_ADD_ATTRIBUTE(PathConstraintPathHermite::Point, mTangent) - JPH_ADD_ATTRIBUTE(PathConstraintPathHermite::Point, mNormal) -} - -JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(PathConstraintPathHermite) -{ - JPH_ADD_BASE_CLASS(PathConstraintPathHermite, PathConstraintPath) - - JPH_ADD_ATTRIBUTE(PathConstraintPathHermite, mPoints) -} - -// Calculate position and tangent for a Cubic Hermite Spline segment -static inline void sCalculatePositionAndTangent(Vec3Arg inP1, Vec3Arg inM1, Vec3Arg inP2, Vec3Arg inM2, float inT, Vec3 &outPosition, Vec3 &outTangent) -{ - // Calculate factors for Cubic Hermite Spline - // See: https://en.wikipedia.org/wiki/Cubic_Hermite_spline - float t2 = inT * inT; - float t3 = inT * t2; - float h00 = 2.0f * t3 - 3.0f * t2 + 1.0f; - float h10 = t3 - 2.0f * t2 + inT; - float h01 = -2.0f * t3 + 3.0f * t2; - float h11 = t3 - t2; - - // Calculate d/dt for factors to calculate the tangent - float ddt_h00 = 6.0f * (t2 - inT); - float ddt_h10 = 3.0f * t2 - 4.0f * inT + 1.0f; - float ddt_h01 = -ddt_h00; - float ddt_h11 = 3.0f * t2 - 2.0f * inT; - - outPosition = h00 * inP1 + h10 * inM1 + h01 * inP2 + h11 * inM2; - outTangent = ddt_h00 * inP1 + ddt_h10 * inM1 + ddt_h01 * inP2 + ddt_h11 * inM2; -} - -// Calculate the closest point to the origin for a Cubic Hermite Spline segment -// This is used to get an estimate for the interval in which the closest point can be found, -// the interval [0, 1] is too big for Newton Raphson to work on because it is solving a 5th degree polynomial which may -// have multiple local minima that are not the root. This happens especially when the path is straight (tangents aligned with inP2 - inP1). -// Based on the bisection method: https://en.wikipedia.org/wiki/Bisection_method -static inline void sCalculateClosestPointThroughBisection(Vec3Arg inP1, Vec3Arg inM1, Vec3Arg inP2, Vec3Arg inM2, float &outTMin, float &outTMax) -{ - outTMin = 0.0f; - outTMax = 1.0f; - - // To get the closest point of the curve to the origin we need to solve: - // d/dt P(t) . P(t) = 0 for t, where P(t) is the point on the curve segment - // Using d/dt (a(t) . b(t)) = d/dt a(t) . b(t) + a(t) . d/dt b(t) - // See: https://proofwiki.org/wiki/Derivative_of_Dot_Product_of_Vector-Valued_Functions - // d/dt P(t) . P(t) = 2 P(t) d/dt P(t) = 2 P(t) . Tangent(t) - - // Calculate the derivative at t = 0, we know P(0) = inP1 and Tangent(0) = inM1 - float ddt_min = inP1.Dot(inM1); // Leaving out factor 2, we're only interested in the root - if (abs(ddt_min) < 1.0e-6f) - { - // Derivative is near zero, we found our root - outTMax = 0.0f; - return; - } - bool ddt_min_negative = ddt_min < 0.0f; - - // Calculate derivative at t = 1, we know P(1) = inP2 and Tangent(1) = inM2 - float ddt_max = inP2.Dot(inM2); - if (abs(ddt_max) < 1.0e-6f) - { - // Derivative is near zero, we found our root - outTMin = 1.0f; - return; - } - bool ddt_max_negative = ddt_max < 0.0f; - - // If the signs of the derivative are not different, this algorithm can't find the root - if (ddt_min_negative == ddt_max_negative) - return; - - // With 4 iterations we'll get a result accurate to 1 / 2^4 = 0.0625 - for (int iteration = 0; iteration < 4; ++iteration) - { - float t_mid = 0.5f * (outTMin + outTMax); - Vec3 position, tangent; - sCalculatePositionAndTangent(inP1, inM1, inP2, inM2, t_mid, position, tangent); - float ddt_mid = position.Dot(tangent); - if (abs(ddt_mid) < 1.0e-6f) - { - // Derivative is near zero, we found our root - outTMin = outTMax = t_mid; - return; - } - bool ddt_mid_negative = ddt_mid < 0.0f; - - // Update the search interval so that the signs of the derivative at both ends of the interval are still different - if (ddt_mid_negative == ddt_min_negative) - outTMin = t_mid; - else - outTMax = t_mid; - } -} - -// Calculate the closest point to the origin for a Cubic Hermite Spline segment -// Only considers the range t e [inTMin, inTMax] and will stop as soon as the closest point falls outside of that range -static inline float sCalculateClosestPointThroughNewtonRaphson(Vec3Arg inP1, Vec3Arg inM1, Vec3Arg inP2, Vec3Arg inM2, float inTMin, float inTMax, float &outDistanceSq) -{ - // This is the closest position on the curve to the origin that we found - Vec3 position; - - // Calculate the size of the interval - float interval = inTMax - inTMin; - - // Start in the middle of the interval - float t = 0.5f * (inTMin + inTMax); - - // Do max 10 iterations to prevent taking too much CPU time - for (int iteration = 0; iteration < 10; ++iteration) - { - // Calculate derivative at t, see comment at sCalculateClosestPointThroughBisection for derivation of the equations - Vec3 tangent; - sCalculatePositionAndTangent(inP1, inM1, inP2, inM2, t, position, tangent); - float ddt = position.Dot(tangent); // Leaving out factor 2, we're only interested in the root - - // Calculate derivative of ddt: d^2/dt P(t) . P(t) = d/dt (2 P(t) . Tangent(t)) - // = 2 (d/dt P(t)) . Tangent(t) + P(t) . d/dt Tangent(t)) = 2 (Tangent(t) . Tangent(t) + P(t) . d/dt Tangent(t)) - float d2dt_h00 = 12.0f * t - 6.0f; - float d2dt_h10 = 6.0f * t - 4.0f; - float d2dt_h01 = -d2dt_h00; - float d2dt_h11 = 6.0f * t - 2.0f; - Vec3 ddt_tangent = d2dt_h00 * inP1 + d2dt_h10 * inM1 + d2dt_h01 * inP2 + d2dt_h11 * inM2; - float d2dt = tangent.Dot(tangent) + position.Dot(ddt_tangent); // Leaving out factor 2, because we left it out above too - - // If d2dt is zero, the curve is flat and there are multiple t's for which we are closest to the origin, stop now - if (d2dt == 0.0f) - break; - - // Do a Newton Raphson step - // See: https://en.wikipedia.org/wiki/Newton%27s_method - // Clamp against [-interval, interval] to avoid overshooting too much, we're not interested outside the interval - float delta = Clamp(-ddt / d2dt, -interval, interval); - - // If we're stepping away further from t e [inTMin, inTMax] stop now - if ((t > inTMax && delta > 0.0f) || (t < inTMin && delta < 0.0f)) - break; - - // If we've converged, stop now - t += delta; - if (abs(delta) < 1.0e-4f) - break; - } - - // Calculate the distance squared for the origin to the curve - outDistanceSq = position.LengthSq(); - return t; -} - -void PathConstraintPathHermite::GetIndexAndT(float inFraction, int &outIndex, float &outT) const -{ - int num_points = int(mPoints.size()); - - // Start by truncating the fraction to get the index and storing the remainder in t - int index = int(trunc(inFraction)); - float t = inFraction - float(index); - - if (IsLooping()) - { - JPH_ASSERT(!mPoints.front().mPosition.IsClose(mPoints.back().mPosition), "A looping path should have a different first and last point!"); - - // Make sure index is positive by adding a multiple of num_points - if (index < 0) - index += (-index / num_points + 1) * num_points; - - // Index needs to be modulo num_points - index = index % num_points; - } - else - { - // Clamp against range of points - if (index < 0) - { - index = 0; - t = 0.0f; - } - else if (index >= num_points - 1) - { - index = num_points - 2; - t = 1.0f; - } - } - - outIndex = index; - outT = t; -} - -float PathConstraintPathHermite::GetClosestPoint(Vec3Arg inPosition, float inFractionHint) const -{ - JPH_PROFILE_FUNCTION(); - - int num_points = int(mPoints.size()); - - // Start with last point on the path, in the non-looping case we won't be visiting this point - float best_dist_sq = (mPoints[num_points - 1].mPosition - inPosition).LengthSq(); - float best_t = float(num_points - 1); - - // Loop over all points - for (int i = 0, max_i = IsLooping()? num_points : num_points - 1; i < max_i; ++i) - { - const Point &p1 = mPoints[i]; - const Point &p2 = mPoints[(i + 1) % num_points]; - - // Make the curve relative to inPosition - Vec3 p1_pos = p1.mPosition - inPosition; - Vec3 p2_pos = p2.mPosition - inPosition; - - // Get distance to p1 - float dist_sq = p1_pos.LengthSq(); - if (dist_sq < best_dist_sq) - { - best_t = float(i); - best_dist_sq = dist_sq; - } - - // First find an interval for the closest point so that we can start doing Newton Raphson steps - float t_min, t_max; - sCalculateClosestPointThroughBisection(p1_pos, p1.mTangent, p2_pos, p2.mTangent, t_min, t_max); - - if (t_min == t_max) - { - // If the function above returned no interval then it found the root already and we can just calculate the distance - Vec3 position, tangent; - sCalculatePositionAndTangent(p1_pos, p1.mTangent, p2_pos, p2.mTangent, t_min, position, tangent); - dist_sq = position.LengthSq(); - if (dist_sq < best_dist_sq) - { - best_t = float(i) + t_min; - best_dist_sq = dist_sq; - } - } - else - { - // Get closest distance along curve segment - float t = sCalculateClosestPointThroughNewtonRaphson(p1_pos, p1.mTangent, p2_pos, p2.mTangent, t_min, t_max, dist_sq); - if (t >= 0.0f && t <= 1.0f && dist_sq < best_dist_sq) - { - best_t = float(i) + t; - best_dist_sq = dist_sq; - } - } - } - - return best_t; -} - -void PathConstraintPathHermite::GetPointOnPath(float inFraction, Vec3 &outPathPosition, Vec3 &outPathTangent, Vec3 &outPathNormal, Vec3 &outPathBinormal) const -{ - JPH_PROFILE_FUNCTION(); - - // Determine which hermite spline segment we need - int index; - float t; - GetIndexAndT(inFraction, index, t); - - // Get the points on the segment - const Point &p1 = mPoints[index]; - const Point &p2 = mPoints[(index + 1) % int(mPoints.size())]; - - // Calculate the position and tangent on the path - Vec3 tangent; - sCalculatePositionAndTangent(p1.mPosition, p1.mTangent, p2.mPosition, p2.mTangent, t, outPathPosition, tangent); - outPathTangent = tangent.Normalized(); - - // Just linearly interpolate the normal - Vec3 normal = (1.0f - t) * p1.mNormal + t * p2.mNormal; - - // Calculate binormal - outPathBinormal = normal.Cross(outPathTangent).Normalized(); - - // Recalculate normal so it is perpendicular to both (linear interpolation will cause it not to be) - outPathNormal = outPathTangent.Cross(outPathBinormal); - JPH_ASSERT(outPathNormal.IsNormalized()); -} - -void PathConstraintPathHermite::SaveBinaryState(StreamOut &inStream) const -{ - PathConstraintPath::SaveBinaryState(inStream); - - inStream.Write(mPoints); -} - -void PathConstraintPathHermite::RestoreBinaryState(StreamIn &inStream) -{ - PathConstraintPath::RestoreBinaryState(inStream); - - inStream.Read(mPoints); -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PathConstraintPathHermite.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PathConstraintPathHermite.h deleted file mode 100644 index aaf2ed8f752..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PathConstraintPathHermite.h +++ /dev/null @@ -1,54 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -/// A path that follows a Hermite spline -class JPH_EXPORT PathConstraintPathHermite final : public PathConstraintPath -{ -public: - JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, PathConstraintPathHermite) - - // See PathConstraintPath::GetPathMaxFraction - virtual float GetPathMaxFraction() const override { return float(IsLooping()? mPoints.size() : mPoints.size() - 1); } - - // See PathConstraintPath::GetClosestPoint - virtual float GetClosestPoint(Vec3Arg inPosition, float inFractionHint) const override; - - // See PathConstraintPath::GetPointOnPath - virtual void GetPointOnPath(float inFraction, Vec3 &outPathPosition, Vec3 &outPathTangent, Vec3 &outPathNormal, Vec3 &outPathBinormal) const override; - - /// Adds a point to the path - void AddPoint(Vec3Arg inPosition, Vec3Arg inTangent, Vec3Arg inNormal) { mPoints.push_back({ inPosition, inTangent, inNormal}); } - - // See: PathConstraintPath::SaveBinaryState - virtual void SaveBinaryState(StreamOut &inStream) const override; - - struct Point - { - JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, Point) - - Vec3 mPosition; ///< Position on the path - Vec3 mTangent; ///< Tangent of the path, does not need to be normalized (in the direction of the path) - Vec3 mNormal; ///< Normal of the path (together with the tangent along the curve this forms a basis for the constraint) - }; - -protected: - // See: PathConstraintPath::RestoreBinaryState - virtual void RestoreBinaryState(StreamIn &inStream) override; - -private: - /// Helper function that returns the index of the path segment and the fraction t on the path segment based on the full path fraction - inline void GetIndexAndT(float inFraction, int &outIndex, float &outT) const; - - using Points = Array; - - Points mPoints; ///< Points on the Hermite spline -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PointConstraint.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PointConstraint.cpp deleted file mode 100644 index 74d0ecd7c74..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PointConstraint.cpp +++ /dev/null @@ -1,157 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include -#include -#include -#ifdef JPH_DEBUG_RENDERER - #include -#endif // JPH_DEBUG_RENDERER - -JPH_NAMESPACE_BEGIN - -JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(PointConstraintSettings) -{ - JPH_ADD_BASE_CLASS(PointConstraintSettings, TwoBodyConstraintSettings) - - JPH_ADD_ENUM_ATTRIBUTE(PointConstraintSettings, mSpace) - JPH_ADD_ATTRIBUTE(PointConstraintSettings, mPoint1) - JPH_ADD_ATTRIBUTE(PointConstraintSettings, mPoint2) -} - -void PointConstraintSettings::SaveBinaryState(StreamOut &inStream) const -{ - ConstraintSettings::SaveBinaryState(inStream); - - inStream.Write(mSpace); - inStream.Write(mPoint1); - inStream.Write(mPoint2); -} - -void PointConstraintSettings::RestoreBinaryState(StreamIn &inStream) -{ - ConstraintSettings::RestoreBinaryState(inStream); - - inStream.Read(mSpace); - inStream.Read(mPoint1); - inStream.Read(mPoint2); -} - -TwoBodyConstraint *PointConstraintSettings::Create(Body &inBody1, Body &inBody2) const -{ - return new PointConstraint(inBody1, inBody2, *this); -} - -PointConstraint::PointConstraint(Body &inBody1, Body &inBody2, const PointConstraintSettings &inSettings) : - TwoBodyConstraint(inBody1, inBody2, inSettings) -{ - if (inSettings.mSpace == EConstraintSpace::WorldSpace) - { - // If all properties were specified in world space, take them to local space now - mLocalSpacePosition1 = Vec3(inBody1.GetInverseCenterOfMassTransform() * inSettings.mPoint1); - mLocalSpacePosition2 = Vec3(inBody2.GetInverseCenterOfMassTransform() * inSettings.mPoint2); - } - else - { - mLocalSpacePosition1 = Vec3(inSettings.mPoint1); - mLocalSpacePosition2 = Vec3(inSettings.mPoint2); - } -} - -void PointConstraint::NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM) -{ - if (mBody1->GetID() == inBodyID) - mLocalSpacePosition1 -= inDeltaCOM; - else if (mBody2->GetID() == inBodyID) - mLocalSpacePosition2 -= inDeltaCOM; -} - -void PointConstraint::SetPoint1(EConstraintSpace inSpace, RVec3Arg inPoint1) -{ - if (inSpace == EConstraintSpace::WorldSpace) - mLocalSpacePosition1 = Vec3(mBody1->GetInverseCenterOfMassTransform() * inPoint1); - else - mLocalSpacePosition1 = Vec3(inPoint1); -} - -void PointConstraint::SetPoint2(EConstraintSpace inSpace, RVec3Arg inPoint2) -{ - if (inSpace == EConstraintSpace::WorldSpace) - mLocalSpacePosition2 = Vec3(mBody2->GetInverseCenterOfMassTransform() * inPoint2); - else - mLocalSpacePosition2 = Vec3(inPoint2); -} - -void PointConstraint::CalculateConstraintProperties() -{ - mPointConstraintPart.CalculateConstraintProperties(*mBody1, Mat44::sRotation(mBody1->GetRotation()), mLocalSpacePosition1, *mBody2, Mat44::sRotation(mBody2->GetRotation()), mLocalSpacePosition2); -} - -void PointConstraint::SetupVelocityConstraint(float inDeltaTime) -{ - CalculateConstraintProperties(); -} - -void PointConstraint::ResetWarmStart() -{ - mPointConstraintPart.Deactivate(); -} - -void PointConstraint::WarmStartVelocityConstraint(float inWarmStartImpulseRatio) -{ - // Warm starting: Apply previous frame impulse - mPointConstraintPart.WarmStart(*mBody1, *mBody2, inWarmStartImpulseRatio); -} - -bool PointConstraint::SolveVelocityConstraint(float inDeltaTime) -{ - return mPointConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2); -} - -bool PointConstraint::SolvePositionConstraint(float inDeltaTime, float inBaumgarte) -{ - // Update constraint properties (bodies may have moved) - CalculateConstraintProperties(); - - return mPointConstraintPart.SolvePositionConstraint(*mBody1, *mBody2, inBaumgarte); -} - -#ifdef JPH_DEBUG_RENDERER -void PointConstraint::DrawConstraint(DebugRenderer *inRenderer) const -{ - // Draw constraint - inRenderer->DrawMarker(mBody1->GetCenterOfMassTransform() * mLocalSpacePosition1, Color::sRed, 0.1f); - inRenderer->DrawMarker(mBody2->GetCenterOfMassTransform() * mLocalSpacePosition2, Color::sGreen, 0.1f); -} -#endif // JPH_DEBUG_RENDERER - -void PointConstraint::SaveState(StateRecorder &inStream) const -{ - TwoBodyConstraint::SaveState(inStream); - - mPointConstraintPart.SaveState(inStream); -} - -void PointConstraint::RestoreState(StateRecorder &inStream) -{ - TwoBodyConstraint::RestoreState(inStream); - - mPointConstraintPart.RestoreState(inStream); -} - -Ref PointConstraint::GetConstraintSettings() const -{ - PointConstraintSettings *settings = new PointConstraintSettings; - ToConstraintSettings(*settings); - settings->mSpace = EConstraintSpace::LocalToBodyCOM; - settings->mPoint1 = RVec3(mLocalSpacePosition1); - settings->mPoint2 = RVec3(mLocalSpacePosition2); - return settings; -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PointConstraint.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PointConstraint.h deleted file mode 100644 index f88756931ec..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PointConstraint.h +++ /dev/null @@ -1,94 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include - -JPH_NAMESPACE_BEGIN - -/// Point constraint settings, used to create a point constraint -class JPH_EXPORT PointConstraintSettings final : public TwoBodyConstraintSettings -{ -public: - JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, PointConstraintSettings) - - // See: ConstraintSettings::SaveBinaryState - virtual void SaveBinaryState(StreamOut &inStream) const override; - - /// Create an instance of this constraint - virtual TwoBodyConstraint * Create(Body &inBody1, Body &inBody2) const override; - - /// This determines in which space the constraint is setup, all properties below should be in the specified space - EConstraintSpace mSpace = EConstraintSpace::WorldSpace; - - /// Body 1 constraint position (space determined by mSpace). - RVec3 mPoint1 = RVec3::sZero(); - - /// Body 2 constraint position (space determined by mSpace). - /// Note: Normally you would set mPoint1 = mPoint2 if the bodies are already placed how you want to constrain them (if mSpace = world space). - RVec3 mPoint2 = RVec3::sZero(); - -protected: - // See: ConstraintSettings::RestoreBinaryState - virtual void RestoreBinaryState(StreamIn &inStream) override; -}; - -/// A point constraint constrains 2 bodies on a single point (removing 3 degrees of freedom) -class JPH_EXPORT PointConstraint final : public TwoBodyConstraint -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Construct point constraint - PointConstraint(Body &inBody1, Body &inBody2, const PointConstraintSettings &inSettings); - - // Generic interface of a constraint - virtual EConstraintSubType GetSubType() const override { return EConstraintSubType::Point; } - virtual void NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM) override; - virtual void SetupVelocityConstraint(float inDeltaTime) override; - virtual void ResetWarmStart() override; - virtual void WarmStartVelocityConstraint(float inWarmStartImpulseRatio) override; - virtual bool SolveVelocityConstraint(float inDeltaTime) override; - virtual bool SolvePositionConstraint(float inDeltaTime, float inBaumgarte) override; -#ifdef JPH_DEBUG_RENDERER - virtual void DrawConstraint(DebugRenderer *inRenderer) const override; -#endif // JPH_DEBUG_RENDERER - virtual void SaveState(StateRecorder &inStream) const override; - virtual void RestoreState(StateRecorder &inStream) override; - virtual Ref GetConstraintSettings() const override; - - /// Update the attachment point for body 1 - void SetPoint1(EConstraintSpace inSpace, RVec3Arg inPoint1); - - /// Update the attachment point for body 2 - void SetPoint2(EConstraintSpace inSpace, RVec3Arg inPoint2); - - /// Get the attachment point for body 1 relative to body 1 COM - inline Vec3 GetLocalSpacePoint1() const { return mLocalSpacePosition1; } - - /// Get the attachment point for body 2 relative to body 2 COM - inline Vec3 GetLocalSpacePoint2() const { return mLocalSpacePosition2; } - - // See: TwoBodyConstraint - virtual Mat44 GetConstraintToBody1Matrix() const override { return Mat44::sTranslation(mLocalSpacePosition1); } - virtual Mat44 GetConstraintToBody2Matrix() const override { return Mat44::sTranslation(mLocalSpacePosition2); } // Note: Incorrect rotation as we don't track the original rotation difference, should not matter though as the constraint is not limiting rotation. - - ///@name Get Lagrange multiplier from last physics update (the linear impulse applied to satisfy the constraint) - inline Vec3 GetTotalLambdaPosition() const { return mPointConstraintPart.GetTotalLambda(); } - -private: - // Internal helper function to calculate the values below - void CalculateConstraintProperties(); - - // Local space constraint positions - Vec3 mLocalSpacePosition1; - Vec3 mLocalSpacePosition2; - - // The constraint part - PointConstraintPart mPointConstraintPart; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PulleyConstraint.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PulleyConstraint.cpp deleted file mode 100644 index 9d15c1d4dc1..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PulleyConstraint.cpp +++ /dev/null @@ -1,253 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2022 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include -#include -#include -#ifdef JPH_DEBUG_RENDERER - #include -#endif // JPH_DEBUG_RENDERER - -JPH_NAMESPACE_BEGIN - -using namespace literals; - -JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(PulleyConstraintSettings) -{ - JPH_ADD_BASE_CLASS(PulleyConstraintSettings, TwoBodyConstraintSettings) - - JPH_ADD_ENUM_ATTRIBUTE(PulleyConstraintSettings, mSpace) - JPH_ADD_ATTRIBUTE(PulleyConstraintSettings, mBodyPoint1) - JPH_ADD_ATTRIBUTE(PulleyConstraintSettings, mFixedPoint1) - JPH_ADD_ATTRIBUTE(PulleyConstraintSettings, mBodyPoint2) - JPH_ADD_ATTRIBUTE(PulleyConstraintSettings, mFixedPoint2) - JPH_ADD_ATTRIBUTE(PulleyConstraintSettings, mRatio) - JPH_ADD_ATTRIBUTE(PulleyConstraintSettings, mMinLength) - JPH_ADD_ATTRIBUTE(PulleyConstraintSettings, mMaxLength) -} - -void PulleyConstraintSettings::SaveBinaryState(StreamOut &inStream) const -{ - ConstraintSettings::SaveBinaryState(inStream); - - inStream.Write(mSpace); - inStream.Write(mBodyPoint1); - inStream.Write(mFixedPoint1); - inStream.Write(mBodyPoint2); - inStream.Write(mFixedPoint2); - inStream.Write(mRatio); - inStream.Write(mMinLength); - inStream.Write(mMaxLength); -} - -void PulleyConstraintSettings::RestoreBinaryState(StreamIn &inStream) -{ - ConstraintSettings::RestoreBinaryState(inStream); - - inStream.Read(mSpace); - inStream.Read(mBodyPoint1); - inStream.Read(mFixedPoint1); - inStream.Read(mBodyPoint2); - inStream.Read(mFixedPoint2); - inStream.Read(mRatio); - inStream.Read(mMinLength); - inStream.Read(mMaxLength); -} - -TwoBodyConstraint *PulleyConstraintSettings::Create(Body &inBody1, Body &inBody2) const -{ - return new PulleyConstraint(inBody1, inBody2, *this); -} - -PulleyConstraint::PulleyConstraint(Body &inBody1, Body &inBody2, const PulleyConstraintSettings &inSettings) : - TwoBodyConstraint(inBody1, inBody2, inSettings), - mFixedPosition1(inSettings.mFixedPoint1), - mFixedPosition2(inSettings.mFixedPoint2), - mRatio(inSettings.mRatio), - mMinLength(inSettings.mMinLength), - mMaxLength(inSettings.mMaxLength) -{ - if (inSettings.mSpace == EConstraintSpace::WorldSpace) - { - // If all properties were specified in world space, take them to local space now - mLocalSpacePosition1 = Vec3(inBody1.GetInverseCenterOfMassTransform() * inSettings.mBodyPoint1); - mLocalSpacePosition2 = Vec3(inBody2.GetInverseCenterOfMassTransform() * inSettings.mBodyPoint2); - mWorldSpacePosition1 = inSettings.mBodyPoint1; - mWorldSpacePosition2 = inSettings.mBodyPoint2; - } - else - { - // If properties were specified in local space, we need to calculate world space positions - mLocalSpacePosition1 = Vec3(inSettings.mBodyPoint1); - mLocalSpacePosition2 = Vec3(inSettings.mBodyPoint2); - mWorldSpacePosition1 = inBody1.GetCenterOfMassTransform() * inSettings.mBodyPoint1; - mWorldSpacePosition2 = inBody2.GetCenterOfMassTransform() * inSettings.mBodyPoint2; - } - - // Calculate min/max length if it was not provided - float current_length = GetCurrentLength(); - if (mMinLength < 0.0f) - mMinLength = current_length; - if (mMaxLength < 0.0f) - mMaxLength = current_length; - - // Initialize the normals to a likely valid axis in case the fixed points overlap with the attachment points (most likely the fixed points are above both bodies) - mWorldSpaceNormal1 = mWorldSpaceNormal2 = -Vec3::sAxisY(); -} - -void PulleyConstraint::NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM) -{ - if (mBody1->GetID() == inBodyID) - mLocalSpacePosition1 -= inDeltaCOM; - else if (mBody2->GetID() == inBodyID) - mLocalSpacePosition2 -= inDeltaCOM; -} - -float PulleyConstraint::CalculatePositionsNormalsAndLength() -{ - // Update world space positions (the bodies may have moved) - mWorldSpacePosition1 = mBody1->GetCenterOfMassTransform() * mLocalSpacePosition1; - mWorldSpacePosition2 = mBody2->GetCenterOfMassTransform() * mLocalSpacePosition2; - - // Calculate world space normals - Vec3 delta1 = Vec3(mWorldSpacePosition1 - mFixedPosition1); - float delta1_len = delta1.Length(); - if (delta1_len > 0.0f) - mWorldSpaceNormal1 = delta1 / delta1_len; - - Vec3 delta2 = Vec3(mWorldSpacePosition2 - mFixedPosition2); - float delta2_len = delta2.Length(); - if (delta2_len > 0.0f) - mWorldSpaceNormal2 = delta2 / delta2_len; - - // Calculate length - return delta1_len + mRatio * delta2_len; -} - -void PulleyConstraint::CalculateConstraintProperties() -{ - // Calculate attachment points relative to COM - Vec3 r1 = Vec3(mWorldSpacePosition1 - mBody1->GetCenterOfMassPosition()); - Vec3 r2 = Vec3(mWorldSpacePosition2 - mBody2->GetCenterOfMassPosition()); - - mIndependentAxisConstraintPart.CalculateConstraintProperties(*mBody1, *mBody2, r1, mWorldSpaceNormal1, r2, mWorldSpaceNormal2, mRatio); -} - -void PulleyConstraint::SetupVelocityConstraint(float inDeltaTime) -{ - // Determine if the constraint is active - float current_length = CalculatePositionsNormalsAndLength(); - bool min_length_violation = current_length <= mMinLength; - bool max_length_violation = current_length >= mMaxLength; - if (min_length_violation || max_length_violation) - { - // Determine max lambda based on if the length is too big or small - mMinLambda = max_length_violation? -FLT_MAX : 0.0f; - mMaxLambda = min_length_violation? FLT_MAX : 0.0f; - - CalculateConstraintProperties(); - } - else - mIndependentAxisConstraintPart.Deactivate(); -} - -void PulleyConstraint::ResetWarmStart() -{ - mIndependentAxisConstraintPart.Deactivate(); -} - -void PulleyConstraint::WarmStartVelocityConstraint(float inWarmStartImpulseRatio) -{ - mIndependentAxisConstraintPart.WarmStart(*mBody1, *mBody2, mWorldSpaceNormal1, mWorldSpaceNormal2, mRatio, inWarmStartImpulseRatio); -} - -bool PulleyConstraint::SolveVelocityConstraint(float inDeltaTime) -{ - if (mIndependentAxisConstraintPart.IsActive()) - return mIndependentAxisConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2, mWorldSpaceNormal1, mWorldSpaceNormal2, mRatio, mMinLambda, mMaxLambda); - else - return false; -} - -bool PulleyConstraint::SolvePositionConstraint(float inDeltaTime, float inBaumgarte) -{ - // Calculate new length (bodies may have changed) - float current_length = CalculatePositionsNormalsAndLength(); - - float position_error = 0.0f; - if (current_length < mMinLength) - position_error = current_length - mMinLength; - else if (current_length > mMaxLength) - position_error = current_length - mMaxLength; - - if (position_error != 0.0f) - { - // Update constraint properties (bodies may have moved) - CalculateConstraintProperties(); - - return mIndependentAxisConstraintPart.SolvePositionConstraint(*mBody1, *mBody2, mWorldSpaceNormal1, mWorldSpaceNormal2, mRatio, position_error, inBaumgarte); - } - - return false; -} - -#ifdef JPH_DEBUG_RENDERER -void PulleyConstraint::DrawConstraint(DebugRenderer *inRenderer) const -{ - // Color according to length vs min/max length - float current_length = GetCurrentLength(); - Color color = Color::sGreen; - if (current_length < mMinLength) - color = Color::sYellow; - else if (current_length > mMaxLength) - color = Color::sRed; - - // Draw constraint - inRenderer->DrawLine(mWorldSpacePosition1, mFixedPosition1, color); - inRenderer->DrawLine(mFixedPosition1, mFixedPosition2, color); - inRenderer->DrawLine(mFixedPosition2, mWorldSpacePosition2, color); - - // Draw current length - inRenderer->DrawText3D(0.5_r * (mFixedPosition1 + mFixedPosition2), StringFormat("%.2f", (double)current_length)); -} -#endif // JPH_DEBUG_RENDERER - -void PulleyConstraint::SaveState(StateRecorder &inStream) const -{ - TwoBodyConstraint::SaveState(inStream); - - mIndependentAxisConstraintPart.SaveState(inStream); - inStream.Write(mWorldSpaceNormal1); // When distance to fixed point = 0, the normal is used from last frame so we need to store it - inStream.Write(mWorldSpaceNormal2); -} - -void PulleyConstraint::RestoreState(StateRecorder &inStream) -{ - TwoBodyConstraint::RestoreState(inStream); - - mIndependentAxisConstraintPart.RestoreState(inStream); - inStream.Read(mWorldSpaceNormal1); - inStream.Read(mWorldSpaceNormal2); -} - -Ref PulleyConstraint::GetConstraintSettings() const -{ - PulleyConstraintSettings *settings = new PulleyConstraintSettings; - ToConstraintSettings(*settings); - settings->mSpace = EConstraintSpace::LocalToBodyCOM; - settings->mBodyPoint1 = RVec3(mLocalSpacePosition1); - settings->mFixedPoint1 = mFixedPosition1; - settings->mBodyPoint2 = RVec3(mLocalSpacePosition2); - settings->mFixedPoint2 = mFixedPosition2; - settings->mRatio = mRatio; - settings->mMinLength = mMinLength; - settings->mMaxLength = mMaxLength; - return settings; -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PulleyConstraint.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PulleyConstraint.h deleted file mode 100644 index 5f2523dae2b..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/PulleyConstraint.h +++ /dev/null @@ -1,137 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2022 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include - -JPH_NAMESPACE_BEGIN - -/// Pulley constraint settings, used to create a pulley constraint. -/// A pulley connects two bodies via two fixed world points to each other similar to a distance constraint. -/// We define Length1 = |BodyPoint1 - FixedPoint1| where Body1 is a point on body 1 in world space and FixedPoint1 a fixed point in world space -/// Length2 = |BodyPoint2 - FixedPoint2| -/// The constraint keeps the two line segments constrained so that -/// MinDistance <= Length1 + Ratio * Length2 <= MaxDistance -class JPH_EXPORT PulleyConstraintSettings final : public TwoBodyConstraintSettings -{ -public: - JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, PulleyConstraintSettings) - - // See: ConstraintSettings::SaveBinaryState - virtual void SaveBinaryState(StreamOut &inStream) const override; - - /// Create an instance of this constraint - virtual TwoBodyConstraint * Create(Body &inBody1, Body &inBody2) const override; - - /// This determines in which space the constraint is setup, specified properties below should be in the specified space - EConstraintSpace mSpace = EConstraintSpace::WorldSpace; - - /// Body 1 constraint attachment point (space determined by mSpace). - RVec3 mBodyPoint1 = RVec3::sZero(); - - /// Fixed world point to which body 1 is connected (always world space) - RVec3 mFixedPoint1 = RVec3::sZero(); - - /// Body 2 constraint attachment point (space determined by mSpace) - RVec3 mBodyPoint2 = RVec3::sZero(); - - /// Fixed world point to which body 2 is connected (always world space) - RVec3 mFixedPoint2 = RVec3::sZero(); - - /// Ratio between the two line segments (see formula above), can be used to create a block and tackle - float mRatio = 1.0f; - - /// The minimum length of the line segments (see formula above), use -1 to calculate the length based on the positions of the objects when the constraint is created. - float mMinLength = 0.0f; - - /// The maximum length of the line segments (see formula above), use -1 to calculate the length based on the positions of the objects when the constraint is created. - float mMaxLength = -1.0f; - -protected: - // See: ConstraintSettings::RestoreBinaryState - virtual void RestoreBinaryState(StreamIn &inStream) override; -}; - -/// A pulley constraint. -class JPH_EXPORT PulleyConstraint final : public TwoBodyConstraint -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Construct distance constraint - PulleyConstraint(Body &inBody1, Body &inBody2, const PulleyConstraintSettings &inSettings); - - // Generic interface of a constraint - virtual EConstraintSubType GetSubType() const override { return EConstraintSubType::Pulley; } - virtual void NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM) override; - virtual void SetupVelocityConstraint(float inDeltaTime) override; - virtual void ResetWarmStart() override; - virtual void WarmStartVelocityConstraint(float inWarmStartImpulseRatio) override; - virtual bool SolveVelocityConstraint(float inDeltaTime) override; - virtual bool SolvePositionConstraint(float inDeltaTime, float inBaumgarte) override; -#ifdef JPH_DEBUG_RENDERER - virtual void DrawConstraint(DebugRenderer *inRenderer) const override; -#endif // JPH_DEBUG_RENDERER - virtual void SaveState(StateRecorder &inStream) const override; - virtual void RestoreState(StateRecorder &inStream) override; - virtual Ref GetConstraintSettings() const override; - - // See: TwoBodyConstraint - virtual Mat44 GetConstraintToBody1Matrix() const override { return Mat44::sTranslation(mLocalSpacePosition1); } - virtual Mat44 GetConstraintToBody2Matrix() const override { return Mat44::sTranslation(mLocalSpacePosition2); } // Note: Incorrect rotation as we don't track the original rotation difference, should not matter though as the constraint is not limiting rotation. - - /// Update the minimum and maximum length for the constraint - void SetLength(float inMinLength, float inMaxLength) { JPH_ASSERT(inMinLength >= 0.0f && inMinLength <= inMaxLength); mMinLength = inMinLength; mMaxLength = inMaxLength; } - float GetMinLength() const { return mMinLength; } - float GetMaxLength() const { return mMaxLength; } - - /// Get the current length of both segments (multiplied by the ratio for segment 2) - float GetCurrentLength() const { return Vec3(mWorldSpacePosition1 - mFixedPosition1).Length() + mRatio * Vec3(mWorldSpacePosition2 - mFixedPosition2).Length(); } - - ///@name Get Lagrange multiplier from last physics update (the linear impulse applied to satisfy the constraint) - inline float GetTotalLambdaPosition() const { return mIndependentAxisConstraintPart.GetTotalLambda(); } - -private: - // Calculates world positions and normals and returns current length - float CalculatePositionsNormalsAndLength(); - - // Internal helper function to calculate the values below - void CalculateConstraintProperties(); - - // CONFIGURATION PROPERTIES FOLLOW - - // Local space constraint positions on the bodies - Vec3 mLocalSpacePosition1; - Vec3 mLocalSpacePosition2; - - // World space fixed positions - RVec3 mFixedPosition1; - RVec3 mFixedPosition2; - - /// Ratio between the two line segments - float mRatio; - - // The minimum/maximum length of the line segments - float mMinLength; - float mMaxLength; - - // RUN TIME PROPERTIES FOLLOW - - // World space positions and normal - RVec3 mWorldSpacePosition1; - RVec3 mWorldSpacePosition2; - Vec3 mWorldSpaceNormal1; - Vec3 mWorldSpaceNormal2; - - // Depending on if the length < min or length > max we can apply forces to prevent further violations - float mMinLambda; - float mMaxLambda; - - // The constraint part - IndependentAxisConstraintPart mIndependentAxisConstraintPart; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/RackAndPinionConstraint.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/RackAndPinionConstraint.cpp deleted file mode 100644 index 8c54d48f4f1..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/RackAndPinionConstraint.cpp +++ /dev/null @@ -1,189 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include -#include -#include -#include -#include -#ifdef JPH_DEBUG_RENDERER - #include -#endif // JPH_DEBUG_RENDERER - -JPH_NAMESPACE_BEGIN - -JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(RackAndPinionConstraintSettings) -{ - JPH_ADD_BASE_CLASS(RackAndPinionConstraintSettings, TwoBodyConstraintSettings) - - JPH_ADD_ENUM_ATTRIBUTE(RackAndPinionConstraintSettings, mSpace) - JPH_ADD_ATTRIBUTE(RackAndPinionConstraintSettings, mHingeAxis) - JPH_ADD_ATTRIBUTE(RackAndPinionConstraintSettings, mSliderAxis) - JPH_ADD_ATTRIBUTE(RackAndPinionConstraintSettings, mRatio) -} - -void RackAndPinionConstraintSettings::SaveBinaryState(StreamOut &inStream) const -{ - ConstraintSettings::SaveBinaryState(inStream); - - inStream.Write(mSpace); - inStream.Write(mHingeAxis); - inStream.Write(mSliderAxis); - inStream.Write(mRatio); -} - -void RackAndPinionConstraintSettings::RestoreBinaryState(StreamIn &inStream) -{ - ConstraintSettings::RestoreBinaryState(inStream); - - inStream.Read(mSpace); - inStream.Read(mHingeAxis); - inStream.Read(mSliderAxis); - inStream.Read(mRatio); -} - -TwoBodyConstraint *RackAndPinionConstraintSettings::Create(Body &inBody1, Body &inBody2) const -{ - return new RackAndPinionConstraint(inBody1, inBody2, *this); -} - -RackAndPinionConstraint::RackAndPinionConstraint(Body &inBody1, Body &inBody2, const RackAndPinionConstraintSettings &inSettings) : - TwoBodyConstraint(inBody1, inBody2, inSettings), - mLocalSpaceHingeAxis(inSettings.mHingeAxis), - mLocalSpaceSliderAxis(inSettings.mSliderAxis), - mRatio(inSettings.mRatio) -{ - if (inSettings.mSpace == EConstraintSpace::WorldSpace) - { - // If all properties were specified in world space, take them to local space now - mLocalSpaceHingeAxis = inBody1.GetInverseCenterOfMassTransform().Multiply3x3(mLocalSpaceHingeAxis).Normalized(); - mLocalSpaceSliderAxis = inBody2.GetInverseCenterOfMassTransform().Multiply3x3(mLocalSpaceSliderAxis).Normalized(); - } -} - -void RackAndPinionConstraint::CalculateConstraintProperties(Mat44Arg inRotation1, Mat44Arg inRotation2) -{ - // Calculate world space normals - mWorldSpaceHingeAxis = inRotation1 * mLocalSpaceHingeAxis; - mWorldSpaceSliderAxis = inRotation2 * mLocalSpaceSliderAxis; - - mRackAndPinionConstraintPart.CalculateConstraintProperties(*mBody1, mWorldSpaceHingeAxis, *mBody2, mWorldSpaceSliderAxis, mRatio); -} - -void RackAndPinionConstraint::SetupVelocityConstraint(float inDeltaTime) -{ - // Calculate constraint properties that are constant while bodies don't move - Mat44 rotation1 = Mat44::sRotation(mBody1->GetRotation()); - Mat44 rotation2 = Mat44::sRotation(mBody2->GetRotation()); - CalculateConstraintProperties(rotation1, rotation2); -} - -void RackAndPinionConstraint::ResetWarmStart() -{ - mRackAndPinionConstraintPart.Deactivate(); -} - -void RackAndPinionConstraint::WarmStartVelocityConstraint(float inWarmStartImpulseRatio) -{ - // Warm starting: Apply previous frame impulse - mRackAndPinionConstraintPart.WarmStart(*mBody1, *mBody2, inWarmStartImpulseRatio); -} - -bool RackAndPinionConstraint::SolveVelocityConstraint(float inDeltaTime) -{ - return mRackAndPinionConstraintPart.SolveVelocityConstraint(*mBody1, mWorldSpaceHingeAxis, *mBody2, mWorldSpaceSliderAxis, mRatio); -} - -bool RackAndPinionConstraint::SolvePositionConstraint(float inDeltaTime, float inBaumgarte) -{ - if (mRackConstraint == nullptr || mPinionConstraint == nullptr) - return false; - - float rotation; - if (mPinionConstraint->GetSubType() == EConstraintSubType::Hinge) - { - rotation = static_cast(mPinionConstraint.GetPtr())->GetCurrentAngle(); - } - else - { - JPH_ASSERT(false, "Unsupported"); - return false; - } - - float translation; - if (mRackConstraint->GetSubType() == EConstraintSubType::Slider) - { - translation = static_cast(mRackConstraint.GetPtr())->GetCurrentPosition(); - } - else - { - JPH_ASSERT(false, "Unsupported"); - return false; - } - - float error = CenterAngleAroundZero(fmod(rotation - mRatio * translation, 2.0f * JPH_PI)); - if (error == 0.0f) - return false; - - Mat44 rotation1 = Mat44::sRotation(mBody1->GetRotation()); - Mat44 rotation2 = Mat44::sRotation(mBody2->GetRotation()); - CalculateConstraintProperties(rotation1, rotation2); - return mRackAndPinionConstraintPart.SolvePositionConstraint(*mBody1, *mBody2, error, inBaumgarte); -} - -#ifdef JPH_DEBUG_RENDERER -void RackAndPinionConstraint::DrawConstraint(DebugRenderer *inRenderer) const -{ - RMat44 transform1 = mBody1->GetCenterOfMassTransform(); - RMat44 transform2 = mBody2->GetCenterOfMassTransform(); - - // Draw constraint axis - inRenderer->DrawArrow(transform1.GetTranslation(), transform1 * mLocalSpaceHingeAxis, Color::sGreen, 0.01f); - inRenderer->DrawArrow(transform2.GetTranslation(), transform2 * mLocalSpaceSliderAxis, Color::sBlue, 0.01f); -} - -#endif // JPH_DEBUG_RENDERER - -void RackAndPinionConstraint::SaveState(StateRecorder &inStream) const -{ - TwoBodyConstraint::SaveState(inStream); - - mRackAndPinionConstraintPart.SaveState(inStream); -} - -void RackAndPinionConstraint::RestoreState(StateRecorder &inStream) -{ - TwoBodyConstraint::RestoreState(inStream); - - mRackAndPinionConstraintPart.RestoreState(inStream); -} - -Ref RackAndPinionConstraint::GetConstraintSettings() const -{ - RackAndPinionConstraintSettings *settings = new RackAndPinionConstraintSettings; - ToConstraintSettings(*settings); - settings->mSpace = EConstraintSpace::LocalToBodyCOM; - settings->mHingeAxis = mLocalSpaceHingeAxis; - settings->mSliderAxis = mLocalSpaceSliderAxis; - settings->mRatio = mRatio; - return settings; -} - -Mat44 RackAndPinionConstraint::GetConstraintToBody1Matrix() const -{ - Vec3 perp = mLocalSpaceHingeAxis.GetNormalizedPerpendicular(); - return Mat44(Vec4(mLocalSpaceHingeAxis, 0), Vec4(perp, 0), Vec4(mLocalSpaceHingeAxis.Cross(perp), 0), Vec4(0, 0, 0, 1)); -} - -Mat44 RackAndPinionConstraint::GetConstraintToBody2Matrix() const -{ - Vec3 perp = mLocalSpaceSliderAxis.GetNormalizedPerpendicular(); - return Mat44(Vec4(mLocalSpaceSliderAxis, 0), Vec4(perp, 0), Vec4(mLocalSpaceSliderAxis.Cross(perp), 0), Vec4(0, 0, 0, 1)); -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/RackAndPinionConstraint.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/RackAndPinionConstraint.h deleted file mode 100644 index 1a0e43efbc4..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/RackAndPinionConstraint.h +++ /dev/null @@ -1,118 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include - -JPH_NAMESPACE_BEGIN - -/// Rack and pinion constraint (slider & gear) settings -class JPH_EXPORT RackAndPinionConstraintSettings final : public TwoBodyConstraintSettings -{ -public: - JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, RackAndPinionConstraintSettings) - - // See: ConstraintSettings::SaveBinaryState - virtual void SaveBinaryState(StreamOut &inStream) const override; - - /// Create an instance of this constraint. - /// Body1 should be the pinion (gear) and body 2 the rack (slider). - virtual TwoBodyConstraint * Create(Body &inBody1, Body &inBody2) const override; - - /// Defines the ratio between the rotation of the pinion and the translation of the rack. - /// The ratio is defined as: PinionRotation(t) = ratio * RackTranslation(t) - /// @param inNumTeethRack Number of teeth that the rack has - /// @param inRackLength Length of the rack - /// @param inNumTeethPinion Number of teeth the pinion has - void SetRatio(int inNumTeethRack, float inRackLength, int inNumTeethPinion) - { - mRatio = 2.0f * JPH_PI * inNumTeethRack / (inRackLength * inNumTeethPinion); - } - - /// This determines in which space the constraint is setup, all properties below should be in the specified space - EConstraintSpace mSpace = EConstraintSpace::WorldSpace; - - /// Body 1 (pinion) constraint reference frame (space determined by mSpace). - Vec3 mHingeAxis = Vec3::sAxisX(); - - /// Body 2 (rack) constraint reference frame (space determined by mSpace) - Vec3 mSliderAxis = Vec3::sAxisX(); - - /// Ratio between the rack and pinion, see SetRatio. - float mRatio = 1.0f; - -protected: - // See: ConstraintSettings::RestoreBinaryState - virtual void RestoreBinaryState(StreamIn &inStream) override; -}; - -/// A rack and pinion constraint constrains the rotation of body1 to the translation of body 2. -/// Note that this constraint needs to be used in conjunction with a hinge constraint for body 1 and a slider constraint for body 2. -class JPH_EXPORT RackAndPinionConstraint final : public TwoBodyConstraint -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Construct gear constraint - RackAndPinionConstraint(Body &inBody1, Body &inBody2, const RackAndPinionConstraintSettings &inSettings); - - // Generic interface of a constraint - virtual EConstraintSubType GetSubType() const override { return EConstraintSubType::RackAndPinion; } - virtual void NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM) override { /* Nothing */ } - virtual void SetupVelocityConstraint(float inDeltaTime) override; - virtual void ResetWarmStart() override; - virtual void WarmStartVelocityConstraint(float inWarmStartImpulseRatio) override; - virtual bool SolveVelocityConstraint(float inDeltaTime) override; - virtual bool SolvePositionConstraint(float inDeltaTime, float inBaumgarte) override; -#ifdef JPH_DEBUG_RENDERER - virtual void DrawConstraint(DebugRenderer *inRenderer) const override; -#endif // JPH_DEBUG_RENDERER - virtual void SaveState(StateRecorder &inStream) const override; - virtual void RestoreState(StateRecorder &inStream) override; - virtual Ref GetConstraintSettings() const override; - - // See: TwoBodyConstraint - virtual Mat44 GetConstraintToBody1Matrix() const override; - virtual Mat44 GetConstraintToBody2Matrix() const override; - - /// The constraints that constrain the rack and pinion (a slider and a hinge), optional and used to calculate the position error and fix numerical drift. - void SetConstraints(const Constraint *inPinion, const Constraint *inRack) { mPinionConstraint = inPinion; mRackConstraint = inRack; } - - ///@name Get Lagrange multiplier from last physics update (the linear/angular impulse applied to satisfy the constraint) - inline float GetTotalLambda() const { return mRackAndPinionConstraintPart.GetTotalLambda(); } - -private: - // Internal helper function to calculate the values below - void CalculateConstraintProperties(Mat44Arg inRotation1, Mat44Arg inRotation2); - - // CONFIGURATION PROPERTIES FOLLOW - - // Local space hinge axis - Vec3 mLocalSpaceHingeAxis; - - // Local space sliding direction - Vec3 mLocalSpaceSliderAxis; - - // Ratio between rack and pinion - float mRatio; - - // The constraints that constrain the rack and pinion (a slider and a hinge), optional and used to calculate the position error and fix numerical drift. - RefConst mPinionConstraint; - RefConst mRackConstraint; - - // RUN TIME PROPERTIES FOLLOW - - // World space hinge axis - Vec3 mWorldSpaceHingeAxis; - - // World space sliding direction - Vec3 mWorldSpaceSliderAxis; - - // The constraint parts - RackAndPinionConstraintPart mRackAndPinionConstraintPart; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/SixDOFConstraint.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/SixDOFConstraint.cpp deleted file mode 100644 index 070a45e213d..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/SixDOFConstraint.cpp +++ /dev/null @@ -1,900 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include -#include -#include -#include -#ifdef JPH_DEBUG_RENDERER - #include -#endif // JPH_DEBUG_RENDERER - -JPH_NAMESPACE_BEGIN - -JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(SixDOFConstraintSettings) -{ - JPH_ADD_BASE_CLASS(SixDOFConstraintSettings, TwoBodyConstraintSettings) - - JPH_ADD_ENUM_ATTRIBUTE(SixDOFConstraintSettings, mSpace) - JPH_ADD_ATTRIBUTE(SixDOFConstraintSettings, mPosition1) - JPH_ADD_ATTRIBUTE(SixDOFConstraintSettings, mAxisX1) - JPH_ADD_ATTRIBUTE(SixDOFConstraintSettings, mAxisY1) - JPH_ADD_ATTRIBUTE(SixDOFConstraintSettings, mPosition2) - JPH_ADD_ATTRIBUTE(SixDOFConstraintSettings, mAxisX2) - JPH_ADD_ATTRIBUTE(SixDOFConstraintSettings, mAxisY2) - JPH_ADD_ATTRIBUTE(SixDOFConstraintSettings, mMaxFriction) - JPH_ADD_ENUM_ATTRIBUTE(SixDOFConstraintSettings, mSwingType) - JPH_ADD_ATTRIBUTE(SixDOFConstraintSettings, mLimitMin) - JPH_ADD_ATTRIBUTE(SixDOFConstraintSettings, mLimitMax) - JPH_ADD_ATTRIBUTE(SixDOFConstraintSettings, mLimitsSpringSettings) - JPH_ADD_ATTRIBUTE(SixDOFConstraintSettings, mMotorSettings) -} - -void SixDOFConstraintSettings::SaveBinaryState(StreamOut &inStream) const -{ - ConstraintSettings::SaveBinaryState(inStream); - - inStream.Write(mSpace); - inStream.Write(mPosition1); - inStream.Write(mAxisX1); - inStream.Write(mAxisY1); - inStream.Write(mPosition2); - inStream.Write(mAxisX2); - inStream.Write(mAxisY2); - inStream.Write(mMaxFriction); - inStream.Write(mSwingType); - inStream.Write(mLimitMin); - inStream.Write(mLimitMax); - for (const SpringSettings &s : mLimitsSpringSettings) - s.SaveBinaryState(inStream); - for (const MotorSettings &m : mMotorSettings) - m.SaveBinaryState(inStream); -} - -void SixDOFConstraintSettings::RestoreBinaryState(StreamIn &inStream) -{ - ConstraintSettings::RestoreBinaryState(inStream); - - inStream.Read(mSpace); - inStream.Read(mPosition1); - inStream.Read(mAxisX1); - inStream.Read(mAxisY1); - inStream.Read(mPosition2); - inStream.Read(mAxisX2); - inStream.Read(mAxisY2); - inStream.Read(mMaxFriction); - inStream.Read(mSwingType); - inStream.Read(mLimitMin); - inStream.Read(mLimitMax); - for (SpringSettings &s : mLimitsSpringSettings) - s.RestoreBinaryState(inStream); - for (MotorSettings &m : mMotorSettings) - m.RestoreBinaryState(inStream); -} - -TwoBodyConstraint *SixDOFConstraintSettings::Create(Body &inBody1, Body &inBody2) const -{ - return new SixDOFConstraint(inBody1, inBody2, *this); -} - -void SixDOFConstraint::UpdateTranslationLimits() -{ - // Set to zero if the limits are inversed - for (int i = EAxis::TranslationX; i <= EAxis::TranslationZ; ++i) - if (mLimitMin[i] > mLimitMax[i]) - mLimitMin[i] = mLimitMax[i] = 0.0f; -} - -void SixDOFConstraint::UpdateRotationLimits() -{ - if (mSwingTwistConstraintPart.GetSwingType() == ESwingType::Cone) - { - // Cone swing upper limit needs to be positive - mLimitMax[EAxis::RotationY] = max(0.0f, mLimitMax[EAxis::RotationY]); - mLimitMax[EAxis::RotationZ] = max(0.0f, mLimitMax[EAxis::RotationZ]); - - // Cone swing limits only support symmetric ranges - mLimitMin[EAxis::RotationY] = -mLimitMax[EAxis::RotationY]; - mLimitMin[EAxis::RotationZ] = -mLimitMax[EAxis::RotationZ]; - } - - for (int i = EAxis::RotationX; i <= EAxis::RotationZ; ++i) - { - // Clamp to [-PI, PI] range - mLimitMin[i] = Clamp(mLimitMin[i], -JPH_PI, JPH_PI); - mLimitMax[i] = Clamp(mLimitMax[i], -JPH_PI, JPH_PI); - - // Set to zero if the limits are inversed - if (mLimitMin[i] > mLimitMax[i]) - mLimitMin[i] = mLimitMax[i] = 0.0f; - } - - // Pass limits on to constraint part - mSwingTwistConstraintPart.SetLimits(mLimitMin[EAxis::RotationX], mLimitMax[EAxis::RotationX], mLimitMin[EAxis::RotationY], mLimitMax[EAxis::RotationY], mLimitMin[EAxis::RotationZ], mLimitMax[EAxis::RotationZ]); -} - -void SixDOFConstraint::UpdateFixedFreeAxis() -{ - uint8 old_free_axis = mFreeAxis; - uint8 old_fixed_axis = mFixedAxis; - - // Cache which axis are fixed and which ones are free - mFreeAxis = 0; - mFixedAxis = 0; - for (int a = 0; a < EAxis::Num; ++a) - { - float limit = a >= EAxis::RotationX? JPH_PI : FLT_MAX; - - if (mLimitMin[a] >= mLimitMax[a]) - mFixedAxis |= 1 << a; - else if (mLimitMin[a] <= -limit && mLimitMax[a] >= limit) - mFreeAxis |= 1 << a; - } - - // On change we deactivate all constraints to reset warm starting - if (old_free_axis != mFreeAxis || old_fixed_axis != mFixedAxis) - { - for (AxisConstraintPart &c : mTranslationConstraintPart) - c.Deactivate(); - mPointConstraintPart.Deactivate(); - mSwingTwistConstraintPart.Deactivate(); - mRotationConstraintPart.Deactivate(); - for (AxisConstraintPart &c : mMotorTranslationConstraintPart) - c.Deactivate(); - for (AngleConstraintPart &c : mMotorRotationConstraintPart) - c.Deactivate(); - } -} - -SixDOFConstraint::SixDOFConstraint(Body &inBody1, Body &inBody2, const SixDOFConstraintSettings &inSettings) : - TwoBodyConstraint(inBody1, inBody2, inSettings) -{ - // Override swing type - mSwingTwistConstraintPart.SetSwingType(inSettings.mSwingType); - - // Calculate rotation needed to go from constraint space to body1 local space - Vec3 axis_z1 = inSettings.mAxisX1.Cross(inSettings.mAxisY1); - Mat44 c_to_b1(Vec4(inSettings.mAxisX1, 0), Vec4(inSettings.mAxisY1, 0), Vec4(axis_z1, 0), Vec4(0, 0, 0, 1)); - mConstraintToBody1 = c_to_b1.GetQuaternion(); - - // Calculate rotation needed to go from constraint space to body2 local space - Vec3 axis_z2 = inSettings.mAxisX2.Cross(inSettings.mAxisY2); - Mat44 c_to_b2(Vec4(inSettings.mAxisX2, 0), Vec4(inSettings.mAxisY2, 0), Vec4(axis_z2, 0), Vec4(0, 0, 0, 1)); - mConstraintToBody2 = c_to_b2.GetQuaternion(); - - if (inSettings.mSpace == EConstraintSpace::WorldSpace) - { - // If all properties were specified in world space, take them to local space now - mLocalSpacePosition1 = Vec3(inBody1.GetInverseCenterOfMassTransform() * inSettings.mPosition1); - mConstraintToBody1 = inBody1.GetRotation().Conjugated() * mConstraintToBody1; - - mLocalSpacePosition2 = Vec3(inBody2.GetInverseCenterOfMassTransform() * inSettings.mPosition2); - mConstraintToBody2 = inBody2.GetRotation().Conjugated() * mConstraintToBody2; - } - else - { - mLocalSpacePosition1 = Vec3(inSettings.mPosition1); - mLocalSpacePosition2 = Vec3(inSettings.mPosition2); - } - - // Copy translation and rotation limits - memcpy(mLimitMin, inSettings.mLimitMin, sizeof(mLimitMin)); - memcpy(mLimitMax, inSettings.mLimitMax, sizeof(mLimitMax)); - memcpy(mLimitsSpringSettings, inSettings.mLimitsSpringSettings, sizeof(mLimitsSpringSettings)); - UpdateTranslationLimits(); - UpdateRotationLimits(); - UpdateFixedFreeAxis(); - CacheHasSpringLimits(); - - // Store friction settings - memcpy(mMaxFriction, inSettings.mMaxFriction, sizeof(mMaxFriction)); - - // Store motor settings - for (int i = 0; i < EAxis::Num; ++i) - mMotorSettings[i] = inSettings.mMotorSettings[i]; - - // Cache if motors are active (motors are off initially, but we may have friction) - CacheTranslationMotorActive(); - CacheRotationMotorActive(); -} - -void SixDOFConstraint::NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM) -{ - if (mBody1->GetID() == inBodyID) - mLocalSpacePosition1 -= inDeltaCOM; - else if (mBody2->GetID() == inBodyID) - mLocalSpacePosition2 -= inDeltaCOM; -} - -void SixDOFConstraint::SetTranslationLimits(Vec3Arg inLimitMin, Vec3Arg inLimitMax) -{ - mLimitMin[EAxis::TranslationX] = inLimitMin.GetX(); - mLimitMin[EAxis::TranslationY] = inLimitMin.GetY(); - mLimitMin[EAxis::TranslationZ] = inLimitMin.GetZ(); - mLimitMax[EAxis::TranslationX] = inLimitMax.GetX(); - mLimitMax[EAxis::TranslationY] = inLimitMax.GetY(); - mLimitMax[EAxis::TranslationZ] = inLimitMax.GetZ(); - - UpdateTranslationLimits(); - UpdateFixedFreeAxis(); -} - -void SixDOFConstraint::SetRotationLimits(Vec3Arg inLimitMin, Vec3Arg inLimitMax) -{ - mLimitMin[EAxis::RotationX] = inLimitMin.GetX(); - mLimitMin[EAxis::RotationY] = inLimitMin.GetY(); - mLimitMin[EAxis::RotationZ] = inLimitMin.GetZ(); - mLimitMax[EAxis::RotationX] = inLimitMax.GetX(); - mLimitMax[EAxis::RotationY] = inLimitMax.GetY(); - mLimitMax[EAxis::RotationZ] = inLimitMax.GetZ(); - - UpdateRotationLimits(); - UpdateFixedFreeAxis(); -} - -void SixDOFConstraint::SetMaxFriction(EAxis inAxis, float inFriction) -{ - mMaxFriction[inAxis] = inFriction; - - if (inAxis >= EAxis::TranslationX && inAxis <= EAxis::TranslationZ) - CacheTranslationMotorActive(); - else - CacheRotationMotorActive(); -} - -void SixDOFConstraint::GetPositionConstraintProperties(Vec3 &outR1PlusU, Vec3 &outR2, Vec3 &outU) const -{ - RVec3 p1 = mBody1->GetCenterOfMassTransform() * mLocalSpacePosition1; - RVec3 p2 = mBody2->GetCenterOfMassTransform() * mLocalSpacePosition2; - outR1PlusU = Vec3(p2 - mBody1->GetCenterOfMassPosition()); // r1 + u = (p1 - x1) + (p2 - p1) = p2 - x1 - outR2 = Vec3(p2 - mBody2->GetCenterOfMassPosition()); - outU = Vec3(p2 - p1); -} - -Quat SixDOFConstraint::GetRotationInConstraintSpace() const -{ - // Let b1, b2 be the center of mass transform of body1 and body2 (For body1 this is mBody1->GetCenterOfMassTransform()) - // Let c1, c2 be the transform that takes a vector from constraint space to local space of body1 and body2 (For body1 this is Mat44::sRotationTranslation(mConstraintToBody1, mLocalSpacePosition1)) - // Let q be the rotation of the constraint in constraint space - // b2 takes a vector from the local space of body2 to world space - // To express this in terms of b1: b2 = b1 * c1 * q * c2^-1 - // c2^-1 goes from local body 2 space to constraint space - // q rotates the constraint - // c1 goes from constraint space to body 1 local space - // b1 goes from body 1 local space to world space - // So when the body rotations are given, q = (b1 * c1)^-1 * b2 c2 - // Or: q = (q1 * c1)^-1 * (q2 * c2) if we're only interested in rotations - return (mBody1->GetRotation() * mConstraintToBody1).Conjugated() * mBody2->GetRotation() * mConstraintToBody2; -} - -void SixDOFConstraint::CacheTranslationMotorActive() -{ - mTranslationMotorActive = mMotorState[EAxis::TranslationX] != EMotorState::Off - || mMotorState[EAxis::TranslationY] != EMotorState::Off - || mMotorState[EAxis::TranslationZ] != EMotorState::Off - || HasFriction(EAxis::TranslationX) - || HasFriction(EAxis::TranslationY) - || HasFriction(EAxis::TranslationZ); -} - -void SixDOFConstraint::CacheRotationMotorActive() -{ - mRotationMotorActive = mMotorState[EAxis::RotationX] != EMotorState::Off - || mMotorState[EAxis::RotationY] != EMotorState::Off - || mMotorState[EAxis::RotationZ] != EMotorState::Off - || HasFriction(EAxis::RotationX) - || HasFriction(EAxis::RotationY) - || HasFriction(EAxis::RotationZ); -} - -void SixDOFConstraint::CacheRotationPositionMotorActive() -{ - mRotationPositionMotorActive = 0; - for (int i = 0; i < 3; ++i) - if (mMotorState[EAxis::RotationX + i] == EMotorState::Position) - mRotationPositionMotorActive |= 1 << i; -} - -void SixDOFConstraint::CacheHasSpringLimits() -{ - mHasSpringLimits = mLimitsSpringSettings[EAxis::TranslationX].mFrequency > 0.0f - || mLimitsSpringSettings[EAxis::TranslationY].mFrequency > 0.0f - || mLimitsSpringSettings[EAxis::TranslationZ].mFrequency > 0.0f; -} - -void SixDOFConstraint::SetMotorState(EAxis inAxis, EMotorState inState) -{ - JPH_ASSERT(inState == EMotorState::Off || mMotorSettings[inAxis].IsValid()); - - if (mMotorState[inAxis] != inState) - { - mMotorState[inAxis] = inState; - - // Ensure that warm starting next frame doesn't apply any impulses (motor parts are repurposed for different modes) - if (inAxis >= EAxis::TranslationX && inAxis <= EAxis::TranslationZ) - { - mMotorTranslationConstraintPart[inAxis - EAxis::TranslationX].Deactivate(); - - CacheTranslationMotorActive(); - } - else - { - JPH_ASSERT(inAxis >= EAxis::RotationX && inAxis <= EAxis::RotationZ); - - mMotorRotationConstraintPart[inAxis - EAxis::RotationX].Deactivate(); - - CacheRotationMotorActive(); - CacheRotationPositionMotorActive(); - } - } -} - -void SixDOFConstraint::SetTargetOrientationCS(QuatArg inOrientation) -{ - Quat q_swing, q_twist; - inOrientation.GetSwingTwist(q_swing, q_twist); - - uint clamped_axis; - mSwingTwistConstraintPart.ClampSwingTwist(q_swing, q_twist, clamped_axis); - - if (clamped_axis != 0) - mTargetOrientation = q_swing * q_twist; - else - mTargetOrientation = inOrientation; -} - -void SixDOFConstraint::SetupVelocityConstraint(float inDeltaTime) -{ - // Get body rotations - Quat rotation1 = mBody1->GetRotation(); - Quat rotation2 = mBody2->GetRotation(); - - // Quaternion that rotates from body1's constraint space to world space - Quat constraint_body1_to_world = rotation1 * mConstraintToBody1; - - // Store world space axis of constraint space - Mat44 translation_axis_mat = Mat44::sRotation(constraint_body1_to_world); - for (int i = 0; i < 3; ++i) - mTranslationAxis[i] = translation_axis_mat.GetColumn3(i); - - if (IsTranslationFullyConstrained()) - { - // All translation locked: Setup point constraint - mPointConstraintPart.CalculateConstraintProperties(*mBody1, Mat44::sRotation(rotation1), mLocalSpacePosition1, *mBody2, Mat44::sRotation(rotation2), mLocalSpacePosition2); - } - else if (IsTranslationConstrained() || mTranslationMotorActive) - { - // Update world space positions (the bodies may have moved) - Vec3 r1_plus_u, r2, u; - GetPositionConstraintProperties(r1_plus_u, r2, u); - - // Setup axis constraint parts - for (int i = 0; i < 3; ++i) - { - EAxis axis = EAxis(EAxis::TranslationX + i); - - Vec3 translation_axis = mTranslationAxis[i]; - - // Calculate displacement along this axis - float d = translation_axis.Dot(u); - mDisplacement[i] = d; // Store for SolveVelocityConstraint - - // Setup limit constraint - bool constraint_active = false; - float constraint_value = 0.0f; - if (IsFixedAxis(axis)) - { - // When constraint is fixed it is always active - constraint_value = d - mLimitMin[i]; - constraint_active = true; - } - else if (!IsFreeAxis(axis)) - { - // When constraint is limited, it is only active when outside of the allowed range - if (d <= mLimitMin[i]) - { - constraint_value = d - mLimitMin[i]; - constraint_active = true; - } - else if (d >= mLimitMax[i]) - { - constraint_value = d - mLimitMax[i]; - constraint_active = true; - } - } - - if (constraint_active) - mTranslationConstraintPart[i].CalculateConstraintPropertiesWithSettings(inDeltaTime, *mBody1, r1_plus_u, *mBody2, r2, translation_axis, 0.0f, constraint_value, mLimitsSpringSettings[i]); - else - mTranslationConstraintPart[i].Deactivate(); - - // Setup motor constraint - switch (mMotorState[i]) - { - case EMotorState::Off: - if (HasFriction(axis)) - mMotorTranslationConstraintPart[i].CalculateConstraintProperties(*mBody1, r1_plus_u, *mBody2, r2, translation_axis); - else - mMotorTranslationConstraintPart[i].Deactivate(); - break; - - case EMotorState::Velocity: - mMotorTranslationConstraintPart[i].CalculateConstraintProperties(*mBody1, r1_plus_u, *mBody2, r2, translation_axis, -mTargetVelocity[i]); - break; - - case EMotorState::Position: - { - const SpringSettings &spring_settings = mMotorSettings[i].mSpringSettings; - if (spring_settings.HasStiffness()) - mMotorTranslationConstraintPart[i].CalculateConstraintPropertiesWithSettings(inDeltaTime, *mBody1, r1_plus_u, *mBody2, r2, translation_axis, 0.0f, translation_axis.Dot(u) - mTargetPosition[i], spring_settings); - else - mMotorTranslationConstraintPart[i].Deactivate(); - break; - } - } - } - } - - // Setup rotation constraints - if (IsRotationFullyConstrained()) - { - // All rotation locked: Setup rotation constraint - mRotationConstraintPart.CalculateConstraintProperties(*mBody1, Mat44::sRotation(mBody1->GetRotation()), *mBody2, Mat44::sRotation(mBody2->GetRotation())); - } - else if (IsRotationConstrained() || mRotationMotorActive) - { - // GetRotationInConstraintSpace without redoing the calculation of constraint_body1_to_world - Quat constraint_body2_to_world = mBody2->GetRotation() * mConstraintToBody2; - Quat q = constraint_body1_to_world.Conjugated() * constraint_body2_to_world; - - // Use swing twist constraint part - if (IsRotationConstrained()) - mSwingTwistConstraintPart.CalculateConstraintProperties(*mBody1, *mBody2, q, constraint_body1_to_world); - else - mSwingTwistConstraintPart.Deactivate(); - - if (mRotationMotorActive) - { - // Calculate rotation motor axis - Mat44 ws_axis = Mat44::sRotation(constraint_body2_to_world); - for (int i = 0; i < 3; ++i) - mRotationAxis[i] = ws_axis.GetColumn3(i); - - // Get target orientation along the shortest path from q - Quat target_orientation = q.Dot(mTargetOrientation) > 0.0f? mTargetOrientation : -mTargetOrientation; - - // The definition of the constraint rotation q: - // R2 * ConstraintToBody2 = R1 * ConstraintToBody1 * q (1) - // - // R2' is the rotation of body 2 when reaching the target_orientation: - // R2' * ConstraintToBody2 = R1 * ConstraintToBody1 * target_orientation (2) - // - // The difference in body 2 space: - // R2' = R2 * diff_body2 (3) - // - // We want to specify the difference in the constraint space of body 2: - // diff_body2 = ConstraintToBody2 * diff * ConstraintToBody2^* (4) - // - // Extracting R2' from 2: R2' = R1 * ConstraintToBody1 * target_orientation * ConstraintToBody2^* (5) - // Combining 3 & 4: R2' = R2 * ConstraintToBody2 * diff * ConstraintToBody2^* (6) - // Combining 1 & 6: R2' = R1 * ConstraintToBody1 * q * diff * ConstraintToBody2^* (7) - // Combining 5 & 7: R1 * ConstraintToBody1 * target_orientation * ConstraintToBody2^* = R1 * ConstraintToBody1 * q * diff * ConstraintToBody2^* - // <=> target_orientation = q * diff - // <=> diff = q^* * target_orientation - Quat diff = q.Conjugated() * target_orientation; - - // Project diff so that only rotation around axis that have a position motor are remaining - Quat projected_diff; - switch (mRotationPositionMotorActive) - { - case 0b001: - // Keep only rotation around X - projected_diff = diff.GetTwist(Vec3::sAxisX()); - break; - - case 0b010: - // Keep only rotation around Y - projected_diff = diff.GetTwist(Vec3::sAxisY()); - break; - - case 0b100: - // Keep only rotation around Z - projected_diff = diff.GetTwist(Vec3::sAxisZ()); - break; - - case 0b011: - // Remove rotation around Z - // q = swing_xy * twist_z <=> swing_xy = q * twist_z^* - projected_diff = diff * diff.GetTwist(Vec3::sAxisZ()).Conjugated(); - break; - - case 0b101: - // Remove rotation around Y - // q = swing_xz * twist_y <=> swing_xz = q * twist_y^* - projected_diff = diff * diff.GetTwist(Vec3::sAxisY()).Conjugated(); - break; - - case 0b110: - // Remove rotation around X - // q = swing_yz * twist_x <=> swing_yz = q * twist_x^* - projected_diff = diff * diff.GetTwist(Vec3::sAxisX()).Conjugated(); - break; - - case 0b111: - default: // All motors off is handled here but the results are unused - // Keep entire rotation - projected_diff = diff; - break; - } - - // Approximate error angles - // The imaginary part of a quaternion is rotation_axis * sin(angle / 2) - // If angle is small, sin(x) = x so angle[i] ~ 2.0f * rotation_axis[i] - // We'll be making small time steps, so if the angle is not small at least the sign will be correct and we'll move in the right direction - Vec3 rotation_error = -2.0f * projected_diff.GetXYZ(); - - // Setup motors - for (int i = 0; i < 3; ++i) - { - EAxis axis = EAxis(EAxis::RotationX + i); - - Vec3 rotation_axis = mRotationAxis[i]; - - switch (mMotorState[axis]) - { - case EMotorState::Off: - if (HasFriction(axis)) - mMotorRotationConstraintPart[i].CalculateConstraintProperties(*mBody1, *mBody2, rotation_axis); - else - mMotorRotationConstraintPart[i].Deactivate(); - break; - - case EMotorState::Velocity: - mMotorRotationConstraintPart[i].CalculateConstraintProperties(*mBody1, *mBody2, rotation_axis, -mTargetAngularVelocity[i]); - break; - - case EMotorState::Position: - { - const SpringSettings &spring_settings = mMotorSettings[axis].mSpringSettings; - if (spring_settings.HasStiffness()) - mMotorRotationConstraintPart[i].CalculateConstraintPropertiesWithSettings(inDeltaTime, *mBody1, *mBody2, rotation_axis, 0.0f, rotation_error[i], spring_settings); - else - mMotorRotationConstraintPart[i].Deactivate(); - break; - } - } - } - } - } -} - -void SixDOFConstraint::ResetWarmStart() -{ - for (AxisConstraintPart &c : mMotorTranslationConstraintPart) - c.Deactivate(); - for (AngleConstraintPart &c : mMotorRotationConstraintPart) - c.Deactivate(); - mRotationConstraintPart.Deactivate(); - mSwingTwistConstraintPart.Deactivate(); - mPointConstraintPart.Deactivate(); - for (AxisConstraintPart &c : mTranslationConstraintPart) - c.Deactivate(); -} - -void SixDOFConstraint::WarmStartVelocityConstraint(float inWarmStartImpulseRatio) -{ - // Warm start translation motors - if (mTranslationMotorActive) - for (int i = 0; i < 3; ++i) - if (mMotorTranslationConstraintPart[i].IsActive()) - mMotorTranslationConstraintPart[i].WarmStart(*mBody1, *mBody2, mTranslationAxis[i], inWarmStartImpulseRatio); - - // Warm start rotation motors - if (mRotationMotorActive) - for (AngleConstraintPart &c : mMotorRotationConstraintPart) - if (c.IsActive()) - c.WarmStart(*mBody1, *mBody2, inWarmStartImpulseRatio); - - // Warm start rotation constraints - if (IsRotationFullyConstrained()) - mRotationConstraintPart.WarmStart(*mBody1, *mBody2, inWarmStartImpulseRatio); - else if (IsRotationConstrained()) - mSwingTwistConstraintPart.WarmStart(*mBody1, *mBody2, inWarmStartImpulseRatio); - - // Warm start translation constraints - if (IsTranslationFullyConstrained()) - mPointConstraintPart.WarmStart(*mBody1, *mBody2, inWarmStartImpulseRatio); - else if (IsTranslationConstrained()) - for (int i = 0; i < 3; ++i) - if (mTranslationConstraintPart[i].IsActive()) - mTranslationConstraintPart[i].WarmStart(*mBody1, *mBody2, mTranslationAxis[i], inWarmStartImpulseRatio); -} - -bool SixDOFConstraint::SolveVelocityConstraint(float inDeltaTime) -{ - bool impulse = false; - - // Solve translation motor - if (mTranslationMotorActive) - for (int i = 0; i < 3; ++i) - if (mMotorTranslationConstraintPart[i].IsActive()) - switch (mMotorState[i]) - { - case EMotorState::Off: - { - // Apply friction only - float max_lambda = mMaxFriction[i] * inDeltaTime; - impulse |= mMotorTranslationConstraintPart[i].SolveVelocityConstraint(*mBody1, *mBody2, mTranslationAxis[i], -max_lambda, max_lambda); - break; - } - - case EMotorState::Velocity: - case EMotorState::Position: - // Drive motor - impulse |= mMotorTranslationConstraintPart[i].SolveVelocityConstraint(*mBody1, *mBody2, mTranslationAxis[i], inDeltaTime * mMotorSettings[i].mMinForceLimit, inDeltaTime * mMotorSettings[i].mMaxForceLimit); - break; - } - - // Solve rotation motor - if (mRotationMotorActive) - for (int i = 0; i < 3; ++i) - { - EAxis axis = EAxis(EAxis::RotationX + i); - if (mMotorRotationConstraintPart[i].IsActive()) - switch (mMotorState[axis]) - { - case EMotorState::Off: - { - // Apply friction only - float max_lambda = mMaxFriction[axis] * inDeltaTime; - impulse |= mMotorRotationConstraintPart[i].SolveVelocityConstraint(*mBody1, *mBody2, mRotationAxis[i], -max_lambda, max_lambda); - break; - } - - case EMotorState::Velocity: - case EMotorState::Position: - // Drive motor - impulse |= mMotorRotationConstraintPart[i].SolveVelocityConstraint(*mBody1, *mBody2, mRotationAxis[i], inDeltaTime * mMotorSettings[axis].mMinTorqueLimit, inDeltaTime * mMotorSettings[axis].mMaxTorqueLimit); - break; - } - } - - // Solve rotation constraint - if (IsRotationFullyConstrained()) - impulse |= mRotationConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2); - else if (IsRotationConstrained()) - impulse |= mSwingTwistConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2); - - // Solve position constraint - if (IsTranslationFullyConstrained()) - impulse |= mPointConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2); - else if (IsTranslationConstrained()) - for (int i = 0; i < 3; ++i) - if (mTranslationConstraintPart[i].IsActive()) - { - // If the axis is not fixed it must be limited (or else the constraint would not be active) - // Calculate the min and max constraint force based on on which side we're limited - float limit_min = -FLT_MAX, limit_max = FLT_MAX; - if (!IsFixedAxis(EAxis(EAxis::TranslationX + i))) - { - JPH_ASSERT(!IsFreeAxis(EAxis(EAxis::TranslationX + i))); - if (mDisplacement[i] <= mLimitMin[i]) - limit_min = 0; - else if (mDisplacement[i] >= mLimitMax[i]) - limit_max = 0; - } - - impulse |= mTranslationConstraintPart[i].SolveVelocityConstraint(*mBody1, *mBody2, mTranslationAxis[i], limit_min, limit_max); - } - - return impulse; -} - -bool SixDOFConstraint::SolvePositionConstraint(float inDeltaTime, float inBaumgarte) -{ - bool impulse = false; - - if (IsRotationFullyConstrained()) - { - // Rotation locked: Solve rotation constraint - - // Inverse of initial rotation from body 1 to body 2 in body 1 space - // Definition of initial orientation r0: q2 = q1 r0 - // Initial rotation (see: GetRotationInConstraintSpace): q2 = q1 c1 c2^-1 - // So: r0^-1 = (c1 c2^-1)^-1 = c2 * c1^-1 - Quat constraint_to_body1 = mConstraintToBody1 * Quat::sEulerAngles(GetRotationLimitsMin()); - Quat inv_initial_orientation = mConstraintToBody2 * constraint_to_body1.Conjugated(); - - // Solve rotation violations - mRotationConstraintPart.CalculateConstraintProperties(*mBody1, Mat44::sRotation(mBody1->GetRotation()), *mBody2, Mat44::sRotation(mBody2->GetRotation())); - impulse |= mRotationConstraintPart.SolvePositionConstraint(*mBody1, *mBody2, inv_initial_orientation, inBaumgarte); - } - else if (IsRotationConstrained()) - { - // Rotation partially constraint - - // Solve rotation violations - Quat q = GetRotationInConstraintSpace(); - impulse |= mSwingTwistConstraintPart.SolvePositionConstraint(*mBody1, *mBody2, q, mConstraintToBody1, mConstraintToBody2, inBaumgarte); - } - - // Solve position violations - if (IsTranslationFullyConstrained()) - { - // Translation locked: Solve point constraint - Vec3 local_space_position1 = mLocalSpacePosition1 + mConstraintToBody1 * GetTranslationLimitsMin(); - mPointConstraintPart.CalculateConstraintProperties(*mBody1, Mat44::sRotation(mBody1->GetRotation()), local_space_position1, *mBody2, Mat44::sRotation(mBody2->GetRotation()), mLocalSpacePosition2); - impulse |= mPointConstraintPart.SolvePositionConstraint(*mBody1, *mBody2, inBaumgarte); - } - else if (IsTranslationConstrained()) - { - // Translation partially locked: Solve per axis - for (int i = 0; i < 3; ++i) - if (mLimitsSpringSettings[i].mFrequency <= 0.0f) // If not soft limit - { - // Update world space positions (the bodies may have moved) - Vec3 r1_plus_u, r2, u; - GetPositionConstraintProperties(r1_plus_u, r2, u); - - // Quaternion that rotates from body1's constraint space to world space - Quat constraint_body1_to_world = mBody1->GetRotation() * mConstraintToBody1; - - // Calculate axis - Vec3 translation_axis; - switch (i) - { - case 0: translation_axis = constraint_body1_to_world.RotateAxisX(); break; - case 1: translation_axis = constraint_body1_to_world.RotateAxisY(); break; - default: JPH_ASSERT(i == 2); translation_axis = constraint_body1_to_world.RotateAxisZ(); break; - } - - // Determine position error - float error = 0.0f; - EAxis axis(EAxis(EAxis::TranslationX + i)); - if (IsFixedAxis(axis)) - error = u.Dot(translation_axis) - mLimitMin[axis]; - else if (!IsFreeAxis(axis)) - { - float displacement = u.Dot(translation_axis); - if (displacement <= mLimitMin[axis]) - error = displacement - mLimitMin[axis]; - else if (displacement >= mLimitMax[axis]) - error = displacement - mLimitMax[axis]; - } - - if (error != 0.0f) - { - // Setup axis constraint part and solve it - mTranslationConstraintPart[i].CalculateConstraintProperties(*mBody1, r1_plus_u, *mBody2, r2, translation_axis); - impulse |= mTranslationConstraintPart[i].SolvePositionConstraint(*mBody1, *mBody2, translation_axis, error, inBaumgarte); - } - } - } - - return impulse; -} - -#ifdef JPH_DEBUG_RENDERER -void SixDOFConstraint::DrawConstraint(DebugRenderer *inRenderer) const -{ - // Get constraint properties in world space - RVec3 position1 = mBody1->GetCenterOfMassTransform() * mLocalSpacePosition1; - Quat rotation1 = mBody1->GetRotation() * mConstraintToBody1; - Quat rotation2 = mBody2->GetRotation() * mConstraintToBody2; - - // Draw constraint orientation - inRenderer->DrawCoordinateSystem(RMat44::sRotationTranslation(rotation1, position1), mDrawConstraintSize); - - if ((IsRotationConstrained() || mRotationPositionMotorActive != 0) && !IsRotationFullyConstrained()) - { - // Draw current swing and twist - Quat q = GetRotationInConstraintSpace(); - Quat q_swing, q_twist; - q.GetSwingTwist(q_swing, q_twist); - inRenderer->DrawLine(position1, position1 + mDrawConstraintSize * (rotation1 * q_twist).RotateAxisY(), Color::sWhite); - inRenderer->DrawLine(position1, position1 + mDrawConstraintSize * (rotation1 * q_swing).RotateAxisX(), Color::sWhite); - } - - // Draw target rotation - Quat m_swing, m_twist; - mTargetOrientation.GetSwingTwist(m_swing, m_twist); - if (mMotorState[EAxis::RotationX] == EMotorState::Position) - inRenderer->DrawLine(position1, position1 + mDrawConstraintSize * (rotation1 * m_twist).RotateAxisY(), Color::sYellow); - if (mMotorState[EAxis::RotationY] == EMotorState::Position || mMotorState[EAxis::RotationZ] == EMotorState::Position) - inRenderer->DrawLine(position1, position1 + mDrawConstraintSize * (rotation1 * m_swing).RotateAxisX(), Color::sYellow); - - // Draw target angular velocity - Vec3 target_angular_velocity = Vec3::sZero(); - for (int i = 0; i < 3; ++i) - if (mMotorState[EAxis::RotationX + i] == EMotorState::Velocity) - target_angular_velocity.SetComponent(i, mTargetAngularVelocity[i]); - if (target_angular_velocity != Vec3::sZero()) - inRenderer->DrawArrow(position1, position1 + rotation2 * target_angular_velocity, Color::sRed, 0.1f); -} - -void SixDOFConstraint::DrawConstraintLimits(DebugRenderer *inRenderer) const -{ - // Get matrix that transforms from constraint space to world space - RMat44 constraint_body1_to_world = RMat44::sRotationTranslation(mBody1->GetRotation() * mConstraintToBody1, mBody1->GetCenterOfMassTransform() * mLocalSpacePosition1); - - // Draw limits - if (mSwingTwistConstraintPart.GetSwingType() == ESwingType::Pyramid) - inRenderer->DrawSwingPyramidLimits(constraint_body1_to_world, mLimitMin[EAxis::RotationY], mLimitMax[EAxis::RotationY], mLimitMin[EAxis::RotationZ], mLimitMax[EAxis::RotationZ], mDrawConstraintSize, Color::sGreen, DebugRenderer::ECastShadow::Off); - else - inRenderer->DrawSwingConeLimits(constraint_body1_to_world, mLimitMax[EAxis::RotationY], mLimitMax[EAxis::RotationZ], mDrawConstraintSize, Color::sGreen, DebugRenderer::ECastShadow::Off); - inRenderer->DrawPie(constraint_body1_to_world.GetTranslation(), mDrawConstraintSize, constraint_body1_to_world.GetAxisX(), constraint_body1_to_world.GetAxisY(), mLimitMin[EAxis::RotationX], mLimitMax[EAxis::RotationX], Color::sPurple, DebugRenderer::ECastShadow::Off); -} -#endif // JPH_DEBUG_RENDERER - -void SixDOFConstraint::SaveState(StateRecorder &inStream) const -{ - TwoBodyConstraint::SaveState(inStream); - - for (const AxisConstraintPart &c : mTranslationConstraintPart) - c.SaveState(inStream); - mPointConstraintPart.SaveState(inStream); - mSwingTwistConstraintPart.SaveState(inStream); - mRotationConstraintPart.SaveState(inStream); - for (const AxisConstraintPart &c : mMotorTranslationConstraintPart) - c.SaveState(inStream); - for (const AngleConstraintPart &c : mMotorRotationConstraintPart) - c.SaveState(inStream); - - inStream.Write(mMotorState); - inStream.Write(mTargetVelocity); - inStream.Write(mTargetAngularVelocity); - inStream.Write(mTargetPosition); - inStream.Write(mTargetOrientation); -} - -void SixDOFConstraint::RestoreState(StateRecorder &inStream) -{ - TwoBodyConstraint::RestoreState(inStream); - - for (AxisConstraintPart &c : mTranslationConstraintPart) - c.RestoreState(inStream); - mPointConstraintPart.RestoreState(inStream); - mSwingTwistConstraintPart.RestoreState(inStream); - mRotationConstraintPart.RestoreState(inStream); - for (AxisConstraintPart &c : mMotorTranslationConstraintPart) - c.RestoreState(inStream); - for (AngleConstraintPart &c : mMotorRotationConstraintPart) - c.RestoreState(inStream); - - inStream.Read(mMotorState); - inStream.Read(mTargetVelocity); - inStream.Read(mTargetAngularVelocity); - inStream.Read(mTargetPosition); - inStream.Read(mTargetOrientation); - - CacheTranslationMotorActive(); - CacheRotationMotorActive(); - CacheRotationPositionMotorActive(); -} - -Ref SixDOFConstraint::GetConstraintSettings() const -{ - SixDOFConstraintSettings *settings = new SixDOFConstraintSettings; - ToConstraintSettings(*settings); - settings->mSpace = EConstraintSpace::LocalToBodyCOM; - settings->mPosition1 = RVec3(mLocalSpacePosition1); - settings->mAxisX1 = mConstraintToBody1.RotateAxisX(); - settings->mAxisY1 = mConstraintToBody1.RotateAxisY(); - settings->mPosition2 = RVec3(mLocalSpacePosition2); - settings->mAxisX2 = mConstraintToBody2.RotateAxisX(); - settings->mAxisY2 = mConstraintToBody2.RotateAxisY(); - settings->mSwingType = mSwingTwistConstraintPart.GetSwingType(); - memcpy(settings->mLimitMin, mLimitMin, sizeof(mLimitMin)); - memcpy(settings->mLimitMax, mLimitMax, sizeof(mLimitMax)); - memcpy(settings->mMaxFriction, mMaxFriction, sizeof(mMaxFriction)); - for (int i = 0; i < EAxis::Num; ++i) - settings->mMotorSettings[i] = mMotorSettings[i]; - return settings; -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/SixDOFConstraint.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/SixDOFConstraint.h deleted file mode 100644 index 1bc03eceda4..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/SixDOFConstraint.h +++ /dev/null @@ -1,289 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -/// 6 Degree Of Freedom Constraint setup structure. Allows control over each of the 6 degrees of freedom. -class JPH_EXPORT SixDOFConstraintSettings final : public TwoBodyConstraintSettings -{ -public: - JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, SixDOFConstraintSettings) - - /// Constraint is split up into translation/rotation around X, Y and Z axis. - enum EAxis - { - TranslationX, - TranslationY, - TranslationZ, - - RotationX, - RotationY, - RotationZ, - - Num, - NumTranslation = TranslationZ + 1, - }; - - // See: ConstraintSettings::SaveBinaryState - virtual void SaveBinaryState(StreamOut &inStream) const override; - - /// Create an instance of this constraint - virtual TwoBodyConstraint * Create(Body &inBody1, Body &inBody2) const override; - - /// This determines in which space the constraint is setup, all properties below should be in the specified space - EConstraintSpace mSpace = EConstraintSpace::WorldSpace; - - /// Body 1 constraint reference frame (space determined by mSpace) - RVec3 mPosition1 = RVec3::sZero(); - Vec3 mAxisX1 = Vec3::sAxisX(); - Vec3 mAxisY1 = Vec3::sAxisY(); - - /// Body 2 constraint reference frame (space determined by mSpace) - RVec3 mPosition2 = RVec3::sZero(); - Vec3 mAxisX2 = Vec3::sAxisX(); - Vec3 mAxisY2 = Vec3::sAxisY(); - - /// Friction settings. - /// For translation: Max friction force in N. 0 = no friction. - /// For rotation: Max friction torque in Nm. 0 = no friction. - float mMaxFriction[EAxis::Num] = { 0, 0, 0, 0, 0, 0 }; - - /// The type of swing constraint that we want to use. - ESwingType mSwingType = ESwingType::Cone; - - /// Limits. - /// For translation: Min and max linear limits in m (0 is frame of body 1 and 2 coincide). - /// For rotation: Min and max angular limits in rad (0 is frame of body 1 and 2 coincide). See comments at Axis enum for limit ranges. - /// - /// Remove degree of freedom by setting min = FLT_MAX and max = -FLT_MAX. The constraint will be driven to 0 for this axis. - /// - /// Free movement over an axis is allowed when min = -FLT_MAX and max = FLT_MAX. - /// - /// Rotation limit around X-Axis: When limited, should be \f$\in [-\pi, \pi]\f$. Can be asymmetric around zero. - /// - /// Rotation limit around Y-Z Axis: Forms a pyramid or cone shaped limit: - /// * For pyramid, should be \f$\in [-\pi, \pi]\f$ and does not need to be symmetrical around zero. - /// * For cone should be \f$\in [0, \pi]\f$ and needs to be symmetrical around zero (min limit is assumed to be -max limit). - float mLimitMin[EAxis::Num] = { -FLT_MAX, -FLT_MAX, -FLT_MAX, -FLT_MAX, -FLT_MAX, -FLT_MAX }; - float mLimitMax[EAxis::Num] = { FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX }; - - /// When enabled, this makes the limits soft. When the constraint exceeds the limits, a spring force will pull it back. - /// Only soft translation limits are supported, soft rotation limits are not currently supported. - SpringSettings mLimitsSpringSettings[EAxis::NumTranslation]; - - /// Make axis free (unconstrained) - void MakeFreeAxis(EAxis inAxis) { mLimitMin[inAxis] = -FLT_MAX; mLimitMax[inAxis] = FLT_MAX; } - bool IsFreeAxis(EAxis inAxis) const { return mLimitMin[inAxis] == -FLT_MAX && mLimitMax[inAxis] == FLT_MAX; } - - /// Make axis fixed (fixed at value 0) - void MakeFixedAxis(EAxis inAxis) { mLimitMin[inAxis] = FLT_MAX; mLimitMax[inAxis] = -FLT_MAX; } - bool IsFixedAxis(EAxis inAxis) const { return mLimitMin[inAxis] >= mLimitMax[inAxis]; } - - /// Set a valid range for the constraint (if inMax < inMin, the axis will become fixed) - void SetLimitedAxis(EAxis inAxis, float inMin, float inMax) { mLimitMin[inAxis] = inMin; mLimitMax[inAxis] = inMax; } - - /// Motor settings for each axis - MotorSettings mMotorSettings[EAxis::Num]; - -protected: - // See: ConstraintSettings::RestoreBinaryState - virtual void RestoreBinaryState(StreamIn &inStream) override; -}; - -/// 6 Degree Of Freedom Constraint. Allows control over each of the 6 degrees of freedom. -class JPH_EXPORT SixDOFConstraint final : public TwoBodyConstraint -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Get Axis from settings class - using EAxis = SixDOFConstraintSettings::EAxis; - - /// Construct six DOF constraint - SixDOFConstraint(Body &inBody1, Body &inBody2, const SixDOFConstraintSettings &inSettings); - - /// Generic interface of a constraint - virtual EConstraintSubType GetSubType() const override { return EConstraintSubType::SixDOF; } - virtual void NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM) override; - virtual void SetupVelocityConstraint(float inDeltaTime) override; - virtual void ResetWarmStart() override; - virtual void WarmStartVelocityConstraint(float inWarmStartImpulseRatio) override; - virtual bool SolveVelocityConstraint(float inDeltaTime) override; - virtual bool SolvePositionConstraint(float inDeltaTime, float inBaumgarte) override; -#ifdef JPH_DEBUG_RENDERER - virtual void DrawConstraint(DebugRenderer *inRenderer) const override; - virtual void DrawConstraintLimits(DebugRenderer *inRenderer) const override; -#endif // JPH_DEBUG_RENDERER - virtual void SaveState(StateRecorder &inStream) const override; - virtual void RestoreState(StateRecorder &inStream) override; - virtual Ref GetConstraintSettings() const override; - - // See: TwoBodyConstraint - virtual Mat44 GetConstraintToBody1Matrix() const override { return Mat44::sRotationTranslation(mConstraintToBody1, mLocalSpacePosition1); } - virtual Mat44 GetConstraintToBody2Matrix() const override { return Mat44::sRotationTranslation(mConstraintToBody2, mLocalSpacePosition2); } - - /// Update the translation limits for this constraint - void SetTranslationLimits(Vec3Arg inLimitMin, Vec3Arg inLimitMax); - - /// Update the rotational limits for this constraint - void SetRotationLimits(Vec3Arg inLimitMin, Vec3Arg inLimitMax); - - /// Get constraint Limits - float GetLimitsMin(EAxis inAxis) const { return mLimitMin[inAxis]; } - float GetLimitsMax(EAxis inAxis) const { return mLimitMax[inAxis]; } - Vec3 GetTranslationLimitsMin() const { return Vec3::sLoadFloat3Unsafe(*reinterpret_cast(&mLimitMin[EAxis::TranslationX])); } - Vec3 GetTranslationLimitsMax() const { return Vec3::sLoadFloat3Unsafe(*reinterpret_cast(&mLimitMax[EAxis::TranslationX])); } - Vec3 GetRotationLimitsMin() const { return Vec3::sLoadFloat3Unsafe(*reinterpret_cast(&mLimitMin[EAxis::RotationX])); } - Vec3 GetRotationLimitsMax() const { return Vec3::sLoadFloat3Unsafe(*reinterpret_cast(&mLimitMax[EAxis::RotationX])); } - - /// Check which axis are fixed/free - inline bool IsFixedAxis(EAxis inAxis) const { return (mFixedAxis & (1 << inAxis)) != 0; } - inline bool IsFreeAxis(EAxis inAxis) const { return (mFreeAxis & (1 << inAxis)) != 0; } - - /// Update the limits spring settings - const SpringSettings & GetLimitsSpringSettings(EAxis inAxis) const { JPH_ASSERT(inAxis < EAxis::NumTranslation); return mLimitsSpringSettings[inAxis]; } - void SetLimitsSpringSettings(EAxis inAxis, const SpringSettings& inLimitsSpringSettings) { JPH_ASSERT(inAxis < EAxis::NumTranslation); mLimitsSpringSettings[inAxis] = inLimitsSpringSettings; CacheHasSpringLimits(); } - - /// Set the max friction for each axis - void SetMaxFriction(EAxis inAxis, float inFriction); - float GetMaxFriction(EAxis inAxis) const { return mMaxFriction[inAxis]; } - - /// Get rotation of constraint in constraint space - Quat GetRotationInConstraintSpace() const; - - /// Motor settings - MotorSettings & GetMotorSettings(EAxis inAxis) { return mMotorSettings[inAxis]; } - const MotorSettings & GetMotorSettings(EAxis inAxis) const { return mMotorSettings[inAxis]; } - - /// Motor controls. - /// Translation motors work in constraint space of body 1. - /// Rotation motors work in constraint space of body 2 (!). - void SetMotorState(EAxis inAxis, EMotorState inState); - EMotorState GetMotorState(EAxis inAxis) const { return mMotorState[inAxis]; } - - /// Set the target velocity in body 1 constraint space - Vec3 GetTargetVelocityCS() const { return mTargetVelocity; } - void SetTargetVelocityCS(Vec3Arg inVelocity) { mTargetVelocity = inVelocity; } - - /// Set the target angular velocity in body 2 constraint space (!) - void SetTargetAngularVelocityCS(Vec3Arg inAngularVelocity) { mTargetAngularVelocity = inAngularVelocity; } - Vec3 GetTargetAngularVelocityCS() const { return mTargetAngularVelocity; } - - /// Set the target position in body 1 constraint space - Vec3 GetTargetPositionCS() const { return mTargetPosition; } - void SetTargetPositionCS(Vec3Arg inPosition) { mTargetPosition = inPosition; } - - /// Set the target orientation in body 1 constraint space - void SetTargetOrientationCS(QuatArg inOrientation); - Quat GetTargetOrientationCS() const { return mTargetOrientation; } - - /// Set the target orientation in body space (R2 = R1 * inOrientation, where R1 and R2 are the world space rotations for body 1 and 2). - /// Solve: R2 * ConstraintToBody2 = R1 * ConstraintToBody1 * q (see SwingTwistConstraint::GetSwingTwist) and R2 = R1 * inOrientation for q. - void SetTargetOrientationBS(QuatArg inOrientation) { SetTargetOrientationCS(mConstraintToBody1.Conjugated() * inOrientation * mConstraintToBody2); } - - ///@name Get Lagrange multiplier from last physics update (the linear/angular impulse applied to satisfy the constraint) - inline Vec3 GetTotalLambdaPosition() const { return IsTranslationFullyConstrained()? mPointConstraintPart.GetTotalLambda() : Vec3(mTranslationConstraintPart[0].GetTotalLambda(), mTranslationConstraintPart[1].GetTotalLambda(), mTranslationConstraintPart[2].GetTotalLambda()); } - inline Vec3 GetTotalLambdaRotation() const { return IsRotationFullyConstrained()? mRotationConstraintPart.GetTotalLambda() : Vec3(mSwingTwistConstraintPart.GetTotalTwistLambda(), mSwingTwistConstraintPart.GetTotalSwingYLambda(), mSwingTwistConstraintPart.GetTotalSwingZLambda()); } - inline Vec3 GetTotalLambdaMotorTranslation() const { return Vec3(mMotorTranslationConstraintPart[0].GetTotalLambda(), mMotorTranslationConstraintPart[1].GetTotalLambda(), mMotorTranslationConstraintPart[2].GetTotalLambda()); } - inline Vec3 GetTotalLambdaMotorRotation() const { return Vec3(mMotorRotationConstraintPart[0].GetTotalLambda(), mMotorRotationConstraintPart[1].GetTotalLambda(), mMotorRotationConstraintPart[2].GetTotalLambda()); } - -private: - // Calculate properties needed for the position constraint - inline void GetPositionConstraintProperties(Vec3 &outR1PlusU, Vec3 &outR2, Vec3 &outU) const; - - // Sanitize the translation limits - inline void UpdateTranslationLimits(); - - // Propagate the rotation limits to the constraint part - inline void UpdateRotationLimits(); - - // Update the cached state of which axis are free and which ones are fixed - inline void UpdateFixedFreeAxis(); - - // Cache the state of mTranslationMotorActive - void CacheTranslationMotorActive(); - - // Cache the state of mRotationMotorActive - void CacheRotationMotorActive(); - - // Cache the state of mRotationPositionMotorActive - void CacheRotationPositionMotorActive(); - - /// Cache the state of mHasSpringLimits - void CacheHasSpringLimits(); - - // Constraint settings helper functions - inline bool IsTranslationConstrained() const { return (mFreeAxis & 0b111) != 0b111; } - inline bool IsTranslationFullyConstrained() const { return (mFixedAxis & 0b111) == 0b111 && !mHasSpringLimits; } - inline bool IsRotationConstrained() const { return (mFreeAxis & 0b111000) != 0b111000; } - inline bool IsRotationFullyConstrained() const { return (mFixedAxis & 0b111000) == 0b111000; } - inline bool HasFriction(EAxis inAxis) const { return !IsFixedAxis(inAxis) && mMaxFriction[inAxis] > 0.0f; } - - // CONFIGURATION PROPERTIES FOLLOW - - // Local space constraint positions - Vec3 mLocalSpacePosition1; - Vec3 mLocalSpacePosition2; - - // Transforms from constraint space to body space - Quat mConstraintToBody1; - Quat mConstraintToBody2; - - // Limits - uint8 mFreeAxis = 0; // Bitmask of free axis (bit 0 = TranslationX) - uint8 mFixedAxis = 0; // Bitmask of fixed axis (bit 0 = TranslationX) - bool mTranslationMotorActive = false; // If any of the translational frictions / motors are active - bool mRotationMotorActive = false; // If any of the rotational frictions / motors are active - uint8 mRotationPositionMotorActive = 0; // Bitmask of axis that have position motor active (bit 0 = RotationX) - bool mHasSpringLimits = false; // If any of the limit springs have a non-zero frequency/stiffness - float mLimitMin[EAxis::Num]; - float mLimitMax[EAxis::Num]; - SpringSettings mLimitsSpringSettings[EAxis::NumTranslation]; - - // Motor settings for each axis - MotorSettings mMotorSettings[EAxis::Num]; - - // Friction settings for each axis - float mMaxFriction[EAxis::Num]; - - // Motor controls - EMotorState mMotorState[EAxis::Num] = { EMotorState::Off, EMotorState::Off, EMotorState::Off, EMotorState::Off, EMotorState::Off, EMotorState::Off }; - Vec3 mTargetVelocity = Vec3::sZero(); - Vec3 mTargetAngularVelocity = Vec3::sZero(); - Vec3 mTargetPosition = Vec3::sZero(); - Quat mTargetOrientation = Quat::sIdentity(); - - // RUN TIME PROPERTIES FOLLOW - - // Constraint space axis in world space - Vec3 mTranslationAxis[3]; - Vec3 mRotationAxis[3]; - - // Translation displacement (valid when translation axis has a range limit) - float mDisplacement[3]; - - // Individual constraint parts for translation, or a combined point constraint part if all axis are fixed - AxisConstraintPart mTranslationConstraintPart[3]; - PointConstraintPart mPointConstraintPart; - - // Individual constraint parts for rotation or a combined constraint part if rotation is fixed - SwingTwistConstraintPart mSwingTwistConstraintPart; - RotationEulerConstraintPart mRotationConstraintPart; - - // Motor or friction constraints - AxisConstraintPart mMotorTranslationConstraintPart[3]; - AngleConstraintPart mMotorRotationConstraintPart[3]; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/SliderConstraint.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/SliderConstraint.cpp deleted file mode 100644 index 75335c32cba..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/SliderConstraint.cpp +++ /dev/null @@ -1,501 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include -#include -#include -#ifdef JPH_DEBUG_RENDERER - #include -#endif // JPH_DEBUG_RENDERER - -JPH_NAMESPACE_BEGIN - -using namespace literals; - -JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(SliderConstraintSettings) -{ - JPH_ADD_BASE_CLASS(SliderConstraintSettings, TwoBodyConstraintSettings) - - JPH_ADD_ENUM_ATTRIBUTE(SliderConstraintSettings, mSpace) - JPH_ADD_ATTRIBUTE(SliderConstraintSettings, mAutoDetectPoint) - JPH_ADD_ATTRIBUTE(SliderConstraintSettings, mPoint1) - JPH_ADD_ATTRIBUTE(SliderConstraintSettings, mSliderAxis1) - JPH_ADD_ATTRIBUTE(SliderConstraintSettings, mNormalAxis1) - JPH_ADD_ATTRIBUTE(SliderConstraintSettings, mPoint2) - JPH_ADD_ATTRIBUTE(SliderConstraintSettings, mSliderAxis2) - JPH_ADD_ATTRIBUTE(SliderConstraintSettings, mNormalAxis2) - JPH_ADD_ATTRIBUTE(SliderConstraintSettings, mLimitsMin) - JPH_ADD_ATTRIBUTE(SliderConstraintSettings, mLimitsMax) - JPH_ADD_ENUM_ATTRIBUTE_WITH_ALIAS(SliderConstraintSettings, mLimitsSpringSettings.mMode, "mSpringMode") - JPH_ADD_ATTRIBUTE_WITH_ALIAS(SliderConstraintSettings, mLimitsSpringSettings.mFrequency, "mFrequency") // Renaming attributes to stay compatible with old versions of the library - JPH_ADD_ATTRIBUTE_WITH_ALIAS(SliderConstraintSettings, mLimitsSpringSettings.mDamping, "mDamping") - JPH_ADD_ATTRIBUTE(SliderConstraintSettings, mMaxFrictionForce) - JPH_ADD_ATTRIBUTE(SliderConstraintSettings, mMotorSettings) -} - -void SliderConstraintSettings::SetSliderAxis(Vec3Arg inSliderAxis) -{ - JPH_ASSERT(mSpace == EConstraintSpace::WorldSpace); - - mSliderAxis1 = mSliderAxis2 = inSliderAxis; - mNormalAxis1 = mNormalAxis2 = inSliderAxis.GetNormalizedPerpendicular(); -} - -void SliderConstraintSettings::SaveBinaryState(StreamOut &inStream) const -{ - ConstraintSettings::SaveBinaryState(inStream); - - inStream.Write(mSpace); - inStream.Write(mAutoDetectPoint); - inStream.Write(mPoint1); - inStream.Write(mSliderAxis1); - inStream.Write(mNormalAxis1); - inStream.Write(mPoint2); - inStream.Write(mSliderAxis2); - inStream.Write(mNormalAxis2); - inStream.Write(mLimitsMin); - inStream.Write(mLimitsMax); - inStream.Write(mMaxFrictionForce); - mLimitsSpringSettings.SaveBinaryState(inStream); - mMotorSettings.SaveBinaryState(inStream); -} - -void SliderConstraintSettings::RestoreBinaryState(StreamIn &inStream) -{ - ConstraintSettings::RestoreBinaryState(inStream); - - inStream.Read(mSpace); - inStream.Read(mAutoDetectPoint); - inStream.Read(mPoint1); - inStream.Read(mSliderAxis1); - inStream.Read(mNormalAxis1); - inStream.Read(mPoint2); - inStream.Read(mSliderAxis2); - inStream.Read(mNormalAxis2); - inStream.Read(mLimitsMin); - inStream.Read(mLimitsMax); - inStream.Read(mMaxFrictionForce); - mLimitsSpringSettings.RestoreBinaryState(inStream); - mMotorSettings.RestoreBinaryState(inStream); -} - -TwoBodyConstraint *SliderConstraintSettings::Create(Body &inBody1, Body &inBody2) const -{ - return new SliderConstraint(inBody1, inBody2, *this); -} - -SliderConstraint::SliderConstraint(Body &inBody1, Body &inBody2, const SliderConstraintSettings &inSettings) : - TwoBodyConstraint(inBody1, inBody2, inSettings), - mMaxFrictionForce(inSettings.mMaxFrictionForce), - mMotorSettings(inSettings.mMotorSettings) -{ - // Store inverse of initial rotation from body 1 to body 2 in body 1 space - mInvInitialOrientation = RotationEulerConstraintPart::sGetInvInitialOrientationXY(inSettings.mSliderAxis1, inSettings.mNormalAxis1, inSettings.mSliderAxis2, inSettings.mNormalAxis2); - - if (inSettings.mSpace == EConstraintSpace::WorldSpace) - { - RMat44 inv_transform1 = inBody1.GetInverseCenterOfMassTransform(); - RMat44 inv_transform2 = inBody2.GetInverseCenterOfMassTransform(); - - if (inSettings.mAutoDetectPoint) - { - // Determine anchor point: If any of the bodies can never be dynamic use the other body as anchor point - RVec3 anchor; - if (!inBody1.CanBeKinematicOrDynamic()) - anchor = inBody2.GetCenterOfMassPosition(); - else if (!inBody2.CanBeKinematicOrDynamic()) - anchor = inBody1.GetCenterOfMassPosition(); - else - { - // Otherwise use weighted anchor point towards the lightest body - Real inv_m1 = Real(inBody1.GetMotionPropertiesUnchecked()->GetInverseMassUnchecked()); - Real inv_m2 = Real(inBody2.GetMotionPropertiesUnchecked()->GetInverseMassUnchecked()); - Real total_inv_mass = inv_m1 + inv_m2; - if (total_inv_mass != 0.0_r) - anchor = (inv_m1 * inBody1.GetCenterOfMassPosition() + inv_m2 * inBody2.GetCenterOfMassPosition()) / total_inv_mass; - else - anchor = inBody1.GetCenterOfMassPosition(); - } - - // Store local positions - mLocalSpacePosition1 = Vec3(inv_transform1 * anchor); - mLocalSpacePosition2 = Vec3(inv_transform2 * anchor); - } - else - { - // Store local positions - mLocalSpacePosition1 = Vec3(inv_transform1 * inSettings.mPoint1); - mLocalSpacePosition2 = Vec3(inv_transform2 * inSettings.mPoint2); - } - - // If all properties were specified in world space, take them to local space now - mLocalSpaceSliderAxis1 = inv_transform1.Multiply3x3(inSettings.mSliderAxis1).Normalized(); - mLocalSpaceNormal1 = inv_transform1.Multiply3x3(inSettings.mNormalAxis1).Normalized(); - - // Constraints were specified in world space, so we should have replaced c1 with q10^-1 c1 and c2 with q20^-1 c2 - // => r0^-1 = (q20^-1 c2) (q10^-1 c1)^1 = q20^-1 (c2 c1^-1) q10 - mInvInitialOrientation = inBody2.GetRotation().Conjugated() * mInvInitialOrientation * inBody1.GetRotation(); - } - else - { - // Store local positions - mLocalSpacePosition1 = Vec3(inSettings.mPoint1); - mLocalSpacePosition2 = Vec3(inSettings.mPoint2); - - // Store local space axis - mLocalSpaceSliderAxis1 = inSettings.mSliderAxis1; - mLocalSpaceNormal1 = inSettings.mNormalAxis1; - } - - // Calculate 2nd local space normal - mLocalSpaceNormal2 = mLocalSpaceSliderAxis1.Cross(mLocalSpaceNormal1); - - // Store limits - JPH_ASSERT(inSettings.mLimitsMin != inSettings.mLimitsMax || inSettings.mLimitsSpringSettings.mFrequency > 0.0f, "Better use a fixed constraint"); - SetLimits(inSettings.mLimitsMin, inSettings.mLimitsMax); - - // Store spring settings - SetLimitsSpringSettings(inSettings.mLimitsSpringSettings); -} - -void SliderConstraint::NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM) -{ - if (mBody1->GetID() == inBodyID) - mLocalSpacePosition1 -= inDeltaCOM; - else if (mBody2->GetID() == inBodyID) - mLocalSpacePosition2 -= inDeltaCOM; -} - -float SliderConstraint::GetCurrentPosition() const -{ - // See: CalculateR1R2U and CalculateSlidingAxisAndPosition - Vec3 r1 = mBody1->GetRotation() * mLocalSpacePosition1; - Vec3 r2 = mBody2->GetRotation() * mLocalSpacePosition2; - Vec3 u = Vec3(mBody2->GetCenterOfMassPosition() - mBody1->GetCenterOfMassPosition()) + r2 - r1; - return u.Dot(mBody1->GetRotation() * mLocalSpaceSliderAxis1); -} - -void SliderConstraint::SetLimits(float inLimitsMin, float inLimitsMax) -{ - JPH_ASSERT(inLimitsMin <= 0.0f); - JPH_ASSERT(inLimitsMax >= 0.0f); - mLimitsMin = inLimitsMin; - mLimitsMax = inLimitsMax; - mHasLimits = mLimitsMin != -FLT_MAX || mLimitsMax != FLT_MAX; -} - -void SliderConstraint::CalculateR1R2U(Mat44Arg inRotation1, Mat44Arg inRotation2) -{ - // Calculate points relative to body - mR1 = inRotation1 * mLocalSpacePosition1; - mR2 = inRotation2 * mLocalSpacePosition2; - - // Calculate X2 + R2 - X1 - R1 - mU = Vec3(mBody2->GetCenterOfMassPosition() - mBody1->GetCenterOfMassPosition()) + mR2 - mR1; -} - -void SliderConstraint::CalculatePositionConstraintProperties(Mat44Arg inRotation1, Mat44Arg inRotation2) -{ - // Calculate world space normals - mN1 = inRotation1 * mLocalSpaceNormal1; - mN2 = inRotation1 * mLocalSpaceNormal2; - - mPositionConstraintPart.CalculateConstraintProperties(*mBody1, inRotation1, mR1 + mU, *mBody2, inRotation2, mR2, mN1, mN2); -} - -void SliderConstraint::CalculateSlidingAxisAndPosition(Mat44Arg inRotation1) -{ - if (mHasLimits || mMotorState != EMotorState::Off || mMaxFrictionForce > 0.0f) - { - // Calculate world space slider axis - mWorldSpaceSliderAxis = inRotation1 * mLocalSpaceSliderAxis1; - - // Calculate slide distance along axis - mD = mU.Dot(mWorldSpaceSliderAxis); - } -} - -void SliderConstraint::CalculatePositionLimitsConstraintProperties(float inDeltaTime) -{ - // Check if distance is within limits - bool below_min = mD <= mLimitsMin; - if (mHasLimits && (below_min || mD >= mLimitsMax)) - mPositionLimitsConstraintPart.CalculateConstraintPropertiesWithSettings(inDeltaTime, *mBody1, mR1 + mU, *mBody2, mR2, mWorldSpaceSliderAxis, 0.0f, mD - (below_min? mLimitsMin : mLimitsMax), mLimitsSpringSettings); - else - mPositionLimitsConstraintPart.Deactivate(); -} - -void SliderConstraint::CalculateMotorConstraintProperties(float inDeltaTime) -{ - switch (mMotorState) - { - case EMotorState::Off: - if (mMaxFrictionForce > 0.0f) - mMotorConstraintPart.CalculateConstraintProperties(*mBody1, mR1 + mU, *mBody2, mR2, mWorldSpaceSliderAxis); - else - mMotorConstraintPart.Deactivate(); - break; - - case EMotorState::Velocity: - mMotorConstraintPart.CalculateConstraintProperties(*mBody1, mR1 + mU, *mBody2, mR2, mWorldSpaceSliderAxis, -mTargetVelocity); - break; - - case EMotorState::Position: - if (mMotorSettings.mSpringSettings.HasStiffness()) - mMotorConstraintPart.CalculateConstraintPropertiesWithSettings(inDeltaTime, *mBody1, mR1 + mU, *mBody2, mR2, mWorldSpaceSliderAxis, 0.0f, mD - mTargetPosition, mMotorSettings.mSpringSettings); - else - mMotorConstraintPart.Deactivate(); - break; - } -} - -void SliderConstraint::SetupVelocityConstraint(float inDeltaTime) -{ - // Calculate constraint properties that are constant while bodies don't move - Mat44 rotation1 = Mat44::sRotation(mBody1->GetRotation()); - Mat44 rotation2 = Mat44::sRotation(mBody2->GetRotation()); - CalculateR1R2U(rotation1, rotation2); - CalculatePositionConstraintProperties(rotation1, rotation2); - mRotationConstraintPart.CalculateConstraintProperties(*mBody1, rotation1, *mBody2, rotation2); - CalculateSlidingAxisAndPosition(rotation1); - CalculatePositionLimitsConstraintProperties(inDeltaTime); - CalculateMotorConstraintProperties(inDeltaTime); -} - -void SliderConstraint::ResetWarmStart() -{ - mMotorConstraintPart.Deactivate(); - mPositionConstraintPart.Deactivate(); - mRotationConstraintPart.Deactivate(); - mPositionLimitsConstraintPart.Deactivate(); -} - -void SliderConstraint::WarmStartVelocityConstraint(float inWarmStartImpulseRatio) -{ - // Warm starting: Apply previous frame impulse - mMotorConstraintPart.WarmStart(*mBody1, *mBody2, mWorldSpaceSliderAxis, inWarmStartImpulseRatio); - mPositionConstraintPart.WarmStart(*mBody1, *mBody2, mN1, mN2, inWarmStartImpulseRatio); - mRotationConstraintPart.WarmStart(*mBody1, *mBody2, inWarmStartImpulseRatio); - mPositionLimitsConstraintPart.WarmStart(*mBody1, *mBody2, mWorldSpaceSliderAxis, inWarmStartImpulseRatio); -} - -bool SliderConstraint::SolveVelocityConstraint(float inDeltaTime) -{ - // Solve motor - bool motor = false; - if (mMotorConstraintPart.IsActive()) - { - switch (mMotorState) - { - case EMotorState::Off: - { - float max_lambda = mMaxFrictionForce * inDeltaTime; - motor = mMotorConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2, mWorldSpaceSliderAxis, -max_lambda, max_lambda); - break; - } - - case EMotorState::Velocity: - case EMotorState::Position: - motor = mMotorConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2, mWorldSpaceSliderAxis, inDeltaTime * mMotorSettings.mMinForceLimit, inDeltaTime * mMotorSettings.mMaxForceLimit); - break; - } - } - - // Solve position constraint along 2 axis - bool pos = mPositionConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2, mN1, mN2); - - // Solve rotation constraint - bool rot = mRotationConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2); - - // Solve limits along slider axis - bool limit = false; - if (mPositionLimitsConstraintPart.IsActive()) - { - float min_lambda, max_lambda; - if (mLimitsMin == mLimitsMax) - { - min_lambda = -FLT_MAX; - max_lambda = FLT_MAX; - } - else if (mD <= mLimitsMin) - { - min_lambda = 0.0f; - max_lambda = FLT_MAX; - } - else - { - min_lambda = -FLT_MAX; - max_lambda = 0.0f; - } - limit = mPositionLimitsConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2, mWorldSpaceSliderAxis, min_lambda, max_lambda); - } - - return motor || pos || rot || limit; -} - -bool SliderConstraint::SolvePositionConstraint(float inDeltaTime, float inBaumgarte) -{ - // Motor operates on velocities only, don't call SolvePositionConstraint - - // Solve position constraint along 2 axis - Mat44 rotation1 = Mat44::sRotation(mBody1->GetRotation()); - Mat44 rotation2 = Mat44::sRotation(mBody2->GetRotation()); - CalculateR1R2U(rotation1, rotation2); - CalculatePositionConstraintProperties(rotation1, rotation2); - bool pos = mPositionConstraintPart.SolvePositionConstraint(*mBody1, *mBody2, mU, mN1, mN2, inBaumgarte); - - // Solve rotation constraint - mRotationConstraintPart.CalculateConstraintProperties(*mBody1, Mat44::sRotation(mBody1->GetRotation()), *mBody2, Mat44::sRotation(mBody2->GetRotation())); - bool rot = mRotationConstraintPart.SolvePositionConstraint(*mBody1, *mBody2, mInvInitialOrientation, inBaumgarte); - - // Solve limits along slider axis - bool limit = false; - if (mHasLimits && mLimitsSpringSettings.mFrequency <= 0.0f) - { - rotation1 = Mat44::sRotation(mBody1->GetRotation()); - rotation2 = Mat44::sRotation(mBody2->GetRotation()); - CalculateR1R2U(rotation1, rotation2); - CalculateSlidingAxisAndPosition(rotation1); - CalculatePositionLimitsConstraintProperties(inDeltaTime); - if (mPositionLimitsConstraintPart.IsActive()) - { - if (mD <= mLimitsMin) - limit = mPositionLimitsConstraintPart.SolvePositionConstraint(*mBody1, *mBody2, mWorldSpaceSliderAxis, mD - mLimitsMin, inBaumgarte); - else - { - JPH_ASSERT(mD >= mLimitsMax); - limit = mPositionLimitsConstraintPart.SolvePositionConstraint(*mBody1, *mBody2, mWorldSpaceSliderAxis, mD - mLimitsMax, inBaumgarte); - } - } - } - - return pos || rot || limit; -} - -#ifdef JPH_DEBUG_RENDERER -void SliderConstraint::DrawConstraint(DebugRenderer *inRenderer) const -{ - RMat44 transform1 = mBody1->GetCenterOfMassTransform(); - RMat44 transform2 = mBody2->GetCenterOfMassTransform(); - - // Transform the local positions into world space - Vec3 slider_axis = transform1.Multiply3x3(mLocalSpaceSliderAxis1); - RVec3 position1 = transform1 * mLocalSpacePosition1; - RVec3 position2 = transform2 * mLocalSpacePosition2; - - // Draw constraint - inRenderer->DrawMarker(position1, Color::sRed, 0.1f); - inRenderer->DrawMarker(position2, Color::sGreen, 0.1f); - inRenderer->DrawLine(position1, position2, Color::sGreen); - - // Draw motor - switch (mMotorState) - { - case EMotorState::Position: - inRenderer->DrawMarker(position1 + mTargetPosition * slider_axis, Color::sYellow, 1.0f); - break; - - case EMotorState::Velocity: - { - Vec3 cur_vel = (mBody2->GetLinearVelocity() - mBody1->GetLinearVelocity()).Dot(slider_axis) * slider_axis; - inRenderer->DrawLine(position2, position2 + cur_vel, Color::sBlue); - inRenderer->DrawArrow(position2 + cur_vel, position2 + mTargetVelocity * slider_axis, Color::sRed, 0.1f); - break; - } - - case EMotorState::Off: - break; - } -} - -void SliderConstraint::DrawConstraintLimits(DebugRenderer *inRenderer) const -{ - if (mHasLimits) - { - RMat44 transform1 = mBody1->GetCenterOfMassTransform(); - RMat44 transform2 = mBody2->GetCenterOfMassTransform(); - - // Transform the local positions into world space - Vec3 slider_axis = transform1.Multiply3x3(mLocalSpaceSliderAxis1); - RVec3 position1 = transform1 * mLocalSpacePosition1; - RVec3 position2 = transform2 * mLocalSpacePosition2; - - // Calculate the limits in world space - RVec3 limits_min = position1 + mLimitsMin * slider_axis; - RVec3 limits_max = position1 + mLimitsMax * slider_axis; - - inRenderer->DrawLine(limits_min, position1, Color::sWhite); - inRenderer->DrawLine(position2, limits_max, Color::sWhite); - - inRenderer->DrawMarker(limits_min, Color::sWhite, 0.1f); - inRenderer->DrawMarker(limits_max, Color::sWhite, 0.1f); - } -} -#endif // JPH_DEBUG_RENDERER - -void SliderConstraint::SaveState(StateRecorder &inStream) const -{ - TwoBodyConstraint::SaveState(inStream); - - mMotorConstraintPart.SaveState(inStream); - mPositionConstraintPart.SaveState(inStream); - mRotationConstraintPart.SaveState(inStream); - mPositionLimitsConstraintPart.SaveState(inStream); - - inStream.Write(mMotorState); - inStream.Write(mTargetVelocity); - inStream.Write(mTargetPosition); -} - -void SliderConstraint::RestoreState(StateRecorder &inStream) -{ - TwoBodyConstraint::RestoreState(inStream); - - mMotorConstraintPart.RestoreState(inStream); - mPositionConstraintPart.RestoreState(inStream); - mRotationConstraintPart.RestoreState(inStream); - mPositionLimitsConstraintPart.RestoreState(inStream); - - inStream.Read(mMotorState); - inStream.Read(mTargetVelocity); - inStream.Read(mTargetPosition); -} - -Ref SliderConstraint::GetConstraintSettings() const -{ - SliderConstraintSettings *settings = new SliderConstraintSettings; - ToConstraintSettings(*settings); - settings->mSpace = EConstraintSpace::LocalToBodyCOM; - settings->mPoint1 = RVec3(mLocalSpacePosition1); - settings->mSliderAxis1 = mLocalSpaceSliderAxis1; - settings->mNormalAxis1 = mLocalSpaceNormal1; - settings->mPoint2 = RVec3(mLocalSpacePosition2); - Mat44 inv_initial_rotation = Mat44::sRotation(mInvInitialOrientation); - settings->mSliderAxis2 = inv_initial_rotation.Multiply3x3(mLocalSpaceSliderAxis1); - settings->mNormalAxis2 = inv_initial_rotation.Multiply3x3(mLocalSpaceNormal1); - settings->mLimitsMin = mLimitsMin; - settings->mLimitsMax = mLimitsMax; - settings->mLimitsSpringSettings = mLimitsSpringSettings; - settings->mMaxFrictionForce = mMaxFrictionForce; - settings->mMotorSettings = mMotorSettings; - return settings; -} - -Mat44 SliderConstraint::GetConstraintToBody1Matrix() const -{ - return Mat44(Vec4(mLocalSpaceSliderAxis1, 0), Vec4(mLocalSpaceNormal1, 0), Vec4(mLocalSpaceNormal2, 0), Vec4(mLocalSpacePosition1, 1)); -} - -Mat44 SliderConstraint::GetConstraintToBody2Matrix() const -{ - Mat44 mat = Mat44::sRotation(mInvInitialOrientation).Multiply3x3(Mat44(Vec4(mLocalSpaceSliderAxis1, 0), Vec4(mLocalSpaceNormal1, 0), Vec4(mLocalSpaceNormal2, 0), Vec4(0, 0, 0, 1))); - mat.SetTranslation(mLocalSpacePosition2); - return mat; -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/SliderConstraint.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/SliderConstraint.h deleted file mode 100644 index 2838517f256..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/SliderConstraint.h +++ /dev/null @@ -1,198 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -/// Slider constraint settings, used to create a slider constraint -class JPH_EXPORT SliderConstraintSettings final : public TwoBodyConstraintSettings -{ -public: - JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, SliderConstraintSettings) - - // See: ConstraintSettings::SaveBinaryState - virtual void SaveBinaryState(StreamOut &inStream) const override; - - /// Create an instance of this constraint. - /// Note that the rotation constraint will be solved from body 1. This means that if body 1 and body 2 have different masses / inertias (kinematic body = infinite mass / inertia), body 1 should be the heaviest body. - virtual TwoBodyConstraint * Create(Body &inBody1, Body &inBody2) const override; - - /// Simple way of setting the slider and normal axis in world space (assumes the bodies are already oriented correctly when the constraint is created) - void SetSliderAxis(Vec3Arg inSliderAxis); - - /// This determines in which space the constraint is setup, all properties below should be in the specified space - EConstraintSpace mSpace = EConstraintSpace::WorldSpace; - - /// When mSpace is WorldSpace mPoint1 and mPoint2 can be automatically calculated based on the positions of the bodies when the constraint is created (the current relative position/orientation is chosen as the '0' position). Set this to false if you want to supply the attachment points yourself. - bool mAutoDetectPoint = false; - - /// Body 1 constraint reference frame (space determined by mSpace). - /// Slider axis is the axis along which movement is possible (direction), normal axis is a perpendicular vector to define the frame. - RVec3 mPoint1 = RVec3::sZero(); - Vec3 mSliderAxis1 = Vec3::sAxisX(); - Vec3 mNormalAxis1 = Vec3::sAxisY(); - - /// Body 2 constraint reference frame (space determined by mSpace) - RVec3 mPoint2 = RVec3::sZero(); - Vec3 mSliderAxis2 = Vec3::sAxisX(); - Vec3 mNormalAxis2 = Vec3::sAxisY(); - - /// When the bodies move so that mPoint1 coincides with mPoint2 the slider position is defined to be 0, movement will be limited between [mLimitsMin, mLimitsMax] where mLimitsMin e [-inf, 0] and mLimitsMax e [0, inf] - float mLimitsMin = -FLT_MAX; - float mLimitsMax = FLT_MAX; - - /// When enabled, this makes the limits soft. When the constraint exceeds the limits, a spring force will pull it back. - SpringSettings mLimitsSpringSettings; - - /// Maximum amount of friction force to apply (N) when not driven by a motor. - float mMaxFrictionForce = 0.0f; - - /// In case the constraint is powered, this determines the motor settings around the sliding axis - MotorSettings mMotorSettings; - -protected: - // See: ConstraintSettings::RestoreBinaryState - virtual void RestoreBinaryState(StreamIn &inStream) override; -}; - -/// A slider constraint allows movement in only 1 axis (and no rotation). Also known as a prismatic constraint. -class JPH_EXPORT SliderConstraint final : public TwoBodyConstraint -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Construct slider constraint - SliderConstraint(Body &inBody1, Body &inBody2, const SliderConstraintSettings &inSettings); - - // Generic interface of a constraint - virtual EConstraintSubType GetSubType() const override { return EConstraintSubType::Slider; } - virtual void NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM) override; - virtual void SetupVelocityConstraint(float inDeltaTime) override; - virtual void ResetWarmStart() override; - virtual void WarmStartVelocityConstraint(float inWarmStartImpulseRatio) override; - virtual bool SolveVelocityConstraint(float inDeltaTime) override; - virtual bool SolvePositionConstraint(float inDeltaTime, float inBaumgarte) override; -#ifdef JPH_DEBUG_RENDERER - virtual void DrawConstraint(DebugRenderer *inRenderer) const override; - virtual void DrawConstraintLimits(DebugRenderer *inRenderer) const override; -#endif // JPH_DEBUG_RENDERER - virtual void SaveState(StateRecorder &inStream) const override; - virtual void RestoreState(StateRecorder &inStream) override; - virtual Ref GetConstraintSettings() const override; - - // See: TwoBodyConstraint - virtual Mat44 GetConstraintToBody1Matrix() const override; - virtual Mat44 GetConstraintToBody2Matrix() const override; - - /// Get the current distance from the rest position - float GetCurrentPosition() const; - - /// Friction control - void SetMaxFrictionForce(float inFrictionForce) { mMaxFrictionForce = inFrictionForce; } - float GetMaxFrictionForce() const { return mMaxFrictionForce; } - - /// Motor settings - MotorSettings & GetMotorSettings() { return mMotorSettings; } - const MotorSettings & GetMotorSettings() const { return mMotorSettings; } - - // Motor controls - void SetMotorState(EMotorState inState) { JPH_ASSERT(inState == EMotorState::Off || mMotorSettings.IsValid()); mMotorState = inState; } - EMotorState GetMotorState() const { return mMotorState; } - void SetTargetVelocity(float inVelocity) { mTargetVelocity = inVelocity; } - float GetTargetVelocity() const { return mTargetVelocity; } - void SetTargetPosition(float inPosition) { mTargetPosition = mHasLimits? Clamp(inPosition, mLimitsMin, mLimitsMax) : inPosition; } - float GetTargetPosition() const { return mTargetPosition; } - - /// Update the limits of the slider constraint (see SliderConstraintSettings) - void SetLimits(float inLimitsMin, float inLimitsMax); - float GetLimitsMin() const { return mLimitsMin; } - float GetLimitsMax() const { return mLimitsMax; } - bool HasLimits() const { return mHasLimits; } - - /// Update the limits spring settings - const SpringSettings & GetLimitsSpringSettings() const { return mLimitsSpringSettings; } - SpringSettings & GetLimitsSpringSettings() { return mLimitsSpringSettings; } - void SetLimitsSpringSettings(const SpringSettings &inLimitsSpringSettings) { mLimitsSpringSettings = inLimitsSpringSettings; } - - ///@name Get Lagrange multiplier from last physics update (the linear/angular impulse applied to satisfy the constraint) - inline Vector<2> GetTotalLambdaPosition() const { return mPositionConstraintPart.GetTotalLambda(); } - inline float GetTotalLambdaPositionLimits() const { return mPositionLimitsConstraintPart.GetTotalLambda(); } - inline Vec3 GetTotalLambdaRotation() const { return mRotationConstraintPart.GetTotalLambda(); } - inline float GetTotalLambdaMotor() const { return mMotorConstraintPart.GetTotalLambda(); } - -private: - // Internal helper function to calculate the values below - void CalculateR1R2U(Mat44Arg inRotation1, Mat44Arg inRotation2); - void CalculateSlidingAxisAndPosition(Mat44Arg inRotation1); - void CalculatePositionConstraintProperties(Mat44Arg inRotation1, Mat44Arg inRotation2); - void CalculatePositionLimitsConstraintProperties(float inDeltaTime); - void CalculateMotorConstraintProperties(float inDeltaTime); - - // CONFIGURATION PROPERTIES FOLLOW - - // Local space constraint positions - Vec3 mLocalSpacePosition1; - Vec3 mLocalSpacePosition2; - - // Local space sliding direction - Vec3 mLocalSpaceSliderAxis1; - - // Local space normals to the sliding direction (in body 1 space) - Vec3 mLocalSpaceNormal1; - Vec3 mLocalSpaceNormal2; - - // Inverse of initial rotation from body 1 to body 2 in body 1 space - Quat mInvInitialOrientation; - - // Slider limits - bool mHasLimits; - float mLimitsMin; - float mLimitsMax; - - // Soft constraint limits - SpringSettings mLimitsSpringSettings; - - // Friction - float mMaxFrictionForce; - - // Motor controls - MotorSettings mMotorSettings; - EMotorState mMotorState = EMotorState::Off; - float mTargetVelocity = 0.0f; - float mTargetPosition = 0.0f; - - // RUN TIME PROPERTIES FOLLOW - - // Positions where the point constraint acts on (middle point between center of masses) - Vec3 mR1; - Vec3 mR2; - - // X2 + R2 - X1 - R1 - Vec3 mU; - - // World space sliding direction - Vec3 mWorldSpaceSliderAxis; - - // Normals to the slider axis - Vec3 mN1; - Vec3 mN2; - - // Distance along the slide axis - float mD = 0.0f; - - // The constraint parts - DualAxisConstraintPart mPositionConstraintPart; - RotationEulerConstraintPart mRotationConstraintPart; - AxisConstraintPart mPositionLimitsConstraintPart; - AxisConstraintPart mMotorConstraintPart; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/SpringSettings.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/SpringSettings.cpp deleted file mode 100644 index c2c32400fed..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/SpringSettings.cpp +++ /dev/null @@ -1,35 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2023 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SpringSettings) -{ - JPH_ADD_ENUM_ATTRIBUTE(SpringSettings, mMode) - JPH_ADD_ATTRIBUTE(SpringSettings, mFrequency) - JPH_ADD_ATTRIBUTE(SpringSettings, mDamping) -} - -void SpringSettings::SaveBinaryState(StreamOut &inStream) const -{ - inStream.Write(mMode); - inStream.Write(mFrequency); - inStream.Write(mDamping); -} - -void SpringSettings::RestoreBinaryState(StreamIn &inStream) -{ - inStream.Read(mMode); - inStream.Read(mFrequency); - inStream.Read(mDamping); -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/SpringSettings.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/SpringSettings.h deleted file mode 100644 index b2f6b7e2430..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/SpringSettings.h +++ /dev/null @@ -1,70 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2023 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -class StreamIn; -class StreamOut; - -/// Enum used by constraints to specify how the spring is defined -enum class ESpringMode : uint8 -{ - FrequencyAndDamping, ///< Frequency and damping are specified - StiffnessAndDamping, ///< Stiffness and damping are specified -}; - -/// Settings for a linear or angular spring -class JPH_EXPORT SpringSettings -{ -public: - JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, SpringSettings) - - /// Constructor - SpringSettings() = default; - SpringSettings(const SpringSettings &) = default; - SpringSettings & operator = (const SpringSettings &) = default; - SpringSettings(ESpringMode inMode, float inFrequencyOrStiffness, float inDamping) : mMode(inMode), mFrequency(inFrequencyOrStiffness), mDamping(inDamping) { } - - /// Saves the contents of the spring settings in binary form to inStream. - void SaveBinaryState(StreamOut &inStream) const; - - /// Restores contents from the binary stream inStream. - void RestoreBinaryState(StreamIn &inStream); - - /// Check if the spring has a valid frequency / stiffness, if not the spring will be hard - inline bool HasStiffness() const { return mFrequency > 0.0f; } - - /// Selects the way in which the spring is defined - /// If the mode is StiffnessAndDamping then mFrequency becomes the stiffness (k) and mDamping becomes the damping ratio (c) in the spring equation F = -k * x - c * v. Otherwise the properties are as documented. - ESpringMode mMode = ESpringMode::FrequencyAndDamping; - - union - { - /// Valid when mSpringMode = ESpringMode::FrequencyAndDamping. - /// If mFrequency > 0 the constraint will be soft and mFrequency specifies the oscillation frequency in Hz. - /// If mFrequency <= 0, mDamping is ignored and the constraint will have hard limits (as hard as the time step / the number of velocity / position solver steps allows). - float mFrequency = 0.0f; - - /// Valid when mSpringMode = ESpringMode::StiffnessAndDamping. - /// If mStiffness > 0 the constraint will be soft and mStiffness specifies the stiffness (k) in the spring equation F = -k * x - c * v for a linear or T = -k * theta - c * w for an angular spring. - /// If mStiffness <= 0, mDamping is ignored and the constraint will have hard limits (as hard as the time step / the number of velocity / position solver steps allows). - /// - /// Note that stiffness values are large numbers. To calculate a ballpark value for the needed stiffness you can use: - /// force = stiffness * delta_spring_length = mass * gravity <=> stiffness = mass * gravity / delta_spring_length. - /// So if your object weighs 1500 kg and the spring compresses by 2 meters, you need a stiffness in the order of 1500 * 9.81 / 2 ~ 7500 N/m. - float mStiffness; - }; - - /// When mSpringMode = ESpringMode::FrequencyAndDamping mDamping is the damping ratio (0 = no damping, 1 = critical damping). - /// When mSpringMode = ESpringMode::StiffnessAndDamping mDamping is the damping (c) in the spring equation F = -k * x - c * v for a linear or T = -k * theta - c * w for an angular spring. - /// Note that if you set mDamping = 0, you will not get an infinite oscillation. Because we integrate physics using an explicit Euler scheme, there is always energy loss. - /// This is done to keep the simulation from exploding, because with a damping of 0 and even the slightest rounding error, the oscillation could become bigger and bigger until the simulation explodes. - float mDamping = 0.0f; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/SwingTwistConstraint.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/SwingTwistConstraint.cpp deleted file mode 100644 index bcd74ff305a..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/SwingTwistConstraint.cpp +++ /dev/null @@ -1,524 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include -#include -#include -#ifdef JPH_DEBUG_RENDERER - #include -#endif // JPH_DEBUG_RENDERER - -JPH_NAMESPACE_BEGIN - -JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(SwingTwistConstraintSettings) -{ - JPH_ADD_BASE_CLASS(SwingTwistConstraintSettings, TwoBodyConstraintSettings) - - JPH_ADD_ENUM_ATTRIBUTE(SwingTwistConstraintSettings, mSpace) - JPH_ADD_ATTRIBUTE(SwingTwistConstraintSettings, mPosition1) - JPH_ADD_ATTRIBUTE(SwingTwistConstraintSettings, mTwistAxis1) - JPH_ADD_ATTRIBUTE(SwingTwistConstraintSettings, mPlaneAxis1) - JPH_ADD_ATTRIBUTE(SwingTwistConstraintSettings, mPosition2) - JPH_ADD_ATTRIBUTE(SwingTwistConstraintSettings, mTwistAxis2) - JPH_ADD_ATTRIBUTE(SwingTwistConstraintSettings, mPlaneAxis2) - JPH_ADD_ENUM_ATTRIBUTE(SwingTwistConstraintSettings, mSwingType) - JPH_ADD_ATTRIBUTE(SwingTwistConstraintSettings, mNormalHalfConeAngle) - JPH_ADD_ATTRIBUTE(SwingTwistConstraintSettings, mPlaneHalfConeAngle) - JPH_ADD_ATTRIBUTE(SwingTwistConstraintSettings, mTwistMinAngle) - JPH_ADD_ATTRIBUTE(SwingTwistConstraintSettings, mTwistMaxAngle) - JPH_ADD_ATTRIBUTE(SwingTwistConstraintSettings, mMaxFrictionTorque) - JPH_ADD_ATTRIBUTE(SwingTwistConstraintSettings, mSwingMotorSettings) - JPH_ADD_ATTRIBUTE(SwingTwistConstraintSettings, mTwistMotorSettings) -} - -void SwingTwistConstraintSettings::SaveBinaryState(StreamOut &inStream) const -{ - ConstraintSettings::SaveBinaryState(inStream); - - inStream.Write(mSpace); - inStream.Write(mPosition1); - inStream.Write(mTwistAxis1); - inStream.Write(mPlaneAxis1); - inStream.Write(mPosition2); - inStream.Write(mTwistAxis2); - inStream.Write(mPlaneAxis2); - inStream.Write(mSwingType); - inStream.Write(mNormalHalfConeAngle); - inStream.Write(mPlaneHalfConeAngle); - inStream.Write(mTwistMinAngle); - inStream.Write(mTwistMaxAngle); - inStream.Write(mMaxFrictionTorque); - mSwingMotorSettings.SaveBinaryState(inStream); - mTwistMotorSettings.SaveBinaryState(inStream); -} - -void SwingTwistConstraintSettings::RestoreBinaryState(StreamIn &inStream) -{ - ConstraintSettings::RestoreBinaryState(inStream); - - inStream.Read(mSpace); - inStream.Read(mPosition1); - inStream.Read(mTwistAxis1); - inStream.Read(mPlaneAxis1); - inStream.Read(mPosition2); - inStream.Read(mTwistAxis2); - inStream.Read(mPlaneAxis2); - inStream.Read(mSwingType); - inStream.Read(mNormalHalfConeAngle); - inStream.Read(mPlaneHalfConeAngle); - inStream.Read(mTwistMinAngle); - inStream.Read(mTwistMaxAngle); - inStream.Read(mMaxFrictionTorque); - mSwingMotorSettings.RestoreBinaryState(inStream); - mTwistMotorSettings.RestoreBinaryState(inStream); -} - -TwoBodyConstraint *SwingTwistConstraintSettings::Create(Body &inBody1, Body &inBody2) const -{ - return new SwingTwistConstraint(inBody1, inBody2, *this); -} - -void SwingTwistConstraint::UpdateLimits() -{ - // Pass limits on to swing twist constraint part - mSwingTwistConstraintPart.SetLimits(mTwistMinAngle, mTwistMaxAngle, -mPlaneHalfConeAngle, mPlaneHalfConeAngle, -mNormalHalfConeAngle, mNormalHalfConeAngle); -} - -SwingTwistConstraint::SwingTwistConstraint(Body &inBody1, Body &inBody2, const SwingTwistConstraintSettings &inSettings) : - TwoBodyConstraint(inBody1, inBody2, inSettings), - mNormalHalfConeAngle(inSettings.mNormalHalfConeAngle), - mPlaneHalfConeAngle(inSettings.mPlaneHalfConeAngle), - mTwistMinAngle(inSettings.mTwistMinAngle), - mTwistMaxAngle(inSettings.mTwistMaxAngle), - mMaxFrictionTorque(inSettings.mMaxFrictionTorque), - mSwingMotorSettings(inSettings.mSwingMotorSettings), - mTwistMotorSettings(inSettings.mTwistMotorSettings) -{ - // Override swing type - mSwingTwistConstraintPart.SetSwingType(inSettings.mSwingType); - - // Calculate rotation needed to go from constraint space to body1 local space - Vec3 normal_axis1 = inSettings.mPlaneAxis1.Cross(inSettings.mTwistAxis1); - Mat44 c_to_b1(Vec4(inSettings.mTwistAxis1, 0), Vec4(normal_axis1, 0), Vec4(inSettings.mPlaneAxis1, 0), Vec4(0, 0, 0, 1)); - mConstraintToBody1 = c_to_b1.GetQuaternion(); - - // Calculate rotation needed to go from constraint space to body2 local space - Vec3 normal_axis2 = inSettings.mPlaneAxis2.Cross(inSettings.mTwistAxis2); - Mat44 c_to_b2(Vec4(inSettings.mTwistAxis2, 0), Vec4(normal_axis2, 0), Vec4(inSettings.mPlaneAxis2, 0), Vec4(0, 0, 0, 1)); - mConstraintToBody2 = c_to_b2.GetQuaternion(); - - if (inSettings.mSpace == EConstraintSpace::WorldSpace) - { - // If all properties were specified in world space, take them to local space now - mLocalSpacePosition1 = Vec3(inBody1.GetInverseCenterOfMassTransform() * inSettings.mPosition1); - mConstraintToBody1 = inBody1.GetRotation().Conjugated() * mConstraintToBody1; - - mLocalSpacePosition2 = Vec3(inBody2.GetInverseCenterOfMassTransform() * inSettings.mPosition2); - mConstraintToBody2 = inBody2.GetRotation().Conjugated() * mConstraintToBody2; - } - else - { - mLocalSpacePosition1 = Vec3(inSettings.mPosition1); - mLocalSpacePosition2 = Vec3(inSettings.mPosition2); - } - - UpdateLimits(); -} - -void SwingTwistConstraint::NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM) -{ - if (mBody1->GetID() == inBodyID) - mLocalSpacePosition1 -= inDeltaCOM; - else if (mBody2->GetID() == inBodyID) - mLocalSpacePosition2 -= inDeltaCOM; -} - -Quat SwingTwistConstraint::GetRotationInConstraintSpace() const -{ - // Let b1, b2 be the center of mass transform of body1 and body2 (For body1 this is mBody1->GetCenterOfMassTransform()) - // Let c1, c2 be the transform that takes a vector from constraint space to local space of body1 and body2 (For body1 this is Mat44::sRotationTranslation(mConstraintToBody1, mLocalSpacePosition1)) - // Let q be the rotation of the constraint in constraint space - // b2 takes a vector from the local space of body2 to world space - // To express this in terms of b1: b2 = b1 * c1 * q * c2^-1 - // c2^-1 goes from local body 2 space to constraint space - // q rotates the constraint - // c1 goes from constraint space to body 1 local space - // b1 goes from body 1 local space to world space - // So when the body rotations are given, q = (b1 * c1)^-1 * b2 c2 - // Or: q = (q1 * c1)^-1 * (q2 * c2) if we're only interested in rotations - Quat constraint_body1_to_world = mBody1->GetRotation() * mConstraintToBody1; - Quat constraint_body2_to_world = mBody2->GetRotation() * mConstraintToBody2; - return constraint_body1_to_world.Conjugated() * constraint_body2_to_world; -} - -void SwingTwistConstraint::SetSwingMotorState(EMotorState inState) -{ - JPH_ASSERT(inState == EMotorState::Off || mSwingMotorSettings.IsValid()); - - if (mSwingMotorState != inState) - { - mSwingMotorState = inState; - - // Ensure that warm starting next frame doesn't apply any impulses (motor parts are repurposed for different modes) - for (AngleConstraintPart &c : mMotorConstraintPart) - c.Deactivate(); - } -} - -void SwingTwistConstraint::SetTwistMotorState(EMotorState inState) -{ - JPH_ASSERT(inState == EMotorState::Off || mTwistMotorSettings.IsValid()); - - if (mTwistMotorState != inState) - { - mTwistMotorState = inState; - - // Ensure that warm starting next frame doesn't apply any impulses (motor parts are repurposed for different modes) - mMotorConstraintPart[0].Deactivate(); - } -} - -void SwingTwistConstraint::SetTargetOrientationCS(QuatArg inOrientation) -{ - Quat q_swing, q_twist; - inOrientation.GetSwingTwist(q_swing, q_twist); - - uint clamped_axis; - mSwingTwistConstraintPart.ClampSwingTwist(q_swing, q_twist, clamped_axis); - - if (clamped_axis != 0) - mTargetOrientation = q_swing * q_twist; - else - mTargetOrientation = inOrientation; -} - -void SwingTwistConstraint::SetupVelocityConstraint(float inDeltaTime) -{ - // Setup point constraint - Mat44 rotation1 = Mat44::sRotation(mBody1->GetRotation()); - Mat44 rotation2 = Mat44::sRotation(mBody2->GetRotation()); - mPointConstraintPart.CalculateConstraintProperties(*mBody1, rotation1, mLocalSpacePosition1, *mBody2, rotation2, mLocalSpacePosition2); - - // GetRotationInConstraintSpace written out since we reuse the sub expressions - Quat constraint_body1_to_world = mBody1->GetRotation() * mConstraintToBody1; - Quat constraint_body2_to_world = mBody2->GetRotation() * mConstraintToBody2; - Quat q = constraint_body1_to_world.Conjugated() * constraint_body2_to_world; - - // Calculate constraint properties for the swing twist limit - mSwingTwistConstraintPart.CalculateConstraintProperties(*mBody1, *mBody2, q, constraint_body1_to_world); - - if (mSwingMotorState != EMotorState::Off || mTwistMotorState != EMotorState::Off || mMaxFrictionTorque > 0.0f) - { - // Calculate rotation motor axis - Mat44 ws_axis = Mat44::sRotation(constraint_body2_to_world); - for (int i = 0; i < 3; ++i) - mWorldSpaceMotorAxis[i] = ws_axis.GetColumn3(i); - - Vec3 rotation_error; - if (mSwingMotorState == EMotorState::Position || mTwistMotorState == EMotorState::Position) - { - // Get target orientation along the shortest path from q - Quat target_orientation = q.Dot(mTargetOrientation) > 0.0f? mTargetOrientation : -mTargetOrientation; - - // The definition of the constraint rotation q: - // R2 * ConstraintToBody2 = R1 * ConstraintToBody1 * q (1) - // - // R2' is the rotation of body 2 when reaching the target_orientation: - // R2' * ConstraintToBody2 = R1 * ConstraintToBody1 * target_orientation (2) - // - // The difference in body 2 space: - // R2' = R2 * diff_body2 (3) - // - // We want to specify the difference in the constraint space of body 2: - // diff_body2 = ConstraintToBody2 * diff * ConstraintToBody2^* (4) - // - // Extracting R2' from 2: R2' = R1 * ConstraintToBody1 * target_orientation * ConstraintToBody2^* (5) - // Combining 3 & 4: R2' = R2 * ConstraintToBody2 * diff * ConstraintToBody2^* (6) - // Combining 1 & 6: R2' = R1 * ConstraintToBody1 * q * diff * ConstraintToBody2^* (7) - // Combining 5 & 7: R1 * ConstraintToBody1 * target_orientation * ConstraintToBody2^* = R1 * ConstraintToBody1 * q * diff * ConstraintToBody2^* - // <=> target_orientation = q * diff - // <=> diff = q^* * target_orientation - Quat diff = q.Conjugated() * target_orientation; - - // Approximate error angles - // The imaginary part of a quaternion is rotation_axis * sin(angle / 2) - // If angle is small, sin(x) = x so angle[i] ~ 2.0f * rotation_axis[i] - // We'll be making small time steps, so if the angle is not small at least the sign will be correct and we'll move in the right direction - rotation_error = -2.0f * diff.GetXYZ(); - } - - // Swing motor - switch (mSwingMotorState) - { - case EMotorState::Off: - if (mMaxFrictionTorque > 0.0f) - { - // Enable friction - for (int i = 1; i < 3; ++i) - mMotorConstraintPart[i].CalculateConstraintProperties(*mBody1, *mBody2, mWorldSpaceMotorAxis[i], 0.0f); - } - else - { - // Disable friction - for (AngleConstraintPart &c : mMotorConstraintPart) - c.Deactivate(); - } - break; - - case EMotorState::Velocity: - // Use motor to create angular velocity around desired axis - for (int i = 1; i < 3; ++i) - mMotorConstraintPart[i].CalculateConstraintProperties(*mBody1, *mBody2, mWorldSpaceMotorAxis[i], -mTargetAngularVelocity[i]); - break; - - case EMotorState::Position: - // Use motor to drive rotation error to zero - if (mSwingMotorSettings.mSpringSettings.HasStiffness()) - { - for (int i = 1; i < 3; ++i) - mMotorConstraintPart[i].CalculateConstraintPropertiesWithSettings(inDeltaTime, *mBody1, *mBody2, mWorldSpaceMotorAxis[i], 0.0f, rotation_error[i], mSwingMotorSettings.mSpringSettings); - } - else - { - for (int i = 1; i < 3; ++i) - mMotorConstraintPart[i].Deactivate(); - } - break; - } - - // Twist motor - switch (mTwistMotorState) - { - case EMotorState::Off: - if (mMaxFrictionTorque > 0.0f) - { - // Enable friction - mMotorConstraintPart[0].CalculateConstraintProperties(*mBody1, *mBody2, mWorldSpaceMotorAxis[0], 0.0f); - } - else - { - // Disable friction - mMotorConstraintPart[0].Deactivate(); - } - break; - - case EMotorState::Velocity: - // Use motor to create angular velocity around desired axis - mMotorConstraintPart[0].CalculateConstraintProperties(*mBody1, *mBody2, mWorldSpaceMotorAxis[0], -mTargetAngularVelocity[0]); - break; - - case EMotorState::Position: - // Use motor to drive rotation error to zero - if (mTwistMotorSettings.mSpringSettings.HasStiffness()) - mMotorConstraintPart[0].CalculateConstraintPropertiesWithSettings(inDeltaTime, *mBody1, *mBody2, mWorldSpaceMotorAxis[0], 0.0f, rotation_error[0], mTwistMotorSettings.mSpringSettings); - else - mMotorConstraintPart[0].Deactivate(); - break; - } - } - else - { - // Disable rotation motor - for (AngleConstraintPart &c : mMotorConstraintPart) - c.Deactivate(); - } -} - -void SwingTwistConstraint::ResetWarmStart() -{ - for (AngleConstraintPart &c : mMotorConstraintPart) - c.Deactivate(); - mSwingTwistConstraintPart.Deactivate(); - mPointConstraintPart.Deactivate(); -} - -void SwingTwistConstraint::WarmStartVelocityConstraint(float inWarmStartImpulseRatio) -{ - // Warm starting: Apply previous frame impulse - for (AngleConstraintPart &c : mMotorConstraintPart) - c.WarmStart(*mBody1, *mBody2, inWarmStartImpulseRatio); - mSwingTwistConstraintPart.WarmStart(*mBody1, *mBody2, inWarmStartImpulseRatio); - mPointConstraintPart.WarmStart(*mBody1, *mBody2, inWarmStartImpulseRatio); -} - -bool SwingTwistConstraint::SolveVelocityConstraint(float inDeltaTime) -{ - bool impulse = false; - - // Solve twist rotation motor - if (mMotorConstraintPart[0].IsActive()) - { - // Twist limits - float min_twist_limit, max_twist_limit; - if (mTwistMotorState == EMotorState::Off) - { - max_twist_limit = inDeltaTime * mMaxFrictionTorque; - min_twist_limit = -max_twist_limit; - } - else - { - min_twist_limit = inDeltaTime * mTwistMotorSettings.mMinTorqueLimit; - max_twist_limit = inDeltaTime * mTwistMotorSettings.mMaxTorqueLimit; - } - - impulse |= mMotorConstraintPart[0].SolveVelocityConstraint(*mBody1, *mBody2, mWorldSpaceMotorAxis[0], min_twist_limit, max_twist_limit); - } - - // Solve swing rotation motor - if (mMotorConstraintPart[1].IsActive()) - { - // Swing parts should turn on / off together - JPH_ASSERT(mMotorConstraintPart[2].IsActive()); - - // Swing limits - float min_swing_limit, max_swing_limit; - if (mSwingMotorState == EMotorState::Off) - { - max_swing_limit = inDeltaTime * mMaxFrictionTorque; - min_swing_limit = -max_swing_limit; - } - else - { - min_swing_limit = inDeltaTime * mSwingMotorSettings.mMinTorqueLimit; - max_swing_limit = inDeltaTime * mSwingMotorSettings.mMaxTorqueLimit; - } - - for (int i = 1; i < 3; ++i) - impulse |= mMotorConstraintPart[i].SolveVelocityConstraint(*mBody1, *mBody2, mWorldSpaceMotorAxis[i], min_swing_limit, max_swing_limit); - } - else - { - // Swing parts should turn on / off together - JPH_ASSERT(!mMotorConstraintPart[2].IsActive()); - } - - // Solve rotation limits - impulse |= mSwingTwistConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2); - - // Solve position constraint - impulse |= mPointConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2); - - return impulse; -} - -bool SwingTwistConstraint::SolvePositionConstraint(float inDeltaTime, float inBaumgarte) -{ - bool impulse = false; - - // Solve rotation violations - Quat q = GetRotationInConstraintSpace(); - impulse |= mSwingTwistConstraintPart.SolvePositionConstraint(*mBody1, *mBody2, q, mConstraintToBody1, mConstraintToBody2, inBaumgarte); - - // Solve position violations - mPointConstraintPart.CalculateConstraintProperties(*mBody1, Mat44::sRotation(mBody1->GetRotation()), mLocalSpacePosition1, *mBody2, Mat44::sRotation(mBody2->GetRotation()), mLocalSpacePosition2); - impulse |= mPointConstraintPart.SolvePositionConstraint(*mBody1, *mBody2, inBaumgarte); - - return impulse; -} - -#ifdef JPH_DEBUG_RENDERER -void SwingTwistConstraint::DrawConstraint(DebugRenderer *inRenderer) const -{ - // Get constraint properties in world space - RMat44 transform1 = mBody1->GetCenterOfMassTransform(); - RVec3 position1 = transform1 * mLocalSpacePosition1; - Quat rotation1 = mBody1->GetRotation() * mConstraintToBody1; - Quat rotation2 = mBody2->GetRotation() * mConstraintToBody2; - - // Draw constraint orientation - inRenderer->DrawCoordinateSystem(RMat44::sRotationTranslation(rotation1, position1), mDrawConstraintSize); - - // Draw current swing and twist - Quat q = GetRotationInConstraintSpace(); - Quat q_swing, q_twist; - q.GetSwingTwist(q_swing, q_twist); - inRenderer->DrawLine(position1, position1 + mDrawConstraintSize * (rotation1 * q_twist).RotateAxisY(), Color::sWhite); - inRenderer->DrawLine(position1, position1 + mDrawConstraintSize * (rotation1 * q_swing).RotateAxisX(), Color::sWhite); - - if (mSwingMotorState == EMotorState::Velocity || mTwistMotorState == EMotorState::Velocity) - { - // Draw target angular velocity - inRenderer->DrawArrow(position1, position1 + rotation2 * mTargetAngularVelocity, Color::sRed, 0.1f); - } - if (mSwingMotorState == EMotorState::Position || mTwistMotorState == EMotorState::Position) - { - // Draw motor swing and twist - Quat swing, twist; - mTargetOrientation.GetSwingTwist(swing, twist); - inRenderer->DrawLine(position1, position1 + mDrawConstraintSize * (rotation1 * twist).RotateAxisY(), Color::sYellow); - inRenderer->DrawLine(position1, position1 + mDrawConstraintSize * (rotation1 * swing).RotateAxisX(), Color::sCyan); - } -} - -void SwingTwistConstraint::DrawConstraintLimits(DebugRenderer *inRenderer) const -{ - // Get matrix that transforms from constraint space to world space - RMat44 constraint_to_world = RMat44::sRotationTranslation(mBody1->GetRotation() * mConstraintToBody1, mBody1->GetCenterOfMassTransform() * mLocalSpacePosition1); - - // Draw limits - if (mSwingTwistConstraintPart.GetSwingType() == ESwingType::Pyramid) - inRenderer->DrawSwingPyramidLimits(constraint_to_world, -mPlaneHalfConeAngle, mPlaneHalfConeAngle, -mNormalHalfConeAngle, mNormalHalfConeAngle, mDrawConstraintSize, Color::sGreen, DebugRenderer::ECastShadow::Off); - else - inRenderer->DrawSwingConeLimits(constraint_to_world, mPlaneHalfConeAngle, mNormalHalfConeAngle, mDrawConstraintSize, Color::sGreen, DebugRenderer::ECastShadow::Off); - inRenderer->DrawPie(constraint_to_world.GetTranslation(), mDrawConstraintSize, constraint_to_world.GetAxisX(), constraint_to_world.GetAxisY(), mTwistMinAngle, mTwistMaxAngle, Color::sPurple, DebugRenderer::ECastShadow::Off); -} -#endif // JPH_DEBUG_RENDERER - -void SwingTwistConstraint::SaveState(StateRecorder &inStream) const -{ - TwoBodyConstraint::SaveState(inStream); - - mPointConstraintPart.SaveState(inStream); - mSwingTwistConstraintPart.SaveState(inStream); - for (const AngleConstraintPart &c : mMotorConstraintPart) - c.SaveState(inStream); - - inStream.Write(mSwingMotorState); - inStream.Write(mTwistMotorState); - inStream.Write(mTargetAngularVelocity); - inStream.Write(mTargetOrientation); -} - -void SwingTwistConstraint::RestoreState(StateRecorder &inStream) -{ - TwoBodyConstraint::RestoreState(inStream); - - mPointConstraintPart.RestoreState(inStream); - mSwingTwistConstraintPart.RestoreState(inStream); - for (AngleConstraintPart &c : mMotorConstraintPart) - c.RestoreState(inStream); - - inStream.Read(mSwingMotorState); - inStream.Read(mTwistMotorState); - inStream.Read(mTargetAngularVelocity); - inStream.Read(mTargetOrientation); -} - -Ref SwingTwistConstraint::GetConstraintSettings() const -{ - SwingTwistConstraintSettings *settings = new SwingTwistConstraintSettings; - ToConstraintSettings(*settings); - settings->mSpace = EConstraintSpace::LocalToBodyCOM; - settings->mPosition1 = RVec3(mLocalSpacePosition1); - settings->mTwistAxis1 = mConstraintToBody1.RotateAxisX(); - settings->mPlaneAxis1 = mConstraintToBody1.RotateAxisZ(); - settings->mPosition2 = RVec3(mLocalSpacePosition2); - settings->mTwistAxis2 = mConstraintToBody2.RotateAxisX(); - settings->mPlaneAxis2 = mConstraintToBody2.RotateAxisZ(); - settings->mSwingType = mSwingTwistConstraintPart.GetSwingType(); - settings->mNormalHalfConeAngle = mNormalHalfConeAngle; - settings->mPlaneHalfConeAngle = mPlaneHalfConeAngle; - settings->mTwistMinAngle = mTwistMinAngle; - settings->mTwistMaxAngle = mTwistMaxAngle; - settings->mMaxFrictionTorque = mMaxFrictionTorque; - settings->mSwingMotorSettings = mSwingMotorSettings; - settings->mTwistMotorSettings = mTwistMotorSettings; - return settings; -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/SwingTwistConstraint.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/SwingTwistConstraint.h deleted file mode 100644 index d915c001534..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/SwingTwistConstraint.h +++ /dev/null @@ -1,197 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -/// Swing twist constraint settings, used to create a swing twist constraint -/// All values in this structure are copied to the swing twist constraint and the settings object is no longer needed afterwards. -/// -/// This image describes the limit settings: -/// @image html Docs/SwingTwistConstraint.png -class JPH_EXPORT SwingTwistConstraintSettings final : public TwoBodyConstraintSettings -{ -public: - JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, SwingTwistConstraintSettings) - - // See: ConstraintSettings::SaveBinaryState - virtual void SaveBinaryState(StreamOut &inStream) const override; - - /// Create an instance of this constraint - virtual TwoBodyConstraint * Create(Body &inBody1, Body &inBody2) const override; - - /// This determines in which space the constraint is setup, all properties below should be in the specified space - EConstraintSpace mSpace = EConstraintSpace::WorldSpace; - - ///@name Body 1 constraint reference frame (space determined by mSpace) - RVec3 mPosition1 = RVec3::sZero(); - Vec3 mTwistAxis1 = Vec3::sAxisX(); - Vec3 mPlaneAxis1 = Vec3::sAxisY(); - - ///@name Body 2 constraint reference frame (space determined by mSpace) - RVec3 mPosition2 = RVec3::sZero(); - Vec3 mTwistAxis2 = Vec3::sAxisX(); - Vec3 mPlaneAxis2 = Vec3::sAxisY(); - - /// The type of swing constraint that we want to use. - ESwingType mSwingType = ESwingType::Cone; - - ///@name Swing rotation limits - float mNormalHalfConeAngle = 0.0f; ///< See image at Detailed Description. Angle in radians. - float mPlaneHalfConeAngle = 0.0f; ///< See image at Detailed Description. Angle in radians. - - ///@name Twist rotation limits - float mTwistMinAngle = 0.0f; ///< See image at Detailed Description. Angle in radians. Should be \f$\in [-\pi, \pi]\f$. - float mTwistMaxAngle = 0.0f; ///< See image at Detailed Description. Angle in radians. Should be \f$\in [-\pi, \pi]\f$. - - ///@name Friction - float mMaxFrictionTorque = 0.0f; ///< Maximum amount of torque (N m) to apply as friction when the constraint is not powered by a motor - - ///@name In case the constraint is powered, this determines the motor settings around the swing and twist axis - MotorSettings mSwingMotorSettings; - MotorSettings mTwistMotorSettings; - -protected: - // See: ConstraintSettings::RestoreBinaryState - virtual void RestoreBinaryState(StreamIn &inStream) override; -}; - -/// A swing twist constraint is a specialized constraint for humanoid ragdolls that allows limited rotation only -/// -/// @see SwingTwistConstraintSettings for a description of the limits -class JPH_EXPORT SwingTwistConstraint final : public TwoBodyConstraint -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Construct swing twist constraint - SwingTwistConstraint(Body &inBody1, Body &inBody2, const SwingTwistConstraintSettings &inSettings); - - ///@name Generic interface of a constraint - virtual EConstraintSubType GetSubType() const override { return EConstraintSubType::SwingTwist; } - virtual void NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM) override; - virtual void SetupVelocityConstraint(float inDeltaTime) override; - virtual void ResetWarmStart() override; - virtual void WarmStartVelocityConstraint(float inWarmStartImpulseRatio) override; - virtual bool SolveVelocityConstraint(float inDeltaTime) override; - virtual bool SolvePositionConstraint(float inDeltaTime, float inBaumgarte) override; -#ifdef JPH_DEBUG_RENDERER - virtual void DrawConstraint(DebugRenderer *inRenderer) const override; - virtual void DrawConstraintLimits(DebugRenderer *inRenderer) const override; -#endif // JPH_DEBUG_RENDERER - virtual void SaveState(StateRecorder &inStream) const override; - virtual void RestoreState(StateRecorder &inStream) override; - virtual Ref GetConstraintSettings() const override; - - // See: TwoBodyConstraint - virtual Mat44 GetConstraintToBody1Matrix() const override { return Mat44::sRotationTranslation(mConstraintToBody1, mLocalSpacePosition1); } - virtual Mat44 GetConstraintToBody2Matrix() const override { return Mat44::sRotationTranslation(mConstraintToBody2, mLocalSpacePosition2); } - - ///@name Constraint reference frame - inline Vec3 GetLocalSpacePosition1() const { return mLocalSpacePosition1; } - inline Vec3 GetLocalSpacePosition2() const { return mLocalSpacePosition2; } - inline Quat GetConstraintToBody1() const { return mConstraintToBody1; } - inline Quat GetConstraintToBody2() const { return mConstraintToBody2; } - - ///@name Constraint limits - inline float GetNormalHalfConeAngle() const { return mNormalHalfConeAngle; } - inline void SetNormalHalfConeAngle(float inAngle) { mNormalHalfConeAngle = inAngle; UpdateLimits(); } - inline float GetPlaneHalfConeAngle() const { return mPlaneHalfConeAngle; } - inline void SetPlaneHalfConeAngle(float inAngle) { mPlaneHalfConeAngle = inAngle; UpdateLimits(); } - inline float GetTwistMinAngle() const { return mTwistMinAngle; } - inline void SetTwistMinAngle(float inAngle) { mTwistMinAngle = inAngle; UpdateLimits(); } - inline float GetTwistMaxAngle() const { return mTwistMaxAngle; } - inline void SetTwistMaxAngle(float inAngle) { mTwistMaxAngle = inAngle; UpdateLimits(); } - - ///@name Motor settings - const MotorSettings & GetSwingMotorSettings() const { return mSwingMotorSettings; } - MotorSettings & GetSwingMotorSettings() { return mSwingMotorSettings; } - const MotorSettings & GetTwistMotorSettings() const { return mTwistMotorSettings; } - MotorSettings & GetTwistMotorSettings() { return mTwistMotorSettings; } - - ///@name Friction control - void SetMaxFrictionTorque(float inFrictionTorque) { mMaxFrictionTorque = inFrictionTorque; } - float GetMaxFrictionTorque() const { return mMaxFrictionTorque; } - - ///@name Motor controls - - /// Controls if the motors are on or off - void SetSwingMotorState(EMotorState inState); - EMotorState GetSwingMotorState() const { return mSwingMotorState; } - void SetTwistMotorState(EMotorState inState); - EMotorState GetTwistMotorState() const { return mTwistMotorState; } - - /// Set the target angular velocity of body 2 in constraint space of body 2 - void SetTargetAngularVelocityCS(Vec3Arg inAngularVelocity) { mTargetAngularVelocity = inAngularVelocity; } - Vec3 GetTargetAngularVelocityCS() const { return mTargetAngularVelocity; } - - /// Set the target orientation in constraint space (drives constraint to: GetRotationInConstraintSpace() == inOrientation) - void SetTargetOrientationCS(QuatArg inOrientation); - Quat GetTargetOrientationCS() const { return mTargetOrientation; } - - /// Set the target orientation in body space (R2 = R1 * inOrientation, where R1 and R2 are the world space rotations for body 1 and 2). - /// Solve: R2 * ConstraintToBody2 = R1 * ConstraintToBody1 * q (see SwingTwistConstraint::GetSwingTwist) and R2 = R1 * inOrientation for q. - void SetTargetOrientationBS(QuatArg inOrientation) { SetTargetOrientationCS(mConstraintToBody1.Conjugated() * inOrientation * mConstraintToBody2); } - - /// Get current rotation of constraint in constraint space. - /// Solve: R2 * ConstraintToBody2 = R1 * ConstraintToBody1 * q for q. - Quat GetRotationInConstraintSpace() const; - - ///@name Get Lagrange multiplier from last physics update (the linear/angular impulse applied to satisfy the constraint) - inline Vec3 GetTotalLambdaPosition() const { return mPointConstraintPart.GetTotalLambda(); } - inline float GetTotalLambdaTwist() const { return mSwingTwistConstraintPart.GetTotalTwistLambda(); } - inline float GetTotalLambdaSwingY() const { return mSwingTwistConstraintPart.GetTotalSwingYLambda(); } - inline float GetTotalLambdaSwingZ() const { return mSwingTwistConstraintPart.GetTotalSwingZLambda(); } - inline Vec3 GetTotalLambdaMotor() const { return Vec3(mMotorConstraintPart[0].GetTotalLambda(), mMotorConstraintPart[1].GetTotalLambda(), mMotorConstraintPart[2].GetTotalLambda()); } - -private: - // Update the limits in the swing twist constraint part - void UpdateLimits(); - - // CONFIGURATION PROPERTIES FOLLOW - - // Local space constraint positions - Vec3 mLocalSpacePosition1; - Vec3 mLocalSpacePosition2; - - // Transforms from constraint space to body space - Quat mConstraintToBody1; - Quat mConstraintToBody2; - - // Limits - float mNormalHalfConeAngle; - float mPlaneHalfConeAngle; - float mTwistMinAngle; - float mTwistMaxAngle; - - // Friction - float mMaxFrictionTorque; - - // Motor controls - MotorSettings mSwingMotorSettings; - MotorSettings mTwistMotorSettings; - EMotorState mSwingMotorState = EMotorState::Off; - EMotorState mTwistMotorState = EMotorState::Off; - Vec3 mTargetAngularVelocity = Vec3::sZero(); - Quat mTargetOrientation = Quat::sIdentity(); - - // RUN TIME PROPERTIES FOLLOW - - // Rotation axis for motor constraint parts - Vec3 mWorldSpaceMotorAxis[3]; - - // The constraint parts - PointConstraintPart mPointConstraintPart; - SwingTwistConstraintPart mSwingTwistConstraintPart; - AngleConstraintPart mMotorConstraintPart[3]; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/TwoBodyConstraint.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/TwoBodyConstraint.cpp deleted file mode 100644 index 9ee7cc5d892..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/TwoBodyConstraint.cpp +++ /dev/null @@ -1,56 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include -#include - -#ifdef JPH_DEBUG_RENDERER - #include -#endif // JPH_DEBUG_RENDERER - -JPH_NAMESPACE_BEGIN - -JPH_IMPLEMENT_SERIALIZABLE_ABSTRACT(TwoBodyConstraintSettings) -{ - JPH_ADD_BASE_CLASS(TwoBodyConstraintSettings, ConstraintSettings) -} - -void TwoBodyConstraint::BuildIslands(uint32 inConstraintIndex, IslandBuilder &ioBuilder, BodyManager &inBodyManager) -{ - // Activate bodies - BodyID body_ids[2]; - int num_bodies = 0; - if (mBody1->IsDynamic() && !mBody1->IsActive()) - body_ids[num_bodies++] = mBody1->GetID(); - if (mBody2->IsDynamic() && !mBody2->IsActive()) - body_ids[num_bodies++] = mBody2->GetID(); - if (num_bodies > 0) - inBodyManager.ActivateBodies(body_ids, num_bodies); - - // Link the bodies into the same island - ioBuilder.LinkConstraint(inConstraintIndex, mBody1->GetIndexInActiveBodiesInternal(), mBody2->GetIndexInActiveBodiesInternal()); -} - -uint TwoBodyConstraint::BuildIslandSplits(LargeIslandSplitter &ioSplitter) const -{ - return ioSplitter.AssignSplit(mBody1, mBody2); -} - -#ifdef JPH_DEBUG_RENDERER - -void TwoBodyConstraint::DrawConstraintReferenceFrame(DebugRenderer *inRenderer) const -{ - RMat44 transform1 = mBody1->GetCenterOfMassTransform() * GetConstraintToBody1Matrix(); - RMat44 transform2 = mBody2->GetCenterOfMassTransform() * GetConstraintToBody2Matrix(); - inRenderer->DrawCoordinateSystem(transform1, 1.1f * mDrawConstraintSize); - inRenderer->DrawCoordinateSystem(transform2, mDrawConstraintSize); -} - -#endif // JPH_DEBUG_RENDERER - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/TwoBodyConstraint.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/TwoBodyConstraint.h deleted file mode 100644 index 028c073af8b..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Constraints/TwoBodyConstraint.h +++ /dev/null @@ -1,65 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include - -JPH_NAMESPACE_BEGIN - -class TwoBodyConstraint; - -/// Base class for settings for all constraints that involve 2 bodies -class JPH_EXPORT TwoBodyConstraintSettings : public ConstraintSettings -{ -public: - JPH_DECLARE_SERIALIZABLE_ABSTRACT(JPH_EXPORT, TwoBodyConstraintSettings) - - /// Create an instance of this constraint - /// You can use Body::sFixedToWorld for inBody1 if you want to attach inBody2 to the world - virtual TwoBodyConstraint * Create(Body &inBody1, Body &inBody2) const = 0; -}; - -/// Base class for all constraints that involve 2 bodies. Body1 is usually considered the parent, Body2 the child. -class JPH_EXPORT TwoBodyConstraint : public Constraint -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Constructor - TwoBodyConstraint(Body &inBody1, Body &inBody2, const TwoBodyConstraintSettings &inSettings) : Constraint(inSettings), mBody1(&inBody1), mBody2(&inBody2) { } - - /// Get the type of a constraint - virtual EConstraintType GetType() const override { return EConstraintType::TwoBodyConstraint; } - - /// Solver interface - virtual bool IsActive() const override { return Constraint::IsActive() && (mBody1->IsActive() || mBody2->IsActive()) && (mBody2->IsDynamic() || mBody1->IsDynamic()); } -#ifdef JPH_DEBUG_RENDERER - virtual void DrawConstraintReferenceFrame(DebugRenderer *inRenderer) const override; -#endif // JPH_DEBUG_RENDERER - - /// Access to the connected bodies - Body * GetBody1() const { return mBody1; } - Body * GetBody2() const { return mBody2; } - - /// Calculates the transform that transforms from constraint space to body 1 space. The first column of the matrix is the primary constraint axis (e.g. the hinge axis / slider direction), second column the secondary etc. - virtual Mat44 GetConstraintToBody1Matrix() const = 0; - - /// Calculates the transform that transforms from constraint space to body 2 space. The first column of the matrix is the primary constraint axis (e.g. the hinge axis / slider direction), second column the secondary etc. - virtual Mat44 GetConstraintToBody2Matrix() const = 0; - - /// Link bodies that are connected by this constraint in the island builder - virtual void BuildIslands(uint32 inConstraintIndex, IslandBuilder &ioBuilder, BodyManager &inBodyManager) override; - - /// Link bodies that are connected by this constraint in the same split. Returns the split index. - virtual uint BuildIslandSplits(LargeIslandSplitter &ioSplitter) const override; - -protected: - /// The two bodies involved - Body * mBody1; - Body * mBody2; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/DeterminismLog.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/DeterminismLog.cpp deleted file mode 100644 index 7985a36bf97..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/DeterminismLog.cpp +++ /dev/null @@ -1,17 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2022 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include - -#ifdef JPH_ENABLE_DETERMINISM_LOG - -JPH_NAMESPACE_BEGIN - -DeterminismLog DeterminismLog::sLog; - -JPH_NAMESPACE_END - -#endif // JPH_ENABLE_DETERMINISM_LOG diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/DeterminismLog.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/DeterminismLog.h deleted file mode 100644 index e2930ff3cea..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/DeterminismLog.h +++ /dev/null @@ -1,159 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2022 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -//#define JPH_ENABLE_DETERMINISM_LOG -#ifdef JPH_ENABLE_DETERMINISM_LOG - -#include -#include - -JPH_SUPPRESS_WARNINGS_STD_BEGIN -#include -#include -JPH_SUPPRESS_WARNINGS_STD_END - -JPH_NAMESPACE_BEGIN - -/// A simple class that logs the state of the simulation. The resulting text file can be used to diff between platforms and find issues in determinism. -class DeterminismLog -{ -private: - JPH_INLINE uint32 Convert(float inValue) const - { - return *(uint32 *)&inValue; - } - - JPH_INLINE uint64 Convert(double inValue) const - { - return *(uint64 *)&inValue; - } - -public: - DeterminismLog() - { - mLog.open("detlog.txt", std::ios::out | std::ios::trunc | std::ios::binary); // Binary because we don't want a difference between Unix and Windows line endings. - mLog.fill('0'); - } - - DeterminismLog & operator << (char inValue) - { - mLog << inValue; - return *this; - } - - DeterminismLog & operator << (const char *inValue) - { - mLog << std::dec << inValue; - return *this; - } - - DeterminismLog & operator << (const string &inValue) - { - mLog << std::dec << inValue; - return *this; - } - - DeterminismLog & operator << (const BodyID &inValue) - { - mLog << std::hex << std::setw(8) << inValue.GetIndexAndSequenceNumber(); - return *this; - } - - DeterminismLog & operator << (const SubShapeID &inValue) - { - mLog << std::hex << std::setw(8) << inValue.GetValue(); - return *this; - } - - DeterminismLog & operator << (float inValue) - { - mLog << std::hex << std::setw(8) << Convert(inValue); - return *this; - } - - DeterminismLog & operator << (int inValue) - { - mLog << inValue; - return *this; - } - - DeterminismLog & operator << (uint32 inValue) - { - mLog << std::hex << std::setw(8) << inValue; - return *this; - } - - DeterminismLog & operator << (uint64 inValue) - { - mLog << std::hex << std::setw(16) << inValue; - return *this; - } - - DeterminismLog & operator << (Vec3Arg inValue) - { - mLog << std::hex << std::setw(8) << Convert(inValue.GetX()) << " " << std::setw(8) << Convert(inValue.GetY()) << " " << std::setw(8) << Convert(inValue.GetZ()); - return *this; - } - - DeterminismLog & operator << (DVec3Arg inValue) - { - mLog << std::hex << std::setw(16) << Convert(inValue.GetX()) << " " << std::setw(16) << Convert(inValue.GetY()) << " " << std::setw(16) << Convert(inValue.GetZ()); - return *this; - } - - DeterminismLog & operator << (Vec4Arg inValue) - { - mLog << std::hex << std::setw(8) << Convert(inValue.GetX()) << " " << std::setw(8) << Convert(inValue.GetY()) << " " << std::setw(8) << Convert(inValue.GetZ()) << " " << std::setw(8) << Convert(inValue.GetW()); - return *this; - } - - DeterminismLog & operator << (const Float3 &inValue) - { - mLog << std::hex << std::setw(8) << Convert(inValue.x) << " " << std::setw(8) << Convert(inValue.y) << " " << std::setw(8) << Convert(inValue.z); - return *this; - } - - DeterminismLog & operator << (Mat44Arg inValue) - { - *this << inValue.GetColumn4(0) << " " << inValue.GetColumn4(1) << " " << inValue.GetColumn4(2) << " " << inValue.GetColumn4(3); - return *this; - } - - DeterminismLog & operator << (DMat44Arg inValue) - { - *this << inValue.GetColumn4(0) << " " << inValue.GetColumn4(1) << " " << inValue.GetColumn4(2) << " " << inValue.GetTranslation(); - return *this; - } - - DeterminismLog & operator << (QuatArg inValue) - { - *this << inValue.GetXYZW(); - return *this; - } - - // Singleton instance - static DeterminismLog sLog; - -private: - std::ofstream mLog; -}; - -/// Will log something to the determinism log, usage: JPH_DET_LOG("label " << value); -#define JPH_DET_LOG(...) DeterminismLog::sLog << __VA_ARGS__ << '\n' - -JPH_NAMESPACE_END - -#else - -JPH_SUPPRESS_WARNING_PUSH -JPH_SUPPRESS_WARNINGS - -/// By default we log nothing -#define JPH_DET_LOG(...) - -JPH_SUPPRESS_WARNING_POP - -#endif // JPH_ENABLE_DETERMINISM_LOG diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/EActivation.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/EActivation.h deleted file mode 100644 index 08c10c20dd4..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/EActivation.h +++ /dev/null @@ -1,16 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -JPH_NAMESPACE_BEGIN - -/// Enum used by AddBody to determine if the body needs to be initially active -enum class EActivation -{ - Activate, ///< Activate the body, making it part of the simulation - DontActivate ///< Leave activation state as it is (will not deactivate an active body) -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/EPhysicsUpdateError.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/EPhysicsUpdateError.h deleted file mode 100644 index c9edd6deda0..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/EPhysicsUpdateError.h +++ /dev/null @@ -1,37 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2023 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -JPH_NAMESPACE_BEGIN - -/// Enum used by PhysicsSystem to report error conditions during the PhysicsSystem::Update call. This is a bit field, multiple errors can trigger in the same update. -enum class EPhysicsUpdateError : uint32 -{ - None = 0, ///< No errors - ManifoldCacheFull = 1 << 0, ///< The manifold cache is full, this means that the total number of contacts between bodies is too high. Some contacts were ignored. Increase inMaxContactConstraints in PhysicsSystem::Init. - BodyPairCacheFull = 1 << 1, ///< The body pair cache is full, this means that too many bodies contacted. Some contacts were ignored. Increase inMaxBodyPairs in PhysicsSystem::Init. - ContactConstraintsFull = 1 << 2, ///< The contact constraints buffer is full. Some contacts were ignored. Increase inMaxContactConstraints in PhysicsSystem::Init. -}; - -/// OR operator for EPhysicsUpdateError -inline EPhysicsUpdateError operator | (EPhysicsUpdateError inA, EPhysicsUpdateError inB) -{ - return static_cast(static_cast(inA) | static_cast(inB)); -} - -/// OR operator for EPhysicsUpdateError -inline EPhysicsUpdateError operator |= (EPhysicsUpdateError &ioA, EPhysicsUpdateError inB) -{ - ioA = ioA | inB; - return ioA; -} - -/// AND operator for EPhysicsUpdateError -inline EPhysicsUpdateError operator & (EPhysicsUpdateError inA, EPhysicsUpdateError inB) -{ - return static_cast(static_cast(inA) & static_cast(inB)); -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/IslandBuilder.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/IslandBuilder.cpp deleted file mode 100644 index ed1064df996..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/IslandBuilder.cpp +++ /dev/null @@ -1,484 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -IslandBuilder::~IslandBuilder() -{ - JPH_ASSERT(mConstraintLinks == nullptr); - JPH_ASSERT(mContactLinks == nullptr); - JPH_ASSERT(mBodyIslands == nullptr); - JPH_ASSERT(mBodyIslandEnds == nullptr); - JPH_ASSERT(mConstraintIslands == nullptr); - JPH_ASSERT(mConstraintIslandEnds == nullptr); - JPH_ASSERT(mContactIslands == nullptr); - JPH_ASSERT(mContactIslandEnds == nullptr); - JPH_ASSERT(mIslandsSorted == nullptr); - - delete [] mBodyLinks; -} - -void IslandBuilder::Init(uint32 inMaxActiveBodies) -{ - mMaxActiveBodies = inMaxActiveBodies; - - // Link each body to itself, BuildBodyIslands() will restore this so that we don't need to do this each step - JPH_ASSERT(mBodyLinks == nullptr); - mBodyLinks = new BodyLink [mMaxActiveBodies]; - for (uint32 i = 0; i < mMaxActiveBodies; ++i) - mBodyLinks[i].mLinkedTo.store(i, memory_order_relaxed); -} - -void IslandBuilder::PrepareContactConstraints(uint32 inMaxContacts, TempAllocator *inTempAllocator) -{ - JPH_PROFILE_FUNCTION(); - - // Need to call Init first - JPH_ASSERT(mBodyLinks != nullptr); - - // Check that the builder has been reset - JPH_ASSERT(mNumContacts == 0); - JPH_ASSERT(mNumIslands == 0); - - // Create contact link buffer, not initialized so each contact needs to be explicitly set - JPH_ASSERT(mContactLinks == nullptr); - mContactLinks = (uint32 *)inTempAllocator->Allocate(inMaxContacts * sizeof(uint32)); - mMaxContacts = inMaxContacts; - -#ifdef JPH_VALIDATE_ISLAND_BUILDER - // Create validation structures - JPH_ASSERT(mLinkValidation == nullptr); - mLinkValidation = (LinkValidation *)inTempAllocator->Allocate(inMaxContacts * sizeof(LinkValidation)); - mNumLinkValidation = 0; -#endif -} - -void IslandBuilder::PrepareNonContactConstraints(uint32 inNumConstraints, TempAllocator *inTempAllocator) -{ - JPH_PROFILE_FUNCTION(); - - // Need to call Init first - JPH_ASSERT(mBodyLinks != nullptr); - - // Check that the builder has been reset - JPH_ASSERT(mNumIslands == 0); - - // Store number of constraints - mNumConstraints = inNumConstraints; - - // Create constraint link buffer, not initialized so each constraint needs to be explicitly set - JPH_ASSERT(mConstraintLinks == nullptr); - mConstraintLinks = (uint32 *)inTempAllocator->Allocate(inNumConstraints * sizeof(uint32)); -} - -uint32 IslandBuilder::GetLowestBodyIndex(uint32 inActiveBodyIndex) const -{ - uint32 index = inActiveBodyIndex; - for (;;) - { - uint32 link_to = mBodyLinks[index].mLinkedTo.load(memory_order_relaxed); - if (link_to == index) - break; - index = link_to; - } - return index; -} - -void IslandBuilder::LinkBodies(uint32 inFirst, uint32 inSecond) -{ - JPH_PROFILE_FUNCTION(); - - // Both need to be active, we don't want to create an island with static objects - if (inFirst >= mMaxActiveBodies || inSecond >= mMaxActiveBodies) - return; - -#ifdef JPH_VALIDATE_ISLAND_BUILDER - // Add link to the validation list - if (mNumLinkValidation < uint32(mMaxContacts)) - mLinkValidation[mNumLinkValidation++] = { inFirst, inSecond }; - else - JPH_ASSERT(false, "Out of links"); -#endif - - // Start the algorithm with the two bodies - uint32 first_link_to = inFirst; - uint32 second_link_to = inSecond; - - for (;;) - { - // Follow the chain until we get to the body with lowest index - // If the swap compare below fails, we'll keep searching from the lowest index for the new lowest index - first_link_to = GetLowestBodyIndex(first_link_to); - second_link_to = GetLowestBodyIndex(second_link_to); - - // If the targets are the same, the bodies are already connected - if (first_link_to != second_link_to) - { - // We always link the highest to the lowest - if (first_link_to < second_link_to) - { - // Attempt to link the second to the first - // Since we found this body to be at the end of the chain it must point to itself, and if it - // doesn't it has been reparented and we need to retry the algorithm - if (!mBodyLinks[second_link_to].mLinkedTo.compare_exchange_weak(second_link_to, first_link_to, memory_order_relaxed)) - continue; - } - else - { - // Attempt to link the first to the second - // Since we found this body to be at the end of the chain it must point to itself, and if it - // doesn't it has been reparented and we need to retry the algorithm - if (!mBodyLinks[first_link_to].mLinkedTo.compare_exchange_weak(first_link_to, second_link_to, memory_order_relaxed)) - continue; - } - } - - // Linking succeeded! - // Chains of bodies can become really long, resulting in an O(N) loop to find the lowest body index - // to prevent this we attempt to update the link of the bodies that were passed in to directly point - // to the lowest index that we found. If the value became lower than our lowest link, some other - // thread must have relinked these bodies in the mean time so we won't update the value. - uint32 lowest_link_to = min(first_link_to, second_link_to); - AtomicMin(mBodyLinks[inFirst].mLinkedTo, lowest_link_to, memory_order_relaxed); - AtomicMin(mBodyLinks[inSecond].mLinkedTo, lowest_link_to, memory_order_relaxed); - break; - } -} - -void IslandBuilder::LinkConstraint(uint32 inConstraintIndex, uint32 inFirst, uint32 inSecond) -{ - LinkBodies(inFirst, inSecond); - - JPH_ASSERT(inConstraintIndex < mNumConstraints); - uint32 min_value = min(inFirst, inSecond); // Use fact that invalid index is 0xffffffff, we want the active body of two - JPH_ASSERT(min_value != Body::cInactiveIndex); // At least one of the bodies must be active - mConstraintLinks[inConstraintIndex] = min_value; -} - -void IslandBuilder::LinkContact(uint32 inContactIndex, uint32 inFirst, uint32 inSecond) -{ - JPH_ASSERT(inContactIndex < mMaxContacts); - mContactLinks[inContactIndex] = min(inFirst, inSecond); // Use fact that invalid index is 0xffffffff, we want the active body of two -} - -#ifdef JPH_VALIDATE_ISLAND_BUILDER - -void IslandBuilder::ValidateIslands(uint32 inNumActiveBodies) const -{ - JPH_PROFILE_FUNCTION(); - - // Go through all links so far - for (uint32 i = 0; i < mNumLinkValidation; ++i) - { - // If the bodies in this link ended up in different groups we have a problem - if (mBodyLinks[mLinkValidation[i].mFirst].mIslandIndex != mBodyLinks[mLinkValidation[i].mSecond].mIslandIndex) - { - Trace("Fail: %u, %u", mLinkValidation[i].mFirst, mLinkValidation[i].mSecond); - Trace("Num Active: %u", inNumActiveBodies); - - for (uint32 j = 0; j < mNumLinkValidation; ++j) - Trace("builder.Link(%u, %u);", mLinkValidation[j].mFirst, mLinkValidation[j].mSecond); - - IslandBuilder tmp; - tmp.Init(inNumActiveBodies); - for (uint32 j = 0; j < mNumLinkValidation; ++j) - { - Trace("Link %u -> %u", mLinkValidation[j].mFirst, mLinkValidation[j].mSecond); - tmp.LinkBodies(mLinkValidation[j].mFirst, mLinkValidation[j].mSecond); - for (uint32 t = 0; t < inNumActiveBodies; ++t) - Trace("%u -> %u", t, (uint32)tmp.mBodyLinks[t].mLinkedTo); - } - - JPH_ASSERT(false, "IslandBuilder validation failed"); - } - } -} - -#endif - -void IslandBuilder::BuildBodyIslands(const BodyID *inActiveBodies, uint32 inNumActiveBodies, TempAllocator *inTempAllocator) -{ - JPH_PROFILE_FUNCTION(); - - // Store the amount of active bodies - mNumActiveBodies = inNumActiveBodies; - - // Create output arrays for body ID's, don't call constructors - JPH_ASSERT(mBodyIslands == nullptr); - mBodyIslands = (BodyID *)inTempAllocator->Allocate(inNumActiveBodies * sizeof(BodyID)); - - // Create output array for start index of each island. At this point we don't know how many islands there will be, but we know it cannot be more than inNumActiveBodies. - // Note: We allocate 1 extra entry because we always increment the count of the next island. - uint32 *body_island_starts = (uint32 *)inTempAllocator->Allocate((inNumActiveBodies + 1) * sizeof(uint32)); - - // First island always starts at 0 - body_island_starts[0] = 0; - - // Calculate island index for all bodies - JPH_ASSERT(mNumIslands == 0); - for (uint32 i = 0; i < inNumActiveBodies; ++i) - { - BodyLink &link = mBodyLinks[i]; - uint32 s = link.mLinkedTo.load(memory_order_relaxed); - if (s != i) - { - // Links to another body, take island index from other body (this must have been filled in already since we're looping from low to high) - JPH_ASSERT(s < uint32(i)); - uint32 island_index = mBodyLinks[s].mIslandIndex; - link.mIslandIndex = island_index; - - // Increment the start of the next island - body_island_starts[island_index + 1]++; - } - else - { - // Does not link to other body, this is the start of a new island - link.mIslandIndex = mNumIslands; - ++mNumIslands; - - // Set the start of the next island to 1 - body_island_starts[mNumIslands] = 1; - } - } - -#ifdef JPH_VALIDATE_ISLAND_BUILDER - ValidateIslands(inNumActiveBodies); -#endif - - // Make the start array absolute (so far we only counted) - for (uint32 island = 1; island < mNumIslands; ++island) - body_island_starts[island] += body_island_starts[island - 1]; - - // Convert the to a linear list grouped by island - for (uint32 i = 0; i < inNumActiveBodies; ++i) - { - BodyLink &link = mBodyLinks[i]; - - // Copy the body to the correct location in the array and increment it - uint32 &start = body_island_starts[link.mIslandIndex]; - mBodyIslands[start] = inActiveBodies[i]; - start++; - - // Reset linked to field for the next update - link.mLinkedTo.store(i, memory_order_relaxed); - } - - // We should now have a full array - JPH_ASSERT(mNumIslands == 0 || body_island_starts[mNumIslands - 1] == inNumActiveBodies); - - // We've incremented all body indices so that they now point at the end instead of the starts - JPH_ASSERT(mBodyIslandEnds == nullptr); - mBodyIslandEnds = body_island_starts; -} - -void IslandBuilder::BuildConstraintIslands(const uint32 *inConstraintToBody, uint32 inNumConstraints, uint32 *&outConstraints, uint32 *&outConstraintsEnd, TempAllocator *inTempAllocator) const -{ - JPH_PROFILE_FUNCTION(); - - // Check if there's anything to do - if (inNumConstraints == 0) - return; - - // Create output arrays for constraints - // Note: For the end indices we allocate 1 extra entry so we don't have to do an if in the inner loop - uint32 *constraints = (uint32 *)inTempAllocator->Allocate(inNumConstraints * sizeof(uint32)); - uint32 *constraint_ends = (uint32 *)inTempAllocator->Allocate((mNumIslands + 1) * sizeof(uint32)); - - // Reset sizes - for (uint32 island = 0; island < mNumIslands; ++island) - constraint_ends[island] = 0; - - // Loop over array and increment start relative position for the next island - for (uint32 constraint = 0; constraint < inNumConstraints; ++constraint) - { - uint32 body_idx = inConstraintToBody[constraint]; - uint32 next_island_idx = mBodyLinks[body_idx].mIslandIndex + 1; - JPH_ASSERT(next_island_idx <= mNumIslands); - constraint_ends[next_island_idx]++; - } - - // Make start positions absolute - for (uint32 island = 1; island < mNumIslands; ++island) - constraint_ends[island] += constraint_ends[island - 1]; - - // Loop over array and collect constraints - for (uint32 constraint = 0; constraint < inNumConstraints; ++constraint) - { - uint32 body_idx = inConstraintToBody[constraint]; - uint32 island_idx = mBodyLinks[body_idx].mIslandIndex; - constraints[constraint_ends[island_idx]++] = constraint; - } - - JPH_ASSERT(outConstraints == nullptr); - outConstraints = constraints; - JPH_ASSERT(outConstraintsEnd == nullptr); - outConstraintsEnd = constraint_ends; -} - -void IslandBuilder::SortIslands(TempAllocator *inTempAllocator) -{ - JPH_PROFILE_FUNCTION(); - - if (mNumContacts > 0 || mNumConstraints > 0) - { - // Allocate mapping table - JPH_ASSERT(mIslandsSorted == nullptr); - mIslandsSorted = (uint32 *)inTempAllocator->Allocate(mNumIslands * sizeof(uint32)); - - // Initialize index - for (uint32 island = 0; island < mNumIslands; ++island) - mIslandsSorted[island] = island; - - // Determine the sum of contact constraints / constraints per island - uint32 *num_constraints = (uint32 *)inTempAllocator->Allocate(mNumIslands * sizeof(uint32)); - if (mNumContacts > 0 && mNumConstraints > 0) - { - num_constraints[0] = mConstraintIslandEnds[0] + mContactIslandEnds[0]; - for (uint32 island = 1; island < mNumIslands; ++island) - num_constraints[island] = mConstraintIslandEnds[island] - mConstraintIslandEnds[island - 1] - + mContactIslandEnds[island] - mContactIslandEnds[island - 1]; - } - else if (mNumContacts > 0) - { - num_constraints[0] = mContactIslandEnds[0]; - for (uint32 island = 1; island < mNumIslands; ++island) - num_constraints[island] = mContactIslandEnds[island] - mContactIslandEnds[island - 1]; - } - else - { - num_constraints[0] = mConstraintIslandEnds[0]; - for (uint32 island = 1; island < mNumIslands; ++island) - num_constraints[island] = mConstraintIslandEnds[island] - mConstraintIslandEnds[island - 1]; - } - - // Sort so the biggest islands go first, this means that the jobs that take longest will be running - // first which improves the chance that all jobs finish at the same time. - QuickSort(mIslandsSorted, mIslandsSorted + mNumIslands, [num_constraints](uint32 inLHS, uint32 inRHS) { - return num_constraints[inLHS] > num_constraints[inRHS]; - }); - - inTempAllocator->Free(num_constraints, mNumIslands * sizeof(uint32)); - } -} - -void IslandBuilder::Finalize(const BodyID *inActiveBodies, uint32 inNumActiveBodies, uint32 inNumContacts, TempAllocator *inTempAllocator) -{ - JPH_PROFILE_FUNCTION(); - - mNumContacts = inNumContacts; - - BuildBodyIslands(inActiveBodies, inNumActiveBodies, inTempAllocator); - BuildConstraintIslands(mConstraintLinks, mNumConstraints, mConstraintIslands, mConstraintIslandEnds, inTempAllocator); - BuildConstraintIslands(mContactLinks, mNumContacts, mContactIslands, mContactIslandEnds, inTempAllocator); - SortIslands(inTempAllocator); - - mNumPositionSteps = (uint8 *)inTempAllocator->Allocate(mNumIslands * sizeof(uint8)); -} - -void IslandBuilder::GetBodiesInIsland(uint32 inIslandIndex, BodyID *&outBodiesBegin, BodyID *&outBodiesEnd) const -{ - JPH_ASSERT(inIslandIndex < mNumIslands); - uint32 sorted_index = mIslandsSorted != nullptr? mIslandsSorted[inIslandIndex] : inIslandIndex; - outBodiesBegin = sorted_index > 0? mBodyIslands + mBodyIslandEnds[sorted_index - 1] : mBodyIslands; - outBodiesEnd = mBodyIslands + mBodyIslandEnds[sorted_index]; -} - -bool IslandBuilder::GetConstraintsInIsland(uint32 inIslandIndex, uint32 *&outConstraintsBegin, uint32 *&outConstraintsEnd) const -{ - JPH_ASSERT(inIslandIndex < mNumIslands); - if (mNumConstraints == 0) - { - outConstraintsBegin = nullptr; - outConstraintsEnd = nullptr; - return false; - } - else - { - uint32 sorted_index = mIslandsSorted[inIslandIndex]; - outConstraintsBegin = sorted_index > 0? mConstraintIslands + mConstraintIslandEnds[sorted_index - 1] : mConstraintIslands; - outConstraintsEnd = mConstraintIslands + mConstraintIslandEnds[sorted_index]; - return outConstraintsBegin != outConstraintsEnd; - } -} - -bool IslandBuilder::GetContactsInIsland(uint32 inIslandIndex, uint32 *&outContactsBegin, uint32 *&outContactsEnd) const -{ - JPH_ASSERT(inIslandIndex < mNumIslands); - if (mNumContacts == 0) - { - outContactsBegin = nullptr; - outContactsEnd = nullptr; - return false; - } - else - { - uint32 sorted_index = mIslandsSorted[inIslandIndex]; - outContactsBegin = sorted_index > 0? mContactIslands + mContactIslandEnds[sorted_index - 1] : mContactIslands; - outContactsEnd = mContactIslands + mContactIslandEnds[sorted_index]; - return outContactsBegin != outContactsEnd; - } -} - -void IslandBuilder::ResetIslands(TempAllocator *inTempAllocator) -{ - JPH_PROFILE_FUNCTION(); - - inTempAllocator->Free(mNumPositionSteps, mNumIslands * sizeof(uint8)); - - if (mIslandsSorted != nullptr) - { - inTempAllocator->Free(mIslandsSorted, mNumIslands * sizeof(uint32)); - mIslandsSorted = nullptr; - } - - if (mContactIslands != nullptr) - { - inTempAllocator->Free(mContactIslandEnds, (mNumIslands + 1) * sizeof(uint32)); - mContactIslandEnds = nullptr; - inTempAllocator->Free(mContactIslands, mNumContacts * sizeof(uint32)); - mContactIslands = nullptr; - } - - if (mConstraintIslands != nullptr) - { - inTempAllocator->Free(mConstraintIslandEnds, (mNumIslands + 1) * sizeof(uint32)); - mConstraintIslandEnds = nullptr; - inTempAllocator->Free(mConstraintIslands, mNumConstraints * sizeof(uint32)); - mConstraintIslands = nullptr; - } - - inTempAllocator->Free(mBodyIslandEnds, (mNumActiveBodies + 1) * sizeof(uint32)); - mBodyIslandEnds = nullptr; - inTempAllocator->Free(mBodyIslands, mNumActiveBodies * sizeof(uint32)); - mBodyIslands = nullptr; - - inTempAllocator->Free(mConstraintLinks, mNumConstraints * sizeof(uint32)); - mConstraintLinks = nullptr; - -#ifdef JPH_VALIDATE_ISLAND_BUILDER - inTempAllocator->Free(mLinkValidation, mMaxContacts * sizeof(LinkValidation)); - mLinkValidation = nullptr; -#endif - - inTempAllocator->Free(mContactLinks, mMaxContacts * sizeof(uint32)); - mContactLinks = nullptr; - - mNumActiveBodies = 0; - mNumConstraints = 0; - mMaxContacts = 0; - mNumContacts = 0; - mNumIslands = 0; -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/IslandBuilder.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/IslandBuilder.h deleted file mode 100644 index 4c2f097d60c..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/IslandBuilder.h +++ /dev/null @@ -1,125 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -class TempAllocator; - -//#define JPH_VALIDATE_ISLAND_BUILDER - -/// Keeps track of connected bodies and builds islands for multithreaded velocity/position update -class IslandBuilder : public NonCopyable -{ -public: - /// Destructor - ~IslandBuilder(); - - /// Initialize the island builder with the maximum amount of bodies that could be active - void Init(uint32 inMaxActiveBodies); - - /// Prepare for simulation step by allocating space for the contact constraints - void PrepareContactConstraints(uint32 inMaxContactConstraints, TempAllocator *inTempAllocator); - - /// Prepare for simulation step by allocating space for the non-contact constraints - void PrepareNonContactConstraints(uint32 inNumConstraints, TempAllocator *inTempAllocator); - - /// Link two bodies by their index in the BodyManager::mActiveBodies list to form islands - void LinkBodies(uint32 inFirst, uint32 inSecond); - - /// Link a constraint to a body by their index in the BodyManager::mActiveBodies - void LinkConstraint(uint32 inConstraintIndex, uint32 inFirst, uint32 inSecond); - - /// Link a contact to a body by their index in the BodyManager::mActiveBodies - void LinkContact(uint32 inContactIndex, uint32 inFirst, uint32 inSecond); - - /// Finalize the islands after all bodies have been Link()-ed - void Finalize(const BodyID *inActiveBodies, uint32 inNumActiveBodies, uint32 inNumContacts, TempAllocator *inTempAllocator); - - /// Get the amount of islands formed - uint32 GetNumIslands() const { return mNumIslands; } - - /// Get iterator for a particular island, return false if there are no constraints - void GetBodiesInIsland(uint32 inIslandIndex, BodyID *&outBodiesBegin, BodyID *&outBodiesEnd) const; - bool GetConstraintsInIsland(uint32 inIslandIndex, uint32 *&outConstraintsBegin, uint32 *&outConstraintsEnd) const; - bool GetContactsInIsland(uint32 inIslandIndex, uint32 *&outContactsBegin, uint32 *&outContactsEnd) const; - - /// The number of position iterations for each island - void SetNumPositionSteps(uint32 inIslandIndex, uint inNumPositionSteps) { JPH_ASSERT(inIslandIndex < mNumIslands); JPH_ASSERT(inNumPositionSteps < 256); mNumPositionSteps[inIslandIndex] = uint8(inNumPositionSteps); } - uint GetNumPositionSteps(uint32 inIslandIndex) const { JPH_ASSERT(inIslandIndex < mNumIslands); return mNumPositionSteps[inIslandIndex]; } - - /// After you're done calling the three functions above, call this function to free associated data - void ResetIslands(TempAllocator *inTempAllocator); - -private: - /// Returns the index of the lowest body in the group - uint32 GetLowestBodyIndex(uint32 inActiveBodyIndex) const; - -#ifdef JPH_VALIDATE_ISLAND_BUILDER - /// Helper function to validate all islands so far generated - void ValidateIslands(uint32 inNumActiveBodies) const; -#endif - - // Helper functions to build various islands - void BuildBodyIslands(const BodyID *inActiveBodies, uint32 inNumActiveBodies, TempAllocator *inTempAllocator); - void BuildConstraintIslands(const uint32 *inConstraintToBody, uint32 inNumConstraints, uint32 *&outConstraints, uint32 *&outConstraintsEnd, TempAllocator *inTempAllocator) const; - - /// Sorts the islands so that the islands with most constraints go first - void SortIslands(TempAllocator *inTempAllocator); - - /// Intermediate data structure that for each body keeps track what the lowest index of the body is that it is connected to - struct BodyLink - { - JPH_OVERRIDE_NEW_DELETE - - atomic mLinkedTo; ///< An index in mBodyLinks pointing to another body in this island with a lower index than this body - uint32 mIslandIndex; ///< The island index of this body (filled in during Finalize) - }; - - // Intermediate data - BodyLink * mBodyLinks = nullptr; ///< Maps bodies to the first body in the island - uint32 * mConstraintLinks = nullptr; ///< Maps constraint index to body index (which maps to island index) - uint32 * mContactLinks = nullptr; ///< Maps contact constraint index to body index (which maps to island index) - - // Final data - BodyID * mBodyIslands = nullptr; ///< Bodies ordered by island - uint32 * mBodyIslandEnds = nullptr; ///< End index of each body island - - uint32 * mConstraintIslands = nullptr; ///< Constraints ordered by island - uint32 * mConstraintIslandEnds = nullptr; ///< End index of each constraint island - - uint32 * mContactIslands = nullptr; ///< Contacts ordered by island - uint32 * mContactIslandEnds = nullptr; ///< End index of each contact island - - uint32 * mIslandsSorted = nullptr; ///< A list of island indices in order of most constraints first - - uint8 * mNumPositionSteps = nullptr; ///< Number of position steps for each island - - // Counters - uint32 mMaxActiveBodies; ///< Maximum size of the active bodies list (see BodyManager::mActiveBodies) - uint32 mNumActiveBodies = 0; ///< Number of active bodies passed to - uint32 mNumConstraints = 0; ///< Size of the constraint list (see ConstraintManager::mConstraints) - uint32 mMaxContacts = 0; ///< Maximum amount of contacts supported - uint32 mNumContacts = 0; ///< Size of the contacts list (see ContactConstraintManager::mNumConstraints) - uint32 mNumIslands = 0; ///< Final number of islands - -#ifdef JPH_VALIDATE_ISLAND_BUILDER - /// Structure to keep track of all added links to validate that islands were generated correctly - struct LinkValidation - { - uint32 mFirst; - uint32 mSecond; - }; - - LinkValidation * mLinkValidation = nullptr; - atomic mNumLinkValidation; -#endif -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/LargeIslandSplitter.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/LargeIslandSplitter.cpp deleted file mode 100644 index ebe6fbfea21..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/LargeIslandSplitter.cpp +++ /dev/null @@ -1,579 +0,0 @@ -// SPDX-FileCopyrightText: 2023 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -//#define JPH_LARGE_ISLAND_SPLITTER_DEBUG - -JPH_NAMESPACE_BEGIN - -LargeIslandSplitter::EStatus LargeIslandSplitter::Splits::FetchNextBatch(uint32 &outConstraintsBegin, uint32 &outConstraintsEnd, uint32 &outContactsBegin, uint32 &outContactsEnd, bool &outFirstIteration) -{ - { - // First check if we can get a new batch (doing a relaxed read to avoid hammering an atomic with an atomic subtract) - // Note this also avoids overflowing the status counter if we're done but there's still one thread processing items - uint64 status = mStatus.load(memory_order_relaxed); - if (sGetIteration(status) >= mNumIterations) - return EStatus::AllBatchesDone; - - // Check for special value that indicates that the splits are still being built - // (note we do not check for this condition again below as we reset all splits before kicking off jobs that fetch batches of work) - if (status == StatusItemMask) - return EStatus::WaitingForBatch; - - uint item = sGetItem(status); - uint split_index = sGetSplit(status); - if (split_index == cNonParallelSplitIdx) - { - // Non parallel split needs to be taken as a single batch, only the thread that takes element 0 will do it - if (item != 0) - return EStatus::WaitingForBatch; - } - else - { - // Parallel split is split into batches - JPH_ASSERT(split_index < mNumSplits); - const Split &split = mSplits[split_index]; - if (item >= split.GetNumItems()) - return EStatus::WaitingForBatch; - } - } - - // Then try to actually get the batch - uint64 status = mStatus.fetch_add(cBatchSize, memory_order_acquire); - int iteration = sGetIteration(status); - if (iteration >= mNumIterations) - return EStatus::AllBatchesDone; - - uint split_index = sGetSplit(status); - JPH_ASSERT(split_index < mNumSplits || split_index == cNonParallelSplitIdx); - const Split &split = mSplits[split_index]; - uint item_begin = sGetItem(status); - if (split_index == cNonParallelSplitIdx) - { - if (item_begin == 0) - { - // Non-parallel split always goes as a single batch - outConstraintsBegin = split.mConstraintBufferBegin; - outConstraintsEnd = split.mConstraintBufferEnd; - outContactsBegin = split.mContactBufferBegin; - outContactsEnd = split.mContactBufferEnd; - outFirstIteration = iteration == 0; - return EStatus::BatchRetrieved; - } - else - { - // Otherwise we're done with this split - return EStatus::WaitingForBatch; - } - } - - // Parallel split is split into batches - uint num_constraints = split.GetNumConstraints(); - uint num_contacts = split.GetNumContacts(); - uint num_items = num_constraints + num_contacts; - if (item_begin >= num_items) - return EStatus::WaitingForBatch; - - uint item_end = min(item_begin + cBatchSize, num_items); - if (item_end >= num_constraints) - { - if (item_begin < num_constraints) - { - // Partially from constraints and partially from contacts - outConstraintsBegin = split.mConstraintBufferBegin + item_begin; - outConstraintsEnd = split.mConstraintBufferEnd; - } - else - { - // Only contacts - outConstraintsBegin = 0; - outConstraintsEnd = 0; - } - - outContactsBegin = split.mContactBufferBegin + (max(item_begin, num_constraints) - num_constraints); - outContactsEnd = split.mContactBufferBegin + (item_end - num_constraints); - } - else - { - // Only constraints - outConstraintsBegin = split.mConstraintBufferBegin + item_begin; - outConstraintsEnd = split.mConstraintBufferBegin + item_end; - - outContactsBegin = 0; - outContactsEnd = 0; - } - - outFirstIteration = iteration == 0; - return EStatus::BatchRetrieved; -} - -void LargeIslandSplitter::Splits::MarkBatchProcessed(uint inNumProcessed, bool &outLastIteration, bool &outFinalBatch) -{ - // We fetched this batch, nobody should change the split and or iteration until we mark the last batch as processed so we can safely get the current status - uint64 status = mStatus.load(memory_order_relaxed); - uint split_index = sGetSplit(status); - JPH_ASSERT(split_index < mNumSplits || split_index == cNonParallelSplitIdx); - const Split &split = mSplits[split_index]; - uint num_items_in_split = split.GetNumItems(); - - // Determine if this is the last iteration before possibly incrementing it - int iteration = sGetIteration(status); - outLastIteration = iteration == mNumIterations - 1; - - // Add the number of items we processed to the total number of items processed - // Note: This needs to happen after we read the status as other threads may update the status after we mark items as processed - JPH_ASSERT(inNumProcessed > 0); // Logic will break if we mark a block of 0 items as processed - uint total_items_processed = mItemsProcessed.fetch_add(inNumProcessed, memory_order_acq_rel) + inNumProcessed; - - // Check if we're at the end of the split - if (total_items_processed >= num_items_in_split) - { - JPH_ASSERT(total_items_processed == num_items_in_split); // Should not overflow, that means we're retiring more items than we should process - - // Set items processed back to 0 for the next split/iteration - mItemsProcessed.store(0, memory_order_release); - - // Determine next split - do - { - if (split_index == cNonParallelSplitIdx) - { - // At start of next iteration - split_index = 0; - ++iteration; - } - else - { - // At start of next split - ++split_index; - } - - // If we're beyond the end of splits, go to the non-parallel split - if (split_index >= mNumSplits) - split_index = cNonParallelSplitIdx; - } - while (iteration < mNumIterations - && mSplits[split_index].GetNumItems() == 0); // We don't support processing empty splits, skip to the next split in this case - - mStatus.store((uint64(iteration) << StatusIterationShift) | (uint64(split_index) << StatusSplitShift), memory_order_release); - } - - // Track if this is the final batch - outFinalBatch = iteration >= mNumIterations; -} - -LargeIslandSplitter::~LargeIslandSplitter() -{ - JPH_ASSERT(mSplitMasks == nullptr); - JPH_ASSERT(mContactAndConstraintsSplitIdx == nullptr); - JPH_ASSERT(mContactAndConstraintIndices == nullptr); - JPH_ASSERT(mSplitIslands == nullptr); -} - -void LargeIslandSplitter::Prepare(const IslandBuilder &inIslandBuilder, uint32 inNumActiveBodies, TempAllocator *inTempAllocator) -{ - JPH_PROFILE_FUNCTION(); - - // Count the total number of constraints and contacts that we will be putting in splits - mContactAndConstraintsSize = 0; - for (uint32 island = 0; island < inIslandBuilder.GetNumIslands(); ++island) - { - // Get the contacts in this island - uint32 *contacts_start, *contacts_end; - inIslandBuilder.GetContactsInIsland(island, contacts_start, contacts_end); - uint num_contacts_in_island = uint(contacts_end - contacts_start); - - // Get the constraints in this island - uint32 *constraints_start, *constraints_end; - inIslandBuilder.GetConstraintsInIsland(island, constraints_start, constraints_end); - uint num_constraints_in_island = uint(constraints_end - constraints_start); - - uint island_size = num_contacts_in_island + num_constraints_in_island; - if (island_size >= cLargeIslandTreshold) - { - mNumSplitIslands++; - mContactAndConstraintsSize += island_size; - } - else - break; // If this island doesn't have enough constraints, the next islands won't either since they're sorted from big to small - } - - if (mContactAndConstraintsSize > 0) - { - mNumActiveBodies = inNumActiveBodies; - - // Allocate split mask buffer - mSplitMasks = (SplitMask *)inTempAllocator->Allocate(mNumActiveBodies * sizeof(SplitMask)); - - // Allocate contact and constraint buffer - uint contact_and_constraint_indices_size = mContactAndConstraintsSize * sizeof(uint32); - mContactAndConstraintsSplitIdx = (uint32 *)inTempAllocator->Allocate(contact_and_constraint_indices_size); - mContactAndConstraintIndices = (uint32 *)inTempAllocator->Allocate(contact_and_constraint_indices_size); - - // Allocate island split buffer - mSplitIslands = (Splits *)inTempAllocator->Allocate(mNumSplitIslands * sizeof(Splits)); - - // Prevent any of the splits from being picked up as work - for (uint i = 0; i < mNumSplitIslands; ++i) - mSplitIslands[i].ResetStatus(); - } -} - -uint LargeIslandSplitter::AssignSplit(const Body *inBody1, const Body *inBody2) -{ - uint32 idx1 = inBody1->GetIndexInActiveBodiesInternal(); - uint32 idx2 = inBody2->GetIndexInActiveBodiesInternal(); - - // Test if either index is negative - if (idx1 == Body::cInactiveIndex || !inBody1->IsDynamic()) - { - // Body 1 is not active or a kinematic body, so we only need to set 1 body - JPH_ASSERT(idx2 < mNumActiveBodies); - SplitMask &mask = mSplitMasks[idx2]; - uint split = min(CountTrailingZeros(~uint32(mask)), cNonParallelSplitIdx); - mask |= SplitMask(1U << split); - return split; - } - else if (idx2 == Body::cInactiveIndex || !inBody2->IsDynamic()) - { - // Body 2 is not active or a kinematic body, so we only need to set 1 body - JPH_ASSERT(idx1 < mNumActiveBodies); - SplitMask &mask = mSplitMasks[idx1]; - uint split = min(CountTrailingZeros(~uint32(mask)), cNonParallelSplitIdx); - mask |= SplitMask(1U << split); - return split; - } - else - { - // If both bodies are active, we need to set 2 bodies - JPH_ASSERT(idx1 < mNumActiveBodies); - JPH_ASSERT(idx2 < mNumActiveBodies); - SplitMask &mask1 = mSplitMasks[idx1]; - SplitMask &mask2 = mSplitMasks[idx2]; - uint split = min(CountTrailingZeros((~uint32(mask1)) & (~uint32(mask2))), cNonParallelSplitIdx); - SplitMask mask = SplitMask(1U << split); - mask1 |= mask; - mask2 |= mask; - return split; - } -} - -uint LargeIslandSplitter::AssignToNonParallelSplit(const Body *inBody) -{ - uint32 idx = inBody->GetIndexInActiveBodiesInternal(); - if (idx != Body::cInactiveIndex) - { - JPH_ASSERT(idx < mNumActiveBodies); - mSplitMasks[idx] |= 1U << cNonParallelSplitIdx; - } - - return cNonParallelSplitIdx; -} - -bool LargeIslandSplitter::SplitIsland(uint32 inIslandIndex, const IslandBuilder &inIslandBuilder, const BodyManager &inBodyManager, const ContactConstraintManager &inContactManager, Constraint **inActiveConstraints, CalculateSolverSteps &ioStepsCalculator) -{ - JPH_PROFILE_FUNCTION(); - - // Get the contacts in this island - uint32 *contacts_start, *contacts_end; - inIslandBuilder.GetContactsInIsland(inIslandIndex, contacts_start, contacts_end); - uint num_contacts_in_island = uint(contacts_end - contacts_start); - - // Get the constraints in this island - uint32 *constraints_start, *constraints_end; - inIslandBuilder.GetConstraintsInIsland(inIslandIndex, constraints_start, constraints_end); - uint num_constraints_in_island = uint(constraints_end - constraints_start); - - // Check if it exceeds the threshold - uint island_size = num_contacts_in_island + num_constraints_in_island; - if (island_size < cLargeIslandTreshold) - return false; - - // Get bodies in this island - BodyID *bodies_start, *bodies_end; - inIslandBuilder.GetBodiesInIsland(inIslandIndex, bodies_start, bodies_end); - - // Reset the split mask for all bodies in this island - Body const * const *bodies = inBodyManager.GetBodies().data(); - for (const BodyID *b = bodies_start; b < bodies_end; ++b) - mSplitMasks[bodies[b->GetIndex()]->GetIndexInActiveBodiesInternal()] = 0; - - // Count the number of contacts and constraints per split - uint num_contacts_in_split[cNumSplits] = { }; - uint num_constraints_in_split[cNumSplits] = { }; - - // Get space to store split indices - uint offset = mContactAndConstraintsNextFree.fetch_add(island_size, memory_order_relaxed); - uint32 *contact_split_idx = mContactAndConstraintsSplitIdx + offset; - uint32 *constraint_split_idx = contact_split_idx + num_contacts_in_island; - - // Assign the contacts to a split - uint32 *cur_contact_split_idx = contact_split_idx; - for (const uint32 *c = contacts_start; c < contacts_end; ++c) - { - const Body *body1, *body2; - inContactManager.GetAffectedBodies(*c, body1, body2); - uint split = AssignSplit(body1, body2); - num_contacts_in_split[split]++; - *cur_contact_split_idx++ = split; - - if (body1->IsDynamic()) - ioStepsCalculator(body1->GetMotionPropertiesUnchecked()); - if (body2->IsDynamic()) - ioStepsCalculator(body2->GetMotionPropertiesUnchecked()); - } - - // Assign the constraints to a split - uint32 *cur_constraint_split_idx = constraint_split_idx; - for (const uint32 *c = constraints_start; c < constraints_end; ++c) - { - const Constraint *constraint = inActiveConstraints[*c]; - uint split = constraint->BuildIslandSplits(*this); - num_constraints_in_split[split]++; - *cur_constraint_split_idx++ = split; - - ioStepsCalculator(constraint); - } - - ioStepsCalculator.Finalize(); - - // Start with 0 splits - uint split_remap_table[cNumSplits]; - uint new_split_idx = mNextSplitIsland.fetch_add(1, memory_order_relaxed); - JPH_ASSERT(new_split_idx < mNumSplitIslands); - Splits &splits = mSplitIslands[new_split_idx]; - splits.mIslandIndex = inIslandIndex; - splits.mNumSplits = 0; - splits.mNumIterations = ioStepsCalculator.GetNumVelocitySteps() + 1; // Iteration 0 is used for warm starting - splits.mNumVelocitySteps = ioStepsCalculator.GetNumVelocitySteps(); - splits.mNumPositionSteps = ioStepsCalculator.GetNumPositionSteps(); - splits.mItemsProcessed.store(0, memory_order_release); - - // Allocate space to store the sorted constraint and contact indices per split - uint32 *constraint_buffer_cur[cNumSplits], *contact_buffer_cur[cNumSplits]; - for (uint s = 0; s < cNumSplits; ++s) - { - // If this split doesn't contain enough constraints and contacts, we will combine it with the non parallel split - if (num_constraints_in_split[s] + num_contacts_in_split[s] < cSplitCombineTreshold - && s < cNonParallelSplitIdx) // The non-parallel split cannot merge into itself - { - // Remap it - split_remap_table[s] = cNonParallelSplitIdx; - - // Add the counts to the non parallel split - num_contacts_in_split[cNonParallelSplitIdx] += num_contacts_in_split[s]; - num_constraints_in_split[cNonParallelSplitIdx] += num_constraints_in_split[s]; - } - else - { - // This split is valid, map it to the next empty slot - uint target_split; - if (s < cNonParallelSplitIdx) - target_split = splits.mNumSplits++; - else - target_split = cNonParallelSplitIdx; - Split &split = splits.mSplits[target_split]; - split_remap_table[s] = target_split; - - // Allocate space for contacts - split.mContactBufferBegin = offset; - split.mContactBufferEnd = split.mContactBufferBegin + num_contacts_in_split[s]; - - // Allocate space for constraints - split.mConstraintBufferBegin = split.mContactBufferEnd; - split.mConstraintBufferEnd = split.mConstraintBufferBegin + num_constraints_in_split[s]; - - // Store start for each split - contact_buffer_cur[target_split] = mContactAndConstraintIndices + split.mContactBufferBegin; - constraint_buffer_cur[target_split] = mContactAndConstraintIndices + split.mConstraintBufferBegin; - - // Update offset - offset = split.mConstraintBufferEnd; - } - } - - // Split the contacts - for (uint c = 0; c < num_contacts_in_island; ++c) - { - uint split = split_remap_table[contact_split_idx[c]]; - *contact_buffer_cur[split]++ = contacts_start[c]; - } - - // Split the constraints - for (uint c = 0; c < num_constraints_in_island; ++c) - { - uint split = split_remap_table[constraint_split_idx[c]]; - *constraint_buffer_cur[split]++ = constraints_start[c]; - } - -#ifdef JPH_LARGE_ISLAND_SPLITTER_DEBUG - // Trace the size of all splits - uint sum = 0; - String stats; - for (uint s = 0; s < cNumSplits; ++s) - { - // If we've processed all splits, jump to the non-parallel split - if (s >= splits.GetNumSplits()) - s = cNonParallelSplitIdx; - - const Split &split = splits.mSplits[s]; - stats += StringFormat("g:%d:%d:%d, ", s, split.GetNumContacts(), split.GetNumConstraints()); - sum += split.GetNumItems(); - } - stats += StringFormat("sum: %d", sum); - Trace(stats.c_str()); -#endif // JPH_LARGE_ISLAND_SPLITTER_DEBUG - -#ifdef JPH_ENABLE_ASSERTS - for (uint s = 0; s < cNumSplits; ++s) - { - // If there are no more splits, process the non-parallel split - if (s >= splits.mNumSplits) - s = cNonParallelSplitIdx; - - // Check that we wrote all elements - Split &split = splits.mSplits[s]; - JPH_ASSERT(contact_buffer_cur[s] == mContactAndConstraintIndices + split.mContactBufferEnd); - JPH_ASSERT(constraint_buffer_cur[s] == mContactAndConstraintIndices + split.mConstraintBufferEnd); - } - -#ifdef _DEBUG - // Validate that the splits are indeed not touching the same body - for (uint s = 0; s < splits.mNumSplits; ++s) - { - Array body_used(mNumActiveBodies, false); - - // Validate contacts - uint32 split_contacts_begin, split_contacts_end; - splits.GetContactsInSplit(s, split_contacts_begin, split_contacts_end); - for (uint32 *c = mContactAndConstraintIndices + split_contacts_begin; c < mContactAndConstraintIndices + split_contacts_end; ++c) - { - const Body *body1, *body2; - inContactManager.GetAffectedBodies(*c, body1, body2); - - uint32 idx1 = body1->GetIndexInActiveBodiesInternal(); - if (idx1 != Body::cInactiveIndex && body1->IsDynamic()) - { - JPH_ASSERT(!body_used[idx1]); - body_used[idx1] = true; - } - - uint32 idx2 = body2->GetIndexInActiveBodiesInternal(); - if (idx2 != Body::cInactiveIndex && body2->IsDynamic()) - { - JPH_ASSERT(!body_used[idx2]); - body_used[idx2] = true; - } - } - } -#endif // _DEBUG -#endif // JPH_ENABLE_ASSERTS - - // Allow other threads to pick up this split island now - splits.StartFirstBatch(); - return true; -} - -LargeIslandSplitter::EStatus LargeIslandSplitter::FetchNextBatch(uint &outSplitIslandIndex, uint32 *&outConstraintsBegin, uint32 *&outConstraintsEnd, uint32 *&outContactsBegin, uint32 *&outContactsEnd, bool &outFirstIteration) -{ - // We can't be done when all islands haven't been submitted yet - uint num_splits_created = mNextSplitIsland.load(memory_order_acquire); - bool all_done = num_splits_created == mNumSplitIslands; - - // Loop over all split islands to find work - uint32 constraints_begin, constraints_end, contacts_begin, contacts_end; - for (Splits *s = mSplitIslands; s < mSplitIslands + num_splits_created; ++s) - switch (s->FetchNextBatch(constraints_begin, constraints_end, contacts_begin, contacts_end, outFirstIteration)) - { - case EStatus::AllBatchesDone: - break; - - case EStatus::WaitingForBatch: - all_done = false; - break; - - case EStatus::BatchRetrieved: - outSplitIslandIndex = uint(s - mSplitIslands); - outConstraintsBegin = mContactAndConstraintIndices + constraints_begin; - outConstraintsEnd = mContactAndConstraintIndices + constraints_end; - outContactsBegin = mContactAndConstraintIndices + contacts_begin; - outContactsEnd = mContactAndConstraintIndices + contacts_end; - return EStatus::BatchRetrieved; - } - - return all_done? EStatus::AllBatchesDone : EStatus::WaitingForBatch; -} - -void LargeIslandSplitter::MarkBatchProcessed(uint inSplitIslandIndex, const uint32 *inConstraintsBegin, const uint32 *inConstraintsEnd, const uint32 *inContactsBegin, const uint32 *inContactsEnd, bool &outLastIteration, bool &outFinalBatch) -{ - uint num_items_processed = uint(inConstraintsEnd - inConstraintsBegin) + uint(inContactsEnd - inContactsBegin); - - JPH_ASSERT(inSplitIslandIndex < mNextSplitIsland.load(memory_order_relaxed)); - Splits &splits = mSplitIslands[inSplitIslandIndex]; - splits.MarkBatchProcessed(num_items_processed, outLastIteration, outFinalBatch); -} - -void LargeIslandSplitter::PrepareForSolvePositions() -{ - for (Splits *s = mSplitIslands, *s_end = mSplitIslands + mNumSplitIslands; s < s_end; ++s) - { - // Set the number of iterations to the number of position steps - s->mNumIterations = s->mNumPositionSteps; - - // We can start again from the first batch - s->StartFirstBatch(); - } -} - -void LargeIslandSplitter::Reset(TempAllocator *inTempAllocator) -{ - JPH_PROFILE_FUNCTION(); - - // Everything should have been used - JPH_ASSERT(mContactAndConstraintsNextFree.load(memory_order_relaxed) == mContactAndConstraintsSize); - JPH_ASSERT(mNextSplitIsland.load(memory_order_relaxed) == mNumSplitIslands); - - // Free split islands - if (mNumSplitIslands > 0) - { - inTempAllocator->Free(mSplitIslands, mNumSplitIslands * sizeof(Splits)); - mSplitIslands = nullptr; - - mNumSplitIslands = 0; - mNextSplitIsland.store(0, memory_order_relaxed); - } - - // Free contact and constraint buffers - if (mContactAndConstraintsSize > 0) - { - inTempAllocator->Free(mContactAndConstraintIndices, mContactAndConstraintsSize * sizeof(uint32)); - mContactAndConstraintIndices = nullptr; - - inTempAllocator->Free(mContactAndConstraintsSplitIdx, mContactAndConstraintsSize * sizeof(uint32)); - mContactAndConstraintsSplitIdx = nullptr; - - mContactAndConstraintsSize = 0; - mContactAndConstraintsNextFree.store(0, memory_order_relaxed); - } - - // Free split masks - if (mSplitMasks != nullptr) - { - inTempAllocator->Free(mSplitMasks, mNumActiveBodies * sizeof(SplitMask)); - mSplitMasks = nullptr; - - mNumActiveBodies = 0; - } -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/LargeIslandSplitter.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/LargeIslandSplitter.h deleted file mode 100644 index 8e61d093055..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/LargeIslandSplitter.h +++ /dev/null @@ -1,185 +0,0 @@ -// SPDX-FileCopyrightText: 2023 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include - -JPH_NAMESPACE_BEGIN - -class Body; -class BodyID; -class IslandBuilder; -class TempAllocator; -class Constraint; -class BodyManager; -class ContactConstraintManager; -class CalculateSolverSteps; - -/// Assigns bodies in large islands to multiple groups that can run in parallel -/// -/// This basically implements what is described in: High-Performance Physical Simulations on Next-Generation Architecture with Many Cores by Chen et al. -/// See: http://web.eecs.umich.edu/~msmelyan/papers/physsim_onmanycore_itj.pdf section "PARALLELIZATION METHODOLOGY" -class LargeIslandSplitter : public NonCopyable -{ -private: - using SplitMask = uint32; - -public: - static constexpr uint cNumSplits = sizeof(SplitMask) * 8; - static constexpr uint cNonParallelSplitIdx = cNumSplits - 1; - static constexpr uint cLargeIslandTreshold = 128; ///< If the number of constraints + contacts in an island is larger than this, we will try to split the island - - /// Status code for retrieving a batch - enum class EStatus - { - WaitingForBatch, ///< Work is expected to be available later - BatchRetrieved, ///< Work is being returned - AllBatchesDone, ///< No further work is expected from this - }; - - /// Describes a split of constraints and contacts - struct Split - { - inline uint GetNumContacts() const { return mContactBufferEnd - mContactBufferBegin; } - inline uint GetNumConstraints() const { return mConstraintBufferEnd - mConstraintBufferBegin; } - inline uint GetNumItems() const { return GetNumContacts() + GetNumConstraints(); } - - uint32 mContactBufferBegin; ///< Begin of the contact buffer (offset relative to mContactAndConstraintIndices) - uint32 mContactBufferEnd; ///< End of the contact buffer - - uint32 mConstraintBufferBegin; ///< Begin of the constraint buffer (offset relative to mContactAndConstraintIndices) - uint32 mConstraintBufferEnd; ///< End of the constraint buffer - }; - - /// Structure that describes the resulting splits from the large island splitter - class Splits - { - public: - inline uint GetNumSplits() const - { - return mNumSplits; - } - - inline void GetConstraintsInSplit(uint inSplitIndex, uint32 &outConstraintsBegin, uint32 &outConstraintsEnd) const - { - const Split &split = mSplits[inSplitIndex]; - outConstraintsBegin = split.mConstraintBufferBegin; - outConstraintsEnd = split.mConstraintBufferEnd; - } - - inline void GetContactsInSplit(uint inSplitIndex, uint32 &outContactsBegin, uint32 &outContactsEnd) const - { - const Split &split = mSplits[inSplitIndex]; - outContactsBegin = split.mContactBufferBegin; - outContactsEnd = split.mContactBufferEnd; - } - - /// Reset current status so that no work can be picked up from this split - inline void ResetStatus() - { - mStatus.store(StatusItemMask, memory_order_relaxed); - } - - /// Make the first batch available to other threads - inline void StartFirstBatch() - { - uint split_index = mNumSplits > 0? 0 : cNonParallelSplitIdx; - mStatus.store(uint64(split_index) << StatusSplitShift, memory_order_release); - } - - /// Fetch the next batch to process - EStatus FetchNextBatch(uint32 &outConstraintsBegin, uint32 &outConstraintsEnd, uint32 &outContactsBegin, uint32 &outContactsEnd, bool &outFirstIteration); - - /// Mark a batch as processed - void MarkBatchProcessed(uint inNumProcessed, bool &outLastIteration, bool &outFinalBatch); - - enum EIterationStatus : uint64 - { - StatusIterationMask = 0xffff000000000000, - StatusIterationShift = 48, - StatusSplitMask = 0x0000ffff00000000, - StatusSplitShift = 32, - StatusItemMask = 0x00000000ffffffff, - }; - - static inline int sGetIteration(uint64 inStatus) - { - return int((inStatus & StatusIterationMask) >> StatusIterationShift); - } - - static inline uint sGetSplit(uint64 inStatus) - { - return uint((inStatus & StatusSplitMask) >> StatusSplitShift); - } - - static inline uint sGetItem(uint64 inStatus) - { - return uint(inStatus & StatusItemMask); - } - - Split mSplits[cNumSplits]; ///< Data per split - uint32 mIslandIndex; ///< Index of the island that was split - uint mNumSplits; ///< Number of splits that were created (excluding the non-parallel split) - int mNumIterations; ///< Number of iterations to do - int mNumVelocitySteps; ///< Number of velocity steps to do (cached for 2nd sub step) - int mNumPositionSteps; ///< Number of position steps to do - atomic mStatus; ///< Status of the split, see EIterationStatus - atomic mItemsProcessed; ///< Number of items that have been marked as processed - }; - -public: - /// Destructor - ~LargeIslandSplitter(); - - /// Prepare the island splitter by allocating memory - void Prepare(const IslandBuilder &inIslandBuilder, uint32 inNumActiveBodies, TempAllocator *inTempAllocator); - - /// Assign two bodies to a split. Returns the split index. - uint AssignSplit(const Body *inBody1, const Body *inBody2); - - /// Force a body to be in a non parallel split. Returns the split index. - uint AssignToNonParallelSplit(const Body *inBody); - - /// Splits up an island, the created splits will be added to the list of batches and can be fetched with FetchNextBatch. Returns false if the island did not need splitting. - bool SplitIsland(uint32 inIslandIndex, const IslandBuilder &inIslandBuilder, const BodyManager &inBodyManager, const ContactConstraintManager &inContactManager, Constraint **inActiveConstraints, CalculateSolverSteps &ioStepsCalculator); - - /// Fetch the next batch to process, returns a handle in outSplitIslandIndex that must be provided to MarkBatchProcessed when complete - EStatus FetchNextBatch(uint &outSplitIslandIndex, uint32 *&outConstraintsBegin, uint32 *&outConstraintsEnd, uint32 *&outContactsBegin, uint32 *&outContactsEnd, bool &outFirstIteration); - - /// Mark a batch as processed - void MarkBatchProcessed(uint inSplitIslandIndex, const uint32 *inConstraintsBegin, const uint32 *inConstraintsEnd, const uint32 *inContactsBegin, const uint32 *inContactsEnd, bool &outLastIteration, bool &outFinalBatch); - - /// Get the island index of the island that was split for a particular split island index - inline uint32 GetIslandIndex(uint inSplitIslandIndex) const - { - JPH_ASSERT(inSplitIslandIndex < mNumSplitIslands); - return mSplitIslands[inSplitIslandIndex].mIslandIndex; - } - - /// Prepare the island splitter for iterating over the split islands again for position solving. Marks all batches as startable. - void PrepareForSolvePositions(); - - /// Reset the island splitter - void Reset(TempAllocator *inTempAllocator); - -private: - static constexpr uint cSplitCombineTreshold = 32; ///< If the number of constraints + contacts in a split is lower than this, we will merge this split into the 'non-parallel split' - static constexpr uint cBatchSize = 16; ///< Number of items to process in a constraint batch - - uint32 mNumActiveBodies = 0; ///< Cached number of active bodies - - SplitMask * mSplitMasks = nullptr; ///< Bits that indicate for each body in the BodyManager::mActiveBodies list which split they already belong to - - uint32 * mContactAndConstraintsSplitIdx = nullptr; ///< Buffer to store the split index per constraint or contact - uint32 * mContactAndConstraintIndices = nullptr; ///< Buffer to store the ordered constraint indices per split - uint mContactAndConstraintsSize = 0; ///< Total size of mContactAndConstraintsSplitIdx and mContactAndConstraintIndices - atomic mContactAndConstraintsNextFree { 0 }; ///< Next element that is free in both buffers - - uint mNumSplitIslands = 0; ///< Total number of islands that required splitting - Splits * mSplitIslands = nullptr; ///< List of islands that required splitting - atomic mNextSplitIsland = 0; ///< Next split island to pick from mSplitIslands -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsLock.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsLock.cpp deleted file mode 100644 index 690b5e58232..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsLock.cpp +++ /dev/null @@ -1,17 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include - -#ifdef JPH_ENABLE_ASSERTS - -JPH_NAMESPACE_BEGIN - -thread_local PhysicsLock::LockData PhysicsLock::sLocks[4]; - -JPH_NAMESPACE_END - -#endif diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsLock.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsLock.h deleted file mode 100644 index 62cf72e9962..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsLock.h +++ /dev/null @@ -1,169 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -#ifdef JPH_ENABLE_ASSERTS - -/// This is the list of locks used by the physics engine, they need to be locked in a particular order (from top of the list to bottom of the list) in order to prevent deadlocks -enum class EPhysicsLockTypes -{ - BroadPhaseQuery = 1 << 0, - PerBody = 1 << 1, - BodiesList = 1 << 2, - BroadPhaseUpdate = 1 << 3, - ConstraintsList = 1 << 4, - ActiveBodiesList = 1 << 5, -}; - -/// A token that indicates the context of a lock (we use 1 per physics system and we use the body manager pointer because it's convenient) -class BodyManager; -using PhysicsLockContext = const BodyManager *; - -#endif // !JPH_ENABLE_ASSERTS - -/// Helpers to safely lock the different mutexes that are part of the physics system while preventing deadlock -/// Class that keeps track per thread which lock are taken and if the order of locking is correct -class PhysicsLock -{ -public: -#ifdef JPH_ENABLE_ASSERTS - /// Call before taking the lock - static inline void sCheckLock(PhysicsLockContext inContext, EPhysicsLockTypes inType) - { - uint32 &mutexes = sGetLockedMutexes(inContext); - JPH_ASSERT(uint32(inType) > mutexes, "A lock of same or higher priority was already taken, this can create a deadlock!"); - mutexes = mutexes | uint32(inType); - } - - /// Call after releasing the lock - static inline void sCheckUnlock(PhysicsLockContext inContext, EPhysicsLockTypes inType) - { - uint32 &mutexes = sGetLockedMutexes(inContext); - JPH_ASSERT((mutexes & uint32(inType)) != 0, "Mutex was not locked!"); - mutexes = mutexes & ~uint32(inType); - } -#endif // !JPH_ENABLE_ASSERTS - - template - static inline void sLock(LockType &inMutex JPH_IF_ENABLE_ASSERTS(, PhysicsLockContext inContext, EPhysicsLockTypes inType)) - { - JPH_IF_ENABLE_ASSERTS(sCheckLock(inContext, inType);) - inMutex.lock(); - } - - template - static inline void sUnlock(LockType &inMutex JPH_IF_ENABLE_ASSERTS(, PhysicsLockContext inContext, EPhysicsLockTypes inType)) - { - JPH_IF_ENABLE_ASSERTS(sCheckUnlock(inContext, inType);) - inMutex.unlock(); - } - - template - static inline void sLockShared(LockType &inMutex JPH_IF_ENABLE_ASSERTS(, PhysicsLockContext inContext, EPhysicsLockTypes inType)) - { - JPH_IF_ENABLE_ASSERTS(sCheckLock(inContext, inType);) - inMutex.lock_shared(); - } - - template - static inline void sUnlockShared(LockType &inMutex JPH_IF_ENABLE_ASSERTS(, PhysicsLockContext inContext, EPhysicsLockTypes inType)) - { - JPH_IF_ENABLE_ASSERTS(sCheckUnlock(inContext, inType);) - inMutex.unlock_shared(); - } - -#ifdef JPH_ENABLE_ASSERTS -private: - struct LockData - { - uint32 mLockedMutexes = 0; - PhysicsLockContext mContext = nullptr; - }; - - static thread_local LockData sLocks[4]; - - // Helper function to find the locked mutexes for a particular context - static uint32 & sGetLockedMutexes(PhysicsLockContext inContext) - { - // If we find a matching context we can use it - for (LockData &l : sLocks) - if (l.mContext == inContext) - return l.mLockedMutexes; - - // Otherwise we look for an entry that is not in use - for (LockData &l : sLocks) - if (l.mLockedMutexes == 0) - { - l.mContext = inContext; - return l.mLockedMutexes; - } - - JPH_ASSERT(false, "Too many physics systems locked at the same time!"); - return sLocks[0].mLockedMutexes; - } -#endif // !JPH_ENABLE_ASSERTS -}; - -/// Helper class that is similar to std::unique_lock -template -class UniqueLock : public NonCopyable -{ -public: - explicit UniqueLock(LockType &inLock JPH_IF_ENABLE_ASSERTS(, PhysicsLockContext inContext, EPhysicsLockTypes inType)) : - mLock(inLock) -#ifdef JPH_ENABLE_ASSERTS - , mContext(inContext), - mType(inType) -#endif // JPH_ENABLE_ASSERTS - { - PhysicsLock::sLock(mLock JPH_IF_ENABLE_ASSERTS(, mContext, mType)); - } - - ~UniqueLock() - { - PhysicsLock::sUnlock(mLock JPH_IF_ENABLE_ASSERTS(, mContext, mType)); - } - -private: - LockType & mLock; -#ifdef JPH_ENABLE_ASSERTS - PhysicsLockContext mContext; - EPhysicsLockTypes mType; -#endif // JPH_ENABLE_ASSERTS -}; - -/// Helper class that is similar to std::shared_lock -template -class SharedLock : public NonCopyable -{ -public: - explicit SharedLock(LockType &inLock JPH_IF_ENABLE_ASSERTS(, PhysicsLockContext inContext, EPhysicsLockTypes inType)) : - mLock(inLock) -#ifdef JPH_ENABLE_ASSERTS - , mContext(inContext) - , mType(inType) -#endif // JPH_ENABLE_ASSERTS - { - PhysicsLock::sLockShared(mLock JPH_IF_ENABLE_ASSERTS(, mContext, mType)); - } - - ~SharedLock() - { - PhysicsLock::sUnlockShared(mLock JPH_IF_ENABLE_ASSERTS(, mContext, mType)); - } - -private: - LockType & mLock; -#ifdef JPH_ENABLE_ASSERTS - PhysicsLockContext mContext; - EPhysicsLockTypes mType; -#endif // JPH_ENABLE_ASSERTS -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsScene.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsScene.cpp deleted file mode 100644 index ed7e11c354b..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsScene.cpp +++ /dev/null @@ -1,261 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(PhysicsScene) -{ - JPH_ADD_ATTRIBUTE(PhysicsScene, mBodies) - JPH_ADD_ATTRIBUTE(PhysicsScene, mConstraints) - JPH_ADD_ATTRIBUTE(PhysicsScene, mSoftBodies) -} - -JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(PhysicsScene::ConnectedConstraint) -{ - JPH_ADD_ATTRIBUTE(PhysicsScene::ConnectedConstraint, mSettings) - JPH_ADD_ATTRIBUTE(PhysicsScene::ConnectedConstraint, mBody1) - JPH_ADD_ATTRIBUTE(PhysicsScene::ConnectedConstraint, mBody2) -} - -void PhysicsScene::AddBody(const BodyCreationSettings &inBody) -{ - mBodies.push_back(inBody); -} - -void PhysicsScene::AddConstraint(const TwoBodyConstraintSettings *inConstraint, uint32 inBody1, uint32 inBody2) -{ - mConstraints.emplace_back(inConstraint, inBody1, inBody2); -} - -void PhysicsScene::AddSoftBody(const SoftBodyCreationSettings &inSoftBody) -{ - mSoftBodies.push_back(inSoftBody); -} - -bool PhysicsScene::FixInvalidScales() -{ - const Vec3 unit_scale = Vec3::sReplicate(1.0f); - - bool success = true; - for (BodyCreationSettings &b : mBodies) - { - // Test if there is an invalid scale in the shape hierarchy - const Shape *shape = b.GetShape(); - if (!shape->IsValidScale(unit_scale)) - { - // Fix it up - Shape::ShapeResult result = shape->ScaleShape(unit_scale); - if (result.IsValid()) - b.SetShape(result.Get()); - else - success = false; - } - } - return success; -} - -bool PhysicsScene::CreateBodies(PhysicsSystem *inSystem) const -{ - BodyInterface &bi = inSystem->GetBodyInterface(); - - BodyIDVector body_ids; - body_ids.reserve(mBodies.size() + mSoftBodies.size()); - - // Create bodies - for (const BodyCreationSettings &b : mBodies) - { - const Body *body = bi.CreateBody(b); - if (body == nullptr) - break; - body_ids.push_back(body->GetID()); - } - - // Create soft bodies - for (const SoftBodyCreationSettings &b : mSoftBodies) - { - const Body *body = bi.CreateSoftBody(b); - if (body == nullptr) - break; - body_ids.push_back(body->GetID()); - } - - // Batch add bodies - BodyIDVector temp_body_ids = body_ids; // Body ID's get shuffled by AddBodiesPrepare - BodyInterface::AddState add_state = bi.AddBodiesPrepare(temp_body_ids.data(), (int)temp_body_ids.size()); - bi.AddBodiesFinalize(temp_body_ids.data(), (int)temp_body_ids.size(), add_state, EActivation::Activate); - - // If not all bodies are created, creating constraints will be unreliable - if (body_ids.size() != mBodies.size() + mSoftBodies.size()) - return false; - - // Create constraints - for (const ConnectedConstraint &cc : mConstraints) - { - BodyID body1_id = cc.mBody1 == cFixedToWorld? BodyID() : body_ids[cc.mBody1]; - BodyID body2_id = cc.mBody2 == cFixedToWorld? BodyID() : body_ids[cc.mBody2]; - Constraint *c = bi.CreateConstraint(cc.mSettings, body1_id, body2_id); - inSystem->AddConstraint(c); - } - - // Everything was created - return true; -} - -void PhysicsScene::SaveBinaryState(StreamOut &inStream, bool inSaveShapes, bool inSaveGroupFilter) const -{ - BodyCreationSettings::ShapeToIDMap shape_to_id; - BodyCreationSettings::MaterialToIDMap material_to_id; - BodyCreationSettings::GroupFilterToIDMap group_filter_to_id; - SoftBodyCreationSettings::SharedSettingsToIDMap settings_to_id; - - // Save bodies - inStream.Write((uint32)mBodies.size()); - for (const BodyCreationSettings &b : mBodies) - b.SaveWithChildren(inStream, inSaveShapes? &shape_to_id : nullptr, inSaveShapes? &material_to_id : nullptr, inSaveGroupFilter? &group_filter_to_id : nullptr); - - // Save constraints - inStream.Write((uint32)mConstraints.size()); - for (const ConnectedConstraint &cc : mConstraints) - { - cc.mSettings->SaveBinaryState(inStream); - inStream.Write(cc.mBody1); - inStream.Write(cc.mBody2); - } - - // Save soft bodies - inStream.Write((uint32)mSoftBodies.size()); - for (const SoftBodyCreationSettings &b : mSoftBodies) - b.SaveWithChildren(inStream, &settings_to_id, &material_to_id, inSaveGroupFilter? &group_filter_to_id : nullptr); -} - -PhysicsScene::PhysicsSceneResult PhysicsScene::sRestoreFromBinaryState(StreamIn &inStream) -{ - PhysicsSceneResult result; - - // Create scene - Ref scene = new PhysicsScene(); - - BodyCreationSettings::IDToShapeMap id_to_shape; - BodyCreationSettings::IDToMaterialMap id_to_material; - BodyCreationSettings::IDToGroupFilterMap id_to_group_filter; - SoftBodyCreationSettings::IDToSharedSettingsMap id_to_settings; - - // Reserve some memory to avoid frequent reallocations - id_to_shape.reserve(1024); - id_to_material.reserve(128); - id_to_group_filter.reserve(128); - - // Read bodies - uint32 len = 0; - inStream.Read(len); - scene->mBodies.resize(len); - for (BodyCreationSettings &b : scene->mBodies) - { - // Read creation settings - BodyCreationSettings::BCSResult bcs_result = BodyCreationSettings::sRestoreWithChildren(inStream, id_to_shape, id_to_material, id_to_group_filter); - if (bcs_result.HasError()) - { - result.SetError(bcs_result.GetError()); - return result; - } - b = bcs_result.Get(); - } - - // Read constraints - len = 0; - inStream.Read(len); - scene->mConstraints.resize(len); - for (ConnectedConstraint &cc : scene->mConstraints) - { - ConstraintSettings::ConstraintResult c_result = ConstraintSettings::sRestoreFromBinaryState(inStream); - if (c_result.HasError()) - { - result.SetError(c_result.GetError()); - return result; - } - cc.mSettings = static_cast(c_result.Get().GetPtr()); - inStream.Read(cc.mBody1); - inStream.Read(cc.mBody2); - } - - // Read soft bodies - len = 0; - inStream.Read(len); - scene->mSoftBodies.resize(len); - for (SoftBodyCreationSettings &b : scene->mSoftBodies) - { - // Read creation settings - SoftBodyCreationSettings::SBCSResult sbcs_result = SoftBodyCreationSettings::sRestoreWithChildren(inStream, id_to_settings, id_to_material, id_to_group_filter); - if (sbcs_result.HasError()) - { - result.SetError(sbcs_result.GetError()); - return result; - } - b = sbcs_result.Get(); - } - - result.Set(scene); - return result; -} - -void PhysicsScene::FromPhysicsSystem(const PhysicsSystem *inSystem) -{ - // This map will track where each body went in mBodies - using BodyIDToIdxMap = UnorderedMap; - BodyIDToIdxMap body_id_to_idx; - - // Map invalid ID - body_id_to_idx[BodyID()] = cFixedToWorld; - - // Get all bodies - BodyIDVector body_ids; - inSystem->GetBodies(body_ids); - - // Loop over all bodies - const BodyLockInterface &bli = inSystem->GetBodyLockInterface(); - for (const BodyID &id : body_ids) - { - BodyLockRead lock(bli, id); - if (lock.Succeeded()) - { - // Store location of body - body_id_to_idx[id] = (uint32)mBodies.size(); - - const Body &body = lock.GetBody(); - - // Convert to body creation settings - if (body.IsRigidBody()) - AddBody(body.GetBodyCreationSettings()); - else - AddSoftBody(body.GetSoftBodyCreationSettings()); - } - } - - // Loop over all constraints - Constraints constraints = inSystem->GetConstraints(); - for (const Constraint *c : constraints) - if (c->GetType() == EConstraintType::TwoBodyConstraint) - { - // Cast to two body constraint - const TwoBodyConstraint *tbc = static_cast(c); - - // Find the body indices - BodyIDToIdxMap::const_iterator b1 = body_id_to_idx.find(tbc->GetBody1()->GetID()); - BodyIDToIdxMap::const_iterator b2 = body_id_to_idx.find(tbc->GetBody2()->GetID()); - JPH_ASSERT(b1 != body_id_to_idx.end() && b2 != body_id_to_idx.end()); - - // Create constraint settings and add the constraint - Ref settings = c->GetConstraintSettings(); - AddConstraint(static_cast(settings.GetPtr()), b1->second, b2->second); - } -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsScene.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsScene.h deleted file mode 100644 index 530b79d61e4..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsScene.h +++ /dev/null @@ -1,104 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -class PhysicsSystem; - -/// Contains the creation settings of a set of bodies -class JPH_EXPORT PhysicsScene : public RefTarget -{ -public: - JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, PhysicsScene) - - /// Add a body to the scene - void AddBody(const BodyCreationSettings &inBody); - - /// Body constant to use to indicate that the constraint is attached to the fixed world - static constexpr uint32 cFixedToWorld = 0xffffffff; - - /// Add a constraint to the scene - /// @param inConstraint Constraint settings - /// @param inBody1 Index in the bodies list of first body to attach constraint to - /// @param inBody2 Index in the bodies list of the second body to attach constraint to - void AddConstraint(const TwoBodyConstraintSettings *inConstraint, uint32 inBody1, uint32 inBody2); - - /// Add a soft body to the scene - void AddSoftBody(const SoftBodyCreationSettings &inSoftBody); - - /// Get number of bodies in this scene - size_t GetNumBodies() const { return mBodies.size(); } - - /// Access to the body settings for this scene - const Array & GetBodies() const { return mBodies; } - Array & GetBodies() { return mBodies; } - - /// A constraint and how it is connected to the bodies in the scene - class ConnectedConstraint - { - public: - JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, ConnectedConstraint) - - ConnectedConstraint() = default; - ConnectedConstraint(const TwoBodyConstraintSettings *inSettings, uint inBody1, uint inBody2) : mSettings(inSettings), mBody1(inBody1), mBody2(inBody2) { } - - RefConst mSettings; ///< Constraint settings - uint32 mBody1; ///< Index of first body (in mBodies) - uint32 mBody2; ///< Index of second body (in mBodies) - }; - - /// Get number of constraints in this scene - size_t GetNumConstraints() const { return mConstraints.size(); } - - /// Access to the constraints for this scene - const Array & GetConstraints() const { return mConstraints; } - Array & GetConstraints() { return mConstraints; } - - /// Get number of bodies in this scene - size_t GetNumSoftBodies() const { return mSoftBodies.size(); } - - /// Access to the soft body settings for this scene - const Array & GetSoftBodies() const { return mSoftBodies; } - Array & GetSoftBodies() { return mSoftBodies; } - - /// Instantiate all bodies, returns false if not all bodies could be created - bool CreateBodies(PhysicsSystem *inSystem) const; - - /// Go through all body creation settings and fix shapes that are scaled incorrectly (note this will change the scene a bit). - /// @return False when not all scales could be fixed. - bool FixInvalidScales(); - - /// Saves the state of this object in binary form to inStream. - /// @param inStream The stream to save the state to - /// @param inSaveShapes If the shapes should be saved as well (these could be shared between physics scenes, in which case the calling application may want to write custom code to restore them) - /// @param inSaveGroupFilter If the group filter should be saved as well (these could be shared) - void SaveBinaryState(StreamOut &inStream, bool inSaveShapes, bool inSaveGroupFilter) const; - - using PhysicsSceneResult = Result>; - - /// Restore a saved scene from inStream - static PhysicsSceneResult sRestoreFromBinaryState(StreamIn &inStream); - - /// For debugging purposes: Construct a scene from the current state of the physics system - void FromPhysicsSystem(const PhysicsSystem *inSystem); - -private: - /// The bodies that are part of this scene - Array mBodies; - - /// Constraints that are part of this scene - Array mConstraints; - - /// Soft bodies that are part of this scene - Array mSoftBodies; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsSettings.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsSettings.h deleted file mode 100644 index eb8ecbbeaf8..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsSettings.h +++ /dev/null @@ -1,119 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -JPH_NAMESPACE_BEGIN - -/// If objects are closer than this distance, they are considered to be colliding (used for GJK) (unit: meter) -constexpr float cDefaultCollisionTolerance = 1.0e-4f; - -/// A factor that determines the accuracy of the penetration depth calculation. If the change of the squared distance is less than tolerance * current_penetration_depth^2 the algorithm will terminate. (unit: dimensionless) -constexpr float cDefaultPenetrationTolerance = 1.0e-4f; ///< Stop when there's less than 1% change - -/// How much padding to add around objects -constexpr float cDefaultConvexRadius = 0.05f; - -/// Used by (Tapered)CapsuleShape to determine when supporting face is an edge rather than a point (unit: meter) -static constexpr float cCapsuleProjectionSlop = 0.02f; - -/// Maximum amount of jobs to allow -constexpr int cMaxPhysicsJobs = 2048; - -/// Maximum amount of barriers to allow -constexpr int cMaxPhysicsBarriers = 8; - -struct PhysicsSettings -{ - JPH_OVERRIDE_NEW_DELETE - - /// Size of body pairs array, corresponds to the maximum amount of potential body pairs that can be in flight at any time. - /// Setting this to a low value will use less memory but slow down simulation as threads may run out of narrow phase work. - int mMaxInFlightBodyPairs = 16384; - - /// How many PhysicsStepListeners to notify in 1 batch - int mStepListenersBatchSize = 8; - - /// How many step listener batches are needed before spawning another job (set to INT_MAX if no parallelism is desired) - int mStepListenerBatchesPerJob = 1; - - /// Baumgarte stabilization factor (how much of the position error to 'fix' in 1 update) (unit: dimensionless, 0 = nothing, 1 = 100%) - float mBaumgarte = 0.2f; - - /// Radius around objects inside which speculative contact points will be detected. Note that if this is too big - /// you will get ghost collisions as speculative contacts are based on the closest points during the collision detection - /// step which may not be the actual closest points by the time the two objects hit (unit: meters) - float mSpeculativeContactDistance = 0.02f; - - /// How much bodies are allowed to sink into each other (unit: meters) - float mPenetrationSlop = 0.02f; - - /// Fraction of its inner radius a body must move per step to enable casting for the LinearCast motion quality - float mLinearCastThreshold = 0.75f; - - /// Fraction of its inner radius a body may penetrate another body for the LinearCast motion quality - float mLinearCastMaxPenetration = 0.25f; - - /// Max squared distance to use to determine if two points are on the same plane for determining the contact manifold between two shape faces (unit: meter^2) - float mManifoldToleranceSq = 1.0e-6f; - - /// Maximum distance to correct in a single iteration when solving position constraints (unit: meters) - float mMaxPenetrationDistance = 0.2f; - - /// Maximum relative delta position for body pairs to be able to reuse collision results from last frame (units: meter^2) - float mBodyPairCacheMaxDeltaPositionSq = Square(0.001f); ///< 1 mm - - /// Maximum relative delta orientation for body pairs to be able to reuse collision results from last frame, stored as cos(max angle / 2) - float mBodyPairCacheCosMaxDeltaRotationDiv2 = 0.99984769515639123915701155881391f; ///< cos(2 degrees / 2) - - /// Maximum angle between normals that allows manifolds between different sub shapes of the same body pair to be combined - float mContactNormalCosMaxDeltaRotation = 0.99619469809174553229501040247389f; ///< cos(5 degree) - - /// Maximum allowed distance between old and new contact point to preserve contact forces for warm start (units: meter^2) - float mContactPointPreserveLambdaMaxDistSq = Square(0.01f); ///< 1 cm - - /// Number of solver velocity iterations to run - /// Note that this needs to be >= 2 in order for friction to work (friction is applied using the non-penetration impulse from the previous iteration) - uint mNumVelocitySteps = 10; - - /// Number of solver position iterations to run - uint mNumPositionSteps = 2; - - /// Minimal velocity needed before a collision can be elastic (unit: m) - float mMinVelocityForRestitution = 1.0f; - - /// Time before object is allowed to go to sleep (unit: seconds) - float mTimeBeforeSleep = 0.5f; - - /// Velocity of points on bounding box of object below which an object can be considered sleeping (unit: m/s) - float mPointVelocitySleepThreshold = 0.03f; - - /// By default the simulation is deterministic, it is possible to turn this off by setting this setting to false. This will make the simulation run faster but it will no longer be deterministic. - bool mDeterministicSimulation = true; - - ///@name These variables are mainly for debugging purposes, they allow turning on/off certain subsystems. You probably want to leave them alone. - ///@{ - - /// Whether or not to use warm starting for constraints (initially applying previous frames impulses) - bool mConstraintWarmStart = true; - - /// Whether or not to use the body pair cache, which removes the need for narrow phase collision detection when orientation between two bodies didn't change - bool mUseBodyPairContactCache = true; - - /// Whether or not to reduce manifolds with similar contact normals into one contact manifold (see description at Body::SetUseManifoldReduction) - bool mUseManifoldReduction = true; - - /// If we split up large islands into smaller parallel batches of work (to improve performance) - bool mUseLargeIslandSplitter = true; - - /// If objects can go to sleep or not - bool mAllowSleeping = true; - - /// When false, we prevent collision against non-active (shared) edges. Mainly for debugging the algorithm. - bool mCheckActiveEdges = true; - - ///@} -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsStepListener.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsStepListener.h deleted file mode 100644 index 80cf0519bde..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsStepListener.h +++ /dev/null @@ -1,27 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -JPH_NAMESPACE_BEGIN - -class PhysicsSystem; - -/// A listener class that receives a callback before every physics simulation step -class JPH_EXPORT PhysicsStepListener -{ -public: - /// Ensure virtual destructor - virtual ~PhysicsStepListener() = default; - - /// Called before every simulation step (received inCollisionSteps times for every PhysicsSystem::Update(...) call) - /// This is called while all body and constraint mutexes are locked. You can read/write bodies and constraints but not add/remove them. - /// Multiple listeners can be executed in parallel and it is the responsibility of the listener to avoid race conditions. - /// The best way to do this is to have each step listener operate on a subset of the bodies and constraints - /// and making sure that these bodies and constraints are not touched by any other step listener. - /// Note that this function is not called if there aren't any active bodies or when the physics system is updated with 0 delta time. - virtual void OnStep(float inDeltaTime, PhysicsSystem &inPhysicsSystem) = 0; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsSystem.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsSystem.cpp deleted file mode 100644 index be0281a3ee0..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsSystem.cpp +++ /dev/null @@ -1,2703 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef JPH_DEBUG_RENDERER - #include -#endif // JPH_DEBUG_RENDERER - -JPH_NAMESPACE_BEGIN - -#ifdef JPH_DEBUG_RENDERER -bool PhysicsSystem::sDrawMotionQualityLinearCast = false; -#endif // JPH_DEBUG_RENDERER - -//#define BROAD_PHASE BroadPhaseBruteForce -#define BROAD_PHASE BroadPhaseQuadTree - -static const Color cColorUpdateBroadPhaseFinalize = Color::sGetDistinctColor(1); -static const Color cColorUpdateBroadPhasePrepare = Color::sGetDistinctColor(2); -static const Color cColorFindCollisions = Color::sGetDistinctColor(3); -static const Color cColorApplyGravity = Color::sGetDistinctColor(4); -static const Color cColorSetupVelocityConstraints = Color::sGetDistinctColor(5); -static const Color cColorBuildIslandsFromConstraints = Color::sGetDistinctColor(6); -static const Color cColorDetermineActiveConstraints = Color::sGetDistinctColor(7); -static const Color cColorFinalizeIslands = Color::sGetDistinctColor(8); -static const Color cColorContactRemovedCallbacks = Color::sGetDistinctColor(9); -static const Color cColorBodySetIslandIndex = Color::sGetDistinctColor(10); -static const Color cColorStartNextStep = Color::sGetDistinctColor(11); -static const Color cColorSolveVelocityConstraints = Color::sGetDistinctColor(12); -static const Color cColorPreIntegrateVelocity = Color::sGetDistinctColor(13); -static const Color cColorIntegrateVelocity = Color::sGetDistinctColor(14); -static const Color cColorPostIntegrateVelocity = Color::sGetDistinctColor(15); -static const Color cColorResolveCCDContacts = Color::sGetDistinctColor(16); -static const Color cColorSolvePositionConstraints = Color::sGetDistinctColor(17); -static const Color cColorFindCCDContacts = Color::sGetDistinctColor(18); -static const Color cColorStepListeners = Color::sGetDistinctColor(19); -static const Color cColorSoftBodyPrepare = Color::sGetDistinctColor(20); -static const Color cColorSoftBodyCollide = Color::sGetDistinctColor(21); -static const Color cColorSoftBodySimulate = Color::sGetDistinctColor(22); -static const Color cColorSoftBodyFinalize = Color::sGetDistinctColor(23); - -PhysicsSystem::~PhysicsSystem() -{ - // Remove broadphase - delete mBroadPhase; -} - -void PhysicsSystem::Init(uint inMaxBodies, uint inNumBodyMutexes, uint inMaxBodyPairs, uint inMaxContactConstraints, const BroadPhaseLayerInterface &inBroadPhaseLayerInterface, const ObjectVsBroadPhaseLayerFilter &inObjectVsBroadPhaseLayerFilter, const ObjectLayerPairFilter &inObjectLayerPairFilter) -{ - mObjectVsBroadPhaseLayerFilter = &inObjectVsBroadPhaseLayerFilter; - mObjectLayerPairFilter = &inObjectLayerPairFilter; - - // Initialize body manager - mBodyManager.Init(inMaxBodies, inNumBodyMutexes, inBroadPhaseLayerInterface); - - // Create broadphase - mBroadPhase = new BROAD_PHASE(); - mBroadPhase->Init(&mBodyManager, inBroadPhaseLayerInterface); - - // Init contact constraint manager - mContactManager.Init(inMaxBodyPairs, inMaxContactConstraints); - - // Init islands builder - mIslandBuilder.Init(inMaxBodies); - - // Initialize body interface - mBodyInterfaceLocking.Init(mBodyLockInterfaceLocking, mBodyManager, *mBroadPhase); - mBodyInterfaceNoLock.Init(mBodyLockInterfaceNoLock, mBodyManager, *mBroadPhase); - - // Initialize narrow phase query - mNarrowPhaseQueryLocking.Init(mBodyLockInterfaceLocking, *mBroadPhase); - mNarrowPhaseQueryNoLock.Init(mBodyLockInterfaceNoLock, *mBroadPhase); -} - -void PhysicsSystem::OptimizeBroadPhase() -{ - mBroadPhase->Optimize(); -} - -void PhysicsSystem::AddStepListener(PhysicsStepListener *inListener) -{ - lock_guard lock(mStepListenersMutex); - - JPH_ASSERT(find(mStepListeners.begin(), mStepListeners.end(), inListener) == mStepListeners.end()); - mStepListeners.push_back(inListener); -} - -void PhysicsSystem::RemoveStepListener(PhysicsStepListener *inListener) -{ - lock_guard lock(mStepListenersMutex); - - StepListeners::iterator i = find(mStepListeners.begin(), mStepListeners.end(), inListener); - JPH_ASSERT(i != mStepListeners.end()); - *i = mStepListeners.back(); - mStepListeners.pop_back(); -} - -EPhysicsUpdateError PhysicsSystem::Update(float inDeltaTime, int inCollisionSteps, TempAllocator *inTempAllocator, JobSystem *inJobSystem) -{ - JPH_PROFILE_FUNCTION(); - - JPH_DET_LOG("PhysicsSystem::Update: dt: " << inDeltaTime << " steps: " << inCollisionSteps); - - JPH_ASSERT(inCollisionSteps > 0); - JPH_ASSERT(inDeltaTime >= 0.0f); - - // Sync point for the broadphase. This will allow it to do clean up operations without having any mutexes locked yet. - mBroadPhase->FrameSync(); - - // If there are no active bodies or there's no time delta - uint32 num_active_rigid_bodies = mBodyManager.GetNumActiveBodies(EBodyType::RigidBody); - uint32 num_active_soft_bodies = mBodyManager.GetNumActiveBodies(EBodyType::SoftBody); - if ((num_active_rigid_bodies == 0 && num_active_soft_bodies == 0) || inDeltaTime <= 0.0f) - { - mBodyManager.LockAllBodies(); - - // Update broadphase - mBroadPhase->LockModifications(); - BroadPhase::UpdateState update_state = mBroadPhase->UpdatePrepare(); - mBroadPhase->UpdateFinalize(update_state); - mBroadPhase->UnlockModifications(); - - // Call contact removal callbacks from contacts that existed in the previous update - mContactManager.FinalizeContactCacheAndCallContactPointRemovedCallbacks(0, 0); - - mBodyManager.UnlockAllBodies(); - return EPhysicsUpdateError::None; - } - - // Calculate ratio between current and previous frame delta time to scale initial constraint forces - float step_delta_time = inDeltaTime / inCollisionSteps; - float warm_start_impulse_ratio = mPhysicsSettings.mConstraintWarmStart && mPreviousStepDeltaTime > 0.0f? step_delta_time / mPreviousStepDeltaTime : 0.0f; - mPreviousStepDeltaTime = step_delta_time; - - // Create the context used for passing information between jobs - PhysicsUpdateContext context(*inTempAllocator); - context.mPhysicsSystem = this; - context.mJobSystem = inJobSystem; - context.mBarrier = inJobSystem->CreateBarrier(); - context.mIslandBuilder = &mIslandBuilder; - context.mStepDeltaTime = step_delta_time; - context.mWarmStartImpulseRatio = warm_start_impulse_ratio; - context.mSteps.resize(inCollisionSteps); - - // Allocate space for body pairs - JPH_ASSERT(context.mBodyPairs == nullptr); - context.mBodyPairs = static_cast(inTempAllocator->Allocate(sizeof(BodyPair) * mPhysicsSettings.mMaxInFlightBodyPairs)); - - // Lock all bodies for write so that we can freely touch them - mStepListenersMutex.lock(); - mBodyManager.LockAllBodies(); - mBroadPhase->LockModifications(); - - // Get max number of concurrent jobs - int max_concurrency = context.GetMaxConcurrency(); - - // Calculate how many step listener jobs we spawn - int num_step_listener_jobs = mStepListeners.empty()? 0 : max(1, min((int)mStepListeners.size() / mPhysicsSettings.mStepListenersBatchSize / mPhysicsSettings.mStepListenerBatchesPerJob, max_concurrency)); - - // Number of gravity jobs depends on the amount of active bodies. - // Launch max 1 job per batch of active bodies - // Leave 1 thread for update broadphase prepare and 1 for determine active constraints - int num_apply_gravity_jobs = max(1, min(((int)num_active_rigid_bodies + cApplyGravityBatchSize - 1) / cApplyGravityBatchSize, max_concurrency - 2)); - - // Number of determine active constraints jobs to run depends on number of constraints. - // Leave 1 thread for update broadphase prepare and 1 for apply gravity - int num_determine_active_constraints_jobs = max(1, min(((int)mConstraintManager.GetNumConstraints() + cDetermineActiveConstraintsBatchSize - 1) / cDetermineActiveConstraintsBatchSize, max_concurrency - 2)); - - // Number of setup velocity constraints jobs to run depends on number of constraints. - int num_setup_velocity_constraints_jobs = max(1, min(((int)mConstraintManager.GetNumConstraints() + cSetupVelocityConstraintsBatchSize - 1) / cSetupVelocityConstraintsBatchSize, max_concurrency)); - - // Number of find collisions jobs to run depends on number of active bodies. - // Note that when we have more than 1 thread, we always spawn at least 2 find collisions jobs so that the first job can wait for build islands from constraints - // (which may activate additional bodies that need to be processed) while the second job can start processing collision work. - int num_find_collisions_jobs = max(max_concurrency == 1? 1 : 2, min(((int)num_active_rigid_bodies + cActiveBodiesBatchSize - 1) / cActiveBodiesBatchSize, max_concurrency)); - - // Number of integrate velocity jobs depends on number of active bodies. - int num_integrate_velocity_jobs = max(1, min(((int)num_active_rigid_bodies + cIntegrateVelocityBatchSize - 1) / cIntegrateVelocityBatchSize, max_concurrency)); - - { - JPH_PROFILE("Build Jobs"); - - // Iterate over collision steps - for (int step_idx = 0; step_idx < inCollisionSteps; ++step_idx) - { - bool is_first_step = step_idx == 0; - bool is_last_step = step_idx == inCollisionSteps - 1; - - PhysicsUpdateContext::Step &step = context.mSteps[step_idx]; - step.mContext = &context; - step.mIsFirst = is_first_step; - step.mIsLast = is_last_step; - - // Create job to do broadphase finalization - // This job must finish before integrating velocities. Until then the positions will not be updated neither will bodies be added / removed. - step.mUpdateBroadphaseFinalize = inJobSystem->CreateJob("UpdateBroadPhaseFinalize", cColorUpdateBroadPhaseFinalize, [&context, &step]() - { - // Validate that all find collision jobs have stopped - JPH_ASSERT(step.mActiveFindCollisionJobs == 0); - - // Finalize the broadphase update - context.mPhysicsSystem->mBroadPhase->UpdateFinalize(step.mBroadPhaseUpdateState); - - // Signal that it is done - step.mPreIntegrateVelocity.RemoveDependency(); - }, num_find_collisions_jobs + 2); // depends on: find collisions, broadphase prepare update, finish building jobs - - // The immediate jobs below are only immediate for the first step, the all finished job will kick them for the next step - int previous_step_dependency_count = is_first_step? 0 : 1; - - // Start job immediately: Start the prepare broadphase - // Must be done under body lock protection since the order is body locks then broadphase mutex - // If this is turned around the RemoveBody call will hang since it locks in that order - step.mBroadPhasePrepare = inJobSystem->CreateJob("UpdateBroadPhasePrepare", cColorUpdateBroadPhasePrepare, [&context, &step]() - { - // Prepare the broadphase update - step.mBroadPhaseUpdateState = context.mPhysicsSystem->mBroadPhase->UpdatePrepare(); - - // Now the finalize can run (if other dependencies are met too) - step.mUpdateBroadphaseFinalize.RemoveDependency(); - }, previous_step_dependency_count); - - // This job will find all collisions - step.mBodyPairQueues.resize(max_concurrency); - step.mMaxBodyPairsPerQueue = mPhysicsSettings.mMaxInFlightBodyPairs / max_concurrency; - step.mActiveFindCollisionJobs = ~PhysicsUpdateContext::JobMask(0) >> (sizeof(PhysicsUpdateContext::JobMask) * 8 - num_find_collisions_jobs); - step.mFindCollisions.resize(num_find_collisions_jobs); - for (int i = 0; i < num_find_collisions_jobs; ++i) - { - // Build islands from constraints may activate additional bodies, so the first job will wait for this to finish in order to not miss any active bodies - int num_dep_build_islands_from_constraints = i == 0? 1 : 0; - step.mFindCollisions[i] = inJobSystem->CreateJob("FindCollisions", cColorFindCollisions, [&step, i]() - { - step.mContext->mPhysicsSystem->JobFindCollisions(&step, i); - }, num_apply_gravity_jobs + num_determine_active_constraints_jobs + 1 + num_dep_build_islands_from_constraints); // depends on: apply gravity, determine active constraints, finish building jobs, build islands from constraints - } - - if (is_first_step) - { - #ifdef JPH_ENABLE_ASSERTS - // Don't allow write operations to the active bodies list - mBodyManager.SetActiveBodiesLocked(true); - #endif - - // Store the number of active bodies at the start of the step - step.mNumActiveBodiesAtStepStart = mBodyManager.GetNumActiveBodies(EBodyType::RigidBody); - - // Lock all constraints - mConstraintManager.LockAllConstraints(); - - // Allocate memory for storing the active constraints - JPH_ASSERT(context.mActiveConstraints == nullptr); - context.mActiveConstraints = static_cast(inTempAllocator->Allocate(mConstraintManager.GetNumConstraints() * sizeof(Constraint *))); - - // Prepare contact buffer - mContactManager.PrepareConstraintBuffer(&context); - - // Setup island builder - mIslandBuilder.PrepareContactConstraints(mContactManager.GetMaxConstraints(), context.mTempAllocator); - } - - // This job applies gravity to all active bodies - step.mApplyGravity.resize(num_apply_gravity_jobs); - for (int i = 0; i < num_apply_gravity_jobs; ++i) - step.mApplyGravity[i] = inJobSystem->CreateJob("ApplyGravity", cColorApplyGravity, [&context, &step]() - { - context.mPhysicsSystem->JobApplyGravity(&context, &step); - - JobHandle::sRemoveDependencies(step.mFindCollisions); - }, num_step_listener_jobs > 0? num_step_listener_jobs : previous_step_dependency_count); // depends on: step listeners (or previous step if no step listeners) - - // This job will setup velocity constraints for non-collision constraints - step.mSetupVelocityConstraints.resize(num_setup_velocity_constraints_jobs); - for (int i = 0; i < num_setup_velocity_constraints_jobs; ++i) - step.mSetupVelocityConstraints[i] = inJobSystem->CreateJob("SetupVelocityConstraints", cColorSetupVelocityConstraints, [&context, &step]() - { - context.mPhysicsSystem->JobSetupVelocityConstraints(context.mStepDeltaTime, &step); - - JobHandle::sRemoveDependencies(step.mSolveVelocityConstraints); - }, num_determine_active_constraints_jobs + 1); // depends on: determine active constraints, finish building jobs - - // This job will build islands from constraints - step.mBuildIslandsFromConstraints = inJobSystem->CreateJob("BuildIslandsFromConstraints", cColorBuildIslandsFromConstraints, [&context, &step]() - { - context.mPhysicsSystem->JobBuildIslandsFromConstraints(&context, &step); - - step.mFindCollisions[0].RemoveDependency(); // The first collisions job cannot start running until we've finished building islands and activated all bodies - step.mFinalizeIslands.RemoveDependency(); - }, num_determine_active_constraints_jobs + 1); // depends on: determine active constraints, finish building jobs - - // This job determines active constraints - step.mDetermineActiveConstraints.resize(num_determine_active_constraints_jobs); - for (int i = 0; i < num_determine_active_constraints_jobs; ++i) - step.mDetermineActiveConstraints[i] = inJobSystem->CreateJob("DetermineActiveConstraints", cColorDetermineActiveConstraints, [&context, &step]() - { - context.mPhysicsSystem->JobDetermineActiveConstraints(&step); - - step.mBuildIslandsFromConstraints.RemoveDependency(); - - // Kick these jobs last as they will use up all CPU cores leaving no space for the previous job, we prefer setup velocity constraints to finish first so we kick it first - JobHandle::sRemoveDependencies(step.mSetupVelocityConstraints); - JobHandle::sRemoveDependencies(step.mFindCollisions); - }, num_step_listener_jobs > 0? num_step_listener_jobs : previous_step_dependency_count); // depends on: step listeners (or previous step if no step listeners) - - // This job calls the step listeners - step.mStepListeners.resize(num_step_listener_jobs); - for (int i = 0; i < num_step_listener_jobs; ++i) - step.mStepListeners[i] = inJobSystem->CreateJob("StepListeners", cColorStepListeners, [&context, &step]() - { - // Call the step listeners - context.mPhysicsSystem->JobStepListeners(&step); - - // Kick apply gravity and determine active constraint jobs - JobHandle::sRemoveDependencies(step.mApplyGravity); - JobHandle::sRemoveDependencies(step.mDetermineActiveConstraints); - }, previous_step_dependency_count); - - // Unblock the previous step - if (!is_first_step) - context.mSteps[step_idx - 1].mStartNextStep.RemoveDependency(); - - // This job will finalize the simulation islands - step.mFinalizeIslands = inJobSystem->CreateJob("FinalizeIslands", cColorFinalizeIslands, [&context, &step]() - { - // Validate that all find collision jobs have stopped - JPH_ASSERT(step.mActiveFindCollisionJobs == 0); - - context.mPhysicsSystem->JobFinalizeIslands(&context); - - JobHandle::sRemoveDependencies(step.mSolveVelocityConstraints); - step.mBodySetIslandIndex.RemoveDependency(); - }, num_find_collisions_jobs + 2); // depends on: find collisions, build islands from constraints, finish building jobs - - // Unblock previous job - // Note: technically we could release find collisions here but we don't want to because that could make them run before 'setup velocity constraints' which means that job won't have a thread left - step.mBuildIslandsFromConstraints.RemoveDependency(); - - // This job will call the contact removed callbacks - step.mContactRemovedCallbacks = inJobSystem->CreateJob("ContactRemovedCallbacks", cColorContactRemovedCallbacks, [&context, &step]() - { - context.mPhysicsSystem->JobContactRemovedCallbacks(&step); - - if (step.mStartNextStep.IsValid()) - step.mStartNextStep.RemoveDependency(); - }, 1); // depends on the find ccd contacts - - // This job will set the island index on each body (only used for debug drawing purposes) - // It will also delete any bodies that have been destroyed in the last frame - step.mBodySetIslandIndex = inJobSystem->CreateJob("BodySetIslandIndex", cColorBodySetIslandIndex, [&context, &step]() - { - context.mPhysicsSystem->JobBodySetIslandIndex(); - - JobHandle::sRemoveDependencies(step.mSolvePositionConstraints); - }, 2); // depends on: finalize islands, finish building jobs - - // Job to start the next collision step - if (!is_last_step) - { - PhysicsUpdateContext::Step *next_step = &context.mSteps[step_idx + 1]; - step.mStartNextStep = inJobSystem->CreateJob("StartNextStep", cColorStartNextStep, [this, next_step]() - { - #ifdef _DEBUG - // Validate that the cached bounds are correct - mBodyManager.ValidateActiveBodyBounds(); - #endif // _DEBUG - - // Store the number of active bodies at the start of the step - next_step->mNumActiveBodiesAtStepStart = mBodyManager.GetNumActiveBodies(EBodyType::RigidBody); - - // Clear the large island splitter - TempAllocator *temp_allocator = next_step->mContext->mTempAllocator; - mLargeIslandSplitter.Reset(temp_allocator); - - // Clear the island builder - mIslandBuilder.ResetIslands(temp_allocator); - - // Setup island builder - mIslandBuilder.PrepareContactConstraints(mContactManager.GetMaxConstraints(), temp_allocator); - - // Restart the contact manager - mContactManager.RecycleConstraintBuffer(); - - // Kick the jobs of the next step (in the same order as the first step) - next_step->mBroadPhasePrepare.RemoveDependency(); - if (next_step->mStepListeners.empty()) - { - // Kick the gravity and active constraints jobs immediately - JobHandle::sRemoveDependencies(next_step->mApplyGravity); - JobHandle::sRemoveDependencies(next_step->mDetermineActiveConstraints); - } - else - { - // Kick the step listeners job first - JobHandle::sRemoveDependencies(next_step->mStepListeners); - } - }, 3); // depends on: update soft bodies, contact removed callbacks, finish building the previous step - } - - // This job will solve the velocity constraints - step.mSolveVelocityConstraints.resize(max_concurrency); - for (int i = 0; i < max_concurrency; ++i) - step.mSolveVelocityConstraints[i] = inJobSystem->CreateJob("SolveVelocityConstraints", cColorSolveVelocityConstraints, [&context, &step]() - { - context.mPhysicsSystem->JobSolveVelocityConstraints(&context, &step); - - step.mPreIntegrateVelocity.RemoveDependency(); - }, num_setup_velocity_constraints_jobs + 2); // depends on: finalize islands, setup velocity constraints, finish building jobs. - - // We prefer setup velocity constraints to finish first so we kick it first - JobHandle::sRemoveDependencies(step.mSetupVelocityConstraints); - JobHandle::sRemoveDependencies(step.mFindCollisions); - - // Finalize islands is a dependency on find collisions so it can go last - step.mFinalizeIslands.RemoveDependency(); - - // This job will prepare the position update of all active bodies - step.mPreIntegrateVelocity = inJobSystem->CreateJob("PreIntegrateVelocity", cColorPreIntegrateVelocity, [&context, &step]() - { - context.mPhysicsSystem->JobPreIntegrateVelocity(&context, &step); - - JobHandle::sRemoveDependencies(step.mIntegrateVelocity); - }, 2 + max_concurrency); // depends on: broadphase update finalize, solve velocity constraints, finish building jobs. - - // Unblock previous jobs - step.mUpdateBroadphaseFinalize.RemoveDependency(); - JobHandle::sRemoveDependencies(step.mSolveVelocityConstraints); - - // This job will update the positions of all active bodies - step.mIntegrateVelocity.resize(num_integrate_velocity_jobs); - for (int i = 0; i < num_integrate_velocity_jobs; ++i) - step.mIntegrateVelocity[i] = inJobSystem->CreateJob("IntegrateVelocity", cColorIntegrateVelocity, [&context, &step]() - { - context.mPhysicsSystem->JobIntegrateVelocity(&context, &step); - - step.mPostIntegrateVelocity.RemoveDependency(); - }, 2); // depends on: pre integrate velocity, finish building jobs. - - // Unblock previous job - step.mPreIntegrateVelocity.RemoveDependency(); - - // This job will finish the position update of all active bodies - step.mPostIntegrateVelocity = inJobSystem->CreateJob("PostIntegrateVelocity", cColorPostIntegrateVelocity, [&context, &step]() - { - context.mPhysicsSystem->JobPostIntegrateVelocity(&context, &step); - - step.mResolveCCDContacts.RemoveDependency(); - }, num_integrate_velocity_jobs + 1); // depends on: integrate velocity, finish building jobs - - // Unblock previous jobs - JobHandle::sRemoveDependencies(step.mIntegrateVelocity); - - // This job will update the positions and velocities for all bodies that need continuous collision detection - step.mResolveCCDContacts = inJobSystem->CreateJob("ResolveCCDContacts", cColorResolveCCDContacts, [&context, &step]() - { - context.mPhysicsSystem->JobResolveCCDContacts(&context, &step); - - JobHandle::sRemoveDependencies(step.mSolvePositionConstraints); - }, 2); // depends on: integrate velocities, detect ccd contacts (added dynamically), finish building jobs. - - // Unblock previous job - step.mPostIntegrateVelocity.RemoveDependency(); - - // Fixes up drift in positions and updates the broadphase with new body positions - step.mSolvePositionConstraints.resize(max_concurrency); - for (int i = 0; i < max_concurrency; ++i) - step.mSolvePositionConstraints[i] = inJobSystem->CreateJob("SolvePositionConstraints", cColorSolvePositionConstraints, [&context, &step]() - { - context.mPhysicsSystem->JobSolvePositionConstraints(&context, &step); - - // Kick the next step - if (step.mSoftBodyPrepare.IsValid()) - step.mSoftBodyPrepare.RemoveDependency(); - }, 3); // depends on: resolve ccd contacts, body set island index, finish building jobs. - - // Unblock previous jobs. - step.mResolveCCDContacts.RemoveDependency(); - step.mBodySetIslandIndex.RemoveDependency(); - - // The soft body prepare job will create other jobs if needed - step.mSoftBodyPrepare = inJobSystem->CreateJob("SoftBodyPrepare", cColorSoftBodyPrepare, [&context, &step]() - { - context.mPhysicsSystem->JobSoftBodyPrepare(&context, &step); - }, max_concurrency); // depends on: solve position constraints. - - // Unblock previous jobs - JobHandle::sRemoveDependencies(step.mSolvePositionConstraints); - } - } - - // Build the list of jobs to wait for - JobSystem::Barrier *barrier = context.mBarrier; - { - JPH_PROFILE("Build job barrier"); - - StaticArray handles; - for (const PhysicsUpdateContext::Step &step : context.mSteps) - { - if (step.mBroadPhasePrepare.IsValid()) - handles.push_back(step.mBroadPhasePrepare); - for (const JobHandle &h : step.mStepListeners) - handles.push_back(h); - for (const JobHandle &h : step.mDetermineActiveConstraints) - handles.push_back(h); - for (const JobHandle &h : step.mApplyGravity) - handles.push_back(h); - for (const JobHandle &h : step.mFindCollisions) - handles.push_back(h); - if (step.mUpdateBroadphaseFinalize.IsValid()) - handles.push_back(step.mUpdateBroadphaseFinalize); - for (const JobHandle &h : step.mSetupVelocityConstraints) - handles.push_back(h); - handles.push_back(step.mBuildIslandsFromConstraints); - handles.push_back(step.mFinalizeIslands); - handles.push_back(step.mBodySetIslandIndex); - for (const JobHandle &h : step.mSolveVelocityConstraints) - handles.push_back(h); - handles.push_back(step.mPreIntegrateVelocity); - for (const JobHandle &h : step.mIntegrateVelocity) - handles.push_back(h); - handles.push_back(step.mPostIntegrateVelocity); - handles.push_back(step.mResolveCCDContacts); - for (const JobHandle &h : step.mSolvePositionConstraints) - handles.push_back(h); - handles.push_back(step.mContactRemovedCallbacks); - if (step.mSoftBodyPrepare.IsValid()) - handles.push_back(step.mSoftBodyPrepare); - if (step.mStartNextStep.IsValid()) - handles.push_back(step.mStartNextStep); - } - barrier->AddJobs(handles.data(), handles.size()); - } - - // Wait until all jobs finish - // Note we don't just wait for the last job. If we would and another job - // would be scheduled in between there is the possibility of a deadlock. - // The other job could try to e.g. add/remove a body which would try to - // lock a body mutex while this thread has already locked the mutex - inJobSystem->WaitForJobs(barrier); - - // We're done with the barrier for this update - inJobSystem->DestroyBarrier(barrier); - -#ifdef _DEBUG - // Validate that the cached bounds are correct - mBodyManager.ValidateActiveBodyBounds(); -#endif // _DEBUG - - // Clear the large island splitter - mLargeIslandSplitter.Reset(inTempAllocator); - - // Clear the island builder - mIslandBuilder.ResetIslands(inTempAllocator); - - // Clear the contact manager - mContactManager.FinishConstraintBuffer(); - - // Free active constraints - inTempAllocator->Free(context.mActiveConstraints, mConstraintManager.GetNumConstraints() * sizeof(Constraint *)); - context.mActiveConstraints = nullptr; - - // Free body pairs - inTempAllocator->Free(context.mBodyPairs, sizeof(BodyPair) * mPhysicsSettings.mMaxInFlightBodyPairs); - context.mBodyPairs = nullptr; - - // Unlock the broadphase - mBroadPhase->UnlockModifications(); - - // Unlock all constraints - mConstraintManager.UnlockAllConstraints(); - -#ifdef JPH_ENABLE_ASSERTS - // Allow write operations to the active bodies list - mBodyManager.SetActiveBodiesLocked(false); -#endif - - // Unlock all bodies - mBodyManager.UnlockAllBodies(); - - // Unlock step listeners - mStepListenersMutex.unlock(); - - // Return any errors - EPhysicsUpdateError errors = static_cast(context.mErrors.load(memory_order_acquire)); - JPH_ASSERT(errors == EPhysicsUpdateError::None, "An error occurred during the physics update, see EPhysicsUpdateError for more information"); - return errors; -} - -void PhysicsSystem::JobStepListeners(PhysicsUpdateContext::Step *ioStep) -{ -#ifdef JPH_ENABLE_ASSERTS - // Read positions (broadphase updates concurrently so we can't write), read/write velocities - BodyAccess::Grant grant(BodyAccess::EAccess::ReadWrite, BodyAccess::EAccess::Read); - - // Can activate bodies only (we cache the amount of active bodies at the beginning of the step in mNumActiveBodiesAtStepStart so we cannot deactivate here) - BodyManager::GrantActiveBodiesAccess grant_active(true, false); -#endif - - float step_time = ioStep->mContext->mStepDeltaTime; - uint32 batch_size = mPhysicsSettings.mStepListenersBatchSize; - for (;;) - { - // Get the start of a new batch - uint32 batch = ioStep->mStepListenerReadIdx.fetch_add(batch_size); - if (batch >= mStepListeners.size()) - break; - - // Call the listeners - for (uint32 i = batch, i_end = min((uint32)mStepListeners.size(), batch + batch_size); i < i_end; ++i) - mStepListeners[i]->OnStep(step_time, *this); - } -} - -void PhysicsSystem::JobDetermineActiveConstraints(PhysicsUpdateContext::Step *ioStep) const -{ -#ifdef JPH_ENABLE_ASSERTS - // No body access - BodyAccess::Grant grant(BodyAccess::EAccess::None, BodyAccess::EAccess::None); -#endif - - uint32 num_constraints = mConstraintManager.GetNumConstraints(); - uint32 num_active_constraints; - Constraint **active_constraints = (Constraint **)JPH_STACK_ALLOC(cDetermineActiveConstraintsBatchSize * sizeof(Constraint *)); - - for (;;) - { - // Atomically fetch a batch of constraints - uint32 constraint_idx = ioStep->mDetermineActiveConstraintReadIdx.fetch_add(cDetermineActiveConstraintsBatchSize); - if (constraint_idx >= num_constraints) - break; - - // Calculate the end of the batch - uint32 constraint_idx_end = min(num_constraints, constraint_idx + cDetermineActiveConstraintsBatchSize); - - // Store the active constraints at the start of the step (bodies get activated during the step which in turn may activate constraints leading to an inconsistent shapshot) - mConstraintManager.GetActiveConstraints(constraint_idx, constraint_idx_end, active_constraints, num_active_constraints); - - // Copy the block of active constraints to the global list of active constraints - if (num_active_constraints > 0) - { - uint32 active_constraint_idx = ioStep->mNumActiveConstraints.fetch_add(num_active_constraints); - memcpy(ioStep->mContext->mActiveConstraints + active_constraint_idx, active_constraints, num_active_constraints * sizeof(Constraint *)); - } - } -} - -void PhysicsSystem::JobApplyGravity(const PhysicsUpdateContext *ioContext, PhysicsUpdateContext::Step *ioStep) -{ -#ifdef JPH_ENABLE_ASSERTS - // We update velocities and need the rotation to do so - BodyAccess::Grant grant(BodyAccess::EAccess::ReadWrite, BodyAccess::EAccess::Read); -#endif - - // Get list of active bodies that we had at the start of the physics update. - // Any body that is activated as part of the simulation step does not receive gravity this frame. - // Note that bodies may be activated during this job but not deactivated, this means that only elements - // will be added to the array. Since the array is made to not reallocate, this is a safe operation. - const BodyID *active_bodies = mBodyManager.GetActiveBodiesUnsafe(EBodyType::RigidBody); - uint32 num_active_bodies_at_step_start = ioStep->mNumActiveBodiesAtStepStart; - - // Fetch delta time once outside the loop - float delta_time = ioContext->mStepDeltaTime; - - // Update velocities from forces - for (;;) - { - // Atomically fetch a batch of bodies - uint32 active_body_idx = ioStep->mApplyGravityReadIdx.fetch_add(cApplyGravityBatchSize); - if (active_body_idx >= num_active_bodies_at_step_start) - break; - - // Calculate the end of the batch - uint32 active_body_idx_end = min(num_active_bodies_at_step_start, active_body_idx + cApplyGravityBatchSize); - - // Process the batch - while (active_body_idx < active_body_idx_end) - { - Body &body = mBodyManager.GetBody(active_bodies[active_body_idx]); - if (body.IsDynamic()) - { - MotionProperties *mp = body.GetMotionProperties(); - Quat rotation = body.GetRotation(); - - if (body.GetApplyGyroscopicForce()) - mp->ApplyGyroscopicForceInternal(rotation, delta_time); - - mp->ApplyForceTorqueAndDragInternal(rotation, mGravity, delta_time); - } - active_body_idx++; - } - } -} - -void PhysicsSystem::JobSetupVelocityConstraints(float inDeltaTime, PhysicsUpdateContext::Step *ioStep) const -{ -#ifdef JPH_ENABLE_ASSERTS - // We only read positions - BodyAccess::Grant grant(BodyAccess::EAccess::None, BodyAccess::EAccess::Read); -#endif - - uint32 num_constraints = ioStep->mNumActiveConstraints; - - for (;;) - { - // Atomically fetch a batch of constraints - uint32 constraint_idx = ioStep->mSetupVelocityConstraintsReadIdx.fetch_add(cSetupVelocityConstraintsBatchSize); - if (constraint_idx >= num_constraints) - break; - - ConstraintManager::sSetupVelocityConstraints(ioStep->mContext->mActiveConstraints + constraint_idx, min(cSetupVelocityConstraintsBatchSize, num_constraints - constraint_idx), inDeltaTime); - } -} - -void PhysicsSystem::JobBuildIslandsFromConstraints(PhysicsUpdateContext *ioContext, PhysicsUpdateContext::Step *ioStep) -{ -#ifdef JPH_ENABLE_ASSERTS - // We read constraints and positions - BodyAccess::Grant grant(BodyAccess::EAccess::None, BodyAccess::EAccess::Read); - - // Can only activate bodies - BodyManager::GrantActiveBodiesAccess grant_active(true, false); -#endif - - // Prepare the island builder - mIslandBuilder.PrepareNonContactConstraints(ioStep->mNumActiveConstraints, ioContext->mTempAllocator); - - // Build the islands - ConstraintManager::sBuildIslands(ioStep->mContext->mActiveConstraints, ioStep->mNumActiveConstraints, mIslandBuilder, mBodyManager); -} - -void PhysicsSystem::TrySpawnJobFindCollisions(PhysicsUpdateContext::Step *ioStep) const -{ - // Get how many jobs we can spawn and check if we can spawn more - uint max_jobs = ioStep->mBodyPairQueues.size(); - if (CountBits(ioStep->mActiveFindCollisionJobs) >= max_jobs) - return; - - // Count how many body pairs we have waiting - uint32 num_body_pairs = 0; - for (const PhysicsUpdateContext::BodyPairQueue &queue : ioStep->mBodyPairQueues) - num_body_pairs += queue.mWriteIdx - queue.mReadIdx; - - // Count how many active bodies we have waiting - uint32 num_active_bodies = mBodyManager.GetNumActiveBodies(EBodyType::RigidBody) - ioStep->mActiveBodyReadIdx; - - // Calculate how many jobs we would like - uint desired_num_jobs = min((num_body_pairs + cNarrowPhaseBatchSize - 1) / cNarrowPhaseBatchSize + (num_active_bodies + cActiveBodiesBatchSize - 1) / cActiveBodiesBatchSize, max_jobs); - - for (;;) - { - // Get the bit mask of active jobs and see if we can spawn more - PhysicsUpdateContext::JobMask current_active_jobs = ioStep->mActiveFindCollisionJobs; - if (CountBits(current_active_jobs) >= desired_num_jobs) - break; - - // Loop through all possible job indices - for (uint job_index = 0; job_index < max_jobs; ++job_index) - { - // Test if it has been started - PhysicsUpdateContext::JobMask job_mask = PhysicsUpdateContext::JobMask(1) << job_index; - if ((current_active_jobs & job_mask) == 0) - { - // Try to claim the job index - PhysicsUpdateContext::JobMask prev_value = ioStep->mActiveFindCollisionJobs.fetch_or(job_mask); - if ((prev_value & job_mask) == 0) - { - // Add dependencies from the find collisions job to the next jobs - ioStep->mUpdateBroadphaseFinalize.AddDependency(); - ioStep->mFinalizeIslands.AddDependency(); - - // Start the job - JobHandle job = ioStep->mContext->mJobSystem->CreateJob("FindCollisions", cColorFindCollisions, [step = ioStep, job_index]() - { - step->mContext->mPhysicsSystem->JobFindCollisions(step, job_index); - }); - - // Add the job to the job barrier so the main updating thread can execute the job too - ioStep->mContext->mBarrier->AddJob(job); - - // Spawn only 1 extra job at a time - return; - } - } - } - } -} - -static void sFinalizeContactAllocator(PhysicsUpdateContext::Step &ioStep, const ContactConstraintManager::ContactAllocator &inAllocator) -{ - // Atomically accumulate the number of found manifolds and body pairs - ioStep.mNumBodyPairs.fetch_add(inAllocator.mNumBodyPairs, memory_order_relaxed); - ioStep.mNumManifolds.fetch_add(inAllocator.mNumManifolds, memory_order_relaxed); - - // Combine update errors - ioStep.mContext->mErrors.fetch_or((uint32)inAllocator.mErrors, memory_order_relaxed); -} - -void PhysicsSystem::JobFindCollisions(PhysicsUpdateContext::Step *ioStep, int inJobIndex) -{ -#ifdef JPH_ENABLE_ASSERTS - // We read positions and read velocities (for elastic collisions) - BodyAccess::Grant grant(BodyAccess::EAccess::Read, BodyAccess::EAccess::Read); - - // Can only activate bodies - BodyManager::GrantActiveBodiesAccess grant_active(true, false); -#endif - - // Allocation context for allocating new contact points - ContactAllocator contact_allocator(mContactManager.GetContactAllocator()); - - // Determine initial queue to read pairs from if no broadphase work can be done - // (always start looking at results from the next job) - int read_queue_idx = (inJobIndex + 1) % ioStep->mBodyPairQueues.size(); - - for (;;) - { - // Check if there are active bodies to be processed - uint32 active_bodies_read_idx = ioStep->mActiveBodyReadIdx; - uint32 num_active_bodies = mBodyManager.GetNumActiveBodies(EBodyType::RigidBody); - if (active_bodies_read_idx < num_active_bodies) - { - // Take a batch of active bodies - uint32 active_bodies_read_idx_end = min(num_active_bodies, active_bodies_read_idx + cActiveBodiesBatchSize); - if (ioStep->mActiveBodyReadIdx.compare_exchange_strong(active_bodies_read_idx, active_bodies_read_idx_end)) - { - // Callback when a new body pair is found - class MyBodyPairCallback : public BodyPairCollector - { - public: - // Constructor - MyBodyPairCallback(PhysicsUpdateContext::Step *inStep, ContactAllocator &ioContactAllocator, int inJobIndex) : - mStep(inStep), - mContactAllocator(ioContactAllocator), - mJobIndex(inJobIndex) - { - } - - // Callback function when a body pair is found - virtual void AddHit(const BodyPair &inPair) override - { - // Check if we have space in our write queue - PhysicsUpdateContext::BodyPairQueue &queue = mStep->mBodyPairQueues[mJobIndex]; - uint32 body_pairs_in_queue = queue.mWriteIdx - queue.mReadIdx; - if (body_pairs_in_queue >= mStep->mMaxBodyPairsPerQueue) - { - // Buffer full, process the pair now - mStep->mContext->mPhysicsSystem->ProcessBodyPair(mContactAllocator, inPair); - } - else - { - // Store the pair in our own queue - mStep->mContext->mBodyPairs[mJobIndex * mStep->mMaxBodyPairsPerQueue + queue.mWriteIdx % mStep->mMaxBodyPairsPerQueue] = inPair; - ++queue.mWriteIdx; - } - } - - private: - PhysicsUpdateContext::Step * mStep; - ContactAllocator & mContactAllocator; - int mJobIndex; - }; - MyBodyPairCallback add_pair(ioStep, contact_allocator, inJobIndex); - - // Copy active bodies to temporary array, broadphase will reorder them - uint32 batch_size = active_bodies_read_idx_end - active_bodies_read_idx; - BodyID *active_bodies = (BodyID *)JPH_STACK_ALLOC(batch_size * sizeof(BodyID)); - memcpy(active_bodies, mBodyManager.GetActiveBodiesUnsafe(EBodyType::RigidBody) + active_bodies_read_idx, batch_size * sizeof(BodyID)); - - // Find pairs in the broadphase - mBroadPhase->FindCollidingPairs(active_bodies, batch_size, mPhysicsSettings.mSpeculativeContactDistance, *mObjectVsBroadPhaseLayerFilter, *mObjectLayerPairFilter, add_pair); - - // Check if we have enough pairs in the buffer to start a new job - const PhysicsUpdateContext::BodyPairQueue &queue = ioStep->mBodyPairQueues[inJobIndex]; - uint32 body_pairs_in_queue = queue.mWriteIdx - queue.mReadIdx; - if (body_pairs_in_queue >= cNarrowPhaseBatchSize) - TrySpawnJobFindCollisions(ioStep); - } - } - else - { - // Lockless loop to get the next body pair from the pairs buffer - const PhysicsUpdateContext *context = ioStep->mContext; - int first_read_queue_idx = read_queue_idx; - for (;;) - { - PhysicsUpdateContext::BodyPairQueue &queue = ioStep->mBodyPairQueues[read_queue_idx]; - - // Get the next pair to process - uint32 pair_idx = queue.mReadIdx; - - // If the pair hasn't been written yet - if (pair_idx >= queue.mWriteIdx) - { - // Go to the next queue - read_queue_idx = (read_queue_idx + 1) % ioStep->mBodyPairQueues.size(); - - // If we're back at the first queue, we've looked at all of them and found nothing - if (read_queue_idx == first_read_queue_idx) - { - // Collect information from the contact allocator and accumulate it in the step. - sFinalizeContactAllocator(*ioStep, contact_allocator); - - // Mark this job as inactive - ioStep->mActiveFindCollisionJobs.fetch_and(~PhysicsUpdateContext::JobMask(1 << inJobIndex)); - - // Trigger the next jobs - ioStep->mUpdateBroadphaseFinalize.RemoveDependency(); - ioStep->mFinalizeIslands.RemoveDependency(); - return; - } - - // Try again reading from the next queue - continue; - } - - // Copy the body pair out of the buffer - const BodyPair bp = context->mBodyPairs[read_queue_idx * ioStep->mMaxBodyPairsPerQueue + pair_idx % ioStep->mMaxBodyPairsPerQueue]; - - // Mark this pair as taken - if (queue.mReadIdx.compare_exchange_strong(pair_idx, pair_idx + 1)) - { - // Process the actual body pair - ProcessBodyPair(contact_allocator, bp); - break; - } - } - } - } -} - -void PhysicsSystem::ProcessBodyPair(ContactAllocator &ioContactAllocator, const BodyPair &inBodyPair) -{ - JPH_PROFILE_FUNCTION(); - - // Fetch body pair - Body *body1 = &mBodyManager.GetBody(inBodyPair.mBodyA); - Body *body2 = &mBodyManager.GetBody(inBodyPair.mBodyB); - JPH_ASSERT(body1->IsActive()); - - JPH_DET_LOG("ProcessBodyPair: id1: " << inBodyPair.mBodyA << " id2: " << inBodyPair.mBodyB << " p1: " << body1->GetCenterOfMassPosition() << " p2: " << body2->GetCenterOfMassPosition() << " r1: " << body1->GetRotation() << " r2: " << body2->GetRotation()); - - // Check for soft bodies - if (body2->IsSoftBody()) - { - // If the 2nd body is a soft body and not active, we activate it now - if (!body2->IsActive()) - mBodyManager.ActivateBodies(&inBodyPair.mBodyB, 1); - - // Soft body processing is done later in the pipeline - return; - } - - // Ensure that body1 has the higher motion type (i.e. dynamic trumps kinematic), this ensures that we do the collision detection in the space of a moving body, - // which avoids accuracy problems when testing a very large static object against a small dynamic object - // Ensure that body1 id < body2 id when motion types are the same. - if (body1->GetMotionType() < body2->GetMotionType() - || (body1->GetMotionType() == body2->GetMotionType() && inBodyPair.mBodyB < inBodyPair.mBodyA)) - swap(body1, body2); - - // Check if the contact points from the previous frame are reusable and if so copy them - bool pair_handled = false, constraint_created = false; - if (mPhysicsSettings.mUseBodyPairContactCache && !(body1->IsCollisionCacheInvalid() || body2->IsCollisionCacheInvalid())) - mContactManager.GetContactsFromCache(ioContactAllocator, *body1, *body2, pair_handled, constraint_created); - - // If the cache hasn't handled this body pair do actual collision detection - if (!pair_handled) - { - // Create entry in the cache for this body pair - // Needs to happen irrespective if we found a collision or not (we want to remember that no collision was found too) - ContactConstraintManager::BodyPairHandle body_pair_handle = mContactManager.AddBodyPair(ioContactAllocator, *body1, *body2); - if (body_pair_handle == nullptr) - return; // Out of cache space - - // Create the query settings - CollideShapeSettings settings; - settings.mCollectFacesMode = ECollectFacesMode::CollectFaces; - settings.mActiveEdgeMode = mPhysicsSettings.mCheckActiveEdges? EActiveEdgeMode::CollideOnlyWithActive : EActiveEdgeMode::CollideWithAll; - settings.mMaxSeparationDistance = body1->IsSensor() || body2->IsSensor()? 0.0f : mPhysicsSettings.mSpeculativeContactDistance; - settings.mActiveEdgeMovementDirection = body1->GetLinearVelocity() - body2->GetLinearVelocity(); - - // Get transforms relative to body1 - RVec3 offset = body1->GetCenterOfMassPosition(); - Mat44 transform1 = Mat44::sRotation(body1->GetRotation()); - Mat44 transform2 = body2->GetCenterOfMassTransform().PostTranslated(-offset).ToMat44(); - - if (mPhysicsSettings.mUseManifoldReduction // Check global flag - && body1->GetUseManifoldReductionWithBody(*body2)) // Check body flag - { - // Version WITH contact manifold reduction - - class MyManifold : public ContactManifold - { - public: - Vec3 mFirstWorldSpaceNormal; - }; - - // A temporary structure that allows us to keep track of the all manifolds between this body pair - using Manifolds = StaticArray; - - // Create collector - class ReductionCollideShapeCollector : public CollideShapeCollector - { - public: - ReductionCollideShapeCollector(PhysicsSystem *inSystem, const Body *inBody1, const Body *inBody2) : - mSystem(inSystem), - mBody1(inBody1), - mBody2(inBody2) - { - } - - virtual void AddHit(const CollideShapeResult &inResult) override - { - // The first body should be the one with the highest motion type - JPH_ASSERT(mBody1->GetMotionType() >= mBody2->GetMotionType()); - JPH_ASSERT(!ShouldEarlyOut()); - - // Test if we want to accept this hit - if (mValidateBodyPair) - { - switch (mSystem->mContactManager.ValidateContactPoint(*mBody1, *mBody2, mBody1->GetCenterOfMassPosition(), inResult)) - { - case ValidateResult::AcceptContact: - // We're just accepting this one, nothing to do - break; - - case ValidateResult::AcceptAllContactsForThisBodyPair: - // Accept and stop calling the validate callback - mValidateBodyPair = false; - break; - - case ValidateResult::RejectContact: - // Skip this contact - return; - - case ValidateResult::RejectAllContactsForThisBodyPair: - // Skip this and early out - ForceEarlyOut(); - return; - } - } - - // Calculate normal - Vec3 world_space_normal = inResult.mPenetrationAxis.Normalized(); - - // Check if we can add it to an existing manifold - Manifolds::iterator manifold; - float contact_normal_cos_max_delta_rot = mSystem->mPhysicsSettings.mContactNormalCosMaxDeltaRotation; - for (manifold = mManifolds.begin(); manifold != mManifolds.end(); ++manifold) - if (world_space_normal.Dot(manifold->mFirstWorldSpaceNormal) >= contact_normal_cos_max_delta_rot) - { - // Update average normal - manifold->mWorldSpaceNormal += world_space_normal; - manifold->mPenetrationDepth = max(manifold->mPenetrationDepth, inResult.mPenetrationDepth); - break; - } - if (manifold == mManifolds.end()) - { - // Check if array is full - if (mManifolds.size() == mManifolds.capacity()) - { - // Full, find manifold with least amount of penetration - manifold = mManifolds.begin(); - for (Manifolds::iterator m = mManifolds.begin() + 1; m < mManifolds.end(); ++m) - if (m->mPenetrationDepth < manifold->mPenetrationDepth) - manifold = m; - - // If this contacts penetration is smaller than the smallest manifold, we skip this contact - if (inResult.mPenetrationDepth < manifold->mPenetrationDepth) - return; - - // Replace the manifold - *manifold = { { mBody1->GetCenterOfMassPosition(), world_space_normal, inResult.mPenetrationDepth, inResult.mSubShapeID1, inResult.mSubShapeID2, { }, { } }, world_space_normal }; - } - else - { - // Not full, create new manifold - mManifolds.push_back({ { mBody1->GetCenterOfMassPosition(), world_space_normal, inResult.mPenetrationDepth, inResult.mSubShapeID1, inResult.mSubShapeID2, { }, { } }, world_space_normal }); - manifold = mManifolds.end() - 1; - } - } - - // Determine contact points - const PhysicsSettings &settings = mSystem->mPhysicsSettings; - ManifoldBetweenTwoFaces(inResult.mContactPointOn1, inResult.mContactPointOn2, inResult.mPenetrationAxis, Square(settings.mSpeculativeContactDistance) + settings.mManifoldToleranceSq, inResult.mShape1Face, inResult.mShape2Face, manifold->mRelativeContactPointsOn1, manifold->mRelativeContactPointsOn2 JPH_IF_DEBUG_RENDERER(, mBody1->GetCenterOfMassPosition())); - - // Prune if we have more than 32 points (this means we could run out of space in the next iteration) - if (manifold->mRelativeContactPointsOn1.size() > 32) - PruneContactPoints(manifold->mFirstWorldSpaceNormal, manifold->mRelativeContactPointsOn1, manifold->mRelativeContactPointsOn2 JPH_IF_DEBUG_RENDERER(, manifold->mBaseOffset)); - } - - PhysicsSystem * mSystem; - const Body * mBody1; - const Body * mBody2; - bool mValidateBodyPair = true; - Manifolds mManifolds; - }; - ReductionCollideShapeCollector collector(this, body1, body2); - - // Perform collision detection between the two shapes - SubShapeIDCreator part1, part2; - auto f = body1->GetEnhancedInternalEdgeRemovalWithBody(*body2)? InternalEdgeRemovingCollector::sCollideShapeVsShape : CollisionDispatch::sCollideShapeVsShape; - f(body1->GetShape(), body2->GetShape(), Vec3::sReplicate(1.0f), Vec3::sReplicate(1.0f), transform1, transform2, part1, part2, settings, collector, { }); - - // Add the contacts - for (ContactManifold &manifold : collector.mManifolds) - { - // Normalize the normal (is a sum of all normals from merged manifolds) - manifold.mWorldSpaceNormal = manifold.mWorldSpaceNormal.Normalized(); - - // If we still have too many points, prune them now - if (manifold.mRelativeContactPointsOn1.size() > 4) - PruneContactPoints(manifold.mWorldSpaceNormal, manifold.mRelativeContactPointsOn1, manifold.mRelativeContactPointsOn2 JPH_IF_DEBUG_RENDERER(, manifold.mBaseOffset)); - - // Actually add the contact points to the manager - constraint_created |= mContactManager.AddContactConstraint(ioContactAllocator, body_pair_handle, *body1, *body2, manifold); - } - } - else - { - // Version WITHOUT contact manifold reduction - - // Create collector - class NonReductionCollideShapeCollector : public CollideShapeCollector - { - public: - NonReductionCollideShapeCollector(PhysicsSystem *inSystem, ContactAllocator &ioContactAllocator, Body *inBody1, Body *inBody2, const ContactConstraintManager::BodyPairHandle &inPairHandle) : - mSystem(inSystem), - mContactAllocator(ioContactAllocator), - mBody1(inBody1), - mBody2(inBody2), - mBodyPairHandle(inPairHandle) - { - } - - virtual void AddHit(const CollideShapeResult &inResult) override - { - // The first body should be the one with the highest motion type - JPH_ASSERT(mBody1->GetMotionType() >= mBody2->GetMotionType()); - JPH_ASSERT(!ShouldEarlyOut()); - - // Test if we want to accept this hit - if (mValidateBodyPair) - { - switch (mSystem->mContactManager.ValidateContactPoint(*mBody1, *mBody2, mBody1->GetCenterOfMassPosition(), inResult)) - { - case ValidateResult::AcceptContact: - // We're just accepting this one, nothing to do - break; - - case ValidateResult::AcceptAllContactsForThisBodyPair: - // Accept and stop calling the validate callback - mValidateBodyPair = false; - break; - - case ValidateResult::RejectContact: - // Skip this contact - return; - - case ValidateResult::RejectAllContactsForThisBodyPair: - // Skip this and early out - ForceEarlyOut(); - return; - } - } - - // Determine contact points - ContactManifold manifold; - manifold.mBaseOffset = mBody1->GetCenterOfMassPosition(); - const PhysicsSettings &settings = mSystem->mPhysicsSettings; - ManifoldBetweenTwoFaces(inResult.mContactPointOn1, inResult.mContactPointOn2, inResult.mPenetrationAxis, Square(settings.mSpeculativeContactDistance) + settings.mManifoldToleranceSq, inResult.mShape1Face, inResult.mShape2Face, manifold.mRelativeContactPointsOn1, manifold.mRelativeContactPointsOn2 JPH_IF_DEBUG_RENDERER(, manifold.mBaseOffset)); - - // Calculate normal - manifold.mWorldSpaceNormal = inResult.mPenetrationAxis.Normalized(); - - // Store penetration depth - manifold.mPenetrationDepth = inResult.mPenetrationDepth; - - // Prune if we have more than 4 points - if (manifold.mRelativeContactPointsOn1.size() > 4) - PruneContactPoints(manifold.mWorldSpaceNormal, manifold.mRelativeContactPointsOn1, manifold.mRelativeContactPointsOn2 JPH_IF_DEBUG_RENDERER(, manifold.mBaseOffset)); - - // Set other properties - manifold.mSubShapeID1 = inResult.mSubShapeID1; - manifold.mSubShapeID2 = inResult.mSubShapeID2; - - // Actually add the contact points to the manager - mConstraintCreated |= mSystem->mContactManager.AddContactConstraint(mContactAllocator, mBodyPairHandle, *mBody1, *mBody2, manifold); - } - - PhysicsSystem * mSystem; - ContactAllocator & mContactAllocator; - Body * mBody1; - Body * mBody2; - ContactConstraintManager::BodyPairHandle mBodyPairHandle; - bool mValidateBodyPair = true; - bool mConstraintCreated = false; - }; - NonReductionCollideShapeCollector collector(this, ioContactAllocator, body1, body2, body_pair_handle); - - // Perform collision detection between the two shapes - SubShapeIDCreator part1, part2; - auto f = body1->GetEnhancedInternalEdgeRemovalWithBody(*body2)? InternalEdgeRemovingCollector::sCollideShapeVsShape : CollisionDispatch::sCollideShapeVsShape; - f(body1->GetShape(), body2->GetShape(), Vec3::sReplicate(1.0f), Vec3::sReplicate(1.0f), transform1, transform2, part1, part2, settings, collector, { }); - - constraint_created = collector.mConstraintCreated; - } - } - - // If a contact constraint was created, we need to do some extra work - if (constraint_created) - { - // Wake up sleeping bodies - BodyID body_ids[2]; - int num_bodies = 0; - if (body1->IsDynamic() && !body1->IsActive()) - body_ids[num_bodies++] = body1->GetID(); - if (body2->IsDynamic() && !body2->IsActive()) - body_ids[num_bodies++] = body2->GetID(); - if (num_bodies > 0) - mBodyManager.ActivateBodies(body_ids, num_bodies); - - // Link the two bodies - mIslandBuilder.LinkBodies(body1->GetIndexInActiveBodiesInternal(), body2->GetIndexInActiveBodiesInternal()); - } -} - -void PhysicsSystem::JobFinalizeIslands(PhysicsUpdateContext *ioContext) -{ -#ifdef JPH_ENABLE_ASSERTS - // We only touch island data - BodyAccess::Grant grant(BodyAccess::EAccess::None, BodyAccess::EAccess::None); -#endif - - // Finish collecting the islands, at this point the active body list doesn't change so it's safe to access - mIslandBuilder.Finalize(mBodyManager.GetActiveBodiesUnsafe(EBodyType::RigidBody), mBodyManager.GetNumActiveBodies(EBodyType::RigidBody), mContactManager.GetNumConstraints(), ioContext->mTempAllocator); - - // Prepare the large island splitter - if (mPhysicsSettings.mUseLargeIslandSplitter) - mLargeIslandSplitter.Prepare(mIslandBuilder, mBodyManager.GetNumActiveBodies(EBodyType::RigidBody), ioContext->mTempAllocator); -} - -void PhysicsSystem::JobBodySetIslandIndex() -{ -#ifdef JPH_ENABLE_ASSERTS - // We only touch island data - BodyAccess::Grant grant(BodyAccess::EAccess::None, BodyAccess::EAccess::None); -#endif - - // Loop through the result and tag all bodies with an island index - for (uint32 island_idx = 0, n = mIslandBuilder.GetNumIslands(); island_idx < n; ++island_idx) - { - BodyID *body_start, *body_end; - mIslandBuilder.GetBodiesInIsland(island_idx, body_start, body_end); - for (const BodyID *body = body_start; body < body_end; ++body) - mBodyManager.GetBody(*body).GetMotionProperties()->SetIslandIndexInternal(island_idx); - } -} - -JPH_SUPPRESS_WARNING_PUSH -JPH_CLANG_SUPPRESS_WARNING("-Wundefined-func-template") // ConstraintManager::sWarmStartVelocityConstraints / ContactConstraintManager::WarmStartVelocityConstraints is instantiated in the cpp file - -void PhysicsSystem::JobSolveVelocityConstraints(PhysicsUpdateContext *ioContext, PhysicsUpdateContext::Step *ioStep) -{ -#ifdef JPH_ENABLE_ASSERTS - // We update velocities and need to read positions to do so - BodyAccess::Grant grant(BodyAccess::EAccess::ReadWrite, BodyAccess::EAccess::Read); -#endif - - float delta_time = ioContext->mStepDeltaTime; - Constraint **active_constraints = ioContext->mActiveConstraints; - - // Only the first step to correct for the delta time difference in the previous update - float warm_start_impulse_ratio = ioStep->mIsFirst? ioContext->mWarmStartImpulseRatio : 1.0f; - - bool check_islands = true, check_split_islands = mPhysicsSettings.mUseLargeIslandSplitter; - do - { - // First try to get work from large islands - if (check_split_islands) - { - bool first_iteration; - uint split_island_index; - uint32 *constraints_begin, *constraints_end, *contacts_begin, *contacts_end; - switch (mLargeIslandSplitter.FetchNextBatch(split_island_index, constraints_begin, constraints_end, contacts_begin, contacts_end, first_iteration)) - { - case LargeIslandSplitter::EStatus::BatchRetrieved: - { - if (first_iteration) - { - // Iteration 0 is used to warm start the batch (we added 1 to the number of iterations in LargeIslandSplitter::SplitIsland) - DummyCalculateSolverSteps dummy; - ConstraintManager::sWarmStartVelocityConstraints(active_constraints, constraints_begin, constraints_end, warm_start_impulse_ratio, dummy); - mContactManager.WarmStartVelocityConstraints(contacts_begin, contacts_end, warm_start_impulse_ratio, dummy); - } - else - { - // Solve velocity constraints - ConstraintManager::sSolveVelocityConstraints(active_constraints, constraints_begin, constraints_end, delta_time); - mContactManager.SolveVelocityConstraints(contacts_begin, contacts_end); - } - - // Mark the batch as processed - bool last_iteration, final_batch; - mLargeIslandSplitter.MarkBatchProcessed(split_island_index, constraints_begin, constraints_end, contacts_begin, contacts_end, last_iteration, final_batch); - - // Save back the lambdas in the contact cache for the warm start of the next physics update - if (last_iteration) - mContactManager.StoreAppliedImpulses(contacts_begin, contacts_end); - - // We processed work, loop again - continue; - } - case LargeIslandSplitter::EStatus::WaitingForBatch: - break; - case LargeIslandSplitter::EStatus::AllBatchesDone: - check_split_islands = false; - break; - } - } - - // If that didn't succeed try to process an island - if (check_islands) - { - // Next island - uint32 island_idx = ioStep->mSolveVelocityConstraintsNextIsland++; - if (island_idx >= mIslandBuilder.GetNumIslands()) - { - // We processed all islands, stop checking islands - check_islands = false; - continue; - } - - JPH_PROFILE("Island"); - - // Get iterators for this island - uint32 *constraints_begin, *constraints_end, *contacts_begin, *contacts_end; - bool has_constraints = mIslandBuilder.GetConstraintsInIsland(island_idx, constraints_begin, constraints_end); - bool has_contacts = mIslandBuilder.GetContactsInIsland(island_idx, contacts_begin, contacts_end); - - // If we don't have any contacts or constraints, we know that none of the following islands have any contacts or constraints - // (because they're sorted by most constraints first). This means we're done. - if (!has_contacts && !has_constraints) - { - #ifdef JPH_ENABLE_ASSERTS - // Validate our assumption that the next islands don't have any constraints or contacts - for (; island_idx < mIslandBuilder.GetNumIslands(); ++island_idx) - { - JPH_ASSERT(!mIslandBuilder.GetConstraintsInIsland(island_idx, constraints_begin, constraints_end)); - JPH_ASSERT(!mIslandBuilder.GetContactsInIsland(island_idx, contacts_begin, contacts_end)); - } - #endif // JPH_ENABLE_ASSERTS - - check_islands = false; - continue; - } - - // Sorting is costly but needed for a deterministic simulation, allow the user to turn this off - if (mPhysicsSettings.mDeterministicSimulation) - { - // Sort constraints to give a deterministic simulation - ConstraintManager::sSortConstraints(active_constraints, constraints_begin, constraints_end); - - // Sort contacts to give a deterministic simulation - mContactManager.SortContacts(contacts_begin, contacts_end); - } - - // Split up large islands - CalculateSolverSteps steps_calculator(mPhysicsSettings); - if (mPhysicsSettings.mUseLargeIslandSplitter - && mLargeIslandSplitter.SplitIsland(island_idx, mIslandBuilder, mBodyManager, mContactManager, active_constraints, steps_calculator)) - continue; // Loop again to try to fetch the newly split island - - // We didn't create a split, just run the solver now for this entire island. Begin by warm starting. - ConstraintManager::sWarmStartVelocityConstraints(active_constraints, constraints_begin, constraints_end, warm_start_impulse_ratio, steps_calculator); - mContactManager.WarmStartVelocityConstraints(contacts_begin, contacts_end, warm_start_impulse_ratio, steps_calculator); - steps_calculator.Finalize(); - - // Store the number of position steps for later - mIslandBuilder.SetNumPositionSteps(island_idx, steps_calculator.GetNumPositionSteps()); - - // Solve velocity constraints - for (uint velocity_step = 0; velocity_step < steps_calculator.GetNumVelocitySteps(); ++velocity_step) - { - bool applied_impulse = ConstraintManager::sSolveVelocityConstraints(active_constraints, constraints_begin, constraints_end, delta_time); - applied_impulse |= mContactManager.SolveVelocityConstraints(contacts_begin, contacts_end); - if (!applied_impulse) - break; - } - - // Save back the lambdas in the contact cache for the warm start of the next physics update - mContactManager.StoreAppliedImpulses(contacts_begin, contacts_end); - - // We processed work, loop again - continue; - } - - // If we didn't find any work, give up a time slice - std::this_thread::yield(); - } - while (check_islands || check_split_islands); -} - -JPH_SUPPRESS_WARNING_POP - -void PhysicsSystem::JobPreIntegrateVelocity(PhysicsUpdateContext *ioContext, PhysicsUpdateContext::Step *ioStep) -{ - // Reserve enough space for all bodies that may need a cast - TempAllocator *temp_allocator = ioContext->mTempAllocator; - JPH_ASSERT(ioStep->mCCDBodies == nullptr); - ioStep->mCCDBodiesCapacity = mBodyManager.GetNumActiveCCDBodies(); - ioStep->mCCDBodies = (CCDBody *)temp_allocator->Allocate(ioStep->mCCDBodiesCapacity * sizeof(CCDBody)); - - // Initialize the mapping table between active body and CCD body - JPH_ASSERT(ioStep->mActiveBodyToCCDBody == nullptr); - ioStep->mNumActiveBodyToCCDBody = mBodyManager.GetNumActiveBodies(EBodyType::RigidBody); - ioStep->mActiveBodyToCCDBody = (int *)temp_allocator->Allocate(ioStep->mNumActiveBodyToCCDBody * sizeof(int)); - - // Prepare the split island builder for solving the position constraints - mLargeIslandSplitter.PrepareForSolvePositions(); -} - -void PhysicsSystem::JobIntegrateVelocity(const PhysicsUpdateContext *ioContext, PhysicsUpdateContext::Step *ioStep) -{ -#ifdef JPH_ENABLE_ASSERTS - // We update positions and need velocity to do so, we also clamp velocities so need to write to them - BodyAccess::Grant grant(BodyAccess::EAccess::ReadWrite, BodyAccess::EAccess::ReadWrite); -#endif - - float delta_time = ioContext->mStepDeltaTime; - const BodyID *active_bodies = mBodyManager.GetActiveBodiesUnsafe(EBodyType::RigidBody); - uint32 num_active_bodies = mBodyManager.GetNumActiveBodies(EBodyType::RigidBody); - uint32 num_active_bodies_after_find_collisions = ioStep->mActiveBodyReadIdx; - - // We can move bodies that are not part of an island. In this case we need to notify the broadphase of the movement. - static constexpr int cBodiesBatch = 64; - BodyID *bodies_to_update_bounds = (BodyID *)JPH_STACK_ALLOC(cBodiesBatch * sizeof(BodyID)); - int num_bodies_to_update_bounds = 0; - - for (;;) - { - // Atomically fetch a batch of bodies - uint32 active_body_idx = ioStep->mIntegrateVelocityReadIdx.fetch_add(cIntegrateVelocityBatchSize); - if (active_body_idx >= num_active_bodies) - break; - - // Calculate the end of the batch - uint32 active_body_idx_end = min(num_active_bodies, active_body_idx + cIntegrateVelocityBatchSize); - - // Process the batch - while (active_body_idx < active_body_idx_end) - { - // Update the positions using an Symplectic Euler step (which integrates using the updated velocity v1' rather - // than the original velocity v1): - // x1' = x1 + h * v1' - // At this point the active bodies list does not change, so it is safe to access the array. - BodyID body_id = active_bodies[active_body_idx]; - Body &body = mBodyManager.GetBody(body_id); - MotionProperties *mp = body.GetMotionProperties(); - - JPH_DET_LOG("JobIntegrateVelocity: id: " << body_id << " v: " << body.GetLinearVelocity() << " w: " << body.GetAngularVelocity()); - - // Clamp velocities (not for kinematic bodies) - if (body.IsDynamic()) - { - mp->ClampLinearVelocity(); - mp->ClampAngularVelocity(); - } - - // Update the rotation of the body according to the angular velocity - // For motion type discrete we need to do this anyway, for motion type linear cast we have multiple choices - // 1. Rotate the body first and then sweep - // 2. First sweep and then rotate the body at the end - // 3. Pick some in between rotation (e.g. half way), then sweep and finally rotate the remainder - // (1) has some clear advantages as when a long thin body hits a surface away from the center of mass, this will result in a large angular velocity and a limited reduction in linear velocity. - // When simulation the rotation first before doing the translation, the body will be able to rotate away from the contact point allowing the center of mass to approach the surface. When using - // approach (2) in this case what will happen is that we will immediately detect the same collision again (the body has not rotated and the body was already colliding at the end of the previous - // time step) resulting in a lot of stolen time and the body appearing to be frozen in an unnatural pose (like it is glued at an angle to the surface). (2) obviously has some negative side effects - // too as simulating the rotation first may cause it to tunnel through a small object that the linear cast might have otherwise detected. In any case a linear cast is not good for detecting - // tunneling due to angular rotation, so we don't care about that too much (you'd need a full cast to take angular effects into account). - body.AddRotationStep(body.GetAngularVelocity() * delta_time); - - // Get delta position - Vec3 delta_pos = body.GetLinearVelocity() * delta_time; - - // If the position should be updated (or if it is delayed because of CCD) - bool update_position = true; - - switch (mp->GetMotionQuality()) - { - case EMotionQuality::Discrete: - // No additional collision checking to be done - break; - - case EMotionQuality::LinearCast: - if (body.IsDynamic() // Kinematic bodies cannot be stopped - && !body.IsSensor()) // We don't support CCD sensors - { - // Determine inner radius (the smallest sphere that fits into the shape) - float inner_radius = body.GetShape()->GetInnerRadius(); - JPH_ASSERT(inner_radius > 0.0f, "The shape has no inner radius, this makes the shape unsuitable for the linear cast motion quality as we cannot move it without risking tunneling."); - - // Measure translation in this step and check if it above the threshold to perform a linear cast - float linear_cast_threshold_sq = Square(mPhysicsSettings.mLinearCastThreshold * inner_radius); - if (delta_pos.LengthSq() > linear_cast_threshold_sq) - { - // This body needs a cast - uint32 ccd_body_idx = ioStep->mNumCCDBodies++; - JPH_ASSERT(active_body_idx < ioStep->mNumActiveBodyToCCDBody); - ioStep->mActiveBodyToCCDBody[active_body_idx] = ccd_body_idx; - new (&ioStep->mCCDBodies[ccd_body_idx]) CCDBody(body_id, delta_pos, linear_cast_threshold_sq, min(mPhysicsSettings.mPenetrationSlop, mPhysicsSettings.mLinearCastMaxPenetration * inner_radius)); - - update_position = false; - } - } - break; - } - - if (update_position) - { - // Move the body now - body.AddPositionStep(delta_pos); - - // If the body was activated due to an earlier CCD step it will have an index in the active - // body list that it higher than the highest one we processed during FindCollisions - // which means it hasn't been assigned an island and will not be updated by an island - // this means that we need to update its bounds manually - if (mp->GetIndexInActiveBodiesInternal() >= num_active_bodies_after_find_collisions) - { - body.CalculateWorldSpaceBoundsInternal(); - bodies_to_update_bounds[num_bodies_to_update_bounds++] = body.GetID(); - if (num_bodies_to_update_bounds == cBodiesBatch) - { - // Buffer full, flush now - mBroadPhase->NotifyBodiesAABBChanged(bodies_to_update_bounds, num_bodies_to_update_bounds, false); - num_bodies_to_update_bounds = 0; - } - } - - // We did not create a CCD body - ioStep->mActiveBodyToCCDBody[active_body_idx] = -1; - } - - active_body_idx++; - } - } - - // Notify change bounds on requested bodies - if (num_bodies_to_update_bounds > 0) - mBroadPhase->NotifyBodiesAABBChanged(bodies_to_update_bounds, num_bodies_to_update_bounds, false); -} - -void PhysicsSystem::JobPostIntegrateVelocity(PhysicsUpdateContext *ioContext, PhysicsUpdateContext::Step *ioStep) const -{ - // Validate that our reservations were correct - JPH_ASSERT(ioStep->mNumCCDBodies <= mBodyManager.GetNumActiveCCDBodies()); - - if (ioStep->mNumCCDBodies == 0) - { - // No continuous collision detection jobs -> kick the next job ourselves - ioStep->mContactRemovedCallbacks.RemoveDependency(); - } - else - { - // Run the continuous collision detection jobs - int num_continuous_collision_jobs = min(int(ioStep->mNumCCDBodies + cNumCCDBodiesPerJob - 1) / cNumCCDBodiesPerJob, ioContext->GetMaxConcurrency()); - ioStep->mResolveCCDContacts.AddDependency(num_continuous_collision_jobs); - ioStep->mContactRemovedCallbacks.AddDependency(num_continuous_collision_jobs - 1); // Already had 1 dependency - for (int i = 0; i < num_continuous_collision_jobs; ++i) - { - JobHandle job = ioContext->mJobSystem->CreateJob("FindCCDContacts", cColorFindCCDContacts, [ioContext, ioStep]() - { - ioContext->mPhysicsSystem->JobFindCCDContacts(ioContext, ioStep); - - ioStep->mResolveCCDContacts.RemoveDependency(); - ioStep->mContactRemovedCallbacks.RemoveDependency(); - }); - ioContext->mBarrier->AddJob(job); - } - } -} - -// Helper function to calculate the motion of a body during this CCD step -inline static Vec3 sCalculateBodyMotion(const Body &inBody, float inDeltaTime) -{ - // If the body is linear casting, the body has not yet moved so we need to calculate its motion - if (inBody.IsDynamic() && inBody.GetMotionProperties()->GetMotionQuality() == EMotionQuality::LinearCast) - return inDeltaTime * inBody.GetLinearVelocity(); - - // Body has already moved, so we don't need to correct for anything - return Vec3::sZero(); -} - -// Helper function that finds the CCD body corresponding to a body (if it exists) -inline static PhysicsUpdateContext::Step::CCDBody *sGetCCDBody(const Body &inBody, PhysicsUpdateContext::Step *inStep) -{ - // Only rigid bodies can have a CCD body - if (!inBody.IsRigidBody()) - return nullptr; - - // If the body has no motion properties it cannot have a CCD body - const MotionProperties *motion_properties = inBody.GetMotionPropertiesUnchecked(); - if (motion_properties == nullptr) - return nullptr; - - // If it is not active it cannot have a CCD body - uint32 active_index = motion_properties->GetIndexInActiveBodiesInternal(); - if (active_index == Body::cInactiveIndex) - return nullptr; - - // Check if the active body has a corresponding CCD body - JPH_ASSERT(active_index < inStep->mNumActiveBodyToCCDBody); // Ensure that the body has a mapping to CCD body - int ccd_index = inStep->mActiveBodyToCCDBody[active_index]; - if (ccd_index < 0) - return nullptr; - - PhysicsUpdateContext::Step::CCDBody *ccd_body = &inStep->mCCDBodies[ccd_index]; - JPH_ASSERT(ccd_body->mBodyID1 == inBody.GetID(), "We found the wrong CCD body!"); - return ccd_body; -} - -void PhysicsSystem::JobFindCCDContacts(const PhysicsUpdateContext *ioContext, PhysicsUpdateContext::Step *ioStep) -{ -#ifdef JPH_ENABLE_ASSERTS - // We only read positions, but the validate callback may read body positions and velocities - BodyAccess::Grant grant(BodyAccess::EAccess::Read, BodyAccess::EAccess::Read); -#endif - - // Allocation context for allocating new contact points - ContactAllocator contact_allocator(mContactManager.GetContactAllocator()); - - // Settings - ShapeCastSettings settings; - settings.mUseShrunkenShapeAndConvexRadius = true; - settings.mBackFaceModeTriangles = EBackFaceMode::IgnoreBackFaces; - settings.mBackFaceModeConvex = EBackFaceMode::IgnoreBackFaces; - settings.mReturnDeepestPoint = true; - settings.mCollectFacesMode = ECollectFacesMode::CollectFaces; - settings.mActiveEdgeMode = mPhysicsSettings.mCheckActiveEdges? EActiveEdgeMode::CollideOnlyWithActive : EActiveEdgeMode::CollideWithAll; - - for (;;) - { - // Fetch the next body to cast - uint32 idx = ioStep->mNextCCDBody++; - if (idx >= ioStep->mNumCCDBodies) - break; - CCDBody &ccd_body = ioStep->mCCDBodies[idx]; - const Body &body = mBodyManager.GetBody(ccd_body.mBodyID1); - - // Filter out layers - DefaultBroadPhaseLayerFilter broadphase_layer_filter = GetDefaultBroadPhaseLayerFilter(body.GetObjectLayer()); - DefaultObjectLayerFilter object_layer_filter = GetDefaultLayerFilter(body.GetObjectLayer()); - - #ifdef JPH_DEBUG_RENDERER - // Draw start and end shape of cast - if (sDrawMotionQualityLinearCast) - { - RMat44 com = body.GetCenterOfMassTransform(); - body.GetShape()->Draw(DebugRenderer::sInstance, com, Vec3::sReplicate(1.0f), Color::sGreen, false, true); - DebugRenderer::sInstance->DrawArrow(com.GetTranslation(), com.GetTranslation() + ccd_body.mDeltaPosition, Color::sGreen, 0.1f); - body.GetShape()->Draw(DebugRenderer::sInstance, com.PostTranslated(ccd_body.mDeltaPosition), Vec3::sReplicate(1.0f), Color::sRed, false, true); - } - #endif // JPH_DEBUG_RENDERER - - // Create a collector that will find the maximum distance allowed to travel while not penetrating more than 'max penetration' - class CCDNarrowPhaseCollector : public CastShapeCollector - { - public: - CCDNarrowPhaseCollector(const BodyManager &inBodyManager, ContactConstraintManager &inContactConstraintManager, CCDBody &inCCDBody, ShapeCastResult &inResult, float inDeltaTime) : - mBodyManager(inBodyManager), - mContactConstraintManager(inContactConstraintManager), - mCCDBody(inCCDBody), - mResult(inResult), - mDeltaTime(inDeltaTime) - { - } - - virtual void AddHit(const ShapeCastResult &inResult) override - { - JPH_PROFILE_FUNCTION(); - - // Check if this is a possible earlier hit than the one before - float fraction = inResult.mFraction; - if (fraction < mCCDBody.mFractionPlusSlop) - { - // Normalize normal - Vec3 normal = inResult.mPenetrationAxis.Normalized(); - - // Calculate how much we can add to the fraction to penetrate the collision point by mMaxPenetration. - // Note that the normal is pointing towards body 2! - // Let the extra distance that we can travel along delta_pos be 'dist': mMaxPenetration / dist = cos(angle between normal and delta_pos) = normal . delta_pos / |delta_pos| - // <=> dist = mMaxPenetration * |delta_pos| / normal . delta_pos - // Converting to a faction: delta_fraction = dist / |delta_pos| = mLinearCastTreshold / normal . delta_pos - float denominator = normal.Dot(mCCDBody.mDeltaPosition); - if (denominator > mCCDBody.mMaxPenetration) // Avoid dividing by zero, if extra hit fraction > 1 there's also no point in continuing - { - float fraction_plus_slop = fraction + mCCDBody.mMaxPenetration / denominator; - if (fraction_plus_slop < mCCDBody.mFractionPlusSlop) - { - const Body &body2 = mBodyManager.GetBody(inResult.mBodyID2); - - // Check if we've already accepted all hits from this body - if (mValidateBodyPair) - { - // Validate the contact result - const Body &body1 = mBodyManager.GetBody(mCCDBody.mBodyID1); - ValidateResult validate_result = mContactConstraintManager.ValidateContactPoint(body1, body2, body1.GetCenterOfMassPosition(), inResult); // Note that the center of mass of body 1 is the start of the sweep and is used as base offset below - switch (validate_result) - { - case ValidateResult::AcceptContact: - // Just continue - break; - - case ValidateResult::AcceptAllContactsForThisBodyPair: - // Accept this and all following contacts from this body - mValidateBodyPair = false; - break; - - case ValidateResult::RejectContact: - return; - - case ValidateResult::RejectAllContactsForThisBodyPair: - // Reject this and all following contacts from this body - mRejectAll = true; - ForceEarlyOut(); - return; - } - } - - // This is the earliest hit so far, store it - mCCDBody.mContactNormal = normal; - mCCDBody.mBodyID2 = inResult.mBodyID2; - mCCDBody.mSubShapeID2 = inResult.mSubShapeID2; - mCCDBody.mFraction = fraction; - mCCDBody.mFractionPlusSlop = fraction_plus_slop; - mResult = inResult; - - // Result was assuming body 2 is not moving, but it is, so we need to correct for it - Vec3 movement2 = fraction * sCalculateBodyMotion(body2, mDeltaTime); - if (!movement2.IsNearZero()) - { - mResult.mContactPointOn1 += movement2; - mResult.mContactPointOn2 += movement2; - for (Vec3 &v : mResult.mShape1Face) - v += movement2; - for (Vec3 &v : mResult.mShape2Face) - v += movement2; - } - - // Update early out fraction - UpdateEarlyOutFraction(fraction_plus_slop); - } - } - } - } - - bool mValidateBodyPair; ///< If we still have to call the ValidateContactPoint for this body pair - bool mRejectAll; ///< Reject all further contacts between this body pair - - private: - const BodyManager & mBodyManager; - ContactConstraintManager & mContactConstraintManager; - CCDBody & mCCDBody; - ShapeCastResult & mResult; - float mDeltaTime; - BodyID mAcceptedBodyID; - }; - - // Narrowphase collector - ShapeCastResult cast_shape_result; - CCDNarrowPhaseCollector np_collector(mBodyManager, mContactManager, ccd_body, cast_shape_result, ioContext->mStepDeltaTime); - - // This collector wraps the narrowphase collector and collects the closest hit - class CCDBroadPhaseCollector : public CastShapeBodyCollector - { - public: - CCDBroadPhaseCollector(const CCDBody &inCCDBody, const Body &inBody1, const RShapeCast &inShapeCast, ShapeCastSettings &inShapeCastSettings, CCDNarrowPhaseCollector &ioCollector, const BodyManager &inBodyManager, PhysicsUpdateContext::Step *inStep, float inDeltaTime) : - mCCDBody(inCCDBody), - mBody1(inBody1), - mBody1Extent(inShapeCast.mShapeWorldBounds.GetExtent()), - mShapeCast(inShapeCast), - mShapeCastSettings(inShapeCastSettings), - mCollector(ioCollector), - mBodyManager(inBodyManager), - mStep(inStep), - mDeltaTime(inDeltaTime) - { - } - - virtual void AddHit(const BroadPhaseCastResult &inResult) override - { - JPH_PROFILE_FUNCTION(); - - JPH_ASSERT(inResult.mFraction <= GetEarlyOutFraction(), "This hit should not have been passed on to the collector"); - - // Test if we're colliding with ourselves - if (mBody1.GetID() == inResult.mBodyID) - return; - - // Avoid treating duplicates, if both bodies are doing CCD then only consider collision if body ID < other body ID - const Body &body2 = mBodyManager.GetBody(inResult.mBodyID); - const CCDBody *ccd_body2 = sGetCCDBody(body2, mStep); - if (ccd_body2 != nullptr && mCCDBody.mBodyID1 > ccd_body2->mBodyID1) - return; - - // Test group filter - if (!mBody1.GetCollisionGroup().CanCollide(body2.GetCollisionGroup())) - return; - - // TODO: For now we ignore sensors - if (body2.IsSensor()) - return; - - // Get relative movement of these two bodies - Vec3 direction = mShapeCast.mDirection - sCalculateBodyMotion(body2, mDeltaTime); - - // Test if the remaining movement is less than our movement threshold - if (direction.LengthSq() < mCCDBody.mLinearCastThresholdSq) - return; - - // Get the bounds of 2, widen it by the extent of 1 and test a ray to see if it hits earlier than the current early out fraction - AABox bounds = body2.GetWorldSpaceBounds(); - bounds.mMin -= mBody1Extent; - bounds.mMax += mBody1Extent; - float hit_fraction = RayAABox(Vec3(mShapeCast.mCenterOfMassStart.GetTranslation()), RayInvDirection(direction), bounds.mMin, bounds.mMax); - if (hit_fraction > GetPositiveEarlyOutFraction()) // If early out fraction <= 0, we have the possibility of finding a deeper hit so we need to clamp the early out fraction - return; - - // Reset collector (this is a new body pair) - mCollector.ResetEarlyOutFraction(GetEarlyOutFraction()); - mCollector.mValidateBodyPair = true; - mCollector.mRejectAll = false; - - // Provide direction as hint for the active edges algorithm - mShapeCastSettings.mActiveEdgeMovementDirection = direction; - - // Do narrow phase collision check - RShapeCast relative_cast(mShapeCast.mShape, mShapeCast.mScale, mShapeCast.mCenterOfMassStart, direction, mShapeCast.mShapeWorldBounds); - body2.GetTransformedShape().CastShape(relative_cast, mShapeCastSettings, mShapeCast.mCenterOfMassStart.GetTranslation(), mCollector); - - // Update early out fraction based on narrow phase collector - if (!mCollector.mRejectAll) - UpdateEarlyOutFraction(mCollector.GetEarlyOutFraction()); - } - - const CCDBody & mCCDBody; - const Body & mBody1; - Vec3 mBody1Extent; - RShapeCast mShapeCast; - ShapeCastSettings & mShapeCastSettings; - CCDNarrowPhaseCollector & mCollector; - const BodyManager & mBodyManager; - PhysicsUpdateContext::Step *mStep; - float mDeltaTime; - }; - - // Check if we collide with any other body. Note that we use the non-locking interface as we know the broadphase cannot be modified at this point. - RShapeCast shape_cast(body.GetShape(), Vec3::sReplicate(1.0f), body.GetCenterOfMassTransform(), ccd_body.mDeltaPosition); - CCDBroadPhaseCollector bp_collector(ccd_body, body, shape_cast, settings, np_collector, mBodyManager, ioStep, ioContext->mStepDeltaTime); - mBroadPhase->CastAABoxNoLock({ shape_cast.mShapeWorldBounds, shape_cast.mDirection }, bp_collector, broadphase_layer_filter, object_layer_filter); - - // Check if there was a hit - if (ccd_body.mFractionPlusSlop < 1.0f) - { - const Body &body2 = mBodyManager.GetBody(ccd_body.mBodyID2); - - // Determine contact manifold - ContactManifold manifold; - manifold.mBaseOffset = shape_cast.mCenterOfMassStart.GetTranslation(); - ManifoldBetweenTwoFaces(cast_shape_result.mContactPointOn1, cast_shape_result.mContactPointOn2, cast_shape_result.mPenetrationAxis, mPhysicsSettings.mManifoldToleranceSq, cast_shape_result.mShape1Face, cast_shape_result.mShape2Face, manifold.mRelativeContactPointsOn1, manifold.mRelativeContactPointsOn2 JPH_IF_DEBUG_RENDERER(, manifold.mBaseOffset)); - manifold.mSubShapeID1 = cast_shape_result.mSubShapeID1; - manifold.mSubShapeID2 = cast_shape_result.mSubShapeID2; - manifold.mPenetrationDepth = cast_shape_result.mPenetrationDepth; - manifold.mWorldSpaceNormal = ccd_body.mContactNormal; - - // Call contact point callbacks - mContactManager.OnCCDContactAdded(contact_allocator, body, body2, manifold, ccd_body.mContactSettings); - - if (ccd_body.mContactSettings.mIsSensor) - { - // If this is a sensor, we don't want to solve the contact - ccd_body.mFractionPlusSlop = 1.0f; - ccd_body.mBodyID2 = BodyID(); - } - else - { - // Calculate the average position from the manifold (this will result in the same impulse applied as when we apply impulses to all contact points) - if (manifold.mRelativeContactPointsOn2.size() > 1) - { - Vec3 average_contact_point = Vec3::sZero(); - for (const Vec3 &v : manifold.mRelativeContactPointsOn2) - average_contact_point += v; - average_contact_point /= (float)manifold.mRelativeContactPointsOn2.size(); - ccd_body.mContactPointOn2 = manifold.mBaseOffset + average_contact_point; - } - else - ccd_body.mContactPointOn2 = manifold.mBaseOffset + cast_shape_result.mContactPointOn2; - } - } - } - - // Collect information from the contact allocator and accumulate it in the step. - sFinalizeContactAllocator(*ioStep, contact_allocator); -} - -void PhysicsSystem::JobResolveCCDContacts(PhysicsUpdateContext *ioContext, PhysicsUpdateContext::Step *ioStep) -{ -#ifdef JPH_ENABLE_ASSERTS - // Read/write body access - BodyAccess::Grant grant(BodyAccess::EAccess::ReadWrite, BodyAccess::EAccess::ReadWrite); - - // We activate bodies that we collide with - BodyManager::GrantActiveBodiesAccess grant_active(true, false); -#endif - - uint32 num_active_bodies_after_find_collisions = ioStep->mActiveBodyReadIdx; - TempAllocator *temp_allocator = ioContext->mTempAllocator; - - // Check if there's anything to do - uint num_ccd_bodies = ioStep->mNumCCDBodies; - if (num_ccd_bodies > 0) - { - // Sort on fraction so that we process earliest collisions first - // This is needed to make the simulation deterministic and also to be able to stop contact processing - // between body pairs if an earlier hit was found involving the body by another CCD body - // (if it's body ID < this CCD body's body ID - see filtering logic in CCDBroadPhaseCollector) - CCDBody **sorted_ccd_bodies = (CCDBody **)temp_allocator->Allocate(num_ccd_bodies * sizeof(CCDBody *)); - { - JPH_PROFILE("Sort"); - - // We don't want to copy the entire struct (it's quite big), so we create a pointer array first - CCDBody *src_ccd_bodies = ioStep->mCCDBodies; - CCDBody **dst_ccd_bodies = sorted_ccd_bodies; - CCDBody **dst_ccd_bodies_end = dst_ccd_bodies + num_ccd_bodies; - while (dst_ccd_bodies < dst_ccd_bodies_end) - *(dst_ccd_bodies++) = src_ccd_bodies++; - - // Which we then sort - QuickSort(sorted_ccd_bodies, sorted_ccd_bodies + num_ccd_bodies, [](const CCDBody *inBody1, const CCDBody *inBody2) - { - if (inBody1->mFractionPlusSlop != inBody2->mFractionPlusSlop) - return inBody1->mFractionPlusSlop < inBody2->mFractionPlusSlop; - - return inBody1->mBodyID1 < inBody2->mBodyID1; - }); - } - - // We can collide with bodies that are not active, we track them here so we can activate them in one go at the end. - // This is also needed because we can't modify the active body array while we iterate it. - static constexpr int cBodiesBatch = 64; - BodyID *bodies_to_activate = (BodyID *)JPH_STACK_ALLOC(cBodiesBatch * sizeof(BodyID)); - int num_bodies_to_activate = 0; - - // We can move bodies that are not part of an island. In this case we need to notify the broadphase of the movement. - BodyID *bodies_to_update_bounds = (BodyID *)JPH_STACK_ALLOC(cBodiesBatch * sizeof(BodyID)); - int num_bodies_to_update_bounds = 0; - - for (uint i = 0; i < num_ccd_bodies; ++i) - { - const CCDBody *ccd_body = sorted_ccd_bodies[i]; - Body &body1 = mBodyManager.GetBody(ccd_body->mBodyID1); - MotionProperties *body_mp = body1.GetMotionProperties(); - - // If there was a hit - if (!ccd_body->mBodyID2.IsInvalid()) - { - Body &body2 = mBodyManager.GetBody(ccd_body->mBodyID2); - - // Determine if the other body has a CCD body - CCDBody *ccd_body2 = sGetCCDBody(body2, ioStep); - if (ccd_body2 != nullptr) - { - JPH_ASSERT(ccd_body2->mBodyID2 != ccd_body->mBodyID1, "If we collided with another body, that other body should have ignored collisions with us!"); - - // Check if the other body found a hit that is further away - if (ccd_body2->mFraction > ccd_body->mFraction) - { - // Reset the colliding body of the other CCD body. The other body will shorten its distance traveled and will not do any collision response (we'll do that). - // This means that at this point we have triggered a contact point add/persist for our further hit by accident for the other body. - // We accept this as calling the contact point callbacks here would require persisting the manifolds up to this point and doing the callbacks single threaded. - ccd_body2->mBodyID2 = BodyID(); - ccd_body2->mFractionPlusSlop = ccd_body->mFraction; - } - } - - // If the other body moved less than us before hitting something, we're not colliding with it so we again have triggered contact point add/persist callbacks by accident. - // We'll just move to the collision position anyway (as that's the last position we know is good), but we won't do any collision response. - if (ccd_body2 == nullptr || ccd_body2->mFraction >= ccd_body->mFraction) - { - const ContactSettings &contact_settings = ccd_body->mContactSettings; - - // Calculate contact point velocity for body 1 - Vec3 r1_plus_u = Vec3(ccd_body->mContactPointOn2 - (body1.GetCenterOfMassPosition() + ccd_body->mFraction * ccd_body->mDeltaPosition)); - Vec3 v1 = body1.GetPointVelocityCOM(r1_plus_u); - - // Calculate inverse mass for body 1 - float inv_m1 = contact_settings.mInvMassScale1 * body_mp->GetInverseMass(); - - if (body2.IsRigidBody()) - { - // Calculate contact point velocity for body 2 - Vec3 r2 = Vec3(ccd_body->mContactPointOn2 - body2.GetCenterOfMassPosition()); - Vec3 v2 = body2.GetPointVelocityCOM(r2); - - // Calculate relative contact velocity - Vec3 relative_velocity = v2 - v1; - float normal_velocity = relative_velocity.Dot(ccd_body->mContactNormal); - - // Calculate velocity bias due to restitution - float normal_velocity_bias; - if (contact_settings.mCombinedRestitution > 0.0f && normal_velocity < -mPhysicsSettings.mMinVelocityForRestitution) - normal_velocity_bias = contact_settings.mCombinedRestitution * normal_velocity; - else - normal_velocity_bias = 0.0f; - - // Get inverse mass of body 2 - float inv_m2 = body2.GetMotionPropertiesUnchecked() != nullptr? contact_settings.mInvMassScale2 * body2.GetMotionPropertiesUnchecked()->GetInverseMassUnchecked() : 0.0f; - - // Solve contact constraint - AxisConstraintPart contact_constraint; - contact_constraint.CalculateConstraintPropertiesWithMassOverride(body1, inv_m1, contact_settings.mInvInertiaScale1, r1_plus_u, body2, inv_m2, contact_settings.mInvInertiaScale2, r2, ccd_body->mContactNormal, normal_velocity_bias); - contact_constraint.SolveVelocityConstraintWithMassOverride(body1, inv_m1, body2, inv_m2, ccd_body->mContactNormal, -FLT_MAX, FLT_MAX); - - // Apply friction - if (contact_settings.mCombinedFriction > 0.0f) - { - // Calculate friction direction by removing normal velocity from the relative velocity - Vec3 friction_direction = relative_velocity - normal_velocity * ccd_body->mContactNormal; - float friction_direction_len_sq = friction_direction.LengthSq(); - if (friction_direction_len_sq > 1.0e-12f) - { - // Normalize friction direction - friction_direction /= sqrt(friction_direction_len_sq); - - // Calculate max friction impulse - float max_lambda_f = contact_settings.mCombinedFriction * contact_constraint.GetTotalLambda(); - - AxisConstraintPart friction; - friction.CalculateConstraintPropertiesWithMassOverride(body1, inv_m1, contact_settings.mInvInertiaScale1, r1_plus_u, body2, inv_m2, contact_settings.mInvInertiaScale2, r2, friction_direction); - friction.SolveVelocityConstraintWithMassOverride(body1, inv_m1, body2, inv_m2, friction_direction, -max_lambda_f, max_lambda_f); - } - } - - // Clamp velocity of body 2 - if (body2.IsDynamic()) - { - MotionProperties *body2_mp = body2.GetMotionProperties(); - body2_mp->ClampLinearVelocity(); - body2_mp->ClampAngularVelocity(); - } - } - else - { - SoftBodyMotionProperties *soft_mp = static_cast(body2.GetMotionProperties()); - const SoftBodyShape *soft_shape = static_cast(body2.GetShape()); - - // Convert the sub shape ID of the soft body to a face - uint32 face_idx = soft_shape->GetFaceIndex(ccd_body->mSubShapeID2); - const SoftBodyMotionProperties::Face &face = soft_mp->GetFace(face_idx); - - // Get vertices of the face - SoftBodyMotionProperties::Vertex &vtx0 = soft_mp->GetVertex(face.mVertex[0]); - SoftBodyMotionProperties::Vertex &vtx1 = soft_mp->GetVertex(face.mVertex[1]); - SoftBodyMotionProperties::Vertex &vtx2 = soft_mp->GetVertex(face.mVertex[2]); - - // Inverse mass of the face - float vtx0_mass = vtx0.mInvMass > 0.0f? 1.0f / vtx0.mInvMass : 1.0e10f; - float vtx1_mass = vtx1.mInvMass > 0.0f? 1.0f / vtx1.mInvMass : 1.0e10f; - float vtx2_mass = vtx2.mInvMass > 0.0f? 1.0f / vtx2.mInvMass : 1.0e10f; - float inv_m2 = 1.0f / (vtx0_mass + vtx1_mass + vtx2_mass); - - // Calculate barycentric coordinates of the contact point on the soft body's face - float u, v, w; - RMat44 inv_body2_transform = body2.GetInverseCenterOfMassTransform(); - Vec3 local_contact = Vec3(inv_body2_transform * ccd_body->mContactPointOn2); - ClosestPoint::GetBaryCentricCoordinates(vtx0.mPosition - local_contact, vtx1.mPosition - local_contact, vtx2.mPosition - local_contact, u, v, w); - - // Calculate contact point velocity for the face - Vec3 v2 = inv_body2_transform.Multiply3x3Transposed(u * vtx0.mVelocity + v * vtx1.mVelocity + w * vtx2.mVelocity); - float normal_velocity = (v2 - v1).Dot(ccd_body->mContactNormal); - - // Calculate velocity bias due to restitution - float normal_velocity_bias; - if (contact_settings.mCombinedRestitution > 0.0f && normal_velocity < -mPhysicsSettings.mMinVelocityForRestitution) - normal_velocity_bias = contact_settings.mCombinedRestitution * normal_velocity; - else - normal_velocity_bias = 0.0f; - - // Calculate resulting velocity change (the math here is similar to AxisConstraintPart but without an inertia term for body 2 as we treat it as a point mass) - Vec3 r1_plus_u_x_n = r1_plus_u.Cross(ccd_body->mContactNormal); - Vec3 invi1_r1_plus_u_x_n = contact_settings.mInvInertiaScale1 * body1.GetInverseInertia().Multiply3x3(r1_plus_u_x_n); - float jv = r1_plus_u_x_n.Dot(body_mp->GetAngularVelocity()) - normal_velocity - normal_velocity_bias; - float inv_effective_mass = inv_m1 + inv_m2 + invi1_r1_plus_u_x_n.Dot(r1_plus_u_x_n); - float lambda = jv / inv_effective_mass; - body_mp->SubLinearVelocityStep((lambda * inv_m1) * ccd_body->mContactNormal); - body_mp->SubAngularVelocityStep(lambda * invi1_r1_plus_u_x_n); - Vec3 delta_v2 = inv_body2_transform.Multiply3x3(lambda * ccd_body->mContactNormal); - vtx0.mVelocity += delta_v2 * vtx0.mInvMass; - vtx1.mVelocity += delta_v2 * vtx1.mInvMass; - vtx2.mVelocity += delta_v2 * vtx2.mInvMass; - } - - // Clamp velocity of body 1 - body_mp->ClampLinearVelocity(); - body_mp->ClampAngularVelocity(); - - // Activate the 2nd body if it is not already active - if (body2.IsDynamic() && !body2.IsActive()) - { - bodies_to_activate[num_bodies_to_activate++] = ccd_body->mBodyID2; - if (num_bodies_to_activate == cBodiesBatch) - { - // Batch is full, activate now - mBodyManager.ActivateBodies(bodies_to_activate, num_bodies_to_activate); - num_bodies_to_activate = 0; - } - } - - #ifdef JPH_DEBUG_RENDERER - if (sDrawMotionQualityLinearCast) - { - // Draw the collision location - RMat44 collision_transform = body1.GetCenterOfMassTransform().PostTranslated(ccd_body->mFraction * ccd_body->mDeltaPosition); - body1.GetShape()->Draw(DebugRenderer::sInstance, collision_transform, Vec3::sReplicate(1.0f), Color::sYellow, false, true); - - // Draw the collision location + slop - RMat44 collision_transform_plus_slop = body1.GetCenterOfMassTransform().PostTranslated(ccd_body->mFractionPlusSlop * ccd_body->mDeltaPosition); - body1.GetShape()->Draw(DebugRenderer::sInstance, collision_transform_plus_slop, Vec3::sReplicate(1.0f), Color::sOrange, false, true); - - // Draw contact normal - DebugRenderer::sInstance->DrawArrow(ccd_body->mContactPointOn2, ccd_body->mContactPointOn2 - ccd_body->mContactNormal, Color::sYellow, 0.1f); - - // Draw post contact velocity - DebugRenderer::sInstance->DrawArrow(collision_transform.GetTranslation(), collision_transform.GetTranslation() + body1.GetLinearVelocity(), Color::sOrange, 0.1f); - DebugRenderer::sInstance->DrawArrow(collision_transform.GetTranslation(), collision_transform.GetTranslation() + body1.GetAngularVelocity(), Color::sPurple, 0.1f); - } - #endif // JPH_DEBUG_RENDERER - } - } - - // Update body position - body1.AddPositionStep(ccd_body->mDeltaPosition * ccd_body->mFractionPlusSlop); - - // If the body was activated due to an earlier CCD step it will have an index in the active - // body list that it higher than the highest one we processed during FindCollisions - // which means it hasn't been assigned an island and will not be updated by an island - // this means that we need to update its bounds manually - if (body_mp->GetIndexInActiveBodiesInternal() >= num_active_bodies_after_find_collisions) - { - body1.CalculateWorldSpaceBoundsInternal(); - bodies_to_update_bounds[num_bodies_to_update_bounds++] = body1.GetID(); - if (num_bodies_to_update_bounds == cBodiesBatch) - { - // Buffer full, flush now - mBroadPhase->NotifyBodiesAABBChanged(bodies_to_update_bounds, num_bodies_to_update_bounds, false); - num_bodies_to_update_bounds = 0; - } - } - } - - // Activate the requested bodies - if (num_bodies_to_activate > 0) - mBodyManager.ActivateBodies(bodies_to_activate, num_bodies_to_activate); - - // Notify change bounds on requested bodies - if (num_bodies_to_update_bounds > 0) - mBroadPhase->NotifyBodiesAABBChanged(bodies_to_update_bounds, num_bodies_to_update_bounds, false); - - // Free the sorted ccd bodies - temp_allocator->Free(sorted_ccd_bodies, num_ccd_bodies * sizeof(CCDBody *)); - } - - // Ensure we free the CCD bodies array now, will not call the destructor! - temp_allocator->Free(ioStep->mActiveBodyToCCDBody, ioStep->mNumActiveBodyToCCDBody * sizeof(int)); - ioStep->mActiveBodyToCCDBody = nullptr; - ioStep->mNumActiveBodyToCCDBody = 0; - temp_allocator->Free(ioStep->mCCDBodies, ioStep->mCCDBodiesCapacity * sizeof(CCDBody)); - ioStep->mCCDBodies = nullptr; - ioStep->mCCDBodiesCapacity = 0; -} - -void PhysicsSystem::JobContactRemovedCallbacks(const PhysicsUpdateContext::Step *ioStep) -{ -#ifdef JPH_ENABLE_ASSERTS - // We don't touch any bodies - BodyAccess::Grant grant(BodyAccess::EAccess::None, BodyAccess::EAccess::None); -#endif - - // Reset the Body::EFlags::InvalidateContactCache flag for all bodies - mBodyManager.ValidateContactCacheForAllBodies(); - - // Finalize the contact cache (this swaps the read and write versions of the contact cache) - // Trigger all contact removed callbacks by looking at last step contact points that have not been flagged as reused - mContactManager.FinalizeContactCacheAndCallContactPointRemovedCallbacks(ioStep->mNumBodyPairs, ioStep->mNumManifolds); -} - -class PhysicsSystem::BodiesToSleep : public NonCopyable -{ -public: - static constexpr int cBodiesToSleepSize = 512; - static constexpr int cMaxBodiesToPutInBuffer = 128; - - inline BodiesToSleep(BodyManager &inBodyManager, BodyID *inBodiesToSleepBuffer) : mBodyManager(inBodyManager), mBodiesToSleepBuffer(inBodiesToSleepBuffer), mBodiesToSleepCur(inBodiesToSleepBuffer) { } - - inline ~BodiesToSleep() - { - // Flush the bodies to sleep buffer - int num_bodies_in_buffer = int(mBodiesToSleepCur - mBodiesToSleepBuffer); - if (num_bodies_in_buffer > 0) - mBodyManager.DeactivateBodies(mBodiesToSleepBuffer, num_bodies_in_buffer); - } - - inline void PutToSleep(const BodyID *inBegin, const BodyID *inEnd) - { - int num_bodies_to_sleep = int(inEnd - inBegin); - if (num_bodies_to_sleep > cMaxBodiesToPutInBuffer) - { - // Too many bodies, deactivate immediately - mBodyManager.DeactivateBodies(inBegin, num_bodies_to_sleep); - } - else - { - // Check if there's enough space in the bodies to sleep buffer - int num_bodies_in_buffer = int(mBodiesToSleepCur - mBodiesToSleepBuffer); - if (num_bodies_in_buffer + num_bodies_to_sleep > cBodiesToSleepSize) - { - // Flush the bodies to sleep buffer - mBodyManager.DeactivateBodies(mBodiesToSleepBuffer, num_bodies_in_buffer); - mBodiesToSleepCur = mBodiesToSleepBuffer; - } - - // Copy the bodies in the buffer - memcpy(mBodiesToSleepCur, inBegin, num_bodies_to_sleep * sizeof(BodyID)); - mBodiesToSleepCur += num_bodies_to_sleep; - } - } - -private: - BodyManager & mBodyManager; - BodyID * mBodiesToSleepBuffer; - BodyID * mBodiesToSleepCur; -}; - -void PhysicsSystem::CheckSleepAndUpdateBounds(uint32 inIslandIndex, const PhysicsUpdateContext *ioContext, const PhysicsUpdateContext::Step *ioStep, BodiesToSleep &ioBodiesToSleep) -{ - // Get the bodies that belong to this island - BodyID *bodies_begin, *bodies_end; - mIslandBuilder.GetBodiesInIsland(inIslandIndex, bodies_begin, bodies_end); - - // Only check sleeping in the last step - // Also resets force and torque used during the apply gravity phase - if (ioStep->mIsLast) - { - JPH_PROFILE("Check Sleeping"); - - static_assert(int(ECanSleep::CannotSleep) == 0 && int(ECanSleep::CanSleep) == 1, "Loop below makes this assumption"); - int all_can_sleep = mPhysicsSettings.mAllowSleeping? int(ECanSleep::CanSleep) : int(ECanSleep::CannotSleep); - - float time_before_sleep = mPhysicsSettings.mTimeBeforeSleep; - float max_movement = mPhysicsSettings.mPointVelocitySleepThreshold * time_before_sleep; - - for (const BodyID *body_id = bodies_begin; body_id < bodies_end; ++body_id) - { - Body &body = mBodyManager.GetBody(*body_id); - - // Update bounding box - body.CalculateWorldSpaceBoundsInternal(); - - // Update sleeping - all_can_sleep &= int(body.UpdateSleepStateInternal(ioContext->mStepDeltaTime, max_movement, time_before_sleep)); - - // Reset force and torque - MotionProperties *mp = body.GetMotionProperties(); - mp->ResetForce(); - mp->ResetTorque(); - } - - // If all bodies indicate they can sleep we can deactivate them - if (all_can_sleep == int(ECanSleep::CanSleep)) - ioBodiesToSleep.PutToSleep(bodies_begin, bodies_end); - } - else - { - JPH_PROFILE("Update Bounds"); - - // Update bounding box only for all other steps - for (const BodyID *body_id = bodies_begin; body_id < bodies_end; ++body_id) - { - Body &body = mBodyManager.GetBody(*body_id); - body.CalculateWorldSpaceBoundsInternal(); - } - } - - // Notify broadphase of changed objects (find ccd contacts can do linear casts in the next step, so we need to do this every step) - // Note: Shuffles the BodyID's around!!! - mBroadPhase->NotifyBodiesAABBChanged(bodies_begin, int(bodies_end - bodies_begin), false); -} - -void PhysicsSystem::JobSolvePositionConstraints(PhysicsUpdateContext *ioContext, PhysicsUpdateContext::Step *ioStep) -{ -#ifdef JPH_ENABLE_ASSERTS - // We fix up position errors - BodyAccess::Grant grant(BodyAccess::EAccess::None, BodyAccess::EAccess::ReadWrite); - - // Can only deactivate bodies - BodyManager::GrantActiveBodiesAccess grant_active(false, true); -#endif - - float delta_time = ioContext->mStepDeltaTime; - float baumgarte = mPhysicsSettings.mBaumgarte; - Constraint **active_constraints = ioContext->mActiveConstraints; - - // Keep a buffer of bodies that need to go to sleep in order to not constantly lock the active bodies mutex and create contention between all solving threads - BodiesToSleep bodies_to_sleep(mBodyManager, (BodyID *)JPH_STACK_ALLOC(BodiesToSleep::cBodiesToSleepSize * sizeof(BodyID))); - - bool check_islands = true, check_split_islands = mPhysicsSettings.mUseLargeIslandSplitter; - do - { - // First try to get work from large islands - if (check_split_islands) - { - bool first_iteration; - uint split_island_index; - uint32 *constraints_begin, *constraints_end, *contacts_begin, *contacts_end; - switch (mLargeIslandSplitter.FetchNextBatch(split_island_index, constraints_begin, constraints_end, contacts_begin, contacts_end, first_iteration)) - { - case LargeIslandSplitter::EStatus::BatchRetrieved: - // Solve the batch - ConstraintManager::sSolvePositionConstraints(active_constraints, constraints_begin, constraints_end, delta_time, baumgarte); - mContactManager.SolvePositionConstraints(contacts_begin, contacts_end); - - // Mark the batch as processed - bool last_iteration, final_batch; - mLargeIslandSplitter.MarkBatchProcessed(split_island_index, constraints_begin, constraints_end, contacts_begin, contacts_end, last_iteration, final_batch); - - // The final batch will update all bounds and check sleeping - if (final_batch) - CheckSleepAndUpdateBounds(mLargeIslandSplitter.GetIslandIndex(split_island_index), ioContext, ioStep, bodies_to_sleep); - - // We processed work, loop again - continue; - case LargeIslandSplitter::EStatus::WaitingForBatch: - break; - case LargeIslandSplitter::EStatus::AllBatchesDone: - check_split_islands = false; - break; - } - } - - // If that didn't succeed try to process an island - if (check_islands) - { - // Next island - uint32 island_idx = ioStep->mSolvePositionConstraintsNextIsland++; - if (island_idx >= mIslandBuilder.GetNumIslands()) - { - // We processed all islands, stop checking islands - check_islands = false; - continue; - } - - JPH_PROFILE("Island"); - - // Get iterators for this island - uint32 *constraints_begin, *constraints_end, *contacts_begin, *contacts_end; - mIslandBuilder.GetConstraintsInIsland(island_idx, constraints_begin, constraints_end); - mIslandBuilder.GetContactsInIsland(island_idx, contacts_begin, contacts_end); - - // If this island is a large island, it will be picked up as a batch and we don't need to do anything here - uint num_items = uint(constraints_end - constraints_begin) + uint(contacts_end - contacts_begin); - if (mPhysicsSettings.mUseLargeIslandSplitter - && num_items >= LargeIslandSplitter::cLargeIslandTreshold) - continue; - - // Check if this island needs solving - if (num_items > 0) - { - // Iterate - uint num_position_steps = mIslandBuilder.GetNumPositionSteps(island_idx); - for (uint position_step = 0; position_step < num_position_steps; ++position_step) - { - bool applied_impulse = ConstraintManager::sSolvePositionConstraints(active_constraints, constraints_begin, constraints_end, delta_time, baumgarte); - applied_impulse |= mContactManager.SolvePositionConstraints(contacts_begin, contacts_end); - if (!applied_impulse) - break; - } - } - - // After solving we will update all bounds and check sleeping - CheckSleepAndUpdateBounds(island_idx, ioContext, ioStep, bodies_to_sleep); - - // We processed work, loop again - continue; - } - - // If we didn't find any work, give up a time slice - std::this_thread::yield(); - } - while (check_islands || check_split_islands); -} - -void PhysicsSystem::JobSoftBodyPrepare(PhysicsUpdateContext *ioContext, PhysicsUpdateContext::Step *ioStep) -{ - JPH_PROFILE_FUNCTION(); - - { - #ifdef JPH_ENABLE_ASSERTS - // Reading soft body positions - BodyAccess::Grant grant(BodyAccess::EAccess::None, BodyAccess::EAccess::Read); - #endif - - // Get the active soft bodies - BodyIDVector active_bodies; - mBodyManager.GetActiveBodies(EBodyType::SoftBody, active_bodies); - - // Quit if there are no active soft bodies - if (active_bodies.empty()) - { - // Kick the next step - if (ioStep->mStartNextStep.IsValid()) - ioStep->mStartNextStep.RemoveDependency(); - return; - } - - // Sort to get a deterministic update order - QuickSort(active_bodies.begin(), active_bodies.end()); - - // Allocate soft body contexts - ioContext->mNumSoftBodies = (uint)active_bodies.size(); - ioContext->mSoftBodyUpdateContexts = (SoftBodyUpdateContext *)ioContext->mTempAllocator->Allocate(ioContext->mNumSoftBodies * sizeof(SoftBodyUpdateContext)); - - // Initialize soft body contexts - for (SoftBodyUpdateContext *sb_ctx = ioContext->mSoftBodyUpdateContexts, *sb_ctx_end = ioContext->mSoftBodyUpdateContexts + ioContext->mNumSoftBodies; sb_ctx < sb_ctx_end; ++sb_ctx) - { - new (sb_ctx) SoftBodyUpdateContext; - Body &body = mBodyManager.GetBody(active_bodies[sb_ctx - ioContext->mSoftBodyUpdateContexts]); - SoftBodyMotionProperties *mp = static_cast(body.GetMotionProperties()); - mp->InitializeUpdateContext(ioContext->mStepDeltaTime, body, *this, *sb_ctx); - } - } - - // We're ready to collide the first soft body - ioContext->mSoftBodyToCollide.store(0, memory_order_release); - - // Determine number of jobs to spawn - int num_soft_body_jobs = ioContext->GetMaxConcurrency(); - - // Create finalize job - ioStep->mSoftBodyFinalize = ioContext->mJobSystem->CreateJob("SoftBodyFinalize", cColorSoftBodyFinalize, [ioContext, ioStep]() - { - ioContext->mPhysicsSystem->JobSoftBodyFinalize(ioContext); - - // Kick the next step - if (ioStep->mStartNextStep.IsValid()) - ioStep->mStartNextStep.RemoveDependency(); - }, num_soft_body_jobs); // depends on: soft body simulate - ioContext->mBarrier->AddJob(ioStep->mSoftBodyFinalize); - - // Create simulate jobs - ioStep->mSoftBodySimulate.resize(num_soft_body_jobs); - for (int i = 0; i < num_soft_body_jobs; ++i) - ioStep->mSoftBodySimulate[i] = ioContext->mJobSystem->CreateJob("SoftBodySimulate", cColorSoftBodySimulate, [ioStep, i]() - { - ioStep->mContext->mPhysicsSystem->JobSoftBodySimulate(ioStep->mContext, i); - - ioStep->mSoftBodyFinalize.RemoveDependency(); - }, num_soft_body_jobs); // depends on: soft body collide - ioContext->mBarrier->AddJobs(ioStep->mSoftBodySimulate.data(), ioStep->mSoftBodySimulate.size()); - - // Create collision jobs - ioStep->mSoftBodyCollide.resize(num_soft_body_jobs); - for (int i = 0; i < num_soft_body_jobs; ++i) - ioStep->mSoftBodyCollide[i] = ioContext->mJobSystem->CreateJob("SoftBodyCollide", cColorSoftBodyCollide, [ioContext, ioStep]() - { - ioContext->mPhysicsSystem->JobSoftBodyCollide(ioContext); - - for (const JobHandle &h : ioStep->mSoftBodySimulate) - h.RemoveDependency(); - }); // depends on: nothing - ioContext->mBarrier->AddJobs(ioStep->mSoftBodyCollide.data(), ioStep->mSoftBodyCollide.size()); -} - -void PhysicsSystem::JobSoftBodyCollide(PhysicsUpdateContext *ioContext) const -{ -#ifdef JPH_ENABLE_ASSERTS - // Reading rigid body positions and velocities - BodyAccess::Grant grant(BodyAccess::EAccess::Read, BodyAccess::EAccess::Read); -#endif - - for (;;) - { - // Fetch the next soft body - uint sb_idx = ioContext->mSoftBodyToCollide.fetch_add(1, std::memory_order_acquire); - if (sb_idx >= ioContext->mNumSoftBodies) - break; - - // Do a broadphase check - SoftBodyUpdateContext &sb_ctx = ioContext->mSoftBodyUpdateContexts[sb_idx]; - sb_ctx.mMotionProperties->DetermineCollidingShapes(sb_ctx, *this, GetBodyLockInterfaceNoLock()); - } -} - -void PhysicsSystem::JobSoftBodySimulate(PhysicsUpdateContext *ioContext, uint inThreadIndex) const -{ -#ifdef JPH_ENABLE_ASSERTS - // Updating velocities of soft bodies, allow the contact listener to read the soft body state - BodyAccess::Grant grant(BodyAccess::EAccess::ReadWrite, BodyAccess::EAccess::Read); -#endif - - // Calculate at which body we start to distribute the workload across the threads - uint num_soft_bodies = ioContext->mNumSoftBodies; - uint start_idx = inThreadIndex * num_soft_bodies / ioContext->GetMaxConcurrency(); - - // Keep running partial updates until everything has been updated - uint status; - do - { - // Reset status - status = 0; - - // Update all soft bodies - for (uint i = 0; i < num_soft_bodies; ++i) - { - // Fetch the soft body context - SoftBodyUpdateContext &sb_ctx = ioContext->mSoftBodyUpdateContexts[(start_idx + i) % num_soft_bodies]; - - // To avoid trashing the cache too much, we prefer to stick to one soft body until we cannot progress it any further - uint sb_status; - do - { - sb_status = (uint)sb_ctx.mMotionProperties->ParallelUpdate(sb_ctx, mPhysicsSettings); - status |= sb_status; - } while (sb_status == (uint)SoftBodyMotionProperties::EStatus::DidWork); - } - - // If we didn't perform any work, yield the thread so that something else can run - if (!(status & (uint)SoftBodyMotionProperties::EStatus::DidWork)) - std::this_thread::yield(); - } - while (status != (uint)SoftBodyMotionProperties::EStatus::Done); -} - -void PhysicsSystem::JobSoftBodyFinalize(PhysicsUpdateContext *ioContext) -{ -#ifdef JPH_ENABLE_ASSERTS - // Updating rigid body velocities and soft body positions / velocities - BodyAccess::Grant grant(BodyAccess::EAccess::ReadWrite, BodyAccess::EAccess::ReadWrite); - - // Can activate and deactivate bodies - BodyManager::GrantActiveBodiesAccess grant_active(true, true); -#endif - - static constexpr int cBodiesBatch = 64; - BodyID *bodies_to_update_bounds = (BodyID *)JPH_STACK_ALLOC(cBodiesBatch * sizeof(BodyID)); - int num_bodies_to_update_bounds = 0; - BodyID *bodies_to_put_to_sleep = (BodyID *)JPH_STACK_ALLOC(cBodiesBatch * sizeof(BodyID)); - int num_bodies_to_put_to_sleep = 0; - - for (SoftBodyUpdateContext *sb_ctx = ioContext->mSoftBodyUpdateContexts, *sb_ctx_end = ioContext->mSoftBodyUpdateContexts + ioContext->mNumSoftBodies; sb_ctx < sb_ctx_end; ++sb_ctx) - { - // Apply the rigid body velocity deltas - sb_ctx->mMotionProperties->UpdateRigidBodyVelocities(*sb_ctx, GetBodyInterfaceNoLock()); - - // Update the position - sb_ctx->mBody->SetPositionAndRotationInternal(sb_ctx->mBody->GetPosition() + sb_ctx->mDeltaPosition, sb_ctx->mBody->GetRotation(), false); - - BodyID id = sb_ctx->mBody->GetID(); - bodies_to_update_bounds[num_bodies_to_update_bounds++] = id; - if (num_bodies_to_update_bounds == cBodiesBatch) - { - // Buffer full, flush now - mBroadPhase->NotifyBodiesAABBChanged(bodies_to_update_bounds, num_bodies_to_update_bounds, false); - num_bodies_to_update_bounds = 0; - } - - if (sb_ctx->mCanSleep == ECanSleep::CanSleep) - { - // This body should go to sleep - bodies_to_put_to_sleep[num_bodies_to_put_to_sleep++] = id; - if (num_bodies_to_put_to_sleep == cBodiesBatch) - { - mBodyManager.DeactivateBodies(bodies_to_put_to_sleep, num_bodies_to_put_to_sleep); - num_bodies_to_put_to_sleep = 0; - } - } - } - - // Notify change bounds on requested bodies - if (num_bodies_to_update_bounds > 0) - mBroadPhase->NotifyBodiesAABBChanged(bodies_to_update_bounds, num_bodies_to_update_bounds, false); - - // Notify bodies to go to sleep - if (num_bodies_to_put_to_sleep > 0) - mBodyManager.DeactivateBodies(bodies_to_put_to_sleep, num_bodies_to_put_to_sleep); - - // Free soft body contexts - ioContext->mTempAllocator->Free(ioContext->mSoftBodyUpdateContexts, ioContext->mNumSoftBodies * sizeof(SoftBodyUpdateContext)); -} - -void PhysicsSystem::SaveState(StateRecorder &inStream, EStateRecorderState inState, const StateRecorderFilter *inFilter) const -{ - JPH_PROFILE_FUNCTION(); - - inStream.Write(inState); - - if (uint8(inState) & uint8(EStateRecorderState::Global)) - { - inStream.Write(mPreviousStepDeltaTime); - inStream.Write(mGravity); - } - - if (uint8(inState) & uint8(EStateRecorderState::Bodies)) - mBodyManager.SaveState(inStream, inFilter); - - if (uint8(inState) & uint8(EStateRecorderState::Contacts)) - mContactManager.SaveState(inStream, inFilter); - - if (uint8(inState) & uint8(EStateRecorderState::Constraints)) - mConstraintManager.SaveState(inStream, inFilter); -} - -bool PhysicsSystem::RestoreState(StateRecorder &inStream) -{ - JPH_PROFILE_FUNCTION(); - - EStateRecorderState state = EStateRecorderState::All; // Set this value for validation. If a partial state is saved, validation will not work anyway. - inStream.Read(state); - - if (uint8(state) & uint8(EStateRecorderState::Global)) - { - inStream.Read(mPreviousStepDeltaTime); - inStream.Read(mGravity); - } - - if (uint8(state) & uint8(EStateRecorderState::Bodies)) - { - if (!mBodyManager.RestoreState(inStream)) - return false; - - // Update bounding boxes for all bodies in the broadphase - Array bodies; - for (const Body *b : mBodyManager.GetBodies()) - if (BodyManager::sIsValidBodyPointer(b) && b->IsInBroadPhase()) - bodies.push_back(b->GetID()); - if (!bodies.empty()) - mBroadPhase->NotifyBodiesAABBChanged(&bodies[0], (int)bodies.size()); - } - - if (uint8(state) & uint8(EStateRecorderState::Contacts)) - { - if (!mContactManager.RestoreState(inStream)) - return false; - } - - if (uint8(state) & uint8(EStateRecorderState::Constraints)) - { - if (!mConstraintManager.RestoreState(inStream)) - return false; - } - - return true; -} - -void PhysicsSystem::SaveBodyState(const Body &inBody, StateRecorder &inStream) const -{ - mBodyManager.SaveBodyState(inBody, inStream); -} - -void PhysicsSystem::RestoreBodyState(Body &ioBody, StateRecorder &inStream) -{ - mBodyManager.RestoreBodyState(ioBody, inStream); - - BodyID id = ioBody.GetID(); - mBroadPhase->NotifyBodiesAABBChanged(&id, 1); -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsSystem.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsSystem.h deleted file mode 100644 index 3234e5b53f7..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsSystem.h +++ /dev/null @@ -1,320 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -class JobSystem; -class StateRecorder; -class TempAllocator; -class PhysicsStepListener; -class SoftBodyContactListener; - -/// The main class for the physics system. It contains all rigid bodies and simulates them. -/// -/// The main simulation is performed by the Update() call on multiple threads (if the JobSystem is configured to use them). Please refer to the general architecture overview in the Docs folder for more information. -class JPH_EXPORT PhysicsSystem : public NonCopyable -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Constructor / Destructor - PhysicsSystem() : mContactManager(mPhysicsSettings) JPH_IF_ENABLE_ASSERTS(, mConstraintManager(&mBodyManager)) { } - ~PhysicsSystem(); - - /// Initialize the system. - /// @param inMaxBodies Maximum number of bodies to support. - /// @param inNumBodyMutexes Number of body mutexes to use. Should be a power of 2 in the range [1, 64], use 0 to auto detect. - /// @param inMaxBodyPairs Maximum amount of body pairs to process (anything else will fall through the world), this number should generally be much higher than the max amount of contact points as there will be lots of bodies close that are not actually touching. - /// @param inMaxContactConstraints Maximum amount of contact constraints to process (anything else will fall through the world). - /// @param inBroadPhaseLayerInterface Information on the mapping of object layers to broad phase layers. Since this is a virtual interface, the instance needs to stay alive during the lifetime of the PhysicsSystem. - /// @param inObjectVsBroadPhaseLayerFilter Filter callback function that is used to determine if an object layer collides with a broad phase layer. Since this is a virtual interface, the instance needs to stay alive during the lifetime of the PhysicsSystem. - /// @param inObjectLayerPairFilter Filter callback function that is used to determine if two object layers collide. Since this is a virtual interface, the instance needs to stay alive during the lifetime of the PhysicsSystem. - void Init(uint inMaxBodies, uint inNumBodyMutexes, uint inMaxBodyPairs, uint inMaxContactConstraints, const BroadPhaseLayerInterface &inBroadPhaseLayerInterface, const ObjectVsBroadPhaseLayerFilter &inObjectVsBroadPhaseLayerFilter, const ObjectLayerPairFilter &inObjectLayerPairFilter); - - /// Listener that is notified whenever a body is activated/deactivated - void SetBodyActivationListener(BodyActivationListener *inListener) { mBodyManager.SetBodyActivationListener(inListener); } - BodyActivationListener * GetBodyActivationListener() const { return mBodyManager.GetBodyActivationListener(); } - - /// Listener that is notified whenever a contact point between two bodies is added/updated/removed - void SetContactListener(ContactListener *inListener) { mContactManager.SetContactListener(inListener); } - ContactListener * GetContactListener() const { return mContactManager.GetContactListener(); } - - /// Listener that is notified whenever a contact point between a soft body and another body - void SetSoftBodyContactListener(SoftBodyContactListener *inListener) { mSoftBodyContactListener = inListener; } - SoftBodyContactListener * GetSoftBodyContactListener() const { return mSoftBodyContactListener; } - - /// Set the function that combines the friction of two bodies and returns it - /// Default method is the geometric mean: sqrt(friction1 * friction2). - void SetCombineFriction(ContactConstraintManager::CombineFunction inCombineFriction) { mContactManager.SetCombineFriction(inCombineFriction); } - ContactConstraintManager::CombineFunction GetCombineFriction() const { return mContactManager.GetCombineFriction(); } - - /// Set the function that combines the restitution of two bodies and returns it - /// Default method is max(restitution1, restitution1) - void SetCombineRestitution(ContactConstraintManager::CombineFunction inCombineRestition) { mContactManager.SetCombineRestitution(inCombineRestition); } - ContactConstraintManager::CombineFunction GetCombineRestitution() const { return mContactManager.GetCombineRestitution(); } - - /// Control the main constants of the physics simulation - void SetPhysicsSettings(const PhysicsSettings &inSettings) { mPhysicsSettings = inSettings; } - const PhysicsSettings & GetPhysicsSettings() const { return mPhysicsSettings; } - - /// Access to the body interface. This interface allows to to create / remove bodies and to change their properties. - const BodyInterface & GetBodyInterface() const { return mBodyInterfaceLocking; } - BodyInterface & GetBodyInterface() { return mBodyInterfaceLocking; } - const BodyInterface & GetBodyInterfaceNoLock() const { return mBodyInterfaceNoLock; } ///< Version that does not lock the bodies, use with great care! - BodyInterface & GetBodyInterfaceNoLock() { return mBodyInterfaceNoLock; } ///< Version that does not lock the bodies, use with great care! - - /// Access to the broadphase interface that allows coarse collision queries - const BroadPhaseQuery & GetBroadPhaseQuery() const { return *mBroadPhase; } - - /// Interface that allows fine collision queries against first the broad phase and then the narrow phase. - const NarrowPhaseQuery & GetNarrowPhaseQuery() const { return mNarrowPhaseQueryLocking; } - const NarrowPhaseQuery & GetNarrowPhaseQueryNoLock() const { return mNarrowPhaseQueryNoLock; } ///< Version that does not lock the bodies, use with great care! - - /// Add constraint to the world - void AddConstraint(Constraint *inConstraint) { mConstraintManager.Add(&inConstraint, 1); } - - /// Remove constraint from the world - void RemoveConstraint(Constraint *inConstraint) { mConstraintManager.Remove(&inConstraint, 1); } - - /// Batch add constraints. Note that the inConstraints array is allowed to have nullptrs, these will be ignored. - void AddConstraints(Constraint **inConstraints, int inNumber) { mConstraintManager.Add(inConstraints, inNumber); } - - /// Batch remove constraints. Note that the inConstraints array is allowed to have nullptrs, these will be ignored. - void RemoveConstraints(Constraint **inConstraints, int inNumber) { mConstraintManager.Remove(inConstraints, inNumber); } - - /// Get a list of all constraints - Constraints GetConstraints() const { return mConstraintManager.GetConstraints(); } - - /// Optimize the broadphase, needed only if you've added many bodies prior to calling Update() for the first time. - void OptimizeBroadPhase(); - - /// Adds a new step listener - void AddStepListener(PhysicsStepListener *inListener); - - /// Removes a step listener - void RemoveStepListener(PhysicsStepListener *inListener); - - /// Simulate the system. - /// The world steps for a total of inDeltaTime seconds. This is divided in inCollisionSteps iterations. - /// Each iteration consists of collision detection followed by an integration step. - /// This function internally spawns jobs using inJobSystem and waits for them to complete, so no jobs will be running when this function returns. - EPhysicsUpdateError Update(float inDeltaTime, int inCollisionSteps, TempAllocator *inTempAllocator, JobSystem *inJobSystem); - - /// Saving state for replay - void SaveState(StateRecorder &inStream, EStateRecorderState inState = EStateRecorderState::All, const StateRecorderFilter *inFilter = nullptr) const; - - /// Restoring state for replay. Returns false if failed. - bool RestoreState(StateRecorder &inStream); - - /// Saving state of a single body. - void SaveBodyState(const Body &inBody, StateRecorder &inStream) const; - - /// Restoring state of a single body. - void RestoreBodyState(Body &ioBody, StateRecorder &inStream); - -#ifdef JPH_DEBUG_RENDERER - // Drawing properties - static bool sDrawMotionQualityLinearCast; ///< Draw debug info for objects that perform continuous collision detection through the linear cast motion quality - - /// Draw the state of the bodies (debugging purposes) - void DrawBodies(const BodyManager::DrawSettings &inSettings, DebugRenderer *inRenderer, const BodyDrawFilter *inBodyFilter = nullptr) { mBodyManager.Draw(inSettings, mPhysicsSettings, inRenderer, inBodyFilter); } - - /// Draw the constraints only (debugging purposes) - void DrawConstraints(DebugRenderer *inRenderer) { mConstraintManager.DrawConstraints(inRenderer); } - - /// Draw the constraint limits only (debugging purposes) - void DrawConstraintLimits(DebugRenderer *inRenderer) { mConstraintManager.DrawConstraintLimits(inRenderer); } - - /// Draw the constraint reference frames only (debugging purposes) - void DrawConstraintReferenceFrame(DebugRenderer *inRenderer) { mConstraintManager.DrawConstraintReferenceFrame(inRenderer); } -#endif // JPH_DEBUG_RENDERER - - /// Set gravity value - void SetGravity(Vec3Arg inGravity) { mGravity = inGravity; } - Vec3 GetGravity() const { return mGravity; } - - /// Returns a locking interface that won't actually lock the body. Use with great care! - inline const BodyLockInterfaceNoLock & GetBodyLockInterfaceNoLock() const { return mBodyLockInterfaceNoLock; } - - /// Returns a locking interface that locks the body so other threads cannot modify it. - inline const BodyLockInterfaceLocking & GetBodyLockInterface() const { return mBodyLockInterfaceLocking; } - - /// Get an broadphase layer filter that uses the default pair filter and a specified object layer to determine if broadphase layers collide - DefaultBroadPhaseLayerFilter GetDefaultBroadPhaseLayerFilter(ObjectLayer inLayer) const { return DefaultBroadPhaseLayerFilter(*mObjectVsBroadPhaseLayerFilter, inLayer); } - - /// Get an object layer filter that uses the default pair filter and a specified layer to determine if layers collide - DefaultObjectLayerFilter GetDefaultLayerFilter(ObjectLayer inLayer) const { return DefaultObjectLayerFilter(*mObjectLayerPairFilter, inLayer); } - - /// Gets the current amount of bodies that are in the body manager - uint GetNumBodies() const { return mBodyManager.GetNumBodies(); } - - /// Gets the current amount of active bodies that are in the body manager - uint32 GetNumActiveBodies(EBodyType inType) const { return mBodyManager.GetNumActiveBodies(inType); } - - /// Get the maximum amount of bodies that this physics system supports - uint GetMaxBodies() const { return mBodyManager.GetMaxBodies(); } - - /// Helper struct that counts the number of bodies of each type - using BodyStats = BodyManager::BodyStats; - - /// Get stats about the bodies in the body manager (slow, iterates through all bodies) - BodyStats GetBodyStats() const { return mBodyManager.GetBodyStats(); } - - /// Get copy of the list of all bodies under protection of a lock. - /// @param outBodyIDs On return, this will contain the list of BodyIDs - void GetBodies(BodyIDVector &outBodyIDs) const { return mBodyManager.GetBodyIDs(outBodyIDs); } - - /// Get copy of the list of active bodies under protection of a lock. - /// @param inType The type of bodies to get - /// @param outBodyIDs On return, this will contain the list of BodyIDs - void GetActiveBodies(EBodyType inType, BodyIDVector &outBodyIDs) const { return mBodyManager.GetActiveBodies(inType, outBodyIDs); } - - /// Get the list of active bodies, use GetNumActiveBodies() to find out how long the list is. - /// Note: Not thread safe. The active bodies list can change at any moment when other threads are doing work. Use GetActiveBodies() if you need a thread safe version. - const BodyID * GetActiveBodiesUnsafe(EBodyType inType) const { return mBodyManager.GetActiveBodiesUnsafe(inType); } - - /// Check if 2 bodies were in contact during the last simulation step. Since contacts are only detected between active bodies, so at least one of the bodies must be active in order for this function to work. - /// It queries the state at the time of the last PhysicsSystem::Update and will return true if the bodies were in contact, even if one of the bodies was moved / removed afterwards. - /// This function can be called from any thread when the PhysicsSystem::Update is not running. During PhysicsSystem::Update this function is only valid during contact callbacks: - /// - During the ContactListener::OnContactAdded callback this function can be used to determine if a different contact pair between the bodies was active in the previous simulation step (function returns true) or if this is the first step that the bodies are touching (function returns false). - /// - During the ContactListener::OnContactRemoved callback this function can be used to determine if this is the last contact pair between the bodies (function returns false) or if there are other contacts still present (function returns true). - bool WereBodiesInContact(const BodyID &inBody1ID, const BodyID &inBody2ID) const { return mContactManager.WereBodiesInContact(inBody1ID, inBody2ID); } - - /// Get the bounding box of all bodies in the physics system - AABox GetBounds() const { return mBroadPhase->GetBounds(); } - -#ifdef JPH_TRACK_BROADPHASE_STATS - /// Trace the accumulated broadphase stats to the TTY - void ReportBroadphaseStats() { mBroadPhase->ReportStats(); } -#endif // JPH_TRACK_BROADPHASE_STATS - -private: - using CCDBody = PhysicsUpdateContext::Step::CCDBody; - - // Various job entry points - void JobStepListeners(PhysicsUpdateContext::Step *ioStep); - void JobDetermineActiveConstraints(PhysicsUpdateContext::Step *ioStep) const; - void JobApplyGravity(const PhysicsUpdateContext *ioContext, PhysicsUpdateContext::Step *ioStep); - void JobSetupVelocityConstraints(float inDeltaTime, PhysicsUpdateContext::Step *ioStep) const; - void JobBuildIslandsFromConstraints(PhysicsUpdateContext *ioContext, PhysicsUpdateContext::Step *ioStep); - void JobFindCollisions(PhysicsUpdateContext::Step *ioStep, int inJobIndex); - void JobFinalizeIslands(PhysicsUpdateContext *ioContext); - void JobBodySetIslandIndex(); - void JobSolveVelocityConstraints(PhysicsUpdateContext *ioContext, PhysicsUpdateContext::Step *ioStep); - void JobPreIntegrateVelocity(PhysicsUpdateContext *ioContext, PhysicsUpdateContext::Step *ioStep); - void JobIntegrateVelocity(const PhysicsUpdateContext *ioContext, PhysicsUpdateContext::Step *ioStep); - void JobPostIntegrateVelocity(PhysicsUpdateContext *ioContext, PhysicsUpdateContext::Step *ioStep) const; - void JobFindCCDContacts(const PhysicsUpdateContext *ioContext, PhysicsUpdateContext::Step *ioStep); - void JobResolveCCDContacts(PhysicsUpdateContext *ioContext, PhysicsUpdateContext::Step *ioStep); - void JobContactRemovedCallbacks(const PhysicsUpdateContext::Step *ioStep); - void JobSolvePositionConstraints(PhysicsUpdateContext *ioContext, PhysicsUpdateContext::Step *ioStep); - void JobSoftBodyPrepare(PhysicsUpdateContext *ioContext, PhysicsUpdateContext::Step *ioStep); - void JobSoftBodyCollide(PhysicsUpdateContext *ioContext) const; - void JobSoftBodySimulate(PhysicsUpdateContext *ioContext, uint inThreadIndex) const; - void JobSoftBodyFinalize(PhysicsUpdateContext *ioContext); - - /// Tries to spawn a new FindCollisions job if max concurrency hasn't been reached yet - void TrySpawnJobFindCollisions(PhysicsUpdateContext::Step *ioStep) const; - - using ContactAllocator = ContactConstraintManager::ContactAllocator; - - /// Process narrow phase for a single body pair - void ProcessBodyPair(ContactAllocator &ioContactAllocator, const BodyPair &inBodyPair); - - /// This helper batches up bodies that need to put to sleep to avoid contention on the activation mutex - class BodiesToSleep; - - /// Called at the end of JobSolveVelocityConstraints to check if bodies need to go to sleep and to update their bounding box in the broadphase - void CheckSleepAndUpdateBounds(uint32 inIslandIndex, const PhysicsUpdateContext *ioContext, const PhysicsUpdateContext::Step *ioStep, BodiesToSleep &ioBodiesToSleep); - - /// Number of constraints to process at once in JobDetermineActiveConstraints - static constexpr int cDetermineActiveConstraintsBatchSize = 64; - - /// Number of constraints to process at once in JobSetupVelocityConstraints, we want a low number of threads working on this so we take fairly large batches - static constexpr int cSetupVelocityConstraintsBatchSize = 256; - - /// Number of bodies to process at once in JobApplyGravity - static constexpr int cApplyGravityBatchSize = 64; - - /// Number of active bodies to test for collisions per batch - static constexpr int cActiveBodiesBatchSize = 16; - - /// Number of active bodies to integrate velocities for - static constexpr int cIntegrateVelocityBatchSize = 64; - - /// Number of contacts that need to be queued before another narrow phase job is started - static constexpr int cNarrowPhaseBatchSize = 16; - - /// Number of continuous collision shape casts that need to be queued before another job is started - static constexpr int cNumCCDBodiesPerJob = 4; - - /// Broadphase layer filter that decides if two objects can collide - const ObjectVsBroadPhaseLayerFilter *mObjectVsBroadPhaseLayerFilter = nullptr; - - /// Object layer filter that decides if two objects can collide - const ObjectLayerPairFilter *mObjectLayerPairFilter = nullptr; - - /// The body manager keeps track which bodies are in the simulation - BodyManager mBodyManager; - - /// Body locking interfaces - BodyLockInterfaceNoLock mBodyLockInterfaceNoLock { mBodyManager }; - BodyLockInterfaceLocking mBodyLockInterfaceLocking { mBodyManager }; - - /// Body interfaces - BodyInterface mBodyInterfaceNoLock; - BodyInterface mBodyInterfaceLocking; - - /// Narrow phase query interface - NarrowPhaseQuery mNarrowPhaseQueryNoLock; - NarrowPhaseQuery mNarrowPhaseQueryLocking; - - /// The broadphase does quick collision detection between body pairs - BroadPhase * mBroadPhase = nullptr; - - /// The soft body contact listener - SoftBodyContactListener * mSoftBodyContactListener = nullptr; - - /// Simulation settings - PhysicsSettings mPhysicsSettings; - - /// The contact manager resolves all contacts during a simulation step - ContactConstraintManager mContactManager; - - /// All non-contact constraints - ConstraintManager mConstraintManager; - - /// Keeps track of connected bodies and builds islands for multithreaded velocity/position update - IslandBuilder mIslandBuilder; - - /// Will split large islands into smaller groups of bodies that can be processed in parallel - LargeIslandSplitter mLargeIslandSplitter; - - /// Mutex protecting mStepListeners - Mutex mStepListenersMutex; - - /// List of physics step listeners - using StepListeners = Array; - StepListeners mStepListeners; - - /// This is the global gravity vector - Vec3 mGravity = Vec3(0, -9.81f, 0); - - /// Previous frame's delta time of one sub step to allow scaling previous frame's constraint impulses - float mPreviousStepDeltaTime = 0.0f; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsUpdateContext.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsUpdateContext.cpp deleted file mode 100644 index 7c60ae6be66..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsUpdateContext.cpp +++ /dev/null @@ -1,23 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include - -JPH_NAMESPACE_BEGIN - -PhysicsUpdateContext::PhysicsUpdateContext(TempAllocator &inTempAllocator) : - mTempAllocator(&inTempAllocator), - mSteps(inTempAllocator) -{ -} - -PhysicsUpdateContext::~PhysicsUpdateContext() -{ - JPH_ASSERT(mBodyPairs == nullptr); - JPH_ASSERT(mActiveConstraints == nullptr); -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsUpdateContext.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsUpdateContext.h deleted file mode 100644 index fbcdb261775..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/PhysicsUpdateContext.h +++ /dev/null @@ -1,172 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -class PhysicsSystem; -class IslandBuilder; -class Constraint; -class TempAllocator; -class SoftBodyUpdateContext; - -/// Information used during the Update call -class PhysicsUpdateContext : public NonCopyable -{ -public: - /// Destructor - explicit PhysicsUpdateContext(TempAllocator &inTempAllocator); - ~PhysicsUpdateContext(); - - static constexpr int cMaxConcurrency = 32; ///< Maximum supported amount of concurrent jobs - - using JobHandleArray = StaticArray; - - struct Step; - - struct BodyPairQueue - { - atomic mWriteIdx { 0 }; ///< Next index to write in mBodyPair array (need to add thread index * mMaxBodyPairsPerQueue and modulo mMaxBodyPairsPerQueue) - uint8 mPadding1[JPH_CACHE_LINE_SIZE - sizeof(atomic)];///< Moved to own cache line to avoid conflicts with consumer jobs - - atomic mReadIdx { 0 }; ///< Next index to read in mBodyPair array (need to add thread index * mMaxBodyPairsPerQueue and modulo mMaxBodyPairsPerQueue) - uint8 mPadding2[JPH_CACHE_LINE_SIZE - sizeof(atomic)];///< Moved to own cache line to avoid conflicts with producer/consumer jobs - }; - - using BodyPairQueues = StaticArray; - - using JobMask = uint32; ///< A mask that has as many bits as we can have concurrent jobs - static_assert(sizeof(JobMask) * 8 >= cMaxConcurrency); - - /// Structure that contains data needed for each collision step. - struct Step - { - Step() = default; - Step(const Step &) { JPH_ASSERT(false); } // vector needs a copy constructor, but we're never going to call it - - PhysicsUpdateContext *mContext; ///< The physics update context - - bool mIsFirst; ///< If this is the first step - bool mIsLast; ///< If this is the last step - - BroadPhase::UpdateState mBroadPhaseUpdateState; ///< Handle returned by Broadphase::UpdatePrepare - - uint32 mNumActiveBodiesAtStepStart; ///< Number of bodies that were active at the start of the physics update step. Only these bodies will receive gravity (they are the first N in the active body list). - - atomic mDetermineActiveConstraintReadIdx { 0 }; ///< Next constraint for determine active constraints - uint8 mPadding1[JPH_CACHE_LINE_SIZE - sizeof(atomic)];///< Padding to avoid sharing cache line with the next atomic - - atomic mNumActiveConstraints { 0 }; ///< Number of constraints in the mActiveConstraints array - uint8 mPadding2[JPH_CACHE_LINE_SIZE - sizeof(atomic)];///< Padding to avoid sharing cache line with the next atomic - - atomic mSetupVelocityConstraintsReadIdx { 0 }; ///< Next constraint for setting up velocity constraints - uint8 mPadding3[JPH_CACHE_LINE_SIZE - sizeof(atomic)];///< Padding to avoid sharing cache line with the next atomic - - atomic mStepListenerReadIdx { 0 }; ///< Next step listener to call - uint8 mPadding4[JPH_CACHE_LINE_SIZE - sizeof(atomic)];///< Padding to avoid sharing cache line with the next atomic - - atomic mApplyGravityReadIdx { 0 }; ///< Next body to apply gravity to - uint8 mPadding5[JPH_CACHE_LINE_SIZE - sizeof(atomic)];///< Padding to avoid sharing cache line with the next atomic - - atomic mActiveBodyReadIdx { 0 }; ///< Index of fist active body that has not yet been processed by the broadphase - uint8 mPadding6[JPH_CACHE_LINE_SIZE - sizeof(atomic)];///< Padding to avoid sharing cache line with the next atomic - - BodyPairQueues mBodyPairQueues; ///< Queues in which to put body pairs that need to be tested by the narrowphase - - uint32 mMaxBodyPairsPerQueue; ///< Amount of body pairs that we can queue per queue - - atomic mActiveFindCollisionJobs; ///< A bitmask that indicates which jobs are still active - - atomic mNumBodyPairs { 0 }; ///< The number of body pairs found in this step (used to size the contact cache in the next step) - atomic mNumManifolds { 0 }; ///< The number of manifolds found in this step (used to size the contact cache in the next step) - - atomic mSolveVelocityConstraintsNextIsland { 0 }; ///< Next island that needs to be processed for the solve velocity constraints step (doesn't need own cache line since position jobs don't run at same time) - atomic mSolvePositionConstraintsNextIsland { 0 }; ///< Next island that needs to be processed for the solve position constraints step (doesn't need own cache line since velocity jobs don't run at same time) - - /// Contains the information needed to cast a body through the scene to do continuous collision detection - struct CCDBody - { - CCDBody(BodyID inBodyID1, Vec3Arg inDeltaPosition, float inLinearCastThresholdSq, float inMaxPenetration) : mDeltaPosition(inDeltaPosition), mBodyID1(inBodyID1), mLinearCastThresholdSq(inLinearCastThresholdSq), mMaxPenetration(inMaxPenetration) { } - - Vec3 mDeltaPosition; ///< Desired rotation step - Vec3 mContactNormal; ///< World space normal of closest hit (only valid if mFractionPlusSlop < 1) - RVec3 mContactPointOn2; ///< World space contact point on body 2 of closest hit (only valid if mFractionPlusSlop < 1) - BodyID mBodyID1; ///< Body 1 (the body that is performing collision detection) - BodyID mBodyID2; ///< Body 2 (the body of the closest hit, only valid if mFractionPlusSlop < 1) - SubShapeID mSubShapeID2; ///< Sub shape of body 2 that was hit (only valid if mFractionPlusSlop < 1) - float mFraction = 1.0f; ///< Fraction at which the hit occurred - float mFractionPlusSlop = 1.0f; ///< Fraction at which the hit occurred + extra delta to allow body to penetrate by mMaxPenetration - float mLinearCastThresholdSq; ///< Maximum allowed squared movement before doing a linear cast (determined by inner radius of shape) - float mMaxPenetration; ///< Maximum allowed penetration (determined by inner radius of shape) - ContactSettings mContactSettings; ///< The contact settings for this contact - }; - atomic mIntegrateVelocityReadIdx { 0 }; ///< Next active body index to take when integrating velocities - CCDBody * mCCDBodies = nullptr; ///< List of bodies that need to do continuous collision detection - uint32 mCCDBodiesCapacity = 0; ///< Capacity of the mCCDBodies list - atomic mNumCCDBodies = 0; ///< Number of CCD bodies in mCCDBodies - atomic mNextCCDBody { 0 }; ///< Next unprocessed body index in mCCDBodies - int * mActiveBodyToCCDBody = nullptr; ///< A mapping between an index in BodyManager::mActiveBodies and the index in mCCDBodies - uint32 mNumActiveBodyToCCDBody = 0; ///< Number of indices in mActiveBodyToCCDBody - - // Jobs in order of execution (some run in parallel) - JobHandle mBroadPhasePrepare; ///< Prepares the new tree in the background - JobHandleArray mStepListeners; ///< Listeners to notify of the beginning of a physics step - JobHandleArray mDetermineActiveConstraints; ///< Determine which constraints will be active during this step - JobHandleArray mApplyGravity; ///< Update velocities of bodies with gravity - JobHandleArray mFindCollisions; ///< Find all collisions between active bodies an the world - JobHandle mUpdateBroadphaseFinalize; ///< Swap the newly built tree with the current tree - JobHandleArray mSetupVelocityConstraints; ///< Calculate properties for all constraints in the constraint manager - JobHandle mBuildIslandsFromConstraints; ///< Go over all constraints and assign the bodies they're attached to to an island - JobHandle mFinalizeIslands; ///< Finalize calculation simulation islands - JobHandle mBodySetIslandIndex; ///< Set the current island index on each body (not used by the simulation, only for drawing purposes) - JobHandleArray mSolveVelocityConstraints; ///< Solve the constraints in the velocity domain - JobHandle mPreIntegrateVelocity; ///< Setup integration of all body positions - JobHandleArray mIntegrateVelocity; ///< Integrate all body positions - JobHandle mPostIntegrateVelocity; ///< Finalize integration of all body positions - JobHandle mResolveCCDContacts; ///< Updates the positions and velocities for all bodies that need continuous collision detection - JobHandleArray mSolvePositionConstraints; ///< Solve all constraints in the position domain - JobHandle mContactRemovedCallbacks; ///< Calls the contact removed callbacks - JobHandle mSoftBodyPrepare; ///< Prepares updating the soft bodies - JobHandleArray mSoftBodyCollide; ///< Finds all colliding shapes for soft bodies - JobHandleArray mSoftBodySimulate; ///< Simulates all particles - JobHandle mSoftBodyFinalize; ///< Finalizes the soft body update - JobHandle mStartNextStep; ///< Job that kicks the next step (empty for the last step) - }; - - using Steps = std::vector>; - - /// Maximum amount of concurrent jobs on this machine - int GetMaxConcurrency() const { const int max_concurrency = PhysicsUpdateContext::cMaxConcurrency; return min(max_concurrency, mJobSystem->GetMaxConcurrency()); } ///< Need to put max concurrency in temp var as min requires a reference - - PhysicsSystem * mPhysicsSystem; ///< The physics system we belong to - TempAllocator * mTempAllocator; ///< Temporary allocator used during the update - JobSystem * mJobSystem; ///< Job system that processes jobs - JobSystem::Barrier * mBarrier; ///< Barrier used to wait for all physics jobs to complete - - float mStepDeltaTime; ///< Delta time for a simulation step (collision step) - float mWarmStartImpulseRatio; ///< Ratio of this step delta time vs last step - atomic mErrors { 0 }; ///< Errors that occurred during the update, actual type is EPhysicsUpdateError - - Constraint ** mActiveConstraints = nullptr; ///< Constraints that were active at the start of the physics update step (activating bodies can activate constraints and we need a consistent snapshot). Only these constraints will be resolved. - - BodyPair * mBodyPairs = nullptr; ///< A list of body pairs found by the broadphase - - IslandBuilder * mIslandBuilder; ///< Keeps track of connected bodies and builds islands for multithreaded velocity/position update - - Steps mSteps; - - uint mNumSoftBodies; ///< Number of active soft bodies in the simulation - SoftBodyUpdateContext * mSoftBodyUpdateContexts = nullptr; ///< Contexts for updating soft bodies - atomic mSoftBodyToCollide { 0 }; ///< Next soft body to take when running SoftBodyCollide jobs -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Ragdoll/Ragdoll.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Ragdoll/Ragdoll.cpp deleted file mode 100644 index f66212f2647..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Ragdoll/Ragdoll.cpp +++ /dev/null @@ -1,699 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(RagdollSettings::Part) -{ - JPH_ADD_BASE_CLASS(RagdollSettings::Part, BodyCreationSettings) - - JPH_ADD_ATTRIBUTE(RagdollSettings::Part, mToParent) -} - -JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(RagdollSettings::AdditionalConstraint) -{ - JPH_ADD_ATTRIBUTE(RagdollSettings::AdditionalConstraint, mBodyIdx) - JPH_ADD_ATTRIBUTE(RagdollSettings::AdditionalConstraint, mConstraint) -} - -JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(RagdollSettings) -{ - JPH_ADD_ATTRIBUTE(RagdollSettings, mSkeleton) - JPH_ADD_ATTRIBUTE(RagdollSettings, mParts) - JPH_ADD_ATTRIBUTE(RagdollSettings, mAdditionalConstraints) -} - -static inline BodyInterface &sGetBodyInterface(PhysicsSystem *inSystem, bool inLockBodies) -{ - return inLockBodies? inSystem->GetBodyInterface() : inSystem->GetBodyInterfaceNoLock(); -} - -static inline const BodyLockInterface &sGetBodyLockInterface(const PhysicsSystem *inSystem, bool inLockBodies) -{ - return inLockBodies? static_cast(inSystem->GetBodyLockInterface()) : static_cast(inSystem->GetBodyLockInterfaceNoLock()); -} - -bool RagdollSettings::Stabilize() -{ - // Based on: Stop my Constraints from Blowing Up! - Oliver Strunk (Havok) - // Do 2 things: - // 1. Limit the mass ratios between parents and children (slide 16) - // 2. Increase the inertia of parents so that they're bigger or equal to the sum of their children (slide 34) - - // If we don't have any joints there's nothing to stabilize - if (mSkeleton->GetJointCount() == 0) - return true; - - // The skeleton can contain one or more static bodies. We can't modify the mass for those so we start a new stabilization chain for each joint under a static body until we reach the next static body. - // This array keeps track of which joints have been processed. - Array visited; - visited.resize(mSkeleton->GetJointCount()); - for (size_t v = 0; v < visited.size(); ++v) - { - // Mark static bodies as visited so we won't process these - Part &p = mParts[v]; - bool has_mass_properties = p.HasMassProperties(); - visited[v] = !has_mass_properties; - - if (has_mass_properties && p.mOverrideMassProperties != EOverrideMassProperties::MassAndInertiaProvided) - { - // Mass properties not yet calculated, do it now - p.mMassPropertiesOverride = p.GetMassProperties(); - p.mOverrideMassProperties = EOverrideMassProperties::MassAndInertiaProvided; - } - } - - // Find first unvisited part that either has no parent or that has a parent that was visited - for (int first_idx = 0; first_idx < mSkeleton->GetJointCount(); ++first_idx) - { - int first_idx_parent = mSkeleton->GetJoint(first_idx).mParentJointIndex; - if (!visited[first_idx] && (first_idx_parent == -1 || visited[first_idx_parent])) - { - // Find all children of first_idx and their children up to the next static part - int next_to_process = 0; - Array indices; - indices.reserve(mSkeleton->GetJointCount()); - visited[first_idx] = true; - indices.push_back(first_idx); - do - { - int parent_idx = indices[next_to_process++]; - for (int child_idx = 0; child_idx < mSkeleton->GetJointCount(); ++child_idx) - if (!visited[child_idx] && mSkeleton->GetJoint(child_idx).mParentJointIndex == parent_idx) - { - visited[child_idx] = true; - indices.push_back(child_idx); - } - } while (next_to_process < (int)indices.size()); - - // If there's only 1 body, we can't redistribute mass - if (indices.size() == 1) - continue; - - const float cMinMassRatio = 0.8f; - const float cMaxMassRatio = 1.2f; - - // Ensure that the mass ratio from parent to child is within a range - float total_mass_ratio = 1.0f; - Array mass_ratios; - mass_ratios.resize(mSkeleton->GetJointCount()); - mass_ratios[indices[0]] = 1.0f; - for (int i = 1; i < (int)indices.size(); ++i) - { - int child_idx = indices[i]; - int parent_idx = mSkeleton->GetJoint(child_idx).mParentJointIndex; - float ratio = mParts[child_idx].mMassPropertiesOverride.mMass / mParts[parent_idx].mMassPropertiesOverride.mMass; - mass_ratios[child_idx] = mass_ratios[parent_idx] * Clamp(ratio, cMinMassRatio, cMaxMassRatio); - total_mass_ratio += mass_ratios[child_idx]; - } - - // Calculate total mass of this chain - float total_mass = 0.0f; - for (int idx : indices) - total_mass += mParts[idx].mMassPropertiesOverride.mMass; - - // Calculate how much mass belongs to a ratio of 1 - float ratio_to_mass = total_mass / total_mass_ratio; - - // Adjust all masses and inertia tensors for the new mass - for (int i : indices) - { - Part &p = mParts[i]; - float old_mass = p.mMassPropertiesOverride.mMass; - float new_mass = mass_ratios[i] * ratio_to_mass; - p.mMassPropertiesOverride.mMass = new_mass; - p.mMassPropertiesOverride.mInertia *= new_mass / old_mass; - p.mMassPropertiesOverride.mInertia.SetColumn4(3, Vec4(0, 0, 0, 1)); - } - - const float cMaxInertiaIncrease = 2.0f; - - // Get the principal moments of inertia for all parts - struct Principal - { - Mat44 mRotation; - Vec3 mDiagonal; - float mChildSum = 0.0f; - }; - Array principals; - principals.resize(mParts.size()); - for (int i : indices) - if (!mParts[i].mMassPropertiesOverride.DecomposePrincipalMomentsOfInertia(principals[i].mRotation, principals[i].mDiagonal)) - { - JPH_ASSERT(false, "Failed to decompose the inertia tensor!"); - return false; - } - - // Calculate sum of child inertias - // Walk backwards so we sum the leaves first - for (int i = (int)indices.size() - 1; i > 0; --i) - { - int child_idx = indices[i]; - int parent_idx = mSkeleton->GetJoint(child_idx).mParentJointIndex; - principals[parent_idx].mChildSum += principals[child_idx].mDiagonal[0] + principals[child_idx].mChildSum; - } - - // Adjust inertia tensors for all parts - for (int i : indices) - { - Part &p = mParts[i]; - Principal &principal = principals[i]; - if (principal.mChildSum != 0.0f) - { - // Calculate minimum inertia this object should have based on it children - float minimum = min(cMaxInertiaIncrease * principal.mDiagonal[0], principal.mChildSum); - principal.mDiagonal = Vec3::sMax(principal.mDiagonal, Vec3::sReplicate(minimum)); - - // Recalculate moment of inertia in body space - p.mMassPropertiesOverride.mInertia = principal.mRotation * Mat44::sScale(principal.mDiagonal) * principal.mRotation.Inversed3x3(); - } - } - } - } - - return true; -} - -void RagdollSettings::DisableParentChildCollisions(const Mat44 *inJointMatrices, float inMinSeparationDistance) -{ - int joint_count = mSkeleton->GetJointCount(); - JPH_ASSERT(joint_count == (int)mParts.size()); - - // Create a group filter table that disables collisions between parent and child - Ref group_filter = new GroupFilterTable(joint_count); - for (int joint_idx = 0; joint_idx < joint_count; ++joint_idx) - { - int parent_joint = mSkeleton->GetJoint(joint_idx).mParentJointIndex; - if (parent_joint >= 0) - group_filter->DisableCollision(joint_idx, parent_joint); - } - - // If joint matrices are provided - if (inJointMatrices != nullptr) - { - // Loop over all joints - for (int j1 = 0; j1 < joint_count; ++j1) - { - // Shape and transform for joint 1 - const Part &part1 = mParts[j1]; - const Shape *shape1 = part1.GetShape(); - Vec3 scale1; - Mat44 com1 = (inJointMatrices[j1].PreTranslated(shape1->GetCenterOfMass())).Decompose(scale1); - - // Loop over all other joints - for (int j2 = j1 + 1; j2 < joint_count; ++j2) - if (group_filter->IsCollisionEnabled(j1, j2)) // Only if collision is still enabled we need to test - { - // Shape and transform for joint 2 - const Part &part2 = mParts[j2]; - const Shape *shape2 = part2.GetShape(); - Vec3 scale2; - Mat44 com2 = (inJointMatrices[j2].PreTranslated(shape2->GetCenterOfMass())).Decompose(scale2); - - // Collision settings - CollideShapeSettings settings; - settings.mActiveEdgeMode = EActiveEdgeMode::CollideWithAll; - settings.mBackFaceMode = EBackFaceMode::CollideWithBackFaces; - settings.mMaxSeparationDistance = inMinSeparationDistance; - - // Only check if one of the two bodies can become dynamic - if (part1.HasMassProperties() || part2.HasMassProperties()) - { - // If there is a collision, disable the collision between the joints - AnyHitCollisionCollector collector; - if (part1.HasMassProperties()) // Ensure that the first shape is always a dynamic one (we can't check mesh vs convex but we can check convex vs mesh) - CollisionDispatch::sCollideShapeVsShape(shape1, shape2, scale1, scale2, com1, com2, SubShapeIDCreator(), SubShapeIDCreator(), settings, collector); - else - CollisionDispatch::sCollideShapeVsShape(shape2, shape1, scale2, scale1, com2, com1, SubShapeIDCreator(), SubShapeIDCreator(), settings, collector); - if (collector.HadHit()) - group_filter->DisableCollision(j1, j2); - } - } - } - } - - // Loop over the body parts and assign them a sub group ID and the group filter - for (int joint_idx = 0; joint_idx < joint_count; ++joint_idx) - { - Part &part = mParts[joint_idx]; - part.mCollisionGroup.SetSubGroupID(joint_idx); - part.mCollisionGroup.SetGroupFilter(group_filter); - } -} - -void RagdollSettings::SaveBinaryState(StreamOut &inStream, bool inSaveShapes, bool inSaveGroupFilter) const -{ - BodyCreationSettings::ShapeToIDMap shape_to_id; - BodyCreationSettings::MaterialToIDMap material_to_id; - BodyCreationSettings::GroupFilterToIDMap group_filter_to_id; - - // Save skeleton - mSkeleton->SaveBinaryState(inStream); - - // Save parts - inStream.Write((uint32)mParts.size()); - for (const Part &p : mParts) - { - // Write body creation settings - p.SaveWithChildren(inStream, inSaveShapes? &shape_to_id : nullptr, inSaveShapes? &material_to_id : nullptr, inSaveGroupFilter? &group_filter_to_id : nullptr); - - // Save constraint - inStream.Write(p.mToParent != nullptr); - if (p.mToParent != nullptr) - p.mToParent->SaveBinaryState(inStream); - } - - // Save additional constraints - inStream.Write((uint32)mAdditionalConstraints.size()); - for (const AdditionalConstraint &c : mAdditionalConstraints) - { - // Save bodies indices - inStream.Write(c.mBodyIdx); - - // Save constraint - c.mConstraint->SaveBinaryState(inStream); - } -} - -RagdollSettings::RagdollResult RagdollSettings::sRestoreFromBinaryState(StreamIn &inStream) -{ - RagdollResult result; - - // Restore skeleton - Skeleton::SkeletonResult skeleton_result = Skeleton::sRestoreFromBinaryState(inStream); - if (skeleton_result.HasError()) - { - result.SetError(skeleton_result.GetError()); - return result; - } - - // Create ragdoll - Ref ragdoll = new RagdollSettings(); - ragdoll->mSkeleton = skeleton_result.Get(); - - BodyCreationSettings::IDToShapeMap id_to_shape; - BodyCreationSettings::IDToMaterialMap id_to_material; - BodyCreationSettings::IDToGroupFilterMap id_to_group_filter; - - // Reserve some memory to avoid frequent reallocations - id_to_shape.reserve(1024); - id_to_material.reserve(128); - id_to_group_filter.reserve(128); - - // Read parts - uint32 len = 0; - inStream.Read(len); - ragdoll->mParts.resize(len); - for (Part &p : ragdoll->mParts) - { - // Read creation settings - BodyCreationSettings::BCSResult bcs_result = BodyCreationSettings::sRestoreWithChildren(inStream, id_to_shape, id_to_material, id_to_group_filter); - if (bcs_result.HasError()) - { - result.SetError(bcs_result.GetError()); - return result; - } - static_cast(p) = bcs_result.Get(); - - // Read constraint - bool has_constraint = false; - inStream.Read(has_constraint); - if (has_constraint) - { - ConstraintSettings::ConstraintResult constraint_result = ConstraintSettings::sRestoreFromBinaryState(inStream); - if (constraint_result.HasError()) - { - result.SetError(constraint_result.GetError()); - return result; - } - p.mToParent = DynamicCast(constraint_result.Get().GetPtr()); - } - } - - // Read additional constraints - len = 0; - inStream.Read(len); - ragdoll->mAdditionalConstraints.resize(len); - for (AdditionalConstraint &c : ragdoll->mAdditionalConstraints) - { - // Read body indices - inStream.Read(c.mBodyIdx); - - // Read constraint - ConstraintSettings::ConstraintResult constraint_result = ConstraintSettings::sRestoreFromBinaryState(inStream); - if (constraint_result.HasError()) - { - result.SetError(constraint_result.GetError()); - return result; - } - c.mConstraint = DynamicCast(constraint_result.Get().GetPtr()); - } - - // Create mapping tables - ragdoll->CalculateBodyIndexToConstraintIndex(); - ragdoll->CalculateConstraintIndexToBodyIdxPair(); - - result.Set(ragdoll); - return result; -} - -Ragdoll *RagdollSettings::CreateRagdoll(CollisionGroup::GroupID inCollisionGroup, uint64 inUserData, PhysicsSystem *inSystem) const -{ - Ragdoll *r = new Ragdoll(inSystem); - r->mRagdollSettings = this; - r->mBodyIDs.reserve(mParts.size()); - r->mConstraints.reserve(mParts.size() + mAdditionalConstraints.size()); - - // Create bodies and constraints - BodyInterface &bi = inSystem->GetBodyInterface(); - Body **bodies = (Body **)JPH_STACK_ALLOC(mParts.size() * sizeof(Body *)); - int joint_idx = 0; - for (const Part &p : mParts) - { - Body *body2 = bi.CreateBody(p); - if (body2 == nullptr) - { - // Out of bodies, failed to create ragdoll - delete r; - return nullptr; - } - body2->GetCollisionGroup().SetGroupID(inCollisionGroup); - body2->SetUserData(inUserData); - - // Temporarily store body pointer for hooking up constraints - bodies[joint_idx] = body2; - - // Create constraint - if (p.mToParent != nullptr) - { - Body *body1 = bodies[mSkeleton->GetJoint(joint_idx).mParentJointIndex]; - r->mConstraints.push_back(p.mToParent->Create(*body1, *body2)); - } - - // Store body ID and constraint in parallel arrays - r->mBodyIDs.push_back(body2->GetID()); - - ++joint_idx; - } - - // Add additional constraints - for (const AdditionalConstraint &c : mAdditionalConstraints) - { - Body *body1 = bodies[c.mBodyIdx[0]]; - Body *body2 = bodies[c.mBodyIdx[1]]; - r->mConstraints.push_back(c.mConstraint->Create(*body1, *body2)); - } - - return r; -} - -void RagdollSettings::CalculateBodyIndexToConstraintIndex() -{ - mBodyIndexToConstraintIndex.clear(); - mBodyIndexToConstraintIndex.reserve(mParts.size()); - - int constraint_index = 0; - for (const Part &p : mParts) - { - if (p.mToParent != nullptr) - mBodyIndexToConstraintIndex.push_back(constraint_index++); - else - mBodyIndexToConstraintIndex.push_back(-1); - } -} - -void RagdollSettings::CalculateConstraintIndexToBodyIdxPair() -{ - mConstraintIndexToBodyIdxPair.clear(); - mConstraintIndexToBodyIdxPair.reserve(mParts.size() + mAdditionalConstraints.size()); - - // Add constraints between parts - int joint_idx = 0; - for (const Part &p : mParts) - { - if (p.mToParent != nullptr) - { - int parent_joint_idx = mSkeleton->GetJoint(joint_idx).mParentJointIndex; - mConstraintIndexToBodyIdxPair.emplace_back(parent_joint_idx, joint_idx); - } - - ++joint_idx; - } - - // Add additional constraints - for (const AdditionalConstraint &c : mAdditionalConstraints) - mConstraintIndexToBodyIdxPair.emplace_back(c.mBodyIdx[0], c.mBodyIdx[1]); -} - -Ragdoll::~Ragdoll() -{ - // Destroy all bodies - mSystem->GetBodyInterface().DestroyBodies(mBodyIDs.data(), (int)mBodyIDs.size()); -} - -void Ragdoll::AddToPhysicsSystem(EActivation inActivationMode, bool inLockBodies) -{ - // Scope for JPH_STACK_ALLOC - { - // Create copy of body ids since they will be shuffled - int num_bodies = (int)mBodyIDs.size(); - BodyID *bodies = (BodyID *)JPH_STACK_ALLOC(num_bodies * sizeof(BodyID)); - memcpy(bodies, mBodyIDs.data(), num_bodies * sizeof(BodyID)); - - // Insert bodies as a batch - BodyInterface &bi = sGetBodyInterface(mSystem, inLockBodies); - BodyInterface::AddState add_state = bi.AddBodiesPrepare(bodies, num_bodies); - bi.AddBodiesFinalize(bodies, num_bodies, add_state, inActivationMode); - } - - // Add all constraints - mSystem->AddConstraints((Constraint **)mConstraints.data(), (int)mConstraints.size()); -} - -void Ragdoll::RemoveFromPhysicsSystem(bool inLockBodies) -{ - // Remove all constraints before removing the bodies - mSystem->RemoveConstraints((Constraint **)mConstraints.data(), (int)mConstraints.size()); - - // Scope for JPH_STACK_ALLOC - { - // Create copy of body ids since they will be shuffled - int num_bodies = (int)mBodyIDs.size(); - BodyID *bodies = (BodyID *)JPH_STACK_ALLOC(num_bodies * sizeof(BodyID)); - memcpy(bodies, mBodyIDs.data(), num_bodies * sizeof(BodyID)); - - // Remove all bodies as a batch - sGetBodyInterface(mSystem, inLockBodies).RemoveBodies(bodies, num_bodies); - } -} - -void Ragdoll::Activate(bool inLockBodies) -{ - sGetBodyInterface(mSystem, inLockBodies).ActivateBodies(mBodyIDs.data(), (int)mBodyIDs.size()); -} - -bool Ragdoll::IsActive(bool inLockBodies) const -{ - // Lock the bodies - int body_count = (int)mBodyIDs.size(); - BodyLockMultiRead lock(sGetBodyLockInterface(mSystem, inLockBodies), mBodyIDs.data(), body_count); - - // Test if any body is active - for (int b = 0; b < body_count; ++b) - { - const Body *body = lock.GetBody(b); - if (body->IsActive()) - return true; - } - - return false; -} - -void Ragdoll::SetGroupID(CollisionGroup::GroupID inGroupID, bool inLockBodies) -{ - // Lock the bodies - int body_count = (int)mBodyIDs.size(); - BodyLockMultiWrite lock(sGetBodyLockInterface(mSystem, inLockBodies), mBodyIDs.data(), body_count); - - // Update group ID - for (int b = 0; b < body_count; ++b) - { - Body *body = lock.GetBody(b); - body->GetCollisionGroup().SetGroupID(inGroupID); - } -} - -void Ragdoll::SetPose(const SkeletonPose &inPose, bool inLockBodies) -{ - JPH_ASSERT(inPose.GetSkeleton() == mRagdollSettings->mSkeleton); - - SetPose(inPose.GetRootOffset(), inPose.GetJointMatrices().data(), inLockBodies); -} - -void Ragdoll::SetPose(RVec3Arg inRootOffset, const Mat44 *inJointMatrices, bool inLockBodies) -{ - // Move bodies instantly into the correct position - BodyInterface &bi = sGetBodyInterface(mSystem, inLockBodies); - for (int i = 0; i < (int)mBodyIDs.size(); ++i) - { - const Mat44 &joint = inJointMatrices[i]; - bi.SetPositionAndRotation(mBodyIDs[i], inRootOffset + joint.GetTranslation(), joint.GetQuaternion(), EActivation::DontActivate); - } -} - -void Ragdoll::GetPose(SkeletonPose &outPose, bool inLockBodies) -{ - JPH_ASSERT(outPose.GetSkeleton() == mRagdollSettings->mSkeleton); - - RVec3 root_offset; - GetPose(root_offset, outPose.GetJointMatrices().data(), inLockBodies); - outPose.SetRootOffset(root_offset); -} - -void Ragdoll::GetPose(RVec3 &outRootOffset, Mat44 *outJointMatrices, bool inLockBodies) -{ - // Lock the bodies - int body_count = (int)mBodyIDs.size(); - if (body_count == 0) - return; - BodyLockMultiRead lock(sGetBodyLockInterface(mSystem, inLockBodies), mBodyIDs.data(), body_count); - - // Get root matrix - const Body *root = lock.GetBody(0); - RMat44 root_transform = root->GetWorldTransform(); - outRootOffset = root_transform.GetTranslation(); - outJointMatrices[0] = Mat44(root_transform.GetColumn4(0), root_transform.GetColumn4(1), root_transform.GetColumn4(2), Vec4(0, 0, 0, 1)); - - // Get other matrices - for (int b = 1; b < body_count; ++b) - { - const Body *body = lock.GetBody(b); - RMat44 transform = body->GetWorldTransform(); - outJointMatrices[b] = Mat44(transform.GetColumn4(0), transform.GetColumn4(1), transform.GetColumn4(2), Vec4(Vec3(transform.GetTranslation() - outRootOffset), 1)); - } -} - -void Ragdoll::ResetWarmStart() -{ - for (TwoBodyConstraint *c : mConstraints) - c->ResetWarmStart(); -} - -void Ragdoll::DriveToPoseUsingKinematics(const SkeletonPose &inPose, float inDeltaTime, bool inLockBodies) -{ - JPH_ASSERT(inPose.GetSkeleton() == mRagdollSettings->mSkeleton); - - DriveToPoseUsingKinematics(inPose.GetRootOffset(), inPose.GetJointMatrices().data(), inDeltaTime, inLockBodies); -} - -void Ragdoll::DriveToPoseUsingKinematics(RVec3Arg inRootOffset, const Mat44 *inJointMatrices, float inDeltaTime, bool inLockBodies) -{ - // Move bodies into the correct position using kinematics - BodyInterface &bi = sGetBodyInterface(mSystem, inLockBodies); - for (int i = 0; i < (int)mBodyIDs.size(); ++i) - { - const Mat44 &joint = inJointMatrices[i]; - bi.MoveKinematic(mBodyIDs[i], inRootOffset + joint.GetTranslation(), joint.GetQuaternion(), inDeltaTime); - } -} - -void Ragdoll::DriveToPoseUsingMotors(const SkeletonPose &inPose) -{ - JPH_ASSERT(inPose.GetSkeleton() == mRagdollSettings->mSkeleton); - - // Move bodies into the correct position using constraints - for (int i = 0; i < (int)inPose.GetJointMatrices().size(); ++i) - { - int constraint_idx = mRagdollSettings->GetConstraintIndexForBodyIndex(i); - if (constraint_idx >= 0) - { - SwingTwistConstraint *constraint = (SwingTwistConstraint *)mConstraints[constraint_idx].GetPtr(); - - // Get desired rotation of this body relative to its parent - Quat joint_transform = inPose.GetJoint(i).mRotation; - - // Drive constraint to target - constraint->SetSwingMotorState(EMotorState::Position); - constraint->SetTwistMotorState(EMotorState::Position); - constraint->SetTargetOrientationBS(joint_transform); - } - } -} - -void Ragdoll::SetLinearAndAngularVelocity(Vec3Arg inLinearVelocity, Vec3Arg inAngularVelocity, bool inLockBodies) -{ - BodyInterface &bi = sGetBodyInterface(mSystem, inLockBodies); - for (BodyID body_id : mBodyIDs) - bi.SetLinearAndAngularVelocity(body_id, inLinearVelocity, inAngularVelocity); -} - -void Ragdoll::SetLinearVelocity(Vec3Arg inLinearVelocity, bool inLockBodies) -{ - BodyInterface &bi = sGetBodyInterface(mSystem, inLockBodies); - for (BodyID body_id : mBodyIDs) - bi.SetLinearVelocity(body_id, inLinearVelocity); -} - -void Ragdoll::AddLinearVelocity(Vec3Arg inLinearVelocity, bool inLockBodies) -{ - BodyInterface &bi = sGetBodyInterface(mSystem, inLockBodies); - for (BodyID body_id : mBodyIDs) - bi.AddLinearVelocity(body_id, inLinearVelocity); -} - -void Ragdoll::AddImpulse(Vec3Arg inImpulse, bool inLockBodies) -{ - BodyInterface &bi = sGetBodyInterface(mSystem, inLockBodies); - for (BodyID body_id : mBodyIDs) - bi.AddImpulse(body_id, inImpulse); -} - -void Ragdoll::GetRootTransform(RVec3 &outPosition, Quat &outRotation, bool inLockBodies) const -{ - BodyLockRead lock(sGetBodyLockInterface(mSystem, inLockBodies), mBodyIDs[0]); - if (lock.Succeeded()) - { - const Body &body = lock.GetBody(); - outPosition = body.GetPosition(); - outRotation = body.GetRotation(); - } - else - { - outPosition = RVec3::sZero(); - outRotation = Quat::sIdentity(); - } -} - -AABox Ragdoll::GetWorldSpaceBounds(bool inLockBodies) const -{ - // Lock the bodies - int body_count = (int)mBodyIDs.size(); - BodyLockMultiRead lock(sGetBodyLockInterface(mSystem, inLockBodies), mBodyIDs.data(), body_count); - - // Encapsulate all bodies - AABox bounds; - for (int b = 0; b < body_count; ++b) - { - const Body *body = lock.GetBody(b); - if (body != nullptr) - bounds.Encapsulate(body->GetWorldSpaceBounds()); - } - return bounds; -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Ragdoll/Ragdoll.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Ragdoll/Ragdoll.h deleted file mode 100644 index c31586f2562..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Ragdoll/Ragdoll.h +++ /dev/null @@ -1,240 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -class Ragdoll; -class PhysicsSystem; - -/// Contains the structure of a ragdoll -class JPH_EXPORT RagdollSettings : public RefTarget -{ -public: - JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, RagdollSettings) - - /// Stabilize the constraints of the ragdoll - /// @return True on success, false on failure. - bool Stabilize(); - - /// After the ragdoll has been fully configured, call this function to automatically create and add a GroupFilterTable collision filter to all bodies - /// and configure them so that parent and children don't collide. - /// - /// This will: - /// - Create a GroupFilterTable and assign it to all of the bodies in a ragdoll. - /// - Each body in your ragdoll will get a SubGroupID that is equal to the joint index in the Skeleton that it is attached to. - /// - Loop over all joints in the Skeleton and call GroupFilterTable::DisableCollision(joint index, parent joint index). - /// - When a pose is provided through inJointMatrices the function will detect collisions between joints - /// (they must be separated by more than inMinSeparationDistance to be treated as not colliding) and automatically disable collisions. - /// - /// When you create an instance using Ragdoll::CreateRagdoll pass in a unique GroupID for each ragdoll (e.g. a simple counter), note that this number - /// should be unique throughout the PhysicsSystem, so if you have different types of ragdolls they should not share the same GroupID. - void DisableParentChildCollisions(const Mat44 *inJointMatrices = nullptr, float inMinSeparationDistance = 0.0f); - - /// Saves the state of this object in binary form to inStream. - /// @param inStream The stream to save the state to - /// @param inSaveShapes If the shapes should be saved as well (these could be shared between ragdolls, in which case the calling application may want to write custom code to restore them) - /// @param inSaveGroupFilter If the group filter should be saved as well (these could be shared) - void SaveBinaryState(StreamOut &inStream, bool inSaveShapes, bool inSaveGroupFilter) const; - - using RagdollResult = Result>; - - /// Restore a saved ragdoll from inStream - static RagdollResult sRestoreFromBinaryState(StreamIn &inStream); - - /// Create ragdoll instance from these settings - /// @return Newly created ragdoll or null when out of bodies - Ragdoll * CreateRagdoll(CollisionGroup::GroupID inCollisionGroup, uint64 inUserData, PhysicsSystem *inSystem) const; - - /// Access to the skeleton of this ragdoll - const Skeleton * GetSkeleton() const { return mSkeleton; } - Skeleton * GetSkeleton() { return mSkeleton; } - - /// Calculate the map needed for GetBodyIndexToConstraintIndex() - void CalculateBodyIndexToConstraintIndex(); - - /// Get table that maps a body index to the constraint index with which it is connected to its parent. -1 if there is no constraint associated with the body. - /// Note that this will only tell you which constraint connects the body to its parent, it will not look in the additional constraint list. - const Array & GetBodyIndexToConstraintIndex() const { return mBodyIndexToConstraintIndex; } - - /// Map a single body index to a constraint index - int GetConstraintIndexForBodyIndex(int inBodyIndex) const { return mBodyIndexToConstraintIndex[inBodyIndex]; } - - /// Calculate the map needed for GetConstraintIndexToBodyIdxPair() - void CalculateConstraintIndexToBodyIdxPair(); - - using BodyIdxPair = pair; - - /// Table that maps a constraint index (index in mConstraints) to the indices of the bodies that the constraint is connected to (index in mBodyIDs) - const Array & GetConstraintIndexToBodyIdxPair() const { return mConstraintIndexToBodyIdxPair; } - - /// Map a single constraint index (index in mConstraints) to the indices of the bodies that the constraint is connected to (index in mBodyIDs) - BodyIdxPair GetBodyIndicesForConstraintIndex(int inConstraintIndex) const { return mConstraintIndexToBodyIdxPair[inConstraintIndex]; } - - /// A single rigid body sub part of the ragdoll - class Part : public BodyCreationSettings - { - public: - JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, Part) - - Ref mToParent; - }; - - /// List of ragdoll parts - using PartVector = Array; ///< The constraint that connects this part to its parent part (should be null for the root) - - /// A constraint that connects two bodies in a ragdoll (for non parent child related constraints) - class AdditionalConstraint - { - public: - JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, AdditionalConstraint) - - /// Constructors - AdditionalConstraint() = default; - AdditionalConstraint(int inBodyIdx1, int inBodyIdx2, TwoBodyConstraintSettings *inConstraint) : mBodyIdx { inBodyIdx1, inBodyIdx2 }, mConstraint(inConstraint) { } - - int mBodyIdx[2]; ///< Indices of the bodies that this constraint connects - Ref mConstraint; ///< The constraint that connects these bodies - }; - - /// List of additional constraints - using AdditionalConstraintVector = Array; - - /// The skeleton for this ragdoll - Ref mSkeleton; - - /// For each of the joints, the body and constraint attaching it to its parent body (1-on-1 with mSkeleton.GetJoints()) - PartVector mParts; - - /// A list of constraints that connects two bodies in a ragdoll (for non parent child related constraints) - AdditionalConstraintVector mAdditionalConstraints; - -private: - /// Table that maps a body index (index in mBodyIDs) to the constraint index with which it is connected to its parent. -1 if there is no constraint associated with the body. - Array mBodyIndexToConstraintIndex; - - /// Table that maps a constraint index (index in mConstraints) to the indices of the bodies that the constraint is connected to (index in mBodyIDs) - Array mConstraintIndexToBodyIdxPair; -}; - -/// Runtime ragdoll information -class JPH_EXPORT Ragdoll : public RefTarget, public NonCopyable -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Constructor - explicit Ragdoll(PhysicsSystem *inSystem) : mSystem(inSystem) { } - - /// Destructor - ~Ragdoll(); - - /// Add bodies and constraints to the system and optionally activate the bodies - void AddToPhysicsSystem(EActivation inActivationMode, bool inLockBodies = true); - - /// Remove bodies and constraints from the system - void RemoveFromPhysicsSystem(bool inLockBodies = true); - - /// Wake up all bodies in the ragdoll - void Activate(bool inLockBodies = true); - - /// Check if one or more of the bodies in the ragdoll are active. - /// Note that this involves locking the bodies (if inLockBodies is true) and looping over them. An alternative and possibly faster - /// way could be to install a BodyActivationListener and count the number of active bodies of a ragdoll as they're activated / deactivated - /// (basically check if the body that activates / deactivates is in GetBodyIDs() and increment / decrement a counter). - bool IsActive(bool inLockBodies = true) const; - - /// Set the group ID on all bodies in the ragdoll - void SetGroupID(CollisionGroup::GroupID inGroupID, bool inLockBodies = true); - - /// Set the ragdoll to a pose (calls BodyInterface::SetPositionAndRotation to instantly move the ragdoll) - void SetPose(const SkeletonPose &inPose, bool inLockBodies = true); - - /// Lower level version of SetPose that directly takes the world space joint matrices - void SetPose(RVec3Arg inRootOffset, const Mat44 *inJointMatrices, bool inLockBodies = true); - - /// Get the ragdoll pose (uses the world transform of the bodies to calculate the pose) - void GetPose(SkeletonPose &outPose, bool inLockBodies = true); - - /// Lower level version of GetPose that directly returns the world space joint matrices - void GetPose(RVec3 &outRootOffset, Mat44 *outJointMatrices, bool inLockBodies = true); - - /// This function calls ResetWarmStart on all constraints. It can be used after calling SetPose to reset previous frames impulses. See: Constraint::ResetWarmStart. - void ResetWarmStart(); - - /// Drive the ragdoll to a specific pose by setting velocities on each of the bodies so that it will reach inPose in inDeltaTime - void DriveToPoseUsingKinematics(const SkeletonPose &inPose, float inDeltaTime, bool inLockBodies = true); - - /// Lower level version of DriveToPoseUsingKinematics that directly takes the world space joint matrices - void DriveToPoseUsingKinematics(RVec3Arg inRootOffset, const Mat44 *inJointMatrices, float inDeltaTime, bool inLockBodies = true); - - /// Drive the ragdoll to a specific pose by activating the motors on each constraint - void DriveToPoseUsingMotors(const SkeletonPose &inPose); - - /// Control the linear and velocity of all bodies in the ragdoll - void SetLinearAndAngularVelocity(Vec3Arg inLinearVelocity, Vec3Arg inAngularVelocity, bool inLockBodies = true); - - /// Set the world space linear velocity of all bodies in the ragdoll. - void SetLinearVelocity(Vec3Arg inLinearVelocity, bool inLockBodies = true); - - /// Add a world space velocity (in m/s) to all bodies in the ragdoll. - void AddLinearVelocity(Vec3Arg inLinearVelocity, bool inLockBodies = true); - - /// Add impulse to all bodies of the ragdoll (center of mass of each of them) - void AddImpulse(Vec3Arg inImpulse, bool inLockBodies = true); - - /// Get the position and orientation of the root of the ragdoll - void GetRootTransform(RVec3 &outPosition, Quat &outRotation, bool inLockBodies = true) const; - - /// Get number of bodies in the ragdoll - size_t GetBodyCount() const { return mBodyIDs.size(); } - - /// Access a body ID - BodyID GetBodyID(int inBodyIndex) const { return mBodyIDs[inBodyIndex]; } - - /// Access to the array of body IDs - const Array & GetBodyIDs() const { return mBodyIDs; } - - /// Get number of constraints in the ragdoll - size_t GetConstraintCount() const { return mConstraints.size(); } - - /// Access a constraint by index - TwoBodyConstraint * GetConstraint(int inConstraintIndex) { return mConstraints[inConstraintIndex]; } - - /// Access a constraint by index - const TwoBodyConstraint * GetConstraint(int inConstraintIndex) const { return mConstraints[inConstraintIndex]; } - - /// Get world space bounding box for all bodies of the ragdoll - AABox GetWorldSpaceBounds(bool inLockBodies = true) const; - - /// Get the settings object that created this ragdoll - const RagdollSettings * GetRagdollSettings() const { return mRagdollSettings; } - -private: - /// For RagdollSettings::CreateRagdoll function - friend class RagdollSettings; - - /// The settings that created this ragdoll - RefConst mRagdollSettings; - - /// The bodies and constraints that this ragdoll consists of (1-on-1 with mRagdollSettings->mParts) - Array mBodyIDs; - - /// Array of constraints that connect the bodies together - Array> mConstraints; - - /// Cached physics system - PhysicsSystem * mSystem; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyContactListener.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyContactListener.h deleted file mode 100644 index 27e185375f3..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyContactListener.h +++ /dev/null @@ -1,55 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2024 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -JPH_NAMESPACE_BEGIN - -class Body; -class SoftBodyManifold; - -/// Return value for the OnSoftBodyContactValidate callback. Determines if the contact will be processed or not. -enum class SoftBodyValidateResult -{ - AcceptContact, ///< Accept this contact - RejectContact, ///< Reject this contact -}; - -/// Contact settings for a soft body contact. -/// The values are filled in with their defaults by the system so the callback doesn't need to modify anything, but it can if it wants to. -class SoftBodyContactSettings -{ -public: - float mInvMassScale1 = 1.0f; ///< Scale factor for the inverse mass of the soft body (0 = infinite mass, 1 = use original mass, 2 = body has half the mass). For the same contact pair, you should strive to keep the value the same over time. - float mInvMassScale2 = 1.0f; ///< Scale factor for the inverse mass of the other body (0 = infinite mass, 1 = use original mass, 2 = body has half the mass). For the same contact pair, you should strive to keep the value the same over time. - float mInvInertiaScale2 = 1.0f; ///< Scale factor for the inverse inertia of the other body (usually same as mInvMassScale2) - bool mIsSensor; ///< If the contact should be treated as a sensor vs body contact (no collision response) -}; - -/// A listener class that receives collision contact events for soft bodies against rigid bodies. -/// It can be registered with the PhysicsSystem. -class SoftBodyContactListener -{ -public: - /// Ensure virtual destructor - virtual ~SoftBodyContactListener() = default; - - /// Called whenever the soft body's aabox overlaps with another body's aabox (so receiving this callback doesn't tell if any of the vertices will collide). - /// This callback can be used to change the behavior of the collision response for all vertices in the soft body or to completely reject the contact. - /// Note that this callback is called when all bodies are locked, so don't use any locking functions! - /// @param inSoftBody The soft body that collided. It is safe to access this as the soft body is only updated on the current thread. - /// @param inOtherBody The other body that collided. Note that accessing the position/orientation/velocity of inOtherBody may result in a race condition as other threads may be modifying the body at the same time. - /// @param ioSettings The settings for all contact points that are generated by this collision. - /// @return Whether the contact should be processed or not. - virtual SoftBodyValidateResult OnSoftBodyContactValidate([[maybe_unused]] const Body &inSoftBody, [[maybe_unused]] const Body &inOtherBody, [[maybe_unused]] SoftBodyContactSettings &ioSettings) { return SoftBodyValidateResult::AcceptContact; } - - /// Called after all contact points for a soft body have been handled. You only receive one callback per body pair per simulation step and can use inManifold to iterate through all contacts. - /// Note that this callback is called when all bodies are locked, so don't use any locking functions! - /// You will receive a single callback for a soft body per simulation step for performance reasons, this callback will apply to all vertices in the soft body. - /// @param inSoftBody The soft body that collided. It is safe to access this as the soft body is only updated on the current thread. - /// @param inManifold The manifold that describes the contact surface between the two bodies. Other bodies may be modified by other threads during this callback. - virtual void OnSoftBodyContactAdded([[maybe_unused]] const Body &inSoftBody, const SoftBodyManifold &inManifold) { /* Do nothing */ } -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyCreationSettings.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyCreationSettings.cpp deleted file mode 100644 index 3ae026df64a..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyCreationSettings.cpp +++ /dev/null @@ -1,122 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2023 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SoftBodyCreationSettings) -{ - JPH_ADD_ATTRIBUTE(SoftBodyCreationSettings, mSettings) - JPH_ADD_ATTRIBUTE(SoftBodyCreationSettings, mPosition) - JPH_ADD_ATTRIBUTE(SoftBodyCreationSettings, mRotation) - JPH_ADD_ATTRIBUTE(SoftBodyCreationSettings, mUserData) - JPH_ADD_ENUM_ATTRIBUTE(SoftBodyCreationSettings, mObjectLayer) - JPH_ADD_ATTRIBUTE(SoftBodyCreationSettings, mCollisionGroup) - JPH_ADD_ATTRIBUTE(SoftBodyCreationSettings, mNumIterations) - JPH_ADD_ATTRIBUTE(SoftBodyCreationSettings, mLinearDamping) - JPH_ADD_ATTRIBUTE(SoftBodyCreationSettings, mMaxLinearVelocity) - JPH_ADD_ATTRIBUTE(SoftBodyCreationSettings, mRestitution) - JPH_ADD_ATTRIBUTE(SoftBodyCreationSettings, mFriction) - JPH_ADD_ATTRIBUTE(SoftBodyCreationSettings, mPressure) - JPH_ADD_ATTRIBUTE(SoftBodyCreationSettings, mGravityFactor) - JPH_ADD_ATTRIBUTE(SoftBodyCreationSettings, mUpdatePosition) - JPH_ADD_ATTRIBUTE(SoftBodyCreationSettings, mMakeRotationIdentity) - JPH_ADD_ATTRIBUTE(SoftBodyCreationSettings, mAllowSleeping) -} - -void SoftBodyCreationSettings::SaveBinaryState(StreamOut &inStream) const -{ - inStream.Write(mPosition); - inStream.Write(mRotation); - inStream.Write(mUserData); - inStream.Write(mObjectLayer); - mCollisionGroup.SaveBinaryState(inStream); - inStream.Write(mNumIterations); - inStream.Write(mLinearDamping); - inStream.Write(mMaxLinearVelocity); - inStream.Write(mRestitution); - inStream.Write(mFriction); - inStream.Write(mPressure); - inStream.Write(mGravityFactor); - inStream.Write(mUpdatePosition); - inStream.Write(mMakeRotationIdentity); - inStream.Write(mAllowSleeping); -} - -void SoftBodyCreationSettings::RestoreBinaryState(StreamIn &inStream) -{ - inStream.Read(mPosition); - inStream.Read(mRotation); - inStream.Read(mUserData); - inStream.Read(mObjectLayer); - mCollisionGroup.RestoreBinaryState(inStream); - inStream.Read(mNumIterations); - inStream.Read(mLinearDamping); - inStream.Read(mMaxLinearVelocity); - inStream.Read(mRestitution); - inStream.Read(mFriction); - inStream.Read(mPressure); - inStream.Read(mGravityFactor); - inStream.Read(mUpdatePosition); - inStream.Read(mMakeRotationIdentity); - inStream.Read(mAllowSleeping); -} - -void SoftBodyCreationSettings::SaveWithChildren(StreamOut &inStream, SharedSettingsToIDMap *ioSharedSettingsMap, MaterialToIDMap *ioMaterialMap, GroupFilterToIDMap *ioGroupFilterMap) const -{ - // Save creation settings - SaveBinaryState(inStream); - - // Save shared settings - if (ioSharedSettingsMap != nullptr && ioMaterialMap != nullptr) - mSettings->SaveWithMaterials(inStream, *ioSharedSettingsMap, *ioMaterialMap); - else - inStream.Write(~uint32(0)); - - // Save group filter - StreamUtils::SaveObjectReference(inStream, mCollisionGroup.GetGroupFilter(), ioGroupFilterMap); -} - -SoftBodyCreationSettings::SBCSResult SoftBodyCreationSettings::sRestoreWithChildren(StreamIn &inStream, IDToSharedSettingsMap &ioSharedSettingsMap, IDToMaterialMap &ioMaterialMap, IDToGroupFilterMap &ioGroupFilterMap) -{ - SBCSResult result; - - // Read creation settings - SoftBodyCreationSettings settings; - settings.RestoreBinaryState(inStream); - if (inStream.IsEOF() || inStream.IsFailed()) - { - result.SetError("Error reading body creation settings"); - return result; - } - - // Read shared settings - SoftBodySharedSettings::SettingsResult settings_result = SoftBodySharedSettings::sRestoreWithMaterials(inStream, ioSharedSettingsMap, ioMaterialMap); - if (settings_result.HasError()) - { - result.SetError(settings_result.GetError()); - return result; - } - settings.mSettings = settings_result.Get(); - - // Read group filter - Result gfresult = StreamUtils::RestoreObjectReference(inStream, ioGroupFilterMap); - if (gfresult.HasError()) - { - result.SetError(gfresult.GetError()); - return result; - } - settings.mCollisionGroup.SetGroupFilter(gfresult.Get()); - - result.Set(settings); - return result; -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyCreationSettings.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyCreationSettings.h deleted file mode 100644 index 9d153290e80..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyCreationSettings.h +++ /dev/null @@ -1,73 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2023 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -/// This class contains the information needed to create a soft body object -/// Note: Soft bodies are still in development and come with several caveats. Read the Architecture and API documentation for more information! -class JPH_EXPORT SoftBodyCreationSettings -{ -public: - JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, SoftBodyCreationSettings) - - /// Constructor - SoftBodyCreationSettings() = default; - SoftBodyCreationSettings(const SoftBodySharedSettings *inSettings, RVec3Arg inPosition, QuatArg inRotation, ObjectLayer inObjectLayer) : mSettings(inSettings), mPosition(inPosition), mRotation(inRotation), mObjectLayer(inObjectLayer) { } - - /// Saves the state of this object in binary form to inStream. Doesn't store the shared settings nor the group filter. - void SaveBinaryState(StreamOut &inStream) const; - - /// Restore the state of this object from inStream. Doesn't restore the shared settings nor the group filter. - void RestoreBinaryState(StreamIn &inStream); - - using GroupFilterToIDMap = StreamUtils::ObjectToIDMap; - using IDToGroupFilterMap = StreamUtils::IDToObjectMap; - using SharedSettingsToIDMap = SoftBodySharedSettings::SharedSettingsToIDMap; - using IDToSharedSettingsMap = SoftBodySharedSettings::IDToSharedSettingsMap; - using MaterialToIDMap = StreamUtils::ObjectToIDMap; - using IDToMaterialMap = StreamUtils::IDToObjectMap; - - /// Save this body creation settings, its shared settings and group filter. Pass in an empty map in ioSharedSettingsMap / ioMaterialMap / ioGroupFilterMap or reuse the same map while saving multiple shapes to the same stream in order to avoid writing duplicates. - /// Pass nullptr to ioSharedSettingsMap and ioMaterial map to skip saving shared settings and materials - /// Pass nullptr to ioGroupFilterMap to skip saving group filters - void SaveWithChildren(StreamOut &inStream, SharedSettingsToIDMap *ioSharedSettingsMap, MaterialToIDMap *ioMaterialMap, GroupFilterToIDMap *ioGroupFilterMap) const; - - using SBCSResult = Result; - - /// Restore a shape, all its children and materials. Pass in an empty map in ioSharedSettingsMap / ioMaterialMap / ioGroupFilterMap or reuse the same map while reading multiple shapes from the same stream in order to restore duplicates. - static SBCSResult sRestoreWithChildren(StreamIn &inStream, IDToSharedSettingsMap &ioSharedSettingsMap, IDToMaterialMap &ioMaterialMap, IDToGroupFilterMap &ioGroupFilterMap); - - RefConst mSettings; ///< Defines the configuration of this soft body - - RVec3 mPosition { RVec3::sZero() }; ///< Initial position of the soft body - Quat mRotation { Quat::sIdentity() }; ///< Initial rotation of the soft body - - /// User data value (can be used by application) - uint64 mUserData = 0; - - ///@name Collision settings - ObjectLayer mObjectLayer = 0; ///< The collision layer this body belongs to (determines if two objects can collide) - CollisionGroup mCollisionGroup; ///< The collision group this body belongs to (determines if two objects can collide) - - uint32 mNumIterations = 5; ///< Number of solver iterations - float mLinearDamping = 0.1f; ///< Linear damping: dv/dt = -mLinearDamping * v - float mMaxLinearVelocity = 500.0f; ///< Maximum linear velocity that a vertex can reach (m/s) - float mRestitution = 0.0f; ///< Restitution when colliding - float mFriction = 0.2f; ///< Friction coefficient when colliding - float mPressure = 0.0f; ///< n * R * T, amount of substance * ideal gas constant * absolute temperature, see https://en.wikipedia.org/wiki/Pressure - float mGravityFactor = 1.0f; ///< Value to multiply gravity with for this body - bool mUpdatePosition = true; ///< Update the position of the body while simulating (set to false for something that is attached to the static world) - bool mMakeRotationIdentity = true; ///< Bake specified mRotation in the vertices and set the body rotation to identity (simulation is slightly more accurate if the rotation of a soft body is kept to identity) - bool mAllowSleeping = true; ///< If this body can go to sleep or not -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyManifold.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyManifold.h deleted file mode 100644 index ea3a65b3bf3..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyManifold.h +++ /dev/null @@ -1,59 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2024 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -/// An interface to query which vertices of a soft body are colliding with other bodies -class SoftBodyManifold -{ -public: - /// Get the vertices of the soft body for iterating - const Array & GetVertices() const { return mVertices; } - - /// Check if a vertex has collided with something in this update - JPH_INLINE bool HasContact(const SoftBodyVertex &inVertex) const - { - return inVertex.mHasContact; - } - - /// Get the local space contact point (multiply by GetCenterOfMassTransform() of the soft body to get world space) - JPH_INLINE Vec3 GetLocalContactPoint(const SoftBodyVertex &inVertex) const - { - return inVertex.mPosition - inVertex.mCollisionPlane.SignedDistance(inVertex.mPosition) * inVertex.mCollisionPlane.GetNormal(); - } - - /// Get the contact normal for the vertex (assumes there is a contact). - JPH_INLINE Vec3 GetContactNormal(const SoftBodyVertex &inVertex) const - { - return -inVertex.mCollisionPlane.GetNormal(); - } - - /// Get the body with which the vertex has collided in this update - JPH_INLINE BodyID GetContactBodyID(const SoftBodyVertex &inVertex) const - { - return inVertex.mHasContact? mCollidingShapes[inVertex.mCollidingShapeIndex].mBodyID : BodyID(); - } - -private: - /// Allow SoftBodyMotionProperties to construct us - friend class SoftBodyMotionProperties; - - /// Constructor - explicit SoftBodyManifold(const SoftBodyMotionProperties *inMotionProperties) : - mVertices(inMotionProperties->mVertices), - mCollidingShapes(inMotionProperties->mCollidingShapes) - { - } - - using CollidingShape = SoftBodyMotionProperties::CollidingShape; - - const Array & mVertices; - const Array & mCollidingShapes; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyMotionProperties.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyMotionProperties.cpp deleted file mode 100644 index 31b068455b9..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyMotionProperties.cpp +++ /dev/null @@ -1,1089 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2023 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include -#include -#include -#ifdef JPH_DEBUG_RENDERER - #include -#endif // JPH_DEBUG_RENDERER - -JPH_NAMESPACE_BEGIN - -using namespace JPH::literals; - -void SoftBodyMotionProperties::CalculateMassAndInertia() -{ - MassProperties mp; - - for (const Vertex &v : mVertices) - if (v.mInvMass > 0.0f) - { - Vec3 pos = v.mPosition; - - // Accumulate mass - float mass = 1.0f / v.mInvMass; - mp.mMass += mass; - - // Inertia tensor, diagonal - // See equations https://en.wikipedia.org/wiki/Moment_of_inertia section 'Inertia Tensor' - for (int i = 0; i < 3; ++i) - mp.mInertia(i, i) += mass * (Square(pos[(i + 1) % 3]) + Square(pos[(i + 2) % 3])); - - // Inertia tensor off diagonal - for (int i = 0; i < 3; ++i) - for (int j = 0; j < 3; ++j) - if (i != j) - mp.mInertia(i, j) -= mass * pos[i] * pos[j]; - } - else - { - // If one vertex is kinematic, the entire body will have infinite mass and inertia - SetInverseMass(0.0f); - SetInverseInertia(Vec3::sZero(), Quat::sIdentity()); - return; - } - - SetMassProperties(EAllowedDOFs::All, mp); -} - -void SoftBodyMotionProperties::Initialize(const SoftBodyCreationSettings &inSettings) -{ - // Store settings - mSettings = inSettings.mSettings; - mNumIterations = inSettings.mNumIterations; - mPressure = inSettings.mPressure; - mUpdatePosition = inSettings.mUpdatePosition; - - // Initialize vertices - mVertices.resize(inSettings.mSettings->mVertices.size()); - Mat44 rotation = inSettings.mMakeRotationIdentity? Mat44::sRotation(inSettings.mRotation) : Mat44::sIdentity(); - for (Array::size_type v = 0, s = mVertices.size(); v < s; ++v) - { - const SoftBodySharedSettings::Vertex &in_vertex = inSettings.mSettings->mVertices[v]; - Vertex &out_vertex = mVertices[v]; - out_vertex.mPreviousPosition = out_vertex.mPosition = rotation * Vec3(in_vertex.mPosition); - out_vertex.mVelocity = rotation.Multiply3x3(Vec3(in_vertex.mVelocity)); - out_vertex.mCollidingShapeIndex = -1; - out_vertex.mHasContact = false; - out_vertex.mLargestPenetration = -FLT_MAX; - out_vertex.mInvMass = in_vertex.mInvMass; - mLocalBounds.Encapsulate(out_vertex.mPosition); - } - - // Allocate space for skinned vertices - if (!inSettings.mSettings->mSkinnedConstraints.empty()) - mSkinState.resize(mVertices.size()); - - // We don't know delta time yet, so we can't predict the bounds and use the local bounds as the predicted bounds - mLocalPredictedBounds = mLocalBounds; - - CalculateMassAndInertia(); -} - -float SoftBodyMotionProperties::GetVolumeTimesSix() const -{ - float six_volume = 0.0f; - for (const Face &f : mSettings->mFaces) - { - Vec3 x1 = mVertices[f.mVertex[0]].mPosition; - Vec3 x2 = mVertices[f.mVertex[1]].mPosition; - Vec3 x3 = mVertices[f.mVertex[2]].mPosition; - six_volume += x1.Cross(x2).Dot(x3); // We pick zero as the origin as this is the center of the bounding box so should give good accuracy - } - return six_volume; -} - -void SoftBodyMotionProperties::DetermineCollidingShapes(const SoftBodyUpdateContext &inContext, const PhysicsSystem &inSystem, const BodyLockInterface &inBodyLockInterface) -{ - JPH_PROFILE_FUNCTION(); - - struct Collector : public CollideShapeBodyCollector - { - Collector(const SoftBodyUpdateContext &inContext, const PhysicsSystem &inSystem, const BodyLockInterface &inBodyLockInterface, Array &ioHits) : - mContext(inContext), - mInverseTransform(inContext.mCenterOfMassTransform.InversedRotationTranslation()), - mBodyLockInterface(inBodyLockInterface), - mCombineFriction(inSystem.GetCombineFriction()), - mCombineRestitution(inSystem.GetCombineRestitution()), - mHits(ioHits) - { - } - - virtual void AddHit(const BodyID &inResult) override - { - BodyLockRead lock(mBodyLockInterface, inResult); - if (lock.Succeeded()) - { - const Body &soft_body = *mContext.mBody; - const Body &body = lock.GetBody(); - if (body.IsRigidBody() // TODO: We should support soft body vs soft body - && soft_body.GetCollisionGroup().CanCollide(body.GetCollisionGroup())) - { - // Call the contact listener to see if we should accept this contact - // If there is no contact listener then we can ignore the contact if the other body is a sensor - SoftBodyContactSettings settings; - settings.mIsSensor = body.IsSensor(); - if (mContext.mContactListener == nullptr? !settings.mIsSensor : mContext.mContactListener->OnSoftBodyContactValidate(soft_body, body, settings) == SoftBodyValidateResult::AcceptContact) - { - CollidingShape cs; - cs.mCenterOfMassTransform = (mInverseTransform * body.GetCenterOfMassTransform()).ToMat44(); - cs.mShape = body.GetShape(); - cs.mBodyID = inResult; - cs.mMotionType = body.GetMotionType(); - cs.mIsSensor = settings.mIsSensor; - cs.mUpdateVelocities = false; - cs.mFriction = mCombineFriction(soft_body, SubShapeID(), body, SubShapeID()); - cs.mRestitution = mCombineRestitution(soft_body, SubShapeID(), body, SubShapeID()); - if (cs.mMotionType == EMotionType::Dynamic) - { - const MotionProperties *mp = body.GetMotionProperties(); - cs.mInvMass = settings.mInvMassScale2 * mp->GetInverseMass(); - cs.mInvInertia = settings.mInvInertiaScale2 * mp->GetInverseInertiaForRotation(cs.mCenterOfMassTransform.GetRotation()); - cs.mSoftBodyInvMassScale = settings.mInvMassScale1; - cs.mOriginalLinearVelocity = cs.mLinearVelocity = mInverseTransform.Multiply3x3(mp->GetLinearVelocity()); - cs.mOriginalAngularVelocity = cs.mAngularVelocity = mInverseTransform.Multiply3x3(mp->GetAngularVelocity()); - } - mHits.push_back(cs); - } - } - } - } - - private: - const SoftBodyUpdateContext &mContext; - RMat44 mInverseTransform; - const BodyLockInterface & mBodyLockInterface; - ContactConstraintManager::CombineFunction mCombineFriction; - ContactConstraintManager::CombineFunction mCombineRestitution; - Array & mHits; - }; - - Collector collector(inContext, inSystem, inBodyLockInterface, mCollidingShapes); - AABox bounds = mLocalBounds; - bounds.Encapsulate(mLocalPredictedBounds); - bounds = bounds.Transformed(inContext.mCenterOfMassTransform); - bounds.ExpandBy(Vec3::sReplicate(mSettings->mVertexRadius)); - ObjectLayer layer = inContext.mBody->GetObjectLayer(); - DefaultBroadPhaseLayerFilter broadphase_layer_filter = inSystem.GetDefaultBroadPhaseLayerFilter(layer); - DefaultObjectLayerFilter object_layer_filter = inSystem.GetDefaultLayerFilter(layer); - inSystem.GetBroadPhaseQuery().CollideAABox(bounds, collector, broadphase_layer_filter, object_layer_filter); -} - -void SoftBodyMotionProperties::DetermineCollisionPlanes(const SoftBodyUpdateContext &inContext, uint inVertexStart, uint inNumVertices) -{ - JPH_PROFILE_FUNCTION(); - - // Generate collision planes - for (const CollidingShape &cs : mCollidingShapes) - cs.mShape->CollideSoftBodyVertices(cs.mCenterOfMassTransform, Vec3::sReplicate(1.0f), mVertices.data() + inVertexStart, inNumVertices, inContext.mDeltaTime, inContext.mDisplacementDueToGravity, int(&cs - mCollidingShapes.data())); -} - -void SoftBodyMotionProperties::ApplyPressure(const SoftBodyUpdateContext &inContext) -{ - JPH_PROFILE_FUNCTION(); - - float dt = inContext.mSubStepDeltaTime; - float pressure_coefficient = mPressure; - if (pressure_coefficient > 0.0f) - { - // Calculate total volume - float six_volume = GetVolumeTimesSix(); - if (six_volume > 0.0f) - { - // Apply pressure - // p = F / A = n R T / V (see https://en.wikipedia.org/wiki/Pressure) - // Our pressure coefficient is n R T so the impulse is: - // P = F dt = pressure_coefficient / V * A * dt - float coefficient = pressure_coefficient * dt / six_volume; // Need to still multiply by 6 for the volume - for (const Face &f : mSettings->mFaces) - { - Vec3 x1 = mVertices[f.mVertex[0]].mPosition; - Vec3 x2 = mVertices[f.mVertex[1]].mPosition; - Vec3 x3 = mVertices[f.mVertex[2]].mPosition; - - Vec3 impulse = coefficient * (x2 - x1).Cross(x3 - x1); // Area is half the cross product so need to still divide by 2 - for (uint32 i : f.mVertex) - { - Vertex &v = mVertices[i]; - v.mVelocity += v.mInvMass * impulse; // Want to divide by 3 because we spread over 3 vertices - } - } - } - } -} - -void SoftBodyMotionProperties::IntegratePositions(const SoftBodyUpdateContext &inContext) -{ - JPH_PROFILE_FUNCTION(); - - float dt = inContext.mSubStepDeltaTime; - float linear_damping = max(0.0f, 1.0f - GetLinearDamping() * dt); // See: MotionProperties::ApplyForceTorqueAndDragInternal - - // Integrate - Vec3 sub_step_gravity = inContext.mGravity * dt; - for (Vertex &v : mVertices) - if (v.mInvMass > 0.0f) - { - // Gravity - v.mVelocity += sub_step_gravity; - - // Damping - v.mVelocity *= linear_damping; - - // Integrate - v.mPreviousPosition = v.mPosition; - v.mPosition += v.mVelocity * dt; - } - else - { - // Integrate - v.mPreviousPosition = v.mPosition; - v.mPosition += v.mVelocity * dt; - } -} - -void SoftBodyMotionProperties::ApplyBendConstraints(const SoftBodyUpdateContext &inContext) -{ - JPH_PROFILE_FUNCTION(); - - float inv_dt_sq = 1.0f / Square(inContext.mSubStepDeltaTime); - - for (const DihedralBend &b : mSettings->mDihedralBendConstraints) - { - Vertex &v0 = mVertices[b.mVertex[0]]; - Vertex &v1 = mVertices[b.mVertex[1]]; - Vertex &v2 = mVertices[b.mVertex[2]]; - Vertex &v3 = mVertices[b.mVertex[3]]; - - // Get positions - Vec3 x0 = v0.mPosition; - Vec3 x1 = v1.mPosition; - Vec3 x2 = v2.mPosition; - Vec3 x3 = v3.mPosition; - - /* - x2 - e1/ \e3 - / \ - x0----x1 - \ e0 / - e2\ /e4 - x3 - */ - - // Calculate the shared edge of the triangles - Vec3 e = x1 - x0; - float e_len = e.Length(); - if (e_len < 1.0e-6f) - continue; - - // Calculate the normals of the triangles - Vec3 x1x2 = x2 - x1; - Vec3 x1x3 = x3 - x1; - Vec3 n1 = (x2 - x0).Cross(x1x2); - Vec3 n2 = x1x3.Cross(x3 - x0); - float n1_len_sq = n1.LengthSq(); - float n2_len_sq = n2.LengthSq(); - float n1_len_sq_n2_len_sq = n1_len_sq * n2_len_sq; - if (n1_len_sq_n2_len_sq < 1.0e-24f) - continue; - - // Calculate constraint equation - // As per "Strain Based Dynamics" Appendix A we need to negate the gradients when (n1 x n2) . e > 0, instead we make sure that the sign of the constraint equation is correct - float sign = Sign(n2.Cross(n1).Dot(e)); - float d = n1.Dot(n2) / sqrt(n1_len_sq_n2_len_sq); - float c = sign * ACos(d) - b.mInitialAngle; - - // Ensure the range is -PI to PI - if (c > JPH_PI) - c -= 2.0f * JPH_PI; - else if (c < -JPH_PI) - c += 2.0f * JPH_PI; - - // Calculate gradient of constraint equation - // Taken from "Strain Based Dynamics" - Matthias Muller et al. (Appendix A) - // with p1 = x2, p2 = x3, p3 = x0 and p4 = x1 - // which in turn is based on "Simulation of Clothing with Folds and Wrinkles" - R. Bridson et al. (Section 4) - n1 /= n1_len_sq; - n2 /= n2_len_sq; - Vec3 d0c = (x1x2.Dot(e) * n1 + x1x3.Dot(e) * n2) / e_len; - Vec3 d2c = e_len * n1; - Vec3 d3c = e_len * n2; - - // The sum of the gradients must be zero (see "Strain Based Dynamics" section 4) - Vec3 d1c = -d0c - d2c - d3c; - - // Get masses - float w0 = v0.mInvMass; - float w1 = v1.mInvMass; - float w2 = v2.mInvMass; - float w3 = v3.mInvMass; - - // Calculate -lambda - float denom = w0 * d0c.LengthSq() + w1 * d1c.LengthSq() + w2 * d2c.LengthSq() + w3 * d3c.LengthSq() + b.mCompliance * inv_dt_sq; - if (denom < 1.0e-12f) - continue; - float minus_lambda = c / denom; - - // Apply correction - v0.mPosition = x0 - minus_lambda * w0 * d0c; - v1.mPosition = x1 - minus_lambda * w1 * d1c; - v2.mPosition = x2 - minus_lambda * w2 * d2c; - v3.mPosition = x3 - minus_lambda * w3 * d3c; - } -} - -void SoftBodyMotionProperties::ApplyVolumeConstraints(const SoftBodyUpdateContext &inContext) -{ - JPH_PROFILE_FUNCTION(); - - float inv_dt_sq = 1.0f / Square(inContext.mSubStepDeltaTime); - - // Satisfy volume constraints - for (const Volume &v : mSettings->mVolumeConstraints) - { - Vertex &v1 = mVertices[v.mVertex[0]]; - Vertex &v2 = mVertices[v.mVertex[1]]; - Vertex &v3 = mVertices[v.mVertex[2]]; - Vertex &v4 = mVertices[v.mVertex[3]]; - - Vec3 x1 = v1.mPosition; - Vec3 x2 = v2.mPosition; - Vec3 x3 = v3.mPosition; - Vec3 x4 = v4.mPosition; - - // Calculate constraint equation - Vec3 x1x2 = x2 - x1; - Vec3 x1x3 = x3 - x1; - Vec3 x1x4 = x4 - x1; - float c = abs(x1x2.Cross(x1x3).Dot(x1x4)) - v.mSixRestVolume; - - // Calculate gradient of constraint equation - Vec3 d1c = (x4 - x2).Cross(x3 - x2); - Vec3 d2c = x1x3.Cross(x1x4); - Vec3 d3c = x1x4.Cross(x1x2); - Vec3 d4c = x1x2.Cross(x1x3); - - // Get masses - float w1 = v1.mInvMass; - float w2 = v2.mInvMass; - float w3 = v3.mInvMass; - float w4 = v4.mInvMass; - - // Calculate -lambda - float denom = w1 * d1c.LengthSq() + w2 * d2c.LengthSq() + w3 * d3c.LengthSq() + w4 * d4c.LengthSq() + v.mCompliance * inv_dt_sq; - if (denom < 1.0e-12f) - continue; - float minus_lambda = c / denom; - - // Apply correction - v1.mPosition = x1 - minus_lambda * w1 * d1c; - v2.mPosition = x2 - minus_lambda * w2 * d2c; - v3.mPosition = x3 - minus_lambda * w3 * d3c; - v4.mPosition = x4 - minus_lambda * w4 * d4c; - } -} - -void SoftBodyMotionProperties::ApplySkinConstraints([[maybe_unused]] const SoftBodyUpdateContext &inContext) -{ - // Early out if nothing to do - if (mSettings->mSkinnedConstraints.empty() || !mEnableSkinConstraints) - return; - - JPH_ASSERT(mSkinStateTransform == inContext.mCenterOfMassTransform, "Skinning state is stale, artifacts will show!"); - - // Apply the constraints - Vertex *vertices = mVertices.data(); - const SkinState *skin_states = mSkinState.data(); - for (const Skinned &s : mSettings->mSkinnedConstraints) - { - Vertex &vertex = vertices[s.mVertex]; - const SkinState &skin_state = skin_states[s.mVertex]; - float max_distance = s.mMaxDistance * mSkinnedMaxDistanceMultiplier; - if (max_distance > 0.0f) - { - // Move vertex if it violated the back stop - if (s.mBackStopDistance < max_distance) - { - // Center of the back stop sphere - Vec3 center = skin_state.mPosition - skin_state.mNormal * (s.mBackStopDistance + s.mBackStopRadius); - - // Check if we're inside the back stop sphere - Vec3 delta = vertex.mPosition - center; - float delta_len_sq = delta.LengthSq(); - if (delta_len_sq < Square(s.mBackStopRadius)) - { - // Push the vertex to the surface of the back stop sphere - float delta_len = sqrt(delta_len_sq); - vertex.mPosition = delta_len > 0.0f? - center + delta * (s.mBackStopRadius / delta_len) - : center + skin_state.mNormal * s.mBackStopRadius; - } - } - - // Clamp vertex distance to max distance from skinned position - if (max_distance < FLT_MAX) - { - Vec3 delta = vertex.mPosition - skin_state.mPosition; - float delta_len_sq = delta.LengthSq(); - float max_distance_sq = Square(max_distance); - if (delta_len_sq > max_distance_sq) - vertex.mPosition = skin_state.mPosition + delta * sqrt(max_distance_sq / delta_len_sq); - } - } - else - { - // Kinematic: Just update the vertex position - vertex.mPosition = skin_state.mPosition; - } - } -} - -void SoftBodyMotionProperties::ApplyEdgeConstraints(const SoftBodyUpdateContext &inContext, uint inStartIndex, uint inEndIndex) -{ - JPH_PROFILE_FUNCTION(); - - float inv_dt_sq = 1.0f / Square(inContext.mSubStepDeltaTime); - - // Satisfy edge constraints - const Array &edge_constraints = mSettings->mEdgeConstraints; - for (uint i = inStartIndex; i < inEndIndex; ++i) - { - const Edge &e = edge_constraints[i]; - Vertex &v0 = mVertices[e.mVertex[0]]; - Vertex &v1 = mVertices[e.mVertex[1]]; - - // Get positions - Vec3 x0 = v0.mPosition; - Vec3 x1 = v1.mPosition; - - // Calculate current length - Vec3 delta = x1 - x0; - float length = delta.Length(); - - // Apply correction - float denom = length * (v0.mInvMass + v1.mInvMass + e.mCompliance * inv_dt_sq); - if (denom < 1.0e-12f) - continue; - Vec3 correction = delta * (length - e.mRestLength) / denom; - v0.mPosition = x0 + v0.mInvMass * correction; - v1.mPosition = x1 - v1.mInvMass * correction; - } -} - -void SoftBodyMotionProperties::ApplyLRAConstraints() -{ - JPH_PROFILE_FUNCTION(); - - // Satisfy LRA constraints - Vertex *vertices = mVertices.data(); - for (const LRA &lra : mSettings->mLRAConstraints) - { - JPH_ASSERT(lra.mVertex[0] < mVertices.size()); - JPH_ASSERT(lra.mVertex[1] < mVertices.size()); - const Vertex &vertex0 = vertices[lra.mVertex[0]]; - Vertex &vertex1 = vertices[lra.mVertex[1]]; - - Vec3 x0 = vertex0.mPosition; - Vec3 delta = vertex1.mPosition - x0; - float delta_len_sq = delta.LengthSq(); - if (delta_len_sq > Square(lra.mMaxDistance)) - vertex1.mPosition = x0 + delta * lra.mMaxDistance / sqrt(delta_len_sq); - } -} - -void SoftBodyMotionProperties::ApplyCollisionConstraintsAndUpdateVelocities(const SoftBodyUpdateContext &inContext) -{ - JPH_PROFILE_FUNCTION(); - - float dt = inContext.mSubStepDeltaTime; - float restitution_treshold = -2.0f * inContext.mGravity.Length() * dt; - float vertex_radius = mSettings->mVertexRadius; - for (Vertex &v : mVertices) - if (v.mInvMass > 0.0f) - { - // Remember previous velocity for restitution calculations - Vec3 prev_v = v.mVelocity; - - // XPBD velocity update - v.mVelocity = (v.mPosition - v.mPreviousPosition) / dt; - - // Satisfy collision constraint - if (v.mCollidingShapeIndex >= 0) - { - // Check if there is a collision - float projected_distance = -v.mCollisionPlane.SignedDistance(v.mPosition) + vertex_radius; - if (projected_distance > 0.0f) - { - // Remember that there was a collision - v.mHasContact = true; - mHasContact = true; - - // Sensors should not have a collision response - CollidingShape &cs = mCollidingShapes[v.mCollidingShapeIndex]; - if (!cs.mIsSensor) - { - // Note that we already calculated the velocity, so this does not affect the velocity (next iteration starts by setting previous position to current position) - Vec3 contact_normal = v.mCollisionPlane.GetNormal(); - v.mPosition += contact_normal * projected_distance; - - // Apply friction as described in Detailed Rigid Body Simulation with Extended Position Based Dynamics - Matthias Muller et al. - // See section 3.6: - // Inverse mass: w1 = 1 / m1, w2 = 1 / m2 + (r2 x n)^T I^-1 (r2 x n) = 0 for a static object - // r2 are the contact point relative to the center of mass of body 2 - // Lagrange multiplier for contact: lambda = -c / (w1 + w2) - // Where c is the constraint equation (the distance to the plane, negative because penetrating) - // Contact normal force: fn = lambda / dt^2 - // Delta velocity due to friction dv = -vt / |vt| * min(dt * friction * fn * (w1 + w2), |vt|) = -vt * min(-friction * c / (|vt| * dt), 1) - // Note that I think there is an error in the paper, I added a mass term, see: https://github.com/matthias-research/pages/issues/29 - // Relative velocity: vr = v1 - v2 - omega2 x r2 - // Normal velocity: vn = vr . contact_normal - // Tangential velocity: vt = vr - contact_normal * vn - // Impulse: p = dv / (w1 + w2) - // Changes in particle velocities: - // v1 = v1 + p / m1 - // v2 = v2 - p / m2 (no change when colliding with a static body) - // w2 = w2 - I^-1 (r2 x p) (no change when colliding with a static body) - if (cs.mMotionType == EMotionType::Dynamic) - { - // Calculate normal and tangential velocity (equation 30) - Vec3 r2 = v.mPosition - cs.mCenterOfMassTransform.GetTranslation(); - Vec3 v2 = cs.GetPointVelocity(r2); - Vec3 relative_velocity = v.mVelocity - v2; - Vec3 v_normal = contact_normal * contact_normal.Dot(relative_velocity); - Vec3 v_tangential = relative_velocity - v_normal; - float v_tangential_length = v_tangential.Length(); - - // Calculate resulting inverse mass of vertex - float vertex_inv_mass = cs.mSoftBodyInvMassScale * v.mInvMass; - - // Calculate inverse effective mass - Vec3 r2_cross_n = r2.Cross(contact_normal); - float w2 = cs.mInvMass + r2_cross_n.Dot(cs.mInvInertia * r2_cross_n); - float w1_plus_w2 = vertex_inv_mass + w2; - - // Calculate delta relative velocity due to friction (modified equation 31) - Vec3 dv; - if (v_tangential_length > 0.0f) - dv = v_tangential * min(cs.mFriction * projected_distance / (v_tangential_length * dt), 1.0f); - else - dv = Vec3::sZero(); - - // Calculate delta relative velocity due to restitution (equation 35) - dv += v_normal; - float prev_v_normal = (prev_v - v2).Dot(contact_normal); - if (prev_v_normal < restitution_treshold) - dv += cs.mRestitution * prev_v_normal * contact_normal; - - // Calculate impulse - Vec3 p = dv / w1_plus_w2; - - // Apply impulse to particle - v.mVelocity -= p * vertex_inv_mass; - - // Apply impulse to rigid body - cs.mLinearVelocity += p * cs.mInvMass; - cs.mAngularVelocity += cs.mInvInertia * r2.Cross(p); - - // Mark that the velocities of the body we hit need to be updated - cs.mUpdateVelocities = true; - } - else - { - // Body is not movable, equations are simpler - - // Calculate normal and tangential velocity (equation 30) - Vec3 v_normal = contact_normal * contact_normal.Dot(v.mVelocity); - Vec3 v_tangential = v.mVelocity - v_normal; - float v_tangential_length = v_tangential.Length(); - - // Apply friction (modified equation 31) - if (v_tangential_length > 0.0f) - v.mVelocity -= v_tangential * min(cs.mFriction * projected_distance / (v_tangential_length * dt), 1.0f); - - // Apply restitution (equation 35) - v.mVelocity -= v_normal; - float prev_v_normal = prev_v.Dot(contact_normal); - if (prev_v_normal < restitution_treshold) - v.mVelocity -= cs.mRestitution * prev_v_normal * contact_normal; - } - } - } - } - } -} - -void SoftBodyMotionProperties::UpdateSoftBodyState(SoftBodyUpdateContext &ioContext, const PhysicsSettings &inPhysicsSettings) -{ - JPH_PROFILE_FUNCTION(); - - // Contact callback - if (mHasContact && ioContext.mContactListener != nullptr) - ioContext.mContactListener->OnSoftBodyContactAdded(*ioContext.mBody, SoftBodyManifold(this)); - - // Loop through vertices once more to update the global state - float dt = ioContext.mDeltaTime; - float max_linear_velocity_sq = Square(GetMaxLinearVelocity()); - float max_v_sq = 0.0f; - Vec3 linear_velocity = Vec3::sZero(), angular_velocity = Vec3::sZero(); - mLocalPredictedBounds = mLocalBounds = { }; - mHasContact = false; - for (Vertex &v : mVertices) - { - // Calculate max square velocity - float v_sq = v.mVelocity.LengthSq(); - max_v_sq = max(max_v_sq, v_sq); - - // Clamp if velocity is too high - if (v_sq > max_linear_velocity_sq) - v.mVelocity *= sqrt(max_linear_velocity_sq / v_sq); - - // Calculate local linear/angular velocity - linear_velocity += v.mVelocity; - angular_velocity += v.mPosition.Cross(v.mVelocity); - - // Update local bounding box - mLocalBounds.Encapsulate(v.mPosition); - - // Create predicted position for the next frame in order to detect collisions before they happen - mLocalPredictedBounds.Encapsulate(v.mPosition + v.mVelocity * dt + ioContext.mDisplacementDueToGravity); - - // Reset collision data for the next iteration - v.mCollidingShapeIndex = -1; - v.mHasContact = false; - v.mLargestPenetration = -FLT_MAX; - } - - // Calculate linear/angular velocity of the body by averaging all vertices and bringing the value to world space - float num_vertices_divider = float(max(int(mVertices.size()), 1)); - SetLinearVelocity(ioContext.mCenterOfMassTransform.Multiply3x3(linear_velocity / num_vertices_divider)); - SetAngularVelocity(ioContext.mCenterOfMassTransform.Multiply3x3(angular_velocity / num_vertices_divider)); - - if (mUpdatePosition) - { - // Shift the body so that the position is the center of the local bounds - Vec3 delta = mLocalBounds.GetCenter(); - ioContext.mDeltaPosition = ioContext.mCenterOfMassTransform.Multiply3x3(delta); - for (Vertex &v : mVertices) - v.mPosition -= delta; - - // Offset bounds to match new position - mLocalBounds.Translate(-delta); - mLocalPredictedBounds.Translate(-delta); - } - else - ioContext.mDeltaPosition = Vec3::sZero(); - - // Test if we should go to sleep - if (GetAllowSleeping()) - { - if (max_v_sq > inPhysicsSettings.mPointVelocitySleepThreshold) - { - ResetSleepTestTimer(); - ioContext.mCanSleep = ECanSleep::CannotSleep; - } - else - ioContext.mCanSleep = AccumulateSleepTime(dt, inPhysicsSettings.mTimeBeforeSleep); - } - else - ioContext.mCanSleep = ECanSleep::CannotSleep; -} - -void SoftBodyMotionProperties::UpdateRigidBodyVelocities(const SoftBodyUpdateContext &inContext, BodyInterface &inBodyInterface) -{ - JPH_PROFILE_FUNCTION(); - - // Write back velocity deltas - for (const CollidingShape &cs : mCollidingShapes) - if (cs.mUpdateVelocities) - inBodyInterface.AddLinearAndAngularVelocity(cs.mBodyID, inContext.mCenterOfMassTransform.Multiply3x3(cs.mLinearVelocity - cs.mOriginalLinearVelocity), inContext.mCenterOfMassTransform.Multiply3x3(cs.mAngularVelocity - cs.mOriginalAngularVelocity)); - - // Clear colliding shapes to avoid hanging on to references to shapes - mCollidingShapes.clear(); -} - -void SoftBodyMotionProperties::InitializeUpdateContext(float inDeltaTime, Body &inSoftBody, const PhysicsSystem &inSystem, SoftBodyUpdateContext &ioContext) -{ - JPH_PROFILE_FUNCTION(); - - // Store body - ioContext.mBody = &inSoftBody; - ioContext.mMotionProperties = this; - ioContext.mContactListener = inSystem.GetSoftBodyContactListener(); - - // Convert gravity to local space - ioContext.mCenterOfMassTransform = inSoftBody.GetCenterOfMassTransform(); - ioContext.mGravity = ioContext.mCenterOfMassTransform.Multiply3x3Transposed(GetGravityFactor() * inSystem.GetGravity()); - - // Calculate delta time for sub step - ioContext.mDeltaTime = inDeltaTime; - ioContext.mSubStepDeltaTime = inDeltaTime / mNumIterations; - - // Calculate total displacement we'll have due to gravity over all sub steps - // The total displacement as produced by our integrator can be written as: Sum(i * g * dt^2, i = 0..mNumIterations). - // This is bigger than 0.5 * g * dt^2 because we first increment the velocity and then update the position - // Using Sum(i, i = 0..n) = n * (n + 1) / 2 we can write this as: - ioContext.mDisplacementDueToGravity = (0.5f * mNumIterations * (mNumIterations + 1) * Square(ioContext.mSubStepDeltaTime)) * ioContext.mGravity; -} - -void SoftBodyMotionProperties::StartNextIteration(const SoftBodyUpdateContext &ioContext) -{ - ApplyPressure(ioContext); - - IntegratePositions(ioContext); - - ApplyBendConstraints(ioContext); - - ApplyVolumeConstraints(ioContext); -} - -SoftBodyMotionProperties::EStatus SoftBodyMotionProperties::ParallelDetermineCollisionPlanes(SoftBodyUpdateContext &ioContext) -{ - // Do a relaxed read first to see if there is any work to do (this prevents us from doing expensive atomic operations and also prevents us from continuously incrementing the counter and overflowing it) - uint num_vertices = (uint)mVertices.size(); - if (ioContext.mNextCollisionVertex.load(memory_order_relaxed) < num_vertices) - { - // Fetch next batch of vertices to process - uint next_vertex = ioContext.mNextCollisionVertex.fetch_add(SoftBodyUpdateContext::cVertexCollisionBatch, memory_order_acquire); - if (next_vertex < num_vertices) - { - // Process collision planes - uint num_vertices_to_process = min(SoftBodyUpdateContext::cVertexCollisionBatch, num_vertices - next_vertex); - DetermineCollisionPlanes(ioContext, next_vertex, num_vertices_to_process); - uint vertices_processed = ioContext.mNumCollisionVerticesProcessed.fetch_add(SoftBodyUpdateContext::cVertexCollisionBatch, memory_order_release) + num_vertices_to_process; - if (vertices_processed >= num_vertices) - { - // Start the first iteration - JPH_IF_ENABLE_ASSERTS(uint iteration =) ioContext.mNextIteration.fetch_add(1, memory_order_relaxed); - JPH_ASSERT(iteration == 0); - StartNextIteration(ioContext); - ioContext.mState.store(SoftBodyUpdateContext::EState::ApplyEdgeConstraints, memory_order_release); - } - return EStatus::DidWork; - } - } - return EStatus::NoWork; -} - -SoftBodyMotionProperties::EStatus SoftBodyMotionProperties::ParallelApplyEdgeConstraints(SoftBodyUpdateContext &ioContext, const PhysicsSettings &inPhysicsSettings) -{ - // Do a relaxed read first to see if there is any work to do (this prevents us from doing expensive atomic operations and also prevents us from continuously incrementing the counter and overflowing it) - uint num_groups = (uint)mSettings->mEdgeGroupEndIndices.size(); - JPH_ASSERT(num_groups > 0, "SoftBodySharedSettings::Optimize should have been called!"); - uint32 edge_group, edge_start_idx; - SoftBodyUpdateContext::sGetEdgeGroupAndStartIdx(ioContext.mNextEdgeConstraint.load(memory_order_relaxed), edge_group, edge_start_idx); - if (edge_group < num_groups) - { - uint edge_group_size = mSettings->GetEdgeGroupSize(edge_group); - if (edge_start_idx < edge_group_size || edge_group_size == 0) - { - // Fetch the next batch of edges to process - uint64 next_edge_batch = ioContext.mNextEdgeConstraint.fetch_add(SoftBodyUpdateContext::cEdgeConstraintBatch, memory_order_acquire); - SoftBodyUpdateContext::sGetEdgeGroupAndStartIdx(next_edge_batch, edge_group, edge_start_idx); - if (edge_group < num_groups) - { - bool non_parallel_group = edge_group == num_groups - 1; // Last group is the non-parallel group and goes as a whole - edge_group_size = mSettings->GetEdgeGroupSize(edge_group); - if (non_parallel_group? edge_start_idx == 0 : edge_start_idx < edge_group_size) - { - // Process edges - uint num_edges_to_process = non_parallel_group? edge_group_size : min(SoftBodyUpdateContext::cEdgeConstraintBatch, edge_group_size - edge_start_idx); - if (edge_group > 0) - edge_start_idx += mSettings->mEdgeGroupEndIndices[edge_group - 1]; - ApplyEdgeConstraints(ioContext, edge_start_idx, edge_start_idx + num_edges_to_process); - - // Test if we're at the end of this group - uint edge_constraints_processed = ioContext.mNumEdgeConstraintsProcessed.fetch_add(num_edges_to_process, memory_order_relaxed) + num_edges_to_process; - if (edge_constraints_processed >= edge_group_size) - { - // Non parallel group is the last group (which is also the only group that can be empty) - if (non_parallel_group || mSettings->GetEdgeGroupSize(edge_group + 1) == 0) - { - // Finish the iteration - ApplyLRAConstraints(); - - ApplyCollisionConstraintsAndUpdateVelocities(ioContext); - - ApplySkinConstraints(ioContext); - - uint iteration = ioContext.mNextIteration.fetch_add(1, memory_order_relaxed); - if (iteration < mNumIterations) - { - // Start a new iteration - StartNextIteration(ioContext); - - // Reset next edge to process - ioContext.mNumEdgeConstraintsProcessed.store(0, memory_order_relaxed); - ioContext.mNextEdgeConstraint.store(0, memory_order_release); - } - else - { - // On final iteration we update the state - UpdateSoftBodyState(ioContext, inPhysicsSettings); - - ioContext.mState.store(SoftBodyUpdateContext::EState::Done, memory_order_release); - return EStatus::Done; - } - } - else - { - // Next group - ioContext.mNumEdgeConstraintsProcessed.store(0, memory_order_relaxed); - ioContext.mNextEdgeConstraint.store(SoftBodyUpdateContext::sGetEdgeGroupStart(edge_group + 1), memory_order_release); - } - } - return EStatus::DidWork; - } - } - } - } - return EStatus::NoWork; -} - -SoftBodyMotionProperties::EStatus SoftBodyMotionProperties::ParallelUpdate(SoftBodyUpdateContext &ioContext, const PhysicsSettings &inPhysicsSettings) -{ - switch (ioContext.mState.load(memory_order_relaxed)) - { - case SoftBodyUpdateContext::EState::DetermineCollisionPlanes: - return ParallelDetermineCollisionPlanes(ioContext); - - case SoftBodyUpdateContext::EState::ApplyEdgeConstraints: - return ParallelApplyEdgeConstraints(ioContext, inPhysicsSettings); - - case SoftBodyUpdateContext::EState::Done: - return EStatus::Done; - - default: - JPH_ASSERT(false); - return EStatus::NoWork; - } -} - -void SoftBodyMotionProperties::SkinVertices(RMat44Arg inCenterOfMassTransform, const Mat44 *inJointMatrices, [[maybe_unused]] uint inNumJoints, bool inHardSkinAll, TempAllocator &ioTempAllocator) -{ - // Calculate the skin matrices - uint num_skin_matrices = uint(mSettings->mInvBindMatrices.size()); - uint skin_matrices_size = num_skin_matrices * sizeof(Mat44); - Mat44 *skin_matrices = (Mat44 *)ioTempAllocator.Allocate(skin_matrices_size); - const Mat44 *skin_matrices_end = skin_matrices + num_skin_matrices; - const InvBind *inv_bind_matrix = mSettings->mInvBindMatrices.data(); - for (Mat44 *s = skin_matrices; s < skin_matrices_end; ++s, ++inv_bind_matrix) - *s = inJointMatrices[inv_bind_matrix->mJointIndex] * inv_bind_matrix->mInvBind; - - // Skin the vertices - mSkinStateTransform = inCenterOfMassTransform; - JPH_IF_ENABLE_ASSERTS(uint num_vertices = uint(mSettings->mVertices.size());) - JPH_ASSERT(mSkinState.size() == num_vertices); - const SoftBodySharedSettings::Vertex *in_vertices = mSettings->mVertices.data(); - for (const Skinned &s : mSettings->mSkinnedConstraints) - { - // Get bind pose - JPH_ASSERT(s.mVertex < num_vertices); - Vec3 bind_pos = Vec3::sLoadFloat3Unsafe(in_vertices[s.mVertex].mPosition); - - // Skin vertex - Vec3 pos = Vec3::sZero(); - for (const SkinWeight &w : s.mWeights) - { - // We assume that the first zero weight is the end of the list - if (w.mWeight == 0.0f) - break; - - JPH_ASSERT(w.mInvBindIndex < num_skin_matrices); - pos += w.mWeight * (skin_matrices[w.mInvBindIndex] * bind_pos); - } - mSkinState[s.mVertex].mPosition = pos; - } - - // Calculate the normals - for (const Skinned &s : mSettings->mSkinnedConstraints) - { - Vec3 normal = Vec3::sZero(); - uint32 num_faces = s.mNormalInfo >> 24; - if (num_faces > 0) - { - // Calculate normal - const uint32 *f = &mSettings->mSkinnedConstraintNormals[s.mNormalInfo & 0xffffff]; - const uint32 *f_end = f + num_faces; - while (f < f_end) - { - const Face &face = mSettings->mFaces[*f]; - Vec3 v0 = mSkinState[face.mVertex[0]].mPosition; - Vec3 v1 = mSkinState[face.mVertex[1]].mPosition; - Vec3 v2 = mSkinState[face.mVertex[2]].mPosition; - normal += (v1 - v0).Cross(v2 - v0).NormalizedOr(Vec3::sZero()); - ++f; - } - normal = normal.NormalizedOr(Vec3::sZero()); - } - mSkinState[s.mVertex].mNormal = normal; - } - - ioTempAllocator.Free(skin_matrices, skin_matrices_size); - - if (inHardSkinAll) - { - // Hard skin all vertices and reset their velocities - for (const Skinned &s : mSettings->mSkinnedConstraints) - { - Vertex &vertex = mVertices[s.mVertex]; - vertex.mPosition = mSkinState[s.mVertex].mPosition; - vertex.mVelocity = Vec3::sZero(); - } - } - else if (!mEnableSkinConstraints) - { - // Hard skin only the kinematic vertices as we will not solve the skin constraints later - for (const Skinned &s : mSettings->mSkinnedConstraints) - if (s.mMaxDistance == 0.0f) - { - Vertex &vertex = mVertices[s.mVertex]; - vertex.mPosition = mSkinState[s.mVertex].mPosition; - } - } -} - -void SoftBodyMotionProperties::CustomUpdate(float inDeltaTime, Body &ioSoftBody, PhysicsSystem &inSystem) -{ - JPH_PROFILE_FUNCTION(); - - // Create update context - SoftBodyUpdateContext context; - InitializeUpdateContext(inDeltaTime, ioSoftBody, inSystem, context); - - // Determine bodies we're colliding with - DetermineCollidingShapes(context, inSystem, inSystem.GetBodyLockInterface()); - - // Call the internal update until it finishes - EStatus status; - const PhysicsSettings &settings = inSystem.GetPhysicsSettings(); - while ((status = ParallelUpdate(context, settings)) == EStatus::DidWork) - continue; - JPH_ASSERT(status == EStatus::Done); - - // Update the state of the bodies we've collided with - UpdateRigidBodyVelocities(context, inSystem.GetBodyInterface()); - - // Update position of the soft body - if (mUpdatePosition) - inSystem.GetBodyInterface().SetPosition(ioSoftBody.GetID(), ioSoftBody.GetPosition() + context.mDeltaPosition, EActivation::DontActivate); -} - -#ifdef JPH_DEBUG_RENDERER - -void SoftBodyMotionProperties::DrawVertices(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform) const -{ - for (const Vertex &v : mVertices) - inRenderer->DrawMarker(inCenterOfMassTransform * v.mPosition, v.mInvMass > 0.0f? Color::sGreen : Color::sRed, 0.05f); -} - -void SoftBodyMotionProperties::DrawVertexVelocities(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform) const -{ - for (const Vertex &v : mVertices) - inRenderer->DrawArrow(inCenterOfMassTransform * v.mPosition, inCenterOfMassTransform * (v.mPosition + v.mVelocity), Color::sYellow, 0.01f); -} - -void SoftBodyMotionProperties::DrawEdgeConstraints(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform) const -{ - for (const Edge &e : mSettings->mEdgeConstraints) - inRenderer->DrawLine(inCenterOfMassTransform * mVertices[e.mVertex[0]].mPosition, inCenterOfMassTransform * mVertices[e.mVertex[1]].mPosition, Color::sWhite); -} - -void SoftBodyMotionProperties::DrawBendConstraints(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform) const -{ - for (const DihedralBend &b : mSettings->mDihedralBendConstraints) - { - RVec3 x0 = inCenterOfMassTransform * mVertices[b.mVertex[0]].mPosition; - RVec3 x1 = inCenterOfMassTransform * mVertices[b.mVertex[1]].mPosition; - RVec3 x2 = inCenterOfMassTransform * mVertices[b.mVertex[2]].mPosition; - RVec3 x3 = inCenterOfMassTransform * mVertices[b.mVertex[3]].mPosition; - RVec3 c_edge = 0.5_r * (x0 + x1); - RVec3 c0 = (x0 + x1 + x2) / 3.0_r; - RVec3 c1 = (x0 + x1 + x3) / 3.0_r; - - inRenderer->DrawArrow(0.9_r * x0 + 0.1_r * x1, 0.1_r * x0 + 0.9_r * x1, Color::sDarkGreen, 0.01f); - inRenderer->DrawLine(c_edge, 0.1_r * c_edge + 0.9_r * c0, Color::sGreen); - inRenderer->DrawLine(c_edge, 0.1_r * c_edge + 0.9_r * c1, Color::sGreen); - } -} - -void SoftBodyMotionProperties::DrawVolumeConstraints(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform) const -{ - for (const Volume &v : mSettings->mVolumeConstraints) - { - RVec3 x1 = inCenterOfMassTransform * mVertices[v.mVertex[0]].mPosition; - RVec3 x2 = inCenterOfMassTransform * mVertices[v.mVertex[1]].mPosition; - RVec3 x3 = inCenterOfMassTransform * mVertices[v.mVertex[2]].mPosition; - RVec3 x4 = inCenterOfMassTransform * mVertices[v.mVertex[3]].mPosition; - - inRenderer->DrawTriangle(x1, x3, x2, Color::sYellow, DebugRenderer::ECastShadow::On); - inRenderer->DrawTriangle(x2, x3, x4, Color::sYellow, DebugRenderer::ECastShadow::On); - inRenderer->DrawTriangle(x1, x4, x3, Color::sYellow, DebugRenderer::ECastShadow::On); - inRenderer->DrawTriangle(x1, x2, x4, Color::sYellow, DebugRenderer::ECastShadow::On); - } -} - -void SoftBodyMotionProperties::DrawSkinConstraints(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform) const -{ - for (const Skinned &s : mSettings->mSkinnedConstraints) - { - const SkinState &skin_state = mSkinState[s.mVertex]; - DebugRenderer::sInstance->DrawArrow(mSkinStateTransform * skin_state.mPosition, mSkinStateTransform * (skin_state.mPosition + 0.1f * skin_state.mNormal), Color::sOrange, 0.01f); - DebugRenderer::sInstance->DrawLine(mSkinStateTransform * skin_state.mPosition, inCenterOfMassTransform * mVertices[s.mVertex].mPosition, Color::sBlue); - } -} - -void SoftBodyMotionProperties::DrawLRAConstraints(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform) const -{ - for (const LRA &l : mSettings->mLRAConstraints) - inRenderer->DrawLine(inCenterOfMassTransform * mVertices[l.mVertex[0]].mPosition, inCenterOfMassTransform * mVertices[l.mVertex[1]].mPosition, Color::sGrey); -} - -void SoftBodyMotionProperties::DrawPredictedBounds(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform) const -{ - inRenderer->DrawWireBox(inCenterOfMassTransform, mLocalPredictedBounds, Color::sRed); -} - -#endif // JPH_DEBUG_RENDERER - -void SoftBodyMotionProperties::SaveState(StateRecorder &inStream) const -{ - MotionProperties::SaveState(inStream); - - for (const Vertex &v : mVertices) - { - inStream.Write(v.mPreviousPosition); - inStream.Write(v.mPosition); - inStream.Write(v.mVelocity); - } - - inStream.Write(mLocalBounds.mMin); - inStream.Write(mLocalBounds.mMax); - inStream.Write(mLocalPredictedBounds.mMin); - inStream.Write(mLocalPredictedBounds.mMax); -} - -void SoftBodyMotionProperties::RestoreState(StateRecorder &inStream) -{ - MotionProperties::RestoreState(inStream); - - for (Vertex &v : mVertices) - { - inStream.Read(v.mPreviousPosition); - inStream.Read(v.mPosition); - inStream.Read(v.mVelocity); - } - - inStream.Read(mLocalBounds.mMin); - inStream.Read(mLocalBounds.mMax); - inStream.Read(mLocalPredictedBounds.mMin); - inStream.Read(mLocalPredictedBounds.mMax); -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyMotionProperties.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyMotionProperties.h deleted file mode 100644 index 0124a33b3b1..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyMotionProperties.h +++ /dev/null @@ -1,254 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2023 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -class PhysicsSystem; -class BodyInterface; -class BodyLockInterface; -struct PhysicsSettings; -class Body; -class Shape; -class SoftBodyCreationSettings; -class TempAllocator; -#ifdef JPH_DEBUG_RENDERER -class DebugRenderer; -#endif // JPH_DEBUG_RENDERER - -/// This class contains the runtime information of a soft body. -// -// Based on: XPBD, Extended Position Based Dynamics, Matthias Muller, Ten Minute Physics -// See: https://matthias-research.github.io/pages/tenMinutePhysics/09-xpbd.pdf -class JPH_EXPORT SoftBodyMotionProperties : public MotionProperties -{ -public: - using Vertex = SoftBodyVertex; - using Edge = SoftBodySharedSettings::Edge; - using Face = SoftBodySharedSettings::Face; - using DihedralBend = SoftBodySharedSettings::DihedralBend; - using Volume = SoftBodySharedSettings::Volume; - using InvBind = SoftBodySharedSettings::InvBind; - using SkinWeight = SoftBodySharedSettings::SkinWeight; - using Skinned = SoftBodySharedSettings::Skinned; - using LRA = SoftBodySharedSettings::LRA; - - /// Initialize the soft body motion properties - void Initialize(const SoftBodyCreationSettings &inSettings); - - /// Get the shared settings of the soft body - const SoftBodySharedSettings * GetSettings() const { return mSettings; } - - /// Get the vertices of the soft body - const Array & GetVertices() const { return mVertices; } - Array & GetVertices() { return mVertices; } - - /// Access an individual vertex - const Vertex & GetVertex(uint inIndex) const { return mVertices[inIndex]; } - Vertex & GetVertex(uint inIndex) { return mVertices[inIndex]; } - - /// Get the materials of the soft body - const PhysicsMaterialList & GetMaterials() const { return mSettings->mMaterials; } - - /// Get the faces of the soft body - const Array & GetFaces() const { return mSettings->mFaces; } - - /// Access to an individual face - const Face & GetFace(uint inIndex) const { return mSettings->mFaces[inIndex]; } - - /// Get the number of solver iterations - uint32 GetNumIterations() const { return mNumIterations; } - void SetNumIterations(uint32 inNumIterations) { mNumIterations = inNumIterations; } - - /// Get the pressure of the soft body - float GetPressure() const { return mPressure; } - void SetPressure(float inPressure) { mPressure = inPressure; } - - /// Update the position of the body while simulating (set to false for something that is attached to the static world) - bool GetUpdatePosition() const { return mUpdatePosition; } - void SetUpdatePosition(bool inUpdatePosition) { mUpdatePosition = inUpdatePosition; } - - /// Global setting to turn on/off skin constraints - bool GetEnableSkinConstraints() const { return mEnableSkinConstraints; } - void SetEnableSkinConstraints(bool inEnableSkinConstraints) { mEnableSkinConstraints = inEnableSkinConstraints; } - - /// Multiplier applied to Skinned::mMaxDistance to allow tightening or loosening of the skin constraints. 0 to hard skin all vertices. - float GetSkinnedMaxDistanceMultiplier() const { return mSkinnedMaxDistanceMultiplier; } - void SetSkinnedMaxDistanceMultiplier(float inSkinnedMaxDistanceMultiplier) { mSkinnedMaxDistanceMultiplier = inSkinnedMaxDistanceMultiplier; } - - /// Get local bounding box - const AABox & GetLocalBounds() const { return mLocalBounds; } - - /// Get the volume of the soft body. Note can become negative if the shape is inside out! - float GetVolume() const { return GetVolumeTimesSix() / 6.0f; } - - /// Calculate the total mass and inertia of this body based on the current state of the vertices - void CalculateMassAndInertia(); - -#ifdef JPH_DEBUG_RENDERER - /// Draw the state of a soft body - void DrawVertices(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform) const; - void DrawVertexVelocities(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform) const; - void DrawEdgeConstraints(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform) const; - void DrawBendConstraints(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform) const; - void DrawVolumeConstraints(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform) const; - void DrawSkinConstraints(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform) const; - void DrawLRAConstraints(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform) const; - void DrawPredictedBounds(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform) const; -#endif // JPH_DEBUG_RENDERER - - /// Saving state for replay - void SaveState(StateRecorder &inStream) const; - - /// Restoring state for replay - void RestoreState(StateRecorder &inStream); - - /// Skin vertices to supplied joints, information is used by the skinned constraints. - /// @param inCenterOfMassTransform Value of Body::GetCenterOfMassTransform(). - /// @param inJointMatrices The joint matrices must be expressed relative to inCenterOfMassTransform. - /// @param inNumJoints Indicates how large the inJointMatrices array is (used only for validating out of bounds). - /// @param inHardSkinAll Can be used to position all vertices on the skinned vertices and can be used to hard reset the soft body. - /// @param ioTempAllocator Allocator. - void SkinVertices(RMat44Arg inCenterOfMassTransform, const Mat44 *inJointMatrices, uint inNumJoints, bool inHardSkinAll, TempAllocator &ioTempAllocator); - - /// This function allows you to update the soft body immediately without going through the PhysicsSystem. - /// This is useful if the soft body is teleported and needs to 'settle' or it can be used if a the soft body - /// is not added to the PhysicsSystem and needs to be updated manually. One reason for not adding it to the - /// PhyicsSystem is that you might want to update a soft body immediately after updating an animated object - /// that has the soft body attached to it. If the soft body is added to the PhysicsSystem it will be updated - /// by it, so calling this function will effectively update it twice. Note that when you use this function, - /// only the current thread will be used, whereas if you update through the PhysicsSystem, multiple threads may - /// be used. - /// Note that this will bypass any sleep checks. Since the dynamic objects that the soft body touches - /// will not move during this call, there can be simulation artifacts if you call this function multiple times - /// without running the physics simulation step. - void CustomUpdate(float inDeltaTime, Body &ioSoftBody, PhysicsSystem &inSystem); - - //////////////////////////////////////////////////////////// - // FUNCTIONS BELOW THIS LINE ARE FOR INTERNAL USE ONLY - //////////////////////////////////////////////////////////// - - /// Initialize the update context. Not part of the public API. - void InitializeUpdateContext(float inDeltaTime, Body &inSoftBody, const PhysicsSystem &inSystem, SoftBodyUpdateContext &ioContext); - - /// Do a broad phase check and collect all bodies that can possibly collide with this soft body. Not part of the public API. - void DetermineCollidingShapes(const SoftBodyUpdateContext &inContext, const PhysicsSystem &inSystem, const BodyLockInterface &inBodyLockInterface); - - /// Return code for ParallelUpdate - enum class EStatus - { - NoWork = 1 << 0, ///< No work was done because other threads were still working on a batch that cannot run concurrently - DidWork = 1 << 1, ///< Work was done to progress the update - Done = 1 << 2, ///< All work is done - }; - - /// Update the soft body, will process a batch of work. Not part of the public API. - EStatus ParallelUpdate(SoftBodyUpdateContext &ioContext, const PhysicsSettings &inPhysicsSettings); - - /// Update the velocities of all rigid bodies that we collided with. Not part of the public API. - void UpdateRigidBodyVelocities(const SoftBodyUpdateContext &inContext, BodyInterface &inBodyInterface); - -private: - // SoftBodyManifold needs to have access to CollidingShape - friend class SoftBodyManifold; - - // Collect information about the colliding bodies - struct CollidingShape - { - /// Get the velocity of a point on this body - Vec3 GetPointVelocity(Vec3Arg inPointRelativeToCOM) const - { - return mLinearVelocity + mAngularVelocity.Cross(inPointRelativeToCOM); - } - - Mat44 mCenterOfMassTransform; ///< Transform of the body relative to the soft body - RefConst mShape; ///< Shape of the body we hit - BodyID mBodyID; ///< Body ID of the body we hit - EMotionType mMotionType; ///< Motion type of the body we hit - bool mIsSensor; ///< If the contact should be treated as a sensor vs body contact (no collision response) - float mInvMass; ///< Inverse mass of the body we hit - float mFriction; ///< Combined friction of the two bodies - float mRestitution; ///< Combined restitution of the two bodies - float mSoftBodyInvMassScale; ///< Scale factor for the inverse mass of the soft body vertices - bool mUpdateVelocities; ///< If the linear/angular velocity changed and the body needs to be updated - Mat44 mInvInertia; ///< Inverse inertia in local space to the soft body - Vec3 mLinearVelocity; ///< Linear velocity of the body in local space to the soft body - Vec3 mAngularVelocity; ///< Angular velocity of the body in local space to the soft body - Vec3 mOriginalLinearVelocity; ///< Linear velocity of the body in local space to the soft body at start - Vec3 mOriginalAngularVelocity; ///< Angular velocity of the body in local space to the soft body at start - }; - - // Information about the state of all skinned vertices - struct SkinState - { - Vec3 mPosition = Vec3::sNaN(); - Vec3 mNormal = Vec3::sNaN(); - }; - - /// Do a narrow phase check and determine the closest feature that we can collide with - void DetermineCollisionPlanes(const SoftBodyUpdateContext &inContext, uint inVertexStart, uint inNumVertices); - - /// Apply pressure force and update the vertex velocities - void ApplyPressure(const SoftBodyUpdateContext &inContext); - - /// Integrate the positions of all vertices by 1 sub step - void IntegratePositions(const SoftBodyUpdateContext &inContext); - - /// Enforce all bend constraints - void ApplyBendConstraints(const SoftBodyUpdateContext &inContext); - - /// Enforce all volume constraints - void ApplyVolumeConstraints(const SoftBodyUpdateContext &inContext); - - /// Enforce all skin constraints - void ApplySkinConstraints(const SoftBodyUpdateContext &inContext); - - /// Enforce all edge constraints - void ApplyEdgeConstraints(const SoftBodyUpdateContext &inContext, uint inStartIndex, uint inEndIndex); - - /// Enforce all LRA constraints - void ApplyLRAConstraints(); - - /// Enforce all collision constraints & update all velocities according the XPBD algorithm - void ApplyCollisionConstraintsAndUpdateVelocities(const SoftBodyUpdateContext &inContext); - - /// Update the state of the soft body (position, velocity, bounds) - void UpdateSoftBodyState(SoftBodyUpdateContext &ioContext, const PhysicsSettings &inPhysicsSettings); - - /// Executes tasks that need to run on the start of an iteration (i.e. the stuff that can't run in parallel) - void StartNextIteration(const SoftBodyUpdateContext &ioContext); - - /// Helper function for ParallelUpdate that works on batches of collision planes - EStatus ParallelDetermineCollisionPlanes(SoftBodyUpdateContext &ioContext); - - /// Helper function for ParallelUpdate that works on batches of edges - EStatus ParallelApplyEdgeConstraints(SoftBodyUpdateContext &ioContext, const PhysicsSettings &inPhysicsSettings); - - /// Returns 6 times the volume of the soft body - float GetVolumeTimesSix() const; - - RMat44 mSkinStateTransform = RMat44::sIdentity(); ///< The matrix that transforms mSkinState to world space - RefConst mSettings; ///< Configuration of the particles and constraints - Array mVertices; ///< Current state of all vertices in the simulation - Array mCollidingShapes; ///< List of colliding shapes retrieved during the last update - Array mSkinState; ///< List of skinned positions (1-on-1 with mVertices but only those that are used by the skinning constraints are filled in) - AABox mLocalBounds; ///< Bounding box of all vertices - AABox mLocalPredictedBounds; ///< Predicted bounding box for all vertices using extrapolation of velocity by last step delta time - uint32 mNumIterations; ///< Number of solver iterations - float mPressure; ///< n * R * T, amount of substance * ideal gas constant * absolute temperature, see https://en.wikipedia.org/wiki/Pressure - float mSkinnedMaxDistanceMultiplier = 1.0f; ///< Multiplier applied to Skinned::mMaxDistance to allow tightening or loosening of the skin constraints - bool mUpdatePosition; ///< Update the position of the body while simulating (set to false for something that is attached to the static world) - bool mHasContact = false; ///< True if the soft body has collided with anything in the last update - bool mEnableSkinConstraints = true; ///< If skin constraints are enabled -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyShape.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyShape.cpp deleted file mode 100644 index 391bdef1bbb..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyShape.cpp +++ /dev/null @@ -1,338 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2023 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef JPH_DEBUG_RENDERER - #include -#endif // JPH_DEBUG_RENDERER - -JPH_NAMESPACE_BEGIN - -uint SoftBodyShape::GetSubShapeIDBits() const -{ - // Ensure we have enough bits to encode our shape [0, n - 1] - uint32 n = (uint32)mSoftBodyMotionProperties->GetFaces().size() - 1; - return 32 - CountLeadingZeros(n); -} - -uint32 SoftBodyShape::GetFaceIndex(const SubShapeID &inSubShapeID) const -{ - SubShapeID remainder; - uint32 face_index = inSubShapeID.PopID(GetSubShapeIDBits(), remainder); - JPH_ASSERT(remainder.IsEmpty()); - return face_index; -} - -AABox SoftBodyShape::GetLocalBounds() const -{ - return mSoftBodyMotionProperties->GetLocalBounds(); -} - -bool SoftBodyShape::CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const -{ - JPH_PROFILE_FUNCTION(); - - uint num_triangle_bits = GetSubShapeIDBits(); - uint triangle_idx = uint(-1); - - const Array &vertices = mSoftBodyMotionProperties->GetVertices(); - for (const SoftBodyMotionProperties::Face &f : mSoftBodyMotionProperties->GetFaces()) - { - Vec3 x1 = vertices[f.mVertex[0]].mPosition; - Vec3 x2 = vertices[f.mVertex[1]].mPosition; - Vec3 x3 = vertices[f.mVertex[2]].mPosition; - - float fraction = RayTriangle(inRay.mOrigin, inRay.mDirection, x1, x2, x3); - if (fraction < ioHit.mFraction) - { - // Store fraction - ioHit.mFraction = fraction; - - // Store triangle index - triangle_idx = uint(&f - mSoftBodyMotionProperties->GetFaces().data()); - } - } - - if (triangle_idx == uint(-1)) - return false; - - ioHit.mSubShapeID2 = inSubShapeIDCreator.PushID(triangle_idx, num_triangle_bits).GetID(); - return true; -} - -void SoftBodyShape::CastRay(const RayCast &inRay, const RayCastSettings &inRayCastSettings, const SubShapeIDCreator &inSubShapeIDCreator, CastRayCollector &ioCollector, const ShapeFilter &inShapeFilter) const -{ - JPH_PROFILE_FUNCTION(); - - // Test shape filter - if (!inShapeFilter.ShouldCollide(this, inSubShapeIDCreator.GetID())) - return; - - uint num_triangle_bits = GetSubShapeIDBits(); - - const Array &vertices = mSoftBodyMotionProperties->GetVertices(); - for (const SoftBodyMotionProperties::Face &f : mSoftBodyMotionProperties->GetFaces()) - { - Vec3 x1 = vertices[f.mVertex[0]].mPosition; - Vec3 x2 = vertices[f.mVertex[1]].mPosition; - Vec3 x3 = vertices[f.mVertex[2]].mPosition; - - // Back facing check - if (inRayCastSettings.mBackFaceMode == EBackFaceMode::IgnoreBackFaces && (x2 - x1).Cross(x3 - x1).Dot(inRay.mDirection) > 0.0f) - return; - - // Test ray against triangle - float fraction = RayTriangle(inRay.mOrigin, inRay.mDirection, x1, x2, x3); - if (fraction < ioCollector.GetEarlyOutFraction()) - { - // Better hit than the current hit - RayCastResult hit; - hit.mBodyID = TransformedShape::sGetBodyID(ioCollector.GetContext()); - hit.mFraction = fraction; - hit.mSubShapeID2 = inSubShapeIDCreator.PushID(uint(&f - mSoftBodyMotionProperties->GetFaces().data()), num_triangle_bits).GetID(); - ioCollector.AddHit(hit); - } - } -} - -void SoftBodyShape::CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter) const -{ - sCollidePointUsingRayCast(*this, inPoint, inSubShapeIDCreator, ioCollector, inShapeFilter); -} - -void SoftBodyShape::CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, SoftBodyVertex *ioVertices, uint inNumVertices, float inDeltaTime, Vec3Arg inDisplacementDueToGravity, int inCollidingShapeIndex) const -{ - /* Not implemented */ -} - -const PhysicsMaterial *SoftBodyShape::GetMaterial(const SubShapeID &inSubShapeID) const -{ - SubShapeID remainder; - uint triangle_idx = inSubShapeID.PopID(GetSubShapeIDBits(), remainder); - JPH_ASSERT(remainder.IsEmpty()); - - const SoftBodyMotionProperties::Face &f = mSoftBodyMotionProperties->GetFace(triangle_idx); - return mSoftBodyMotionProperties->GetMaterials()[f.mMaterialIndex]; -} - -Vec3 SoftBodyShape::GetSurfaceNormal(const SubShapeID &inSubShapeID, Vec3Arg inLocalSurfacePosition) const -{ - SubShapeID remainder; - uint triangle_idx = inSubShapeID.PopID(GetSubShapeIDBits(), remainder); - JPH_ASSERT(remainder.IsEmpty()); - - const SoftBodyMotionProperties::Face &f = mSoftBodyMotionProperties->GetFace(triangle_idx); - const Array &vertices = mSoftBodyMotionProperties->GetVertices(); - - Vec3 x1 = vertices[f.mVertex[0]].mPosition; - Vec3 x2 = vertices[f.mVertex[1]].mPosition; - Vec3 x3 = vertices[f.mVertex[2]].mPosition; - - return (x2 - x1).Cross(x3 - x1).NormalizedOr(Vec3::sAxisY()); -} - -void SoftBodyShape::GetSupportingFace(const SubShapeID &inSubShapeID, Vec3Arg inDirection, Vec3Arg inScale, Mat44Arg inCenterOfMassTransform, SupportingFace &outVertices) const -{ - SubShapeID remainder; - uint triangle_idx = inSubShapeID.PopID(GetSubShapeIDBits(), remainder); - JPH_ASSERT(remainder.IsEmpty()); - - const SoftBodyMotionProperties::Face &f = mSoftBodyMotionProperties->GetFace(triangle_idx); - const Array &vertices = mSoftBodyMotionProperties->GetVertices(); - - for (uint32 i : f.mVertex) - outVertices.push_back(inCenterOfMassTransform * (inScale * vertices[i].mPosition)); -} - -void SoftBodyShape::GetSubmergedVolume(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, const Plane &inSurface, float &outTotalVolume, float &outSubmergedVolume, Vec3 &outCenterOfBuoyancy JPH_IF_DEBUG_RENDERER(, RVec3Arg inBaseOffset)) const -{ - outSubmergedVolume = 0.0f; - outTotalVolume = mSoftBodyMotionProperties->GetVolume(); - outCenterOfBuoyancy = Vec3::sZero(); -} - -#ifdef JPH_DEBUG_RENDERER - -void SoftBodyShape::Draw(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inUseMaterialColors, bool inDrawWireframe) const -{ - const Array &vertices = mSoftBodyMotionProperties->GetVertices(); - for (const SoftBodyMotionProperties::Face &f : mSoftBodyMotionProperties->GetFaces()) - { - RVec3 x1 = inCenterOfMassTransform * vertices[f.mVertex[0]].mPosition; - RVec3 x2 = inCenterOfMassTransform * vertices[f.mVertex[1]].mPosition; - RVec3 x3 = inCenterOfMassTransform * vertices[f.mVertex[2]].mPosition; - - inRenderer->DrawTriangle(x1, x2, x3, inColor, DebugRenderer::ECastShadow::On); - } -} - -#endif // JPH_DEBUG_RENDERER - -struct SoftBodyShape::SBSGetTrianglesContext -{ - Mat44 mCenterOfMassTransform; - int mTriangleIndex; -}; - -void SoftBodyShape::GetTrianglesStart(GetTrianglesContext &ioContext, [[maybe_unused]] const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) const -{ - SBSGetTrianglesContext &context = reinterpret_cast(ioContext); - context.mCenterOfMassTransform = Mat44::sRotationTranslation(inRotation, inPositionCOM) * Mat44::sScale(inScale); - context.mTriangleIndex = 0; -} - -int SoftBodyShape::GetTrianglesNext(GetTrianglesContext &ioContext, int inMaxTrianglesRequested, Float3 *outTriangleVertices, const PhysicsMaterial **outMaterials) const -{ - SBSGetTrianglesContext &context = reinterpret_cast(ioContext); - - const Array &faces = mSoftBodyMotionProperties->GetFaces(); - const Array &vertices = mSoftBodyMotionProperties->GetVertices(); - const PhysicsMaterialList &materials = mSoftBodyMotionProperties->GetMaterials(); - - int num_triangles = min(inMaxTrianglesRequested, (int)faces.size() - context.mTriangleIndex); - for (int i = 0; i < num_triangles; ++i) - { - const SoftBodyMotionProperties::Face &f = faces[context.mTriangleIndex + i]; - - Vec3 x1 = context.mCenterOfMassTransform * vertices[f.mVertex[0]].mPosition; - Vec3 x2 = context.mCenterOfMassTransform * vertices[f.mVertex[1]].mPosition; - Vec3 x3 = context.mCenterOfMassTransform * vertices[f.mVertex[2]].mPosition; - - x1.StoreFloat3(outTriangleVertices++); - x2.StoreFloat3(outTriangleVertices++); - x3.StoreFloat3(outTriangleVertices++); - - if (outMaterials != nullptr) - *outMaterials++ = materials[f.mMaterialIndex]; - } - - context.mTriangleIndex += num_triangles; - return num_triangles; -} - -Shape::Stats SoftBodyShape::GetStats() const -{ - return Stats(sizeof(*this), (uint)mSoftBodyMotionProperties->GetFaces().size()); -} - -float SoftBodyShape::GetVolume() const -{ - return mSoftBodyMotionProperties->GetVolume(); -} - -void SoftBodyShape::sCollideConvexVsSoftBody(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, [[maybe_unused]] const ShapeFilter &inShapeFilter) -{ - JPH_ASSERT(inShape1->GetType() == EShapeType::Convex); - const ConvexShape *shape1 = static_cast(inShape1); - JPH_ASSERT(inShape2->GetSubType() == EShapeSubType::SoftBody); - const SoftBodyShape *shape2 = static_cast(inShape2); - - const Array &vertices = shape2->mSoftBodyMotionProperties->GetVertices(); - const Array &faces = shape2->mSoftBodyMotionProperties->GetFaces(); - uint num_triangle_bits = shape2->GetSubShapeIDBits(); - - CollideConvexVsTriangles collider(shape1, inScale1, inScale2, inCenterOfMassTransform1, inCenterOfMassTransform2, inSubShapeIDCreator1.GetID(), inCollideShapeSettings, ioCollector); - for (const SoftBodyMotionProperties::Face &f : faces) - { - Vec3 x1 = vertices[f.mVertex[0]].mPosition; - Vec3 x2 = vertices[f.mVertex[1]].mPosition; - Vec3 x3 = vertices[f.mVertex[2]].mPosition; - - collider.Collide(x1, x2, x3, 0b111, inSubShapeIDCreator2.PushID(uint(&f - faces.data()), num_triangle_bits).GetID()); - } -} - -void SoftBodyShape::sCollideSphereVsSoftBody(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, [[maybe_unused]] const ShapeFilter &inShapeFilter) -{ - JPH_ASSERT(inShape1->GetSubType() == EShapeSubType::Sphere); - const SphereShape *shape1 = static_cast(inShape1); - JPH_ASSERT(inShape2->GetSubType() == EShapeSubType::SoftBody); - const SoftBodyShape *shape2 = static_cast(inShape2); - - const Array &vertices = shape2->mSoftBodyMotionProperties->GetVertices(); - const Array &faces = shape2->mSoftBodyMotionProperties->GetFaces(); - uint num_triangle_bits = shape2->GetSubShapeIDBits(); - - CollideSphereVsTriangles collider(shape1, inScale1, inScale2, inCenterOfMassTransform1, inCenterOfMassTransform2, inSubShapeIDCreator1.GetID(), inCollideShapeSettings, ioCollector); - for (const SoftBodyMotionProperties::Face &f : faces) - { - Vec3 x1 = vertices[f.mVertex[0]].mPosition; - Vec3 x2 = vertices[f.mVertex[1]].mPosition; - Vec3 x3 = vertices[f.mVertex[2]].mPosition; - - collider.Collide(x1, x2, x3, 0b111, inSubShapeIDCreator2.PushID(uint(&f - faces.data()), num_triangle_bits).GetID()); - } -} - -void SoftBodyShape::sCastConvexVsSoftBody(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, [[maybe_unused]] const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector) -{ - JPH_ASSERT(inShape->GetSubType() == EShapeSubType::SoftBody); - const SoftBodyShape *shape = static_cast(inShape); - - const Array &vertices = shape->mSoftBodyMotionProperties->GetVertices(); - const Array &faces = shape->mSoftBodyMotionProperties->GetFaces(); - uint num_triangle_bits = shape->GetSubShapeIDBits(); - - CastConvexVsTriangles caster(inShapeCast, inShapeCastSettings, inScale, inCenterOfMassTransform2, inSubShapeIDCreator1, ioCollector); - for (const SoftBodyMotionProperties::Face &f : faces) - { - Vec3 x1 = vertices[f.mVertex[0]].mPosition; - Vec3 x2 = vertices[f.mVertex[1]].mPosition; - Vec3 x3 = vertices[f.mVertex[2]].mPosition; - - caster.Cast(x1, x2, x3, 0b111, inSubShapeIDCreator2.PushID(uint(&f - faces.data()), num_triangle_bits).GetID()); - } -} - -void SoftBodyShape::sCastSphereVsSoftBody(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, [[maybe_unused]] const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector) -{ - JPH_ASSERT(inShape->GetSubType() == EShapeSubType::SoftBody); - const SoftBodyShape *shape = static_cast(inShape); - - const Array &vertices = shape->mSoftBodyMotionProperties->GetVertices(); - const Array &faces = shape->mSoftBodyMotionProperties->GetFaces(); - uint num_triangle_bits = shape->GetSubShapeIDBits(); - - CastSphereVsTriangles caster(inShapeCast, inShapeCastSettings, inScale, inCenterOfMassTransform2, inSubShapeIDCreator1, ioCollector); - for (const SoftBodyMotionProperties::Face &f : faces) - { - Vec3 x1 = vertices[f.mVertex[0]].mPosition; - Vec3 x2 = vertices[f.mVertex[1]].mPosition; - Vec3 x3 = vertices[f.mVertex[2]].mPosition; - - caster.Cast(x1, x2, x3, 0b111, inSubShapeIDCreator2.PushID(uint(&f - faces.data()), num_triangle_bits).GetID()); - } -} - -void SoftBodyShape::sRegister() -{ - ShapeFunctions &f = ShapeFunctions::sGet(EShapeSubType::SoftBody); - f.mConstruct = nullptr; // Not supposed to be constructed by users! - f.mColor = Color::sDarkGreen; - - for (EShapeSubType s : sConvexSubShapeTypes) - { - CollisionDispatch::sRegisterCollideShape(s, EShapeSubType::SoftBody, sCollideConvexVsSoftBody); - CollisionDispatch::sRegisterCastShape(s, EShapeSubType::SoftBody, sCastConvexVsSoftBody); - } - - // Specialized collision functions - CollisionDispatch::sRegisterCollideShape(EShapeSubType::Sphere, EShapeSubType::SoftBody, sCollideSphereVsSoftBody); - CollisionDispatch::sRegisterCastShape(EShapeSubType::Sphere, EShapeSubType::SoftBody, sCastSphereVsSoftBody); -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyShape.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyShape.h deleted file mode 100644 index 605ec80a239..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyShape.h +++ /dev/null @@ -1,73 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2023 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -class SoftBodyMotionProperties; -class CollideShapeSettings; - -/// Shape used exclusively for soft bodies. Adds the ability to perform collision checks against soft bodies. -class JPH_EXPORT SoftBodyShape final : public Shape -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Constructor - SoftBodyShape() : Shape(EShapeType::SoftBody, EShapeSubType::SoftBody) { } - - /// Determine amount of bits needed to encode sub shape id - uint GetSubShapeIDBits() const; - - /// Convert a sub shape ID back to a face index - uint32 GetFaceIndex(const SubShapeID &inSubShapeID) const; - - // See Shape - virtual bool MustBeStatic() const override { return false; } - virtual Vec3 GetCenterOfMass() const override { return Vec3::sZero(); } - virtual AABox GetLocalBounds() const override; - virtual uint GetSubShapeIDBitsRecursive() const override { return GetSubShapeIDBits(); } - virtual float GetInnerRadius() const override { return 0.0f; } - virtual MassProperties GetMassProperties() const override { return MassProperties(); } - virtual const PhysicsMaterial * GetMaterial(const SubShapeID &inSubShapeID) const override; - virtual Vec3 GetSurfaceNormal(const SubShapeID &inSubShapeID, Vec3Arg inLocalSurfacePosition) const override; - virtual void GetSupportingFace(const SubShapeID &inSubShapeID, Vec3Arg inDirection, Vec3Arg inScale, Mat44Arg inCenterOfMassTransform, SupportingFace &outVertices) const override; - virtual void GetSubmergedVolume(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, const Plane &inSurface, float &outTotalVolume, float &outSubmergedVolume, Vec3 &outCenterOfBuoyancy -#ifdef JPH_DEBUG_RENDERER // Not using JPH_IF_DEBUG_RENDERER for Doxygen - , RVec3Arg inBaseOffset -#endif - ) const override; -#ifdef JPH_DEBUG_RENDERER - virtual void Draw(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inUseMaterialColors, bool inDrawWireframe) const override; -#endif // JPH_DEBUG_RENDERER - virtual bool CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const override; - virtual void CastRay(const RayCast &inRay, const RayCastSettings &inRayCastSettings, const SubShapeIDCreator &inSubShapeIDCreator, CastRayCollector &ioCollector, const ShapeFilter &inShapeFilter = { }) const override; - virtual void CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter = { }) const override; - virtual void CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, SoftBodyVertex *ioVertices, uint inNumVertices, float inDeltaTime, Vec3Arg inDisplacementDueToGravity, int inCollidingShapeIndex) const override; - virtual void GetTrianglesStart(GetTrianglesContext &ioContext, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) const override; - virtual int GetTrianglesNext(GetTrianglesContext &ioContext, int inMaxTrianglesRequested, Float3 *outTriangleVertices, const PhysicsMaterial **outMaterials = nullptr) const override; - virtual Stats GetStats() const override; - virtual float GetVolume() const override; - - // Register shape functions with the registry - static void sRegister(); - -private: - // Helper functions called by CollisionDispatch - static void sCollideConvexVsSoftBody(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter); - static void sCollideSphereVsSoftBody(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter); - static void sCastConvexVsSoftBody(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector); - static void sCastSphereVsSoftBody(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector); - - struct SBSGetTrianglesContext; - - friend class BodyManager; - - const SoftBodyMotionProperties *mSoftBodyMotionProperties; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodySharedSettings.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodySharedSettings.cpp deleted file mode 100644 index 91eae3fb247..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodySharedSettings.cpp +++ /dev/null @@ -1,698 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2023 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -JPH_SUPPRESS_WARNINGS_STD_BEGIN -#include -JPH_SUPPRESS_WARNINGS_STD_END - -JPH_NAMESPACE_BEGIN - -template, class Compare = std::less> using PriorityQueue = std::priority_queue; - -JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SoftBodySharedSettings::Vertex) -{ - JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Vertex, mPosition) - JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Vertex, mVelocity) - JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Vertex, mInvMass) -} - -JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SoftBodySharedSettings::Face) -{ - JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Face, mVertex) - JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Face, mMaterialIndex) -} - -JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SoftBodySharedSettings::Edge) -{ - JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Edge, mVertex) - JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Edge, mRestLength) - JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Edge, mCompliance) -} - -JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SoftBodySharedSettings::DihedralBend) -{ - JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::DihedralBend, mVertex) - JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::DihedralBend, mCompliance) - JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::DihedralBend, mInitialAngle) -} - -JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SoftBodySharedSettings::Volume) -{ - JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Volume, mVertex) - JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Volume, mSixRestVolume) - JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Volume, mCompliance) -} - -JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SoftBodySharedSettings::InvBind) -{ - JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::InvBind, mJointIndex) - JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::InvBind, mInvBind) -} - -JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SoftBodySharedSettings::SkinWeight) -{ - JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::SkinWeight, mInvBindIndex) - JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::SkinWeight, mWeight) -} - -JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SoftBodySharedSettings::Skinned) -{ - JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Skinned, mVertex) - JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Skinned, mWeights) - JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Skinned, mMaxDistance) - JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Skinned, mBackStopDistance) - JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Skinned, mBackStopRadius) -} - -JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SoftBodySharedSettings::LRA) -{ - JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::LRA, mVertex) - JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::LRA, mMaxDistance) -} - -JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SoftBodySharedSettings) -{ - JPH_ADD_ATTRIBUTE(SoftBodySharedSettings, mVertices) - JPH_ADD_ATTRIBUTE(SoftBodySharedSettings, mFaces) - JPH_ADD_ATTRIBUTE(SoftBodySharedSettings, mEdgeConstraints) - JPH_ADD_ATTRIBUTE(SoftBodySharedSettings, mEdgeGroupEndIndices) - JPH_ADD_ATTRIBUTE(SoftBodySharedSettings, mDihedralBendConstraints) - JPH_ADD_ATTRIBUTE(SoftBodySharedSettings, mVolumeConstraints) - JPH_ADD_ATTRIBUTE(SoftBodySharedSettings, mSkinnedConstraints) - JPH_ADD_ATTRIBUTE(SoftBodySharedSettings, mInvBindMatrices) - JPH_ADD_ATTRIBUTE(SoftBodySharedSettings, mLRAConstraints) - JPH_ADD_ATTRIBUTE(SoftBodySharedSettings, mMaterials) - JPH_ADD_ATTRIBUTE(SoftBodySharedSettings, mVertexRadius) -} - -void SoftBodySharedSettings::CalculateClosestKinematic() -{ - // Check if we already calculated this - if (!mClosestKinematic.empty()) - return; - - // Reserve output size - mClosestKinematic.resize(mVertices.size()); - - // Create a list of connected vertices - Array> connectivity; - connectivity.resize(mVertices.size()); - for (const Edge &e : mEdgeConstraints) - { - connectivity[e.mVertex[0]].push_back(e.mVertex[1]); - connectivity[e.mVertex[1]].push_back(e.mVertex[0]); - } - - // Use Dijkstra's algorithm to find the closest kinematic vertex for each vertex - // See: https://en.wikipedia.org/wiki/Dijkstra's_algorithm - // - // An element in the open list - struct Open - { - // Order so that we get the shortest distance first - bool operator < (const Open &inRHS) const - { - return mDistance > inRHS.mDistance; - } - - uint32 mVertex; - float mDistance; - }; - - // Start with all kinematic elements - PriorityQueue to_visit; - for (uint32 v = 0; v < mVertices.size(); ++v) - if (mVertices[v].mInvMass == 0.0f) - { - mClosestKinematic[v].mVertex = v; - mClosestKinematic[v].mDistance = 0.0f; - to_visit.push({ v, 0.0f }); - } - - // Visit all vertices remembering the closest kinematic vertex and its distance - while (!to_visit.empty()) - { - // Pop element from the open list - Open current = to_visit.top(); - to_visit.pop(); - - // Loop through all of its connected vertices - for (uint32 v : connectivity[current.mVertex]) - { - // Calculate distance from the current vertex to this target vertex and check if it is smaller - float new_distance = current.mDistance + (Vec3(mVertices[v].mPosition) - Vec3(mVertices[current.mVertex].mPosition)).Length(); - if (new_distance < mClosestKinematic[v].mDistance) - { - // Remember new closest vertex - mClosestKinematic[v].mVertex = mClosestKinematic[current.mVertex].mVertex; - mClosestKinematic[v].mDistance = new_distance; - to_visit.push({ v, new_distance }); - } - } - } -} - -void SoftBodySharedSettings::CreateConstraints(const VertexAttributes *inVertexAttributes, uint inVertexAttributesLength, EBendType inBendType, float inAngleTolerance) -{ - struct EdgeHelper - { - uint32 mVertex[2]; - uint32 mEdgeIdx; - }; - - // Create list of all edges - Array edges; - edges.reserve(mFaces.size() * 3); - for (const Face &f : mFaces) - for (int i = 0; i < 3; ++i) - { - uint32 v0 = f.mVertex[i]; - uint32 v1 = f.mVertex[(i + 1) % 3]; - - EdgeHelper e; - e.mVertex[0] = min(v0, v1); - e.mVertex[1] = max(v0, v1); - e.mEdgeIdx = uint32(&f - mFaces.data()) * 3 + i; - edges.push_back(e); - } - - // Sort the edges - QuickSort(edges.begin(), edges.end(), [](const EdgeHelper &inLHS, const EdgeHelper &inRHS) { return inLHS.mVertex[0] < inRHS.mVertex[0] || (inLHS.mVertex[0] == inRHS.mVertex[0] && inLHS.mVertex[1] < inRHS.mVertex[1]); }); - - // Only add edges if one of the vertices is movable - auto add_edge = [this](uint32 inVtx1, uint32 inVtx2, float inCompliance1, float inCompliance2) { - if ((mVertices[inVtx1].mInvMass > 0.0f || mVertices[inVtx2].mInvMass > 0.0f) - && inCompliance1 < FLT_MAX && inCompliance2 < FLT_MAX) - { - Edge temp_edge; - temp_edge.mVertex[0] = inVtx1; - temp_edge.mVertex[1] = inVtx2; - temp_edge.mCompliance = 0.5f * (inCompliance1 + inCompliance2); - temp_edge.mRestLength = (Vec3(mVertices[inVtx2].mPosition) - Vec3(mVertices[inVtx1].mPosition)).Length(); - JPH_ASSERT(temp_edge.mRestLength > 0.0f); - mEdgeConstraints.push_back(temp_edge); - } - }; - - // Helper function to get the attributes of a vertex - auto attr = [inVertexAttributes, inVertexAttributesLength](uint32 inVertex) { - return inVertexAttributes[min(inVertex, inVertexAttributesLength - 1)]; - }; - - // Create the constraints - float sq_sin_tolerance = Square(Sin(inAngleTolerance)); - float sq_cos_tolerance = Square(Cos(inAngleTolerance)); - mEdgeConstraints.clear(); - mEdgeConstraints.reserve(edges.size()); - for (Array::size_type i = 0; i < edges.size(); ++i) - { - const EdgeHelper &e0 = edges[i]; - - // Get attributes for the vertices of the edge - const VertexAttributes &a0 = attr(e0.mVertex[0]); - const VertexAttributes &a1 = attr(e0.mVertex[1]); - - // Flag that indicates if this edge is a shear edge (if 2 triangles form a quad-like shape and this edge is on the diagonal) - bool is_shear = false; - - // Test if there are any shared edges - for (Array::size_type j = i + 1; j < edges.size(); ++j) - { - const EdgeHelper &e1 = edges[j]; - if (e0.mVertex[0] == e1.mVertex[0] && e0.mVertex[1] == e1.mVertex[1]) - { - // Get opposing vertices - const Face &f0 = mFaces[e0.mEdgeIdx / 3]; - const Face &f1 = mFaces[e1.mEdgeIdx / 3]; - uint32 vopposite0 = f0.mVertex[(e0.mEdgeIdx + 2) % 3]; - uint32 vopposite1 = f1.mVertex[(e1.mEdgeIdx + 2) % 3]; - const VertexAttributes &a_opposite0 = attr(vopposite0); - const VertexAttributes &a_opposite1 = attr(vopposite1); - - // Faces should be roughly in a plane - Vec3 n0 = (Vec3(mVertices[f0.mVertex[2]].mPosition) - Vec3(mVertices[f0.mVertex[0]].mPosition)).Cross(Vec3(mVertices[f0.mVertex[1]].mPosition) - Vec3(mVertices[f0.mVertex[0]].mPosition)); - Vec3 n1 = (Vec3(mVertices[f1.mVertex[2]].mPosition) - Vec3(mVertices[f1.mVertex[0]].mPosition)).Cross(Vec3(mVertices[f1.mVertex[1]].mPosition) - Vec3(mVertices[f1.mVertex[0]].mPosition)); - if (Square(n0.Dot(n1)) > sq_cos_tolerance * n0.LengthSq() * n1.LengthSq()) - { - // Faces should approximately form a quad - Vec3 e0_dir = Vec3(mVertices[vopposite0].mPosition) - Vec3(mVertices[e0.mVertex[0]].mPosition); - Vec3 e1_dir = Vec3(mVertices[vopposite1].mPosition) - Vec3(mVertices[e0.mVertex[0]].mPosition); - if (Square(e0_dir.Dot(e1_dir)) < sq_sin_tolerance * e0_dir.LengthSq() * e1_dir.LengthSq()) - { - // Shear constraint - add_edge(vopposite0, vopposite1, a_opposite0.mShearCompliance, a_opposite1.mShearCompliance); - is_shear = true; - } - } - - // Bend constraint - switch (inBendType) - { - case EBendType::None: - // Do nothing - break; - - case EBendType::Distance: - // Create an edge constraint to represent the bend constraint - // Use the bend compliance of the shared edge - if (!is_shear) - add_edge(vopposite0, vopposite1, a0.mBendCompliance, a1.mBendCompliance); - break; - - case EBendType::Dihedral: - // Test if both opposite vertices are free to move - if ((mVertices[vopposite0].mInvMass > 0.0f || mVertices[vopposite1].mInvMass > 0.0f) - && a0.mBendCompliance < FLT_MAX && a1.mBendCompliance < FLT_MAX) - { - // Create a bend constraint - // Use the bend compliance of the shared edge - mDihedralBendConstraints.emplace_back(e0.mVertex[0], e0.mVertex[1], vopposite0, vopposite1, 0.5f * (a0.mBendCompliance + a1.mBendCompliance)); - } - break; - } - } - else - { - // Start iterating from the first non-shared edge - i = j - 1; - break; - } - } - - // Create a edge constraint for the current edge - add_edge(e0.mVertex[0], e0.mVertex[1], is_shear? a0.mShearCompliance : a0.mCompliance, is_shear? a1.mShearCompliance : a1.mCompliance); - } - mEdgeConstraints.shrink_to_fit(); - - // Calculate the initial angle for all bend constraints - CalculateBendConstraintConstants(); - - // Check if any vertices have LRA constraints - bool has_lra_constraints = false; - for (const VertexAttributes *va = inVertexAttributes; va < inVertexAttributes + inVertexAttributesLength; ++va) - if (va->mLRAType != ELRAType::None) - { - has_lra_constraints = true; - break; - } - if (has_lra_constraints) - { - // Ensure we have calculated the closest kinematic vertex for each vertex - CalculateClosestKinematic(); - - // Find non-kinematic vertices - for (uint32 v = 0; v < (uint32)mVertices.size(); ++v) - if (mVertices[v].mInvMass > 0.0f) - { - // Check if a closest vertex was found - uint32 closest = mClosestKinematic[v].mVertex; - if (closest != 0xffffffff) - { - // Check which LRA constraint to create - const VertexAttributes &va = attr(v); - switch (va.mLRAType) - { - case ELRAType::None: - break; - - case ELRAType::EuclideanDistance: - mLRAConstraints.emplace_back(closest, v, va.mLRAMaxDistanceMultiplier * (Vec3(mVertices[closest].mPosition) - Vec3(mVertices[v].mPosition)).Length()); - break; - - case ELRAType::GeodesicDistance: - mLRAConstraints.emplace_back(closest, v, va.mLRAMaxDistanceMultiplier * mClosestKinematic[v].mDistance); - break; - } - } - } - } -} - -void SoftBodySharedSettings::CalculateEdgeLengths() -{ - for (Edge &e : mEdgeConstraints) - { - e.mRestLength = (Vec3(mVertices[e.mVertex[1]].mPosition) - Vec3(mVertices[e.mVertex[0]].mPosition)).Length(); - JPH_ASSERT(e.mRestLength > 0.0f); - } -} - -void SoftBodySharedSettings::CalculateLRALengths(float inMaxDistanceMultiplier) -{ - for (LRA &l : mLRAConstraints) - { - l.mMaxDistance = inMaxDistanceMultiplier * (Vec3(mVertices[l.mVertex[1]].mPosition) - Vec3(mVertices[l.mVertex[0]].mPosition)).Length(); - JPH_ASSERT(l.mMaxDistance > 0.0f); - } -} - -void SoftBodySharedSettings::CalculateBendConstraintConstants() -{ - for (DihedralBend &b : mDihedralBendConstraints) - { - // Get positions - Vec3 x0 = Vec3(mVertices[b.mVertex[0]].mPosition); - Vec3 x1 = Vec3(mVertices[b.mVertex[1]].mPosition); - Vec3 x2 = Vec3(mVertices[b.mVertex[2]].mPosition); - Vec3 x3 = Vec3(mVertices[b.mVertex[3]].mPosition); - - /* - x2 - e1/ \e3 - / \ - x0----x1 - \ e0 / - e2\ /e4 - x3 - */ - - // Calculate edges - Vec3 e0 = x1 - x0; - Vec3 e1 = x2 - x0; - Vec3 e2 = x3 - x0; - - // Normals of both triangles - Vec3 n1 = e0.Cross(e1); - Vec3 n2 = e2.Cross(e0); - float denom = sqrt(n1.LengthSq() * n2.LengthSq()); - if (denom < 1.0e-12f) - b.mInitialAngle = 0.0f; - else - { - float sign = Sign(n2.Cross(n1).Dot(e0)); - b.mInitialAngle = sign * ACos(n1.Dot(n2) / denom); - } - } -} - -void SoftBodySharedSettings::CalculateVolumeConstraintVolumes() -{ - for (Volume &v : mVolumeConstraints) - { - Vec3 x1(mVertices[v.mVertex[0]].mPosition); - Vec3 x2(mVertices[v.mVertex[1]].mPosition); - Vec3 x3(mVertices[v.mVertex[2]].mPosition); - Vec3 x4(mVertices[v.mVertex[3]].mPosition); - - Vec3 x1x2 = x2 - x1; - Vec3 x1x3 = x3 - x1; - Vec3 x1x4 = x4 - x1; - - v.mSixRestVolume = abs(x1x2.Cross(x1x3).Dot(x1x4)); - } -} - -void SoftBodySharedSettings::CalculateSkinnedConstraintNormals() -{ - // Clear any previous results - mSkinnedConstraintNormals.clear(); - - // If there are no skinned constraints, we're done - if (mSkinnedConstraints.empty()) - return; - - // First collect all vertices that are skinned - UnorderedSet skinned_vertices; - skinned_vertices.reserve(mSkinnedConstraints.size()); - for (const Skinned &s : mSkinnedConstraints) - skinned_vertices.insert(s.mVertex); - - // Now collect all faces that connect only to skinned vertices - UnorderedMap> connected_faces; - connected_faces.reserve(mVertices.size()); - for (const Face &f : mFaces) - { - // Must connect to only skinned vertices - bool valid = true; - for (uint32 v : f.mVertex) - valid &= skinned_vertices.find(v) != skinned_vertices.end(); - if (!valid) - continue; - - // Store faces that connect to vertices - for (uint32 v : f.mVertex) - connected_faces[v].insert(uint32(&f - mFaces.data())); - } - - // Populate the list of connecting faces per skinned vertex - mSkinnedConstraintNormals.reserve(mFaces.size()); - for (Skinned &s : mSkinnedConstraints) - { - uint32 start = uint32(mSkinnedConstraintNormals.size()); - JPH_ASSERT((start >> 24) == 0); - const UnorderedSet &faces = connected_faces[s.mVertex]; - uint32 num = uint32(faces.size()); - JPH_ASSERT(num < 256); - mSkinnedConstraintNormals.insert(mSkinnedConstraintNormals.end(), faces.begin(), faces.end()); - QuickSort(mSkinnedConstraintNormals.begin() + start, mSkinnedConstraintNormals.begin() + start + num); - s.mNormalInfo = start + (num << 24); - } - mSkinnedConstraintNormals.shrink_to_fit(); -} - -void SoftBodySharedSettings::Optimize(OptimizationResults &outResults) -{ - const uint cMaxNumGroups = 32; - const uint cNonParallelGroupIdx = cMaxNumGroups - 1; - const uint cMinimumSize = 2 * SoftBodyUpdateContext::cEdgeConstraintBatch; // There should be at least 2 batches, otherwise there's no point in parallelizing - - // Assign edges to non-overlapping groups - Array masks; - masks.resize(mVertices.size(), 0); - Array edge_groups[cMaxNumGroups]; - for (const Edge &e : mEdgeConstraints) - { - uint32 &mask1 = masks[e.mVertex[0]]; - uint32 &mask2 = masks[e.mVertex[1]]; - uint group = min(CountTrailingZeros((~mask1) & (~mask2)), cNonParallelGroupIdx); - uint32 mask = uint32(1U << group); - mask1 |= mask; - mask2 |= mask; - edge_groups[group].push_back(uint(&e - mEdgeConstraints.data())); - } - - // Merge groups that are too small into the non-parallel group - for (uint i = 0; i < cNonParallelGroupIdx; ++i) - if (edge_groups[i].size() < cMinimumSize) - { - edge_groups[cNonParallelGroupIdx].insert(edge_groups[cNonParallelGroupIdx].end(), edge_groups[i].begin(), edge_groups[i].end()); - edge_groups[i].clear(); - } - - // Make sure we know the closest kinematic vertex so we can sort - CalculateClosestKinematic(); - - // Sort the edge constraints - for (Array &group : edge_groups) - QuickSort(group.begin(), group.end(), [this](uint inLHS, uint inRHS) - { - const Edge &e1 = mEdgeConstraints[inLHS]; - const Edge &e2 = mEdgeConstraints[inRHS]; - - // First sort so that the edge with the smallest distance to a kinematic vertex comes first - float d1 = min(mClosestKinematic[e1.mVertex[0]].mDistance, mClosestKinematic[e1.mVertex[1]].mDistance); - float d2 = min(mClosestKinematic[e2.mVertex[0]].mDistance, mClosestKinematic[e2.mVertex[1]].mDistance); - if (d1 != d2) - return d1 < d2; - - // Order the edges so that the ones with the smallest index go first (hoping to get better cache locality when we process the edges). - // Note we could also re-order the vertices but that would be much more of a burden to the end user - return e1.GetMinVertexIndex() < e2.GetMinVertexIndex(); - }); - - // Assign the edges to groups and reorder them - Array temp_edges; - temp_edges.swap(mEdgeConstraints); - mEdgeConstraints.reserve(temp_edges.size()); - for (const Array &group : edge_groups) - if (!group.empty()) - { - for (uint idx : group) - { - mEdgeConstraints.push_back(temp_edges[idx]); - outResults.mEdgeRemap.push_back(idx); - } - mEdgeGroupEndIndices.push_back((uint)mEdgeConstraints.size()); - } - - // If there is no non-parallel group then add an empty group at the end - if (edge_groups[cNonParallelGroupIdx].empty()) - mEdgeGroupEndIndices.push_back((uint)mEdgeConstraints.size()); - - // Sort the bend constraints - outResults.mDihedralBendRemap.resize(mDihedralBendConstraints.size()); - for (int i = 0; i < (int)mDihedralBendConstraints.size(); ++i) - outResults.mDihedralBendRemap[i] = i; - QuickSort(outResults.mDihedralBendRemap.begin(), outResults.mDihedralBendRemap.end(), [this](uint inLHS, uint inRHS) - { - const DihedralBend &b1 = mDihedralBendConstraints[inLHS]; - const DihedralBend &b2 = mDihedralBendConstraints[inRHS]; - - // First sort so that the constraint with the smallest distance to a kinematic vertex comes first - float d1 = min( - min(mClosestKinematic[b1.mVertex[0]].mDistance, mClosestKinematic[b1.mVertex[1]].mDistance), - min(mClosestKinematic[b1.mVertex[2]].mDistance, mClosestKinematic[b1.mVertex[3]].mDistance)); - float d2 = min( - min(mClosestKinematic[b2.mVertex[0]].mDistance, mClosestKinematic[b2.mVertex[1]].mDistance), - min(mClosestKinematic[b2.mVertex[2]].mDistance, mClosestKinematic[b2.mVertex[3]].mDistance)); - if (d1 != d2) - return d1 < d2; - - // Order constraints so that the ones with the smallest index go first - return b1.GetMinVertexIndex() < b2.GetMinVertexIndex(); - }); - - // Reorder the bend constraints - Array temp_bends; - temp_bends.swap(mDihedralBendConstraints); - mDihedralBendConstraints.reserve(temp_bends.size()); - for (uint idx : outResults.mDihedralBendRemap) - mDihedralBendConstraints.push_back(temp_bends[idx]); - - // Free closest kinematic buffer - mClosestKinematic.clear(); - mClosestKinematic.shrink_to_fit(); -} - -Ref SoftBodySharedSettings::Clone() const -{ - Ref clone = new SoftBodySharedSettings; - clone->mVertices = mVertices; - clone->mFaces = mFaces; - clone->mEdgeConstraints = mEdgeConstraints; - clone->mEdgeGroupEndIndices = mEdgeGroupEndIndices; - clone->mDihedralBendConstraints = mDihedralBendConstraints; - clone->mVolumeConstraints = mVolumeConstraints; - clone->mSkinnedConstraints = mSkinnedConstraints; - clone->mSkinnedConstraintNormals = mSkinnedConstraintNormals; - clone->mInvBindMatrices = mInvBindMatrices; - clone->mLRAConstraints = mLRAConstraints; - clone->mMaterials = mMaterials; - clone->mVertexRadius = mVertexRadius; - return clone; -} - -void SoftBodySharedSettings::SaveBinaryState(StreamOut &inStream) const -{ - inStream.Write(mVertices); - inStream.Write(mFaces); - inStream.Write(mEdgeConstraints); - inStream.Write(mEdgeGroupEndIndices); - inStream.Write(mDihedralBendConstraints); - inStream.Write(mVolumeConstraints); - inStream.Write(mSkinnedConstraints); - inStream.Write(mSkinnedConstraintNormals); - inStream.Write(mLRAConstraints); - inStream.Write(mVertexRadius); - - // Can't write mInvBindMatrices directly because the class contains padding - inStream.Write(mInvBindMatrices, [](const InvBind &inElement, StreamOut &inS) { - inS.Write(inElement.mJointIndex); - inS.Write(inElement.mInvBind); - }); -} - -void SoftBodySharedSettings::RestoreBinaryState(StreamIn &inStream) -{ - inStream.Read(mVertices); - inStream.Read(mFaces); - inStream.Read(mEdgeConstraints); - inStream.Read(mEdgeGroupEndIndices); - inStream.Read(mDihedralBendConstraints); - inStream.Read(mVolumeConstraints); - inStream.Read(mSkinnedConstraints); - inStream.Read(mSkinnedConstraintNormals); - inStream.Read(mLRAConstraints); - inStream.Read(mVertexRadius); - - inStream.Read(mInvBindMatrices, [](StreamIn &inS, InvBind &outElement) { - inS.Read(outElement.mJointIndex); - inS.Read(outElement.mInvBind); - }); -} - -void SoftBodySharedSettings::SaveWithMaterials(StreamOut &inStream, SharedSettingsToIDMap &ioSettingsMap, MaterialToIDMap &ioMaterialMap) const -{ - SharedSettingsToIDMap::const_iterator settings_iter = ioSettingsMap.find(this); - if (settings_iter == ioSettingsMap.end()) - { - // Write settings ID - uint32 settings_id = (uint32)ioSettingsMap.size(); - ioSettingsMap[this] = settings_id; - inStream.Write(settings_id); - - // Write the settings - SaveBinaryState(inStream); - - // Write materials - StreamUtils::SaveObjectArray(inStream, mMaterials, &ioMaterialMap); - } - else - { - // Known settings, just write the ID - inStream.Write(settings_iter->second); - } -} - -SoftBodySharedSettings::SettingsResult SoftBodySharedSettings::sRestoreWithMaterials(StreamIn &inStream, IDToSharedSettingsMap &ioSettingsMap, IDToMaterialMap &ioMaterialMap) -{ - SettingsResult result; - - // Read settings id - uint32 settings_id; - inStream.Read(settings_id); - if (inStream.IsEOF() || inStream.IsFailed()) - { - result.SetError("Failed to read settings id"); - return result; - } - - // Check nullptr settings - if (settings_id == ~uint32(0)) - { - result.Set(nullptr); - return result; - } - - // Check if we already read this settings - if (settings_id < ioSettingsMap.size()) - { - result.Set(ioSettingsMap[settings_id]); - return result; - } - - // Create new object - Ref settings = new SoftBodySharedSettings; - - // Read state - settings->RestoreBinaryState(inStream); - - // Read materials - Result mlresult = StreamUtils::RestoreObjectArray(inStream, ioMaterialMap); - if (mlresult.HasError()) - { - result.SetError(mlresult.GetError()); - return result; - } - settings->mMaterials = mlresult.Get(); - - // Add the settings to the map - ioSettingsMap.push_back(settings); - - result.Set(settings); - return result; -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodySharedSettings.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodySharedSettings.h deleted file mode 100644 index 250bd68917d..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodySharedSettings.h +++ /dev/null @@ -1,322 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2023 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -/// This class defines the setup of all particles and their constraints. -/// It is used during the simulation and can be shared between multiple soft bodies. -class JPH_EXPORT SoftBodySharedSettings : public RefTarget -{ -public: - JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, SoftBodySharedSettings) - - /// Which type of bend constraint should be created - enum class EBendType - { - None, ///< No bend constraints will be created - Distance, ///< A simple distance constraint - Dihedral, ///< A dihedral bend constraint (most expensive, but also supports triangles that are initially not in the same plane) - }; - - /// The type of long range attachment constraint to create - enum class ELRAType - { - None, ///< Don't create a LRA constraint - EuclideanDistance, ///< Create a LRA constraint based on Euclidean distance between the closest kinematic vertex and this vertex - GeodesicDistance, ///< Create a LRA constraint based on the geodesic distance between the closest kinematic vertex and this vertex (follows the edge constraints) - }; - - /// Per vertex attributes used during the CreateConstraints function. - /// For an edge or shear constraint, the compliance is averaged between the two attached vertices. - /// For a bend constraint, the compliance is averaged between the two vertices on the shared edge. - struct JPH_EXPORT VertexAttributes - { - /// Constructor - VertexAttributes() = default; - VertexAttributes(float inCompliance, float inShearCompliance, float inBendCompliance, ELRAType inLRAType = ELRAType::None, float inLRAMaxDistanceMultiplier = 1.0f) : mCompliance(inCompliance), mShearCompliance(inShearCompliance), mBendCompliance(inBendCompliance), mLRAType(inLRAType), mLRAMaxDistanceMultiplier(inLRAMaxDistanceMultiplier) { } - - float mCompliance = 0.0f; ///< The compliance of the normal edges. Set to FLT_MAX to disable regular edges for any edge involving this vertex. - float mShearCompliance = 0.0f; ///< The compliance of the shear edges. Set to FLT_MAX to disable shear edges for any edge involving this vertex. - float mBendCompliance = FLT_MAX; ///< The compliance of the bend edges. Set to FLT_MAX to disable bend edges for any bend constraint involving this vertex. - ELRAType mLRAType = ELRAType::None; ///< The type of long range attachment constraint to create. - float mLRAMaxDistanceMultiplier = 1.0f; ///< Multiplier for the max distance of the LRA constraint, e.g. 1.01 means the max distance is 1% longer than the calculated distance in the rest pose. - }; - - /// Automatically create constraints based on the faces of the soft body - /// @param inVertexAttributes A list of attributes for each vertex (1-on-1 with mVertices, note that if the list is smaller than mVertices the last element will be repeated). This defines the properties of the constraints that are created. - /// @param inVertexAttributesLength The length of inVertexAttributes - /// @param inBendType The type of bend constraint to create - /// @param inAngleTolerance Shear edges are created when two connected triangles form a quad (are roughly in the same plane and form a square with roughly 90 degree angles). This defines the tolerance (in radians). - void CreateConstraints(const VertexAttributes *inVertexAttributes, uint inVertexAttributesLength, EBendType inBendType = EBendType::Distance, float inAngleTolerance = DegreesToRadians(8.0f)); - - /// Calculate the initial lengths of all springs of the edges of this soft body (if you use CreateConstraint, this is already done) - void CalculateEdgeLengths(); - - /// Calculate the max lengths for the long range attachment constraints based on Euclidean distance (if you use CreateConstraints, this is already done) - /// @param inMaxDistanceMultiplier Multiplier for the max distance of the LRA constraint, e.g. 1.01 means the max distance is 1% longer than the calculated distance in the rest pose. - void CalculateLRALengths(float inMaxDistanceMultiplier = 1.0f); - - /// Calculate the constants for the bend constraints (if you use CreateConstraints, this is already done) - void CalculateBendConstraintConstants(); - - /// Calculates the initial volume of all tetrahedra of this soft body - void CalculateVolumeConstraintVolumes(); - - /// Calculate information needed to be able to calculate the skinned constraint normals at run-time - void CalculateSkinnedConstraintNormals(); - - /// Information about the optimization of the soft body, the indices of certain elements may have changed. - class OptimizationResults - { - public: - Array mEdgeRemap; ///< Maps old edge index to new edge index - Array mDihedralBendRemap; ///< Maps old dihedral bend index to new dihedral bend index - }; - - /// Optimize the soft body settings for simulation. This will reorder constraints so they can be executed in parallel. - void Optimize(OptimizationResults &outResults); - - /// Optimize the soft body settings without results - void Optimize() { OptimizationResults results; Optimize(results); } - - /// Clone this object - Ref Clone() const; - - /// Saves the state of this object in binary form to inStream. Doesn't store the material list. - void SaveBinaryState(StreamOut &inStream) const; - - /// Restore the state of this object from inStream. Doesn't restore the material list. - void RestoreBinaryState(StreamIn &inStream); - - using SharedSettingsToIDMap = StreamUtils::ObjectToIDMap; - using IDToSharedSettingsMap = StreamUtils::IDToObjectMap; - using MaterialToIDMap = StreamUtils::ObjectToIDMap; - using IDToMaterialMap = StreamUtils::IDToObjectMap; - - /// Save this shared settings and its materials. Pass in an empty map ioSettingsMap / ioMaterialMap or reuse the same map while saving multiple settings objects to the same stream in order to avoid writing duplicates. - void SaveWithMaterials(StreamOut &inStream, SharedSettingsToIDMap &ioSettingsMap, MaterialToIDMap &ioMaterialMap) const; - - using SettingsResult = Result>; - - /// Restore a shape and materials. Pass in an empty map in ioSettingsMap / ioMaterialMap or reuse the same map while reading multiple settings objects from the same stream in order to restore duplicates. - static SettingsResult sRestoreWithMaterials(StreamIn &inStream, IDToSharedSettingsMap &ioSettingsMap, IDToMaterialMap &ioMaterialMap); - - /// A vertex is a particle, the data in this structure is only used during creation of the soft body and not during simulation - struct JPH_EXPORT Vertex - { - JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, Vertex) - - /// Constructor - Vertex() = default; - Vertex(const Float3 &inPosition, const Float3 &inVelocity = Float3(0, 0, 0), float inInvMass = 1.0f) : mPosition(inPosition), mVelocity(inVelocity), mInvMass(inInvMass) { } - - Float3 mPosition { 0, 0, 0 }; ///< Initial position of the vertex - Float3 mVelocity { 0, 0, 0 }; ///< Initial velocity of the vertex - float mInvMass = 1.0f; ///< Initial inverse of the mass of the vertex - }; - - /// A face defines the surface of the body - struct JPH_EXPORT Face - { - JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, Face) - - /// Constructor - Face() = default; - Face(uint32 inVertex1, uint32 inVertex2, uint32 inVertex3, uint32 inMaterialIndex = 0) : mVertex { inVertex1, inVertex2, inVertex3 }, mMaterialIndex(inMaterialIndex) { } - - /// Check if this is a degenerate face (a face which points to the same vertex twice) - bool IsDegenerate() const { return mVertex[0] == mVertex[1] || mVertex[0] == mVertex[2] || mVertex[1] == mVertex[2]; } - - uint32 mVertex[3]; ///< Indices of the vertices that form the face - uint32 mMaterialIndex = 0; ///< Index of the material of the face in SoftBodySharedSettings::mMaterials - }; - - /// An edge keeps two vertices at a constant distance using a spring: |x1 - x2| = rest length - struct JPH_EXPORT Edge - { - JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, Edge) - - /// Constructor - Edge() = default; - Edge(uint32 inVertex1, uint32 inVertex2, float inCompliance = 0.0f) : mVertex { inVertex1, inVertex2 }, mCompliance(inCompliance) { } - - /// Return the lowest vertex index of this constraint - uint32 GetMinVertexIndex() const { return min(mVertex[0], mVertex[1]); } - - uint32 mVertex[2]; ///< Indices of the vertices that form the edge - float mRestLength = 1.0f; ///< Rest length of the spring - float mCompliance = 0.0f; ///< Inverse of the stiffness of the spring - }; - - /** - * A dihedral bend constraint keeps the angle between two triangles constant along their shared edge. - * - * x2 - * / \ - * / t0 \ - * x0----x1 - * \ t1 / - * \ / - * x3 - * - * x0..x3 are the vertices, t0 and t1 are the triangles that share the edge x0..x1 - * - * Based on: - * - "Position Based Dynamics" - Matthias Muller et al. - * - "Strain Based Dynamics" - Matthias Muller et al. - * - "Simulation of Clothing with Folds and Wrinkles" - R. Bridson et al. - */ - struct JPH_EXPORT DihedralBend - { - JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, DihedralBend) - - /// Constructor - DihedralBend() = default; - DihedralBend(uint32 inVertex1, uint32 inVertex2, uint32 inVertex3, uint32 inVertex4, float inCompliance = 0.0f) : mVertex { inVertex1, inVertex2, inVertex3, inVertex4 }, mCompliance(inCompliance) { } - - /// Return the lowest vertex index of this constraint - uint32 GetMinVertexIndex() const { return min(min(mVertex[0], mVertex[1]), min(mVertex[2], mVertex[3])); } - - uint32 mVertex[4]; ///< Indices of the vertices of the 2 triangles that share an edge (the first 2 vertices are the shared edge) - float mCompliance = 0.0f; ///< Inverse of the stiffness of the constraint - float mInitialAngle = 0.0f; ///< Initial angle between the normals of the triangles (pi - dihedral angle). - }; - - /// Volume constraint, keeps the volume of a tetrahedron constant - struct JPH_EXPORT Volume - { - JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, Volume) - - /// Constructor - Volume() = default; - Volume(uint32 inVertex1, uint32 inVertex2, uint32 inVertex3, uint32 inVertex4, float inCompliance = 0.0f) : mVertex { inVertex1, inVertex2, inVertex3, inVertex4 }, mCompliance(inCompliance) { } - - /// Return the lowest vertex index of this constraint - uint32 GetMinVertexIndex() const { return min(min(mVertex[0], mVertex[1]), min(mVertex[2], mVertex[3])); } - - uint32 mVertex[4]; ///< Indices of the vertices that form the tetrhedron - float mSixRestVolume = 1.0f; ///< 6 times the rest volume of the tetrahedron (calculated by CalculateVolumeConstraintVolumes()) - float mCompliance = 0.0f; ///< Inverse of the stiffness of the constraint - }; - - /// An inverse bind matrix take a skinned vertex from its bind pose into joint local space - class JPH_EXPORT InvBind - { - JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, InvBind) - - public: - /// Constructor - InvBind() = default; - InvBind(uint32 inJointIndex, Mat44Arg inInvBind) : mJointIndex(inJointIndex), mInvBind(inInvBind) { } - - uint32 mJointIndex = 0; ///< Joint index to which this is attached - Mat44 mInvBind = Mat44::sIdentity(); ///< The inverse bind matrix, this takes a vertex in its bind pose (Vertex::mPosition) to joint local space - }; - - /// A joint and its skin weight - class JPH_EXPORT SkinWeight - { - JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, SkinWeight) - - public: - /// Constructor - SkinWeight() = default; - SkinWeight(uint32 inInvBindIndex, float inWeight) : mInvBindIndex(inInvBindIndex), mWeight(inWeight) { } - - uint32 mInvBindIndex = 0; ///< Index in mInvBindMatrices - float mWeight = 0.0f; ///< Weight with which it is skinned - }; - - /// A constraint that skins a vertex to joints and limits the distance that the simulated vertex can travel from this vertex - class JPH_EXPORT Skinned - { - JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, Skinned) - - public: - /// Constructor - Skinned() = default; - Skinned(uint32 inVertex, float inMaxDistance, float inBackStopDistance, float inBackStopRadius) : mVertex(inVertex), mMaxDistance(inMaxDistance), mBackStopDistance(inBackStopDistance), mBackStopRadius(inBackStopRadius) { } - - /// Normalize the weights so that they add up to 1 - void NormalizeWeights() - { - // Get the total weight - float total = 0.0f; - for (const SkinWeight &w : mWeights) - total += w.mWeight; - - // Normalize - if (total > 0.0f) - for (SkinWeight &w : mWeights) - w.mWeight /= total; - } - - uint32 mVertex = 0; ///< Index in mVertices which indicates which vertex is being skinned - SkinWeight mWeights[4]; ///< Skin weights, the bind pose of the vertex is assumed to be stored in Vertex::mPosition. The first weight that is zero indicates the end of the list. Weights should add up to 1. - float mMaxDistance = FLT_MAX; ///< Maximum distance that this vertex can reach from the skinned vertex, disabled when FLT_MAX. 0 when you want to hard skin the vertex to the skinned vertex. - float mBackStopDistance = FLT_MAX; ///< Disabled if mBackStopDistance >= mMaxDistance. The faces surrounding mVertex determine an average normal. mBackStopDistance behind the vertex in the opposite direction of this normal, the back stop sphere starts. The simulated vertex will be pushed out of this sphere and it can be used to approximate the volume of she skinned mesh behind the skinned vertex. - float mBackStopRadius = 40.0f; ///< Radius of the backstop sphere. By default this is a fairly large radius so the sphere approximates a plane. - uint32 mNormalInfo = 0; ///< Information needed to calculate the normal of this vertex, lowest 24 bit is start index in mSkinnedConstraintNormals, highest 8 bit is number of faces (generated by CalculateSkinnedConstraintNormals()) - }; - - /// A long range attachment constraint, this is a constraint that sets a max distance between a kinematic vertex and a dynamic vertex - /// See: "Long Range Attachments - A Method to Simulate Inextensible Clothing in Computer Games", Tae-Yong Kim, Nuttapong Chentanez and Matthias Mueller-Fischer - class JPH_EXPORT LRA - { - JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, LRA) - - public: - /// Constructor - LRA() = default; - LRA(uint32 inVertex1, uint32 inVertex2, float inMaxDistance) : mVertex { inVertex1, inVertex2 }, mMaxDistance(inMaxDistance) { } - - /// Return the lowest vertex index of this constraint - uint32 GetMinVertexIndex() const { return min(mVertex[0], mVertex[1]); } - - uint32 mVertex[2]; ///< The vertices that are connected. The first vertex should be kinematic, the 2nd dynamic. - float mMaxDistance = 0.0f; ///< The maximum distance between the vertices - }; - - /// Add a face to this soft body - void AddFace(const Face &inFace) { JPH_ASSERT(!inFace.IsDegenerate()); mFaces.push_back(inFace); } - - Array mVertices; ///< The list of vertices or particles of the body - Array mFaces; ///< The list of faces of the body - Array mEdgeConstraints; ///< The list of edges or springs of the body - Array mDihedralBendConstraints; ///< The list of dihedral bend constraints of the body - Array mVolumeConstraints; ///< The list of volume constraints of the body that keep the volume of tetrahedra in the soft body constant - Array mSkinnedConstraints; ///< The list of vertices that are constrained to a skinned vertex - Array mInvBindMatrices; ///< The list of inverse bind matrices for skinning vertices - Array mLRAConstraints; ///< The list of long range attachment constraints - PhysicsMaterialList mMaterials { PhysicsMaterial::sDefault }; ///< The materials of the faces of the body, referenced by Face::mMaterialIndex - float mVertexRadius = 0.0f; ///< How big the particles are, can be used to push the vertices a little bit away from the surface of other bodies to prevent z-fighting - -private: - friend class SoftBodyMotionProperties; - - /// Tracks the closest kinematic vertex - struct ClosestKinematic - { - uint32 mVertex = 0xffffffff; ///< Vertex index of closest kinematic vertex - float mDistance = FLT_MAX; ///< Distance to the closest kinematic vertex - }; - - /// Calculate the closest kinematic vertex array - void CalculateClosestKinematic(); - - /// Get the size of an edge group (edge groups can run in parallel) - uint GetEdgeGroupSize(uint inGroupIdx) const { return inGroupIdx == 0? mEdgeGroupEndIndices[0] : mEdgeGroupEndIndices[inGroupIdx] - mEdgeGroupEndIndices[inGroupIdx - 1]; } - - Array mClosestKinematic; ///< The closest kinematic vertex to each vertex in mVertices - Array mEdgeGroupEndIndices; ///< The start index of each group of edges that can be solved in parallel, calculated by Optimize() - Array mSkinnedConstraintNormals; ///< A list of indices in the mFaces array used by mSkinnedConstraints, calculated by CalculateSkinnedConstraintNormals() -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyUpdateContext.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyUpdateContext.h deleted file mode 100644 index cd50aea085c..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyUpdateContext.h +++ /dev/null @@ -1,67 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2023 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include - -JPH_NAMESPACE_BEGIN - -class Body; -class SoftBodyMotionProperties; -class SoftBodyContactListener; - -/// Temporary data used by the update of a soft body -class SoftBodyUpdateContext : public NonCopyable -{ -public: - static constexpr uint cVertexCollisionBatch = 64; ///< Number of vertices to process in a batch in DetermineCollisionPlanes - static constexpr uint cEdgeConstraintBatch = 256; ///< Number of edge constraints to process in a batch in ApplyEdgeConstraints - - // Input - Body * mBody; ///< Body that is being updated - SoftBodyMotionProperties * mMotionProperties; ///< Motion properties of that body - SoftBodyContactListener * mContactListener; ///< Contact listener to fire callbacks to - RMat44 mCenterOfMassTransform; ///< Transform of the body relative to the soft body - Vec3 mGravity; ///< Gravity vector in local space of the soft body - Vec3 mDisplacementDueToGravity; ///< Displacement of the center of mass due to gravity in the current time step - float mDeltaTime; ///< Delta time for the current time step - float mSubStepDeltaTime; ///< Delta time for each sub step - - /// Describes progress in the current update - enum class EState - { - DetermineCollisionPlanes, ///< Determine collision planes for vertices in parallel - ApplyEdgeConstraints, ///< Apply edge constraints in parallel - Done ///< Update is finished - }; - - /// Construct the edge constraint iterator starting at a new group - static inline uint64 sGetEdgeGroupStart(uint32 inGroup) - { - return uint64(inGroup) << 32; - } - - /// Get the group and start index from the edge constraint iterator - static inline void sGetEdgeGroupAndStartIdx(uint64 inNextEdgeConstraint, uint32 &outGroup, uint32 &outStartIdx) - { - outGroup = uint32(inNextEdgeConstraint >> 32); - outStartIdx = uint32(inNextEdgeConstraint); - } - - // State of the update - atomic mState { EState::DetermineCollisionPlanes };///< Current state of the update - atomic mNextCollisionVertex { 0 }; ///< Next vertex to process for DetermineCollisionPlanes - atomic mNumCollisionVerticesProcessed { 0 }; ///< Number of vertices processed by DetermineCollisionPlanes, used to determine if we can start simulating - atomic mNextIteration { 0 }; ///< Next simulation iteration to process - atomic mNextEdgeConstraint { 0 }; ///< Next edge constraint group and start index to process - atomic mNumEdgeConstraintsProcessed { 0 }; ///< Number of edge constraints processed by ApplyEdgeConstraints, used to determine if we can go to the next group / iteration - - // Output - Vec3 mDeltaPosition; ///< Delta position of the body in the current time step, should be applied after the update - ECanSleep mCanSleep; ///< Can the body sleep? Should be applied after the update -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyVertex.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyVertex.h deleted file mode 100644 index 34e7bb32fbb..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/SoftBody/SoftBodyVertex.h +++ /dev/null @@ -1,28 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2023 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -/// Run time information for a single particle of a soft body -/// Note that at run-time you should only modify the inverse mass and/or velocity of a vertex to control the soft body. -/// Modifying the position can lead to missed collisions. -/// The other members are used internally by the soft body solver. -class SoftBodyVertex -{ -public: - Vec3 mPreviousPosition; ///< Position at the previous time step - Vec3 mPosition; ///< Position, relative to the center of mass of the soft body - Vec3 mVelocity; ///< Velocity, relative to the center of mass of the soft body - Plane mCollisionPlane; ///< Nearest collision plane, relative to the center of mass of the soft body - int mCollidingShapeIndex; ///< Index in the colliding shapes list of the body we may collide with - bool mHasContact; ///< True if the vertex has collided with anything in the last update - float mLargestPenetration; ///< Used while finding the collision plane, stores the largest penetration found so far - float mInvMass; ///< Inverse mass (1 / mass) -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/StateRecorder.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/StateRecorder.h deleted file mode 100644 index 7ec0844e675..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/StateRecorder.h +++ /dev/null @@ -1,66 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include - -JPH_NAMESPACE_BEGIN - -class Body; -class Constraint; -class BodyID; - -/// A bit field that determines which aspects of the simulation to save -enum class EStateRecorderState : uint8 -{ - None = 0, ///< Save nothing - Global = 1, ///< Save global physics system state (delta time, gravity, etc.) - Bodies = 2, ///< Save the state of bodies - Contacts = 4, ///< Save the state of contacts - Constraints = 8, ///< Save the state of constraints - All = Global | Bodies | Contacts | Constraints ///< Save all state -}; - -/// User callbacks that allow determining which parts of the simulation should be saved by a StateRecorder -class JPH_EXPORT StateRecorderFilter -{ -public: - /// Destructor - virtual ~StateRecorderFilter() = default; - - /// If the state of a specific body should be saved - virtual bool ShouldSaveBody([[maybe_unused]] const Body &inBody) const { return true; } - - /// If the state of a specific constraint should be saved - virtual bool ShouldSaveConstraint([[maybe_unused]] const Constraint &inConstraint) const { return true; } - - /// If the state of a specific contact should be saved - virtual bool ShouldSaveContact([[maybe_unused]] const BodyID &inBody1, [[maybe_unused]] const BodyID &inBody2) const { return true; } -}; - -/// Class that records the state of a physics system. Can be used to check if the simulation is deterministic by putting the recorder in validation mode. -/// Can be used to restore the state to an earlier point in time. Note that only the state that is modified by the simulation is saved, configuration settings -/// like body friction or restitution, motion quality etc. are not saved and need to be saved by the user if desired. -class JPH_EXPORT StateRecorder : public StreamIn, public StreamOut -{ -public: - /// Constructor - StateRecorder() = default; - StateRecorder(const StateRecorder &inRHS) : mIsValidating(inRHS.mIsValidating) { } - - /// Sets the stream in validation mode. In this case the physics system ensures that before it calls ReadBytes that it will - /// ensure that those bytes contain the current state. This makes it possible to step and save the state, restore to the previous - /// step and step again and when the recorded state is not the same it can restore the expected state and any byte that changes - /// due to a ReadBytes function can be caught to find out which part of the simulation is not deterministic. - /// Note that validation only works when saving the full state of the simulation (EStateRecorderState::All, StateRecorderFilter == nullptr). - void SetValidating(bool inValidating) { mIsValidating = inValidating; } - bool IsValidating() const { return mIsValidating; } - -private: - bool mIsValidating = false; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/StateRecorderImpl.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/StateRecorderImpl.cpp deleted file mode 100644 index 7624135896e..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/StateRecorderImpl.cpp +++ /dev/null @@ -1,90 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include - -JPH_NAMESPACE_BEGIN - -void StateRecorderImpl::WriteBytes(const void *inData, size_t inNumBytes) -{ - mStream.write((const char *)inData, inNumBytes); -} - -void StateRecorderImpl::Rewind() -{ - mStream.seekg(0, std::stringstream::beg); -} - -void StateRecorderImpl::Clear() -{ - mStream.clear(); - mStream.str({}); -} - -void StateRecorderImpl::ReadBytes(void *outData, size_t inNumBytes) -{ - if (IsValidating()) - { - // Read data in temporary buffer to compare with current value - void *data = JPH_STACK_ALLOC(inNumBytes); - mStream.read((char *)data, inNumBytes); - if (memcmp(data, outData, inNumBytes) != 0) - { - // Mismatch, print error - Trace("Mismatch reading %u bytes", (uint)inNumBytes); - for (size_t i = 0; i < inNumBytes; ++i) - { - int b1 = reinterpret_cast(outData)[i]; - int b2 = reinterpret_cast(data)[i]; - if (b1 != b2) - Trace("Offset %d: %02X -> %02X", i, b1, b2); - } - JPH_BREAKPOINT; - } - - // Copy the temporary data to the final destination - memcpy(outData, data, inNumBytes); - return; - } - - mStream.read((char *)outData, inNumBytes); -} - -bool StateRecorderImpl::IsEqual(StateRecorderImpl &inReference) -{ - // Get length of new state - mStream.seekg(0, std::stringstream::end); - std::streamoff this_len = mStream.tellg(); - mStream.seekg(0, std::stringstream::beg); - - // Get length of old state - inReference.mStream.seekg(0, std::stringstream::end); - std::streamoff reference_len = inReference.mStream.tellg(); - inReference.mStream.seekg(0, std::stringstream::beg); - - // Compare size - bool fail = reference_len != this_len; - if (fail) - { - Trace("Failed to properly recover state, different stream length!"); - return false; - } - - // Compare byte by byte - for (std::streamoff i = 0, l = this_len; !fail && i < l; ++i) - { - fail = inReference.mStream.get() != mStream.get(); - if (fail) - { - Trace("Failed to properly recover state, different at offset %d!", (int)i); - return false; - } - } - - return true; -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/StateRecorderImpl.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/StateRecorderImpl.h deleted file mode 100644 index b852679c0b9..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/StateRecorderImpl.h +++ /dev/null @@ -1,47 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -/// Implementation of the StateRecorder class that uses a stringstream as underlying store and that implements checking if the state doesn't change upon reading -class JPH_EXPORT StateRecorderImpl final : public StateRecorder -{ -public: - /// Constructor - StateRecorderImpl() = default; - StateRecorderImpl(StateRecorderImpl &&inRHS) : StateRecorder(inRHS), mStream(std::move(inRHS.mStream)) { } - - /// Write a string of bytes to the binary stream - virtual void WriteBytes(const void *inData, size_t inNumBytes) override; - - /// Rewind the stream for reading - void Rewind(); - - /// Clear the stream for reuse - void Clear(); - - /// Read a string of bytes from the binary stream - virtual void ReadBytes(void *outData, size_t inNumBytes) override; - - // See StreamIn - virtual bool IsEOF() const override { return mStream.eof(); } - - // See StreamIn / StreamOut - virtual bool IsFailed() const override { return mStream.fail(); } - - /// Compare this state with a reference state and ensure they are the same - bool IsEqual(StateRecorderImpl &inReference); - - /// Convert the binary data to a string - string GetData() const { return mStream.str(); } - -private: - std::stringstream mStream; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/MotorcycleController.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/MotorcycleController.cpp deleted file mode 100644 index 533d4cfc4ab..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/MotorcycleController.cpp +++ /dev/null @@ -1,293 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2023 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include -#include -#include -#ifdef JPH_DEBUG_RENDERER - #include -#endif // JPH_DEBUG_RENDERER - -JPH_NAMESPACE_BEGIN - -JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(MotorcycleControllerSettings) -{ - JPH_ADD_BASE_CLASS(MotorcycleControllerSettings, VehicleControllerSettings) - - JPH_ADD_ATTRIBUTE(MotorcycleControllerSettings, mMaxLeanAngle) - JPH_ADD_ATTRIBUTE(MotorcycleControllerSettings, mLeanSpringConstant) - JPH_ADD_ATTRIBUTE(MotorcycleControllerSettings, mLeanSpringDamping) - JPH_ADD_ATTRIBUTE(MotorcycleControllerSettings, mLeanSpringIntegrationCoefficient) - JPH_ADD_ATTRIBUTE(MotorcycleControllerSettings, mLeanSpringIntegrationCoefficientDecay) - JPH_ADD_ATTRIBUTE(MotorcycleControllerSettings, mLeanSmoothingFactor) -} - -VehicleController *MotorcycleControllerSettings::ConstructController(VehicleConstraint &inConstraint) const -{ - return new MotorcycleController(*this, inConstraint); -} - -void MotorcycleControllerSettings::SaveBinaryState(StreamOut &inStream) const -{ - WheeledVehicleControllerSettings::SaveBinaryState(inStream); - - inStream.Write(mMaxLeanAngle); - inStream.Write(mLeanSpringConstant); - inStream.Write(mLeanSpringDamping); - inStream.Write(mLeanSpringIntegrationCoefficient); - inStream.Write(mLeanSpringIntegrationCoefficientDecay); - inStream.Write(mLeanSmoothingFactor); -} - -void MotorcycleControllerSettings::RestoreBinaryState(StreamIn &inStream) -{ - WheeledVehicleControllerSettings::RestoreBinaryState(inStream); - - inStream.Read(mMaxLeanAngle); - inStream.Read(mLeanSpringConstant); - inStream.Read(mLeanSpringDamping); - inStream.Read(mLeanSpringIntegrationCoefficient); - inStream.Read(mLeanSpringIntegrationCoefficientDecay); - inStream.Read(mLeanSmoothingFactor); -} - -MotorcycleController::MotorcycleController(const MotorcycleControllerSettings &inSettings, VehicleConstraint &inConstraint) : - WheeledVehicleController(inSettings, inConstraint), - mMaxLeanAngle(inSettings.mMaxLeanAngle), - mLeanSpringConstant(inSettings.mLeanSpringConstant), - mLeanSpringDamping(inSettings.mLeanSpringDamping), - mLeanSpringIntegrationCoefficient(inSettings.mLeanSpringIntegrationCoefficient), - mLeanSpringIntegrationCoefficientDecay(inSettings.mLeanSpringIntegrationCoefficientDecay), - mLeanSmoothingFactor(inSettings.mLeanSmoothingFactor) -{ -} - -float MotorcycleController::GetWheelBase() const -{ - float low = FLT_MAX, high = -FLT_MAX; - - for (const Wheel *w : mConstraint.GetWheels()) - { - const WheelSettings *s = w->GetSettings(); - - // Measure distance along the forward axis by looking at the fully extended suspension. - // If the suspension force point is active, use that instead. - Vec3 force_point = s->mEnableSuspensionForcePoint? s->mSuspensionForcePoint : s->mPosition + s->mSuspensionDirection * s->mSuspensionMaxLength; - float value = force_point.Dot(mConstraint.GetLocalForward()); - - // Update min and max - low = min(low, value); - high = max(high, value); - } - - return high - low; -} - -void MotorcycleController::PreCollide(float inDeltaTime, PhysicsSystem &inPhysicsSystem) -{ - WheeledVehicleController::PreCollide(inDeltaTime, inPhysicsSystem); - - const Body *body = mConstraint.GetVehicleBody(); - Vec3 forward = body->GetRotation() * mConstraint.GetLocalForward(); - float wheel_base = GetWheelBase(); - Vec3 world_up = mConstraint.GetWorldUp(); - - if (mEnableLeanController) - { - // Calculate the target lean vector, this is in the direction of the total applied impulse by the ground on the wheels - Vec3 target_lean = Vec3::sZero(); - for (const Wheel *w : mConstraint.GetWheels()) - if (w->HasContact()) - target_lean += w->GetContactNormal() * w->GetSuspensionLambda() + w->GetContactLateral() * w->GetLateralLambda(); - - // Normalize the impulse - target_lean = target_lean.NormalizedOr(world_up); - - // Smooth the impulse to avoid jittery behavior - mTargetLean = mLeanSmoothingFactor * mTargetLean + (1.0f - mLeanSmoothingFactor) * target_lean; - - // Remove forward component, we can only lean sideways - mTargetLean -= forward * mTargetLean.Dot(forward); - mTargetLean = mTargetLean.NormalizedOr(world_up); - - // Clamp the target lean against the max lean angle - Vec3 adjusted_world_up = world_up - forward * world_up.Dot(forward); - adjusted_world_up = adjusted_world_up.NormalizedOr(world_up); - float w_angle = -Sign(mTargetLean.Cross(adjusted_world_up).Dot(forward)) * ACos(mTargetLean.Dot(adjusted_world_up)); - if (abs(w_angle) > mMaxLeanAngle) - mTargetLean = Quat::sRotation(forward, Sign(w_angle) * mMaxLeanAngle) * adjusted_world_up; - - // Integrate the delta angle - Vec3 up = body->GetRotation() * mConstraint.GetLocalUp(); - float d_angle = -Sign(mTargetLean.Cross(up).Dot(forward)) * ACos(mTargetLean.Dot(up)); - mLeanSpringIntegratedDeltaAngle += d_angle * inDeltaTime; - } - else - { - // Controller not enabled, reset target lean - mTargetLean = world_up; - - // Reset integrated delta angle - mLeanSpringIntegratedDeltaAngle = 0; - } - - JPH_DET_LOG("WheeledVehicleController::PreCollide: mTargetLean: " << mTargetLean); - - // Calculate max steering angle based on the max lean angle we're willing to take - // See: https://en.wikipedia.org/wiki/Bicycle_and_motorcycle_dynamics#Leaning - // LeanAngle = Atan(Velocity^2 / (Gravity * TurnRadius)) - // And: https://en.wikipedia.org/wiki/Turning_radius (we're ignoring the tire width) - // The CasterAngle is the added according to https://en.wikipedia.org/wiki/Bicycle_and_motorcycle_dynamics#Turning (this is the same formula but without small angle approximation) - // TurnRadius = WheelBase / (Sin(SteerAngle) * Cos(CasterAngle)) - // => SteerAngle = ASin(WheelBase * Tan(LeanAngle) * Gravity / (Velocity^2 * Cos(CasterAngle)) - // The caster angle is different for each wheel so we can only calculate part of the equation here - float max_steer_angle_factor = wheel_base * Tan(mMaxLeanAngle) * inPhysicsSystem.GetGravity().Length(); - - // Calculate forward velocity - float velocity = body->GetLinearVelocity().Dot(forward); - float velocity_sq = Square(velocity); - - // Decompose steering into sign and direction - float steer_strength = abs(mRightInput); - float steer_sign = -Sign(mRightInput); - - for (Wheel *w_base : mConstraint.GetWheels()) - { - WheelWV *w = static_cast(w_base); - const WheelSettingsWV *s = w->GetSettings(); - - // Check if this wheel can steer - if (s->mMaxSteerAngle != 0.0f) - { - // Calculate cos(caster angle), the angle between the steering axis and the up vector - float cos_caster_angle = s->mSteeringAxis.Dot(mConstraint.GetLocalUp()); - - // Calculate steer angle - float steer_angle = steer_strength * w->GetSettings()->mMaxSteerAngle; - - // Clamp to max steering angle - if (mEnableLeanSteeringLimit - && velocity_sq > 1.0e-6f && cos_caster_angle > 1.0e-6f) - { - float max_steer_angle = ASin(max_steer_angle_factor / (velocity_sq * cos_caster_angle)); - steer_angle = min(steer_angle, max_steer_angle); - } - - // Set steering angle - w->SetSteerAngle(steer_sign * steer_angle); - } - } - - // Reset applied impulse - mAppliedImpulse = 0; -} - -bool MotorcycleController::SolveLongitudinalAndLateralConstraints(float inDeltaTime) -{ - bool impulse = WheeledVehicleController::SolveLongitudinalAndLateralConstraints(inDeltaTime); - - if (mEnableLeanController) - { - // Only apply a lean impulse if all wheels are in contact, otherwise we can easily spin out - bool all_in_contact = true; - for (const Wheel *w : mConstraint.GetWheels()) - if (!w->HasContact() || w->GetSuspensionLambda() <= 0.0f) - { - all_in_contact = false; - break; - } - - if (all_in_contact) - { - Body *body = mConstraint.GetVehicleBody(); - const MotionProperties *mp = body->GetMotionProperties(); - - Vec3 forward = body->GetRotation() * mConstraint.GetLocalForward(); - Vec3 up = body->GetRotation() * mConstraint.GetLocalUp(); - - // Calculate delta to target angle and derivative - float d_angle = -Sign(mTargetLean.Cross(up).Dot(forward)) * ACos(mTargetLean.Dot(up)); - float ddt_angle = body->GetAngularVelocity().Dot(forward); - - // Calculate impulse to apply to get to target lean angle - float total_impulse = (mLeanSpringConstant * d_angle - mLeanSpringDamping * ddt_angle + mLeanSpringIntegrationCoefficient * mLeanSpringIntegratedDeltaAngle) * inDeltaTime; - - // Remember angular velocity pre angular impulse - Vec3 old_w = mp->GetAngularVelocity(); - - // Apply impulse taking into account the impulse we've applied earlier - float delta_impulse = total_impulse - mAppliedImpulse; - body->AddAngularImpulse(delta_impulse * forward); - mAppliedImpulse = total_impulse; - - // Calculate delta angular velocity due to angular impulse - Vec3 dw = mp->GetAngularVelocity() - old_w; - Vec3 linear_acceleration = Vec3::sZero(); - float total_lambda = 0.0f; - for (Wheel *w_base : mConstraint.GetWheels()) - { - const WheelWV *w = static_cast(w_base); - - // We weigh the importance of each contact point according to the contact force - float lambda = w->GetSuspensionLambda(); - total_lambda += lambda; - - // Linear acceleration of contact point is dw x com_to_contact - Vec3 r = Vec3(w->GetContactPosition() - body->GetCenterOfMassPosition()); - linear_acceleration += lambda * dw.Cross(r); - } - - // Apply linear impulse to COM to cancel the average velocity change on the wheels due to the angular impulse - Vec3 linear_impulse = -linear_acceleration / (total_lambda * mp->GetInverseMass()); - body->AddImpulse(linear_impulse); - - // Return true if we applied an impulse - impulse |= delta_impulse != 0.0f; - } - else - { - // Decay the integrated angle because we won't be applying a torque this frame - // Uses 1st order Taylor approximation of e^(-decay * dt) = 1 - decay * dt - mLeanSpringIntegratedDeltaAngle *= max(0.0f, 1.0f - mLeanSpringIntegrationCoefficientDecay * inDeltaTime); - } - } - - return impulse; -} - -void MotorcycleController::SaveState(StateRecorder &inStream) const -{ - WheeledVehicleController::SaveState(inStream); - - inStream.Write(mTargetLean); -} - -void MotorcycleController::RestoreState(StateRecorder &inStream) -{ - WheeledVehicleController::RestoreState(inStream); - - inStream.Read(mTargetLean); -} - -#ifdef JPH_DEBUG_RENDERER - -void MotorcycleController::Draw(DebugRenderer *inRenderer) const -{ - WheeledVehicleController::Draw(inRenderer); - - // Draw current and desired lean angle - Body *body = mConstraint.GetVehicleBody(); - RVec3 center_of_mass = body->GetCenterOfMassPosition(); - Vec3 up = body->GetRotation() * mConstraint.GetLocalUp(); - inRenderer->DrawArrow(center_of_mass, center_of_mass + up, Color::sYellow, 0.1f); - inRenderer->DrawArrow(center_of_mass, center_of_mass + mTargetLean, Color::sRed, 0.1f); -} - -#endif // JPH_DEBUG_RENDERER - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/MotorcycleController.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/MotorcycleController.h deleted file mode 100644 index 59ee17cdc9d..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/MotorcycleController.h +++ /dev/null @@ -1,116 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2023 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -/// Settings of a two wheeled motorcycle (adds a spring to balance the motorcycle) -/// Note: The motor cycle controller is still in development and may need a lot of tweaks/hacks to work properly! -class JPH_EXPORT MotorcycleControllerSettings : public WheeledVehicleControllerSettings -{ -public: - JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, MotorcycleControllerSettings) - - // See: VehicleControllerSettings - virtual VehicleController * ConstructController(VehicleConstraint &inConstraint) const override; - virtual void SaveBinaryState(StreamOut &inStream) const override; - virtual void RestoreBinaryState(StreamIn &inStream) override; - - /// How far we're willing to make the bike lean over in turns (in radians) - float mMaxLeanAngle = DegreesToRadians(45.0f); - - /// Spring constant for the lean spring - float mLeanSpringConstant = 5000.0f; - - /// Spring damping constant for the lean spring - float mLeanSpringDamping = 1000.0f; - - /// The lean spring applies an additional force equal to this coefficient * Integral(delta angle, 0, t), this effectively makes the lean spring a PID controller - float mLeanSpringIntegrationCoefficient = 0.0f; - - /// How much to decay the angle integral when the wheels are not touching the floor: new_value = e^(-decay * t) * initial_value - float mLeanSpringIntegrationCoefficientDecay = 4.0f; - - /// How much to smooth the lean angle (0 = no smoothing, 1 = lean angle never changes) - /// Note that this is frame rate dependent because the formula is: smoothing_factor * previous + (1 - smoothing_factor) * current - float mLeanSmoothingFactor = 0.8f; -}; - -/// Runtime controller class -class JPH_EXPORT MotorcycleController : public WheeledVehicleController -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Constructor - MotorcycleController(const MotorcycleControllerSettings &inSettings, VehicleConstraint &inConstraint); - - /// Get the distance between the front and back wheels - float GetWheelBase() const; - - /// Enable or disable the lean spring. This allows you to temporarily disable the lean spring to allow the motorcycle to fall over. - void EnableLeanController(bool inEnable) { mEnableLeanController = inEnable; } - - /// Check if the lean spring is enabled. - bool IsLeanControllerEnabled() const { return mEnableLeanController; } - - /// Enable or disable the lean steering limit. When enabled (default) the steering angle is limited based on the vehicle speed to prevent steering that would cause an inertial force that causes the motorcycle to topple over. - void EnableLeanSteeringLimit(bool inEnable) { mEnableLeanSteeringLimit = inEnable; } - bool IsLeanSteeringLimitEnabled() const { return mEnableLeanSteeringLimit; } - - /// Spring constant for the lean spring - void SetLeanSpringConstant(float inConstant) { mLeanSpringConstant = inConstant; } - float GetLeanSpringConstant() const { return mLeanSpringConstant; } - - /// Spring damping constant for the lean spring - void SetLeanSpringDamping(float inDamping) { mLeanSpringDamping = inDamping; } - float GetLeanSpringDamping() const { return mLeanSpringDamping; } - - /// The lean spring applies an additional force equal to this coefficient * Integral(delta angle, 0, t), this effectively makes the lean spring a PID controller - void SetLeanSpringIntegrationCoefficient(float inCoefficient) { mLeanSpringIntegrationCoefficient = inCoefficient; } - float GetLeanSpringIntegrationCoefficient() const { return mLeanSpringIntegrationCoefficient; } - - /// How much to decay the angle integral when the wheels are not touching the floor: new_value = e^(-decay * t) * initial_value - void SetLeanSpringIntegrationCoefficientDecay(float inDecay) { mLeanSpringIntegrationCoefficientDecay = inDecay; } - float GetLeanSpringIntegrationCoefficientDecay() const { return mLeanSpringIntegrationCoefficientDecay; } - - /// How much to smooth the lean angle (0 = no smoothing, 1 = lean angle never changes) - /// Note that this is frame rate dependent because the formula is: smoothing_factor * previous + (1 - smoothing_factor) * current - void SetLeanSmoothingFactor(float inFactor) { mLeanSmoothingFactor = inFactor; } - float GetLeanSmoothingFactor() const { return mLeanSmoothingFactor; } - -protected: - // See: VehicleController - virtual void PreCollide(float inDeltaTime, PhysicsSystem &inPhysicsSystem) override; - virtual bool SolveLongitudinalAndLateralConstraints(float inDeltaTime) override; - virtual void SaveState(StateRecorder &inStream) const override; - virtual void RestoreState(StateRecorder &inStream) override; -#ifdef JPH_DEBUG_RENDERER - virtual void Draw(DebugRenderer *inRenderer) const override; -#endif // JPH_DEBUG_RENDERER - - // Configuration properties - bool mEnableLeanController = true; - bool mEnableLeanSteeringLimit = true; - float mMaxLeanAngle; - float mLeanSpringConstant; - float mLeanSpringDamping; - float mLeanSpringIntegrationCoefficient; - float mLeanSpringIntegrationCoefficientDecay; - float mLeanSmoothingFactor; - - // Run-time calculated target lean vector - Vec3 mTargetLean = Vec3::sZero(); - - // Integrated error for the lean spring - float mLeanSpringIntegratedDeltaAngle = 0.0f; - - // Run-time total angular impulse applied to turn the cycle towards the target lean angle - float mAppliedImpulse = 0.0f; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/TrackedVehicleController.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/TrackedVehicleController.cpp deleted file mode 100644 index c941a829571..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/TrackedVehicleController.cpp +++ /dev/null @@ -1,531 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include -#include -#include -#ifdef JPH_DEBUG_RENDERER - #include -#endif // JPH_DEBUG_RENDERER - -JPH_NAMESPACE_BEGIN - -JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(TrackedVehicleControllerSettings) -{ - JPH_ADD_BASE_CLASS(TrackedVehicleControllerSettings, VehicleControllerSettings) - - JPH_ADD_ATTRIBUTE(TrackedVehicleControllerSettings, mEngine) - JPH_ADD_ATTRIBUTE(TrackedVehicleControllerSettings, mTransmission) - JPH_ADD_ATTRIBUTE(TrackedVehicleControllerSettings, mTracks) -} - -JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(WheelSettingsTV) -{ - JPH_ADD_ATTRIBUTE(WheelSettingsTV, mLongitudinalFriction) - JPH_ADD_ATTRIBUTE(WheelSettingsTV, mLateralFriction) -} - -void WheelSettingsTV::SaveBinaryState(StreamOut &inStream) const -{ - inStream.Write(mLongitudinalFriction); - inStream.Write(mLateralFriction); -} - -void WheelSettingsTV::RestoreBinaryState(StreamIn &inStream) -{ - inStream.Read(mLongitudinalFriction); - inStream.Read(mLateralFriction); -} - -WheelTV::WheelTV(const WheelSettingsTV &inSettings) : - Wheel(inSettings) -{ -} - -void WheelTV::CalculateAngularVelocity(const VehicleConstraint &inConstraint) -{ - const WheelSettingsTV *settings = GetSettings(); - const Wheels &wheels = inConstraint.GetWheels(); - const VehicleTrack &track = static_cast(inConstraint.GetController())->GetTracks()[mTrackIndex]; - - // Calculate angular velocity of this wheel - mAngularVelocity = track.mAngularVelocity * wheels[track.mDrivenWheel]->GetSettings()->mRadius / settings->mRadius; -} - -void WheelTV::Update(uint inWheelIndex, float inDeltaTime, const VehicleConstraint &inConstraint) -{ - CalculateAngularVelocity(inConstraint); - - // Update rotation of wheel - mAngle = fmod(mAngle + mAngularVelocity * inDeltaTime, 2.0f * JPH_PI); - - // Reset brake impulse, will be set during post collision again - mBrakeImpulse = 0.0f; - - if (mContactBody != nullptr) - { - // Friction at the point of this wheel between track and floor - const WheelSettingsTV *settings = GetSettings(); - VehicleConstraint::CombineFunction combine_friction = inConstraint.GetCombineFriction(); - mCombinedLongitudinalFriction = settings->mLongitudinalFriction; - mCombinedLateralFriction = settings->mLateralFriction; - combine_friction(inWheelIndex, mCombinedLongitudinalFriction, mCombinedLateralFriction, *mContactBody, mContactSubShapeID); - } - else - { - // No collision - mCombinedLongitudinalFriction = mCombinedLateralFriction = 0.0f; - } -} - -VehicleController *TrackedVehicleControllerSettings::ConstructController(VehicleConstraint &inConstraint) const -{ - return new TrackedVehicleController(*this, inConstraint); -} - -TrackedVehicleControllerSettings::TrackedVehicleControllerSettings() -{ - // Numbers guestimated from: https://en.wikipedia.org/wiki/M1_Abrams - mEngine.mMinRPM = 500.0f; - mEngine.mMaxRPM = 4000.0f; - mEngine.mMaxTorque = 500.0f; // Note actual torque for M1 is around 5000 but we need a reduced mass in order to keep the simulation sane - - mTransmission.mShiftDownRPM = 1000.0f; - mTransmission.mShiftUpRPM = 3500.0f; - mTransmission.mGearRatios = { 4.0f, 3.0f, 2.0f, 1.0f }; - mTransmission.mReverseGearRatios = { -4.0f, -3.0f }; -} - -void TrackedVehicleControllerSettings::SaveBinaryState(StreamOut &inStream) const -{ - mEngine.SaveBinaryState(inStream); - - mTransmission.SaveBinaryState(inStream); - - for (const VehicleTrackSettings &t : mTracks) - t.SaveBinaryState(inStream); -} - -void TrackedVehicleControllerSettings::RestoreBinaryState(StreamIn &inStream) -{ - mEngine.RestoreBinaryState(inStream); - - mTransmission.RestoreBinaryState(inStream); - - for (VehicleTrackSettings &t : mTracks) - t.RestoreBinaryState(inStream); -} - -TrackedVehicleController::TrackedVehicleController(const TrackedVehicleControllerSettings &inSettings, VehicleConstraint &inConstraint) : - VehicleController(inConstraint) -{ - // Copy engine settings - static_cast(mEngine) = inSettings.mEngine; - JPH_ASSERT(inSettings.mEngine.mMinRPM >= 0.0f); - JPH_ASSERT(inSettings.mEngine.mMinRPM <= inSettings.mEngine.mMaxRPM); - mEngine.SetCurrentRPM(mEngine.mMinRPM); - - // Copy transmission settings - static_cast(mTransmission) = inSettings.mTransmission; -#ifdef JPH_ENABLE_ASSERTS - for (float r : inSettings.mTransmission.mGearRatios) - JPH_ASSERT(r > 0.0f); - for (float r : inSettings.mTransmission.mReverseGearRatios) - JPH_ASSERT(r < 0.0f); -#endif // JPH_ENABLE_ASSERTS - JPH_ASSERT(inSettings.mTransmission.mSwitchTime >= 0.0f); - JPH_ASSERT(inSettings.mTransmission.mShiftDownRPM > 0.0f); - JPH_ASSERT(inSettings.mTransmission.mMode != ETransmissionMode::Auto || inSettings.mTransmission.mShiftUpRPM < inSettings.mEngine.mMaxRPM); - JPH_ASSERT(inSettings.mTransmission.mShiftUpRPM > inSettings.mTransmission.mShiftDownRPM); - - // Copy track settings - for (uint i = 0; i < size(mTracks); ++i) - { - const VehicleTrackSettings &d = inSettings.mTracks[i]; - static_cast(mTracks[i]) = d; - JPH_ASSERT(d.mInertia >= 0.0f); - JPH_ASSERT(d.mAngularDamping >= 0.0f); - JPH_ASSERT(d.mMaxBrakeTorque >= 0.0f); - JPH_ASSERT(d.mDifferentialRatio > 0.0f); - } -} - -bool TrackedVehicleController::AllowSleep() const -{ - return mForwardInput == 0.0f // No user input - && mTransmission.AllowSleep() // Transmission is not shifting - && mEngine.AllowSleep(); // Engine is idling -} - -void TrackedVehicleController::PreCollide(float inDeltaTime, PhysicsSystem &inPhysicsSystem) -{ - Wheels &wheels = mConstraint.GetWheels(); - - // Fill in track index - for (size_t t = 0; t < size(mTracks); ++t) - for (uint w : mTracks[t].mWheels) - static_cast(wheels[w])->mTrackIndex = (uint)t; - - // Angular damping: dw/dt = -c * w - // Solution: w(t) = w(0) * e^(-c * t) or w2 = w1 * e^(-c * dt) - // Taylor expansion of e^(-c * dt) = 1 - c * dt + ... - // Since dt is usually in the order of 1/60 and c is a low number too this approximation is good enough - for (VehicleTrack &t : mTracks) - t.mAngularVelocity *= max(0.0f, 1.0f - t.mAngularDamping * inDeltaTime); -} - -void TrackedVehicleController::SyncLeftRightTracks() -{ - // Apply left to right ratio according to track inertias - VehicleTrack &tl = mTracks[(int)ETrackSide::Left]; - VehicleTrack &tr = mTracks[(int)ETrackSide::Right]; - - if (mLeftRatio * mRightRatio > 0.0f) - { - // Solve: (tl.mAngularVelocity + dl) / (tr.mAngularVelocity + dr) = mLeftRatio / mRightRatio and dl * tr.mInertia = -dr * tl.mInertia, where dl/dr are the delta angular velocities for left and right tracks - float impulse = (mLeftRatio * tr.mAngularVelocity - mRightRatio * tl.mAngularVelocity) / (mLeftRatio * tr.mInertia + mRightRatio * tl.mInertia); - tl.mAngularVelocity += impulse * tl.mInertia; - tr.mAngularVelocity -= impulse * tr.mInertia; - } - else - { - // Solve: (tl.mAngularVelocity + dl) / (tr.mAngularVelocity + dr) = mLeftRatio / mRightRatio and dl * tr.mInertia = dr * tl.mInertia, where dl/dr are the delta angular velocities for left and right tracks - float impulse = (mLeftRatio * tr.mAngularVelocity - mRightRatio * tl.mAngularVelocity) / (mRightRatio * tl.mInertia - mLeftRatio * tr.mInertia); - tl.mAngularVelocity += impulse * tl.mInertia; - tr.mAngularVelocity += impulse * tr.mInertia; - } -} - -void TrackedVehicleController::PostCollide(float inDeltaTime, PhysicsSystem &inPhysicsSystem) -{ - JPH_PROFILE_FUNCTION(); - - Wheels &wheels = mConstraint.GetWheels(); - - // Update wheel angle, do this before applying torque to the wheels (as friction will slow them down again) - for (uint wheel_index = 0, num_wheels = (uint)wheels.size(); wheel_index < num_wheels; ++wheel_index) - { - WheelTV *w = static_cast(wheels[wheel_index]); - w->Update(wheel_index, inDeltaTime, mConstraint); - } - - // First calculate engine speed based on speed of all wheels - bool can_engine_apply_torque = false; - if (mTransmission.GetCurrentGear() != 0 && mTransmission.GetClutchFriction() > 1.0e-3f) - { - float transmission_ratio = mTransmission.GetCurrentRatio(); - bool forward = transmission_ratio >= 0.0f; - float fastest_wheel_speed = forward? -FLT_MAX : FLT_MAX; - for (const VehicleTrack &t : mTracks) - { - if (forward) - fastest_wheel_speed = max(fastest_wheel_speed, t.mAngularVelocity * t.mDifferentialRatio); - else - fastest_wheel_speed = min(fastest_wheel_speed, t.mAngularVelocity * t.mDifferentialRatio); - for (uint w : t.mWheels) - if (wheels[w]->HasContact()) - { - can_engine_apply_torque = true; - break; - } - } - - // Update RPM only if the tracks are connected to the engine - if (fastest_wheel_speed > -FLT_MAX && fastest_wheel_speed < FLT_MAX) - mEngine.SetCurrentRPM(fastest_wheel_speed * mTransmission.GetCurrentRatio() * VehicleEngine::cAngularVelocityToRPM); - } - else - { - // Update engine with damping - mEngine.ApplyDamping(inDeltaTime); - - // In auto transmission mode, don't accelerate the engine when switching gears - float forward_input = mTransmission.mMode == ETransmissionMode::Manual? abs(mForwardInput) : 0.0f; - - // Engine not connected to wheels, update RPM based on engine inertia alone - mEngine.ApplyTorque(mEngine.GetTorque(forward_input), inDeltaTime); - } - - // Update transmission - // Note: only allow switching gears up when the tracks are rolling in the same direction - mTransmission.Update(inDeltaTime, mEngine.GetCurrentRPM(), mForwardInput, mLeftRatio * mRightRatio > 0.0f && can_engine_apply_torque); - - // Calculate the amount of torque the transmission gives to the differentials - float transmission_ratio = mTransmission.GetCurrentRatio(); - float transmission_torque = mTransmission.GetClutchFriction() * transmission_ratio * mEngine.GetTorque(abs(mForwardInput)); - if (transmission_torque != 0.0f) - { - // Apply the transmission torque to the wheels - for (uint i = 0; i < size(mTracks); ++i) - { - VehicleTrack &t = mTracks[i]; - - // Get wheel rotation ratio for this track - float ratio = i == 0? mLeftRatio : mRightRatio; - - // Calculate the max angular velocity of the driven wheel of the track given current engine RPM - // Note this adds 0.1% slop to avoid numerical accuracy issues - float track_max_angular_velocity = mEngine.GetCurrentRPM() / (transmission_ratio * t.mDifferentialRatio * ratio * VehicleEngine::cAngularVelocityToRPM) * 1.001f; - - // Calculate torque on the driven wheel - float differential_torque = t.mDifferentialRatio * ratio * transmission_torque; - - // Apply torque to driven wheel - if (t.mAngularVelocity * track_max_angular_velocity < 0.0f || abs(t.mAngularVelocity) < abs(track_max_angular_velocity)) - t.mAngularVelocity += differential_torque * inDeltaTime / t.mInertia; - } - } - - // Ensure that we have the correct ratio between the two tracks - SyncLeftRightTracks(); - - // Braking - for (VehicleTrack &t : mTracks) - { - // Calculate brake torque - float brake_torque = mBrakeInput * t.mMaxBrakeTorque; - if (brake_torque > 0.0f) - { - // Calculate how much torque is needed to stop the track from rotating in this time step - float brake_torque_to_lock_track = abs(t.mAngularVelocity) * t.mInertia / inDeltaTime; - if (brake_torque > brake_torque_to_lock_track) - { - // Wheels are locked - t.mAngularVelocity = 0.0f; - brake_torque -= brake_torque_to_lock_track; - } - else - { - // Slow down the track - t.mAngularVelocity -= Sign(t.mAngularVelocity) * brake_torque * inDeltaTime / t.mInertia; - } - } - - if (brake_torque > 0.0f) - { - // Sum the radius of all wheels touching the floor - float total_radius = 0.0f; - for (uint wheel_index : t.mWheels) - { - const WheelTV *w = static_cast(wheels[wheel_index]); - - if (w->HasContact()) - total_radius += w->GetSettings()->mRadius; - } - - if (total_radius > 0.0f) - { - brake_torque /= total_radius; - for (uint wheel_index : t.mWheels) - { - WheelTV *w = static_cast(wheels[wheel_index]); - if (w->HasContact()) - { - // Impulse: p = F * dt = Torque / Wheel_Radius * dt, Torque = Total_Torque * Wheel_Radius / Summed_Radius => p = Total_Torque * dt / Summed_Radius - w->mBrakeImpulse = brake_torque * inDeltaTime; - } - } - } - } - } - - // Update wheel angular velocity based on that of the track - for (Wheel *w_base : wheels) - { - WheelTV *w = static_cast(w_base); - w->CalculateAngularVelocity(mConstraint); - } -} - -bool TrackedVehicleController::SolveLongitudinalAndLateralConstraints(float inDeltaTime) -{ - bool impulse = false; - - for (Wheel *w_base : mConstraint.GetWheels()) - if (w_base->HasContact()) - { - WheelTV *w = static_cast(w_base); - const WheelSettingsTV *settings = w->GetSettings(); - VehicleTrack &track = mTracks[w->mTrackIndex]; - - // Calculate max impulse that we can apply on the ground - float max_longitudinal_friction_impulse = w->mCombinedLongitudinalFriction * w->GetSuspensionLambda(); - - // Calculate relative velocity between wheel contact point and floor in longitudinal direction - Vec3 relative_velocity = mConstraint.GetVehicleBody()->GetPointVelocity(w->GetContactPosition()) - w->GetContactPointVelocity(); - float relative_longitudinal_velocity = relative_velocity.Dot(w->GetContactLongitudinal()); - - // Calculate brake force to apply - float min_longitudinal_impulse, max_longitudinal_impulse; - if (w->mBrakeImpulse != 0.0f) - { - // Limit brake force by max tire friction - float brake_impulse = min(w->mBrakeImpulse, max_longitudinal_friction_impulse); - - // Check which direction the brakes should be applied (we don't want to apply an impulse that would accelerate the vehicle) - if (relative_longitudinal_velocity >= 0.0f) - { - min_longitudinal_impulse = -brake_impulse; - max_longitudinal_impulse = 0.0f; - } - else - { - min_longitudinal_impulse = 0.0f; - max_longitudinal_impulse = brake_impulse; - } - - // Longitudinal impulse, note that we assume that once the wheels are locked that the brakes have more than enough torque to keep the wheels locked so we exclude any rotation deltas - impulse |= w->SolveLongitudinalConstraintPart(mConstraint, min_longitudinal_impulse, max_longitudinal_impulse); - } - else - { - // Assume we want to apply an angular impulse that makes the delta velocity between track and ground zero in one time step, calculate the amount of linear impulse needed to do that - float desired_angular_velocity = relative_longitudinal_velocity / settings->mRadius; - float linear_impulse = (track.mAngularVelocity - desired_angular_velocity) * track.mInertia / settings->mRadius; - - // Limit the impulse by max track friction - float prev_lambda = w->GetLongitudinalLambda(); - min_longitudinal_impulse = max_longitudinal_impulse = Clamp(prev_lambda + linear_impulse, -max_longitudinal_friction_impulse, max_longitudinal_friction_impulse); - - // Longitudinal impulse - impulse |= w->SolveLongitudinalConstraintPart(mConstraint, min_longitudinal_impulse, max_longitudinal_impulse); - - // Update the angular velocity of the track according to the lambda that was applied - track.mAngularVelocity -= (w->GetLongitudinalLambda() - prev_lambda) * settings->mRadius / track.mInertia; - SyncLeftRightTracks(); - } - } - - for (Wheel *w_base : mConstraint.GetWheels()) - if (w_base->HasContact()) - { - WheelTV *w = static_cast(w_base); - - // Update angular velocity of wheel for the next iteration - w->CalculateAngularVelocity(mConstraint); - - // Lateral friction - float max_lateral_friction_impulse = w->mCombinedLateralFriction * w->GetSuspensionLambda(); - impulse |= w->SolveLateralConstraintPart(mConstraint, -max_lateral_friction_impulse, max_lateral_friction_impulse); - } - - return impulse; -} - -#ifdef JPH_DEBUG_RENDERER - -void TrackedVehicleController::Draw(DebugRenderer *inRenderer) const -{ - float constraint_size = mConstraint.GetDrawConstraintSize(); - - // Draw RPM - Body *body = mConstraint.GetVehicleBody(); - Vec3 rpm_meter_up = body->GetRotation() * mConstraint.GetLocalUp(); - RVec3 rpm_meter_pos = body->GetPosition() + body->GetRotation() * mRPMMeterPosition; - Vec3 rpm_meter_fwd = body->GetRotation() * mConstraint.GetLocalForward(); - mEngine.DrawRPM(inRenderer, rpm_meter_pos, rpm_meter_fwd, rpm_meter_up, mRPMMeterSize, mTransmission.mShiftDownRPM, mTransmission.mShiftUpRPM); - - // Draw current vehicle state - String status = StringFormat("Forward: %.1f, LRatio: %.1f, RRatio: %.1f, Brake: %.1f\n" - "Gear: %d, Clutch: %.1f, EngineRPM: %.0f, V: %.1f km/h", - (double)mForwardInput, (double)mLeftRatio, (double)mRightRatio, (double)mBrakeInput, - mTransmission.GetCurrentGear(), (double)mTransmission.GetClutchFriction(), (double)mEngine.GetCurrentRPM(), (double)body->GetLinearVelocity().Length() * 3.6); - inRenderer->DrawText3D(body->GetPosition(), status, Color::sWhite, constraint_size); - - for (const VehicleTrack &t : mTracks) - { - const WheelTV *w = static_cast(mConstraint.GetWheels()[t.mDrivenWheel]); - const WheelSettings *settings = w->GetSettings(); - - // Calculate where the suspension attaches to the body in world space - RVec3 ws_position = body->GetCenterOfMassPosition() + body->GetRotation() * (settings->mPosition - body->GetShape()->GetCenterOfMass()); - - DebugRenderer::sInstance->DrawText3D(ws_position, StringFormat("W: %.1f", (double)t.mAngularVelocity), Color::sWhite, constraint_size); - } - - RMat44 body_transform = body->GetWorldTransform(); - - for (const Wheel *w_base : mConstraint.GetWheels()) - { - const WheelTV *w = static_cast(w_base); - const WheelSettings *settings = w->GetSettings(); - - // Calculate where the suspension attaches to the body in world space - RVec3 ws_position = body_transform * settings->mPosition; - Vec3 ws_direction = body_transform.Multiply3x3(settings->mSuspensionDirection); - - // Draw suspension - RVec3 min_suspension_pos = ws_position + ws_direction * settings->mSuspensionMinLength; - RVec3 max_suspension_pos = ws_position + ws_direction * settings->mSuspensionMaxLength; - inRenderer->DrawLine(ws_position, min_suspension_pos, Color::sRed); - inRenderer->DrawLine(min_suspension_pos, max_suspension_pos, Color::sGreen); - - // Draw current length - RVec3 wheel_pos = ws_position + ws_direction * w->GetSuspensionLength(); - inRenderer->DrawMarker(wheel_pos, w->GetSuspensionLength() < settings->mSuspensionMinLength? Color::sRed : Color::sGreen, constraint_size); - - // Draw wheel basis - Vec3 wheel_forward, wheel_up, wheel_right; - mConstraint.GetWheelLocalBasis(w, wheel_forward, wheel_up, wheel_right); - wheel_forward = body_transform.Multiply3x3(wheel_forward); - wheel_up = body_transform.Multiply3x3(wheel_up); - wheel_right = body_transform.Multiply3x3(wheel_right); - Vec3 steering_axis = body_transform.Multiply3x3(settings->mSteeringAxis); - inRenderer->DrawLine(wheel_pos, wheel_pos + wheel_forward, Color::sRed); - inRenderer->DrawLine(wheel_pos, wheel_pos + wheel_up, Color::sGreen); - inRenderer->DrawLine(wheel_pos, wheel_pos + wheel_right, Color::sBlue); - inRenderer->DrawLine(wheel_pos, wheel_pos + steering_axis, Color::sYellow); - - // Draw wheel - RMat44 wheel_transform(Vec4(wheel_up, 0.0f), Vec4(wheel_right, 0.0f), Vec4(wheel_forward, 0.0f), wheel_pos); - wheel_transform.SetRotation(wheel_transform.GetRotation() * Mat44::sRotationY(-w->GetRotationAngle())); - inRenderer->DrawCylinder(wheel_transform, settings->mWidth * 0.5f, settings->mRadius, w->GetSuspensionLength() <= settings->mSuspensionMinLength? Color::sRed : Color::sGreen, DebugRenderer::ECastShadow::Off, DebugRenderer::EDrawMode::Wireframe); - - if (w->HasContact()) - { - // Draw contact - inRenderer->DrawLine(w->GetContactPosition(), w->GetContactPosition() + w->GetContactNormal(), Color::sYellow); - inRenderer->DrawLine(w->GetContactPosition(), w->GetContactPosition() + w->GetContactLongitudinal(), Color::sRed); - inRenderer->DrawLine(w->GetContactPosition(), w->GetContactPosition() + w->GetContactLateral(), Color::sBlue); - - DebugRenderer::sInstance->DrawText3D(w->GetContactPosition(), StringFormat("S: %.2f", (double)w->GetSuspensionLength()), Color::sWhite, constraint_size); - } - } -} - -#endif // JPH_DEBUG_RENDERER - -void TrackedVehicleController::SaveState(StateRecorder &inStream) const -{ - inStream.Write(mForwardInput); - inStream.Write(mLeftRatio); - inStream.Write(mRightRatio); - inStream.Write(mBrakeInput); - - mEngine.SaveState(inStream); - mTransmission.SaveState(inStream); - - for (const VehicleTrack &t : mTracks) - t.SaveState(inStream); -} - -void TrackedVehicleController::RestoreState(StateRecorder &inStream) -{ - inStream.Read(mForwardInput); - inStream.Read(mLeftRatio); - inStream.Read(mRightRatio); - inStream.Read(mBrakeInput); - - mEngine.RestoreState(inStream); - mTransmission.RestoreState(inStream); - - for (VehicleTrack &t : mTracks) - t.RestoreState(inStream); -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/TrackedVehicleController.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/TrackedVehicleController.h deleted file mode 100644 index f5dff5276e5..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/TrackedVehicleController.h +++ /dev/null @@ -1,166 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -class PhysicsSystem; - -/// WheelSettings object specifically for TrackedVehicleController -class JPH_EXPORT WheelSettingsTV : public WheelSettings -{ -public: - JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, WheelSettingsTV) - - // See: WheelSettings - virtual void SaveBinaryState(StreamOut &inStream) const override; - virtual void RestoreBinaryState(StreamIn &inStream) override; - - float mLongitudinalFriction = 4.0f; ///< Friction in forward direction of tire - float mLateralFriction = 2.0f; ///< Friction in sideway direction of tire -}; - -/// Wheel object specifically for TrackedVehicleController -class JPH_EXPORT WheelTV : public Wheel -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Constructor - explicit WheelTV(const WheelSettingsTV &inWheel); - - /// Override GetSettings and cast to the correct class - const WheelSettingsTV * GetSettings() const { return static_cast(mSettings.GetPtr()); } - - /// Update the angular velocity of the wheel based on the angular velocity of the track - void CalculateAngularVelocity(const VehicleConstraint &inConstraint); - - /// Update the wheel rotation based on the current angular velocity - void Update(uint inWheelIndex, float inDeltaTime, const VehicleConstraint &inConstraint); - - int mTrackIndex = -1; ///< Index in mTracks to which this wheel is attached (calculated on initialization) - float mCombinedLongitudinalFriction = 0.0f; ///< Combined friction coefficient in longitudinal direction (combines terrain and track) - float mCombinedLateralFriction = 0.0f; ///< Combined friction coefficient in lateral direction (combines terrain and track) - float mBrakeImpulse = 0.0f; ///< Amount of impulse that the brakes can apply to the floor (excluding friction), spread out from brake impulse applied on track -}; - -/// Settings of a vehicle with tank tracks -/// -/// Default settings are based around what I could find about the M1 Abrams tank. -/// Note to avoid issues with very heavy objects vs very light objects the mass of the tank should be a lot lower (say 10x) than that of a real tank. That means that the engine/brake torque is also 10x less. -class JPH_EXPORT TrackedVehicleControllerSettings : public VehicleControllerSettings -{ -public: - JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, TrackedVehicleControllerSettings) - - // Constructor - TrackedVehicleControllerSettings(); - - // See: VehicleControllerSettings - virtual VehicleController * ConstructController(VehicleConstraint &inConstraint) const override; - virtual void SaveBinaryState(StreamOut &inStream) const override; - virtual void RestoreBinaryState(StreamIn &inStream) override; - - VehicleEngineSettings mEngine; ///< The properties of the engine - VehicleTransmissionSettings mTransmission; ///< The properties of the transmission (aka gear box) - VehicleTrackSettings mTracks[(int)ETrackSide::Num]; ///< List of tracks and their properties -}; - -/// Runtime controller class for vehicle with tank tracks -class JPH_EXPORT TrackedVehicleController : public VehicleController -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Constructor - TrackedVehicleController(const TrackedVehicleControllerSettings &inSettings, VehicleConstraint &inConstraint); - - /// Set input from driver - /// @param inForward Value between -1 and 1 for auto transmission and value between 0 and 1 indicating desired driving direction and amount the gas pedal is pressed - /// @param inLeftRatio Value between -1 and 1 indicating an extra multiplier to the rotation rate of the left track (used for steering) - /// @param inRightRatio Value between -1 and 1 indicating an extra multiplier to the rotation rate of the right track (used for steering) - /// @param inBrake Value between 0 and 1 indicating how strong the brake pedal is pressed - void SetDriverInput(float inForward, float inLeftRatio, float inRightRatio, float inBrake) { JPH_ASSERT(inLeftRatio != 0.0f && inRightRatio != 0.0f); mForwardInput = inForward; mLeftRatio = inLeftRatio; mRightRatio = inRightRatio; mBrakeInput = inBrake; } - - /// Value between -1 and 1 for auto transmission and value between 0 and 1 indicating desired driving direction and amount the gas pedal is pressed - void SetForwardInput(float inForward) { mForwardInput = inForward; } - float GetForwardInput() const { return mForwardInput; } - - /// Value between -1 and 1 indicating an extra multiplier to the rotation rate of the left track (used for steering) - void SetLeftRatio(float inLeftRatio) { JPH_ASSERT(inLeftRatio != 0.0f); mLeftRatio = inLeftRatio; } - float GetLeftRatio() const { return mLeftRatio; } - - /// Value between -1 and 1 indicating an extra multiplier to the rotation rate of the right track (used for steering) - void SetRightRatio(float inRightRatio) { JPH_ASSERT(inRightRatio != 0.0f); mRightRatio = inRightRatio; } - float GetRightRatio() const { return mRightRatio; } - - /// Value between 0 and 1 indicating how strong the brake pedal is pressed - void SetBrakeInput(float inBrake) { mBrakeInput = inBrake; } - float GetBrakeInput() const { return mBrakeInput; } - - /// Get current engine state - const VehicleEngine & GetEngine() const { return mEngine; } - - /// Get current engine state (writable interface, allows you to make changes to the configuration which will take effect the next time step) - VehicleEngine & GetEngine() { return mEngine; } - - /// Get current transmission state - const VehicleTransmission & GetTransmission() const { return mTransmission; } - - /// Get current transmission state (writable interface, allows you to make changes to the configuration which will take effect the next time step) - VehicleTransmission & GetTransmission() { return mTransmission; } - - /// Get the tracks this vehicle has - const VehicleTracks & GetTracks() const { return mTracks; } - - /// Get the tracks this vehicle has (writable interface, allows you to make changes to the configuration which will take effect the next time step) - VehicleTracks & GetTracks() { return mTracks; } - -#ifdef JPH_DEBUG_RENDERER - /// Debug drawing of RPM meter - void SetRPMMeter(Vec3Arg inPosition, float inSize) { mRPMMeterPosition = inPosition; mRPMMeterSize = inSize; } -#endif // JPH_DEBUG_RENDERER - -protected: - /// Synchronize angular velocities of left and right tracks according to their ratios - void SyncLeftRightTracks(); - - // See: VehicleController - virtual Wheel * ConstructWheel(const WheelSettings &inWheel) const override { JPH_ASSERT(IsKindOf(&inWheel, JPH_RTTI(WheelSettingsTV))); return new WheelTV(static_cast(inWheel)); } - virtual bool AllowSleep() const override; - virtual void PreCollide(float inDeltaTime, PhysicsSystem &inPhysicsSystem) override; - virtual void PostCollide(float inDeltaTime, PhysicsSystem &inPhysicsSystem) override; - virtual bool SolveLongitudinalAndLateralConstraints(float inDeltaTime) override; - virtual void SaveState(StateRecorder &inStream) const override; - virtual void RestoreState(StateRecorder &inStream) override; -#ifdef JPH_DEBUG_RENDERER - virtual void Draw(DebugRenderer *inRenderer) const override; -#endif // JPH_DEBUG_RENDERER - - // Control information - float mForwardInput = 0.0f; ///< Value between -1 and 1 for auto transmission and value between 0 and 1 indicating desired driving direction and amount the gas pedal is pressed - float mLeftRatio = 1.0f; ///< Value between -1 and 1 indicating an extra multiplier to the rotation rate of the left track (used for steering) - float mRightRatio = 1.0f; ///< Value between -1 and 1 indicating an extra multiplier to the rotation rate of the right track (used for steering) - float mBrakeInput = 0.0f; ///< Value between 0 and 1 indicating how strong the brake pedal is pressed - - // Simulation information - VehicleEngine mEngine; ///< Engine state of the vehicle - VehicleTransmission mTransmission; ///< Transmission state of the vehicle - VehicleTracks mTracks; ///< Tracks of the vehicle - -#ifdef JPH_DEBUG_RENDERER - // Debug settings - Vec3 mRPMMeterPosition { 0, 1, 0 }; ///< Position (in local space of the body) of the RPM meter when drawing the constraint - float mRPMMeterSize = 0.5f; ///< Size of the RPM meter when drawing the constraint -#endif // JPH_DEBUG_RENDERER -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleAntiRollBar.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleAntiRollBar.cpp deleted file mode 100644 index 859bcafa175..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleAntiRollBar.cpp +++ /dev/null @@ -1,33 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include - -JPH_NAMESPACE_BEGIN - -JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(VehicleAntiRollBar) -{ - JPH_ADD_ATTRIBUTE(VehicleAntiRollBar, mLeftWheel) - JPH_ADD_ATTRIBUTE(VehicleAntiRollBar, mRightWheel) - JPH_ADD_ATTRIBUTE(VehicleAntiRollBar, mStiffness) -} - -void VehicleAntiRollBar::SaveBinaryState(StreamOut &inStream) const -{ - inStream.Write(mLeftWheel); - inStream.Write(mRightWheel); - inStream.Write(mStiffness); -} - -void VehicleAntiRollBar::RestoreBinaryState(StreamIn &inStream) -{ - inStream.Read(mLeftWheel); - inStream.Read(mRightWheel); - inStream.Read(mStiffness); -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleAntiRollBar.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleAntiRollBar.h deleted file mode 100644 index 197c3a0d2ff..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleAntiRollBar.h +++ /dev/null @@ -1,31 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -/// An anti rollbar is a stiff spring that connects two wheels to reduce the amount of roll the vehicle makes in sharp corners -/// See: https://en.wikipedia.org/wiki/Anti-roll_bar -class JPH_EXPORT VehicleAntiRollBar -{ -public: - JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, VehicleAntiRollBar) - - /// Saves the contents in binary form to inStream. - void SaveBinaryState(StreamOut &inStream) const; - - /// Restores the contents in binary form to inStream. - void RestoreBinaryState(StreamIn &inStream); - - int mLeftWheel = 0; ///< Index (in mWheels) that represents the left wheel of this anti-rollbar - int mRightWheel = 1; ///< Index (in mWheels) that represents the right wheel of this anti-rollbar - float mStiffness = 1000.0f; ///< Stiffness (spring constant in N/m) of anti rollbar, can be 0 to disable the anti-rollbar -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleCollisionTester.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleCollisionTester.cpp deleted file mode 100644 index 56246835794..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleCollisionTester.cpp +++ /dev/null @@ -1,376 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -bool VehicleCollisionTesterRay::Collide(PhysicsSystem &inPhysicsSystem, const VehicleConstraint &inVehicleConstraint, uint inWheelIndex, RVec3Arg inOrigin, Vec3Arg inDirection, const BodyID &inVehicleBodyID, Body *&outBody, SubShapeID &outSubShapeID, RVec3 &outContactPosition, Vec3 &outContactNormal, float &outSuspensionLength) const -{ - const DefaultBroadPhaseLayerFilter default_broadphase_layer_filter = inPhysicsSystem.GetDefaultBroadPhaseLayerFilter(mObjectLayer); - const BroadPhaseLayerFilter &broadphase_layer_filter = mBroadPhaseLayerFilter != nullptr? *mBroadPhaseLayerFilter : default_broadphase_layer_filter; - - const DefaultObjectLayerFilter default_object_layer_filter = inPhysicsSystem.GetDefaultLayerFilter(mObjectLayer); - const ObjectLayerFilter &object_layer_filter = mObjectLayerFilter != nullptr? *mObjectLayerFilter : default_object_layer_filter; - - const IgnoreSingleBodyFilter default_body_filter(inVehicleBodyID); - const BodyFilter &body_filter = mBodyFilter != nullptr? *mBodyFilter : default_body_filter; - - const WheelSettings *wheel_settings = inVehicleConstraint.GetWheel(inWheelIndex)->GetSettings(); - float wheel_radius = wheel_settings->mRadius; - float ray_length = wheel_settings->mSuspensionMaxLength + wheel_radius; - RRayCast ray { inOrigin, ray_length * inDirection }; - - class MyCollector : public CastRayCollector - { - public: - MyCollector(PhysicsSystem &inPhysicsSystem, const RRayCast &inRay, Vec3Arg inUpDirection, float inCosMaxSlopeAngle) : - mPhysicsSystem(inPhysicsSystem), - mRay(inRay), - mUpDirection(inUpDirection), - mCosMaxSlopeAngle(inCosMaxSlopeAngle) - { - } - - virtual void AddHit(const RayCastResult &inResult) override - { - // Test if this collision is closer than the previous one - if (inResult.mFraction < GetEarlyOutFraction()) - { - // Lock the body - BodyLockRead lock(mPhysicsSystem.GetBodyLockInterfaceNoLock(), inResult.mBodyID); - JPH_ASSERT(lock.Succeeded()); // When this runs all bodies are locked so this should not fail - const Body *body = &lock.GetBody(); - - if (body->IsSensor()) - return; - - // Test that we're not hitting a vertical wall - RVec3 contact_pos = mRay.GetPointOnRay(inResult.mFraction); - Vec3 normal = body->GetWorldSpaceSurfaceNormal(inResult.mSubShapeID2, contact_pos); - if (normal.Dot(mUpDirection) > mCosMaxSlopeAngle) - { - // Update early out fraction to this hit - UpdateEarlyOutFraction(inResult.mFraction); - - // Get the contact properties - mBody = body; - mSubShapeID2 = inResult.mSubShapeID2; - mContactPosition = contact_pos; - mContactNormal = normal; - } - } - } - - // Configuration - PhysicsSystem & mPhysicsSystem; - RRayCast mRay; - Vec3 mUpDirection; - float mCosMaxSlopeAngle; - - // Resulting closest collision - const Body * mBody = nullptr; - SubShapeID mSubShapeID2; - RVec3 mContactPosition; - Vec3 mContactNormal; - }; - - RayCastSettings settings; - - MyCollector collector(inPhysicsSystem, ray, mUp, mCosMaxSlopeAngle); - inPhysicsSystem.GetNarrowPhaseQueryNoLock().CastRay(ray, settings, collector, broadphase_layer_filter, object_layer_filter, body_filter); - if (collector.mBody == nullptr) - return false; - - outBody = const_cast(collector.mBody); - outSubShapeID = collector.mSubShapeID2; - outContactPosition = collector.mContactPosition; - outContactNormal = collector.mContactNormal; - outSuspensionLength = max(0.0f, ray_length * collector.GetEarlyOutFraction() - wheel_radius); - - return true; -} - -void VehicleCollisionTesterRay::PredictContactProperties(PhysicsSystem &inPhysicsSystem, const VehicleConstraint &inVehicleConstraint, uint inWheelIndex, RVec3Arg inOrigin, Vec3Arg inDirection, const BodyID &inVehicleBodyID, Body *&ioBody, SubShapeID &ioSubShapeID, RVec3 &ioContactPosition, Vec3 &ioContactNormal, float &ioSuspensionLength) const -{ - // Recalculate the contact points assuming the contact point is on an infinite plane - const WheelSettings *wheel_settings = inVehicleConstraint.GetWheel(inWheelIndex)->GetSettings(); - float d_dot_n = inDirection.Dot(ioContactNormal); - if (d_dot_n < -1.0e-6f) - { - // Reproject the contact position using the suspension ray and the plane formed by the contact position and normal - ioContactPosition = inOrigin + Vec3(ioContactPosition - inOrigin).Dot(ioContactNormal) / d_dot_n * inDirection; - - // The suspension length is simply the distance between the contact position and the suspension origin excluding the wheel radius - ioSuspensionLength = Clamp(Vec3(ioContactPosition - inOrigin).Dot(inDirection) - wheel_settings->mRadius, 0.0f, wheel_settings->mSuspensionMaxLength); - } - else - { - // If the normal is pointing away we assume there's no collision anymore - ioSuspensionLength = wheel_settings->mSuspensionMaxLength; - } -} - -bool VehicleCollisionTesterCastSphere::Collide(PhysicsSystem &inPhysicsSystem, const VehicleConstraint &inVehicleConstraint, uint inWheelIndex, RVec3Arg inOrigin, Vec3Arg inDirection, const BodyID &inVehicleBodyID, Body *&outBody, SubShapeID &outSubShapeID, RVec3 &outContactPosition, Vec3 &outContactNormal, float &outSuspensionLength) const -{ - const DefaultBroadPhaseLayerFilter default_broadphase_layer_filter = inPhysicsSystem.GetDefaultBroadPhaseLayerFilter(mObjectLayer); - const BroadPhaseLayerFilter &broadphase_layer_filter = mBroadPhaseLayerFilter != nullptr? *mBroadPhaseLayerFilter : default_broadphase_layer_filter; - - const DefaultObjectLayerFilter default_object_layer_filter = inPhysicsSystem.GetDefaultLayerFilter(mObjectLayer); - const ObjectLayerFilter &object_layer_filter = mObjectLayerFilter != nullptr? *mObjectLayerFilter : default_object_layer_filter; - - const IgnoreSingleBodyFilter default_body_filter(inVehicleBodyID); - const BodyFilter &body_filter = mBodyFilter != nullptr? *mBodyFilter : default_body_filter; - - SphereShape sphere(mRadius); - sphere.SetEmbedded(); - - const WheelSettings *wheel_settings = inVehicleConstraint.GetWheel(inWheelIndex)->GetSettings(); - float wheel_radius = wheel_settings->mRadius; - float shape_cast_length = wheel_settings->mSuspensionMaxLength + wheel_radius - mRadius; - RShapeCast shape_cast(&sphere, Vec3::sReplicate(1.0f), RMat44::sTranslation(inOrigin), inDirection * shape_cast_length); - - ShapeCastSettings settings; - settings.mUseShrunkenShapeAndConvexRadius = true; - settings.mReturnDeepestPoint = true; - - class MyCollector : public CastShapeCollector - { - public: - MyCollector(PhysicsSystem &inPhysicsSystem, const RShapeCast &inShapeCast, Vec3Arg inUpDirection, float inCosMaxSlopeAngle) : - mPhysicsSystem(inPhysicsSystem), - mShapeCast(inShapeCast), - mUpDirection(inUpDirection), - mCosMaxSlopeAngle(inCosMaxSlopeAngle) - { - } - - virtual void AddHit(const ShapeCastResult &inResult) override - { - // Test if this collision is closer/deeper than the previous one - float early_out = inResult.GetEarlyOutFraction(); - if (early_out < GetEarlyOutFraction()) - { - // Lock the body - BodyLockRead lock(mPhysicsSystem.GetBodyLockInterfaceNoLock(), inResult.mBodyID2); - JPH_ASSERT(lock.Succeeded()); // When this runs all bodies are locked so this should not fail - const Body *body = &lock.GetBody(); - - if (body->IsSensor()) - return; - - // Test that we're not hitting a vertical wall - Vec3 normal = -inResult.mPenetrationAxis.Normalized(); - if (normal.Dot(mUpDirection) > mCosMaxSlopeAngle) - { - // Update early out fraction to this hit - UpdateEarlyOutFraction(early_out); - - // Get the contact properties - mBody = body; - mSubShapeID2 = inResult.mSubShapeID2; - mContactPosition = mShapeCast.mCenterOfMassStart.GetTranslation() + inResult.mContactPointOn2; - mContactNormal = normal; - mFraction = inResult.mFraction; - } - } - } - - // Configuration - PhysicsSystem & mPhysicsSystem; - const RShapeCast & mShapeCast; - Vec3 mUpDirection; - float mCosMaxSlopeAngle; - - // Resulting closest collision - const Body * mBody = nullptr; - SubShapeID mSubShapeID2; - RVec3 mContactPosition; - Vec3 mContactNormal; - float mFraction; - }; - - MyCollector collector(inPhysicsSystem, shape_cast, mUp, mCosMaxSlopeAngle); - inPhysicsSystem.GetNarrowPhaseQueryNoLock().CastShape(shape_cast, settings, shape_cast.mCenterOfMassStart.GetTranslation(), collector, broadphase_layer_filter, object_layer_filter, body_filter); - if (collector.mBody == nullptr) - return false; - - outBody = const_cast(collector.mBody); - outSubShapeID = collector.mSubShapeID2; - outContactPosition = collector.mContactPosition; - outContactNormal = collector.mContactNormal; - outSuspensionLength = max(0.0f, shape_cast_length * collector.mFraction + mRadius - wheel_radius); - - return true; -} - -void VehicleCollisionTesterCastSphere::PredictContactProperties(PhysicsSystem &inPhysicsSystem, const VehicleConstraint &inVehicleConstraint, uint inWheelIndex, RVec3Arg inOrigin, Vec3Arg inDirection, const BodyID &inVehicleBodyID, Body *&ioBody, SubShapeID &ioSubShapeID, RVec3 &ioContactPosition, Vec3 &ioContactNormal, float &ioSuspensionLength) const -{ - // Recalculate the contact points assuming the contact point is on an infinite plane - const WheelSettings *wheel_settings = inVehicleConstraint.GetWheel(inWheelIndex)->GetSettings(); - float d_dot_n = inDirection.Dot(ioContactNormal); - if (d_dot_n < -1.0e-6f) - { - // Reproject the contact position using the suspension cast sphere and the plane formed by the contact position and normal - // This solves x = inOrigin + fraction * inDirection and (x - ioContactPosition) . ioContactNormal = mRadius for fraction - float oc_dot_n = Vec3(ioContactPosition - inOrigin).Dot(ioContactNormal); - float fraction = (mRadius + oc_dot_n) / d_dot_n; - ioContactPosition = inOrigin + fraction * inDirection - mRadius * ioContactNormal; - - // Calculate the new suspension length in the same way as the cast sphere normally does - ioSuspensionLength = Clamp(fraction + mRadius - wheel_settings->mRadius, 0.0f, wheel_settings->mSuspensionMaxLength); - } - else - { - // If the normal is pointing away we assume there's no collision anymore - ioSuspensionLength = wheel_settings->mSuspensionMaxLength; - } -} - -bool VehicleCollisionTesterCastCylinder::Collide(PhysicsSystem &inPhysicsSystem, const VehicleConstraint &inVehicleConstraint, uint inWheelIndex, RVec3Arg inOrigin, Vec3Arg inDirection, const BodyID &inVehicleBodyID, Body *&outBody, SubShapeID &outSubShapeID, RVec3 &outContactPosition, Vec3 &outContactNormal, float &outSuspensionLength) const -{ - const DefaultBroadPhaseLayerFilter default_broadphase_layer_filter = inPhysicsSystem.GetDefaultBroadPhaseLayerFilter(mObjectLayer); - const BroadPhaseLayerFilter &broadphase_layer_filter = mBroadPhaseLayerFilter != nullptr? *mBroadPhaseLayerFilter : default_broadphase_layer_filter; - - const DefaultObjectLayerFilter default_object_layer_filter = inPhysicsSystem.GetDefaultLayerFilter(mObjectLayer); - const ObjectLayerFilter &object_layer_filter = mObjectLayerFilter != nullptr? *mObjectLayerFilter : default_object_layer_filter; - - const IgnoreSingleBodyFilter default_body_filter(inVehicleBodyID); - const BodyFilter &body_filter = mBodyFilter != nullptr? *mBodyFilter : default_body_filter; - - const WheelSettings *wheel_settings = inVehicleConstraint.GetWheel(inWheelIndex)->GetSettings(); - float max_suspension_length = wheel_settings->mSuspensionMaxLength; - - // Get the wheel transform given that the cylinder rotates around the Y axis - RMat44 shape_cast_start = inVehicleConstraint.GetWheelWorldTransform(inWheelIndex, Vec3::sAxisY(), Vec3::sAxisX()); - shape_cast_start.SetTranslation(inOrigin); - - // Construct a cylinder with the dimensions of the wheel - float wheel_half_width = 0.5f * wheel_settings->mWidth; - CylinderShape cylinder(wheel_half_width, wheel_settings->mRadius, min(wheel_half_width, wheel_settings->mRadius) * mConvexRadiusFraction); - cylinder.SetEmbedded(); - - RShapeCast shape_cast(&cylinder, Vec3::sReplicate(1.0f), shape_cast_start, inDirection * max_suspension_length); - - ShapeCastSettings settings; - settings.mUseShrunkenShapeAndConvexRadius = true; - settings.mReturnDeepestPoint = true; - - class MyCollector : public CastShapeCollector - { - public: - MyCollector(PhysicsSystem &inPhysicsSystem, const RShapeCast &inShapeCast) : - mPhysicsSystem(inPhysicsSystem), - mShapeCast(inShapeCast) - { - } - - virtual void AddHit(const ShapeCastResult &inResult) override - { - // Test if this collision is closer/deeper than the previous one - float early_out = inResult.GetEarlyOutFraction(); - if (early_out < GetEarlyOutFraction()) - { - // Lock the body - BodyLockRead lock(mPhysicsSystem.GetBodyLockInterfaceNoLock(), inResult.mBodyID2); - JPH_ASSERT(lock.Succeeded()); // When this runs all bodies are locked so this should not fail - const Body *body = &lock.GetBody(); - - if (body->IsSensor()) - return; - - // Update early out fraction to this hit - UpdateEarlyOutFraction(early_out); - - // Get the contact properties - mBody = body; - mSubShapeID2 = inResult.mSubShapeID2; - mContactPosition = mShapeCast.mCenterOfMassStart.GetTranslation() + inResult.mContactPointOn2; - mContactNormal = -inResult.mPenetrationAxis.Normalized(); - mFraction = inResult.mFraction; - } - } - - // Configuration - PhysicsSystem & mPhysicsSystem; - const RShapeCast & mShapeCast; - - // Resulting closest collision - const Body * mBody = nullptr; - SubShapeID mSubShapeID2; - RVec3 mContactPosition; - Vec3 mContactNormal; - float mFraction; - }; - - MyCollector collector(inPhysicsSystem, shape_cast); - inPhysicsSystem.GetNarrowPhaseQueryNoLock().CastShape(shape_cast, settings, shape_cast.mCenterOfMassStart.GetTranslation(), collector, broadphase_layer_filter, object_layer_filter, body_filter); - if (collector.mBody == nullptr) - return false; - - outBody = const_cast(collector.mBody); - outSubShapeID = collector.mSubShapeID2; - outContactPosition = collector.mContactPosition; - outContactNormal = collector.mContactNormal; - outSuspensionLength = max_suspension_length * collector.mFraction; - - return true; -} - -void VehicleCollisionTesterCastCylinder::PredictContactProperties(PhysicsSystem &inPhysicsSystem, const VehicleConstraint &inVehicleConstraint, uint inWheelIndex, RVec3Arg inOrigin, Vec3Arg inDirection, const BodyID &inVehicleBodyID, Body *&ioBody, SubShapeID &ioSubShapeID, RVec3 &ioContactPosition, Vec3 &ioContactNormal, float &ioSuspensionLength) const -{ - // Recalculate the contact points assuming the contact point is on an infinite plane - const WheelSettings *wheel_settings = inVehicleConstraint.GetWheel(inWheelIndex)->GetSettings(); - float d_dot_n = inDirection.Dot(ioContactNormal); - if (d_dot_n < -1.0e-6f) - { - // Wheel size - float half_width = 0.5f * wheel_settings->mWidth; - float radius = wheel_settings->mRadius; - - // Get the inverse local space contact normal for a cylinder pointing along Y - RMat44 wheel_transform = inVehicleConstraint.GetWheelWorldTransform(inWheelIndex, Vec3::sAxisY(), Vec3::sAxisX()); - Vec3 inverse_local_normal = -wheel_transform.Multiply3x3Transposed(ioContactNormal); - - // Get the support point of this normal in local space of the cylinder - // See CylinderShape::Cylinder::GetSupport - float x = inverse_local_normal.GetX(), y = inverse_local_normal.GetY(), z = inverse_local_normal.GetZ(); - float o = sqrt(Square(x) + Square(z)); - Vec3 support_point; - if (o > 0.0f) - support_point = Vec3((radius * x) / o, Sign(y) * half_width, (radius * z) / o); - else - support_point = Vec3(0, Sign(y) * half_width, 0); - - // Rotate back to world space - support_point = wheel_transform.Multiply3x3(support_point); - - // Now we can use inOrigin + support_point as the start of a ray of our suspension to the contact plane - // as know that it is the first point on the wheel that will hit the plane - RVec3 origin = inOrigin + support_point; - - // Calculate contact position and suspension length, the is the same as VehicleCollisionTesterRay - // but we don't need to take the radius into account anymore - Vec3 oc(ioContactPosition - origin); - ioContactPosition = origin + oc.Dot(ioContactNormal) / d_dot_n * inDirection; - ioSuspensionLength = Clamp(oc.Dot(inDirection), 0.0f, wheel_settings->mSuspensionMaxLength); - } - else - { - // If the normal is pointing away we assume there's no collision anymore - ioSuspensionLength = wheel_settings->mSuspensionMaxLength; - } -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleCollisionTester.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleCollisionTester.h deleted file mode 100644 index 7c222756c8b..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleCollisionTester.h +++ /dev/null @@ -1,146 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include - -JPH_NAMESPACE_BEGIN - -class PhysicsSystem; -class VehicleConstraint; -class BroadPhaseLayerFilter; -class ObjectLayerFilter; -class BodyFilter; - -/// Class that does collision detection between wheels and ground -class JPH_EXPORT VehicleCollisionTester : public RefTarget, public NonCopyable -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Constructors - VehicleCollisionTester() = default; - explicit VehicleCollisionTester(ObjectLayer inObjectLayer) : mObjectLayer(inObjectLayer) { } - - /// Virtual destructor - virtual ~VehicleCollisionTester() = default; - - /// Object layer to use for collision detection, this is used when the filters are not overridden - ObjectLayer GetObjectLayer() const { return mObjectLayer; } - void SetObjectLayer(ObjectLayer inObjectLayer) { mObjectLayer = inObjectLayer; } - - /// Access to the broad phase layer filter, when set this overrides the object layer supplied in the constructor - void SetBroadPhaseLayerFilter(const BroadPhaseLayerFilter *inFilter) { mBroadPhaseLayerFilter = inFilter; } - const BroadPhaseLayerFilter * GetBroadPhaseLayerFilter() const { return mBroadPhaseLayerFilter; } - - /// Access to the object layer filter, when set this overrides the object layer supplied in the constructor - void SetObjectLayerFilter(const ObjectLayerFilter *inFilter) { mObjectLayerFilter = inFilter; } - const ObjectLayerFilter * GetObjectLayerFilter() const { return mObjectLayerFilter; } - - /// Access to the body filter, when set this overrides the default filter that filters out the vehicle body - void SetBodyFilter(const BodyFilter *inFilter) { mBodyFilter = inFilter; } - const BodyFilter * GetBodyFilter() const { return mBodyFilter; } - - /// Do a collision test with the world - /// @param inPhysicsSystem The physics system that should be tested against - /// @param inVehicleConstraint The vehicle constraint - /// @param inWheelIndex Index of the wheel that we're testing collision for - /// @param inOrigin Origin for the test, corresponds to the world space position for the suspension attachment point - /// @param inDirection Direction for the test (unit vector, world space) - /// @param inVehicleBodyID This body should be filtered out during collision detection to avoid self collisions - /// @param outBody Body that the wheel collided with - /// @param outSubShapeID Sub shape ID that the wheel collided with - /// @param outContactPosition Contact point between wheel and floor, in world space - /// @param outContactNormal Contact normal between wheel and floor, pointing away from the floor - /// @param outSuspensionLength New length of the suspension [0, inSuspensionMaxLength] - /// @return True when collision found, false if not - virtual bool Collide(PhysicsSystem &inPhysicsSystem, const VehicleConstraint &inVehicleConstraint, uint inWheelIndex, RVec3Arg inOrigin, Vec3Arg inDirection, const BodyID &inVehicleBodyID, Body *&outBody, SubShapeID &outSubShapeID, RVec3 &outContactPosition, Vec3 &outContactNormal, float &outSuspensionLength) const = 0; - - /// Do a cheap contact properties prediction based on the contact properties from the last collision test (provided as input parameters) - /// @param inPhysicsSystem The physics system that should be tested against - /// @param inVehicleConstraint The vehicle constraint - /// @param inWheelIndex Index of the wheel that we're testing collision for - /// @param inOrigin Origin for the test, corresponds to the world space position for the suspension attachment point - /// @param inDirection Direction for the test (unit vector, world space) - /// @param inVehicleBodyID The body ID for the vehicle itself - /// @param ioBody Body that the wheel previously collided with - /// @param ioSubShapeID Sub shape ID that the wheel collided with during the last check - /// @param ioContactPosition Contact point between wheel and floor during the last check, in world space - /// @param ioContactNormal Contact normal between wheel and floor during the last check, pointing away from the floor - /// @param ioSuspensionLength New length of the suspension [0, inSuspensionMaxLength] - virtual void PredictContactProperties(PhysicsSystem &inPhysicsSystem, const VehicleConstraint &inVehicleConstraint, uint inWheelIndex, RVec3Arg inOrigin, Vec3Arg inDirection, const BodyID &inVehicleBodyID, Body *&ioBody, SubShapeID &ioSubShapeID, RVec3 &ioContactPosition, Vec3 &ioContactNormal, float &ioSuspensionLength) const = 0; - -protected: - const BroadPhaseLayerFilter * mBroadPhaseLayerFilter = nullptr; - const ObjectLayerFilter * mObjectLayerFilter = nullptr; - const BodyFilter * mBodyFilter = nullptr; - ObjectLayer mObjectLayer = cObjectLayerInvalid; -}; - -/// Collision tester that tests collision using a raycast -class JPH_EXPORT VehicleCollisionTesterRay : public VehicleCollisionTester -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Constructor - /// @param inObjectLayer Object layer to test collision with - /// @param inUp World space up vector, used to avoid colliding with vertical walls. - /// @param inMaxSlopeAngle Max angle (rad) that is considered for colliding wheels. This is to avoid colliding with vertical walls. - VehicleCollisionTesterRay(ObjectLayer inObjectLayer, Vec3Arg inUp = Vec3::sAxisY(), float inMaxSlopeAngle = DegreesToRadians(80.0f)) : VehicleCollisionTester(inObjectLayer), mUp(inUp), mCosMaxSlopeAngle(Cos(inMaxSlopeAngle)) { } - - // See: VehicleCollisionTester - virtual bool Collide(PhysicsSystem &inPhysicsSystem, const VehicleConstraint &inVehicleConstraint, uint inWheelIndex, RVec3Arg inOrigin, Vec3Arg inDirection, const BodyID &inVehicleBodyID, Body *&outBody, SubShapeID &outSubShapeID, RVec3 &outContactPosition, Vec3 &outContactNormal, float &outSuspensionLength) const override; - virtual void PredictContactProperties(PhysicsSystem &inPhysicsSystem, const VehicleConstraint &inVehicleConstraint, uint inWheelIndex, RVec3Arg inOrigin, Vec3Arg inDirection, const BodyID &inVehicleBodyID, Body *&ioBody, SubShapeID &ioSubShapeID, RVec3 &ioContactPosition, Vec3 &ioContactNormal, float &ioSuspensionLength) const override; - -private: - Vec3 mUp; - float mCosMaxSlopeAngle; -}; - -/// Collision tester that tests collision using a sphere cast -class JPH_EXPORT VehicleCollisionTesterCastSphere : public VehicleCollisionTester -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Constructor - /// @param inObjectLayer Object layer to test collision with - /// @param inUp World space up vector, used to avoid colliding with vertical walls. - /// @param inRadius Radius of sphere - /// @param inMaxSlopeAngle Max angle (rad) that is considered for colliding wheels. This is to avoid colliding with vertical walls. - VehicleCollisionTesterCastSphere(ObjectLayer inObjectLayer, float inRadius, Vec3Arg inUp = Vec3::sAxisY(), float inMaxSlopeAngle = DegreesToRadians(80.0f)) : VehicleCollisionTester(inObjectLayer), mRadius(inRadius), mUp(inUp), mCosMaxSlopeAngle(Cos(inMaxSlopeAngle)) { } - - // See: VehicleCollisionTester - virtual bool Collide(PhysicsSystem &inPhysicsSystem, const VehicleConstraint &inVehicleConstraint, uint inWheelIndex, RVec3Arg inOrigin, Vec3Arg inDirection, const BodyID &inVehicleBodyID, Body *&outBody, SubShapeID &outSubShapeID, RVec3 &outContactPosition, Vec3 &outContactNormal, float &outSuspensionLength) const override; - virtual void PredictContactProperties(PhysicsSystem &inPhysicsSystem, const VehicleConstraint &inVehicleConstraint, uint inWheelIndex, RVec3Arg inOrigin, Vec3Arg inDirection, const BodyID &inVehicleBodyID, Body *&ioBody, SubShapeID &ioSubShapeID, RVec3 &ioContactPosition, Vec3 &ioContactNormal, float &ioSuspensionLength) const override; - -private: - float mRadius; - Vec3 mUp; - float mCosMaxSlopeAngle; -}; - -/// Collision tester that tests collision using a cylinder shape -class JPH_EXPORT VehicleCollisionTesterCastCylinder : public VehicleCollisionTester -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Constructor - /// @param inObjectLayer Object layer to test collision with - /// @param inConvexRadiusFraction Fraction of half the wheel width (or wheel radius if it is smaller) that is used as the convex radius - VehicleCollisionTesterCastCylinder(ObjectLayer inObjectLayer, float inConvexRadiusFraction = 0.1f) : VehicleCollisionTester(inObjectLayer), mConvexRadiusFraction(inConvexRadiusFraction) { JPH_ASSERT(mConvexRadiusFraction >= 0.0f && mConvexRadiusFraction <= 1.0f); } - - // See: VehicleCollisionTester - virtual bool Collide(PhysicsSystem &inPhysicsSystem, const VehicleConstraint &inVehicleConstraint, uint inWheelIndex, RVec3Arg inOrigin, Vec3Arg inDirection, const BodyID &inVehicleBodyID, Body *&outBody, SubShapeID &outSubShapeID, RVec3 &outContactPosition, Vec3 &outContactNormal, float &outSuspensionLength) const override; - virtual void PredictContactProperties(PhysicsSystem &inPhysicsSystem, const VehicleConstraint &inVehicleConstraint, uint inWheelIndex, RVec3Arg inOrigin, Vec3Arg inDirection, const BodyID &inVehicleBodyID, Body *&ioBody, SubShapeID &ioSubShapeID, RVec3 &ioContactPosition, Vec3 &ioContactNormal, float &ioSuspensionLength) const override; - -private: - float mConvexRadiusFraction; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleConstraint.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleConstraint.cpp deleted file mode 100644 index 1be908457c0..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleConstraint.cpp +++ /dev/null @@ -1,681 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(VehicleConstraintSettings) -{ - JPH_ADD_BASE_CLASS(VehicleConstraintSettings, ConstraintSettings) - - JPH_ADD_ATTRIBUTE(VehicleConstraintSettings, mUp) - JPH_ADD_ATTRIBUTE(VehicleConstraintSettings, mForward) - JPH_ADD_ATTRIBUTE(VehicleConstraintSettings, mMaxPitchRollAngle) - JPH_ADD_ATTRIBUTE(VehicleConstraintSettings, mWheels) - JPH_ADD_ATTRIBUTE(VehicleConstraintSettings, mAntiRollBars) - JPH_ADD_ATTRIBUTE(VehicleConstraintSettings, mController) -} - -void VehicleConstraintSettings::SaveBinaryState(StreamOut &inStream) const -{ - ConstraintSettings::SaveBinaryState(inStream); - - inStream.Write(mUp); - inStream.Write(mForward); - inStream.Write(mMaxPitchRollAngle); - - uint32 num_anti_rollbars = (uint32)mAntiRollBars.size(); - inStream.Write(num_anti_rollbars); - for (const VehicleAntiRollBar &r : mAntiRollBars) - r.SaveBinaryState(inStream); - - uint32 num_wheels = (uint32)mWheels.size(); - inStream.Write(num_wheels); - for (const WheelSettings *w : mWheels) - w->SaveBinaryState(inStream); - - inStream.Write(mController->GetRTTI()->GetHash()); - mController->SaveBinaryState(inStream); -} - -void VehicleConstraintSettings::RestoreBinaryState(StreamIn &inStream) -{ - ConstraintSettings::RestoreBinaryState(inStream); - - inStream.Read(mUp); - inStream.Read(mForward); - inStream.Read(mMaxPitchRollAngle); - - uint32 num_anti_rollbars = 0; - inStream.Read(num_anti_rollbars); - mAntiRollBars.resize(num_anti_rollbars); - for (VehicleAntiRollBar &r : mAntiRollBars) - r.RestoreBinaryState(inStream); - - uint32 num_wheels = 0; - inStream.Read(num_wheels); - mWheels.resize(num_wheels); - for (WheelSettings *w : mWheels) - w->RestoreBinaryState(inStream); - - uint32 hash = 0; - inStream.Read(hash); - const RTTI *rtti = Factory::sInstance->Find(hash); - mController = reinterpret_cast(rtti->CreateObject()); - mController->RestoreBinaryState(inStream); -} - -VehicleConstraint::VehicleConstraint(Body &inVehicleBody, const VehicleConstraintSettings &inSettings) : - Constraint(inSettings), - mBody(&inVehicleBody), - mForward(inSettings.mForward), - mUp(inSettings.mUp), - mWorldUp(inSettings.mUp) -{ - // Check sanity of incoming settings - JPH_ASSERT(inSettings.mUp.IsNormalized()); - JPH_ASSERT(inSettings.mForward.IsNormalized()); - JPH_ASSERT(!inSettings.mWheels.empty()); - - // Store max pitch/roll angle - SetMaxPitchRollAngle(inSettings.mMaxPitchRollAngle); - - // Copy anti-rollbar settings - mAntiRollBars.resize(inSettings.mAntiRollBars.size()); - for (uint i = 0; i < mAntiRollBars.size(); ++i) - { - const VehicleAntiRollBar &r = inSettings.mAntiRollBars[i]; - mAntiRollBars[i] = r; - JPH_ASSERT(r.mStiffness >= 0.0f); - } - - // Construct our controller class - mController = inSettings.mController->ConstructController(*this); - - // Create wheels - mWheels.resize(inSettings.mWheels.size()); - for (uint i = 0; i < mWheels.size(); ++i) - mWheels[i] = mController->ConstructWheel(*inSettings.mWheels[i]); - - // Use the body ID as a seed for the step counter so that not all vehicles will update at the same time - mCurrentStep = uint32(Hash64(inVehicleBody.GetID().GetIndex())); -} - -VehicleConstraint::~VehicleConstraint() -{ - // Destroy controller - delete mController; - - // Destroy our wheels - for (Wheel *w : mWheels) - delete w; -} - -void VehicleConstraint::GetWheelLocalBasis(const Wheel *inWheel, Vec3 &outForward, Vec3 &outUp, Vec3 &outRight) const -{ - const WheelSettings *settings = inWheel->mSettings; - - Quat steer_rotation = Quat::sRotation(settings->mSteeringAxis, inWheel->mSteerAngle); - outUp = steer_rotation * settings->mWheelUp; - outForward = steer_rotation * settings->mWheelForward; - outRight = outForward.Cross(outUp).Normalized(); - outForward = outUp.Cross(outRight).Normalized(); -} - -Mat44 VehicleConstraint::GetWheelLocalTransform(uint inWheelIndex, Vec3Arg inWheelRight, Vec3Arg inWheelUp) const -{ - JPH_ASSERT(inWheelIndex < mWheels.size()); - - const Wheel *wheel = mWheels[inWheelIndex]; - const WheelSettings *settings = wheel->mSettings; - - // Use the two vectors provided to calculate a matrix that takes us from wheel model space to X = right, Y = up, Z = forward (the space where we will rotate the wheel) - Mat44 wheel_to_rotational = Mat44(Vec4(inWheelRight, 0), Vec4(inWheelUp, 0), Vec4(inWheelUp.Cross(inWheelRight), 0), Vec4(0, 0, 0, 1)).Transposed(); - - // Calculate the matrix that takes us from the rotational space to vehicle local space - Vec3 local_forward, local_up, local_right; - GetWheelLocalBasis(wheel, local_forward, local_up, local_right); - Vec3 local_wheel_pos = settings->mPosition + settings->mSuspensionDirection * wheel->mSuspensionLength; - Mat44 rotational_to_local(Vec4(local_right, 0), Vec4(local_up, 0), Vec4(local_forward, 0), Vec4(local_wheel_pos, 1)); - - // Calculate transform of rotated wheel - return rotational_to_local * Mat44::sRotationX(wheel->mAngle) * wheel_to_rotational; -} - -RMat44 VehicleConstraint::GetWheelWorldTransform(uint inWheelIndex, Vec3Arg inWheelRight, Vec3Arg inWheelUp) const -{ - return mBody->GetWorldTransform() * GetWheelLocalTransform(inWheelIndex, inWheelRight, inWheelUp); -} - -void VehicleConstraint::OnStep(float inDeltaTime, PhysicsSystem &inPhysicsSystem) -{ - JPH_PROFILE_FUNCTION(); - - // Callback to higher-level systems. We do it before PreCollide, in case steering changes. - if (mPreStepCallback != nullptr) - mPreStepCallback(*this, inDeltaTime, inPhysicsSystem); - - // Calculate new world up vector by inverting gravity - mWorldUp = (-inPhysicsSystem.GetGravity()).NormalizedOr(mWorldUp); - - // Callback on our controller - mController->PreCollide(inDeltaTime, inPhysicsSystem); - - // Calculate if this constraint is active by checking if our main vehicle body is active or any of the bodies we touch are active - mIsActive = mBody->IsActive(); - - // Test how often we need to update the wheels - uint num_steps_between_collisions = mIsActive? mNumStepsBetweenCollisionTestActive : mNumStepsBetweenCollisionTestInactive; - - RMat44 body_transform = mBody->GetWorldTransform(); - - // Test collision for wheels - for (uint wheel_index = 0; wheel_index < mWheels.size(); ++wheel_index) - { - Wheel *w = mWheels[wheel_index]; - const WheelSettings *settings = w->mSettings; - - // Calculate suspension origin and direction - RVec3 ws_origin = body_transform * settings->mPosition; - Vec3 ws_direction = body_transform.Multiply3x3(settings->mSuspensionDirection); - - // Test if we need to update this wheel - if (num_steps_between_collisions == 0 - || (mCurrentStep + wheel_index) % num_steps_between_collisions != 0) - { - // Simplified wheel contact test - if (!w->mContactBodyID.IsInvalid()) - { - // Test if the body is still valid - w->mContactBody = inPhysicsSystem.GetBodyLockInterfaceNoLock().TryGetBody(w->mContactBodyID); - if (w->mContactBody == nullptr) - { - // It's not, forget the contact - w->mContactBodyID = BodyID(); - w->mContactSubShapeID = SubShapeID(); - w->mSuspensionLength = settings->mSuspensionMaxLength; - } - else - { - // Extrapolate the wheel contact properties - mVehicleCollisionTester->PredictContactProperties(inPhysicsSystem, *this, wheel_index, ws_origin, ws_direction, mBody->GetID(), w->mContactBody, w->mContactSubShapeID, w->mContactPosition, w->mContactNormal, w->mSuspensionLength); - } - } - } - else - { - // Full wheel contact test, start by resetting the contact data - w->mContactBodyID = BodyID(); - w->mContactBody = nullptr; - w->mContactSubShapeID = SubShapeID(); - w->mSuspensionLength = settings->mSuspensionMaxLength; - - // Test collision to find the floor - if (mVehicleCollisionTester->Collide(inPhysicsSystem, *this, wheel_index, ws_origin, ws_direction, mBody->GetID(), w->mContactBody, w->mContactSubShapeID, w->mContactPosition, w->mContactNormal, w->mSuspensionLength)) - { - // Store ID (pointer is not valid outside of the simulation step) - w->mContactBodyID = w->mContactBody->GetID(); - } - } - - if (w->mContactBody != nullptr) - { - // Store contact velocity, cache this as the contact body may be removed - w->mContactPointVelocity = w->mContactBody->GetPointVelocity(w->mContactPosition); - - // Determine plane constant for axle contact plane - w->mAxlePlaneConstant = RVec3(w->mContactNormal).Dot(ws_origin + w->mSuspensionLength * ws_direction); - - // Check if body is active, if so the entire vehicle should be active - mIsActive |= w->mContactBody->IsActive(); - - // Determine world space forward using steering angle and body rotation - Vec3 forward, up, right; - GetWheelLocalBasis(w, forward, up, right); - forward = body_transform.Multiply3x3(forward); - right = body_transform.Multiply3x3(right); - - // The longitudinal axis is in the up/forward plane - w->mContactLongitudinal = w->mContactNormal.Cross(right); - - // Make sure that the longitudinal axis is aligned with the forward axis - if (w->mContactLongitudinal.Dot(forward) < 0.0f) - w->mContactLongitudinal = -w->mContactLongitudinal; - - // Normalize it - w->mContactLongitudinal = w->mContactLongitudinal.NormalizedOr(w->mContactNormal.GetNormalizedPerpendicular()); - - // The lateral axis is perpendicular to contact normal and longitudinal axis - w->mContactLateral = w->mContactLongitudinal.Cross(w->mContactNormal).Normalized(); - } - } - - // Callback to higher-level systems. We do it immediately after wheel collision. - if (mPostCollideCallback != nullptr) - mPostCollideCallback(*this, inDeltaTime, inPhysicsSystem); - - // Calculate anti-rollbar impulses - for (const VehicleAntiRollBar &r : mAntiRollBars) - { - Wheel *lw = mWheels[r.mLeftWheel]; - Wheel *rw = mWheels[r.mRightWheel]; - - if (lw->mContactBody != nullptr && rw->mContactBody != nullptr) - { - // Calculate the impulse to apply based on the difference in suspension length - float difference = rw->mSuspensionLength - lw->mSuspensionLength; - float impulse = difference * r.mStiffness * inDeltaTime; - lw->mAntiRollBarImpulse = -impulse; - rw->mAntiRollBarImpulse = impulse; - } - else - { - // When one of the wheels is not on the ground we don't apply any impulses - lw->mAntiRollBarImpulse = rw->mAntiRollBarImpulse = 0.0f; - } - } - - // Callback on our controller - mController->PostCollide(inDeltaTime, inPhysicsSystem); - - // Callback to higher-level systems. We do it before the sleep section, in case velocities change. - if (mPostStepCallback != nullptr) - mPostStepCallback(*this, inDeltaTime, inPhysicsSystem); - - // If the wheels are rotating, we don't want to go to sleep yet - bool allow_sleep = mController->AllowSleep(); - if (allow_sleep) - for (const Wheel *w : mWheels) - if (abs(w->mAngularVelocity) > DegreesToRadians(10.0f)) - { - allow_sleep = false; - break; - } - if (mBody->GetAllowSleeping() != allow_sleep) - mBody->SetAllowSleeping(allow_sleep); - - // Increment step counter - ++mCurrentStep; -} - -void VehicleConstraint::BuildIslands(uint32 inConstraintIndex, IslandBuilder &ioBuilder, BodyManager &inBodyManager) -{ - // Find dynamic bodies that our wheels are touching - BodyID *body_ids = (BodyID *)JPH_STACK_ALLOC((mWheels.size() + 1) * sizeof(BodyID)); - int num_bodies = 0; - bool needs_to_activate = false; - for (const Wheel *w : mWheels) - if (w->mContactBody != nullptr) - { - // Avoid adding duplicates - bool duplicate = false; - BodyID id = w->mContactBody->GetID(); - for (int i = 0; i < num_bodies; ++i) - if (body_ids[i] == id) - { - duplicate = true; - break; - } - if (duplicate) - continue; - - if (w->mContactBody->IsDynamic()) - { - body_ids[num_bodies++] = id; - needs_to_activate |= !w->mContactBody->IsActive(); - } - } - - // Activate bodies, note that if we get here we have already told the system that we're active so that means our main body needs to be active too - if (!mBody->IsActive()) - { - // Our main body is not active, activate it too - body_ids[num_bodies] = mBody->GetID(); - inBodyManager.ActivateBodies(body_ids, num_bodies + 1); - } - else if (needs_to_activate) - { - // Only activate bodies the wheels are touching - inBodyManager.ActivateBodies(body_ids, num_bodies); - } - - // Link the bodies into the same island - uint32 min_active_index = Body::cInactiveIndex; - for (int i = 0; i < num_bodies; ++i) - { - const Body &body = inBodyManager.GetBody(body_ids[i]); - min_active_index = min(min_active_index, body.GetIndexInActiveBodiesInternal()); - ioBuilder.LinkBodies(mBody->GetIndexInActiveBodiesInternal(), body.GetIndexInActiveBodiesInternal()); - } - - // Link the constraint in the island - ioBuilder.LinkConstraint(inConstraintIndex, mBody->GetIndexInActiveBodiesInternal(), min_active_index); -} - -uint VehicleConstraint::BuildIslandSplits(LargeIslandSplitter &ioSplitter) const -{ - return ioSplitter.AssignToNonParallelSplit(mBody); -} - -void VehicleConstraint::CalculateSuspensionForcePoint(const Wheel &inWheel, Vec3 &outR1PlusU, Vec3 &outR2) const -{ - // Determine point to apply force to - RVec3 force_point; - if (inWheel.mSettings->mEnableSuspensionForcePoint) - force_point = mBody->GetWorldTransform() * inWheel.mSettings->mSuspensionForcePoint; - else - force_point = inWheel.mContactPosition; - - // Calculate r1 + u and r2 - outR1PlusU = Vec3(force_point - mBody->GetCenterOfMassPosition()); - outR2 = Vec3(force_point - inWheel.mContactBody->GetCenterOfMassPosition()); -} - -void VehicleConstraint::CalculatePitchRollConstraintProperties(RMat44Arg inBodyTransform) -{ - // Check if a limit was specified - if (mCosMaxPitchRollAngle > -1.0f) - { - // Calculate cos of angle between world up vector and vehicle up vector - Vec3 vehicle_up = inBodyTransform.Multiply3x3(mUp); - mCosPitchRollAngle = mWorldUp.Dot(vehicle_up); - if (mCosPitchRollAngle < mCosMaxPitchRollAngle) - { - // Calculate rotation axis to rotate vehicle towards up - Vec3 rotation_axis = mWorldUp.Cross(vehicle_up); - float len = rotation_axis.Length(); - if (len > 0.0f) - mPitchRollRotationAxis = rotation_axis / len; - - mPitchRollPart.CalculateConstraintProperties(*mBody, Body::sFixedToWorld, mPitchRollRotationAxis); - } - else - mPitchRollPart.Deactivate(); - } - else - mPitchRollPart.Deactivate(); -} - -void VehicleConstraint::SetupVelocityConstraint(float inDeltaTime) -{ - RMat44 body_transform = mBody->GetWorldTransform(); - - for (Wheel *w : mWheels) - if (w->mContactBody != nullptr) - { - const WheelSettings *settings = w->mSettings; - - Vec3 neg_contact_normal = -w->mContactNormal; - - Vec3 r1_plus_u, r2; - CalculateSuspensionForcePoint(*w, r1_plus_u, r2); - - // Suspension spring - if (settings->mSuspensionMaxLength > settings->mSuspensionMinLength) - { - float stiffness, damping; - if (settings->mSuspensionSpring.mMode == ESpringMode::FrequencyAndDamping) - { - // Calculate effective mass based on vehicle configuration (the stiffness of the spring should not be affected by the dynamics of the vehicle): K = 1 / (J M^-1 J^T) - // Note that if no suspension force point is supplied we don't know where the force is applied so we assume it is applied at average suspension length - Vec3 force_point = settings->mEnableSuspensionForcePoint? settings->mSuspensionForcePoint : settings->mPosition + 0.5f * (settings->mSuspensionMinLength + settings->mSuspensionMaxLength) * settings->mSuspensionDirection; - Vec3 force_point_x_neg_up = force_point.Cross(-mUp); - const MotionProperties *mp = mBody->GetMotionProperties(); - float effective_mass = 1.0f / (mp->GetInverseMass() + force_point_x_neg_up.Dot(mp->GetLocalSpaceInverseInertia().Multiply3x3(force_point_x_neg_up))); - - // Convert frequency and damping to stiffness and damping - float omega = 2.0f * JPH_PI * settings->mSuspensionSpring.mFrequency; - stiffness = effective_mass * Square(omega); - damping = 2.0f * effective_mass * settings->mSuspensionSpring.mDamping * omega; - } - else - { - // In this case we can simply copy the properties - stiffness = settings->mSuspensionSpring.mStiffness; - damping = settings->mSuspensionSpring.mDamping; - } - - // Calculate the damping and frequency of the suspension spring given the angle between the suspension direction and the contact normal - // If the angle between the suspension direction and the inverse of the contact normal is alpha then the force on the spring relates to the force along the contact normal as: - // - // Fspring = Fnormal * cos(alpha) - // - // The spring force is: - // - // Fspring = -k * x - // - // where k is the spring constant and x is the displacement of the spring. So we have: - // - // Fnormal * cos(alpha) = -k * x <=> Fnormal = -k / cos(alpha) * x - // - // So we can see this as a spring with spring constant: - // - // k' = k / cos(alpha) - // - // In the same way the velocity relates like: - // - // Vspring = Vnormal * cos(alpha) - // - // Which results in the modified damping constant c: - // - // c' = c / cos(alpha) - // - // Note that we clamp 1 / cos(alpha) to the range [0.1, 1] in order not to increase the stiffness / damping by too much. - Vec3 ws_direction = body_transform.Multiply3x3(settings->mSuspensionDirection); - float cos_angle = max(0.1f, ws_direction.Dot(neg_contact_normal)); - stiffness /= cos_angle; - damping /= cos_angle; - - // Get the value of the constraint equation - float c = w->mSuspensionLength - settings->mSuspensionMaxLength - settings->mSuspensionPreloadLength; - - w->mSuspensionPart.CalculateConstraintPropertiesWithStiffnessAndDamping(inDeltaTime, *mBody, r1_plus_u, *w->mContactBody, r2, neg_contact_normal, w->mAntiRollBarImpulse, c, stiffness, damping); - } - else - w->mSuspensionPart.Deactivate(); - - // Check if we reached the 'max up' position and if so add a hard velocity constraint that stops any further movement in the normal direction - if (w->mSuspensionLength < settings->mSuspensionMinLength) - w->mSuspensionMaxUpPart.CalculateConstraintProperties(*mBody, r1_plus_u, *w->mContactBody, r2, neg_contact_normal); - else - w->mSuspensionMaxUpPart.Deactivate(); - - // Friction and propulsion - w->mLongitudinalPart.CalculateConstraintProperties(*mBody, r1_plus_u, *w->mContactBody, r2, -w->mContactLongitudinal); - w->mLateralPart.CalculateConstraintProperties(*mBody, r1_plus_u, *w->mContactBody, r2, -w->mContactLateral); - } - else - { - // No contact -> disable everything - w->mSuspensionPart.Deactivate(); - w->mSuspensionMaxUpPart.Deactivate(); - w->mLongitudinalPart.Deactivate(); - w->mLateralPart.Deactivate(); - } - - CalculatePitchRollConstraintProperties(body_transform); -} - -void VehicleConstraint::ResetWarmStart() -{ - for (Wheel *w : mWheels) - { - w->mSuspensionPart.Deactivate(); - w->mSuspensionMaxUpPart.Deactivate(); - w->mLongitudinalPart.Deactivate(); - w->mLateralPart.Deactivate(); - } - - mPitchRollPart.Deactivate(); -} - -void VehicleConstraint::WarmStartVelocityConstraint(float inWarmStartImpulseRatio) -{ - for (Wheel *w : mWheels) - if (w->mContactBody != nullptr) - { - Vec3 neg_contact_normal = -w->mContactNormal; - - w->mSuspensionPart.WarmStart(*mBody, *w->mContactBody, neg_contact_normal, inWarmStartImpulseRatio); - w->mSuspensionMaxUpPart.WarmStart(*mBody, *w->mContactBody, neg_contact_normal, inWarmStartImpulseRatio); - w->mLongitudinalPart.WarmStart(*mBody, *w->mContactBody, -w->mContactLongitudinal, 0.0f); // Don't warm start the longitudinal part (the engine/brake force, we don't want to preserve anything from the last frame) - w->mLateralPart.WarmStart(*mBody, *w->mContactBody, -w->mContactLateral, inWarmStartImpulseRatio); - } - - mPitchRollPart.WarmStart(*mBody, Body::sFixedToWorld, inWarmStartImpulseRatio); -} - -bool VehicleConstraint::SolveVelocityConstraint(float inDeltaTime) -{ - bool impulse = false; - - // Solve suspension - for (Wheel *w : mWheels) - if (w->mContactBody != nullptr) - { - Vec3 neg_contact_normal = -w->mContactNormal; - - // Suspension spring, note that it can only push and not pull - if (w->mSuspensionPart.IsActive()) - impulse |= w->mSuspensionPart.SolveVelocityConstraint(*mBody, *w->mContactBody, neg_contact_normal, 0.0f, FLT_MAX); - - // When reaching the minimal suspension length only allow forces pushing the bodies away - if (w->mSuspensionMaxUpPart.IsActive()) - impulse |= w->mSuspensionMaxUpPart.SolveVelocityConstraint(*mBody, *w->mContactBody, neg_contact_normal, 0.0f, FLT_MAX); - } - - // Solve the horizontal movement of the vehicle - impulse |= mController->SolveLongitudinalAndLateralConstraints(inDeltaTime); - - // Apply the pitch / roll constraint to avoid the vehicle from toppling over - if (mPitchRollPart.IsActive()) - impulse |= mPitchRollPart.SolveVelocityConstraint(*mBody, Body::sFixedToWorld, mPitchRollRotationAxis, 0, FLT_MAX); - - return impulse; -} - -bool VehicleConstraint::SolvePositionConstraint(float inDeltaTime, float inBaumgarte) -{ - bool impulse = false; - - RMat44 body_transform = mBody->GetWorldTransform(); - - for (Wheel *w : mWheels) - if (w->mContactBody != nullptr) - { - const WheelSettings *settings = w->mSettings; - - // Check if we reached the 'max up' position now that the body has possibly moved - // We do this by calculating the axle position at minimum suspension length and making sure it does not go through the - // plane defined by the contact normal and the axle position when the contact happened - // TODO: This assumes that only the vehicle moved and not the ground as we kept the axle contact plane in world space - Vec3 ws_direction = body_transform.Multiply3x3(settings->mSuspensionDirection); - RVec3 ws_position = body_transform * settings->mPosition; - RVec3 min_suspension_pos = ws_position + settings->mSuspensionMinLength * ws_direction; - float max_up_error = float(RVec3(w->mContactNormal).Dot(min_suspension_pos) - w->mAxlePlaneConstant); - if (max_up_error < 0.0f) - { - Vec3 neg_contact_normal = -w->mContactNormal; - - // Recalculate constraint properties since the body may have moved - Vec3 r1_plus_u, r2; - CalculateSuspensionForcePoint(*w, r1_plus_u, r2); - w->mSuspensionMaxUpPart.CalculateConstraintProperties(*mBody, r1_plus_u, *w->mContactBody, r2, neg_contact_normal); - - impulse |= w->mSuspensionMaxUpPart.SolvePositionConstraint(*mBody, *w->mContactBody, neg_contact_normal, max_up_error, inBaumgarte); - } - } - - // Apply the pitch / roll constraint to avoid the vehicle from toppling over - CalculatePitchRollConstraintProperties(body_transform); - if (mPitchRollPart.IsActive()) - impulse |= mPitchRollPart.SolvePositionConstraint(*mBody, Body::sFixedToWorld, mCosPitchRollAngle - mCosMaxPitchRollAngle, inBaumgarte); - - return impulse; -} - -#ifdef JPH_DEBUG_RENDERER - -void VehicleConstraint::DrawConstraint(DebugRenderer *inRenderer) const -{ - mController->Draw(inRenderer); -} - -void VehicleConstraint::DrawConstraintLimits(DebugRenderer *inRenderer) const -{ -} - -#endif // JPH_DEBUG_RENDERER - -void VehicleConstraint::SaveState(StateRecorder &inStream) const -{ - Constraint::SaveState(inStream); - - mController->SaveState(inStream); - - for (const Wheel *w : mWheels) - { - inStream.Write(w->mAngularVelocity); - inStream.Write(w->mAngle); - inStream.Write(w->mContactBodyID); // Used by MotorcycleController::PreCollide - inStream.Write(w->mContactPosition); // Used by VehicleCollisionTester::PredictContactProperties - inStream.Write(w->mContactNormal); // Used by MotorcycleController::PreCollide - inStream.Write(w->mContactLateral); // Used by MotorcycleController::PreCollide - inStream.Write(w->mSuspensionLength); // Used by VehicleCollisionTester::PredictContactProperties - - w->mSuspensionPart.SaveState(inStream); - w->mSuspensionMaxUpPart.SaveState(inStream); - w->mLongitudinalPart.SaveState(inStream); - w->mLateralPart.SaveState(inStream); - } - - inStream.Write(mPitchRollRotationAxis); // When rotation is too small we use last frame so we need to store it - mPitchRollPart.SaveState(inStream); - inStream.Write(mCurrentStep); -} - -void VehicleConstraint::RestoreState(StateRecorder &inStream) -{ - Constraint::RestoreState(inStream); - - mController->RestoreState(inStream); - - for (Wheel *w : mWheels) - { - inStream.Read(w->mAngularVelocity); - inStream.Read(w->mAngle); - inStream.Read(w->mContactBodyID); - inStream.Read(w->mContactPosition); - inStream.Read(w->mContactNormal); - inStream.Read(w->mContactLateral); - inStream.Read(w->mSuspensionLength); - w->mContactBody = nullptr; // No longer valid - - w->mSuspensionPart.RestoreState(inStream); - w->mSuspensionMaxUpPart.RestoreState(inStream); - w->mLongitudinalPart.RestoreState(inStream); - w->mLateralPart.RestoreState(inStream); - } - - inStream.Read(mPitchRollRotationAxis); - mPitchRollPart.RestoreState(inStream); - inStream.Read(mCurrentStep); -} - -Ref VehicleConstraint::GetConstraintSettings() const -{ - JPH_ASSERT(false); // Not implemented yet - return nullptr; -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleConstraint.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleConstraint.h deleted file mode 100644 index e855b12a07d..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleConstraint.h +++ /dev/null @@ -1,236 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -class PhysicsSystem; - -/// Configuration for constraint that simulates a wheeled vehicle. -/// -/// The properties in this constraint are largely based on "Car Physics for Games" by Marco Monster. -/// See: https://www.asawicki.info/Mirror/Car%20Physics%20for%20Games/Car%20Physics%20for%20Games.html -class JPH_EXPORT VehicleConstraintSettings : public ConstraintSettings -{ -public: - JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, VehicleConstraintSettings) - - /// Saves the contents of the constraint settings in binary form to inStream. - virtual void SaveBinaryState(StreamOut &inStream) const override; - - Vec3 mUp { 0, 1, 0 }; ///< Vector indicating the up direction of the vehicle (in local space to the body) - Vec3 mForward { 0, 0, 1 }; ///< Vector indicating forward direction of the vehicle (in local space to the body) - float mMaxPitchRollAngle = JPH_PI; ///< Defines the maximum pitch/roll angle (rad), can be used to avoid the car from getting upside down. The vehicle up direction will stay within a cone centered around the up axis with half top angle mMaxPitchRollAngle, set to pi to turn off. - Array> mWheels; ///< List of wheels and their properties - Array mAntiRollBars; ///< List of anti rollbars and their properties - Ref mController; ///< Defines how the vehicle can accelerate / decelerate - -protected: - /// This function should not be called directly, it is used by sRestoreFromBinaryState. - virtual void RestoreBinaryState(StreamIn &inStream) override; -}; - -/// Constraint that simulates a vehicle -/// Note: Don't forget to register the constraint as a StepListener with the PhysicsSystem! -/// -/// When the vehicle drives over very light objects (rubble) you may see the car body dip down. This is a known issue and is an artifact of the iterative solver that Jolt is using. -/// Basically if a light object is sandwiched between two heavy objects (the static floor and the car body), the light object is not able to transfer enough force from the ground to -/// the car body to keep the car body up. You can see this effect in the HeavyOnLightTest sample, the boxes on the right have a lot of penetration because they're on top of light objects. -/// -/// There are a couple of ways to improve this: -/// -/// 1. You can increase the number of velocity steps (global settings PhysicsSettings::mNumVelocitySteps or if you only want to increase it on -/// the vehicle you can use VehicleConstraintSettings::mNumVelocityStepsOverride). E.g. going from 10 to 30 steps in the HeavyOnLightTest sample makes the penetration a lot less. -/// The number of position steps can also be increased (the first prevents the body from going down, the second corrects it if the problem did -/// occur which inevitably happens due to numerical drift). This solution costs CPU cycles. -/// -/// 2. You can reduce the mass difference between the vehicle body and the rubble on the floor (by making the rubble heavier or the car lighter). -/// -/// 3. You could filter out collisions between the vehicle collision test and the rubble completely. This would make the wheels ignore the rubble but would cause the vehicle to drive -/// through it as if nothing happened. You could create fake wheels (keyframed bodies) that move along with the vehicle and that only collide with rubble (and not the vehicle or the ground). -/// This would cause the vehicle to push away the rubble without the rubble being able to affect the vehicle (unless it hits the main body of course). -/// -/// Note that when driving over rubble, you may see the wheel jump up and down quite quickly because one frame a collision is found and the next frame not. -/// To alleviate this, it may be needed to smooth the motion of the visual mesh for the wheel. -class JPH_EXPORT VehicleConstraint : public Constraint, public PhysicsStepListener -{ -public: - /// Constructor / destructor - VehicleConstraint(Body &inVehicleBody, const VehicleConstraintSettings &inSettings); - virtual ~VehicleConstraint() override; - - /// Get the type of a constraint - virtual EConstraintSubType GetSubType() const override { return EConstraintSubType::Vehicle; } - - /// Defines the maximum pitch/roll angle (rad), can be used to avoid the car from getting upside down. The vehicle up direction will stay within a cone centered around the up axis with half top angle mMaxPitchRollAngle, set to pi to turn off. - void SetMaxPitchRollAngle(float inMaxPitchRollAngle) { mCosMaxPitchRollAngle = Cos(inMaxPitchRollAngle); } - - /// Set the interface that tests collision between wheel and ground - void SetVehicleCollisionTester(const VehicleCollisionTester *inTester) { mVehicleCollisionTester = inTester; } - - /// Callback function to combine the friction of a tire with the friction of the body it is colliding with. - /// On input ioLongitudinalFriction and ioLateralFriction contain the friction of the tire, on output they should contain the combined friction with inBody2. - using CombineFunction = function; - - /// Set the function that combines the friction of two bodies and returns it - /// Default method is the geometric mean: sqrt(friction1 * friction2). - void SetCombineFriction(const CombineFunction &inCombineFriction) { mCombineFriction = inCombineFriction; } - const CombineFunction & GetCombineFriction() const { return mCombineFriction; } - - /// Callback function to notify of current stage in PhysicsStepListener::OnStep. - using StepCallback = function; - - /// Callback function to notify that PhysicsStepListener::OnStep has started for this vehicle. Default is to do nothing. - /// Can be used to allow higher-level code to e.g. control steering. This is the last moment that the position/orientation of the vehicle can be changed. - /// Wheel collision checks have not been performed yet. - const StepCallback & GetPreStepCallback() const { return mPreStepCallback; } - void SetPreStepCallback(const StepCallback &inPreStepCallback) { mPreStepCallback = inPreStepCallback; } - - /// Callback function to notify that PhysicsStepListener::OnStep has just completed wheel collision checks. Default is to do nothing. - /// Can be used to allow higher-level code to e.g. detect tire contact or to modify the velocity of the vehicle based on the wheel contacts. - /// You should not change the position of the vehicle in this callback as the wheel collision checks have already been performed. - const StepCallback & GetPostCollideCallback() const { return mPostCollideCallback; } - void SetPostCollideCallback(const StepCallback &inPostCollideCallback) { mPostCollideCallback = inPostCollideCallback; } - - /// Callback function to notify that PhysicsStepListener::OnStep has completed for this vehicle. Default is to do nothing. - /// Can be used to allow higher-level code to e.g. control the vehicle in the air. - /// You should not change the position of the vehicle in this callback as the wheel collision checks have already been performed. - const StepCallback & GetPostStepCallback() const { return mPostStepCallback; } - void SetPostStepCallback(const StepCallback &inPostStepCallback) { mPostStepCallback = inPostStepCallback; } - - /// Get the local space forward vector of the vehicle - Vec3 GetLocalForward() const { return mForward; } - - /// Get the local space up vector of the vehicle - Vec3 GetLocalUp() const { return mUp; } - - /// Vector indicating the world space up direction (used to limit vehicle pitch/roll), calculated every frame by inverting gravity - Vec3 GetWorldUp() const { return mWorldUp; } - - /// Access to the vehicle body - Body * GetVehicleBody() const { return mBody; } - - /// Access to the vehicle controller interface (determines acceleration / deceleration) - const VehicleController * GetController() const { return mController; } - - /// Access to the vehicle controller interface (determines acceleration / deceleration) - VehicleController * GetController() { return mController; } - - /// Get the state of the wheels - const Wheels & GetWheels() const { return mWheels; } - - /// Get the state of a wheels (writable interface, allows you to make changes to the configuration which will take effect the next time step) - Wheels & GetWheels() { return mWheels; } - - /// Get the state of a wheel - Wheel * GetWheel(uint inIdx) { return mWheels[inIdx]; } - const Wheel * GetWheel(uint inIdx) const { return mWheels[inIdx]; } - - /// Get the basis vectors for the wheel in local space to the vehicle body (note: basis does not rotate when the wheel rotates around its axis) - /// @param inWheel Wheel to fetch basis for - /// @param outForward Forward vector for the wheel - /// @param outUp Up vector for the wheel - /// @param outRight Right vector for the wheel - void GetWheelLocalBasis(const Wheel *inWheel, Vec3 &outForward, Vec3 &outUp, Vec3 &outRight) const; - - /// Get the transform of a wheel in local space to the vehicle body, returns a matrix that transforms a cylinder aligned with the Y axis in body space (not COM space) - /// @param inWheelIndex Index of the wheel to fetch - /// @param inWheelRight Unit vector that indicates right in model space of the wheel (so if you only have 1 wheel model, you probably want to specify the opposite direction for the left and right wheels) - /// @param inWheelUp Unit vector that indicates up in model space of the wheel - Mat44 GetWheelLocalTransform(uint inWheelIndex, Vec3Arg inWheelRight, Vec3Arg inWheelUp) const; - - /// Get the transform of a wheel in world space, returns a matrix that transforms a cylinder aligned with the Y axis in world space - /// @param inWheelIndex Index of the wheel to fetch - /// @param inWheelRight Unit vector that indicates right in model space of the wheel (so if you only have 1 wheel model, you probably want to specify the opposite direction for the left and right wheels) - /// @param inWheelUp Unit vector that indicates up in model space of the wheel - RMat44 GetWheelWorldTransform(uint inWheelIndex, Vec3Arg inWheelRight, Vec3Arg inWheelUp) const; - - /// Number of simulation steps between wheel collision tests when the vehicle is active. Default is 1. 0 = never, 1 = every step, 2 = every other step, etc. - /// Note that if a vehicle has multiple wheels and the number of steps > 1, the wheels will be tested in a round robin fashion. - /// If there are multiple vehicles, the tests will be spread out based on the BodyID of the vehicle. - /// If you set this to test less than every step, you may see simulation artifacts. This setting can be used to reduce the cost of simulating vehicles in the distance. - void SetNumStepsBetweenCollisionTestActive(uint inSteps) { mNumStepsBetweenCollisionTestActive = inSteps; } - uint GetNumStepsBetweenCollisionTestActive() const { return mNumStepsBetweenCollisionTestActive; } - - /// Number of simulation steps between wheel collision tests when the vehicle is inactive. Default is 1. 0 = never, 1 = every step, 2 = every other step, etc. - /// Note that if a vehicle has multiple wheels and the number of steps > 1, the wheels will be tested in a round robin fashion. - /// If there are multiple vehicles, the tests will be spread out based on the BodyID of the vehicle. - /// This number can be lower than the number of steps when the vehicle is active as the only purpose of this test is - /// to allow the vehicle to wake up in response to bodies moving into the wheels but not touching the body of the vehicle. - void SetNumStepsBetweenCollisionTestInactive(uint inSteps) { mNumStepsBetweenCollisionTestInactive = inSteps; } - uint GetNumStepsBetweenCollisionTestInactive() const { return mNumStepsBetweenCollisionTestInactive; } - - // Generic interface of a constraint - virtual bool IsActive() const override { return mIsActive && Constraint::IsActive(); } - virtual void NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM) override { /* Do nothing */ } - virtual void SetupVelocityConstraint(float inDeltaTime) override; - virtual void ResetWarmStart() override; - virtual void WarmStartVelocityConstraint(float inWarmStartImpulseRatio) override; - virtual bool SolveVelocityConstraint(float inDeltaTime) override; - virtual bool SolvePositionConstraint(float inDeltaTime, float inBaumgarte) override; - virtual void BuildIslands(uint32 inConstraintIndex, IslandBuilder &ioBuilder, BodyManager &inBodyManager) override; - virtual uint BuildIslandSplits(LargeIslandSplitter &ioSplitter) const override; -#ifdef JPH_DEBUG_RENDERER - virtual void DrawConstraint(DebugRenderer *inRenderer) const override; - virtual void DrawConstraintLimits(DebugRenderer *inRenderer) const override; -#endif // JPH_DEBUG_RENDERER - virtual void SaveState(StateRecorder &inStream) const override; - virtual void RestoreState(StateRecorder &inStream) override; - virtual Ref GetConstraintSettings() const override; - -private: - // See: PhysicsStepListener - virtual void OnStep(float inDeltaTime, PhysicsSystem &inPhysicsSystem) override; - - // Calculate the position where the suspension and traction forces should be applied in world space, relative to the center of mass of both bodies - void CalculateSuspensionForcePoint(const Wheel &inWheel, Vec3 &outR1PlusU, Vec3 &outR2) const; - - // Calculate the constraint properties for mPitchRollPart - void CalculatePitchRollConstraintProperties(RMat44Arg inBodyTransform); - - // Simulation information - Body * mBody; ///< Body of the vehicle - Vec3 mForward; ///< Local space forward vector for the vehicle - Vec3 mUp; ///< Local space up vector for the vehicle - Vec3 mWorldUp; ///< Vector indicating the world space up direction (used to limit vehicle pitch/roll) - Wheels mWheels; ///< Wheel states of the vehicle - Array mAntiRollBars; ///< Anti rollbars of the vehicle - VehicleController * mController; ///< Controls the acceleration / deceleration of the vehicle - bool mIsActive = false; ///< If this constraint is active - uint mNumStepsBetweenCollisionTestActive = 1; ///< Number of simulation steps between wheel collision tests when the vehicle is active - uint mNumStepsBetweenCollisionTestInactive = 1; ///< Number of simulation steps between wheel collision tests when the vehicle is inactive - uint mCurrentStep = 0; ///< Current step number, used to determine when to test a wheel - - // Prevent vehicle from toppling over - float mCosMaxPitchRollAngle; ///< Cos of the max pitch/roll angle - float mCosPitchRollAngle; ///< Cos of the current pitch/roll angle - Vec3 mPitchRollRotationAxis { 0, 1, 0 }; ///< Current axis along which to apply torque to prevent the car from toppling over - AngleConstraintPart mPitchRollPart; ///< Constraint part that prevents the car from toppling over - - // Interfaces - RefConst mVehicleCollisionTester; ///< Class that performs testing of collision for the wheels - CombineFunction mCombineFriction = [](uint, float &ioLongitudinalFriction, float &ioLateralFriction, const Body &inBody2, const SubShapeID &) - { - float body_friction = inBody2.GetFriction(); - - ioLongitudinalFriction = sqrt(ioLongitudinalFriction * body_friction); - ioLateralFriction = sqrt(ioLateralFriction * body_friction); - }; - - // Callbacks - StepCallback mPreStepCallback; - StepCallback mPostCollideCallback; - StepCallback mPostStepCallback; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleController.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleController.cpp deleted file mode 100644 index ffa8ff1a64b..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleController.cpp +++ /dev/null @@ -1,17 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include - -JPH_NAMESPACE_BEGIN - -JPH_IMPLEMENT_SERIALIZABLE_ABSTRACT(VehicleControllerSettings) -{ - JPH_ADD_BASE_CLASS(VehicleControllerSettings, SerializableObject) -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleController.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleController.h deleted file mode 100644 index b916a8bfa0a..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleController.h +++ /dev/null @@ -1,80 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include -#ifdef JPH_DEBUG_RENDERER - #include -#endif // JPH_DEBUG_RENDERER - -JPH_NAMESPACE_BEGIN - -class PhysicsSystem; -class VehicleController; -class VehicleConstraint; -class WheelSettings; -class Wheel; -class StateRecorder; - -/// Basic settings object for interface that controls acceleration / deceleration of the vehicle -class JPH_EXPORT VehicleControllerSettings : public SerializableObject, public RefTarget -{ -public: - JPH_DECLARE_SERIALIZABLE_ABSTRACT(JPH_EXPORT, VehicleControllerSettings) - - /// Saves the contents of the controller settings in binary form to inStream. - virtual void SaveBinaryState(StreamOut &inStream) const = 0; - - /// Restore the contents of the controller settings in binary form from inStream. - virtual void RestoreBinaryState(StreamIn &inStream) = 0; - - /// Create an instance of the vehicle controller class - virtual VehicleController * ConstructController(VehicleConstraint &inConstraint) const = 0; -}; - -/// Runtime data for interface that controls acceleration / deceleration of the vehicle -class JPH_EXPORT VehicleController : public RefTarget, public NonCopyable -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Constructor / destructor - explicit VehicleController(VehicleConstraint &inConstraint) : mConstraint(inConstraint) { } - virtual ~VehicleController() = default; - -protected: - // The functions below are only for the VehicleConstraint - friend class VehicleConstraint; - - // Create a new instance of wheel - virtual Wheel * ConstructWheel(const WheelSettings &inWheel) const = 0; - - // If the vehicle is allowed to go to sleep - virtual bool AllowSleep() const = 0; - - // Called before the wheel probes have been done - virtual void PreCollide(float inDeltaTime, PhysicsSystem &inPhysicsSystem) = 0; - - // Called after the wheel probes have been done - virtual void PostCollide(float inDeltaTime, PhysicsSystem &inPhysicsSystem) = 0; - - // Solve longitudinal and lateral constraint parts for all of the wheels - virtual bool SolveLongitudinalAndLateralConstraints(float inDeltaTime) = 0; - - // Saving state for replay - virtual void SaveState(StateRecorder &inStream) const = 0; - virtual void RestoreState(StateRecorder &inStream) = 0; - -#ifdef JPH_DEBUG_RENDERER - // Drawing interface - virtual void Draw(DebugRenderer *inRenderer) const = 0; -#endif // JPH_DEBUG_RENDERER - - VehicleConstraint & mConstraint; ///< The vehicle constraint we belong to -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleDifferential.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleDifferential.cpp deleted file mode 100644 index ef7cf4cb914..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleDifferential.cpp +++ /dev/null @@ -1,81 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include - -JPH_NAMESPACE_BEGIN - -JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(VehicleDifferentialSettings) -{ - JPH_ADD_ATTRIBUTE(VehicleDifferentialSettings, mLeftWheel) - JPH_ADD_ATTRIBUTE(VehicleDifferentialSettings, mRightWheel) - JPH_ADD_ATTRIBUTE(VehicleDifferentialSettings, mDifferentialRatio) - JPH_ADD_ATTRIBUTE(VehicleDifferentialSettings, mLeftRightSplit) - JPH_ADD_ATTRIBUTE(VehicleDifferentialSettings, mLimitedSlipRatio) - JPH_ADD_ATTRIBUTE(VehicleDifferentialSettings, mEngineTorqueRatio) -} - -void VehicleDifferentialSettings::SaveBinaryState(StreamOut &inStream) const -{ - inStream.Write(mLeftWheel); - inStream.Write(mRightWheel); - inStream.Write(mDifferentialRatio); - inStream.Write(mLeftRightSplit); - inStream.Write(mLimitedSlipRatio); - inStream.Write(mEngineTorqueRatio); -} - -void VehicleDifferentialSettings::RestoreBinaryState(StreamIn &inStream) -{ - inStream.Read(mLeftWheel); - inStream.Read(mRightWheel); - inStream.Read(mDifferentialRatio); - inStream.Read(mLeftRightSplit); - inStream.Read(mLimitedSlipRatio); - inStream.Read(mEngineTorqueRatio); -} - -void VehicleDifferentialSettings::CalculateTorqueRatio(float inLeftAngularVelocity, float inRightAngularVelocity, float &outLeftTorqueFraction, float &outRightTorqueFraction) const -{ - // Start with the default torque ratio - outLeftTorqueFraction = 1.0f - mLeftRightSplit; - outRightTorqueFraction = mLeftRightSplit; - - if (mLimitedSlipRatio < FLT_MAX) - { - JPH_ASSERT(mLimitedSlipRatio > 1.0f); - - // This is a limited slip differential, adjust torque ratios according to wheel speeds - float omega_l = max(1.0e-3f, abs(inLeftAngularVelocity)); // prevent div by zero by setting a minimum velocity and ignoring that the wheels may be rotating in different directions - float omega_r = max(1.0e-3f, abs(inRightAngularVelocity)); - float omega_min = min(omega_l, omega_r); - float omega_max = max(omega_l, omega_r); - - // Map into a value that is 0 when the wheels are turning at an equal rate and 1 when the wheels are turning at mLimitedSlipRotationRatio - float alpha = min((omega_max / omega_min - 1.0f) / (mLimitedSlipRatio - 1.0f), 1.0f); - JPH_ASSERT(alpha >= 0.0f); - float one_min_alpha = 1.0f - alpha; - - if (omega_l < omega_r) - { - // Redirect more power to the left wheel - outLeftTorqueFraction = outLeftTorqueFraction * one_min_alpha + alpha; - outRightTorqueFraction = outRightTorqueFraction * one_min_alpha; - } - else - { - // Redirect more power to the right wheel - outLeftTorqueFraction = outLeftTorqueFraction * one_min_alpha; - outRightTorqueFraction = outRightTorqueFraction * one_min_alpha + alpha; - } - } - - // Assert the values add up to 1 - JPH_ASSERT(abs(outLeftTorqueFraction + outRightTorqueFraction - 1.0f) < 1.0e-6f); -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleDifferential.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleDifferential.h deleted file mode 100644 index 34a5011530c..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleDifferential.h +++ /dev/null @@ -1,39 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -class JPH_EXPORT VehicleDifferentialSettings -{ -public: - JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, VehicleDifferentialSettings) - - /// Saves the contents in binary form to inStream. - void SaveBinaryState(StreamOut &inStream) const; - - /// Restores the contents in binary form to inStream. - void RestoreBinaryState(StreamIn &inStream); - - /// Calculate the torque ratio between left and right wheel - /// @param inLeftAngularVelocity Angular velocity of left wheel (rad / s) - /// @param inRightAngularVelocity Angular velocity of right wheel (rad / s) - /// @param outLeftTorqueFraction Fraction of torque that should go to the left wheel - /// @param outRightTorqueFraction Fraction of torque that should go to the right wheel - void CalculateTorqueRatio(float inLeftAngularVelocity, float inRightAngularVelocity, float &outLeftTorqueFraction, float &outRightTorqueFraction) const; - - int mLeftWheel = -1; ///< Index (in mWheels) that represents the left wheel of this differential (can be -1 to indicate no wheel) - int mRightWheel = -1; ///< Index (in mWheels) that represents the right wheel of this differential (can be -1 to indicate no wheel) - float mDifferentialRatio = 3.42f; ///< Ratio between rotation speed of gear box and wheels - float mLeftRightSplit = 0.5f; ///< Defines how the engine torque is split across the left and right wheel (0 = left, 0.5 = center, 1 = right) - float mLimitedSlipRatio = 1.4f; ///< Ratio max / min wheel speed. When this ratio is exceeded, all torque gets distributed to the slowest moving wheel. This allows implementing a limited slip differential. Set to FLT_MAX for an open differential. Value should be > 1. - float mEngineTorqueRatio = 1.0f; ///< How much of the engines torque is applied to this differential (0 = none, 1 = full), make sure the sum of all differentials is 1. -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleEngine.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleEngine.cpp deleted file mode 100644 index 1352cc0d51f..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleEngine.cpp +++ /dev/null @@ -1,122 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#ifdef JPH_DEBUG_RENDERER - #include -#endif // JPH_DEBUG_RENDERER - -JPH_NAMESPACE_BEGIN - -JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(VehicleEngineSettings) -{ - JPH_ADD_ATTRIBUTE(VehicleEngineSettings, mMaxTorque) - JPH_ADD_ATTRIBUTE(VehicleEngineSettings, mMinRPM) - JPH_ADD_ATTRIBUTE(VehicleEngineSettings, mMaxRPM) - JPH_ADD_ATTRIBUTE(VehicleEngineSettings, mNormalizedTorque) -} - -VehicleEngineSettings::VehicleEngineSettings() -{ - mNormalizedTorque.Reserve(3); - mNormalizedTorque.AddPoint(0.0f, 0.8f); - mNormalizedTorque.AddPoint(0.66f, 1.0f); - mNormalizedTorque.AddPoint(1.0f, 0.8f); -} - -void VehicleEngineSettings::SaveBinaryState(StreamOut &inStream) const -{ - inStream.Write(mMaxTorque); - inStream.Write(mMinRPM); - inStream.Write(mMaxRPM); - mNormalizedTorque.SaveBinaryState(inStream); -} - -void VehicleEngineSettings::RestoreBinaryState(StreamIn &inStream) -{ - inStream.Read(mMaxTorque); - inStream.Read(mMinRPM); - inStream.Read(mMaxRPM); - mNormalizedTorque.RestoreBinaryState(inStream); -} - -void VehicleEngine::ApplyTorque(float inTorque, float inDeltaTime) -{ - // Accelerate engine using torque - mCurrentRPM += cAngularVelocityToRPM * inTorque * inDeltaTime / mInertia; - ClampRPM(); -} - -void VehicleEngine::ApplyDamping(float inDeltaTime) -{ - // Angular damping: dw/dt = -c * w - // Solution: w(t) = w(0) * e^(-c * t) or w2 = w1 * e^(-c * dt) - // Taylor expansion of e^(-c * dt) = 1 - c * dt + ... - // Since dt is usually in the order of 1/60 and c is a low number too this approximation is good enough - mCurrentRPM *= max(0.0f, 1.0f - mAngularDamping * inDeltaTime); - ClampRPM(); -} - -#ifdef JPH_DEBUG_RENDERER - -void VehicleEngine::DrawRPM(DebugRenderer *inRenderer, RVec3Arg inPosition, Vec3Arg inForward, Vec3Arg inUp, float inSize, float inShiftDownRPM, float inShiftUpRPM) const -{ - // Function to draw part of a pie - auto draw_pie = [this, inRenderer, inSize, inPosition, inForward, inUp](float inMinRPM, float inMaxRPM, Color inColor) { - inRenderer->DrawPie(inPosition, inSize, inForward, inUp, ConvertRPMToAngle(inMinRPM), ConvertRPMToAngle(inMaxRPM), inColor, DebugRenderer::ECastShadow::Off); - }; - - // Draw segment under min RPM - draw_pie(0, mMinRPM, Color::sGrey); - - // Draw segment until inShiftDownRPM - if (mCurrentRPM < inShiftDownRPM) - { - draw_pie(mMinRPM, mCurrentRPM, Color::sRed); - draw_pie(mCurrentRPM, inShiftDownRPM, Color::sDarkRed); - } - else - { - draw_pie(mMinRPM, inShiftDownRPM, Color::sRed); - } - - // Draw segment between inShiftDownRPM and inShiftUpRPM - if (mCurrentRPM > inShiftDownRPM && mCurrentRPM < inShiftUpRPM) - { - draw_pie(inShiftDownRPM, mCurrentRPM, Color::sOrange); - draw_pie(mCurrentRPM, inShiftUpRPM, Color::sDarkOrange); - } - else - { - draw_pie(inShiftDownRPM, inShiftUpRPM, mCurrentRPM <= inShiftDownRPM? Color::sDarkOrange : Color::sOrange); - } - - // Draw segment above inShiftUpRPM - if (mCurrentRPM > inShiftUpRPM) - { - draw_pie(inShiftUpRPM, mCurrentRPM, Color::sGreen); - draw_pie(mCurrentRPM, mMaxRPM, Color::sDarkGreen); - } - else - { - draw_pie(inShiftUpRPM, mMaxRPM, Color::sDarkGreen); - } -} - -#endif // JPH_DEBUG_RENDERER - -void VehicleEngine::SaveState(StateRecorder &inStream) const -{ - inStream.Write(mCurrentRPM); -} - -void VehicleEngine::RestoreState(StateRecorder &inStream) -{ - inStream.Read(mCurrentRPM); -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleEngine.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleEngine.h deleted file mode 100644 index 25cf6391b9e..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleEngine.h +++ /dev/null @@ -1,93 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -#ifdef JPH_DEBUG_RENDERER - class DebugRenderer; -#endif // JPH_DEBUG_RENDERER - -/// Generic properties for a vehicle engine -class JPH_EXPORT VehicleEngineSettings -{ -public: - JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, VehicleEngineSettings) - - /// Constructor - VehicleEngineSettings(); - - /// Saves the contents in binary form to inStream. - void SaveBinaryState(StreamOut &inStream) const; - - /// Restores the contents in binary form to inStream. - void RestoreBinaryState(StreamIn &inStream); - - float mMaxTorque = 500.0f; ///< Max amount of torque (Nm) that the engine can deliver - float mMinRPM = 1000.0f; ///< Min amount of revolutions per minute (rpm) the engine can produce without stalling - float mMaxRPM = 6000.0f; ///< Max amount of revolutions per minute (rpm) the engine can generate - LinearCurve mNormalizedTorque; ///< Y-axis: Curve that describes a ratio of the max torque the engine can produce (0 = 0, 1 = mMaxTorque). X-axis: the fraction of the RPM of the engine (0 = mMinRPM, 1 = mMaxRPM) - float mInertia = 0.5f; ///< Moment of inertia (kg m^2) of the engine - float mAngularDamping = 0.2f; ///< Angular damping factor of the wheel: dw/dt = -c * w -}; - -/// Runtime data for engine -class JPH_EXPORT VehicleEngine : public VehicleEngineSettings -{ -public: - /// Multiply an angular velocity (rad/s) with this value to get rounds per minute (RPM) - static constexpr float cAngularVelocityToRPM = 60.0f / (2.0f * JPH_PI); - - /// Clamp the RPM between min and max RPM - inline void ClampRPM() { mCurrentRPM = Clamp(mCurrentRPM, mMinRPM, mMaxRPM); } - - /// Current rotation speed of engine in rounds per minute - float GetCurrentRPM() const { return mCurrentRPM; } - - /// Update rotation speed of engine in rounds per minute - void SetCurrentRPM(float inRPM) { mCurrentRPM = inRPM; ClampRPM(); } - - /// Get current angular velocity of the engine in radians / second - inline float GetAngularVelocity() const { return mCurrentRPM / cAngularVelocityToRPM; } - - /// Get the amount of torque (N m) that the engine can supply - /// @param inAcceleration How much the gas pedal is pressed [0, 1] - float GetTorque(float inAcceleration) const { return inAcceleration * mMaxTorque * mNormalizedTorque.GetValue(mCurrentRPM / mMaxRPM); } - - /// Apply a torque to the engine rotation speed - /// @param inTorque Torque in N m - /// @param inDeltaTime Delta time in seconds - void ApplyTorque(float inTorque, float inDeltaTime); - - /// Update the engine RPM for damping - /// @param inDeltaTime Delta time in seconds - void ApplyDamping(float inDeltaTime); - -#ifdef JPH_DEBUG_RENDERER - // Function that converts RPM to an angle in radians for debugging purposes - float ConvertRPMToAngle(float inRPM) const { return (-0.75f + 1.5f * inRPM / mMaxRPM) * JPH_PI; } - - /// Debug draw a RPM meter - void DrawRPM(DebugRenderer *inRenderer, RVec3Arg inPosition, Vec3Arg inForward, Vec3Arg inUp, float inSize, float inShiftDownRPM, float inShiftUpRPM) const; -#endif // JPH_DEBUG_RENDERER - - /// If the engine is idle we allow the vehicle to sleep - bool AllowSleep() const { return mCurrentRPM <= 1.01f * mMinRPM; } - - /// Saving state for replay - void SaveState(StateRecorder &inStream) const; - void RestoreState(StateRecorder &inStream); - -private: - float mCurrentRPM = mMinRPM; ///< Current rotation speed of engine in rounds per minute -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleTrack.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleTrack.cpp deleted file mode 100644 index 3bef2e80493..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleTrack.cpp +++ /dev/null @@ -1,52 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include - -JPH_NAMESPACE_BEGIN - -JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(VehicleTrackSettings) -{ - JPH_ADD_ATTRIBUTE(VehicleTrackSettings, mDrivenWheel) - JPH_ADD_ATTRIBUTE(VehicleTrackSettings, mWheels) - JPH_ADD_ATTRIBUTE(VehicleTrackSettings, mInertia) - JPH_ADD_ATTRIBUTE(VehicleTrackSettings, mAngularDamping) - JPH_ADD_ATTRIBUTE(VehicleTrackSettings, mMaxBrakeTorque) - JPH_ADD_ATTRIBUTE(VehicleTrackSettings, mDifferentialRatio) -} - -void VehicleTrackSettings::SaveBinaryState(StreamOut &inStream) const -{ - inStream.Write(mDrivenWheel); - inStream.Write(mWheels); - inStream.Write(mInertia); - inStream.Write(mAngularDamping); - inStream.Write(mMaxBrakeTorque); - inStream.Write(mDifferentialRatio); -} - -void VehicleTrackSettings::RestoreBinaryState(StreamIn &inStream) -{ - inStream.Read(mDrivenWheel); - inStream.Read(mWheels); - inStream.Read(mInertia); - inStream.Read(mAngularDamping); - inStream.Read(mMaxBrakeTorque); - inStream.Read(mDifferentialRatio); -} - -void VehicleTrack::SaveState(StateRecorder &inStream) const -{ - inStream.Write(mAngularVelocity); -} - -void VehicleTrack::RestoreState(StateRecorder &inStream) -{ - inStream.Read(mAngularVelocity); -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleTrack.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleTrack.h deleted file mode 100644 index c90acccc660..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleTrack.h +++ /dev/null @@ -1,56 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -/// On which side of the vehicle the track is located (for steering) -enum class ETrackSide : uint -{ - Left = 0, - Right = 1, - Num = 2 -}; - -/// Generic properties for tank tracks -class JPH_EXPORT VehicleTrackSettings -{ -public: - JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, VehicleTrackSettings) - - /// Saves the contents in binary form to inStream. - void SaveBinaryState(StreamOut &inStream) const; - - /// Restores the contents in binary form to inStream. - void RestoreBinaryState(StreamIn &inStream); - - uint mDrivenWheel; ///< Which wheel on the track is connected to the engine - Array mWheels; ///< Indices of wheels that are inside this track, should include the driven wheel too - float mInertia = 10.0f; ///< Moment of inertia (kg m^2) of the track and its wheels as seen on the driven wheel - float mAngularDamping = 0.5f; ///< Damping factor of track and its wheels: dw/dt = -c * w as seen on the driven wheel - float mMaxBrakeTorque = 15000.0f; ///< How much torque (Nm) the brakes can apply on the driven wheel - float mDifferentialRatio = 6.0f; ///< Ratio between rotation speed of gear box and driven wheel of track -}; - -/// Runtime data for tank tracks -class JPH_EXPORT VehicleTrack : public VehicleTrackSettings -{ -public: - /// Saving state for replay - void SaveState(StateRecorder &inStream) const; - void RestoreState(StateRecorder &inStream); - - float mAngularVelocity = 0.0f; ///< Angular velocity of the driven wheel, will determine the speed of the entire track -}; - -using VehicleTracks = VehicleTrack[(int)ETrackSide::Num]; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleTransmission.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleTransmission.cpp deleted file mode 100644 index 43336a537f0..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleTransmission.cpp +++ /dev/null @@ -1,159 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include - -JPH_NAMESPACE_BEGIN - -JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(VehicleTransmissionSettings) -{ - JPH_ADD_ENUM_ATTRIBUTE(VehicleTransmissionSettings, mMode) - JPH_ADD_ATTRIBUTE(VehicleTransmissionSettings, mGearRatios) - JPH_ADD_ATTRIBUTE(VehicleTransmissionSettings, mReverseGearRatios) - JPH_ADD_ATTRIBUTE(VehicleTransmissionSettings, mSwitchTime) - JPH_ADD_ATTRIBUTE(VehicleTransmissionSettings, mClutchReleaseTime) - JPH_ADD_ATTRIBUTE(VehicleTransmissionSettings, mSwitchLatency) - JPH_ADD_ATTRIBUTE(VehicleTransmissionSettings, mShiftUpRPM) - JPH_ADD_ATTRIBUTE(VehicleTransmissionSettings, mShiftDownRPM) - JPH_ADD_ATTRIBUTE(VehicleTransmissionSettings, mClutchStrength) -} - -void VehicleTransmissionSettings::SaveBinaryState(StreamOut &inStream) const -{ - inStream.Write(mMode); - inStream.Write(mGearRatios); - inStream.Write(mReverseGearRatios); - inStream.Write(mSwitchTime); - inStream.Write(mClutchReleaseTime); - inStream.Write(mSwitchLatency); - inStream.Write(mShiftUpRPM); - inStream.Write(mShiftDownRPM); - inStream.Write(mClutchStrength); -} - -void VehicleTransmissionSettings::RestoreBinaryState(StreamIn &inStream) -{ - inStream.Read(mMode); - inStream.Read(mGearRatios); - inStream.Read(mReverseGearRatios); - inStream.Read(mSwitchTime); - inStream.Read(mClutchReleaseTime); - inStream.Read(mSwitchLatency); - inStream.Read(mShiftUpRPM); - inStream.Read(mShiftDownRPM); - inStream.Read(mClutchStrength); -} - -void VehicleTransmission::Update(float inDeltaTime, float inCurrentRPM, float inForwardInput, bool inCanShiftUp) -{ - // Update current gear and calculate clutch friction - if (mMode == ETransmissionMode::Auto) - { - // Switch gears based on rpm - int old_gear = mCurrentGear; - if (mCurrentGear == 0 // In neutral - || inForwardInput * float(mCurrentGear) < 0.0f) // Changing between forward / reverse - { - // Switch to first gear or reverse depending on input - mCurrentGear = inForwardInput > 0.0f? 1 : (inForwardInput < 0.0f? -1 : 0); - } - else if (mGearSwitchLatencyTimeLeft == 0.0f) // If not in the timout after switching gears - { - if (inCanShiftUp && inCurrentRPM > mShiftUpRPM) - { - if (mCurrentGear < 0) - { - // Shift up, reverse - if (mCurrentGear > -(int)mReverseGearRatios.size()) - mCurrentGear--; - } - else - { - // Shift up, forward - if (mCurrentGear < (int)mGearRatios.size()) - mCurrentGear++; - } - } - else if (inCurrentRPM < mShiftDownRPM) - { - if (mCurrentGear < 0) - { - // Shift down, reverse - int max_gear = inForwardInput != 0.0f? -1 : 0; - if (mCurrentGear < max_gear) - mCurrentGear++; - } - else - { - // Shift down, forward - int min_gear = inForwardInput != 0.0f? 1 : 0; - if (mCurrentGear > min_gear) - mCurrentGear--; - } - } - } - - if (old_gear != mCurrentGear) - { - // We've shifted gear, start switch countdown - mGearSwitchTimeLeft = old_gear != 0? mSwitchTime : 0.0f; - mClutchReleaseTimeLeft = mClutchReleaseTime; - mGearSwitchLatencyTimeLeft = mSwitchLatency; - mClutchFriction = 0.0f; - } - else if (mGearSwitchTimeLeft > 0.0f) - { - // If still switching gears, count down - mGearSwitchTimeLeft = max(0.0f, mGearSwitchTimeLeft - inDeltaTime); - mClutchFriction = 0.0f; - } - else if (mClutchReleaseTimeLeft > 0.0f) - { - // After switching the gears we slowly release the clutch - mClutchReleaseTimeLeft = max(0.0f, mClutchReleaseTimeLeft - inDeltaTime); - mClutchFriction = 1.0f - mClutchReleaseTimeLeft / mClutchReleaseTime; - } - else - { - // Clutch has full friction - mClutchFriction = 1.0f; - - // Count down switch latency - mGearSwitchLatencyTimeLeft = max(0.0f, mGearSwitchLatencyTimeLeft - inDeltaTime); - } - } -} - -float VehicleTransmission::GetCurrentRatio() const -{ - if (mCurrentGear < 0) - return mReverseGearRatios[-mCurrentGear - 1]; - else if (mCurrentGear == 0) - return 0.0f; - else - return mGearRatios[mCurrentGear - 1]; -} - -void VehicleTransmission::SaveState(StateRecorder &inStream) const -{ - inStream.Write(mCurrentGear); - inStream.Write(mClutchFriction); - inStream.Write(mGearSwitchTimeLeft); - inStream.Write(mClutchReleaseTimeLeft); - inStream.Write(mGearSwitchLatencyTimeLeft); -} - -void VehicleTransmission::RestoreState(StateRecorder &inStream) -{ - inStream.Read(mCurrentGear); - inStream.Read(mClutchFriction); - inStream.Read(mGearSwitchTimeLeft); - inStream.Read(mClutchReleaseTimeLeft); - inStream.Read(mGearSwitchLatencyTimeLeft); -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleTransmission.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleTransmission.h deleted file mode 100644 index 1d3306a8f9d..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/VehicleTransmission.h +++ /dev/null @@ -1,87 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -/// How gears are shifted -enum class ETransmissionMode : uint8 -{ - Auto, ///< Automatically shift gear up and down - Manual, ///< Manual gear shift (call SetTransmissionInput) -}; - -/// Configuration for the transmission of a vehicle (gear box) -class JPH_EXPORT VehicleTransmissionSettings -{ -public: - JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, VehicleTransmissionSettings) - - /// Saves the contents in binary form to inStream. - void SaveBinaryState(StreamOut &inStream) const; - - /// Restores the contents in binary form to inStream. - void RestoreBinaryState(StreamIn &inStream); - - ETransmissionMode mMode = ETransmissionMode::Auto; ///< How to switch gears - Array mGearRatios { 2.66f, 1.78f, 1.3f, 1.0f, 0.74f }; ///< Ratio in rotation rate between engine and gear box, first element is 1st gear, 2nd element 2nd gear etc. - Array mReverseGearRatios { -2.90f }; ///< Ratio in rotation rate between engine and gear box when driving in reverse - float mSwitchTime = 0.5f; ///< How long it takes to switch gears (s), only used in auto mode - float mClutchReleaseTime = 0.3f; ///< How long it takes to release the clutch (go to full friction), only used in auto mode - float mSwitchLatency = 0.5f; ///< How long to wait after releasing the clutch before another switch is attempted (s), only used in auto mode - float mShiftUpRPM = 4000.0f; ///< If RPM of engine is bigger then this we will shift a gear up, only used in auto mode - float mShiftDownRPM = 2000.0f; ///< If RPM of engine is smaller then this we will shift a gear down, only used in auto mode - float mClutchStrength = 10.0f; ///< Strength of the clutch when fully engaged. Total torque a clutch applies is Torque = ClutchStrength * (Velocity Engine - Avg Velocity Wheels At Clutch) (units: k m^2 s^-1) -}; - -/// Runtime data for transmission -class JPH_EXPORT VehicleTransmission : public VehicleTransmissionSettings -{ -public: - /// Set input from driver regarding the transmission (only relevant when transmission is set to manual mode) - /// @param inCurrentGear Current gear, -1 = reverse, 0 = neutral, 1 = 1st gear etc. - /// @param inClutchFriction Value between 0 and 1 indicating how much friction the clutch gives (0 = no friction, 1 = full friction) - void Set(int inCurrentGear, float inClutchFriction) { mCurrentGear = inCurrentGear; mClutchFriction = inClutchFriction; } - - /// Update the current gear and clutch friction if the transmission is in auto mode - /// @param inDeltaTime Time step delta time in s - /// @param inCurrentRPM Current RPM for engine - /// @param inForwardInput Hint if the user wants to drive forward (> 0) or backwards (< 0) - /// @param inCanShiftUp Indicates if we want to allow the transmission to shift up (e.g. pass false if wheels are slipping) - void Update(float inDeltaTime, float inCurrentRPM, float inForwardInput, bool inCanShiftUp); - - /// Current gear, -1 = reverse, 0 = neutral, 1 = 1st gear etc. - int GetCurrentGear() const { return mCurrentGear; } - - /// Value between 0 and 1 indicating how much friction the clutch gives (0 = no friction, 1 = full friction) - float GetClutchFriction() const { return mClutchFriction; } - - /// If the auto box is currently switching gears - bool IsSwitchingGear() const { return mGearSwitchTimeLeft > 0.0f; } - - /// Return the transmission ratio based on the current gear (ratio between engine and differential) - float GetCurrentRatio() const; - - /// Only allow sleeping when the transmission is idle - bool AllowSleep() const { return mGearSwitchTimeLeft <= 0.0f && mClutchReleaseTimeLeft <= 0.0f && mGearSwitchLatencyTimeLeft <= 0.0f; } - - /// Saving state for replay - void SaveState(StateRecorder &inStream) const; - void RestoreState(StateRecorder &inStream); - -private: - int mCurrentGear = 0; ///< Current gear, -1 = reverse, 0 = neutral, 1 = 1st gear etc. - float mClutchFriction = 1.0f; ///< Value between 0 and 1 indicating how much friction the clutch gives (0 = no friction, 1 = full friction) - float mGearSwitchTimeLeft = 0.0f; ///< When switching gears this will be > 0 and will cause the engine to not provide any torque to the wheels for a short time (used for automatic gear switching only) - float mClutchReleaseTimeLeft = 0.0f; ///< After switching gears this will be > 0 and will cause the clutch friction to go from 0 to 1 (used for automatic gear switching only) - float mGearSwitchLatencyTimeLeft = 0.0f; ///< After releasing the clutch this will be > 0 and will prevent another gear switch (used for automatic gear switching only) -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/Wheel.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/Wheel.cpp deleted file mode 100644 index e43ffb8c9a4..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/Wheel.cpp +++ /dev/null @@ -1,93 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(WheelSettings) -{ - JPH_ADD_ATTRIBUTE(WheelSettings, mSuspensionForcePoint) - JPH_ADD_ATTRIBUTE(WheelSettings, mPosition) - JPH_ADD_ATTRIBUTE(WheelSettings, mSuspensionDirection) - JPH_ADD_ATTRIBUTE(WheelSettings, mSteeringAxis) - JPH_ADD_ATTRIBUTE(WheelSettings, mWheelForward) - JPH_ADD_ATTRIBUTE(WheelSettings, mWheelUp) - JPH_ADD_ATTRIBUTE(WheelSettings, mSuspensionMinLength) - JPH_ADD_ATTRIBUTE(WheelSettings, mSuspensionMaxLength) - JPH_ADD_ATTRIBUTE(WheelSettings, mSuspensionPreloadLength) - JPH_ADD_ENUM_ATTRIBUTE_WITH_ALIAS(WheelSettings, mSuspensionSpring.mMode, "mSuspensionSpringMode") - JPH_ADD_ATTRIBUTE_WITH_ALIAS(WheelSettings, mSuspensionSpring.mFrequency, "mSuspensionFrequency") // Renaming attributes to stay compatible with old versions of the library - JPH_ADD_ATTRIBUTE_WITH_ALIAS(WheelSettings, mSuspensionSpring.mDamping, "mSuspensionDamping") - JPH_ADD_ATTRIBUTE(WheelSettings, mRadius) - JPH_ADD_ATTRIBUTE(WheelSettings, mWidth) - JPH_ADD_ATTRIBUTE(WheelSettings, mEnableSuspensionForcePoint) -} - -void WheelSettings::SaveBinaryState(StreamOut &inStream) const -{ - inStream.Write(mSuspensionForcePoint); - inStream.Write(mPosition); - inStream.Write(mSuspensionDirection); - inStream.Write(mSteeringAxis); - inStream.Write(mWheelForward); - inStream.Write(mWheelUp); - inStream.Write(mSuspensionMinLength); - inStream.Write(mSuspensionMaxLength); - inStream.Write(mSuspensionPreloadLength); - mSuspensionSpring.SaveBinaryState(inStream); - inStream.Write(mRadius); - inStream.Write(mWidth); - inStream.Write(mEnableSuspensionForcePoint); -} - -void WheelSettings::RestoreBinaryState(StreamIn &inStream) -{ - inStream.Read(mSuspensionForcePoint); - inStream.Read(mPosition); - inStream.Read(mSuspensionDirection); - inStream.Read(mSteeringAxis); - inStream.Read(mWheelForward); - inStream.Read(mWheelUp); - inStream.Read(mSuspensionMinLength); - inStream.Read(mSuspensionMaxLength); - inStream.Read(mSuspensionPreloadLength); - mSuspensionSpring.RestoreBinaryState(inStream); - inStream.Read(mRadius); - inStream.Read(mWidth); - inStream.Read(mEnableSuspensionForcePoint); -} - -Wheel::Wheel(const WheelSettings &inSettings) : - mSettings(&inSettings), - mSuspensionLength(inSettings.mSuspensionMaxLength) -{ - JPH_ASSERT(inSettings.mSuspensionDirection.IsNormalized()); - JPH_ASSERT(inSettings.mSteeringAxis.IsNormalized()); - JPH_ASSERT(inSettings.mWheelForward.IsNormalized()); - JPH_ASSERT(inSettings.mWheelUp.IsNormalized()); - JPH_ASSERT(inSettings.mSuspensionMinLength >= 0.0f); - JPH_ASSERT(inSettings.mSuspensionMaxLength >= inSettings.mSuspensionMinLength); - JPH_ASSERT(inSettings.mSuspensionPreloadLength >= 0.0f); - JPH_ASSERT(inSettings.mSuspensionSpring.mFrequency > 0.0f); - JPH_ASSERT(inSettings.mSuspensionSpring.mDamping >= 0.0f); - JPH_ASSERT(inSettings.mRadius > 0.0f); - JPH_ASSERT(inSettings.mWidth >= 0.0f); -} - -bool Wheel::SolveLongitudinalConstraintPart(const VehicleConstraint &inConstraint, float inMinImpulse, float inMaxImpulse) -{ - return mLongitudinalPart.SolveVelocityConstraint(*inConstraint.GetVehicleBody(), *mContactBody, -mContactLongitudinal, inMinImpulse, inMaxImpulse); -} - -bool Wheel::SolveLateralConstraintPart(const VehicleConstraint &inConstraint, float inMinImpulse, float inMaxImpulse) -{ - return mLateralPart.SolveVelocityConstraint(*inConstraint.GetVehicleBody(), *mContactBody, -mContactLateral, inMinImpulse, inMaxImpulse); -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/Wheel.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/Wheel.h deleted file mode 100644 index eb5f6874c30..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/Wheel.h +++ /dev/null @@ -1,148 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -class VehicleConstraint; - -/// Base class for wheel settings, each VehicleController can implement a derived class of this -class JPH_EXPORT WheelSettings : public SerializableObject, public RefTarget -{ -public: - JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, WheelSettings) - - /// Saves the contents in binary form to inStream. - virtual void SaveBinaryState(StreamOut &inStream) const; - - /// Restores the contents in binary form to inStream. - virtual void RestoreBinaryState(StreamIn &inStream); - - Vec3 mPosition { 0, 0, 0 }; ///< Attachment point of wheel suspension in local space of the body - Vec3 mSuspensionForcePoint { 0, 0, 0 }; ///< Where tire forces (suspension and traction) are applied, in local space of the body. A good default is the center of the wheel in its neutral pose. See mEnableSuspensionForcePoint. - Vec3 mSuspensionDirection { 0, -1, 0 }; ///< Direction of the suspension in local space of the body, should point down - Vec3 mSteeringAxis { 0, 1, 0 }; ///< Direction of the steering axis in local space of the body, should point up (e.g. for a bike would be -mSuspensionDirection) - Vec3 mWheelUp { 0, 1, 0 }; ///< Up direction when the wheel is in the neutral steering position (usually VehicleConstraintSettings::mUp but can be used to give the wheel camber or for a bike would be -mSuspensionDirection) - Vec3 mWheelForward { 0, 0, 1 }; ///< Forward direction when the wheel is in the neutral steering position (usually VehicleConstraintSettings::mForward but can be used to give the wheel toe, does not need to be perpendicular to mWheelUp) - float mSuspensionMinLength = 0.3f; ///< How long the suspension is in max raised position relative to the attachment point (m) - float mSuspensionMaxLength = 0.5f; ///< How long the suspension is in max droop position relative to the attachment point (m) - float mSuspensionPreloadLength = 0.0f; ///< The natural length (m) of the suspension spring is defined as mSuspensionMaxLength + mSuspensionPreloadLength. Can be used to preload the suspension as the spring is compressed by mSuspensionPreloadLength when the suspension is in max droop position. Note that this means when the vehicle touches the ground there is a discontinuity so it will also make the vehicle more bouncy as we're updating with discrete time steps. - SpringSettings mSuspensionSpring { ESpringMode::FrequencyAndDamping, 1.5f, 0.5f }; ///< Settings for the suspension spring - float mRadius = 0.3f; ///< Radius of the wheel (m) - float mWidth = 0.1f; ///< Width of the wheel (m) - bool mEnableSuspensionForcePoint = false; ///< Enables mSuspensionForcePoint, if disabled, the forces are applied at the collision contact point. This leads to a more accurate simulation when interacting with dynamic objects but makes the vehicle less stable. When setting this to true, all forces will be applied to a fixed point on the vehicle body. -}; - -/// Base class for runtime data for a wheel, each VehicleController can implement a derived class of this -class JPH_EXPORT Wheel : public NonCopyable -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Constructor / destructor - explicit Wheel(const WheelSettings &inSettings); - virtual ~Wheel() = default; - - /// Get settings for the wheel - const WheelSettings * GetSettings() const { return mSettings; } - - /// Get the angular velocity (rad/s) for this wheel, note that positive means the wheel is rotating such that the car moves forward - float GetAngularVelocity() const { return mAngularVelocity; } - - /// Update the angular velocity (rad/s) - void SetAngularVelocity(float inVel) { mAngularVelocity = inVel; } - - /// Get the current rotation angle of the wheel in radians [0, 2 pi] - float GetRotationAngle() const { return mAngle; } - - /// Set the current rotation angle of the wheel in radians [0, 2 pi] - void SetRotationAngle(float inAngle) { mAngle = inAngle; } - - /// Get the current steer angle of the wheel in radians [-pi, pi], positive is to the left - float GetSteerAngle() const { return mSteerAngle; } - - /// Set the current steer angle of the wheel in radians [-pi, pi] - void SetSteerAngle(float inAngle) { mSteerAngle = inAngle; } - - /// Returns true if the wheel is touching an object - inline bool HasContact() const { return !mContactBodyID.IsInvalid(); } - - /// Returns the body ID of the body that this wheel is touching - BodyID GetContactBodyID() const { return mContactBodyID; } - - /// Returns the sub shape ID where we're contacting the body - SubShapeID GetContactSubShapeID() const { return mContactSubShapeID; } - - /// Returns the current contact position in world space (note by the time you call this the vehicle has moved) - RVec3 GetContactPosition() const { JPH_ASSERT(HasContact()); return mContactPosition; } - - /// Velocity of the contact point (m / s, not relative to the wheel but in world space) - Vec3 GetContactPointVelocity() const { JPH_ASSERT(HasContact()); return mContactPointVelocity; } - - /// Returns the current contact normal in world space (note by the time you call this the vehicle has moved) - Vec3 GetContactNormal() const { JPH_ASSERT(HasContact()); return mContactNormal; } - - /// Returns longitudinal direction (direction along the wheel relative to floor) in world space (note by the time you call this the vehicle has moved) - Vec3 GetContactLongitudinal() const { JPH_ASSERT(HasContact()); return mContactLongitudinal; } - - /// Returns lateral direction (sideways direction) in world space (note by the time you call this the vehicle has moved) - Vec3 GetContactLateral() const { JPH_ASSERT(HasContact()); return mContactLateral; } - - /// Get the length of the suspension for a wheel (m) relative to the suspension attachment point (hard point) - float GetSuspensionLength() const { return mSuspensionLength; } - - /// Check if the suspension hit its upper limit - bool HasHitHardPoint() const { return mSuspensionMaxUpPart.IsActive(); } - - /// Get the total impulse (N s) that was applied by the suspension - float GetSuspensionLambda() const { return mSuspensionPart.GetTotalLambda() + mSuspensionMaxUpPart.GetTotalLambda(); } - - /// Get total impulse (N s) applied along the forward direction of the wheel - float GetLongitudinalLambda() const { return mLongitudinalPart.GetTotalLambda(); } - - /// Get total impulse (N s) applied along the sideways direction of the wheel - float GetLateralLambda() const { return mLateralPart.GetTotalLambda(); } - - /// Internal function that should only be called by the controller. Used to apply impulses in the forward direction of the vehicle. - bool SolveLongitudinalConstraintPart(const VehicleConstraint &inConstraint, float inMinImpulse, float inMaxImpulse); - - /// Internal function that should only be called by the controller. Used to apply impulses in the sideways direction of the vehicle. - bool SolveLateralConstraintPart(const VehicleConstraint &inConstraint, float inMinImpulse, float inMaxImpulse); - -protected: - friend class VehicleConstraint; - - RefConst mSettings; ///< Configuration settings for this wheel - BodyID mContactBodyID; ///< ID of body for ground - SubShapeID mContactSubShapeID; ///< Sub shape ID for ground - Body * mContactBody = nullptr; ///< Body for ground - float mSuspensionLength; ///< Current length of the suspension - RVec3 mContactPosition; ///< Position of the contact point between wheel and ground - Vec3 mContactPointVelocity; ///< Velocity of the contact point (m / s, not relative to the wheel but in world space) - Vec3 mContactNormal; ///< Normal of the contact point between wheel and ground - Vec3 mContactLongitudinal; ///< Vector perpendicular to normal in the forward direction - Vec3 mContactLateral; ///< Vector perpendicular to normal and longitudinal direction in the right direction - Real mAxlePlaneConstant; ///< Constant for the contact plane of the axle, defined as ContactNormal . (WorldSpaceSuspensionPoint + SuspensionLength * WorldSpaceSuspensionDirection) - float mAntiRollBarImpulse = 0.0f; ///< Amount of impulse applied to the suspension from the anti-rollbars - - float mSteerAngle = 0.0f; ///< Rotation around the suspension direction, positive is to the left - float mAngularVelocity = 0.0f; ///< Rotation speed of wheel, positive when the wheels cause the vehicle to move forwards (rad/s) - float mAngle = 0.0f; ///< Current rotation of the wheel (rad, [0, 2 pi]) - - AxisConstraintPart mSuspensionPart; ///< Controls movement up/down along the contact normal - AxisConstraintPart mSuspensionMaxUpPart; ///< Adds a hard limit when reaching the minimal suspension length - AxisConstraintPart mLongitudinalPart; ///< Controls movement forward/backward - AxisConstraintPart mLateralPart; ///< Controls movement sideways (slip) -}; - -using Wheels = Array; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/WheeledVehicleController.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/WheeledVehicleController.cpp deleted file mode 100644 index 8e4a71a4fb8..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/WheeledVehicleController.cpp +++ /dev/null @@ -1,845 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include -#include -#include -#include -#include -#ifdef JPH_DEBUG_RENDERER - #include -#endif // JPH_DEBUG_RENDERER - -//#define JPH_TRACE_VEHICLE_STATS - -JPH_NAMESPACE_BEGIN - -JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(WheeledVehicleControllerSettings) -{ - JPH_ADD_BASE_CLASS(WheeledVehicleControllerSettings, VehicleControllerSettings) - - JPH_ADD_ATTRIBUTE(WheeledVehicleControllerSettings, mEngine) - JPH_ADD_ATTRIBUTE(WheeledVehicleControllerSettings, mTransmission) - JPH_ADD_ATTRIBUTE(WheeledVehicleControllerSettings, mDifferentials) - JPH_ADD_ATTRIBUTE(WheeledVehicleControllerSettings, mDifferentialLimitedSlipRatio) -} - -JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(WheelSettingsWV) -{ - JPH_ADD_ATTRIBUTE(WheelSettingsWV, mInertia) - JPH_ADD_ATTRIBUTE(WheelSettingsWV, mAngularDamping) - JPH_ADD_ATTRIBUTE(WheelSettingsWV, mMaxSteerAngle) - JPH_ADD_ATTRIBUTE(WheelSettingsWV, mLongitudinalFriction) - JPH_ADD_ATTRIBUTE(WheelSettingsWV, mLateralFriction) - JPH_ADD_ATTRIBUTE(WheelSettingsWV, mMaxBrakeTorque) - JPH_ADD_ATTRIBUTE(WheelSettingsWV, mMaxHandBrakeTorque) -} - -WheelSettingsWV::WheelSettingsWV() -{ - mLongitudinalFriction.Reserve(3); - mLongitudinalFriction.AddPoint(0.0f, 0.0f); - mLongitudinalFriction.AddPoint(0.06f, 1.2f); - mLongitudinalFriction.AddPoint(0.2f, 1.0f); - - mLateralFriction.Reserve(3); - mLateralFriction.AddPoint(0.0f, 0.0f); - mLateralFriction.AddPoint(3.0f, 1.2f); - mLateralFriction.AddPoint(20.0f, 1.0f); -} - -void WheelSettingsWV::SaveBinaryState(StreamOut &inStream) const -{ - inStream.Write(mInertia); - inStream.Write(mAngularDamping); - inStream.Write(mMaxSteerAngle); - mLongitudinalFriction.SaveBinaryState(inStream); - mLateralFriction.SaveBinaryState(inStream); - inStream.Write(mMaxBrakeTorque); - inStream.Write(mMaxHandBrakeTorque); -} - -void WheelSettingsWV::RestoreBinaryState(StreamIn &inStream) -{ - inStream.Read(mInertia); - inStream.Read(mAngularDamping); - inStream.Read(mMaxSteerAngle); - mLongitudinalFriction.RestoreBinaryState(inStream); - mLateralFriction.RestoreBinaryState(inStream); - inStream.Read(mMaxBrakeTorque); - inStream.Read(mMaxHandBrakeTorque); -} - -WheelWV::WheelWV(const WheelSettingsWV &inSettings) : - Wheel(inSettings) -{ - JPH_ASSERT(inSettings.mInertia >= 0.0f); - JPH_ASSERT(inSettings.mAngularDamping >= 0.0f); - JPH_ASSERT(abs(inSettings.mMaxSteerAngle) <= 0.5f * JPH_PI); - JPH_ASSERT(inSettings.mMaxBrakeTorque >= 0.0f); - JPH_ASSERT(inSettings.mMaxHandBrakeTorque >= 0.0f); -} - -void WheelWV::Update(uint inWheelIndex, float inDeltaTime, const VehicleConstraint &inConstraint) -{ - const WheelSettingsWV *settings = GetSettings(); - - // Angular damping: dw/dt = -c * w - // Solution: w(t) = w(0) * e^(-c * t) or w2 = w1 * e^(-c * dt) - // Taylor expansion of e^(-c * dt) = 1 - c * dt + ... - // Since dt is usually in the order of 1/60 and c is a low number too this approximation is good enough - mAngularVelocity *= max(0.0f, 1.0f - settings->mAngularDamping * inDeltaTime); - - // Update rotation of wheel - mAngle = fmod(mAngle + mAngularVelocity * inDeltaTime, 2.0f * JPH_PI); - - if (mContactBody != nullptr) - { - const Body *body = inConstraint.GetVehicleBody(); - - // Calculate relative velocity between wheel contact point and floor - Vec3 relative_velocity = body->GetPointVelocity(mContactPosition) - mContactPointVelocity; - - // Cancel relative velocity in the normal plane - relative_velocity -= mContactNormal.Dot(relative_velocity) * mContactNormal; - float relative_longitudinal_velocity = relative_velocity.Dot(mContactLongitudinal); - - // Calculate longitudinal friction based on difference between velocity of rolling wheel and drive surface - float relative_longitudinal_velocity_denom = Sign(relative_longitudinal_velocity) * max(1.0e-3f, abs(relative_longitudinal_velocity)); // Ensure we don't divide by zero - mLongitudinalSlip = abs((mAngularVelocity * settings->mRadius - relative_longitudinal_velocity) / relative_longitudinal_velocity_denom); - float longitudinal_slip_friction = settings->mLongitudinalFriction.GetValue(mLongitudinalSlip); - - // Calculate lateral friction based on slip angle - float relative_velocity_len = relative_velocity.Length(); - mLateralSlip = relative_velocity_len < 1.0e-3f ? 0.0f : ACos(abs(relative_longitudinal_velocity) / relative_velocity_len); - float lateral_slip_angle = RadiansToDegrees(mLateralSlip); - float lateral_slip_friction = settings->mLateralFriction.GetValue(lateral_slip_angle); - - // Tire friction - VehicleConstraint::CombineFunction combine_friction = inConstraint.GetCombineFriction(); - mCombinedLongitudinalFriction = longitudinal_slip_friction; - mCombinedLateralFriction = lateral_slip_friction; - combine_friction(inWheelIndex, mCombinedLongitudinalFriction, mCombinedLateralFriction, *mContactBody, mContactSubShapeID); - } - else - { - // No collision - mLongitudinalSlip = 0.0f; - mLateralSlip = 0.0f; - mCombinedLongitudinalFriction = mCombinedLateralFriction = 0.0f; - } -} - -VehicleController *WheeledVehicleControllerSettings::ConstructController(VehicleConstraint &inConstraint) const -{ - return new WheeledVehicleController(*this, inConstraint); -} - -void WheeledVehicleControllerSettings::SaveBinaryState(StreamOut &inStream) const -{ - mEngine.SaveBinaryState(inStream); - - mTransmission.SaveBinaryState(inStream); - - uint32 num_differentials = (uint32)mDifferentials.size(); - inStream.Write(num_differentials); - for (const VehicleDifferentialSettings &d : mDifferentials) - d.SaveBinaryState(inStream); - - inStream.Write(mDifferentialLimitedSlipRatio); -} - -void WheeledVehicleControllerSettings::RestoreBinaryState(StreamIn &inStream) -{ - mEngine.RestoreBinaryState(inStream); - - mTransmission.RestoreBinaryState(inStream); - - uint32 num_differentials = 0; - inStream.Read(num_differentials); - mDifferentials.resize(num_differentials); - for (VehicleDifferentialSettings &d : mDifferentials) - d.RestoreBinaryState(inStream); - - inStream.Read(mDifferentialLimitedSlipRatio); -} - -WheeledVehicleController::WheeledVehicleController(const WheeledVehicleControllerSettings &inSettings, VehicleConstraint &inConstraint) : - VehicleController(inConstraint) -{ - // Copy engine settings - static_cast(mEngine) = inSettings.mEngine; - JPH_ASSERT(inSettings.mEngine.mMinRPM >= 0.0f); - JPH_ASSERT(inSettings.mEngine.mMinRPM <= inSettings.mEngine.mMaxRPM); - mEngine.SetCurrentRPM(mEngine.mMinRPM); - - // Copy transmission settings - static_cast(mTransmission) = inSettings.mTransmission; -#ifdef JPH_ENABLE_ASSERTS - for (float r : inSettings.mTransmission.mGearRatios) - JPH_ASSERT(r > 0.0f); - for (float r : inSettings.mTransmission.mReverseGearRatios) - JPH_ASSERT(r < 0.0f); -#endif // JPH_ENABLE_ASSERTS - JPH_ASSERT(inSettings.mTransmission.mSwitchTime >= 0.0f); - JPH_ASSERT(inSettings.mTransmission.mShiftDownRPM > 0.0f); - JPH_ASSERT(inSettings.mTransmission.mMode != ETransmissionMode::Auto || inSettings.mTransmission.mShiftUpRPM < inSettings.mEngine.mMaxRPM); - JPH_ASSERT(inSettings.mTransmission.mShiftUpRPM > inSettings.mTransmission.mShiftDownRPM); - JPH_ASSERT(inSettings.mTransmission.mClutchStrength > 0.0f); - - // Copy differential settings - mDifferentials.resize(inSettings.mDifferentials.size()); - for (uint i = 0; i < mDifferentials.size(); ++i) - { - const VehicleDifferentialSettings &d = inSettings.mDifferentials[i]; - mDifferentials[i] = d; - JPH_ASSERT(d.mDifferentialRatio > 0.0f); - JPH_ASSERT(d.mLeftRightSplit >= 0.0f && d.mLeftRightSplit <= 1.0f); - JPH_ASSERT(d.mEngineTorqueRatio >= 0.0f); - JPH_ASSERT(d.mLimitedSlipRatio > 1.0f); - } - - mDifferentialLimitedSlipRatio = inSettings.mDifferentialLimitedSlipRatio; - JPH_ASSERT(mDifferentialLimitedSlipRatio > 1.0f); -} - -float WheeledVehicleController::GetWheelSpeedAtClutch() const -{ - float wheel_speed_at_clutch = 0.0f; - int num_driven_wheels = 0; - for (const VehicleDifferentialSettings &d : mDifferentials) - { - int wheels[] = { d.mLeftWheel, d.mRightWheel }; - for (int w : wheels) - if (w >= 0) - { - wheel_speed_at_clutch += mConstraint.GetWheel(w)->GetAngularVelocity() * d.mDifferentialRatio; - num_driven_wheels++; - } - } - return wheel_speed_at_clutch / float(num_driven_wheels) * VehicleEngine::cAngularVelocityToRPM * mTransmission.GetCurrentRatio(); -} - -bool WheeledVehicleController::AllowSleep() const -{ - return mForwardInput == 0.0f // No user input - && mTransmission.AllowSleep() // Transmission is not shifting - && mEngine.AllowSleep(); // Engine is idling -} - -void WheeledVehicleController::PreCollide(float inDeltaTime, PhysicsSystem &inPhysicsSystem) -{ - JPH_PROFILE_FUNCTION(); - -#ifdef JPH_TRACE_VEHICLE_STATS - static bool sTracedHeader = false; - if (!sTracedHeader) - { - Trace("Time, ForwardInput, Gear, ClutchFriction, EngineRPM, WheelRPM, Velocity (km/h)"); - sTracedHeader = true; - } - static float sTime = 0.0f; - sTime += inDeltaTime; - Trace("%.3f, %.1f, %d, %.1f, %.1f, %.1f, %.1f", sTime, mForwardInput, mTransmission.GetCurrentGear(), mTransmission.GetClutchFriction(), mEngine.GetCurrentRPM(), GetWheelSpeedAtClutch(), mConstraint.GetVehicleBody()->GetLinearVelocity().Length() * 3.6f); -#endif // JPH_TRACE_VEHICLE_STATS - - for (Wheel *w_base : mConstraint.GetWheels()) - { - WheelWV *w = static_cast(w_base); - - // Set steering angle - w->SetSteerAngle(-mRightInput * w->GetSettings()->mMaxSteerAngle); - } -} - -void WheeledVehicleController::PostCollide(float inDeltaTime, PhysicsSystem &inPhysicsSystem) -{ - JPH_PROFILE_FUNCTION(); - - // Remember old RPM so we can detect if we're increasing or decreasing - float old_engine_rpm = mEngine.GetCurrentRPM(); - - Wheels &wheels = mConstraint.GetWheels(); - - // Update wheel angle, do this before applying torque to the wheels (as friction will slow them down again) - for (uint wheel_index = 0, num_wheels = (uint)wheels.size(); wheel_index < num_wheels; ++wheel_index) - { - WheelWV *w = static_cast(wheels[wheel_index]); - w->Update(wheel_index, inDeltaTime, mConstraint); - } - - // In auto transmission mode, don't accelerate the engine when switching gears - float forward_input = abs(mForwardInput); - if (mTransmission.mMode == ETransmissionMode::Auto) - forward_input *= mTransmission.GetClutchFriction(); - - // Apply engine damping - mEngine.ApplyDamping(inDeltaTime); - - // Calculate engine torque - float engine_torque = mEngine.GetTorque(forward_input); - - // Define a struct that contains information about driven differentials (i.e. that have wheels connected) - struct DrivenDifferential - { - const VehicleDifferentialSettings * mDifferential; - float mAngularVelocity; - float mClutchToDifferentialTorqueRatio; - float mTempTorqueFactor; - }; - - // Collect driven differentials and their speeds - Array driven_differentials; - driven_differentials.reserve(mDifferentials.size()); - float differential_omega_min = FLT_MAX, differential_omega_max = 0.0f; - for (const VehicleDifferentialSettings &d : mDifferentials) - { - float avg_omega = 0.0f; - int avg_omega_denom = 0; - int indices[] = { d.mLeftWheel, d.mRightWheel }; - for (int idx : indices) - if (idx != -1) - { - avg_omega += wheels[idx]->GetAngularVelocity(); - avg_omega_denom++; - } - - if (avg_omega_denom > 0) - { - avg_omega = abs(avg_omega * d.mDifferentialRatio / float(avg_omega_denom)); // ignoring that the differentials may be rotating in different directions - driven_differentials.push_back({ &d, avg_omega, d.mEngineTorqueRatio, 0 }); - - // Remember min and max velocity - differential_omega_min = min(differential_omega_min, avg_omega); - differential_omega_max = max(differential_omega_max, avg_omega); - } - } - - if (mDifferentialLimitedSlipRatio < FLT_MAX // Limited slip differential needs to be turned on - && differential_omega_max > differential_omega_min) // There needs to be a velocity difference - { - // Calculate factor based on relative speed of a differential - float sum_factor = 0.0f; - for (DrivenDifferential &d : driven_differentials) - { - // Differential with max velocity gets factor 0, differential with min velocity 1 - d.mTempTorqueFactor = (differential_omega_max - d.mAngularVelocity) / (differential_omega_max - differential_omega_min); - sum_factor += d.mTempTorqueFactor; - } - - // Normalize the result - for (DrivenDifferential &d : driven_differentials) - d.mTempTorqueFactor /= sum_factor; - - // Prevent div by zero - differential_omega_min = max(1.0e-3f, differential_omega_min); - differential_omega_max = max(1.0e-3f, differential_omega_max); - - // Map into a value that is 0 when the wheels are turning at an equal rate and 1 when the wheels are turning at mDifferentialLimitedSlipRatio - float alpha = min((differential_omega_max / differential_omega_min - 1.0f) / (mDifferentialLimitedSlipRatio - 1.0f), 1.0f); - JPH_ASSERT(alpha >= 0.0f); - float one_min_alpha = 1.0f - alpha; - - // Update torque ratio for all differentials - for (DrivenDifferential &d : driven_differentials) - d.mClutchToDifferentialTorqueRatio = one_min_alpha * d.mClutchToDifferentialTorqueRatio + alpha * d.mTempTorqueFactor; - } - -#ifdef JPH_ENABLE_ASSERTS - // Assert the values add up to 1 - float sum_torque_factors = 0.0f; - for (DrivenDifferential &d : driven_differentials) - sum_torque_factors += d.mClutchToDifferentialTorqueRatio; - JPH_ASSERT(abs(sum_torque_factors - 1.0f) < 1.0e-6f); -#endif // JPH_ENABLE_ASSERTS - - // Define a struct that collects information about the wheels that connect to the engine - struct DrivenWheel - { - WheelWV * mWheel; - float mClutchToWheelRatio; - float mClutchToWheelTorqueRatio; - float mEstimatedAngularImpulse; - }; - Array driven_wheels; - driven_wheels.reserve(wheels.size()); - - // Collect driven wheels - float transmission_ratio = mTransmission.GetCurrentRatio(); - for (const DrivenDifferential &dd : driven_differentials) - { - VehicleDifferentialSettings d = *dd.mDifferential; - - WheelWV *wl = d.mLeftWheel != -1? static_cast(wheels[d.mLeftWheel]) : nullptr; - WheelWV *wr = d.mRightWheel != -1? static_cast(wheels[d.mRightWheel]) : nullptr; - - float clutch_to_wheel_ratio = transmission_ratio * d.mDifferentialRatio; - - if (wl != nullptr && wr != nullptr) - { - // Calculate torque ratio - float ratio_l, ratio_r; - d.CalculateTorqueRatio(wl->GetAngularVelocity(), wr->GetAngularVelocity(), ratio_l, ratio_r); - - // Add both wheels - driven_wheels.push_back({ wl, clutch_to_wheel_ratio, dd.mClutchToDifferentialTorqueRatio * ratio_l, 0.0f }); - driven_wheels.push_back({ wr, clutch_to_wheel_ratio, dd.mClutchToDifferentialTorqueRatio * ratio_r, 0.0f }); - } - else if (wl != nullptr) - { - // Only left wheel, all power to left - driven_wheels.push_back({ wl, clutch_to_wheel_ratio, dd.mClutchToDifferentialTorqueRatio, 0.0f }); - } - else if (wr != nullptr) - { - // Only right wheel, all power to right - driven_wheels.push_back({ wr, clutch_to_wheel_ratio, dd.mClutchToDifferentialTorqueRatio, 0.0f }); - } - } - - bool solved = false; - if (!driven_wheels.empty()) - { - // Define the torque at the clutch at time t as: - // - // tc(t):=S*(we(t)-sum(R(j)*ww(j,t),j,1,N)/N) - // - // Where: - // S is the total strength of clutch (= friction * strength) - // we(t) is the engine angular velocity at time t - // R(j) is the total gear ratio of clutch to wheel for wheel j - // ww(j,t) is the angular velocity of wheel j at time t - // N is the amount of wheels - // - // The torque that increases the engine angular velocity at time t is: - // - // te(t):=TE-tc(t) - // - // Where: - // TE is the torque delivered by the engine - // - // The torque that increases the wheel angular velocity for wheel i at time t is: - // - // tw(i,t):=TW(i)+R(i)*F(i)*tc(t) - // - // Where: - // TW(i) is the torque applied to the wheel outside of the engine (brake + torque due to friction with the ground) - // F(i) is the fraction of the engine torque applied from engine to wheel i - // - // Because the angular acceleration and torque are connected through: Torque = I * dw/dt - // - // We have the angular acceleration of the engine at time t: - // - // ddt_we(t):=te(t)/Ie - // - // Where: - // Ie is the inertia of the engine - // - // We have the angular acceleration of wheel i at time t: - // - // ddt_ww(i,t):=tw(i,t)/Iw(i) - // - // Where: - // Iw(i) is the inertia of wheel i - // - // We could take a simple Euler step to calculate the resulting accelerations but because the system is very stiff this turns out to be unstable, so we need to use implicit Euler instead: - // - // we(t+dt)=we(t)+dt*ddt_we(t+dt) - // - // and: - // - // ww(i,t+dt)=ww(i,t)+dt*ddt_ww(i,t+dt) - // - // Expanding both equations (the equations above are in wxMaxima format and this can easily be done by expand(%)): - // - // For wheel: - // - // ww(i,t+dt) + (S*dt*F(i)*R(i)*sum(R(j)*ww(j,t+dt),j,1,N))/(N*Iw(i)) - (S*dt*F(i)*R(i)*we(t+dt))/Iw(i) = ww(i,t)+(dt*TW(i))/Iw(i) - // - // For engine: - // - // we(t+dt) + (S*dt*we(t+dt))/Ie - (S*dt*sum(R(j)*ww(j,t+dt),j,1,N))/(Ie*N) = we(t)+(TE*dt)/Ie - // - // Defining a vector w(t) = (ww(1, t), ww(2, t), ..., ww(N, t), we(t)) we can write both equations as a matrix multiplication: - // - // a * w(t + dt) = b - // - // We then invert the matrix to get the new angular velocities. - - // Dimension of matrix is N + 1 - int n = (int)driven_wheels.size() + 1; - - // Last column of w is for the engine angular velocity - int engine = n - 1; - - // Define a and b - DynMatrix a(n, n); - DynMatrix b(n, 1); - - // Get number of driven wheels as a float - float num_driven_wheels_float = float(driven_wheels.size()); - - // Angular velocity of engine - float w_engine = mEngine.GetAngularVelocity(); - - // Calculate the total strength of the clutch - float clutch_strength = transmission_ratio != 0.0f? mTransmission.GetClutchFriction() * mTransmission.mClutchStrength : 0.0f; - - // dt / Ie - float dt_div_ie = inDeltaTime / mEngine.mInertia; - - // Calculate scale factor for impulses based on previous delta time - float impulse_scale = mPreviousDeltaTime > 0.0f? inDeltaTime / mPreviousDeltaTime : 0.0f; - - // Iterate the rows for the wheels - for (int i = 0; i < (int)driven_wheels.size(); ++i) - { - DrivenWheel &w_i = driven_wheels[i]; - const WheelSettingsWV *settings = w_i.mWheel->GetSettings(); - - // Get wheel inertia - float inertia = settings->mInertia; - - // S * R(i) - float s_r = clutch_strength * w_i.mClutchToWheelRatio; - - // dt * S * R(i) * F(i) / Iw - float dt_s_r_f_div_iw = inDeltaTime * s_r * w_i.mClutchToWheelTorqueRatio / inertia; - - // Fill in the columns of a for wheel j - for (int j = 0; j < (int)driven_wheels.size(); ++j) - { - const DrivenWheel &w_j = driven_wheels[j]; - a(i, j) = dt_s_r_f_div_iw * w_j.mClutchToWheelRatio / num_driven_wheels_float; - } - - // Add ww(i, t+dt) - a(i, i) += 1.0f; - - // Add the column for the engine - a(i, engine) = -dt_s_r_f_div_iw; - - // Calculate external angular impulse operating on the wheel: TW(i) * dt - float dt_tw = 0.0f; - - // Combine brake with hand brake torque - float brake_torque = mBrakeInput * settings->mMaxBrakeTorque + mHandBrakeInput * settings->mMaxHandBrakeTorque; - if (brake_torque > 0.0f) - { - // We're braking - // Calculate brake angular impulse - float sign; - if (w_i.mWheel->GetAngularVelocity() != 0.0f) - sign = Sign(w_i.mWheel->GetAngularVelocity()); - else - sign = Sign(mTransmission.GetCurrentRatio()); // When wheels have locked up use the transmission ratio to determine the sign - dt_tw = sign * inDeltaTime * brake_torque; - } - - if (w_i.mWheel->HasContact()) - { - // We have wheel contact with the floor - // Note that we don't know the torque due to the ground contact yet, so we use the impulse applied from the last frame to estimate it - // Wheel torque TW = force * radius = lambda / dt * radius - dt_tw += impulse_scale * w_i.mWheel->GetLongitudinalLambda() * settings->mRadius; - } - - w_i.mEstimatedAngularImpulse = dt_tw; - - // Fill in the constant b = ww(i,t)+(dt*TW(i))/Iw(i) - b(i, 0) = w_i.mWheel->GetAngularVelocity() - dt_tw / inertia; - - // To avoid looping over the wheels again, we also fill in the wheel columns of the engine row here - a(engine, i) = -dt_div_ie * s_r / num_driven_wheels_float; - } - - // Finalize the engine row - a(engine, engine) = (1.0f + dt_div_ie * clutch_strength); - b(engine, 0) = w_engine + dt_div_ie * engine_torque; - - // Solve the linear equation - if (GaussianElimination(a, b)) - { - // Update the angular velocities for the wheels - for (int i = 0; i < (int)driven_wheels.size(); ++i) - { - DrivenWheel &w_i = driven_wheels[i]; - const WheelSettingsWV *settings = w_i.mWheel->GetSettings(); - - // Get solved wheel angular velocity - float angular_velocity = b(i, 0); - - // We estimated TW and applied it in the equation above, but we haven't actually applied this torque yet so we undo it here. - // It will be applied when we solve the actual braking / the constraints with the floor. - angular_velocity += w_i.mEstimatedAngularImpulse / settings->mInertia; - - // Update angular velocity - w_i.mWheel->SetAngularVelocity(angular_velocity); - } - - // Update the engine RPM - mEngine.SetCurrentRPM(b(engine, 0) * VehicleEngine::cAngularVelocityToRPM); - - // The speeds have been solved - solved = true; - } - else - { - JPH_ASSERT(false, "New engine/wheel speeds could not be calculated!"); - } - } - - if (!solved) - { - // Engine not connected to wheels, apply all torque to engine rotation - mEngine.ApplyTorque(engine_torque, inDeltaTime); - } - - // Calculate if any of the wheels are slipping, this is used to prevent gear switching - bool wheels_slipping = false; - for (const DrivenWheel &w : driven_wheels) - wheels_slipping |= w.mClutchToWheelTorqueRatio > 0.0f && (!w.mWheel->HasContact() || w.mWheel->mLongitudinalSlip > 0.1f); - - // Only allow shifting up when we're not slipping and we're increasing our RPM. - // After a jump, we have a very high engine RPM but once we hit the ground the RPM should be decreasing and we don't want to shift up - // during that time. - bool can_shift_up = !wheels_slipping && mEngine.GetCurrentRPM() >= old_engine_rpm; - - // Update transmission - mTransmission.Update(inDeltaTime, mEngine.GetCurrentRPM(), mForwardInput, can_shift_up); - - // Braking - for (Wheel *w_base : wheels) - { - WheelWV *w = static_cast(w_base); - const WheelSettingsWV *settings = w->GetSettings(); - - // Combine brake with hand brake torque - float brake_torque = mBrakeInput * settings->mMaxBrakeTorque + mHandBrakeInput * settings->mMaxHandBrakeTorque; - if (brake_torque > 0.0f) - { - // Calculate how much torque is needed to stop the wheels from rotating in this time step - float brake_torque_to_lock_wheels = abs(w->GetAngularVelocity()) * settings->mInertia / inDeltaTime; - if (brake_torque > brake_torque_to_lock_wheels) - { - // Wheels are locked - w->SetAngularVelocity(0.0f); - w->mBrakeImpulse = (brake_torque - brake_torque_to_lock_wheels) * inDeltaTime / settings->mRadius; - } - else - { - // Slow down the wheels - w->ApplyTorque(-Sign(w->GetAngularVelocity()) * brake_torque, inDeltaTime); - w->mBrakeImpulse = 0.0f; - } - } - else - { - // Not braking - w->mBrakeImpulse = 0.0f; - } - } - - // Remember previous delta time so we can scale the impulses correctly - mPreviousDeltaTime = inDeltaTime; -} - -bool WheeledVehicleController::SolveLongitudinalAndLateralConstraints(float inDeltaTime) -{ - bool impulse = false; - - float *max_lateral_friction_impulse = (float *)JPH_STACK_ALLOC(mConstraint.GetWheels().size() * sizeof(float)); - - uint wheel_index = 0; - for (Wheel *w_base : mConstraint.GetWheels()) - { - if (w_base->HasContact()) - { - WheelWV *w = static_cast(w_base); - const WheelSettingsWV *settings = w->GetSettings(); - - // Calculate max impulse that we can apply on the ground - float max_longitudinal_friction_impulse; - mTireMaxImpulseCallback(wheel_index, - max_longitudinal_friction_impulse, max_lateral_friction_impulse[wheel_index], w->GetSuspensionLambda(), - w->mCombinedLongitudinalFriction, w->mCombinedLateralFriction, w->mLongitudinalSlip, w->mLateralSlip, inDeltaTime); - - // Calculate relative velocity between wheel contact point and floor in longitudinal direction - Vec3 relative_velocity = mConstraint.GetVehicleBody()->GetPointVelocity(w->GetContactPosition()) - w->GetContactPointVelocity(); - float relative_longitudinal_velocity = relative_velocity.Dot(w->GetContactLongitudinal()); - - // Calculate brake force to apply - float min_longitudinal_impulse, max_longitudinal_impulse; - if (w->mBrakeImpulse != 0.0f) - { - // Limit brake force by max tire friction - float brake_impulse = min(w->mBrakeImpulse, max_longitudinal_friction_impulse); - - // Check which direction the brakes should be applied (we don't want to apply an impulse that would accelerate the vehicle) - if (relative_longitudinal_velocity >= 0.0f) - { - min_longitudinal_impulse = -brake_impulse; - max_longitudinal_impulse = 0.0f; - } - else - { - min_longitudinal_impulse = 0.0f; - max_longitudinal_impulse = brake_impulse; - } - - // Longitudinal impulse, note that we assume that once the wheels are locked that the brakes have more than enough torque to keep the wheels locked so we exclude any rotation deltas - impulse |= w->SolveLongitudinalConstraintPart(mConstraint, min_longitudinal_impulse, max_longitudinal_impulse); - } - else - { - // Assume we want to apply an angular impulse that makes the delta velocity between wheel and ground zero in one time step, calculate the amount of linear impulse needed to do that - float desired_angular_velocity = relative_longitudinal_velocity / settings->mRadius; - float linear_impulse = (w->GetAngularVelocity() - desired_angular_velocity) * settings->mInertia / settings->mRadius; - - // Limit the impulse by max tire friction - float prev_lambda = w->GetLongitudinalLambda(); - min_longitudinal_impulse = max_longitudinal_impulse = Clamp(prev_lambda + linear_impulse, -max_longitudinal_friction_impulse, max_longitudinal_friction_impulse); - - // Longitudinal impulse - impulse |= w->SolveLongitudinalConstraintPart(mConstraint, min_longitudinal_impulse, max_longitudinal_impulse); - - // Update the angular velocity of the wheels according to the lambda that was applied - w->SetAngularVelocity(w->GetAngularVelocity() - (w->GetLongitudinalLambda() - prev_lambda) * settings->mRadius / settings->mInertia); - } - } - ++wheel_index; - } - - wheel_index = 0; - for (Wheel *w_base : mConstraint.GetWheels()) - { - if (w_base->HasContact()) - { - WheelWV *w = static_cast(w_base); - - // Lateral friction - float max_lateral_impulse = max_lateral_friction_impulse[wheel_index]; - impulse |= w->SolveLateralConstraintPart(mConstraint, -max_lateral_impulse, max_lateral_impulse); - } - ++wheel_index; - } - - return impulse; -} - -#ifdef JPH_DEBUG_RENDERER - -void WheeledVehicleController::Draw(DebugRenderer *inRenderer) const -{ - float constraint_size = mConstraint.GetDrawConstraintSize(); - - // Draw RPM - Body *body = mConstraint.GetVehicleBody(); - Vec3 rpm_meter_up = body->GetRotation() * mConstraint.GetLocalUp(); - RVec3 rpm_meter_pos = body->GetPosition() + body->GetRotation() * mRPMMeterPosition; - Vec3 rpm_meter_fwd = body->GetRotation() * mConstraint.GetLocalForward(); - mEngine.DrawRPM(inRenderer, rpm_meter_pos, rpm_meter_fwd, rpm_meter_up, mRPMMeterSize, mTransmission.mShiftDownRPM, mTransmission.mShiftUpRPM); - - if (mTransmission.GetCurrentRatio() != 0.0f) - { - // Calculate average wheel speed at clutch - float wheel_speed_at_clutch = GetWheelSpeedAtClutch(); - - // Draw the average wheel speed measured at clutch to compare engine RPM with wheel RPM - inRenderer->DrawLine(rpm_meter_pos, rpm_meter_pos + Quat::sRotation(rpm_meter_fwd, mEngine.ConvertRPMToAngle(wheel_speed_at_clutch)) * (rpm_meter_up * 1.1f * mRPMMeterSize), Color::sYellow); - } - - // Draw current vehicle state - String status = StringFormat("Forward: %.1f, Right: %.1f\nBrake: %.1f, HandBrake: %.1f\n" - "Gear: %d, Clutch: %.1f\nEngineRPM: %.0f, V: %.1f km/h", - (double)mForwardInput, (double)mRightInput, (double)mBrakeInput, (double)mHandBrakeInput, - mTransmission.GetCurrentGear(), (double)mTransmission.GetClutchFriction(), (double)mEngine.GetCurrentRPM(), (double)body->GetLinearVelocity().Length() * 3.6); - inRenderer->DrawText3D(body->GetPosition(), status, Color::sWhite, constraint_size); - - RMat44 body_transform = body->GetWorldTransform(); - - for (const Wheel *w_base : mConstraint.GetWheels()) - { - const WheelWV *w = static_cast(w_base); - const WheelSettings *settings = w->GetSettings(); - - // Calculate where the suspension attaches to the body in world space - RVec3 ws_position = body_transform * settings->mPosition; - Vec3 ws_direction = body_transform.Multiply3x3(settings->mSuspensionDirection); - - // Draw suspension - RVec3 min_suspension_pos = ws_position + ws_direction * settings->mSuspensionMinLength; - RVec3 max_suspension_pos = ws_position + ws_direction * settings->mSuspensionMaxLength; - inRenderer->DrawLine(ws_position, min_suspension_pos, Color::sRed); - inRenderer->DrawLine(min_suspension_pos, max_suspension_pos, Color::sGreen); - - // Draw current length - RVec3 wheel_pos = ws_position + ws_direction * w->GetSuspensionLength(); - inRenderer->DrawMarker(wheel_pos, w->GetSuspensionLength() < settings->mSuspensionMinLength? Color::sRed : Color::sGreen, constraint_size); - - // Draw wheel basis - Vec3 wheel_forward, wheel_up, wheel_right; - mConstraint.GetWheelLocalBasis(w, wheel_forward, wheel_up, wheel_right); - wheel_forward = body_transform.Multiply3x3(wheel_forward); - wheel_up = body_transform.Multiply3x3(wheel_up); - wheel_right = body_transform.Multiply3x3(wheel_right); - Vec3 steering_axis = body_transform.Multiply3x3(settings->mSteeringAxis); - inRenderer->DrawLine(wheel_pos, wheel_pos + wheel_forward, Color::sRed); - inRenderer->DrawLine(wheel_pos, wheel_pos + wheel_up, Color::sGreen); - inRenderer->DrawLine(wheel_pos, wheel_pos + wheel_right, Color::sBlue); - inRenderer->DrawLine(wheel_pos, wheel_pos + steering_axis, Color::sYellow); - - // Draw wheel - RMat44 wheel_transform(Vec4(wheel_up, 0.0f), Vec4(wheel_right, 0.0f), Vec4(wheel_forward, 0.0f), wheel_pos); - wheel_transform.SetRotation(wheel_transform.GetRotation() * Mat44::sRotationY(-w->GetRotationAngle())); - inRenderer->DrawCylinder(wheel_transform, settings->mWidth * 0.5f, settings->mRadius, w->GetSuspensionLength() <= settings->mSuspensionMinLength? Color::sRed : Color::sGreen, DebugRenderer::ECastShadow::Off, DebugRenderer::EDrawMode::Wireframe); - - if (w->HasContact()) - { - // Draw contact - inRenderer->DrawLine(w->GetContactPosition(), w->GetContactPosition() + w->GetContactNormal(), Color::sYellow); - inRenderer->DrawLine(w->GetContactPosition(), w->GetContactPosition() + w->GetContactLongitudinal(), Color::sRed); - inRenderer->DrawLine(w->GetContactPosition(), w->GetContactPosition() + w->GetContactLateral(), Color::sBlue); - - DebugRenderer::sInstance->DrawText3D(wheel_pos, StringFormat("W: %.1f, S: %.2f\nSlipLateral: %.1f, SlipLong: %.2f\nFrLateral: %.1f, FrLong: %.1f", (double)w->GetAngularVelocity(), (double)w->GetSuspensionLength(), (double)RadiansToDegrees(w->mLateralSlip), (double)w->mLongitudinalSlip, (double)w->mCombinedLateralFriction, (double)w->mCombinedLongitudinalFriction), Color::sWhite, constraint_size); - } - else - { - // Draw 'no hit' - DebugRenderer::sInstance->DrawText3D(wheel_pos, StringFormat("W: %.1f", (double)w->GetAngularVelocity()), Color::sRed, constraint_size); - } - } -} - -#endif // JPH_DEBUG_RENDERER - -void WheeledVehicleController::SaveState(StateRecorder &inStream) const -{ - inStream.Write(mForwardInput); - inStream.Write(mRightInput); - inStream.Write(mBrakeInput); - inStream.Write(mHandBrakeInput); - inStream.Write(mPreviousDeltaTime); - - mEngine.SaveState(inStream); - mTransmission.SaveState(inStream); -} - -void WheeledVehicleController::RestoreState(StateRecorder &inStream) -{ - inStream.Read(mForwardInput); - inStream.Read(mRightInput); - inStream.Read(mBrakeInput); - inStream.Read(mHandBrakeInput); - inStream.Read(mPreviousDeltaTime); - - mEngine.RestoreState(inStream); - mTransmission.RestoreState(inStream); -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/WheeledVehicleController.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/WheeledVehicleController.h deleted file mode 100644 index 3d314e29c9b..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Physics/Vehicle/WheeledVehicleController.h +++ /dev/null @@ -1,199 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -class PhysicsSystem; - -/// WheelSettings object specifically for WheeledVehicleController -class JPH_EXPORT WheelSettingsWV : public WheelSettings -{ -public: - JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, WheelSettingsWV) - - /// Constructor - WheelSettingsWV(); - - // See: WheelSettings - virtual void SaveBinaryState(StreamOut &inStream) const override; - virtual void RestoreBinaryState(StreamIn &inStream) override; - - float mInertia = 0.9f; ///< Moment of inertia (kg m^2), for a cylinder this would be 0.5 * M * R^2 which is 0.9 for a wheel with a mass of 20 kg and radius 0.3 m - float mAngularDamping = 0.2f; ///< Angular damping factor of the wheel: dw/dt = -c * w - float mMaxSteerAngle = DegreesToRadians(70.0f); ///< How much this wheel can steer (radians) - LinearCurve mLongitudinalFriction; ///< On the Y-axis: friction in the forward direction of the tire. Friction is normally between 0 (no friction) and 1 (full friction) although friction can be a little bit higher than 1 because of the profile of a tire. On the X-axis: the slip ratio (fraction) defined as (omega_wheel * r_wheel - v_longitudinal) / |v_longitudinal|. You can see slip ratio as the amount the wheel is spinning relative to the floor: 0 means the wheel has full traction and is rolling perfectly in sync with the ground, 1 is for example when the wheel is locked and sliding over the ground. - LinearCurve mLateralFriction; ///< On the Y-axis: friction in the sideways direction of the tire. Friction is normally between 0 (no friction) and 1 (full friction) although friction can be a little bit higher than 1 because of the profile of a tire. On the X-axis: the slip angle (degrees) defined as angle between relative contact velocity and tire direction. - float mMaxBrakeTorque = 1500.0f; ///< How much torque (Nm) the brakes can apply to this wheel - float mMaxHandBrakeTorque = 4000.0f; ///< How much torque (Nm) the hand brake can apply to this wheel (usually only applied to the rear wheels) -}; - -/// Wheel object specifically for WheeledVehicleController -class JPH_EXPORT WheelWV : public Wheel -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Constructor - explicit WheelWV(const WheelSettingsWV &inWheel); - - /// Override GetSettings and cast to the correct class - const WheelSettingsWV * GetSettings() const { return static_cast(mSettings.GetPtr()); } - - /// Apply a torque (N m) to the wheel for a particular delta time - void ApplyTorque(float inTorque, float inDeltaTime) - { - mAngularVelocity += inTorque * inDeltaTime / GetSettings()->mInertia; - } - - /// Update the wheel rotation based on the current angular velocity - void Update(uint inWheelIndex, float inDeltaTime, const VehicleConstraint &inConstraint); - - float mLongitudinalSlip = 0.0f; ///< Velocity difference between ground and wheel relative to ground velocity - float mLateralSlip = 0.0f; ///< Angular difference (in radians) between ground and wheel relative to ground velocity - float mCombinedLongitudinalFriction = 0.0f; ///< Combined friction coefficient in longitudinal direction (combines terrain and tires) - float mCombinedLateralFriction = 0.0f; ///< Combined friction coefficient in lateral direction (combines terrain and tires) - float mBrakeImpulse = 0.0f; ///< Amount of impulse that the brakes can apply to the floor (excluding friction) -}; - -/// Settings of a vehicle with regular wheels -/// -/// The properties in this controller are largely based on "Car Physics for Games" by Marco Monster. -/// See: https://www.asawicki.info/Mirror/Car%20Physics%20for%20Games/Car%20Physics%20for%20Games.html -class JPH_EXPORT WheeledVehicleControllerSettings : public VehicleControllerSettings -{ -public: - JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, WheeledVehicleControllerSettings) - - // See: VehicleControllerSettings - virtual VehicleController * ConstructController(VehicleConstraint &inConstraint) const override; - virtual void SaveBinaryState(StreamOut &inStream) const override; - virtual void RestoreBinaryState(StreamIn &inStream) override; - - VehicleEngineSettings mEngine; ///< The properties of the engine - VehicleTransmissionSettings mTransmission; ///< The properties of the transmission (aka gear box) - Array mDifferentials; ///< List of differentials and their properties - float mDifferentialLimitedSlipRatio = 1.4f; ///< Ratio max / min average wheel speed of each differential (measured at the clutch). When the ratio is exceeded all torque gets distributed to the differential with the minimal average velocity. This allows implementing a limited slip differential between differentials. Set to FLT_MAX for an open differential. Value should be > 1. -}; - -/// Runtime controller class -class JPH_EXPORT WheeledVehicleController : public VehicleController -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Constructor - WheeledVehicleController(const WheeledVehicleControllerSettings &inSettings, VehicleConstraint &inConstraint); - - /// Typedefs - using Differentials = Array; - - /// Set input from driver - /// @param inForward Value between -1 and 1 for auto transmission and value between 0 and 1 indicating desired driving direction and amount the gas pedal is pressed - /// @param inRight Value between -1 and 1 indicating desired steering angle (1 = right) - /// @param inBrake Value between 0 and 1 indicating how strong the brake pedal is pressed - /// @param inHandBrake Value between 0 and 1 indicating how strong the hand brake is pulled - void SetDriverInput(float inForward, float inRight, float inBrake, float inHandBrake) { mForwardInput = inForward; mRightInput = inRight; mBrakeInput = inBrake; mHandBrakeInput = inHandBrake; } - - /// Value between -1 and 1 for auto transmission and value between 0 and 1 indicating desired driving direction and amount the gas pedal is pressed - void SetForwardInput(float inForward) { mForwardInput = inForward; } - float GetForwardInput() const { return mForwardInput; } - - /// Value between -1 and 1 indicating desired steering angle (1 = right) - void SetRightInput(float inRight) { mRightInput = inRight; } - float GetRightInput() const { return mRightInput; } - - /// Value between 0 and 1 indicating how strong the brake pedal is pressed - void SetBrakeInput(float inBrake) { mBrakeInput = inBrake; } - float GetBrakeInput() const { return mBrakeInput; } - - /// Value between 0 and 1 indicating how strong the hand brake is pulled - void SetHandBrakeInput(float inHandBrake) { mHandBrakeInput = inHandBrake; } - float GetHandBrakeInput() const { return mHandBrakeInput; } - - /// Get current engine state - const VehicleEngine & GetEngine() const { return mEngine; } - - /// Get current engine state (writable interface, allows you to make changes to the configuration which will take effect the next time step) - VehicleEngine & GetEngine() { return mEngine; } - - /// Get current transmission state - const VehicleTransmission & GetTransmission() const { return mTransmission; } - - /// Get current transmission state (writable interface, allows you to make changes to the configuration which will take effect the next time step) - VehicleTransmission & GetTransmission() { return mTransmission; } - - /// Get the differentials this vehicle has - const Differentials & GetDifferentials() const { return mDifferentials; } - - /// Get the differentials this vehicle has (writable interface, allows you to make changes to the configuration which will take effect the next time step) - Differentials & GetDifferentials() { return mDifferentials; } - - /// Ratio max / min average wheel speed of each differential (measured at the clutch). - float GetDifferentialLimitedSlipRatio() const { return mDifferentialLimitedSlipRatio; } - void SetDifferentialLimitedSlipRatio(float inV) { mDifferentialLimitedSlipRatio = inV; } - - /// Get the average wheel speed of all driven wheels (measured at the clutch) - float GetWheelSpeedAtClutch() const; - - /// Calculate max tire impulses by combining friction, slip, and suspension impulse. Note that the actual applied impulse may be lower (e.g. when the vehicle is stationary on a horizontal surface the actual impulse applied will be 0). - using TireMaxImpulseCallback = function; - const TireMaxImpulseCallback&GetTireMaxImpulseCallback() const { return mTireMaxImpulseCallback; } - void SetTireMaxImpulseCallback(const TireMaxImpulseCallback &inTireMaxImpulseCallback) { mTireMaxImpulseCallback = inTireMaxImpulseCallback; } - -#ifdef JPH_DEBUG_RENDERER - /// Debug drawing of RPM meter - void SetRPMMeter(Vec3Arg inPosition, float inSize) { mRPMMeterPosition = inPosition; mRPMMeterSize = inSize; } -#endif // JPH_DEBUG_RENDERER - -protected: - // See: VehicleController - virtual Wheel * ConstructWheel(const WheelSettings &inWheel) const override { JPH_ASSERT(IsKindOf(&inWheel, JPH_RTTI(WheelSettingsWV))); return new WheelWV(static_cast(inWheel)); } - virtual bool AllowSleep() const override; - virtual void PreCollide(float inDeltaTime, PhysicsSystem &inPhysicsSystem) override; - virtual void PostCollide(float inDeltaTime, PhysicsSystem &inPhysicsSystem) override; - virtual bool SolveLongitudinalAndLateralConstraints(float inDeltaTime) override; - virtual void SaveState(StateRecorder &inStream) const override; - virtual void RestoreState(StateRecorder &inStream) override; -#ifdef JPH_DEBUG_RENDERER - virtual void Draw(DebugRenderer *inRenderer) const override; -#endif // JPH_DEBUG_RENDERER - - // Control information - float mForwardInput = 0.0f; ///< Value between -1 and 1 for auto transmission and value between 0 and 1 indicating desired driving direction and amount the gas pedal is pressed - float mRightInput = 0.0f; ///< Value between -1 and 1 indicating desired steering angle - float mBrakeInput = 0.0f; ///< Value between 0 and 1 indicating how strong the brake pedal is pressed - float mHandBrakeInput = 0.0f; ///< Value between 0 and 1 indicating how strong the hand brake is pulled - - // Simulation information - VehicleEngine mEngine; ///< Engine state of the vehicle - VehicleTransmission mTransmission; ///< Transmission state of the vehicle - Differentials mDifferentials; ///< Differential states of the vehicle - float mDifferentialLimitedSlipRatio; ///< Ratio max / min average wheel speed of each differential (measured at the clutch). - float mPreviousDeltaTime = 0.0f; ///< Delta time of the last step - - // Callback that calculates the max impulse that the tire can apply to the ground - TireMaxImpulseCallback mTireMaxImpulseCallback = - [](uint, float &outLongitudinalImpulse, float &outLateralImpulse, float inSuspensionImpulse, float inLongitudinalFriction, float inLateralFriction, float, float, float) - { - outLongitudinalImpulse = inLongitudinalFriction * inSuspensionImpulse; - outLateralImpulse = inLateralFriction * inSuspensionImpulse; - }; - -#ifdef JPH_DEBUG_RENDERER - // Debug settings - Vec3 mRPMMeterPosition { 0, 1, 0 }; ///< Position (in local space of the body) of the RPM meter when drawing the constraint - float mRPMMeterSize = 0.5f; ///< Size of the RPM meter when drawing the constraint -#endif // JPH_DEBUG_RENDERER -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/RegisterTypes.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/RegisterTypes.cpp deleted file mode 100644 index d9f9b896931..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/RegisterTypes.cpp +++ /dev/null @@ -1,197 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, Skeleton) -JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, SkeletalAnimation) -JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, CompoundShapeSettings) -JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, StaticCompoundShapeSettings) -JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, MutableCompoundShapeSettings) -JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, TriangleShapeSettings) -JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, SphereShapeSettings) -JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, BoxShapeSettings) -JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, CapsuleShapeSettings) -JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, TaperedCapsuleShapeSettings) -JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, CylinderShapeSettings) -JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, ScaledShapeSettings) -JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, MeshShapeSettings) -JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, ConvexHullShapeSettings) -JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, HeightFieldShapeSettings) -JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, RotatedTranslatedShapeSettings) -JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, OffsetCenterOfMassShapeSettings) -JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, RagdollSettings) -JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, PointConstraintSettings) -JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, SixDOFConstraintSettings) -JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, SliderConstraintSettings) -JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, SwingTwistConstraintSettings) -JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, DistanceConstraintSettings) -JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, HingeConstraintSettings) -JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, FixedConstraintSettings) -JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, ConeConstraintSettings) -JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, PathConstraintSettings) -JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, PathConstraintPath) -JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, PathConstraintPathHermite) -JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, VehicleConstraintSettings) -JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, WheeledVehicleControllerSettings) -JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, RackAndPinionConstraintSettings) -JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, GearConstraintSettings) -JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, PulleyConstraintSettings) -JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, MotorSettings) -JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, PhysicsScene) -JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, PhysicsMaterial) -JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, GroupFilter) -JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, GroupFilterTable) -JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, BodyCreationSettings) -JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(JPH_EXPORT, JPH, SoftBodyCreationSettings) - -JPH_NAMESPACE_BEGIN - -bool VerifyJoltVersionIDInternal(uint64 inVersionID) -{ - return inVersionID == JPH_VERSION_ID; -} - -void RegisterTypesInternal(uint64 inVersionID) -{ - // Version check - if (!VerifyJoltVersionIDInternal(inVersionID)) - { - Trace("Version mismatch, make sure you compile the client code with the same Jolt version and compiler definitions!"); - uint64 mismatch = JPH_VERSION_ID ^ inVersionID; - auto check_bit = [mismatch](int inBit, const char *inLabel) { if (mismatch & (uint64(1) << (inBit + 23))) Trace("Mismatching define %s.", inLabel); }; - check_bit(1, "JPH_DOUBLE_PRECISION"); - check_bit(2, "JPH_CROSS_PLATFORM_DETERMINISTIC"); - check_bit(3, "JPH_FLOATING_POINT_EXCEPTIONS_ENABLED"); - check_bit(4, "JPH_PROFILE_ENABLED"); - check_bit(5, "JPH_EXTERNAL_PROFILE"); - check_bit(6, "JPH_DEBUG_RENDERER"); - check_bit(7, "JPH_DISABLE_TEMP_ALLOCATOR"); - check_bit(8, "JPH_DISABLE_CUSTOM_ALLOCATOR"); - check_bit(9, "JPH_OBJECT_LAYER_BITS"); - check_bit(10, "JPH_ENABLE_ASSERTS"); - std::abort(); - } - -#ifndef JPH_DISABLE_CUSTOM_ALLOCATOR - JPH_ASSERT(Allocate != nullptr && Free != nullptr && AlignedAllocate != nullptr && AlignedFree != nullptr, "Need to supply an allocator first or call RegisterDefaultAllocator()"); -#endif // !JPH_DISABLE_CUSTOM_ALLOCATOR - - JPH_ASSERT(Factory::sInstance != nullptr, "Need to create a factory first!"); - - // Initialize dispatcher - CollisionDispatch::sInit(); - - // Register base classes first so that we can specialize them later - CompoundShape::sRegister(); - ConvexShape::sRegister(); - - // Register compounds before others so that we can specialize them later (register them in reverse order of collision complexity) - MutableCompoundShape::sRegister(); - StaticCompoundShape::sRegister(); - - // Leaf classes - TriangleShape::sRegister(); - SphereShape::sRegister(); - BoxShape::sRegister(); - CapsuleShape::sRegister(); - TaperedCapsuleShape::sRegister(); - CylinderShape::sRegister(); - MeshShape::sRegister(); - ConvexHullShape::sRegister(); - HeightFieldShape::sRegister(); - SoftBodyShape::sRegister(); - - // Register these last because their collision functions are simple so we want to execute them first (register them in reverse order of collision complexity) - RotatedTranslatedShape::sRegister(); - OffsetCenterOfMassShape::sRegister(); - ScaledShape::sRegister(); - - // Create list of all types - const RTTI *types[] = { - JPH_RTTI(SkeletalAnimation), - JPH_RTTI(Skeleton), - JPH_RTTI(CompoundShapeSettings), - JPH_RTTI(StaticCompoundShapeSettings), - JPH_RTTI(MutableCompoundShapeSettings), - JPH_RTTI(TriangleShapeSettings), - JPH_RTTI(SphereShapeSettings), - JPH_RTTI(BoxShapeSettings), - JPH_RTTI(CapsuleShapeSettings), - JPH_RTTI(TaperedCapsuleShapeSettings), - JPH_RTTI(CylinderShapeSettings), - JPH_RTTI(ScaledShapeSettings), - JPH_RTTI(MeshShapeSettings), - JPH_RTTI(ConvexHullShapeSettings), - JPH_RTTI(HeightFieldShapeSettings), - JPH_RTTI(RotatedTranslatedShapeSettings), - JPH_RTTI(OffsetCenterOfMassShapeSettings), - JPH_RTTI(RagdollSettings), - JPH_RTTI(PointConstraintSettings), - JPH_RTTI(SixDOFConstraintSettings), - JPH_RTTI(SliderConstraintSettings), - JPH_RTTI(SwingTwistConstraintSettings), - JPH_RTTI(DistanceConstraintSettings), - JPH_RTTI(HingeConstraintSettings), - JPH_RTTI(FixedConstraintSettings), - JPH_RTTI(ConeConstraintSettings), - JPH_RTTI(PathConstraintSettings), - JPH_RTTI(VehicleConstraintSettings), - JPH_RTTI(WheeledVehicleControllerSettings), - JPH_RTTI(PathConstraintPath), - JPH_RTTI(PathConstraintPathHermite), - JPH_RTTI(RackAndPinionConstraintSettings), - JPH_RTTI(GearConstraintSettings), - JPH_RTTI(PulleyConstraintSettings), - JPH_RTTI(MotorSettings), - JPH_RTTI(PhysicsScene), - JPH_RTTI(PhysicsMaterial), - JPH_RTTI(PhysicsMaterialSimple), - JPH_RTTI(GroupFilter), - JPH_RTTI(GroupFilterTable), - JPH_RTTI(BodyCreationSettings), - JPH_RTTI(SoftBodyCreationSettings) - }; - - // Register them all - Factory::sInstance->Register(types, (uint)size(types)); - - // Initialize default physics material - if (PhysicsMaterial::sDefault == nullptr) - PhysicsMaterial::sDefault = new PhysicsMaterialSimple("Default", Color::sGrey); -} - -void UnregisterTypes() -{ - // Unregister all types - if (Factory::sInstance != nullptr) - Factory::sInstance->Clear(); - - // Delete default physics material - PhysicsMaterial::sDefault = nullptr; -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/RegisterTypes.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/RegisterTypes.h deleted file mode 100644 index 372ef7f4f40..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/RegisterTypes.h +++ /dev/null @@ -1,29 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -JPH_NAMESPACE_BEGIN - -/// Internal helper function -JPH_EXPORT extern bool VerifyJoltVersionIDInternal(uint64 inVersionID); - -/// This function can be used to verify the library ABI is compatible with your -/// application. -/// Use it in this way: `assert(VerifyJoltVersionID());`. -/// Returns `false` if the library used is not compatible with your app. -JPH_INLINE bool VerifyJoltVersionID() { return VerifyJoltVersionIDInternal(JPH_VERSION_ID); } - -/// Internal helper function -JPH_EXPORT extern void RegisterTypesInternal(uint64 inVersionID); - -/// Register all physics types with the factory and install their collision handlers with the CollisionDispatch class. -/// If you have your own custom shape types you probably need to register their handlers with the CollisionDispatch before calling this function. -/// If you implement your own default material (PhysicsMaterial::sDefault) make sure to initialize it before this function or else this function will create one for you. -JPH_INLINE void RegisterTypes() { RegisterTypesInternal(JPH_VERSION_ID); } - -/// Unregisters all types with the factory and cleans up the default material -JPH_EXPORT extern void UnregisterTypes(); - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Renderer/DebugRenderer.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Renderer/DebugRenderer.cpp deleted file mode 100644 index 44025aaff9f..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Renderer/DebugRenderer.cpp +++ /dev/null @@ -1,1071 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#ifdef JPH_DEBUG_RENDERER - -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -DebugRenderer *DebugRenderer::sInstance = nullptr; - -// Number of LOD levels to create -static const int sMaxLevel = 4; - -// Distance for each LOD level, these are tweaked for an object of approx. size 1. Use the lod scale to scale these distances. -static const float sLODDistanceForLevel[] = { 5.0f, 10.0f, 40.0f, FLT_MAX }; - -DebugRenderer::Triangle::Triangle(Vec3Arg inV1, Vec3Arg inV2, Vec3Arg inV3, ColorArg inColor) -{ - // Set position - inV1.StoreFloat3(&mV[0].mPosition); - inV2.StoreFloat3(&mV[1].mPosition); - inV3.StoreFloat3(&mV[2].mPosition); - - // Set color - mV[0].mColor = mV[1].mColor = mV[2].mColor = inColor; - - // Calculate normal - Vec3 normal = (inV2 - inV1).Cross(inV3 - inV1); - float normal_len = normal.Length(); - if (normal_len > 0.0f) - normal /= normal_len; - Float3 normal3; - normal.StoreFloat3(&normal3); - mV[0].mNormal = mV[1].mNormal = mV[2].mNormal = normal3; - - // Reset UV's - mV[0].mUV = mV[1].mUV = mV[2].mUV = { 0, 0 }; -} - -DebugRenderer::Triangle::Triangle(Vec3Arg inV1, Vec3Arg inV2, Vec3Arg inV3, ColorArg inColor, Vec3Arg inUVOrigin, Vec3Arg inUVDirection) -{ - // Set position - inV1.StoreFloat3(&mV[0].mPosition); - inV2.StoreFloat3(&mV[1].mPosition); - inV3.StoreFloat3(&mV[2].mPosition); - - // Set color - mV[0].mColor = mV[1].mColor = mV[2].mColor = inColor; - - // Calculate normal - Vec3 normal = (inV2 - inV1).Cross(inV3 - inV1).Normalized(); - Float3 normal3; - normal.StoreFloat3(&normal3); - mV[0].mNormal = mV[1].mNormal = mV[2].mNormal = normal3; - - // Set UV's - Vec3 uv1 = inV1 - inUVOrigin; - Vec3 uv2 = inV2 - inUVOrigin; - Vec3 uv3 = inV3 - inUVOrigin; - Vec3 axis2 = normal.Cross(inUVDirection); - mV[0].mUV = { inUVDirection.Dot(uv1), axis2.Dot(uv1) }; - mV[1].mUV = { inUVDirection.Dot(uv2), axis2.Dot(uv2) }; - mV[2].mUV = { inUVDirection.Dot(uv3), axis2.Dot(uv3) }; -} - -DebugRenderer::DebugRenderer() -{ - // Store singleton - JPH_ASSERT(sInstance == nullptr); - sInstance = this; -} - -DebugRenderer::~DebugRenderer() -{ - JPH_ASSERT(sInstance == this); - sInstance = nullptr; -} - -void DebugRenderer::DrawWireBox(const AABox &inBox, ColorArg inColor) -{ - JPH_PROFILE_FUNCTION(); - - // 8 vertices - RVec3 v1(Real(inBox.mMin.GetX()), Real(inBox.mMin.GetY()), Real(inBox.mMin.GetZ())); - RVec3 v2(Real(inBox.mMin.GetX()), Real(inBox.mMin.GetY()), Real(inBox.mMax.GetZ())); - RVec3 v3(Real(inBox.mMin.GetX()), Real(inBox.mMax.GetY()), Real(inBox.mMin.GetZ())); - RVec3 v4(Real(inBox.mMin.GetX()), Real(inBox.mMax.GetY()), Real(inBox.mMax.GetZ())); - RVec3 v5(Real(inBox.mMax.GetX()), Real(inBox.mMin.GetY()), Real(inBox.mMin.GetZ())); - RVec3 v6(Real(inBox.mMax.GetX()), Real(inBox.mMin.GetY()), Real(inBox.mMax.GetZ())); - RVec3 v7(Real(inBox.mMax.GetX()), Real(inBox.mMax.GetY()), Real(inBox.mMin.GetZ())); - RVec3 v8(Real(inBox.mMax.GetX()), Real(inBox.mMax.GetY()), Real(inBox.mMax.GetZ())); - - // 12 edges - DrawLine(v1, v2, inColor); - DrawLine(v1, v3, inColor); - DrawLine(v1, v5, inColor); - DrawLine(v2, v4, inColor); - DrawLine(v2, v6, inColor); - DrawLine(v3, v4, inColor); - DrawLine(v3, v7, inColor); - DrawLine(v4, v8, inColor); - DrawLine(v5, v6, inColor); - DrawLine(v5, v7, inColor); - DrawLine(v6, v8, inColor); - DrawLine(v7, v8, inColor); -} - -void DebugRenderer::DrawWireBox(const OrientedBox &inBox, ColorArg inColor) -{ - JPH_PROFILE_FUNCTION(); - - // 8 vertices - RVec3 v1(inBox.mOrientation * Vec3(-inBox.mHalfExtents.GetX(), -inBox.mHalfExtents.GetY(), -inBox.mHalfExtents.GetZ())); - RVec3 v2(inBox.mOrientation * Vec3(-inBox.mHalfExtents.GetX(), -inBox.mHalfExtents.GetY(), inBox.mHalfExtents.GetZ())); - RVec3 v3(inBox.mOrientation * Vec3(-inBox.mHalfExtents.GetX(), inBox.mHalfExtents.GetY(), -inBox.mHalfExtents.GetZ())); - RVec3 v4(inBox.mOrientation * Vec3(-inBox.mHalfExtents.GetX(), inBox.mHalfExtents.GetY(), inBox.mHalfExtents.GetZ())); - RVec3 v5(inBox.mOrientation * Vec3(inBox.mHalfExtents.GetX(), -inBox.mHalfExtents.GetY(), -inBox.mHalfExtents.GetZ())); - RVec3 v6(inBox.mOrientation * Vec3(inBox.mHalfExtents.GetX(), -inBox.mHalfExtents.GetY(), inBox.mHalfExtents.GetZ())); - RVec3 v7(inBox.mOrientation * Vec3(inBox.mHalfExtents.GetX(), inBox.mHalfExtents.GetY(), -inBox.mHalfExtents.GetZ())); - RVec3 v8(inBox.mOrientation * Vec3(inBox.mHalfExtents.GetX(), inBox.mHalfExtents.GetY(), inBox.mHalfExtents.GetZ())); - - // 12 edges - DrawLine(v1, v2, inColor); - DrawLine(v1, v3, inColor); - DrawLine(v1, v5, inColor); - DrawLine(v2, v4, inColor); - DrawLine(v2, v6, inColor); - DrawLine(v3, v4, inColor); - DrawLine(v3, v7, inColor); - DrawLine(v4, v8, inColor); - DrawLine(v5, v6, inColor); - DrawLine(v5, v7, inColor); - DrawLine(v6, v8, inColor); - DrawLine(v7, v8, inColor); -} - -void DebugRenderer::DrawWireBox(RMat44Arg inMatrix, const AABox &inBox, ColorArg inColor) -{ - JPH_PROFILE_FUNCTION(); - - // 8 vertices - RVec3 v1 = inMatrix * Vec3(inBox.mMin.GetX(), inBox.mMin.GetY(), inBox.mMin.GetZ()); - RVec3 v2 = inMatrix * Vec3(inBox.mMin.GetX(), inBox.mMin.GetY(), inBox.mMax.GetZ()); - RVec3 v3 = inMatrix * Vec3(inBox.mMin.GetX(), inBox.mMax.GetY(), inBox.mMin.GetZ()); - RVec3 v4 = inMatrix * Vec3(inBox.mMin.GetX(), inBox.mMax.GetY(), inBox.mMax.GetZ()); - RVec3 v5 = inMatrix * Vec3(inBox.mMax.GetX(), inBox.mMin.GetY(), inBox.mMin.GetZ()); - RVec3 v6 = inMatrix * Vec3(inBox.mMax.GetX(), inBox.mMin.GetY(), inBox.mMax.GetZ()); - RVec3 v7 = inMatrix * Vec3(inBox.mMax.GetX(), inBox.mMax.GetY(), inBox.mMin.GetZ()); - RVec3 v8 = inMatrix * Vec3(inBox.mMax.GetX(), inBox.mMax.GetY(), inBox.mMax.GetZ()); - - // 12 edges - DrawLine(v1, v2, inColor); - DrawLine(v1, v3, inColor); - DrawLine(v1, v5, inColor); - DrawLine(v2, v4, inColor); - DrawLine(v2, v6, inColor); - DrawLine(v3, v4, inColor); - DrawLine(v3, v7, inColor); - DrawLine(v4, v8, inColor); - DrawLine(v5, v6, inColor); - DrawLine(v5, v7, inColor); - DrawLine(v6, v8, inColor); - DrawLine(v7, v8, inColor); -} - -void DebugRenderer::DrawMarker(RVec3Arg inPosition, ColorArg inColor, float inSize) -{ - JPH_PROFILE_FUNCTION(); - - Vec3 dx(inSize, 0, 0); - Vec3 dy(0, inSize, 0); - Vec3 dz(0, 0, inSize); - DrawLine(inPosition - dy, inPosition + dy, inColor); - DrawLine(inPosition - dx, inPosition + dx, inColor); - DrawLine(inPosition - dz, inPosition + dz, inColor); -} - -void DebugRenderer::DrawArrow(RVec3Arg inFrom, RVec3Arg inTo, ColorArg inColor, float inSize) -{ - JPH_PROFILE_FUNCTION(); - - // Draw base line - DrawLine(inFrom, inTo, inColor); - - if (inSize > 0.0f) - { - // Draw arrow head - Vec3 dir = Vec3(inTo - inFrom); - float len = dir.Length(); - if (len != 0.0f) - dir = dir * (inSize / len); - else - dir = Vec3(inSize, 0, 0); - Vec3 perp = inSize * dir.GetNormalizedPerpendicular(); - DrawLine(inTo - dir + perp, inTo, inColor); - DrawLine(inTo - dir - perp, inTo, inColor); - } -} - -void DebugRenderer::DrawCoordinateSystem(RMat44Arg inTransform, float inSize) -{ - JPH_PROFILE_FUNCTION(); - - DrawArrow(inTransform.GetTranslation(), inTransform * Vec3(inSize, 0, 0), Color::sRed, 0.1f * inSize); - DrawArrow(inTransform.GetTranslation(), inTransform * Vec3(0, inSize, 0), Color::sGreen, 0.1f * inSize); - DrawArrow(inTransform.GetTranslation(), inTransform * Vec3(0, 0, inSize), Color::sBlue, 0.1f * inSize); -} - -void DebugRenderer::DrawPlane(RVec3Arg inPoint, Vec3Arg inNormal, ColorArg inColor, float inSize) -{ - // Create orthogonal basis - Vec3 perp1 = inNormal.Cross(Vec3::sAxisY()).NormalizedOr(Vec3::sAxisX()); - Vec3 perp2 = perp1.Cross(inNormal).Normalized(); - perp1 = inNormal.Cross(perp2); - - // Calculate corners - RVec3 corner1 = inPoint + inSize * (perp1 + perp2); - RVec3 corner2 = inPoint + inSize * (perp1 - perp2); - RVec3 corner3 = inPoint + inSize * (-perp1 - perp2); - RVec3 corner4 = inPoint + inSize * (-perp1 + perp2); - - // Draw cross - DrawLine(corner1, corner3, inColor); - DrawLine(corner2, corner4, inColor); - - // Draw square - DrawLine(corner1, corner2, inColor); - DrawLine(corner2, corner3, inColor); - DrawLine(corner3, corner4, inColor); - DrawLine(corner4, corner1, inColor); - - // Draw normal - DrawArrow(inPoint, inPoint + inSize * inNormal, inColor, 0.1f * inSize); -} - -void DebugRenderer::DrawWireTriangle(RVec3Arg inV1, RVec3Arg inV2, RVec3Arg inV3, ColorArg inColor) -{ - JPH_PROFILE_FUNCTION(); - - DrawLine(inV1, inV2, inColor); - DrawLine(inV2, inV3, inColor); - DrawLine(inV3, inV1, inColor); -} - -void DebugRenderer::DrawWireSphere(RVec3Arg inCenter, float inRadius, ColorArg inColor, int inLevel) -{ - RMat44 matrix = RMat44::sTranslation(inCenter) * Mat44::sScale(inRadius); - - DrawWireUnitSphere(matrix, inColor, inLevel); -} - -void DebugRenderer::DrawWireUnitSphere(RMat44Arg inMatrix, ColorArg inColor, int inLevel) -{ - JPH_PROFILE_FUNCTION(); - - DrawWireUnitSphereRecursive(inMatrix, inColor, Vec3::sAxisX(), Vec3::sAxisY(), Vec3::sAxisZ(), inLevel); - DrawWireUnitSphereRecursive(inMatrix, inColor, -Vec3::sAxisX(), Vec3::sAxisY(), Vec3::sAxisZ(), inLevel); - DrawWireUnitSphereRecursive(inMatrix, inColor, Vec3::sAxisX(), -Vec3::sAxisY(), Vec3::sAxisZ(), inLevel); - DrawWireUnitSphereRecursive(inMatrix, inColor, -Vec3::sAxisX(), -Vec3::sAxisY(), Vec3::sAxisZ(), inLevel); - DrawWireUnitSphereRecursive(inMatrix, inColor, Vec3::sAxisX(), Vec3::sAxisY(), -Vec3::sAxisZ(), inLevel); - DrawWireUnitSphereRecursive(inMatrix, inColor, -Vec3::sAxisX(), Vec3::sAxisY(), -Vec3::sAxisZ(), inLevel); - DrawWireUnitSphereRecursive(inMatrix, inColor, Vec3::sAxisX(), -Vec3::sAxisY(), -Vec3::sAxisZ(), inLevel); - DrawWireUnitSphereRecursive(inMatrix, inColor, -Vec3::sAxisX(), -Vec3::sAxisY(), -Vec3::sAxisZ(), inLevel); -} - -void DebugRenderer::DrawWireUnitSphereRecursive(RMat44Arg inMatrix, ColorArg inColor, Vec3Arg inDir1, Vec3Arg inDir2, Vec3Arg inDir3, int inLevel) -{ - if (inLevel == 0) - { - RVec3 d1 = inMatrix * inDir1; - RVec3 d2 = inMatrix * inDir2; - RVec3 d3 = inMatrix * inDir3; - - DrawLine(d1, d2, inColor); - DrawLine(d2, d3, inColor); - DrawLine(d3, d1, inColor); - } - else - { - Vec3 center1 = (inDir1 + inDir2).Normalized(); - Vec3 center2 = (inDir2 + inDir3).Normalized(); - Vec3 center3 = (inDir3 + inDir1).Normalized(); - - DrawWireUnitSphereRecursive(inMatrix, inColor, inDir1, center1, center3, inLevel - 1); - DrawWireUnitSphereRecursive(inMatrix, inColor, center1, center2, center3, inLevel - 1); - DrawWireUnitSphereRecursive(inMatrix, inColor, center1, inDir2, center2, inLevel - 1); - DrawWireUnitSphereRecursive(inMatrix, inColor, center3, center2, inDir3, inLevel - 1); - } -} - -void DebugRenderer::Create8thSphereRecursive(Array &ioIndices, Array &ioVertices, Vec3Arg inDir1, uint32 &ioIdx1, Vec3Arg inDir2, uint32 &ioIdx2, Vec3Arg inDir3, uint32 &ioIdx3, const Float2 &inUV, SupportFunction inGetSupport, int inLevel) -{ - if (inLevel == 0) - { - if (ioIdx1 == 0xffffffff) - { - ioIdx1 = (uint32)ioVertices.size(); - Float3 position, normal; - inGetSupport(inDir1).StoreFloat3(&position); - inDir1.StoreFloat3(&normal); - ioVertices.push_back({ position, normal, inUV, Color::sWhite }); - } - - if (ioIdx2 == 0xffffffff) - { - ioIdx2 = (uint32)ioVertices.size(); - Float3 position, normal; - inGetSupport(inDir2).StoreFloat3(&position); - inDir2.StoreFloat3(&normal); - ioVertices.push_back({ position, normal, inUV, Color::sWhite }); - } - - if (ioIdx3 == 0xffffffff) - { - ioIdx3 = (uint32)ioVertices.size(); - Float3 position, normal; - inGetSupport(inDir3).StoreFloat3(&position); - inDir3.StoreFloat3(&normal); - ioVertices.push_back({ position, normal, inUV, Color::sWhite }); - } - - ioIndices.push_back(ioIdx1); - ioIndices.push_back(ioIdx2); - ioIndices.push_back(ioIdx3); - } - else - { - Vec3 center1 = (inDir1 + inDir2).Normalized(); - Vec3 center2 = (inDir2 + inDir3).Normalized(); - Vec3 center3 = (inDir3 + inDir1).Normalized(); - - uint32 idx1 = 0xffffffff; - uint32 idx2 = 0xffffffff; - uint32 idx3 = 0xffffffff; - - Create8thSphereRecursive(ioIndices, ioVertices, inDir1, ioIdx1, center1, idx1, center3, idx3, inUV, inGetSupport, inLevel - 1); - Create8thSphereRecursive(ioIndices, ioVertices, center1, idx1, center2, idx2, center3, idx3, inUV, inGetSupport, inLevel - 1); - Create8thSphereRecursive(ioIndices, ioVertices, center1, idx1, inDir2, ioIdx2, center2, idx2, inUV, inGetSupport, inLevel - 1); - Create8thSphereRecursive(ioIndices, ioVertices, center3, idx3, center2, idx2, inDir3, ioIdx3, inUV, inGetSupport, inLevel - 1); - } -} - -void DebugRenderer::Create8thSphere(Array &ioIndices, Array &ioVertices, Vec3Arg inDir1, Vec3Arg inDir2, Vec3Arg inDir3, const Float2 &inUV, SupportFunction inGetSupport, int inLevel) -{ - uint32 idx1 = 0xffffffff; - uint32 idx2 = 0xffffffff; - uint32 idx3 = 0xffffffff; - - Create8thSphereRecursive(ioIndices, ioVertices, inDir1, idx1, inDir2, idx2, inDir3, idx3, inUV, inGetSupport, inLevel); -} - -void DebugRenderer::CreateQuad(Array &ioIndices, Array &ioVertices, Vec3Arg inV1, Vec3Arg inV2, Vec3Arg inV3, Vec3Arg inV4) -{ - // Make room - uint32 start_idx = uint32(ioVertices.size()); - ioVertices.resize(start_idx + 4); - Vertex *vertices = &ioVertices[start_idx]; - - // Set position - inV1.StoreFloat3(&vertices[0].mPosition); - inV2.StoreFloat3(&vertices[1].mPosition); - inV3.StoreFloat3(&vertices[2].mPosition); - inV4.StoreFloat3(&vertices[3].mPosition); - - // Set color - vertices[0].mColor = vertices[1].mColor = vertices[2].mColor = vertices[3].mColor = Color::sWhite; - - // Calculate normal - Vec3 normal = (inV2 - inV1).Cross(inV3 - inV1).Normalized(); - Float3 normal3; - normal.StoreFloat3(&normal3); - vertices[0].mNormal = vertices[1].mNormal = vertices[2].mNormal = vertices[3].mNormal = normal3; - - // Set UV's - vertices[0].mUV = { 0, 0 }; - vertices[1].mUV = { 2, 0 }; - vertices[2].mUV = { 2, 2 }; - vertices[3].mUV = { 0, 2 }; - - // Set indices - ioIndices.push_back(start_idx); - ioIndices.push_back(start_idx + 1); - ioIndices.push_back(start_idx + 2); - - ioIndices.push_back(start_idx); - ioIndices.push_back(start_idx + 2); - ioIndices.push_back(start_idx + 3); -} - -void DebugRenderer::Initialize() -{ - // Box - { - Array box_vertices; - Array box_indices; - - // Get corner points - Vec3 v0 = Vec3(-1, 1, -1); - Vec3 v1 = Vec3( 1, 1, -1); - Vec3 v2 = Vec3( 1, 1, 1); - Vec3 v3 = Vec3(-1, 1, 1); - Vec3 v4 = Vec3(-1, -1, -1); - Vec3 v5 = Vec3( 1, -1, -1); - Vec3 v6 = Vec3( 1, -1, 1); - Vec3 v7 = Vec3(-1, -1, 1); - - // Top - CreateQuad(box_indices, box_vertices, v0, v3, v2, v1); - - // Bottom - CreateQuad(box_indices, box_vertices, v4, v5, v6, v7); - - // Left - CreateQuad(box_indices, box_vertices, v0, v4, v7, v3); - - // Right - CreateQuad(box_indices, box_vertices, v2, v6, v5, v1); - - // Front - CreateQuad(box_indices, box_vertices, v3, v7, v6, v2); - - // Back - CreateQuad(box_indices, box_vertices, v0, v1, v5, v4); - - mBox = new Geometry(CreateTriangleBatch(box_vertices, box_indices), AABox(Vec3(-1, -1, -1), Vec3(1, 1, 1))); - } - - // Support function that returns a unit sphere - auto sphere_support = [](Vec3Arg inDirection) { return inDirection; }; - - // Construct geometries - mSphere = new Geometry(AABox(Vec3(-1, -1, -1), Vec3(1, 1, 1))); - mCapsuleBottom = new Geometry(AABox(Vec3(-1, -1, -1), Vec3(1, 0, 1))); - mCapsuleTop = new Geometry(AABox(Vec3(-1, 0, -1), Vec3(1, 1, 1))); - mCapsuleMid = new Geometry(AABox(Vec3(-1, -1, -1), Vec3(1, 1, 1))); - mOpenCone = new Geometry(AABox(Vec3(-1, 0, -1), Vec3(1, 1, 1))); - mCylinder = new Geometry(AABox(Vec3(-1, -1, -1), Vec3(1, 1, 1))); - - // Iterate over levels - for (int level = sMaxLevel; level >= 1; --level) - { - // Determine at which distance this level should be active - float distance = sLODDistanceForLevel[sMaxLevel - level]; - - // Sphere - mSphere->mLODs.push_back({ CreateTriangleBatchForConvex(sphere_support, level), distance }); - - // Capsule bottom half sphere - { - Array capsule_bottom_vertices; - Array capsule_bottom_indices; - Create8thSphere(capsule_bottom_indices, capsule_bottom_vertices, -Vec3::sAxisX(), -Vec3::sAxisY(), Vec3::sAxisZ(), Float2(0.25f, 0.25f), sphere_support, level); - Create8thSphere(capsule_bottom_indices, capsule_bottom_vertices, -Vec3::sAxisY(), Vec3::sAxisX(), Vec3::sAxisZ(), Float2(0.25f, 0.75f), sphere_support, level); - Create8thSphere(capsule_bottom_indices, capsule_bottom_vertices, Vec3::sAxisX(), -Vec3::sAxisY(), -Vec3::sAxisZ(), Float2(0.25f, 0.25f), sphere_support, level); - Create8thSphere(capsule_bottom_indices, capsule_bottom_vertices, -Vec3::sAxisY(), -Vec3::sAxisX(), -Vec3::sAxisZ(), Float2(0.25f, 0.75f), sphere_support, level); - mCapsuleBottom->mLODs.push_back({ CreateTriangleBatch(capsule_bottom_vertices, capsule_bottom_indices), distance }); - } - - // Capsule top half sphere - { - Array capsule_top_vertices; - Array capsule_top_indices; - Create8thSphere(capsule_top_indices, capsule_top_vertices, Vec3::sAxisX(), Vec3::sAxisY(), Vec3::sAxisZ(), Float2(0.25f, 0.75f), sphere_support, level); - Create8thSphere(capsule_top_indices, capsule_top_vertices, Vec3::sAxisY(), -Vec3::sAxisX(), Vec3::sAxisZ(), Float2(0.25f, 0.25f), sphere_support, level); - Create8thSphere(capsule_top_indices, capsule_top_vertices, Vec3::sAxisY(), Vec3::sAxisX(), -Vec3::sAxisZ(), Float2(0.25f, 0.25f), sphere_support, level); - Create8thSphere(capsule_top_indices, capsule_top_vertices, -Vec3::sAxisX(), Vec3::sAxisY(), -Vec3::sAxisZ(), Float2(0.25f, 0.75f), sphere_support, level); - mCapsuleTop->mLODs.push_back({ CreateTriangleBatch(capsule_top_vertices, capsule_top_indices), distance }); - } - - // Capsule middle part - { - Array capsule_mid_vertices; - Array capsule_mid_indices; - for (int q = 0; q < 4; ++q) - { - Float2 uv = (q & 1) == 0? Float2(0.25f, 0.25f) : Float2(0.25f, 0.75f); - - uint32 start_idx = (uint32)capsule_mid_vertices.size(); - - int num_parts = 1 << level; - for (int i = 0; i <= num_parts; ++i) - { - float angle = 0.5f * JPH_PI * (float(q) + float(i) / num_parts); - float s = Sin(angle); - float c = Cos(angle); - Float3 vt(s, 1.0f, c); - Float3 vb(s, -1.0f, c); - Float3 n(s, 0, c); - - capsule_mid_vertices.push_back({ vt, n, uv, Color::sWhite }); - capsule_mid_vertices.push_back({ vb, n, uv, Color::sWhite }); - } - - for (int i = 0; i < num_parts; ++i) - { - uint32 start = start_idx + 2 * i; - - capsule_mid_indices.push_back(start); - capsule_mid_indices.push_back(start + 1); - capsule_mid_indices.push_back(start + 3); - - capsule_mid_indices.push_back(start); - capsule_mid_indices.push_back(start + 3); - capsule_mid_indices.push_back(start + 2); - } - } - mCapsuleMid->mLODs.push_back({ CreateTriangleBatch(capsule_mid_vertices, capsule_mid_indices), distance }); - } - - // Open cone - { - Array open_cone_vertices; - Array open_cone_indices; - for (int q = 0; q < 4; ++q) - { - Float2 uv = (q & 1) == 0? Float2(0.25f, 0.25f) : Float2(0.25f, 0.75f); - - uint32 start_idx = (uint32)open_cone_vertices.size(); - - int num_parts = 2 << level; - Float3 vt(0, 0, 0); - for (int i = 0; i <= num_parts; ++i) - { - // Calculate bottom vertex - float angle = 0.5f * JPH_PI * (float(q) + float(i) / num_parts); - float s = Sin(angle); - float c = Cos(angle); - Float3 vb(s, 1.0f, c); - - // Calculate normal - // perpendicular = Y cross vb (perpendicular to the plane in which 0, y and vb exists) - // normal = perpendicular cross vb (normal to the edge 0 vb) - Vec3 normal = Vec3(s, -Square(s) - Square(c), c).Normalized(); - Float3 n; normal.StoreFloat3(&n); - - open_cone_vertices.push_back({ vt, n, uv, Color::sWhite }); - open_cone_vertices.push_back({ vb, n, uv, Color::sWhite }); - } - - for (int i = 0; i < num_parts; ++i) - { - uint32 start = start_idx + 2 * i; - - open_cone_indices.push_back(start); - open_cone_indices.push_back(start + 1); - open_cone_indices.push_back(start + 3); - } - } - mOpenCone->mLODs.push_back({ CreateTriangleBatch(open_cone_vertices, open_cone_indices), distance }); - } - - // Cylinder - { - Array cylinder_vertices; - Array cylinder_indices; - for (int q = 0; q < 4; ++q) - { - Float2 uv = (q & 1) == 0? Float2(0.25f, 0.75f) : Float2(0.25f, 0.25f); - - uint32 center_start_idx = (uint32)cylinder_vertices.size(); - - Float3 nt(0.0f, 1.0f, 0.0f); - Float3 nb(0.0f, -1.0f, 0.0f); - cylinder_vertices.push_back({ Float3(0.0f, 1.0f, 0.0f), nt, uv, Color::sWhite }); - cylinder_vertices.push_back({ Float3(0.0f, -1.0f, 0.0f), nb, uv, Color::sWhite }); - - uint32 vtx_start_idx = (uint32)cylinder_vertices.size(); - - int num_parts = 1 << level; - for (int i = 0; i <= num_parts; ++i) - { - float angle = 0.5f * JPH_PI * (float(q) + float(i) / num_parts); - float s = Sin(angle); - float c = Cos(angle); - Float3 vt(s, 1.0f, c); - Float3 vb(s, -1.0f, c); - Float3 n(s, 0, c); - - cylinder_vertices.push_back({ vt, nt, uv, Color::sWhite }); - cylinder_vertices.push_back({ vb, nb, uv, Color::sWhite }); - cylinder_vertices.push_back({ vt, n, uv, Color::sWhite }); - cylinder_vertices.push_back({ vb, n, uv, Color::sWhite }); - } - - for (int i = 0; i < num_parts; ++i) - { - uint32 start = vtx_start_idx + 4 * i; - - // Top - cylinder_indices.push_back(center_start_idx); - cylinder_indices.push_back(start); - cylinder_indices.push_back(start + 4); - - // Bottom - cylinder_indices.push_back(center_start_idx + 1); - cylinder_indices.push_back(start + 5); - cylinder_indices.push_back(start + 1); - - // Side - cylinder_indices.push_back(start + 2); - cylinder_indices.push_back(start + 3); - cylinder_indices.push_back(start + 7); - - cylinder_indices.push_back(start + 2); - cylinder_indices.push_back(start + 7); - cylinder_indices.push_back(start + 6); - } - } - mCylinder->mLODs.push_back({ CreateTriangleBatch(cylinder_vertices, cylinder_indices), distance }); - } - } -} - -AABox DebugRenderer::sCalculateBounds(const Vertex *inVertices, int inVertexCount) -{ - AABox bounds; - for (const Vertex *v = inVertices, *v_end = inVertices + inVertexCount; v < v_end; ++v) - bounds.Encapsulate(Vec3(v->mPosition)); - return bounds; -} - -DebugRenderer::Batch DebugRenderer::CreateTriangleBatch(const VertexList &inVertices, const IndexedTriangleNoMaterialList &inTriangles) -{ - JPH_PROFILE_FUNCTION(); - - Array vertices; - - // Create render vertices - vertices.resize(inVertices.size()); - for (size_t v = 0; v < inVertices.size(); ++v) - { - vertices[v].mPosition = inVertices[v]; - vertices[v].mNormal = Float3(0, 0, 0); - vertices[v].mUV = Float2(0, 0); - vertices[v].mColor = Color::sWhite; - } - - // Calculate normals - for (size_t i = 0; i < inTriangles.size(); ++i) - { - const IndexedTriangleNoMaterial &tri = inTriangles[i]; - - // Calculate normal of face - Vec3 vtx[3]; - for (int j = 0; j < 3; ++j) - vtx[j] = Vec3::sLoadFloat3Unsafe(vertices[tri.mIdx[j]].mPosition); - Vec3 normal = ((vtx[1] - vtx[0]).Cross(vtx[2] - vtx[0])).Normalized(); - - // Add normal to all vertices in face - for (int j = 0; j < 3; ++j) - (Vec3::sLoadFloat3Unsafe(vertices[tri.mIdx[j]].mNormal) + normal).StoreFloat3(&vertices[tri.mIdx[j]].mNormal); - } - - // Renormalize vertex normals - for (size_t i = 0; i < vertices.size(); ++i) - Vec3::sLoadFloat3Unsafe(vertices[i].mNormal).Normalized().StoreFloat3(&vertices[i].mNormal); - - return CreateTriangleBatch(&vertices[0], (int)vertices.size(), &inTriangles[0].mIdx[0], (int)(3 * inTriangles.size())); -} - -DebugRenderer::Batch DebugRenderer::CreateTriangleBatchForConvex(SupportFunction inGetSupport, int inLevel, AABox *outBounds) -{ - JPH_PROFILE_FUNCTION(); - - Array vertices; - Array indices; - Create8thSphere(indices, vertices, Vec3::sAxisX(), Vec3::sAxisY(), Vec3::sAxisZ(), Float2(0.25f, 0.25f), inGetSupport, inLevel); - Create8thSphere(indices, vertices, Vec3::sAxisY(), -Vec3::sAxisX(), Vec3::sAxisZ(), Float2(0.25f, 0.75f), inGetSupport, inLevel); - Create8thSphere(indices, vertices, -Vec3::sAxisY(), Vec3::sAxisX(), Vec3::sAxisZ(), Float2(0.25f, 0.75f), inGetSupport, inLevel); - Create8thSphere(indices, vertices, -Vec3::sAxisX(), -Vec3::sAxisY(), Vec3::sAxisZ(), Float2(0.25f, 0.25f), inGetSupport, inLevel); - Create8thSphere(indices, vertices, Vec3::sAxisY(), Vec3::sAxisX(), -Vec3::sAxisZ(), Float2(0.25f, 0.75f), inGetSupport, inLevel); - Create8thSphere(indices, vertices, -Vec3::sAxisX(), Vec3::sAxisY(), -Vec3::sAxisZ(), Float2(0.25f, 0.25f), inGetSupport, inLevel); - Create8thSphere(indices, vertices, Vec3::sAxisX(), -Vec3::sAxisY(), -Vec3::sAxisZ(), Float2(0.25f, 0.25f), inGetSupport, inLevel); - Create8thSphere(indices, vertices, -Vec3::sAxisY(), -Vec3::sAxisX(), -Vec3::sAxisZ(), Float2(0.25f, 0.75f), inGetSupport, inLevel); - - if (outBounds != nullptr) - *outBounds = sCalculateBounds(&vertices[0], (int)vertices.size()); - - return CreateTriangleBatch(vertices, indices); -} - -DebugRenderer::GeometryRef DebugRenderer::CreateTriangleGeometryForConvex(SupportFunction inGetSupport) -{ - GeometryRef geometry; - - // Iterate over levels - for (int level = sMaxLevel; level >= 1; --level) - { - // Determine at which distance this level should be active - float distance = sLODDistanceForLevel[sMaxLevel - level]; - - // Create triangle batch and only calculate bounds for highest LOD level - AABox bounds; - Batch batch = CreateTriangleBatchForConvex(inGetSupport, level, geometry == nullptr? &bounds : nullptr); - - // Construct geometry in the first iteration - if (geometry == nullptr) - geometry = new Geometry(bounds); - - // Add the LOD - geometry->mLODs.push_back({ batch, distance }); - } - - return geometry; -} - -void DebugRenderer::DrawBox(const AABox &inBox, ColorArg inColor, ECastShadow inCastShadow, EDrawMode inDrawMode) -{ - JPH_PROFILE_FUNCTION(); - - RMat44 m = RMat44::sScale(inBox.GetExtent()); - m.SetTranslation(RVec3(inBox.GetCenter())); - DrawGeometry(m, inColor, mBox, ECullMode::CullBackFace, inCastShadow, inDrawMode); -} - -void DebugRenderer::DrawBox(RMat44Arg inMatrix, const AABox &inBox, ColorArg inColor, ECastShadow inCastShadow, EDrawMode inDrawMode) -{ - JPH_PROFILE_FUNCTION(); - - Mat44 m = Mat44::sScale(inBox.GetExtent()); - m.SetTranslation(inBox.GetCenter()); - DrawGeometry(inMatrix * m, inColor, mBox, ECullMode::CullBackFace, inCastShadow, inDrawMode); -} - -void DebugRenderer::DrawSphere(RVec3Arg inCenter, float inRadius, ColorArg inColor, ECastShadow inCastShadow, EDrawMode inDrawMode) -{ - JPH_PROFILE_FUNCTION(); - - RMat44 matrix = RMat44::sTranslation(inCenter) * Mat44::sScale(inRadius); - - DrawUnitSphere(matrix, inColor, inCastShadow, inDrawMode); -} - -void DebugRenderer::DrawUnitSphere(RMat44Arg inMatrix, ColorArg inColor, ECastShadow inCastShadow, EDrawMode inDrawMode) -{ - JPH_PROFILE_FUNCTION(); - - DrawGeometry(inMatrix, inColor, mSphere, ECullMode::CullBackFace, inCastShadow, inDrawMode); -} - -void DebugRenderer::DrawCapsule(RMat44Arg inMatrix, float inHalfHeightOfCylinder, float inRadius, ColorArg inColor, ECastShadow inCastShadow, EDrawMode inDrawMode) -{ - JPH_PROFILE_FUNCTION(); - - Mat44 scale_matrix = Mat44::sScale(inRadius); - - // Calculate world space bounding box - AABox local_bounds(Vec3(-inRadius, -inHalfHeightOfCylinder - inRadius, -inRadius), Vec3(inRadius, inHalfHeightOfCylinder + inRadius, inRadius)); - AABox world_bounds = local_bounds.Transformed(inMatrix); - - float radius_sq = Square(inRadius); - - // Draw bottom half sphere - RMat44 bottom_matrix = inMatrix * Mat44::sTranslation(Vec3(0, -inHalfHeightOfCylinder, 0)) * scale_matrix; - DrawGeometry(bottom_matrix, world_bounds, radius_sq, inColor, mCapsuleBottom, ECullMode::CullBackFace, inCastShadow, inDrawMode); - - // Draw top half sphere - RMat44 top_matrix = inMatrix * Mat44::sTranslation(Vec3(0, inHalfHeightOfCylinder, 0)) * scale_matrix; - DrawGeometry(top_matrix, world_bounds, radius_sq, inColor, mCapsuleTop, ECullMode::CullBackFace, inCastShadow, inDrawMode); - - // Draw middle part - DrawGeometry(inMatrix * Mat44::sScale(Vec3(inRadius, inHalfHeightOfCylinder, inRadius)), world_bounds, radius_sq, inColor, mCapsuleMid, ECullMode::CullBackFace, inCastShadow, inDrawMode); -} - -void DebugRenderer::DrawCylinder(RMat44Arg inMatrix, float inHalfHeight, float inRadius, ColorArg inColor, ECastShadow inCastShadow, EDrawMode inDrawMode) -{ - JPH_PROFILE_FUNCTION(); - - Mat44 local_transform(Vec4(inRadius, 0, 0, 0), Vec4(0, inHalfHeight, 0, 0), Vec4(0, 0, inRadius, 0), Vec4(0, 0, 0, 1)); - RMat44 transform = inMatrix * local_transform; - - DrawGeometry(transform, mCylinder->mBounds.Transformed(transform), Square(inRadius), inColor, mCylinder, ECullMode::CullBackFace, inCastShadow, inDrawMode); -} - -void DebugRenderer::DrawOpenCone(RVec3Arg inTop, Vec3Arg inAxis, Vec3Arg inPerpendicular, float inHalfAngle, float inLength, ColorArg inColor, ECastShadow inCastShadow, EDrawMode inDrawMode) -{ - JPH_PROFILE_FUNCTION(); - - JPH_ASSERT(inAxis.IsNormalized(1.0e-4f)); - JPH_ASSERT(inPerpendicular.IsNormalized(1.0e-4f)); - JPH_ASSERT(abs(inPerpendicular.Dot(inAxis)) < 1.0e-4f); - - Vec3 axis = Sign(inHalfAngle) * inLength * inAxis; - float scale = inLength * Tan(abs(inHalfAngle)); - if (scale != 0.0f) - { - Vec3 perp1 = scale * inPerpendicular; - Vec3 perp2 = scale * inAxis.Cross(inPerpendicular); - RMat44 transform(Vec4(perp1, 0), Vec4(axis, 0), Vec4(perp2, 0), inTop); - DrawGeometry(transform, inColor, mOpenCone, ECullMode::Off, inCastShadow, inDrawMode); - } -} - -DebugRenderer::Geometry *DebugRenderer::CreateSwingLimitGeometry(int inNumSegments, const Vec3 *inVertices) -{ - // Allocate space for vertices - int num_vertices = 2 * inNumSegments; - Vertex *vertices_start = (Vertex *)JPH_STACK_ALLOC(num_vertices * sizeof(Vertex)); - Vertex *vertices = vertices_start; - - for (int i = 0; i < inNumSegments; ++i) - { - // Get output vertices - Vertex &top = *(vertices++); - Vertex &bottom = *(vertices++); - - // Get local position - const Vec3 &pos = inVertices[i]; - - // Get local normal - const Vec3 &prev_pos = inVertices[(i + inNumSegments - 1) % inNumSegments]; - const Vec3 &next_pos = inVertices[(i + 1) % inNumSegments]; - Vec3 normal = 0.5f * (next_pos.Cross(pos).NormalizedOr(Vec3::sZero()) + pos.Cross(prev_pos).NormalizedOr(Vec3::sZero())); - - // Store top vertex - top.mPosition = { 0, 0, 0 }; - normal.StoreFloat3(&top.mNormal); - top.mColor = Color::sWhite; - top.mUV = { 0, 0 }; - - // Store bottom vertex - pos.StoreFloat3(&bottom.mPosition); - normal.StoreFloat3(&bottom.mNormal); - bottom.mColor = Color::sWhite; - bottom.mUV = { 0, 0 }; - } - - // Allocate space for indices - int num_indices = 3 * inNumSegments; - uint32 *indices_start = (uint32 *)JPH_STACK_ALLOC(num_indices * sizeof(uint32)); - uint32 *indices = indices_start; - - // Calculate indices - for (int i = 0; i < inNumSegments; ++i) - { - int first = 2 * i; - int second = (first + 3) % num_vertices; - int third = first + 1; - - // Triangle - *indices++ = first; - *indices++ = second; - *indices++ = third; - } - - // Convert to triangle batch - return new Geometry(CreateTriangleBatch(vertices_start, num_vertices, indices_start, num_indices), sCalculateBounds(vertices_start, num_vertices)); -} - -void DebugRenderer::DrawSwingConeLimits(RMat44Arg inMatrix, float inSwingYHalfAngle, float inSwingZHalfAngle, float inEdgeLength, ColorArg inColor, ECastShadow inCastShadow, EDrawMode inDrawMode) -{ - JPH_PROFILE_FUNCTION(); - - // Assert sane input - JPH_ASSERT(inSwingYHalfAngle >= 0.0f && inSwingYHalfAngle <= JPH_PI); - JPH_ASSERT(inSwingZHalfAngle >= 0.0f && inSwingZHalfAngle <= JPH_PI); - JPH_ASSERT(inEdgeLength > 0.0f); - - // Check cache - SwingConeLimits limits { inSwingYHalfAngle, inSwingZHalfAngle }; - GeometryRef &geometry = mSwingConeLimits[limits]; - if (geometry == nullptr) - { - SwingConeBatches::iterator it = mPrevSwingConeLimits.find(limits); - if (it != mPrevSwingConeLimits.end()) - geometry = it->second; - } - if (geometry == nullptr) - { - // Number of segments to draw the cone with - const int num_segments = 64; - int half_num_segments = num_segments / 2; - - // The y and z values of the quaternion are limited to an ellipse, e1 and e2 are the radii of this ellipse - float e1 = Sin(0.5f * inSwingZHalfAngle); - float e2 = Sin(0.5f * inSwingYHalfAngle); - - // Check if the limits will draw something - if ((e1 <= 0.0f && e2 <= 0.0f) || (e2 >= 1.0f && e1 >= 1.0f)) - return; - - // Calculate squared values - float e1_sq = Square(e1); - float e2_sq = Square(e2); - - // Calculate local space vertices for shape - Vec3 ls_vertices[num_segments]; - int tgt_vertex = 0; - for (int side_iter = 0; side_iter < 2; ++side_iter) - for (int segment_iter = 0; segment_iter < half_num_segments; ++segment_iter) - { - float y, z; - if (e2_sq > e1_sq) - { - // Trace the y value of the quaternion - y = e2 - 2.0f * segment_iter * e2 / half_num_segments; - - // Calculate the corresponding z value of the quaternion - float z_sq = e1_sq - e1_sq / e2_sq * Square(y); - z = z_sq <= 0.0f? 0.0f : sqrt(z_sq); - } - else - { - // Trace the z value of the quaternion - z = -e1 + 2.0f * segment_iter * e1 / half_num_segments; - - // Calculate the corresponding y value of the quaternion - float y_sq = e2_sq - e2_sq / e1_sq * Square(z); - y = y_sq <= 0.0f? 0.0f : sqrt(y_sq); - } - - // If we're tracing the opposite side, flip the values - if (side_iter == 1) - { - z = -z; - y = -y; - } - - // Create quaternion - Vec3 q_xyz(0, y, z); - float w = sqrt(1.0f - q_xyz.LengthSq()); - Quat q(Vec4(q_xyz, w)); - - // Store vertex - ls_vertices[tgt_vertex++] = q.RotateAxisX(); - } - - geometry = CreateSwingLimitGeometry(num_segments, ls_vertices); - } - - DrawGeometry(inMatrix * Mat44::sScale(inEdgeLength), inColor, geometry, ECullMode::Off, inCastShadow, inDrawMode); -} - -void DebugRenderer::DrawSwingPyramidLimits(RMat44Arg inMatrix, float inMinSwingYAngle, float inMaxSwingYAngle, float inMinSwingZAngle, float inMaxSwingZAngle, float inEdgeLength, ColorArg inColor, ECastShadow inCastShadow, EDrawMode inDrawMode) -{ - JPH_PROFILE_FUNCTION(); - - // Assert sane input - JPH_ASSERT(inMinSwingYAngle <= inMaxSwingYAngle && inMinSwingZAngle <= inMaxSwingZAngle); - JPH_ASSERT(inEdgeLength > 0.0f); - - // Check cache - SwingPyramidLimits limits { inMinSwingYAngle, inMaxSwingYAngle, inMinSwingZAngle, inMaxSwingZAngle }; - GeometryRef &geometry = mSwingPyramidLimits[limits]; - if (geometry == nullptr) - { - SwingPyramidBatches::iterator it = mPrevSwingPyramidLimits.find(limits); - if (it != mPrevSwingPyramidLimits.end()) - geometry = it->second; - } - if (geometry == nullptr) - { - // Number of segments to draw the cone with - const int num_segments = 64; - int quarter_num_segments = num_segments / 4; - - // Note that this is q = Quat::sRotation(Vec3::sAxisZ(), z) * Quat::sRotation(Vec3::sAxisY(), y) with q.x set to zero so we don't introduce twist - // This matches the calculation in SwingTwistConstraintPart::ClampSwingTwist - auto get_axis = [](float inY, float inZ) { - float hy = 0.5f * inY; - float hz = 0.5f * inZ; - float cos_hy = Cos(hy); - float cos_hz = Cos(hz); - return Quat(0, Sin(hy) * cos_hz, cos_hy * Sin(hz), cos_hy * cos_hz).Normalized().RotateAxisX(); - }; - - // Calculate local space vertices for shape - Vec3 ls_vertices[num_segments]; - int tgt_vertex = 0; - for (int segment_iter = 0; segment_iter < quarter_num_segments; ++segment_iter) - ls_vertices[tgt_vertex++] = get_axis(inMinSwingYAngle, inMaxSwingZAngle - (inMaxSwingZAngle - inMinSwingZAngle) * segment_iter / quarter_num_segments); - for (int segment_iter = 0; segment_iter < quarter_num_segments; ++segment_iter) - ls_vertices[tgt_vertex++] = get_axis(inMinSwingYAngle + (inMaxSwingYAngle - inMinSwingYAngle) * segment_iter / quarter_num_segments, inMinSwingZAngle); - for (int segment_iter = 0; segment_iter < quarter_num_segments; ++segment_iter) - ls_vertices[tgt_vertex++] = get_axis(inMaxSwingYAngle, inMinSwingZAngle + (inMaxSwingZAngle - inMinSwingZAngle) * segment_iter / quarter_num_segments); - for (int segment_iter = 0; segment_iter < quarter_num_segments; ++segment_iter) - ls_vertices[tgt_vertex++] = get_axis(inMaxSwingYAngle - (inMaxSwingYAngle - inMinSwingYAngle) * segment_iter / quarter_num_segments, inMaxSwingZAngle); - - geometry = CreateSwingLimitGeometry(num_segments, ls_vertices); - } - - DrawGeometry(inMatrix * Mat44::sScale(inEdgeLength), inColor, geometry, ECullMode::Off, inCastShadow, inDrawMode); -} - -void DebugRenderer::DrawPie(RVec3Arg inCenter, float inRadius, Vec3Arg inNormal, Vec3Arg inAxis, float inMinAngle, float inMaxAngle, ColorArg inColor, ECastShadow inCastShadow, EDrawMode inDrawMode) -{ - if (inMinAngle >= inMaxAngle) - return; - - JPH_PROFILE_FUNCTION(); - - JPH_ASSERT(inAxis.IsNormalized(1.0e-4f)); - JPH_ASSERT(inNormal.IsNormalized(1.0e-4f)); - JPH_ASSERT(abs(inNormal.Dot(inAxis)) < 1.0e-4f); - - // Pies have a unique batch based on the difference between min and max angle - float delta_angle = inMaxAngle - inMinAngle; - GeometryRef &geometry = mPieLimits[delta_angle]; - if (geometry == nullptr) - { - PieBatces::iterator it = mPrevPieLimits.find(delta_angle); - if (it != mPrevPieLimits.end()) - geometry = it->second; - } - if (geometry == nullptr) - { - int num_parts = (int)ceil(64.0f * delta_angle / (2.0f * JPH_PI)); - - Float3 normal = { 0, 1, 0 }; - Float3 center = { 0, 0, 0 }; - - // Allocate space for vertices - int num_vertices = num_parts + 2; - Vertex *vertices_start = (Vertex *)JPH_STACK_ALLOC(num_vertices * sizeof(Vertex)); - Vertex *vertices = vertices_start; - - // Center of circle - *vertices++ = { center, normal, { 0, 0 }, Color::sWhite }; - - // Outer edge of pie - for (int i = 0; i <= num_parts; ++i) - { - float angle = float(i) / float(num_parts) * delta_angle; - - Float3 pos = { Cos(angle), 0, Sin(angle) }; - *vertices++ = { pos, normal, { 0, 0 }, Color::sWhite }; - } - - // Allocate space for indices - int num_indices = num_parts * 3; - uint32 *indices_start = (uint32 *)JPH_STACK_ALLOC(num_indices * sizeof(uint32)); - uint32 *indices = indices_start; - - for (int i = 0; i < num_parts; ++i) - { - *indices++ = 0; - *indices++ = i + 1; - *indices++ = i + 2; - } - - // Convert to triangle batch - geometry = new Geometry(CreateTriangleBatch(vertices_start, num_vertices, indices_start, num_indices), sCalculateBounds(vertices_start, num_vertices)); - } - - // Construct matrix that transforms pie into world space - RMat44 matrix = RMat44(Vec4(inRadius * inAxis, 0), Vec4(inRadius * inNormal, 0), Vec4(inRadius * inNormal.Cross(inAxis), 0), inCenter) * Mat44::sRotationY(-inMinAngle); - - DrawGeometry(matrix, inColor, geometry, ECullMode::Off, inCastShadow, inDrawMode); -} - -void DebugRenderer::NextFrame() -{ - mPrevSwingConeLimits.clear(); - std::swap(mSwingConeLimits, mPrevSwingConeLimits); - - mPrevSwingPyramidLimits.clear(); - std::swap(mSwingPyramidLimits, mPrevSwingPyramidLimits); - - mPrevPieLimits.clear(); - std::swap(mPieLimits, mPrevPieLimits); -} - -JPH_NAMESPACE_END - -#endif // JPH_DEBUG_RENDERER diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Renderer/DebugRenderer.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Renderer/DebugRenderer.h deleted file mode 100644 index dd3e7bc8203..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Renderer/DebugRenderer.h +++ /dev/null @@ -1,347 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#ifndef JPH_DEBUG_RENDERER - #error This file should only be included when JPH_DEBUG_RENDERER is defined -#endif // !JPH_DEBUG_RENDERER - -#ifndef JPH_DEBUG_RENDERER_EXPORT - // By default export the debug renderer - #define JPH_DEBUG_RENDERER_EXPORT JPH_EXPORT -#endif // !JPH_DEBUG_RENDERER_EXPORT - -#include -#include -#include -#include -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -class OrientedBox; - -/// Simple triangle renderer for debugging purposes. -/// -/// Inherit from this class to provide your own implementation. -/// -/// Implement the following virtual functions: -/// - DrawLine -/// - DrawTriangle -/// - DrawText3D -/// - CreateTriangleBatch -/// - DrawGeometry -/// -/// Make sure you call Initialize() from the constructor of your implementation. -/// -/// The CreateTriangleBatch is used to prepare a batch of triangles to be drawn by a single DrawGeometry call, -/// which means that Jolt can render a complex scene much more efficiently than when each triangle in that scene would have been drawn through DrawTriangle. -/// -/// Note that an implementation that implements CreateTriangleBatch and DrawGeometry is provided by DebugRendererSimple which can be used to start quickly. -class JPH_DEBUG_RENDERER_EXPORT DebugRenderer : public NonCopyable -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Constructor - DebugRenderer(); - virtual ~DebugRenderer(); - - /// Call once after frame is complete. Releases unused dynamically generated geometry assets. - void NextFrame(); - - /// Draw line - virtual void DrawLine(RVec3Arg inFrom, RVec3Arg inTo, ColorArg inColor) = 0; - - /// Draw wireframe box - void DrawWireBox(const AABox &inBox, ColorArg inColor); - void DrawWireBox(const OrientedBox &inBox, ColorArg inColor); - void DrawWireBox(RMat44Arg inMatrix, const AABox &inBox, ColorArg inColor); - - /// Draw a marker on a position - void DrawMarker(RVec3Arg inPosition, ColorArg inColor, float inSize); - - /// Draw an arrow - void DrawArrow(RVec3Arg inFrom, RVec3Arg inTo, ColorArg inColor, float inSize); - - /// Draw coordinate system (3 arrows, x = red, y = green, z = blue) - void DrawCoordinateSystem(RMat44Arg inTransform, float inSize = 1.0f); - - /// Draw a plane through inPoint with normal inNormal - void DrawPlane(RVec3Arg inPoint, Vec3Arg inNormal, ColorArg inColor, float inSize); - - /// Draw wireframe triangle - void DrawWireTriangle(RVec3Arg inV1, RVec3Arg inV2, RVec3Arg inV3, ColorArg inColor); - - /// Draw a wireframe polygon - template - void DrawWirePolygon(RMat44Arg inTransform, const VERTEX_ARRAY &inVertices, ColorArg inColor, float inArrowSize = 0.0f) { for (typename VERTEX_ARRAY::size_type i = 0; i < inVertices.size(); ++i) DrawArrow(inTransform * inVertices[i], inTransform * inVertices[(i + 1) % inVertices.size()], inColor, inArrowSize); } - - /// Draw wireframe sphere - void DrawWireSphere(RVec3Arg inCenter, float inRadius, ColorArg inColor, int inLevel = 3); - void DrawWireUnitSphere(RMat44Arg inMatrix, ColorArg inColor, int inLevel = 3); - - /// Enum that determines if a shadow should be cast or not - enum class ECastShadow - { - On, // This shape should cast a shadow - Off // This shape should not cast a shadow - }; - - /// Determines how triangles are drawn - enum class EDrawMode - { - Solid, ///< Draw as a solid shape - Wireframe, ///< Draw as wireframe - }; - - /// Draw a single back face culled triangle - virtual void DrawTriangle(RVec3Arg inV1, RVec3Arg inV2, RVec3Arg inV3, ColorArg inColor, ECastShadow inCastShadow = ECastShadow::Off) = 0; - - /// Draw a box - void DrawBox(const AABox &inBox, ColorArg inColor, ECastShadow inCastShadow = ECastShadow::On, EDrawMode inDrawMode = EDrawMode::Solid); - void DrawBox(RMat44Arg inMatrix, const AABox &inBox, ColorArg inColor, ECastShadow inCastShadow = ECastShadow::On, EDrawMode inDrawMode = EDrawMode::Solid); - - /// Draw a sphere - void DrawSphere(RVec3Arg inCenter, float inRadius, ColorArg inColor, ECastShadow inCastShadow = ECastShadow::On, EDrawMode inDrawMode = EDrawMode::Solid); - void DrawUnitSphere(RMat44Arg inMatrix, ColorArg inColor, ECastShadow inCastShadow = ECastShadow::On, EDrawMode inDrawMode = EDrawMode::Solid); - - /// Draw a capsule with one half sphere at (0, -inHalfHeightOfCylinder, 0) and the other half sphere at (0, inHalfHeightOfCylinder, 0) and radius inRadius. - /// The capsule will be transformed by inMatrix. - void DrawCapsule(RMat44Arg inMatrix, float inHalfHeightOfCylinder, float inRadius, ColorArg inColor, ECastShadow inCastShadow = ECastShadow::On, EDrawMode inDrawMode = EDrawMode::Solid); - - /// Draw a cylinder with top (0, inHalfHeight, 0) and bottom (0, -inHalfHeight, 0) and radius inRadius. - /// The cylinder will be transformed by inMatrix - void DrawCylinder(RMat44Arg inMatrix, float inHalfHeight, float inRadius, ColorArg inColor, ECastShadow inCastShadow = ECastShadow::On, EDrawMode inDrawMode = EDrawMode::Solid); - - /// Draw a bottomless cone. - /// @param inTop Top of cone, center of base is at inTop + inAxis. - /// @param inAxis Height and direction of cone - /// @param inPerpendicular Perpendicular vector to inAxis. - /// @param inHalfAngle Specifies the cone angle in radians (angle measured between inAxis and cone surface). - /// @param inLength The length of the cone. - /// @param inColor Color to use for drawing the cone. - /// @param inCastShadow determines if this geometry should cast a shadow or not. - /// @param inDrawMode determines if we draw the geometry solid or in wireframe. - void DrawOpenCone(RVec3Arg inTop, Vec3Arg inAxis, Vec3Arg inPerpendicular, float inHalfAngle, float inLength, ColorArg inColor, ECastShadow inCastShadow = ECastShadow::On, EDrawMode inDrawMode = EDrawMode::Solid); - - /// Draws cone rotation limits as used by the SwingTwistConstraintPart. - /// @param inMatrix Matrix that transforms from constraint space to world space - /// @param inSwingYHalfAngle See SwingTwistConstraintPart - /// @param inSwingZHalfAngle See SwingTwistConstraintPart - /// @param inEdgeLength Size of the edge of the cone shape - /// @param inColor Color to use for drawing the cone. - /// @param inCastShadow determines if this geometry should cast a shadow or not. - /// @param inDrawMode determines if we draw the geometry solid or in wireframe. - void DrawSwingConeLimits(RMat44Arg inMatrix, float inSwingYHalfAngle, float inSwingZHalfAngle, float inEdgeLength, ColorArg inColor, ECastShadow inCastShadow = ECastShadow::On, EDrawMode inDrawMode = EDrawMode::Solid); - - /// Draws rotation limits as used by the SwingTwistConstraintPart. - /// @param inMatrix Matrix that transforms from constraint space to world space - /// @param inMinSwingYAngle See SwingTwistConstraintPart - /// @param inMaxSwingYAngle See SwingTwistConstraintPart - /// @param inMinSwingZAngle See SwingTwistConstraintPart - /// @param inMaxSwingZAngle See SwingTwistConstraintPart - /// @param inEdgeLength Size of the edge of the cone shape - /// @param inColor Color to use for drawing the cone. - /// @param inCastShadow determines if this geometry should cast a shadow or not. - /// @param inDrawMode determines if we draw the geometry solid or in wireframe. - void DrawSwingPyramidLimits(RMat44Arg inMatrix, float inMinSwingYAngle, float inMaxSwingYAngle, float inMinSwingZAngle, float inMaxSwingZAngle, float inEdgeLength, ColorArg inColor, ECastShadow inCastShadow = ECastShadow::On, EDrawMode inDrawMode = EDrawMode::Solid); - - /// Draw a pie (part of a circle). - /// @param inCenter The center of the circle. - /// @param inRadius Radius of the circle. - /// @param inNormal The plane normal in which the pie resides. - /// @param inAxis The axis that defines an angle of 0 radians. - /// @param inMinAngle The pie will be drawn between [inMinAngle, inMaxAngle] (in radians). - /// @param inMaxAngle The pie will be drawn between [inMinAngle, inMaxAngle] (in radians). - /// @param inColor Color to use for drawing the pie. - /// @param inCastShadow determines if this geometry should cast a shadow or not. - /// @param inDrawMode determines if we draw the geometry solid or in wireframe. - void DrawPie(RVec3Arg inCenter, float inRadius, Vec3Arg inNormal, Vec3Arg inAxis, float inMinAngle, float inMaxAngle, ColorArg inColor, ECastShadow inCastShadow = ECastShadow::On, EDrawMode inDrawMode = EDrawMode::Solid); - - /// Singleton instance - static DebugRenderer * sInstance; - - /// Vertex format used by the triangle renderer - class Vertex - { - public: - Float3 mPosition; - Float3 mNormal; - Float2 mUV; - Color mColor; - }; - - /// A single triangle - class JPH_DEBUG_RENDERER_EXPORT Triangle - { - public: - Triangle() = default; - Triangle(Vec3Arg inV1, Vec3Arg inV2, Vec3Arg inV3, ColorArg inColor); - Triangle(Vec3Arg inV1, Vec3Arg inV2, Vec3Arg inV3, ColorArg inColor, Vec3Arg inUVOrigin, Vec3Arg inUVDirection); - - Vertex mV[3]; - }; - - /// Handle for a batch of triangles - using Batch = Ref; - - /// A single level of detail - class LOD - { - public: - Batch mTriangleBatch; - float mDistance; - }; - - /// A geometry primitive containing triangle batches for various lods - class Geometry : public RefTarget - { - public: - JPH_OVERRIDE_NEW_DELETE - - /// Constructor - Geometry(const AABox &inBounds) : mBounds(inBounds) { } - Geometry(const Batch &inBatch, const AABox &inBounds) : mBounds(inBounds) { mLODs.push_back({ inBatch, FLT_MAX }); } - - /// Determine which LOD to render - /// @param inCameraPosition Current position of the camera - /// @param inWorldSpaceBounds World space bounds for this geometry (transform mBounds by model space matrix) - /// @param inLODScaleSq is the squared scale of the model matrix, it is multiplied with the LOD distances in inGeometry to calculate the real LOD distance (so a number > 1 will force a higher LOD). - /// @return The selected LOD. - const LOD & GetLOD(Vec3Arg inCameraPosition, const AABox &inWorldSpaceBounds, float inLODScaleSq) const - { - float dist_sq = inWorldSpaceBounds.GetSqDistanceTo(inCameraPosition); - for (const LOD &lod : mLODs) - if (dist_sq <= inLODScaleSq * Square(lod.mDistance)) - return lod; - - return mLODs.back(); - } - - /// All level of details for this mesh - Array mLODs; - - /// Bounding box that encapsulates all LODs - AABox mBounds; - }; - - /// Handle for a lodded triangle batch - using GeometryRef = Ref; - - /// Calculate bounding box for a batch of triangles - static AABox sCalculateBounds(const Vertex *inVertices, int inVertexCount); - - /// Create a batch of triangles that can be drawn efficiently - virtual Batch CreateTriangleBatch(const Triangle *inTriangles, int inTriangleCount) = 0; - virtual Batch CreateTriangleBatch(const Vertex *inVertices, int inVertexCount, const uint32 *inIndices, int inIndexCount) = 0; - Batch CreateTriangleBatch(const Array &inTriangles) { return CreateTriangleBatch(inTriangles.empty()? nullptr : &inTriangles[0], int(inTriangles.size())); } - Batch CreateTriangleBatch(const Array &inVertices, const Array &inIndices) { return CreateTriangleBatch(inVertices.empty()? nullptr : &inVertices[0], int(inVertices.size()), inIndices.empty()? nullptr : &inIndices[0], int(inIndices.size())); } - Batch CreateTriangleBatch(const VertexList &inVertices, const IndexedTriangleNoMaterialList &inTriangles); - - /// Create a primitive for a convex shape using its support function - using SupportFunction = function; - Batch CreateTriangleBatchForConvex(SupportFunction inGetSupport, int inLevel, AABox *outBounds = nullptr); - GeometryRef CreateTriangleGeometryForConvex(SupportFunction inGetSupport); - - /// Determines which polygons are culled - enum class ECullMode - { - CullBackFace, ///< Don't draw backfacing polygons - CullFrontFace, ///< Don't draw front facing polygons - Off ///< Don't do culling and draw both sides - }; - - /// Draw some geometry - /// @param inModelMatrix is the matrix that transforms the geometry to world space. - /// @param inWorldSpaceBounds is the bounding box of the geometry after transforming it into world space. - /// @param inLODScaleSq is the squared scale of the model matrix, it is multiplied with the LOD distances in inGeometry to calculate the real LOD distance (so a number > 1 will force a higher LOD). - /// @param inModelColor is the color with which to multiply the vertex colors in inGeometry. - /// @param inGeometry The geometry to draw. - /// @param inCullMode determines which polygons are culled. - /// @param inCastShadow determines if this geometry should cast a shadow or not. - /// @param inDrawMode determines if we draw the geometry solid or in wireframe. - virtual void DrawGeometry(RMat44Arg inModelMatrix, const AABox &inWorldSpaceBounds, float inLODScaleSq, ColorArg inModelColor, const GeometryRef &inGeometry, ECullMode inCullMode = ECullMode::CullBackFace, ECastShadow inCastShadow = ECastShadow::On, EDrawMode inDrawMode = EDrawMode::Solid) = 0; - void DrawGeometry(RMat44Arg inModelMatrix, ColorArg inModelColor, const GeometryRef &inGeometry, ECullMode inCullMode = ECullMode::CullBackFace, ECastShadow inCastShadow = ECastShadow::On, EDrawMode inDrawMode = EDrawMode::Solid) { DrawGeometry(inModelMatrix, inGeometry->mBounds.Transformed(inModelMatrix), max(max(inModelMatrix.GetAxisX().LengthSq(), inModelMatrix.GetAxisY().LengthSq()), inModelMatrix.GetAxisZ().LengthSq()), inModelColor, inGeometry, inCullMode, inCastShadow, inDrawMode); } - - /// Draw text - virtual void DrawText3D(RVec3Arg inPosition, const string_view &inString, ColorArg inColor = Color::sWhite, float inHeight = 0.5f) = 0; - -protected: - /// Initialize the system, must be called from the constructor of the DebugRenderer implementation - void Initialize(); - -private: - /// Recursive helper function for DrawWireUnitSphere - void DrawWireUnitSphereRecursive(RMat44Arg inMatrix, ColorArg inColor, Vec3Arg inDir1, Vec3Arg inDir2, Vec3Arg inDir3, int inLevel); - - /// Helper functions to create a box - void CreateQuad(Array &ioIndices, Array &ioVertices, Vec3Arg inV1, Vec3Arg inV2, Vec3Arg inV3, Vec3Arg inV4); - - /// Helper functions to create a vertex and index buffer for a sphere - void Create8thSphereRecursive(Array &ioIndices, Array &ioVertices, Vec3Arg inDir1, uint32 &ioIdx1, Vec3Arg inDir2, uint32 &ioIdx2, Vec3Arg inDir3, uint32 &ioIdx3, const Float2 &inUV, SupportFunction inGetSupport, int inLevel); - void Create8thSphere(Array &ioIndices, Array &ioVertices, Vec3Arg inDir1, Vec3Arg inDir2, Vec3Arg inDir3, const Float2 &inUV, SupportFunction inGetSupport, int inLevel); - - /// Helper function for DrawSwingConeLimits and DrawSwingPyramidLimits - Geometry * CreateSwingLimitGeometry(int inNumSegments, const Vec3 *inVertices); - - // Predefined shapes - GeometryRef mBox; - GeometryRef mSphere; - GeometryRef mCapsuleTop; - GeometryRef mCapsuleMid; - GeometryRef mCapsuleBottom; - GeometryRef mOpenCone; - GeometryRef mCylinder; - - struct SwingConeLimits - { - bool operator == (const SwingConeLimits &inRHS) const - { - return mSwingYHalfAngle == inRHS.mSwingYHalfAngle - && mSwingZHalfAngle == inRHS.mSwingZHalfAngle; - } - - float mSwingYHalfAngle; - float mSwingZHalfAngle; - }; - - JPH_MAKE_HASH_STRUCT(SwingConeLimits, SwingConeLimitsHasher, t.mSwingYHalfAngle, t.mSwingZHalfAngle) - - using SwingConeBatches = UnorderedMap; - SwingConeBatches mSwingConeLimits; - SwingConeBatches mPrevSwingConeLimits; - - struct SwingPyramidLimits - { - bool operator == (const SwingPyramidLimits &inRHS) const - { - return mMinSwingYAngle == inRHS.mMinSwingYAngle - && mMaxSwingYAngle == inRHS.mMaxSwingYAngle - && mMinSwingZAngle == inRHS.mMinSwingZAngle - && mMaxSwingZAngle == inRHS.mMaxSwingZAngle; - } - - float mMinSwingYAngle; - float mMaxSwingYAngle; - float mMinSwingZAngle; - float mMaxSwingZAngle; - }; - - JPH_MAKE_HASH_STRUCT(SwingPyramidLimits, SwingPyramidLimitsHasher, t.mMinSwingYAngle, t.mMaxSwingYAngle, t.mMinSwingZAngle, t.mMaxSwingZAngle) - - using SwingPyramidBatches = UnorderedMap; - SwingPyramidBatches mSwingPyramidLimits; - SwingPyramidBatches mPrevSwingPyramidLimits; - - using PieBatces = UnorderedMap; - PieBatces mPieLimits; - PieBatces mPrevPieLimits; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Renderer/DebugRendererPlayback.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Renderer/DebugRendererPlayback.cpp deleted file mode 100644 index bea7e4e08f8..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Renderer/DebugRendererPlayback.cpp +++ /dev/null @@ -1,168 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#ifdef JPH_DEBUG_RENDERER - -#include - -JPH_NAMESPACE_BEGIN - -void DebugRendererPlayback::Parse(StreamIn &inStream) -{ - using ECommand = DebugRendererRecorder::ECommand; - - for (;;) - { - // Read the next command - ECommand command; - inStream.Read(command); - - if (inStream.IsEOF() || inStream.IsFailed()) - return; - - if (command == ECommand::CreateBatch) - { - uint32 id; - inStream.Read(id); - - uint32 triangle_count; - inStream.Read(triangle_count); - - DebugRenderer::Triangle *triangles = new DebugRenderer::Triangle [triangle_count]; - inStream.ReadBytes(triangles, triangle_count * sizeof(DebugRenderer::Triangle)); - - mBatches.insert({ id, mRenderer.CreateTriangleBatch(triangles, triangle_count) }); - - delete [] triangles; - } - else if (command == ECommand::CreateBatchIndexed) - { - uint32 id; - inStream.Read(id); - - uint32 vertex_count; - inStream.Read(vertex_count); - - DebugRenderer::Vertex *vertices = new DebugRenderer::Vertex [vertex_count]; - inStream.ReadBytes(vertices, vertex_count * sizeof(DebugRenderer::Vertex)); - - uint32 index_count; - inStream.Read(index_count); - - uint32 *indices = new uint32 [index_count]; - inStream.ReadBytes(indices, index_count * sizeof(uint32)); - - mBatches.insert({ id, mRenderer.CreateTriangleBatch(vertices, vertex_count, indices, index_count) }); - - delete [] indices; - delete [] vertices; - } - else if (command == ECommand::CreateGeometry) - { - uint32 geometry_id; - inStream.Read(geometry_id); - - AABox bounds; - inStream.Read(bounds.mMin); - inStream.Read(bounds.mMax); - - DebugRenderer::GeometryRef geometry = new DebugRenderer::Geometry(bounds); - mGeometries[geometry_id] = geometry; - - uint32 num_lods; - inStream.Read(num_lods); - for (uint32 l = 0; l < num_lods; ++l) - { - DebugRenderer::LOD lod; - inStream.Read(lod.mDistance); - - uint32 batch_id; - inStream.Read(batch_id); - lod.mTriangleBatch = mBatches.find(batch_id)->second; - - geometry->mLODs.push_back(lod); - } - } - else if (command == ECommand::EndFrame) - { - mFrames.push_back({}); - Frame &frame = mFrames.back(); - - // Read all lines - uint32 num_lines = 0; - inStream.Read(num_lines); - frame.mLines.resize(num_lines); - for (DebugRendererRecorder::LineBlob &line : frame.mLines) - { - inStream.Read(line.mFrom); - inStream.Read(line.mTo); - inStream.Read(line.mColor); - } - - // Read all triangles - uint32 num_triangles = 0; - inStream.Read(num_triangles); - frame.mTriangles.resize(num_triangles); - for (DebugRendererRecorder::TriangleBlob &triangle : frame.mTriangles) - { - inStream.Read(triangle.mV1); - inStream.Read(triangle.mV2); - inStream.Read(triangle.mV3); - inStream.Read(triangle.mColor); - inStream.Read(triangle.mCastShadow); - } - - // Read all texts - uint32 num_texts = 0; - inStream.Read(num_texts); - frame.mTexts.resize(num_texts); - for (DebugRendererRecorder::TextBlob &text : frame.mTexts) - { - inStream.Read(text.mPosition); - inStream.Read(text.mString); - inStream.Read(text.mColor); - inStream.Read(text.mHeight); - } - - // Read all geometries - uint32 num_geometries = 0; - inStream.Read(num_geometries); - frame.mGeometries.resize(num_geometries); - for (DebugRendererRecorder::GeometryBlob &geom : frame.mGeometries) - { - inStream.Read(geom.mModelMatrix); - inStream.Read(geom.mModelColor); - inStream.Read(geom.mGeometryID); - inStream.Read(geom.mCullMode); - inStream.Read(geom.mCastShadow); - inStream.Read(geom.mDrawMode); - } - } - else - JPH_ASSERT(false); - } -} - -void DebugRendererPlayback::DrawFrame(uint inFrameNumber) const -{ - const Frame &frame = mFrames[inFrameNumber]; - - for (const DebugRendererRecorder::LineBlob &line : frame.mLines) - mRenderer.DrawLine(line.mFrom, line.mTo, line.mColor); - - for (const DebugRendererRecorder::TriangleBlob &triangle : frame.mTriangles) - mRenderer.DrawTriangle(triangle.mV1, triangle.mV2, triangle.mV3, triangle.mColor, triangle.mCastShadow); - - for (const DebugRendererRecorder::TextBlob &text : frame.mTexts) - mRenderer.DrawText3D(text.mPosition, text.mString, text.mColor, text.mHeight); - - for (const DebugRendererRecorder::GeometryBlob &geom : frame.mGeometries) - mRenderer.DrawGeometry(geom.mModelMatrix, geom.mModelColor, mGeometries.find(geom.mGeometryID)->second, geom.mCullMode, geom.mCastShadow, geom.mDrawMode); -} - -JPH_NAMESPACE_END - -#endif // JPH_DEBUG_RENDERER diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Renderer/DebugRendererPlayback.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Renderer/DebugRendererPlayback.h deleted file mode 100644 index 23ed4542389..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Renderer/DebugRendererPlayback.h +++ /dev/null @@ -1,48 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#ifndef JPH_DEBUG_RENDERER - #error This file should only be included when JPH_DEBUG_RENDERER is defined -#endif // !JPH_DEBUG_RENDERER - -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -/// Class that can read a recorded stream from DebugRendererRecorder and plays it back trough a DebugRenderer -class JPH_DEBUG_RENDERER_EXPORT DebugRendererPlayback -{ -public: - /// Constructor - DebugRendererPlayback(DebugRenderer &inRenderer) : mRenderer(inRenderer) { } - - /// Parse a stream of frames - void Parse(StreamIn &inStream); - - /// Get the number of parsed frames - uint GetNumFrames() const { return (uint)mFrames.size(); } - - /// Draw a frame - void DrawFrame(uint inFrameNumber) const; - -private: - /// The debug renderer we're using to do the actual rendering - DebugRenderer & mRenderer; - - /// Mapping of ID to batch - UnorderedMap mBatches; - - /// Mapping of ID to geometry - UnorderedMap mGeometries; - - /// The list of parsed frames - using Frame = DebugRendererRecorder::Frame; - Array mFrames; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Renderer/DebugRendererRecorder.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Renderer/DebugRendererRecorder.cpp deleted file mode 100644 index 2e25912957f..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Renderer/DebugRendererRecorder.cpp +++ /dev/null @@ -1,158 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#ifdef JPH_DEBUG_RENDERER - -#include - -JPH_NAMESPACE_BEGIN - -void DebugRendererRecorder::DrawLine(RVec3Arg inFrom, RVec3Arg inTo, ColorArg inColor) -{ - lock_guard lock(mMutex); - - mCurrentFrame.mLines.push_back({ inFrom, inTo, inColor }); -} - -void DebugRendererRecorder::DrawTriangle(RVec3Arg inV1, RVec3Arg inV2, RVec3Arg inV3, ColorArg inColor, ECastShadow inCastShadow) -{ - lock_guard lock(mMutex); - - mCurrentFrame.mTriangles.push_back({ inV1, inV2, inV3, inColor, inCastShadow }); -} - -DebugRenderer::Batch DebugRendererRecorder::CreateTriangleBatch(const Triangle *inTriangles, int inTriangleCount) -{ - if (inTriangles == nullptr || inTriangleCount == 0) - return new BatchImpl(0); - - lock_guard lock(mMutex); - - mStream.Write(ECommand::CreateBatch); - - uint32 batch_id = mNextBatchID++; - JPH_ASSERT(batch_id != 0); - mStream.Write(batch_id); - mStream.Write((uint32)inTriangleCount); - mStream.WriteBytes(inTriangles, inTriangleCount * sizeof(Triangle)); - - return new BatchImpl(batch_id); -} - -DebugRenderer::Batch DebugRendererRecorder::CreateTriangleBatch(const Vertex *inVertices, int inVertexCount, const uint32 *inIndices, int inIndexCount) -{ - if (inVertices == nullptr || inVertexCount == 0 || inIndices == nullptr || inIndexCount == 0) - return new BatchImpl(0); - - lock_guard lock(mMutex); - - mStream.Write(ECommand::CreateBatchIndexed); - - uint32 batch_id = mNextBatchID++; - JPH_ASSERT(batch_id != 0); - mStream.Write(batch_id); - mStream.Write((uint32)inVertexCount); - mStream.WriteBytes(inVertices, inVertexCount * sizeof(Vertex)); - mStream.Write((uint32)inIndexCount); - mStream.WriteBytes(inIndices, inIndexCount * sizeof(uint32)); - - return new BatchImpl(batch_id); -} - -void DebugRendererRecorder::DrawGeometry(RMat44Arg inModelMatrix, const AABox &inWorldSpaceBounds, float inLODScaleSq, ColorArg inModelColor, const GeometryRef &inGeometry, ECullMode inCullMode, ECastShadow inCastShadow, EDrawMode inDrawMode) -{ - lock_guard lock(mMutex); - - // See if this geometry was used before - uint32 &geometry_id = mGeometries[inGeometry]; - if (geometry_id == 0) - { - mStream.Write(ECommand::CreateGeometry); - - // Create a new ID - geometry_id = mNextGeometryID++; - JPH_ASSERT(geometry_id != 0); - mStream.Write(geometry_id); - - // Save bounds - mStream.Write(inGeometry->mBounds.mMin); - mStream.Write(inGeometry->mBounds.mMax); - - // Save the LODs - mStream.Write((uint32)inGeometry->mLODs.size()); - for (const LOD & lod : inGeometry->mLODs) - { - mStream.Write(lod.mDistance); - mStream.Write(static_cast(lod.mTriangleBatch.GetPtr())->mID); - } - } - - mCurrentFrame.mGeometries.push_back({ inModelMatrix, inModelColor, geometry_id, inCullMode, inCastShadow, inDrawMode }); -} - -void DebugRendererRecorder::DrawText3D(RVec3Arg inPosition, const string_view &inString, ColorArg inColor, float inHeight) -{ - lock_guard lock(mMutex); - - mCurrentFrame.mTexts.push_back({ inPosition, inString, inColor, inHeight }); -} - -void DebugRendererRecorder::EndFrame() -{ - lock_guard lock(mMutex); - - mStream.Write(ECommand::EndFrame); - - // Write all lines - mStream.Write((uint32)mCurrentFrame.mLines.size()); - for (const LineBlob &line : mCurrentFrame.mLines) - { - mStream.Write(line.mFrom); - mStream.Write(line.mTo); - mStream.Write(line.mColor); - } - mCurrentFrame.mLines.clear(); - - // Write all triangles - mStream.Write((uint32)mCurrentFrame.mTriangles.size()); - for (const TriangleBlob &triangle : mCurrentFrame.mTriangles) - { - mStream.Write(triangle.mV1); - mStream.Write(triangle.mV2); - mStream.Write(triangle.mV3); - mStream.Write(triangle.mColor); - mStream.Write(triangle.mCastShadow); - } - mCurrentFrame.mTriangles.clear(); - - // Write all texts - mStream.Write((uint32)mCurrentFrame.mTexts.size()); - for (const TextBlob &text : mCurrentFrame.mTexts) - { - mStream.Write(text.mPosition); - mStream.Write(text.mString); - mStream.Write(text.mColor); - mStream.Write(text.mHeight); - } - mCurrentFrame.mTexts.clear(); - - // Write all geometries - mStream.Write((uint32)mCurrentFrame.mGeometries.size()); - for (const GeometryBlob &geom : mCurrentFrame.mGeometries) - { - mStream.Write(geom.mModelMatrix); - mStream.Write(geom.mModelColor); - mStream.Write(geom.mGeometryID); - mStream.Write(geom.mCullMode); - mStream.Write(geom.mCastShadow); - mStream.Write(geom.mDrawMode); - } - mCurrentFrame.mGeometries.clear(); -} - -JPH_NAMESPACE_END - -#endif // JPH_DEBUG_RENDERER diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Renderer/DebugRendererRecorder.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Renderer/DebugRendererRecorder.h deleted file mode 100644 index 9608e03c908..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Renderer/DebugRendererRecorder.h +++ /dev/null @@ -1,130 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#ifndef JPH_DEBUG_RENDERER - #error This file should only be included when JPH_DEBUG_RENDERER is defined -#endif // !JPH_DEBUG_RENDERER - -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -/// Implementation of DebugRenderer that records the API invocations to be played back later -class JPH_DEBUG_RENDERER_EXPORT DebugRendererRecorder final : public DebugRenderer -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Constructor - DebugRendererRecorder(StreamOut &inStream) : mStream(inStream) { Initialize(); } - - /// Implementation of DebugRenderer interface - virtual void DrawLine(RVec3Arg inFrom, RVec3Arg inTo, ColorArg inColor) override; - virtual void DrawTriangle(RVec3Arg inV1, RVec3Arg inV2, RVec3Arg inV3, ColorArg inColor, ECastShadow inCastShadow) override; - virtual Batch CreateTriangleBatch(const Triangle *inTriangles, int inTriangleCount) override; - virtual Batch CreateTriangleBatch(const Vertex *inVertices, int inVertexCount, const uint32 *inIndices, int inIndexCount) override; - virtual void DrawGeometry(RMat44Arg inModelMatrix, const AABox &inWorldSpaceBounds, float inLODScaleSq, ColorArg inModelColor, const GeometryRef &inGeometry, ECullMode inCullMode, ECastShadow inCastShadow, EDrawMode inDrawMode) override; - virtual void DrawText3D(RVec3Arg inPosition, const string_view &inString, ColorArg inColor, float inHeight) override; - - /// Mark the end of a frame - void EndFrame(); - - /// Control commands written into the stream - enum class ECommand : uint8 - { - CreateBatch, - CreateBatchIndexed, - CreateGeometry, - EndFrame - }; - - /// Holds a single line segment - struct LineBlob - { - RVec3 mFrom; - RVec3 mTo; - Color mColor; - }; - - /// Holds a single triangle - struct TriangleBlob - { - RVec3 mV1; - RVec3 mV2; - RVec3 mV3; - Color mColor; - ECastShadow mCastShadow; - }; - - /// Holds a single text entry - struct TextBlob - { - TextBlob() = default; - TextBlob(RVec3Arg inPosition, const string_view &inString, ColorArg inColor, float inHeight) : mPosition(inPosition), mString(inString), mColor(inColor), mHeight(inHeight) { } - - RVec3 mPosition; - String mString; - Color mColor; - float mHeight; - }; - - /// Holds a single geometry draw call - struct GeometryBlob - { - RMat44 mModelMatrix; - Color mModelColor; - uint32 mGeometryID; - ECullMode mCullMode; - ECastShadow mCastShadow; - EDrawMode mDrawMode; - }; - - /// All information for a single frame - struct Frame - { - Array mLines; - Array mTriangles; - Array mTexts; - Array mGeometries; - }; - -private: - /// Implementation specific batch object - class BatchImpl : public RefTargetVirtual - { - public: - JPH_OVERRIDE_NEW_DELETE - - BatchImpl(uint32 inID) : mID(inID) { } - - virtual void AddRef() override { ++mRefCount; } - virtual void Release() override { if (--mRefCount == 0) delete this; } - - atomic mRefCount = 0; - uint32 mID; - }; - - /// Lock that prevents concurrent access to the internal structures - Mutex mMutex; - - /// Stream that recorded data will be sent to - StreamOut & mStream; - - /// Next available ID - uint32 mNextBatchID = 1; - uint32 mNextGeometryID = 1; - - /// Cached geometries and their IDs - UnorderedMap mGeometries; - - /// Data that is being accumulated for the current frame - Frame mCurrentFrame; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Renderer/DebugRendererSimple.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Renderer/DebugRendererSimple.cpp deleted file mode 100644 index a404d95a002..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Renderer/DebugRendererSimple.cpp +++ /dev/null @@ -1,80 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2024 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#ifdef JPH_DEBUG_RENDERER - -#include - -JPH_NAMESPACE_BEGIN - -DebugRendererSimple::DebugRendererSimple() -{ - Initialize(); -} - -DebugRenderer::Batch DebugRendererSimple::CreateTriangleBatch(const Triangle *inTriangles, int inTriangleCount) -{ - BatchImpl *batch = new BatchImpl; - if (inTriangles == nullptr || inTriangleCount == 0) - return batch; - - batch->mTriangles.assign(inTriangles, inTriangles + inTriangleCount); - return batch; -} - -DebugRenderer::Batch DebugRendererSimple::CreateTriangleBatch(const Vertex *inVertices, int inVertexCount, const uint32 *inIndices, int inIndexCount) -{ - BatchImpl *batch = new BatchImpl; - if (inVertices == nullptr || inVertexCount == 0 || inIndices == nullptr || inIndexCount == 0) - return batch; - - // Convert indexed triangle list to triangle list - batch->mTriangles.resize(inIndexCount / 3); - for (size_t t = 0; t < batch->mTriangles.size(); ++t) - { - Triangle &triangle = batch->mTriangles[t]; - triangle.mV[0] = inVertices[inIndices[t * 3 + 0]]; - triangle.mV[1] = inVertices[inIndices[t * 3 + 1]]; - triangle.mV[2] = inVertices[inIndices[t * 3 + 2]]; - } - - return batch; -} - -void DebugRendererSimple::DrawGeometry(RMat44Arg inModelMatrix, const AABox &inWorldSpaceBounds, float inLODScaleSq, ColorArg inModelColor, const GeometryRef &inGeometry, ECullMode inCullMode, ECastShadow inCastShadow, EDrawMode inDrawMode) -{ - // Figure out which LOD to use - const LOD *lod = inGeometry->mLODs.data(); - if (mCameraPosSet) - lod = &inGeometry->GetLOD(Vec3(mCameraPos), inWorldSpaceBounds, inLODScaleSq); - - // Draw the batch - const BatchImpl *batch = static_cast(lod->mTriangleBatch.GetPtr()); - for (const Triangle &triangle : batch->mTriangles) - { - RVec3 v0 = inModelMatrix * Vec3(triangle.mV[0].mPosition); - RVec3 v1 = inModelMatrix * Vec3(triangle.mV[1].mPosition); - RVec3 v2 = inModelMatrix * Vec3(triangle.mV[2].mPosition); - Color color = inModelColor * triangle.mV[0].mColor; - - switch (inDrawMode) - { - case EDrawMode::Wireframe: - DrawLine(v0, v1, color); - DrawLine(v1, v2, color); - DrawLine(v2, v0, color); - break; - - case EDrawMode::Solid: - DrawTriangle(v0, v1, v2, color, inCastShadow); - break; - } - } -} - -JPH_NAMESPACE_END - -#endif // JPH_DEBUG_RENDERER diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Renderer/DebugRendererSimple.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Renderer/DebugRendererSimple.h deleted file mode 100644 index 4a23ab758b7..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Renderer/DebugRendererSimple.h +++ /dev/null @@ -1,88 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2024 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#ifndef JPH_DEBUG_RENDERER - #error This file should only be included when JPH_DEBUG_RENDERER is defined -#endif // !JPH_DEBUG_RENDERER - -#include - -JPH_NAMESPACE_BEGIN - -/// Inherit from this class to simplify implementing a debug renderer, start with this implementation: -/// -/// class MyDebugRenderer : public JPH::DebugRendererSimple -/// { -/// public: -/// virtual void DrawLine(JPH::RVec3Arg inFrom, JPH::RVec3Arg inTo, JPH::ColorArg inColor) override -/// { -/// // Implement -/// } -/// -/// virtual void DrawTriangle(JPH::RVec3Arg inV1, JPH::RVec3Arg inV2, JPH::RVec3Arg inV3, JPH::ColorArg inColor, ECastShadow inCastShadow) override -/// { -/// // Implement -/// } -/// -/// virtual void DrawText3D(JPH::RVec3Arg inPosition, const string_view &inString, JPH::ColorArg inColor, float inHeight) override -/// { -/// // Implement -/// } -/// }; -/// -/// Note that this class is meant to be a quick start for implementing a debug renderer, it is not the most efficient way to implement a debug renderer. -class JPH_DEBUG_RENDERER_EXPORT DebugRendererSimple : public DebugRenderer -{ -public: - JPH_OVERRIDE_NEW_DELETE - - /// Constructor - DebugRendererSimple(); - - /// Should be called every frame by the application to provide the camera position. - /// This is used to determine the correct LOD for rendering. - void SetCameraPos(RVec3Arg inCameraPos) - { - mCameraPos = inCameraPos; - mCameraPosSet = true; - } - - /// Fallback implementation that uses DrawLine to draw a triangle (override this if you have a version that renders solid triangles) - virtual void DrawTriangle(RVec3Arg inV1, RVec3Arg inV2, RVec3Arg inV3, ColorArg inColor, ECastShadow inCastShadow) override - { - DrawLine(inV1, inV2, inColor); - DrawLine(inV2, inV3, inColor); - DrawLine(inV3, inV1, inColor); - } - -protected: - /// Implementation of DebugRenderer interface - virtual Batch CreateTriangleBatch(const Triangle *inTriangles, int inTriangleCount) override; - virtual Batch CreateTriangleBatch(const Vertex *inVertices, int inVertexCount, const uint32 *inIndices, int inIndexCount) override; - virtual void DrawGeometry(RMat44Arg inModelMatrix, const AABox &inWorldSpaceBounds, float inLODScaleSq, ColorArg inModelColor, const GeometryRef &inGeometry, ECullMode inCullMode, ECastShadow inCastShadow, EDrawMode inDrawMode) override; - -private: - /// Implementation specific batch object - class BatchImpl : public RefTargetVirtual - { - public: - JPH_OVERRIDE_NEW_DELETE - - virtual void AddRef() override { ++mRefCount; } - virtual void Release() override { if (--mRefCount == 0) delete this; } - - Array mTriangles; - - private: - atomic mRefCount = 0; - }; - - /// Last provided camera position - RVec3 mCameraPos; - bool mCameraPosSet = false; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Skeleton/SkeletalAnimation.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Skeleton/SkeletalAnimation.cpp deleted file mode 100644 index 4bf87007e9a..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Skeleton/SkeletalAnimation.cpp +++ /dev/null @@ -1,110 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SkeletalAnimation::JointState) -{ - JPH_ADD_ATTRIBUTE(JointState, mRotation) - JPH_ADD_ATTRIBUTE(JointState, mTranslation) -} - -JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SkeletalAnimation::Keyframe) -{ - JPH_ADD_BASE_CLASS(Keyframe, JointState) - - JPH_ADD_ATTRIBUTE(Keyframe, mTime) -} - -JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SkeletalAnimation::AnimatedJoint) -{ - JPH_ADD_ATTRIBUTE(AnimatedJoint, mJointName) - JPH_ADD_ATTRIBUTE(AnimatedJoint, mKeyframes) -} - -JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SkeletalAnimation) -{ - JPH_ADD_ATTRIBUTE(SkeletalAnimation, mAnimatedJoints) - JPH_ADD_ATTRIBUTE(SkeletalAnimation, mIsLooping) -} - - -void SkeletalAnimation::JointState::FromMatrix(Mat44Arg inMatrix) -{ - mRotation = inMatrix.GetQuaternion(); - mTranslation = inMatrix.GetTranslation(); -} - -float SkeletalAnimation::GetDuration() const -{ - if (!mAnimatedJoints.empty() && !mAnimatedJoints[0].mKeyframes.empty()) - return mAnimatedJoints[0].mKeyframes.back().mTime; - else - return 0.0f; -} - -void SkeletalAnimation::ScaleJoints(float inScale) -{ - for (SkeletalAnimation::AnimatedJoint &j : mAnimatedJoints) - for (SkeletalAnimation::Keyframe &k : j.mKeyframes) - k.mTranslation *= inScale; -} - -void SkeletalAnimation::Sample(float inTime, SkeletonPose &ioPose) const -{ - // Correct time when animation is looping - JPH_ASSERT(inTime >= 0.0f); - float duration = GetDuration(); - float time = duration > 0.0f && mIsLooping? fmod(inTime, duration) : inTime; - - for (const AnimatedJoint &aj : mAnimatedJoints) - { - // Do binary search for keyframe - int high = (int)aj.mKeyframes.size(), low = -1; - while (high - low > 1) - { - int probe = (high + low) / 2; - if (aj.mKeyframes[probe].mTime < time) - low = probe; - else - high = probe; - } - - JointState &state = ioPose.GetJoint(ioPose.GetSkeleton()->GetJointIndex(aj.mJointName)); - - if (low == -1) - { - // Before first key, return first key - state = static_cast(aj.mKeyframes.front()); - } - else if (high == (int)aj.mKeyframes.size()) - { - // Beyond last key, return last key - state = static_cast(aj.mKeyframes.back()); - } - else - { - // Interpolate - const Keyframe &s1 = aj.mKeyframes[low]; - const Keyframe &s2 = aj.mKeyframes[low + 1]; - - float fraction = (time - s1.mTime) / (s2.mTime - s1.mTime); - JPH_ASSERT(fraction >= 0.0f && fraction <= 1.0f); - - state.mTranslation = (1.0f - fraction) * s1.mTranslation + fraction * s2.mTranslation; - JPH_ASSERT(s1.mRotation.IsNormalized()); - JPH_ASSERT(s2.mRotation.IsNormalized()); - state.mRotation = s1.mRotation.SLERP(s2.mRotation, fraction); - JPH_ASSERT(state.mRotation.IsNormalized()); - } - } -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Skeleton/SkeletalAnimation.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Skeleton/SkeletalAnimation.h deleted file mode 100644 index 805912091e7..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Skeleton/SkeletalAnimation.h +++ /dev/null @@ -1,77 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include - -JPH_NAMESPACE_BEGIN - -class SkeletonPose; - -/// Resource for a skinned animation -class JPH_EXPORT SkeletalAnimation : public RefTarget -{ -public: - JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, SkeletalAnimation) - - /// Contains the current state of a joint, a local space transformation relative to its parent joint - class JointState - { - public: - JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, JointState) - - /// Convert from a local space matrix - void FromMatrix(Mat44Arg inMatrix); - - /// Convert to matrix representation - inline Mat44 ToMatrix() const { return Mat44::sRotationTranslation(mRotation, mTranslation); } - - Quat mRotation = Quat::sIdentity(); ///< Local space rotation of the joint - Vec3 mTranslation = Vec3::sZero(); ///< Local space translation of the joint - }; - - /// Contains the state of a single joint at a particular time - class Keyframe : public JointState - { - public: - JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, Keyframe) - - float mTime = 0.0f; ///< Time of keyframe in seconds - }; - - using KeyframeVector = Array; - - /// Contains the animation for a single joint - class AnimatedJoint - { - public: - JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, AnimatedJoint) - - String mJointName; ///< Name of the joint - KeyframeVector mKeyframes; ///< List of keyframes over time - }; - - using AnimatedJointVector = Array; - - /// Get the length (in seconds) of this animation - float GetDuration() const; - - /// Scale the size of all joints by inScale - void ScaleJoints(float inScale); - - /// Get the (interpolated) joint transforms at time inTime - void Sample(float inTime, SkeletonPose &ioPose) const; - - /// Get joint samples - const AnimatedJointVector & GetAnimatedJoints() const { return mAnimatedJoints; } - AnimatedJointVector & GetAnimatedJoints() { return mAnimatedJoints; } - -private: - AnimatedJointVector mAnimatedJoints; ///< List of joints and keyframes - bool mIsLooping = true; ///< If this animation loops back to start -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Skeleton/Skeleton.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Skeleton/Skeleton.cpp deleted file mode 100644 index 5ab7f56e792..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Skeleton/Skeleton.cpp +++ /dev/null @@ -1,82 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(Skeleton::Joint) -{ - JPH_ADD_ATTRIBUTE(Joint, mName) - JPH_ADD_ATTRIBUTE(Joint, mParentName) -} - -JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(Skeleton) -{ - JPH_ADD_ATTRIBUTE(Skeleton, mJoints) -} - -int Skeleton::GetJointIndex(const string_view &inName) const -{ - for (int i = 0; i < (int)mJoints.size(); ++i) - if (mJoints[i].mName == inName) - return i; - - return -1; -} - -void Skeleton::CalculateParentJointIndices() -{ - for (Joint &j : mJoints) - j.mParentJointIndex = GetJointIndex(j.mParentName); -} - -bool Skeleton::AreJointsCorrectlyOrdered() const -{ - for (int i = 0; i < (int)mJoints.size(); ++i) - if (mJoints[i].mParentJointIndex >= i) - return false; - - return true; -} - -void Skeleton::SaveBinaryState(StreamOut &inStream) const -{ - inStream.Write((uint32)mJoints.size()); - for (const Joint &j : mJoints) - { - inStream.Write(j.mName); - inStream.Write(j.mParentJointIndex); - inStream.Write(j.mParentName); - } -} - -Skeleton::SkeletonResult Skeleton::sRestoreFromBinaryState(StreamIn &inStream) -{ - Ref skeleton = new Skeleton; - - uint32 len = 0; - inStream.Read(len); - skeleton->mJoints.resize(len); - for (Joint &j : skeleton->mJoints) - { - inStream.Read(j.mName); - inStream.Read(j.mParentJointIndex); - inStream.Read(j.mParentName); - } - - SkeletonResult result; - if (inStream.IsEOF() || inStream.IsFailed()) - result.SetError("Failed to read skeleton from stream"); - else - result.Set(skeleton); - return result; -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Skeleton/Skeleton.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Skeleton/Skeleton.h deleted file mode 100644 index 3c2b84c6693..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Skeleton/Skeleton.h +++ /dev/null @@ -1,72 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -class StreamIn; -class StreamOut; - -/// Resource that contains the joint hierarchy for a skeleton -class JPH_EXPORT Skeleton : public RefTarget -{ -public: - JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, Skeleton) - - using SkeletonResult = Result>; - - /// Declare internal structure for a joint - class Joint - { - public: - JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, Joint) - - Joint() = default; - Joint(const string_view &inName, const string_view &inParentName, int inParentJointIndex) : mName(inName), mParentName(inParentName), mParentJointIndex(inParentJointIndex) { } - - String mName; ///< Name of the joint - String mParentName; ///< Name of parent joint - int mParentJointIndex = -1; ///< Index of parent joint (in mJoints) or -1 if it has no parent - }; - - using JointVector = Array; - - ///@name Access to the joints - ///@{ - const JointVector & GetJoints() const { return mJoints; } - JointVector & GetJoints() { return mJoints; } - int GetJointCount() const { return (int)mJoints.size(); } - const Joint & GetJoint(int inJoint) const { return mJoints[inJoint]; } - Joint & GetJoint(int inJoint) { return mJoints[inJoint]; } - uint AddJoint(const string_view &inName, const string_view &inParentName = string_view()) { mJoints.emplace_back(inName, inParentName, -1); return (uint)mJoints.size() - 1; } - uint AddJoint(const string_view &inName, int inParentIndex) { mJoints.emplace_back(inName, inParentIndex >= 0? mJoints[inParentIndex].mName : String(), inParentIndex); return (uint)mJoints.size() - 1; } - ///@} - - /// Find joint by name - int GetJointIndex(const string_view &inName) const; - - /// Fill in parent joint indices based on name - void CalculateParentJointIndices(); - - /// Many of the algorithms that use the Skeleton class require that parent joints are in the mJoints array before their children. - /// This function returns true if this is the case, false if not. - bool AreJointsCorrectlyOrdered() const; - - /// Saves the state of this object in binary form to inStream. - void SaveBinaryState(StreamOut &inStream) const; - - /// Restore the state of this object from inStream. - static SkeletonResult sRestoreFromBinaryState(StreamIn &inStream); - -private: - /// Joints - JointVector mJoints; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Skeleton/SkeletonMapper.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Skeleton/SkeletonMapper.cpp deleted file mode 100644 index 17f6a46de26..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Skeleton/SkeletonMapper.cpp +++ /dev/null @@ -1,237 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include - -JPH_NAMESPACE_BEGIN - -void SkeletonMapper::Initialize(const Skeleton *inSkeleton1, const Mat44 *inNeutralPose1, const Skeleton *inSkeleton2, const Mat44 *inNeutralPose2, const CanMapJoint &inCanMapJoint) -{ - JPH_ASSERT(mMappings.empty() && mChains.empty() && mUnmapped.empty()); // Should not be initialized yet - - // Count joints - int n1 = inSkeleton1->GetJointCount(); - int n2 = inSkeleton2->GetJointCount(); - JPH_ASSERT(n1 <= n2, "Skeleton 1 should be the low detail skeleton!"); - - // Keep track of mapped joints (initialize to false) - Array mapped1(n1, false); - Array mapped2(n2, false); - - // Find joints that can be mapped directly - for (int j1 = 0; j1 < n1; ++j1) - for (int j2 = 0; j2 < n2; ++j2) - if (inCanMapJoint(inSkeleton1, j1, inSkeleton2, j2)) - { - // Calculate the transform that takes this joint from skeleton 1 to 2 - Mat44 joint_1_to_2 = inNeutralPose1[j1].Inversed() * inNeutralPose2[j2]; - - // Ensure bottom right element is 1 (numerical imprecision in the inverse can make this not so) - joint_1_to_2(3, 3) = 1.0f; - - mMappings.emplace_back(j1, j2, joint_1_to_2); - mapped1[j1] = true; - mapped2[j2] = true; - break; - } - - Array cur_chain; // Taken out of the loop to minimize amount of allocations - - // Find joint chains - for (int m1 = 0; m1 < (int)mMappings.size(); ++m1) - { - Array chain2; - int chain2_m = -1; - - for (int m2 = m1 + 1; m2 < (int)mMappings.size(); ++m2) - { - // Find the chain from back from m2 to m1 - int start = mMappings[m1].mJointIdx2; - int end = mMappings[m2].mJointIdx2; - int cur = end; - cur_chain.clear(); // Should preserve memory - do - { - cur_chain.push_back(cur); - cur = inSkeleton2->GetJoint(cur).mParentJointIndex; - } - while (cur >= 0 && cur != start && !mapped2[cur]); - cur_chain.push_back(start); - - if (cur == start // This should be the correct chain - && cur_chain.size() > 2 // It should have joints between the mapped joints - && cur_chain.size() > chain2.size()) // And it should be the longest so far - { - chain2.swap(cur_chain); - chain2_m = m2; - } - } - - if (!chain2.empty()) - { - // Get the chain for 1 - Array chain1; - int start = mMappings[m1].mJointIdx1; - int cur = mMappings[chain2_m].mJointIdx1; - do - { - chain1.push_back(cur); - cur = inSkeleton1->GetJoint(cur).mParentJointIndex; - } - while (cur >= 0 && cur != start && !mapped1[cur]); - chain1.push_back(start); - - // If the chain exists in 1 too - if (cur == start) - { - // Reverse the chains - reverse(chain1.begin(), chain1.end()); - reverse(chain2.begin(), chain2.end()); - - // Mark elements mapped - for (int j1 : chain1) - mapped1[j1] = true; - for (int j2 : chain2) - mapped2[j2] = true; - - // Insert the chain - mChains.emplace_back(std::move(chain1), std::move(chain2)); - } - } - } - - // Collect unmapped joints from 2 - for (int j2 = 0; j2 < n2; ++j2) - if (!mapped2[j2]) - mUnmapped.emplace_back(j2, inSkeleton2->GetJoint(j2).mParentJointIndex); -} - -void SkeletonMapper::LockTranslations(const Skeleton *inSkeleton2, const bool *inLockedTranslations, const Mat44 *inNeutralPose2) -{ - JPH_ASSERT(inSkeleton2->AreJointsCorrectlyOrdered()); - - int n = inSkeleton2->GetJointCount(); - - // Copy locked joints to array but don't actually include the first joint (this is physics driven) - for (int i = 0; i < n; ++i) - if (inLockedTranslations[i]) - { - Locked l; - l.mJointIdx = i; - l.mParentJointIdx = inSkeleton2->GetJoint(i).mParentJointIndex; - if (l.mParentJointIdx >= 0) - l.mTranslation = inNeutralPose2[l.mParentJointIdx].Inversed() * inNeutralPose2[i].GetTranslation(); - else - l.mTranslation = inNeutralPose2[i].GetTranslation(); - mLockedTranslations.push_back(l); - } -} - -void SkeletonMapper::LockAllTranslations(const Skeleton *inSkeleton2, const Mat44 *inNeutralPose2) -{ - JPH_ASSERT(!mMappings.empty(), "Call Initialize first!"); - JPH_ASSERT(inSkeleton2->AreJointsCorrectlyOrdered()); - - // The first mapping is the top most one (remember that joints should be ordered so that parents go before children). - // Because we created the mappings from the lowest joint first, this should contain the first mappable joint. - int root_idx = mMappings[0].mJointIdx2; - - // Create temp array to hold locked joints - int n = inSkeleton2->GetJointCount(); - bool *locked_translations = (bool *)JPH_STACK_ALLOC(n * sizeof(bool)); - memset(locked_translations, 0, n * sizeof(bool)); - - // Mark root as locked - locked_translations[root_idx] = true; - - // Loop over all joints and propagate the locked flag to all children - for (int i = root_idx + 1; i < n; ++i) - { - int parent_idx = inSkeleton2->GetJoint(i).mParentJointIndex; - if (parent_idx >= 0) - locked_translations[i] = locked_translations[parent_idx]; - } - - // Unmark root because we don't actually want to include this (this determines the position of the entire ragdoll) - locked_translations[root_idx] = false; - - // Call the generic function - LockTranslations(inSkeleton2, locked_translations, inNeutralPose2); -} - -void SkeletonMapper::Map(const Mat44 *inPose1ModelSpace, const Mat44 *inPose2LocalSpace, Mat44 *outPose2ModelSpace) const -{ - // Apply direct mappings - for (const Mapping &m : mMappings) - outPose2ModelSpace[m.mJointIdx2] = inPose1ModelSpace[m.mJointIdx1] * m.mJoint1To2; - - // Apply chain mappings - for (const Chain &c : mChains) - { - // Calculate end of chain given local space transforms of the joints of the chain - Mat44 &chain_start = outPose2ModelSpace[c.mJointIndices2.front()]; - Mat44 chain_end = chain_start; - for (int j = 1; j < (int)c.mJointIndices2.size(); ++j) - chain_end = chain_end * inPose2LocalSpace[c.mJointIndices2[j]]; - - // Calculate the direction in world space for skeleton 1 and skeleton 2 and the rotation between them - Vec3 actual = chain_end.GetTranslation() - chain_start.GetTranslation(); - Vec3 desired = inPose1ModelSpace[c.mJointIndices1.back()].GetTranslation() - inPose1ModelSpace[c.mJointIndices1.front()].GetTranslation(); - Quat rotation = Quat::sFromTo(actual, desired); - - // Rotate the start of the chain - chain_start.SetRotation(Mat44::sRotation(rotation) * chain_start.GetRotation()); - - // Update all joints but the first and the last joint using their local space transforms - for (int j = 1; j < (int)c.mJointIndices2.size() - 1; ++j) - { - int parent = c.mJointIndices2[j - 1]; - int child = c.mJointIndices2[j]; - outPose2ModelSpace[child] = outPose2ModelSpace[parent] * inPose2LocalSpace[child]; - } - } - - // All unmapped joints take the local pose and convert it to model space - for (const Unmapped &u : mUnmapped) - if (u.mParentJointIdx >= 0) - { - JPH_ASSERT(u.mParentJointIdx < u.mJointIdx, "Joints must be ordered: parents first"); - outPose2ModelSpace[u.mJointIdx] = outPose2ModelSpace[u.mParentJointIdx] * inPose2LocalSpace[u.mJointIdx]; - } - else - outPose2ModelSpace[u.mJointIdx] = inPose2LocalSpace[u.mJointIdx]; - - // Update all locked joint translations - for (const Locked &l : mLockedTranslations) - outPose2ModelSpace[l.mJointIdx].SetTranslation(outPose2ModelSpace[l.mParentJointIdx] * l.mTranslation); -} - -void SkeletonMapper::MapReverse(const Mat44 *inPose2ModelSpace, Mat44 *outPose1ModelSpace) const -{ - // Normally each joint in skeleton 1 should be present in the mapping, so we only need to apply the direct mappings - for (const Mapping &m : mMappings) - outPose1ModelSpace[m.mJointIdx1] = inPose2ModelSpace[m.mJointIdx2] * m.mJoint2To1; -} - -int SkeletonMapper::GetMappedJointIdx(int inJoint1Idx) const -{ - for (const Mapping &m : mMappings) - if (m.mJointIdx1 == inJoint1Idx) - return m.mJointIdx2; - - return -1; -} - -bool SkeletonMapper::IsJointTranslationLocked(int inJoint2Idx) const -{ - for (const Locked &l : mLockedTranslations) - if (l.mJointIdx == inJoint2Idx) - return true; - - return false; -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Skeleton/SkeletonMapper.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Skeleton/SkeletonMapper.h deleted file mode 100644 index 05cc8866a4b..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Skeleton/SkeletonMapper.h +++ /dev/null @@ -1,145 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2022 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include - -JPH_NAMESPACE_BEGIN - -/// Class that is able to map a low detail (ragdoll) skeleton to a high detail (animation) skeleton and vice versa -class JPH_EXPORT SkeletonMapper : public RefTarget -{ -public: - /// A joint that maps 1-on-1 to a joint in the other skeleton - class Mapping - { - public: - Mapping() = default; - Mapping(int inJointIdx1, int inJointIdx2, Mat44Arg inJoint1To2) : mJointIdx1(inJointIdx1), mJointIdx2(inJointIdx2), mJoint1To2(inJoint1To2), mJoint2To1(inJoint1To2.Inversed()) - { - // Ensure bottom right element is 1 (numerical imprecision in the inverse can make this not so) - mJoint2To1(3, 3) = 1.0f; - } - - int mJointIdx1; ///< Index of joint from skeleton 1 - int mJointIdx2; ///< Corresponding index of joint from skeleton 2 - Mat44 mJoint1To2; ///< Transforms this joint from skeleton 1 to 2 - Mat44 mJoint2To1; ///< Inverse of the transform above - }; - - /// A joint chain that starts with a 1-on-1 mapped joint and ends with a 1-on-1 mapped joint with intermediate joints that cannot be mapped - class Chain - { - public: - Chain() = default; - Chain(Array &&inJointIndices1, Array &&inJointIndices2) : mJointIndices1(std::move(inJointIndices1)), mJointIndices2(std::move(inJointIndices2)) { } - - Array mJointIndices1; ///< Joint chain from skeleton 1 - Array mJointIndices2; ///< Corresponding joint chain from skeleton 2 - }; - - /// Joints that could not be mapped from skeleton 1 to 2 - class Unmapped - { - public: - Unmapped() = default; - Unmapped(int inJointIdx, int inParentJointIdx) : mJointIdx(inJointIdx), mParentJointIdx(inParentJointIdx) { } - - int mJointIdx; ///< Joint index of unmappable joint - int mParentJointIdx; ///< Parent joint index of unmappable joint - }; - - /// Joints that should have their translation locked (fixed) - class Locked - { - public: - int mJointIdx; ///< Joint index of joint with locked translation (in skeleton 2) - int mParentJointIdx; ///< Parent joint index of joint with locked translation (in skeleton 2) - Vec3 mTranslation; ///< Translation of neutral pose - }; - - /// A function that is called to determine if a joint can be mapped from source to target skeleton - using CanMapJoint = function; - - /// Default function that checks if the names of the joints are equal - static bool sDefaultCanMapJoint(const Skeleton *inSkeleton1, int inIndex1, const Skeleton *inSkeleton2, int inIndex2) - { - return inSkeleton1->GetJoint(inIndex1).mName == inSkeleton2->GetJoint(inIndex2).mName; - } - - /// Initialize the skeleton mapper. Skeleton 1 should be the (low detail) ragdoll skeleton and skeleton 2 the (high detail) animation skeleton. - /// We assume that each joint in skeleton 1 can be mapped to a joint in skeleton 2 (if not mapping from animation skeleton to ragdoll skeleton will be undefined). - /// Skeleton 2 should have the same hierarchy as skeleton 1 but can contain extra joints between those in skeleton 1 and it can have extra joints at the root and leaves of the skeleton. - /// @param inSkeleton1 Source skeleton to map from. - /// @param inNeutralPose1 Neutral pose of the source skeleton (model space) - /// @param inSkeleton2 Target skeleton to map to. - /// @param inNeutralPose2 Neutral pose of the target skeleton (model space), inNeutralPose1 and inNeutralPose2 must match as closely as possible, preferably the position of the mappable joints should be identical. - /// @param inCanMapJoint Function that checks if joints in skeleton 1 and skeleton 2 are equal. - void Initialize(const Skeleton *inSkeleton1, const Mat44 *inNeutralPose1, const Skeleton *inSkeleton2, const Mat44 *inNeutralPose2, const CanMapJoint &inCanMapJoint = sDefaultCanMapJoint); - - /// This can be called so lock the translation of a specified set of joints in skeleton 2. - /// Because constraints are never 100% rigid, there's always a little bit of stretch in the ragdoll when the ragdoll is under stress. - /// Locking the translations of the pose will remove the visual stretch from the ragdoll but will introduce a difference between the - /// physical simulation and the visual representation. - /// @param inSkeleton2 Target skeleton to map to. - /// @param inLockedTranslations An array of bools the size of inSkeleton2->GetJointCount(), for each joint indicating if the joint is locked. - /// @param inNeutralPose2 Neutral pose to take reference translations from - void LockTranslations(const Skeleton *inSkeleton2, const bool *inLockedTranslations, const Mat44 *inNeutralPose2); - - /// After Initialize(), this can be called to lock the translation of all joints in skeleton 2 below the first mapped joint to those of the neutral pose. - /// Because constraints are never 100% rigid, there's always a little bit of stretch in the ragdoll when the ragdoll is under stress. - /// Locking the translations of the pose will remove the visual stretch from the ragdoll but will introduce a difference between the - /// physical simulation and the visual representation. - /// @param inSkeleton2 Target skeleton to map to. - /// @param inNeutralPose2 Neutral pose to take reference translations from - void LockAllTranslations(const Skeleton *inSkeleton2, const Mat44 *inNeutralPose2); - - /// Map a pose. Joints that were directly mappable will be copied in model space from pose 1 to pose 2. Any joints that are only present in skeleton 2 - /// will get their model space transform calculated through the local space transforms of pose 2. Joints that are part of a joint chain between two - /// mapped joints will be reoriented towards the next joint in skeleton 1. This means that it is possible for unmapped joints to have some animation, - /// but very extreme animation poses will show artifacts. - /// @param inPose1ModelSpace Pose on skeleton 1 in model space - /// @param inPose2LocalSpace Pose on skeleton 2 in local space (used for the joints that cannot be mapped) - /// @param outPose2ModelSpace Model space pose on skeleton 2 (the output of the mapping) - void Map(const Mat44 *inPose1ModelSpace, const Mat44 *inPose2LocalSpace, Mat44 *outPose2ModelSpace) const; - - /// Reverse map a pose, this will only use the mappings and not the chains (it assumes that all joints in skeleton 1 are mapped) - /// @param inPose2ModelSpace Model space pose on skeleton 2 - /// @param outPose1ModelSpace When the function returns this will contain the model space pose for skeleton 1 - void MapReverse(const Mat44 *inPose2ModelSpace, Mat44 *outPose1ModelSpace) const; - - /// Search through the directly mapped joints (mMappings) and find inJoint1Idx, returns the corresponding Joint2Idx or -1 if not found. - int GetMappedJointIdx(int inJoint1Idx) const; - - /// Search through the locked translations (mLockedTranslations) and find if joint inJoint2Idx is locked. - bool IsJointTranslationLocked(int inJoint2Idx) const; - - using MappingVector = Array; - using ChainVector = Array; - using UnmappedVector = Array; - using LockedVector = Array; - - ///@name Access to the mapped joints - ///@{ - const MappingVector & GetMappings() const { return mMappings; } - MappingVector & GetMappings() { return mMappings; } - const ChainVector & GetChains() const { return mChains; } - ChainVector & GetChains() { return mChains; } - const UnmappedVector & GetUnmapped() const { return mUnmapped; } - UnmappedVector & GetUnmapped() { return mUnmapped; } - const LockedVector & GetLockedTranslations() const { return mLockedTranslations; } - LockedVector & GetLockedTranslations() { return mLockedTranslations; } - ///@} - -private: - /// Joint mappings - MappingVector mMappings; - ChainVector mChains; - UnmappedVector mUnmapped; ///< Joint indices that could not be mapped from 1 to 2 (these are indices in 2) - LockedVector mLockedTranslations; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Skeleton/SkeletonPose.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Skeleton/SkeletonPose.cpp deleted file mode 100644 index c64bf049716..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Skeleton/SkeletonPose.cpp +++ /dev/null @@ -1,87 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#ifdef JPH_DEBUG_RENDERER - #include -#endif // JPH_DEBUG_RENDERER - -JPH_NAMESPACE_BEGIN - -void SkeletonPose::SetSkeleton(const Skeleton *inSkeleton) -{ - mSkeleton = inSkeleton; - - mJoints.resize(mSkeleton->GetJointCount()); - mJointMatrices.resize(mSkeleton->GetJointCount()); -} - -void SkeletonPose::CalculateJointMatrices() -{ - for (int i = 0; i < (int)mJoints.size(); ++i) - { - mJointMatrices[i] = mJoints[i].ToMatrix(); - - int parent = mSkeleton->GetJoint(i).mParentJointIndex; - if (parent >= 0) - { - JPH_ASSERT(parent < i, "Joints must be ordered: parents first"); - mJointMatrices[i] = mJointMatrices[parent] * mJointMatrices[i]; - } - } -} - -void SkeletonPose::CalculateJointStates() -{ - for (int i = 0; i < (int)mJoints.size(); ++i) - { - Mat44 local_transform; - int parent = mSkeleton->GetJoint(i).mParentJointIndex; - if (parent >= 0) - local_transform = mJointMatrices[parent].Inversed() * mJointMatrices[i]; - else - local_transform = mJointMatrices[i]; - - JointState &joint = mJoints[i]; - joint.mTranslation = local_transform.GetTranslation(); - joint.mRotation = local_transform.GetQuaternion(); - } -} - -void SkeletonPose::CalculateLocalSpaceJointMatrices(Mat44 *outMatrices) const -{ - for (int i = 0; i < (int)mJoints.size(); ++i) - outMatrices[i] = mJoints[i].ToMatrix(); -} - -#ifdef JPH_DEBUG_RENDERER -void SkeletonPose::Draw(const DrawSettings &inDrawSettings, DebugRenderer *inRenderer, RMat44Arg inOffset) const -{ - RMat44 offset = inOffset * RMat44::sTranslation(mRootOffset); - - const Skeleton::JointVector &joints = mSkeleton->GetJoints(); - - for (int b = 0; b < mSkeleton->GetJointCount(); ++b) - { - RMat44 joint_transform = offset * mJointMatrices[b]; - - if (inDrawSettings.mDrawJoints) - { - int parent = joints[b].mParentJointIndex; - if (parent >= 0) - inRenderer->DrawLine(offset * mJointMatrices[parent].GetTranslation(), joint_transform.GetTranslation(), Color::sGreen); - } - - if (inDrawSettings.mDrawJointOrientations) - inRenderer->DrawCoordinateSystem(joint_transform, 0.05f); - - if (inDrawSettings.mDrawJointNames) - inRenderer->DrawText3D(joint_transform.GetTranslation(), joints[b].mName, Color::sWhite, 0.05f); - } -} -#endif // JPH_DEBUG_RENDERER - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Skeleton/SkeletonPose.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Skeleton/SkeletonPose.h deleted file mode 100644 index 326227e447a..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/Skeleton/SkeletonPose.h +++ /dev/null @@ -1,82 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include - -JPH_NAMESPACE_BEGIN - -#ifdef JPH_DEBUG_RENDERER -class DebugRenderer; -#endif // JPH_DEBUG_RENDERER - -/// Instance of a skeleton, contains the pose the current skeleton is in -class JPH_EXPORT SkeletonPose -{ -public: - JPH_OVERRIDE_NEW_DELETE - - using JointState = SkeletalAnimation::JointState; - using JointStateVector = Array; - using Mat44Vector = Array; - - ///@name Skeleton - ///@{ - void SetSkeleton(const Skeleton *inSkeleton); - const Skeleton * GetSkeleton() const { return mSkeleton; } - ///@} - - /// Extra offset applied to the root (and therefore also to all of its children) - void SetRootOffset(RVec3Arg inOffset) { mRootOffset = inOffset; } - RVec3 GetRootOffset() const { return mRootOffset; } - - ///@name Properties of the joints - ///@{ - uint GetJointCount() const { return (uint)mJoints.size(); } - const JointStateVector & GetJoints() const { return mJoints; } - JointStateVector & GetJoints() { return mJoints; } - const JointState & GetJoint(int inJoint) const { return mJoints[inJoint]; } - JointState & GetJoint(int inJoint) { return mJoints[inJoint]; } - ///@} - - ///@name Joint matrices - ///@{ - const Mat44Vector & GetJointMatrices() const { return mJointMatrices; } - Mat44Vector & GetJointMatrices() { return mJointMatrices; } - const Mat44 & GetJointMatrix(int inJoint) const { return mJointMatrices[inJoint]; } - Mat44 & GetJointMatrix(int inJoint) { return mJointMatrices[inJoint]; } - ///@} - - /// Convert the joint states to joint matrices - void CalculateJointMatrices(); - - /// Convert joint matrices to joint states - void CalculateJointStates(); - - /// Outputs the joint matrices in local space (ensure that outMatrices has GetJointCount() elements, assumes that values in GetJoints() is up to date) - void CalculateLocalSpaceJointMatrices(Mat44 *outMatrices) const; - -#ifdef JPH_DEBUG_RENDERER - /// Draw settings - struct DrawSettings - { - bool mDrawJoints = true; - bool mDrawJointOrientations = true; - bool mDrawJointNames = false; - }; - - /// Draw current pose - void Draw(const DrawSettings &inDrawSettings, DebugRenderer *inRenderer, RMat44Arg inOffset = RMat44::sIdentity()) const; -#endif // JPH_DEBUG_RENDERER - -private: - RefConst mSkeleton; ///< Skeleton definition - RVec3 mRootOffset { RVec3::sZero() }; ///< Extra offset applied to the root (and therefore also to all of its children) - JointStateVector mJoints; ///< Local joint orientations (local to parent Joint) - Mat44Vector mJointMatrices; ///< Local joint matrices (local to world matrix) -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleGrouper/TriangleGrouper.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleGrouper/TriangleGrouper.h deleted file mode 100644 index 9d75691f68c..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleGrouper/TriangleGrouper.h +++ /dev/null @@ -1,27 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include - -JPH_NAMESPACE_BEGIN - -/// A class that groups triangles in batches of N (according to closeness) -class JPH_EXPORT TriangleGrouper : public NonCopyable -{ -public: - /// Virtual destructor - virtual ~TriangleGrouper() = default; - - /// Group a batch of indexed triangles - /// @param inVertices The list of vertices - /// @param inTriangles The list of indexed triangles (indexes into inVertices) - /// @param inGroupSize How big each group should be - /// @param outGroupedTriangleIndices An ordered list of indices (indexing into inTriangles), contains groups of inGroupSize large worth of indices to triangles that are grouped together. If the triangle count is not an exact multiple of inGroupSize the last batch will be smaller. - virtual void Group(const VertexList &inVertices, const IndexedTriangleList &inTriangles, int inGroupSize, Array &outGroupedTriangleIndices) = 0; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleGrouper/TriangleGrouperClosestCentroid.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleGrouper/TriangleGrouperClosestCentroid.cpp deleted file mode 100644 index b59cdda9adb..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleGrouper/TriangleGrouperClosestCentroid.cpp +++ /dev/null @@ -1,95 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -void TriangleGrouperClosestCentroid::Group(const VertexList &inVertices, const IndexedTriangleList &inTriangles, int inGroupSize, Array &outGroupedTriangleIndices) -{ - const uint triangle_count = (uint)inTriangles.size(); - const uint num_batches = (triangle_count + inGroupSize - 1) / inGroupSize; - - Array centroids; - centroids.resize(triangle_count); - - outGroupedTriangleIndices.resize(triangle_count); - - for (uint t = 0; t < triangle_count; ++t) - { - // Store centroid - centroids[t] = inTriangles[t].GetCentroid(inVertices); - - // Initialize sort table - outGroupedTriangleIndices[t] = t; - } - - Array::iterator triangles_end = outGroupedTriangleIndices.end(); - - // Sort per batch - for (uint b = 0; b < num_batches - 1; ++b) - { - // Get iterators - Array::iterator batch_begin = outGroupedTriangleIndices.begin() + b * inGroupSize; - Array::iterator batch_end = batch_begin + inGroupSize; - Array::iterator batch_begin_plus_1 = batch_begin + 1; - Array::iterator batch_end_minus_1 = batch_end - 1; - - // Find triangle with centroid with lowest X coordinate - Array::iterator lowest_iter = batch_begin; - float lowest_val = centroids[*lowest_iter].GetX(); - for (Array::iterator other = batch_begin; other != triangles_end; ++other) - { - float val = centroids[*other].GetX(); - if (val < lowest_val) - { - lowest_iter = other; - lowest_val = val; - } - } - - // Make this triangle the first in a new batch - swap(*batch_begin, *lowest_iter); - Vec3 first_centroid = centroids[*batch_begin]; - - // Sort remaining triangles in batch on distance to first triangle - QuickSort(batch_begin_plus_1, batch_end, - [&first_centroid, ¢roids](uint inLHS, uint inRHS) - { - return (centroids[inLHS] - first_centroid).LengthSq() < (centroids[inRHS] - first_centroid).LengthSq(); - }); - - // Loop over remaining triangles - float furthest_dist = (centroids[*batch_end_minus_1] - first_centroid).LengthSq(); - for (Array::iterator other = batch_end; other != triangles_end; ++other) - { - // Check if this triangle is closer than the furthest triangle in the batch - float dist = (centroids[*other] - first_centroid).LengthSq(); - if (dist < furthest_dist) - { - // Replace furthest triangle - uint other_val = *other; - *other = *batch_end_minus_1; - - // Find first element that is bigger than this one and insert the current item before it - Array::iterator upper = std::upper_bound(batch_begin_plus_1, batch_end, dist, - [&first_centroid, ¢roids](float inLHS, uint inRHS) - { - return inLHS < (centroids[inRHS] - first_centroid).LengthSq(); - }); - copy_backward(upper, batch_end_minus_1, batch_end); - *upper = other_val; - - // Calculate new furthest distance - furthest_dist = (centroids[*batch_end_minus_1] - first_centroid).LengthSq(); - } - } - } -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleGrouper/TriangleGrouperClosestCentroid.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleGrouper/TriangleGrouperClosestCentroid.h deleted file mode 100644 index 58322741646..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleGrouper/TriangleGrouperClosestCentroid.h +++ /dev/null @@ -1,21 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -/// A class that groups triangles in batches of N. -/// Starts with centroid with lowest X coordinate and finds N closest centroids, this repeats until all groups have been found. -/// Time complexity: O(N^2) -class JPH_EXPORT TriangleGrouperClosestCentroid : public TriangleGrouper -{ -public: - // See: TriangleGrouper::Group - virtual void Group(const VertexList &inVertices, const IndexedTriangleList &inTriangles, int inGroupSize, Array &outGroupedTriangleIndices) override; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleGrouper/TriangleGrouperMorton.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleGrouper/TriangleGrouperMorton.cpp deleted file mode 100644 index a6e5a564426..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleGrouper/TriangleGrouperMorton.cpp +++ /dev/null @@ -1,49 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -void TriangleGrouperMorton::Group(const VertexList &inVertices, const IndexedTriangleList &inTriangles, int inGroupSize, Array &outGroupedTriangleIndices) -{ - const uint triangle_count = (uint)inTriangles.size(); - - Array centroids; - centroids.resize(triangle_count); - - outGroupedTriangleIndices.resize(triangle_count); - - for (uint t = 0; t < triangle_count; ++t) - { - // Store centroid - centroids[t] = inTriangles[t].GetCentroid(inVertices); - - // Initialize sort table - outGroupedTriangleIndices[t] = t; - } - - // Get bounding box of all centroids - AABox centroid_bounds; - for (uint t = 0; t < triangle_count; ++t) - centroid_bounds.Encapsulate(centroids[t]); - - // Make sure box is not degenerate - centroid_bounds.EnsureMinimalEdgeLength(1.0e-5f); - - // Calculate morton code for each centroid - Array morton_codes; - morton_codes.resize(triangle_count); - for (uint t = 0; t < triangle_count; ++t) - morton_codes[t] = MortonCode::sGetMortonCode(centroids[t], centroid_bounds); - - // Sort triangles based on morton code - QuickSort(outGroupedTriangleIndices.begin(), outGroupedTriangleIndices.end(), [&morton_codes](uint inLHS, uint inRHS) { return morton_codes[inLHS] < morton_codes[inRHS]; }); -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleGrouper/TriangleGrouperMorton.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleGrouper/TriangleGrouperMorton.h deleted file mode 100644 index a35f9af004e..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleGrouper/TriangleGrouperMorton.h +++ /dev/null @@ -1,20 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -/// A class that groups triangles in batches of N according to morton code of centroid. -/// Time complexity: O(N log(N)) -class JPH_EXPORT TriangleGrouperMorton : public TriangleGrouper -{ -public: - // See: TriangleGrouper::Group - virtual void Group(const VertexList &inVertices, const IndexedTriangleList &inTriangles, int inGroupSize, Array &outGroupedTriangleIndices) override; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitter.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitter.cpp deleted file mode 100644 index b7852a919c5..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitter.cpp +++ /dev/null @@ -1,67 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include - -JPH_NAMESPACE_BEGIN - -TriangleSplitter::TriangleSplitter(const VertexList &inVertices, const IndexedTriangleList &inTriangles) : - mVertices(inVertices), - mTriangles(inTriangles) -{ - mSortedTriangleIdx.resize(inTriangles.size()); - mCentroids.resize(inTriangles.size()); - - for (uint t = 0; t < inTriangles.size(); ++t) - { - // Initially triangles start unsorted - mSortedTriangleIdx[t] = t; - - // Calculate centroid - inTriangles[t].GetCentroid(inVertices).StoreFloat3(&mCentroids[t]); - } -} - -bool TriangleSplitter::SplitInternal(const Range &inTriangles, uint inDimension, float inSplit, Range &outLeft, Range &outRight) -{ - // Divide triangles - uint start = inTriangles.mBegin, end = inTriangles.mEnd; - while (start < end) - { - // Search for first element that is on the right hand side of the split plane - while (start < end && mCentroids[mSortedTriangleIdx[start]][inDimension] < inSplit) - ++start; - - // Search for the first element that is on the left hand side of the split plane - while (start < end && mCentroids[mSortedTriangleIdx[end - 1]][inDimension] >= inSplit) - --end; - - if (start < end) - { - // Swap the two elements - swap(mSortedTriangleIdx[start], mSortedTriangleIdx[end - 1]); - ++start; - --end; - } - } - JPH_ASSERT(start == end); - -#ifdef JPH_ENABLE_ASSERTS - // Validate division algorithm - JPH_ASSERT(inTriangles.mBegin <= start); - JPH_ASSERT(start <= inTriangles.mEnd); - for (uint i = inTriangles.mBegin; i < start; ++i) - JPH_ASSERT(mCentroids[mSortedTriangleIdx[i]][inDimension] < inSplit); - for (uint i = start; i < inTriangles.mEnd; ++i) - JPH_ASSERT(mCentroids[mSortedTriangleIdx[i]][inDimension] >= inSplit); -#endif - - outLeft = Range(inTriangles.mBegin, start); - outRight = Range(start, inTriangles.mEnd); - return outLeft.Count() > 0 && outRight.Count() > 0; -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitter.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitter.h deleted file mode 100644 index a66672238fc..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitter.h +++ /dev/null @@ -1,84 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include - -JPH_NAMESPACE_BEGIN - -/// A class that splits a triangle list into two parts for building a tree -class JPH_EXPORT TriangleSplitter : public NonCopyable -{ -public: - /// Constructor - TriangleSplitter(const VertexList &inVertices, const IndexedTriangleList &inTriangles); - - /// Virtual destructor - virtual ~TriangleSplitter() = default; - - struct Stats - { - const char * mSplitterName = nullptr; - int mLeafSize = 0; - }; - - /// Get stats of splitter - virtual void GetStats(Stats &outStats) const = 0; - - /// Helper struct to indicate triangle range before and after the split - struct Range - { - /// Constructor - Range() = default; - Range(uint inBegin, uint inEnd) : mBegin(inBegin), mEnd(inEnd) { } - - /// Get number of triangles in range - uint Count() const - { - return mEnd - mBegin; - } - - /// Start and end index (end = 1 beyond end) - uint mBegin; - uint mEnd; - }; - - /// Range of triangles to start with - Range GetInitialRange() const - { - return Range(0, (uint)mSortedTriangleIdx.size()); - } - - /// Split triangles into two groups left and right, returns false if no split could be made - /// @param inTriangles The range of triangles (in mSortedTriangleIdx) to process - /// @param outLeft On return this will contain the ranges for the left subpart. mSortedTriangleIdx may have been shuffled. - /// @param outRight On return this will contain the ranges for the right subpart. mSortedTriangleIdx may have been shuffled. - /// @return Returns true when a split was found - virtual bool Split(const Range &inTriangles, Range &outLeft, Range &outRight) = 0; - - /// Get the list of vertices - const VertexList & GetVertices() const - { - return mVertices; - } - - /// Get triangle by index - const IndexedTriangle & GetTriangle(uint inIdx) const - { - return mTriangles[mSortedTriangleIdx[inIdx]]; - } - -protected: - /// Helper function to split triangles based on dimension and split value - bool SplitInternal(const Range &inTriangles, uint inDimension, float inSplit, Range &outLeft, Range &outRight); - - const VertexList & mVertices; ///< Vertices of the indexed triangles - const IndexedTriangleList & mTriangles; ///< Unsorted triangles - Array mCentroids; ///< Unsorted centroids of triangles - Array mSortedTriangleIdx; ///< Indices to sort triangles -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterBinning.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterBinning.cpp deleted file mode 100644 index 989520c45ba..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterBinning.cpp +++ /dev/null @@ -1,112 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include - - JPH_NAMESPACE_BEGIN - -TriangleSplitterBinning::TriangleSplitterBinning(const VertexList &inVertices, const IndexedTriangleList &inTriangles, uint inMinNumBins, uint inMaxNumBins, uint inNumTrianglesPerBin) : - TriangleSplitter(inVertices, inTriangles), - mMinNumBins(inMinNumBins), - mMaxNumBins(inMaxNumBins), - mNumTrianglesPerBin(inNumTrianglesPerBin) -{ - mBins.resize(mMaxNumBins); -} - -bool TriangleSplitterBinning::Split(const Range &inTriangles, Range &outLeft, Range &outRight) -{ - // Calculate bounds for this range - AABox centroid_bounds; - for (uint t = inTriangles.mBegin; t < inTriangles.mEnd; ++t) - centroid_bounds.Encapsulate(Vec3(mCentroids[mSortedTriangleIdx[t]])); - - float best_cp = FLT_MAX; - uint best_dim = 0xffffffff; - float best_split = 0; - - // Bin in all dimensions - uint num_bins = Clamp(inTriangles.Count() / mNumTrianglesPerBin, mMinNumBins, mMaxNumBins); - for (uint dim = 0; dim < 3; ++dim) - { - float bounds_min = centroid_bounds.mMin[dim]; - float bounds_size = centroid_bounds.mMax[dim] - bounds_min; - - // Skip axis if too small - if (bounds_size < 1.0e-5f) - continue; - - // Initialize bins - for (uint b = 0; b < num_bins; ++b) - { - Bin &bin = mBins[b]; - bin.mBounds.SetEmpty(); - bin.mMinCentroid = bounds_min + bounds_size * (b + 1) / num_bins; - bin.mNumTriangles = 0; - } - - // Bin all triangles - for (uint t = inTriangles.mBegin; t < inTriangles.mEnd; ++t) - { - float centroid_pos = mCentroids[mSortedTriangleIdx[t]][dim]; - - // Select bin - uint bin_no = min(uint((centroid_pos - bounds_min) / bounds_size * num_bins), num_bins - 1); - Bin &bin = mBins[bin_no]; - - // Accumulate triangle in bin - bin.mBounds.Encapsulate(mVertices, GetTriangle(t)); - bin.mMinCentroid = min(bin.mMinCentroid, centroid_pos); - bin.mNumTriangles++; - } - - // Calculate totals left to right - AABox prev_bounds; - int prev_triangles = 0; - for (uint b = 0; b < num_bins; ++b) - { - Bin &bin = mBins[b]; - bin.mBoundsAccumulatedLeft = prev_bounds; // Don't include this node as we'll take a split on the left side of the bin - bin.mNumTrianglesAccumulatedLeft = prev_triangles; - prev_bounds.Encapsulate(bin.mBounds); - prev_triangles += bin.mNumTriangles; - } - - // Calculate totals right to left - prev_bounds.SetEmpty(); - prev_triangles = 0; - for (int b = num_bins - 1; b >= 0; --b) - { - Bin &bin = mBins[b]; - prev_bounds.Encapsulate(bin.mBounds); - prev_triangles += bin.mNumTriangles; - bin.mBoundsAccumulatedRight = prev_bounds; - bin.mNumTrianglesAccumulatedRight = prev_triangles; - } - - // Get best splitting plane - for (uint b = 1; b < num_bins; ++b) // Start at 1 since selecting bin 0 would result in everything ending up on the right side - { - // Calculate surface area heuristic and see if it is better than the current best - const Bin &bin = mBins[b]; - float cp = bin.mBoundsAccumulatedLeft.GetSurfaceArea() * bin.mNumTrianglesAccumulatedLeft + bin.mBoundsAccumulatedRight.GetSurfaceArea() * bin.mNumTrianglesAccumulatedRight; - if (cp < best_cp) - { - best_cp = cp; - best_dim = dim; - best_split = bin.mMinCentroid; - } - } - } - - // No split found? - if (best_dim == 0xffffffff) - return false; - - return SplitInternal(inTriangles, best_dim, best_split, outLeft, outRight); -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterBinning.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterBinning.h deleted file mode 100644 index 2cb35c9cc7b..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterBinning.h +++ /dev/null @@ -1,52 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include - -JPH_NAMESPACE_BEGIN - -/// Binning splitter approach taken from: Realtime Ray Tracing on GPU with BVH-based Packet Traversal by Johannes Gunther et al. -class JPH_EXPORT TriangleSplitterBinning : public TriangleSplitter -{ -public: - /// Constructor - TriangleSplitterBinning(const VertexList &inVertices, const IndexedTriangleList &inTriangles, uint inMinNumBins = 8, uint inMaxNumBins = 128, uint inNumTrianglesPerBin = 6); - - // See TriangleSplitter::GetStats - virtual void GetStats(Stats &outStats) const override - { - outStats.mSplitterName = "TriangleSplitterBinning"; - } - - // See TriangleSplitter::Split - virtual bool Split(const Range &inTriangles, Range &outLeft, Range &outRight) override; - -private: - // Configuration - const uint mMinNumBins; - const uint mMaxNumBins; - const uint mNumTrianglesPerBin; - - struct Bin - { - // Properties of this bin - AABox mBounds; - float mMinCentroid; - uint mNumTriangles; - - // Accumulated data from left most / right most bin to current (including this bin) - AABox mBoundsAccumulatedLeft; - AABox mBoundsAccumulatedRight; - uint mNumTrianglesAccumulatedLeft; - uint mNumTrianglesAccumulatedRight; - }; - - // Scratch area to store the bins - Array mBins; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterFixedLeafSize.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterFixedLeafSize.cpp deleted file mode 100644 index 2e8ae7371f8..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterFixedLeafSize.cpp +++ /dev/null @@ -1,170 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include - -JPH_NAMESPACE_BEGIN - -TriangleSplitterFixedLeafSize::TriangleSplitterFixedLeafSize(const VertexList &inVertices, const IndexedTriangleList &inTriangles, uint inLeafSize, uint inMinNumBins, uint inMaxNumBins, uint inNumTrianglesPerBin) : - TriangleSplitter(inVertices, inTriangles), - mLeafSize(inLeafSize), - mMinNumBins(inMinNumBins), - mMaxNumBins(inMaxNumBins), - mNumTrianglesPerBin(inNumTrianglesPerBin) -{ - // Group the triangles - TriangleGrouperClosestCentroid grouper; - grouper.Group(inVertices, inTriangles, mLeafSize, mSortedTriangleIdx); - - // Pad triangles so that we have a multiple of mLeafSize - const uint num_triangles = (uint)inTriangles.size(); - const uint num_groups = (num_triangles + mLeafSize - 1) / mLeafSize; - const uint last_triangle_idx = mSortedTriangleIdx.back(); - for (uint t = num_triangles, t_end = num_groups * mLeafSize; t < t_end; ++t) - mSortedTriangleIdx.push_back(last_triangle_idx); -} - -Vec3 TriangleSplitterFixedLeafSize::GetCentroidForGroup(uint inFirstTriangleInGroup) -{ - JPH_ASSERT(inFirstTriangleInGroup % mLeafSize == 0); - AABox box; - for (uint g = 0; g < mLeafSize; ++g) - box.Encapsulate(mVertices, GetTriangle(inFirstTriangleInGroup + g)); - return box.GetCenter(); -} - -bool TriangleSplitterFixedLeafSize::Split(const Range &inTriangles, Range &outLeft, Range &outRight) -{ - // Cannot split anything smaller than leaf size - JPH_ASSERT(inTriangles.Count() > mLeafSize); - JPH_ASSERT(inTriangles.Count() % mLeafSize == 0); - - // Calculate bounds for this range - AABox centroid_bounds; - for (uint t = inTriangles.mBegin; t < inTriangles.mEnd; t += mLeafSize) - centroid_bounds.Encapsulate(GetCentroidForGroup(t)); - - float best_cp = FLT_MAX; - uint best_dim = 0xffffffff; - float best_split = 0; - - // Bin in all dimensions - uint num_bins = Clamp(inTriangles.Count() / mNumTrianglesPerBin, mMinNumBins, mMaxNumBins); - Array bins(num_bins); - for (uint dim = 0; dim < 3; ++dim) - { - float bounds_min = centroid_bounds.mMin[dim]; - float bounds_size = centroid_bounds.mMax[dim] - bounds_min; - - // Skip axis if too small - if (bounds_size < 1.0e-5f) - continue; - - // Initialize bins - for (uint b = 0; b < num_bins; ++b) - { - Bin &bin = bins[b]; - bin.mBounds.SetEmpty(); - bin.mMinCentroid = bounds_min + bounds_size * (b + 1) / num_bins; - bin.mNumTriangles = 0; - } - - // Bin all triangles - for (uint t = inTriangles.mBegin; t < inTriangles.mEnd; t += mLeafSize) - { - // Calculate average centroid for group - float centroid_pos = GetCentroidForGroup(t)[dim]; - - // Select bin - uint bin_no = min(uint((centroid_pos - bounds_min) / bounds_size * num_bins), num_bins - 1); - Bin &bin = bins[bin_no]; - - // Put all triangles of group in same bin - for (uint g = 0; g < mLeafSize; ++g) - bin.mBounds.Encapsulate(mVertices, GetTriangle(t + g)); - bin.mMinCentroid = min(bin.mMinCentroid, centroid_pos); - bin.mNumTriangles += mLeafSize; - } - - // Calculate totals left to right - AABox prev_bounds; - int prev_triangles = 0; - for (uint b = 0; b < num_bins; ++b) - { - Bin &bin = bins[b]; - bin.mBoundsAccumulatedLeft = prev_bounds; // Don't include this node as we'll take a split on the left side of the bin - bin.mNumTrianglesAccumulatedLeft = prev_triangles; - prev_bounds.Encapsulate(bin.mBounds); - prev_triangles += bin.mNumTriangles; - } - - // Calculate totals right to left - prev_bounds.SetEmpty(); - prev_triangles = 0; - for (int b = num_bins - 1; b >= 0; --b) - { - Bin &bin = bins[b]; - prev_bounds.Encapsulate(bin.mBounds); - prev_triangles += bin.mNumTriangles; - bin.mBoundsAccumulatedRight = prev_bounds; - bin.mNumTrianglesAccumulatedRight = prev_triangles; - } - - // Get best splitting plane - for (uint b = 1; b < num_bins; ++b) // Start at 1 since selecting bin 0 would result in everything ending up on the right side - { - // Calculate surface area heuristic and see if it is better than the current best - const Bin &bin = bins[b]; - float cp = bin.mBoundsAccumulatedLeft.GetSurfaceArea() * bin.mNumTrianglesAccumulatedLeft + bin.mBoundsAccumulatedRight.GetSurfaceArea() * bin.mNumTrianglesAccumulatedRight; - if (cp < best_cp) - { - best_cp = cp; - best_dim = dim; - best_split = bin.mMinCentroid; - } - } - } - - // No split found? - if (best_dim == 0xffffffff) - return false; - - // Divide triangles - uint start = inTriangles.mBegin, end = inTriangles.mEnd; - while (start < end) - { - // Search for first element that is on the right hand side of the split plane - while (start < end && GetCentroidForGroup(start)[best_dim] < best_split) - start += mLeafSize; - - // Search for the first element that is on the left hand side of the split plane - while (start < end && GetCentroidForGroup(end - mLeafSize)[best_dim] >= best_split) - end -= mLeafSize; - - if (start < end) - { - // Swap the two elements - for (uint g = 0; g < mLeafSize; ++g) - swap(mSortedTriangleIdx[start + g], mSortedTriangleIdx[end - mLeafSize + g]); - start += mLeafSize; - end -= mLeafSize; - } - } - JPH_ASSERT(start == end); - - // No suitable split found, doing random split in half - if (start == inTriangles.mBegin || start == inTriangles.mEnd) - start = inTriangles.mBegin + (inTriangles.Count() / mLeafSize + 1) / 2 * mLeafSize; - - outLeft = Range(inTriangles.mBegin, start); - outRight = Range(start, inTriangles.mEnd); - JPH_ASSERT(outLeft.mEnd > outLeft.mBegin && outRight.mEnd > outRight.mBegin); - JPH_ASSERT(outLeft.Count() % mLeafSize == 0 && outRight.Count() % mLeafSize == 0); - return true; -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterFixedLeafSize.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterFixedLeafSize.h deleted file mode 100644 index 029121daea5..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterFixedLeafSize.h +++ /dev/null @@ -1,55 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include - -JPH_NAMESPACE_BEGIN - -/// Same as TriangleSplitterBinning, but ensuring that leaves have a fixed amount of triangles -/// The resulting tree should be suitable for processing on GPU where we want all threads to process an equal amount of triangles -class JPH_EXPORT TriangleSplitterFixedLeafSize : public TriangleSplitter -{ -public: - /// Constructor - TriangleSplitterFixedLeafSize(const VertexList &inVertices, const IndexedTriangleList &inTriangles, uint inLeafSize, uint inMinNumBins = 8, uint inMaxNumBins = 128, uint inNumTrianglesPerBin = 6); - - // See TriangleSplitter::GetStats - virtual void GetStats(Stats &outStats) const override - { - outStats.mSplitterName = "TriangleSplitterFixedLeafSize"; - outStats.mLeafSize = mLeafSize; - } - - // See TriangleSplitter::Split - virtual bool Split(const Range &inTriangles, Range &outLeft, Range &outRight) override; - -private: - /// Get centroid for group - Vec3 GetCentroidForGroup(uint inFirstTriangleInGroup); - - // Configuration - const uint mLeafSize; - const uint mMinNumBins; - const uint mMaxNumBins; - const uint mNumTrianglesPerBin; - - struct Bin - { - // Properties of this bin - AABox mBounds; - float mMinCentroid; - uint mNumTriangles; - - // Accumulated data from left most / right most bin to current (including this bin) - AABox mBoundsAccumulatedLeft; - AABox mBoundsAccumulatedRight; - uint mNumTrianglesAccumulatedLeft; - uint mNumTrianglesAccumulatedRight; - }; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterLongestAxis.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterLongestAxis.cpp deleted file mode 100644 index f8115ab8999..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterLongestAxis.cpp +++ /dev/null @@ -1,31 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include - -JPH_NAMESPACE_BEGIN - -TriangleSplitterLongestAxis::TriangleSplitterLongestAxis(const VertexList &inVertices, const IndexedTriangleList &inTriangles) : - TriangleSplitter(inVertices, inTriangles) -{ -} - -bool TriangleSplitterLongestAxis::Split(const Range &inTriangles, Range &outLeft, Range &outRight) -{ - // Calculate bounding box for triangles - AABox bounds; - for (uint t = inTriangles.mBegin; t < inTriangles.mEnd; ++t) - bounds.Encapsulate(mVertices, GetTriangle(t)); - - // Calculate split plane - uint dimension = bounds.GetExtent().GetHighestComponentIndex(); - float split = bounds.GetCenter()[dimension]; - - return SplitInternal(inTriangles, dimension, split, outLeft, outRight); -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterLongestAxis.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterLongestAxis.h deleted file mode 100644 index daf0d437019..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterLongestAxis.h +++ /dev/null @@ -1,28 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -/// Splitter using center of bounding box with longest axis -class JPH_EXPORT TriangleSplitterLongestAxis : public TriangleSplitter -{ -public: - /// Constructor - TriangleSplitterLongestAxis(const VertexList &inVertices, const IndexedTriangleList &inTriangles); - - // See TriangleSplitter::GetStats - virtual void GetStats(Stats &outStats) const override - { - outStats.mSplitterName = "TriangleSplitterLongestAxis"; - } - - // See TriangleSplitter::Split - virtual bool Split(const Range &inTriangles, Range &outLeft, Range &outRight) override; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterMean.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterMean.cpp deleted file mode 100644 index e884246fea5..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterMean.cpp +++ /dev/null @@ -1,40 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include - -JPH_NAMESPACE_BEGIN - -TriangleSplitterMean::TriangleSplitterMean(const VertexList &inVertices, const IndexedTriangleList &inTriangles) : - TriangleSplitter(inVertices, inTriangles) -{ -} - -bool TriangleSplitterMean::Split(const Range &inTriangles, Range &outLeft, Range &outRight) -{ - // Calculate mean value for these triangles - Vec3 mean = Vec3::sZero(); - for (uint t = inTriangles.mBegin; t < inTriangles.mEnd; ++t) - mean += Vec3(mCentroids[mSortedTriangleIdx[t]]); - mean *= 1.0f / inTriangles.Count(); - - // Calculate deviation - Vec3 deviation = Vec3::sZero(); - for (uint t = inTriangles.mBegin; t < inTriangles.mEnd; ++t) - { - Vec3 delta = Vec3(mCentroids[mSortedTriangleIdx[t]]) - mean; - deviation += delta * delta; - } - deviation *= 1.0f / inTriangles.Count(); - - // Calculate split plane - uint dimension = deviation.GetHighestComponentIndex(); - float split = mean[dimension]; - - return SplitInternal(inTriangles, dimension, split, outLeft, outRight); -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterMean.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterMean.h deleted file mode 100644 index 737d76e1c1f..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterMean.h +++ /dev/null @@ -1,28 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -/// Splitter using mean of axis with biggest centroid deviation -class JPH_EXPORT TriangleSplitterMean : public TriangleSplitter -{ -public: - /// Constructor - TriangleSplitterMean(const VertexList &inVertices, const IndexedTriangleList &inTriangles); - - // See TriangleSplitter::GetStats - virtual void GetStats(Stats &outStats) const override - { - outStats.mSplitterName = "TriangleSplitterMean"; - } - - // See TriangleSplitter::Split - virtual bool Split(const Range &inTriangles, Range &outLeft, Range &outRight) override; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterMorton.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterMorton.cpp deleted file mode 100644 index 35b0f4212b7..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterMorton.cpp +++ /dev/null @@ -1,63 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include - -JPH_NAMESPACE_BEGIN - -TriangleSplitterMorton::TriangleSplitterMorton(const VertexList &inVertices, const IndexedTriangleList &inTriangles) : - TriangleSplitter(inVertices, inTriangles) -{ - // Calculate bounds of centroids - AABox bounds; - for (uint t = 0; t < inTriangles.size(); ++t) - bounds.Encapsulate(Vec3(mCentroids[t])); - - // Make sure box is not degenerate - bounds.EnsureMinimalEdgeLength(1.0e-5f); - - // Calculate morton codes - mMortonCodes.resize(inTriangles.size()); - for (uint t = 0; t < inTriangles.size(); ++t) - mMortonCodes[t] = MortonCode::sGetMortonCode(Vec3(mCentroids[t]), bounds); - - // Sort triangles on morton code - const Array &morton_codes = mMortonCodes; - QuickSort(mSortedTriangleIdx.begin(), mSortedTriangleIdx.end(), [&morton_codes](uint inLHS, uint inRHS) { return morton_codes[inLHS] < morton_codes[inRHS]; }); -} - -bool TriangleSplitterMorton::Split(const Range &inTriangles, Range &outLeft, Range &outRight) -{ - uint32 first_code = mMortonCodes[mSortedTriangleIdx[inTriangles.mBegin]]; - uint32 last_code = mMortonCodes[mSortedTriangleIdx[inTriangles.mEnd - 1]]; - - uint common_prefix = CountLeadingZeros(first_code ^ last_code); - - // Use binary search to find where the next bit differs - uint split = inTriangles.mBegin; // Initial guess - uint step = inTriangles.Count(); - do - { - step = (step + 1) >> 1; // Exponential decrease - uint new_split = split + step; // Proposed new position - if (new_split < inTriangles.mEnd) - { - uint32 split_code = mMortonCodes[mSortedTriangleIdx[new_split]]; - uint split_prefix = CountLeadingZeros(first_code ^ split_code); - if (split_prefix > common_prefix) - split = new_split; // Accept proposal - } - } - while (step > 1); - - outLeft = Range(inTriangles.mBegin, split + 1); - outRight = Range(split + 1, inTriangles.mEnd); - return outLeft.Count() > 0 && outRight.Count() > 0; -} - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterMorton.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterMorton.h deleted file mode 100644 index 2f48c0ea96c..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Jolt/TriangleSplitter/TriangleSplitterMorton.h +++ /dev/null @@ -1,32 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -JPH_NAMESPACE_BEGIN - -/// Splitter using Morton codes, see: http://devblogs.nvidia.com/parallelforall/thinking-parallel-part-iii-tree-construction-gpu/ -class JPH_EXPORT TriangleSplitterMorton : public TriangleSplitter -{ -public: - /// Constructor - TriangleSplitterMorton(const VertexList &inVertices, const IndexedTriangleList &inTriangles); - - // See TriangleSplitter::GetStats - virtual void GetStats(Stats &outStats) const override - { - outStats.mSplitterName = "TriangleSplitterMorton"; - } - - // See TriangleSplitter::Split - virtual bool Split(const Range &inTriangles, Range &outLeft, Range &outRight) override; - -private: - // Precalculated Morton codes - Array mMortonCodes; -}; - -JPH_NAMESPACE_END diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/JoltViewer/JoltViewer.cmake b/crates/joltc-sys-patched/JoltC/JoltPhysics/JoltViewer/JoltViewer.cmake deleted file mode 100644 index 8ba517a47f4..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/JoltViewer/JoltViewer.cmake +++ /dev/null @@ -1,20 +0,0 @@ -# Root -set(JOLT_VIEWER_ROOT ${PHYSICS_REPO_ROOT}/JoltViewer) - -# Source files -set(JOLT_VIEWER_SRC_FILES - ${JOLT_VIEWER_ROOT}/JoltViewer.cmake - ${JOLT_VIEWER_ROOT}/JoltViewer.cpp - ${JOLT_VIEWER_ROOT}/JoltViewer.h -) - -# Group source files -source_group(TREE ${JOLT_VIEWER_ROOT} FILES ${JOLT_VIEWER_SRC_FILES}) - -# Create JoltViewer executable -add_executable(JoltViewer ${JOLT_VIEWER_SRC_FILES}) -target_include_directories(JoltViewer PUBLIC ${JOLT_VIEWER_ROOT}) -target_link_libraries(JoltViewer LINK_PUBLIC TestFramework d3d12.lib shcore.lib) - -# Set the correct working directory -set_property(TARGET JoltViewer PROPERTY VS_DEBUGGER_WORKING_DIRECTORY "${PHYSICS_REPO_ROOT}") diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/JoltViewer/JoltViewer.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/JoltViewer/JoltViewer.cpp deleted file mode 100644 index b60bac7be5a..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/JoltViewer/JoltViewer.cpp +++ /dev/null @@ -1,154 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#include - -#include -#include -#include -#include -#include -#include - -JPH_SUPPRESS_WARNINGS_STD_BEGIN -#include -JPH_SUPPRESS_WARNINGS_STD_END - -#ifndef JPH_DEBUG_RENDERER - // Hack to still compile DebugRenderer inside the test framework when Jolt is compiled without - #define JPH_DEBUG_RENDERER - #include - #include - #undef JPH_DEBUG_RENDERER -#endif - -JoltViewer::JoltViewer() -{ - // Get file name from commandline - String cmd_line = GetCommandLineA(); - Array args; - StringToVector(cmd_line, args, " "); - - // Check arguments - if (args.size() != 2 || args[1].empty()) - { - MessageBoxA(nullptr, "Usage: JoltViewer ", "Error", MB_OK); - return; - } - - // Open file - ifstream stream(args[1].c_str(), ifstream::in | ifstream::binary); - if (!stream.is_open()) - { - MessageBoxA(nullptr, "Could not open recording file", "Error", MB_OK); - return; - } - - // Parse the stream - StreamInWrapper wrapper(stream); - mRendererPlayback.Parse(wrapper); - if (mRendererPlayback.GetNumFrames() == 0) - { - MessageBoxA(nullptr, "Recording file did not contain any frames", "Error", MB_OK); - return; - } - - // Draw the first frame - mRendererPlayback.DrawFrame(0); - - // Start paused - Pause(true); - - // Create UI - UIElement *main_menu = mDebugUI->CreateMenu(); - mDebugUI->CreateTextButton(main_menu, "Help", [this](){ - UIElement *help = mDebugUI->CreateMenu(); - mDebugUI->CreateStaticText(help, - "ESC: Back to previous menu.\n" - "WASD + Mouse: Fly around. Hold Shift to speed up, Ctrl to slow down.\n" - "P: Pause / unpause simulation.\n" - "O: Single step simulation.\n" - ",: Step back.\n" - ".: Step forward.\n" - "Shift + ,: Play reverse.\n" - "Shift + .: Replay forward." - ); - mDebugUI->ShowMenu(help); - }); - mDebugUI->ShowMenu(main_menu); -} - -bool JoltViewer::UpdateFrame(float inDeltaTime) -{ - // If no frames were read, abort - if (mRendererPlayback.GetNumFrames() == 0) - return false; - - // Handle keyboard input - bool shift = mKeyboard->IsKeyPressed(DIK_LSHIFT) || mKeyboard->IsKeyPressed(DIK_RSHIFT); - for (int key = mKeyboard->GetFirstKey(); key != 0; key = mKeyboard->GetNextKey()) - switch (key) - { - case DIK_R: - // Restart - mCurrentFrame = 0; - mPlaybackMode = EPlaybackMode::Play; - Pause(true); - break; - - case DIK_O: - // Step - mPlaybackMode = EPlaybackMode::Play; - SingleStep(); - break; - - case DIK_COMMA: - // Back - mPlaybackMode = shift? EPlaybackMode::Rewind : EPlaybackMode::StepBack; - Pause(false); - break; - - case DIK_PERIOD: - // Forward - mPlaybackMode = shift? EPlaybackMode::Play : EPlaybackMode::StepForward; - Pause(false); - break; - } - - // If paused, do nothing - if (inDeltaTime > 0.0f) - { - // Determine new frame number - switch (mPlaybackMode) - { - case EPlaybackMode::StepForward: - mPlaybackMode = EPlaybackMode::Stop; - [[fallthrough]]; - - case EPlaybackMode::Play: - if (mCurrentFrame + 1 < mRendererPlayback.GetNumFrames()) - ++mCurrentFrame; - break; - - case EPlaybackMode::StepBack: - mPlaybackMode = EPlaybackMode::Stop; - [[fallthrough]]; - - case EPlaybackMode::Rewind: - if (mCurrentFrame > 0) - --mCurrentFrame; - break; - - case EPlaybackMode::Stop: - break; - } - - // Render the frame - mRendererPlayback.DrawFrame(mCurrentFrame); - } - - return true; -} - -ENTRY_POINT(JoltViewer, RegisterDefaultAllocator) diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/JoltViewer/JoltViewer.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/JoltViewer/JoltViewer.h deleted file mode 100644 index 87f8a87e732..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/JoltViewer/JoltViewer.h +++ /dev/null @@ -1,46 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#ifdef JPH_DEBUG_RENDERER - #include -#else - // Hack to still compile DebugRenderer inside the test framework when Jolt is compiled without - #define JPH_DEBUG_RENDERER - // Make sure the debug renderer symbols don't get imported or exported - #define JPH_DEBUG_RENDERER_EXPORT - #include - #undef JPH_DEBUG_RENDERER - #undef JPH_DEBUG_RENDERER_EXPORT -#endif - -using namespace std; - -// Application that views recordings produced by DebugRendererRecorder -class JoltViewer : public Application -{ -public: - // Constructor / destructor - JoltViewer(); - - // Update the application - virtual bool UpdateFrame(float inDeltaTime) override; - -private: - enum class EPlaybackMode - { - Rewind, - StepBack, - Stop, - StepForward, - Play - }; - - DebugRendererPlayback mRendererPlayback { *mDebugRenderer }; - - EPlaybackMode mPlaybackMode = EPlaybackMode::Play; // Current playback state. Indicates if we're playing or scrubbing back/forward. - uint mCurrentFrame = 0; -}; diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/LICENSE b/crates/joltc-sys-patched/JoltC/JoltPhysics/LICENSE deleted file mode 100644 index 4f097684852..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/LICENSE +++ /dev/null @@ -1,7 +0,0 @@ -Copyright 2021 Jorrit Rouwe - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/PerformanceTest/ConvexVsMeshScene.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/PerformanceTest/ConvexVsMeshScene.h deleted file mode 100644 index f4c0857b3df..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/PerformanceTest/ConvexVsMeshScene.h +++ /dev/null @@ -1,122 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -// Jolt includes -#include -#include -#include -#include -#include -#include - -// Local includes -#include "PerformanceTestScene.h" -#include "Layers.h" - -// A scene that drops a number of convex shapes on a sloping terrain made out of a mesh shape -class ConvexVsMeshScene : public PerformanceTestScene -{ -public: - virtual const char * GetName() const override - { - return "ConvexVsMesh"; - } - - virtual bool Load() override - { - const int n = 100; - const float cell_size = 3.0f; - const float max_height = 5.0f; - float center = n * cell_size / 2; - - // Create vertices - const int num_vertices = (n + 1) * (n + 1); - VertexList vertices; - vertices.resize(num_vertices); - for (int x = 0; x <= n; ++x) - for (int z = 0; z <= n; ++z) - { - float height = Sin(float(x) * 50.0f / n) * Cos(float(z) * 50.0f / n); - vertices[z * (n + 1) + x] = Float3(cell_size * x, max_height * height, cell_size * z); - } - - // Create regular grid of triangles - const int num_triangles = n * n * 2; - IndexedTriangleList indices; - indices.resize(num_triangles); - IndexedTriangle *next = indices.data(); - for (int x = 0; x < n; ++x) - for (int z = 0; z < n; ++z) - { - int start = (n + 1) * z + x; - - next->mIdx[0] = start; - next->mIdx[1] = start + n + 1; - next->mIdx[2] = start + 1; - next++; - - next->mIdx[0] = start + 1; - next->mIdx[1] = start + n + 1; - next->mIdx[2] = start + n + 2; - next++; - } - - // Create mesh shape settings - Ref mesh_shape_settings = new MeshShapeSettings(vertices, indices); - mesh_shape_settings->mMaxTrianglesPerLeaf = 4; - - // Create mesh shape creation settings - mMeshSettings.mMotionType = EMotionType::Static; - mMeshSettings.mObjectLayer = Layers::NON_MOVING; - mMeshSettings.mPosition = RVec3(Real(-center), Real(max_height), Real(-center)); - mMeshSettings.mFriction = 0.5f; - mMeshSettings.mRestitution = 0.6f; - mMeshSettings.SetShapeSettings(mesh_shape_settings); - - // Create other shapes - mShapes = { - new BoxShape(Vec3(0.5f, 0.75f, 1.0f)), - new SphereShape(0.5f), - new CapsuleShape(0.75f, 0.5f), - ConvexHullShapeSettings({ Vec3(0, 1, 0), Vec3(1, 0, 0), Vec3(-1, 0, 0), Vec3(0, 0, 1), Vec3(0, 0, -1) }).Create().Get(), - }; - - return true; - } - - virtual void StartTest(PhysicsSystem &inPhysicsSystem, EMotionQuality inMotionQuality) override - { - // Reduce the solver iteration count, the scene doesn't have any constraints so we don't need the default amount of iterations - PhysicsSettings settings = inPhysicsSystem.GetPhysicsSettings(); - settings.mNumVelocitySteps = 4; - settings.mNumPositionSteps = 1; - inPhysicsSystem.SetPhysicsSettings(settings); - - // Create background - BodyInterface &bi = inPhysicsSystem.GetBodyInterface(); - bi.CreateAndAddBody(mMeshSettings, EActivation::DontActivate); - - // Construct bodies - for (int x = -10; x <= 10; ++x) - for (int y = 0; y < (int)mShapes.size(); ++y) - for (int z = -10; z <= 10; ++z) - { - BodyCreationSettings creation_settings; - creation_settings.mMotionType = EMotionType::Dynamic; - creation_settings.mMotionQuality = inMotionQuality; - creation_settings.mObjectLayer = Layers::MOVING; - creation_settings.mPosition = RVec3(7.5_r * x, 15.0_r + 2.0_r * y, 7.5_r * z); - creation_settings.mFriction = 0.5f; - creation_settings.mRestitution = 0.6f; - creation_settings.SetShape(mShapes[y]); - bi.CreateAndAddBody(creation_settings, EActivation::Activate); - } - } - -private: - BodyCreationSettings mMeshSettings; - Array> mShapes; -}; diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/PerformanceTest/Layers.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/PerformanceTest/Layers.h deleted file mode 100644 index 68dfb8ada09..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/PerformanceTest/Layers.h +++ /dev/null @@ -1,100 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include - -/// Layer that objects can be in, determines which other objects it can collide with -namespace Layers -{ - static constexpr ObjectLayer NON_MOVING = 0; - static constexpr ObjectLayer MOVING = 1; - static constexpr ObjectLayer NUM_LAYERS = 2; -}; - -/// Class that determines if two object layers can collide -class ObjectLayerPairFilterImpl : public ObjectLayerPairFilter -{ -public: - virtual bool ShouldCollide(ObjectLayer inObject1, ObjectLayer inObject2) const override - { - switch (inObject1) - { - case Layers::NON_MOVING: - return inObject2 == Layers::MOVING; // Non moving only collides with moving - case Layers::MOVING: - return true; // Moving collides with everything - default: - JPH_ASSERT(false); - return false; - } - } -}; - -/// Broadphase layers -namespace BroadPhaseLayers -{ - static constexpr BroadPhaseLayer NON_MOVING(0); - static constexpr BroadPhaseLayer MOVING(1); - static constexpr uint NUM_LAYERS(2); -}; - -/// BroadPhaseLayerInterface implementation -class BPLayerInterfaceImpl final : public BroadPhaseLayerInterface -{ -public: - BPLayerInterfaceImpl() - { - // Create a mapping table from object to broad phase layer - mObjectToBroadPhase[Layers::NON_MOVING] = BroadPhaseLayers::NON_MOVING; - mObjectToBroadPhase[Layers::MOVING] = BroadPhaseLayers::MOVING; - } - - virtual uint GetNumBroadPhaseLayers() const override - { - return BroadPhaseLayers::NUM_LAYERS; - } - - virtual BroadPhaseLayer GetBroadPhaseLayer(ObjectLayer inLayer) const override - { - JPH_ASSERT(inLayer < Layers::NUM_LAYERS); - return mObjectToBroadPhase[inLayer]; - } - -#if defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED) - virtual const char * GetBroadPhaseLayerName(BroadPhaseLayer inLayer) const override - { - switch ((BroadPhaseLayer::Type)inLayer) - { - case (BroadPhaseLayer::Type)BroadPhaseLayers::NON_MOVING: return "NON_MOVING"; - case (BroadPhaseLayer::Type)BroadPhaseLayers::MOVING: return "MOVING"; - default: JPH_ASSERT(false); return "INVALID"; - } - } -#endif // JPH_EXTERNAL_PROFILE || JPH_PROFILE_ENABLED - -private: - BroadPhaseLayer mObjectToBroadPhase[Layers::NUM_LAYERS]; -}; - -/// Class that determines if an object layer can collide with a broadphase layer -class ObjectVsBroadPhaseLayerFilterImpl : public ObjectVsBroadPhaseLayerFilter -{ -public: - virtual bool ShouldCollide(ObjectLayer inLayer1, BroadPhaseLayer inLayer2) const override - { - switch (inLayer1) - { - case Layers::NON_MOVING: - return inLayer2 == BroadPhaseLayers::MOVING; - case Layers::MOVING: - return true; - default: - JPH_ASSERT(false); - return false; - } - } -}; diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/PerformanceTest/PerformanceTest.cmake b/crates/joltc-sys-patched/JoltC/JoltPhysics/PerformanceTest/PerformanceTest.cmake deleted file mode 100644 index 29cec49f9ee..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/PerformanceTest/PerformanceTest.cmake +++ /dev/null @@ -1,16 +0,0 @@ -# Root -set(PERFORMANCE_TEST_ROOT ${PHYSICS_REPO_ROOT}/PerformanceTest) - -# Source files -set(PERFORMANCE_TEST_SRC_FILES - ${PERFORMANCE_TEST_ROOT}/PyramidScene.h - ${PERFORMANCE_TEST_ROOT}/PerformanceTest.cpp - ${PERFORMANCE_TEST_ROOT}/PerformanceTest.cmake - ${PERFORMANCE_TEST_ROOT}/PerformanceTestScene.h - ${PERFORMANCE_TEST_ROOT}/RagdollScene.h - ${PERFORMANCE_TEST_ROOT}/ConvexVsMeshScene.h - ${PERFORMANCE_TEST_ROOT}/Layers.h -) - -# Group source files -source_group(TREE ${PERFORMANCE_TEST_ROOT} FILES ${PERFORMANCE_TEST_SRC_FILES}) diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/PerformanceTest/PerformanceTest.cpp b/crates/joltc-sys-patched/JoltC/JoltPhysics/PerformanceTest/PerformanceTest.cpp deleted file mode 100644 index de15ca25bea..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/PerformanceTest/PerformanceTest.cpp +++ /dev/null @@ -1,464 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -// Jolt includes -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef JPH_DEBUG_RENDERER - #include - #include -#endif // JPH_DEBUG_RENDERER -#ifdef JPH_PLATFORM_ANDROID -#include -#include -#endif // JPH_PLATFORM_ANDROID - -// STL includes -JPH_SUPPRESS_WARNINGS_STD_BEGIN -#include -#include -#include -#include -#include -JPH_SUPPRESS_WARNINGS_STD_END - -using namespace JPH; -using namespace JPH::literals; -using namespace std; - -// Disable common warnings triggered by Jolt -JPH_SUPPRESS_WARNINGS - -// Local includes -#include "RagdollScene.h" -#include "ConvexVsMeshScene.h" -#include "PyramidScene.h" - -// Time step for physics -constexpr float cDeltaTime = 1.0f / 60.0f; - -static void TraceImpl(const char *inFMT, ...) -{ - // Format the message - va_list list; - va_start(list, inFMT); - char buffer[1024]; - vsnprintf(buffer, sizeof(buffer), inFMT, list); - va_end(list); - - // Print to the TTY -#ifndef JPH_PLATFORM_ANDROID - cout << buffer << endl; -#else - __android_log_write(ANDROID_LOG_INFO, "Jolt", buffer); -#endif -} - -// Program entry point -int main(int argc, char** argv) -{ - // Install callbacks - Trace = TraceImpl; - - // Register allocation hook - RegisterDefaultAllocator(); - - // Helper function that creates the default scene - auto create_ragdoll_scene = []{ return unique_ptr(new RagdollScene(4, 10, 0.6f)); }; - - // Parse command line parameters - int specified_quality = -1; - int specified_threads = -1; - uint max_iterations = 500; - bool disable_sleep = false; - bool enable_profiler = false; -#ifdef JPH_DEBUG_RENDERER - bool enable_debug_renderer = false; -#endif // JPH_DEBUG_RENDERER - bool enable_per_frame_recording = false; - bool record_state = false; - bool validate_state = false; - unique_ptr scene; - const char *validate_hash = nullptr; - int repeat = 1; - for (int argidx = 1; argidx < argc; ++argidx) - { - const char *arg = argv[argidx]; - - if (strncmp(arg, "-s=", 3) == 0) - { - // Parse scene - if (strcmp(arg + 3, "Ragdoll") == 0) - scene = create_ragdoll_scene(); - else if (strcmp(arg + 3, "RagdollSinglePile") == 0) - scene = unique_ptr(new RagdollScene(1, 160, 0.4f)); - else if (strcmp(arg + 3, "ConvexVsMesh") == 0) - scene = unique_ptr(new ConvexVsMeshScene); - else if (strcmp(arg + 3, "Pyramid") == 0) - scene = unique_ptr(new PyramidScene); - else - { - Trace("Invalid scene"); - return 1; - } - } - else if (strncmp(arg, "-i=", 3) == 0) - { - // Parse max iterations - max_iterations = (uint)atoi(arg + 3); - } - else if (strncmp(arg, "-q=", 3) == 0) - { - // Parse quality - if (strcmp(arg + 3, "Discrete") == 0) - specified_quality = 0; - else if (strcmp(arg + 3, "LinearCast") == 0) - specified_quality = 1; - else - { - Trace("Invalid quality"); - return 1; - } - } - else if (strncmp(arg, "-t=max", 6) == 0) - { - // Default to number of threads on the system - specified_threads = thread::hardware_concurrency(); - } - else if (strncmp(arg, "-t=", 3) == 0) - { - // Parse threads - specified_threads = atoi(arg + 3); - } - else if (strcmp(arg, "-no_sleep") == 0) - { - disable_sleep = true; - } - else if (strcmp(arg, "-p") == 0) - { - enable_profiler = true; - } - #ifdef JPH_DEBUG_RENDERER - else if (strcmp(arg, "-r") == 0) - { - enable_debug_renderer = true; - } - #endif // JPH_DEBUG_RENDERER - else if (strcmp(arg, "-f") == 0) - { - enable_per_frame_recording = true; - } - else if (strcmp(arg, "-rs") == 0) - { - record_state = true; - } - else if (strcmp(arg, "-vs") == 0) - { - validate_state = true; - } - else if (strncmp(arg, "-validate_hash=", 15) == 0) - { - validate_hash = arg + 15; - } - else if (strncmp(arg, "-repeat=", 8) == 0) - { - // Parse repeat count - repeat = atoi(arg + 8); - } - else if (strcmp(arg, "-h") == 0) - { - // Print usage - Trace("Usage:\n" - "-s=: Select scene (Ragdoll, RagdollSinglePile, ConvexVsMesh, Pyramid)\n" - "-i=: Number of physics steps to simulate (default 500)\n" - "-q=: Test only with specified quality (Discrete, LinearCast)\n" - "-t=: Test only with N threads (default is to iterate over 1 .. num hardware threads)\n" - "-t=max: Test with the number of threads available on the system\n" - "-p: Write out profiles\n" - "-r: Record debug renderer output for JoltViewer\n" - "-f: Record per frame timings\n" - "-no_sleep: Disable sleeping\n" - "-rs: Record state\n" - "-vs: Validate state\n" - "-validate_hash=: Validate hash (return 0 if successful, 1 if failed)\n" - "-repeat=: Repeat all tests times"); - return 0; - } - } - - // Create a factory - Factory::sInstance = new Factory(); - - // Register all Jolt physics types - RegisterTypes(); - - // Create temp allocator - TempAllocatorImpl temp_allocator(32 * 1024 * 1024); - - // Load the scene - if (scene == nullptr) - scene = create_ragdoll_scene(); - if (!scene->Load()) - return 1; - - // Show used instruction sets - Trace(GetConfigurationString()); - - // Output scene we're running - Trace("Running scene: %s", scene->GetName()); - - // Create mapping table from object layer to broadphase layer - BPLayerInterfaceImpl broad_phase_layer_interface; - - // Create class that filters object vs broadphase layers - ObjectVsBroadPhaseLayerFilterImpl object_vs_broadphase_layer_filter; - - // Create class that filters object vs object layers - ObjectLayerPairFilterImpl object_vs_object_layer_filter; - - // Start profiling this program - JPH_PROFILE_START("Main"); - - // Trace header - Trace("Motion Quality, Thread Count, Steps / Second, Hash"); - - // Repeat test - for (int r = 0; r < repeat; ++r) - { - // Iterate motion qualities - for (uint mq = 0; mq < 2; ++mq) - { - // Skip quality if another was specified - if (specified_quality != -1 && mq != (uint)specified_quality) - continue; - - // Determine motion quality - EMotionQuality motion_quality = mq == 0? EMotionQuality::Discrete : EMotionQuality::LinearCast; - String motion_quality_str = mq == 0? "Discrete" : "LinearCast"; - - // Determine which thread counts to test - Array thread_permutations; - if (specified_threads > 0) - thread_permutations.push_back((uint)specified_threads - 1); - else - for (uint num_threads = 0; num_threads < thread::hardware_concurrency(); ++num_threads) - thread_permutations.push_back(num_threads); - - // Test thread permutations - for (uint num_threads : thread_permutations) - { - // Create job system with desired number of threads - JobSystemThreadPool job_system(cMaxPhysicsJobs, cMaxPhysicsBarriers, num_threads); - - // Create physics system - PhysicsSystem physics_system; - physics_system.Init(10240, 0, 65536, 20480, broad_phase_layer_interface, object_vs_broadphase_layer_filter, object_vs_object_layer_filter); - - // Start test scene - scene->StartTest(physics_system, motion_quality); - - // Disable sleeping if requested - if (disable_sleep) - { - const BodyLockInterface &bli = physics_system.GetBodyLockInterfaceNoLock(); - BodyIDVector body_ids; - physics_system.GetBodies(body_ids); - for (BodyID id : body_ids) - { - BodyLockWrite lock(bli, id); - if (lock.Succeeded()) - { - Body &body = lock.GetBody(); - if (!body.IsStatic()) - body.SetAllowSleeping(false); - } - } - } - - // Optimize the broadphase to prevent an expensive first frame - physics_system.OptimizeBroadPhase(); - - // A tag used to identify the test - String tag = ToLower(motion_quality_str) + "_th" + ConvertToString(num_threads + 1); - - #ifdef JPH_DEBUG_RENDERER - // Open renderer output - ofstream renderer_file; - if (enable_debug_renderer) - renderer_file.open(("performance_test_" + tag + ".jor").c_str(), ofstream::out | ofstream::binary | ofstream::trunc); - StreamOutWrapper renderer_stream(renderer_file); - DebugRendererRecorder renderer(renderer_stream); - #endif // JPH_DEBUG_RENDERER - - // Open per frame timing output - ofstream per_frame_file; - if (enable_per_frame_recording) - { - per_frame_file.open(("per_frame_" + tag + ".csv").c_str(), ofstream::out | ofstream::trunc); - per_frame_file << "Frame, Time (ms)" << endl; - } - - ofstream record_state_file; - ifstream validate_state_file; - if (record_state) - record_state_file.open(("state_" + ToLower(motion_quality_str) + ".bin").c_str(), ofstream::out | ofstream::binary | ofstream::trunc); - else if (validate_state) - validate_state_file.open(("state_" + ToLower(motion_quality_str) + ".bin").c_str(), ifstream::in | ifstream::binary); - - chrono::nanoseconds total_duration(0); - - // Step the world for a fixed amount of iterations - for (uint iterations = 0; iterations < max_iterations; ++iterations) - { - JPH_PROFILE_NEXTFRAME(); - JPH_DET_LOG("Iteration: " << iterations); - - // Start measuring - chrono::high_resolution_clock::time_point clock_start = chrono::high_resolution_clock::now(); - - // Do a physics step - physics_system.Update(cDeltaTime, 1, &temp_allocator, &job_system); - - // Stop measuring - chrono::high_resolution_clock::time_point clock_end = chrono::high_resolution_clock::now(); - chrono::nanoseconds duration = chrono::duration_cast(clock_end - clock_start); - total_duration += duration; - - #ifdef JPH_DEBUG_RENDERER - if (enable_debug_renderer) - { - // Draw the state of the world - BodyManager::DrawSettings settings; - physics_system.DrawBodies(settings, &renderer); - - // Mark end of frame - renderer.EndFrame(); - } - #endif // JPH_DEBUG_RENDERER - - // Record time taken this iteration - if (enable_per_frame_recording) - per_frame_file << iterations << ", " << (1.0e-6 * duration.count()) << endl; - - // Dump profile information every 100 iterations - if (enable_profiler && iterations % 100 == 0) - { - JPH_PROFILE_DUMP(tag + "_it" + ConvertToString(iterations)); - } - - if (record_state) - { - // Record state - StateRecorderImpl recorder; - physics_system.SaveState(recorder); - - // Write to file - string data = recorder.GetData(); - size_t size = data.size(); - record_state_file.write((char *)&size, sizeof(size)); - record_state_file.write(data.data(), size); - } - else if (validate_state) - { - // Read state - size_t size = 0; - validate_state_file.read((char *)&size, sizeof(size)); - string data; - data.resize(size); - validate_state_file.read(data.data(), size); - - // Copy to validator - StateRecorderImpl validator; - validator.WriteBytes(data.data(), size); - - // Validate state - validator.SetValidating(true); - physics_system.RestoreState(validator); - } - - #ifdef JPH_ENABLE_DETERMINISM_LOG - const BodyLockInterface &bli = physics_system.GetBodyLockInterfaceNoLock(); - BodyIDVector body_ids; - physics_system.GetBodies(body_ids); - for (BodyID id : body_ids) - { - BodyLockRead lock(bli, id); - const Body &body = lock.GetBody(); - if (!body.IsStatic()) - JPH_DET_LOG(id << ": p: " << body.GetPosition() << " r: " << body.GetRotation() << " v: " << body.GetLinearVelocity() << " w: " << body.GetAngularVelocity()); - } - #endif // JPH_ENABLE_DETERMINISM_LOG - } - - // Calculate hash of all positions and rotations of the bodies - uint64 hash = HashBytes(nullptr, 0); // Ensure we start with the proper seed - BodyInterface &bi = physics_system.GetBodyInterfaceNoLock(); - BodyIDVector body_ids; - physics_system.GetBodies(body_ids); - for (BodyID id : body_ids) - { - RVec3 pos = bi.GetPosition(id); - hash = HashBytes(&pos, 3 * sizeof(Real), hash); - Quat rot = bi.GetRotation(id); - hash = HashBytes(&rot, sizeof(Quat), hash); - } - - // Convert hash to string - stringstream hash_stream; - hash_stream << "0x" << hex << hash << dec; - string hash_str = hash_stream.str(); - - // Stop test scene - scene->StopTest(physics_system); - - // Trace stat line - Trace("%s, %d, %f, %s", motion_quality_str.c_str(), num_threads + 1, double(max_iterations) / (1.0e-9 * total_duration.count()), hash_str.c_str()); - - // Check hash code - if (validate_hash != nullptr && hash_str != validate_hash) - { - Trace("Fail hash validation. Was: %s, expected: %s", hash_str.c_str(), validate_hash); - return 1; - } - } - } - } - -#ifdef JPH_TRACK_NARROWPHASE_STATS - NarrowPhaseStat::sReportStats(); -#endif // JPH_TRACK_NARROWPHASE_STATS - - // Unregisters all types with the factory and cleans up the default material - UnregisterTypes(); - - // Destroy the factory - delete Factory::sInstance; - Factory::sInstance = nullptr; - - // End profiling this program - JPH_PROFILE_END(); - - return 0; -} - -#ifdef JPH_PLATFORM_ANDROID - -// Main entry point for android -void android_main(struct android_app *ioApp) -{ - // Run the regular main function - const char *args[] = { "Unused", "-s=ConvexVsMesh", "-t=max" }; - main(size(args), (char **)args); -} - -#endif // JPH_PLATFORM_ANDROID diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/PerformanceTest/PerformanceTestScene.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/PerformanceTest/PerformanceTestScene.h deleted file mode 100644 index b912a4927b9..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/PerformanceTest/PerformanceTestScene.h +++ /dev/null @@ -1,25 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -// Base class for a test scene to test performance -class PerformanceTestScene -{ -public: - // Virtual destructor - virtual ~PerformanceTestScene() { } - - // Get name of test for debug purposes - virtual const char * GetName() const = 0; - - // Load assets for the scene - virtual bool Load() { return true; } - - // Start a new test by adding objects to inPhysicsSystem - virtual void StartTest(PhysicsSystem &inPhysicsSystem, EMotionQuality inMotionQuality) = 0; - - // Stop a test and remove objects from inPhysicsSystem - virtual void StopTest(PhysicsSystem &inPhysicsSystem) { } -}; diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/PerformanceTest/PyramidScene.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/PerformanceTest/PyramidScene.h deleted file mode 100644 index 469b902031f..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/PerformanceTest/PyramidScene.h +++ /dev/null @@ -1,48 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2023 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -// Jolt includes -#include - -// Local includes -#include "PerformanceTestScene.h" -#include "Layers.h" - -// A scene that creates a pyramid of boxes to create a very large island -class PyramidScene : public PerformanceTestScene -{ -public: - virtual const char * GetName() const override - { - return "Pyramid"; - } - - virtual void StartTest(PhysicsSystem &inPhysicsSystem, EMotionQuality inMotionQuality) override - { - BodyInterface &bi = inPhysicsSystem.GetBodyInterface(); - - // Floor - bi.CreateAndAddBody(BodyCreationSettings(new BoxShape(Vec3(50.0f, 1.0f, 50.0f), 0.0f), RVec3(Vec3(0.0f, -1.0f, 0.0f)), Quat::sIdentity(), EMotionType::Static, Layers::NON_MOVING), EActivation::DontActivate); - - const float cBoxSize = 2.0f; - const float cBoxSeparation = 0.5f; - const float cHalfBoxSize = 0.5f * cBoxSize; - const int cPyramidHeight = 15; - - RefConst box_shape = new BoxShape(Vec3::sReplicate(cHalfBoxSize), 0.0f); // No convex radius to force more collisions - - // Pyramid - for (int i = 0; i < cPyramidHeight; ++i) - for (int j = i / 2; j < cPyramidHeight - (i + 1) / 2; ++j) - for (int k = i / 2; k < cPyramidHeight - (i + 1) / 2; ++k) - { - RVec3 position(Real(-cPyramidHeight + cBoxSize * j + (i & 1? cHalfBoxSize : 0.0f)), Real(1.0f + (cBoxSize + cBoxSeparation) * i), Real(-cPyramidHeight + cBoxSize * k + (i & 1? cHalfBoxSize : 0.0f))); - BodyCreationSettings settings(box_shape, position, Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING); - settings.mAllowSleeping = false; // No sleeping to force the large island to stay awake - bi.CreateAndAddBody(settings, EActivation::Activate); - } - } -}; diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/PerformanceTest/RagdollScene.h b/crates/joltc-sys-patched/JoltC/JoltPhysics/PerformanceTest/RagdollScene.h deleted file mode 100644 index e728929bc4f..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/PerformanceTest/RagdollScene.h +++ /dev/null @@ -1,140 +0,0 @@ -// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) -// SPDX-FileCopyrightText: 2021 Jorrit Rouwe -// SPDX-License-Identifier: MIT - -#pragma once - -// Jolt includes -#include -#include -#include -#include -#include - -// Local includes -#include "PerformanceTestScene.h" -#include "Layers.h" - -// A scene that loads a part of a Horizon Zero Dawn level and drops many ragdolls on the terrain (motors enabled) -class RagdollScene : public PerformanceTestScene -{ -public: - RagdollScene(int inNumPilesPerAxis, int inPileSize, float inVerticalSeparation) : mNumPilesPerAxis(inNumPilesPerAxis), mPileSize(inPileSize), mVerticalSeparation(inVerticalSeparation) { } - - virtual const char * GetName() const override - { - return mNumPilesPerAxis == 1? "RagdollSinglePile" : "Ragdoll"; - } - - virtual bool Load() override - { - // Load ragdoll - if (!ObjectStreamIn::sReadObject("Assets/Human.tof", mRagdollSettings)) - { - cerr << "Unable to load ragdoll" << endl; - return false; - } - for (BodyCreationSettings &body : mRagdollSettings->mParts) - body.mObjectLayer = Layers::MOVING; - - // Init ragdoll - mRagdollSettings->GetSkeleton()->CalculateParentJointIndices(); - mRagdollSettings->Stabilize(); - mRagdollSettings->CalculateBodyIndexToConstraintIndex(); - mRagdollSettings->CalculateConstraintIndexToBodyIdxPair(); - - // Load animation - if (!ObjectStreamIn::sReadObject("Assets/Human/dead_pose1.tof", mAnimation)) - { - cerr << "Unable to load animation" << endl; - return false; - } - - // Sample pose - mPose.SetSkeleton(mRagdollSettings->GetSkeleton()); - mAnimation->Sample(0.0f, mPose); - - // Read the background scene - if (!ObjectStreamIn::sReadObject("Assets/terrain2.bof", mBackground)) - { - cerr << "Unable to load terrain" << endl; - return false; - } - for (BodyCreationSettings &body : mBackground->GetBodies()) - body.mObjectLayer = Layers::NON_MOVING; - mBackground->FixInvalidScales(); - - return true; - } - - virtual void StartTest(PhysicsSystem &inPhysicsSystem, EMotionQuality inMotionQuality) override - { - // Test configuration - const Real cHorizontalSeparation = 4.0_r; - - // Set motion quality on ragdoll - for (BodyCreationSettings &body : mRagdollSettings->mParts) - body.mMotionQuality = inMotionQuality; - - // Add background geometry - mBackground->CreateBodies(&inPhysicsSystem); - - // Create ragdoll piles - mt19937 random; - uniform_real_distribution angle(0.0f, JPH_PI); - CollisionGroup::GroupID group_id = 1; - for (int row = 0; row < mNumPilesPerAxis; ++row) - for (int col = 0; col < mNumPilesPerAxis; ++col) - { - // Determine start location of ray - RVec3 start(cHorizontalSeparation * (col - (mNumPilesPerAxis - 1) / 2.0_r), 100, cHorizontalSeparation * (row - (mNumPilesPerAxis - 1) / 2.0_r)); - - // Cast ray down to terrain - RayCastResult hit; - Vec3 ray_direction(0, -200, 0); - RRayCast ray { start, ray_direction }; - if (inPhysicsSystem.GetNarrowPhaseQuery().CastRay(ray, hit, SpecifiedBroadPhaseLayerFilter(BroadPhaseLayers::NON_MOVING), SpecifiedObjectLayerFilter(Layers::NON_MOVING))) - start = ray.GetPointOnRay(hit.mFraction); - - for (int i = 0; i < mPileSize; ++i) - { - // Create ragdoll - Ref ragdoll = mRagdollSettings->CreateRagdoll(group_id++, 0, &inPhysicsSystem); - - // Override root - SkeletonPose pose_copy = mPose; - pose_copy.SetRootOffset(start); - SkeletonPose::JointState &root = pose_copy.GetJoint(0); - root.mTranslation = Vec3(0, mVerticalSeparation * (i + 1), 0); - root.mRotation = Quat::sRotation(Vec3::sAxisY(), angle(random)) * root.mRotation; - pose_copy.CalculateJointMatrices(); - - // Drive to pose - ragdoll->SetPose(pose_copy); - ragdoll->DriveToPoseUsingMotors(pose_copy); - ragdoll->AddToPhysicsSystem(EActivation::Activate); - - // Keep reference - mRagdolls.push_back(ragdoll); - } - } - } - - virtual void StopTest(PhysicsSystem &inPhysicsSystem) override - { - // Remove ragdolls - for (Ragdoll *ragdoll : mRagdolls) - ragdoll->RemoveFromPhysicsSystem(); - mRagdolls.clear(); - } - -private: - int mNumPilesPerAxis; - int mPileSize; - float mVerticalSeparation; - Ref mRagdollSettings; - Ref mAnimation; - SkeletonPose mPose; - Ref mBackground; - Array> mRagdolls; -}; diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/README.md b/crates/joltc-sys-patched/JoltC/JoltPhysics/README.md deleted file mode 100644 index f4d46900886..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/README.md +++ /dev/null @@ -1,159 +0,0 @@ -[![CLA assistant](https://cla-assistant.io/readme/badge/jrouwe/JoltPhysics)](https://cla-assistant.io/jrouwe/JoltPhysics) -[![Build Status](https://github.com/jrouwe/JoltPhysics/actions/workflows/build.yml/badge.svg)](https://github.com/jrouwe/JoltPhysics/actions/) -[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=jrouwe_JoltPhysics&metric=alert_status)](https://sonarcloud.io/dashboard?id=jrouwe_JoltPhysics) -[![Bugs](https://sonarcloud.io/api/project_badges/measure?project=jrouwe_JoltPhysics&metric=bugs)](https://sonarcloud.io/dashboard?id=jrouwe_JoltPhysics) -[![Code Smells](https://sonarcloud.io/api/project_badges/measure?project=jrouwe_JoltPhysics&metric=code_smells)](https://sonarcloud.io/dashboard?id=jrouwe_JoltPhysics) - -# Jolt Physics - -A multi core friendly rigid body physics and collision detection library suitable for games and VR applications, used by Horizon Forbidden West. - -[![Horizon Forbidden West Cover Art](https://jrouwe.nl/jolt/Horizon_Forbidden_West.png)](https://www.playstation.com/en-us/games/horizon-forbidden-west/) - -|[![Ragdoll Pile](https://img.youtube.com/vi/pwyCW0yNKMA/hqdefault.jpg)](https://www.youtube.com/watch?v=pwyCW0yNKMA)| -|:-| -|*A YouTube video showing a ragdoll pile simulated with Jolt Physics.*| - -For more demos and [videos](https://www.youtube.com/watch?v=pwyCW0yNKMA&list=PLYXVwtOr1CBxbA50jVg2dKUQvHW_5OOom) go to the [Samples](Docs/Samples.md) section. - -## Design Considerations - -So why create yet another physics engine? First of all, this has been a personal learning project and secondly I wanted to address some issues that I had with existing physics engines: - -* In games we usually need to do many more things than to simulate the physics world and we need to do this across multiple threads. We therefore place a lot of emphasis on concurrently accessing the physics simulation data outside of the main physics simulation update: - * Sections of the world can be loaded / unloaded in the background. A batch of physics bodies can be prepared on a background thread without locking or affecting the physics simulation and then inserted into the world all at once with a minimal impact on performance. - * Collision queries can run in parallel with other operations like insertion / removal of bodies. The query code is guaranteed to see a body in a consistent state, but when a body is changed during a collision query there is no guarantee if the change is visible to the query or not. If a thread modifies the position of a body and then does a collision query, it will immediately see the updated state (this is often a problem when working with a read version and a write version of the world). - * It is also possible to run collision queries in parallel to the main physics simulation by doing the broad phase query before the simulation step. This way, long running processes (like navigation mesh generation) can be spread out across multiple frames while still running the physics simulation every frame. -* One of the main sources of performance problems we found was waking up too many bodies while loading / unloading content. Therefore, bodies will not automatically wake up when created and neighboring bodies will not be woken up when bodies are removed. This can be triggered manually if desired. -* The simulation runs deterministically, so you could replicate a simulation to a remote client by merely replicating the inputs to the simulation. Read the [Deterministic Simulation](https://jrouwe.github.io/JoltPhysics/#deterministic-simulation) section to understand the limits of this. -* The simulation of this physics engine tries to simulate behavior of rigid bodies in the real world but makes approximations in the simulation so should mainly be used for games or VR simulations. - -## Features - -* Simulation of rigid bodies of various shapes using continuous collision detection: - * Sphere. - * Box. - * Capsule. - * Tapered-capsule. - * Cylinder. - * Convex hull. - * Compound. - * Mesh (triangle). - * Terrain (height field). -* Simulation of constraints between bodies: - * Fixed. - * Point. - * Distance (including springs). - * Hinge. - * Slider (also called prismatic). - * Cone. - * Rack and Pinion. - * Gear. - * Pulley. - * Smooth spline paths. - * Swing-twist (for humanoid shoulders). - * 6 DOF. -* Motors to drive the constraints. -* Collision detection: - * Casting rays. - * Testing shapes vs shapes. - * Casting a shape vs another shape. - * Broadphase only tests for quickly determining which objects may intersect. -* Sensors (trigger volumes). -* Animated ragdolls: - * Hard keying (kinematic only rigid bodies). - * Soft keying (setting velocities on dynamic rigid bodies). - * Driving constraint motors to an animated pose. - * Mapping a high detail (animation) skeleton onto a low detail (ragdoll) skeleton and vice versa. -* Game character simulation (capsule) - * Rigid body character. Moves during the physics simulation. Cheapest option and most accurate collision response between character and dynamic bodies. - * Virtual character. Does not have a rigid body in the world but simulates one using collision checks. Updated outside of the physics update for more control. Less accurate interaction with dynamic bodies. -* Vehicles - * Wheeled vehicles. - * Tracked vehicles. - * Motorcycles. -* Soft body simulation (e.g. a soft ball or piece of cloth). - * Edge constraints. - * Dihedral bend constraints. - * Tetrahedron volume constraints. - * Long range attachment constraints (also called tethers). - * Limiting the simulation to stay within a certain range of a skinned vertex. - * Internal pressure. - * Collision with simulated rigid bodies. - * Collision tests against soft bodies. -* Water buoyancy calculations. -* An optional double precision mode that allows large worlds. - -## Supported Platforms - -* Windows (VS2019, VS2022) x86/x64/ARM32/ARM64 (Desktop/UWP) -* Linux (tested on Ubuntu 22.04) x64/ARM64 -* FreeBSD -* Android (tested on Android 14) x86/x64/ARM32/ARM64 -* Platform Blue (a popular game console) x64 -* macOS (tested on Monterey) x64/ARM64 -* iOS (tested on iOS 15) x64/ARM64 -* WebAssembly, see [this](https://github.com/jrouwe/JoltPhysics.js) separate project. - -## Required CPU features - -* On x86 the minimal requirements are SSE2 but the library can be compiled using SSE4.1, SSE4.2, AVX, AVX2, or AVX512. -* On ARM64 the library by default compiles with NEON and FP16, on ARM32 it can be compiled without any special CPU instructions. - -## Documentation - -To learn more about Jolt go to the [Architecture and API documentation](https://jrouwe.github.io/JoltPhysics/). - -To get started, look at the [HelloWorld](HelloWorld/HelloWorld.cpp) example. A [HelloWorld example using CMake FetchContent](https://github.com/jrouwe/JoltPhysicsHelloWorld) is also available to show how you can integrate Jolt Physics in a CMake project. - -Some algorithms used by Jolt are described in detail in my GDC 2022 talk Architecting Jolt Physics for 'Horizon Forbidden West' ([slides](https://gdcvault.com/play/1027560/Architecting-Jolt-Physics-for-Horizon), [slides with speaker notes](https://jrouwe.nl/architectingjolt/ArchitectingJoltPhysics_Rouwe_Jorrit_Notes.pdf), [video](https://gdcvault.com/play/1027891/Architecting-Jolt-Physics-for-Horizon)). - -## Compiling - -* The library has been tested to compile with Cl (Visual Studio 2019-2022), Clang 10+ and GCC 9+. -* It uses C++17 and only depends on the standard template library. -* It doesn't make use of compiler generated RTTI or exceptions. -* If you want to run on Platform Blue you'll need to provide your own build environment and PlatformBlue.h file due to NDA requirements (see Core.h for further info). - -For build instructions go to the [Build](Build/README.md) section. When upgrading from an older version of the library go to the [Release Notes](Docs/ReleaseNotes.md) or [API Changes](Docs/APIChanges.md) sections. - -## Performance - -If you're interested in how Jolt scales with multiple CPUs and compares to other physics engines, take a look at [this document](https://jrouwe.nl/jolt/JoltPhysicsMulticoreScaling.pdf). - -## Folder Structure - -* Assets - This folder contains assets used by the TestFramework, Samples and JoltViewer. -* Build - Contains everything needed to build the library, see the [Build](Build/README.md) section. -* Docs - Contains documentation for the library. -* HelloWorld - A simple application demonstrating how to use the Jolt Physics library. -* Jolt - All source code for the library is in this folder. -* JoltViewer - It is possible to record the output of the physics engine using the DebugRendererRecorder class (a .jor file), this folder contains the source code to an application that can visualize a recording. This is useful for e.g. visualizing the output of the PerformanceTest from different platforms. Currently available on Windows only. -* PerformanceTest - Contains a simple application that runs a [performance test](Docs/PerformanceTest.md) and collects timing information. -* Samples - This contains the sample application, see the [Samples](Docs/Samples.md) section. Currently available on Windows only. -* TestFramework - A rendering framework to visualize the results of the physics engine. Used by Samples and JoltViewer. Currently available on Windows only. -* UnitTests - A set of unit tests to validate the behavior of the physics engine. -* WebIncludes - A number of JavaScript resources used by the internal profiling framework of the physics engine. - -## Bindings For Other Languages - -* C [here](https://github.com/michal-z/zig-gamedev/tree/main/libs/zphysics/libs) and [here](https://github.com/amerkoleci/JoltPhysicsSharp/tree/main/src/joltc) -* [C#](https://github.com/amerkoleci/JoltPhysicsSharp) -* [Java](https://github.com/aecsocket/jolt-java) -* [JavaScript](https://github.com/jrouwe/JoltPhysics.js) -* [Zig](https://github.com/michal-z/zig-gamedev/tree/main/libs/zphysics) - -## Integrations in Other Engines - -* [Godot](https://github.com/godot-jolt/godot-jolt) -* [Source Engine](https://github.com/Joshua-Ashton/VPhysics-Jolt) - -See [a list of projects that use Jolt Physics here](Docs/ProjectsUsingJolt.md). - -## License - -The project is distributed under the [MIT license](LICENSE). - -## Contributions - -All contributions are welcome! If you intend to make larger changes, please discuss first in the GitHub Discussion section. For non-trivial changes, we require that you agree to a [Contributor Agreement](ContributorAgreement.md). When you create a PR, [CLA assistant](https://cla-assistant.io/) will prompt you to sign it. diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/WebIncludes/profile_chart.css b/crates/joltc-sys-patched/JoltC/JoltPhysics/WebIncludes/profile_chart.css deleted file mode 100644 index ae60f3b3b7c..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/WebIncludes/profile_chart.css +++ /dev/null @@ -1,36 +0,0 @@ -html, body { - padding: 0px; - border: 0px; - margin: 0px; - width: 100%; - height: 100%; - overflow: hidden; -} - -canvas { - position: absolute; - top: 10px; - left: 10px; - padding: 0px; - border: 0px; - margin: 0px; -} - -#tooltip { - font: Courier New; - position: absolute; - background-color: white; - border: 1px; - border-style: solid; - border-color: black; - pointer-events: none; - padding: 5px; - font: 14px Arial; - visibility: hidden; - height: auto; -} - -.stat { - color: blue; - text-align: right; -} diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/WebIncludes/profile_chart.js b/crates/joltc-sys-patched/JoltC/JoltPhysics/WebIncludes/profile_chart.js deleted file mode 100644 index fa4c1c4bff0..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/WebIncludes/profile_chart.js +++ /dev/null @@ -1,285 +0,0 @@ -var canvas; -var ctx; -var tooltip; -var min_scale; -var scale; -var offset_x = 0; -var offset_y = 0; -var size_y; -var dragging = false; -var previous_x = 0; -var previous_y = 0; -var bar_height = 15; -var line_height = bar_height + 2; -var thread_separation = 6; -var thread_font_size = 12; -var thread_font = thread_font_size + "px Arial"; -var bar_font_size = 10; -var bar_font = bar_font_size + "px Arial"; -var end_cycle = 0; - -function drawChart() -{ - ctx.clearRect(0, 0, canvas.width, canvas.height); - - ctx.lineWidth = 1; - - var y = offset_y; - - for (var t = 0; t < threads.length; t++) - { - // Check if thread has samples - var thread = threads[t]; - if (thread.start.length == 0) - continue; - - // Draw thread name - y += thread_font_size; - ctx.font = thread_font; - ctx.fillStyle = "#000000"; - ctx.fillText(thread.thread_name, 0, y); - y += thread_separation; - - // Draw outlines for each bar of samples - ctx.fillStyle = "#c0c0c0"; - for (var d = 0; d <= thread.max_depth; d++) - ctx.fillRect(0, y + d * line_height, canvas.width, bar_height); - - // Draw samples - ctx.font = bar_font; - for (var s = 0; s < thread.start.length; s++) - { - // Cull bar - var rx = scale * (offset_x + thread.start[s]); - if (rx > canvas.width) // right of canvas - break; - var rw = scale * thread.cycles[s]; - if (rw < 0.5) // less than half pixel, skip - continue; - if (rx + rw < 0) // left of canvas - continue; - - // Draw bar - var ry = y + line_height * thread.depth[s]; - ctx.fillStyle = thread.color[s]; - ctx.fillRect(rx, ry, rw, bar_height); - ctx.strokeStyle = thread.darkened_color[s]; - ctx.strokeRect(rx, ry, rw, bar_height); - - // Get index in aggregated list - var a = thread.aggregator[s]; - - // Draw text - if (rw > aggregated.name_width[a]) - { - ctx.fillStyle = "#000000"; - ctx.fillText(aggregated.name[a], rx + (rw - aggregated.name_width[a]) / 2, ry + bar_height - 4); - } - } - - // Next line - y += line_height * (1 + thread.max_depth) + thread_separation; - } - - // Update size - size_y = y - offset_y; -} - -function drawTooltip(mouse_x, mouse_y) -{ - var y = offset_y; - - for (var t = 0; t < threads.length; t++) - { - // Check if thread has samples - var thread = threads[t]; - if (thread.start.length == 0) - continue; - - // Thead name - y += thread_font_size + thread_separation; - - // Draw samples - for (var s = 0; s < thread.start.length; s++) - { - // Cull bar - var rx = scale * (offset_x + thread.start[s]); - if (rx > mouse_x) - break; - var rw = scale * thread.cycles[s]; - if (rx + rw < mouse_x) - continue; - - var ry = y + line_height * thread.depth[s]; - if (mouse_y >= ry && mouse_y < ry + bar_height) - { - // Get index into aggregated list - var a = thread.aggregator[s]; - - // Found bar, fill in tooltip - tooltip.style.left = (canvas.offsetLeft + mouse_x) + "px"; - tooltip.style.top = (canvas.offsetTop + mouse_y) + "px"; - tooltip.style.visibility = "visible"; - tooltip.innerHTML = aggregated.name[a] + "
              " - + "" - + "" - + "" - + "" - + "" - + "" - + "" - + "" - + "" - + "
              Time:" + (1000000 * thread.cycles[s] / cycles_per_second).toFixed(2) + " µs
              Start:" + (1000000 * thread.start[s] / cycles_per_second).toFixed(2) + " µs
              End:" + (1000000 * (thread.start[s] + thread.cycles[s]) / cycles_per_second).toFixed(2) + " µs
              Avg. Time:" + (1000000 * aggregated.cycles_per_frame[a] / cycles_per_second / aggregated.calls[a]).toFixed(2) + " µs
              Min Time:" + (1000000 * aggregated.min_cycles[a] / cycles_per_second).toFixed(2) + " µs
              Max Time:" + (1000000 * aggregated.max_cycles[a] / cycles_per_second).toFixed(2) + " µs
              Time / Frame:" + (1000000 * aggregated.cycles_per_frame[a] / cycles_per_second).toFixed(2) + " µs
              Calls:" + aggregated.calls[a] + "
              "; - return; - } - } - - // Next line - y += line_height * (1 + thread.max_depth) + thread_separation; - } - - // No bar found, hide tooltip - tooltip.style.visibility = "hidden"; -} - -function onMouseDown(evt) -{ - dragging = true; - previous_x = evt.clientX, previous_y = evt.clientY; - tooltip.style.visibility = "hidden"; -} - -function onMouseUp(evt) -{ - dragging = false; -} - -function clampMotion() -{ - // Clamp horizontally - var min_offset_x = canvas.width / scale - end_cycle; - if (offset_x < min_offset_x) - offset_x = min_offset_x; - if (offset_x > 0) - offset_x = 0; - - // Clamp vertically - var min_offset_y = canvas.height - size_y; - if (offset_y < min_offset_y) - offset_y = min_offset_y; - if (offset_y > 0) - offset_y = 0; - - // Clamp scale - if (scale < min_scale) - scale = min_scale; - var max_scale = 1000 * min_scale; - if (scale > max_scale) - scale = max_scale; -} - -function onMouseMove(evt) -{ - if (dragging) - { - // Calculate new offset - offset_x += (evt.clientX - previous_x) / scale; - offset_y += evt.clientY - previous_y; - - clampMotion(); - - drawChart(); - } - else - drawTooltip(evt.clientX - canvas.offsetLeft, evt.clientY - canvas.offsetTop); - - previous_x = evt.clientX, previous_y = evt.clientY; -} - -function onScroll(evt) -{ - tooltip.style.visibility = "hidden"; - - var old_scale = scale; - if (evt.deltaY > 0) - scale /= 1.1; - else - scale *= 1.1; - - clampMotion(); - - // Ensure that event under mouse stays under mouse - var x = previous_x - canvas.offsetLeft; - offset_x += x / scale - x / old_scale; - - clampMotion(); - - drawChart(); -} - -function darkenColor(color) -{ - var i = parseInt(color.slice(1), 16); - - var r = i >> 16; - var g = (i >> 8) & 0xff; - var b = i & 0xff; - - r = Math.round(0.8 * r); - g = Math.round(0.8 * g); - b = Math.round(0.8 * b); - - i = (r << 16) + (g << 8) + b; - - return "#" + i.toString(16); -} - -function startChart() -{ - // Fetch elements - canvas = document.getElementById('canvas'); - ctx = canvas.getContext("2d"); - tooltip = document.getElementById('tooltip'); - - // Resize canvas to fill screen - canvas.width = document.body.offsetWidth - 20; - canvas.height = document.body.offsetHeight - 20; - - // Register mouse handlers - canvas.onmousedown = onMouseDown; - canvas.onmouseup = onMouseUp; - canvas.onmouseout = onMouseUp; - canvas.onmousemove = onMouseMove; - canvas.onwheel = onScroll; - - for (var t = 0; t < threads.length; t++) - { - var thread = threads[t]; - - // Calculate darkened colors - thread.darkened_color = new Array(thread.color.length); - for (var s = 0; s < thread.color.length; s++) - thread.darkened_color[s] = darkenColor(thread.color[s]); - - // Calculate max depth and end cycle - thread.max_depth = 0; - for (var s = 0; s < thread.start.length; s++) - { - thread.max_depth = Math.max(thread.max_depth, thread.depth[s]); - end_cycle = Math.max(end_cycle, thread.start[s] + thread.cycles[s]); - } - } - - // Calculate width of name strings - ctx.font = bar_font; - aggregated.name_width = new Array(aggregated.name.length); - for (var a = 0; a < aggregated.name.length; a++) - aggregated.name_width[a] = ctx.measureText(aggregated.name[a]).width; - - // Store scale properties - min_scale = canvas.width / end_cycle; - scale = min_scale; - - drawChart(); -} \ No newline at end of file diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/run_doxygen.bat b/crates/joltc-sys-patched/JoltC/JoltPhysics/run_doxygen.bat deleted file mode 100644 index 8f8b85d3243..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/run_doxygen.bat +++ /dev/null @@ -1,4 +0,0 @@ -@echo off -del %~dp0%Build\Doxygen /s /q > NUL -doxygen -pause \ No newline at end of file diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/sonar-project.properties b/crates/joltc-sys-patched/JoltC/JoltPhysics/sonar-project.properties deleted file mode 100644 index 82c213972bb..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/sonar-project.properties +++ /dev/null @@ -1,14 +0,0 @@ -sonar.projectKey=jrouwe_JoltPhysics -sonar.organization=jrouwe - -# This is the name and version displayed in the SonarCloud UI. -sonar.projectName=Jolt Physics -sonar.projectVersion=1.0 - -# Path is relative to the sonar-project.properties file. Replace "\" by "/" on Windows. -sonar.sources=Jolt - -# Encoding of the source code. Default is default system encoding -#sonar.sourceEncoding=UTF-8 - -sonar.cfamily.threads=2 diff --git a/crates/joltc-sys-patched/JoltC/LICENSE-APACHE b/crates/joltc-sys-patched/JoltC/LICENSE-APACHE deleted file mode 100644 index 8dada3edaf5..00000000000 --- a/crates/joltc-sys-patched/JoltC/LICENSE-APACHE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright {yyyy} {name of copyright owner} - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/crates/joltc-sys-patched/JoltC/LICENSE-MIT b/crates/joltc-sys-patched/JoltC/LICENSE-MIT deleted file mode 100644 index e769463c211..00000000000 --- a/crates/joltc-sys-patched/JoltC/LICENSE-MIT +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Portions Copyright (c) 2024 Second Half Games - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/crates/joltc-sys-patched/JoltC/README.md b/crates/joltc-sys-patched/JoltC/README.md deleted file mode 100644 index 544fe9b5053..00000000000 --- a/crates/joltc-sys-patched/JoltC/README.md +++ /dev/null @@ -1,45 +0,0 @@ -# JoltC -C wrapper for [Jolt Physics](https://github.com/jrouwe/JoltPhysics) 5.0.0. - -Currently a work in progress. Bindings contain functions that we've needed as part of our game or the Rust bindings we're working on in [jolt-rust](https://github.com/SecondHalfGames/jolt-rust). - -## Goals -1. Sound C wrapper around current version of Jolt Physics -2. Headers suitable for usage in automatic binding generation tools (i.e. Rust `bindgen`, LuaJIT FFI) - -## Building -Use CMake: - -```bash -cmake -B build -cmake --build build -``` - -## Hello, world! -A port of Jolt's "HelloWorld" example is provided in [HelloWorld/main.cpp](HelloWorld/main.cpp). - -## Other C Wrappers -Other C wrappers for Jolt Physics include: -- "JoltC", part of the [zphysics] Zig library started by [Michal Ziulek][michal-ziulek] -- "JoltC", part of the [jolt-rs] Rust library started by [cohaereo] and a fork of the zphysics C wrapper -- "joltc", part of the [JoltPhysicsSharp] C# library started by [Amer Koleci][amerkoleci] - -The goal of this project is to be the first C wrapper around Jolt Physics that is not part of a larger binding project and to eliminate sources of undefined behavior. It's intended to be useful for any other language-specific bindings and to reduce the need to duplicate work. - -## License -Licensed under either of - -* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) -* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) - -at your option. - -### Contribution -Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. - -[zphysics]: https://github.com/zig-gamedev/zig-gamedev/tree/main/libs/zphysics -[jolt-rs]: https://github.com/cohaereo/jolt-rs -[JoltPhysicsSharp]: https://github.com/amerkoleci/JoltPhysicsSharp -[michal-ziulek]: https://github.com/michal-z -[amerkoleci]: https://github.com/amerkoleci -[cohaereo]: https://github.com/cohaereo \ No newline at end of file diff --git a/crates/joltc-sys-patched/JoltC/test-all-flags.sh b/crates/joltc-sys-patched/JoltC/test-all-flags.sh deleted file mode 100644 index ee80034da2d..00000000000 --- a/crates/joltc-sys-patched/JoltC/test-all-flags.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -run_tests() { - echo "Building with flags $1" - - cmake $1 -B build - cmake --build build - - ./build/Debug/HelloWorld -} - -run_tests "" -run_tests "-DDOUBLE_PRECISION=ON" -run_tests "-DOBJECT_LAYER_BITS=32" \ No newline at end of file diff --git a/crates/joltc-sys-patched/README.md b/crates/joltc-sys-patched/README.md deleted file mode 100644 index 5b5400f9fee..00000000000 --- a/crates/joltc-sys-patched/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# `joltc-sys` — Jolt bindings via [JoltC] -This crate contains unsafe bindings to JoltC. - -## Build Requirements -- CMake 3.16 or newer -- `libclang`, see the [bindgen guide](https://rust-lang.github.io/rust-bindgen/requirements.html) for installation steps. - -## Features -- `double-precision`: Enable higher precision simulation using doubles instead of floats. -- `object-layer-u32`: Changes the ObjectLayer type to use 32 bits instead of 16 bits. \ No newline at end of file diff --git a/crates/joltc-sys-patched/build.rs b/crates/joltc-sys-patched/build.rs deleted file mode 100644 index 8f2695680f6..00000000000 --- a/crates/joltc-sys-patched/build.rs +++ /dev/null @@ -1,166 +0,0 @@ -use std::env; -use std::path::Path; - -use anyhow::Context; - -fn main() { - let flags = build_flags(); - - build_joltc(); - link(); - shadow_msvcprt(); - generate_bindings(&flags).unwrap(); -} - -/// Create a stub msvcprt.lib in our output directory to shadow the system one. -/// -/// V8 explicitly links msvcprt.lib (the dynamic C++ CRT import library) via -/// `cargo:rustc-link-lib=dylib=msvcprt`. Its objects carry -/// `/failifmismatch:RuntimeLibrary=MD_DynamicRelease`, which conflicts with -/// Tracy and Jolt objects compiled with /MT (`MT_StaticRelease`). -/// -/// Neither lld-link nor MSVC link.exe can suppress /failifmismatch, even with -/// /FORCE. The workaround: place an empty msvcprt.lib in our link search path -/// (which comes before system paths). The linker finds our stub first and loads -/// no conflicting objects. All C++ stdlib symbols V8 needs are already provided -/// by the static CRT (libcpmt.lib) linked via +crt-static. -fn shadow_msvcprt() { - if !cfg!(windows) { - return; - } - - let out_dir = env::var("OUT_DIR").unwrap(); - let stub_path = Path::new(&out_dir).join("lib").join("msvcprt.lib"); - - // Write a minimal valid COFF archive (empty, no members). - // Format: 8-byte signature + first linker member (empty symbol table). - let mut data = Vec::new(); - // Archive signature - data.extend_from_slice(b"!\n"); - // First linker member header (60 bytes total) - // Name (16 bytes) - data.extend_from_slice(b"/ "); - // Date (12 bytes) - data.extend_from_slice(b"0 "); - // User ID (6 bytes) - data.extend_from_slice(b" "); - // Group ID (6 bytes) - data.extend_from_slice(b" "); - // Mode (8 bytes) - data.extend_from_slice(b"0 "); - // Size (10 bytes) - 4 bytes for the symbol count (0) - data.extend_from_slice(b"4 "); - // End marker (2 bytes) - data.extend_from_slice(b"`\n"); - // Content: number of symbols = 0 (big-endian u32) - data.extend_from_slice(&[0u8; 4]); - - std::fs::write(&stub_path, &data) - .expect("failed to write stub msvcprt.lib"); -} - -fn build_joltc() { - let mut config = cmake::Config::new("JoltC"); - - // We always have to build in Release. - // - // On Windows, Rust always links against the non-debug CRT. Using the Debug - // profile (which the cmake crate will sometimes pick by default) causes - // Jolt/JoltC to be linked against the debug CRT, causing linker issues. - // - // Forcing Jolt and JoltC to be compiled with the non-debug CRT (/MT) - // doesn't change enough about the build to work. - // - // As a nice side effect, this ensures that we build with a known - // configuration instead of accidentally enabling or disabling extra - // features just based on opt-level. - config.profile("Release"); - - // Jolt fails to compile via the cmake crate without specifying exception - // handling behavior under MSVC. - if cfg!(windows) { - config.cxxflag("/EHsc"); - } - - // Build Jolt with static CRT (/MT) to match the rest of SpacetimeDB. - // See shadow_msvcprt() for how we handle the V8 CRT mismatch. - if cfg!(windows) { - config.static_crt(true); - config.configure_arg("-DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded"); - config.configure_arg("-DCMAKE_POLICY_DEFAULT_CMP0091=NEW"); - } - - // Having IPO/LTO turned on breaks lld on Windows. - config.configure_arg("-DINTERPROCEDURAL_OPTIMIZATION=OFF"); - - // These feature flags go through CMake and affect compilation of both Jolt - // and JoltC. - if cfg!(feature = "double-precision") { - config.configure_arg("-DDOUBLE_PRECISION=ON"); - } - if cfg!(feature = "object-layer-u32") { - config.configure_arg("-DOBJECT_LAYER_BITS=32"); - } - - let mut dst = config.build(); - - // Jolt and JoltC put libraries in the 'lib' subfolder. This goes against - // the docs of the cmake crate, but it's possible that it's just mishandling - // an output path and not account for the install target's configurability. - dst.push("lib"); - - println!("cargo:rustc-link-search=native={}", dst.display()); -} - -fn link() { - println!("cargo:rustc-link-lib=Jolt"); - println!("cargo:rustc-link-lib=joltc"); -} - -/// Generate build flags specifically for generating bindings. -/// -/// This is redundant with Jolt and JoltC's CMake files, which do this mapping -/// for us, but we're unable to leverage that config when running bindgen. -fn build_flags() -> Vec<(&'static str, &'static str)> { - let mut flags = Vec::new(); - - // Force the debug renderer on. In the future, we might want to tie this to - // a crate feature. - flags.push(("JPH_DEBUG_RENDERER", "ON")); - - // It's important that these flags are never out of sync for Jolt and JoltC. - if cfg!(feature = "double-precision") { - flags.push(("JPC_DOUBLE_PRECISION", "ON")); - flags.push(("JPH_DOUBLE_PRECISION", "ON")); - } - - if cfg!(feature = "object-layer-u32") { - flags.push(("JPC_OBJECT_LAYER_BITS", "32")); - flags.push(("JPH_OBJECT_LAYER_BITS", "32")); - } - - flags -} - -fn generate_bindings(flags: &[(&'static str, &'static str)]) -> anyhow::Result<()> { - let mut builder = bindgen::Builder::default() - .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) - .header("JoltC/JoltC/JoltC.h") - .clang_arg("-IJoltC") - .allowlist_item("JPC_.*") - .default_enum_style(bindgen::EnumVariation::Consts) - .prepend_enum_name(false); - - for (key, value) in flags { - builder = builder.clang_arg(format!("-D{key}={value}")); - } - - let bindings = builder - .generate() - .context("failed to generate JoltC bindings")?; - - let out_path = Path::new(&env::var("OUT_DIR").unwrap()).join("bindings.rs"); - bindings - .write_to_file(out_path) - .context("Couldn't write bindings!") -} diff --git a/crates/joltc-sys-patched/src/generated.rs b/crates/joltc-sys-patched/src/generated.rs deleted file mode 100644 index a38a13a81db..00000000000 --- a/crates/joltc-sys-patched/src/generated.rs +++ /dev/null @@ -1,5 +0,0 @@ -#![allow(non_upper_case_globals)] -#![allow(non_camel_case_types)] -#![allow(non_snake_case)] - -include!(concat!(env!("OUT_DIR"), "/bindings.rs")); diff --git a/crates/joltc-sys-patched/src/lib.rs b/crates/joltc-sys-patched/src/lib.rs deleted file mode 100644 index abf5115794b..00000000000 --- a/crates/joltc-sys-patched/src/lib.rs +++ /dev/null @@ -1,40 +0,0 @@ -mod generated; - -pub use generated::*; - -#[cfg(feature = "double-precision")] -pub type Real = f64; - -#[cfg(not(feature = "double-precision"))] -pub type Real = f32; - -macro_rules! ffi_default { - ($($c_struct:ident -> $default_fn:ident,)*) => { - $( - impl Default for $c_struct { - fn default() -> Self { - unsafe { - let mut settings = std::mem::MaybeUninit::<$c_struct>::zeroed(); - $default_fn(settings.as_mut_ptr()); - settings.assume_init() - } - } - } - )* - }; -} - -ffi_default! { - JPC_BodyCreationSettings -> JPC_BodyCreationSettings_default, - - // All of the ShapeSettings types - JPC_TriangleShapeSettings -> JPC_TriangleShapeSettings_default, - JPC_BoxShapeSettings -> JPC_BoxShapeSettings_default, - JPC_SphereShapeSettings -> JPC_SphereShapeSettings_default, - JPC_CapsuleShapeSettings -> JPC_CapsuleShapeSettings_default, - JPC_CylinderShapeSettings -> JPC_CylinderShapeSettings_default, - JPC_ConvexHullShapeSettings -> JPC_ConvexHullShapeSettings_default, - JPC_SubShapeSettings -> JPC_SubShapeSettings_default, - JPC_StaticCompoundShapeSettings -> JPC_StaticCompoundShapeSettings_default, - JPC_MutableCompoundShapeSettings -> JPC_MutableCompoundShapeSettings_default, -} diff --git a/crates/joltc-sys-patched/tests/framework/mod.rs b/crates/joltc-sys-patched/tests/framework/mod.rs deleted file mode 100644 index c42ca2fdcd1..00000000000 --- a/crates/joltc-sys-patched/tests/framework/mod.rs +++ /dev/null @@ -1,206 +0,0 @@ -use std::ffi::{c_uint, c_void, CStr, CString}; -use std::ptr; - -// Everything prefixed with `JPC_` comes from the joltc_sys crate. -use joltc_sys::*; - -pub const OL_NON_MOVING: JPC_ObjectLayer = 0; -pub const OL_MOVING: JPC_ObjectLayer = 1; - -pub const BPL_NON_MOVING: JPC_BroadPhaseLayer = 0; -pub const BPL_MOVING: JPC_BroadPhaseLayer = 1; -pub const BPL_COUNT: JPC_BroadPhaseLayer = 2; - -#[allow(unused_variables)] -pub trait SmokeTest { - unsafe fn setup(system: *mut JPC_PhysicsSystem) -> Self; - - unsafe fn post_update(&mut self, system: *mut JPC_PhysicsSystem) -> bool { - false - } - - unsafe fn teardown(&mut self, system: *mut JPC_PhysicsSystem) {} -} - -pub fn create_box(settings: &JPC_BoxShapeSettings) -> Result<*mut JPC_Shape, CString> { - let mut shape: *mut JPC_Shape = ptr::null_mut(); - let mut err: *mut JPC_String = ptr::null_mut(); - - unsafe { - if JPC_BoxShapeSettings_Create(settings, &mut shape, &mut err) { - Ok(shape) - } else { - Err(CStr::from_ptr(JPC_String_c_str(err)).to_owned()) - } - } -} - -pub fn create_sphere(settings: &JPC_SphereShapeSettings) -> Result<*mut JPC_Shape, CString> { - let mut shape: *mut JPC_Shape = ptr::null_mut(); - let mut err: *mut JPC_String = ptr::null_mut(); - - unsafe { - if JPC_SphereShapeSettings_Create(settings, &mut shape, &mut err) { - Ok(shape) - } else { - Err(CStr::from_ptr(JPC_String_c_str(err)).to_owned()) - } - } -} - -pub fn create_convex_hull( - settings: &JPC_ConvexHullShapeSettings, -) -> Result<*mut JPC_Shape, CString> { - let mut shape: *mut JPC_Shape = ptr::null_mut(); - let mut err: *mut JPC_String = ptr::null_mut(); - - unsafe { - if JPC_ConvexHullShapeSettings_Create(settings, &mut shape, &mut err) { - Ok(shape) - } else { - Err(CStr::from_ptr(JPC_String_c_str(err)).to_owned()) - } - } -} - -pub fn vec3(x: f32, y: f32, z: f32) -> JPC_Vec3 { - JPC_Vec3 { x, y, z, _w: z } -} - -pub fn rvec3(x: Real, y: Real, z: Real) -> JPC_RVec3 { - JPC_RVec3 { x, y, z, _w: z } -} - -fn global_init() { - use std::sync::OnceLock; - - static INITIALIZED: OnceLock<()> = OnceLock::new(); - - INITIALIZED.get_or_init(|| unsafe { - JPC_RegisterDefaultAllocator(); - JPC_FactoryInit(); - JPC_RegisterTypes(); - }); -} - -pub fn run_test() { - global_init(); - - unsafe { - let temp_allocator = JPC_TempAllocatorImpl_new(10 * 1024 * 1024); - - let job_system = - JPC_JobSystemThreadPool_new2(JPC_MAX_PHYSICS_JOBS as _, JPC_MAX_PHYSICS_BARRIERS as _); - - let broad_phase_layer_interface = JPC_BroadPhaseLayerInterface_new(ptr::null(), BPL); - - let object_vs_broad_phase_layer_filter = - JPC_ObjectVsBroadPhaseLayerFilter_new(ptr::null_mut(), OVB); - - let object_vs_object_layer_filter = JPC_ObjectLayerPairFilter_new(ptr::null_mut(), OVO); - - let physics_system = JPC_PhysicsSystem_new(); - - let max_bodies = 1024; - let num_body_mutexes = 0; - let max_body_pairs = 1024; - let max_contact_constraints = 1024; - - JPC_PhysicsSystem_Init( - physics_system, - max_bodies, - num_body_mutexes, - max_body_pairs, - max_contact_constraints, - broad_phase_layer_interface, - object_vs_broad_phase_layer_filter, - object_vs_object_layer_filter, - ); - - let mut test = S::setup(physics_system); - - // TODO: register body activation listener - // TODO: register contact listener - - // TODO: PhysicsSystem::OptimizeBroadPhase - - let delta_time = 1.0 / 60.0; - let collision_steps = 1; - - loop { - JPC_PhysicsSystem_Update( - physics_system, - delta_time, - collision_steps, - temp_allocator, - job_system, - ); - - if !test.post_update(physics_system) { - break; - } - } - - test.teardown(physics_system); - - JPC_PhysicsSystem_delete(physics_system); - JPC_BroadPhaseLayerInterface_delete(broad_phase_layer_interface); - JPC_ObjectVsBroadPhaseLayerFilter_delete(object_vs_broad_phase_layer_filter); - JPC_ObjectLayerPairFilter_delete(object_vs_object_layer_filter); - - JPC_JobSystemThreadPool_delete(job_system); - JPC_TempAllocatorImpl_delete(temp_allocator); - } -} - -unsafe extern "C" fn bpl_get_num_broad_phase_layers(_this: *const c_void) -> c_uint { - BPL_COUNT as _ -} - -unsafe extern "C" fn bpl_get_broad_phase_layer( - _this: *const c_void, - layer: JPC_ObjectLayer, -) -> JPC_BroadPhaseLayer { - match layer { - OL_NON_MOVING => BPL_NON_MOVING, - OL_MOVING => BPL_MOVING, - _ => unreachable!(), - } -} - -const BPL: JPC_BroadPhaseLayerInterfaceFns = JPC_BroadPhaseLayerInterfaceFns { - GetNumBroadPhaseLayers: Some(bpl_get_num_broad_phase_layers as _), - GetBroadPhaseLayer: Some(bpl_get_broad_phase_layer as _), -}; - -unsafe extern "C" fn ovb_should_collide( - _this: *const c_void, - layer1: JPC_ObjectLayer, - layer2: JPC_BroadPhaseLayer, -) -> bool { - match layer1 { - OL_NON_MOVING => layer2 == BPL_MOVING, - OL_MOVING => true, - _ => unreachable!(), - } -} - -const OVB: JPC_ObjectVsBroadPhaseLayerFilterFns = JPC_ObjectVsBroadPhaseLayerFilterFns { - ShouldCollide: Some(ovb_should_collide as _), -}; - -unsafe extern "C" fn ovo_should_collide( - _this: *const c_void, - layer1: JPC_ObjectLayer, - layer2: JPC_ObjectLayer, -) -> bool { - match layer1 { - OL_NON_MOVING => layer2 == OL_MOVING, - OL_MOVING => true, - _ => unreachable!(), - } -} - -const OVO: JPC_ObjectLayerPairFilterFns = JPC_ObjectLayerPairFilterFns { - ShouldCollide: Some(ovo_should_collide as _), -}; diff --git a/crates/joltc-sys-patched/tests/smoke_test.rs b/crates/joltc-sys-patched/tests/smoke_test.rs deleted file mode 100644 index 7e10361e007..00000000000 --- a/crates/joltc-sys-patched/tests/smoke_test.rs +++ /dev/null @@ -1,246 +0,0 @@ -mod framework; - -use std::f32::consts::{PI, TAU}; -use std::mem; - -use joltc_sys::*; -use rand::Rng; - -use crate::framework::*; - -struct SetupTeardown; - -impl SmokeTest for SetupTeardown { - unsafe fn setup(_system: *mut JPC_PhysicsSystem) -> Self { - Self - } -} - -#[test] -fn setup_teardown() { - run_test::(); -} - -struct HelloShapes { - floor: JPC_BodyID, - sphere: JPC_BodyID, -} - -impl SmokeTest for HelloShapes { - unsafe fn setup(system: *mut JPC_PhysicsSystem) -> Self { - let body_interface = JPC_PhysicsSystem_GetBodyInterface(system); - - let floor_shape = create_box(&JPC_BoxShapeSettings { - HalfExtent: vec3(100.0, 1.0, 100.0), - ..Default::default() - }) - .unwrap(); - - let floor_settings = JPC_BodyCreationSettings { - Position: rvec3(0.0, -1.0, 0.0), - MotionType: JPC_MOTION_TYPE_STATIC, - ObjectLayer: OL_NON_MOVING, - Shape: floor_shape, - ..Default::default() - }; - - let floor = JPC_BodyInterface_CreateBody(body_interface, &floor_settings); - let floor_id = JPC_Body_GetID(floor); - JPC_BodyInterface_AddBody(body_interface, floor_id, JPC_ACTIVATION_DONT_ACTIVATE); - - let sphere_shape = create_sphere(&JPC_SphereShapeSettings { - Radius: 0.5, - ..Default::default() - }) - .unwrap(); - - let sphere_settings = JPC_BodyCreationSettings { - Position: rvec3(0.0, 2.0, 0.0), - MotionType: JPC_MOTION_TYPE_DYNAMIC, - ObjectLayer: OL_MOVING, - Shape: sphere_shape, - ..Default::default() - }; - - let sphere = JPC_BodyInterface_CreateBody(body_interface, &sphere_settings); - let sphere_id = JPC_Body_GetID(sphere); - JPC_BodyInterface_AddBody(body_interface, sphere_id, JPC_ACTIVATION_ACTIVATE); - - JPC_BodyInterface_SetLinearVelocity(body_interface, sphere_id, vec3(0.0, -5.0, 0.0)); - - Self { - sphere: sphere_id, - floor: floor_id, - } - } - - unsafe fn post_update(&mut self, system: *mut JPC_PhysicsSystem) -> bool { - let body_interface = JPC_PhysicsSystem_GetBodyInterface(system); - JPC_BodyInterface_IsActive(body_interface, self.sphere) - } - - unsafe fn teardown(&mut self, system: *mut JPC_PhysicsSystem) { - let body_interface = JPC_PhysicsSystem_GetBodyInterface(system); - - JPC_BodyInterface_RemoveBody(body_interface, self.floor); - JPC_BodyInterface_DestroyBody(body_interface, self.floor); - - JPC_BodyInterface_RemoveBody(body_interface, self.sphere); - JPC_BodyInterface_DestroyBody(body_interface, self.sphere); - } -} - -#[test] -fn hello_shapes() { - run_test::(); -} - -struct HelloConvexHull { - floor: JPC_BodyID, - hull: JPC_BodyID, -} - -impl SmokeTest for HelloConvexHull { - unsafe fn setup(system: *mut JPC_PhysicsSystem) -> Self { - let body_interface = JPC_PhysicsSystem_GetBodyInterface(system); - - let floor_shape = create_box(&JPC_BoxShapeSettings { - HalfExtent: vec3(100.0, 1.0, 100.0), - ..Default::default() - }) - .unwrap(); - - let floor_settings = JPC_BodyCreationSettings { - Position: rvec3(0.0, -1.0, 0.0), - MotionType: JPC_MOTION_TYPE_STATIC, - ObjectLayer: OL_NON_MOVING, - Shape: floor_shape, - ..Default::default() - }; - - let floor = JPC_BodyInterface_CreateBody(body_interface, &floor_settings); - let floor_id = JPC_Body_GetID(floor); - JPC_BodyInterface_AddBody(body_interface, floor_id, JPC_ACTIVATION_DONT_ACTIVATE); - - let mut rng = rand::thread_rng(); - let mut points = Vec::with_capacity(200); - for _ in 0..200 { - let theta = rng.gen_range(0.0..PI); - let phi = rng.gen_range(0.0..TAU); - points.push(vec3( - theta.sin() * phi.cos(), - theta.sin() * phi.sin(), - theta.cos(), - )); - } - - let hull_shape = create_convex_hull(&JPC_ConvexHullShapeSettings { - Points: points.as_ptr(), - PointsLen: points.len(), - ..Default::default() - }) - .unwrap(); - - let hull_settings = JPC_BodyCreationSettings { - Position: rvec3(0.0, 2.0, 0.0), - MotionType: JPC_MOTION_TYPE_DYNAMIC, - ObjectLayer: OL_MOVING, - Shape: hull_shape, - ..Default::default() - }; - - let hull = JPC_BodyInterface_CreateBody(body_interface, &hull_settings); - let hull_id = JPC_Body_GetID(hull); - JPC_BodyInterface_AddBody(body_interface, hull_id, JPC_ACTIVATION_ACTIVATE); - - JPC_BodyInterface_SetLinearVelocity(body_interface, hull_id, vec3(0.0, -5.0, 0.0)); - - Self { - hull: hull_id, - floor: floor_id, - } - } - - unsafe fn post_update(&mut self, system: *mut JPC_PhysicsSystem) -> bool { - let body_interface = JPC_PhysicsSystem_GetBodyInterface(system); - JPC_BodyInterface_IsActive(body_interface, self.hull) - } - - unsafe fn teardown(&mut self, system: *mut JPC_PhysicsSystem) { - let body_interface = JPC_PhysicsSystem_GetBodyInterface(system); - - JPC_BodyInterface_RemoveBody(body_interface, self.floor); - JPC_BodyInterface_DestroyBody(body_interface, self.floor); - - JPC_BodyInterface_RemoveBody(body_interface, self.hull); - JPC_BodyInterface_DestroyBody(body_interface, self.hull); - } -} - -#[test] -fn hello_convex_hull() { - run_test::(); -} - -struct NarrowPhaseRayCast { - sphere: JPC_BodyID, -} - -impl SmokeTest for NarrowPhaseRayCast { - unsafe fn setup(system: *mut JPC_PhysicsSystem) -> Self { - let body_interface = JPC_PhysicsSystem_GetBodyInterface(system); - - let sphere_shape = create_sphere(&JPC_SphereShapeSettings { - Radius: 0.5, - ..Default::default() - }) - .unwrap(); - - let sphere_settings = JPC_BodyCreationSettings { - Position: rvec3(0.0, 2.0, 0.0), - MotionType: JPC_MOTION_TYPE_DYNAMIC, - ObjectLayer: OL_MOVING, - Shape: sphere_shape, - ..Default::default() - }; - - let sphere = JPC_BodyInterface_CreateBody(body_interface, &sphere_settings); - let sphere_id = JPC_Body_GetID(sphere); - JPC_BodyInterface_AddBody(body_interface, sphere_id, JPC_ACTIVATION_ACTIVATE); - - let query = JPC_PhysicsSystem_GetNarrowPhaseQuery(system); - - let mut args = JPC_NarrowPhaseQuery_CastRayArgs { - Ray: JPC_RRayCast { - Origin: rvec3(1.0, 2.0, 0.0), - Direction: vec3(-2.0, 0.0, 0.0), - }, - ..mem::zeroed() - }; - let hit = JPC_NarrowPhaseQuery_CastRay(query, &mut args); - - assert!(hit, "ray should hit the sphere"); - assert!( - (args.Result.Fraction - 0.25).abs() < 0.01, - "ray should hit at around 0.25 fraction" - ); - - Self { sphere: sphere_id } - } - - unsafe fn post_update(&mut self, _system: *mut JPC_PhysicsSystem) -> bool { - false - } - - unsafe fn teardown(&mut self, system: *mut JPC_PhysicsSystem) { - let body_interface = JPC_PhysicsSystem_GetBodyInterface(system); - - JPC_BodyInterface_RemoveBody(body_interface, self.sphere); - JPC_BodyInterface_DestroyBody(body_interface, self.sphere); - } -} - -#[test] -fn narrow_phase_ray_cast() { - run_test::(); -} From 9714a68b88d6e00c60c9ded089bfeded335e05e5 Mon Sep 17 00:00:00 2001 From: twodreams Date: Mon, 16 Mar 2026 18:16:32 -0300 Subject: [PATCH 3/4] refactor: Move PhysicsState from global singleton to per-module Each module now owns its own Arc, ensuring: - Module A cannot access Module B's physics worlds - Physics resources are automatically cleaned up when a module is dropped - Multiple database instances can run independent physics simulations The PhysicsState flows through: ModuleCommon -> InstanceEnv -> host functions. Removed the global static PHYSICS singleton and physics() accessor. Co-Authored-By: Claude Opus 4.6 --- Cargo.lock | 15 ++--- crates/core/src/host/instance_env.rs | 4 +- crates/core/src/host/module_common.rs | 11 +++- crates/core/src/host/physics.rs | 8 --- crates/core/src/host/v8/mod.rs | 17 +++-- .../src/host/wasm_common/module_host_actor.rs | 9 +-- .../src/host/wasmtime/wasm_instance_env.rs | 64 ++++++++++--------- 7 files changed, 70 insertions(+), 58 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index adacb3a26ac..533758b9160 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2567,15 +2567,15 @@ dependencies = [ [[package]] name = "glam" -version = "0.27.0" +version = "0.29.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e05e7e6723e3455f4818c7b26e855439f7546cf617ef669d1adedb8669e5cb9" +checksum = "8babf46d4c1c9d92deac9f7be466f76dfc4482b6452fc5024b5e8daf6ffeb3ee" [[package]] name = "glam" -version = "0.29.3" +version = "0.30.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8babf46d4c1c9d92deac9f7be466f76dfc4482b6452fc5024b5e8daf6ffeb3ee" +checksum = "19fc433e8437a212d1b6f1e68c7824af3aed907da60afa994e7f542d18d12aa9" [[package]] name = "glob" @@ -3549,10 +3549,10 @@ dependencies = [ [[package]] name = "joltc-sys" version = "0.3.1+Jolt-5.0.0" +source = "git+https://github.com/pbisogno/jolt-rust?branch=feature%2Fspacetimedb#781042924ea20873b9698c9fe9d0e8616eb44ff3" dependencies = [ "anyhow", "bindgen 0.69.5", - "cc", "cmake", "walkdir", ] @@ -6782,10 +6782,9 @@ dependencies = [ [[package]] name = "rolt" version = "0.3.1+Jolt-5.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd0675375c16f769f5c726fecd4ffea39f1ee4ebb46b9060d7dd6a58db5d3961" +source = "git+https://github.com/pbisogno/jolt-rust?branch=feature%2Fspacetimedb#781042924ea20873b9698c9fe9d0e8616eb44ff3" dependencies = [ - "glam 0.27.0", + "glam 0.30.10", "joltc-sys", "paste", ] diff --git a/crates/core/src/host/instance_env.rs b/crates/core/src/host/instance_env.rs index 0efcd59014c..3e2358c06e6 100644 --- a/crates/core/src/host/instance_env.rs +++ b/crates/core/src/host/instance_env.rs @@ -40,6 +40,7 @@ use std::vec::IntoIter; pub struct InstanceEnv { pub replica_ctx: Arc, pub scheduler: Scheduler, + pub physics: Arc, pub tx: TxSlot, /// The timestamp the current function began running. pub start_time: Timestamp, @@ -224,10 +225,11 @@ impl ChunkedWriter { // Generic 'instance environment' delegated to from various host types. impl InstanceEnv { - pub fn new(replica_ctx: Arc, scheduler: Scheduler) -> Self { + pub fn new(replica_ctx: Arc, scheduler: Scheduler, physics: Arc) -> Self { Self { replica_ctx, scheduler, + physics, tx: TxSlot::default(), start_time: Timestamp::now(), start_instant: Instant::now(), diff --git a/crates/core/src/host/module_common.rs b/crates/core/src/host/module_common.rs index bff864759f3..7fbc95ea577 100644 --- a/crates/core/src/host/module_common.rs +++ b/crates/core/src/host/module_common.rs @@ -5,6 +5,7 @@ use crate::{ energy::EnergyMonitor, host::{ module_host::ModuleInfo, + physics::PhysicsState, wasm_common::{module_host_actor::DescribeError, DESCRIBE_MODULE_DUNDER}, Scheduler, }, @@ -19,6 +20,7 @@ use std::sync::Arc; pub fn build_common_module_from_raw( mcc: ModuleCreationContext, raw_def: RawModuleDef, + physics: Arc, ) -> Result { // Perform a bunch of validation on the raw definition. let def: ModuleDef = raw_def.try_into()?; @@ -37,7 +39,7 @@ pub fn build_common_module_from_raw( replica_ctx.subscriptions.clone(), ); - Ok(ModuleCommon::new(replica_ctx, mcc.scheduler, info, mcc.energy_monitor)) + Ok(ModuleCommon::new(replica_ctx, mcc.scheduler, info, mcc.energy_monitor, physics)) } /// Non-runtime-specific parts of a module. @@ -47,6 +49,7 @@ pub(crate) struct ModuleCommon { scheduler: Scheduler, info: Arc, energy_monitor: Arc, + physics: Arc, } impl ModuleCommon { @@ -56,12 +59,14 @@ impl ModuleCommon { scheduler: Scheduler, info: Arc, energy_monitor: Arc, + physics: Arc, ) -> Self { Self { replica_context, scheduler, info, energy_monitor, + physics, } } @@ -89,6 +94,10 @@ impl ModuleCommon { pub fn scheduler(&self) -> &Scheduler { &self.scheduler } + + pub fn physics(&self) -> &Arc { + &self.physics + } } /// Runs the describer of modules in `run` and does some logging around it. diff --git a/crates/core/src/host/physics.rs b/crates/core/src/host/physics.rs index e351a45a044..b53863c545a 100644 --- a/crates/core/src/host/physics.rs +++ b/crates/core/src/host/physics.rs @@ -663,11 +663,3 @@ impl Default for PhysicsState { unsafe impl Send for PhysicsState {} unsafe impl Sync for PhysicsState {} -/// Global physics state, shared across all module instances. -static PHYSICS: once_cell::sync::Lazy = - once_cell::sync::Lazy::new(PhysicsState::new); - -/// Get the global physics state. -pub fn physics() -> &'static PhysicsState { - &PHYSICS -} diff --git a/crates/core/src/host/v8/mod.rs b/crates/core/src/host/v8/mod.rs index b611fcaef26..093e5cab86b 100644 --- a/crates/core/src/host/v8/mod.rs +++ b/crates/core/src/host/v8/mod.rs @@ -524,6 +524,7 @@ fn startup_instance_worker<'scope>( scope: &mut PinScope<'scope, '_>, program: Arc, module_or_mcc: Either, + physics: Arc, ) -> anyhow::Result<(HookFunctions<'scope>, ModuleCommon)> { let hook_functions = catch_exception(scope, |scope| { // Start-up the user's module. @@ -542,7 +543,7 @@ fn startup_instance_worker<'scope>( let def = extract_description(scope, &hook_functions, &mcc.replica_ctx)?; // Validate and create a common module from the raw definition. - build_common_module_from_raw(mcc, def)? + build_common_module_from_raw(mcc, def, physics)? } }; @@ -599,11 +600,15 @@ async fn spawn_instance_worker( scope_with_context!(let scope, &mut isolate, Context::new(scope, Default::default())); // Setup the instance environment. - let (replica_ctx, scheduler) = match &module_or_mcc { - Either::Left(module) => (module.replica_ctx(), module.scheduler()), - Either::Right(mcc) => (&mcc.replica_ctx, &mcc.scheduler), + let early_physics; + let (replica_ctx, scheduler, physics) = match &module_or_mcc { + Either::Left(module) => (module.replica_ctx(), module.scheduler(), module.physics()), + Either::Right(mcc) => { + early_physics = Arc::new(crate::host::physics::PhysicsState::new()); + (&mcc.replica_ctx, &mcc.scheduler, &early_physics) + } }; - let instance_env = InstanceEnv::new(replica_ctx.clone(), scheduler.clone()); + let instance_env = InstanceEnv::new(replica_ctx.clone(), scheduler.clone(), physics.clone()); scope.set_slot(JsInstanceEnv::new(instance_env)); catch_exception(scope, |scope| Ok(builtins::evaluate_builtins(scope)?)) @@ -617,7 +622,7 @@ async fn spawn_instance_worker( log::error!("startup result receiver disconnected"); }) }; - let (hooks, module_common) = match startup_instance_worker(scope, program, module_or_mcc) { + let (hooks, module_common) = match startup_instance_worker(scope, program, module_or_mcc, physics.clone()) { Err(err) => { // There was some error in module setup. // Return the error and terminate the worker. diff --git a/crates/core/src/host/wasm_common/module_host_actor.rs b/crates/core/src/host/wasm_common/module_host_actor.rs index 654c081c4c6..81351939473 100644 --- a/crates/core/src/host/wasm_common/module_host_actor.rs +++ b/crates/core/src/host/wasm_common/module_host_actor.rs @@ -375,13 +375,14 @@ impl WasmModuleHostActor { func_names }; let uninit_instance = module.instantiate_pre()?; - let instance_env = InstanceEnv::new(mcc.replica_ctx.clone(), mcc.scheduler.clone()); + let physics = Arc::new(crate::host::physics::PhysicsState::new()); + let instance_env = InstanceEnv::new(mcc.replica_ctx.clone(), mcc.scheduler.clone(), physics.clone()); let mut instance = uninit_instance.instantiate(instance_env, &func_names)?; let desc = instance.extract_descriptions()?; - // Validate and create a common module rom the raw definition. - let common = build_common_module_from_raw(mcc, desc)?; + // Validate and create a common module from the raw definition. + let common = build_common_module_from_raw(mcc, desc, physics)?; let func_names = Arc::new(func_names); let module = WasmModuleHostActor { @@ -422,7 +423,7 @@ impl WasmModuleHostActor { pub fn create_instance(&self) -> WasmModuleInstance { let common = &self.common; - let env = InstanceEnv::new(common.replica_ctx().clone(), common.scheduler().clone()); + let env = InstanceEnv::new(common.replica_ctx().clone(), common.scheduler().clone(), common.physics().clone()); // this shouldn't fail, since we already called module.create_instance() // before and it didn't error, and ideally they should be deterministic let mut instance = self diff --git a/crates/core/src/host/wasmtime/wasm_instance_env.rs b/crates/core/src/host/wasmtime/wasm_instance_env.rs index ac69ea6d0d0..2d5dd98c746 100644 --- a/crates/core/src/host/wasmtime/wasm_instance_env.rs +++ b/crates/core/src/host/wasmtime/wasm_instance_env.rs @@ -1956,13 +1956,13 @@ impl WasmInstanceEnv { out: WasmPtr, ) -> RtResult { Self::cvt_ret::(caller, AbiCall::PhysicsCreateWorld, out, |caller| { - let (mem, _env) = Self::mem_env(caller); + let (mem, env) = Self::mem_env(caller); let gravity_bytes = mem.deref_slice(gravity_ptr, 12)?; // 3 * f32 let gx = f32::from_le_bytes(gravity_bytes[0..4].try_into().unwrap()); let gy = f32::from_le_bytes(gravity_bytes[4..8].try_into().unwrap()); let gz = f32::from_le_bytes(gravity_bytes[8..12].try_into().unwrap()); - let world_id = crate::host::physics::physics().create_world([gx, gy, gz]); + let world_id = env.instance_env.physics.create_world([gx, gy, gz]); Ok(world_id) }) } @@ -1974,8 +1974,8 @@ impl WasmInstanceEnv { caller: Caller<'_, Self>, world_id: u32, ) -> RtResult { - Self::cvt::(caller, AbiCall::PhysicsDestroyWorld, |_caller| { - crate::host::physics::physics().destroy_world(world_id); + Self::cvt::(caller, AbiCall::PhysicsDestroyWorld, |caller| { + caller.data().instance_env.physics.destroy_world(world_id); Ok(()) }) } @@ -2002,7 +2002,7 @@ impl WasmInstanceEnv { out: WasmPtr, ) -> RtResult { Self::cvt_ret::(caller, AbiCall::PhysicsCreateBody, out, |caller| { - let (mem, _env) = Self::mem_env(caller); + let (mem, env) = Self::mem_env(caller); let data = mem.deref_slice(params_ptr, 54)?; let shape_type = data[0]; @@ -2019,7 +2019,7 @@ impl WasmInstanceEnv { let restitution = read_f32(46); let friction = read_f32(50); - let body_id = crate::host::physics::physics() + let body_id = env.instance_env.physics .create_body( world_id, shape_type, @@ -2043,8 +2043,8 @@ impl WasmInstanceEnv { world_id: u32, body_id: u32, ) -> RtResult { - Self::cvt::(caller, AbiCall::PhysicsDestroyBody, |_caller| { - crate::host::physics::physics().destroy_body(world_id, body_id); + Self::cvt::(caller, AbiCall::PhysicsDestroyBody, |caller| { + caller.data().instance_env.physics.destroy_body(world_id, body_id); Ok(()) }) } @@ -2059,9 +2059,9 @@ impl WasmInstanceEnv { delta_time_bits: u32, num_steps: u32, ) -> RtResult { - Self::cvt::(caller, AbiCall::PhysicsStep, |_caller| { + Self::cvt::(caller, AbiCall::PhysicsStep, |caller| { let dt = f32::from_bits(delta_time_bits); - crate::host::physics::physics().step(world_id, dt, num_steps as i32); + caller.data().instance_env.physics.step(world_id, dt, num_steps as i32); Ok(()) }) } @@ -2076,7 +2076,8 @@ impl WasmInstanceEnv { out_ptr: WasmPtr, ) -> RtResult { Self::cvt::(caller, AbiCall::PhysicsGetBodyTransform, |caller| { - let (pos, rot) = crate::host::physics::physics() + let physics = caller.data().instance_env.physics.clone(); + let (pos, rot) = physics .get_body_transform(world_id, body_id) .ok_or_else(|| WasmError::Wasm(anyhow!("Body not found")))?; @@ -2103,7 +2104,8 @@ impl WasmInstanceEnv { out_ptr: WasmPtr, ) -> RtResult { Self::cvt::(caller, AbiCall::PhysicsGetBodyVelocity, |caller| { - let vel = crate::host::physics::physics() + let physics = caller.data().instance_env.physics.clone(); + let vel = physics .get_body_velocity(world_id, body_id) .ok_or_else(|| WasmError::Wasm(anyhow!("Body not found")))?; @@ -2126,14 +2128,14 @@ impl WasmInstanceEnv { pos_ptr: WasmPtr, ) -> RtResult { Self::cvt::(caller, AbiCall::PhysicsSetBodyPosition, |caller| { - let (mem, _env) = Self::mem_env(caller); + let (mem, env) = Self::mem_env(caller); let data = mem.deref_slice(pos_ptr, 12)?; let pos = [ f32::from_le_bytes(data[0..4].try_into().unwrap()), f32::from_le_bytes(data[4..8].try_into().unwrap()), f32::from_le_bytes(data[8..12].try_into().unwrap()), ]; - crate::host::physics::physics().set_body_position(world_id, body_id, pos); + env.instance_env.physics.set_body_position(world_id, body_id, pos); Ok(()) }) } @@ -2148,14 +2150,14 @@ impl WasmInstanceEnv { vel_ptr: WasmPtr, ) -> RtResult { Self::cvt::(caller, AbiCall::PhysicsSetBodyVelocity, |caller| { - let (mem, _env) = Self::mem_env(caller); + let (mem, env) = Self::mem_env(caller); let data = mem.deref_slice(vel_ptr, 12)?; let vel = [ f32::from_le_bytes(data[0..4].try_into().unwrap()), f32::from_le_bytes(data[4..8].try_into().unwrap()), f32::from_le_bytes(data[8..12].try_into().unwrap()), ]; - crate::host::physics::physics().set_body_velocity(world_id, body_id, vel); + env.instance_env.physics.set_body_velocity(world_id, body_id, vel); Ok(()) }) } @@ -2170,7 +2172,7 @@ impl WasmInstanceEnv { rot_ptr: WasmPtr, ) -> RtResult { Self::cvt::(caller, AbiCall::PhysicsSetBodyRotation, |caller| { - let (mem, _env) = Self::mem_env(caller); + let (mem, env) = Self::mem_env(caller); let data = mem.deref_slice(rot_ptr, 16)?; let rot = [ f32::from_le_bytes(data[0..4].try_into().unwrap()), @@ -2178,7 +2180,7 @@ impl WasmInstanceEnv { f32::from_le_bytes(data[8..12].try_into().unwrap()), f32::from_le_bytes(data[12..16].try_into().unwrap()), ]; - crate::host::physics::physics().set_body_rotation(world_id, body_id, rot); + env.instance_env.physics.set_body_rotation(world_id, body_id, rot); Ok(()) }) } @@ -2193,14 +2195,14 @@ impl WasmInstanceEnv { force_ptr: WasmPtr, ) -> RtResult { Self::cvt::(caller, AbiCall::PhysicsAddForce, |caller| { - let (mem, _env) = Self::mem_env(caller); + let (mem, env) = Self::mem_env(caller); let data = mem.deref_slice(force_ptr, 12)?; let force = [ f32::from_le_bytes(data[0..4].try_into().unwrap()), f32::from_le_bytes(data[4..8].try_into().unwrap()), f32::from_le_bytes(data[8..12].try_into().unwrap()), ]; - crate::host::physics::physics().add_force(world_id, body_id, force); + env.instance_env.physics.add_force(world_id, body_id, force); Ok(()) }) } @@ -2215,14 +2217,14 @@ impl WasmInstanceEnv { impulse_ptr: WasmPtr, ) -> RtResult { Self::cvt::(caller, AbiCall::PhysicsAddImpulse, |caller| { - let (mem, _env) = Self::mem_env(caller); + let (mem, env) = Self::mem_env(caller); let data = mem.deref_slice(impulse_ptr, 12)?; let impulse = [ f32::from_le_bytes(data[0..4].try_into().unwrap()), f32::from_le_bytes(data[4..8].try_into().unwrap()), f32::from_le_bytes(data[8..12].try_into().unwrap()), ]; - crate::host::physics::physics().add_impulse(world_id, body_id, impulse); + env.instance_env.physics.add_impulse(world_id, body_id, impulse); Ok(()) }) } @@ -2246,7 +2248,7 @@ impl WasmInstanceEnv { out: WasmPtr, ) -> RtResult { Self::cvt_ret::(caller, AbiCall::PhysicsCreateVehicle, out, |caller| { - let (mem, _env) = Self::mem_env(caller); + let (mem, env) = Self::mem_env(caller); let params = mem.deref_slice(params_ptr, params_len)?.to_vec(); let pos_data = mem.deref_slice(pos_ptr, 12)?; let rot_data = mem.deref_slice(rot_ptr, 16)?; @@ -2258,7 +2260,7 @@ impl WasmInstanceEnv { let position = [read_f32(&pos_data, 0), read_f32(&pos_data, 4), read_f32(&pos_data, 8)]; let rotation = [read_f32(&rot_data, 0), read_f32(&rot_data, 4), read_f32(&rot_data, 8), read_f32(&rot_data, 12)]; - let vid = crate::host::physics::physics() + let vid = env.instance_env.physics .create_vehicle(world_id, ¶ms, position, rotation) .ok_or_else(|| WasmError::Wasm(anyhow!("Failed to create vehicle")))?; @@ -2278,13 +2280,13 @@ impl WasmInstanceEnv { brake_bits: u32, handbrake_bits: u32, ) -> RtResult { - Self::cvt::(caller, AbiCall::PhysicsSetVehicleInput, |_caller| { + Self::cvt::(caller, AbiCall::PhysicsSetVehicleInput, |caller| { let forward = f32::from_bits(forward_bits); let right = f32::from_bits(right_bits); let brake = f32::from_bits(brake_bits); let handbrake = f32::from_bits(handbrake_bits); - if !crate::host::physics::physics().set_vehicle_input(world_id, vehicle_id, forward, right, brake, handbrake) { + if !caller.data().instance_env.physics.set_vehicle_input(world_id, vehicle_id, forward, right, brake, handbrake) { return Err(WasmError::Wasm(anyhow!("Vehicle or world not found")).into()); } Ok(()) @@ -2302,7 +2304,8 @@ impl WasmInstanceEnv { out: WasmPtr, ) -> RtResult { Self::cvt_ret::(caller, AbiCall::PhysicsGetVehicleState, out, |caller| { - let state_bytes = crate::host::physics::physics() + let physics = caller.data().instance_env.physics.clone(); + let state_bytes = physics .get_vehicle_state(world_id, vehicle_id) .ok_or_else(|| WasmError::Wasm(anyhow!("Vehicle or world not found")))?; @@ -2318,8 +2321,8 @@ impl WasmInstanceEnv { world_id: u32, vehicle_id: u32, ) -> RtResult { - Self::cvt::(caller, AbiCall::PhysicsDestroyVehicle, |_caller| { - if !crate::host::physics::physics().destroy_vehicle(world_id, vehicle_id) { + Self::cvt::(caller, AbiCall::PhysicsDestroyVehicle, |caller| { + if !caller.data().instance_env.physics.destroy_vehicle(world_id, vehicle_id) { return Err(WasmError::Wasm(anyhow!("Vehicle or world not found")).into()); } Ok(()) @@ -2364,7 +2367,8 @@ impl WasmInstanceEnv { out: WasmPtr, ) -> RtResult { Self::cvt_ret::(caller, AbiCall::PhysicsGetAllBodyTransforms, out, |caller| { - let transforms = crate::host::physics::physics() + let physics = caller.data().instance_env.physics.clone(); + let transforms = physics .get_all_body_transforms(world_id) .ok_or_else(|| WasmError::Wasm(anyhow!("World not found")))?; From 3d9dbc01cbff7bdb7759068c6acb9f9805e3f792 Mon Sep 17 00:00:00 2001 From: twodreams Date: Mon, 16 Mar 2026 19:52:56 -0300 Subject: [PATCH 4/4] fix: Update rolt API calls and fix borrow issue for compilation - Replace as_raw() with raw() to match current rolt/joltc-sys API - Add ? operator for create_body return type (now returns Option) - Fix borrow-then-move issue in v8/mod.rs by cloning physics Arc early - Add build_check.ps1 with MSVC + Strawberry Perl + LLVM setup Co-Authored-By: Claude Opus 4.6 --- build_check.ps1 | 19 +++++++++++++++++++ crates/core/src/host/physics.rs | 24 ++++++++++++------------ crates/core/src/host/v8/mod.rs | 3 ++- 3 files changed, 33 insertions(+), 13 deletions(-) create mode 100644 build_check.ps1 diff --git a/build_check.ps1 b/build_check.ps1 new file mode 100644 index 00000000000..bebc11a6c56 --- /dev/null +++ b/build_check.ps1 @@ -0,0 +1,19 @@ +# Setup MSVC via vcvars64.bat +cmd /c "`"C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat`" >nul 2>&1 && set" | ForEach-Object { + if ($_ -match "^([^=]+)=(.*)$") { + [System.Environment]::SetEnvironmentVariable($matches[1], $matches[2], "Process") + } +} + +# Ensure Strawberry Perl and lld-link are in PATH +$env:PATH = "C:\Strawberry\perl\bin;C:\Strawberry\c\bin;C:\Program Files\LLVM\bin;" + $env:PATH + +Write-Host "=== Perl ===" +& where.exe perl | Select-Object -First 1 +Write-Host "=== link.exe ===" +& where.exe link | Select-Object -First 1 +Write-Host "=== cl.exe ===" +& where.exe cl | Select-Object -First 1 + +Set-Location "C:\Users\Pedro\Documents\SpacetimeDB\SpacetimeDB" +cargo check -p spacetimedb-core 2>&1 diff --git a/crates/core/src/host/physics.rs b/crates/core/src/host/physics.rs index b53863c545a..1e46b510c38 100644 --- a/crates/core/src/host/physics.rs +++ b/crates/core/src/host/physics.rs @@ -292,7 +292,7 @@ impl PhysicsState { // Create body via body interface let bi = world.physics_system.body_interface(); let body = bi.create_body(&body_settings); - let jolt_body_id = body.id(); + let jolt_body_id = body?.id(); // Add to world bi.add_body(jolt_body_id, activation); @@ -353,7 +353,7 @@ impl PhysicsState { let pos = bi.center_of_mass_position(*jolt_id); // Get rotation via raw API let rot = unsafe { - JPC_BodyInterface_GetRotation(bi.as_raw(), jolt_id.raw()) + JPC_BodyInterface_GetRotation(bi.raw(), jolt_id.raw()) }; Some(( @@ -395,7 +395,7 @@ impl PhysicsState { let bi = world.physics_system.body_interface(); let pos = jpc_vec3(position[0], position[1], position[2]); JPC_BodyInterface_SetPosition( - bi.as_raw(), + bi.raw(), jolt_id.raw(), pos, JPC_ACTIVATION_ACTIVATE, @@ -424,7 +424,7 @@ impl PhysicsState { unsafe { let bi = world.physics_system.body_interface(); let vel = jpc_vec3(velocity[0], velocity[1], velocity[2]); - JPC_BodyInterface_SetLinearVelocity(bi.as_raw(), jolt_id.raw(), vel); + JPC_BodyInterface_SetLinearVelocity(bi.raw(), jolt_id.raw(), vel); } true } else { @@ -455,7 +455,7 @@ impl PhysicsState { w: rotation[3], }; JPC_BodyInterface_SetRotation( - bi.as_raw(), + bi.raw(), jolt_id.raw(), rot, JPC_ACTIVATION_ACTIVATE, @@ -484,7 +484,7 @@ impl PhysicsState { unsafe { let bi = world.physics_system.body_interface(); let f = jpc_vec3(force[0], force[1], force[2]); - JPC_BodyInterface_AddForce(bi.as_raw(), jolt_id.raw(), f); + JPC_BodyInterface_AddForce(bi.raw(), jolt_id.raw(), f); } true } else { @@ -509,7 +509,7 @@ impl PhysicsState { unsafe { let bi = world.physics_system.body_interface(); let imp = jpc_vec3(impulse[0], impulse[1], impulse[2]); - JPC_BodyInterface_AddImpulse(bi.as_raw(), jolt_id.raw(), imp); + JPC_BodyInterface_AddImpulse(bi.raw(), jolt_id.raw(), imp); } true } else { @@ -531,7 +531,7 @@ impl PhysicsState { .iter() .map(|(&our_id, &jolt_id)| { let pos = bi.center_of_mass_position(jolt_id); - let rot = unsafe { JPC_BodyInterface_GetRotation(bi.as_raw(), jolt_id.raw()) }; + let rot = unsafe { JPC_BodyInterface_GetRotation(bi.raw(), jolt_id.raw()) }; let vel = bi.linear_velocity(jolt_id); ( our_id, @@ -567,7 +567,7 @@ impl PhysicsState { unsafe { let vparams = params.as_ptr() as *const JPC_VehicleParams; let vid = JPC_PhysicsSystem_CreateVehicle( - world.physics_system.as_raw(), + world.physics_system.raw(), vparams, position[0], position[1], position[2], rotation[0], rotation[1], rotation[2], rotation[3], @@ -594,7 +594,7 @@ impl PhysicsState { unsafe { JPC_PhysicsSystem_SetVehicleInput( - world.physics_system.as_raw(), + world.physics_system.raw(), vehicle_id, forward, right, brake, handbrake, ); @@ -615,7 +615,7 @@ impl PhysicsState { unsafe { let mut state: JPC_VehicleState = std::mem::zeroed(); let ok = JPC_PhysicsSystem_GetVehicleState( - world.physics_system.as_raw(), + world.physics_system.raw(), vehicle_id, &mut state, ); @@ -646,7 +646,7 @@ impl PhysicsState { unsafe { JPC_PhysicsSystem_DestroyVehicle( - world.physics_system.as_raw(), + world.physics_system.raw(), vehicle_id, ); } diff --git a/crates/core/src/host/v8/mod.rs b/crates/core/src/host/v8/mod.rs index 093e5cab86b..747361ec79b 100644 --- a/crates/core/src/host/v8/mod.rs +++ b/crates/core/src/host/v8/mod.rs @@ -608,6 +608,7 @@ async fn spawn_instance_worker( (&mcc.replica_ctx, &mcc.scheduler, &early_physics) } }; + let physics_cloned = physics.clone(); let instance_env = InstanceEnv::new(replica_ctx.clone(), scheduler.clone(), physics.clone()); scope.set_slot(JsInstanceEnv::new(instance_env)); @@ -622,7 +623,7 @@ async fn spawn_instance_worker( log::error!("startup result receiver disconnected"); }) }; - let (hooks, module_common) = match startup_instance_worker(scope, program, module_or_mcc, physics.clone()) { + let (hooks, module_common) = match startup_instance_worker(scope, program, module_or_mcc, physics_cloned) { Err(err) => { // There was some error in module setup. // Return the error and terminate the worker.

            y&+wp3mU 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

        3. %7K9(ewwvKR298%WDkZz$7`L$u4XK4z3SAUlm6 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�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 z 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

        K7UpUK$>l@ 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 E2O(~oYXATM diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/gradle/wrapper/gradle-wrapper.properties b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index 3ffebc84b17..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,5 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip -distributionPath=wrapper/dists -zipStorePath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/gradlew b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/gradlew deleted file mode 100644 index 4f906e0c811..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/gradlew +++ /dev/null @@ -1,185 +0,0 @@ -#!/usr/bin/env sh - -# -# Copyright 2015 the original author or authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -############################################################################## -## -## Gradle start up script for UN*X -## -############################################################################## - -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null - -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" - -warn () { - echo "$*" -} - -die () { - echo - echo "$*" - echo - exit 1 -} - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." -fi - -# Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi -fi - -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi - -# For Cygwin or MSYS, switch paths to Windows format before running java -if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi - # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" - fi - i=`expr $i + 1` - done - case $i in - 0) set -- ;; - 1) set -- "$args0" ;; - 2) set -- "$args0" "$args1" ;; - 3) set -- "$args0" "$args1" "$args2" ;; - 4) set -- "$args0" "$args1" "$args2" "$args3" ;; - 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac -fi - -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=`save "$@"` - -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" - -exec "$JAVACMD" "$@" diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/gradlew.bat b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/gradlew.bat deleted file mode 100644 index 107acd32c4e..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/gradlew.bat +++ /dev/null @@ -1,89 +0,0 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/settings.gradle b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/settings.gradle deleted file mode 100644 index 35a66bb3769..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/Android/settings.gradle +++ /dev/null @@ -1,10 +0,0 @@ -dependencyResolutionManagement { - repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) - repositories { - google() - mavenCentral() - } -} -rootProject.name = "Jolt Physics" -include ':UnitTests' -include ':PerformanceTest' diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/CMakeLists.txt b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/CMakeLists.txt deleted file mode 100644 index 830453f8093..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/CMakeLists.txt +++ /dev/null @@ -1,319 +0,0 @@ -cmake_minimum_required(VERSION 3.16 FATAL_ERROR) - -project(JoltPhysics CXX) - -# When turning this option on, the library will be compiled using doubles for positions. This allows for much bigger worlds. -option(DOUBLE_PRECISION "Use double precision math" OFF) - -# When turning this option on, the library will be compiled with debug symbols -option(GENERATE_DEBUG_SYMBOLS "Generate debug symbols" ON) - -# When turning this option on, the library will override the default CMAKE_CXX_FLAGS_DEBUG/RELEASE values, otherwise they will use the platform defaults -option(OVERRIDE_CXX_FLAGS "Override CMAKE_CXX_FLAGS_DEBUG/RELEASE" ON) - -# When turning this option on, the library will be compiled in such a way to attempt to keep the simulation deterministic across platforms -option(CROSS_PLATFORM_DETERMINISTIC "Cross platform deterministic" OFF) - -# When turning this option on, the library will be compiled for ARM (aarch64-linux-gnu), requires compiling with clang -option(CROSS_COMPILE_ARM "Cross compile to aarch64-linux-gnu" OFF) - -# When turning this option on, Jolt will be compiled as a shared library and public symbols will be exported. -option(BUILD_SHARED_LIBS "Compile Jolt as a shared library" OFF) - -# When turning this option on, the library will be compiled with interprocedural optimizations enabled, also known as link-time optimizations or link-time code generation. -# Note that if you turn this on you need to use SET_INTERPROCEDURAL_OPTIMIZATION() or set(CMAKE_INTERPROCEDURAL_OPTIMIZATION ON) to enable LTO specifically for your own project as well. -# If you don't do this you may get an error: /usr/bin/ld: libJolt.a: error adding symbols: file format not recognized -option(INTERPROCEDURAL_OPTIMIZATION "Enable interprocedural optimizations" ON) - -# When turning this on, in Debug and Release mode, the library will emit extra code to ensure that the 4th component of a 3-vector is kept the same as the 3rd component -# and will enable floating point exceptions during simulation to detect divisions by zero. -# Note that this currently only works using MSVC. Clang turns Float2 into a SIMD vector sometimes causing floating point exceptions (the option is ignored). -option(FLOATING_POINT_EXCEPTIONS_ENABLED "Enable floating point exceptions" ON) - -# Number of bits to use in ObjectLayer. Can be 16 or 32. -option(OBJECT_LAYER_BITS "Number of bits in ObjectLayer" 16) - -# Select X86 processor features to use (if everything is off it will be SSE2 compatible) -option(USE_SSE4_1 "Enable SSE4.1" ON) -option(USE_SSE4_2 "Enable SSE4.2" ON) -option(USE_AVX "Enable AVX" ON) -option(USE_AVX2 "Enable AVX2" ON) -option(USE_AVX512 "Enable AVX512" OFF) -option(USE_LZCNT "Enable LZCNT" ON) -option(USE_TZCNT "Enable TZCNT" ON) -option(USE_F16C "Enable F16C" ON) -option(USE_FMADD "Enable FMADD" ON) - -# Enable all warnings -option(ENABLE_ALL_WARNINGS "Enable all warnings and warnings as errors" ON) - -# Setting to periodically trace broadphase stats to help determine if the broadphase layer configuration is optimal -option(TRACK_BROADPHASE_STATS "Track Broadphase Stats" OFF) - -# Setting to periodically trace narrowphase stats to help determine which collision queries could be optimized -option(TRACK_NARROWPHASE_STATS "Track Narrowphase Stats" OFF) - -# Enable the debug renderer in the Debug and Release builds. Note that DEBUG_RENDERER_IN_DISTRIBUTION will override this setting. -option(DEBUG_RENDERER_IN_DEBUG_AND_RELEASE "Enable debug renderer in Debug and Release builds" ON) - -# Setting to enable the debug renderer in all builds. -# Note that enabling this reduces the performance of the library even if you're not drawing anything. -option(DEBUG_RENDERER_IN_DISTRIBUTION "Enable debug renderer in all builds" OFF) - -# Enable the profiler in Debug and Release builds. Note that PROFILER_IN_DISTRIBUTION will override this setting. -option(PROFILER_IN_DEBUG_AND_RELEASE "Enable the profiler in Debug and Release builds" ON) - -# Enable the profiler in all builds. -# Note that enabling this reduces the performance of the library. -option(PROFILER_IN_DISTRIBUTION "Enable the profiler in all builds" OFF) - -# Setting this option will force the library to use malloc/free instead of allowing the user to override the memory allocator -option(DISABLE_CUSTOM_ALLOCATOR "Disable support for a custom memory allocator" OFF) - -include(CMakeDependentOption) - -# Ability to toggle between the static and DLL versions of the MSVC runtime library -# Windows Store only supports the DLL version -cmake_dependent_option(USE_STATIC_MSVC_RUNTIME_LIBRARY "Use the static MSVC runtime library" ON "MSVC;NOT WINDOWS_STORE" OFF) - -# Determine which configurations exist -if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) # Only do this when we're at the top level, see: https://gitlab.kitware.com/cmake/cmake/-/issues/24181 - if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") - set(CMAKE_CONFIGURATION_TYPES "Debug;Release;Distribution") - elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang") - set(CMAKE_CONFIGURATION_TYPES "Debug;Release;ReleaseASAN;ReleaseUBSAN;ReleaseCoverage;Distribution") - endif() -endif() - -if (MSVC) - # Fill in the path to the asan libraries - set(CLANG_LIB_PATH "\"$(VSInstallDir)\\VC\\Tools\\Llvm\\x64\\lib\\clang\\${CMAKE_CXX_COMPILER_VERSION}\\lib\\windows\"") - - # 64 bit architecture - set(CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE "x64") - - # Set runtime library - if (USE_STATIC_MSVC_RUNTIME_LIBRARY) - set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") - endif() - - # Set general compiler flags - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zc:__cplusplus /Gm- /MP /nologo /diagnostics:classic /FC /fp:except- /Zc:inline") - - # Enable warnings - if (ENABLE_ALL_WARNINGS) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Wall /WX") - endif() - - # Optionally generate debug symbols - if (GENERATE_DEBUG_SYMBOLS) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zi") - endif() - - # Remove any existing compiler flag that enables RTTI - string(REPLACE "/GR" "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS}) - - # Set compiler flag for disabling RTTI - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /GR-") - - if ("${CMAKE_VS_PLATFORM_NAME}" STREQUAL "ARM") - # On ARM the exception handling flag is missing which causes warnings - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /EHsc") - endif() - - # Set compiler flags for various configurations - set(CMAKE_CXX_FLAGS_DEBUG "/GS /Od /Ob0 /RTC1") - set(CMAKE_CXX_FLAGS_RELEASE "/GS- /Gy /O2 /Oi /Ot") - set(CMAKE_CXX_FLAGS_DISTRIBUTION "/GS- /Gy /O2 /Oi /Ot") - set(CMAKE_CXX_FLAGS_RELEASEASAN "-fsanitize=address /Od") - set(CMAKE_CXX_FLAGS_RELEASEUBSAN "-fsanitize=undefined,implicit-conversion,float-divide-by-zero,local-bounds -fno-sanitize-recover=all") - set(CMAKE_CXX_FLAGS_RELEASECOVERAGE "-fprofile-instr-generate -fcoverage-mapping") - - # Set linker flags - set(CMAKE_EXE_LINKER_FLAGS "/SUBSYSTEM:WINDOWS /ignore:4221") - if (GENERATE_DEBUG_SYMBOLS) - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /DEBUG") - endif() - if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") - if (CROSS_PLATFORM_DETERMINISTIC) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fp:precise") - else() - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fp:fast") # Clang doesn't use fast math because it cannot be turned off inside a single compilation unit - endif() - elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /showFilenames") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Qunused-arguments") # Clang emits warnings about unused arguments such as /MP and /GL - set(CMAKE_EXE_LINKER_FLAGS_RELEASEASAN "/SUBSYSTEM:CONSOLE /LIBPATH:${CLANG_LIB_PATH} clang_rt.asan-x86_64.lib -wholearchive:clang_rt.asan-x86_64.lib clang_rt.asan_cxx-x86_64.lib -wholearchive:clang_rt.asan_cxx-x86_64.lib") - set(CMAKE_EXE_LINKER_FLAGS_RELEASEUBSAN "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /LIBPATH:${CLANG_LIB_PATH}") - set(CMAKE_EXE_LINKER_FLAGS_RELEASECOVERAGE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /LIBPATH:${CLANG_LIB_PATH}") - endif() -else() - # Enable warnings - if (ENABLE_ALL_WARNINGS) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Werror") - endif() - - # Optionally generate debug symbols - if (GENERATE_DEBUG_SYMBOLS) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g") - endif() - - if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") - # Also disable -Wstringop-overflow or it will generate false positives that can't be disabled from code when link-time optimizations are enabled - # Also turn off automatic fused multiply add contractions, there doesn't seem to be a way to do this selectively through the macro JPH_PRECISE_MATH_OFF - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-stringop-overflow -ffp-contract=off") - else() - # Do not use -ffast-math since it cannot be turned off in a single compilation unit under clang, see Core.h - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ffp-model=precise") - - # On clang 14 and later we can turn off float contraction through a pragma, older versions and deterministic versions need it off always, see Core.h - if (CMAKE_CXX_COMPILER_VERSION LESS 14 OR CROSS_PLATFORM_DETERMINISTIC) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ffp-contract=off") - endif() - endif() - - # See https://github.com/jrouwe/JoltPhysics/issues/922. When compiling with DOUBLE_PRECISION=YES and CMAKE_OSX_DEPLOYMENT_TARGET=10.12 clang triggers a warning that we silence here. - if ("${CMAKE_SYSTEM_NAME}" MATCHES "Darwin" AND "${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -faligned-allocation") - endif() - - # Cross compiler flags - if (CROSS_COMPILE_ARM) - set(CMAKE_CXX_FLAGS "--target=aarch64-linux-gnu ${CMAKE_CXX_FLAGS}") - endif() - - # Set compiler flags for various configurations - if (OVERRIDE_CXX_FLAGS) - set(CMAKE_CXX_FLAGS_DEBUG "") - set(CMAKE_CXX_FLAGS_RELEASE "-O3") - endif() - set(CMAKE_CXX_FLAGS_DISTRIBUTION "${CMAKE_CXX_FLAGS_RELEASE}") - set(CMAKE_CXX_FLAGS_RELEASEASAN "-fsanitize=address") - set(CMAKE_CXX_FLAGS_RELEASEUBSAN "-fsanitize=undefined,implicit-conversion,float-divide-by-zero,local-bounds -fno-sanitize-recover=all") - set(CMAKE_CXX_FLAGS_RELEASECOVERAGE "-O0 -DJPH_NO_FORCE_INLINE -fprofile-instr-generate -fcoverage-mapping") - - # Set linker flags - if (NOT ("${CMAKE_SYSTEM_NAME}" STREQUAL "Windows")) - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -pthread") - endif() -endif() - -# Set linker flags -set(CMAKE_EXE_LINKER_FLAGS_DISTRIBUTION "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") - -# Enable link time optimization in Release and Distribution mode if requested and available -function(SET_INTERPROCEDURAL_OPTIMIZATION) - set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE OFF PARENT_SCOPE) - set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_DISTRIBUTION OFF PARENT_SCOPE) - - # On ARM, whole program optimization triggers an internal compiler error during code gen, so we don't turn it on - if (INTERPROCEDURAL_OPTIMIZATION AND NOT ("${CMAKE_VS_PLATFORM_NAME}" STREQUAL "ARM64") AND NOT ("${CMAKE_VS_PLATFORM_NAME}" STREQUAL "ARM")) - include(CheckIPOSupported) - check_ipo_supported(RESULT IS_IPO_SUPPORTED OUTPUT IPO_CHECK_OUTPUT) - - if (IS_IPO_SUPPORTED) - set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE ON PARENT_SCOPE) - set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_DISTRIBUTION ON PARENT_SCOPE) - else() - message(WARNING "Interprocedural optimizations are not supported: ${IPO_CHECK_OUTPUT}") - endif() - endif() -endfunction() -SET_INTERPROCEDURAL_OPTIMIZATION() - -# Set repository root -set(PHYSICS_REPO_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/../) - -# Make Jolt Library -include(${PHYSICS_REPO_ROOT}/Jolt/Jolt.cmake) -if (XCODE) - # Ensure that we enable SSE4.2 for the x86_64 build, XCode builds multiple architectures - set_property(TARGET Jolt PROPERTY XCODE_ATTRIBUTE_OTHER_CPLUSPLUSFLAGS[arch=x86_64] "$(inherited) -msse4.2 -mpopcnt") -endif() - -# Install Jolt library and includes -install(TARGETS Jolt DESTINATION lib) -foreach(SRC_FILE ${JOLT_PHYSICS_SRC_FILES}) - string(REPLACE ${PHYSICS_REPO_ROOT} "" RELATIVE_SRC_FILE ${SRC_FILE}) - get_filename_component(DESTINATION_PATH ${RELATIVE_SRC_FILE} DIRECTORY) - if (NOT RELATIVE_SRC_FILE MATCHES "\.cpp") - install(FILES ${SRC_FILE} DESTINATION include/${DESTINATION_PATH}) - endif() -endforeach() - -# Check if we're the root CMakeLists.txt, if not we are included by another CMake file and we should disable everything except for the main library -if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) - # Ability to turn ON/OFF individual applications - option(TARGET_UNIT_TESTS "Build Unit Tests" ON) - option(TARGET_HELLO_WORLD "Build Hello World" ON) - option(TARGET_PERFORMANCE_TEST "Build Performance Test" ON) - option(TARGET_SAMPLES "Build Samples" ON) - option(TARGET_VIEWER "Build JoltViewer" ON) - - if (TARGET_UNIT_TESTS) - # Create UnitTests executable - include(${PHYSICS_REPO_ROOT}/UnitTests/UnitTests.cmake) - add_executable(UnitTests ${UNIT_TESTS_SRC_FILES}) - target_include_directories(UnitTests PUBLIC ${UNIT_TESTS_ROOT}) - target_link_libraries(UnitTests LINK_PUBLIC Jolt) - target_precompile_headers(UnitTests PRIVATE ${JOLT_PHYSICS_ROOT}/Jolt.h) - if (MSVC) - target_link_options(UnitTests PUBLIC "/SUBSYSTEM:CONSOLE") - endif() - if (IOS) - # Set the bundle information - set_property(TARGET UnitTests PROPERTY MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/iOS/UnitTestsInfo.plist") - set_property(TARGET UnitTests PROPERTY XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "com.joltphysics.unittests") - endif() - if (XCODE) - # Ensure that we enable SSE4.2 for the x86_64 build, XCode builds multiple architectures - set_property(TARGET UnitTests PROPERTY XCODE_ATTRIBUTE_OTHER_CPLUSPLUSFLAGS[arch=x86_64] "$(inherited) -msse4.2 -mpopcnt") - endif() - - # Register unit tests as a test so that it can be run with: - # ctest --output-on-failure - enable_testing() - add_test(UnitTests UnitTests) - endif() - - if (NOT "${CMAKE_SYSTEM_NAME}" STREQUAL "WindowsStore") - if (TARGET_HELLO_WORLD) - # Example 'Hello World' application - include(${PHYSICS_REPO_ROOT}/HelloWorld/HelloWorld.cmake) - add_executable(HelloWorld ${HELLO_WORLD_SRC_FILES}) - target_include_directories(HelloWorld PUBLIC ${HELLO_WORLD_ROOT}) - target_link_libraries(HelloWorld LINK_PUBLIC Jolt) - if (MSVC) - target_link_options(HelloWorld PUBLIC "/SUBSYSTEM:CONSOLE") - endif() - endif() - - if (TARGET_PERFORMANCE_TEST) - # Performance Test application - include(${PHYSICS_REPO_ROOT}/PerformanceTest/PerformanceTest.cmake) - add_executable(PerformanceTest ${PERFORMANCE_TEST_SRC_FILES}) - target_include_directories(PerformanceTest PUBLIC ${PERFORMANCE_TEST_ROOT}) - target_link_libraries(PerformanceTest LINK_PUBLIC Jolt) - if (MSVC) - target_link_options(PerformanceTest PUBLIC "/SUBSYSTEM:CONSOLE") - endif() - set_property(TARGET PerformanceTest PROPERTY VS_DEBUGGER_WORKING_DIRECTORY "${PHYSICS_REPO_ROOT}") - - # Copy the assets folder - add_custom_command(TARGET PerformanceTest PRE_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${PHYSICS_REPO_ROOT}/Assets/ $/Assets/) - endif() - endif() - - if ("${CMAKE_SYSTEM_NAME}" STREQUAL "Windows" AND NOT ("${CMAKE_VS_PLATFORM_NAME}" STREQUAL "ARM")) # ARM 32-bit is missing dinput8.lib - # Windows only targets - if (TARGET_SAMPLES OR TARGET_VIEWER) - include(${PHYSICS_REPO_ROOT}/TestFramework/TestFramework.cmake) - endif() - if (TARGET_SAMPLES) - include(${PHYSICS_REPO_ROOT}/Samples/Samples.cmake) - endif() - if (TARGET_VIEWER) - include(${PHYSICS_REPO_ROOT}/JoltViewer/JoltViewer.cmake) - endif() - endif() -endif() diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/README.md b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/README.md deleted file mode 100644 index 637477e5b55..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/README.md +++ /dev/null @@ -1,237 +0,0 @@ -# Building and Using Jolt Physics - -## Build Types - -Each platform supports multiple build targets - -- Debug - Debug version of the library, turns on asserts -- Release - Release version of the library, no asserts but includes profiling support and can draw the world and simulation properties -- ReleaseASAN - As Release but turns on Address Sanitizer (clang only) to find bugs -- ReleaseUBSAN - As Release but turns on Undefined Behavior Sanitizer (clang only) to find bugs -- ReleaseCoverage - As Release but turns on Coverage reporting (clang only) to find which areas of the code are not executed -- Distribution - Shippable version of the library, turns off all debugging support - -## Includes - -The Jolt headers don't include Jolt/Jolt.h. Always include Jolt/Jolt.h before including any other Jolt header. -You can use Jolt/Jolt.h in your precompiled header to speed up compilation. - -## Defines - -There are a number of user configurable defines that turn on/off certain features: -

        - General Options (click to see more) -
          -
        • JPH_PROFILE_ENABLED - Turns on the internal profiler.
        • -
        • JPH_EXTERNAL_PROFILE - Turns on the internal profiler but forwards the information to a user defined external system (see Profiler.h).
        • -
        • JPH_DEBUG_RENDERER - Adds support to draw lines and triangles, used to be able to debug draw the state of the world.
        • -
        • JPH_DISABLE_TEMP_ALLOCATOR - Disables the temporary memory allocator, used mainly to allow ASAN to do its job.
        • -
        • JPH_DISABLE_CUSTOM_ALLOCATOR - Disables the ability to override the memory allocator.
        • -
        • JPH_FLOATING_POINT_EXCEPTIONS_ENABLED - Turns on division by zero and invalid floating point exception support in order to detect bugs (Windows only).
        • -
        • JPH_CROSS_PLATFORM_DETERMINISTIC - Turns on behavior to attempt cross platform determinism. If this is set, JPH_USE_FMADD is ignored.
        • -
        • JPH_DOUBLE_PRECISION - Compiles the library so that all positions are stored in doubles instead of floats. This makes larger worlds possible.
        • -
        -
        - -
        - CPU Instruction Sets (click to see more) -
          -
        • JPH_USE_SSE4_1 - Enable SSE4.1 CPU instructions (default: on, x86/x64 only)
        • -
        • JPH_USE_SSE4_2 - Enable SSE4.2 CPU instructions (default: on, x86/x64 only)
        • -
        • JPH_USE_F16C - Enable half float CPU instructions (default: on, x86/x64 only)
        • -
        • JPH_USE_LZCNT - Enable the lzcnt CPU instruction (default: on, x86/x64 only)
        • -
        • JPH_USE_TZCNT - Enable the tzcnt CPU instruction (default: on, x86/x64 only)
        • -
        • JPH_USE_AVX - Enable AVX CPU instructions (default: on, x86/x64 only)
        • -
        • JPH_USE_AVX2 - Enable AVX2 CPU instructions (default: on, x86/x64 only)
        • -
        • JPH_USE_AVX512 - Enable AVX512F+AVX512VL CPU instructions (default: off, x86/x64 only)
        • -
        • JPH_USE_FMADD - Enable fused multiply add CPU instructions (default: on, x86/x64 only)
        • -
        -
        - -## Logging & Asserting - -To override the default trace and assert mechanism install your own custom handlers in Trace and AssertFailed (see IssueReporting.h). - -## Custom Memory Allocator - -To implement your custom memory allocator override Allocate, Free, AlignedAllocate and AlignedFree (see Memory.h). - -## Building - -
        - Windows 10+ -
        • -
          - MSVC CL (default compiler) -
            -
          • Download Visual Studio 2022 (Community or other edition)
          • -
          • Download CMake 3.15+ (https://cmake.org/download/)
          • -
          • Run cmake_vs2022_cl.bat
          • -
          • Open the resulting project file VS2022_CL\JoltPhysics.sln
          • -
          • Compile and run either 'Samples' or 'UnitTests'
          • -
          -
          -
          - MSVC CL - 32 bit -
            -
          • Download Visual Studio 2022 (Community or other edition)
          • -
          • Download CMake 3.15+ (https://cmake.org/download/)
          • -
          • Run cmake_vs2022_cl_32bit.bat
          • -
          • Open the resulting project file VS2022_CL_32BIT\JoltPhysics.sln
          • -
          • Compile and run either 'Samples' or 'UnitTests'
          • -
          -
          -
          - MSVC Clang compiler -
            -
          • Download Visual Studio 2022 (Community or other edition)
          • -
          • Make sure to install "C++ Clang Compiler for Windows 11.0.0+" and "C++ Clang-cl for v142+ build tools (x64/x86)" using the Visual Studio Installer
          • -
          • Download CMake 3.15+ (https://cmake.org/download/)
          • -
          • Run cmake_vs2022_clang.bat
          • -
          • Open the resulting project file VS2022_Clang\JoltPhysics.sln
          • -
          • Compile and run either 'Samples' or 'UnitTests'
          • -
          -
          -
          - MSVC Universal Windows Platform -
            -
          • Download Visual Studio 2022+ (Community or other edition)
          • -
          • Make sure to install "Universal Windows Platform development" using the Visual Studio Installer
          • -
          • Download CMake 3.15+ (https://cmake.org/download/)
          • -
          • Run cmake_vs2022_uwp.bat
          • -
          • Open the resulting project file VS2022_UWP\JoltPhysics.sln
          • -
          • Compile and run 'UnitTests'
          • -
          -
          -
          - MinGW -
            -
          • Follow download instructions for MSYS2 (https://www.msys2.org/)
          • -
          • From the MSYS2 MSYS app run: pacman -S --needed mingw-w64-x86_64-toolchain mingw-w64-x86_64-cmake
          • -
          • From the MSYS2 MINGW x64 app, in the Build folder run: ./cmake_windows_mingw.sh
          • -
          • Run: cmake --build MinGW_Debug
          • -
          • Run: MinGW_Debug/UnitTests.exe
          • -
          -
          -
        -
        - -
        - Linux -
        • -
          - Debian flavor, x64 or ARM64 -
            -
          • Install clang (apt-get install clang)
          • -
          • Install cmake (apt-get install cmake)
          • -
          • Run: ./cmake_linux_clang_gcc.sh
          • -
          • Go to the Linux_Debug folder
          • -
          • Run: make -j$(nproc) && ./UnitTests
          • -
          -
          -
          - Debian flavor, MinGW Cross Compile -
            -
          • This setup can be used to run samples on Linux using wine and vkd3d. Tested on Ubuntu 22.04
          • -
          • Graphics card must support Vulkan and related drivers must be installed
          • -
          • Install mingw-w64 (apt-get install mingw-w64)
          • -
          • Run: update-alternatives --config x86_64-w64-mingw32-g++ (Select /usr/bin/x86_64-w64-mingw32-g++-posix)
          • -
          • Install cmake (apt-get install cmake)
          • -
          • Install wine64 (apt-get install wine64)
          • -
          • Run: export WINEPATH="/usr/x86_64-w64-mingw32/lib;/usr/lib/gcc/x86_64-w64-mingw32/10-posix" (change it based on your environment)
          • -
          • Run: ./cmake_linux_mingw.sh Release (Debug doesn't work)
          • -
          • Go to the MinGW_Release folder
          • -
          • Run: make -j$(nproc) && wine UnitTests.exe
          • -
          • Run: wine Samples.exe
          • -
          -
          -
        -
        - -
        - Android -
          -
        • Install Android Studio 2020.3.1+ (https://developer.android.com/studio/)
        • -
        • Open the 'Android' folder in Android Studio and wait until gradle finishes
        • -
        • Select 'Run' / 'Run...' and 'UnitTests'
        • -
        • If the screen turns green after a while the unit tests succeeded, when red they failed (see the android log for details)
        • -
        -
        - -
        - macOS -
          -
        • Install XCode
        • -
        • Download CMake 3.23+ (https://cmake.org/download/)
        • -
        • Run: ./cmake_xcode_macos.sh
        • -
        • This will open XCode with a newly generated project
        • -
        • Build and run the project
        • -
        • Note that you can also follow the steps in the 'Linux' section if you wish to build without XCode.
        • -
        -
        - -
        - iOS -
          -
        • Install XCode
        • -
        • Download CMake 3.23+ (https://cmake.org/download/)
        • -
        • Run: ./cmake_xcode.ios.sh
        • -
        • This will open XCode with a newly generated project
        • -
        • Build and run the project (note that this will only work in the simulator as the code signing information is not set up)
        • -
        -
        - -## Other Build Tools - -* A vcpkg package is available [here](https://github.com/microsoft/vcpkg/tree/master/ports/joltphysics). -* A xmake package is available [here](https://github.com/xmake-io/xmake-repo/tree/dev/packages/j/joltphysics). -* Jolt has been verified to build with [ninja](https://ninja-build.org/) through CMake. - -## Errors - -### Link Error: File Format Not Recognized - -If you receive the following error when linking: - -``` -/usr/bin/ld: libJolt.a: error adding symbols: file format not recognized -``` - -Then you have not enabled interprocedural optimizations (link time optimizations) for your own application. See the INTERPROCEDURAL_OPTIMIZATION option in CMakeLists.txt. - -### Link Error: Unresolved External Symbol - -If you receive a link error that looks like: - -``` -error LNK2001: unresolved external symbol "public: virtual void __cdecl JPH::ConvexShape::GetSubmergedVolume(...) const" -``` - -you have a mismatch in defines between your own code and the Jolt library. In this case the mismatch is in the define `JPH_DEBUG_RENDERER` which is most likely defined in `Jolt.lib` and not in your own project. In `Debug` and `Release` builds, Jolt by default has `JPH_DEBUG_RENDERER` defined, in `Distribution` it is not defined. The cmake options `DEBUG_RENDERER_IN_DEBUG_AND_RELEASE` and `DEBUG_RENDERER_IN_DISTRIBUTION` override this behavior. - -The `RegisterTypes` function (which you have to call to initialize the library) checks the other important defines and will trace and abort if there are more mismatches. - -### DirectX Error - -The samples use DirectX for the graphics implementation, when attempting to run the samples you may get a DirectX error pop-up which may say "The GPU device instance has been suspended", in your debugger you may see the message "Using the Redistributable D3D12 SDKLayers dll also requires that the latest SDKLayers for Windows 10 is installed.". - -Fix this by enabling "Graphics Tools" which is an optional Windows settings. To enable it you have to press the windows key, search for "Manage Optional Features", and then click "Add a Feature", and install "Graphics Tools". - -### Illegal Instruction Error - -If your CPU doesn't support all of the instructions you'll get an `Illegal instruction` exception. - -On Linux to see what instructions your CPU supports run `lscpu` and then look at the flags section, on Windows you can use a program like [`coreinfo`](https://learn.microsoft.com/en-us/sysinternals/downloads/coreinfo). Once you know what instructions your cpu supports you can configure the project through cmake and for example disable all special instructions: - -``` -./cmake_linux_clang_gcc.sh Release clang++ -DUSE_SSE4_1=OFF -DUSE_SSE4_2=OFF -DUSE_AVX=OFF -DUSE_AVX2=OFF -DUSE_AVX512=OFF -DUSE_LZCNT=OFF -DUSE_TZCNT=OFF -DUSE_F16C=OFF -DUSE_FMADD=OFF -``` - -Note that this example is for Linux but the cmake settings work on Windows too. - -## Doxygen on Windows - -Documentation can be generated through doxygen: - -- Install Doxygen (https://www.doxygen.nl/download.html) -- Run: run_doxygen.bat diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_linux_clang_gcc.sh b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_linux_clang_gcc.sh deleted file mode 100644 index 6d563c4c8da..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_linux_clang_gcc.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/sh - -if [ -z $1 ] -then - BUILD_TYPE=Debug -else - BUILD_TYPE=$1 - shift -fi - -if [ -z $1 ] -then - COMPILER=clang++ -else - COMPILER=$1 - shift -fi - -BUILD_DIR=Linux_$BUILD_TYPE - -echo Usage: ./cmake_linux_clang_gcc.sh [Configuration] [Compiler] -echo "Possible configurations: Debug (default), Release, Distribution, ReleaseUBSAN, ReleaseASAN, ReleaseCoverage" -echo "Possible compilers: clang++, clang++-XX, g++, g++-XX where XX is the version" -echo Generating Makefile for build type \"$BUILD_TYPE\" and compiler \"$COMPILER\" in folder \"$BUILD_DIR\" - -cmake -S . -B $BUILD_DIR -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_COMPILER=$COMPILER "${@}" - -echo Compile by running \"make -j 8 \&\& ./UnitTests\" in folder \"$BUILD_DIR\" diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_linux_mingw.sh b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_linux_mingw.sh deleted file mode 100644 index 700265ae478..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_linux_mingw.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/sh - -if [ -z $1 ] -then - BUILD_TYPE=Release -else - BUILD_TYPE=$1 - shift -fi - -BUILD_DIR=MinGW_$BUILD_TYPE - -echo Usage: ./cmake_linux_mingw.sh [Configuration] -echo "Possible configurations: Debug, Release (default), Distribution" -echo Generating Makefile for build type \"$BUILD_TYPE\" in folder \"$BUILD_DIR\" - -cmake -S . -B $BUILD_DIR -DCMAKE_TOOLCHAIN_FILE=mingw-w64-x86_64.cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE "${@}" - -echo Compile by running \"cmake --build $BUILD_DIR -j 8\" diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2019_cl.bat b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2019_cl.bat deleted file mode 100644 index 3eb4886e6e9..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2019_cl.bat +++ /dev/null @@ -1,3 +0,0 @@ -@echo off -cmake -S . -B VS2019_CL -G "Visual Studio 16 2019" -A x64 %* -echo Open VS2019_CL\JoltPhysics.sln to build the project. diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2019_cl_arm.bat b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2019_cl_arm.bat deleted file mode 100644 index 8c733e056ba..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2019_cl_arm.bat +++ /dev/null @@ -1,3 +0,0 @@ -@echo off -cmake -S . -B VS2019_CL_ARM -G "Visual Studio 16 2019" -A ARM64 %* -echo Open VS2019_CL_ARM\JoltPhysics.sln to build the project. diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2019_cl_arm_32bit.bat b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2019_cl_arm_32bit.bat deleted file mode 100644 index 7e78624a2ec..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2019_cl_arm_32bit.bat +++ /dev/null @@ -1,3 +0,0 @@ -@echo off -cmake -S . -B VS2019_CL_ARM_32BIT -G "Visual Studio 16 2019" -A ARM %* -echo Open VS2019_CL_ARM_32BIT\JoltPhysics.sln to build the project. diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2019_clang.bat b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2019_clang.bat deleted file mode 100644 index e25a36cafb2..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2019_clang.bat +++ /dev/null @@ -1,10 +0,0 @@ -@echo off -cmake -S . -B VS2019_Clang -G "Visual Studio 16 2019" -A x64 -T ClangCL %* -echo: -echo !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -echo Make sure to install: -echo - C++ Clang Compiler for Windows 11.0.0+ -echo - C++ Clang-cl for v142+ build tools (x64/x86) -echo Using the Visual Studio Installer -echo !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -echo Open VS2019_Clang/JoltPhysics.sln to build the project. diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_cl.bat b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_cl.bat deleted file mode 100644 index 4cf9990ee1c..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_cl.bat +++ /dev/null @@ -1,3 +0,0 @@ -@echo off -cmake -S . -B VS2022_CL -G "Visual Studio 17 2022" -A x64 %* -echo Open VS2022_CL\JoltPhysics.sln to build the project. diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_cl_32bit.bat b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_cl_32bit.bat deleted file mode 100644 index 299fd331d04..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_cl_32bit.bat +++ /dev/null @@ -1,3 +0,0 @@ -@echo off -cmake -S . -B VS2022_CL_32BIT -G "Visual Studio 17 2022" -A Win32 -DUSE_SSE4_1=OFF -DUSE_SSE4_2=OFF -DUSE_AVX=OFF -DUSE_AVX2=OFF -DUSE_AVX512=OFF -DUSE_LZCNT=OFF -DUSE_TZCNT=OFF -DUSE_F16C=OFF -DUSE_FMADD=OFF %* -echo Open VS2022_CL_32BIT\JoltPhysics.sln to build the project. diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_cl_arm.bat b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_cl_arm.bat deleted file mode 100644 index 146f01f914a..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_cl_arm.bat +++ /dev/null @@ -1,3 +0,0 @@ -@echo off -cmake -S . -B VS2022_CL_ARM -G "Visual Studio 17 2022" -A ARM64 %* -echo Open VS2022_CL_ARM\JoltPhysics.sln to build the project. diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_cl_arm_32bit.bat b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_cl_arm_32bit.bat deleted file mode 100644 index ad65e5c4295..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_cl_arm_32bit.bat +++ /dev/null @@ -1,3 +0,0 @@ -@echo off -cmake -S . -B VS2022_CL_ARM_32BIT -G "Visual Studio 17 2022" -A ARM %* -echo Open VS2022_CL_ARM_32BIT\JoltPhysics.sln to build the project. diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_cl_cross_platform_deterministic.bat b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_cl_cross_platform_deterministic.bat deleted file mode 100644 index c054c8e8143..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_cl_cross_platform_deterministic.bat +++ /dev/null @@ -1,3 +0,0 @@ -@echo off -cmake -S . -B VS2022_CL_CPD -G "Visual Studio 17 2022" -A x64 -DCROSS_PLATFORM_DETERMINISTIC=ON %* -echo Open VS2022_CL_CPD\JoltPhysics.sln to build the project. diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_cl_double.bat b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_cl_double.bat deleted file mode 100644 index 52ac1245cfd..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_cl_double.bat +++ /dev/null @@ -1,3 +0,0 @@ -@echo off -cmake -S . -B VS2022_CL_Double -G "Visual Studio 17 2022" -A x64 -DDOUBLE_PRECISION=ON %* -echo Open VS2022_CL_Double\JoltPhysics.sln to build the project. diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_clang.bat b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_clang.bat deleted file mode 100644 index 1f49861304e..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_clang.bat +++ /dev/null @@ -1,10 +0,0 @@ -@echo off -cmake -S . -B VS2022_Clang -G "Visual Studio 17 2022" -A x64 -T ClangCL %* -echo: -echo !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -echo Make sure to install: -echo - C++ Clang Compiler for Windows 12.0.0+ -echo - C++ Clang-cl for v143+ build tools (x64/x86) -echo Using the Visual Studio Installer -echo !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -echo Open VS2022_Clang/JoltPhysics.sln to build the project. diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_clang_double.bat b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_clang_double.bat deleted file mode 100644 index 908a893bdba..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_clang_double.bat +++ /dev/null @@ -1,10 +0,0 @@ -@echo off -cmake -S . -B VS2022_Clang_Double -G "Visual Studio 17 2022" -A x64 -T ClangCL -DDOUBLE_PRECISION=YES %* -echo: -echo !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -echo Make sure to install: -echo - C++ Clang Compiler for Windows 12.0.0+ -echo - C++ Clang-cl for v143+ build tools (x64/x86) -echo Using the Visual Studio Installer -echo !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -echo Open VS2022_Clang_Double/JoltPhysics.sln to build the project. diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_uwp.bat b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_uwp.bat deleted file mode 100644 index 43167bbd3e7..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_uwp.bat +++ /dev/null @@ -1,5 +0,0 @@ -@echo off -cmake -S . -B VS2022_UWP -G "Visual Studio 17 2022" -DCMAKE_SYSTEM_NAME=WindowsStore -DCMAKE_SYSTEM_VERSION=10.0 %* -echo If cmake failed then be sure to check the "Universal Windows Platform development" checkbox in the Visual Studio Installer -echo Open VS2022_UWP\JoltPhysics.sln to build the project. -echo Note that none of the sample applications are available for the Universal Windows Platform (use cmake_vs2022_cl.bat instead). diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_uwp_arm.bat b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_uwp_arm.bat deleted file mode 100644 index 31651ae33b6..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_vs2022_uwp_arm.bat +++ /dev/null @@ -1,5 +0,0 @@ -@echo off -cmake -S . -B VS2022_UWP_ARM -G "Visual Studio 17 2022" -A ARM64 -DCMAKE_SYSTEM_NAME=WindowsStore -DCMAKE_SYSTEM_VERSION=10.0 %* -echo If cmake failed then be sure to check the "Universal Windows Platform development" checkbox in the Visual Studio Installer -echo Open VS2022_UWP_ARM\JoltPhysics.sln to build the project. -echo Note that none of the sample applications are available for the Universal Windows Platform (use cmake_vs2022_cl_arm.bat instead). diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_windows_mingw.sh b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_windows_mingw.sh deleted file mode 100644 index 39d6d28432f..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_windows_mingw.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/sh - -if [ -z $1 ] -then - BUILD_TYPE=Debug -else - BUILD_TYPE=$1 - shift -fi - -BUILD_DIR=MinGW_$BUILD_TYPE - -echo Usage: ./cmake_windows_mingw.sh [Configuration] -echo "Possible configurations: Debug (default), Release, Distribution" -echo Generating Makefile for build type \"$BUILD_TYPE\" in folder \"$BUILD_DIR\" - -cmake -S . -B $BUILD_DIR -G "MinGW Makefiles" -DCMAKE_BUILD_TYPE=$BUILD_TYPE "${@}" - -echo Compile by running \"cmake --build $BUILD_DIR -j 8\" diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_xcode_ios.sh b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_xcode_ios.sh deleted file mode 100644 index 65e9aac1004..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_xcode_ios.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh - -cmake -S . -B XCode_iOS -DTARGET_HELLO_WORLD=OFF -DTARGET_PERFORMANCE_TEST=OFF -DCMAKE_SYSTEM_NAME=iOS -GXcode -open XCode_iOS/JoltPhysics.xcodeproj diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_xcode_macos.sh b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_xcode_macos.sh deleted file mode 100644 index 6dd1488a80d..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/cmake_xcode_macos.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh - -cmake -S . -B XCode_MacOS -GXcode -D"CMAKE_OSX_ARCHITECTURES=x86_64;arm64" -open XCode_MacOS/JoltPhysics.xcodeproj diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/iOS/UnitTestsInfo.plist b/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/iOS/UnitTestsInfo.plist deleted file mode 100644 index 84c416f439e..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Build/iOS/UnitTestsInfo.plist +++ /dev/null @@ -1,34 +0,0 @@ - - - - - CFBundleDevelopmentRegion - English - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleGetInfoString - - CFBundleIconFile - - CFBundleIdentifier - com.joltphysics.unittests - CFBundleInfoDictionaryVersion - 6.0 - CFBundleLongVersionString - 1.0 - CFBundleName - UnitTests - CFBundlePackageType - APPL - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1.0 - CSResourcesFileMapped - - NSHumanReadableCopyright - - - diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/ContributorAgreement.md b/crates/joltc-sys-patched/JoltC/JoltPhysics/ContributorAgreement.md deleted file mode 100644 index 37065f490b2..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/ContributorAgreement.md +++ /dev/null @@ -1,123 +0,0 @@ -## Fiduciary License Agreement 2.0 - -based on the - -## Individual Contributor Exclusive License Agreement - -## (including the Traditional Patent License OPTION) - -Thank you for your interest in contributing to Jolt Physics ("We" or "Us"). - -The purpose of this contributor agreement ("Agreement") is to clarify and document the rights granted by contributors to Us. - -### 0\. Preamble - -Software is deeply embedded in all aspects of our lives and it is important that it empower, rather than restrict us. Free Software gives everybody the rights to use, understand, adapt and share software. These rights help support other fundamental freedoms like freedom of speech, press and privacy. - -Development of Free Software can follow many patterns. In some cases whole development is handled by a sole programmer or a small group of people. But usually, the creation and maintenance of software is a complex process that requires the contribution of many individuals. This also affects who owns the rights to the software. In the latter case, rights in software are owned jointly by a great number of individuals. - -To tackle this issue some projects require a full copyright assignment to be signed by all contributors. The problem with such assignments is that they often lack checks and balances that would protect the contributors from potential abuse of power from the new copyright holder. - -FSFE’s Fiduciary License Agreement (FLA) was created by the Free Software Foundation Europe e.V. with just that in mind – to concentrate all deciding power within one entity and prevent fragmentation of rights on one hand, while on the other preventing that single entity from abusing its power. The main aim is to ensure that the software covered under the FLA will forever remain Free Software. - -This process only serves for the transfer of economic rights. So-called moral rights (e.g. authors right to be identified as author) remain with the original author(s) and are inalienable. - -### How to use this FLA - -If You are an employee and have created the Contribution as part of your employment, You need to have Your employer approve this Agreement or sign the Entity version of this document. If You do not own the Copyright in the entire work of authorship, any other author of the Contribution should also sign this – in any event, please contact Us at jorrit@jrouwe.nl - -### 1\. Definitions - -**"You"** means the individual Copyright owner who Submits a Contribution to Us. - -**"Legal Entity"** means an entity that is not a natural person. - -**"Affiliate"** means any other Legal Entity that controls, is controlled by, or under common control with that Legal Entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such Legal Entity, whether by contract or otherwise, (ii) ownership of fifty percent (50%) or more of the outstanding shares or securities that vote to elect the management or other persons who direct such Legal Entity or (iii) beneficial ownership of such entity. - -**"Contribution"** means any original work of authorship, including any original modifications or additions to an existing work of authorship, Submitted by You to Us, in which You own the Copyright. - -**"Copyright"** means all rights protecting works of authorship, including copyright, moral and neighboring rights, as appropriate, for the full term of their existence. - -**"Material"** means the software or documentation made available by Us to third parties. When this Agreement covers more than one software project, the Material means the software or documentation to which the Contribution was Submitted. After You Submit the Contribution, it may be included in the Material. - -**"Submit"** means any act by which a Contribution is transferred to Us by You by means of tangible or intangible media, including but not limited to electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, Us, but excluding any transfer that is conspicuously marked or otherwise designated in writing by You as "Not a Contribution." - -**"Documentation"** means any non-software portion of a Contribution. - -### 2\. License grant - -#### 2.1 Copyright license to Us - -Subject to the terms and conditions of this Agreement, You hereby grant to Us a worldwide, royalty-free, exclusive, perpetual and irrevocable (except as stated in Section 8.2) license, with the right to transfer an unlimited number of non-exclusive licenses or to grant sublicenses to third parties, under the Copyright covering the Contribution to use the Contribution by all means, including, but not limited to: - -* publish the Contribution, -* modify the Contribution, -* prepare derivative works based upon or containing the Contribution and/or to combine the Contribution with other Materials, -* reproduce the Contribution in original or modified form, -* distribute, to make the Contribution available to the public, display and publicly perform the Contribution in original or modified form. - -#### 2.2 Moral rights - -Moral Rights remain unaffected to the extent they are recognized and not waivable by applicable law. Notwithstanding, You may add your name to the attribution mechanism customary used in the Materials you Contribute to, such as the header of the source code files of Your Contribution, and We will respect this attribution when using Your Contribution. - -#### 2.3 Copyright license back to You - -Upon such grant of rights to Us, We immediately grant to You a worldwide, royalty-free, non-exclusive, perpetual and irrevocable license, with the right to transfer an unlimited number of non-exclusive licenses or to grant sublicenses to third parties, under the Copyright covering the Contribution to use the Contribution by all means, including, but not limited to: - -* publish the Contribution, -* modify the Contribution, -* prepare derivative works based upon or containing the Contribution and/or to combine the Contribution with other Materials, -* reproduce the Contribution in original or modified form, -* distribute, to make the Contribution available to the public, display and publicly perform the Contribution in original or modified form. - -This license back is limited to the Contribution and does not provide any rights to the Material. - -### 3\. Patents - -#### 3.1 Patent license - -Subject to the terms and conditions of this Agreement You hereby grant to Us and to recipients of Materials distributed by Us a worldwide, royalty-free, non-exclusive, perpetual and irrevocable (except as stated in Section 3.2) patent license, with the right to transfer an unlimited number of non-exclusive licenses or to grant sublicenses to third parties, to make, have made, use, sell, offer for sale, import and otherwise transfer the Contribution and the Contribution in combination with any Material (and portions of such combination). This license applies to all patents owned or controlled by You, whether already acquired or hereafter acquired, that would be infringed by making, having made, using, selling, offering for sale, importing or otherwise transferring of Your Contribution(s) alone or by combination of Your Contribution(s) with any Material. - -#### 3.2 Revocation of patent license - -You reserve the right to revoke the patent license stated in section 3.1 if We make any infringement claim that is targeted at your Contribution and not asserted for a Defensive Purpose. An assertion of claims of the Patents shall be considered for a "Defensive Purpose" if the claims are asserted against an entity that has filed, maintained, threatened, or voluntarily participated in a patent infringement lawsuit against Us or any of Our licensees. - -### 4\. License obligations by Us - -We agree to (sub)license the Contribution or any Materials containing, based on or derived from your Contribution under the terms of any licenses the Free Software Foundation classifies as Free Software License and which are approved by the Open Source Initiative as Open Source licenses. - -More specifically and in strict accordance with the above paragraph, we agree to (sub)license the Contribution or any Materials containing, based on or derived from the Contribution only under the terms of the following license(s) MIT (including any right to adopt any future version of a license if permitted). - -We agree to license patents owned or controlled by You only to the extent necessary to (sub)license Your Contribution(s) and the combination of Your Contribution(s) with the Material under the terms of any licenses the Free Software Foundation classifies as Free Software licenses and which are approved by the Open Source Initiative as Open Source licenses.. - -### 5. Disclaimer - -THE CONTRIBUTION IS PROVIDED "AS IS". MORE PARTICULARLY, ALL EXPRESS OR IMPLIED WARRANTIES INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTY OF SATISFACTORY QUALITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE EXPRESSLY DISCLAIMED BY YOU TO US AND BY US TO YOU. TO THE EXTENT THAT ANY SUCH WARRANTIES CANNOT BE DISCLAIMED, SUCH WARRANTY IS LIMITED IN DURATION AND EXTENT TO THE MINIMUM PERIOD AND EXTENT PERMITTED BY LAW. - -### 6. Consequential damage waiver - -TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT WILL YOU OR WE BE LIABLE FOR ANY LOSS OF PROFITS, LOSS OF ANTICIPATED SAVINGS, LOSS OF DATA, INDIRECT, SPECIAL, INCIDENTAL, CONSEQUENTIAL AND EXEMPLARY DAMAGES ARISING OUT OF THIS AGREEMENT REGARDLESS OF THE LEGAL OR EQUITABLE THEORY (CONTRACT, TORT OR OTHERWISE) UPON WHICH THE CLAIM IS BASED. - -### 7. Approximation of disclaimer and damage waiver - -IF THE DISCLAIMER AND DAMAGE WAIVER MENTIONED IN SECTION 5. AND SECTION 6. CANNOT BE GIVEN LEGAL EFFECT UNDER APPLICABLE LOCAL LAW, REVIEWING COURTS SHALL APPLY LOCAL LAW THAT MOST CLOSELY APPROXIMATES AN ABSOLUTE WAIVER OF ALL CIVIL OR CONTRACTUAL LIABILITY IN CONNECTION WITH THE CONTRIBUTION. - -### 8. Term - -8.1 This Agreement shall come into effect upon Your acceptance of the terms and conditions. - -8.2 This Agreement shall apply for the term of the copyright and patents licensed here. However, You shall have the right to terminate the Agreement if We do not fulfill the obligations as set forth in Section 4. Such termination must be made in writing. - -8.3 In the event of a termination of this Agreement Sections 5., 6., 7., 8., and 9. shall survive such termination and shall remain in full force thereafter. For the avoidance of doubt, Free and Open Source Software (sub)licenses that have already been granted for Contributions at the date of the termination shall remain in full force after the termination of this Agreement. - -### 9. Miscellaneous - -9.1 This Agreement and all disputes, claims, actions, suits or other proceedings arising out of this agreement or relating in any way to it shall be governed by the laws of Netherlands excluding its private international law provisions. - -9.2 This Agreement sets out the entire agreement between You and Us for Your Contributions to Us and overrides all other agreements or understandings. - -9.3 In case of Your death, this agreement shall continue with Your heirs. In case of more than one heir, all heirs must exercise their rights through a commonly authorized person. - -9.4 If any provision of this Agreement is found void and unenforceable, such provision will be replaced to the extent possible with a provision that comes closest to the meaning of the original provision and that is enforceable. The terms and conditions set forth in this Agreement shall apply notwithstanding any failure of essential purpose of this Agreement or any limited remedy to the maximum extent possible under law. - -9.5 You agree to notify Us of any facts or circumstances of which you become aware that would make this Agreement inaccurate in any respect. diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/APIChanges.md b/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/APIChanges.md deleted file mode 100644 index 874050962c7..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/APIChanges.md +++ /dev/null @@ -1,111 +0,0 @@ -# Breaking API Changes - -This document lists all breaking API changes by date and by release tag. Note that not all API changes are listed here, trivial changes (that cause a compile error and require an obvious fix) are not listed. - -Changes that make some state saved through SaveBinaryState from a prior version of the library unreadable by the new version is marked as *SBS*. See [Saving Shapes](https://jrouwe.github.io/JoltPhysics/#saving-shapes) for further information. - -## Changes between v4.0.2 and v5.0.0 - -* 20240327 - *SBS* - SoftBodySharedSettings::CreateEdges was renamed to CreateConstraints and can now also create shear and bend constraints. This also breaks the serialization format for SoftBodySharedSettings. (8e4bf3fa03f59cff6af7394d69cdf62abaf7a1d2) -* 20240310 - *SBS* - Soft body skinned constraints now use a sphere as backstop instead of an infinite plane. This also breaks the serialization format for SoftBodySharedSettings. (17db6d3f245d2198319c3787f62498fe5935b7c8) -* 20240225 - *SBS* - Changes were made to SoftBodySharedSettings that break the binary serialization format of that class. (277b818ffefed4f15477ff1e6d0cc07065899903) -* 20240223 - Added ConvexShape::ESupportMode::Default. If you have custom convex shapes you need to handle this in ConvexShape::GetSupportFunction. (0f67cc2915c5e34a4a38480580dad73888a1952e) -* 20240216 - Restriction angular motion using EAllowedDOFs now works in world space rather than in local space. This change was made to be more in line with other physics engines and to fix some issues with constraints. If you need the old behavior then copy [this](https://github.com/jrouwe/JoltPhysics/blob/9631e217e54b8492ac36471f2aa966df40d6c2ad/Jolt/Physics/Body/MotionProperties.cpp#L33-L118) code into your own code base and call MotionProperties::SetInverseInertia(diagonal, rotation) where diagonal is called mInvInertiaDiagonal and rotation is called mInertiaRotation in the code snippet. (191536d51d71ee29147205aa09d1acab52789e5f) -* 20240210 - Fixed spelling error EPathRotationConstraintType::ConstaintToPath to EPathRotationConstraintType::ConstrainToPath (6c095bbf7906b01f427b52d43212f5ebf760fc81) -* 20240210 - Added extra parameter fraction hint to PathConstraintPath::GetClosestPoint. This can be used to speed up the search along the curve and to disambiguate fractions in case a path reaches the same point multiple times (i.e. a figure-8) (b91e729e6e2c34df16cc03f5ac3b3f6d3fa8b762) -* 20240203 - Longitudinal friction impulse for wheeled/tracked vehicles could become much higher than the calculated max because each iteration it was clamped to the max friction impulse which meant the total friction impulse could be PhysicsSettings::mNumVelocitySteps times too high. In case this breaks your vehicle, the new max tire impulse callback can be used to restore the old behavior, see [the vehicle constraint test](https://github.com/jrouwe/JoltPhysics/blob/a456b244aa2ad2ce0a8124d27823377ed0b1c4b4/Samples/Tests/Vehicle/VehicleConstraintTest.cpp#L156-L164). (a456b244aa2ad2ce0a8124d27823377ed0b1c4b4) -* 20240120 - *SBS* - Implemented enhanced internal edge removal algorithm. This breaks the binary serialization format for BodyCreationSettings. (94c1ad811b95c72f4d3bb6841c73c1c3461caa91) -* 20240113 - VehicleConstraint::CombineFunction now calculates both longitudinal and lateral friction in 1 call so there can be dependencies between the two. (d6ed5b3e7b22904af555088b6ae4770f8fb0e00f) -* 20240105 - CharacterVirtual will now receive an OnContactAdded callback when it collides with a sensor (but will have no further interaction). You may need to update the logic in your CharacterContactListener to ignore those contacts. (fb778c568d3ba14556559324671ffec172957f5c) -* 20240101 - Renamed SensorDetectsStatic to CollideKinematicVsNonDynamic and made it work for non-sensors. This means that kinematic bodies can now get collision callbacks when they collide with other static / kinematic objects. It can also affect the order in which bodies are passed in the ContactListener::OnContactValidate callback. (2d607c4161a65201d66558a2cc76d1265aea527e) -* 20231220 - *SBS* - Added ability to enable gyroscopic forces on BodyCreationSettings. This breaks the binary serialization format for this class. (9d7748eaa91341adc17554f32bf991bfed04e47e) -* 20231219 - *SBS* - Added a 'swing type' attribute to SixDOFConstraint and SwingTwistConstraint. This breaks the binary serialization format. (41016256e2cf1262ec05cff3cfa7645668ee0bf0) -* 20231208 - Changed the meaning of Constraint::mNumVelocity/PositionStepsOverride. Before the number of steps would be the maximum of all constraints and the default value, now an overridden value of 0 means that the constraint uses the default value, otherwise it will use the value as specified. This means that if all constraints in an island have a lower value than the default, we will now use the lower value instead of the default. (0771808a03b850d16f1c64156f0aee827ca3706b) -* 20231208 - *SBS* - Bodies can now also override the default number of solver iterations. This breaks the binary serialization format. (0771808a03b850d16f1c64156f0aee827ca3706b) -* 20231203 - VehicleConstraint::CombineFunction got two additional parameters to identify which wheel is requesting friction. (8d80155f93d0d0c3ffe3dd46550650b9c830d304) - -## Changes between v4.0.0 and v4.0.2 - -* No breaking changes. - -## Changes between v3.0.1 and v4.0.0 - -* 20231003 - *SBS* - Bug fix in serialization of SoftBodySharedSettings breaks binary serialization format. (ccb250747eee4dedebfa02d950775478fb52f786) -* 20230914 - Removed GetProcessorTicksPerSecond as it was not correctly implemented for all platforms. (d44f4bad0872075d5cef2779742c89203d4f4488) -* 20230819 - *SBS* - RagdollSettings got the ability to have constraints that do not follow the skeleton. This changes the binary serialization format for this class. (08fc49d2d7abfa1a69e21971785d37724c748bb6) -* 20230807 - Renamed ContactSettings::mRelativeSurfaceVelocity to mRelativeLinearSurfaceVelocity. (76b809ddb1abf96641acc587fffa70101323d323) -* 20230807 - *SBS* - PhysicsScene is now able to load/save soft bodies. This changes the binary serialization format. (779ba3673beebdc4021842516f4ff6aa7c1e09b4) -* 20230805 - Body::SaveState and MotionProperties::SaveState now only save the state that can be changed by the simulation. Configuration properties like friction, restitution etc. must be saved by the user if desired. (7ff50429abd53f1914fd25a9e80ff47f22bc9f0e) -* 20230801 - *SBS* - Constraint priority was added to all constraints which changes the binary serialization format. (e341bb3e959460fbe196032095c1ab0346d7e746) -* 20230704 - *SBS* - A new flag was added to BodyCreationSettings that changes the binary serialization format. (2dd3a033a41e422eb470484029324cc9bbaf0825) -* 20230629 - Fix for engine RPM being much higher than wheel RPM when measured at clutch. Before we were ignoring bake and wheel torques in engine RPM calculation. Now they're much closer but this unfortunately means that the simulation of the vehicle has changed and mainly the engine torque and clutch strength need to be re-tweaked. (b40090766c545a68dccfac76cde8c6345ca626a6) -* 20230623 - The parameter inIntegrationSubSteps was removed from PhysicsSystem::Update because more and more features didn't support it. If you were using it multiply inCollisionSteps with the value of inIntegrationSubSteps to get roughly the same behavior. (8fcc7a78ec051b215bf13b037b9f975baa803b6f) -* 20230618 - *SBS* - A new flag was added to BodyCreationSettings that changes the binary serialization format. (107b70c7585909f0757a62c318261a18d670ff97) -* 20230610 - A bug was fixed that causes the vehicle suspension to be weaker when driving over low mass objects. This also changes suspension behavior a bit when driving over static objects. (44b82e395697ea553574df3cd806ffe264bfa5c4) -* 20230609 - *SBS* - The MotorcycleController lean controller is now a full PID controller. This changes binary serialization format. (70e7bb3e5808dabc17ee38fb823fbfa7e9140a91) -* 20230609 - *SBS* - VehicleConstraint uses the new SpringSettings class as a member which contains the mFrequency and mDamping members. This requires minor code changes. (0da97d8f3345f14c5b4b0ee3571c05832c556f98) -* 20230609 - *SBS* - DistanceConstraintSettings, SliderConstraintSettings and MotorSettings now use the new SpringSettings class as a member which contains the mFrequency and mDamping members. This requires minor code changes. (3cabc057c1267fde288c1ab2a23076702c71eb79) -* 20230520 - A bug was fixed in CharacterVirtual that makes mPenetrationRecoverySpeed behave according to the documentation (1 = fully resolve collision in 1 update). With the bug the recovery was too little. If you want the penetration recovery to work as before with the bug multiply it by 1 / delta_time. (8dd93317d66a9a72d3afeff4ecb17c257a7e9d91) -* 20230420 - To support compiling Jolt as a shared library, the RTTI macros were changed to be able to specify if a symbol should be exported or not. If you're using Jolt's RTTI system in your own project you need to change e.g. JPH_DECLARE_RTTI_VIRTUAL(XXX) to JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, XXX). (d2f1d97004d036c6f759203c42e264e401472037) - -## Changes between v2.0.1 and v3.0.0 - -* 20230331 - *SBS* - Vehicle wheels now support specifying the steering axis and wheel forward and up axis separately. This breaks the serialization format and requires setting extra properties on the wheels. (4269d8bbc77b889552a842c2e8476ba7ffc6b9a1) -* 20230328 - Vehicle now supports suspension under an angle. The behavior of the suspension, even if it is under 90 degrees with the vehicle body, changed so this may require tweaking the spring constants. (172a99c718bded5faa169ac440517286684fa2f0) -* 20230316 - The signature of ShapeFilter changed and the ShouldCollide function is no longer called for triangles inside a mesh/heightfield shape (you can use CollisionCollector::AddHit to filter per triangle). The previous implementation didn't pass in enough context for the application to fully determine which sub shapes were colliding. See [#473](https://github.com/jrouwe/JoltPhysics/discussions/473) for more information. (bc4fa997f15f2953dc87ee5c1ba51ecf2077c287) -* 20230313 - VehicleCollisionTester::Collide parameter outSuspensionLength was returning suspension length + wheel radius, now it returns the suspension length. If you have your own implementation of VehicleCollisionTester you need to update your code. (fcd9cb0f1677709e30951f2748aefd5f72ffdae1) -* 20230212 - Sensors are now able to detect other Sensors, make sure you put sensors in an ObjectLayer that doesn't collide with other sensors if you want to preserve the old behavior. (a76f5891ee429ae4fcde659c19f1eb769f9d8a21) -* 20230205 - *SBS* - Added 'IsSensor' and 'UseManifoldReduction' to BodyCreationSettings::SaveBinaryState. (8f6f210f53fc71e43760e20aeb2eae28ea168f4b) -* 20221231 - ObjectLayerPairFilter and ObjectVsBroadPhaseLayerFilter are now objects instead of function pointers. (4315ad53e354f094f753664fcf7a52870f6915e4) -* 20221208 - ContactListener::OnContactValidate is reporting collisions relative to inBaseOffset. Add this to the contact point if you want world space positions. (428611482825e369e60e0a5daf17c69a4d0f2a6f) -* 20221204 - Changes related to double precision support for positions (a2c1c22059fa031faf0208258e654bcff79a63e4) - * In many places in the public API Vec3 has been replaced by RVec3 (a Vec3 of Real values which can either be double or float depending on if JPH_DOUBLE_PRECISION is defined). In the same way RMat44 replaces Mat44. When compiling in single precision mode (the default) you should not notice a change. - * Shape::GetSubmergedVolume now takes a plane that's relative to inCenterOfMassTransform instead of one in world space - * Many of the NarrowPhaseQuery and TransformedShape collision queries now have a 'base offset' that you need to specify. Go to [Big Worlds](https://jrouwe.github.io/JoltPhysics/#big-worlds) for more info. - * The NarrowPhaseQuery/TransformedShape CastRay / CastShape functions now take a RRayCast / RShapeCast struct as input. When compiling in single precision mode this is the same as a RayCast or ShapeCast so only the type name needs to be updated. - * If you implement your own TempAllocator and want to compile in double precision, make sure you align to JPH_RVECTOR_ALIGNMENT bytes (instead of 16) - * The SkeletonPose got a 'root offset' member, this means that the ragdoll will now make the joint transform of the first body zero and put that offset in the 'root offset'. - * ContactManifold now stores the contacts relative to mBaseOffset, the arrays containing the contact points have been renamed from mWorldSpaceContactPointsOn1/2 to mRelativeContactPointsOn1/2 to reflect this. - * The DebugRenderer::DrawLine function now takes RVec3Arg parameters instead of Float3 parameters. - * The format of a recording recorded with DebugRendererRecorder has changed, this invalidates any prior recordings. -* 20221128 - MotionProperties::SetMotionQuality has been removed because changing it for an active body could cause crashes. Use BodyInterface::SetMotionQuality instead. (64802d163a7336e60916365ad9bce764cec4ca70) - -## Changes between v1.1.0 and v2.0.0 - -* 20221027 - *SBS* (vehicles only) - Rewrote engine model for wheeled vehicle. Before engine inertia was only used when the clutch was pressed, now it is always used, so you may want to use a lower value. The way torque is distributed over the wheels has also changed and may require tweaking the vehicle parameters. (5ac751cee9afcc097fd4f884308f5e4dc9fdaeaf) -* 20220903 - *SBS* - Added overrides for number of position/velocity solver iterations. Only affects serialization. (38ec33942ead4968a83409bd13d868f60e6397c4) -* 20220826 - *SBS* - Removed FixedConstraintSettings and SliderConstraintSettings SetPoint functions. If you were calling this function replace it by setting mAutoDetectPoint = true. (d16a0b05bfeed42b1618e3774a9c953e6922d22b) -* 20220614 - It is now possible to override the memory allocator, register the default using RegisterDefaultAllocator(). This means that the public API now takes STL containers that use a custom memory allocator so use Array instead of vector, UnorderedMap instead of unordered_map etc. If you're using placement new, add ```::``` in front of new. Define JPH_DISABLE_CUSTOM_ALLOCATOR to disable this new behavior (b68097f582148d6f66c18a6ff95c5ca9b40b48cc) -* 20220606 - *SBS* - The slider constraint now has frequency and damping for its limits (09d6d9d51c46fbd159bf98abfd43cc639f6c0403) -* 20220606 - *SBS* - The rack and pinion and gear constraints were added (09d6d9d51c46fbd159bf98abfd43cc639f6c0403) -* 20220517 - Note: Superseded by d16a0b05bfeed42b1618e3774a9c953e6922d22b. When constructing a FixedConstraint you now need to call FixedConstraintSettings::SetPoint to configure the point where the bodies attach (4f7c925c31f39eda1d8d68e4e72456b5def93d9b) -* 20220516 - Constraint::GetType was renamed to GetSubType, a new GetType function was introduced (3e2151a009e8f11ca724754b2bd25e14d2654fb6) -* 20220516 - *SBS* - Added possibility to save the current state of the physics world as a scene (3e2151a009e8f11ca724754b2bd25e14d2654fb6) -* 20220510 - Factory::sInstance must now be allocated by the application prior to calling RegisterTypes() and has changed to a pointer (3ca62973dae7cda7a9ceece698438a45b9ad1433) -* 20220503 - Unused function SerializableObject::OnLoaded was removed (388d47254a236c053a472e54c10b264765badc09) -* 20220502 - ContactConstraintManager::CombineFunction has additional parameters: the SubShapeIDs from both bodies (6b873563739dfd3d77263c2c50af2f3f418ec15b) -* 20220415 - Removed Body::GetDebugName / SetDebugName, keep this info in a lookaside table if you need it (6db4d3beac6760e55f65102db00f93dfbc56ac26) -* 20220406 - Renamed CollisionDispatch::sCastShapeVsShape to sCastShapeVsShapeLocalSpace (6ba21f50dcf17bd506080ec30759724a7f3097d8) -* 20220327 - Changed the default include path, ```#include ``` must be replaced by ```#include ``` (06e9d17d385814cd24d3b77d689c0a29d854e194) -* 20220318 - Added support for SSE2. If you want to use later versions of SSE make sure you have JPH_USE_SSE4_1 and JPH_USE_SSE4_2 defined (28f363856a007d03f657e46e8f6d90ccd7c6487a) -* 20220303 - Note: Partially superseded by d16a0b05bfeed42b1618e3774a9c953e6922d22b. When constructing a SliderConstraint you now need to call SliderConstraintSettings::SetPoint to configure the point where the bodies attach. Also replace mSliderAxis = x with SetSliderAxis(x) (5a327ec182d0436d435c62d0bccb4e76c6324659) -* 20220228 - PointConstraint::mCommonPoint is now mPoint1 / mPoint2. Replace mCommonPoint = x with mPoint1 = mPoint2 = x (066dfb8940ba3e7dbf8ed47e9a1eeb194730e04b) -* 20220226 - ObjectToBroadPhaseLayer and BroadPhaseLayerToString changed to BroadPhaseLayerInterface, this makes mapping a broadphase layer to an object layer more flexible (36dd3f8c8c31ef1aeb7585b2b615c23bc8b76f13) -* 20220222 - Shape and body user data changed from void * / uint32 to uint64 (14e062ac96abd571c6eff5e40b1df4d8b2333f55) - -## Changes between v1.0.0 and v1.1.0 - -* No breaking changes. - -## Changes between v0.0.0 and v1.0.0 - -* 20220107 - PhysicsSettings::mBodyPairCacheCosMaxDeltaRotation was renamed to mBodyPairCacheCosMaxDeltaRotationDiv2 -* 20211219 - *SBS* - Now storing 3 components for a Vec3 instead of 4 in SaveBinaryState (23c1b9d9029d74076c0549c8779b3b5ac2179ea3) -* 20211212 - Removed StatCollector (92a117e0f05a08de154e86d3cd0b354783aa5593) -* 20210711 - HeightFieldShapeSettings::mBlockSize is subdivided one more time at run-time, so this is effectively 2x the block size (2aa3b443bf71785616f3140c32e6a04c49516535) -* 20211106 - Mutex class now has its own implementation on Platform Blue, users must implement the JPH_PLATFORM_BLUE_MUTEX_* functions (a61dc67503a87ef0e190f7fb31d495ac51aa43de) -* 20211019 - ShapeCast::mShape no longer keeps a reference, the caller is responsible for keeping the reference now (e2bbdda9110b083b49ba323f8fd0d88c19847c2e) -* 20211004 - Removed RTTI from Shape class, use Shape::GetType / GetSubType now (6d5cafd53501c2c1e313f1b1f29d5161db074fd5) -* 20210930 - Changed RestoreMaterialState and RestoreSubShapeState to use pointers instead of vectors to allow loading shapes with fewer memory allocations (b8953791f35a91fcd12568c7dc4cc2f68f40fb3f) -* 20210918 - PhysicsSystem::Init takes an extra parameter to specify the amount of mutexes to use (ef371411af878023f062b9930db09f17411f01ba) -* 20210827 - BroadPhaseLayerPairFilter was changed to ObjectVsBroadPhaseLayerFilter to avoid testing too many layers during collision queries (33883574bbc6fe208a4b62054d00b582872da6f4) diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Architecture.md b/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Architecture.md deleted file mode 100644 index a3524bd98a6..00000000000 --- a/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Architecture.md +++ /dev/null @@ -1,755 +0,0 @@ -[TOC] - -# Architecture of Jolt Physics {#architecture-jolt-physics} - -For demos and videos go to the [Samples](Samples.md) section. - -# Bodies {#bodies} - -We use a pretty traditional physics engine setup, so \ref Body "bodies" in our simulation are objects which have attached collision \ref Shape "shapes" - -## Types {#body-types} - -Bodies can either be: -- [static](@ref EMotionType) (not moving or simulating) -- [dynamic](@ref EMotionType) (moved by forces) or -- [kinematic](@ref EMotionType) (moved by velocities only). - -Moving bodies have a [MotionProperties](@ref MotionProperties) object that contains information about the movement of the object. Static bodies do not have this to save space (but they can be configured to have it if a static body needs to become dynamic during its lifetime by setting [BodyCreationSettings::mAllowDynamicOrKinematic](@ref BodyCreationSettings::mAllowDynamicOrKinematic)). - -## Creating Bodies {#creating-bodies} - -Bodies are inserted into the [PhysicsSystem](@ref PhysicsSystem) and interacted with through the [BodyInterface](@ref BodyInterface). - -## Multithreaded Access - -Jolt is designed to be accessed from multiple threads so the body interface comes in two flavors: A locking and a non-locking variant. The locking variant uses a mutex array (a fixed size array of mutexes, bodies are associated with a mutex through hashing and multiple bodies use the same mutex, see [MutexArray](@ref MutexArray)) to prevent concurrent access to the same body. The non-locking variant doesn't use mutexes, so requires the user to be careful. - -In general, body ID's ([BodyID](@ref BodyID)) are used to refer to bodies. You can access a body through the following construct: - - JPH::BodyLockInterface lock_interface = physics_system.GetBodyLockInterface(); // Or GetBodyLockInterfaceNoLock - JPH::BodyID body_id = ...; // Obtain ID to body - - // Scoped lock - { - JPH::BodyLockRead lock(lock_interface, body_id); - if (lock.Succeeded()) // body_id may no longer be valid - { - const JPH::Body &body = lock.GetBody(); - - // Do something with body - ... - } - } - -When another thread has removed the body between the time the body ID was obtained and the lock, the lock will fail. While the lock is taken, other threads cannot modify the body, so it is safe to work with it. Each body ID contains a sequence number, so body ID's will only be reused after many add/remove cycles. To write to a body use [BodyLockWrite](@ref BodyLockWrite). - -You cannot use BodyLockRead to lock multiple bodies (if two threads lock the same bodies in opposite order you'll get a deadlock). Use [BodyLockMultiRead](@ref BodyLockMultiRead) or [BodyLockMultiWrite](@ref BodyLockMultiWrite) to lock them in a consistent order. - -Note that a lot of convenience functions are exposed through the BodyInterface, but not all functionality is available, so you may need to lock the body to get the pointer and then call the function directly on the body. - -## Single Threaded Access {#single-threaded-access} - -If you're only accessing the physics system from a single thread, you can use Body pointers instead of BodyID's. In this case you can also use the non-locking variant of the body interface. - -Note that there are still some restrictions: - -* You cannot read from / write to bodies or constraints while PhysicsSystem::Update is running. As soon as the Update starts, all body / constraint mutexes are locked. -* Collision callbacks (see ContactListener) are called from within the PhysicsSystem::Update call from multiple threads. You can only read the body data during a callback. -* Activation callbacks (see BodyActivationListener) are called in the same way. Again you should only read the body during the callback and not make any modifications. -* Step callbacks (see PhysicsStepListener) are also called from PhysicsSystem::Update from multiple threads. You're responsible for making sure that there are no race conditions. In a step listener you can read/write bodies or constraints but you cannot add/remove them. - -If you are accessing the physics system from multiple threads, you should probably use BodyID's and the locking variant of the body interface. It is however still possible to use Body pointers if you're really careful. E.g. if there is a clear owner of a Body and you ensure that this owner does not read/write state during PhysicsSystem::Update or while other threads are reading the Body there will not be any race conditions. - -## Shapes {#shapes} - -Each body has a shape attached that determines the collision volume. The following shapes are available (in order of computational complexity): - -* [SphereShape](@ref SphereShape) - A sphere centered around zero. -* [BoxShape](@ref BoxShape) - A box centered around zero. -* [CapsuleShape](@ref CapsuleShape) - A capsule centered around zero. -* [TaperedCapsuleShape](@ref TaperedCapsuleShape) - A capsule with different radii at the bottom and top. -* [CylinderShape](@ref CylinderShape) - A cylinder shape. Note that cylinders are the least stable of all shapes, so use another shape if possible. -* [ConvexHullShape](@ref ConvexHullShape) - A convex hull defined by a set of points. -* [StaticCompoundShape](@ref StaticCompoundShape) - A shape containing other shapes. This shape is constructed once and cannot be changed afterwards. Child shapes are organized in a tree to speed up collision detection. -* [MutableCompoundShape](@ref MutableCompoundShape) - A shape containing other shapes. This shape can be constructed/changed at runtime and trades construction time for runtime performance. Child shapes are organized in a list to make modification easy. -* [MeshShape](@ref MeshShape) - A shape consisting of triangles. They are mostly used for static geometry. -* [HeightFieldShape](@ref HeightFieldShape) - A shape consisting of NxN points that define the height at each point, very suitable for representing hilly terrain. Any body that uses this shape needs to be static. - -Next to this there are a number of decorator shapes that change the behavior of their children: - -* [ScaledShape](@ref ScaledShape) - This shape can scale a child shape. Note that if a shape is rotated first and then scaled, you can introduce shearing which is not supported by the library. -* [RotatedTranslatedShape](@ref RotatedTranslatedShape) - This shape can rotate and translate a child shape, it can e.g. be used to offset a sphere from the origin. -* [OffsetCenterOfMassShape](@ref OffsetCenterOfMassShape) - This shape does not change its child shape but it does shift the calculated center of mass for that shape. It allows you to e.g. shift the center of mass of a vehicle down to improve its handling. - -### Dynamic Mesh Shapes {#dynamic-mesh-shapes} - -Meshes are usually static, but they can be made kinematic or dynamic provided that they don't collide with other mesh- or heightfield shapes (an assert will trigger when this happens and the collision will be ignored). - -Mesh shapes also cannot calculate their mass and inertia, so when you want a dynamic mesh, you need to provide these yourself by setting BodyCreationSettings::mOverrideMassProperties = EOverrideMassProperties::MassAndInertiaProvided and supplying the mass and inertia in BodyCreationSettings::mMassPropertiesOverride. - -An example can be found [here](https://github.com/jrouwe/JoltPhysics/blob/master/Samples/Tests/General/DynamicMeshTest.cpp). - -Note that you should try to avoid dynamic mesh shapes as they are fairly expensive to simulate. Also, mesh shapes don't have a clear inside/outside so a mesh is only considered to be colliding when one of its triangles intersect with the other object. This can result in objects getting stuck inside the mesh without knowing which way is out. - -### Creating Shapes {#creating-shapes} - -Simple shapes like spheres and boxes can be constructed immediately by simply new-ing them. Other shapes need to be converted into an optimized format in order to be usable in the physics simulation. The uncooked data is usually stored in a [ShapeSettings](@ref ShapeSettings) object and then converted to cooked format by a [Create](@ref ShapeSettings::Create) function that returns a [Result](@ref Result) object that indicates success or failure and provides the cooked object. - -Creating a convex hull for example looks like: - - // Shapes are refcounted and can be shared between bodies - JPH::Ref shape; - - // The ShapeSettings object is only required for building the shape, all information is copied into the Shape class - { - // Create an array of vertices - JPH::Array vertices = { ... }; - - // Create the settings object for a convex hull - JPH::ConvexHullShapeSettings settings(vertices, JPH::cDefaultConvexRadius); - - // Create shape - JPH::Shape::ShapeResult result = settings.Create(); - if (result.IsValid()) - shape = result.Get(); - else - ... // Error handling - } - -Note that after you call Create, the shape is cached and ShapeSettings keeps a reference to your shape (see @ref memory-management). If you call Create again, the same shape will be returned regardless of what changed to the settings object (unless you call [ClearCachedResult](@ref ShapeSettings::ClearCachedResult) to clear the cache). - -### Saving Shapes {#saving-shapes} - -There are two ways of serializing data: - -* The uncooked data can be serialized using the [ObjectStream](@ref ObjectStream) system (either in [binary](@ref ObjectStreamBinaryOut) or in [text](@ref ObjectStreamTextOut) format), data stored in this way is likely to be compatible with future versions of the library (although there is no 100% guarantee of this). -* The cooked data can be serialized using the [SaveBinaryState](@ref Shape::SaveBinaryState) interface that various objects provide. Data stored in this way is optimized for simulation performance and loading speed but is very likely to change between versions of the library, so this should never be your primary data format. - -An example of saving a shape in binary format: - - // Create a sphere of radius 1 - JPH::Ref sphere = new JPH::SphereShape(1.0f); - - // For this example we'll be saving the shape in a STL string stream, but if you implement StreamOut you don't have to use STL. - stringstream data; - JPH::StreamOutWrapper stream_out(data); - - // Save the shape (note this function handles CompoundShape too). - // The maps are there to avoid saving the same shape twice (it will assign an ID to each shape the first time it encounters them). - // If you don't want certain shapes to be saved, add them to the map and give them an ID. - // You can save many shapes to the same stream by repeatedly calling SaveWithChildren on different shapes. - JPH::Shape::ShapeToIDMap shape_to_id; - JPH::Shape::MaterialToIDMap material_to_id; - sphere->SaveWithChildren(stream_out, shape_to_id, material_to_id); - - // Wrap the STL stream in a StreamIn - JPH::StreamInWrapper stream_in(data); - - // Load the shape - // If you have assigned custom ID's on save, you need to ensure that the shapes exist in this map on restore too. - JPH::Shape::IDToShapeMap id_to_shape; - JPH::Shape::IDToMaterialMap id_to_material; - JPH::Shape::ShapeResult result = JPH::Shape::sRestoreWithChildren(stream_in, id_to_shape, id_to_material); - - JPH::Ref restored_shape; - if (result.IsValid()) - restored_shape = result.Get(); - else - ... // Error handling - -As the library does not offer an exporter from content creation packages and since most games will have their own content pipeline, we encourage you to store data in your own format, cook data while cooking the game data and store the result using the SaveBinaryState interface (and provide a way to force a re-cook when the library is updated). - -### Convex Radius {#convex-radius} - -In order to speed up the collision detection system, all convex shapes use a convex radius. The provided shape will first be shrunken by the convex radius and then inflated again by the same amount, resulting in a rounded off shape: - -![In this example a box (green) was created with a fairly large convex radius. The shape is shrunken first (dashed green line) and then inflated again equally on all sides. The resulting shape as seen by the collision detection system is shown in blue. A larger convex radius results in better performance but a less accurate simulation. A convex radius of 0 is allowed.](Images/ConvexRadius.jpg) - -### Center of Mass {#center-of-mass} - -__Beware: When a shape is created, it will automatically recenter itself around its center of mass.__ The center of mass can be obtained by calling [Shape::GetCenterOfMass](@ref Shape::GetCenterOfMass) and most functions operate in this Center of Mass (COM) space. Some functions work in the original space the shape was created in, they usually have World Space (WS) or Shape Space (SS) in their name (or documentation). - -![Shape Center of Mass](Images/ShapeCenterOfMass.jpg) - -As an example, say we create a box and then translate it: - - // Create box of 2x2x2 m (you specify half the side) - JPH::BoxShapeSettings box(JPH::Vec3(1, 1, 1)); - JPH::Ref box_shape = box.Create().Get(); - - // Offset it by 10 m - JPH::RotatedTranslatedShapeSettings translated_box(JPH::Vec3(10, 0, 0), JPH::Quat::sIdentity(), box_shape); - JPH::Ref translated_box_shape = translated_box.Create().Get(); - - // Cast a ray against the offset box (WRONG!) - JPH::RayCast ray; - ray.mOrigin = JPH::Vec3(10, 2, 0); - ray.mDirection = JPH::Vec3(0, -2, 0); - - // Cast ray - JPH::RayCastResult hit; - bool had_hit = translated_box_shape->CastRay(ray, JPH::SubShapeIDCreator(), hit); - JPH_ASSERT(!had_hit); // There's no hit because we did not correct for COM! - - // Convert the ray to center of mass space for the shape (CORRECT!) - ray.mOrigin -= translated_box_shape->GetCenterOfMass(); - - // Cast ray - had_hit = translated_box_shape->CastRay(ray, JPH::SubShapeIDCreator(), hit); - JPH_ASSERT(had_hit); // Ray was in COM space, now there's a hit! - -In the same way calling: - - translated_box_shape->GetLocalBounds(); - -will return a box of size 2x2x2 centered around the origin, so in order to get it back to the space in which it was originally created you need to offset the bounding box: - - JPH::AABox shape_bounds = translated_box_shape->GetLocalBounds(); - shape_bounds.Translate(translated_box_shape->GetCenterOfMass()); - JPH_ASSERT(shape_bounds == JPH::AABox(JPH::Vec3(9, -1, -1), JPH::Vec3(11, 1, 1))); // Now we have the box relative to how we created it - -Note that when you work with interface of [BroadPhaseQuery](@ref BroadPhaseQuery), [NarrowPhaseQuery](@ref NarrowPhaseQuery) or [TransformedShape](@ref TransformedShape) this transformation is done for you. - -### Creating Custom Shapes {#creating-custom-shapes} - -If the defined Shape classes are not sufficient, or if your application can make a more efficient implementation because it has specific domain knowledge, it is possible to create a custom collision shape: - -* Derive a new class from Shape (e.g. MyShape). If your shape is convex you can consider deriving from ConvexShape, if it contains multiple sub shapes you can derive from CompoundShape or if it wraps a single other shape it can be derived from DecoratedShape. -* Create a settings class that configures your shape (e.g. MyShapeSettings) and inherit it from the corresponding settings class (e.g. ShapeSettings, CompoundShapeSettings or DecoratedShapeSettings). -* Override the ```MyShapeSettings::Create``` function to construct an instance of MyShape. -* If you want to serialize the settings class, register it with the factory: ```Factory::sInstance->Register(RTTI_OF(MyShapeSettings))``` -* If you inherited from Shape you need to select a shape type, use e.g. ```EShapeType::User1``` -* In all cases you will need to specify a sub shape type, use e.g. ```EShapeSubType::User1``` -* If you inherited from ConvexShape you can also specify a convex sub shape type, e.g. ```EShapeSubType::UserConvex1```, in which case you don't need to implement or register the collision detection functions mentioned below. -* Implement the virtual functions that your selected base class exposes. Some functions could be implemented as a dummy if you don't care about the functionality, e.g. if you don't care about buoyancy then GetSubmergedVolume does not need to be implemented. -* Create a ```MyShape::sRegister()``` function to register all collision functions, make sure you call this function after calling ```RegisterTypes()```, see [MeshShape::sRegister](@ref MeshShape::sRegister) for an example. -* Now write collision detection functions to test collision with all other shape types that this shape could collide with and register them with [CollisionDispatch::sRegisterCollideShape](@ref CollisionDispatch::sRegisterCollideShape) and [CollisionDispatch::sRegisterCastShape](@ref CollisionDispatch::sRegisterCastShape). This can be a lot of work, but there are some helper functions that you can use to reduce the work: - * If you have implemented a collision test for type A vs B then you can register [CollisionDispatch::sReversedCastShape](@ref CollisionDispatch::sReversedCastShape) and [CollisionDispatch::sReversedCollideShape](@ref CollisionDispatch::sReversedCollideShape) for B vs A. - * If your shape is triangle based, you can forward the testing of a shape vs a single triangle to the [CollideConvexVsTriangles](@ref CollideConvexVsTriangles) and [CastConvexVsTriangles](@ref CastConvexVsTriangles) classes. - * If your shape contains sub shapes and you have determined that the shape intersects with one of the sub shapes you can forward the sub shape to the collision dispatch again through [CollisionDispatch::sCollideShapeVsShape](@ref CollisionDispatch::sCollideShapeVsShape) and [CollisionDispatch::sCastShapeVsShapeLocalSpace](@ref CollisionDispatch::sCastShapeVsShapeLocalSpace). - -## Sensors {#sensors} - -Sensors are normal rigid bodies that report contacts with other Dynamic or Kinematic bodies through the [ContactListener](@ref ContactListener) interface. Any detected penetrations will however not be resolved. Sensors can be used to implement triggers that detect when an object enters their area. - -The cheapest sensor has a Static motion type. This type of sensor will only detect active bodies entering their area. As soon as a body goes to sleep, the contact will be lost. Note that you can still move a Static sensor around using [BodyInterface::SetPosition](@ref BodyInterface::SetPosition). - -When you make a sensor Kinematic or Dynamic and activate it, it will also detect collisions with sleeping bodies, albeit with a higher run-time cost. - -To create a sensor, either set [BodyCreationSettings::mIsSensor](@ref BodyCreationSettings::mIsSensor) to true when constructing a body or set it after construction through [Body::SetIsSensor](@ref Body::SetIsSensor). A sensor can only use the discrete motion quality type at this moment. - -To make sensors detect collisions with static objects, set the [BodyCreationSettings::mCollideKinematicVsNonDynamic](@ref BodyCreationSettings::mCollideKinematicVsNonDynamic) to true or call [Body::SetCollideKinematicVsNonDynamic](@ref Body::SetCollideKinematicVsNonDynamic). Note that it can place a large burden on the collision detection system if you have a large sensor intersect with e.g. a large mesh terrain or a height field as you will get many contact callbacks and these contacts will take up a lot of space in the contact cache. Ensure that your sensor is in an object layer that collides with as few static bodies as possible. - -## Sleeping {#sleeping-bodies} - -During the simulation step, bodies are divided in 'islands'. Each island consists of a set of dynamic bodies that are either in contact with each other, or that are connected through a constraint: - -![Simulation islands are enclosed by a red box. Note that the floor is static so not part of an island.](Images/SimulationIsland.jpg) - -At the end of each step, all the bodies in an island are checked to see if they have come to rest, if this is the case then the entire island is put to sleep. When a body is sleeping, it can still detect collisions with other objects that are not sleeping, but it will not move or otherwise participate in the simulation to conserve CPU cycles. Sleeping bodies wake up automatically when they're in contact with non-sleeping objects or they can be explicitly woken through an API call like BodyInterface::ActivateBody. Unlike some other physics engines, removing a Body from the world doesn't wake up any surrounding bodies. If you want this you can call BodyInterface::ActivateBodiesInAABox with the bounding box of the removed body (or the combined bounding box if you're removing multiple bodies). Also, things like setting the velocity through Body::SetLinearVelocity will not wake up the Body, use BodyInterface::SetLinearVelocity instead. You can configure the definition of a body 'at rest' through PhysicsSettings::mTimeBeforeSleep and PhysicsSettings::mPointVelocitySleepThreshold. - -## Soft Bodies {#soft-bodies} - -Soft bodies (also known as deformable bodies) can be used to create e.g. a soft ball or a piece of cloth. They are created in a very similar way to normal rigid bodies: - -* First allocate a new SoftBodySharedSettings object on the heap. This object will contain the initial positions of all particles and the constraints between the particles. This object can be shared between multiple soft bodies and should remain constant during its lifetime. -* Then create a SoftBodyCreationSettings object (e.g. on the stack) and fill in the desired properties of the soft body. -* Finally construct the body and add it to the world through BodyInterface::CreateAndAddSoftBody. - -Soft bodies use the Body class just like rigid bodies but can be identified by checking Body::IsSoftBody. To get to the soft body state, cast the result of Body::GetMotionProperties to SoftBodyMotionProperties and use its API. - -Soft bodies try to implement as much as possible of the normal Body interface, but this interface provides a simplified version of reality, e.g. Body::GetLinearVelocity will return the average particle speed and Body::GetPosition returns the average particle position. During simulation, a soft body will never update its rotation. Internally it stores particle velocities in local space, so if you rotate a soft body e.g. by calling BodyInterface::SetRotation, the body will rotate but its velocity will as well. - -### Soft Body Contact Listeners {#soft-body-contact-listener} - -Soft Bodies provide contacts with other bodies through the SoftBodyContactListener class. This contact listener works a little bit different from the normal contact listener as you will not receive a contact callback per colliding vertex. - -After the broad phase has detected an overlap and the normal layer / collision group filters have had a chance to reject the collision, you will receive a SoftBodyContactListener::OnSoftBodyContactValidate callback. This callback allows you to specify how the vertices of the soft body should interact with the other body. You can override the mass for both bodies and you can turn the contact into a sensor contact. - -The simulation will then proceed to do all collision detection and response and after that is finished, you will receive a SoftBodyContactListener::OnSoftBodyContactAdded callback that allows you to inspect all collisions that happened during the simulation step. In order to do this a SoftBodyManifold is provided which allows you to loop over the vertices and ask each vertex what it collided with. - -Note that at the time of the callback, multiple threads are operating at the same time. The soft body is stable and can be safely read. The other body that is collided with is not stable however, so you cannot safely read its position/orientation and velocity as it may be modified by another soft body collision at the same time. - -### Skinning Soft Bodies {#skinning-soft-bodies} - -Using the [skinning](@ref SoftBodySharedSettings::Skinned) constraints, a soft body can be (partially) skinned to joints. This can be used e.g. to partially drive cloth with a character animation. The [vertices](@ref SoftBodySharedSettings::Vertex::mPosition) of the soft body need to be placed in the neutral pose of the character and the joints for this pose need to be calculated in model space (relative to these vertices). The inverted matrices of this neutral pose need to be stored as the [inverse bind matrices](@ref SoftBodySharedSettings::InvBind) and the skinning constraints can then be [weighted](@ref SoftBodySharedSettings::SkinWeight) to these joints. SoftBodySharedSettings::CalculateSkinnedConstraintNormals must be called to gather information needed to calculate the face normals at run-time. - -At run-time, you need to provide the animated joints every simulation step through the SoftBodyMotionProperties::SkinVertices call. During simulation, each skinned vertex will calculate its position and this position will be used to limit the movement of its simulated counterpart. - -![A Skinned Constraint](Images/SoftBodySkinnedConstraint.jpg) - -The adjacent faces of the soft body will be used to calculate the normal of each vertex (shown in red), the vertex is then free to move inside the sphere formed by the skinned vertex position with radius [MaxDistance](@ref SoftBodySharedSettings::Skinned::mMaxDistance) (green sphere). To prevent the vertex from intersecting with the character, it is possible to specify a [BackStopDistance](@ref SoftBodySharedSettings::Skinned::mBackStopDistance) and [BackStopRadius](@ref SoftBodySharedSettings::Skinned::mBackStopRadius), together these form the red sphere. The vertex is not allowed to move inside this sphere. - -### Soft Body Work In Progress {#soft-body-wip} - -Soft bodies are currently in development, please note the following: - -* Soft bodies can only collide with rigid bodies, collisions between soft bodies are not implemented yet. -* AddForce/AddTorque/SetLinearVelocity/SetLinearVelocityClamped/SetAngularVelocity/SetAngularVelocityClamped/AddImpulse/AddAngularImpulse have no effect on soft bodies as the velocity is stored per particle rather than per body. -* Buoyancy calculations have not been implemented yet. -* Constraints cannot operate on soft bodies, set the inverse mass of a particle to zero and move it by setting a velocity to constrain a soft body to something else. -* When calculating friction / restitution an empty SubShapeID will be passed to the ContactConstraintManager::CombineFunction because this is called once per body pair rather than once per sub shape as is common for rigid bodies. - -# Constraints {#constraints} - -Bodies can be connected to each other using constraints ([Constraint](@ref Constraint)). - -The following constraints are available: - -* [FixedConstraint](@ref FixedConstraintSettings) - Will attach a body to another without any degrees of freedom. -* [DistanceConstraint](@ref DistanceConstraintSettings) - Will attach two bodies with a stick (removing 1 degree of freedom). -* [PointConstraint](@ref PointConstraintSettings) - Will attach two bodies in a single point (removing 3 degrees of freedom) -* [HingeConstraint](@ref HingeConstraintSettings) - Will attach two bodies through a hinge. -* [ConeConstraint](@ref ConeConstraintSettings) - Attaches two bodies in a point and will limit the rotation within a cone. -* [SliderConstraint](@ref SliderConstraintSettings) - Attaches two bodies and allows only movement in a single translation axis (also known as prismatic constraint). -* [SwingTwistConstraint](@ref SwingTwistConstraintSettings) - Attaches two bodies using a point constraint and a swing-twist constraint which approximates the shoulder joint of a human. -* [SixDOFConstraint](@ref SixDOFConstraintSettings) - The most configurable joint allows specifying per translation axis and rotation axis what the limits are. -* [PathConstraint](@ref PathConstraintSettings) - This constraint allows attaching two bodies connected through a Hermite spline path. -* [GearConstraint](@ref GearConstraintSettings) - This constraint connects to two hinge joints and constrains them to connect two gears. -* [RackAndPinionConstraint](@ref RackAndPinionConstraintSettings) - This constraint connects a hinge and a slider constraint to connect a rack and pinion. -* [PulleyConstraint](@ref PulleyConstraintSettings) - This constraint connects two bodies through two fixed points creating something that behaves like two bodies connected through a rope. -* [VehicleConstraint](@ref VehicleConstraintSettings) - This constraint adds virtual wheels or tracks to a body and allows it to behave as a vehicle. - -If you want to constrain a dynamic object to the unmovable 'world' you can use [Body::sFixedToWorld](@ref Body::sFixedToWorld) instead of creating a static body. - -Bodies do not keep track of the constraints that are connected to them. This means that you're responsible for removing any constraints attached to a body before removing the body from the PhysicsSystem. - -Adding and removing constraints can be done from multiple threads, but the constraints themselves do not have any protection against concurrent access. We assume that constraints are owned by some object (e.g. a Ragdoll) and that object ensures that it only modifies its own constraints and contains its own synchronization logic. Constraints can be freely modified except during the physics simulation step. - -Contact constraints (when bodies collide) are not handled through the [Constraint](@ref Constraint) class but through the [ContactConstraintManager](@ref ContactConstraintManager) which is considered an internal class. - -## Constraint Motors {#constraint-motors} - -Most of the constraints support motors (see [MotorSettings](@ref MotorSettings)) which allow you to apply forces/torques on two constrained bodies to drive them to a relative position/orientation. There are two types of motors: -* Linear motors: These motors drive the relative position between two bodies. A linear motor would, for example, slide a body along a straight line when you use a slider constraint. -* Angular motors: These motors drive the relative rotation between two bodies. An example is a hinge constraint. The motor drives the rotation along the hinge axis. - -Motors can have three states (see [EMotorState](@ref EMotorState) or e.g. SliderConstraint::SetMotorState): -* Off: The motor is not doing any work. -* Velocity: This type of motor drives the relative velocity between bodies. For a slider constraint, you would push the bodies towards/away from each other with constant velocity. For a hinge constraint, you would rotate the bodies relative to each other with constant velocity. Set the target velocity through e.g. SliderConstraint::SetTargetVelocity / HingeConstraint::SetTargetAngularVelocity. -* Position: This type of motor drives the relative position between bodies. For a slider constraint, you can specify the relative distance you want to achieve between the bodies. For a hinge constraint you can specify the relative angle you want to achieve between the bodies. Set the target position through e.g. SliderConstraint::SetTargetPosition / HingeConstraint::SetTargetAngle. - -Motors apply a force (when driving position) or torque (when driving angle) every simulation step to achieve the desired velocity or position. You can control the maximum force/torque that the motor can apply through MotorSettings::mMinForceLimit, MotorSettings::mMaxForceLimit, MotorSettings::mMinTorqueLimit and MotorSettings::mMaxTorqueLimit. Note that if a motor is driving to a position, the torque limits are not used. If a constraint is driving to an angle, the force limits are not used. - -Usually the limits are symmetric, so you would set -mMinForceLimit = mMaxForceLimit. This way the motor can push at an equal rate as it can pull. If you would set the range to e.g. [0, FLT_MAX] then the motor would only be able to push in the positive direction. The units for the force limits are Newtons and the values can get pretty big. If your motor doesn't seem to do anything, chances are that you have set the value too low. Since Force = Mass * Acceleration you can calculate the approximate force that a motor would need to supply in order to be effective. Usually the range is set to [-FLT_MAX, FLT_MAX] which lets the motor achieve its target as fast as possible. - -For an angular motor, the units are Newton Meters. The formula is Torque = Inertia * Angular Acceleration. Inertia of a solid sphere is 2/5 * Mass * Radius^2. You can use this to get a sense of the amount of torque needed to get the angular acceleration you want. Again, you'd usually set the range to [-FLT_MAX, FLT_MAX] to not limit the motor. - -When settings the force or torque limits to [-FLT_MAX, FLT_MAX] a velocity motor will accelerate the bodies to the desired relative velocity in a single time step (if no other forces act on those bodies). - -Position motors have two additional parameters: Frequency (MotorSettings::mSpringSettings.mFrequency, Hz) and damping (MotorSettings::mSpringSettings.mDamping, no units). They are implemented as described in [Soft Constraints: Reinventing The Spring - Erin Catto - GDC 2011](https://box2d.org/files/ErinCatto_SoftConstraints_GDC2011.pdf). - -You can see a position motor as a spring between the target position and the rigid body. The force applied to reach the target is linear with the distance between current position and target position. When there is no damping, the position motor will cause the rigid body to oscillate around its target. - -![A rigid body on a slider constraint. The body starts at 1 and is driven to 0 with a position motor. Two different motor frequencies are shown. The higher the frequency, the faster the motor will reach its target, but without damping it will overshoot and oscillate forever.](Images/MotorFrequency.jpg) - -Valid frequencies are in the range (0, 0.5 * simulation frequency]. A frequency of 0 results in no force being applied, a frequency larger than half of the physics simulation frequency will result in instability. For a 60 Hz physics simulation, 20 is a good value for a stiff spring (without damping it will reach its target in 1/(4 * 20) = 0.0125 s), 2 is good for a soft spring (will reach its target in 1/(4 * 2) = 0.125 s). - -In order to prevent the motor from overshooting its target, we use damping. - -![A rigid body on a slider constraint. The body starts at 1 and is driven to 0 with a position motor. The frequency of the motor is 2 Hz and the lines correspond to different damping values.](Images/MotorDamping.jpg) - -Sensible values for damping are [0, 1] but higher values are also possible. When the damping is below 1, the body will still oscillate around its target, but that oscillation will die out. When the damping is 1 (called critical damping) there is no oscillation at all but it will take longer for the motor to reach its target. When damping is bigger than 1, the system is over dampened. There will not be any oscillation, but it will take even longer for the motor to reach its target. - -Because Jolt Physics uses a Symplectic Euler integrator, there will still be a small amount of damping when damping is 0, so you cannot get infinite oscillation (allowing this would make it very likely for the system to become unstable). - -## Breakable Constraints {#breakable-constraints} - -Constraints can be turned on / off by calling Constraint::SetEnabled. After every simulation step, check the total 'lambda' applied on each constraint and disable the constraint if the value goes over a certain threshold. Use e.g. SliderConstraint::GetTotalLambdaPosition / HingeConstraint::GetTotalLambdaRotation. You can see 'lambda' as the linear/angular impulse applied at the constraint in the last physics step to keep the constraint together. - -# Collision Detection {#collision-detection} - -## Broad Phase {#broad-phase} - -When bodies are added to the PhysicsSystem, they are inserted in the broad phase ([BroadPhaseQuadTree](@ref BroadPhaseQuadTree)). This provides quick coarse collision detection based on the axis aligned bounding box (AABB) of a body. - -![To quickly test if two objects overlap you can check if their axis aligned bounding boxes overlap. If they do, a check between the actual shapes is needed to be sure.](Images/EllipsoidAABB.png) - -Our broad phase is a quad tree, which means each node has 4 children. In the following image you see a random collection of spheres and triangles and a possible way to split the tree. - -![QuadTree Example](Images/QuadTreeExample.png) - - At the highest level we split all objects in 4 mostly disjoint sets. Note that nodes are allowed to overlap, but for efficiency reasons we want the amount of overlap to be minimal. The example split here is indicated by a red, blue, green and yellow box and you can see them appear in the tree on the right. Three out of four nodes: blue, yellow and red, have 4 or less shapes in them, so the tree can directly point at the shapes rather than at a next node. One node: green, has more than 4 shapes in it so needs a further split. The three shapes can be added directly to the node and we need to create a new node, dotted green, to hold the last two shapes. The reason why we pick 4 children is that modern CPUs support doing 4 math operations in a single instruction, so when we walk the tree from top to bottom during a collision query, we can handle 4 children at the same time and quickly get to a minimal set of colliding objects. - -Since we want to access bodies concurrently the broad phase has special behavior. When a body moves, all nodes in the AABB tree from root to the node where the body resides will be expanded using a lock-free approach. This way multiple threads can move bodies at the same time without requiring a lock on the broad phase. Nodes that have been expanded are marked and during the next physics step a new tight-fitting tree will be built in the background while the physics step is running. This new tree will replace the old tree before the end of the simulation step. This is possible since no bodies can be added/removed during the physics step. For more information about this see the [GDC 2022 talk](https://jrouwe.nl/architectingjolt/ArchitectingJoltPhysics_Rouwe_Jorrit_Notes.pdf). - -The broad phase is divided in layers (BroadPhaseLayer), each broad phase layer has an AABB quad tree associated with it. A standard setup would be to have at least 2 broad phase layers: One for all static bodies (which is infrequently updated but is expensive to update since it usually contains most bodies) and one for all dynamic bodies (which is updated every simulation step but cheaper to update since it contains fewer objects). In general you should only have a few broad phase layers as there is overhead in querying and maintaining many different broad phase trees. - -When doing a query against the broad phase ([BroadPhaseQuery](@ref BroadPhaseQuery)), you generally will get a body ID for intersecting objects. If a collision query takes a long time to process the resulting bodies (e.g. across multiple simulation steps), you can safely keep using the body ID's as specified in the @ref bodies section. - -## Narrow Phase {#narrow-phase} - -A narrow phase query ([NarrowPhaseQuery](@ref NarrowPhaseQuery)) will first query the broad phase for intersecting bodies and will under the protection of a body lock construct a transformed shape ([TransformedShape](@ref TransformedShape)) object. This object contains the transform, a reference counted shape and a body ID. Since the shape will not be deleted until you destroy the TransformedShape object, it is a consistent snapshot of the collision information of the body. This ensures that the body is only locked for a short time frame and makes it possible to do the bulk of the collision detection work outside the protection of a lock. - -For very long running jobs (e.g. navigation mesh creation) it is possible to query all transformed shapes in an area and then do the processing work using a long running thread without requiring additional locks (see [NarrowPhaseQuery::CollectTransformedShapes](@ref NarrowPhaseQuery::CollectTransformedShapes)). - -The narrow phase queries are all handled through the [GJK](@ref GJKClosestPoint) and [EPA](@ref EPAPenetrationDepth) algorithms. - -## Collision Filtering {#collision-filtering} - -Each Body is in an [ObjectLayer](@ref ObjectLayer). If two object layers don't collide, the bodies inside those layers cannot collide. You can define object layers in any way you like, it could be a simple number from 0 to N or it could be a bitmask. Jolt supports 16 or 32 bit ObjectLayers through the JPH_OBJECT_LAYER_BITS define and you're free to define as many as you like as they don't incur any overhead in the system. - -When constructing the PhysicsSystem you need to provide a number of filtering interfaces: -* BroadPhaseLayerInterface: This class defines a mapping from ObjectLayer to BroadPhaseLayer through the BroadPhaseLayerInterface::GetBroadPhaseLayer function. Each Body can only be in 1 BroadPhaseLayer so an ObjectLayer maps to 1 BroadphaseLayer. In general there will be multiple ObjectLayers mapping to the same BroadPhaseLayer (because each broad phase layer comes at a cost). If there are multiple object layers in a single broad phase layer, they are stored in the same tree. When a query visits the tree it will visit all objects whose AABB overlaps with the query and only when the overlap is detected, the actual object layer will be checked. This means that you should carefully design which object layers end up in which broad phase layer, balancing the requirement of having few broad phase layers with the number of needless objects that are visited because multiple object layers share the same broad phase layer. You can define JPH_TRACK_BROADPHASE_STATS to let Jolt print out some statistics about the query patterns your application is using. In general it is wise to start with only 2 broad phase layers as listed in the \ref broad-phase section. -* ObjectVsBroadPhaseLayerFilter: This class defines a ObjectVsBroadPhaseLayerFilter::ShouldCollide function that checks if an ObjectLayer collides with objects that reside in a particular BroadPhaseLayer. ObjectLayers can collide with as many BroadPhaseLayers as needed, so it is possible for a collision query to visit multiple broad phase trees. -* ObjectLayerPairFilter: This class defines a ObjectLayerPairFilter::ShouldCollide function that checks if an ObjectLayer collides with another ObjectLayer. - -As an example we will use a simple enum as ObjectLayer: -* NON_MOVING - Layer for all static objects. -* MOVING - Layer for all regular dynamic bodies. -* DEBRIS - Layer for all debris dynamic bodies, we want to test these only against the static geometry because we want to save some simulation cost. -* BULLET - Layer for high detail collision bodies that we attach to regular dynamic bodies. These are not used for simulation but we want extra precision when we shoot with bullets. -* WEAPON - This is a query layer so we don't create any bodies with this layer but we use it when doing ray cast querying for our weapon system. - -We define the following object layers to collide: -* MOVING vs NON_MOVING, MOVING vs MOVING - These are for our regular dynamic objects that need to collide with the static world and with each other. -* DEBRIS vs NON_MOVING - As said, we only want debris to collide with the static world and not with anything else. -* WEAPON vs BULLET, WEAPON vs NON_MOVING - We want our weapon ray cast to hit the high detail BULLET collision instead of the normal MOVING collision and we want bullets to be blocked by the static world (obviously the static world could also have a high detail version, but not in this example). - -This means that we need to implement a ObjectLayerPairFilter::ShouldCollide that returns true for the permutations listed above. Note that if ShouldCollide(A, B) returns true, ShouldCollide(B, A) should return true too. - -We define the following broad phase layers: -* BP_NON_MOVING - For everything static (contains object layer: NON_MOVING). -* BP_MOVING - The default layer for dynamic objects (contains object layers: MOVING, BULLET). -* BP_DEBRIS - An extra layer that contains only debris (contains object layers: DEBRIS). - -This means we now implement a BroadPhaseLayerInterface::GetBroadPhaseLayer that maps: NON_MOVING -> BP_NON_MOVING, MOVING -> BP_MOVING, BULLET -> BP_MOVING and DEBRIS -> BP_DEBRIS. We can map WEAPON to anything as we won't create any objects with this layer. - -We also need to implement a ObjectVsBroadPhaseLayerFilter::ShouldCollide that determines which object layer should collide with what broad phase layers, these can be deduced from the two lists above: -* NON_MOVING: BP_MOVING, BP_DEBRIS -* MOVING: BP_NON_MOVING, BP_MOVING -* DEBRIS: BP_NON_MOVING -* BULLET: None (these are not simulated so need no collision with other objects) -* WEAPON: BP_NON_MOVING, BP_MOVING - -So you can see now that when we simulate DEBRIS we only need to visit a single broad phase tree to check for collision, we did this because in our example we know that there are going to be 1000s of debris objects so it is important that their queries are as fast as possible. We could have moved the BULLET layer to its own broad phase layer too because now BP_MOVING contains a lot of bodies that WEAPON is not interested in, but in this example we didn't because we know that there are not enough of these objects for this to be a performance problem. - -For convenience two filtering implementations are provided: -* ObjectLayerPairFilterTable, ObjectVsBroadPhaseLayerFilterTable and BroadPhaseLayerInterfaceTable: These three implement collision layers as a simple table. You construct ObjectLayerPairFilterTable with a fixed number of object layers and then call ObjectLayerPairFilterTable::EnableCollision or ObjectLayerPairFilterTable::DisableCollision to selectively enable or disable collisions between layers. BroadPhaseLayerInterfaceTable is constructed with a number of broad phase layers. You can then map each object layer to a broad phase layer through BroadPhaseLayerInterfaceTable::MapObjectToBroadPhaseLayer. -* ObjectLayerPairFilterMask, ObjectVsBroadPhaseLayerFilterMask and BroadPhaseLayerInterfaceMask: These split an ObjectLayer in an equal amount of bits for group and mask. Two objects collide if (object1.group & object2.mask) != 0 && (object2.group & object1.mask) != 0. This behavior is similar to e.g. Bullet. In order to map groups to broad phase layers, you call BroadPhaseLayerInterfaceMask::ConfigureLayer for each broad phase layer. You determine which groups can be put in that layer and which group must be excluded from that layer. E.g. a broad phase layer could include everything that has the STATIC group but should exclude everything that has the SENSOR group, so that if an object has both STATIC and SENSOR bits set, this broad phase layer will not be used. The broad phase layers are checked one by one and the first one that meets the condition is the one that the body will be put in. If you use this implementation, consider setting the cmake option OBJECT_LAYER_BITS to 32 to get a 32-bit ObjectLayer instead of a 16-bit one. - -Now that we know about the basics, we list the order in which the collision detection pipeline goes through the various collision filters: - -* Broadphase layer: At this stage, the object layer is tested against the broad phase trees that are relevant by checking the [ObjectVsBroadPhaseLayerFilter](@ref ObjectVsBroadPhaseLayerFilter). -* Object layer: Once the broad phase layer test succeeds, we will test object layers vs object layers through [ObjectLayerPairFilter](@ref ObjectLayerPairFilter) (used for simulation) and [ObjectLayerFilter](@ref ObjectLayerFilter) (used for collision queries). The default implementation of ObjectLayerFilter is DefaultObjectLayerFilter and uses ObjectLayerPairFilter so the behavior is consistent between simulation and collision queries. -* Group filter: Most expensive filtering (bounding boxes already overlap), used only during simulation. Allows you fine tune collision e.g. by discarding collisions between bodies connected by a constraint. See [GroupFilter](@ref GroupFilter) and implementation for ragdolls [GroupFilterTable](@ref GroupFilterTable). -* Body filter: This filter is used instead of the group filter if you do collision queries like CastRay. See [BodyFilter](@ref BodyFilter). -* Shape filter: This filter is used only during collision queries and can be used to filter out individual (sub)shapes. See [ShapeFilter](@ref ShapeFilter). -* Contact listener: During simulation, after all collision detection work has been performed you can still choose to discard a contact point. This is a very expensive way of rejecting collisions as most of the work is already done. See [ContactListener](@ref ContactListener). - -To avoid work, try to filter out collisions as early as possible. - -## Continuous Collision Detection {#continuous-collision-detection} - -Each body has a motion quality setting ([EMotionQuality](@ref EMotionQuality)). By default the motion quality is [Discrete](@ref Discrete). This means that at the beginning of each simulation step we will perform collision detection and if no collision is found, the body is free to move according to its velocity. This usually works fine for big or slow moving objects. Fast and small objects can easily 'tunnel' through thin objects because they can completely move through them in a single time step. For these objects there is the motion quality [LinearCast](@ref LinearCast). Objects that have this motion quality setting will do the same collision detection at the beginning of the simulation step, but once their new position is known, they will do an additional CastShape to check for any collisions that may have been missed. If this is the case, the object is placed back to where the collision occurred and will remain there until the next time step. This is called 'time stealing' and has the disadvantage that an object may appear to move much slower for a single time step and then speed up again. The alternative, back stepping the entire simulation, is computationally heavy so was not implemented. - -![With the Discrete motion quality the blue object tunnels through the green object in a single time step. With motion quality LinearCast it doesn't.](Images/MotionQuality.jpg) - -Fast rotating long objects are also to be avoided, as the LinearCast motion quality will fully rotate the object at the beginning of the time step and from that orientation perform the CastShape, there is a chance that the object misses a collision because it rotated through it. - -![Even with the LinearCast motion quality the blue object rotates through the green object in a single time step.](Images/LongAndThin.jpg) - -## Ghost Collisions {#ghost-collisions} - -A ghost collision can occur when a body slides over another body and hits an internal edge of that body. The most common case is where a body hits an edge of a triangle in a mesh shape but it can also happen on 2 box shapes as shown below. - -![A blue box sliding over 2 green boxes. Because the blue box can sink into the green box a little bit, it can hit the edge between the two boxes. This will cause the box to stop or jump up.](Images/GhostCollision.jpg) - -There are a couple of ways to avoid ghost collisions in Jolt. MeshShape and HeightFieldShape keep track of active edges during construction. - -![An inactive edge (concave) and an active edge (convex, angle > threshold angle).](Images/ActiveEdge.jpg) - -Whenever a body hits an inactive edge, the contact normal is the face normal. When it hits an active edge, it can be somewhere in between the connecting face normals so the movement of the body is impeded in the scenario below. - -![Contact normal (red) of hitting an active vs an inactive edge.](Images/ActiveVsInactiveContactNormal.jpg) - -By tweaking MeshShapeSettings::mActiveEdgeCosThresholdAngle or HeightFieldShapeSettings::mActiveEdgeCosThresholdAngle you can determine the angle at which an edge is considered an active edge. By default this is 5 degrees, making this bigger reduces the amount of ghost collisions but can create simulation artifacts if you hit the edge straight on. - -To further reduce ghost collisions, you can turn on BodyCreationSettings::mEnhancedInternalEdgeRemoval. When enabling this setting, additional checks will be made at run-time to detect if an edge is active or inactive based on all of the contact points between the two bodies. Beware that this algorithm only considers 2 bodies at a time, so if the two green boxes above belong to two different bodies, the ghost collision can still occur. Use a StaticCompoundShape to combine the boxes in a single body to allow the system to eliminate ghost collisions between the blue and the two green boxes. You can also use this functionality for your custom collision tests by making use of InternalEdgeRemovingCollector. - -# Character Controllers {#character-controllers} - -The [Character](@ref Character) and [CharacterVirtual](@ref CharacterVirtual) classes can be used to create a character controller. These are usually used to represent the player as a simple capsule or tall box and perform collision detection while the character navigates through the world. - -The Character class is the simplest controller and is essentially a rigid body that has been configured to only allow translation (and no rotation so it stays upright). It is simulated together with the other rigid bodies so it properly reacts to them. Because it is simulated, it is usually not the best solution for a player as the player usually requires a lot of behavior that is non-physical. This character controller is cheap so it is recommended for e.g. simple AI characters. After every PhysicsSystem::Update call you must call Character::PostSimulation to update the ground contacts. - -The CharacterVirtual class is much more advanced. It is implemented using collision detection functionality only (through NarrowPhaseQuery) and is simulated when CharacterVirtual::Update is called. Since the character is not 'added' to the world, it is not visible to rigid bodies and it only interacts with them during the CharacterVirtual::Update function by applying impulses. This does mean there can be some update order artifacts, like the character slightly hovering above an elevator going down, because the characters moves at a different time than the other rigid bodies. Separating it has the benefit that the update can happen at the appropriate moment in the game code. Multiple CharacterVirtuals can update concurrently, so it is not an issue if the game code is parallelized. - -CharacterVirtual has the following extra functionality: -* Sliding along walls -* Interaction with elevators and moving platforms -* Enhanced steep slope detection (standing in a funnel whose sides are too steep to stand on will not be considered as too steep) -* Stair stepping through the CharacterVirtual::ExtendedUpdate call -* Sticking to the ground when walking down a slope through the CharacterVirtual::ExtendedUpdate call -* Support for specifying a local coordinate system that allows e.g. [walking around in a flying space ship](https://github.com/jrouwe/JoltPhysics/blob/master/Samples/Tests/Character/CharacterSpaceShipTest.cpp) that is equipped with 'inertial dampers' (a sci-fi concept often used in games). - -If you want CharacterVirtual to have presence in the world, it is recommended to pair it with a slightly smaller [Kinematic](@ref EMotionType) body (or Character). After each update, move this body using BodyInterface::MoveKinematic to the new location. This ensures that standard collision tests like ray casts are able to find the character in the world and that fast moving objects with motion quality [LinearCast](@ref EMotionQuality) will not pass through the character in 1 update. As an alternative to a Kinematic body, you can also use a regular Dynamic body with a [gravity factor](@ref BodyCreationSettings::mGravityFactor) of 0. Ensure that the character only collides with dynamic objects in this case. The advantage of this approach is that the paired body doesn't have infinite mass so is less strong. - -Characters are usually driven in a kinematic way (i.e. by calling Character::SetLinearVelocity or CharacterVirtual::SetLinearVelocity before their update). - -To get started take a look at the [Character](https://github.com/jrouwe/JoltPhysics/blob/master/Samples/Tests/Character/CharacterTest.cpp) and [CharacterVirtual](https://github.com/jrouwe/JoltPhysics/blob/master/Samples/Tests/Character/CharacterVirtualTest.cpp) examples. - -# The Simulation Step {#the-simulation-step} - -The simulation step [PhysicsSystem::Update](@ref PhysicsSystem::Update) uses jobs ([JobSystem](@ref JobSystem)) to perform the needed work. This allows spreading the workload across multiple CPU's. We use a Sequential Impulse solver with warm starting as described in [Modeling and Solving Constraints - Erin Catto](https://box2d.org/files/ErinCatto_ModelingAndSolvingConstraints_GDC2009.pdf) - -Each physics step can be divided into multiple collision steps. So if you run the simulation at 60 Hz with 2 collision steps we run: - -* Collision (1/120s) -* Integration (1/120s) -* Collision (1/120s) -* Integration (1/120s) - -In general, the system is stable when running at 60 Hz with 1 collision step. - -# Conventions and Limits {#conventions-and-limits} - -Jolt Physics uses a right handed coordinate system with Y-up. It is easy to use another axis as up axis by changing the gravity vector using [PhysicsSystem::SetGravity](@ref PhysicsSystem::SetGravity). Some shapes like the [HeightFieldShape](@ref HeightFieldShapeSettings) will need an additional [RotatedTranslatedShape](@ref RotatedTranslatedShapeSettings) to rotate it to the new up axis and vehicles ([VehicleConstraint](@ref VehicleConstraintSettings)) and characters ([CharacterBaseSettings](@ref CharacterBaseSettings)) will need their new up-axis specified too. - -We use column-major vectors and matrices, this means that to transform a point you need to multiply it on the right hand side: TransformedPoint = Matrix * Point. - -Note that the physics simulation works best if you use SI units (meters, radians, seconds, kg). In order for the simulation to be accurate, dynamic objects should be in the order [0.1, 10] meters long, have speeds in the order of [0, 500] m/s and have gravity in the order of [0, 10] m/s^2. Static object should be in the order [0.1, 2000] meter long. If you are using different units, consider scaling the objects before passing them on to the physics simulation. - -# Big Worlds {#big-worlds} - -By default the library compiles using floats. This means that the simulation gets less accurate the further you go from the origin. If all simulation takes place within roughly 5 km from the origin, floating point precision is accurate enough. - -If you have a bigger world, you may want to compile the library using the JPH_DOUBLE_PRECISION define. When you do this, all positions will be stored as doubles, which will make the simulation accurate even at thousands of kilometers away from the origin. - -Calculations with doubles are much slower than calculations with floats. A naive implementation that changes all calculations to doubles has been measured to run more than 2x slower than the same calculations using floats. Because of this, Jolt Physics will only use doubles where necessary and drop down to floats as soon as possible. In order to do this, many of the collision query functions will need a 'base offset'. All collision results will be returned as floats relative to this base offset. By choosing the base offset wisely (i.e. close to where collision results are expected) the results will be accurate. Make sure your base offset is not kilometers away from the collision result. - -Keep in mind that: - -* There are a lot of 'epsilons' in the code that have been tuned for objects of sizes/speeds as described in the @ref conventions-and-limits section. Try to keep the individual objects to the specified scale even if they're really far from the origin. -* When the collision results of a single query are kilometers apart, precision will suffer as they will be far away from the 'base offset'. -* The effectiveness of the broad phase (which works in floats) will become less at large distances from the origin, e.g. at 10000 km from the origin, the resolution of the broad phase is reduced to 1 m which means that everything that's closer than 1 m will be considered colliding. This will not impact the quality of the simulation but it will result in extra collision tests in the narrow phase so will hurt performance. - -Because of the minimal use of doubles, the simulation runs 5-10% slower in double precision mode compared to float precision mode. - -# Deterministic Simulation {#deterministic-simulation} - -The physics simulation is deterministic provided that: - -* The APIs that modify the simulation are called in exactly the same order. For example, bodies and constraints need to be added/removed/modified in exactly the same order so that the state at the beginning of a simulation step is exactly the same for both simulations. -* The same binary code is used to run the simulation. For example, when you run the simulation on Windows it doesn't matter if you have an AMD or Intel processor. - -If you want cross platform determinism then please turn on the CROSS_PLATFORM_DETERMINISTIC option in CMake. This will make the library approximately 8% slower but the simulation will be deterministic regardless of: - -* Compiler used to compile the library (tested MSVC2022 vs clang) -* Configuration (Debug, Release or Distribution) -* OS (tested Windows, macOS, Linux) -* Architecture (x86 or ARM). - -Some caveats: - -* The same source code must be used to compile the library on all platforms. -* The source code must be compiled with the same defines, e.g. you can't have one platform using JPH_DOUBLE_PRECISION and another not. - -It is quite difficult to verify cross platform determinism, so this feature is less tested than other features. With every build, the following architectures are verified to produce the same results: - -* Windows MSVC x86 64-bit with AVX2 -* Windows MSVC x86 32-bit with SSE2 -* macOS clang x86 64-bit with AVX -* Linux clang x86 64-bit with AVX2 -* Linux clang ARM 64-bit with NEON - -The most important things to look out for in your own application: - -* Compile your application mode in Precise mode (clang: -ffp-model=precise, MSVC: /fp:precise) -* Turn off floating point contract operations (clang: -ffp-contract=off) -* Do not use the standard trigonometry functions (sin, cos etc.) as they have different implementations on different platforms, use Jolt's functions (Sin, Cos etc.) -* Do not use std::sort as it has a different implementation on different platforms, use Jolt's QuickSort function - -When running the Samples Application you can press ESC, Physics Settings and check the 'Check Determinism' checkbox. Before every simulation step we will record the state using the [StateRecorder](@ref StateRecorder) interface, rewind the simulation and do the step again to validate that the simulation runs deterministically. Some of the tests (e.g. the MultiThreaded) test will explicitly disable the check because they randomly add/remove bodies from different threads. This violates the rule that the API calls must be done in the same order so will not result in a deterministic simulation. - -# Rolling Back a Simulation {#rolling-back-a-simulation} - -When synchronizing two simulations via a network, it is possible that a change that needed to be applied at frame N is received at frame N + M. This will require rolling back the simulation to the state of frame N and repeating the simulation with the new inputs. This can be implemented by saving the physics state using [SaveState](@ref PhysicsSystem::SaveState) at every frame. To roll back, call [RestoreState](@ref PhysicsSystem::RestoreState) with the state at frame N. SaveState only records the state that the physics engine modifies during its update step (positions, velocities etc.), so if you change anything else you need to restore this yourself. E.g. if you did a [SetFriction](@ref Body::SetFriction) on frame N + 2 then, when rewinding, you need to restore the friction to what is was on frame N and update it again on frame N + 2 when you replay. If you start adding/removing objects (e.g. bodies or constraints) during these frames, the RestoreState function will not work. If you added a body on frame N + 1, you'll need to remove it when rewinding and then add it back on frame N + 1 again (with the proper initial position/velocity etc. because it won't be contained in the snapshot at frame N). - -If you wish to share saved state between server and client, you need to ensure that all APIs that modify the state of the world are called in the exact same order. So if the client creates physics objects for player 1 then 2 and the server creates the objects for 2 then 1 you already have a problem (the body IDs will be different, which will render the save state snapshots incompatible). When rolling back a simulation, you'll also need to ensure that the BodyIDs are kept the same, so you need to remove/add the body from/to the physics system instead of destroy/re-create them or you need to create bodies with the same ID on both sides using [BodyInterface::CreateBodyWithID](@ref BodyInterface::CreateBodyWithID). - -# Working With Multiple Physics Systems {#working-with-multiple-physics-systems} - -You can create, simulate and interact with multiple PhysicsSystems at the same time provided that you do not share any objects (bodies, constraints) between the systems. -When a Body is created it receives a BodyID that is unique for the PhysicsSystem that it was created for, so it cannot be shared. The only object that can be shared between PhysicsSystems is a Shape. -If you want to move a body from one PhysicsSystem to another, use Body::GetBodyCreationSettings to get the settings needed to create the body in the other PhysicsSystem. - -PhysicsSystems are not completely independent: - -* There is only 1 RTTI factory (Factory::sInstance). -* There is only 1 default material (PhysicsMaterial::sDefault). -* There is only 1 debug renderer (DebugRenderer::sInstance) although many functions take a custom DebugRenderer for drawing. -* Custom shapes and CollisionDispatch functions are shared. -* The custom memory allocation functions (e.g. Allocate), Trace and AssertFailed functions are shared. - -These functions / systems need to be registered in advance. - -# Debug Rendering {#debug-rendering} - -When the define JPH_DEBUG_RENDERER is defined (which by default is defined in Debug and Release but not Distribution), Jolt is able to render its internal state. To integrate this into your own application you must inherit from the DebugRenderer class and implement the pure virtual functions DebugRenderer::DrawLine, DebugRenderer::DrawTriangle, DebugRenderer::CreateTriangleBatch, DebugRenderer::DrawGeometry and DebugRenderer::DrawText3D. The CreateTriangleBatch is used to prepare a batch of triangles to be drawn by a single DrawGeometry call, which means that Jolt can render a complex scene much more efficiently than when each triangle in that scene would have been drawn through DrawTriangle. At run-time create an instance of your DebugRenderer which will internally assign itself to DebugRenderer::sInstance. Finally call for example PhysicsSystem::DrawBodies or PhysicsSystem::DrawConstraints to draw the state of the simulation. For an example implementation see [the DebugRenderer from the Samples application](https://github.com/jrouwe/JoltPhysics/blob/master/TestFramework/Renderer/DebugRendererImp.h) or to get started quickly take a look at DebugRendererSimple. - -# Memory Management {#memory-management} - -Jolt uses reference counting for a number of its classes (everything that inherits from RefTarget). The most important classes are: - -* ShapeSettings -* Shape -* ConstraintSettings -* Constraint -* PhysicsMaterial -* GroupFilter -* PhysicsScene -* SoftBodySharedSettings -* VehicleCollisionTester -* VehicleController -* WheelSettings -* CharacterBaseSettings -* CharacterBase -* RagdollSettings -* Ragdoll -* Skeleton -* SkeletalAnimation -* SkeletonMapper - -Reference counting objects start with a reference count of 0. If you want to keep ownership of the object, you need to call [object->AddRef()](@ref RefTarget::AddRef), this will increment the reference count. If you want to release ownership you call [object->ReleaseRef()](@ref RefTarget::Release), this will decrement the reference count and if the reference count reaches 0 the object will be destroyed. If, after newing, you pass a reference counted object on to another object (e.g. a ShapeSettings to a CompoundShapeSettings or a Shape to a Body) then that other object will take a reference, in that case it is not needed take a reference yourself beforehand so you can skip the calls to ```AddRef/Release```. Note that it is also possible to do ```auto x = new XXX``` followed by ```delete x``` for a reference counted object if no one ever took a reference. The safest way of working with reference counting objects is to use the Ref or RefConst classes, these automatically manage the reference count for you when assigning a new value or on destruction: - -``` -// Calls 'AddRef' to keep a reference the shape -JPH::Ref shape = new JPH::SphereShape(1.0f); - -// Calls 'Release' to release and delete the shape (note that this also happens if JPH::Ref goes out of scope) -shape = nullptr; -``` - -The Body class is a special case, it is destroyed through BodyInterface::DestroyBody (which internally destroys the Body). - -Jolt also supports routing all of its internal allocations through a custom allocation function. See: [Allocate](@ref Allocate), [Free](@ref Free), [AlignedAllocate](@ref AlignedAllocate) and [AlignedFree](@ref AlignedFree). - -# The Simulation Step in Detail {#the-simulation-step-in-detail} - -The job graph looks like this: - -![Job Graph Physics Step](PhysicsSystemUpdate.svg) - -Note that each job indicates if it reads/writes positions/velocities and if it deactivates/activates bodies. We do not allow jobs to read/write the same data concurrently. The arrows indicate the order in which jobs are executed. Yellow blocks mean that there are multiple jobs of this type. Dotted arrows have special meaning and are explained below. - -## Broad Phase Update Prepare {#broad-phase-update-prepare} - -This job will refit the AABBs of the broad phase. It does this by building a new tree while keeping the old one available as described in the @ref broad-phase section. - -## Broad Phase Update Finalize {#broad-phase-update-finalize} - -This job will simply swap the new tree with the old tree. The old tree will be discarded at the beginning of the next PhysicsSystem::Update call so that any broad phase query can continue to run. - -## Step Listeners {#step-listeners-update} - -You can register one or more step listeners (See [PhysicsSystem::AddStepListener](@ref PhysicsSystem::AddStepListener)). This job will call [PhysicsStepListener::OnStep](@ref PhysicsStepListener::OnStep) for every listener. This can be used to do work that needs to be done at the beginning of each step, e.g. set velocities on ragdoll bodies. - -## Apply Gravity {#apply-gravity-update} - -A number of these jobs run in parallel. Each job takes a batch of active bodies and applies gravity and damping (updating linear and angular velocity). - -## Determine Active Constraints {#determine-active-constraints} - -This job will go through all non-contact constraints and determine which constraints are active based on if the bodies that the constraint connects to are active. - -## Build Islands from Constraints {#build-islands-from-constraints} - -This job will go through all non-contact constraints and assign the involved bodies and constraint to the same island. Since we allow concurrent insertion/removal of bodies we do not want to keep island data across multiple simulation steps, so we recreate the islands from scratch every simulation step. The operation is lock-free and O(N) where N is the number of constraints. - -If a constraint connects an active and a non-active body, the non-active body is woken up. One find collisions job will not start until this job has finished in order to pick up any collision testing for newly activated bodies. - -## Find Collisions {#find-collisions} - -This job will do broad and narrow phase checks. Initially a number of jobs are started based on the amount of active bodies. The job will do the following: - -- Take a batch of active bodies and collide them against the broadphase. -- When a collision pair is found it is inserted in a lock free queue to be processed later. -- If the queue is full, it will be processed immediately (more Find Collisions jobs are spawned if not all CPU cores are occupied yet as the queue starts to fill up). -- If there are no more active bodies to process, the job will start to perform narrow phase collision detection and set up contact constraints if any collisions are found. -- As soon as a narrow phase pair is processed it will recheck if there are new active bodies to be processed (active bodies can be generated by an active body colliding with an inactive body) and if so process them. -- When there are no more active bodies to test and no more collision pairs to be processed the job terminates. - -Note that this job cannot start until apply gravity is done because the velocity needs to be known for elastic collisions to be calculated properly. - -The contact points between the two bodies will be determined by the [GJK](@ref GJKClosestPoint) and [EPA](@ref EPAPenetrationDepth) algorithms. For each contact point we will calculate the face that belongs to that contact point. The faces of both bodies are clipped against each other ([ManifoldBetweenTwoFaces](@ref ManifoldBetweenTwoFaces)) so that we have a polygon (or point / line) that represents the contact between the two bodies (contact manifold). - -Multiple contact manifolds with similar normals are merged together (PhysicsSystem::ProcessBodyPair::ReductionCollideShapeCollector). After this the contact constraints are created in the [ContactConstraintManager](@ref ContactConstraintManager) and their Jacobians / effective masses calculated. - -Contacting bodies are also linked together to form islands. This is the same operation as described in the @ref build-islands-from-constraints section. - -The narrow phase makes use of a lock free contact cache. We have 2 caches, one that is used for reading (which contains the contacts from the previous step) and one for writing new contact pairs. When a contact point is preserved from the last simulation step, it will be copied from the read cache to the write cache. - -## Setup Velocity Constraints {#setup-velocity-constraints} - -This job will go through all non-contact constraints and prepare them for execution. This involves calculating Jacobians and effective masses for each constraint part. - -## Finalize Islands {#finalize-islands} - -This job will finalize the building of the simulation islands. Each island contains bodies that interact with each other through a contact point or through a constraint. These islands will be simulated separately in different jobs later. The finalization of the islands is an O(N) operation where N is the amount of active bodies (see [IslandBuilder::Finalize](@ref IslandBuilder::Finalize)). - -## Set Body Island Idx {#set-body-island-idx} - -This job does some housekeeping work that can be executed concurrent to the solver: - -* It will assign the island ID to all bodies (which is mainly used for debugging purposes) - -## Solve Velocity Constraints {#solve-velocity-constraints} - -A number of these jobs will run in parallel. Each job takes the next unprocessed island and will run the iterative constraint solver for that island. It will first apply the impulses applied from the previous simulation step (which are stored in the contact cache) to warm start the solver. It will then repeatedly iterate over all contact and non-contact constraints until either the applied impulses are too small or a max iteration count is reached ([PhysicsSettings::mNumVelocitySteps](@ref PhysicsSettings::mNumVelocitySteps)). The result will be that the new velocities are known for all active bodies. The applied impulses are stored in the contact cache for the next step. - -When an island consists of more than LargeIslandSplitter::cLargeIslandTreshold contacts plus constraints it is considered a large island. In order to not do all work on a single thread, this island will be split up by the LargeIslandSplitter. This follows an algorithm described in High-Performance Physical Simulations on Next-Generation Architecture with Many Cores by Chen et al. This is basically a greedy algorithm that tries to group contacts and constraints into groups where no contact or constraint affects the same body. Within a group, the order of execution does not matter since every memory location is only read/written once, so we can parallelize the update. At the end of each group, we need to synchronize the CPU cores before starting on the next group. When the number of groups becomes too large, a final group is created that contains all other contacts and constraints and these are solved on a single thread. The groups are processed PhysicsSettings::mNumVelocitySteps times so the end result is almost the same as an island that was not split up (only the evaluation order changes in a consistent way). - -## Pre Integrate {#pre-integrate} - -This job prepares the CCD buffers. - -## Integrate & Clamp Velocities {#integrate-and-clamp-velocities} - -This job will integrate the velocity and update the position. It will clamp the velocity to the max velocity. - -Depending on the motion quality ([EMotionQuality](@ref EMotionQuality)) of the body, it will schedule a body for continuous collision detection (CCD) if its movement is bigger than some threshold based on the [inner radius](@ref Shape::GetInnerRadius)) of the shape. - -## Post Integrate {#post-integrate} - -Find CCD Contact jobs are created on the fly depending on how many CCD bodies were found. If there are no CCD bodies it will immediately start Resolve CCD Contacts. - -## Find CCD Contacts {#find-ccd-contacts} - -A number of jobs will run in parallel and pick up bodies that have been scheduled for CCD and will do a linear cast to detect the first collision. It always allows movement of the object by a fraction if its inner radius in order to prevent it from getting fully stuck. - -## Resolve CCD Contacts {#resolve-ccd-contacts} - -This job will take the collision results from the previous job and update position and velocity of the involved bodies. If an object hits another object, its time will be 'stolen' (it will move less far than it should according to its velocity). - -## Finalize Contact Cache, Contact Removed Callbacks {#finalize-contact-cache} - -This job will: - -* Swap the read/write contact cache and prepare the contact cache for the next step. -* It will detect all contacts that existed previous step and do not exist anymore to fire callbacks for them through the [ContactListener](@ref ContactListener) interface. - -## Solve Position Constraints, Update Bodies Broad Phase {#solve-position-constraints} - -A number of these jobs will run in parallel. Each job takes the next unprocessed island and run the position based constraint solver. This fixes numerical drift that may have caused constrained bodies to separate (remember that the constraints are solved in the velocity domain, so errors get introduced when doing a linear integration step). It will run until either the applied position corrections are too small or until the max amount of iterations is reached ([PhysicsSettings::mNumPositionSteps](@ref PhysicsSettings::mNumPositionSteps)). Here there is also support for large islands, the island splits that were calculated in the Solve Velocity Constraints job are reused to solve partial islands in the same way as before. - -It will also notify the broad phase of the new body positions / AABBs. - -When objects move too little the body will be put to sleep. This is detected by taking the biggest two axis of the local space bounding box of the shape together with the center of mass of the shape (all points in world space) and keep track of 3 bounding spheres for those points over time. If the bounding spheres become too big, the bounding spheres are reset and the timer restarted. When the timer reaches a certain time, the object has is considered non-moving and is put to sleep. - -## Soft Body Prepare {#soft-body-prepare} - -If there are any active soft bodies, this job will create the Soft Body Collide, Simulate and Finalize Jobs. It will also create a list of sorted SoftBodyUpdateContext objects that forms the context for those jobs. - -## Soft Body Collide {#soft-body-collide} - -These jobs will do broadphase checks for all of the soft bodies. A thread picks up a single soft body and uses the bounding box of the soft body to find intersecting rigid bodies. Once found, information will be collected about that rigid body so that Simulate can run in parallel. - -## Soft Body Simulate {#soft-body-simulate} - -These jobs will do the actual simulation of the soft bodies. They first collide batches of soft body vertices with the rigid bodies found during the Collide job (multiple threads can work on a single soft body) and then perform the simulation using XPBD (also partially distributing a single soft body on multiple threads). - -## Soft Body Finalize {#soft-body-finalize} - -This job writes back all the rigid body velocity changes and updates the positions and velocities of the soft bodies. It can activate/deactivate bodies as needed. diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Images/ActiveEdge.jpg b/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Images/ActiveEdge.jpg deleted file mode 100644 index 8ec8073147fc56a2f10d7cd0386064b619eadafa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15391 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 diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Images/ActiveVsInactiveContactNormal.jpg b/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Images/ActiveVsInactiveContactNormal.jpg deleted file mode 100644 index 434bc142078019d69ae2418e148d106d95d11c8e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16028 zcmeHu2UJtr*6v1{A{c4XiGm6Of>J~&iKvK(7%5VPD2NnAL_va-hzLlH$iV_i6hu0q zgS5~CA|Smf5F))Lln_aK+k5VR?;Ypd@!tL4J^%m4`^VYI8e#0U_S$Q%^{qMQH`jzc z#GU|lpFeAS7U1CE08rouV2=W)0nTmPetyA=3;c3#=jP_(;^yP!<=M{9$Iri$Z|6>d zT|&DBb_wpxPO1UWbbIoM4A6aYB5!O{L4@Lw;EZQvMrc(?E1+X*%(-3@Hx;N;xK z#rbp8VCx{T9^ew>7TR~zfJfNu8n2wUh+;DYs_qgZdd*2V`{~+{nSa`&f z$f#!tza&0?@$ywtYTEns4;h(R*+s=ArDf$6A1fOgo0?k)t!_g>LJ@jPQ||~tluH8VNMoxy4%gSXTK)#0OhA>f06886YSCdm1KVt?C)}o z0Q{UB;No!#0&sw7ks754{8|2-gFi9w--CgClga6iK}2*IOmo1ejn$FmK5}5`TkfbR zsQ3c8PO7+P;xrR)N5vS5L9z*uc=oOg1p5l9(s*=3K@%LH)g5U89JRMv`ZL zt_HfHeso%{c6`xZI3|X~yllb-1S}}!tSBhUOa)Er_rI9H2KMHtZB1ggw&(cL_3l>h zG^UF)33Ylm4zhtJwErBM+z*#R&%R>=Q$hqb5Nr0^F49Gj!#2<#YtRa$LlYb5B5bC- z_)Qz=Z~CxKGBcuD@v;I5%aHs@^bCpK1&%B{i#Eyz3?Xbl@j0B~fuVU|WAW>k*uZL} zGaG2h=}=6gD|4~|nDZv@pf`Mt!kkHG1C5ppPd1Pm!3KUYfwHvh@es6!TJ$Cx5Y`j? zFdwW<4^GayIdDwdiVX}O^InNEPIT~7v6V1CYrvZ#fU5Ci=&*rjHRN7aOP&TBIKStk zx9x2{5e3DbD0f(RpEoMfGU{HyCfrUjHHvYI{^bwY2%IG7s4$7pgouEP`*d(Nzz(t+ zdtJUb)jm@zu)lKqm#-JYisg<%mDD5%GAFbq@xPrsXSzQdXlrs~15~9(HjwToI^nxj zX7+-pd^M{xegEi!^JL*r_Bur<=3vi;z8Xt1gXF~&fiqHV*Z?IPPRtHI#s<1ivw=?s zFl;~)rj7BRaLUT-LJ ziQ|thR}={NAN$aP=*262==JC4mlqz>Ky2<;n^a>)Ct+C6+U(6(DnVv%5FuO8#3Tgc zQ7(QV3Oy^A*Kex8GO$8!G{e^gtrj*j8H$V^`-0Rcr6v(GL*Qo-EhjIq&Tlp%=#nK} zOhvn>yW|P;4$>7w5)qddY^zxznzxkqG6tjLhdi}Q-g>h+ zP>P7BDfvYQ+D_u0h`~i%t6wzQ4LKI7eCp79p>w%)O07GKbp>WT zCgPEKpgBXkA|sF`s^OY=S(+We@r-$agU-w4yy8x$H+SYbcK{Qlr-p&1?reaol3c#X z45FAcxBEMj&^-8%4YypME}=OJ!vDj#XWY3DMk8iXxpzbM+6;rttxYRWnSO3GAXVYE z^}Wde=tz6Ru`(-i*_*sQaayOWwtGJX*g#4toS{VsWdpNP`RMT{Xxh>Po)_f726gwc zm@(0A+a!?#$rm3#yB`<4WXlG^TqoGTmZ&D2AqHCGrojd8w4`CX5;R$u`0auy=n%2i zI`G7NJt!ME`RyKy0X8ClRzJLgjAnBBcF`%It0aRn{#{4iY=Ez;o(-%kxuI#Qpl1}B zO+8W0U}0F>)Z8C|w%Th&Z^ReE>UqG>fJs%^p^n=v{IuD-GAZiTY*#orVxIkvjnAL;akp zod~HN+L1}m=3JapN(xp@hMF^`+uTy4);o{<#y|}m_?`DvQHWpe*O%yy0F5GIAWAV6 zzXN`bPcil3zf)k_nVI;BJV#!dZl;H$B>7;Xc5I-ntNdt()~A|$th>_d-AxZypFYS2 zGUyv?_{Byxzm_Ey!g zu9sxxbyw;`=F{Lk!M&0JMkFu(#$%cCtHi-u-d>po^_;ir0_(05_w&DENs_yUy;wUE zCdMDu?x)OBF29I?DbmnZbYZu=$-_bt%ei{R-ZSz$M1Q$0bb9Tb6!mUS$VsSVa4(d6 z$twaaQLAi&N;d4SM%=vSl}gCa^rM!w9g25-e(I$0kY`h9?OllIG?F|h3j*Wq?T>6A zcpvDym2Q+TWswoY@vvY$ce~)|2{nE4a$DM~CnRBpl( zZE0{;m?_u=_ZQG%)Y`gXZF2Pau3fPnmGHR>G8EKIBiGs z(h=m^5_2XQ-FO8BXN6SGFliv(;szbaB$8DG<+NEf_8O}urnzrjFH#*0(6x+w>3aOY$=1AC^Uuy^I{i+h{{61zC?lS% z`iyyxZbQ)}Srq7&{7-dBY#dbv9Y4*F?Y!UE(qPSsx=H09J=XaBr9w;-L+R5u`yf30 z4C;zna75iSd_@CPGBv#&d`jVkxrS+V`{QNW@4PeOqNnt?k1h-* zO&7WgYbWx)gd|GT?Y-B9J5OT_MjbifC?|gK>w@X~)XICh$~7-08JNBbuf}T96SCGl zRJzHIi|Pqks@0KYspSd1pJ0Bv4-`{t>lh_J#bXLEBjDc$7@)5A!f!cvJ1#j2|HdQ6 zZ*M_Y_k(Pf(baQ@4b(@ZG9E=K=4;&f1BmGFB76D5q;bYm{1ZG?)dRj|a=_2GAus@> z$6^#QR&xh9YsS~}z+l53SY~HN9TW1Y~@_B{J~I&s7^piTQz|02%kw%Y@XS!y2V75}-ayNu-fTX7#s+)_b)x8reI z$UFXHGIoMi=a+^bl#NdYD<|DR<&q?NWFrb*ROqf8vj>}nj}frRMVRG5g_ zN$%g-p+aRZ;vge&B^*oDa>s66Ds7Hs1NXCLJ7eG!=lS3gkjv1Fw@&^8nfVV=^Pm0Y z_(Az6e0~L6j%B<)3-WWPT=j( zT{01Uy(MVAvg80yAD3O1FRsg{2@bKe#KJT)lFs(47Ol1#WBlg(`vzzHFJ5NWAn9n0 z&}mN{GkpE_s^;2d`&w);w$fW$2%Ys(yFL(ZcKobH+`;>Zk-pddHxj8aVwHKWzOH5I zB4)@8XqN%Vu6Sp?7EiE&gXI%AdJwoF7os*j$k?HKEK{#~B$W=The6The%e_L<}{9m zoeoZjVDamVeFv$ock*9W>E}xQk>w6n7E};n3`0;I+%p45WUE~D+b!T}1nQ-;zA)a| zU@Ld{Dqj&B$Ug*H*Ql95PzF^vwU-UJ z+q9psfpYkIMapIWZM}@w_qWxo3a6I|xT}rhd*>Zmfl}Msq$m$3$C-*;!(ntv{KM$lA?+jQ6&b3%ewG zvW~O#u!!~Cq5Zj-y5zfl0U$0e{I_GvAJV)+vl}Nrvqv}V1mmZeBDG{TAd6inuU**~ z*Zwx#iy5eS5H-_ew$*gv#OuJ`V)ZPDyR3cbY#@jh2~ifh7}TOMQDCHPxPQG=bQ8vdIddM0tadh#8HJ^ZF@UWMrrwLOf7+eM z-+KdsELbTfD=^Uh{`$5)bc-PxyEwmP7X=5AJTI5Q;${N?EQ<{w4s2pI!7%Gz>ft0g zd>7R>=r9=MLvILLuz_uC;33qK4P-o{cO{{Jt%g$Kku!9i+UKd5Mf}ziu#*sU9V8pw z+ZHs_0splULuLqg88?j zn1R|h1Wm!Zakl?*pczT)vN&I5SOxF^O{h_0=$6+(*EMP2V)KBZRt5%X_N30GB3m`= z*8PKKK?igRm6hHDT^86wDL3Ou*IrKbS}vTTJ}dT^YhF(3KUM}i)vqV-?ZID`v25K$k#W;h|gj&?|vJBRRhOf5%;M5n(yY%&9+upbrAbOetmZe{${~k&tNNF0pkUm!ktI#HP6dPRRe>CA;4~r@ zPImTU1Fahy>!@PYrqd}2o?Pmc{bHJuZnU^4`aPD+=mca-G7tn{s1%xIl=j;jAY&i6 zC5HX#&WwMu3X}g0K^z={lb6Q<^p{=|f*}q%`m6fxC?s_USs^UYEcNC9Yahj>e;^@< z=N*@|UxA+K{);lFIe`5x{`I_EtIAlxCL5g+7gNWj``)EAVwS80DmvN%mr_a(7QxY1s6r6*OSHIGh z_IoVjpnn7NF17!XO_BCj9bw(mXT}82T+Bq_WHlPc{}2r_qfNjqL1;G5(PU6nefMB} zLdY+J5fJQ~gX0@eg{>|PTnEjmf=5=&Wv|ftxU`N|Tvf1UaNC#&ZDw)-DY#%)i_bug z4&|rhy#LxPBtBrgpKB$u^WMIdI9^dO^znNPAHSfw=Z$y^YViWnt*_{Z8)~Cyel>GS zN^9mLB{-T~J2IT_b!%f^;4_Kbyg)64WQ!EWUzbV_5KCz6ZOgag{;|0H%6ulI&H7R{VXkEB zMhspnuMu9+sOA-36=3(?3;zp9%lu|a*KW~;C_fSr?pAF`)70E&=i36GZw4V^6md>u zWW8F1m}K-^t!96 zY#@I2cXt-bnK$bFnIMT97RSg>>L@RD(fqs~!e`wn87zMAVD5)3=65$XlDTI#d8udR zTNlySx_)TkxzVF5tQS|TtE>-@bBOt`$f0#Q$j3hJ%ga9BbURklYkmEbKPOIkHeWUx zyBEVz2en(cxh8$4|9tQ3`=0owtu3=V8R9iPpd(;?BJ&UM$w40;<+T(|zo@YIg8Udo ztUf+==tkew-a&*I{`&OErHZzxwiqrw&l&wp&aau-l@8>-l2Mbo6q|~$1H-SEzP=^F zW9{Dlg#rI$!BO9X4>K@F4RK%?^Ow^ZRy3n{+>zC_=c9hBKv}3K^W&!OYNz4&FIK+KQSuW zcx<`_KK1&W6oz;^IB`Sta=&JkzDJ9_F5SlK@Bwj@>&f}X*_$nS&k5MkYc7j3Q^gxb zBIInwI#vY_Njkz1q(H&I+V0%i?$q)3%W3NN@ODk)iUN|5QRCCU))kAtVZ1)NvV;RX zgxt#uk8KqBM}gtd-V+ZnX|D51xW{OB0c3+t1XZlbR2}7t0mgFeT{sJUX6wM?A+)?L z;YfaLxY@mJmjJNuSAKrXpL6G-~xy+qSl3tM|vn%U4Ltab!=TrnLT)%8V zojcsDKS~@@ZvG}S_aPtT)gO-hqapa0|E@+-ijgO%(j6Eb zMDR@a`@}w(6Vu7`3pe3=*44sI?~$zydKiWzL|Fsgx&OOm^x_Q6dE07JRYZG#<(@hP z5svxw%ZK-v%#{R)^-abJ7{86pPklTkWS)kyNT|C-N~-!B;gZH+V0%!0lUi9At0IfL zVuE9hs|aX!6~;M!$x$$90{R3CET{MBX`r^o9!Uj0%@z}910SBw$YUl?)1tPXN39(_ z5u|daCUw)5(GEruxTbO&E44Ju=DaQwHIKZVIpEZ2@pKr?i!f=QnZ|1AA5~a-o_VYa-Fs$s+$?m(5dnDu+aKJyM)1mR@Ezw@ z<~F*aw)+ZM=)w&RRy+TLT^odr>We!sT=!37ENnRN>zCR|T3I^`&SSC_^P$9*7BvCJ zxkw8{)vA`j#~|3uPSER`ZT#vwF8bBF^n9+e;l<=i-L&H!_YA**{L}9Fe{i;c_g9pR zXY7HqO!thkTI|)BqWVGINRUs|JA>w~+Y`U&0@}T9i!VHz43V1zm^{soE=9#TsQ!PhKph3^oOe(ITmbv^?Pr zDPq{xFG~PYUDwDEjz7+EA-3vSFipZ8Kaqu9y%x;|kV)2Sq8poxUMM}34J`2i6!@?* zDIx~#4jCG2Y%VKyF9 w=y4i8sM>x~{BVyp!5mEP9 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 K|1UAX9{yijqgQDF diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Images/ConvexRadius.jpg b/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Images/ConvexRadius.jpg deleted file mode 100644 index 01d3c253d999e715daedf580f7c0637967b09dee..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11149 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 z 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 diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Images/MotorDamping.jpg b/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Images/MotorDamping.jpg deleted file mode 100644 index 9e3c1e5ed40faee1c18137c479e354a2bd3db961..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 96716 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

        P^@SPnPU! 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

        +AMgOAbzx-VeOHHV0jS9`&q|@`IELK80uPG8X z*D&H>U!unxe5Yt~*f}mkgiDME|Gn-1 zeE`UpLX~G zggILVIs3@+&%X7qL#Le4$zAX#cHG?M$b+4k(r%7D`RxEOezbkUYm;R*dHK*Z*%g4h z)E%s*WCe6tWr+@`F2iWruUovMyskrs`QvT>D*){8Gl|oFeqtNYF`@g25+QTE1ZyuK z_nmV0=gud;6aHMFp+J9uLYEJc6sM8Ws{pGsoM`P(+7M66?p}xy_;2AzfPxSySS}t% z7A5sK*Cp@iU3sL1XdeAhPN&uo>22rgHXtlOAy9til&{Tkjy@l*(pBX>jZUa3Z_A>_ z8#3$`pU1ipW8JoAuKpX^__u$CM#Ht|js8RxyI~B@zP)_19dV^P6FEx0+X<+Kw++CN zp3jyzpE>2#yTXs8A>SVo9Yw$Vm%Z4@QqzsGDqpb>FbF(%l|SZ)Qm>^DJGT9AHnI+jFh8yDws%9{EyU zKA<~vJQf^|!BX6O)}BsASN2O@+!8vP*;lo+^ewyk)H@<^5D=to#UK_y%ns>yMqc8# z^IW*N!w3zT#^XP$gJr*#g>&^W^S031U-n9Mq$o5$>Y!)a39r%U3Vi zrRV&3pJUJF4(hZYHT0-73DC~#GA0|9g0VxtU`BH+JB@+n$~npMSAPs{+RCi%TEvme z3Uf;QrHq%6_jN+BVPc1YNLlr!DpBi{ug#)_=Aiijv1(^Gk_H#PJjz^?wl2K%_1H|p z2|IL|Vo+~o(CuaPboEBi(^av<<(1Gnpn(dMJgjib>gqB{;A1O`nJhv$Z8mw>2Sw!@ z^MoINfL;Dl0N&mYEke+A-Ro;=!h2=kPn?eAv%FdAqGSq@KN{DN`05h6=0itDO(Cmo_wpRjzXV8$8;4Gt^#r2Xb)<- zme!y#h0lO8*f>(6CZRWMF--p-890^}ic>kXc#J6HkwuI9cs6ZbwX3{WRc<51* z8rzyP*Kr!y7{v@1(*Yc*(wi={NgCx&K2gVq19_Cw$A9vI{;NVr!RttqQLsV(YIqcc zOT|)ub(!%0(e~X@O{U+vSW!R`q$wpTA_8J86s5&R7m$v$h;$+#N-v2G=|rSSmkv^* zRH;#_fK-9dd#?#41XAX{>d%>R&ROT)yVjh4Sj<|kSs%&!?RW2IKg9*uDmE6mmYWnr zQUpBE6o!iTm805qgSlGdSj>bIi6 zwu^gBq=80{AxbX8330XVjf3!OIMj%3uHjc&tS7p;qF{?_oS?koS2oZdD; zp&8xtCAL0I(2h5hlky(v0CxI+biw+wA^D$@V80jRJ&8SfEBxmUvB=*Wy0jDQoEjHt z9PM*l&XnXfzPJns$~k=DDFFgKGAxHCY)?SmjM^B+*RaEg15G;U+krcZp9kub0xCr^ zNI}X87NypV+c+d-;xqW?B2|4;RjQmfnX9=G?!gS zF5>#b=xCM+X%|_ov$O@~hmw3#W9CKAh8kJ3s7B(|dk${$Kt;U2ImW7OI-=Bjr3!|a ziAYF)>w0Uot@5f6tke01lHRTv)vii3F)GUOm?=qPEFt>qhyYH8dP97XnqXVv@n*dy z`hsH8T^+vS68@+}-ksF>OKO*eAEV65P-85QDP1DSzFjVmL+f%7vZT+(7JAn&Rf6#GZngzqT%OrNf{}~ja4S6kpo5u0V zMFugUd7ODU4*$&*0wtEd+-yCD-tyZK?}Hc*#uA@AZuR79Jf>OOeGm5o6(W$uAP{%z3vo|j+8QB?*c8##$aQ> zb(6L#KIL^OKDY~)Sn_*XZrFOLFtB~3ze=#K>2O~hNBL8ZK4y;L`|x(;t`uZ~^%Efs zFpczKky1g%UYm^vfboqXA^{Z&)h_!Q3WmK=YGXmsA*A3%0Dy^(51dOjZU=rYP@ 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

        ok1`_ODn#6BnjW=I=73Qy)u$Q`{iC8K{k5 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 diff --git a/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Images/EllipsoidAABB.png b/crates/joltc-sys-patched/JoltC/JoltPhysics/Docs/Images/EllipsoidAABB.png deleted file mode 100644 index f4ad423f1ac3e08d868e9ddd0f263edc1fddd183..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 158978 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

      y&+wp3mU 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

  3. %7K9(ewwvKR298%WDkZz$7`L$u4XK4z3SAUlm6 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�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 z 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